Repository: Cai1Hsu/re3 Branch: miami Commit: e19f40616349 Files: 623 Total size: 10.3 MB Directory structure: gitextract_6lszgg_8/ ├── .clang-format ├── .gitattributes ├── .github/ │ ├── pull_request_template.md │ └── workflows/ │ ├── build-cmake-conan.yml │ ├── msbuildvc.yml │ ├── msbuildvcAyml │ ├── reLCS_msvc_amd64.yml │ ├── reLCS_msvc_x86.yml │ ├── reVC_msvc_amd64.yml │ └── reVC_msvc_x86.yml ├── .gitignore ├── .gitmodules ├── .vscode/ │ ├── c_cpp_properties.json │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── CMakeLists.txt ├── CODING_STYLE.md ├── README.md ├── cmake/ │ ├── FindMilesSDK.cmake │ ├── FindSndFile.cmake │ ├── Findmpg123.cmake │ ├── Findopusfile.cmake │ ├── GetGitRevisionDescription.cmake │ └── GetGitRevisionDescription.cmake.in ├── codewarrior/ │ └── reVC.mcp ├── conanfile.py ├── gamefiles/ │ ├── TEXT/ │ │ ├── american.gxt │ │ ├── french.gxt │ │ ├── german.gxt │ │ ├── italian.gxt │ │ ├── russian.gxt │ │ └── spanish.gxt │ ├── data/ │ │ └── freeroam_miami.scm │ ├── gamecontrollerdb.txt │ ├── models/ │ │ ├── fonts_r.txd │ │ ├── frontend_ds2.txd │ │ ├── frontend_ds3.txd │ │ ├── frontend_ds4.txd │ │ ├── frontend_x360.txd │ │ ├── frontend_xone.txd │ │ ├── generic.txd │ │ ├── particle.txd │ │ ├── ps3btns.txd │ │ └── x360btns.txd │ └── neo/ │ └── neo.txd ├── premake-vs2015.cmd ├── premake-vs2017.cmd ├── premake-vs2019.cmd ├── premake5.lua ├── premake5Linux ├── printHash.bat ├── printHash.sh ├── src/ │ ├── CMakeLists.txt │ ├── animation/ │ │ ├── AnimBlendAssocGroup.cpp │ │ ├── AnimBlendAssocGroup.h │ │ ├── AnimBlendAssociation.cpp │ │ ├── AnimBlendAssociation.h │ │ ├── AnimBlendClumpData.cpp │ │ ├── AnimBlendClumpData.h │ │ ├── AnimBlendHierarchy.cpp │ │ ├── AnimBlendHierarchy.h │ │ ├── AnimBlendList.h │ │ ├── AnimBlendNode.cpp │ │ ├── AnimBlendNode.h │ │ ├── AnimBlendSequence.cpp │ │ ├── AnimBlendSequence.h │ │ ├── AnimManager.cpp │ │ ├── AnimManager.h │ │ ├── AnimationId.h │ │ ├── Bones.cpp │ │ ├── Bones.h │ │ ├── CutsceneMgr.cpp │ │ ├── CutsceneMgr.h │ │ ├── FrameUpdate.cpp │ │ ├── RpAnimBlend.cpp │ │ └── RpAnimBlend.h │ ├── audio/ │ │ ├── AudioCollision.cpp │ │ ├── AudioCollision.h │ │ ├── AudioLogic.cpp │ │ ├── AudioManager.cpp │ │ ├── AudioManager.h │ │ ├── AudioSamples.h │ │ ├── AudioScriptObject.cpp │ │ ├── AudioScriptObject.h │ │ ├── DMAudio.cpp │ │ ├── DMAudio.h │ │ ├── MusicManager.cpp │ │ ├── MusicManager.h │ │ ├── PoliceRadio.cpp │ │ ├── PoliceRadio.h │ │ ├── audio_enums.h │ │ ├── eax/ │ │ │ ├── eax-util.cpp │ │ │ ├── eax-util.h │ │ │ └── eax.h │ │ ├── oal/ │ │ │ ├── aldlist.cpp │ │ │ ├── aldlist.h │ │ │ ├── channel.cpp │ │ │ ├── channel.h │ │ │ ├── oal_utils.cpp │ │ │ ├── oal_utils.h │ │ │ ├── stream.cpp │ │ │ └── stream.h │ │ ├── sampman.h │ │ ├── sampman_miles.cpp │ │ ├── sampman_null.cpp │ │ ├── sampman_oal.cpp │ │ └── soundlist.h │ ├── buildings/ │ │ ├── Building.cpp │ │ ├── Building.h │ │ ├── Solid.h │ │ ├── Treadable.cpp │ │ └── Treadable.h │ ├── collision/ │ │ ├── ColBox.cpp │ │ ├── ColBox.h │ │ ├── ColLine.cpp │ │ ├── ColLine.h │ │ ├── ColModel.cpp │ │ ├── ColModel.h │ │ ├── ColPoint.cpp │ │ ├── ColPoint.h │ │ ├── ColSphere.cpp │ │ ├── ColSphere.h │ │ ├── ColStore.cpp │ │ ├── ColStore.h │ │ ├── ColTriangle.cpp │ │ ├── ColTriangle.h │ │ ├── Collision.cpp │ │ ├── Collision.h │ │ ├── CompressedVector.h │ │ ├── TempColModels.cpp │ │ ├── TempColModels.h │ │ ├── VuCollision.cpp │ │ ├── VuCollision.h │ │ ├── vu0Collision.dsm │ │ ├── vu0Collision_1.s │ │ └── vu0Collision_2.s │ ├── control/ │ │ ├── AutoPilot.cpp │ │ ├── AutoPilot.h │ │ ├── Bridge.cpp │ │ ├── Bridge.h │ │ ├── CarAI.cpp │ │ ├── CarAI.h │ │ ├── CarCtrl.cpp │ │ ├── CarCtrl.h │ │ ├── Curves.cpp │ │ ├── Curves.h │ │ ├── Darkel.cpp │ │ ├── Darkel.h │ │ ├── GameLogic.cpp │ │ ├── GameLogic.h │ │ ├── Garages.cpp │ │ ├── Garages.h │ │ ├── NameGrid.cpp │ │ ├── NameGrid.h │ │ ├── OnscreenTimer.cpp │ │ ├── OnscreenTimer.h │ │ ├── PathFind.cpp │ │ ├── PathFind.h │ │ ├── Phones.cpp │ │ ├── Phones.h │ │ ├── Pickups.cpp │ │ ├── Pickups.h │ │ ├── PowerPoints.cpp │ │ ├── PowerPoints.h │ │ ├── Record.cpp │ │ ├── Record.h │ │ ├── Remote.cpp │ │ ├── Remote.h │ │ ├── Replay.cpp │ │ ├── Replay.h │ │ ├── Restart.cpp │ │ ├── Restart.h │ │ ├── RoadBlocks.cpp │ │ ├── RoadBlocks.h │ │ ├── SceneEdit.cpp │ │ ├── SceneEdit.h │ │ ├── Script.cpp │ │ ├── Script.h │ │ ├── Script2.cpp │ │ ├── Script3.cpp │ │ ├── Script4.cpp │ │ ├── Script5.cpp │ │ ├── Script6.cpp │ │ ├── Script7.cpp │ │ ├── Script8.cpp │ │ ├── ScriptCommands.h │ │ ├── SetPieces.cpp │ │ ├── SetPieces.h │ │ ├── TrafficLights.cpp │ │ └── TrafficLights.h │ ├── core/ │ │ ├── Accident.cpp │ │ ├── Accident.h │ │ ├── AnimViewer.cpp │ │ ├── AnimViewer.h │ │ ├── Cam.cpp │ │ ├── Camera.cpp │ │ ├── Camera.h │ │ ├── CdStream.cpp │ │ ├── CdStream.h │ │ ├── CdStreamPosix.cpp │ │ ├── Clock.cpp │ │ ├── Clock.h │ │ ├── ControllerConfig.cpp │ │ ├── ControllerConfig.h │ │ ├── Crime.h │ │ ├── Debug.cpp │ │ ├── Debug.h │ │ ├── Directory.cpp │ │ ├── Directory.h │ │ ├── EventList.cpp │ │ ├── EventList.h │ │ ├── FileLoader.cpp │ │ ├── FileLoader.h │ │ ├── FileMgr.cpp │ │ ├── FileMgr.h │ │ ├── Fire.cpp │ │ ├── Fire.h │ │ ├── FrontEndControls.cpp │ │ ├── FrontEndControls.h │ │ ├── Frontend.cpp │ │ ├── Frontend.h │ │ ├── FrontendTriggers.h │ │ ├── Frontend_PS2.cpp │ │ ├── Frontend_PS2.h │ │ ├── Game.cpp │ │ ├── Game.h │ │ ├── General.h │ │ ├── IniFile.cpp │ │ ├── IniFile.h │ │ ├── Lists.cpp │ │ ├── Lists.h │ │ ├── MenuScreens.cpp │ │ ├── MenuScreensCustom.cpp │ │ ├── Pad.cpp │ │ ├── Pad.h │ │ ├── Placeable.cpp │ │ ├── Placeable.h │ │ ├── PlayerInfo.cpp │ │ ├── PlayerInfo.h │ │ ├── Pools.cpp │ │ ├── Pools.h │ │ ├── Profile.cpp │ │ ├── Profile.h │ │ ├── Radar.cpp │ │ ├── Radar.h │ │ ├── Range2D.cpp │ │ ├── Range2D.h │ │ ├── Range3D.cpp │ │ ├── Range3D.h │ │ ├── References.cpp │ │ ├── References.h │ │ ├── Ropes.cpp │ │ ├── Ropes.h │ │ ├── Stats.cpp │ │ ├── Stats.h │ │ ├── Streaming.cpp │ │ ├── Streaming.h │ │ ├── SurfaceTable.cpp │ │ ├── SurfaceTable.h │ │ ├── TimeStep.cpp │ │ ├── TimeStep.h │ │ ├── Timer.cpp │ │ ├── Timer.h │ │ ├── User.cpp │ │ ├── User.h │ │ ├── Wanted.cpp │ │ ├── Wanted.h │ │ ├── World.cpp │ │ ├── World.h │ │ ├── ZoneCull.cpp │ │ ├── ZoneCull.h │ │ ├── Zones.cpp │ │ ├── Zones.h │ │ ├── common.h │ │ ├── config.h │ │ ├── main.cpp │ │ ├── main.h │ │ ├── obrstr.cpp │ │ ├── obrstr.h │ │ ├── re3.cpp │ │ ├── templates.h │ │ ├── timebars.cpp │ │ └── timebars.h │ ├── entities/ │ │ ├── Dummy.cpp │ │ ├── Dummy.h │ │ ├── Entity.cpp │ │ ├── Entity.h │ │ ├── Physical.cpp │ │ └── Physical.h │ ├── extras/ │ │ ├── GitSHA1.cpp.in │ │ ├── GitSHA1.h │ │ ├── arrow.inc │ │ ├── cursor.inc │ │ ├── custompipes.cpp │ │ ├── custompipes.h │ │ ├── custompipes_d3d9.cpp │ │ ├── custompipes_gl.cpp │ │ ├── debugmenu.cpp │ │ ├── debugmenu.h │ │ ├── frontendoption.cpp │ │ ├── frontendoption.h │ │ ├── ini_parser.hpp │ │ ├── postfx.cpp │ │ ├── postfx.h │ │ ├── re3_inttypes.h │ │ ├── screendroplets.cpp │ │ ├── screendroplets.h │ │ └── shaders/ │ │ ├── colourfilterVC.frag │ │ ├── colourfilterVC_PS.hlsl │ │ ├── contrast.frag │ │ ├── contrastPS.hlsl │ │ ├── default_UV2.vert │ │ ├── default_UV2_VS.hlsl │ │ ├── im2d.vert │ │ ├── im2d_UV2.vert │ │ ├── lighting.h │ │ ├── make_glsl.sh │ │ ├── make_hlsl.cmd │ │ ├── makeinc_glsl.sh │ │ ├── makeinc_hlsl.sh │ │ ├── neoGloss.frag │ │ ├── neoGloss.vert │ │ ├── neoGloss_PS.hlsl │ │ ├── neoGloss_VS.hlsl │ │ ├── neoRim.vert │ │ ├── neoRimSkin.vert │ │ ├── neoRimSkin_VS.hlsl │ │ ├── neoRim_VS.hlsl │ │ ├── neoVehicle.frag │ │ ├── neoVehicle.vert │ │ ├── neoVehicle_PS.hlsl │ │ ├── neoVehicle_VS.hlsl │ │ ├── neoWorldVC.frag │ │ ├── neoWorldVC_PS.hlsl │ │ ├── screenDroplet.frag │ │ ├── screenDroplet_PS.hlsl │ │ ├── simple.frag │ │ └── standardConstants.h │ ├── fakerw/ │ │ ├── fake.cpp │ │ ├── rpanisot.h │ │ ├── rphanim.h │ │ ├── rpmatfx.h │ │ ├── rpskin.h │ │ ├── rpworld.h │ │ ├── rtbmp.h │ │ ├── rtcharse.h │ │ ├── rtpng.h │ │ ├── rtquat.h │ │ ├── rwcore.h │ │ └── rwplcore.h │ ├── math/ │ │ ├── Matrix.cpp │ │ ├── Matrix.h │ │ ├── Quaternion.cpp │ │ ├── Quaternion.h │ │ ├── Rect.cpp │ │ ├── Rect.h │ │ ├── Vector.cpp │ │ ├── Vector.h │ │ ├── Vector2D.h │ │ ├── VuVector.h │ │ ├── math.cpp │ │ └── maths.h │ ├── modelinfo/ │ │ ├── BaseModelInfo.cpp │ │ ├── BaseModelInfo.h │ │ ├── ClumpModelInfo.cpp │ │ ├── ClumpModelInfo.h │ │ ├── ModelIndices.cpp │ │ ├── ModelIndices.h │ │ ├── ModelInfo.cpp │ │ ├── ModelInfo.h │ │ ├── PedModelInfo.cpp │ │ ├── PedModelInfo.h │ │ ├── SimpleModelInfo.cpp │ │ ├── SimpleModelInfo.h │ │ ├── TimeModelInfo.cpp │ │ ├── TimeModelInfo.h │ │ ├── VehicleModelInfo.cpp │ │ ├── VehicleModelInfo.h │ │ ├── WeaponModelInfo.cpp │ │ └── WeaponModelInfo.h │ ├── objects/ │ │ ├── CutsceneObject.cpp │ │ ├── CutsceneObject.h │ │ ├── DummyObject.cpp │ │ ├── DummyObject.h │ │ ├── Object.cpp │ │ ├── Object.h │ │ ├── ObjectData.cpp │ │ ├── ObjectData.h │ │ ├── ParticleObject.cpp │ │ ├── ParticleObject.h │ │ ├── Projectile.cpp │ │ ├── Projectile.h │ │ ├── Stinger.cpp │ │ └── Stinger.h │ ├── peds/ │ │ ├── CivilianPed.cpp │ │ ├── CivilianPed.h │ │ ├── CopPed.cpp │ │ ├── CopPed.h │ │ ├── DummyPed.h │ │ ├── EmergencyPed.cpp │ │ ├── EmergencyPed.h │ │ ├── Gangs.cpp │ │ ├── Gangs.h │ │ ├── Ped.cpp │ │ ├── Ped.h │ │ ├── PedAI.cpp │ │ ├── PedAttractor.cpp │ │ ├── PedAttractor.h │ │ ├── PedChat.cpp │ │ ├── PedDebug.cpp │ │ ├── PedFight.cpp │ │ ├── PedIK.cpp │ │ ├── PedIK.h │ │ ├── PedPlacement.cpp │ │ ├── PedPlacement.h │ │ ├── PedRoutes.cpp │ │ ├── PedRoutes.h │ │ ├── PedType.cpp │ │ ├── PedType.h │ │ ├── PlayerPed.cpp │ │ ├── PlayerPed.h │ │ ├── Population.cpp │ │ └── Population.h │ ├── render/ │ │ ├── 2dEffect.h │ │ ├── Antennas.cpp │ │ ├── Antennas.h │ │ ├── Clouds.cpp │ │ ├── Clouds.h │ │ ├── Console.cpp │ │ ├── Console.h │ │ ├── Coronas.cpp │ │ ├── Coronas.h │ │ ├── Credits.cpp │ │ ├── Credits.h │ │ ├── CutsceneShadow.cpp │ │ ├── CutsceneShadow.h │ │ ├── Draw.cpp │ │ ├── Draw.h │ │ ├── Fluff.cpp │ │ ├── Fluff.h │ │ ├── Font.cpp │ │ ├── Font.h │ │ ├── Glass.cpp │ │ ├── Glass.h │ │ ├── Hud.cpp │ │ ├── Hud.h │ │ ├── Instance.cpp │ │ ├── Instance.h │ │ ├── Lines.cpp │ │ ├── Lines.h │ │ ├── MBlur.cpp │ │ ├── MBlur.h │ │ ├── Occlusion.cpp │ │ ├── Occlusion.h │ │ ├── Particle.cpp │ │ ├── Particle.h │ │ ├── ParticleMgr.cpp │ │ ├── ParticleMgr.h │ │ ├── ParticleType.h │ │ ├── PlayerSkin.cpp │ │ ├── PlayerSkin.h │ │ ├── PointLights.cpp │ │ ├── PointLights.h │ │ ├── RenderBuffer.cpp │ │ ├── RenderBuffer.h │ │ ├── Renderer.cpp │ │ ├── Renderer.h │ │ ├── Rubbish.cpp │ │ ├── Rubbish.h │ │ ├── ShadowCamera.cpp │ │ ├── ShadowCamera.h │ │ ├── Shadows.cpp │ │ ├── Shadows.h │ │ ├── Skidmarks.cpp │ │ ├── Skidmarks.h │ │ ├── SpecialFX.cpp │ │ ├── SpecialFX.h │ │ ├── Sprite.cpp │ │ ├── Sprite.h │ │ ├── Sprite2d.cpp │ │ ├── Sprite2d.h │ │ ├── TexList.cpp │ │ ├── TexList.h │ │ ├── Timecycle.cpp │ │ ├── Timecycle.h │ │ ├── VarConsole.cpp │ │ ├── VarConsole.h │ │ ├── WaterCannon.cpp │ │ ├── WaterCannon.h │ │ ├── WaterCreatures.cpp │ │ ├── WaterCreatures.h │ │ ├── WaterLevel.cpp │ │ ├── WaterLevel.h │ │ ├── Weather.cpp │ │ ├── Weather.h │ │ ├── WindModifiers.cpp │ │ └── WindModifiers.h │ ├── rw/ │ │ ├── ClumpRead.cpp │ │ ├── Lights.cpp │ │ ├── Lights.h │ │ ├── MemoryHeap.cpp │ │ ├── MemoryHeap.h │ │ ├── MemoryMgr.cpp │ │ ├── MemoryMgr.h │ │ ├── NodeName.cpp │ │ ├── NodeName.h │ │ ├── RwHelper.cpp │ │ ├── RwHelper.h │ │ ├── RwMatFX.cpp │ │ ├── RwPS2AlphaTest.cpp │ │ ├── TexRead.cpp │ │ ├── TexturePools.cpp │ │ ├── TexturePools.h │ │ ├── TxdStore.cpp │ │ ├── TxdStore.h │ │ ├── VisibilityPlugins.cpp │ │ └── VisibilityPlugins.h │ ├── save/ │ │ ├── Date.cpp │ │ ├── Date.h │ │ ├── GenericGameStorage.cpp │ │ ├── GenericGameStorage.h │ │ ├── MemoryCard.cpp │ │ ├── MemoryCard.h │ │ ├── PCSave.cpp │ │ └── PCSave.h │ ├── skel/ │ │ ├── crossplatform.cpp │ │ ├── crossplatform.h │ │ ├── events.cpp │ │ ├── events.h │ │ ├── glfw/ │ │ │ └── glfw.cpp │ │ ├── platform.h │ │ ├── skeleton.cpp │ │ ├── skeleton.h │ │ └── win/ │ │ ├── resource.h │ │ ├── win.cpp │ │ ├── win.h │ │ └── win.rc │ ├── text/ │ │ ├── Messages.cpp │ │ ├── Messages.h │ │ ├── Pager.cpp │ │ ├── Pager.h │ │ ├── Text.cpp │ │ └── Text.h │ ├── vehicles/ │ │ ├── Automobile.cpp │ │ ├── Automobile.h │ │ ├── Bike.cpp │ │ ├── Bike.h │ │ ├── Boat.cpp │ │ ├── Boat.h │ │ ├── CarGen.cpp │ │ ├── CarGen.h │ │ ├── Cranes.cpp │ │ ├── Cranes.h │ │ ├── DamageManager.cpp │ │ ├── DamageManager.h │ │ ├── Door.cpp │ │ ├── Door.h │ │ ├── Floater.cpp │ │ ├── Floater.h │ │ ├── HandlingMgr.cpp │ │ ├── HandlingMgr.h │ │ ├── Heli.cpp │ │ ├── Heli.h │ │ ├── Plane.cpp │ │ ├── Plane.h │ │ ├── Train.cpp │ │ ├── Train.h │ │ ├── Transmission.cpp │ │ ├── Transmission.h │ │ ├── Vehicle.cpp │ │ └── Vehicle.h │ └── weapons/ │ ├── BulletInfo.cpp │ ├── BulletInfo.h │ ├── Explosion.cpp │ ├── Explosion.h │ ├── ProjectileInfo.cpp │ ├── ProjectileInfo.h │ ├── ShotInfo.cpp │ ├── ShotInfo.h │ ├── Weapon.cpp │ ├── Weapon.h │ ├── WeaponEffects.cpp │ ├── WeaponEffects.h │ ├── WeaponInfo.cpp │ ├── WeaponInfo.h │ └── WeaponType.h ├── utils/ │ └── gxt/ │ ├── american.txt │ ├── build.bat │ ├── french.txt │ ├── german.txt │ ├── italian.txt │ └── spanish.txt └── vendor/ ├── libsndfile/ │ ├── ChangeLog │ ├── NEWS │ ├── include/ │ │ ├── sndfile.h │ │ └── sndfile.hh │ └── lib/ │ ├── Win32/ │ │ ├── libsndfile-1.def │ │ ├── libsndfile-1.lib │ │ └── pkgconfig/ │ │ └── sndfile.pc │ └── Win64/ │ ├── libsndfile-1.def │ ├── libsndfile-1.lib │ └── pkgconfig/ │ └── sndfile.pc ├── milessdk/ │ ├── include/ │ │ └── mss.h │ └── lib/ │ └── mss32.lib ├── mpg123/ │ ├── include/ │ │ ├── fmt123.h │ │ └── mpg123.h │ └── lib/ │ ├── Win32/ │ │ └── libmpg123-0.lib │ └── Win64/ │ └── libmpg123-0.lib └── openal-soft/ ├── COPYING ├── include/ │ └── AL/ │ ├── al.h │ ├── alc.h │ ├── alext.h │ ├── efx-creative.h │ ├── efx-presets.h │ └── efx.h ├── libs/ │ ├── Win32/ │ │ ├── OpenAL32.def │ │ ├── OpenAL32.lib │ │ └── libOpenAL32.dll.a │ └── Win64/ │ ├── OpenAL32.def │ ├── OpenAL32.lib │ └── libOpenAL32.dll.a └── readme.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ --- AllowShortBlocksOnASingleLine: 'true' AllowShortCaseLabelsOnASingleLine: 'true' AllowShortIfStatementsOnASingleLine: 'true' AllowShortLoopsOnASingleLine: 'true' AlwaysBreakAfterReturnType: TopLevel AccessModifierOffset: -8 BreakBeforeBraces: Linux ColumnLimit: 160 IndentCaseLabels: 'false' IndentWidth: '8' Language: Cpp PointerAlignment: Right SpaceAfterCStyleCast: 'false' SpaceBeforeAssignmentOperators: 'true' SpaceBeforeCtorInitializerColon: 'true' SpaceBeforeInheritanceColon: 'true' SpaceBeforeParens: Never SpaceInEmptyParentheses: 'false' SpacesInAngles: 'false' SpacesInCStyleCastParentheses: 'false' SpacesInContainerLiterals: 'false' SpacesInParentheses: 'false' SpacesInSquareBrackets: 'false' TabWidth: '8' UseTab: ForIndentation ... ================================================ FILE: .gitattributes ================================================ sdk/* linguist-vendored vendor/* linguist-vendored ================================================ FILE: .github/pull_request_template.md ================================================ As long as it's not linux/cross-platform skeleton/compatibility layer, all of the code on the repo that's not behind a preprocessor condition(like FIX_BUGS) are **completely** reversed code from original binaries. We **don't** accept custom codes, as long as it's not wrapped via preprocessor conditions, or it's linux/cross-platform skeleton/compatibility layer. We accept only these kinds of PRs; - A new feature that exists in at least one of the GTAs (if it wasn't in III/VC then it doesn't have to be decompilation) - Game, UI or UX bug fixes (if it's a fix to R* code, it should be behind FIX_BUGS) - Platform-specific and/or unused code that's not been reversed yet - Makes reversed code more understandable/accurate, as in "which code would produce this assembly". - A new cross-platform skeleton/compatibility layer, or improvements to them - Translation fixes, for languages R* supported/outsourced - Code that increase maintainability ================================================ FILE: .github/workflows/build-cmake-conan.yml ================================================ name: reLCS conan+cmake on: pull_request: push: release: types: published jobs: build-cmake: strategy: matrix: include: - os: 'windows-latest' platform: 'gl3' gl3_gfxlib: 'glfw' audio: 'openal' # - os: 'windows-latest' # platform: 'gl3' # gl3_gfxlib: 'sdl2' # audio: 'openal' - os: 'windows-latest' platform: 'd3d9' audio: 'openal' # - os: 'windows-latest' # platform: 'd3d9' # audio: 'miles' - os: 'ubuntu-latest' platform: 'gl3' gl3_gfxlib: 'glfw' audio: 'openal' # - os: 'ubuntu-latest' # platform: 'gl3' # gl3_gfxlib: 'sdl2' # audio: 'openal' - os: 'macos-latest' platform: 'gl3' gl3_gfxlib: 'glfw' audio: 'openal' # - os: 'macos-latest' # platform: 'gl3' # gl3_gfxlib: 'sdl2' # audio: 'openal' runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.platform == 'ps2' || matrix.gl3_gfxlib == 'sdl2' || matrix.audio == 'miles' }} steps: - uses: actions/checkout@v2 with: submodules: true - name: "Checkout Miles SDK Import Library project" uses: actions/checkout@v2 if: ${{ matrix.audio == 'miles' }} with: repository: 'withmorten/re3mss' path: 're3mss' - uses: actions/setup-python@v2 with: python-version: '3.x' - name: "Use XCode 11 as default (conan-center-index does not provide XCode 12 binaries at the moment)" if: startsWith(matrix.os, 'macos') run: | sudo xcode-select --switch /Applications/Xcode_11.7.app - name: "Setup conan" run: | python -m pip install conan conan config init conan config set log.print_run_commands=True conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan conan remote add madebr_ps2dev https://api.bintray.com/conan/madebr/ps2dev - name: "Add os=playstation2 + gcc.version=3.2 to .conan/settings.yml" shell: python run: | import os, yaml settings_path = os.path.expanduser("~/.conan/settings.yml") yml = yaml.safe_load(open(settings_path)) yml["os"]["playstation2"] = None yml["compiler"]["gcc"]["version"].append("3.2") yml["compiler"]["gcc"]["version"].sort() yaml.safe_dump(yml, open(settings_path, "w")) - name: "Create host profile" shell: bash run: | if test "${{ matrix.platform }}" = "ps2"; then cp vendor/librw/conan/playstation2 host_profile else cp ~/.conan/profiles/default host_profile fi - name: "Export Playstation 2 CMake toolchain conan recipe" run: | conan export vendor/librw/cmake/ps2toolchain ps2dev-cmaketoolchain/master@ - name: "Export librw conan recipe" run: | conan export vendor/librw librw/master@ - name: "Export Miles SDK conan recipe" if: ${{ matrix.audio == 'miles' }} run: | conan export re3mss miles-sdk/master@ - name: "Download/build dependencies (conan install)" run: | conan install ${{ github.workspace }} reLCS/master@ -if build -o reLCS:audio=${{ matrix.audio }} -o librw:platform=${{ matrix.platform }} -o librw:gl3_gfxlib=${{ matrix.gl3_gfxlib || 'glfw' }} --build missing -pr:h ./host_profile -pr:b default -s reLCS:build_type=RelWithDebInfo -s librw:build_type=RelWithDebInfo env: CONAN_SYSREQUIRES_MODE: enabled - name: "Build reLCS (conan build)" run: | conan build ${{ github.workspace }} -if build -bf build -pf package - name: "Package reLCS (conan package)" run: | conan package ${{ github.workspace }} -if build -bf build -pf package - name: "Create binary package (cpack)" working-directory: ./build run: | cpack -C RelWithDebInfo - name: "Archive binary package (github artifacts)" uses: actions/upload-artifact@v2 with: name: "${{ matrix.os }}-${{ matrix.platform }}" path: build/*.tar.xz if-no-files-found: error ================================================ FILE: .github/workflows/msbuildvc.yml ================================================ name: MSBuild_vc on: [push] env: # Path to the solution file relative to the root of the project. SOLUTION_FILE_PATH: . # Configuration type to build. # You can convert this to a build matrix if you need coverage of multiple configuration types. # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix BUILD_CONFIGURATION: Release jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v2 - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1 - name: Build working-directory: ${{env.SOLUTION_FILE_PATH}} # Add additional options to the MSBuild command line here (like platform or verbosity level). # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference run: | git submodule update --init --recursive .\premake-vs2019.cmd msbuild /m /p:Configuration=Release build/reVC.sln ================================================ FILE: .github/workflows/msbuildvcAyml ================================================ name: MSBuild_vc on: [push] env: # Path to the solution file relative to the root of the project. SOLUTION_FILE_PATH: . # Configuration type to build. # You can convert this to a build matrix if you need coverage of multiple configuration types. # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix BUILD_CONFIGURATION: Release jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v2 - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1 - name: Build working-directory: ${{env.SOLUTION_FILE_PATH}} # Add additional options to the MSBuild command line here (like platform or verbosity level). # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference run: | .\premake-vs2019.cmd msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} .\build\reVC.sln ================================================ FILE: .github/workflows/reLCS_msvc_amd64.yml ================================================ name: reLCS premake amd64 on: pull_request: push: release: types: published env: GLFW_VER: "3.3.2" GLFW_BASE: "glfw-3.3.2.bin.WIN64" GLFW_FILE: "glfw-3.3.2.bin.WIN64.zip" GLFW_URL: "https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.bin.WIN64.zip" jobs: build: runs-on: windows-2019 strategy: matrix: platform: [win-amd64-librw_d3d9-oal, win-amd64-librw_gl3_glfw-oal] buildtype: [Debug, Release] steps: - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.0.2 - uses: actions/checkout@v2 with: submodules: 'true' - if: ${{ matrix.platform }} == "win-amd64-librw_gl3_glfw-mss" name: Download glfw uses: carlosperate/download-file-action@v1.0.3 with: file-url: ${{env.GLFW_URL}} - if: ${{ matrix.platform }} == "win-amd64-librw_gl3_glfw-mss" name: Unpack archives run: | 7z x ${{env.GLFW_FILE}} - name: Configure build run: | ./premake5 vs2019 --with-librw --glfwdir64=${{env.GLFW_BASE}} - name: Build run: | msbuild -m build/reLCS.sln /property:Configuration=${{matrix.buildtype}} /property:Platform=${{matrix.platform}} # - name: Pack artifacts # run: | # 7z a reLCS_${{matrix.buildtype}}_${{matrix.platform}}.zip ./bin/${{matrix.platform}}/${{matrix.buildtype}}/* - name: Move binaries to gamefiles run: | mv ./bin/${{matrix.platform}}/${{matrix.buildtype}}/reLCS.exe ./gamefiles/ mv ./bin/${{matrix.platform}}/${{matrix.buildtype}}/reLCS.pdb ./gamefiles/ - name: Move dynamic dependencies to gamefiles run: | mv ./vendor/mpg123/dist/Win64/libmpg123-0.dll ./gamefiles/ mv ./vendor/openal-soft/dist/Win64/OpenAL32.dll ./gamefiles/ - name: Upload artifact to actions uses: actions/upload-artifact@v2 with: name: reLCS_${{matrix.buildtype}}_${{matrix.platform}} path: ./gamefiles/* # - name: Upload artifact to Bintray # uses: hpcsc/upload-bintray-docker-action@v1 # with: # repository: reLCS # package: ${{matrix.buildtype}}_${{matrix.platform}} # version: 1.0-$(echo ${GITHUB_SHA} # sourcePath: ./bin/${{matrix.platform}}/${{matrix.buildtype}} # username: gtamodding # apiKey: ${{secrets.BINTRAY_API_KEY}} ================================================ FILE: .github/workflows/reLCS_msvc_x86.yml ================================================ name: reLCS premake x86 on: pull_request: push: release: types: published env: GLFW_VER: "3.3.2" GLFW_BASE: "glfw-3.3.2.bin.WIN32" GLFW_FILE: "glfw-3.3.2.bin.WIN32.zip" GLFW_URL: "https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.bin.WIN32.zip" jobs: build: runs-on: windows-2019 strategy: matrix: platform: [win-x86-librw_d3d9-mss, win-x86-librw_gl3_glfw-mss, win-x86-librw_d3d9-oal, win-x86-librw_gl3_glfw-oal] buildtype: [Debug, Release] steps: - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.0.2 - uses: actions/checkout@v2 with: submodules: 'true' - if: ${{ matrix.platform }} == "win-x86-librw_gl3_glfw-mss" name: Download glfw uses: carlosperate/download-file-action@v1.0.3 with: file-url: ${{env.GLFW_URL}} - if: ${{ matrix.platform }} == "win-x86-librw_gl3_glfw-mss" name: Unpack archives run: | 7z x ${{env.GLFW_FILE}} - name: Configure build run: | ./premake5 vs2019 --with-librw --glfwdir32=${{env.GLFW_BASE}} - name: Build run: | msbuild -m build/reLCS.sln /property:Configuration=${{matrix.buildtype}} /property:Platform=${{matrix.platform}} # - name: Pack artifacts # run: | # 7z a reLCS_${{matrix.buildtype}}_${{matrix.platform}}.zip ./bin/${{matrix.platform}}/${{matrix.buildtype}}/* - name: Move binaries to gamefiles run: | mv ./bin/${{matrix.platform}}/${{matrix.buildtype}}/reLCS.exe ./gamefiles/ mv ./bin/${{matrix.platform}}/${{matrix.buildtype}}/reLCS.pdb ./gamefiles/ - if: contains(matrix.platform, 'oal') name: Move dynamic dependencies to gamefiles run: | mv ./vendor/mpg123/dist/Win32/libmpg123-0.dll ./gamefiles/ mv ./vendor/openal-soft/dist/Win32/OpenAL32.dll ./gamefiles/ - name: Upload artifact to actions uses: actions/upload-artifact@v2 with: name: reLCS_${{matrix.buildtype}}_${{matrix.platform}} path: ./gamefiles/* # - name: Upload artifact to Bintray # uses: hpcsc/upload-bintray-docker-action@v1 # with: # repository: reLCS # package: ${{matrix.buildtype}}_${{matrix.platform}} # version: 1.0-$(echo ${GITHUB_SHA} # sourcePath: ./bin/${{matrix.platform}}/${{matrix.buildtype}} # username: gtamodding # apiKey: ${{secrets.BINTRAY_API_KEY}} ================================================ FILE: .github/workflows/reVC_msvc_amd64.yml ================================================ name: reVC premake amd64 on: pull_request: push: release: types: published env: GLFW_VER: "3.3.2" GLFW_BASE: "glfw-3.3.2.bin.WIN64" GLFW_FILE: "glfw-3.3.2.bin.WIN64.zip" GLFW_URL: "https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.bin.WIN64.zip" jobs: build: runs-on: windows-2019 strategy: matrix: platform: [win-amd64-librw_d3d9-oal, win-amd64-librw_gl3_glfw-oal] buildtype: [Debug, Release, Vanilla] steps: - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.0.2 - uses: actions/checkout@v2 with: submodules: 'true' - if: ${{ matrix.platform }} == "win-amd64-librw_gl3_glfw-mss" name: Download glfw uses: carlosperate/download-file-action@v1.0.3 with: file-url: ${{env.GLFW_URL}} - if: ${{ matrix.platform }} == "win-amd64-librw_gl3_glfw-mss" name: Unpack archives run: | 7z x ${{env.GLFW_FILE}} - name: Configure build run: | ./premake5 vs2019 --with-librw --glfwdir64=${{env.GLFW_BASE}} - name: Build run: | msbuild -m build/reVC.sln /property:Configuration=${{matrix.buildtype}} /property:Platform=${{matrix.platform}} # - name: Pack artifacts # run: | # 7z a reVC_${{matrix.buildtype}}_${{matrix.platform}}.zip ./bin/${{matrix.platform}}/${{matrix.buildtype}}/* - name: Move binaries to gamefiles run: | mv ./bin/${{matrix.platform}}/${{matrix.buildtype}}/reVC.exe ./gamefiles/ mv ./bin/${{matrix.platform}}/${{matrix.buildtype}}/reVC.pdb ./gamefiles/ - name: Move dynamic dependencies to gamefiles run: | mv ./vendor/mpg123/dist/Win64/libmpg123-0.dll ./gamefiles/ mv ./vendor/openal-soft/dist/Win64/OpenAL32.dll ./gamefiles/ - name: Upload artifact to actions uses: actions/upload-artifact@v2 with: name: reVC_${{matrix.buildtype}}_${{matrix.platform}} path: ./gamefiles/* # - name: Upload artifact to Bintray # uses: hpcsc/upload-bintray-docker-action@v1 # with: # repository: reVC # package: ${{matrix.buildtype}}_${{matrix.platform}} # version: 1.0-$(echo ${GITHUB_SHA} # sourcePath: ./bin/${{matrix.platform}}/${{matrix.buildtype}} # username: gtamodding # apiKey: ${{secrets.BINTRAY_API_KEY}} ================================================ FILE: .github/workflows/reVC_msvc_x86.yml ================================================ name: reVC premake x86 on: pull_request: push: release: types: published env: GLFW_VER: "3.3.2" GLFW_BASE: "glfw-3.3.2.bin.WIN32" GLFW_FILE: "glfw-3.3.2.bin.WIN32.zip" GLFW_URL: "https://github.com/glfw/glfw/releases/download/3.3.2/glfw-3.3.2.bin.WIN32.zip" jobs: build: runs-on: windows-2019 strategy: matrix: platform: [win-x86-librw_d3d9-mss, win-x86-librw_gl3_glfw-mss, win-x86-librw_d3d9-oal, win-x86-librw_gl3_glfw-oal] buildtype: [Debug, Release, Vanilla] steps: - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.0.2 - uses: actions/checkout@v2 with: submodules: 'true' - if: ${{ matrix.platform }} == "win-x86-librw_gl3_glfw-mss" name: Download glfw uses: carlosperate/download-file-action@v1.0.3 with: file-url: ${{env.GLFW_URL}} - if: ${{ matrix.platform }} == "win-x86-librw_gl3_glfw-mss" name: Unpack archives run: | 7z x ${{env.GLFW_FILE}} - name: Configure build run: | ./premake5 vs2019 --with-librw --glfwdir32=${{env.GLFW_BASE}} - name: Build run: | msbuild -m build/reVC.sln /property:Configuration=${{matrix.buildtype}} /property:Platform=${{matrix.platform}} # - name: Pack artifacts # run: | # 7z a reVC_${{matrix.buildtype}}_${{matrix.platform}}.zip ./bin/${{matrix.platform}}/${{matrix.buildtype}}/* - name: Move binaries to gamefiles run: | mv ./bin/${{matrix.platform}}/${{matrix.buildtype}}/reVC.exe ./gamefiles/ mv ./bin/${{matrix.platform}}/${{matrix.buildtype}}/reVC.pdb ./gamefiles/ - if: contains(matrix.platform, 'oal') name: Move dynamic dependencies to gamefiles run: | mv ./vendor/mpg123/dist/Win32/libmpg123-0.dll ./gamefiles/ mv ./vendor/openal-soft/dist/Win32/OpenAL32.dll ./gamefiles/ - name: Upload artifact to actions uses: actions/upload-artifact@v2 with: name: reVC_${{matrix.buildtype}}_${{matrix.platform}} path: ./gamefiles/* # - name: Upload artifact to Bintray # uses: hpcsc/upload-bintray-docker-action@v1 # with: # repository: reVC # package: ${{matrix.buildtype}}_${{matrix.platform}} # version: 1.0-$(echo ${GITHUB_SHA} # sourcePath: ./bin/${{matrix.platform}}/${{matrix.buildtype}} # username: gtamodding # apiKey: ${{secrets.BINTRAY_API_KEY}} ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Mono auto generated files mono_crash.* # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ [Bb]uild/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUnit *.VisualState.xml TestResult.xml nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # NuGet Symbol Packages *.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx *.appxbundle *.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !?*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- [Bb]ackup.rdl *- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ # BeatPulse healthcheck temp database healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ vendor/glew-2.1.0/ vendor/glfw-3.3.2.bin.WIN32/ vendor/glfw-3.3.2.bin.WIN64/ sdk/ codewarrior/reVC_Data/ codewarrior/Release/ codewarrior/Debug/ src/extras/GitSHA1.cpp ================================================ FILE: .gitmodules ================================================ [submodule "vendor/ogg"] path = vendor/ogg url = https://github.com/xiph/ogg.git branch = master [submodule "vendor/opus"] path = vendor/opus url = https://github.com/xiph/opus.git branch = master [submodule "vendor/opusfile"] path = vendor/opusfile url = https://github.com/xiph/opusfile.git branch = master [submodule "vendor/librw"] path = vendor/librw url = https://github.com/aap/librw.git branch = master ================================================ FILE: .vscode/c_cpp_properties.json ================================================ { "configurations": [ { "name": "Mac", "includePath": ["${default}"], "defines": [], "macFrameworkPath": [ "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" ], "compilerPath": "/opt/local/bin/clang", "compilerArgs": ["-g"], "cStandard": "gnu11", "cppStandard": "gnu++14", "browse": { "path": [ "/opt/local/include", "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include" ] } }, { "name": "Linux", "includePath": ["${default}"], "defines": ["XDG_ROOT"], "compilerPath": "/usr/bin/gcc", "compilerArgs": ["-ggdb"], "cStandard": "gnu11", "cppStandard": "gnu++14" } ], "version": 4 } ================================================ FILE: .vscode/launch.json ================================================ { "configurations": [ { "MIMode": "gdb", "args": [], "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "name": "(gdb) Launch (Linux Debug)", "preLaunchTask": "Compile (Debug Linux x64)", "program": "${workspaceFolder}/bin/linux-amd64-librw_gl3_glfw-oal/Debug/re3", "request": "launch", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "ignoreFailures": true, "text": "-enable-pretty-printing" } ], "stopAtEntry": false, "targetArchitecture": "x64", "type": "cppdbg" }, { "MIMode": "gdb", "args": [], "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "name": "(gdb) Launch (Linux Release)", "preLaunchTask": "Compile (Release Linux x64)", "program": "${workspaceFolder}/bin/linux-amd64-librw_gl3_glfw-oal/Release/re3", "request": "launch", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "ignoreFailures": true, "text": "-enable-pretty-printing" } ], "stopAtEntry": false, "targetArchitecture": "x64", "type": "cppdbg" }, { "MIMode": "lldb", "args": [], "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "name": "(lldb) Launch (macOS Debug)", "preLaunchTask": "Compile (Debug macOS x64)", "program": "${workspaceFolder}/bin/macosx-amd64-librw_gl3_glfw-oal/Debug/re3.app", "request": "launch", "setupCommands": [ { "description": "Enable pretty-printing for lldb", "ignoreFailures": true, "text": "-enable-pretty-printing" } ], "stopAtEntry": false, "targetArchitecture": "x64", "type": "cppdbg" }, { "MIMode": "lldb", "args": [], "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "name": "(lldb) Launch (macOS Release)", "preLaunchTask": "Compile (Release macOS x64)", "program": "${workspaceFolder}/bin/macosx-amd64-librw_gl3_glfw-oal/Release/re3.app", "request": "launch", "setupCommands": [ { "description": "Enable pretty-printing for lldb", "ignoreFailures": true, "text": "-enable-pretty-printing" } ], "stopAtEntry": false, "targetArchitecture": "x64", "type": "cppdbg" } ], "version": "0.2.0" } ================================================ FILE: .vscode/settings.json ================================================ { "C_Cpp.default.cStandard": "gnu11", "C_Cpp.default.cppStandard": "gnu++14", "C_Cpp.default.includePath": [ "src/animation", "src/audio", "src/control", "src/core", "src/entities", "src/extras", "src/fakerw", "src/math", "src/modelinfo", "src/objects", "src/peds", "src/render", "src/rw", "src/save", "src/skel", "src/text", "src/vehicles", "src/weapons", "vendor/librw" ], "C_Cpp.vcFormat.indent.gotoLabels": "leftmostColumn", "C_Cpp.vcFormat.space.pointerReferenceAlignment": "right", "cSpell.enabled": false, "files.trimFinalNewlines": false, "files.trimTrailingWhitespace": false } ================================================ FILE: .vscode/tasks.json ================================================ { "tasks": [ { "args": ["--with-librw", "gmake2"], "command": "./premake5Linux", "label": "Premake (Linux)", "problemMatcher": "$gcc", "type": "shell" }, { "args": ["--with-librw", "gmake2"], "command": "premake5", "label": "Premake (macOS)", "problemMatcher": "$gcc", "type": "shell" }, { "args": [ "-j5", "config=debug_linux-amd64-librw_gl3_glfw-oal", "verbose=1" ], "command": "make", "dependsOn": "Premake (Linux)", "group": { "isDefault": true, "kind": "build" }, "label": "Compile (Debug Linux x64)", "options": { "cwd": "${workspaceFolder}/build" }, "problemMatcher": "$gcc", "type": "shell" }, { "args": [ "-j5", "config=release_linux-amd64-librw_gl3_glfw-oal", "verbose=1" ], "command": "make", "dependsOn": "Premake (Linux)", "group": { "isDefault": true, "kind": "build" }, "label": "Compile (Release Linux x64)", "options": { "cwd": "${workspaceFolder}/build" }, "problemMatcher": "$gcc", "type": "shell" }, { "args": [ "-j5", "config=debug_macosx-amd64-librw_gl3_glfw-oal", "verbose=1" ], "command": "make", "dependsOn": "Premake (macOS)", "group": { "isDefault": true, "kind": "build" }, "label": "Compile (Debug macOS x64)", "options": { "cwd": "${workspaceFolder}/build" }, "problemMatcher": "$gcc", "type": "shell" }, { "args": [ "-j5", "config=release_macosx-amd64-librw_gl3_glfw-oal", "verbose=1" ], "command": "make", "dependsOn": "Premake (macOS)", "group": { "isDefault": true, "kind": "build" }, "label": "Compile (Release macOS x64)", "options": { "cwd": "${workspaceFolder}/build" }, "problemMatcher": "$gcc", "type": "shell" } ], "version": "2.0.0" } ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.8) set(EXECUTABLE reVC) set(PROJECT REVC) project(${EXECUTABLE} C CXX) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC GIT_SHA1 "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") message(STATUS "Building ${CMAKE_PROJECT_NAME} GIT SHA1: ${GIT_SHA1}") if(WIN32) set(${PROJECT}_AUDIOS "OAL" "MSS") else() set(${PROJECT}_AUDIOS "OAL") endif() set(${PROJECT}_AUDIO "OAL" CACHE STRING "Audio") option(${PROJECT}_WITH_OPUS "Build ${EXECUTABLE} with opus support" OFF) option(${PROJECT}_WITH_LIBSNDFILE "Build ${EXECUTABLE} with libsndfile (instead of internal decoder)" OFF) set_property(CACHE ${PROJECT}_AUDIO PROPERTY STRINGS ${${PROJECT}_AUDIOS}) message(STATUS "${PROJECT}_AUDIO = ${${PROJECT}_AUDIO} (choices=${${PROJECT}_AUDIOS})") set("${PROJECT}_AUDIO_${${PROJECT}_AUDIO}" ON) if(NOT ${PROJECT}_AUDIO IN_LIST ${PROJECT}_AUDIOS) message(FATAL_ERROR "Illegal ${PROJECT}_AUDIO=${${PROJECT}_AUDIO}") endif() option(${PROJECT}_VENDORED_LIBRW "Use vendored librw" ON) if(${PROJECT}_VENDORED_LIBRW) add_subdirectory(vendor/librw) else() find_package(librw REQUIRED) endif() add_subdirectory(src) if(${PROJECT}_INSTALL) install(DIRECTORY gamefiles/ DESTINATION ".") if(LIBRW_PLATFORM_NULL) set(platform "-null") elseif(LIBRW_PLATFORM_PS2) set(platform "-ps2") elseif(LIBRW_PLATFORM_GL3) if(LIBRW_GL3_GFXLIB STREQUAL "GLFW") set(platform "-gl3-glfw") else() set(platform "-gl3-sdl2") endif() elseif(LIBRW_PLATFORM_D3D9) set(platform "-d3d9") endif() if(${PROJECT}_AUDIO_OAL) set(audio "-oal") elseif(${PROJECT}_AUDIO_MSS) set(audio "-mss") endif() if(${PROJECT}_WITH_OPUS) set(audio "${audio}-opus") endif() if(NOT LIBRW_PLATFORM_PS2) if(WIN32) set(os "-win") elseif(APPLE) set(os "-apple") elseif(UNIX) set(os "-linux") else() set(compiler "-UNK") message(WARNING "Unknown os. Created cpack package will be wrong. (override using cpack -P)") endif() endif() set(CPACK_PACKAGE_NAME "${PROJECT_NAME}${platform}${audio}${os}${compiler}") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "GTA III reversed") set(CPACK_PACKAGE_VENDOR "GTAModding") # FIXME: missing license (https://github.com/GTAmodding/re3/issues/794) # set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/LICENSE") # set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}") set(CPACK_GENERATOR "TXZ") include(CPack) endif() ================================================ FILE: CODING_STYLE.md ================================================ # Coding style I started writing in [Plan 9 style](http://man.cat-v.org/plan_9/6/style), but realize that this is not the most popular style, so I'm willing to compromise. Try not to deviate too much so the code will look similar across the whole project. To give examples, these two styles (or anything in between) are fine: ``` type functionname(args) { if(a == b){ s1; s2; }else{ s3; s4; } if(x != y) s5; } type functionname(args) { if (a == b) { s1; s2; } else { s3; s4; } if (x != y) s5; } ``` This one (or anything more extreme) is heavily discouraged: ``` type functionname ( args ) { if ( a == b ) { s1; s2; } else { s3; s4; } if ( x != y ) { s5; } } ``` i.e. * Put the brace on the same line as control statements * Put the brace on the next line after function definitions and structs/classes * Put an `else` on the same line with the braces * Don't put braces around single statements * Put the function return type on a separate line * Indent with TABS As for the less cosmetic choices, here are some guidelines how the code should look: * Don't use magic numbers where the original source code would have had an enum or similar. Even if you don't know the exact meaning it's better to call something `FOOBAR_TYPE_4` than just `4`, since `4` will be used in other places and you can't easily see where else the enum value is used. * Don't just copy paste code from IDA, make it look nice * Use the right types. In particular: * don't use types like `__int16`, we have `int16` for that * don't use `unsigned`, we have typedefs for that * don't use `char` for anything but actual characters, use `int8`, `uint8` or `bool` * don't even think about using win32 types (`BYTE`, `WORD`, &c.) unless you're writing win32 specific code * declare pointers like `int *ptr;`, not `int* ptr;` * As for variable names, the original gta source code was not written in a uniform style, but here are some observations: * many variables employ a form of hungarian notation, i.e.: * `m_` may be used for class member variables (mostly those that are considered private) * `ms_` for (mostly private) static members * `f` is a float, `i` or `n` is an integer, `b` is a boolean, `a` is an array * do *not* use `dw` for `DWORD` or so, we're not programming win32 * Generally, try to make the code look as if R* could have written it ================================================ FILE: README.md ================================================ # Take Two, all of their employees, and anyone who supports them can go fuck themselves :). Sincerely, Starman. reVC logo [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2FGTAmodding%2Fre3%2Fbadge%3Fref%3Dmiami&style=flat)](https://actions-badge.atrox.dev/GTAmodding/re3/goto?ref=miami) ## Intro In this repository you'll find the fully reversed source code for GTA III ([master](https://github.com/GTAmodding/re3/tree/master/) branch) and GTA VC ([miami](https://github.com/GTAmodding/re3/tree/miami/) branch). It has been tested and works on Windows, Linux and FreeBSD, on x86, amd64, arm and arm64.\ Rendering is handled either by original RenderWare (D3D8) or the reimplementation [librw](https://github.com/aap/librw) (D3D9, OpenGL 2.1 or above, OpenGL ES 2.0 or above).\ Audio is done with MSS (using dlls from original GTA) or OpenAL. We cannot build for PS2 or Xbox yet. If you're interested in doing so, get in touch with us. ## How can I try it? - reVC requires game assets to work, so you **must** own [a copy of GTA Vice City](https://store.steampowered.com/app/12110/Grand_Theft_Auto_Vice_City/). - Build reVC or download the latest build: - [Windows D3D9 MSS 32bit](https://nightly.link/GTAmodding/re3/workflows/reVC_msvc_x86/miami/reVC_Release_win-x86-librw_d3d9-mss.zip) - [Windows D3D9 64bit](https://nightly.link/GTAmodding/re3/workflows/reVC_msvc_amd64/miami/reVC_Release_win-amd64-librw_d3d9-oal.zip) - [Windows OpenGL 64bit](https://nightly.link/GTAmodding/re3/workflows/reVC_msvc_amd64/miami/reVC_Release_win-amd64-librw_gl3_glfw-oal.zip) - [Linux 64bit](https://nightly.link/GTAmodding/re3/workflows/build-cmake-conan/miami/ubuntu-latest-gl3.zip) - [MacOS 64bit](https://nightly.link/GTAmodding/re3/workflows/build-cmake-conan/miami/macos-latest-gl3.zip) - Extract the downloaded zip over your GTA VC directory and run reVC. The zip includes the gamefiles and in case of OpenAL the required dlls. ## Screenshots ![screen_ 1613087332](https://user-images.githubusercontent.com/1521437/107714111-f84f3200-6ccc-11eb-902e-d757481d579a.png) ![screen_ 1613086852](https://user-images.githubusercontent.com/1521437/107714115-fa18f580-6ccc-11eb-9de5-eb4cd04865d3.png) ![screen_ 1613086989](https://user-images.githubusercontent.com/1521437/107714103-f38a7e00-6ccc-11eb-88a3-c8c2033c51d6.png) ![screen_ 1613087193](https://user-images.githubusercontent.com/1521437/107714106-f4bbab00-6ccc-11eb-96a9-13821d9b9684.png) ## Improvements We have implemented a number of changes and improvements to the original game. They can be configured in `core/config.h`. Some of them can be toggled at runtime, some cannot. * Fixed a lot of smaller and bigger bugs * User files (saves and settings) stored in GTA root directory * Settings stored in reVC.ini file instead of gta_vc.set * Debug menu to do and change various things (Ctrl-M to open) * Debug camera (Ctrl-B to toggle) * Rotatable camera * XInput controller support (Windows) * No loading screens between islands ("map memory usage" in menu) * Rendering * Widescreen support (properly scaled HUD, Menu and FOV) * PS2 MatFX (vehicle reflections) * PS2 alpha test (better rendering of transparency) * Xbox vehicle rendering * Xbox world lightmap rendering (needs Xbox map) * Xbox ped rim light * Xbox screen rain droplets * More customizable colourfilter * Menu * More options * Controller configuration menu * ... * Can load DFFs and TXDs from other platforms, possibly with a performance penalty * ... ## To-Do The following things would be nice to have/do: * Fix physics for high FPS * Improve performance on lower end devices, especially the OpenGL layer on the Raspberry Pi (if you have experience with this, please get in touch) * [PS2 port](https://github.com/GTAmodding/re3/wiki/PS2-port) * Xbox port (not quite as important) * reverse remaining unused/debug functions * compare CodeWarrior build with original binary for more accurate code (very tedious) ## Modding Asset modifications (models, texture, handling, script, ...) should work the same way as with original GTA for the most part. Mods that make changes to the code (dll/asi, CLEO, limit adjusters) will *not* work. Some things these mods do are already implemented in re3 (much of SkyGFX, GInput, SilentPatch, Widescreen fix), others can easily be achieved (increasing limis, see `config.h`), others will simply have to be rewritten and integrated into the code directly. Sorry for the inconvenience. ## Building from Source When using premake, you may want to point GTA_VC_RE_DIR environment variable to GTA Vice City root folder if you want the executable to be moved there via post-build script. Clone the repository with `git clone --recursive -b miami https://github.com/GTAmodding/re3.git reVC`. Then `cd reVC` into the cloned repository.
Linux Premake For Linux using premake, proceed: [Building on Linux](https://github.com/GTAmodding/re3/wiki/Building-on-Linux)
Linux Conan Install python and conan, and then run build. ``` conan export vendor/librw librw/master@ mkdir build cd build conan install .. reVC/master@ -if build -o reVC:audio=openal -o librw:platform=gl3 -o librw:gl3_gfxlib=glfw --build missing -s reVC:build_type=RelWithDebInfo -s librw:build_type=RelWithDebInfo conan build .. -if build -bf build -pf package ```
FreeBSD For FreeBSD using premake, proceed: [Building on FreeBSD](https://github.com/GTAmodding/re3/wiki/Building-on-FreeBSD)
Windows Assuming you have Visual Studio 2015/2017/2019: - Run one of the `premake-vsXXXX.cmd` variants on root folder. - Open build/reVC.sln with Visual Studio and compile the solution. Microsoft recently discontinued its downloads of the DX9 SDK. You can download an archived version here: https://archive.org/details/dxsdk_jun10 **If you choose OpenAL on Windows** You must read [Running OpenAL build on Windows](https://github.com/GTAmodding/re3/wiki/Running-OpenAL-build-on-Windows).
> :information_source: premake has an `--lto` option if you want the project to be compiled with Link Time Optimization. > :information_source: There are various settings in [config.h](https://github.com/GTAmodding/re3/tree/miami/src/core/config.h), you may want to take a look there. > :information_source: reVC uses completely homebrew RenderWare-replacement rendering engine; [librw](https://github.com/aap/librw/). librw comes as submodule of re3, but you also can use LIBRW enviorenment variable to specify path to your own librw. If you feel the need, you can also use CodeWarrior 7 to compile reVC using the supplied codewarrior/reVC.mcp project - this requires the original RW34 libraries, and the DX8 SDK. The build is unstable compared to the MSVC builds though, and is mostly meant to serve as a reference. ## Contributing As long as it's not linux/cross-platform skeleton/compatibility layer, all of the code on the repo that's not behind a preprocessor condition(like FIX_BUGS) are **completely** reversed code from original binaries. We **don't** accept custom codes, as long as it's not wrapped via preprocessor conditions, or it's linux/cross-platform skeleton/compatibility layer. We accept only these kinds of PRs; - A new feature that exists in at least one of the GTAs (if it wasn't in III/VC then it doesn't have to be decompilation) - Game, UI or UX bug fixes (if it's a fix to original code, it should be behind FIX_BUGS) - Platform-specific and/or unused code that's not been reversed yet - Makes reversed code more understandable/accurate, as in "which code would produce this assembly". - A new cross-platform skeleton/compatibility layer, or improvements to them - Translation fixes, for languages original game supported - Code that increase maintainability We have a [Coding Style](https://github.com/GTAmodding/re3/blob/master/CODING_STYLE.md) document that isn't followed or enforced very well. Do not use features from C++11 or later. ## History re3 was started sometime in the spring of 2018, initially as a way to test reversed collision and physics code inside the game. This was done by replacing single functions of the game with their reversed counterparts using a dll. After a bit of work the project lay dormant for about a year and was picked up again and pushed to github in May 2019. At the time I (aap) had reversed around 10k lines of code and estimated the final game to have around 200-250k. Others quickly joined the effort (Fire_Head, shfil, erorcun and Nick007J in time order, and Serge a bit later) and we made very quick progress throughout the summer of 2019 after which the pace slowed down a bit. Due to everyone staying home during the start of the Corona pandemic everybody had a lot of time to work on re3 again and we finally got a standalone exe in April 2020 (around 180k lines by then). After the initial excitement and fixing and polishing the code further, reVC was started in early May 2020 by starting from re3 code, not by starting from scratch replacing functions with a dll. After a few months of mostly steady progress we considered reVC finished in December. Since then we have started reLCS, which is currently work in progress. ## License We don't feel like we're in a position to give this code a license.\ The code should only be used for educational, documentation and modding purposes.\ We do not encourage piracy or commercial use.\ Please keep derivate work open source and give proper credit. ================================================ FILE: cmake/FindMilesSDK.cmake ================================================ # - Find Miles SDK # Find the Miles SDK header + import library # # MilesSDK_INCLUDE_DIR - Where to find mss.h # MilesSDK_LIBRARIES - List of libraries when using MilesSDK. # MilesSDK_FOUND - True if Miles SDK found. # MilesSDK::MilesSDK - Imported library of Miles SDK find_path(MilesSDK_INCLUDE_DIR mss.h PATHS "${MilesSDK_DIR}" PATH_SUFFIXES include ) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_miles_sdk_libname mss64) else() set(_miles_sdk_libname mss32) endif() find_library(MilesSDK_LIBRARIES NAMES ${_miles_sdk_libname} PATHS "${MilesSDK_DIR}" PATH_SUFFIXES lib ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MilesSDK DEFAULT_MSG MilesSDK_LIBRARIES MilesSDK_INCLUDE_DIR) if(NOT TARGET MilesSDK::MilesSDK) add_library(MilesSDK::MilesSDK UNKNOWN IMPORTED) set_target_properties(MilesSDK::MilesSDK PROPERTIES IMPORTED_LOCATION "${MilesSDK_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES "${MilesSDK_INCLUDE_DIR}" ) endif() ================================================ FILE: cmake/FindSndFile.cmake ================================================ # Found on http://hg.kvats.net # # - Try to find libsndfile # # Once done this will define # # SNDFILE_FOUND - system has libsndfile # SNDFILE_INCLUDE_DIRS - the libsndfile include directory # SNDFILE_LIBRARIES - Link these to use libsndfile # SNDFILE_CFLAGS - Compile options to use libsndfile # SndFile::SndFile - Imported library of libsndfile # # Copyright (C) 2006 Wengo # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_search_module(PKG_SNDFILE "sndfile") endif() find_path(SNDFILE_INCLUDE_DIR NAMES sndfile.h HINTS ${PKG_SNDFILE_INCLUDE_DIRS} PATHS /usr/include /usr/local/include /opt/local/include /sw/include ) find_library(SNDFILE_LIBRARY NAMES sndfile HINTS ${PKG_SNDFILE_LIBRARIES} PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) set(SNDFILE_CFLAGS "${PKG_SNDFILE_CFLAGS_OTHER}" CACHE STRING "CFLAGS of libsndfile") set(SNDFILE_INCLUDE_DIRS "${SNDFILE_INCLUDE_DIR}") set(SNDFILE_LIBRARIES "${SNDFILE_LIBRARY}") if(SNDFILE_INCLUDE_DIRS AND SNDFILE_LIBRARIES) set(SNDFILE_FOUND TRUE) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SndFile DEFAULT_MSG SNDFILE_INCLUDE_DIRS SNDFILE_LIBRARIES) if(NOT TARGET SndFile::SndFile) add_library(__SndFile INTERFACE) target_compile_options(__SndFile INTERFACE ${SNDFILE_CFLAGS}) target_include_directories(__SndFile INTERFACE ${SNDFILE_INCLUDE_DIRS}) target_link_libraries(__SndFile INTERFACE ${SNDFILE_LIBRARIES}) add_library(SndFile::SndFile ALIAS __SndFile) endif() ================================================ FILE: cmake/Findmpg123.cmake ================================================ # - Find mpg123 # Find the native mpg123 includes and library # # mpg123_INCLUDE_DIR - Where to find mpg123.h # mpg123_LIBRARIES - List of libraries when using mpg123. # mpg123_CFLAGS - Compile options to use mpg123 # mpg123_FOUND - True if mpg123 found. # MPG123::libmpg123 - Imported library of libmpg123 find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_search_module(PKG_MPG123 mpg123) endif() find_path(mpg123_INCLUDE_DIR mpg123.h HINTS ${PKG_MPG123_INCLUDE_DIRS} PATHS "${mpg123_DIR}" PATH_SUFFIXES include ) find_library(mpg123_LIBRARIES NAMES mpg123 mpg123-0 HINTS ${PKG_MPG123_LIBRARIES} PATHS "${mpg123_DIR}" PATH_SUFFIXES lib ) set(mpg123_CFLAGS "${PKG_MPG123_CFLAGS_OTHER}" CACHE STRING "CFLAGS of mpg123") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(mpg123 DEFAULT_MSG mpg123_LIBRARIES mpg123_INCLUDE_DIR) if(NOT TARGET MPG123::libmpg123) add_library(__libmpg123 INTERFACE) target_compile_options(__libmpg123 INTERFACE ${mpg123_CFLAGS}) target_include_directories(__libmpg123 INTERFACE ${mpg123_INCLUDE_DIR}) target_link_libraries(__libmpg123 INTERFACE ${mpg123_LIBRARIES}) add_library(MPG123::libmpg123 ALIAS __libmpg123) endif() ================================================ FILE: cmake/Findopusfile.cmake ================================================ # - Try to find opusfile # # Once done this will define # # OPUSFILE_FOUND - system has opusfile # OPUSFILE_INCLUDE_DIRS - the opusfile include directories # OPUSFILE_LIBRARIES - Link these to use opusfile # OPUSFILE_CFLAGS - Compile options to use opusfile # opusfile::opusfile - Imported library of opusfile # # FIXME: opusfile does not ship an official opusfile cmake script, # rename this file/variables/target when/if it has. find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_search_module(PKG_OPUSFILE "opusfile") endif() find_path(OPUSFILE_INCLUDE_DIR NAMES opusfile.h PATH_SUFFIXES opusfile HINTS ${PKG_OPUSFILE_INCLUDE_DIRS} PATHS /usr/include /usr/local/include /opt/local/include /sw/include ) find_library(OPUSFILE_LIBRARY NAMES opusfile HINTS ${PKG_OPUSFILE_LIBRARIES} PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) set(OPUSFILE_CFLAGS "${PKG_OPUSFILE_CFLAGS_OTHER}" CACHE STRING "CFLAGS of opusfile") set(OPUSFILE_INCLUDE_DIRS "${OPUSFILE_INCLUDE_DIR}") set(OPUSFILE_LIBRARIES "${OPUSFILE_LIBRARY}") if (OPUSFILE_INCLUDE_DIRS AND OPUSFILE_LIBRARIES) set(OPUSFILE_FOUND TRUE) endif (OPUSFILE_INCLUDE_DIRS AND OPUSFILE_LIBRARIES) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(opusfile DEFAULT_MSG OPUSFILE_INCLUDE_DIRS OPUSFILE_LIBRARIES) if(NOT TARGET opusfile::opusfile) add_library(__opusfile INTERFACE) target_compile_options(__opusfile INTERFACE ${OPUSFILE_CFLAGS}) target_include_directories(__opusfile INTERFACE ${OPUSFILE_INCLUDE_DIRS}) target_link_libraries(__opusfile INTERFACE ${OPUSFILE_LIBRARIES}) add_library(opusfile::opusfile ALIAS __opusfile) endif() ================================================ FILE: cmake/GetGitRevisionDescription.cmake ================================================ # - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) # # Returns the refspec and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_describe_working_tree( [ ...]) # # Returns the results of git describe on the working tree (--dirty option), # and adjusting the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # git_local_changes() # # Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. # Uses the return code of "git diff-index --quiet HEAD --". # Does not regard untracked files. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2020 Ryan Pavlik # http://academic.cleardefinition.com # # Copyright 2009-2013, Iowa State University. # Copyright 2013-2020, Ryan Pavlik # Copyright 2013-2020, Contributors # SPDX-License-Identifier: BSL-1.0 # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) # Function _git_find_closest_git_dir finds the next closest .git directory # that is part of any directory in the path defined by _start_dir. # The result is returned in the parent scope variable whose name is passed # as variable _git_dir_var. If no .git directory can be found, the # function returns an empty string via _git_dir_var. # # Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and # neither foo nor bar contain a file/directory .git. This wil return # C:/bla/.git # function(_git_find_closest_git_dir _start_dir _git_dir_var) set(cur_dir "${_start_dir}") set(git_dir "${_start_dir}/.git") while(NOT EXISTS "${git_dir}") # .git dir not found, search parent directories set(git_previous_parent "${cur_dir}") get_filename_component(cur_dir ${cur_dir} DIRECTORY) if(cur_dir STREQUAL git_previous_parent) # We have reached the root directory, we are not in git set(${_git_dir_var} "" PARENT_SCOPE) return() endif() set(git_dir "${cur_dir}/.git") endwhile() set(${_git_dir_var} "${git_dir}" PARENT_SCOPE) endfunction() function(get_git_head_revision _refspecvar _hashvar) _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE) else() set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE) endif() if(NOT "${GIT_DIR}" STREQUAL "") file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" "${GIT_DIR}") if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) # We've gone above the CMake root dir. set(GIT_DIR "") endif() endif() if("${GIT_DIR}" STREQUAL "") set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() # Check if the current source dir is a git submodule or a worktree. # In both cases .git is a file instead of a directory. # if(NOT IS_DIRECTORY ${GIT_DIR}) # The following git command will return a non empty string that # points to the super project working tree if the current # source dir is inside a git submodule. # Otherwise the command will return an empty string. # execute_process( COMMAND "${GIT_EXECUTABLE}" rev-parse --show-superproject-working-tree WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT "${out}" STREQUAL "") # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule file(READ ${GIT_DIR} submodule) string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule}) string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") else() # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree file(READ ${GIT_DIR} worktree_ref) # The .git directory contains a path to the worktree information directory # inside the parent git repo of the worktree. # string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir ${worktree_ref}) string(STRIP ${git_worktree_dir} git_worktree_dir) _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") endif() else() set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") endif() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() if(NOT EXISTS "${HEAD_SOURCE_FILE}") return() endif() set(HEAD_FILE "${GIT_DATA}/HEAD") configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() function(git_describe _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process( COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_describe_working_tree _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() execute_process( COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_local_changes _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() execute_process( COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(res EQUAL 0) set(${_var} "CLEAN" PARENT_SCOPE) else() set(${_var} "DIRTY" PARENT_SCOPE) endif() endfunction() ================================================ FILE: cmake/GetGitRevisionDescription.cmake.in ================================================ # # Internal file for GetGitRevisionDescription.cmake # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright 2009-2012, Iowa State University # Copyright 2011-2015, Contributors # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) # SPDX-License-Identifier: BSL-1.0 set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) else() configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") set(HEAD_HASH "${CMAKE_MATCH_1}") endif() endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() ================================================ FILE: conanfile.py ================================================ from conans import ConanFile, CMake, tools from conans.errors import ConanException, ConanInvalidConfiguration import os import shutil import textwrap class ReVCConan(ConanFile): name = "reVC" version = "master" license = "???" # FIXME: https://github.com/GTAmodding/re3/issues/794 settings = "os", "arch", "compiler", "build_type" generators = "cmake", "cmake_find_package" options = { "audio": ["openal", "miles"], "with_libsndfile": [True, False], "with_opus": [True, False], } default_options = { "audio": "openal", "with_libsndfile": False, "with_opus": False, # "libsndfile:with_external_libs": False, # "mpg123:flexible_resampling": False, # "mpg123:network": False, # "mpg123:icy": False, # "mpg123:id3v2": False, # "mpg123:ieeefloat": False, # "mpg123:layer1": False, # "mpg123:layer2": False, # "mpg123:layer3": False, # "mpg123:moreinfo": False, # "sdl2:vulkan": False, # "sdl2:opengl": True, # "sdl2:sdl2main": True, } no_copy_source = True @property def _os_is_playstation2(self): try: return self.settings.os == "Playstation2" except ConanException: return False def configure(self): if self.options.audio != "openal": self.options.with_libsndfile = False def requirements(self): self.requires("librw/{}".format(self.version)) self.requires("mpg123/1.26.4") if self.options.audio == "openal": self.requires("openal/1.21.0") elif self.options.audio == "miles": self.requires("miles-sdk/{}".format(self.version)) if self.options.with_libsndfile: self.requires("libsndfile/1.0.30") if self.options.with_opus: self.requires("opusfile/0.12") def export_sources(self): for d in ("cmake", "src"): shutil.copytree(src=d, dst=os.path.join(self.export_sources_folder, d)) self.copy("CMakeLists.txt") def validate(self): if self.options["librw"].platform == "gl3" and self.options["librw"].gl3_gfxlib != "glfw": raise ConanInvalidConfiguration("Only `glfw` is supported as gl3_gfxlib.") #if not self.options.with_opus: # if not self.options["libsndfile"].with_external_libs: # raise ConanInvalidConfiguration("reVC with opus support requires a libsndfile built with external libs (=ogg/flac/opus/vorbis)") @property def _reVC_audio(self): return { "miles": "MSS", "openal": "OAL", }[str(self.options.audio)] def build(self): if self.source_folder == self.build_folder: raise Exception("cannot build with source_folder == build_folder") try: os.unlink(os.path.join(self.install_folder, "Findlibrw.cmake")) tools.save("FindOpenAL.cmake", textwrap.dedent( """ set(OPENAL_FOUND ON) set(OPENAL_INCLUDE_DIR ${OpenAL_INCLUDE_DIRS}) set(OPENAL_LIBRARY ${OpenAL_LIBRARIES}) set(OPENAL_DEFINITIONS ${OpenAL_DEFINITIONS}) """), append=True) if self.options["librw"].platform == "gl3" and self.options["librw"].gl3_gfxlib == "glfw": tools.save("Findglfw3.cmake", textwrap.dedent( """ if(NOT TARGET glfw) message(STATUS "Creating glfw TARGET") add_library(glfw INTERFACE IMPORTED) set_target_properties(glfw PROPERTIES INTERFACE_LINK_LIBRARIES CONAN_PKG::glfw) endif() """), append=True) tools.save("CMakeLists.txt", textwrap.dedent( """ cmake_minimum_required(VERSION 3.0) project(cmake_wrapper) include("{}/conanbuildinfo.cmake") conan_basic_setup(TARGETS NO_OUTPUT_DIRS) add_subdirectory("{}" reVC) """).format(self.install_folder.replace("\\", "/"), self.source_folder.replace("\\", "/"))) except FileNotFoundError: pass cmake = CMake(self) cmake.definitions["REVC_AUDIO"] = self._reVC_audio cmake.definitions["REVC_WITH_OPUS"] = self.options.with_opus cmake.definitions["REVC_INSTALL"] = True cmake.definitions["REVC_VENDORED_LIBRW"] = False env = {} if self._os_is_playstation2: cmake.definitions["CMAKE_TOOLCHAIN_FILE"] = self.deps_user_info["ps2dev-cmaketoolchain"].cmake_toolchain_file env["PS2SDK"] = self.deps_cpp_info["ps2dev-ps2sdk"].rootpath with tools.environment_append(env): cmake.configure(source_folder=self.build_folder) cmake.build() def package(self): cmake = CMake(self) cmake.install() ================================================ FILE: gamefiles/gamecontrollerdb.txt ================================================ # Game Controller DB for SDL in 2.0.9 format # Source: https://github.com/gabomdq/SDL_GameControllerDB # Windows 03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, 03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d000011ab000000000000,8BitDo F30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00015900000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00065280000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000351000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000031000000000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000006f0e00001413000000000000,Afterglow,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d62000001d57000000000000,Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000869800002400000000007801,Astro C40 TR,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, 030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000bc2000000055000000000000,Betop BFM Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000bc2000006321000000000000,BETOP CONTROLLER,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000790000000700000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, 0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, 03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000457500000401000000000000,Cobra,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000a102000000000000,Controller (Xbox 360 Wireless Receiver for Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000ff02000000000000,Controller (Xbox One For Windows) - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000ea02000000000000,Controller (Xbox One For Windows) - Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows, 03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000007d0400000840000000000000,Destroyer Tiltpad,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows, 03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows, 030000008f0e00000910000000000000,DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows, 030000006f0e00003001000000000000,EA SPORTS PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, 03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, 03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, 03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006f0e00008401000000000000,Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00008001000000000000,Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, 030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows, 03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, 03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, 03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000ac0500003d03000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00000102000000007801,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000009b2800003200000000000000,GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, 030000009b2800006000000000000000,GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, 030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000f0250000c283000000000000,Gioteck,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000f0250000c383000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000f0250000c483000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000007d0400000540000000000000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000001008000001e1000000000000,Havit HV-G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, 03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, 03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00001600000000007803,HORI Real Arcade Pro EX-SE (Xbox 360),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows, 030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows, 03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows, 03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 030000006f0e00002401000000000000,INJUSTICE FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000ac0500002c02000000000000,IPEGA,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000491900000304000000000000,Ipega PG-9087 - Bluetooth Gamepad,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows, 030000006e0500000a20000000000000,JC-DUX60 ELECOM MMO Gamepad,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows, 030000006e0500000520000000000000,JC-P301U,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, 030000006e0500000320000000000000,JC-U3613M (DInput),a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, 030000006e0500000720000000000000,JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, 030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows, 030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows, 030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows, 03000000242f00002d00000000000000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, 03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows, 030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d0400001ac2000000000000,Logitech Precision Gamepad,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows, 03000000380700006652000000000000,Mad Catz C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008433000000000000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008483000000000000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700006252000000000000,Mad Catz Micro C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, 03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, 0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows, 0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, 03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000790000002418000000000000,Mega Drive,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows, 03000000380700006382000000000000,MLG GamePad PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000c62400002a89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c62400002b89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c62400001a89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c62400001b89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 030000006b140000010c000000000000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows, 03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, 030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows, 03000000550900001472000000000000,NVIDIA Controller v01.04,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows, 030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, 03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows, 03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows, 03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00000901000000000000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000004c050000da0c000000000000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, 03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000666600006706000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows, 030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, 030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows, 03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, 030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000003807000056a8000000000000,PS3 RF pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, 03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, 03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, 03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000321500000204000000000000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000104000000000000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000321500000011000000000000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000bd12000013d0000000000000,Retrolink USB SEGA Saturn Classic,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows, 0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, 0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, 030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006b140000020d000000000000,Revolution Pro Controller 2(1/2),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Windows, 03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,platform:Windows, 03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows, 03000000a30600002106000000000000,Saitek PS1000,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000a306000020f6000000000000,Saitek PS2700,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows, 0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, 030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, 030000005e0400008e02000000007801,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows, 03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, 03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows, 03000000790000001c18000000000000,STK-7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows, 03000000d620000011a7000000000000,Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000457500002211000000000000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000004f04000007d0000000000000,T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000004f0400000ab1000000000000,T.16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows, 03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, 030000004f04000023b3000000000000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004f0400000ed0000000000000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows, 030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, 03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows, 03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000006e0500001320000000000000,U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000300f00000701000000000000,USB 4-Axis 12-Button Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000341a00002308000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000005509000000b4000000000000,USB gamepad,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,platform:Windows, 030000006b1400000203000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000790000000a00000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000f0250000c183000000000000,USB gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ff1100004133000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000790000001a18000000000000,Venom,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00000302000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00000702000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:+a3,righty:+a4,start:b4,x:b2,y:b3,platform:Windows, 030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000ff02000000007801,Xbox One Elite Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000ac0500005b05000000000000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000790000004f18000000000000,ZD-T Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000120c0000101e000000000000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, # Mac OS X 030000008f0e00000300000009010000,2In1 USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00005106000000010000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 030000003512000012ab000001000000,8BitDo NES30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000260000001000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000031000001000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006f0e00000102000000000000,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000ee00000000010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X, 03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, 03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, 030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000242f00002d00000007010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000006d04000018c2000000010000,Logitech RumblePad 2 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700008483000000010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X, 0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X, 03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, 03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X, 03000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, 03000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c62400002b89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000632500007505000000020000,NEOGEO mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X, 030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000d620000011a7000000020000,Nintendo Switch Core (Plus) Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X, 030000006f0e00000901000002010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X, 030000004c050000da0c000000010000,Playstation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000321500000204000000010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000104000000010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000321500000011000000010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000c6240000fefa000000000000,Rock Candy Gamepad for PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X, 03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, 03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, 030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X, 030000004c050000e60c000000010000,Sony DualSense,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X, 03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, 03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, 03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X, 03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X, 03000000457500002211000000010000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, 030000004f0400000ed0000000020000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, 03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, 030000006f0e00000302000025040000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006f0e00000702000003060000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000791d00000103000009010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, 050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, 030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000120c0000101e000000010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, # Linux 03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00001038000000010000,8Bitdo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, 05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, 03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 030000003512000012ab000010010000,8Bitdo SFC30 GamePad,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux, 05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000260000011010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000202800000900000000010000,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 030000005e0400008e02000020010000,8BitDo Wireless Adapter (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000031000011010000,8BitDo Wireless Adapter (DInput),a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00003901000013020000,Afterglow Prismatic Wired Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, 05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, 03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, 03000000c62400001b89000011010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000c21100000791000011010000,Be1 GC101 Controller 1.03 mode,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000c31100000791000011010000,Be1 GC101 GAMEPAD 1.03 mode,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e0400008e02000003030000,Be1 GC101 Xbox 360 Controller mode,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, 03000000ffff0000ffff000000010000,Chinese-made Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux, 03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux, 03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, 03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, 030000004f04000004b3000010010000,Dual Power 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000007d0400000540000000010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux, 030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, 03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000632500002605000010010000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, 030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f0000c100000011010000,HORI CO. LTD. HORIPAD S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00008500000010010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00008600000002010000,Hori Fighting Commander,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f0000aa00000011010000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000000d0f0000d800000072056800,HORI Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 030000000d0f00001600000000010000,Hori Real Arcade Pro.EX-SE (Xbox 360),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux, 03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, 050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000d80400008200000003000000,IMS PCU#0 Gamepad Interface,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux, 03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux, 0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000632500007505000011010000,Ipega PG-9099 - Bluetooth Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, 03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux, 050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, 03000000242f00002d00000011010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, 030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d0400001ec2000019200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux, 030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux, 050000004d4f435554452d3035305800,M54-PC,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Linux, 05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, 0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, 03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, 030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000050b000003090000,Microsoft X-Box One Elite 2 pad,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 030000005e040000000b000008040000,Microsoft Xbox One Elite 2 pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000ea02000008040000,Microsoft Xbox One S pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c62400001a53000000010000,Mini PE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, 05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 03000000c62400002b89000011010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c62400001a89000000010000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000006b140000010c000010010000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux, 060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, 030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux, 03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b0,y:b3,platform:Linux, 050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500000920000001800000,Nintendo Switch Pro Controller (joycond),a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, 030000007e0500000920000011810000,Nintendo Switch Pro Controller Wired (joycond),a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, 050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, 05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux, 03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, 05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, 03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 19000000010000000100000001010000,odroidgo2_joypad,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux, 19000000010000000200000011000000,odroidgo2_joypad_v11,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux, 030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, 05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, 03000000830500005020000010010000,Padix Co. Ltd. Rockfire PSX/USB Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux, 03000000790000001c18000011010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006f0e0000b802000001010000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000b802000013020000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00008001000011010000,PDP CO. LTD. Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00008701000011010000,PDP Rock Candy Wired Controller for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006f0e00000901000011010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000006f0e00008501000011010000,PDP Wired Fight Pad Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 05000000491900000204000000000000,PG-9118,x:b76,a:b73,b:b74,y:b77,back:b83,start:b84,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b79,lefttrigger:b81,rightshoulder:b80,righttrigger:b82,leftstick:b86,rightstick:b87,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux, 0500000049190000030400001b010000,PG-9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000c62400001a58000001010000,PowerA Xbox One Cabled,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux, 030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800006000000001010000,Raphnet Technologies GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, 030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000810000011010000,Razer Panthera Evo Arcade Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000321500000011000011010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, 0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux, 0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, 030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000a30600001005000000010000,Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux, 03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Linux, 03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,platform:Linux, 03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux, 03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux, 03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux, 03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000a306000020f6000011010000,Saitek PS2700 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux, 03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux, 03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d11800000094000011010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, 03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, 03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000003b07000004a1000000010000,Suncom SFX Plus for USB,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux, 03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, 03000000457500002211000010010000,SZMY-POWER CO. LTD. GAMEPAD,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000008f0e00000d31000010010000,SZMY-POWER CO. LTD. GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000008f0e00001431000010010000,SZMY-POWER CO.,LTD. PS3 gamepad,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux, 030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004f0400000ed0000011010000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,platform:Linux, 030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,platform:Linux, 030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux, 030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004f04000007d0000000010000,Thrustmaster T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, 030000005e0400008e02000070050000,Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c01100000591000011010000,Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, 03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, 030000006f0e00000302000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00000702000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000791d00000103000010010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 050000000d0f0000f600000001000000,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, 030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e040000ea02000000000000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e040000ea02000001030000,Xbox One Wireless Controller (Model 1708),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e0400008e02000000010000,xbox360 Wireless EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000ac0500005b05000010010000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux, 03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000120c0000101e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000c0160000dc27000001010000,OnyxSoft Dual JoyDivision,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b6,leftshoulder:b4,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0, # Android 05000000c82d000006500000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000051060000ffff3f00,8BitDo M30 Gamepad,a:b1,b:b0,back:b4,guide:b17,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000000610000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000012900000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000062280000ffff3f00,8BitDo SN30 Gamepad,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000002600000ffff0f00,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000002028000009000000ffff3f00,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000003512000020ab000000780f00,8BitDo SNES30 Gamepad,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android, 05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 0500000031366332860c44aadfff0f00,GS Gamepad,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b3,y:b2,platform:Android, 64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, 7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Android, 050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android, 37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000003720000cf7f3f00,NVIDIA Controller v01.01,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000010720000ffff3f00,NVIDIA Controller v01.03,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000014720000df7f3f00,NVIDIA Controller v01.04,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 030000004c050000cc09000000006800,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000005070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000007070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, 32633532643734376632656664383733,Sony DualSense,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, 61303162353165316365336436343139,Sony DualSense,a:b1,b:b19,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, 050000004f0400000ed00000fffe3f00,ThrustMaster eSwap PRO Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android, 30306539356238653637313730656134,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, 050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, 050000005e040000fd020000ffff3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android, 050000001727000044310000ffff3f00,XiaoMi Game Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, # iOS 05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, 05000000ac050000010000004f066d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, 05000000ac05000001000000cf076d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, 05000000ac05000001000000df076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 05000000ac05000001000000ff076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, 05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, 4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, 4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, 050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000004c050000cc090000ff070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 050000004c050000cc090000ff870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,touchpad:b11,x:b2,y:b3,platform:iOS, 05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, 05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS, 050000005e040000050b0000ff070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000005e040000e0020000ff070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, ================================================ FILE: premake-vs2015.cmd ================================================ premake5 vs2015 --with-librw ================================================ FILE: premake-vs2017.cmd ================================================ premake5 vs2017 --with-librw ================================================ FILE: premake-vs2019.cmd ================================================ premake5 vs2019 --with-librw ================================================ FILE: premake5.lua ================================================ newoption { trigger = "glfwdir64", value = "PATH", description = "Directory of glfw", default = "vendor/glfw-3.3.2.bin.WIN64", } newoption { trigger = "glfwdir32", value = "PATH", description = "Directory of glfw", default = "vendor/glfw-3.3.2.bin.WIN32", } newoption { trigger = "with-asan", description = "Build with address sanitizer" } newoption { trigger = "with-librw", description = "Build and use librw from this solution" } newoption { trigger = "with-opus", description = "Build with opus" } newoption { trigger = "lto", description = "Use link time optimization" } if(_OPTIONS["with-librw"]) then Librw = "vendor/librw" else Librw = os.getenv("LIBRW") or "vendor/librw" end function getsys(a) if a == 'windows' then return 'win' end return a end function getarch(a) if a == 'x86_64' then return 'amd64' elseif a == 'ARM' then return 'arm' elseif a == 'ARM64' then return 'arm64' end return a end workspace "reVC" language "C++" configurations { "Debug", "Release", "Vanilla" } startproject "reVC" location "build" symbols "Full" staticruntime "off" if _OPTIONS["with-asan"] then buildoptions { "-fsanitize=address -g3 -fno-omit-frame-pointer" } linkoptions { "-fsanitize=address" } end filter { "system:windows" } platforms { "win-x86-RW34_d3d8-mss", "win-x86-librw_d3d9-mss", "win-x86-librw_gl3_glfw-mss", "win-x86-RW34_d3d8-oal", "win-x86-librw_d3d9-oal", "win-x86-librw_gl3_glfw-oal", "win-amd64-librw_d3d9-oal", "win-amd64-librw_gl3_glfw-oal", } filter { "system:linux" } platforms { "linux-x86-librw_gl3_glfw-oal", "linux-amd64-librw_gl3_glfw-oal", "linux-arm-librw_gl3_glfw-oal", "linux-arm64-librw_gl3_glfw-oal", } filter { "system:bsd" } platforms { "bsd-x86-librw_gl3_glfw-oal", "bsd-amd64-librw_gl3_glfw-oal", "bsd-arm-librw_gl3_glfw-oal", "bsd-arm64-librw_gl3_glfw-oal" } filter { "system:macosx" } platforms { "macosx-arm64-librw_gl3_glfw-oal", "macosx-amd64-librw_gl3_glfw-oal", } filter "configurations:Debug" defines { "DEBUG" } filter "configurations:not Debug" defines { "NDEBUG" } optimize "Speed" if(_OPTIONS["lto"]) then flags { "LinkTimeOptimization" } end filter "configurations:Vanilla" defines { "VANILLA_DEFINES" } filter { "platforms:win*" } system "windows" filter { "platforms:linux*" } system "linux" filter { "platforms:bsd*" } system "bsd" filter { "platforms:macosx*" } system "macosx" filter { "platforms:*x86*" } architecture "x86" filter { "platforms:*amd64*" } architecture "amd64" filter { "platforms:*arm*" } architecture "ARM" filter { "platforms:macosx-arm64-*" } buildoptions { "-target", "arm64-apple-macos11", "-std=gnu++14" } filter { "platforms:macosx-amd64-*" } buildoptions { "-target", "x86_64-apple-macos10.12", "-std=gnu++14" } filter { "platforms:*librw_d3d9*" } defines { "RW_D3D9" } if(not _OPTIONS["with-librw"]) then libdirs { path.join(Librw, "lib/win-%{getarch(cfg.architecture)}-d3d9/%{cfg.buildcfg}") } end filter "platforms:*librw_gl3_glfw*" defines { "RW_GL3" } if(not _OPTIONS["with-librw"]) then libdirs { path.join(Librw, "lib/%{getsys(cfg.system)}-%{getarch(cfg.architecture)}-gl3/%{cfg.buildcfg}") } end filter "platforms:*x86-librw_gl3_glfw*" includedirs { path.join(_OPTIONS["glfwdir32"], "include") } filter "platforms:*amd64-librw_gl3_glfw*" includedirs { path.join(_OPTIONS["glfwdir64"], "include") } filter {} function setpaths (gamepath, exepath) if (gamepath) then postbuildcommands { '{COPYFILE} "%{cfg.buildtarget.abspath}" "' .. gamepath .. '%{cfg.buildtarget.name}"' } debugdir (gamepath) if (exepath) then -- Used VS variable $(TargetFileName) because it doesn't accept premake tokens. Does debugcommand even work outside VS?? debugcommand (gamepath .. "$(TargetFileName)") dir, file = exepath:match'(.*/)(.*)' debugdir (gamepath .. (dir or "")) end end end if(_OPTIONS["with-librw"]) then project "librw" kind "StaticLib" targetname "rw" targetdir(path.join(Librw, "lib/%{cfg.platform}/%{cfg.buildcfg}")) files { path.join(Librw, "src/*.*") } files { path.join(Librw, "src/*/*.*") } files { path.join(Librw, "src/gl/*/*.*") } filter { "platforms:*x86*" } architecture "x86" filter { "platforms:*amd64*" } architecture "amd64" filter "platforms:win*" defines { "_CRT_SECURE_NO_WARNINGS", "_CRT_NONSTDC_NO_DEPRECATE" } staticruntime "on" buildoptions { "/Zc:sizedDealloc-" } filter "platforms:bsd*" includedirs { "/usr/local/include" } libdirs { "/usr/local/lib" } filter "platforms:macosx*" -- Support MacPorts and Homebrew includedirs { "/opt/local/include" } includedirs {"/usr/local/include" } libdirs { "/opt/local/lib" } libdirs { "/usr/local/lib" } filter "platforms:*gl3_glfw*" staticruntime "off" filter "platforms:*RW34*" flags { "ExcludeFromBuild" } filter {} end local function addSrcFiles( prefix ) return prefix .. "/*cpp", prefix .. "/*.h", prefix .. "/*.c", prefix .. "/*.ico", prefix .. "/*.rc" end project "reVC" kind "WindowedApp" targetname "reVC" targetdir "bin/%{cfg.platform}/%{cfg.buildcfg}" if(_OPTIONS["with-librw"]) then dependson "librw" end files { addSrcFiles("src") } files { addSrcFiles("src/animation") } files { addSrcFiles("src/audio") } files { addSrcFiles("src/audio/eax") } files { addSrcFiles("src/audio/oal") } files { addSrcFiles("src/buildings") } files { addSrcFiles("src/collision") } files { addSrcFiles("src/control") } files { addSrcFiles("src/core") } files { addSrcFiles("src/entities") } files { addSrcFiles("src/math") } files { addSrcFiles("src/modelinfo") } files { addSrcFiles("src/objects") } files { addSrcFiles("src/peds") } files { addSrcFiles("src/render") } files { addSrcFiles("src/rw") } files { addSrcFiles("src/save") } files { addSrcFiles("src/skel") } files { addSrcFiles("src/skel/glfw") } files { addSrcFiles("src/text") } files { addSrcFiles("src/vehicles") } files { addSrcFiles("src/weapons") } files { addSrcFiles("src/extras") } files { "src/extras/GitSHA1.cpp" } -- this won't be in repo in first build includedirs { "src" } includedirs { "src/animation" } includedirs { "src/audio" } includedirs { "src/audio/eax" } includedirs { "src/audio/oal" } includedirs { "src/buildings" } includedirs { "src/collision" } includedirs { "src/control" } includedirs { "src/core" } includedirs { "src/entities" } includedirs { "src/math" } includedirs { "src/modelinfo" } includedirs { "src/objects" } includedirs { "src/peds" } includedirs { "src/render" } includedirs { "src/rw" } includedirs { "src/save/" } includedirs { "src/skel/" } includedirs { "src/skel/glfw" } includedirs { "src/text" } includedirs { "src/vehicles" } includedirs { "src/weapons" } includedirs { "src/extras" } if _OPTIONS["with-opus"] then includedirs { "vendor/ogg/include" } includedirs { "vendor/opus/include" } includedirs { "vendor/opusfile/include" } end filter "platforms:*mss" defines { "AUDIO_MSS" } includedirs { "vendor/milessdk/include" } libdirs { "vendor/milessdk/lib" } if _OPTIONS["with-opus"] then filter "platforms:win*" libdirs { "vendor/ogg/win32/VS2015/Win32/%{cfg.buildcfg}" } libdirs { "vendor/opus/win32/VS2015/Win32/%{cfg.buildcfg}" } libdirs { "vendor/opusfile/win32/VS2015/Win32/Release-NoHTTP" } filter {} defines { "AUDIO_OPUS" } end filter "platforms:*oal" defines { "AUDIO_OAL" } filter {} if(os.getenv("GTA_VC_RE_DIR")) then setpaths(os.getenv("GTA_VC_RE_DIR") .. "/", "%(cfg.buildtarget.name)") end filter "platforms:win*" files { addSrcFiles("src/skel/win") } includedirs { "src/skel/win" } buildoptions { "/Zc:sizedDealloc-" } linkoptions "/SAFESEH:NO" characterset ("MBCS") targetextension ".exe" if(_OPTIONS["with-librw"]) then -- external librw is dynamic staticruntime "on" end prebuildcommands { '"%{prj.location}..\\printHash.bat" "%{prj.location}..\\src\\extras\\GitSHA1.cpp"' } filter "platforms:not win*" prebuildcommands { '"%{prj.location}/../printHash.sh" "%{prj.location}/../src/extras/GitSHA1.cpp"' } filter "platforms:win*glfw*" staticruntime "off" filter "platforms:win*oal" includedirs { "vendor/openal-soft/include" } includedirs { "vendor/libsndfile/include" } includedirs { "vendor/mpg123/include" } filter "platforms:win-x86*oal" libdirs { "vendor/mpg123/lib/Win32" } libdirs { "vendor/libsndfile/lib/Win32" } libdirs { "vendor/openal-soft/libs/Win32" } filter "platforms:win-amd64*oal" libdirs { "vendor/mpg123/lib/Win64" } libdirs { "vendor/libsndfile/lib/Win64" } libdirs { "vendor/openal-soft/libs/Win64" } filter "platforms:linux*oal" links { "openal", "mpg123", "sndfile", "pthread", "X11" } filter "platforms:bsd*oal" links { "openal", "mpg123", "sndfile", "pthread", "X11" } filter "platforms:macosx*oal" links { "openal", "mpg123", "sndfile", "pthread" } includedirs { "/usr/local/opt/openal-soft/include" } libdirs { "/usr/local/opt/openal-soft/lib" } if _OPTIONS["with-opus"] then filter {} links { "libogg" } links { "opus" } links { "opusfile" } end filter "platforms:*RW34*" includedirs { "sdk/rwsdk/include/d3d8" } libdirs { "sdk/rwsdk/lib/d3d8/release" } links { "rwcore", "rpworld", "rpmatfx", "rpskin", "rphanim", "rtbmp", "rtquat", "rtanim", "rtcharse", "rpanisot" } defines { "RWLIBS" } linkoptions "/SECTION:_rwcseg,ER!W /MERGE:_rwcseg=.text" filter "platforms:*librw*" defines { "LIBRW" } files { addSrcFiles("src/fakerw") } includedirs { "src/fakerw" } includedirs { Librw } if(_OPTIONS["with-librw"]) then libdirs { "vendor/librw/lib/%{cfg.platform}/%{cfg.buildcfg}" } end links { "rw" } filter "platforms:*d3d9*" defines { "USE_D3D9" } links { "d3d9" } filter "platforms:*x86*d3d*" includedirs { "sdk/dx8sdk/include" } libdirs { "sdk/dx8sdk/lib" } filter "platforms:win-x86*gl3_glfw*" libdirs { path.join(_OPTIONS["glfwdir32"], "lib-" .. string.gsub(_ACTION or '', "vs", "vc")) } links { "opengl32", "glfw3" } filter "platforms:win-amd64*gl3_glfw*" libdirs { path.join(_OPTIONS["glfwdir64"], "lib-" .. string.gsub(_ACTION or '', "vs", "vc")) } links { "opengl32", "glfw3" } filter "platforms:linux*gl3_glfw*" links { "GL", "glfw" } filter "platforms:bsd*gl3_glfw*" links { "GL", "glfw", "sysinfo" } includedirs { "/usr/local/include" } libdirs { "/usr/local/lib" } filter "platforms:macosx*gl3_glfw*" links { "glfw" } linkoptions { "-framework OpenGL" } includedirs { "/opt/local/include" } includedirs { "/usr/local/include" } libdirs { "/opt/local/lib" } libdirs { "/usr/local/lib" } ================================================ FILE: printHash.bat ================================================ @echo off REM creates version.h with HEAD commit hash REM params: $1=full path to output file (usually points version.h) setlocal enableextensions enabledelayedexpansion cd /d "%~dp0" break> %1 %1 where git if "%errorlevel%" == "0" ( goto :havegit ) else ( goto :writeending ) :havegit for /f %%v in ('git rev-parse --short HEAD') do set version=%%v > %1 :writeending echo ^" >> %1 echo const char* g_GIT_SHA1 = GIT_SHA1; >> %1 EXIT /B ================================================ FILE: printHash.sh ================================================ #!/usr/bin/env bash > $1 echo -n "#define GIT_SHA1 \"" > $1 if (command -v "git" >/dev/null) then git rev-parse --short HEAD | tr -d '\n' >> $1 fi echo "\"" >> $1 echo "const char* g_GIT_SHA1 = GIT_SHA1;" >> $1 ================================================ FILE: src/CMakeLists.txt ================================================ find_package(Threads REQUIRED) set(THREADS_PREFER_PTHREAD_FLAG ON) file(GLOB_RECURSE ${PROJECT}_SOURCES "*.cpp" "*.h" "*.rc") function(header_directories RETURN_LIST) file(GLOB_RECURSE ALL_SRCS *.h *.cpp *.c) set(RELDIRS) foreach(SRC ${ALL_SRCS}) file(RELATIVE_PATH RELSRC "${CMAKE_CURRENT_SOURCE_DIR}" "${SRC}") get_filename_component(RELDIR "${RELSRC}" DIRECTORY) list(APPEND RELDIRS ${RELDIR}) endforeach() list(REMOVE_DUPLICATES RELDIRS) set(${RETURN_LIST} ${RELDIRS} PARENT_SCOPE) endfunction() header_directories(${PROJECT}_INCLUDES) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/extras/GitSHA1.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/extras/GitSHA1.cpp" @ONLY) list(APPEND ${PROJECT}_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/extras/GitSHA1.cpp") add_executable(${EXECUTABLE} WIN32 ${${PROJECT}_SOURCES} ) target_link_libraries(${EXECUTABLE} PRIVATE librw::librw Threads::Threads ) target_include_directories(${EXECUTABLE} PRIVATE $ $ ) target_compile_definitions(${EXECUTABLE} PRIVATE $,DEBUG,NDEBUG> LIBRW CMAKE_NO_AUTOLINK ) if(LIBRW_PLATFORM_D3D9) target_compile_definitions(${EXECUTABLE} PUBLIC USE_D3D9 ) endif() target_compile_definitions(${EXECUTABLE} PRIVATE CMAKE_BUILD) if(${PROJECT}_AUDIO STREQUAL "OAL") find_package(OpenAL REQUIRED) target_include_directories(${EXECUTABLE} PRIVATE ${OPENAL_INCLUDE_DIR}) target_link_libraries(${EXECUTABLE} PRIVATE ${OPENAL_LIBRARY}) target_compile_definitions(${EXECUTABLE} PRIVATE ${OPENAL_DEFINITIONS}) target_compile_definitions(${EXECUTABLE} PRIVATE AUDIO_OAL) elseif(${PROJECT}_AUDIO STREQUAL "MSS") find_package(MilesSDK REQUIRED) target_compile_definitions(${EXECUTABLE} PRIVATE AUDIO_MSS) target_link_libraries(${EXECUTABLE} PRIVATE MilesSDK::MilesSDK) endif() find_package(mpg123 REQUIRED) target_link_libraries(${EXECUTABLE} PRIVATE MPG123::libmpg123 ) if(${PROJECT}_WITH_OPUS) find_package(opusfile REQUIRED) target_link_libraries(${EXECUTABLE} PRIVATE opusfile::opusfile ) target_compile_definitions(${EXECUTABLE} PRIVATE AUDIO_OPUS) endif() if(${PROJECT}_WITH_LIBSNDFILE) find_package(SndFile REQUIRED) target_link_libraries(${EXECUTABLE} PRIVATE SndFile::SndFile ) target_compile_definitions(${EXECUTABLE} PRIVATE AUDIO_OAL_USE_SNDFILE) endif() target_compile_definitions(${EXECUTABLE} PRIVATE ) option(${PROJECT}_WITH_SANITIZERS "Use UB sanitizers (better crash log)" OFF) option(${PROJECT}_WITH_ASAN "Use Address sanitizer (better crash log)" OFF) if(${PROJECT}_WITH_SANITIZERS) target_compile_options(${EXECUTABLE} PUBLIC -fsanitize=undefined,float-divide-by-zero,integer,implicit-conversion,implicit-integer-truncation,implicit-integer-arithmetic-value-change,local-bounds,nullability -g3 -fno-omit-frame-pointer) target_link_options(${EXECUTABLE} PUBLIC -fsanitize=undefined,float-divide-by-zero,integer,implicit-conversion,implicit-integer-truncation,implicit-integer-arithmetic-value-change,local-bounds,nullability) endif() if(${PROJECT}_WITH_ASAN) target_compile_options(${EXECUTABLE} PUBLIC -fsanitize=address -g3 -fno-omit-frame-pointer) target_link_options(${EXECUTABLE} PUBLIC -fsanitize=address) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") target_compile_options(${EXECUTABLE} PRIVATE "-Wall" ) if (NOT LIBRW_PLATFORM_PS2) target_compile_options(${EXECUTABLE} PRIVATE -Wextra -Wdouble-promotion -Wpedantic ) endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options(${EXECUTABLE} PUBLIC /Zc:sizedDealloc- ) endif() set_target_properties(${EXECUTABLE} PROPERTIES C_STANDARD 11 C_EXTENSIONS OFF C_STANDARD_REQUIRED ON CXX_STANDARD 11 CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON ) if(${PROJECT}_INSTALL) install( TARGETS ${EXECUTABLE} EXPORT ${EXECUTABLE}-targets RUNTIME DESTINATION "." ) if(MSVC) install(FILES $ DESTINATION "." OPTIONAL) endif() endif() ================================================ FILE: src/animation/AnimBlendAssocGroup.cpp ================================================ #include "common.h" #if defined _WIN32 && !defined __MINGW32__ #if defined __MWERKS__ #include #else #include "ctype.h" #endif #else #include #endif #include "General.h" #include "RwHelper.h" #include "ModelIndices.h" #include "ModelInfo.h" #include "AnimManager.h" #include "RpAnimBlend.h" #include "AnimBlendAssociation.h" #include "AnimBlendAssocGroup.h" CAnimBlendAssocGroup::CAnimBlendAssocGroup(void) { animBlock = nil; assocList = nil; numAssociations = 0; firstAnimId = 0; groupId = -1; } CAnimBlendAssocGroup::~CAnimBlendAssocGroup(void) { DestroyAssociations(); } void CAnimBlendAssocGroup::DestroyAssociations(void) { if(assocList){ delete[] assocList; assocList = nil; numAssociations = 0; } } CAnimBlendAssociation* CAnimBlendAssocGroup::GetAnimation(uint32 id) { return &assocList[id - firstAnimId]; } CAnimBlendAssociation* CAnimBlendAssocGroup::GetAnimation(const char *name) { int i; for(i = 0; i < numAssociations; i++) if(!CGeneral::faststricmp(assocList[i].hierarchy->name, name)) return &assocList[i]; debug("\n\nCan't find the fucking animation %s\n\n\n", name); return nil; } CAnimBlendAssociation* CAnimBlendAssocGroup::CopyAnimation(uint32 id) { CAnimBlendAssociation *anim = GetAnimation(id); if(anim == nil) return nil; CAnimManager::UncompressAnimation(anim->hierarchy); return new CAnimBlendAssociation(*anim); } CAnimBlendAssociation* CAnimBlendAssocGroup::CopyAnimation(const char *name) { CAnimBlendAssociation *anim = GetAnimation(name); if(anim == nil) return nil; CAnimManager::UncompressAnimation(anim->hierarchy); return new CAnimBlendAssociation(*anim); } bool strcmpIgnoringDigits(const char *s1, const char *s2) { char c1, c2; for(;;){ c1 = *s1; c2 = *s2; if(c1) s1++; if(c2) s2++; if(c1 == '\0' && c2 == '\0') return true; #ifndef ASCII_STRCMP if(iswdigit(c1) && iswdigit(c2)) #else if(__ascii_iswdigit(c1) && __ascii_iswdigit(c2)) #endif continue; #ifndef ASCII_STRCMP c1 = toupper(c1); c2 = toupper(c2); #else c1 = __ascii_toupper(c1); c2 = __ascii_toupper(c2); #endif if(c1 && c2 && c1 != c2) return false; } } CBaseModelInfo* GetModelFromName(const char *name) { int i; CBaseModelInfo *mi; char playername[32]; if(strncasecmp(name, "CSplay", 6) == 0 && strncasecmp(CModelInfo::GetModelInfo(MI_PLAYER)->GetModelName(), "ig", 2) == 0){ strcpy(playername, CModelInfo::GetModelInfo(MI_PLAYER)->GetModelName()); playername[0] = 'C'; playername[1] = 'S'; name = playername; } for(i = 0; i < MODELINFOSIZE; i++){ mi = CModelInfo::GetModelInfo(i); if(mi && mi->GetRwObject() && RwObjectGetType(mi->GetRwObject()) == rpCLUMP && strcmpIgnoringDigits(mi->GetModelName(), name)) return mi; } return nil; } void CAnimBlendAssocGroup::CreateAssociations(const char *name) { int i; CAnimBlock *animBlock; DestroyAssociations(); animBlock = CAnimManager::GetAnimationBlock(name); assocList = new CAnimBlendAssociation[animBlock->numAnims]; numAssociations = 0; for(i = 0; i < animBlock->numAnims; i++){ CAnimBlendHierarchy *anim = CAnimManager::GetAnimation(animBlock->firstIndex + i); CBaseModelInfo *model = GetModelFromName(anim->name); if(model){ debug("Associated anim %s with model %s\n", anim->name, model->GetModelName()); RpClump *clump = (RpClump*)model->CreateInstance(); RpAnimBlendClumpInit(clump); assocList[i].Init(clump, anim); if(IsClumpSkinned(clump)) RpClumpForAllAtomics(clump, AtomicRemoveAnimFromSkinCB, nil); RpClumpDestroy(clump); assocList[i].animId = firstAnimId + i; assocList[i].groupId = groupId; }else debug("\n\nCANNOT FIND MODELINFO WITH NAME %s\n\n\n", anim->name); } numAssociations = animBlock->numAnims; } // Create associations from hierarchies for a given clump void CAnimBlendAssocGroup::CreateAssociations(const char *blockName, RpClump *clump, const char **animNames, int numAssocs) { int i; DestroyAssociations(); animBlock = CAnimManager::GetAnimationBlock(blockName); assocList = new CAnimBlendAssociation[numAssocs]; numAssociations = 0; for(i = 0; i < numAssocs; i++){ assocList[i].Init(clump, CAnimManager::GetAnimation(animNames[i], animBlock)); assocList[i].animId = firstAnimId + i; assocList[i].groupId = groupId; } numAssociations = numAssocs; } ================================================ FILE: src/animation/AnimBlendAssocGroup.h ================================================ #pragma once class CAnimBlendAssociation; struct CAnimBlock; class CAnimBlendAssocGroup { public: CAnimBlock *animBlock; CAnimBlendAssociation *assocList; int32 numAssociations; int32 firstAnimId; int32 groupId; // id of self in ms_aAnimAssocGroups CAnimBlendAssocGroup(void); ~CAnimBlendAssocGroup(void); void DestroyAssociations(void); CAnimBlendAssociation *GetAnimation(uint32 id); CAnimBlendAssociation *GetAnimation(const char *name); CAnimBlendAssociation *CopyAnimation(uint32 id); CAnimBlendAssociation *CopyAnimation(const char *name); void CreateAssociations(const char *name); void CreateAssociations(const char *blockName, RpClump *clump, const char **animNames, int numAssocs); }; ================================================ FILE: src/animation/AnimBlendAssociation.cpp ================================================ #include "common.h" #include "AnimBlendHierarchy.h" #include "AnimBlendClumpData.h" #include "RpAnimBlend.h" #include "AnimManager.h" #include "AnimBlendAssociation.h" #include "MemoryMgr.h" CAnimBlendAssociation::CAnimBlendAssociation(void) { groupId = -1; nodes = nil; blendAmount = 1.0f; blendDelta = 0.0f; currentTime = 0.0f; speed = 1.0f; timeStep = 0.0f; animId = -1; flags = 0; callbackType = CB_NONE; link.Init(); } CAnimBlendAssociation::CAnimBlendAssociation(CAnimBlendAssociation &other) { nodes = nil; blendAmount = 1.0f; blendDelta = 0.0f; currentTime = 0.0f; speed = 1.0f; timeStep = 0.0f; callbackType = CB_NONE; link.Init(); Init(other); } CAnimBlendAssociation::~CAnimBlendAssociation(void) { FreeAnimBlendNodeArray(); link.Remove(); } void CAnimBlendAssociation::AllocateAnimBlendNodeArray(int n) { int i; nodes = (CAnimBlendNode*)RwMallocAlign(n*sizeof(CAnimBlendNode), 64); for(i = 0; i < n; i++) nodes[i].Init(); } void CAnimBlendAssociation::FreeAnimBlendNodeArray(void) { if(nodes) RwFreeAlign(nodes); } void CAnimBlendAssociation::Init(RpClump *clump, CAnimBlendHierarchy *hier) { int i; AnimBlendFrameData *frame; CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); numNodes = clumpData->numFrames; AllocateAnimBlendNodeArray(numNodes); for(i = 0; i < numNodes; i++) nodes[i].association = this; hierarchy = hier; // Init every node from a sequence and a Clump frame // NB: This is where the order of nodes is defined for(i = 0; i < hier->numSequences; i++){ CAnimBlendSequence *seq = &hier->sequences[i]; if(seq->boneTag == -1) frame = RpAnimBlendClumpFindFrame(clump, seq->name); else frame = RpAnimBlendClumpFindBone(clump, seq->boneTag); if(frame && seq->numFrames > 0) nodes[frame - clumpData->frames].sequence = seq; } } void CAnimBlendAssociation::Init(CAnimBlendAssociation &assoc) { int i; hierarchy = assoc.hierarchy; numNodes = assoc.numNodes; flags = assoc.flags; animId = assoc.animId; groupId = assoc.groupId; AllocateAnimBlendNodeArray(numNodes); for(i = 0; i < numNodes; i++){ nodes[i] = assoc.nodes[i]; nodes[i].association = this; } } void CAnimBlendAssociation::SetBlend(float amount, float delta) { blendAmount = amount; blendDelta = delta; } void CAnimBlendAssociation::SetFinishCallback(void (*cb)(CAnimBlendAssociation*, void*), void *arg) { callbackType = CB_FINISH; callback = cb; callbackArg = arg; } void CAnimBlendAssociation::SetDeleteCallback(void (*cb)(CAnimBlendAssociation*, void*), void *arg) { callbackType = CB_DELETE; callback = cb; callbackArg = arg; } void CAnimBlendAssociation::SetCurrentTime(float time) { int i; for(currentTime = time; currentTime >= hierarchy->totalLength; currentTime -= hierarchy->totalLength) if (!IsRepeating()) { currentTime = hierarchy->totalLength; break; } CAnimManager::UncompressAnimation(hierarchy); #ifdef ANIM_COMPRESSION // strangely PC has this but android doesn't if(hierarchy->keepCompressed){ for(i = 0; i < numNodes; i++) if(nodes[i].sequence) nodes[i].SetupKeyFrameCompressed(); }else #endif { for(i = 0; i < numNodes; i++) if(nodes[i].sequence) nodes[i].FindKeyFrame(currentTime); } } void CAnimBlendAssociation::SyncAnimation(CAnimBlendAssociation *other) { SetCurrentTime(other->currentTime/other->hierarchy->totalLength * hierarchy->totalLength); } void CAnimBlendAssociation::Start(float time) { flags |= ASSOC_RUNNING; SetCurrentTime(time); } void CAnimBlendAssociation::UpdateTimeStep(float timeDelta, float relSpeed) { if(IsRunning()) timeStep = (flags & ASSOC_MOVEMENT ? relSpeed*hierarchy->totalLength : speed) * timeDelta; } bool CAnimBlendAssociation::UpdateTime(float timeDelta, float relSpeed) { if(!IsRunning()) return true; if(currentTime >= hierarchy->totalLength){ flags &= ~ASSOC_RUNNING; return true; } currentTime += timeStep; if(currentTime >= hierarchy->totalLength){ // Ran past end if(IsRepeating()) currentTime -= hierarchy->totalLength; else{ currentTime = hierarchy->totalLength; if(flags & ASSOC_FADEOUTWHENDONE){ flags |= ASSOC_DELETEFADEDOUT; blendDelta = -4.0f; } if(callbackType == CB_FINISH){ callbackType = CB_NONE; callback(this, callbackArg); } } } return true; } // return whether we still exist after this function bool CAnimBlendAssociation::UpdateBlend(float timeDelta) { blendAmount += blendDelta * timeDelta; if(blendAmount <= 0.0f && blendDelta < 0.0f){ // We're faded out and are not fading in blendAmount = 0.0f; blendDelta = Max(0.0f, blendDelta); if(flags & ASSOC_DELETEFADEDOUT){ if(callbackType == CB_FINISH || callbackType == CB_DELETE) callback(this, callbackArg); delete this; return false; } } if(blendAmount > 1.0f){ // Maximally faded in, clamp values blendAmount = 1.0f; blendDelta = Min(0.0f, blendDelta); } return true; } ================================================ FILE: src/animation/AnimBlendAssociation.h ================================================ #pragma once #include "AnimBlendList.h" #include "AnimBlendNode.h" #include "AnimBlendHierarchy.h" enum { ASSOC_RUNNING = 1, ASSOC_REPEAT = 2, ASSOC_DELETEFADEDOUT = 4, ASSOC_FADEOUTWHENDONE = 8, ASSOC_PARTIAL = 0x10, ASSOC_MOVEMENT = 0x20, // ??? ASSOC_HAS_TRANSLATION = 0x40, ASSOC_HAS_X_TRANSLATION = 0x80, // for 2d velocity extraction ASSOC_WALK = 0x100, // for CPed::PlayFootSteps(void) ASSOC_IDLE = 0x200, // only xpress scratch has it by default, but game adds it to player's idle animations later ASSOC_NOWALK = 0x400, // see CPed::PlayFootSteps(void) ASSOC_BLOCK = 0x800, // unused in assoc description, blocks other anims from being played ASSOC_FRONTAL = 0x1000, // anims that we fall to front ASSOC_DRIVING = 0x2000, // new in VC }; // Anim hierarchy associated with a clump // Holds the interpolated state of all nodes. // Also used as template for other clumps. class CAnimBlendAssociation { public: enum { // callbackType CB_NONE, CB_FINISH, CB_DELETE }; CAnimBlendLink link; int16 numNodes; // taken from CAnimBlendClumpData::numFrames int16 groupId; // ID of CAnimBlendAssocGroup this is in // NB: Order of these depends on order of nodes in Clump this was built from CAnimBlendNode *nodes; CAnimBlendHierarchy *hierarchy; float blendAmount; float blendDelta; // how much blendAmount changes over time float currentTime; float speed; float timeStep; int16 animId; int16 flags; int32 callbackType; void (*callback)(CAnimBlendAssociation*, void*); void *callbackArg; bool IsRunning(void) { return !!(flags & ASSOC_RUNNING); } bool IsRepeating(void) { return !!(flags & ASSOC_REPEAT); } bool IsPartial(void) { return !!(flags & ASSOC_PARTIAL); } bool IsMovement(void) { return !!(flags & ASSOC_MOVEMENT); } bool HasTranslation(void) { return !!(flags & ASSOC_HAS_TRANSLATION); } bool HasXTranslation(void) { return !!(flags & ASSOC_HAS_X_TRANSLATION); } float GetBlendAmount(float weight) { return IsPartial() ? blendAmount : blendAmount*weight; } CAnimBlendNode *GetNode(int i) { return &nodes[i]; } CAnimBlendAssociation(void); CAnimBlendAssociation(CAnimBlendAssociation &other); #ifndef FIX_BUGS virtual #endif ~CAnimBlendAssociation(void); void AllocateAnimBlendNodeArray(int n); void FreeAnimBlendNodeArray(void); void Init(RpClump *clump, CAnimBlendHierarchy *hier); void Init(CAnimBlendAssociation &assoc); void SetBlend(float amount, float delta); void SetFinishCallback(void (*callback)(CAnimBlendAssociation*, void*), void *arg); void SetDeleteCallback(void (*callback)(CAnimBlendAssociation*, void*), void *arg); void SetCurrentTime(float time); void SyncAnimation(CAnimBlendAssociation *other); void Start(float time); void UpdateTimeStep(float timeDelta, float relSpeed); bool UpdateTime(float timeDelta, float relSpeed); bool UpdateBlend(float timeDelta); void SetRun(void) { flags |= ASSOC_RUNNING; } float GetTimeLeft() { return hierarchy->totalLength - currentTime; } float GetProgress() { return currentTime / hierarchy->totalLength; } static CAnimBlendAssociation *FromLink(CAnimBlendLink *l) { return (CAnimBlendAssociation*)((uint8*)l - offsetof(CAnimBlendAssociation, link)); } }; ================================================ FILE: src/animation/AnimBlendClumpData.cpp ================================================ #include "common.h" #include "AnimBlendClumpData.h" #include "MemoryMgr.h" CAnimBlendClumpData::CAnimBlendClumpData(void) { numFrames = 0; velocity2d = nil; frames = nil; link.Init(); } CAnimBlendClumpData::~CAnimBlendClumpData(void) { link.Remove(); if(frames) RwFreeAlign(frames); } void CAnimBlendClumpData::SetNumberOfFrames(int n) { if(frames) RwFreeAlign(frames); numFrames = n; frames = (AnimBlendFrameData*)RwMallocAlign(numFrames * sizeof(AnimBlendFrameData), 64); } void CAnimBlendClumpData::ForAllFrames(void (*cb)(AnimBlendFrameData*, void*), void *arg) { int i; for(i = 0; i < numFrames; i++) cb(&frames[i], arg); } ================================================ FILE: src/animation/AnimBlendClumpData.h ================================================ #pragma once #include "AnimBlendList.h" struct AnimBlendFrameData { enum { IGNORE_ROTATION = 2, IGNORE_TRANSLATION = 4, VELOCITY_EXTRACTION = 8, VELOCITY_EXTRACTION_3D = 0x10, UPDATE_KEYFRAMES = 0x20, COMPRESSED = 0x40 }; uint8 flag; RwV3d resetPos; union { RwFrame *frame; RpHAnimStdInterpFrame *hanimFrame; }; int32 nodeID; }; VALIDATE_SIZE(AnimBlendFrameData, 0x18); class CAnimBlendClumpData { public: CAnimBlendLink link; int32 numFrames; union { CVector2D *velocity2d; CVector *velocity3d; }; // order of frames is determined by RW hierarchy AnimBlendFrameData *frames; CAnimBlendClumpData(void); ~CAnimBlendClumpData(void); void SetNumberOfFrames(int n); void SetNumberOfBones(int n) { SetNumberOfFrames(n); } void ForAllFrames(void (*cb)(AnimBlendFrameData*, void*), void *arg); }; ================================================ FILE: src/animation/AnimBlendHierarchy.cpp ================================================ #include "common.h" #include "AnimBlendSequence.h" #include "AnimBlendHierarchy.h" #include "AnimManager.h" CAnimBlendHierarchy::CAnimBlendHierarchy(void) { sequences = nil; numSequences = 0; compressed = 0; totalLength = 0.0f; linkPtr = nil; } void CAnimBlendHierarchy::Shutdown(void) { CAnimManager::RemoveFromUncompressedCache(this); RemoveAnimSequences(); totalLength = 0.0f; compressed = 0; } void CAnimBlendHierarchy::SetName(char *name) { strncpy(this->name, name, 24); } void CAnimBlendHierarchy::CalcTotalTime(void) { int i, j; totalLength = 0.0f; for(i = 0; i < numSequences; i++){ #ifdef FIX_BUGS if(sequences[i].numFrames == 0) continue; #endif totalLength = Max(totalLength, sequences[i].GetKeyFrame(sequences[i].numFrames-1)->deltaTime); for(j = sequences[i].numFrames-1; j >= 1; j--){ KeyFrame *kf1 = sequences[i].GetKeyFrame(j); KeyFrame *kf2 = sequences[i].GetKeyFrame(j-1); kf1->deltaTime -= kf2->deltaTime; } } } void CAnimBlendHierarchy::CalcTotalTimeCompressed(void) { int i, j; totalLength = 0.0f; for(i = 0; i < numSequences; i++){ #ifdef FIX_BUGS if(sequences[i].numFrames == 0) continue; #endif totalLength = Max(totalLength, sequences[i].GetKeyFrameCompressed(sequences[i].numFrames-1)->GetDeltaTime()); for(j = sequences[i].numFrames-1; j >= 1; j--){ KeyFrameCompressed *kf1 = sequences[i].GetKeyFrameCompressed(j); KeyFrameCompressed *kf2 = sequences[i].GetKeyFrameCompressed(j-1); kf1->deltaTime -= kf2->deltaTime; } } } void CAnimBlendHierarchy::RemoveQuaternionFlips(void) { int i; for(i = 0; i < numSequences; i++) sequences[i].RemoveQuaternionFlips(); } void CAnimBlendHierarchy::RemoveAnimSequences(void) { delete[] sequences; sequences = nil; numSequences = 0; } void CAnimBlendHierarchy::Uncompress(void) { #ifdef ANIM_COMPRESSION int i; assert(compressed); for(i = 0; i < numSequences; i++) sequences[i].Uncompress(); #endif compressed = 0; if(totalLength == 0.0f){ RemoveQuaternionFlips(); CalcTotalTime(); } } void CAnimBlendHierarchy::RemoveUncompressedData(void) { #ifdef ANIM_COMPRESSION int i; assert(!compressed); for(i = 0; i < numSequences; i++) sequences[i].RemoveUncompressedData(); #endif compressed = 1; } #ifdef USE_CUSTOM_ALLOCATOR void CAnimBlendHierarchy::MoveMemory(bool onlyone) { int i; for(i = 0; i < numSequences; i++) if(sequences[i].MoveMemory() && onlyone) return; } #endif ================================================ FILE: src/animation/AnimBlendHierarchy.h ================================================ #pragma once #include "templates.h" #ifdef MoveMemory #undef MoveMemory // windows shit #endif class CAnimBlendSequence; // A collection of sequences class CAnimBlendHierarchy { public: char name[24]; CAnimBlendSequence *sequences; int16 numSequences; bool compressed; bool keepCompressed; float totalLength; CLink *linkPtr; CAnimBlendHierarchy(void); void Shutdown(void); void SetName(char *name); void CalcTotalTime(void); void CalcTotalTimeCompressed(void); void RemoveQuaternionFlips(void); void RemoveAnimSequences(void); void Uncompress(void); void RemoveUncompressedData(void); void MoveMemory(bool onlyone = false); bool IsCompressed() { return !!compressed; }; }; VALIDATE_SIZE(CAnimBlendHierarchy, 0x28); ================================================ FILE: src/animation/AnimBlendList.h ================================================ #pragma once // name made up class CAnimBlendLink { public: CAnimBlendLink *next; CAnimBlendLink *prev; void Init(void){ next = nil; prev = nil; } void Prepend(CAnimBlendLink *link){ if(next) next->prev = link; link->next = next; link->prev = this; next = link; } void Remove(void){ if(prev) prev->next = next; if(next) next->prev = prev; Init(); } }; ================================================ FILE: src/animation/AnimBlendNode.cpp ================================================ #include "common.h" #include "AnimBlendAssociation.h" #include "AnimBlendNode.h" void CAnimBlendNode::Init(void) { frameA = -1; frameB = -1; remainingTime = 0.0f; sequence = nil; association = nil; } bool CAnimBlendNode::Update(CVector &trans, CQuaternion &rot, float weight) { bool looped = false; trans = CVector(0.0f, 0.0f, 0.0f); rot = CQuaternion(0.0f, 0.0f, 0.0f, 0.0f); if(association->IsRunning()){ remainingTime -= association->timeStep; if(remainingTime <= 0.0f) looped = NextKeyFrame(); } float blend = association->GetBlendAmount(weight); if(blend > 0.0f){ KeyFrameTrans *kfA = (KeyFrameTrans*)sequence->GetKeyFrame(frameA); KeyFrameTrans *kfB = (KeyFrameTrans*)sequence->GetKeyFrame(frameB); float t = kfA->deltaTime == 0.0f ? 0.0f : (kfA->deltaTime - remainingTime)/kfA->deltaTime; if(sequence->type & CAnimBlendSequence::KF_TRANS){ trans = kfB->translation + t*(kfA->translation - kfB->translation); trans *= blend; } if(sequence->type & CAnimBlendSequence::KF_ROT){ rot.Slerp(kfB->rotation, kfA->rotation, theta, invSin, t); rot *= blend; } } return looped; } bool CAnimBlendNode::UpdateCompressed(CVector &trans, CQuaternion &rot, float weight) { bool looped = false; trans = CVector(0.0f, 0.0f, 0.0f); rot = CQuaternion(0.0f, 0.0f, 0.0f, 0.0f); if(association->IsRunning()){ remainingTime -= association->timeStep; if(remainingTime <= 0.0f) looped = NextKeyFrameCompressed(); } float blend = association->GetBlendAmount(weight); if(blend > 0.0f){ KeyFrameTransCompressed *kfA = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(frameA); KeyFrameTransCompressed *kfB = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(frameB); float t = kfA->deltaTime == 0 ? 0.0f : (kfA->GetDeltaTime() - remainingTime)/kfA->GetDeltaTime(); if(sequence->type & CAnimBlendSequence::KF_TRANS){ CVector transA, transB; kfA->GetTranslation(&transA); kfB->GetTranslation(&transB); trans = transB + t*(transA - transB); trans *= blend; } if(sequence->type & CAnimBlendSequence::KF_ROT){ CQuaternion rotA, rotB; kfA->GetRotation(&rotA); kfB->GetRotation(&rotB); rot.Slerp(rotB, rotA, theta, invSin, t); rot *= blend; } } return looped; } bool CAnimBlendNode::NextKeyFrame(void) { bool looped; if(sequence->numFrames <= 1) return false; looped = false; frameB = frameA; // Advance as long as we have to while(remainingTime <= 0.0f){ frameA++; if(frameA >= sequence->numFrames){ // reached end of animation if(!association->IsRepeating()){ frameA--; remainingTime = 0.0f; return false; } looped = true; frameA = 0; } remainingTime += sequence->GetKeyFrame(frameA)->deltaTime; } frameB = frameA - 1; if(frameB < 0) frameB += sequence->numFrames; CalcDeltas(); return looped; } bool CAnimBlendNode::NextKeyFrameCompressed(void) { bool looped; if(sequence->numFrames <= 1) return false; looped = false; frameB = frameA; // Advance as long as we have to while(remainingTime <= 0.0f){ frameA++; if(frameA >= sequence->numFrames){ // reached end of animation if(!association->IsRepeating()){ frameA--; remainingTime = 0.0f; return false; } looped = true; frameA = 0; } remainingTime += sequence->GetKeyFrameCompressed(frameA)->GetDeltaTime(); } frameB = frameA - 1; if(frameB < 0) frameB += sequence->numFrames; CalcDeltasCompressed(); return looped; } // Set animation to time t bool CAnimBlendNode::FindKeyFrame(float t) { if(sequence->numFrames < 1) return false; frameA = 0; frameB = frameA; if(sequence->numFrames == 1){ remainingTime = 0.0f; }else{ // advance until t is between frameB and frameA while (t > sequence->GetKeyFrame(++frameA)->deltaTime) { t -= sequence->GetKeyFrame(frameA)->deltaTime; if (frameA + 1 >= sequence->numFrames) { // reached end of animation if (!association->IsRepeating()) { CalcDeltas(); remainingTime = 0.0f; return false; } frameA = 0; } frameB = frameA; } remainingTime = sequence->GetKeyFrame(frameA)->deltaTime - t; } CalcDeltas(); return true; } bool CAnimBlendNode::SetupKeyFrameCompressed(void) { if(sequence->numFrames < 1) return false; frameA = 1; frameB = 0; if(sequence->numFrames == 1){ frameA = 0; remainingTime = 0.0f; }else remainingTime = sequence->GetKeyFrameCompressed(frameA)->GetDeltaTime(); CalcDeltasCompressed(); return true; } void CAnimBlendNode::CalcDeltas(void) { if((sequence->type & CAnimBlendSequence::KF_ROT) == 0) return; KeyFrame *kfA = sequence->GetKeyFrame(frameA); KeyFrame *kfB = sequence->GetKeyFrame(frameB); float cos = DotProduct(kfA->rotation, kfB->rotation); if(cos > 1.0f) cos = 1.0f; theta = Acos(cos); invSin = theta == 0.0f ? 0.0f : 1.0f/Sin(theta); } void CAnimBlendNode::CalcDeltasCompressed(void) { if((sequence->type & CAnimBlendSequence::KF_ROT) == 0) return; KeyFrameCompressed *kfA = sequence->GetKeyFrameCompressed(frameA); KeyFrameCompressed *kfB = sequence->GetKeyFrameCompressed(frameB); CQuaternion rotA, rotB; kfA->GetRotation(&rotA); kfB->GetRotation(&rotB); float cos = DotProduct(rotA, rotB); if(cos < 0.0f){ rotB *= -1.0f; kfB->SetRotation(rotB); } cos = DotProduct(rotA, rotB); if(cos > 1.0f) cos = 1.0f; theta = Acos(cos); invSin = theta == 0.0f ? 0.0f : 1.0f/Sin(theta); } void CAnimBlendNode::GetCurrentTranslation(CVector &trans, float weight) { trans = CVector(0.0f, 0.0f, 0.0f); float blend = association->GetBlendAmount(weight); if(blend > 0.0f){ KeyFrameTrans *kfA = (KeyFrameTrans*)sequence->GetKeyFrame(frameA); KeyFrameTrans *kfB = (KeyFrameTrans*)sequence->GetKeyFrame(frameB); float t = kfA->deltaTime == 0.0f ? 0.0f : (kfA->deltaTime - remainingTime)/kfA->deltaTime; if(sequence->type & CAnimBlendSequence::KF_TRANS){ trans = kfB->translation + t*(kfA->translation - kfB->translation); trans *= blend; } } } void CAnimBlendNode::GetCurrentTranslationCompressed(CVector &trans, float weight) { trans = CVector(0.0f, 0.0f, 0.0f); float blend = association->GetBlendAmount(weight); if(blend > 0.0f){ KeyFrameTransCompressed *kfA = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(frameA); KeyFrameTransCompressed *kfB = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(frameB); float t = kfA->deltaTime == 0 ? 0.0f : (kfA->GetDeltaTime() - remainingTime)/kfA->GetDeltaTime(); if(sequence->type & CAnimBlendSequence::KF_TRANS){ CVector transA, transB; kfA->GetTranslation(&transA); kfB->GetTranslation(&transB); trans = transB + t*(transA - transB); trans *= blend; } } } void CAnimBlendNode::GetEndTranslation(CVector &trans, float weight) { trans = CVector(0.0f, 0.0f, 0.0f); float blend = association->GetBlendAmount(weight); if(blend > 0.0f){ KeyFrameTrans *kf = (KeyFrameTrans*)sequence->GetKeyFrame(sequence->numFrames-1); if(sequence->type & CAnimBlendSequence::KF_TRANS) trans = kf->translation * blend; } } void CAnimBlendNode::GetEndTranslationCompressed(CVector &trans, float weight) { trans = CVector(0.0f, 0.0f, 0.0f); float blend = association->GetBlendAmount(weight); if(blend > 0.0f){ KeyFrameTransCompressed *kf = (KeyFrameTransCompressed*)sequence->GetKeyFrameCompressed(sequence->numFrames-1); if(sequence->type & CAnimBlendSequence::KF_TRANS){ CVector pos; kf->GetTranslation(&pos); trans = pos * blend; } } } ================================================ FILE: src/animation/AnimBlendNode.h ================================================ #pragma once #include "AnimBlendSequence.h" class CAnimBlendAssociation; // The interpolated state between two key frames in a sequence class CAnimBlendNode { public: // for slerp float theta; // angle between quaternions float invSin; // 1/Sin(theta) // indices into array in sequence int32 frameA; // next key frame int32 frameB; // previous key frame float remainingTime; // time until frames have to advance CAnimBlendSequence *sequence; CAnimBlendAssociation *association; void Init(void); bool Update(CVector &trans, CQuaternion &rot, float weight); bool UpdateCompressed(CVector &trans, CQuaternion &rot, float weight); bool NextKeyFrame(void); bool NextKeyFrameCompressed(void); bool FindKeyFrame(float t); bool SetupKeyFrameCompressed(void); void CalcDeltas(void); void CalcDeltasCompressed(void); void GetCurrentTranslation(CVector &trans, float weight); void GetCurrentTranslationCompressed(CVector &trans, float weight); void GetEndTranslation(CVector &trans, float weight); void GetEndTranslationCompressed(CVector &trans, float weight); }; VALIDATE_SIZE(CAnimBlendNode, 0x1C); ================================================ FILE: src/animation/AnimBlendSequence.cpp ================================================ #include "common.h" #include "AnimBlendSequence.h" #include "MemoryHeap.h" CAnimBlendSequence::CAnimBlendSequence(void) { type = 0; numFrames = 0; keyFrames = nil; keyFramesCompressed = nil; boneTag = -1; } CAnimBlendSequence::~CAnimBlendSequence(void) { if(keyFrames) RwFree(keyFrames); if(keyFramesCompressed) RwFree(keyFramesCompressed); } void CAnimBlendSequence::SetName(char *name) { strncpy(this->name, name, 24); } void CAnimBlendSequence::SetNumFrames(int numFrames, bool translation, bool compressed) { if(translation){ type |= KF_ROT | KF_TRANS; if(compressed) keyFramesCompressed = RwMalloc(sizeof(KeyFrameTrans) * numFrames); else keyFrames = RwMalloc(sizeof(KeyFrameTrans) * numFrames); }else{ type |= KF_ROT; if(compressed) keyFramesCompressed = RwMalloc(sizeof(KeyFrame) * numFrames); else keyFrames = RwMalloc(sizeof(KeyFrame) * numFrames); } this->numFrames = numFrames; } void CAnimBlendSequence::RemoveQuaternionFlips(void) { int i; CQuaternion last; KeyFrame *frame; if(numFrames < 2) return; frame = GetKeyFrame(0); last = frame->rotation; for(i = 1; i < numFrames; i++){ frame = GetKeyFrame(i); if(DotProduct(last, frame->rotation) < 0.0f) frame->rotation = -frame->rotation; last = frame->rotation; } } void CAnimBlendSequence::Uncompress(void) { int i; if(numFrames == 0) return; PUSH_MEMID(MEMID_ANIMATION); float rotScale = 1.0f/4096.0f; float timeScale = 1.0f/60.0f; float transScale = 1.0f/1024.0f; if(type & KF_TRANS){ void *newKfs = RwMalloc(numFrames * sizeof(KeyFrameTrans)); KeyFrameTransCompressed *ckf = (KeyFrameTransCompressed*)keyFramesCompressed; KeyFrameTrans *kf = (KeyFrameTrans*)newKfs; for(i = 0; i < numFrames; i++){ kf->rotation.x = ckf->rot[0]*rotScale; kf->rotation.y = ckf->rot[1]*rotScale; kf->rotation.z = ckf->rot[2]*rotScale; kf->rotation.w = ckf->rot[3]*rotScale; kf->deltaTime = ckf->deltaTime*timeScale; kf->translation.x = ckf->trans[0]*transScale; kf->translation.y = ckf->trans[1]*transScale; kf->translation.z = ckf->trans[2]*transScale; kf++; ckf++; } keyFrames = newKfs; }else{ void *newKfs = RwMalloc(numFrames * sizeof(KeyFrame)); KeyFrameCompressed *ckf = (KeyFrameCompressed*)keyFramesCompressed; KeyFrame *kf = (KeyFrame*)newKfs; for(i = 0; i < numFrames; i++){ kf->rotation.x = ckf->rot[0]*rotScale; kf->rotation.y = ckf->rot[1]*rotScale; kf->rotation.z = ckf->rot[2]*rotScale; kf->rotation.w = ckf->rot[3]*rotScale; kf->deltaTime = ckf->deltaTime*timeScale; kf++; ckf++; } keyFrames = newKfs; } REGISTER_MEMPTR(&keyFrames); RwFree(keyFramesCompressed); keyFramesCompressed = nil; POP_MEMID(); } void CAnimBlendSequence::CompressKeyframes(void) { int i; if(numFrames == 0) return; PUSH_MEMID(MEMID_ANIMATION); float rotScale = 4096.0f; float timeScale = 60.0f; float transScale = 1024.0f; if(type & KF_TRANS){ void *newKfs = RwMalloc(numFrames * sizeof(KeyFrameTransCompressed)); KeyFrameTransCompressed *ckf = (KeyFrameTransCompressed*)newKfs; KeyFrameTrans *kf = (KeyFrameTrans*)keyFrames; for(i = 0; i < numFrames; i++){ ckf->rot[0] = kf->rotation.x*rotScale; ckf->rot[1] = kf->rotation.y*rotScale; ckf->rot[2] = kf->rotation.z*rotScale; ckf->rot[3] = kf->rotation.w*rotScale; ckf->deltaTime = kf->deltaTime*timeScale + 0.5f; ckf->trans[0] = kf->translation.x*transScale; ckf->trans[1] = kf->translation.y*transScale; ckf->trans[2] = kf->translation.z*transScale; kf++; ckf++; } keyFramesCompressed = newKfs; }else{ void *newKfs = RwMalloc(numFrames * sizeof(KeyFrameCompressed)); KeyFrameCompressed *ckf = (KeyFrameCompressed*)newKfs; KeyFrame *kf = (KeyFrame*)keyFrames; for(i = 0; i < numFrames; i++){ ckf->rot[0] = kf->rotation.x*rotScale; ckf->rot[1] = kf->rotation.y*rotScale; ckf->rot[2] = kf->rotation.z*rotScale; ckf->rot[3] = kf->rotation.w*rotScale; ckf->deltaTime = kf->deltaTime*timeScale + 0.5f; kf++; ckf++; } keyFramesCompressed = newKfs; } REGISTER_MEMPTR(&keyFramesCompressed); POP_MEMID(); } void CAnimBlendSequence::RemoveUncompressedData(void) { if(numFrames == 0) return; CompressKeyframes(); RwFree(keyFrames); keyFrames = nil; } #ifdef USE_CUSTOM_ALLOCATOR bool CAnimBlendSequence::MoveMemory(void) { if(keyFrames){ void *newaddr = gMainHeap.MoveMemory(keyFrames); if(newaddr != keyFrames){ keyFrames = newaddr; return true; } }else if(keyFramesCompressed){ void *newaddr = gMainHeap.MoveMemory(keyFramesCompressed); if(newaddr != keyFramesCompressed){ keyFramesCompressed = newaddr; return true; } } return false; } #endif ================================================ FILE: src/animation/AnimBlendSequence.h ================================================ #pragma once #include "Quaternion.h" #ifdef MoveMemory #undef MoveMemory // windows shit #endif // TODO: put them somewhere else? struct KeyFrame { CQuaternion rotation; float deltaTime; // relative to previous key frame }; struct KeyFrameTrans : KeyFrame { CVector translation; }; struct KeyFrameCompressed { int16 rot[4]; // 4096 int16 deltaTime; // 60 void GetRotation(CQuaternion *quat){ float scale = 1.0f/4096.0f; quat->x = rot[0]*scale; quat->y = rot[1]*scale; quat->z = rot[2]*scale; quat->w = rot[3]*scale; } void SetRotation(const CQuaternion &quat){ rot[0] = quat.x * 4096.0f; rot[1] = quat.y * 4096.0f; rot[2] = quat.z * 4096.0f; rot[3] = quat.w * 4096.0f; } float GetDeltaTime(void) { return deltaTime/60.0f; } void SetTime(float t) { deltaTime = t*60.0f + 0.5f; } }; struct KeyFrameTransCompressed : KeyFrameCompressed { int16 trans[3]; // 1024 void GetTranslation(CVector *vec) { float scale = 1.0f/1024.0f; vec->x = trans[0]*scale; vec->y = trans[1]*scale; vec->z = trans[2]*scale; } void SetTranslation(const CVector &vec){ trans[0] = vec.x*1024.0f; trans[1] = vec.y*1024.0f; trans[2] = vec.z*1024.0f; } }; // The sequence of key frames of one animated node class CAnimBlendSequence { public: enum { KF_ROT = 1, KF_TRANS = 2 }; int32 type; char name[24]; int32 numFrames; int16 boneTag; void *keyFrames; void *keyFramesCompressed; CAnimBlendSequence(void); virtual ~CAnimBlendSequence(void); void SetName(char *name); void SetNumFrames(int numFrames, bool translation, bool compressed); void RemoveQuaternionFlips(void); KeyFrame *GetKeyFrame(int n) { return type & KF_TRANS ? &((KeyFrameTrans*)keyFrames)[n] : &((KeyFrame*)keyFrames)[n]; } KeyFrameCompressed *GetKeyFrameCompressed(int n) { return type & KF_TRANS ? &((KeyFrameTransCompressed*)keyFramesCompressed)[n] : &((KeyFrameCompressed*)keyFramesCompressed)[n]; } bool HasTranslation(void) { return !!(type & KF_TRANS); } void Uncompress(void); void CompressKeyframes(void); void RemoveUncompressedData(void); bool MoveMemory(void); void SetBoneTag(int tag) { boneTag = tag; } }; VALIDATE_SIZE(CAnimBlendSequence, 0x30); ================================================ FILE: src/animation/AnimManager.cpp ================================================ #include "common.h" #include "General.h" #include "RwHelper.h" #include "ModelInfo.h" #include "ModelIndices.h" #include "FileMgr.h" #include "RpAnimBlend.h" #include "AnimBlendClumpData.h" #include "AnimBlendAssociation.h" #include "AnimBlendAssocGroup.h" #include "AnimManager.h" #include "Streaming.h" CAnimBlock CAnimManager::ms_aAnimBlocks[NUMANIMBLOCKS]; CAnimBlendHierarchy CAnimManager::ms_aAnimations[NUMANIMATIONS]; int32 CAnimManager::ms_numAnimBlocks; int32 CAnimManager::ms_numAnimations; CAnimBlendAssocGroup *CAnimManager::ms_aAnimAssocGroups; CLinkList CAnimManager::ms_animCache; AnimAssocDesc aStdAnimDescs[] = { { ANIM_STD_WALK, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_WALK }, { ANIM_STD_RUN, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_WALK }, { ANIM_STD_RUNFAST, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_WALK }, { ANIM_STD_IDLE, ASSOC_REPEAT }, { ANIM_STD_STARTWALK, ASSOC_HAS_TRANSLATION }, { ANIM_STD_RUNSTOP1, ASSOC_DELETEFADEDOUT | ASSOC_HAS_TRANSLATION }, { ANIM_STD_RUNSTOP2, ASSOC_DELETEFADEDOUT | ASSOC_HAS_TRANSLATION }, { ANIM_STD_IDLE_CAM, ASSOC_REPEAT | ASSOC_PARTIAL }, { ANIM_STD_IDLE_HBHB, ASSOC_REPEAT | ASSOC_PARTIAL }, { ANIM_STD_IDLE_TIRED, ASSOC_REPEAT }, { ANIM_STD_IDLE_BIGGUN, ASSOC_REPEAT | ASSOC_PARTIAL }, { ANIM_STD_CHAT, ASSOC_REPEAT | ASSOC_PARTIAL }, { ANIM_STD_HAILTAXI, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_KO_FRONT, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, { ANIM_STD_KO_LEFT, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, { ANIM_STD_KO_BACK, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, { ANIM_STD_KO_RIGHT, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, { ANIM_STD_KO_SHOT_FACE, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, { ANIM_STD_KO_SHOT_STOMACH, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_KO_SHOT_ARM_L, ASSOC_PARTIAL | ASSOC_FRONTAL }, { ANIM_STD_KO_SHOT_ARM_R, ASSOC_PARTIAL | ASSOC_FRONTAL }, { ANIM_STD_KO_SHOT_LEG_L, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_KO_SHOT_LEG_R, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_SPINFORWARD_LEFT, ASSOC_PARTIAL | ASSOC_FRONTAL }, { ANIM_STD_SPINFORWARD_RIGHT, ASSOC_PARTIAL | ASSOC_FRONTAL }, { ANIM_STD_HIGHIMPACT_FRONT, ASSOC_PARTIAL }, { ANIM_STD_HIGHIMPACT_LEFT, ASSOC_PARTIAL }, { ANIM_STD_HIGHIMPACT_BACK, ASSOC_PARTIAL | ASSOC_FRONTAL }, { ANIM_STD_HIGHIMPACT_RIGHT, ASSOC_PARTIAL }, { ANIM_STD_HITBYGUN_FRONT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, { ANIM_STD_HITBYGUN_LEFT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, { ANIM_STD_HITBYGUN_BACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, { ANIM_STD_HITBYGUN_RIGHT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, { ANIM_STD_HIT_FRONT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_HIT_LEFT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_HIT_BACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_HIT_RIGHT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_HIT_FLOOR, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_HIT_BODYBLOW, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_HIT_CHEST, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_HIT_HEAD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_HIT_WALK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_HIT_WALL, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_HIT_FLOOR_FRONT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_FRONTAL }, { ANIM_STD_HIT_BEHIND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_IDLE, ASSOC_REPEAT }, { ANIM_STD_FIGHT_2IDLE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_SHUFFLE_F, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_FIGHT_BODYBLOW, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_HEAD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_KICK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_KNEE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_LHOOK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_PUNCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_ROUNDHOUSE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_FIGHT_LONGKICK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_PARTIAL_PUNCH, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_NOWALK }, { ANIM_STD_FIGHT_JAB, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_ELBOW_L, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_ELBOW_R, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_BKICK_L, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_BKICK_R, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_DETONATE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_PUNCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_PARTIALPUNCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_KICKGROUND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_THROW_UNDER, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_FIGHT_SHUFFLE_B, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_JACKEDCAR_RHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_JACKEDCAR_LO_RHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_JACKEDCAR_LHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_JACKEDCAR_LO_LHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_QUICKJACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_QUICKJACKED, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_CAR_ALIGN_DOOR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_ALIGNHI_DOOR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_OPEN_DOOR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CARDOOR_LOCKED_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_PULL_OUT_PED_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_PULL_OUT_PED_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_GET_IN_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_GET_IN_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_CLOSE_DOOR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_CLOSE_DOOR_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_JUMP_IN_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_GETOUT_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_GETOUT_LO_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_CLOSE_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_ALIGN_DOOR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_ALIGNHI_DOOR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_OPEN_DOOR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CARDOOR_LOCKED_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_PULL_OUT_PED_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_PULL_OUT_PED_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_GET_IN_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_GET_IN_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_CLOSE_DOOR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_CLOSE_DOOR_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_SHUFFLE_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_SHUFFLE_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_SIT, ASSOC_DELETEFADEDOUT}, { ANIM_STD_CAR_SIT_LO, ASSOC_DELETEFADEDOUT}, { ANIM_STD_CAR_SIT_P, ASSOC_DELETEFADEDOUT}, { ANIM_STD_CAR_SIT_P_LO, ASSOC_DELETEFADEDOUT}, { ANIM_STD_CAR_DRIVE_LEFT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_CAR_DRIVE_RIGHT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_CAR_DRIVE_LEFT_LO, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_CAR_DRIVE_RIGHT_LO, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_CAR_DRIVEBY_LEFT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_CAR_DRIVEBY_RIGHT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_CAR_DRIVEBY_LEFT_LO, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_CAR_DRIVEBY_RIGHT_LO, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_CAR_LOOKBEHIND, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_BOAT_DRIVE, ASSOC_DELETEFADEDOUT | ASSOC_DRIVING }, { ANIM_STD_BOAT_DRIVE_LEFT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_BOAT_DRIVE_RIGHT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_BOAT_LOOKBEHIND, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_STD_BIKE_PICKUP_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_BIKE_PICKUP_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_BIKE_PULLUP_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_BIKE_PULLUP_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_BIKE_ELBOW_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_BIKE_ELBOW_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_BIKE_FALLOFF, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_BIKE_FALLBACK, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_GETOUT_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_GETOUT_LO_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_CLOSE_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CAR_HOOKERTALK, ASSOC_REPEAT | ASSOC_PARTIAL }, { ANIM_STD_TRAIN_GETIN, ASSOC_PARTIAL }, { ANIM_STD_TRAIN_GETOUT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CRAWLOUT_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_CRAWLOUT_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_ROLLOUT_LHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, { ANIM_STD_ROLLOUT_RHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, { ANIM_STD_GET_UP, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_GET_UP_LEFT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_GET_UP_RIGHT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_GET_UP_FRONT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_JUMP_LAUNCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_JUMP_GLIDE, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_JUMP_LAND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_FALL, ASSOC_REPEAT | ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_FALL_GLIDE, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_FALL_LAND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_FALL_COLLAPSE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_FALL_ONBACK, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_FALL_ONFRONT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_FRONTAL }, { ANIM_STD_EVADE_STEP, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_EVADE_DIVE, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_FRONTAL }, { ANIM_STD_XPRESS_SCRATCH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_IDLE }, { ANIM_STD_ROADCROSS, ASSOC_REPEAT | ASSOC_PARTIAL }, { ANIM_STD_TURN180, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_ARREST, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_DROWN, ASSOC_PARTIAL }, { ANIM_STD_DUCK_DOWN, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_DUCK_LOW, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_DUCK_WEAPON, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_RBLOCK_SHOOT, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_HANDSUP, ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_HANDSCOWER, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_STD_PARTIAL_FUCKU, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, { ANIM_STD_PHONE_IN, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_PHONE_OUT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_PHONE_TALK, ASSOC_REPEAT | ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, { ANIM_STD_SEAT_DOWN, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_SEAT_UP, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_SEAT_IDLE, ASSOC_REPEAT | ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_SEAT_RVRS, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_ATM, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_ABSEIL, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL }, }; AnimAssocDesc aVanAnimDescs[] = { { ANIM_STD_VAN_OPEN_DOOR_REAR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_VAN_GET_IN_REAR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_VAN_GET_OUT_REAR_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_VAN_OPEN_DOOR_REAR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_VAN_GET_IN_REAR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_VAN_GET_OUT_REAR_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, }; AnimAssocDesc aCoachAnimDescs[] = { { ANIM_STD_COACH_OPEN_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_COACH_OPEN_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_COACH_GET_IN_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_COACH_GET_IN_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STD_COACH_GET_OUT_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, }; AnimAssocDesc aBikeAnimDescs[] = { { ANIM_BIKE_RIDE, ASSOC_DELETEFADEDOUT}, { ANIM_BIKE_READY, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_BIKE_LEFT, ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_BIKE_RIGHT, ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_BIKE_LEANB, ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_BIKE_LEANF, ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_BIKE_WALKBACK, ASSOC_REPEAT | ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_BIKE_JUMPON_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_BIKE_JUMPON_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_BIKE_KICK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_BIKE_HIT, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_BIKE_GETOFF_LHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_BIKE_GETOFF_RHS, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_BIKE_GETOFF_BACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, { ANIM_BIKE_DRIVEBY_LHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_BIKE_DRIVEBY_RHS, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_BIKE_DRIVEBY_FORWARD, ASSOC_DELETEFADEDOUT | ASSOC_PARTIAL | ASSOC_DRIVING }, { ANIM_BIKE_RIDE_P, ASSOC_DELETEFADEDOUT | ASSOC_DRIVING }, }; AnimAssocDesc aMeleeAnimDescs[] = { { ANIM_MELEE_ATTACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_MELEE_ATTACK_2ND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_MELEE_ATTACK_START, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_NOWALK }, { ANIM_MELEE_IDLE_FIGHTMODE, ASSOC_REPEAT }, { ANIM_MELEE_ATTACK_FINISH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION }, }; AnimAssocDesc aSwingAnimDescs[] = { { ANIM_MELEE_ATTACK, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_MELEE_ATTACK_2ND, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_MELEE_ATTACK_START, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_MELEE_IDLE_FIGHTMODE, ASSOC_REPEAT }, { ANIM_MELEE_ATTACK_FINISH, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, }; AnimAssocDesc aWeaponAnimDescs[] = { { ANIM_WEAPON_FIRE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_WEAPON_CROUCHFIRE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_WEAPON_RELOAD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_WEAPON_CROUCHRELOAD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_WEAPON_FIRE_3RD, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, }; AnimAssocDesc aMedicAnimDescs[] = { { ANIM_MEDIC_CPR, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, }; AnimAssocDesc aSunbatheAnimDescs[] = { { ANIM_SUNBATHE_IDLE, ASSOC_REPEAT | ASSOC_PARTIAL }, { ANIM_SUNBATHE_DOWN, ASSOC_REPEAT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, { ANIM_SUNBATHE_UP, ASSOC_REPEAT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, { ANIM_SUNBATHE_ESCAPE, ASSOC_REPEAT | ASSOC_PARTIAL | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, }; AnimAssocDesc aPlayerIdleAnimDescs[] = { { ANIM_PLAYER_IDLE1, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_PLAYER_IDLE2, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_PLAYER_IDLE3, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_PLAYER_IDLE4, ASSOC_DELETEFADEDOUT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, }; AnimAssocDesc aRiotAnimDescs[] = { { ANIM_RIOT_ANGRY, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_RIOT_ANGRY_B, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_RIOT_CHANT, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_RIOT_PUNCHES, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_RIOT_SHOUT, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_RIOT_CHALLENGE, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_RIOT_FUCKYOU, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, }; AnimAssocDesc aStripAnimDescs[] = { { ANIM_STRIP_A, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STRIP_B, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STRIP_C, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STRIP_D, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STRIP_E, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STRIP_F, ASSOC_REPEAT | ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, { ANIM_STRIP_G, ASSOC_FADEOUTWHENDONE | ASSOC_PARTIAL }, }; #ifdef PC_PLAYER_CONTROLS AnimAssocDesc aStdAnimDescsSide[] = { { ANIM_STD_WALK, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION | ASSOC_WALK }, { ANIM_STD_RUN, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION | ASSOC_WALK }, { ANIM_STD_RUNFAST, ASSOC_REPEAT | ASSOC_MOVEMENT | ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION | ASSOC_WALK }, { ANIM_STD_IDLE, ASSOC_REPEAT }, { ANIM_STD_STARTWALK, ASSOC_HAS_TRANSLATION | ASSOC_HAS_X_TRANSLATION }, }; #endif char const* aStdAnimations[] = { "walk_civi", "run_civi", "sprint_panic", "idle_stance", "walk_start", "run_stop", "run_stopR", "idle_hbhb", "idle_hbhb", "idle_tired", "idle_armed", "idle_chat", "idle_taxi", "KO_shot_front", "KO_shot_front", "KO_shot_front", "KO_shot_front", "KO_shot_face", "KO_shot_stom", "KO_shot_arml", "KO_shot_armR", "KO_shot_legl", "KO_shot_legR", "KD_left", "KD_right", "KO_skid_front", "KO_spin_R", "KO_skid_back", "KO_spin_L", "SHOT_partial", "SHOT_leftP", "SHOT_partial", "SHOT_rightP", "HIT_front", "HIT_L", "HIT_back", "HIT_R", "FLOOR_hit", "HIT_bodyblow", "HIT_chest", "HIT_head", "HIT_walk", "HIT_wall", "FLOOR_hit_f", "HIT_behind", "FIGHTIDLE", "FIGHT2IDLE", "FIGHTsh_F", "FIGHTbodyblow", "FIGHThead", "FIGHTkick", "FIGHTknee", "FIGHTLhook", "FIGHTpunch", "FIGHTrndhse", "FIGHTlngkck", "FIGHTppunch", "FIGHTjab", "FIGHTelbowL", "FIGHTelbowR", "FIGHTbkickL", "FIGHTbkickR", "bomber", "punchR", "FIGHTppunch", "KICK_floor", "WEAPON_throwu", "FIGHTsh_back", "car_jackedRHS", "car_LjackedRHS", "car_jackedLHS", "car_LjackedLHS", "CAR_Qjack", "CAR_Qjacked", "CAR_align_LHS", "CAR_alignHI_LHS", "CAR_open_LHS", "CAR_doorlocked_LHS", "CAR_pullout_LHS", "CAR_pulloutL_LHS", "CAR_getin_LHS", "CAR_getinL_LHS", "CAR_closedoor_LHS", "CAR_closedoorL_LHS", "CAR_rolldoor", "CAR_rolldoorLO", "CAR_jumpin_LHS", "CAR_getout_LHS", "CAR_getoutL_LHS", "CAR_close_LHS", "CAR_align_RHS", "CAR_alignHI_RHS", "CAR_open_RHS", "CAR_doorlocked_RHS", "CAR_pullout_RHS", "CAR_pulloutL_RHS", "CAR_getin_RHS", "CAR_getinL_RHS", "CAR_closedoor_RHS", "CAR_closedoorL_RHS", "CAR_shuffle_RHS", "CAR_Lshuffle_RHS", "CAR_sit", "CAR_Lsit", "CAR_sitp", "CAR_sitpLO", "DRIVE_L", "Drive_R", "Drive_LO_l", "Drive_LO_R", "Driveby_L", "Driveby_R", "DrivebyL_L", "DrivebyL_R", "CAR_LB", "DRIVE_BOAT", "DRIVE_BOAT_L", "DRIVE_BOAT_R", "DRIVE_BOAT_back", "BIKE_pickupR", "BIKE_pickupL", "BIKE_pullupR", "BIKE_pullupL", "BIKE_elbowR", "BIKE_elbowL", "BIKE_fall_off", "BIKE_fallR", "CAR_getout_RHS", "CAR_getoutL_RHS", "CAR_close_RHS", "car_hookertalk", "idle_stance", "idle_stance", "CAR_crawloutRHS", "CAR_crawloutRHS", "CAR_rollout_LHS", "CAR_rollout_LHS", "Getup", "Getup", "Getup", "Getup_front", "JUMP_launch", "JUMP_glide", "JUMP_land", "FALL_fall", "FALL_glide", "FALL_land", "FALL_collapse", "FALL_back", "FALL_front", "EV_step", "EV_dive", "XPRESSscratch", "roadcross", "TURN_180", "ARRESTgun", "DROWN", "DUCK_down", "DUCK_low", "WEAPON_crouch", "RBLOCK_Cshoot", "handsup", "handsCOWER", "FUCKU", "PHONE_in", "PHONE_out", "PHONE_talk", "SEAT_down", "SEAT_up", "SEAT_idle", "SEAT_down", "ATM", "abseil", }; char const* aVanAnimations[] = { "VAN_openL", "VAN_getinL", "VAN_closeL", "VAN_getoutL", "VAN_open", "VAN_getin", "VAN_close", "VAN_getout", }; char const* aCoachAnimations[] = { "COACH_opnL", "COACH_opnL", "COACH_inL", "COACH_inL", "COACH_outL", }; char const* aBikesAnimations[] = { "BIKEs_Ride", "BIKEs_Still", "BIKEs_Left", "BIKEs_Right", "BIKEs_Back", "BIKEs_Fwd", "BIKEs_pushes", "BIKEs_jumponR", "BIKEs_jumponL", "BIKEs_kick", "BIKEs_hit", "BIKEs_getoffRHS", "BIKEs_getoffLHS", "BIKEs_getoffBACK", "BIKEs_drivebyLHS", "BIKEs_drivebyRHS", "BIKEs_drivebyFT", "BIKEs_passenger", }; char const* aBikevAnimations[] = { "BIKEv_Ride", "BIKEv_Still", "BIKEv_Left", "BIKEv_Right", "BIKEv_Back", "BIKEv_Fwd", "BIKEv_pushes", "BIKEv_jumponR", "BIKEv_jumponL", "BIKEv_kick", "BIKEv_hit", "BIKEv_getoffRHS", "BIKEv_getoffLHS", "BIKEv_getoffBACK", "BIKEv_drivebyLHS", "BIKEv_drivebyRHS", "BIKEv_drivebyFT", "BIKEv_passenger", }; char const* aBikehAnimations[] = { "BIKEh_Ride", "BIKEh_Still", "BIKEh_Left", "BIKEh_Right", "BIKEh_Back", "BIKEh_Fwd", "BIKEh_pushes", "BIKEh_jumponR", "BIKEh_jumponL", "BIKEh_kick", "BIKEh_hit", "BIKEh_getoffRHS", "BIKEh_getoffLHS", "BIKEh_getoffBACK", "BIKEh_drivebyLHS", "BIKEh_drivebyRHS", "BIKEh_drivebyFT", "BIKEh_passenger", }; char const* aBikedAnimations[] = { "BIKEd_Ride", "BIKEd_Still", "BIKEd_Left", "BIKEd_Right", "BIKEd_Back", "BIKEd_Fwd", "BIKEd_pushes", "BIKEd_jumponR", "BIKEd_jumponL", "BIKEd_kick", "BIKEd_hit", "BIKEd_getoffRHS", "BIKEd_getoffLHS", "BIKEd_getoffBACK", "BIKEd_drivebyLHS", "BIKEd_drivebyRHS", "BIKEd_drivebyFT", "BIKEd_passenger", }; char const* aUnarmedAnimations[] = { "punchR", "KICK_floor", "FIGHTppunch", }; char const* aScrewdriverAnimations[] = { "FIGHTbodyblow", "FIGHTbodyblow", "FIGHTppunch", "FIGHTIDLE", "FIGHTbodyblow", }; char const* aKnifeAnimations[] = { "WEAPON_knife_1", "WEAPON_knife_2", "knife_part", "WEAPON_knifeidle", "WEAPON_knife_3", }; char const* aBaseballbatAnimations[] = { "WEAPON_bat_h", "WEAPON_bat_v", "BAT_PART", "WEAPON_bat_h", "WEAPON_golfclub", }; char const* aGolfclubAnimations[] = { "WEAPON_bat_h", "WEAPON_golfclub", "BAT_PART", "WEAPON_bat_h", "WEAPON_bat_v", }; char const* aChainsawAnimations[] = { "WEAPON_csaw", "WEAPON_csawlo", "csaw_part", }; char const* aPythonAnimations[] = { "python_fire", "python_crouchfire", "python_reload", "python_crouchreload", }; char const* aColtAnimations[] = { "colt45_fire", "colt45_crouchfire", "colt45_reload", "colt45_crouchreload", "colt45_cop", }; char const* aShotgunAnimations[] = { "shotgun_fire", "shotgun_crouchfire", }; char const* aBuddyAnimations[] = { "buddy_fire", "buddy_crouchfire", }; char const* aTecAnimations[] = { "TEC_fire", "TEC_crouchfire", "TEC_reload", "TEC_crouchreload", }; char const* aUziAnimations[] = { "UZI_fire", "UZI_crouchfire", "UZI_reload", "UZI_crouchreload", }; char const* aRifleAnimations[] = { "RIFLE_fire", "RIFLE_crouchfire", "RIFLE_load", "RIFLE_crouchload", }; char const* aM60Animations[] = { "M60_fire", "M60_fire", "M60_reload", }; char const* aSniperAnimations[] = { "WEAPON_sniper", }; char const* aThrowAnimations[] = { "WEAPON_throw", "WEAPON_throwu", "WEAPON_start_throw", }; char const* aFlamethrowerAnimations[] = { "FLAME_fire", }; char const* aMedicAnimations[] = { "CPR", }; char const* aSunbatheAnimations[] = { "bather", "batherdown", "batherup", "batherscape", }; char const* aPlayerIdleAnimations[] = { "stretch", "time", "shldr", "strleg", }; char const* aRiotAnimations[] = { "riot_angry", "riot_angry_b", "riot_chant", "riot_punches", "riot_shout", "riot_challenge", "riot_fuku", }; char const* aStripAnimations[] = { "strip_A", "strip_B", "strip_C", "strip_D", "strip_E", "strip_F", "strip_G", }; char const* aLanceAnimations[] = { "lance", }; char const* aPlayerAnimations[] = { "walk_player", "run_player", "SPRINT_civi", "IDLE_STANCE", "walk_start", }; char const* aPlayerWithRocketAnimations[] = { "walk_rocket", "run_rocket", "run_rocket", "idle_rocket", "walk_start_rocket", }; char const* aPlayer1ArmedAnimations[] = { "walk_player", "run_1armed", "SPRINT_civi", "IDLE_STANCE", "walk_start", }; char const* aPlayer2ArmedAnimations[] = { "walk_armed", "run_armed", "run_armed", "idle_armed", "walk_start_armed", }; char const* aPlayerBBBatAnimations[] = { "walk_player", "run_player", "run_player", "IDLE_STANCE", "walk_start", }; char const* aPlayerChainsawAnimations[] = { "walk_csaw", "run_csaw", "run_csaw", "IDLE_csaw", "walk_start_csaw", }; char const* aShuffleAnimations[] = { "WALK_shuffle", "RUN_civi", "SPRINT_civi", "IDLE_STANCE", }; char const* aOldAnimations[] = { "walk_old", "run_civi", "sprint_civi", "idle_stance", }; char const* aGang1Animations[] = { "walk_gang1", "run_gang1", "sprint_civi", "idle_stance", }; char const* aGang2Animations[] = { "walk_gang2", "run_gang1", "sprint_civi", "idle_stance", }; char const* aFatAnimations[] = { "walk_fat", "run_civi", "woman_runpanic", "idle_stance", }; char const* aOldFatAnimations[] = { "walk_fatold", "run_fatold", "woman_runpanic", "idle_stance", }; char const* aJoggerAnimations[] = { "JOG_maleA", "run_civi", "sprint_civi", "idle_stance", }; char const* aStdWomanAnimations[] = { "woman_walknorm", "woman_run", "woman_runpanic", "woman_idlestance", }; char const* aWomanShopAnimations[] = { "woman_walkshop", "woman_run", "woman_run", "woman_idlestance", }; char const* aBusyWomanAnimations[] = { "woman_walkbusy", "woman_run", "woman_runpanic", "woman_idlestance", }; char const* aSexyWomanAnimations[] = { "woman_walksexy", "woman_run", "woman_runpanic", "woman_idlestance", }; char const* aFatWomanAnimations[] = { "walk_fat", "woman_run", "woman_runpanic", "woman_idlestance", }; char const* aOldWomanAnimations[] = { "woman_walkold", "woman_run", "woman_runpanic", "woman_idlestance", }; char const* aJoggerWomanAnimations[] = { "JOG_maleB", "woman_run", "woman_runpanic", "woman_idlestance", }; char const* aPanicChunkyAnimations[] = { "run_fatold", "woman_runpanic", "woman_runpanic", "idle_stance", }; char const* aSkateAnimations[] = { "skate_run", "skate_sprint", "skate_sprint", "skate_idle", }; #ifdef PC_PLAYER_CONTROLS char const* aPlayerStrafeBackAnimations[] = { "walk_back", "run_back", "run_back", "IDLE_STANCE", "walk_start_back", }; char const* aPlayerStrafeLeftAnimations[] = { "walk_left", "run_left", "run_left", "IDLE_STANCE", "walk_start_left", }; char const* aPlayerStrafeRightAnimations[] = { "walk_right", "run_right", "run_right", "IDLE_STANCE", "walk_start_right", }; char const* aRocketStrafeBackAnimations[] = { "walk_rocket_back", "run_rocket_back", "run_rocket_back", "idle_rocket", "walkst_rocket_back", }; char const* aRocketStrafeLeftAnimations[] = { "walk_rocket_left", "run_rocket_left", "run_rocket_left", "idle_rocket", "walkst_rocket_left", }; char const* aRocketStrafeRightAnimations[] = { "walk_rocket_right", "run_rocket_right", "run_rocket_right", "idle_rocket", "walkst_rocket_right", }; char const* aChainsawStrafeBackAnimations[] = { "walk_csaw_back", "run_csaw_back", "run_csaw_back", "idle_csaw", "walkst_csaw_back", }; char const* aChainsawStrafeLeftAnimations[] = { "walk_csaw_left", "run_csaw_left", "run_csaw_left", "idle_csaw", "walkst_csaw_left", }; char const* aChainsawStrafeRightAnimations[] = { "walk_csaw_right", "run_csaw_right", "run_csaw_right", "idle_csaw", "walkst_csaw_right", }; #endif #define awc(a) ARRAY_SIZE(a), a const AnimAssocDefinition CAnimManager::ms_aAnimAssocDefinitions[NUM_ANIM_ASSOC_GROUPS] = { { "man", "ped", MI_COP, awc(aStdAnimations), aStdAnimDescs }, { "van", "van", MI_COP, awc(aVanAnimations), aVanAnimDescs }, { "coach", "coach", MI_COP, awc(aCoachAnimations), aCoachAnimDescs }, { "bikes", "bikes", MI_COP, awc(aBikesAnimations), aBikeAnimDescs }, { "bikev", "bikev", MI_COP, awc(aBikevAnimations), aBikeAnimDescs }, { "bikeh", "bikeh", MI_COP, awc(aBikehAnimations), aBikeAnimDescs }, { "biked", "biked", MI_COP, awc(aBikedAnimations), aBikeAnimDescs }, { "unarmed", "ped", MI_COP, awc(aUnarmedAnimations), aMeleeAnimDescs }, { "screwdrv", "ped", MI_COP, awc(aScrewdriverAnimations), aMeleeAnimDescs }, { "knife", "knife", MI_COP, awc(aKnifeAnimations), aMeleeAnimDescs }, { "baseball", "baseball", MI_COP, awc(aBaseballbatAnimations), aSwingAnimDescs }, { "golfclub", "baseball", MI_COP, awc(aGolfclubAnimations), aSwingAnimDescs }, { "chainsaw", "chainsaw", MI_COP, awc(aChainsawAnimations), aMeleeAnimDescs }, { "python", "python", MI_COP, awc(aPythonAnimations), aWeaponAnimDescs }, { "colt45", "colt45", MI_COP, awc(aColtAnimations), aWeaponAnimDescs }, { "shotgun", "shotgun", MI_COP, awc(aShotgunAnimations), aWeaponAnimDescs }, { "buddy", "buddy", MI_COP, awc(aBuddyAnimations), aWeaponAnimDescs }, { "tec", "tec", MI_COP, awc(aTecAnimations), aWeaponAnimDescs }, { "uzi", "uzi", MI_COP, awc(aUziAnimations), aWeaponAnimDescs }, { "rifle", "rifle", MI_COP, awc(aRifleAnimations), aWeaponAnimDescs }, { "m60", "m60", MI_COP, awc(aM60Animations), aWeaponAnimDescs }, { "sniper", "sniper", MI_COP, awc(aSniperAnimations), aWeaponAnimDescs }, { "grenade", "grenade", MI_COP, awc(aThrowAnimations), aWeaponAnimDescs }, { "flame", "flame", MI_COP, awc(aFlamethrowerAnimations), aWeaponAnimDescs }, { "medic", "medic", MI_COP, awc(aMedicAnimations), aMedicAnimDescs }, { "sunbathe", "sunbathe", MI_COP, 1, aSunbatheAnimations, aSunbatheAnimDescs }, // NB: not using awc here! { "playidles", "playidles", MI_COP, awc(aPlayerIdleAnimations), aPlayerIdleAnimDescs }, { "riot", "riot", MI_COP, awc(aRiotAnimations), aRiotAnimDescs }, { "strip", "strip", MI_COP, awc(aStripAnimations), aStripAnimDescs }, { "lance", "lance", MI_COP, awc(aLanceAnimations), aSunbatheAnimDescs }, { "player", "ped", MI_COP, awc(aPlayerAnimations), aStdAnimDescs }, { "playerrocket", "ped", MI_COP, awc(aPlayerWithRocketAnimations), aStdAnimDescs }, { "player1armed", "ped", MI_COP, awc(aPlayer1ArmedAnimations), aStdAnimDescs }, { "player2armed", "ped", MI_COP, awc(aPlayer2ArmedAnimations), aStdAnimDescs }, { "playerBBBat", "ped", MI_COP, awc(aPlayerBBBatAnimations), aStdAnimDescs }, { "playercsaw", "ped", MI_COP, awc(aPlayerChainsawAnimations), aStdAnimDescs }, { "shuffle", "ped", MI_COP, awc(aShuffleAnimations), aStdAnimDescs }, { "oldman", "ped", MI_COP, awc(aOldAnimations), aStdAnimDescs }, { "gang1", "ped", MI_COP, awc(aGang1Animations), aStdAnimDescs }, { "gang2", "ped", MI_COP, awc(aGang2Animations), aStdAnimDescs }, { "fatman", "ped", MI_COP, awc(aFatAnimations), aStdAnimDescs }, { "oldfatman", "ped", MI_COP, awc(aOldFatAnimations), aStdAnimDescs }, { "jogger", "ped", MI_COP, awc(aJoggerAnimations), aStdAnimDescs }, { "woman", "ped", MI_COP, awc(aStdWomanAnimations), aStdAnimDescs }, { "shopping", "ped", MI_COP, awc(aWomanShopAnimations), aStdAnimDescs }, { "busywoman", "ped", MI_COP, awc(aBusyWomanAnimations), aStdAnimDescs }, { "sexywoman", "ped", MI_COP, awc(aSexyWomanAnimations), aStdAnimDescs }, { "fatwoman", "ped", MI_COP, awc(aFatWomanAnimations), aStdAnimDescs }, { "oldwoman", "ped", MI_COP, awc(aOldWomanAnimations), aStdAnimDescs }, { "jogwoman", "ped", MI_COP, awc(aJoggerWomanAnimations), aStdAnimDescs }, { "panicchunky", "ped", MI_COP, awc(aPanicChunkyAnimations), aStdAnimDescs }, { "skate", "skate", MI_COP, awc(aSkateAnimations), aStdAnimDescs }, #ifdef PC_PLAYER_CONTROLS { "playerback", "ped", MI_COP, awc(aPlayerStrafeBackAnimations), aStdAnimDescs }, { "playerleft", "ped", MI_COP, awc(aPlayerStrafeLeftAnimations), aStdAnimDescsSide }, { "playerright", "ped", MI_COP, awc(aPlayerStrafeRightAnimations), aStdAnimDescsSide }, { "rocketback", "ped", MI_COP, awc(aRocketStrafeBackAnimations), aStdAnimDescs }, { "rocketleft", "ped", MI_COP, awc(aRocketStrafeLeftAnimations), aStdAnimDescsSide }, { "rocketright", "ped", MI_COP, awc(aRocketStrafeRightAnimations), aStdAnimDescsSide }, { "csawback", "ped", MI_COP, awc(aChainsawStrafeBackAnimations), aStdAnimDescs }, { "csawleft", "ped", MI_COP, awc(aChainsawStrafeLeftAnimations), aStdAnimDescsSide }, { "csawright", "ped", MI_COP, awc(aChainsawStrafeRightAnimations), aStdAnimDescsSide }, #endif }; #undef awc void CAnimManager::Initialise(void) { ms_numAnimations = 0; ms_numAnimBlocks = 0; ms_animCache.Init(25); } void CAnimManager::Shutdown(void) { int i; for(i = 0; i < NUMANIMBLOCKS; i++) CStreaming::RemoveAnim(i); for(i = 0; i < ms_numAnimations; i++) ms_aAnimations[i].Shutdown(); ms_animCache.Shutdown(); delete[] ms_aAnimAssocGroups; } void CAnimManager::UncompressAnimation(CAnimBlendHierarchy *hier) { if(hier->keepCompressed){ if(hier->totalLength == 0.0f) hier->CalcTotalTimeCompressed(); }else{ if(!hier->compressed){ if(hier->linkPtr){ hier->linkPtr->Remove(); ms_animCache.head.Insert(hier->linkPtr); } }else{ CLink *link = ms_animCache.Insert(hier); if(link == nil){ CAnimBlendHierarchy *lastHier = ms_animCache.tail.prev->item; lastHier->RemoveUncompressedData(); ms_animCache.Remove(ms_animCache.tail.prev); lastHier->linkPtr = nil; link = ms_animCache.Insert(hier); } hier->linkPtr = link; hier->Uncompress(); } } } void CAnimManager::RemoveFromUncompressedCache(CAnimBlendHierarchy *hier) { if(hier->linkPtr){ ms_animCache.Remove(hier->linkPtr); hier->linkPtr = nil; } } CAnimBlock* CAnimManager::GetAnimationBlock(const char *name) { int i; for(i = 0; i < ms_numAnimBlocks; i++) if(strcasecmp(ms_aAnimBlocks[i].name, name) == 0) return &ms_aAnimBlocks[i]; return nil; } int32 CAnimManager::GetAnimationBlockIndex(const char *name) { int i; for(i = 0; i < ms_numAnimBlocks; i++) if(strcasecmp(ms_aAnimBlocks[i].name, name) == 0) return i; return -1; } int32 CAnimManager::RegisterAnimBlock(const char *name) { CAnimBlock *animBlock = GetAnimationBlock(name); if(animBlock == nil){ animBlock = &ms_aAnimBlocks[ms_numAnimBlocks++]; strncpy(animBlock->name, name, MAX_ANIMBLOCK_NAME); animBlock->numAnims = 0; assert(animBlock->refCount == 0); } return animBlock - ms_aAnimBlocks; } int32 CAnimManager::GetNumRefsToAnimBlock(int32 block) { return ms_aAnimBlocks[block].refCount; } void CAnimManager::AddAnimBlockRef(int32 block) { ms_aAnimBlocks[block].refCount++; } void CAnimManager::RemoveAnimBlockRefWithoutDelete(int32 block) { ms_aAnimBlocks[block].refCount--; } void CAnimManager::RemoveAnimBlockRef(int32 block) { ms_aAnimBlocks[block].refCount--; if(ms_aAnimBlocks[block].refCount == 0) CStreaming::RemoveAnim(block); } void CAnimManager::RemoveAnimBlock(int32 block) { int i; CAnimBlock *animblock; animblock = &ms_aAnimBlocks[block]; debug("Removing ANIMS %s\n", animblock->name); for(i = 0; i < NUM_ANIM_ASSOC_GROUPS; i++) if(ms_aAnimAssocGroups[i].animBlock == animblock) ms_aAnimAssocGroups[i].DestroyAssociations(); for(i = 0; i < animblock->numAnims; i++) ms_aAnimations[animblock->firstIndex + i].Shutdown(); animblock->isLoaded = false; animblock->refCount = 0; } CAnimBlendHierarchy* CAnimManager::GetAnimation(const char *name, CAnimBlock *animBlock) { int i; CAnimBlendHierarchy *hier = &ms_aAnimations[animBlock->firstIndex]; for(i = 0; i < animBlock->numAnims; i++){ if(strcasecmp(hier->name, name) == 0) return hier; hier++; } return nil; } const char* CAnimManager::GetAnimGroupName(AssocGroupId groupId) { return ms_aAnimAssocDefinitions[groupId].name; } CAnimBlendAssociation* CAnimManager::CreateAnimAssociation(AssocGroupId groupId, AnimationId animId) { return ms_aAnimAssocGroups[groupId].CopyAnimation(animId); } CAnimBlendAssociation* CAnimManager::GetAnimAssociation(AssocGroupId groupId, AnimationId animId) { return ms_aAnimAssocGroups[groupId].GetAnimation(animId); } CAnimBlendAssociation* CAnimManager::GetAnimAssociation(AssocGroupId groupId, const char *name) { return ms_aAnimAssocGroups[groupId].GetAnimation(name); } CAnimBlendAssociation* CAnimManager::AddAnimation(RpClump *clump, AssocGroupId groupId, AnimationId animId) { CAnimBlendAssociation *anim = CreateAnimAssociation(groupId, animId); CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); if(anim->IsMovement()){ CAnimBlendAssociation *syncanim = nil; CAnimBlendLink *link; for(link = clumpData->link.next; link; link = link->next){ syncanim = CAnimBlendAssociation::FromLink(link); if(syncanim->IsMovement()) break; } if(link){ anim->SyncAnimation(syncanim); anim->flags |= ASSOC_RUNNING; }else anim->Start(0.0f); }else anim->Start(0.0f); clumpData->link.Prepend(&anim->link); return anim; } CAnimBlendAssociation* CAnimManager::AddAnimationAndSync(RpClump *clump, CAnimBlendAssociation *syncanim, AssocGroupId groupId, AnimationId animId) { CAnimBlendAssociation *anim = CreateAnimAssociation(groupId, animId); CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); if (anim->IsMovement() && syncanim){ anim->SyncAnimation(syncanim); anim->flags |= ASSOC_RUNNING; }else anim->Start(0.0f); clumpData->link.Prepend(&anim->link); return anim; } CAnimBlendAssociation* CAnimManager::BlendAnimation(RpClump *clump, AssocGroupId groupId, AnimationId animId, float delta) { int removePrevAnim = 0; CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); CAnimBlendAssociation *anim = GetAnimAssociation(groupId, animId); bool isMovement = anim->IsMovement(); bool isPartial = anim->IsPartial(); CAnimBlendLink *link; CAnimBlendAssociation *found = nil, *movementAnim = nil; for(link = clumpData->link.next; link; link = link->next){ anim = CAnimBlendAssociation::FromLink(link); if(isMovement && anim->IsMovement()) movementAnim = anim; if(anim->animId == animId) found = anim; else{ if(isPartial == anim->IsPartial()){ if(anim->blendAmount > 0.0f){ float blendDelta = -delta*anim->blendAmount; if(blendDelta < anim->blendDelta || !isPartial) anim->blendDelta = blendDelta; }else{ anim->blendDelta = -1.0f; } anim->flags |= ASSOC_DELETEFADEDOUT; removePrevAnim = 1; } } } if(found){ found->blendDelta = (1.0f - found->blendAmount)*delta; if(!found->IsRunning() && found->currentTime == found->hierarchy->totalLength) found->Start(0.0f); }else{ found = AddAnimationAndSync(clump, movementAnim, groupId, animId); if(!removePrevAnim && !isPartial){ found->blendAmount = 1.0f; return found; } found->blendAmount = 0.0f; found->blendDelta = delta; } UncompressAnimation(found->hierarchy); return found; } void CAnimManager::LoadAnimFiles(void) { LoadAnimFile("ANIM\\PED.IFP"); ms_aAnimAssocGroups = new CAnimBlendAssocGroup[NUM_ANIM_ASSOC_GROUPS]; CreateAnimAssocGroups(); } void CAnimManager::CreateAnimAssocGroups(void) { int i, j; for(i = 0; i < NUM_ANIM_ASSOC_GROUPS; i++){ CAnimBlock *block = GetAnimationBlock(ms_aAnimAssocDefinitions[i].blockName); if(block == nil || !block->isLoaded || ms_aAnimAssocGroups[i].assocList) continue; CBaseModelInfo *mi = CModelInfo::GetModelInfo(ms_aAnimAssocDefinitions[i].modelIndex); RpClump *clump = (RpClump*)mi->CreateInstance(); RpAnimBlendClumpInit(clump); CAnimBlendAssocGroup *group = &ms_aAnimAssocGroups[i]; const AnimAssocDefinition *def = &ms_aAnimAssocDefinitions[i]; group->groupId = i; group->firstAnimId = def->animDescs[0].animId; group->CreateAssociations(def->blockName, clump, def->animNames, def->numAnims); for(j = 0; j < group->numAssociations; j++) // GetAnimation(i) in III (but it's in LoadAnimFiles), GetAnimation(group->animDesc[j].animId) in VC group->GetAnimation(def->animDescs[j].animId)->flags |= def->animDescs[j].flags; if(IsClumpSkinned(clump)) RpClumpForAllAtomics(clump, AtomicRemoveAnimFromSkinCB, nil); RpClumpDestroy(clump); } } void CAnimManager::LoadAnimFile(const char *filename) { RwStream *stream; stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); assert(stream); LoadAnimFile(stream, true); RwStreamClose(stream, nil); } void CAnimManager::LoadAnimFile(RwStream *stream, bool compress, char (*uncompressedAnims)[32]) { #define ROUNDSIZE(x) if((x) & 3) (x) += 4 - ((x)&3) struct IfpHeader { char ident[4]; uint32 size; }; IfpHeader anpk, info, name, dgan, cpan, anim; char buf[256]; int j, k, l; float *fbuf = (float*)buf; // block name RwStreamRead(stream, &anpk, sizeof(IfpHeader)); ROUNDSIZE(anpk.size); RwStreamRead(stream, &info, sizeof(IfpHeader)); ROUNDSIZE(info.size); RwStreamRead(stream, buf, info.size); CAnimBlock *animBlock = GetAnimationBlock(buf+4); if(animBlock){ if(animBlock->numAnims == 0){ animBlock->numAnims = *(int*)buf; animBlock->firstIndex = ms_numAnimations; } }else{ animBlock = &ms_aAnimBlocks[ms_numAnimBlocks++]; strncpy(animBlock->name, buf+4, MAX_ANIMBLOCK_NAME); animBlock->numAnims = *(int*)buf; animBlock->firstIndex = ms_numAnimations; } debug("Loading ANIMS %s\n", animBlock->name); animBlock->isLoaded = true; int animIndex = animBlock->firstIndex; for(j = 0; j < animBlock->numAnims; j++){ assert(animIndex < ARRAY_SIZE(ms_aAnimations)); CAnimBlendHierarchy *hier = &ms_aAnimations[animIndex++]; // animation name RwStreamRead(stream, &name, sizeof(IfpHeader)); ROUNDSIZE(name.size); RwStreamRead(stream, buf, name.size); hier->SetName(buf); #ifdef ANIM_COMPRESSION bool compressHier = compress; #else bool compressHier = false; #endif if (uncompressedAnims) { for (int i = 0; uncompressedAnims[i][0]; i++) { if (!CGeneral::faststricmp(uncompressedAnims[i], hier->name)){ debug("Loading %s uncompressed\n", hier->name); compressHier = false; } } } hier->compressed = compressHier; hier->keepCompressed = false; // DG info has number of nodes/sequences RwStreamRead(stream, (char*)&dgan, sizeof(IfpHeader)); ROUNDSIZE(dgan.size); RwStreamRead(stream, (char*)&info, sizeof(IfpHeader)); ROUNDSIZE(info.size); RwStreamRead(stream, buf, info.size); hier->numSequences = *(int*)buf; hier->sequences = new CAnimBlendSequence[hier->numSequences]; CAnimBlendSequence *seq = hier->sequences; for(k = 0; k < hier->numSequences; k++, seq++){ // Each node has a name and key frames RwStreamRead(stream, &cpan, sizeof(IfpHeader)); ROUNDSIZE(dgan.size); RwStreamRead(stream, &anim, sizeof(IfpHeader)); ROUNDSIZE(anim.size); RwStreamRead(stream, buf, anim.size); int numFrames = *(int*)(buf+28); seq->SetName(buf); if(anim.size == 44) seq->SetBoneTag(*(int*)(buf+40)); if(numFrames == 0) continue; bool hasScale = false; bool hasTranslation = false; RwStreamRead(stream, &info, sizeof(info)); if(strncmp(info.ident, "KRTS", 4) == 0){ hasScale = true; seq->SetNumFrames(numFrames, true, compressHier); }else if(strncmp(info.ident, "KRT0", 4) == 0){ hasTranslation = true; seq->SetNumFrames(numFrames, true, compressHier); }else if(strncmp(info.ident, "KR00", 4) == 0){ seq->SetNumFrames(numFrames, false, compressHier); } if(strstr(seq->name, "L Toe")) debug("anim %s has toe keyframes\n", hier->name); // BUG: seq->name for(l = 0; l < numFrames; l++){ if(hasScale){ RwStreamRead(stream, buf, 0x2C); CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]); rot.Invert(); CVector trans(fbuf[4], fbuf[5], fbuf[6]); if(compressHier){ KeyFrameTransCompressed *kf = (KeyFrameTransCompressed*)seq->GetKeyFrameCompressed(l); kf->SetRotation(rot); kf->SetTranslation(trans); // scaling ignored kf->SetTime(fbuf[10]); // absolute time here }else{ KeyFrameTrans *kf = (KeyFrameTrans*)seq->GetKeyFrame(l); kf->rotation = rot; kf->translation = trans; // scaling ignored kf->deltaTime = fbuf[10]; // absolute time here } }else if(hasTranslation){ RwStreamRead(stream, buf, 0x20); CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]); rot.Invert(); CVector trans(fbuf[4], fbuf[5], fbuf[6]); if(compressHier){ KeyFrameTransCompressed *kf = (KeyFrameTransCompressed*)seq->GetKeyFrameCompressed(l); kf->SetRotation(rot); kf->SetTranslation(trans); kf->SetTime(fbuf[7]); // absolute time here }else{ KeyFrameTrans *kf = (KeyFrameTrans*)seq->GetKeyFrame(l); kf->rotation = rot; kf->translation = trans; kf->deltaTime = fbuf[7]; // absolute time here } }else{ RwStreamRead(stream, buf, 0x14); CQuaternion rot(fbuf[0], fbuf[1], fbuf[2], fbuf[3]); rot.Invert(); if(compressHier){ KeyFrameCompressed *kf = (KeyFrameCompressed*)seq->GetKeyFrameCompressed(l); kf->SetRotation(rot); kf->SetTime(fbuf[4]); // absolute time here }else{ KeyFrame *kf = (KeyFrame*)seq->GetKeyFrame(l); kf->rotation = rot; kf->deltaTime = fbuf[4]; // absolute time here } } } } if(!compressHier){ hier->RemoveQuaternionFlips(); hier->CalcTotalTime(); } } if(animIndex > ms_numAnimations) ms_numAnimations = animIndex; } void CAnimManager::RemoveLastAnimFile(void) { int i; ms_numAnimBlocks--; ms_numAnimations = ms_aAnimBlocks[ms_numAnimBlocks].firstIndex; for(i = 0; i < ms_aAnimBlocks[ms_numAnimBlocks].numAnims; i++) ms_aAnimations[ms_aAnimBlocks[ms_numAnimBlocks].firstIndex + i].Shutdown(); ms_aAnimBlocks[ms_numAnimBlocks].isLoaded = false; } ================================================ FILE: src/animation/AnimManager.h ================================================ #pragma once #include "AnimBlendHierarchy.h" #include "AnimationId.h" enum AssocGroupId { ASSOCGRP_STD, ASSOCGRP_VAN, ASSOCGRP_COACH, ASSOCGRP_BIKE_STANDARD, ASSOCGRP_BIKE_VESPA, ASSOCGRP_BIKE_HARLEY, ASSOCGRP_BIKE_DIRT, ASSOCGRP_UNARMED, ASSOCGRP_SCREWDRIVER, ASSOCGRP_KNIFE, ASSOCGRP_BASEBALLBAT, ASSOCGRP_GOLFCLUB, ASSOCGRP_CHAINSAW, ASSOCGRP_PYTHON, ASSOCGRP_COLT, ASSOCGRP_SHOTGUN, ASSOCGRP_BUDDY, ASSOCGRP_TEC, ASSOCGRP_UZI, ASSOCGRP_RIFLE, ASSOCGRP_M60, ASSOCGRP_SNIPER, ASSOCGRP_THROW, ASSOCGRP_FLAMETHROWER, ASSOCGRP_MEDIC, ASSOCGRP_SUNBATHE, ASSOCGRP_PLAYER_IDLE, ASSOCGRP_RIOT, ASSOCGRP_STRIP, ASSOCGRP_LANCE, ASSOCGRP_PLAYER, ASSOCGRP_PLAYERROCKET, ASSOCGRP_PLAYER1ARMED, ASSOCGRP_PLAYER2ARMED, ASSOCGRP_PLAYERBBBAT, ASSOCGRP_PLAYERCHAINSAW, ASSOCGRP_SHUFFLE, ASSOCGRP_OLD, ASSOCGRP_GANG1, ASSOCGRP_GANG2, ASSOCGRP_FAT, ASSOCGRP_OLDFAT, ASSOCGRP_JOGGER, ASSOCGRP_WOMAN, ASSOCGRP_WOMANSHOP, ASSOCGRP_BUSYWOMAN, ASSOCGRP_SEXYWOMAN, ASSOCGRP_FATWOMAN, ASSOCGRP_OLDWOMAN, ASSOCGRP_JOGWOMAN, ASSOCGRP_PANICCHUNKY, ASSOCGRP_SKATE, #ifdef PC_PLAYER_CONTROLS ASSOCGRP_PLAYERBACK, ASSOCGRP_PLAYERLEFT, ASSOCGRP_PLAYERRIGHT, ASSOCGRP_ROCKETBACK, ASSOCGRP_ROCKETLEFT, ASSOCGRP_ROCKETRIGHT, ASSOCGRP_CHAINSAWBACK, ASSOCGRP_CHAINSAWLEFT, ASSOCGRP_CHAINSAWRIGHT, #endif NUM_ANIM_ASSOC_GROUPS }; class CAnimBlendAssociation; class CAnimBlendAssocGroup; #define MAX_ANIMBLOCK_NAME 20 // A block of hierarchies struct CAnimBlock { char name[MAX_ANIMBLOCK_NAME]; bool isLoaded; int16 refCount; int32 firstIndex; // first animtion in ms_aAnimations int32 numAnims; }; struct AnimAssocDesc { int32 animId; int32 flags; }; struct AnimAssocDefinition { char const *name; char const *blockName; int32 modelIndex; int32 numAnims; char const **animNames; AnimAssocDesc *animDescs; }; class CAnimManager { static const AnimAssocDefinition ms_aAnimAssocDefinitions[NUM_ANIM_ASSOC_GROUPS]; static CAnimBlock ms_aAnimBlocks[NUMANIMBLOCKS]; static CAnimBlendHierarchy ms_aAnimations[NUMANIMATIONS]; static int32 ms_numAnimBlocks; static int32 ms_numAnimations; static CAnimBlendAssocGroup *ms_aAnimAssocGroups; static CLinkList ms_animCache; public: static void Initialise(void); static void Shutdown(void); static void UncompressAnimation(CAnimBlendHierarchy *anim); static void RemoveFromUncompressedCache(CAnimBlendHierarchy *hier); static CAnimBlock *GetAnimationBlock(int32 block) { return &ms_aAnimBlocks[block]; } static CAnimBlock *GetAnimationBlock(const char *name); static int32 GetAnimationBlockIndex(const char *name); static int32 RegisterAnimBlock(const char *name); static int32 GetNumRefsToAnimBlock(int32 block); static void AddAnimBlockRef(int32 block); static void RemoveAnimBlockRefWithoutDelete(int32 block); static void RemoveAnimBlockRef(int32 block); static void RemoveAnimBlock(int32 block); static CAnimBlendHierarchy *GetAnimation(const char *name, CAnimBlock *animBlock); static CAnimBlendHierarchy *GetAnimation(int32 n) { return &ms_aAnimations[n]; } static const char *GetAnimGroupName(AssocGroupId groupId); static CAnimBlendAssociation *CreateAnimAssociation(AssocGroupId groupId, AnimationId animId); static CAnimBlendAssociation *GetAnimAssociation(AssocGroupId groupId, AnimationId animId); static CAnimBlendAssociation *GetAnimAssociation(AssocGroupId groupId, const char *name); static CAnimBlendAssociation *AddAnimation(RpClump *clump, AssocGroupId groupId, AnimationId animId); static CAnimBlendAssociation *AddAnimationAndSync(RpClump *clump, CAnimBlendAssociation *syncanim, AssocGroupId groupId, AnimationId animId); static CAnimBlendAssociation *BlendAnimation(RpClump *clump, AssocGroupId groupId, AnimationId animId, float delta); static void LoadAnimFiles(void); static void LoadAnimFile(const char *filename); static void LoadAnimFile(RwStream *stream, bool compress, char (*uncompressedAnims)[32] = nil); static void CreateAnimAssocGroups(void); static void RemoveLastAnimFile(void); static CAnimBlendAssocGroup* GetAnimAssocGroups(void) { return ms_aAnimAssocGroups; } }; ================================================ FILE: src/animation/AnimationId.h ================================================ #pragma once enum AnimationId { ANIM_STD_WALK, ANIM_STD_RUN, ANIM_STD_RUNFAST, ANIM_STD_IDLE, ANIM_STD_STARTWALK, ANIM_STD_RUNSTOP1, ANIM_STD_RUNSTOP2, ANIM_STD_IDLE_CAM, ANIM_STD_IDLE_HBHB, ANIM_STD_IDLE_TIRED, ANIM_STD_IDLE_BIGGUN, ANIM_STD_CHAT, ANIM_STD_HAILTAXI, ANIM_STD_KO_FRONT, ANIM_STD_KO_LEFT, ANIM_STD_KO_BACK, ANIM_STD_KO_RIGHT, ANIM_STD_KO_SHOT_FACE, ANIM_STD_KO_SHOT_STOMACH, ANIM_STD_KO_SHOT_ARM_L, ANIM_STD_KO_SHOT_ARM_R, ANIM_STD_KO_SHOT_LEG_L, ANIM_STD_KO_SHOT_LEG_R, ANIM_STD_SPINFORWARD_LEFT, ANIM_STD_SPINFORWARD_RIGHT, ANIM_STD_HIGHIMPACT_FRONT, ANIM_STD_HIGHIMPACT_LEFT, ANIM_STD_HIGHIMPACT_BACK, ANIM_STD_HIGHIMPACT_RIGHT, ANIM_STD_HITBYGUN_FRONT, ANIM_STD_HITBYGUN_LEFT, ANIM_STD_HITBYGUN_BACK, ANIM_STD_HITBYGUN_RIGHT, ANIM_STD_HIT_FRONT, ANIM_STD_HIT_LEFT, ANIM_STD_HIT_BACK, ANIM_STD_HIT_RIGHT, ANIM_STD_HIT_FLOOR, /* names made up */ ANIM_STD_HIT_BODYBLOW, ANIM_STD_HIT_CHEST, ANIM_STD_HIT_HEAD, ANIM_STD_HIT_WALK, /**/ ANIM_STD_HIT_WALL, ANIM_STD_HIT_FLOOR_FRONT, ANIM_STD_HIT_BEHIND, ANIM_STD_FIGHT_IDLE, ANIM_STD_FIGHT_2IDLE, ANIM_STD_FIGHT_SHUFFLE_F, /* names made up */ ANIM_STD_FIGHT_BODYBLOW, ANIM_STD_FIGHT_HEAD, ANIM_STD_FIGHT_KICK, ANIM_STD_FIGHT_KNEE, ANIM_STD_FIGHT_LHOOK, ANIM_STD_FIGHT_PUNCH, ANIM_STD_FIGHT_ROUNDHOUSE, ANIM_STD_FIGHT_LONGKICK, /**/ ANIM_STD_PARTIAL_PUNCH, /* names made up */ ANIM_STD_FIGHT_JAB, ANIM_STD_FIGHT_ELBOW_L, ANIM_STD_FIGHT_ELBOW_R, ANIM_STD_FIGHT_BKICK_L, ANIM_STD_FIGHT_BKICK_R, /**/ ANIM_STD_DETONATE, ANIM_STD_PUNCH, ANIM_STD_PARTIALPUNCH, ANIM_STD_KICKGROUND, ANIM_STD_THROW_UNDER, ANIM_STD_FIGHT_SHUFFLE_B, ANIM_STD_JACKEDCAR_RHS, ANIM_STD_JACKEDCAR_LO_RHS, ANIM_STD_JACKEDCAR_LHS, ANIM_STD_JACKEDCAR_LO_LHS, ANIM_STD_QUICKJACK, ANIM_STD_QUICKJACKED, ANIM_STD_CAR_ALIGN_DOOR_LHS, ANIM_STD_CAR_ALIGNHI_DOOR_LHS, ANIM_STD_CAR_OPEN_DOOR_LHS, ANIM_STD_CARDOOR_LOCKED_LHS, ANIM_STD_CAR_PULL_OUT_PED_LHS, ANIM_STD_CAR_PULL_OUT_PED_LO_LHS, ANIM_STD_CAR_GET_IN_LHS, ANIM_STD_CAR_GET_IN_LO_LHS, ANIM_STD_CAR_CLOSE_DOOR_LHS, ANIM_STD_CAR_CLOSE_DOOR_LO_LHS, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS, ANIM_STD_CAR_JUMP_IN_LO_LHS, ANIM_STD_GETOUT_LHS, ANIM_STD_GETOUT_LO_LHS, ANIM_STD_CAR_CLOSE_LHS, ANIM_STD_CAR_ALIGN_DOOR_RHS, ANIM_STD_CAR_ALIGNHI_DOOR_RHS, ANIM_STD_CAR_OPEN_DOOR_RHS, ANIM_STD_CARDOOR_LOCKED_RHS, ANIM_STD_CAR_PULL_OUT_PED_RHS, ANIM_STD_CAR_PULL_OUT_PED_LO_RHS, ANIM_STD_CAR_GET_IN_RHS, ANIM_STD_CAR_GET_IN_LO_RHS, ANIM_STD_CAR_CLOSE_DOOR_RHS, ANIM_STD_CAR_CLOSE_DOOR_LO_RHS, ANIM_STD_CAR_SHUFFLE_RHS, ANIM_STD_CAR_SHUFFLE_LO_RHS, ANIM_STD_CAR_SIT, ANIM_STD_CAR_SIT_LO, ANIM_STD_CAR_SIT_P, ANIM_STD_CAR_SIT_P_LO, ANIM_STD_CAR_DRIVE_LEFT, ANIM_STD_CAR_DRIVE_RIGHT, ANIM_STD_CAR_DRIVE_LEFT_LO, ANIM_STD_CAR_DRIVE_RIGHT_LO, ANIM_STD_CAR_DRIVEBY_LEFT, ANIM_STD_CAR_DRIVEBY_RIGHT, ANIM_STD_CAR_DRIVEBY_LEFT_LO, ANIM_STD_CAR_DRIVEBY_RIGHT_LO, ANIM_STD_CAR_LOOKBEHIND, ANIM_STD_BOAT_DRIVE, ANIM_STD_BOAT_DRIVE_LEFT, ANIM_STD_BOAT_DRIVE_RIGHT, ANIM_STD_BOAT_LOOKBEHIND, ANIM_STD_BIKE_PICKUP_LHS, ANIM_STD_BIKE_PICKUP_RHS, ANIM_STD_BIKE_PULLUP_LHS, ANIM_STD_BIKE_PULLUP_RHS, ANIM_STD_BIKE_ELBOW_LHS, ANIM_STD_BIKE_ELBOW_RHS, ANIM_STD_BIKE_FALLOFF, ANIM_STD_BIKE_FALLBACK, ANIM_STD_GETOUT_RHS, ANIM_STD_GETOUT_LO_RHS, ANIM_STD_CAR_CLOSE_RHS, ANIM_STD_CAR_HOOKERTALK, ANIM_STD_TRAIN_GETIN, ANIM_STD_TRAIN_GETOUT, ANIM_STD_CRAWLOUT_LHS, ANIM_STD_CRAWLOUT_RHS, ANIM_STD_ROLLOUT_LHS, ANIM_STD_ROLLOUT_RHS, ANIM_STD_GET_UP, ANIM_STD_GET_UP_LEFT, ANIM_STD_GET_UP_RIGHT, ANIM_STD_GET_UP_FRONT, ANIM_STD_JUMP_LAUNCH, ANIM_STD_JUMP_GLIDE, ANIM_STD_JUMP_LAND, ANIM_STD_FALL, ANIM_STD_FALL_GLIDE, ANIM_STD_FALL_LAND, ANIM_STD_FALL_COLLAPSE, ANIM_STD_FALL_ONBACK, ANIM_STD_FALL_ONFRONT, ANIM_STD_EVADE_STEP, ANIM_STD_EVADE_DIVE, ANIM_STD_XPRESS_SCRATCH, ANIM_STD_ROADCROSS, ANIM_STD_TURN180, ANIM_STD_ARREST, ANIM_STD_DROWN, ANIM_STD_DUCK_DOWN, ANIM_STD_DUCK_LOW, ANIM_STD_DUCK_WEAPON, ANIM_STD_RBLOCK_SHOOT, ANIM_STD_HANDSUP, ANIM_STD_HANDSCOWER, ANIM_STD_PARTIAL_FUCKU, ANIM_STD_PHONE_IN, ANIM_STD_PHONE_OUT, ANIM_STD_PHONE_TALK, ANIM_STD_SEAT_DOWN, ANIM_STD_SEAT_UP, ANIM_STD_SEAT_IDLE, ANIM_STD_SEAT_RVRS, ANIM_STD_ATM, ANIM_STD_ABSEIL, ANIM_STD_NUM, ANIM_STD_VAN_OPEN_DOOR_REAR_LHS, ANIM_STD_VAN_GET_IN_REAR_LHS, ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS, ANIM_STD_VAN_GET_OUT_REAR_LHS, ANIM_STD_VAN_OPEN_DOOR_REAR_RHS, ANIM_STD_VAN_GET_IN_REAR_RHS, ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS, ANIM_STD_VAN_GET_OUT_REAR_RHS, ANIM_STD_COACH_OPEN_LHS, ANIM_STD_COACH_OPEN_RHS, ANIM_STD_COACH_GET_IN_LHS, ANIM_STD_COACH_GET_IN_RHS, ANIM_STD_COACH_GET_OUT_LHS, ANIM_BIKE_RIDE, ANIM_BIKE_READY, ANIM_BIKE_LEFT, ANIM_BIKE_RIGHT, ANIM_BIKE_LEANB, ANIM_BIKE_LEANF, ANIM_BIKE_WALKBACK, ANIM_BIKE_JUMPON_LHS, ANIM_BIKE_JUMPON_RHS, ANIM_BIKE_KICK, ANIM_BIKE_HIT, ANIM_BIKE_GETOFF_LHS, ANIM_BIKE_GETOFF_RHS, ANIM_BIKE_GETOFF_BACK, ANIM_BIKE_DRIVEBY_LHS, ANIM_BIKE_DRIVEBY_RHS, ANIM_BIKE_DRIVEBY_FORWARD, ANIM_BIKE_RIDE_P, ANIM_ATTACK_1, ANIM_ATTACK_2, ANIM_ATTACK_EXTRA1, ANIM_ATTACK_EXTRA2, ANIM_ATTACK_3, // our synonyms... because originals are hard to understand ANIM_WEAPON_FIRE = ANIM_ATTACK_1, ANIM_WEAPON_CROUCHFIRE, ANIM_WEAPON_FIRE_2ND = ANIM_WEAPON_CROUCHFIRE, ANIM_WEAPON_RELOAD, ANIM_WEAPON_CROUCHRELOAD, ANIM_WEAPON_FIRE_3RD, ANIM_THROWABLE_THROW = ANIM_ATTACK_1, ANIM_THROWABLE_THROWU, ANIM_THROWABLE_START_THROW, ANIM_MELEE_ATTACK = ANIM_ATTACK_1, ANIM_MELEE_ATTACK_2ND, ANIM_MELEE_ATTACK_START, ANIM_MELEE_IDLE_FIGHTMODE, ANIM_MELEE_ATTACK_FINISH, ANIM_SUNBATHE_IDLE, ANIM_SUNBATHE_DOWN, ANIM_SUNBATHE_UP, ANIM_SUNBATHE_ESCAPE, ANIM_MEDIC_CPR, ANIM_PLAYER_IDLE1, ANIM_PLAYER_IDLE2, ANIM_PLAYER_IDLE3, ANIM_PLAYER_IDLE4, ANIM_RIOT_ANGRY, ANIM_RIOT_ANGRY_B, ANIM_RIOT_CHANT, ANIM_RIOT_PUNCHES, ANIM_RIOT_SHOUT, ANIM_RIOT_CHALLENGE, ANIM_RIOT_FUCKYOU, ANIM_STRIP_A, ANIM_STRIP_B, ANIM_STRIP_C, ANIM_STRIP_D, ANIM_STRIP_E, ANIM_STRIP_F, ANIM_STRIP_G, }; ================================================ FILE: src/animation/Bones.cpp ================================================ #include "common.h" #include "PedModelInfo.h" #include "Bones.h" int ConvertPedNode2BoneTag(int node) { switch(node){ case PED_MID: return BONE_spine1; case PED_HEAD: return BONE_head; case PED_UPPERARML: return BONE_l_upperarm; case PED_UPPERARMR: return BONE_r_upperarm; case PED_HANDL: return BONE_l_hand; case PED_HANDR: return BONE_r_hand; case PED_UPPERLEGL: return BONE_l_thigh; case PED_UPPERLEGR: return BONE_r_thigh; case PED_FOOTL: return BONE_l_foot; case PED_FOOTR: return BONE_r_foot; case PED_LOWERLEGR: return BONE_r_calf; case PED_LOWERLEGL: return BONE_l_calf; case PED_FOREARML: return BONE_l_forearm; case PED_FOREARMR: return BONE_r_forearm; case PED_CLAVICLEL: return BONE_l_clavicle; case PED_CLAVICLER: return BONE_r_clavicle; case PED_NECK: return BONE_neck; } assert(0 && "this node has no bone"); return -1; } const char* ConvertBoneTag2BoneName(int tag) { switch(tag){ case BONE_root: return "Root"; case BONE_pelvis: return "Pelvis"; case BONE_spine: return "Spine"; case BONE_spine1: return "Spine1"; case BONE_neck: return "Neck"; case BONE_head: return "Head"; case BONE_r_clavicle: return "Bip01 R Clavicle"; case BONE_r_upperarm: return "R UpperArm"; case BONE_r_forearm: return "R Forearm"; case BONE_r_hand: return "R Hand"; case BONE_r_finger: return "R Fingers"; case BONE_l_clavicle: return "Bip01 L Clavicle"; case BONE_l_upperarm: return "L UpperArm"; case BONE_l_forearm: return "L Forearm"; case BONE_l_hand: return "L Hand"; case BONE_l_finger: return "L Fingers"; case BONE_l_thigh: return "L Thigh"; case BONE_l_calf: return "L Calf"; case BONE_l_foot: return "L Foot"; case BONE_r_thigh: return "R Thigh"; case BONE_r_calf: return "R Calf"; case BONE_r_foot: return "R Foot"; } return nil; } ================================================ FILE: src/animation/Bones.h ================================================ #pragma once enum BoneTag { BONE_root = 0, BONE_pelvis = 1, BONE_spine = 2, BONE_spine1 = 3, BONE_neck = 4, BONE_head = 5, BONE_l_clavicle = 31, BONE_l_upperarm = 32, BONE_l_forearm = 33, BONE_l_hand = 34, BONE_l_finger = 35, BONE_r_clavicle = 21, BONE_r_upperarm = 22, BONE_r_forearm = 23, BONE_r_hand = 24, BONE_r_finger = 25, BONE_l_thigh = 41, BONE_l_calf = 42, BONE_l_foot = 43, BONE_r_thigh = 51, BONE_r_calf = 52, BONE_r_foot = 53, }; int ConvertPedNode2BoneTag(int node); const char *ConvertBoneTag2BoneName(int tag); ================================================ FILE: src/animation/CutsceneMgr.cpp ================================================ #include "common.h" #include "General.h" #include "CutsceneMgr.h" #include "Directory.h" #include "Camera.h" #include "Streaming.h" #include "FileMgr.h" #include "main.h" #include "AnimManager.h" #include "AnimBlendAssociation.h" #include "AnimBlendAssocGroup.h" #include "AnimBlendClumpData.h" #include "Pad.h" #include "DMAudio.h" #include "World.h" #include "PlayerPed.h" #include "Wanted.h" #include "RpAnimBlend.h" #include "ModelIndices.h" #include "TempColModels.h" #include "ColStore.h" #include "Radar.h" #include "Pools.h" const struct { const char *szTrackName; int iTrackId; } musicNameIdAssoc[] = { { "ASS_1", STREAMED_SOUND_CUTSCENE_ASS_1 }, { "ASS_2", STREAMED_SOUND_CUTSCENE_ASS_2 }, { "BANK_1", STREAMED_SOUND_CUTSCENE_BANK_1 }, { "BANK_2A", STREAMED_SOUND_CUTSCENE_BANK_2A }, { "BANK_2B", STREAMED_SOUND_CUTSCENE_BANK_2B }, { "BANK_3A", STREAMED_SOUND_CUTSCENE_BANK_3A }, { "BANK_3B", STREAMED_SOUND_CUTSCENE_BANK_3B }, { "BANK_4", STREAMED_SOUND_CUTSCENE_BANK_4 }, { "BIKE_1", STREAMED_SOUND_CUTSCENE_BIKE_1 }, { "BIKE_2", STREAMED_SOUND_CUTSCENE_BIKE_2 }, { "BIKE_3", STREAMED_SOUND_CUTSCENE_BIKE_3 }, { "BUD_1", STREAMED_SOUND_CUTSCENE_BUD_1 }, { "BUD_2", STREAMED_SOUND_CUTSCENE_BUD_2 }, { "BUD_3", STREAMED_SOUND_CUTSCENE_BUD_3 }, { "CAP_1", STREAMED_SOUND_CUTSCENE_CAP_1 }, { "CAR_1", STREAMED_SOUND_CUTSCENE_CAR_1 }, { "CNT_1A", STREAMED_SOUND_CUTSCENE_CNT_1A }, { "CNT_1B", STREAMED_SOUND_CUTSCENE_CNT_1B }, { "CNT_2", STREAMED_SOUND_CUTSCENE_CNT_2 }, { "COK_1", STREAMED_SOUND_CUTSCENE_COK_1 }, { "COK_2A", STREAMED_SOUND_CUTSCENE_COK_2A }, { "COK_2B", STREAMED_SOUND_CUTSCENE_COK_2B }, { "COK_3", STREAMED_SOUND_CUTSCENE_COK_3 }, { "COK_4A", STREAMED_SOUND_CUTSCENE_COK_4A }, { "COK_4A2", STREAMED_SOUND_CUTSCENE_COK_4A2 }, { "COK_4B", STREAMED_SOUND_CUTSCENE_COK_4B }, { "COL_1", STREAMED_SOUND_CUTSCENE_COL_1 }, { "COL_2", STREAMED_SOUND_CUTSCENE_COL_2 }, { "COL_3A", STREAMED_SOUND_CUTSCENE_COL_3A }, { "COL_4A", STREAMED_SOUND_CUTSCENE_COL_4A }, { "COL_5A", STREAMED_SOUND_CUTSCENE_COL_5A }, { "COL_5B", STREAMED_SOUND_CUTSCENE_COL_5B }, { "CUB_1", STREAMED_SOUND_CUTSCENE_CUB_1 }, { "CUB_2", STREAMED_SOUND_CUTSCENE_CUB_2 }, { "CUB_3", STREAMED_SOUND_CUTSCENE_CUB_3 }, { "CUB_4", STREAMED_SOUND_CUTSCENE_CUB_4 }, { "DRUG_1", STREAMED_SOUND_CUTSCENE_DRUG_1 }, { "FIN", STREAMED_SOUND_CUTSCENE_FIN }, { "FIN_2", STREAMED_SOUND_CUTSCENE_FIN2 }, { "FINALE", STREAMED_SOUND_CUTSCENE_FINALE }, { "HAT_1", STREAMED_SOUND_CUTSCENE_HAT_1 }, { "HAT_2", STREAMED_SOUND_CUTSCENE_HAT_2 }, { "HAT_3", STREAMED_SOUND_CUTSCENE_HAT_3 }, { "ICE_1", STREAMED_SOUND_CUTSCENE_ICE_1 }, { "INT_A", STREAMED_SOUND_CUTSCENE_INT_A }, { "INT_B", STREAMED_SOUND_CUTSCENE_INT_B }, { "INT_D", STREAMED_SOUND_CUTSCENE_INT_D }, { "INT_M", STREAMED_SOUND_CUTSCENE_INT_M }, { "LAW_1A", STREAMED_SOUND_CUTSCENE_LAW_1A }, { "LAW_1B", STREAMED_SOUND_CUTSCENE_LAW_1B }, { "LAW_2A", STREAMED_SOUND_CUTSCENE_LAW_2A }, { "LAW_2B", STREAMED_SOUND_CUTSCENE_LAW_2B }, { "LAW_2C", STREAMED_SOUND_CUTSCENE_LAW_2C }, { "LAW_3", STREAMED_SOUND_CUTSCENE_LAW_3 }, { "LAW_4", STREAMED_SOUND_CUTSCENE_LAW_4 }, { "PHIL_1", STREAMED_SOUND_CUTSCENE_PHIL_1 }, { "PHIL_2", STREAMED_SOUND_CUTSCENE_PHIL_2 }, { "PORN_1", STREAMED_SOUND_CUTSCENE_PORN_1 }, { "PORN_2", STREAMED_SOUND_CUTSCENE_PORN_2 }, { "PORN_3", STREAMED_SOUND_CUTSCENE_PORN_3 }, { "PORN_4", STREAMED_SOUND_CUTSCENE_PORN_4 }, { "RESC_1A", STREAMED_SOUND_CUTSCENE_RESC_1A }, { "ROK_1", STREAMED_SOUND_CUTSCENE_ROK_1 }, { "ROK_2", STREAMED_SOUND_CUTSCENE_ROK_2 }, { "ROK_3A", STREAMED_SOUND_CUTSCENE_ROK_3A }, { "STRIPA", STREAMED_SOUND_CUTSCENE_STRIPA }, { "TAX_1", STREAMED_SOUND_CUTSCENE_TAX_1 }, { "TEX_1", STREAMED_SOUND_CUTSCENE_TEX_1 }, { "TEX_2", STREAMED_SOUND_CUTSCENE_TEX_2 }, { "TEX_3", STREAMED_SOUND_CUTSCENE_TEX_3 }, { "GSPOT", STREAMED_SOUND_CUTSCENE_GLIGHT }, { "FIST", STREAMED_SOUND_CUTSCENE_FIST }, { "EL_PH1", STREAMED_SOUND_CUTSCENE_ELBURRO1_PH1 }, { "EL_PH2", STREAMED_SOUND_CUTSCENE_ELBURRO2_PH2 }, { NULL, 0 } }; int FindCutsceneAudioTrackId(const char *szCutsceneName) { for (int i = 0; musicNameIdAssoc[i].szTrackName; i++) { if (!CGeneral::faststricmp(musicNameIdAssoc[i].szTrackName, szCutsceneName)) return musicNameIdAssoc[i].iTrackId; } return -1; } bool CCutsceneMgr::ms_running; bool CCutsceneMgr::ms_cutsceneProcessing; CDirectory *CCutsceneMgr::ms_pCutsceneDir; CCutsceneObject *CCutsceneMgr::ms_pCutsceneObjects[NUMCUTSCENEOBJECTS]; int32 CCutsceneMgr::ms_numCutsceneObjs; bool CCutsceneMgr::ms_loaded; bool CCutsceneMgr::ms_animLoaded; bool CCutsceneMgr::ms_useLodMultiplier; char CCutsceneMgr::ms_cutsceneName[CUTSCENENAMESIZE]; CAnimBlendAssocGroup CCutsceneMgr::ms_cutsceneAssociations; CVector CCutsceneMgr::ms_cutsceneOffset; float CCutsceneMgr::ms_cutsceneTimer; bool CCutsceneMgr::ms_wasCutsceneSkipped; uint32 CCutsceneMgr::ms_cutsceneLoadStatus; bool CCutsceneMgr::ms_useCutsceneShadows = true; bool bCamLoaded; bool bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver; // pls don't shrink the name :P int32 NumberOfSavedWeapons; eWeaponType SavedWeaponIDs[TOTAL_WEAPON_SLOTS]; int32 SavedWeaponAmmo[TOTAL_WEAPON_SLOTS]; char uncompressedAnims[8][32]; uint32 numUncompressedAnims; RpAtomic * CalculateBoundingSphereRadiusCB(RpAtomic *atomic, void *data) { float radius = RpAtomicGetBoundingSphere(atomic)->radius; RwV3d center = RpAtomicGetBoundingSphere(atomic)->center; for (RwFrame *frame = RpAtomicGetFrame(atomic); RwFrameGetParent(frame); frame = RwFrameGetParent(frame)) RwV3dTransformPoints(¢er, ¢er, 1, RwFrameGetMatrix(frame)); float size = RwV3dLength(¢er) + radius; if (size > *(float *)data) *(float *)data = size; return atomic; } void CCutsceneMgr::Initialise(void) { ms_numCutsceneObjs = 0; ms_loaded = false; ms_wasCutsceneSkipped = false; ms_running = false; ms_useLodMultiplier = false; ms_animLoaded = false; ms_cutsceneProcessing = false; ms_pCutsceneDir = new CDirectory(CUTSCENEDIRSIZE); ms_pCutsceneDir->ReadDirFile("ANIM\\CUTS.DIR"); numUncompressedAnims = 0; uncompressedAnims[0][0] = '\0'; } void CCutsceneMgr::Shutdown(void) { delete ms_pCutsceneDir; } void CCutsceneMgr::LoadCutsceneData(const char *szCutsceneName) { int file; uint32 size; uint32 offset; CPlayerPed *pPlayerPed; ms_cutsceneProcessing = true; ms_wasCutsceneSkipped = false; CTimer::Suspend(); if (!bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver) CStreaming::RemoveCurrentZonesModels(); ms_pCutsceneDir->numEntries = 0; ms_pCutsceneDir->ReadDirFile("ANIM\\CUTS.DIR"); CStreaming::RemoveUnusedModelsInLoadedList(); CGame::DrasticTidyUpMemory(true); strcpy(ms_cutsceneName, szCutsceneName); RwStream *stream; stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, "ANIM\\CUTS.IMG"); assert(stream); // Load animations sprintf(gString, "%s.IFP", szCutsceneName); if (ms_pCutsceneDir->FindItem(gString, offset, size)) { CStreaming::MakeSpaceFor(size << 11); CStreaming::ImGonnaUseStreamingMemory(); RwStreamSkip(stream, offset << 11); CAnimManager::LoadAnimFile(stream, true, uncompressedAnims); ms_cutsceneAssociations.CreateAssociations(szCutsceneName); CStreaming::IHaveUsedStreamingMemory(); ms_animLoaded = true; } else { ms_animLoaded = false; } RwStreamClose(stream, nil); // Load camera data file = CFileMgr::OpenFile("ANIM\\CUTS.IMG", "rb"); sprintf(gString, "%s.DAT", szCutsceneName); if (ms_pCutsceneDir->FindItem(gString, offset, size)) { CStreaming::ImGonnaUseStreamingMemory(); CFileMgr::Seek(file, offset << 11, SEEK_SET); TheCamera.LoadPathSplines(file); CStreaming::IHaveUsedStreamingMemory(); bCamLoaded = true; } else { bCamLoaded = false; } CFileMgr::CloseFile(file); if (CGeneral::faststricmp(ms_cutsceneName, "finale")) { DMAudio.ChangeMusicMode(MUSICMODE_CUTSCENE); int trackId = FindCutsceneAudioTrackId(szCutsceneName); if (trackId != -1) { printf("Start preload audio %s\n", szCutsceneName); DMAudio.PreloadCutSceneMusic(trackId); printf("End preload audio %s\n", szCutsceneName); } } ms_cutsceneTimer = 0.0f; ms_loaded = true; ms_cutsceneOffset = CVector(0.0f, 0.0f, 0.0f); pPlayerPed = FindPlayerPed(); pPlayerPed->m_pWanted->ClearQdCrimes(); pPlayerPed->bIsVisible = false; pPlayerPed->m_fCurrentStamina = pPlayerPed->m_fMaxStamina; CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_CUTSCENE); CWorld::Players[CWorld::PlayerInFocus].MakePlayerSafe(true); CTimer::Resume(); } void CCutsceneMgr::FinishCutscene() { ms_wasCutsceneSkipped = true; if (bCamLoaded) { CCutsceneMgr::ms_cutsceneTimer = TheCamera.GetCutSceneFinishTime() * 0.001f; TheCamera.FinishCutscene(); } FindPlayerPed()->bIsVisible = true; CWorld::Players[CWorld::PlayerInFocus].MakePlayerSafe(false); } void CCutsceneMgr::SetupCutsceneToStart(void) { if (bCamLoaded) { TheCamera.SetCamCutSceneOffSet(ms_cutsceneOffset); TheCamera.TakeControlWithSpline(JUMP_CUT); TheCamera.SetWideScreenOn(); } ms_cutsceneOffset.z++; for (int i = ms_numCutsceneObjs - 1; i >= 0; i--) { assert(RwObjectGetType(ms_pCutsceneObjects[i]->m_rwObject) == rpCLUMP); if (CAnimBlendAssociation *pAnimBlendAssoc = RpAnimBlendClumpGetFirstAssociation((RpClump*)ms_pCutsceneObjects[i]->m_rwObject)) { assert(pAnimBlendAssoc->hierarchy->sequences[0].HasTranslation()); if (ms_pCutsceneObjects[i]->m_pAttachTo != nil) { pAnimBlendAssoc->flags &= (~ASSOC_HAS_TRANSLATION); } else { if (pAnimBlendAssoc->hierarchy->IsCompressed()){ KeyFrameTransCompressed *keyFrames = ((KeyFrameTransCompressed*)pAnimBlendAssoc->hierarchy->sequences[0].GetKeyFrameCompressed(0)); CVector trans; keyFrames->GetTranslation(&trans); ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset + trans); }else{ KeyFrameTrans *keyFrames = ((KeyFrameTrans*)pAnimBlendAssoc->hierarchy->sequences[0].GetKeyFrame(0)); ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset + keyFrames->translation); } } pAnimBlendAssoc->SetRun(); } else { ms_pCutsceneObjects[i]->SetPosition(ms_cutsceneOffset); } CWorld::Add(ms_pCutsceneObjects[i]); if (RwObjectGetType(ms_pCutsceneObjects[i]->m_rwObject) == rpCLUMP) { ms_pCutsceneObjects[i]->UpdateRpHAnim(); } } CTimer::Update(); CTimer::Update(); ms_running = true; ms_cutsceneTimer = 0.0f; } void CCutsceneMgr::SetCutsceneAnim(const char *animName, CObject *pObject) { CAnimBlendAssociation *pNewAnim; CAnimBlendClumpData *pAnimBlendClumpData; assert(RwObjectGetType(pObject->m_rwObject) == rpCLUMP); debug("Give cutscene anim %s\n", animName); RpAnimBlendClumpRemoveAllAssociations((RpClump*)pObject->m_rwObject); pNewAnim = ms_cutsceneAssociations.GetAnimation(animName); if (!pNewAnim) { debug("\n\nHaven't I told you I can't find the fucking animation %s\n\n\n", animName); return; } if (pNewAnim->hierarchy->IsCompressed()) pNewAnim->hierarchy->keepCompressed = true; CStreaming::ImGonnaUseStreamingMemory(); pNewAnim = ms_cutsceneAssociations.CopyAnimation(animName); CStreaming::IHaveUsedStreamingMemory(); pNewAnim->SetCurrentTime(0.0f); pNewAnim->flags |= ASSOC_HAS_TRANSLATION; pNewAnim->flags &= ~ASSOC_RUNNING; pAnimBlendClumpData = *RPANIMBLENDCLUMPDATA(pObject->m_rwObject); pAnimBlendClumpData->link.Prepend(&pNewAnim->link); if (pNewAnim->hierarchy->keepCompressed) pAnimBlendClumpData->frames->flag |= AnimBlendFrameData::COMPRESSED; } void CCutsceneMgr::SetCutsceneAnimToLoop(const char* animName) { ms_cutsceneAssociations.GetAnimation(animName)->flags |= ASSOC_REPEAT; } CCutsceneHead * CCutsceneMgr::AddCutsceneHead(CObject *pObject, int modelId) { return nil; } void UpdateCutsceneObjectBoundingBox(RpClump* clump, int modelId) { if (modelId >= MI_CUTOBJ01 && modelId <= MI_CUTOBJ05) { CColModel* pColModel = &CTempColModels::ms_colModelCutObj[modelId - MI_CUTOBJ01]; float radius = 0.0f; RpClumpForAllAtomics(clump, CalculateBoundingSphereRadiusCB, &radius); pColModel->boundingSphere.radius = radius; pColModel->boundingBox.min = CVector(-radius, -radius, -radius); pColModel->boundingBox.max = CVector(radius, radius, radius); } } CCutsceneObject * CCutsceneMgr::CreateCutsceneObject(int modelId) { CBaseModelInfo *pModelInfo; CColModel *pColModel; CCutsceneObject *pCutsceneObject; CStreaming::ImGonnaUseStreamingMemory(); debug("Created cutscene object %s\n", CModelInfo::GetModelInfo(modelId)->GetModelName()); if (modelId >= MI_CUTOBJ01 && modelId <= MI_CUTOBJ05) { pModelInfo = CModelInfo::GetModelInfo(modelId); pColModel = &CTempColModels::ms_colModelCutObj[modelId - MI_CUTOBJ01]; pModelInfo->SetColModel(pColModel); UpdateCutsceneObjectBoundingBox((RpClump*)pModelInfo->GetRwObject(), modelId); } else if (modelId >= MI_SPECIAL01 && modelId <= MI_SPECIAL21) { pModelInfo = CModelInfo::GetModelInfo(modelId); if (pModelInfo->GetColModel() == &CTempColModels::ms_colModelPed1) { CColModel *colModel = new CColModel(); colModel->boundingSphere.radius = 2.0f; colModel->boundingSphere.center = CVector(0.0f, 0.0f, 0.0f); pModelInfo->SetColModel(colModel, true); } pColModel = pModelInfo->GetColModel(); float radius = 2.0f; pColModel->boundingSphere.radius = radius; pColModel->boundingBox.min = CVector(-radius, -radius, -radius); pColModel->boundingBox.max = CVector(radius, radius, radius); } pCutsceneObject = new CCutsceneObject(); pCutsceneObject->SetModelIndex(modelId); if (ms_useCutsceneShadows) pCutsceneObject->CreateShadow(); ms_pCutsceneObjects[ms_numCutsceneObjs++] = pCutsceneObject; CStreaming::IHaveUsedStreamingMemory(); return pCutsceneObject; } void CCutsceneMgr::DeleteCutsceneData(void) { if (!ms_loaded) return; CTimer::Suspend(); ms_cutsceneProcessing = false; ms_useLodMultiplier = false; ms_useCutsceneShadows = true; for (--ms_numCutsceneObjs; ms_numCutsceneObjs >= 0; ms_numCutsceneObjs--) { CWorld::Remove(ms_pCutsceneObjects[ms_numCutsceneObjs]); ms_pCutsceneObjects[ms_numCutsceneObjs]->DeleteRwObject(); delete ms_pCutsceneObjects[ms_numCutsceneObjs]; ms_pCutsceneObjects[ms_numCutsceneObjs] = nil; } ms_numCutsceneObjs = 0; for (int i = MI_SPECIAL01; i < MI_SPECIAL21; i++) { CBaseModelInfo *minfo = CModelInfo::GetModelInfo(i); CColModel *colModel = minfo->GetColModel(); if (colModel != &CTempColModels::ms_colModelPed1) { delete colModel; minfo->SetColModel(&CTempColModels::ms_colModelPed1); } } if (ms_animLoaded) CAnimManager::RemoveLastAnimFile(); ms_animLoaded = false; numUncompressedAnims = 0; uncompressedAnims[0][0] = '\0'; if (bCamLoaded) { TheCamera.RestoreWithJumpCut(); TheCamera.SetWideScreenOff(); TheCamera.DeleteCutSceneCamDataMemory(); } ms_running = false; ms_loaded = false; FindPlayerPed()->bIsVisible = true; CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_CUTSCENE); CWorld::Players[CWorld::PlayerInFocus].MakePlayerSafe(false); if (CGeneral::faststricmp(ms_cutsceneName, "finale")) { DMAudio.StopCutSceneMusic(); DMAudio.ChangeMusicMode(MUSICMODE_GAME); } CStreaming::ms_disableStreaming = false; CWorld::bProcessCutsceneOnly = false; if(bCamLoaded) CGame::DrasticTidyUpMemory(TheCamera.GetScreenFadeStatus() == FADE_2); CPad::GetPad(0)->Clear(false); if (bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver) { CStreaming::LoadInitialPeds(); CStreaming::LoadInitialWeapons(); CStreaming::LoadInitialVehicles(); bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver = false; CPlayerPed *pPlayerPed = FindPlayerPed(); for (int i = 0; i < NumberOfSavedWeapons; i++) { int32 weaponModelId = CWeaponInfo::GetWeaponInfo(SavedWeaponIDs[i])->m_nModelId; uint8 flags = CStreaming::ms_aInfoForModel[weaponModelId].m_flags; CStreaming::RequestModel(weaponModelId, STREAMFLAGS_DONT_REMOVE); CStreaming::LoadAllRequestedModels(false); if (CWeaponInfo::GetWeaponInfo(SavedWeaponIDs[i])->m_nModel2Id != -1) { CStreaming::RequestModel(CWeaponInfo::GetWeaponInfo(SavedWeaponIDs[i])->m_nModel2Id, 0); CStreaming::LoadAllRequestedModels(false); } if (!(flags & STREAMFLAGS_DONT_REMOVE)) CStreaming::SetModelIsDeletable(weaponModelId); pPlayerPed->GiveWeapon(SavedWeaponIDs[i], SavedWeaponAmmo[i], true); } NumberOfSavedWeapons = 0; } CTimer::Resume(); } void CCutsceneMgr::Update(void) { enum { CUTSCENE_LOADING_0 = 0, CUTSCENE_LOADING_AUDIO, CUTSCENE_LOADING_2, CUTSCENE_LOADING_3, CUTSCENE_LOADING_4 }; switch (ms_cutsceneLoadStatus) { case CUTSCENE_LOADING_AUDIO: SetupCutsceneToStart(); if (CGeneral::faststricmp(ms_cutsceneName, "finale")) DMAudio.PlayPreloadedCutSceneMusic(); ms_cutsceneLoadStatus++; break; case CUTSCENE_LOADING_2: case CUTSCENE_LOADING_3: ms_cutsceneLoadStatus++; break; case CUTSCENE_LOADING_4: ms_cutsceneLoadStatus = CUTSCENE_LOADING_0; break; default: break; } if (!ms_running) return; ms_cutsceneTimer += CTimer::GetTimeStepNonClippedInSeconds(); for (int i = 0; i < ms_numCutsceneObjs; i++) { int modelId = ms_pCutsceneObjects[i]->GetModelIndex(); if (modelId >= MI_CUTOBJ01 && modelId <= MI_CUTOBJ05) UpdateCutsceneObjectBoundingBox(ms_pCutsceneObjects[i]->GetClump(), modelId); if (ms_pCutsceneObjects[i]->m_pAttachTo != nil && modelId >= MI_SPECIAL01 && modelId <= MI_SPECIAL21) UpdateCutsceneObjectBoundingBox(ms_pCutsceneObjects[i]->GetClump(), modelId); } if (bCamLoaded) if (CGeneral::faststricmp(ms_cutsceneName, "finale") && TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FLYBY && ms_cutsceneLoadStatus == CUTSCENE_LOADING_0) { if (CPad::GetPad(0)->GetCrossJustDown() || (CGame::playingIntro && CPad::GetPad(0)->GetStartJustDown()) || CPad::GetPad(0)->GetLeftMouseJustDown() || CPad::GetPad(0)->GetEnterJustDown() || CPad::GetPad(0)->GetCharJustDown(' ')) FinishCutscene(); } } bool CCutsceneMgr::HasCutsceneFinished(void) { return !bCamLoaded || TheCamera.GetPositionAlongSpline() == 1.0f; } void CCutsceneMgr::LoadAnimationUncompressed(char const* name) { strcpy(uncompressedAnims[numUncompressedAnims], name); // Because that's how CAnimManager knows the end of array ++numUncompressedAnims; assert(numUncompressedAnims < ARRAY_SIZE(uncompressedAnims)); uncompressedAnims[numUncompressedAnims][0] = '\0'; } void CCutsceneMgr::AttachObjectToParent(CObject *pObject, CEntity *pAttachTo) { ((CCutsceneObject*)pObject)->m_pAttachmentObject = nil; ((CCutsceneObject*)pObject)->m_pAttachTo = RpClumpGetFrame(pAttachTo->GetClump()); debug("Attach %s to %s\n", CModelInfo::GetModelInfo(pObject->GetModelIndex())->GetModelName(), CModelInfo::GetModelInfo(pAttachTo->GetModelIndex())->GetModelName()); } void CCutsceneMgr::AttachObjectToFrame(CObject *pObject, CEntity *pAttachTo, const char *frame) { ((CCutsceneObject*)pObject)->m_pAttachmentObject = nil; ((CCutsceneObject*)pObject)->m_pAttachTo = RpAnimBlendClumpFindFrame(pAttachTo->GetClump(), frame)->frame; debug("Attach %s to component %s of %s\n", CModelInfo::GetModelInfo(pObject->GetModelIndex())->GetModelName(), frame, CModelInfo::GetModelInfo(pAttachTo->GetModelIndex())->GetModelName()); if (RwObjectGetType(pObject->m_rwObject) == rpCLUMP) { RpClump *clump = (RpClump*)pObject->m_rwObject; if (IsClumpSkinned(clump)) RpAtomicGetBoundingSphere(GetFirstAtomic(clump))->radius *= 1.1f; } } void CCutsceneMgr::AttachObjectToBone(CObject *pObject, CObject *pAttachTo, int bone) { RpHAnimHierarchy *hanim = GetAnimHierarchyFromSkinClump(pAttachTo->GetClump()); RwInt32 id = RpHAnimIDGetIndex(hanim, bone); RwMatrix *matrixArray = RpHAnimHierarchyGetMatrixArray(hanim); ((CCutsceneObject*)pObject)->m_pAttachmentObject = pAttachTo; ((CCutsceneObject*)pObject)->m_pAttachTo = &matrixArray[id]; debug("Attach %s to %s\n", CModelInfo::GetModelInfo(pObject->GetModelIndex())->GetModelName(), CModelInfo::GetModelInfo(pAttachTo->GetModelIndex())->GetModelName()); } void CCutsceneMgr::RemoveEverythingFromTheWorldForTheBiggestFuckoffCutsceneEver() { CStreaming::ms_disableStreaming = true; CColStore::RemoveAllCollision(); CWorld::bProcessCutsceneOnly = true; ms_cutsceneProcessing = true; for (int i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--) { CPed *pPed = CPools::GetPedPool()->GetSlot(i); if (pPed) { if (!pPed->IsPlayer() && pPed->CanBeDeleted()) { CWorld::Remove(pPed); delete pPed; } } } for (int i = CPools::GetVehiclePool()->GetSize() - 1; i >= 0; i--) { CVehicle *pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (pVehicle) { if (pVehicle->CanBeDeleted()) { CWorld::Remove(pVehicle); delete pVehicle; } } } bIsEverythingRemovedFromTheWorldForTheBiggestFuckoffCutsceneEver = true; CStreaming::RemoveCurrentZonesModels(); CStreaming::SetModelIsDeletable(MI_MALE01); CStreaming::SetModelTxdIsDeletable(MI_MALE01); CStreaming::SetModelIsDeletable(MI_TAXI_D); CStreaming::SetModelTxdIsDeletable(MI_TAXI_D); CStreaming::SetModelIsDeletable(MI_NIGHTSTICK); CStreaming::SetModelTxdIsDeletable(MI_NIGHTSTICK); CStreaming::SetModelIsDeletable(MI_MISSILE); CStreaming::SetModelTxdIsDeletable(MI_MISSILE); CStreaming::SetModelIsDeletable(MI_POLICE); CStreaming::SetModelTxdIsDeletable(MI_POLICE); while (CStreaming::RemoveLoadedVehicle()) ; CRadar::RemoveRadarSections(); for (int i = CPools::GetDummyPool()->GetSize() - 1; i >= 0; i--) { CDummy* pDummy = CPools::GetDummyPool()->GetSlot(i); if (pDummy) pDummy->DeleteRwObject(); } for (int i = CPools::GetObjectPool()->GetSize() - 1; i >= 0; i--) { CObject* pObject = CPools::GetObjectPool()->GetSlot(i); if (pObject) pObject->DeleteRwObject(); } for (int i = CPools::GetBuildingPool()->GetSize() - 1; i >= 0; i--) { CBuilding* pBuilding = CPools::GetBuildingPool()->GetSlot(i); if (pBuilding && pBuilding->m_rwObject != nil && pBuilding->bIsBIGBuilding && pBuilding->bStreamBIGBuilding) { if (pBuilding->bIsBIGBuilding) CStreaming::RequestModel(pBuilding->GetModelIndex(), 0); if (!pBuilding->bImBeingRendered) pBuilding->DeleteRwObject(); } } CPlayerPed *pPlayerPed = FindPlayerPed(); pPlayerPed->RemoveWeaponAnims(0, -1000.0f); NumberOfSavedWeapons = 0; for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { if (pPlayerPed->m_weapons[i].m_eWeaponType != WEAPONTYPE_UNARMED) { SavedWeaponIDs[NumberOfSavedWeapons] = pPlayerPed->m_weapons[i].m_eWeaponType; SavedWeaponAmmo[NumberOfSavedWeapons] = pPlayerPed->m_weapons[i].m_nAmmoTotal; NumberOfSavedWeapons++; } } pPlayerPed->ClearWeapons(); CGame::DrasticTidyUpMemory(true); } ================================================ FILE: src/animation/CutsceneMgr.h ================================================ #pragma once #include "CutsceneObject.h" #define CUTSCENENAMESIZE 8 class CDirectory; class CAnimBlendAssocGroup; class CCutsceneHead; class CCutsceneMgr { static bool ms_running; static CCutsceneObject *ms_pCutsceneObjects[NUMCUTSCENEOBJECTS]; static int32 ms_numCutsceneObjs; static bool ms_loaded; static bool ms_animLoaded; static bool ms_useLodMultiplier; static char ms_cutsceneName[CUTSCENENAMESIZE]; static CAnimBlendAssocGroup ms_cutsceneAssociations; static CVector ms_cutsceneOffset; static float ms_cutsceneTimer; static bool ms_wasCutsceneSkipped; static bool ms_cutsceneProcessing; static bool ms_useCutsceneShadows; public: static CDirectory *ms_pCutsceneDir; static uint32 ms_cutsceneLoadStatus; static void StartCutsceneProcessing() { ms_cutsceneProcessing = true; } static bool IsRunning(void) { return ms_running; } static bool HasLoaded(void) { return ms_loaded; } static bool IsCutsceneProcessing(void) { return ms_cutsceneProcessing; } static bool WasCutsceneSkipped(void) { return ms_wasCutsceneSkipped; } static bool UseLodMultiplier(void) { return ms_useLodMultiplier; } static CCutsceneObject* GetCutsceneObject(int id) { return ms_pCutsceneObjects[id]; } static int GetCutsceneTimeInMilleseconds(void) { return 1000.0f * ms_cutsceneTimer; } static char *GetCutsceneName(void) { return ms_cutsceneName; } static void SetCutsceneOffset(const CVector& vec) { ms_cutsceneOffset = vec; } static bool HasCutsceneFinished(void); static void Initialise(void); static void Shutdown(void); static void LoadCutsceneData(const char *szCutsceneName); static void FinishCutscene(void); static void SetupCutsceneToStart(void); static void SetCutsceneAnim(const char *animName, CObject *pObject); static void SetCutsceneAnimToLoop(const char *animName); static CCutsceneHead *AddCutsceneHead(CObject *pObject, int modelId); static CCutsceneObject *CreateCutsceneObject(int modelId); static void DeleteCutsceneData(void); static void LoadAnimationUncompressed(char const*); static void Update(void); static void AttachObjectToParent(CObject *pObject, CEntity *pAttachTo); static void AttachObjectToFrame(CObject *pObject, CEntity *pAttachTo, const char *frame); static void AttachObjectToBone(CObject *pObject, CObject *pAttachTo, int frame); static void RemoveEverythingFromTheWorldForTheBiggestFuckoffCutsceneEver(); static void DisableCutsceneShadows() { ms_useCutsceneShadows = false; } }; ================================================ FILE: src/animation/FrameUpdate.cpp ================================================ #include "common.h" #include "NodeName.h" #include "VisibilityPlugins.h" #include "AnimBlendClumpData.h" #include "AnimBlendAssociation.h" #include "RpAnimBlend.h" CAnimBlendClumpData *gpAnimBlendClump; // PS2 names without "NonSkinned" void FrameUpdateCallBackNonSkinned(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackWithVelocityExtractionNonSkinned(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackWith3dVelocityExtractionNonSkinned(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackSkinned(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackWithVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackWith3dVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackNonSkinnedCompressed(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackSkinnedCompressed(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackNonSkinned(AnimBlendFrameData *frame, void *arg) { CVector vec, pos(0.0f, 0.0f, 0.0f); CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); float totalBlendAmount = 0.0f; RwMatrix *mat = RwFrameGetMatrix(frame->frame); CAnimBlendNode **node; AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION && gpAnimBlendClump->velocity2d){ if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION_3D) FrameUpdateCallBackWith3dVelocityExtractionNonSkinned(frame, arg); else FrameUpdateCallBackWithVelocityExtractionNonSkinned(frame, arg); return; } if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ (*node)->Update(vec, q, 1.0f-totalBlendAmount); if((*node)->sequence->HasTranslation()) pos += vec; #ifdef FIX_BUGS if(DotProduct(rot, q) < 0.0f) rot -= q; else #endif rot += q; } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ RwMatrixSetIdentity(mat); rot.Normalise(); rot.Get(mat); } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ mat->pos.x = pos.x; mat->pos.y = pos.y; mat->pos.z = pos.z; mat->pos.x += frame->resetPos.x; mat->pos.y += frame->resetPos.y; mat->pos.z += frame->resetPos.z; } RwMatrixUpdate(mat); } void FrameUpdateCallBackWithVelocityExtractionNonSkinned(AnimBlendFrameData *frame, void *arg) { CVector vec, pos(0.0f, 0.0f, 0.0f); CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); float totalBlendAmount = 0.0f; float transx = 0.0f, transy = 0.0f; float curx = 0.0f, cury = 0.0f; float endx = 0.0f, endy = 0.0f; bool looped = false; RwMatrix *mat = RwFrameGetMatrix(frame->frame); CAnimBlendNode **node; AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->sequence->HasTranslation()){ if((*node)->association->HasTranslation()){ (*node)->GetCurrentTranslation(vec, 1.0f-totalBlendAmount); cury += vec.y; if((*node)->association->HasXTranslation()) curx += vec.x; } } for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ bool nodelooped = (*node)->Update(vec, q, 1.0f-totalBlendAmount); #ifdef FIX_BUGS if(DotProduct(rot, q) < 0.0f) rot -= q; else #endif rot += q; if((*node)->sequence->HasTranslation()){ pos += vec; if((*node)->association->HasTranslation()){ transy += vec.y; if((*node)->association->HasXTranslation()) transx += vec.x; looped |= nodelooped; if(nodelooped){ (*node)->GetEndTranslation(vec, 1.0f-totalBlendAmount); endy += vec.y; if((*node)->association->HasXTranslation()) endx += vec.x; } } } } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ RwMatrixSetIdentity(mat); rot.Normalise(); rot.Get(mat); } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ gpAnimBlendClump->velocity2d->x = transx - curx; gpAnimBlendClump->velocity2d->y = transy - cury; if(looped){ gpAnimBlendClump->velocity2d->x += endx; gpAnimBlendClump->velocity2d->y += endy; } mat->pos.x = pos.x - transx; mat->pos.y = pos.y - transy; mat->pos.z = pos.z; if(mat->pos.z >= -0.8f) { if(mat->pos.z < -0.4f) mat->pos.z += (2.5f * mat->pos.z + 2.0f) * frame->resetPos.z; else mat->pos.z += frame->resetPos.z; } mat->pos.x += frame->resetPos.x; mat->pos.y += frame->resetPos.y; } RwMatrixUpdate(mat); } // original code uses do loops? void FrameUpdateCallBackWith3dVelocityExtractionNonSkinned(AnimBlendFrameData *frame, void *arg) { CVector vec, pos(0.0f, 0.0f, 0.0f); CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); float totalBlendAmount = 0.0f; CVector trans(0.0f, 0.0f, 0.0f); CVector cur(0.0f, 0.0f, 0.0f); CVector end(0.0f, 0.0f, 0.0f); bool looped = false; RwMatrix *mat = RwFrameGetMatrix(frame->frame); CAnimBlendNode **node; AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->sequence->HasTranslation()){ if((*node)->association->HasTranslation()){ (*node)->GetCurrentTranslation(vec, 1.0f-totalBlendAmount); cur += vec; } } for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ bool nodelooped = (*node)->Update(vec, q, 1.0f-totalBlendAmount); #ifdef FIX_BUGS if(DotProduct(rot, q) < 0.0f) rot -= q; else #endif rot += q; if((*node)->sequence->HasTranslation()){ pos += vec; if((*node)->association->HasTranslation()){ trans += vec; looped |= nodelooped; if(nodelooped){ (*node)->GetEndTranslation(vec, 1.0f-totalBlendAmount); end += vec; } } } } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ RwMatrixSetIdentity(mat); rot.Normalise(); rot.Get(mat); } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ *gpAnimBlendClump->velocity3d = trans - cur; if(looped) *gpAnimBlendClump->velocity3d += end; mat->pos.x = (pos - trans).x + frame->resetPos.x; mat->pos.y = (pos - trans).y + frame->resetPos.y; mat->pos.z = (pos - trans).z + frame->resetPos.z; } RwMatrixUpdate(mat); } void FrameUpdateCallBackSkinned(AnimBlendFrameData *frame, void *arg) { CVector vec, pos(0.0f, 0.0f, 0.0f); float transBlendAmount = 0.0f; CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); float totalBlendAmount = 0.0f; RpHAnimStdInterpFrame *xform = frame->hanimFrame; CAnimBlendNode **node; AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION && gpAnimBlendClump->velocity2d){ if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION_3D) FrameUpdateCallBackWith3dVelocityExtractionSkinned(frame, arg); else FrameUpdateCallBackWithVelocityExtractionSkinned(frame, arg); return; } if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ (*node)->Update(vec, q, 1.0f-totalBlendAmount); if((*node)->sequence->HasTranslation()){ pos += vec; transBlendAmount += (*node)->association->blendAmount; } if(DotProduct(rot, q) < 0.0f) rot -= q; else rot += q; } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ rot.Normalise(); xform->q.imag.x = rot.x; xform->q.imag.y = rot.y; xform->q.imag.z = rot.z; xform->q.real = rot.w; } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ xform->t.x = transBlendAmount*pos.x; xform->t.y = transBlendAmount*pos.y; xform->t.z = transBlendAmount*pos.z; xform->t.x += (1.0f-transBlendAmount)*frame->resetPos.x; xform->t.y += (1.0f-transBlendAmount)*frame->resetPos.y; xform->t.z += (1.0f-transBlendAmount)*frame->resetPos.z; } } void FrameUpdateCallBackWithVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg) { CVector vec, pos(0.0f, 0.0f, 0.0f); CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); float totalBlendAmount = 0.0f; float transx = 0.0f, transy = 0.0f; float curx = 0.0f, cury = 0.0f; float endx = 0.0f, endy = 0.0f; bool looped = false; RpHAnimStdInterpFrame *xform = frame->hanimFrame; CAnimBlendNode **node; AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->sequence->HasTranslation()){ if((*node)->association->HasTranslation()){ (*node)->GetCurrentTranslation(vec, 1.0f-totalBlendAmount); cury += vec.y; if((*node)->association->HasXTranslation()) curx += vec.x; } } for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ bool nodelooped = (*node)->Update(vec, q, 1.0f-totalBlendAmount); if(DotProduct(rot, q) < 0.0f) rot -= q; else rot += q; if((*node)->sequence->HasTranslation()){ pos += vec; if((*node)->association->HasTranslation()){ transy += vec.y; if((*node)->association->HasXTranslation()) transx += vec.x; looped |= nodelooped; if(nodelooped){ (*node)->GetEndTranslation(vec, 1.0f-totalBlendAmount); endy += vec.y; if((*node)->association->HasXTranslation()) endx += vec.x; } } } } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ rot.Normalise(); xform->q.imag.x = rot.x; xform->q.imag.y = rot.y; xform->q.imag.z = rot.z; xform->q.real = rot.w; } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ gpAnimBlendClump->velocity2d->x = transx - curx; gpAnimBlendClump->velocity2d->y = transy - cury; if(looped){ gpAnimBlendClump->velocity2d->x += endx; gpAnimBlendClump->velocity2d->y += endy; } xform->t.x = pos.x - transx; xform->t.y = pos.y - transy; xform->t.z = pos.z; if(xform->t.z >= -0.8f) { if(xform->t.z < -0.4f) xform->t.z += (2.5f * xform->t.z + 2.0f) * frame->resetPos.z; else xform->t.z += frame->resetPos.z; } xform->t.x += frame->resetPos.x; xform->t.y += frame->resetPos.y; } } void FrameUpdateCallBackWith3dVelocityExtractionSkinned(AnimBlendFrameData *frame, void *arg) { CVector vec, pos(0.0f, 0.0f, 0.0f); CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); float totalBlendAmount = 0.0f; CVector trans(0.0f, 0.0f, 0.0f); CVector cur(0.0f, 0.0f, 0.0f); CVector end(0.0f, 0.0f, 0.0f); bool looped = false; RpHAnimStdInterpFrame *xform = frame->hanimFrame; CAnimBlendNode **node; AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->sequence->HasTranslation()){ if((*node)->association->HasTranslation()){ (*node)->GetCurrentTranslation(vec, 1.0f-totalBlendAmount); cur += vec; } } for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ bool nodelooped = (*node)->Update(vec, q, 1.0f-totalBlendAmount); #ifdef FIX_BUGS if(DotProduct(rot, q) < 0.0f) rot -= q; else #endif rot += q; if((*node)->sequence->HasTranslation()){ pos += vec; if((*node)->association->HasTranslation()){ trans += vec; looped |= nodelooped; if(nodelooped){ (*node)->GetEndTranslation(vec, 1.0f-totalBlendAmount); end += vec; } } } } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ rot.Normalise(); xform->q.imag.x = rot.x; xform->q.imag.y = rot.y; xform->q.imag.z = rot.z; xform->q.real = rot.w; } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ *gpAnimBlendClump->velocity3d = trans - cur; if(looped) *gpAnimBlendClump->velocity3d += end; xform->t.x = (pos - trans).x + frame->resetPos.x; xform->t.y = (pos - trans).y + frame->resetPos.y; xform->t.z = (pos - trans).z + frame->resetPos.z; } } void FrameUpdateCallBackOffscreen(AnimBlendFrameData *frame, void *arg) { if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION && gpAnimBlendClump->velocity2d) FrameUpdateCallBackWithVelocityExtractionSkinned(frame, arg); } void FrameUpdateCallBackNonSkinnedCompressed(AnimBlendFrameData *frame, void *arg) { CVector vec, pos(0.0f, 0.0f, 0.0f); CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); float totalBlendAmount = 0.0f; CVector trans(0.0f, 0.0f, 0.0f); CVector cur(0.0f, 0.0f, 0.0f); CVector end(0.0f, 0.0f, 0.0f); bool looped = false; RwMatrix *mat = RwFrameGetMatrix(frame->frame); CAnimBlendNode **node; AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION && gpAnimBlendClump->velocity2d){ if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->sequence->HasTranslation()){ if((*node)->association->HasTranslation()){ (*node)->GetCurrentTranslationCompressed(vec, 1.0f-totalBlendAmount); cur += vec; } } for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ bool nodelooped = (*node)->UpdateCompressed(vec, q, 1.0f-totalBlendAmount); #ifdef FIX_BUGS if(DotProduct(rot, q) < 0.0f) rot -= q; else #endif rot += q; if((*node)->sequence->HasTranslation()){ pos += vec; if((*node)->association->HasTranslation()){ trans += vec; looped |= nodelooped; if(nodelooped){ (*node)->GetEndTranslationCompressed(vec, 1.0f-totalBlendAmount); end += vec; } } } } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ RwMatrixSetIdentity(mat); rot.Normalise(); rot.Get(mat); } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ *gpAnimBlendClump->velocity3d = trans - cur; if(looped) *gpAnimBlendClump->velocity3d += end; mat->pos.x = (pos - trans).x + frame->resetPos.x; mat->pos.y = (pos - trans).y + frame->resetPos.y; mat->pos.z = (pos - trans).z + frame->resetPos.z; } RwMatrixUpdate(mat); }else{ if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ (*node)->UpdateCompressed(vec, q, 1.0f-totalBlendAmount); if((*node)->sequence->HasTranslation()) pos += vec; #ifdef FIX_BUGS if(DotProduct(rot, q) < 0.0f) rot -= q; else #endif rot += q; } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ RwMatrixSetIdentity(mat); rot.Normalise(); rot.Get(mat); } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ mat->pos.x = pos.x; mat->pos.y = pos.y; mat->pos.z = pos.z; mat->pos.x += frame->resetPos.x; mat->pos.y += frame->resetPos.y; mat->pos.z += frame->resetPos.z; } RwMatrixUpdate(mat); } } void FrameUpdateCallBackSkinnedCompressed(AnimBlendFrameData *frame, void *arg) { CVector vec, pos(0.0f, 0.0f, 0.0f); CQuaternion q, rot(0.0f, 0.0f, 0.0f, 0.0f); float totalBlendAmount = 0.0f; CVector trans(0.0f, 0.0f, 0.0f); CVector cur(0.0f, 0.0f, 0.0f); CVector end(0.0f, 0.0f, 0.0f); bool looped = false; RpHAnimStdInterpFrame *xform = frame->hanimFrame; CAnimBlendNode **node; AnimBlendFrameUpdateData *updateData = (AnimBlendFrameUpdateData*)arg; if(frame->flag & AnimBlendFrameData::VELOCITY_EXTRACTION && gpAnimBlendClump->velocity2d){ if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->sequence->HasTranslation()){ if((*node)->association->HasTranslation()){ (*node)->GetCurrentTranslationCompressed(vec, 1.0f-totalBlendAmount); cur += vec; } } for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ bool nodelooped = (*node)->UpdateCompressed(vec, q, 1.0f-totalBlendAmount); #ifdef FIX_BUGS if(DotProduct(rot, q) < 0.0f) rot -= q; else #endif rot += q; if((*node)->sequence->HasTranslation()){ pos += vec; if((*node)->association->HasTranslation()){ trans += vec; looped |= nodelooped; if(nodelooped){ (*node)->GetEndTranslationCompressed(vec, 1.0f-totalBlendAmount); end += vec; } } } } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ rot.Normalise(); xform->q.imag.x = rot.x; xform->q.imag.y = rot.y; xform->q.imag.z = rot.z; xform->q.real = rot.w; } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ *gpAnimBlendClump->velocity3d = trans - cur; if(looped) *gpAnimBlendClump->velocity3d += end; xform->t.x = (pos - trans).x + frame->resetPos.x; xform->t.y = (pos - trans).y + frame->resetPos.y; xform->t.z = (pos - trans).z + frame->resetPos.z; } }else{ float transBlendAmount = 0.0f; if(updateData->foobar) for(node = updateData->nodes; *node; node++) if((*node)->sequence && (*node)->association->IsPartial()) totalBlendAmount += (*node)->association->blendAmount; for(node = updateData->nodes; *node; node++){ if((*node)->sequence){ (*node)->UpdateCompressed(vec, q, 1.0f-totalBlendAmount); if((*node)->sequence->HasTranslation()){ pos += vec; transBlendAmount += (*node)->association->blendAmount; } if(DotProduct(rot, q) < 0.0f) rot -= q; else rot += q; } ++*node; } if((frame->flag & AnimBlendFrameData::IGNORE_ROTATION) == 0){ rot.Normalise(); xform->q.imag.x = rot.x; xform->q.imag.y = rot.y; xform->q.imag.z = rot.z; xform->q.real = rot.w; } if((frame->flag & AnimBlendFrameData::IGNORE_TRANSLATION) == 0){ xform->t.x = transBlendAmount*pos.x; xform->t.y = transBlendAmount*pos.y; xform->t.z = transBlendAmount*pos.z; xform->t.x += (1.0f-transBlendAmount)*frame->resetPos.x; xform->t.y += (1.0f-transBlendAmount)*frame->resetPos.y; xform->t.z += (1.0f-transBlendAmount)*frame->resetPos.z; } } } ================================================ FILE: src/animation/RpAnimBlend.cpp ================================================ #include "common.h" #include "RwHelper.h" #include "General.h" #include "NodeName.h" #include "VisibilityPlugins.h" #include "Bones.h" #include "AnimBlendClumpData.h" #include "AnimBlendHierarchy.h" #include "AnimBlendAssociation.h" #include "AnimManager.h" #include "RpAnimBlend.h" #include "PedModelInfo.h" RwInt32 ClumpOffset; enum { ID_RPANIMBLEND = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0xFD), }; void* AnimBlendClumpCreate(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) { *RWPLUGINOFFSET(CAnimBlendClumpData*, object, offsetInObject) = nil; return object; } void* AnimBlendClumpDestroy(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) { CAnimBlendClumpData *data; data = *RPANIMBLENDCLUMPDATA(object); if(data){ RpAnimBlendClumpRemoveAllAssociations((RpClump*)object); delete data; *RPANIMBLENDCLUMPDATA(object) = nil; } return object; } void *AnimBlendClumpCopy(void *dstObject, const void *srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject) { return nil; } bool RpAnimBlendPluginAttach(void) { ClumpOffset = RpClumpRegisterPlugin(sizeof(CAnimBlendClumpData*), ID_RPANIMBLEND, AnimBlendClumpCreate, AnimBlendClumpDestroy, AnimBlendClumpCopy); return ClumpOffset >= 0; } CAnimBlendAssociation* RpAnimBlendGetNextAssociation(CAnimBlendAssociation *assoc) { if(assoc->link.next) return CAnimBlendAssociation::FromLink(assoc->link.next); return nil; } CAnimBlendAssociation* RpAnimBlendGetNextAssociation(CAnimBlendAssociation *assoc, uint32 mask) { CAnimBlendLink *link; for(link = assoc->link.next; link; link = link->next){ assoc = CAnimBlendAssociation::FromLink(link); if(assoc->flags & mask) return assoc; } return nil; } void RpAnimBlendAllocateData(RpClump *clump) { *RPANIMBLENDCLUMPDATA(clump) = new CAnimBlendClumpData; } void RpAnimBlendClumpSetBlendDeltas(RpClump *clump, uint32 mask, float delta) { CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); if(mask == 0 || (assoc->flags & mask)) assoc->blendDelta = delta; } } void RpAnimBlendClumpRemoveAllAssociations(RpClump *clump) { RpAnimBlendClumpRemoveAssociations(clump, 0); } void RpAnimBlendClumpRemoveAssociations(RpClump *clump, uint32 mask) { CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); CAnimBlendLink *next; for(CAnimBlendLink *link = clumpData->link.next; link; link = next){ next = link->next; CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); if(mask == 0 || (assoc->flags & mask)) if(assoc) delete assoc; } } RwFrame* FrameForAllChildrenCountCallBack(RwFrame *frame, void *data) { int *numFrames = (int*)data; (*numFrames)++; RwFrameForAllChildren(frame, FrameForAllChildrenCountCallBack, data); return frame; } RwFrame* FrameForAllChildrenFillFrameArrayCallBack(RwFrame *frame, void *data) { AnimBlendFrameData **frames = (AnimBlendFrameData**)data; (*frames)->frame = frame; (*frames)++; RwFrameForAllChildren(frame, FrameForAllChildrenFillFrameArrayCallBack, frames); return frame; } // FrameInitCallBack on PS2 void FrameInitCBnonskin(AnimBlendFrameData *frameData, void*) { frameData->flag = 0; frameData->resetPos = *RwMatrixGetPos(RwFrameGetMatrix(frameData->frame)); } void FrameInitCBskin(AnimBlendFrameData *frameData, void*) { frameData->flag = 0; } void RpAnimBlendClumpInitSkinned(RpClump *clump) { int i; RwV3d boneTab[64]; CAnimBlendClumpData *clumpData; RpAtomic *atomic; RpSkin *skin; RpHAnimHierarchy *hier; int numBones; RpAnimBlendAllocateData(clump); clumpData = *RPANIMBLENDCLUMPDATA(clump); atomic = GetFirstAtomic(clump); assert(atomic); skin = RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)); assert(skin); numBones = RpSkinGetNumBones(skin); clumpData->SetNumberOfBones(numBones); hier = GetAnimHierarchyFromSkinClump(clump); assert(hier); memset(boneTab, 0, sizeof(boneTab)); SkinGetBonePositionsToTable(clump, boneTab); AnimBlendFrameData *frames = clumpData->frames; for(i = 0; i < numBones; i++){ frames[i].nodeID = HIERNODEID(hier, i); frames[i].resetPos = boneTab[i]; #ifdef LIBRW frames[i].hanimFrame = (RpHAnimStdInterpFrame*)rpHANIMHIERARCHYGETINTERPFRAME(hier, i); #else frames[i].hanimFrame = (RpHAnimStdInterpFrame*)rtANIMGETINTERPFRAME(hier->currentAnim, i); #endif } clumpData->ForAllFrames(FrameInitCBskin, nil); clumpData->frames[0].flag |= AnimBlendFrameData::VELOCITY_EXTRACTION; } void RpAnimBlendClumpInitNotSkinned(RpClump *clump) { int numFrames = 0; CAnimBlendClumpData *clumpData; RwFrame *root; AnimBlendFrameData *frames; RpAnimBlendAllocateData(clump); clumpData = *RPANIMBLENDCLUMPDATA(clump); root = RpClumpGetFrame(clump); RwFrameForAllChildren(root, FrameForAllChildrenCountCallBack, &numFrames); clumpData->SetNumberOfFrames(numFrames); frames = clumpData->frames; RwFrameForAllChildren(root, FrameForAllChildrenFillFrameArrayCallBack, &frames); clumpData->ForAllFrames(FrameInitCBnonskin, nil); clumpData->frames[0].flag |= AnimBlendFrameData::VELOCITY_EXTRACTION; } void RpAnimBlendClumpInit(RpClump *clump) { if(IsClumpSkinned(clump)) RpAnimBlendClumpInitSkinned(clump); else RpAnimBlendClumpInitNotSkinned(clump); } bool RpAnimBlendClumpIsInitialized(RpClump *clump) { CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); return clumpData && clumpData->numFrames != 0; } CAnimBlendAssociation* RpAnimBlendClumpGetAssociation(RpClump *clump, uint32 id) { CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); if(clumpData == nil) return nil; for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); if(assoc->animId == id) return assoc; } return nil; } CAnimBlendAssociation* RpAnimBlendClumpGetMainAssociation(RpClump *clump, CAnimBlendAssociation **assocRet, float *blendRet) { CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); if(clumpData == nil) return nil; CAnimBlendAssociation *mainAssoc = nil; CAnimBlendAssociation *secondAssoc = nil; float mainBlend = 0.0f; float secondBlend = 0.0f; for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); if(assoc->IsPartial()) continue; if(assoc->blendAmount > mainBlend){ secondBlend = mainBlend; mainBlend = assoc->blendAmount; secondAssoc = mainAssoc; mainAssoc = assoc; }else if(assoc->blendAmount > secondBlend){ secondBlend = assoc->blendAmount; secondAssoc = assoc; } } if(assocRet) *assocRet = secondAssoc; if(blendRet) *blendRet = secondBlend; return mainAssoc; } CAnimBlendAssociation* RpAnimBlendClumpGetMainPartialAssociation(RpClump *clump) { CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); if(clumpData == nil) return nil; CAnimBlendAssociation *mainAssoc = nil; float mainBlend = 0.0f; for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); if(!assoc->IsPartial()) continue; if(assoc->blendAmount > mainBlend){ mainBlend = assoc->blendAmount; mainAssoc = assoc; } } return mainAssoc; } CAnimBlendAssociation* RpAnimBlendClumpGetMainAssociation_N(RpClump *clump, int n) { int i; CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); if(clumpData == nil) return nil; i = 0; for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); if(assoc->IsPartial()) continue; if(i == n) return assoc; i++; } return nil; } CAnimBlendAssociation* RpAnimBlendClumpGetMainPartialAssociation_N(RpClump *clump, int n) { int i; CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); if(clumpData == nil) return nil; i = 0; for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); if(!assoc->IsPartial()) continue; if(i == n) return assoc; i++; } return nil; } CAnimBlendAssociation* RpAnimBlendClumpGetFirstAssociation(RpClump *clump, uint32 mask) { CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); if(clumpData == nil) return nil; for(CAnimBlendLink *link = clumpData->link.next; link; link = link->next){ CAnimBlendAssociation *assoc = CAnimBlendAssociation::FromLink(link); if(assoc->flags & mask) return assoc; } return nil; } CAnimBlendAssociation* RpAnimBlendClumpGetFirstAssociation(RpClump *clump) { CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); if(!RpAnimBlendClumpIsInitialized(clump)) return nil; if(clumpData->link.next) return CAnimBlendAssociation::FromLink(clumpData->link.next); return nil; } // FillFrameArrayCallBack on PS2 void FillFrameArrayCBnonskin(AnimBlendFrameData *frame, void *arg) { AnimBlendFrameData **frames = (AnimBlendFrameData**)arg; frames[CVisibilityPlugins::GetFrameHierarchyId(frame->frame)] = frame; } void RpAnimBlendClumpFillFrameArraySkin(RpClump *clump, AnimBlendFrameData **frames) { int i; CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump); for(i = PED_MID; i < PED_NODE_MAX; i++) frames[i] = &clumpData->frames[RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(i))]; } void RpAnimBlendClumpFillFrameArray(RpClump *clump, AnimBlendFrameData **frames) { if(IsClumpSkinned(clump)) RpAnimBlendClumpFillFrameArraySkin(clump, frames); else (*RPANIMBLENDCLUMPDATA(clump))->ForAllFrames(FillFrameArrayCBnonskin, frames); } AnimBlendFrameData *pFrameDataFound; void FrameFindByNameCBnonskin(AnimBlendFrameData *frame, void *arg) { char *nodename = GetFrameNodeName(frame->frame); if(!CGeneral::faststricmp(nodename, (char*)arg)) pFrameDataFound = frame; } void FrameFindByNameCBskin(AnimBlendFrameData *frame, void *arg) { const char *name = ConvertBoneTag2BoneName(frame->nodeID); if(name && CGeneral::faststricmp(name, (char*)arg) == 0) pFrameDataFound = frame; } void FrameFindByBoneCB(AnimBlendFrameData *frame, void *arg) { if(frame->nodeID == (int32)(uintptr)arg) pFrameDataFound = frame; } AnimBlendFrameData* RpAnimBlendClumpFindFrame(RpClump *clump, const char *name) { pFrameDataFound = nil; if(IsClumpSkinned(clump)) (*RPANIMBLENDCLUMPDATA(clump))->ForAllFrames(FrameFindByNameCBskin, (void*)name); else (*RPANIMBLENDCLUMPDATA(clump))->ForAllFrames(FrameFindByNameCBnonskin, (void*)name); return pFrameDataFound; } AnimBlendFrameData* RpAnimBlendClumpFindBone(RpClump *clump, uint32 boneTag) { pFrameDataFound = nil; (*RPANIMBLENDCLUMPDATA(clump))->ForAllFrames(FrameFindByBoneCB, (void*)boneTag); return pFrameDataFound; } void RpAnimBlendNodeUpdateKeyframes(AnimBlendFrameData *frames, AnimBlendFrameUpdateData *updateData, int32 numNodes) { CAnimBlendNode **node; int i; for(node = updateData->nodes; *node; node++){ CAnimBlendAssociation *a = (*node)->association; for(i = 0; i < numNodes; i++) if((frames[i].flag & AnimBlendFrameData::VELOCITY_EXTRACTION) == 0 || gpAnimBlendClump->velocity2d == nil){ if((*node)[i].sequence) (*node)[i].FindKeyFrame(a->currentTime - a->timeStep); } } } // TODO: // CAnimBlendClumpData::LoadFramesIntoSPR // CAnimBlendClumpData::ForAllFramesInSPR void RpAnimBlendClumpUpdateAnimations(RpClump *clump, float timeDelta, bool doRender) { int i; CAnimBlendAssociation *assoc; AnimBlendFrameUpdateData updateData; float totalLength = 0.0f; float totalBlend = 0.0f; CAnimBlendLink *link, *next; CAnimBlendClumpData *clumpData = *RPANIMBLENDCLUMPDATA(clump); gpAnimBlendClump = clumpData; if(clumpData->link.next == nil) return; // Update blend and get node array i = 0; updateData.foobar = 0; for(link = clumpData->link.next; link; link = next){ next = link->next; assoc = CAnimBlendAssociation::FromLink(link); if(assoc->UpdateBlend(timeDelta)){ if(assoc->hierarchy->sequences){ CAnimManager::UncompressAnimation(assoc->hierarchy); if(i < 11) updateData.nodes[i++] = assoc->GetNode(0); if(assoc->flags & ASSOC_MOVEMENT){ totalLength += assoc->hierarchy->totalLength/assoc->speed * assoc->blendAmount; totalBlend += assoc->blendAmount; }else updateData.foobar = 1; }else debug("anim %s is not loaded\n", assoc->hierarchy->name); } } for(link = clumpData->link.next; link; link = link->next){ assoc = CAnimBlendAssociation::FromLink(link); assoc->UpdateTimeStep(timeDelta, totalLength == 0.0f ? 1.0f : totalBlend/totalLength); } updateData.nodes[i] = nil; #ifdef ANIM_COMPRESSION if(clumpData->frames[0].flag & AnimBlendFrameData::COMPRESSED){ if(IsClumpSkinned(clump)) clumpData->ForAllFrames(FrameUpdateCallBackSkinnedCompressed, &updateData); else clumpData->ForAllFrames(FrameUpdateCallBackNonSkinnedCompressed, &updateData); }else #endif if(doRender){ if(clumpData->frames[0].flag & AnimBlendFrameData::UPDATE_KEYFRAMES) RpAnimBlendNodeUpdateKeyframes(clumpData->frames, &updateData, clumpData->numFrames); if(IsClumpSkinned(clump)) clumpData->ForAllFrames(FrameUpdateCallBackSkinned, &updateData); else clumpData->ForAllFrames(FrameUpdateCallBackNonSkinned, &updateData); clumpData->frames[0].flag &= ~AnimBlendFrameData::UPDATE_KEYFRAMES; }else{ clumpData->ForAllFrames(FrameUpdateCallBackOffscreen, &updateData); clumpData->frames[0].flag |= AnimBlendFrameData::UPDATE_KEYFRAMES; } for(link = clumpData->link.next; link; link = link->next){ assoc = CAnimBlendAssociation::FromLink(link); assoc->UpdateTime(timeDelta, totalLength == 0.0f ? 1.0f : totalBlend/totalLength); } RwFrameUpdateObjects(RpClumpGetFrame(clump)); } ================================================ FILE: src/animation/RpAnimBlend.h ================================================ #pragma once class CAnimBlendNode; class CAnimBlendAssociation; class CAnimBlendClumpData; struct AnimBlendFrameData; struct AnimBlendFrameUpdateData { int foobar; // TODO: figure out what this actually means CAnimBlendNode *nodes[16]; }; extern RwInt32 ClumpOffset; #define RPANIMBLENDCLUMPDATA(o) (RWPLUGINOFFSET(CAnimBlendClumpData*, o, ClumpOffset)) bool RpAnimBlendPluginAttach(void); CAnimBlendAssociation *RpAnimBlendGetNextAssociation(CAnimBlendAssociation *assoc); CAnimBlendAssociation *RpAnimBlendGetNextAssociation(CAnimBlendAssociation *assoc, uint32 mask); void RpAnimBlendAllocateData(RpClump *clump); void RpAnimBlendClumpSetBlendDeltas(RpClump *clump, uint32 mask, float delta); void RpAnimBlendClumpRemoveAllAssociations(RpClump *clump); void RpAnimBlendClumpRemoveAssociations(RpClump *clump, uint32 mask); void RpAnimBlendClumpInit(RpClump *clump); bool RpAnimBlendClumpIsInitialized(RpClump *clump); void RpAnimBlendClumpFillFrameArray(RpClump* clump, AnimBlendFrameData** frames); AnimBlendFrameData *RpAnimBlendClumpFindFrame(RpClump *clump, const char *name); AnimBlendFrameData *RpAnimBlendClumpFindBone(RpClump *clump, uint32 boneTag); void FillFrameArrayCallBack(AnimBlendFrameData *frame, void *arg); CAnimBlendAssociation *RpAnimBlendClumpGetAssociation(RpClump *clump, uint32 id); CAnimBlendAssociation *RpAnimBlendClumpGetMainAssociation(RpClump *clump, CAnimBlendAssociation **assocRet, float *blendRet); CAnimBlendAssociation *RpAnimBlendClumpGetMainPartialAssociation(RpClump *clump); CAnimBlendAssociation *RpAnimBlendClumpGetMainAssociation_N(RpClump *clump, int n); CAnimBlendAssociation *RpAnimBlendClumpGetMainPartialAssociation_N(RpClump *clump, int n); CAnimBlendAssociation *RpAnimBlendClumpGetFirstAssociation(RpClump *clump, uint32 mask); CAnimBlendAssociation *RpAnimBlendClumpGetFirstAssociation(RpClump *clump); void RpAnimBlendNodeUpdateKeyframes(AnimBlendFrameData *frames, AnimBlendFrameUpdateData *updateData, int32 numNodes); void RpAnimBlendClumpUpdateAnimations(RpClump* clump, float timeDelta, bool doRender = true); extern CAnimBlendClumpData *gpAnimBlendClump; void FrameUpdateCallBackNonSkinned(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackSkinned(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackOffscreen(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackNonSkinnedCompressed(AnimBlendFrameData *frame, void *arg); void FrameUpdateCallBackSkinnedCompressed(AnimBlendFrameData *frame, void *arg); ================================================ FILE: src/audio/AudioCollision.cpp ================================================ #include "common.h" #include "DMAudio.h" #include "Entity.h" #include "AudioCollision.h" #include "AudioManager.h" #include "AudioSamples.h" #include "SurfaceTable.h" #include "sampman.h" const int CollisionSoundIntensity = 60; cAudioCollisionManager::cAudioCollisionManager() { m_sQueue.m_pEntity1 = nil; m_sQueue.m_pEntity2 = nil; m_sQueue.m_bSurface1 = SURFACE_DEFAULT; m_sQueue.m_bSurface2 = SURFACE_DEFAULT; m_sQueue.m_fIntensity2 = 0.0f; m_sQueue.m_fIntensity1 = 0.0f; m_sQueue.m_vecPosition = CVector(0.0f, 0.0f, 0.0f); for (int i = 0; i < NUMAUDIOCOLLISIONS; i++) m_bIndicesTable[i] = NUMAUDIOCOLLISIONS; m_bCollisionsInQueue = 0; } void cAudioCollisionManager::AddCollisionToRequestedQueue() { int32 collisionsIndex; int32 i; if (m_bCollisionsInQueue < NUMAUDIOCOLLISIONS) collisionsIndex = m_bCollisionsInQueue++; else { collisionsIndex = m_bIndicesTable[NUMAUDIOCOLLISIONS - 1]; if (m_sQueue.m_fDistance >= m_asCollisions1[collisionsIndex].m_fDistance) return; } m_asCollisions1[collisionsIndex] = m_sQueue; i = 0; if(collisionsIndex) { while(m_asCollisions1[m_bIndicesTable[i]].m_fDistance <= m_asCollisions1[collisionsIndex].m_fDistance) { if(++i >= collisionsIndex) { m_bIndicesTable[i] = collisionsIndex; return; } } memmove(&m_bIndicesTable[i + 1], &m_bIndicesTable[i], NUMAUDIOCOLLISIONS - 1 - i); } m_bIndicesTable[i] = collisionsIndex; } float cAudioManager::GetCollisionLoopingRatio(uint32 a, uint32 b, float c) const { return GetCollisionRatio(c, 0.0f, 0.02f, 0.02f); } float cAudioManager::GetCollisionOneShotRatio(int32 a, float b) const { float result; switch(a) { case SURFACE_DEFAULT: case SURFACE_TARMAC: case SURFACE_PAVEMENT: case SURFACE_STEEP_CLIFF: case SURFACE_TRANSPARENT_STONE: case SURFACE_CONCRETE_BEACH: result = GetCollisionRatio(b, 10.f, 60.f, 50.f); break; case SURFACE_GRASS: case SURFACE_GRAVEL: case SURFACE_MUD_DRY: case SURFACE_CARDBOARDBOX: result = GetCollisionRatio(b, 0.f, 2.f, 2.f); break; case SURFACE_CAR: result = GetCollisionRatio(b, 6.f, 50.f, 44.f); break; case SURFACE_GLASS: case SURFACE_METAL_CHAIN_FENCE: result = GetCollisionRatio(b, 0.1f, 10.f, 9.9f); break; case SURFACE_TRANSPARENT_CLOTH: case SURFACE_THICK_METAL_PLATE: result = GetCollisionRatio(b, 30.f, 130.f, 100.f); break; case SURFACE_GARAGE_DOOR: result = GetCollisionRatio(b, 20.f, 100.f, 80.f); break; case SURFACE_CAR_PANEL: result = GetCollisionRatio(b, 0.f, 4.f, 4.f); break; case SURFACE_SCAFFOLD_POLE: case SURFACE_METAL_GATE: case SURFACE_LAMP_POST: result = GetCollisionRatio(b, 1.f, 10.f, 9.f); break; case SURFACE_FIRE_HYDRANT: result = GetCollisionRatio(b, 1.f, 15.f, 14.f); break; case SURFACE_GIRDER: result = GetCollisionRatio(b, 8.f, 50.f, 42.f); break; case SURFACE_PED: result = GetCollisionRatio(b, 0.f, 20.f, 20.f); break; case SURFACE_SAND: case SURFACE_WATER: case SURFACE_RUBBER: case SURFACE_WHEELBASE: case SURFACE_SAND_BEACH: result = GetCollisionRatio(b, 0.f, 10.f, 10.f); break; case SURFACE_WOOD_CRATES: result = GetCollisionRatio(b, 1.f, 4.f, 3.f); break; case SURFACE_WOOD_BENCH: result = GetCollisionRatio(b, 0.1f, 5.f, 4.9f); break; case SURFACE_WOOD_SOLID: result = GetCollisionRatio(b, 0.1f, 40.f, 39.9f); break; case SURFACE_PLASTIC: result = GetCollisionRatio(b, 0.1f, 4.f, 3.9f); break; case SURFACE_HEDGE: result = GetCollisionRatio(b, 0.f, 0.5f, 0.5f); break; case SURFACE_CONTAINER: result = GetCollisionRatio(b, 4.f, 40.f, 36.f); break; case SURFACE_NEWS_VENDOR: result = GetCollisionRatio(b, 0.f, 5.f, 5.f); break; default: result = 0.f; break; } return result; } float cAudioManager::GetCollisionRatio(float a, float b, float c, float d) const { float e; e = a; if(a <= b) return 0.0f; if(c <= a) e = c; return (e - b) / d; } uint32 cAudioManager::SetLoopingCollisionRequestedSfxFreqAndGetVol(const cAudioCollision &audioCollision) { uint8 surface1 = audioCollision.m_bSurface1; uint8 surface2 = audioCollision.m_bSurface2; int32 vol; float ratio; if(surface1 == SURFACE_GRASS || surface2 == SURFACE_GRASS || surface1 == SURFACE_HEDGE || surface2 == SURFACE_HEDGE) { ratio = GetCollisionRatio(audioCollision.m_fIntensity2, 0.0001f, 0.09f, 0.0899f); m_sQueueSample.m_nSampleIndex = SFX_RAIN; m_sQueueSample.m_nFrequency = 13000.f * ratio + 35000; vol = 50.f * ratio; } else if(surface1 == SURFACE_WATER || surface2 == SURFACE_WATER) { ratio = GetCollisionRatio(audioCollision.m_fIntensity2, 0.0001f, 0.09f, 0.0899f); m_sQueueSample.m_nSampleIndex = SFX_BOAT_WATER_LOOP; m_sQueueSample.m_nFrequency = 6050.f * ratio + 16000; vol = 30.f * ratio; } else if(surface1 == SURFACE_GRAVEL || surface2 == SURFACE_GRAVEL || surface1 == SURFACE_MUD_DRY || surface2 == SURFACE_MUD_DRY || surface1 == SURFACE_SAND || surface2 == SURFACE_SAND || surface1 == SURFACE_SAND_BEACH || surface2 == SURFACE_SAND_BEACH) { ratio = GetCollisionRatio(audioCollision.m_fIntensity2, 0.0001f, 0.09f, 0.0899f); m_sQueueSample.m_nSampleIndex = SFX_GRAVEL_SKID; m_sQueueSample.m_nFrequency = 6000.f * ratio + 10000; vol = 50.f * ratio; } else if(surface1 == SURFACE_PED || surface2 == SURFACE_PED) { return 0; } else { ratio = GetCollisionRatio(audioCollision.m_fIntensity2, 0.0001f, 0.09f, 0.0899f); m_sQueueSample.m_nSampleIndex = SFX_SCRAPE_CAR_1; m_sQueueSample.m_nFrequency = 10000.f * ratio + 10000; vol = 40.f * ratio; } if(audioCollision.m_nBaseVolume < 2) vol = audioCollision.m_nBaseVolume * vol / 2; return vol; } void cAudioManager::SetUpLoopingCollisionSound(const cAudioCollision &col, uint8 counter) { bool distCalculated = false; if(col.m_fIntensity2 > 0.0016f) { uint8 emittingVol = SetLoopingCollisionRequestedSfxFreqAndGetVol(col); if(emittingVol) { CalculateDistance(distCalculated, m_sQueueSample.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, CollisionSoundIntensity, m_sQueueSample.m_fDistance); if(m_sQueueSample.m_nVolume) { m_sQueueSample.m_nCounter = counter; m_sQueueSample.m_vecPos = col.m_vecPosition; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 7; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = CollisionSoundIntensity; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } } static const int32 gOneShotCol[] = {SFX_COL_TARMAC_1, SFX_COL_TARMAC_1, SFX_COL_GRASS_1, SFX_COL_GRAVEL_1, SFX_COL_MUD_1, SFX_COL_TARMAC_1, SFX_COL_CAR_1, SFX_COL_GRASS_1, SFX_COL_SCAFFOLD_POLE_1, SFX_COL_GARAGE_DOOR_1, SFX_COL_CAR_PANEL_1, SFX_COL_THICK_METAL_PLATE_1, SFX_COL_SCAFFOLD_POLE_1, SFX_COL_LAMP_POST_1, SFX_COL_HYDRANT_1, SFX_COL_HYDRANT_1, SFX_COL_METAL_CHAIN_FENCE_1, SFX_COL_PED_1, SFX_COL_SAND_1, SFX_SPLASH_1, SFX_COL_WOOD_CRATES_1, SFX_COL_WOOD_BENCH_1, SFX_COL_WOOD_SOLID_1, SFX_COL_GRASS_1, SFX_COL_GRASS_1, SFX_COL_VEG_1, SFX_COL_TARMAC_1, SFX_COL_CONTAINER_1, SFX_COL_NEWS_VENDOR_1, SFX_TYRE_BUMP, SFX_COL_CARDBOARD_1, SFX_COL_TARMAC_1, SFX_COL_GATE, SFX_COL_SAND_1, SFX_COL_TARMAC_1 }; void cAudioManager::SetUpOneShotCollisionSound(const cAudioCollision &col) { int16 s1; int16 s2; int32 emittingVol; float ratio; static uint16 counter = 28; for(int32 i = 0; i < 2; i++) { if(i) { s1 = col.m_bSurface2; s2 = col.m_bSurface1; } else { s1 = col.m_bSurface1; s2 = col.m_bSurface2; } ratio = GetCollisionOneShotRatio(s1, col.m_fIntensity1); if(s1 == SURFACE_CAR && s2 == SURFACE_PED) ratio /= 4.0f; if(s1 == SURFACE_CAR && ratio < 0.6f) { s1 = SURFACE_CAR_PANEL; ratio = Min(1.f, 2.f * ratio); } emittingVol = 40.f * ratio; if(emittingVol) { m_sQueueSample.m_fDistance = Sqrt(col.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, CollisionSoundIntensity, m_sQueueSample.m_fDistance); if(m_sQueueSample.m_nVolume) { m_sQueueSample.m_nSampleIndex = gOneShotCol[s1]; switch(m_sQueueSample.m_nSampleIndex) { case SFX_COL_TARMAC_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[3] % 5; break; case SFX_COL_CAR_PANEL_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[0] % 6; break; case SFX_COL_LAMP_POST_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[1] % 2; break; case SFX_COL_METAL_CHAIN_FENCE_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[3] % 4; break; case SFX_COL_PED_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[4] % 2; break; case SFX_COL_WOOD_CRATES_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[4] % 4; break; case SFX_COL_WOOD_BENCH_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[1] % 4; break; case SFX_COL_VEG_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[2] % 5; break; case SFX_COL_NEWS_VENDOR_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[2] % 3; break; case SFX_COL_CAR_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[1] % 5; break; case SFX_COL_CARDBOARD_1: m_sQueueSample.m_nSampleIndex += m_anRandomTable[3] % 2; break; default: break; } switch(s1) { case SURFACE_GLASS: m_sQueueSample.m_nFrequency = 13500; break; case SURFACE_GIRDER: m_sQueueSample.m_nFrequency = 8819; break; case SURFACE_WATER: m_sQueueSample.m_nFrequency = 2 * SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); break; case SURFACE_RUBBER: m_sQueueSample.m_nFrequency = 6000; break; case SURFACE_PLASTIC: m_sQueueSample.m_nFrequency = 8000; break; default: m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); break; } m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nCounter = counter++; if(counter >= 255) counter = 28; m_sQueueSample.m_vecPos = col.m_vecPosition; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 11; m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = CollisionSoundIntensity; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } } void cAudioManager::ServiceCollisions() { int i, j; bool abRepeatedCollision1[NUMAUDIOCOLLISIONS]; bool abRepeatedCollision2[NUMAUDIOCOLLISIONS]; m_sQueueSample.m_nEntityIndex = m_nCollisionEntity; for (int i = 0; i < NUMAUDIOCOLLISIONS; i++) abRepeatedCollision1[i] = abRepeatedCollision2[i] = false; for (i = 0; i < m_sCollisionManager.m_bCollisionsInQueue; i++) { for (j = 0; j < NUMAUDIOCOLLISIONS; j++) { int index = m_sCollisionManager.m_bIndicesTable[i]; if ((m_sCollisionManager.m_asCollisions1[index].m_pEntity1 == m_sCollisionManager.m_asCollisions2[j].m_pEntity1) && (m_sCollisionManager.m_asCollisions1[index].m_pEntity2 == m_sCollisionManager.m_asCollisions2[j].m_pEntity2) && (m_sCollisionManager.m_asCollisions1[index].m_bSurface1 == m_sCollisionManager.m_asCollisions2[j].m_bSurface1) && (m_sCollisionManager.m_asCollisions1[index].m_bSurface2 == m_sCollisionManager.m_asCollisions2[j].m_bSurface2) ) { abRepeatedCollision1[index] = true; abRepeatedCollision2[j] = true; m_sCollisionManager.m_asCollisions1[index].m_nBaseVolume = ++m_sCollisionManager.m_asCollisions2[j].m_nBaseVolume; SetUpLoopingCollisionSound(m_sCollisionManager.m_asCollisions1[index], j); break; } } } for (i = 0; i < NUMAUDIOCOLLISIONS; i++) { if (!abRepeatedCollision2[i]) { m_sCollisionManager.m_asCollisions2[i].m_pEntity1 = nil; m_sCollisionManager.m_asCollisions2[i].m_pEntity2 = nil; m_sCollisionManager.m_asCollisions2[i].m_bSurface1 = SURFACE_DEFAULT; m_sCollisionManager.m_asCollisions2[i].m_bSurface2 = SURFACE_DEFAULT; m_sCollisionManager.m_asCollisions2[i].m_fIntensity2 = 0.0f; m_sCollisionManager.m_asCollisions2[i].m_fIntensity1 = 0.0f; m_sCollisionManager.m_asCollisions2[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f); m_sCollisionManager.m_asCollisions2[i].m_fDistance = 0.0f; } } for (i = 0; i < m_sCollisionManager.m_bCollisionsInQueue; i++) { int index = m_sCollisionManager.m_bIndicesTable[i]; if (!abRepeatedCollision1[index]) { for (j = 0; j < NUMAUDIOCOLLISIONS; j++) { if (!abRepeatedCollision2[j]) { m_sCollisionManager.m_asCollisions2[j].m_nBaseVolume = 1; m_sCollisionManager.m_asCollisions2[j].m_pEntity1 = m_sCollisionManager.m_asCollisions1[index].m_pEntity1; m_sCollisionManager.m_asCollisions2[j].m_pEntity2 = m_sCollisionManager.m_asCollisions1[index].m_pEntity2; m_sCollisionManager.m_asCollisions2[j].m_bSurface1 = m_sCollisionManager.m_asCollisions1[index].m_bSurface1; m_sCollisionManager.m_asCollisions2[j].m_bSurface2 = m_sCollisionManager.m_asCollisions1[index].m_bSurface2; break; } } SetUpOneShotCollisionSound(m_sCollisionManager.m_asCollisions1[index]); SetUpLoopingCollisionSound(m_sCollisionManager.m_asCollisions1[index], j); } } for (int i = 0; i < NUMAUDIOCOLLISIONS; i++) m_sCollisionManager.m_bIndicesTable[i] = NUMAUDIOCOLLISIONS; m_sCollisionManager.m_bCollisionsInQueue = 0; } void cAudioManager::ReportCollision(CEntity *entity1, CEntity *entity2, uint8 surface1, uint8 surface2, float collisionPower, float velocity) { float distSquared; CVector v1; CVector v2; if(!m_bIsInitialised || m_nCollisionEntity < 0 || m_nUserPause || (velocity < 0.0016f && collisionPower < 0.01f)) return; if(entity1->IsBuilding()) { v1 = v2 = entity2->GetPosition(); } else if(entity2->IsBuilding()) { v1 = v2 = entity1->GetPosition(); } else { v1 = entity1->GetPosition(); v2 = entity2->GetPosition(); } CVector pos = (v1 + v2) * 0.5f; distSquared = GetDistanceSquared(pos); if(distSquared < SQR(CollisionSoundIntensity)) { m_sCollisionManager.m_sQueue.m_pEntity1 = entity1; m_sCollisionManager.m_sQueue.m_pEntity2 = entity2; m_sCollisionManager.m_sQueue.m_bSurface1 = surface1; m_sCollisionManager.m_sQueue.m_bSurface2 = surface2; m_sCollisionManager.m_sQueue.m_fIntensity1 = collisionPower; m_sCollisionManager.m_sQueue.m_fIntensity2 = velocity; m_sCollisionManager.m_sQueue.m_vecPosition = pos; m_sCollisionManager.m_sQueue.m_fDistance = distSquared; m_sCollisionManager.AddCollisionToRequestedQueue(); } } ================================================ FILE: src/audio/AudioCollision.h ================================================ #pragma once #define NUMAUDIOCOLLISIONS 10 class CEntity; class cAudioCollision { public: CEntity *m_pEntity1; CEntity *m_pEntity2; uint8 m_bSurface1; uint8 m_bSurface2; float m_fIntensity1; float m_fIntensity2; CVector m_vecPosition; float m_fDistance; int32 m_nBaseVolume; // no methods }; VALIDATE_SIZE(cAudioCollision, 40); class cAudioCollisionManager { public: cAudioCollision m_asCollisions1[NUMAUDIOCOLLISIONS]; cAudioCollision m_asCollisions2[NUMAUDIOCOLLISIONS]; uint8 m_bIndicesTable[NUMAUDIOCOLLISIONS]; uint8 m_bCollisionsInQueue; cAudioCollision m_sQueue; cAudioCollisionManager(); void AddCollisionToRequestedQueue(); }; VALIDATE_SIZE(cAudioCollisionManager, 0x354); ================================================ FILE: src/audio/AudioLogic.cpp ================================================ #include "common.h" #include "AudioManager.h" #include "audio_enums.h" #include "Automobile.h" #include "Boat.h" #include "Bridge.h" #include "Camera.h" #include "Cranes.h" #include "DMAudio.h" #include "Entity.h" #include "Explosion.h" #include "Fire.h" #include "Garages.h" #include "General.h" #include "HandlingMgr.h" #include "Heli.h" #include "ModelIndices.h" #include "MusicManager.h" #include "Pad.h" #include "Ped.h" #include "Physical.h" #include "Placeable.h" #include "Plane.h" #include "PlayerPed.h" #include "Pools.h" #include "Projectile.h" #include "ProjectileInfo.h" #include "Replay.h" #include "Stats.h" #include "SurfaceTable.h" #include "Train.h" #include "Transmission.h" #include "Vehicle.h" #include "WaterCannon.h" #include "Weather.h" #include "ZoneCull.h" #include "sampman.h" #include "Bike.h" #include "WindModifiers.h" #include "Fluff.h" #include "Script.h" #include "Wanted.h" const int channels = ARRAY_SIZE(AudioManager.m_asActiveSamples); const int policeChannel = channels + 1; const int allChannels = channels + 2; enum PLAY_STATUS { PLAY_STATUS_STOPPED = 0, PLAY_STATUS_PLAYING, PLAY_STATUS_FINISHED }; enum LOADING_STATUS { LOADING_STATUS_NOT_LOADED = 0, LOADING_STATUS_LOADED, LOADING_STATUS_FAILED }; void cAudioManager::PreInitialiseGameSpecificSetup() const { BankStartOffset[SFX_BANK_0] = SAMPLEBANK_START; #ifdef GTA_PS2 BankStartOffset[SAMPLEBANK_CAR_PACARD] = SFX_CAR_ACCEL_1; BankStartOffset[SAMPLEBANK_CAR_PATHFINDER] = SFX_CAR_ACCEL_2; BankStartOffset[SAMPLEBANK_CAR_PORSCHE] = SFX_CAR_ACCEL_3; BankStartOffset[SAMPLEBANK_CAR_SPIDER] = SFX_CAR_ACCEL_4; BankStartOffset[SAMPLEBANK_CAR_MERC] = SFX_CAR_ACCEL_5; BankStartOffset[SAMPLEBANK_CAR_MACKTRUCK] = SFX_CAR_ACCEL_6; BankStartOffset[SAMPLEBANK_CAR_HOTROD] = SFX_CAR_ACCEL_7; BankStartOffset[SAMPLEBANK_CAR_COBRA] = SFX_CAR_ACCEL_8; BankStartOffset[SAMPLEBANK_CAR_NONE] = SFX_CAR_ACCEL_9; BankStartOffset[SAMPLEBANK_FRONTEND] = SFX_PAGE_CHANGE_AND_BACK_LEFT; BankStartOffset[SAMPLEBANK_TRAIN] = SFX_TRAIN_STATION_AMBIENCE_LOOP; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_1] = SFX_CLUB_1; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_2] = SFX_CLUB_2; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_3] = SFX_CLUB_3; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_4] = SFX_CLUB_4; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_5] = SFX_CLUB_5; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_6] = SFX_CLUB_6; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_7] = SFX_CLUB_7; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_8] = SFX_CLUB_8; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_9] = SFX_CLUB_9; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_10] = SFX_CLUB_10; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_11] = SFX_CLUB_11; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_12] = SFX_CLUB_12; BankStartOffset[SAMPLEBANK_BUILDING_CLUB_RAGGA] = SFX_CLUB_RAGGA; BankStartOffset[SAMPLEBANK_BUILDING_STRIP_CLUB_1] = SFX_STRIP_CLUB_1; BankStartOffset[SAMPLEBANK_BUILDING_STRIP_CLUB_2] = SFX_STRIP_CLUB_2; BankStartOffset[SAMPLEBANK_BUILDING_WORKSHOP] = SFX_WORKSHOP_1; BankStartOffset[SAMPLEBANK_BUILDING_PIANO_BAR] = SFX_PIANO_BAR_1; BankStartOffset[SAMPLEBANK_BUILDING_SAWMILL] = SFX_SAWMILL_LOOP; BankStartOffset[SAMPLEBANK_BUILDING_DOG_FOOD_FACTORY] = SFX_DOG_FOOD_FACTORY; BankStartOffset[SAMPLEBANK_BUILDING_LAUNDERETTE] = SFX_LAUNDERETTE_LOOP; BankStartOffset[SAMPLEBANK_BUILDING_RESTAURANT_CHINATOWN] = SFX_RESTAURANT_CHINATOWN; BankStartOffset[SAMPLEBANK_BUILDING_RESTAURANT_ITALY] = SFX_RESTAURANT_ITALY; BankStartOffset[SAMPLEBANK_BUILDING_RESTAURANT_GENERIC_1] = SFX_RESTAURANT_GENERIC_1; BankStartOffset[SAMPLEBANK_BUILDING_RESTAURANT_GENERIC_2] = SFX_RESTAURANT_GENERIC_2; BankStartOffset[SAMPLEBANK_BUILDING_AIRPORT] = SFX_AIRPORT_ANNOUNCEMENT_1; BankStartOffset[SAMPLEBANK_BUILDING_SHOP] = SFX_SHOP_LOOP; BankStartOffset[SAMPLEBANK_BUILDING_CINEMA] = SFX_CINEMA_BASS_1; BankStartOffset[SAMPLEBANK_BUILDING_DOCKS] = SFX_DOCKS_FOGHORN; BankStartOffset[SAMPLEBANK_BUILDING_HOME] = SFX_HOME_1; BankStartOffset[SAMPLEBANK_BUILDING_PORN_1] = SFX_PORN_1_LOOP; BankStartOffset[SAMPLEBANK_BUILDING_PORN_2] = SFX_PORN_2_LOOP; BankStartOffset[SAMPLEBANK_BUILDING_PORN_3] = SFX_PORN_3_LOOP; BankStartOffset[SAMPLEBANK_BUILDING_POLICE_BALL] = SFX_POLICE_BALL_1; BankStartOffset[SAMPLEBANK_BUILDING_BANK_ALARM] = SFX_BANK_ALARM_1; BankStartOffset[SAMPLEBANK_BUILDING_RAVE_INDUSTRIAL] = SFX_RAVE_INDUSTRIAL; BankStartOffset[SAMPLEBANK_BUILDING_RAVE_COMMERCIAL] = SFX_RAVE_COMMERCIAL; BankStartOffset[SAMPLEBANK_BUILDING_RAVE_SUBURBAN] = SFX_RAVE_SUBURBAN; BankStartOffset[SAMPLEBANK_BUILDING_RAVE_COMMERCIAL_2] = SFX_RAVE_COMMERCIAL_2; BankStartOffset[SAMPLEBANK_BUILDING_39] = SFX_CLUB_1_1; BankStartOffset[SAMPLEBANK_BUILDING_40] = SFX_CLUB_1_2; BankStartOffset[SAMPLEBANK_BUILDING_41] = SFX_CLUB_1_3; BankStartOffset[SAMPLEBANK_BUILDING_42] = SFX_CLUB_1_4; BankStartOffset[SAMPLEBANK_BUILDING_43] = SFX_CLUB_1_5; BankStartOffset[SAMPLEBANK_BUILDING_44] = SFX_CLUB_1_6; BankStartOffset[SAMPLEBANK_BUILDING_45] = SFX_CLUB_1_7; BankStartOffset[SAMPLEBANK_BUILDING_46] = SFX_CLUB_1_8; BankStartOffset[SAMPLEBANK_BUILDING_47] = SFX_CLUB_1_9; BankStartOffset[SAMPLEBANK_EXTRAS] = SFX_EXPLOSION_1; #endif // GTA_PS2 BankStartOffset[SFX_BANK_PED_COMMENTS] = SAMPLEBANK_PED_START; } void cAudioManager::PostInitialiseGameSpecificSetup() { m_nFireAudioEntity = CreateEntity(AUDIOTYPE_FIRE, &gFireManager); if (m_nFireAudioEntity >= 0) SetEntityStatus(m_nFireAudioEntity, true); m_nCollisionEntity = CreateEntity(AUDIOTYPE_COLLISION, (void *)1); if (m_nCollisionEntity >= 0) SetEntityStatus(m_nCollisionEntity, true); m_nFrontEndEntity = CreateEntity(AUDIOTYPE_FRONTEND, (void *)1); if (m_nFrontEndEntity >= 0) SetEntityStatus(m_nFrontEndEntity, true); m_nProjectileEntity = CreateEntity(AUDIOTYPE_PROJECTILE, (void *)1); if (m_nProjectileEntity >= 0) SetEntityStatus(m_nProjectileEntity, true); m_nWaterCannonEntity = CreateEntity(AUDIOTYPE_WATERCANNON, (void *)1); if (m_nWaterCannonEntity >= 0) SetEntityStatus(m_nWaterCannonEntity, true); m_nPoliceChannelEntity = CreateEntity(AUDIOTYPE_POLICERADIO, (void *)1); if (m_nPoliceChannelEntity >= 0) SetEntityStatus(m_nPoliceChannelEntity, true); #ifdef GTA_BRIDGE m_nBridgeEntity = CreateEntity(AUDIOTYPE_BRIDGE, (void*)1); if (m_nBridgeEntity >= 0) SetEntityStatus(m_nBridgeEntity, true); #endif // GTA_BRIDGE m_nEscalatorEntity = CreateEntity(AUDIOTYPE_ESCALATOR, (void*)1); if (m_nEscalatorEntity >= 0) SetEntityStatus(m_nEscalatorEntity, true); m_nExtraSoundsEntity = CreateEntity(AUDIOTYPE_EXTRA_SOUNDS, (void*)1); if (m_nExtraSoundsEntity >= 0) SetEntityStatus(m_nExtraSoundsEntity, true); m_sMissionAudio.m_nSampleIndex[0] = NO_SAMPLE; m_sMissionAudio.m_nLoadingStatus[0] = LOADING_STATUS_NOT_LOADED; m_sMissionAudio.m_nPlayStatus[0] = PLAY_STATUS_STOPPED; m_sMissionAudio.m_bIsPlaying[0] = false; m_sMissionAudio.m_bIsPlayed[0] = false; m_sMissionAudio.m_bPredefinedProperties[0] = true; m_sMissionAudio.m_nMissionAudioCounter[0] = 0; m_sMissionAudio.m_bIsMobile[0] = false; field_5538 = 127; m_sMissionAudio.m_nSampleIndex[1] = NO_SAMPLE; m_sMissionAudio.m_nLoadingStatus[1] = LOADING_STATUS_NOT_LOADED; m_sMissionAudio.m_nPlayStatus[1] = PLAY_STATUS_STOPPED; m_sMissionAudio.m_bIsPlaying[1] = false; m_sMissionAudio.m_bIsPlayed[1] = false; m_sMissionAudio.m_bPredefinedProperties[1] = true; m_sMissionAudio.m_nMissionAudioCounter[1] = 0; m_sMissionAudio.m_bIsMobile[1] = false; field_5538 = 127; ResetAudioLogicTimers(CTimer::GetTimeInMilliseconds()); m_bIsPlayerShutUp = false; m_nPlayerMood = PLAYER_MOOD_CALM; m_nPlayerMoodTimer = 0; } void cAudioManager::PreTerminateGameSpecificShutdown() { #ifdef GTA_BRIDGE if (m_nBridgeEntity >= 0) { DestroyEntity(m_nBridgeEntity); m_nBridgeEntity = AEHANDLE_NONE; } #endif if (m_nEscalatorEntity >= 0) { DestroyEntity(m_nEscalatorEntity); m_nEscalatorEntity = AEHANDLE_NONE; } if (m_nExtraSoundsEntity >= 0) { DestroyEntity(m_nExtraSoundsEntity); m_nExtraSoundsEntity = AEHANDLE_NONE; } if (m_nPoliceChannelEntity >= 0) { DestroyEntity(m_nPoliceChannelEntity); m_nPoliceChannelEntity = AEHANDLE_NONE; } if (m_nWaterCannonEntity >= 0) { DestroyEntity(m_nWaterCannonEntity); m_nWaterCannonEntity = AEHANDLE_NONE; } if (m_nFireAudioEntity >= 0) { DestroyEntity(m_nFireAudioEntity); m_nFireAudioEntity = AEHANDLE_NONE; } if (m_nCollisionEntity >= 0) { DestroyEntity(m_nCollisionEntity); m_nCollisionEntity = AEHANDLE_NONE; } if (m_nFrontEndEntity >= 0) { DestroyEntity(m_nFrontEndEntity); m_nFrontEndEntity = AEHANDLE_NONE; } if (m_nProjectileEntity >= 0) { DestroyEntity(m_nProjectileEntity); m_nProjectileEntity = AEHANDLE_NONE; } } void cAudioManager::PostTerminateGameSpecificShutdown() { ; } void cAudioManager::ResetAudioLogicTimers(uint32 timer) { for (int32 i = 0; i < m_nAudioEntitiesTotal; i++) { if (m_asAudioEntities[m_anAudioEntityIndices[i]].m_nType == AUDIOTYPE_PHYSICAL) { CPed *ped = (CPed *)m_asAudioEntities[m_anAudioEntityIndices[i]].m_pEntity; if (ped->IsPed()) { ped->m_lastSoundStart = timer; ped->m_soundStart = timer + m_anRandomTable[0] % 3000; } } } ClearMissionAudio(0); ClearMissionAudio(1); SampleManager.StopChannel(policeChannel); } void cAudioManager::ProcessReverb() const { #ifdef FIX_BUGS const uint32 numChannels = channels; #else const uint32 numChannels = 28; #endif if (SampleManager.UpdateReverb() && m_bDynamicAcousticModelingStatus) { for (uint32 i = 0; i < numChannels; i++) { if (m_asActiveSamples[i].m_bReverbFlag) SampleManager.SetChannelReverbFlag(i, true); } } } float cAudioManager::GetDistanceSquared(const CVector &v) const { const CVector &c = TheCamera.GetPosition(); return sq(v.x - c.x) + sq(v.y - c.y) + sq((v.z - c.z) * 0.2f); } void cAudioManager::CalculateDistance(bool &distCalculated, float dist) { if (!distCalculated) { m_sQueueSample.m_fDistance = Sqrt(dist); distCalculated = true; } } CVehicle *cAudioManager::FindVehicleOfPlayer() { CVehicle* vehicle = FindPlayerVehicle(); CPlayerPed* ped = FindPlayerPed(); if (vehicle == nil && ped != nil) { CEntity *attachedTo = ped->m_attachedTo; if (attachedTo && attachedTo->IsVehicle()) vehicle = (CVehicle*)attachedTo; } return vehicle; } void cAudioManager::ProcessPlayerMood() { CPlayerPed *playerPed; uint32& lastMissionPassedTime = CTheScripts::GetLastMissionPassedTime(); uint32 curTime = CTimer::GetTimeInMilliseconds(); if (m_nPlayerMoodTimer <= curTime) { playerPed = FindPlayerPed(); if (playerPed != nil) { if (playerPed->m_pWanted->GetWantedLevel() > 3) { m_nPlayerMood = PLAYER_MOOD_ANGRY; return; } if (playerPed->m_pWanted->GetWantedLevel() > 1) { m_nPlayerMood = PLAYER_MOOD_PISSED_OFF; return; } if (lastMissionPassedTime != -1) { if (curTime < lastMissionPassedTime) { lastMissionPassedTime = curTime; return; } if (curTime < lastMissionPassedTime + 180000) { m_nPlayerMood = PLAYER_MOOD_WISECRACKING; return; } } m_nPlayerMood = PLAYER_MOOD_CALM; } } } void cAudioManager::ProcessSpecial() { CPlayerPed *playerPed; if (m_nUserPause) { if (!m_nPreviousUserPause) { SampleManager.SetEffectsFadeVolume(MAX_VOLUME); SampleManager.SetMusicFadeVolume(MAX_VOLUME); } } else { if (!CReplay::IsPlayingBack()) ProcessPlayerMood(); playerPed = FindPlayerPed(); if (playerPed != nil) { if (playerPed->m_audioEntityId >= 0 && m_asAudioEntities[playerPed->m_audioEntityId].m_bIsUsed) { if (playerPed->EnteringCar()) { if(!playerPed->bInVehicle && CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle == nil) SampleManager.StopChannel(m_nActiveSamples); } } } } } void cAudioManager::ProcessEntity(int32 id) { if (m_asAudioEntities[id].m_bStatus) { m_sQueueSample.m_nEntityIndex = id; switch (m_asAudioEntities[id].m_nType) { case AUDIOTYPE_PHYSICAL: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessPhysical(id); } break; case AUDIOTYPE_EXPLOSION: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessExplosions(id); } break; case AUDIOTYPE_FIRE: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessFires(id); } break; case AUDIOTYPE_WEATHER: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; if(CGame::currArea == AREA_MAIN_MAP || CGame::currArea == AREA_EVERYWHERE) ProcessWeather(id); } break; /* case AUDIOTYPE_CRANE: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessCrane(); } break;*/ case AUDIOTYPE_SCRIPTOBJECT: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessScriptObject(id); } break; #ifdef GTA_BRIDGE case AUDIOTYPE_BRIDGE: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessBridge(); } break; #endif case AUDIOTYPE_FRONTEND: m_sQueueSample.m_bReverbFlag = false; ProcessFrontEnd(); break; case AUDIOTYPE_PROJECTILE: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessProjectiles(); } break; case AUDIOTYPE_GARAGE: if (!m_nUserPause) ProcessGarages(); break; case AUDIOTYPE_FIREHYDRANT: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessFireHydrant(); } break; case AUDIOTYPE_WATERCANNON: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessWaterCannon(id); } break; case AUDIOTYPE_ESCALATOR: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessEscalators(); } break; case AUDIOTYPE_EXTRA_SOUNDS: if (!m_nUserPause) { m_sQueueSample.m_bReverbFlag = true; ProcessExtraSounds(); } break; default: return; } } } void cAudioManager::ProcessPhysical(int32 id) { CPhysical *entity = (CPhysical *)m_asAudioEntities[id].m_pEntity; if (entity) { switch (entity->GetType()) { case ENTITY_TYPE_VEHICLE: ProcessVehicle((CVehicle *)m_asAudioEntities[id].m_pEntity); break; case ENTITY_TYPE_PED: ProcessPed((CPhysical *)m_asAudioEntities[id].m_pEntity); break; default: return; } } } #pragma region VEHICLE AUDIO enum eVehicleModel { LANDSTAL, IDAHO, STINGER, LINERUN, PEREN, SENTINEL, RIO, FIRETRUK, TRASH, STRETCH, MANANA, INFERNUS, VOODOO, PONY, MULE, CHEETAH, AMBULAN, FBICAR, MOONBEAM, ESPERANT, TAXI, WASHING, BOBCAT, MRWHOOP, BFINJECT, HUNTER, POLICE, ENFORCER, SECURICA, BANSHEE, PREDATOR, BUS, RHINO, BARRACKS, CUBAN, CHOPPER, ANGEL, COACH, CABBIE, STALLION, RUMPO, RCBANDIT, ROMERO, PACKER, SENTXS, ADMIRAL, SQUALO, SEASPAR, PIZZABOY, GANGBUR, AIRTRAIN, DEADDODO, SPEEDER, REEFER, TROPIC, FLATBED, YANKEE, CADDY, ZEBRA, TOPFUN, SKIMMER, PCJ600, FAGGIO, FREEWAY, RCBARON, RCRAIDER, GLENDALE, OCEANIC, SANCHEZ, SPARROW, PATRIOT, LOVEFIST, COASTG, DINGHY, HERMES, SABRE, SABRETUR, PHEONIX, WALTON, REGINA, COMET, DELUXO, BURRITO, SPAND, MARQUIS, BAGGAGE, KAUFMAN, MAVERICK, VCNMAV, RANCHER, FBIRANCH, VIRGO, GREENWOO, JETMAX, HOTRING, SANDKING, BLISTAC, POLMAV, BOXVILLE, BENSON, MESA, RCGOBLIN, HOTRINA, HOTRINB, BLOODRA, BLOODRB, VICECHEE, CAR237, CAR238, CAR239, MAX_CARS, // HACK so this compiles // TODO(MIAMI): check it out DODO = -1 }; enum { OLD_DOOR = 0, NEW_DOOR, TRUCK_DOOR, BUS_DOOR, }; struct tVehicleSampleData { eSfxSample m_nAccelerationSampleIndex; uint8 m_nBank; eSfxSample m_nHornSample; int32 m_nHornFrequency; uint8 m_nSirenOrAlarmSample; int32 m_nSirenOrAlarmFrequency; uint8 m_bDoorType; }; const tVehicleSampleData aVehicleSettings[MAX_CARS] = { {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9935, OLD_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 11487, SFX_CAR_HORN_JEEP, 9900, OLD_DOOR}, {SFX_CAR_REV_2, SFX_BANK_PORSCHE, SFX_CAR_HORN_PORSCHE, 11025, SFX_CAR_HORN_JEEP, 9890, NEW_DOOR}, {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 29711, SFX_CAR_HORN_JEEP, 9960, TRUCK_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 12893, SFX_CAR_HORN_JEEP, 9500, OLD_DOOR}, {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_BMW328, 10706, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_TRUCK, 29711, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 29711, SFX_POLICE_SIREN_SLOW, 10588, TRUCK_DOOR}, {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 31478, SFX_CAR_HORN_JEEP, 9800, TRUCK_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_BMW328, 9538, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 10842, SFX_CAR_HORN_JEEP, 10000, OLD_DOOR}, {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_BMW328, 12017, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_JEEP, 22293, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS2, 18000, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS, 18286, SFX_CAR_HORN_JEEP, 9600, OLD_DOOR}, {SFX_CAR_REV_2, SFX_BANK_PORSCHE, SFX_CAR_HORN_PORSCHE, 11025, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_JEEP, 22295, SFX_AMBULANCE_SIREN_SLOW, 12688, OLD_DOOR}, {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_PORSCHE, 9271, SFX_POLICE_SIREN_SLOW, 11471, NEW_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 12170, SFX_CAR_HORN_JEEP, 9400, OLD_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_BMW328, 11000, SFX_CAR_HORN_JEEP, 9300, OLD_DOOR}, {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_BMW328, 10796, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_BMW328, 10500, SFX_CAR_HORN_JEEP, 9100, NEW_DOOR}, {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_PICKUP, 10924, SFX_CAR_HORN_JEEP, 9000, OLD_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_PICKUP, 11025, SFX_ICE_CREAM_TUNE, 11025, OLD_DOOR}, {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9100, OLD_DOOR}, {SFX_HELI_APACHE_1, SFX_BANK_HELI_APACHE, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_BMW328, 10706, SFX_POLICE_SIREN_SLOW, 10511, NEW_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS, 17260, SFX_POLICE_SIREN_SLOW, 11029, OLD_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_PICKUP, 8670, SFX_CAR_HORN_JEEP, 9300, OLD_DOOR}, {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_PORSCHE, 10400, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_POLICE_SIREN_SLOW, 11912, NEW_DOOR}, {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_BUS2, 11652, SFX_CAR_HORN_JEEP, 9500, BUS_DOOR}, {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 29711, SFX_CAR_HORN_JEEP, 9600, TRUCK_DOOR}, {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 28043, SFX_CAR_HORN_JEEP, 9700, TRUCK_DOOR}, {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_JEEP, 25400, SFX_CAR_HORN_JEEP, 9800, OLD_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, {SFX_CAR_REV_17, SFX_BANK_VTWIN, SFX_CAR_HORN_JEEP, 26313, SFX_CAR_HORN_JEEP, 10000, NEW_DOOR}, {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_BUS, 16291, SFX_CAR_HORN_JEEP, 10100, BUS_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 10842, SFX_CAR_HORN_JEEP, 9900, OLD_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 10233, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_PICKUP, 8670, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, {SFX_RC_REV, SFX_BANK_RC, SFX_CAR_HORN_PICKUP, 20000, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 29000, SFX_CAR_HORN_JEEP, 9400, TRUCK_DOOR}, {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_BMW328, 9003, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_PORSCHE, 12375, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_BUS2, 15554, SFX_CAR_HORN_JEEP, 9100, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_BUS2, 13857, SFX_CAR_HORN_JEEP, 9000, TRUCK_DOOR}, {SFX_MOPED_REV, SFX_BANK_MOPED, SFX_CAR_HORN_JEEP, 30000, SFX_CAR_HORN_JEEP, 9100, NEW_DOOR}, {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_JEEP, 22043, SFX_CAR_HORN_JEEP, 9200, OLD_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 21043, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, {SFX_CAR_REV_5, SFX_BANK_TRUCK, SFX_CAR_HORN_TRUCK, 28043, SFX_CAR_HORN_JEEP, 9800, TRUCK_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS, 18286, SFX_CAR_HORN_JEEP, 9900, OLD_DOOR}, {SFX_CAR_REV_12, SFX_BANK_GOLF_CART, SFX_CAR_HORN_JEEP, 28500, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_56CHEV, 10842, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_BUS2, 18000, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, {SFX_SEAPLANE_PRO1, SFX_BANK_PLANE_SEAPLANE, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, {SFX_CAR_REV_20, SFX_BANK_SPORTS_BIKE, SFX_CAR_HORN_JEEP, 27000, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, {SFX_MOPED_REV, SFX_BANK_MOPED, SFX_CAR_HORN_JEEP, 31000, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, {SFX_CAR_REV_17, SFX_BANK_VTWIN, SFX_CAR_HORN_PICKUP, 11000, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, {SFX_RC_REV, SFX_BANK_RC, SFX_CAR_HORN_JEEP, 30000, SFX_CAR_HORN_JEEP, 15000, NEW_DOOR}, {SFX_CAR_RC_HELI, SFX_BANK_RC_HELI, SFX_CAR_HORN_JEEP, 30000, SFX_CAR_HORN_JEEP, 15000, NEW_DOOR}, {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_56CHEV, 10300, SFX_CAR_HORN_JEEP, 9100, OLD_DOOR}, {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_56CHEV, 10500, SFX_CAR_HORN_JEEP, 9000, OLD_DOOR}, {SFX_CAR_REV_19, SFX_BANK_HONDA250, SFX_CAR_HORN_JEEP, 30000, SFX_CAR_HORN_JEEP, 9000, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9100, TRUCK_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_TRUCK, 28000, SFX_CAR_HORN_JEEP, 9200, TRUCK_DOOR}, {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_PICKUP, 11200, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_56CHEV, 10700, SFX_CAR_HORN_JEEP, 9600, OLD_DOOR}, {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_BMW328, 9000, SFX_CAR_HORN_JEEP, 9700, OLD_DOOR}, {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_BMW328, 9200, SFX_CAR_HORN_JEEP, 9800, OLD_DOOR}, {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, {SFX_CAR_REV_11, SFX_BANK_PACARD, SFX_CAR_HORN_56CHEV, 10540, SFX_CAR_HORN_JEEP, 9935, TRUCK_DOOR}, {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_PICKUP, 11000, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, {SFX_CAR_REV_2, SFX_BANK_PORSCHE, SFX_CAR_HORN_BMW328, 9500, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_BMW328, 9700, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_BUS2, 18000, SFX_CAR_HORN_JEEP, 9600, OLD_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_BUS, 18000, SFX_CAR_HORN_JEEP, 9500, TRUCK_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_JEEP, 27513, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_56CHEV, 10700, SFX_CAR_HORN_JEEP, 9200, OLD_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9100, TRUCK_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9000, TRUCK_DOOR}, {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_BUS2, 18000, SFX_CAR_HORN_JEEP, 9100, TRUCK_DOOR}, {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_BUS2, 17900, SFX_POLICE_SIREN_SLOW, 10511, TRUCK_DOOR}, {SFX_CAR_REV_4, SFX_BANK_MERC, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, {SFX_CAR_REV_8, SFX_BANK_PONTIAC_SLOW, SFX_CAR_HORN_BMW328, 9600, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, {SFX_CAR_REV_4, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_PORSCHE, 10000, SFX_CAR_HORN_JEEP, 9500, OLD_DOOR}, {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_PORSCHE, 10500, SFX_CAR_HORN_JEEP, 9600, OLD_DOOR}, {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_JEEP, 25513, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, {SFX_CAR_REV_1, SFX_BANK_0, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, {SFX_CAR_REV_3, SFX_BANK_SPIDER, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9900, NEW_DOOR}, {SFX_CAR_REV_10, SFX_BANK_PATHFINDER, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9800, NEW_DOOR}, {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, {SFX_CAR_RC_HELI, SFX_BANK_RC_HELI, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, {SFX_CAR_REV_6, SFX_BANK_HOTROD, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9700, NEW_DOOR}, {SFX_CAR_REV_7, SFX_BANK_COBRA, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9600, NEW_DOOR}, {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9500, NEW_DOOR}, {SFX_CAR_REV_9, SFX_BANK_CADILLAC, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR}, {SFX_CAR_REV_2, SFX_BANK_PORSCHE, SFX_CAR_HORN_PORSCHE, 11025, SFX_POLICE_SIREN_SLOW, 11000, NEW_DOOR}, {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9200, NEW_DOOR}, {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9300, NEW_DOOR}, {SFX_CAR_REV_1, CAR_SFX_BANKS_OFFSET, SFX_CAR_HORN_JEEP, 26513, SFX_CAR_HORN_JEEP, 9400, NEW_DOOR} }; bool bPlayerJustEnteredCar; const bool hornPatternsArray[8][44] = { {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false}, {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false}, {false, false, true, true, true, true, true, true, true, true, true, true, false, false, false, false, true, true, true, true, true, false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false}, {false, false, true, true, true, true, true, false, false, true, true, true, true, true, false, false, false, true, true, true, true, true, true, true, true, true, true, false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, false}, {false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {false, false, true, true, true, false, false, false, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {false, false, true, true, true, true, false, false, false, false, true, true, true, false, false, true, true, true, false, false, true, true, true, true, true, true, false, false, false, false, false, true, true, true, true, true, true, true, true, true, true, true, false, false}, {false, false, true, true, true, true, false, false, true, true, true, true, true, false, false, false, true, true, true, true, true, true, false, false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false}, }; void cAudioManager::ProcessVehicle(CVehicle* veh) { CVehicle* playerVeh; cVehicleParams params; CBike* bike; CAutomobile* automobile; playerVeh = FindVehicleOfPlayer(); if (playerVeh == veh || CGame::currArea == AREA_OVALRING || CGame::currArea == AREA_BLOOD || CGame::currArea == AREA_DIRT || CGame::currArea == AREA_EVERYWHERE || CGame::currArea == AREA_MALL || CGame::currArea == AREA_MAIN_MAP) { m_sQueueSample.m_vecPos = veh->GetPosition(); params.m_bDistanceCalculated = false; params.m_pVehicle = veh; params.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); params.m_pTransmission = veh->pHandling != nil ? &veh->pHandling->Transmission : nil; params.m_nIndex = veh->m_modelIndex - MI_FIRST_VEHICLE; if (veh->GetStatus() == STATUS_SIMPLE) params.m_fVelocityChange = veh->AutoPilot.m_fMaxTrafficSpeed * 0.02f; else params.m_fVelocityChange = DotProduct(veh->m_vecMoveSpeed, veh->GetForward()); params.m_VehicleType = veh->m_vehType; if (CGame::currArea == AREA_MALL && playerVeh != veh) { ProcessVehicleOneShots(params); ProcessVehicleSirenOrAlarm(params); ProcessEngineDamage(params); return; } switch (params.m_VehicleType) { case VEHICLE_TYPE_CAR: automobile = (CAutomobile*)veh; UpdateGasPedalAudio(veh, params.m_VehicleType); if (veh->m_modelIndex == MI_RCBANDIT || veh->m_modelIndex == MI_RCBARON) { ProcessModelVehicle(params); ProcessEngineDamage(params); } else if (veh->m_modelIndex == MI_RCRAIDER || veh->m_modelIndex == MI_RCGOBLIN) { ProcessModelHeliVehicle(params); ProcessEngineDamage(params); } else { switch (veh->GetVehicleAppearance()) { case VEHICLE_APPEARANCE_HELI: ProcessCarHeli(params); ProcessVehicleFlatTyre(params); ProcessEngineDamage(params); break; case VEHICLE_APPEARANCE_BOAT: case VEHICLE_APPEARANCE_PLANE: break; default: if (ProcessVehicleRoadNoise(params)) { ProcessReverseGear(params); if (CWeather::WetRoads > 0.0f) ProcessWetRoadNoise(params); ProcessVehicleSkidding(params); ProcessVehicleFlatTyre(params); ProcessVehicleHorn(params); ProcessVehicleSirenOrAlarm(params); if (UsesReverseWarning(params.m_nIndex)) ProcessVehicleReverseWarning(params); if(HasAirBrakes(params.m_nIndex)) ProcessAirBrakes(params); ProcessCarBombTick(params); ProcessVehicleEngine(params); ProcessEngineDamage(params); ProcessVehicleDoors(params); } break; } } ProcessVehicleOneShots(params); automobile->m_fVelocityChangeForAudio = params.m_fVelocityChange; break; case VEHICLE_TYPE_BOAT: if (veh->m_modelIndex == MI_SKIMMER) ProcessCarHeli(params); else ProcessBoatEngine(params); ProcessBoatMovingOverWater(params); ProcessVehicleOneShots(params); break; case VEHICLE_TYPE_HELI: ProcessCarHeli(params); ProcessVehicleOneShots(params); break; case VEHICLE_TYPE_PLANE: ProcessPlane(params); ProcessVehicleOneShots(params); ProcessVehicleFlatTyre(params); break; case VEHICLE_TYPE_BIKE: bike = (CBike*)veh; UpdateGasPedalAudio(veh, params.m_VehicleType); if (ProcessVehicleRoadNoise(params)) { if (CWeather::WetRoads > 0.0f) ProcessWetRoadNoise(params); ProcessVehicleSkidding(params); ProcessVehicleHorn(params); ProcessVehicleSirenOrAlarm(params); ProcessCarBombTick(params); ProcessEngineDamage(params); ProcessVehicleEngine(params); ProcessVehicleFlatTyre(params); } ProcessVehicleOneShots(params); bike->m_fVelocityChangeForAudio = params.m_fVelocityChange; break; default: break; } ProcessRainOnVehicle(params); } } void cAudioManager::ProcessRainOnVehicle(cVehicleParams& params) { const int SOUND_INTENSITY = 22.0f; CVehicle *veh; uint8 emittingVol; if (params.m_fDistance >= SQR(SOUND_INTENSITY) || CWeather::Rain <= 0.01f || CCullZones::CamNoRain() && CCullZones::PlayerNoRain()) return; veh = params.m_pVehicle; veh->m_bRainAudioCounter++; if (veh->m_bRainAudioCounter >= 2) { veh->m_bRainAudioCounter = 0; CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); emittingVol = 30.0f * CWeather::Rain; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = veh->m_bRainSamplesCounter++; if (veh->m_bRainSamplesCounter > 4) veh->m_bRainSamplesCounter = 68; m_sQueueSample.m_nSampleIndex = (m_anRandomTable[1] & 3) + SFX_CAR_RAIN_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 9; m_sQueueSample.m_nFrequency = m_anRandomTable[1] % 4000 + 28000; m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bReverbFlag = false; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } bool cAudioManager::ProcessReverseGear(cVehicleParams& params) { const int reverseGearIntensity = 30; CAutomobile* automobile; float modificator; uint8 emittingVolume; if (params.m_fDistance >= SQR(reverseGearIntensity)) return false; automobile = (CAutomobile*)params.m_pVehicle; if (automobile->m_modelIndex == MI_CADDY) return true; if (automobile->bEngineOn && (automobile->m_fGasPedal < 0.0f || automobile->m_nCurrentGear == 0)) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); if (automobile->m_nDriveWheelsOnGround != 0) { modificator = params.m_fVelocityChange / params.m_pTransmission->fMaxReverseVelocity; } else { if (automobile->m_nDriveWheelsOnGroundPrev != 0) automobile->m_fGasPedalAudio *= 0.4f; modificator = automobile->m_fGasPedalAudio; } modificator = Abs(modificator); emittingVolume = modificator * 24.0f; m_sQueueSample.m_nVolume = ComputeVolume(emittingVolume, reverseGearIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { if (params.m_pVehicle->m_fGasPedal >= 0.0f) { m_sQueueSample.m_nCounter = 62; m_sQueueSample.m_nSampleIndex = SFX_REVERSE_GEAR_2; } else { m_sQueueSample.m_nCounter = 61; m_sQueueSample.m_nSampleIndex = SFX_REVERSE_GEAR; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nFrequency = (6000.0f * modificator) + 7000; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVolume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = reverseGearIntensity; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } return true; } void cAudioManager::ProcessModelVehicle(cVehicleParams& params) { const float SOUND_INTENSITY = 35.0f; static uint32 prevFreq = 14000; static uint8 prevVolume = 0; uint32 freq; int16 acceletateState; int16 brakeState; uint8 volume; bool isPlayerVeh; bool vehSlowdown; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return; if (FindPlayerVehicle() == params.m_pVehicle) isPlayerVeh = true; else #ifdef FIX_BUGS isPlayerVeh = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle == params.m_pVehicle; #else isPlayerVeh = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle != nil; #endif if (params.m_pVehicle->m_modelIndex == MI_RCBANDIT) { if (((CAutomobile*)params.m_pVehicle)->m_nDriveWheelsOnGround != 0) { volume = Min(127, 127.0f * Abs(params.m_fVelocityChange) * 3.0f); freq = 8000.0f * Abs(params.m_fVelocityChange) + 14000; } else { volume = 127; freq = 25000; } if (isPlayerVeh) { volume = clamp2(volume, prevVolume, 7); freq = clamp2(freq, prevFreq, 800); } if (volume > 0) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(volume, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 2; m_sQueueSample.m_nSampleIndex = SFX_RC_REV; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = freq; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = volume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_RC_REV); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_RC_REV); m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 4; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } if (isPlayerVeh) { prevFreq = freq; prevVolume = volume; } } else if (params.m_pVehicle != nil) { if (isPlayerVeh) { acceletateState = Pads[0].GetAccelerate(); brakeState = Pads[0].GetBrake(); } else { acceletateState = 255.0f * params.m_pVehicle->m_fGasPedal; brakeState = 255.0f * params.m_pVehicle->m_fBrakePedal; } if (acceletateState < brakeState) acceletateState = brakeState; if (acceletateState <= 0) { vehSlowdown = true; volume = 127; freq = 18000; } else { vehSlowdown = false; volume = Min(127, (127 * acceletateState / 255) * 3.0f * Abs(params.m_fVelocityChange)); freq = Min(22000, (8000 * acceletateState / 255 + 14000) * 3.0f * Abs(params.m_fVelocityChange)); } if (isPlayerVeh && !vehSlowdown) { volume = clamp2(volume, prevVolume, 7); freq = clamp2(freq, prevFreq, 800); } if (!vehSlowdown) #ifdef THIS_IS_STUPID freq += 8000.0f * Abs(DotProduct(params.m_pVehicle->GetUp(), CVector(0.0f, 1.0f, 0.0f))); #else freq += 8000.0f * Abs(params.m_pVehicle->GetUp().y); #endif if (params.m_pVehicle->bIsDrowning) volume /= 4; if (volume > 0) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(volume, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { if (vehSlowdown) { m_sQueueSample.m_nCounter = 0; m_sQueueSample.m_nSampleIndex = SFX_RC_IDLE; m_sQueueSample.m_nReleasingVolumeDivider = 6; } else { m_sQueueSample.m_nCounter = 2; m_sQueueSample.m_nSampleIndex = SFX_RC_REV; m_sQueueSample.m_nReleasingVolumeDivider = 4; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nFrequency = freq; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = volume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } if (isPlayerVeh) { if (vehSlowdown) { prevFreq = freq; prevVolume = volume; } } } } void cAudioManager::ProcessModelHeliVehicle(cVehicleParams& params) { const float SOUND_INTENSITY = 35.0f; static uint32 prevFreq = 22050; uint32 freq; bool isPlayerVeh; int16 acceletateState; int16 brakeState; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return; if (FindPlayerVehicle() == params.m_pVehicle) isPlayerVeh = true; else #ifdef FIX_BUGS isPlayerVeh = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle == params.m_pVehicle; #else isPlayerVeh = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle != nil; #endif if (isPlayerVeh) { brakeState = Pads[0].GetBrake(); acceletateState = Max(Pads[0].GetAccelerate(), Abs(Pads[0].GetCarGunUpDown()) * 2); } else { acceletateState = 255.0f * params.m_pVehicle->m_fGasPedal; brakeState = 255.0f * params.m_pVehicle->m_fBrakePedal; } if (acceletateState < brakeState) acceletateState = brakeState; freq = clamp2(5 * acceletateState + 22050, prevFreq, 30); CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(70, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 2; m_sQueueSample.m_nSampleIndex = SFX_CAR_RC_HELI; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nFrequency = freq; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = 70; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_CAR_RC_HELI); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_CAR_RC_HELI); m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 4; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } if (isPlayerVeh) prevFreq = freq; } bool cAudioManager::ProcessVehicleRoadNoise(cVehicleParams& params) { const float SOUND_INTENSITY = 95.0f; int32 emittingVol; uint32 freq; float multiplier; int sampleFreq; float velocity; uint8 wheelsOnGround; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; switch (params.m_VehicleType) { case VEHICLE_TYPE_CAR: wheelsOnGround = ((CAutomobile*)params.m_pVehicle)->m_nWheelsOnGround; break; case VEHICLE_TYPE_BIKE: wheelsOnGround = ((CBike*)params.m_pVehicle)->m_nWheelsOnGround; break; default: wheelsOnGround = 4; break; } if (params.m_pTransmission == nil || wheelsOnGround == 0) return true; velocity = Abs(params.m_fVelocityChange); if (velocity > 0.0f) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); emittingVol = 30.f * Min(1.f, velocity / (0.5f * params.m_pTransmission->fMaxVelocity)); m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 0; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; if (params.m_pVehicle->m_nSurfaceTouched == SURFACE_WATER) { m_sQueueSample.m_nSampleIndex = SFX_BOAT_WATER_LOOP; freq = 6050 * emittingVol / 30 + 16000; } else { m_sQueueSample.m_nSampleIndex = SFX_ROAD_NOISE; multiplier = (m_sQueueSample.m_fDistance / SOUND_INTENSITY) * 0.5f; sampleFreq = SampleManager.GetSampleBaseFrequency(SFX_ROAD_NOISE); freq = (sampleFreq * multiplier) + ((3 * sampleFreq) / 4); } m_sQueueSample.m_nFrequency = freq; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 4; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } return true; } bool cAudioManager::ProcessWetRoadNoise(cVehicleParams& params) { const float SOUND_INTENSITY = 30.0f; float relativeVelocity; int32 emittingVol; float multiplier; int freq; float velocity; uint8 wheelsOnGround; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; switch (params.m_VehicleType) { case VEHICLE_TYPE_CAR: wheelsOnGround = ((CAutomobile*)params.m_pVehicle)->m_nWheelsOnGround; break; case VEHICLE_TYPE_BIKE: wheelsOnGround = ((CBike*)params.m_pVehicle)->m_nWheelsOnGround; break; default: wheelsOnGround = 4; break; } if (params.m_pTransmission == nil || wheelsOnGround == 0) return true; velocity = Abs(params.m_fVelocityChange); if (velocity > 0.0f) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); relativeVelocity = Min(1.0f, velocity / (0.5f * params.m_pTransmission->fMaxVelocity)); emittingVol = 23.0f * relativeVelocity * CWeather::WetRoads; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 1; m_sQueueSample.m_nSampleIndex = SFX_ROAD_NOISE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; multiplier = (m_sQueueSample.m_fDistance / SOUND_INTENSITY) * 0.5f; freq = SampleManager.GetSampleBaseFrequency(SFX_ROAD_NOISE); m_sQueueSample.m_nFrequency = freq + freq * multiplier; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 4; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } return true; } void cAudioManager::ProcessVehicleEngine(cVehicleParams& params) { const float SOUND_INTENSITY = 50.0f; CVehicle* playerVeh; CVehicle* veh; CAutomobile* automobile; cTransmission* transmission; CBike* bike; tWheelState* wheelState; float* gasPedalAudioPtr; int32 freq = 0; uint8 currentGear; uint8 emittingVol; int8 wheelsOnGround; int8 wheelsOnGroundPrev; float relativeGearChange; float relativeChange; float modificator; float traction; bool isMoped; bool caddyBool; isMoped = false; caddyBool = false; traction = 0.0f; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return; playerVeh = FindPlayerVehicle(); veh = params.m_pVehicle; if (playerVeh == veh && veh->GetStatus() == STATUS_WRECKED) { SampleManager.StopChannel(m_nActiveSamples); return; } if (!veh->bEngineOn) return; CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); if (playerVeh == veh && veh->m_modelIndex != MI_CADDY) { ProcessPlayersVehicleEngine(params, params.m_pVehicle); return; } transmission = params.m_pTransmission; if (transmission != nil) { switch (veh->m_modelIndex) { case MI_PIZZABOY: case MI_FAGGIO: isMoped = true; currentGear = transmission->nNumberOfGears; break; case MI_CADDY: currentGear = transmission->nNumberOfGears; caddyBool = true; break; default: currentGear = veh->m_nCurrentGear; break; } switch (params.m_VehicleType) { case VEHICLE_TYPE_CAR: automobile = (CAutomobile*)veh; wheelsOnGround = automobile->m_nDriveWheelsOnGround; wheelsOnGroundPrev = automobile->m_nDriveWheelsOnGroundPrev; wheelState = automobile->m_aWheelState; gasPedalAudioPtr = &automobile->m_fGasPedalAudio; break; case VEHICLE_TYPE_BIKE: bike = (CBike*)veh; wheelsOnGround = bike->m_nDriveWheelsOnGround; wheelsOnGroundPrev = bike->m_nDriveWheelsOnGroundPrev; wheelState = bike->m_aWheelState; gasPedalAudioPtr = &bike->m_fGasPedalAudio; break; default: debug(" ** AUDIOLOG: Unrecognised vehicle type %d in ProcessVehicleEngine() * \n", params.m_VehicleType); return; } if (wheelsOnGround != 0) { if (!veh->bIsHandbrakeOn || isMoped && caddyBool) { //mb bug, bcs it's can't be true together if (veh->GetStatus() == STATUS_SIMPLE || isMoped || caddyBool) { traction = 0.0f; } else { switch (transmission->nDriveType) { case '4': if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { for (int i = 0; i < 2; i++) if (wheelState[i] == WHEEL_STATE_SPINNING) traction += 0.1f; } else { for (int i = 0; i < 4; i++) if (wheelState[i] == WHEEL_STATE_SPINNING) traction += 0.05f; } break; case 'F': if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { if (wheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SPINNING) traction += 0.2f; } else { if (wheelState[CARWHEEL_FRONT_LEFT] == WHEEL_STATE_SPINNING) traction += 0.1f; if (wheelState[CARWHEEL_FRONT_RIGHT] == WHEEL_STATE_SPINNING) traction += 0.1f; } break; case 'R': if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { if (wheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING) traction += 0.2f; } else { if (wheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING) traction += 0.1f; if (wheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING) traction += 0.1f; } break; default: break; } } } else if (params.m_fVelocityChange == 0.0f) { traction = 0.9f; } if (transmission->fMaxVelocity <= 0.0f) { relativeChange = 0.0f; modificator = 0.0f; } else { if (!isMoped && !caddyBool) { if (currentGear != 0) { relativeGearChange = Min(1.0f, params.m_fVelocityChange - transmission->Gears[currentGear].fShiftDownVelocity) / transmission->fMaxVelocity * 2.5f; if (traction == 0.0f && veh->GetStatus() != STATUS_SIMPLE && params.m_fVelocityChange < transmission->Gears[1].fShiftUpVelocity) traction = 0.7f; relativeChange = traction * *gasPedalAudioPtr * 0.95f + (1.0f - traction) * relativeGearChange; } else { relativeChange = Min(1.0f, 1.0f - Abs((params.m_fVelocityChange - transmission->Gears[0].fShiftDownVelocity) / transmission->fMaxReverseVelocity)); } modificator = relativeChange; } else { modificator = Min(1.0f, Abs(params.m_fVelocityChange / transmission->fMaxVelocity > 1.0f)); } } } else { if (wheelsOnGroundPrev != 0) *gasPedalAudioPtr *= 0.4f; relativeChange = *gasPedalAudioPtr; modificator = relativeChange; } if (currentGear != 0 || wheelsOnGround == 0) freq = 1200 * currentGear + 18000.0f * modificator + 14000; else if (params.m_VehicleType == VEHICLE_TYPE_BIKE) freq = 22050; else freq = 13000.0f * modificator + 14000; if (modificator >= 0.75f) emittingVol = 90; else emittingVol = modificator * (4.0f / 3.0f) * 15.0f + 75; } else { modificator = 0.0f; emittingVol = 75; } if (veh->bIsDrowning) emittingVol /= 4; if (caddyBool) { emittingVol = 100.0f * modificator; freq = 2130.0f * modificator + 4270; m_sQueueSample.m_nCounter = 2; } m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { if (!caddyBool) { if (veh->GetStatus() == STATUS_SIMPLE) { if (modificator < 0.02f) { m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nBank - CAR_SFX_BANKS_OFFSET + SFX_CAR_IDLE_1; m_sQueueSample.m_nCounter = 52; freq = 10000.0f * modificator + 22050; } else { m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nAccelerationSampleIndex; m_sQueueSample.m_nCounter = 2; } } else { if (veh->m_fGasPedal < 0.02f) { m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nBank - CAR_SFX_BANKS_OFFSET + SFX_CAR_IDLE_1; m_sQueueSample.m_nCounter = 52; freq = 10000.0f * modificator + 22050; } else { m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nAccelerationSampleIndex; m_sQueueSample.m_nCounter = 2; } } m_sQueueSample.m_nFrequency = freq + 100 * m_sQueueSample.m_nBankIndex % 1000; } else { if (FindVehicleOfPlayer() == params.m_pVehicle) m_sQueueSample.m_nSampleIndex = SFX_CAR_AFTER_ACCEL_12; else m_sQueueSample.m_nSampleIndex = SFX_CAR_REV_12; m_sQueueSample.m_nFrequency = freq + 20 * m_sQueueSample.m_nBankIndex % 100; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; if (m_sQueueSample.m_nSampleIndex == SFX_CAR_IDLE_5 || m_sQueueSample.m_nSampleIndex == SFX_CAR_REV_5) m_sQueueSample.m_nFrequency /= 2; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 8; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } void cAudioManager::UpdateGasPedalAudio(CVehicle* veh, int vehType) { float gasPedal = Abs(veh->m_fGasPedal); float* gasPealAudioPtr; switch(vehType) { case VEHICLE_TYPE_CAR: gasPealAudioPtr = &((CAutomobile *)veh)->m_fGasPedalAudio; break; case VEHICLE_TYPE_BIKE: gasPealAudioPtr = &((CBike *)veh)->m_fGasPedalAudio; break; default: return; } if (*gasPealAudioPtr < gasPedal) *gasPealAudioPtr = Min(*gasPealAudioPtr + 0.09f, gasPedal); else *gasPealAudioPtr = Max(*gasPealAudioPtr - 0.07f, gasPedal); } void cAudioManager::PlayerJustGotInCar() const { if (m_bIsInitialised) bPlayerJustEnteredCar = true; } void cAudioManager::PlayerJustLeftCar(void) const { // UNUSED: This is a perfectly empty function. } void cAudioManager::AddPlayerCarSample(uint8 emittingVolume, int32 freq, uint32 sample, uint8 bank, uint8 counter, bool notLooping) { m_sQueueSample.m_nVolume = ComputeVolume(emittingVolume, 50.f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = counter; m_sQueueSample.m_nSampleIndex = sample; #ifdef GTA_PS2 m_sQueueSample.m_nBankIndex = bank; #else m_sQueueSample.m_nBankIndex = SFX_BANK_0; #endif // GTA_PS2 m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_nFrequency = freq; if (notLooping) { m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nReleasingVolumeDivider = 8; } else { m_sQueueSample.m_nLoopCount = 1; } m_sQueueSample.m_nEmittingVolume = emittingVolume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = 50.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } void cAudioManager::ProcessCesna(cVehicleParams ¶ms) { if(params.m_fDistance < SQR(200)) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(80, 200.f, m_sQueueSample.m_fDistance); if(m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 52; m_sQueueSample.m_nSampleIndex = SFX_CESNA_IDLE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_nFrequency = 12500; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nReleasingVolumeDivider = 8; m_sQueueSample.m_nEmittingVolume = 80; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = 200.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } if(params.m_fDistance < SQR(90)) { m_sQueueSample.m_nVolume = ComputeVolume(80, 90.f, m_sQueueSample.m_fDistance); if(m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 2; m_sQueueSample.m_nSampleIndex = SFX_CESNA_REV; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nFrequency = 25000; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nReleasingVolumeDivider = 4; m_sQueueSample.m_nEmittingVolume = 80; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = 90.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } } void cAudioManager::ProcessPlayersVehicleEngine(cVehicleParams& params, CVehicle* veh) { static int32 GearFreqAdj[] = { 6000, 6000, 3400, 1200, 0, -1000 }; tWheelState* wheelState; CAutomobile* automobile; CBike* bike; CVector pos; float* gasPedalAudioPtr; int32 accelerateState; int32 brakeState; int32 freq; int32 baseFreq; int32 freqModifier; uint32 gearSoundLength; uint32 soundOffset; uint8 engineSoundType; uint8 wheelInUseCounter; uint8 wheelsOnGround; uint8 vol; uint8 currentGear; uint8 wheelsOnGroundPrev; float accelerationMultipler; float gasPedalAudio; float velocityChangeForAudio; float relativeVelocityChange; float time; bool channelUsed; bool lostTraction; bool noGearBox; bool stuckInSand; bool processedAccelSampleStopped; bool isMoped; static uint32 gearSoundStartTime = CTimer::GetTimeInMilliseconds(); static int32 nCruising = 0; static int16 LastAccel = 0; static uint8 CurrentPretendGear = 1; static bool bLostTractionLastFrame = false; static bool bHandbrakeOnLastFrame = false; static bool bAccelSampleStopped = true; lostTraction = false; isMoped = params.m_pVehicle->m_modelIndex == MI_PIZZABOY || params.m_pVehicle->m_modelIndex == MI_FAGGIO; processedAccelSampleStopped = false; if (bPlayerJustEnteredCar) { bAccelSampleStopped = true; bPlayerJustEnteredCar = false; nCruising = 0; LastAccel = 0; bLostTractionLastFrame = false; CurrentPretendGear = 1; bHandbrakeOnLastFrame = false; } if (CReplay::IsPlayingBack()) { accelerateState = (255.0f * clamp(params.m_pVehicle->m_fGasPedal, 0.0f, 1.0f)); brakeState = (255.0f * clamp(params.m_pVehicle->m_fBrakePedal, 0.0f, 1.0f)); } else { accelerateState = Pads[0].GetAccelerate(); brakeState = Pads[0].GetBrake(); } channelUsed = SampleManager.GetChannelUsedFlag(m_nActiveSamples); if (isMoped) { CurrentPretendGear = params.m_pTransmission->nNumberOfGears; currentGear = CurrentPretendGear; if (params.m_pVehicle->bIsHandbrakeOn) { brakeState = 0; nCruising = 0; LastAccel = 0; accelerateState = 0; } else { nCruising = 1; } } else { currentGear = params.m_pVehicle->m_nCurrentGear; } switch (params.m_VehicleType) { case VEHICLE_TYPE_CAR: automobile = (CAutomobile*)params.m_pVehicle; wheelsOnGround = automobile->m_nDriveWheelsOnGround; wheelsOnGroundPrev = automobile->m_nDriveWheelsOnGroundPrev; gasPedalAudioPtr = &automobile->m_fGasPedalAudio; wheelState = automobile->m_aWheelState; velocityChangeForAudio = automobile->m_fVelocityChangeForAudio; break; case VEHICLE_TYPE_BIKE: bike = (CBike*)params.m_pVehicle; wheelsOnGround = bike->m_nDriveWheelsOnGround; wheelsOnGroundPrev = bike->m_nDriveWheelsOnGroundPrev; gasPedalAudioPtr = &bike->m_fGasPedalAudio; wheelState = bike->m_aWheelState; velocityChangeForAudio = bike->m_fVelocityChangeForAudio; break; default: debug(" ** AUDIOLOG: Unrecognised vehicle type %d in ProcessVehicleEngine() * \n", params.m_VehicleType); return; } if (!isMoped) { switch (params.m_pTransmission->nDriveType) { case '4': if (params.m_VehicleType != VEHICLE_TYPE_BIKE) { wheelInUseCounter = 0; for (uint8 i = 0; i < 4; i++) { if (wheelState[i] != WHEEL_STATE_NORMAL) ++wheelInUseCounter; } if (wheelInUseCounter > 2) lostTraction = true; } break; case 'F': if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { if (wheelState[BIKEWHEEL_FRONT] != WHEEL_STATE_NORMAL) lostTraction = true; } else { if ((wheelState[CARWHEEL_FRONT_LEFT] != WHEEL_STATE_NORMAL || wheelState[CARWHEEL_FRONT_RIGHT] != WHEEL_STATE_NORMAL) && (wheelState[CARWHEEL_REAR_LEFT] != WHEEL_STATE_NORMAL || wheelState[CARWHEEL_REAR_RIGHT] != WHEEL_STATE_NORMAL)) lostTraction = true; } break; case 'R': if (params.m_VehicleType == VEHICLE_TYPE_BIKE) { if (wheelState[BIKEWHEEL_REAR] != WHEEL_STATE_NORMAL) lostTraction = true; } else { if (wheelState[CARWHEEL_REAR_LEFT] != WHEEL_STATE_NORMAL || wheelState[CARWHEEL_REAR_RIGHT] != WHEEL_STATE_NORMAL) lostTraction = true; } break; default: break; } } if (params.m_fVelocityChange != 0.0f) { time = params.m_pVehicle->m_vecMoveSpeed.z / params.m_fVelocityChange; if (time > 0.0f) freqModifier = -(Min(0.2f, time) * 3000.0f * 5.0f); else freqModifier = -(Max(-0.2f, time) * 3000.0f * 5.0f); if (params.m_fVelocityChange < -0.001f) freqModifier = -freqModifier; } else freqModifier = 0; if (params.m_VehicleType == VEHICLE_TYPE_BIKE && bike->bExtraSpeed) freqModifier += 1400; gearSoundLength = 0; engineSoundType = aVehicleSettings[params.m_nIndex].m_nBank; soundOffset = 3 * (engineSoundType - CAR_SFX_BANKS_OFFSET); noGearBox = false; switch (engineSoundType) { case SFX_BANK_PONTIAC: gearSoundLength = 2526; break; case SFX_BANK_PORSCHE: gearSoundLength = 3587; break; case SFX_BANK_SPIDER: gearSoundLength = 4898; break; case SFX_BANK_MERC: gearSoundLength = 4003; break; case SFX_BANK_TRUCK: gearSoundLength = 6289; break; case SFX_BANK_HOTROD: gearSoundLength = 2766; break; case SFX_BANK_COBRA: gearSoundLength = 3523; break; case SFX_BANK_PONTIAC_SLOW: gearSoundLength = 2773; break; case SFX_BANK_CADILLAC: gearSoundLength = 2560; break; case SFX_BANK_PATHFINDER: gearSoundLength = 4228; break; case SFX_BANK_PACARD: gearSoundLength = 4648; break; case SFX_BANK_VTWIN: gearSoundLength = 3480; break; case SFX_BANK_HONDA250: gearSoundLength = 2380; break; case SFX_BANK_SPORTS_BIKE: gearSoundLength = 2410; break; default: noGearBox = true; break; } if (!channelUsed || nCruising || noGearBox) { gearSoundStartTime = CTimer::GetTimeInMilliseconds(); } else { gearSoundLength -= 1000; if (CTimer::GetTimeInMilliseconds() - gearSoundStartTime > gearSoundLength) { channelUsed = false; gearSoundStartTime = CTimer::GetTimeInMilliseconds(); } } relativeVelocityChange = 2.0f * params.m_fVelocityChange / params.m_pTransmission->fMaxVelocity; accelerationMultipler = clamp(relativeVelocityChange, 0.0f, 1.0f); gasPedalAudio = accelerationMultipler; switch (engineSoundType) { case SFX_BANK_MOPED: ++soundOffset; break; case SFX_BANK_HONDA250: soundOffset += 2; break; case SFX_BANK_SPORTS_BIKE: soundOffset += 3; break; default: break; } if (accelerateState <= 0) { if (params.m_fVelocityChange < -0.001f) { if (channelUsed) { SampleManager.StopChannel(m_nActiveSamples); bAccelSampleStopped = true; } if (wheelsOnGround == 0 || params.m_pVehicle->bIsHandbrakeOn || lostTraction) gasPedalAudio = *gasPedalAudioPtr; else if (params.m_VehicleType == VEHICLE_TYPE_BIKE) gasPedalAudio = 0.0f; else gasPedalAudio = Min(1.0f, params.m_fVelocityChange / params.m_pTransmission->fMaxReverseVelocity); *gasPedalAudioPtr = Max(0.0f, gasPedalAudio); } else if (LastAccel > 0) { if (channelUsed) { SampleManager.StopChannel(m_nActiveSamples); bAccelSampleStopped = true; } nCruising = 0; if (wheelsOnGround == 0 || params.m_pVehicle->bIsHandbrakeOn || lostTraction || params.m_fVelocityChange < 0.01f && *gasPedalAudioPtr > 0.2f) { if (isMoped) { gasPedalAudio = 0.0f; } else { *gasPedalAudioPtr *= 0.6f; gasPedalAudio = *gasPedalAudioPtr; } } if (gasPedalAudio > 0.05f) { freq = (5000.f * (gasPedalAudio - 0.05f) * 20.f / 19) + 19000; vol = (25.0f * (gasPedalAudio - 0.05f) * 20.f / 19) + 40; if (params.m_pVehicle->bIsDrowning) vol /= 4; if (engineSoundType == SFX_BANK_TRUCK) freq /= 2; AddPlayerCarSample(vol, freq, soundOffset + SFX_CAR_FINGER_OFF_ACCEL_1, engineSoundType, 63, false); } } freq = (10000.f * gasPedalAudio) + 22050; vol = 110 - (40.0f * gasPedalAudio); if (engineSoundType == SFX_BANK_TRUCK) freq /= 2; if (params.m_pVehicle->bIsDrowning) vol /= 4; AddPlayerCarSample(vol, freq, engineSoundType - CAR_SFX_BANKS_OFFSET + SFX_CAR_IDLE_1, SFX_BANK_0, 52, true); CurrentPretendGear = Max(1, currentGear); } else { if (nCruising == 0){ stuckInSand = params.m_VehicleType == VEHICLE_TYPE_CAR && ((CAutomobile*)params.m_pVehicle)->bStuckInSand; if (accelerateState < 150 || wheelsOnGround == 0 || params.m_pVehicle->bIsHandbrakeOn || lostTraction || (currentGear < 2 && params.m_fVelocityChange - velocityChangeForAudio < 0.01f) || brakeState > 0) { if (((wheelsOnGround && !params.m_pVehicle->bIsHandbrakeOn && !lostTraction ) || stuckInSand) && brakeState <= 0) { baseFreq = (8000.0f * accelerationMultipler) + 16000; vol = (25.0f * accelerationMultipler) + 60; *gasPedalAudioPtr = accelerationMultipler; } else { if (wheelsOnGround == 0 && wheelsOnGroundPrev != 0 || (params.m_pVehicle->bIsHandbrakeOn && !bHandbrakeOnLastFrame || lostTraction && !bLostTractionLastFrame) && wheelsOnGround != 0) { *gasPedalAudioPtr *= 0.6f; } freqModifier = 0; if (engineSoundType != SFX_BANK_GOLF_CART && engineSoundType != SFX_BANK_CAR_CHAINSAW) baseFreq = (25000.0f * *gasPedalAudioPtr) + 14000; else baseFreq = (15000.0f * *gasPedalAudioPtr) + 14000; vol = (25.0f * *gasPedalAudioPtr) + 60; } freq = freqModifier + baseFreq; if (engineSoundType == SFX_BANK_TRUCK) freq /= 2; if (channelUsed) { SampleManager.StopChannel(m_nActiveSamples); bAccelSampleStopped = true; } if (params.m_pVehicle->bIsDrowning) vol /= 4; AddPlayerCarSample(vol, freq, engineSoundType - CAR_SFX_BANKS_OFFSET + SFX_CAR_REV_1, SFX_BANK_0, 2, true); } else { TranslateEntity(&m_sQueueSample.m_vecPos, &pos); if (bAccelSampleStopped) { if (CurrentPretendGear != 1 || currentGear != 2) CurrentPretendGear = Max(1, currentGear - 1); processedAccelSampleStopped = true; bAccelSampleStopped = false; } if (channelUsed) { SampleManager.SetChannelEmittingVolume(m_nActiveSamples, 120); SampleManager.SetChannel3DPosition(m_nActiveSamples, pos.x, pos.y, pos.z); SampleManager.SetChannel3DDistances(m_nActiveSamples, 50.0f, 12.5f); freq = (GearFreqAdj[CurrentPretendGear] + freqModifier + 22050) ; if (engineSoundType == SFX_BANK_TRUCK) freq /= 2; SampleManager.SetChannelFrequency(m_nActiveSamples, freq); if (!channelUsed) { SampleManager.SetChannelReverbFlag(m_nActiveSamples, m_bDynamicAcousticModelingStatus != false); SampleManager.StartChannel(m_nActiveSamples); } } else if (processedAccelSampleStopped) { gearSoundStartTime = CTimer::GetTimeInMilliseconds(); params.m_pVehicle->bAudioChangingGear = true; if (!SampleManager.InitialiseChannel(m_nActiveSamples, soundOffset + SFX_CAR_ACCEL_1, SFX_BANK_0)) return; SampleManager.SetChannelLoopCount(m_nActiveSamples, 1); SampleManager.SetChannelLoopPoints(m_nActiveSamples, 0, -1); SampleManager.SetChannelEmittingVolume(m_nActiveSamples, 120); SampleManager.SetChannel3DPosition(m_nActiveSamples, pos.x, pos.y, pos.z); SampleManager.SetChannel3DDistances(m_nActiveSamples, 50.0f, 12.5f); freq = (GearFreqAdj[CurrentPretendGear] + freqModifier + 22050); if (engineSoundType == SFX_BANK_TRUCK) freq /= 2; SampleManager.SetChannelFrequency(m_nActiveSamples, freq); if (!channelUsed) { SampleManager.SetChannelReverbFlag(m_nActiveSamples, m_bDynamicAcousticModelingStatus != false); SampleManager.StartChannel(m_nActiveSamples); } } else if (CurrentPretendGear < params.m_pTransmission->nNumberOfGears - 1) { ++CurrentPretendGear; gearSoundStartTime = CTimer::GetTimeInMilliseconds(); params.m_pVehicle->bAudioChangingGear = true; if (!SampleManager.InitialiseChannel(m_nActiveSamples, soundOffset + SFX_CAR_ACCEL_1, SFX_BANK_0)) return; SampleManager.SetChannelLoopCount(m_nActiveSamples, 1); SampleManager.SetChannelLoopPoints(m_nActiveSamples, 0, -1); SampleManager.SetChannelEmittingVolume(m_nActiveSamples, 120); SampleManager.SetChannel3DPosition(m_nActiveSamples, pos.x, pos.y, pos.z); SampleManager.SetChannel3DDistances(m_nActiveSamples, 50.0f, 12.5f); freq = (GearFreqAdj[CurrentPretendGear] + freqModifier + 22050); if (engineSoundType == SFX_BANK_TRUCK) freq /= 2; SampleManager.SetChannelFrequency(m_nActiveSamples, freq); if (!channelUsed) { SampleManager.SetChannelReverbFlag(m_nActiveSamples, m_bDynamicAcousticModelingStatus != false); SampleManager.StartChannel(m_nActiveSamples); } } else { nCruising = 1; goto PlayCruising; } } } else { PlayCruising: bAccelSampleStopped = true; SampleManager.StopChannel(m_nActiveSamples); if (isMoped || accelerateState >= 150 && wheelsOnGround && brakeState <= 0 && !params.m_pVehicle->bIsHandbrakeOn && !lostTraction && currentGear >= params.m_pTransmission->nNumberOfGears - 1) { if (accelerateState >= 220 && params.m_fVelocityChange + 0.001f >= velocityChangeForAudio) { if (nCruising < 800) ++nCruising; } else if (nCruising > 3) { --nCruising; } freq = 27 * nCruising + freqModifier + 22050; if (engineSoundType == SFX_BANK_TRUCK) freq /= 2; AddPlayerCarSample(120, freq, soundOffset + SFX_CAR_AFTER_ACCEL_1, engineSoundType, 64, true); } else { nCruising = 0; } } } LastAccel = accelerateState; bHandbrakeOnLastFrame = params.m_pVehicle->bIsHandbrakeOn; bLostTractionLastFrame = lostTraction; return; } bool cAudioManager::ProcessVehicleSkidding(cVehicleParams& params) { const float SOUND_INTENSITY = 40.0f; CAutomobile *automobile; CBike *bike; uint8 numWheels; uint8 wheelsOnGround; float gasPedalAudio; tWheelState* wheelStateArr; cTransmission *transmission; int32 emittingVol; float newSkidVal = 0.0f; float skidVal = 0.0f; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; switch (params.m_VehicleType) { case VEHICLE_TYPE_CAR: automobile = (CAutomobile*)params.m_pVehicle; numWheels = 4; wheelStateArr = automobile->m_aWheelState; wheelsOnGround = automobile->m_nWheelsOnGround; gasPedalAudio = automobile->m_fGasPedalAudio; break; case VEHICLE_TYPE_BIKE: bike = (CBike*)params.m_pVehicle; numWheels = 2; wheelStateArr = bike->m_aWheelState; wheelsOnGround = bike->m_nWheelsOnGround; gasPedalAudio = bike->m_fGasPedalAudio; break; default: debug("\n * AUDIOLOG: ProcessVehicleSkidding() Unsupported vehicle type %d * \n", params.m_VehicleType); return true; } if (wheelsOnGround == 0) return true; CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); for (int32 i = 0; i < numWheels; i++) { if (wheelStateArr[i] == WHEEL_STATE_NORMAL) continue; transmission = params.m_pTransmission; switch (transmission->nDriveType) { case '4': newSkidVal = GetVehicleDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], gasPedalAudio, transmission, params.m_fVelocityChange); break; case 'F': if (i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT) newSkidVal = GetVehicleDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], gasPedalAudio, transmission, params.m_fVelocityChange); else newSkidVal = GetVehicleNonDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], transmission, params.m_fVelocityChange); break; case 'R': if (i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT) newSkidVal = GetVehicleDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], gasPedalAudio, transmission, params.m_fVelocityChange); else newSkidVal = GetVehicleNonDriveWheelSkidValue(params.m_pVehicle, wheelStateArr[i], transmission, params.m_fVelocityChange); break; default: break; } skidVal = Max(skidVal, newSkidVal); } if (skidVal > 0.0f) { emittingVol = 50.f * skidVal; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 3; switch (params.m_pVehicle->m_nSurfaceTouched) { case SURFACE_GRASS: case SURFACE_HEDGE: m_sQueueSample.m_nSampleIndex = SFX_RAIN; emittingVol /= 4; m_sQueueSample.m_nFrequency = 13000.f * skidVal + 35000.f; m_sQueueSample.m_nVolume /= 4; if (m_sQueueSample.m_nVolume == 0) return true; break; case SURFACE_GRAVEL: case SURFACE_MUD_DRY: case SURFACE_SAND: case SURFACE_WATER: case SURFACE_SAND_BEACH: m_sQueueSample.m_nSampleIndex = SFX_GRAVEL_SKID; m_sQueueSample.m_nFrequency = 6000.f * skidVal + 10000.f; break; default: m_sQueueSample.m_nSampleIndex = SFX_SKID; m_sQueueSample.m_nFrequency = 5000.f * skidVal + 11000.f; if (params.m_VehicleType == VEHICLE_TYPE_BIKE) m_sQueueSample.m_nFrequency += 2000; break; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 8; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } return true; } float cAudioManager::GetVehicleDriveWheelSkidValue(CVehicle *veh, tWheelState wheelState, float gasPedalAudio, cTransmission *transmission, float velocityChange) { float relativeVelChange = 0.0f; float velChange; float relativeVel; switch (wheelState) { case WHEEL_STATE_SPINNING: if (gasPedalAudio > 0.4f) relativeVelChange = (gasPedalAudio - 0.4f) * (5.0f / 3.0f) * 0.75f; break; case WHEEL_STATE_SKIDDING: relativeVelChange = Min(1.0f, Abs(velocityChange) / transmission->fMaxVelocity); break; case WHEEL_STATE_FIXED: relativeVel = gasPedalAudio; if (relativeVel > 0.4f) relativeVel = (gasPedalAudio - 0.4f) * (5.0f / 3.0f); velChange = Abs(velocityChange); if (velChange > 0.04f) relativeVelChange = Min(1.0f, velChange / transmission->fMaxVelocity); if (relativeVel > relativeVelChange) relativeVelChange = relativeVel; break; default: break; } return Max(relativeVelChange, Min(1.0f, Abs(veh->m_vecTurnSpeed.z) * 20.0f)); } float cAudioManager::GetVehicleNonDriveWheelSkidValue(CVehicle *veh, tWheelState wheelState, cTransmission *transmission, float velocityChange) { float relativeVelChange = 0.0f; if (wheelState == WHEEL_STATE_SKIDDING) relativeVelChange = Min(1.0f, Abs(velocityChange) / transmission->fMaxVelocity); return Max(relativeVelChange, Min(1.0f, Abs(veh->m_vecTurnSpeed.z) * 20.0f)); } bool cAudioManager::ProcessVehicleHorn(cVehicleParams& params) { const float SOUND_INTENSITY = 40.0f; CVehicle *veh; uint8 volume; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; veh = params.m_pVehicle; if (veh->m_bSirenOrAlarm && UsesSirenSwitching(params)) return true; if (veh->m_modelIndex == MI_MRWHOOP) return true; if (veh->IsAlarmOn()) return true; if (veh->m_nCarHornTimer != 0) { if (veh->GetStatus() != STATUS_PLAYER) { veh->m_nCarHornTimer = Min(44, veh->m_nCarHornTimer); if (veh->m_nCarHornTimer == 44) veh->m_nCarHornPattern = (m_FrameCounter + m_sQueueSample.m_nEntityIndex) & 7; if (!hornPatternsArray[veh->m_nCarHornPattern][44 - veh->m_nCarHornTimer]) return true; } CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); volume = veh->bIsDrowning ? 20 : 80; m_sQueueSample.m_nVolume = ComputeVolume(volume, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 4; m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nHornSample; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_nFrequency = aVehicleSettings[params.m_nIndex].m_nHornFrequency; m_sQueueSample.m_nLoopCount = 0; #ifdef FIX_BUGS m_sQueueSample.m_nEmittingVolume = volume; #else m_sQueueSample.m_nEmittingVolume = 80; #endif m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 5.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 4; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } return true; } bool cAudioManager::UsesSiren(cVehicleParams& params) const { return params.m_pVehicle->UsesSiren(); } bool cAudioManager::UsesSirenSwitching(cVehicleParams& params) const { if (params.m_nIndex == FIRETRUK || params.m_nIndex == MRWHOOP) return false; return UsesSiren(params); } bool cAudioManager::ProcessVehicleSirenOrAlarm(cVehicleParams& params) { const float SOUND_INTENSITY = 110.0f; CVehicle *veh; uint8 volume; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; veh = params.m_pVehicle; if (!veh->m_bSirenOrAlarm && !veh->IsAlarmOn()) return true; if (veh->IsAlarmOn()) { if (CTimer::GetTimeInMilliseconds() > veh->m_nCarHornTimer) veh->m_nCarHornTimer = CTimer::GetTimeInMilliseconds() + 750; if (veh->m_nCarHornTimer < CTimer::GetTimeInMilliseconds() + 375) return true; } CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); volume = veh->bIsDrowning ? 20 : 80; m_sQueueSample.m_nVolume = ComputeVolume(volume, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 5; if (UsesSiren(params)) { if (params.m_pVehicle->GetStatus() == STATUS_ABANDONED) return true; if (veh->m_nCarHornTimer != 0 && params.m_nIndex != FIRETRUK && params.m_nIndex != MRWHOOP) { m_sQueueSample.m_nSampleIndex = SFX_SIREN_FAST; if (params.m_nIndex == FBIRANCH) m_sQueueSample.m_nFrequency = 12668; else m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SIREN_FAST); m_sQueueSample.m_nCounter = 60; } else if (params.m_nIndex == VICECHEE) { m_sQueueSample.m_nSampleIndex = SFX_POLICE_SIREN_SLOW; m_sQueueSample.m_nFrequency = 11440; } else { m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nSirenOrAlarmSample; m_sQueueSample.m_nFrequency = aVehicleSettings[params.m_nIndex].m_nSirenOrAlarmFrequency; } } else { m_sQueueSample.m_nSampleIndex = aVehicleSettings[params.m_nIndex].m_nHornSample; m_sQueueSample.m_nFrequency = aVehicleSettings[params.m_nIndex].m_nHornFrequency; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = volume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 7.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } return true; } bool cAudioManager::UsesReverseWarning(int32 model) const { return model == LINERUN || model == FIRETRUK || model == BUS || model == COACH || model == PACKER || model == FLATBED; } bool cAudioManager::ProcessVehicleReverseWarning(cVehicleParams& params) { const float SOUND_INTENSITY = 50.0f; CVehicle *veh = params.m_pVehicle; uint8 volume; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; if (veh->bEngineOn && veh->m_fGasPedal < 0.0f) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); volume = veh->bIsDrowning ? 15 : 60; m_sQueueSample.m_nVolume = ComputeVolume(volume, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 12; m_sQueueSample.m_nSampleIndex = SFX_REVERSE_WARNING; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_nFrequency = (100 * m_sQueueSample.m_nEntityIndex & 1023) + SampleManager.GetSampleBaseFrequency(SFX_REVERSE_WARNING); m_sQueueSample.m_nLoopCount = 0; #ifdef FIX_BUGS m_sQueueSample.m_nEmittingVolume = volume; #else m_sQueueSample.m_nEmittingVolume = 60; #endif m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } return true; } bool cAudioManager::ProcessVehicleDoors(cVehicleParams& params) { const float SOUND_INTENSITY = 40.0f; CAutomobile *automobile; int8 doorState; int32 emittingVol; float velocity; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; automobile = (CAutomobile *)params.m_pVehicle; CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); for (int32 i = 0; i < ARRAY_SIZE(automobile->Doors); i++) { if (automobile->Damage.GetDoorStatus(i) == DOOR_STATUS_SWINGING) { doorState = automobile->Doors[i].m_nDoorState; if (doorState == DOORST_OPEN || doorState == DOORST_CLOSED) { velocity = Min(0.3f, Abs(automobile->Doors[i].m_fAngVel)); if (velocity > 0.0035f) { emittingVol = (100.0f * velocity * 10.0f / 3.0f); m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = i + 6; m_sQueueSample.m_nSampleIndex = m_anRandomTable[1] % 6 + SFX_COL_CAR_PANEL_1; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex) + RandomDisplacement(1000); m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 10; m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_fSpeedMultiplier = 1.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = true; AddSampleToRequestedQueue(); } } } } } return true; } bool cAudioManager::ProcessAirBrakes(cVehicleParams& params) { const float SOUND_INTENSITY = 30.0f; CAutomobile *automobile; uint8 volume; if (params.m_fDistance > SQR(SOUND_INTENSITY)) return false; automobile = (CAutomobile *)params.m_pVehicle; if (!automobile->bEngineOn) return true; if ((automobile->m_fVelocityChangeForAudio < 0.025f || params.m_fVelocityChange >= 0.025f) && (automobile->m_fVelocityChangeForAudio > -0.025f || params.m_fVelocityChange <= 0.025f)) return true; CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); volume = m_anRandomTable[0] % 10 + 70; m_sQueueSample.m_nVolume = ComputeVolume(volume, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 13; m_sQueueSample.m_nSampleIndex = SFX_AIR_BRAKES; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_AIR_BRAKES); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 10; m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nEmittingVolume = volume; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } return true; } bool cAudioManager::HasAirBrakes(int32 model) const { return model == LINERUN || model == FIRETRUK || model == TRASH || model == BUS || model == BARRACKS || model == COACH || model == PACKER || model == FLATBED; } bool cAudioManager::ProcessEngineDamage(cVehicleParams& params) { const float SOUND_INTENSITY = 40.0f; float health; uint8 emittingVolume; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; if (params.m_pVehicle->m_modelIndex == MI_CADDY) return true; if (params.m_pVehicle->GetStatus() == STATUS_WRECKED) return true; health = params.m_pVehicle->m_fHealth; if (health < 390.0f) { if (health < 250.0f) { emittingVolume = 60; m_sQueueSample.m_nSampleIndex = SFX_CAR_ON_FIRE; m_sQueueSample.m_nReleasingVolumeModificator = 7; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_ON_FIRE); } else { emittingVolume = 30; m_sQueueSample.m_nSampleIndex = SFX_PALM_TREE_LO; m_sQueueSample.m_nReleasingVolumeModificator = 7; m_sQueueSample.m_nFrequency = 27000; } CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); if (params.m_pVehicle->bIsDrowning) emittingVolume /= 2; m_sQueueSample.m_nVolume = ComputeVolume(emittingVolume, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 28; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVolume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } return true; } bool cAudioManager::ProcessCarBombTick(cVehicleParams& params) { const float SOUND_INTENSITY = 40.0f; const uint8 EMITTING_VOLUME = 60; uint8 bombType; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; if (params.m_pVehicle->bEngineOn) { switch (params.m_VehicleType) { case VEHICLE_TYPE_CAR: bombType = params.m_pVehicle->m_bombType; break; case VEHICLE_TYPE_BIKE: bombType = params.m_pVehicle->m_bombType; break; default: debug("\n * AUDIOLOG: ProcessCarBombTick() Unsupported vehicle type %d * \n", params.m_VehicleType); return true; break; } if (bombType == CARBOMB_TIMEDACTIVE) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(EMITTING_VOLUME, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 35; m_sQueueSample.m_nSampleIndex = SFX_COUNTDOWN; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_COUNTDOWN); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = EMITTING_VOLUME; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } return true; } void cAudioManager::ProcessVehicleOneShots(cVehicleParams& params) { int16 event; uint8 emittingVol; float relVol; float vol; bool noReflections; bool isHeli; float maxDist; static uint8 GunIndex = 53; for (int i = 0; i < m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents; i++) { noReflections = false; isHeli = false; m_sQueueSample.m_bRequireReflection = false; event = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]; switch (event) { case SOUND_CAR_DOOR_CLOSE_BONNET: case SOUND_CAR_DOOR_CLOSE_BUMPER: case SOUND_CAR_DOOR_CLOSE_FRONT_LEFT: case SOUND_CAR_DOOR_CLOSE_FRONT_RIGHT: case SOUND_CAR_DOOR_CLOSE_BACK_LEFT: case SOUND_CAR_DOOR_CLOSE_BACK_RIGHT: { const float SOUND_INTENSITY = 50.0f; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[2] % 5 + 122; switch (aVehicleSettings[params.m_nIndex].m_bDoorType) { case OLD_DOOR: m_sQueueSample.m_nSampleIndex = SFX_OLD_CAR_DOOR_CLOSE; break; case NEW_DOOR: m_sQueueSample.m_nSampleIndex = SFX_NEW_CAR_DOOR_CLOSE; break; case TRUCK_DOOR: m_sQueueSample.m_nSampleIndex = SFX_TRUCK_DOOR_CLOSE; break; case BUS_DOOR: m_sQueueSample.m_nSampleIndex = SFX_AIR_BRAKES; break; default: m_sQueueSample.m_nSampleIndex = SFX_NEW_CAR_DOOR_CLOSE; break; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; #ifdef THIS_IS_STUPID m_sQueueSample.m_nCounter = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] + 22; #else m_sQueueSample.m_nCounter = event + 22; #endif if (params.m_pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI) m_sQueueSample.m_nFrequency = 28062; else m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bRequireReflection = true; break; } case SOUND_CAR_DOOR_OPEN_BONNET: case SOUND_CAR_DOOR_OPEN_BUMPER: case SOUND_CAR_DOOR_OPEN_FRONT_LEFT: case SOUND_CAR_DOOR_OPEN_FRONT_RIGHT: case SOUND_CAR_DOOR_OPEN_BACK_LEFT: case SOUND_CAR_DOOR_OPEN_BACK_RIGHT: { const float SOUND_INTENSITY = 50.0f; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[1] % 10 + 117; switch (aVehicleSettings[params.m_nIndex].m_bDoorType) { case OLD_DOOR: m_sQueueSample.m_nSampleIndex = SFX_OLD_CAR_DOOR_OPEN; break; case TRUCK_DOOR: m_sQueueSample.m_nSampleIndex = SFX_TRUCK_DOOR_OPEN; break; case BUS_DOOR: m_sQueueSample.m_nSampleIndex = SFX_AIR_BRAKES; break; default: m_sQueueSample.m_nSampleIndex = SFX_NEW_CAR_DOOR_OPEN; break; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; #ifdef THIS_IS_STUPID m_sQueueSample.m_nCounter = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] + 10; #else m_sQueueSample.m_nCounter = event + 10; #endif if (params.m_pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI) m_sQueueSample.m_nFrequency = 23459; else m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bRequireReflection = true; break; } case SOUND_CAR_WINDSHIELD_CRACK: { const float SOUND_INTENSITY = 40.0f; maxDist = SQR(SOUND_INTENSITY); m_sQueueSample.m_nSampleIndex = SFX_GLASS_CRACK; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 68; emittingVol = m_anRandomTable[1] % 30 + 80; //GetRandomNumberInRange(1, 80, 109) m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GLASS_CRACK); m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; } break; case SOUND_CAR_JUMP: case SOUND_CAR_JUMP_2: { const float SOUND_INTENSITY = 35.0f; static uint8 WheelIndex = 82; maxDist = SQR(SOUND_INTENSITY); #ifdef THIS_IS_STUPID if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] == SOUND_CAR_JUMP_2) { #else if (event == SOUND_CAR_JUMP_2) { #endif m_sQueueSample.m_nSampleIndex = SFX_TYRE_BURST_B; emittingVol = Max(50.0f, 2 * (60.0f * m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i])); } else { m_sQueueSample.m_nSampleIndex = SFX_TYRE_BUMP; emittingVol = Max(80.f, 2 * (100.0f * m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i])); } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = WheelIndex++; if (WheelIndex > 85) WheelIndex = 82; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TYRE_BUMP); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); if (params.m_VehicleType == VEHICLE_TYPE_BIKE) m_sQueueSample.m_nFrequency *= 2; m_sQueueSample.m_nReleasingVolumeModificator = 6; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; break; } case SOUND_CAR_TYRE_POP: { const float SOUND_INTENSITY = 60.0f; static uint8 WheelIndex = 91; m_sQueueSample.m_nSampleIndex = SFX_TYRE_BURST; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = WheelIndex++; if (WheelIndex > 94) WheelIndex = 91; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TYRE_BURST); m_sQueueSample.m_nFrequency += RandomDisplacement(2000); m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[4] % 10 + 117; break; } case SOUND_CAR_ENGINE_START: { const float SOUND_INTENSITY = 40.0f; if (params.m_pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR || params.m_pVehicle->m_modelIndex == MI_CADDY) continue; emittingVol = 60; maxDist = SQR(SOUND_INTENSITY); m_sQueueSample.m_nSampleIndex = SFX_CAR_STARTER; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 33; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_STARTER); m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bRequireReflection = true; break; } case SOUND_CAR_LIGHT_BREAK: { const float SOUND_INTENSITY = 30.0f; m_sQueueSample.m_nSampleIndex = SFX_GLASS_SHARD_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 37; m_sQueueSample.m_nFrequency = 9 * SampleManager.GetSampleBaseFrequency(SFX_GLASS_SHARD_1) / 10; m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 8); m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[4] % 10 + 30; break; } case SOUND_CAR_HYDRAULIC_1: case SOUND_CAR_HYDRAULIC_2: { const float SOUND_INTENSITY = 35.0f; if (event == SOUND_CAR_HYDRAULIC_1) m_sQueueSample.m_nFrequency = 15600; else m_sQueueSample.m_nFrequency = 13118; m_sQueueSample.m_nSampleIndex = SFX_SUSPENSION_FAST_MOVE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 51; m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 8); m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[0] % 15 + 55; break; } case SOUND_CAR_HYDRAULIC_3: { const float SOUND_INTENSITY = 35.0f; m_sQueueSample.m_nSampleIndex = SFX_SUSPENSION_SLOW_MOVE_LOOP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 86; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SUSPENSION_SLOW_MOVE_LOOP); m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_nReleasingVolumeDivider = 7; noReflections = true; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[0] % 15 + 55; break; } case SOUND_CAR_JERK: { const float SOUND_INTENSITY = 35.0f; m_sQueueSample.m_nSampleIndex = SFX_SHAG_SUSPENSION; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 87; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SHAG_SUSPENSION); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 8); m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[1] % 15 + 55; break; } case SOUND_CAR_SPLASH: { const float SOUND_INTENSITY = 60.0f; static uint8 WaveIndex = 41; vol = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; if (vol <= 150.0f) continue; if (vol > 800.0f) m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] = 800.0f; relVol = (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] - 150.0f) / 650.0f; m_sQueueSample.m_nSampleIndex = (m_anRandomTable[0] & 1) + SFX_BOAT_SPLASH_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = WaveIndex++; if (WaveIndex > 46) WaveIndex = 41; m_sQueueSample.m_nFrequency = (7000.0f * relVol) + 6000; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; emittingVol = (35.0f * relVol); maxDist = SQR(SOUND_INTENSITY); break; } /*case SOUND_17: { const float SOUND_INTENSITY = 50.0f; m_sQueueSample.m_nSampleIndex = SFX_POLICE_BOAT_THUMB_OFF; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 47; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_POLICE_BOAT_THUMB_OFF) + RandomDisplacement(600); m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; emittingVol = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; maxDist = SQR(SOUND_INTENSITY); break; }*/ #ifdef GTA_TRAIN case SOUND_TRAIN_DOOR_CLOSE: case SOUND_TRAIN_DOOR_OPEN: { const float SOUND_INTENSITY = 35.0f; m_sQueueSample.m_nSampleIndex = SFX_AIR_BRAKES; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 59; m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 11025; m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_fSpeedMultiplier = 5.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[1] % 20 + 70; break; } #endif case SOUND_CAR_TANK_TURRET_ROTATE: { const float SOUND_INTENSITY = 40.0f; vol = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; if (vol > 24.0f / 625.0f) vol = 24.0f / 625.0f; m_sQueueSample.m_nSampleIndex = SFX_TANK_TURRET; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 79; m_sQueueSample.m_nFrequency = (3000.0f * vol * 625.0f / 24.0f) + 9000; m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; emittingVol = (37.0f * vol * 625.0f / 24.0f) + 90; maxDist = SQR(SOUND_INTENSITY); noReflections = true; break; } case SOUND_CAR_BOMB_TICK: { const float SOUND_INTENSITY = 30.0f; m_sQueueSample.m_nSampleIndex = SFX_BOMB_BEEP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 80; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BOMB_BEEP); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); m_sQueueSample.m_bRequireReflection = true; emittingVol = 60; break; } case SOUND_PLANE_ON_GROUND: { const float SOUND_INTENSITY = 180.0f; m_sQueueSample.m_nSampleIndex = SFX_JUMBO_LAND_WHEELS; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 81; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_JUMBO_LAND_WHEELS); m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[4] % 25 + 75; break; } case SOUND_HELI_BLADE:{ const float SOUND_INTENSITY = 35.0f; static uint8 HeliIndex = 89; relVol = ((CAutomobile*)params.m_pVehicle)->m_aWheelSpeed[1] * 50.0f / 11.0f; if (relVol < 0.2f || relVol == 1.0f) continue; emittingVol = (1.0f - relVol) * 70.0f; maxDist = SQR(SOUND_INTENSITY); m_sQueueSample.m_nSampleIndex = SFX_CAR_HELI_ROT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = HeliIndex++; if (HeliIndex > 90) HeliIndex = 89; m_sQueueSample.m_nFrequency = (8000.0f * relVol) + 16000; m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; break; } case SOUND_WEAPON_SHOT_FIRED: { const float SOUND_INTENSITY = 120.0f; CVehicle *playerVeh; CPlayerPed *playerPed; switch (params.m_pVehicle->m_modelIndex) { case MI_HUNTER: case MI_CHOPPER: case MI_SEASPAR: case MI_SPARROW: case MI_MAVERICK: case MI_VCNMAV: if (params.m_pVehicle->m_modelIndex == MI_HUNTER) { if (Pads[0].GetHandBrake() == 0) { playerVeh = FindPlayerVehicle(); playerPed = FindPlayerPed(); if (playerVeh == nil && playerPed != nil) { if (playerPed->m_attachedTo != nil && playerPed->m_attachedTo->GetType() == ENTITY_TYPE_VEHICLE) playerVeh = (CVehicle*)playerPed->m_attachedTo; } if (playerVeh != params.m_pVehicle) { m_sQueueSample.m_nSampleIndex = SFX_M60_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; } else { m_sQueueSample.m_nSampleIndex = SFX_ROCKET_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; } } else { m_sQueueSample.m_nSampleIndex = SFX_M60_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; } } else { m_sQueueSample.m_nSampleIndex = SFX_M60_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; } maxDist = SQR(SOUND_INTENSITY); m_sQueueSample.m_nCounter = GunIndex++; emittingVol = MAX_VOLUME; if (GunIndex > 58) GunIndex = 53; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_M60_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bRequireReflection = true; isHeli = true; break; default: { maxDist = SQR(SOUND_INTENSITY); #ifdef FIX_BUGS int32 sampleIndex; int32 frequency; CPed *pPed = params.m_pVehicle->pDriver; if(!pPed) break; if(!pPed->HasWeaponSlot(WEAPONSLOT_SUBMACHINEGUN)) { sampleIndex = SFX_UZI_LEFT; frequency = SampleManager.GetSampleBaseFrequency(sampleIndex); frequency += RandomDisplacement(frequency / 32); } else switch(pPed->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_eWeaponType) { case WEAPONTYPE_TEC9: sampleIndex = SFX_TEC_LEFT; frequency = RandomDisplacement(500) + 17000; break; case WEAPONTYPE_SILENCED_INGRAM: sampleIndex = SFX_TEC_LEFT; frequency = RandomDisplacement(1000) + 34000; break; case WEAPONTYPE_MP5: sampleIndex = SFX_MP5_LEFT; frequency = SampleManager.GetSampleBaseFrequency(sampleIndex); frequency += RandomDisplacement(frequency / 32); break; default: sampleIndex = SFX_UZI_LEFT; frequency = SampleManager.GetSampleBaseFrequency(sampleIndex); frequency += RandomDisplacement(frequency / 32); break; } m_sQueueSample.m_nSampleIndex = sampleIndex; #else m_sQueueSample.m_nSampleIndex = SFX_UZI_LEFT; #endif m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = GunIndex++; emittingVol = m_anRandomTable[2] % 15 + 65; if(GunIndex > 58) GunIndex = 53; #ifdef FIX_BUGS m_sQueueSample.m_nFrequency = frequency; #else m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_UZI_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); #endif m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bRequireReflection = true; break; } } break; } case SOUND_WEAPON_HIT_VEHICLE: { const float SOUND_INTENSITY = 40.0f; m_sQueueSample.m_nSampleIndex = m_anRandomTable[m_sQueueSample.m_nEntityIndex % ARRAY_SIZE(m_anRandomTable)] % 6 + SFX_BULLET_CAR_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 34; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 7; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[3] % 20 + 90; break; } case SOUND_BOMB_TIMED_ACTIVATED: case SOUND_91: case SOUND_BOMB_ONIGNITION_ACTIVATED: case SOUND_BOMB_TICK: { const float SOUND_INTENSITY = 50.0f; m_sQueueSample.m_nSampleIndex = SFX_ARM_BOMB; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 36; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ARM_BOMB); m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bRequireReflection = true; emittingVol = 50; maxDist = SQR(SOUND_INTENSITY); break; } case SOUND_PED_HELI_PLAYER_FOUND: { cPedParams pedParams; pedParams.m_bDistanceCalculated = params.m_bDistanceCalculated; pedParams.m_fDistance = params.m_fDistance; SetupPedComments(pedParams, SOUND_PED_HELI_PLAYER_FOUND); continue; } /* case SOUND_PED_BODYCAST_HIT: pedParams.m_pPed = nil; pedParams.m_bDistanceCalculated = false; pedParams.m_fDistance = 0.0f; pedParams.m_bDistanceCalculated = params.m_bDistanceCalculated; pedParams.m_fDistance = params.m_fDistance; SetupPedComments(&pedParams, SOUND_PED_BODYCAST_HIT); continue; */ case SOUND_PED_VCPA_PLAYER_FOUND: { cPedParams pedParams; pedParams.m_bDistanceCalculated = params.m_bDistanceCalculated; pedParams.m_fDistance = params.m_fDistance; SetupPedComments(pedParams, SOUND_PED_VCPA_PLAYER_FOUND); continue; } case SOUND_WATER_FALL: { const float SOUND_INTENSITY = 40.0f; static uint32 WaterFallFrame = 0; if (m_FrameCounter <= WaterFallFrame) continue; WaterFallFrame = m_FrameCounter + 6; m_sQueueSample.m_nSampleIndex = SFX_SPLASH_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 15; m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 16000; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); m_sQueueSample.m_bRequireReflection = true; emittingVol = m_anRandomTable[4] % 20 + 90; break; } case SOUND_SPLATTER: { const float SOUND_INTENSITY = 40.0f; static uint8 CrunchOffset = 0; m_sQueueSample.m_nSampleIndex = CrunchOffset + SFX_PED_CRUNCH_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 48; m_sQueueSample.m_nFrequency = RandomDisplacement(6000) + 16000; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; ++CrunchOffset; maxDist = SQR(SOUND_INTENSITY); emittingVol = m_anRandomTable[4] % 20 + 55; CrunchOffset %= 2; m_sQueueSample.m_bRequireReflection = true; break; } case SOUND_CAR_PED_COLLISION: { const float SOUND_INTENSITY = 40.0f; vol = Min(20.0f, m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]); emittingVol = Min(127, (3 * (vol / 20.0f * 127.f)) / 2); if (emittingVol == 0) continue; m_sQueueSample.m_nSampleIndex = SFX_FIGHT_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 50; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; maxDist = SQR(SOUND_INTENSITY); break; } default: continue; } if (params.m_fDistance < maxDist) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { if (noReflections) { m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_bReleasingSoundFlag = false; } else { m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_bReleasingSoundFlag = true; } m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bReverbFlag = true; if (isHeli) { if (0.2f * m_sQueueSample.m_fSoundIntensity > m_sQueueSample.m_fDistance) { m_sQueueSample.m_bIs2D = true; m_sQueueSample.m_nOffset = 0; #ifdef THIS_IS_STUPID goto AddSample; #else AddSampleToRequestedQueue(); m_sQueueSample.m_nOffset = 127; m_sQueueSample.m_nSampleIndex++; m_sQueueSample.m_nCounter = GunIndex++; if (GunIndex > 58) GunIndex = 53; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); continue; #endif } isHeli = false; } m_sQueueSample.m_bIs2D = false; #ifdef THIS_IS_STUPID AddSample: AddSampleToRequestedQueue(); if (isHeli) { m_sQueueSample.m_nOffset = 127; m_sQueueSample.m_nSampleIndex++; m_sQueueSample.m_nCounter = GunIndex++; if (GunIndex > 58) GunIndex = 53; m_sQueueSample.m_bRequireReflection = 0; AddSampleToRequestedQueue(); } #else AddSampleToRequestedQueue(); #endif continue; } } } } #ifdef GTA_TRAIN bool cAudioManager::ProcessTrainNoise(cVehicleParams& params) { const float SOUND_INTENSITY = 300.0f; CTrain *train; uint8 emittingVol; float speedMultipler; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return false; if (params.m_fVelocityChange > 0.0f) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); train = (CTrain *)params.m_pVehicle; speedMultipler = Min(1.0f, train->m_fSpeed * 250.f / 51.f); emittingVol = (75.f * speedMultipler); if (train->m_fWagonPosition == 0.0f) { m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 32; m_sQueueSample.m_nSampleIndex = SFX_TRAIN_FAR; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TRAIN_FAR); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } const float SOUND_INTENSITY = 70.0f; if (params.m_fDistance < SQR(SOUND_INTENSITY)) { m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 33; m_sQueueSample.m_nSampleIndex = SFX_TRAIN_NEAR; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TRAIN_NEAR) + 100 * m_sQueueSample.m_nEntityIndex % 987; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } return true; } #endif bool cAudioManager::ProcessBoatEngine(cVehicleParams& params) { CBoat *boat; float padRelativeAccerate; bool isV12 = false; static int32 LastFreq = 2000; static int8 LastVol = 0; static const float intensity = 90.0f; if (params.m_fDistance < SQR(intensity)) { boat = (CBoat *)params.m_pVehicle; if(boat->GetStatus() == STATUS_WRECKED) return true; float freqModificator; float volModificator; int BaseVol; int BaseFreq; switch(boat->GetModelIndex()) { case MI_RIO: freqModificator = 490.0f; volModificator = 60.0f; BaseVol = 20; BaseFreq = 1888; break; case MI_PREDATOR: case MI_SQUALO: case MI_SPEEDER: case MI_COASTG: case MI_DINGHY: case MI_JETMAX: freqModificator = 6000.0f; volModificator = 60.0f; isV12 = true; BaseFreq = 9000; BaseVol = 20; break; case MI_REEFER: freqModificator = 715.0f; volModificator = 80.0f; BaseVol = 0; BaseFreq = 3775; break; case MI_TROPIC: case MI_MARQUIS: freqModificator = 463.0f; volModificator = 60.0f; BaseVol = 20; BaseFreq = 1782; break; default: return true; } bool bIsPlayerVeh; if(FindPlayerVehicle() == params.m_pVehicle) { float padAccelerate = Max(Pads[0].GetAccelerate(), Pads[0].GetBrake()); padRelativeAccerate = padAccelerate / 255.0f; bIsPlayerVeh = true; } else { padRelativeAccerate = Max(params.m_pVehicle->m_fGasPedal, params.m_pVehicle->m_fBrakePedal); bIsPlayerVeh = false; } int Freq = BaseFreq + (padRelativeAccerate * freqModificator); int Vol = BaseVol + (padRelativeAccerate * volModificator); if(!boat->bPropellerInWater) Freq = (9 * Freq) / 8; if(bIsPlayerVeh) { if(Freq > LastFreq) { if(isV12) Freq = Min(Freq, LastFreq + 100); else Freq = Min(Freq, LastFreq + 15); } else { if(isV12) Freq = Max(Freq, LastFreq - 100); else Freq = Max(Freq, LastFreq - 15); } if(Vol > LastVol) Vol = Min(Vol, LastVol + 3); else Vol = Max(Vol, LastVol - 3); } if (Vol > 0) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(Vol, intensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nFrequency = Freq; m_sQueueSample.m_nCounter = 40; if (isV12) m_sQueueSample.m_nSampleIndex = SFX_BOAT_V12_LOOP; else m_sQueueSample.m_nSampleIndex = SFX_BOAT_CRUISER_LOOP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = Vol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = intensity; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 7; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } if(boat->GetModelIndex() == MI_REEFER) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(80, intensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nFrequency = 6000; m_sQueueSample.m_nCounter = 39; m_sQueueSample.m_nSampleIndex = SFX_FISHING_BOAT_IDLE; m_sQueueSample.m_nFrequency += (m_sQueueSample.m_nEntityIndex * 65536) % 1000; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = 80; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = intensity; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 7; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } if(bIsPlayerVeh) { LastFreq = Freq; LastVol = Vol; } return true; } return false; } bool cAudioManager::ProcessBoatMovingOverWater(cVehicleParams& params) { float velocityChange; int32 vol; float multiplier; if (params.m_fDistance > SQR(50)) return false; velocityChange = Abs(params.m_fVelocityChange); if (velocityChange <= 0.0005f && ((CBoat*)params.m_pVehicle)->bBoatInWater) return true; velocityChange = Min(0.75f, velocityChange); multiplier = (velocityChange - 0.0005f) / (1499.0f / 2000.0f); CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); vol = (30.f * multiplier); m_sQueueSample.m_nVolume = ComputeVolume(vol, 50.f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 38; m_sQueueSample.m_nSampleIndex = SFX_BOAT_WATER_LOOP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nFrequency = (6050.f * multiplier) + 16000; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = vol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = 50.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 6; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } return true; } void cAudioManager::ProcessCarHeli(cVehicleParams& params) { const float SOUND_INTENSITY = 250.0f; CVehicle* playerVeh; CVehicle* veh; CAutomobile* automobile; CBoat* boat; uint8 emittingVol; int16 brakeState; int16 accelerateState; uint32 freq; float propellerSpeed; float freqModifier; //may be relate to angle with horison float cameraAngle; bool distanceCalculatedOld; float distanceOld; CVector vecPosOld; float volumeModifier;//TODO find better name bool hunterBool; static uint32 freqFrontPrev = 14287; static uint32 freqPropellerPrev = 7143; static uint32 freqSkimmerPrev = 14287; boat = nil; automobile = nil; hunterBool = false; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return; playerVeh = FindPlayerVehicle(); veh = params.m_pVehicle; if (playerVeh == veh) { accelerateState = Pads[0].GetAccelerate(); brakeState = Pads[0].GetBrake(); } else { accelerateState = veh->m_fGasPedal * 255.0f; brakeState = veh->m_fBrakePedal * 255.0f; } freqModifier = Abs(veh->GetUp().y); cameraAngle = (DotProduct(veh->m_matrix.GetForward(), TheCamera.GetForward()) + 1.0f) / 2.0f; if (veh->m_modelIndex == MI_SKIMMER) { boat = (CBoat*)veh; propellerSpeed = boat->m_fMovingSpeed * 50.0f / 11.0f; } else if (params.m_VehicleType == VEHICLE_TYPE_HELI) { propellerSpeed = 1.0f; } else { automobile = (CAutomobile*)veh; propellerSpeed = automobile->m_aWheelSpeed[1] * 50.0f / 11.0f; } if (propellerSpeed == 0.0f) return; propellerSpeed = Min(1.0f, propellerSpeed); CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); //sound on long distances if (m_sQueueSample.m_fDistance >= 40.0f) emittingVol = propellerSpeed * 75.0f; else if (m_sQueueSample.m_fDistance >= 25.0f) emittingVol = (m_sQueueSample.m_fDistance - 25.0f) * (75.0f * propellerSpeed) / 15.0f; else emittingVol = 0; if (emittingVol != 0) { m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 88; if (boat != nil) { m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_PRO3; m_sQueueSample.m_nBankIndex = SFX_BANK_0; if (accelerateState > 0 || brakeState > 0) m_sQueueSample.m_nFrequency = 4600 + Min(1.0f, (Max(accelerateState, brakeState) / 255.0f) * freqModifier) * 563; else m_sQueueSample.m_nFrequency = 3651 + Min(1.0f, freqModifier) * 949; } else { m_sQueueSample.m_nSampleIndex = SFX_HELI_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); } m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } if (params.m_fDistance >= SQR(140.0f)) return; if (propellerSpeed >= 0.4f) volumeModifier = (propellerSpeed - 0.4f) * 5.0f / 3.0f; else volumeModifier = 0.0f; if (!boat) { freq = Min(1300, 7000.0f * freqModifier); if (playerVeh == veh && (accelerateState > 0 || brakeState > 0) && freq < 1300)//unnesesary freqModifier alredy <= 1300 freq = 1300; if (veh->m_modelIndex == MI_HUNTER) hunterBool = true; } //sound from front of helicopter emittingVol = (1.0f - cameraAngle) * volumeModifier * 127.0f; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, 140.0f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 3; if (hunterBool) { m_sQueueSample.m_nSampleIndex = SFX_HELI_APACHE_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = (volumeModifier + 1.0f) * 16000 + freq; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = 140.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } else if (boat != nil) { m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_PRO1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; if (accelerateState > 0 || brakeState > 0) m_sQueueSample.m_nFrequency = 18000 + Min(1.0f, freqModifier * (Max(accelerateState, brakeState) / 255.0f)) * 2204; else m_sQueueSample.m_nFrequency = 14287 + Min(1.0f, freqModifier) * 3713; if (propellerSpeed < 1.0f) m_sQueueSample.m_nFrequency = (propellerSpeed + 1.0f) * (m_sQueueSample.m_nFrequency / 2.0f); m_sQueueSample.m_nFrequency = clamp2(m_sQueueSample.m_nFrequency, freqFrontPrev, 197); freqFrontPrev = m_sQueueSample.m_nFrequency; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = 140.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } else { m_sQueueSample.m_nSampleIndex = SFX_CAR_HELI_MAI; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = (volumeModifier + 1) * 16000 + freq; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = 140.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } //after accel rotor sound emittingVol = ((cameraAngle + 1.0f) * volumeModifier * 127.0f) / 2.0f; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, 140.0f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 1; if (hunterBool) { m_sQueueSample.m_nSampleIndex = SFX_HELI_APACHE_2; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = (volumeModifier + 1) * 16000 + freq; } else if (boat) { m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_PRO2; m_sQueueSample.m_nBankIndex = SFX_BANK_0; if (accelerateState > 0 || brakeState > 0) m_sQueueSample.m_nFrequency = 9000 + Min(1.0f, (Max(accelerateState, brakeState) / 255) * freqModifier) * 1102; else m_sQueueSample.m_nFrequency = 7143 + Min(1.0f, freqModifier) * 1857; if (propellerSpeed < 1.0f) m_sQueueSample.m_nFrequency = (propellerSpeed + 1) * (m_sQueueSample.m_nFrequency / 2); m_sQueueSample.m_nFrequency = clamp2(m_sQueueSample.m_nFrequency, freqPropellerPrev, 98); freqPropellerPrev = m_sQueueSample.m_nFrequency; } else { m_sQueueSample.m_nSampleIndex = SFX_CAR_HELI_MAI2; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = (volumeModifier + 1) * 16000 + freq; } m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = 140.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } //engine starting sound if (boat == nil && params.m_VehicleType != VEHICLE_TYPE_HELI && m_sQueueSample.m_fDistance < 30.0f) { //strange way to check if automobile != nil if (automobile->bEngineOn) { if (propellerSpeed < 1.0f) { emittingVol = (1.0f - propellerSpeed / 2.0f) * 70.0f; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, 30.0f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume) { if (hunterBool) { m_sQueueSample.m_nSampleIndex = SFX_HELI_APACHE_4; m_sQueueSample.m_nBankIndex = SFX_BANK_0; freq = 3000.0f * propellerSpeed + 30000; } else { m_sQueueSample.m_nSampleIndex = SFX_CAR_HELI_STA; m_sQueueSample.m_nBankIndex = SFX_BANK_0; freq = 3000.0f * propellerSpeed + 6000; } m_sQueueSample.m_nFrequency = freq; m_sQueueSample.m_nCounter = 12; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = 30.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 30; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } } if (boat) { if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIXED && m_sQueueSample.m_fDistance < 20.0f && propellerSpeed > 0.0f) { m_sQueueSample.m_nVolume = ComputeVolume(propellerSpeed * 100.0f, 20.0f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume) { if (accelerateState > 0 || brakeState > 0) m_sQueueSample.m_nFrequency = 18000 + Min(1.0f, (Max(accelerateState, brakeState) / 255.0f) * freqModifier) * 2204; else m_sQueueSample.m_nFrequency = 14287 + Min(1.0f, freqModifier) * 3713; if (propellerSpeed < 1.0f) m_sQueueSample.m_nFrequency = (propellerSpeed + 1) * (m_sQueueSample.m_nFrequency / 2.0f); m_sQueueSample.m_nFrequency = clamp2(m_sQueueSample.m_nFrequency, freqSkimmerPrev, 197); freqSkimmerPrev = m_sQueueSample.m_nFrequency; m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_PRO4; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 12; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = propellerSpeed * 100.0f; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_SEAPLANE_PRO4); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_SEAPLANE_PRO4); m_sQueueSample.m_fSpeedMultiplier = 5.0f; m_sQueueSample.m_fSoundIntensity = 20.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 7; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } else { //vacuum cleaner sound vecPosOld = m_sQueueSample.m_vecPos; distanceCalculatedOld = params.m_bDistanceCalculated; distanceOld = params.m_fDistance; if (automobile != nil) automobile->GetComponentWorldPosition(CAR_BOOT, m_sQueueSample.m_vecPos); else if (params.m_VehicleType == VEHICLE_TYPE_HELI) m_sQueueSample.m_vecPos = CVector(0.0f, -10.0f, 0.0f); //this is from android, but for real it's not used params.m_bDistanceCalculated = false; params.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); if (params.m_fDistance < SQR(27.0f)) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(volumeModifier * 25.0f, 27.0f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume) { m_sQueueSample.m_nCounter = 2; m_sQueueSample.m_nSampleIndex = hunterBool ? SFX_HELI_APACHE_3 : SFX_CAR_HELI_REA; m_sQueueSample.m_nBankIndex = 0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = (volumeModifier + 1.0f) * 16000; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = volumeModifier * 25.0f; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 6.0f; m_sQueueSample.m_fSoundIntensity = 27.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } m_sQueueSample.m_vecPos = vecPosOld; params.m_bDistanceCalculated = distanceCalculatedOld; params.m_fDistance = distanceOld; } } void cAudioManager::ProcessVehicleFlatTyre(cVehicleParams& params) { const float SOUND_INTENSITY = 60.0f; CAutomobile* automobile; CBike* bike; bool wheelBurst; uint8 emittingVol; float modifier; if (params.m_fDistance >= SQR(SOUND_INTENSITY)) return; switch (params.m_VehicleType) { case VEHICLE_TYPE_CAR: automobile = (CAutomobile*)params.m_pVehicle; wheelBurst = false; for (int i = 0; i < 4; i++) if (automobile->Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST && automobile->m_aWheelTimer[i] > 0.0f) wheelBurst = true; if (!wheelBurst) return; break; case VEHICLE_TYPE_BIKE: bike = (CBike*)params.m_pVehicle; wheelBurst = false; for(int i = 0; i < 2; i++) if (bike->m_wheelStatus[i] == WHEEL_STATUS_BURST && bike->m_aWheelTimer[i] > 0.0f) wheelBurst = true; if (!wheelBurst) return; break; default: return; } modifier = Min(1.0f, Abs(params.m_fVelocityChange) / (0.3f * params.m_pTransmission->fMaxVelocity)); if (modifier > 0.01f) { //mb can be replaced by (emittingVol > 1) emittingVol = (100.0f * modifier); CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume) { m_sQueueSample.m_nCounter = 95; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_nSampleIndex = SFX_TYRE_BURST_L; m_sQueueSample.m_nFrequency = (5500.0f * modifier) + 8000; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_TYRE_BURST_L); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_TYRE_BURST_L); m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } //TODO use it in ProcessVehicle void cAudioManager::ProcessPlane(cVehicleParams& params) { switch (params.m_nIndex) { case AIRTRAIN: ProcessJumbo(params); break; case DEADDODO: ProcessCesna(params); break; default: break; } } #pragma region JUMBO uint8 gJumboVolOffsetPercentage; void DoJumboVolOffset() { if (!(AudioManager.GetFrameCounter() % (AudioManager.GetRandomNumber(0) % 6 + 3))) gJumboVolOffsetPercentage = AudioManager.GetRandomNumber(1) % 60; } void cAudioManager::ProcessJumbo(cVehicleParams& params) { CPlane *plane; float position; if (params.m_fDistance >= SQR(440)) return; CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); plane = (CPlane*)params.m_pVehicle; DoJumboVolOffset(); position = PlanePathPosition[plane->m_nPlaneId]; if (position <= TakeOffPoint) { if (plane->m_fSpeed > 0.103344f) { ProcessJumboAccel(plane); } else { ProcessJumboTaxi(); } } else if (position <= TakeOffPoint + 300.0f) { ProcessJumboTakeOff(plane); } else if (position <= LandingPoint - 350.0f) { ProcessJumboFlying(); } else if (position <= LandingPoint) { ProcessJumboLanding(plane); } else if (plane->m_fSpeed > 0.103344f) { ProcessJumboDecel(plane); } else { ProcessJumboTaxi(); } } void cAudioManager::ProcessJumboTaxi() { if (SetupJumboFlySound(20)) { if (SetupJumboTaxiSound(75)) SetupJumboWhineSound(18, 29500); } } void cAudioManager::ProcessJumboAccel(CPlane *plane) { int32 engineFreq; int32 vol; float modificator; float freqModifier; if (SetupJumboFlySound(20)) { modificator = Min(1.0f, (plane->m_fSpeed - 0.103344f) * 1.6760077f); if (SetupJumboRumbleSound(MAX_VOLUME * modificator) && SetupJumboTaxiSound((1.0f - modificator) * 75.f)) { if (modificator < 0.2f) { freqModifier = modificator * 5.0f; vol = MAX_VOLUME * freqModifier; engineFreq = 6050.0f * freqModifier + 16000; } else { freqModifier = 1.0f; engineFreq = 22050; vol = MAX_VOLUME; } SetupJumboEngineSound(vol, engineFreq); SetupJumboWhineSound(18, 14600.0f * freqModifier + 29500); } } } void cAudioManager::ProcessJumboTakeOff(CPlane *plane) { const float modificator = (PlanePathPosition[plane->m_nPlaneId] - TakeOffPoint) / 300.f; if (SetupJumboFlySound((107.f * modificator) + 20) && SetupJumboRumbleSound(MAX_VOLUME * (1.f - modificator))) { if (SetupJumboEngineSound(MAX_VOLUME, 22050)) SetupJumboWhineSound(18.f * (1.f - modificator), 44100); } } void cAudioManager::ProcessJumboFlying() { if (SetupJumboFlySound(MAX_VOLUME)) SetupJumboEngineSound(63, 22050); } void cAudioManager::ProcessJumboLanding(CPlane *plane) { const float modificator = (LandingPoint - PlanePathPosition[plane->m_nPlaneId]) / 350.f; if (SetupJumboFlySound(107.f * modificator + 20)) { if (SetupJumboTaxiSound(75.f * (1.f - modificator))) { SetupJumboEngineSound(MAX_VOLUME, 22050); SetupJumboWhineSound(18.f * (1.f - modificator), 14600.f * modificator + 29500); } } } void cAudioManager::ProcessJumboDecel(CPlane *plane) { if (SetupJumboFlySound(20) && SetupJumboTaxiSound(75)) { const float modificator = Min(1.f, (plane->m_fSpeed - 0.103344f) * 1.6760077f); SetupJumboEngineSound(MAX_VOLUME * modificator, 6050.f * modificator + 16000); SetupJumboWhineSound(18, 29500); } } bool cAudioManager::SetupJumboTaxiSound(uint8 vol) { const float SOUND_INTENSITY = 180.0f; if (m_sQueueSample.m_fDistance >= SOUND_INTENSITY) return false; uint8 emittingVol = (vol / 2) + ((vol / 2) * m_sQueueSample.m_fDistance / SOUND_INTENSITY); if (m_sQueueSample.m_fDistance / SOUND_INTENSITY < 0.7f) emittingVol -= emittingVol * gJumboVolOffsetPercentage / 100; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 1; m_sQueueSample.m_nSampleIndex = SFX_JUMBO_TAXI; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = GetJumboTaxiFreq(); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 4; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } return true; } bool cAudioManager::SetupJumboWhineSound(uint8 emittingVol, uint32 freq) { const float SOUND_INTENSITY = 170.0f; if (m_sQueueSample.m_fDistance >= SOUND_INTENSITY) return false; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 2; m_sQueueSample.m_nSampleIndex = SFX_JUMBO_WHINE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = freq; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 4; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } return true; } bool cAudioManager::SetupJumboEngineSound(uint8 vol, uint32 freq) { const float SOUND_INTENSITY = 180.0f; if (m_sQueueSample.m_fDistance >= SOUND_INTENSITY) return false; uint8 emittingVol = vol - gJumboVolOffsetPercentage / 100; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 3; m_sQueueSample.m_nSampleIndex = SFX_JUMBO_ENGINE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = freq; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 4; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } return true; } bool cAudioManager::SetupJumboFlySound(uint8 emittingVol) { const float SOUND_INTENSITY = 440.0f; if(m_sQueueSample.m_fDistance >= SOUND_INTENSITY) return false; int32 vol = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); m_sQueueSample.m_nVolume = vol; if(m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 0; m_sQueueSample.m_nSampleIndex = SFX_JUMBO_DIST_FLY; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_JUMBO_DIST_FLY); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; // todo port fix to re3 AddSampleToRequestedQueue(); } return true; } bool cAudioManager::SetupJumboRumbleSound(uint8 emittingVol) { const float SOUND_INTENSITY = 240.0f; if (m_sQueueSample.m_fDistance >= SOUND_INTENSITY) return false; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 5; m_sQueueSample.m_nSampleIndex = SFX_JUMBO_RUMBLE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = true; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_JUMBO_RUMBLE); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 12; m_sQueueSample.m_nOffset = 0; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); m_sQueueSample.m_nCounter = 6; m_sQueueSample.m_nSampleIndex = SFX_JUMBO_RUMBLE; m_sQueueSample.m_nFrequency += 200; m_sQueueSample.m_nOffset = MAX_VOLUME; AddSampleToRequestedQueue(); } return true; } int32 cAudioManager::GetJumboTaxiFreq() const { return (1.f / 180 * 10950 * m_sQueueSample.m_fDistance) + 22050; // todo port fix to re3 } #pragma endregion Some jumbo crap #pragma endregion All the vehicle audio code #pragma region PED AUDIO void cAudioManager::ProcessPed(CPhysical *ped) { cPedParams params; m_sQueueSample.m_vecPos = ped->GetPosition(); params.m_bDistanceCalculated = false; params.m_pPed = (CPed *)ped; params.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); ProcessPedOneShots(params); } void cAudioManager::ProcessPedOneShots(cPedParams ¶ms) { uint8 emittingVol; int32 sampleIndex; CPed *ped = params.m_pPed; bool narrowSoundRange; int16 sound; bool stereo; CWeapon *weapon; float maxDist = 0.f; // uninitialized variable static uint8 iSound = 21; static uint32 iSplashFrame = 0; weapon = params.m_pPed->GetWeapon(); for (uint32 i = 0; i < m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents; i++) { stereo = false; narrowSoundRange = false; m_sQueueSample.m_bRequireReflection = false; sound = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]; switch (sound) { case SOUND_STEP_START: case SOUND_STEP_END: if (params.m_pPed->bIsInTheAir) continue; emittingVol = m_anRandomTable[3] % 15 + 45; if (FindPlayerPed() != m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_pEntity) emittingVol /= 2; maxDist = 400.f; switch (params.m_pPed->m_nSurfaceTouched) { case SURFACE_GRASS: sampleIndex = m_anRandomTable[1] % 5 + SFX_FOOTSTEP_GRASS_1; break; case SURFACE_GRAVEL: case SURFACE_MUD_DRY: sampleIndex = m_anRandomTable[4] % 5 + SFX_FOOTSTEP_GRAVEL_1; break; case SURFACE_CAR: case SURFACE_GARAGE_DOOR: case SURFACE_CAR_PANEL: case SURFACE_THICK_METAL_PLATE: case SURFACE_SCAFFOLD_POLE: case SURFACE_LAMP_POST: case SURFACE_FIRE_HYDRANT: case SURFACE_GIRDER: case SURFACE_METAL_CHAIN_FENCE: case SURFACE_CONTAINER: case SURFACE_NEWS_VENDOR: sampleIndex = m_anRandomTable[0] % 5 + SFX_FOOTSTEP_METAL_1; break; case SURFACE_SAND: sampleIndex = (m_anRandomTable[4] & 3) + SFX_FOOTSTEP_SAND_1; break; case SURFACE_WATER: sampleIndex = (m_anRandomTable[3] & 3) + SFX_FOOTSTEP_WATER_1; break; case SURFACE_WOOD_CRATES: case SURFACE_WOOD_BENCH: case SURFACE_WOOD_SOLID: sampleIndex = m_anRandomTable[2] % 5 + SFX_FOOTSTEP_WOOD_1; break; case SURFACE_HEDGE: sampleIndex = m_anRandomTable[2] % 5 + SFX_COL_VEG_1; break; default: sampleIndex = m_anRandomTable[2] % 5 + SFX_FOOTSTEP_CONCRETE_1; break; } m_sQueueSample.m_nSampleIndex = sampleIndex; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] - SOUND_STEP_START + 1; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 17); switch (params.m_pPed->m_nMoveState) { case PEDMOVE_WALK: emittingVol /= 4; m_sQueueSample.m_nFrequency = 9 * m_sQueueSample.m_nFrequency / 10; break; case PEDMOVE_RUN: emittingVol /= 2; m_sQueueSample.m_nFrequency = 11 * m_sQueueSample.m_nFrequency / 10; break; case PEDMOVE_SPRINT: m_sQueueSample.m_nFrequency = 12 * m_sQueueSample.m_nFrequency / 10; break; default: break; } m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_fSpeedMultiplier = 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_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; case SOUND_FALL_LAND: case SOUND_FALL_COLLAPSE: if (ped->bIsInTheAir) continue; maxDist = SQR(30); emittingVol = m_anRandomTable[3] % 20 + 80; if (ped->m_nSurfaceTouched == SURFACE_WATER) { m_sQueueSample.m_nSampleIndex = (m_anRandomTable[3] & 3) + SFX_FOOTSTEP_WATER_1; } else if (sound == SOUND_FALL_LAND) { m_sQueueSample.m_nSampleIndex = SFX_BODY_LAND; } else { m_sQueueSample.m_nSampleIndex = SFX_BODY_LAND_AND_FALL; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 1; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 17); m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 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_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; case SOUND_FIGHT_37: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_1; m_sQueueSample.m_nFrequency = 18000; goto AddFightSound; case SOUND_FIGHT_38: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_1; m_sQueueSample.m_nFrequency = 16500; goto AddFightSound; case SOUND_FIGHT_39: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_1; m_sQueueSample.m_nFrequency = 20000; goto AddFightSound; case SOUND_FIGHT_40: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_2; m_sQueueSample.m_nFrequency = 18000; goto AddFightSound; case SOUND_FIGHT_41: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_2; m_sQueueSample.m_nFrequency = 16500; goto AddFightSound; case SOUND_FIGHT_42: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_2; m_sQueueSample.m_nFrequency = 20000; goto AddFightSound; case SOUND_FIGHT_43: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_4; m_sQueueSample.m_nFrequency = 18000; goto AddFightSound; case SOUND_FIGHT_44: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_4; m_sQueueSample.m_nFrequency = 16500; goto AddFightSound; case SOUND_FIGHT_45: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_4; m_sQueueSample.m_nFrequency = 20000; goto AddFightSound; case SOUND_FIGHT_46: case SOUND_187: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_5; m_sQueueSample.m_nFrequency = 18000; goto AddFightSound; case SOUND_FIGHT_47: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_5; m_sQueueSample.m_nFrequency = 16500; goto AddFightSound; case SOUND_FIGHT_48: m_sQueueSample.m_nSampleIndex = SFX_FIGHT_5; m_sQueueSample.m_nFrequency = 20000; AddFightSound: { uint32 soundParams = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; // wtf? storing int as float uint8 damagerType = soundParams & 0xFF; uint32 weaponType = soundParams >> 8; if (damagerType == ENTITY_TYPE_PED) { if (weaponType == WEAPONTYPE_BRASSKNUCKLE) { CPed* ped = params.m_pPed; uint32 fightMove = ped->m_curFightMove; if (fightMove == FIGHTMOVE_BACKLEFT || fightMove == FIGHTMOVE_STDPUNCH || fightMove == FIGHTMOVE_PUNCH || ped->m_nPedState == PED_ATTACK) { CEntity* damageEntity = ped->m_pDamageEntity; if (!damageEntity) m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 2 + SFX_HAMMER_HIT_1; else if (damageEntity->GetType() != ENTITY_TYPE_PED) m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 2 + SFX_HAMMER_HIT_1; else if (((CPed*)damageEntity)->m_curFightMove != FIGHTMOVE_HITHEAD) m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 2 + SFX_HAMMER_HIT_1; else m_sQueueSample.m_nSampleIndex = SFX_HAMMER_HIT_1; } } } else { m_sQueueSample.m_nSampleIndex = m_anRandomTable[4] % 6 + SFX_COL_CAR_PANEL_1; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); } } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound; narrowSoundRange = true; ++iSound; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 30.0f; maxDist = SQR(30); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; emittingVol = m_anRandomTable[3] % 26 + 100; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; case SOUND_WEAPON_BAT_ATTACK: case SOUND_WEAPON_KNIFE_ATTACK: { uint32 soundParams = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; // wtf? storing int as float uint8 damagerType = soundParams & 0xFF; uint32 weaponType = soundParams >> 8; if (damagerType == ENTITY_TYPE_PED) { switch (weaponType) { case WEAPONTYPE_SCREWDRIVER: case WEAPONTYPE_KNIFE: case WEAPONTYPE_CLEAVER: case WEAPONTYPE_MACHETE: case WEAPONTYPE_KATANA: if (sound == SOUND_WEAPON_KNIFE_ATTACK) m_sQueueSample.m_nSampleIndex = SFX_KNIFE_SLASH; else m_sQueueSample.m_nSampleIndex = SFX_KNIFE_STAB; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); break; case WEAPONTYPE_HAMMER: m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 2 + SFX_HAMMER_HIT_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); break; default: m_sQueueSample.m_nSampleIndex = SFX_BAT_HIT_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = RandomDisplacement(2000) + 22000; stereo = true; break; } } else { m_sQueueSample.m_nSampleIndex = m_anRandomTable[4] % 6 + SFX_COL_CAR_PANEL_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); } m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 30.0f; maxDist = SQR(30); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = m_anRandomTable[2] % 20 + 100; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; } case SOUND_WEAPON_CHAINSAW_ATTACK: if (FindVehicleOfPlayer()) continue; m_sQueueSample.m_nSampleIndex = SFX_CAR_ACCEL_13; m_sQueueSample.m_nBankIndex = SFX_BANK_0; // SFX_BANK_CAR_CHAINSAW m_sQueueSample.m_nCounter = 70; m_sQueueSample.m_nFrequency = 27000; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = 50.0f; maxDist = SQR(50); m_sQueueSample.m_nLoopCount = 0; emittingVol = 100; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_CAR_ACCEL_13); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_CAR_ACCEL_13); m_sQueueSample.m_nEmittingVolume = 100; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; break; case SOUND_WEAPON_CHAINSAW_IDLE: if (FindVehicleOfPlayer()) continue; m_sQueueSample.m_nSampleIndex = SFX_CAR_AFTER_ACCEL_13; m_sQueueSample.m_nBankIndex = SFX_BANK_0; // SFX_BANK_CAR_CHAINSAW m_sQueueSample.m_nCounter = 68; m_sQueueSample.m_nFrequency = 27000; m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = 60.0f; maxDist = SQR(60); m_sQueueSample.m_nLoopCount = 0; emittingVol = 100; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_CAR_AFTER_ACCEL_13); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_CAR_AFTER_ACCEL_13); m_sQueueSample.m_nEmittingVolume = 100; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; break; case SOUND_WEAPON_CHAINSAW_MADECONTACT: if (FindVehicleOfPlayer()) continue; if ((int32)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] != ENTITY_TYPE_PED) ReportCollision(params.m_pPed, params.m_pPed, SURFACE_CAR, SURFACE_TARMAC, 0.0f, 0.09f); m_sQueueSample.m_nSampleIndex = SFX_CAR_AFTER_ACCEL_13; m_sQueueSample.m_nBankIndex = SFX_BANK_0; // SFX_BANK_CAR_CHAINSAW m_sQueueSample.m_nCounter = 68; m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 22000; m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = 60.0f; maxDist = SQR(60); m_sQueueSample.m_nLoopCount = 0; emittingVol = 100; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_CAR_AFTER_ACCEL_13); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_CAR_AFTER_ACCEL_13); m_sQueueSample.m_nEmittingVolume = 100; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 5; break; case SOUND_WEAPON_SHOT_FIRED: weapon = ped->GetWeapon(); if (!weapon) continue; switch (weapon->m_eWeaponType) { case WEAPONTYPE_ROCKET: case WEAPONTYPE_ROCKETLAUNCHER: m_sQueueSample.m_nSampleIndex = SFX_ROCKET_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ROCKET_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = m_anRandomTable[0] % 20 + 80; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; stereo = true; break; case WEAPONTYPE_COLT45: m_sQueueSample.m_nSampleIndex = SFX_COLT45_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_COLT45_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = m_anRandomTable[1] % 10 + 90; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; stereo = true; break; case WEAPONTYPE_PYTHON: m_sQueueSample.m_nSampleIndex = SFX_PYTHON_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PYTHON_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = 127; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; stereo = true; break; case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: m_sQueueSample.m_nSampleIndex = SFX_SHOTGUN_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SHOTGUN_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = m_anRandomTable[2] % 10 + 100; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; stereo = true; break; case WEAPONTYPE_SPAS12_SHOTGUN: m_sQueueSample.m_nSampleIndex = SFX_SPAS12_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_SPAS12_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = m_anRandomTable[2] % 10 + 100; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; stereo = true; break; case WEAPONTYPE_TEC9: m_sQueueSample.m_nSampleIndex = SFX_TEC_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 17000; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; emittingVol = m_anRandomTable[3] % 15 + 70; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; stereo = true; break; case WEAPONTYPE_UZI: case WEAPONTYPE_MINIGUN: m_sQueueSample.m_nSampleIndex = SFX_UZI_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_UZI_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; emittingVol = m_anRandomTable[3] % 15 + 70; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; stereo = true; break; case WEAPONTYPE_SILENCED_INGRAM: m_sQueueSample.m_nSampleIndex = SFX_TEC_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 34000; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; emittingVol = m_anRandomTable[3] % 15 + 70; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; stereo = true; break; case WEAPONTYPE_MP5: m_sQueueSample.m_nSampleIndex = SFX_MP5_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_MP5_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; emittingVol = m_anRandomTable[3] % 15 + 70; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; stereo = true; break; case WEAPONTYPE_M4: m_sQueueSample.m_nSampleIndex = SFX_RUGER_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 43150; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; emittingVol = m_anRandomTable[3] % 15 + 90; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; stereo = true; break; case WEAPONTYPE_RUGER: m_sQueueSample.m_nSampleIndex = SFX_RUGER_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_RUGER_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; emittingVol = m_anRandomTable[3] % 15 + 90; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; stereo = true; break; case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: m_sQueueSample.m_nSampleIndex = SFX_SNIPER_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; if (weapon->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE) m_sQueueSample.m_nFrequency = 25472; else m_sQueueSample.m_nFrequency = 20182; m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = m_anRandomTable[4] % 10 + 110; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; stereo = true; break; case WEAPONTYPE_FLAMETHROWER: m_sQueueSample.m_nSampleIndex = SFX_FLAMETHROWER_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 9; emittingVol = 90; m_sQueueSample.m_nFrequency = (10 * m_sQueueSample.m_nEntityIndex & 2047) + SampleManager.GetSampleBaseFrequency(SFX_FLAMETHROWER_LEFT); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = 60.0f; maxDist = SQR(60); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nEmittingVolume = 90; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 6; stereo = true; break; case WEAPONTYPE_M60: case WEAPONTYPE_HELICANNON: m_sQueueSample.m_nSampleIndex = SFX_M60_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_M60_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = 127; m_sQueueSample.m_nEmittingVolume = 127; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; stereo = true; break; default: continue; } break; case SOUND_WEAPON_RELOAD: switch ((int32)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]) { case WEAPONTYPE_ROCKET: case WEAPONTYPE_ROCKETLAUNCHER: m_sQueueSample.m_nSampleIndex = SFX_ROCKET_RELOAD; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ROCKET_RELOAD); break; case WEAPONTYPE_COLT45: case WEAPONTYPE_PYTHON: m_sQueueSample.m_nSampleIndex = SFX_PISTOL_RELOAD; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PISTOL_RELOAD) + RandomDisplacement(300); break; case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_SPAS12_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: case WEAPONTYPE_RUGER: m_sQueueSample.m_nSampleIndex = SFX_AK47_RELOAD; m_sQueueSample.m_nFrequency = 30290; break; case WEAPONTYPE_TEC9: case WEAPONTYPE_UZI: case WEAPONTYPE_SILENCED_INGRAM: case WEAPONTYPE_MP5: case WEAPONTYPE_M4: case WEAPONTYPE_M60: case WEAPONTYPE_HELICANNON: m_sQueueSample.m_nSampleIndex = SFX_AK47_RELOAD; m_sQueueSample.m_nFrequency = 39243; break; case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: m_sQueueSample.m_nSampleIndex = SFX_RIFLE_RELOAD; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_RIFLE_RELOAD); break; default: continue; } emittingVol = 75; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency += RandomDisplacement(300); m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 30.0f; maxDist = SQR(30); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = 75; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; case SOUND_WEAPON_AK47_BULLET_ECHO: { uint32 weaponType = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; switch (weaponType) { case WEAPONTYPE_SPAS12_SHOTGUN: m_sQueueSample.m_nSampleIndex = SFX_SPAS12_TAIL_LEFT; break; case WEAPONTYPE_TEC9: case WEAPONTYPE_SILENCED_INGRAM: m_sQueueSample.m_nSampleIndex = SFX_TEC_TAIL; break; case WEAPONTYPE_UZI: case WEAPONTYPE_MP5: m_sQueueSample.m_nSampleIndex = SFX_UZI_END_LEFT; break; case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: m_sQueueSample.m_nSampleIndex = SFX_RUGER_TAIL; break; case WEAPONTYPE_M60: case WEAPONTYPE_HELICANNON: m_sQueueSample.m_nSampleIndex = SFX_M60_TAIL_LEFT; break; default: continue; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; switch (weaponType) { case WEAPONTYPE_TEC9: m_sQueueSample.m_nFrequency = 13000; break; case WEAPONTYPE_SILENCED_INGRAM: m_sQueueSample.m_nFrequency = 26000; break; case WEAPONTYPE_M4: m_sQueueSample.m_nFrequency = 15600; break; case WEAPONTYPE_SNIPERRIFLE: m_sQueueSample.m_nFrequency = 9959; break; case WEAPONTYPE_LASERSCOPE: m_sQueueSample.m_nFrequency = 7904; break; default: m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); break; } m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 120.0f; maxDist = SQR(120); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = m_anRandomTable[4] % 10 + 80; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; } case SOUND_WEAPON_FLAMETHROWER_FIRE: m_sQueueSample.m_nSampleIndex = SFX_FLAMETHROWER_START_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_FLAMETHROWER_START_LEFT); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = 60.0f; maxDist = SQR(60); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = 70; m_sQueueSample.m_nEmittingVolume = 70; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; break; case SOUND_WEAPON_HIT_PED: m_sQueueSample.m_nSampleIndex = SFX_BULLET_PED; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BULLET_PED); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 8); m_sQueueSample.m_nReleasingVolumeModificator = 7; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 30.0f; maxDist = SQR(30); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; emittingVol = m_anRandomTable[0] % 20 + 90; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; break; case SOUND_SPLASH: if (m_FrameCounter <= iSplashFrame) continue; iSplashFrame = m_FrameCounter + 6; m_sQueueSample.m_nSampleIndex = SFX_SPLASH_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = RandomDisplacement(1400) + 20000; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 40.0f; maxDist = SQR(40); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; emittingVol = m_anRandomTable[2] % 30 + 70; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; case SOUND_MELEE_ATTACK_START: { uint32 weaponType = ((uint32)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]) >> 8; switch (weaponType) { case WEAPONTYPE_SCREWDRIVER: case WEAPONTYPE_KNIFE: case WEAPONTYPE_CLEAVER: case WEAPONTYPE_MACHETE: case WEAPONTYPE_KATANA: m_sQueueSample.m_nSampleIndex = SFX_KNIFE_SWING; break; default: m_sQueueSample.m_nSampleIndex = SFX_GOLF_CLUB_SWING; break; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound++; narrowSoundRange = true; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 30.0f; if (weaponType == WEAPONTYPE_UNARMED || weaponType == WEAPONTYPE_BRASSKNUCKLE) emittingVol = m_anRandomTable[1] % 10 + 35; else emittingVol = m_anRandomTable[2] % 20 + 70; maxDist = SQR(30); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; } case SOUND_SKATING: { uint32 soundParams = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; uint8 param1 = soundParams & 0xFF; uint32 param2 = soundParams >> 8; m_sQueueSample.m_nSampleIndex = (m_anRandomTable[3] & 1) + SFX_SKATE_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = iSound; stereo = true; ++iSound; m_sQueueSample.m_nFrequency = m_anRandomTable[1] % 1000 + 17000; if (param2 == 0) m_sQueueSample.m_nFrequency = (3 * m_sQueueSample.m_nFrequency) / 4; m_sQueueSample.m_nReleasingVolumeModificator = 6; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = 20.0f; maxDist = SQR(20); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = (m_anRandomTable[2] % 20 + 70) * param1 / 127; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; } case SOUND_WEAPON_MINIGUN_ATTACK: m_sQueueSample.m_nSampleIndex = SFX_MINIGUN_FIRE_LEFT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 68; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_MINIGUN_FIRE_LEFT); m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = 150.0f; emittingVol = 127; maxDist = SQR(150); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_MINIGUN_FIRE_LEFT); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_MINIGUN_FIRE_LEFT); m_sQueueSample.m_nEmittingVolume = 127; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; break; case SOUND_WEAPON_MINIGUN_2: m_sQueueSample.m_nSampleIndex = SFX_MINIGUN_FIRE_RIGHT; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 69; m_sQueueSample.m_nFrequency = 18569; m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = 150.0f; emittingVol = 127.0f * m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; maxDist = SQR(150); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_MINIGUN_FIRE_RIGHT); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_MINIGUN_FIRE_RIGHT); m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; break; case SOUND_WEAPON_MINIGUN_3: m_sQueueSample.m_nSampleIndex = SFX_MINIGUN_STOP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 69; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_MINIGUN_STOP); m_sQueueSample.m_nReleasingVolumeModificator = 4; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = 150.0f; maxDist = SQR(150); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; emittingVol = 127; m_sQueueSample.m_nEmittingVolume = 127; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bRequireReflection = true; break; case SOUND_SHIRT_WIND_FLAP: if (params.m_pPed->IsPlayer() && params.m_pPed->m_pMyVehicle) { if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] > 0.0f) { if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] > 1.0f) m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i] = 1.0f; emittingVol = 90.0f * m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; switch (params.m_pPed->m_pMyVehicle->GetModelIndex()) { case MI_ANGEL: case MI_FREEWAY: m_sQueueSample.m_nSampleIndex = SFX_CAR_WIND_17; break; case MI_PIZZABOY: case MI_FAGGIO: m_sQueueSample.m_nSampleIndex = SFX_CAR_WIND_18; break; case MI_PCJ600: m_sQueueSample.m_nSampleIndex = SFX_CAR_WIND_20; break; case MI_SANCHEZ: m_sQueueSample.m_nSampleIndex = SFX_CAR_WIND_19; break; default: continue; }; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nCounter = 71; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_fSoundIntensity = 15.0f; maxDist = SQR(15); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; } } continue; default: SetupPedComments(params, sound); continue; } if (narrowSoundRange && iSound > 60) iSound = 21; if (params.m_fDistance < maxDist) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { if (stereo) { if (m_sQueueSample.m_fDistance < 0.2f * m_sQueueSample.m_fSoundIntensity) { m_sQueueSample.m_bIs2D = true; m_sQueueSample.m_nOffset = 0; } else { stereo = false; } } m_sQueueSample.m_bReverbFlag = true; AddSampleToRequestedQueue(); if (stereo) { m_sQueueSample.m_nOffset = 127; ++m_sQueueSample.m_nSampleIndex; if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] != SOUND_WEAPON_SHOT_FIRED || weapon->m_eWeaponType != WEAPONTYPE_FLAMETHROWER) { m_sQueueSample.m_nCounter = iSound++; if (iSound > 60) iSound = 21; } else { ++m_sQueueSample.m_nCounter; } AddSampleToRequestedQueue(); } } } } } void cAudioManager::SetPedTalkingStatus(CPed *ped, uint8 status) { if (ped != nil) ped->m_canTalk = status; } void cAudioManager::SetPlayersMood(uint8 mood, uint32 time) { if (!m_bIsInitialised) return; if (mood < MAX_PLAYER_MOODS) { m_nPlayerMood = mood; m_nPlayerMoodTimer = CTimer::GetTimeInMilliseconds() + time; } } void cAudioManager::SetupPedComments(cPedParams ¶ms, uint16 sound) { CPed *ped = params.m_pPed; uint8 emittingVol; float soundIntensity; tPedComment pedComment; if(ped != nil) { if(!ped->m_canTalk) return; m_bGenericSfx = false; pedComment.m_nSampleIndex = GetPedCommentSfx(ped, sound); if(pedComment.m_nSampleIndex == NO_SAMPLE) return; soundIntensity = 40.0f; } else { m_bGenericSfx = true; switch(sound) { case SOUND_PED_HELI_PLAYER_FOUND: soundIntensity = 400.0f; pedComment.m_nSampleIndex = GetRandomNumberInRange(m_sQueueSample.m_nEntityIndex % 4, SFX_POLICE_HELI_1, SFX_POLICE_HELI_20); break; case SOUND_PED_VCPA_PLAYER_FOUND: soundIntensity = 400.0f; #ifdef FIX_BUGS pedComment.m_nSampleIndex = m_anRandomTable[m_sQueueSample.m_nEntityIndex % 4] % 23 + SFX_POLICE_BOAT_1; #else pedComment.m_nSampleIndex = m_anRandomTable[m_sQueueSample.m_nEntityIndex % 4] % 29 + SFX_POLICE_BOAT_1; #endif break; case SOUND_INJURED_PED_MALE_OUCH: soundIntensity = 40.0f; pedComment.m_nSampleIndex = GetRandomNumberInRange(m_sQueueSample.m_nEntityIndex % 4, SFX_GENERIC_MALE_GRUNT_1, SFX_GENERIC_MALE_GRUNT_41); break; case SOUND_INJURED_PED_FEMALE: soundIntensity = 40.0f; pedComment.m_nSampleIndex = GetRandomNumberInRange(m_sQueueSample.m_nEntityIndex % 4, SFX_GENERIC_FEMALE_GRUNT_1, SFX_GENERIC_FEMALE_GRUNT_33); break; default: return; } } if(params.m_fDistance < SQR(soundIntensity)) { CalculateDistance(params.m_bDistanceCalculated, params.m_fDistance); if(CWorld::GetIsLineOfSightClear(TheCamera.GetPosition(), m_sQueueSample.m_vecPos, true, false, false, false, false, false)) emittingVol = MAX_VOLUME; else emittingVol = 31; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, soundIntensity, m_sQueueSample.m_fDistance); pedComment.m_nProcess = 10; if(m_sQueueSample.m_nVolume != 0) { pedComment.m_nEntityIndex = m_sQueueSample.m_nEntityIndex; pedComment.m_vecPos = m_sQueueSample.m_vecPos; pedComment.m_fDistance = m_sQueueSample.m_fDistance; pedComment.m_bVolume = m_sQueueSample.m_nVolume; m_sPedComments.Add(&pedComment); } } } uint32 cAudioManager::GetPedCommentSfx(CPed *ped, int32 sound) { if(ped->m_nPedState != PED_FALL || sound == MI_VICE8 || sound == MI_WFYG1 || sound == MI_WFYG2) { if(ped->m_getUpTimer == UINT32_MAX || ped->m_getUpTimer > CTimer::GetTimeInMilliseconds()) { if(sound != SOUND_PED_DAMAGE && sound != SOUND_PED_HIT && sound != SOUND_PED_LAND) return NO_SAMPLE; } if(ped->IsPlayer()) return GetPlayerTalkSfx(ped, sound); switch(ped->GetModelIndex()) { case MI_PLAYER: return GetPlayerTalkSfx(ped, sound); case MI_COP: return GetCopTalkSfx(ped, sound); case MI_SWAT: return GetSwatTalkSfx(ped, sound); case MI_FBI: return GetFBITalkSfx(ped, sound); case MI_ARMY: return GetGenericMaleTalkSfx(ped, sound); case MI_MEDIC: return GetMedicTalkSfx(ped, sound); case MI_FIREMAN: return GetFiremanTalkSfx(ped, sound); case MI_MALE01: return GetDefaultTalkSfx(ped, sound); case MI_HFYST: return GetHFYSTTalkSfx(ped, sound); case MI_HFOST: return GetHFOSTTalkSfx(ped, sound); case MI_HMYST: return GetHMYSTTalkSfx(ped, sound); case MI_HMOST: return GetHMOSTTalkSfx(ped, sound); case MI_HFYRI: return GetHFYRITalkSfx(ped, sound); case MI_HFORI: return GetHFORITalkSfx(ped, sound); case MI_HMYRI: return GetHMYRITalkSfx(ped, sound); case MI_HMORI: return GetHMORITalkSfx(ped, sound); case MI_HFYBE: return GetHFYBETalkSfx(ped, sound); case MI_HFOBE: return GetHFOBETalkSfx(ped, sound); case MI_HMYBE: return GetHMYBETalkSfx(ped, sound); case MI_HMOBE: return GetHMOBETalkSfx(ped, sound); case MI_HFYBU: return GetHFYBUTalkSfx(ped, sound); case MI_HFYMD: return GetHFYMDTalkSfx(ped, sound); case MI_HFYCG: return GetHFYCGTalkSfx(ped, sound); case MI_HFYPR: return GetHFYPRTalkSfx(ped, sound); case MI_HFOTR: return GetHFOTRTalkSfx(ped, sound); case MI_HMOTR: return GetHMOTRTalkSfx(ped, sound); case MI_HMYAP: return GetHMYAPTalkSfx(ped, sound); case MI_HMOCA: return GetHMOCATalkSfx(ped, sound); case MI_BMODK: return GetBMODKTalkSfx(ped, sound); case MI_BMYKR: return GetBMYCRTalkSfx(ped, sound); case MI_BFYST: return GetBFYSTTalkSfx(ped, sound); case MI_BFOST: return GetBFOSTTalkSfx(ped, sound); case MI_BMYST: return GetBMYSTTalkSfx(ped, sound); case MI_BMOST: return GetBMOSTTalkSfx(ped, sound); case MI_BFYRI: return GetBFYRITalkSfx(ped, sound); case MI_BFORI: return GetBFORITalkSfx(ped, sound); case MI_BMYRI: return GetBMYRITalkSfx(ped, sound); case MI_BFYBE: return GetBFYBETalkSfx(ped, sound); case MI_BMYBE: return GetBMYBETalkSfx(ped, sound); case MI_BFOBE: return GetBFOBETalkSfx(ped, sound); case MI_BMOBE: return GetBMOBETalkSfx(ped, sound); case MI_BMYBU: return GetBMYBUTalkSfx(ped, sound); case MI_BFYPR: return GetBFYPRTalkSfx(ped, sound); case MI_BFOTR: return GetBFOTRTalkSfx(ped, sound); case MI_BMOTR: return GetBMOTRTalkSfx(ped, sound); case MI_BMYPI: return GetBMYPITalkSfx(ped, sound); case MI_BMYBB: return GetBMYBBTalkSfx(ped, sound); case MI_WMYCR: return GetWMYCRTalkSfx(ped, sound); case MI_WFYST: return GetWFYSTTalkSfx(ped, sound); case MI_WFOST: return GetWFOSTTalkSfx(ped, sound); case MI_WMYST: return GetWMYSTTalkSfx(ped, sound); case MI_WMOST: return GetWMOSTTalkSfx(ped, sound); case MI_WFYRI: return GetWFYRITalkSfx(ped, sound); case MI_WFORI: return GetWFORITalkSfx(ped, sound); case MI_WMYRI: return GetWMYRITalkSfx(ped, sound); case MI_WMORI: return GetWMORITalkSfx(ped, sound); case MI_WFYBE: return GetWFYBETalkSfx(ped, sound); case MI_WMYBE: return GetWMYBETalkSfx(ped, sound); case MI_WFOBE: return GetWFOBETalkSfx(ped, sound); case MI_WMOBE: return GetWMOBETalkSfx(ped, sound); case MI_WMYCW: return GetWMYCWTalkSfx(ped, sound); case MI_WMYGO: return GetWMYGOTalkSfx(ped, sound); case MI_WFOGO: return GetWFOGOTalkSfx(ped, sound); case MI_WMOGO: return GetWMOGOTalkSfx(ped, sound); case MI_WFYLG: return GetWFYLGTalkSfx(ped, sound); case MI_WMYLG: return GetWMYLGTalkSfx(ped, sound); case MI_WFYBU: return GetWFYBUTalkSfx(ped, sound); case MI_WMYBU: return GetWMYBUTalkSfx(ped, sound); case MI_WMOBU: return GetWMOBUTalkSfx(ped, sound); case MI_WFYPR: return GetWFYPRTalkSfx(ped, sound); case MI_WFOTR: return GetWFOTRTalkSfx(ped, sound); case MI_WMOTR: return GetWMOTRTalkSfx(ped, sound); case MI_WMYPI: return GetWMYPITalkSfx(ped, sound); case MI_WMOCA: return GetWMOCATalkSfx(ped, sound); case MI_WFYJG: return GetWFYJGTalkSfx(ped, sound); case MI_WMYJG: return GetWMYJGTalkSfx(ped, sound); case MI_WFYSK: return GetWFYSKTalkSfx(ped, sound); case MI_WMYSK: return GetWMYSKTalkSfx(ped, sound); case MI_WFYSH: return GetWFYSHTalkSfx(ped, sound); case MI_WFOSH: return GetWFOSHTalkSfx(ped, sound); case MI_JFOTO: return GetJFOTOTalkSfx(ped, sound); case MI_JMOTO: return GetJMOTOTalkSfx(ped, sound); case MI_CBA: case MI_CBB: return GetCBTalkSfx(ped, sound); case MI_HNA: case MI_HNB: return GetHNTalkSfx(ped, sound); case MI_SGA: case MI_SGB: return GetSGTalkSfx(ped, sound); case MI_CLA: case MI_CLB: return GetCLTalkSfx(ped, sound); case MI_GDA: case MI_GDB: return GetGDTalkSfx(ped, sound); case MI_BKA: case MI_BKB: return GetBKTalkSfx(ped, sound); case MI_PGA: case MI_PGB: return GetPGTalkSfx(ped, sound); case MI_VICE1: case MI_VICE2: case MI_VICE3: case MI_VICE4: case MI_VICE5: case MI_VICE6: case MI_VICE7: case MI_VICE8: return GetVICETalkSfx(ped, sound, ped->GetModelIndex()); case MI_WFYG1: return GetWFYG1TalkSfx(ped, sound); case MI_WFYG2: return GetWFYG2TalkSfx(ped, sound); case MI_SPECIAL01: case MI_SPECIAL02: case MI_SPECIAL03: case MI_SPECIAL04: case MI_SPECIAL05: case MI_SPECIAL06: case MI_SPECIAL07: case MI_SPECIAL08: case MI_SPECIAL09: case MI_SPECIAL10: case MI_SPECIAL11: case MI_SPECIAL12: case MI_SPECIAL13: case MI_SPECIAL14: case MI_SPECIAL15: case MI_SPECIAL16: case MI_SPECIAL17: case MI_SPECIAL18: case MI_SPECIAL19: case MI_SPECIAL20: case MI_SPECIAL21: return NO_SAMPLE; default: return GetGenericMaleTalkSfx(ped, sound); } } return NO_SAMPLE; } void cAudioManager::GetPhrase(uint32 &phrase, uint32 &prevPhrase, uint32 sample, uint32 maxOffset) const { phrase = sample + m_anRandomTable[m_sQueueSample.m_nEntityIndex & 3] % maxOffset; // check if the same sfx like last time, if yes, then try use next one, // if exceeded range, then choose first available sample if (phrase == prevPhrase && ++phrase >= sample + maxOffset) phrase = sample; prevPhrase = phrase; } #pragma region PED_COMMENTS #define cooldown_phrase(count) static uint8 cooldown = 0;\ if (cooldown != 0) {\ if (++cooldown == count) cooldown = 0;\ return NO_SAMPLE;\ }\ cooldown = 1; uint32 cAudioManager::GetPlayerTalkSfx(CPed *ped, int16 sound) { uint32 sfx; if(m_bIsPlayerShutUp) return NO_SAMPLE; switch(sound) { case SOUND_PED_DEATH: return 9796; case SOUND_PED_DAMAGE: case SOUND_PED_BULLET_HIT: GetPhrase(sfx, ped->m_lastComment, 9815, 33); break; case SOUND_PED_HIT: case SOUND_PED_DEFEND: GetPhrase(sfx, ped->m_lastComment, 9883, 42); break; case SOUND_PED_LAND: GetPhrase(sfx, ped->m_lastComment, 9848, 35); break; case SOUND_PED_BURNING: GetPhrase(sfx, ped->m_lastComment, 9925, 16); break; case SOUND_PED_PLAYER_REACTTOCOP: switch(m_nPlayerMood) { case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8694, 38); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9615, 20); break; default: GetPhrase(sfx, ped->m_lastComment, 9046, 22); break; } break; case SOUND_PED_ON_FIRE: { cooldown_phrase(8); switch(m_nPlayerMood) { case PLAYER_MOOD_PISSED_OFF: GetPhrase(sfx, ped->m_lastComment, 9586, 29); break; case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 9007, 39); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9787, 9); break; default: GetPhrase(sfx, ped->m_lastComment, 9322, 35); break; } break; } case SOUND_PED_AIMING: { cooldown_phrase(8); switch(m_nPlayerMood) { case PLAYER_MOOD_PISSED_OFF: GetPhrase(sfx, ped->m_lastComment, 9561, 25); break; case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8937, 52); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9758, 19); break; default: GetPhrase(sfx, ped->m_lastComment, 9275, 39); break; } break; } case SOUND_PED_CAR_JACKING: { cooldown_phrase(4); switch(m_nPlayerMood) { case PLAYER_MOOD_PISSED_OFF: GetPhrase(sfx, ped->m_lastComment, 9483, 36); break; case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8876, 43); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9706, 18); break; default: GetPhrase(sfx, ped->m_lastComment, 9202, 40); break; } break; } case SOUND_PED_MUGGING: { cooldown_phrase(8); switch(m_nPlayerMood) { case PLAYER_MOOD_PISSED_OFF: GetPhrase(sfx, ped->m_lastComment, 9519, 25); break; case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8919, 12); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9724, 23); break; default: GetPhrase(sfx, ped->m_lastComment, 9242, 11); break; } break; } case SOUND_PED_CAR_JACKED: { cooldown_phrase(4); switch(m_nPlayerMood) { case PLAYER_MOOD_PISSED_OFF: GetPhrase(sfx, ped->m_lastComment, 9462, 21); break; case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8843, 33); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9688, 18); break; default: GetPhrase(sfx, ped->m_lastComment, 9178, 24); break; } break; } case SOUND_PED_PLAYER_AFTERSEX: GetPhrase(sfx, ped->m_lastComment, 9797, 18); break; case SOUND_PED_PLAYER_BEFORESEX: switch(m_nPlayerMood) { case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8989, 18); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9777, 10); break; default: GetPhrase(sfx, ped->m_lastComment, 9314, 8); break; } break; case SOUND_PED_PLAYER_FARFROMCOPS: { cooldown_phrase(4); switch(m_nPlayerMood) { case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8732, 9); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9635, 7); break; default: GetPhrase(sfx, ped->m_lastComment, 9068, 20); break; } break; } case SOUND_PED_ATTACK: { cooldown_phrase(4); switch(m_nPlayerMood) { case PLAYER_MOOD_PISSED_OFF: GetPhrase(sfx, ped->m_lastComment, 9401, 61); break; case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8782, 61); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9661, 27); break; default: GetPhrase(sfx, ped->m_lastComment, 9131, 47); break; } break; } case SOUND_PED_CRASH_VEHICLE: case SOUND_PED_CRASH_CAR: case SOUND_PED_ANNOYED_DRIVER: { cooldown_phrase(4); switch(m_nPlayerMood) { case PLAYER_MOOD_PISSED_OFF: GetPhrase(sfx, ped->m_lastComment, 9357, 44); break; case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8741, 41); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9642, 19); break; default: GetPhrase(sfx, ped->m_lastComment, 9088, 43); break; } break; } case SOUND_PED_SOLICIT: { cooldown_phrase(4); switch(m_nPlayerMood) { case PLAYER_MOOD_PISSED_OFF: GetPhrase(sfx, ped->m_lastComment, 9544, 17); break; case PLAYER_MOOD_ANGRY: GetPhrase(sfx, ped->m_lastComment, 8931, 6); break; case PLAYER_MOOD_WISECRACKING: GetPhrase(sfx, ped->m_lastComment, 9747, 11); break; default: GetPhrase(sfx, ped->m_lastComment, 9253, 22); break; } break; } default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetCopTalkSfx(CPed *ped, int16 sound) { uint32 sfx; PedState objective; switch(sound) { case SOUND_PED_ARREST_COP: GetPhrase(sfx, ped->m_lastComment, 8469, 4); break; case SOUND_PED_PULLOUTWEAPON: GetPhrase(sfx, ped->m_lastComment, 8473, 3); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 8500, 2); break; case SOUND_PED_COP_UNK_129: GetPhrase(sfx, ped->m_lastComment, 8510, 4); break; case SOUND_PED_COP_MANYCOPSAROUND: GetPhrase(sfx, ped->m_lastComment, 8508, 2); break; case SOUND_PED_GUNAIMEDAT2: GetPhrase(sfx, ped->m_lastComment, 8498, 2); break; case SOUND_PED_COP_ALONE: GetPhrase(sfx, ped->m_lastComment, 8504, 4); break; case SOUND_PED_GUNAIMEDAT3: GetPhrase(sfx, ped->m_lastComment, 8485, 2); break; case SOUND_PED_COP_REACTION: { cooldown_phrase(4); GetPhrase(sfx, ped->m_lastComment, 8502, 2); break; } case SOUND_PED_COP_LITTLECOPSAROUND: objective = FindPlayerPed()->m_nPedState; if(objective == PED_ARRESTED || objective == PED_DEAD || objective == PED_DIE) return NO_SAMPLE; GetPhrase(sfx, ped->m_lastComment, 8481, 4); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 8494, 4); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 8491, 3); break; case SOUND_PED_PED_COLLISION: if(FindPlayerPed()->m_pWanted->GetWantedLevel() <= 0) return NO_SAMPLE; GetPhrase(sfx, ped->m_lastComment, 8476, 5); break; default: return GetGenericMaleTalkSfx(ped, sound); } return 45 * (m_sQueueSample.m_nEntityIndex % 5) + sfx; } uint32 cAudioManager::GetSwatTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_COP_HELIPILOTPHRASE: GetPhrase(sfx, ped->m_lastComment, 3285, 7); break; case SOUND_PED_COP_UNK_129: GetPhrase(sfx, ped->m_lastComment, 3292, 4); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3282, 3); break; default: return GetGenericMaleTalkSfx(ped, sound); } sfx += 14 * (m_sQueueSample.m_nEntityIndex % 3); return sfx; } uint32 cAudioManager::GetFBITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_COP_UNK_129: GetPhrase(sfx, ped->m_lastComment, 3240u, 4u); break; case SOUND_PED_COP_MANYCOPSAROUND: GetPhrase(sfx, ped->m_lastComment, 3237u, 3u); break; case SOUND_PED_GUNAIMEDAT2: sfx = 3236; break; case SOUND_PED_GUNAIMEDAT3: GetPhrase(sfx, ped->m_lastComment, 3228u, 4u); break; case SOUND_PED_CRASH_VEHICLE: case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 3232u, 4u); break; default: return GetGenericMaleTalkSfx(ped, sound); } sfx += 16 * (m_sQueueSample.m_nEntityIndex % 3); return sfx; } uint32 cAudioManager::GetArmyTalkSfx(CPed *ped, int16 sound) { return GetGenericMaleTalkSfx(ped, sound); } uint32 cAudioManager::GetMedicTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 3162, 6); break; case SOUND_PED_HEALING: GetPhrase(sfx, ped->m_lastComment, 3178, 17); break; case SOUND_PED_LEAVE_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 3168, 10); break; // SFX_MEDIC_VOICE_1_GET_OUT_VAN_CHAT_1 default: return GetGenericMaleTalkSfx(ped, sound); } sfx += 33 * (m_sQueueSample.m_nEntityIndex % 2); return sfx; } uint32 cAudioManager::GetFiremanTalkSfx(CPed *ped, int16 sound) { return GetGenericMaleTalkSfx(ped, sound); } uint32 cAudioManager::GetDefaultTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 2033, 12); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 2045, 12); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 2075, 4); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 2098, 4); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 2108, 5); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 2004, 16); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 1979, 19); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 2079, 19); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 2020, 13); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 1939, 15); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 1898, 16); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 2070, 5); break; case SOUND_153: GetPhrase(sfx, ped->m_lastComment, 1998, 6); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 2102, 6); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 1914, 25); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 1954, 25); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFYSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 5736, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 5747, 4); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 5755, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 5741, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5753, 2); break; case SOUND_PED_TAXI_WAIT: return 5759; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5722, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5712, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 5729, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 5695, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 5678, 7); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 5751, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5685, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 5703, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFOSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 4382, 6); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 4388, 8); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 4398, 3); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 4401, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 4363, 8); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4353, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 4371, 11); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 4334, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 4313, 9); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 4396, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4322, 12); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 4342, 11); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHMYSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7961, 6); break; case SOUND_PED_ACCIDENTREACTION1: return 7971; case SOUND_PED_TAXI_WAIT: return 7974; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7946, 6); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7967, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7954, 7); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 7952, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7972, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7922, 13); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7935, 11); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHMOSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 5820, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 5831, 3); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 5825, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5836, 2); break; case SOUND_PED_TAXI_WAIT: return 5838; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5805, 8); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5795, 9); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 5813, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 5777, 7); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 5760, 7); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 5834, 2); break; case SOUND_PED_CHAT_SEXY: return 5804; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5767, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 5784, 11); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFYRITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 6965, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6970, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6978, 4); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6986, 2); break; case SOUND_PED_TAXI_WAIT: return 6991; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6948, 10); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 6982, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6958, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6940, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6923, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 6976, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 6988, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6931, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFORITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7244, 6); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7250, 9); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7261, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 7267; case SOUND_PED_TAXI_WAIT: return 7270; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7229, 6); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7263, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7237, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7222, 7); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 7206, 6); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 7259, 2); break; case SOUND_153: GetPhrase(sfx, ped->m_lastComment, 7235, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7268, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7212, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHMYRITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 5890, 7); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 5905, 3); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 5897, 8); break; case SOUND_PED_ROBBED: return 5908; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5873, 5); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5864, 9); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 5878, 12); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 5856, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 5839, 7); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 5909, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5846, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHMORITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 4454, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 4459, 8); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 4469, 3); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 4478, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4436, 7); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 4472, 6); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 4443, 11); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 4422, 6); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 4403, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 4467, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4411, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 4428, 8); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFYBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 6897, 7); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6904, 7); break; case SOUND_PED_TAXI_WAIT: return 6922; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6878, 11); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6889, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6862, 6); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 6911, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 6920, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6854, 8); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 6868, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFOBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 1018, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 1023, 6); break; case SOUND_PED_ACCIDENTREACTION1: return 1035; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 1038, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 1006, 7); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 1031, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 1013, 5); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 990, 6); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 973, 6); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 1029, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 1036, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 979, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 996, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHMYBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 4892, 6); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 4902, 12); break; case SOUND_PED_ACCIDENTREACTION1: return 4917; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 4898, 4); break; case SOUND_PED_TAXI_WAIT: return 4920; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 4874, 8); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4862, 7); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 4882, 10); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 4845, 7); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 4914, 3); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 4869, 5); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 4918, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4835, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 4852, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHMOBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 4703, 3); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 4709, 6); break; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 4706, 3); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4690, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 4672, 10); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 4699, 4); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4682, 8); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFYBUTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 4771, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 4782, 3); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 4776, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 4787, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 4789; case SOUND_PED_TAXI_WAIT: return 4790; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 4752, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4742, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 4759, 12); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 4734, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 4715, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 4785, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4723, 11); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFYMDTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 6014, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6019, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6021, 3); break; case SOUND_PED_TAXI_WAIT: return 8231; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 6005, 9); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5997, 8); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 6024, 15); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5988, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFYCGTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 4808, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 4813, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 4819; case SOUND_PED_TAXI_WAIT: return 8231; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4800, 8); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 4815, 4); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 4820, 14); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4791, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFYPRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 5964, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5970, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 5972; case SOUND_PED_PLAYER_BEFORESEX: GetPhrase(sfx, ped->m_lastComment, 5956, 8); break; case SOUND_PED_TAXI_WAIT: return 5987; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5946, 10); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5934, 9); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 5973, 14); break; case SOUND_153: GetPhrase(sfx, ped->m_lastComment, 5943, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5912, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 5922, 12); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHFOTRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 4660, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 4665, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 4667; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 4670, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 4654, 6); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4646, 8); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 4668, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4623, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 4634, 12); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHMOTRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 4515, 6); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 4521, 2); break; case SOUND_PED_TAXI_WAIT: return 4534; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 4508, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4497, 11); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 4526, 8); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 4523, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4480, 8); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 4488, 9); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHMYAPTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 4591, 7); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 4605, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 4598, 7); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 4611, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 4619, 2); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 4621, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4573, 9); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 4613, 6); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 4585, 6); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 4555, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 4535, 9); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 4609, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 4582, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4544, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 4564, 9); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetHMOCATalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 3506, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 3521, 11); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 3511, 10); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 3532, 7); break; case SOUND_PED_TAXI_WAIT: return 3541; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 3539, 2); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 3486, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 3478, 8); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 3504, 2); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 3494, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMODKTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 6831, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6838, 9); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6847, 2); break; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 6835, 3); break; case SOUND_PED_TAXI_WAIT: return 6853; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6817, 7); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 6849, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6824, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6794, 10); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6776, 8); break; case SOUND_PED_147: GetPhrase(sfx, ped->m_lastComment, 6805, 11); switch(sfx) { case 6809: case 6810: case 6811: GetPhrase(sfx, ped->m_lastComment, 6805, 4); break; default: break; } break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6784, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMYCRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 6578, 6); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 6594, 12); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 6609, 6); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6588, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6606, 3); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6615, 2); break; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 6584, 4); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 6563, 8); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6553, 8); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6571, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6544, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6521, 12); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 6561, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6533, 11); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBFYSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7184, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7188, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7195, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 7203, 2); break; case SOUND_PED_TAXI_WAIT: return 7205; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7167, 9); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7197, 6); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7176, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7149, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 7132, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 7193, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7140, 9); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7158, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBFOSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7046, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7051, 8); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7061, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 7067, 2); break; case SOUND_PED_TAXI_WAIT: return 7069; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7027, 11); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7063, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7038, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7009, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6992, 7); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 7059, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6999, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7017, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMYSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 6413, 6); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 6427, 4); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 6433, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6419, 8); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6431, 2); break; case SOUND_PED_TAXI_WAIT: return 6437; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 6400, 6); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6392, 8); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6406, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6371, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6352, 8); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6360, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 6380, 12); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMOSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 4292, 9); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 4307, 4); break; case SOUND_PED_ACCIDENTREACTION1: return 4311; case SOUND_PED_TAXI_WAIT: return 4312; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 4272, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4258, 8); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 4279, 13); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 4232, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 4301, 6); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 4266, 6); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4215, 17); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 4240, 18); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBFYRITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 6161, 4); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 6173, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6165, 8); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6179, 3); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6188, 2); break; case SOUND_PED_TAXI_WAIT: return 6194; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6143, 8); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 6182, 6); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6154, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6135, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6117, 9); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 6177, 2); break; case SOUND_153: GetPhrase(sfx, ped->m_lastComment, 6151, 3); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 6190, 4); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6126, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBFORITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7110, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7115, 4); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7121, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 7127; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 7130, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7094, 9); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7123, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7103, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7087, 7); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 7070, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 7119, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7128, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7078, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMYRITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 5430, 7); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 5437, 4); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5443, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 5449; case SOUND_PED_TAXI_WAIT: return 5453; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5414, 8); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 5445, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 5423, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 5407, 7); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 5394, 6); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 5441, 2); break; case SOUND_PED_CHAT_SEXY: return 5422; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 5450, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5400, 7); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBFYBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 6255, 6); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6261, 8); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6273, 5); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6284, 2); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 6290, 3); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6233, 10); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 6278, 6); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6247, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6207, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6195, 12); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 6269, 4); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 6243, 4); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 6286, 4); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 6217, 16); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMYBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 956, 4); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 966, 3); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 960, 6); break; case SOUND_PED_ROBBED: return 970; case SOUND_PED_ACCIDENTREACTION1: return 971; case SOUND_PED_TAXI_WAIT: return 972; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 940, 8); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 928, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 948, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 910, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 892, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: return 969; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 938, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 900, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 918, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBFOBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 8213, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 8223, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 8218, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 8227, 2); break; case SOUND_PED_TAXI_WAIT: return 8231; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 8197, 9); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 8206, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 8182, 7); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 8166, 8); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 8229, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 8174, 8); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 8189, 8); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMOBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7611, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7616, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7622, 4); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 7626, 3); break; case SOUND_PED_TAXI_WAIT: return 7632; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 7594, 10); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7583, 11); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7604, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7564, 9); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7629, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7559, 5); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7573, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMYBUTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 5500, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 5507, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5513, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 5515; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 5505, 2); break; case SOUND_PED_TAXI_WAIT: return 5518; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5488, 5); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5476, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 5493, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 5469, 7); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 5454, 8); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 5486, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 5516, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5462, 7); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBFYPRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 5369, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5374, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 5376; case SOUND_PED_PLAYER_BEFORESEX: GetPhrase(sfx, ped->m_lastComment, 5362, 7); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 5392, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5355, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5348, 7); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 5379, 13); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 5377, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5324, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 5335, 13); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBFOTRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 5232, 6); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 5240, 3); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5238, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 5243; case SOUND_PED_TAXI_WAIT: return 5252; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5226, 6); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5217, 9); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 5247, 5); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 5244, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5192, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 5202, 15); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMOTRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 6327, 5); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6343, 1); break; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 6332, 4); break; case SOUND_PED_TAXI_WAIT: return 6351; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6313, 11); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 6336, 7); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 6344, 7); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 6324, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6293, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 6303, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMYPITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 4033, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 4044, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 4038, 6); break; case SOUND_PED_ROBBED: return 4048; case SOUND_PED_ACCIDENTREACTION1: return 4049; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 4050, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 4012, 8); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3998, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 4020, 13); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 3993, 5); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 3978, 6); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 4008, 4); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 3984, 9); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetBMYBBTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 639, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 659, 9); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 691, 8); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 648, 11); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 686, 5); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 699, 6); break; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 644, 4); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 711, 3); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 618, 12); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 584, 18); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 630, 9); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 554, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 524, 13); break; case SOUND_PED_149: GetPhrase(sfx, ped->m_lastComment, 668, 16); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 684, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 602, 16); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 705, 6); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 537, 17); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 563, 21); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYCRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 5056, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 5061, 6); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 5070, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5067, 3); break; case SOUND_PED_TAXI_WAIT: return 5075; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5040, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5030, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 5047, 9); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 5021, 9); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5003, 18); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 8445, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 8456, 4); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 8463, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 8450, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 8461, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 8467; case SOUND_PED_TAXI_WAIT: return 8468; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 8430, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 8420, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 8437, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 8402, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 8386, 6); break; case SOUND_PED_WAIT_DOUBLEBACK: return 8460; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 8392, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 8410, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFOSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 8354, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 8358, 8); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 8369, 5); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 8381, 4); break; case SOUND_PED_TAXI_WAIT: return 8385; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 8332, 12); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 8374, 7); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 8344, 10); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 8305, 11); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 8274, 12); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 8366, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 8286, 19); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 8316, 16); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 3947, 5); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 3963, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 3955, 5); break; case SOUND_PED_ROBBED: return 3962; case SOUND_PED_ACCIDENTREACTION1: return 3975; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 3952, 3); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 3976, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3930, 10); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 3968, 7); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 3942, 5); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 3912, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 3893, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 3960, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 3940, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 3901, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 3920, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMOSTTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 5170, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 5178, 4); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5188, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 5190; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 5175, 3); break; case SOUND_PED_TAXI_WAIT: return 5191; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5155, 8); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5145, 8); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 5163, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 5129, 7); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 5111, 8); break; case SOUND_PED_149: GetPhrase(sfx, ped->m_lastComment, 5182, 4); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 5186, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 5153, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5119, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 5136, 9); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYRITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 5299, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 5304, 7); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5313, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 5320; case SOUND_PED_TAXI_WAIT: return 5323; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5280, 9); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 5315, 5); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 5291, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 5271, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 5253, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 5311, 2); break; case SOUND_153: GetPhrase(sfx, ped->m_lastComment, 5289, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 5321, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5261, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFORITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7825, 6); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7831, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7839, 3); break; case SOUND_PED_ACCIDENTREACTION1: return 7842; case SOUND_PED_TAXI_WAIT: return 7846; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 7810, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7799, 11); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7817, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7789, 10); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 7771, 7); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 7837, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7843, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7778, 11); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYRITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 4186, 8); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 4194, 8); break; case SOUND_PED_ACCIDENTREACTION1: return 4208; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 4213, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4163, 9); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 4203, 5); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 4175, 11); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 4144, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 4126, 10); break; case SOUND_PED_WAIT_DOUBLEBACK: return 4202; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 4172, 3); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 4209, 4); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4136, 8); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMORITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 6668, 9); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6677, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6685, 4); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6701, 2); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 6707, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6647, 10); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 6689, 12); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6660, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6641, 6); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6617, 10); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 6683, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 6657, 3); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 6703, 4); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6627, 14); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7752, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7757, 4); break; case SOUND_PED_ACCIDENTREACTION1: return 7766; case SOUND_PED_TAXI_WAIT: return 7770; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7738, 8); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7761, 5); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7746, 6); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7722, 6); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 7704, 7); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7767, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7711, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7728, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 8127, 8); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 8142, 3); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 8135, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 8105, 12); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 8155, 5); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 8119, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 8086, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 8063, 9); break; case SOUND_PED_149: GetPhrase(sfx, ped->m_lastComment, 8145, 7); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 8152, 3); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 8117, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 8160, 6); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 8072, 14); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 8094, 11); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFOBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 6093, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6098, 4); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6109, 3); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 6115, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6075, 8); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 6102, 7); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6083, 10); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6058, 7); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6040, 8); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 6112, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6048, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 6065, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMOBETalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 3759, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 3772, 4); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 3792, 6); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 3764, 8); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 3802, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3742, 8); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 3798, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 3752, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 3724, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 3706, 6); break; case SOUND_PED_149: GetPhrase(sfx, ped->m_lastComment, 3776, 16); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 3750, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 3804, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 3712, 12); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 3732, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYCWTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 5650, 6); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 5670, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 5659, 6); break; case SOUND_PED_ROBBED: return 5676; case SOUND_PED_TAXI_WAIT: return 5677; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5635, 8); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5622, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 5643, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 5598, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 5580, 9); break; case SOUND_PED_149: GetPhrase(sfx, ped->m_lastComment, 5665, 5); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 5674, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 5632, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5589, 9); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 5607, 15); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYGOTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7679, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7684, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7690, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 7698; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 7701, 3); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7659, 11); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7692, 6); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7672, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7642, 7); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 7670, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7699, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7633, 9); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7649, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFOGOTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7904, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7909, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7915, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 7919; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 7883, 14); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7874, 9); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7917, 2); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7897, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7855, 8); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7920, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7847, 8); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7863, 11); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMOGOTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 4982, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 4987, 6); break; case SOUND_PED_ACCIDENTREACTION1: return 4998; case SOUND_PED_TAXI_WAIT: return 5002; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 4961, 13); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4947, 12); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 4993, 5); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 4974, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 4929, 9); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 4959, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 4999, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4921, 8); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 4938, 9); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYLGTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 8267, 5); break; case SOUND_PED_ACCIDENTREACTION1: return 8272; case SOUND_PED_TAXI_WAIT: return 8273; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 8260, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 8252, 8); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 8232, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 8242, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYLGTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 3698, 6); break; case SOUND_PED_ACCIDENTREACTION1: return 3704; case SOUND_PED_TAXI_WAIT: return 3705; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 3691, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3682, 9); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 3662, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 3672, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYBUTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7309, 8); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 7317, 8); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7325, 4); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 7340, 2); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7329, 8); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 7301, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7292, 9); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7337, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7271, 21); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYBUTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 3862, 6); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 3870, 5); break; case SOUND_PED_ROBBED: return 3880; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 3884, 2); break; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 3868, 2); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 3891, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3845, 10); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 3881, 3); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 3857, 5); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 3826, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 3706, 6); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 3875, 5); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 3855, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 3886, 5); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 3815, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 3835, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMOBUTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 6753, 6); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6759, 7); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6769, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6771, 3); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 6774, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 6743, 3); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6733, 8); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6746, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6726, 7); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6709, 7); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 6766, 3); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 6741, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6716, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYPRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 4101, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 4107, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 4109; case SOUND_PED_PLAYER_BEFORESEX: GetPhrase(sfx, ped->m_lastComment, 4096, 5); break; case SOUND_PED_TAXI_WAIT: return 4125; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 4087, 9); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 4077, 10); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 4110, 15); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 4052, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 4063, 14); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFOTRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 7371, 6); break; case SOUND_PED_ACCIDENTREACTION1: return 7383; case SOUND_PED_TAXI_WAIT: return 7393; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7362, 9); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7377, 6); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 7384, 9); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7342, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7353, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMOTRTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 7542, 5); break; case SOUND_PED_ACCIDENTREACTION1: return 7547; case SOUND_PED_TAXI_WAIT: return 7558; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 7536, 6); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7517, 17); break; case SOUND_PED_SOLICIT: GetPhrase(sfx, ped->m_lastComment, 7551, 7); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 7534, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7548, 3); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7494, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7504, 13); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYPITalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 6496, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 6509, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 6503, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 6513, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 6515, 2); break; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 6501, 2); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 6517, 4); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 6479, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 6465, 8); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 6488, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 6457, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 6439, 8); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 6473, 6); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 6447, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMOCATalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 8032, 6); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 8048, 11); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 8038, 10); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 8059, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 8061; case SOUND_PED_TAXI_WAIT: return 8062; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 8015, 8); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 8003, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 8023, 9); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 7993, 10); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 7975, 12); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 8013, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7987, 6); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYJGTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 7414, 4); break; case SOUND_PED_ACCIDENTREACTION1: sfx = 7424; break; case SOUND_PED_TAXI_WAIT: sfx = 7425; break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7406, 8); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7418, 6); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7394, 12); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYJGTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 5098, 4); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5102, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 5109; case SOUND_PED_TAXI_WAIT: return 5110; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 5104, 5); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 5076, 10); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 5096, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5086, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYSKTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 3652, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 3657, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 3659, 2); break; case SOUND_PED_TAXI_WAIT: return 3661; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 3641, 11); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3632, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 3603, 11); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 3614, 18); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWMYSKTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 5563, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 5573, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 5575, 2); break; case SOUND_PED_UNK_126: GetPhrase(sfx, ped->m_lastComment, 5568, 3); break; case SOUND_PED_TAXI_WAIT: return 5579; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 5558, 5); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 5546, 10); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 5571, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 5556, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 5577, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 5519, 14); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 5533, 13); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYSHTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 7459, 9); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 7470, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 7483, 4); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 7492, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 7448, 11); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 7472, 11); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 7468, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 7487, 5); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 7426, 12); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 7438, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFOSHTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 3571, 10); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 3583, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 3594, 3); break; case SOUND_PED_TAXI_WAIT: return 3602; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3561, 10); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 3585, 9); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 3581, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 3597, 5); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 3542, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 3552, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetJFOTOTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 811, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 815, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 821, 2); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 828, 2); break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 831, 2); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 796, 9); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 823, 5); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 805, 6); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 775, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 757, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: return 820; case SOUND_PED_CHAT_EVENT: return 830; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 765, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 783, 13); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetJMOTOTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_COWER: GetPhrase(sfx, ped->m_lastComment, 874, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 878, 4); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 883, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 889; case SOUND_PED_TAXI_WAIT: return 891; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 862, 6); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 885, 4); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 868, 6); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 849, 6); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 833, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: return 882; case SOUND_PED_CHAT_EVENT: return 890; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 841, 8); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 855, 7); break; default: return GetGenericMaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetCBTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 2178, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 2187, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 2183, 4); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 2194, 2); break; case SOUND_PED_ACCIDENTREACTION1: sfx = 2196; break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 2197, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 2161, 9); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 2150, 9); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 2170, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 2132, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 2113, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 2192, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 2159, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 2121, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 2140, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return 86 * (m_sQueueSample.m_nEntityIndex % 3) + sfx; } uint32 cAudioManager::GetHNTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 2692, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 2703, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 2697, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 2711, 3); break; case SOUND_PED_ACCIDENTREACTION1: sfx = 2714; break; case SOUND_PED_TAXI_WAIT: sfx = 2715; break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 2673, 10); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 2661, 10); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 2683, 9); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 2638, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 2617, 9); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 2707, 4); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 2671, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 2626, 12); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 2647, 14); break; default: return GetGenericMaleTalkSfx(ped, sound); } return 99 * (m_sQueueSample.m_nEntityIndex % 3) + sfx; } uint32 cAudioManager::GetSGTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 1104, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 1114, 5); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 1124, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 1109, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 1121, 3); break; case SOUND_PED_ACCIDENTREACTION1: sfx = 1129; break; case SOUND_PED_TAXI_WAIT: sfx = 1132; break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 1088, 10); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 1076, 9); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 1098, 6); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 1058, 6); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 1040, 8); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 1119, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 1085, 3); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 1130, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 1048, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 1064, 12); break; default: return GetGenericMaleTalkSfx(ped, sound); } if(ped->GetModelIndex() == MI_SGB) sfx += 93; return sfx; } uint32 cAudioManager::GetCLTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 1299, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 1310, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 1304, 6); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 1317, 2); break; case SOUND_PED_ACCIDENTREACTION1: sfx = 1319; break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 1320, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 1281, 10); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 1266, 13); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 1291, 8); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 1246, 10); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 1226, 10); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 1315, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 1279, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 1236, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 1256, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return 96 * (m_sQueueSample.m_nEntityIndex % 3) + sfx; } uint32 cAudioManager::GetGDTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 1762, 6); break; case SOUND_PED_ACCIDENTREACTION1: GetPhrase(sfx, ped->m_lastComment, 1770, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 1755, 7); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 1744, 9); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 1768, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 1753, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 1772, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 1724, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 1734, 10); break; default: return GetGenericMaleTalkSfx(ped, sound); } return 50 * (m_sQueueSample.m_nEntityIndex % 3) + sfx; } uint32 cAudioManager::GetBKTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 2429, 5); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 2442, 4); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 2434, 8); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 2448, 2); break; case SOUND_PED_ACCIDENTREACTION1: sfx = 2450; break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 2451, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 2412, 9); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 2403, 9); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 2421, 8); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 2371, 10); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 2446, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 2381, 10); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 2391, 12); break; default: return GetGenericMaleTalkSfx(ped, sound); } return 82 * (m_sQueueSample.m_nEntityIndex % 3) + sfx; } uint32 cAudioManager::GetPGTalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 1561, 4); break; case SOUND_PED_CAR_JACKING: GetPhrase(sfx, ped->m_lastComment, 1570, 5); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 1565, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 1577, 2); break; case SOUND_PED_ACCIDENTREACTION1: sfx = 1579; break; case SOUND_PED_TAXI_WAIT: GetPhrase(sfx, ped->m_lastComment, 1582, 2); break; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 1551, 5); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 1542, 7); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 1556, 5); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 1529, 5); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 1514, 10); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 1575, 2); break; case SOUND_PED_CHAT_SEXY: GetPhrase(sfx, ped->m_lastComment, 1549, 2); break; case SOUND_PED_CHAT_EVENT: GetPhrase(sfx, ped->m_lastComment, 1580, 2); break; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 1524, 5); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 1534, 8); break; default: return GetGenericMaleTalkSfx(ped, sound); } return 70 * (m_sQueueSample.m_nEntityIndex % 3) + sfx; } uint32 cAudioManager::GetVICETalkSfx(CPed *ped, int16 sound, int16 model) { uint32 sfx; if(model == MI_VICE6) { switch(sound) { case SOUND_PED_ARREST_COP: GetPhrase(sfx, ped->m_lastComment, 1894, 3); break; case SOUND_PED_MIAMIVICE_EXITING_CAR: return 1897; default: return GetGenericMaleTalkSfx(ped, sound); } } switch(sound) { case SOUND_PED_ARREST_COP: GetPhrase(sfx, ped->m_lastComment, 1874, 3); break; case SOUND_PED_MIAMIVICE_EXITING_CAR: sfx = 1877; break; default: return GetGenericMaleTalkSfx(ped, sound); } sfx += 4 * (m_sQueueSample.m_nEntityIndex % 5); return sfx; } uint32 cAudioManager::GetWFYG1TalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 3383, 6); break; case SOUND_PED_MUGGING: GetPhrase(sfx, ped->m_lastComment, 3399, 2); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 3389, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 3397, 2); break; case SOUND_PED_ACCIDENTREACTION1: return 3403; case SOUND_PED_TAXI_WAIT: return 3405; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 3372, 4); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3361, 9); break; case SOUND_PED_FLEE_RUN: GetPhrase(sfx, ped->m_lastComment, 3401, 2); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 3376, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 3342, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 3324, 7); break; case SOUND_PED_WAIT_DOUBLEBACK: GetPhrase(sfx, ped->m_lastComment, 3394, 3); break; case SOUND_153: GetPhrase(sfx, ped->m_lastComment, 3370, 2); break; case SOUND_PED_CHAT_EVENT: return 3404; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 3331, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 3351, 10); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetWFYG2TalkSfx(CPed *ped, int16 sound) { uint32 sfx; switch(sound) { case SOUND_PED_HANDS_UP: GetPhrase(sfx, ped->m_lastComment, 3464, 3); break; case SOUND_PED_CAR_JACKED: GetPhrase(sfx, ped->m_lastComment, 3467, 5); break; case SOUND_PED_ROBBED: GetPhrase(sfx, ped->m_lastComment, 3473, 2); break; case SOUND_PED_TAXI_WAIT: return 3476; case SOUND_PED_ATTACK: GetPhrase(sfx, ped->m_lastComment, 3452, 5); break; case SOUND_PED_EVADE: GetPhrase(sfx, ped->m_lastComment, 3440, 8); break; case SOUND_PED_CRASH_VEHICLE: GetPhrase(sfx, ped->m_lastComment, 3457, 7); break; case SOUND_PED_CRASH_CAR: GetPhrase(sfx, ped->m_lastComment, 3422, 9); break; case SOUND_PED_ANNOYED_DRIVER: GetPhrase(sfx, ped->m_lastComment, 3406, 5); break; case SOUND_PED_WAIT_DOUBLEBACK: return 3472; case SOUND_153: GetPhrase(sfx, ped->m_lastComment, 3448, 4); break; case SOUND_PED_CHAT_EVENT: return 3475; case SOUND_PED_PED_COLLISION: GetPhrase(sfx, ped->m_lastComment, 3411, 11); break; case SOUND_PED_CHAT: GetPhrase(sfx, ped->m_lastComment, 3431, 9); break; default: return GetGenericFemaleTalkSfx(ped, sound); } return sfx; } uint32 cAudioManager::GetGenericMaleTalkSfx(CPed *ped, int16 sound) { uint32 sfx; m_bGenericSfx = true; switch(sound) { case SOUND_PED_DEATH: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_MALE_DEATH_1, 41); break; case SOUND_PED_BULLET_HIT: case SOUND_PED_DEFEND: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_MALE_GRUNT_1, 41); break; case SOUND_PED_BURNING: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_MALE_FIRE_1, 32); break; case SOUND_PED_FLEE_SPRINT: GetPhrase(sfx, ped->m_lastComment, SFX_GENERIC_MALE_PANIC_1, 35); break; default: return NO_SAMPLE; } return sfx; } uint32 cAudioManager::GetGenericFemaleTalkSfx(CPed *ped, int16 sound) { uint32 sfx; m_bGenericSfx = true; switch(sound) { case SOUND_PED_DEATH: GetPhrase(sfx, ped->m_lastComment, 2931, 22); break; case SOUND_PED_BULLET_HIT: case SOUND_PED_DEFEND: GetPhrase(sfx, ped->m_lastComment, 2953, 33); break; case SOUND_PED_BURNING: GetPhrase(sfx, ped->m_lastComment, 2914, 17); break; case SOUND_PED_FLEE_SPRINT: GetPhrase(sfx, ped->m_lastComment, 2986, 27); break; default: return NO_SAMPLE; } return sfx; } void cPedComments::Add(tPedComment *com) { uint8 index; if (m_nCommentsInBank[m_nActiveBank] >= NUM_PED_COMMENTS_SLOTS) { index = m_nIndexMap[m_nActiveBank][NUM_PED_COMMENTS_SLOTS - 1]; if (m_asPedComments[m_nActiveBank][index].m_bVolume > com->m_bVolume) return; } else { index = m_nCommentsInBank[m_nActiveBank]++; } m_asPedComments[m_nActiveBank][index] = *com; uint32 i = 0; if (index != 0) { for (i = 0; i < index; i++) { if (m_asPedComments[m_nActiveBank][m_nIndexMap[m_nActiveBank][i]].m_bVolume < m_asPedComments[m_nActiveBank][index].m_bVolume) { break; } } if (i < index) memmove(&m_nIndexMap[m_nActiveBank][i + 1], &m_nIndexMap[m_nActiveBank][i], NUM_PED_COMMENTS_SLOTS - 1 - i); } m_nIndexMap[m_nActiveBank][i] = index; } void cPedComments::Process() { uint32 sampleIndex; uint8 actualUsedBank; tPedComment *comment; bool prevUsed = false; static uint8 counter = 0; static int32 prevSamples[10]; if(AudioManager.m_nUserPause != 0) return; if(m_nCommentsInBank[m_nActiveBank]) { for(int i = 0; i < ARRAY_SIZE(prevSamples); i++) { if(m_asPedComments[m_nActiveBank][m_nIndexMap[m_nActiveBank][0]].m_nSampleIndex == prevSamples[(counter + 1 + i) % ARRAY_SIZE(prevSamples)]) { m_asPedComments[m_nActiveBank][m_nIndexMap[m_nActiveBank][0]].m_nProcess = -1; prevUsed = true; break; } } if(!prevUsed) { sampleIndex = m_asPedComments[m_nActiveBank][m_nIndexMap[m_nActiveBank][0]].m_nSampleIndex; if(!SampleManager.IsPedCommentLoaded(sampleIndex)) { #if defined(GTA_PC) && !defined(FIX_BUGS) if(!m_bDelay) #endif SampleManager.LoadPedComment(sampleIndex); } else { AudioManager.m_sQueueSample.m_nEntityIndex = m_asPedComments[m_nActiveBank][m_nIndexMap[m_nActiveBank][0]].m_nEntityIndex; AudioManager.m_sQueueSample.m_nCounter = 0; AudioManager.m_sQueueSample.m_nSampleIndex = sampleIndex; AudioManager.m_sQueueSample.m_nBankIndex = SFX_BANK_PED_COMMENTS; AudioManager.m_sQueueSample.m_nReleasingVolumeModificator = 3; AudioManager.m_sQueueSample.m_nVolume = m_asPedComments[m_nActiveBank][m_nIndexMap[m_nActiveBank][0]].m_bVolume; AudioManager.m_sQueueSample.m_fDistance = m_asPedComments[m_nActiveBank][m_nIndexMap[m_nActiveBank][0]].m_fDistance; AudioManager.m_sQueueSample.m_nLoopCount = 1; AudioManager.m_sQueueSample.m_nLoopStart = 0; AudioManager.m_sQueueSample.m_nLoopEnd = -1; AudioManager.m_sQueueSample.m_nEmittingVolume = MAX_VOLUME; AudioManager.m_sQueueSample.m_fSpeedMultiplier = 3.0f; AudioManager.m_sQueueSample.m_fSoundIntensity = 40.0f; AudioManager.m_sQueueSample.m_bReleasingSoundFlag = true; AudioManager.m_sQueueSample.m_vecPos = m_asPedComments[m_nActiveBank][m_nIndexMap[m_nActiveBank][0]].m_vecPos; AudioManager.m_sQueueSample.m_bReverbFlag = true; AudioManager.m_sQueueSample.m_bRequireReflection = true; AudioManager.m_sQueueSample.m_bIs2D = false; #ifdef FIX_BUGS if (sampleIndex >= 8694 && sampleIndex < TOTAL_AUDIO_SAMPLES) { // check if player sfx, TODO: enum AudioManager.m_sQueueSample.m_bIs2D = true; AudioManager.m_sQueueSample.m_nOffset = 63; } #endif // FIX_BUGS AudioManager.m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(AudioManager.m_sQueueSample.m_nSampleIndex) + AudioManager.RandomDisplacement(750); if(CTimer::GetIsSlowMotionActive()) AudioManager.m_sQueueSample.m_nFrequency /= 2; m_asPedComments[m_nActiveBank][m_nIndexMap[m_nActiveBank][0]].m_nProcess = -1; prevSamples[counter++] = sampleIndex; if(counter == 10) counter = 0; AudioManager.AddSampleToRequestedQueue(); #if defined(GTA_PC) && !defined(FIX_BUGS) m_nDelayTimer = CTimer::GetTimeInMilliseconds(); m_bDelay = true; #endif } } } // Switch bank if (m_nActiveBank) { actualUsedBank = SFX_BANK_PED_COMMENTS; m_nActiveBank = SFX_BANK_0; } else { actualUsedBank = SFX_BANK_0; m_nActiveBank = SFX_BANK_PED_COMMENTS; } comment = m_asPedComments[actualUsedBank]; for (uint32 i = 0; i < m_nCommentsInBank[actualUsedBank]; i++) { if (m_asPedComments[actualUsedBank][m_nIndexMap[actualUsedBank][i]].m_nProcess > 0) { --m_asPedComments[actualUsedBank][m_nIndexMap[actualUsedBank][i]].m_nProcess; Add(&comment[m_nIndexMap[actualUsedBank][i]]); } } for (uint32 i = 0; i < NUM_PED_COMMENTS_SLOTS; i++) { m_nIndexMap[actualUsedBank][i] = NUM_PED_COMMENTS_SLOTS; } m_nCommentsInBank[actualUsedBank] = 0; #if defined(GTA_PC) && !defined(FIX_BUGS) if(m_bDelay) if(CTimer::GetTimeInMilliseconds() - m_nDelayTimer > 6000) m_bDelay = false; #endif } #undef cooldown_phrase #pragma endregion #pragma endregion All the ped audio code void cAudioManager::ProcessExplosions(int32 explosion) { uint8 type; float distSquared; for (uint8 i = 0; i < ARRAY_SIZE(gaExplosion); i++) { if (CExplosion::DoesExplosionMakeSound(i) && 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: m_sQueueSample.m_fSoundIntensity = 200.0f; m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_2; m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 19000; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bRequireReflection = true; break; case EXPLOSION_MOLOTOV: m_sQueueSample.m_fSoundIntensity = 150.0f; m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_3; m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 19000; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bRequireReflection = false; break; case EXPLOSION_MINE: case EXPLOSION_HELI_BOMB: m_sQueueSample.m_fSoundIntensity = 200.0f; m_sQueueSample.m_nSampleIndex = SFX_ROCKET_LEFT; m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 12347; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bRequireReflection = true; break; default: m_sQueueSample.m_fSoundIntensity = 200.0f; m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_1; m_sQueueSample.m_nFrequency = RandomDisplacement(1000) + 19500; if (type == EXPLOSION_HELI) m_sQueueSample.m_nFrequency = 8 * m_sQueueSample.m_nFrequency / 10; //same *= 8 / 10; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_nBankIndex = SFX_BANK_GENERIC_EXTRA; break; } m_sQueueSample.m_vecPos = *CExplosion::GetExplosionPosition(i); distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distSquared < SQR(m_sQueueSample.m_fSoundIntensity)) { m_sQueueSample.m_fDistance = Sqrt(distSquared); m_sQueueSample.m_nVolume = ComputeVolume(MAX_VOLUME, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = i; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_nEmittingVolume = MAX_VOLUME; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_bReverbFlag = true; AddSampleToRequestedQueue(); } } } } } void cAudioManager::ProcessFires(int32) { CEntity *entity; uint8 emittingVol; float distSquared; for (uint8 i = 0; i < NUM_FIRES; i++) { if (gFireManager.m_aFires[i].m_bIsOngoing && gFireManager.m_aFires[i].m_bAudioSet) { entity = gFireManager.m_aFires[i].m_pEntity; if (entity) { switch (entity->GetType()) { case ENTITY_TYPE_BUILDING: m_sQueueSample.m_fSoundIntensity = 80.0f; m_sQueueSample.m_nSampleIndex = SFX_CAR_ON_FIRE; emittingVol = 100; m_sQueueSample.m_nFrequency = 8 * SampleManager.GetSampleBaseFrequency(SFX_CAR_ON_FIRE) / 10; m_sQueueSample.m_nFrequency += i * (m_sQueueSample.m_nFrequency / 64); m_sQueueSample.m_nReleasingVolumeModificator = 6; break; case ENTITY_TYPE_PED: m_sQueueSample.m_fSoundIntensity = 25.0f; m_sQueueSample.m_nSampleIndex = SFX_PED_ON_FIRE; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PED_ON_FIRE); emittingVol = 60; m_sQueueSample.m_nFrequency += i * (m_sQueueSample.m_nFrequency / 64); m_sQueueSample.m_nReleasingVolumeModificator = 10; break; default: m_sQueueSample.m_fSoundIntensity = 80.0f; m_sQueueSample.m_nSampleIndex = SFX_CAR_ON_FIRE; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_ON_FIRE); m_sQueueSample.m_nFrequency += i * (m_sQueueSample.m_nFrequency / 64); emittingVol = 80; m_sQueueSample.m_nReleasingVolumeModificator = 8; } } else { m_sQueueSample.m_fSoundIntensity = 80.0f; m_sQueueSample.m_nSampleIndex = SFX_CAR_ON_FIRE; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_ON_FIRE); emittingVol = 80; m_sQueueSample.m_nReleasingVolumeModificator = 8; } m_sQueueSample.m_vecPos = gFireManager.m_aFires[i].m_vecPos; distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distSquared < SQR(m_sQueueSample.m_fSoundIntensity)) { m_sQueueSample.m_fDistance = Sqrt(distSquared); m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = i; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_nReleasingVolumeDivider = 10; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } if (gFireManager.m_aFires[i].m_bExtinguishedWithWater) { gFireManager.m_aFires[i].m_bExtinguishedWithWater = false; emittingVol = 100.0f * gFireManager.m_aFires[i].m_fWaterExtinguishCountdown; m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nSampleIndex = SFX_JUMBO_TAXI; m_sQueueSample.m_nFrequency = 19591; m_sQueueSample.m_nFrequency += i * (m_sQueueSample.m_nFrequency / 64); m_sQueueSample.m_nReleasingVolumeModificator = 9; m_sQueueSample.m_nCounter = i + 40; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_nReleasingVolumeDivider = 10; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } } } void cAudioManager::ProcessWaterCannon(int32) { const float SOUND_INTENSITY = 30.0f; for (int32 i = 0; i < NUM_WATERCANNONS; i++) { if (CWaterCannons::aCannons[i].m_nId) { m_sQueueSample.m_vecPos = CWaterCannons::aCannons[0].m_avecPos[CWaterCannons::aCannons[i].m_nCur]; float distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distSquared < SQR(SOUND_INTENSITY)) { m_sQueueSample.m_fDistance = Sqrt(distSquared); m_sQueueSample.m_nVolume = ComputeVolume(50, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_nSampleIndex = SFX_JUMBO_TAXI; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = 15591; m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_nCounter = i; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_nReleasingVolumeDivider = 8; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nEmittingVolume = 50; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } } } //positon of arcade machines CVector aVecExtraSoundPosition[] = { CVector(-1042.546f, 88.794f, 11.324f), CVector(-1004.476f, 181.697f, 11.324f) }; void cAudioManager::ProcessExtraSounds() { const float SOUND_INTENSITY = 18.0f; const uint8 EMITTING_VOLUME = 50; float distance; for (int i = 0; i < ARRAY_SIZE(aVecExtraSoundPosition); i++) { m_sQueueSample.m_vecPos = aVecExtraSoundPosition[i]; distance = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distance < SQR(SOUND_INTENSITY)) { m_sQueueSample.m_fDistance = Sqrt(distance); m_sQueueSample.m_nVolume = ComputeVolume(EMITTING_VOLUME, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = i; m_sQueueSample.m_nSampleIndex = SFX_ARCADE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ARCADE); m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeModificator = 4; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_nEmittingVolume = EMITTING_VOLUME; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_ARCADE); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_ARCADE); m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bRequireReflection = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; AddSampleToRequestedQueue(); } } } } void cAudioManager::ProcessEscalators() { const float SOUND_INTENSITY = 30.0f; const uint8 EMITTING_VOLUME = 26; float distance; for (int i = 0; i < CEscalators::NumEscalators; i++) { if (!CEscalators::GetEscalator(i).IsActive()) continue; m_sQueueSample.m_vecPos = CEscalators::GetEscalator(i).GetPosition(); distance = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distance < SQR(SOUND_INTENSITY)) { m_sQueueSample.m_fDistance = Sqrt(distance); m_sQueueSample.m_nVolume = ComputeVolume(EMITTING_VOLUME, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nSampleIndex = SFX_BOAT_V12_LOOP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = i * 50 % 250 + 3973; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_nReleasingVolumeDivider = 5; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_nCounter = i; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = EMITTING_VOLUME; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_BOAT_V12_LOOP); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_BOAT_V12_LOOP); m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } } #pragma region SCRIPT_OBJECTS const int SCRIPT_OBJECT_INTENSITY_S = 30; const int SCRIPT_OBJECT_INTENSITY_L = 80; void cAudioManager::ProcessScriptObject(int32 id) { if (MusicManager.m_nMusicMode == MUSICMODE_GAME) { cAudioScriptObject* entity = (cAudioScriptObject*)m_asAudioEntities[id].m_pEntity; if (entity != nil) { m_sQueueSample.m_vecPos = entity->Posn; if (m_asAudioEntities[id].m_AudioEvents == 1) ProcessOneShotScriptObject(m_asAudioEntities[id].m_awAudioEvent[0]); else ProcessLoopingScriptObject(entity->AudioId); } } } void cAudioManager::ProcessOneShotScriptObject(uint8 sound) { CPlayerPed *playerPed; uint8 emittingVolume; float distSquared; static uint8 iSound = 0; switch (sound) { case SCRIPT_SOUND_POLICE_CELL_DOOR_CLUNK: m_sQueueSample.m_fSoundIntensity = 40.0f; m_sQueueSample.m_nSampleIndex = SFX_COL_GATE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = 10600; m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 3; emittingVolume = 60; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = true; break; case SCRIPT_SOUND_GARAGE_DOOR_CLUNK: m_sQueueSample.m_fSoundIntensity = 80.0f; m_sQueueSample.m_nSampleIndex = SFX_COL_CAR_PANEL_2; // huh? m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = 22000; m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 4; emittingVolume = 60; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = true; break; case SCRIPT_SOUND_SHOOTING_RANGE_TARGET_HIT: case SCRIPT_SOUND_BULLET_HIT_GROUND_1: case SCRIPT_SOUND_BULLET_HIT_GROUND_2: case SCRIPT_SOUND_BULLET_HIT_GROUND_3: m_sQueueSample.m_fSoundIntensity = 50.0f; m_sQueueSample.m_nSampleIndex = m_anRandomTable[iSound % 5] % 3 + SFX_BULLET_WALL_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); m_sQueueSample.m_nReleasingVolumeModificator = 9; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; emittingVolume = m_anRandomTable[2] % 20 + 90; break; case SCRIPT_SOUND_WILLIE_CARD_SWIPE: emittingVolume = 70; m_sQueueSample.m_fSoundIntensity = 40.0f; m_sQueueSample.m_nSampleIndex = SFX_BOMB_BEEP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = 20159; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_fSpeedMultiplier = 1.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = false; break; case SCRIPT_SOUND_MALE_AMBULANCE_OUCH: { cPedParams pedParams; pedParams.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); SetupPedComments(pedParams, SOUND_INJURED_PED_MALE_OUCH); return; } case SCRIPT_SOUND_FEMALE_AMBULANCE_OUCH: { cPedParams pedParams; pedParams.m_fDistance = GetDistanceSquared(m_sQueueSample.m_vecPos); SetupPedComments(pedParams, SOUND_INJURED_PED_FEMALE); return; } case SCRIPT_SOUND_SEAPLANE_LOW_FUEL: m_sQueueSample.m_fSoundIntensity = 1000.0f; m_sQueueSample.m_nSampleIndex = SFX_SEAPLANE_LOW; m_sQueueSample.m_nBankIndex = SFX_BANK_0; emittingVolume = 100; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_CAR_HORN_JEEP); // BUG? m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = true; m_sQueueSample.m_bRequireReflection = false; break; case SCRIPT_SOUND_PAYPHONE_RINGING: m_sQueueSample.m_fSoundIntensity = 80.0f; m_sQueueSample.m_nSampleIndex = SFX_PHONE_RING; m_sQueueSample.m_nBankIndex = SFX_BANK_0; emittingVolume = 80; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PHONE_RING); m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = false; break; case SCRIPT_SOUND_GLASS_BREAK_L: m_sQueueSample.m_fSoundIntensity = 60.0f; m_sQueueSample.m_nSampleIndex = SFX_GLASS_SMASH; m_sQueueSample.m_nBankIndex = SFX_BANK_0; emittingVolume = 70; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GLASS_SMASH); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_GLASS_BREAK_S: m_sQueueSample.m_fSoundIntensity = 60.0f; m_sQueueSample.m_nSampleIndex = SFX_GLASS_SMASH; m_sQueueSample.m_nBankIndex = SFX_BANK_0; emittingVolume = 60; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GLASS_SMASH); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_GLASS_CRACK: m_sQueueSample.m_fSoundIntensity = 60.0f; m_sQueueSample.m_nSampleIndex = SFX_GLASS_CRACK; m_sQueueSample.m_nBankIndex = SFX_BANK_0; emittingVolume = 70; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GLASS_CRACK); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = true; break; case SCRIPT_SOUND_GLASS_LIGHT_BREAK: m_sQueueSample.m_fSoundIntensity = 55.0f; m_sQueueSample.m_nSampleIndex = (m_anRandomTable[4] & 3) + SFX_GLASS_SHARD_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = RandomDisplacement(2000) + 19000; m_sQueueSample.m_nReleasingVolumeModificator = 9; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; emittingVolume = RandomDisplacement(11) + 25; break; case SCRIPT_SOUND_BOX_DESTROYED_1: m_sQueueSample.m_fSoundIntensity = 60.0f; m_sQueueSample.m_nSampleIndex = SFX_WOODEN_BOX_SMASH; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = RandomDisplacement(1500) + 18600; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = true; emittingVolume = m_anRandomTable[2] % 20 + 80; break; case SCRIPT_SOUND_BOX_DESTROYED_2: m_sQueueSample.m_fSoundIntensity = 60.0f; m_sQueueSample.m_nSampleIndex = SFX_CARDBOARD_BOX_SMASH; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = RandomDisplacement(1500) + 18600; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = true; emittingVolume = m_anRandomTable[2] % 20 + 80; break; case SCRIPT_SOUND_METAL_COLLISION: m_sQueueSample.m_fSoundIntensity = 60.0f; m_sQueueSample.m_nSampleIndex = m_anRandomTable[3] % 5 + SFX_COL_CAR_1; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = true; emittingVolume = m_anRandomTable[2] % 30 + 70; break; case SCRIPT_SOUND_TIRE_COLLISION: m_sQueueSample.m_fSoundIntensity = 60.0f; m_sQueueSample.m_nSampleIndex = SFX_TYRE_BUMP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = true; emittingVolume = m_anRandomTable[2] % 30 + 60; break; case SCRIPT_SOUND_HIT_BALL: m_sQueueSample.m_fSoundIntensity = 60.0f; m_sQueueSample.m_nSampleIndex = SFX_HIT_BALL; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nReleasingVolumeModificator = 5; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bRequireReflection = true; 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_GRAVEL: case SURFACE_MUD_DRY: case SURFACE_TRANSPARENT_CLOTH: case SURFACE_PED: case SURFACE_SAND: case SURFACE_RUBBER: case SURFACE_HEDGE: case SURFACE_SAND_BEACH: m_sQueueSample.m_nSampleIndex = SFX_BULLET_SHELL_HIT_GROUND_2; m_sQueueSample.m_nFrequency = RandomDisplacement(600) + 10600; m_sQueueSample.m_nReleasingVolumeModificator = 18; break; case SURFACE_WATER: return; default: m_sQueueSample.m_nSampleIndex = SFX_BULLET_SHELL_HIT_GROUND_1; m_sQueueSample.m_nFrequency = RandomDisplacement(1500) + 30000; m_sQueueSample.m_nReleasingVolumeModificator = 15; break; } } else { m_sQueueSample.m_nSampleIndex = SFX_BULLET_SHELL_HIT_GROUND_1; m_sQueueSample.m_nFrequency = RandomDisplacement(1500) + 30000; m_sQueueSample.m_nReleasingVolumeModificator = 15; } m_sQueueSample.m_fSoundIntensity = 20.0f; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; emittingVolume = m_anRandomTable[2] % 20 + 30; break; case SCRIPT_SOUND_GUNSHELL_DROP_SOFT: m_sQueueSample.m_nSampleIndex = SFX_BULLET_SHELL_HIT_GROUND_2; m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 11000; m_sQueueSample.m_nReleasingVolumeModificator = 18; m_sQueueSample.m_fSoundIntensity = 20.0f; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_bIs2D = false; emittingVolume = m_anRandomTable[2] % 20 + 30; break; default: return; } distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distSquared < SQR(m_sQueueSample.m_fSoundIntensity)) { m_sQueueSample.m_fDistance = Sqrt(distSquared); m_sQueueSample.m_nVolume = ComputeVolume(emittingVolume, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = iSound++; m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_nEmittingVolume = emittingVolume; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_bReverbFlag = true; AddSampleToRequestedQueue(); } } } void cAudioManager::ProcessLoopingScriptObject(uint8 sound) { uint8 emittingVolume; float distSquared; switch(sound) { case SCRIPT_SOUND_BANK_ALARM_LOOP: m_sQueueSample.m_fSoundIntensity = 80.0f; m_sQueueSample.m_nSampleIndex = SFX_BUILDINGS_BANK_ALARM; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BANK_ALARM; emittingVolume = 90; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BUILDINGS_BANK_ALARM); m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_POLICE_CELL_DOOR_SLIDING_LOOP: case SCRIPT_SOUND_GARAGE_DOOR_SLIDING_LOOP: m_sQueueSample.m_fSoundIntensity = 80.0f; m_sQueueSample.m_nSampleIndex = SFX_GARAGE_DOOR_LOOP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; emittingVolume = 90; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_GARAGE_DOOR_LOOP); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_SNORING_LOOP: m_sQueueSample.m_fSoundIntensity = 6.0f; m_sQueueSample.m_nSampleIndex = SFX_BUILDING_SNORE; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_SNORING; emittingVolume = 25; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BUILDING_SNORE); m_sQueueSample.m_nReleasingVolumeModificator = 6; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_fSpeedMultiplier = 3.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_SHOOTING_RANGE_TARGET_MOVING_LOOP: m_sQueueSample.m_fSoundIntensity = 40.0f; m_sQueueSample.m_nSampleIndex = SFX_TANK_TURRET; m_sQueueSample.m_nBankIndex = SFX_BANK_0; emittingVolume = 60; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_TANK_TURRET); m_sQueueSample.m_nReleasingVolumeModificator = 4; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_BAR_1: m_sQueueSample.m_nSampleIndex = SFX_BUILDING_BAR_1; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BAR_1; m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_BAR_2: m_sQueueSample.m_nSampleIndex = SFX_BUILDING_BAR_2; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BAR_2; m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_BAR_3: m_sQueueSample.m_nSampleIndex = SFX_BUILDING_BAR_3; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BAR_3; m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_BAR_4: m_sQueueSample.m_nSampleIndex = SFX_BUILDING_BAR_4; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_BAR_4; m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_MALIBU_1: if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_MALIBU_AMBIENT) return; m_sQueueSample.m_nSampleIndex = SFX_BUILDING_MAL1; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_MALIBU_1; MusicManager.SetMalibuClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_MALIBU_1); m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_MALIBU_2: if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_MALIBU_AMBIENT) return; m_sQueueSample.m_nSampleIndex = SFX_BUILDING_MAL2; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_MALIBU_2; MusicManager.SetMalibuClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_MALIBU_2); m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_MALIBU_3: if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_MALIBU_AMBIENT) return; m_sQueueSample.m_nSampleIndex = SFX_BUILDING_MAL3; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_MALIBU_3; MusicManager.SetMalibuClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_MALIBU_3); m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_STRIP_1: if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_STRIPCLUB_AMBIENT) return; m_sQueueSample.m_nSampleIndex = SFX_BUILDING_STR1; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_STRIP_1; MusicManager.SetStripClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_STRIP_1); m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_STRIP_2: if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_STRIPCLUB_AMBIENT) return; m_sQueueSample.m_nSampleIndex = SFX_BUILDING_STR2; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_STRIP_2; MusicManager.SetStripClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_STRIP_2); m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_STRIP_3: if(MusicManager.m_nPlayingTrack == STREAMED_SOUND_STRIPCLUB_AMBIENT) return; m_sQueueSample.m_nSampleIndex = SFX_BUILDING_STR3; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_STRIP_3; MusicManager.SetStripClubTrackPos(SCRIPT_SOUND_NEW_BUILDING_STRIP_3); m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_BUILDING_CHURCH: m_sQueueSample.m_nSampleIndex = SFX_BUILDING_CHURCH; m_sQueueSample.m_nBankIndex = SFX_BANK_BUILDING_CHURCH; m_sQueueSample.m_fSoundIntensity = 80.0f; emittingVolume = 127; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nReleasingVolumeDivider = 15; m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_bIs2D = false; break; case SCRIPT_SOUND_NEW_WATERFALL: emittingVolume = 30; m_sQueueSample.m_fSoundIntensity = 80.0f; m_sQueueSample.m_nSampleIndex = SFX_BOAT_WATER_LOOP; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = 20812; m_sQueueSample.m_nReleasingVolumeModificator = 4; m_sQueueSample.m_nReleasingVolumeDivider = 9; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_bIs2D = false; break; default: return; } distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); if(distSquared < SQR(m_sQueueSample.m_fSoundIntensity)) { m_sQueueSample.m_fDistance = Sqrt(distSquared); m_sQueueSample.m_nVolume = ComputeVolume(emittingVolume, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if(m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_nEmittingVolume = emittingVolume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } #pragma endregion All the code for script object audio on the map void cAudioManager::ProcessWeather(int32 id) { uint8 vol; float x; float y; float modifier; float wind; static uint8 iSound = 0; if (m_asAudioEntities[id].m_AudioEvents != 0 && m_asAudioEntities[id].m_awAudioEvent[0] == SOUND_LIGHTNING) { if (m_asAudioEntities[id].m_afVolume[0] >= 10.f) { m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_1; m_sQueueSample.m_nBankIndex = SFX_BANK_GENERIC_EXTRA; m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 4000; vol = (m_asAudioEntities[id].m_afVolume[0] - 10.0f) + 40; } else { m_sQueueSample.m_nSampleIndex = SFX_EXPLOSION_2; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = RandomDisplacement(500) + 4000; vol = (m_asAudioEntities[id].m_afVolume[0]) + 35; } m_sQueueSample.m_nVolume = vol; if (TheCamera.SoundDistUp < 20.0f) m_sQueueSample.m_nVolume /= 2; if (iSound == 4) iSound = 0; m_sQueueSample.m_nCounter = iSound++; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_nOffset = (m_anRandomTable[2] & 15) + 55; m_sQueueSample.m_bIs2D = true; m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_nEmittingVolume = m_sQueueSample.m_nVolume; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_bReverbFlag = false; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } if (CWeather::Rain > 0.0f && (!CCullZones::CamNoRain() || !CCullZones::PlayerNoRain())) { m_sQueueSample.m_nSampleIndex = SFX_RAIN; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_RAIN); m_sQueueSample.m_nVolume = (uint8)(25.0f * CWeather::Rain); m_sQueueSample.m_nCounter = 4; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_nOffset = 63; m_sQueueSample.m_bIs2D = true; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 30; m_sQueueSample.m_bReverbFlag = false; m_sQueueSample.m_nEmittingVolume = m_sQueueSample.m_nVolume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } x = 0.0f; y = 0.0f; CWindModifiers::FindWindModifier(TheCamera.GetPosition(), &x, &y); modifier = Max(Abs(x), Abs(y)) * 10.0f; modifier = Min(1.0f, modifier); wind = Max(CWeather::Wind, modifier); if (wind > 0.0f && CObject::fDistToNearestTree < 75.0f) { m_sQueueSample.m_nSampleIndex = SFX_PALM_TREE_LO; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_PALM_TREE_LO); m_sQueueSample.m_nVolume = (m_anRandomTable[1] % 10 + 45.0f) * (75.0f - CObject::fDistToNearestTree) * (4.0f / 300.0f) * wind; m_sQueueSample.m_nCounter = 5; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nOffset = 63; m_sQueueSample.m_bIs2D = true; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 7; m_sQueueSample.m_bReverbFlag = false; m_sQueueSample.m_nEmittingVolume = m_sQueueSample.m_nVolume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); CObject::fDistToNearestTree = 999999.9f; } } void cAudioManager::ProcessFrontEnd() { bool stereo; bool processedPickup; bool processedMission; bool staticFreq; bool center; int16 sample; static uint8 iSound = 0; static uint32 cPickupNextFrame = 0; static uint32 cPartMisComNextFrame = 0; static uint32 radioDial = SFX_RADIO_DIAL_1; for (uint32 i = 0; i < m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents; i++) { staticFreq = false; processedPickup = false; center = false; processedMission = false; stereo = false; switch (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]) { case SOUND_WEAPON_SNIPER_SHOT_NO_ZOOM: m_sQueueSample.m_nSampleIndex = SFX_ERROR_FIRE_RIFLE; break; case SOUND_WEAPON_ROCKET_SHOT_NO_ZOOM: m_sQueueSample.m_nSampleIndex = SFX_ERROR_FIRE_ROCKET_LAUNCHER; break; case SOUND_GARAGE_NO_MONEY: case SOUND_GARAGE_BAD_VEHICLE: case SOUND_GARAGE_BOMB_ALREADY_SET: m_sQueueSample.m_nSampleIndex = SFX_WEAPON_LEFT; stereo = true; staticFreq = true; center = true; break; case SOUND_GARAGE_OPENING: case SOUND_71: //case SOUND_41: case SOUND_GARAGE_VEHICLE_DECLINED: case SOUND_GARAGE_VEHICLE_ACCEPTED: case SOUND_EVIDENCE_PICKUP: case SOUND_UNLOAD_GOLD: stereo = true; processedPickup = true; m_sQueueSample.m_nSampleIndex = SFX_MONEY_LEFT; break; case SOUND_GARAGE_BOMB1_SET: case SOUND_GARAGE_BOMB2_SET: case SOUND_GARAGE_BOMB3_SET: case SOUND_PICKUP_WEAPON_BOUGHT: case SOUND_PICKUP_WEAPON: center = true; processedPickup = true; m_sQueueSample.m_nSampleIndex = SFX_WEAPON_LEFT; stereo = true; break; case SOUND_PICKUP_HEALTH: case SOUND_81: //case SOUND_4B: case SOUND_PICKUP_ADRENALINE: case SOUND_PICKUP_ARMOUR: stereo = true; processedPickup = true; m_sQueueSample.m_nSampleIndex = SFX_MONEY_LEFT; break; case SOUND_80: stereo = true; processedPickup = true; m_sQueueSample.m_nSampleIndex = SFX_WEAPON_LEFT; center = true; staticFreq = true; break; case SOUND_PICKUP_BONUS: case SOUND_FRONTEND_MENU_STARTING: case SOUND_HUD: stereo = true; m_sQueueSample.m_nSampleIndex = SFX_INFO_LEFT; center = true; break; case SOUND_PICKUP_MONEY: stereo = true; processedPickup = true; m_sQueueSample.m_nSampleIndex = SFX_MONEY_LEFT; break; case SOUND_PICKUP_HIDDEN_PACKAGE: case SOUND_PICKUP_PACMAN_PILL: case SOUND_PICKUP_PACMAN_PACKAGE: case SOUND_PICKUP_FLOAT_PACKAGE: center = true; processedPickup = true; m_sQueueSample.m_nSampleIndex = SFX_PART_MISSION_COMPLETE_LEFT; stereo = true; break; case SOUND_RACE_START_3: case SOUND_RACE_START_2: case SOUND_RACE_START_1: case SOUND_PART_MISSION_COMPLETE: stereo = true; m_sQueueSample.m_nSampleIndex = SFX_PART_MISSION_COMPLETE_LEFT; processedMission = true; center = true; break; case SOUND_RACE_START_GO: stereo = true; m_sQueueSample.m_nSampleIndex = SFX_GO_LEFT; center = true; break; case SOUND_CLOCK_TICK: m_sQueueSample.m_nSampleIndex = SFX_TIMER; break; case SOUND_FRONTEND_RADIO_TURN_OFF: case SOUND_FRONTEND_RADIO_TURN_ON: m_sQueueSample.m_nSampleIndex = SFX_RADIO_CLICK; break; case SOUND_FRONTEND_HURRICANE: m_sQueueSample.m_nSampleIndex = SFX_HURRICANE_MA; break; case SOUND_BULLETTRACE_1: case SOUND_BULLETTRACE_2: m_sQueueSample.m_nSampleIndex = (m_anRandomTable[0] % 2) + SFX_BULLET_PASS_1; break; case SOUND_AMMUNATION_IMRAN_ARM_BOMB: m_sQueueSample.m_nSampleIndex = SFX_ARM_BOMB; break; case SOUND_RADIO_CHANGE: m_sQueueSample.m_nSampleIndex = (m_anRandomTable[1] % 2) ? radioDial + 1 : radioDial + 2; if (m_sQueueSample.m_nSampleIndex > SFX_RADIO_DIAL_12) m_sQueueSample.m_nSampleIndex -= 12; radioDial = m_sQueueSample.m_nSampleIndex; break; case SOUND_FRONTEND_HIGHLIGHT_OPTION: stereo = true; m_sQueueSample.m_nSampleIndex = SFX_FE_HIGHLIGHT_LEFT; break; case SOUND_FRONTEND_ENTER_OR_ADJUST: stereo = true; m_sQueueSample.m_nSampleIndex = SFX_FE_SELECT_LEFT; break; case SOUND_FRONTEND_BACK: stereo = true; m_sQueueSample.m_nSampleIndex = SFX_FE_BACK_LEFT; break; case SOUND_FRONTEND_FAIL: stereo = true; m_sQueueSample.m_nSampleIndex = SFX_FE_ERROR_LEFT; break; case SOUND_FRONTEND_AUDIO_TEST: m_sQueueSample.m_nSampleIndex = m_anRandomTable[0] % 3 + SFX_FE_NOISE_BURST_1; break; default: continue; } if (processedPickup) { if (m_FrameCounter <= cPickupNextFrame) continue; cPickupNextFrame = m_FrameCounter + 5; } else if (processedMission) { if (m_FrameCounter <= cPartMisComNextFrame) continue; cPartMisComNextFrame = m_FrameCounter + 5; } sample = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]; if (sample == SOUND_FRONTEND_RADIO_TURN_OFF) m_sQueueSample.m_nFrequency = 28509; else if (sample == SOUND_FRONTEND_RADIO_TURN_ON) m_sQueueSample.m_nFrequency = 32000; else if (sample == SOUND_BULLETTRACE_1 || sample == SOUND_BULLETTRACE_2) { m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 32); } else if (staticFreq) m_sQueueSample.m_nFrequency = 5382; else m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nVolume = 127; if (m_sQueueSample.m_nSampleIndex == SFX_HURRICANE_MA && CWeather::Wind > 1.0f) m_sQueueSample.m_nVolume = (CWeather::Wind - 1.0f) * m_sQueueSample.m_nVolume; m_sQueueSample.m_nCounter = iSound++; m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_nBankIndex = SFX_BANK_FRONT_END_MENU; m_sQueueSample.m_nReleasingVolumeModificator = 0; m_sQueueSample.m_bIs2D = true; m_sQueueSample.m_nEmittingVolume = m_sQueueSample.m_nVolume; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_fDistance = 1.0f; if (stereo) m_sQueueSample.m_nOffset = 0; else { sample = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i]; if (sample == SOUND_BULLETTRACE_1) { m_sQueueSample.m_nOffset = 20; m_sQueueSample.m_nVolume = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; m_sQueueSample.m_nReleasingVolumeModificator = 10; m_sQueueSample.m_fDistance = 100.0f; } if (sample == SOUND_BULLETTRACE_2) { m_sQueueSample.m_nOffset = 107; m_sQueueSample.m_nVolume = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_afVolume[i]; m_sQueueSample.m_nReleasingVolumeModificator = 10; m_sQueueSample.m_fDistance = 100.0f; } m_sQueueSample.m_nOffset = 63; } m_sQueueSample.m_bReverbFlag = false; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); if (stereo) { ++m_sQueueSample.m_nSampleIndex; m_sQueueSample.m_nCounter = iSound++; m_sQueueSample.m_nOffset = 127 - m_sQueueSample.m_nOffset; AddSampleToRequestedQueue(); } if (center) { ++m_sQueueSample.m_nSampleIndex; m_sQueueSample.m_nCounter = iSound++; m_sQueueSample.m_nOffset = 63; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); AddSampleToRequestedQueue(); } } } /*void cAudioManager::ProcessCrane() { CCrane *crane = (CCrane *)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_pEntity; float distSquared; bool distCalculated = false; static const int intensity = 80; if (crane) { if (crane->m_nCraneStatus == CCrane::ACTIVATED) { if (crane->m_nCraneState != CCrane::IDLE) { m_sQueueSample.m_vecPos = crane->m_pCraneEntity->GetPosition(); distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distSquared < SQR(intensity)) { CalculateDistance(distCalculated, distSquared); m_sQueueSample.m_nVolume = ComputeVolume(100, 80.f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 0; m_sQueueSample.m_nSampleIndex = SFX_CRANE_MAGNET; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 2; m_sQueueSample.m_nFrequency = 6000; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = 100; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_fSoundIntensity = intensity; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents) { m_sQueueSample.m_nCounter = 1; m_sQueueSample.m_nSampleIndex = SFX_COL_CAR_2; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_COL_CAR_2); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = true; AddSampleToRequestedQueue(); } } } } } }*/ void cAudioManager::ProcessProjectiles() { uint8 emittingVol; float distSquared; for (int32 i = 0; i < NUM_PROJECTILES; i++) { if (CProjectileInfo::GetProjectileInfo(i)->m_bInUse) { switch (CProjectileInfo::GetProjectileInfo(i)->m_eWeaponType) { case WEAPONTYPE_TEARGAS: emittingVol = 80; m_sQueueSample.m_fSoundIntensity = 40.0f; m_sQueueSample.m_nSampleIndex = SFX_PALM_TREE_LO; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = 13879; m_sQueueSample.m_nReleasingVolumeModificator = 7; break; case WEAPONTYPE_MOLOTOV: emittingVol = 50; m_sQueueSample.m_fSoundIntensity = 30.0f; m_sQueueSample.m_nSampleIndex = SFX_PED_ON_FIRE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = 32 * SampleManager.GetSampleBaseFrequency(SFX_PED_ON_FIRE) / 25; m_sQueueSample.m_nReleasingVolumeModificator = 7; break; case WEAPONTYPE_ROCKET: emittingVol = 127; m_sQueueSample.m_fSoundIntensity = 90.0f; m_sQueueSample.m_nSampleIndex = SFX_ROCKET_FLY; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_ROCKET_FLY); m_sQueueSample.m_nReleasingVolumeModificator = 3; break; default: return; } m_sQueueSample.m_fSpeedMultiplier = 4.0f; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_vecPos = CProjectileInfo::ms_apProjectile[i]->GetPosition(); distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distSquared < SQR(m_sQueueSample.m_fSoundIntensity)) { m_sQueueSample.m_fDistance = Sqrt(distSquared); m_sQueueSample.m_nVolume = ComputeVolume(emittingVol, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = i; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = emittingVol; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } } } void cAudioManager::ProcessGarages() { const float SOUND_INTENSITY = 80.0f; CEntity *entity; uint8 state; uint32 sampleIndex; uint8 j; float distSquared; bool distCalculated; static uint8 iSound = 32; for (uint32 i = 0; i < CGarages::NumGarages; ++i) { if (CGarages::aGarages[i].m_eGarageType == GARAGE_NONE) continue; entity = CGarages::aGarages[i].m_pDoor1; if (entity == nil) continue; m_sQueueSample.m_vecPos = entity->GetPosition(); distCalculated = false; distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distSquared < SQR(SOUND_INTENSITY)) { state = CGarages::aGarages[i].m_eGarageState; // while is here just to exit prematurely and avoid goto while (state == GS_OPENING || state == GS_CLOSING || state == GS_AFTERDROPOFF) { CalculateDistance(distCalculated, distSquared); m_sQueueSample.m_nVolume = ComputeVolume(90, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { if (CGarages::aGarages[i].m_eGarageType == GARAGE_CRUSHER) { if (CGarages::aGarages[i].m_eGarageState == GS_AFTERDROPOFF) { if (m_FrameCounter & 1) { if (m_anRandomTable[1] & 1) sampleIndex = m_anRandomTable[2] % 5 + SFX_COL_CAR_1; else sampleIndex = m_anRandomTable[2] % 6 + SFX_COL_CAR_PANEL_1; m_sQueueSample.m_nSampleIndex = sampleIndex; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex) / 2; m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 16); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_nCounter = iSound++; if (iSound < 32) iSound = 32; } else break; // premature exit to go straight to the for loop } else { m_sQueueSample.m_nSampleIndex = SFX_FISHING_BOAT_IDLE; m_sQueueSample.m_nFrequency = 6543; m_sQueueSample.m_nCounter = i; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReleasingSoundFlag = false; } } else { m_sQueueSample.m_nSampleIndex = SFX_GARAGE_DOOR_LOOP; m_sQueueSample.m_nFrequency = 13961; m_sQueueSample.m_nCounter = i; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReleasingSoundFlag = false; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 3; m_sQueueSample.m_nEmittingVolume = 90; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } break; } } for (j = 0; j < m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_AudioEvents; ++j) { switch (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[j]) { case SOUND_GARAGE_DOOR_CLOSED: case SOUND_GARAGE_DOOR_OPENED: if (distSquared < SQR(SOUND_INTENSITY)) { CalculateDistance(distCalculated, distSquared); m_sQueueSample.m_nVolume = ComputeVolume(60, SOUND_INTENSITY, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { if (CGarages::aGarages[i].m_eGarageType == GARAGE_CRUSHER) { m_sQueueSample.m_nSampleIndex = SFX_COL_CAR_PANEL_2; m_sQueueSample.m_nFrequency = 6735; } else if (m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[j] == SOUND_GARAGE_DOOR_OPENED) { m_sQueueSample.m_nSampleIndex = SFX_COL_CAR_PANEL_2; m_sQueueSample.m_nFrequency = 22000; } else { m_sQueueSample.m_nSampleIndex = SFX_COL_GARAGE_DOOR_1; m_sQueueSample.m_nFrequency = 18000; } m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_nReleasingVolumeModificator = 4; m_sQueueSample.m_nEmittingVolume = 60; m_sQueueSample.m_fSpeedMultiplier = 0.0f; m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReverbFlag = true; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_nCounter = iSound++; if (iSound < 32) iSound = 32; m_sQueueSample.m_bRequireReflection = true; AddSampleToRequestedQueue(); } } break; default: break; } } } } void cAudioManager::ProcessFireHydrant() { const float SOUND_INTENSITY = 35; float distSquared; bool distCalculated = false; m_sQueueSample.m_vecPos = ((CEntity *)m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_pEntity)->GetPosition(); distSquared = GetDistanceSquared(m_sQueueSample.m_vecPos); if (distSquared < SQR(SOUND_INTENSITY)) { CalculateDistance(distCalculated, distSquared); m_sQueueSample.m_nVolume = ComputeVolume(40, 35.0f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nSampleIndex = SFX_JUMBO_TAXI; m_sQueueSample.m_nReleasingVolumeModificator = 4; m_sQueueSample.m_nFrequency = 15591; m_sQueueSample.m_nCounter = 0; m_sQueueSample.m_nEmittingVolume = 40; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSoundIntensity = SOUND_INTENSITY; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_bRequireReflection = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_fSpeedMultiplier = 2.0f; AddSampleToRequestedQueue(); } } } #ifdef GTA_BRIDGE #pragma region BRIDGE const int bridgeIntensity = 400; void cAudioManager::ProcessBridge() { float dist; bool distCalculated = false; if (CBridge::pLiftRoad) { m_sQueueSample.m_vecPos = CBridge::pLiftRoad->GetPosition(); dist = GetDistanceSquared(m_sQueueSample.m_vecPos); if (dist < SQR(450.0f)) { CalculateDistance(distCalculated, dist); switch (CBridge::State) { case STATE_BRIDGE_LOCKED: case STATE_LIFT_PART_IS_UP: case STATE_LIFT_PART_ABOUT_TO_MOVE_UP: ProcessBridgeWarning(); break; case STATE_LIFT_PART_MOVING_DOWN: case STATE_LIFT_PART_MOVING_UP: ProcessBridgeWarning(); ProcessBridgeMotor(); break; default: break; } ProcessBridgeOneShots(); } } } void cAudioManager::ProcessBridgeWarning() { if (CStats::CommercialPassed && m_sQueueSample.m_fDistance < 450.f) { m_sQueueSample.m_nVolume = ComputeVolume(100, 450.f, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 0; m_sQueueSample.m_nSampleIndex = SFX_BRIDGE_OPEN_WARNING; m_sQueueSample.m_nBankIndex = SAMPLEBANK_EXTRAS; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_BRIDGE_OPEN_WARNING); m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = 100; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = 450.0f; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 8; m_sQueueSample.m_bReverbFlag = false; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } void cAudioManager::ProcessBridgeMotor() { if (m_sQueueSample.m_fDistance < bridgeIntensity) { m_sQueueSample.m_nVolume = ComputeVolume(MAX_VOLUME, bridgeIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 1; m_sQueueSample.m_nSampleIndex = SFX_FISHING_BOAT_IDLE; // todo check sfx name m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = 5500; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = MAX_VOLUME; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = bridgeIntensity; m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bReverbFlag = false; AddSampleToRequestedQueue(); } } } void cAudioManager::ProcessBridgeOneShots() { if (CBridge::State == STATE_LIFT_PART_IS_UP && CBridge::OldState == STATE_LIFT_PART_MOVING_UP) m_sQueueSample.m_nSampleIndex = SFX_COL_CONTAINER_1; else if (CBridge::State == STATE_LIFT_PART_IS_DOWN && CBridge::OldState == STATE_LIFT_PART_MOVING_DOWN) m_sQueueSample.m_nSampleIndex = SFX_COL_CONTAINER_1; else if (CBridge::State == STATE_LIFT_PART_MOVING_UP && CBridge::OldState == STATE_LIFT_PART_ABOUT_TO_MOVE_UP) m_sQueueSample.m_nSampleIndex = SFX_COL_CONTAINER_1; else if (CBridge::State == STATE_LIFT_PART_MOVING_DOWN && CBridge::OldState == STATE_LIFT_PART_IS_UP) m_sQueueSample.m_nSampleIndex = SFX_COL_CONTAINER_1; else return; if (m_sQueueSample.m_fDistance < bridgeIntensity) { m_sQueueSample.m_nVolume = ComputeVolume(MAX_VOLUME, bridgeIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume != 0) { m_sQueueSample.m_nCounter = 2; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = false; m_sQueueSample.m_nReleasingVolumeModificator = 1; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex); m_sQueueSample.m_nLoopCount = 1; m_sQueueSample.m_nEmittingVolume = MAX_VOLUME; m_sQueueSample.m_nLoopStart = 0; m_sQueueSample.m_nLoopEnd = -1; m_sQueueSample.m_fSpeedMultiplier = 2.0f; m_sQueueSample.m_fSoundIntensity = bridgeIntensity; m_sQueueSample.m_bReleasingSoundFlag = true; m_sQueueSample.m_bReverbFlag = false; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } } } #pragma endregion #endif #pragma region MISSION_AUDIO bool g_bMissionAudioLoadFailed[MISSION_AUDIO_SLOTS]; struct MissionAudioData { const char *m_pName; int32 m_nId; }; const MissionAudioData MissionAudioNameSfxAssoc[] = { {"mobring", STREAMED_SOUND_MISSION_MOBR1}, {"pagring", STREAMED_SOUND_MISSION_PAGER}, {"carrev", STREAMED_SOUND_MISSION_CARREV}, {"bikerev", STREAMED_SOUND_MISSION_BIKEREV}, {"liftop", STREAMED_SOUND_MISSION_LIFTOP}, {"liftcl", STREAMED_SOUND_MISSION_LIFTCL}, {"liftrun", STREAMED_SOUND_MISSION_LIFTRUN}, {"liftbel", STREAMED_SOUND_MISSION_LIFTBEL}, {"inlift", STREAMED_SOUND_MISSION_INLIFT}, {"caml", STREAMED_SOUND_MISSION_CAMERAL}, {"camr", STREAMED_SOUND_MISSION_CAMERAR}, {"cheer1", STREAMED_SOUND_MISSION_CHEER1}, {"cheer2", STREAMED_SOUND_MISSION_CHEER2}, {"cheer3", STREAMED_SOUND_MISSION_CHEER3}, {"cheer4", STREAMED_SOUND_MISSION_CHEER4}, {"ooh1", STREAMED_SOUND_MISSION_OOH1}, {"ooh2", STREAMED_SOUND_MISSION_OOH2}, {"race1", STREAMED_SOUND_MISSION_RACE1}, {"race2", STREAMED_SOUND_MISSION_RACE2}, {"race3", STREAMED_SOUND_MISSION_RACE3}, {"race4", STREAMED_SOUND_MISSION_RACE4}, {"race5", STREAMED_SOUND_MISSION_RACE5}, {"race6", STREAMED_SOUND_MISSION_RACE6}, {"race7", STREAMED_SOUND_MISSION_RACE7}, {"race8", STREAMED_SOUND_MISSION_RACE8}, {"race9", STREAMED_SOUND_MISSION_RACE9}, {"race10", STREAMED_SOUND_MISSION_RACE10}, {"race11", STREAMED_SOUND_MISSION_RACE11}, {"race12", STREAMED_SOUND_MISSION_RACE12}, {"race13", STREAMED_SOUND_MISSION_RACE13}, {"race14", STREAMED_SOUND_MISSION_RACE14}, {"race15", STREAMED_SOUND_MISSION_RACE15}, {"hot1", STREAMED_SOUND_MISSION_HOT1}, {"hot2", STREAMED_SOUND_MISSION_HOT2}, {"hot3", STREAMED_SOUND_MISSION_HOT3}, {"hot4", STREAMED_SOUND_MISSION_HOT4}, {"hot5", STREAMED_SOUND_MISSION_HOT5}, {"hot6", STREAMED_SOUND_MISSION_HOT6}, {"hot7", STREAMED_SOUND_MISSION_HOT7}, {"hot8", STREAMED_SOUND_MISSION_HOT8}, {"hot9", STREAMED_SOUND_MISSION_HOT9}, {"hot10", STREAMED_SOUND_MISSION_HOT10}, {"hot11", STREAMED_SOUND_MISSION_HOT11}, {"hot12", STREAMED_SOUND_MISSION_HOT12}, {"hot13", STREAMED_SOUND_MISSION_HOT13}, {"hot14", STREAMED_SOUND_MISSION_HOT14}, {"hot15", STREAMED_SOUND_MISSION_HOT15}, {"lanstp1", STREAMED_SOUND_MISSION_LANSTP1}, {"lanstp2", STREAMED_SOUND_MISSION_LANSTP2}, {"lanamu1", STREAMED_SOUND_MISSION_LANAMU1}, {"lanamu2", STREAMED_SOUND_MISSION_LANAMU2}, {"airhrnl", STREAMED_SOUND_MISSION_AIRHORNL}, {"airhrnr", STREAMED_SOUND_MISSION_AIRHORNR}, {"sniper", STREAMED_SOUND_MISSION_SNIPSCRL}, {"snipsh", STREAMED_SOUND_MISSION_SNIPSHORT}, {"bloroof", STREAMED_SOUND_MISSION_BLOWROOF}, {"sfx_01", STREAMED_SOUND_MISSION_SFX_01}, {"sfx_02", STREAMED_SOUND_MISSION_SFX_02}, {"LAW1_1", STREAMED_SOUND_MISSION_LAW1_1}, {"LAW1_2", STREAMED_SOUND_MISSION_LAW1_2}, {"LAW1_3", STREAMED_SOUND_MISSION_LAW1_3}, {"LAW1_4", STREAMED_SOUND_MISSION_LAW1_4}, {"LAW1_5", STREAMED_SOUND_MISSION_LAW1_5}, {"LAW1_6", STREAMED_SOUND_MISSION_LAW1_6}, {"LAW1_7", STREAMED_SOUND_MISSION_LAW1_7}, {"LAW1_8", STREAMED_SOUND_MISSION_LAW1_8}, {"LAW1_9", STREAMED_SOUND_MISSION_LAW1_9}, {"LAW1_10", STREAMED_SOUND_MISSION_LAW1_10}, {"LAW2_1", STREAMED_SOUND_MISSION_LAW2_1}, {"LAW2_2", STREAMED_SOUND_MISSION_LAW2_2}, {"LAW2_3", STREAMED_SOUND_MISSION_LAW2_3}, {"LAW2_4", STREAMED_SOUND_MISSION_LAW2_4}, {"LAW2_5", STREAMED_SOUND_MISSION_LAW2_5}, {"LAW2_6", STREAMED_SOUND_MISSION_LAW2_6}, {"LAW2_7", STREAMED_SOUND_MISSION_LAW2_7}, {"LAW2_8", STREAMED_SOUND_MISSION_LAW2_8}, {"LAW2_9", STREAMED_SOUND_MISSION_LAW2_9}, {"LAW2_10", STREAMED_SOUND_MISSION_LAW2_10}, {"LAW3_1", STREAMED_SOUND_MISSION_LAW3_1}, {"LAW3_2", STREAMED_SOUND_MISSION_LAW3_2}, {"LAW3_3", STREAMED_SOUND_MISSION_LAW3_3}, {"LAW3_4", STREAMED_SOUND_MISSION_LAW3_4}, {"LAW3_5", STREAMED_SOUND_MISSION_LAW3_5}, {"LAW3_6", STREAMED_SOUND_MISSION_LAW3_6}, {"LAW3_10", STREAMED_SOUND_MISSION_LAW3_10}, {"LAW3_11", STREAMED_SOUND_MISSION_LAW3_11}, {"LAW3_12", STREAMED_SOUND_MISSION_LAW3_12}, {"LAW3_13", STREAMED_SOUND_MISSION_LAW3_13}, {"LAW3_14", STREAMED_SOUND_MISSION_LAW3_14}, {"LAW3_16", STREAMED_SOUND_MISSION_LAW3_16}, {"LAW3_17", STREAMED_SOUND_MISSION_LAW3_17}, {"LAW3_18", STREAMED_SOUND_MISSION_LAW3_18}, {"LAW3_19", STREAMED_SOUND_MISSION_LAW3_19}, {"LAW3_20", STREAMED_SOUND_MISSION_LAW3_20}, {"LAW3_21", STREAMED_SOUND_MISSION_LAW3_21}, {"LAW3_22", STREAMED_SOUND_MISSION_LAW3_22}, {"LAW3_23", STREAMED_SOUND_MISSION_LAW3_23}, {"LAW3_24", STREAMED_SOUND_MISSION_LAW3_24}, {"LAW3_25", STREAMED_SOUND_MISSION_LAW3_25}, {"LAW4_1a", STREAMED_SOUND_MISSION_LAW4_1A}, {"LAW4_1b", STREAMED_SOUND_MISSION_LAW4_1B}, {"LAW4_1c", STREAMED_SOUND_MISSION_LAW4_1C}, {"LAW4_1d", STREAMED_SOUND_MISSION_LAW4_1D}, {"LAW4_10", STREAMED_SOUND_MISSION_LAW4_10}, {"LAW4_3", STREAMED_SOUND_MISSION_LAW4_3}, {"LAW4_4", STREAMED_SOUND_MISSION_LAW4_4}, {"LAW4_5", STREAMED_SOUND_MISSION_LAW4_5}, {"LAW4_6", STREAMED_SOUND_MISSION_LAW4_6}, {"LAW4_7", STREAMED_SOUND_MISSION_LAW4_7}, {"LAW4_8", STREAMED_SOUND_MISSION_LAW4_8}, {"LAW4_9", STREAMED_SOUND_MISSION_LAW4_9}, {"COL1_1", STREAMED_SOUND_MISSION_COL1_1}, {"COL1_2", STREAMED_SOUND_MISSION_COL1_2}, {"COL1_3", STREAMED_SOUND_MISSION_COL1_3}, {"COL1_4", STREAMED_SOUND_MISSION_COL1_4}, {"COL1_5", STREAMED_SOUND_MISSION_COL1_5}, {"COL1_6", STREAMED_SOUND_MISSION_COL1_6}, {"COL1_7", STREAMED_SOUND_MISSION_COL1_7}, {"COL1_8", STREAMED_SOUND_MISSION_COL1_8}, {"COL2_1", STREAMED_SOUND_MISSION_COL2_1}, {"COL2_2", STREAMED_SOUND_MISSION_COL2_2}, {"COL2_3", STREAMED_SOUND_MISSION_COL2_3}, {"COL2_4", STREAMED_SOUND_MISSION_COL2_4}, {"COL2_5", STREAMED_SOUND_MISSION_COL2_5}, {"COL2_6a", STREAMED_SOUND_MISSION_COL2_6A}, {"COL2_7", STREAMED_SOUND_MISSION_COL2_7}, {"COL2_8", STREAMED_SOUND_MISSION_COL2_8}, {"COL2_9", STREAMED_SOUND_MISSION_COL2_9}, {"COL2_10", STREAMED_SOUND_MISSION_COL2_10}, {"COL2_11", STREAMED_SOUND_MISSION_COL2_11}, {"COL2_12", STREAMED_SOUND_MISSION_COL2_12}, {"COL2_13", STREAMED_SOUND_MISSION_COL2_13}, {"COL2_14", STREAMED_SOUND_MISSION_COL2_14}, {"COL2_15", STREAMED_SOUND_MISSION_COL2_15}, {"COL2_16", STREAMED_SOUND_MISSION_COL2_16}, {"COL3_1", STREAMED_SOUND_MISSION_COL3_1}, {"COL3_2", STREAMED_SOUND_MISSION_COL3_2}, {"COL3_2a", STREAMED_SOUND_MISSION_COL3_2A}, {"COL3_2b", STREAMED_SOUND_MISSION_COL3_2B}, {"COL3_3", STREAMED_SOUND_MISSION_COL3_3}, {"COL3_4", STREAMED_SOUND_MISSION_COL3_4}, {"COL3_5", STREAMED_SOUND_MISSION_COL3_5}, {"COL3_6", STREAMED_SOUND_MISSION_COL3_6}, {"COL3_7", STREAMED_SOUND_MISSION_COL3_7}, {"COL3_8", STREAMED_SOUND_MISSION_COL3_8}, {"COL3_9", STREAMED_SOUND_MISSION_COL3_9}, {"COL3_10", STREAMED_SOUND_MISSION_COL3_10}, {"COL3_11", STREAMED_SOUND_MISSION_COL3_11}, {"COL3_12", STREAMED_SOUND_MISSION_COL3_12}, {"COL3_13", STREAMED_SOUND_MISSION_COL3_13}, {"COL3_14", STREAMED_SOUND_MISSION_COL3_14}, {"COL3_15", STREAMED_SOUND_MISSION_COL3_15}, {"COL3_16", STREAMED_SOUND_MISSION_COL3_16}, {"COL3_17", STREAMED_SOUND_MISSION_COL3_17}, {"COL3_18", STREAMED_SOUND_MISSION_COL3_18}, {"COL3_19", STREAMED_SOUND_MISSION_COL3_19}, {"COL3_20", STREAMED_SOUND_MISSION_COL3_20}, {"COL3_21", STREAMED_SOUND_MISSION_COL3_21}, {"COL3_23", STREAMED_SOUND_MISSION_COL3_23}, {"COL3_24", STREAMED_SOUND_MISSION_COL3_24}, {"COL3_25", STREAMED_SOUND_MISSION_COL3_25}, {"COL4_1", STREAMED_SOUND_MISSION_COL4_1}, {"COL4_2", STREAMED_SOUND_MISSION_COL4_2}, {"COL4_3", STREAMED_SOUND_MISSION_COL4_3}, {"COL4_4", STREAMED_SOUND_MISSION_COL4_4}, {"COL4_5", STREAMED_SOUND_MISSION_COL4_5}, {"COL4_6", STREAMED_SOUND_MISSION_COL4_6}, {"COL4_7", STREAMED_SOUND_MISSION_COL4_7}, {"COL4_8", STREAMED_SOUND_MISSION_COL4_8}, {"COL4_9", STREAMED_SOUND_MISSION_COL4_9}, {"COL4_10", STREAMED_SOUND_MISSION_COL4_10}, {"COL4_11", STREAMED_SOUND_MISSION_COL4_11}, {"COL4_12", STREAMED_SOUND_MISSION_COL4_12}, {"COL4_13", STREAMED_SOUND_MISSION_COL4_13}, {"COL4_14", STREAMED_SOUND_MISSION_COL4_14}, {"COL4_15", STREAMED_SOUND_MISSION_COL4_15}, {"COL4_16", STREAMED_SOUND_MISSION_COL4_16}, {"COL4_17", STREAMED_SOUND_MISSION_COL4_17}, {"COL4_18", STREAMED_SOUND_MISSION_COL4_18}, {"COL4_19", STREAMED_SOUND_MISSION_COL4_19}, {"COL4_20", STREAMED_SOUND_MISSION_COL4_20}, {"COL4_21", STREAMED_SOUND_MISSION_COL4_21}, {"COL4_22", STREAMED_SOUND_MISSION_COL4_22}, {"COL4_23", STREAMED_SOUND_MISSION_COL4_23}, {"COL4_24", STREAMED_SOUND_MISSION_COL4_24}, {"COL4_25", STREAMED_SOUND_MISSION_COL4_25}, {"COL4_26", STREAMED_SOUND_MISSION_COL4_26}, {"COL5_1", STREAMED_SOUND_MISSION_COL5_1}, {"COL5_2", STREAMED_SOUND_MISSION_COL5_2}, {"COL5_3", STREAMED_SOUND_MISSION_COL5_3}, {"COL5_4", STREAMED_SOUND_MISSION_COL5_4}, {"COL5_5", STREAMED_SOUND_MISSION_COL5_5}, {"COL5_6", STREAMED_SOUND_MISSION_COL5_6}, {"COL5_7", STREAMED_SOUND_MISSION_COL5_7}, {"COL5_8", STREAMED_SOUND_MISSION_COL5_8}, {"COL5_9", STREAMED_SOUND_MISSION_COL5_9}, {"COL5_10", STREAMED_SOUND_MISSION_COL5_10}, {"COL5_11", STREAMED_SOUND_MISSION_COL5_11}, {"COL5_12", STREAMED_SOUND_MISSION_COL5_12}, {"COL5_13", STREAMED_SOUND_MISSION_COL5_13}, {"COL5_14", STREAMED_SOUND_MISSION_COL5_14}, {"COL5_15", STREAMED_SOUND_MISSION_COL5_15}, {"COL5_16", STREAMED_SOUND_MISSION_COL5_16}, {"COL5_17", STREAMED_SOUND_MISSION_COL5_17}, {"COL5_18", STREAMED_SOUND_MISSION_COL5_18}, {"COL5_19", STREAMED_SOUND_MISSION_COL5_19}, {"COL5_20", STREAMED_SOUND_MISSION_COL5_20}, {"COL5_21", STREAMED_SOUND_MISSION_COL5_21}, {"COL5_22", STREAMED_SOUND_MISSION_COL5_22}, {"COK1_1", STREAMED_SOUND_MISSION_COK1_1}, {"COK1_2", STREAMED_SOUND_MISSION_COK1_2}, {"COK1_3", STREAMED_SOUND_MISSION_COK1_3}, {"COK1_4", STREAMED_SOUND_MISSION_COK1_4}, {"COK1_5", STREAMED_SOUND_MISSION_COK1_5}, {"COK1_6", STREAMED_SOUND_MISSION_COK1_6}, {"COK2_1", STREAMED_SOUND_MISSION_COK2_1}, {"COK2_2", STREAMED_SOUND_MISSION_COK2_2}, {"COK2_3", STREAMED_SOUND_MISSION_COK2_3}, {"COK2_4", STREAMED_SOUND_MISSION_COK2_4}, {"COK2_5", STREAMED_SOUND_MISSION_COK2_5}, {"COK2_6", STREAMED_SOUND_MISSION_COK2_6}, {"COK2_7a", STREAMED_SOUND_MISSION_COK2_7A}, {"COK2_7b", STREAMED_SOUND_MISSION_COK2_7B}, {"COK2_7c", STREAMED_SOUND_MISSION_COK2_7C}, {"COK2_8a", STREAMED_SOUND_MISSION_COK2_8A}, {"COK2_8b", STREAMED_SOUND_MISSION_COK2_8B}, {"COK2_8c", STREAMED_SOUND_MISSION_COK2_8C}, {"COK2_8d", STREAMED_SOUND_MISSION_COK2_8D}, {"COK2_9", STREAMED_SOUND_MISSION_COK2_9}, {"COK210a", STREAMED_SOUND_MISSION_COK210A}, {"COK210b", STREAMED_SOUND_MISSION_COK210B}, {"COK210c", STREAMED_SOUND_MISSION_COK210C}, {"COK212a", STREAMED_SOUND_MISSION_COK212A}, {"COK212b", STREAMED_SOUND_MISSION_COK212B}, {"COK2_13", STREAMED_SOUND_MISSION_COK2_13}, {"COK2_14", STREAMED_SOUND_MISSION_COK2_14}, {"COK2_15", STREAMED_SOUND_MISSION_COK2_15}, {"COK2_16", STREAMED_SOUND_MISSION_COK2_16}, {"COK2_20", STREAMED_SOUND_MISSION_COK2_20}, {"COK2_21", STREAMED_SOUND_MISSION_COK2_21}, {"COK2_22", STREAMED_SOUND_MISSION_COK2_22}, {"COK3_1", STREAMED_SOUND_MISSION_COK3_1}, {"COK3_2", STREAMED_SOUND_MISSION_COK3_2}, {"COK3_3", STREAMED_SOUND_MISSION_COK3_3}, {"COK3_4", STREAMED_SOUND_MISSION_COK3_4}, {"COK4_1", STREAMED_SOUND_MISSION_COK4_1}, {"COK4_2", STREAMED_SOUND_MISSION_COK4_2}, {"COK4_3", STREAMED_SOUND_MISSION_COK4_3}, {"COK4_4", STREAMED_SOUND_MISSION_COK4_4}, {"COK4_5", STREAMED_SOUND_MISSION_COK4_5}, {"COK4_6", STREAMED_SOUND_MISSION_COK4_6}, {"COK4_7", STREAMED_SOUND_MISSION_COK4_7}, {"COK4_8", STREAMED_SOUND_MISSION_COK4_8}, {"COK4_9", STREAMED_SOUND_MISSION_COK4_9}, {"COK4_9A", STREAMED_SOUND_MISSION_COK4_9A}, {"COK4_10", STREAMED_SOUND_MISSION_COK4_10}, {"COK4_11", STREAMED_SOUND_MISSION_COK4_11}, {"COK4_12", STREAMED_SOUND_MISSION_COK4_12}, {"COK4_13", STREAMED_SOUND_MISSION_COK4_13}, {"COK4_14", STREAMED_SOUND_MISSION_COK4_14}, {"COK4_15", STREAMED_SOUND_MISSION_COK4_15}, {"COK4_16", STREAMED_SOUND_MISSION_COK4_16}, {"COK4_17", STREAMED_SOUND_MISSION_COK4_17}, {"COK4_18", STREAMED_SOUND_MISSION_COK4_18}, {"COK4_19", STREAMED_SOUND_MISSION_COK4_19}, {"COK4_20", STREAMED_SOUND_MISSION_COK4_20}, {"COK4_21", STREAMED_SOUND_MISSION_COK4_21}, {"COK4_22", STREAMED_SOUND_MISSION_COK4_22}, {"COK4_23", STREAMED_SOUND_MISSION_COK4_23}, {"COK4_24", STREAMED_SOUND_MISSION_COK4_24}, {"COK4_25", STREAMED_SOUND_MISSION_COK4_25}, {"COK4_26", STREAMED_SOUND_MISSION_COK4_26}, {"COK4_27", STREAMED_SOUND_MISSION_COK4_27}, {"RESC_1", STREAMED_SOUND_MISSION_RESC_1}, {"RESC_2", STREAMED_SOUND_MISSION_RESC_2}, {"RESC_3", STREAMED_SOUND_MISSION_RESC_3}, {"RESC_4", STREAMED_SOUND_MISSION_RESC_4}, {"RESC_5", STREAMED_SOUND_MISSION_RESC_5}, {"RESC_6", STREAMED_SOUND_MISSION_RESC_6}, {"RESC_7", STREAMED_SOUND_MISSION_RESC_7}, {"RESC_8", STREAMED_SOUND_MISSION_RESC_8}, {"RESC_9", STREAMED_SOUND_MISSION_RESC_9}, {"RESC_10", STREAMED_SOUND_MISSION_RESC_10}, {"ASS_1", STREAMED_SOUND_MISSION_ASS_1}, {"ASS_2", STREAMED_SOUND_MISSION_ASS_2}, {"ASS_3", STREAMED_SOUND_MISSION_ASS_3}, {"ASS_4", STREAMED_SOUND_MISSION_ASS_4}, {"ASS_5", STREAMED_SOUND_MISSION_ASS_5}, {"ASS_6", STREAMED_SOUND_MISSION_ASS_6}, {"ASS_7", STREAMED_SOUND_MISSION_ASS_7}, {"ASS_8", STREAMED_SOUND_MISSION_ASS_8}, {"ASS_9", STREAMED_SOUND_MISSION_ASS_9}, {"ASS_10", STREAMED_SOUND_MISSION_ASS_10}, {"ASS_11", STREAMED_SOUND_MISSION_ASS_11}, {"ASS_12", STREAMED_SOUND_MISSION_ASS_12}, {"ASS_13", STREAMED_SOUND_MISSION_ASS_13}, {"ASS_14", STREAMED_SOUND_MISSION_ASS_14}, {"BUD1_1", STREAMED_SOUND_MISSION_BUD1_1}, {"BUD1_2", STREAMED_SOUND_MISSION_BUD1_2}, {"BUD1_3", STREAMED_SOUND_MISSION_BUD1_3}, {"BUD1_4", STREAMED_SOUND_MISSION_BUD1_4}, {"BUD1_5", STREAMED_SOUND_MISSION_BUD1_5}, {"BUD1_9", STREAMED_SOUND_MISSION_BUD1_9}, {"BUD1_10", STREAMED_SOUND_MISSION_BUD1_10}, {"BUD2_1", STREAMED_SOUND_MISSION_BUD2_1}, {"BUD2_2", STREAMED_SOUND_MISSION_BUD2_2}, {"BUD2_3", STREAMED_SOUND_MISSION_BUD2_3}, {"BUD2_4", STREAMED_SOUND_MISSION_BUD2_4}, {"BUD2_5", STREAMED_SOUND_MISSION_BUD2_5}, {"BUD2_6", STREAMED_SOUND_MISSION_BUD2_6}, {"BUD2_7", STREAMED_SOUND_MISSION_BUD2_7}, {"BUD3_1a", STREAMED_SOUND_MISSION_BUD3_1A}, {"BUD3_1b", STREAMED_SOUND_MISSION_BUD3_1B}, {"BUD3_1", STREAMED_SOUND_MISSION_BUD3_1}, {"BUD3_2", STREAMED_SOUND_MISSION_BUD3_2}, {"BUD3_3", STREAMED_SOUND_MISSION_BUD3_3}, {"BUD3_4", STREAMED_SOUND_MISSION_BUD3_4}, {"BUD3_1c", STREAMED_SOUND_MISSION_BUD3_1C}, {"BUD3_5", STREAMED_SOUND_MISSION_BUD3_5}, {"BUD3_6", STREAMED_SOUND_MISSION_BUD3_6}, {"BUD3_7", STREAMED_SOUND_MISSION_BUD3_7}, {"BUD3_8a", STREAMED_SOUND_MISSION_BUD3_8A}, {"BUD3_8b", STREAMED_SOUND_MISSION_BUD3_8B}, {"BUD3_8c", STREAMED_SOUND_MISSION_BUD3_8C}, {"BUD3_9a", STREAMED_SOUND_MISSION_BUD3_9A}, {"BUD3_9b", STREAMED_SOUND_MISSION_BUD3_9B}, {"BUD3_9c", STREAMED_SOUND_MISSION_BUD3_9C}, {"CAP1_2", STREAMED_SOUND_MISSION_CAP1_2}, {"CAP1_3", STREAMED_SOUND_MISSION_CAP1_3}, {"CAP1_4", STREAMED_SOUND_MISSION_CAP1_4}, {"CAP1_5", STREAMED_SOUND_MISSION_CAP1_5}, {"CAP1_6", STREAMED_SOUND_MISSION_CAP1_6}, {"CAP1_7", STREAMED_SOUND_MISSION_CAP1_7}, {"CAP1_8", STREAMED_SOUND_MISSION_CAP1_8}, {"CAP1_9", STREAMED_SOUND_MISSION_CAP1_9}, {"CAP1_10", STREAMED_SOUND_MISSION_CAP1_10}, {"CAP1_11", STREAMED_SOUND_MISSION_CAP1_11}, {"CAP1_12", STREAMED_SOUND_MISSION_CAP1_12}, {"FINKILL", STREAMED_SOUND_MISSION_FINKILL}, {"FIN_1a", STREAMED_SOUND_MISSION_FIN_1A}, {"FIN_1b", STREAMED_SOUND_MISSION_FIN_1B}, {"FIN_1c", STREAMED_SOUND_MISSION_FIN_1C}, {"FIN_2b", STREAMED_SOUND_MISSION_FIN_2B}, {"FIN_2c", STREAMED_SOUND_MISSION_FIN_2C}, {"FIN_3", STREAMED_SOUND_MISSION_FIN_3}, {"FIN_4", STREAMED_SOUND_MISSION_FIN_4}, {"FIN_5", STREAMED_SOUND_MISSION_FIN_5}, {"FIN_6", STREAMED_SOUND_MISSION_FIN_6}, {"FIN_10", STREAMED_SOUND_MISSION_FIN_10}, {"FIN_11a", STREAMED_SOUND_MISSION_FIN_11A}, {"FIN_11b", STREAMED_SOUND_MISSION_FIN_11B}, {"FIN_12a", STREAMED_SOUND_MISSION_FIN_12A}, {"FIN_12b", STREAMED_SOUND_MISSION_FIN_12B}, {"FIN_12c", STREAMED_SOUND_MISSION_FIN_12C}, {"FIN_13", STREAMED_SOUND_MISSION_FIN_13}, {"BNK1_1", STREAMED_SOUND_MISSION_BNK1_1}, {"BNK1_2", STREAMED_SOUND_MISSION_BNK1_2}, {"BNK1_3", STREAMED_SOUND_MISSION_BNK1_3}, {"BNK1_4", STREAMED_SOUND_MISSION_BNK1_4}, {"BNK1_5", STREAMED_SOUND_MISSION_BNK1_5}, {"BNK1_6", STREAMED_SOUND_MISSION_BNK1_6}, {"BNK1_7", STREAMED_SOUND_MISSION_BNK1_7}, {"BNK1_8", STREAMED_SOUND_MISSION_BNK1_8}, {"BNK1_10", STREAMED_SOUND_MISSION_BNK1_10}, {"BNK1_11", STREAMED_SOUND_MISSION_BNK1_11}, {"BNK1_12", STREAMED_SOUND_MISSION_BNK1_12}, {"BNK1_13", STREAMED_SOUND_MISSION_BNK1_13}, {"BNK1_14", STREAMED_SOUND_MISSION_BNK1_14}, {"BNK2_1", STREAMED_SOUND_MISSION_BNK2_1}, {"BNK2_2", STREAMED_SOUND_MISSION_BNK2_2}, {"BNK2_3", STREAMED_SOUND_MISSION_BNK2_3}, {"BNK2_4", STREAMED_SOUND_MISSION_BNK2_4}, {"BNK2_5", STREAMED_SOUND_MISSION_BNK2_5}, {"BNK2_6", STREAMED_SOUND_MISSION_BNK2_6}, {"BNK2_7", STREAMED_SOUND_MISSION_BNK2_7}, {"BNK2_8", STREAMED_SOUND_MISSION_BNK2_8}, {"BNK2_9", STREAMED_SOUND_MISSION_BNK2_9}, {"BNK3_1", STREAMED_SOUND_MISSION_BNK3_1}, {"BNK3_2", STREAMED_SOUND_MISSION_BNK3_2}, {"BNK3_3a", STREAMED_SOUND_MISSION_BNK3_3A}, {"BNK3_3b", STREAMED_SOUND_MISSION_BNK3_3B}, {"BNK3_3c", STREAMED_SOUND_MISSION_BNK3_3C}, {"BNK3_4a", STREAMED_SOUND_MISSION_BNK3_4A}, {"BNK3_4b", STREAMED_SOUND_MISSION_BNK3_4B}, {"BNK3_4c", STREAMED_SOUND_MISSION_BNK3_4C}, {"BNK4_1", STREAMED_SOUND_MISSION_BNK4_1}, {"BNK4_2", STREAMED_SOUND_MISSION_BNK4_2}, {"BNK4_3A", STREAMED_SOUND_MISSION_BNK4_3A}, {"BNK4_3B", STREAMED_SOUND_MISSION_BNK4_3B}, {"BNK4_3C", STREAMED_SOUND_MISSION_BNK4_3C}, {"BNK4_3D", STREAMED_SOUND_MISSION_BNK4_3D}, {"BNK4_3E", STREAMED_SOUND_MISSION_BNK4_3E}, {"BNK4_3F", STREAMED_SOUND_MISSION_BNK4_3F}, {"BNK4_3G", STREAMED_SOUND_MISSION_BNK4_3G}, {"BNK4_3H", STREAMED_SOUND_MISSION_BNK4_3H}, {"BNK4_3I", STREAMED_SOUND_MISSION_BNK4_3I}, {"BNK4_3J", STREAMED_SOUND_MISSION_BNK4_3J}, {"BNK4_3K", STREAMED_SOUND_MISSION_BNK4_3K}, {"BNK4_3M", STREAMED_SOUND_MISSION_BNK4_3M}, {"BNK4_3O", STREAMED_SOUND_MISSION_BNK4_3O}, {"BNK4_3P", STREAMED_SOUND_MISSION_BNK4_3P}, {"BNK4_3Q", STREAMED_SOUND_MISSION_BNK4_3Q}, {"BNK4_3R", STREAMED_SOUND_MISSION_BNK4_3R}, {"BNK4_3S", STREAMED_SOUND_MISSION_BNK4_3S}, {"BNK4_3T", STREAMED_SOUND_MISSION_BNK4_3T}, {"BNK4_3U", STREAMED_SOUND_MISSION_BNK4_3U}, {"BNK4_3V", STREAMED_SOUND_MISSION_BNK4_3V}, {"BNK4_4a", STREAMED_SOUND_MISSION_BNK4_4A}, {"BNK4_4b", STREAMED_SOUND_MISSION_BNK4_4B}, {"BNK4_5", STREAMED_SOUND_MISSION_BNK4_5}, {"BNK4_6", STREAMED_SOUND_MISSION_BNK4_6}, {"BNK4_7", STREAMED_SOUND_MISSION_BNK4_7}, {"BNK4_8", STREAMED_SOUND_MISSION_BNK4_8}, {"BNK4_9", STREAMED_SOUND_MISSION_BNK4_9}, {"BNK4_10", STREAMED_SOUND_MISSION_BNK4_10}, {"BNK4_11", STREAMED_SOUND_MISSION_BNK4_11}, {"BK4_12a", STREAMED_SOUND_MISSION_BK4_12A}, {"BK4_12b", STREAMED_SOUND_MISSION_BK4_12B}, {"BK4_12c", STREAMED_SOUND_MISSION_BK4_12C}, {"BNK4_13", STREAMED_SOUND_MISSION_BNK4_13}, {"BK4_14a", STREAMED_SOUND_MISSION_BK4_14A}, {"BK4_14b", STREAMED_SOUND_MISSION_BK4_14B}, {"BNK4_15", STREAMED_SOUND_MISSION_BNK4_15}, {"BNK4_16", STREAMED_SOUND_MISSION_BNK4_16}, {"BNK4_17", STREAMED_SOUND_MISSION_BNK4_17}, {"BNK4_18", STREAMED_SOUND_MISSION_BNK4_18}, {"BK4_19a", STREAMED_SOUND_MISSION_BK4_19A}, {"BK4_19b", STREAMED_SOUND_MISSION_BK4_19B}, {"BK4_20a", STREAMED_SOUND_MISSION_BK4_20A}, {"BK4_20b", STREAMED_SOUND_MISSION_BK4_20B}, {"BNK4_21", STREAMED_SOUND_MISSION_BNK4_21}, {"BNK422a", STREAMED_SOUND_MISSION_BNK422A}, {"BNK422b", STREAMED_SOUND_MISSION_BNK422B}, {"BK4_23a", STREAMED_SOUND_MISSION_BK4_23A}, {"BK4_23b", STREAMED_SOUND_MISSION_BK4_23B}, {"BK4_23c", STREAMED_SOUND_MISSION_BK4_23C}, {"BK4_23d", STREAMED_SOUND_MISSION_BK4_23D}, {"BK4_24a", STREAMED_SOUND_MISSION_BK4_24A}, {"BK4_24b", STREAMED_SOUND_MISSION_BK4_24B}, {"BNK4_25", STREAMED_SOUND_MISSION_BNK4_25}, {"BNK4_26", STREAMED_SOUND_MISSION_BNK4_26}, {"BNK4_27", STREAMED_SOUND_MISSION_BNK4_27}, {"BNK4_28", STREAMED_SOUND_MISSION_BNK4_28}, {"BNK4_29", STREAMED_SOUND_MISSION_BNK4_29}, {"BNK4_30", STREAMED_SOUND_MISSION_BNK4_30}, {"BK4_31a", STREAMED_SOUND_MISSION_BK4_31A}, {"BK4_31b", STREAMED_SOUND_MISSION_BK4_31B}, {"BNK4_32", STREAMED_SOUND_MISSION_BNK4_32}, {"BK4_34a", STREAMED_SOUND_MISSION_BK4_34A}, {"BK4_34b", STREAMED_SOUND_MISSION_BK4_34B}, {"BK4_35a", STREAMED_SOUND_MISSION_BK4_35A}, {"BK4_35b", STREAMED_SOUND_MISSION_BK4_35B}, {"BNK4_36", STREAMED_SOUND_MISSION_BNK4_36}, {"BNK4_37", STREAMED_SOUND_MISSION_BNK4_37}, {"BNK4_38", STREAMED_SOUND_MISSION_BNK4_38}, {"BNK_39", STREAMED_SOUND_MISSION_BNK4_39}, {"BK4_40a", STREAMED_SOUND_MISSION_BK4_40A}, {"BK4_40b", STREAMED_SOUND_MISSION_BK4_40B}, {"BNK4_41", STREAMED_SOUND_MISSION_BNK4_41}, {"BNK4_42", STREAMED_SOUND_MISSION_BNK4_42}, {"BNK4_43", STREAMED_SOUND_MISSION_BNK4_43}, {"BNK4_44", STREAMED_SOUND_MISSION_BNK4_44}, {"BNK4_45", STREAMED_SOUND_MISSION_BNK4_45}, {"BNK4_46", STREAMED_SOUND_MISSION_BNK4_46}, {"BNK4_47", STREAMED_SOUND_MISSION_BNK4_47}, {"BNK4_48", STREAMED_SOUND_MISSION_BNK4_48}, {"BNK4_49", STREAMED_SOUND_MISSION_BNK4_49}, {"BNK450A", STREAMED_SOUND_MISSION_BNK450A}, {"BNK450B", STREAMED_SOUND_MISSION_BNK450B}, {"BNK4_51", STREAMED_SOUND_MISSION_BNK4_51}, {"BNK4_94", STREAMED_SOUND_MISSION_BNK4_94}, {"BNK4_95", STREAMED_SOUND_MISSION_BNK4_95}, {"BNK4_96", STREAMED_SOUND_MISSION_BNK4_96}, {"BNK4_97", STREAMED_SOUND_MISSION_BNK4_97}, {"BNK4_98", STREAMED_SOUND_MISSION_BNK4_98}, {"BNK4_99", STREAMED_SOUND_MISSION_BNK4_99}, {"CNT1_1", STREAMED_SOUND_MISSION_CNT1_1}, {"CNT1_2", STREAMED_SOUND_MISSION_CNT1_2}, {"CNT1_3", STREAMED_SOUND_MISSION_CNT1_3}, {"CNT1_4", STREAMED_SOUND_MISSION_CNT1_4}, {"CNT1_5", STREAMED_SOUND_MISSION_CNT1_5}, {"CNT2_1", STREAMED_SOUND_MISSION_CNT2_1}, {"CNT2_2", STREAMED_SOUND_MISSION_CNT2_2}, {"CNT2_3", STREAMED_SOUND_MISSION_CNT2_3}, {"CNT2_4", STREAMED_SOUND_MISSION_CNT2_4}, {"PORN1_1", STREAMED_SOUND_MISSION_PORN1_1}, {"PORN1_2", STREAMED_SOUND_MISSION_PORN1_2}, {"PORN1_3", STREAMED_SOUND_MISSION_PORN1_3}, {"PRN1_3A", STREAMED_SOUND_MISSION_PRN1_3A}, {"PORN1_4", STREAMED_SOUND_MISSION_PORN1_4}, {"PORN1_5", STREAMED_SOUND_MISSION_PORN1_5}, {"PORN1_6", STREAMED_SOUND_MISSION_PORN1_6}, {"PORN1_7", STREAMED_SOUND_MISSION_PORN1_7}, {"PORN1_8", STREAMED_SOUND_MISSION_PORN1_8}, {"PORN1_9", STREAMED_SOUND_MISSION_PORN1_9}, {"PRN1_10", STREAMED_SOUND_MISSION_PRN1_10}, {"PRN1_11", STREAMED_SOUND_MISSION_PRN1_11}, {"PRN1_12", STREAMED_SOUND_MISSION_PRN1_12}, {"PRN1_13", STREAMED_SOUND_MISSION_PRN1_13}, {"PRN1_14", STREAMED_SOUND_MISSION_PRN1_14}, {"PRN1_15", STREAMED_SOUND_MISSION_PRN1_15}, {"PRN1_16", STREAMED_SOUND_MISSION_PRN1_16}, {"PRN1_17", STREAMED_SOUND_MISSION_PRN1_17}, {"PRN1_18", STREAMED_SOUND_MISSION_PRN1_18}, {"PRN1_19", STREAMED_SOUND_MISSION_PRN1_19}, {"PRN1_20", STREAMED_SOUND_MISSION_PRN1_20}, {"PRN1_21", STREAMED_SOUND_MISSION_PRN1_21}, {"PORN3_1", STREAMED_SOUND_MISSION_PORN3_1}, {"PORN3_2", STREAMED_SOUND_MISSION_PORN3_2}, {"PORN3_3", STREAMED_SOUND_MISSION_PORN3_3}, {"PORN3_4", STREAMED_SOUND_MISSION_PORN3_4}, {"TAX1_1", STREAMED_SOUND_MISSION_TAX1_1}, {"TAX1_2", STREAMED_SOUND_MISSION_TAX1_2}, {"TAX1_3", STREAMED_SOUND_MISSION_TAX1_3}, {"TAX1_4", STREAMED_SOUND_MISSION_TAX1_4}, {"TAX1_5", STREAMED_SOUND_MISSION_TAX1_5}, {"TAX2_1", STREAMED_SOUND_MISSION_TAX2_1}, {"TAX2_2", STREAMED_SOUND_MISSION_TAX2_2}, {"TAX2_3", STREAMED_SOUND_MISSION_TAX2_3}, {"TAX2_4", STREAMED_SOUND_MISSION_TAX2_4}, {"TAX2_5", STREAMED_SOUND_MISSION_TAX2_5}, {"TAX2_6", STREAMED_SOUND_MISSION_TAX2_6}, {"TAX2_7", STREAMED_SOUND_MISSION_TAX2_7}, {"TAX3_1", STREAMED_SOUND_MISSION_TAX3_1}, {"TAX3_2", STREAMED_SOUND_MISSION_TAX3_2}, {"TAX3_3", STREAMED_SOUND_MISSION_TAX3_3}, {"TAX3_4", STREAMED_SOUND_MISSION_TAX3_4}, {"TAX3_5", STREAMED_SOUND_MISSION_TAX3_5}, {"TEX1_1", STREAMED_SOUND_MISSION_TEX1_1}, {"TEX1_2", STREAMED_SOUND_MISSION_TEX1_2}, {"TEX1_3", STREAMED_SOUND_MISSION_TEX1_3}, {"TEX1_4", STREAMED_SOUND_MISSION_TEX1_4}, {"TEX1_5", STREAMED_SOUND_MISSION_TEX1_5}, {"TEX1_6", STREAMED_SOUND_MISSION_TEX1_6}, {"TEX2_1", STREAMED_SOUND_MISSION_TEX2_1}, {"TEX3_1", STREAMED_SOUND_MISSION_TEX3_1}, {"TEX3_2", STREAMED_SOUND_MISSION_TEX3_2}, {"TEX3_3", STREAMED_SOUND_MISSION_TEX3_3}, {"TEX3_4", STREAMED_SOUND_MISSION_TEX3_4}, {"TEX3_5", STREAMED_SOUND_MISSION_TEX3_5}, {"TEX3_6", STREAMED_SOUND_MISSION_TEX3_6}, {"TEX3_7", STREAMED_SOUND_MISSION_TEX3_7}, {"TEX3_8", STREAMED_SOUND_MISSION_TEX3_8}, {"PHIL1_2", STREAMED_SOUND_MISSION_PHIL1_2}, {"PHIL1_3", STREAMED_SOUND_MISSION_PHIL1_3}, {"PHIL2_1", STREAMED_SOUND_MISSION_PHIL2_1}, {"PHIL2_2", STREAMED_SOUND_MISSION_PHIL2_2}, {"PHIL2_3", STREAMED_SOUND_MISSION_PHIL2_3}, {"PHIL2_4", STREAMED_SOUND_MISSION_PHIL2_4}, {"PHIL2_5", STREAMED_SOUND_MISSION_PHIL2_5}, {"PHIL2_6", STREAMED_SOUND_MISSION_PHIL2_6}, {"PHIL2_7", STREAMED_SOUND_MISSION_PHIL2_7}, {"PHIL2_8", STREAMED_SOUND_MISSION_PHIL2_8}, {"PHIL2_9", STREAMED_SOUND_MISSION_PHIL2_9}, {"PHIL210", STREAMED_SOUND_MISSION_PHIL210}, {"PHIL211", STREAMED_SOUND_MISSION_PHIL211}, {"BIKE1_1", STREAMED_SOUND_MISSION_BIKE1_1}, {"BIKE1_2", STREAMED_SOUND_MISSION_BIKE1_2}, {"BIKE1_3", STREAMED_SOUND_MISSION_BIKE1_3}, {"ROK1_1a", STREAMED_SOUND_MISSION_ROK1_1A}, {"ROK1_1b", STREAMED_SOUND_MISSION_ROK1_1B}, {"ROK1_5", STREAMED_SOUND_MISSION_ROK1_5}, {"ROK1_6", STREAMED_SOUND_MISSION_ROK1_6}, {"ROK1_7", STREAMED_SOUND_MISSION_ROK1_7}, {"ROK1_8", STREAMED_SOUND_MISSION_ROK1_8}, {"ROK1_9", STREAMED_SOUND_MISSION_ROK1_9}, {"PSYCH_1", STREAMED_SOUND_MISSION_PSYCH_1}, {"PSYCH_2", STREAMED_SOUND_MISSION_PSYCH_2}, {"ROK2_01", STREAMED_SOUND_MISSION_ROK2_01}, {"ROK3_1", STREAMED_SOUND_MISSION_ROK3_1}, {"ROK3_2", STREAMED_SOUND_MISSION_ROK3_2}, {"ROK3_3", STREAMED_SOUND_MISSION_ROK3_3}, {"ROK3_4", STREAMED_SOUND_MISSION_ROK3_4}, {"ROK3_5", STREAMED_SOUND_MISSION_ROK3_5}, {"ROK3_6", STREAMED_SOUND_MISSION_ROK3_6}, {"ROK3_7", STREAMED_SOUND_MISSION_ROK3_7}, {"ROK3_8", STREAMED_SOUND_MISSION_ROK3_8}, {"ROK3_9", STREAMED_SOUND_MISSION_ROK3_9}, {"ROK3_10", STREAMED_SOUND_MISSION_ROK3_10}, {"ROK3_11", STREAMED_SOUND_MISSION_ROK3_11}, {"ROK3_12", STREAMED_SOUND_MISSION_ROK3_12}, {"ROK3_13", STREAMED_SOUND_MISSION_ROK3_13}, {"ROK3_14", STREAMED_SOUND_MISSION_ROK3_14}, {"ROK3_15", STREAMED_SOUND_MISSION_ROK3_15}, {"ROK3_16", STREAMED_SOUND_MISSION_ROK3_16}, {"ROK3_17", STREAMED_SOUND_MISSION_ROK3_17}, {"ROK3_18", STREAMED_SOUND_MISSION_ROK3_18}, {"ROK3_19", STREAMED_SOUND_MISSION_ROK3_19}, {"ROK3_20", STREAMED_SOUND_MISSION_ROK3_20}, {"ROK3_21", STREAMED_SOUND_MISSION_ROK3_21}, {"ROK3_22", STREAMED_SOUND_MISSION_ROK3_22}, {"ROK3_23", STREAMED_SOUND_MISSION_ROK3_23}, {"ROK3_24", STREAMED_SOUND_MISSION_ROK3_24}, {"ROK3_25", STREAMED_SOUND_MISSION_ROK3_25}, {"ROK3_26", STREAMED_SOUND_MISSION_ROK3_26}, {"ROK3_27", STREAMED_SOUND_MISSION_ROK3_27}, {"ROK3_62", STREAMED_SOUND_MISSION_ROK3_62}, {"ROK3_63", STREAMED_SOUND_MISSION_ROK3_63}, {"ROK3_64", STREAMED_SOUND_MISSION_ROK3_64}, {"ROK3_65", STREAMED_SOUND_MISSION_ROK3_65}, {"ROK3_66", STREAMED_SOUND_MISSION_ROK3_66}, {"ROK3_67", STREAMED_SOUND_MISSION_ROK3_67}, {"ROK3_68", STREAMED_SOUND_MISSION_ROK3_68}, {"ROK3_69", STREAMED_SOUND_MISSION_ROK3_69}, {"ROK3_70", STREAMED_SOUND_MISSION_ROK3_70}, {"ROK3_71", STREAMED_SOUND_MISSION_ROK3_71}, {"ROK3_73", STREAMED_SOUND_MISSION_ROK3_73}, {"HAT_1a", STREAMED_SOUND_MISSION_HAT_1A}, {"intro1", STREAMED_SOUND_MISSION_INTRO1}, {"intro2", STREAMED_SOUND_MISSION_INTRO2}, {"intro3", STREAMED_SOUND_MISSION_INTRO3}, {"intro4", STREAMED_SOUND_MISSION_INTRO4}, {"CUB1_1", STREAMED_SOUND_MISSION_CUB1_1}, {"CUB1_2", STREAMED_SOUND_MISSION_CUB1_2}, {"CUB1_3", STREAMED_SOUND_MISSION_CUB1_3}, {"CUB1_4", STREAMED_SOUND_MISSION_CUB1_4}, {"CUB1_5", STREAMED_SOUND_MISSION_CUB1_5}, {"CUB1_6", STREAMED_SOUND_MISSION_CUB1_6}, {"CUB1_7", STREAMED_SOUND_MISSION_CUB1_7}, {"CUB1_8", STREAMED_SOUND_MISSION_CUB1_8}, {"CUB1_9", STREAMED_SOUND_MISSION_CUB1_9}, {"CUB1_10", STREAMED_SOUND_MISSION_CUB1_10}, {"CUB2_1", STREAMED_SOUND_MISSION_CUB2_1}, {"CUB2_2", STREAMED_SOUND_MISSION_CUB2_2}, {"CUB2_3a", STREAMED_SOUND_MISSION_CUB2_3A}, {"CUB2_3b", STREAMED_SOUND_MISSION_CUB2_3B}, {"CUB2_3c", STREAMED_SOUND_MISSION_CUB2_3C}, {"CUB2_4a", STREAMED_SOUND_MISSION_CUB2_4A}, {"CUB2_5", STREAMED_SOUND_MISSION_CUB2_5}, {"CUB2_6", STREAMED_SOUND_MISSION_CUB2_6}, {"CUB2_7", STREAMED_SOUND_MISSION_CUB2_7}, {"CUB2_8", STREAMED_SOUND_MISSION_CUB2_8}, {"CUB2_9", STREAMED_SOUND_MISSION_CUB2_9}, {"CUB2_10", STREAMED_SOUND_MISSION_CUB2_10}, {"CUB2_11", STREAMED_SOUND_MISSION_CUB2_11}, {"CUB3_1", STREAMED_SOUND_MISSION_CUB3_1}, {"CUB3_2", STREAMED_SOUND_MISSION_CUB3_2}, {"CUB3_3", STREAMED_SOUND_MISSION_CUB3_3}, {"CUB3_4", STREAMED_SOUND_MISSION_CUB3_4}, {"CUB4_1", STREAMED_SOUND_MISSION_CUB4_1}, {"CUB4_2", STREAMED_SOUND_MISSION_CUB4_2}, {"CUB4_3", STREAMED_SOUND_MISSION_CUB4_3}, {"CUB4_4", STREAMED_SOUND_MISSION_CUB4_4}, {"CUB4_5", STREAMED_SOUND_MISSION_CUB4_5}, {"CUB4_5A", STREAMED_SOUND_MISSION_CUB4_5A}, {"CUB4_6", STREAMED_SOUND_MISSION_CUB4_6}, {"CUB4_7", STREAMED_SOUND_MISSION_CUB4_7}, {"CUB4_8", STREAMED_SOUND_MISSION_CUB4_8}, {"CUB4_9", STREAMED_SOUND_MISSION_CUB4_9}, {"CUB4_10", STREAMED_SOUND_MISSION_CUB4_10}, {"CUB4_11", STREAMED_SOUND_MISSION_CUB4_11}, {"CUB4_12", STREAMED_SOUND_MISSION_CUB4_12}, {"CUB4_13", STREAMED_SOUND_MISSION_CUB4_13}, {"CUB4_14", STREAMED_SOUND_MISSION_CUB4_14}, {"CUB4_15", STREAMED_SOUND_MISSION_CUB4_15}, {"CUB4_16", STREAMED_SOUND_MISSION_CUB4_16}, {"golf_1", STREAMED_SOUND_MISSION_GOLF_1}, {"golf_2", STREAMED_SOUND_MISSION_GOLF_2}, {"golf_3", STREAMED_SOUND_MISSION_GOLF_3}, {"bar_1", STREAMED_SOUND_MISSION_BAR_1}, {"bar_2", STREAMED_SOUND_MISSION_BAR_2}, {"bar_3", STREAMED_SOUND_MISSION_BAR_3}, {"bar_4", STREAMED_SOUND_MISSION_BAR_4}, {"bar_5", STREAMED_SOUND_MISSION_BAR_5}, {"bar_6", STREAMED_SOUND_MISSION_BAR_6}, {"bar_7", STREAMED_SOUND_MISSION_BAR_7}, {"bar_8", STREAMED_SOUND_MISSION_BAR_8}, {"strip_1", STREAMED_SOUND_MISSION_STRIP_1}, {"strip_2", STREAMED_SOUND_MISSION_STRIP_2}, {"strip_3", STREAMED_SOUND_MISSION_STRIP_3}, {"strip_4", STREAMED_SOUND_MISSION_STRIP_4}, {"strip_5", STREAMED_SOUND_MISSION_STRIP_5}, {"strip_6", STREAMED_SOUND_MISSION_STRIP_6}, {"strip_7", STREAMED_SOUND_MISSION_STRIP_7}, {"strip_8", STREAMED_SOUND_MISSION_STRIP_8}, {"strip_9", STREAMED_SOUND_MISSION_STRIP_9}, {"star_1", STREAMED_SOUND_MISSION_STAR_1}, {"star_2", STREAMED_SOUND_MISSION_STAR_2}, {"star_3", STREAMED_SOUND_MISSION_STAR_3}, {"star_4", STREAMED_SOUND_MISSION_STAR_4}, {"mob_01a", STREAMED_SOUND_MISSION_MOB_01A}, {"mob_01b", STREAMED_SOUND_MISSION_MOB_01B}, {"mob_01c", STREAMED_SOUND_MISSION_MOB_01C}, {"mob_02a", STREAMED_SOUND_MISSION_MOB_02A}, {"mob_02b", STREAMED_SOUND_MISSION_MOB_02B}, {"mob_02c", STREAMED_SOUND_MISSION_MOB_02C}, {"mob_03a", STREAMED_SOUND_MISSION_MOB_03A}, {"mob_03b", STREAMED_SOUND_MISSION_MOB_03B}, {"mob_03c", STREAMED_SOUND_MISSION_MOB_03C}, {"mob_03d", STREAMED_SOUND_MISSION_MOB_03D}, {"mob_03e", STREAMED_SOUND_MISSION_MOB_03E}, {"shark_1", STREAMED_SOUND_MISSION_SHARK_1}, {"shark_2", STREAMED_SOUND_MISSION_SHARK_2}, {"shark_3", STREAMED_SOUND_MISSION_SHARK_3}, {"shark_4", STREAMED_SOUND_MISSION_SHARK_4}, {"shark_5", STREAMED_SOUND_MISSION_SHARK_5}, {"mob_04a", STREAMED_SOUND_MISSION_MOB_04A}, {"mob_04b", STREAMED_SOUND_MISSION_MOB_04B}, {"mob_04c", STREAMED_SOUND_MISSION_MOB_04C}, {"mob_04d", STREAMED_SOUND_MISSION_MOB_04D}, {"mob_05a", STREAMED_SOUND_MISSION_MOB_05A}, {"mob_05b", STREAMED_SOUND_MISSION_MOB_05B}, {"mob_05c", STREAMED_SOUND_MISSION_MOB_05C}, {"mob_05d", STREAMED_SOUND_MISSION_MOB_05D}, {"mob_06a", STREAMED_SOUND_MISSION_MOB_06A}, {"mob_06b", STREAMED_SOUND_MISSION_MOB_06B}, {"mob_06c", STREAMED_SOUND_MISSION_MOB_06C}, {"mob_07a", STREAMED_SOUND_MISSION_MOB_07A}, {"mob_07b", STREAMED_SOUND_MISSION_MOB_07B}, {"mob_08a", STREAMED_SOUND_MISSION_MOB_08A}, {"mob_08b", STREAMED_SOUND_MISSION_MOB_08B}, {"mob_08c", STREAMED_SOUND_MISSION_MOB_08C}, {"mob_08d", STREAMED_SOUND_MISSION_MOB_08D}, {"mob_08e", STREAMED_SOUND_MISSION_MOB_08E}, {"mob_08f", STREAMED_SOUND_MISSION_MOB_08F}, {"mob_08g", STREAMED_SOUND_MISSION_MOB_08G}, {"mob_09a", STREAMED_SOUND_MISSION_MOB_09A}, {"mob_09b", STREAMED_SOUND_MISSION_MOB_09B}, {"mob_09c", STREAMED_SOUND_MISSION_MOB_09C}, {"mob_09d", STREAMED_SOUND_MISSION_MOB_09D}, {"mob_09e", STREAMED_SOUND_MISSION_MOB_09E}, {"mob_09f", STREAMED_SOUND_MISSION_MOB_09F}, {"mob_10a", STREAMED_SOUND_MISSION_MOB_10A}, {"mob_10b", STREAMED_SOUND_MISSION_MOB_10B}, {"mob_10c", STREAMED_SOUND_MISSION_MOB_10C}, {"mob_10d", STREAMED_SOUND_MISSION_MOB_10D}, {"mob_10e", STREAMED_SOUND_MISSION_MOB_10E}, {"mob_11a", STREAMED_SOUND_MISSION_MOB_11A}, {"mob_11b", STREAMED_SOUND_MISSION_MOB_11B}, {"mob_11c", STREAMED_SOUND_MISSION_MOB_11C}, {"mob_11d", STREAMED_SOUND_MISSION_MOB_11D}, {"mob_11e", STREAMED_SOUND_MISSION_MOB_11E}, {"mob_11f", STREAMED_SOUND_MISSION_MOB_11F}, {"mob_14a", STREAMED_SOUND_MISSION_MOB_14A}, {"mob_14b", STREAMED_SOUND_MISSION_MOB_14B}, {"mob_14c", STREAMED_SOUND_MISSION_MOB_14C}, {"mob_14d", STREAMED_SOUND_MISSION_MOB_14D}, {"mob_14e", STREAMED_SOUND_MISSION_MOB_14E}, {"mob_14f", STREAMED_SOUND_MISSION_MOB_14F}, {"mob_14g", STREAMED_SOUND_MISSION_MOB_14G}, {"mob_14h", STREAMED_SOUND_MISSION_MOB_14H}, {"mob_16a", STREAMED_SOUND_MISSION_MOB_16A}, {"mob_16b", STREAMED_SOUND_MISSION_MOB_16B}, {"mob_16c", STREAMED_SOUND_MISSION_MOB_16C}, {"mob_16d", STREAMED_SOUND_MISSION_MOB_16D}, {"mob_16e", STREAMED_SOUND_MISSION_MOB_16E}, {"mob_16f", STREAMED_SOUND_MISSION_MOB_16F}, {"mob_16g", STREAMED_SOUND_MISSION_MOB_16G}, {"mob_17a", STREAMED_SOUND_MISSION_MOB_17A}, {"mob_17b", STREAMED_SOUND_MISSION_MOB_17B}, {"mob_17c", STREAMED_SOUND_MISSION_MOB_17C}, {"mob_17d", STREAMED_SOUND_MISSION_MOB_17D}, {"mob_17e", STREAMED_SOUND_MISSION_MOB_17E}, {"mob_17g", STREAMED_SOUND_MISSION_MOB_17G}, {"mob_17h", STREAMED_SOUND_MISSION_MOB_17H}, {"mob_17i", STREAMED_SOUND_MISSION_MOB_17I}, {"mob_17j", STREAMED_SOUND_MISSION_MOB_17J}, {"mob_17k", STREAMED_SOUND_MISSION_MOB_17K}, {"mob_17l", STREAMED_SOUND_MISSION_MOB_17L}, {"mob_18a", STREAMED_SOUND_MISSION_MOB_18A}, {"mob_18b", STREAMED_SOUND_MISSION_MOB_18B}, {"mob_18c", STREAMED_SOUND_MISSION_MOB_18C}, {"mob_18d", STREAMED_SOUND_MISSION_MOB_18D}, {"mob_18e", STREAMED_SOUND_MISSION_MOB_18E}, {"mob_18f", STREAMED_SOUND_MISSION_MOB_18F}, {"mob_18g", STREAMED_SOUND_MISSION_MOB_18G}, {"mob_20a", STREAMED_SOUND_MISSION_MOB_20A}, {"mob_20b", STREAMED_SOUND_MISSION_MOB_20B}, {"mob_20c", STREAMED_SOUND_MISSION_MOB_20C}, {"mob_20d", STREAMED_SOUND_MISSION_MOB_20D}, {"mob_20e", STREAMED_SOUND_MISSION_MOB_20E}, {"mob_24a", STREAMED_SOUND_MISSION_MOB_24A}, {"mob_24b", STREAMED_SOUND_MISSION_MOB_24B}, {"mob_24c", STREAMED_SOUND_MISSION_MOB_24C}, {"mob_24d", STREAMED_SOUND_MISSION_MOB_24D}, {"mob_24e", STREAMED_SOUND_MISSION_MOB_24E}, {"mob_24f", STREAMED_SOUND_MISSION_MOB_24F}, {"mob_24g", STREAMED_SOUND_MISSION_MOB_24G}, {"mob_24h", STREAMED_SOUND_MISSION_MOB_24H}, {"mob_25a", STREAMED_SOUND_MISSION_MOB_25A}, {"mob_25b", STREAMED_SOUND_MISSION_MOB_25B}, {"mob_25c", STREAMED_SOUND_MISSION_MOB_25C}, {"mob_25d", STREAMED_SOUND_MISSION_MOB_25D}, {"mob_26a", STREAMED_SOUND_MISSION_MOB_26A}, {"mob_26b", STREAMED_SOUND_MISSION_MOB_26B}, {"mob_26c", STREAMED_SOUND_MISSION_MOB_26C}, {"mob_26d", STREAMED_SOUND_MISSION_MOB_26D}, {"mob_26e", STREAMED_SOUND_MISSION_MOB_26E}, {"mob_29a", STREAMED_SOUND_MISSION_MOB_29A}, {"mob_29b", STREAMED_SOUND_MISSION_MOB_29B}, {"mob_29c", STREAMED_SOUND_MISSION_MOB_29C}, {"mob_29d", STREAMED_SOUND_MISSION_MOB_29D}, {"mob_29e", STREAMED_SOUND_MISSION_MOB_29E}, {"mob_29f", STREAMED_SOUND_MISSION_MOB_29F}, {"mob_29g", STREAMED_SOUND_MISSION_MOB_29G}, {"mob_30a", STREAMED_SOUND_MISSION_MOB_30A}, {"mob_30b", STREAMED_SOUND_MISSION_MOB_30B}, {"mob_30c", STREAMED_SOUND_MISSION_MOB_30C}, {"mob_30d", STREAMED_SOUND_MISSION_MOB_30D}, {"mob_30e", STREAMED_SOUND_MISSION_MOB_30E}, {"mob_30f", STREAMED_SOUND_MISSION_MOB_30F}, {"mob_33a", STREAMED_SOUND_MISSION_MOB_33A}, {"mob_33b", STREAMED_SOUND_MISSION_MOB_33B}, {"mob_33c", STREAMED_SOUND_MISSION_MOB_33C}, {"mob_33d", STREAMED_SOUND_MISSION_MOB_33D}, {"mob_34a", STREAMED_SOUND_MISSION_MOB_34A}, {"mob_34b", STREAMED_SOUND_MISSION_MOB_34B}, {"mob_34c", STREAMED_SOUND_MISSION_MOB_34C}, {"mob_34d", STREAMED_SOUND_MISSION_MOB_34D}, {"mob_35a", STREAMED_SOUND_MISSION_MOB_35A}, {"mob_35b", STREAMED_SOUND_MISSION_MOB_35B}, {"mob_35c", STREAMED_SOUND_MISSION_MOB_35C}, {"mob_35d", STREAMED_SOUND_MISSION_MOB_35D}, {"mob_36a", STREAMED_SOUND_MISSION_MOB_36A}, {"mob_36b", STREAMED_SOUND_MISSION_MOB_36B}, {"mob_36c", STREAMED_SOUND_MISSION_MOB_36C}, {"mob_40a", STREAMED_SOUND_MISSION_MOB_40A}, {"mob_40b", STREAMED_SOUND_MISSION_MOB_40B}, {"mob_40c", STREAMED_SOUND_MISSION_MOB_40C}, {"mob_40d", STREAMED_SOUND_MISSION_MOB_40D}, {"mob_40e", STREAMED_SOUND_MISSION_MOB_40E}, {"mob_40f", STREAMED_SOUND_MISSION_MOB_40F}, {"mob_40g", STREAMED_SOUND_MISSION_MOB_40G}, {"mob_40h", STREAMED_SOUND_MISSION_MOB_40H}, {"mob_40i", STREAMED_SOUND_MISSION_MOB_40I}, {"mob_41a", STREAMED_SOUND_MISSION_MOB_41A}, {"mob_41b", STREAMED_SOUND_MISSION_MOB_41B}, {"mob_41c", STREAMED_SOUND_MISSION_MOB_41C}, {"mob_41d", STREAMED_SOUND_MISSION_MOB_41D}, {"mob_41e", STREAMED_SOUND_MISSION_MOB_41E}, {"mob_41f", STREAMED_SOUND_MISSION_MOB_41F}, {"mob_41g", STREAMED_SOUND_MISSION_MOB_41G}, {"mob_41h", STREAMED_SOUND_MISSION_MOB_41H}, {"mob_42a", STREAMED_SOUND_MISSION_MOB_42A}, {"mob_42b", STREAMED_SOUND_MISSION_MOB_42B}, {"mob_42c", STREAMED_SOUND_MISSION_MOB_42C}, {"mob_42d", STREAMED_SOUND_MISSION_MOB_42D}, {"mob_42e", STREAMED_SOUND_MISSION_MOB_42E}, {"mob_43a", STREAMED_SOUND_MISSION_MOB_43A}, {"mob_43b", STREAMED_SOUND_MISSION_MOB_43B}, {"mob_43c", STREAMED_SOUND_MISSION_MOB_43C}, {"mob_43d", STREAMED_SOUND_MISSION_MOB_43D}, {"mob_43e", STREAMED_SOUND_MISSION_MOB_43E}, {"mob_43f", STREAMED_SOUND_MISSION_MOB_43F}, {"mob_43g", STREAMED_SOUND_MISSION_MOB_43G}, {"mob_43h", STREAMED_SOUND_MISSION_MOB_43H}, {"mob_45a", STREAMED_SOUND_MISSION_MOB_45A}, {"mob_45b", STREAMED_SOUND_MISSION_MOB_45B}, {"mob_45c", STREAMED_SOUND_MISSION_MOB_45C}, {"mob_45d", STREAMED_SOUND_MISSION_MOB_45D}, {"mob_45e", STREAMED_SOUND_MISSION_MOB_45E}, {"mob_45f", STREAMED_SOUND_MISSION_MOB_45F}, {"mob_45g", STREAMED_SOUND_MISSION_MOB_45G}, {"mob_45h", STREAMED_SOUND_MISSION_MOB_45H}, {"mob_45i", STREAMED_SOUND_MISSION_MOB_45I}, {"mob_45j", STREAMED_SOUND_MISSION_MOB_45J}, {"mob_45k", STREAMED_SOUND_MISSION_MOB_45K}, {"mob_45l", STREAMED_SOUND_MISSION_MOB_45L}, {"mob_45m", STREAMED_SOUND_MISSION_MOB_45M}, {"mob_45n", STREAMED_SOUND_MISSION_MOB_45N}, {"mob_46a", STREAMED_SOUND_MISSION_MOB_46A}, {"mob_46b", STREAMED_SOUND_MISSION_MOB_46B}, {"mob_46c", STREAMED_SOUND_MISSION_MOB_46C}, {"mob_46d", STREAMED_SOUND_MISSION_MOB_46D}, {"mob_46e", STREAMED_SOUND_MISSION_MOB_46E}, {"mob_46f", STREAMED_SOUND_MISSION_MOB_46F}, {"mob_46g", STREAMED_SOUND_MISSION_MOB_46G}, {"mob_46h", STREAMED_SOUND_MISSION_MOB_46H}, {"mob_47a", STREAMED_SOUND_MISSION_MOB_47A}, {"mob_52a", STREAMED_SOUND_MISSION_MOB_52A}, {"mob_52b", STREAMED_SOUND_MISSION_MOB_52B}, {"mob_52c", STREAMED_SOUND_MISSION_MOB_52C}, {"mob_52d", STREAMED_SOUND_MISSION_MOB_52D}, {"mob_52e", STREAMED_SOUND_MISSION_MOB_52E}, {"mob_52f", STREAMED_SOUND_MISSION_MOB_52F}, {"mob_52g", STREAMED_SOUND_MISSION_MOB_52G}, {"mob_52h", STREAMED_SOUND_MISSION_MOB_52H}, {"mob_54a", STREAMED_SOUND_MISSION_MOB_54A}, {"mob_54b", STREAMED_SOUND_MISSION_MOB_54B}, {"mob_54c", STREAMED_SOUND_MISSION_MOB_54C}, {"mob_54d", STREAMED_SOUND_MISSION_MOB_54D}, {"mob_54e", STREAMED_SOUND_MISSION_MOB_54E}, {"mob_55a", STREAMED_SOUND_MISSION_MOB_55A}, {"mob_55b", STREAMED_SOUND_MISSION_MOB_55B}, {"mob_55c", STREAMED_SOUND_MISSION_MOB_55C}, {"mob_55d", STREAMED_SOUND_MISSION_MOB_55D}, {"mob_55e", STREAMED_SOUND_MISSION_MOB_55E}, {"mob_55f", STREAMED_SOUND_MISSION_MOB_55F}, {"mob_56a", STREAMED_SOUND_MISSION_MOB_56A}, {"mob_56b", STREAMED_SOUND_MISSION_MOB_56B}, {"mob_56c", STREAMED_SOUND_MISSION_MOB_56C}, {"mob_56d", STREAMED_SOUND_MISSION_MOB_56D}, {"mob_56e", STREAMED_SOUND_MISSION_MOB_56E}, {"mob_56f", STREAMED_SOUND_MISSION_MOB_56F}, {"mob_57a", STREAMED_SOUND_MISSION_MOB_57A}, {"mob_57b", STREAMED_SOUND_MISSION_MOB_57B}, {"mob_57c", STREAMED_SOUND_MISSION_MOB_57C}, {"mob_57d", STREAMED_SOUND_MISSION_MOB_57D}, {"mob_57e", STREAMED_SOUND_MISSION_MOB_57E}, {"mob_58a", STREAMED_SOUND_MISSION_MOB_58A}, {"mob_58b", STREAMED_SOUND_MISSION_MOB_58B}, {"mob_58c", STREAMED_SOUND_MISSION_MOB_58C}, {"mob_58d", STREAMED_SOUND_MISSION_MOB_58D}, {"mob_58e", STREAMED_SOUND_MISSION_MOB_58E}, {"mob_58f", STREAMED_SOUND_MISSION_MOB_58F}, {"mob_58g", STREAMED_SOUND_MISSION_MOB_58G}, {"mob_61a", STREAMED_SOUND_MISSION_MOB_61A}, {"mob_61b", STREAMED_SOUND_MISSION_MOB_61B}, {"mob_62a", STREAMED_SOUND_MISSION_MOB_62A}, {"mob_62b", STREAMED_SOUND_MISSION_MOB_62B}, {"mob_62c", STREAMED_SOUND_MISSION_MOB_62C}, {"mob_62d", STREAMED_SOUND_MISSION_MOB_62D}, {"mob_63a", STREAMED_SOUND_MISSION_MOB_63A}, {"mob_63b", STREAMED_SOUND_MISSION_MOB_63B}, {"mob_63c", STREAMED_SOUND_MISSION_MOB_63C}, {"mob_63d", STREAMED_SOUND_MISSION_MOB_63D}, {"mob_63e", STREAMED_SOUND_MISSION_MOB_63E}, {"mob_63f", STREAMED_SOUND_MISSION_MOB_63F}, {"mob_63g", STREAMED_SOUND_MISSION_MOB_63G}, {"mob_63h", STREAMED_SOUND_MISSION_MOB_63H}, {"mob_63i", STREAMED_SOUND_MISSION_MOB_63I}, {"mob_63j", STREAMED_SOUND_MISSION_MOB_63J}, {"mob_66a", STREAMED_SOUND_MISSION_MOB_66A}, {"mob_66b", STREAMED_SOUND_MISSION_MOB_66B}, {"mob_68a", STREAMED_SOUND_MISSION_MOB_68A}, {"mob_68b", STREAMED_SOUND_MISSION_MOB_68B}, {"mob_68c", STREAMED_SOUND_MISSION_MOB_68C}, {"mob_68d", STREAMED_SOUND_MISSION_MOB_68D}, {"mob_70a", STREAMED_SOUND_MISSION_MOB_70A}, {"mob_70b", STREAMED_SOUND_MISSION_MOB_70B}, {"mob_71a", STREAMED_SOUND_MISSION_MOB_71A}, {"mob_71b", STREAMED_SOUND_MISSION_MOB_71B}, {"mob_71c", STREAMED_SOUND_MISSION_MOB_71C}, {"mob_71d", STREAMED_SOUND_MISSION_MOB_71D}, {"mob_71e", STREAMED_SOUND_MISSION_MOB_71E}, {"mob_71f", STREAMED_SOUND_MISSION_MOB_71F}, {"mob_71g", STREAMED_SOUND_MISSION_MOB_71G}, {"mob_71h", STREAMED_SOUND_MISSION_MOB_71H}, {"mob_71i", STREAMED_SOUND_MISSION_MOB_71I}, {"mob_71j", STREAMED_SOUND_MISSION_MOB_71J}, {"mob_71k", STREAMED_SOUND_MISSION_MOB_71K}, {"mob_71l", STREAMED_SOUND_MISSION_MOB_71L}, {"mob_71m", STREAMED_SOUND_MISSION_MOB_71M}, {"mob_71n", STREAMED_SOUND_MISSION_MOB_71N}, {"mob_72a", STREAMED_SOUND_MISSION_MOB_72A}, {"mob_72b", STREAMED_SOUND_MISSION_MOB_72B}, {"mob_72c", STREAMED_SOUND_MISSION_MOB_72C}, {"mob_72d", STREAMED_SOUND_MISSION_MOB_72D}, {"mob_72e", STREAMED_SOUND_MISSION_MOB_72E}, {"mob_72f", STREAMED_SOUND_MISSION_MOB_72F}, {"mob_72g", STREAMED_SOUND_MISSION_MOB_72G}, {"mob_73a", STREAMED_SOUND_MISSION_MOB_73A}, {"mob_73c", STREAMED_SOUND_MISSION_MOB_73C}, {"mob_73d", STREAMED_SOUND_MISSION_MOB_73D}, {"mob_73f", STREAMED_SOUND_MISSION_MOB_73F}, {"mob_73g", STREAMED_SOUND_MISSION_MOB_73G}, {"mob_73i", STREAMED_SOUND_MISSION_MOB_73I}, {"mob_95a", STREAMED_SOUND_MISSION_MOB_95A}, {"mob_96a", STREAMED_SOUND_MISSION_MOB_96A}, {"mob_98a", STREAMED_SOUND_MISSION_MOB_98A}, {"mob_99a", STREAMED_SOUND_MISSION_MOB_99A}, {"job1_1b", STREAMED_SOUND_MISSION_JOB1_1B}, {"job1_1c", STREAMED_SOUND_MISSION_JOB1_1C}, {"job1_1d", STREAMED_SOUND_MISSION_JOB1_1D}, {"job2_1b", STREAMED_SOUND_MISSION_JOB2_1B}, {"job2_2", STREAMED_SOUND_MISSION_JOB2_2}, {"job2_3", STREAMED_SOUND_MISSION_JOB2_3}, {"job2_4", STREAMED_SOUND_MISSION_JOB2_4}, {"job2_5", STREAMED_SOUND_MISSION_JOB2_5}, {"job2_6", STREAMED_SOUND_MISSION_JOB2_6}, {"job2_7", STREAMED_SOUND_MISSION_JOB2_7}, {"job2_8", STREAMED_SOUND_MISSION_JOB2_8}, {"job2_9", STREAMED_SOUND_MISSION_JOB2_9}, {"job3_1", STREAMED_SOUND_MISSION_JOB3_1}, {"job3_2", STREAMED_SOUND_MISSION_JOB3_2}, {"job3_3", STREAMED_SOUND_MISSION_JOB3_3}, {"job4_1", STREAMED_SOUND_MISSION_JOB4_1}, {"job4_2", STREAMED_SOUND_MISSION_JOB4_2}, {"job4_3", STREAMED_SOUND_MISSION_JOB4_3}, {"job5_1", STREAMED_SOUND_MISSION_JOB5_1}, {"job5_2", STREAMED_SOUND_MISSION_JOB5_2}, {"job5_3", STREAMED_SOUND_MISSION_JOB5_3}, {"bjm1_20", STREAMED_SOUND_MISSION_BJM1_20}, {"bjm1_4", STREAMED_SOUND_MISSION_BJM1_4}, {"bjm1_5", STREAMED_SOUND_MISSION_BJM1_5}, {"merc_39", STREAMED_SOUND_MISSION_MERC_39}, {"mono_1", STREAMED_SOUND_MISSION_MONO_1}, {"mono_2", STREAMED_SOUND_MISSION_MONO_2}, {"mono_3", STREAMED_SOUND_MISSION_MONO_3}, {"mono_4", STREAMED_SOUND_MISSION_MONO_4}, {"mono_5", STREAMED_SOUND_MISSION_MONO_5}, {"mono_6", STREAMED_SOUND_MISSION_MONO_6}, {"mono_7", STREAMED_SOUND_MISSION_MONO_7}, {"mono_8", STREAMED_SOUND_MISSION_MONO_8}, {"mono_9", STREAMED_SOUND_MISSION_MONO_9}, {"mono10", STREAMED_SOUND_MISSION_MONO10}, {"mono11", STREAMED_SOUND_MISSION_MONO11}, {"mono12", STREAMED_SOUND_MISSION_MONO12}, {"mono13", STREAMED_SOUND_MISSION_MONO13}, {"mono14", STREAMED_SOUND_MISSION_MONO14}, {"mono15", STREAMED_SOUND_MISSION_MONO15}, {"mono16", STREAMED_SOUND_MISSION_MONO16}, {"fud_01", STREAMED_SOUND_MISSION_FUD_01}, {"fud_02", STREAMED_SOUND_MISSION_FUD_02}, {"fud_03", STREAMED_SOUND_MISSION_FUD_03}, {"fud_04", STREAMED_SOUND_MISSION_FUD_04}, {"fud_05", STREAMED_SOUND_MISSION_FUD_05}, {"fud_06", STREAMED_SOUND_MISSION_FUD_06}, {"fud_07", STREAMED_SOUND_MISSION_FUD_07}, {"fud_08", STREAMED_SOUND_MISSION_FUD_08}, {"fud_09", STREAMED_SOUND_MISSION_FUD_09}, {"fud_10", STREAMED_SOUND_MISSION_FUD_10}, {"fud_11", STREAMED_SOUND_MISSION_FUD_11}, {"fud_12", STREAMED_SOUND_MISSION_FUD_12}, {"fud_13", STREAMED_SOUND_MISSION_FUD_13}, {"fud_14", STREAMED_SOUND_MISSION_FUD_14}, {"fud_15", STREAMED_SOUND_MISSION_FUD_15}, {"fud_16", STREAMED_SOUND_MISSION_FUD_16}, {"fud_17", STREAMED_SOUND_MISSION_FUD_17}, {"fud_18", STREAMED_SOUND_MISSION_FUD_18}, {"fud_19", STREAMED_SOUND_MISSION_FUD_19}, {"fud_20", STREAMED_SOUND_MISSION_FUD_20}, {"burg_01", STREAMED_SOUND_MISSION_BURG_01}, {"burg_02", STREAMED_SOUND_MISSION_BURG_02}, {"burg_03", STREAMED_SOUND_MISSION_BURG_03}, {"burg_04", STREAMED_SOUND_MISSION_BURG_04}, {"burg_05", STREAMED_SOUND_MISSION_BURG_05}, {"burg_06", STREAMED_SOUND_MISSION_BURG_06}, {"burg_07", STREAMED_SOUND_MISSION_BURG_07}, {"burg_08", STREAMED_SOUND_MISSION_BURG_08}, {"burg_09", STREAMED_SOUND_MISSION_BURG_09}, {"burg_10", STREAMED_SOUND_MISSION_BURG_10}, {"burg_11", STREAMED_SOUND_MISSION_BURG_11}, {"burg_12", STREAMED_SOUND_MISSION_BURG_12}, {"crust01", STREAMED_SOUND_MISSION_CRUST01}, {"crust02", STREAMED_SOUND_MISSION_CRUST02}, {"crust03", STREAMED_SOUND_MISSION_CRUST03}, {"crust04", STREAMED_SOUND_MISSION_CRUST04}, {"crust05", STREAMED_SOUND_MISSION_CRUST05}, {"crust06", STREAMED_SOUND_MISSION_CRUST06}, {"crust07", STREAMED_SOUND_MISSION_CRUST07}, {"crust08", STREAMED_SOUND_MISSION_CRUST08}, {"crust09", STREAMED_SOUND_MISSION_CRUST09}, {"band_01", STREAMED_SOUND_MISSION_BAND_01}, {"band_02", STREAMED_SOUND_MISSION_BAND_02}, {"band_03", STREAMED_SOUND_MISSION_BAND_03}, {"band_04", STREAMED_SOUND_MISSION_BAND_04}, {"band_05", STREAMED_SOUND_MISSION_BAND_05}, {"band_06", STREAMED_SOUND_MISSION_BAND_06}, {"band_07", STREAMED_SOUND_MISSION_BAND_07}, {"band_08", STREAMED_SOUND_MISSION_BAND_08}, {"shaft01", STREAMED_SOUND_MISSION_SHAFT01}, {"shaft02", STREAMED_SOUND_MISSION_SHAFT02}, {"shaft03", STREAMED_SOUND_MISSION_SHAFT03}, {"shaft04", STREAMED_SOUND_MISSION_SHAFT04}, {"shaft05", STREAMED_SOUND_MISSION_SHAFT05}, {"shaft06", STREAMED_SOUND_MISSION_SHAFT06}, {"shaft07", STREAMED_SOUND_MISSION_SHAFT07}, {"shaft08", STREAMED_SOUND_MISSION_SHAFT08}, {"piss_01", STREAMED_SOUND_MISSION_PISS_01}, {"piss_02", STREAMED_SOUND_MISSION_PISS_02}, {"piss_03", STREAMED_SOUND_MISSION_PISS_03}, {"piss_04", STREAMED_SOUND_MISSION_PISS_04}, {"piss_05", STREAMED_SOUND_MISSION_PISS_05}, {"piss_06", STREAMED_SOUND_MISSION_PISS_06}, {"piss_07", STREAMED_SOUND_MISSION_PISS_07}, {"piss_08", STREAMED_SOUND_MISSION_PISS_08}, {"piss_09", STREAMED_SOUND_MISSION_PISS_09}, {"piss_10", STREAMED_SOUND_MISSION_PISS_10}, {"piss_11", STREAMED_SOUND_MISSION_PISS_11}, {"piss_12", STREAMED_SOUND_MISSION_PISS_12}, {"piss_13", STREAMED_SOUND_MISSION_PISS_13}, {"piss_14", STREAMED_SOUND_MISSION_PISS_14}, {"piss_15", STREAMED_SOUND_MISSION_PISS_15}, {"piss_16", STREAMED_SOUND_MISSION_PISS_16}, {"piss_17", STREAMED_SOUND_MISSION_PISS_17}, {"piss_18", STREAMED_SOUND_MISSION_PISS_18}, {"piss_19", STREAMED_SOUND_MISSION_PISS_19}, {"gimme01", STREAMED_SOUND_MISSION_GIMME01}, {"gimme02", STREAMED_SOUND_MISSION_GIMME02}, {"gimme03", STREAMED_SOUND_MISSION_GIMME03}, {"gimme04", STREAMED_SOUND_MISSION_GIMME04}, {"gimme05", STREAMED_SOUND_MISSION_GIMME05}, {"gimme06", STREAMED_SOUND_MISSION_GIMME06}, {"gimme07", STREAMED_SOUND_MISSION_GIMME07}, {"gimme08", STREAMED_SOUND_MISSION_GIMME08}, {"gimme09", STREAMED_SOUND_MISSION_GIMME09}, {"gimme10", STREAMED_SOUND_MISSION_GIMME10}, {"gimme11", STREAMED_SOUND_MISSION_GIMME11}, {"gimme12", STREAMED_SOUND_MISSION_GIMME12}, {"gimme13", STREAMED_SOUND_MISSION_GIMME13}, {"gimme14", STREAMED_SOUND_MISSION_GIMME14}, {"gimme15", STREAMED_SOUND_MISSION_GIMME15}, {"bust_01", STREAMED_SOUND_MISSION_BUST_01}, {"bust_02", STREAMED_SOUND_MISSION_BUST_02}, {"bust_03", STREAMED_SOUND_MISSION_BUST_03}, {"bust_04", STREAMED_SOUND_MISSION_BUST_04}, {"bust_05", STREAMED_SOUND_MISSION_BUST_05}, {"bust_06", STREAMED_SOUND_MISSION_BUST_06}, {"bust_07", STREAMED_SOUND_MISSION_BUST_07}, {"bust_08", STREAMED_SOUND_MISSION_BUST_08}, {"bust_09", STREAMED_SOUND_MISSION_BUST_09}, {"bust_10", STREAMED_SOUND_MISSION_BUST_10}, {"bust_11", STREAMED_SOUND_MISSION_BUST_11}, {"bust_12", STREAMED_SOUND_MISSION_BUST_12}, {"bust_13", STREAMED_SOUND_MISSION_BUST_13}, {"bust_14", STREAMED_SOUND_MISSION_BUST_14}, {"bust_15", STREAMED_SOUND_MISSION_BUST_15}, {"bust_16", STREAMED_SOUND_MISSION_BUST_16}, {"bust_17", STREAMED_SOUND_MISSION_BUST_17}, {"bust_18", STREAMED_SOUND_MISSION_BUST_18}, {"bust_19", STREAMED_SOUND_MISSION_BUST_19}, {"bust_20", STREAMED_SOUND_MISSION_BUST_20}, {"bust_21", STREAMED_SOUND_MISSION_BUST_21}, {"bust_22", STREAMED_SOUND_MISSION_BUST_22}, {"bust_23", STREAMED_SOUND_MISSION_BUST_23}, {"bust_24", STREAMED_SOUND_MISSION_BUST_24}, {"bust_25", STREAMED_SOUND_MISSION_BUST_25}, {"bust_26", STREAMED_SOUND_MISSION_BUST_26}, {"bust_27", STREAMED_SOUND_MISSION_BUST_27}, {"bust_28", STREAMED_SOUND_MISSION_BUST_28}, {nil, 0} }; int32 FindMissionAudioSfx(const char *name) { for (uint32 i = 0; MissionAudioNameSfxAssoc[i].m_pName != nil; ++i) { if (!CGeneral::faststricmp(MissionAudioNameSfxAssoc[i].m_pName, name)) return MissionAudioNameSfxAssoc[i].m_nId; } debug("Can't find mission audio %s", name); return NO_SAMPLE; } bool cAudioManager::MissionScriptAudioUsesPoliceChannel(int32 soundMission) const { return false; } void cAudioManager::PreloadMissionAudio(uint8 slot, Const char *name) { if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS) { int32 missionAudioSfx = FindMissionAudioSfx(name); if (missionAudioSfx != NO_SAMPLE) { m_sMissionAudio.m_nSampleIndex[slot] = missionAudioSfx; m_sMissionAudio.m_nLoadingStatus[slot] = LOADING_STATUS_NOT_LOADED; m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_STOPPED; m_sMissionAudio.m_bIsPlaying[slot] = false; m_sMissionAudio.m_nMissionAudioCounter[slot] = m_nTimeSpent * SampleManager.GetStreamedFileLength(missionAudioSfx) / 1000; m_sMissionAudio.m_nMissionAudioCounter[slot] *= 4; m_sMissionAudio.m_bIsPlayed[slot] = false; m_sMissionAudio.m_bPredefinedProperties[slot] = true; g_bMissionAudioLoadFailed[slot] = false; } } } uint8 cAudioManager::GetMissionAudioLoadingStatus(uint8 slot) const { if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS) return m_sMissionAudio.m_nLoadingStatus[slot]; return LOADING_STATUS_LOADED; } void cAudioManager::SetMissionAudioLocation(uint8 slot, float x, float y, float z) { if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS) { m_sMissionAudio.m_bPredefinedProperties[slot] = false; m_sMissionAudio.m_vecPos[slot] = CVector(x, y, z); } } void cAudioManager::PlayLoadedMissionAudio(uint8 slot) { if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS && m_sMissionAudio.m_nSampleIndex[slot] != NO_SAMPLE && m_sMissionAudio.m_nLoadingStatus[slot] == LOADING_STATUS_LOADED && m_sMissionAudio.m_nPlayStatus[slot] == PLAY_STATUS_STOPPED) m_sMissionAudio.m_bIsPlayed[slot] = true; } bool cAudioManager::ShouldDuckMissionAudio(uint8 slot) const { if (IsMissionAudioSamplePlaying(slot)) return m_sMissionAudio.m_nSampleIndex[slot] != STREAMED_SOUND_MISSION_ROK2_01; return false; } bool cAudioManager::IsMissionAudioSamplePlaying(uint8 slot) const { if (m_bIsInitialised) { if (slot < MISSION_AUDIO_SLOTS) return m_sMissionAudio.m_nPlayStatus[slot] == PLAY_STATUS_PLAYING; else return true; } else { static int32 cPretendFrame[MISSION_AUDIO_SLOTS] = { 1, 1 }; return (cPretendFrame[slot]++ % 64) != 0; } } bool cAudioManager::IsMissionAudioSampleFinished(uint8 slot) { if (m_bIsInitialised) { if (slot < MISSION_AUDIO_SLOTS) return m_sMissionAudio.m_nPlayStatus[slot] == PLAY_STATUS_FINISHED; else return true; } static int32 cPretendFrame[MISSION_AUDIO_SLOTS] = { 1, 1 }; return (cPretendFrame[slot]++ % 64) == 0; } void cAudioManager::ClearMissionAudio(uint8 slot) { if (m_bIsInitialised && slot < MISSION_AUDIO_SLOTS) { m_sMissionAudio.m_nSampleIndex[slot] = NO_SAMPLE; m_sMissionAudio.m_nLoadingStatus[slot] = LOADING_STATUS_NOT_LOADED; m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_STOPPED; m_sMissionAudio.m_bIsPlaying[slot] = false; m_sMissionAudio.m_bIsPlayed[slot] = false; m_sMissionAudio.m_bPredefinedProperties[slot] = true; m_sMissionAudio.m_nMissionAudioCounter[slot] = 0; m_sMissionAudio.m_bIsMobile[slot] = false; SampleManager.StopStreamedFile(slot + 1); } } void cAudioManager::ProcessMissionAudioSlot(uint8 slot) { float dist; uint8 emittingVol; uint8 pan; float distSquared; CVector vec; static uint8 nCheckPlayingDelay[MISSION_AUDIO_SLOTS] = { 0, 0 }; static uint8 nFramesUntilFailedLoad[MISSION_AUDIO_SLOTS] = { 0, 0 }; static uint8 nFramesForPretendPlaying[MISSION_AUDIO_SLOTS] = { 0, 0 }; if (m_sMissionAudio.m_nSampleIndex[slot] == NO_SAMPLE) return; switch (m_sMissionAudio.m_nLoadingStatus[slot]) { case LOADING_STATUS_NOT_LOADED: SampleManager.PreloadStreamedFile(m_sMissionAudio.m_nSampleIndex[slot], slot + 1); m_sMissionAudio.m_nLoadingStatus[slot] = LOADING_STATUS_LOADED; nFramesUntilFailedLoad[slot] = 0; break; case LOADING_STATUS_LOADED: if (!m_sMissionAudio.m_bIsPlayed[slot]) return; if (g_bMissionAudioLoadFailed[slot]) { if (m_bTimerJustReset) { ClearMissionAudio(slot); SampleManager.StopStreamedFile(slot + 1); nFramesForPretendPlaying[slot] = 0; nCheckPlayingDelay[slot] = 0; nFramesUntilFailedLoad[slot] = 0; } else if (!m_nUserPause) { if (++nFramesForPretendPlaying[slot] < 90) { m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_PLAYING; } else { m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_FINISHED; m_sMissionAudio.m_nSampleIndex[slot] = NO_SAMPLE; } } break; } switch (m_sMissionAudio.m_nPlayStatus[slot]) { case PLAY_STATUS_STOPPED: if (MissionScriptAudioUsesPoliceChannel(m_sMissionAudio.m_nSampleIndex[slot])) { SetMissionScriptPoliceAudio(m_sMissionAudio.m_nSampleIndex[slot]); } else { if (m_nUserPause) SampleManager.PauseStream(1, slot + 1); if (m_sMissionAudio.m_bPredefinedProperties[slot]) { if (m_sMissionAudio.m_nSampleIndex[slot] == STREAMED_SOUND_MISSION_CAMERAL) SampleManager.SetStreamedVolumeAndPan(80, 0, 1, slot + 1); else if (m_sMissionAudio.m_nSampleIndex[slot] == STREAMED_SOUND_MISSION_CAMERAR) SampleManager.SetStreamedVolumeAndPan(80, 127, 1, slot + 1); else SampleManager.SetStreamedVolumeAndPan(80, 63, 1, slot + 1); } else { distSquared = GetDistanceSquared(m_sMissionAudio.m_vecPos[slot]); if (distSquared >= SQR(80.0f)) { emittingVol = 0; pan = 63; } else { emittingVol = 80; if (distSquared > 0.0f) { dist = Sqrt(distSquared); emittingVol = ComputeVolume(80, 80.0f, dist); } TranslateEntity(&m_sMissionAudio.m_vecPos[slot], &vec); pan = ComputePan(80.f, &vec); } SampleManager.SetStreamedVolumeAndPan(emittingVol, pan, 1, slot + 1); } SampleManager.StartPreloadedStreamedFile(slot + 1); } m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_PLAYING; nCheckPlayingDelay[slot] = 30; if (m_sMissionAudio.m_nSampleIndex[slot] >= STREAMED_SOUND_MISSION_MOB_01A && m_sMissionAudio.m_nSampleIndex[slot] <= STREAMED_SOUND_MISSION_MOB_99A) m_sMissionAudio.m_bIsMobile[slot] = true; break; case PLAY_STATUS_PLAYING: if (m_bTimerJustReset) { ClearMissionAudio(slot); SampleManager.StopStreamedFile(slot + 1); break; } if (MissionScriptAudioUsesPoliceChannel(m_sMissionAudio.m_nSampleIndex[slot])) { if (!m_nUserPause) { if (nCheckPlayingDelay[slot]) { --nCheckPlayingDelay[slot]; } else if (GetMissionScriptPoliceAudioPlayingStatus() == PLAY_STATUS_FINISHED || m_sMissionAudio.m_nMissionAudioCounter[slot]-- == 0) { m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_FINISHED; m_sMissionAudio.m_nSampleIndex[slot] = NO_SAMPLE; SampleManager.StopStreamedFile(slot + 1); m_sMissionAudio.m_nMissionAudioCounter[slot] = 0; } } } else if (m_sMissionAudio.m_bIsPlaying[slot]) { if (SampleManager.IsStreamPlaying(slot + 1) || m_nUserPause || m_nPreviousUserPause) { if (m_nUserPause) SampleManager.PauseStream(1, slot + 1); else { SampleManager.PauseStream(0, slot + 1); if (!m_sMissionAudio.m_bPredefinedProperties[slot]) { distSquared = GetDistanceSquared(m_sMissionAudio.m_vecPos[slot]); if (distSquared >= SQR(80.0f)) { emittingVol = 0; pan = 63; } else { emittingVol = 127; if (distSquared > 0.0f) { dist = Sqrt(distSquared); emittingVol = ComputeVolume(127, 80.0f, dist); } TranslateEntity(&m_sMissionAudio.m_vecPos[slot], &vec); pan = ComputePan(80.f, &vec); } SampleManager.SetStreamedVolumeAndPan(emittingVol, pan, 1, slot + 1); } } } else if (m_sMissionAudio.m_nSampleIndex[slot] == STREAMED_SOUND_MISSION_ROK2_01) { m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_STOPPED; } else { m_sMissionAudio.m_nPlayStatus[slot] = PLAY_STATUS_FINISHED; if (m_sMissionAudio.m_nSampleIndex[slot] >= STREAMED_SOUND_MISSION_MOB_01A && m_sMissionAudio.m_nSampleIndex[slot] <= STREAMED_SOUND_MISSION_MOB_99A) m_sMissionAudio.m_bIsMobile[slot] = false; m_sMissionAudio.m_nSampleIndex[slot] = NO_SAMPLE; SampleManager.StopStreamedFile(slot + 1); m_sMissionAudio.m_nMissionAudioCounter[slot] = 0; } } else { if (m_nUserPause) break; if (nCheckPlayingDelay[slot]--) { if (!SampleManager.IsStreamPlaying(slot + 1)) break; nCheckPlayingDelay[slot] = 0; } m_sMissionAudio.m_bIsPlaying[slot] = true; } break; default: break; } break; case LOADING_STATUS_FAILED: if (++nFramesUntilFailedLoad[slot] >= 120) { nFramesForPretendPlaying[slot] = 0; g_bMissionAudioLoadFailed[slot] = true; nFramesUntilFailedLoad[slot] = 0; m_sMissionAudio.m_nLoadingStatus[slot] = LOADING_STATUS_LOADED; } break; default: break; } } void cAudioManager::ProcessMissionAudio() { if (!m_bIsInitialised) return; for (int i = 0; i < MISSION_AUDIO_SLOTS; i++) ProcessMissionAudioSlot(i); if (m_sMissionAudio.m_bIsMobile[0] || m_sMissionAudio.m_bIsMobile[1]) field_5538 = 64; else if (field_5538 < 127) { field_5538 += 5; if (field_5538 > 127) field_5538 = 127; } } #pragma endregion All the mission audio stuff ================================================ FILE: src/audio/AudioManager.cpp ================================================ #include "common.h" #include "AudioManager.h" #include "audio_enums.h" #include "AudioScriptObject.h" #include "MusicManager.h" #include "Timer.h" #include "DMAudio.h" #include "sampman.h" #include "Camera.h" #include "World.h" #include "ZoneCull.h" cAudioManager AudioManager; const int channels = ARRAY_SIZE(AudioManager.m_asActiveSamples); const int policeChannel = channels + 1; const int allChannels = channels + 2; #define SPEED_OF_SOUND 343.f #define TIME_SPENT 40 cAudioManager::cAudioManager() { m_bIsInitialised = false; m_bReverb = true; field_6 = 0; m_fSpeedOfSound = SPEED_OF_SOUND / TIME_SPENT; m_nTimeSpent = TIME_SPENT; m_nActiveSamples = NUM_SOUNDS_SAMPLES_SLOTS; m_nActiveSampleQueue = 1; ClearRequestedQueue(); m_nActiveSampleQueue = 0; ClearRequestedQueue(); ClearActiveSamples(); GenerateIntegerRandomNumberTable(); field_4 = 0; m_bDynamicAcousticModelingStatus = true; for (int i = 0; i < NUM_AUDIOENTITIES; i++) { m_asAudioEntities[i].m_bIsUsed = false; m_anAudioEntityIndices[i] = NUM_AUDIOENTITIES; } m_nAudioEntitiesTotal = 0; m_FrameCounter = 0; m_bFifthFrameFlag = false; m_bTimerJustReset = false; m_nTimer = 0; } cAudioManager::~cAudioManager() { if (m_bIsInitialised) Terminate(); } void cAudioManager::Initialise() { if (!m_bIsInitialised) { PreInitialiseGameSpecificSetup(); m_bIsInitialised = SampleManager.Initialise(); if (m_bIsInitialised) { m_nActiveSamples = SampleManager.GetMaximumSupportedChannels(); if (m_nActiveSamples <= 1) { Terminate(); } else { --m_nActiveSamples; PostInitialiseGameSpecificSetup(); InitialisePoliceRadioZones(); InitialisePoliceRadio(); MusicManager.Initialise(); } } } } void cAudioManager::Terminate() { if (m_bIsInitialised) { MusicManager.Terminate(); for (uint32 i = 0; i < NUM_AUDIOENTITIES; i++) { m_asAudioEntities[i].m_bIsUsed = false; m_anAudioEntityIndices[i] = ARRAY_SIZE(m_anAudioEntityIndices); } m_nAudioEntitiesTotal = 0; m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal = 0; PreTerminateGameSpecificShutdown(); for (uint32 i = 0; i < MAX_SFX_BANKS; i++) { if (SampleManager.IsSampleBankLoaded(i)) SampleManager.UnloadSampleBank(i); } SampleManager.Terminate(); m_bIsInitialised = false; PostTerminateGameSpecificShutdown(); } } void cAudioManager::Service() { GenerateIntegerRandomNumberTable(); if (m_bTimerJustReset) { ResetAudioLogicTimers(m_nTimer); MusicManager.ResetTimers(m_nTimer); m_bTimerJustReset = false; } if (m_bIsInitialised) { m_nPreviousUserPause = m_nUserPause; m_nUserPause = CTimer::GetIsUserPaused(); UpdateReflections(); ServiceSoundEffects(); MusicManager.Service(); } } int32 cAudioManager::CreateEntity(eAudioType type, void *entity) { if (!m_bIsInitialised) return AEHANDLE_ERROR_NOAUDIOSYS; if (!entity) return AEHANDLE_ERROR_NOENTITY; if (type >= TOTAL_AUDIO_TYPES) return AEHANDLE_ERROR_BADAUDIOTYPE; for (uint32 i = 0; i < ARRAY_SIZE(m_asAudioEntities); i++) { if (!m_asAudioEntities[i].m_bIsUsed) { m_asAudioEntities[i].m_bIsUsed = true; m_asAudioEntities[i].m_bStatus = false; m_asAudioEntities[i].m_nType = type; m_asAudioEntities[i].m_pEntity = entity; m_asAudioEntities[i].m_awAudioEvent[0] = SOUND_NO_SOUND; m_asAudioEntities[i].m_awAudioEvent[1] = SOUND_NO_SOUND; m_asAudioEntities[i].m_awAudioEvent[2] = SOUND_NO_SOUND; m_asAudioEntities[i].m_awAudioEvent[3] = SOUND_NO_SOUND; m_asAudioEntities[i].m_AudioEvents = 0; m_anAudioEntityIndices[m_nAudioEntitiesTotal++] = i; return i; } } return AEHANDLE_ERROR_NOFREESLOT; } void cAudioManager::DestroyEntity(int32 id) { if (m_bIsInitialised && id >= 0 && id < NUM_AUDIOENTITIES && m_asAudioEntities[id].m_bIsUsed) { m_asAudioEntities[id].m_bIsUsed = false; for (int32 i = 0; i < m_nAudioEntitiesTotal; ++i) { if (id == m_anAudioEntityIndices[i]) { if (i < NUM_AUDIOENTITIES - 1) memmove(&m_anAudioEntityIndices[i], &m_anAudioEntityIndices[i + 1], NUM_AUDIOENTITY_EVENTS * (m_nAudioEntitiesTotal - (i + 1))); m_anAudioEntityIndices[--m_nAudioEntitiesTotal] = NUM_AUDIOENTITIES; return; } } } } void cAudioManager::SetEntityStatus(int32 id, uint8 status) { if (m_bIsInitialised && id >= 0 && id < NUM_AUDIOENTITIES && m_asAudioEntities[id].m_bIsUsed) m_asAudioEntities[id].m_bStatus = status; } void cAudioManager::PlayOneShot(int32 index, uint16 sound, float vol) { static const uint8 OneShotPriority[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 2, 5, 5, 3, 5, 2, 2, 1, 1, 3, 1, 3, 3, 1, 1, 1, 1, 4, 4, 4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 3, 4, 2, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 3, 1, 1, 1, 9, 0, 0, 0, 1, 2, 2, 0, 0, 2, 3, 3, 3, 5, 1, 1, 1, 1, 1, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 7, 1, 4, 3, 4, 2, 2, 2, 3, 1, 2, 1, 3, 5, 3, 4, 6, 4, 6, 3, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0, 3, 3, 1, 0 }; if (m_bIsInitialised) { if (index >= 0 && index < NUM_AUDIOENTITIES) { tAudioEntity &entity = m_asAudioEntities[index]; if (entity.m_bIsUsed) { if (sound < SOUND_TOTAL_SOUNDS) { if (entity.m_nType == AUDIOTYPE_SCRIPTOBJECT) { if (m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal < ARRAY_SIZE(m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices)) { entity.m_awAudioEvent[0] = sound; entity.m_AudioEvents = 1; m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices[m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal++] = index; } } else { int32 i = 0; while (true) { if (i >= entity.m_AudioEvents) { if (entity.m_AudioEvents < ARRAY_SIZE(entity.m_awAudioEvent)) { entity.m_awAudioEvent[i] = sound; entity.m_afVolume[i] = vol; ++entity.m_AudioEvents; } return; } if (OneShotPriority[entity.m_awAudioEvent[i]] > OneShotPriority[sound]) break; ++i; } if (i < NUM_AUDIOENTITY_EVENTS - 1) { memmove(&entity.m_awAudioEvent[i + 1], &entity.m_awAudioEvent[i], (NUM_AUDIOENTITY_EVENTS - 1 - i) * NUM_AUDIOENTITY_EVENTS / 2); memmove(&entity.m_afVolume[i + 1], &entity.m_afVolume[i], (NUM_AUDIOENTITY_EVENTS - 1 - i) * NUM_AUDIOENTITY_EVENTS); } entity.m_awAudioEvent[i] = sound; entity.m_afVolume[i] = vol; if (entity.m_AudioEvents < ARRAY_SIZE(entity.m_awAudioEvent)) ++entity.m_AudioEvents; } } } } } } void cAudioManager::SetMP3BoostVolume(uint8 volume) const { SampleManager.SetMP3BoostVolume(volume); } void cAudioManager::SetEffectsMasterVolume(uint8 volume) const { SampleManager.SetEffectsMasterVolume(volume); } void cAudioManager::SetMusicMasterVolume(uint8 volume) const { SampleManager.SetMusicMasterVolume(volume); } void cAudioManager::SetEffectsFadeVol(uint8 volume) const { SampleManager.SetEffectsFadeVolume(volume); } void cAudioManager::SetMonoMode(uint8 mono) { SampleManager.SetMonoMode(mono); } void cAudioManager::SetMusicFadeVol(uint8 volume) const { SampleManager.SetMusicFadeVolume(volume); } void cAudioManager::ResetTimers(uint32 time) { if (m_bIsInitialised) { m_bTimerJustReset = true; m_nTimer = time; ClearRequestedQueue(); if (m_nActiveSampleQueue) { m_nActiveSampleQueue = 0; ClearRequestedQueue(); m_nActiveSampleQueue = 1; } else { m_nActiveSampleQueue = 1; ClearRequestedQueue(); m_nActiveSampleQueue = 0; } ClearActiveSamples(); ClearMissionAudio(0); ClearMissionAudio(1); SampleManager.StopChannel(policeChannel); SampleManager.SetEffectsFadeVolume(0); SampleManager.SetMusicFadeVolume(0); MusicManager.ResetMusicAfterReload(); m_bIsPlayerShutUp = false; #ifdef AUDIO_OAL SampleManager.Service(); #endif } } void cAudioManager::DestroyAllGameCreatedEntities() { cAudioScriptObject *entity; if (m_bIsInitialised) { for (uint32 i = 0; i < ARRAY_SIZE(m_asAudioEntities); i++) { if (m_asAudioEntities[i].m_bIsUsed) { switch (m_asAudioEntities[i].m_nType) { case AUDIOTYPE_PHYSICAL: case AUDIOTYPE_EXPLOSION: case AUDIOTYPE_WEATHER: //case AUDIOTYPE_CRANE: case AUDIOTYPE_GARAGE: case AUDIOTYPE_FIREHYDRANT: DestroyEntity(i); break; case AUDIOTYPE_SCRIPTOBJECT: entity = (cAudioScriptObject *)m_asAudioEntities[i].m_pEntity; if (entity) { delete entity; m_asAudioEntities[i].m_pEntity = nil; } DestroyEntity(i); break; default: break; } } } m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal = 0; } } uint8 cAudioManager::GetNum3DProvidersAvailable() const { if (m_bIsInitialised) return SampleManager.GetNum3DProvidersAvailable(); return 0; } char * cAudioManager::Get3DProviderName(uint8 id) const { if (!m_bIsInitialised) return nil; #ifdef AUDIO_OAL id = clamp(id, 0, SampleManager.GetNum3DProvidersAvailable() - 1); #else // We don't want that either since it will crash the game, but skipping for now if (id >= SampleManager.GetNum3DProvidersAvailable()) return nil; #endif return SampleManager.Get3DProviderName(id); } int8 cAudioManager::GetCurrent3DProviderIndex() const { if (m_bIsInitialised) return SampleManager.GetCurrent3DProviderIndex(); return -1; } int8 cAudioManager::AutoDetect3DProviders() const { if (m_bIsInitialised) return SampleManager.AutoDetect3DProviders(); return -1; } int8 cAudioManager::SetCurrent3DProvider(uint8 which) { if (!m_bIsInitialised) return -1; for (uint8 i = 0; i < m_nActiveSamples + 1; ++i) SampleManager.StopChannel(i); ClearRequestedQueue(); if (m_nActiveSampleQueue == 0) m_nActiveSampleQueue = 1; else m_nActiveSampleQueue = 0; ClearRequestedQueue(); ClearActiveSamples(); int8 current = SampleManager.SetCurrent3DProvider(which); if (current > 0) { m_nActiveSamples = SampleManager.GetMaximumSupportedChannels(); if (m_nActiveSamples > 1) --m_nActiveSamples; } return current; } void cAudioManager::SetSpeakerConfig(int32 conf) const { SampleManager.SetSpeakerConfig(conf); } bool cAudioManager::IsMP3RadioChannelAvailable() const { if (m_bIsInitialised) return SampleManager.IsMP3RadioChannelAvailable(); return false; } void cAudioManager::ReleaseDigitalHandle() const { if (m_bIsInitialised) { SampleManager.ReleaseDigitalHandle(); } } void cAudioManager::ReacquireDigitalHandle() const { if (m_bIsInitialised) { SampleManager.ReacquireDigitalHandle(); } } void cAudioManager::SetDynamicAcousticModelingStatus(uint8 status) { m_bDynamicAcousticModelingStatus = status!=0; } bool cAudioManager::CheckForAnAudioFileOnCD() const { return SampleManager.CheckForAnAudioFileOnCD(); } uint8 cAudioManager::GetCDAudioDriveLetter() const { if(m_bIsInitialised) return SampleManager.GetCDAudioDriveLetter(); return 0; } bool cAudioManager::IsAudioInitialised() const { return m_bIsInitialised; } void cAudioManager::ServiceSoundEffects() { m_bFifthFrameFlag = (m_FrameCounter++ % 5) == 0; if (m_nUserPause && !m_nPreviousUserPause) { for (int32 i = 0; i < allChannels; i++) SampleManager.StopChannel(i); ClearRequestedQueue(); if (m_nActiveSampleQueue) { m_nActiveSampleQueue = 0; ClearRequestedQueue(); m_nActiveSampleQueue = 1; } else { m_nActiveSampleQueue = 1; ClearRequestedQueue(); m_nActiveSampleQueue = 0; } ClearActiveSamples(); } m_nActiveSampleQueue = m_nActiveSampleQueue == 1 ? 0 : 1; if(m_bReverb) ProcessReverb(); ProcessSpecial(); ClearRequestedQueue(); InterrogateAudioEntities(); m_sPedComments.Process(); ServicePoliceRadio(); ServiceCollisions(); AddReleasingSounds(); ProcessMissionAudio(); #ifdef GTA_PC AdjustSamplesVolume(); #endif ProcessActiveQueues(); #ifdef AUDIO_OAL SampleManager.Service(); #endif for (int32 i = 0; i < m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal; ++i) { cAudioScriptObject *object = (cAudioScriptObject *)m_asAudioEntities[m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices[i]].m_pEntity; delete object; m_asAudioEntities[m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices[i]].m_pEntity = nil; DestroyEntity(m_sAudioScriptObjectManager.m_anScriptObjectEntityIndices[i]); } m_sAudioScriptObjectManager.m_nScriptObjectEntityTotal = 0; } uint8 cAudioManager::ComputeVolume(uint8 emittingVolume, float soundIntensity, float distance) const { float newSoundIntensity; float newEmittingVolume; if (soundIntensity <= 0.0f) return 0; newSoundIntensity = soundIntensity / 5.0f; if (newSoundIntensity > distance) return emittingVolume; newEmittingVolume = emittingVolume * SQR((soundIntensity - newSoundIntensity - (distance - newSoundIntensity)) / (soundIntensity - newSoundIntensity)); return Min(127u, newEmittingVolume); } void cAudioManager::TranslateEntity(Const CVector *in, CVector *out) const { *out = MultiplyInverse(TheCamera.GetMatrix(), *in); } int32 cAudioManager::ComputePan(float dist, CVector *vec) { const uint8 PanTable[64] = { 0, 3, 8, 12, 16, 19, 22, 24, 26, 28, 30, 31, 33, 34, 36, 37, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 49, 50, 51, 52, 53, 53, 54, 55, 55, 56, 56, 57, 57, 58, 58, 58, 59, 59, 59, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63}; int32 index = Min(63, Abs(int32(vec->x / (dist / 64.f)))); if (vec->x > 0.f) return Max(20, 63 - PanTable[index]); return Min(107, PanTable[index] + 63); } uint32 cAudioManager::ComputeDopplerEffectedFrequency(uint32 oldFreq, float position1, float position2, float speedMultiplier) const { uint32 newFreq = oldFreq; if (!TheCamera.Get_Just_Switched_Status() && speedMultiplier != 0.0f) { float dist = position2 - position1; if (dist != 0.0f) { float speedOfSource = (dist / m_nTimeSpent) * speedMultiplier; if (m_fSpeedOfSound > Abs(speedOfSource)) { speedOfSource = clamp2(speedOfSource, 0.0f, 1.5f); newFreq = (oldFreq * m_fSpeedOfSound) / (speedOfSource + m_fSpeedOfSound); } } } return newFreq; } int32 cAudioManager::RandomDisplacement(uint32 seed) const { int32 value; static bool bPos = true; static uint32 Adjustment = 0; if (!seed) return 0; value = m_anRandomTable[(Adjustment + seed) % 5] % seed; Adjustment += value; if (value % 2) { bPos = !bPos; } if (!bPos) value = -value; return value; } void cAudioManager::InterrogateAudioEntities() { for (int32 i = 0; i < m_nAudioEntitiesTotal; i++) { ProcessEntity(m_anAudioEntityIndices[i]); m_asAudioEntities[m_anAudioEntityIndices[i]].m_AudioEvents = 0; } } void cAudioManager::AddSampleToRequestedQueue() { int32 calculatedVolume; uint8 sampleIndex; bool bReflections; if (m_sQueueSample.m_nSampleIndex < TOTAL_AUDIO_SAMPLES) { calculatedVolume = m_sQueueSample.m_nReleasingVolumeModificator * (MAX_VOLUME - m_sQueueSample.m_nVolume); sampleIndex = m_SampleRequestQueuesStatus[m_nActiveSampleQueue]; if (sampleIndex >= m_nActiveSamples) { sampleIndex = m_abSampleQueueIndexTable[m_nActiveSampleQueue][m_nActiveSamples - 1]; if (m_asSamples[m_nActiveSampleQueue][sampleIndex].m_nCalculatedVolume <= calculatedVolume) return; } else { ++m_SampleRequestQueuesStatus[m_nActiveSampleQueue]; } m_sQueueSample.m_nCalculatedVolume = calculatedVolume; m_sQueueSample.m_bLoopEnded = false; if (m_sQueueSample.m_bIs2D || CCullZones::InRoomForAudio()) { m_sQueueSample.m_bRequireReflection = false; m_sQueueSample.m_nLoopsRemaining = 0; } if (m_bDynamicAcousticModelingStatus && m_sQueueSample.m_nLoopCount) { bReflections = m_sQueueSample.m_bRequireReflection; } else { bReflections = false; m_sQueueSample.m_nLoopsRemaining = 0; } m_sQueueSample.m_bRequireReflection = false; if ( m_bReverb && m_sQueueSample.m_bIs2D ) m_sQueueSample.field_4C = 30; if (!m_bDynamicAcousticModelingStatus) m_sQueueSample.m_bReverbFlag = false; m_asSamples[m_nActiveSampleQueue][sampleIndex] = m_sQueueSample; AddDetailsToRequestedOrderList(sampleIndex); if (bReflections) AddReflectionsToRequestedQueue(); } } void cAudioManager::AddDetailsToRequestedOrderList(uint8 sample) { uint32 i = 0; if (sample != 0) { for (; i < sample; i++) { if (m_asSamples[m_nActiveSampleQueue][m_abSampleQueueIndexTable[m_nActiveSampleQueue][i]].m_nCalculatedVolume > m_asSamples[m_nActiveSampleQueue][sample].m_nCalculatedVolume) break; } if (i < sample) { memmove(&m_abSampleQueueIndexTable[m_nActiveSampleQueue][i + 1], &m_abSampleQueueIndexTable[m_nActiveSampleQueue][i], m_nActiveSamples - i - 1); } } m_abSampleQueueIndexTable[m_nActiveSampleQueue][i] = sample; } void cAudioManager::AddReflectionsToRequestedQueue() { #ifdef FIX_BUGS uint32 oldFreq = 0; #else uint32 oldFreq; #endif float reflectionDistance; int32 noise; uint8 emittingVolume; uint32 oldCounter = m_sQueueSample.m_nCounter; float oldDist = m_sQueueSample.m_fDistance; CVector oldPos = m_sQueueSample.m_vecPos; if ( CTimer::GetIsSlowMotionActive() ) { emittingVolume = m_sQueueSample.m_nVolume; oldFreq = m_sQueueSample.m_nFrequency; } else { emittingVolume = (9 * m_sQueueSample.m_nVolume) / 16; } m_sQueueSample.m_fSoundIntensity /= 2.f; int halfOldFreq = oldFreq >> 1; for (uint32 i = 0; i < ARRAY_SIZE(m_afReflectionsDistances); i++) { if ( CTimer::GetIsSlowMotionActive() ) m_afReflectionsDistances[i] = GetRandomNumberInRange(i % 4, 0, 2) * 100.f / 8.f; reflectionDistance = m_afReflectionsDistances[i]; if (reflectionDistance > 0.0f && reflectionDistance < 100.f && reflectionDistance < m_sQueueSample.m_fSoundIntensity) { m_sQueueSample.m_nLoopsRemaining = CTimer::GetIsSlowMotionActive() ? (reflectionDistance * 800.f / 1029.f) : (reflectionDistance * 500.f / 1029.f); if (m_sQueueSample.m_nLoopsRemaining > 3) { m_sQueueSample.m_fDistance = m_afReflectionsDistances[i]; m_sQueueSample.m_nEmittingVolume = emittingVolume; m_sQueueSample.m_nVolume = ComputeVolume(emittingVolume, m_sQueueSample.m_fSoundIntensity, m_sQueueSample.m_fDistance); if (m_sQueueSample.m_nVolume > emittingVolume / 16) { m_sQueueSample.m_nCounter = oldCounter + (i + 1) * 256; if (m_sQueueSample.m_nLoopCount) { if ( CTimer::GetIsSlowMotionActive() ) { m_sQueueSample.m_nFrequency = halfOldFreq + ((halfOldFreq * i) / ARRAY_SIZE(m_afReflectionsDistances)); } else { noise = RandomDisplacement(m_sQueueSample.m_nFrequency / 32); if (noise <= 0) m_sQueueSample.m_nFrequency += noise; else m_sQueueSample.m_nFrequency -= noise; } } m_sQueueSample.m_nReleasingVolumeModificator += 20; m_sQueueSample.m_vecPos = m_avecReflectionsPos[i]; AddSampleToRequestedQueue(); } } } } m_sQueueSample.m_vecPos = oldPos; m_sQueueSample.m_fDistance = oldDist; } void cAudioManager::UpdateReflections() { CVector camPos = TheCamera.GetPosition(); CColPoint colpoint; CEntity *ent; if (m_FrameCounter % 8 == 0) { m_avecReflectionsPos[0] = camPos; m_avecReflectionsPos[0].y += 100.f; if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[0], colpoint, ent, true, false, false, true, false, true, true)) m_afReflectionsDistances[0] = Distance(camPos, colpoint.point); else m_afReflectionsDistances[0] = 100.0f; } else if ((m_FrameCounter + 1) % 8 == 0) { m_avecReflectionsPos[1] = camPos; m_avecReflectionsPos[1].y -= 100.0f; if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[1], colpoint, ent, true, false, false, true, false, true, true)) m_afReflectionsDistances[1] = Distance(camPos, colpoint.point); else m_afReflectionsDistances[1] = 100.0f; } else if ((m_FrameCounter + 2) % 8 == 0) { m_avecReflectionsPos[2] = camPos; m_avecReflectionsPos[2].x -= 100.0f; if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[2], colpoint, ent, true, false, false, true, false, true, true)) m_afReflectionsDistances[2] = Distance(camPos, colpoint.point); else m_afReflectionsDistances[2] = 100.0f; } else if ((m_FrameCounter + 3) % 8 == 0) { m_avecReflectionsPos[3] = camPos; m_avecReflectionsPos[3].x += 100.0f; if (CWorld::ProcessLineOfSight(camPos, m_avecReflectionsPos[3], colpoint, ent, true, false, false, true, false, true, true)) m_afReflectionsDistances[3] = Distance(camPos, colpoint.point); else m_afReflectionsDistances[3] = 100.0f; } else if ((m_FrameCounter + 4) % 8 == 0) { camPos.y += 1.0f; m_avecReflectionsPos[4] = camPos; m_avecReflectionsPos[4].z += 100.0f; if (CWorld::ProcessVerticalLine(camPos, m_avecReflectionsPos[4].z, colpoint, ent, true, false, false, false, true, false, nil)) m_afReflectionsDistances[4] = colpoint.point.z - camPos.z; else m_afReflectionsDistances[4] = 100.0f; } else if ((m_FrameCounter + 5) % 8 == 0) { camPos.y -= 1.0f; m_avecReflectionsPos[5] = camPos; m_avecReflectionsPos[5].z += 100.0f; if (CWorld::ProcessVerticalLine(camPos, m_avecReflectionsPos[5].z, colpoint, ent, true, false, false, false, true, false, nil)) m_afReflectionsDistances[5] = colpoint.point.z - camPos.z; else m_afReflectionsDistances[5] = 100.0f; } else if ((m_FrameCounter + 6) % 8 == 0) { camPos.x -= 1.0f; m_avecReflectionsPos[6] = camPos; m_avecReflectionsPos[6].z += 100.0f; if (CWorld::ProcessVerticalLine(camPos, m_avecReflectionsPos[6].z, colpoint, ent, true, false, false, false, true, false, nil)) m_afReflectionsDistances[6] = colpoint.point.z - camPos.z; else m_afReflectionsDistances[6] = 100.0f; } else if ((m_FrameCounter + 7) % 8 == 0) { camPos.x += 1.0f; m_avecReflectionsPos[7] = camPos; m_avecReflectionsPos[7].z += 100.0f; if (CWorld::ProcessVerticalLine(camPos, m_avecReflectionsPos[7].z, colpoint, ent, true, false, false, false, true, false, nil)) m_afReflectionsDistances[7] = colpoint.point.z - camPos.z; else m_afReflectionsDistances[7] = 100.0f; } } void cAudioManager::AddReleasingSounds() { bool toProcess[44]; // why not 27? int8 queue = m_nActiveSampleQueue == 0 ? 1 : 0; for (int32 i = 0; i < m_SampleRequestQueuesStatus[queue]; i++) { tSound &sample = m_asSamples[queue][m_abSampleQueueIndexTable[queue][i]]; if (sample.m_bLoopEnded) continue; toProcess[i] = false; for (int32 j = 0; j < m_SampleRequestQueuesStatus[m_nActiveSampleQueue]; j++) { if (sample.m_nEntityIndex == m_asSamples[m_nActiveSampleQueue][m_abSampleQueueIndexTable[m_nActiveSampleQueue][j]].m_nEntityIndex && sample.m_nCounter == m_asSamples[m_nActiveSampleQueue][m_abSampleQueueIndexTable[m_nActiveSampleQueue][j]].m_nCounter) { toProcess[i] = true; break; } } if (!toProcess[i]) { if (sample.m_nCounter <= 255 || !sample.m_nLoopsRemaining) { if (!sample.m_nReleasingVolumeDivider) continue; if (!sample.m_nLoopCount) { if (sample.m_nVolumeChange == -1) { sample.m_nVolumeChange = sample.m_nVolume / sample.m_nReleasingVolumeDivider; if (sample.m_nVolumeChange <= 0) sample.m_nVolumeChange = 1; } if (sample.m_nVolume <= sample.m_nVolumeChange) { sample.m_nReleasingVolumeDivider = 0; continue; } sample.m_nVolume -= sample.m_nVolumeChange; } --sample.m_nReleasingVolumeDivider; if (m_bFifthFrameFlag) { if (sample.m_nReleasingVolumeModificator < 20) ++sample.m_nReleasingVolumeModificator; } sample.m_bReleasingSoundFlag = false; } memcpy(&m_sQueueSample, &sample, sizeof(tSound)); AddSampleToRequestedQueue(); } } } void cAudioManager::ProcessActiveQueues() { CVector position; uint32 freqDivided; uint32 loopCount; uint8 emittingVol; uint8 vol; uint8 offset; float x; bool flag; bool missionState; for (int32 i = 0; i < m_nActiveSamples; i++) { m_asSamples[m_nActiveSampleQueue][i].m_bIsProcessed = false; m_asActiveSamples[i].m_bIsProcessed = false; } for (int32 i = 0; i < m_SampleRequestQueuesStatus[m_nActiveSampleQueue]; i++) { tSound& sample = m_asSamples[m_nActiveSampleQueue][m_abSampleQueueIndexTable[m_nActiveSampleQueue][i]]; if (sample.m_nSampleIndex != NO_SAMPLE) { for (int32 j = 0; j < m_nActiveSamples; j++) { if (sample.m_nEntityIndex == m_asActiveSamples[j].m_nEntityIndex && sample.m_nCounter == m_asActiveSamples[j].m_nCounter && sample.m_nSampleIndex == m_asActiveSamples[j].m_nSampleIndex) { if (sample.m_nLoopCount) { if (m_FrameCounter & 1) { if (!(j & 1)) { flag = false; } else { flag = true; } } else if (j & 1) { flag = false; } else { flag = true; } if (flag && !SampleManager.GetChannelUsedFlag(j)) { sample.m_bLoopEnded = true; m_asActiveSamples[j].m_bLoopEnded = true; m_asActiveSamples[j].m_nSampleIndex = NO_SAMPLE; m_asActiveSamples[j].m_nEntityIndex = AEHANDLE_NONE; continue; } if (!sample.m_nReleasingVolumeDivider) sample.m_nReleasingVolumeDivider = 1; } sample.m_bIsProcessed = true; m_asActiveSamples[j].m_bIsProcessed = true; sample.m_nVolumeChange = -1; if (!sample.m_bReleasingSoundFlag) { if (sample.m_bIs2D) { if (field_4) { emittingVol = 2 * Min(63, sample.m_nEmittingVolume); } else { emittingVol = sample.m_nEmittingVolume; } SampleManager.SetChannelFrequency(j, sample.m_nFrequency); SampleManager.SetChannelEmittingVolume(j, emittingVol); } else { m_asActiveSamples[j].m_fDistance = sample.m_fDistance; sample.m_nFrequency = ComputeDopplerEffectedFrequency( sample.m_nFrequency, m_asActiveSamples[j].m_fDistance, sample.m_fDistance, sample.m_fSpeedMultiplier); if (sample.m_nFrequency != m_asActiveSamples[j].m_nFrequency) { m_asActiveSamples[j].m_nFrequency = clamp2((int32)sample.m_nFrequency, (int32)m_asActiveSamples[j].m_nFrequency, 6000); SampleManager.SetChannelFrequency(j, m_asActiveSamples[j].m_nFrequency); } if (sample.m_nEmittingVolume != m_asActiveSamples[j].m_nEmittingVolume) { vol = clamp2((int8)sample.m_nEmittingVolume, (int8)m_asActiveSamples[j].m_nEmittingVolume, 10); if (field_4) { emittingVol = 2 * Min(63, vol); } else { emittingVol = vol; } missionState = false; for (int32 k = 0; k < ARRAY_SIZE(m_sMissionAudio.m_bIsMobile); k++) { if (m_sMissionAudio.m_bIsMobile[k]) { missionState = true; break; } } if (missionState) { emittingVol = (emittingVol * field_5538) / 127; } else { if (field_5538 < 127) emittingVol = (emittingVol * field_5538) / 127; } SampleManager.SetChannelEmittingVolume(j, emittingVol); m_asActiveSamples[j].m_nEmittingVolume = vol; } TranslateEntity(&sample.m_vecPos, &position); SampleManager.SetChannel3DPosition(j, position.x, position.y, position.z); SampleManager.SetChannel3DDistances(j, sample.m_fSoundIntensity, 0.25f * sample.m_fSoundIntensity); } SampleManager.SetChannelReverbFlag(j, sample.m_bReverbFlag); break; //continue for i } sample.m_bIsProcessed = false; m_asActiveSamples[j].m_bIsProcessed = false; //continue for j } } } } for (int32 i = 0; i < m_nActiveSamples; i++) { if (m_asActiveSamples[i].m_nSampleIndex != NO_SAMPLE && !m_asActiveSamples[i].m_bIsProcessed) { SampleManager.StopChannel(i); m_asActiveSamples[i].m_nSampleIndex = NO_SAMPLE; m_asActiveSamples[i].m_nEntityIndex = AEHANDLE_NONE; } } for (uint8 i = 0; i < m_SampleRequestQueuesStatus[m_nActiveSampleQueue]; i++) { tSound& sample = m_asSamples[m_nActiveSampleQueue][m_abSampleQueueIndexTable[m_nActiveSampleQueue][i]]; if (!sample.m_bIsProcessed && !sample.m_bLoopEnded && m_asAudioEntities[sample.m_nEntityIndex].m_bIsUsed && sample.m_nSampleIndex < NO_SAMPLE) { if (sample.m_nCounter > 255 && sample.m_nLoopCount && sample.m_nLoopsRemaining) { sample.m_nLoopsRemaining--; sample.m_nReleasingVolumeDivider = 1; } else { for (uint8 j = 0; j < m_nActiveSamples; j++) { uint8 k = (j + field_6) % m_nActiveSamples; if (!m_asActiveSamples[k].m_bIsProcessed) { if (sample.m_nLoopCount != 0) { freqDivided = sample.m_nFrequency / m_nTimeSpent; loopCount = sample.m_nLoopCount * SampleManager.GetSampleLength(sample.m_nSampleIndex); if (freqDivided == 0) continue; sample.m_nReleasingVolumeDivider = loopCount / freqDivided + 1; } memcpy(&m_asActiveSamples[k], &sample, sizeof(tSound)); if (!m_asActiveSamples[k].m_bIs2D) TranslateEntity(&m_asActiveSamples[k].m_vecPos, &position); if (field_4) { emittingVol = 2 * Min(63, m_asActiveSamples[k].m_nEmittingVolume); } else { emittingVol = m_asActiveSamples[k].m_nEmittingVolume; } if (SampleManager.InitialiseChannel(k, m_asActiveSamples[k].m_nSampleIndex, m_asActiveSamples[k].m_nBankIndex)) { SampleManager.SetChannelFrequency(k, m_asActiveSamples[k].m_nFrequency); bool isMobile = false; for (int32 l = 0; l < ARRAY_SIZE(m_sMissionAudio.m_bIsMobile); l++) { if (m_sMissionAudio.m_bIsMobile[l]) { isMobile = true; break; } } if (!isMobile || m_asActiveSamples[k].m_bIs2D) { if (field_5538 < 127) emittingVol *= field_5538 / 127; vol = emittingVol; } else { vol = (emittingVol * field_5538 / 127); } SampleManager.SetChannelEmittingVolume(k, vol); SampleManager.SetChannelLoopPoints(k, m_asActiveSamples[k].m_nLoopStart, m_asActiveSamples[k].m_nLoopEnd); SampleManager.SetChannelLoopCount(k, m_asActiveSamples[k].m_nLoopCount); SampleManager.SetChannelReverbFlag(k, m_asActiveSamples[k].m_bReverbFlag); if (m_asActiveSamples[k].m_bIs2D) { offset = m_asActiveSamples[k].m_nOffset; if (offset == 63) { x = 0.0f; } else if (offset >= 63) { x = (offset - 63) * 1000.0f / 63; } else { x = -(63 - offset) * 1000.0f / 63; //same like line below } position = CVector(x, 0.0f, 0.0f); m_asActiveSamples[k].m_fSoundIntensity = 100000.0f; } SampleManager.SetChannel3DPosition(k, position.x, position.y, position.z); SampleManager.SetChannel3DDistances(k, m_asActiveSamples[k].m_fSoundIntensity, 0.25f * m_asActiveSamples[k].m_fSoundIntensity); SampleManager.StartChannel(k); } m_asActiveSamples[k].m_bIsProcessed = true; sample.m_bIsProcessed = true; sample.m_nVolumeChange = -1; break; } } } } } field_6 %= m_nActiveSamples; } void cAudioManager::ClearRequestedQueue() { for (int32 i = 0; i < m_nActiveSamples; i++) { m_abSampleQueueIndexTable[m_nActiveSampleQueue][i] = m_nActiveSamples; } m_SampleRequestQueuesStatus[m_nActiveSampleQueue] = 0; } void cAudioManager::ClearActiveSamples() { for (uint8 i = 0; i < m_nActiveSamples; i++) { m_asActiveSamples[i].m_nEntityIndex = AEHANDLE_NONE; m_asActiveSamples[i].m_nCounter = 0; m_asActiveSamples[i].m_nSampleIndex = NO_SAMPLE; m_asActiveSamples[i].m_nBankIndex = INVALID_SFX_BANK; m_asActiveSamples[i].m_bIs2D = false; m_asActiveSamples[i].m_nReleasingVolumeModificator = 5; m_asActiveSamples[i].m_nFrequency = 0; m_asActiveSamples[i].m_nVolume = 0; m_asActiveSamples[i].m_nEmittingVolume = 0; m_asActiveSamples[i].m_fDistance = 0.0f; m_asActiveSamples[i].m_bIsProcessed = false; m_asActiveSamples[i].m_bLoopEnded = false; m_asActiveSamples[i].m_nLoopCount = 1; m_asActiveSamples[i].m_nLoopStart = 0; m_asActiveSamples[i].m_nLoopEnd = -1; m_asActiveSamples[i].m_fSpeedMultiplier = 0.0f; m_asActiveSamples[i].m_fSoundIntensity = 200.0f; m_asActiveSamples[i].m_nOffset = 63; m_asActiveSamples[i].m_bReleasingSoundFlag = false; m_asActiveSamples[i].m_nCalculatedVolume = 0; m_asActiveSamples[i].m_nReleasingVolumeDivider = 0; m_asActiveSamples[i].m_nVolumeChange = -1; m_asActiveSamples[i].m_vecPos = CVector(0.0f, 0.0f, 0.0f); m_asActiveSamples[i].m_bReverbFlag = false; m_asActiveSamples[i].m_nLoopsRemaining = 0; m_asActiveSamples[i].m_bRequireReflection = false; } } void cAudioManager::GenerateIntegerRandomNumberTable() { for (int32 i = 0; i < ARRAY_SIZE(m_anRandomTable); i++) { m_anRandomTable[i] = myrand(); } } #ifdef GTA_PC void cAudioManager::AdjustSamplesVolume() { for (int i = 0; i < m_SampleRequestQueuesStatus[m_nActiveSampleQueue]; i++) { tSound *pSample = &m_asSamples[m_nActiveSampleQueue][m_abSampleQueueIndexTable[m_nActiveSampleQueue][i] + 1]; if (!pSample->m_bIs2D) pSample->m_nEmittingVolume = ComputeEmittingVolume(pSample->m_nEmittingVolume, pSample->m_fSoundIntensity, pSample->m_fDistance); } } uint8 cAudioManager::ComputeEmittingVolume(uint8 emittingVolume, float intensity, float dist) { float quatIntensity = intensity / 4.0f; float diffIntensity = intensity - quatIntensity; if (dist > diffIntensity) return (quatIntensity - (dist - diffIntensity)) * (float)emittingVolume / quatIntensity; return emittingVolume; } #endif ================================================ FILE: src/audio/AudioManager.h ================================================ #pragma once #include "audio_enums.h" #include "AudioCollision.h" #include "PoliceRadio.h" #include "VehicleModelInfo.h" #include "Vehicle.h" class tSound { public: int32 m_nEntityIndex; int32 m_nCounter; int32 m_nSampleIndex; uint8 m_nBankIndex; bool m_bIs2D; int32 m_nReleasingVolumeModificator; uint32 m_nFrequency; uint8 m_nVolume; float m_fDistance; int32 m_nLoopCount; int32 m_nLoopStart; int32 m_nLoopEnd; uint8 m_nEmittingVolume; float m_fSpeedMultiplier; float m_fSoundIntensity; bool m_bReleasingSoundFlag; CVector m_vecPos; bool m_bReverbFlag; uint8 m_nLoopsRemaining; bool m_bRequireReflection; // Used for oneshots uint8 m_nOffset; uint8 field_4C; int32 m_nReleasingVolumeDivider; bool m_bIsProcessed; bool m_bLoopEnded; int32 m_nCalculatedVolume; int8 m_nVolumeChange; }; VALIDATE_SIZE(tSound, 96); class CPhysical; class CAutomobile; class tAudioEntity { public: eAudioType m_nType; void *m_pEntity; bool m_bIsUsed; uint8 m_bStatus; int16 m_awAudioEvent[NUM_AUDIOENTITY_EVENTS]; float m_afVolume[NUM_AUDIOENTITY_EVENTS]; uint8 m_AudioEvents; }; VALIDATE_SIZE(tAudioEntity, 40); class tPedComment { public: uint32 m_nSampleIndex; int32 m_nEntityIndex; CVector m_vecPos; float m_fDistance; uint8 m_bVolume; int8 m_nProcess; }; VALIDATE_SIZE(tPedComment, 28); class cPedComments { public: tPedComment m_asPedComments[NUM_PED_COMMENTS_BANKS][NUM_PED_COMMENTS_SLOTS]; uint8 m_nIndexMap[NUM_PED_COMMENTS_BANKS][NUM_PED_COMMENTS_SLOTS]; uint8 m_nCommentsInBank[NUM_PED_COMMENTS_BANKS]; uint8 m_nActiveBank; #ifdef GTA_PC bool m_bDelay; uint32 m_nDelayTimer; #endif cPedComments() { for (int i = 0; i < NUM_PED_COMMENTS_SLOTS; i++) for (int j = 0; j < NUM_PED_COMMENTS_BANKS; j++) { m_asPedComments[j][i].m_nProcess = -1; m_nIndexMap[j][i] = NUM_PED_COMMENTS_SLOTS; } for (int i = 0; i < NUM_PED_COMMENTS_BANKS; i++) m_nCommentsInBank[i] = 0; m_nActiveBank = 0; } void Add(tPedComment *com); // done void Process(); // done }; VALIDATE_SIZE(cPedComments, 0x490); class CEntity; #define MISSION_AUDIO_SLOTS (2) // So instead of doing cMissionAudio [2] they've added [2] to every field of the struct... // Only someone with a VERY EXTRAORDINARY mind could have come up with that class cMissionAudio { public: CVector m_vecPos[MISSION_AUDIO_SLOTS]; bool m_bPredefinedProperties[MISSION_AUDIO_SLOTS]; int32 m_nSampleIndex[MISSION_AUDIO_SLOTS]; uint8 m_nLoadingStatus[MISSION_AUDIO_SLOTS]; uint8 m_nPlayStatus[MISSION_AUDIO_SLOTS]; bool m_bIsPlaying[MISSION_AUDIO_SLOTS]; int32 m_nMissionAudioCounter[MISSION_AUDIO_SLOTS]; bool m_bIsPlayed[MISSION_AUDIO_SLOTS]; bool m_bIsMobile[MISSION_AUDIO_SLOTS]; }; VALIDATE_SIZE(cMissionAudio, 0x38); // name made up class cAudioScriptObjectManager { public: int32 m_anScriptObjectEntityIndices[NUM_SCRIPT_MAX_ENTITIES]; int32 m_nScriptObjectEntityTotal; cAudioScriptObjectManager() { m_nScriptObjectEntityTotal = 0; } ~cAudioScriptObjectManager() { m_nScriptObjectEntityTotal = 0; } }; class cTransmission; class CPlane; class CVehicle; class CPed; class cPedParams { public: bool m_bDistanceCalculated; float m_fDistance; CPed *m_pPed; cPedParams() { m_bDistanceCalculated = false; m_fDistance = 0.0f; m_pPed = nil; } }; class cVehicleParams { public: int32 m_VehicleType; bool m_bDistanceCalculated; float m_fDistance; CVehicle *m_pVehicle; cTransmission *m_pTransmission; int32 m_nIndex; float m_fVelocityChange; cVehicleParams() { m_VehicleType = -1; m_bDistanceCalculated = false; m_fDistance = 0.0f; m_pVehicle = nil; m_pTransmission = nil; m_nIndex = 0; m_fVelocityChange = 0.0f; } }; VALIDATE_SIZE(cVehicleParams, 0x1C); enum { REFLECTION_NORTH = 0, REFLECTION_SOUTH, REFLECTION_WEST, REFLECTION_EAST, REFLECTION_CEIL_NORTH, REFLECTION_CEIL_SOUTH, REFLECTION_CEIL_WEST, REFLECTION_CEIL_EAST, MAX_REFLECTIONS, }; class cAudioManager { public: bool m_bIsInitialised; uint8 m_bReverb; // unused bool m_bFifthFrameFlag; uint8 m_nActiveSamples; uint8 field_4; // unused bool m_bDynamicAcousticModelingStatus; int8 field_6; float m_fSpeedOfSound; bool m_bTimerJustReset; int32 m_nTimer; tSound m_sQueueSample; uint8 m_nActiveSampleQueue; tSound m_asSamples[NUM_SOUNDS_SAMPLES_BANKS][NUM_SOUNDS_SAMPLES_SLOTS]; uint8 m_abSampleQueueIndexTable[NUM_SOUNDS_SAMPLES_BANKS][NUM_SOUNDS_SAMPLES_SLOTS]; uint8 m_SampleRequestQueuesStatus[NUM_SOUNDS_SAMPLES_BANKS]; tSound m_asActiveSamples[NUM_SOUNDS_SAMPLES_SLOTS]; tAudioEntity m_asAudioEntities[NUM_AUDIOENTITIES]; int32 m_anAudioEntityIndices[NUM_AUDIOENTITIES]; int32 m_nAudioEntitiesTotal; CVector m_avecReflectionsPos[NUM_AUDIO_REFLECTIONS]; float m_afReflectionsDistances[NUM_AUDIO_REFLECTIONS]; cAudioScriptObjectManager m_sAudioScriptObjectManager; // miami uint8 m_bIsPlayerShutUp; uint8 m_nPlayerMood; uint32 m_nPlayerMoodTimer; uint8 field_rest[4]; bool m_bGenericSfx; cPedComments m_sPedComments; int32 m_nFireAudioEntity; int32 m_nWaterCannonEntity; int32 m_nPoliceChannelEntity; cPoliceRadioQueue m_sPoliceRadioQueue; int32 m_nFrontEndEntity; int32 m_nCollisionEntity; cAudioCollisionManager m_sCollisionManager; int32 m_nProjectileEntity; #ifdef GTA_BRIDGE int32 m_nBridgeEntity; #endif int32 m_nEscalatorEntity; int32 m_nExtraSoundsEntity; cMissionAudio m_sMissionAudio; uint8 field_5538; // something related to phone dialogues int32 m_anRandomTable[5]; uint8 m_nTimeSpent; uint8 m_nUserPause; uint8 m_nPreviousUserPause; uint32 m_FrameCounter; cAudioManager(); ~cAudioManager(); // getters uint32 GetFrameCounter() const { return m_FrameCounter; } // done float GetReflectionsDistance(int32 idx) const { return m_afReflectionsDistances[idx]; } // done int32 GetRandomNumber(int32 idx) const { return m_anRandomTable[idx]; } int32 GetRandomNumberInRange(int32 idx, int32 low, int32 high) const { return (m_anRandomTable[idx] % (high - low + 1)) + low; } bool IsMissionAudioSamplePlaying(uint8 slot) const; // { return m_sMissionAudio.m_nPlayStatus == 1; } bool ShouldDuckMissionAudio(uint8 slot) const; // "Should" be in alphabetic order, except "getXTalkSfx" void AddDetailsToRequestedOrderList(uint8 sample); // done (inlined in vc) void AddPlayerCarSample(uint8 emittingVolume, int32 freq, uint32 sample, uint8 bank, uint8 counter, bool notLooping); // done void AddReflectionsToRequestedQueue(); // done void AddReleasingSounds(); // done void AddSampleToRequestedQueue(); // done void AgeCrimes(); // done (inlined in vc) void CalculateDistance(bool &condition, float dist); // done bool CheckForAnAudioFileOnCD() const; // done void ClearActiveSamples(); // done void ClearMissionAudio(uint8 slot); // done (inlined in vc) void ClearRequestedQueue(); // done (inlined in vc) uint32 ComputeDopplerEffectedFrequency(uint32 oldFreq, float position1, float position2, float speedMultiplier) const; // done int32 ComputePan(float, CVector *); // done uint8 ComputeVolume(uint8 emittingVolume, float soundIntensity, float distance) const; // done int32 CreateEntity(eAudioType type, void *entity); // done void DestroyAllGameCreatedEntities(); // done void DestroyEntity(int32 id); // done (inlined in vc) void DoPoliceRadioCrackle(); // done // functions returning talk sfx, // order from GetPedCommentSfx uint32 GetPlayerTalkSfx(CPed *ped, int16 sound); uint32 GetCopTalkSfx(CPed *ped, int16 sound); uint32 GetSwatTalkSfx(CPed *ped, int16 sound); uint32 GetFBITalkSfx(CPed *ped, int16 sound); uint32 GetArmyTalkSfx(CPed *ped, int16 sound); uint32 GetMedicTalkSfx(CPed *ped, int16 sound); uint32 GetFiremanTalkSfx(CPed *ped, int16 sound); uint32 GetDefaultTalkSfx(CPed *ped, int16 sound); uint32 GetHFYSTTalkSfx(CPed *ped, int16 sound); uint32 GetHFOSTTalkSfx(CPed *ped, int16 sound); uint32 GetHMYSTTalkSfx(CPed *ped, int16 sound); uint32 GetHMOSTTalkSfx(CPed *ped, int16 sound); uint32 GetHFYRITalkSfx(CPed *ped, int16 sound); uint32 GetHFORITalkSfx(CPed *ped, int16 sound); uint32 GetHMYRITalkSfx(CPed *ped, int16 sound); uint32 GetHMORITalkSfx(CPed *ped, int16 sound); uint32 GetHFYBETalkSfx(CPed *ped, int16 sound); uint32 GetHFOBETalkSfx(CPed *ped, int16 sound); uint32 GetHMYBETalkSfx(CPed *ped, int16 sound); uint32 GetHMOBETalkSfx(CPed *ped, int16 sound); uint32 GetHFYBUTalkSfx(CPed *ped, int16 sound); uint32 GetHFYMDTalkSfx(CPed *ped, int16 sound); uint32 GetHFYCGTalkSfx(CPed *ped, int16 sound); uint32 GetHFYPRTalkSfx(CPed *ped, int16 sound); uint32 GetHFOTRTalkSfx(CPed *ped, int16 sound); uint32 GetHMOTRTalkSfx(CPed *ped, int16 sound); uint32 GetHMYAPTalkSfx(CPed *ped, int16 sound); uint32 GetHMOCATalkSfx(CPed *ped, int16 sound); uint32 GetBMODKTalkSfx(CPed *ped, int16 sound); uint32 GetBMYCRTalkSfx(CPed *ped, int16 sound); uint32 GetBFYSTTalkSfx(CPed *ped, int16 sound); uint32 GetBFOSTTalkSfx(CPed *ped, int16 sound); uint32 GetBMYSTTalkSfx(CPed *ped, int16 sound); uint32 GetBMOSTTalkSfx(CPed *ped, int16 sound); uint32 GetBFYRITalkSfx(CPed *ped, int16 sound); uint32 GetBFORITalkSfx(CPed *ped, int16 sound); uint32 GetBMYRITalkSfx(CPed *ped, int16 sound); uint32 GetBFYBETalkSfx(CPed *ped, int16 sound); uint32 GetBMYBETalkSfx(CPed *ped, int16 sound); uint32 GetBFOBETalkSfx(CPed *ped, int16 sound); uint32 GetBMOBETalkSfx(CPed *ped, int16 sound); uint32 GetBMYBUTalkSfx(CPed *ped, int16 sound); uint32 GetBFYPRTalkSfx(CPed *ped, int16 sound); uint32 GetBFOTRTalkSfx(CPed *ped, int16 sound); uint32 GetBMOTRTalkSfx(CPed *ped, int16 sound); uint32 GetBMYPITalkSfx(CPed *ped, int16 sound); uint32 GetBMYBBTalkSfx(CPed *ped, int16 sound); uint32 GetWMYCRTalkSfx(CPed *ped, int16 sound); uint32 GetWFYSTTalkSfx(CPed *ped, int16 sound); uint32 GetWFOSTTalkSfx(CPed *ped, int16 sound); uint32 GetWMYSTTalkSfx(CPed *ped, int16 sound); uint32 GetWMOSTTalkSfx(CPed *ped, int16 sound); uint32 GetWFYRITalkSfx(CPed *ped, int16 sound); uint32 GetWFORITalkSfx(CPed *ped, int16 sound); uint32 GetWMYRITalkSfx(CPed *ped, int16 sound); uint32 GetWMORITalkSfx(CPed *ped, int16 sound); uint32 GetWFYBETalkSfx(CPed *ped, int16 sound); uint32 GetWMYBETalkSfx(CPed *ped, int16 sound); uint32 GetWFOBETalkSfx(CPed *ped, int16 sound); uint32 GetWMOBETalkSfx(CPed *ped, int16 sound); uint32 GetWMYCWTalkSfx(CPed *ped, int16 sound); uint32 GetWMYGOTalkSfx(CPed *ped, int16 sound); uint32 GetWFOGOTalkSfx(CPed *ped, int16 sound); uint32 GetWMOGOTalkSfx(CPed *ped, int16 sound); uint32 GetWFYLGTalkSfx(CPed *ped, int16 sound); uint32 GetWMYLGTalkSfx(CPed *ped, int16 sound); uint32 GetWFYBUTalkSfx(CPed *ped, int16 sound); uint32 GetWMYBUTalkSfx(CPed *ped, int16 sound); uint32 GetWMOBUTalkSfx(CPed *ped, int16 sound); uint32 GetWFYPRTalkSfx(CPed *ped, int16 sound); uint32 GetWFOTRTalkSfx(CPed *ped, int16 sound); uint32 GetWMOTRTalkSfx(CPed *ped, int16 sound); uint32 GetWMYPITalkSfx(CPed *ped, int16 sound); uint32 GetWMOCATalkSfx(CPed *ped, int16 sound); uint32 GetWFYJGTalkSfx(CPed *ped, int16 sound); uint32 GetWMYJGTalkSfx(CPed *ped, int16 sound); uint32 GetWFYSKTalkSfx(CPed *ped, int16 sound); uint32 GetWMYSKTalkSfx(CPed *ped, int16 sound); uint32 GetWFYSHTalkSfx(CPed *ped, int16 sound); uint32 GetWFOSHTalkSfx(CPed *ped, int16 sound); uint32 GetJFOTOTalkSfx(CPed *ped, int16 sound); uint32 GetJMOTOTalkSfx(CPed *ped, int16 sound); uint32 GetCBTalkSfx(CPed *ped, int16 sound); uint32 GetHNTalkSfx(CPed *ped, int16 sound); uint32 GetSGTalkSfx(CPed *ped, int16 sound); uint32 GetCLTalkSfx(CPed *ped, int16 sound); uint32 GetGDTalkSfx(CPed *ped, int16 sound); uint32 GetBKTalkSfx(CPed *ped, int16 sound); uint32 GetPGTalkSfx(CPed *ped, int16 sound); uint32 GetVICETalkSfx(CPed *ped, int16 sound, int16 model); uint32 GetWFYG1TalkSfx(CPed *ped, int16 sound); uint32 GetWFYG2TalkSfx(CPed *ped, int16 sound); uint32 GetGenericMaleTalkSfx(CPed *ped, int16 sound); // todo names (inlined in vc) uint32 GetGenericFemaleTalkSfx(CPed *ped, int16 sound); // todo names (inlined in vc) // end of functions returning talk sfx void GenerateIntegerRandomNumberTable(); // done char *Get3DProviderName(uint8 id) const; // done uint8 GetCDAudioDriveLetter() const; // done int8 GetCurrent3DProviderIndex() const; // done int8 AutoDetect3DProviders() const; // done float GetCollisionLoopingRatio(uint32 a, uint32 b, float c) const; // not used float GetCollisionOneShotRatio(int32 a, float b) const; // done float GetCollisionRatio(float a, float b, float c, float d) const; // done (inlined in vc) float GetDistanceSquared(const CVector &v) const; // done (inlined in vc) int32 GetJumboTaxiFreq() const; // done (inlined in vc) uint8 GetMissionAudioLoadingStatus(uint8 slot) const; // done int8 GetMissionScriptPoliceAudioPlayingStatus() const; // done uint8 GetNum3DProvidersAvailable() const; // done uint32 GetPedCommentSfx(CPed *ped, int32 sound); // done void GetPhrase(uint32 &phrase, uint32 &prevPhrase, uint32 sample, uint32 maxOffset) const; // done float GetVehicleDriveWheelSkidValue(CVehicle *veh, tWheelState wheelState, float gasPedalAudio, cTransmission *transmission, float velocityChange); // done float GetVehicleNonDriveWheelSkidValue(CVehicle *veh, tWheelState wheelState, cTransmission *transmission, float velocityChange); // done bool HasAirBrakes(int32 model) const; // done void Initialise(); // done void InitialisePoliceRadio(); // done void InitialisePoliceRadioZones(); // done void InterrogateAudioEntities(); // done (inlined) bool IsAudioInitialised() const; // done bool IsMissionAudioSampleFinished(uint8 slot); // done bool IsMP3RadioChannelAvailable() const; // done bool MissionScriptAudioUsesPoliceChannel(int32 soundMission) const; //done void PlayLoadedMissionAudio(uint8 slot); // done void PlayOneShot(int32 index, uint16 sound, float vol); // done void PlaySuspectLastSeen(float x, float y, float z); // done void PlayerJustGotInCar() const; // done void PlayerJustLeftCar() const; // done void PostInitialiseGameSpecificSetup(); // done void PostTerminateGameSpecificShutdown(); // done void PreInitialiseGameSpecificSetup() const; // done void PreloadMissionAudio(uint8 slot, Const char *name); // done void PreTerminateGameSpecificShutdown(); // done /// processX - main logic of adding new sounds void ProcessActiveQueues(); // done bool ProcessAirBrakes(cVehicleParams& params); // done bool ProcessBoatEngine(cVehicleParams& params); bool ProcessBoatMovingOverWater(cVehicleParams& params); //done #ifdef GTA_BRIDGE void ProcessBridge(); // done(bcs not exists in VC) void ProcessBridgeMotor(); // done(bcs not exists in VC) void ProcessBridgeOneShots(); // done(bcs not exists in VC) void ProcessBridgeWarning(); // done(bcs not exists in VC) #endif bool ProcessCarBombTick(cVehicleParams& params); // done void ProcessCarHeli(cVehicleParams& params); // done void ProcessCesna(cVehicleParams& params); // done //void ProcessCrane(); // done(bcs not exists in VC) bool ProcessEngineDamage(cVehicleParams& params); // done void ProcessEntity(int32 sound); // done void ProcessExplosions(int32 explosion); // done void ProcessFireHydrant(); // done void ProcessFires(int32 entity); // done void ProcessFrontEnd(); // done void ProcessGarages(); // done void ProcessJumbo(cVehicleParams& params); // done void ProcessJumboAccel(CPlane *plane); // done void ProcessJumboDecel(CPlane *plane); // done void ProcessJumboFlying(); // done void ProcessJumboLanding(CPlane *plane); // done void ProcessJumboTakeOff(CPlane *plane); // done void ProcessJumboTaxi(); // done void ProcessLoopingScriptObject(uint8 sound); // done void ProcessMissionAudio(); // done void ProcessMissionAudioSlot(uint8 slot); // done void ProcessModelHeliVehicle(cVehicleParams& params); // done void ProcessModelVehicle(cVehicleParams& params); // done void ProcessOneShotScriptObject(uint8 sound); // void ProcessPed(CPhysical *ped); // done void ProcessPedOneShots(cPedParams ¶ms); // void ProcessPhysical(int32 id); // done void ProcessPlane(cVehicleParams& params); // done void ProcessPlayerMood(); // done void ProcessPlayersVehicleEngine(cVehicleParams& params, CVehicle* veh); // done void ProcessProjectiles(); // done void ProcessRainOnVehicle(cVehicleParams& params); // done void ProcessReverb() const; // done bool ProcessReverseGear(cVehicleParams& params); // done void ProcessScriptObject(int32 id); // done void ProcessSpecial(); // done #ifdef GTA_TRAIN bool ProcessTrainNoise(cVehicleParams *params); //done(bcs not exists in VC) #endif void ProcessVehicle(CVehicle *vehicle); // done bool ProcessVehicleDoors(cVehicleParams ¶ms); // done void ProcessVehicleEngine(cVehicleParams ¶ms); // done void ProcessVehicleFlatTyre(cVehicleParams ¶ms); // done bool ProcessVehicleHorn(cVehicleParams ¶ms); // done void ProcessVehicleOneShots(cVehicleParams ¶ms); // done bool ProcessVehicleReverseWarning(cVehicleParams ¶ms); // done bool ProcessVehicleRoadNoise(cVehicleParams ¶ms); // done bool ProcessVehicleSirenOrAlarm(cVehicleParams ¶ms); // done bool ProcessVehicleSkidding(cVehicleParams ¶ms); // done void ProcessWaterCannon(int32); // done void ProcessWeather(int32 id); // done bool ProcessWetRoadNoise(cVehicleParams& params); // done void ProcessEscalators(); // done void ProcessExtraSounds(); // done int32 RandomDisplacement(uint32 seed) const; // done void ReacquireDigitalHandle() const; // done void ReleaseDigitalHandle() const; // done void ReportCollision(CEntity *entity1, CEntity *entity2, uint8 surface1, uint8 surface2, float collisionPower, float intensity2); // done void ReportCrime(eCrimeType crime, const CVector &pos); // done void ResetAudioLogicTimers(uint32 timer); // done void ResetPoliceRadio(); // done void ResetTimers(uint32 time); // done void Service(); // done void ServiceCollisions(); // done void ServicePoliceRadio(); // done void ServicePoliceRadioChannel(uint8 wantedLevel); // done void ServiceSoundEffects(); // done int8 SetCurrent3DProvider(uint8 which); // done void SetDynamicAcousticModelingStatus(uint8 status); // done void SetEffectsFadeVol(uint8 volume) const; // done void SetEffectsMasterVolume(uint8 volume) const; // done void SetMP3BoostVolume(uint8 volume) const; // done void SetEntityStatus(int32 id, uint8 status); // done uint32 SetLoopingCollisionRequestedSfxFreqAndGetVol(const cAudioCollision &audioCollision); // done void SetMissionAudioLocation(uint8 slot, float x, float y, float z); // done void SetMissionScriptPoliceAudio(int32 sfx) const; // inlined and optimized void SetMonoMode(uint8 mono); // done void SetMusicFadeVol(uint8 volume) const; // done void SetMusicMasterVolume(uint8 volume) const; // done void SetSpeakerConfig(int32 conf) const; // done void SetUpLoopingCollisionSound(const cAudioCollision &col, uint8 counter); // done void SetUpOneShotCollisionSound(const cAudioCollision &col); // done bool SetupCrimeReport(); // done bool SetupJumboEngineSound(uint8 vol, uint32 freq); // done bool SetupJumboFlySound(uint8 emittingVol); // done bool SetupJumboRumbleSound(uint8 emittingVol); // done bool SetupJumboTaxiSound(uint8 vol); // done bool SetupJumboWhineSound(uint8 emittingVol, uint32 freq); // done void SetupPedComments(cPedParams ¶ms, uint16 sound); // done void SetupSuspectLastSeenReport(); void Terminate(); // done void TranslateEntity(Const CVector *v1, CVector *v2) const; // done void UpdateGasPedalAudio(CVehicle *veh, int vehType); // done void UpdateReflections(); // done bool UsesReverseWarning(int32 model) const; // done bool UsesSiren(cVehicleParams ¶ms) const; // done bool UsesSirenSwitching(cVehicleParams ¶ms) const; // done CVehicle *FindVehicleOfPlayer(); // done void SetPedTalkingStatus(CPed *ped, uint8 status); // done void SetPlayersMood(uint8 mood, uint32 time); // done float Sqrt(float v) const { return v <= 0.0f ? 0.0f : ::Sqrt(v); } #ifdef GTA_PC // only used in pc void AdjustSamplesVolume(); // done (inlined) uint8 ComputeEmittingVolume(uint8 emittingVolume, float intensity, float dist); // done (inlined) #endif }; #ifdef AUDIO_MSS static_assert(sizeof(cAudioManager) == 0x5558, "cAudioManager: error"); #endif extern cAudioManager AudioManager; ================================================ FILE: src/audio/AudioSamples.h ================================================ #pragma once #include "common.h" enum eSfxSample { SFX_CAR_HORN_JEEP = 0, SFX_CAR_HORN_BMW328, SFX_CAR_HORN_BUS, SFX_CAR_HORN_BUS2, SFX_CAR_HORN_56CHEV, SFX_CAR_HORN_PICKUP, SFX_CAR_HORN_PORSCHE, SFX_CAR_HORN_TRUCK, SFX_CAR_HELI_MAI, // 8 SFX_CAR_HELI_MAI2, // 9 SFX_CAR_HELI_REA, // 10 SFX_CAR_HELI_STA, // 11 SFX_CAR_HELI_ROT, // 12 SFX_CAR_HELI_FAR, // 13 SFX_CAR_HELI_ROL, // 14 SFX_OLD_CAR_DOOR_OPEN, SFX_OLD_CAR_DOOR_CLOSE, SFX_NEW_CAR_DOOR_OPEN, SFX_NEW_CAR_DOOR_CLOSE, SFX_TRUCK_DOOR_OPEN, SFX_TRUCK_DOOR_CLOSE, SFX_REVERSE_GEAR, SFX_REVERSE_GEAR_2, SFX_CAR_STARTER, // 23 SFX_ROAD_NOISE, // 24 SFX_SKID, // 25 SFX_GRAVEL_SKID, // 26 SFX_POLICE_SIREN_SLOW, SFX_SIREN_FAST, // 28 SFX_AMBULANCE_SIREN_SLOW, SFX_REVERSE_WARNING, SFX_ICE_CREAM_TUNE, SFX_AIR_BRAKES, // 32 SFX_TYRE_BUMP, // 33 SFX_TYRE_BURST_B, // 34 SFX_TYRE_BURST, // 35 SFX_TYRE_BURST_L, // 36 SFX_PALM_TREE_LO, // 37 SFX_BULLET_PASS_1, // 38 SFX_BULLET_PASS_2, // 39 SFX_SKATE_1, // 40 SFX_SKATE_2, // 41 SFX_FOOTSTEP_CONCRETE_1, SFX_FOOTSTEP_CONCRETE_2, SFX_FOOTSTEP_CONCRETE_3, SFX_FOOTSTEP_CONCRETE_4, SFX_FOOTSTEP_CONCRETE_5, SFX_EXPLOSION_1, // 47 SFX_EXPLOSION_2, // 48 SFX_EXPLOSION_3, // 49 SFX_COLT45_LEFT, // 50 SFX_COLT45_RIGHT, // 51 SFX_AK47_LEFT, // 52 SFX_AK47_RIGHT, // 53 SFX_UZI_LEFT, // 54 SFX_UZI_RIGHT, // 55 SFX_UZI_END_LEFT, // 56 SFX_SNIPER_LEFT, // 57 SFX_SNIPER_RIGHT, // 58 SFX_ROCKET_LEFT, // 59 SFX_ROCKET_RIGHT, // 60 SFX_ROCKET_FLY, // 61 SFX_FLAMETHROWER_LEFT, // 62 SFX_FLAMETHROWER_RIGHT, // 63 SFX_FLAMETHROWER_START_LEFT, // 64 SFX_FLAMETHROWER_START_RIGHT, // 65 SFX_SHOTGUN_LEFT, // 66 SFX_SHOTGUN_RIGH, // 67 SFX_M60_LEFT, // 68 SFX_M60_RIGHT, // 69 SFX_M60_TAIL_LEFT, // 70 SFX_TEC_LEFT, // 71 SFX_TEC_RIGHT, // 72 SFX_TEC_TAIL, // 73 SFX_RUGER_LEFT, // 74 SFX_RUGER_RIGHT, // 75 SFX_RUGER_TAIL, // 76 SFX_PISTOL_RELOAD, // 77 SFX_AK47_RELOAD, // 78 SFX_ROCKET_RELOAD, // 79 SFX_RIFLE_RELOAD, // 80 SFX_GOLF_CLUB_SWING, // 81 SFX_MINIGUN_FIRE_LEFT, // 82 SFX_MINIGUN_FIRE_RIGHT, // 83 SFX_MINIGUN_STOP, // 84 SFX_SPAS12_LEFT, // 85 SFX_SPAS12_RIGHT, // 86 SFX_SPAS12_TAIL_LEFT, // 87 SFX_PYTHON_LEFT, // 88 SFX_PYTHON_RIGHT, // 89 SFX_MP5_LEFT, // 90 SFX_MP5_RIGHT, // 91 SFX_COL_TARMAC_1, // 92 SFX_COL_TARMAC_2, // 93 SFX_COL_TARMAC_3, // 94 SFX_COL_TARMAC_4, // 95 SFX_COL_TARMAC_5, // 96 SFX_COL_GRASS_1, SFX_COL_GRAVEL_1, SFX_COL_MUD_1, SFX_COL_GARAGE_DOOR_1, SFX_COL_CAR_PANEL_1, SFX_COL_CAR_PANEL_2, SFX_COL_CAR_PANEL_3, SFX_COL_CAR_PANEL_4, SFX_COL_CAR_PANEL_5, SFX_COL_CAR_PANEL_6, SFX_COL_THICK_METAL_PLATE_1, SFX_COL_SCAFFOLD_POLE_1, SFX_COL_LAMP_POST_1, SFX_COL_HYDRANT_1, SFX_COL_METAL_CHAIN_FENCE_1, SFX_COL_METAL_CHAIN_FENCE_2, SFX_COL_METAL_CHAIN_FENCE_3, SFX_COL_METAL_CHAIN_FENCE_4, SFX_COL_PED_1, // 115 SFX_COL_PED_2, // 116 SFX_COL_SAND_1, SFX_COL_WOOD_CRATES_1, SFX_COL_WOOD_CRATES_2, SFX_COL_WOOD_CRATES_3, SFX_COL_WOOD_CRATES_4, SFX_COL_WOOD_BENCH_1, SFX_COL_WOOD_BENCH_2, SFX_COL_WOOD_BENCH_3, SFX_COL_WOOD_BENCH_4, SFX_COL_WOOD_SOLID_1, SFX_COL_VEG_1, // 127 SFX_COL_VEG_2, // 128 SFX_COL_VEG_3, // 129 SFX_COL_VEG_4, // 130 SFX_COL_VEG_5, // 131 SFX_COL_CONTAINER_1, SFX_COL_NEWS_VENDOR_1, SFX_COL_NEWS_VENDOR_2, SFX_COL_NEWS_VENDOR_3, SFX_COL_CAR_1, // 136 SFX_COL_CAR_2, // 137 SFX_COL_CAR_3, // 138 SFX_COL_CAR_4, // 139 SFX_COL_CAR_5, // 140 SFX_COL_CARDBOARD_1, SFX_COL_CARDBOARD_2, SFX_COL_GATE, // 143 SFX_SCRAPE_CAR_1, // 144 SFX_CRATE_SMASH, SFX_GLASS_CRACK, // 146 SFX_GLASS_SMASH, // 147 SFX_GLASS_SHARD_1, SFX_GLASS_SHARD_2, SFX_GLASS_SHARD_3, SFX_GLASS_SHARD_4, SFX_PED_ON_FIRE, // 152 SFX_CAR_ON_FIRE, // 153 SFX_RAIN, // 154 SFX_HURRICANE_MA, // 155 SFX_BULLET_SHELL_HIT_GROUND_1, SFX_BULLET_SHELL_HIT_GROUND_2, SFX_BULLET_PED, // 158 SFX_BULLET_CAR_1, // 159 SFX_BULLET_CAR_2, // 160 SFX_BULLET_CAR_3, // 161 SFX_BULLET_WALL_1, // 162 SFX_BULLET_WALL_2, // 163 SFX_BULLET_WALL_3, // 164 SFX_BAT_HIT_LEFT, // 165 SFX_BAT_HIT_RIGH, // 166 SFX_FIGHT_1, // 167 SFX_FIGHT_2, // 168 SFX_FIGHT_4, // 169 SFX_FIGHT_5, // 170 SFX_KNIFE_SWING, // 171 SFX_KNIFE_SLASH, // 172 SFX_KNIFE_STAB, // 173 SFX_HAMMER_HIT_1, // 174 SFX_HAMMER_HIT_2, // 175 SFX_GARAGE_DOOR_LOOP, // 176 SFX_COUNTDOWN, // 177 SFX_ARM_BOMB, // 178 SFX_POLICE_RADIO_CRACKLE, // 179 SFX_WEVE_GOT, SFX_THERES, SFX_RESPOND_TO, SFX_A_10, SFX_IN, SFX_NORTH, SFX_EAST, SFX_SOUTH, SFX_WEST, SFX_CENTRAL, SFX_POLICE_RADIO_MESSAGE_NOISE_1, SFX_POLICE_RADIO_SUSPECT, SFX_POLICE_RADIO_LAST_SEEN, SFX_POLICE_RADIO_ON_FOOT, SFX_POLICE_RADIO_IN_A, SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_BRIGHT, SFX_CRIME_1, SFX_CRIME_2, SFX_CRIME_3, SFX_CRIME_4, SFX_CRIME_5, SFX_CRIME_6, SFX_CRIME_7, SFX_CRIME_8, SFX_CRIME_9, SFX_CRIME_10, SFX_CRIME_11, SFX_CRIME_12, SFX_POLICE_RADIO_VICE_CITY, SFX_POLICE_RADIO_VICE_CITY_BEACH, SFX_POLICE_RADIO_VICE_CITY_MAINLAND, SFX_POLICE_RADIO_OCEAN_BEACH, //??? SFX_POLICE_RADIO_WASHINGTON_BEACH, SFX_POLICE_RADIO_VICE_POINT, SFX_POLICE_RADIO_LEAF_LINKS, SFX_POLICE_RADIO_STRAFISH_ISLAND, //??????????? SFX_POLICE_RADIO_VICE_PORT, SFX_POLICE_RADIO_LITTLE_HAVANA, SFX_POLICE_RADIO_LITTLE_HAITI, SFX_POLICE_RADIO_PRAWN_ISLAND, //??????????? IS THAT HOW SHE PRONOUNCES ISLAND? SFX_POLICE_RADIO_DOWNTOWN, SFX_POLICE_RADIO_ESCOBAR_INTERNATIONAL, SFX_POLICE_RADIO_BLACK, SFX_POLICE_RADIO_WHITE, SFX_POLICE_RADIO_BLUE, SFX_POLICE_RADIO_RED, SFX_POLICE_RADIO_PURPLE, SFX_POLICE_RADIO_YELLOW, SFX_POLICE_RADIO_GREY, SFX_POLICE_RADIO_ORANGE, SFX_POLICE_RADIO_GREEN, SFX_POLICE_RADIO_SILVER, SFX_POLICE_RADIO_AMBULANCE, SFX_POLICE_RADIO_2_DOOR, SFX_POLICE_RADIO_TRUCK, SFX_POLICE_RADIO_FIRE_TRUCK, SFX_POLICE_RADIO_PICKUP, SFX_POLICE_RADIO_POLICE_CAR, SFX_POLICE_RADIO_BOAT, SFX_POLICE_RADIO_BUGGY, SFX_POLICE_RADIO_BUS, SFX_POLICE_RADIO_COACH, SFX_POLICE_RADIO_CRUISER, SFX_POLICE_RADIO_DINGHY, SFX_POLICE_RADIO_GARBAGE_TRUCK, SFX_POLICE_RADIO_GOLF_CART, SFX_POLICE_RADIO_HEARSE, SFX_POLICE_RADIO_HELICOPTER, SFX_POLICE_RADIO_ICE_CREAM_VAN, SFX_POLICE_RADIO_LOWRIDER, SFX_POLICE_RADIO_MOPED, SFX_POLICE_RADIO_MOTOBIKE, SFX_POLICE_RADIO_OFFROAD, SFX_POLICE_RADIO_PLANE, SFX_POLICE_RADIO_RIG, SFX_POLICE_RADIO_SEDAN, SFX_POLICE_RADIO_SPEEDBOAT, SFX_POLICE_RADIO_SPORTS_CAR, SFX_POLICE_RADIO_STATION_WAGON, SFX_POLICE_RADIO_STRETCH, SFX_POLICE_RADIO_SWAT_VAN, SFX_POLICE_RADIO_TANK, SFX_POLICE_RADIO_TAXI, SFX_POLICE_RADIO_VAN, SFX_HELI_1, // 198 SFX_PHONE_RING, // 199 SFX_CAR_REV_1, // PONT SFX_CAR_REV_2, // PORSHE SFX_CAR_REV_3, // SPIDER SFX_CAR_REV_4, // MERC SFX_CAR_REV_5, // TRUC SFX_CAR_REV_6, // HOTROD SFX_CAR_REV_7, // COBRA SFX_CAR_REV_8, // PONT2 SFX_CAR_REV_9, // CADI SFX_CAR_REV_10, // PATHFINDER SFX_CAR_REV_11, // PACARD SFX_CAR_REV_12, // GOLFCART SFX_CAR_REV_13, // SFX_CAR_IDLE_GOL SFX_CAR_REV_14, // SFX_CAR_IDLE_GOL SFX_CAR_REV_15, // SFX_CAR_IDLE_GOL SFX_CAR_REV_16, // SFX_CAR_IDLE_GOL SFX_CAR_REV_17, // VTWI SFX_MOPED_REV, // just moped SFX_CAR_REV_19, // HOND(A) SFX_CAR_REV_20, // SPOR(TCAR) SFX_CAR_IDLE_1, // PONT SFX_CAR_IDLE_2, // PORSHE SFX_CAR_IDLE_3, // SPIDER SFX_CAR_IDLE_4, // MERC SFX_CAR_IDLE_5, // TRUC SFX_CAR_IDLE_6, // HOTROD SFX_CAR_IDLE_7, // COBRA SFX_CAR_IDLE_8, // PONT2 SFX_CAR_IDLE_9, // CADI SFX_CAR_IDLE_10, // PATHFINDER SFX_CAR_IDLE_11, // PACARD SFX_CAR_IDLE_12, // GOLFCART SFX_CAR_IDLE_13, // SFX_CAR_IDLE_GOL SFX_CAR_IDLE_14, // SFX_CAR_IDLE_GOL SFX_CAR_IDLE_15, // SFX_CAR_IDLE_GOL SFX_CAR_IDLE_16, // SFX_CAR_IDLE_GOL SFX_CAR_IDLE_17, // VTWI SFX_MOPED_IDLE, // 237 SFX_CAR_IDLE_19, // HOND(A) SFX_CAR_IDLE_20, // SPOR(TCAR) SFX_JUMBO_DIST_FLY, SFX_JUMBO_TAXI, // 241 SFX_JUMBO_WHINE, // 242 SFX_JUMBO_ENGINE, // 243 SFX_JUMBO_RUMBLE, // 244 SFX_JUMBO_LAND_WHEELS, SFX_BOAT_CRUISER_LOOP, // 246 SFX_BOAT_V12_LOOP, // 247 SFX_BOAT_WATER_LOOP, SFX_BOAT_SPLASH_1, SFX_BOAT_SPLASH_2, SFX_FISHING_BOAT_IDLE, SFX_CAR_RAIN_1, // 252 SFX_CAR_RAIN_2, // 253 SFX_CAR_RAIN_3, // 254 SFX_CAR_RAIN_4, // 255 SFX_SPLASH_1, // 256 SFX_PED_CRUNCH_1, // 257 SFX_PED_CRUNCH_2, // 258 SFX_WOODEN_BOX_SMASH, SFX_CARDBOARD_BOX_SMASH, SFX_ERROR_FIRE_ROCKET_LAUNCHER, SFX_ERROR_FIRE_RIFLE, SFX_TANK_TURRET, // 263 SFX_BODY_LAND_AND_FALL, SFX_BODY_LAND, // 265 SFX_BOMB_BEEP, // 266 SFX_TIMER_BEEP, // 267 SFX_SUSPENSION_FAST_MOVE, SFX_SUSPENSION_SLOW_MOVE_LOOP, SFX_SHAG_SUSPENSION, SFX_HIT_BALL, // 271 SFX_ARCADE, // 272 SFX_CESNA_IDLE, // 273 SFX_CESNA_REV, // 274 SFX_RADIO_CLICK, // 275 SFX_RADIO_DIAL_1, // 276 SFX_RADIO_DIAL_2, // 277 SFX_RADIO_DIAL_3, // 278 // pc only SFX_RADIO_DIAL_4, SFX_RADIO_DIAL_5, SFX_RADIO_DIAL_6, SFX_RADIO_DIAL_7, SFX_RADIO_DIAL_8, SFX_RADIO_DIAL_9, SFX_RADIO_DIAL_10, SFX_RADIO_DIAL_11, SFX_RADIO_DIAL_12, SFX_INFO_LEFT, // 279 SFX_INFO_RIGHT, // 280 SFX_INFO_CENTRE, // 281 SFX_MONEY_LEFT, // 282 SFX_MONEY_RIGHT, // 283 SFX_WEAPON_LEFT, // 284 SFX_WEAPON_RIGHT, // 285 SFX_WEAPON_CENTRE, // 286 SFX_PART_MISSION_COMPLETE_LEFT, // 287 SFX_PART_MISSION_COMPLETE_RIGHT, // 288 SFX_PART_MISSION_COMPLETE_CENTRE, // 289 SFX_GO_LEFT, // 290 SFX_GO_RIGHT, // 291 SFX_GO_CENTRE, // 292 SFX_TIMER, // 293 SFX_EMPTY, // 294 SFX_FE_HIGHLIGHT_LEFT, // SFX_FE_HIGHLIGHT_RIGHT, // SFX_FE_SELECT_LEFT, // SFX_FE_SELECT_RIGHT, // SFX_FE_BACK_LEFT, // SFX_FE_BACK_RIGHT, // SFX_FE_ERROR_LEFT, // SFX_FE_ERROR_RIGHT, // SFX_FE_NOISE_BURST_1, SFX_FE_NOISE_BURST_2, SFX_FE_NOISE_BURST_3, SFX_CAR_ACCEL_1, SFX_CAR_AFTER_ACCEL_1, SFX_CAR_FINGER_OFF_ACCEL_1, SFX_CAR_ACCEL_2, SFX_CAR_AFTER_ACCEL_2, SFX_CAR_FINGER_OFF_ACCEL_2, SFX_CAR_ACCEL_3, SFX_CAR_AFTER_ACCEL_3, SFX_CAR_FINGER_OFF_ACCEL_3, SFX_CAR_ACCEL_4, SFX_CAR_AFTER_ACCEL_4, SFX_CAR_FINGER_OFF_ACCEL_4, SFX_CAR_ACCEL_5, SFX_CAR_AFTER_ACCEL_5, SFX_CAR_FINGER_OFF_ACCEL_5, SFX_CAR_ACCEL_6, SFX_CAR_AFTER_ACCEL_6, SFX_CAR_FINGER_OFF_ACCEL_6, SFX_CAR_ACCEL_7, SFX_CAR_AFTER_ACCEL_7, SFX_CAR_FINGER_OFF_ACCEL_7, SFX_CAR_ACCEL_8, SFX_CAR_AFTER_ACCEL_8, SFX_CAR_FINGER_OFF_ACCEL_8, SFX_CAR_ACCEL_9, SFX_CAR_AFTER_ACCEL_9, SFX_CAR_FINGER_OFF_ACCEL_9, SFX_CAR_ACCEL_10, SFX_CAR_AFTER_ACCEL_10, SFX_CAR_FINGER_OFF_ACCEL_10, SFX_CAR_ACCEL_11, SFX_CAR_AFTER_ACCEL_11, SFX_CAR_FINGER_OFF_ACCEL_11, SFX_CAR_ACCEL_12, SFX_CAR_AFTER_ACCEL_12, SFX_CAR_FINGER_OFF_ACCEL_12, // some CHAINSAW STUFF // SFX_CAR_CHAINSAW, //10973 // SFX_CAR_CHAINSAW, //10974 // SFX_CAR_CHAINSAW, //10975 SFX_CAR_ACCEL_13, SFX_CAR_AFTER_ACCEL_13, SFX_CAR_FINGER_OFF_ACCEL_13, SFX_RC_IDLE, // 10976 SFX_RC_REV, // 10977 SFX_RC_EMPTY, // 10978 SFX_CAR_RC_HELI, // 10979 SFX_CAR_AFTER_ACCEL_15, // empty SFX_CAR_FINGER_OFF_ACCEL_15, // empty SFX_CAR_ACCEL_16, // empty SFX_CAR_AFTER_ACCEL_16, // empty SFX_CAR_FINGER_OFF_ACCEL_16, // empty // bike stuff apparently SFX_CAR_ACCEL_17, SFX_CAR_AFTER_ACCEL_17, SFX_CAR_FINGER_OFF_ACCEL_17, SFX_CAR_WIND_17, SFX_CAR_ACCEL_18, SFX_CAR_AFTER_ACCEL_18, SFX_CAR_FINGER_OFF_ACCEL_18, SFX_CAR_WIND_18, SFX_CAR_ACCEL_19, SFX_CAR_AFTER_ACCEL_19, SFX_CAR_FINGER_OFF_ACCEL_19, SFX_CAR_WIND_19, SFX_CAR_ACCEL_20, SFX_CAR_AFTER_ACCEL_20, SFX_CAR_FINGER_OFF_ACCEL_20, SFX_CAR_WIND_20, // some emptinnes here SFX_CAR_ACCEL_21, SFX_CAR_AFTER_ACCEL_21, SFX_CAR_FINGER_OFF_ACCEL_21, SFX_CAR_ACCEL_22, SFX_CAR_AFTER_ACCEL_22, SFX_CAR_FINGER_OFF_ACCEL_22, SFX_HELI_APACHE_1, SFX_HELI_APACHE_2, SFX_HELI_APACHE_3, SFX_HELI_APACHE_4, // something padded for more heli? SFX_HELI_UNUSED_1, SFX_HELI_UNUSED_2, SFX_HELI_UNUSED_3, SFX_HELI_UNUSED_4, SFX_SEAPLANE_PRO1, // 11018 SFX_SEAPLANE_PRO2, // 11019 SFX_SEAPLANE_PRO3, // 11020 SFX_SEAPLANE_PRO4, // 11021 // low fuel SFX_SEAPLANE_LOW, // 11022 // something padded for more plane? SFX_PLANE_UNUSED_1, SFX_PLANE_UNUSED_2, SFX_PLANE_UNUSED_3, SFX_PLANE_UNUSED_4, // script objects SFX_BUILDINGS_BANK_ALARM, // 11027 SFX_BUILDING_SNORE, // 11028 SFX_BUILDING_BAR_1, // 11029 SFX_BUILDING_BAR_2, // 11030 SFX_BUILDING_BAR_3, // 11031 SFX_BUILDING_BAR_4, // 11032 SFX_BUILDING_MAL1, // 11033 SFX_BUILDING_MAL2, // 11034 SFX_BUILDING_MAL3, // 11035 SFX_BUILDING_STR1, // 11036 SFX_BUILDING_STR2, // 11037 SFX_BUILDING_STR3, // 11038 SFX_BUILDING_CHURCH, // 11039 SFX_BUILDING_FAN_1, // 11040 SFX_BUILDING_FAN_2, // 11041 SFX_BUILDING_FAN_3, // 11042 SFX_BUILDING_FAN_4, // 11043 SFX_BUILDING_INSECTS_1, // 11044 SFX_BUILDING_INSECTS_2, // 11045 SFX_BUILDING_INSECTS_3, // 11046 SFX_BUILDING_INSECTS_4, // 11047 SFX_BUILDING_INSECTS_5, // 11048 SFX_CLUB_1, // 11049 SFX_CLUB_2, // 11050 SFX_CLUB_3, // 11051 SFX_CLUB_4, // 11052 SFX_FOOTSTEP_GRASS_1, SFX_FOOTSTEP_GRASS_2, SFX_FOOTSTEP_GRASS_3, SFX_FOOTSTEP_GRASS_4, SFX_FOOTSTEP_GRASS_5, SFX_FOOTSTEP_GRAVEL_1, SFX_FOOTSTEP_GRAVEL_2, SFX_FOOTSTEP_GRAVEL_3, SFX_FOOTSTEP_GRAVEL_4, SFX_FOOTSTEP_GRAVEL_5, SFX_FOOTSTEP_WOOD_1, SFX_FOOTSTEP_WOOD_2, SFX_FOOTSTEP_WOOD_3, SFX_FOOTSTEP_WOOD_4, SFX_FOOTSTEP_WOOD_5, SFX_FOOTSTEP_METAL_1, SFX_FOOTSTEP_METAL_2, SFX_FOOTSTEP_METAL_3, SFX_FOOTSTEP_METAL_4, SFX_FOOTSTEP_METAL_5, SFX_FOOTSTEP_WATER_1, SFX_FOOTSTEP_WATER_2, SFX_FOOTSTEP_WATER_3, SFX_FOOTSTEP_WATER_4, SFX_FOOTSTEP_SAND_1, SFX_FOOTSTEP_SAND_2, SFX_FOOTSTEP_SAND_3, SFX_FOOTSTEP_SAND_4, // TODO: miami ped comments... THERE'S OVER 9000 SFX_POLICE_BOAT_1 = 714, SFX_POLICE_BOAT_2, SFX_POLICE_BOAT_3, SFX_POLICE_BOAT_4, SFX_POLICE_BOAT_5, SFX_POLICE_BOAT_6, SFX_POLICE_BOAT_7, SFX_POLICE_BOAT_8, SFX_POLICE_BOAT_9, SFX_POLICE_BOAT_10, SFX_POLICE_BOAT_11, SFX_POLICE_BOAT_12, SFX_POLICE_BOAT_13, SFX_POLICE_BOAT_14, SFX_POLICE_BOAT_15, SFX_POLICE_BOAT_16, SFX_POLICE_BOAT_17, SFX_POLICE_BOAT_18, SFX_POLICE_BOAT_19, SFX_POLICE_BOAT_20, SFX_POLICE_BOAT_21, SFX_POLICE_BOAT_22, SFX_POLICE_BOAT_23, SFX_POLICE_HELI_1, SFX_POLICE_HELI_2, SFX_POLICE_HELI_3, SFX_POLICE_HELI_4, SFX_POLICE_HELI_5, SFX_POLICE_HELI_6, SFX_POLICE_HELI_7, SFX_POLICE_HELI_8, SFX_POLICE_HELI_9, SFX_POLICE_HELI_10, SFX_POLICE_HELI_11, SFX_POLICE_HELI_12, SFX_POLICE_HELI_13, SFX_POLICE_HELI_14, SFX_POLICE_HELI_15, SFX_POLICE_HELI_16, SFX_POLICE_HELI_17, SFX_POLICE_HELI_18, SFX_POLICE_HELI_19, SFX_POLICE_HELI_20, SFX_GENERIC_FEMALE_GRUNT_1 = 2953, SFX_GENERIC_FEMALE_GRUNT_2 = 2954, SFX_GENERIC_FEMALE_GRUNT_3 = 2955, SFX_GENERIC_FEMALE_GRUNT_4 = 2956, SFX_GENERIC_FEMALE_GRUNT_5 = 2957, SFX_GENERIC_FEMALE_GRUNT_6 = 2958, SFX_GENERIC_FEMALE_GRUNT_7 = 2959, SFX_GENERIC_FEMALE_GRUNT_8 = 2960, SFX_GENERIC_FEMALE_GRUNT_9 = 2961, SFX_GENERIC_FEMALE_GRUNT_10 = 2962, SFX_GENERIC_FEMALE_GRUNT_11 = 2963, SFX_GENERIC_FEMALE_GRUNT_12 = 2964, SFX_GENERIC_FEMALE_GRUNT_13 = 2965, SFX_GENERIC_FEMALE_GRUNT_14 = 2966, SFX_GENERIC_FEMALE_GRUNT_15 = 2967, SFX_GENERIC_FEMALE_GRUNT_16 = 2968, SFX_GENERIC_FEMALE_GRUNT_17 = 2969, SFX_GENERIC_FEMALE_GRUNT_18 = 2970, SFX_GENERIC_FEMALE_GRUNT_19 = 2971, SFX_GENERIC_FEMALE_GRUNT_20 = 2972, SFX_GENERIC_FEMALE_GRUNT_21 = 2973, SFX_GENERIC_FEMALE_GRUNT_22 = 2974, SFX_GENERIC_FEMALE_GRUNT_23 = 2975, SFX_GENERIC_FEMALE_GRUNT_24 = 2976, SFX_GENERIC_FEMALE_GRUNT_25 = 2977, SFX_GENERIC_FEMALE_GRUNT_26 = 2978, SFX_GENERIC_FEMALE_GRUNT_27 = 2979, SFX_GENERIC_FEMALE_GRUNT_28 = 2980, SFX_GENERIC_FEMALE_GRUNT_29 = 2981, SFX_GENERIC_FEMALE_GRUNT_30 = 2982, SFX_GENERIC_FEMALE_GRUNT_31 = 2983, SFX_GENERIC_FEMALE_GRUNT_32 = 2984, SFX_GENERIC_FEMALE_GRUNT_33 = 2985, SFX_GENERIC_MALE_FIRE_1 = 3013, SFX_GENERIC_MALE_FIRE_2 = 3014, SFX_GENERIC_MALE_FIRE_3 = 3015, SFX_GENERIC_MALE_FIRE_4 = 3016, SFX_GENERIC_MALE_FIRE_5 = 3017, SFX_GENERIC_MALE_FIRE_6 = 3018, SFX_GENERIC_MALE_FIRE_7 = 3019, SFX_GENERIC_MALE_FIRE_8 = 3020, SFX_GENERIC_MALE_FIRE_9 = 3021, SFX_GENERIC_MALE_FIRE_10 = 3022, SFX_GENERIC_MALE_FIRE_11 = 3023, SFX_GENERIC_MALE_FIRE_12 = 3024, SFX_GENERIC_MALE_FIRE_13 = 3025, SFX_GENERIC_MALE_FIRE_14 = 3026, SFX_GENERIC_MALE_FIRE_15 = 3027, SFX_GENERIC_MALE_FIRE_16 = 3028, SFX_GENERIC_MALE_FIRE_17 = 3029, SFX_GENERIC_MALE_FIRE_18 = 3030, SFX_GENERIC_MALE_FIRE_19 = 3031, SFX_GENERIC_MALE_FIRE_20 = 3032, SFX_GENERIC_MALE_FIRE_21 = 3033, SFX_GENERIC_MALE_FIRE_22 = 3034, SFX_GENERIC_MALE_FIRE_23 = 3035, SFX_GENERIC_MALE_FIRE_24 = 3036, SFX_GENERIC_MALE_FIRE_25 = 3037, SFX_GENERIC_MALE_FIRE_26 = 3038, SFX_GENERIC_MALE_FIRE_27 = 3039, SFX_GENERIC_MALE_FIRE_28 = 3040, SFX_GENERIC_MALE_FIRE_29 = 3041, SFX_GENERIC_MALE_FIRE_30 = 3042, SFX_GENERIC_MALE_FIRE_31 = 3043, SFX_GENERIC_MALE_FIRE_32 = 3044, SFX_GENERIC_MALE_DEATH_1 = 3045, SFX_GENERIC_MALE_DEATH_2 = 3046, SFX_GENERIC_MALE_DEATH_3 = 3047, SFX_GENERIC_MALE_DEATH_4 = 3048, SFX_GENERIC_MALE_DEATH_5 = 3049, SFX_GENERIC_MALE_DEATH_6 = 3050, SFX_GENERIC_MALE_DEATH_7 = 3051, SFX_GENERIC_MALE_DEATH_8 = 3052, SFX_GENERIC_MALE_DEATH_9 = 3053, SFX_GENERIC_MALE_DEATH_10 = 3054, SFX_GENERIC_MALE_DEATH_11 = 3055, SFX_GENERIC_MALE_DEATH_12 = 3056, SFX_GENERIC_MALE_DEATH_13 = 3057, SFX_GENERIC_MALE_DEATH_14 = 3058, SFX_GENERIC_MALE_DEATH_15 = 3059, SFX_GENERIC_MALE_DEATH_16 = 3060, SFX_GENERIC_MALE_DEATH_17 = 3061, SFX_GENERIC_MALE_DEATH_18 = 3062, SFX_GENERIC_MALE_DEATH_19 = 3063, SFX_GENERIC_MALE_DEATH_20 = 3064, SFX_GENERIC_MALE_DEATH_21 = 3065, SFX_GENERIC_MALE_DEATH_22 = 3066, SFX_GENERIC_MALE_DEATH_23 = 3067, SFX_GENERIC_MALE_DEATH_24 = 3068, SFX_GENERIC_MALE_DEATH_25 = 3069, SFX_GENERIC_MALE_DEATH_26 = 3070, SFX_GENERIC_MALE_DEATH_27 = 3071, SFX_GENERIC_MALE_DEATH_28 = 3072, SFX_GENERIC_MALE_DEATH_29 = 3073, SFX_GENERIC_MALE_DEATH_30 = 3074, SFX_GENERIC_MALE_DEATH_31 = 3075, SFX_GENERIC_MALE_DEATH_32 = 3076, SFX_GENERIC_MALE_DEATH_33 = 3077, SFX_GENERIC_MALE_DEATH_34 = 3078, SFX_GENERIC_MALE_DEATH_35 = 3079, SFX_GENERIC_MALE_DEATH_36 = 3080, SFX_GENERIC_MALE_DEATH_37 = 3081, SFX_GENERIC_MALE_DEATH_38 = 3082, SFX_GENERIC_MALE_DEATH_39 = 3083, SFX_GENERIC_MALE_DEATH_40 = 3084, SFX_GENERIC_MALE_DEATH_41 = 3085, SFX_GENERIC_MALE_GRUNT_1 = 3086, SFX_GENERIC_MALE_GRUNT_2 = 3087, SFX_GENERIC_MALE_GRUNT_3 = 3088, SFX_GENERIC_MALE_GRUNT_4 = 3089, SFX_GENERIC_MALE_GRUNT_5 = 3090, SFX_GENERIC_MALE_GRUNT_6 = 3091, SFX_GENERIC_MALE_GRUNT_7 = 3092, SFX_GENERIC_MALE_GRUNT_8 = 3093, SFX_GENERIC_MALE_GRUNT_9 = 3094, SFX_GENERIC_MALE_GRUNT_10 = 3095, SFX_GENERIC_MALE_GRUNT_11 = 3096, SFX_GENERIC_MALE_GRUNT_12 = 3097, SFX_GENERIC_MALE_GRUNT_13 = 3098, SFX_GENERIC_MALE_GRUNT_14 = 3099, SFX_GENERIC_MALE_GRUNT_15 = 3100, SFX_GENERIC_MALE_GRUNT_16 = 3101, SFX_GENERIC_MALE_GRUNT_17 = 3102, SFX_GENERIC_MALE_GRUNT_18 = 3103, SFX_GENERIC_MALE_GRUNT_19 = 3104, SFX_GENERIC_MALE_GRUNT_20 = 3105, SFX_GENERIC_MALE_GRUNT_21 = 3106, SFX_GENERIC_MALE_GRUNT_22 = 3107, SFX_GENERIC_MALE_GRUNT_23 = 3108, SFX_GENERIC_MALE_GRUNT_24 = 3109, SFX_GENERIC_MALE_GRUNT_25 = 3110, SFX_GENERIC_MALE_GRUNT_26 = 3111, SFX_GENERIC_MALE_GRUNT_27 = 3112, SFX_GENERIC_MALE_GRUNT_28 = 3113, SFX_GENERIC_MALE_GRUNT_29 = 3114, SFX_GENERIC_MALE_GRUNT_30 = 3115, SFX_GENERIC_MALE_GRUNT_31 = 3116, SFX_GENERIC_MALE_GRUNT_32 = 3117, SFX_GENERIC_MALE_GRUNT_33 = 3118, SFX_GENERIC_MALE_GRUNT_34 = 3119, SFX_GENERIC_MALE_GRUNT_35 = 3120, SFX_GENERIC_MALE_GRUNT_36 = 3121, SFX_GENERIC_MALE_GRUNT_37 = 3122, SFX_GENERIC_MALE_GRUNT_38 = 3123, SFX_GENERIC_MALE_GRUNT_39 = 3124, SFX_GENERIC_MALE_GRUNT_40 = 3125, SFX_GENERIC_MALE_GRUNT_41 = 3126, SFX_GENERIC_MALE_PANIC_1 = 3127, SFX_GENERIC_MALE_PANIC_2 = 3128, SFX_GENERIC_MALE_PANIC_3 = 3129, SFX_GENERIC_MALE_PANIC_4 = 3130, SFX_GENERIC_MALE_PANIC_5 = 3131, SFX_GENERIC_MALE_PANIC_6 = 3132, SFX_GENERIC_MALE_PANIC_7 = 3133, SFX_GENERIC_MALE_PANIC_8 = 3134, SFX_GENERIC_MALE_PANIC_9 = 3135, SFX_GENERIC_MALE_PANIC_10 = 3136, SFX_GENERIC_MALE_PANIC_11 = 3137, SFX_GENERIC_MALE_PANIC_12 = 3138, SFX_GENERIC_MALE_PANIC_13 = 3139, SFX_GENERIC_MALE_PANIC_14 = 3140, SFX_GENERIC_MALE_PANIC_15 = 3141, SFX_GENERIC_MALE_PANIC_16 = 3142, SFX_GENERIC_MALE_PANIC_17 = 3143, SFX_GENERIC_MALE_PANIC_18 = 3144, SFX_GENERIC_MALE_PANIC_19 = 3145, SFX_GENERIC_MALE_PANIC_20 = 3146, SFX_GENERIC_MALE_PANIC_21 = 3147, SFX_GENERIC_MALE_PANIC_22 = 3148, SFX_GENERIC_MALE_PANIC_23 = 3149, SFX_GENERIC_MALE_PANIC_24 = 3150, SFX_GENERIC_MALE_PANIC_25 = 3151, SFX_GENERIC_MALE_PANIC_26 = 3152, SFX_GENERIC_MALE_PANIC_27 = 3153, SFX_GENERIC_MALE_PANIC_28 = 3154, SFX_GENERIC_MALE_PANIC_29 = 3155, SFX_GENERIC_MALE_PANIC_30 = 3156, SFX_GENERIC_MALE_PANIC_31 = 3157, SFX_GENERIC_MALE_PANIC_32 = 3158, SFX_GENERIC_MALE_PANIC_33 = 3159, SFX_GENERIC_MALE_PANIC_34 = 3160, SFX_GENERIC_MALE_PANIC_35 = 3161, TOTAL_AUDIO_SAMPLES = 9941, NO_SAMPLE, // shorthands SAMPLEBANK_START = SFX_CAR_HORN_JEEP, SAMPLEBANK_END = SFX_FOOTSTEP_SAND_4, SAMPLEBANK_MAX = SFX_FOOTSTEP_SAND_4 + 1, SAMPLEBANK_PED_START = SFX_FOOTSTEP_SAND_4 + 1, SAMPLEBANK_PED_END = 9940, SAMPLEBANK_PED_MAX = SAMPLEBANK_PED_END + 1, }; ================================================ FILE: src/audio/AudioScriptObject.cpp ================================================ #include "common.h" #include "AudioScriptObject.h" #include "Pools.h" #include "DMAudio.h" cAudioScriptObject::cAudioScriptObject() { Reset(); }; cAudioScriptObject::~cAudioScriptObject() { Reset(); }; void cAudioScriptObject::Reset() { AudioId = SCRIPT_SOUND_INVALID; Posn = CVector(0.0f, 0.0f, 0.0f); AudioEntity = AEHANDLE_NONE; } 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 cAudioScriptObject::LoadAllAudioScriptObjects(uint8 *buf, uint32 size) { INITSAVEBUF CheckSaveHeader(buf, 'A', 'U', 'D', '\0', size - SAVE_HEADER_SIZE); int32 pool_size = ReadSaveBuf(buf); for (int32 i = 0; i < pool_size; i++) { int handle = ReadSaveBuf(buf); cAudioScriptObject *p = new(handle) cAudioScriptObject; assert(p != nil); *p = ReadSaveBuf(buf); p->AudioEntity = DMAudio.CreateLoopingScriptObject(p); } VALIDATESAVEBUF(size); } void cAudioScriptObject::SaveAllAudioScriptObjects(uint8 *buf, uint32 *size) { INITSAVEBUF int32 pool_size = CPools::GetAudioScriptObjectPool()->GetNoOfUsedSpaces(); *size = SAVE_HEADER_SIZE + sizeof(int32) + pool_size * (sizeof(cAudioScriptObject) + sizeof(int32)); WriteSaveHeader(buf, 'A', 'U', 'D', '\0', *size - SAVE_HEADER_SIZE); WriteSaveBuf(buf, pool_size); int32 i = CPools::GetAudioScriptObjectPool()->GetSize(); while (i--) { cAudioScriptObject *p = CPools::GetAudioScriptObjectPool()->GetSlot(i); if (p != nil) { WriteSaveBuf(buf, CPools::GetAudioScriptObjectPool()->GetIndex(p)); WriteSaveBuf(buf, *p); } } VALIDATESAVEBUF(*size); } void PlayOneShotScriptObject(uint8 id, CVector const &pos) { cAudioScriptObject *audioScriptObject = new cAudioScriptObject(); audioScriptObject->Posn = pos; audioScriptObject->AudioId = id; audioScriptObject->AudioEntity = AEHANDLE_NONE; DMAudio.CreateOneShotScriptObject(audioScriptObject); } ================================================ FILE: src/audio/AudioScriptObject.h ================================================ #pragma once class cAudioScriptObject { public: int16 AudioId; CVector Posn; int32 AudioEntity; cAudioScriptObject(); ~cAudioScriptObject(); void Reset(); /// ok 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 LoadAllAudioScriptObjects(uint8 *buf, uint32 size); static void SaveAllAudioScriptObjects(uint8 *buf, uint32 *size); }; VALIDATE_SIZE(cAudioScriptObject, 20); extern void PlayOneShotScriptObject(uint8 id, CVector const &pos); ================================================ FILE: src/audio/DMAudio.cpp ================================================ #include "common.h" #include "DMAudio.h" #include "MusicManager.h" #include "AudioManager.h" #include "AudioScriptObject.h" #include "sampman.h" cDMAudio DMAudio; void cDMAudio::Initialise(void) { AudioManager.Initialise(); } void cDMAudio::Terminate(void) { AudioManager.Terminate(); } void cDMAudio::Service(void) { AudioManager.Service(); } int32 cDMAudio::CreateEntity(eAudioType type, void *UID) { return AudioManager.CreateEntity(type, (CPhysical *)UID); } void cDMAudio::DestroyEntity(int32 audioEntity) { AudioManager.DestroyEntity(audioEntity); } void cDMAudio::SetEntityStatus(int32 audioEntity, uint8 status) { AudioManager.SetEntityStatus(audioEntity, status); } void cDMAudio::PlayOneShot(int32 audioEntity, uint16 oneShot, float volume) { AudioManager.PlayOneShot(audioEntity, oneShot, volume); } void cDMAudio::DestroyAllGameCreatedEntities(void) { AudioManager.DestroyAllGameCreatedEntities(); } void cDMAudio::SetMonoMode(uint8 mono) { AudioManager.SetMonoMode(mono); } void cDMAudio::SetMP3BoostVolume(uint8 volume) { uint8 vol = volume; if (vol > MAX_VOLUME) vol = MAX_VOLUME; AudioManager.SetMP3BoostVolume(vol); } void cDMAudio::SetEffectsMasterVolume(uint8 volume) { uint8 vol = volume; if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; AudioManager.SetEffectsMasterVolume(vol); } void cDMAudio::SetMusicMasterVolume(uint8 volume) { uint8 vol = volume; if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; AudioManager.SetMusicMasterVolume(vol); } void cDMAudio::SetEffectsFadeVol(uint8 volume) { uint8 vol = volume; if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; AudioManager.SetEffectsFadeVol(vol); } void cDMAudio::SetMusicFadeVol(uint8 volume) { uint8 vol = volume; if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; AudioManager.SetMusicFadeVol(vol); } uint8 cDMAudio::GetNum3DProvidersAvailable(void) { return AudioManager.GetNum3DProvidersAvailable(); } char * cDMAudio::Get3DProviderName(uint8 id) { return AudioManager.Get3DProviderName(id); } int8 cDMAudio::AutoDetect3DProviders(void) { return AudioManager.AutoDetect3DProviders(); } int8 cDMAudio::GetCurrent3DProviderIndex(void) { return AudioManager.GetCurrent3DProviderIndex(); } int8 cDMAudio::SetCurrent3DProvider(uint8 which) { return AudioManager.SetCurrent3DProvider(which); } void cDMAudio::SetSpeakerConfig(int32 config) { AudioManager.SetSpeakerConfig(config); } bool cDMAudio::IsMP3RadioChannelAvailable(void) { return AudioManager.IsMP3RadioChannelAvailable(); } void cDMAudio::ReleaseDigitalHandle(void) { AudioManager.ReleaseDigitalHandle(); } void cDMAudio::ReacquireDigitalHandle(void) { AudioManager.ReacquireDigitalHandle(); } void cDMAudio::SetDynamicAcousticModelingStatus(uint8 status) { AudioManager.SetDynamicAcousticModelingStatus(status); } bool cDMAudio::CheckForAnAudioFileOnCD(void) { return AudioManager.CheckForAnAudioFileOnCD(); } char cDMAudio::GetCDAudioDriveLetter(void) { return AudioManager.GetCDAudioDriveLetter(); } bool cDMAudio::IsAudioInitialised(void) { return AudioManager.IsAudioInitialised(); } void cDMAudio::ReportCrime(eCrimeType crime, const CVector &pos) { AudioManager.ReportCrime(crime, pos); } int32 cDMAudio::CreateLoopingScriptObject(cAudioScriptObject *scriptObject) { int32 audioEntity = AudioManager.CreateEntity(AUDIOTYPE_SCRIPTOBJECT, scriptObject); if ( AEHANDLE_IS_OK(audioEntity) ) AudioManager.SetEntityStatus(audioEntity, true); return audioEntity; } void cDMAudio::DestroyLoopingScriptObject(int32 audioEntity) { AudioManager.DestroyEntity(audioEntity); } void cDMAudio::CreateOneShotScriptObject(cAudioScriptObject *scriptObject) { int32 audioEntity = AudioManager.CreateEntity(AUDIOTYPE_SCRIPTOBJECT, scriptObject); if ( AEHANDLE_IS_OK(audioEntity) ) { AudioManager.SetEntityStatus(audioEntity, true); AudioManager.PlayOneShot(audioEntity, scriptObject->AudioId, 0.0f); } } void cDMAudio::PlaySuspectLastSeen(float x, float y, float z) { AudioManager.PlaySuspectLastSeen(x, y, z); } void cDMAudio::ReportCollision(CEntity *entityA, CEntity *entityB, uint8 surfaceTypeA, uint8 surfaceTypeB, float collisionPower, float velocity) { AudioManager.ReportCollision(entityA, entityB, surfaceTypeA, surfaceTypeB, collisionPower, velocity); } void cDMAudio::PlayFrontEndSound(uint16 frontend, uint32 volume) { AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, frontend, (float)volume); } void cDMAudio::PlayRadioAnnouncement(uint32 announcement) { MusicManager.PlayAnnouncement(announcement); } void cDMAudio::PlayFrontEndTrack(uint32 track, uint8 frontendFlag) { MusicManager.PlayFrontEndTrack(track, frontendFlag); } void cDMAudio::StopFrontEndTrack(void) { MusicManager.StopFrontEndTrack(); } void cDMAudio::ResetTimers(uint32 time) { AudioManager.ResetTimers(time); } void cDMAudio::ChangeMusicMode(uint8 mode) { MusicManager.ChangeMusicMode(mode); } void cDMAudio::PreloadCutSceneMusic(uint32 track) { MusicManager.PreloadCutSceneMusic(track); } void cDMAudio::PlayPreloadedCutSceneMusic(void) { MusicManager.PlayPreloadedCutSceneMusic(); } void cDMAudio::StopCutSceneMusic(void) { MusicManager.StopCutSceneMusic(); } void cDMAudio::PreloadMissionAudio(uint8 slot, Const char *missionAudio) { AudioManager.PreloadMissionAudio(slot, missionAudio); } uint8 cDMAudio::GetMissionAudioLoadingStatus(uint8 slot) { return AudioManager.GetMissionAudioLoadingStatus(slot); } void cDMAudio::SetMissionAudioLocation(uint8 slot, float x, float y, float z) { AudioManager.SetMissionAudioLocation(slot, x, y, z); } void cDMAudio::PlayLoadedMissionAudio(uint8 slot) { AudioManager.PlayLoadedMissionAudio(slot); } bool cDMAudio::IsMissionAudioSampleFinished(uint8 slot) { return AudioManager.IsMissionAudioSampleFinished(slot); } void cDMAudio::ClearMissionAudio(uint8 slot) { AudioManager.ClearMissionAudio(slot); } uint8 cDMAudio::GetRadioInCar(void) { return MusicManager.GetRadioInCar(); } void cDMAudio::SetRadioInCar(uint32 radio) { MusicManager.SetRadioInCar(radio); } void cDMAudio::SetRadioChannel(uint32 radio, int32 pos) { MusicManager.SetRadioChannelByScript(radio, pos); } void cDMAudio::SetStartingTrackPositions(uint8 isStartGame) { MusicManager.SetStartingTrackPositions(isStartGame); } float * cDMAudio::GetListenTimeArray() { return MusicManager.GetListenTimeArray(); } uint32 cDMAudio::GetFavouriteRadioStation() { return MusicManager.GetFavouriteRadioStation(); } int32 cDMAudio::GetRadioPosition(uint32 station) { return MusicManager.GetRadioPosition(station); } void cDMAudio::SetPedTalkingStatus(CPed *ped, uint8 status) { return AudioManager.SetPedTalkingStatus(ped, status); } void cDMAudio::SetPlayersMood(uint8 mood, uint32 time) { return AudioManager.SetPlayersMood(mood, time); } void cDMAudio::ShutUpPlayerTalking(uint8 state) { AudioManager.m_bIsPlayerShutUp = state; } ================================================ FILE: src/audio/DMAudio.h ================================================ #pragma once #include "audio_enums.h" #include "soundlist.h" #include "Crime.h" #define AEHANDLE_IS_FAILED(h) ((h)<0) #define AEHANDLE_IS_OK(h) ((h)>=0) #define NO_AUDIO_PROVIDER -3 #define AUDIO_PROVIDER_NOT_DETERMINED -99 class cAudioScriptObject; class CEntity; class cDMAudio { public: ~cDMAudio() { } void Initialise(void); void Terminate(void); void Service(void); int32 CreateEntity(eAudioType type, void *UID); void DestroyEntity(int32 audioEntity); void SetEntityStatus(int32 audioEntity, uint8 status); void PlayOneShot(int32 audioEntity, uint16 oneShot, float volume); void DestroyAllGameCreatedEntities(void); void SetMonoMode(uint8 mono); void SetMP3BoostVolume(uint8 volume); void SetEffectsMasterVolume(uint8 volume); void SetMusicMasterVolume(uint8 volume); void SetEffectsFadeVol(uint8 volume); void SetMusicFadeVol(uint8 volume); uint8 GetNum3DProvidersAvailable(void); char *Get3DProviderName(uint8 id); int8 AutoDetect3DProviders(void); int8 GetCurrent3DProviderIndex(void); int8 SetCurrent3DProvider(uint8 which); void SetSpeakerConfig(int32 config); bool IsMP3RadioChannelAvailable(void); void ReleaseDigitalHandle(void); void ReacquireDigitalHandle(void); void SetDynamicAcousticModelingStatus(uint8 status); bool CheckForAnAudioFileOnCD(void); char GetCDAudioDriveLetter(void); bool IsAudioInitialised(void); void ReportCrime(eCrimeType crime, CVector const &pos); int32 CreateLoopingScriptObject(cAudioScriptObject *scriptObject); void DestroyLoopingScriptObject(int32 audioEntity); void CreateOneShotScriptObject(cAudioScriptObject *scriptObject); void PlaySuspectLastSeen(float x, float y, float z); void ReportCollision(CEntity *entityA, CEntity *entityB, uint8 surfaceTypeA, uint8 surfaceTypeB, float collisionPower, float velocity); void PlayFrontEndSound(uint16 frontend, uint32 volume); void PlayRadioAnnouncement(uint32 announcement); void PlayFrontEndTrack(uint32 track, uint8 frontendFlag); void StopFrontEndTrack(void); void ResetTimers(uint32 time); void ChangeMusicMode(uint8 mode); void PreloadCutSceneMusic(uint32 track); void PlayPreloadedCutSceneMusic(void); void StopCutSceneMusic(void); void PreloadMissionAudio(uint8 slot, Const char *missionAudio); uint8 GetMissionAudioLoadingStatus(uint8 slot); void SetMissionAudioLocation(uint8 slot, float x, float y, float z); void PlayLoadedMissionAudio(uint8 slot); bool IsMissionAudioSampleFinished(uint8 slot); void ClearMissionAudio(uint8 slot); uint8 GetRadioInCar(void); void SetRadioInCar(uint32 radio); void SetRadioChannel(uint32 radio, int32 pos); void SetStartingTrackPositions(uint8 isStartGame); float *GetListenTimeArray(); uint32 GetFavouriteRadioStation(); int32 GetRadioPosition(uint32 station); void SetPedTalkingStatus(class CPed *ped, uint8 status); void SetPlayersMood(uint8 mood, uint32 time); void ShutUpPlayerTalking(uint8 state); }; extern cDMAudio DMAudio; ================================================ FILE: src/audio/MusicManager.cpp ================================================ #include "common.h" #include #include "soundlist.h" #include "MusicManager.h" #include "AudioManager.h" #include "ControllerConfig.h" #include "Camera.h" #include "Font.h" #include "Hud.h" #include "ModelIndices.h" #include "Replay.h" #include "Pad.h" #include "Text.h" #include "Timer.h" #include "World.h" #include "sampman.h" #include "Stats.h" #include "Script.h" #include "ZoneCull.h" #include "Weather.h" #include "DMAudio.h" #include "GenericGameStorage.h" #if !defined FIX_BUGS && (defined RADIO_SCROLL_TO_PREV_STATION || defined RADIO_OFF_TEXT) static_assert(false, "R*'s radio implementation is quite buggy, RADIO_SCROLL_TO_PREV_STATION and RADIO_OFF_TEXT won't work without FIX_BUGS"); #endif cMusicManager MusicManager; int32 gNumRetunePresses; int32 gRetuneCounter; bool g_bAnnouncementReadPosAlready; uint8 RadioStaticCounter = 5; uint32 RadioStaticTimer; CVector vecRiotPosition(300.7f, -322.0f, 12.0f); uint32 NewGameRadioTimers[10] = { 948160, 452150, 2438150, 3538230, 3513100, 4246050, 1418050, 3178240, 471210, 0 }; cMusicManager::cMusicManager() { m_bIsInitialised = false; m_bDisabled = false; m_nFrontendTrack = NO_TRACK; m_nPlayingTrack = NO_TRACK; m_nUpcomingMusicMode = MUSICMODE_DISABLED; m_nMusicMode = MUSICMODE_DISABLED; m_bSetNextStation = false; for (int i = 0; i < NUM_RADIOS; i++) aListenTimeArray[i] = 0.0f; m_nLastTrackServiceTime = 0.0f; m_nVolumeLatency = 0; m_nCurrentVolume = 0; m_nMaxVolume = 0; m_nAnnouncement = NO_TRACK; m_bAnnouncementInProgress = false; } void cMusicManager::ResetMusicAfterReload() { float afRadioTime[NUM_RADIOS]; m_bRadioSetByScript = false; m_nRadioStationScript = WILDSTYLE; m_nRadioPosition = -1; m_nAnnouncement = NO_TRACK; m_bAnnouncementInProgress = false; m_bSetNextStation = false; RadioStaticTimer = 0; gNumRetunePresses = 0; gRetuneCounter = 0; m_nFrontendTrack = NO_TRACK; m_nPlayingTrack = NO_TRACK; m_FrontendLoopFlag = false; m_bTrackChangeStarted = false; m_nNextTrack = NO_TRACK; m_nNextLoopFlag = false; m_bVerifyNextTrackStartedToPlay = false; m_bGameplayAllowsRadio = false; m_bRadioStreamReady = false; nFramesSinceCutsceneEnded = -1; m_bUserResumedGame = false; m_bMusicModeChangeStarted = false; m_bEarlyFrontendTrack = false; m_nVolumeLatency = 0; m_nCurrentVolume = 0; m_nMaxVolume = 0; bool bRadioWasEverListened = false; for (int i = 0; i < NUM_RADIOS; i++) { afRadioTime[i] = CStats::GetFavoriteRadioStationList(i); if (!bRadioWasEverListened && afRadioTime[i] != 0.0f) bRadioWasEverListened = true; } if (!bRadioWasEverListened) return; for (int i = 0; i < NUM_RADIOS; i++) { aListenTimeArray[i] = afRadioTime[i]; int32 trackPos = GetSavedRadioStationPosition(i); if (trackPos != -1) { if (trackPos > m_aTracks[i].m_nLength) { debug("Radio Track %d saved position is %d, Length is only %d\n", i, trackPos, m_aTracks[i].m_nLength); trackPos %= m_aTracks[i].m_nLength; } m_aTracks[i].m_nPosition = trackPos; m_aTracks[i].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); } } } void cMusicManager::SetStartingTrackPositions(uint8 isNewGameTimer) { int pos; if (IsInitialised()) { time_t timevalue = time(0); if (timevalue == -1) { pos = AudioManager.GetRandomNumber(0); } else { tm* pTm = localtime(&timevalue); if (pTm->tm_sec == 0) pTm->tm_sec = AudioManager.GetRandomNumber(0); if (pTm->tm_min == 0) pTm->tm_min = AudioManager.GetRandomNumber(1); if (pTm->tm_hour == 0) pTm->tm_hour = AudioManager.GetRandomNumber(2); if (pTm->tm_mday == 0) pTm->tm_mday = AudioManager.GetRandomNumber(3); if (pTm->tm_mon == 0) pTm->tm_mon = AudioManager.GetRandomNumber(4); if (pTm->tm_year == 0) pTm->tm_year = AudioManager.GetRandomNumber(3); if (pTm->tm_wday == 0) pTm->tm_wday = AudioManager.GetRandomNumber(2); pos = pTm->tm_yday * pTm->tm_wday * pTm->tm_year * pTm->tm_mon * pTm->tm_mday * pTm->tm_hour * pTm->tm_hour * pTm->tm_min * pTm->tm_min * pTm->tm_sec * pTm->tm_sec * pTm->tm_sec * pTm->tm_sec; } for (int i = 0; i < TOTAL_STREAMED_SOUNDS; i++) { m_aTracks[i].m_nLength = SampleManager.GetStreamedFileLength(i); if (i < STREAMED_SOUND_CITY_AMBIENT && isNewGameTimer) m_aTracks[i].m_nPosition = NewGameRadioTimers[i]; else if (i < STREAMED_SOUND_ANNOUNCE_BRIDGE_CLOSED) m_aTracks[i].m_nPosition = (pos * AudioManager.GetRandomNumber(i % 5)) % m_aTracks[i].m_nLength; else m_aTracks[i].m_nPosition = 0; m_aTracks[i].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); } } } bool cMusicManager::Initialise() { if (!IsInitialised()) { m_bIsInitialised = true; SetStartingTrackPositions(false); m_bResetTimers = false; m_nResetTime = 0; m_bRadioSetByScript = false; m_nRadioStationScript = WILDSTYLE; m_nRadioPosition = -1; m_nRadioInCar = NO_TRACK; gRetuneCounter = 0; gNumRetunePresses = 0; m_nFrontendTrack = NO_TRACK; m_nPlayingTrack = NO_TRACK; m_nUpcomingMusicMode = MUSICMODE_DISABLED; m_nMusicMode = MUSICMODE_DISABLED; m_FrontendLoopFlag = false; m_bTrackChangeStarted = false; m_nNextTrack = NO_TRACK; m_nNextLoopFlag = false; m_bVerifyNextTrackStartedToPlay = false; m_bGameplayAllowsRadio = false; m_bRadioStreamReady = false; nFramesSinceCutsceneEnded = -1; m_bUserResumedGame = false; m_bMusicModeChangeStarted = false; m_nMusicModeToBeSet = MUSICMODE_DISABLED; m_bEarlyFrontendTrack = false; m_nVolumeLatency = 0; m_nCurrentVolume = 0; m_nMaxVolume = 0; } return m_bIsInitialised; } void cMusicManager::Terminate() { if (!IsInitialised()) return; if (SampleManager.IsStreamPlaying(0)) { SampleManager.StopStreamedFile(0); m_nPlayingTrack = NO_TRACK; } m_bIsInitialised = false; } void cMusicManager::SetRadioChannelByScript(uint32 station, int32 pos) { if (m_bIsInitialised) { if (station == STREAMED_SOUND_RADIO_MP3_PLAYER) station = STREAMED_SOUND_CITY_AMBIENT; if (station <= STREAMED_SOUND_RADIO_POLICE) { m_bRadioSetByScript = true; m_nRadioStationScript = station; m_nRadioPosition = pos == -1 ? -1 : pos % m_aTracks[station].m_nLength; } } } bool cMusicManager::PlayerInCar() { CVehicle *vehicle = AudioManager.FindVehicleOfPlayer(); if(!vehicle) return false; int32 State = FindPlayerPed()->m_nPedState; if(State == PED_DRAG_FROM_CAR || State == PED_EXIT_CAR || State == PED_ARRESTED) return false; if (vehicle->GetStatus() == STATUS_WRECKED) return false; return true; } uint32 cMusicManager::GetRadioInCar(void) { if (!m_bIsInitialised) return WILDSTYLE; if (PlayerInCar()) { CVehicle* veh = AudioManager.FindVehicleOfPlayer(); if (veh != nil) { if (UsesPoliceRadio(veh) || UsesTaxiRadio(veh)) { if (m_nRadioInCar == NO_TRACK || (CReplay::IsPlayingBack() && AudioManager.m_nUserPause == 0)) return STREAMED_SOUND_RADIO_POLICE; return m_nRadioInCar; } else return veh->m_nRadioStation; } } if (m_nRadioInCar == NO_TRACK || (CReplay::IsPlayingBack() && AudioManager.m_nUserPause == 0)) return RADIO_OFF; return m_nRadioInCar; } void cMusicManager::SetRadioInCar(uint32 station) { if (m_bIsInitialised) { if (!PlayerInCar()) { m_nRadioInCar = station; return; } CVehicle* veh = AudioManager.FindVehicleOfPlayer(); if (veh == nil) return; if (UsesPoliceRadio(veh) || UsesTaxiRadio(veh)) m_nRadioInCar = station; else veh->m_nRadioStation = station; } } void cMusicManager::RecordRadioStats() { if (m_nPlayingTrack < NUM_RADIOS) { double time /*Rusty*/ = CTimer::GetTimeInMillisecondsPauseMode(); if (time > m_nLastTrackServiceTime) aListenTimeArray[m_nPlayingTrack] += time - m_nLastTrackServiceTime; } } void cMusicManager::ChangeMusicMode(uint8 mode) { if (!IsInitialised()) return; switch (mode) { case MUSICMODE_FRONTEND: m_nUpcomingMusicMode = MUSICMODE_FRONTEND; break; case MUSICMODE_GAME: m_nUpcomingMusicMode = MUSICMODE_GAME; break; case MUSICMODE_CUTSCENE: m_nUpcomingMusicMode = MUSICMODE_CUTSCENE; if (SampleManager.IsStreamPlaying(0)) { if (m_nPlayingTrack != NO_TRACK) { RecordRadioStats(); m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(0); m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); } } SampleManager.StopStreamedFile(0); while (SampleManager.IsStreamPlaying(0)) SampleManager.StopStreamedFile(0); m_nMusicMode = m_nUpcomingMusicMode; m_bMusicModeChangeStarted = false; m_bTrackChangeStarted = false; m_nNextTrack = NO_TRACK; m_nNextLoopFlag = false; m_bVerifyNextTrackStartedToPlay = false; m_nPlayingTrack = NO_TRACK; m_nFrontendTrack = NO_TRACK; m_bAnnouncementInProgress = false; m_nAnnouncement = NO_TRACK; g_bAnnouncementReadPosAlready = false; break; case MUSICMODE_DISABLE: m_nUpcomingMusicMode = MUSICMODE_DISABLED; break; default: return; } } void cMusicManager::ResetTimers(int32 time) { m_bResetTimers = true; m_nResetTime = time; } void cMusicManager::Service() { if (m_bResetTimers) { m_bResetTimers = false; m_nLastTrackServiceTime = m_nResetTime; } static bool bRadioStatsRecorded = false; if (!m_bIsInitialised || m_bDisabled) return; if (!m_bMusicModeChangeStarted) m_nMusicModeToBeSet = m_nUpcomingMusicMode; if (m_nMusicModeToBeSet == m_nMusicMode) { if (!AudioManager.m_nUserPause || AudioManager.m_nPreviousUserPause || m_nMusicMode != MUSICMODE_FRONTEND) { switch (m_nMusicMode) { case MUSICMODE_FRONTEND: ServiceFrontEndMode(); break; case MUSICMODE_GAME: ServiceGameMode(); break; case MUSICMODE_CUTSCENE: SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, 1, 0); break; } } else m_nMusicMode = MUSICMODE_DISABLED; } else { m_bMusicModeChangeStarted = true; if (!m_bUserResumedGame && !AudioManager.m_nUserPause && AudioManager.m_nPreviousUserPause) m_bUserResumedGame = true; if (AudioManager.m_FrameCounter % 4 == 0) { gNumRetunePresses = 0; gRetuneCounter = 0; m_bSetNextStation = false; if (SampleManager.IsStreamPlaying(0)) { if (m_nPlayingTrack != NO_TRACK && !bRadioStatsRecorded) { RecordRadioStats(); m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(0); m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); bRadioStatsRecorded = true; } SampleManager.StopStreamedFile(0); } else { bRadioStatsRecorded = false; m_nMusicMode = m_nMusicModeToBeSet; m_bMusicModeChangeStarted = false; m_bTrackChangeStarted = false; m_nNextTrack = NO_TRACK; m_nNextLoopFlag = false; m_bVerifyNextTrackStartedToPlay = false; m_nPlayingTrack = NO_TRACK; if (m_bEarlyFrontendTrack) m_bEarlyFrontendTrack = false; else m_nFrontendTrack = NO_TRACK; } } } } void cMusicManager::ServiceFrontEndMode() { static bool bRadioStatsRecorded = false; if (m_bAnnouncementInProgress) { SampleManager.StopStreamedFile(0); if (SampleManager.IsStreamPlaying(0)) return; g_bAnnouncementReadPosAlready = false; m_nAnnouncement = NO_TRACK; m_bAnnouncementInProgress = false; m_nNextTrack = NO_TRACK; m_nFrontendTrack = NO_TRACK; m_nPlayingTrack = NO_TRACK; } if (AudioManager.m_FrameCounter % 4 != 0) return; if (!m_bTrackChangeStarted && !m_bVerifyNextTrackStartedToPlay) { m_nNextTrack = m_nFrontendTrack; m_nNextLoopFlag = m_FrontendLoopFlag; } if (m_nNextTrack == m_nPlayingTrack) { if (SampleManager.IsStreamPlaying(0)) { if (m_nVolumeLatency > 0) m_nVolumeLatency--; else { if (m_nCurrentVolume < m_nMaxVolume) m_nCurrentVolume = Min(m_nMaxVolume, m_nCurrentVolume + 6); SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, 0, 0); } } else { if (m_nPlayingTrack == STREAMED_SOUND_RADIO_MP3_PLAYER) SampleManager.StartStreamedFile(STREAMED_SOUND_RADIO_MP3_PLAYER, 0, 0); else if (m_nPlayingTrack == STREAMED_SOUND_MISSION_COMPLETED && AudioManager.m_nUserPause == 0) ChangeMusicMode(MUSICMODE_GAME); } } else { m_bTrackChangeStarted = true; if (m_bVerifyNextTrackStartedToPlay || !SampleManager.IsStreamPlaying(0)) { bRadioStatsRecorded = false; if (SampleManager.IsStreamPlaying(0) || m_nNextTrack == NO_TRACK) { m_nPlayingTrack = m_nNextTrack; m_bVerifyNextTrackStartedToPlay = false; m_bTrackChangeStarted = false; } else { uint32 trackStartPos = (m_nNextTrack > STREAMED_SOUND_RADIO_POLICE) ? 0 : GetTrackStartPos(m_nNextTrack); if (m_nNextTrack != NO_TRACK) { SampleManager.SetStreamedFileLoopFlag(m_nNextLoopFlag, 0); SampleManager.StartStreamedFile(m_nNextTrack, trackStartPos, 0); m_nVolumeLatency = 3; m_nCurrentVolume = 0; m_nMaxVolume = 100; SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, 0, 0); if (m_nNextTrack < STREAMED_SOUND_CITY_AMBIENT) m_nLastTrackServiceTime = CTimer::GetTimeInMillisecondsPauseMode(); m_bVerifyNextTrackStartedToPlay = true; } } } else { if (m_nPlayingTrack != NO_TRACK && !bRadioStatsRecorded) { m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(0); m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); RecordRadioStats(); bRadioStatsRecorded = true; } SampleManager.SetStreamedVolumeAndPan(0, 63, 0, 0); SampleManager.StopStreamedFile(0); } } } void cMusicManager::ServiceGameMode() { CPed *ped = FindPlayerPed(); CVehicle *vehicle = AudioManager.FindVehicleOfPlayer(); m_bRadioStreamReady = m_bGameplayAllowsRadio; m_bGameplayAllowsRadio = false; switch (CGame::currArea) { case AREA_HOTEL: case AREA_MALL: case AREA_STRIP_CLUB: case AREA_DIRT: case AREA_BLOOD: case AREA_OVALRING: case AREA_MALIBU_CLUB: m_bGameplayAllowsRadio = false; break; default: if (SampleManager.GetMusicVolume()) { if (PlayerInCar()) m_bGameplayAllowsRadio = true; } else m_bGameplayAllowsRadio = false; break; } if (!m_bGameplayAllowsRadio) { nFramesSinceCutsceneEnded = -1; gNumRetunePresses = 0; gRetuneCounter = 0; m_bSetNextStation = false; } else if (ped) { if(!ped->DyingOrDead() && vehicle) { #ifdef GTA_PC if (SampleManager.IsMP3RadioChannelAvailable() && vehicle->m_nRadioStation < USERTRACK && ControlsManager.GetIsKeyboardKeyJustDown(rsF9)) { if (!UsesPoliceRadio(vehicle) && !UsesTaxiRadio(vehicle)) { gNumRetunePresses = 0; gRetuneCounter = 20; RadioStaticCounter = 0; if (vehicle->m_nRadioStation < USERTRACK) { do ++gNumRetunePresses; while (gNumRetunePresses + vehicle->m_nRadioStation < USERTRACK); } } } #endif if (CPad::GetPad(0)->ChangeStationJustDown()) { if (!UsesPoliceRadio(vehicle) && !UsesTaxiRadio(vehicle)) { gNumRetunePresses++; gRetuneCounter = 20; RadioStaticCounter = 0; } } #ifdef RADIO_SCROLL_TO_PREV_STATION else if(CPad::GetPad(0)->GetMouseWheelDownJustDown() || CPad::GetPad(0)->GetMouseWheelUpJustDown()) { if(!UsesPoliceRadio(vehicle) && !UsesTaxiRadio(vehicle)) { int scrollNext = ControlsManager.GetControllerKeyAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, MOUSE); int scrollPrev = scrollNext == rsMOUSEWHEELUPBUTTON ? rsMOUSEWHEELDOWNBUTTON : scrollNext == rsMOUSEWHEELDOWNBUTTON ? rsMOUSEWHEELUPBUTTON : -1; if(scrollPrev != -1 && !ControlsManager.IsAnyVehicleActionAssignedToMouseKey(scrollPrev)) { gNumRetunePresses--; gRetuneCounter = 20; RadioStaticCounter = 0; int track = gNumRetunePresses + vehicle->m_nRadioStation; while(track < 0) track += NUM_RADIOS + 1; while(track >= NUM_RADIOS + 1) track -= NUM_RADIOS + 1; if(!DMAudio.IsMP3RadioChannelAvailable() && track == USERTRACK) gNumRetunePresses--; } } } #endif } } if (m_bUserResumedGame) { m_bRadioStreamReady = false; m_bUserResumedGame = false; } if (m_nPlayingTrack == NO_TRACK && m_nFrontendTrack == NO_TRACK) m_bRadioStreamReady = false; if (m_bGameplayAllowsRadio) { if (!m_bRadioStreamReady) { if(vehicle == nil) { m_nFrontendTrack = STREAMED_SOUND_RADIO_WAVE; // huh? return; } if(m_bRadioSetByScript) { if(UsesPoliceRadio(vehicle)) m_nFrontendTrack = STREAMED_SOUND_RADIO_POLICE; else if(UsesTaxiRadio(vehicle)) m_nFrontendTrack = STREAMED_SOUND_RADIO_TAXI; else { m_nFrontendTrack = m_nRadioStationScript; vehicle->m_nRadioStation = m_nRadioStationScript; } if(m_nRadioPosition != -1) { m_aTracks[m_nFrontendTrack].m_nPosition = m_nRadioPosition; m_aTracks[m_nFrontendTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); } m_bRadioSetByScript = false; return; } // This starts the radio when you enter the car. m_nFrontendTrack = GetCarTuning(); return; } if (m_nAnnouncement < NO_TRACK) { if ((m_bAnnouncementInProgress || m_nFrontendTrack == m_nPlayingTrack) && ServiceAnnouncement()) { if (m_bAnnouncementInProgress) { m_bSetNextStation = false; gNumRetunePresses = 0; gRetuneCounter = 0; return; } if(m_nAnnouncement == NO_TRACK) { m_nNextTrack = NO_TRACK; m_nFrontendTrack = GetCarTuning(); m_bSetNextStation = false; gRetuneCounter = 0; gNumRetunePresses = 0; } } } if (!m_bAnnouncementInProgress && m_nAnnouncement == NO_TRACK && m_nPlayingTrack == STREAMED_SOUND_RADIO_MP3_PLAYER && !SampleManager.IsStreamPlaying(0)) { SampleManager.StartStreamedFile(STREAMED_SOUND_RADIO_MP3_PLAYER, 0, 0); } if (!m_bRadioSetByScript) { // Because when you switch radio back and forth, gNumRetunePresses will be 0 but gRetuneCounter won't. #ifdef RADIO_SCROLL_TO_PREV_STATION if(gRetuneCounter != 0) { if(gRetuneCounter > 1) gRetuneCounter--; else if(gRetuneCounter == 1) { m_bSetNextStation = true; gRetuneCounter = 0; } } #else if (gNumRetunePresses != 0) { if (--gRetuneCounter == 0) { m_bSetNextStation = true; gRetuneCounter = 0; } } #endif if (gRetuneCounter) { int32 station = gNumRetunePresses + vehicle->m_nRadioStation; #ifdef RADIO_SCROLL_TO_PREV_STATION while (station < 0) station += NUM_RADIOS + 1; #endif while (station >= NUM_RADIOS + 1) station -= NUM_RADIOS + 1; // Scrolling back won't hit here, so increasing isn't problem if (!DMAudio.IsMP3RadioChannelAvailable() && station == USERTRACK) { ++gNumRetunePresses; station = RADIO_OFF; } if (station == RADIO_OFF) { if (gRetuneCounter == 19) // One less then what switching radio sets, so runs right after turning off radio { AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_RADIO_TURN_OFF, 0.0f); RadioStaticCounter = 5; } } else { #ifdef RADIO_SCROLL_TO_PREV_STATION if (vehicle->m_nRadioStation == RADIO_OFF && gRetuneCounter == 19) // Right after turning on the radio #else if (station == 0 && gRetuneCounter == 19) // Right after turning on the radio #endif AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_RADIO_TURN_ON, 0.0f); AudioManager.DoPoliceRadioCrackle(); } } if (RadioStaticCounter < 2 && CTimer::GetTimeInMilliseconds() > RadioStaticTimer + 800) { AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_RADIO_CHANGE, 0.0f); RadioStaticCounter++; RadioStaticTimer = CTimer::GetTimeInMilliseconds(); } if (m_bSetNextStation) m_nFrontendTrack = GetNextCarTuning(); if (m_nFrontendTrack >= STREAMED_SOUND_CITY_AMBIENT && m_nFrontendTrack <= STREAMED_SOUND_AMBSIL_AMBIENT) SetUpCorrectAmbienceTrack(); ServiceTrack(vehicle, ped); if (m_bSetNextStation) m_bSetNextStation = false; return; } if (UsesPoliceRadio(vehicle)) m_nFrontendTrack = STREAMED_SOUND_RADIO_POLICE; else if (UsesTaxiRadio(vehicle)) m_nFrontendTrack = STREAMED_SOUND_RADIO_TAXI; else { m_nFrontendTrack = m_nRadioStationScript; vehicle->m_nRadioStation = m_nRadioStationScript; } if (m_nRadioPosition != -1) { m_aTracks[m_nFrontendTrack].m_nPosition = m_nRadioPosition; m_aTracks[m_nFrontendTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); } gRetuneCounter = 0; gNumRetunePresses = 0; m_bSetNextStation = false; m_bRadioSetByScript = false; if (m_nFrontendTrack >= STREAMED_SOUND_CITY_AMBIENT && m_nFrontendTrack <= STREAMED_SOUND_AMBSIL_AMBIENT) SetUpCorrectAmbienceTrack(); ServiceTrack(vehicle, ped); if (m_bSetNextStation) m_bSetNextStation = false; return; } if (m_bAnnouncementInProgress) { SampleManager.StopStreamedFile(0); if (SampleManager.IsStreamPlaying(0)) return; g_bAnnouncementReadPosAlready = false; m_nAnnouncement = NO_TRACK; m_bAnnouncementInProgress = false; m_nNextTrack = NO_TRACK; m_nFrontendTrack = NO_TRACK; m_nPlayingTrack = NO_TRACK; } SetUpCorrectAmbienceTrack(); ServiceTrack(nil, ped); } void cMusicManager::SetUpCorrectAmbienceTrack() { switch (CGame::currArea) { case AREA_MAIN_MAP: case AREA_EVERYWHERE: if (CTheScripts::RiotIntensity != 0 && ((TheCamera.GetPosition() - vecRiotPosition).MagnitudeSqr() < SQR(65.0f))) m_nFrontendTrack = STREAMED_SOUND_LAW4RIOT_AMBIENT; else if (TheCamera.DistanceToWater <= 90.0f) { if (CCullZones::bAtBeachForAudio) { if (CWeather::OldWeatherType != WEATHER_HURRICANE && CWeather::NewWeatherType != WEATHER_HURRICANE || CWeather::Wind <= 1.0f) m_nFrontendTrack = STREAMED_SOUND_BEACH_AMBIENT; else m_nFrontendTrack = STREAMED_SOUND_HAVANA_BEACH_AMBIENT; } else if (CWeather::OldWeatherType != WEATHER_HURRICANE && CWeather::NewWeatherType != WEATHER_HURRICANE || CWeather::Wind <= 1.0f) m_nFrontendTrack = STREAMED_SOUND_WATER_AMBIENT; else m_nFrontendTrack = STREAMED_SOUND_HAVANA_WATER_AMBIENT; } else if (CWeather::OldWeatherType != WEATHER_HURRICANE && CWeather::NewWeatherType != WEATHER_HURRICANE || CWeather::Wind <= 1.0f) m_nFrontendTrack = STREAMED_SOUND_CITY_AMBIENT; else m_nFrontendTrack = STREAMED_SOUND_HAVANA_CITY_AMBIENT; break; case AREA_HOTEL: m_nFrontendTrack = STREAMED_SOUND_HOTEL_AMBIENT; break; case AREA_MALL: m_nFrontendTrack = STREAMED_SOUND_MALL_AMBIENT; break; case AREA_STRIP_CLUB: m_nFrontendTrack = STREAMED_SOUND_STRIPCLUB_AMBIENT; break; case AREA_DIRT: case AREA_BLOOD: case AREA_OVALRING: m_nFrontendTrack = STREAMED_SOUND_DIRTRING_AMBIENT; break; case AREA_MALIBU_CLUB: m_nFrontendTrack = STREAMED_SOUND_MALIBU_AMBIENT; break; case AREA_MANSION: case AREA_BANK: case AREA_LAWYERS: case AREA_COFFEE_SHOP: case AREA_CONCERT_HALL: case AREA_STUDIO: case AREA_RIFLE_RANGE: case AREA_BIKER_BAR: case AREA_POLICE_STATION: m_nFrontendTrack = STREAMED_SOUND_AMBSIL_AMBIENT; break; } } float GetHeightScale() { if (TheCamera.GetPosition().z > 20.0f) { if (TheCamera.GetPosition().z < 50.0f) return 1.0f - (TheCamera.GetPosition().z - 20.0f) / 30.0f; return 0.0f; } return 1.0f; } void cMusicManager::ComputeAmbienceVol(uint8 reset, uint8& outVolume) { static float fVol = 0.0f; float fHeightScale = GetHeightScale(); if (CTheScripts::RiotIntensity > 0) { float distToRiotSq = (TheCamera.GetPosition() - vecRiotPosition).MagnitudeSqr(); if (distToRiotSq < SQR(100.0f)) { if (distToRiotSq >= SQR(65.0f)) outVolume = (Sqrt(distToRiotSq) - 65.0f) / 35.0f * (127.0f * fHeightScale); else if (distToRiotSq >= SQR(20.0f)) outVolume = (CTheScripts::RiotIntensity * (1.0f - (Sqrt(distToRiotSq) - 20.0f) / 45.0f) * (127.0f * fHeightScale)) / MAX_VOLUME; else outVolume = (CTheScripts::RiotIntensity * (127.0f * fHeightScale)) / MAX_VOLUME; return; } } if (reset) fVol = 0.0f; else if (fVol < 60.0f) { if ((m_nPlayingTrack >= STREAMED_SOUND_HAVANA_CITY_AMBIENT) && (m_nPlayingTrack <= STREAMED_SOUND_HAVANA_BEACH_AMBIENT)) fVol += 20.0f; else fVol += 1.0f; fVol = Min(fVol, 60.0f); } if ((m_nPlayingTrack >= STREAMED_SOUND_MALL_AMBIENT) && (m_nPlayingTrack <= STREAMED_SOUND_AMBSIL_AMBIENT)) { outVolume = fVol; return; } if (CWeather::OldWeatherType == WEATHER_HURRICANE || CWeather::NewWeatherType == WEATHER_HURRICANE) { if (CWeather::Wind > 1.0f) { outVolume = (CWeather::Wind - 1.0f) * fVol; return; } fVol = (1.0f - CWeather::Wind) * fVol; } if (TheCamera.DistanceToWater > 140.0f) { outVolume = fVol; return; } if (TheCamera.DistanceToWater > 90.0f) { outVolume = ((TheCamera.DistanceToWater - 90.0f) / 50.0f * fVol * fHeightScale); return; } if (TheCamera.DistanceToWater > 40.0f) { outVolume = fVol; return; } outVolume = (90.0f - fHeightScale) / 50.0f * fVol; } bool cMusicManager::ServiceAnnouncement() { if (m_bAnnouncementInProgress) { if (SampleManager.IsStreamPlaying(0)) m_nPlayingTrack = m_nNextTrack; else if (m_nPlayingTrack != NO_TRACK) { m_nAnnouncement = NO_TRACK; m_bAnnouncementInProgress = false; m_nPlayingTrack = NO_TRACK; } return true; } else if (SampleManager.IsStreamPlaying(0)) { if (m_nPlayingTrack != NO_TRACK && !g_bAnnouncementReadPosAlready) { RecordRadioStats(); m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(0); g_bAnnouncementReadPosAlready = true; m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); } SampleManager.StopStreamedFile(0); } else { g_bAnnouncementReadPosAlready = false; m_nPlayingTrack = NO_TRACK; m_nNextTrack = m_nAnnouncement; SampleManager.SetStreamedFileLoopFlag(0, 0); SampleManager.StartStreamedFile(m_nNextTrack, 0, 0); SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, 0, 0); m_bAnnouncementInProgress = true; } return true; } void cMusicManager::ServiceTrack(CVehicle *veh, CPed *ped) { static bool bRadioStatsRecorded = false; static bool bRadioStatsRecorded2 = false; uint8 volume; if (!m_bTrackChangeStarted) m_nNextTrack = m_nFrontendTrack; if (gRetuneCounter != 0 || m_bSetNextStation) { if (SampleManager.IsStreamPlaying(0)) { if (m_nPlayingTrack != NO_TRACK && !bRadioStatsRecorded) { m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(0); m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); RecordRadioStats(); bRadioStatsRecorded = true; } SampleManager.SetStreamedVolumeAndPan(0, 63, 0, 0); SampleManager.StopStreamedFile(0); } return; } if (bRadioStatsRecorded) { bRadioStatsRecorded = false; m_nPlayingTrack = NO_TRACK; } if (m_nNextTrack != m_nPlayingTrack) { m_bTrackChangeStarted = true; SampleManager.SetStreamedVolumeAndPan(0, 63, 0, 0); if (!(AudioManager.m_FrameCounter & 1)) { if (m_bVerifyNextTrackStartedToPlay || !SampleManager.IsStreamPlaying(0)) { bRadioStatsRecorded2 = false; if (SampleManager.IsStreamPlaying(0)) { m_nPlayingTrack = m_nNextTrack; m_bVerifyNextTrackStartedToPlay = false; m_bTrackChangeStarted = false; if (veh) { #ifdef FIX_BUGS if (m_nPlayingTrack >= STREAMED_SOUND_CITY_AMBIENT && m_nPlayingTrack <= STREAMED_SOUND_AMBSIL_AMBIENT) veh->m_nRadioStation = RADIO_OFF; else if (m_nPlayingTrack < STREAMED_SOUND_CITY_AMBIENT) veh->m_nRadioStation = m_nPlayingTrack; #else if (veh->m_nRadioStation >= STREAMED_SOUND_CITY_AMBIENT && veh->m_nRadioStation <= STREAMED_SOUND_AMBSIL_AMBIENT) veh->m_nRadioStation = RADIO_OFF; else veh->m_nRadioStation = m_nPlayingTrack; #endif } } else { uint32 pos = GetTrackStartPos(m_nNextTrack); if (m_nNextTrack != NO_TRACK) { SampleManager.SetStreamedFileLoopFlag(1, 0); SampleManager.StartStreamedFile(m_nNextTrack, pos, 0); if (m_nFrontendTrack < STREAMED_SOUND_CITY_AMBIENT || m_nFrontendTrack > STREAMED_SOUND_AMBSIL_AMBIENT) { m_nVolumeLatency = 10; m_nCurrentVolume = 0; m_nMaxVolume = 100; SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, 0, 0); } else { ComputeAmbienceVol(true, volume); SampleManager.SetStreamedVolumeAndPan(volume, 63, 1, 0); } if (m_nNextTrack < STREAMED_SOUND_CITY_AMBIENT) m_nLastTrackServiceTime = CTimer::GetTimeInMillisecondsPauseMode(); m_bVerifyNextTrackStartedToPlay = true; } } } else { if (m_nPlayingTrack == NO_TRACK) debug("m_nPlayingTrack == NO_TRACK, yet track playing - tidying up\n"); else if (!bRadioStatsRecorded2) { m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition(0); m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); bRadioStatsRecorded2 = true; RecordRadioStats(); if (m_nPlayingTrack >= STREAMED_SOUND_HAVANA_CITY_AMBIENT && m_nPlayingTrack <= STREAMED_SOUND_HAVANA_BEACH_AMBIENT) { if (m_nNextTrack >= STREAMED_SOUND_HAVANA_CITY_AMBIENT && m_nNextTrack <= STREAMED_SOUND_HAVANA_BEACH_AMBIENT) AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_HURRICANE, 0.0); } } SampleManager.SetStreamedVolumeAndPan(0, 63, 0, 0); SampleManager.StopStreamedFile(0); } } return; } if (m_nPlayingTrack >= STREAMED_SOUND_CITY_AMBIENT && m_nPlayingTrack <= STREAMED_SOUND_AMBSIL_AMBIENT) { ComputeAmbienceVol(false, volume); SampleManager.SetStreamedVolumeAndPan(volume, 63, 1, 0); return; } if (CTimer::GetIsSlowMotionActive()) { if (TheCamera.pTargetEntity) { float DistToTargetSq = (TheCamera.pTargetEntity->GetPosition() - TheCamera.GetPosition()).MagnitudeSqr(); if (DistToTargetSq >= SQR(55.0f)) { SampleManager.SetStreamedVolumeAndPan(0, 63, 0, 0); } else if (DistToTargetSq >= SQR(10.0f)) { volume = (45.0f - (Sqrt(DistToTargetSq) - 10.0f)) / 45.0f * m_nCurrentVolume; if (AudioManager.ShouldDuckMissionAudio(0) || AudioManager.ShouldDuckMissionAudio(1)) volume /= 4; uint8 pan = 0; if (volume > 0) { CVector panVec; AudioManager.TranslateEntity(&TheCamera.pTargetEntity->GetPosition(), &panVec); pan = AudioManager.ComputePan(55.0f, &panVec); } if (gRetuneCounter != 0) volume = 0; SampleManager.SetStreamedVolumeAndPan(volume, pan, 0, 0); } else if (AudioManager.ShouldDuckMissionAudio(0) || AudioManager.ShouldDuckMissionAudio(1)) SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, 0, 0); else if (gRetuneCounter != 0) SampleManager.SetStreamedVolumeAndPan(0, 63, 0, 0); else SampleManager.SetStreamedVolumeAndPan(m_nCurrentVolume, 63, 0, 0); } } else if (AudioManager.ShouldDuckMissionAudio(0) || AudioManager.ShouldDuckMissionAudio(1)) { SampleManager.SetStreamedVolumeAndPan(Min(m_nCurrentVolume, 25), 63, 0, 0); nFramesSinceCutsceneEnded = 0; } else { if (nFramesSinceCutsceneEnded == -1) volume = m_nCurrentVolume; else if (nFramesSinceCutsceneEnded < 20) { volume = Min(m_nCurrentVolume, 25); nFramesSinceCutsceneEnded++; } else if (nFramesSinceCutsceneEnded < 40) { volume = Min(m_nCurrentVolume, 3 * (nFramesSinceCutsceneEnded - 20) + 25); nFramesSinceCutsceneEnded++; } else { volume = m_nCurrentVolume; nFramesSinceCutsceneEnded = -1; } if (gRetuneCounter != 0) volume = 0; SampleManager.SetStreamedVolumeAndPan(volume, 63, 0, 0); } if (m_nVolumeLatency > 0) m_nVolumeLatency--; else if (m_nCurrentVolume < m_nMaxVolume) m_nCurrentVolume = Min(m_nMaxVolume, m_nCurrentVolume + 6); } void cMusicManager::PreloadCutSceneMusic(uint32 track) { if (IsInitialised() && !m_bDisabled && track < TOTAL_STREAMED_SOUNDS && m_nMusicMode == MUSICMODE_CUTSCENE) { AudioManager.ResetPoliceRadio(); while (SampleManager.IsStreamPlaying(0)) SampleManager.StopStreamedFile(0); SampleManager.PreloadStreamedFile(track, 0); SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, 1, 0); m_nPlayingTrack = track; } } void cMusicManager::PlayPreloadedCutSceneMusic(void) { if (IsInitialised() && !m_bDisabled && m_nMusicMode == MUSICMODE_CUTSCENE) SampleManager.StartPreloadedStreamedFile(0); } void cMusicManager::StopCutSceneMusic(void) { if (IsInitialised() && !m_bDisabled && m_nMusicMode == MUSICMODE_CUTSCENE) { SampleManager.StopStreamedFile(0); m_nPlayingTrack = NO_TRACK; } } void cMusicManager::PlayFrontEndTrack(uint32 track, uint8 loopFlag) { if (IsInitialised() && !m_bDisabled && track < TOTAL_STREAMED_SOUNDS && (m_nUpcomingMusicMode == MUSICMODE_FRONTEND || m_nMusicMode == MUSICMODE_FRONTEND)) { m_nFrontendTrack = track; m_FrontendLoopFlag = loopFlag; if (m_nMusicMode != MUSICMODE_FRONTEND) m_bEarlyFrontendTrack = true; } } void cMusicManager::StopFrontEndTrack() { if (m_nUpcomingMusicMode == MUSICMODE_FRONTEND || m_nMusicMode == MUSICMODE_FRONTEND) m_nFrontendTrack = NO_TRACK; } void cMusicManager::PlayAnnouncement(uint32 announcement) { if (IsInitialised() && !m_bDisabled && !m_bAnnouncementInProgress) m_nAnnouncement = announcement; } uint32 cMusicManager::GetNextCarTuning() { CVehicle *veh = AudioManager.FindVehicleOfPlayer(); if (veh == nil) return STREAMED_SOUND_CITY_AMBIENT; if (UsesPoliceRadio(veh)) return STREAMED_SOUND_RADIO_POLICE; if (UsesTaxiRadio(veh)) return STREAMED_SOUND_RADIO_TAXI; if (gNumRetunePresses != 0) { #ifdef RADIO_SCROLL_TO_PREV_STATION // m_nRadioStation is unsigned, so... int station = veh->m_nRadioStation + gNumRetunePresses; while(station < 0) station += NUM_RADIOS + 1; while(station >= NUM_RADIOS + 1) station -= NUM_RADIOS + 1; veh->m_nRadioStation = station; #else veh->m_nRadioStation += gNumRetunePresses; while(veh->m_nRadioStation >= NUM_RADIOS + 1) veh->m_nRadioStation -= NUM_RADIOS + 1; #endif DMAudio.IsMP3RadioChannelAvailable(); // woof, just call and do nothing =P they manipulate gNumRetunePresses on DisplayRadioStationName in this case gNumRetunePresses = 0; } return veh->m_nRadioStation; } uint32 cMusicManager::GetCarTuning() { CVehicle* veh = AudioManager.FindVehicleOfPlayer(); if (veh == nil) return STREAMED_SOUND_CITY_AMBIENT; if (UsesPoliceRadio(veh)) return STREAMED_SOUND_RADIO_POLICE; if (UsesTaxiRadio(veh)) return STREAMED_SOUND_RADIO_TAXI; if (veh->m_nRadioStation == USERTRACK && !SampleManager.IsMP3RadioChannelAvailable()) veh->m_nRadioStation = AudioManager.GetRandomNumber(2) % USERTRACK; return veh->m_nRadioStation; } float* cMusicManager::GetListenTimeArray() { return aListenTimeArray; } uint32 cMusicManager::GetTrackStartPos(uint32 track) { if (!IsInitialised()) return 0; uint32 pos = m_aTracks[track].m_nPosition; if (CTimer::GetTimeInMillisecondsPauseMode() > m_aTracks[track].m_nLastPosCheckTimer) pos += Min(CTimer::GetTimeInMillisecondsPauseMode() - m_aTracks[track].m_nLastPosCheckTimer, 270000); else m_aTracks[track].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); if (pos > m_aTracks[track].m_nLength) pos %= m_aTracks[track].m_nLength; return pos; } uint32 cMusicManager::GetRadioPosition(uint32 station) { if (station < NUM_RADIOS) return GetTrackStartPos(station); return 0; } uint32 cMusicManager::GetFavouriteRadioStation() { uint32 favstation = 0; for (int i = 1; i < NUM_RADIOS; i++) { if (aListenTimeArray[i] > aListenTimeArray[favstation]) favstation = i; } return favstation; } bool cMusicManager::CheckForMusicInterruptions() { return (m_nPlayingTrack == STREAMED_SOUND_MISSION_COMPLETED) || (m_nPlayingTrack == STREAMED_SOUND_CUTSCENE_FINALE); } void cMusicManager::SetMalibuClubTrackPos(uint8 scriptObject) { if (!IsInitialised()) m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nPosition = 8640; if (m_nNextTrack != STREAMED_SOUND_MALIBU_AMBIENT && m_nPlayingTrack != STREAMED_SOUND_MALIBU_AMBIENT) { switch (scriptObject) { case SCRIPT_SOUND_NEW_BUILDING_MALIBU_1: m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 8640; break; case SCRIPT_SOUND_NEW_BUILDING_MALIBU_2: m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 286720; break; case SCRIPT_SOUND_NEW_BUILDING_MALIBU_3: m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 509120; break; } m_aTracks[STREAMED_SOUND_MALIBU_AMBIENT].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); } } void cMusicManager::SetStripClubTrackPos(uint8 scriptObject) { if (!IsInitialised()) m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nPosition = 0; if (m_nNextTrack != STREAMED_SOUND_STRIPCLUB_AMBIENT && m_nPlayingTrack != STREAMED_SOUND_STRIPCLUB_AMBIENT) { switch (scriptObject) { case SCRIPT_SOUND_NEW_BUILDING_STRIP_1: m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nPosition = AudioManager.m_anRandomTable[0] % 128; break; case SCRIPT_SOUND_NEW_BUILDING_STRIP_2: m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 320200; break; case SCRIPT_SOUND_NEW_BUILDING_STRIP_3: m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nPosition = (AudioManager.m_anRandomTable[0] % 128) + 672000; break; } m_aTracks[STREAMED_SOUND_STRIPCLUB_AMBIENT].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode(); } } void cMusicManager::DisplayRadioStationName() { uint8 gStreamedSound; static wchar *pCurrentStation = nil; static uint8 cDisplay = 0; if(!CTimer::GetIsPaused() && !TheCamera.m_WideScreenOn && PlayerInCar() && !CReplay::IsPlayingBack()) { CVehicle *vehicle = AudioManager.FindVehicleOfPlayer(); if (vehicle) { #if defined RADIO_SCROLL_TO_PREV_STATION || defined FIX_BUGS // Because m_nFrontendTrack can have NO_TRACK int track; #else uint8 track; #endif gStreamedSound = vehicle->m_nRadioStation; if (gStreamedSound >= STREAMED_SOUND_CITY_AMBIENT && gStreamedSound <= STREAMED_SOUND_AMBSIL_AMBIENT) gStreamedSound = RADIO_OFF; if (gNumRetunePresses != 0) { track = gNumRetunePresses + gStreamedSound; #ifdef RADIO_SCROLL_TO_PREV_STATION while (track < 0) track += NUM_RADIOS + 1; #endif while (track >= NUM_RADIOS + 1) track -= NUM_RADIOS + 1; // We already handle this condition while scrolling back, on key press. No need to change this. if (!DMAudio.IsMP3RadioChannelAvailable() && track == USERTRACK) gNumRetunePresses++; } else track = m_nFrontendTrack; wchar* string = nil; switch (track) { case WILDSTYLE: string = TheText.Get("FEA_FM0"); break; case FLASH_FM: string = TheText.Get("FEA_FM1"); break; case KCHAT: string = TheText.Get("FEA_FM2"); break; case FEVER: string = TheText.Get("FEA_FM3"); break; case V_ROCK: string = TheText.Get("FEA_FM4"); break; case VCPR: string = TheText.Get("FEA_FM5"); break; case RADIO_ESPANTOSO: string = TheText.Get("FEA_FM6"); break; case EMOTION: string = TheText.Get("FEA_FM7"); break; case WAVE: string = TheText.Get("FEA_FM8"); break; case USERTRACK: if (!SampleManager.IsMP3RadioChannelAvailable()) return; string = TheText.Get("FEA_MP3"); break; #ifdef RADIO_OFF_TEXT case STREAMED_SOUND_RADIO_POLICE: case STREAMED_SOUND_RADIO_TAXI: return; default: { // Otherwise pausing-resuming game will show RADIO OFF, since radio isn't set yet if (m_nPlayingTrack == NO_TRACK && m_nFrontendTrack == NO_TRACK) return; extern wchar WideErrorString[]; string = TheText.Get("FEA_NON"); if (string == WideErrorString) { pCurrentStation = nil; return; } break; } #else default: return; #endif }; if (pCurrentStation != string) { pCurrentStation = string; cDisplay = 60; } else { if (cDisplay == 0) return; cDisplay--; } CFont::SetJustifyOff(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(0.8f), SCREEN_SCALE_Y(1.35f)); CFont::SetPropOn(); CFont::SetFontStyle(FONT_STANDARD); CFont::SetCentreOn(); CFont::SetCentreSize(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString(SCREEN_WIDTH / 2 + SCREEN_SCALE_X(2.0f), SCREEN_SCALE_Y(22.0f) + SCREEN_SCALE_Y(2.0f), pCurrentStation); if (gNumRetunePresses) CFont::SetColor(CRGBA(102, 133, 143, 255)); else CFont::SetColor(CRGBA(147, 196, 211, 255)); CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_SCALE_Y(22.0f), pCurrentStation); CFont::DrawFonts(); } } // Always show station text after entering car. Same behaviour as III and SA. #ifdef FIX_BUGS else pCurrentStation = nil; #endif } bool cMusicManager::UsesPoliceRadio(CVehicle *veh) { switch (veh->GetModelIndex()) { case MI_VCNMAV: case MI_POLMAV: case MI_COASTG: case MI_RHINO: case MI_BARRACKS: return true; case MI_MRWHOOP: case MI_HUNTER: return false; } return veh->UsesSiren(); } bool cMusicManager::UsesTaxiRadio(CVehicle *veh) { if (veh->GetModelIndex() != MI_KAUFMAN) return false; return CTheScripts::bPlayerHasMetDebbieHarry; } void cMusicManager::ServiceAmbience() { } bool cMusicManager::ChangeRadioChannel() { return true; } // these two are empty void cMusicManager::Enable() {} void cMusicManager::Disable() {} ================================================ FILE: src/audio/MusicManager.h ================================================ #pragma once #include "audio_enums.h" class tStreamedSample { public: uint32 m_nLength; uint32 m_nPosition; uint32 m_nLastPosCheckTimer; }; class CVehicle; class CPed; class cMusicManager { public: bool m_bIsInitialised; bool m_bDisabled; bool m_bSetNextStation; uint8 m_nVolumeLatency; uint8 m_nCurrentVolume; uint8 m_nMaxVolume; uint32 m_nAnnouncement; bool m_bAnnouncementInProgress; tStreamedSample m_aTracks[TOTAL_STREAMED_SOUNDS]; bool m_bResetTimers; uint32 m_nResetTime; bool m_bRadioSetByScript; uint8 m_nRadioStationScript; int32 m_nRadioPosition; uint32 m_nRadioInCar; uint32 m_nFrontendTrack; uint32 m_nPlayingTrack; uint8 m_nUpcomingMusicMode; uint8 m_nMusicMode; bool m_FrontendLoopFlag; bool m_bTrackChangeStarted; uint32 m_nNextTrack; bool m_nNextLoopFlag; bool m_bVerifyNextTrackStartedToPlay; bool m_bGameplayAllowsRadio; bool m_bRadioStreamReady; int8 nFramesSinceCutsceneEnded; bool m_bUserResumedGame; bool m_bMusicModeChangeStarted; uint8 m_nMusicModeToBeSet; bool m_bEarlyFrontendTrack; float aListenTimeArray[NUM_RADIOS]; float m_nLastTrackServiceTime; public: cMusicManager(); bool IsInitialised() { return m_bIsInitialised; } uint8 GetMusicMode() { return m_nMusicMode; } uint32 GetCurrentTrack() { return m_nPlayingTrack; } void ResetMusicAfterReload(); void SetStartingTrackPositions(uint8 isNewGameTimer); bool Initialise(); void Terminate(); void ChangeMusicMode(uint8 mode); void StopFrontEndTrack(); bool PlayerInCar(); void DisplayRadioStationName(); void PlayAnnouncement(uint32); void PlayFrontEndTrack(uint32, uint8); void PreloadCutSceneMusic(uint32); void PlayPreloadedCutSceneMusic(void); void StopCutSceneMusic(void); uint32 GetRadioInCar(void); void SetRadioInCar(uint32); void SetRadioChannelByScript(uint32, int32); void ResetTimers(int32); void Service(); void ServiceFrontEndMode(); void ServiceGameMode(); void ServiceAmbience(); void ServiceTrack(CVehicle *veh, CPed *ped); bool UsesPoliceRadio(CVehicle *veh); bool UsesTaxiRadio(CVehicle *veh); uint32 GetTrackStartPos(uint32 track); void ComputeAmbienceVol(uint8 reset, uint8& outVolume); bool ServiceAnnouncement(); uint32 GetCarTuning(); uint32 GetNextCarTuning(); bool ChangeRadioChannel(); void RecordRadioStats(); void SetUpCorrectAmbienceTrack(); float *GetListenTimeArray(); uint32 GetRadioPosition(uint32 station); uint32 GetFavouriteRadioStation(); void SetMalibuClubTrackPos(uint8 pos); void SetStripClubTrackPos(uint8 pos); bool CheckForMusicInterruptions(); void Enable(); void Disable(); }; VALIDATE_SIZE(cMusicManager, 0x95C); extern cMusicManager MusicManager; extern bool g_bAnnouncementReadPosAlready; // we have a symbol of this so it was declared in .h float GetHeightScale(); ================================================ FILE: src/audio/PoliceRadio.cpp ================================================ #include "common.h" #include "DMAudio.h" #include "AudioManager.h" #include "AudioSamples.h" #include "MusicManager.h" #include "PlayerPed.h" #include "PoliceRadio.h" #include "Replay.h" #include "Vehicle.h" #include "World.h" #include "Zones.h" #include "sampman.h" #include "Wanted.h" const int channels = ARRAY_SIZE(AudioManager.m_asActiveSamples); const int policeChannel = channels + 1; struct tPoliceRadioZone { char m_aName[8]; uint32 m_nSampleIndex; int32 field_12; }; tPoliceRadioZone ZoneSfx[NUMAUDIOZONES]; int32 g_nMissionAudioSfx = TOTAL_AUDIO_SAMPLES; int8 g_nMissionAudioPlayingStatus = 2; uint8 gSpecialSuspectLastSeenReport; uint32 gMinTimeToNextReport[NUM_CRIME_TYPES]; void cAudioManager::InitialisePoliceRadioZones() { for (int32 i = 0; i < NUMAUDIOZONES; i++) memset(ZoneSfx[i].m_aName, 0, 8); #define SETZONESFX(i, name, sample) \ strcpy(ZoneSfx[i].m_aName, name); \ ZoneSfx[i].m_nSampleIndex = sample; SETZONESFX(0, "VICE_C", SFX_POLICE_RADIO_VICE_CITY); SETZONESFX(1, "IND_ZON", SFX_POLICE_RADIO_VICE_CITY_BEACH); SETZONESFX(2, "COM_ZON", SFX_POLICE_RADIO_VICE_CITY_MAINLAND); SETZONESFX(3, "BEACH1", SFX_POLICE_RADIO_OCEAN_BEACH); SETZONESFX(4, "BEACH2", SFX_POLICE_RADIO_WASHINGTON_BEACH); SETZONESFX(5, "BEACH3", SFX_POLICE_RADIO_VICE_POINT); SETZONESFX(6, "GOLFC", SFX_POLICE_RADIO_LEAF_LINKS); SETZONESFX(7, "STARI", SFX_POLICE_RADIO_STRAFISH_ISLAND); SETZONESFX(8, "DOCKS", SFX_POLICE_RADIO_VICE_PORT); SETZONESFX(9, "HAVANA", SFX_POLICE_RADIO_LITTLE_HAVANA); SETZONESFX(10, "HAITI", SFX_POLICE_RADIO_LITTLE_HAITI); SETZONESFX(11, "PORNI", SFX_POLICE_RADIO_PRAWN_ISLAND); SETZONESFX(12, "DTOWN", SFX_POLICE_RADIO_DOWNTOWN); SETZONESFX(13, "A_PORT", SFX_POLICE_RADIO_ESCOBAR_INTERNATIONAL); #undef SETZONESFX } void cAudioManager::InitialisePoliceRadio() { m_sPoliceRadioQueue.policeChannelTimer = 0; m_sPoliceRadioQueue.policeChannelTimerSeconds = 0; m_sPoliceRadioQueue.policeChannelCounterSeconds = 0; for (int32 i = 0; i < ARRAY_SIZE(m_sPoliceRadioQueue.crimes); i++) m_sPoliceRadioQueue.crimes[i].type = CRIME_NONE; SampleManager.SetChannelReverbFlag(policeChannel, false); gSpecialSuspectLastSeenReport = false; for (int32 i = 0; i < ARRAY_SIZE(gMinTimeToNextReport); i++) gMinTimeToNextReport[i] = m_FrameCounter; } void cAudioManager::ResetPoliceRadio() { if (!m_bIsInitialised) return; if (SampleManager.GetChannelUsedFlag(policeChannel)) SampleManager.StopChannel(policeChannel); InitialisePoliceRadio(); } void cAudioManager::SetMissionScriptPoliceAudio(int32 sfx) const { if (!m_bIsInitialised) return; if (g_nMissionAudioPlayingStatus != 1) { g_nMissionAudioPlayingStatus = 0; g_nMissionAudioSfx = sfx; } } int8 cAudioManager::GetMissionScriptPoliceAudioPlayingStatus() const { return g_nMissionAudioPlayingStatus; } void cAudioManager::DoPoliceRadioCrackle() { m_sQueueSample.m_nEntityIndex = m_nPoliceChannelEntity; m_sQueueSample.m_nCounter = 0; m_sQueueSample.m_nSampleIndex = SFX_POLICE_RADIO_CRACKLE; m_sQueueSample.m_nBankIndex = SFX_BANK_0; m_sQueueSample.m_bIs2D = true; m_sQueueSample.m_nReleasingVolumeModificator = 10; m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_POLICE_RADIO_CRACKLE); m_sQueueSample.m_nVolume = m_anRandomTable[2] % 20 + 15; m_sQueueSample.m_nLoopCount = 0; m_sQueueSample.m_nEmittingVolume = m_sQueueSample.m_nVolume; m_sQueueSample.m_nLoopStart = SampleManager.GetSampleLoopStartOffset(SFX_POLICE_RADIO_CRACKLE); m_sQueueSample.m_nLoopEnd = SampleManager.GetSampleLoopEndOffset(SFX_POLICE_RADIO_CRACKLE); m_sQueueSample.m_bReleasingSoundFlag = false; m_sQueueSample.m_bReverbFlag = false; m_sQueueSample.m_nOffset = 63; m_sQueueSample.m_nReleasingVolumeDivider = 3; m_sQueueSample.m_bRequireReflection = false; AddSampleToRequestedQueue(); } void cAudioManager::ServicePoliceRadio() { int32 wantedLevel = 0; // uninitialized variable static uint32 nLastSeen = 300; if(!m_bIsInitialised) return; if(m_nUserPause == 0) { bool crimeReport = SetupCrimeReport(); #ifdef FIX_BUGS // Crash at 0x5fe6ef if(CReplay::IsPlayingBack() || !FindPlayerPed() || !FindPlayerPed()->m_pWanted) return; #endif CPlayerPed *playerPed = FindPlayerPed(); if (playerPed) { wantedLevel = playerPed->m_pWanted->GetWantedLevel(); if (!crimeReport) { if (wantedLevel != 0) { if (nLastSeen != 0) --nLastSeen; else { nLastSeen = m_anRandomTable[1] % 1000 + 2000; SetupSuspectLastSeenReport(); } } } } } ServicePoliceRadioChannel(wantedLevel); } void cAudioManager::ServicePoliceRadioChannel(uint8 wantedLevel) { bool processed = false; uint32 sample; int32 freq; static int cWait = 0; static bool bChannelOpen = false; static uint8 bMissionAudioPhysicalPlayingStatus = 0; static int32 PoliceChannelFreq = 22050; if (!m_bIsInitialised) return; if (m_nUserPause != 0) { if (SampleManager.GetChannelUsedFlag(policeChannel)) SampleManager.StopChannel(policeChannel); if (g_nMissionAudioSfx != NO_SAMPLE && bMissionAudioPhysicalPlayingStatus == 1 && SampleManager.IsStreamPlaying(1)) { SampleManager.PauseStream(1, 1); } } else { if (m_nPreviousUserPause && g_nMissionAudioSfx != NO_SAMPLE && bMissionAudioPhysicalPlayingStatus == 1) { SampleManager.PauseStream(0, 1); } if (m_sPoliceRadioQueue.policeChannelTimer == 0) bChannelOpen = false; if (cWait) { --cWait; return; } if (g_nMissionAudioSfx != NO_SAMPLE && !bChannelOpen) { if (g_nMissionAudioPlayingStatus) { if (g_nMissionAudioPlayingStatus == 1 && !bMissionAudioPhysicalPlayingStatus && SampleManager.IsStreamPlaying(1)) { bMissionAudioPhysicalPlayingStatus = 1; } if (bMissionAudioPhysicalPlayingStatus == 1) { if (SampleManager.IsStreamPlaying(1)) { DoPoliceRadioCrackle(); } else { bMissionAudioPhysicalPlayingStatus = 2; g_nMissionAudioPlayingStatus = 2; g_nMissionAudioSfx = NO_SAMPLE; cWait = 30; } return; } } else if (!SampleManager.GetChannelUsedFlag(policeChannel)) { SampleManager.PreloadStreamedFile(g_nMissionAudioSfx, 1); SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, 1, 1); SampleManager.StartPreloadedStreamedFile(1); g_nMissionAudioPlayingStatus = 1; bMissionAudioPhysicalPlayingStatus = 0; return; } } if (bChannelOpen) DoPoliceRadioCrackle(); if ((g_nMissionAudioSfx == NO_SAMPLE || g_nMissionAudioPlayingStatus != 1) && !SampleManager.GetChannelUsedFlag(policeChannel) && m_sPoliceRadioQueue.policeChannelTimer) { if (m_sPoliceRadioQueue.policeChannelTimer) { sample = m_sPoliceRadioQueue.crimesSamples[m_sPoliceRadioQueue.policeChannelCounterSeconds]; m_sPoliceRadioQueue.policeChannelTimer--; m_sPoliceRadioQueue.policeChannelCounterSeconds = (m_sPoliceRadioQueue.policeChannelCounterSeconds + 1) % 60; } else { sample = NO_SAMPLE; } if (wantedLevel == 0) { if (gSpecialSuspectLastSeenReport) { gSpecialSuspectLastSeenReport = 0; } else if (sample == SFX_POLICE_RADIO_MESSAGE_NOISE_1) { bChannelOpen = false; processed = true; } } if (sample == NO_SAMPLE) { if (!processed) cWait = 30; } else { SampleManager.InitialiseChannel(policeChannel, sample, 0); switch (sample) { case SFX_POLICE_RADIO_MESSAGE_NOISE_1: freq = m_anRandomTable[4] % 2000 + 10025; bChannelOpen = bChannelOpen == false; break; default: freq = SampleManager.GetSampleBaseFrequency(sample); break; } PoliceChannelFreq = freq; SampleManager.SetChannelFrequency(policeChannel, freq); SampleManager.SetChannelVolume(policeChannel, 100); SampleManager.SetChannelPan(policeChannel, 63); SampleManager.SetChannelLoopCount(policeChannel, 1); SampleManager.SetChannelLoopPoints(policeChannel, 0, -1); SampleManager.StartChannel(policeChannel); } if (processed) ResetPoliceRadio(); } } } bool cAudioManager::SetupCrimeReport() { int16 audioZoneId; CZone *zone; float rangeX; float rangeY; float halfX; float halfY; float quarterX; float quarterY; int i; int32 sampleIndex; bool processed = false; if (MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE) return false; if (60 - m_sPoliceRadioQueue.policeChannelTimer <= 9) { AgeCrimes(); return true; } for (i = 0; i < ARRAY_SIZE(m_sPoliceRadioQueue.crimes); i++) { if (m_sPoliceRadioQueue.crimes[i].type != CRIME_NONE) break; } if (i == ARRAY_SIZE(m_sPoliceRadioQueue.crimes)) return false; audioZoneId = CTheZones::FindAudioZone(&m_sPoliceRadioQueue.crimes[i].position); if (audioZoneId >= 0 && audioZoneId < NUMAUDIOZONES) { zone = CTheZones::GetAudioZone(audioZoneId); for (int j = 0; j < NUMAUDIOZONES; j++) { if (strcmp(zone->name, ZoneSfx[j].m_aName) == 0) { sampleIndex = ZoneSfx[j].m_nSampleIndex; m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); m_sPoliceRadioQueue.Add(m_anRandomTable[0] % 3 + SFX_WEVE_GOT); m_sPoliceRadioQueue.Add(SFX_A_10); switch (m_sPoliceRadioQueue.crimes[i].type) { case CRIME_PED_BURNED: case CRIME_HIT_PED_NASTYWEAPON: m_sPoliceRadioQueue.crimes[i].type = CRIME_HIT_PED; break; case CRIME_COP_BURNED: case CRIME_HIT_COP_NASTYWEAPON: m_sPoliceRadioQueue.crimes[i].type = CRIME_HIT_COP; break; case CRIME_VEHICLE_BURNED: m_sPoliceRadioQueue.crimes[i].type = CRIME_STEAL_CAR; break; case CRIME_DESTROYED_CESSNA: m_sPoliceRadioQueue.crimes[i].type = CRIME_SHOOT_HELI; break; case CRIME_EXPLOSION: m_sPoliceRadioQueue.crimes[i].type = CRIME_STEAL_CAR; break; // huh? default: break; } #ifdef FIX_BUGS m_sPoliceRadioQueue.Add(m_sPoliceRadioQueue.crimes[i].type + SFX_CRIME_1 - 1); #else m_sPoliceRadioQueue.Add(m_sPoliceRadioQueue.crimes[i].type + SFX_CRIME_1); #endif m_sPoliceRadioQueue.Add(SFX_IN); rangeX = zone->maxx - zone->minx; rangeY = zone->maxy - zone->miny; halfX = 0.5f * rangeX + zone->minx; halfY = 0.5f * rangeY + zone->miny; quarterX = 0.25f * rangeX; quarterY = 0.25f * rangeY; if (m_sPoliceRadioQueue.crimes[i].position.y > halfY + quarterY) { m_sPoliceRadioQueue.Add(SFX_NORTH); processed = true; } else if (m_sPoliceRadioQueue.crimes[i].position.y < halfY - quarterY) { m_sPoliceRadioQueue.Add(SFX_SOUTH); processed = true; } if (m_sPoliceRadioQueue.crimes[i].position.x > halfX + quarterX) m_sPoliceRadioQueue.Add(SFX_EAST); else if (m_sPoliceRadioQueue.crimes[i].position.x < halfX - quarterX) m_sPoliceRadioQueue.Add(SFX_WEST); else if (!processed) m_sPoliceRadioQueue.Add(SFX_CENTRAL); m_sPoliceRadioQueue.Add(sampleIndex); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); m_sPoliceRadioQueue.Add(NO_SAMPLE); break; } } } m_sPoliceRadioQueue.crimes[i].type = CRIME_NONE; AgeCrimes(); return true; } void cAudioManager::SetupSuspectLastSeenReport() { CVehicle *veh; uint8 color1; int32 main_color; int32 sample; int32 color_pre_modifier; int32 color_post_modifier; const int32 gCarColourTable[][3] = { {NO_SAMPLE, SFX_POLICE_RADIO_BLACK, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_WHITE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, {SFX_POLICE_RADIO_BRIGHT, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_BLUE, SFX_POLICE_RADIO_GREY}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_RED, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_RED, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_ORANGE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_YELLOW, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_GREEN, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_BLUE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_PURPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, {NO_SAMPLE, SFX_POLICE_RADIO_SILVER, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_LIGHT, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE}, {SFX_POLICE_RADIO_DARK, NO_SAMPLE, NO_SAMPLE} }; if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) { veh = FindVehicleOfPlayer(); if (veh != nil) { if (60 - m_sPoliceRadioQueue.policeChannelTimer > 9) { color1 = veh->m_currentColour1; if (color1 >= ARRAY_SIZE(gCarColourTable)) { debug("\n *** UNKNOWN CAR COLOUR %d *** ", color1); } else { main_color = gCarColourTable[color1][1]; color_pre_modifier = gCarColourTable[color1][0]; color_post_modifier = gCarColourTable[color1][2]; switch (veh->GetModelIndex()) { case MI_LANDSTAL: case MI_PATRIOT: case MI_RANCHER: case MI_FBIRANCH: case MI_SANDKING: sample = SFX_POLICE_RADIO_OFFROAD; break; case MI_IDAHO: case MI_MANANA: case MI_ESPERANT: case MI_CUBAN: case MI_STALLION: case MI_SABRE: case MI_SABRETUR: case MI_VIRGO: case MI_BLISTAC: sample = SFX_POLICE_RADIO_2_DOOR; break; case MI_STINGER: case MI_INFERNUS: case MI_CHEETAH: case MI_BANSHEE: case MI_PHEONIX: case MI_COMET: case MI_DELUXO: case MI_HOTRING: sample = SFX_POLICE_RADIO_SPORTS_CAR; break; case MI_LINERUN: sample = SFX_POLICE_RADIO_RIG; break; case MI_PEREN: case MI_REGINA: sample = SFX_POLICE_RADIO_STATION_WAGON; break; case MI_SENTINEL: case MI_FBICAR: case MI_WASHING: case MI_SENTXS: case MI_ADMIRAL: case MI_GLENDALE: case MI_OCEANIC: case MI_HERMES: case MI_GREENWOO: sample = SFX_POLICE_RADIO_SEDAN; break; case MI_RIO: sample = SFX_POLICE_RADIO_CRUISER; break; case MI_FIRETRUCK: sample = SFX_POLICE_RADIO_FIRE_TRUCK; break; case MI_TRASH: sample = SFX_POLICE_RADIO_GARBAGE_TRUCK; break; case MI_STRETCH: case MI_LOVEFIST: sample = SFX_POLICE_RADIO_STRETCH; break; case MI_VOODOO: sample = SFX_POLICE_RADIO_LOWRIDER; break; case MI_PONY: case MI_MOONBEAM: case MI_SECURICA: case MI_RUMPO: case MI_GANGBUR: case MI_YANKEE: case MI_TOPFUN: case MI_BURRITO: case MI_SPAND: sample = SFX_POLICE_RADIO_VAN; break; case MI_MULE: case MI_BARRACKS: case MI_PACKER: case MI_FLATBED: sample = SFX_POLICE_RADIO_TRUCK; break; case MI_AMBULAN: sample = SFX_POLICE_RADIO_AMBULANCE; break; case MI_TAXI: case MI_CABBIE: case MI_ZEBRA: case MI_KAUFMAN: sample = SFX_POLICE_RADIO_TAXI; break; case MI_BOBCAT: case MI_WALTON: sample = SFX_POLICE_RADIO_PICKUP; break; case MI_MRWHOOP: sample = SFX_POLICE_RADIO_ICE_CREAM_VAN; break; case MI_BFINJECT: sample = SFX_POLICE_RADIO_BUGGY; break; case MI_HUNTER: case MI_CHOPPER: case MI_SEASPAR: case MI_SPARROW: case MI_MAVERICK: case MI_VCNMAV: case MI_POLMAV: sample = SFX_POLICE_RADIO_HELICOPTER; break; case MI_POLICE: sample = SFX_POLICE_RADIO_POLICE_CAR; break; case MI_ENFORCER: sample = SFX_POLICE_RADIO_SWAT_VAN; break; case MI_PREDATOR: case MI_SQUALO: case MI_SPEEDER: sample = SFX_POLICE_RADIO_SPEEDBOAT; break; case MI_BUS: sample = SFX_POLICE_RADIO_BUS; break; case MI_RHINO: sample = SFX_POLICE_RADIO_TANK; break; case MI_ANGEL: case MI_PCJ600: case MI_FREEWAY: case MI_SANCHEZ: sample = SFX_POLICE_RADIO_MOTOBIKE; break; case MI_COACH: sample = SFX_POLICE_RADIO_COACH; break; case MI_ROMERO: sample = SFX_POLICE_RADIO_HEARSE; break; case MI_PIZZABOY: case MI_FAGGIO: sample = SFX_POLICE_RADIO_MOPED; break; case MI_DEADDODO: case MI_SKIMMER: sample = SFX_POLICE_RADIO_PLANE; break; case MI_REEFER: case MI_TROPIC: case MI_COASTG: case MI_MARQUIS: case MI_JETMAX: sample = SFX_POLICE_RADIO_BOAT; break; case MI_CADDY: sample = SFX_POLICE_RADIO_GOLF_CART; break; case MI_DINGHY: sample = SFX_POLICE_RADIO_DINGHY; break; default: //debug("\n *** UNKNOWN CAR MODEL INDEX %d *** ", veh->GetModelIndex()); return; } m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_SUSPECT); if (m_anRandomTable[3] % 2) m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_LAST_SEEN); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_IN_A); if (color_pre_modifier != NO_SAMPLE) m_sPoliceRadioQueue.Add(color_pre_modifier); if (main_color != NO_SAMPLE) m_sPoliceRadioQueue.Add(main_color); if (color_post_modifier != NO_SAMPLE) m_sPoliceRadioQueue.Add(color_post_modifier); m_sPoliceRadioQueue.Add(sample); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); m_sPoliceRadioQueue.Add(NO_SAMPLE); } } } else if (60 - m_sPoliceRadioQueue.policeChannelTimer > 4) { m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_SUSPECT); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_ON_FOOT); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); m_sPoliceRadioQueue.Add(NO_SAMPLE); } } } void cAudioManager::ReportCrime(eCrimeType type, const CVector &pos) { int32 lastCrime = ARRAY_SIZE(m_sPoliceRadioQueue.crimes); if (m_bIsInitialised && MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 && (type > CRIME_NONE || type < NUM_CRIME_TYPES) && m_FrameCounter >= gMinTimeToNextReport[type]) { for (int32 i = 0; i < ARRAY_SIZE(m_sPoliceRadioQueue.crimes); i++) { if (m_sPoliceRadioQueue.crimes[i].type != CRIME_NONE) { if (m_sPoliceRadioQueue.crimes[i].type == type) { m_sPoliceRadioQueue.crimes[i].position = pos; m_sPoliceRadioQueue.crimes[i].timer = 0; return; } } else lastCrime = i; } if (lastCrime < ARRAY_SIZE(m_sPoliceRadioQueue.crimes)) { m_sPoliceRadioQueue.crimes[lastCrime].type = type; m_sPoliceRadioQueue.crimes[lastCrime].position = pos; m_sPoliceRadioQueue.crimes[lastCrime].timer = 0; gMinTimeToNextReport[type] = m_FrameCounter + 500; } } } void cAudioManager::PlaySuspectLastSeen(float x, float y, float z) { int16 audioZone; CZone *zone; float rangeX; float rangeY; float halfX; float halfY; float quarterX; float quarterY; int32 sample; bool processed = false; CVector vec = CVector(x, y, z); if (!m_bIsInitialised) return; if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE && 60 - m_sPoliceRadioQueue.policeChannelTimer > 9) { audioZone = CTheZones::FindAudioZone(&vec); if (audioZone >= 0 && audioZone < NUMAUDIOZONES) { zone = CTheZones::GetAudioZone(audioZone); for (int i = 0; i < NUMAUDIOZONES; i++) { if (strcmp(zone->name, ZoneSfx[i].m_aName) == 0) { sample = ZoneSfx[i].m_nSampleIndex; m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_SUSPECT); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_LAST_SEEN); m_sPoliceRadioQueue.Add(SFX_IN); rangeX = zone->maxx - zone->minx; rangeY = zone->maxy - zone->miny; halfX = 0.5f * rangeX + zone->minx; halfY = 0.5f * rangeY + zone->miny; quarterX = 0.25f * rangeX; quarterY = 0.25f * rangeY; if (vec.y > halfY + quarterY) { m_sPoliceRadioQueue.Add(SFX_NORTH); processed = true; } else if (vec.y < halfY - quarterY) { m_sPoliceRadioQueue.Add(SFX_SOUTH); processed = true; } if (vec.x > halfX + quarterX) m_sPoliceRadioQueue.Add(SFX_EAST); else if (vec.x < halfX - quarterX) m_sPoliceRadioQueue.Add(SFX_WEST); else if (!processed) m_sPoliceRadioQueue.Add(SFX_CENTRAL); m_sPoliceRadioQueue.Add(sample); m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1); m_sPoliceRadioQueue.Add(NO_SAMPLE); gSpecialSuspectLastSeenReport = true; break; } } } } } void cAudioManager::AgeCrimes() { for (uint8 i = 0; i < ARRAY_SIZE(m_sPoliceRadioQueue.crimes); i++) { if (m_sPoliceRadioQueue.crimes[i].type != CRIME_NONE) { if (++m_sPoliceRadioQueue.crimes[i].timer > 1200) m_sPoliceRadioQueue.crimes[i].type = CRIME_NONE; } } } ================================================ FILE: src/audio/PoliceRadio.h ================================================ #pragma once #include "Crime.h" struct cAMCrime { int32 type; CVector position; uint16 timer; cAMCrime() { type = CRIME_NONE; position = CVector(0.0f, 0.0f, 0.0f); timer = 0; } }; VALIDATE_SIZE(cAMCrime, 20); class cPoliceRadioQueue { public: int32 crimesSamples[60]; uint8 policeChannelTimer; uint8 policeChannelTimerSeconds; uint8 policeChannelCounterSeconds; cAMCrime crimes[10]; cPoliceRadioQueue() { policeChannelTimerSeconds = 0; policeChannelCounterSeconds = 0; policeChannelTimer = 0; } void Add(uint32 sample) { if (policeChannelTimer != 60) { crimesSamples[policeChannelTimerSeconds] = sample; policeChannelTimer++; policeChannelTimerSeconds = (policeChannelTimerSeconds + 1) % 60; } } }; VALIDATE_SIZE(cPoliceRadioQueue, 0x1BC); ================================================ FILE: src/audio/audio_enums.h ================================================ #pragma once enum eRadioStation { WILDSTYLE, FLASH_FM, KCHAT, FEVER, V_ROCK, VCPR, RADIO_ESPANTOSO, EMOTION, WAVE, USERTRACK, NUM_RADIOS = 10, POLICE_RADIO = 10, RADIO_OFF = 10, //TAXI_RADIO, }; enum eMusicMode { MUSICMODE_FRONTEND = 0, MUSICMODE_GAME, MUSICMODE_CUTSCENE, MUSICMODE_DISABLE, MUSICMODE_DISABLED, }; enum ePlayerMood { PLAYER_MOOD_CALM = 0, PLAYER_MOOD_PISSED_OFF, PLAYER_MOOD_ANGRY, PLAYER_MOOD_WISECRACKING, MAX_PLAYER_MOODS, }; enum eStreamedSounds { STREAMED_SOUND_RADIO_WILD, STREAMED_SOUND_RADIO_FLASH, STREAMED_SOUND_RADIO_KCHAT, STREAMED_SOUND_RADIO_FEVER, STREAMED_SOUND_RADIO_VROCK, STREAMED_SOUND_RADIO_VCPR, STREAMED_SOUND_RADIO_ESPANTOSO, STREAMED_SOUND_RADIO_EMOTION, STREAMED_SOUND_RADIO_WAVE, STREAMED_SOUND_RADIO_MP3_PLAYER, STREAMED_SOUND_CITY_AMBIENT, STREAMED_SOUND_WATER_AMBIENT, STREAMED_SOUND_BEACH_AMBIENT, STREAMED_SOUND_HAVANA_CITY_AMBIENT, STREAMED_SOUND_HAVANA_WATER_AMBIENT, STREAMED_SOUND_HAVANA_BEACH_AMBIENT, STREAMED_SOUND_MALL_AMBIENT, STREAMED_SOUND_STRIPCLUB_AMBIENT, STREAMED_SOUND_MALIBU_AMBIENT, STREAMED_SOUND_HOTEL_AMBIENT, STREAMED_SOUND_DIRTRING_AMBIENT, STREAMED_SOUND_LAW4RIOT_AMBIENT, STREAMED_SOUND_AMBSIL_AMBIENT, STREAMED_SOUND_RADIO_POLICE, STREAMED_SOUND_RADIO_TAXI, STREAMED_SOUND_ANNOUNCE_BRIDGE_CLOSED, STREAMED_SOUND_ANNOUNCE_BRIDGE_OPEN, STREAMED_SOUND_CUTSCENE_ASS_1, STREAMED_SOUND_CUTSCENE_ASS_2, STREAMED_SOUND_CUTSCENE_BANK_1, STREAMED_SOUND_CUTSCENE_BANK_2A, STREAMED_SOUND_CUTSCENE_BANK_2B, STREAMED_SOUND_CUTSCENE_BANK_3A, STREAMED_SOUND_CUTSCENE_BANK_3B, STREAMED_SOUND_CUTSCENE_BANK_4, STREAMED_SOUND_CUTSCENE_BIKE_1, STREAMED_SOUND_CUTSCENE_BIKE_2, STREAMED_SOUND_CUTSCENE_BIKE_3, STREAMED_SOUND_CUTSCENE_BUD_1, STREAMED_SOUND_CUTSCENE_BUD_2, STREAMED_SOUND_CUTSCENE_BUD_3, STREAMED_SOUND_CUTSCENE_CAP_1, STREAMED_SOUND_CUTSCENE_CAR_1, STREAMED_SOUND_CUTSCENE_CNT_1A, STREAMED_SOUND_CUTSCENE_CNT_1B, STREAMED_SOUND_CUTSCENE_CNT_2, STREAMED_SOUND_CUTSCENE_COK_1, STREAMED_SOUND_CUTSCENE_COK_2A, STREAMED_SOUND_CUTSCENE_COK_2B, STREAMED_SOUND_CUTSCENE_COK_3, STREAMED_SOUND_CUTSCENE_COK_4A, STREAMED_SOUND_CUTSCENE_COK_4A2, STREAMED_SOUND_CUTSCENE_COK_4B, STREAMED_SOUND_CUTSCENE_COL_1, STREAMED_SOUND_CUTSCENE_COL_2, STREAMED_SOUND_CUTSCENE_COL_3A, STREAMED_SOUND_CUTSCENE_COL_4A, STREAMED_SOUND_CUTSCENE_COL_5A, STREAMED_SOUND_CUTSCENE_COL_5B, STREAMED_SOUND_CUTSCENE_CUB_1, STREAMED_SOUND_CUTSCENE_CUB_2, STREAMED_SOUND_CUTSCENE_CUB_3, STREAMED_SOUND_CUTSCENE_CUB_4, STREAMED_SOUND_CUTSCENE_DRUG_1, STREAMED_SOUND_CUTSCENE_FIN, STREAMED_SOUND_CUTSCENE_FIN2, STREAMED_SOUND_CUTSCENE_FINALE, STREAMED_SOUND_CUTSCENE_HAT_1, STREAMED_SOUND_CUTSCENE_HAT_2, STREAMED_SOUND_CUTSCENE_HAT_3, STREAMED_SOUND_CUTSCENE_ICE_1, STREAMED_SOUND_CUTSCENE_INT_A, STREAMED_SOUND_CUTSCENE_INT_B, STREAMED_SOUND_CUTSCENE_INT_D, STREAMED_SOUND_CUTSCENE_INT_M, STREAMED_SOUND_CUTSCENE_LAW_1A, STREAMED_SOUND_CUTSCENE_LAW_1B, STREAMED_SOUND_CUTSCENE_LAW_2A, STREAMED_SOUND_CUTSCENE_LAW_2B, STREAMED_SOUND_CUTSCENE_LAW_2C, STREAMED_SOUND_CUTSCENE_LAW_3, STREAMED_SOUND_CUTSCENE_LAW_4, STREAMED_SOUND_CUTSCENE_PHIL_1, STREAMED_SOUND_CUTSCENE_PHIL_2, STREAMED_SOUND_CUTSCENE_PORN_1, STREAMED_SOUND_CUTSCENE_PORN_2, STREAMED_SOUND_CUTSCENE_PORN_3, STREAMED_SOUND_CUTSCENE_PORN_4, STREAMED_SOUND_CUTSCENE_RESC_1A, STREAMED_SOUND_CUTSCENE_ROK_1, STREAMED_SOUND_CUTSCENE_ROK_2, STREAMED_SOUND_CUTSCENE_ROK_3A, STREAMED_SOUND_CUTSCENE_STRIPA, STREAMED_SOUND_CUTSCENE_TAX_1, STREAMED_SOUND_CUTSCENE_TEX_1, STREAMED_SOUND_CUTSCENE_TEX_2, STREAMED_SOUND_CUTSCENE_TEX_3, STREAMED_SOUND_CUTSCENE_GLIGHT, STREAMED_SOUND_CUTSCENE_FIST, STREAMED_SOUND_CUTSCENE_ELBURRO1_PH1, STREAMED_SOUND_CUTSCENE_ELBURRO2_PH2, STREAMED_SOUND_MISSION_COMPLETED, STREAMED_SOUND_MISSION_COMPLETED4, STREAMED_SOUND_MISSION_MOBR1, STREAMED_SOUND_MISSION_PAGER, STREAMED_SOUND_MISSION_CARREV, STREAMED_SOUND_MISSION_BIKEREV, STREAMED_SOUND_MISSION_LIFTOP, STREAMED_SOUND_MISSION_LIFTCL, STREAMED_SOUND_MISSION_LIFTRUN, STREAMED_SOUND_MISSION_LIFTBEL, STREAMED_SOUND_MISSION_INLIFT, STREAMED_SOUND_MISSION_SFX_01, STREAMED_SOUND_MISSION_SFX_02, STREAMED_SOUND_MISSION_CAMERAL, STREAMED_SOUND_MISSION_CAMERAR, STREAMED_SOUND_MISSION_CHEER1, STREAMED_SOUND_MISSION_CHEER2, STREAMED_SOUND_MISSION_CHEER3, STREAMED_SOUND_MISSION_CHEER4, STREAMED_SOUND_MISSION_OOH1, STREAMED_SOUND_MISSION_OOH2, STREAMED_SOUND_MISSION_RACE1, STREAMED_SOUND_MISSION_RACE2, STREAMED_SOUND_MISSION_RACE3, STREAMED_SOUND_MISSION_RACE4, STREAMED_SOUND_MISSION_RACE5, STREAMED_SOUND_MISSION_RACE6, STREAMED_SOUND_MISSION_RACE7, STREAMED_SOUND_MISSION_RACE8, STREAMED_SOUND_MISSION_RACE9, STREAMED_SOUND_MISSION_RACE10, STREAMED_SOUND_MISSION_RACE11, STREAMED_SOUND_MISSION_RACE12, STREAMED_SOUND_MISSION_RACE13, STREAMED_SOUND_MISSION_RACE14, STREAMED_SOUND_MISSION_RACE15, STREAMED_SOUND_MISSION_HOT1, STREAMED_SOUND_MISSION_HOT2, STREAMED_SOUND_MISSION_HOT3, STREAMED_SOUND_MISSION_HOT4, STREAMED_SOUND_MISSION_HOT5, STREAMED_SOUND_MISSION_HOT6, STREAMED_SOUND_MISSION_HOT7, STREAMED_SOUND_MISSION_HOT8, STREAMED_SOUND_MISSION_HOT9, STREAMED_SOUND_MISSION_HOT10, STREAMED_SOUND_MISSION_HOT11, STREAMED_SOUND_MISSION_HOT12, STREAMED_SOUND_MISSION_HOT13, STREAMED_SOUND_MISSION_HOT14, STREAMED_SOUND_MISSION_HOT15, STREAMED_SOUND_MISSION_LANSTP1, STREAMED_SOUND_MISSION_LANSTP2, STREAMED_SOUND_MISSION_LANAMU1, STREAMED_SOUND_MISSION_LANAMU2, STREAMED_SOUND_MISSION_AIRHORNL, STREAMED_SOUND_MISSION_AIRHORNR, STREAMED_SOUND_MISSION_SNIPSCRL, STREAMED_SOUND_MISSION_SNIPSHORT, STREAMED_SOUND_MISSION_BLOWROOF, STREAMED_SOUND_MISSION_ASS_1, STREAMED_SOUND_MISSION_ASS_2, STREAMED_SOUND_MISSION_ASS_3, STREAMED_SOUND_MISSION_ASS_4, STREAMED_SOUND_MISSION_ASS_5, STREAMED_SOUND_MISSION_ASS_6, STREAMED_SOUND_MISSION_ASS_7, STREAMED_SOUND_MISSION_ASS_8, STREAMED_SOUND_MISSION_ASS_9, STREAMED_SOUND_MISSION_ASS_10, STREAMED_SOUND_MISSION_ASS_11, STREAMED_SOUND_MISSION_ASS_12, STREAMED_SOUND_MISSION_ASS_13, STREAMED_SOUND_MISSION_ASS_14, STREAMED_SOUND_MISSION_BIKE1_1, STREAMED_SOUND_MISSION_BIKE1_2, STREAMED_SOUND_MISSION_BIKE1_3, STREAMED_SOUND_MISSION_BNK1_1, STREAMED_SOUND_MISSION_BNK1_2, STREAMED_SOUND_MISSION_BNK1_3, STREAMED_SOUND_MISSION_BNK1_4, STREAMED_SOUND_MISSION_BNK1_5, STREAMED_SOUND_MISSION_BNK1_6, STREAMED_SOUND_MISSION_BNK1_7, STREAMED_SOUND_MISSION_BNK1_8, STREAMED_SOUND_MISSION_BNK1_10, STREAMED_SOUND_MISSION_BNK1_11, STREAMED_SOUND_MISSION_BNK1_12, STREAMED_SOUND_MISSION_BNK1_13, STREAMED_SOUND_MISSION_BNK1_14, STREAMED_SOUND_MISSION_BNK2_1, STREAMED_SOUND_MISSION_BNK2_2, STREAMED_SOUND_MISSION_BNK2_3, STREAMED_SOUND_MISSION_BNK2_4, STREAMED_SOUND_MISSION_BNK2_5, STREAMED_SOUND_MISSION_BNK2_6, STREAMED_SOUND_MISSION_BNK2_7, STREAMED_SOUND_MISSION_BNK2_8, STREAMED_SOUND_MISSION_BNK2_9, STREAMED_SOUND_MISSION_BNK3_1, STREAMED_SOUND_MISSION_BNK3_2, STREAMED_SOUND_MISSION_BNK3_3A, STREAMED_SOUND_MISSION_BNK3_3B, STREAMED_SOUND_MISSION_BNK3_3C, STREAMED_SOUND_MISSION_BNK3_4A, STREAMED_SOUND_MISSION_BNK3_4B, STREAMED_SOUND_MISSION_BNK3_4C, STREAMED_SOUND_MISSION_BNK4_1, STREAMED_SOUND_MISSION_BNK4_2, STREAMED_SOUND_MISSION_BNK4_3A, STREAMED_SOUND_MISSION_BNK4_3B, STREAMED_SOUND_MISSION_BNK4_3C, STREAMED_SOUND_MISSION_BNK4_3D, STREAMED_SOUND_MISSION_BNK4_3E, STREAMED_SOUND_MISSION_BNK4_3F, STREAMED_SOUND_MISSION_BNK4_3G, STREAMED_SOUND_MISSION_BNK4_3H, STREAMED_SOUND_MISSION_BNK4_3I, STREAMED_SOUND_MISSION_BNK4_3J, STREAMED_SOUND_MISSION_BNK4_3K, STREAMED_SOUND_MISSION_BNK4_3M, STREAMED_SOUND_MISSION_BNK4_3O, STREAMED_SOUND_MISSION_BNK4_3P, STREAMED_SOUND_MISSION_BNK4_3Q, STREAMED_SOUND_MISSION_BNK4_3R, STREAMED_SOUND_MISSION_BNK4_3S, STREAMED_SOUND_MISSION_BNK4_3T, STREAMED_SOUND_MISSION_BNK4_3U, STREAMED_SOUND_MISSION_BNK4_3V, STREAMED_SOUND_MISSION_BNK4_4A, STREAMED_SOUND_MISSION_BNK4_4B, STREAMED_SOUND_MISSION_BNK4_5, STREAMED_SOUND_MISSION_BNK4_6, STREAMED_SOUND_MISSION_BNK4_7, STREAMED_SOUND_MISSION_BNK4_8, STREAMED_SOUND_MISSION_BNK4_9, STREAMED_SOUND_MISSION_BNK4_10, STREAMED_SOUND_MISSION_BNK4_11, STREAMED_SOUND_MISSION_BK4_12A, STREAMED_SOUND_MISSION_BK4_12B, STREAMED_SOUND_MISSION_BK4_12C, STREAMED_SOUND_MISSION_BNK4_13, STREAMED_SOUND_MISSION_BK4_14A, STREAMED_SOUND_MISSION_BK4_14B, STREAMED_SOUND_MISSION_BNK4_15, STREAMED_SOUND_MISSION_BNK4_16, STREAMED_SOUND_MISSION_BNK4_17, STREAMED_SOUND_MISSION_BNK4_18, STREAMED_SOUND_MISSION_BK4_19A, STREAMED_SOUND_MISSION_BK4_19B, STREAMED_SOUND_MISSION_BK4_20A, STREAMED_SOUND_MISSION_BK4_20B, STREAMED_SOUND_MISSION_BNK4_21, STREAMED_SOUND_MISSION_BNK422A, STREAMED_SOUND_MISSION_BNK422B, STREAMED_SOUND_MISSION_BK4_23A, STREAMED_SOUND_MISSION_BK4_23B, STREAMED_SOUND_MISSION_BK4_23C, STREAMED_SOUND_MISSION_BK4_23D, STREAMED_SOUND_MISSION_BK4_24A, STREAMED_SOUND_MISSION_BK4_24B, STREAMED_SOUND_MISSION_BNK4_25, STREAMED_SOUND_MISSION_BNK4_26, STREAMED_SOUND_MISSION_BNK4_27, STREAMED_SOUND_MISSION_BNK4_28, STREAMED_SOUND_MISSION_BNK4_29, STREAMED_SOUND_MISSION_BNK4_30, STREAMED_SOUND_MISSION_BK4_31A, STREAMED_SOUND_MISSION_BK4_31B, STREAMED_SOUND_MISSION_BNK4_32, STREAMED_SOUND_MISSION_BK4_34A, STREAMED_SOUND_MISSION_BK4_34B, STREAMED_SOUND_MISSION_BK4_35A, STREAMED_SOUND_MISSION_BK4_35B, STREAMED_SOUND_MISSION_BNK4_36, STREAMED_SOUND_MISSION_BNK4_37, STREAMED_SOUND_MISSION_BNK4_38, STREAMED_SOUND_MISSION_BNK4_39, STREAMED_SOUND_MISSION_BK4_40A, STREAMED_SOUND_MISSION_BK4_40B, STREAMED_SOUND_MISSION_BNK4_41, STREAMED_SOUND_MISSION_BNK4_42, STREAMED_SOUND_MISSION_BNK4_43, STREAMED_SOUND_MISSION_BNK4_44, STREAMED_SOUND_MISSION_BNK4_45, STREAMED_SOUND_MISSION_BNK4_46, STREAMED_SOUND_MISSION_BNK4_47, STREAMED_SOUND_MISSION_BNK4_48, STREAMED_SOUND_MISSION_BNK4_49, STREAMED_SOUND_MISSION_BNK450A, STREAMED_SOUND_MISSION_BNK450B, STREAMED_SOUND_MISSION_BNK4_51, STREAMED_SOUND_MISSION_BNK4_94, STREAMED_SOUND_MISSION_BNK4_95, STREAMED_SOUND_MISSION_BNK4_96, STREAMED_SOUND_MISSION_BNK4_97, STREAMED_SOUND_MISSION_BNK4_98, STREAMED_SOUND_MISSION_BNK4_99, STREAMED_SOUND_MISSION_BUD1_1, STREAMED_SOUND_MISSION_BUD1_2, STREAMED_SOUND_MISSION_BUD1_3, STREAMED_SOUND_MISSION_BUD1_4, STREAMED_SOUND_MISSION_BUD1_5, STREAMED_SOUND_MISSION_BUD1_9, STREAMED_SOUND_MISSION_BUD1_10, STREAMED_SOUND_MISSION_BUD2_1, STREAMED_SOUND_MISSION_BUD2_2, STREAMED_SOUND_MISSION_BUD2_3, STREAMED_SOUND_MISSION_BUD2_4, STREAMED_SOUND_MISSION_BUD2_5, STREAMED_SOUND_MISSION_BUD2_6, STREAMED_SOUND_MISSION_BUD2_7, STREAMED_SOUND_MISSION_BUD3_1, STREAMED_SOUND_MISSION_BUD3_1A, STREAMED_SOUND_MISSION_BUD3_1B, STREAMED_SOUND_MISSION_BUD3_1C, STREAMED_SOUND_MISSION_BUD3_2, STREAMED_SOUND_MISSION_BUD3_3, STREAMED_SOUND_MISSION_BUD3_4, STREAMED_SOUND_MISSION_BUD3_5, STREAMED_SOUND_MISSION_BUD3_6, STREAMED_SOUND_MISSION_BUD3_7, STREAMED_SOUND_MISSION_BUD3_8A, STREAMED_SOUND_MISSION_BUD3_8B, STREAMED_SOUND_MISSION_BUD3_8C, STREAMED_SOUND_MISSION_BUD3_9A, STREAMED_SOUND_MISSION_BUD3_9B, STREAMED_SOUND_MISSION_BUD3_9C, STREAMED_SOUND_MISSION_CAP1_2, STREAMED_SOUND_MISSION_CAP1_3, STREAMED_SOUND_MISSION_CAP1_4, STREAMED_SOUND_MISSION_CAP1_5, STREAMED_SOUND_MISSION_CAP1_6, STREAMED_SOUND_MISSION_CAP1_7, STREAMED_SOUND_MISSION_CAP1_8, STREAMED_SOUND_MISSION_CAP1_9, STREAMED_SOUND_MISSION_CAP1_10, STREAMED_SOUND_MISSION_CAP1_11, STREAMED_SOUND_MISSION_CAP1_12, STREAMED_SOUND_MISSION_CNT1_1, STREAMED_SOUND_MISSION_CNT1_2, STREAMED_SOUND_MISSION_CNT1_3, STREAMED_SOUND_MISSION_CNT1_4, STREAMED_SOUND_MISSION_CNT1_5, STREAMED_SOUND_MISSION_CNT2_1, STREAMED_SOUND_MISSION_CNT2_2, STREAMED_SOUND_MISSION_CNT2_3, STREAMED_SOUND_MISSION_CNT2_4, STREAMED_SOUND_MISSION_COK1_1, STREAMED_SOUND_MISSION_COK1_2, STREAMED_SOUND_MISSION_COK1_3, STREAMED_SOUND_MISSION_COK1_4, STREAMED_SOUND_MISSION_COK1_5, STREAMED_SOUND_MISSION_COK1_6, STREAMED_SOUND_MISSION_COK2_1, STREAMED_SOUND_MISSION_COK2_2, STREAMED_SOUND_MISSION_COK2_3, STREAMED_SOUND_MISSION_COK2_4, STREAMED_SOUND_MISSION_COK2_5, STREAMED_SOUND_MISSION_COK2_6, STREAMED_SOUND_MISSION_COK2_7A, STREAMED_SOUND_MISSION_COK2_7B, STREAMED_SOUND_MISSION_COK2_7C, STREAMED_SOUND_MISSION_COK2_8A, STREAMED_SOUND_MISSION_COK2_8B, STREAMED_SOUND_MISSION_COK2_8C, STREAMED_SOUND_MISSION_COK2_8D, STREAMED_SOUND_MISSION_COK2_9, STREAMED_SOUND_MISSION_COK210A, STREAMED_SOUND_MISSION_COK210B, STREAMED_SOUND_MISSION_COK210C, STREAMED_SOUND_MISSION_COK212A, STREAMED_SOUND_MISSION_COK212B, STREAMED_SOUND_MISSION_COK2_13, STREAMED_SOUND_MISSION_COK2_14, STREAMED_SOUND_MISSION_COK2_15, STREAMED_SOUND_MISSION_COK2_16, STREAMED_SOUND_MISSION_COK2_20, STREAMED_SOUND_MISSION_COK2_21, STREAMED_SOUND_MISSION_COK2_22, STREAMED_SOUND_MISSION_COK3_1, STREAMED_SOUND_MISSION_COK3_2, STREAMED_SOUND_MISSION_COK3_3, STREAMED_SOUND_MISSION_COK3_4, STREAMED_SOUND_MISSION_COK4_1, STREAMED_SOUND_MISSION_COK4_2, STREAMED_SOUND_MISSION_COK4_3, STREAMED_SOUND_MISSION_COK4_4, STREAMED_SOUND_MISSION_COK4_5, STREAMED_SOUND_MISSION_COK4_6, STREAMED_SOUND_MISSION_COK4_7, STREAMED_SOUND_MISSION_COK4_8, STREAMED_SOUND_MISSION_COK4_9, STREAMED_SOUND_MISSION_COK4_9A, STREAMED_SOUND_MISSION_COK4_10, STREAMED_SOUND_MISSION_COK4_11, STREAMED_SOUND_MISSION_COK4_12, STREAMED_SOUND_MISSION_COK4_13, STREAMED_SOUND_MISSION_COK4_14, STREAMED_SOUND_MISSION_COK4_15, STREAMED_SOUND_MISSION_COK4_16, STREAMED_SOUND_MISSION_COK4_17, STREAMED_SOUND_MISSION_COK4_18, STREAMED_SOUND_MISSION_COK4_19, STREAMED_SOUND_MISSION_COK4_20, STREAMED_SOUND_MISSION_COK4_21, STREAMED_SOUND_MISSION_COK4_22, STREAMED_SOUND_MISSION_COK4_23, STREAMED_SOUND_MISSION_COK4_24, STREAMED_SOUND_MISSION_COK4_25, STREAMED_SOUND_MISSION_COK4_26, STREAMED_SOUND_MISSION_COK4_27, STREAMED_SOUND_MISSION_COL1_1, STREAMED_SOUND_MISSION_COL1_2, STREAMED_SOUND_MISSION_COL1_3, STREAMED_SOUND_MISSION_COL1_4, STREAMED_SOUND_MISSION_COL1_5, STREAMED_SOUND_MISSION_COL1_6, STREAMED_SOUND_MISSION_COL1_7, STREAMED_SOUND_MISSION_COL1_8, STREAMED_SOUND_MISSION_COL2_1, STREAMED_SOUND_MISSION_COL2_2, STREAMED_SOUND_MISSION_COL2_3, STREAMED_SOUND_MISSION_COL2_4, STREAMED_SOUND_MISSION_COL2_5, STREAMED_SOUND_MISSION_COL2_6A, STREAMED_SOUND_MISSION_COL2_7, STREAMED_SOUND_MISSION_COL2_8, STREAMED_SOUND_MISSION_COL2_9, STREAMED_SOUND_MISSION_COL2_10, STREAMED_SOUND_MISSION_COL2_11, STREAMED_SOUND_MISSION_COL2_12, STREAMED_SOUND_MISSION_COL2_13, STREAMED_SOUND_MISSION_COL2_14, STREAMED_SOUND_MISSION_COL2_15, STREAMED_SOUND_MISSION_COL2_16, STREAMED_SOUND_MISSION_COL3_1, STREAMED_SOUND_MISSION_COL3_2, STREAMED_SOUND_MISSION_COL3_2A, STREAMED_SOUND_MISSION_COL3_2B, STREAMED_SOUND_MISSION_COL3_3, STREAMED_SOUND_MISSION_COL3_4, STREAMED_SOUND_MISSION_COL3_5, STREAMED_SOUND_MISSION_COL3_6, STREAMED_SOUND_MISSION_COL3_7, STREAMED_SOUND_MISSION_COL3_8, STREAMED_SOUND_MISSION_COL3_9, STREAMED_SOUND_MISSION_COL3_10, STREAMED_SOUND_MISSION_COL3_11, STREAMED_SOUND_MISSION_COL3_12, STREAMED_SOUND_MISSION_COL3_13, STREAMED_SOUND_MISSION_COL3_14, STREAMED_SOUND_MISSION_COL3_15, STREAMED_SOUND_MISSION_COL3_16, STREAMED_SOUND_MISSION_COL3_17, STREAMED_SOUND_MISSION_COL3_18, STREAMED_SOUND_MISSION_COL3_19, STREAMED_SOUND_MISSION_COL3_20, STREAMED_SOUND_MISSION_COL3_21, STREAMED_SOUND_MISSION_COL3_23, STREAMED_SOUND_MISSION_COL3_24, STREAMED_SOUND_MISSION_COL3_25, STREAMED_SOUND_MISSION_COL4_1, STREAMED_SOUND_MISSION_COL4_2, STREAMED_SOUND_MISSION_COL4_3, STREAMED_SOUND_MISSION_COL4_4, STREAMED_SOUND_MISSION_COL4_5, STREAMED_SOUND_MISSION_COL4_6, STREAMED_SOUND_MISSION_COL4_7, STREAMED_SOUND_MISSION_COL4_8, STREAMED_SOUND_MISSION_COL4_9, STREAMED_SOUND_MISSION_COL4_10, STREAMED_SOUND_MISSION_COL4_11, STREAMED_SOUND_MISSION_COL4_12, STREAMED_SOUND_MISSION_COL4_13, STREAMED_SOUND_MISSION_COL4_14, STREAMED_SOUND_MISSION_COL4_15, STREAMED_SOUND_MISSION_COL4_16, STREAMED_SOUND_MISSION_COL4_17, STREAMED_SOUND_MISSION_COL4_18, STREAMED_SOUND_MISSION_COL4_19, STREAMED_SOUND_MISSION_COL4_20, STREAMED_SOUND_MISSION_COL4_21, STREAMED_SOUND_MISSION_COL4_22, STREAMED_SOUND_MISSION_COL4_23, STREAMED_SOUND_MISSION_COL4_24, STREAMED_SOUND_MISSION_COL4_25, STREAMED_SOUND_MISSION_COL4_26, STREAMED_SOUND_MISSION_COL5_1, STREAMED_SOUND_MISSION_COL5_2, STREAMED_SOUND_MISSION_COL5_3, STREAMED_SOUND_MISSION_COL5_4, STREAMED_SOUND_MISSION_COL5_5, STREAMED_SOUND_MISSION_COL5_6, STREAMED_SOUND_MISSION_COL5_7, STREAMED_SOUND_MISSION_COL5_8, STREAMED_SOUND_MISSION_COL5_9, STREAMED_SOUND_MISSION_COL5_10, STREAMED_SOUND_MISSION_COL5_11, STREAMED_SOUND_MISSION_COL5_12, STREAMED_SOUND_MISSION_COL5_13, STREAMED_SOUND_MISSION_COL5_14, STREAMED_SOUND_MISSION_COL5_15, STREAMED_SOUND_MISSION_COL5_16, STREAMED_SOUND_MISSION_COL5_17, STREAMED_SOUND_MISSION_COL5_18, STREAMED_SOUND_MISSION_COL5_19, STREAMED_SOUND_MISSION_COL5_20, STREAMED_SOUND_MISSION_COL5_21, STREAMED_SOUND_MISSION_COL5_22, STREAMED_SOUND_MISSION_CUB1_1, STREAMED_SOUND_MISSION_CUB1_2, STREAMED_SOUND_MISSION_CUB1_3, STREAMED_SOUND_MISSION_CUB1_4, STREAMED_SOUND_MISSION_CUB1_5, STREAMED_SOUND_MISSION_CUB1_6, STREAMED_SOUND_MISSION_CUB1_7, STREAMED_SOUND_MISSION_CUB1_8, STREAMED_SOUND_MISSION_CUB1_9, STREAMED_SOUND_MISSION_CUB1_10, STREAMED_SOUND_MISSION_CUB2_1, STREAMED_SOUND_MISSION_CUB2_2, STREAMED_SOUND_MISSION_CUB2_3A, STREAMED_SOUND_MISSION_CUB2_3B, STREAMED_SOUND_MISSION_CUB2_3C, STREAMED_SOUND_MISSION_CUB2_4A, STREAMED_SOUND_MISSION_CUB2_5, STREAMED_SOUND_MISSION_CUB2_6, STREAMED_SOUND_MISSION_CUB2_7, STREAMED_SOUND_MISSION_CUB2_8, STREAMED_SOUND_MISSION_CUB2_9, STREAMED_SOUND_MISSION_CUB2_10, STREAMED_SOUND_MISSION_CUB2_11, STREAMED_SOUND_MISSION_CUB3_1, STREAMED_SOUND_MISSION_CUB3_2, STREAMED_SOUND_MISSION_CUB3_3, STREAMED_SOUND_MISSION_CUB3_4, STREAMED_SOUND_MISSION_CUB4_1, STREAMED_SOUND_MISSION_CUB4_2, STREAMED_SOUND_MISSION_CUB4_3, STREAMED_SOUND_MISSION_CUB4_4, STREAMED_SOUND_MISSION_CUB4_5, STREAMED_SOUND_MISSION_CUB4_5A, STREAMED_SOUND_MISSION_CUB4_6, STREAMED_SOUND_MISSION_CUB4_7, STREAMED_SOUND_MISSION_CUB4_8, STREAMED_SOUND_MISSION_CUB4_9, STREAMED_SOUND_MISSION_CUB4_10, STREAMED_SOUND_MISSION_CUB4_11, STREAMED_SOUND_MISSION_CUB4_12, STREAMED_SOUND_MISSION_CUB4_13, STREAMED_SOUND_MISSION_CUB4_14, STREAMED_SOUND_MISSION_CUB4_15, STREAMED_SOUND_MISSION_CUB4_16, STREAMED_SOUND_MISSION_GOLF_1, STREAMED_SOUND_MISSION_GOLF_2, STREAMED_SOUND_MISSION_GOLF_3, STREAMED_SOUND_MISSION_BAR_1, STREAMED_SOUND_MISSION_BAR_2, STREAMED_SOUND_MISSION_BAR_3, STREAMED_SOUND_MISSION_BAR_4, STREAMED_SOUND_MISSION_BAR_5, STREAMED_SOUND_MISSION_BAR_6, STREAMED_SOUND_MISSION_BAR_7, STREAMED_SOUND_MISSION_BAR_8, STREAMED_SOUND_MISSION_STRIP_1, STREAMED_SOUND_MISSION_STRIP_2, STREAMED_SOUND_MISSION_STRIP_3, STREAMED_SOUND_MISSION_STRIP_4, STREAMED_SOUND_MISSION_STRIP_5, STREAMED_SOUND_MISSION_STRIP_6, STREAMED_SOUND_MISSION_STRIP_7, STREAMED_SOUND_MISSION_STRIP_8, STREAMED_SOUND_MISSION_STRIP_9, STREAMED_SOUND_MISSION_STAR_1, STREAMED_SOUND_MISSION_STAR_2, STREAMED_SOUND_MISSION_STAR_3, STREAMED_SOUND_MISSION_STAR_4, STREAMED_SOUND_MISSION_FIN_1A, STREAMED_SOUND_MISSION_FIN_1B, STREAMED_SOUND_MISSION_FIN_1C, STREAMED_SOUND_MISSION_FIN_2B, STREAMED_SOUND_MISSION_FIN_2C, STREAMED_SOUND_MISSION_FIN_3, STREAMED_SOUND_MISSION_FIN_4, STREAMED_SOUND_MISSION_FIN_5, STREAMED_SOUND_MISSION_FIN_6, STREAMED_SOUND_MISSION_FIN_10, STREAMED_SOUND_MISSION_FIN_11A, STREAMED_SOUND_MISSION_FIN_11B, STREAMED_SOUND_MISSION_FIN_12A, STREAMED_SOUND_MISSION_FIN_12B, STREAMED_SOUND_MISSION_FIN_12C, STREAMED_SOUND_MISSION_FIN_13, STREAMED_SOUND_MISSION_FINKILL, STREAMED_SOUND_MISSION_LAW1_1, STREAMED_SOUND_MISSION_LAW1_2, STREAMED_SOUND_MISSION_LAW1_3, STREAMED_SOUND_MISSION_LAW1_4, STREAMED_SOUND_MISSION_LAW1_5, STREAMED_SOUND_MISSION_LAW1_6, STREAMED_SOUND_MISSION_LAW1_7, STREAMED_SOUND_MISSION_LAW1_8, STREAMED_SOUND_MISSION_LAW1_9, STREAMED_SOUND_MISSION_LAW1_10, STREAMED_SOUND_MISSION_LAW2_1, STREAMED_SOUND_MISSION_LAW2_2, STREAMED_SOUND_MISSION_LAW2_3, STREAMED_SOUND_MISSION_LAW2_4, STREAMED_SOUND_MISSION_LAW2_5, STREAMED_SOUND_MISSION_LAW2_6, STREAMED_SOUND_MISSION_LAW2_7, STREAMED_SOUND_MISSION_LAW2_8, STREAMED_SOUND_MISSION_LAW2_9, STREAMED_SOUND_MISSION_LAW2_10, STREAMED_SOUND_MISSION_LAW3_1, STREAMED_SOUND_MISSION_LAW3_2, STREAMED_SOUND_MISSION_LAW3_3, STREAMED_SOUND_MISSION_LAW3_4, STREAMED_SOUND_MISSION_LAW3_5, STREAMED_SOUND_MISSION_LAW3_6, STREAMED_SOUND_MISSION_LAW3_10, STREAMED_SOUND_MISSION_LAW3_11, STREAMED_SOUND_MISSION_LAW3_12, STREAMED_SOUND_MISSION_LAW3_13, STREAMED_SOUND_MISSION_LAW3_14, STREAMED_SOUND_MISSION_LAW3_16, STREAMED_SOUND_MISSION_LAW3_17, STREAMED_SOUND_MISSION_LAW3_18, STREAMED_SOUND_MISSION_LAW3_19, STREAMED_SOUND_MISSION_LAW3_20, STREAMED_SOUND_MISSION_LAW3_21, STREAMED_SOUND_MISSION_LAW3_22, STREAMED_SOUND_MISSION_LAW3_23, STREAMED_SOUND_MISSION_LAW3_24, STREAMED_SOUND_MISSION_LAW3_25, STREAMED_SOUND_MISSION_LAW4_1A, STREAMED_SOUND_MISSION_LAW4_1B, STREAMED_SOUND_MISSION_LAW4_1C, STREAMED_SOUND_MISSION_LAW4_1D, STREAMED_SOUND_MISSION_LAW4_10, STREAMED_SOUND_MISSION_LAW4_3, STREAMED_SOUND_MISSION_LAW4_4, STREAMED_SOUND_MISSION_LAW4_5, STREAMED_SOUND_MISSION_LAW4_6, STREAMED_SOUND_MISSION_LAW4_7, STREAMED_SOUND_MISSION_LAW4_8, STREAMED_SOUND_MISSION_LAW4_9, STREAMED_SOUND_MISSION_PHIL1_2, STREAMED_SOUND_MISSION_PHIL1_3, STREAMED_SOUND_MISSION_PHIL2_1, STREAMED_SOUND_MISSION_PHIL2_2, STREAMED_SOUND_MISSION_PHIL2_3, STREAMED_SOUND_MISSION_PHIL2_4, STREAMED_SOUND_MISSION_PHIL2_5, STREAMED_SOUND_MISSION_PHIL2_6, STREAMED_SOUND_MISSION_PHIL2_7, STREAMED_SOUND_MISSION_PHIL2_8, STREAMED_SOUND_MISSION_PHIL2_9, STREAMED_SOUND_MISSION_PHIL210, STREAMED_SOUND_MISSION_PHIL211, STREAMED_SOUND_MISSION_PORN1_1, STREAMED_SOUND_MISSION_PORN1_2, STREAMED_SOUND_MISSION_PORN1_3, STREAMED_SOUND_MISSION_PRN1_3A, STREAMED_SOUND_MISSION_PORN1_4, STREAMED_SOUND_MISSION_PORN1_5, STREAMED_SOUND_MISSION_PORN1_6, STREAMED_SOUND_MISSION_PORN1_7, STREAMED_SOUND_MISSION_PORN1_8, STREAMED_SOUND_MISSION_PORN1_9, STREAMED_SOUND_MISSION_PRN1_10, STREAMED_SOUND_MISSION_PRN1_11, STREAMED_SOUND_MISSION_PRN1_12, STREAMED_SOUND_MISSION_PRN1_13, STREAMED_SOUND_MISSION_PRN1_14, STREAMED_SOUND_MISSION_PRN1_15, STREAMED_SOUND_MISSION_PRN1_16, STREAMED_SOUND_MISSION_PRN1_17, STREAMED_SOUND_MISSION_PRN1_18, STREAMED_SOUND_MISSION_PRN1_19, STREAMED_SOUND_MISSION_PRN1_20, STREAMED_SOUND_MISSION_PRN1_21, STREAMED_SOUND_MISSION_PORN3_1, STREAMED_SOUND_MISSION_PORN3_2, STREAMED_SOUND_MISSION_PORN3_3, STREAMED_SOUND_MISSION_PORN3_4, STREAMED_SOUND_MISSION_PSYCH_1, STREAMED_SOUND_MISSION_PSYCH_2, STREAMED_SOUND_MISSION_ROK2_01, STREAMED_SOUND_MISSION_ROK3_1, STREAMED_SOUND_MISSION_ROK3_2, STREAMED_SOUND_MISSION_ROK3_3, STREAMED_SOUND_MISSION_ROK3_4, STREAMED_SOUND_MISSION_ROK3_5, STREAMED_SOUND_MISSION_ROK3_6, STREAMED_SOUND_MISSION_ROK3_7, STREAMED_SOUND_MISSION_ROK3_8, STREAMED_SOUND_MISSION_ROK3_9, STREAMED_SOUND_MISSION_ROK3_10, STREAMED_SOUND_MISSION_ROK3_11, STREAMED_SOUND_MISSION_ROK3_12, STREAMED_SOUND_MISSION_ROK3_13, STREAMED_SOUND_MISSION_ROK3_14, STREAMED_SOUND_MISSION_ROK3_15, STREAMED_SOUND_MISSION_ROK3_16, STREAMED_SOUND_MISSION_ROK3_17, STREAMED_SOUND_MISSION_ROK3_18, STREAMED_SOUND_MISSION_ROK3_19, STREAMED_SOUND_MISSION_ROK3_20, STREAMED_SOUND_MISSION_ROK3_21, STREAMED_SOUND_MISSION_ROK3_22, STREAMED_SOUND_MISSION_ROK3_23, STREAMED_SOUND_MISSION_ROK3_24, STREAMED_SOUND_MISSION_ROK3_25, STREAMED_SOUND_MISSION_ROK3_26, STREAMED_SOUND_MISSION_ROK3_27, STREAMED_SOUND_MISSION_ROK3_62, STREAMED_SOUND_MISSION_ROK3_63, STREAMED_SOUND_MISSION_ROK3_64, STREAMED_SOUND_MISSION_ROK3_65, STREAMED_SOUND_MISSION_ROK3_66, STREAMED_SOUND_MISSION_ROK3_67, STREAMED_SOUND_MISSION_ROK3_68, STREAMED_SOUND_MISSION_ROK3_69, STREAMED_SOUND_MISSION_ROK3_70, STREAMED_SOUND_MISSION_ROK3_71, STREAMED_SOUND_MISSION_ROK3_73, STREAMED_SOUND_MISSION_RESC_1, STREAMED_SOUND_MISSION_RESC_2, STREAMED_SOUND_MISSION_RESC_3, STREAMED_SOUND_MISSION_RESC_4, STREAMED_SOUND_MISSION_RESC_5, STREAMED_SOUND_MISSION_RESC_6, STREAMED_SOUND_MISSION_RESC_7, STREAMED_SOUND_MISSION_RESC_8, STREAMED_SOUND_MISSION_RESC_9, STREAMED_SOUND_MISSION_RESC_10, STREAMED_SOUND_MISSION_ROK1_1A, STREAMED_SOUND_MISSION_ROK1_1B, STREAMED_SOUND_MISSION_ROK1_5, STREAMED_SOUND_MISSION_ROK1_6, STREAMED_SOUND_MISSION_ROK1_7, STREAMED_SOUND_MISSION_ROK1_8, STREAMED_SOUND_MISSION_ROK1_9, STREAMED_SOUND_MISSION_TAX1_1, STREAMED_SOUND_MISSION_TAX1_2, STREAMED_SOUND_MISSION_TAX1_3, STREAMED_SOUND_MISSION_TAX1_4, STREAMED_SOUND_MISSION_TAX1_5, STREAMED_SOUND_MISSION_TAX2_1, STREAMED_SOUND_MISSION_TAX2_2, STREAMED_SOUND_MISSION_TAX2_3, STREAMED_SOUND_MISSION_TAX2_4, STREAMED_SOUND_MISSION_TAX2_5, STREAMED_SOUND_MISSION_TAX2_6, STREAMED_SOUND_MISSION_TAX2_7, STREAMED_SOUND_MISSION_TAX3_1, STREAMED_SOUND_MISSION_TAX3_2, STREAMED_SOUND_MISSION_TAX3_3, STREAMED_SOUND_MISSION_TAX3_4, STREAMED_SOUND_MISSION_TAX3_5, STREAMED_SOUND_MISSION_TEX1_1, STREAMED_SOUND_MISSION_TEX1_2, STREAMED_SOUND_MISSION_TEX1_3, STREAMED_SOUND_MISSION_TEX1_4, STREAMED_SOUND_MISSION_TEX1_5, STREAMED_SOUND_MISSION_TEX1_6, STREAMED_SOUND_MISSION_TEX2_1, STREAMED_SOUND_MISSION_TEX3_1, STREAMED_SOUND_MISSION_TEX3_2, STREAMED_SOUND_MISSION_TEX3_3, STREAMED_SOUND_MISSION_TEX3_4, STREAMED_SOUND_MISSION_TEX3_5, STREAMED_SOUND_MISSION_TEX3_6, STREAMED_SOUND_MISSION_TEX3_7, STREAMED_SOUND_MISSION_TEX3_8, STREAMED_SOUND_MISSION_HAT_1A, STREAMED_SOUND_MISSION_INTRO1, STREAMED_SOUND_MISSION_INTRO2, STREAMED_SOUND_MISSION_INTRO3, STREAMED_SOUND_MISSION_INTRO4, STREAMED_SOUND_MISSION_MOB_01A, STREAMED_SOUND_MISSION_MOB_01B, STREAMED_SOUND_MISSION_MOB_01C, STREAMED_SOUND_MISSION_MOB_02A, STREAMED_SOUND_MISSION_MOB_02B, STREAMED_SOUND_MISSION_MOB_02C, STREAMED_SOUND_MISSION_MOB_03A, STREAMED_SOUND_MISSION_MOB_03B, STREAMED_SOUND_MISSION_MOB_03C, STREAMED_SOUND_MISSION_MOB_03D, STREAMED_SOUND_MISSION_MOB_03E, STREAMED_SOUND_MISSION_SHARK_1, STREAMED_SOUND_MISSION_SHARK_2, STREAMED_SOUND_MISSION_SHARK_3, STREAMED_SOUND_MISSION_SHARK_4, STREAMED_SOUND_MISSION_SHARK_5, STREAMED_SOUND_MISSION_MOB_04A, STREAMED_SOUND_MISSION_MOB_04B, STREAMED_SOUND_MISSION_MOB_04C, STREAMED_SOUND_MISSION_MOB_04D, STREAMED_SOUND_MISSION_MOB_05A, STREAMED_SOUND_MISSION_MOB_05B, STREAMED_SOUND_MISSION_MOB_05C, STREAMED_SOUND_MISSION_MOB_05D, STREAMED_SOUND_MISSION_MOB_06A, STREAMED_SOUND_MISSION_MOB_06B, STREAMED_SOUND_MISSION_MOB_06C, STREAMED_SOUND_MISSION_MOB_07A, STREAMED_SOUND_MISSION_MOB_07B, STREAMED_SOUND_MISSION_MOB_08A, STREAMED_SOUND_MISSION_MOB_08B, STREAMED_SOUND_MISSION_MOB_08C, STREAMED_SOUND_MISSION_MOB_08D, STREAMED_SOUND_MISSION_MOB_08E, STREAMED_SOUND_MISSION_MOB_08F, STREAMED_SOUND_MISSION_MOB_08G, STREAMED_SOUND_MISSION_MOB_09A, STREAMED_SOUND_MISSION_MOB_09B, STREAMED_SOUND_MISSION_MOB_09C, STREAMED_SOUND_MISSION_MOB_09D, STREAMED_SOUND_MISSION_MOB_09E, STREAMED_SOUND_MISSION_MOB_09F, STREAMED_SOUND_MISSION_MOB_10A, STREAMED_SOUND_MISSION_MOB_10B, STREAMED_SOUND_MISSION_MOB_10C, STREAMED_SOUND_MISSION_MOB_10D, STREAMED_SOUND_MISSION_MOB_10E, STREAMED_SOUND_MISSION_MOB_11A, STREAMED_SOUND_MISSION_MOB_11B, STREAMED_SOUND_MISSION_MOB_11C, STREAMED_SOUND_MISSION_MOB_11D, STREAMED_SOUND_MISSION_MOB_11E, STREAMED_SOUND_MISSION_MOB_11F, STREAMED_SOUND_MISSION_MOB_14A, STREAMED_SOUND_MISSION_MOB_14B, STREAMED_SOUND_MISSION_MOB_14C, STREAMED_SOUND_MISSION_MOB_14D, STREAMED_SOUND_MISSION_MOB_14E, STREAMED_SOUND_MISSION_MOB_14F, STREAMED_SOUND_MISSION_MOB_14G, STREAMED_SOUND_MISSION_MOB_14H, STREAMED_SOUND_MISSION_MOB_16A, STREAMED_SOUND_MISSION_MOB_16B, STREAMED_SOUND_MISSION_MOB_16C, STREAMED_SOUND_MISSION_MOB_16D, STREAMED_SOUND_MISSION_MOB_16E, STREAMED_SOUND_MISSION_MOB_16F, STREAMED_SOUND_MISSION_MOB_16G, STREAMED_SOUND_MISSION_MOB_17A, STREAMED_SOUND_MISSION_MOB_17B, STREAMED_SOUND_MISSION_MOB_17C, STREAMED_SOUND_MISSION_MOB_17D, STREAMED_SOUND_MISSION_MOB_17E, STREAMED_SOUND_MISSION_MOB_17G, STREAMED_SOUND_MISSION_MOB_17H, STREAMED_SOUND_MISSION_MOB_17I, STREAMED_SOUND_MISSION_MOB_17J, STREAMED_SOUND_MISSION_MOB_17K, STREAMED_SOUND_MISSION_MOB_17L, STREAMED_SOUND_MISSION_MOB_18A, STREAMED_SOUND_MISSION_MOB_18B, STREAMED_SOUND_MISSION_MOB_18C, STREAMED_SOUND_MISSION_MOB_18D, STREAMED_SOUND_MISSION_MOB_18E, STREAMED_SOUND_MISSION_MOB_18F, STREAMED_SOUND_MISSION_MOB_18G, STREAMED_SOUND_MISSION_MOB_20A, STREAMED_SOUND_MISSION_MOB_20B, STREAMED_SOUND_MISSION_MOB_20C, STREAMED_SOUND_MISSION_MOB_20D, STREAMED_SOUND_MISSION_MOB_20E, STREAMED_SOUND_MISSION_MOB_24A, STREAMED_SOUND_MISSION_MOB_24B, STREAMED_SOUND_MISSION_MOB_24C, STREAMED_SOUND_MISSION_MOB_24D, STREAMED_SOUND_MISSION_MOB_24E, STREAMED_SOUND_MISSION_MOB_24F, STREAMED_SOUND_MISSION_MOB_24G, STREAMED_SOUND_MISSION_MOB_24H, STREAMED_SOUND_MISSION_MOB_25A, STREAMED_SOUND_MISSION_MOB_25B, STREAMED_SOUND_MISSION_MOB_25C, STREAMED_SOUND_MISSION_MOB_25D, STREAMED_SOUND_MISSION_MOB_26A, STREAMED_SOUND_MISSION_MOB_26B, STREAMED_SOUND_MISSION_MOB_26C, STREAMED_SOUND_MISSION_MOB_26D, STREAMED_SOUND_MISSION_MOB_26E, STREAMED_SOUND_MISSION_MOB_29A, STREAMED_SOUND_MISSION_MOB_29B, STREAMED_SOUND_MISSION_MOB_29C, STREAMED_SOUND_MISSION_MOB_29D, STREAMED_SOUND_MISSION_MOB_29E, STREAMED_SOUND_MISSION_MOB_29F, STREAMED_SOUND_MISSION_MOB_29G, STREAMED_SOUND_MISSION_MOB_30A, STREAMED_SOUND_MISSION_MOB_30B, STREAMED_SOUND_MISSION_MOB_30C, STREAMED_SOUND_MISSION_MOB_30D, STREAMED_SOUND_MISSION_MOB_30E, STREAMED_SOUND_MISSION_MOB_30F, STREAMED_SOUND_MISSION_MOB_33A, STREAMED_SOUND_MISSION_MOB_33B, STREAMED_SOUND_MISSION_MOB_33C, STREAMED_SOUND_MISSION_MOB_33D, STREAMED_SOUND_MISSION_MOB_34A, STREAMED_SOUND_MISSION_MOB_34B, STREAMED_SOUND_MISSION_MOB_34C, STREAMED_SOUND_MISSION_MOB_34D, STREAMED_SOUND_MISSION_MOB_35A, STREAMED_SOUND_MISSION_MOB_35B, STREAMED_SOUND_MISSION_MOB_35C, STREAMED_SOUND_MISSION_MOB_35D, STREAMED_SOUND_MISSION_MOB_36A, STREAMED_SOUND_MISSION_MOB_36B, STREAMED_SOUND_MISSION_MOB_36C, STREAMED_SOUND_MISSION_MOB_40A, STREAMED_SOUND_MISSION_MOB_40B, STREAMED_SOUND_MISSION_MOB_40C, STREAMED_SOUND_MISSION_MOB_40D, STREAMED_SOUND_MISSION_MOB_40E, STREAMED_SOUND_MISSION_MOB_40F, STREAMED_SOUND_MISSION_MOB_40G, STREAMED_SOUND_MISSION_MOB_40H, STREAMED_SOUND_MISSION_MOB_40I, STREAMED_SOUND_MISSION_MOB_41A, STREAMED_SOUND_MISSION_MOB_41B, STREAMED_SOUND_MISSION_MOB_41C, STREAMED_SOUND_MISSION_MOB_41D, STREAMED_SOUND_MISSION_MOB_41E, STREAMED_SOUND_MISSION_MOB_41F, STREAMED_SOUND_MISSION_MOB_41G, STREAMED_SOUND_MISSION_MOB_41H, STREAMED_SOUND_MISSION_MOB_42A, STREAMED_SOUND_MISSION_MOB_42B, STREAMED_SOUND_MISSION_MOB_42C, STREAMED_SOUND_MISSION_MOB_42D, STREAMED_SOUND_MISSION_MOB_42E, STREAMED_SOUND_MISSION_MOB_43A, STREAMED_SOUND_MISSION_MOB_43B, STREAMED_SOUND_MISSION_MOB_43C, STREAMED_SOUND_MISSION_MOB_43D, STREAMED_SOUND_MISSION_MOB_43E, STREAMED_SOUND_MISSION_MOB_43F, STREAMED_SOUND_MISSION_MOB_43G, STREAMED_SOUND_MISSION_MOB_43H, STREAMED_SOUND_MISSION_MOB_45A, STREAMED_SOUND_MISSION_MOB_45B, STREAMED_SOUND_MISSION_MOB_45C, STREAMED_SOUND_MISSION_MOB_45D, STREAMED_SOUND_MISSION_MOB_45E, STREAMED_SOUND_MISSION_MOB_45F, STREAMED_SOUND_MISSION_MOB_45G, STREAMED_SOUND_MISSION_MOB_45H, STREAMED_SOUND_MISSION_MOB_45I, STREAMED_SOUND_MISSION_MOB_45J, STREAMED_SOUND_MISSION_MOB_45K, STREAMED_SOUND_MISSION_MOB_45L, STREAMED_SOUND_MISSION_MOB_45M, STREAMED_SOUND_MISSION_MOB_45N, STREAMED_SOUND_MISSION_MOB_46A, STREAMED_SOUND_MISSION_MOB_46B, STREAMED_SOUND_MISSION_MOB_46C, STREAMED_SOUND_MISSION_MOB_46D, STREAMED_SOUND_MISSION_MOB_46E, STREAMED_SOUND_MISSION_MOB_46F, STREAMED_SOUND_MISSION_MOB_46G, STREAMED_SOUND_MISSION_MOB_46H, STREAMED_SOUND_MISSION_MOB_47A, STREAMED_SOUND_MISSION_MOB_52A, STREAMED_SOUND_MISSION_MOB_52B, STREAMED_SOUND_MISSION_MOB_52C, STREAMED_SOUND_MISSION_MOB_52D, STREAMED_SOUND_MISSION_MOB_52E, STREAMED_SOUND_MISSION_MOB_52F, STREAMED_SOUND_MISSION_MOB_52G, STREAMED_SOUND_MISSION_MOB_52H, STREAMED_SOUND_MISSION_MOB_54A, STREAMED_SOUND_MISSION_MOB_54B, STREAMED_SOUND_MISSION_MOB_54C, STREAMED_SOUND_MISSION_MOB_54D, STREAMED_SOUND_MISSION_MOB_54E, STREAMED_SOUND_MISSION_MOB_55A, STREAMED_SOUND_MISSION_MOB_55B, STREAMED_SOUND_MISSION_MOB_55C, STREAMED_SOUND_MISSION_MOB_55D, STREAMED_SOUND_MISSION_MOB_55E, STREAMED_SOUND_MISSION_MOB_55F, STREAMED_SOUND_MISSION_MOB_56A, STREAMED_SOUND_MISSION_MOB_56B, STREAMED_SOUND_MISSION_MOB_56C, STREAMED_SOUND_MISSION_MOB_56D, STREAMED_SOUND_MISSION_MOB_56E, STREAMED_SOUND_MISSION_MOB_56F, STREAMED_SOUND_MISSION_MOB_57A, STREAMED_SOUND_MISSION_MOB_57B, STREAMED_SOUND_MISSION_MOB_57C, STREAMED_SOUND_MISSION_MOB_57D, STREAMED_SOUND_MISSION_MOB_57E, STREAMED_SOUND_MISSION_MOB_58A, STREAMED_SOUND_MISSION_MOB_58B, STREAMED_SOUND_MISSION_MOB_58C, STREAMED_SOUND_MISSION_MOB_58D, STREAMED_SOUND_MISSION_MOB_58E, STREAMED_SOUND_MISSION_MOB_58F, STREAMED_SOUND_MISSION_MOB_58G, STREAMED_SOUND_MISSION_MOB_61A, STREAMED_SOUND_MISSION_MOB_61B, STREAMED_SOUND_MISSION_MOB_62A, STREAMED_SOUND_MISSION_MOB_62B, STREAMED_SOUND_MISSION_MOB_62C, STREAMED_SOUND_MISSION_MOB_62D, STREAMED_SOUND_MISSION_MOB_63A, STREAMED_SOUND_MISSION_MOB_63B, STREAMED_SOUND_MISSION_MOB_63C, STREAMED_SOUND_MISSION_MOB_63D, STREAMED_SOUND_MISSION_MOB_63E, STREAMED_SOUND_MISSION_MOB_63F, STREAMED_SOUND_MISSION_MOB_63G, STREAMED_SOUND_MISSION_MOB_63H, STREAMED_SOUND_MISSION_MOB_63I, STREAMED_SOUND_MISSION_MOB_63J, STREAMED_SOUND_MISSION_MOB_66A, STREAMED_SOUND_MISSION_MOB_66B, STREAMED_SOUND_MISSION_MOB_68A, STREAMED_SOUND_MISSION_MOB_68B, STREAMED_SOUND_MISSION_MOB_68C, STREAMED_SOUND_MISSION_MOB_68D, STREAMED_SOUND_MISSION_MOB_70A, STREAMED_SOUND_MISSION_MOB_70B, STREAMED_SOUND_MISSION_MOB_71A, STREAMED_SOUND_MISSION_MOB_71B, STREAMED_SOUND_MISSION_MOB_71C, STREAMED_SOUND_MISSION_MOB_71D, STREAMED_SOUND_MISSION_MOB_71E, STREAMED_SOUND_MISSION_MOB_71F, STREAMED_SOUND_MISSION_MOB_71G, STREAMED_SOUND_MISSION_MOB_71H, STREAMED_SOUND_MISSION_MOB_71I, STREAMED_SOUND_MISSION_MOB_71J, STREAMED_SOUND_MISSION_MOB_71K, STREAMED_SOUND_MISSION_MOB_71L, STREAMED_SOUND_MISSION_MOB_71M, STREAMED_SOUND_MISSION_MOB_71N, STREAMED_SOUND_MISSION_MOB_72A, STREAMED_SOUND_MISSION_MOB_72B, STREAMED_SOUND_MISSION_MOB_72C, STREAMED_SOUND_MISSION_MOB_72D, STREAMED_SOUND_MISSION_MOB_72E, STREAMED_SOUND_MISSION_MOB_72F, STREAMED_SOUND_MISSION_MOB_72G, STREAMED_SOUND_MISSION_MOB_73A, STREAMED_SOUND_MISSION_MOB_73C, STREAMED_SOUND_MISSION_MOB_73D, STREAMED_SOUND_MISSION_MOB_73F, STREAMED_SOUND_MISSION_MOB_73G, STREAMED_SOUND_MISSION_MOB_73I, STREAMED_SOUND_MISSION_MOB_95A, STREAMED_SOUND_MISSION_MOB_96A, STREAMED_SOUND_MISSION_MOB_98A, STREAMED_SOUND_MISSION_MOB_99A, STREAMED_SOUND_MISSION_JOB1_1B, STREAMED_SOUND_MISSION_JOB1_1C, STREAMED_SOUND_MISSION_JOB1_1D, STREAMED_SOUND_MISSION_JOB2_1B, STREAMED_SOUND_MISSION_JOB2_2, STREAMED_SOUND_MISSION_JOB2_3, STREAMED_SOUND_MISSION_JOB2_4, STREAMED_SOUND_MISSION_JOB2_5, STREAMED_SOUND_MISSION_JOB2_6, STREAMED_SOUND_MISSION_JOB2_7, STREAMED_SOUND_MISSION_JOB2_8, STREAMED_SOUND_MISSION_JOB2_9, STREAMED_SOUND_MISSION_JOB3_1, STREAMED_SOUND_MISSION_JOB3_2, STREAMED_SOUND_MISSION_JOB3_3, STREAMED_SOUND_MISSION_JOB4_1, STREAMED_SOUND_MISSION_JOB4_2, STREAMED_SOUND_MISSION_JOB4_3, STREAMED_SOUND_MISSION_JOB5_1, STREAMED_SOUND_MISSION_JOB5_2, STREAMED_SOUND_MISSION_JOB5_3, STREAMED_SOUND_MISSION_BJM1_20, STREAMED_SOUND_MISSION_BJM1_4, STREAMED_SOUND_MISSION_BJM1_5, STREAMED_SOUND_MISSION_MERC_39, STREAMED_SOUND_MISSION_MONO_1, STREAMED_SOUND_MISSION_MONO_2, STREAMED_SOUND_MISSION_MONO_3, STREAMED_SOUND_MISSION_MONO_4, STREAMED_SOUND_MISSION_MONO_5, STREAMED_SOUND_MISSION_MONO_6, STREAMED_SOUND_MISSION_MONO_7, STREAMED_SOUND_MISSION_MONO_8, STREAMED_SOUND_MISSION_MONO_9, STREAMED_SOUND_MISSION_MONO10, STREAMED_SOUND_MISSION_MONO11, STREAMED_SOUND_MISSION_MONO12, STREAMED_SOUND_MISSION_MONO13, STREAMED_SOUND_MISSION_MONO14, STREAMED_SOUND_MISSION_MONO15, STREAMED_SOUND_MISSION_MONO16, STREAMED_SOUND_MISSION_FUD_01, STREAMED_SOUND_MISSION_FUD_02, STREAMED_SOUND_MISSION_FUD_03, STREAMED_SOUND_MISSION_FUD_04, STREAMED_SOUND_MISSION_FUD_05, STREAMED_SOUND_MISSION_FUD_06, STREAMED_SOUND_MISSION_FUD_07, STREAMED_SOUND_MISSION_FUD_08, STREAMED_SOUND_MISSION_FUD_09, STREAMED_SOUND_MISSION_FUD_10, STREAMED_SOUND_MISSION_FUD_11, STREAMED_SOUND_MISSION_FUD_12, STREAMED_SOUND_MISSION_FUD_13, STREAMED_SOUND_MISSION_FUD_14, STREAMED_SOUND_MISSION_FUD_15, STREAMED_SOUND_MISSION_FUD_16, STREAMED_SOUND_MISSION_FUD_17, STREAMED_SOUND_MISSION_FUD_18, STREAMED_SOUND_MISSION_FUD_19, STREAMED_SOUND_MISSION_FUD_20, STREAMED_SOUND_MISSION_BURG_01, STREAMED_SOUND_MISSION_BURG_02, STREAMED_SOUND_MISSION_BURG_03, STREAMED_SOUND_MISSION_BURG_04, STREAMED_SOUND_MISSION_BURG_05, STREAMED_SOUND_MISSION_BURG_06, STREAMED_SOUND_MISSION_BURG_07, STREAMED_SOUND_MISSION_BURG_08, STREAMED_SOUND_MISSION_BURG_09, STREAMED_SOUND_MISSION_BURG_10, STREAMED_SOUND_MISSION_BURG_11, STREAMED_SOUND_MISSION_BURG_12, STREAMED_SOUND_MISSION_CRUST01, STREAMED_SOUND_MISSION_CRUST02, STREAMED_SOUND_MISSION_CRUST03, STREAMED_SOUND_MISSION_CRUST04, STREAMED_SOUND_MISSION_CRUST05, STREAMED_SOUND_MISSION_CRUST06, STREAMED_SOUND_MISSION_CRUST07, STREAMED_SOUND_MISSION_CRUST08, STREAMED_SOUND_MISSION_CRUST09, STREAMED_SOUND_MISSION_BAND_01, STREAMED_SOUND_MISSION_BAND_02, STREAMED_SOUND_MISSION_BAND_03, STREAMED_SOUND_MISSION_BAND_04, STREAMED_SOUND_MISSION_BAND_05, STREAMED_SOUND_MISSION_BAND_06, STREAMED_SOUND_MISSION_BAND_07, STREAMED_SOUND_MISSION_BAND_08, STREAMED_SOUND_MISSION_SHAFT01, STREAMED_SOUND_MISSION_SHAFT02, STREAMED_SOUND_MISSION_SHAFT03, STREAMED_SOUND_MISSION_SHAFT04, STREAMED_SOUND_MISSION_SHAFT05, STREAMED_SOUND_MISSION_SHAFT06, STREAMED_SOUND_MISSION_SHAFT07, STREAMED_SOUND_MISSION_SHAFT08, STREAMED_SOUND_MISSION_PISS_01, STREAMED_SOUND_MISSION_PISS_02, STREAMED_SOUND_MISSION_PISS_03, STREAMED_SOUND_MISSION_PISS_04, STREAMED_SOUND_MISSION_PISS_05, STREAMED_SOUND_MISSION_PISS_06, STREAMED_SOUND_MISSION_PISS_07, STREAMED_SOUND_MISSION_PISS_08, STREAMED_SOUND_MISSION_PISS_09, STREAMED_SOUND_MISSION_PISS_10, STREAMED_SOUND_MISSION_PISS_11, STREAMED_SOUND_MISSION_PISS_12, STREAMED_SOUND_MISSION_PISS_13, STREAMED_SOUND_MISSION_PISS_14, STREAMED_SOUND_MISSION_PISS_15, STREAMED_SOUND_MISSION_PISS_16, STREAMED_SOUND_MISSION_PISS_17, STREAMED_SOUND_MISSION_PISS_18, STREAMED_SOUND_MISSION_PISS_19, STREAMED_SOUND_MISSION_GIMME01, STREAMED_SOUND_MISSION_GIMME02, STREAMED_SOUND_MISSION_GIMME03, STREAMED_SOUND_MISSION_GIMME04, STREAMED_SOUND_MISSION_GIMME05, STREAMED_SOUND_MISSION_GIMME06, STREAMED_SOUND_MISSION_GIMME07, STREAMED_SOUND_MISSION_GIMME08, STREAMED_SOUND_MISSION_GIMME09, STREAMED_SOUND_MISSION_GIMME10, STREAMED_SOUND_MISSION_GIMME11, STREAMED_SOUND_MISSION_GIMME12, STREAMED_SOUND_MISSION_GIMME13, STREAMED_SOUND_MISSION_GIMME14, STREAMED_SOUND_MISSION_GIMME15, STREAMED_SOUND_MISSION_BUST_01, STREAMED_SOUND_MISSION_BUST_02, STREAMED_SOUND_MISSION_BUST_03, STREAMED_SOUND_MISSION_BUST_04, STREAMED_SOUND_MISSION_BUST_05, STREAMED_SOUND_MISSION_BUST_06, STREAMED_SOUND_MISSION_BUST_07, STREAMED_SOUND_MISSION_BUST_08, STREAMED_SOUND_MISSION_BUST_09, STREAMED_SOUND_MISSION_BUST_10, STREAMED_SOUND_MISSION_BUST_11, STREAMED_SOUND_MISSION_BUST_12, STREAMED_SOUND_MISSION_BUST_13, STREAMED_SOUND_MISSION_BUST_14, STREAMED_SOUND_MISSION_BUST_15, STREAMED_SOUND_MISSION_BUST_16, STREAMED_SOUND_MISSION_BUST_17, STREAMED_SOUND_MISSION_BUST_18, STREAMED_SOUND_MISSION_BUST_19, STREAMED_SOUND_MISSION_BUST_20, STREAMED_SOUND_MISSION_BUST_21, STREAMED_SOUND_MISSION_BUST_22, STREAMED_SOUND_MISSION_BUST_23, STREAMED_SOUND_MISSION_BUST_24, STREAMED_SOUND_MISSION_BUST_25, STREAMED_SOUND_MISSION_BUST_26, STREAMED_SOUND_MISSION_BUST_27, STREAMED_SOUND_MISSION_BUST_28, TOTAL_STREAMED_SOUNDS, NO_TRACK, }; enum AudioEntityHandle { AEHANDLE_NONE = -5, AEHANDLE_ERROR_NOAUDIOSYS = -4, AEHANDLE_ERROR_NOFREESLOT = -3, AEHANDLE_ERROR_NOENTITY = -2, AEHANDLE_ERROR_BADAUDIOTYPE = -1, }; enum eAudioType { AUDIOTYPE_PHYSICAL = 0, AUDIOTYPE_EXPLOSION, AUDIOTYPE_FIRE, AUDIOTYPE_WEATHER, AUDIOTYPE_SCRIPTOBJECT, #ifdef GTA_BRIDGE AUDIOTYPE_BRIDGE, #endif AUDIOTYPE_COLLISION, AUDIOTYPE_FRONTEND, AUDIOTYPE_PROJECTILE, AUDIOTYPE_GARAGE, AUDIOTYPE_FIREHYDRANT, AUDIOTYPE_WATERCANNON, AUDIOTYPE_ESCALATOR, AUDIOTYPE_EXTRA_SOUNDS, AUDIOTYPE_POLICERADIO, TOTAL_AUDIO_TYPES, }; ================================================ FILE: src/audio/eax/eax-util.cpp ================================================ /***********************************************************************************************\ * * * EAX-UTIL.CPP - utilities for EAX 3.0 * * Function declaration for EAX Morphing * * String names of the all the presets defined in eax-util.h * * Arrays grouping together all the EAX presets in a scenario * * * ************************************************************************************************/ #include "eax-util.h" #include // Function prototypes used by EAX3ListenerInterpolate void Clamp(EAXVECTOR *eaxVector); bool CheckEAX3LP(LPEAXLISTENERPROPERTIES lpEAX3LP); /***********************************************************************************************\ * * Definition of the EAXMorph function - EAX3ListenerInterpolate * \***********************************************************************************************/ /* EAX3ListenerInterpolate lpStart - Initial EAX 3 Listener parameters lpFinish - Final EAX 3 Listener parameters flRatio - Ratio Destination : Source (0.0 == Source, 1.0 == Destination) lpResult - Interpolated EAX 3 Listener parameters bCheckValues - Check EAX 3.0 parameters are in range, default = false (no checking) */ bool EAX3ListenerInterpolate(LPEAXLISTENERPROPERTIES lpStart, LPEAXLISTENERPROPERTIES lpFinish, float flRatio, LPEAXLISTENERPROPERTIES lpResult, bool bCheckValues) { EAXVECTOR StartVector, FinalVector; float flInvRatio; if (bCheckValues) { if (!CheckEAX3LP(lpStart)) return false; if (!CheckEAX3LP(lpFinish)) return false; } if (flRatio >= 1.0f) { memcpy(lpResult, lpFinish, sizeof(EAXLISTENERPROPERTIES)); return true; } else if (flRatio <= 0.0f) { memcpy(lpResult, lpStart, sizeof(EAXLISTENERPROPERTIES)); return true; } flInvRatio = (1.0f - flRatio); // Environment lpResult->ulEnvironment = 26; // (UNDEFINED environment) // Environment Size if (lpStart->flEnvironmentSize == lpFinish->flEnvironmentSize) lpResult->flEnvironmentSize = lpStart->flEnvironmentSize; else lpResult->flEnvironmentSize = (float)exp( (log(lpStart->flEnvironmentSize) * flInvRatio) + (log(lpFinish->flEnvironmentSize) * flRatio) ); // Environment Diffusion if (lpStart->flEnvironmentDiffusion == lpFinish->flEnvironmentDiffusion) lpResult->flEnvironmentDiffusion = lpStart->flEnvironmentDiffusion; else lpResult->flEnvironmentDiffusion = (lpStart->flEnvironmentDiffusion * flInvRatio) + (lpFinish->flEnvironmentDiffusion * flRatio); // Room if (lpStart->lRoom == lpFinish->lRoom) lpResult->lRoom = lpStart->lRoom; else lpResult->lRoom = (int)( ((float)lpStart->lRoom * flInvRatio) + ((float)lpFinish->lRoom * flRatio) ); // Room HF if (lpStart->lRoomHF == lpFinish->lRoomHF) lpResult->lRoomHF = lpStart->lRoomHF; else lpResult->lRoomHF = (int)( ((float)lpStart->lRoomHF * flInvRatio) + ((float)lpFinish->lRoomHF * flRatio) ); // Room LF if (lpStart->lRoomLF == lpFinish->lRoomLF) lpResult->lRoomLF = lpStart->lRoomLF; else lpResult->lRoomLF = (int)( ((float)lpStart->lRoomLF * flInvRatio) + ((float)lpFinish->lRoomLF * flRatio) ); // Decay Time if (lpStart->flDecayTime == lpFinish->flDecayTime) lpResult->flDecayTime = lpStart->flDecayTime; else lpResult->flDecayTime = (float)exp( (log(lpStart->flDecayTime) * flInvRatio) + (log(lpFinish->flDecayTime) * flRatio) ); // Decay HF Ratio if (lpStart->flDecayHFRatio == lpFinish->flDecayHFRatio) lpResult->flDecayHFRatio = lpStart->flDecayHFRatio; else lpResult->flDecayHFRatio = (float)exp( (log(lpStart->flDecayHFRatio) * flInvRatio) + (log(lpFinish->flDecayHFRatio) * flRatio) ); // Decay LF Ratio if (lpStart->flDecayLFRatio == lpFinish->flDecayLFRatio) lpResult->flDecayLFRatio = lpStart->flDecayLFRatio; else lpResult->flDecayLFRatio = (float)exp( (log(lpStart->flDecayLFRatio) * flInvRatio) + (log(lpFinish->flDecayLFRatio) * flRatio) ); // Reflections if (lpStart->lReflections == lpFinish->lReflections) lpResult->lReflections = lpStart->lReflections; else lpResult->lReflections = (int)( ((float)lpStart->lReflections * flInvRatio) + ((float)lpFinish->lReflections * flRatio) ); // Reflections Delay if (lpStart->flReflectionsDelay == lpFinish->flReflectionsDelay) lpResult->flReflectionsDelay = lpStart->flReflectionsDelay; else lpResult->flReflectionsDelay = (float)exp( (log(lpStart->flReflectionsDelay+0.0001f) * flInvRatio) + (log(lpFinish->flReflectionsDelay+0.0001f) * flRatio) ); // Reflections Pan // To interpolate the vector correctly we need to ensure that both the initial and final vectors vectors are clamped to a length of 1.0f StartVector = lpStart->vReflectionsPan; FinalVector = lpFinish->vReflectionsPan; Clamp(&StartVector); Clamp(&FinalVector); if (lpStart->vReflectionsPan.x == lpFinish->vReflectionsPan.x) lpResult->vReflectionsPan.x = lpStart->vReflectionsPan.x; else lpResult->vReflectionsPan.x = FinalVector.x + (flInvRatio * (StartVector.x - FinalVector.x)); if (lpStart->vReflectionsPan.y == lpFinish->vReflectionsPan.y) lpResult->vReflectionsPan.y = lpStart->vReflectionsPan.y; else lpResult->vReflectionsPan.y = FinalVector.y + (flInvRatio * (StartVector.y - FinalVector.y)); if (lpStart->vReflectionsPan.z == lpFinish->vReflectionsPan.z) lpResult->vReflectionsPan.z = lpStart->vReflectionsPan.z; else lpResult->vReflectionsPan.z = FinalVector.z + (flInvRatio * (StartVector.z - FinalVector.z)); // Reverb if (lpStart->lReverb == lpFinish->lReverb) lpResult->lReverb = lpStart->lReverb; else lpResult->lReverb = (int)( ((float)lpStart->lReverb * flInvRatio) + ((float)lpFinish->lReverb * flRatio) ); // Reverb Delay if (lpStart->flReverbDelay == lpFinish->flReverbDelay) lpResult->flReverbDelay = lpStart->flReverbDelay; else lpResult->flReverbDelay = (float)exp( (log(lpStart->flReverbDelay+0.0001f) * flInvRatio) + (log(lpFinish->flReverbDelay+0.0001f) * flRatio) ); // Reverb Pan // To interpolate the vector correctly we need to ensure that both the initial and final vectors are clamped to a length of 1.0f StartVector = lpStart->vReverbPan; FinalVector = lpFinish->vReverbPan; Clamp(&StartVector); Clamp(&FinalVector); if (lpStart->vReverbPan.x == lpFinish->vReverbPan.x) lpResult->vReverbPan.x = lpStart->vReverbPan.x; else lpResult->vReverbPan.x = FinalVector.x + (flInvRatio * (StartVector.x - FinalVector.x)); if (lpStart->vReverbPan.y == lpFinish->vReverbPan.y) lpResult->vReverbPan.y = lpStart->vReverbPan.y; else lpResult->vReverbPan.y = FinalVector.y + (flInvRatio * (StartVector.y - FinalVector.y)); if (lpStart->vReverbPan.z == lpFinish->vReverbPan.z) lpResult->vReverbPan.z = lpStart->vReverbPan.z; else lpResult->vReverbPan.z = FinalVector.z + (flInvRatio * (StartVector.z - FinalVector.z)); // Echo Time if (lpStart->flEchoTime == lpFinish->flEchoTime) lpResult->flEchoTime = lpStart->flEchoTime; else lpResult->flEchoTime = (float)exp( (log(lpStart->flEchoTime) * flInvRatio) + (log(lpFinish->flEchoTime) * flRatio) ); // Echo Depth if (lpStart->flEchoDepth == lpFinish->flEchoDepth) lpResult->flEchoDepth = lpStart->flEchoDepth; else lpResult->flEchoDepth = (lpStart->flEchoDepth * flInvRatio) + (lpFinish->flEchoDepth * flRatio); // Modulation Time if (lpStart->flModulationTime == lpFinish->flModulationTime) lpResult->flModulationTime = lpStart->flModulationTime; else lpResult->flModulationTime = (float)exp( (log(lpStart->flModulationTime) * flInvRatio) + (log(lpFinish->flModulationTime) * flRatio) ); // Modulation Depth if (lpStart->flModulationDepth == lpFinish->flModulationDepth) lpResult->flModulationDepth = lpStart->flModulationDepth; else lpResult->flModulationDepth = (lpStart->flModulationDepth * flInvRatio) + (lpFinish->flModulationDepth * flRatio); // Air Absorption HF if (lpStart->flAirAbsorptionHF == lpFinish->flAirAbsorptionHF) lpResult->flAirAbsorptionHF = lpStart->flAirAbsorptionHF; else lpResult->flAirAbsorptionHF = (lpStart->flAirAbsorptionHF * flInvRatio) + (lpFinish->flAirAbsorptionHF * flRatio); // HF Reference if (lpStart->flHFReference == lpFinish->flHFReference) lpResult->flHFReference = lpStart->flHFReference; else lpResult->flHFReference = (float)exp( (log(lpStart->flHFReference) * flInvRatio) + (log(lpFinish->flHFReference) * flRatio) ); // LF Reference if (lpStart->flLFReference == lpFinish->flLFReference) lpResult->flLFReference = lpStart->flLFReference; else lpResult->flLFReference = (float)exp( (log(lpStart->flLFReference) * flInvRatio) + (log(lpFinish->flLFReference) * flRatio) ); // Room Rolloff Factor if (lpStart->flRoomRolloffFactor == lpFinish->flRoomRolloffFactor) lpResult->flRoomRolloffFactor = lpStart->flRoomRolloffFactor; else lpResult->flRoomRolloffFactor = (lpStart->flRoomRolloffFactor * flInvRatio) + (lpFinish->flRoomRolloffFactor * flRatio); // Flags lpResult->ulFlags = (lpStart->ulFlags & lpFinish->ulFlags); // Clamp Delays if (lpResult->flReflectionsDelay > EAXLISTENER_MAXREFLECTIONSDELAY) lpResult->flReflectionsDelay = EAXLISTENER_MAXREFLECTIONSDELAY; if (lpResult->flReverbDelay > EAXLISTENER_MAXREVERBDELAY) lpResult->flReverbDelay = EAXLISTENER_MAXREVERBDELAY; return true; } /* CheckEAX3LP Checks that the parameters in the EAX 3 Listener Properties structure are in-range */ bool CheckEAX3LP(LPEAXLISTENERPROPERTIES lpEAX3LP) { if ( (lpEAX3LP->lRoom < EAXLISTENER_MINROOM) || (lpEAX3LP->lRoom > EAXLISTENER_MAXROOM) ) return false; if ( (lpEAX3LP->lRoomHF < EAXLISTENER_MINROOMHF) || (lpEAX3LP->lRoomHF > EAXLISTENER_MAXROOMHF) ) return false; if ( (lpEAX3LP->lRoomLF < EAXLISTENER_MINROOMLF) || (lpEAX3LP->lRoomLF > EAXLISTENER_MAXROOMLF) ) return false; if ( (lpEAX3LP->ulEnvironment < EAXLISTENER_MINENVIRONMENT) || (lpEAX3LP->ulEnvironment > EAXLISTENER_MAXENVIRONMENT) ) return false; if ( (lpEAX3LP->flEnvironmentSize < EAXLISTENER_MINENVIRONMENTSIZE) || (lpEAX3LP->flEnvironmentSize > EAXLISTENER_MAXENVIRONMENTSIZE) ) return false; if ( (lpEAX3LP->flEnvironmentDiffusion < EAXLISTENER_MINENVIRONMENTDIFFUSION) || (lpEAX3LP->flEnvironmentDiffusion > EAXLISTENER_MAXENVIRONMENTDIFFUSION) ) return false; if ( (lpEAX3LP->flDecayTime < EAXLISTENER_MINDECAYTIME) || (lpEAX3LP->flDecayTime > EAXLISTENER_MAXDECAYTIME) ) return false; if ( (lpEAX3LP->flDecayHFRatio < EAXLISTENER_MINDECAYHFRATIO) || (lpEAX3LP->flDecayHFRatio > EAXLISTENER_MAXDECAYHFRATIO) ) return false; if ( (lpEAX3LP->flDecayLFRatio < EAXLISTENER_MINDECAYLFRATIO) || (lpEAX3LP->flDecayLFRatio > EAXLISTENER_MAXDECAYLFRATIO) ) return false; if ( (lpEAX3LP->lReflections < EAXLISTENER_MINREFLECTIONS) || (lpEAX3LP->lReflections > EAXLISTENER_MAXREFLECTIONS) ) return false; if ( (lpEAX3LP->flReflectionsDelay < EAXLISTENER_MINREFLECTIONSDELAY) || (lpEAX3LP->flReflectionsDelay > EAXLISTENER_MAXREFLECTIONSDELAY) ) return false; if ( (lpEAX3LP->lReverb < EAXLISTENER_MINREVERB) || (lpEAX3LP->lReverb > EAXLISTENER_MAXREVERB) ) return false; if ( (lpEAX3LP->flReverbDelay < EAXLISTENER_MINREVERBDELAY) || (lpEAX3LP->flReverbDelay > EAXLISTENER_MAXREVERBDELAY) ) return false; if ( (lpEAX3LP->flEchoTime < EAXLISTENER_MINECHOTIME) || (lpEAX3LP->flEchoTime > EAXLISTENER_MAXECHOTIME) ) return false; if ( (lpEAX3LP->flEchoDepth < EAXLISTENER_MINECHODEPTH) || (lpEAX3LP->flEchoDepth > EAXLISTENER_MAXECHODEPTH) ) return false; if ( (lpEAX3LP->flModulationTime < EAXLISTENER_MINMODULATIONTIME) || (lpEAX3LP->flModulationTime > EAXLISTENER_MAXMODULATIONTIME) ) return false; if ( (lpEAX3LP->flModulationDepth < EAXLISTENER_MINMODULATIONDEPTH) || (lpEAX3LP->flModulationDepth > EAXLISTENER_MAXMODULATIONDEPTH) ) return false; if ( (lpEAX3LP->flAirAbsorptionHF < EAXLISTENER_MINAIRABSORPTIONHF) || (lpEAX3LP->flAirAbsorptionHF > EAXLISTENER_MAXAIRABSORPTIONHF) ) return false; if ( (lpEAX3LP->flHFReference < EAXLISTENER_MINHFREFERENCE) || (lpEAX3LP->flHFReference > EAXLISTENER_MAXHFREFERENCE) ) return false; if ( (lpEAX3LP->flLFReference < EAXLISTENER_MINLFREFERENCE) || (lpEAX3LP->flLFReference > EAXLISTENER_MAXLFREFERENCE) ) return false; if ( (lpEAX3LP->flRoomRolloffFactor < EAXLISTENER_MINROOMROLLOFFFACTOR) || (lpEAX3LP->flRoomRolloffFactor > EAXLISTENER_MAXROOMROLLOFFFACTOR) ) return false; if (lpEAX3LP->ulFlags & EAXLISTENERFLAGS_RESERVED) return false; return true; } /* Clamp Clamps the length of the vector to 1.0f */ void Clamp(EAXVECTOR *eaxVector) { float flMagnitude; float flInvMagnitude; flMagnitude = (float)sqrt((eaxVector->x*eaxVector->x) + (eaxVector->y*eaxVector->y) + (eaxVector->z*eaxVector->z)); if (flMagnitude <= 1.0f) return; flInvMagnitude = 1.0f / flMagnitude; eaxVector->x *= flInvMagnitude; eaxVector->y *= flInvMagnitude; eaxVector->z *= flInvMagnitude; } /***********************************************************************************************\ * * To assist those developers wishing to add EAX effects to their level editors, each of the * List of string names of the various EAX 3.0 presets defined in eax-util.h * Arrays to group together presets of the same scenario * \***********************************************************************************************/ ////////////////////////////////////////////////////// // Array of scenario names // ////////////////////////////////////////////////////// const char* EAX30_SCENARIO_NAMES[] = { "Castle", "Factory", "IcePalace", "SpaceStation", "WoodenShip", "Sports", "Prefab", "Domes and Pipes", "Outdoors", "Mood", "Driving", "City", "Miscellaneous", "Original" }; ////////////////////////////////////////////////////// // Array of standardised location names // ////////////////////////////////////////////////////// const char* EAX30_LOCATION_NAMES[] = { "Hall", "Large Room", "Medium Room", "Small Room", "Cupboard", "Alcove", "Long Passage", "Short Passage", "Courtyard" }; ////////////////////////////////////////////////////// // Standardised Location effects can be accessed // // from a matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_STANDARD_PRESETS[EAX30_NUM_STANDARD_SCENARIOS][EAX30_NUM_LOCATIONS]= { {EAX30_PRESET_CASTLE_HALL, EAX30_PRESET_CASTLE_LARGEROOM, EAX30_PRESET_CASTLE_MEDIUMROOM, EAX30_PRESET_CASTLE_SMALLROOM, EAX30_PRESET_CASTLE_CUPBOARD, EAX30_PRESET_CASTLE_ALCOVE, EAX30_PRESET_CASTLE_LONGPASSAGE, EAX30_PRESET_CASTLE_SHORTPASSAGE, EAX30_PRESET_CASTLE_COURTYARD}, {EAX30_PRESET_FACTORY_HALL, EAX30_PRESET_FACTORY_LARGEROOM, EAX30_PRESET_FACTORY_MEDIUMROOM, EAX30_PRESET_FACTORY_SMALLROOM, EAX30_PRESET_FACTORY_CUPBOARD, EAX30_PRESET_FACTORY_ALCOVE, EAX30_PRESET_FACTORY_LONGPASSAGE, EAX30_PRESET_FACTORY_SHORTPASSAGE, EAX30_PRESET_FACTORY_COURTYARD}, {EAX30_PRESET_ICEPALACE_HALL, EAX30_PRESET_ICEPALACE_LARGEROOM, EAX30_PRESET_ICEPALACE_MEDIUMROOM, EAX30_PRESET_ICEPALACE_SMALLROOM, EAX30_PRESET_ICEPALACE_CUPBOARD, EAX30_PRESET_ICEPALACE_ALCOVE, EAX30_PRESET_ICEPALACE_LONGPASSAGE, EAX30_PRESET_ICEPALACE_SHORTPASSAGE, EAX30_PRESET_ICEPALACE_COURTYARD}, {EAX30_PRESET_SPACESTATION_HALL,EAX30_PRESET_SPACESTATION_LARGEROOM,EAX30_PRESET_SPACESTATION_MEDIUMROOM, EAX30_PRESET_SPACESTATION_SMALLROOM,EAX30_PRESET_SPACESTATION_CUPBOARD, EAX30_PRESET_SPACESTATION_ALCOVE, EAX30_PRESET_SPACESTATION_LONGPASSAGE, EAX30_PRESET_SPACESTATION_SHORTPASSAGE, EAX30_PRESET_SPACESTATION_HALL}, {EAX30_PRESET_WOODEN_HALL, EAX30_PRESET_WOODEN_LARGEROOM, EAX30_PRESET_WOODEN_MEDIUMROOM, EAX30_PRESET_WOODEN_SMALLROOM, EAX30_PRESET_WOODEN_CUPBOARD, EAX30_PRESET_WOODEN_ALCOVE, EAX30_PRESET_WOODEN_LONGPASSAGE, EAX30_PRESET_WOODEN_SHORTPASSAGE, EAX30_PRESET_WOODEN_COURTYARD}, }; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Array of original environment names // ////////////////////////////////////////////////////// const char* EAX30_ORIGINAL_PRESET_NAMES[] = { "Generic", "Padded Cell", "Room", "Bathroom", "Living Room", "Stone Room", "Auditorium", "Concert Hall", "Cave", "Arena", "Hangar", "Carpetted Hallway", "Hallway", "Stone Corridor", "Alley", "Forest", "City", "Mountains", "Quarry", "Plain", "Parking Lot", "Sewer Pipe", "Underwater", "Drugged", "Dizzy", "Psychotic" }; ////////////////////////////////////////////////////// // Sports effects matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_ORIGINAL_PRESETS[] = { EAX30_PRESET_GENERIC, EAX30_PRESET_PADDEDCELL, EAX30_PRESET_ROOM, EAX30_PRESET_BATHROOM, EAX30_PRESET_LIVINGROOM, EAX30_PRESET_STONEROOM, EAX30_PRESET_AUDITORIUM, EAX30_PRESET_CONCERTHALL, EAX30_PRESET_CAVE, EAX30_PRESET_ARENA, EAX30_PRESET_HANGAR, EAX30_PRESET_CARPETTEDHALLWAY, EAX30_PRESET_HALLWAY, EAX30_PRESET_STONECORRIDOR, EAX30_PRESET_ALLEY, EAX30_PRESET_FOREST, EAX30_PRESET_CITY, EAX30_PRESET_MOUNTAINS, EAX30_PRESET_QUARRY, EAX30_PRESET_PLAIN, EAX30_PRESET_PARKINGLOT, EAX30_PRESET_SEWERPIPE, EAX30_PRESET_UNDERWATER, EAX30_PRESET_DRUGGED, EAX30_PRESET_DIZZY, EAX30_PRESET_PSYCHOTIC }; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Array of sport environment names // ////////////////////////////////////////////////////// const char* EAX30_SPORTS_PRESET_NAMES[] = { "Empty Stadium", "Full Stadium", "Stadium Tannoy", "Squash Court", "Small Swimming Pool", "Large Swimming Pool", "Gymnasium" }; ////////////////////////////////////////////////////// // Sports effects matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_SPORTS_PRESETS[] = { EAX30_PRESET_SPORT_EMPTYSTADIUM, EAX30_PRESET_SPORT_FULLSTADIUM, EAX30_PRESET_SPORT_STADIUMTANNOY, EAX30_PRESET_SPORT_SQUASHCOURT, EAX30_PRESET_SPORT_SMALLSWIMMINGPOOL, EAX30_PRESET_SPORT_LARGESWIMMINGPOOL, EAX30_PRESET_SPORT_GYMNASIUM }; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Array of prefab environment names // ////////////////////////////////////////////////////// const char* EAX30_PREFAB_PRESET_NAMES[] = { "Workshop", "School Room", "Practise Room", "Outhouse", "Caravan" }; ////////////////////////////////////////////////////// // Prefab effects matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_PREFAB_PRESETS[] = { EAX30_PRESET_PREFAB_WORKSHOP, EAX30_PRESET_PREFAB_SCHOOLROOM, EAX30_PRESET_PREFAB_PRACTISEROOM, EAX30_PRESET_PREFAB_OUTHOUSE, EAX30_PRESET_PREFAB_CARAVAN }; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Array of Domes & Pipes environment names // ////////////////////////////////////////////////////// const char* EAX30_DOMESNPIPES_PRESET_NAMES[] = { "Domed Tomb", "Saint Paul's Dome", "Small Pipe", "Long Thin Pipe", "Large Pipe", "Resonant Pipe" }; ////////////////////////////////////////////////////// // Domes & Pipes effects matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_DOMESNPIPES_PRESETS[] = { EAX30_PRESET_DOME_TOMB, EAX30_PRESET_DOME_SAINTPAULS, EAX30_PRESET_PIPE_SMALL, EAX30_PRESET_PIPE_LONGTHIN, EAX30_PRESET_PIPE_LARGE, EAX30_PRESET_PIPE_RESONANT }; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Array of Outdoors environment names // ////////////////////////////////////////////////////// const char* EAX30_OUTDOORS_PRESET_NAMES[] = { "Backyard", "Rolling Plains", "Deep Canyon", "Creek", "Valley" }; ////////////////////////////////////////////////////// // Outdoors effects matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_OUTDOORS_PRESETS[] = { EAX30_PRESET_OUTDOORS_BACKYARD, EAX30_PRESET_OUTDOORS_ROLLINGPLAINS, EAX30_PRESET_OUTDOORS_DEEPCANYON, EAX30_PRESET_OUTDOORS_CREEK, EAX30_PRESET_OUTDOORS_VALLEY }; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Array of Mood environment names // ////////////////////////////////////////////////////// const char* EAX30_MOOD_PRESET_NAMES[] = { "Heaven", "Hell", "Memory" }; ////////////////////////////////////////////////////// // Mood effects matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_MOOD_PRESETS[] = { EAX30_PRESET_MOOD_HEAVEN, EAX30_PRESET_MOOD_HELL, EAX30_PRESET_MOOD_MEMORY }; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Array of driving environment names // ////////////////////////////////////////////////////// const char* EAX30_DRIVING_PRESET_NAMES[] = { "Race Commentator", "Pit Garage", "In-car (Stripped out racer)", "In-car (Sportscar)", "In-car (Luxury)", "Full Grandstand", "Empty Grandstand", "Tunnel" }; ////////////////////////////////////////////////////// // Driving effects matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_DRIVING_PRESETS[] = { EAX30_PRESET_DRIVING_COMMENTATOR, EAX30_PRESET_DRIVING_PITGARAGE, EAX30_PRESET_DRIVING_INCAR_RACER, EAX30_PRESET_DRIVING_INCAR_SPORTS, EAX30_PRESET_DRIVING_INCAR_LUXURY, EAX30_PRESET_DRIVING_FULLGRANDSTAND, EAX30_PRESET_DRIVING_EMPTYGRANDSTAND, EAX30_PRESET_DRIVING_TUNNEL }; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Array of City environment names // ////////////////////////////////////////////////////// const char* EAX30_CITY_PRESET_NAMES[] = { "City Streets", "Subway", "Museum", "Library", "Underpass", "Abandoned City" }; ////////////////////////////////////////////////////// // City effects matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_CITY_PRESETS[] = { EAX30_PRESET_CITY_STREETS, EAX30_PRESET_CITY_SUBWAY, EAX30_PRESET_CITY_MUSEUM, EAX30_PRESET_CITY_LIBRARY, EAX30_PRESET_CITY_UNDERPASS, EAX30_PRESET_CITY_ABANDONED }; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Array of Misc environment names // ////////////////////////////////////////////////////// const char* EAX30_MISC_PRESET_NAMES[] = { "Dusty Box Room", "Chapel", "Small Water Room" }; ////////////////////////////////////////////////////// // Misc effects matrix // ////////////////////////////////////////////////////// EAXLISTENERPROPERTIES EAX30_MISC_PRESETS[] = { EAX30_PRESET_DUSTYROOM, EAX30_PRESET_CHAPEL, EAX30_PRESET_SMALLWATERROOM }; ================================================ FILE: src/audio/eax/eax-util.h ================================================ /*******************************************************************\ * * * EAX-UTIL.H - utilities for Environmental Audio Extensions v. 3.0 * * Definitions of the Original 26 EAX Presets * * Definitions for some new EAX Presets * * Definitions of some Material Presets * * Function declaration for EAX Morphing * * * \*******************************************************************/ #ifndef EAXUTIL_INCLUDED #define EAXUTIL_INCLUDED #include /*********************************************************************************************** * Function : EAX3ListenerInterpolate * Params : lpStart - Initial EAX 3 Listener parameters * : lpFinish - Final EAX 3 Listener parameters * : flRatio - Ratio Destination : Source (0.0 == Source, 1.0 == Destination) * : lpResult - Interpolated EAX 3 Listener parameters * : bCheckValues - Check EAX 3.0 parameters are in range, - default == false (no checking) ************************************************************************************************/ bool EAX3ListenerInterpolate(EAXLISTENERPROPERTIES *lpStartEAX3LP, EAXLISTENERPROPERTIES *lpFinishEAX3LP, float flRatio, EAXLISTENERPROPERTIES *lpResultEAX3LP, bool bCheckValues = false); /***********************************************************************************************\ * * Legacy environment presets for use with DSPROPERTY_EAXLISTENER_ALLPARAMETERS. * Each array conforms to the DSPROPSETID_EAX30_ListenerProperties structure defined in EAX.H. * ************************************************************************************************/ // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_GENERIC \ {0, 7.5f, 1.000f, -1000, -100, 0, 1.49f, 0.83f, 1.00f, -2602, 0.007f, 0.00f,0.00f,0.00f, 200, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_PADDEDCELL \ {1, 1.4f, 1.000f, -1000, -6000, 0, 0.17f, 0.10f, 1.00f, -1204, 0.001f, 0.00f,0.00f,0.00f, 207, 0.002f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_ROOM \ {2, 1.9f, 1.000f, -1000, -454, 0, 0.40f, 0.83f, 1.00f, -1646, 0.002f, 0.00f,0.00f,0.00f, 53, 0.003f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_BATHROOM \ {3, 1.4f, 1.000f, -1000, -1200, 0, 1.49f, 0.54f, 1.00f, -370, 0.007f, 0.00f,0.00f,0.00f, 1030, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_LIVINGROOM \ {4, 2.5f, 1.000f, -1000, -6000, 0, 0.50f, 0.10f, 1.00f, -1376, 0.003f, 0.00f,0.00f,0.00f, -1104, 0.004f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_STONEROOM \ {5, 11.6f, 1.000f, -1000, -300, 0, 2.31f, 0.64f, 1.00f, -711, 0.012f, 0.00f,0.00f,0.00f, 83, 0.017f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_AUDITORIUM \ {6, 21.6f, 1.000f, -1000, -476, 0, 4.32f, 0.59f, 1.00f, -789, 0.020f, 0.00f,0.00f,0.00f, -289, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_CONCERTHALL \ {7, 19.6f, 1.000f, -1000, -500, 0, 3.92f, 0.70f, 1.00f, -1230, 0.020f, 0.00f,0.00f,0.00f, -02, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_CAVE \ {8, 14.6f, 1.000f, -1000, 0, 0, 2.91f, 1.30f, 1.00f, -602, 0.015f, 0.00f,0.00f,0.00f, -302, 0.022f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } #define EAX30_PRESET_ARENA \ {9, 36.2f, 1.000f, -1000, -698, 0, 7.24f, 0.33f, 1.00f, -1166, 0.020f, 0.00f,0.00f,0.00f, 16, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_HANGAR \ {10, 50.3f, 1.000f, -1000, -1000, 0, 10.05f, 0.23f, 1.00f, -602, 0.020f, 0.00f,0.00f,0.00f, 198, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_CARPETTEDHALLWAY \ {11, 1.9f, 1.000f, -1000, -4000, 0, 0.30f, 0.10f, 1.00f, -1831, 0.002f, 0.00f,0.00f,0.00f, -1630, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_HALLWAY \ {12, 1.8f, 1.000f, -1000, -300, 0, 1.49f, 0.59f, 1.00f, -1219, 0.007f, 0.00f,0.00f,0.00f, 441, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_STONECORRIDOR \ {13, 13.5f, 1.000f, -1000, -237, 0, 2.70f, 0.79f, 1.00f, -1214, 0.013f, 0.00f,0.00f,0.00f, 395, 0.020f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_ALLEY \ {14, 7.5f, 0.300f, -1000, -270, 0, 1.49f, 0.86f, 1.00f, -1204, 0.007f, 0.00f,0.00f,0.00f, -4, 0.011f, 0.00f,0.00f,0.00f, 0.125f, 0.950f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_FOREST \ {15, 38.0f, 0.300f, -1000, -3300, 0, 1.49f, 0.54f, 1.00f, -2560, 0.162f, 0.00f,0.00f,0.00f, -229, 0.088f, 0.00f,0.00f,0.00f, 0.125f, 1.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_CITY \ {16, 7.5f, 0.500f, -1000, -800, 0, 1.49f, 0.67f, 1.00f, -2273, 0.007f, 0.00f,0.00f,0.00f, -1691, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_MOUNTAINS \ {17, 100.0f, 0.270f, -1000, -2500, 0, 1.49f, 0.21f, 1.00f, -2780, 0.300f, 0.00f,0.00f,0.00f, -1434, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } #define EAX30_PRESET_QUARRY \ {18, 17.5f, 1.000f, -1000, -1000, 0, 1.49f, 0.83f, 1.00f, -10000, 0.061f, 0.00f,0.00f,0.00f, 500, 0.025f, 0.00f,0.00f,0.00f, 0.125f, 0.700f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_PLAIN \ {19, 42.5f, 0.210f, -1000, -2000, 0, 1.49f, 0.50f, 1.00f, -2466, 0.179f, 0.00f,0.00f,0.00f, -1926, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_PARKINGLOT \ {20, 8.3f, 1.000f, -1000, 0, 0, 1.65f, 1.50f, 1.00f, -1363, 0.008f, 0.00f,0.00f,0.00f, -1153, 0.012f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } #define EAX30_PRESET_SEWERPIPE \ {21, 1.7f, 0.800f, -1000, -1000, 0, 2.81f, 0.14f, 1.00f, 429, 0.014f, 0.00f,0.00f,0.00f, 1023, 0.021f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_UNDERWATER \ {22, 1.8f, 1.000f, -1000, -4000, 0, 1.49f, 0.10f, 1.00f, -449, 0.007f, 0.00f,0.00f,0.00f, 1700, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 1.180f, 0.348f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_DRUGGED \ {23, 1.9f, 0.500f, -1000, 0, 0, 8.39f, 1.39f, 1.00f, -115, 0.002f, 0.00f,0.00f,0.00f, 985, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 1.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } #define EAX30_PRESET_DIZZY \ {24, 1.8f, 0.600f, -1000, -400, 0, 17.23f, 0.56f, 1.00f, -1713, 0.020f, 0.00f,0.00f,0.00f, -613, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.810f, 0.310f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } #define EAX30_PRESET_PSYCHOTIC \ {25, 1.0f, 0.500f, -1000, -151, 0, 7.56f, 0.91f, 1.00f, -626, 0.020f, 0.00f,0.00f,0.00f, 774, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 4.000f, 1.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } /***********************************************************************************************\ * * New environment presets for use with DSPROPERTY_EAXLISTENER_ALLPARAMETERS. * Each array conforms to the DSPROPSETID_EAX30_ListenerProperties structure defined in EAX.H. * ************************************************************************************************/ // STANDARDISED-LOCATION SCENARIOS // CASTLE PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_CASTLE_SMALLROOM \ { 26, 8.3f, 0.890f, -1100, -800, -2000, 1.22f, 0.83f, 0.31f, -100, 0.022f, 0.00f,0.00f,0.00f, 0, 0.011f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } #define EAX30_PRESET_CASTLE_SHORTPASSAGE \ { 26, 8.3f, 0.890f, -1000, -1000, -2000, 2.32f, 0.83f, 0.31f, -100, 0.007f, 0.00f,0.00f,0.00f, -500, 0.023f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } #define EAX30_PRESET_CASTLE_MEDIUMROOM \ { 26, 8.3f, 0.930f, -1000, -1100, -2000, 2.04f, 0.83f, 0.46f, -300, 0.022f, 0.00f,0.00f,0.00f, -200, 0.011f, 0.00f,0.00f,0.00f, 0.155f, 0.030f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } #define EAX30_PRESET_CASTLE_LONGPASSAGE \ { 26, 8.3f, 0.890f, -1000, -800, -2000, 3.42f, 0.83f, 0.31f, -200, 0.007f, 0.00f,0.00f,0.00f, -600, 0.023f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } #define EAX30_PRESET_CASTLE_LARGEROOM \ { 26, 8.3f, 0.820f, -1000, -1100, -1800, 2.53f, 0.83f, 0.50f, -900, 0.034f, 0.00f,0.00f,0.00f, -400, 0.016f, 0.00f,0.00f,0.00f, 0.185f, 0.070f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } #define EAX30_PRESET_CASTLE_HALL \ { 26, 8.3f, 0.810f, -1000, -1100, -1500, 3.14f, 0.79f, 0.62f, -1300, 0.056f, 0.00f,0.00f,0.00f, -500, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } #define EAX30_PRESET_CASTLE_CUPBOARD \ { 26, 8.3f, 0.890f, -1000, -1100, -2000, 0.67f, 0.87f, 0.31f, 300, 0.010f, 0.00f,0.00f,0.00f, 300, 0.007f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } #define EAX30_PRESET_CASTLE_COURTYARD \ { 26, 8.3f, 0.420f, -1100, -700, -900, 2.13f, 0.61f, 0.23f, -2300, 0.112f, 0.00f,0.00f,0.00f, -1500, 0.036f, 0.00f,0.00f,0.00f, 0.250f, 0.370f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x1f } #define EAX30_PRESET_CASTLE_ALCOVE \ { 26, 8.3f, 0.890f, -1000, -600, -2000, 1.64f, 0.87f, 0.31f, -100, 0.007f, 0.00f,0.00f,0.00f, -500, 0.034f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } // FACTORY PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_FACTORY_ALCOVE \ { 26, 1.8f, 0.590f, -1200, -200, -600, 3.14f, 0.65f, 1.31f, 300, 0.010f, 0.00f,0.00f,0.00f, -1200, 0.038f, 0.00f,0.00f,0.00f, 0.114f, 0.100f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } #define EAX30_PRESET_FACTORY_SHORTPASSAGE \ { 26, 1.8f, 0.640f, -1200, -200, -600, 2.53f, 0.65f, 1.31f, 0, 0.010f, 0.00f,0.00f,0.00f, -600, 0.038f, 0.00f,0.00f,0.00f, 0.135f, 0.230f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } #define EAX30_PRESET_FACTORY_MEDIUMROOM \ { 26, 1.9f, 0.820f, -1200, -200, -600, 2.76f, 0.65f, 1.31f, -1100, 0.022f, 0.00f,0.00f,0.00f, -400, 0.023f, 0.00f,0.00f,0.00f, 0.174f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } #define EAX30_PRESET_FACTORY_LONGPASSAGE \ { 26, 1.8f, 0.640f, -1200, -200, -600, 4.06f, 0.65f, 1.31f, 0, 0.020f, 0.00f,0.00f,0.00f, -900, 0.037f, 0.00f,0.00f,0.00f, 0.135f, 0.230f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } #define EAX30_PRESET_FACTORY_LARGEROOM \ { 26, 1.9f, 0.750f, -1200, -300, -400, 4.24f, 0.51f, 1.31f, -1500, 0.039f, 0.00f,0.00f,0.00f, -600, 0.023f, 0.00f,0.00f,0.00f, 0.231f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } #define EAX30_PRESET_FACTORY_HALL \ { 26, 1.9f, 0.750f, -1000, -300, -400, 7.43f, 0.51f, 1.31f, -2400, 0.073f, 0.00f,0.00f,0.00f, -500, 0.027f, 0.00f,0.00f,0.00f, 0.250f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } #define EAX30_PRESET_FACTORY_CUPBOARD \ { 26, 1.7f, 0.630f, -1200, -200, -600, 0.49f, 0.65f, 1.31f, 200, 0.010f, 0.00f,0.00f,0.00f, 200, 0.032f, 0.00f,0.00f,0.00f, 0.107f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } #define EAX30_PRESET_FACTORY_COURTYARD \ { 26, 1.7f, 0.570f, -1000, -1000, -400, 2.32f, 0.29f, 0.56f, -2400, 0.090f, 0.00f,0.00f,0.00f, -2000, 0.039f, 0.00f,0.00f,0.00f, 0.250f, 0.290f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } #define EAX30_PRESET_FACTORY_SMALLROOM \ { 26, 1.8f, 0.820f, -1200, -200, -600, 1.72f, 0.65f, 1.31f, -300, 0.010f, 0.00f,0.00f,0.00f, -200, 0.024f, 0.00f,0.00f,0.00f, 0.119f, 0.070f, 0.250f, 0.000f, -0.0f, 3762.6f, 362.5f, 0.00f, 0x20 } // ICE PALACE PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_ICEPALACE_ALCOVE \ { 26, 2.7f, 0.840f, -1000, -500, -1100, 2.76f, 1.46f, 0.28f, 100, 0.010f, 0.00f,0.00f,0.00f, -1200, 0.030f, 0.00f,0.00f,0.00f, 0.161f, 0.090f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } #define EAX30_PRESET_ICEPALACE_SHORTPASSAGE \ { 26, 2.7f, 0.750f, -1000, -500, -1100, 1.79f, 1.46f, 0.28f, -600, 0.010f, 0.00f,0.00f,0.00f, -700, 0.019f, 0.00f,0.00f,0.00f, 0.177f, 0.090f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } #define EAX30_PRESET_ICEPALACE_MEDIUMROOM \ { 26, 2.7f, 0.870f, -1000, -500, -700, 2.22f, 1.53f, 0.32f, -800, 0.039f, 0.00f,0.00f,0.00f, -1200, 0.027f, 0.00f,0.00f,0.00f, 0.186f, 0.120f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } #define EAX30_PRESET_ICEPALACE_LONGPASSAGE \ { 26, 2.7f, 0.770f, -1000, -500, -800, 3.01f, 1.46f, 0.28f, -200, 0.012f, 0.00f,0.00f,0.00f, -800, 0.025f, 0.00f,0.00f,0.00f, 0.186f, 0.040f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } #define EAX30_PRESET_ICEPALACE_LARGEROOM \ { 26, 2.9f, 0.810f, -1000, -500, -700, 3.14f, 1.53f, 0.32f, -1200, 0.039f, 0.00f,0.00f,0.00f, -1300, 0.027f, 0.00f,0.00f,0.00f, 0.214f, 0.110f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } #define EAX30_PRESET_ICEPALACE_HALL \ { 26, 2.9f, 0.760f, -1000, -700, -500, 5.49f, 1.53f, 0.38f, -1900, 0.054f, 0.00f,0.00f,0.00f, -1400, 0.052f, 0.00f,0.00f,0.00f, 0.226f, 0.110f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } #define EAX30_PRESET_ICEPALACE_CUPBOARD \ { 26, 2.7f, 0.830f, -1000, -600, -1300, 0.76f, 1.53f, 0.26f, 100, 0.012f, 0.00f,0.00f,0.00f, 100, 0.016f, 0.00f,0.00f,0.00f, 0.143f, 0.080f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } #define EAX30_PRESET_ICEPALACE_COURTYARD \ { 26, 2.9f, 0.590f, -1000, -1100, -1000, 2.04f, 1.20f, 0.38f, -2000, 0.073f, 0.00f,0.00f,0.00f, -2200, 0.043f, 0.00f,0.00f,0.00f, 0.235f, 0.480f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } #define EAX30_PRESET_ICEPALACE_SMALLROOM \ { 26, 2.7f, 0.840f, -1000, -500, -1100, 1.51f, 1.53f, 0.27f, -100, 0.010f, 0.00f,0.00f,0.00f, -900, 0.011f, 0.00f,0.00f,0.00f, 0.164f, 0.140f, 0.250f, 0.000f, -0.0f, 12428.5f, 99.6f, 0.00f, 0x20 } // SPACE STATION PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_SPACESTATION_ALCOVE \ { 26, 1.5f, 0.780f, -1100, -300, -100, 1.16f, 0.81f, 0.55f, 300, 0.007f, 0.00f,0.00f,0.00f, -500, 0.018f, 0.00f,0.00f,0.00f, 0.192f, 0.210f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } #define EAX30_PRESET_SPACESTATION_MEDIUMROOM \ { 26, 1.5f, 0.750f, -1000, -400, -100, 3.01f, 0.50f, 0.55f, -1000, 0.034f, 0.00f,0.00f,0.00f, -700, 0.035f, 0.00f,0.00f,0.00f, 0.209f, 0.310f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } #define EAX30_PRESET_SPACESTATION_SHORTPASSAGE \ { 26, 1.5f, 0.870f, -1000, -400, -100, 3.57f, 0.50f, 0.55f, 0, 0.012f, 0.00f,0.00f,0.00f, -600, 0.016f, 0.00f,0.00f,0.00f, 0.172f, 0.200f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } #define EAX30_PRESET_SPACESTATION_LONGPASSAGE \ { 26, 1.9f, 0.820f, -1000, -400, -100, 4.62f, 0.62f, 0.55f, 0, 0.012f, 0.00f,0.00f,0.00f, -800, 0.031f, 0.00f,0.00f,0.00f, 0.250f, 0.230f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } #define EAX30_PRESET_SPACESTATION_LARGEROOM \ { 26, 1.8f, 0.810f, -1000, -400, -100, 3.89f, 0.38f, 0.61f, -1200, 0.056f, 0.00f,0.00f,0.00f, -800, 0.035f, 0.00f,0.00f,0.00f, 0.233f, 0.280f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } #define EAX30_PRESET_SPACESTATION_HALL \ { 26, 1.9f, 0.870f, -1000, -400, -100, 7.11f, 0.38f, 0.61f, -1500, 0.100f, 0.00f,0.00f,0.00f, -1000, 0.047f, 0.00f,0.00f,0.00f, 0.250f, 0.250f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } #define EAX30_PRESET_SPACESTATION_CUPBOARD \ { 26, 1.4f, 0.560f, -1000, -300, -100, 0.79f, 0.81f, 0.55f, 200, 0.007f, 0.00f,0.00f,0.00f, 400, 0.018f, 0.00f,0.00f,0.00f, 0.181f, 0.310f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } #define EAX30_PRESET_SPACESTATION_SMALLROOM \ { 26, 1.5f, 0.700f, -1000, -300, -100, 1.72f, 0.82f, 0.55f, -400, 0.007f, 0.00f,0.00f,0.00f, -500, 0.013f, 0.00f,0.00f,0.00f, 0.188f, 0.260f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } // WOODEN GALLEON PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_WOODEN_ALCOVE \ { 26, 7.5f, 1.000f, -1100, -1800, -1000, 1.22f, 0.62f, 0.91f, -100, 0.012f, 0.00f,0.00f,0.00f, -600, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } #define EAX30_PRESET_WOODEN_SHORTPASSAGE \ { 26, 7.5f, 1.000f, -1100, -1800, -1000, 1.45f, 0.50f, 0.87f, -300, 0.012f, 0.00f,0.00f,0.00f, -700, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } #define EAX30_PRESET_WOODEN_MEDIUMROOM \ { 26, 7.5f, 1.000f, -1200, -2000, -1100, 1.07f, 0.42f, 0.82f, -300, 0.039f, 0.00f,0.00f,0.00f, -400, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } #define EAX30_PRESET_WOODEN_LONGPASSAGE \ { 26, 7.5f, 1.000f, -1100, -2000, -1000, 1.79f, 0.40f, 0.79f, -200, 0.020f, 0.00f,0.00f,0.00f, -1000, 0.036f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } #define EAX30_PRESET_WOODEN_LARGEROOM \ { 26, 7.5f, 1.000f, -1200, -2100, -1100, 1.45f, 0.33f, 0.82f, -300, 0.056f, 0.00f,0.00f,0.00f, -500, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } #define EAX30_PRESET_WOODEN_HALL \ { 26, 7.5f, 1.000f, -1200, -2200, -1100, 1.95f, 0.30f, 0.82f, -300, 0.068f, 0.00f,0.00f,0.00f, -500, 0.063f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } #define EAX30_PRESET_WOODEN_CUPBOARD \ { 26, 7.5f, 1.000f, -1000, -1700, -1000, 0.56f, 0.46f, 0.91f, -100, 0.012f, 0.00f,0.00f,0.00f, -100, 0.028f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } #define EAX30_PRESET_WOODEN_SMALLROOM \ { 26, 7.5f, 1.000f, -1200, -1900, -1000, 0.79f, 0.32f, 0.87f, -200, 0.032f, 0.00f,0.00f,0.00f, -300, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } #define EAX30_PRESET_WOODEN_COURTYARD \ { 26, 7.5f, 0.650f, -1700, -2200, -1000, 1.79f, 0.35f, 0.79f, -700, 0.063f, 0.00f,0.00f,0.00f, -2300, 0.032f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } // OTHER SCENARIOS // SPORTS PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_SPORT_EMPTYSTADIUM \ { 26, 7.2f, 1.000f, -1300, -700, -200, 6.26f, 0.51f, 1.10f, -2400, 0.183f, 0.00f,0.00f,0.00f, -1100, 0.038f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x20 } #define EAX30_PRESET_SPORT_SQUASHCOURT \ { 26, 7.5f, 0.750f, -1100, -1000, -200, 2.22f, 0.91f, 1.16f, -700, 0.007f, 0.00f,0.00f,0.00f, -300, 0.011f, 0.00f,0.00f,0.00f, 0.126f, 0.190f, 0.250f, 0.000f, -0.0f, 7176.9f, 211.2f, 0.00f, 0x20 } #define EAX30_PRESET_SPORT_SMALLSWIMMINGPOOL \ { 26, 36.2f, 0.700f, -1400, -200, -100, 2.76f, 1.25f, 1.14f, -400, 0.020f, 0.00f,0.00f,0.00f, -300, 0.030f, 0.00f,0.00f,0.00f, 0.179f, 0.150f, 0.895f, 0.190f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } #define EAX30_PRESET_SPORT_LARGESWIMMINGPOOL\ { 26, 36.2f, 0.820f, -1200, -200, 0, 5.49f, 1.31f, 1.14f, -700, 0.039f, 0.00f,0.00f,0.00f, -800, 0.049f, 0.00f,0.00f,0.00f, 0.222f, 0.550f, 1.159f, 0.210f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } #define EAX30_PRESET_SPORT_GYMNASIUM \ { 26, 7.5f, 0.810f, -1200, -700, -100, 3.14f, 1.06f, 1.35f, -800, 0.029f, 0.00f,0.00f,0.00f, -700, 0.045f, 0.00f,0.00f,0.00f, 0.146f, 0.140f, 0.250f, 0.000f, -0.0f, 7176.9f, 211.2f, 0.00f, 0x20 } #define EAX30_PRESET_SPORT_FULLSTADIUM \ { 26, 7.2f, 1.000f, -1300, -2300, -200, 5.25f, 0.17f, 0.80f, -2000, 0.188f, 0.00f,0.00f,0.00f, -1300, 0.038f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x20 } #define EAX30_PRESET_SPORT_STADIUMTANNOY \ { 26, 3.0f, 0.780f, -900, -500, -600, 2.53f, 0.88f, 0.68f, -1100, 0.230f, 0.00f,0.00f,0.00f, -600, 0.063f, 0.00f,0.00f,0.00f, 0.250f, 0.200f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } // PREFAB PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_PREFAB_WORKSHOP \ { 26, 1.9f, 1.000f, -1000, -1700, -800, 0.76f, 1.00f, 1.00f, 0, 0.012f, 0.00f,0.00f,0.00f, -200, 0.012f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } #define EAX30_PRESET_PREFAB_SCHOOLROOM \ { 26, 1.86f, 0.690f, -1100, -400, -600, 0.98f, 0.45f, 0.18f, 300, 0.017f, 0.00f,0.00f,0.00f, 0, 0.015f, 0.00f,0.00f,0.00f, 0.095f, 0.140f, 0.250f, 0.000f, -0.0f, 7176.9f, 211.2f, 0.00f, 0x20 } #define EAX30_PRESET_PREFAB_PRACTISEROOM \ { 26, 1.86f, 0.870f, -1000, -800, -600, 1.12f, 0.56f, 0.18f, 200, 0.010f, 0.00f,0.00f,0.00f, -200, 0.011f, 0.00f,0.00f,0.00f, 0.095f, 0.140f, 0.250f, 0.000f, -0.0f, 7176.9f, 211.2f, 0.00f, 0x20 } #define EAX30_PRESET_PREFAB_OUTHOUSE \ { 26, 80.3f, 0.820f, -1100, -1900, -1600, 1.38f, 0.38f, 0.35f, -100, 0.024f, 0.00f,0.00f,-0.00f, -800, 0.044f, 0.00f,0.00f,0.00f, 0.121f, 0.170f, 0.250f, 0.000f, -0.0f, 2854.4f, 107.5f, 0.00f, 0x0 } #define EAX30_PRESET_PREFAB_CARAVAN \ { 26, 8.3f, 1.000f, -1000, -2100, -1800, 0.43f, 1.50f, 1.00f, 0, 0.012f, 0.00f,0.00f,0.00f, 400, 0.012f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } // for US developers, a caravan is the same as a trailer =o) // DOME AND PIPE PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_DOME_TOMB \ { 26, 51.8f, 0.790f, -1000, -900, -1300, 4.18f, 0.21f, 0.10f, -825, 0.030f, 0.00f,0.00f,0.00f, -125, 0.022f, 0.00f,0.00f,0.00f, 0.177f, 0.190f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x0 } #define EAX30_PRESET_PIPE_SMALL \ { 26, 50.3f, 1.000f, -1000, -900, -1300, 5.04f, 0.10f, 0.10f, -600, 0.032f, 0.00f,0.00f,0.00f, 400, 0.015f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x3f } #define EAX30_PRESET_DOME_SAINTPAULS \ { 26, 50.3f, 0.870f, -1000, -900, -1300, 10.48f, 0.19f, 0.10f, -1500, 0.090f, 0.00f,0.00f,0.00f, -500, 0.042f, 0.00f,0.00f,0.00f, 0.250f, 0.120f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x3f } #define EAX30_PRESET_PIPE_LONGTHIN \ { 26, 1.6f, 0.910f, -1200, -700, -1100, 9.21f, 0.18f, 0.10f, -300, 0.010f, 0.00f,0.00f,0.00f, -1000, 0.022f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x0 } #define EAX30_PRESET_PIPE_LARGE \ { 26, 50.3f, 1.000f, -1000, -900, -1300, 8.45f, 0.10f, 0.10f, -800, 0.046f, 0.00f,0.00f,0.00f, 0, 0.032f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x3f } #define EAX30_PRESET_PIPE_RESONANT \ { 26, 1.3f, 0.910f, -1200, -700, -1100, 6.81f, 0.18f, 0.10f, -300, 0.010f, 0.00f,0.00f,0.00f, -700, 0.022f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x0 } // OUTDOORS PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_OUTDOORS_BACKYARD \ { 26, 80.3f, 0.450f, -1100, -1200, -600, 1.12f, 0.34f, 0.46f, -1100, 0.049f, 0.00f,0.00f,-0.00f, -1300, 0.023f, 0.00f,0.00f,0.00f, 0.218f, 0.340f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } #define EAX30_PRESET_OUTDOORS_ROLLINGPLAINS \ { 26, 80.3f, 0.000f, -1100, -3900, -400, 2.13f, 0.21f, 0.46f, -2000, 0.300f, 0.00f,0.00f,-0.00f, -1500, 0.019f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } #define EAX30_PRESET_OUTDOORS_DEEPCANYON \ { 26, 80.3f, 0.740f, -1100, -1500, -400, 3.89f, 0.21f, 0.46f, -2000, 0.193f, 0.00f,0.00f,-0.00f, -1100, 0.019f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } #define EAX30_PRESET_OUTDOORS_CREEK \ { 26, 80.3f, 0.350f, -1100, -1500, -600, 2.13f, 0.21f, 0.46f, -1700, 0.115f, 0.00f,0.00f,-0.00f, -1100, 0.031f, 0.00f,0.00f,0.00f, 0.218f, 0.340f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } #define EAX30_PRESET_OUTDOORS_VALLEY \ { 26, 80.3f, 0.280f, -1100, -3100, -1600, 2.88f, 0.26f, 0.35f, -3200, 0.163f, 0.00f,0.00f,-0.00f, -1000, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 0.340f, 0.250f, 0.000f, -0.0f, 2854.4f, 107.5f, 0.00f, 0x0 } // MOOD PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_MOOD_HEAVEN \ { 26, 19.6f, 0.940f, -1000, -200, -700, 5.04f, 1.12f, 0.56f, -1230, 0.020f, 0.00f,0.00f,0.00f, -200, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.080f, 2.742f, 0.050f, -2.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_MOOD_HELL \ { 26, 100.0f, 0.570f, -1000, -900, -700, 3.57f, 0.49f, 2.00f, -10000, 0.020f, 0.00f,0.00f,0.00f, 100, 0.030f, 0.00f,0.00f,0.00f, 0.110f, 0.040f, 2.109f, 0.520f, -5.0f, 5000.0f, 139.5f, 0.00f, 0x40 } #define EAX30_PRESET_MOOD_MEMORY \ { 26, 8.0f, 0.850f, -1000, -400, -900, 4.06f, 0.82f, 0.56f, -2800, 0.000f, 0.00f,0.00f,0.00f, -500, 0.000f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.474f, 0.450f, -2.0f, 5000.0f, 250.0f, 0.00f, 0x0 } // DRIVING SIMULATION PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_DRIVING_COMMENTATOR \ { 26, 3.0f, 0.000f, -900, -500, -600, 2.42f, 0.88f, 0.68f, -1400, 0.093f, 0.00f,0.00f,0.00f, -1200, 0.017f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } #define EAX30_PRESET_DRIVING_PITGARAGE \ { 26, 1.9f, 0.590f, -1400, -300, -500, 1.72f, 0.93f, 0.87f, -500, 0.000f, 0.00f,0.00f,0.00f, 0, 0.016f, 0.00f,0.00f,0.00f, 0.250f, 0.110f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } #define EAX30_PRESET_DRIVING_INCAR_RACER \ { 26, 1.1f, 0.800f, -700, 0, -200, 0.17f, 2.00f, 0.41f, 500, 0.007f, 0.00f,0.00f,0.00f, -500, 0.015f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -0.0f, 10268.2f, 251.0f, 0.00f, 0x20 } #define EAX30_PRESET_DRIVING_INCAR_SPORTS \ { 26, 1.1f, 0.800f, -900, -400, 0, 0.17f, 0.75f, 0.41f, 0, 0.010f, 0.00f,0.00f,0.00f, -600, 0.000f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -0.0f, 10268.2f, 251.0f, 0.00f, 0x20 } #define EAX30_PRESET_DRIVING_INCAR_LUXURY \ { 26, 1.6f, 1.000f, -800, -2000, -600, 0.13f, 0.41f, 0.46f, -200, 0.010f, 0.00f,0.00f,0.00f, 300, 0.010f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -0.0f, 10268.2f, 251.0f, 0.00f, 0x20 } #define EAX30_PRESET_DRIVING_FULLGRANDSTAND \ { 26, 8.3f, 1.000f, -1100, -1100, -400, 3.01f, 1.37f, 1.28f, -900, 0.090f, 0.00f,0.00f,0.00f, -1700, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 10420.2f, 250.0f, 0.00f, 0x1f } #define EAX30_PRESET_DRIVING_EMPTYGRANDSTAND \ { 26, 8.3f, 1.000f, -700, 0, -200, 4.62f, 1.75f, 1.40f, -1363, 0.090f, 0.00f,0.00f,0.00f, -1900, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 10420.2f, 250.0f, 0.00f, 0x1f } #define EAX30_PRESET_DRIVING_TUNNEL \ { 26, 3.1f, 0.810f, -900, -800, -100, 3.42f, 0.94f, 1.31f, -300, 0.051f, 0.00f,0.00f,0.00f, -500, 0.047f, 0.00f,0.00f,0.00f, 0.214f, 0.050f, 0.250f, 0.000f, -0.0f, 5000.0f, 155.3f, 0.00f, 0x20 } // CITY PRESETS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_CITY_STREETS \ { 26, 3.0f, 0.780f, -1100, -300, -100, 1.79f, 1.12f, 0.91f, -1700, 0.046f, 0.00f,0.00f,0.00f, -2800, 0.028f, 0.00f,0.00f,0.00f, 0.250f, 0.200f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } #define EAX30_PRESET_CITY_SUBWAY \ { 26, 3.0f, 0.740f, -1100, -300, -100, 3.01f, 1.23f, 0.91f, -700, 0.046f, 0.00f,0.00f,0.00f, -1000, 0.028f, 0.00f,0.00f,0.00f, 0.125f, 0.210f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } #define EAX30_PRESET_CITY_MUSEUM \ { 26, 80.3f, 0.820f, -1100, -1500, -1500, 3.28f, 1.40f, 0.57f, -1600, 0.039f, 0.00f,0.00f,-0.00f, -600, 0.034f, 0.00f,0.00f,0.00f, 0.130f, 0.170f, 0.250f, 0.000f, -0.0f, 2854.4f, 107.5f, 0.00f, 0x0 } #define EAX30_PRESET_CITY_LIBRARY \ { 26, 80.3f, 0.820f, -1100, -1100, -2100, 2.76f, 0.89f, 0.41f, -1100, 0.029f, 0.00f,0.00f,-0.00f, -500, 0.020f, 0.00f,0.00f,0.00f, 0.130f, 0.170f, 0.250f, 0.000f, -0.0f, 2854.4f, 107.5f, 0.00f, 0x0 } #define EAX30_PRESET_CITY_UNDERPASS \ { 26, 3.0f, 0.820f, -1500, -700, -100, 3.57f, 1.12f, 0.91f, -1500, 0.059f, 0.00f,0.00f,0.00f, -1100, 0.037f, 0.00f,0.00f,0.00f, 0.250f, 0.140f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } #define EAX30_PRESET_CITY_ABANDONED \ { 26, 3.0f, 0.690f, -1100, -200, -100, 3.28f, 1.17f, 0.91f, -1400, 0.044f, 0.00f,0.00f,0.00f, -2400, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.200f, 0.250f, 0.000f, -0.0f, 5000.0f, 250.0f, 0.00f, 0x20 } // MISC ROOMS // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS #define EAX30_PRESET_DUSTYROOM \ { 26, 1.8f, 0.560f, -1100, -200, -300, 1.79f, 0.38f, 0.21f, -600, 0.002f, 0.00f,0.00f,0.00f, 200, 0.006f, 0.00f,0.00f,0.00f, 0.202f, 0.050f, 0.250f, 0.000f, -3.0f, 13046.0f, 163.3f, 0.00f, 0x20 } #define EAX30_PRESET_CHAPEL \ { 26, 19.6f, 0.840f, -1000, -500, 0, 4.62f, 0.64f, 1.23f, -700, 0.032f, 0.00f,0.00f,0.00f, -800, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.110f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } #define EAX30_PRESET_SMALLWATERROOM \ { 26, 36.2f, 0.700f, -1200, -698, 0, 1.51f, 1.25f, 1.14f, -100, 0.020f, 0.00f,0.00f,0.00f, 200, 0.030f, 0.00f,0.00f,0.00f, 0.179f, 0.150f, 0.895f, 0.190f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } /********************************************************************************************************/ ////////////////////////////////////////////////////// // Effect Scenarios enumerated // ////////////////////////////////////////////////////// typedef enum { EAX30_SCENARIO_CASTLE = 0, EAX30_SCENARIO_FACTORY, EAX30_SCENARIO_ICEPALACE, EAX30_SCENARIO_SPACESTATION, EAX30_SCENARIO_WOODGALLEON, EAX30_SCENARIO_SPORTS, EAX30_SCENARIO_PREFAB, EAX30_SCENARIO_DOMESNPIPES, EAX30_SCENARIO_OUTDOORS, EAX30_SCENARIO_MOOD, EAX30_SCENARIO_DRIVING, EAX30_SCENARIO_CITY, EAX30_SCENARIO_MISC, EAX30_SCENARIO_ORIGINAL } EAX30_SCENARIO; ////////////////////////////////////////////////////// // Number of Effect Scenarios // ////////////////////////////////////////////////////// #define EAX30_NUM_SCENARIOS 14 ////////////////////////////////////////////////////// // Number of Effect Scenarios with standardised // // locations // ////////////////////////////////////////////////////// #define EAX30_NUM_STANDARD_SCENARIOS 5 ////////////////////////////////////////////////////// // Array of scenario names // ////////////////////////////////////////////////////// extern const char* EAX30_SCENARIO_NAMES[]; ////////////////////////////////////////////////////// // Standardised Locations enumerated // ////////////////////////////////////////////////////// typedef enum { EAX30_LOCATION_HALL = 0, EAX30_LOCATION_LARGEROOM, EAX30_LOCATION_MEDIUMROOM, EAX30_LOCATION_SMALLROOM, EAX30_LOCATION_CUPBOARD, EAX30_LOCATION_ALCOVE, EAX30_LOCATION_LONGPASSAGE, EAX30_LOCATION_SHORTPASSAGE, EAX30_LOCATION_COURTYARD } EAX30_LOCATION; ////////////////////////////////////////////////////// // Number of Standardised Locations // ////////////////////////////////////////////////////// #define EAX30_NUM_LOCATIONS 9 ////////////////////////////////////////////////////// // Array of standardised location names // ////////////////////////////////////////////////////// extern const char* EAX30_LOCATION_NAMES[]; ////////////////////////////////////////////////////// // Number of effects in each scenario // ////////////////////////////////////////////////////// #define EAX30_NUM_ORIGINAL_PRESETS 26 #define EAX30_NUM_CASTLE_PRESETS EAX30_NUM_LOCATIONS #define EAX30_NUM_FACTORY_PRESETS EAX30_NUM_LOCATIONS #define EAX30_NUM_ICEPALACE_PRESETS EAX30_NUM_LOCATIONS #define EAX30_NUM_SPACESTATION_PRESETS EAX30_NUM_LOCATIONS #define EAX30_NUM_WOODGALLEON_PRESETS EAX30_NUM_LOCATIONS #define EAX30_NUM_SPORTS_PRESETS 7 #define EAX30_NUM_PREFAB_PRESETS 5 #define EAX30_NUM_DOMESNPIPES_PRESETS 6 #define EAX30_NUM_OUTDOORS_PRESETS 5 #define EAX30_NUM_MOOD_PRESETS 3 #define EAX30_NUM_DRIVING_PRESETS 8 #define EAX30_NUM_CITY_PRESETS 6 #define EAX30_NUM_MISC_PRESETS 3 ////////////////////////////////////////////////////// // Standardised Location effects can be accessed // // from a matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_STANDARD_PRESETS[EAX30_NUM_STANDARD_SCENARIOS][EAX30_NUM_LOCATIONS]; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Original Preset effects enumerated // ////////////////////////////////////////////////////// typedef enum { ORIGINAL_GENERIC = 0, ORIGINAL_PADDEDCELL, ORIGINAL_ROOM, ORIGINAL_BATHROOM, ORIGINAL_LIVINGROOM, ORIGINAL_STONEROOM, ORIGINAL_AUDITORIUM, ORIGINAL_CONCERTHALL, ORIGINAL_CAVE, ORIGINAL_ARENA, ORIGINAL_HANGAR, ORIGINAL_CARPETTEDHALLWAY, ORIGINAL_HALLWAY, ORIGINAL_STONECORRIDOR, ORIGINAL_ALLEY, ORIGINAL_FOREST, ORIGINAL_CITY, ORIGINAL_MOUNTAINS, ORIGINAL_QUARRY, ORIGINAL_PLAIN, ORIGINAL_PARKINGLOT, ORIGINAL_SEWERPIPE, ORIGINAL_UNDERWATER, ORIGINAL_DRUGGED, ORIGINAL_DIZZY, ORIGINAL_PSYCHOTIC } EAX30_ORIGINAL_PRESET_ENUMS; ////////////////////////////////////////////////////// // Array of original environment names // ////////////////////////////////////////////////////// extern const char* EAX30_ORIGINAL_PRESET_NAMES[]; ////////////////////////////////////////////////////// // Original effects matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_ORIGINAL_PRESETS[]; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Sports scenario effects enumerated // ////////////////////////////////////////////////////// typedef enum { SPORT_EMPTYSTADIUM=0, SPORT_FULLSTADIUM, SPORT_STADIUMTANNOY, SPORT_SQUASHCOURT, SPORT_SMALLSWIMMINGPOOL, SPORT_LARGESWIMMINGPOOL, SPORT_GYMNASIUM } EAX30_SPORTS_PRESET_ENUMS; ////////////////////////////////////////////////////// // Array of sport environment names // ////////////////////////////////////////////////////// extern const char* EAX30_SPORTS_PRESET_NAMES[]; ////////////////////////////////////////////////////// // Sports effects matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_SPORTS_PRESETS[]; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Prefab scenario effects enumerated // ////////////////////////////////////////////////////// typedef enum { PREFAB_WORKSHOP, PREFAB_SCHOOLROOM, PREFAB_PRACTISEROOM, PREFAB_OUTHOUSE, PREFAB_CARAVAN } EAX30_PREFAB_PRESET_ENUMS; ////////////////////////////////////////////////////// // Array of prefab environment names // ////////////////////////////////////////////////////// extern const char* EAX30_PREFAB_PRESET_NAMES[]; ////////////////////////////////////////////////////// // Prefab effects matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_PREFAB_PRESETS[]; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Domes & Pipes effects enumerated // ////////////////////////////////////////////////////// typedef enum { DOME_TOMB, DOME_SAINTPAULS, PIPE_SMALL, PIPE_LONGTHIN, PIPE_LARGE, PIPE_RESONANT } EAX30_DOMESNPIPES_PRESET_ENUMS; ////////////////////////////////////////////////////// // Array of Domes & Pipes environment names // ////////////////////////////////////////////////////// extern const char* EAX30_DOMESNPIPES_PRESET_NAMES[]; ////////////////////////////////////////////////////// // Domes & Pipes effects matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_DOMESNPIPES_PRESETS[]; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Outdoors scenario effects enumerated // ////////////////////////////////////////////////////// typedef enum { OUTDOORS_BACKYARD, OUTDOORS_ROLLINGPLAINS, OUTDOORS_DEEPCANYON, OUTDOORS_CREEK, OUTDOORS_VALLEY } EAX30_OUTDOORS_PRESET_ENUMS; ////////////////////////////////////////////////////// // Array of Outdoors environment names // ////////////////////////////////////////////////////// extern const char* EAX30_OUTDOORS_PRESET_NAMES[]; ////////////////////////////////////////////////////// // Outdoors effects matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_OUTDOORS_PRESETS[]; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Mood scenario effects enumerated // ////////////////////////////////////////////////////// typedef enum { MOOD_HEAVEN, MOOD_HELL, MOOD_MEMORY } EAX30_MOOD_PRESET_ENUMS; ////////////////////////////////////////////////////// // Array of Mood environment names // ////////////////////////////////////////////////////// extern const char* EAX30_MOOD_PRESET_NAMES[]; ////////////////////////////////////////////////////// // Mood effects matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_MOOD_PRESETS[]; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Driving scenario effects enumerated // ////////////////////////////////////////////////////// typedef enum { DRIVING_COMMENTATOR, DRIVING_PITGARAGE, DRIVING_INCAR_RACER, DRIVING_INCAR_SPORTS, DRIVING_INCAR_LUXURY, DRIVING_FULLGRANDSTAND, DRIVING_EMPTYGRANDSTAND, DRIVING_TUNNEL } EAX30_DRIVING_PRESET_ENUMS; ////////////////////////////////////////////////////// // Array of driving environment names // ////////////////////////////////////////////////////// extern const char* EAX30_DRIVING_PRESET_NAMES[]; ////////////////////////////////////////////////////// // Driving effects matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_DRIVING_PRESETS[]; /********************************************************************************************************/ ////////////////////////////////////////////////////// // City scenario effects enumerated // ////////////////////////////////////////////////////// typedef enum { CITY_STREETS, CITY_SUBWAY, CITY_MUSEUM, CITY_LIBRARY, CITY_UNDERPASS, CITY_ABANDONED } EAX30_CITY_PRESET_ENUMS; ////////////////////////////////////////////////////// // Array of City environment names // ////////////////////////////////////////////////////// extern const char* EAX30_CITY_PRESET_NAMES[]; ////////////////////////////////////////////////////// // City effects matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_CITY_PRESETS[]; /********************************************************************************************************/ ////////////////////////////////////////////////////// // Misc scenario effects enumerated // ////////////////////////////////////////////////////// typedef enum { DUSTYROOM, CHAPEL, SMALLWATERROOM } EAX30_MISC_PRESET_ENUMS; ////////////////////////////////////////////////////// // Array of Misc environment names // ////////////////////////////////////////////////////// extern const char* EAX30_MISC_PRESET_NAMES[]; ////////////////////////////////////////////////////// // Misc effects matrix // ////////////////////////////////////////////////////// extern EAXLISTENERPROPERTIES EAX30_MISC_PRESETS[]; /***********************************************************************************************\ * * Material transmission presets * * Three values in this order :- * * 1. Occlusion (or Obstruction) * 2. Occlusion LF Ratio (or Obstruction LF Ratio) * 3. Occlusion Room Ratio * ************************************************************************************************/ // Single window material preset #define EAX_MATERIAL_SINGLEWINDOW (-2800) #define EAX_MATERIAL_SINGLEWINDOWLF 0.71f #define EAX_MATERIAL_SINGLEWINDOWROOMRATIO 0.43f // Double window material preset #define EAX_MATERIAL_DOUBLEWINDOW (-5000) #define EAX_MATERIAL_DOUBLEWINDOWLF 0.40f #define EAX_MATERIAL_DOUBLEWINDOWROOMRATIO 0.24f // Thin door material preset #define EAX_MATERIAL_THINDOOR (-1800) #define EAX_MATERIAL_THINDOORLF 0.66f #define EAX_MATERIAL_THINDOORROOMRATIO 0.66f // Thick door material preset #define EAX_MATERIAL_THICKDOOR (-4400) #define EAX_MATERIAL_THICKDOORLF 0.64f #define EAX_MATERIAL_THICKDOORROOMRATIO 0.27f // Wood wall material preset #define EAX_MATERIAL_WOODWALL (-4000) #define EAX_MATERIAL_WOODWALLLF 0.50f #define EAX_MATERIAL_WOODWALLROOMRATIO 0.30f // Brick wall material preset #define EAX_MATERIAL_BRICKWALL (-5000) #define EAX_MATERIAL_BRICKWALLLF 0.60f #define EAX_MATERIAL_BRICKWALLROOMRATIO 0.24f // Stone wall material preset #define EAX_MATERIAL_STONEWALL (-6000) #define EAX_MATERIAL_STONEWALLLF 0.68f #define EAX_MATERIAL_STONEWALLROOMRATIO 0.20f // Curtain material preset #define EAX_MATERIAL_CURTAIN (-1200) #define EAX_MATERIAL_CURTAINLF 0.15f #define EAX_MATERIAL_CURTAINROOMRATIO 1.00f #endif // EAXUTIL_INCLUDED ================================================ FILE: src/audio/eax/eax.h ================================================ /*******************************************************************\ * * * EAX.H - Environmental Audio Extensions version 3.0 * * for OpenAL and DirectSound3D * * * ********************************************************************/ #ifndef EAX_H_INCLUDED #define EAX_H_INCLUDED #ifdef __cplusplus extern "C" { #endif // __cplusplus #ifndef AUDIO_OAL #include /* * EAX Wrapper Interface (using Direct X 7) {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} */ DEFINE_GUID(CLSID_EAXDirectSound, 0x4ff53b81, 0x1ce0, 0x11d3, 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); /* * EAX Wrapper Interface (using Direct X 8) {CA503B60-B176-11d4-A094-D0C0BF3A560C} */ DEFINE_GUID(CLSID_EAXDirectSound8, 0xca503b60, 0xb176, 0x11d4, 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc); #ifdef DIRECTSOUND_VERSION #if DIRECTSOUND_VERSION >= 0x0800 __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate8(GUID*, LPDIRECTSOUND8*, IUnknown FAR *); typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE8)(GUID*, LPDIRECTSOUND8*, IUnknown FAR*); #endif #endif __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); __declspec(dllimport) void CDECL GetCurrentVersion(LPDWORD major, LPDWORD minor); typedef void (CDECL *LPGETCURRENTVERSION)(LPDWORD major, LPDWORD minor); #else // AUDIO_OAL #include #include #ifndef GUID_DEFINED #define GUID_DEFINED typedef struct _GUID { unsigned long Data1; unsigned short Data2; unsigned short Data3; unsigned char Data4[8]; } GUID; #endif // !GUID_DEFINED #ifndef DEFINE_GUID #ifndef INITGUID #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ extern const GUID /*FAR*/ name #else #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } #endif // INITGUID #endif // DEFINE_GUID /* * EAX OpenAL Extension */ typedef ALenum (*EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); typedef ALenum (*EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); #endif #pragma pack(push, 4) /* * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} */ DEFINE_GUID(DSPROPSETID_EAX30_ListenerProperties, 0xa8fa6882, 0xb476, 0x11d3, 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); // For compatibility with future EAX versions: #define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties typedef enum { DSPROPERTY_EAXLISTENER_NONE, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, DSPROPERTY_EAXLISTENER_ENVIRONMENT, DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, DSPROPERTY_EAXLISTENER_ROOM, DSPROPERTY_EAXLISTENER_ROOMHF, DSPROPERTY_EAXLISTENER_ROOMLF, DSPROPERTY_EAXLISTENER_DECAYTIME, DSPROPERTY_EAXLISTENER_DECAYHFRATIO, DSPROPERTY_EAXLISTENER_DECAYLFRATIO, DSPROPERTY_EAXLISTENER_REFLECTIONS, DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, DSPROPERTY_EAXLISTENER_REVERB, DSPROPERTY_EAXLISTENER_REVERBDELAY, DSPROPERTY_EAXLISTENER_REVERBPAN, DSPROPERTY_EAXLISTENER_ECHOTIME, DSPROPERTY_EAXLISTENER_ECHODEPTH, DSPROPERTY_EAXLISTENER_MODULATIONTIME, DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, DSPROPERTY_EAXLISTENER_HFREFERENCE, DSPROPERTY_EAXLISTENER_LFREFERENCE, DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, DSPROPERTY_EAXLISTENER_FLAGS } DSPROPERTY_EAX_LISTENERPROPERTY; // OR these flags with property id #define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately #define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later #define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ DSPROPERTY_EAXLISTENER_IMMEDIATE) typedef struct _EAXVECTOR { float x; float y; float z; } EAXVECTOR; // Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS // - all levels are hundredths of decibels // - all times and delays are in seconds // // NOTE: This structure may change in future EAX versions. // It is recommended to initialize fields by name: // myListener.lRoom = -1000; // myListener.lRoomHF = -100; // ... // myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; // instead of: // myListener = { -1000, -100, ... , 0x00000009 }; // If you want to save and load presets in binary form, you // should define your own structure to insure future compatibility. // typedef struct _EAXLISTENERPROPERTIES { unsigned long ulEnvironment; // sets all listener properties float flEnvironmentSize; // environment size in meters float flEnvironmentDiffusion; // environment diffusion long lRoom; // room effect level (at mid frequencies) long lRoomHF; // relative room effect level at high frequencies long lRoomLF; // relative room effect level at low frequencies float flDecayTime; // reverberation decay time at mid frequencies float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio long lReflections; // early reflections level relative to room effect float flReflectionsDelay; // initial reflection delay time EAXVECTOR vReflectionsPan; // early reflections panning vector long lReverb; // late reverberation level relative to room effect float flReverbDelay; // late reverberation delay time relative to initial reflection EAXVECTOR vReverbPan; // late reverberation panning vector float flEchoTime; // echo time float flEchoDepth; // echo depth float flModulationTime; // modulation time float flModulationDepth; // modulation depth float flAirAbsorptionHF; // change in level per meter at high frequencies float flHFReference; // reference high frequency float flLFReference; // reference low frequency float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect unsigned long ulFlags; // modifies the behavior of properties } EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; // used by DSPROPERTY_EAXLISTENER_ENVIRONMENT enum { EAX_ENVIRONMENT_GENERIC, EAX_ENVIRONMENT_PADDEDCELL, EAX_ENVIRONMENT_ROOM, EAX_ENVIRONMENT_BATHROOM, EAX_ENVIRONMENT_LIVINGROOM, EAX_ENVIRONMENT_STONEROOM, EAX_ENVIRONMENT_AUDITORIUM, EAX_ENVIRONMENT_CONCERTHALL, EAX_ENVIRONMENT_CAVE, EAX_ENVIRONMENT_ARENA, EAX_ENVIRONMENT_HANGAR, EAX_ENVIRONMENT_CARPETEDHALLWAY, EAX_ENVIRONMENT_HALLWAY, EAX_ENVIRONMENT_STONECORRIDOR, EAX_ENVIRONMENT_ALLEY, EAX_ENVIRONMENT_FOREST, EAX_ENVIRONMENT_CITY, EAX_ENVIRONMENT_MOUNTAINS, EAX_ENVIRONMENT_QUARRY, EAX_ENVIRONMENT_PLAIN, EAX_ENVIRONMENT_PARKINGLOT, EAX_ENVIRONMENT_SEWERPIPE, EAX_ENVIRONMENT_UNDERWATER, EAX_ENVIRONMENT_DRUGGED, EAX_ENVIRONMENT_DIZZY, EAX_ENVIRONMENT_PSYCHOTIC, EAX_ENVIRONMENT_UNDEFINED, EAX_ENVIRONMENT_COUNT }; // Used by DSPROPERTY_EAXLISTENER_FLAGS // // Note: The number and order of flags may change in future EAX versions. // It is recommended to use the flag defines as follows: // myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; // instead of: // myFlags = 0x00000009; // // These flags determine what properties are affected by environment size. #define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time #define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level #define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time #define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level #define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time #define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time #define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time // This flag limits high-frequency decay time according to air absorption. #define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 #define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use // Property ranges and defaults: #define EAXLISTENER_MINENVIRONMENT 0 #define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) #define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC #define EAXLISTENER_MINENVIRONMENTSIZE 1.0f #define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f #define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f #define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f #define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f #define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f #define EAXLISTENER_MINROOM (-10000) #define EAXLISTENER_MAXROOM 0 #define EAXLISTENER_DEFAULTROOM (-1000) #define EAXLISTENER_MINROOMHF (-10000) #define EAXLISTENER_MAXROOMHF 0 #define EAXLISTENER_DEFAULTROOMHF (-100) #define EAXLISTENER_MINROOMLF (-10000) #define EAXLISTENER_MAXROOMLF 0 #define EAXLISTENER_DEFAULTROOMLF 0 #define EAXLISTENER_MINDECAYTIME 0.1f #define EAXLISTENER_MAXDECAYTIME 20.0f #define EAXLISTENER_DEFAULTDECAYTIME 1.49f #define EAXLISTENER_MINDECAYHFRATIO 0.1f #define EAXLISTENER_MAXDECAYHFRATIO 2.0f #define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f #define EAXLISTENER_MINDECAYLFRATIO 0.1f #define EAXLISTENER_MAXDECAYLFRATIO 2.0f #define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f #define EAXLISTENER_MINREFLECTIONS (-10000) #define EAXLISTENER_MAXREFLECTIONS 1000 #define EAXLISTENER_DEFAULTREFLECTIONS (-2602) #define EAXLISTENER_MINREFLECTIONSDELAY 0.0f #define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f #define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f #define EAXLISTENER_MINREVERB (-10000) #define EAXLISTENER_MAXREVERB 2000 #define EAXLISTENER_DEFAULTREVERB 200 #define EAXLISTENER_MINREVERBDELAY 0.0f #define EAXLISTENER_MAXREVERBDELAY 0.1f #define EAXLISTENER_DEFAULTREVERBDELAY 0.011f #define EAXLISTENER_MINECHOTIME 0.075f #define EAXLISTENER_MAXECHOTIME 0.25f #define EAXLISTENER_DEFAULTECHOTIME 0.25f #define EAXLISTENER_MINECHODEPTH 0.0f #define EAXLISTENER_MAXECHODEPTH 1.0f #define EAXLISTENER_DEFAULTECHODEPTH 0.0f #define EAXLISTENER_MINMODULATIONTIME 0.04f #define EAXLISTENER_MAXMODULATIONTIME 4.0f #define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f #define EAXLISTENER_MINMODULATIONDEPTH 0.0f #define EAXLISTENER_MAXMODULATIONDEPTH 1.0f #define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f #define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) #define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f #define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) #define EAXLISTENER_MINHFREFERENCE 1000.0f #define EAXLISTENER_MAXHFREFERENCE 20000.0f #define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f #define EAXLISTENER_MINLFREFERENCE 20.0f #define EAXLISTENER_MAXLFREFERENCE 1000.0f #define EAXLISTENER_DEFAULTLFREFERENCE 250.0f #define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f #define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f #define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f #define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ EAXLISTENERFLAGS_REFLECTIONSSCALE | \ EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ EAXLISTENERFLAGS_REVERBSCALE | \ EAXLISTENERFLAGS_REVERBDELAYSCALE | \ EAXLISTENERFLAGS_DECAYHFLIMIT) /* * EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} */ DEFINE_GUID(DSPROPSETID_EAX30_BufferProperties, 0xa8fa6881, 0xb476, 0x11d3, 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); // For compatibility with future EAX versions: #define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties #define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties typedef enum { DSPROPERTY_EAXBUFFER_NONE, DSPROPERTY_EAXBUFFER_ALLPARAMETERS, DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, DSPROPERTY_EAXBUFFER_DIRECT, DSPROPERTY_EAXBUFFER_DIRECTHF, DSPROPERTY_EAXBUFFER_ROOM, DSPROPERTY_EAXBUFFER_ROOMHF, DSPROPERTY_EAXBUFFER_OBSTRUCTION, DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, DSPROPERTY_EAXBUFFER_OCCLUSION, DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, DSPROPERTY_EAXBUFFER_EXCLUSION, DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, DSPROPERTY_EAXBUFFER_FLAGS } DSPROPERTY_EAX_BUFFERPROPERTY; // OR these flags with property id #define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately #define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later #define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ DSPROPERTY_EAXBUFFER_IMMEDIATE) // Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS // - all levels are hundredths of decibels // - all delays are in seconds // // NOTE: This structure may change in future EAX versions. // It is recommended to initialize fields by name: // myBuffer.lDirect = 0; // myBuffer.lDirectHF = -200; // ... // myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; // instead of: // myBuffer = { 0, -200, ... , 0x00000003 }; // typedef struct _EAXBUFFERPROPERTIES { long lDirect; // direct path level (at low and mid frequencies) long lDirectHF; // relative direct path level at high frequencies long lRoom; // room effect level (at low and mid frequencies) long lRoomHF; // relative room effect level at high frequencies long lObstruction; // main obstruction control (attenuation at high frequencies) float flObstructionLFRatio; // obstruction low-frequency level re. main control long lOcclusion; // main occlusion control (attenuation at high frequencies) float flOcclusionLFRatio; // occlusion low-frequency level re. main control float flOcclusionRoomRatio; // relative occlusion control for room effect float flOcclusionDirectRatio; // relative occlusion control for direct path long lExclusion; // main exlusion control (attenuation at high frequencies) float flExclusionLFRatio; // exclusion low-frequency level re. main control long lOutsideVolumeHF; // outside sound cone level at high frequencies float flDopplerFactor; // like DS3D flDopplerFactor but per source float flRolloffFactor; // like DS3D flRolloffFactor but per source float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF unsigned long ulFlags; // modifies the behavior of properties } EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; // Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, typedef struct _EAXOBSTRUCTIONPROPERTIES { long lObstruction; float flObstructionLFRatio; } EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; // Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION typedef struct _EAXOCCLUSIONPROPERTIES { long lOcclusion; float flOcclusionLFRatio; float flOcclusionRoomRatio; float flOcclusionDirectRatio; } EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; // Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION typedef struct _EAXEXCLUSIONPROPERTIES { long lExclusion; float flExclusionLFRatio; } EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; // Used by DSPROPERTY_EAXBUFFER_FLAGS // TRUE: value is computed automatically - property is an offset // FALSE: value is used directly // // Note: The number and order of flags may change in future EAX versions. // To insure future compatibility, use flag defines as follows: // myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; // instead of: // myFlags = 0x00000003; // #define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF #define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM #define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF #define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use // Property ranges and defaults: #define EAXBUFFER_MINDIRECT (-10000) #define EAXBUFFER_MAXDIRECT 1000 #define EAXBUFFER_DEFAULTDIRECT 0 #define EAXBUFFER_MINDIRECTHF (-10000) #define EAXBUFFER_MAXDIRECTHF 0 #define EAXBUFFER_DEFAULTDIRECTHF 0 #define EAXBUFFER_MINROOM (-10000) #define EAXBUFFER_MAXROOM 1000 #define EAXBUFFER_DEFAULTROOM 0 #define EAXBUFFER_MINROOMHF (-10000) #define EAXBUFFER_MAXROOMHF 0 #define EAXBUFFER_DEFAULTROOMHF 0 #define EAXBUFFER_MINOBSTRUCTION (-10000) #define EAXBUFFER_MAXOBSTRUCTION 0 #define EAXBUFFER_DEFAULTOBSTRUCTION 0 #define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f #define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f #define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f #define EAXBUFFER_MINOCCLUSION (-10000) #define EAXBUFFER_MAXOCCLUSION 0 #define EAXBUFFER_DEFAULTOCCLUSION 0 #define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f #define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f #define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f #define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f #define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f #define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f #define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f #define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f #define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f #define EAXBUFFER_MINEXCLUSION (-10000) #define EAXBUFFER_MAXEXCLUSION 0 #define EAXBUFFER_DEFAULTEXCLUSION 0 #define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f #define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f #define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f #define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) #define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 #define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 #define EAXBUFFER_MINDOPPLERFACTOR 0.0f #define EAXBUFFER_MAXDOPPLERFACTOR 10.f #define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f #define EAXBUFFER_MINROLLOFFFACTOR 0.0f #define EAXBUFFER_MAXROLLOFFFACTOR 10.f #define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f #define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f #define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f #define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f #define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f #define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f #define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f #define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ EAXBUFFERFLAGS_ROOMAUTO | \ EAXBUFFERFLAGS_ROOMHFAUTO ) #pragma pack(pop) #ifdef __cplusplus } #endif // __cplusplus #endif ================================================ FILE: src/audio/oal/aldlist.cpp ================================================ /* * Copyright (c) 2006, Creative Labs Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided * that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of conditions and * the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions * and the following disclaimer in the documentation and/or other materials provided with the distribution. * * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to endorse or * promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "aldlist.h" #ifndef _WIN32 #define _stricmp strcasecmp #define _strnicmp strncasecmp #define _strdup strdup #endif #ifdef AUDIO_OAL /* * Init call */ ALDeviceList::ALDeviceList() { char *devices; int index; const char *defaultDeviceName; const char *actualDeviceName; // DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec version #, and extension support nNumOfDevices = 0; defaultDeviceIndex = 0; if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) { devices = (char *)alcGetString(NULL, ALC_DEVICE_SPECIFIER); defaultDeviceName = (char *)alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); index = 0; // go through device list (each device terminated with a single NULL, list terminated with double NULL) while (*devices != '\0') { if (strcmp(defaultDeviceName, devices) == 0) { defaultDeviceIndex = index; } ALCdevice *device = alcOpenDevice(devices); if (device) { ALCcontext *context = alcCreateContext(device, NULL); if (context) { alcMakeContextCurrent(context); // if new actual device name isn't already in the list, then add it... actualDeviceName = alcGetString(device, ALC_DEVICE_SPECIFIER); bool bNewName = true; for (unsigned int i = 0; i < GetNumDevices(); i++) { if (strcmp(GetDeviceName(i), actualDeviceName) == 0) { bNewName = false; } } if ((bNewName) && (actualDeviceName != NULL) && (strlen(actualDeviceName) > 0)) { ALDEVICEINFO ALDeviceInfo; ALDeviceInfo.bSelected = true; ALDeviceInfo.strDeviceName = _strdup(actualDeviceName); alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(int), &ALDeviceInfo.iMajorVersion); alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(int), &ALDeviceInfo.iMinorVersion); // Check for ALC Extensions if (alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EXT_CAPTURE; if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EXT_EFX; // Check for AL Extensions if (alIsExtensionPresent("AL_EXT_OFFSET") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EXT_OFFSET; if (alIsExtensionPresent("AL_EXT_LINEAR_DISTANCE") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EXT_LINEAR_DISTANCE; if (alIsExtensionPresent("AL_EXT_EXPONENT_DISTANCE") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EXT_EXPONENT_DISTANCE; if (alIsExtensionPresent("EAX2.0") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EAX2; if (alIsExtensionPresent("EAX3.0") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EAX3; if (alIsExtensionPresent("EAX4.0") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EAX4; if (alIsExtensionPresent("EAX5.0") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EAX5; if (alIsExtensionPresent("EAX-RAM") == AL_TRUE) ALDeviceInfo.Extensions |= ADEXT_EAX_RAM; // Get Source Count ALDeviceInfo.uiSourceCount = GetMaxNumSources(); aDeviceInfo[nNumOfDevices++] = ALDeviceInfo; } alcMakeContextCurrent(NULL); alcDestroyContext(context); } alcCloseDevice(device); } devices += strlen(devices) + 1; index += 1; } } ResetFilters(); } /* * Exit call */ ALDeviceList::~ALDeviceList() { } /* * Returns the number of devices in the complete device list */ unsigned int ALDeviceList::GetNumDevices() { return nNumOfDevices; } /* * Returns the device name at an index in the complete device list */ const char * ALDeviceList::GetDeviceName(unsigned int index) { if (index < GetNumDevices()) return aDeviceInfo[index].strDeviceName; else return NULL; } /* * Returns the major and minor version numbers for a device at a specified index in the complete list */ void ALDeviceList::GetDeviceVersion(unsigned int index, int *major, int *minor) { if (index < GetNumDevices()) { if (major) *major = aDeviceInfo[index].iMajorVersion; if (minor) *minor = aDeviceInfo[index].iMinorVersion; } return; } /* * Returns the maximum number of Sources that can be generate on the given device */ unsigned int ALDeviceList::GetMaxNumSources(unsigned int index) { if (index < GetNumDevices()) return aDeviceInfo[index].uiSourceCount; else return 0; } /* * Checks if the extension is supported on the given device */ bool ALDeviceList::IsExtensionSupported(int index, unsigned short ext) { return !!(aDeviceInfo[index].Extensions & ext); } /* * returns the index of the default device in the complete device list */ int ALDeviceList::GetDefaultDevice() { return defaultDeviceIndex; } /* * Deselects devices which don't have the specified minimum version */ void ALDeviceList::FilterDevicesMinVer(int major, int minor) { int dMajor, dMinor; for (unsigned int i = 0; i < nNumOfDevices; i++) { GetDeviceVersion(i, &dMajor, &dMinor); if ((dMajor < major) || ((dMajor == major) && (dMinor < minor))) { aDeviceInfo[i].bSelected = false; } } } /* * Deselects devices which don't have the specified maximum version */ void ALDeviceList::FilterDevicesMaxVer(int major, int minor) { int dMajor, dMinor; for (unsigned int i = 0; i < nNumOfDevices; i++) { GetDeviceVersion(i, &dMajor, &dMinor); if ((dMajor > major) || ((dMajor == major) && (dMinor > minor))) { aDeviceInfo[i].bSelected = false; } } } /* * Deselects device which don't support the given extension name */ void ALDeviceList::FilterDevicesExtension(unsigned short ext) { for (unsigned int i = 0; i < nNumOfDevices; i++) { if (!IsExtensionSupported(i, ext)) aDeviceInfo[i].bSelected = false; } } /* * Resets all filtering, such that all devices are in the list */ void ALDeviceList::ResetFilters() { for (unsigned int i = 0; i < GetNumDevices(); i++) { aDeviceInfo[i].bSelected = true; } filterIndex = 0; } /* * Gets index of first filtered device */ int ALDeviceList::GetFirstFilteredDevice() { unsigned int i; for (i = 0; i < GetNumDevices(); i++) { if (aDeviceInfo[i].bSelected == true) { break; } } filterIndex = i + 1; return i; } /* * Gets index of next filtered device */ int ALDeviceList::GetNextFilteredDevice() { unsigned int i; for (i = filterIndex; i < GetNumDevices(); i++) { if (aDeviceInfo[i].bSelected == true) { break; } } filterIndex = i + 1; return i; } /* * Internal function to detemine max number of Sources that can be generated */ unsigned int ALDeviceList::GetMaxNumSources() { ALuint uiSources[256]; unsigned int iSourceCount = 0; // Clear AL Error Code alGetError(); // Generate up to 256 Sources, checking for any errors for (iSourceCount = 0; iSourceCount < 256; iSourceCount++) { alGenSources(1, &uiSources[iSourceCount]); if (alGetError() != AL_NO_ERROR) break; } // Release the Sources alDeleteSources(iSourceCount, uiSources); if (alGetError() != AL_NO_ERROR) { for (unsigned int i = 0; i < 256; i++) { alDeleteSources(1, &uiSources[i]); } } return iSourceCount; } #endif ================================================ FILE: src/audio/oal/aldlist.h ================================================ #ifndef ALDEVICELIST_H #define ALDEVICELIST_H #include "oal_utils.h" #ifdef AUDIO_OAL #pragma warning(disable: 4786) //disable warning "identifier was truncated to '255' characters in the browser information" enum { ADEXT_EXT_CAPTURE = (1 << 0), ADEXT_EXT_EFX = (1 << 1), ADEXT_EXT_OFFSET = (1 << 2), ADEXT_EXT_LINEAR_DISTANCE = (1 << 3), ADEXT_EXT_EXPONENT_DISTANCE = (1 << 4), ADEXT_EAX2 = (1 << 5), ADEXT_EAX3 = (1 << 6), ADEXT_EAX4 = (1 << 7), ADEXT_EAX5 = (1 << 8), ADEXT_EAX_RAM = (1 << 9), }; struct ALDEVICEINFO { const char *strDeviceName; int iMajorVersion; int iMinorVersion; unsigned int uiSourceCount; unsigned short Extensions; bool bSelected; ALDEVICEINFO() : iMajorVersion(0), iMinorVersion(0), uiSourceCount(0), bSelected(false) { strDeviceName = NULL; Extensions = 0; } }; typedef ALDEVICEINFO *LPALDEVICEINFO; class ALDeviceList { private: ALDEVICEINFO aDeviceInfo[64]; unsigned int nNumOfDevices; int defaultDeviceIndex; int filterIndex; public: ALDeviceList (); ~ALDeviceList (); unsigned int GetNumDevices(); const char *GetDeviceName(unsigned int index); void GetDeviceVersion(unsigned int index, int *major, int *minor); unsigned int GetMaxNumSources(unsigned int index); bool IsExtensionSupported(int index, unsigned short ext); int GetDefaultDevice(); void FilterDevicesMinVer(int major, int minor); void FilterDevicesMaxVer(int major, int minor); void FilterDevicesExtension(unsigned short ext); void ResetFilters(); int GetFirstFilteredDevice(); int GetNextFilteredDevice(); private: unsigned int GetMaxNumSources(); }; #endif #endif // ALDEVICELIST_H ================================================ FILE: src/audio/oal/channel.cpp ================================================ #include "common.h" #ifdef AUDIO_OAL #include "channel.h" #include "sampman.h" #ifndef _WIN32 #include #endif extern bool IsFXSupported(); ALuint alSources[MAXCHANNELS+MAX2DCHANNELS]; ALuint alFilters[MAXCHANNELS+MAX2DCHANNELS]; ALuint alBuffers[MAXCHANNELS+MAX2DCHANNELS]; bool bChannelsCreated = false; int32 CChannel::channelsThatNeedService = 0; void CChannel::InitChannels() { alGenSources(MAXCHANNELS+MAX2DCHANNELS, alSources); alGenBuffers(MAXCHANNELS+MAX2DCHANNELS, alBuffers); if (IsFXSupported()) alGenFilters(MAXCHANNELS + MAX2DCHANNELS, alFilters); bChannelsCreated = true; } void CChannel::DestroyChannels() { if (bChannelsCreated) { alDeleteSources(MAXCHANNELS + MAX2DCHANNELS, alSources); memset(alSources, 0, sizeof(alSources)); alDeleteBuffers(MAXCHANNELS + MAX2DCHANNELS, alBuffers); memset(alBuffers, 0, sizeof(alBuffers)); if (IsFXSupported()) { alDeleteFilters(MAXCHANNELS + MAX2DCHANNELS, alFilters); memset(alFilters, 0, sizeof(alFilters)); } bChannelsCreated = false; } } CChannel::CChannel() { Data = nil; DataSize = 0; SetDefault(); } void CChannel::SetDefault() { Pitch = 1.0f; Gain = 1.0f; Mix = 0.0f; Position[0] = 0.0f; Position[1] = 0.0f; Position[2] = 0.0f; Distances[0] = 0.0f; Distances[1] = FLT_MAX; LoopCount = 1; LastProcessedOffset = UINT32_MAX; LoopPoints[0] = 0; LoopPoints[1] = -1; Frequency = MAX_FREQ; } void CChannel::Reset() { // Here is safe because ctor don't call this if (LoopCount > 1) channelsThatNeedService--; ClearBuffer(); SetDefault(); } void CChannel::Init(uint32 _id, bool Is2D) { id = _id; if ( HasSource() ) { alSourcei(alSources[id], AL_SOURCE_RELATIVE, AL_TRUE); if ( IsFXSupported() ) alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); if ( Is2D ) { alSource3f(alSources[id], AL_POSITION, 0.0f, 0.0f, 0.0f); alSourcef(alSources[id], AL_GAIN, 1.0f); } } } void CChannel::Term() { Stop(); if ( HasSource() ) { if ( IsFXSupported() ) { alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL); } } } void CChannel::Start() { if ( !HasSource() ) return; if ( !Data ) return; alBufferData(alBuffers[id], AL_FORMAT_MONO16, Data, DataSize, Frequency); if ( LoopPoints[0] != 0 && LoopPoints[0] != -1 ) alBufferiv(alBuffers[id], AL_LOOP_POINTS_SOFT, LoopPoints); alSourcei(alSources[id], AL_BUFFER, alBuffers[id]); alSourcePlay(alSources[id]); } void CChannel::Stop() { if ( HasSource() ) alSourceStop(alSources[id]); Reset(); } bool CChannel::HasSource() { return alSources[id] != AL_NONE; } bool CChannel::IsUsed() { if ( HasSource() ) { ALint sourceState; alGetSourcei(alSources[id], AL_SOURCE_STATE, &sourceState); return sourceState == AL_PLAYING; } return false; } void CChannel::SetPitch(float pitch) { if ( !HasSource() ) return; alSourcef(alSources[id], AL_PITCH, pitch); } void CChannel::SetGain(float gain) { if ( !HasSource() ) return; alSourcef(alSources[id], AL_GAIN, gain); } void CChannel::SetVolume(int32 vol) { SetGain(ALfloat(vol) / MAX_VOLUME); } void CChannel::SetSampleData(void *_data, size_t _DataSize, int32 freq) { Data = _data; DataSize = _DataSize; Frequency = freq; } void CChannel::SetCurrentFreq(uint32 freq) { SetPitch(ALfloat(freq) / Frequency); } void CChannel::SetLoopCount(int32 count) { if ( !HasSource() ) return; // 0: loop indefinitely, 1: play one time, 2: play two times etc... // only > 1 needs manual processing if (LoopCount > 1 && count < 2) channelsThatNeedService--; else if (LoopCount < 2 && count > 1) channelsThatNeedService++; alSourcei(alSources[id], AL_LOOPING, count == 1 ? AL_FALSE : AL_TRUE); LoopCount = count; } bool CChannel::Update() { if (!HasSource()) return false; if (LoopCount < 2) return false; ALint state; alGetSourcei(alSources[id], AL_SOURCE_STATE, &state); if (state == AL_STOPPED) { debug("Looping channels(%d in this case) shouldn't report AL_STOPPED, but nvm\n", id); SetLoopCount(1); return true; } assert(channelsThatNeedService > 0 && "Ref counting is broken"); ALint offset; alGetSourcei(alSources[id], AL_SAMPLE_OFFSET, &offset); // Rewound if (offset < LastProcessedOffset) { LoopCount--; if (LoopCount == 1) { // Playing last tune... channelsThatNeedService--; alSourcei(alSources[id], AL_LOOPING, AL_FALSE); } } LastProcessedOffset = offset; return true; } void CChannel::SetLoopPoints(ALint start, ALint end) { LoopPoints[0] = start; LoopPoints[1] = end; } void CChannel::SetPosition(float x, float y, float z) { if ( !HasSource() ) return; alSource3f(alSources[id], AL_POSITION, x, y, z); } void CChannel::SetDistances(float max, float min) { if ( !HasSource() ) return; alSourcef (alSources[id], AL_MAX_DISTANCE, max); alSourcef (alSources[id], AL_REFERENCE_DISTANCE, min); alSourcef (alSources[id], AL_MAX_GAIN, 1.0f); alSourcef (alSources[id], AL_ROLLOFF_FACTOR, 1.0f); } void CChannel::SetPan(int32 pan) { SetPosition((pan-63)/64.0f, 0.0f, Sqrt(1.0f-SQR((pan-63)/64.0f))); } void CChannel::ClearBuffer() { if ( !HasSource() ) return; alSourcei(alSources[id], AL_LOOPING, AL_FALSE); alSourcei(alSources[id], AL_BUFFER, AL_NONE); Data = nil; DataSize = 0; } void CChannel::SetReverbMix(ALuint slot, float mix) { if ( !IsFXSupported() ) return; if ( !HasSource() ) return; if ( alFilters[id] == AL_FILTER_NULL ) return; Mix = mix; EAX3_SetReverbMix(alFilters[id], mix); alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, slot, 0, alFilters[id]); } void CChannel::UpdateReverb(ALuint slot) { if ( !IsFXSupported() ) return; if ( !HasSource() ) return; if ( alFilters[id] == AL_FILTER_NULL ) return; EAX3_SetReverbMix(alFilters[id], Mix); alSource3i(alSources[id], AL_AUXILIARY_SEND_FILTER, slot, 0, alFilters[id]); } #endif ================================================ FILE: src/audio/oal/channel.h ================================================ #pragma once #ifdef AUDIO_OAL #include "oal/oal_utils.h" #include #include #include class CChannel { uint32 id; float Pitch, Gain; float Mix; void *Data; size_t DataSize; int32 Frequency; float Position[3]; float Distances[2]; int32 LoopCount; ALint LoopPoints[2]; ALint LastProcessedOffset; public: static int32 channelsThatNeedService; static void InitChannels(); static void DestroyChannels(); CChannel(); void SetDefault(); void Reset(); void Init(uint32 _id, bool Is2D = false); void Term(); void Start(); void Stop(); bool HasSource(); bool IsUsed(); void SetPitch(float pitch); void SetGain(float gain); void SetVolume(int32 vol); void SetSampleData(void *_data, size_t _DataSize, int32 freq); void SetCurrentFreq(uint32 freq); void SetLoopCount(int32 count); void SetLoopPoints(ALint start, ALint end); void SetPosition(float x, float y, float z); void SetDistances(float max, float min); void SetPan(int32 pan); void ClearBuffer(); void SetReverbMix(ALuint slot, float mix); void UpdateReverb(ALuint slot); bool Update(); }; #endif ================================================ FILE: src/audio/oal/oal_utils.cpp ================================================ #include "common.h" #include "oal_utils.h" #ifdef AUDIO_OAL /* * When linking to a static openal-soft library, * the extension function inside the openal library conflict with the variables here. * Therefore declare these re3 owned symbols in a private namespace. */ namespace re3_openal { LPALGENEFFECTS alGenEffects; LPALDELETEEFFECTS alDeleteEffects; LPALISEFFECT alIsEffect; LPALEFFECTI alEffecti; LPALEFFECTIV alEffectiv; LPALEFFECTF alEffectf; LPALEFFECTFV alEffectfv; LPALGETEFFECTI alGetEffecti; LPALGETEFFECTIV alGetEffectiv; LPALGETEFFECTF alGetEffectf; LPALGETEFFECTFV alGetEffectfv; LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; LPALGENFILTERS alGenFilters; LPALDELETEFILTERS alDeleteFilters; LPALISFILTER alIsFilter; LPALFILTERI alFilteri; LPALFILTERIV alFilteriv; LPALFILTERF alFilterf; LPALFILTERFV alFilterfv; LPALGETFILTERI alGetFilteri; LPALGETFILTERIV alGetFilteriv; LPALGETFILTERF alGetFilterf; LPALGETFILTERFV alGetFilterfv; } using namespace re3_openal; void EFXInit() { /* Define a macro to help load the function pointers. */ #define LOAD_PROC(T, x) ((x) = (T)alGetProcAddress(#x)) LOAD_PROC(LPALGENEFFECTS, alGenEffects); LOAD_PROC(LPALDELETEEFFECTS, alDeleteEffects); LOAD_PROC(LPALISEFFECT, alIsEffect); LOAD_PROC(LPALEFFECTI, alEffecti); LOAD_PROC(LPALEFFECTIV, alEffectiv); LOAD_PROC(LPALEFFECTF, alEffectf); LOAD_PROC(LPALEFFECTFV, alEffectfv); LOAD_PROC(LPALGETEFFECTI, alGetEffecti); LOAD_PROC(LPALGETEFFECTIV, alGetEffectiv); LOAD_PROC(LPALGETEFFECTF, alGetEffectf); LOAD_PROC(LPALGETEFFECTFV, alGetEffectfv); LOAD_PROC(LPALGENFILTERS, alGenFilters); LOAD_PROC(LPALDELETEFILTERS, alDeleteFilters); LOAD_PROC(LPALISFILTER, alIsFilter); LOAD_PROC(LPALFILTERI, alFilteri); LOAD_PROC(LPALFILTERIV, alFilteriv); LOAD_PROC(LPALFILTERF, alFilterf); LOAD_PROC(LPALFILTERFV, alFilterfv); LOAD_PROC(LPALGETFILTERI, alGetFilteri); LOAD_PROC(LPALGETFILTERIV, alGetFilteriv); LOAD_PROC(LPALGETFILTERF, alGetFilterf); LOAD_PROC(LPALGETFILTERFV, alGetFilterfv); LOAD_PROC(LPALGENAUXILIARYEFFECTSLOTS, alGenAuxiliaryEffectSlots); LOAD_PROC(LPALDELETEAUXILIARYEFFECTSLOTS, alDeleteAuxiliaryEffectSlots); LOAD_PROC(LPALISAUXILIARYEFFECTSLOT, alIsAuxiliaryEffectSlot); LOAD_PROC(LPALAUXILIARYEFFECTSLOTI, alAuxiliaryEffectSloti); LOAD_PROC(LPALAUXILIARYEFFECTSLOTIV, alAuxiliaryEffectSlotiv); LOAD_PROC(LPALAUXILIARYEFFECTSLOTF, alAuxiliaryEffectSlotf); LOAD_PROC(LPALAUXILIARYEFFECTSLOTFV, alAuxiliaryEffectSlotfv); LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTI, alGetAuxiliaryEffectSloti); LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTIV, alGetAuxiliaryEffectSlotiv); LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTF, alGetAuxiliaryEffectSlotf); LOAD_PROC(LPALGETAUXILIARYEFFECTSLOTFV, alGetAuxiliaryEffectSlotfv); #undef LOAD_PROC } void SetEffectsLevel(ALuint uiFilter, float level) { alFilteri(uiFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); alFilterf(uiFilter, AL_LOWPASS_GAIN, 1.0f); alFilterf(uiFilter, AL_LOWPASS_GAINHF, level); } static inline float gain_to_mB(float gain) { return (gain > 1e-5f) ? (float)(log10f(gain) * 2000.0f) : -10000l; } static inline float mB_to_gain(float millibels) { return (millibels > -10000.0f) ? powf(10.0f, millibels/2000.0f) : 0.0f; } static inline float clampF(float val, float minval, float maxval) { if(val >= maxval) return maxval; if(val <= minval) return minval; return val; } void EAX3_Set(ALuint effect, const EAXLISTENERPROPERTIES *props) { alEffecti (effect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); alEffectf (effect, AL_EAXREVERB_DENSITY, clampF(powf(props->flEnvironmentSize, 3.0f) / 16.0f, 0.0f, 1.0f)); alEffectf (effect, AL_EAXREVERB_DIFFUSION, props->flEnvironmentDiffusion); alEffectf (effect, AL_EAXREVERB_GAIN, mB_to_gain((float)props->lRoom)); alEffectf (effect, AL_EAXREVERB_GAINHF, mB_to_gain((float)props->lRoomHF)); alEffectf (effect, AL_EAXREVERB_GAINLF, mB_to_gain((float)props->lRoomLF)); alEffectf (effect, AL_EAXREVERB_DECAY_TIME, props->flDecayTime); alEffectf (effect, AL_EAXREVERB_DECAY_HFRATIO, props->flDecayHFRatio); alEffectf (effect, AL_EAXREVERB_DECAY_LFRATIO, props->flDecayLFRatio); alEffectf (effect, AL_EAXREVERB_REFLECTIONS_GAIN, clampF(mB_to_gain((float)props->lReflections), AL_EAXREVERB_MIN_REFLECTIONS_GAIN, AL_EAXREVERB_MAX_REFLECTIONS_GAIN)); alEffectf (effect, AL_EAXREVERB_REFLECTIONS_DELAY, props->flReflectionsDelay); alEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN, &props->vReflectionsPan.x); alEffectf (effect, AL_EAXREVERB_LATE_REVERB_GAIN, clampF(mB_to_gain((float)props->lReverb), AL_EAXREVERB_MIN_LATE_REVERB_GAIN, AL_EAXREVERB_MAX_LATE_REVERB_GAIN)); alEffectf (effect, AL_EAXREVERB_LATE_REVERB_DELAY, props->flReverbDelay); alEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN, &props->vReverbPan.x); alEffectf (effect, AL_EAXREVERB_ECHO_TIME, props->flEchoTime); alEffectf (effect, AL_EAXREVERB_ECHO_DEPTH, props->flEchoDepth); alEffectf (effect, AL_EAXREVERB_MODULATION_TIME, props->flModulationTime); alEffectf (effect, AL_EAXREVERB_MODULATION_DEPTH, props->flModulationDepth); alEffectf (effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, clampF(mB_to_gain(props->flAirAbsorptionHF), AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF, AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)); alEffectf (effect, AL_EAXREVERB_HFREFERENCE, props->flHFReference); alEffectf (effect, AL_EAXREVERB_LFREFERENCE, props->flLFReference); alEffectf (effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, props->flRoomRolloffFactor); alEffecti (effect, AL_EAXREVERB_DECAY_HFLIMIT, (props->ulFlags&EAXLISTENERFLAGS_DECAYHFLIMIT) ? AL_TRUE : AL_FALSE); } void EFX_Set(ALuint effect, const EAXLISTENERPROPERTIES *props) { alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB); alEffectf(effect, AL_REVERB_DENSITY, clampF(powf(props->flEnvironmentSize, 3.0f) / 16.0f, 0.0f, 1.0f)); alEffectf(effect, AL_REVERB_DIFFUSION, props->flEnvironmentDiffusion); alEffectf(effect, AL_REVERB_GAIN, mB_to_gain((float)props->lRoom)); alEffectf(effect, AL_REVERB_GAINHF, mB_to_gain((float)props->lRoomHF)); alEffectf(effect, AL_REVERB_DECAY_TIME, props->flDecayTime); alEffectf(effect, AL_REVERB_DECAY_HFRATIO, props->flDecayHFRatio); alEffectf(effect, AL_REVERB_REFLECTIONS_GAIN, clampF(mB_to_gain((float)props->lReflections), AL_EAXREVERB_MIN_REFLECTIONS_GAIN, AL_EAXREVERB_MAX_REFLECTIONS_GAIN)); alEffectf(effect, AL_REVERB_REFLECTIONS_DELAY, props->flReflectionsDelay); alEffectf(effect, AL_REVERB_LATE_REVERB_GAIN, clampF(mB_to_gain((float)props->lReverb), AL_EAXREVERB_MIN_LATE_REVERB_GAIN, AL_EAXREVERB_MAX_LATE_REVERB_GAIN)); alEffectf(effect, AL_REVERB_LATE_REVERB_DELAY, props->flReverbDelay); alEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, clampF(mB_to_gain(props->flAirAbsorptionHF), AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF, AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)); alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, props->flRoomRolloffFactor); alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, (props->ulFlags&EAXLISTENERFLAGS_DECAYHFLIMIT) ? AL_TRUE : AL_FALSE); } void EAX3_SetReverbMix(ALuint filter, float mix) { //long vol=(long)linear_to_dB(mix); //DSPROPERTY_EAXBUFFER_ROOMHF, //DSPROPERTY_EAXBUFFER_ROOM, //DSPROPERTY_EAXBUFFER_REVERBMIX, long mbvol = gain_to_mB(mix); float mb = mbvol; float mbhf = mbvol; alFilteri(filter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); alFilterf(filter, AL_LOWPASS_GAIN, mB_to_gain(Min(mb, 0.0f))); alFilterf(filter, AL_LOWPASS_GAINHF, mB_to_gain(mbhf)); } #endif ================================================ FILE: src/audio/oal/oal_utils.h ================================================ #pragma once #ifdef AUDIO_OAL #include "eax.h" #include "AL/efx.h" void EFXInit(); void EAX3_Set(ALuint effect, const EAXLISTENERPROPERTIES *props); void EFX_Set(ALuint effect, const EAXLISTENERPROPERTIES *props); void EAX3_SetReverbMix(ALuint filter, float mix); void SetEffectsLevel(ALuint uiFilter, float level); namespace re3_openal { extern LPALGENEFFECTS alGenEffects; extern LPALDELETEEFFECTS alDeleteEffects; extern LPALISEFFECT alIsEffect; extern LPALEFFECTI alEffecti; extern LPALEFFECTIV alEffectiv; extern LPALEFFECTF alEffectf; extern LPALEFFECTFV alEffectfv; extern LPALGETEFFECTI alGetEffecti; extern LPALGETEFFECTIV alGetEffectiv; extern LPALGETEFFECTF alGetEffectf; extern LPALGETEFFECTFV alGetEffectfv; extern LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; extern LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; extern LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; extern LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; extern LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; extern LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; extern LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; extern LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; extern LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; extern LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; extern LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; extern LPALGENFILTERS alGenFilters; extern LPALDELETEFILTERS alDeleteFilters; extern LPALISFILTER alIsFilter; extern LPALFILTERI alFilteri; extern LPALFILTERIV alFilteriv; extern LPALFILTERF alFilterf; extern LPALFILTERFV alFilterfv; extern LPALGETFILTERI alGetFilteri; extern LPALGETFILTERIV alGetFilteriv; extern LPALGETFILTERF alGetFilterf; extern LPALGETFILTERFV alGetFilterfv; } using namespace re3_openal; #endif ================================================ FILE: src/audio/oal/stream.cpp ================================================ #include "common.h" #ifdef AUDIO_OAL #include "stream.h" #include "sampman.h" #if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK #ifdef AUDIO_OAL_USE_SNDFILE #pragma comment( lib, "libsndfile-1.lib" ) #endif #ifdef AUDIO_OAL_USE_MPG123 #pragma comment( lib, "libmpg123-0.lib" ) #endif #endif #ifdef AUDIO_OAL_USE_SNDFILE #include #endif #ifdef AUDIO_OAL_USE_MPG123 #include #endif #ifdef AUDIO_OAL_USE_OPUS #include #endif #ifndef _WIN32 #include "crossplatform.h" #endif /* As we ran onto an issue of having different volume levels for mono streams and stereo streams we are now handling all the stereo panning ourselves. Each stream now has two sources - one panned to the left and one to the right, and uses two separate buffers to store data for each individual channel. For that we also have to reshuffle all decoded PCM stereo data from LRLRLRLR to LLLLRRRR (handled by CSortStereoBuffer). */ class CSortStereoBuffer { uint16* PcmBuf; size_t BufSize; public: CSortStereoBuffer() : PcmBuf(nil), BufSize(0) {} ~CSortStereoBuffer() { if (PcmBuf) free(PcmBuf); } uint16* GetBuffer(size_t size) { if (size == 0) return nil; if (!PcmBuf) { BufSize = size; PcmBuf = (uint16*)malloc(BufSize); } else if (BufSize < size) { BufSize = size; PcmBuf = (uint16*)realloc(PcmBuf, size); } return PcmBuf; } void SortStereo(void* buf, size_t size) { uint16* InBuf = (uint16*)buf; uint16* OutBuf = GetBuffer(size); if (!OutBuf) return; size_t rightStart = size / 4; for (size_t i = 0; i < size / 4; i++) { OutBuf[i] = InBuf[i*2]; OutBuf[i+rightStart] = InBuf[i*2+1]; } memcpy(InBuf, OutBuf, size); } }; CSortStereoBuffer SortStereoBuffer; class CImaADPCMDecoder { const uint16 StepTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; int16 Sample, StepIndex; public: CImaADPCMDecoder() { Init(0, 0); } void Init(int16 _Sample, int16 _StepIndex) { Sample = _Sample; StepIndex = _StepIndex; } void Decode(uint8 *inbuf, int16 *_outbuf, size_t size) { int16* outbuf = _outbuf; for (size_t i = 0; i < size; i++) { *(outbuf++) = DecodeSample(inbuf[i] & 0xF); *(outbuf++) = DecodeSample(inbuf[i] >> 4); } } int16 DecodeSample(uint8 adpcm) { uint16 step = StepTable[StepIndex]; if (adpcm & 4) StepIndex += ((adpcm & 3) + 1) * 2; else StepIndex--; StepIndex = clamp(StepIndex, 0, 88); int delta = step >> 3; if (adpcm & 1) delta += step >> 2; if (adpcm & 2) delta += step >> 1; if (adpcm & 4) delta += step; if (adpcm & 8) delta = -delta; int newSample = Sample + delta; Sample = clamp(newSample, -32768, 32767); return Sample; } }; class CWavFile : public IDecoder { enum { WAVEFMT_PCM = 1, WAVEFMT_IMA_ADPCM = 0x11, WAVEFMT_XBOX_ADPCM = 0x69, }; struct tDataHeader { uint32 ID; uint32 Size; }; struct tFormatHeader { uint16 AudioFormat; uint16 NumChannels; uint32 SampleRate; uint32 ByteRate; uint16 BlockAlign; uint16 BitsPerSample; uint16 extra[2]; // adpcm only tFormatHeader() { memset(this, 0, sizeof(*this)); } }; FILE *m_pFile; bool m_bIsOpen; tFormatHeader m_FormatHeader; uint32 m_DataStartOffset; // TODO: 64 bit? uint32 m_nSampleCount; uint32 m_nSamplesPerBlock; // ADPCM things uint8 *m_pAdpcmBuffer; int16 **m_ppPcmBuffers; CImaADPCMDecoder *m_pAdpcmDecoders; void Close() { if (m_pFile) { fclose(m_pFile); m_pFile = nil; } delete[] m_pAdpcmBuffer; delete[] m_ppPcmBuffers; delete[] m_pAdpcmDecoders; } uint32 GetCurrentSample() const { // TODO: 64 bit? uint32 FilePos = ftell(m_pFile); if (FilePos <= m_DataStartOffset) return 0; return (FilePos - m_DataStartOffset) / m_FormatHeader.BlockAlign * m_nSamplesPerBlock; } public: CWavFile(const char* path) : m_bIsOpen(false), m_DataStartOffset(0), m_nSampleCount(0), m_nSamplesPerBlock(0), m_pAdpcmBuffer(nil), m_ppPcmBuffers(nil), m_pAdpcmDecoders(nil) { m_pFile = fopen(path, "rb"); if (!m_pFile) return; #define CLOSE_ON_ERROR(op)\ if (op) { \ Close(); \ return; \ } tDataHeader DataHeader; CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0); CLOSE_ON_ERROR(DataHeader.ID != 'FFIR'); // TODO? validate filesizes int WAVE; CLOSE_ON_ERROR(fread(&WAVE, 4, 1, m_pFile) == 0); CLOSE_ON_ERROR(WAVE != 'EVAW') CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0); CLOSE_ON_ERROR(DataHeader.ID != ' tmf'); CLOSE_ON_ERROR(fread(&m_FormatHeader, Min(DataHeader.Size, sizeof(tFormatHeader)), 1, m_pFile) == 0); CLOSE_ON_ERROR(DataHeader.Size > sizeof(tFormatHeader)); switch (m_FormatHeader.AudioFormat) { case WAVEFMT_XBOX_ADPCM: m_FormatHeader.AudioFormat = WAVEFMT_IMA_ADPCM; case WAVEFMT_IMA_ADPCM: m_nSamplesPerBlock = (m_FormatHeader.BlockAlign / m_FormatHeader.NumChannels - 4) * 2 + 1; m_pAdpcmBuffer = new uint8[m_FormatHeader.BlockAlign]; m_ppPcmBuffers = new int16*[m_FormatHeader.NumChannels]; m_pAdpcmDecoders = new CImaADPCMDecoder[m_FormatHeader.NumChannels]; break; case WAVEFMT_PCM: m_nSamplesPerBlock = 1; if (m_FormatHeader.BitsPerSample != 16) { debug("Unsupported PCM (%d bits), only signed 16-bit is supported (%s)\n", m_FormatHeader.BitsPerSample, path); Close(); return; } break; default: debug("Unsupported wav format 0x%x (%s)\n", m_FormatHeader.AudioFormat, path); Close(); return; } while (true) { CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0); if (DataHeader.ID == 'atad') break; fseek(m_pFile, DataHeader.Size, SEEK_CUR); // TODO? validate data size // maybe check if there no extreme custom headers that might break this } m_DataStartOffset = ftell(m_pFile); m_nSampleCount = DataHeader.Size / m_FormatHeader.BlockAlign * m_nSamplesPerBlock; m_bIsOpen = true; #undef CLOSE_ON_ERROR } ~CWavFile() { Close(); } bool IsOpened() { return m_bIsOpen; } uint32 GetSampleSize() { return sizeof(uint16); } uint32 GetSampleCount() { return m_nSampleCount; } uint32 GetSampleRate() { return m_FormatHeader.SampleRate; } uint32 GetChannels() { return m_FormatHeader.NumChannels; } void Seek(uint32 milliseconds) { if (!IsOpened()) return; fseek(m_pFile, m_DataStartOffset + ms2samples(milliseconds) / m_nSamplesPerBlock * m_FormatHeader.BlockAlign, SEEK_SET); } uint32 Tell() { if (!IsOpened()) return 0; return samples2ms(GetCurrentSample()); } #define SAMPLES_IN_LINE (8) uint32 Decode(void* buffer) { if (!IsOpened()) return 0; if (m_FormatHeader.AudioFormat == WAVEFMT_PCM) { // just read the file and sort the samples uint32 size = fread(buffer, 1, GetBufferSize(), m_pFile); if (m_FormatHeader.NumChannels == 2) SortStereoBuffer.SortStereo(buffer, size); return size; } else if (m_FormatHeader.AudioFormat == WAVEFMT_IMA_ADPCM) { // trim the buffer size if we're at the end of our file uint32 nMaxSamples = GetBufferSamples() / m_FormatHeader.NumChannels; uint32 nSamplesLeft = m_nSampleCount - GetCurrentSample(); nMaxSamples = Min(nMaxSamples, nSamplesLeft); // align sample count to our block nMaxSamples = nMaxSamples / m_nSamplesPerBlock * m_nSamplesPerBlock; // count the size of output buffer uint32 OutBufSizePerChannel = nMaxSamples * GetSampleSize(); uint32 OutBufSize = OutBufSizePerChannel * m_FormatHeader.NumChannels; // calculate the pointers to individual channel buffers for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++) m_ppPcmBuffers[i] = (int16*)((int8*)buffer + OutBufSizePerChannel * i); uint32 samplesRead = 0; while (samplesRead < nMaxSamples) { // read the file uint8 *pAdpcmBuf = m_pAdpcmBuffer; if (fread(m_pAdpcmBuffer, 1, m_FormatHeader.BlockAlign, m_pFile) == 0) return 0; // get the first sample in adpcm block and initialise the decoder(s) for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++) { int16 Sample = *(int16*)pAdpcmBuf; pAdpcmBuf += sizeof(int16); int16 Step = *(int16*)pAdpcmBuf; pAdpcmBuf += sizeof(int16); m_pAdpcmDecoders[i].Init(Sample, Step); *(m_ppPcmBuffers[i]) = Sample; m_ppPcmBuffers[i]++; } samplesRead++; // decode the rest of the block for (uint32 s = 1; s < m_nSamplesPerBlock; s += SAMPLES_IN_LINE) { for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++) { m_pAdpcmDecoders[i].Decode(pAdpcmBuf, m_ppPcmBuffers[i], SAMPLES_IN_LINE / 2); pAdpcmBuf += SAMPLES_IN_LINE / 2; m_ppPcmBuffers[i] += SAMPLES_IN_LINE; } samplesRead += SAMPLES_IN_LINE; } } return OutBufSize; } return 0; } }; #ifdef AUDIO_OAL_USE_SNDFILE class CSndFile : public IDecoder { SNDFILE *m_pfSound; SF_INFO m_soundInfo; public: CSndFile(const char *path) : m_pfSound(nil) { memset(&m_soundInfo, 0, sizeof(m_soundInfo)); m_pfSound = sf_open(path, SFM_READ, &m_soundInfo); } ~CSndFile() { if ( m_pfSound ) { sf_close(m_pfSound); m_pfSound = nil; } } bool IsOpened() { return m_pfSound != nil; } uint32 GetSampleSize() { return sizeof(uint16); } uint32 GetSampleCount() { return m_soundInfo.frames; } uint32 GetSampleRate() { return m_soundInfo.samplerate; } uint32 GetChannels() { return m_soundInfo.channels; } void Seek(uint32 milliseconds) { if ( !IsOpened() ) return; sf_seek(m_pfSound, ms2samples(milliseconds), SF_SEEK_SET); } uint32 Tell() { if ( !IsOpened() ) return 0; return samples2ms(sf_seek(m_pfSound, 0, SF_SEEK_CUR)); } uint32 Decode(void *buffer) { if ( !IsOpened() ) return 0; size_t size = sf_read_short(m_pfSound, (short*)buffer, GetBufferSamples()) * GetSampleSize(); if (GetChannels()==2) SortStereoBuffer.SortStereo(buffer, size); return size; } }; #endif #ifdef AUDIO_OAL_USE_MPG123 // fuzzy seek eliminates stutter when playing ADF but spams errors a lot (and breaks radio sometimes) //#define MP3_USE_FUZZY_SEEK class CMP3File : public IDecoder { protected: mpg123_handle *m_pMH; bool m_bOpened; uint32 m_nRate; uint32 m_nChannels; CMP3File() : m_pMH(nil), m_bOpened(false), m_nRate(0), m_nChannels(0) {} public: CMP3File(const char *path) : m_pMH(nil), m_bOpened(false), m_nRate(0), m_nChannels(0) { m_pMH = mpg123_new(nil, nil); if ( m_pMH ) { #ifdef MP3_USE_FUZZY_SEEK mpg123_param(m_pMH, MPG123_FLAGS, MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS | MPG123_QUIET, 0.0); #else mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0); #endif long rate = 0; int channels = 0; int encoding = 0; m_bOpened = mpg123_open(m_pMH, path) == MPG123_OK && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK; m_nRate = rate; m_nChannels = channels; if ( IsOpened() ) { mpg123_format_none(m_pMH); mpg123_format(m_pMH, rate, channels, encoding); } } } ~CMP3File() { if ( m_pMH ) { mpg123_close(m_pMH); mpg123_delete(m_pMH); m_pMH = nil; } } bool IsOpened() { return m_bOpened; } uint32 GetSampleSize() { return sizeof(uint16); } uint32 GetSampleCount() { if ( !IsOpened() ) return 0; return mpg123_length(m_pMH); } uint32 GetSampleRate() { return m_nRate; } uint32 GetChannels() { return m_nChannels; } void Seek(uint32 milliseconds) { if ( !IsOpened() ) return; mpg123_seek(m_pMH, ms2samples(milliseconds), SEEK_SET); } uint32 Tell() { if ( !IsOpened() ) return 0; return samples2ms(mpg123_tell(m_pMH)); } uint32 Decode(void *buffer) { if ( !IsOpened() ) return 0; size_t size; int err = mpg123_read(m_pMH, (unsigned char *)buffer, GetBufferSize(), &size); #if defined(__LP64__) || defined(_WIN64) assert("We can't handle audio files more then 2 GB yet :shrug:" && (size < UINT32_MAX)); #endif if (err != MPG123_OK && err != MPG123_DONE) return 0; if (GetChannels() == 2) SortStereoBuffer.SortStereo(buffer, size); return (uint32)size; } }; class CADFFile : public CMP3File { static ssize_t r_read(void* fh, void* buf, size_t size) { size_t bytesRead = fread(buf, 1, size, (FILE*)fh); uint8* _buf = (uint8*)buf; for (size_t i = 0; i < size; i++) _buf[i] ^= 0x22; return bytesRead; } static off_t r_seek(void* fh, off_t pos, int seekType) { fseek((FILE*)fh, pos, seekType); return ftell((FILE*)fh); } static void r_close(void* fh) { fclose((FILE*)fh); } public: CADFFile(const char* path) { m_pMH = mpg123_new(nil, nil); if (m_pMH) { #ifdef MP3_USE_FUZZY_SEEK mpg123_param(m_pMH, MPG123_FLAGS, MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS | MPG123_QUIET, 0.0); #else mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0); #endif long rate = 0; int channels = 0; int encoding = 0; FILE* f = fopen(path, "rb"); m_bOpened = mpg123_replace_reader_handle(m_pMH, r_read, r_seek, r_close) == MPG123_OK && mpg123_open_handle(m_pMH, f) == MPG123_OK && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK; m_nRate = rate; m_nChannels = channels; if (IsOpened()) { mpg123_format_none(m_pMH); mpg123_format(m_pMH, rate, channels, encoding); } } } }; #endif #define VAG_LINE_SIZE (0x10) #define VAG_SAMPLES_IN_LINE (28) class CVagDecoder { const double f[5][2] = { { 0.0, 0.0 }, { 60.0 / 64.0, 0.0 }, { 115.0 / 64.0, -52.0 / 64.0 }, { 98.0 / 64.0, -55.0 / 64.0 }, { 122.0 / 64.0, -60.0 / 64.0 } }; double s_1; double s_2; public: CVagDecoder() { ResetState(); } void ResetState() { s_1 = s_2 = 0.0; } static short quantize(double sample) { int a = int(sample + 0.5); return short(clamp(a, -32768, 32767)); } void Decode(void* _inbuf, int16* _outbuf, size_t size) { uint8* inbuf = (uint8*)_inbuf; int16* outbuf = _outbuf; size &= ~(VAG_LINE_SIZE - 1); while (size > 0) { double samples[VAG_SAMPLES_IN_LINE]; int predict_nr, shift_factor, flags; predict_nr = *(inbuf++); shift_factor = predict_nr & 0xf; predict_nr >>= 4; flags = *(inbuf++); if (flags == 7) // TODO: ignore? break; for (int i = 0; i < VAG_SAMPLES_IN_LINE; i += 2) { int d = *(inbuf++); int16 s = int16((d & 0xf) << 12); samples[i] = (double)(s >> shift_factor); s = int16((d & 0xf0) << 8); samples[i + 1] = (double)(s >> shift_factor); } for (int i = 0; i < VAG_SAMPLES_IN_LINE; i++) { samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1]; s_2 = s_1; s_1 = samples[i]; *(outbuf++) = quantize(samples[i] + 0.5); } size -= VAG_LINE_SIZE; } } }; #define VB_BLOCK_SIZE (0x2000) #define NUM_VAG_LINES_IN_BLOCK (VB_BLOCK_SIZE / VAG_LINE_SIZE) #define NUM_VAG_SAMPLES_IN_BLOCK (NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE) class CVbFile : public IDecoder { FILE *m_pFile; CVagDecoder *m_pVagDecoders; size_t m_FileSize; size_t m_nNumberOfBlocks; uint32 m_nSampleRate; uint8 m_nChannels; bool m_bBlockRead; uint16 m_LineInBlock; size_t m_CurrentBlock; uint8 **m_ppVagBuffers; // buffers that cache actual ADPCM file data int16 **m_ppPcmBuffers; void ReadBlock(int32 block = -1) { // just read next block if -1 if (block != -1) fseek(m_pFile, block * m_nChannels * VB_BLOCK_SIZE, SEEK_SET); for (int i = 0; i < m_nChannels; i++) fread(m_ppVagBuffers[i], VB_BLOCK_SIZE, 1, m_pFile); m_bBlockRead = true; } public: CVbFile(const char* path, uint32 nSampleRate = 32000, uint8 nChannels = 2) : m_nSampleRate(nSampleRate), m_nChannels(nChannels), m_pVagDecoders(nil), m_ppVagBuffers(nil), m_ppPcmBuffers(nil), m_FileSize(0), m_nNumberOfBlocks(0), m_bBlockRead(false), m_LineInBlock(0), m_CurrentBlock(0) { m_pFile = fopen(path, "rb"); if (!m_pFile) return; fseek(m_pFile, 0, SEEK_END); m_FileSize = ftell(m_pFile); fseek(m_pFile, 0, SEEK_SET); m_nNumberOfBlocks = m_FileSize / (nChannels * VB_BLOCK_SIZE); m_pVagDecoders = new CVagDecoder[nChannels]; m_ppVagBuffers = new uint8*[nChannels]; m_ppPcmBuffers = new int16*[nChannels]; for (uint8 i = 0; i < nChannels; i++) m_ppVagBuffers[i] = new uint8[VB_BLOCK_SIZE]; } ~CVbFile() { if (m_pFile) { fclose(m_pFile); delete[] m_pVagDecoders; for (int i = 0; i < m_nChannels; i++) delete[] m_ppVagBuffers[i]; delete[] m_ppVagBuffers; delete[] m_ppPcmBuffers; } } bool IsOpened() { return m_pFile != nil; } uint32 GetSampleSize() { return sizeof(uint16); } uint32 GetSampleCount() { if (!IsOpened()) return 0; return m_nNumberOfBlocks * NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE; } uint32 GetSampleRate() { return m_nSampleRate; } uint32 GetChannels() { return m_nChannels; } void Seek(uint32 milliseconds) { if (!IsOpened()) return; uint32 samples = ms2samples(milliseconds); // find the block of our sample uint32 block = samples / NUM_VAG_SAMPLES_IN_BLOCK; if (block > m_nNumberOfBlocks) { samples = 0; block = 0; } if (block != m_CurrentBlock) m_bBlockRead = false; // find a line of our sample within our block uint32 remainingSamples = samples - block * NUM_VAG_SAMPLES_IN_BLOCK; uint32 newLine = remainingSamples / VAG_SAMPLES_IN_LINE / VAG_LINE_SIZE; if (m_CurrentBlock != block || m_LineInBlock != newLine) { m_CurrentBlock = block; m_LineInBlock = newLine; for (uint32 i = 0; i < GetChannels(); i++) m_pVagDecoders[i].ResetState(); } } uint32 Tell() { if (!IsOpened()) return 0; uint32 pos = (m_CurrentBlock * NUM_VAG_LINES_IN_BLOCK + m_LineInBlock) * VAG_SAMPLES_IN_LINE; return samples2ms(pos); } uint32 Decode(void* buffer) { if (!IsOpened()) return 0; if (m_CurrentBlock >= m_nNumberOfBlocks) return 0; // cache current ADPCM block if (!m_bBlockRead) ReadBlock(m_CurrentBlock); // trim the buffer size if we're at the end of our file int numberOfRequiredLines = GetBufferSamples() / m_nChannels / VAG_SAMPLES_IN_LINE; int numberOfRemainingLines = (m_nNumberOfBlocks - m_CurrentBlock) * NUM_VAG_LINES_IN_BLOCK - m_LineInBlock; int bufSizePerChannel = Min(numberOfRequiredLines, numberOfRemainingLines) * VAG_SAMPLES_IN_LINE * GetSampleSize(); // calculate the pointers to individual channel buffers for (uint32 i = 0; i < m_nChannels; i++) m_ppPcmBuffers[i] = (int16*)((int8*)buffer + bufSizePerChannel * i); int size = 0; while (size < bufSizePerChannel) { // decode the VAG lines for (uint32 i = 0; i < m_nChannels; i++) { m_pVagDecoders[i].Decode(m_ppVagBuffers[i] + m_LineInBlock * VAG_LINE_SIZE, m_ppPcmBuffers[i], VAG_LINE_SIZE); m_ppPcmBuffers[i] += VAG_SAMPLES_IN_LINE; } size += VAG_SAMPLES_IN_LINE * GetSampleSize(); m_LineInBlock++; // block is over, read the next block if (m_LineInBlock >= NUM_VAG_LINES_IN_BLOCK) { m_CurrentBlock++; if (m_CurrentBlock >= m_nNumberOfBlocks) // end of file break; m_LineInBlock = 0; ReadBlock(); } } return bufSizePerChannel * m_nChannels; } }; #ifdef AUDIO_OAL_USE_OPUS class COpusFile : public IDecoder { OggOpusFile *m_FileH; bool m_bOpened; uint32 m_nRate; uint32 m_nChannels; public: COpusFile(const char *path) : m_FileH(nil), m_bOpened(false), m_nRate(0), m_nChannels(0) { int ret; m_FileH = op_open_file(path, &ret); if (m_FileH) { m_nChannels = op_head(m_FileH, 0)->channel_count; m_nRate = 48000; const OpusTags *tags = op_tags(m_FileH, 0); for (int i = 0; i < tags->comments; i++) { if (strncmp(tags->user_comments[i], "SAMPLERATE", sizeof("SAMPLERATE")-1) == 0) { sscanf(tags->user_comments[i], "SAMPLERATE=%i", &m_nRate); break; } } m_bOpened = true; } } ~COpusFile() { if (m_FileH) { op_free(m_FileH); m_FileH = nil; } } bool IsOpened() { return m_bOpened; } uint32 GetSampleSize() { return sizeof(uint16); } uint32 GetSampleCount() { if ( !IsOpened() ) return 0; return op_pcm_total(m_FileH, 0); } uint32 GetSampleRate() { return m_nRate; } uint32 GetChannels() { return m_nChannels; } void Seek(uint32 milliseconds) { if ( !IsOpened() ) return; op_pcm_seek(m_FileH, ms2samples(milliseconds) / GetChannels()); } uint32 Tell() { if ( !IsOpened() ) return 0; return samples2ms(op_pcm_tell(m_FileH) * GetChannels()); } uint32 Decode(void *buffer) { if ( !IsOpened() ) return 0; int size = op_read(m_FileH, (opus_int16 *)buffer, GetBufferSamples(), NULL); if (size < 0) return 0; if (GetChannels() == 2) SortStereoBuffer.SortStereo(buffer, size * m_nChannels * GetSampleSize()); return size * m_nChannels * GetSampleSize(); } }; #endif void CStream::Initialise() { #ifdef AUDIO_OAL_USE_MPG123 mpg123_init(); #endif } void CStream::Terminate() { #ifdef AUDIO_OAL_USE_MPG123 mpg123_exit(); #endif } CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate) : m_pAlSources(sources), m_alBuffers(buffers), m_pBuffer(nil), m_bPaused(false), m_bActive(false), m_pSoundFile(nil), m_bReset(false), m_nVolume(0), m_nPan(0), m_nPosBeforeReset(0), m_nLoopCount(1) { // Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/) #if !defined(_WIN32) char *real = casepath(filename); if (real) { strcpy(m_aFilename, real); free(real); } else { #else { #endif strcpy(m_aFilename, filename); } DEV("Stream %s\n", m_aFilename); if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".wav")], ".wav")) #ifdef AUDIO_OAL_USE_SNDFILE m_pSoundFile = new CSndFile(m_aFilename); #else m_pSoundFile = new CWavFile(m_aFilename); #endif #ifdef AUDIO_OAL_USE_MPG123 else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".mp3")], ".mp3")) m_pSoundFile = new CMP3File(m_aFilename); else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".adf")], ".adf")) m_pSoundFile = new CADFFile(m_aFilename); #endif else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".vb")], ".VB")) m_pSoundFile = new CVbFile(m_aFilename, overrideSampleRate); #ifdef AUDIO_OAL_USE_OPUS else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".opus")], ".opus")) m_pSoundFile = new COpusFile(m_aFilename); #endif else m_pSoundFile = nil; if ( IsOpened() ) { m_pBuffer = malloc(m_pSoundFile->GetBufferSize()); ASSERT(m_pBuffer!=nil); DEV("AvgSamplesPerSec: %d\n", m_pSoundFile->GetAvgSamplesPerSec()); DEV("SampleCount: %d\n", m_pSoundFile->GetSampleCount()); DEV("SampleRate: %d\n", m_pSoundFile->GetSampleRate()); DEV("Channels: %d\n", m_pSoundFile->GetChannels()); DEV("Buffer Samples: %d\n", m_pSoundFile->GetBufferSamples()); DEV("Buffer sec: %f\n", (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate()))); DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60); return; } } CStream::~CStream() { Delete(); } void CStream::Delete() { Stop(); ClearBuffers(); if ( m_pSoundFile ) { delete m_pSoundFile; m_pSoundFile = nil; } if ( m_pBuffer ) { free(m_pBuffer); m_pBuffer = nil; } } bool CStream::HasSource() { return (m_pAlSources[0] != AL_NONE) && (m_pAlSources[1] != AL_NONE); } bool CStream::IsOpened() { return m_pSoundFile && m_pSoundFile->IsOpened(); } bool CStream::IsPlaying() { if ( !HasSource() || !IsOpened() ) return false; if ( !m_bPaused ) { ALint sourceState[2]; alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState[0]); alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]); if (sourceState[0] == AL_PLAYING || sourceState[1] == AL_PLAYING) return true; } return false; } void CStream::Pause() { if ( !HasSource() ) return; ALint sourceState = AL_PAUSED; alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState); if (sourceState != AL_PAUSED) alSourcePause(m_pAlSources[0]); alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState); if (sourceState != AL_PAUSED) alSourcePause(m_pAlSources[1]); } void CStream::SetPause(bool bPause) { if ( !HasSource() ) return; if ( bPause ) { Pause(); m_bPaused = true; } else { if (m_bPaused) SetPlay(true); m_bPaused = false; } } void CStream::SetPitch(float pitch) { if ( !HasSource() ) return; alSourcef(m_pAlSources[0], AL_PITCH, pitch); alSourcef(m_pAlSources[1], AL_PITCH, pitch); } void CStream::SetGain(float gain) { if ( !HasSource() ) return; alSourcef(m_pAlSources[0], AL_GAIN, gain); alSourcef(m_pAlSources[1], AL_GAIN, gain); } void CStream::SetPosition(int i, float x, float y, float z) { if ( !HasSource() ) return; alSource3f(m_pAlSources[i], AL_POSITION, x, y, z); } void CStream::SetVolume(uint32 nVol) { m_nVolume = nVol; SetGain(ALfloat(nVol) / MAX_VOLUME); } void CStream::SetPan(uint8 nPan) { m_nPan = clamp((int8)nPan - 63, 0, 63); SetPosition(0, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f))); m_nPan = clamp((int8)nPan + 64, 64, 127); SetPosition(1, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f))); m_nPan = nPan; } // Should only be called if source is stopped void CStream::SetPosMS(uint32 nPos) { if ( !IsOpened() ) return; m_pSoundFile->Seek(nPos); ClearBuffers(); } uint32 CStream::GetPosMS() { if ( !HasSource() ) return 0; if ( !IsOpened() ) return 0; ALint offset; //alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset); alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset); return m_pSoundFile->Tell() - m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels() + m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels(); } uint32 CStream::GetLengthMS() { if ( !IsOpened() ) return 0; return m_pSoundFile->GetLength(); } bool CStream::FillBuffer(ALuint *alBuffer) { if ( !HasSource() ) return false; if ( !IsOpened() ) return false; if ( !(alBuffer[0] != AL_NONE && alIsBuffer(alBuffer[0])) ) return false; if ( !(alBuffer[1] != AL_NONE && alIsBuffer(alBuffer[1])) ) return false; uint32 size = m_pSoundFile->Decode(m_pBuffer); if( size == 0 ) return false; uint32 channelSize = size / m_pSoundFile->GetChannels(); alBufferData(alBuffer[0], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate()); // TODO: use just one buffer if we play mono if (m_pSoundFile->GetChannels() == 1) alBufferData(alBuffer[1], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate()); else alBufferData(alBuffer[1], AL_FORMAT_MONO16, (uint8*)m_pBuffer + channelSize, channelSize, m_pSoundFile->GetSampleRate()); return true; } int32 CStream::FillBuffers() { int32 i = 0; for ( i = 0; i < NUM_STREAMBUFFERS/2; i++ ) { if ( !FillBuffer(&m_alBuffers[i*2]) ) break; alSourceQueueBuffers(m_pAlSources[0], 1, &m_alBuffers[i*2]); alSourceQueueBuffers(m_pAlSources[1], 1, &m_alBuffers[i*2+1]); } return i; } void CStream::ClearBuffers() { if ( !HasSource() ) return; ALint buffersQueued[2]; alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &buffersQueued[0]); alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &buffersQueued[1]); ALuint value; while (buffersQueued[0]--) alSourceUnqueueBuffers(m_pAlSources[0], 1, &value); while (buffersQueued[1]--) alSourceUnqueueBuffers(m_pAlSources[1], 1, &value); } bool CStream::Setup(bool imSureQueueIsEmpty) { if ( IsOpened() ) { alSourcei(m_pAlSources[0], AL_LOOPING, AL_FALSE); alSourcei(m_pAlSources[1], AL_LOOPING, AL_FALSE); if (!imSureQueueIsEmpty) { SetPlay(false); ClearBuffers(); } m_pSoundFile->Seek(0); //SetPosition(0.0f, 0.0f, 0.0f); SetPitch(1.0f); //SetPan(m_nPan); //SetVolume(100); } return IsOpened(); } void CStream::SetLoopCount(int32 count) { if ( !HasSource() ) return; m_nLoopCount = count; } void CStream::SetPlay(bool state) { if ( !HasSource() ) return; if ( state ) { ALint sourceState = AL_PLAYING; alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState); if (sourceState != AL_PLAYING ) alSourcePlay(m_pAlSources[0]); sourceState = AL_PLAYING; alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState); if (sourceState != AL_PLAYING) alSourcePlay(m_pAlSources[1]); m_bActive = true; } else { ALint sourceState = AL_STOPPED; alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState); if (sourceState != AL_STOPPED ) alSourceStop(m_pAlSources[0]); sourceState = AL_STOPPED; alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState); if (sourceState != AL_STOPPED) alSourceStop(m_pAlSources[1]); m_bActive = false; } } void CStream::Start() { if ( !HasSource() ) return; if ( FillBuffers() != 0 ) SetPlay(true); } void CStream::Stop() { if ( !HasSource() ) return; SetPlay(false); } void CStream::Update() { if ( !IsOpened() ) return; if ( !HasSource() ) return; if ( m_bReset ) return; if ( !m_bPaused ) { ALint totalBuffers[2] = { 0, 0 }; ALint buffersProcessed[2] = { 0, 0 }; // Relying a lot on left buffer states in here do { //alSourcef(m_pAlSources[0], AL_ROLLOFF_FACTOR, 0.0f); alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &totalBuffers[0]); alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed[0]); //alSourcef(m_pAlSources[1], AL_ROLLOFF_FACTOR, 0.0f); alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &totalBuffers[1]); alGetSourcei(m_pAlSources[1], AL_BUFFERS_PROCESSED, &buffersProcessed[1]); } while (buffersProcessed[0] != buffersProcessed[1]); assert(buffersProcessed[0] == buffersProcessed[1]); // Correcting OpenAL concepts here: // AL_BUFFERS_QUEUED = Number of *all* buffers in queue, including processed, processing and pending // AL_BUFFERS_PROCESSED = Index of the buffer being processing right now. Buffers coming after that(have greater index) are pending buffers. // which means: totalBuffers[0] - buffersProcessed[0] = pending buffers bool buffersRefilled = false; // We should wait queue to be cleared to loop track, because position calculation relies on queue. if (m_nLoopCount != 1 && m_bActive && totalBuffers[0] == 0) { Setup(true); buffersRefilled = FillBuffers() != 0; if (m_nLoopCount != 0) m_nLoopCount--; } else { while( buffersProcessed[0]-- ) { ALuint buffer[2]; alSourceUnqueueBuffers(m_pAlSources[0], 1, &buffer[0]); alSourceUnqueueBuffers(m_pAlSources[1], 1, &buffer[1]); if (m_bActive && FillBuffer(buffer)) { buffersRefilled = true; alSourceQueueBuffers(m_pAlSources[0], 1, &buffer[0]); alSourceQueueBuffers(m_pAlSources[1], 1, &buffer[1]); } } } // Two reasons: 1-Source may be starved to audio and stopped itself, 2- We're already waiting it to starve and die for looping track! if (m_bActive && (buffersRefilled || (totalBuffers[1] - buffersProcessed[1] != 0))) SetPlay(true); } } void CStream::ProviderInit() { if ( m_bReset ) { if ( Setup(true) ) { SetPan(m_nPan); SetVolume(m_nVolume); SetLoopCount(m_nLoopCount); SetPosMS(m_nPosBeforeReset); if (m_bActive) FillBuffers(); SetPlay(m_bActive); if ( m_bPaused ) Pause(); } m_bReset = false; } } void CStream::ProviderTerm() { m_bReset = true; m_nPosBeforeReset = GetPosMS(); ClearBuffers(); } #endif ================================================ FILE: src/audio/oal/stream.h ================================================ #pragma once #ifdef AUDIO_OAL #include #define NUM_STREAMBUFFERS 8 class IDecoder { public: virtual ~IDecoder() { } virtual bool IsOpened() = 0; virtual uint32 GetSampleSize() = 0; virtual uint32 GetSampleCount() = 0; virtual uint32 GetSampleRate() = 0; virtual uint32 GetChannels() = 0; uint32 GetAvgSamplesPerSec() { return GetChannels() * GetSampleRate(); } uint32 ms2samples(uint32 ms) { return float(ms) / 1000.0f * float(GetSampleRate()); } uint32 samples2ms(uint32 sm) { return float(sm) * 1000.0f / float(GetSampleRate()); } uint32 GetBufferSamples() { //return (GetAvgSamplesPerSec() >> 2) - (GetSampleCount() % GetChannels()); return (GetAvgSamplesPerSec() / 4); // 250ms } uint32 GetBufferSize() { return GetBufferSamples() * GetSampleSize(); } virtual void Seek(uint32 milliseconds) = 0; virtual uint32 Tell() = 0; uint32 GetLength() { return float(GetSampleCount()) * 1000.0f / float(GetSampleRate()); } virtual uint32 Decode(void *buffer) = 0; }; class CStream { char m_aFilename[128]; ALuint *m_pAlSources; ALuint (&m_alBuffers)[NUM_STREAMBUFFERS]; bool m_bPaused; bool m_bActive; void *m_pBuffer; bool m_bReset; uint32 m_nVolume; uint8 m_nPan; uint32 m_nPosBeforeReset; int32 m_nLoopCount; IDecoder *m_pSoundFile; bool HasSource(); void SetPosition(int i, float x, float y, float z); void SetPitch(float pitch); void SetGain(float gain); void Pause(); void SetPlay(bool state); bool FillBuffer(ALuint *alBuffer); int32 FillBuffers(); void ClearBuffers(); public: static void Initialise(); static void Terminate(); CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate = 32000); ~CStream(); void Delete(); bool IsOpened(); bool IsPlaying(); void SetPause (bool bPause); void SetVolume(uint32 nVol); void SetPan (uint8 nPan); void SetPosMS (uint32 nPos); uint32 GetPosMS(); uint32 GetLengthMS(); bool Setup(bool imSureQueueIsEmpty = false); void Start(); void Stop(); void Update(void); void SetLoopCount(int32); void ProviderInit(); void ProviderTerm(); }; #endif ================================================ FILE: src/audio/sampman.h ================================================ #pragma once #include "AudioSamples.h" #define MAX_VOLUME 127 #define MAX_FREQ DIGITALRATE struct tSample { int32 nOffset; uint32 nSize; int32 nFrequency; int32 nLoopStart; int32 nLoopEnd; }; #ifdef GTA_PS2 #define PS2BANK(e) e #else #define PS2BANK(e) e = SFX_BANK_0 #endif // GTA_PS2 enum { SFX_BANK_0, #ifdef GTA_PS2 SFX_BANK_GENERIC_EXTRA, SFX_BANK_PED_COMMENTS, SFX_BANK_FRONT_END_MENU, #else SFX_BANK_GENERIC_EXTRA = SFX_BANK_0, SFX_BANK_FRONT_END_MENU = SFX_BANK_0, SFX_BANK_PED_COMMENTS, MAX_SFX_BANKS, INVALID_SFX_BANK, #endif CAR_SFX_BANKS_OFFSET, SFX_BANK_PONTIAC = CAR_SFX_BANKS_OFFSET, SFX_BANK_PORSCHE, SFX_BANK_SPIDER, SFX_BANK_MERC, SFX_BANK_TRUCK, SFX_BANK_HOTROD, SFX_BANK_COBRA, SFX_BANK_PONTIAC_SLOW, SFX_BANK_CADILLAC, SFX_BANK_PATHFINDER, SFX_BANK_PACARD, SFX_BANK_GOLF_CART, SFX_BANK_CAR_CHAINSAW, SFX_BANK_RC, SFX_BANK_RC_HELI, SFX_BANK_CAR_UNUSED_4, // bikes SFX_BANK_VTWIN, SFX_BANK_MOPED, SFX_BANK_HONDA250, SFX_BANK_SPORTS_BIKE, SFX_BANK_BIKE_UNUSED_1, SFX_BANK_BIKE_UNUSED_2, SFX_BANK_BIKE_UNUSED_3, SFX_BANK_BIKE_UNUSED_4, SFX_BANK_BIKE_UNUSED_5, SFX_BANK_BIKE_UNUSED_6, // heli SFX_BANK_HELI_APACHE, SFX_BANK_HELI_UNUSED_1, SFX_BANK_HELI_UNUSED_2, SFX_BANK_HELI_UNUSED_3, SFX_BANK_HELI_UNUSED_4, // plane SFX_BANK_PLANE_SEAPLANE, SFX_BANK_PLANE_UNUSED_1, SFX_BANK_PLANE_UNUSED_2, SFX_BANK_PLANE_UNUSED_3, SFX_BANK_PLANE_UNUSED_4, PS2BANK(SFX_BANK_BUILDING_BANK_ALARM), PS2BANK(SFX_BANK_BUILDING_SNORING), PS2BANK(SFX_BANK_BUILDING_BAR_1), PS2BANK(SFX_BANK_BUILDING_BAR_2), PS2BANK(SFX_BANK_BUILDING_BAR_3), PS2BANK(SFX_BANK_BUILDING_BAR_4), PS2BANK(SFX_BANK_BUILDING_MALIBU_1), PS2BANK(SFX_BANK_BUILDING_MALIBU_2), PS2BANK(SFX_BANK_BUILDING_MALIBU_3), PS2BANK(SFX_BANK_BUILDING_STRIP_1), PS2BANK(SFX_BANK_BUILDING_STRIP_2), PS2BANK(SFX_BANK_BUILDING_STRIP_3), PS2BANK(SFX_BANK_BUILDING_CHURCH), PS2BANK(SFX_BANK_BUILDING_FAN_1), PS2BANK(SFX_BANK_BUILDING_FAN_2), PS2BANK(SFX_BANK_BUILDING_INSECT_1), PS2BANK(SFX_BANK_BUILDING_INSECT_2), PS2BANK(SFX_BANK_BUILDING_18), PS2BANK(SFX_BANK_BUILDING_19), PS2BANK(SFX_BANK_BUILDING_20), PS2BANK(SFX_BANK_BUILDING_21), PS2BANK(SFX_BANK_FOOTSTEPS_GRASS), PS2BANK(SFX_BANK_FOOTSTEPS_GRAVEL), PS2BANK(SFX_BANK_FOOTSTEPS_WOOD), PS2BANK(SFX_BANK_FOOTSTEPS_METAL), PS2BANK(SFX_BANK_FOOTSTEPS_WATER), PS2BANK(SFX_BANK_FOOTSTEPS_SAND), #ifdef GTA_PS2 MAX_SFX_BANKS, INVALID_SFX_BANK #endif }; #define MAX_PEDSFX 7 #define PED_BLOCKSIZE 79000 #define MAXPROVIDERS 64 #define MAXCHANNELS 28 #define MAXCHANNELS_SURROUND 24 #define MAX2DCHANNELS 1 #define CHANNEL2D MAXCHANNELS #define MAX_STREAMS 3 #define DIGITALRATE 32000 #define DIGITALBITS 16 #define DIGITALCHANNELS 2 #define MAX_DIGITAL_MIXER_CHANNELS 32 class cSampleManager { uint8 m_nEffectsVolume; uint8 m_nMusicVolume; uint8 m_nMP3BoostVolume; uint8 m_nEffectsFadeVolume; uint8 m_nMusicFadeVolume; uint8 m_nMonoMode; char m_szCDRomRootPath[80]; bool m_bInitialised; uint8 m_nNumberOfProviders; char *m_aAudioProviders[MAXPROVIDERS]; tSample m_aSamples[TOTAL_AUDIO_SAMPLES]; char m_MiscomPath[260]; char m_WavFilesPath[260]; char m_MP3FilesPath[188]; void *m_aChannels[18]; public: cSampleManager(void); ~cSampleManager(void); void SetSpeakerConfig(int32 nConfig); uint32 GetMaximumSupportedChannels(void); uint32 GetNum3DProvidersAvailable(void); void SetNum3DProvidersAvailable(uint32 num); char *Get3DProviderName(uint8 id); void Set3DProviderName(uint8 id, char *name); int8 GetCurrent3DProviderIndex(void); int8 SetCurrent3DProvider(uint8 which); int8 AutoDetect3DProviders(); bool IsMP3RadioChannelAvailable(void); void ReleaseDigitalHandle (void); void ReacquireDigitalHandle(void); bool Initialise(void); void Terminate (void); bool CheckForAnAudioFileOnCD(void); char GetCDAudioDriveLetter (void); void UpdateEffectsVolume(void); void SetEffectsMasterVolume(uint8 nVolume); void SetMusicMasterVolume (uint8 nVolume); void SetMP3BoostVolume (uint8 nVolume); void SetEffectsFadeVolume (uint8 nVolume); void SetMusicFadeVolume (uint8 nVolume); void SetMonoMode (uint8 nMode); bool LoadSampleBank (uint8 nBank); void UnloadSampleBank (uint8 nBank); bool IsSampleBankLoaded(uint8 nBank); bool IsPedCommentLoaded(uint32 nComment); bool LoadPedComment (uint32 nComment); int32 GetBankContainingSound(uint32 offset); int32 _GetPedCommentSlot(uint32 nComment); int32 GetSampleBaseFrequency (uint32 nSample); int32 GetSampleLoopStartOffset(uint32 nSample); int32 GetSampleLoopEndOffset (uint32 nSample); uint32 GetSampleLength (uint32 nSample); bool UpdateReverb(void); void SetChannelReverbFlag (uint32 nChannel, uint8 nReverbFlag); bool InitialiseChannel (uint32 nChannel, uint32 nSfx, uint8 nBank); void SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume); void SetChannel3DPosition (uint32 nChannel, float fX, float fY, float fZ); void SetChannel3DDistances (uint32 nChannel, float fMax, float fMin); void SetChannelVolume (uint32 nChannel, uint32 nVolume); void SetChannelPan (uint32 nChannel, uint32 nPan); void SetChannelFrequency (uint32 nChannel, uint32 nFreq); void SetChannelLoopPoints (uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd); void SetChannelLoopCount (uint32 nChannel, uint32 nLoopCount); bool GetChannelUsedFlag (uint32 nChannel); void StartChannel (uint32 nChannel); void StopChannel (uint32 nChannel); void PreloadStreamedFile (uint32 nFile, uint8 nStream); void PauseStream (uint8 nPauseFlag, uint8 nStream); void StartPreloadedStreamedFile (uint8 nStream); bool StartStreamedFile (uint32 nFile, uint32 nPos, uint8 nStream); void StopStreamedFile (uint8 nStream); int32 GetStreamedFilePosition (uint8 nStream); void SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffectFlag, uint8 nStream); int32 GetStreamedFileLength (uint8 nStream); bool IsStreamPlaying (uint8 nStream); #ifdef AUDIO_OAL void Service(void); #endif bool InitialiseSampleBanks(void); uint8 GetMusicVolume() const { return m_nMusicVolume; } void SetStreamedFileLoopFlag(uint8 nLoopFlag, uint8 nStream); }; extern cSampleManager SampleManager; extern uint32 BankStartOffset[MAX_SFX_BANKS]; #if defined(OPUS_AUDIO_PATHS) static char StreamedNameTable[][25] = { "AUDIO\\HEAD.OPUS", "AUDIO\\CLASS.OPUS", "AUDIO\\KJAH.OPUS", "AUDIO\\RISE.OPUS", "AUDIO\\LIPS.OPUS", "AUDIO\\GAME.OPUS", "AUDIO\\MSX.OPUS", "AUDIO\\FLASH.OPUS", "AUDIO\\CHAT.OPUS", "AUDIO\\HEAD.OPUS", "AUDIO\\POLICE.OPUS", "AUDIO\\CITY.OPUS", "AUDIO\\WATER.OPUS", "AUDIO\\COMOPEN.OPUS", "AUDIO\\SUBOPEN.OPUS", "AUDIO\\JB.OPUS", "AUDIO\\BET.OPUS", "AUDIO\\L1_LG.OPUS", "AUDIO\\L2_DSB.OPUS", "AUDIO\\L3_DM.OPUS", "AUDIO\\L4_PAP.OPUS", "AUDIO\\L5_TFB.OPUS", "AUDIO\\J0_DM2.OPUS", "AUDIO\\J1_LFL.OPUS", "AUDIO\\J2_KCL.OPUS", "AUDIO\\J3_VH.OPUS", "AUDIO\\J4_ETH.OPUS", "AUDIO\\J5_DST.OPUS", "AUDIO\\J6_TBJ.OPUS", "AUDIO\\T1_TOL.OPUS", "AUDIO\\T2_TPU.OPUS", "AUDIO\\T3_MAS.OPUS", "AUDIO\\T4_TAT.OPUS", "AUDIO\\T5_BF.OPUS", "AUDIO\\S0_MAS.OPUS", "AUDIO\\S1_PF.OPUS", "AUDIO\\S2_CTG.OPUS", "AUDIO\\S3_RTC.OPUS", "AUDIO\\S5_LRQ.OPUS", "AUDIO\\S4_BDBA.OPUS", "AUDIO\\S4_BDBB.OPUS", "AUDIO\\S2_CTG2.OPUS", "AUDIO\\S4_BDBD.OPUS", "AUDIO\\S5_LRQB.OPUS", "AUDIO\\S5_LRQC.OPUS", "AUDIO\\A1_SSO.OPUS", "AUDIO\\A2_PP.OPUS", "AUDIO\\A3_SS.OPUS", "AUDIO\\A4_PDR.OPUS", "AUDIO\\A5_K2FT.OPUS", "AUDIO\\K1_KBO.OPUS", "AUDIO\\K2_GIS.OPUS", "AUDIO\\K3_DS.OPUS", "AUDIO\\K4_SHI.OPUS", "AUDIO\\K5_SD.OPUS", "AUDIO\\R0_PDR2.OPUS", "AUDIO\\R1_SW.OPUS", "AUDIO\\R2_AP.OPUS", "AUDIO\\R3_ED.OPUS", "AUDIO\\R4_GF.OPUS", "AUDIO\\R5_PB.OPUS", "AUDIO\\R6_MM.OPUS", "AUDIO\\D1_STOG.OPUS", "AUDIO\\D2_KK.OPUS", "AUDIO\\D3_ADO.OPUS", "AUDIO\\D5_ES.OPUS", "AUDIO\\D7_MLD.OPUS", "AUDIO\\D4_GTA.OPUS", "AUDIO\\D4_GTA2.OPUS", "AUDIO\\D6_STS.OPUS", "AUDIO\\A6_BAIT.OPUS", "AUDIO\\A7_ETG.OPUS", "AUDIO\\A8_PS.OPUS", "AUDIO\\A9_ASD.OPUS", "AUDIO\\K4_SHI2.OPUS", "AUDIO\\C1_TEX.OPUS", "AUDIO\\EL_PH1.OPUS", "AUDIO\\EL_PH2.OPUS", "AUDIO\\EL_PH3.OPUS", "AUDIO\\EL_PH4.OPUS", "AUDIO\\YD_PH1.OPUS", "AUDIO\\YD_PH2.OPUS", "AUDIO\\YD_PH3.OPUS", "AUDIO\\YD_PH4.OPUS", "AUDIO\\HD_PH1.OPUS", "AUDIO\\HD_PH2.OPUS", "AUDIO\\HD_PH3.OPUS", "AUDIO\\HD_PH4.OPUS", "AUDIO\\HD_PH5.OPUS", "AUDIO\\MT_PH1.OPUS", "AUDIO\\MT_PH2.OPUS", "AUDIO\\MT_PH3.OPUS", "AUDIO\\MT_PH4.OPUS", "AUDIO\\MISCOM.OPUS", "AUDIO\\END.OPUS", "AUDIO\\lib_a1.OPUS", "AUDIO\\lib_a2.OPUS", "AUDIO\\lib_a.OPUS", "AUDIO\\lib_b.OPUS", "AUDIO\\lib_c.OPUS", "AUDIO\\lib_d.OPUS", "AUDIO\\l2_a.OPUS", "AUDIO\\j4t_1.OPUS", "AUDIO\\j4t_2.OPUS", "AUDIO\\j4t_3.OPUS", "AUDIO\\j4t_4.OPUS", "AUDIO\\j4_a.OPUS", "AUDIO\\j4_b.OPUS", "AUDIO\\j4_c.OPUS", "AUDIO\\j4_d.OPUS", "AUDIO\\j4_e.OPUS", "AUDIO\\j4_f.OPUS", "AUDIO\\j6_1.OPUS", "AUDIO\\j6_a.OPUS", "AUDIO\\j6_b.OPUS", "AUDIO\\j6_c.OPUS", "AUDIO\\j6_d.OPUS", "AUDIO\\t4_a.OPUS", "AUDIO\\s1_a.OPUS", "AUDIO\\s1_a1.OPUS", "AUDIO\\s1_b.OPUS", "AUDIO\\s1_c.OPUS", "AUDIO\\s1_c1.OPUS", "AUDIO\\s1_d.OPUS", "AUDIO\\s1_e.OPUS", "AUDIO\\s1_f.OPUS", "AUDIO\\s1_g.OPUS", "AUDIO\\s1_h.OPUS", "AUDIO\\s1_i.OPUS", "AUDIO\\s1_j.OPUS", "AUDIO\\s1_k.OPUS", "AUDIO\\s1_l.OPUS", "AUDIO\\s3_a.OPUS", "AUDIO\\s3_b.OPUS", "AUDIO\\el3_a.OPUS", "AUDIO\\mf1_a.OPUS", "AUDIO\\mf2_a.OPUS", "AUDIO\\mf3_a.OPUS", "AUDIO\\mf3_b.OPUS", "AUDIO\\mf3_b1.OPUS", "AUDIO\\mf3_c.OPUS", "AUDIO\\mf4_a.OPUS", "AUDIO\\mf4_b.OPUS", "AUDIO\\mf4_c.OPUS", "AUDIO\\a1_a.OPUS", "AUDIO\\a3_a.OPUS", "AUDIO\\a5_a.OPUS", "AUDIO\\a4_a.OPUS", "AUDIO\\a4_b.OPUS", "AUDIO\\a4_c.OPUS", "AUDIO\\a4_d.OPUS", "AUDIO\\k1_a.OPUS", "AUDIO\\k3_a.OPUS", "AUDIO\\r1_a.OPUS", "AUDIO\\r2_a.OPUS", "AUDIO\\r2_b.OPUS", "AUDIO\\r2_c.OPUS", "AUDIO\\r2_d.OPUS", "AUDIO\\r2_e.OPUS", "AUDIO\\r2_f.OPUS", "AUDIO\\r2_g.OPUS", "AUDIO\\r2_h.OPUS", "AUDIO\\r5_a.OPUS", "AUDIO\\r6_a.OPUS", "AUDIO\\r6_a1.OPUS", "AUDIO\\r6_b.OPUS", "AUDIO\\lo2_a.OPUS", "AUDIO\\lo6_a.OPUS", "AUDIO\\yd2_a.OPUS", "AUDIO\\yd2_b.OPUS", "AUDIO\\yd2_c.OPUS", "AUDIO\\yd2_c1.OPUS", "AUDIO\\yd2_d.OPUS", "AUDIO\\yd2_e.OPUS", "AUDIO\\yd2_f.OPUS", "AUDIO\\yd2_g.OPUS", "AUDIO\\yd2_h.OPUS", "AUDIO\\yd2_ass.OPUS", "AUDIO\\yd2_ok.OPUS", "AUDIO\\h5_a.OPUS", "AUDIO\\h5_b.OPUS", "AUDIO\\h5_c.OPUS", "AUDIO\\ammu_a.OPUS", "AUDIO\\ammu_b.OPUS", "AUDIO\\ammu_c.OPUS", "AUDIO\\door_1.OPUS", "AUDIO\\door_2.OPUS", "AUDIO\\door_3.OPUS", "AUDIO\\door_4.OPUS", "AUDIO\\door_5.OPUS", "AUDIO\\door_6.OPUS", "AUDIO\\t3_a.OPUS", "AUDIO\\t3_b.OPUS", "AUDIO\\t3_c.OPUS", "AUDIO\\k1_b.OPUS", "AUDIO\\cat1.OPUS"}; #elif defined(PS2_AUDIO_PATHS) static char StreamedNameTable[][40] = { "AUDIO\\MUSIC\\WILD.VB", "AUDIO\\MUSIC\\FLASH.VB", "AUDIO\\MUSIC\\KCHAT.VB", // 16 khz "AUDIO\\MUSIC\\FEVER.VB", "AUDIO\\MUSIC\\VROCK.VB", "AUDIO\\MUSIC\\VCPR.VB", // 16 khz "AUDIO\\MUSIC\\ESPANT.VB", "AUDIO\\MUSIC\\EMOTION.VB", "AUDIO\\MUSIC\\WAVE.VB", "AUDIO\\MUSIC\\MISCOM.VB", "AUDIO\\MUSIC\\CITY.VB", "AUDIO\\MUSIC\\WATER.VB", "AUDIO\\MUSIC\\BEACHAMB.VB", "AUDIO\\MUSIC\\HCITY.VB", "AUDIO\\MUSIC\\HWATER.VB", "AUDIO\\MUSIC\\HBEACH.VB", "AUDIO\\MUSIC\\MALLAMB.VB", "AUDIO\\MUSIC\\STRIP.VB", "AUDIO\\MUSIC\\MALIBU.VB", "AUDIO\\MUSIC\\HOTEL.VB", "AUDIO\\MUSIC\\DIRTRING.VB", "AUDIO\\MUSIC\\LAW4RIOT.VB", "AUDIO\\MUSIC\\AMBSIL.VB", "AUDIO\\MUSIC\\POLICE.VB", // 16 khz "AUDIO\\MUSIC\\TAXI.VB", "AUDIO\\MUSIC\\BCLOSED.VB", "AUDIO\\MUSIC\\BOPEN.VB", "AUDIO\\CUTSCENE\\ASS\\ASS_1.VB", "AUDIO\\CUTSCENE\\ASS\\ASS_2.VB", "AUDIO\\CUTSCENE\\BANK\\BANK_1.VB", "AUDIO\\CUTSCENE\\BANK\\BANK_2A.VB", "AUDIO\\CUTSCENE\\BANK\\BANK_2B.VB", "AUDIO\\CUTSCENE\\BANK\\BANK_3A.VB", "AUDIO\\CUTSCENE\\BANK\\BANK_3B.VB", "AUDIO\\CUTSCENE\\BANK\\BANK_4.VB", "AUDIO\\CUTSCENE\\BIKE\\BIKE_1.VB", "AUDIO\\CUTSCENE\\BIKE\\BIKE_2.VB", "AUDIO\\CUTSCENE\\BIKE\\BIKE_3.VB", "AUDIO\\CUTSCENE\\BUD\\BUD_1.VB", "AUDIO\\CUTSCENE\\BUD\\BUD_2.VB", "AUDIO\\CUTSCENE\\BUD\\BUD_3.VB", "AUDIO\\CUTSCENE\\CAP\\CAP_1.VB", "AUDIO\\CUTSCENE\\CAR\\CAR_1.VB", "AUDIO\\CUTSCENE\\CNT\\CNT_1A.VB", "AUDIO\\CUTSCENE\\CNT\\CNT_1B.VB", "AUDIO\\CUTSCENE\\CNT\\CNT_2.VB", "AUDIO\\CUTSCENE\\COK\\COK_1.VB", "AUDIO\\CUTSCENE\\COK\\COK_2A.VB", "AUDIO\\CUTSCENE\\COK\\COK_2B.VB", "AUDIO\\CUTSCENE\\COK\\COK_3.VB", "AUDIO\\CUTSCENE\\COK\\COK_4A.VB", "AUDIO\\CUTSCENE\\COK\\COK_4A2.VB", "AUDIO\\CUTSCENE\\COK\\COK_4B.VB", "AUDIO\\CUTSCENE\\COL\\COL_1.VB", "AUDIO\\CUTSCENE\\COL\\COL_2.VB", "AUDIO\\CUTSCENE\\COL\\COL_3A.VB", "AUDIO\\CUTSCENE\\COL\\COL_4A.VB", "AUDIO\\CUTSCENE\\COL\\COL_5A.VB", "AUDIO\\CUTSCENE\\COL\\COL_5B.VB", "AUDIO\\CUTSCENE\\CUB\\CUB_1.VB", "AUDIO\\CUTSCENE\\CUB\\CUB_2.VB", "AUDIO\\CUTSCENE\\CUB\\CUB_3.VB", "AUDIO\\CUTSCENE\\CUB\\CUB_4.VB", "AUDIO\\CUTSCENE\\DRUG\\DRUG_1.VB", "AUDIO\\CUTSCENE\\FIN\\FIN.VB", "AUDIO\\CUTSCENE\\FIN\\FIN2.VB", "AUDIO\\CUTSCENE\\FINALE\\FINALE.VB", "AUDIO\\CUTSCENE\\HAT\\HAT_1.VB", "AUDIO\\CUTSCENE\\HAT\\HAT_2.VB", "AUDIO\\CUTSCENE\\HAT\\HAT_3.VB", "AUDIO\\CUTSCENE\\ICE\\ICE_1.VB", "AUDIO\\CUTSCENE\\INT\\INT_A.VB", "AUDIO\\CUTSCENE\\INT\\INT_B.VB", "AUDIO\\CUTSCENE\\INT\\INT_D.VB", "AUDIO\\CUTSCENE\\INT\\INT_M.VB", "AUDIO\\CUTSCENE\\LAW\\LAW_1A.VB", "AUDIO\\CUTSCENE\\LAW\\LAW_1B.VB", "AUDIO\\CUTSCENE\\LAW\\LAW_2A.VB", "AUDIO\\CUTSCENE\\LAW\\LAW_2B.VB", "AUDIO\\CUTSCENE\\LAW\\LAW_2C.VB", "AUDIO\\CUTSCENE\\LAW\\LAW_3.VB", "AUDIO\\CUTSCENE\\LAW\\LAW_4.VB", "AUDIO\\CUTSCENE\\PHIL\\PHIL_1.VB", "AUDIO\\CUTSCENE\\PHIL\\PHIL_2.VB", "AUDIO\\CUTSCENE\\PORN\\PORN_1.VB", "AUDIO\\CUTSCENE\\PORN\\PORN_2.VB", "AUDIO\\CUTSCENE\\PORN\\PORN_3.VB", "AUDIO\\CUTSCENE\\PORN\\PORN_4.VB", "AUDIO\\CUTSCENE\\RESC\\RESC_1A.VB", "AUDIO\\CUTSCENE\\ROK\\ROK_1.VB", "AUDIO\\CUTSCENE\\ROK\\ROK_2.VB", "AUDIO\\CUTSCENE\\ROK\\ROK_3A.VB", "AUDIO\\CUTSCENE\\STRIPA\\STRIPA.VB", "AUDIO\\CUTSCENE\\TAX\\TAX_1.VB", "AUDIO\\CUTSCENE\\TEX\\TEX_1.VB", "AUDIO\\CUTSCENE\\TEX\\TEX_2.VB", "AUDIO\\CUTSCENE\\TEX\\TEX_3.VB", "AUDIO\\MUSIC\\GLIGHT.VB", "AUDIO\\MUSIC\\FIST.VB", "AUDIO\\MUSIC\\MISCOM.VB", "AUDIO\\MUSIC\\MISCOM.VB", "AUDIO\\MUSIC\\MISCOM.VB", "AUDIO\\MUSIC\\MISCOM.VB", #else static char StreamedNameTable[][25] = { "AUDIO\\WILD.ADF", "AUDIO\\FLASH.ADF", "AUDIO\\KCHAT.ADF", "AUDIO\\FEVER.ADF", "AUDIO\\VROCK.ADF", "AUDIO\\VCPR.ADF", "AUDIO\\ESPANT.ADF", "AUDIO\\EMOTION.ADF", "AUDIO\\WAVE.ADF", "AUDIO\\MISCOM.MP3", "AUDIO\\CITY.MP3", "AUDIO\\WATER.MP3", "AUDIO\\BEACHAMB.MP3", "AUDIO\\HCITY.MP3", "AUDIO\\HWATER.MP3", "AUDIO\\HBEACH.MP3", "AUDIO\\MALLAMB.MP3", "AUDIO\\STRIP.MP3", "AUDIO\\MALIBU.MP3", "AUDIO\\HOTEL.MP3", "AUDIO\\DIRTRING.MP3", "AUDIO\\LAW4RIOT.MP3", "AUDIO\\AMBSIL.MP3", "AUDIO\\POLICE.MP3", "AUDIO\\TAXI.MP3", "AUDIO\\BCLOSED.MP3", "AUDIO\\BOPEN.MP3", "AUDIO\\ASS_1.MP3", "AUDIO\\ASS_2.MP3", "AUDIO\\BANK_1.MP3", "AUDIO\\BANK_2A.MP3", "AUDIO\\BANK_2B.MP3", "AUDIO\\BANK_3A.MP3", "AUDIO\\BANK_3B.MP3", "AUDIO\\BANK_4.MP3", "AUDIO\\BIKE_1.MP3", "AUDIO\\BIKE_2.MP3", "AUDIO\\BIKE_3.MP3", "AUDIO\\BUD_1.MP3", "AUDIO\\BUD_2.MP3", "AUDIO\\BUD_3.MP3", "AUDIO\\CAP_1.MP3", "AUDIO\\CAR_1.MP3", "AUDIO\\CNT_1A.MP3", "AUDIO\\CNT_1B.MP3", "AUDIO\\CNT_2.MP3", "AUDIO\\COK_1.MP3", "AUDIO\\COK_2A.MP3", "AUDIO\\COK_2B.MP3", "AUDIO\\COK_3.MP3", "AUDIO\\COK_4A.MP3", "AUDIO\\COK_4A2.MP3", "AUDIO\\COK_4B.MP3", "AUDIO\\COL_1.MP3", "AUDIO\\COL_2.MP3", "AUDIO\\COL_3A.MP3", "AUDIO\\COL_4A.MP3", "AUDIO\\COL_5A.MP3", "AUDIO\\COL_5B.MP3", "AUDIO\\CUB_1.MP3", "AUDIO\\CUB_2.MP3", "AUDIO\\CUB_3.MP3", "AUDIO\\CUB_4.MP3", "AUDIO\\DRUG_1.MP3", "AUDIO\\FIN.MP3", "AUDIO\\FIN2.MP3", "AUDIO\\FINALE.MP3", "AUDIO\\HAT_1.MP3", "AUDIO\\HAT_2.MP3", "AUDIO\\HAT_3.MP3", "AUDIO\\ICE_1.MP3", "AUDIO\\INT_A.MP3", "AUDIO\\INT_B.MP3", "AUDIO\\INT_D.MP3", "AUDIO\\INT_M.MP3", "AUDIO\\LAW_1A.MP3", "AUDIO\\LAW_1B.MP3", "AUDIO\\LAW_2A.MP3", "AUDIO\\LAW_2B.MP3", "AUDIO\\LAW_2C.MP3", "AUDIO\\LAW_3.MP3", "AUDIO\\LAW_4.MP3", "AUDIO\\PHIL_1.MP3", "AUDIO\\PHIL_2.MP3", "AUDIO\\PORN_1.MP3", "AUDIO\\PORN_2.MP3", "AUDIO\\PORN_3.MP3", "AUDIO\\PORN_4.MP3", "AUDIO\\RESC_1A.MP3", "AUDIO\\ROK_1.MP3", "AUDIO\\ROK_2.MP3", "AUDIO\\ROK_3A.MP3", "AUDIO\\STRIPA.MP3", "AUDIO\\TAX_1.MP3", "AUDIO\\TEX_1.MP3", "AUDIO\\TEX_2.MP3", "AUDIO\\TEX_3.MP3", "AUDIO\\GLIGHT.MP3", "AUDIO\\FIST.MP3", "AUDIO\\MISCOM.MP3", "AUDIO\\MISCOM.MP3", "AUDIO\\MISCOM.MP3", "AUDIO\\MISCOM.MP3", #endif "AUDIO\\MOBR1.WAV", "AUDIO\\PAGER.WAV", "AUDIO\\CARREV.WAV", "AUDIO\\BIKEREV.WAV", "AUDIO\\LIFTOP.WAV", "AUDIO\\LIFTCL.WAV", "AUDIO\\LIFTRUN.WAV", "AUDIO\\LIFTBEL.WAV", "AUDIO\\INLIFT.WAV", "AUDIO\\SFX_01.WAV", "AUDIO\\SFX_02.WAV", "AUDIO\\CAMERAL.WAV", "AUDIO\\CAMERAR.WAV", "AUDIO\\CHEER1.WAV", "AUDIO\\CHEER2.WAV", "AUDIO\\CHEER3.WAV", "AUDIO\\CHEER4.WAV", "AUDIO\\OOH1.WAV", "AUDIO\\OOH2.WAV", "AUDIO\\RACE1.WAV", "AUDIO\\RACE2.WAV", "AUDIO\\RACE3.WAV", "AUDIO\\RACE4.WAV", "AUDIO\\RACE5.WAV", "AUDIO\\RACE6.WAV", "AUDIO\\RACE7.WAV", "AUDIO\\RACE8.WAV", "AUDIO\\RACE9.WAV", "AUDIO\\RACE10.WAV", "AUDIO\\RACE11.WAV", "AUDIO\\RACE12.WAV", "AUDIO\\RACE13.WAV", "AUDIO\\RACE14.WAV", "AUDIO\\RACE15.WAV", "AUDIO\\HOT1.WAV", "AUDIO\\HOT2.WAV", "AUDIO\\HOT3.WAV", "AUDIO\\HOT4.WAV", "AUDIO\\HOT5.WAV", "AUDIO\\HOT6.WAV", "AUDIO\\HOT7.WAV", "AUDIO\\HOT8.WAV", "AUDIO\\HOT9.WAV", "AUDIO\\HOT10.WAV", "AUDIO\\HOT11.WAV", "AUDIO\\HOT12.WAV", "AUDIO\\HOT13.WAV", "AUDIO\\HOT14.WAV", "AUDIO\\HOT15.WAV", "AUDIO\\LANSTP1.WAV", "AUDIO\\LANSTP2.WAV", "AUDIO\\LANAMU1.WAV", "AUDIO\\LANAMU2.WAV", "AUDIO\\AIRHORNL.WAV", "AUDIO\\AIRHORNR.WAV", "AUDIO\\SNIPSCRL.WAV", "AUDIO\\SNIPSHORT.WAV", "AUDIO\\BLOWROOF.WAV", "AUDIO\\ASS_1.WAV", "AUDIO\\ASS_2.WAV", "AUDIO\\ASS_3.WAV", "AUDIO\\ASS_4.WAV", "AUDIO\\ASS_5.WAV", "AUDIO\\ASS_6.WAV", "AUDIO\\ASS_7.WAV", "AUDIO\\ASS_8.WAV", "AUDIO\\ASS_9.WAV", "AUDIO\\ASS_10.WAV", "AUDIO\\ASS_11.WAV", "AUDIO\\ASS_12.WAV", "AUDIO\\ASS_13.WAV", "AUDIO\\ASS_14.WAV", "AUDIO\\BIKE1_1.WAV", "AUDIO\\BIKE1_2.WAV", "AUDIO\\BIKE1_3.WAV", "AUDIO\\BNK1_1.WAV", "AUDIO\\BNK1_2.WAV", "AUDIO\\BNK1_3.WAV", "AUDIO\\BNK1_4.WAV", "AUDIO\\BNK1_5.WAV", "AUDIO\\BNK1_6.WAV", "AUDIO\\BNK1_7.WAV", "AUDIO\\BNK1_8.WAV", "AUDIO\\BNK1_10.WAV", "AUDIO\\BNK1_11.WAV", "AUDIO\\BNK1_12.WAV", "AUDIO\\BNK1_13.WAV", "AUDIO\\BNK1_14.WAV", "AUDIO\\BNK2_1.WAV", "AUDIO\\BNK2_2.WAV", "AUDIO\\BNK2_3.WAV", "AUDIO\\BNK2_4.WAV", "AUDIO\\BNK2_5.WAV", "AUDIO\\BNK2_6.WAV", "AUDIO\\BNK2_7.WAV", "AUDIO\\BNK2_8.WAV", "AUDIO\\BNK2_9.WAV", "AUDIO\\BNK3_1.WAV", "AUDIO\\BNK3_2.WAV", "AUDIO\\BNK3_3A.WAV", "AUDIO\\BNK3_3B.WAV", "AUDIO\\BNK3_3C.WAV", "AUDIO\\BNK3_4A.WAV", "AUDIO\\BNK3_4B.WAV", "AUDIO\\BNK3_4C.WAV", "AUDIO\\BNK4_1.WAV", "AUDIO\\BNK4_2.WAV", "AUDIO\\BNK4_3A.WAV", "AUDIO\\BNK4_3B.WAV", "AUDIO\\BNK4_3C.WAV", "AUDIO\\BNK4_3D.WAV", "AUDIO\\BNK4_3E.WAV", "AUDIO\\BNK4_3F.WAV", "AUDIO\\BNK4_3G.WAV", "AUDIO\\BNK4_3H.WAV", "AUDIO\\BNK4_3I.WAV", "AUDIO\\BNK4_3J.WAV", "AUDIO\\BNK4_3K.WAV", "AUDIO\\BNK4_3M.WAV", "AUDIO\\BNK4_3O.WAV", "AUDIO\\BNK4_3P.WAV", "AUDIO\\BNK4_3Q.WAV", "AUDIO\\BNK4_3R.WAV", "AUDIO\\BNK4_3S.WAV", "AUDIO\\BNK4_3T.WAV", "AUDIO\\BNK4_3U.WAV", "AUDIO\\BNK4_3V.WAV", "AUDIO\\BNK4_4A.WAV", "AUDIO\\BNK4_4B.WAV", "AUDIO\\BNK4_5.WAV", "AUDIO\\BNK4_6.WAV", "AUDIO\\BNK4_7.WAV", "AUDIO\\BNK4_8.WAV", "AUDIO\\BNK4_9.WAV", "AUDIO\\BNK4_10.WAV", "AUDIO\\BNK4_11.WAV", "AUDIO\\BK4_12A.WAV", "AUDIO\\BK4_12B.WAV", "AUDIO\\BK4_12C.WAV", "AUDIO\\BNK4_13.WAV", "AUDIO\\BK4_14A.WAV", "AUDIO\\BK4_14B.WAV", "AUDIO\\BNK4_15.WAV", "AUDIO\\BNK4_16.WAV", "AUDIO\\BNK4_17.WAV", "AUDIO\\BNK4_18.WAV", "AUDIO\\BK4_19A.WAV", "AUDIO\\BK4_19B.WAV", "AUDIO\\BK4_20A.WAV", "AUDIO\\BK4_20B.WAV", "AUDIO\\BNK4_21.WAV", "AUDIO\\BNK422A.WAV", "AUDIO\\BNK422B.WAV", "AUDIO\\BK4_23A.WAV", "AUDIO\\BK4_23B.WAV", "AUDIO\\BK4_23C.WAV", "AUDIO\\BK4_23D.WAV", "AUDIO\\BK4_24A.WAV", "AUDIO\\BK4_24B.WAV", "AUDIO\\BNK4_25.WAV", "AUDIO\\BNK4_26.WAV", "AUDIO\\BNK4_27.WAV", "AUDIO\\BNK4_28.WAV", "AUDIO\\BNK4_29.WAV", "AUDIO\\BNK4_30.WAV", "AUDIO\\BK4_31A.WAV", "AUDIO\\BK4_31B.WAV", "AUDIO\\BNK4_32.WAV", "AUDIO\\BK4_34A.WAV", "AUDIO\\BK4_34B.WAV", "AUDIO\\BK4_35A.WAV", "AUDIO\\BK4_35B.WAV", "AUDIO\\BNK4_36.WAV", "AUDIO\\BNK4_37.WAV", "AUDIO\\BNK4_38.WAV", "AUDIO\\BNK4_39.WAV", "AUDIO\\BK4_40A.WAV", "AUDIO\\BK4_40B.WAV", "AUDIO\\BNK4_41.WAV", "AUDIO\\BNK4_42.WAV", "AUDIO\\BNK4_43.WAV", "AUDIO\\BNK4_44.WAV", "AUDIO\\BNK4_45.WAV", "AUDIO\\BNK4_46.WAV", "AUDIO\\BNK4_47.WAV", "AUDIO\\BNK4_48.WAV", "AUDIO\\BNK4_49.WAV", "AUDIO\\BNK450A.WAV", "AUDIO\\BNK450B.WAV", "AUDIO\\BNK4_51.WAV", "AUDIO\\BNK4_94.WAV", "AUDIO\\BNK4_95.WAV", "AUDIO\\BNK4_96.WAV", "AUDIO\\BNK4_97.WAV", "AUDIO\\BNK4_98.WAV", "AUDIO\\BNK4_99.WAV", "AUDIO\\BUD1_1.WAV", "AUDIO\\BUD1_2.WAV", "AUDIO\\BUD1_3.WAV", "AUDIO\\BUD1_4.WAV", "AUDIO\\BUD1_5.WAV", "AUDIO\\BUD1_9.WAV", "AUDIO\\BUD1_10.WAV", "AUDIO\\BUD2_1.WAV", "AUDIO\\BUD2_2.WAV", "AUDIO\\BUD2_3.WAV", "AUDIO\\BUD2_4.WAV", "AUDIO\\BUD2_5.WAV", "AUDIO\\BUD2_6.WAV", "AUDIO\\BUD2_7.WAV", "AUDIO\\BUD3_1.WAV", "AUDIO\\BUD3_1A.WAV", "AUDIO\\BUD3_1B.WAV", "AUDIO\\BUD3_1C.WAV", "AUDIO\\BUD3_2.WAV", "AUDIO\\BUD3_3.WAV", "AUDIO\\BUD3_4.WAV", "AUDIO\\BUD3_5.WAV", "AUDIO\\BUD3_6.WAV", "AUDIO\\BUD3_7.WAV", "AUDIO\\BUD3_8A.WAV", "AUDIO\\BUD3_8B.WAV", "AUDIO\\BUD3_8C.WAV", "AUDIO\\BUD3_9A.WAV", "AUDIO\\BUD3_9B.WAV", "AUDIO\\BUD3_9C.WAV", "AUDIO\\CAP1_2.WAV", "AUDIO\\CAP1_3.WAV", "AUDIO\\CAP1_4.WAV", "AUDIO\\CAP1_5.WAV", "AUDIO\\CAP1_6.WAV", "AUDIO\\CAP1_7.WAV", "AUDIO\\CAP1_8.WAV", "AUDIO\\CAP1_9.WAV", "AUDIO\\CAP1_10.WAV", "AUDIO\\CAP1_11.WAV", "AUDIO\\CAP1_12.WAV", "AUDIO\\CNT1_1.WAV", "AUDIO\\CNT1_2.WAV", "AUDIO\\CNT1_3.WAV", "AUDIO\\CNT1_4.WAV", "AUDIO\\CNT1_5.WAV", "AUDIO\\CNT2_1.WAV", "AUDIO\\CNT2_2.WAV", "AUDIO\\CNT2_3.WAV", "AUDIO\\CNT2_4.WAV", "AUDIO\\COK1_1.WAV", "AUDIO\\COK1_2.WAV", "AUDIO\\COK1_3.WAV", "AUDIO\\COK1_4.WAV", "AUDIO\\COK1_5.WAV", "AUDIO\\COK1_6.WAV", "AUDIO\\COK2_1.WAV", "AUDIO\\COK2_2.WAV", "AUDIO\\COK2_3.WAV", "AUDIO\\COK2_4.WAV", "AUDIO\\COK2_5.WAV", "AUDIO\\COK2_6.WAV", "AUDIO\\COK2_7A.WAV", "AUDIO\\COK2_7B.WAV", "AUDIO\\COK2_7C.WAV", "AUDIO\\COK2_8A.WAV", "AUDIO\\COK2_8B.WAV", "AUDIO\\COK2_8C.WAV", "AUDIO\\COK2_8D.WAV", "AUDIO\\COK2_9.WAV", "AUDIO\\COK210A.WAV", "AUDIO\\COK210B.WAV", "AUDIO\\COK210C.WAV", "AUDIO\\COK212A.WAV", "AUDIO\\COK212B.WAV", "AUDIO\\COK2_13.WAV", "AUDIO\\COK2_14.WAV", "AUDIO\\COK2_15.WAV", "AUDIO\\COK2_16.WAV", "AUDIO\\COK2_20.WAV", "AUDIO\\COK2_21.WAV", "AUDIO\\COK2_2.WAV", // this is probably a typo of COK2_22 "AUDIO\\COK3_1.WAV", "AUDIO\\COK3_2.WAV", "AUDIO\\COK3_3.WAV", "AUDIO\\COK3_4.WAV", "AUDIO\\COK4_1.WAV", "AUDIO\\COK4_2.WAV", "AUDIO\\COK4_3.WAV", "AUDIO\\COK4_4.WAV", "AUDIO\\COK4_5.WAV", "AUDIO\\COK4_6.WAV", "AUDIO\\COK4_7.WAV", "AUDIO\\COK4_8.WAV", "AUDIO\\COK4_9.WAV", "AUDIO\\COK4_9A.WAV", "AUDIO\\COK4_10.WAV", "AUDIO\\COK4_11.WAV", "AUDIO\\COK4_12.WAV", "AUDIO\\COK4_13.WAV", "AUDIO\\COK4_14.WAV", "AUDIO\\COK4_15.WAV", "AUDIO\\COK4_16.WAV", "AUDIO\\COK4_17.WAV", "AUDIO\\COK4_18.WAV", "AUDIO\\COK4_19.WAV", "AUDIO\\COK4_20.WAV", "AUDIO\\COK4_21.WAV", "AUDIO\\COK4_22.WAV", "AUDIO\\COK4_23.WAV", "AUDIO\\COK4_24.WAV", "AUDIO\\COK4_25.WAV", "AUDIO\\COK4_26.WAV", "AUDIO\\COK4_27.WAV", "AUDIO\\COL1_1.WAV", "AUDIO\\COL1_2.WAV", "AUDIO\\COL1_3.WAV", "AUDIO\\COL1_4.WAV", "AUDIO\\COL1_5.WAV", "AUDIO\\COL1_6.WAV", "AUDIO\\COL1_7.WAV", "AUDIO\\COL1_8.WAV", "AUDIO\\COL2_1.WAV", "AUDIO\\COL2_2.WAV", "AUDIO\\COL2_3.WAV", "AUDIO\\COL2_4.WAV", "AUDIO\\COL2_5.WAV", "AUDIO\\COL2_6A.WAV", "AUDIO\\COL2_7.WAV", "AUDIO\\COL2_8.WAV", "AUDIO\\COL2_9.WAV", "AUDIO\\COL2_10.WAV", "AUDIO\\COL2_11.WAV", "AUDIO\\COL2_12.WAV", "AUDIO\\COL2_13.WAV", "AUDIO\\COL2_14.WAV", "AUDIO\\COL2_15.WAV", "AUDIO\\COL2_16.WAV", "AUDIO\\COL3_1.WAV", "AUDIO\\COL3_2.WAV", "AUDIO\\COL3_2A.WAV", "AUDIO\\COL3_2B.WAV", "AUDIO\\COL3_3.WAV", "AUDIO\\COL3_4.WAV", "AUDIO\\COL3_5.WAV", "AUDIO\\COL3_6.WAV", "AUDIO\\COL3_7.WAV", "AUDIO\\COL3_8.WAV", "AUDIO\\COL3_9.WAV", "AUDIO\\COL3_10.WAV", "AUDIO\\COL3_11.WAV", "AUDIO\\COL3_12.WAV", "AUDIO\\COL3_13.WAV", "AUDIO\\COL3_14.WAV", "AUDIO\\COL3_15.WAV", "AUDIO\\COL3_16.WAV", "AUDIO\\COL3_17.WAV", "AUDIO\\COL3_18.WAV", "AUDIO\\COL3_19.WAV", "AUDIO\\COL3_20.WAV", "AUDIO\\COL3_21.WAV", "AUDIO\\COL3_23.WAV", "AUDIO\\COL3_24.WAV", "AUDIO\\COL3_25.WAV", "AUDIO\\COL4_1.WAV", "AUDIO\\COL4_2.WAV", "AUDIO\\COL4_3.WAV", "AUDIO\\COL4_4.WAV", "AUDIO\\COL4_5.WAV", "AUDIO\\COL4_6.WAV", "AUDIO\\COL4_7.WAV", "AUDIO\\COL4_8.WAV", "AUDIO\\COL4_9.WAV", "AUDIO\\COL4_10.WAV", "AUDIO\\COL4_11.WAV", "AUDIO\\COL4_12.WAV", "AUDIO\\COL4_13.WAV", "AUDIO\\COL4_14.WAV", "AUDIO\\COL4_15.WAV", "AUDIO\\COL4_16.WAV", "AUDIO\\COL4_17.WAV", "AUDIO\\COL4_18.WAV", "AUDIO\\COL4_19.WAV", "AUDIO\\COL4_20.WAV", "AUDIO\\COL4_21.WAV", "AUDIO\\COL4_22.WAV", "AUDIO\\COL4_23.WAV", "AUDIO\\COL4_24.WAV", "AUDIO\\COL4_25.WAV", "AUDIO\\COL4_26.WAV", "AUDIO\\COL5_1.WAV", "AUDIO\\COL5_2.WAV", "AUDIO\\COL5_3.WAV", "AUDIO\\COL5_4.WAV", "AUDIO\\COL5_5.WAV", "AUDIO\\COL5_6.WAV", "AUDIO\\COL5_7.WAV", "AUDIO\\COL5_8.WAV", "AUDIO\\COL5_9.WAV", "AUDIO\\COL5_10.WAV", "AUDIO\\COL5_11.WAV", "AUDIO\\COL5_12.WAV", "AUDIO\\COL5_13.WAV", "AUDIO\\COL5_14.WAV", "AUDIO\\COL5_15.WAV", "AUDIO\\COL5_16.WAV", "AUDIO\\COL5_17.WAV", "AUDIO\\COL5_18.WAV", "AUDIO\\COL5_19.WAV", "AUDIO\\COL5_20.WAV", "AUDIO\\COL5_21.WAV", "AUDIO\\COL5_22.WAV", "AUDIO\\CUB1_1.WAV", "AUDIO\\CUB1_2.WAV", "AUDIO\\CUB1_3.WAV", "AUDIO\\CUB1_4.WAV", "AUDIO\\CUB1_5.WAV", "AUDIO\\CUB1_6.WAV", "AUDIO\\CUB1_7.WAV", "AUDIO\\CUB1_8.WAV", "AUDIO\\CUB1_9.WAV", "AUDIO\\CUB1_10.WAV", "AUDIO\\CUB2_1.WAV", "AUDIO\\CUB2_2.WAV", "AUDIO\\CUB2_3A.WAV", "AUDIO\\CUB2_3B.WAV", "AUDIO\\CUB2_3C.WAV", "AUDIO\\CUB2_4A.WAV", "AUDIO\\CUB2_5.WAV", "AUDIO\\CUB2_6.WAV", "AUDIO\\CUB2_7.WAV", "AUDIO\\CUB2_8.WAV", "AUDIO\\CUB2_9.WAV", "AUDIO\\CUB2_10.WAV", "AUDIO\\CUB2_11.WAV", "AUDIO\\CUB3_1.WAV", "AUDIO\\CUB3_2.WAV", "AUDIO\\CUB3_3.WAV", "AUDIO\\CUB3_4.WAV", "AUDIO\\CUB4_1.WAV", "AUDIO\\CUB4_2.WAV", "AUDIO\\CUB4_3.WAV", "AUDIO\\CUB4_4.WAV", "AUDIO\\CUB4_5.WAV", "AUDIO\\CUB4_5A.WAV", "AUDIO\\CUB4_6.WAV", "AUDIO\\CUB4_7.WAV", "AUDIO\\CUB4_8.WAV", "AUDIO\\CUB4_9.WAV", "AUDIO\\CUB4_10.WAV", "AUDIO\\CUB4_11.WAV", "AUDIO\\CUB4_12.WAV", "AUDIO\\CUB4_13.WAV", "AUDIO\\CUB4_14.WAV", "AUDIO\\CUB4_15.WAV", "AUDIO\\CUB4_16.WAV", "AUDIO\\GOLF_1.WAV", "AUDIO\\GOLF_2.WAV", "AUDIO\\GOLF_3.WAV", "AUDIO\\BAR_1.WAV", "AUDIO\\BAR_2.WAV", "AUDIO\\BAR_3.WAV", "AUDIO\\BAR_4.WAV", "AUDIO\\BAR_5.WAV", "AUDIO\\BAR_6.WAV", "AUDIO\\BAR_7.WAV", "AUDIO\\BAR_8.WAV", "AUDIO\\STRIP_1.WAV", "AUDIO\\STRIP_2.WAV", "AUDIO\\STRIP_3.WAV", "AUDIO\\STRIP_4.WAV", "AUDIO\\STRIP_5.WAV", "AUDIO\\STRIP_6.WAV", "AUDIO\\STRIP_7.WAV", "AUDIO\\STRIP_8.WAV", "AUDIO\\STRIP_9.WAV", "AUDIO\\STAR_1.WAV", "AUDIO\\STAR_2.WAV", "AUDIO\\STAR_3.WAV", "AUDIO\\STAR_4.WAV", "AUDIO\\FIN_1A.WAV", "AUDIO\\FIN_1B.WAV", "AUDIO\\FIN_1C.WAV", "AUDIO\\FIN_2B.WAV", "AUDIO\\FIN_2C.WAV", "AUDIO\\FIN_3.WAV", "AUDIO\\FIN_4.WAV", "AUDIO\\FIN_5.WAV", "AUDIO\\FIN_6.WAV", "AUDIO\\FIN_10.WAV", "AUDIO\\FIN_11A.WAV", "AUDIO\\FIN_11B.WAV", "AUDIO\\FIN_12A.WAV", "AUDIO\\FIN_12B.WAV", "AUDIO\\FIN_12C.WAV", "AUDIO\\FIN_13.WAV", "AUDIO\\FINKILL.WAV", "AUDIO\\LAW1_1.WAV", "AUDIO\\LAW1_2.WAV", "AUDIO\\LAW1_3.WAV", "AUDIO\\LAW1_4.WAV", "AUDIO\\LAW1_5.WAV", "AUDIO\\LAW1_6.WAV", "AUDIO\\LAW1_7.WAV", "AUDIO\\LAW1_8.WAV", "AUDIO\\LAW1_9.WAV", "AUDIO\\LAW1_10.WAV", "AUDIO\\LAW2_1.WAV", "AUDIO\\LAW2_2.WAV", "AUDIO\\LAW2_3.WAV", "AUDIO\\LAW2_4.WAV", "AUDIO\\LAW2_5.WAV", "AUDIO\\LAW2_6.WAV", "AUDIO\\LAW2_7.WAV", "AUDIO\\LAW2_8.WAV", "AUDIO\\LAW2_9.WAV", "AUDIO\\LAW2_10.WAV", "AUDIO\\LAW3_1.WAV", "AUDIO\\LAW3_2.WAV", "AUDIO\\LAW3_3.WAV", "AUDIO\\LAW3_4.WAV", "AUDIO\\LAW3_5.WAV", "AUDIO\\LAW3_6.WAV", "AUDIO\\LAW3_10.WAV", "AUDIO\\LAW3_11.WAV", "AUDIO\\LAW3_12.WAV", "AUDIO\\LAW3_13.WAV", "AUDIO\\LAW3_14.WAV", "AUDIO\\LAW3_16.WAV", "AUDIO\\LAW3_17.WAV", "AUDIO\\LAW3_18.WAV", "AUDIO\\LAW3_19.WAV", "AUDIO\\LAW3_20.WAV", "AUDIO\\LAW3_21.WAV", "AUDIO\\LAW3_22.WAV", "AUDIO\\LAW3_23.WAV", "AUDIO\\LAW3_24.WAV", "AUDIO\\LAW3_25.WAV", "AUDIO\\LAW4_1A.WAV", "AUDIO\\LAW4_1B.WAV", "AUDIO\\LAW4_1C.WAV", "AUDIO\\LAW4_1D.WAV", "AUDIO\\LAW4_10.WAV", "AUDIO\\LAW4_3.WAV", "AUDIO\\LAW4_4.WAV", "AUDIO\\LAW4_5.WAV", "AUDIO\\LAW4_6.WAV", "AUDIO\\LAW4_7.WAV", "AUDIO\\LAW4_8.WAV", "AUDIO\\LAW4_9.WAV", "AUDIO\\PHIL1_2.WAV", "AUDIO\\PHIL1_3.WAV", "AUDIO\\PHIL2_1.WAV", "AUDIO\\PHIL2_2.WAV", "AUDIO\\PHIL2_3.WAV", "AUDIO\\PHIL2_4.WAV", "AUDIO\\PHIL2_5.WAV", "AUDIO\\PHIL2_6.WAV", "AUDIO\\PHIL2_7.WAV", "AUDIO\\PHIL2_8.WAV", "AUDIO\\PHIL2_9.WAV", "AUDIO\\PHIL210.WAV", "AUDIO\\PHIL211.WAV", "AUDIO\\PORN1_1.WAV", "AUDIO\\PORN1_2.WAV", "AUDIO\\PORN1_3.WAV", "AUDIO\\PRN1_3A.WAV", "AUDIO\\PORN1_4.WAV", "AUDIO\\PORN1_5.WAV", "AUDIO\\PORN1_6.WAV", "AUDIO\\PORN1_7.WAV", "AUDIO\\PORN1_8.WAV", "AUDIO\\PORN1_9.WAV", "AUDIO\\PRN1_10.WAV", "AUDIO\\PRN1_11.WAV", "AUDIO\\PRN1_12.WAV", "AUDIO\\PRN1_13.WAV", "AUDIO\\PRN1_14.WAV", "AUDIO\\PRN1_15.WAV", "AUDIO\\PRN1_16.WAV", "AUDIO\\PRN1_17.WAV", "AUDIO\\PRN1_18.WAV", "AUDIO\\PRN1_19.WAV", "AUDIO\\PRN1_20.WAV", "AUDIO\\PRN1_21.WAV", "AUDIO\\PORN3_1.WAV", "AUDIO\\PORN3_2.WAV", "AUDIO\\PORN3_3.WAV", "AUDIO\\PORN3_4.WAV", "AUDIO\\PSYCH_1.WAV", "AUDIO\\PSYCH_2.WAV", "AUDIO\\ROK2_01.WAV", "AUDIO\\ROK3_1.WAV", "AUDIO\\ROK3_2.WAV", "AUDIO\\ROK3_3.WAV", "AUDIO\\ROK3_4.WAV", "AUDIO\\ROK3_5.WAV", "AUDIO\\ROK3_6.WAV", "AUDIO\\ROK3_7.WAV", "AUDIO\\ROK3_8.WAV", "AUDIO\\ROK3_9.WAV", "AUDIO\\ROK3_10.WAV", "AUDIO\\ROK3_11.WAV", "AUDIO\\ROK3_12.WAV", "AUDIO\\ROK3_13.WAV", "AUDIO\\ROK3_14.WAV", "AUDIO\\ROK3_15.WAV", "AUDIO\\ROK3_16.WAV", "AUDIO\\ROK3_17.WAV", "AUDIO\\ROK3_18.WAV", "AUDIO\\ROK3_19.WAV", "AUDIO\\ROK3_20.WAV", "AUDIO\\ROK3_21.WAV", "AUDIO\\ROK3_22.WAV", "AUDIO\\ROK3_23.WAV", "AUDIO\\ROK3_24.WAV", "AUDIO\\ROK3_25.WAV", "AUDIO\\ROK3_26.WAV", "AUDIO\\ROK3_27.WAV", "AUDIO\\ROK3_62.WAV", "AUDIO\\ROK3_63.WAV", "AUDIO\\ROK3_64.WAV", "AUDIO\\ROK3_65.WAV", "AUDIO\\ROK3_66.WAV", "AUDIO\\ROK3_67.WAV", "AUDIO\\ROK3_68.WAV", "AUDIO\\ROK3_69.WAV", "AUDIO\\ROK3_70.WAV", "AUDIO\\ROK3_71.WAV", "AUDIO\\ROK3_73.WAV", "AUDIO\\RESC_1.WAV", "AUDIO\\RESC_2.WAV", "AUDIO\\RESC_3.WAV", "AUDIO\\RESC_4.WAV", "AUDIO\\RESC_5.WAV", "AUDIO\\RESC_6.WAV", "AUDIO\\RESC_7.WAV", "AUDIO\\RESC_8.WAV", "AUDIO\\RESC_9.WAV", "AUDIO\\RESC_10.WAV", "AUDIO\\ROK1_1A.WAV", "AUDIO\\ROK1_1B.WAV", "AUDIO\\ROK1_5.WAV", "AUDIO\\ROK1_6.WAV", "AUDIO\\ROK1_7.WAV", "AUDIO\\ROK1_8.WAV", "AUDIO\\ROK1_9.WAV", "AUDIO\\TAX1_1.WAV", "AUDIO\\TAX1_2.WAV", "AUDIO\\TAX1_3.WAV", "AUDIO\\TAX1_4.WAV", "AUDIO\\TAX1_5.WAV", "AUDIO\\TAX2_1.WAV", "AUDIO\\TAX2_2.WAV", "AUDIO\\TAX2_3.WAV", "AUDIO\\TAX2_4.WAV", "AUDIO\\TAX2_5.WAV", "AUDIO\\TAX2_6.WAV", "AUDIO\\TAX2_7.WAV", "AUDIO\\TAX3_1.WAV", "AUDIO\\TAX3_2.WAV", "AUDIO\\TAX3_3.WAV", "AUDIO\\TAX3_4.WAV", "AUDIO\\TAX3_5.WAV", "AUDIO\\TEX1_1.WAV", "AUDIO\\TEX1_2.WAV", "AUDIO\\TEX1_3.WAV", "AUDIO\\TEX1_4.WAV", "AUDIO\\TEX1_5.WAV", "AUDIO\\TEX1_6.WAV", "AUDIO\\TEX2_1.WAV", "AUDIO\\TEX3_1.WAV", "AUDIO\\TEX3_2.WAV", "AUDIO\\TEX3_3.WAV", "AUDIO\\TEX3_4.WAV", "AUDIO\\TEX3_5.WAV", "AUDIO\\TEX3_6.WAV", "AUDIO\\TEX3_7.WAV", "AUDIO\\TEX3_8.WAV", "AUDIO\\HAT_1A.WAV", "AUDIO\\INTRO1.WAV", "AUDIO\\INTRO2.WAV", "AUDIO\\INTRO3.WAV", "AUDIO\\INTRO4.WAV", "AUDIO\\MOB_01A.WAV", "AUDIO\\MOB_01B.WAV", "AUDIO\\MOB_01C.WAV", "AUDIO\\MOB_02A.WAV", "AUDIO\\MOB_02B.WAV", "AUDIO\\MOB_02C.WAV", "AUDIO\\MOB_03A.WAV", "AUDIO\\MOB_03B.WAV", "AUDIO\\MOB_03C.WAV", "AUDIO\\MOB_03D.WAV", "AUDIO\\MOB_03E.WAV", "AUDIO\\SHARK_1.WAV", "AUDIO\\SHARK_2.WAV", "AUDIO\\SHARK_3.WAV", "AUDIO\\SHARK_4.WAV", "AUDIO\\SHARK_5.WAV", "AUDIO\\MOB_04A.WAV", "AUDIO\\MOB_04B.WAV", "AUDIO\\MOB_04C.WAV", "AUDIO\\MOB_04D.WAV", "AUDIO\\MOB_05A.WAV", "AUDIO\\MOB_05B.WAV", "AUDIO\\MOB_05C.WAV", "AUDIO\\MOB_05D.WAV", "AUDIO\\MOB_06A.WAV", "AUDIO\\MOB_06B.WAV", "AUDIO\\MOB_06C.WAV", "AUDIO\\MOB_07A.WAV", "AUDIO\\MOB_07B.WAV", "AUDIO\\MOB_08A.WAV", "AUDIO\\MOB_08B.WAV", "AUDIO\\MOB_08C.WAV", "AUDIO\\MOB_08D.WAV", "AUDIO\\MOB_08E.WAV", "AUDIO\\MOB_08F.WAV", "AUDIO\\MOB_08G.WAV", "AUDIO\\MOB_09A.WAV", "AUDIO\\MOB_09B.WAV", "AUDIO\\MOB_09C.WAV", "AUDIO\\MOB_09D.WAV", "AUDIO\\MOB_09E.WAV", "AUDIO\\MOB_09F.WAV", "AUDIO\\MOB_10A.WAV", "AUDIO\\MOB_10B.WAV", "AUDIO\\MOB_10C.WAV", "AUDIO\\MOB_10D.WAV", "AUDIO\\MOB_10E.WAV", "AUDIO\\MOB_11A.WAV", "AUDIO\\MOB_11B.WAV", "AUDIO\\MOB_11C.WAV", "AUDIO\\MOB_11D.WAV", "AUDIO\\MOB_11E.WAV", "AUDIO\\MOB_11F.WAV", "AUDIO\\MOB_14A.WAV", "AUDIO\\MOB_14B.WAV", "AUDIO\\MOB_14C.WAV", "AUDIO\\MOB_14D.WAV", "AUDIO\\MOB_14E.WAV", "AUDIO\\MOB_14F.WAV", "AUDIO\\MOB_14G.WAV", "AUDIO\\MOB_14H.WAV", "AUDIO\\MOB_16A.WAV", "AUDIO\\MOB_16B.WAV", "AUDIO\\MOB_16C.WAV", "AUDIO\\MOB_16D.WAV", "AUDIO\\MOB_16E.WAV", "AUDIO\\MOB_16F.WAV", "AUDIO\\MOB_16G.WAV", "AUDIO\\MOB_17A.WAV", "AUDIO\\MOB_17B.WAV", "AUDIO\\MOB_17C.WAV", "AUDIO\\MOB_17D.WAV", "AUDIO\\MOB_17E.WAV", "AUDIO\\MOB_17G.WAV", "AUDIO\\MOB_17H.WAV", "AUDIO\\MOB_17I.WAV", "AUDIO\\MOB_17J.WAV", "AUDIO\\MOB_17K.WAV", "AUDIO\\MOB_17L.WAV", "AUDIO\\MOB_18A.WAV", "AUDIO\\MOB_18B.WAV", "AUDIO\\MOB_18C.WAV", "AUDIO\\MOB_18D.WAV", "AUDIO\\MOB_18E.WAV", "AUDIO\\MOB_18F.WAV", "AUDIO\\MOB_18G.WAV", "AUDIO\\MOB_20A.WAV", "AUDIO\\MOB_20B.WAV", "AUDIO\\MOB_20C.WAV", "AUDIO\\MOB_20D.WAV", "AUDIO\\MOB_20E.WAV", "AUDIO\\MOB_24A.WAV", "AUDIO\\MOB_24B.WAV", "AUDIO\\MOB_24C.WAV", "AUDIO\\MOB_24D.WAV", "AUDIO\\MOB_24E.WAV", "AUDIO\\MOB_24F.WAV", "AUDIO\\MOB_24G.WAV", "AUDIO\\MOB_24H.WAV", "AUDIO\\MOB_25A.WAV", "AUDIO\\MOB_25B.WAV", "AUDIO\\MOB_25C.WAV", "AUDIO\\MOB_25D.WAV", "AUDIO\\MOB_26A.WAV", "AUDIO\\MOB_26B.WAV", "AUDIO\\MOB_26C.WAV", "AUDIO\\MOB_26D.WAV", "AUDIO\\MOB_26E.WAV", "AUDIO\\MOB_29A.WAV", "AUDIO\\MOB_29B.WAV", "AUDIO\\MOB_29C.WAV", "AUDIO\\MOB_29D.WAV", "AUDIO\\MOB_29E.WAV", "AUDIO\\MOB_29F.WAV", "AUDIO\\MOB_29G.WAV", "AUDIO\\MOB_30A.WAV", "AUDIO\\MOB_30B.WAV", "AUDIO\\MOB_30C.WAV", "AUDIO\\MOB_30D.WAV", "AUDIO\\MOB_30E.WAV", "AUDIO\\MOB_30F.WAV", "AUDIO\\MOB_33A.WAV", "AUDIO\\MOB_33B.WAV", "AUDIO\\MOB_33C.WAV", "AUDIO\\MOB_33D.WAV", "AUDIO\\MOB_34A.WAV", "AUDIO\\MOB_34B.WAV", "AUDIO\\MOB_34C.WAV", "AUDIO\\MOB_34D.WAV", "AUDIO\\MOB_35A.WAV", "AUDIO\\MOB_35B.WAV", "AUDIO\\MOB_35C.WAV", "AUDIO\\MOB_35D.WAV", "AUDIO\\MOB_36A.WAV", "AUDIO\\MOB_36B.WAV", "AUDIO\\MOB_36C.WAV", "AUDIO\\MOB_40A.WAV", "AUDIO\\MOB_40B.WAV", "AUDIO\\MOB_40C.WAV", "AUDIO\\MOB_40D.WAV", "AUDIO\\MOB_40E.WAV", "AUDIO\\MOB_40F.WAV", "AUDIO\\MOB_40G.WAV", "AUDIO\\MOB_40H.WAV", "AUDIO\\MOB_40I.WAV", "AUDIO\\MOB_41A.WAV", "AUDIO\\MOB_41B.WAV", "AUDIO\\MOB_41C.WAV", "AUDIO\\MOB_41D.WAV", "AUDIO\\MOB_41E.WAV", "AUDIO\\MOB_41F.WAV", "AUDIO\\MOB_41G.WAV", "AUDIO\\MOB_41H.WAV", "AUDIO\\MOB_42A.WAV", "AUDIO\\MOB_42B.WAV", "AUDIO\\MOB_42C.WAV", "AUDIO\\MOB_42D.WAV", "AUDIO\\MOB_42E.WAV", "AUDIO\\MOB_43A.WAV", "AUDIO\\MOB_43B.WAV", "AUDIO\\MOB_43C.WAV", "AUDIO\\MOB_43D.WAV", "AUDIO\\MOB_43E.WAV", "AUDIO\\MOB_43F.WAV", "AUDIO\\MOB_43G.WAV", "AUDIO\\MOB_43H.WAV", "AUDIO\\MOB_45A.WAV", "AUDIO\\MOB_45B.WAV", "AUDIO\\MOB_45C.WAV", "AUDIO\\MOB_45D.WAV", "AUDIO\\MOB_45E.WAV", "AUDIO\\MOB_45F.WAV", "AUDIO\\MOB_45G.WAV", "AUDIO\\MOB_45H.WAV", "AUDIO\\MOB_45I.WAV", "AUDIO\\MOB_45J.WAV", "AUDIO\\MOB_45K.WAV", "AUDIO\\MOB_45L.WAV", "AUDIO\\MOB_45M.WAV", "AUDIO\\MOB_45N.WAV", "AUDIO\\MOB_46A.WAV", "AUDIO\\MOB_46B.WAV", "AUDIO\\MOB_46C.WAV", "AUDIO\\MOB_46D.WAV", "AUDIO\\MOB_46E.WAV", "AUDIO\\MOB_46F.WAV", "AUDIO\\MOB_46G.WAV", "AUDIO\\MOB_46H.WAV", "AUDIO\\MOB_47A.WAV", "AUDIO\\MOB_52A.WAV", "AUDIO\\MOB_52B.WAV", "AUDIO\\MOB_52C.WAV", "AUDIO\\MOB_52D.WAV", "AUDIO\\MOB_52E.WAV", "AUDIO\\MOB_52F.WAV", "AUDIO\\MOB_52G.WAV", "AUDIO\\MOB_52H.WAV", "AUDIO\\MOB_54A.WAV", "AUDIO\\MOB_54B.WAV", "AUDIO\\MOB_54C.WAV", "AUDIO\\MOB_54D.WAV", "AUDIO\\MOB_54E.WAV", "AUDIO\\MOB_55A.WAV", "AUDIO\\MOB_55B.WAV", "AUDIO\\MOB_55C.WAV", "AUDIO\\MOB_55D.WAV", "AUDIO\\MOB_55E.WAV", "AUDIO\\MOB_55F.WAV", "AUDIO\\MOB_56A.WAV", "AUDIO\\MOB_56B.WAV", "AUDIO\\MOB_56C.WAV", "AUDIO\\MOB_56D.WAV", "AUDIO\\MOB_56E.WAV", "AUDIO\\MOB_56F.WAV", "AUDIO\\MOB_57A.WAV", "AUDIO\\MOB_57B.WAV", "AUDIO\\MOB_57C.WAV", "AUDIO\\MOB_57D.WAV", "AUDIO\\MOB_57E.WAV", "AUDIO\\MOB_58A.WAV", "AUDIO\\MOB_58B.WAV", "AUDIO\\MOB_58C.WAV", "AUDIO\\MOB_58D.WAV", "AUDIO\\MOB_58E.WAV", "AUDIO\\MOB_58F.WAV", "AUDIO\\MOB_58G.WAV", "AUDIO\\MOB_61A.WAV", "AUDIO\\MOB_61B.WAV", "AUDIO\\MOB_62A.WAV", "AUDIO\\MOB_62B.WAV", "AUDIO\\MOB_62C.WAV", "AUDIO\\MOB_62D.WAV", "AUDIO\\MOB_63A.WAV", "AUDIO\\MOB_63B.WAV", "AUDIO\\MOB_63C.WAV", "AUDIO\\MOB_63D.WAV", "AUDIO\\MOB_63E.WAV", "AUDIO\\MOB_63F.WAV", "AUDIO\\MOB_63G.WAV", "AUDIO\\MOB_63H.WAV", "AUDIO\\MOB_63I.WAV", "AUDIO\\MOB_63J.WAV", "AUDIO\\MOB_66A.WAV", "AUDIO\\MOB_66B.WAV", "AUDIO\\MOB_68A.WAV", "AUDIO\\MOB_68B.WAV", "AUDIO\\MOB_68C.WAV", "AUDIO\\MOB_68D.WAV", "AUDIO\\MOB_70A.WAV", "AUDIO\\MOB_70B.WAV", "AUDIO\\MOB_71A.WAV", "AUDIO\\MOB_71B.WAV", "AUDIO\\MOB_71C.WAV", "AUDIO\\MOB_71D.WAV", "AUDIO\\MOB_71E.WAV", "AUDIO\\MOB_71F.WAV", "AUDIO\\MOB_71G.WAV", "AUDIO\\MOB_71H.WAV", "AUDIO\\MOB_71I.WAV", "AUDIO\\MOB_71J.WAV", "AUDIO\\MOB_71K.WAV", "AUDIO\\MOB_71L.WAV", "AUDIO\\MOB_71M.WAV", "AUDIO\\MOB_71N.WAV", "AUDIO\\MOB_72A.WAV", "AUDIO\\MOB_72B.WAV", "AUDIO\\MOB_72C.WAV", "AUDIO\\MOB_72D.WAV", "AUDIO\\MOB_72E.WAV", "AUDIO\\MOB_72F.WAV", "AUDIO\\MOB_72G.WAV", "AUDIO\\MOB_73A.WAV", "AUDIO\\MOB_73C.WAV", "AUDIO\\MOB_73D.WAV", "AUDIO\\MOB_73F.WAV", "AUDIO\\MOB_73G.WAV", "AUDIO\\MOB_73I.WAV", "AUDIO\\MOB_95A.WAV", "AUDIO\\MOB_96A.WAV", "AUDIO\\MOB_98A.WAV", "AUDIO\\MOB_99A.WAV", "AUDIO\\JOB1_1B.WAV", "AUDIO\\JOB1_1C.WAV", "AUDIO\\JOB1_1D.WAV", "AUDIO\\JOB2_1B.WAV", "AUDIO\\JOB2_2.WAV", "AUDIO\\JOB2_3.WAV", "AUDIO\\JOB2_4.WAV", "AUDIO\\JOB2_5.WAV", "AUDIO\\JOB2_6.WAV", "AUDIO\\JOB2_7.WAV", "AUDIO\\JOB2_8.WAV", "AUDIO\\JOB2_9.WAV", "AUDIO\\JOB3_1.WAV", "AUDIO\\JOB3_2.WAV", "AUDIO\\JOB3_3.WAV", "AUDIO\\JOB4_1.WAV", "AUDIO\\JOB4_2.WAV", "AUDIO\\JOB4_3.WAV", "AUDIO\\JOB5_1.WAV", "AUDIO\\JOB5_2.WAV", "AUDIO\\JOB5_3.WAV", "AUDIO\\BJM1_20.WAV", "AUDIO\\BJM1_4.WAV", "AUDIO\\BJM1_5.WAV", "AUDIO\\MERC_39.WAV", "AUDIO\\MONO_1.WAV", "AUDIO\\MONO_2.WAV", "AUDIO\\MONO_3.WAV", "AUDIO\\MONO_4.WAV", "AUDIO\\MONO_5.WAV", "AUDIO\\MONO_6.WAV", "AUDIO\\MONO_7.WAV", "AUDIO\\MONO_8.WAV", "AUDIO\\MONO_9.WAV", "AUDIO\\MONO10.WAV", "AUDIO\\MONO11.WAV", "AUDIO\\MONO12.WAV", "AUDIO\\MONO13.WAV", "AUDIO\\MONO14.WAV", "AUDIO\\MONO15.WAV", "AUDIO\\MONO16.WAV", "AUDIO\\FUD_01.WAV", "AUDIO\\FUD_02.WAV", "AUDIO\\FUD_03.WAV", "AUDIO\\FUD_04.WAV", "AUDIO\\FUD_05.WAV", "AUDIO\\FUD_06.WAV", "AUDIO\\FUD_07.WAV", "AUDIO\\FUD_08.WAV", "AUDIO\\FUD_09.WAV", "AUDIO\\FUD_10.WAV", "AUDIO\\FUD_11.WAV", "AUDIO\\FUD_12.WAV", "AUDIO\\FUD_13.WAV", "AUDIO\\FUD_14.WAV", "AUDIO\\FUD_15.WAV", "AUDIO\\FUD_16.WAV", "AUDIO\\FUD_17.WAV", "AUDIO\\FUD_18.WAV", "AUDIO\\FUD_19.WAV", "AUDIO\\FUD_20.WAV", "AUDIO\\BURG_01.WAV", "AUDIO\\BURG_02.WAV", "AUDIO\\BURG_03.WAV", "AUDIO\\BURG_04.WAV", "AUDIO\\BURG_05.WAV", "AUDIO\\BURG_06.WAV", "AUDIO\\BURG_07.WAV", "AUDIO\\BURG_08.WAV", "AUDIO\\BURG_09.WAV", "AUDIO\\BURG_10.WAV", "AUDIO\\BURG_11.WAV", "AUDIO\\BURG_12.WAV", "AUDIO\\CRUST01.WAV", "AUDIO\\CRUST02.WAV", "AUDIO\\CRUST03.WAV", "AUDIO\\CRUST04.WAV", "AUDIO\\CRUST05.WAV", "AUDIO\\CRUST06.WAV", "AUDIO\\CRUST07.WAV", "AUDIO\\CRUST08.WAV", "AUDIO\\CRUST09.WAV", "AUDIO\\BAND_01.WAV", "AUDIO\\BAND_02.WAV", "AUDIO\\BAND_03.WAV", "AUDIO\\BAND_04.WAV", "AUDIO\\BAND_05.WAV", "AUDIO\\BAND_06.WAV", "AUDIO\\BAND_07.WAV", "AUDIO\\BAND_08.WAV", "AUDIO\\SHAFT01.WAV", "AUDIO\\SHAFT02.WAV", "AUDIO\\SHAFT03.WAV", "AUDIO\\SHAFT04.WAV", "AUDIO\\SHAFT05.WAV", "AUDIO\\SHAFT06.WAV", "AUDIO\\SHAFT07.WAV", "AUDIO\\SHAFT08.WAV", "AUDIO\\PISS_01.WAV", "AUDIO\\PISS_02.WAV", "AUDIO\\PISS_03.WAV", "AUDIO\\PISS_04.WAV", "AUDIO\\PISS_05.WAV", "AUDIO\\PISS_06.WAV", "AUDIO\\PISS_07.WAV", "AUDIO\\PISS_08.WAV", "AUDIO\\PISS_09.WAV", "AUDIO\\PISS_10.WAV", "AUDIO\\PISS_11.WAV", "AUDIO\\PISS_12.WAV", "AUDIO\\PISS_13.WAV", "AUDIO\\PISS_14.WAV", "AUDIO\\PISS_15.WAV", "AUDIO\\PISS_16.WAV", "AUDIO\\PISS_17.WAV", "AUDIO\\PISS_18.WAV", "AUDIO\\PISS_19.WAV", "AUDIO\\GIMME01.WAV", "AUDIO\\GIMME02.WAV", "AUDIO\\GIMME03.WAV", "AUDIO\\GIMME04.WAV", "AUDIO\\GIMME05.WAV", "AUDIO\\GIMME06.WAV", "AUDIO\\GIMME07.WAV", "AUDIO\\GIMME08.WAV", "AUDIO\\GIMME09.WAV", "AUDIO\\GIMME10.WAV", "AUDIO\\GIMME11.WAV", "AUDIO\\GIMME12.WAV", "AUDIO\\GIMME13.WAV", "AUDIO\\GIMME14.WAV", "AUDIO\\GIMME15.WAV", "AUDIO\\BUST_01.WAV", "AUDIO\\BUST_02.WAV", "AUDIO\\BUST_03.WAV", "AUDIO\\BUST_04.WAV", "AUDIO\\BUST_05.WAV", "AUDIO\\BUST_06.WAV", "AUDIO\\BUST_07.WAV", "AUDIO\\BUST_08.WAV", "AUDIO\\BUST_09.WAV", "AUDIO\\BUST_10.WAV", "AUDIO\\BUST_11.WAV", "AUDIO\\BUST_12.WAV", "AUDIO\\BUST_13.WAV", "AUDIO\\BUST_14.WAV", "AUDIO\\BUST_15.WAV", "AUDIO\\BUST_16.WAV", "AUDIO\\BUST_17.WAV", "AUDIO\\BUST_18.WAV", "AUDIO\\BUST_19.WAV", "AUDIO\\BUST_20.WAV", "AUDIO\\BUST_21.WAV", "AUDIO\\BUST_22.WAV", "AUDIO\\BUST_23.WAV", "AUDIO\\BUST_24.WAV", "AUDIO\\BUST_25.WAV", "AUDIO\\BUST_26.WAV", "AUDIO\\BUST_27.WAV", "AUDIO\\BUST_28.WAV", }; ================================================ FILE: src/audio/sampman_miles.cpp ================================================ #include "common.h" #ifdef AUDIO_MSS #include #include #include #include "eax.h" #include "eax-util.h" #include "mss.h" #include "sampman.h" #include "AudioManager.h" #include "MusicManager.h" #include "Frontend.h" #include "Timer.h" #include "crossplatform.h" #pragma comment( lib, "mss32.lib" ) cSampleManager SampleManager; uint32 BankStartOffset[MAX_SFX_BANKS]; /////////////////////////////////////////////////////////////// char SampleBankDescFilename[] = "AUDIO\\SFX.SDT"; char SampleBankDataFilename[] = "AUDIO\\SFX.RAW"; FILE *fpSampleDescHandle; FILE *fpSampleDataHandle; bool bSampleBankLoaded [MAX_SFX_BANKS]; int32 nSampleBankDiscStartOffset [MAX_SFX_BANKS]; int32 nSampleBankSize [MAX_SFX_BANKS]; int32 nSampleBankMemoryStartAddress[MAX_SFX_BANKS]; int32 _nSampleDataEndOffset; int32 nPedSlotSfx [MAX_PEDSFX]; int32 nPedSlotSfxAddr[MAX_PEDSFX]; uint8 nCurrentPedSlot; uint8 nChannelVolume[MAXCHANNELS+MAX2DCHANNELS]; uint32 nStreamLength[TOTAL_STREAMED_SOUNDS]; /////////////////////////////////////////////////////////////// struct tMP3Entry { char aFilename[MAX_PATH]; uint32 nTrackLength; uint32 nTrackStreamPos; tMP3Entry *pNext; char *pLinkPath; }; uint32 nNumMP3s; tMP3Entry *_pMP3List; char _mp3DirectoryPath[MAX_PATH]; HSTREAM mp3Stream [MAX_STREAMS]; int8 nStreamPan [MAX_STREAMS]; int8 nStreamVolume[MAX_STREAMS]; uint8 nStreamLoopedFlag[MAX_STREAMS]; uint32 _CurMP3Index; int32 _CurMP3Pos; bool _bIsMp3Active; /////////////////////////////////////////////////////////////// bool _bSampmanInitialised = false; // // Miscellaneous globals / defines // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS EAXLISTENERPROPERTIES StartEAX3 = {26, 1.7f, 0.8f, -1000, -1000, -100, 4.42f, 0.14f, 1.00f, 429, 0.014f, 0.00f,0.00f,0.00f, 1023, 0.021f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2727.1f, 250.0f, 0.00f, 0x3f }; EAXLISTENERPROPERTIES FinishEAX3 = {26, 100.0f, 1.0f, 0, -1000, -2200, 20.0f, 1.39f, 1.00f, 1000, 0.069f, 0.00f,0.00f,0.00f, 400, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 3.982f, 0.000f, -18.0f, 3530.8f, 417.9f, 6.70f, 0x3f }; EAXLISTENERPROPERTIES EAX3Params; S32 prevprovider=-1; S32 curprovider=-1; S32 usingEAX=0; S32 usingEAX3=0; HPROVIDER opened_provider=0; H3DSAMPLE opened_samples[MAXCHANNELS] = {0}; HSAMPLE opened_2dsamples[MAX2DCHANNELS] = {0}; HDIGDRIVER DIG; S32 speaker_type=0; U32 _maxSamples; float _fPrevEaxRatioDestination; bool _usingMilesFast2D; float _fEffectsLevel; struct { HPROVIDER id; char name[80]; }providers[MAXPROVIDERS]; typedef struct provider_stuff { char* name; HPROVIDER id; } provider_stuff; static int __cdecl comp(const provider_stuff*s1,const provider_stuff*s2) { return( _stricmp(s1->name,s2->name) ); } static void add_providers() { provider_stuff pi[MAXPROVIDERS]; U32 n,i,j; SampleManager.SetNum3DProvidersAvailable(0); HPROENUM next = HPROENUM_FIRST; n=0; while (AIL_enumerate_3D_providers(&next, &pi[n].id, &pi[n].name) && (n MAXCHANNELS ) _maxSamples = MAXCHANNELS; SampleManager.SetSpeakerConfig(speaker_type); //obtain a 3D sample handles for ( U32 i = 0; i < _maxSamples; ++i ) { opened_samples[i] = AIL_allocate_3D_sample_handle(opened_provider); if ( opened_samples[i] != NULL ) AIL_set_3D_sample_effects_level(opened_samples[i], 0.0f); } return true; } } return false; } U32 RadioHandlers[9]; U32 WINAPI vfs_open_callback(char const* Filename, U32* FileHandle) { *FileHandle = (U32)fopen(Filename, "rb"); // couldn't they just use stricmp once? and strlen? this is very inefficient if ((strcmp(Filename + strlen(Filename) - 4, ".adf") == 0) || (strcmp(Filename + strlen(Filename) - 4, ".ADF") == 0)) { for (int i = 0; i < ARRAY_SIZE(RadioHandlers); i++) { if (RadioHandlers[i] == NULL) { RadioHandlers[i] = *FileHandle; break; } } strcpy((char*)Filename + strlen(Filename) - 4, ".mp3"); } return *FileHandle; } void WINAPI vfs_close_callback(U32 FileHandle) { for (int i = 0; i < ARRAY_SIZE(RadioHandlers); i++) { if (RadioHandlers[i] == FileHandle) { RadioHandlers[i] = NULL; break; } } fclose((FILE*)FileHandle); } S32 WINAPI vfs_seek_callback(U32 FileHandle, S32 Offset, U32 Type) { fseek((FILE*)FileHandle, Offset, Type); return ftell((FILE*)FileHandle); } U32 WINAPI vfs_read_callback(U32 FileHandle, void* Buffer, U32 Bytes) { fread(Buffer, Bytes, 1, (FILE*)FileHandle); uint8* _Buffer = (uint8*)Buffer; for (int i = 0; i < ARRAY_SIZE(RadioHandlers); i++) { if (FileHandle == RadioHandlers[i]) { for (U32 k = 0; k < Bytes; k++) _Buffer[k] ^= 0x22; break; } } return Bytes; } cSampleManager::cSampleManager(void) : m_nNumberOfProviders(0) { ; AIL_set_file_callbacks(vfs_open_callback, vfs_close_callback, vfs_seek_callback, vfs_read_callback); } cSampleManager::~cSampleManager(void) { } void cSampleManager::SetSpeakerConfig(int32 which) { switch ( which ) { case 1: speaker_type=AIL_3D_2_SPEAKER; break; case 2: speaker_type=AIL_3D_HEADPHONE; break; case 3: speaker_type=AIL_3D_4_SPEAKER; break; default: return; break; } if (opened_provider) AIL_set_3D_speaker_type(opened_provider, speaker_type); } uint32 cSampleManager::GetMaximumSupportedChannels(void) { if ( _maxSamples > MAXCHANNELS ) return MAXCHANNELS; return _maxSamples; } uint32 cSampleManager::GetNum3DProvidersAvailable() { return m_nNumberOfProviders; } void cSampleManager::SetNum3DProvidersAvailable(uint32 num) { m_nNumberOfProviders = num; } char *cSampleManager::Get3DProviderName(uint8 id) { return m_aAudioProviders[id]; } void cSampleManager::Set3DProviderName(uint8 id, char *name) { m_aAudioProviders[id] = name; } int8 cSampleManager::GetCurrent3DProviderIndex(void) { return curprovider; } int8 cSampleManager::SetCurrent3DProvider(uint8 nProvider) { S32 savedprovider = curprovider; if ( nProvider < m_nNumberOfProviders ) { if ( set_new_provider(nProvider) ) return curprovider; else if ( savedprovider != -1 && savedprovider < m_nNumberOfProviders && set_new_provider(savedprovider) ) return curprovider; else return -1; } else return curprovider; } int8 cSampleManager::AutoDetect3DProviders() { if (!AudioManager.IsAudioInitialised()) return -1; int eax = -1, eax2 = -1, eax3 = -1, ds3dh = -1, ds3ds = -1; for (uint32 i = 0; i < GetNum3DProvidersAvailable(); i++) { char* providername = Get3DProviderName(i); if (!strcasecmp(providername, "CREATIVE LABS EAX (TM)")) { AudioManager.SetCurrent3DProvider(i); if (GetCurrent3DProviderIndex() == i) eax = i; } if (!strcasecmp(providername, "CREATIVE LABS EAX 2 (TM)")) { AudioManager.SetCurrent3DProvider(i); if (GetCurrent3DProviderIndex() == i) eax2 = i; } if (!strcasecmp(providername, "CREATIVE LABS EAX 3 (TM)")) { AudioManager.SetCurrent3DProvider(i); if (GetCurrent3DProviderIndex() == i) { eax3 = i; } } if (!strcasecmp(providername, "DIRECTSOUND3D HARDWARE SUPPORT")) { AudioManager.SetCurrent3DProvider(i); if (GetCurrent3DProviderIndex() == i) ds3dh = i; } if (!strcasecmp(providername, "DIRECTSOUND3D SOFTWARE EMULATION")) { AudioManager.SetCurrent3DProvider(i); if (GetCurrent3DProviderIndex() == i) ds3ds = i; } } if (eax3 != -1) return eax3; if (eax2 != -1) return eax2; if (eax != -1) return eax; if (ds3dh != -1) return ds3dh; if (ds3ds != -1) return ds3ds; return -1; } static bool _ResolveLink(char const *path, char *out) { IShellLink* psl; WIN32_FIND_DATA fd; char filepath[MAX_PATH]; CoInitialize(NULL); if (SUCCEEDED( CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl ) )) { IPersistFile *ppf; if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf))) { WCHAR wpath[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, MAX_PATH); if (SUCCEEDED(ppf->Load(wpath, STGM_READ))) { /* Resolve the link */ if (SUCCEEDED(psl->Resolve(NULL, SLR_ANY_MATCH|SLR_NO_UI|SLR_NOSEARCH))) { strcpy(filepath, path); if (SUCCEEDED(psl->GetPath(filepath, MAX_PATH, &fd, SLGP_UNCPRIORITY))) { OutputDebugString(fd.cFileName); strcpy(out, filepath); // FIX: Release the objects. Taken from SA. #ifdef FIX_BUGS ppf->Release(); psl->Release(); #endif return true; } } } ppf->Release(); } psl->Release(); } return false; } static void _FindMP3s(void) { tMP3Entry *pList; bool bShortcut; bool bInitFirstEntry; HANDLE hFind; char path[MAX_PATH]; char filepath[MAX_PATH*2]; S32 total_ms; WIN32_FIND_DATA fd; if ( GetCurrentDirectory(MAX_PATH, _mp3DirectoryPath) == 0 ) { GetLastError(); return; } OutputDebugString("Finding MP3s..."); strcpy(path, _mp3DirectoryPath); strcat(path, "\\MP3\\"); strcpy(_mp3DirectoryPath, path); OutputDebugString(_mp3DirectoryPath); strcat(path, "*"); hFind = FindFirstFile(path, &fd); if ( hFind == INVALID_HANDLE_VALUE ) { GetLastError(); return; } strcpy(filepath, _mp3DirectoryPath); strcat(filepath, fd.cFileName); int32 filepathlen = strlen(filepath); if ( filepathlen <= 0) { FindClose(hFind); return; } if ( filepathlen > 4 ) { if ( !strcmp(&filepath[filepathlen - 4], ".lnk") ) { if ( _ResolveLink(filepath, filepath) ) { OutputDebugString("Resolving Link"); OutputDebugString(filepath); } bShortcut = true; } else bShortcut = false; } mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); if ( mp3Stream[0] ) { AIL_stream_ms_position(mp3Stream[0], &total_ms, NULL); AIL_close_stream(mp3Stream[0]); mp3Stream[0] = NULL; OutputDebugString(fd.cFileName); _pMP3List = new tMP3Entry; if ( _pMP3List == NULL ) { FindClose(hFind); return; } nNumMP3s = 1; strcpy(_pMP3List->aFilename, fd.cFileName); _pMP3List->nTrackLength = total_ms; _pMP3List->pNext = NULL; pList = _pMP3List; if ( bShortcut ) { _pMP3List->pLinkPath = new char[MAX_PATH*2]; strcpy(_pMP3List->pLinkPath, filepath); } else { _pMP3List->pLinkPath = NULL; } bInitFirstEntry = false; } else { strcat(filepath, " - NOT A VALID MP3"); OutputDebugString(filepath); bInitFirstEntry = true; } while ( true ) { if ( !FindNextFile(hFind, &fd) ) break; if ( bInitFirstEntry ) { strcpy(filepath, _mp3DirectoryPath); strcat(filepath, fd.cFileName); int32 filepathlen = strlen(filepath); if ( filepathlen > 0 ) { if ( filepathlen > 4 ) { if ( !strcmp(&filepath[filepathlen - 4], ".lnk") ) { if ( _ResolveLink(filepath, filepath) ) { OutputDebugString("Resolving Link"); OutputDebugString(filepath); } bShortcut = true; } else { bShortcut = false; if ( filepathlen > MAX_PATH ) { continue; } } } mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); if ( mp3Stream[0] ) { AIL_stream_ms_position(mp3Stream[0], &total_ms, NULL); AIL_close_stream(mp3Stream[0]); mp3Stream[0] = NULL; OutputDebugString(fd.cFileName); _pMP3List = new tMP3Entry; if ( _pMP3List == NULL) break; nNumMP3s = 1; strcpy(_pMP3List->aFilename, fd.cFileName); _pMP3List->nTrackLength = total_ms; _pMP3List->pNext = NULL; if ( bShortcut ) { _pMP3List->pLinkPath = new char [MAX_PATH*2]; strcpy(_pMP3List->pLinkPath, filepath); } else { _pMP3List->pLinkPath = NULL; } pList = _pMP3List; bInitFirstEntry = false; } else { strcat(filepath, " - NOT A VALID MP3"); OutputDebugString(filepath); } } } else { strcpy(filepath, _mp3DirectoryPath); strcat(filepath, fd.cFileName); int32 filepathlen = strlen(filepath); if ( filepathlen > 0 ) { if ( filepathlen > 4 ) { if ( !strcmp(&filepath[filepathlen - 4], ".lnk") ) { if ( _ResolveLink(filepath, filepath) ) { OutputDebugString("Resolving Link"); OutputDebugString(filepath); } bShortcut = true; } else { bShortcut = false; } } mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); if ( mp3Stream[0] ) { AIL_stream_ms_position(mp3Stream[0], &total_ms, NULL); AIL_close_stream(mp3Stream[0]); mp3Stream[0] = NULL; pList->pNext = new tMP3Entry; tMP3Entry *e = pList->pNext; if ( e == NULL ) break; pList = pList->pNext; strcpy(e->aFilename, fd.cFileName); e->nTrackLength = total_ms; e->pNext = NULL; if ( bShortcut ) { e->pLinkPath = new char [MAX_PATH*2]; strcpy(e->pLinkPath, filepath); } else { e->pLinkPath = NULL; } nNumMP3s++; OutputDebugString(fd.cFileName); } else { strcat(filepath, " - NOT A VALID MP3"); OutputDebugString(filepath); } } } } FindClose(hFind); } static void _DeleteMP3Entries(void) { tMP3Entry *e = _pMP3List; while ( e != NULL ) { tMP3Entry *next = e->pNext; if ( next == NULL ) next = NULL; if ( e->pLinkPath != NULL ) { #ifndef FIX_BUGS delete e->pLinkPath; // BUG: should be delete [] #else delete[] e->pLinkPath; #endif e->pLinkPath = NULL; } delete e; if ( next ) e = next; else e = NULL; nNumMP3s--; } if ( nNumMP3s != 0 ) { OutputDebugString("Not all MP3 entries were deleted"); nNumMP3s = 0; } _pMP3List = NULL; } static tMP3Entry * _GetMP3EntryByIndex(uint32 idx) { uint32 n = ( idx < nNumMP3s ) ? idx : 0; if ( _pMP3List != NULL ) { tMP3Entry *e = _pMP3List; for ( uint32 i = 0; i < n; i++ ) e = e->pNext; return e; } return NULL; } static inline bool _GetMP3PosFromStreamPos(uint32 *pPosition, tMP3Entry **pEntry) { _CurMP3Index = 0; for ( *pEntry = _pMP3List; *pEntry != NULL; *pEntry = (*pEntry)->pNext ) { if ( *pPosition >= (*pEntry)->nTrackStreamPos && *pPosition < (*pEntry)->nTrackLength + (*pEntry)->nTrackStreamPos ) { *pPosition -= (*pEntry)->nTrackStreamPos; _CurMP3Pos = *pPosition; return true; } _CurMP3Index++; } *pPosition = 0; *pEntry = _pMP3List; _CurMP3Pos = 0; _CurMP3Index = 0; return false; } bool cSampleManager::IsMP3RadioChannelAvailable(void) { return nNumMP3s != 0; } void cSampleManager::ReleaseDigitalHandle(void) { if ( DIG ) { prevprovider = curprovider; release_existing(); curprovider = -1; AIL_digital_handle_release(DIG); } } void cSampleManager::ReacquireDigitalHandle(void) { if ( DIG ) { AIL_digital_handle_reacquire(DIG); if ( prevprovider != -1 ) set_new_provider(prevprovider); } } bool cSampleManager::Initialise(void) { TRACE("start"); if ( _bSampmanInitialised ) return true; { for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ ) { m_aSamples[i].nOffset = 0; m_aSamples[i].nSize = 0; m_aSamples[i].nFrequency = 22050; m_aSamples[i].nLoopStart = 0; m_aSamples[i].nLoopEnd = -1; } m_nEffectsVolume = MAX_VOLUME; m_nMusicVolume = MAX_VOLUME; m_nEffectsFadeVolume = MAX_VOLUME; m_nMusicFadeVolume = MAX_VOLUME; m_nMonoMode = 0; } // miles TRACE("MILES"); { curprovider = -1; prevprovider = -1; _usingMilesFast2D = false; usingEAX=0; usingEAX3=0; _fEffectsLevel = 0.0f; _maxSamples = 0; opened_provider = NULL; DIG = NULL; for ( int32 i = 0; i < MAXCHANNELS; i++ ) opened_samples[i] = NULL; } // banks TRACE("banks"); { fpSampleDescHandle = NULL; fpSampleDataHandle = NULL; _nSampleDataEndOffset = 0; for ( int32 i = 0; i < MAX_SFX_BANKS; i++ ) { bSampleBankLoaded[i] = false; nSampleBankDiscStartOffset[i] = 0; nSampleBankSize[i] = 0; nSampleBankMemoryStartAddress[i] = 0; } } // pedsfx TRACE("pedsfx"); { for ( int32 i = 0; i < MAX_PEDSFX; i++ ) { nPedSlotSfx[i] = NO_SAMPLE; nPedSlotSfxAddr[i] = 0; } nCurrentPedSlot = 0; } // channel volume TRACE("vol"); { for ( int32 i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++ ) nChannelVolume[i] = 0; } TRACE("mss"); { AIL_set_redist_directory( "mss" ); AIL_startup(); AIL_set_preference(DIG_MIXER_CHANNELS, MAX_DIGITAL_MIXER_CHANNELS); DIG = AIL_open_digital_driver(DIGITALRATE, DIGITALBITS, DIGITALCHANNELS, 0); } #ifdef AUDIO_CACHE TRACE("cache"); FILE *cacheFile = fcaseopen("audio\\sound.cache", "rb"); bool CreateCache = false; if (cacheFile) { fread(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile); fclose(cacheFile); }else CreateCache = true; #endif char filepath[MAX_PATH]; bool bFileNotFound; S32 tatalms; TRACE("cdrom"); { m_bInitialised = false; while (true) { // Find path of WAVs (originally in HDD) int32 drive = 'C'; #ifndef NO_CDCHECK do { char latter[2]; latter[0] = drive; latter[1] = '\0'; strcpy(m_szCDRomRootPath, latter); strcat(m_szCDRomRootPath, ":\\"); if ( GetDriveType(m_szCDRomRootPath) == DRIVE_CDROM ) { strcpy(filepath, m_szCDRomRootPath); strcat(filepath, StreamedNameTable[0]); FILE *f = fopen(filepath, "rb"); if ( f ) { fclose(f); strcpy(m_MiscomPath, m_szCDRomRootPath); break; } } } while ( ++drive <= 'Z' ); #else m_MiscomPath[0] = '\0'; #endif if ( DIG == NULL ) { OutputDebugString(AIL_last_error()); Terminate(); return false; } add_providers(); m_szCDRomRootPath[0] = '\0'; strcpy(m_WavFilesPath, m_szCDRomRootPath); #ifdef AUDIO_CACHE if ( CreateCache ) #endif for ( int32 i = STREAMED_SOUND_MISSION_MOBR1; i < TOTAL_STREAMED_SOUNDS; i++ ) { strcpy(filepath, m_szCDRomRootPath); strcat(filepath, StreamedNameTable[i]); mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); if ( mp3Stream[0] ) { AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL); AIL_close_stream(mp3Stream[0]); mp3Stream[0] = NULL; nStreamLength[i] = tatalms; } else { m_bInitialised = false; Terminate(); return false; } } // Find path of MP3s (originally in CD-Rom) // if NO_CDCHECK is NOT defined but AUDIO_CACHE is defined, we still need to find MP3s' path, but will exit after the first file #ifndef NO_CDCHECK int32 drive = 'C'; do { latter[0] = drive; latter[1] = '\0'; strcpy(m_szCDRomRootPath, latter); strcat(m_szCDRomRootPath, ":"); strcat(m_MP3FilesPath, m_szCDRomRootPath); #else m_MP3FilesPath[0] = '\0'; { #endif for (int32 i = 0; i < STREAMED_SOUND_MISSION_MOBR1; i++) { strcpy(filepath, m_MP3FilesPath); strcat(filepath, StreamedNameTable[i]); mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); if (mp3Stream[0]) { AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL); AIL_close_stream(mp3Stream[0]); mp3Stream[0] = NULL; bFileNotFound = false; #ifdef AUDIO_CACHE if (!CreateCache) break; else #endif nStreamLength[i] = tatalms; } else { bFileNotFound = true; break; } } #ifndef NO_CDCHECK if (!bFileNotFound) // otherwise try next drive break; } while (++drive <= 'Z'); #else } #endif if ( !bFileNotFound ) { #ifdef AUDIO_CACHE if ( CreateCache ) #endif for ( int32 i = STREAMED_SOUND_MISSION_COMPLETED4; i < STREAMED_SOUND_MISSION_PAGER; i++ ) { strcpy(filepath, m_MiscomPath); strcat(filepath, StreamedNameTable[i]); mp3Stream[0] = AIL_open_stream(DIG, filepath, 0); if ( mp3Stream[0] ) { AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL); AIL_close_stream(mp3Stream[0]); mp3Stream[0] = NULL; nStreamLength[i] = tatalms; bFileNotFound = false; } else { bFileNotFound = true; break; } } } m_bInitialised = !bFileNotFound; if ( !m_bInitialised ) { #if !defined(GTA3_STEAM_PATCH) && !defined(NO_CDCHECK) FrontEndMenuManager.WaitForUserCD(); if ( FrontEndMenuManager.m_bQuitGameNoCD ) { Terminate(); return false; } continue; #else m_bInitialised = true; #endif } break; } } #ifdef AUDIO_CACHE if (CreateCache) { cacheFile = fcaseopen("audio\\sound.cache", "wb"); fwrite(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile); fclose(cacheFile); } #endif if ( !InitialiseSampleBanks() ) { Terminate(); return false; } nSampleBankMemoryStartAddress[SFX_BANK_0] = (int32)AIL_mem_alloc_lock(nSampleBankSize[SFX_BANK_0]); if ( !nSampleBankMemoryStartAddress[SFX_BANK_0] ) { Terminate(); return false; } nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = (int32)AIL_mem_alloc_lock(PED_BLOCKSIZE*MAX_PEDSFX); LoadSampleBank(SFX_BANK_0); TRACE("stream"); { for ( int32 i = 0; i < MAX_STREAMS; i++ ) { mp3Stream [i] = NULL; nStreamPan [i] = 63; nStreamVolume[i] = 100; } } for ( int32 i = 0; i < MAX2DCHANNELS; i++ ) { opened_2dsamples[i] = AIL_allocate_sample_handle(DIG); if ( opened_2dsamples[i] ) { AIL_init_sample(opened_2dsamples[i]); AIL_set_sample_type(opened_2dsamples[i], DIG_F_MONO_16, DIG_PCM_SIGN); } } TRACE("providerset"); { _bSampmanInitialised = true; U32 n = 0; while ( n < m_nNumberOfProviders ) { if ( !strcmp(strupr(providers[n].name), "DIRECTSOUND3D SOFTWARE EMULATION") ) { set_new_provider(n); break; } n++; } if ( n == m_nNumberOfProviders ) { Terminate(); return false; } } // mp3 TRACE("mp3"); { nNumMP3s = 0; _pMP3List = NULL; _FindMP3s(); if ( nNumMP3s != 0 ) { nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] = 0; for ( tMP3Entry *e = _pMP3List; e != NULL; e = e->pNext ) { e->nTrackStreamPos = nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER]; nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] += e->nTrackLength; } time_t t = time(NULL); tm *localtm; bool bUseRandomTable; if ( t == -1 ) bUseRandomTable = true; else { bUseRandomTable = false; localtm = localtime(&t); } int32 randval; if ( bUseRandomTable ) randval = AudioManager.GetRandomNumber(1); else randval = localtm->tm_sec * localtm->tm_min; _CurMP3Index = randval % nNumMP3s; tMP3Entry *randmp3 = _pMP3List; for ( int32 i = randval % nNumMP3s; i > 0; --i) randmp3 = randmp3->pNext; if ( bUseRandomTable ) _CurMP3Pos = AudioManager.GetRandomNumber(0) % randmp3->nTrackLength; else { if ( localtm->tm_sec > 0 ) { int32 s = localtm->tm_sec; _CurMP3Pos = s*s*s*s*s*s*s*s % randmp3->nTrackLength; } else _CurMP3Pos = AudioManager.GetRandomNumber(0) % randmp3->nTrackLength; } } else _CurMP3Pos = 0; _bIsMp3Active = false; } TRACE("end"); return true; } void cSampleManager::Terminate(void) { for ( int32 i = 0; i < MAX_STREAMS; i++ ) { if ( mp3Stream[i] ) { AIL_pause_stream(mp3Stream[i], 1); AIL_close_stream(mp3Stream[i]); mp3Stream[i] = NULL; } } for ( int32 i = 0; i < MAX2DCHANNELS; i++ ) { if ( opened_2dsamples[i] ) { AIL_release_sample_handle(opened_2dsamples[i]); opened_2dsamples[i] = NULL; } } release_existing(); _DeleteMP3Entries(); if ( nSampleBankMemoryStartAddress[SFX_BANK_0] != 0 ) { AIL_mem_free_lock((void *)nSampleBankMemoryStartAddress[SFX_BANK_0]); nSampleBankMemoryStartAddress[SFX_BANK_0] = 0; } if ( nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] != 0 ) { AIL_mem_free_lock((void *)nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS]); nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = 0; } if ( DIG ) { AIL_close_digital_driver(DIG); DIG = NULL; } AIL_shutdown(); _bSampmanInitialised = false; } bool cSampleManager::CheckForAnAudioFileOnCD(void) { #if !defined(NO_CDCHECK) // TODO: check steam, probably GTAVC_STEAM_PATCH needs to be added char filepath[MAX_PATH]; strcpy(filepath, m_MiscomPath); strcat(filepath, StreamedNameTable[STREAMED_SOUND_MISSION_COMPLETED4]); FILE *f = fopen(filepath, "rb"); if ( f ) { fclose(f); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); DMAudio.Service(); return true; } DMAudio.SetMusicMasterVolume(0); DMAudio.SetEffectsMasterVolume(0); DMAudio.Service(); return false; #else return true; #endif // #if !defined(NO_CDCHECK) } char cSampleManager::GetCDAudioDriveLetter(void) { if ( strlen(m_MiscomPath) != 0 ) return m_MiscomPath[0]; else return '\0'; } void cSampleManager::UpdateEffectsVolume(void) //[Y], cSampleManager::UpdateSoundBuffers ? { if ( _bSampmanInitialised ) { for ( int32 i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++ ) { if ( i < MAXCHANNELS ) { if ( opened_samples[i] && GetChannelUsedFlag(i) ) { if ( nChannelVolume[i] ) { AIL_set_3D_sample_volume(opened_samples[i], m_nEffectsFadeVolume * nChannelVolume[i] * m_nEffectsVolume >> 14); } } } else { if ( opened_2dsamples[i - MAXCHANNELS] ) { if ( GetChannelUsedFlag(i - MAXCHANNELS) ) { if ( nChannelVolume[i - MAXCHANNELS] ) { AIL_set_sample_volume(opened_2dsamples[i - MAXCHANNELS], m_nEffectsFadeVolume * nChannelVolume[i - MAXCHANNELS] * m_nEffectsVolume >> 14); } } } } } } } void cSampleManager::SetEffectsMasterVolume(uint8 nVolume) { m_nEffectsVolume = nVolume; UpdateEffectsVolume(); } void cSampleManager::SetMusicMasterVolume(uint8 nVolume) { m_nMusicVolume = nVolume; } void cSampleManager::SetMP3BoostVolume(uint8 nVolume) { m_nMP3BoostVolume = nVolume; } void cSampleManager::SetEffectsFadeVolume(uint8 nVolume) { m_nEffectsFadeVolume = nVolume; UpdateEffectsVolume(); } void cSampleManager::SetMusicFadeVolume(uint8 nVolume) { m_nMusicFadeVolume = nVolume; } void cSampleManager::SetMonoMode(uint8 nMode) { m_nMonoMode = nMode; } bool cSampleManager::LoadSampleBank(uint8 nBank) { if ( CTimer::GetIsCodePaused() ) return false; if ( MusicManager.IsInitialised() && MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE && nBank != SFX_BANK_0 ) { return false; } if ( fseek(fpSampleDataHandle, nSampleBankDiscStartOffset[nBank], SEEK_SET) != 0 ) return false; if ( fread((void *)nSampleBankMemoryStartAddress[nBank], 1, nSampleBankSize[nBank],fpSampleDataHandle) != nSampleBankSize[nBank] ) return false; bSampleBankLoaded[nBank] = true; return true; } void cSampleManager::UnloadSampleBank(uint8 nBank) { bSampleBankLoaded[nBank] = false; } bool cSampleManager::IsSampleBankLoaded(uint8 nBank) { return bSampleBankLoaded[nBank]; } bool cSampleManager::IsPedCommentLoaded(uint32 nComment) { int8 slot; for ( int32 i = 0; i < _TODOCONST(3); i++ ) { slot = nCurrentPedSlot - i - 1; #ifdef FIX_BUGS if (slot < 0) slot += ARRAY_SIZE(nPedSlotSfx); #endif if ( nComment == nPedSlotSfx[slot] ) return true; } return false; } int32 cSampleManager::_GetPedCommentSlot(uint32 nComment) { int8 slot; for ( int32 i = 0; i < _TODOCONST(3); i++ ) { slot = nCurrentPedSlot - i - 1; #ifdef FIX_BUGS if (slot < 0) slot += ARRAY_SIZE(nPedSlotSfx); #endif if ( nComment == nPedSlotSfx[slot] ) return slot; } return -1; } bool cSampleManager::LoadPedComment(uint32 nComment) { if ( CTimer::GetIsCodePaused() ) return false; // no talking peds during cutsenes or the game end if ( MusicManager.IsInitialised() ) { switch ( MusicManager.GetMusicMode() ) { case MUSICMODE_CUTSCENE: { return false; break; } } } if ( fseek(fpSampleDataHandle, m_aSamples[nComment].nOffset, SEEK_SET) != 0 ) return false; if ( fread((void *)(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE*nCurrentPedSlot), 1, m_aSamples[nComment].nSize, fpSampleDataHandle) != m_aSamples[nComment].nSize ) return false; nPedSlotSfxAddr[nCurrentPedSlot] = nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE*nCurrentPedSlot; nPedSlotSfx [nCurrentPedSlot] = nComment; if ( ++nCurrentPedSlot >= MAX_PEDSFX ) nCurrentPedSlot = 0; return true; } int32 cSampleManager::GetBankContainingSound(uint32 offset) { if ( offset >= BankStartOffset[SFX_BANK_PED_COMMENTS] ) return SFX_BANK_PED_COMMENTS; if ( offset >= BankStartOffset[SFX_BANK_0] ) return SFX_BANK_0; return INVALID_SFX_BANK; } int32 cSampleManager::GetSampleBaseFrequency(uint32 nSample) { return m_aSamples[nSample].nFrequency; } int32 cSampleManager::GetSampleLoopStartOffset(uint32 nSample) { return m_aSamples[nSample].nLoopStart; } int32 cSampleManager::GetSampleLoopEndOffset(uint32 nSample) { return m_aSamples[nSample].nLoopEnd; } uint32 cSampleManager::GetSampleLength(uint32 nSample) { return m_aSamples[nSample].nSize >> 1; } bool cSampleManager::UpdateReverb(void) { if ( !usingEAX ) return false; if ( AudioManager.GetFrameCounter() & 15 ) return false; float fRatio = 0.0f; #define MIN_DIST 0.5f #define CALCULATE_RATIO(value, maxDist, maxRatio) (value > MIN_DIST && value < maxDist ? value / maxDist * maxRatio : 0) fRatio += CALCULATE_RATIO(AudioManager.GetReflectionsDistance(REFLECTION_CEIL_NORTH), 10.0f, 1/2.f); fRatio += CALCULATE_RATIO(AudioManager.GetReflectionsDistance(REFLECTION_CEIL_SOUTH), 10.0f, 1/2.f); fRatio += CALCULATE_RATIO(AudioManager.GetReflectionsDistance(REFLECTION_CEIL_WEST), 10.0f, 1/2.f); fRatio += CALCULATE_RATIO(AudioManager.GetReflectionsDistance(REFLECTION_CEIL_EAST), 10.0f, 1/2.f); fRatio += CALCULATE_RATIO((AudioManager.GetReflectionsDistance(REFLECTION_NORTH) + AudioManager.GetReflectionsDistance(REFLECTION_SOUTH)) / 2.f, 4.0f, 1/3.f); fRatio += CALCULATE_RATIO((AudioManager.GetReflectionsDistance(REFLECTION_WEST) + AudioManager.GetReflectionsDistance(REFLECTION_EAST)) / 2.f, 4.0f, 1/3.f); #undef CALCULATE_RATIO #undef MIN_DIST fRatio = clamp(fRatio, 0.0f, 0.6f); if ( fRatio == _fPrevEaxRatioDestination ) return false; if ( usingEAX3 ) { fRatio = Min(fRatio * 1.67f, 1.0f); if ( EAX3ListenerInterpolate(&StartEAX3, &FinishEAX3, fRatio, &EAX3Params, false) ) { AIL_set_3D_provider_preference(opened_provider, "EAX all parameters", &EAX3Params); _fEffectsLevel = fRatio * 0.75f; } } else { if ( _usingMilesFast2D ) _fEffectsLevel = fRatio * 0.8f; else _fEffectsLevel = fRatio * 0.22f; } _fEffectsLevel = Min(_fEffectsLevel, 1.0f); _fPrevEaxRatioDestination = fRatio; return true; } void cSampleManager::SetChannelReverbFlag(uint32 nChannel, uint8 nReverbFlag) { bool b2d = false; switch ( nChannel ) { case CHANNEL2D: { b2d = true; break; } } if ( usingEAX ) { if ( nReverbFlag != 0 ) { if ( !b2d ) AIL_set_3D_sample_effects_level(opened_samples[nChannel], _fEffectsLevel); } else { if ( !b2d ) AIL_set_3D_sample_effects_level(opened_samples[nChannel], 0.0f); } } } bool cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank) { bool b2d = false; switch ( nChannel ) { case CHANNEL2D: { b2d = true; break; } } int32 addr; if ( nSfx < SAMPLEBANK_MAX ) { if ( !IsSampleBankLoaded(nBank) ) return false; addr = nSampleBankMemoryStartAddress[nBank] + m_aSamples[nSfx].nOffset - m_aSamples[BankStartOffset[nBank]].nOffset; } else { if ( !IsPedCommentLoaded(nSfx) ) return false; int32 slot = _GetPedCommentSlot(nSfx); addr = nPedSlotSfxAddr[slot]; } if ( b2d ) { if ( opened_2dsamples[nChannel - MAXCHANNELS] ) { AIL_set_sample_address(opened_2dsamples[nChannel - MAXCHANNELS], (void *)addr, m_aSamples[nSfx].nSize); return true; } else return false; } else { AILSOUNDINFO info; info.format = WAVE_FORMAT_PCM; info.data_ptr = (void *)addr; info.channels = 1; info.data_len = m_aSamples[nSfx].nSize; info.rate = m_aSamples[nSfx].nFrequency; info.bits = 16; if ( AIL_set_3D_sample_info(opened_samples[nChannel], &info) == 0 ) { OutputDebugString(AIL_last_error()); return false; } return true; } } void cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume) { uint32 vol = nVolume; if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; nChannelVolume[nChannel] = vol; // increase the volume for JB.MP3 and S4_BDBD.MP3 if (MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE ) { if (MusicManager.GetCurrentTrack() == STREAMED_SOUND_CUTSCENE_FINALE) nChannelVolume[nChannel] = 0; else nChannelVolume[nChannel] >>= 2; } if ( opened_samples[nChannel] ) AIL_set_3D_sample_volume(opened_samples[nChannel], m_nEffectsFadeVolume*nChannelVolume[nChannel]*m_nEffectsVolume >> 14); } void cSampleManager::SetChannel3DPosition(uint32 nChannel, float fX, float fY, float fZ) { if ( opened_samples[nChannel] ) AIL_set_3D_position(opened_samples[nChannel], -fX, fY, fZ); } void cSampleManager::SetChannel3DDistances(uint32 nChannel, float fMax, float fMin) { if ( opened_samples[nChannel] ) AIL_set_3D_sample_distances(opened_samples[nChannel], fMax, fMin); } void cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume) { uint32 vol = nVolume; if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; switch ( nChannel ) { case CHANNEL2D: { nChannelVolume[nChannel] = vol; // increase the volume for JB.MP3 and S4_BDBD.MP3 if ( MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE && MusicManager.GetCurrentTrack() != STREAMED_SOUND_CUTSCENE_FINALE ) { nChannelVolume[nChannel] >>= 2; } if ( opened_2dsamples[nChannel - MAXCHANNELS] ) { AIL_set_sample_volume(opened_2dsamples[nChannel - MAXCHANNELS], m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14); } break; } } } void cSampleManager::SetChannelPan(uint32 nChannel, uint32 nPan) { switch ( nChannel ) { case CHANNEL2D: { #ifndef FIX_BUGS if ( opened_samples[nChannel - MAXCHANNELS] ) // BUG #else if ( opened_2dsamples[nChannel - MAXCHANNELS] ) #endif AIL_set_sample_pan(opened_2dsamples[nChannel - MAXCHANNELS], nPan); break; } } } void cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq) { bool b2d = false; switch ( nChannel ) { case CHANNEL2D: { b2d = true; break; } } if ( b2d ) { if ( opened_2dsamples[nChannel - MAXCHANNELS] ) AIL_set_sample_playback_rate(opened_2dsamples[nChannel - MAXCHANNELS], nFreq); } else { if ( opened_samples[nChannel] ) AIL_set_3D_sample_playback_rate(opened_samples[nChannel], nFreq); } } void cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd) { bool b2d = false; switch ( nChannel ) { case CHANNEL2D: { b2d = true; break; } } if ( b2d ) { if ( opened_2dsamples[nChannel - MAXCHANNELS] ) AIL_set_sample_loop_block(opened_2dsamples[nChannel - MAXCHANNELS], nLoopStart, nLoopEnd); } else { if ( opened_samples[nChannel] ) AIL_set_3D_sample_loop_block(opened_samples[nChannel], nLoopStart, nLoopEnd); } } void cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount) { bool b2d = false; switch ( nChannel ) { case CHANNEL2D: { b2d = true; break; } } if ( b2d ) { if ( opened_2dsamples[nChannel - MAXCHANNELS] ) AIL_set_sample_loop_count(opened_2dsamples[nChannel - MAXCHANNELS], nLoopCount); } else { if ( opened_samples[nChannel] ) AIL_set_3D_sample_loop_count(opened_samples[nChannel], nLoopCount); } } bool cSampleManager::GetChannelUsedFlag(uint32 nChannel) { bool b2d = false; switch ( nChannel ) { case CHANNEL2D: { b2d = true; break; } } if ( b2d ) { if ( opened_2dsamples[nChannel - MAXCHANNELS] ) return AIL_sample_status(opened_2dsamples[nChannel - MAXCHANNELS]) == SMP_PLAYING; else return false; } else { if ( opened_samples[nChannel] ) return AIL_3D_sample_status(opened_samples[nChannel]) == SMP_PLAYING; else return false; } } void cSampleManager::StartChannel(uint32 nChannel) { bool b2d = false; switch ( nChannel ) { case CHANNEL2D: { b2d = true; break; } } if ( b2d ) { if ( opened_2dsamples[nChannel - MAXCHANNELS] ) AIL_start_sample(opened_2dsamples[nChannel - MAXCHANNELS]); } else { if ( opened_samples[nChannel] ) AIL_start_3D_sample(opened_samples[nChannel]); } } void cSampleManager::StopChannel(uint32 nChannel) { bool b2d = false; switch ( nChannel ) { case CHANNEL2D: { b2d = true; break; } } if ( b2d ) { if ( opened_2dsamples[nChannel - MAXCHANNELS] ) AIL_end_sample(opened_2dsamples[nChannel - MAXCHANNELS]); } else { if ( opened_samples[nChannel] ) { if ( AIL_3D_sample_status(opened_samples[nChannel]) == SMP_PLAYING ) AIL_end_3D_sample(opened_samples[nChannel]); } } } void cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream) { if ( m_bInitialised ) { if ( nFile < TOTAL_STREAMED_SOUNDS ) { if ( mp3Stream[nStream] ) { AIL_pause_stream(mp3Stream[nStream], 1); AIL_close_stream(mp3Stream[nStream]); } char filepath[MAX_PATH]; strcpy(filepath, nFile < STREAMED_SOUND_MISSION_COMPLETED4 ? m_MP3FilesPath : (nFile < STREAMED_SOUND_MISSION_MOBR1 ? m_MiscomPath : m_WavFilesPath)); strcat(filepath, StreamedNameTable[nFile]); mp3Stream[nStream] = AIL_open_stream(DIG, filepath, 0); if ( mp3Stream[nStream] ) { AIL_set_stream_loop_count(mp3Stream[nStream], 1); AIL_service_stream(mp3Stream[nStream], 1); } else OutputDebugString(AIL_last_error()); } } } void cSampleManager::PauseStream(uint8 nPauseFlag, uint8 nStream) { if ( m_bInitialised ) { if ( mp3Stream[nStream] ) AIL_pause_stream(mp3Stream[nStream], nPauseFlag != 0); } } void cSampleManager::StartPreloadedStreamedFile(uint8 nStream) { if ( m_bInitialised ) { if ( mp3Stream[nStream] ) AIL_start_stream(mp3Stream[nStream]); } } bool cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) { int i = 0; uint32 position = nPos; char filename[MAX_PATH]; if ( !m_bInitialised || nFile >= TOTAL_STREAMED_SOUNDS ) return false; if ( mp3Stream[nStream] ) { AIL_pause_stream(mp3Stream[nStream], 1); AIL_close_stream(mp3Stream[nStream]); } if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER ) { do { // Just switched to MP3 player if ( !_bIsMp3Active && i == 0 ) { if ( nPos > nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] ) position = 0; tMP3Entry *e = _pMP3List; // Try to continue from previous song, if already started if(!_GetMP3PosFromStreamPos(&position, &e) && !e) { nFile = 0; strcpy(filename, m_MiscomPath); strcat(filename, StreamedNameTable[nFile]); mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); if(mp3Stream[nStream]) { AIL_set_stream_loop_count(mp3Stream[nStream], nStreamLoopedFlag[nStream] ? 0 : 1); nStreamLoopedFlag[nStream] = true; AIL_set_stream_ms_position(mp3Stream[nStream], position); AIL_pause_stream(mp3Stream[nStream], 0); return true; } return false; } else { if ( e->pLinkPath != NULL ) mp3Stream[nStream] = AIL_open_stream(DIG, e->pLinkPath, 0); else { strcpy(filename, _mp3DirectoryPath); strcat(filename, e->aFilename); mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); } if ( mp3Stream[nStream] ) { AIL_set_stream_loop_count(mp3Stream[nStream], 1); AIL_set_stream_ms_position(mp3Stream[nStream], position); AIL_pause_stream(mp3Stream[nStream], 0); _bIsMp3Active = true; return true; } // fall through, start playing from another song } } else { if(++_CurMP3Index >= nNumMP3s) _CurMP3Index = 0; _CurMP3Pos = 0; tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index); if ( !mp3 ) { mp3 = _pMP3List; if ( !_pMP3List ) { nFile = 0; _bIsMp3Active = 0; strcpy(filename, m_MiscomPath); strcat(filename, StreamedNameTable[nFile]); mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); if(mp3Stream[nStream]) { AIL_set_stream_loop_count( mp3Stream[nStream], nStreamLoopedFlag[nStream] ? 0 : 1); nStreamLoopedFlag[nStream] = true; AIL_set_stream_ms_position( mp3Stream[nStream], position); AIL_pause_stream(mp3Stream[nStream], 0); return true; } return false; } } if(mp3->pLinkPath != NULL) mp3Stream[nStream] = AIL_open_stream(DIG, mp3->pLinkPath, 0); else { strcpy(filename, _mp3DirectoryPath); strcat(filename, mp3->aFilename); mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); } if(mp3Stream[nStream]) { AIL_set_stream_loop_count(mp3Stream[nStream], 1); AIL_set_stream_ms_position(mp3Stream[nStream], 0); AIL_pause_stream(mp3Stream[nStream], 0); #ifdef FIX_BUGS _bIsMp3Active = true; #endif return true; } } _bIsMp3Active = 0; } while ( ++i < nNumMP3s ); position = 0; nFile = 0; } strcpy(filename, m_MiscomPath); strcat(filename, StreamedNameTable[nFile]); mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0); if ( mp3Stream[nStream] ) { AIL_set_stream_loop_count(mp3Stream[nStream], nStreamLoopedFlag[nStream] ? 0 : 1); nStreamLoopedFlag[nStream] = true; AIL_set_stream_ms_position(mp3Stream[nStream], position); AIL_pause_stream(mp3Stream[nStream], 0); return true; } return false; } void cSampleManager::StopStreamedFile(uint8 nStream) { if ( m_bInitialised ) { if ( mp3Stream[nStream] ) { AIL_pause_stream(mp3Stream[nStream], 1); AIL_close_stream(mp3Stream[nStream]); mp3Stream[nStream] = NULL; if ( nStream == 0 ) _bIsMp3Active = false; } } } int32 cSampleManager::GetStreamedFilePosition(uint8 nStream) { S32 currentms; if ( m_bInitialised ) { if ( mp3Stream[nStream] ) { if ( _bIsMp3Active ) { tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index); if ( mp3 != NULL ) { AIL_stream_ms_position(mp3Stream[nStream], NULL, ¤tms); return currentms + mp3->nTrackStreamPos; } else return 0; } else { AIL_stream_ms_position(mp3Stream[nStream], NULL, ¤tms); return currentms; } } } return 0; } void cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffectFlag, uint8 nStream) { uint8 vol = nVolume; float boostMult = 0.0f; if ( m_bInitialised ) { if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; if ( MusicManager.GetRadioInCar() == USERTRACK && !MusicManager.CheckForMusicInterruptions() ) boostMult = m_nMP3BoostVolume / 64.f; nStreamVolume[nStream] = vol; nStreamPan[nStream] = nPan; if ( mp3Stream[nStream] ) { if ( nEffectFlag ) { if ( nStream == 1 || nStream == 2 ) AIL_set_stream_volume(mp3Stream[nStream], 128*vol*m_nEffectsVolume >> 14); else AIL_set_stream_volume(mp3Stream[nStream], m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14); } else AIL_set_stream_volume(mp3Stream[nStream], (m_nMusicFadeVolume*vol*(uint32)(m_nMusicVolume * boostMult + m_nMusicVolume)) >> 14); AIL_set_stream_pan(mp3Stream[nStream], nPan); } } } int32 cSampleManager::GetStreamedFileLength(uint8 nStream) { if ( m_bInitialised ) return nStreamLength[nStream]; return 0; } bool cSampleManager::IsStreamPlaying(uint8 nStream) { if ( m_bInitialised ) { if ( mp3Stream[nStream] ) { if ( AIL_stream_status(mp3Stream[nStream]) == SMP_PLAYING ) return true; else return false; } } return false; } bool cSampleManager::InitialiseSampleBanks(void) { int32 nBank = SFX_BANK_0; fpSampleDescHandle = fopen(SampleBankDescFilename, "rb"); if ( fpSampleDescHandle == NULL ) return false; fpSampleDataHandle = fopen(SampleBankDataFilename, "rb"); if ( fpSampleDataHandle == NULL ) { fclose(fpSampleDescHandle); fpSampleDescHandle = NULL; return false; } fseek(fpSampleDataHandle, 0, SEEK_END); _nSampleDataEndOffset = ftell(fpSampleDataHandle); rewind(fpSampleDataHandle); fread(m_aSamples, sizeof(tSample), TOTAL_AUDIO_SAMPLES, fpSampleDescHandle); fclose(fpSampleDescHandle); fpSampleDescHandle = NULL; for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ ) { #ifdef FIX_BUGS if (nBank >= MAX_SFX_BANKS) break; #endif if ( BankStartOffset[nBank] == BankStartOffset[SFX_BANK_0] + i ) { nSampleBankDiscStartOffset[nBank] = m_aSamples[i].nOffset; nBank++; } } nSampleBankSize[SFX_BANK_0] = nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS] - nSampleBankDiscStartOffset[SFX_BANK_0]; nSampleBankSize[SFX_BANK_PED_COMMENTS] = _nSampleDataEndOffset - nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS]; return true; } void cSampleManager::SetStreamedFileLoopFlag(uint8 nLoopFlag, uint8 nChannel) { if (m_bInitialised) nStreamLoopedFlag[nChannel] = nLoopFlag; } #endif ================================================ FILE: src/audio/sampman_null.cpp ================================================ #include "common.h" #if !defined(AUDIO_OAL) && !defined(AUDIO_MSS) #include "sampman.h" #include "AudioManager.h" cSampleManager SampleManager; bool _bSampmanInitialised = false; uint32 BankStartOffset[MAX_SFX_BANKS]; uint32 nNumMP3s; cSampleManager::cSampleManager(void) { ; } cSampleManager::~cSampleManager(void) { } void cSampleManager::SetSpeakerConfig(int32 nConfig) { } uint32 cSampleManager::GetMaximumSupportedChannels(void) { return MAXCHANNELS; } uint32 cSampleManager::GetNum3DProvidersAvailable() { return 1; } void cSampleManager::SetNum3DProvidersAvailable(uint32 num) { } char *cSampleManager::Get3DProviderName(uint8 id) { static char name[64] = "NULL"; return name; } void cSampleManager::Set3DProviderName(uint8 id, char *name) { } int8 cSampleManager::GetCurrent3DProviderIndex(void) { return 0; } int8 cSampleManager::SetCurrent3DProvider(uint8 nProvider) { return 0; } bool cSampleManager::IsMP3RadioChannelAvailable(void) { return nNumMP3s != 0; } void cSampleManager::ReleaseDigitalHandle(void) { } void cSampleManager::ReacquireDigitalHandle(void) { } bool cSampleManager::Initialise(void) { return true; } void cSampleManager::Terminate(void) { } bool cSampleManager::CheckForAnAudioFileOnCD(void) { return true; } char cSampleManager::GetCDAudioDriveLetter(void) { return '\0'; } void cSampleManager::UpdateEffectsVolume(void) { } void cSampleManager::SetEffectsMasterVolume(uint8 nVolume) { } void cSampleManager::SetMusicMasterVolume(uint8 nVolume) { } void cSampleManager::SetMusicMasterVolume(uint8 nVolume) { } void cSampleManager::SetEffectsFadeVolume(uint8 nVolume) { } void cSampleManager::SetMusicFadeVolume(uint8 nVolume) { } void cSampleManager::SetMonoMode(uint8 nMode) { } bool cSampleManager::LoadSampleBank(uint8 nBank) { ASSERT( nBank < MAX_SFX_BANKS ); return false; } void cSampleManager::UnloadSampleBank(uint8 nBank) { ASSERT( nBank < MAX_SFX_BANKS ); } bool cSampleManager::IsSampleBankLoaded(uint8 nBank) { ASSERT( nBank < MAX_SFX_BANKS ); return false; } bool cSampleManager::IsPedCommentLoaded(uint32 nComment) { ASSERT( nComment < TOTAL_AUDIO_SAMPLES ); return false; } int32 cSampleManager::_GetPedCommentSlot(uint32 nComment) { return -1; } bool cSampleManager::LoadPedComment(uint32 nComment) { ASSERT( nComment < TOTAL_AUDIO_SAMPLES ); return false; } int32 cSampleManager::GetBankContainingSound(uint32 offset) { return INVALID_SFX_BANK; } int32 cSampleManager::GetSampleBaseFrequency(uint32 nSample) { ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); return 0; } int32 cSampleManager::GetSampleLoopStartOffset(uint32 nSample) { ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); return 0; } int32 cSampleManager::GetSampleLoopEndOffset(uint32 nSample) { ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); return 0; } uint32 cSampleManager::GetSampleLength(uint32 nSample) { ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); return 0; } bool cSampleManager::UpdateReverb(void) { return false; } void cSampleManager::SetChannelReverbFlag(uint32 nChannel, uint8 nReverbFlag) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } bool cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); return false; } void cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume) { ASSERT( nChannel != CHANNEL2D ); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } void cSampleManager::SetChannel3DPosition(uint32 nChannel, float fX, float fY, float fZ) { ASSERT( nChannel != CHANNEL2D ); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } void cSampleManager::SetChannel3DDistances(uint32 nChannel, float fMax, float fMin) { ASSERT( nChannel != CHANNEL2D ); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } void cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume) { ASSERT( nChannel == CHANNEL2D ); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } void cSampleManager::SetChannelPan(uint32 nChannel, uint32 nPan) { ASSERT(nChannel == CHANNEL2D); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } void cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } void cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } void cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } bool cSampleManager::GetChannelUsedFlag(uint32 nChannel) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); return false; } void cSampleManager::StartChannel(uint32 nChannel) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } void cSampleManager::StopChannel(uint32 nChannel) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); } void cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); } void cSampleManager::PauseStream(uint8 nPauseFlag, uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); } void cSampleManager::StartPreloadedStreamedFile(uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); } bool cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); return false; } void cSampleManager::StopStreamedFile(uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); } int32 cSampleManager::GetStreamedFilePosition(uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); return 0; } void cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffectFlag, uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); } int32 cSampleManager::GetStreamedFileLength(uint8 nStream) { ASSERT( nStream < TOTAL_STREAMED_SOUNDS ); return 1; } bool cSampleManager::IsStreamPlaying(uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); return false; } bool cSampleManager::InitialiseSampleBanks(void) { return true; } void cSampleManager::SetStreamedFileLoopFlag(uint8 nLoopFlag, uint8 nChannel) { } int8 cSampleManager::AutoDetect3DProviders() { return -1; } #endif ================================================ FILE: src/audio/sampman_oal.cpp ================================================ //#define JUICY_OAL #ifdef AUDIO_OAL #include #include "eax.h" #include "eax-util.h" #ifdef _WIN32 #include #include #include #include #include #include // for user MP3s #include #include #include #else #define _getcwd getcwd #endif #if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK #pragma comment( lib, "OpenAL32.lib" ) #endif #include "common.h" #include "crossplatform.h" #include "sampman.h" #include "oal/oal_utils.h" #include "oal/aldlist.h" #include "oal/channel.h" #include "oal/stream.h" #include "AudioManager.h" #include "MusicManager.h" #include "Frontend.h" #include "Timer.h" #ifdef AUDIO_OAL_USE_OPUS #include #endif //TODO: fix eax3 reverb //TODO: max channels cSampleManager SampleManager; bool _bSampmanInitialised = false; uint32 BankStartOffset[MAX_SFX_BANKS]; int prevprovider=-1; int curprovider=-1; int usingEAX=0; int usingEAX3=0; //int speaker_type=0; ALCdevice *ALDevice = NULL; ALCcontext *ALContext = NULL; unsigned int _maxSamples; float _fPrevEaxRatioDestination; bool _usingEFX; float _fEffectsLevel; ALuint ALEffect = AL_EFFECT_NULL; ALuint ALEffectSlot = AL_EFFECTSLOT_NULL; struct { char id[256]; char name[256]; int sources; }providers[MAXPROVIDERS]; int defaultProvider; char SampleBankDescFilename[] = "audio/sfx.SDT"; char SampleBankDataFilename[] = "audio/sfx.RAW"; FILE *fpSampleDescHandle; #ifdef OPUS_SFX OggOpusFile *fpSampleDataHandle; #else FILE *fpSampleDataHandle; #endif bool bSampleBankLoaded [MAX_SFX_BANKS]; int32 nSampleBankDiscStartOffset [MAX_SFX_BANKS]; int32 nSampleBankSize [MAX_SFX_BANKS]; uintptr nSampleBankMemoryStartAddress[MAX_SFX_BANKS]; int32 _nSampleDataEndOffset; int32 nPedSlotSfx [MAX_PEDSFX]; int32 nPedSlotSfxAddr[MAX_PEDSFX]; uint8 nCurrentPedSlot; CChannel aChannel[MAXCHANNELS+MAX2DCHANNELS]; uint8 nChannelVolume[MAXCHANNELS+MAX2DCHANNELS]; uint32 nStreamLength[TOTAL_STREAMED_SOUNDS]; ALuint ALStreamSources[MAX_STREAMS][2]; ALuint ALStreamBuffers[MAX_STREAMS][NUM_STREAMBUFFERS]; struct tMP3Entry { char aFilename[MAX_PATH]; uint32 nTrackLength; uint32 nTrackStreamPos; tMP3Entry* pNext; char* pLinkPath; }; uint32 nNumMP3s; tMP3Entry* _pMP3List; char _mp3DirectoryPath[MAX_PATH]; CStream *aStream[MAX_STREAMS]; uint8 nStreamPan [MAX_STREAMS]; uint8 nStreamVolume[MAX_STREAMS]; uint8 nStreamLoopedFlag[MAX_STREAMS]; uint32 _CurMP3Index; int32 _CurMP3Pos; bool _bIsMp3Active; /////////////////////////////////////////////////////////////// // Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS EAXLISTENERPROPERTIES StartEAX3 = {26, 1.7f, 0.8f, -1000, -1000, -100, 4.42f, 0.14f, 1.00f, 429, 0.014f, 0.00f,0.00f,0.00f, 1023, 0.021f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2727.1f, 250.0f, 0.00f, 0x3f }; EAXLISTENERPROPERTIES FinishEAX3 = {26, 100.0f, 1.0f, 0, -1000, -2200, 20.0f, 1.39f, 1.00f, 1000, 0.069f, 0.00f,0.00f,0.00f, 400, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 3.982f, 0.000f, -18.0f, 3530.8f, 417.9f, 6.70f, 0x3f }; EAXLISTENERPROPERTIES EAX3Params; bool IsFXSupported() { return usingEAX || usingEAX3 || _usingEFX; } void EAX_SetAll(const EAXLISTENERPROPERTIES *allparameters) { if ( usingEAX || usingEAX3 ) EAX3_Set(ALEffect, allparameters); else EFX_Set(ALEffect, allparameters); } static void add_providers() { SampleManager.SetNum3DProvidersAvailable(0); ALDeviceList *pDeviceList = NULL; pDeviceList = new ALDeviceList(); if ((pDeviceList) && (pDeviceList->GetNumDevices())) { const int devNumber = Min(pDeviceList->GetNumDevices(), MAXPROVIDERS); int n = 0; for (int i = 0; i < devNumber; i++) { if ( n < MAXPROVIDERS ) { strcpy(providers[n].id, pDeviceList->GetDeviceName(i)); strncpy(providers[n].name, pDeviceList->GetDeviceName(i), sizeof(providers[n].name)); providers[n].sources = pDeviceList->GetMaxNumSources(i); SampleManager.Set3DProviderName(n, providers[n].name); n++; } if ( alGetEnumValue("AL_EFFECT_EAXREVERB") != 0 || pDeviceList->IsExtensionSupported(i, ADEXT_EAX2) || pDeviceList->IsExtensionSupported(i, ADEXT_EAX3) || pDeviceList->IsExtensionSupported(i, ADEXT_EAX4) || pDeviceList->IsExtensionSupported(i, ADEXT_EAX5) ) { if ( n < MAXPROVIDERS ) { strcpy(providers[n].id, pDeviceList->GetDeviceName(i)); strncpy(providers[n].name, pDeviceList->GetDeviceName(i), sizeof(providers[n].name)); strcat(providers[n].name, " EAX"); providers[n].sources = pDeviceList->GetMaxNumSources(i); SampleManager.Set3DProviderName(n, providers[n].name); n++; } if ( n < MAXPROVIDERS ) { strcpy(providers[n].id, pDeviceList->GetDeviceName(i)); strncpy(providers[n].name, pDeviceList->GetDeviceName(i), sizeof(providers[n].name)); strcat(providers[n].name, " EAX3"); providers[n].sources = pDeviceList->GetMaxNumSources(i); SampleManager.Set3DProviderName(n, providers[n].name); n++; } } } SampleManager.SetNum3DProvidersAvailable(n); for(int j=n;jGetDefaultDevice(); if ( defaultProvider > MAXPROVIDERS ) defaultProvider = 0; } delete pDeviceList; } static void release_existing() { for ( int32 i = 0; i < MAXCHANNELS; i++ ) aChannel[i].Term(); aChannel[CHANNEL2D].Term(); if ( IsFXSupported() ) { if ( alIsEffect(ALEffect) ) { alEffecti(ALEffect, AL_EFFECT_TYPE, AL_EFFECT_NULL); alDeleteEffects(1, &ALEffect); ALEffect = AL_EFFECT_NULL; } if (alIsAuxiliaryEffectSlot(ALEffectSlot)) { alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL); alDeleteAuxiliaryEffectSlots(1, &ALEffectSlot); ALEffectSlot = AL_EFFECTSLOT_NULL; } } for ( int32 i = 0; i < MAX_STREAMS; i++ ) { CStream *stream = aStream[i]; if (stream) stream->ProviderTerm(); alDeleteBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]); } alDeleteSources(MAX_STREAMS*2, ALStreamSources[0]); CChannel::DestroyChannels(); if ( ALContext ) { alcMakeContextCurrent(NULL); alcSuspendContext(ALContext); alcDestroyContext(ALContext); } if ( ALDevice ) alcCloseDevice(ALDevice); ALDevice = NULL; ALContext = NULL; _fPrevEaxRatioDestination = 0.0f; _usingEFX = false; _fEffectsLevel = 0.0f; DEV("release_existing()\n"); } static bool set_new_provider(int index) { if ( curprovider == index ) return true; curprovider = index; release_existing(); if ( curprovider != -1 ) { DEV("set_new_provider()\n"); //TODO: _maxSamples = MAXCHANNELS; ALCint attr[] = {ALC_FREQUENCY,MAX_FREQ, ALC_MONO_SOURCES, MAX_STREAMS * 2 + MAXCHANNELS, 0, }; ALDevice = alcOpenDevice(providers[index].id); ASSERT(ALDevice != NULL); ALContext = alcCreateContext(ALDevice, attr); ASSERT(ALContext != NULL); alcMakeContextCurrent(ALContext); const char* ext=(const char*)alGetString(AL_EXTENSIONS); ASSERT(strstr(ext,"AL_SOFT_loop_points")!=NULL); if ( strstr(ext,"AL_SOFT_loop_points")==NULL ) { curprovider=-1; release_existing(); return false; } alListenerf (AL_GAIN, 1.0f); alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f); alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f); ALfloat orientation[6] = { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }; alListenerfv(AL_ORIENTATION, orientation); alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) { alGenAuxiliaryEffectSlots(1, &ALEffectSlot); alGenEffects(1, &ALEffect); } alGenSources(MAX_STREAMS*2, ALStreamSources[0]); for ( int32 i = 0; i < MAX_STREAMS; i++ ) { alGenBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]); alSourcei(ALStreamSources[i][0], AL_SOURCE_RELATIVE, AL_TRUE); alSource3f(ALStreamSources[i][0], AL_POSITION, 0.0f, 0.0f, 0.0f); alSourcef(ALStreamSources[i][0], AL_GAIN, 1.0f); alSourcei(ALStreamSources[i][1], AL_SOURCE_RELATIVE, AL_TRUE); alSource3f(ALStreamSources[i][1], AL_POSITION, 0.0f, 0.0f, 0.0f); alSourcef(ALStreamSources[i][1], AL_GAIN, 1.0f); CStream *stream = aStream[i]; if (stream) stream->ProviderInit(); } usingEAX = 0; usingEAX3 = 0; _usingEFX = false; if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX3")], " EAX3") && alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) { EAX_SetAll(&FinishEAX3); usingEAX = 1; usingEAX3 = 1; DEV("EAX3\n"); } else if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) ) { EAX_SetAll(&EAX30_ORIGINAL_PRESETS[EAX_ENVIRONMENT_CAVE]); if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX")], " EAX")) { usingEAX = 1; DEV("EAX1\n"); } else { _usingEFX = true; DEV("EFX\n"); } } //SampleManager.SetSpeakerConfig(speaker_type); CChannel::InitChannels(); for ( int32 i = 0; i < MAXCHANNELS; i++ ) aChannel[i].Init(i); aChannel[CHANNEL2D].Init(CHANNEL2D, true); if ( IsFXSupported() ) { /**/ alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect); /**/ for ( int32 i = 0; i < MAXCHANNELS; i++ ) aChannel[i].SetReverbMix(ALEffectSlot, 0.0f); } return true; } return false; } static bool IsThisTrackAt16KHz(uint32 track) { return track == STREAMED_SOUND_RADIO_KCHAT || track == STREAMED_SOUND_RADIO_VCPR || track == STREAMED_SOUND_RADIO_POLICE; } cSampleManager::cSampleManager(void) { ; } cSampleManager::~cSampleManager(void) { } void cSampleManager::SetSpeakerConfig(int32 nConfig) { } uint32 cSampleManager::GetMaximumSupportedChannels(void) { if ( _maxSamples > MAXCHANNELS ) return MAXCHANNELS; return _maxSamples; } uint32 cSampleManager::GetNum3DProvidersAvailable() { return m_nNumberOfProviders; } void cSampleManager::SetNum3DProvidersAvailable(uint32 num) { m_nNumberOfProviders = num; } char *cSampleManager::Get3DProviderName(uint8 id) { return m_aAudioProviders[id]; } void cSampleManager::Set3DProviderName(uint8 id, char *name) { m_aAudioProviders[id] = name; } int8 cSampleManager::GetCurrent3DProviderIndex(void) { return curprovider; } int8 cSampleManager::SetCurrent3DProvider(uint8 nProvider) { int savedprovider = curprovider; nProvider = clamp(nProvider, 0, m_nNumberOfProviders - 1); if ( set_new_provider(nProvider) ) return curprovider; else if ( savedprovider != -1 && savedprovider < m_nNumberOfProviders && set_new_provider(savedprovider) ) return curprovider; else return curprovider; } int8 cSampleManager::AutoDetect3DProviders() { if (!AudioManager.IsAudioInitialised()) return -1; if (defaultProvider >= 0 && defaultProvider < m_nNumberOfProviders) { if (set_new_provider(defaultProvider)) return defaultProvider; } for (uint32 i = 0; i < GetNum3DProvidersAvailable(); i++) { char* providername = Get3DProviderName(i); if (!strcasecmp(providername, "OPENAL SOFT")) { SetCurrent3DProvider(i); if (GetCurrent3DProviderIndex() == i) return i; } } return -1; } static bool _ResolveLink(char const *path, char *out) { #ifdef _WIN32 size_t len = strlen(path); if (len < 4 || strcmp(&path[len - 4], ".lnk") != 0) return false; IShellLink* psl; WIN32_FIND_DATA fd; char filepath[MAX_PATH]; CoInitialize(NULL); if (SUCCEEDED( CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl ) )) { IPersistFile *ppf; if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf))) { WCHAR wpath[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, MAX_PATH); if (SUCCEEDED(ppf->Load(wpath, STGM_READ))) { /* Resolve the link */ if (SUCCEEDED(psl->Resolve(NULL, SLR_ANY_MATCH|SLR_NO_UI|SLR_NOSEARCH))) { strcpy(filepath, path); if (SUCCEEDED(psl->GetPath(filepath, MAX_PATH, &fd, SLGP_UNCPRIORITY))) { OutputDebugString(fd.cFileName); strcpy(out, filepath); // FIX: Release the objects. Taken from SA. #ifdef FIX_BUGS ppf->Release(); psl->Release(); #endif return true; } } } ppf->Release(); } psl->Release(); } return false; #else struct stat sb; if (lstat(path, &sb) == -1) { perror("lstat: "); return false; } if (S_ISLNK(sb.st_mode)) { char* linkname = (char*)alloca(sb.st_size + 1); if (linkname == NULL) { fprintf(stderr, "insufficient memory\n"); return false; } if (readlink(path, linkname, sb.st_size + 1) < 0) { perror("readlink: "); return false; } linkname[sb.st_size] = '\0'; strcpy(out, linkname); return true; } else { return false; } #endif } static void _FindMP3s(void) { tMP3Entry *pList; bool bShortcut; bool bInitFirstEntry; HANDLE hFind; char path[MAX_PATH]; char filepath[MAX_PATH*2]; int total_ms; WIN32_FIND_DATA fd; if (getcwd(_mp3DirectoryPath, MAX_PATH) == NULL) { perror("getcwd: "); return; } OutputDebugString("Finding MP3s..."); strcpy(path, _mp3DirectoryPath); strcat(path, "\\MP3\\"); strcpy(_mp3DirectoryPath, path); OutputDebugString(_mp3DirectoryPath); strcat(path, "*"); hFind = FindFirstFile(path, &fd); if ( hFind == INVALID_HANDLE_VALUE ) { return; } strcpy(filepath, _mp3DirectoryPath); strcat(filepath, fd.cFileName); size_t filepathlen = strlen(filepath); if ( filepathlen <= 0) { FindClose(hFind); return; } if ( _ResolveLink(filepath, filepath) ) { OutputDebugString("Resolving Link"); OutputDebugString(filepath); bShortcut = true; } else bShortcut = false; aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]); if (aStream[0] && aStream[0]->IsOpened()) { total_ms = aStream[0]->GetLengthMS(); delete aStream[0]; aStream[0] = NULL; OutputDebugString(fd.cFileName); _pMP3List = new tMP3Entry; if ( _pMP3List == NULL ) { FindClose(hFind); return; } nNumMP3s = 1; strcpy(_pMP3List->aFilename, fd.cFileName); _pMP3List->nTrackLength = total_ms; _pMP3List->pNext = NULL; pList = _pMP3List; if ( bShortcut ) { _pMP3List->pLinkPath = new char[MAX_PATH*2]; strcpy(_pMP3List->pLinkPath, filepath); } else { _pMP3List->pLinkPath = NULL; } bInitFirstEntry = false; } else { strcat(filepath, " - NOT A VALID MP3"); OutputDebugString(filepath); bInitFirstEntry = true; } while ( true ) { if ( !FindNextFile(hFind, &fd) ) break; if ( bInitFirstEntry ) { strcpy(filepath, _mp3DirectoryPath); strcat(filepath, fd.cFileName); size_t filepathlen = strlen(filepath); if ( filepathlen > 0 ) { if ( _ResolveLink(filepath, filepath) ) { OutputDebugString("Resolving Link"); OutputDebugString(filepath); bShortcut = true; } else { bShortcut = false; if (filepathlen > MAX_PATH) { continue; } } aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]); if (aStream[0] && aStream[0]->IsOpened()) { total_ms = aStream[0]->GetLengthMS(); delete aStream[0]; aStream[0] = NULL; OutputDebugString(fd.cFileName); _pMP3List = new tMP3Entry; if ( _pMP3List == NULL) break; nNumMP3s = 1; strcpy(_pMP3List->aFilename, fd.cFileName); _pMP3List->nTrackLength = total_ms; _pMP3List->pNext = NULL; if ( bShortcut ) { _pMP3List->pLinkPath = new char [MAX_PATH*2]; strcpy(_pMP3List->pLinkPath, filepath); } else { _pMP3List->pLinkPath = NULL; } pList = _pMP3List; bInitFirstEntry = false; } else { strcat(filepath, " - NOT A VALID MP3"); OutputDebugString(filepath); } } } else { strcpy(filepath, _mp3DirectoryPath); strcat(filepath, fd.cFileName); size_t filepathlen = strlen(filepath); if ( filepathlen > 0 ) { if ( _ResolveLink(filepath, filepath) ) { OutputDebugString("Resolving Link"); OutputDebugString(filepath); bShortcut = true; } else bShortcut = false; aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]); if (aStream[0] && aStream[0]->IsOpened()) { total_ms = aStream[0]->GetLengthMS(); delete aStream[0]; aStream[0] = NULL; OutputDebugString(fd.cFileName); pList->pNext = new tMP3Entry; tMP3Entry *e = pList->pNext; if ( e == NULL ) break; pList = pList->pNext; strcpy(e->aFilename, fd.cFileName); e->nTrackLength = total_ms; e->pNext = NULL; if ( bShortcut ) { e->pLinkPath = new char [MAX_PATH*2]; strcpy(e->pLinkPath, filepath); } else { e->pLinkPath = NULL; } nNumMP3s++; OutputDebugString(fd.cFileName); } else { strcat(filepath, " - NOT A VALID MP3"); OutputDebugString(filepath); } } } } FindClose(hFind); } static void _DeleteMP3Entries(void) { tMP3Entry *e = _pMP3List; while ( e != NULL ) { tMP3Entry *next = e->pNext; if ( next == NULL ) next = NULL; if ( e->pLinkPath != NULL ) { #ifndef FIX_BUGS delete e->pLinkPath; // BUG: should be delete [] #else delete[] e->pLinkPath; #endif e->pLinkPath = NULL; } delete e; if ( next ) e = next; else e = NULL; nNumMP3s--; } if ( nNumMP3s != 0 ) { OutputDebugString("Not all MP3 entries were deleted"); nNumMP3s = 0; } _pMP3List = NULL; } static tMP3Entry * _GetMP3EntryByIndex(uint32 idx) { uint32 n = ( idx < nNumMP3s ) ? idx : 0; if ( _pMP3List != NULL ) { tMP3Entry *e = _pMP3List; for ( uint32 i = 0; i < n; i++ ) e = e->pNext; return e; } return NULL; } static inline bool _GetMP3PosFromStreamPos(uint32 *pPosition, tMP3Entry **pEntry) { _CurMP3Index = 0; for ( *pEntry = _pMP3List; *pEntry != NULL; *pEntry = (*pEntry)->pNext ) { if ( *pPosition >= (*pEntry)->nTrackStreamPos && *pPosition < (*pEntry)->nTrackLength + (*pEntry)->nTrackStreamPos ) { *pPosition -= (*pEntry)->nTrackStreamPos; _CurMP3Pos = *pPosition; return true; } _CurMP3Index++; } *pPosition = 0; *pEntry = _pMP3List; _CurMP3Pos = 0; _CurMP3Index = 0; return false; } bool cSampleManager::IsMP3RadioChannelAvailable(void) { return nNumMP3s != 0; } void cSampleManager::ReleaseDigitalHandle(void) { if ( ALDevice ) { prevprovider = curprovider; release_existing(); curprovider = -1; } } void cSampleManager::ReacquireDigitalHandle(void) { if ( ALDevice ) { if ( prevprovider != -1 ) set_new_provider(prevprovider); } } bool cSampleManager::Initialise(void) { if ( _bSampmanInitialised ) return true; EFXInit(); CStream::Initialise(); { for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ ) { m_aSamples[i].nOffset = 0; m_aSamples[i].nSize = 0; m_aSamples[i].nFrequency = MAX_FREQ; m_aSamples[i].nLoopStart = 0; m_aSamples[i].nLoopEnd = -1; } m_nEffectsVolume = MAX_VOLUME; m_nMusicVolume = MAX_VOLUME; m_nEffectsFadeVolume = MAX_VOLUME; m_nMusicFadeVolume = MAX_VOLUME; m_nMonoMode = 0; } { curprovider = -1; prevprovider = -1; _usingEFX = false; usingEAX =0; usingEAX3=0; _fEffectsLevel = 0.0f; _maxSamples = 0; ALDevice = NULL; ALContext = NULL; } { fpSampleDescHandle = NULL; fpSampleDataHandle = NULL; for ( int32 i = 0; i < MAX_SFX_BANKS; i++ ) { bSampleBankLoaded[i] = false; nSampleBankDiscStartOffset[i] = 0; nSampleBankSize[i] = 0; nSampleBankMemoryStartAddress[i] = 0; } } { for ( int32 i = 0; i < MAX_PEDSFX; i++ ) { nPedSlotSfx[i] = NO_SAMPLE; nPedSlotSfxAddr[i] = 0; } nCurrentPedSlot = 0; } { for ( int32 i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++ ) nChannelVolume[i] = 0; } { for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ ) nStreamLength[i] = 0; } add_providers(); #ifdef AUDIO_CACHE FILE *cacheFile = fcaseopen("audio\\sound.cache", "rb"); if (cacheFile) { debug("Loadind audio cache (If game crashes around here, then your cache is corrupted, remove audio/sound.cache)\n"); fread(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile); fclose(cacheFile); } else { debug("Cannot load audio cache\n"); #endif for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ ) { aStream[0] = new CStream(StreamedNameTable[i], ALStreamSources[0], ALStreamBuffers[0], IsThisTrackAt16KHz(i) ? 16000 : 32000); if ( aStream[0] && aStream[0]->IsOpened() ) { uint32 tatalms = aStream[0]->GetLengthMS(); delete aStream[0]; aStream[0] = NULL; nStreamLength[i] = tatalms; } else USERERROR("Can't open '%s'\n", StreamedNameTable[i]); } #ifdef AUDIO_CACHE cacheFile = fcaseopen("audio\\sound.cache", "wb"); if(cacheFile) { debug("Saving audio cache\n"); fwrite(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile); fclose(cacheFile); } else { debug("Cannot save audio cache\n"); } } #endif { if ( !InitialiseSampleBanks() ) { Terminate(); return false; } nSampleBankMemoryStartAddress[SFX_BANK_0] = (uintptr)malloc(nSampleBankSize[SFX_BANK_0]); ASSERT(nSampleBankMemoryStartAddress[SFX_BANK_0] != 0); if ( nSampleBankMemoryStartAddress[SFX_BANK_0] == 0 ) { Terminate(); return false; } nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = (uintptr)malloc(PED_BLOCKSIZE*MAX_PEDSFX); ASSERT(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] != 0); LoadSampleBank(SFX_BANK_0); } { for ( int32 i = 0; i < MAX_STREAMS; i++ ) { aStream[i] = NULL; nStreamVolume[i] = 100; nStreamPan[i] = 63; } } { _bSampmanInitialised = true; if ( defaultProvider >= 0 && defaultProvider < m_nNumberOfProviders ) { set_new_provider(defaultProvider); } else { Terminate(); return false; } } { nNumMP3s = 0; _pMP3List = NULL; _FindMP3s(); if ( nNumMP3s != 0 ) { nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] = 0; for ( tMP3Entry *e = _pMP3List; e != NULL; e = e->pNext ) { e->nTrackStreamPos = nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER]; nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] += e->nTrackLength; } time_t t = time(NULL); tm *localtm; bool bUseRandomTable; if ( t == -1 ) bUseRandomTable = true; else { bUseRandomTable = false; localtm = localtime(&t); } int32 randval; if ( bUseRandomTable ) randval = AudioManager.GetRandomNumber(1); else randval = localtm->tm_sec * localtm->tm_min; _CurMP3Index = randval % nNumMP3s; tMP3Entry *randmp3 = _pMP3List; for ( int32 i = randval % nNumMP3s; i > 0; --i) randmp3 = randmp3->pNext; if ( bUseRandomTable ) _CurMP3Pos = AudioManager.GetRandomNumber(0) % randmp3->nTrackLength; else { if ( localtm->tm_sec > 0 ) { int32 s = localtm->tm_sec; _CurMP3Pos = s*s*s*s*s*s*s*s % randmp3->nTrackLength; } else _CurMP3Pos = AudioManager.GetRandomNumber(0) % randmp3->nTrackLength; } } else _CurMP3Pos = 0; _bIsMp3Active = false; } return true; } void cSampleManager::Terminate(void) { for (int32 i = 0; i < MAX_STREAMS; i++) { CStream *stream = aStream[i]; if (stream) { delete stream; aStream[i] = NULL; } } release_existing(); _DeleteMP3Entries(); CStream::Terminate(); if ( nSampleBankMemoryStartAddress[SFX_BANK_0] != 0 ) { free((void *)nSampleBankMemoryStartAddress[SFX_BANK_0]); nSampleBankMemoryStartAddress[SFX_BANK_0] = 0; } if ( nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] != 0 ) { free((void *)nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS]); nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = 0; } _bSampmanInitialised = false; } bool cSampleManager::CheckForAnAudioFileOnCD(void) { return true; } char cSampleManager::GetCDAudioDriveLetter(void) { return '\0'; } void cSampleManager::UpdateEffectsVolume(void) { if ( _bSampmanInitialised ) { for ( int32 i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++ ) { if ( GetChannelUsedFlag(i) ) { if ( nChannelVolume[i] != 0 ) aChannel[i].SetVolume(m_nEffectsFadeVolume*nChannelVolume[i]*m_nEffectsVolume >> 14); } } } } void cSampleManager::SetEffectsMasterVolume(uint8 nVolume) { m_nEffectsVolume = nVolume; UpdateEffectsVolume(); } void cSampleManager::SetMusicMasterVolume(uint8 nVolume) { m_nMusicVolume = nVolume; } void cSampleManager::SetMP3BoostVolume(uint8 nVolume) { m_nMP3BoostVolume = nVolume; } void cSampleManager::SetEffectsFadeVolume(uint8 nVolume) { m_nEffectsFadeVolume = nVolume; UpdateEffectsVolume(); } void cSampleManager::SetMusicFadeVolume(uint8 nVolume) { m_nMusicFadeVolume = nVolume; } void cSampleManager::SetMonoMode(uint8 nMode) { m_nMonoMode = nMode; } bool cSampleManager::LoadSampleBank(uint8 nBank) { ASSERT( nBank < MAX_SFX_BANKS); if ( CTimer::GetIsCodePaused() ) return false; if ( MusicManager.IsInitialised() && MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE && nBank != SFX_BANK_0 ) { return false; } #ifdef OPUS_SFX int samplesRead = 0; int samplesSize = nSampleBankSize[nBank] / 2; op_pcm_seek(fpSampleDataHandle, 0); while (samplesSize > 0) { int size = op_read(fpSampleDataHandle, (opus_int16 *)(nSampleBankMemoryStartAddress[nBank] + samplesRead), samplesSize, NULL); if (size <= 0) { // huh? //assert(0); break; } samplesRead += size*2; samplesSize -= size; } #else if ( fseek(fpSampleDataHandle, nSampleBankDiscStartOffset[nBank], SEEK_SET) != 0 ) return false; if ( fread((void *)nSampleBankMemoryStartAddress[nBank], 1, nSampleBankSize[nBank], fpSampleDataHandle) != nSampleBankSize[nBank] ) return false; #endif bSampleBankLoaded[nBank] = true; return true; } void cSampleManager::UnloadSampleBank(uint8 nBank) { ASSERT( nBank < MAX_SFX_BANKS); bSampleBankLoaded[nBank] = false; } bool cSampleManager::IsSampleBankLoaded(uint8 nBank) { ASSERT( nBank < MAX_SFX_BANKS); return bSampleBankLoaded[nBank]; } bool cSampleManager::IsPedCommentLoaded(uint32 nComment) { ASSERT( nComment < TOTAL_AUDIO_SAMPLES ); int8 slot; for ( int32 i = 0; i < _TODOCONST(3); i++ ) { slot = nCurrentPedSlot - i - 1; #ifdef FIX_BUGS if (slot < 0) slot += ARRAY_SIZE(nPedSlotSfx); #endif if ( nComment == nPedSlotSfx[slot] ) return true; } return false; } int32 cSampleManager::_GetPedCommentSlot(uint32 nComment) { int8 slot; for (int32 i = 0; i < _TODOCONST(3); i++) { slot = nCurrentPedSlot - i - 1; #ifdef FIX_BUGS if (slot < 0) slot += ARRAY_SIZE(nPedSlotSfx); #endif if (nComment == nPedSlotSfx[slot]) return slot; } return -1; } bool cSampleManager::LoadPedComment(uint32 nComment) { ASSERT( nComment < TOTAL_AUDIO_SAMPLES ); if ( CTimer::GetIsCodePaused() ) return false; // no talking peds during cutsenes or the game end if ( MusicManager.IsInitialised() ) { switch ( MusicManager.GetMusicMode() ) { case MUSICMODE_CUTSCENE: { return false; break; } } } #ifdef OPUS_SFX int samplesRead = 0; int samplesSize = m_aSamples[nComment].nSize / 2; op_pcm_seek(fpSampleDataHandle, m_aSamples[nComment].nOffset / 2); while (samplesSize > 0) { int size = op_read(fpSampleDataHandle, (opus_int16 *)(nSampleBankMemoryStartAddress[SAMPLEBANK_PED] + PED_BLOCKSIZE * nCurrentPedSlot + samplesRead), samplesSize, NULL); if (size <= 0) { return false; } samplesRead += size * 2; samplesSize -= size; } #else if ( fseek(fpSampleDataHandle, m_aSamples[nComment].nOffset, SEEK_SET) != 0 ) return false; if ( fread((void *)(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE*nCurrentPedSlot), 1, m_aSamples[nComment].nSize, fpSampleDataHandle) != m_aSamples[nComment].nSize ) return false; #endif nPedSlotSfx[nCurrentPedSlot] = nComment; if ( ++nCurrentPedSlot >= MAX_PEDSFX ) nCurrentPedSlot = 0; return true; } int32 cSampleManager::GetBankContainingSound(uint32 offset) { if ( offset >= BankStartOffset[SFX_BANK_PED_COMMENTS] ) return SFX_BANK_PED_COMMENTS; if ( offset >= BankStartOffset[SFX_BANK_0] ) return SFX_BANK_0; return INVALID_SFX_BANK; } int32 cSampleManager::GetSampleBaseFrequency(uint32 nSample) { ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); return m_aSamples[nSample].nFrequency; } int32 cSampleManager::GetSampleLoopStartOffset(uint32 nSample) { ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); return m_aSamples[nSample].nLoopStart; } int32 cSampleManager::GetSampleLoopEndOffset(uint32 nSample) { ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); return m_aSamples[nSample].nLoopEnd; } uint32 cSampleManager::GetSampleLength(uint32 nSample) { ASSERT( nSample < TOTAL_AUDIO_SAMPLES ); return m_aSamples[nSample].nSize / sizeof(uint16); } bool cSampleManager::UpdateReverb(void) { if ( !usingEAX && !_usingEFX ) return false; if ( AudioManager.GetFrameCounter() & 15 ) return false; float fRatio = 0.0f; #define MIN_DIST 0.5f #define CALCULATE_RATIO(value, maxDist, maxRatio) (value > MIN_DIST && value < maxDist ? value / maxDist * maxRatio : 0) fRatio += CALCULATE_RATIO(AudioManager.GetReflectionsDistance(REFLECTION_CEIL_NORTH), 10.0f, 1/2.f); fRatio += CALCULATE_RATIO(AudioManager.GetReflectionsDistance(REFLECTION_CEIL_SOUTH), 10.0f, 1/2.f); fRatio += CALCULATE_RATIO(AudioManager.GetReflectionsDistance(REFLECTION_CEIL_WEST), 10.0f, 1/2.f); fRatio += CALCULATE_RATIO(AudioManager.GetReflectionsDistance(REFLECTION_CEIL_EAST), 10.0f, 1/2.f); fRatio += CALCULATE_RATIO((AudioManager.GetReflectionsDistance(REFLECTION_NORTH) + AudioManager.GetReflectionsDistance(REFLECTION_SOUTH)) / 2.f, 4.0f, 1/3.f); fRatio += CALCULATE_RATIO((AudioManager.GetReflectionsDistance(REFLECTION_WEST) + AudioManager.GetReflectionsDistance(REFLECTION_EAST)) / 2.f, 4.0f, 1/3.f); #undef CALCULATE_RATIO #undef MIN_DIST fRatio = clamp(fRatio, 0.0f, 0.6f); if ( fRatio == _fPrevEaxRatioDestination ) return false; #ifdef JUICY_OAL if ( usingEAX3 || _usingEFX ) #else if ( usingEAX3 ) #endif { fRatio = Min(fRatio * 1.67f, 1.0f); if ( EAX3ListenerInterpolate(&StartEAX3, &FinishEAX3, fRatio, &EAX3Params, false) ) { EAX_SetAll(&EAX3Params); /* if ( IsFXSupported() ) { alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect); for ( int32 i = 0; i < MAXCHANNELS; i++ ) aChannel[i].UpdateReverb(ALEffectSlot); } */ _fEffectsLevel = fRatio * 0.75f; } } else { if ( _usingEFX ) _fEffectsLevel = fRatio * 0.8f; else _fEffectsLevel = fRatio * 0.22f; } _fEffectsLevel = Min(_fEffectsLevel, 1.0f); _fPrevEaxRatioDestination = fRatio; return true; } void cSampleManager::SetChannelReverbFlag(uint32 nChannel, uint8 nReverbFlag) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); if ( usingEAX || _usingEFX ) { if ( IsFXSupported() ) { alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect); if ( nReverbFlag != 0 ) aChannel[nChannel].SetReverbMix(ALEffectSlot, _fEffectsLevel); else aChannel[nChannel].SetReverbMix(ALEffectSlot, 0.0f); } } } bool cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); uintptr addr; if ( nSfx < SAMPLEBANK_MAX ) { if ( !IsSampleBankLoaded(nBank) ) return false; addr = nSampleBankMemoryStartAddress[nBank] + m_aSamples[nSfx].nOffset - m_aSamples[BankStartOffset[nBank]].nOffset; } else { if ( !IsPedCommentLoaded(nSfx) ) return false; int32 slot = _GetPedCommentSlot(nSfx); addr = (nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE * slot); } if ( GetChannelUsedFlag(nChannel) ) { TRACE("Stopping channel %d - really!!!", nChannel); StopChannel(nChannel); } aChannel[nChannel].Reset(); if ( aChannel[nChannel].HasSource() ) { aChannel[nChannel].SetSampleData ((void*)addr, m_aSamples[nSfx].nSize, m_aSamples[nSfx].nFrequency); aChannel[nChannel].SetLoopPoints (0, -1); aChannel[nChannel].SetPitch (1.0f); return true; } return false; } void cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume) { ASSERT( nChannel != CHANNEL2D ); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); uint32 vol = nVolume; if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; nChannelVolume[nChannel] = vol; if (MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE ) { if (MusicManager.GetCurrentTrack() == STREAMED_SOUND_CUTSCENE_FINALE) nChannelVolume[nChannel] = 0; else nChannelVolume[nChannel] >>= 2; } // no idea, does this one looks like a bug or it's SetChannelVolume ? aChannel[nChannel].SetVolume(m_nEffectsFadeVolume*nChannelVolume[nChannel]*m_nEffectsVolume >> 14); } void cSampleManager::SetChannel3DPosition(uint32 nChannel, float fX, float fY, float fZ) { ASSERT( nChannel != CHANNEL2D ); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); aChannel[nChannel].SetPosition(-fX, fY, fZ); } void cSampleManager::SetChannel3DDistances(uint32 nChannel, float fMax, float fMin) { ASSERT( nChannel != CHANNEL2D ); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); aChannel[nChannel].SetDistances(fMax, fMin); } void cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume) { ASSERT( nChannel == CHANNEL2D ); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); if ( nChannel == CHANNEL2D ) { uint32 vol = nVolume; if ( vol > MAX_VOLUME ) vol = MAX_VOLUME; nChannelVolume[nChannel] = vol; // increase the volume for JB.MP3 and S4_BDBD.MP3 if (MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE ) { if (MusicManager.GetCurrentTrack() == STREAMED_SOUND_CUTSCENE_FINALE) nChannelVolume[nChannel] = 0; else nChannelVolume[nChannel] >>= 2; } aChannel[nChannel].SetVolume(m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14); } } void cSampleManager::SetChannelPan(uint32 nChannel, uint32 nPan) { ASSERT(nChannel == CHANNEL2D); ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); if ( nChannel == CHANNEL2D ) { aChannel[nChannel].SetPan(nPan); } } void cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); aChannel[nChannel].SetCurrentFreq(nFreq); } void cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); aChannel[nChannel].SetLoopPoints(nLoopStart / (DIGITALBITS / 8), nLoopEnd / (DIGITALBITS / 8)); } void cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); aChannel[nChannel].SetLoopCount(nLoopCount); } bool cSampleManager::GetChannelUsedFlag(uint32 nChannel) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); return aChannel[nChannel].IsUsed(); } void cSampleManager::StartChannel(uint32 nChannel) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); aChannel[nChannel].Start(); } void cSampleManager::StopChannel(uint32 nChannel) { ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS ); aChannel[nChannel].Stop(); } void cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream) { char filename[MAX_PATH]; ASSERT( nStream < MAX_STREAMS ); if ( nFile < TOTAL_STREAMED_SOUNDS ) { if ( aStream[nStream] ) { delete aStream[nStream]; aStream[nStream] = NULL; } strcpy(filename, StreamedNameTable[nFile]); CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); ASSERT(stream != NULL); aStream[nStream] = stream; if ( !stream->Setup() ) { delete stream; aStream[nStream] = NULL; } } } void cSampleManager::PauseStream(uint8 nPauseFlag, uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); CStream *stream = aStream[nStream]; if ( stream ) { stream->SetPause(nPauseFlag != 0); } } void cSampleManager::StartPreloadedStreamedFile(uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); CStream *stream = aStream[nStream]; if ( stream ) { if ( stream->IsOpened() ) { stream->Start(); } } } bool cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) { int i = 0; uint32 position = nPos; char filename[MAX_PATH]; if ( nFile >= TOTAL_STREAMED_SOUNDS ) return false; if ( aStream[nStream] ) { delete aStream[nStream]; aStream[nStream] = NULL; } if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER ) { do { // Just switched to MP3 player if ( !_bIsMp3Active && i == 0 ) { if ( nPos > nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] ) position = 0; tMP3Entry *e = _pMP3List; // Try to continue from previous song, if already started if(!_GetMP3PosFromStreamPos(&position, &e) && !e) { nFile = 0; strcpy(filename, StreamedNameTable[nFile]); CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); aStream[nStream] = stream; if (stream->Setup()) { stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1); nStreamLoopedFlag[nStream] = true; if (position != 0) stream->SetPosMS(position); stream->Start(); return true; } else { delete stream; aStream[nStream] = NULL; } return false; } else { if (e->pLinkPath != NULL) aStream[nStream] = new CStream(e->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); else { strcpy(filename, _mp3DirectoryPath); strcat(filename, e->aFilename); aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]); } if (aStream[nStream]->Setup()) { if (position != 0) aStream[nStream]->SetPosMS(position); aStream[nStream]->Start(); _bIsMp3Active = true; return true; } else { delete aStream[nStream]; aStream[nStream] = NULL; } // fall through, start playing from another song } } else { if(++_CurMP3Index >= nNumMP3s) _CurMP3Index = 0; _CurMP3Pos = 0; tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index); if ( !mp3 ) { mp3 = _pMP3List; if ( !_pMP3List ) { nFile = 0; _bIsMp3Active = 0; strcpy(filename, StreamedNameTable[nFile]); CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); aStream[nStream] = stream; if (stream->Setup()) { stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1); nStreamLoopedFlag[nStream] = true; if (position != 0) stream->SetPosMS(position); stream->Start(); return true; } else { delete stream; aStream[nStream] = NULL; } return false; } } if (mp3->pLinkPath != NULL) aStream[nStream] = new CStream(mp3->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); else { strcpy(filename, _mp3DirectoryPath); strcat(filename, mp3->aFilename); aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); } if (aStream[nStream]->Setup()) { aStream[nStream]->Start(); #ifdef FIX_BUGS _bIsMp3Active = true; #endif return true; } else { delete aStream[nStream]; aStream[nStream] = NULL; } } _bIsMp3Active = 0; } while ( ++i < nNumMP3s ); position = 0; nFile = 0; } strcpy(filename, StreamedNameTable[nFile]); CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); aStream[nStream] = stream; if ( stream->Setup() ) { stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1); nStreamLoopedFlag[nStream] = true; if (position != 0) stream->SetPosMS(position); stream->Start(); return true; } else { delete stream; aStream[nStream] = NULL; } return false; } void cSampleManager::StopStreamedFile(uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); CStream *stream = aStream[nStream]; if ( stream ) { delete stream; aStream[nStream] = NULL; if ( nStream == 0 ) _bIsMp3Active = false; } } int32 cSampleManager::GetStreamedFilePosition(uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); CStream *stream = aStream[nStream]; if ( stream ) { if ( _bIsMp3Active ) { tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index); if ( mp3 != NULL ) { return stream->GetPosMS() + mp3->nTrackStreamPos; } else return 0; } else { return stream->GetPosMS(); } } return 0; } void cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffectFlag, uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); float boostMult = 0.0f; if ( nVolume > MAX_VOLUME ) nVolume = MAX_VOLUME; if ( nPan > MAX_VOLUME ) nPan = MAX_VOLUME; if ( MusicManager.GetRadioInCar() == USERTRACK && !MusicManager.CheckForMusicInterruptions() ) boostMult = m_nMP3BoostVolume / 64.f; nStreamVolume[nStream] = nVolume; nStreamPan [nStream] = nPan; CStream *stream = aStream[nStream]; if ( stream ) { if ( nEffectFlag ) { if ( nStream == 1 || nStream == 2 ) stream->SetVolume(128*nVolume*m_nEffectsVolume >> 14); else stream->SetVolume(m_nEffectsFadeVolume*nVolume*m_nEffectsVolume >> 14); } else stream->SetVolume((m_nMusicFadeVolume*nVolume*(uint32)(m_nMusicVolume * boostMult + m_nMusicVolume)) >> 14); stream->SetPan(nPan); } } int32 cSampleManager::GetStreamedFileLength(uint8 nStream) { ASSERT( nStream < TOTAL_STREAMED_SOUNDS ); return nStreamLength[nStream]; } bool cSampleManager::IsStreamPlaying(uint8 nStream) { ASSERT( nStream < MAX_STREAMS ); CStream *stream = aStream[nStream]; if ( stream ) { if ( stream->IsPlaying() ) return true; } return false; } void cSampleManager::Service(void) { for ( int32 i = 0; i < MAX_STREAMS; i++ ) { CStream *stream = aStream[i]; if ( stream ) stream->Update(); } int refCount = CChannel::channelsThatNeedService; for ( int32 i = 0; refCount && i < MAXCHANNELS+MAX2DCHANNELS; i++ ) { if ( aChannel[i].Update() ) refCount--; } } bool cSampleManager::InitialiseSampleBanks(void) { int32 nBank = SFX_BANK_0; fpSampleDescHandle = fcaseopen(SampleBankDescFilename, "rb"); if ( fpSampleDescHandle == NULL ) return false; #ifndef OPUS_SFX fpSampleDataHandle = fcaseopen(SampleBankDataFilename, "rb"); if ( fpSampleDataHandle == NULL ) { fclose(fpSampleDescHandle); fpSampleDescHandle = NULL; return false; } fseek(fpSampleDataHandle, 0, SEEK_END); int32 _nSampleDataEndOffset = ftell(fpSampleDataHandle); rewind(fpSampleDataHandle); #else int e; fpSampleDataHandle = op_open_file(SampleBankDataFilename, &e); #endif fread(m_aSamples, sizeof(tSample), TOTAL_AUDIO_SAMPLES, fpSampleDescHandle); #ifdef OPUS_SFX int32 _nSampleDataEndOffset = m_aSamples[TOTAL_AUDIO_SAMPLES - 1].nOffset + m_aSamples[TOTAL_AUDIO_SAMPLES - 1].nSize; #endif fclose(fpSampleDescHandle); fpSampleDescHandle = NULL; for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ ) { #ifdef FIX_BUGS if (nBank >= MAX_SFX_BANKS) break; #endif if ( BankStartOffset[nBank] == BankStartOffset[SFX_BANK_0] + i ) { nSampleBankDiscStartOffset[nBank] = m_aSamples[i].nOffset; nBank++; } } nSampleBankSize[SFX_BANK_0] = nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS] - nSampleBankDiscStartOffset[SFX_BANK_0]; nSampleBankSize[SFX_BANK_PED_COMMENTS] = _nSampleDataEndOffset - nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS]; return true; } void cSampleManager::SetStreamedFileLoopFlag(uint8 nLoopFlag, uint8 nChannel) { nStreamLoopedFlag[nChannel] = nLoopFlag; } #endif ================================================ FILE: src/audio/soundlist.h ================================================ #pragma once enum eSound { SOUND_CAR_DOOR_CLOSE_BONNET = 0, SOUND_CAR_DOOR_CLOSE_BUMPER, SOUND_CAR_DOOR_CLOSE_FRONT_LEFT, SOUND_CAR_DOOR_CLOSE_FRONT_RIGHT, SOUND_CAR_DOOR_CLOSE_BACK_LEFT, SOUND_CAR_DOOR_CLOSE_BACK_RIGHT, SOUND_CAR_DOOR_OPEN_BONNET, SOUND_CAR_DOOR_OPEN_BUMPER, SOUND_CAR_DOOR_OPEN_FRONT_LEFT, SOUND_CAR_DOOR_OPEN_FRONT_RIGHT, SOUND_CAR_DOOR_OPEN_BACK_LEFT, SOUND_CAR_DOOR_OPEN_BACK_RIGHT, SOUND_CAR_WINDSHIELD_CRACK, SOUND_CAR_JUMP, SOUND_CAR_JUMP_2, SOUND_CAR_TYRE_POP, SOUND_16, SOUND_17, SOUND_CAR_ENGINE_START, SOUND_CAR_LIGHT_BREAK, SOUND_CAR_HYDRAULIC_1, SOUND_CAR_HYDRAULIC_2, SOUND_CAR_HYDRAULIC_3, SOUND_CAR_JERK, SOUND_CAR_SPLASH, SOUND_BOAT_SLOWDOWN, SOUND_TRAIN_DOOR_CLOSE, SOUND_TRAIN_DOOR_OPEN, SOUND_CAR_TANK_TURRET_ROTATE, SOUND_CAR_BOMB_TICK, SOUND_PLANE_ON_GROUND, SOUND_HELI_BLADE, SOUND_32, SOUND_STEP_START, SOUND_STEP_END, SOUND_FALL_LAND, SOUND_FALL_COLLAPSE, SOUND_FIGHT_37, SOUND_FIGHT_38, SOUND_FIGHT_39, SOUND_FIGHT_40, SOUND_FIGHT_41, SOUND_FIGHT_42, SOUND_FIGHT_43, SOUND_FIGHT_44, SOUND_FIGHT_45, SOUND_FIGHT_46, SOUND_FIGHT_47, SOUND_FIGHT_48, SOUND_49, SOUND_WEAPON_BAT_ATTACK, SOUND_WEAPON_KNIFE_ATTACK, SOUND_WEAPON_CHAINSAW_ATTACK, SOUND_WEAPON_CHAINSAW_IDLE, SOUND_WEAPON_CHAINSAW_MADECONTACT, SOUND_WEAPON_SHOT_FIRED, SOUND_WEAPON_RELOAD, SOUND_WEAPON_AK47_BULLET_ECHO, SOUND_WEAPON_FLAMETHROWER_FIRE, SOUND_WEAPON_SNIPER_SHOT_NO_ZOOM, SOUND_WEAPON_ROCKET_SHOT_NO_ZOOM, SOUND_WEAPON_HIT_PED, SOUND_WEAPON_HIT_VEHICLE, SOUND_GARAGE_NO_MONEY, SOUND_GARAGE_BAD_VEHICLE, SOUND_GARAGE_OPENING, SOUND_GARAGE_BOMB_ALREADY_SET, SOUND_GARAGE_BOMB1_SET, SOUND_GARAGE_BOMB2_SET, SOUND_GARAGE_BOMB3_SET, SOUND_70, SOUND_71, SOUND_GARAGE_VEHICLE_DECLINED, SOUND_GARAGE_VEHICLE_ACCEPTED, SOUND_GARAGE_DOOR_CLOSED, SOUND_GARAGE_DOOR_OPENED, SOUND_CRANE_PICKUP, SOUND_PICKUP_WEAPON_BOUGHT, SOUND_PICKUP_WEAPON, SOUND_PICKUP_HEALTH, SOUND_80, SOUND_81, SOUND_PICKUP_ADRENALINE, SOUND_PICKUP_ARMOUR, SOUND_PICKUP_BONUS, SOUND_PICKUP_MONEY, SOUND_PICKUP_HIDDEN_PACKAGE, SOUND_PICKUP_PACMAN_PILL, SOUND_PICKUP_PACMAN_PACKAGE, SOUND_PICKUP_FLOAT_PACKAGE, SOUND_BOMB_TIMED_ACTIVATED, SOUND_91, SOUND_BOMB_ONIGNITION_ACTIVATED, SOUND_BOMB_TICK, SOUND_RAMPAGE_START, SOUND_RAMPAGE_ONGOING, SOUND_RAMPAGE_PASSED, SOUND_RAMPAGE_FAILED, SOUND_RAMPAGE_KILL, SOUND_RAMPAGE_CAR_BLOWN, SOUND_EVIDENCE_PICKUP, SOUND_UNLOAD_GOLD, SOUND_PAGER, SOUND_PED_DEATH, SOUND_PED_DAMAGE, SOUND_PED_HIT, SOUND_PED_LAND, SOUND_PED_BULLET_HIT, SOUND_PED_BURNING, SOUND_PED_PLAYER_REACTTOCOP, SOUND_PED_ARREST_COP, SOUND_PED_MIAMIVICE_EXITING_CAR, SOUND_PED_COP_HELIPILOTPHRASE, SOUND_PED_PULLOUTWEAPON, SOUND_PED_HELI_PLAYER_FOUND = 114, SOUND_PED_VCPA_PLAYER_FOUND = 115, SOUND_PED_ON_FIRE, SOUND_PED_AIMING, SOUND_PED_HANDS_UP, SOUND_PED_HANDS_COWER, SOUND_PED_FLEE_SPRINT, SOUND_PED_CAR_JACKING, SOUND_PED_MUGGING, SOUND_PED_CAR_JACKED, SOUND_PED_ROBBED, SOUND_PED_ACCIDENTREACTION1, SOUND_PED_UNK_126, SOUND_PED_PLAYER_AFTERSEX, SOUND_PED_PLAYER_BEFORESEX, SOUND_PED_COP_UNK_129, // also used for medics SOUND_PED_COP_MANYCOPSAROUND, // also used for medics SOUND_PED_GUNAIMEDAT2, SOUND_PED_COP_ALONE, // also used for medics SOUND_PED_GUNAIMEDAT3, SOUND_PED_COP_REACTION, SOUND_PED_COP_LITTLECOPSAROUND, // also used for medics SOUND_PED_PLAYER_FARFROMCOPS, // also used for medics SOUND_PED_TAXI_WAIT, SOUND_PED_ATTACK, SOUND_PED_DEFEND, SOUND_PED_HEALING, SOUND_PED_LEAVE_VEHICLE, SOUND_PED_EVADE, SOUND_PED_FLEE_RUN, SOUND_PED_CRASH_VEHICLE, SOUND_PED_CRASH_CAR, SOUND_PED_ANNOYED_DRIVER, SOUND_PED_147, SOUND_PED_SOLICIT, SOUND_PED_149, SOUND_PED_150, SOUND_PED_EXTINGUISHING_FIRE, SOUND_PED_WAIT_DOUBLEBACK, SOUND_153, SOUND_PED_CHAT_SEXY, SOUND_PED_CHAT_EVENT, SOUND_PED_PED_COLLISION, SOUND_PED_CHAT, SOUND_PED_TAXI_CALL, SOUND_RACE_START_3, SOUND_RACE_START_2, SOUND_RACE_START_1, SOUND_RACE_START_GO, SOUND_SPLASH, SOUND_WATER_FALL, SOUND_SPLATTER, SOUND_CAR_PED_COLLISION, SOUND_CLOCK_TICK, SOUND_PART_MISSION_COMPLETE, SOUND_FRONTEND_MENU_STARTING, // same sound as SOUND_HUD // TODO(Miami): What are 170-175?? SOUND_FRONTEND_RADIO_TURN_OFF = 176, // those 2 are same sound SOUND_FRONTEND_RADIO_TURN_ON, SOUND_FRONTEND_HURRICANE, // yes, frontend SOUND_HUD, SOUND_180, SOUND_181, SOUND_182, SOUND_LIGHTNING, SOUND_BULLETTRACE_1, SOUND_BULLETTRACE_2, SOUND_186, // makes same sound as 40 SOUND_187, // makes same sound as 46 SOUND_MELEE_ATTACK_START, SOUND_SKATING, SOUND_WEAPON_MINIGUN_ATTACK, SOUND_WEAPON_MINIGUN_2, SOUND_WEAPON_MINIGUN_3, SOUND_AMMUNATION_IMRAN_ARM_BOMB, SOUND_RADIO_CHANGE, SOUND_FRONTEND_HIGHLIGHT_OPTION, SOUND_FRONTEND_ENTER_OR_ADJUST, SOUND_FRONTEND_BACK, SOUND_FRONTEND_FAIL, SOUND_FRONTEND_AUDIO_TEST, SOUND_INJURED_PED_MALE_OUCH, SOUND_INJURED_PED_FEMALE, SOUND_SHIRT_WIND_FLAP, SOUND_SET_203, SOUND_TOTAL_SOUNDS = 204, SOUND_NO_SOUND = 205, }; enum eScriptSounds { SCRIPT_SOUND_BANK_ALARM_LOOP = 0, SCRIPT_SOUND_PART_MISSION_COMPLETE, SCRIPT_SOUND_POLICE_CELL_DOOR_SLIDING_LOOP, SCRIPT_SOUND_POLICE_CELL_DOOR_CLUNK, SCRIPT_SOUND_GARAGE_DOOR_SLIDING_LOOP, SCRIPT_SOUND_GARAGE_DOOR_CLUNK, SCRIPT_SOUND_SNORING_LOOP, SCRIPT_SOUND_RACE_START_3, SCRIPT_SOUND_RACE_START_2, SCRIPT_SOUND_RACE_START_1, SCRIPT_SOUND_RACE_START_GO, SCRIPT_SOUND_SHOOTING_RANGE_TARGET_MOVING_LOOP, SCRIPT_SOUND_SHOOTING_RANGE_TARGET_HIT, SCRIPT_SOUND_AMMUNATION_BUY_WEAPON, SCRIPT_SOUND_AMMUNATION_BUY_WEAPON_DENIED, SCRIPT_SOUND_WMYCW_TICKET_SPEECH, SCRIPT_SOUND_IMRAN_ARM_BOMB, SCRIPT_SOUND_ANDY_SNIPER_SHOT, SCRIPT_SOUND_WILLIE_CARD_SWIPE, SCRIPT_SOUND_MALE_AMBULANCE_OUCH, SCRIPT_SOUND_FEMALE_AMBULANCE_OUCH, SCRIPT_SOUND_BUILDING_BAR_1, SCRIPT_SOUND_BUILDING_BAR_2, SCRIPT_SOUND_BUILDING_BAR_3, SCRIPT_SOUND_BUILDING_BAR_4, SCRIPT_SOUND_BUILDING_BIKER_BAR, SCRIPT_SOUND_BUILDING_CHURCH, SCRIPT_SOUND_BUILDING_CLUB, SCRIPT_SOUND_BUILDING_CUBA_1, SCRIPT_SOUND_BUILDING_CUBA_2, SCRIPT_SOUND_BUILDING_VOODOO, SCRIPT_SOUND_BUILDING_MUSIC_SHOP, SCRIPT_SOUND_BUILDING_STRIPCLUB_1, SCRIPT_SOUND_BUILDING_STRIPCLUB_2, SCRIPT_SOUND_BUILDING_SUPERSWEEP, SCRIPT_SOUND_SEAPLANE_LOW_FUEL, SCRIPT_SOUND_NEW_BUILDING_BAR_1, SCRIPT_SOUND_NEW_BUILDING_BAR_2, SCRIPT_SOUND_NEW_BUILDING_BAR_3, SCRIPT_SOUND_NEW_BUILDING_BAR_4, SCRIPT_SOUND_NEW_BUILDING_MALIBU_1, SCRIPT_SOUND_NEW_BUILDING_MALIBU_2, SCRIPT_SOUND_NEW_BUILDING_MALIBU_3, SCRIPT_SOUND_NEW_BUILDING_STRIP_1, SCRIPT_SOUND_NEW_BUILDING_STRIP_2, SCRIPT_SOUND_NEW_BUILDING_STRIP_3, SCRIPT_SOUND_NEW_BUILDING_CHURCH, SCRIPT_SOUND_NEW_BUILDING_FAN_1, SCRIPT_SOUND_NEW_BUILDING_FAN_2, SCRIPT_SOUND_NEW_BUILDING_INSECT_1, SCRIPT_SOUND_NEW_BUILDING_INSECT_2, SCRIPT_SOUND_NEW_WATERFALL, SCRIPT_SOUND_BULLET_HIT_GROUND_1, SCRIPT_SOUND_BULLET_HIT_GROUND_2, SCRIPT_SOUND_BULLET_HIT_GROUND_3, SCRIPT_SOUND_BULLET_HIT_WATER, // no sound SCRIPT_SOUND_PAYPHONE_RINGING, SCRIPT_SOUND_GLASS_BREAK_L, SCRIPT_SOUND_GLASS_BREAK_S, SCRIPT_SOUND_GLASS_CRACK, SCRIPT_SOUND_GLASS_LIGHT_BREAK, SCRIPT_SOUND_BOX_DESTROYED_1, SCRIPT_SOUND_BOX_DESTROYED_2, SCRIPT_SOUND_METAL_COLLISION, SCRIPT_SOUND_TIRE_COLLISION, SCRIPT_SOUND_HIT_BALL, SCRIPT_SOUND_GUNSHELL_DROP, SCRIPT_SOUND_GUNSHELL_DROP_SOFT, SCRIPT_SOUND_TOTAL, SCRIPT_SOUND_INVALID, }; ================================================ FILE: src/buildings/Building.cpp ================================================ #include "common.h" #include "Building.h" #include "Streaming.h" #include "Pools.h" void *CBuilding::operator new(size_t sz) { return CPools::GetBuildingPool()->New(); } void CBuilding::operator delete(void *p, size_t sz) { CPools::GetBuildingPool()->Delete((CBuilding*)p); } void CBuilding::ReplaceWithNewModel(int32 id) { DeleteRwObject(); if (CModelInfo::GetModelInfo(m_modelIndex)->GetNumRefs() == 0) CStreaming::RemoveModel(m_modelIndex); m_modelIndex = id; if(bIsBIGBuilding) if(m_level == LEVEL_GENERIC || m_level == CGame::currLevel) CStreaming::RequestModel(id, STREAMFLAGS_DONT_REMOVE); } bool IsBuildingPointerValid(CBuilding* pBuilding) { if (!pBuilding) return false; if (pBuilding->GetIsATreadable()) { int index = CPools::GetTreadablePool()->GetJustIndex_NoFreeAssert((CTreadable*)pBuilding); #ifdef FIX_BUGS return index >= 0 && index < CPools::GetTreadablePool()->GetSize(); #else return index >= 0 && index <= CPools::GetTreadablePool()->GetSize(); #endif } else { int index = CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pBuilding); #ifdef FIX_BUGS return index >= 0 && index < CPools::GetBuildingPool()->GetSize(); #else return index >= 0 && index <= CPools::GetBuildingPool()->GetSize(); #endif } } ================================================ FILE: src/buildings/Building.h ================================================ #pragma once #include "Entity.h" class CBuilding : public CEntity { public: CBuilding(void) { m_type = ENTITY_TYPE_BUILDING; bUsesCollision = true; } static void *operator new(size_t); static void operator delete(void*, size_t); void ReplaceWithNewModel(int32 id); virtual bool GetIsATreadable(void) { return false; } }; bool IsBuildingPointerValid(CBuilding*); ================================================ FILE: src/buildings/Solid.h ================================================ #pragma once #include "Entity.h" class CSolid : public CEntity { public: CSolid(void) { m_type = ENTITY_TYPE_BUILDING; bUsesCollision = true; } }; ================================================ FILE: src/buildings/Treadable.cpp ================================================ #include "common.h" #include "rpworld.h" #include "Treadable.h" #include "Pools.h" void *CTreadable::operator new(size_t sz) { return CPools::GetTreadablePool()->New(); } void CTreadable::operator delete(void *p, size_t sz) { CPools::GetTreadablePool()->Delete((CTreadable*)p); } ================================================ FILE: src/buildings/Treadable.h ================================================ #pragma once #include "Building.h" class CTreadable : public CBuilding { public: static void *operator new(size_t); static void operator delete(void*, size_t); bool GetIsATreadable(void) { return true; } }; ================================================ FILE: src/collision/ColBox.cpp ================================================ #include "common.h" #include "ColBox.h" void CColBox::Set(const CVector &min, const CVector &max, uint8 surf, uint8 piece) { this->min = min; this->max = max; this->surface = surf; this->piece = piece; } CColBox& CColBox::operator=(const CColBox& other) { min = other.min; max = other.max; surface = other.surface; piece = other.piece; return *this; } ================================================ FILE: src/collision/ColBox.h ================================================ #pragma once #include "SurfaceTable.h" struct CBox { CVector min; CVector max; CVector GetSize(void) { return max - min; } void Set(const CVector &min, const CVector &max) { this->min = min; this->max = max; } }; struct CColBox : public CBox { uint8 surface; uint8 piece; void Set(const CVector &min, const CVector &max, uint8 surf, uint8 piece); using CBox::Set; CColBox& operator=(const CColBox &other); }; ================================================ FILE: src/collision/ColLine.cpp ================================================ #include "common.h" #include "ColLine.h" void CColLine::Set(const CVector &p0, const CVector &p1) { this->p0 = p0; this->p1 = p1; } ================================================ FILE: src/collision/ColLine.h ================================================ #pragma once struct CColLine { // NB: this has to be compatible with two CVuVectors CVector p0; int pad0; CVector p1; int pad1; CColLine(void) { }; CColLine(const CVector &p0, const CVector &p1) { this->p0 = p0; this->p1 = p1; }; void Set(const CVector &p0, const CVector &p1); }; ================================================ FILE: src/collision/ColModel.cpp ================================================ #include "common.h" #include "ColModel.h" #include "Collision.h" #include "Game.h" #include "MemoryHeap.h" #include "Pools.h" CColModel::CColModel(void) { numSpheres = 0; spheres = nil; numLines = 0; lines = nil; numBoxes = 0; boxes = nil; numTriangles = 0; vertices = nil; triangles = nil; trianglePlanes = nil; level = LEVEL_GENERIC; // generic col slot ownsCollisionVolumes = true; } CColModel::~CColModel(void) { RemoveCollisionVolumes(); } void* CColModel::operator new(size_t) { CColModel* node = CPools::GetColModelPool()->New(); assert(node); return node; } void CColModel::operator delete(void *p, size_t) { CPools::GetColModelPool()->Delete((CColModel*)p); } void CColModel::RemoveCollisionVolumes(void) { if(ownsCollisionVolumes){ RwFree(spheres); RwFree(lines); RwFree(boxes); RwFree(vertices); RwFree(triangles); CCollision::RemoveTrianglePlanes(this); } numSpheres = 0; numLines = 0; numBoxes = 0; numTriangles = 0; spheres = nil; lines = nil; boxes = nil; vertices = nil; triangles = nil; } void CColModel::CalculateTrianglePlanes(void) { PUSH_MEMID(MEMID_COLLISION); // HACK: allocate space for one more element to stuff the link pointer into trianglePlanes = (CColTrianglePlane*)RwMalloc(sizeof(CColTrianglePlane) * (numTriangles+1)); REGISTER_MEMPTR(&trianglePlanes); for(int i = 0; i < numTriangles; i++) trianglePlanes[i].Set(vertices, triangles[i]); POP_MEMID(); } void CColModel::RemoveTrianglePlanes(void) { RwFree(trianglePlanes); trianglePlanes = nil; } void CColModel::SetLinkPtr(CLink *lptr) { assert(trianglePlanes); *(CLink**)ALIGNPTR(&trianglePlanes[numTriangles]) = lptr; } CLink* CColModel::GetLinkPtr(void) { assert(trianglePlanes); return *(CLink**)ALIGNPTR(&trianglePlanes[numTriangles]); } void CColModel::GetTrianglePoint(CVector &v, int i) const { v = vertices[i].Get(); } CColModel& CColModel::operator=(const CColModel &other) { int i; int numVerts; boundingSphere = other.boundingSphere; boundingBox = other.boundingBox; // copy spheres if(other.numSpheres){ if(numSpheres != other.numSpheres){ numSpheres = other.numSpheres; if(spheres) RwFree(spheres); spheres = (CColSphere*)RwMalloc(numSpheres*sizeof(CColSphere)); } for(i = 0; i < numSpheres; i++) spheres[i] = other.spheres[i]; }else{ numSpheres = 0; if(spheres) RwFree(spheres); spheres = nil; } // copy lines if(other.numLines){ if(numLines != other.numLines){ numLines = other.numLines; if(lines) RwFree(lines); lines = (CColLine*)RwMalloc(numLines*sizeof(CColLine)); } for(i = 0; i < numLines; i++) lines[i] = other.lines[i]; }else{ numLines = 0; if(lines) RwFree(lines); lines = nil; } // copy boxes if(other.numBoxes){ if(numBoxes != other.numBoxes){ numBoxes = other.numBoxes; if(boxes) RwFree(boxes); boxes = (CColBox*)RwMalloc(numBoxes*sizeof(CColBox)); } for(i = 0; i < numBoxes; i++) boxes[i] = other.boxes[i]; }else{ numBoxes = 0; if(boxes) RwFree(boxes); boxes = nil; } // copy mesh if(other.numTriangles){ // copy vertices numVerts = 0; for(i = 0; i < other.numTriangles; i++){ if(other.triangles[i].a > numVerts) numVerts = other.triangles[i].a; if(other.triangles[i].b > numVerts) numVerts = other.triangles[i].b; if(other.triangles[i].c > numVerts) numVerts = other.triangles[i].c; } numVerts++; if(vertices) RwFree(vertices); if(numVerts){ vertices = (CompressedVector*)RwMalloc(numVerts*sizeof(CompressedVector)); for(i = 0; i < numVerts; i++) vertices[i] = other.vertices[i]; } // copy triangles if(numTriangles != other.numTriangles){ numTriangles = other.numTriangles; if(triangles) RwFree(triangles); triangles = (CColTriangle*)RwMalloc(numTriangles*sizeof(CColTriangle)); } for(i = 0; i < numTriangles; i++) triangles[i] = other.triangles[i]; }else{ numTriangles = 0; if(triangles) RwFree(triangles); triangles = nil; if(vertices) RwFree(vertices); vertices = nil; } return *this; } ================================================ FILE: src/collision/ColModel.h ================================================ #pragma once #include "templates.h" #include "ColBox.h" #include "ColSphere.h" #include "ColLine.h" #include "ColPoint.h" #include "ColTriangle.h" struct CColModel { CSphere boundingSphere; CBox boundingBox; int16 numSpheres; int16 numBoxes; int16 numTriangles; int8 numLines; uint8 level; // colstore slot but probably still named level bool ownsCollisionVolumes; CColSphere *spheres; CColLine *lines; CColBox *boxes; CompressedVector *vertices; CColTriangle *triangles; CColTrianglePlane *trianglePlanes; CColModel(void); ~CColModel(void); void RemoveCollisionVolumes(void); void CalculateTrianglePlanes(void); void RemoveTrianglePlanes(void); CLink *GetLinkPtr(void); void SetLinkPtr(CLink*); void GetTrianglePoint(CVector &v, int i) const; void *operator new(size_t); void operator delete(void *p, size_t); CColModel& operator=(const CColModel& other); }; ================================================ FILE: src/collision/ColPoint.cpp ================================================ #include "common.h" #include "ColPoint.h" CColPoint& CColPoint::operator=(const CColPoint &other) { point = other.point; normal = other.normal; surfaceA = other.surfaceA; pieceA = other.pieceA; surfaceB = other.surfaceB; pieceB = other.pieceB; // no depth? return *this; } ================================================ FILE: src/collision/ColPoint.h ================================================ #pragma once struct CColPoint { CVector point; int pad1; // the surface normal on the surface of point CVector normal; int pad2; uint8 surfaceA; uint8 pieceA; uint8 surfaceB; uint8 pieceB; float depth; const CVector &GetNormal() { return normal; } float GetDepth() { return depth; } void Set(float depth, uint8 surfA, uint8 pieceA, uint8 surfB, uint8 pieceB) { this->depth = depth; this->surfaceA = surfA; this->pieceA = pieceA; this->surfaceB = surfB; this->pieceB = pieceB; } void Set(uint8 surfA, uint8 pieceA, uint8 surfB, uint8 pieceB) { this->surfaceA = surfA; this->pieceA = pieceA; this->surfaceB = surfB; this->pieceB = pieceB; } CColPoint &operator=(const CColPoint &other); }; ================================================ FILE: src/collision/ColSphere.cpp ================================================ #include "common.h" #include "ColSphere.h" #include "General.h" void CColSphere::Set(float radius, const CVector ¢er, uint8 surf, uint8 piece) { this->radius = radius; this->center = center; this->surface = surf; this->piece = piece; } bool CColSphere::IntersectRay(CVector const& from, CVector const& dir, CVector &entry, CVector &exit) { CVector distToCenter = from - center; float distToTouchSqr = distToCenter.MagnitudeSqr() - sq(radius); float root1, root2; if (!CGeneral::SolveQuadratic(1.0f, DotProduct(distToCenter, dir) * 2.f, distToTouchSqr, root1, root2)) return false; entry = from + dir * root1; exit = from + dir * root2; return true; } ================================================ FILE: src/collision/ColSphere.h ================================================ #pragma once #include "SurfaceTable.h" struct CSphere { // NB: this has to be compatible with a CVuVector CVector center; float radius; void Set(float radius, const CVector ¢er) { this->center = center; this->radius = radius; } }; struct CColSphere : public CSphere { uint8 surface; uint8 piece; void Set(float radius, const CVector ¢er, uint8 surf, uint8 piece); bool IntersectRay(CVector const &from, CVector const &dir, CVector &entry, CVector &exit); using CSphere::Set; }; ================================================ FILE: src/collision/ColStore.cpp ================================================ #include "common.h" #include "templates.h" #include "General.h" #include "ModelInfo.h" #include "Streaming.h" #include "FileLoader.h" #include "Script.h" #include "Timer.h" #include "Camera.h" #include "Frontend.h" #include "Physical.h" #include "ColStore.h" #include "VarConsole.h" #include "Pools.h" CPool *CColStore::ms_pColPool; #ifndef MASTER bool bDispColInMem; #endif void CColStore::Initialise(void) { if(ms_pColPool == nil) ms_pColPool = new CPool(COLSTORESIZE, "CollisionFiles"); AddColSlot("generic"); // slot 0. not streamed #ifndef MASTER VarConsole.Add("Display collision in memory", &bDispColInMem, true); #endif } void CColStore::Shutdown(void) { int i; for(i = 0; i < COLSTORESIZE; i++) RemoveColSlot(i); if(ms_pColPool) delete ms_pColPool; ms_pColPool = nil; } int CColStore::AddColSlot(const char *name) { ColDef *def = ms_pColPool->New(); assert(def); def->isLoaded = false; def->unused = 0; def->bounds.left = 1000000.0f; def->bounds.top = 1000000.0f; def->bounds.right = -1000000.0f; def->bounds.bottom = -1000000.0f; def->minIndex = INT16_MAX; def->maxIndex = INT16_MIN; strcpy(def->name, name); return ms_pColPool->GetJustIndex(def); } void CColStore::RemoveColSlot(int slot) { if(GetSlot(slot)){ if(GetSlot(slot)->isLoaded) RemoveCol(slot); ms_pColPool->Delete(GetSlot(slot)); } } int CColStore::FindColSlot(const char *name) { ColDef *def; int size = ms_pColPool->GetSize(); for(int i = 0; i < size; i++){ def = GetSlot(i); if(def && !CGeneral::faststricmp(def->name, name)) return i; } return -1; } char* CColStore::GetColName(int32 slot) { return GetSlot(slot)->name; } CRect& CColStore::GetBoundingBox(int32 slot) { return GetSlot(slot)->bounds; } void CColStore::IncludeModelIndex(int32 slot, int32 modelIndex) { ColDef *def = GetSlot(slot); if(modelIndex < def->minIndex) def->minIndex = modelIndex; if(modelIndex > def->maxIndex) def->maxIndex = modelIndex; } bool CColStore::LoadCol(int32 slot, uint8 *buffer, int32 bufsize) { bool success; ColDef *def = GetSlot(slot); if(def->minIndex > def->maxIndex) success = CFileLoader::LoadCollisionFileFirstTime(buffer, bufsize, slot); else success = CFileLoader::LoadCollisionFile(buffer, bufsize, slot); if(success) def->isLoaded = true; else debug("Failed to load Collision\n"); return success; } void CColStore::RemoveCol(int32 slot) { int id; GetSlot(slot)->isLoaded = false; for(id = 0; id < MODELINFOSIZE; id++){ CBaseModelInfo *mi = CModelInfo::GetModelInfo(id); if(mi){ CColModel *col = mi->GetColModel(); if(col && col->level == slot) col->RemoveCollisionVolumes(); } } } void CColStore::LoadAllCollision(void) { int i; for(i = 1; i < COLSTORESIZE; i++) if(GetSlot(i)) CStreaming::RequestCol(i, 0); CStreaming::LoadAllRequestedModels(false); } void CColStore::RemoveAllCollision(void) { int i; for(i = 1; i < COLSTORESIZE; i++) if(GetSlot(i)) if(CStreaming::CanRemoveCol(i)) CStreaming::RemoveCol(i); } static bool bLoadAtSecondPosition; static CVector2D secondPosition; void CColStore::AddCollisionNeededAtPosn(const CVector2D &pos) { bLoadAtSecondPosition = true; secondPosition = pos; } void CColStore::LoadCollision(const CVector2D &pos) { int i; if(CStreaming::ms_disableStreaming) return; for(i = 1; i < COLSTORESIZE; i++){ if(GetSlot(i) == nil) continue; bool wantThisOne = false; if(GetBoundingBox(i).IsPointInside(pos) || bLoadAtSecondPosition && GetBoundingBox(i).IsPointInside(secondPosition, -119.0f) || strcmp(GetColName(i), "yacht") == 0){ wantThisOne = true; }else{ for (int j = 0; j < MAX_CLEANUP; j++) { CPhysical* pEntity = nil; cleanup_entity_struct* pCleanup = &CTheScripts::MissionCleanUp.m_sEntities[i]; if (pCleanup->type == CLEANUP_CAR) { pEntity = CPools::GetVehiclePool()->GetAt(pCleanup->id); if (!pEntity || pEntity->GetStatus() == STATUS_WRECKED) continue; } else if (pCleanup->type == CLEANUP_CHAR) { pEntity = CPools::GetPedPool()->GetAt(pCleanup->id); if (!pEntity || ((CPed*)pEntity)->DyingOrDead()) continue; } if (pEntity && !pEntity->bDontLoadCollision && !pEntity->bIsFrozen) { if (GetBoundingBox(i).IsPointInside(pEntity->GetPosition(), -80.0f)) wantThisOne = true; } } } if(wantThisOne) CStreaming::RequestCol(i, STREAMFLAGS_PRIORITY); else CStreaming::RemoveCol(i); } bLoadAtSecondPosition = false; } void CColStore::RequestCollision(const CVector2D &pos) { int i; for(i = 1; i < COLSTORESIZE; i++) if(GetSlot(i) && GetBoundingBox(i).IsPointInside(pos, -115.0f)) CStreaming::RequestCol(i, STREAMFLAGS_PRIORITY); } void CColStore::EnsureCollisionIsInMemory(const CVector2D &pos) { int i; if(CStreaming::ms_disableStreaming) return; for(i = 1; i < COLSTORESIZE; i++) if(GetSlot(i) && GetBoundingBox(i).IsPointInside(pos, -110.0f) && !CStreaming::HasColLoaded(i)){ CStreaming::RequestCol(i, 0); if(TheCamera.GetScreenFadeStatus() == FADE_0) FrontEndMenuManager.MessageScreen("LOADCOL", false); CTimer::Suspend(); CStreaming::LoadAllRequestedModels(false); CTimer::Resume(); } } bool CColStore::HasCollisionLoaded(const CVector2D &pos) { int i; for(i = 1; i < COLSTORESIZE; i++) if(GetSlot(i) && GetBoundingBox(i).IsPointInside(pos, -115.0f) && !GetSlot(i)->isLoaded) return false; return true; } ================================================ FILE: src/collision/ColStore.h ================================================ #pragma once #include "templates.h" struct ColDef { // made up name int32 unused; bool isLoaded; CRect bounds; char name[20]; int16 minIndex; int16 maxIndex; }; class CColStore { static CPool *ms_pColPool; public: static void Initialise(void); static void Shutdown(void); static int AddColSlot(const char *name); static void RemoveColSlot(int32 slot); static int FindColSlot(const char *name); static char *GetColName(int32 slot); static CRect &GetBoundingBox(int32 slot); static void IncludeModelIndex(int32 slot, int32 modelIndex); static bool LoadCol(int32 storeID, uint8 *buffer, int32 bufsize); static void RemoveCol(int32 slot); static void AddCollisionNeededAtPosn(const CVector2D &pos); static void LoadAllCollision(void); static void RemoveAllCollision(void); static void LoadCollision(const CVector2D &pos); static void RequestCollision(const CVector2D &pos); static void EnsureCollisionIsInMemory(const CVector2D &pos); static bool HasCollisionLoaded(const CVector2D &pos); static ColDef *GetSlot(int slot) { assert(slot >= 0); assert(ms_pColPool); assert(slot < ms_pColPool->GetSize()); return ms_pColPool->GetSlot(slot); } }; ================================================ FILE: src/collision/ColTriangle.cpp ================================================ #include "common.h" #include "ColTriangle.h" #ifdef VU_COLLISION void CColTrianglePlane::Set(const CVector &va, const CVector &vb, const CVector &vc) { CVector norm = CrossProduct(vc-va, vb-va); norm.Normalise(); float d = DotProduct(norm, va); normal.x = norm.x*4096.0f; normal.y = norm.y*4096.0f; normal.z = norm.z*4096.0f; dist = d*128.0f; } #else void CColTrianglePlane::Set(const CVector &va, const CVector &vb, const CVector &vc) { normal = CrossProduct(vc-va, vb-va); normal.Normalise(); dist = DotProduct(normal, va); CVector an(Abs(normal.x), Abs(normal.y), Abs(normal.z)); // find out largest component and its direction if(an.x > an.y && an.x > an.z) dir = normal.x < 0.0f ? DIR_X_NEG : DIR_X_POS; else if(an.y > an.z) dir = normal.y < 0.0f ? DIR_Y_NEG : DIR_Y_POS; else dir = normal.z < 0.0f ? DIR_Z_NEG : DIR_Z_POS; } #endif ================================================ FILE: src/collision/ColTriangle.h ================================================ #pragma once #include "CompressedVector.h" enum Direction { DIR_X_POS, DIR_X_NEG, DIR_Y_POS, DIR_Y_NEG, DIR_Z_POS, DIR_Z_NEG, }; struct CColTriangle { uint16 a; uint16 b; uint16 c; uint8 surface; void Set(int a, int b, int c, uint8 surf) { this->a = a; this->b = b; this->c = c; this->surface = surf; } }; struct CColTrianglePlane { #ifdef VU_COLLISION CompressedVector normal; int16 dist; void Set(const CVector &va, const CVector &vb, const CVector &vc); void Set(const CompressedVector *v, CColTriangle &tri) { Set(v[tri.a].Get(), v[tri.b].Get(), v[tri.c].Get()); } void GetNormal(CVector &n) const { n.x = normal.x/4096.0f; n.y = normal.y/4096.0f; n.z = normal.z/4096.0f; } float CalcPoint(const CVector &v) const { CVector n; GetNormal(n); return DotProduct(n, v) - dist/128.0f; }; #ifdef GTA_PS2 void Unpack(uint128 &qword) const { __asm__ volatile ( "lh $8, 0(%1)\n" "lh $9, 2(%1)\n" "lh $10, 4(%1)\n" "lh $11, 6(%1)\n" "pextlw $10, $8\n" "pextlw $11, $9\n" "pextlw $2, $11, $10\n" "sq $2, %0\n" : "=m" (qword) : "r" (this) : "$8", "$9", "$10", "$11", "$2" ); } #else void Unpack(int32 *qword) const { qword[0] = normal.x; qword[1] = normal.y; qword[2] = normal.z; qword[3] = dist; } #endif #else CVector normal; float dist; uint8 dir; void Set(const CVector &va, const CVector &vb, const CVector &vc); void Set(const CompressedVector *v, CColTriangle &tri) { Set(v[tri.a].Get(), v[tri.b].Get(), v[tri.c].Get()); } void GetNormal(CVector &n) const { n = normal; } float GetNormalX() const { return normal.x; } float GetNormalY() const { return normal.y; } float GetNormalZ() const { return normal.z; } float CalcPoint(const CVector &v) const { return DotProduct(normal, v) - dist; }; #endif }; ================================================ FILE: src/collision/Collision.cpp ================================================ #include "common.h" #include "VuVector.h" #include "main.h" #include "Lists.h" #include "Game.h" #include "Zones.h" #include "General.h" #include "ZoneCull.h" #include "World.h" #include "Entity.h" #include "Train.h" #include "Streaming.h" #include "Pad.h" #include "DMAudio.h" #include "Population.h" #include "FileLoader.h" #include "Replay.h" #include "CutsceneMgr.h" #include "RenderBuffer.h" #include "SurfaceTable.h" #include "Lines.h" #include "Collision.h" #include "Camera.h" #include "ColStore.h" #ifdef VU_COLLISION #include "VuCollision.h" inline int GetVUresult(void) { #ifdef GTA_PS2 int ret; __asm__ volatile ( "cfc2.i %0,vi01\n" // .i important! wait for VU0 to finish : "=r" (ret) ); return ret; #else return vi01; #endif } inline int GetVUresult(CVuVector &point, CVuVector &normal, float &dist) { #ifdef GTA_PS2 int ret; __asm__ volatile ( "cfc2.i %0,vi01\n" // .i important! wait for VU0 to finish "sqc2 vf01,(%1)\n" "sqc2 vf02,(%2)\n" "qmfc2 $12,vf03\n" "sw $12,(%3)\n" : "=r" (ret) : "r" (&point), "r" (&normal), "r" (&dist) : "$12" ); return ret; #else point = vf01; normal = vf02; dist = vf03.x; return vi01; #endif } #endif eLevelName CCollision::ms_collisionInMemory; CLinkList CCollision::ms_colModelCache; void CCollision::Init(void) { ms_colModelCache.Init(NUMCOLCACHELINKS); ms_collisionInMemory = LEVEL_GENERIC; CColStore::Initialise(); } void CCollision::Shutdown(void) { ms_colModelCache.Shutdown(); CColStore::Shutdown(); } void CCollision::Update(void) { } // unused eLevelName GetCollisionInSectorList(CPtrList &list) { CPtrNode *node; CEntity *e; int level; for(node = list.first; node; node = node->next){ e = (CEntity*)node->item; level = CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel()->level; if(level != LEVEL_GENERIC) return (eLevelName)level; } return LEVEL_GENERIC; } // unused // Get a level this sector is in based on collision models eLevelName GetCollisionInSector(CSector §) { int level; level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_BUILDINGS]); if(level == LEVEL_GENERIC) level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); if(level == LEVEL_GENERIC) level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_OBJECTS]); if(level == LEVEL_GENERIC) level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_OBJECTS_OVERLAP]); if(level == LEVEL_GENERIC) level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_DUMMIES]); if(level == LEVEL_GENERIC) level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_DUMMIES_OVERLAP]); return (eLevelName)level; } void CCollision::LoadCollisionWhenINeedIt(bool forceChange) { } void CCollision::SortOutCollisionAfterLoad(void) { CColStore::LoadCollision(TheCamera.GetPosition()); CStreaming::LoadAllRequestedModels(false); } void CCollision::LoadCollisionScreen(eLevelName level) { static Const char *levelNames[4] = { "", "IND_ZON", "COM_ZON", "SUB_ZON" }; // Why twice? LoadingIslandScreen(levelNames[level]); LoadingIslandScreen(levelNames[level]); } // // Test // bool CCollision::TestSphereSphere(const CSphere &s1, const CSphere &s2) { float d = s1.radius + s2.radius; return (s1.center - s2.center).MagnitudeSqr() < d*d; } bool CCollision::TestSphereBox(const CSphere &sph, const CBox &box) { if(sph.center.x + sph.radius < box.min.x) return false; if(sph.center.x - sph.radius > box.max.x) return false; if(sph.center.y + sph.radius < box.min.y) return false; if(sph.center.y - sph.radius > box.max.y) return false; if(sph.center.z + sph.radius < box.min.z) return false; if(sph.center.z - sph.radius > box.max.z) return false; return true; } bool CCollision::TestLineBox(const CColLine &line, const CBox &box) { float t, x, y, z; // If either line point is in the box, we have a collision if(line.p0.x > box.min.x && line.p0.x < box.max.x && line.p0.y > box.min.y && line.p0.y < box.max.y && line.p0.z > box.min.z && line.p0.z < box.max.z) return true; if(line.p1.x > box.min.x && line.p1.x < box.max.x && line.p1.y > box.min.y && line.p1.y < box.max.y && line.p1.z > box.min.z && line.p1.z < box.max.z) return true; // check if points are on opposite sides of min x plane if((box.min.x - line.p1.x) * (box.min.x - line.p0.x) < 0.0f){ // parameter along line where we intersect t = (box.min.x - line.p0.x) / (line.p1.x - line.p0.x); // y of intersection y = line.p0.y + (line.p1.y - line.p0.y)*t; if(y > box.min.y && y < box.max.y){ // z of intersection z = line.p0.z + (line.p1.z - line.p0.z)*t; if(z > box.min.z && z < box.max.z) return true; } } // same test with max x plane if((line.p1.x - box.max.x) * (line.p0.x - box.max.x) < 0.0f){ t = (line.p0.x - box.max.x) / (line.p0.x - line.p1.x); y = line.p0.y + (line.p1.y - line.p0.y)*t; if(y > box.min.y && y < box.max.y){ z = line.p0.z + (line.p1.z - line.p0.z)*t; if(z > box.min.z && z < box.max.z) return true; } } // min y plne if((box.min.y - line.p0.y) * (box.min.y - line.p1.y) < 0.0f){ t = (box.min.y - line.p0.y) / (line.p1.y - line.p0.y); x = line.p0.x + (line.p1.x - line.p0.x)*t; if(x > box.min.x && x < box.max.x){ z = line.p0.z + (line.p1.z - line.p0.z)*t; if(z > box.min.z && z < box.max.z) return true; } } // max y plane if((line.p0.y - box.max.y) * (line.p1.y - box.max.y) < 0.0f){ t = (line.p0.y - box.max.y) / (line.p0.y - line.p1.y); x = line.p0.x + (line.p1.x - line.p0.x)*t; if(x > box.min.x && x < box.max.x){ z = line.p0.z + (line.p1.z - line.p0.z)*t; if(z > box.min.z && z < box.max.z) return true; } } // min z plne if((box.min.z - line.p0.z) * (box.min.z - line.p1.z) < 0.0f){ t = (box.min.z - line.p0.z) / (line.p1.z - line.p0.z); x = line.p0.x + (line.p1.x - line.p0.x)*t; if(x > box.min.x && x < box.max.x){ y = line.p0.y + (line.p1.y - line.p0.y)*t; if(y > box.min.y && y < box.max.y) return true; } } // max z plane if((line.p0.z - box.max.z) * (line.p1.z - box.max.z) < 0.0f){ t = (line.p0.z - box.max.z) / (line.p0.z - line.p1.z); x = line.p0.x + (line.p1.x - line.p0.x)*t; if(x > box.min.x && x < box.max.x){ y = line.p0.y + (line.p1.y - line.p0.y)*t; if(y > box.min.y && y < box.max.y) return true; } } return false; } bool CCollision::TestVerticalLineBox(const CColLine &line, const CBox &box) { if(line.p0.x <= box.min.x) return false; if(line.p0.y <= box.min.y) return false; if(line.p0.x >= box.max.x) return false; if(line.p0.y >= box.max.y) return false; if(line.p0.z < line.p1.z){ if(line.p0.z > box.max.z) return false; if(line.p1.z < box.min.z) return false; }else{ if(line.p1.z > box.max.z) return false; if(line.p0.z < box.min.z) return false; } return true; } bool CCollision::TestLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane) { #ifdef VU_COLLISION // not used in favour of optimized loops VuTriangle vutri; verts[tri.a].Unpack(vutri.v0); verts[tri.b].Unpack(vutri.v1); verts[tri.c].Unpack(vutri.v2); plane.Unpack(vutri.plane); LineToTriangleCollisionCompressed(*(CVuVector*)&line.p0, *(CVuVector*)&line.p1, vutri); if(GetVUresult()) return true; return false; #else float t; CVector normal; plane.GetNormal(normal); // if points are on the same side, no collision if(plane.CalcPoint(line.p0) * plane.CalcPoint(line.p1) > 0.0f) return false; float p0dist = DotProduct(line.p1 - line.p0, normal); #ifdef FIX_BUGS // line lines in the plane, assume no collision if (p0dist == 0.0f) return false; #endif // intersection parameter on line t = -plane.CalcPoint(line.p0) / p0dist; // find point of intersection CVector p = line.p0 + (line.p1-line.p0)*t; const CVector &va = verts[tri.a].Get(); const CVector &vb = verts[tri.b].Get(); const CVector &vc = verts[tri.c].Get(); CVector2D vec1, vec2, vec3, vect; // We do the test in 2D. With the plane direction we // can figure out how to project the vectors. // normal = (c-a) x (b-a) switch(plane.dir){ case DIR_X_POS: vec1.x = va.y; vec1.y = va.z; vec2.x = vc.y; vec2.y = vc.z; vec3.x = vb.y; vec3.y = vb.z; vect.x = p.y; vect.y = p.z; break; case DIR_X_NEG: vec1.x = va.y; vec1.y = va.z; vec2.x = vb.y; vec2.y = vb.z; vec3.x = vc.y; vec3.y = vc.z; vect.x = p.y; vect.y = p.z; break; case DIR_Y_POS: vec1.x = va.z; vec1.y = va.x; vec2.x = vc.z; vec2.y = vc.x; vec3.x = vb.z; vec3.y = vb.x; vect.x = p.z; vect.y = p.x; break; case DIR_Y_NEG: vec1.x = va.z; vec1.y = va.x; vec2.x = vb.z; vec2.y = vb.x; vec3.x = vc.z; vec3.y = vc.x; vect.x = p.z; vect.y = p.x; break; case DIR_Z_POS: vec1.x = va.x; vec1.y = va.y; vec2.x = vc.x; vec2.y = vc.y; vec3.x = vb.x; vec3.y = vb.y; vect.x = p.x; vect.y = p.y; break; case DIR_Z_NEG: vec1.x = va.x; vec1.y = va.y; vec2.x = vb.x; vec2.y = vb.y; vec3.x = vc.x; vec3.y = vc.y; vect.x = p.x; vect.y = p.y; break; default: assert(0); } // This is our triangle: // 3-------2 // \ P / // \ / // \ / // 1 // We can use the "2d cross product" to check on which side // a vector is of another. Test is true if point is inside of all edges. if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return false; if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return false; if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return false; return true; #endif } // Test if line segment intersects with sphere. // If the first point is inside the sphere this test does not register a collision! // The code is reversed from the original code and rather ugly, see Process for a clear version. // TODO: actually rewrite this mess bool CCollision::TestLineSphere(const CColLine &line, const CColSphere &sph) { CVector v01 = line.p1 - line.p0; // vector from p0 to p1 CVector v0c = sph.center - line.p0; // vector from p0 to center float linesq = v01.MagnitudeSqr(); // I leave in the strange -2 factors even though they serve no real purpose float projline = -2.0f * DotProduct(v01, v0c); // project v0c onto line // Square of tangent from p0 multiplied by line length so we can compare with projline. // The length of the tangent would be this: Sqrt((c-p0)^2 - r^2). // Negative if p0 is inside the sphere! This breaks the test! float tansq = 4.0f * linesq * (sph.center.MagnitudeSqr() - 2.0f*DotProduct(sph.center, line.p0) + line.p0.MagnitudeSqr() - sph.radius*sph.radius); float diffsq = projline*projline - tansq; // if diffsq < 0 that means the line is a passant, so no intersection if(diffsq < 0.0f) return false; // projline (negative in GTA for some reason) is the point on the line // in the middle of the two intersection points (startin from p0). // Sqrt(diffsq) somehow works out to be the distance from that // midpoint to the intersection points. // So subtract that and get rid of the awkward scaling: float f = (-projline - Sqrt(diffsq)) / (2.0f*linesq); // f should now be in range [0, 1] for [p0, p1] return f >= 0.0f && f <= 1.0f; } bool CCollision::TestSphereTriangle(const CColSphere &sphere, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane) { #ifdef VU_COLLISION // not used in favour of optimized loops VuTriangle vutri; verts[tri.a].Unpack(vutri.v0); verts[tri.b].Unpack(vutri.v1); verts[tri.c].Unpack(vutri.v2); plane.Unpack(vutri.plane); SphereToTriangleCollisionCompressed(*(CVuVector*)&sphere, vutri); if(GetVUresult()) return true; return false; #else // If sphere and plane don't intersect, no collision float planedist = plane.CalcPoint(sphere.center); if(Abs(planedist) > sphere.radius) return false; const CVector &va = verts[tri.a].Get(); const CVector &vb = verts[tri.b].Get(); const CVector &vc = verts[tri.c].Get(); // calculate two orthogonal basis vectors for the triangle CVector vec2 = vb - va; float len = vec2.Magnitude(); vec2 = vec2 * (1.0f/len); CVector normal; plane.GetNormal(normal); CVector vec1 = CrossProduct(vec2, normal); // We know A has local coordinate [0,0] and B has [0,len]. // Now calculate coordinates on triangle for these two vectors: CVector vac = vc - va; CVector vas = sphere.center - va; CVector2D b(0.0f, len); CVector2D c(DotProduct(vec1, vac), DotProduct(vec2, vac)); CVector2D s(DotProduct(vec1, vas), DotProduct(vec2, vas)); // The three triangle lines partition the space into 6 sectors, // find out in which the center lies. int insideAB = CrossProduct2D(s, b) >= 0.0f; int insideAC = CrossProduct2D(c, s) >= 0.0f; int insideBC = CrossProduct2D(s-b, c-b) >= 0.0f; int testcase = insideAB + insideAC + insideBC; float dist = 0.0f; switch(testcase){ case 0: return false; // shouldn't happen case 1: // closest to a vertex if(insideAB) dist = (sphere.center - vc).Magnitude(); else if(insideAC) dist = (sphere.center - vb).Magnitude(); else if(insideBC) dist = (sphere.center - va).Magnitude(); else assert(0); break; case 2: // closest to an edge // looks like original game as DistToLine manually inlined if(!insideAB) dist = DistToLine(&va, &vb, &sphere.center); else if(!insideAC) dist = DistToLine(&va, &vc, &sphere.center); else if(!insideBC) dist = DistToLine(&vb, &vc, &sphere.center); else assert(0); break; case 3: // center is in triangle dist = Abs(planedist); break; default: assert(0); } return dist < sphere.radius; #endif } bool CCollision::TestLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, bool ignoreSeeThrough, bool ignoreShootThrough) { #ifdef VU_COLLISION CMatrix matTransform; int i; // transform line to model space Invert(matrix, matTransform); CVuVector newline[2]; TransformPoints(newline, 2, matTransform, &line.p0, sizeof(CColLine)/2); // If we don't intersect with the bounding box, no chance on the rest if(!TestLineBox(*(CColLine*)newline, model.boundingBox)) return false; for(i = 0; i < model.numSpheres; i++){ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.spheres[i].surface)) continue; if(TestLineSphere(*(CColLine*)newline, model.spheres[i])) return true; } for(i = 0; i < model.numBoxes; i++){ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.boxes[i].surface)) continue; if(TestLineBox(*(CColLine*)newline, model.boxes[i])) return true; } CalculateTrianglePlanes(&model); int lastTest = -1; VuTriangle vutri; for(i = 0; i < model.numTriangles; i++){ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; CColTriangle *tri = &model.triangles[i]; model.vertices[tri->a].Unpack(vutri.v0); model.vertices[tri->b].Unpack(vutri.v1); model.vertices[tri->c].Unpack(vutri.v2); model.trianglePlanes[i].Unpack(vutri.plane); LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); lastTest = i; break; } #ifdef FIX_BUGS // no need to check first again i++; #endif for(; i < model.numTriangles; i++){ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; CColTriangle *tri = &model.triangles[i]; model.vertices[tri->a].Unpack(vutri.v0); model.vertices[tri->b].Unpack(vutri.v1); model.vertices[tri->c].Unpack(vutri.v2); model.trianglePlanes[i].Unpack(vutri.plane); if(GetVUresult()) return true; LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); lastTest = i; } if(lastTest != -1 && GetVUresult()) return true; return false; #else static CMatrix matTransform; int i; // transform line to model space Invert(matrix, matTransform); CColLine newline(matTransform * line.p0, matTransform * line.p1); // If we don't intersect with the bounding box, no chance on the rest if(!TestLineBox(newline, model.boundingBox)) return false; for(i = 0; i < model.numSpheres; i++){ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.spheres[i].surface)) continue; if(TestLineSphere(newline, model.spheres[i])) return true; } for(i = 0; i < model.numBoxes; i++){ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.boxes[i].surface)) continue; if(TestLineBox(newline, model.boxes[i])) return true; } CalculateTrianglePlanes(&model); for(i = 0; i < model.numTriangles; i++){ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; if(TestLineTriangle(newline, model.vertices, model.triangles[i], model.trianglePlanes[i])) return true; } return false; #endif } // TODO: TestPillWithSpheresInColModel, but only called from overloaded CWeapon::FireMelee which isn't used // // Process // // For Spheres mindist is the squared distance to its center // For Lines mindist is between [0,1] bool CCollision::ProcessSphereSphere(const CColSphere &s1, const CColSphere &s2, CColPoint &point, float &mindistsq) { CVector dist = s1.center - s2.center; float d = dist.Magnitude() - s2.radius; // distance from s1's center to s2 float depth = s1.radius - d; // sphere overlap if(d < 0.0f) d = 0.0f; // clamp to zero, i.e. if s1's center is inside s2 // no collision if sphere is not close enough if(d*d < mindistsq && d < s1.radius){ dist.Normalise(); point.point = s1.center - dist*d; point.normal = dist; #ifndef VU_COLLISION point.surfaceA = s1.surface; point.pieceA = s1.piece; point.surfaceB = s2.surface; point.pieceB = s2.piece; #endif point.depth = depth; mindistsq = d*d; // collision radius return true; } return false; } bool CCollision::ProcessSphereBox(const CColSphere &sph, const CColBox &box, CColPoint &point, float &mindistsq) { CVector p; CVector dist; // GTA's code is too complicated, uses a huge 3x3x3 if statement // we can simplify the structure a lot // first make sure we have a collision at all if(sph.center.x + sph.radius < box.min.x) return false; if(sph.center.x - sph.radius > box.max.x) return false; if(sph.center.y + sph.radius < box.min.y) return false; if(sph.center.y - sph.radius > box.max.y) return false; if(sph.center.z + sph.radius < box.min.z) return false; if(sph.center.z - sph.radius > box.max.z) return false; // Now find out where the sphere center lies in relation to all the sides int xpos = sph.center.x < box.min.x ? 1 : sph.center.x > box.max.x ? 2 : 0; int ypos = sph.center.y < box.min.y ? 1 : sph.center.y > box.max.y ? 2 : 0; int zpos = sph.center.z < box.min.z ? 1 : sph.center.z > box.max.z ? 2 : 0; if(xpos == 0 && ypos == 0 && zpos == 0){ // sphere is inside the box p = (box.min + box.max)*0.5f; dist = sph.center - p; float lensq = dist.MagnitudeSqr(); if(lensq < mindistsq){ point.normal = dist * (1.0f/Sqrt(lensq)); point.point = sph.center - point.normal; #ifndef VU_COLLISION point.surfaceA = sph.surface; point.pieceA = sph.piece; point.surfaceB = box.surface; point.pieceB = box.piece; #endif // find absolute distance to the closer side in each dimension float dx = dist.x > 0.0f ? box.max.x - sph.center.x : sph.center.x - box.min.x; float dy = dist.y > 0.0f ? box.max.y - sph.center.y : sph.center.y - box.min.y; float dz = dist.z > 0.0f ? box.max.z - sph.center.z : sph.center.z - box.min.z; // collision depth is maximum of that: if(dx > dy && dx > dz) point.depth = dx; else if(dy > dz) point.depth = dy; else point.depth = dz; return true; } }else{ // sphere is outside. // closest point on box: p.x = xpos == 1 ? box.min.x : xpos == 2 ? box.max.x : sph.center.x; p.y = ypos == 1 ? box.min.y : ypos == 2 ? box.max.y : sph.center.y; p.z = zpos == 1 ? box.min.z : zpos == 2 ? box.max.z : sph.center.z; dist = sph.center - p; float lensq = dist.MagnitudeSqr(); if(lensq < mindistsq){ float len = Sqrt(lensq); point.point = p; point.normal = dist * (1.0f/len); #ifndef VU_COLLISION point.surfaceA = sph.surface; point.pieceA = sph.piece; point.surfaceB = box.surface; point.pieceB = box.piece; #endif point.depth = sph.radius - len; mindistsq = lensq; return true; } } return false; } bool CCollision::ProcessLineBox(const CColLine &line, const CColBox &box, CColPoint &point, float &mindist) { float mint, t, x, y, z; CVector normal; CVector p; mint = 1.0f; // check if points are on opposite sides of min x plane if((box.min.x - line.p1.x) * (box.min.x - line.p0.x) < 0.0f){ // parameter along line where we intersect t = (box.min.x - line.p0.x) / (line.p1.x - line.p0.x); // y of intersection y = line.p0.y + (line.p1.y - line.p0.y)*t; if(y > box.min.y && y < box.max.y){ // z of intersection z = line.p0.z + (line.p1.z - line.p0.z)*t; if(z > box.min.z && z < box.max.z) if(t < mint){ mint = t; p = CVector(box.min.x, y, z); normal = CVector(-1.0f, 0.0f, 0.0f); } } } // max x plane if((line.p1.x - box.max.x) * (line.p0.x - box.max.x) < 0.0f){ t = (line.p0.x - box.max.x) / (line.p0.x - line.p1.x); y = line.p0.y + (line.p1.y - line.p0.y)*t; if(y > box.min.y && y < box.max.y){ z = line.p0.z + (line.p1.z - line.p0.z)*t; if(z > box.min.z && z < box.max.z) if(t < mint){ mint = t; p = CVector(box.max.x, y, z); normal = CVector(1.0f, 0.0f, 0.0f); } } } // min y plne if((box.min.y - line.p0.y) * (box.min.y - line.p1.y) < 0.0f){ t = (box.min.y - line.p0.y) / (line.p1.y - line.p0.y); x = line.p0.x + (line.p1.x - line.p0.x)*t; if(x > box.min.x && x < box.max.x){ z = line.p0.z + (line.p1.z - line.p0.z)*t; if(z > box.min.z && z < box.max.z) if(t < mint){ mint = t; p = CVector(x, box.min.y, z); normal = CVector(0.0f, -1.0f, 0.0f); } } } // max y plane if((line.p0.y - box.max.y) * (line.p1.y - box.max.y) < 0.0f){ t = (line.p0.y - box.max.y) / (line.p0.y - line.p1.y); x = line.p0.x + (line.p1.x - line.p0.x)*t; if(x > box.min.x && x < box.max.x){ z = line.p0.z + (line.p1.z - line.p0.z)*t; if(z > box.min.z && z < box.max.z) if(t < mint){ mint = t; p = CVector(x, box.max.y, z); normal = CVector(0.0f, 1.0f, 0.0f); } } } // min z plne if((box.min.z - line.p0.z) * (box.min.z - line.p1.z) < 0.0f){ t = (box.min.z - line.p0.z) / (line.p1.z - line.p0.z); x = line.p0.x + (line.p1.x - line.p0.x)*t; if(x > box.min.x && x < box.max.x){ y = line.p0.y + (line.p1.y - line.p0.y)*t; if(y > box.min.y && y < box.max.y) if(t < mint){ mint = t; p = CVector(x, y, box.min.z); normal = CVector(0.0f, 0.0f, -1.0f); } } } // max z plane if((line.p0.z - box.max.z) * (line.p1.z - box.max.z) < 0.0f){ t = (line.p0.z - box.max.z) / (line.p0.z - line.p1.z); x = line.p0.x + (line.p1.x - line.p0.x)*t; if(x > box.min.x && x < box.max.x){ y = line.p0.y + (line.p1.y - line.p0.y)*t; if(y > box.min.y && y < box.max.y) if(t < mint){ mint = t; p = CVector(x, y, box.max.z); normal = CVector(0.0f, 0.0f, 1.0f); } } } if(mint >= mindist) return false; point.point = p; point.normal = normal; #ifndef VU_COLLISION point.surfaceA = 0; point.pieceA = 0; point.surfaceB = box.surface; point.pieceB = box.piece; #endif mindist = mint; return true; } // If line.p0 lies inside sphere, no collision is registered. bool CCollision::ProcessLineSphere(const CColLine &line, const CColSphere &sphere, CColPoint &point, float &mindist) { CVector v01 = line.p1 - line.p0; CVector v0c = sphere.center - line.p0; float linesq = v01.MagnitudeSqr(); // project v0c onto v01, scaled by |v01| this is the midpoint of the two intersections float projline = DotProduct(v01, v0c); // tangent of p0 to sphere, scaled by linesq just like projline^2 float tansq = (v0c.MagnitudeSqr() - sphere.radius*sphere.radius) * linesq; // this works out to be the square of the distance between the midpoint and the intersections float diffsq = projline*projline - tansq; // no intersection if(diffsq < 0.0f) return false; // point of first intersection, in range [0,1] between p0 and p1 float t = (projline - Sqrt(diffsq)) / linesq; // if not on line or beyond mindist, no intersection if(t < 0.0f || t > 1.0f || t >= mindist) return false; point.point = line.p0 + v01*t; point.normal = point.point - sphere.center; point.normal.Normalise(); #ifndef VU_COLLISION point.surfaceA = 0; point.pieceA = 0; point.surfaceB = sphere.surface; point.pieceB = sphere.piece; #endif mindist = t; return true; } // unused bool CCollision::ProcessVerticalLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist, CStoredCollPoly *poly) { #ifdef VU_COLLISION // not used in favour of optimized loops bool res = ProcessLineTriangle(line, verts, tri, plane, point, mindist); if(res && poly){ poly->verts[0] = verts[tri.a].Get(); poly->verts[1] = verts[tri.b].Get(); poly->verts[2] = verts[tri.c].Get(); poly->valid = true; } return res; #else float t; CVector normal; const CVector &p0 = line.p0; const CVector &va = verts[tri.a].Get(); const CVector &vb = verts[tri.b].Get(); const CVector &vc = verts[tri.c].Get(); // early out bound rect test if(p0.x < va.x && p0.x < vb.x && p0.x < vc.x) return false; if(p0.x > va.x && p0.x > vb.x && p0.x > vc.x) return false; if(p0.y < va.y && p0.y < vb.y && p0.y < vc.y) return false; if(p0.y > va.y && p0.y > vb.y && p0.y > vc.y) return false; plane.GetNormal(normal); // if points are on the same side, no collision if(plane.CalcPoint(p0) * plane.CalcPoint(line.p1) > 0.0f) return false; // intersection parameter on line float h = (line.p1 - p0).z; t = -plane.CalcPoint(p0) / (h * normal.z); // early out if we're beyond the mindist if(t >= mindist) return false; CVector p(p0.x, p0.y, p0.z + h*t); CVector2D vec1, vec2, vec3, vect; switch(plane.dir){ case DIR_X_POS: vec1.x = va.y; vec1.y = va.z; vec2.x = vc.y; vec2.y = vc.z; vec3.x = vb.y; vec3.y = vb.z; vect.x = p.y; vect.y = p.z; break; case DIR_X_NEG: vec1.x = va.y; vec1.y = va.z; vec2.x = vb.y; vec2.y = vb.z; vec3.x = vc.y; vec3.y = vc.z; vect.x = p.y; vect.y = p.z; break; case DIR_Y_POS: vec1.x = va.z; vec1.y = va.x; vec2.x = vc.z; vec2.y = vc.x; vec3.x = vb.z; vec3.y = vb.x; vect.x = p.z; vect.y = p.x; break; case DIR_Y_NEG: vec1.x = va.z; vec1.y = va.x; vec2.x = vb.z; vec2.y = vb.x; vec3.x = vc.z; vec3.y = vc.x; vect.x = p.z; vect.y = p.x; break; case DIR_Z_POS: vec1.x = va.x; vec1.y = va.y; vec2.x = vc.x; vec2.y = vc.y; vec3.x = vb.x; vec3.y = vb.y; vect.x = p.x; vect.y = p.y; break; case DIR_Z_NEG: vec1.x = va.x; vec1.y = va.y; vec2.x = vb.x; vec2.y = vb.y; vec3.x = vc.x; vec3.y = vc.y; vect.x = p.x; vect.y = p.y; break; default: assert(0); } if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return false; if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return false; if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return false; if(t >= mindist) return false; point.point = p; point.normal = normal; point.surfaceA = 0; point.pieceA = 0; point.surfaceB = tri.surface; point.pieceB = 0; if(poly){ poly->verts[0] = va; poly->verts[1] = vb; poly->verts[2] = vc; poly->valid = true; } mindist = t; return true; #endif } bool CCollision::IsStoredPolyStillValidVerticalLine(const CVector &pos, float z, CColPoint &point, CStoredCollPoly *poly) { #ifdef VU_COLLISION if(!poly->valid) return false; CVuVector p0 = pos; CVuVector p1 = pos; p1.z = z; CVector v01 = poly->verts[1] - poly->verts[0]; CVector v02 = poly->verts[2] - poly->verts[0]; CVuVector plane = CrossProduct(v02, v01); plane.Normalise(); plane.w = DotProduct(plane, poly->verts[0]); LineToTriangleCollision(p0, p1, poly->verts[0], poly->verts[1], poly->verts[2], plane); CVuVector pnt; float dist; if(!GetVUresult(pnt, plane, dist)) #ifdef FIX_BUGS // perhaps not needed but be safe return poly->valid = false; #else return false; #endif point.point = pnt; return true; #else float t; if(!poly->valid) return false; // maybe inlined? CColTrianglePlane plane; plane.Set(poly->verts[0], poly->verts[1], poly->verts[2]); const CVector &va = poly->verts[0]; const CVector &vb = poly->verts[1]; const CVector &vc = poly->verts[2]; CVector p0 = pos; CVector p1(pos.x, pos.y, z); // The rest is pretty much CCollision::ProcessLineTriangle // if points are on the same side, no collision if(plane.CalcPoint(p0) * plane.CalcPoint(p1) > 0.0f) return poly->valid = false; // intersection parameter on line CVector normal; plane.GetNormal(normal); t = -plane.CalcPoint(p0) / DotProduct(p1 - p0, normal); // find point of intersection CVector p = p0 + (p1-p0)*t; CVector2D vec1, vec2, vec3, vect; switch(plane.dir){ case DIR_X_POS: vec1.x = va.y; vec1.y = va.z; vec2.x = vc.y; vec2.y = vc.z; vec3.x = vb.y; vec3.y = vb.z; vect.x = p.y; vect.y = p.z; break; case DIR_X_NEG: vec1.x = va.y; vec1.y = va.z; vec2.x = vb.y; vec2.y = vb.z; vec3.x = vc.y; vec3.y = vc.z; vect.x = p.y; vect.y = p.z; break; case DIR_Y_POS: vec1.x = va.z; vec1.y = va.x; vec2.x = vc.z; vec2.y = vc.x; vec3.x = vb.z; vec3.y = vb.x; vect.x = p.z; vect.y = p.x; break; case DIR_Y_NEG: vec1.x = va.z; vec1.y = va.x; vec2.x = vb.z; vec2.y = vb.x; vec3.x = vc.z; vec3.y = vc.x; vect.x = p.z; vect.y = p.x; break; case DIR_Z_POS: vec1.x = va.x; vec1.y = va.y; vec2.x = vc.x; vec2.y = vc.y; vec3.x = vb.x; vec3.y = vb.y; vect.x = p.x; vect.y = p.y; break; case DIR_Z_NEG: vec1.x = va.x; vec1.y = va.y; vec2.x = vb.x; vec2.y = vb.y; vec3.x = vc.x; vec3.y = vc.y; vect.x = p.x; vect.y = p.y; break; default: assert(0); } if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return poly->valid = false; if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return poly->valid = false; if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return poly->valid = false; point.point = p; return poly->valid = true; #endif } bool CCollision::ProcessLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist, CStoredCollPoly *poly) { #ifdef VU_COLLISION // not used in favour of optimized loops VuTriangle vutri; verts[tri.a].Unpack(vutri.v0); verts[tri.b].Unpack(vutri.v1); verts[tri.c].Unpack(vutri.v2); plane.Unpack(vutri.plane); LineToTriangleCollisionCompressed(*(CVuVector*)&line.p0, *(CVuVector*)&line.p1, vutri); CVuVector pnt, normal; float dist; if(GetVUresult(pnt, normal, dist)){ if(dist < mindist){ point.point = pnt; point.normal = normal; mindist = dist; return true; } } return false; #else float t; CVector normal; plane.GetNormal(normal); // if points are on the same side, no collision if(plane.CalcPoint(line.p0) * plane.CalcPoint(line.p1) > 0.0f) return false; float p0dist = DotProduct(line.p1 - line.p0, normal); #ifdef FIX_BUGS // line lines in the plane, assume no collision if (p0dist == 0.0f) return false; #endif // intersection parameter on line t = -plane.CalcPoint(line.p0) / p0dist; // early out if we're beyond the mindist if(t >= mindist) return false; // find point of intersection CVector p = line.p0 + (line.p1-line.p0)*t; const CVector &va = verts[tri.a].Get(); const CVector &vb = verts[tri.b].Get(); const CVector &vc = verts[tri.c].Get(); CVector2D vec1, vec2, vec3, vect; switch(plane.dir){ case DIR_X_POS: vec1.x = va.y; vec1.y = va.z; vec2.x = vc.y; vec2.y = vc.z; vec3.x = vb.y; vec3.y = vb.z; vect.x = p.y; vect.y = p.z; break; case DIR_X_NEG: vec1.x = va.y; vec1.y = va.z; vec2.x = vb.y; vec2.y = vb.z; vec3.x = vc.y; vec3.y = vc.z; vect.x = p.y; vect.y = p.z; break; case DIR_Y_POS: vec1.x = va.z; vec1.y = va.x; vec2.x = vc.z; vec2.y = vc.x; vec3.x = vb.z; vec3.y = vb.x; vect.x = p.z; vect.y = p.x; break; case DIR_Y_NEG: vec1.x = va.z; vec1.y = va.x; vec2.x = vb.z; vec2.y = vb.x; vec3.x = vc.z; vec3.y = vc.x; vect.x = p.z; vect.y = p.x; break; case DIR_Z_POS: vec1.x = va.x; vec1.y = va.y; vec2.x = vc.x; vec2.y = vc.y; vec3.x = vb.x; vec3.y = vb.y; vect.x = p.x; vect.y = p.y; break; case DIR_Z_NEG: vec1.x = va.x; vec1.y = va.y; vec2.x = vb.x; vec2.y = vb.y; vec3.x = vc.x; vec3.y = vc.y; vect.x = p.x; vect.y = p.y; break; default: assert(0); } if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return false; if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return false; if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return false; if(t >= mindist) return false; point.point = p; point.normal = normal; point.surfaceA = 0; point.pieceA = 0; point.surfaceB = tri.surface; point.pieceB = 0; if(poly){ poly->verts[0] = va; poly->verts[1] = vb; poly->verts[2] = vc; poly->valid = true; } mindist = t; return true; #endif } bool CCollision::ProcessSphereTriangle(const CColSphere &sphere, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindistsq) { #ifdef VU_COLLISION // not used in favour of optimized loops VuTriangle vutri; verts[tri.a].Unpack(vutri.v0); verts[tri.b].Unpack(vutri.v1); verts[tri.c].Unpack(vutri.v2); plane.Unpack(vutri.plane); SphereToTriangleCollisionCompressed(*(CVuVector*)&sphere, vutri); CVuVector pnt, normal; float dist; if(GetVUresult(pnt, normal, dist) && dist*dist < mindistsq){ float depth = sphere.radius - dist; if(depth > point.depth){ point.point = pnt; point.normal = normal; point.depth = depth; mindistsq = dist*dist; return true; } } return false; #else // If sphere and plane don't intersect, no collision float planedist = plane.CalcPoint(sphere.center); float distsq = planedist*planedist; if(Abs(planedist) > sphere.radius || distsq > mindistsq) return false; const CVector &va = verts[tri.a].Get(); const CVector &vb = verts[tri.b].Get(); const CVector &vc = verts[tri.c].Get(); // calculate two orthogonal basis vectors for the triangle CVector normal; plane.GetNormal(normal); CVector vec2 = vb - va; float len = vec2.Magnitude(); vec2 = vec2 * (1.0f/len); CVector vec1 = CrossProduct(vec2, normal); // We know A has local coordinate [0,0] and B has [0,len]. // Now calculate coordinates on triangle for these two vectors: CVector vac = vc - va; CVector vas = sphere.center - va; CVector2D b(0.0f, len); CVector2D c(DotProduct(vec1, vac), DotProduct(vec2, vac)); CVector2D s(DotProduct(vec1, vas), DotProduct(vec2, vas)); // The three triangle lines partition the space into 6 sectors, // find out in which the center lies. int insideAB = CrossProduct2D(s, b) >= 0.0f; int insideAC = CrossProduct2D(c, s) >= 0.0f; int insideBC = CrossProduct2D(s-b, c-b) >= 0.0f; int testcase = insideAB + insideAC + insideBC; float dist = 0.0f; CVector p; switch(testcase){ case 0: return false; // shouldn't happen case 1: // closest to a vertex if(insideAB) p = vc; else if(insideAC) p = vb; else if(insideBC) p = va; else assert(0); dist = (sphere.center - p).Magnitude(); break; case 2: // closest to an edge // looks like original game as DistToLine manually inlined if(!insideAB) dist = DistToLine(&va, &vb, &sphere.center, p); else if(!insideAC) dist = DistToLine(&va, &vc, &sphere.center, p); else if(!insideBC) dist = DistToLine(&vb, &vc, &sphere.center, p); else assert(0); break; case 3: // center is in triangle dist = Abs(planedist); p = sphere.center - normal*planedist; break; default: assert(0); } if(dist >= sphere.radius || dist*dist >= mindistsq) return false; point.point = p; point.normal = sphere.center - p; point.normal.Normalise(); #ifndef VU_COLLISION point.surfaceA = sphere.surface; point.pieceA = sphere.piece; point.surfaceB = tri.surface; point.pieceB = 0; #endif point.depth = sphere.radius - dist; mindistsq = dist*dist; return true; #endif } bool CCollision::ProcessLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSeeThrough, bool ignoreShootThrough) { #ifdef VU_COLLISION CMatrix matTransform; int i; // transform line to model space Invert(matrix, matTransform); CVuVector newline[2]; TransformPoints(newline, 2, matTransform, &line.p0, sizeof(CColLine)/2); if(mindist < 1.0f) newline[1] = newline[0] + (newline[1] - newline[0])*mindist; // If we don't intersect with the bounding box, no chance on the rest if(!TestLineBox(*(CColLine*)newline, model.boundingBox)) return false; float coldist = 1.0f; for(i = 0; i < model.numSpheres; i++){ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.spheres[i].surface)) continue; if(ProcessLineSphere(*(CColLine*)newline, model.spheres[i], point, coldist)) point.Set(0, 0, model.spheres[i].surface, model.spheres[i].piece); } for(i = 0; i < model.numBoxes; i++){ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue; if(ProcessLineBox(*(CColLine*)newline, model.boxes[i], point, coldist)) point.Set(0, 0, model.boxes[i].surface, model.boxes[i].piece); } CalculateTrianglePlanes(&model); VuTriangle vutri; CColTriangle *lasttri = nil; for(i = 0; i < model.numTriangles; i++){ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; CColTriangle *tri = &model.triangles[i]; model.vertices[tri->a].Unpack(vutri.v0); model.vertices[tri->b].Unpack(vutri.v1); model.vertices[tri->c].Unpack(vutri.v2); model.trianglePlanes[i].Unpack(vutri.plane); LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); lasttri = tri; break; } #ifdef FIX_BUGS // no need to check first again i++; #endif CVuVector pnt, normal; float dist; for(; i < model.numTriangles; i++){ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; CColTriangle *tri = &model.triangles[i]; model.vertices[tri->a].Unpack(vutri.v0); model.vertices[tri->b].Unpack(vutri.v1); model.vertices[tri->c].Unpack(vutri.v2); model.trianglePlanes[i].Unpack(vutri.plane); if(GetVUresult(pnt, normal, dist)) if(dist < coldist){ point.point = pnt; point.normal = normal; point.Set(0, 0, lasttri->surface, 0); coldist = dist; } LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); lasttri = tri; } if(lasttri && GetVUresult(pnt, normal, dist)) if(dist < coldist){ point.point = pnt; point.normal = normal; point.Set(0, 0, lasttri->surface, 0); coldist = dist; } if(coldist < 1.0f){ point.point = matrix * point.point; point.normal = Multiply3x3(matrix, point.normal); mindist *= coldist; return true; } return false; #else static CMatrix matTransform; int i; // transform line to model space Invert(matrix, matTransform); CColLine newline(matTransform * line.p0, matTransform * line.p1); // If we don't intersect with the bounding box, no chance on the rest if(!TestLineBox(newline, model.boundingBox)) return false; float coldist = mindist; for(i = 0; i < model.numSpheres; i++){ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.spheres[i].surface)) continue; ProcessLineSphere(newline, model.spheres[i], point, coldist); } for(i = 0; i < model.numBoxes; i++){ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.boxes[i].surface)) continue; ProcessLineBox(newline, model.boxes[i], point, coldist); } CalculateTrianglePlanes(&model); for(i = 0; i < model.numTriangles; i++){ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue; if(ignoreShootThrough && IsShootThrough(model.triangles[i].surface)) continue; ProcessLineTriangle(newline, model.vertices, model.triangles[i], model.trianglePlanes[i], point, coldist); } if(coldist < mindist){ point.point = matrix * point.point; point.normal = Multiply3x3(matrix, point.normal); mindist = coldist; return true; } return false; #endif } bool CCollision::ProcessVerticalLine(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSeeThrough, bool ignoreShootThrough, CStoredCollPoly *poly) { #ifdef VU_COLLISION static CStoredCollPoly TempStoredPoly; CMatrix matTransform; int i; // transform line to model space Invert(matrix, matTransform); CVuVector newline[2]; TransformPoints(newline, 2, matTransform, &line.p0, sizeof(CColLine)/2); if(mindist < 1.0f) newline[1] = newline[0] + (newline[1] - newline[0])*mindist; if(!TestLineBox(*(CColLine*)newline, model.boundingBox)) return false; float coldist = 1.0f; for(i = 0; i < model.numSpheres; i++){ if(ignoreSeeThrough && IsSeeThroughVertical(model.spheres[i].surface)) continue; if(ProcessLineSphere(*(CColLine*)newline, model.spheres[i], point, coldist)) point.Set(0, 0, model.spheres[i].surface, model.spheres[i].piece); } for(i = 0; i < model.numBoxes; i++){ if(ignoreSeeThrough && IsSeeThroughVertical(model.boxes[i].surface)) continue; if(ProcessLineBox(*(CColLine*)newline, model.boxes[i], point, coldist)) point.Set(0, 0, model.boxes[i].surface, model.boxes[i].piece); } CalculateTrianglePlanes(&model); TempStoredPoly.valid = false; if(model.numTriangles){ bool registeredCol; CColTriangle *lasttri = nil; VuTriangle vutri; for(i = 0; i < model.numTriangles; i++){ if(ignoreSeeThrough && IsSeeThroughVertical(model.triangles[i].surface)) continue; CColTriangle *tri = &model.triangles[i]; model.vertices[tri->a].Unpack(vutri.v0); model.vertices[tri->b].Unpack(vutri.v1); model.vertices[tri->c].Unpack(vutri.v2); model.trianglePlanes[i].Unpack(vutri.plane); LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); lasttri = tri; break; } #ifdef FIX_BUGS // no need to check first again i++; #endif CVuVector pnt, normal; float dist; for(; i < model.numTriangles; i++){ if(ignoreSeeThrough && IsSeeThroughVertical(model.triangles[i].surface)) continue; CColTriangle *tri = &model.triangles[i]; model.vertices[tri->a].Unpack(vutri.v0); model.vertices[tri->b].Unpack(vutri.v1); model.vertices[tri->c].Unpack(vutri.v2); model.trianglePlanes[i].Unpack(vutri.plane); if(GetVUresult(pnt, normal, dist)){ if(dist < coldist){ point.point = pnt; point.normal = normal; point.Set(0, 0, lasttri->surface, 0); coldist = dist; registeredCol = true; }else registeredCol = false; }else registeredCol = false; if(registeredCol){ TempStoredPoly.verts[0] = model.vertices[lasttri->a].Get(); TempStoredPoly.verts[1] = model.vertices[lasttri->b].Get(); TempStoredPoly.verts[2] = model.vertices[lasttri->c].Get(); TempStoredPoly.valid = true; } LineToTriangleCollisionCompressed(newline[0], newline[1], vutri); lasttri = tri; } if(lasttri && GetVUresult(pnt, normal, dist)){ if(dist < coldist){ point.point = pnt; point.normal = normal; point.Set(0, 0, lasttri->surface, 0); coldist = dist; registeredCol = true; }else registeredCol = false; }else registeredCol = false; if(registeredCol){ TempStoredPoly.verts[0] = model.vertices[lasttri->a].Get(); TempStoredPoly.verts[1] = model.vertices[lasttri->b].Get(); TempStoredPoly.verts[2] = model.vertices[lasttri->c].Get(); TempStoredPoly.valid = true; } } if(coldist < 1.0f){ point.point = matrix * point.point; point.normal = Multiply3x3(matrix, point.normal); if(TempStoredPoly.valid && poly){ *poly = TempStoredPoly; poly->verts[0] = matrix * CVector(poly->verts[0]); poly->verts[1] = matrix * CVector(poly->verts[1]); poly->verts[2] = matrix * CVector(poly->verts[2]); } mindist *= coldist; return true; } return false; #else static CStoredCollPoly TempStoredPoly; int i; // transform line to model space // Why does the game seem to do this differently than above? CColLine newline(MultiplyInverse(matrix, line.p0), MultiplyInverse(matrix, line.p1)); if(!TestLineBox(newline, model.boundingBox)) return false; // BUG? is IsSeeThroughVertical really the right thing? also not checking shoot through float coldist = mindist; for(i = 0; i < model.numSpheres; i++){ if(ignoreSeeThrough && IsSeeThroughVertical(model.spheres[i].surface)) continue; ProcessLineSphere(newline, model.spheres[i], point, coldist); } for(i = 0; i < model.numBoxes; i++){ if(ignoreSeeThrough && IsSeeThroughVertical(model.boxes[i].surface)) continue; ProcessLineBox(newline, model.boxes[i], point, coldist); } CalculateTrianglePlanes(&model); TempStoredPoly.valid = false; for(i = 0; i < model.numTriangles; i++){ if(ignoreSeeThrough && IsSeeThroughVertical(model.triangles[i].surface)) continue; ProcessLineTriangle(newline, model.vertices, model.triangles[i], model.trianglePlanes[i], point, coldist, &TempStoredPoly); } if(coldist < mindist){ point.point = matrix * point.point; point.normal = Multiply3x3(matrix, point.normal); if(TempStoredPoly.valid && poly){ *poly = TempStoredPoly; poly->verts[0] = matrix * poly->verts[0]; poly->verts[1] = matrix * poly->verts[1]; poly->verts[2] = matrix * poly->verts[2]; } mindist = coldist; return true; } return false; #endif } enum { MAXNUMSPHERES = 128, MAXNUMBOXES = 32, MAXNUMLINES = 16, MAXNUMTRIS = 600 }; #ifdef VU_COLLISION #ifdef GTA_PS2 #define SPR(off) ((uint8*)(0x70000000 + (off))) #else static uint8 fakeSPR[16*1024]; #define SPR(off) ((uint8*)(fakeSPR + (off))) #endif #endif // This checks model A's spheres and lines against model B's spheres, boxes and triangles. // Returns the number of A's spheres that collide. // Returned ColPoints are in world space. // NB: only vehicles can have col models with lines, exactly 4, one for each wheel int32 CCollision::ProcessColModels(const CMatrix &matrixA, CColModel &modelA, const CMatrix &matrixB, CColModel &modelB, CColPoint *spherepoints, CColPoint *linepoints, float *linedists) { #ifdef VU_COLLISION CVuVector *aSpheresA = (CVuVector*)SPR(0x0000); CVuVector *aSpheresB = (CVuVector*)SPR(0x0800); CVuVector *aLinesA = (CVuVector*)SPR(0x1000); int32 *aSphereIndicesA = (int32*)SPR(0x1200); int32 *aSphereIndicesB = (int32*)SPR(0x1400); int32 *aBoxIndicesB = (int32*)SPR(0x1600); int32 *aTriangleIndicesB = (int32*)SPR(0x1680); bool *aCollided = (bool*)SPR(0x1FE0); CMatrix &matAB = *(CMatrix*)SPR(0x1FF0); CMatrix &matBA = *(CMatrix*)SPR(0x2040); int i, j, k; // From model A space to model B space Invert(matrixB, matAB); matAB *= matrixA; CVuVector bsphereAB; // bounding sphere of A in B space TransformPoint(bsphereAB, matAB, modelA.boundingSphere.center); // inlined bsphereAB.w = modelA.boundingSphere.radius; if(!TestSphereBox(*(CColSphere*)&bsphereAB, modelB.boundingBox)) return 0; // transform modelA's spheres and lines to B space TransformPoints(aSpheresA, modelA.numSpheres, matAB, &modelA.spheres->center, sizeof(CColSphere)); for(i = 0; i < modelA.numSpheres; i++) aSpheresA[i].w = modelA.spheres[i].radius; TransformPoints(aLinesA, modelA.numLines*2, matAB, &modelA.lines->p0, sizeof(CColLine)/2); // Test them against model B's bounding volumes int numSpheresA = 0; for(i = 0; i < modelA.numSpheres; i++) if(TestSphereBox(*(CColSphere*)&aSpheresA[i], modelB.boundingBox)) aSphereIndicesA[numSpheresA++] = i; // No collision if(numSpheresA == 0 && modelA.numLines == 0) return 0; // B to A space Invert(matrixA, matBA); matBA *= matrixB; // transform modelB's spheres to A space TransformPoints(aSpheresB, modelB.numSpheres, matBA, &modelB.spheres->center, sizeof(CColSphere)); for(i = 0; i < modelB.numSpheres; i++) aSpheresB[i].w = modelB.spheres[i].radius; // Check model B against A's bounding volumes int numSpheresB = 0; int numBoxesB = 0; int numTrianglesB = 0; for(i = 0; i < modelB.numSpheres; i++) if(TestSphereBox(*(CColSphere*)&aSpheresB[i], modelA.boundingBox)) aSphereIndicesB[numSpheresB++] = i; for(i = 0; i < modelB.numBoxes; i++) if(TestSphereBox(*(CColSphere*)&bsphereAB, modelB.boxes[i])) aBoxIndicesB[numBoxesB++] = i; CalculateTrianglePlanes(&modelB); if(modelB.numTriangles){ VuTriangle vutri; // process the first triangle CColTriangle *tri = &modelB.triangles[0]; modelB.vertices[tri->a].Unpack(vutri.v0); modelB.vertices[tri->b].Unpack(vutri.v1); modelB.vertices[tri->c].Unpack(vutri.v2); modelB.trianglePlanes[0].Unpack(vutri.plane); SphereToTriangleCollisionCompressed(bsphereAB, vutri); for(i = 1; i < modelB.numTriangles; i++){ // set up the next triangle while VU0 is running tri = &modelB.triangles[i]; modelB.vertices[tri->a].Unpack(vutri.v0); modelB.vertices[tri->b].Unpack(vutri.v1); modelB.vertices[tri->c].Unpack(vutri.v2); modelB.trianglePlanes[i].Unpack(vutri.plane); // check previous result if(GetVUresult()) aTriangleIndicesB[numTrianglesB++] = i-1; // kick off this one SphereToTriangleCollisionCompressed(bsphereAB, vutri); } // check last result if(GetVUresult()) aTriangleIndicesB[numTrianglesB++] = i-1; } // No collision if(numSpheresB == 0 && numBoxesB == 0 && numTrianglesB == 0) return 0; // We now have the collision volumes in A and B that are worth processing. // Process A's spheres against B's collision volumes int numCollisions = 0; spherepoints[numCollisions].depth = -1.0f; for(i = 0; i < numSpheresA; i++){ float coldist = 1.0e24f; bool hasCollided = false; CColSphere *sphA = &modelA.spheres[aSphereIndicesA[i]]; CVuVector *vusphA = &aSpheresA[aSphereIndicesA[i]]; for(j = 0; j < numSpheresB; j++) // This actually looks like something was inlined here if(ProcessSphereSphere(*(CColSphere*)vusphA, modelB.spheres[aSphereIndicesB[j]], spherepoints[numCollisions], coldist)){ spherepoints[numCollisions].Set( sphA->surface, sphA->piece, modelB.spheres[aSphereIndicesB[j]].surface, modelB.spheres[aSphereIndicesB[j]].piece); hasCollided = true; } for(j = 0; j < numBoxesB; j++) if(ProcessSphereBox(*(CColSphere*)vusphA, modelB.boxes[aBoxIndicesB[j]], spherepoints[numCollisions], coldist)){ spherepoints[numCollisions].Set( sphA->surface, sphA->piece, modelB.boxes[aBoxIndicesB[j]].surface, modelB.boxes[aBoxIndicesB[j]].piece); hasCollided = true; } if(numTrianglesB){ CVuVector point, normal; float depth; bool registeredCol; CColTriangle *lasttri; VuTriangle vutri; // process the first triangle k = aTriangleIndicesB[0]; CColTriangle *tri = &modelB.triangles[k]; modelB.vertices[tri->a].Unpack(vutri.v0); modelB.vertices[tri->b].Unpack(vutri.v1); modelB.vertices[tri->c].Unpack(vutri.v2); modelB.trianglePlanes[k].Unpack(vutri.plane); SphereToTriangleCollisionCompressed(*vusphA, vutri); lasttri = tri; for(j = 1; j < numTrianglesB; j++){ k = aTriangleIndicesB[j]; // set up the next triangle while VU0 is running tri = &modelB.triangles[k]; modelB.vertices[tri->a].Unpack(vutri.v0); modelB.vertices[tri->b].Unpack(vutri.v1); modelB.vertices[tri->c].Unpack(vutri.v2); modelB.trianglePlanes[k].Unpack(vutri.plane); // check previous result // TODO: this looks inlined but spherepoints[numCollisions] does not... if(GetVUresult(point, normal, depth)){ depth = sphA->radius - depth; if(depth > spherepoints[numCollisions].depth){ spherepoints[numCollisions].point = point; spherepoints[numCollisions].normal = normal; spherepoints[numCollisions].Set(depth, sphA->surface, sphA->piece, lasttri->surface, 0); registeredCol = true; }else registeredCol = false; }else registeredCol = false; if(registeredCol) hasCollided = true; // kick off this one SphereToTriangleCollisionCompressed(*vusphA, vutri); lasttri = tri; } // check last result // TODO: this looks inlined but spherepoints[numCollisions] does not... if(GetVUresult(point, normal, depth)){ depth = sphA->radius - depth; if(depth > spherepoints[numCollisions].depth){ spherepoints[numCollisions].point = point; spherepoints[numCollisions].normal = normal; spherepoints[numCollisions].Set(depth, sphA->surface, sphA->piece, lasttri->surface, 0); registeredCol = true; }else registeredCol = false; }else registeredCol = false; if(registeredCol) hasCollided = true; } if(hasCollided){ numCollisions++; if(numCollisions == MAX_COLLISION_POINTS) break; spherepoints[numCollisions].depth = -1.0f; } } for(i = 0; i < numCollisions; i++){ // TODO: both VU0 macros spherepoints[i].point = matrixB * spherepoints[i].point; spherepoints[i].normal = Multiply3x3(matrixB, spherepoints[i].normal); } // And the same thing for the lines in A for(i = 0; i < modelA.numLines; i++){ aCollided[i] = false; CVuVector *lineA = &aLinesA[i*2]; for(j = 0; j < numSpheresB; j++) if(ProcessLineSphere(*(CColLine*)lineA, modelB.spheres[aSphereIndicesB[j]], linepoints[i], linedists[i])){ linepoints[i].Set(0, 0, #ifdef FIX_BUGS modelB.spheres[aSphereIndicesB[j]].surface, modelB.spheres[aSphereIndicesB[j]].piece); #else modelB.spheres[j].surface, modelB.spheres[j].piece); #endif aCollided[i] = true; } for(j = 0; j < numBoxesB; j++) if(ProcessLineBox(*(CColLine*)lineA, modelB.boxes[aBoxIndicesB[j]], linepoints[i], linedists[i])){ linepoints[i].Set(0, 0, modelB.boxes[aBoxIndicesB[j]].surface, modelB.boxes[aBoxIndicesB[j]].piece); aCollided[i] = true; } if(numTrianglesB){ CVuVector point, normal; float dist; bool registeredCol; CColTriangle *lasttri; VuTriangle vutri; // process the first triangle k = aTriangleIndicesB[0]; CColTriangle *tri = &modelB.triangles[k]; modelB.vertices[tri->a].Unpack(vutri.v0); modelB.vertices[tri->b].Unpack(vutri.v1); modelB.vertices[tri->c].Unpack(vutri.v2); modelB.trianglePlanes[k].Unpack(vutri.plane); LineToTriangleCollisionCompressed(lineA[0], lineA[1], vutri); lasttri = tri; for(j = 1; j < numTrianglesB; j++){ k = aTriangleIndicesB[j]; // set up the next triangle while VU0 is running CColTriangle *tri = &modelB.triangles[k]; modelB.vertices[tri->a].Unpack(vutri.v0); modelB.vertices[tri->b].Unpack(vutri.v1); modelB.vertices[tri->c].Unpack(vutri.v2); modelB.trianglePlanes[k].Unpack(vutri.plane); // check previous result // TODO: this again somewhat looks inlined if(GetVUresult(point, normal, dist)){ if(dist < linedists[i]){ linepoints[i].point = point; linepoints[i].normal = normal; linedists[i] = dist; linepoints[i].Set(0, 0, lasttri->surface, 0); registeredCol = true; }else registeredCol = false; }else registeredCol = false; if(registeredCol) aCollided[i] = true; // kick of this one LineToTriangleCollisionCompressed(lineA[0], lineA[1], vutri); lasttri = tri; } // check last result if(GetVUresult(point, normal, dist)){ if(dist < linedists[i]){ linepoints[i].point = point; linepoints[i].normal = normal; linedists[i] = dist; linepoints[i].Set(0, 0, lasttri->surface, 0); registeredCol = true; }else registeredCol = false; }else registeredCol = false; if(registeredCol) aCollided[i] = true; } if(aCollided[i]){ // TODO: both VU0 macros linepoints[i].point = matrixB * linepoints[i].point; linepoints[i].normal = Multiply3x3(matrixB, linepoints[i].normal); } } return numCollisions; // sphere collisions #else static int aSphereIndicesA[MAXNUMSPHERES]; static int aLineIndicesA[MAXNUMLINES]; static int aSphereIndicesB[MAXNUMSPHERES]; static int aBoxIndicesB[MAXNUMBOXES]; static int aTriangleIndicesB[MAXNUMTRIS]; static bool aCollided[MAXNUMLINES]; static CColSphere aSpheresA[MAXNUMSPHERES]; static CColLine aLinesA[MAXNUMLINES]; static CMatrix matAB, matBA; CColSphere s; int i, j; assert(modelA.numSpheres <= MAXNUMSPHERES); assert(modelA.numLines <= MAXNUMLINES); // From model A space to model B space matAB = Invert(matrixB, matAB); matAB *= matrixA; CColSphere bsphereAB; // bounding sphere of A in B space bsphereAB.radius = modelA.boundingSphere.radius; bsphereAB.center = matAB * modelA.boundingSphere.center; if(!TestSphereBox(bsphereAB, modelB.boundingBox)) return 0; // B to A space matBA = Invert(matrixA, matBA); matBA *= matrixB; // transform modelA's spheres and lines to B space for(i = 0; i < modelA.numSpheres; i++){ CColSphere &s = modelA.spheres[i]; aSpheresA[i].Set(s.radius, matAB * s.center, s.surface, s.piece); } for(i = 0; i < modelA.numLines; i++) aLinesA[i].Set(matAB * modelA.lines[i].p0, matAB * modelA.lines[i].p1); // Test them against model B's bounding volumes int numSpheresA = 0; int numLinesA = 0; for(i = 0; i < modelA.numSpheres; i++) if(TestSphereBox(aSpheresA[i], modelB.boundingBox)) aSphereIndicesA[numSpheresA++] = i; // no actual check??? for(i = 0; i < modelA.numLines; i++) aLineIndicesA[numLinesA++] = i; // No collision if(numSpheresA == 0 && numLinesA == 0) return 0; // Check model B against A's bounding volumes int numSpheresB = 0; int numBoxesB = 0; int numTrianglesB = 0; for(i = 0; i < modelB.numSpheres; i++){ s.radius = modelB.spheres[i].radius; s.center = matBA * modelB.spheres[i].center; if(TestSphereBox(s, modelA.boundingBox)) aSphereIndicesB[numSpheresB++] = i; } for(i = 0; i < modelB.numBoxes; i++) if(TestSphereBox(bsphereAB, modelB.boxes[i])) aBoxIndicesB[numBoxesB++] = i; CalculateTrianglePlanes(&modelB); for(i = 0; i < modelB.numTriangles; i++) if(TestSphereTriangle(bsphereAB, modelB.vertices, modelB.triangles[i], modelB.trianglePlanes[i])) aTriangleIndicesB[numTrianglesB++] = i; assert(numSpheresB <= MAXNUMSPHERES); assert(numBoxesB <= MAXNUMBOXES); assert(numTrianglesB <= MAXNUMTRIS); // No collision if(numSpheresB == 0 && numBoxesB == 0 && numTrianglesB == 0) return 0; // We now have the collision volumes in A and B that are worth processing. // Process A's spheres against B's collision volumes int numCollisions = 0; for(i = 0; i < numSpheresA; i++){ float coldist = 1.0e24f; bool hasCollided = false; for(j = 0; j < numSpheresB; j++) hasCollided |= ProcessSphereSphere( aSpheresA[aSphereIndicesA[i]], modelB.spheres[aSphereIndicesB[j]], spherepoints[numCollisions], coldist); for(j = 0; j < numBoxesB; j++) hasCollided |= ProcessSphereBox( aSpheresA[aSphereIndicesA[i]], modelB.boxes[aBoxIndicesB[j]], spherepoints[numCollisions], coldist); for(j = 0; j < numTrianglesB; j++) hasCollided |= ProcessSphereTriangle( aSpheresA[aSphereIndicesA[i]], modelB.vertices, modelB.triangles[aTriangleIndicesB[j]], modelB.trianglePlanes[aTriangleIndicesB[j]], spherepoints[numCollisions], coldist); if(hasCollided) numCollisions++; } for(i = 0; i < numCollisions; i++){ spherepoints[i].point = matrixB * spherepoints[i].point; spherepoints[i].normal = Multiply3x3(matrixB, spherepoints[i].normal); } // And the same thing for the lines in A for(i = 0; i < numLinesA; i++){ aCollided[i] = false; for(j = 0; j < numSpheresB; j++) aCollided[i] |= ProcessLineSphere( aLinesA[aLineIndicesA[i]], modelB.spheres[aSphereIndicesB[j]], linepoints[aLineIndicesA[i]], linedists[aLineIndicesA[i]]); for(j = 0; j < numBoxesB; j++) aCollided[i] |= ProcessLineBox( aLinesA[aLineIndicesA[i]], modelB.boxes[aBoxIndicesB[j]], linepoints[aLineIndicesA[i]], linedists[aLineIndicesA[i]]); for(j = 0; j < numTrianglesB; j++) aCollided[i] |= ProcessLineTriangle( aLinesA[aLineIndicesA[i]], modelB.vertices, modelB.triangles[aTriangleIndicesB[j]], modelB.trianglePlanes[aTriangleIndicesB[j]], linepoints[aLineIndicesA[i]], linedists[aLineIndicesA[i]]); } for(i = 0; i < numLinesA; i++) if(aCollided[i]){ j = aLineIndicesA[i]; linepoints[j].point = matrixB * linepoints[j].point; linepoints[j].normal = Multiply3x3(matrixB, linepoints[j].normal); } return numCollisions; // sphere collisions #endif } // // Misc // float CCollision::DistToLine(const CVector *l0, const CVector *l1, const CVector *point) { float lensq = (*l1 - *l0).MagnitudeSqr(); float dot = DotProduct(*point - *l0, *l1 - *l0); // Between 0 and len we're above the line. // if not, calculate distance to endpoint if(dot <= 0.0f) return (*point - *l0).Magnitude(); if(dot >= lensq) return (*point - *l1).Magnitude(); // distance to line float distSqr = (*point - *l0).MagnitudeSqr() - dot * dot / lensq; if(distSqr <= 0.f) return 0.f; return Sqrt(distSqr); } // same as above but also return the point on the line float CCollision::DistToLine(const CVector *l0, const CVector *l1, const CVector *point, CVector &closest) { float lensq = (*l1 - *l0).MagnitudeSqr(); float dot = DotProduct(*point - *l0, *l1 - *l0); // find out which point we're closest to if(dot <= 0.0f) closest = *l0; else if(dot >= lensq) closest = *l1; else closest = *l0 + (*l1 - *l0)*(dot/lensq); // this is the distance return (*point - closest).Magnitude(); } void CCollision::CalculateTrianglePlanes(CColModel *model) { assert(model); if(model->numTriangles == 0) return; CLink *lptr; if(model->trianglePlanes){ // re-insert at front so it's not removed again soon lptr = model->GetLinkPtr(); lptr->Remove(); ms_colModelCache.head.Insert(lptr); }else{ lptr = ms_colModelCache.Insert(model); if(lptr == nil){ // make room if we have to, remove last in list lptr = ms_colModelCache.tail.prev; assert(lptr); assert(lptr->item); lptr->item->RemoveTrianglePlanes(); ms_colModelCache.Remove(lptr); // now this cannot fail lptr = ms_colModelCache.Insert(model); assert(lptr); } model->CalculateTrianglePlanes(); model->SetLinkPtr(lptr); } } void CCollision::RemoveTrianglePlanes(CColModel *model) { if(model->trianglePlanes){ ms_colModelCache.Remove(model->GetLinkPtr()); model->RemoveTrianglePlanes(); } } void CCollision::DrawColModel(const CMatrix &mat, const CColModel &colModel) { int i; CVector min, max; CVector verts[8]; CVector c; float r; RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); min = colModel.boundingBox.min; max = colModel.boundingBox.max; verts[0] = mat * CVector(min.x, min.y, min.z); verts[1] = mat * CVector(min.x, min.y, max.z); verts[2] = mat * CVector(min.x, max.y, min.z); verts[3] = mat * CVector(min.x, max.y, max.z); verts[4] = mat * CVector(max.x, min.y, min.z); verts[5] = mat * CVector(max.x, min.y, max.z); verts[6] = mat * CVector(max.x, max.y, min.z); verts[7] = mat * CVector(max.x, max.y, max.z); CLines::RenderLineWithClipping( verts[0].x, verts[0].y, verts[0].z, verts[1].x, verts[1].y, verts[1].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[1].x, verts[1].y, verts[1].z, verts[3].x, verts[3].y, verts[3].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[3].x, verts[3].y, verts[3].z, verts[2].x, verts[2].y, verts[2].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[2].x, verts[2].y, verts[2].z, verts[0].x, verts[0].y, verts[0].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[4].x, verts[4].y, verts[4].z, verts[5].x, verts[5].y, verts[5].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[5].x, verts[5].y, verts[5].z, verts[7].x, verts[7].y, verts[7].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[7].x, verts[7].y, verts[7].z, verts[6].x, verts[6].y, verts[6].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[6].x, verts[6].y, verts[6].z, verts[4].x, verts[4].y, verts[4].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[0].x, verts[0].y, verts[0].z, verts[4].x, verts[4].y, verts[4].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[1].x, verts[1].y, verts[1].z, verts[5].x, verts[5].y, verts[5].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[2].x, verts[2].y, verts[2].z, verts[6].x, verts[6].y, verts[6].z, 0xFF0000FF, 0xFF0000FF); CLines::RenderLineWithClipping( verts[3].x, verts[3].y, verts[3].z, verts[7].x, verts[7].y, verts[7].z, 0xFF0000FF, 0xFF0000FF); for(i = 0; i < colModel.numSpheres; i++){ c = mat * colModel.spheres[i].center; r = colModel.spheres[i].radius; CLines::RenderLineWithClipping( c.x, c.y, c.z-r, c.x-r, c.y-r, c.z, 0xFF00FFFF, 0xFF00FFFF); CLines::RenderLineWithClipping( c.x, c.y, c.z-r, c.x-r, c.y+r, c.z, 0xFF00FFFF, 0xFF00FFFF); CLines::RenderLineWithClipping( c.x, c.y, c.z-r, c.x+r, c.y-r, c.z, 0xFF00FFFF, 0xFF00FFFF); CLines::RenderLineWithClipping( c.x, c.y, c.z-r, c.x+r, c.y+r, c.z, 0xFF00FFFF, 0xFF00FFFF); CLines::RenderLineWithClipping( c.x-r, c.y-r, c.z, c.x, c.y, c.z+r, 0xFF00FFFF, 0xFF00FFFF); CLines::RenderLineWithClipping( c.x-r, c.y+r, c.z, c.x, c.y, c.z+r, 0xFF00FFFF, 0xFF00FFFF); CLines::RenderLineWithClipping( c.x+r, c.y-r, c.z, c.x, c.y, c.z+r, 0xFF00FFFF, 0xFF00FFFF); CLines::RenderLineWithClipping( c.x+r, c.y+r, c.z, c.x, c.y, c.z+r, 0xFF00FFFF, 0xFF00FFFF); } for(i = 0; i < colModel.numLines; i++){ verts[0] = colModel.lines[i].p0; verts[1] = colModel.lines[i].p1; verts[0] = mat * verts[0]; verts[1] = mat * verts[1]; CLines::RenderLineWithClipping( verts[0].x, verts[0].y, verts[0].z, verts[1].x, verts[1].y, verts[1].z, 0x00FFFFFF, 0x00FFFFFF); } for(i = 0; i < colModel.numBoxes; i++){ min = colModel.boxes[i].min; max = colModel.boxes[i].max; verts[0] = mat * CVector(min.x, min.y, min.z); verts[1] = mat * CVector(min.x, min.y, max.z); verts[2] = mat * CVector(min.x, max.y, min.z); verts[3] = mat * CVector(min.x, max.y, max.z); verts[4] = mat * CVector(max.x, min.y, min.z); verts[5] = mat * CVector(max.x, min.y, max.z); verts[6] = mat * CVector(max.x, max.y, min.z); verts[7] = mat * CVector(max.x, max.y, max.z); CLines::RenderLineWithClipping( verts[0].x, verts[0].y, verts[0].z, verts[1].x, verts[1].y, verts[1].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[1].x, verts[1].y, verts[1].z, verts[3].x, verts[3].y, verts[3].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[3].x, verts[3].y, verts[3].z, verts[2].x, verts[2].y, verts[2].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[2].x, verts[2].y, verts[2].z, verts[0].x, verts[0].y, verts[0].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[4].x, verts[4].y, verts[4].z, verts[5].x, verts[5].y, verts[5].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[5].x, verts[5].y, verts[5].z, verts[7].x, verts[7].y, verts[7].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[7].x, verts[7].y, verts[7].z, verts[6].x, verts[6].y, verts[6].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[6].x, verts[6].y, verts[6].z, verts[4].x, verts[4].y, verts[4].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[0].x, verts[0].y, verts[0].z, verts[4].x, verts[4].y, verts[4].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[1].x, verts[1].y, verts[1].z, verts[5].x, verts[5].y, verts[5].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[2].x, verts[2].y, verts[2].z, verts[6].x, verts[6].y, verts[6].z, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping( verts[3].x, verts[3].y, verts[3].z, verts[7].x, verts[7].y, verts[7].z, 0xFFFFFFFF, 0xFFFFFFFF); } for(i = 0; i < colModel.numTriangles; i++){ colModel.GetTrianglePoint(verts[0], colModel.triangles[i].a); colModel.GetTrianglePoint(verts[1], colModel.triangles[i].b); colModel.GetTrianglePoint(verts[2], colModel.triangles[i].c); verts[0] = mat * verts[0]; verts[1] = mat * verts[1]; verts[2] = mat * verts[2]; CLines::RenderLineWithClipping( verts[0].x, verts[0].y, verts[0].z, verts[1].x, verts[1].y, verts[1].z, 0x00FF00FF, 0x00FF00FF); CLines::RenderLineWithClipping( verts[0].x, verts[0].y, verts[0].z, verts[2].x, verts[2].y, verts[2].z, 0x00FF00FF, 0x00FF00FF); CLines::RenderLineWithClipping( verts[1].x, verts[1].y, verts[1].z, verts[2].x, verts[2].y, verts[2].z, 0x00FF00FF, 0x00FF00FF); } RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); } static void GetSurfaceColor(uint8 surf, uint8 &r, uint8 &g, uint8 &b) { // game doesn't do this r = 255; g = 128; b = 0; switch(CSurfaceTable::GetAdhesionGroup(surf)){ case ADHESIVE_RUBBER: r = 255; g = 0; b = 0; break; case ADHESIVE_HARD: r = 255; g = 255; b = 128; break; case ADHESIVE_ROAD: r = 128; g = 128; b = 128; break; case ADHESIVE_LOOSE: r = 0; g = 255; b = 0; break; case ADHESIVE_SAND: r = 255; g = 128; b = 128; break; case ADHESIVE_WET: r = 0; g = 0; b = 255; break; } if(surf == SURFACE_SAND || surf == SURFACE_SAND_BEACH){ r = 255; g = 255; b = 0; } float f = (surf & 0xF)/32.0f + 0.5f; r *= f; g *= f; b *= f; if(surf == SURFACE_TRANSPARENT_CLOTH || surf == SURFACE_METAL_CHAIN_FENCE || surf == SURFACE_TRANSPARENT_STONE || surf == SURFACE_SCAFFOLD_POLE) if(CTimer::GetFrameCounter() & 1){ r = 0; g = 0; b = 0; } } void CCollision::DrawColModel_Coloured(const CMatrix &mat, const CColModel &colModel, int32 id) { int i; int s; CVector verts[8]; CVector min, max; uint8 r, g, b; RwImVertexIndex *iptr; RwIm3DVertex *vptr; RenderBuffer::ClearRenderBuffer(); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); for(i = 0; i < colModel.numTriangles; i++){ colModel.GetTrianglePoint(verts[0], colModel.triangles[i].a); colModel.GetTrianglePoint(verts[1], colModel.triangles[i].b); colModel.GetTrianglePoint(verts[2], colModel.triangles[i].c); verts[0] = mat * verts[0]; verts[1] = mat * verts[1]; verts[2] = mat * verts[2]; s = colModel.triangles[i].surface; GetSurfaceColor(s, r, g, b); if(s > SURFACE_METAL_GATE){ r = CGeneral::GetRandomNumber(); g = CGeneral::GetRandomNumber(); b = CGeneral::GetRandomNumber(); printf("Illegal surfacetype:%d on MI:%d\n", s, id); } RenderBuffer::StartStoring(6, 3, &iptr, &vptr); RwIm3DVertexSetRGBA(&vptr[0], r, g, b, 255); RwIm3DVertexSetRGBA(&vptr[1], r, g, b, 255); RwIm3DVertexSetRGBA(&vptr[2], r, g, b, 255); RwIm3DVertexSetU(&vptr[0], 0.0f); RwIm3DVertexSetV(&vptr[0], 0.0f); RwIm3DVertexSetU(&vptr[1], 0.0f); RwIm3DVertexSetV(&vptr[1], 1.0f); RwIm3DVertexSetU(&vptr[2], 1.0f); RwIm3DVertexSetV(&vptr[2], 1.0f); RwIm3DVertexSetPos(&vptr[0], verts[0].x, verts[0].y, verts[0].z); RwIm3DVertexSetPos(&vptr[1], verts[1].x, verts[1].y, verts[1].z); RwIm3DVertexSetPos(&vptr[2], verts[2].x, verts[2].y, verts[2].z); iptr[0] = 0; iptr[1] = 1; iptr[2] = 2; iptr[3] = 0; iptr[4] = 2; iptr[5] = 1; RenderBuffer::StopStoring(); } for(i = 0; i < colModel.numBoxes; i++){ min = colModel.boxes[i].min; max = colModel.boxes[i].max; verts[0] = mat * CVector(min.x, min.y, min.z); verts[1] = mat * CVector(min.x, min.y, max.z); verts[2] = mat * CVector(min.x, max.y, min.z); verts[3] = mat * CVector(min.x, max.y, max.z); verts[4] = mat * CVector(max.x, min.y, min.z); verts[5] = mat * CVector(max.x, min.y, max.z); verts[6] = mat * CVector(max.x, max.y, min.z); verts[7] = mat * CVector(max.x, max.y, max.z); s = colModel.boxes[i].surface; GetSurfaceColor(s, r, g, b); RenderBuffer::StartStoring(36, 8, &iptr, &vptr); RwIm3DVertexSetRGBA(&vptr[0], r, g, b, 255); RwIm3DVertexSetRGBA(&vptr[1], r, g, b, 255); RwIm3DVertexSetRGBA(&vptr[2], r, g, b, 255); RwIm3DVertexSetRGBA(&vptr[3], r, g, b, 255); RwIm3DVertexSetRGBA(&vptr[4], r, g, b, 255); RwIm3DVertexSetRGBA(&vptr[5], r, g, b, 255); RwIm3DVertexSetRGBA(&vptr[6], r, g, b, 255); RwIm3DVertexSetRGBA(&vptr[7], r, g, b, 255); RwIm3DVertexSetU(&vptr[0], 0.0f); RwIm3DVertexSetV(&vptr[0], 0.0f); RwIm3DVertexSetU(&vptr[1], 0.0f); RwIm3DVertexSetV(&vptr[1], 1.0f); RwIm3DVertexSetU(&vptr[2], 1.0f); RwIm3DVertexSetV(&vptr[2], 1.0f); RwIm3DVertexSetU(&vptr[3], 0.0f); RwIm3DVertexSetV(&vptr[3], 0.0f); RwIm3DVertexSetU(&vptr[4], 0.0f); RwIm3DVertexSetV(&vptr[4], 1.0f); RwIm3DVertexSetU(&vptr[5], 1.0f); RwIm3DVertexSetV(&vptr[5], 1.0f); RwIm3DVertexSetU(&vptr[6], 0.0f); RwIm3DVertexSetV(&vptr[6], 1.0f); RwIm3DVertexSetU(&vptr[7], 1.0f); RwIm3DVertexSetV(&vptr[7], 1.0f); RwIm3DVertexSetPos(&vptr[0], verts[0].x, verts[0].y, verts[0].z); RwIm3DVertexSetPos(&vptr[1], verts[1].x, verts[1].y, verts[1].z); RwIm3DVertexSetPos(&vptr[2], verts[2].x, verts[2].y, verts[2].z); RwIm3DVertexSetPos(&vptr[3], verts[3].x, verts[3].y, verts[3].z); RwIm3DVertexSetPos(&vptr[4], verts[4].x, verts[4].y, verts[4].z); RwIm3DVertexSetPos(&vptr[5], verts[5].x, verts[5].y, verts[5].z); RwIm3DVertexSetPos(&vptr[6], verts[6].x, verts[6].y, verts[6].z); RwIm3DVertexSetPos(&vptr[7], verts[7].x, verts[7].y, verts[7].z); iptr[0] = 0; iptr[1] = 1; iptr[2] = 2; iptr[3] = 1; iptr[4] = 3; iptr[5] = 2; iptr[6] = 1; iptr[7] = 5; iptr[8] = 7; iptr[9] = 1; iptr[10] = 7; iptr[11] = 3; iptr[12] = 2; iptr[13] = 3; iptr[14] = 7; iptr[15] = 2; iptr[16] = 7; iptr[17] = 6; iptr[18] = 0; iptr[19] = 5; iptr[20] = 1; iptr[21] = 0; iptr[22] = 4; iptr[23] = 5; iptr[24] = 0; iptr[25] = 2; iptr[26] = 4; iptr[27] = 2; iptr[28] = 6; iptr[29] = 4; iptr[30] = 4; iptr[31] = 6; iptr[32] = 7; iptr[33] = 4; iptr[34] = 7; iptr[35] = 5; RenderBuffer::StopStoring(); } RenderBuffer::RenderStuffInBuffer(); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); } ================================================ FILE: src/collision/Collision.h ================================================ #pragma once #include "ColModel.h" #include "Game.h" // for eLevelName #ifdef VU_COLLISION #include "VuVector.h" #endif struct CStoredCollPoly { #ifdef VU_COLLISION CVuVector verts[3]; #else CVector verts[3]; #endif bool valid; }; // If you spawn many tanks at once, you will see that collisions of two entity exceeds 32. #if defined(FIX_BUGS) && !defined(SQUEEZE_PERFORMANCE) #define MAX_COLLISION_POINTS 64 #else #define MAX_COLLISION_POINTS 32 #endif class CCollision { public: static eLevelName ms_collisionInMemory; static CLinkList ms_colModelCache; static void Init(void); static void Shutdown(void); static void Update(void); static void LoadCollisionWhenINeedIt(bool changeLevel); static void SortOutCollisionAfterLoad(void); static void LoadCollisionScreen(eLevelName level); static void DrawColModel(const CMatrix &mat, const CColModel &colModel); static void DrawColModel_Coloured(const CMatrix &mat, const CColModel &colModel, int32 id); static void CalculateTrianglePlanes(CColModel *model); static void RemoveTrianglePlanes(CColModel *model); // all these return true if there's a collision static bool TestSphereSphere(const CSphere &s1, const CSphere &s2); static bool TestSphereBox(const CSphere &sph, const CBox &box); static bool TestLineBox(const CColLine &line, const CBox &box); static bool TestVerticalLineBox(const CColLine &line, const CBox &box); static bool TestLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane); static bool TestLineSphere(const CColLine &line, const CColSphere &sph); static bool TestSphereTriangle(const CColSphere &sphere, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane); static bool TestLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, bool ignoreSeeThrough, bool ignoreShootThrough); static bool ProcessSphereSphere(const CColSphere &s1, const CColSphere &s2, CColPoint &point, float &mindistsq); static bool ProcessSphereBox(const CColSphere &sph, const CColBox &box, CColPoint &point, float &mindistsq); static bool ProcessLineBox(const CColLine &line, const CColBox &box, CColPoint &point, float &mindist); static bool ProcessVerticalLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist, CStoredCollPoly *poly); static bool ProcessLineTriangle(const CColLine &line , const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist, CStoredCollPoly *poly = nil); static bool ProcessLineSphere(const CColLine &line, const CColSphere &sphere, CColPoint &point, float &mindist); static bool ProcessSphereTriangle(const CColSphere &sph, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindistsq); static bool ProcessLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSeeThrough, bool ignoreShootThrough); static bool ProcessVerticalLine(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSeeThrough, bool ignoreShootThrough, CStoredCollPoly *poly); static int32 ProcessColModels(const CMatrix &matrixA, CColModel &modelA, const CMatrix &matrixB, CColModel &modelB, CColPoint *spherepoints, CColPoint *linepoints, float *linedists); static bool IsStoredPolyStillValidVerticalLine(const CVector &pos, float z, CColPoint &point, CStoredCollPoly *poly); static float DistToLine(const CVector *l0, const CVector *l1, const CVector *point); static float DistToLine(const CVector *l0, const CVector *l1, const CVector *point, CVector &closest); }; ================================================ FILE: src/collision/CompressedVector.h ================================================ #pragma once struct CompressedVector { #ifdef COMPRESSED_COL_VECTORS int16 x, y, z; CVector Get(void) const { return CVector(x, y, z)/128.0f; }; void Set(float x, float y, float z) { this->x = x*128.0f; this->y = y*128.0f; this->z = z*128.0f; }; #ifdef GTA_PS2 void Unpack(uint128 &qword) const { __asm__ volatile ( "lh $8, 0(%1)\n" "lh $9, 2(%1)\n" "lh $10, 4(%1)\n" "pextlw $10, $8\n" "pextlw $2, $9, $10\n" "sq $2, %0\n" : "=m" (qword) : "r" (this) : "$8", "$9", "$10", "$2" ); } #else void Unpack(int32 *qword) const { qword[0] = x; qword[1] = y; qword[2] = z; qword[3] = 0; // junk } #endif #else float x, y, z; CVector Get(void) const { return CVector(x, y, z); }; void Set(float x, float y, float z) { this->x = x; this->y = y; this->z = z; }; #endif }; ================================================ FILE: src/collision/TempColModels.cpp ================================================ #include "common.h" #include "TempColModels.h" #include "Game.h" CColModel CTempColModels::ms_colModelPed1; CColModel CTempColModels::ms_colModelPed2; CColModel CTempColModels::ms_colModelBBox; CColModel CTempColModels::ms_colModelBumper1; CColModel CTempColModels::ms_colModelWheel1; CColModel CTempColModels::ms_colModelPanel1; CColModel CTempColModels::ms_colModelBodyPart2; CColModel CTempColModels::ms_colModelBodyPart1; CColModel CTempColModels::ms_colModelCutObj[5]; CColModel CTempColModels::ms_colModelPedGroundHit; CColModel CTempColModels::ms_colModelBoot1; CColModel CTempColModels::ms_colModelDoor1; CColModel CTempColModels::ms_colModelBonnet1; CColModel CTempColModels::ms_colModelWeapon; CColSphere s_aPedSpheres[3]; CColSphere s_aPed2Spheres[3]; CColSphere s_aPedGSpheres[4]; #ifdef FIX_BUGS CColSphere s_aDoorSpheres[3]; #else CColSphere s_aDoorSpheres[4]; #endif CColSphere s_aBumperSpheres[4]; CColSphere s_aPanelSpheres[4]; CColSphere s_aBonnetSpheres[4]; CColSphere s_aBootSpheres[4]; CColSphere s_aWheelSpheres[2]; CColSphere s_aBodyPartSpheres1[2]; CColSphere s_aBodyPartSpheres2[2]; void CTempColModels::Initialise(void) { #define SET_COLMODEL_SPHERES(colmodel, sphrs)\ colmodel.numSpheres = ARRAY_SIZE(sphrs);\ colmodel.spheres = sphrs;\ colmodel.level = LEVEL_GENERIC;\ colmodel.ownsCollisionVolumes = false; int i; ms_colModelBBox.boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); ms_colModelBBox.boundingBox.Set(CVector(-2.0f, -2.0f, -2.0f), CVector(2.0f, 2.0f, 2.0f)); ms_colModelBBox.level = LEVEL_GENERIC; for (i = 0; i < ARRAY_SIZE(ms_colModelCutObj); i++) { ms_colModelCutObj[i].boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); ms_colModelCutObj[i].boundingBox.Set(CVector(-2.0f, -2.0f, -2.0f), CVector(2.0f, 2.0f, 2.0f)); ms_colModelCutObj[i].level = LEVEL_GENERIC; } // Ped Spheres for (i = 0; i < ARRAY_SIZE(s_aPedSpheres); i++) s_aPedSpheres[i].radius = 0.35f; s_aPedSpheres[0].center = CVector(0.0f, 0.0f, -0.25f); s_aPedSpheres[1].center = CVector(0.0f, 0.0f, 0.15f); s_aPedSpheres[2].center = CVector(0.0f, 0.0f, 0.55f); #ifdef FIX_BUGS for (i = 0; i < ARRAY_SIZE(s_aPedSpheres); i++) { #else for (i = 0; i < ARRAY_SIZE(s_aPedGSpheres); i++) { #endif s_aPedSpheres[i].surface = SURFACE_PED; s_aPedSpheres[i].piece = 0; } ms_colModelPed1.boundingSphere.Set(1.25f, CVector(0.0f, 0.0f, 0.0f)); ms_colModelPed1.boundingBox.Set(CVector(-0.35f, -0.35f, -1.0f), CVector(0.35f, 0.35f, 0.9f)); SET_COLMODEL_SPHERES(ms_colModelPed1, s_aPedSpheres); // Ped 2 Spheres s_aPed2Spheres[0].radius = 0.3f; s_aPed2Spheres[1].radius = 0.4f; s_aPed2Spheres[2].radius = 0.3f; s_aPed2Spheres[0].center = CVector(0.0f, 0.35f, -0.9f); s_aPed2Spheres[1].center = CVector(0.0f, 0.0f, -0.9f); s_aPed2Spheres[2].center = CVector(0.0f, -0.35f, -0.9f); for (i = 0; i < ARRAY_SIZE(s_aPed2Spheres); i++) { s_aPed2Spheres[i].surface = SURFACE_PED; s_aPed2Spheres[i].piece = 0; } ms_colModelPed2.boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); ms_colModelPed2.boundingBox.Set(CVector(-0.7f, -0.7f, -1.2f), CVector(0.7f, 0.7f, 0.0f)); SET_COLMODEL_SPHERES(ms_colModelPed2, s_aPed2Spheres); // Ped ground collision s_aPedGSpheres[0].radius = 0.35f; s_aPedGSpheres[1].radius = 0.35f; s_aPedGSpheres[2].radius = 0.35f; s_aPedGSpheres[3].radius = 0.3f; s_aPedGSpheres[0].center = CVector(0.0f, -0.4f, -0.9f); s_aPedGSpheres[1].center = CVector(0.0f, -0.1f, -0.9f); s_aPedGSpheres[2].center = CVector(0.0f, 0.25f, -0.9f); s_aPedGSpheres[3].center = CVector(0.0f, 0.65f, -0.9f); s_aPedGSpheres[0].surface = SURFACE_PED; s_aPedGSpheres[1].surface = SURFACE_PED; s_aPedGSpheres[2].surface = SURFACE_PED; s_aPedGSpheres[3].surface = SURFACE_PED; s_aPedGSpheres[0].piece = 4; s_aPedGSpheres[1].piece = 1; s_aPedGSpheres[2].piece = 0; s_aPedGSpheres[3].piece = 6; ms_colModelPedGroundHit.boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); ms_colModelPedGroundHit.boundingBox.Set(CVector(-0.4f, -1.0f, -1.25f), CVector(0.4f, 1.2f, -0.5f)); SET_COLMODEL_SPHERES(ms_colModelPedGroundHit, s_aPedGSpheres); // Door Spheres s_aDoorSpheres[0].radius = 0.15f; s_aDoorSpheres[1].radius = 0.15f; s_aDoorSpheres[2].radius = 0.25f; s_aDoorSpheres[0].center = CVector(0.0f, -0.25f, -0.35f); s_aDoorSpheres[1].center = CVector(0.0f, -0.95f, -0.35f); s_aDoorSpheres[2].center = CVector(0.0f, -0.6f, 0.25f); #ifdef FIX_BUGS for (i = 0; i < ARRAY_SIZE(s_aDoorSpheres); i++) { #else for (i = 0; i < ARRAY_SIZE(s_aPed2Spheres); i++) { #endif s_aDoorSpheres[i].surface = SURFACE_CAR_PANEL; s_aDoorSpheres[i].piece = 0; } ms_colModelDoor1.boundingSphere.Set(1.5f, CVector(0.0f, -0.6f, 0.0f)); ms_colModelDoor1.boundingBox.Set(CVector(-0.3f, 0.0f, -0.6f), CVector(0.3f, -1.2f, 0.6f)); SET_COLMODEL_SPHERES(ms_colModelDoor1, s_aDoorSpheres); // Bumper Spheres for (i = 0; i < ARRAY_SIZE(s_aBumperSpheres); i++) s_aBumperSpheres[i].radius = 0.15f; s_aBumperSpheres[0].center = CVector(0.85f, -0.05f, 0.0f); s_aBumperSpheres[1].center = CVector(0.4f, 0.05f, 0.0f); s_aBumperSpheres[2].center = CVector(-0.4f, 0.05f, 0.0f); s_aBumperSpheres[3].center = CVector(-0.85f, -0.05f, 0.0f); for (i = 0; i < ARRAY_SIZE(s_aBumperSpheres); i++) { s_aBumperSpheres[i].surface = SURFACE_CAR_PANEL; s_aBumperSpheres[i].piece = 0; } ms_colModelBumper1.boundingSphere.Set(2.2f, CVector(0.0f, -0.6f, 0.0f)); ms_colModelBumper1.boundingBox.Set(CVector(-1.2f, -0.3f, -0.2f), CVector(1.2f, 0.3f, 0.2f)); SET_COLMODEL_SPHERES(ms_colModelBumper1, s_aBumperSpheres); // Panel Spheres for (i = 0; i < ARRAY_SIZE(s_aPanelSpheres); i++) s_aPanelSpheres[i].radius = 0.15f; s_aPanelSpheres[0].center = CVector(0.15f, 0.45f, 0.0f); s_aPanelSpheres[1].center = CVector(0.15f, -0.45f, 0.0f); s_aPanelSpheres[2].center = CVector(-0.15f, -0.45f, 0.0f); s_aPanelSpheres[3].center = CVector(-0.15f, 0.45f, 0.0f); for (i = 0; i < ARRAY_SIZE(s_aPanelSpheres); i++) { s_aPanelSpheres[i].surface = SURFACE_CAR_PANEL; s_aPanelSpheres[i].piece = 0; } ms_colModelPanel1.boundingSphere.Set(1.4f, CVector(0.0f, 0.0f, 0.0f)); ms_colModelPanel1.boundingBox.Set(CVector(-0.3f, -0.6f, -0.15f), CVector(0.3f, 0.6f, 0.15f)); SET_COLMODEL_SPHERES(ms_colModelPanel1, s_aPanelSpheres); // Bonnet Spheres for (i = 0; i < ARRAY_SIZE(s_aBonnetSpheres); i++) s_aBonnetSpheres[i].radius = 0.2f; s_aBonnetSpheres[0].center = CVector(-0.4f, 0.1f, 0.0f); s_aBonnetSpheres[1].center = CVector(-0.4f, 0.9f, 0.0f); s_aBonnetSpheres[2].center = CVector(0.4f, 0.1f, 0.0f); s_aBonnetSpheres[3].center = CVector(0.4f, 0.9f, 0.0f); for (i = 0; i < ARRAY_SIZE(s_aBonnetSpheres); i++) { s_aBonnetSpheres[i].surface = SURFACE_CAR_PANEL; s_aBonnetSpheres[i].piece = 0; } ms_colModelBonnet1.boundingSphere.Set(1.7f, CVector(0.0f, 0.5f, 0.0f)); ms_colModelBonnet1.boundingBox.Set(CVector(-0.7f, -0.2f, -0.3f), CVector(0.7f, 1.2f, 0.3f)); SET_COLMODEL_SPHERES(ms_colModelBonnet1, s_aBonnetSpheres); // Boot Spheres for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) s_aBootSpheres[i].radius = 0.2f; s_aBootSpheres[0].center = CVector(-0.4f, -0.1f, 0.0f); s_aBootSpheres[1].center = CVector(-0.4f, -0.6f, 0.0f); s_aBootSpheres[2].center = CVector(0.4f, -0.1f, 0.0f); s_aBootSpheres[3].center = CVector(0.4f, -0.6f, 0.0f); for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) { s_aBootSpheres[i].surface = SURFACE_CAR_PANEL; s_aBootSpheres[i].piece = 0; } ms_colModelBoot1.boundingSphere.Set(1.4f, CVector(0.0f, -0.4f, 0.0f)); ms_colModelBoot1.boundingBox.Set(CVector(-0.7f, -0.9f, -0.3f), CVector(0.7f, 0.2f, 0.3f)); SET_COLMODEL_SPHERES(ms_colModelBoot1, s_aBootSpheres); // Wheel Spheres s_aWheelSpheres[0].radius = 0.35f; s_aWheelSpheres[1].radius = 0.35f; s_aWheelSpheres[0].center = CVector(-0.3f, 0.0f, 0.0f); s_aWheelSpheres[1].center = CVector(0.3f, 0.0f, 0.0f); #ifdef FIX_BUGS for (i = 0; i < ARRAY_SIZE(s_aWheelSpheres); i++) { #else for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) { #endif s_aWheelSpheres[i].surface = SURFACE_WHEELBASE; s_aWheelSpheres[i].piece = 0; } ms_colModelWheel1.boundingSphere.Set(1.4f, CVector(0.0f, 0.0f, 0.0f)); ms_colModelWheel1.boundingBox.Set(CVector(-0.7f, -0.4f, -0.4f), CVector(0.7f, 0.4f, 0.4f)); SET_COLMODEL_SPHERES(ms_colModelWheel1, s_aWheelSpheres); // Body Part Spheres 1 s_aBodyPartSpheres1[0].radius = 0.2f; s_aBodyPartSpheres1[1].radius = 0.2f; s_aBodyPartSpheres1[0].center = CVector(0.0f, 0.0f, 0.0f); s_aBodyPartSpheres1[1].center = CVector(0.8f, 0.0f, 0.0f); #ifdef FIX_BUGS for (i = 0; i < ARRAY_SIZE(s_aBodyPartSpheres1); i++) { #else for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) { #endif s_aBodyPartSpheres1[i].surface = SURFACE_PED; s_aBodyPartSpheres1[i].piece = 0; } ms_colModelBodyPart1.boundingSphere.Set(0.7f, CVector(0.4f, 0.0f, 0.0f)); ms_colModelBodyPart1.boundingBox.Set(CVector(-0.3f, -0.3f, -0.3f), CVector(1.1f, 0.3f, 0.3f)); SET_COLMODEL_SPHERES(ms_colModelBodyPart1, s_aBodyPartSpheres1); // Body Part Spheres 2 s_aBodyPartSpheres2[0].radius = 0.15f; s_aBodyPartSpheres2[1].radius = 0.15f; s_aBodyPartSpheres2[0].center = CVector(0.0f, 0.0f, 0.0f); s_aBodyPartSpheres2[1].center = CVector(0.5f, 0.0f, 0.0f); #ifdef FIX_BUGS for (i = 0; i < ARRAY_SIZE(s_aBodyPartSpheres2); i++) { #else for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) { #endif s_aBodyPartSpheres2[i].surface = SURFACE_PED; s_aBodyPartSpheres2[i].piece = 0; } ms_colModelBodyPart2.boundingSphere.Set(0.5f, CVector(0.25f, 0.0f, 0.0f)); ms_colModelBodyPart2.boundingBox.Set(CVector(-0.2f, -0.2f, -0.2f), CVector(0.7f, 0.2f, 0.2f)); SET_COLMODEL_SPHERES(ms_colModelBodyPart2, s_aBodyPartSpheres2); ms_colModelWeapon.boundingSphere.radius = 0.25f; ms_colModelWeapon.boundingBox.min.x = -0.25f; ms_colModelWeapon.boundingBox.min.y = -0.25f; ms_colModelWeapon.boundingBox.min.z = -0.25f; ms_colModelWeapon.boundingBox.max.x = 0.25f; ms_colModelWeapon.boundingBox.max.y = 0.25f; ms_colModelWeapon.boundingBox.max.z = 0.25f; #undef SET_COLMODEL_SPHERES } ================================================ FILE: src/collision/TempColModels.h ================================================ #pragma once #include "ColModel.h" class CTempColModels { public: static CColModel ms_colModelPed1; static CColModel ms_colModelPed2; static CColModel ms_colModelBBox; static CColModel ms_colModelBumper1; static CColModel ms_colModelWheel1; static CColModel ms_colModelPanel1; static CColModel ms_colModelBodyPart2; static CColModel ms_colModelBodyPart1; static CColModel ms_colModelCutObj[5]; static CColModel ms_colModelPedGroundHit; static CColModel ms_colModelBoot1; static CColModel ms_colModelDoor1; static CColModel ms_colModelBonnet1; static CColModel ms_colModelWeapon; static void Initialise(void); }; ================================================ FILE: src/collision/VuCollision.cpp ================================================ #include "common.h" #ifdef VU_COLLISION #include "VuVector.h" #include "VuCollision.h" #ifndef GTA_PS2 int16 vi01; CVuVector vf01; CVuVector vf02; CVuVector vf03; CVuVector DistanceBetweenSphereAndLine(const CVuVector ¢er, const CVuVector &p0, const CVuVector &line) { // center VF12 // p0 VF14 // line VF15 CVuVector ret; // VF16 CVuVector p1 = p0+line; CVuVector dist0 = center - p0; // VF20 CVuVector dist1 = center - p1; // VF25 float lenSq = line.MagnitudeSqr(); // VF21 float distSq0 = dist0.MagnitudeSqr(); // VF22 float distSq1 = dist1.MagnitudeSqr(); float dot = DotProduct(dist0, line); // VF23 if(dot < 0.0f){ // not above line, closest to p0 ret = p0; ret.w = distSq0; return ret; } float t = dot/lenSq; // param of nearest point on infinite line if(t > 1.0f){ // not above line, closest to p1 ret = p1; ret.w = distSq1; return ret; } // closest to line ret = p0 + line*t; ret.w = (ret - center).MagnitudeSqr(); return ret; } inline int SignFlags(const CVector &v) { int f = 0; if(v.x < 0.0f) f |= 1; if(v.y < 0.0f) f |= 2; if(v.z < 0.0f) f |= 4; return f; } #endif extern "C" void LineToTriangleCollision(const CVuVector &p0, const CVuVector &p1, const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, const CVuVector &plane) { #ifdef GTA_PS2 __asm__ volatile ( ".set noreorder\n" "lqc2 vf12, 0x0(%0)\n" "lqc2 vf13, 0x0(%1)\n" "lqc2 vf14, 0x0(%2)\n" "lqc2 vf15, 0x0(%3)\n" "lqc2 vf16, 0x0(%4)\n" "lqc2 vf17, 0x0(%5)\n" "vcallms Vu0LineToTriangleCollisionStart\n" ".set reorder\n" : : "r" (&p0), "r" (&p1), "r" (&v0), "r" (&v1), "r" (&v2), "r" (&plane) ); #else float dot0 = DotProduct(plane, p0); float dot1 = DotProduct(plane, p1); float dist0 = plane.w - dot0; float dist1 = plane.w - dot1; // if points are on the same side, no collision if(dist0 * dist1 > 0.0f){ vi01 = 0; return; } CVuVector diff = p1 - p0; float t = dist0/(dot1 - dot0); CVuVector p = p0 + diff*t; p.w = 0.0f; vf01 = p; vf03.x = t; // Check if point is inside CVector cross1 = CrossProduct(p-v0, v1-v0); CVector cross2 = CrossProduct(p-v1, v2-v1); CVector cross3 = CrossProduct(p-v2, v0-v2); // Only check relevant directions int flagmask = 0; if(Abs(plane.x) > 0.5f) flagmask |= 1; if(Abs(plane.y) > 0.5f) flagmask |= 2; if(Abs(plane.z) > 0.5f) flagmask |= 4; int flags1 = SignFlags(cross1) & flagmask; int flags2 = SignFlags(cross2) & flagmask; int flags3 = SignFlags(cross3) & flagmask; // inside if on the same side of all edges if(flags1 != flags2 || flags1 != flags3){ vi01 = 0; return; } vi01 = 1; vf02 = plane; return; #endif } extern "C" void LineToTriangleCollisionCompressed(const CVuVector &p0, const CVuVector &p1, VuTriangle &tri) { #ifdef GTA_PS2 __asm__ volatile ( ".set noreorder\n" "lqc2 vf12, 0x0(%0)\n" "lqc2 vf13, 0x0(%1)\n" "lqc2 vf14, 0x0(%2)\n" "lqc2 vf15, 0x10(%2)\n" "lqc2 vf16, 0x20(%2)\n" "lqc2 vf17, 0x30(%2)\n" "vcallms Vu0LineToTriangleCollisionCompressedStart\n" ".set reorder\n" : : "r" (&p0), "r" (&p1), "r" (&tri) ); #else CVuVector v0, v1, v2, plane; v0.x = tri.v0[0]/128.0f; v0.y = tri.v0[1]/128.0f; v0.z = tri.v0[2]/128.0f; v0.w = tri.v0[3]/128.0f; v1.x = tri.v1[0]/128.0f; v1.y = tri.v1[1]/128.0f; v1.z = tri.v1[2]/128.0f; v1.w = tri.v1[3]/128.0f; v2.x = tri.v2[0]/128.0f; v2.y = tri.v2[1]/128.0f; v2.z = tri.v2[2]/128.0f; v2.w = tri.v2[3]/128.0f; plane.x = tri.plane[0]/4096.0f; plane.y = tri.plane[1]/4096.0f; plane.z = tri.plane[2]/4096.0f; plane.w = tri.plane[3]/128.0f; LineToTriangleCollision(p0, p1, v0, v1, v2, plane); #endif } extern "C" void SphereToTriangleCollision(const CVuVector &sph, const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, const CVuVector &plane) { #ifdef GTA_PS2 __asm__ volatile ( ".set noreorder\n" "lqc2 vf12, 0x0(%0)\n" "lqc2 vf14, 0x0(%1)\n" "lqc2 vf15, 0x0(%2)\n" "lqc2 vf16, 0x0(%3)\n" "lqc2 vf17, 0x0(%4)\n" "vcallms Vu0SphereToTriangleCollisionStart\n" ".set reorder\n" : : "r" (&sph), "r" (&v0), "r" (&v1), "r" (&v2), "r" (&plane) ); #else float planedist = DotProduct(plane, sph) - plane.w; // VF02 if(Abs(planedist) > sph.w){ vi01 = 0; return; } // point on plane CVuVector p = sph - planedist*plane; p.w = 0.0f; vf01 = p; planedist = Abs(planedist); // edges CVuVector v01 = v1 - v0; CVuVector v12 = v2 - v1; CVuVector v20 = v0 - v2; // VU code calculates normal again for some weird reason... // Check sides of point CVector cross1 = CrossProduct(p-v0, v01); CVector cross2 = CrossProduct(p-v1, v12); CVector cross3 = CrossProduct(p-v2, v20); // Only check relevant directions int flagmask = 0; if(Abs(plane.x) > 0.1f) flagmask |= 1; if(Abs(plane.y) > 0.1f) flagmask |= 2; if(Abs(plane.z) > 0.1f) flagmask |= 4; int nflags = SignFlags(plane) & flagmask; int flags1 = SignFlags(cross1) & flagmask; int flags2 = SignFlags(cross2) & flagmask; int flags3 = SignFlags(cross3) & flagmask; int testcase = 0; CVuVector closest(0.0f, 0.0f, 0.0f); // VF04 if(flags1 == nflags){ closest += v2; testcase++; } if(flags2 == nflags){ closest += v0; testcase++; } if(flags3 == nflags){ closest += v1; testcase++; } if(testcase == 3){ // inside triangle - dist to plane already checked vf02 = plane; vf02.w = vf03.x = planedist; vi01 = 1; }else if(testcase == 1){ // outside two sides - closest to point opposide inside edge vf01 = closest; vf02 = sph - closest; float distSq = vf02.MagnitudeSqr(); vi01 = sph.w*sph.w > distSq; vf03.x = Sqrt(distSq); vf02 *= 1.0f/vf03.x; }else{ // inside two sides - closest to third edge if(flags1 != nflags) closest = DistanceBetweenSphereAndLine(sph, v0, v01); else if(flags2 != nflags) closest = DistanceBetweenSphereAndLine(sph, v1, v12); else closest = DistanceBetweenSphereAndLine(sph, v2, v20); vi01 = sph.w*sph.w > closest.w; vf01 = closest; vf02 = sph - closest; vf03.x = Sqrt(closest.w); vf02 *= 1.0f/vf03.x; } #endif } extern "C" void SphereToTriangleCollisionCompressed(const CVuVector &sph, VuTriangle &tri) { #ifdef GTA_PS2 __asm__ volatile ( ".set noreorder\n" "lqc2 vf12, 0x0(%0)\n" "lqc2 vf14, 0x0(%1)\n" "lqc2 vf15, 0x10(%1)\n" "lqc2 vf16, 0x20(%1)\n" "lqc2 vf17, 0x30(%1)\n" "vcallms Vu0SphereToTriangleCollisionCompressedStart\n" ".set reorder\n" : : "r" (&sph), "r" (&tri) ); #else CVuVector v0, v1, v2, plane; v0.x = tri.v0[0]/128.0f; v0.y = tri.v0[1]/128.0f; v0.z = tri.v0[2]/128.0f; v0.w = tri.v0[3]/128.0f; v1.x = tri.v1[0]/128.0f; v1.y = tri.v1[1]/128.0f; v1.z = tri.v1[2]/128.0f; v1.w = tri.v1[3]/128.0f; v2.x = tri.v2[0]/128.0f; v2.y = tri.v2[1]/128.0f; v2.z = tri.v2[2]/128.0f; v2.w = tri.v2[3]/128.0f; plane.x = tri.plane[0]/4096.0f; plane.y = tri.plane[1]/4096.0f; plane.z = tri.plane[2]/4096.0f; plane.w = tri.plane[3]/128.0f; SphereToTriangleCollision(sph, v0, v1, v2, plane); #endif } #endif ================================================ FILE: src/collision/VuCollision.h ================================================ #pragma once struct VuTriangle { // Compressed int16 but unpacked #ifdef GTA_PS2 uint128 v0; uint128 v1; uint128 v2; uint128 plane; #else int32 v0[4]; int32 v1[4]; int32 v2[4]; int32 plane[4]; #endif }; #ifndef GTA_PS2 extern int16 vi01; extern CVuVector vf01; extern CVuVector vf02; extern CVuVector vf03; #endif extern "C" { void LineToTriangleCollision(const CVuVector &p0, const CVuVector &p1, const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, const CVuVector &plane); void LineToTriangleCollisionCompressed(const CVuVector &p0, const CVuVector &p1, VuTriangle &tri); void SphereToTriangleCollision(const CVuVector &sph, const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, const CVuVector &plane); void SphereToTriangleCollisionCompressed(const CVuVector &sph, VuTriangle &tri); } ================================================ FILE: src/collision/vu0Collision.dsm ================================================ .align 4 .global Vu0CollisionDmaTag Vu0CollisionDmaTag: DMAcnt * MPG 0, * .vu .include "vu0Collision_1.s" .EndMPG .EndDmaData DMAend .global Vu0Collision2DmaTag Vu0Collision2DmaTag: DMAcnt * MPG 0, * .vu .include "vu0Collision_2.s" .EndMPG .EndDmaData DMAend .end ================================================ FILE: src/collision/vu0Collision_1.s ================================================ QuitAndFail: NOP[E] IADDIU VI01, VI00, 0 NOP NOP QuitAndSucceed: NOP[E] IADDIU VI01, VI00, 1 NOP NOP ; 20 -- unused ; VF12, VF13 xyz: sphere centers ; VF14, VF15 x: sphere radii ; out: ; VI01: set when collision ; VF01: supposed to be intersection point? ; VF02: normal (pointing towards s1, not normalized) .globl Vu0SphereToSphereCollision Vu0SphereToSphereCollision: SUB.xyz VF02, VF13, VF12 NOP ; dist of centers ADD.x VF04, VF14, VF15 NOP ; s = sum of radii MUL.xyzw VF03, VF02, VF02 NOP ; MUL.x VF04, VF04, VF04 DIV Q, VF14x, VF04x ; square s NOP NOP ; NOP NOP ; MULAx.w ACC, VF00, VF03 NOP ; MADDAy.w ACC, VF00, VF03 NOP ; MADDz.w VF03, VF00, VF03 NOP ; d = DistSq of centers NOP NOP ; MULAw.xyz ACC, VF12, VF00 NOP ; MADDq.xyz VF01, VF02, Q NOP ; intersection, but wrong CLIPw.xyz VF04, VF03 NOP ; compare s and d SUB.xyz VF02, VF00, VF02 NOP ; compute normal NOP NOP ; NOP NOP ; NOP FCAND VI01, 0x3 ; 0x2 cannot be set here NOP[E] NOP ; NOP NOP ; ; B8 -- unused ; VF12: ; VF13: radius ; VF14: ; VF15: box dimensions (?) .globl Vu0SphereToAABBCollision Vu0SphereToAABBCollision: SUB.xyz VF03, VF12, VF14 LOI 0.5 MULi.xyz VF15, VF15, I NOP MUL.x VF13, VF13, VF13 NOP SUB.xyz VF04, VF03, VF15 NOP ADD.xyz VF05, VF03, VF15 MR32.xyzw VF16, VF15 CLIPw.xyz VF03, VF16 MR32.xyzw VF17, VF16 MUL.xyz VF04, VF04, VF04 NOP MUL.xyz VF05, VF05, VF05 NOP CLIPw.xyz VF03, VF17 MR32.xyzw VF16, VF17 NOP FCAND VI01, 0x1 MINI.xyz VF04, VF04, VF05 MFIR.x VF09, VI01 NOP NOP CLIPw.xyz VF03, VF16 FCAND VI01, 0x4 NOP MFIR.y VF09, VI01 NOP NOP MULAx.w ACC, VF00, VF00 NOP ADD.xyz VF01, VF00, VF03 FCAND VI01, 0x10 NOP MFIR.z VF09, VI01 NOP LOI 2 NOP FCAND VI01, 0x30 SUBAw.xyz ACC, VF00, VF00 IADD VI04, VI00, VI01 ITOF0.xyz VF09, VF09 FCAND VI01, 0x300 NOP IADD VI03, VI00, VI01 NOP FCAND VI01, 0x3000 NOP IADD VI02, VI00, VI01 MADDi.xyzw VF09, VF09, I NOP NOP IBEQ VI04, VI00, IgnoreZValue NOP NOP MADDAz.w ACC, VF00, VF04 NOP MUL.z VF01, VF09, VF15 NOP IgnoreZValue: NOP IBEQ VI03, VI00, IgnoreYValue NOP NOP MADDAy.w ACC, VF00, VF04 NOP MUL.y VF01, VF09, VF15 NOP IgnoreYValue: NOP IBEQ VI02, VI00, IgnoreXValue NOP NOP MADDAx.w ACC, VF00, VF04 NOP MUL.x VF01, VF09, VF15 NOP IgnoreXValue: MADDx.w VF06, VF00, VF00 NOP SUB.xyz VF02, VF03, VF01 NOP ADD.xyz VF01, VF01, VF14 NOP MULx.w VF01, VF00, VF00 NOP CLIPw.xyz VF13, VF06 NOP NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x1 QuitMicrocode: NOP[E] NOP NOP NOP ; 240 .globl Vu0LineToSphereCollision Vu0LineToSphereCollision: SUB.xyzw VF01, VF13, VF12 NOP SUB.xyzw VF02, VF14, VF12 NOP MUL.xyz VF03, VF01, VF02 NOP MUL.xyz VF04, VF01, VF01 NOP MUL.x VF15, VF15, VF15 NOP MUL.xyz VF02, VF02, VF02 NOP MULAx.w ACC, VF00, VF03 NOP MADDAy.w ACC, VF00, VF03 NOP MADDz.w VF03, VF00, VF03 NOP MULAx.w ACC, VF00, VF04 NOP MADDAy.w ACC, VF00, VF04 NOP MADDz.w VF01, VF00, VF04 NOP MULAx.w ACC, VF00, VF02 NOP MADDAy.w ACC, VF00, VF02 NOP MADDz.w VF02, VF00, VF02 NOP MULA.w ACC, VF03, VF03 NOP MADDAx.w ACC, VF01, VF15 NOP MSUB.w VF05, VF01, VF02 NOP NOP NOP NOP NOP NOP IADDIU VI02, VI00, 0x10 NOP FMAND VI01, VI02 NOP IBNE VI01, VI00, QuitAndFail NOP NOP CLIPw.xyz VF15, VF02 SQRT Q, VF05w NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x1 NOP IBNE VI00, VI01, LineStartInsideSphere NOP NOP SUBq.w VF05, VF03, Q NOP SUB.w VF05, VF05, VF01 DIV Q, VF05w, VF01w NOP FMAND VI01, VI02 NOP IBNE VI01, VI00, QuitAndFail NOP NOP NOP FMAND VI01, VI02 NOP IBEQ VI01, VI00, QuitAndFail NOP NOP ADDA.xyz ACC, VF12, VF00 NOP MADDq.xyz VF01, VF01, Q NOP MULx.w VF01, VF00, VF00 NOP SUB.xyz VF02, VF01, VF14 NOP NOP[E] NOP NOP NOP LineStartInsideSphere: NOP MOVE.xyzw VF01, VF12 NOP[E] IADDIU VI01, VI00, 0x1 NOP NOP ; 3C0 .globl Vu0LineToAABBCollision Vu0LineToAABBCollision: SUB.xyzw VF08, VF13, VF12 LOI 0.5 MULi.xyz VF15, VF15, I IADDIU VI08, VI00, 0x0 SUB.xyzw VF12, VF12, VF14 NOP SUB.xyzw VF13, VF13, VF14 NOP NOP DIV Q, VF00w, VF08x NOP MR32.xyzw VF03, VF15 SUB.xyz VF06, VF15, VF12 NOP ADD.xyz VF07, VF15, VF12 NOP NOP NOP CLIPw.xyz VF12, VF03 MR32.xyzw VF04, VF03 NOP NOP ADDq.x VF09, VF00, Q DIV Q, VF00w, VF08y NOP NOP CLIPw.xyz VF12, VF04 MR32.xyzw VF05, VF04 SUB.xyz VF07, VF00, VF07 IADDIU VI06, VI00, 0xCC NOP IADDIU VI07, VI00, 0x30 NOP NOP CLIPw.xyz VF12, VF05 FCGET VI02 NOP IAND VI02, VI02, VI06 ADDq.y VF09, VF00, Q DIV Q, VF00w, VF08z SUB.xyz VF10, VF00, VF10 NOP CLIPw.xyz VF13, VF03 FCGET VI03 CLIPw.xyz VF13, VF04 IAND VI03, VI03, VI07 CLIPw.xyz VF13, VF05 FCAND VI01, 0x3330 NOP IBEQ VI01, VI00, StartPointInsideAABB NOP NOP ADDq.z VF09, VF00, Q FCGET VI04 NOP FCGET VI05 NOP IAND VI04, VI04, VI06 NOP IAND VI05, VI05, VI07 MULx.xyz VF17, VF08, VF09 NOP MULy.xyz VF18, VF08, VF09 IADDIU VI07, VI00, 0x80 MULz.xyz VF19, VF08, VF09 IAND VI06, VI02, VI07 MUL.w VF10, VF00, VF00 IAND VI07, VI04, VI07 NOP NOP NOP IBEQ VI06, VI07, CheckMaxXSide NOP NOP MULAx.xyz ACC, VF17, VF07 NOP MADDw.xyz VF16, VF12, VF00 NOP MUL.x VF10, VF07, VF09 NOP CLIPw.xyz VF16, VF04 NOP CLIPw.xyz VF16, VF05 NOP NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x330 NOP IBNE VI01, VI00, CheckMaxXSide NOP NOP MULx.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 ADD.yz VF02, VF00, VF00 MOVE.xyzw VF01, VF16 SUBw.x VF02, VF00, VF00 NOP CheckMaxXSide: MULAx.xyz ACC, VF17, VF06 IADDIU VI07, VI00, 0x40 MADDw.xyz VF16, VF12, VF00 IAND VI06, VI02, VI07 MUL.x VF10, VF06, VF09 IAND VI07, VI04, VI07 NOP NOP NOP IBEQ VI06, VI07, CheckMinYSide NOP NOP CLIPw.xyz VF16, VF04 NOP CLIPw.xyz VF16, VF05 NOP CLIPw.xyz VF10, VF10 NOP NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0xCC03 NOP IBNE VI01, VI00, CheckMinYSide NOP NOP MULx.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 ADD.yz VF02, VF00, VF00 MOVE.xyzw VF01, VF16 ADDw.x VF02, VF00, VF00 NOP CheckMinYSide: MULAy.xyz ACC, VF18, VF07 IADDIU VI07, VI00, 0x8 MADDw.xyz VF16, VF12, VF00 IAND VI06, VI02, VI07 MUL.y VF10, VF07, VF09 IAND VI07, VI04, VI07 NOP NOP NOP IBEQ VI06, VI07, CheckMaxYSide NOP NOP CLIPw.xyz VF16, VF03 NOP CLIPw.xyz VF16, VF05 NOP CLIPw.xyz VF10, VF10 NOP NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x3C0C NOP IBNE VI01, VI00, CheckMaxYSide NOP NOP MULy.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 ADD.xz VF02, VF00, VF00 MOVE.xyzw VF01, VF16 SUBw.y VF02, VF00, VF00 NOP CheckMaxYSide: MULAy.xyz ACC, VF18, VF06 IADDIU VI07, VI00, 0x4 MADDw.xyz VF16, VF12, VF00 IAND VI06, VI02, VI07 MUL.y VF10, VF06, VF09 IAND VI07, VI04, VI07 NOP NOP NOP IBEQ VI06, VI07, CheckMinZSide NOP NOP CLIPw.xyz VF16, VF03 NOP CLIPw.xyz VF16, VF05 NOP CLIPw.xyz VF10, VF10 NOP NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x3C0C NOP IBNE VI01, VI00, CheckMinZSide NOP NOP MULy.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 ADD.xz VF02, VF00, VF00 MOVE.xyzw VF01, VF16 ADDw.y VF02, VF00, VF00 NOP CheckMinZSide: MULAz.xyz ACC, VF19, VF07 IADDIU VI07, VI00, 0x20 MADDw.xyz VF16, VF12, VF00 IAND VI06, VI03, VI07 MUL.z VF10, VF07, VF09 IAND VI07, VI05, VI07 NOP NOP NOP IBEQ VI06, VI07, CheckMaxZSide NOP NOP CLIPw.xyz VF16, VF03 NOP CLIPw.xyz VF16, VF04 NOP CLIPw.xyz VF10, VF10 NOP NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x3330 NOP IBNE VI01, VI00, CheckMaxZSide NOP NOP MULz.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 ADD.xy VF02, VF00, VF00 MOVE.xyzw VF01, VF16 SUBw.z VF02, VF00, VF00 NOP CheckMaxZSide: MULAz.xyz ACC, VF19, VF06 IADDIU VI07, VI00, 0x10 MADDw.xyz VF16, VF12, VF00 IAND VI06, VI03, VI07 MUL.z VF10, VF06, VF09 IAND VI07, VI05, VI07 NOP NOP NOP IBEQ VI06, VI07, DoneAllChecks NOP NOP CLIPw.xyz VF16, VF03 NOP CLIPw.xyz VF16, VF04 NOP CLIPw.xyz VF10, VF10 NOP NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x3330 NOP IBNE VI01, VI00, DoneAllChecks NOP NOP MULz.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1 ADD.xy VF02, VF00, VF00 MOVE.xyzw VF01, VF16 ADDw.z VF02, VF00, VF00 NOP DoneAllChecks: ADD.xyz VF01, VF01, VF14 IADD VI01, VI00, VI08 NOP[E] NOP NOP NOP StartPointInsideAABB: ADD.xyz VF01, VF12, VF14 WAITQ NOP IADDIU VI01, VI00, 0x1 NOP[E] NOP NOP NOP ; 860 .globl Vu0LineToTriangleCollisionCompressedStart Vu0LineToTriangleCollisionCompressedStart: ITOF0.xyzw VF17, VF17 LOI 0.000244140625 ; 1.0/4096.0 ITOF0.xyzw VF14, VF14 NOP ITOF0.xyzw VF15, VF15 NOP ITOF0.xyzw VF16, VF16 NOP MULi.xyz VF17, VF17, I LOI 0.0078125 ; 1.0/128.0 MULi.w VF17, VF17, I NOP MULi.xyzw VF14, VF14, I NOP MULi.xyzw VF15, VF15, I NOP MULi.xyzw VF16, VF16, I NOP ; fall through ; 8A8 ; VF12: point0 ; VF13: point1 ; VF14-16: verts ; VF17: plane ; out: ; VF01: intersection point ; VF02: triangle normal ; VF03 x: intersection parameter .globl Vu0LineToTriangleCollisionStart Vu0LineToTriangleCollisionStart: MUL.xyz VF10, VF17, VF12 LOI 0.5 MUL.xyz VF11, VF17, VF13 NOP SUB.xyz VF02, VF13, VF12 NOP ; line dist ADD.xyz VF17, VF17, VF00 NOP MULi.w VF03, VF00, I NOP MULAx.w ACC, VF00, VF10 NOP MADDAy.w ACC, VF00, VF10 IADDIU VI06, VI00, 0xE0 MADDz.w VF10, VF00, VF10 FMAND VI05, VI06 ; -- normal sign flags, unused MULAx.w ACC, VF00, VF11 NOP MADDAy.w ACC, VF00, VF11 NOP MADDz.w VF11, VF00, VF11 NOP SUB.w VF09, VF17, VF10 NOP ; plane-pos 0 CLIPw.xyz VF17, VF03 NOP ; compare normal against 0.5 to figure out which in which dimension to compare NOP IADDIU VI02, VI00, 0x10 ; Sw flag SUBA.w ACC, VF17, VF11 NOP ; plane-pos 1 SUB.w VF08, VF11, VF10 FMAND VI01, VI02 NOP NOP NOP NOP NOP FMAND VI02, VI02 NOP IBEQ VI01, VI02, QuitAndFail ; if on same side, no collision NOP NOP NOP DIV Q, VF09w, VF08w ; parameter of intersection NOP FCAND VI01, 0x3 ; check x direction NOP IADDIU VI02, VI01, 0x7F NOP IADDIU VI06, VI00, 0x80 NOP IAND VI02, VI02, VI06 ; Sx flag NOP FCAND VI01, 0xC ; check y direction NOP IADDIU VI03, VI01, 0x3F MULAw.xyz ACC, VF12, VF00 IADDIU VI06, VI00, 0x40 MADDq.xyz VF01, VF02, Q IAND VI03, VI03, VI06 ; point of intersection -- Sy flag MULx.w VF01, VF00, VF00 FCAND VI01, 0x30 ; -- check z direction ADDq.x VF03, VF00, Q IADDIU VI04, VI01, 0x1F ; output parameter SUB.xyz VF05, VF15, VF14 IADDIU VI06, VI00, 0x20 ; edge vectors SUB.xyz VF08, VF01, VF14 IAND VI04, VI04, VI06 ; edge vectors -- Sz flag SUB.xyz VF06, VF16, VF15 IADD VI06, VI02, VI03 ; edge vectors SUB.xyz VF09, VF01, VF15 IADD VI06, VI06, VI04 ; edge vectors -- combine flags SUB.xyz VF07, VF14, VF16 NOP ; edge vectors SUB.xyz VF10, VF01, VF16 NOP ; edge vectors OPMULA.xyz ACC, VF08, VF05 NOP OPMSUB.xyz VF18, VF05, VF08 NOP ; cross1 OPMULA.xyz ACC, VF09, VF06 NOP OPMSUB.xyz VF19, VF06, VF09 NOP ; cross2 OPMULA.xyz ACC, VF10, VF07 NOP OPMSUB.xyz VF20, VF07, VF10 FMAND VI02, VI06 ; cross3 NOP NOP NOP FMAND VI03, VI06 NOP NOP NOP FMAND VI04, VI06 NOP NOP NOP IBNE VI03, VI02, QuitAndFail ; point has to lie on the same side of all edges (i.e. inside) NOP NOP NOP IBNE VI04, VI02, QuitAndFail NOP NOP MULw.xyz VF02, VF17, VF00 IADDIU VI01, VI00, 0x1 ; success NOP[E] NOP NOP NOP ; A68 ; VF12: center ; VF14: line origin ; VF15: line vector to other point ; out: VF16 xyz: nearest point on line; w: distance to that point DistanceBetweenSphereAndLine: SUB.xyz VF20, VF12, VF14 NOP MUL.xyz VF21, VF15, VF15 NOP ADDA.xyz ACC, VF14, VF15 NOP MSUBw.xyz VF25, VF12, VF00 NOP ; VF25 = VF12 - (VF14+VF15) MUL.xyz VF22, VF20, VF20 NOP MUL.xyz VF23, VF20, VF15 NOP MULAx.w ACC, VF00, VF21 NOP MADDAy.w ACC, VF00, VF21 NOP MADDz.w VF21, VF00, VF21 NOP ; MagSq VF15 (line length) MULAx.w ACC, VF00, VF23 NOP MADDAy.w ACC, VF00, VF23 NOP MADDz.w VF23, VF00, VF23 NOP ; dot(VF12-VF14, VF15) MULAx.w ACC, VF00, VF22 NOP MADDAy.w ACC, VF00, VF22 NOP MADDz.w VF22, VF00, VF22 IADDIU VI08, VI00, 0x10 ; MagSq VF12-VF14 -- Sw bit MUL.xyz VF25, VF25, VF25 FMAND VI08, VI08 NOP DIV Q, VF23w, VF21w NOP IBNE VI00, VI08, NegativeRatio NOP NOP ADDA.xyz ACC, VF00, VF14 NOP MADDq.xyz VF16, VF15, Q WAITQ ; nearest point on infinte line ADDq.x VF24, VF00, Q NOP ; ratio NOP NOP NOP NOP SUB.xyz VF26, VF16, VF12 NOP CLIPw.xyz VF24, VF00 NOP ; compare ratio to 1.0 NOP NOP NOP NOP MUL.xyz VF26, VF26, VF26 NOP NOP FCAND VI01, 0x1 NOP IBNE VI00, VI01, RatioGreaterThanOne NOP NOP MULAx.w ACC, VF00, VF26 NOP MADDAy.w ACC, VF00, VF26 NOP MADDz.w VF16, VF00, VF26 NOP ; distance NOP JR VI15 NOP NOP NegativeRatio: ADD.xyz VF16, VF00, VF14 NOP ; return line origin MUL.w VF16, VF00, VF22 NOP ; and DistSq to it NOP JR VI15 NOP NOP RatioGreaterThanOne: MULAx.w ACC, VF00, VF25 NOP MADDAy.w ACC, VF00, VF25 NOP MADDz.w VF16, VF00, VF25 NOP ADD.xyz VF16, VF14, VF15 NOP ; return toerh line point NOP JR VI15 NOP NOP ; BE0 .globl Vu0SphereToTriangleCollisionCompressedStart Vu0SphereToTriangleCollisionCompressedStart: ITOF0.xyzw VF17, VF17 LOI 0.000244140625 ; 1.0/4096.0 ITOF0.xyzw VF14, VF14 NOP ITOF0.xyzw VF15, VF15 NOP ITOF0.xyzw VF16, VF16 NOP MULi.xyz VF17, VF17, I LOI 0.0078125 ; 1.0/128.0 MULi.w VF17, VF17, I NOP MULi.xyzw VF14, VF14, I NOP MULi.xyzw VF15, VF15, I NOP MULi.xyzw VF16, VF16, I NOP ; fall through ; C28 ; VF12: sphere ; VF14-16: verts ; VF17: plane ; out: ; VF01: intersection point ; VF02: triangle normal ; VF03 x: intersection parameter .globl Vu0SphereToTriangleCollisionStart Vu0SphereToTriangleCollisionStart: MUL.xyz VF02, VF12, VF17 LOI 0.1 ADD.xyz VF17, VF17, VF00 NOP ADDw.x VF13, VF00, VF12 NOP NOP NOP MULAx.w ACC, VF00, VF02 IADDIU VI06, VI00, 0xE0 MADDAy.w ACC, VF00, VF02 FMAND VI05, VI06 ; normal sign flags MADDAz.w ACC, VF00, VF02 NOP MSUB.w VF02, VF00, VF17 NOP ; center plane pos MULi.w VF03, VF00, I MOVE.xyzw VF04, VF03 NOP NOP NOP NOP CLIPw.xyz VF13, VF02 NOP ; compare dist and radius CLIPw.xyz VF17, VF03 NOP MULAw.xyz ACC, VF12, VF00 IADDIU VI07, VI00, 0x0 ; -- clear test case MSUBw.xyz VF01, VF17, VF02 NOP MULx.w VF01, VF00, VF00 FCAND VI01, 0x3 ; projected center on plane ABS.w VF02, VF02 IBEQ VI00, VI01, QuitAndFail ; no intersection NOP NOP NOP FCAND VI01, 0x3 ; -- check x direction SUB.xyz VF02, VF12, VF01 IADDIU VI02, VI01, 0x7F NOP IADDIU VI06, VI00, 0x80 SUB.xyz VF05, VF15, VF14 IAND VI02, VI02, VI06 SUB.xyz VF08, VF01, VF14 FCAND VI01, 0xC ; -- check y direction SUB.xyz VF06, VF16, VF15 IADDIU VI03, VI01, 0x3F SUB.xyz VF09, VF01, VF15 IADDIU VI06, VI00, 0x40 SUB.xyz VF07, VF14, VF16 IAND VI03, VI03, VI06 SUB.xyz VF10, VF01, VF16 FCAND VI01, 0x30 ; -- check z direction MUL.xyz VF03, VF02, VF02 IADDIU VI04, VI01, 0x1F OPMULA.xyz ACC, VF08, VF05 IADDIU VI06, VI00, 0x20 OPMSUB.xyz VF18, VF05, VF08 IAND VI04, VI04, VI06 OPMULA.xyz ACC, VF09, VF06 NOP OPMSUB.xyz VF19, VF06, VF09 IADD VI06, VI02, VI03 OPMULA.xyz ACC, VF10, VF07 IADD VI06, VI06, VI04 ; -- combine flags OPMSUB.xyz VF20, VF07, VF10 FMAND VI02, VI06 ; -- cross 1 flags MULAx.w ACC, VF00, VF03 IAND VI05, VI05, VI06 MADDAy.w ACC, VF00, VF03 FMAND VI03, VI06 ; -- cross 2 flags MADDz.w VF03, VF00, VF03 IADDIU VI08, VI00, 0x3 NOP FMAND VI04, VI06 ; -- cross 3 flags NOP NOP NOP IBNE VI02, VI05, CheckSide2 NOP RSQRT Q, VF00w, VF03w ADD.xyz VF04, VF00, VF16 IADDIU VI07, VI07, 0x1 ; inside side 1 CheckSide2: NOP IBNE VI03, VI05, CheckSide3 NOP NOP ADD.xyz VF04, VF00, VF14 IADDIU VI07, VI07, 0x1 ; inside side 2 CheckSide3: NOP IBNE VI04, VI05, FinishCheckingSides NOP NOP ADD.xyz VF04, VF00, VF15 IADDIU VI07, VI07, 0x1 ; inside side 3 NOP NOP NOP IBEQ VI07, VI08, TotallyInsideTriangle NOP NOP FinishCheckingSides: MUL.x VF13, VF13, VF13 IADDIU VI08, VI00, 0x2 MULq.xyz VF02, VF02, Q WAITQ NOP IBNE VI07, VI08, IntersectionOutsideTwoSides NOP NOP NOP IBEQ VI02, VI05, CheckDistanceSide2 NOP NOP NOP MOVE.xyzw VF15, VF05 NOP BAL VI15, DistanceBetweenSphereAndLine NOP NOP NOP B ProcessLineResult NOP NOP CheckDistanceSide2: NOP IBEQ VI03, VI05, CheckDistanceSide3 NOP NOP NOP MOVE.xyzw VF14, VF15 NOP MOVE.xyzw VF15, VF06 NOP BAL VI15, DistanceBetweenSphereAndLine NOP NOP NOP B ProcessLineResult NOP NOP CheckDistanceSide3: NOP MOVE.xyzw VF14, VF16 NOP MOVE.xyzw VF15, VF07 NOP BAL VI15, DistanceBetweenSphereAndLine NOP NOP NOP B ProcessLineResult NOP NOP IntersectionOutsideTwoSides: SUB.xyz VF05, VF04, VF12 NOP ADD.xyz VF01, VF00, VF04 NOP ; col point SUB.xyz VF02, VF12, VF04 NOP NOP NOP MUL.xyz VF05, VF05, VF05 NOP NOP NOP NOP NOP NOP NOP MULAx.w ACC, VF00, VF05 NOP MADDAy.w ACC, VF00, VF05 NOP MADDz.w VF05, VF00, VF05 NOP ; distSq to vertex NOP NOP NOP NOP NOP NOP CLIPw.xyz VF13, VF05 SQRT Q, VF05w ; compare radiusSq and distSq NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x1 ADDq.x VF03, VF00, Q WAITQ ; dist to vertex NOP IBEQ VI00, VI01, QuitAndFail ; too far NOP NOP NOP NOP NOP DIV Q, VF00w, VF03x MULq.xyz VF02, VF02, Q WAITQ ; col normal NOP[E] NOP NOP NOP TotallyInsideTriangle: ADDw.x VF03, VF00, VF02 WAITQ MULq.xyz VF02, VF02, Q NOP NOP[E] IADDIU VI01, VI00, 0x1 NOP NOP ProcessLineResult: CLIPw.xyz VF13, VF16 SQRT Q, VF16w ADD.xyz VF01, VF00, VF16 NOP SUB.xyz VF02, VF12, VF16 NOP NOP NOP NOP FCAND VI01, 0x1 ADDq.x VF03, VF00, Q WAITQ NOP IBEQ VI00, VI01, QuitAndFail NOP NOP NOP NOP NOP DIV Q, VF00w, VF03x MULq.xyz VF02, VF02, Q WAITQ NOP[E] NOP NOP NOP EndOfMicrocode: ================================================ FILE: src/collision/vu0Collision_2.s ================================================ QuitAndFail2: NOP[E] IADDIU VI01, VI00, 0x0 NOP NOP QuitAndSucceed2: NOP[E] IADDIU VI01, VI00, 0x1 NOP NOP ; 20 GetBBVertices: MULw.xy VF02, VF01, VF00 NOP MUL.z VF02, VF01, VF11 NOP MULw.xz VF03, VF01, VF00 NOP MUL.y VF03, VF01, VF11 NOP MULw.x VF04, VF01, VF00 NOP MUL.yz VF04, VF01, VF11 NOP NOP JR VI15 NOP NOP ; 60 Vu0OBBToOBBCollision: SUBw.xyz VF11, VF00, VF00 LOI 0.5 MULi.xyz VF12, VF12, I NOP MULi.xyz VF13, VF13, I NOP NOP NOP NOP NOP NOP MOVE.xyz VF01, VF12 NOP BAL VI15, GetBBVertices NOP NOP MULAx.xyz ACC, VF14, VF01 NOP MADDAy.xyz ACC, VF15, VF01 NOP MADDz.xyz VF01, VF16, VF01 NOP MULAx.xyz ACC, VF14, VF02 NOP MADDAy.xyz ACC, VF15, VF02 NOP MADDz.xyz VF02, VF16, VF02 NOP MULAx.xyz ACC, VF14, VF03 NOP MADDAy.xyz ACC, VF15, VF03 NOP MADDz.xyz VF03, VF16, VF03 NOP MULAx.xyz ACC, VF14, VF04 NOP MADDAy.xyz ACC, VF15, VF04 NOP MADDz.xyz VF04, VF16, VF04 NOP ABS.xyz VF05, VF01 NOP ABS.xyz VF06, VF02 NOP ABS.xyz VF07, VF03 NOP ABS.xyz VF08, VF04 NOP NOP NOP MAX.xyz VF05, VF05, VF06 NOP NOP NOP MAX.xyz VF07, VF07, VF08 NOP NOP NOP NOP NOP NOP NOP MAX.xyz VF05, VF05, VF07 NOP NOP NOP NOP NOP NOP NOP ADD.xyz VF09, VF05, VF13 NOP NOP NOP NOP NOP NOP NOP MULx.w VF05, VF00, VF09 NOP MULy.w VF06, VF00, VF09 NOP MULz.w VF07, VF00, VF09 NOP CLIPw.xyz VF17, VF05 NOP CLIPw.xyz VF17, VF06 NOP CLIPw.xyz VF17, VF07 MOVE.xyz VF01, VF13 NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x3330 NOP IBNE VI01, VI00, QuitAndFail2 NOP NOP NOP BAL VI15, GetBBVertices NOP NOP MULAx.xyz ACC, VF18, VF01 NOP MADDAy.xyz ACC, VF19, VF01 NOP MADDz.xyz VF01, VF20, VF01 NOP MULAx.xyz ACC, VF18, VF02 NOP MADDAy.xyz ACC, VF19, VF02 NOP MADDz.xyz VF02, VF20, VF02 NOP MULAx.xyz ACC, VF18, VF03 NOP MADDAy.xyz ACC, VF19, VF03 NOP MADDz.xyz VF03, VF20, VF03 NOP MULAx.xyz ACC, VF18, VF04 NOP MADDAy.xyz ACC, VF19, VF04 NOP MADDz.xyz VF04, VF20, VF04 NOP ABS.xyz VF05, VF01 NOP ABS.xyz VF06, VF02 NOP ABS.xyz VF07, VF03 NOP ABS.xyz VF08, VF04 NOP NOP NOP MAX.xyz VF05, VF05, VF06 NOP NOP NOP MAX.xyz VF07, VF07, VF08 NOP NOP NOP NOP NOP NOP NOP MAX.xyz VF05, VF05, VF07 NOP NOP NOP NOP NOP NOP NOP ADD.xyz VF09, VF05, VF12 NOP NOP NOP NOP NOP NOP NOP MULx.w VF05, VF00, VF09 NOP MULy.w VF06, VF00, VF09 NOP MULz.w VF07, VF00, VF09 NOP CLIPw.xyz VF21, VF05 NOP CLIPw.xyz VF21, VF06 NOP CLIPw.xyz VF21, VF07 NOP NOP NOP NOP NOP NOP NOP NOP FCAND VI01, 0x3330 NOP IBNE VI01, VI00, QuitAndFail2 NOP NOP SUB.xyz VF06, VF02, VF01 NOP SUB.xyz VF07, VF03, VF01 NOP ADD.xyz VF08, VF04, VF01 NOP ADD.x VF09, VF00, VF12 NOP ADD.yz VF09, VF00, VF00 NOP ADD.y VF10, VF00, VF12 NOP ADD.xz VF10, VF00, VF00 NOP ADD.z VF11, VF00, VF12 IADDI VI04, VI00, 0x0 ADD.xy VF11, VF00, VF00 IADD VI02, VI00, VI00 OPMULA.xyz ACC, VF06, VF09 NOP OPMSUB.xyz VF01, VF09, VF06 NOP OPMULA.xyz ACC, VF06, VF10 NOP OPMSUB.xyz VF02, VF10, VF06 NOP OPMULA.xyz ACC, VF06, VF11 NOP OPMSUB.xyz VF03, VF11, VF06 SQI.xyzw VF01, (VI02++) OPMULA.xyz ACC, VF07, VF09 NOP OPMSUB.xyz VF01, VF09, VF07 SQI.xyzw VF02, (VI02++) OPMULA.xyz ACC, VF07, VF10 NOP OPMSUB.xyz VF02, VF10, VF07 SQI.xyzw VF03, (VI02++) OPMULA.xyz ACC, VF07, VF11 NOP OPMSUB.xyz VF03, VF11, VF07 SQI.xyzw VF01, (VI02++) OPMULA.xyz ACC, VF08, VF09 NOP OPMSUB.xyz VF01, VF09, VF08 SQI.xyzw VF02, (VI02++) OPMULA.xyz ACC, VF08, VF10 NOP OPMSUB.xyz VF02, VF10, VF08 SQI.xyzw VF03, (VI02++) OPMULA.xyz ACC, VF08, VF11 LOI 0.5 OPMSUB.xyz VF01, VF11, VF08 SQI.xyzw VF01, (VI02++) MULi.xyz VF06, VF06, I NOP MULi.xyz VF07, VF07, I SQI.xyzw VF02, (VI02++) MULi.xyz VF08, VF08, I NOP MUL.xyz VF02, VF21, VF01 NOP MUL.xyz VF03, VF12, VF01 NOP MUL.xyz VF09, VF06, VF01 NOP MUL.xyz VF10, VF07, VF01 NOP MUL.xyz VF11, VF08, VF01 NOP ABS.xyz VF03, VF03 NOP ADDy.x VF05, VF09, VF09 NOP ADDx.y VF05, VF10, VF10 NOP ADDx.z VF05, VF11, VF11 NOP NOP NOP EdgePairLoop: ADDz.x VF05, VF05, VF09 NOP ADDz.y VF05, VF05, VF10 NOP ADDy.z VF05, VF05, VF11 NOP MULAx.w ACC, VF00, VF02 IADD VI03, VI02, VI00 MADDAy.w ACC, VF00, VF02 LQD.xyzw VF01, (--VI02) MADDz.w VF02, VF00, VF02 NOP ABS.xyz VF05, VF05 NOP MULAx.w ACC, VF00, VF03 NOP MADDAy.w ACC, VF00, VF03 NOP MADDAz.w ACC, VF00, VF03 NOP MADDAx.w ACC, VF00, VF05 NOP MADDAy.w ACC, VF00, VF05 NOP MADDz.w VF03, VF00, VF05 NOP ADDw.x VF04, VF00, VF02 NOP MUL.xyz VF02, VF21, VF01 NOP MUL.xyz VF03, VF12, VF01 NOP MUL.xyz VF09, VF06, VF01 NOP CLIPw.xyz VF04, VF03 NOP MUL.xyz VF10, VF07, VF01 NOP MUL.xyz VF11, VF08, VF01 NOP ABS.xyz VF03, VF03 NOP ADDy.x VF05, VF09, VF09 FCAND VI01, 0x3 ADDx.y VF05, VF10, VF10 IBNE VI01, VI00, QuitAndFail2 ADDx.z VF05, VF11, VF11 NOP NOP IBNE VI03, VI00, EdgePairLoop NOP NOP NOP[E] IADDIU VI01, VI00, 0x1 NOP NOP EndOfMicrocode2: ================================================ FILE: src/control/AutoPilot.cpp ================================================ #include "common.h" #include "AutoPilot.h" #include "CarCtrl.h" #include "Curves.h" #include "PathFind.h" void CAutoPilot::ModifySpeed(float speed) { m_fMaxTrafficSpeed = Max(0.01f, speed); float positionBetweenNodes = (float)(CTimer::GetTimeInMilliseconds() - m_nTimeEnteredCurve) / m_nTimeToSpendOnCurrentCurve; CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[m_nCurrentPathNodeInfo]; CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[m_nNextPathNodeInfo]; float currentPathLinkForwardX = m_nCurrentDirection * ThePaths.m_carPathLinks[m_nCurrentPathNodeInfo].GetDirX(); float currentPathLinkForwardY = m_nCurrentDirection * ThePaths.m_carPathLinks[m_nCurrentPathNodeInfo].GetDirY(); float nextPathLinkForwardX = m_nNextDirection * ThePaths.m_carPathLinks[m_nNextPathNodeInfo].GetDirX(); float nextPathLinkForwardY = m_nNextDirection * ThePaths.m_carPathLinks[m_nNextPathNodeInfo].GetDirY(); CVector positionOnCurrentLinkIncludingLane( pCurrentLink->GetX() + ((m_nCurrentLane + 0.5f) * LANE_WIDTH) * currentPathLinkForwardY, pCurrentLink->GetY() - ((m_nCurrentLane + 0.5f) * LANE_WIDTH) * currentPathLinkForwardX, 0.0f); CVector positionOnNextLinkIncludingLane( pNextLink->GetX() + ((m_nNextLane + 0.5f) * LANE_WIDTH) * nextPathLinkForwardY, pNextLink->GetY() - ((m_nNextLane + 0.5f) * LANE_WIDTH) * nextPathLinkForwardX, 0.0f); m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( &positionOnCurrentLinkIncludingLane, &positionOnNextLinkIncludingLane, currentPathLinkForwardX, currentPathLinkForwardY, nextPathLinkForwardX, nextPathLinkForwardY ) * (1000.0f / m_fMaxTrafficSpeed); #ifdef FIX_BUGS /* Casting timer to float is very unwanted, and in this case even causes crashes. */ m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - (uint32)(positionBetweenNodes * m_nTimeToSpendOnCurrentCurve); #else m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - positionBetweenNodes * m_nTimeToSpendOnCurrentCurve; #endif } void CAutoPilot::RemoveOnePathNode() { --m_nPathFindNodesCount; for (int i = 0; i < m_nPathFindNodesCount; i++) m_aPathFindNodesInfo[i] = m_aPathFindNodesInfo[i + 1]; } #ifdef COMPATIBLE_SAVES void CAutoPilot::Save(uint8*& buf) { WriteSaveBuf(buf, m_nCurrentRouteNode); WriteSaveBuf(buf, m_nNextRouteNode); WriteSaveBuf(buf, m_nPrevRouteNode); WriteSaveBuf(buf, m_nTimeEnteredCurve); WriteSaveBuf(buf, m_nTimeToSpendOnCurrentCurve); WriteSaveBuf(buf, m_nCurrentPathNodeInfo); WriteSaveBuf(buf, m_nNextPathNodeInfo); WriteSaveBuf(buf, m_nPreviousPathNodeInfo); WriteSaveBuf(buf, m_nAntiReverseTimer); WriteSaveBuf(buf, m_nTimeToStartMission); WriteSaveBuf(buf, m_nPreviousDirection); WriteSaveBuf(buf, m_nCurrentDirection); WriteSaveBuf(buf, m_nNextDirection); WriteSaveBuf(buf, m_nCurrentLane); WriteSaveBuf(buf, m_nNextLane); WriteSaveBuf(buf, m_nDrivingStyle); WriteSaveBuf(buf, m_nCarMission); WriteSaveBuf(buf, m_nTempAction); WriteSaveBuf(buf, m_nTimeTempAction); WriteSaveBuf(buf, m_fMaxTrafficSpeed); WriteSaveBuf(buf, m_nCruiseSpeed); WriteSaveBuf(buf, m_nCruiseSpeedMultiplierType); SkipSaveBuf(buf, 2); WriteSaveBuf(buf, m_fCruiseSpeedMultiplier); uint8 flags = 0; if (m_bSlowedDownBecauseOfCars) flags |= BIT(0); if (m_bSlowedDownBecauseOfPeds) flags |= BIT(1); if (m_bStayInCurrentLevel) flags |= BIT(2); if (m_bStayInFastLane) flags |= BIT(3); if (m_bIgnorePathfinding) flags |= BIT(4); WriteSaveBuf(buf, flags); WriteSaveBuf(buf, m_nSwitchDistance); SkipSaveBuf(buf, 2); WriteSaveBuf(buf, m_vecDestinationCoors.x); WriteSaveBuf(buf, m_vecDestinationCoors.y); WriteSaveBuf(buf, m_vecDestinationCoors.z); SkipSaveBuf(buf, 32); WriteSaveBuf(buf, m_nPathFindNodesCount); SkipSaveBuf(buf, 6); } void CAutoPilot::Load(uint8*& buf) { m_nCurrentRouteNode = ReadSaveBuf(buf); m_nNextRouteNode = ReadSaveBuf(buf); m_nPrevRouteNode = ReadSaveBuf(buf); m_nTimeEnteredCurve = ReadSaveBuf(buf); m_nTimeToSpendOnCurrentCurve = ReadSaveBuf(buf); m_nCurrentPathNodeInfo = ReadSaveBuf(buf); m_nNextPathNodeInfo = ReadSaveBuf(buf); m_nPreviousPathNodeInfo = ReadSaveBuf(buf); m_nAntiReverseTimer = ReadSaveBuf(buf); m_nTimeToStartMission = ReadSaveBuf(buf); m_nPreviousDirection = ReadSaveBuf(buf); m_nCurrentDirection = ReadSaveBuf(buf); m_nNextDirection = ReadSaveBuf(buf); m_nCurrentLane = ReadSaveBuf(buf); m_nNextLane = ReadSaveBuf(buf); m_nDrivingStyle = ReadSaveBuf(buf); m_nCarMission = ReadSaveBuf(buf); m_nTempAction = ReadSaveBuf(buf); m_nTimeTempAction = ReadSaveBuf(buf); m_fMaxTrafficSpeed = ReadSaveBuf(buf); m_nCruiseSpeed = ReadSaveBuf(buf); m_nCruiseSpeedMultiplierType = ReadSaveBuf(buf); SkipSaveBuf(buf, 2); m_fCruiseSpeedMultiplier = ReadSaveBuf(buf); uint8 flags = ReadSaveBuf(buf); m_bSlowedDownBecauseOfCars = !!(flags & BIT(0)); m_bSlowedDownBecauseOfPeds = !!(flags & BIT(1)); m_bStayInCurrentLevel = !!(flags & BIT(2)); m_bStayInFastLane = !!(flags & BIT(3)); m_bIgnorePathfinding = !!(flags & BIT(4)); m_nSwitchDistance = ReadSaveBuf(buf); SkipSaveBuf(buf, 2); m_vecDestinationCoors.x = ReadSaveBuf(buf); m_vecDestinationCoors.y = ReadSaveBuf(buf); m_vecDestinationCoors.z = ReadSaveBuf(buf); SkipSaveBuf(buf, 32); m_nPathFindNodesCount = ReadSaveBuf(buf); SkipSaveBuf(buf, 6); } #endif ================================================ FILE: src/control/AutoPilot.h ================================================ #pragma once #include "Timer.h" class CVehicle; struct CPathNode; enum eCarMission { MISSION_NONE, MISSION_CRUISE, MISSION_RAMPLAYER_FARAWAY, MISSION_RAMPLAYER_CLOSE, MISSION_BLOCKPLAYER_FARAWAY, MISSION_BLOCKPLAYER_CLOSE, MISSION_BLOCKPLAYER_HANDBRAKESTOP, MISSION_WAITFORDELETION, MISSION_GOTOCOORDS, MISSION_GOTOCOORDS_STRAIGHT, MISSION_EMERGENCYVEHICLE_STOP, MISSION_STOP_FOREVER, MISSION_GOTOCOORDS_ACCURATE, MISSION_GOTO_COORDS_STRAIGHT_ACCURATE, MISSION_GOTOCOORDS_ASTHECROWSWIMS, MISSION_RAMCAR_FARAWAY, MISSION_RAMCAR_CLOSE, MISSION_BLOCKCAR_FARAWAY, MISSION_BLOCKCAR_CLOSE, MISSION_BLOCKCAR_HANDBRAKESTOP, MISSION_HELI_FLYTOCOORS, MISSION_ATTACKPLAYER, MISSION_PLANE_FLYTOCOORS, MISSION_HELI_LAND, MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1, MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_2, MISSION_BLOCKPLAYER_FORWARDANDBACK }; enum eCarTempAction { TEMPACT_NONE, TEMPACT_WAIT, TEMPACT_REVERSE, TEMPACT_HANDBRAKETURNLEFT, TEMPACT_HANDBRAKETURNRIGHT, TEMPACT_HANDBRAKESTRAIGHT, TEMPACT_TURNLEFT, TEMPACT_TURNRIGHT, TEMPACT_GOFORWARD, TEMPACT_SWERVELEFT, TEMPACT_SWERVERIGHT }; enum eCarDrivingStyle { DRIVINGSTYLE_STOP_FOR_CARS, DRIVINGSTYLE_SLOW_DOWN_FOR_CARS, DRIVINGSTYLE_AVOID_CARS, DRIVINGSTYLE_PLOUGH_THROUGH, DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS }; class CAutoPilot { public: int32 m_nCurrentRouteNode; int32 m_nNextRouteNode; int32 m_nPrevRouteNode; int32 m_nTimeEnteredCurve; int32 m_nTimeToSpendOnCurrentCurve; uint32 m_nCurrentPathNodeInfo; uint32 m_nNextPathNodeInfo; uint32 m_nPreviousPathNodeInfo; uint32 m_nAntiReverseTimer; uint32 m_nTimeToStartMission; int8 m_nPreviousDirection; int8 m_nCurrentDirection; int8 m_nNextDirection; int8 m_nCurrentLane; int8 m_nNextLane; uint8 m_nDrivingStyle; uint8 m_nCarMission; uint8 m_nTempAction; uint32 m_nTimeTempAction; float m_fMaxTrafficSpeed; uint8 m_nCruiseSpeed; uint8 m_nCruiseSpeedMultiplierType; float m_fCruiseSpeedMultiplier; uint8 m_bSlowedDownBecauseOfCars : 1; uint8 m_bSlowedDownBecauseOfPeds : 1; uint8 m_bStayInCurrentLevel : 1; uint8 m_bStayInFastLane : 1; uint8 m_bIgnorePathfinding : 1; uint8 m_nSwitchDistance; CVector m_vecDestinationCoors; CPathNode *m_aPathFindNodesInfo[NUM_PATH_NODES_IN_AUTOPILOT]; int16 m_nPathFindNodesCount; CVehicle *m_pTargetCar; CAutoPilot(void) { m_nPrevRouteNode = 0; m_nNextRouteNode = m_nPrevRouteNode; m_nCurrentRouteNode = m_nNextRouteNode; m_nTimeEnteredCurve = 0; m_nTimeToSpendOnCurrentCurve = 1000; m_nPreviousPathNodeInfo = 0; m_nNextPathNodeInfo = m_nPreviousPathNodeInfo; m_nCurrentPathNodeInfo = m_nNextPathNodeInfo; m_nNextDirection = 1; m_nCurrentDirection = m_nNextDirection; m_nCurrentLane = m_nNextLane = 0; m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; m_nCarMission = MISSION_NONE; m_nTempAction = TEMPACT_NONE; m_nCruiseSpeed = 10; m_fMaxTrafficSpeed = 10.0f; m_bSlowedDownBecauseOfPeds = false; m_bSlowedDownBecauseOfCars = false; m_nPathFindNodesCount = 0; m_pTargetCar = 0; m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); m_nAntiReverseTimer = m_nTimeToStartMission; m_bStayInFastLane = false; m_nCruiseSpeedMultiplierType = 0; m_fCruiseSpeedMultiplier = 1.0f; } void ModifySpeed(float); void RemoveOnePathNode(); #ifdef COMPATIBLE_SAVES void Save(uint8*& buf); void Load(uint8*& buf); #endif float GetCruiseSpeed(void) { return m_nCruiseSpeed * m_fCruiseSpeedMultiplier; } }; VALIDATE_SIZE(CAutoPilot, 0x70); ================================================ FILE: src/control/Bridge.cpp ================================================ #include "common.h" #include "Bridge.h" #include "Pools.h" #include "ModelIndices.h" #include "PathFind.h" #include "Stats.h" CEntity *CBridge::pLiftRoad; CEntity *CBridge::pLiftPart; CEntity *CBridge::pWeight; int CBridge::State; int CBridge::OldState; float CBridge::DefaultZLiftPart; float CBridge::DefaultZLiftRoad; float CBridge::DefaultZLiftWeight; float CBridge::OldLift; uint32 CBridge::TimeOfBridgeBecomingOperational; void CBridge::Init() { #ifdef GTA_BRIDGE FindBridgeEntities(); OldLift = -1.0f; if (pLiftPart && pWeight) { DefaultZLiftPart = pLiftPart->GetPosition().z; DefaultZLiftWeight = pWeight->GetPosition().z; if (pLiftRoad) DefaultZLiftRoad = pLiftRoad->GetPosition().z; ThePaths.SetLinksBridgeLights(-330.0, -230.0, -700.0, -588.0, true); } #endif } void CBridge::Update() { #ifdef GTA_BRIDGE if (!pLiftPart || !pWeight) return; OldState = State; float liftHeight; // Set bridge height and state if (CStats::CommercialPassed) { if (TimeOfBridgeBecomingOperational == 0) TimeOfBridgeBecomingOperational = CTimer::GetTimeInMilliseconds(); // Time remaining for bridge to become operational // uint16, so after about a minute it overflows to 0 and the cycle repeats uint16 timeElapsed = CTimer::GetTimeInMilliseconds() - TimeOfBridgeBecomingOperational; // Calculate lift part height and bridge state if (timeElapsed < 10000) { State = STATE_LIFT_PART_MOVING_DOWN; liftHeight = 25.0f - timeElapsed / 10000.0f * 25.0f; } else if (timeElapsed < 40000) { liftHeight = 0.0f; State = STATE_LIFT_PART_IS_DOWN; } else if (timeElapsed < 50000) { liftHeight = 0.0f; State = STATE_LIFT_PART_ABOUT_TO_MOVE_UP; } else if (timeElapsed < 60000) { State = STATE_LIFT_PART_MOVING_UP; liftHeight = (timeElapsed - 50000) / 10000.0f * 25.0f; } else { liftHeight = 25.0f; State = STATE_LIFT_PART_IS_UP; } } else { liftHeight = 25.0f; TimeOfBridgeBecomingOperational = 0; State = STATE_BRIDGE_LOCKED; } // Move bridge part if (liftHeight != OldLift) { pLiftPart->GetMatrix().GetPosition().z = DefaultZLiftPart + liftHeight; pLiftPart->GetMatrix().UpdateRW(); pLiftPart->UpdateRwFrame(); if (pLiftRoad) { pLiftRoad->GetMatrix().GetPosition().z = DefaultZLiftRoad + liftHeight; pLiftRoad->GetMatrix().UpdateRW(); pLiftRoad->UpdateRwFrame(); } pWeight->GetMatrix().GetPosition().z = DefaultZLiftWeight - liftHeight; pWeight->GetMatrix().UpdateRW(); pWeight->UpdateRwFrame(); OldLift = liftHeight; } if (State == STATE_LIFT_PART_ABOUT_TO_MOVE_UP && OldState == STATE_LIFT_PART_IS_DOWN) ThePaths.SetLinksBridgeLights(-330.0, -230.0, -700.0, -588.0, true); else if (State == STATE_LIFT_PART_IS_DOWN && OldState == STATE_LIFT_PART_MOVING_DOWN) ThePaths.SetLinksBridgeLights(-330.0, -230.0, -700.0, -588.0, false); #endif } bool CBridge::ShouldLightsBeFlashing() { #ifdef GTA_BRIDGE return State != STATE_LIFT_PART_IS_DOWN; #else return false; #endif } void CBridge::FindBridgeEntities() { #ifdef GTA_BRIDGE pWeight = nil; pLiftRoad = nil; pLiftPart = nil; for (int i = CPools::GetBuildingPool()->GetSize()-1; i >= 0; i--) { CBuilding* entry = CPools::GetBuildingPool()->GetSlot(i); if (entry) { if (entry->GetModelIndex() == MI_BRIDGELIFT) pLiftPart = entry; else if (entry->GetModelIndex() == MI_BRIDGEROADSEGMENT) pLiftRoad = entry; else if (entry->GetModelIndex() == MI_BRIDGEWEIGHT) pWeight = entry; } } #endif } bool CBridge::ThisIsABridgeObjectMovingUp(int index) { #ifdef GTA_BRIDGE if (index != MI_BRIDGEROADSEGMENT && index != MI_BRIDGELIFT) return false; return State == STATE_LIFT_PART_ABOUT_TO_MOVE_UP || State == STATE_LIFT_PART_MOVING_UP; #else return false; #endif } ================================================ FILE: src/control/Bridge.h ================================================ #pragma once class CEntity; enum bridgeStates { STATE_BRIDGE_LOCKED, STATE_LIFT_PART_IS_UP, STATE_LIFT_PART_MOVING_DOWN, STATE_LIFT_PART_IS_DOWN, STATE_LIFT_PART_ABOUT_TO_MOVE_UP, STATE_LIFT_PART_MOVING_UP }; class CBridge { public: static CEntity *pLiftRoad, *pLiftPart, *pWeight; static int State, OldState; static float DefaultZLiftPart, DefaultZLiftRoad, DefaultZLiftWeight; static float OldLift; static uint32 TimeOfBridgeBecomingOperational; static void Init(); static void Update(); static bool ShouldLightsBeFlashing(); static void FindBridgeEntities(); static bool ThisIsABridgeObjectMovingUp(int); }; ================================================ FILE: src/control/CarAI.cpp ================================================ #include "common.h" #include "CarAI.h" #include "Accident.h" #include "AutoPilot.h" #include "CarCtrl.h" #include "General.h" #include "HandlingMgr.h" #include "ModelIndices.h" #include "PlayerPed.h" #include "Wanted.h" #include "DMAudio.h" #include "Fire.h" #include "Pools.h" #include "Population.h" #include "Timer.h" #include "TrafficLights.h" #include "Vehicle.h" #include "World.h" #include "ZoneCull.h" #define DISTANCE_TO_SWITCH_DISTANCE_GOTO 20.0f float CCarAI::FindSwitchDistanceClose(CVehicle* pVehicle) { return pVehicle->AutoPilot.m_nSwitchDistance; } float CCarAI::FindSwitchDistanceFarNormalVehicle(CVehicle* pVehicle) { return FindSwitchDistanceClose(pVehicle) + 5.0f; } float CCarAI::FindSwitchDistanceFar(CVehicle* pVehicle) { if (pVehicle->bIsLawEnforcer) return 50.0f; return FindSwitchDistanceFarNormalVehicle(pVehicle); } void CCarAI::BackToCruisingIfNoWantedLevel(CVehicle* pVehicle) { if (FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || pVehicle->bIsLawEnforcer && (FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) { CCarCtrl::JoinCarWithRoadSystem(pVehicle); pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; pVehicle->m_bSirenOrAlarm = false; if (CCullZones::NoPolice()) pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; } } void CCarAI::UpdateCarAI(CVehicle* pVehicle) { if (pVehicle->bIsLawEnforcer){ if (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_FARAWAY || pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE || pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE) pVehicle->AutoPilot.m_nCruiseSpeed = FindPoliceCarSpeedForWantedLevel(pVehicle); } switch (pVehicle->GetStatus()){ case STATUS_PLAYER: case STATUS_PLAYER_PLAYBACKFROMBUFFER: case STATUS_TRAIN_MOVING: case STATUS_TRAIN_NOT_MOVING: case STATUS_HELI: case STATUS_PLANE: case STATUS_PLAYER_REMOTE: case STATUS_PLAYER_DISABLED: break; case STATUS_SIMPLE: case STATUS_PHYSICS: switch (pVehicle->AutoPilot.m_nCarMission) { case MISSION_RAMPLAYER_FARAWAY: if (FindSwitchDistanceClose(pVehicle) > (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() || pVehicle->AutoPilot.m_bIgnorePathfinding) { pVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_CLOSE; if (pVehicle->UsesSiren()) pVehicle->m_bSirenOrAlarm = true; } BackToCruisingIfNoWantedLevel(pVehicle); break; case MISSION_RAMPLAYER_CLOSE: if (FindSwitchDistanceFar(pVehicle) >= (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() || pVehicle->AutoPilot.m_bIgnorePathfinding) { if (FindPlayerVehicle()) { if (pVehicle->GetHasCollidedWith(FindPlayerVehicle())) { if (pVehicle->AutoPilot.m_nTempAction != TEMPACT_TURNLEFT && pVehicle->AutoPilot.m_nTempAction != TEMPACT_TURNRIGHT) { if (FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f) { pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 800; } else { pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 50; } } } } if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f) #ifdef FIX_BUGS pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds(); #else pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep(); #endif else pVehicle->m_nTimeBlocked = 0; if (!FindPlayerVehicle() || FindPlayerVehicle()->IsUpsideDown() || FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f && pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING) { if (pVehicle->bIsLawEnforcer && (pVehicle->GetModelIndex() != MI_RHINO || pVehicle->m_randomSeed > 10000) && (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() < 10.0f) { TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCruiseSpeed = 0; pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; if (FindPlayerPed()->m_pWanted->GetWantedLevel() <= 1) pVehicle->m_bSirenOrAlarm = false; } } } else if (!CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), true)){ pVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_FARAWAY; pVehicle->m_bSirenOrAlarm = false; pVehicle->m_nCarHornTimer = 0; } if (pVehicle->bIsLawEnforcer) MellowOutChaseSpeed(pVehicle); BackToCruisingIfNoWantedLevel(pVehicle); break; case MISSION_BLOCKPLAYER_FARAWAY: if (FindSwitchDistanceClose(pVehicle) > (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() || pVehicle->AutoPilot.m_bIgnorePathfinding) { pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_CLOSE; if (pVehicle->UsesSiren()) pVehicle->m_bSirenOrAlarm = true; } BackToCruisingIfNoWantedLevel(pVehicle); break; case MISSION_BLOCKPLAYER_CLOSE: if (FindSwitchDistanceFar(pVehicle) >= (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() || pVehicle->AutoPilot.m_bIgnorePathfinding) { if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.04f) #ifdef FIX_BUGS pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds(); #else pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep(); #endif else pVehicle->m_nTimeBlocked = 0; if (!FindPlayerVehicle() || FindPlayerVehicle()->IsUpsideDown() || FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.04f && pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING) { if (pVehicle->bIsLawEnforcer && (pVehicle->GetModelIndex() != MI_RHINO || pVehicle->m_randomSeed > 10000) && (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude2D() < 10.0f) { TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCruiseSpeed = 0; pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; if (FindPlayerPed()->m_pWanted->GetWantedLevel() <= 1) pVehicle->m_bSirenOrAlarm = false; } } }else if (!CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), true)) { pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FARAWAY; pVehicle->m_bSirenOrAlarm = false; pVehicle->m_nCarHornTimer = 0; } if (pVehicle->bIsLawEnforcer) MellowOutChaseSpeed(pVehicle); BackToCruisingIfNoWantedLevel(pVehicle); break; case MISSION_GOTOCOORDS: if ((pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) || pVehicle->AutoPilot.m_bIgnorePathfinding) pVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; break; case MISSION_GOTOCOORDS_STRAIGHT: { float distance = (pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D(); if ((pVehicle->bIsAmbulanceOnDuty || pVehicle->bIsFireTruckOnDuty) && distance < 20.0f) pVehicle->AutoPilot.m_nCarMission = MISSION_EMERGENCYVEHICLE_STOP; if (distance < 3.0f){ pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; if (pVehicle->bParking) { TellOccupantsToLeaveCar(pVehicle); pVehicle->bParking = false; } } else if (distance > FindSwitchDistanceFarNormalVehicle(pVehicle) && !pVehicle->AutoPilot.m_bIgnorePathfinding && (CTimer::GetFrameCounter() & 7) == 0){ pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pVehicle->AutoPilot.m_vecDestinationCoors, true)) ? MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS; } break; } case MISSION_EMERGENCYVEHICLE_STOP: if (pVehicle->GetMoveSpeed().Magnitude2D() < 0.01f){ if (pVehicle->bIsAmbulanceOnDuty){ float distance = 30.0f; if (gAccidentManager.FindNearestAccident(pVehicle->AutoPilot.m_vecDestinationCoors, &distance)){ TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; }else{ CCarCtrl::JoinCarWithRoadSystem(pVehicle); pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; pVehicle->m_bSirenOrAlarm = false; pVehicle->AutoPilot.m_nCruiseSpeed = 17; if (pVehicle->bIsAmbulanceOnDuty){ pVehicle->bIsAmbulanceOnDuty = false; --CCarCtrl::NumAmbulancesOnDuty; } } } if (pVehicle->bIsFireTruckOnDuty) { float distance = 30.0f; if (gFireManager.FindNearestFire(pVehicle->AutoPilot.m_vecDestinationCoors, &distance)) { TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; } else { CCarCtrl::JoinCarWithRoadSystem(pVehicle); pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; pVehicle->m_bSirenOrAlarm = false; pVehicle->AutoPilot.m_nCruiseSpeed = 17; if (pVehicle->bIsFireTruckOnDuty) { pVehicle->bIsFireTruckOnDuty = false; --CCarCtrl::NumFiretrucksOnDuty; } } } } break; case MISSION_GOTOCOORDS_ACCURATE: if ((pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) || pVehicle->AutoPilot.m_bIgnorePathfinding) pVehicle->AutoPilot.m_nCarMission = MISSION_GOTO_COORDS_STRAIGHT_ACCURATE; break; case MISSION_GOTO_COORDS_STRAIGHT_ACCURATE: { float distance = (pVehicle->AutoPilot.m_vecDestinationCoors - pVehicle->GetPosition()).Magnitude2D(); if (distance < 1.0f) { pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; if (pVehicle->bParking) { TellOccupantsToLeaveCar(pVehicle); pVehicle->bParking = false; } } else if (distance > FindSwitchDistanceFarNormalVehicle(pVehicle) && !pVehicle->AutoPilot.m_bIgnorePathfinding && (CTimer::GetFrameCounter() & 7) == 0) { pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pVehicle->AutoPilot.m_vecDestinationCoors, true)) ? MISSION_GOTO_COORDS_STRAIGHT_ACCURATE : MISSION_GOTOCOORDS_ACCURATE; } break; } case MISSION_RAMCAR_FARAWAY: if (pVehicle->AutoPilot.m_pTargetCar){ if ((pVehicle->GetPosition() - pVehicle->AutoPilot.m_pTargetCar->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) || pVehicle->AutoPilot.m_bIgnorePathfinding) pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE; }else{ pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; } break; case MISSION_RAMCAR_CLOSE: if (pVehicle->AutoPilot.m_pTargetCar){ #ifdef FIX_BUGS // btw fixed in SA if (FindPlayerVehicle() == pVehicle->AutoPilot.m_pTargetCar) #endif BackToCruisingIfNoWantedLevel(pVehicle); if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() <= FindSwitchDistanceFar(pVehicle) || pVehicle->AutoPilot.m_bIgnorePathfinding){ if (pVehicle->GetHasCollidedWith(pVehicle->AutoPilot.m_pTargetCar)){ if (pVehicle->GetMoveSpeed().Magnitude() < 0.04f){ pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 800; } } }else{ pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_FARAWAY; CCarCtrl::JoinCarWithRoadSystem(pVehicle); } }else{ pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; } break; case MISSION_BLOCKCAR_FARAWAY: if (pVehicle->AutoPilot.m_pTargetCar){ if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() < FindSwitchDistanceClose(pVehicle) || pVehicle->AutoPilot.m_bIgnorePathfinding){ pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_CLOSE; if (pVehicle->UsesSiren()) pVehicle->m_bSirenOrAlarm = true; } }else{ pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; } break; case MISSION_BLOCKCAR_CLOSE: if (pVehicle->AutoPilot.m_pTargetCar){ if ((pVehicle->AutoPilot.m_pTargetCar->GetPosition() - pVehicle->GetPosition()).Magnitude2D() > FindSwitchDistanceFar(pVehicle) && !pVehicle->AutoPilot.m_bIgnorePathfinding){ pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_FARAWAY; pVehicle->m_bSirenOrAlarm = false; pVehicle->m_nCarHornTimer = 0; CCarCtrl::JoinCarWithRoadSystem(pVehicle); } }else{ pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; } break; case MISSION_ATTACKPLAYER: if (pVehicle->bIsLawEnforcer) MellowOutChaseSpeedBoat(pVehicle); BackToCruisingIfNoWantedLevel(pVehicle); break; case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1: if (((CVector2D)(pVehicle->AutoPilot.m_vecDestinationCoors) - pVehicle->GetPosition()).Magnitude() < 1.5f) pVehicle->AutoPilot.m_nCarMission = MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_2; BackToCruisingIfNoWantedLevel(pVehicle); break; case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_2: { float distance = ((CVector2D)FindPlayerCoors() - pVehicle->GetPosition()).Magnitude(); if (distance < 13.0f) { TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCruiseSpeed = 0; pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; } if (distance > 70.0f || FindPlayerPed()->m_pWanted->m_bIgnoredByEveryone || (FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 || FindPlayerPed()->m_pWanted->m_bIgnoredByCops || CCullZones::NoPolice())) { TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCruiseSpeed = 0; pVehicle->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; } break; } case MISSION_BLOCKPLAYER_FORWARDANDBACK: { CVector2D diff = (CVector2D)FindPlayerCoors() - pVehicle->GetPosition(); float distance = Max(0.001f, diff.Magnitude()); if (!FindPlayerVehicle() || DotProduct2D(CVector2D(diff.x / distance, diff.y / distance), FindPlayerSpeed()) > 0.05f) pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_CLOSE; BackToCruisingIfNoWantedLevel(pVehicle); break; } default: if (pVehicle->bIsLawEnforcer && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 && !CCullZones::NoPolice()){ if (ABS(FindPlayerCoors().x - pVehicle->GetPosition().x) > 10.0f || ABS(FindPlayerCoors().y - pVehicle->GetPosition().y) > 10.0f){ pVehicle->AutoPilot.m_nCruiseSpeed = FindPoliceCarSpeedForWantedLevel(pVehicle); pVehicle->SetStatus(STATUS_PHYSICS); pVehicle->AutoPilot.m_nCarMission = pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT ? FindPoliceBoatMissionForWantedLevel() : FindPoliceCarMissionForWantedLevel(); pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; }else if (pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE){ pVehicle->SetStatus(STATUS_PHYSICS); TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCruiseSpeed = 0; pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; if (FindPlayerPed()->m_pWanted->GetWantedLevel() <= 1) pVehicle->m_bSirenOrAlarm = false; } } break; } break; case STATUS_ABANDONED: case STATUS_WRECKED: pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; pVehicle->AutoPilot.m_nCruiseSpeed = 0; break; } if (pVehicle->bIsLawEnforcer && FindPlayerPed()->m_pWanted->GetWantedLevel() >= 1 && CCullZones::PoliceAbandonCars()) { TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCruiseSpeed = 0; pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; } float flatSpeed = pVehicle->GetMoveSpeed().MagnitudeSqr2D(); if (flatSpeed > SQR(0.018f)){ pVehicle->AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); } if (pVehicle->GetStatus() == STATUS_PHYSICS && pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){ if (pVehicle->AutoPilot.m_nCarMission != MISSION_NONE){ if (pVehicle->AutoPilot.m_nCarMission != MISSION_STOP_FOREVER && pVehicle->AutoPilot.m_nCarMission != MISSION_BLOCKPLAYER_HANDBRAKESTOP && pVehicle->AutoPilot.m_nCruiseSpeed != 0 && (pVehicle->VehicleCreatedBy != RANDOM_VEHICLE || pVehicle->AutoPilot.m_nCarMission != MISSION_CRUISE)){ if (pVehicle->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_STOP_FOR_CARS && pVehicle->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS || pVehicle->VehicleCreatedBy == MISSION_VEHICLE ) { if (CTimer::GetTimeInMilliseconds() - pVehicle->m_nLastTimeCollided > 500) pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); if (flatSpeed < SQR(0.018f) && CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nAntiReverseTimer > 2000){ pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; if (pVehicle->AutoPilot.m_nCarMission != MISSION_NONE && pVehicle->AutoPilot.m_nCarMission != MISSION_CRUISE || pVehicle->VehicleCreatedBy == MISSION_VEHICLE) pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1500; else pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 750; pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); if (pVehicle->VehicleCreatedBy == RANDOM_VEHICLE) pVehicle->AutoPilot.m_nDrivingStyle = Max(DRIVINGSTYLE_AVOID_CARS, pVehicle->AutoPilot.m_nDrivingStyle); pVehicle->PlayCarHorn(); } } } } } if ((pVehicle->m_randomSeed & 7) == 0){ if (CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission > 30000 && CTimer::GetPreviousTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission <= 30000 && pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE && !CTrafficLights::ShouldCarStopForBridge(pVehicle)){ pVehicle->SetStatus(STATUS_PHYSICS); CCarCtrl::SwitchVehicleToRealPhysics(pVehicle); pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; pVehicle->AutoPilot.m_nTempAction = TEMPACT_REVERSE; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 400; } } if (pVehicle->bIsLawEnforcer) { if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE) { if (FindPlayerVehicle() && FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FARAWAY; } } if (pVehicle->GetUp().z < -0.7f){ pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; } if (pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){ switch (pVehicle->AutoPilot.m_nCarMission){ case MISSION_RAMPLAYER_FARAWAY: case MISSION_RAMPLAYER_CLOSE: case MISSION_BLOCKPLAYER_FARAWAY: case MISSION_BLOCKPLAYER_CLOSE: if (FindPlayerVehicle() && FindPlayerSpeed().Magnitude() > pVehicle->GetMoveSpeed().Magnitude()){ if (FindPlayerSpeed().Magnitude() > 0.1f){ if (DotProduct2D(FindPlayerVehicle()->GetForward(), pVehicle->GetForward()) > 0.0f){ CVector2D dist = pVehicle->GetPosition() - FindPlayerCoors(); CVector2D speed = FindPlayerSpeed(); if (0.5f * dist.Magnitude() * speed.Magnitude() < DotProduct2D(dist, speed)){ if ((FindPlayerCoors() - pVehicle->GetPosition()).Magnitude() > 12.0f){ pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 500; } } } } } break; default: break; } } if (pVehicle->pDriver && pVehicle->pDriver->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS){ if ((pVehicle->GetPosition() - FindPlayerCoors()).Magnitude() < 15.0f){ if (!FindPlayerVehicle() || pVehicle->GetHasCollidedWith(FindPlayerVehicle())){ pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 3000; } } } if (pVehicle->m_bSirenOrAlarm){ if ((uint8)(pVehicle->m_randomSeed ^ CGeneral::GetRandomNumber()) == 0xAD) pVehicle->m_nCarHornTimer = 45; } float target = 1.0f; if (pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE) target = CCarCtrl::FindSpeedMultiplierWithSpeedFromNodes(pVehicle->AutoPilot.m_nCruiseSpeedMultiplierType); float change = CTimer::GetTimeStep() * 0.01f; if (Abs(pVehicle->AutoPilot.m_fCruiseSpeedMultiplier - target) < change) pVehicle->AutoPilot.m_fCruiseSpeedMultiplier = target; else if (pVehicle->AutoPilot.m_fCruiseSpeedMultiplier > target) pVehicle->AutoPilot.m_fCruiseSpeedMultiplier -= change; else pVehicle->AutoPilot.m_fCruiseSpeedMultiplier += change; if (pVehicle->bIsLawEnforcer && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0) { if (!FindPlayerVehicle() || FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR || FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) { if (pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT) { pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; } } else if (FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT) { if (pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR || pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) { pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; } } } } void CCarAI::CarHasReasonToStop(CVehicle* pVehicle) { pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); } float CCarAI::GetCarToGoToCoors(CVehicle* pVehicle, CVector* pTarget) { if (pVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS && pVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS_STRAIGHT){ pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nCruiseSpeed = 20; pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); pVehicle->SetStatus(STATUS_PHYSICS); pVehicle->AutoPilot.m_nCarMission = (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, *pTarget, false)) ? MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS; }else if (Abs(pTarget->x - pVehicle->AutoPilot.m_vecDestinationCoors.x) > 2.0f || Abs(pTarget->y - pVehicle->AutoPilot.m_vecDestinationCoors.y) > 2.0f){ pVehicle->AutoPilot.m_vecDestinationCoors = *pTarget; } return (pVehicle->GetPosition() - *pTarget).Magnitude2D(); } float CCarAI::GetCarToParkAtCoors(CVehicle* pVehicle, CVector* pTarget) { GetCarToGoToCoors(pVehicle, pTarget); pVehicle->bParking = true; pVehicle->AutoPilot.m_nCruiseSpeed = 10; return (pVehicle->GetPosition() - *pTarget).Magnitude2D(); } void CCarAI::AddPoliceCarOccupants(CVehicle* pVehicle) { if (pVehicle->bOccupantsHaveBeenGenerated) return; pVehicle->bOccupantsHaveBeenGenerated = true; switch (pVehicle->GetModelIndex()){ case MI_FBIRANCH: case MI_ENFORCER: pVehicle->SetUpDriver(); for (int i = 0; i < 3; i++) pVehicle->SetupPassenger(i); return; case MI_POLICE: case MI_RHINO: case MI_BARRACKS: pVehicle->SetUpDriver(); if (FindPlayerPed()->m_pWanted->GetWantedLevel() > 1) pVehicle->SetupPassenger(0); return; case MI_PREDATOR: pVehicle->SetUpDriver(); return; case MI_VICECHEE: { pVehicle->SetUpDriver()->bMiamiViceCop = true; pVehicle->SetupPassenger(0)->bMiamiViceCop = true; CPopulation::NumMiamiViceCops += 2; CCarCtrl::MiamiViceCycle = (CCarCtrl::MiamiViceCycle + 1) % 4; CCarCtrl::LastTimeMiamiViceGenerated = CTimer::GetTimeInMilliseconds(); return; } default: return; } } void CCarAI::AddAmbulanceOccupants(CVehicle* pVehicle) { pVehicle->SetUpDriver(); pVehicle->SetupPassenger(1); } void CCarAI::AddFiretruckOccupants(CVehicle* pVehicle) { pVehicle->SetUpDriver(); pVehicle->SetupPassenger(0); } void CCarAI::TellOccupantsToLeaveCar(CVehicle* pVehicle) { if (pVehicle->pDriver){ pVehicle->pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); if (pVehicle->GetModelIndex() == MI_AMBULAN) pVehicle->pDriver->Say(SOUND_PED_LEAVE_VEHICLE); } int timer = 100; for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++){ if (pVehicle->pPassengers[i]) { pVehicle->pPassengers[i]->m_leaveCarTimer = timer; pVehicle->pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); timer += CGeneral::GetRandomNumberInRange(200, 400); } } } void CCarAI::TellOccupantsToFleeCar(CVehicle* pVehicle) { if (pVehicle->pDriver && !pVehicle->pDriver->IsPlayer()) { pVehicle->pDriver->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); if (pVehicle->GetModelIndex() != MI_FIRETRUCK && pVehicle->GetModelIndex() == MI_AMBULAN) pVehicle->pDriver->Say(SOUND_PED_LEAVE_VEHICLE); } int timer = 100; for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { if (pVehicle->pPassengers[i]) { pVehicle->pPassengers[i]->m_leaveCarTimer = timer; pVehicle->pPassengers[i]->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); timer += CGeneral::GetRandomNumberInRange(200, 400); } } } void CCarAI::TellCarToRamOtherCar(CVehicle* pVehicle, CVehicle* pTarget) { pVehicle->AutoPilot.m_pTargetCar = pTarget; pTarget->RegisterReference((CEntity**)&pVehicle->AutoPilot.m_pTargetCar); pVehicle->AutoPilot.m_nCarMission = MISSION_RAMCAR_FARAWAY; pVehicle->bEngineOn = true; pVehicle->AutoPilot.m_nCruiseSpeed = Max(6, pVehicle->AutoPilot.m_nCruiseSpeed); } void CCarAI::TellCarToBlockOtherCar(CVehicle* pVehicle, CVehicle* pTarget) { pVehicle->AutoPilot.m_pTargetCar = pTarget; pTarget->RegisterReference((CEntity**)&pVehicle->AutoPilot.m_pTargetCar); pVehicle->AutoPilot.m_nCarMission = MISSION_BLOCKCAR_FARAWAY; pVehicle->bEngineOn = true; pVehicle->AutoPilot.m_nCruiseSpeed = Max(6, pVehicle->AutoPilot.m_nCruiseSpeed); } uint8 CCarAI::FindPoliceCarMissionForWantedLevel() { switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()){ case 0: case 1: return MISSION_BLOCKPLAYER_FARAWAY; case 2: return (CGeneral::GetRandomNumber() & 3) >= 3 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY; case 3: return (CGeneral::GetRandomNumber() & 3) >= 2 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY; case 4: case 5: case 6: return (CGeneral::GetRandomNumber() & 3) >= 1 ? MISSION_RAMPLAYER_FARAWAY : MISSION_BLOCKPLAYER_FARAWAY; default: return MISSION_BLOCKPLAYER_FARAWAY; } } uint8 CCarAI::FindPoliceBoatMissionForWantedLevel() { switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()) { case 0: case 1: return MISSION_BLOCKPLAYER_FARAWAY; case 2: case 3: case 4: case 5: case 6: return MISSION_ATTACKPLAYER; default: return MISSION_BLOCKPLAYER_FARAWAY; } } int32 CCarAI::FindPoliceCarSpeedForWantedLevel(CVehicle* pVehicle) { switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()) { case 0: return CGeneral::GetRandomNumberInRange(12, 16); case 1: return 25; case 2: return 34; case 3: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 0.9f; case 4: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.2f; case 5: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.25f; case 6: return GAME_SPEED_TO_CARAI_SPEED * pVehicle->pHandling->Transmission.fMaxVelocity * 1.3f; default: return 0; } } void CCarAI::MellowOutChaseSpeed(CVehicle* pVehicle) { if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel() == 1){ float distanceToPlayer = (pVehicle->GetPosition() - FindPlayerCoors()).Magnitude(); if (FindPlayerVehicle()){ if (distanceToPlayer < 10.0f) pVehicle->AutoPilot.m_nCruiseSpeed = 15; else if (distanceToPlayer < 20.0f) pVehicle->AutoPilot.m_nCruiseSpeed = 22; else pVehicle->AutoPilot.m_nCruiseSpeed = 25; }else{ if (distanceToPlayer < 20.0f) pVehicle->AutoPilot.m_nCruiseSpeed = 5; else if (distanceToPlayer < 40.0f) pVehicle->AutoPilot.m_nCruiseSpeed = 13; else pVehicle->AutoPilot.m_nCruiseSpeed = 25; } }else if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel() == 2){ float distanceToPlayer = (pVehicle->GetPosition() - FindPlayerCoors()).Magnitude(); if (FindPlayerVehicle()) { if (distanceToPlayer < 10.0f) pVehicle->AutoPilot.m_nCruiseSpeed = 27; else if (distanceToPlayer < 20.0f) pVehicle->AutoPilot.m_nCruiseSpeed = 30; else pVehicle->AutoPilot.m_nCruiseSpeed = 34; } else { if (distanceToPlayer < 20.0f) pVehicle->AutoPilot.m_nCruiseSpeed = 5; else if (distanceToPlayer < 40.0f) pVehicle->AutoPilot.m_nCruiseSpeed = 18; else pVehicle->AutoPilot.m_nCruiseSpeed = 34; } } if (!FindPlayerVehicle() && FindPlayerPed()->GetMoveSpeed().Magnitude() < 0.07f) { if ((FindPlayerCoors() - pVehicle->GetPosition()).Magnitude() < 30.0f) pVehicle->AutoPilot.m_nCruiseSpeed = Min(10, pVehicle->AutoPilot.m_nCruiseSpeed); } } void CCarAI::MellowOutChaseSpeedBoat(CVehicle* pVehicle) { switch (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel()) { case 0: pVehicle->AutoPilot.m_nCruiseSpeed = 8; break; case 1: pVehicle->AutoPilot.m_nCruiseSpeed = 10; break; case 2: pVehicle->AutoPilot.m_nCruiseSpeed = 15; break; case 3: pVehicle->AutoPilot.m_nCruiseSpeed = 20; break; case 4: pVehicle->AutoPilot.m_nCruiseSpeed = 25; break; case 5: pVehicle->AutoPilot.m_nCruiseSpeed = 30; break; case 6: pVehicle->AutoPilot.m_nCruiseSpeed = 40; break; } } void CCarAI::MakeWayForCarWithSiren(CVehicle *pVehicle) { float flatSpeed = pVehicle->GetMoveSpeed().Magnitude2D(); if (flatSpeed < 0.1f) return; CVector2D forward = pVehicle->GetMoveSpeed() / flatSpeed; float projection = flatSpeed * 45 + 20; int i = CPools::GetVehiclePool()->GetSize(); while (--i >= 0) { CVehicle* vehicle = CPools::GetVehiclePool()->GetSlot(i); if (!vehicle) continue; if (!vehicle->IsCar() && !vehicle->IsBike()) continue; if (vehicle->GetStatus() != STATUS_SIMPLE && vehicle->GetStatus() != STATUS_PHYSICS) continue; if (vehicle->VehicleCreatedBy != RANDOM_VEHICLE) continue; if (vehicle->bIsLawEnforcer || vehicle->bIsAmbulanceOnDuty || vehicle->bIsFireTruckOnDuty) continue; if (vehicle == pVehicle) continue; if (vehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_AVOID_CARS) return; if (Abs(pVehicle->GetPosition().z - vehicle->GetPosition().z) >= 5.0f) continue; CVector2D distance = vehicle->GetPosition() - pVehicle->GetPosition(); if (distance.Magnitude() >= projection) continue; if (vehicle->GetMoveSpeed().Magnitude2D() <= 0.05f) continue; float correlation = DotProduct2D(forward, distance) / distance.Magnitude(); if (correlation <= 0.0f) continue; if (correlation > 0.8f && DotProduct2D(forward, vehicle->GetForward()) > 0.7f){ if (vehicle->AutoPilot.m_nTempAction != TEMPACT_SWERVELEFT && vehicle->AutoPilot.m_nTempAction != TEMPACT_SWERVERIGHT){ vehicle->AutoPilot.m_nTempAction = (distance.x * forward.y - distance.y * forward.x > 0.0f) ? TEMPACT_SWERVELEFT : TEMPACT_SWERVERIGHT; vehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; } vehicle->SetStatus(STATUS_PHYSICS); }else{ if (DotProduct2D(vehicle->GetMoveSpeed(), distance) < 0.0f && vehicle->AutoPilot.m_nTempAction != TEMPACT_WAIT){ vehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; vehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; } } } } ================================================ FILE: src/control/CarAI.h ================================================ #pragma once #include "AutoPilot.h" class CVehicle; class CCarAI { public: static float FindSwitchDistanceClose(CVehicle*); static float FindSwitchDistanceFarNormalVehicle(CVehicle*); static float FindSwitchDistanceFar(CVehicle*); static void BackToCruisingIfNoWantedLevel(CVehicle*); static void UpdateCarAI(CVehicle*); static void CarHasReasonToStop(CVehicle*); static float GetCarToGoToCoors(CVehicle*, CVector*); static float GetCarToParkAtCoors(CVehicle*, CVector*); static void AddPoliceCarOccupants(CVehicle*); static void AddAmbulanceOccupants(CVehicle*); static void AddFiretruckOccupants(CVehicle*); static void TellOccupantsToLeaveCar(CVehicle*); static void TellOccupantsToFleeCar(CVehicle*); static void TellCarToRamOtherCar(CVehicle*, CVehicle*); static void TellCarToBlockOtherCar(CVehicle*, CVehicle*); static uint8 FindPoliceCarMissionForWantedLevel(); static uint8 FindPoliceBoatMissionForWantedLevel(); static int32 FindPoliceCarSpeedForWantedLevel(CVehicle*); static void MellowOutChaseSpeed(CVehicle*); static void MellowOutChaseSpeedBoat(CVehicle*); static void MakeWayForCarWithSiren(CVehicle *veh); }; ================================================ FILE: src/control/CarCtrl.cpp ================================================ #include "common.h" #include "CarCtrl.h" #include "Accident.h" #include "Automobile.h" #include "Bike.h" #include "Camera.h" #include "CarAI.h" #include "CarGen.h" #include "Cranes.h" #include "Curves.h" #include "CutsceneMgr.h" #include "Gangs.h" #include "Game.h" #include "Garages.h" #include "General.h" #include "IniFile.h" #include "ModelIndices.h" #include "PathFind.h" #include "Ped.h" #include "PlayerInfo.h" #include "PlayerPed.h" #include "Population.h" #include "Wanted.h" #include "Pools.h" #include "Renderer.h" #include "RoadBlocks.h" #include "Timer.h" #include "TrafficLights.h" #include "Streaming.h" #include "VisibilityPlugins.h" #include "Vehicle.h" #include "Fire.h" #include "WaterLevel.h" #include "World.h" #include "Zones.h" #include "Pickups.h" #define DISTANCE_TO_SPAWN_ROADBLOCK_PEDS (51.0f) #define DISTANCE_TO_SCAN_FOR_DANGER (14.0f) #define DISTANCE_TO_SCAN_FOR_PED_DANGER (11.0f) #define SAFE_DISTANCE_TO_PED (3.0f) #define INFINITE_Z (1000000000.0f) #define VEHICLE_HEIGHT_DIFF_TO_CONSIDER_WEAVING (4.0f) #define PED_HEIGHT_DIFF_TO_CONSIDER_WEAVING (4.0f) #define OBJECT_HEIGHT_DIFF_TO_CONSIDER_WEAVING (8.0f) #define WIDTH_COEF_TO_WEAVE_SAFELY (1.2f) #define OBJECT_WIDTH_TO_WEAVE (0.3f) #define PED_WIDTH_TO_WEAVE (0.8f) #define PATH_DIRECTION_NONE (0) #define PATH_DIRECTION_STRAIGHT (1) #define PATH_DIRECTION_RIGHT (2) #define PATH_DIRECTION_LEFT (4) #define ATTEMPTS_TO_FIND_NEXT_NODE (15) #define DISTANCE_TO_SWITCH_FROM_BLOCK_TO_STOP (5.0f) #define DISTANCE_TO_SWITCH_FROM_STOP_TO_BLOCK (10.0f) #define MAX_SPEED_TO_ACCOUNT_IN_INTERCEPTING (0.13f) #define DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN (40.0f) #define MAX_ANGLE_TO_STEER_AT_HIGH_SPEED (0.2f) #define MIN_SPEED_TO_START_LIMITING_STEER (0.45f) #define DISTANCE_TO_NEXT_NODE_TO_SELECT_NEW (5.0f) #define DISTANCE_TO_FACING_NEXT_NODE_TO_SELECT_NEW (8.0f) #define DEFAULT_MAX_STEER_ANGLE (0.5f) #define MIN_LOWERING_SPEED_COEFFICIENT (0.4f) #define MAX_ANGLE_FOR_SPEED_LIMITING (1.2f) #define MIN_ANGLE_FOR_SPEED_LIMITING (0.4f) #define MIN_ANGLE_FOR_SPEED_LIMITING_BETWEEN_NODES (0.1f) #define MIN_ANGLE_TO_APPLY_HANDBRAKE (0.7f) #define MIN_SPEED_TO_APPLY_HANDBRAKE (0.3f) #define PROBABILITY_OF_DEAD_PED_ACCIDENT (0.005f) #define DISTANCE_BETWEEN_CAR_AND_DEAD_PED (6.0f) #define PROBABILITY_OF_PASSENGER_IN_VEHICLE (0.125f) #define ONSCREEN_DESPAWN_RANGE (120.0f) #define MINIMAL_DISTANCE_TO_SPAWN_ONSCREEN (100.0f) #define REQUEST_ONSCREEN_DISTANCE ((ONSCREEN_DESPAWN_RANGE + MINIMAL_DISTANCE_TO_SPAWN_ONSCREEN) / 2) #define OFFSCREEN_DESPAWN_RANGE (40.0f) #define EXTENDED_RANGE_DESPAWN_MULTIPLIER (1.5f) bool CCarCtrl::bMadDriversCheat; int CCarCtrl::NumLawEnforcerCars; int CCarCtrl::NumAmbulancesOnDuty; int CCarCtrl::NumFiretrucksOnDuty; bool CCarCtrl::bCarsGeneratedAroundCamera; float CCarCtrl::CarDensityMultiplier = 1.0f; int32 CCarCtrl::NumMissionCars; int32 CCarCtrl::NumRandomCars; int32 CCarCtrl::NumParkedCars; int32 CCarCtrl::NumPermanentCars; int8 CCarCtrl::CountDownToCarsAtStart; int32 CCarCtrl::MaxNumberOfCarsInUse = 12; uint32 CCarCtrl::LastTimeLawEnforcerCreated; uint32 CCarCtrl::LastTimeFireTruckCreated; uint32 CCarCtrl::LastTimeAmbulanceCreated; int32 CCarCtrl::MiamiViceCycle; uint32 CCarCtrl::LastTimeMiamiViceGenerated; int32 CCarCtrl::TotalNumOfCarsOfRating[TOTAL_CUSTOM_CLASSES]; int32 CCarCtrl::CarArrays[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; int32 CCarCtrl::NumRequestsOfCarRating[TOTAL_CUSTOM_CLASSES]; int32 CCarCtrl::NumOfLoadedCarsOfRating[TOTAL_CUSTOM_CLASSES]; int32 CCarCtrl::CarFreqArrays[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; int32 CCarCtrl::LoadedCarsArray[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; CVehicle* apCarsToKeep[MAX_CARS_TO_KEEP]; uint32 aCarsToKeepTime[MAX_CARS_TO_KEEP]; void CCarCtrl::GenerateRandomCars() { if (CCutsceneMgr::IsRunning()) { CountDownToCarsAtStart = 2; return; } if (NumRandomCars < 30){ if (CountDownToCarsAtStart == 0) GenerateOneRandomCar(); else if (--CountDownToCarsAtStart == 0) { for (int i = 0; i < 100; i++) GenerateOneRandomCar(); CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter = 20; } } /* Approximately once per 4 seconds. */ if ((CTimer::GetTimeInMilliseconds() & 0xFFFFF000) != (CTimer::GetPreviousTimeInMilliseconds() & 0xFFFFF000)) GenerateEmergencyServicesCar(); } void CCarCtrl::GenerateOneRandomCar() { static int32 unk = 0; bool bTopDownCamera = false; CPlayerInfo* pPlayer = &CWorld::Players[CWorld::PlayerInFocus]; CVector vecTargetPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); CVector2D vecPlayerSpeed = FindPlayerSpeed(); CZoneInfo zone; CTheZones::GetZoneInfoForTimeOfDay(&vecTargetPos, &zone); pPlayer->m_nTrafficMultiplier = pPlayer->m_fRoadDensity * zone.carDensity; if (NumRandomCars >= pPlayer->m_nTrafficMultiplier * CarDensityMultiplier * CIniFile::CarNumberMultiplier) return; if (NumFiretrucksOnDuty + NumAmbulancesOnDuty + NumParkedCars + NumMissionCars + NumLawEnforcerCars + NumRandomCars >= MaxNumberOfCarsInUse) return; CWanted* pWanted = pPlayer->m_pPed->m_pWanted; int carClass; int carModel; if (pWanted->GetWantedLevel() > 1 && NumLawEnforcerCars < pWanted->m_MaximumLawEnforcerVehicles && pWanted->m_CurrentCops < pWanted->m_MaxCops && !CGame::IsInInterior() && ( pWanted->GetWantedLevel() > 3 || pWanted->GetWantedLevel() > 2 && CTimer::GetTimeInMilliseconds() > LastTimeLawEnforcerCreated + 5000 || pWanted->GetWantedLevel() > 1 && CTimer::GetTimeInMilliseconds() > LastTimeLawEnforcerCreated + 8000)) { /* Last pWanted->GetWantedLevel() > 1 is unnecessary but I added it for better readability. */ /* Wouldn't be surprised it was there originally but was optimized out. */ carClass = COPS; carModel = ChoosePoliceCarModel(); }else{ carModel = ChooseModel(&zone, &carClass); if (carModel == -1 || (carClass == COPS && pWanted->GetWantedLevel() >= 1)) /* All cop spawns with wanted level are handled by condition above. */ /* In particular it means that cop cars never spawn if player has wanted level of 1. */ return; } float frontX, frontY; float preferredDistance, angleLimit; bool invertAngleLimitTest; CVector spawnPosition; int32 curNodeId, nextNodeId; float positionBetweenNodes; bool testForCollision; CVehicle* pPlayerVehicle = FindPlayerVehicle(); CVector2D vecPlayerVehicleSpeed; float fPlayerVehicleSpeed; if (pPlayerVehicle) { vecPlayerVehicleSpeed = FindPlayerVehicle()->GetMoveSpeed(); fPlayerVehicleSpeed = vecPlayerVehicleSpeed.Magnitude(); } if (TheCamera.GetForward().z < -0.9f){ /* Player uses topdown camera. */ /* Spawn essentially anywhere. */ frontX = frontY = 0.707f; /* 45 degrees */ angleLimit = -1.0f; bTopDownCamera = true; invertAngleLimitTest = true; preferredDistance = OFFSCREEN_DESPAWN_RANGE + 15.0f; /* BUG: testForCollision not initialized in original game. */ testForCollision = false; }else if (!pPlayerVehicle){ /* Player is not in vehicle. */ testForCollision = true; frontX = TheCamera.CamFrontXNorm; frontY = TheCamera.CamFrontYNorm; switch (CTimer::GetFrameCounter() & 1) { case 0: /* Spawn a vehicle relatively far away from player. */ /* Forward to his current direction (camera direction). */ angleLimit = 0.707f; /* 45 degrees */ invertAngleLimitTest = true; preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; break; case 1: /* Spawn a vehicle close to player to his side. */ /* Kinda not within camera angle. */ angleLimit = 0.707f; /* 45 degrees */ invertAngleLimitTest = false; preferredDistance = OFFSCREEN_DESPAWN_RANGE; break; } }else if (fPlayerVehicleSpeed > 0.4f){ /* 72 km/h */ /* Player is moving fast in vehicle */ /* Prefer spawning vehicles very far away from him. */ frontX = vecPlayerVehicleSpeed.x / fPlayerVehicleSpeed; frontY = vecPlayerVehicleSpeed.y / fPlayerVehicleSpeed; testForCollision = false; switch (CTimer::GetFrameCounter() & 3) { case 0: case 1: /* Spawn a vehicle in a very narrow gap in front of a player */ angleLimit = 0.85f; /* approx 30 degrees */ invertAngleLimitTest = true; preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; break; case 2: /* Spawn a vehicle relatively far away from player. */ /* Forward to his current direction (camera direction). */ angleLimit = 0.707f; /* 45 degrees */ invertAngleLimitTest = true; preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; break; case 3: /* Spawn a vehicle close to player to his side. */ /* Kinda not within camera angle. */ angleLimit = 0.707f; /* 45 degrees */ invertAngleLimitTest = false; preferredDistance = OFFSCREEN_DESPAWN_RANGE; break; } }else if (fPlayerVehicleSpeed > 0.1f){ /* 18 km/h */ /* Player is moving moderately fast in vehicle */ /* Spawn more vehicles to player's side. */ frontX = vecPlayerVehicleSpeed.x / fPlayerVehicleSpeed; frontY = vecPlayerVehicleSpeed.y / fPlayerVehicleSpeed; testForCollision = false; switch (CTimer::GetFrameCounter() & 3) { case 0: /* Spawn a vehicle in a very narrow gap in front of a player */ angleLimit = 0.85f; /* approx 30 degrees */ invertAngleLimitTest = true; preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; break; case 1: /* Spawn a vehicle relatively far away from player. */ /* Forward to his current direction (camera direction). */ angleLimit = 0.707f; /* 45 degrees */ invertAngleLimitTest = true; preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; break; case 2: case 3: /* Spawn a vehicle close to player to his side. */ /* Kinda not within camera angle. */ angleLimit = 0.707f; /* 45 degrees */ invertAngleLimitTest = false; preferredDistance = OFFSCREEN_DESPAWN_RANGE; break; } }else{ /* Player is in vehicle but moving very slow. */ /* Then use camera direction instead of vehicle direction. */ testForCollision = true; frontX = TheCamera.CamFrontXNorm; frontY = TheCamera.CamFrontYNorm; switch (CTimer::GetFrameCounter() & 1) { case 0: /* Spawn a vehicle relatively far away from player. */ /* Forward to his current direction (camera direction). */ angleLimit = 0.707f; /* 45 degrees */ invertAngleLimitTest = true; preferredDistance = REQUEST_ONSCREEN_DISTANCE * TheCamera.GenerationDistMultiplier; break; case 1: /* Spawn a vehicle close to player to his side. */ /* Kinda not within camera angle. */ angleLimit = 0.707f; /* 45 degrees */ invertAngleLimitTest = false; preferredDistance = OFFSCREEN_DESPAWN_RANGE; break; } } if (!ThePaths.GenerateCarCreationCoors(vecTargetPos.x, vecTargetPos.y, frontX, frontY, preferredDistance, angleLimit, invertAngleLimitTest, &spawnPosition, &curNodeId, &nextNodeId, &positionBetweenNodes, carClass == COPS && pWanted->GetWantedLevel() >= 1)) return; CPathNode* pCurNode = &ThePaths.m_pathNodes[curNodeId]; CPathNode* pNextNode = &ThePaths.m_pathNodes[nextNodeId]; bool bBoatGenerated = false; if ((CGeneral::GetRandomNumber() & 0xF) > Min(pCurNode->spawnRate, pNextNode->spawnRate)) return; if (pCurNode->bWaterPath) { bBoatGenerated = true; if (carClass == COPS) { carModel = MI_PREDATOR; carClass = COPS_BOAT; if (!CStreaming::HasModelLoaded(MI_PREDATOR)) { CStreaming::RequestModel(MI_PREDATOR, STREAMFLAGS_DEPENDENCY); return; } } else { int i; carModel = -1; for (i = 10; i > 0 && (carModel == -1 || !CStreaming::HasModelLoaded(carModel)); i--) { carModel = ChooseBoatModel(ChooseBoatRating(&zone)); } if (i == 0) return; } if (pCurNode->bOnlySmallBoats || pNextNode->bOnlySmallBoats) { if (BoatWithTallMast(carModel)) return; } } int16 colliding; CWorld::FindObjectsKindaColliding(spawnPosition, bBoatGenerated ? 40.0f : 10.0f, true, &colliding, 2, nil, false, true, true, false, false); if (colliding) /* If something is already present in spawn position, do not create vehicle*/ return; if (!bBoatGenerated && !ThePaths.TestCoorsCloseness(vecTargetPos, false, spawnPosition)) /* Testing if spawn position can reach target position via valid path. */ return; int16 idInNode = 0; while (idInNode < pCurNode->numLinks && ThePaths.ConnectedNode(idInNode + pCurNode->firstLink) != nextNodeId) idInNode++; int16 connectionId = ThePaths.m_carPathConnections[idInNode + pCurNode->firstLink]; CCarPathLink* pPathLink = &ThePaths.m_carPathLinks[connectionId]; int16 lanesOnCurrentRoad = pPathLink->pathNodeIndex == nextNodeId ? pPathLink->numLeftLanes : pPathLink->numRightLanes; CVehicleModelInfo* pModelInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(carModel); if (lanesOnCurrentRoad == 0) /* Not spawning vehicle if road is one way and intended direction is opposide to that way. */ return; CVehicle* pVehicle; if (CModelInfo::IsBoatModel(carModel)) pVehicle = new CBoat(carModel, RANDOM_VEHICLE); else if (CModelInfo::IsBikeModel(carModel)) pVehicle = new CBike(carModel, RANDOM_VEHICLE); else pVehicle = new CAutomobile(carModel, RANDOM_VEHICLE); pVehicle->AutoPilot.m_nPrevRouteNode = 0; pVehicle->AutoPilot.m_nCurrentRouteNode = curNodeId; pVehicle->AutoPilot.m_nNextRouteNode = nextNodeId; switch (carClass) { case COPS: pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted->GetWantedLevel() != 0){ pVehicle->AutoPilot.m_nCruiseSpeed = CCarAI::FindPoliceCarSpeedForWantedLevel(pVehicle); pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed / 2; pVehicle->AutoPilot.m_nCarMission = CCarAI::FindPoliceCarMissionForWantedLevel(); pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; }else{ pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(12, 16); pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed; pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; } if (carModel == MI_FBIRANCH){ pVehicle->m_currentColour1 = 0; pVehicle->m_currentColour2 = 0; } pVehicle->bCreatedAsPoliceVehicle = true; break; case COPS_BOAT: pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(4, 16); pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed; pVehicle->AutoPilot.m_nCarMission = CCarAI::FindPoliceBoatMissionForWantedLevel(); pVehicle->bCreatedAsPoliceVehicle = true; break; default: pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(9, 14); if (carClass == EXEC) pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(12, 18); else if (carClass == POOR) pVehicle->AutoPilot.m_nCruiseSpeed = CGeneral::GetRandomNumberInRange(7, 10); if (pVehicle->GetColModel()->boundingBox.max.y - pVehicle->GetColModel()->boundingBox.min.y > 10.0f || carClass == BIG) { pVehicle->AutoPilot.m_nCruiseSpeed *= 3; pVehicle->AutoPilot.m_nCruiseSpeed /= 4; } pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed; pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; break; } if (pVehicle && pVehicle->GetModelIndex() == MI_MRWHOOP) pVehicle->m_bSirenOrAlarm = true; pVehicle->AutoPilot.m_nNextPathNodeInfo = connectionId; pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = CGeneral::GetRandomNumber() % lanesOnCurrentRoad; CBox* boundingBox = &CModelInfo::GetModelInfo(pVehicle->GetModelIndex())->GetColModel()->boundingBox; float carLength = 1.0f + (boundingBox->max.y - boundingBox->min.y) / 2; float distanceBetweenNodes = (pCurNode->GetPosition() - pNextNode->GetPosition()).Magnitude2D(); /* If car is so long that it doesn't fit between two car nodes, place it directly in the middle. */ /* Otherwise put it at least in a way that full vehicle length fits between two nodes. */ if (distanceBetweenNodes / 2 < carLength) positionBetweenNodes = 0.5f; else positionBetweenNodes = Min(1.0f - carLength / distanceBetweenNodes, Max(carLength / distanceBetweenNodes, positionBetweenNodes)); pVehicle->AutoPilot.m_nNextDirection = (curNodeId >= nextNodeId) ? 1 : -1; if (pCurNode->numLinks == 1){ /* Do not create vehicle if there is nowhere to go. */ delete pVehicle; return; } int16 nextConnection = pVehicle->AutoPilot.m_nNextPathNodeInfo; int16 newLink; while (nextConnection == pVehicle->AutoPilot.m_nNextPathNodeInfo){ newLink = CGeneral::GetRandomNumber() % pCurNode->numLinks; nextConnection = ThePaths.m_carPathConnections[newLink + pCurNode->firstLink]; } pVehicle->AutoPilot.m_nCurrentPathNodeInfo = nextConnection; pVehicle->AutoPilot.m_nCurrentDirection = (ThePaths.ConnectedNode(newLink + pCurNode->firstLink) >= curNodeId) ? 1 : -1; CVector2D vecBetweenNodes = pNextNode->GetPosition() - pCurNode->GetPosition(); float forwardX, forwardY; float distBetweenNodes = vecBetweenNodes.Magnitude(); if (distanceBetweenNodes == 0.0f){ forwardX = 1.0f; forwardY = 0.0f; }else{ forwardX = vecBetweenNodes.x / distBetweenNodes; forwardY = vecBetweenNodes.y / distBetweenNodes; } /* I think the following might be some form of SetRotateZOnly. */ /* Setting up direction between two car nodes. */ pVehicle->GetForward() = CVector(forwardX, forwardY, 0.0f); pVehicle->GetRight() = CVector(forwardY, -forwardX, 0.0f); pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); float currentPathLinkForwardX = pVehicle->AutoPilot.m_nCurrentDirection * ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo].GetDirX(); float currentPathLinkForwardY = pVehicle->AutoPilot.m_nCurrentDirection * ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo].GetDirY(); float nextPathLinkForwardX = pVehicle->AutoPilot.m_nNextDirection * ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo].GetDirX(); float nextPathLinkForwardY = pVehicle->AutoPilot.m_nNextDirection * ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo].GetDirY(); #ifdef FIX_BUGS CCarPathLink* pCurrentLink; CCarPathLink* pNextLink; CVector positionOnCurrentLinkIncludingLane; CVector positionOnNextLinkIncludingLane; float directionCurrentLinkX; float directionCurrentLinkY; float directionNextLinkX; float directionNextLinkY; if (positionBetweenNodes < 0.5f) { pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; positionOnCurrentLinkIncludingLane = CVector( pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, 0.0f); positionOnNextLinkIncludingLane = CVector( pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, 0.0f); directionCurrentLinkX = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; directionCurrentLinkY = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; /* We want to make a path between two links that may not have the same forward directions a curve. */ pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( &positionOnCurrentLinkIncludingLane, &positionOnNextLinkIncludingLane, directionCurrentLinkX, directionCurrentLinkY, directionNextLinkX, directionNextLinkY ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); pVehicle->AutoPilot.m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - (uint32)((0.5f + positionBetweenNodes) * pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); } else { PickNextNodeRandomly(pVehicle); pVehicle->AutoPilot.m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - (uint32)((positionBetweenNodes - 0.5f) * pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; positionOnCurrentLinkIncludingLane = CVector( pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, 0.0f); positionOnNextLinkIncludingLane = CVector( pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, 0.0f); directionCurrentLinkX = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; directionCurrentLinkY = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; } #else CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; CVector positionOnCurrentLinkIncludingLane( pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, 0.0f); CVector positionOnNextLinkIncludingLane( pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, 0.0f); float directionCurrentLinkX = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; float directionCurrentLinkY = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; float directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; float directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; /* We want to make a path between two links that may not have the same forward directions a curve. */ pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( &positionOnCurrentLinkIncludingLane, &positionOnNextLinkIncludingLane, directionCurrentLinkX, directionCurrentLinkY, directionNextLinkX, directionNextLinkY ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); pVehicle->AutoPilot.m_nTimeEnteredCurve = CTimer::GetTimeInMilliseconds() - (0.5f + positionBetweenNodes) * pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; #endif CVector directionCurrentLink(directionCurrentLinkX, directionCurrentLinkY, 0.0f); CVector directionNextLink(directionNextLinkX, directionNextLinkY, 0.0f); CVector positionIncludingCurve; CVector directionIncludingCurve; CCurves::CalcCurvePoint( &positionOnCurrentLinkIncludingLane, &positionOnNextLinkIncludingLane, &directionCurrentLink, &directionNextLink, GetPositionAlongCurrentCurve(pVehicle), pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve, &positionIncludingCurve, &directionIncludingCurve ); CVector vectorBetweenNodes = pCurNode->GetPosition() - pNextNode->GetPosition(); CVector finalPosition = positionIncludingCurve + vectorBetweenNodes * 2.0f / vectorBetweenNodes.Magnitude(); finalPosition.z = positionBetweenNodes * pNextNode->GetZ() + (1.0f - positionBetweenNodes) * pCurNode->GetZ(); float groundZ = INFINITE_Z; CColPoint colPoint; CEntity* pEntity; if (bBoatGenerated) { if (!CWaterLevel::GetWaterLevel(finalPosition, &groundZ, true)) { delete pVehicle; return; } } else { if (CWorld::ProcessVerticalLine(finalPosition, 1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) groundZ = colPoint.point.z; if (CWorld::ProcessVerticalLine(finalPosition, -1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) { if (ABS(colPoint.point.z - finalPosition.z) < ABS(groundZ - finalPosition.z)) groundZ = colPoint.point.z; } } if (groundZ == INFINITE_Z || ABS(groundZ - finalPosition.z) > 7.0f) { /* Failed to find ground or too far from expected position. */ delete pVehicle; return; } if (CModelInfo::IsBoatModel(carModel)) { finalPosition.z = groundZ; pVehicle->bExtendedRange = true; } else finalPosition.z = groundZ + pVehicle->GetHeightAboveRoad(); pVehicle->SetPosition(finalPosition); pVehicle->SetMoveSpeed(directionIncludingCurve / GAME_SPEED_TO_CARAI_SPEED); CVector2D speedDifferenceWithTarget = (CVector2D)pVehicle->GetMoveSpeed() - vecPlayerSpeed; CVector2D distanceToTarget = positionIncludingCurve - vecTargetPos; switch (carClass) { case COPS: pVehicle->SetStatus((pVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE) ? STATUS_SIMPLE : STATUS_PHYSICS); pVehicle->ChangeLawEnforcerState(1); break; case COPS_BOAT: pVehicle->ChangeLawEnforcerState(1); pVehicle->SetStatus(STATUS_PHYSICS); break; default: bBoatGenerated ? pVehicle->SetStatus(STATUS_PHYSICS) : pVehicle->SetStatus(STATUS_SIMPLE); break; } CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), 0); if (!pVehicle->GetIsOnScreen()){ if ((vecTargetPos - pVehicle->GetPosition()).Magnitude2D() > OFFSCREEN_DESPAWN_RANGE * (pVehicle->bExtendedRange ? EXTENDED_RANGE_DESPAWN_MULTIPLIER : 1.0f)) { /* Too far away cars that are not visible aren't needed. */ delete pVehicle; return; } }else{ if ((vecTargetPos - pVehicle->GetPosition()).Magnitude2D() > TheCamera.GenerationDistMultiplier * (pVehicle->bExtendedRange ? EXTENDED_RANGE_DESPAWN_MULTIPLIER : 1.0f) * ONSCREEN_DESPAWN_RANGE || (vecTargetPos - pVehicle->GetPosition()).Magnitude2D() < TheCamera.GenerationDistMultiplier * MINIMAL_DISTANCE_TO_SPAWN_ONSCREEN) { delete pVehicle; return; } if ((TheCamera.GetPosition() - pVehicle->GetPosition()).Magnitude2D() < 82.5f * TheCamera.GenerationDistMultiplier || bTopDownCamera) { delete pVehicle; return; } if (pVehicle->GetModelIndex() == MI_MARQUIS) { // so marquis can only spawn if player doesn't see it? delete pVehicle; return; } } CVehicleModelInfo* pVehicleModel = pVehicle->GetModelInfo(); float radiusToTest = pVehicleModel->GetColModel()->boundingSphere.radius; if (testForCollision){ CWorld::FindObjectsKindaColliding(pVehicle->GetPosition(), radiusToTest + 20.0f, true, &colliding, 2, nil, false, true, false, false, false); if (colliding){ delete pVehicle; return; } } CWorld::FindObjectsKindaColliding(pVehicle->GetPosition(), radiusToTest, true, &colliding, 2, nil, false, true, false, false, false); if (colliding){ delete pVehicle; return; } if (speedDifferenceWithTarget.x * distanceToTarget.x + speedDifferenceWithTarget.y * distanceToTarget.y >= 0.0f){ delete pVehicle; return; } pVehicleModel->AvoidSameVehicleColour(&pVehicle->m_currentColour1, &pVehicle->m_currentColour2); CWorld::Add(pVehicle); if (carClass == COPS || carClass == COPS_BOAT) CCarAI::AddPoliceCarOccupants(pVehicle); else { pVehicle->SetUpDriver(); int32 passengers = 0; for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) passengers += (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < PROBABILITY_OF_PASSENGER_IN_VEHICLE) ? 1 : 0; if (CModelInfo::IsCarModel(carModel) && (CModelInfo::GetModelInfo(carModel)->GetAnimFileIndex() == CAnimManager::GetAnimationBlockIndex("van") && passengers >= 1)) passengers = 1; for (int i = 0; i < passengers; i++) { CPed* pPassenger = pVehicle->SetupPassenger(i); if (pPassenger) { ++CPopulation::ms_nTotalCarPassengerPeds; pPassenger->bCarPassenger = true; } } } int nMadDrivers; switch (pVehicle->GetVehicleAppearance()) { case VEHICLE_APPEARANCE_BIKE: nMadDrivers = 30; break; case VEHICLE_APPEARANCE_BOAT: nMadDrivers = 40; break; default: nMadDrivers = 6; break; } if ((CGeneral::GetRandomNumber() & 0x7F) < nMadDrivers || bMadDriversCheat) { pVehicle->SetStatus(STATUS_PHYSICS); pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; pVehicle->AutoPilot.m_nCruiseSpeed += 10; } if (carClass == COPS) LastTimeLawEnforcerCreated = CTimer::GetTimeInMilliseconds(); if (pVehicle->GetModelIndex() == MI_CADDY) { pVehicle->SetStatus(STATUS_PHYSICS); pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; } if (carClass == COPS && pVehicle->GetModelIndex() == MI_VICECHEE) { CVehicleModelInfo* pVehicleModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_VICECHEE); switch (MiamiViceCycle) { case 0: pVehicleModel->SetVehicleColour(53, 77); break; case 1: pVehicleModel->SetVehicleColour(15, 77); break; case 2: pVehicleModel->SetVehicleColour(41, 77); break; case 3: pVehicleModel->SetVehicleColour(61, 77); break; default: break; } } if (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) >= (1 - PROBABILITY_OF_DEAD_PED_ACCIDENT)) { if (CModelInfo::IsCarModel(pVehicle->GetModelIndex()) && !pVehicle->bIsLawEnforcer) { if (CPopulation::AddDeadPedInFrontOfCar(pVehicle->GetPosition() + pVehicle->GetForward() * DISTANCE_BETWEEN_CAR_AND_DEAD_PED, pVehicle)) { pVehicle->AutoPilot.m_nCruiseSpeed = 0; pVehicle->SetMoveSpeed(0.0f, 0.0f, 0.0f); for (int i = 0; i < pVehicle->m_nNumPassengers; i++) { if (pVehicle->pPassengers[i]) { pVehicle->pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); pVehicle->pPassengers[i]->m_nLastPedState = PED_WANDER_PATH; pVehicle->pPassengers[i]->m_vehicleInAccident = pVehicle; pVehicle->pPassengers[i]->bDeadPedInFrontOfCar = true; pVehicle->RegisterReference((CEntity**)&pVehicle->pPassengers[i]->m_vehicleInAccident); } } if (pVehicle->pDriver) { pVehicle->pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); pVehicle->pDriver->m_nLastPedState = PED_WANDER_PATH; pVehicle->pDriver->m_vehicleInAccident = pVehicle; pVehicle->pDriver->bDeadPedInFrontOfCar = true; pVehicle->RegisterReference((CEntity**)&pVehicle->pDriver->m_vehicleInAccident); } } } } } bool CCarCtrl::BoatWithTallMast(int32 mi) { return mi == MI_RIO || mi == MI_TROPIC || mi == MI_MARQUIS; } int32 CCarCtrl::ChooseBoatModel(int32 rating) { ++NumRequestsOfCarRating[rating]; return ChooseCarModel(rating); } int32 CCarCtrl::ChooseBoatRating(CZoneInfo* pZoneInfo) { int rnd = CGeneral::GetRandomNumberInRange(0, 1000); for (int i = 0; i < NUM_BOAT_CLASSES - 1; i++) { if (rnd < pZoneInfo->boatThreshold[i]) return FIRST_BOAT_RATING + i; } return FIRST_BOAT_RATING + NUM_BOAT_CLASSES - 1; } int32 CCarCtrl::ChooseCarRating(CZoneInfo* pZoneInfo) { int rnd = CGeneral::GetRandomNumberInRange(0, 1000); for (int i = 0; i < NUM_CAR_CLASSES - 1; i++) { if (rnd < pZoneInfo->carThreshold[i]) return i; } return FIRST_CAR_RATING + NUM_CAR_CLASSES - 1; } int32 CCarCtrl::ChooseModel(CZoneInfo* pZone, int* pClass) { int32 model = -1; int32 i; for (i = 10; i > 0 && (model == -1 || !CStreaming::HasModelLoaded(model)); i--) { int rnd = CGeneral::GetRandomNumberInRange(0, 1000); if (rnd < pZone->copThreshold) { *pClass = COPS; model = ChoosePoliceCarModel(); continue; } int32 j; for (j = 0; j < NUM_GANG_CAR_CLASSES; j++) { if (rnd < pZone->gangThreshold[j]) { *pClass = j + FIRST_GANG_CAR_RATING; model = ChooseGangCarModel(j); break; } } if (j != NUM_GANG_CAR_CLASSES) continue; *pClass = ChooseCarRating(pZone); model = ChooseCarModel(*pClass); } if (i == 0) return -1; return model; } int32 CCarCtrl::ChooseCarModel(int32 vehclass) { int32 model = -1; ++NumRequestsOfCarRating[vehclass]; if (NumOfLoadedCarsOfRating[vehclass] == 0) return -1; int32 rnd = CGeneral::GetRandomNumberInRange(0, CarFreqArrays[vehclass][NumOfLoadedCarsOfRating[vehclass] - 1]); int32 index = 0; while (rnd > CarFreqArrays[vehclass][index]) index++; assert(LoadedCarsArray[vehclass][index]); return LoadedCarsArray[vehclass][index]; } void CCarCtrl::AddToLoadedVehicleArray(int32 mi, int32 rating, int32 freq) { LoadedCarsArray[rating][NumOfLoadedCarsOfRating[rating]] = mi; assert(mi >= 130); CarFreqArrays[rating][NumOfLoadedCarsOfRating[rating]] = freq; if (NumOfLoadedCarsOfRating[rating]) CarFreqArrays[rating][NumOfLoadedCarsOfRating[rating]] += CarFreqArrays[rating][NumOfLoadedCarsOfRating[rating] - 1]; NumOfLoadedCarsOfRating[rating]++; } void CCarCtrl::RemoveFromLoadedVehicleArray(int mi, int32 rating) { int index = 0; while (LoadedCarsArray[rating][index] != -1) { if (LoadedCarsArray[rating][index] == mi) break; index++; } assert(LoadedCarsArray[rating][index] == mi); int32 freq = CarFreqArrays[rating][index]; if (index > 0) freq -= CarFreqArrays[rating][index - 1]; while (LoadedCarsArray[rating][index + 1] != -1) { LoadedCarsArray[rating][index] = LoadedCarsArray[rating][index + 1]; CarFreqArrays[rating][index] = CarFreqArrays[rating][index + 1] - freq; index++; } --NumOfLoadedCarsOfRating[rating]; } int32 CCarCtrl::ChooseCarModelToLoad(int rating) { return CarArrays[rating][CGeneral::GetRandomNumberInRange(0, TotalNumOfCarsOfRating[rating])]; } int32 CCarCtrl::ChoosePoliceCarModel(void) { if (FindPlayerPed()->m_pWanted->AreMiamiViceRequired() && #ifdef FIX_BUGS (CTimer::GetTimeInMilliseconds() > LastTimeMiamiViceGenerated + 120000 || LastTimeMiamiViceGenerated == 0) && #else CTimer::GetTimeInMilliseconds() > LastTimeMiamiViceGenerated + 120000 && #endif CStreaming::HasModelLoaded(MI_VICECHEE)) { switch (MiamiViceCycle) { case 0: if (CStreaming::HasModelLoaded(MI_VICE1) && CStreaming::HasModelLoaded(MI_VICE2)) return MI_VICECHEE; break; case 1: if (CStreaming::HasModelLoaded(MI_VICE3) && CStreaming::HasModelLoaded(MI_VICE4)) return MI_VICECHEE; break; case 2: if (CStreaming::HasModelLoaded(MI_VICE5) && CStreaming::HasModelLoaded(MI_VICE6)) return MI_VICECHEE; break; case 3: if (CStreaming::HasModelLoaded(MI_VICE7) && CStreaming::HasModelLoaded(MI_VICE8)) return MI_VICECHEE; break; default: break; } } if (FindPlayerPed()->m_pWanted->AreSwatRequired() && CStreaming::HasModelLoaded(MI_ENFORCER) && CStreaming::HasModelLoaded(MI_POLICE)) return ((CGeneral::GetRandomNumber() & 0xF) == 0) ? MI_ENFORCER : MI_POLICE; if (FindPlayerPed()->m_pWanted->AreFbiRequired() && CStreaming::HasModelLoaded(MI_FBIRANCH) && CStreaming::HasModelLoaded(MI_FBI)) return MI_FBIRANCH; if (FindPlayerPed()->m_pWanted->AreArmyRequired() && CStreaming::HasModelLoaded(MI_RHINO) && CStreaming::HasModelLoaded(MI_BARRACKS) && CStreaming::HasModelLoaded(MI_ARMY)) return CGeneral::GetRandomTrueFalse() ? MI_BARRACKS : MI_RHINO; return MI_POLICE; } int32 CCarCtrl::ChooseGangCarModel(int32 gang) { if (CGangs::HaveGangModelsLoaded(gang)) return CGangs::GetGangVehicleModel(gang); return -1; } void CCarCtrl::AddToCarArray(int32 id, int32 vehclass) { assert(TotalNumOfCarsOfRating[vehclass] < MAX_CAR_MODELS_IN_ARRAY); CarArrays[vehclass][TotalNumOfCarsOfRating[vehclass]++] = id; } void CCarCtrl::RemoveDistantCars() { for (int i = CPools::GetVehiclePool()->GetSize()-1; i >= 0; i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; PossiblyRemoveVehicle(pVehicle); if (pVehicle->bCreateRoadBlockPeds){ if ((pVehicle->GetPosition() - FindPlayerCentreOfWorld(CWorld::PlayerInFocus)).Magnitude2D() < DISTANCE_TO_SPAWN_ROADBLOCK_PEDS) { CRoadBlocks::GenerateRoadBlockCopsForCar(pVehicle, pVehicle->m_nRoadblockType); pVehicle->bCreateRoadBlockPeds = false; } } } } void CCarCtrl::RemoveCarsIfThePoolGetsFull(void) { if ((CTimer::GetFrameCounter() & 7) != 3) return; if (CPools::GetVehiclePool()->GetNoOfFreeSpaces() >= 8) return; int i = CPools::GetVehiclePool()->GetSize(); float md = 10000000.f; CVehicle* pClosestVehicle = nil; while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if (IsThisVehicleInteresting(pVehicle) || pVehicle->bIsLocked) continue; if (!pVehicle->CanBeDeleted() || CCranes::IsThisCarBeingTargettedByAnyCrane(pVehicle)) continue; float distance = (TheCamera.GetPosition() - pVehicle->GetPosition()).Magnitude(); if (distance < md) { md = distance; pClosestVehicle = pVehicle; } } if (pClosestVehicle) { CWorld::Remove(pClosestVehicle); delete pClosestVehicle; } } void CCarCtrl::PossiblyRemoveVehicle(CVehicle* pVehicle) { #ifdef FIX_BUGS if (pVehicle->bIsLocked) return; #endif CVector vecPlayerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); /* BUG: this variable is initialized only in if-block below but can be used outside of it. */ if (!IsThisVehicleInteresting(pVehicle) && !pVehicle->bIsLocked && pVehicle->CanBeDeleted() && !CCranes::IsThisCarBeingTargettedByAnyCrane(pVehicle)){ if (pVehicle->bFadeOut && CVisibilityPlugins::GetClumpAlpha(pVehicle->GetClump()) == 0){ CWorld::Remove(pVehicle); delete pVehicle; return; } float distanceToPlayer = (pVehicle->GetPosition() - vecPlayerPos).Magnitude2D(); float threshold = OFFSCREEN_DESPAWN_RANGE; if (pVehicle->GetIsOnScreen() || TheCamera.Cams[TheCamera.ActiveCam].LookingLeft || TheCamera.Cams[TheCamera.ActiveCam].LookingRight || TheCamera.Cams[TheCamera.ActiveCam].LookingBehind || TheCamera.GetLookDirection() == 0 || pVehicle->VehicleCreatedBy == PARKED_VEHICLE || pVehicle->GetModelIndex() == MI_AMBULAN || pVehicle->GetModelIndex() == MI_FIRETRUCK || pVehicle->bIsLawEnforcer || pVehicle->bIsCarParkVehicle || CTimer::GetTimeInMilliseconds() < pVehicle->m_nSetPieceExtendedRangeTime ){ threshold = ONSCREEN_DESPAWN_RANGE * TheCamera.GenerationDistMultiplier; } if (TheCamera.GetForward().z < -0.9f) threshold = 70.0f; if (pVehicle->bExtendedRange) threshold *= EXTENDED_RANGE_DESPAWN_MULTIPLIER; if (distanceToPlayer > threshold && !CGarages::IsPointWithinHideOutGarage(pVehicle->GetPosition())){ if (pVehicle->GetIsOnScreen()){ pVehicle->bFadeOut = true; }else{ CWorld::Remove(pVehicle); delete pVehicle; } return; } } if ((pVehicle->GetStatus() == STATUS_SIMPLE || pVehicle->GetStatus() == STATUS_PHYSICS && (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS || pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS)) && CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission > 5000 && !pVehicle->GetIsOnScreen() && (pVehicle->GetPosition() - vecPlayerPos).Magnitude2D() > 22.0f && !IsThisVehicleInteresting(pVehicle) && !pVehicle->bIsLocked && pVehicle->CanBeDeleted() && !CTrafficLights::ShouldCarStopForLight(pVehicle, true) && !CTrafficLights::ShouldCarStopForBridge(pVehicle) && !CGarages::IsPointWithinHideOutGarage(pVehicle->GetPosition())){ CWorld::Remove(pVehicle); delete pVehicle; return; } if (pVehicle->GetStatus() != STATUS_WRECKED || pVehicle->m_nTimeOfDeath == 0) return; if (CTimer::GetTimeInMilliseconds() > pVehicle->m_nTimeOfDeath + 60000 && !pVehicle->GetIsOnScreen()){ if ((pVehicle->GetPosition() - vecPlayerPos).MagnitudeSqr() > SQR(7.5f)){ if (!CGarages::IsPointWithinHideOutGarage(pVehicle->GetPosition())){ CWorld::Remove(pVehicle); delete pVehicle; } } } } int32 CCarCtrl::CountCarsOfType(int32 mi) { int32 total = 0; for (int i = CPools::GetVehiclePool()->GetSize()-1; i >= 0; i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if (pVehicle->GetModelIndex() == mi) total++; } return total; } static CVector GetRandomOffsetForVehicle(CVehicle* pVehicle, bool bNext) { CVector offset; int32 seed = ((bNext ? pVehicle->AutoPilot.m_nNextPathNodeInfo : pVehicle->AutoPilot.m_nCurrentPathNodeInfo) + pVehicle->m_randomSeed) & 7; offset.x = (seed - 3) * 0.009f; offset.y = ((seed >> 3) - 3) * 0.009f; offset.z = 0.0f; return offset; } void CCarCtrl::UpdateCarOnRails(CVehicle* pVehicle) { if (pVehicle->AutoPilot.m_nTempAction == TEMPACT_WAIT){ pVehicle->SetMoveSpeed(0.0f, 0.0f, 0.0f); pVehicle->AutoPilot.ModifySpeed(0.0f); if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTempAction){ pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nAntiReverseTimer = 0; pVehicle->AutoPilot.m_nTimeToStartMission = 0; } return; } SlowCarOnRailsDownForTrafficAndLights(pVehicle); if (pVehicle->AutoPilot.m_nTimeEnteredCurve + pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve <= CTimer::GetTimeInMilliseconds()) PickNextNodeAccordingStrategy(pVehicle); if (pVehicle->GetStatus() == STATUS_PHYSICS) return; CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; float currentPathLinkForwardX = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; float currentPathLinkForwardY = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; float nextPathLinkForwardX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; float nextPathLinkForwardY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; CVector positionOnCurrentLinkIncludingLane( pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, 0.0f); CVector positionOnNextLinkIncludingLane( pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, 0.0f); CVector directionCurrentLink = GetRandomOffsetForVehicle(pVehicle, false); directionCurrentLink += CVector(currentPathLinkForwardX, currentPathLinkForwardY, 0.0f); directionCurrentLink.Normalise(); CVector directionNextLink = GetRandomOffsetForVehicle(pVehicle, true); directionNextLink += CVector(nextPathLinkForwardX, nextPathLinkForwardY, 0.0f); directionNextLink.Normalise(); CVector positionIncludingCurve; CVector directionIncludingCurve; CCurves::CalcCurvePoint( &positionOnCurrentLinkIncludingLane, &positionOnNextLinkIncludingLane, &directionCurrentLink, &directionNextLink, GetPositionAlongCurrentCurve(pVehicle), pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve, &positionIncludingCurve, &directionIncludingCurve ); positionIncludingCurve.z = 15.0f; DragCarToPoint(pVehicle, &positionIncludingCurve); pVehicle->SetMoveSpeed(directionIncludingCurve / GAME_SPEED_TO_CARAI_SPEED); } float CCarCtrl::FindMaximumSpeedForThisCarInTraffic(CVehicle* pVehicle) { if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_AVOID_CARS || pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_PLOUGH_THROUGH) return pVehicle->AutoPilot.GetCruiseSpeed(); float left = pVehicle->GetPosition().x - DISTANCE_TO_SCAN_FOR_DANGER; float right = pVehicle->GetPosition().x + DISTANCE_TO_SCAN_FOR_DANGER; float top = pVehicle->GetPosition().y - DISTANCE_TO_SCAN_FOR_DANGER; float bottom = pVehicle->GetPosition().y + DISTANCE_TO_SCAN_FOR_DANGER; int xstart = Max(0, CWorld::GetSectorIndexX(left)); int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); int ystart = Max(0, CWorld::GetSectorIndexY(top)); int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); assert(xstart <= xend); assert(ystart <= yend); float maxSpeed = pVehicle->AutoPilot.GetCruiseSpeed(); CWorld::AdvanceCurrentScanCode(); for (int y = ystart; y <= yend; y++){ for (int x = xstart; x <= xend; x++){ CSector* s = CWorld::GetSector(x, y); SlowCarDownForCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.GetCruiseSpeed()); SlowCarDownForCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.GetCruiseSpeed()); SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.GetCruiseSpeed()); SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS_OVERLAP], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.GetCruiseSpeed()); } } pVehicle->bWarnedPeds = true; if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS || pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS) return maxSpeed; return (maxSpeed + pVehicle->AutoPilot.GetCruiseSpeed()) / 2; } void CCarCtrl::ScanForPedDanger(CVehicle* pVehicle) { bool storedSlowDownFlag = pVehicle->AutoPilot.m_bSlowedDownBecauseOfPeds; float left = pVehicle->GetPosition().x - DISTANCE_TO_SCAN_FOR_PED_DANGER; float right = pVehicle->GetPosition().x + DISTANCE_TO_SCAN_FOR_PED_DANGER; float top = pVehicle->GetPosition().y - DISTANCE_TO_SCAN_FOR_PED_DANGER; float bottom = pVehicle->GetPosition().y + DISTANCE_TO_SCAN_FOR_PED_DANGER; int xstart = Max(0, CWorld::GetSectorIndexX(left)); int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); int ystart = Max(0, CWorld::GetSectorIndexY(top)); int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); assert(xstart <= xend); assert(ystart <= yend); float maxSpeed = pVehicle->AutoPilot.m_nCruiseSpeed; CWorld::AdvanceCurrentScanCode(); for (int y = ystart; y <= yend; y++) { for (int x = xstart; x <= xend; x++) { CSector* s = CWorld::GetSector(x, y); SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed); SlowCarDownForPedsSectorList(s->m_lists[ENTITYLIST_PEDS_OVERLAP], pVehicle, left, top, right, bottom, &maxSpeed, pVehicle->AutoPilot.m_nCruiseSpeed); } } pVehicle->bWarnedPeds = true; pVehicle->AutoPilot.m_bSlowedDownBecauseOfPeds = storedSlowDownFlag; } void CCarCtrl::SlowCarOnRailsDownForTrafficAndLights(CVehicle* pVehicle) { float maxSpeed; if (CTrafficLights::ShouldCarStopForLight(pVehicle, false) || CTrafficLights::ShouldCarStopForBridge(pVehicle)){ CCarAI::CarHasReasonToStop(pVehicle); maxSpeed = 0.0f; }else{ maxSpeed = FindMaximumSpeedForThisCarInTraffic(pVehicle); } float curSpeed = pVehicle->AutoPilot.m_fMaxTrafficSpeed; if (maxSpeed >= curSpeed){ if (maxSpeed > curSpeed) pVehicle->AutoPilot.ModifySpeed(Min(maxSpeed, curSpeed + 0.05f * CTimer::GetTimeStep())); }else if (curSpeed != 0.0f) { if (curSpeed < 0.1f) pVehicle->AutoPilot.ModifySpeed(0.0f); else pVehicle->AutoPilot.ModifySpeed(Max(maxSpeed, curSpeed - 0.7f * CTimer::GetTimeStep())); } } void CCarCtrl::SlowCarDownForPedsSectorList(CPtrList& lst, CVehicle* pVehicle, float x_inf, float y_inf, float x_sup, float y_sup, float* pSpeed, float curSpeed) { float frontOffset = pVehicle->GetModelInfo()->GetColModel()->boundingBox.max.y; float frontSafe = frontOffset + SAFE_DISTANCE_TO_PED; for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next){ CPed* pPed = (CPed*)pNode->item; if (pPed->m_scanCode == CWorld::GetCurrentScanCode()) continue; if (!pPed->bUsesCollision) continue; pPed->m_scanCode = CWorld::GetCurrentScanCode(); CVector vecPedPos = pPed->GetPosition(); if (vecPedPos.x < x_inf || vecPedPos.x > x_sup) continue; if (vecPedPos.y < y_inf || vecPedPos.y > y_sup) continue; if (ABS(vecPedPos.z - pVehicle->GetPosition().z) >= 4.0f) continue; CVector vecToPed = vecPedPos - pVehicle->GetPosition(); float dotDirection = DotProduct(pVehicle->GetForward(), vecToPed); float dotVelocity = DotProduct(pVehicle->GetForward(), pVehicle->GetMoveSpeed()); if (dotDirection <= frontOffset) /* If already run him over, don't care */ continue; float distanceUntilHit = dotDirection - frontOffset; float movementTowardsPedPerSecond = GAME_SPEED_TO_METERS_PER_SECOND * dotVelocity; if (4 * movementTowardsPedPerSecond <= distanceUntilHit) /* If car isn't projected to hit a ped in 4 seconds, don't care */ continue; float sidewaysDistance = ABS(DotProduct(pVehicle->GetRight(), vecToPed)); float sideLength = pVehicle->GetModelInfo()->GetColModel()->boundingBox.max.x; if (pVehicle->m_vehType == VEHICLE_TYPE_BIKE) sideLength *= 1.6f; if (sideLength + 0.5f < sidewaysDistance) /* If car is far enough taking side into account, don't care */ continue; if (pPed->IsPed()){ /* ...how can it not be? */ if (pPed->GetPedState() != PED_STEP_AWAY && pPed->GetPedState() != PED_DIVE_AWAY){ if (distanceUntilHit < movementTowardsPedPerSecond){ /* Very close. Time to evade. */ if (pVehicle->GetModelIndex() == MI_RCBANDIT){ if (dotVelocity * GAME_SPEED_TO_METERS_PER_SECOND / 2 > distanceUntilHit) pPed->SetEvasiveStep(pVehicle, 0); }else if (dotVelocity > 0.3f) { if (sideLength + 0.1f < sidewaysDistance) pPed->SetEvasiveStep(pVehicle, 0); else pPed->SetEvasiveDive(pVehicle, 0); }else if (dotVelocity > 0.1f) { if (sideLength - 0.5f < sidewaysDistance) pPed->SetEvasiveStep(pVehicle, 0); else pPed->SetEvasiveDive(pVehicle, 0); } }else{ /* Relatively safe but annoying. */ if (pVehicle->GetStatus() == STATUS_PLAYER && pPed->GetPedState() != PED_FLEE_ENTITY && pPed->CharCreatedBy == RANDOM_CHAR){ float angleCarToPed = CGeneral::GetRadianAngleBetweenPoints( pVehicle->GetPosition().x, pVehicle->GetPosition().y, pPed->GetPosition().x, pPed->GetPosition().y ); angleCarToPed = CGeneral::LimitRadianAngle(angleCarToPed); pPed->m_headingRate = CGeneral::LimitRadianAngle(pPed->m_headingRate); float visibilityAngle = ABS(angleCarToPed - pPed->m_headingRate); if (visibilityAngle > PI) visibilityAngle = TWOPI - visibilityAngle; if (visibilityAngle < HALFPI || pVehicle->m_nCarHornTimer){ /* if ped sees the danger or if car horn is on */ pPed->SetFlee(pVehicle, 2000); pPed->bUsePedNodeSeek = false; pPed->SetMoveState(PEDMOVE_RUN); } }else{ CPlayerPed* pPlayerPed = (CPlayerPed*)pPed; if (pPlayerPed->IsPlayer() && dotDirection < frontSafe && pPlayerPed->IsPedInControl() && pPlayerPed->m_fMoveSpeed < 1.0f && !pPlayerPed->bIsLooking && CTimer::GetTimeInMilliseconds() > pPlayerPed->m_lookTimer) { pPlayerPed->AnnoyPlayerPed(false); pPlayerPed->SetLookFlag(pVehicle, true); pPlayerPed->SetLookTimer(1500); if (pPlayerPed->GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED || pPlayerPed->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT || pPlayerPed->GetWeapon()->m_eWeaponType == WEAPONTYPE_COLT45 || pPlayerPed->GetWeapon()->m_eWeaponType == WEAPONTYPE_UZI) { pPlayerPed->bShakeFist = true; } } } } } } /* Ped stuff done. Now vehicle stuff. */ if (distanceUntilHit < 10.0f){ if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_STOP_FOR_CARS || pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_SLOW_DOWN_FOR_CARS){ *pSpeed = Min(*pSpeed, ABS(distanceUntilHit - 1.0f) / 10.0f * curSpeed); pVehicle->AutoPilot.m_bSlowedDownBecauseOfPeds = true; if (distanceUntilHit < 2.0f){ pVehicle->AutoPilot.m_nTempAction = TEMPACT_WAIT; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 3000; } } } } } void CCarCtrl::SlowCarDownForCarsSectorList(CPtrList& lst, CVehicle* pVehicle, float x_inf, float y_inf, float x_sup, float y_sup, float* pSpeed, float curSpeed) { for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next){ CVehicle* pTestVehicle = (CVehicle*)pNode->item; if (pVehicle == pTestVehicle) continue; if (pTestVehicle->m_scanCode == CWorld::GetCurrentScanCode()) continue; if (!pTestVehicle->bUsesCollision) continue; pTestVehicle->m_scanCode = CWorld::GetCurrentScanCode(); CVector boundCenter = pTestVehicle->GetBoundCentre(); if (boundCenter.x < x_inf || boundCenter.x > x_sup) continue; if (boundCenter.y < y_inf || boundCenter.y > y_sup) continue; if (Abs(boundCenter.z - pVehicle->GetPosition().z) < 5.0f) SlowCarDownForOtherCar(pTestVehicle, pVehicle, pSpeed, curSpeed); } } void CCarCtrl::SlowCarDownForOtherCar(CEntity* pOtherEntity, CVehicle* pVehicle, float* pSpeed, float curSpeed) { CVector forwardA = pVehicle->GetForward(); ((CVector2D)forwardA).Normalise(); if (DotProduct2D(pOtherEntity->GetPosition() - pVehicle->GetPosition(), forwardA) < 0.0f) return; CVector forwardB = pOtherEntity->GetForward(); ((CVector2D)forwardB).Normalise(); forwardA.z = forwardB.z = 0.0f; CVehicle* pOtherVehicle = (CVehicle*)pOtherEntity; /* why is the argument CEntity if it's always CVehicle anyway and is casted? */ float speedOtherX = GAME_SPEED_TO_CARAI_SPEED * pOtherVehicle->GetMoveSpeed().x; float speedOtherY = GAME_SPEED_TO_CARAI_SPEED * pOtherVehicle->GetMoveSpeed().y; float projectionX = speedOtherX - forwardA.x * curSpeed; float projectionY = speedOtherY - forwardA.y * curSpeed; float proximityA = TestCollisionBetween2MovingRects(pOtherVehicle, pVehicle, projectionX, projectionY, &forwardA, &forwardB, 0); float proximityB = TestCollisionBetween2MovingRects(pVehicle, pOtherVehicle, -projectionX, -projectionY, &forwardB, &forwardA, 1); float minProximity = Min(proximityA, proximityB); if (minProximity >= 0.0f && minProximity < 1.5f){ minProximity = Max(0.0f, (minProximity - 0.2f) / 1.3f); pVehicle->AutoPilot.m_bSlowedDownBecauseOfCars = true; *pSpeed = Min(*pSpeed, minProximity * curSpeed); } if (minProximity >= 0.0f && minProximity < 0.5f && pOtherEntity->IsVehicle() && CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeToStartMission > 15000 && CTimer::GetTimeInMilliseconds() - pOtherVehicle->AutoPilot.m_nTimeToStartMission > 15000){ /* If cars are standing for 15 seconds, annoy one of them and make avoid cars. */ if (pOtherEntity != FindPlayerVehicle() && DotProduct2D(pVehicle->GetForward(), pOtherVehicle->GetForward()) < -0.5f && pVehicle < pOtherVehicle){ /* that comparasion though... */ *pSpeed = Max(curSpeed / 5, *pSpeed); if (pVehicle->GetStatus() == STATUS_SIMPLE){ pVehicle->SetStatus(STATUS_PHYSICS); SwitchVehicleToRealPhysics(pVehicle); } pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; } } } float CCarCtrl::TestCollisionBetween2MovingRects(CVehicle* pVehicleA, CVehicle* pVehicleB, float projectionX, float projectionY, CVector* pForwardA, CVector* pForwardB, uint8 id) { CVector2D vecBToA = pVehicleA->GetPosition() - pVehicleB->GetPosition(); float lenB = pVehicleB->GetModelInfo()->GetColModel()->boundingBox.max.y; float widthB = pVehicleB->GetModelInfo()->GetColModel()->boundingBox.max.x; float backLenB = -pVehicleB->GetModelInfo()->GetColModel()->boundingBox.min.y; float lenA = pVehicleA->GetModelInfo()->GetColModel()->boundingBox.max.y; float widthA = pVehicleA->GetModelInfo()->GetColModel()->boundingBox.max.x; float backLenA = -pVehicleA->GetModelInfo()->GetColModel()->boundingBox.min.y; float proximity = 1.0f; float fullWidthB = 2.0f * widthB; float fullLenB = lenB + backLenB; for (int i = 0; i < 4; i++){ float testedOffsetX; float testedOffsetY; switch (i) { case 0: /* Front right corner */ testedOffsetX = vecBToA.x + widthA * pForwardB->y + lenA * pForwardB->x; testedOffsetY = vecBToA.y + lenA * pForwardB->y - widthA * pForwardB->x; break; case 1: /* Front left corner */ testedOffsetX = vecBToA.x + -widthA * pForwardB->x + lenA * pForwardB->x; testedOffsetY = vecBToA.y + lenA * pForwardB->y + widthA * pForwardB->x; break; case 2: /* Rear right corner */ testedOffsetX = vecBToA.x + widthA * pForwardB->y - backLenA * pForwardB->x; testedOffsetY = vecBToA.y - backLenA * pForwardB->y - widthA * pForwardB->x; break; case 3: /* Rear left corner */ testedOffsetX = vecBToA.x - widthA * pForwardB->y - backLenA * pForwardB->x; testedOffsetY = vecBToA.y - backLenA * pForwardB->y + widthA * pForwardB->x; break; default: break; } /* Testing width collision */ float baseWidthProximity = 0.0f; float fullWidthProximity = 1.0f; float widthDistance = testedOffsetX * pForwardA->y - testedOffsetY * pForwardA->x; float widthProjection = projectionX * pForwardA->y - projectionY * pForwardA->x; if (widthDistance > widthB){ if (widthProjection < 0.0f){ float proximityWidth = -(widthDistance - widthB) / widthProjection; if (proximityWidth < 1.0f){ baseWidthProximity = proximityWidth; fullWidthProximity = Min(1.0f, proximityWidth - fullWidthB / widthProjection); }else{ baseWidthProximity = 1.0f; } }else{ baseWidthProximity = 1.0f; fullWidthProximity = 1.0f; } }else if (widthDistance < -widthB){ if (widthProjection > 0.0f) { float proximityWidth = -(widthDistance + widthB) / widthProjection; if (proximityWidth < 1.0f) { baseWidthProximity = proximityWidth; fullWidthProximity = Min(1.0f, proximityWidth + fullWidthB / widthProjection); } else { baseWidthProximity = 1.0f; } } else { baseWidthProximity = 1.0f; fullWidthProximity = 1.0f; } }else if (widthProjection > 0.0f){ fullWidthProximity = (widthB - widthDistance) / widthProjection; }else if (widthProjection < 0.0f){ fullWidthProximity = -(widthB + widthDistance) / widthProjection; } /* Testing length collision */ float baseLengthProximity = 0.0f; float fullLengthProximity = 1.0f; float lenDistance = testedOffsetX * pForwardA->x + testedOffsetY * pForwardA->y; float lenProjection = projectionX * pForwardA->x + projectionY * pForwardA->y; if (lenDistance > lenB) { if (lenProjection < 0.0f) { float proximityLength = -(lenDistance - lenB) / lenProjection; if (proximityLength < 1.0f) { baseLengthProximity = proximityLength; fullLengthProximity = Min(1.0f, proximityLength - fullLenB / lenProjection); } else { baseLengthProximity = 1.0f; } } else { baseLengthProximity = 1.0f; fullLengthProximity = 1.0f; } } else if (lenDistance < -backLenB) { if (lenProjection > 0.0f) { float proximityLength = -(lenDistance + backLenB) / lenProjection; if (proximityLength < 1.0f) { baseLengthProximity = proximityLength; fullLengthProximity = Min(1.0f, proximityLength + fullLenB / lenProjection); } else { baseLengthProximity = 1.0f; } } else { baseLengthProximity = 1.0f; fullLengthProximity = 1.0f; } } else if (lenProjection > 0.0f) { fullLengthProximity = (lenB - lenDistance) / lenProjection; } else if (lenProjection < 0.0f) { fullLengthProximity = -(backLenB + lenDistance) / lenProjection; } float baseProximity = Max(baseWidthProximity, baseLengthProximity); if (baseProximity < fullWidthProximity && baseProximity < fullLengthProximity) proximity = Min(proximity, baseProximity); } return proximity; } float CCarCtrl::FindAngleToWeaveThroughTraffic(CVehicle* pVehicle, CPhysical* pTarget, float angleToTarget, float angleForward) { float distanceToTest = Min(2.0f, pVehicle->GetMoveSpeed().Magnitude2D() / 0.4f + 1.0f) * 12.0f; float left = pVehicle->GetPosition().x - distanceToTest; float right = pVehicle->GetPosition().x + distanceToTest; float top = pVehicle->GetPosition().y - distanceToTest; float bottom = pVehicle->GetPosition().y + distanceToTest; int xstart = Max(0, CWorld::GetSectorIndexX(left)); int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); int ystart = Max(0, CWorld::GetSectorIndexY(top)); int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); assert(xstart <= xend); assert(ystart <= yend); float angleToWeaveLeft = angleToTarget; float angleToWeaveRight = angleToTarget; CWorld::AdvanceCurrentScanCode(); float angleToWeaveLeftLastIteration = -9999.9f; float angleToWeaveRightLastIteration = -9999.9f; while (angleToWeaveLeft != angleToWeaveLeftLastIteration || angleToWeaveRight != angleToWeaveRightLastIteration){ angleToWeaveLeftLastIteration = angleToWeaveLeft; angleToWeaveRightLastIteration = angleToWeaveRight; for (int y = ystart; y <= yend; y++) { for (int x = xstart; x <= xend; x++) { CSector* s = CWorld::GetSector(x, y); WeaveThroughCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES], pVehicle, pTarget, left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); WeaveThroughCarsSectorList(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP], pVehicle, pTarget, left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); WeaveThroughPedsSectorList(s->m_lists[ENTITYLIST_PEDS], pVehicle, pTarget, left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); WeaveThroughPedsSectorList(s->m_lists[ENTITYLIST_PEDS_OVERLAP], pVehicle, pTarget, left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); WeaveThroughObjectsSectorList(s->m_lists[ENTITYLIST_OBJECTS], pVehicle, left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); WeaveThroughObjectsSectorList(s->m_lists[ENTITYLIST_OBJECTS_OVERLAP], pVehicle, left, top, right, bottom, &angleToWeaveLeft, &angleToWeaveRight); } } } float angleDiffFromActualToTarget = LimitRadianAngle(angleForward - angleToTarget); float angleToBisectActualToTarget = LimitRadianAngle(angleToTarget + angleDiffFromActualToTarget / 2); float angleDiffLeft = LimitRadianAngle(angleToWeaveLeft - angleToBisectActualToTarget); angleDiffLeft = ABS(angleDiffLeft); float angleDiffRight = LimitRadianAngle(angleToWeaveRight - angleToBisectActualToTarget); angleDiffRight = ABS(angleDiffRight); if (angleDiffLeft > HALFPI && angleDiffRight > HALFPI) return angleToBisectActualToTarget; if (ABS(angleDiffLeft - angleDiffRight) < 0.08f) return angleToWeaveRight; return angleDiffLeft < angleDiffRight ? angleToWeaveLeft : angleToWeaveRight; } void CCarCtrl::WeaveThroughCarsSectorList(CPtrList& lst, CVehicle* pVehicle, CPhysical* pTarget, float x_inf, float y_inf, float x_sup, float y_sup, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) { for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next) { CVehicle* pTestVehicle = (CVehicle*)pNode->item; if (pTestVehicle->m_scanCode == CWorld::GetCurrentScanCode()) continue; if (!pTestVehicle->bUsesCollision) continue; if (pTestVehicle == pTarget) continue; pTestVehicle->m_scanCode = CWorld::GetCurrentScanCode(); if (pTestVehicle->GetBoundCentre().x < x_inf || pTestVehicle->GetBoundCentre().x > x_sup) continue; if (pTestVehicle->GetBoundCentre().y < y_inf || pTestVehicle->GetBoundCentre().y > y_sup) continue; if (Abs(pTestVehicle->GetPosition().z - pVehicle->GetPosition().z) >= VEHICLE_HEIGHT_DIFF_TO_CONSIDER_WEAVING) continue; if (pTestVehicle != pVehicle) WeaveForOtherCar(pTestVehicle, pVehicle, pAngleToWeaveLeft, pAngleToWeaveRight); } } void CCarCtrl::WeaveForOtherCar(CEntity* pOtherEntity, CVehicle* pVehicle, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) { CVehicle* pOtherCar = (CVehicle*)pOtherEntity; if (pVehicle->bPartOfConvoy && pOtherCar->bPartOfConvoy) return; if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE && pOtherEntity == FindPlayerVehicle()) return; if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMCAR_CLOSE && pOtherEntity == pVehicle->AutoPilot.m_pTargetCar) return; CVector2D vecDiff = pOtherCar->GetPosition() - pVehicle->GetPosition(); float angleBetweenVehicles = CGeneral::GetATanOfXY(vecDiff.x, vecDiff.y); float distance = vecDiff.Magnitude(); if (distance < 1.0f) return; if (DotProduct2D(pVehicle->GetMoveSpeed() - pOtherCar->GetMoveSpeed(), vecDiff) * 110.0f - pOtherCar->GetColModel()->boundingSphere.radius - pVehicle->GetColModel()->boundingSphere.radius < distance) return; CVector2D forward = pVehicle->GetForward(); forward.Normalise(); float forwardAngle = CGeneral::GetATanOfXY(forward.x, forward.y); float angleDiff = angleBetweenVehicles - forwardAngle; float lenProjection = ABS(pOtherCar->GetColModel()->boundingBox.max.y * sin(angleDiff)); float widthProjection = ABS(pOtherCar->GetColModel()->boundingBox.max.x * cos(angleDiff)); float lengthToEvade = (2 * (lenProjection + widthProjection) + WIDTH_COEF_TO_WEAVE_SAFELY * 2 * pVehicle->GetColModel()->boundingBox.max.x) / distance; float diffToLeftAngle = LimitRadianAngle(angleBetweenVehicles - *pAngleToWeaveLeft); diffToLeftAngle = ABS(diffToLeftAngle); float angleToWeave = lengthToEvade / 2; if (diffToLeftAngle < angleToWeave){ *pAngleToWeaveLeft = angleBetweenVehicles - angleToWeave; while (*pAngleToWeaveLeft < -PI) *pAngleToWeaveLeft += TWOPI; } float diffToRightAngle = LimitRadianAngle(angleBetweenVehicles - *pAngleToWeaveRight); diffToRightAngle = ABS(diffToRightAngle); if (diffToRightAngle < angleToWeave){ *pAngleToWeaveRight = angleBetweenVehicles + angleToWeave; while (*pAngleToWeaveRight > PI) *pAngleToWeaveRight -= TWOPI; } } void CCarCtrl::WeaveThroughPedsSectorList(CPtrList& lst, CVehicle* pVehicle, CPhysical* pTarget, float x_inf, float y_inf, float x_sup, float y_sup, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) { for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next) { CPed* pPed = (CPed*)pNode->item; if (pPed->m_scanCode == CWorld::GetCurrentScanCode()) continue; if (!pPed->bUsesCollision) continue; if (pPed == pTarget) continue; pPed->m_scanCode = CWorld::GetCurrentScanCode(); if (pPed->GetPosition().x < x_inf || pPed->GetPosition().x > x_sup) continue; if (pPed->GetPosition().y < y_inf || pPed->GetPosition().y > y_sup) continue; if (Abs(pPed->GetPosition().z - pVehicle->GetPosition().z) >= PED_HEIGHT_DIFF_TO_CONSIDER_WEAVING) continue; if (pPed->m_pCurSurface != pVehicle && pPed->m_attachedTo != pVehicle) WeaveForPed(pPed, pVehicle, pAngleToWeaveLeft, pAngleToWeaveRight); } } void CCarCtrl::WeaveForPed(CEntity* pOtherEntity, CVehicle* pVehicle, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) { if (pVehicle->AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE && pOtherEntity == FindPlayerPed()) return; CPed* pPed = (CPed*)pOtherEntity; CVector2D vecDiff = pPed->GetPosition() - pVehicle->GetPosition(); float angleBetweenVehicleAndPed = CGeneral::GetATanOfXY(vecDiff.x, vecDiff.y); float distance = vecDiff.Magnitude(); float lengthToEvade = (WIDTH_COEF_TO_WEAVE_SAFELY * 2 * pVehicle->GetColModel()->boundingBox.max.x + PED_WIDTH_TO_WEAVE) / distance; float diffToLeftAngle = LimitRadianAngle(angleBetweenVehicleAndPed - *pAngleToWeaveLeft); diffToLeftAngle = ABS(diffToLeftAngle); float angleToWeave = lengthToEvade / 2; if (diffToLeftAngle < angleToWeave) { *pAngleToWeaveLeft = angleBetweenVehicleAndPed - angleToWeave; while (*pAngleToWeaveLeft < -PI) *pAngleToWeaveLeft += TWOPI; } float diffToRightAngle = LimitRadianAngle(angleBetweenVehicleAndPed - *pAngleToWeaveRight); diffToRightAngle = ABS(diffToRightAngle); if (diffToRightAngle < angleToWeave) { *pAngleToWeaveRight = angleBetweenVehicleAndPed + angleToWeave; while (*pAngleToWeaveRight > PI) *pAngleToWeaveRight -= TWOPI; } } void CCarCtrl::WeaveThroughObjectsSectorList(CPtrList& lst, CVehicle* pVehicle, float x_inf, float y_inf, float x_sup, float y_sup, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) { for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next) { CObject* pObject = (CObject*)pNode->item; if (pObject->m_scanCode == CWorld::GetCurrentScanCode()) continue; if (!pObject->bUsesCollision) continue; pObject->m_scanCode = CWorld::GetCurrentScanCode(); if (pObject->GetPosition().x < x_inf || pObject->GetPosition().x > x_sup) continue; if (pObject->GetPosition().y < y_inf || pObject->GetPosition().y > y_sup) continue; if (Abs(pObject->GetPosition().z - pVehicle->GetPosition().z) >= OBJECT_HEIGHT_DIFF_TO_CONSIDER_WEAVING) continue; if (pObject->GetUp().z > 0.9f) WeaveForObject(pObject, pVehicle, pAngleToWeaveLeft, pAngleToWeaveRight); } } void CCarCtrl::WeaveForObject(CEntity* pOtherEntity, CVehicle* pVehicle, float* pAngleToWeaveLeft, float* pAngleToWeaveRight) { float rightCoef; float forwardCoef; if (pOtherEntity->GetModelIndex() == MI_TRAFFICLIGHTS){ rightCoef = 2.957f; forwardCoef = 0.147f; }else if (pOtherEntity->GetModelIndex() == MI_SINGLESTREETLIGHTS1){ rightCoef = 0.744f; forwardCoef = 0.0f; }else if (pOtherEntity->GetModelIndex() == MI_SINGLESTREETLIGHTS2){ rightCoef = 0.043f; forwardCoef = 0.0f; }else if (pOtherEntity->GetModelIndex() == MI_SINGLESTREETLIGHTS3){ rightCoef = 1.143f; forwardCoef = 0.145f; }else if (pOtherEntity->GetModelIndex() == MI_DOUBLESTREETLIGHTS){ rightCoef = 0.0f; forwardCoef = -0.048f; }else if (IsTreeModel(pOtherEntity->GetModelIndex())){ rightCoef = 0.0f; forwardCoef = 0.0f; }else if (pOtherEntity->GetModelIndex() == MI_STREETLAMP1 || pOtherEntity->GetModelIndex() == MI_STREETLAMP2){ rightCoef = 0.0f; forwardCoef = 0.0f; }else return; CObject* pObject = (CObject*)pOtherEntity; CVector2D vecDiff = pObject->GetPosition() + rightCoef * pObject->GetRight() + forwardCoef * pObject->GetForward() - pVehicle->GetPosition(); float angleBetweenVehicleAndObject = CGeneral::GetATanOfXY(vecDiff.x, vecDiff.y); float distance = vecDiff.Magnitude(); float lengthToEvade = (WIDTH_COEF_TO_WEAVE_SAFELY * 2 * pVehicle->GetColModel()->boundingBox.max.x + OBJECT_WIDTH_TO_WEAVE) / distance; float diffToLeftAngle = LimitRadianAngle(angleBetweenVehicleAndObject - *pAngleToWeaveLeft); diffToLeftAngle = ABS(diffToLeftAngle); float angleToWeave = lengthToEvade / 2; if (diffToLeftAngle < angleToWeave) { *pAngleToWeaveLeft = angleBetweenVehicleAndObject - angleToWeave; while (*pAngleToWeaveLeft < -PI) *pAngleToWeaveLeft += TWOPI; } float diffToRightAngle = LimitRadianAngle(angleBetweenVehicleAndObject - *pAngleToWeaveRight); diffToRightAngle = ABS(diffToRightAngle); if (diffToRightAngle < angleToWeave) { *pAngleToWeaveRight = angleBetweenVehicleAndObject + angleToWeave; while (*pAngleToWeaveRight > PI) *pAngleToWeaveRight -= TWOPI; } } bool CCarCtrl::PickNextNodeAccordingStrategy(CVehicle* pVehicle) { pVehicle->AutoPilot.m_nCruiseSpeedMultiplierType = ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode].speedLimit; switch (pVehicle->AutoPilot.m_nCarMission){ case MISSION_RAMPLAYER_FARAWAY: case MISSION_BLOCKPLAYER_FARAWAY: PickNextNodeToChaseCar(pVehicle, FindPlayerCoors().x, FindPlayerCoors().y, #ifdef FIX_PATHFIND_BUG FindPlayerCoors().z, #endif FindPlayerVehicle()); return false; case MISSION_GOTOCOORDS: case MISSION_GOTOCOORDS_ACCURATE: return PickNextNodeToFollowPath(pVehicle); case MISSION_RAMCAR_FARAWAY: case MISSION_BLOCKCAR_FARAWAY: PickNextNodeToChaseCar(pVehicle, pVehicle->AutoPilot.m_pTargetCar->GetPosition().x, pVehicle->AutoPilot.m_pTargetCar->GetPosition().y, #ifdef FIX_PATHFIND_BUG pVehicle->AutoPilot.m_pTargetCar->GetPosition().z, #endif pVehicle->AutoPilot.m_pTargetCar); return false; default: PickNextNodeRandomly(pVehicle); if (ThePaths.GetNode(pVehicle->AutoPilot.m_nNextRouteNode)->bOnlySmallBoats && BoatWithTallMast(pVehicle->GetModelIndex())) pVehicle->AutoPilot.m_nCruiseSpeed = 0; return false; } } void CCarCtrl::PickNextNodeRandomly(CVehicle* pVehicle) { if (pVehicle->m_nRouteSeed) CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed); int32 prevNode = pVehicle->AutoPilot.m_nCurrentRouteNode; int32 curNode = pVehicle->AutoPilot.m_nNextRouteNode; uint8 totalLinks = ThePaths.m_pathNodes[curNode].numLinks; CCarPathLink* pCurLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; uint8 lanesOnCurrentPath; bool isOnOneWayRoad; if (pCurLink->pathNodeIndex == curNode) { lanesOnCurrentPath = pCurLink->numLeftLanes; isOnOneWayRoad = pCurLink->numRightLanes == 0; } else { lanesOnCurrentPath = pCurLink->numRightLanes; isOnOneWayRoad = pCurLink->numLeftLanes == 0; } uint8 allowedDirections = PATH_DIRECTION_NONE; uint8 nextLane = pVehicle->AutoPilot.m_nNextLane; if (nextLane == 0) /* We are always allowed to turn left from leftmost lane */ allowedDirections |= PATH_DIRECTION_LEFT; if (nextLane == lanesOnCurrentPath - 1) /* We are always allowed to turn right from rightmost lane */ allowedDirections |= PATH_DIRECTION_RIGHT; if (lanesOnCurrentPath < 3 || allowedDirections == PATH_DIRECTION_NONE) /* We are always allowed to go straight on one/two-laned road */ /* or if we are in one of middle lanes of the road */ allowedDirections |= PATH_DIRECTION_STRAIGHT; int attempt; pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode; pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode; CPathNode* pPrevPathNode = &ThePaths.m_pathNodes[prevNode]; CPathNode* pCurPathNode = &ThePaths.m_pathNodes[curNode]; int16 nextLink; CCarPathLink* pNextLink; CPathNode* pNextPathNode; bool goingAgainstOneWayRoad; bool nextNodeIsOneWayRoad; uint8 direction; for(attempt = 0; attempt < ATTEMPTS_TO_FIND_NEXT_NODE; attempt++){ if (attempt != 0){ if (pVehicle->AutoPilot.m_nNextRouteNode != prevNode){ if (direction & allowedDirections){ pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; if ((!pNextPathNode->bDeadEnd || pPrevPathNode->bDeadEnd) && (!pNextPathNode->bDisabled || pPrevPathNode->bDisabled) && (!pNextPathNode->bBetweenLevels || pPrevPathNode->bBetweenLevels || !pVehicle->AutoPilot.m_bStayInCurrentLevel) && !goingAgainstOneWayRoad && (!isOnOneWayRoad || !nextNodeIsOneWayRoad)) break; } } } nextLink = CGeneral::GetRandomNumber() % totalLinks; pVehicle->AutoPilot.m_nNextRouteNode = ThePaths.ConnectedNode(nextLink + pCurPathNode->firstLink); direction = FindPathDirection(prevNode, curNode, pVehicle->AutoPilot.m_nNextRouteNode); pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]]; goingAgainstOneWayRoad = pNextLink->pathNodeIndex == curNode ? pNextLink->numRightLanes == 0 : pNextLink->numLeftLanes == 0; nextNodeIsOneWayRoad = pNextLink->pathNodeIndex == curNode ? pNextLink->numLeftLanes == 0 : pNextLink->numRightLanes == 0; } if (attempt >= ATTEMPTS_TO_FIND_NEXT_NODE) { /* If we failed 15 times, then remove dead end, one way road and current lane limitations */ for (attempt = 0; attempt < ATTEMPTS_TO_FIND_NEXT_NODE; attempt++) { if (attempt != 0) { if (pVehicle->AutoPilot.m_nNextRouteNode != prevNode) { pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; if ((!pNextPathNode->bDisabled || pPrevPathNode->bDisabled) && (!pNextPathNode->bBetweenLevels || pPrevPathNode->bBetweenLevels || !pVehicle->AutoPilot.m_bStayInCurrentLevel) && !goingAgainstOneWayRoad) break; } } nextLink = CGeneral::GetRandomNumber() % totalLinks; pVehicle->AutoPilot.m_nNextRouteNode = ThePaths.ConnectedNode(nextLink + pCurPathNode->firstLink); pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]]; goingAgainstOneWayRoad = pNextLink->pathNodeIndex == curNode ? pNextLink->numRightLanes == 0 : pNextLink->numLeftLanes == 0; } } if (attempt >= ATTEMPTS_TO_FIND_NEXT_NODE) { /* If we failed again, remove no U-turn limitation and remove randomness */ for (nextLink = 0; nextLink < totalLinks; nextLink++) { pVehicle->AutoPilot.m_nNextRouteNode = ThePaths.ConnectedNode(nextLink + pCurPathNode->firstLink); pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]]; goingAgainstOneWayRoad = pNextLink->pathNodeIndex == curNode ? pNextLink->numRightLanes == 0 : pNextLink->numLeftLanes == 0; if (!goingAgainstOneWayRoad) { pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; if ((!pNextPathNode->bDisabled || pPrevPathNode->bDisabled) && (!pNextPathNode->bBetweenLevels || pPrevPathNode->bBetweenLevels || !pVehicle->AutoPilot.m_bStayInCurrentLevel)) /* Nice way to exit loop but this will fail because this is used for indexing! */ nextLink = 1000; } } if (nextLink < 999) /* If everything else failed, turn vehicle around */ pVehicle->AutoPilot.m_nNextRouteNode = prevNode; } pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]]; if (prevNode == pVehicle->AutoPilot.m_nNextRouteNode){ /* We can no longer shift vehicle without physics if we have to turn it around. */ pVehicle->SetStatus(STATUS_PHYSICS); SwitchVehicleToRealPhysics(pVehicle); } pVehicle->AutoPilot.m_nTimeEnteredCurve += pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nCurrentPathNodeInfo; pVehicle->AutoPilot.m_nCurrentPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo; pVehicle->AutoPilot.m_nPreviousDirection = pVehicle->AutoPilot.m_nCurrentDirection; pVehicle->AutoPilot.m_nCurrentDirection = pVehicle->AutoPilot.m_nNextDirection; pVehicle->AutoPilot.m_nCurrentLane = pVehicle->AutoPilot.m_nNextLane; pVehicle->AutoPilot.m_nNextPathNodeInfo = ThePaths.m_carPathConnections[nextLink + pCurPathNode->firstLink]; int8 lanesOnNextNode; if (curNode >= pVehicle->AutoPilot.m_nNextRouteNode){ pVehicle->AutoPilot.m_nNextDirection = 1; lanesOnNextNode = pNextLink->numLeftLanes; }else{ pVehicle->AutoPilot.m_nNextDirection = -1; lanesOnNextNode = pNextLink->numRightLanes; } float currentPathLinkForwardX = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirX(); float nextPathLinkForwardX = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirX(); #ifdef FIX_BUGS float currentPathLinkForwardY = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirY(); float nextPathLinkForwardY = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirY(); #endif if (lanesOnNextNode >= 0){ if ((CGeneral::GetRandomNumber() & 0x600) == 0){ /* 25% chance vehicle will try to switch lane */ CVector2D dist = pNextPathNode->GetPosition() - pCurPathNode->GetPosition(); if (dist.MagnitudeSqr() >= SQR(14.0f)){ if (CGeneral::GetRandomTrueFalse()) pVehicle->AutoPilot.m_nNextLane += 1; else pVehicle->AutoPilot.m_nNextLane -= 1; } } pVehicle->AutoPilot.m_nNextLane = Min(lanesOnNextNode - 1, pVehicle->AutoPilot.m_nNextLane); pVehicle->AutoPilot.m_nNextLane = Max(0, pVehicle->AutoPilot.m_nNextLane); }else{ pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane; } if (pVehicle->AutoPilot.m_bStayInFastLane) pVehicle->AutoPilot.m_nNextLane = 0; CVector positionOnCurrentLinkIncludingLane( pCurLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) #ifdef FIX_BUGS * currentPathLinkForwardY #endif ,pCurLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, 0.0f); CVector positionOnNextLinkIncludingLane( pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) #ifdef FIX_BUGS * nextPathLinkForwardY #endif ,pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, 0.0f); float directionCurrentLinkX = pCurLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; float directionCurrentLinkY = pCurLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; float directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; float directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; /* We want to make a path between two links that may not have the same forward directions a curve. */ pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( &positionOnCurrentLinkIncludingLane, &positionOnNextLinkIncludingLane, directionCurrentLinkX, directionCurrentLinkY, directionNextLinkX, directionNextLinkY ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); if (pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve < 10) /* Oh hey there Obbe */ printf("fout\n"); pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = Max(10, pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); } uint8 CCarCtrl::FindPathDirection(int32 prevNode, int32 curNode, int32 nextNode) { CVector2D prevToCur = ThePaths.m_pathNodes[curNode].GetPosition() - ThePaths.m_pathNodes[prevNode].GetPosition(); CVector2D curToNext = ThePaths.m_pathNodes[nextNode].GetPosition() - ThePaths.m_pathNodes[curNode].GetPosition(); float distPrevToCur = prevToCur.Magnitude(); if (distPrevToCur == 0.0f) return PATH_DIRECTION_NONE; /* We are trying to determine angle between prevToCur and curToNext. */ /* To find it, we consider a to be an angle between y axis and prevToCur */ /* and b to be an angle between x axis and curToNext */ /* Then the angle we are looking for is (pi/2 + a + b). */ float sin_a = prevToCur.x / distPrevToCur; float cos_a = prevToCur.y / distPrevToCur; float distCurToNext = curToNext.Magnitude(); if (distCurToNext == 0.0f) return PATH_DIRECTION_NONE; float sin_b = curToNext.y / distCurToNext; float cos_b = curToNext.x / distCurToNext; /* sin(a) * sin(b) - cos(a) * cos(b) = -cos(a+b) = sin(pi/2+a+b) */ float sin_direction = sin_a * sin_b - cos_a * cos_b; if (sin_direction > 0.77f) /* Roughly between -50 and -130 degrees */ return PATH_DIRECTION_LEFT; if (sin_direction < -0.77f) /* Roughly between 50 and 130 degrees */ return PATH_DIRECTION_RIGHT; return PATH_DIRECTION_STRAIGHT; } #ifdef FIX_PATHFIND_BUG void CCarCtrl::PickNextNodeToChaseCar(CVehicle* pVehicle, float targetX, float targetY, float targetZ, CVehicle* pTarget) #else void CCarCtrl::PickNextNodeToChaseCar(CVehicle* pVehicle, float targetX, float targetY, CVehicle* pTarget) #endif { if (pVehicle->m_nRouteSeed) CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed); int prevNode = pVehicle->AutoPilot.m_nCurrentRouteNode; int curNode = pVehicle->AutoPilot.m_nNextRouteNode; CPathNode* pPrevNode = &ThePaths.m_pathNodes[prevNode]; CPathNode* pCurNode = &ThePaths.m_pathNodes[curNode]; CPathNode* pTargetNode[2]; int16 numNodes; float distanceToTargetNode; ThePaths.DoPathSearch(0, pCurNode->GetPosition(), curNode, #ifdef FIX_PATHFIND_BUG CVector(targetX, targetY, targetZ), #else CVector(targetX, targetY, 0.0f), #endif pTargetNode, &numNodes, 2, pVehicle, &distanceToTargetNode, 999999.9f, -1); int newNextNode; int nextLink; if (numNodes != 1 && numNodes != 2 || pTargetNode[0] == pCurNode){ if (numNodes != 2 || pTargetNode[1] == pCurNode) { float currentAngle = CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y); nextLink = 0; float lowestAngleChange = 10.0f; int numLinks = pCurNode->numLinks; newNextNode = 0; for (int i = 0; i < numLinks; i++) { int conNode = ThePaths.ConnectedNode(i + pCurNode->firstLink); if (conNode == prevNode && i > 1) continue; CPathNode* pTestNode = &ThePaths.m_pathNodes[conNode]; float angle = CGeneral::GetATanOfXY(pTestNode->GetX() - pCurNode->GetX(), pTestNode->GetY() - pCurNode->GetY()); angle = LimitRadianAngle(angle - currentAngle); angle = ABS(angle); if (angle < lowestAngleChange) { lowestAngleChange = angle; newNextNode = conNode; nextLink = i; } } } else { nextLink = 0; newNextNode = pTargetNode[1] - ThePaths.m_pathNodes; for (int i = pCurNode->firstLink; ThePaths.ConnectedNode(i) != newNextNode; i++, nextLink++) ; } } else { nextLink = 0; newNextNode = pTargetNode[0] - ThePaths.m_pathNodes; for (int i = pCurNode->firstLink; ThePaths.ConnectedNode(i) != newNextNode; i++, nextLink++) ; } CPathNode* pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]]; CCarPathLink* pCurLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode; pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode; pVehicle->AutoPilot.m_nNextRouteNode = newNextNode; pVehicle->AutoPilot.m_nTimeEnteredCurve += pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nCurrentPathNodeInfo; pVehicle->AutoPilot.m_nCurrentPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo; pVehicle->AutoPilot.m_nPreviousDirection = pVehicle->AutoPilot.m_nCurrentDirection; pVehicle->AutoPilot.m_nCurrentDirection = pVehicle->AutoPilot.m_nNextDirection; pVehicle->AutoPilot.m_nCurrentLane = pVehicle->AutoPilot.m_nNextLane; pVehicle->AutoPilot.m_nNextPathNodeInfo = ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]; int8 lanesOnNextNode; if (curNode >= pVehicle->AutoPilot.m_nNextRouteNode) { pVehicle->AutoPilot.m_nNextDirection = 1; lanesOnNextNode = pNextLink->numRightLanes; } else { pVehicle->AutoPilot.m_nNextDirection = -1; lanesOnNextNode = pNextLink->numLeftLanes; } float currentPathLinkForwardX = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirX(); float currentPathLinkForwardY = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirY(); float nextPathLinkForwardX = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirX(); float nextPathLinkForwardY = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirY(); if (lanesOnNextNode >= 0) { CVector2D dist = pNextPathNode->GetPosition() - pCurNode->GetPosition(); if (dist.MagnitudeSqr() >= SQR(7.0f)){ /* 25% chance vehicle will try to switch lane */ /* No lane switching if following car from far away */ /* ...although it's always one of those. */ if ((CGeneral::GetRandomNumber() & 0x600) == 0 && pVehicle->AutoPilot.m_nCarMission != MISSION_RAMPLAYER_FARAWAY && pVehicle->AutoPilot.m_nCarMission != MISSION_BLOCKPLAYER_FARAWAY && pVehicle->AutoPilot.m_nCarMission != MISSION_RAMCAR_FARAWAY && pVehicle->AutoPilot.m_nCarMission != MISSION_BLOCKCAR_FARAWAY){ if (CGeneral::GetRandomTrueFalse()) pVehicle->AutoPilot.m_nNextLane += 1; else pVehicle->AutoPilot.m_nNextLane -= 1; } } pVehicle->AutoPilot.m_nNextLane = Min(lanesOnNextNode - 1, pVehicle->AutoPilot.m_nNextLane); pVehicle->AutoPilot.m_nNextLane = Max(0, pVehicle->AutoPilot.m_nNextLane); } else { pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane; } if (pVehicle->AutoPilot.m_bStayInFastLane) pVehicle->AutoPilot.m_nNextLane = 0; CVector positionOnCurrentLinkIncludingLane( pCurLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, pCurLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, 0.0f); CVector positionOnNextLinkIncludingLane( pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, 0.0f); float directionCurrentLinkX = pCurLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; float directionCurrentLinkY = pCurLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; float directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; float directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; /* We want to make a path between two links that may not have the same forward directions a curve. */ pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( &positionOnCurrentLinkIncludingLane, &positionOnNextLinkIncludingLane, directionCurrentLinkX, directionCurrentLinkY, directionNextLinkX, directionNextLinkY ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = Max(10, pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); } bool CCarCtrl::PickNextNodeToFollowPath(CVehicle* pVehicle) { if (pVehicle->m_nRouteSeed) CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed); int curNode = pVehicle->AutoPilot.m_nNextRouteNode; CPathNode* pCurNode = &ThePaths.m_pathNodes[curNode]; if (pVehicle->AutoPilot.m_nPathFindNodesCount == 0){ ThePaths.DoPathSearch(0, pVehicle->GetPosition(), curNode, pVehicle->AutoPilot.m_vecDestinationCoors, pVehicle->AutoPilot.m_aPathFindNodesInfo, &pVehicle->AutoPilot.m_nPathFindNodesCount, NUM_PATH_NODES_IN_AUTOPILOT, pVehicle, nil, 999999.9f, -1); if (pVehicle->AutoPilot.m_nPathFindNodesCount < 2) return true; pVehicle->AutoPilot.RemoveOnePathNode(); } CPathNode* pNextPathNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nNextRouteNode]; CCarPathLink* pCurLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode; pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode; pVehicle->AutoPilot.m_nNextRouteNode = pVehicle->AutoPilot.m_aPathFindNodesInfo[0] - ThePaths.m_pathNodes; pVehicle->AutoPilot.RemoveOnePathNode(); pVehicle->AutoPilot.m_nTimeEnteredCurve += pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nCurrentPathNodeInfo; pVehicle->AutoPilot.m_nCurrentPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo; pVehicle->AutoPilot.m_nPreviousDirection = pVehicle->AutoPilot.m_nCurrentDirection; pVehicle->AutoPilot.m_nCurrentDirection = pVehicle->AutoPilot.m_nNextDirection; pVehicle->AutoPilot.m_nCurrentLane = pVehicle->AutoPilot.m_nNextLane; int nextLink = 0; for (int i = pCurNode->firstLink; ThePaths.ConnectedNode(i) != pVehicle->AutoPilot.m_nNextRouteNode; i++, nextLink++) ; CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]]; pVehicle->AutoPilot.m_nNextPathNodeInfo = ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]; int8 lanesOnNextNode; if (curNode >= pVehicle->AutoPilot.m_nNextRouteNode) { pVehicle->AutoPilot.m_nNextDirection = 1; lanesOnNextNode = pNextLink->numLeftLanes; } else { pVehicle->AutoPilot.m_nNextDirection = -1; lanesOnNextNode = pNextLink->numRightLanes; } float currentPathLinkForwardX = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirX(); float currentPathLinkForwardY = pVehicle->AutoPilot.m_nCurrentDirection * pCurLink->GetDirY(); float nextPathLinkForwardX = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirX(); float nextPathLinkForwardY = pVehicle->AutoPilot.m_nNextDirection * pNextLink->GetDirY(); if (lanesOnNextNode >= 0) { CVector2D dist = pNextPathNode->GetPosition() - pCurNode->GetPosition(); if (dist.MagnitudeSqr() >= SQR(7.0f) && (CGeneral::GetRandomNumber() & 0x600) == 0) { if (CGeneral::GetRandomTrueFalse()) pVehicle->AutoPilot.m_nNextLane += 1; else pVehicle->AutoPilot.m_nNextLane -= 1; } pVehicle->AutoPilot.m_nNextLane = Min(lanesOnNextNode - 1, pVehicle->AutoPilot.m_nNextLane); pVehicle->AutoPilot.m_nNextLane = Max(0, pVehicle->AutoPilot.m_nNextLane); } else { pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane; } if (pVehicle->AutoPilot.m_bStayInFastLane) pVehicle->AutoPilot.m_nNextLane = 0; CVector positionOnCurrentLinkIncludingLane( pCurLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardY, pCurLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForwardX, 0.0f); CVector positionOnNextLinkIncludingLane( pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX, 0.0f); float directionCurrentLinkX = pCurLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; float directionCurrentLinkY = pCurLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; float directionNextLinkX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; float directionNextLinkY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; /* We want to make a path between two links that may not have the same forward directions a curve. */ pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = CCurves::CalcSpeedScaleFactor( &positionOnCurrentLinkIncludingLane, &positionOnNextLinkIncludingLane, directionCurrentLinkX, directionCurrentLinkY, directionNextLinkX, directionNextLinkY ) * (1000.0f / pVehicle->AutoPilot.m_fMaxTrafficSpeed); pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve = Max(10, pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve); return false; } void CCarCtrl::Init(void) { NumRandomCars = 0; NumLawEnforcerCars = 0; NumMissionCars = 0; NumParkedCars = 0; NumPermanentCars = 0; NumAmbulancesOnDuty = 0; NumFiretrucksOnDuty = 0; LastTimeFireTruckCreated = 0; LastTimeAmbulanceCreated = 0; #ifdef FIX_BUGS LastTimeLawEnforcerCreated = 0; LastTimeMiamiViceGenerated = 0; #endif bCarsGeneratedAroundCamera = false; CountDownToCarsAtStart = 2; CarDensityMultiplier = 1.0f; for (int i = 0; i < MAX_CARS_TO_KEEP; i++) apCarsToKeep[i] = nil; for (int i = 0; i < TOTAL_CUSTOM_CLASSES; i++){ for (int j = 0; j < MAX_CAR_MODELS_IN_ARRAY; j++) { LoadedCarsArray[i][j] = -1; } NumOfLoadedCarsOfRating[i] = 0; NumRequestsOfCarRating[i] = 0; TotalNumOfCarsOfRating[i] = 0; } } void CCarCtrl::ReInit(void) { NumRandomCars = 0; NumLawEnforcerCars = 0; NumMissionCars = 0; NumParkedCars = 0; NumPermanentCars = 0; NumAmbulancesOnDuty = 0; NumFiretrucksOnDuty = 0; #ifdef FIX_BUGS LastTimeFireTruckCreated = 0; LastTimeAmbulanceCreated = 0; LastTimeLawEnforcerCreated = 0; LastTimeMiamiViceGenerated = 0; #endif CountDownToCarsAtStart = 2; CarDensityMultiplier = 1.0f; for (int i = 0; i < MAX_CARS_TO_KEEP; i++) apCarsToKeep[i] = nil; for (int i = 0; i < TOTAL_CUSTOM_CLASSES; i++) NumRequestsOfCarRating[i] = 0; } void CCarCtrl::DragCarToPoint(CVehicle* pVehicle, CVector* pPoint) { CVector2D posBehind = (CVector2D)pVehicle->GetPosition() - 3 * pVehicle->GetForward() / 2; CVector2D posTarget = *pPoint; CVector2D direction = posBehind - posTarget; CVector2D midPos = posTarget + direction * 3 / direction.Magnitude(); float actualAheadZ; float actualBehindZ; CColPoint point; CEntity* pRoadObject; if (CCollision::IsStoredPolyStillValidVerticalLine(CVector(posTarget.x, posTarget.y, pVehicle->GetPosition().z - 3.0f), pVehicle->GetPosition().z - 3.0f, point, &pVehicle->m_aCollPolys[0])){ actualAheadZ = point.point.z; }else if (CWorld::ProcessVerticalLine(CVector(posTarget.x, posTarget.y, pVehicle->GetPosition().z + 1.5f), pVehicle->GetPosition().z - 2.0f, point, pRoadObject, true, false, false, false, false, false, &pVehicle->m_aCollPolys[0])){ actualAheadZ = point.point.z; pVehicle->m_pCurGroundEntity = pRoadObject; if (ThisRoadObjectCouldMove(pRoadObject->GetModelIndex())) pVehicle->m_aCollPolys[0].valid = false; }else if (CWorld::ProcessVerticalLine(CVector(posTarget.x, posTarget.y, pVehicle->GetPosition().z + 3.0f), pVehicle->GetPosition().z - 3.0f, point, pRoadObject, true, false, false, false, false, false, &pVehicle->m_aCollPolys[0])) { actualAheadZ = point.point.z; pVehicle->m_pCurGroundEntity = pRoadObject; if (ThisRoadObjectCouldMove(pRoadObject->GetModelIndex())) pVehicle->m_aCollPolys[0].valid = false; }else{ actualAheadZ = pVehicle->m_fMapObjectHeightAhead; } pVehicle->m_fMapObjectHeightAhead = actualAheadZ; if (CCollision::IsStoredPolyStillValidVerticalLine(CVector(midPos.x, midPos.y, pVehicle->GetPosition().z - 3.0f), pVehicle->GetPosition().z - 3.0f, point, &pVehicle->m_aCollPolys[1])){ actualBehindZ = point.point.z; }else if (CWorld::ProcessVerticalLine(CVector(midPos.x, midPos.y, pVehicle->GetPosition().z + 1.5f), pVehicle->GetPosition().z - 2.0f, point, pRoadObject, true, false, false, false, false, false, &pVehicle->m_aCollPolys[1])){ actualBehindZ = point.point.z; pVehicle->m_pCurGroundEntity = pRoadObject; if (ThisRoadObjectCouldMove(pRoadObject->GetModelIndex())) pVehicle->m_aCollPolys[1].valid = false; }else if (CWorld::ProcessVerticalLine(CVector(midPos.x, midPos.y, pVehicle->GetPosition().z + 3.0f), pVehicle->GetPosition().z - 3.0f, point, pRoadObject, true, false, false, false, false, false, &pVehicle->m_aCollPolys[1])){ actualBehindZ = point.point.z; pVehicle->m_pCurGroundEntity = pRoadObject; if (ThisRoadObjectCouldMove(pRoadObject->GetModelIndex())) pVehicle->m_aCollPolys[1].valid = false; }else{ actualBehindZ = pVehicle->m_fMapObjectHeightBehind; } pVehicle->m_fMapObjectHeightBehind = actualBehindZ; float angleZ = Atan2((actualAheadZ - actualBehindZ) / 3, 1.0f); float cosZ = Cos(angleZ); float sinZ = Sin(angleZ); pVehicle->GetRight() = CVector(posTarget.y - midPos.y, -(posTarget.x - midPos.x), 0.0f) / 3; pVehicle->GetForward() = CVector(-cosZ * pVehicle->GetRight().y, cosZ * pVehicle->GetRight().x, sinZ); pVehicle->GetUp() = CrossProduct(pVehicle->GetRight(), pVehicle->GetForward()); pVehicle->SetPosition((CVector(midPos.x, midPos.y, actualBehindZ) + CVector(posTarget.x, posTarget.y, actualAheadZ)) / 2); pVehicle->GetMatrix().GetPosition().z += pVehicle->GetHeightAboveRoad(); } float CCarCtrl::FindSpeedMultiplier(float angleChange, float minAngle, float maxAngle, float coef) { float angle = Abs(LimitRadianAngle(angleChange)); float n = angle - minAngle; n = Max(0.0f, n); float d = maxAngle - minAngle; float mult = 1.0f - n / d * (1.0f - coef); if (n > d) return coef; return mult; } void CCarCtrl::SteerAICarWithPhysics(CVehicle* pVehicle) { float swerve; float accel; float brake; bool handbrake; switch (pVehicle->AutoPilot.m_nTempAction){ case TEMPACT_WAIT: swerve = 0.0f; accel = 0.0f; brake = 0.2f; handbrake = false; if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction){ pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds(); } break; case TEMPACT_REVERSE: SteerAICarWithPhysics_OnlyMission(pVehicle, &swerve, &accel, &brake, &handbrake); handbrake = false; swerve = -swerve; if (DotProduct(pVehicle->GetMoveSpeed(), pVehicle->GetForward()) > 0.04f){ accel = 0.0f; brake = 0.5f; }else{ accel = -0.5f; brake = 0.0f; } if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; break; case TEMPACT_HANDBRAKETURNLEFT: swerve = 1.0f; accel = 0.0f; brake = 0.0f; handbrake = true; if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; break; case TEMPACT_HANDBRAKETURNRIGHT: swerve = -1.0f; accel = 0.0f; brake = 0.0f; handbrake = true; if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; break; case TEMPACT_HANDBRAKESTRAIGHT: swerve = 0.0f; accel = 0.0f; brake = 0.0f; handbrake = true; if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; break; case TEMPACT_TURNLEFT: swerve = 1.0f; accel = 1.0f; brake = 0.0f; handbrake = false; if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; break; case TEMPACT_TURNRIGHT: swerve = -1.0f; accel = 1.0f; brake = 0.0f; handbrake = false; if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; break; case TEMPACT_GOFORWARD: swerve = 0.0f; accel = 0.5f; brake = 0.0f; handbrake = false; if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; break; case TEMPACT_SWERVELEFT: case TEMPACT_SWERVERIGHT: swerve = (pVehicle->AutoPilot.m_nTempAction == TEMPACT_SWERVERIGHT) ? 0.15f : -0.15f; accel = 0.0f; brake = 0.001f; handbrake = false; if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction - 1000) swerve = -swerve; if (CTimer::GetTimeInMilliseconds() > pVehicle->AutoPilot.m_nTimeTempAction) pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; break; default: SteerAICarWithPhysics_OnlyMission(pVehicle, &swerve, &accel, &brake, &handbrake); break; } pVehicle->m_fSteerAngle = swerve; pVehicle->bIsHandbrakeOn = handbrake; pVehicle->m_fGasPedal = accel; pVehicle->m_fBrakePedal = brake; } void CCarCtrl::SteerAICarWithPhysics_OnlyMission(CVehicle* pVehicle, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) { switch (pVehicle->AutoPilot.m_nCarMission) { case MISSION_NONE: *pSwerve = 0.0f; *pAccel = 0.0f; *pBrake = 0.5f; *pHandbrake = true; return; case MISSION_CRUISE: case MISSION_RAMPLAYER_FARAWAY: case MISSION_BLOCKPLAYER_FARAWAY: case MISSION_GOTOCOORDS: case MISSION_GOTOCOORDS_ACCURATE: case MISSION_RAMCAR_FARAWAY: case MISSION_BLOCKCAR_FARAWAY: if (pVehicle->AutoPilot.m_bIgnorePathfinding) { *pSwerve = 0.0f; *pAccel = 1.0f; *pBrake = 0.0f; *pHandbrake = false; }else SteerAICarWithPhysicsFollowPath(pVehicle, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_RAMPLAYER_CLOSE: { CVector2D targetPos = FindPlayerCoors(); if (FindPlayerVehicle()){ if (pVehicle->m_randomSeed & 1 && DotProduct(FindPlayerVehicle()->GetForward(), pVehicle->GetForward()) > 0.5f){ float targetWidth = FindPlayerVehicle()->GetColModel()->boundingBox.max.x; float ownWidth = pVehicle->GetColModel()->boundingBox.max.x; if (pVehicle->m_randomSeed & 2){ targetPos += (targetWidth + ownWidth - 0.2f) * FindPlayerVehicle()->GetRight(); }else{ targetPos -= (targetWidth + ownWidth - 0.2f) * FindPlayerVehicle()->GetRight(); } float targetSpeed = FindPlayerVehicle()->GetMoveSpeed().Magnitude(); float distanceToTarget = ((CVector2D)pVehicle->GetPosition() - targetPos).Magnitude(); if (12.0f * targetSpeed + 2.0f > distanceToTarget && pVehicle->AutoPilot.m_nTempAction == TEMPACT_NONE){ pVehicle->AutoPilot.m_nTempAction = (pVehicle->m_randomSeed & 2) ? TEMPACT_TURNLEFT : TEMPACT_TURNRIGHT; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 250; } }else{ targetPos += FindPlayerVehicle()->GetRight() / 160 * ((pVehicle->m_randomSeed & 0xFF) - 128); } } SteerAICarWithPhysicsHeadingForTarget(pVehicle, FindPlayerVehicle(), targetPos.x, targetPos.y, pSwerve, pAccel, pBrake, pHandbrake); return; } case MISSION_BLOCKPLAYER_CLOSE: SteerAICarWithPhysicsTryingToBlockTarget(pVehicle, FindPlayerCoors().x, FindPlayerCoors().y, FindPlayerSpeed().x, FindPlayerSpeed().y, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_BLOCKPLAYER_HANDBRAKESTOP: SteerAICarWithPhysicsTryingToBlockTarget_Stop(pVehicle, FindPlayerCoors().x, FindPlayerCoors().y, FindPlayerSpeed().x, FindPlayerSpeed().y, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_WAITFORDELETION: case MISSION_HELI_LAND: return; case MISSION_GOTOCOORDS_STRAIGHT: case MISSION_GOTO_COORDS_STRAIGHT_ACCURATE: SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_EMERGENCYVEHICLE_STOP: case MISSION_STOP_FOREVER: *pSwerve = 0.0f; *pAccel = 0.0f; *pHandbrake = true; *pBrake = 0.5f; return; case MISSION_GOTOCOORDS_ASTHECROWSWIMS: SteerAIBoatWithPhysicsHeadingForTarget(pVehicle, pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y, pSwerve, pAccel, pBrake); *pHandbrake = false; return; case MISSION_RAMCAR_CLOSE: SteerAICarWithPhysicsHeadingForTarget(pVehicle, pVehicle->AutoPilot.m_pTargetCar, pVehicle->AutoPilot.m_pTargetCar->GetPosition().x, pVehicle->AutoPilot.m_pTargetCar->GetPosition().y, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_BLOCKCAR_CLOSE: SteerAICarWithPhysicsTryingToBlockTarget(pVehicle, pVehicle->AutoPilot.m_pTargetCar->GetPosition().x, pVehicle->AutoPilot.m_pTargetCar->GetPosition().y, pVehicle->AutoPilot.m_pTargetCar->GetMoveSpeed().x, pVehicle->AutoPilot.m_pTargetCar->GetMoveSpeed().y, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_BLOCKCAR_HANDBRAKESTOP: SteerAICarWithPhysicsTryingToBlockTarget_Stop(pVehicle, pVehicle->AutoPilot.m_pTargetCar->GetPosition().x, pVehicle->AutoPilot.m_pTargetCar->GetPosition().y, pVehicle->AutoPilot.m_pTargetCar->GetMoveSpeed().x, pVehicle->AutoPilot.m_pTargetCar->GetMoveSpeed().y, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_HELI_FLYTOCOORS: SteerAIHeliTowardsTargetCoors((CAutomobile*)pVehicle); return; case MISSION_ATTACKPLAYER: SteerAIBoatWithPhysicsAttackingPlayer(pVehicle, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_PLANE_FLYTOCOORS: SteerAIPlaneTowardsTargetCoors((CAutomobile*)pVehicle); return; case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1: SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_2: SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, FindPlayerCoors().x, FindPlayerCoors().y, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_BLOCKPLAYER_FORWARDANDBACK: SteerAICarBlockingPlayerForwardAndBack(pVehicle, pSwerve, pAccel, pBrake, pHandbrake); return; default: assert(0); return; } } void CCarCtrl::SteerAICarBlockingPlayerForwardAndBack(CVehicle* pVehicle, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) { *pSwerve = 0.0f; *pHandbrake = false; CVector player = FindPlayerSpeed() + 0.1f * FindPlayerEntity()->GetForward(); player.z = 0.0f; CVector right(pVehicle->GetRight().x, pVehicle->GetRight().y, 0.0f); right.Normalise(); CVector forward(pVehicle->GetForward().x, pVehicle->GetForward().y, 0.0f); forward.Normalise(); float dpPlayerAndRight = DotProduct(player, right); if (dpPlayerAndRight == 0.0f) dpPlayerAndRight = 0.01f; float dpDiffAndRight = -DotProduct((FindPlayerCoors() - pVehicle->GetPosition()), right) / dpPlayerAndRight; if (dpDiffAndRight < 0.0f) { *pAccel = 0.0f; *pBrake = 0.0f; return; } float dpSpeedAndForward = DotProduct(pVehicle->GetMoveSpeed(), forward); float dpPlayerAndForward = DotProduct(player, forward); float dpDiffAndForward = DotProduct((FindPlayerCoors() - pVehicle->GetPosition()), forward); float multiplier = dpPlayerAndForward * dpDiffAndRight + dpDiffAndForward - dpSpeedAndForward * dpDiffAndRight; if (multiplier > 0) { *pAccel = Min(1.0f, 0.1f * multiplier); *pBrake = 0.0f; } else if (dpSpeedAndForward > 0) { *pAccel = 0.0f; *pBrake = Min(1.0f, -0.1f * multiplier); if (*pBrake > 0.95f) *pHandbrake = true; } else { *pAccel = Max(-1.0f, 0.1f * multiplier); *pBrake = 0.0f; } } void CCarCtrl::SteerAIBoatWithPhysicsHeadingForTarget(CVehicle* pVehicle, float targetX, float targetY, float* pSwerve, float* pAccel, float* pBrake) { CVector2D forward = pVehicle->GetForward(); forward.Normalise(); float angleToTarget = CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y); float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y); float steerAngle = LimitRadianAngle(angleToTarget - angleForward); steerAngle = clamp(steerAngle, -DEFAULT_MAX_STEER_ANGLE, DEFAULT_MAX_STEER_ANGLE); #ifdef FIX_BUGS float speedTarget = pVehicle->AutoPilot.GetCruiseSpeed(); #else float speedTarget = pVehicle->AutoPilot.m_nCruiseSpeed; #endif float currentSpeed = pVehicle->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_CARAI_SPEED; float speedDiff = speedTarget - currentSpeed; if (speedDiff <= 0.0f) { speedDiff < -5.0f ? *pAccel = -0.2f : *pAccel = -0.1f; steerAngle *= -1; } else if (speedDiff / currentSpeed > 0.25f) { *pAccel = 1.0f; } else { *pAccel = 1.0f - (0.25f - speedDiff / currentSpeed) * 4.0f; } *pBrake = 0.0f; *pSwerve = steerAngle; } void CCarCtrl::SteerAIBoatWithPhysicsAttackingPlayer(CVehicle* pVehicle, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) { float distanceToPlayer = (FindPlayerCoors() - pVehicle->GetPosition()).Magnitude(); float projection = Min(distanceToPlayer / 20.0f, 2.0f); CVector2D forward = pVehicle->GetForward(); forward.Normalise(); CVector2D vecToProjection = FindPlayerCoors() + FindPlayerSpeed() * projection * GAME_SPEED_TO_CARAI_SPEED; float angleToTarget = CGeneral::GetATanOfXY(vecToProjection.x - pVehicle->GetPosition().x, vecToProjection.y - pVehicle->GetPosition().y); float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y); float steerAngle = LimitRadianAngle(angleToTarget - angleForward); #ifdef FIX_BUGS float speedTarget = pVehicle->AutoPilot.GetCruiseSpeed(); #else float speedTarget = pVehicle->AutoPilot.m_nCruiseSpeed; #endif float currentSpeed = pVehicle->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_CARAI_SPEED; float speedDiff = speedTarget - currentSpeed; if (speedDiff <= 0.0f) { speedDiff < -5.0f ? *pAccel = -0.2f : *pAccel = -0.1f; } else if (speedDiff / currentSpeed > 0.25f) { *pAccel = 1.0f; } else { *pAccel = 1.0f - (0.25f - speedDiff / currentSpeed) * 4.0f; } *pBrake = 0.0f; *pSwerve = steerAngle; *pHandbrake = false; if (pVehicle->GetModelIndex() == MI_PREDATOR && distanceToPlayer < 40.0f && steerAngle < 0.15f) pVehicle->FireFixedMachineGuns(); } float CCarCtrl::FindMaxSteerAngle(CVehicle* pVehicle) { return pVehicle->GetModelIndex() == MI_ENFORCER ? 0.7f : DEFAULT_MAX_STEER_ANGLE; } void CCarCtrl::SteerAIHeliTowardsTargetCoors(CAutomobile* pHeli) { if (pHeli->m_aWheelSpeed[1] < 0.22f) pHeli->m_aWheelSpeed[1] += 0.001f; if (pHeli->m_aWheelSpeed[1] < 0.15f) return; CVector2D vecToTarget = pHeli->AutoPilot.m_vecDestinationCoors - pHeli->GetPosition(); float distanceToTarget = vecToTarget.Magnitude(); #ifdef FIX_BUGS float speed = pHeli->AutoPilot.GetCruiseSpeed() * 0.01f; #else float speed = pHeli->AutoPilot.m_nCruiseSpeed * 0.01f; #endif if (distanceToTarget <= 100.0f) { if (distanceToTarget > 75.0f) speed *= 0.7f; else if (distanceToTarget > 10.0f) speed *= 0.4f; else speed *= 0.2f; } vecToTarget.Normalise(); CVector2D vecAdvanceThisFrame(vecToTarget * speed); float resistance = Pow(0.997f, CTimer::GetTimeStep()); pHeli->m_vecMoveSpeed.x *= resistance; pHeli->m_vecMoveSpeed.y *= resistance; CVector2D vecSpeedDirection = vecAdvanceThisFrame - pHeli->m_vecMoveSpeed; float vecSpeedChangeLength = vecSpeedDirection.Magnitude(); vecSpeedDirection.Normalise(); float changeMultiplier = 0.002f * CTimer::GetTimeStep(); if (distanceToTarget < 5.0f) changeMultiplier /= 5.0f; if (vecSpeedChangeLength < changeMultiplier) pHeli->SetMoveSpeed(vecAdvanceThisFrame.x, vecAdvanceThisFrame.y, pHeli->GetMoveSpeed().z); else pHeli->AddToMoveSpeed(vecSpeedDirection * changeMultiplier); pHeli->GetMatrix().Translate(CTimer::GetTimeStep() * pHeli->GetMoveSpeed().x, CTimer::GetTimeStep() * pHeli->GetMoveSpeed().y, 0.0f); float ZTarget = pHeli->AutoPilot.m_vecDestinationCoors.z; if (CTimer::GetTimeInMilliseconds() & 0x800) // switch every ~2 seconds ZTarget += 2.0f; float ZSpeedTarget = (ZTarget - pHeli->GetPosition().z) * 0.01f; float ZSpeedChangeTarget = ZSpeedTarget - pHeli->GetMoveSpeed().z; float ZSpeedChangeMax = 0.001f * CTimer::GetTimeStep(); if (!pHeli->bHeliDestroyed) { if (Abs(ZSpeedChangeTarget) < ZSpeedChangeMax) pHeli->SetMoveSpeed(pHeli->GetMoveSpeed().x, pHeli->GetMoveSpeed().y, ZSpeedTarget); else if (ZSpeedChangeTarget < 0.0f) pHeli->AddToMoveSpeed(0.0f, 0.0f, -ZSpeedChangeMax); else pHeli->AddToMoveSpeed(0.0f, 0.0f, 1.5f * ZSpeedChangeMax); } pHeli->GetMatrix().Translate(0.0f, 0.0f, CTimer::GetTimeStep() * pHeli->GetMoveSpeed().z); pHeli->m_vecTurnSpeed.z *= Pow(0.99f, CTimer::GetTimeStep()); float ZTurnSpeedTarget; if (distanceToTarget < 8.0f && pHeli->m_fHeliOrientation < 0.0f) ZTurnSpeedTarget = 0.0f; else { float fAngleTarget = CGeneral::GetATanOfXY(vecToTarget.x, vecToTarget.y) + PI; if (pHeli->m_fHeliOrientation >= 0.0f) fAngleTarget = pHeli->m_fHeliOrientation; fAngleTarget -= pHeli->m_fOrientation; while (fAngleTarget < -PI) fAngleTarget += TWOPI; while (fAngleTarget > PI) fAngleTarget -= TWOPI; if (Abs(fAngleTarget) <= 0.4f) ZTurnSpeedTarget = 0.0f; else if (fAngleTarget < 0.0f) ZTurnSpeedTarget = -0.03f; else ZTurnSpeedTarget = 0.03f; } float ZTurnSpeedChangeTarget = ZTurnSpeedTarget - pHeli->GetTurnSpeed().z; float ZTurnSpeedLimit = 0.0002f * CTimer::GetTimeStep(); if (Abs(ZTurnSpeedChangeTarget) < ZTurnSpeedLimit) pHeli->m_vecTurnSpeed.z = ZTurnSpeedTarget; else if (ZTurnSpeedChangeTarget < 0.0f) pHeli->m_vecTurnSpeed.z -= ZTurnSpeedLimit; else pHeli->m_vecTurnSpeed.z += ZTurnSpeedLimit; pHeli->m_fOrientation += pHeli->GetTurnSpeed().z * CTimer::GetTimeStep(); CVector up; if (pHeli->bHeliMinimumTilt) up = CVector(0.5f * pHeli->GetMoveSpeed().x, 0.5f * pHeli->GetMoveSpeed().y, 1.0f); else up = CVector(3.0f * pHeli->GetMoveSpeed().x, 3.0f * pHeli->GetMoveSpeed().y, 1.0f); up.Normalise(); CVector forward(Cos(pHeli->m_fOrientation), Sin(pHeli->m_fOrientation), 0.0f); CVector right = CrossProduct(up, forward); forward = CrossProduct(up, right); pHeli->GetMatrix().GetRight() = right; pHeli->GetMatrix().GetForward() = forward; pHeli->GetMatrix().GetUp() = up; } void CCarCtrl::SteerAIPlaneTowardsTargetCoors(CAutomobile* pPlane) { CVector2D vecToTarget = pPlane->AutoPilot.m_vecDestinationCoors - pPlane->GetPosition(); float fForwardZ = (pPlane->AutoPilot.m_vecDestinationCoors.z - pPlane->GetPosition().z) / vecToTarget.Magnitude(); fForwardZ = clamp(fForwardZ, -0.3f, 0.3f); float angle = CGeneral::GetATanOfXY(vecToTarget.x, vecToTarget.y); while (angle > TWOPI) angle -= TWOPI; float difference = LimitRadianAngle(angle - pPlane->m_fOrientation); float steer = difference > 0.0f ? 0.04f : -0.04f; if (Abs(difference) < 0.2f) steer *= 5.0f * Abs(difference); pPlane->m_fPlaneSteer *= Pow(0.96f, CTimer::GetTimeStep()); float steerChange = steer - pPlane->m_fPlaneSteer; float maxChange = 0.003f * CTimer::GetTimeStep(); if (Abs(steerChange) < maxChange) pPlane->m_fPlaneSteer = steer; else if (steerChange < 0.0f) pPlane->m_fPlaneSteer -= maxChange; else pPlane->m_fPlaneSteer += maxChange; pPlane->m_fOrientation += pPlane->m_fPlaneSteer * CTimer::GetTimeStep(); CVector up(0.0f, 0.0f, 1.0f); up.Normalise(); CVector forward(Cos(pPlane->m_fOrientation), Sin(pPlane->m_fOrientation), fForwardZ); forward.Normalise(); CVector right = CrossProduct(up, forward); right.z -= 5.0f * pPlane->m_fPlaneSteer; right.Normalise(); up = CrossProduct(forward, right); up.Normalise(); right = CrossProduct(forward, up); pPlane->GetMatrix().GetRight() = right; pPlane->GetMatrix().GetForward() = forward; pPlane->GetMatrix().GetUp() = up; float newSplit = 1.0f - Pow(0.95f, CTimer::GetTimeStep()); float oldSplit = 1.0f - newSplit; #ifdef FIX_BUGS pPlane->m_vecMoveSpeed = pPlane->m_vecMoveSpeed * oldSplit + pPlane->AutoPilot.GetCruiseSpeed() * 0.01f * forward * newSplit; #else pPlane->m_vecMoveSpeed = pPlane->m_vecMoveSpeed * oldSplit + pPlane->AutoPilot.m_nCruiseSpeed * 0.01f * forward * newSplit; #endif pPlane->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); } void CCarCtrl::SteerAICarWithPhysicsFollowPath(CVehicle* pVehicle, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) { CVector2D forward = pVehicle->GetForward(); forward.Normalise(); CCarPathLink* pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; CCarPathLink* pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; CVector2D currentPathLinkForward(pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection, pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection); float nextPathLinkForwardX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; float nextPathLinkForwardY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; CVector2D positionOnCurrentLinkIncludingLane( pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.y, pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.x); CVector2D positionOnNextLinkIncludingLane( pNextLink->GetX() + ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardY, pNextLink->GetY() - ((pVehicle->AutoPilot.m_nNextLane + pNextLink->OneWayLaneOffset()) * LANE_WIDTH) * nextPathLinkForwardX); CVector2D distanceToNextNode = (CVector2D)pVehicle->GetPosition() - positionOnCurrentLinkIncludingLane; float scalarDistanceToNextNode = distanceToNextNode.Magnitude(); CVector2D distanceBetweenNodes = positionOnNextLinkIncludingLane - positionOnCurrentLinkIncludingLane; float dp = DotProduct2D(distanceBetweenNodes, distanceToNextNode); if (scalarDistanceToNextNode < DISTANCE_TO_NEXT_NODE_TO_SELECT_NEW || dp > 0.0f && scalarDistanceToNextNode < DISTANCE_TO_FACING_NEXT_NODE_TO_SELECT_NEW || dp / (scalarDistanceToNextNode * distanceBetweenNodes.Magnitude()) > 0.7f || pVehicle->AutoPilot.m_nNextPathNodeInfo == pVehicle->AutoPilot.m_nCurrentPathNodeInfo){ if (PickNextNodeAccordingStrategy(pVehicle)) { switch (pVehicle->AutoPilot.m_nCarMission){ case MISSION_GOTOCOORDS: pVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y, pSwerve, pAccel, pBrake, pHandbrake); return; case MISSION_GOTOCOORDS_ACCURATE: pVehicle->AutoPilot.m_nCarMission = MISSION_GOTO_COORDS_STRAIGHT_ACCURATE; SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, pVehicle->AutoPilot.m_vecDestinationCoors.x, pVehicle->AutoPilot.m_vecDestinationCoors.y, pSwerve, pAccel, pBrake, pHandbrake); return; default: break; } } pCurrentLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nCurrentPathNodeInfo]; scalarDistanceToNextNode = CVector2D( pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.y - pVehicle->GetPosition().x, pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.x - pVehicle->GetPosition().y).Magnitude(); pNextLink = &ThePaths.m_carPathLinks[pVehicle->AutoPilot.m_nNextPathNodeInfo]; currentPathLinkForward.x = pCurrentLink->GetDirX() * pVehicle->AutoPilot.m_nCurrentDirection; currentPathLinkForward.y = pCurrentLink->GetDirY() * pVehicle->AutoPilot.m_nCurrentDirection; nextPathLinkForwardX = pNextLink->GetDirX() * pVehicle->AutoPilot.m_nNextDirection; nextPathLinkForwardY = pNextLink->GetDirY() * pVehicle->AutoPilot.m_nNextDirection; } positionOnCurrentLinkIncludingLane.x = pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.y; positionOnCurrentLinkIncludingLane.y = pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.x; CVector2D projectedPosition = positionOnCurrentLinkIncludingLane - currentPathLinkForward * scalarDistanceToNextNode * 0.4f; if (scalarDistanceToNextNode > DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN){ projectedPosition.x = positionOnCurrentLinkIncludingLane.x; projectedPosition.y = positionOnCurrentLinkIncludingLane.y; } CVector2D distanceToProjectedPosition = projectedPosition - pVehicle->GetPosition(); float angleCurrentLink = CGeneral::GetATanOfXY(distanceToProjectedPosition.x, distanceToProjectedPosition.y); float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y); if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_AVOID_CARS) angleCurrentLink = FindAngleToWeaveThroughTraffic(pVehicle, nil, angleCurrentLink, angleForward); float steerAngle = LimitRadianAngle(angleCurrentLink - angleForward); float maxAngle = FindMaxSteerAngle(pVehicle); steerAngle = Min(maxAngle, Max(-maxAngle, steerAngle)); if (pVehicle->GetMoveSpeed().Magnitude() > MIN_SPEED_TO_START_LIMITING_STEER) steerAngle = Min(MAX_ANGLE_TO_STEER_AT_HIGH_SPEED, Max(-MAX_ANGLE_TO_STEER_AT_HIGH_SPEED, steerAngle)); float currentForwardSpeed = DotProduct(pVehicle->GetMoveSpeed(), pVehicle->GetForward()) * GAME_SPEED_TO_CARAI_SPEED; float speedStyleMultiplier; switch (pVehicle->AutoPilot.m_nDrivingStyle) { case DRIVINGSTYLE_STOP_FOR_CARS: case DRIVINGSTYLE_SLOW_DOWN_FOR_CARS: case DRIVINGSTYLE_STOP_FOR_CARS_IGNORE_LIGHTS: speedStyleMultiplier = FindMaximumSpeedForThisCarInTraffic(pVehicle); #ifdef FIX_BUGS if (pVehicle->AutoPilot.GetCruiseSpeed() != 0) speedStyleMultiplier /= pVehicle->AutoPilot.GetCruiseSpeed(); #else speedStyleMultiplier /= pVehicle->AutoPilot.m_nCruiseSpeed; #endif break; default: speedStyleMultiplier = 1.0f; break; } switch (pVehicle->AutoPilot.m_nDrivingStyle) { case DRIVINGSTYLE_STOP_FOR_CARS: case DRIVINGSTYLE_SLOW_DOWN_FOR_CARS: if (CTrafficLights::ShouldCarStopForLight(pVehicle, false)){ CCarAI::CarHasReasonToStop(pVehicle); speedStyleMultiplier = 0.0f; } break; default: break; } if (CTrafficLights::ShouldCarStopForBridge(pVehicle)){ CCarAI::CarHasReasonToStop(pVehicle); speedStyleMultiplier = 0.0f; } CVector2D trajectory(pCurrentLink->GetX() + ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.y, pCurrentLink->GetY() - ((pVehicle->AutoPilot.m_nCurrentLane + pCurrentLink->OneWayLaneOffset()) * LANE_WIDTH) * currentPathLinkForward.x); trajectory -= pVehicle->GetPosition(); float speedAngleMultiplier = FindSpeedMultiplier( CGeneral::GetATanOfXY(trajectory.x, trajectory.y) - angleForward, MIN_ANGLE_FOR_SPEED_LIMITING, MAX_ANGLE_FOR_SPEED_LIMITING, MIN_LOWERING_SPEED_COEFFICIENT); float tmpWideMultiplier = FindSpeedMultiplier( CGeneral::GetATanOfXY(currentPathLinkForward.x, currentPathLinkForward.y) - CGeneral::GetATanOfXY(nextPathLinkForwardX, nextPathLinkForwardY), MIN_ANGLE_FOR_SPEED_LIMITING_BETWEEN_NODES, MAX_ANGLE_FOR_SPEED_LIMITING, MIN_LOWERING_SPEED_COEFFICIENT); float speedNodesMultiplier; if (scalarDistanceToNextNode > DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN || pVehicle->AutoPilot.m_nCruiseSpeed < 12) speedNodesMultiplier = 1.0f; else speedNodesMultiplier = 1.0f - (1.0f - scalarDistanceToNextNode / DISTANCE_TO_NEXT_NODE_TO_CONSIDER_SLOWING_DOWN) * (1.0f - tmpWideMultiplier); float speedMultiplier = Min(speedStyleMultiplier, Min(speedAngleMultiplier, speedNodesMultiplier)); float speed = pVehicle->AutoPilot.m_nCruiseSpeed * speedMultiplier; float speedDifference = speed - currentForwardSpeed; if (speed < 0.05f && speedDifference < 0.03f){ *pBrake = 1.0f; *pAccel = 0.0f; }else if (speedDifference <= 0.0f){ *pBrake = Min(0.5f, -speedDifference * 0.05f); *pAccel = 0.0f; }else if (currentForwardSpeed < 2.0f){ *pBrake = 0.0f; *pAccel = Min(1.0f, speedDifference * 0.25f); }else{ *pBrake = 0.0f; *pAccel = Min(1.0f, speedDifference * 0.125f); } *pSwerve = steerAngle; *pHandbrake = false; } void CCarCtrl::SteerAICarWithPhysicsHeadingForTarget(CVehicle* pVehicle, CPhysical* pTarget, float targetX, float targetY, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) { *pHandbrake = false; CVector2D forward = pVehicle->GetForward(); forward.Normalise(); float angleToTarget = CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y); float angleForward = CGeneral::GetATanOfXY(forward.x, forward.y); if (pVehicle->AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_AVOID_CARS) angleToTarget = FindAngleToWeaveThroughTraffic(pVehicle, pTarget, angleToTarget, angleForward); float steerAngle = LimitRadianAngle(angleToTarget - angleForward); if (pVehicle->GetMoveSpeed().Magnitude() > MIN_SPEED_TO_APPLY_HANDBRAKE) if (ABS(steerAngle) > MIN_ANGLE_TO_APPLY_HANDBRAKE) *pHandbrake = true; float maxAngle = FindMaxSteerAngle(pVehicle); steerAngle = Min(maxAngle, Max(-maxAngle, steerAngle)); float speedMultiplier = FindSpeedMultiplier(CGeneral::GetATanOfXY(targetX - pVehicle->GetPosition().x, targetY - pVehicle->GetPosition().y) - angleForward, MIN_ANGLE_FOR_SPEED_LIMITING, MAX_ANGLE_FOR_SPEED_LIMITING, MIN_LOWERING_SPEED_COEFFICIENT); float speedTarget = pVehicle->AutoPilot.m_nCruiseSpeed * speedMultiplier; float currentSpeed = pVehicle->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_CARAI_SPEED; float speedDiff = speedTarget - currentSpeed; if (speedDiff <= 0.0f){ *pAccel = 0.0f; *pBrake = Min(0.5f, -speedDiff / 20.0f); }else if (currentSpeed < 25.0f){ *pAccel = Min(1.0f, speedDiff * 0.1f); *pBrake = 0.0f; }else{ *pAccel = 1.0f; *pBrake = 0.0f; } *pSwerve = steerAngle; } void CCarCtrl::SteerAICarWithPhysicsTryingToBlockTarget(CVehicle* pVehicle, float targetX, float targetY, float targetSpeedX, float targetSpeedY, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) { CVector2D targetPos(targetX, targetY); CVector2D offset(targetSpeedX, targetSpeedY); float trajectoryLen = offset.Magnitude(); if (trajectoryLen > MAX_SPEED_TO_ACCOUNT_IN_INTERCEPTING) offset *= MAX_SPEED_TO_ACCOUNT_IN_INTERCEPTING / trajectoryLen; targetPos += offset * GAME_SPEED_TO_CARAI_SPEED; pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; SteerAICarWithPhysicsHeadingForTarget(pVehicle, nil, targetPos.x, targetPos.y, pSwerve, pAccel, pBrake, pHandbrake); if ((targetPos - pVehicle->GetPosition()).MagnitudeSqr() < SQR(DISTANCE_TO_SWITCH_FROM_BLOCK_TO_STOP)) pVehicle->AutoPilot.m_nCarMission = (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_CLOSE) ? MISSION_BLOCKCAR_HANDBRAKESTOP : MISSION_BLOCKPLAYER_HANDBRAKESTOP; } void CCarCtrl::SteerAICarWithPhysicsTryingToBlockTarget_Stop(CVehicle* pVehicle, float targetX, float targetY, float targetSpeedX, float targetSpeedY, float* pSwerve, float* pAccel, float* pBrake, bool* pHandbrake) { *pSwerve = 0.0f; *pAccel = 0.0f; *pBrake = 1.0f; *pHandbrake = true; float distanceToTargetSqr = (CVector2D(targetX, targetY) - pVehicle->GetPosition()).MagnitudeSqr(); if (distanceToTargetSqr > SQR(DISTANCE_TO_SWITCH_FROM_STOP_TO_BLOCK)){ pVehicle->AutoPilot.m_nCarMission = (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_HANDBRAKESTOP) ? MISSION_BLOCKCAR_CLOSE : MISSION_BLOCKPLAYER_CLOSE; return; } if (pVehicle->AutoPilot.m_nCarMission == MISSION_BLOCKCAR_HANDBRAKESTOP){ if (((CVector2D)pVehicle->GetMoveSpeed()).MagnitudeSqr() < SQR(0.01f) && CVector2D(targetSpeedX, targetSpeedY).MagnitudeSqr() < SQR(0.02f) && pVehicle->bIsLawEnforcer){ CCarAI::TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCruiseSpeed = 0; pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; } }else{ if (FindPlayerVehicle() && FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f) #ifdef FIX_BUGS pVehicle->m_nTimeBlocked += CTimer::GetTimeStepInMilliseconds(); #else pVehicle->m_nTimeBlocked += 1000.0f / 60.0f * CTimer::GetTimeStep(); // very doubtful constant #endif else pVehicle->m_nTimeBlocked = 0; if (FindPlayerVehicle() == nil || FindPlayerVehicle()->IsUpsideDown() || FindPlayerVehicle()->GetMoveSpeed().Magnitude() < 0.05f && pVehicle->m_nTimeBlocked > TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING){ if (pVehicle->bIsLawEnforcer && distanceToTargetSqr < SQR(DISTANCE_TO_SWITCH_FROM_STOP_TO_BLOCK)){ CCarAI::TellOccupantsToLeaveCar(pVehicle); pVehicle->AutoPilot.m_nCruiseSpeed = 0; pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; } } } } void CCarCtrl::RegisterVehicleOfInterest(CVehicle* pVehicle) { for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { if (apCarsToKeep[i] == pVehicle) { aCarsToKeepTime[i] = CTimer::GetTimeInMilliseconds(); return; } } for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { if (!apCarsToKeep[i]) { apCarsToKeep[i] = pVehicle; aCarsToKeepTime[i] = CTimer::GetTimeInMilliseconds(); return; } } uint32 oldestCarWeKeepTime = UINT32_MAX; int oldestCarWeKeepIndex = 0; for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { if (apCarsToKeep[i] && aCarsToKeepTime[i] < oldestCarWeKeepTime) { oldestCarWeKeepTime = aCarsToKeepTime[i]; oldestCarWeKeepIndex = i; } } apCarsToKeep[oldestCarWeKeepIndex] = pVehicle; aCarsToKeepTime[oldestCarWeKeepIndex] = CTimer::GetTimeInMilliseconds(); } bool CCarCtrl::IsThisVehicleInteresting(CVehicle* pVehicle) { for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { if (apCarsToKeep[i] == pVehicle) return true; } return false; } void CCarCtrl::RemoveFromInterestingVehicleList(CVehicle* pVehicle) { for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { if (apCarsToKeep[i] == pVehicle) apCarsToKeep[i] = nil; } } void CCarCtrl::ClearInterestingVehicleList() { for (int i = 0; i < MAX_CARS_TO_KEEP; i++) { apCarsToKeep[i] = nil; } } void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle* pVehicle) { pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds(); } void CCarCtrl::JoinCarWithRoadSystem(CVehicle* pVehicle) { pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode = 0; pVehicle->AutoPilot.m_nCurrentPathNodeInfo = pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo = 0; int nodeId = ThePaths.FindNodeClosestToCoorsFavourDirection(pVehicle->GetPosition(), 0, pVehicle->GetForward().x, pVehicle->GetForward().y); CPathNode* pNode = &ThePaths.m_pathNodes[nodeId]; int prevNodeId = -1; float minDistance = 999999.9f; for (int i = 0; i < pNode->numLinks; i++){ int candidateId = ThePaths.ConnectedNode(i + pNode->firstLink); CPathNode* pCandidateNode = &ThePaths.m_pathNodes[candidateId]; float distance = (pCandidateNode->GetPosition() - pNode->GetPosition()).Magnitude2D(); if (distance < minDistance){ minDistance = distance; prevNodeId = candidateId; } } if (prevNodeId < 0) return; CVector2D forward = pVehicle->GetForward(); CPathNode* pPrevNode = &ThePaths.m_pathNodes[prevNodeId]; if (forward.x == 0.0f && forward.y == 0.0f) forward.x = 1.0f; if (DotProduct2D(pNode->GetPosition() - pPrevNode->GetPosition(), forward) < 0.0f){ int tmp; tmp = prevNodeId; prevNodeId = nodeId; nodeId = tmp; } pVehicle->AutoPilot.m_nPrevRouteNode = 0; pVehicle->AutoPilot.m_nCurrentRouteNode = prevNodeId; pVehicle->AutoPilot.m_nNextRouteNode = nodeId; pVehicle->AutoPilot.m_nPathFindNodesCount = 0; FindLinksToGoWithTheseNodes(pVehicle); pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = 0; } bool CCarCtrl::JoinCarWithRoadSystemGotoCoors(CVehicle* pVehicle, CVector vecTarget, bool isProperNow) { pVehicle->AutoPilot.m_vecDestinationCoors = vecTarget; ThePaths.DoPathSearch(0, pVehicle->GetPosition(), -1, vecTarget, pVehicle->AutoPilot.m_aPathFindNodesInfo, &pVehicle->AutoPilot.m_nPathFindNodesCount, NUM_PATH_NODES_IN_AUTOPILOT, pVehicle, nil, 999999.9f, -1); ThePaths.RemoveBadStartNode(pVehicle->GetPosition(), pVehicle->AutoPilot.m_aPathFindNodesInfo, &pVehicle->AutoPilot.m_nPathFindNodesCount); if (pVehicle->AutoPilot.m_nPathFindNodesCount < 2){ pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode = 0; pVehicle->AutoPilot.m_nPathFindNodesCount = 0; return true; } pVehicle->AutoPilot.m_nPrevRouteNode = 0; pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_aPathFindNodesInfo[0] - ThePaths.m_pathNodes; pVehicle->AutoPilot.RemoveOnePathNode(); pVehicle->AutoPilot.m_nNextRouteNode = pVehicle->AutoPilot.m_aPathFindNodesInfo[0] - ThePaths.m_pathNodes; pVehicle->AutoPilot.RemoveOnePathNode(); FindLinksToGoWithTheseNodes(pVehicle); pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = 0; return false; } void CCarCtrl::FindLinksToGoWithTheseNodes(CVehicle* pVehicle) { if (pVehicle->m_nRouteSeed) CGeneral::SetRandomSeed(pVehicle->m_nRouteSeed); int nextLink; CPathNode* pCurNode = &ThePaths.m_pathNodes[pVehicle->AutoPilot.m_nCurrentRouteNode]; for (nextLink = 0; nextLink < 12; nextLink++) if (ThePaths.ConnectedNode(nextLink + pCurNode->firstLink) == pVehicle->AutoPilot.m_nNextRouteNode) break; pVehicle->AutoPilot.m_nNextPathNodeInfo = ThePaths.m_carPathConnections[nextLink + pCurNode->firstLink]; pVehicle->AutoPilot.m_nNextDirection = (pVehicle->AutoPilot.m_nCurrentRouteNode >= pVehicle->AutoPilot.m_nNextRouteNode) ? 1 : -1; int curLink; int curConnection; if (pCurNode->numLinks == 1) { curLink = 0; curConnection = ThePaths.m_carPathConnections[pCurNode->firstLink]; }else{ int closestLink = -1; float md = 999999.9f; for (curLink = 0; curLink < pCurNode->numLinks; curLink++) { int node = ThePaths.ConnectedNode(curLink + pCurNode->firstLink); CPathNode* pNode = &ThePaths.m_pathNodes[node]; if (node == pVehicle->AutoPilot.m_nNextRouteNode) continue; CVector vCurPos = pCurNode->GetPosition(); CVector vNextPos = pNode->GetPosition(); float dist = CCollision::DistToLine(&vCurPos, &vNextPos, &pVehicle->GetPosition()); if (dist < md) { md = dist; closestLink = curLink; } } curConnection = ThePaths.m_carPathConnections[closestLink + pCurNode->firstLink]; } pVehicle->AutoPilot.m_nCurrentPathNodeInfo = curConnection; pVehicle->AutoPilot.m_nCurrentDirection = (ThePaths.ConnectedNode(curLink + pCurNode->firstLink) >= pVehicle->AutoPilot.m_nCurrentRouteNode) ? 1 : -1; } void CCarCtrl::GenerateEmergencyServicesCar(void) { if (FindPlayerPed()->m_pWanted->GetWantedLevel() > 3) return; if (CGame::IsInInterior()) return; if (NumFiretrucksOnDuty + NumAmbulancesOnDuty + NumParkedCars + NumMissionCars + NumLawEnforcerCars + NumRandomCars > MaxNumberOfCarsInUse) return; if (NumAmbulancesOnDuty == 0){ if (gAccidentManager.CountActiveAccidents() < 2){ if (CStreaming::HasModelLoaded(MI_AMBULAN)) CStreaming::SetModelIsDeletable(MI_MEDIC); }else{ float distance = 30.0f; CAccident* pNearestAccident = gAccidentManager.FindNearestAccident(FindPlayerCoors(), &distance); if (pNearestAccident){ if (CountCarsOfType(MI_AMBULAN) < 2 && CTimer::GetTimeInMilliseconds() > LastTimeAmbulanceCreated + 30000){ CStreaming::RequestModel(MI_AMBULAN, STREAMFLAGS_DEPENDENCY); CStreaming::RequestModel(MI_MEDIC, STREAMFLAGS_DONT_REMOVE); if (CStreaming::HasModelLoaded(MI_AMBULAN) && CStreaming::HasModelLoaded(MI_MEDIC)){ if (GenerateOneEmergencyServicesCar(MI_AMBULAN, pNearestAccident->m_pVictim->GetPosition())){ LastTimeAmbulanceCreated = CTimer::GetTimeInMilliseconds(); } } } } } } if (NumFiretrucksOnDuty == 0){ if (gFireManager.GetTotalActiveFires() < 3){ if (CStreaming::HasModelLoaded(MI_FIRETRUCK)) CStreaming::SetModelIsDeletable(MI_FIREMAN); }else{ float distance = 30.0f; CFire* pNearestFire = gFireManager.FindNearestFire(FindPlayerCoors(), &distance); if (pNearestFire) { if (CountCarsOfType(MI_FIRETRUCK) < 2 && CTimer::GetTimeInMilliseconds() > LastTimeFireTruckCreated + 35000){ CStreaming::RequestModel(MI_FIRETRUCK, STREAMFLAGS_DEPENDENCY); CStreaming::RequestModel(MI_FIREMAN, STREAMFLAGS_DONT_REMOVE); if (CStreaming::HasModelLoaded(MI_FIRETRUCK) && CStreaming::HasModelLoaded(MI_FIREMAN)){ if (GenerateOneEmergencyServicesCar(MI_FIRETRUCK, pNearestFire->m_vecPos)){ LastTimeFireTruckCreated = CTimer::GetTimeInMilliseconds(); #ifdef SECUROM if ((myrand() & 7) == 5){ // if pirated game CPickups::Init(); } #endif } } } } } } } bool CCarCtrl::GenerateOneEmergencyServicesCar(uint32 mi, CVector vecPos) { CVector pPlayerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); bool created = false; int attempts = 0; CVector spawnPos; int curNode, nextNode; float posBetweenNodes; while (!created && attempts < 5){ if (ThePaths.GenerateCarCreationCoors(pPlayerPos.x, pPlayerPos.y, 0.707f, 0.707f, REQUEST_ONSCREEN_DISTANCE, -1.0f, true, &spawnPos, &curNode, &nextNode, &posBetweenNodes, false)){ int16 colliding[2]; if (!ThePaths.GetNode(curNode)->bWaterPath) { CWorld::FindObjectsKindaColliding(spawnPos, 10.0f, true, colliding, 2, nil, false, true, true, false, false); if (colliding[0] == 0) created = true; } } attempts += 1; } if (attempts >= 5) return false; CAutomobile* pVehicle = new CAutomobile(mi, RANDOM_VEHICLE); pVehicle->AutoPilot.m_vecDestinationCoors = vecPos; pVehicle->SetPosition(spawnPos); pVehicle->AutoPilot.m_nCarMission = (JoinCarWithRoadSystemGotoCoors(pVehicle, vecPos, false)) ? MISSION_GOTOCOORDS_STRAIGHT : MISSION_GOTOCOORDS; pVehicle->AutoPilot.m_fMaxTrafficSpeed = pVehicle->AutoPilot.m_nCruiseSpeed = 25; pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; CVector2D direction = vecPos - spawnPos; direction.Normalise(); pVehicle->GetForward() = CVector(direction.x, direction.y, 0.0f); pVehicle->GetRight() = CVector(direction.y, -direction.x, 0.0f); pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); spawnPos.z = posBetweenNodes * ThePaths.m_pathNodes[curNode].GetZ() + (1.0f - posBetweenNodes) * ThePaths.m_pathNodes[nextNode].GetZ(); float groundZ = INFINITE_Z; CColPoint colPoint; CEntity* pEntity; if (CWorld::ProcessVerticalLine(spawnPos, 1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) groundZ = colPoint.point.z; if (CWorld::ProcessVerticalLine(spawnPos, -1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) { if (ABS(colPoint.point.z - spawnPos.z) < ABS(groundZ - spawnPos.z)) groundZ = colPoint.point.z; } if (groundZ == INFINITE_Z) { delete pVehicle; return false; } spawnPos.z = groundZ + pVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); pVehicle->SetPosition(spawnPos); pVehicle->SetMoveSpeed(CVector(0.0f, 0.0f, 0.0f)); pVehicle->SetStatus(STATUS_PHYSICS); switch (mi){ case MI_FIRETRUCK: pVehicle->bIsFireTruckOnDuty = true; ++NumFiretrucksOnDuty; CCarAI::AddFiretruckOccupants(pVehicle); break; case MI_AMBULAN: pVehicle->bIsAmbulanceOnDuty = true; ++NumAmbulancesOnDuty; CCarAI::AddAmbulanceOccupants(pVehicle); break; } pVehicle->m_bSirenOrAlarm = true; CWorld::Add(pVehicle); printf("CREATED EMERGENCY VEHICLE\n"); return true; } void CCarCtrl::UpdateCarCount(CVehicle* pVehicle, bool remove) { if (remove){ switch (pVehicle->VehicleCreatedBy){ case RANDOM_VEHICLE: if (pVehicle->bIsLawEnforcer) { if (--NumLawEnforcerCars < 0) NumLawEnforcerCars = 0; } if (--NumRandomCars < 0) NumRandomCars = 0; return; case MISSION_VEHICLE: if (--NumMissionCars < 0) NumMissionCars = 0; return; case PARKED_VEHICLE: if (--NumParkedCars < 0) NumParkedCars = 0; return; case PERMANENT_VEHICLE: if (--NumPermanentCars < 0) NumPermanentCars = 0; return; } } else{ switch (pVehicle->VehicleCreatedBy){ case RANDOM_VEHICLE: if (pVehicle->bIsLawEnforcer) ++NumLawEnforcerCars; ++NumRandomCars; return; case MISSION_VEHICLE: ++NumMissionCars; return; case PARKED_VEHICLE: ++NumParkedCars; return; case PERMANENT_VEHICLE: ++NumPermanentCars; return; } } } bool CCarCtrl::ThisRoadObjectCouldMove(int16 mi) { #ifdef GTA_BRIDGE return mi == MI_BRIDGELIFT || mi == MI_BRIDGEROADSEGMENT; #else return false; #endif } bool CCarCtrl::MapCouldMoveInThisArea(float x, float y) { #ifdef GTA_BRIDGE // actually they forgot that in VC... // bridge moves up and down return x > -342.0f && x < -219.0f && y > -677.0f && y < -580.0f; #else return false; #endif } float CCarCtrl::FindSpeedMultiplierWithSpeedFromNodes(int8 type) { switch (type) { case 1: return 1.5f; case 2: return 2.0f; } return 1.0f; } ================================================ FILE: src/control/CarCtrl.h ================================================ #pragma once #include "PathFind.h" #include "Boat.h" #include "Vehicle.h" #define GAME_SPEED_TO_METERS_PER_SECOND 50.0f #define METERS_PER_SECOND_TO_GAME_SPEED (1.0f / GAME_SPEED_TO_METERS_PER_SECOND) #define GAME_SPEED_TO_CARAI_SPEED 60.0f #define TIME_COPS_WAIT_TO_EXIT_AFTER_STOPPING 2500 class CZoneInfo; class CAutomobile; enum{ MAX_CARS_TO_KEEP = 2, MAX_CAR_MODELS_IN_ARRAY = 25, }; #ifdef FIX_BUGS #define FIX_PATHFIND_BUG #endif class CCarCtrl { public: enum eCarClass { NORMAL = 0, POOR, RICH, EXEC, WORKER, BIG, TAXI, MOPED, MOTORBIKE, LEISUREBOAT, WORKERBOAT, COPS, CUBAN, HAITIAN, STREET, DIAZ, BIKER, SECURITY, PLAYER, GOLFERS, GANG9, COPS_BOAT, FIRST_CAR_RATING = NORMAL, FIRST_BOAT_RATING = LEISUREBOAT, FIRST_GANG_CAR_RATING = CUBAN, NUM_CAR_CLASSES = MOTORBIKE - FIRST_CAR_RATING + 1, NUM_BOAT_CLASSES = WORKERBOAT - FIRST_BOAT_RATING + 1, NUM_GANG_CAR_CLASSES = GANG9 - FIRST_GANG_CAR_RATING + 1, TOTAL_CUSTOM_CLASSES = NUM_CAR_CLASSES + NUM_BOAT_CLASSES }; static void SwitchVehicleToRealPhysics(CVehicle*); static void AddToCarArray(int32 id, int32 vehclass); static void UpdateCarCount(CVehicle*, bool); static int32 ChooseCarModel(int32 vehclass); static bool JoinCarWithRoadSystemGotoCoors(CVehicle*, CVector, bool); static void JoinCarWithRoadSystem(CVehicle*); static void UpdateCarOnRails(CVehicle*); static bool MapCouldMoveInThisArea(float x, float y); static void ScanForPedDanger(CVehicle *veh); static void RemoveFromInterestingVehicleList(CVehicle*); static void GenerateRandomCars(void); static void GenerateOneRandomCar(void); static void GenerateEmergencyServicesCar(void); static int32 ChooseModel(CZoneInfo*, int*); static int32 ChoosePoliceCarModel(void); static int32 ChooseGangCarModel(int32 gang); static void RemoveDistantCars(void); static void PossiblyRemoveVehicle(CVehicle*); static bool IsThisVehicleInteresting(CVehicle*); static void RegisterVehicleOfInterest(CVehicle*); static int32 CountCarsOfType(int32 mi); static void SlowCarOnRailsDownForTrafficAndLights(CVehicle*); static bool PickNextNodeAccordingStrategy(CVehicle*); static void DragCarToPoint(CVehicle*, CVector*); static float FindMaximumSpeedForThisCarInTraffic(CVehicle*); static void SlowCarDownForCarsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float); static void SlowCarDownForPedsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float); static void SlowCarDownForOtherCar(CEntity*, CVehicle*, float*, float); static float TestCollisionBetween2MovingRects(CVehicle*, CVehicle*, float, float, CVector*, CVector*, uint8); static float FindAngleToWeaveThroughTraffic(CVehicle*, CPhysical*, float, float); static void WeaveThroughCarsSectorList(CPtrList&, CVehicle*, CPhysical*, float, float, float, float, float*, float*); static void WeaveForOtherCar(CEntity*, CVehicle*, float*, float*); static void WeaveThroughPedsSectorList(CPtrList&, CVehicle*, CPhysical*, float, float, float, float, float*, float*); static void WeaveForPed(CEntity*, CVehicle*, float*, float*); static void WeaveThroughObjectsSectorList(CPtrList&, CVehicle*, float, float, float, float, float*, float*); static void WeaveForObject(CEntity*, CVehicle*, float*, float*); #ifdef FIX_PATHFIND_BUG static void PickNextNodeToChaseCar(CVehicle*, float, float, float, CVehicle*); #else static void PickNextNodeToChaseCar(CVehicle*, float, float, CVehicle*); #endif static bool PickNextNodeToFollowPath(CVehicle*); static void PickNextNodeRandomly(CVehicle*); static uint8 FindPathDirection(int32, int32, int32); static void Init(void); static void ReInit(void); static float FindSpeedMultiplier(float, float, float, float); static void SteerAICarWithPhysics(CVehicle*); static void SteerAICarWithPhysics_OnlyMission(CVehicle*, float*, float*, float*, bool*); static float FindMaxSteerAngle(CVehicle*); static void SteerAICarWithPhysicsFollowPath(CVehicle*, float*, float*, float*, bool*); static void SteerAICarWithPhysicsHeadingForTarget(CVehicle*, CPhysical*, float, float, float*, float*, float*, bool*); static void SteerAICarWithPhysicsTryingToBlockTarget(CVehicle*, float, float, float, float, float*, float*, float*, bool*); static void SteerAICarWithPhysicsTryingToBlockTarget_Stop(CVehicle*, float, float, float, float, float*, float*, float*, bool*); static bool ThisRoadObjectCouldMove(int16); static void ClearInterestingVehicleList(); static void FindLinksToGoWithTheseNodes(CVehicle*); static bool GenerateOneEmergencyServicesCar(uint32, CVector); static float FindSpeedMultiplierWithSpeedFromNodes(int8); static int32 ChooseBoatModel(int32); static int32 ChooseBoatRating(CZoneInfo* pZoneInfo); static int32 ChooseCarRating(CZoneInfo* pZoneInfo); static void AddToLoadedVehicleArray(int32 mi, int32 rating, int32 freq); static void RemoveFromLoadedVehicleArray(int32 mi, int32 rating); static int32 ChooseCarModelToLoad(int32 rating); static bool BoatWithTallMast(int32 mi); static void RemoveCarsIfThePoolGetsFull(void); static void SteerAIBoatWithPhysicsHeadingForTarget(CVehicle*, float, float, float*, float*, float*); static void SteerAIHeliTowardsTargetCoors(CAutomobile*); static void SteerAIPlaneTowardsTargetCoors(CAutomobile*); static void SteerAIBoatWithPhysicsAttackingPlayer(CVehicle*, float*, float*, float*, bool*); static void SteerAICarBlockingPlayerForwardAndBack(CVehicle*, float*, float*, float*, bool*); static float GetPositionAlongCurrentCurve(CVehicle* pVehicle) { uint32 timeInCurve = CTimer::GetTimeInMilliseconds() - pVehicle->AutoPilot.m_nTimeEnteredCurve; return (float)timeInCurve / pVehicle->AutoPilot.m_nTimeToSpendOnCurrentCurve; } static float LimitRadianAngle(float angle) { while (angle < -PI) angle += TWOPI; while (angle > PI) angle -= TWOPI; return angle; } static bool bMadDriversCheat; static int32 NumLawEnforcerCars; static int32 NumAmbulancesOnDuty; static int32 NumFiretrucksOnDuty; static int32 NumRandomCars; static int32 NumMissionCars; static int32 NumParkedCars; static int32 NumPermanentCars; static bool bCarsGeneratedAroundCamera; static float CarDensityMultiplier; static int8 CountDownToCarsAtStart; static int32 MaxNumberOfCarsInUse; static uint32 LastTimeLawEnforcerCreated; static uint32 LastTimeFireTruckCreated; static uint32 LastTimeAmbulanceCreated; static int32 TotalNumOfCarsOfRating[TOTAL_CUSTOM_CLASSES]; static int32 CarArrays[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; static int32 MiamiViceCycle; static uint32 LastTimeMiamiViceGenerated; static int32 NumRequestsOfCarRating[TOTAL_CUSTOM_CLASSES]; static int32 NumOfLoadedCarsOfRating[TOTAL_CUSTOM_CLASSES]; static int32 CarFreqArrays[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; static int32 LoadedCarsArray[TOTAL_CUSTOM_CLASSES][MAX_CAR_MODELS_IN_ARRAY]; }; extern CVehicle* apCarsToKeep[MAX_CARS_TO_KEEP]; ================================================ FILE: src/control/Curves.cpp ================================================ #include "common.h" #include "Curves.h" float CCurves::CalcSpeedScaleFactor(CVector* pPoint1, CVector* pPoint2, float dir1X, float dir1Y, float dir2X, float dir2Y) { CVector2D dir1(dir1X, dir1Y); CVector2D dir2(dir2X, dir2Y); float distance = (*pPoint1 - *pPoint2).Magnitude2D(); float dp = DotProduct2D(dir1, dir2); if (dp > 0.9f) return distance + Abs((pPoint1->x * dir1Y - pPoint1->y * dir1X) - (pPoint2->x * dir1Y - pPoint2->y * dir1X)); else return ((1.0f - dp) * 0.25f + 1.0f) * distance; } void CCurves::CalcCurvePoint(CVector* pPos1, CVector* pPos2, CVector* pDir1, CVector* pDir2, float between, int32 timeOnCurve, CVector* pOutPos, CVector* pOutDir) { float actualFactor = CalcSpeedScaleFactor(pPos1, pPos2, pDir1->x, pDir1->y, pDir2->x, pDir2->y); CVector2D dir1 = *pDir1 * actualFactor; CVector2D dir2 = *pDir2 * actualFactor; float t1 = Abs(DotProduct2D(*pPos1 - *pPos2, *pDir1)); float t2 = Abs(DotProduct2D(*pPos2 - *pPos1, *pDir2)); float curveCoef; if (t1 > t2) { if (between < (t1 - t2) / (t1 + t2)) curveCoef = 0.0f; else curveCoef = 0.5f - 0.5f * Cos(3.1415f * (t1 + t2) / (2 * t2) * (between - (t1 - t2) / (t1 + t2))); } else { if (2 * t1 / (t1 + t2) < between) curveCoef = 1.0f; else curveCoef = 0.5f - 0.5f * Cos(3.1415f * between * (t1 + t2) / (2 * t1)); } *pOutPos = CVector( (pPos1->x + between * dir1.x) * (1.0f - curveCoef) + (pPos2->x - (1 - between) * dir2.x) * curveCoef, (pPos1->y + between * dir1.y) * (1.0f - curveCoef) + (pPos2->y - (1 - between) * dir2.y) * curveCoef, 0.0f); *pOutDir = CVector( (dir1.x * (1.0f - curveCoef) + dir2.x * curveCoef) / (timeOnCurve * 0.001f), (dir1.y * (1.0f - curveCoef) + dir2.y * curveCoef) / (timeOnCurve * 0.001f), 0.0f); } ================================================ FILE: src/control/Curves.h ================================================ #pragma once class CVector; class CCurves { public: static float CalcSpeedScaleFactor(CVector*, CVector*, float, float, float, float); static void CalcCurvePoint(CVector*, CVector*, CVector*, CVector*, float, int32, CVector*, CVector*); }; ================================================ FILE: src/control/Darkel.cpp ================================================ #include "common.h" #include "main.h" #include "Darkel.h" #include "PlayerPed.h" #include "Wanted.h" #include "Timer.h" #include "DMAudio.h" #include "Population.h" #include "Replay.h" #include "Weapon.h" #include "World.h" #include "Stats.h" #include "Font.h" #include "Text.h" #include "Vehicle.h" #include "GameLogic.h" #define FRENZY_ANY_PED -1 #define FRENZY_ANY_CAR -2 int32 CDarkel::TimeLimit; int32 CDarkel::PreviousTime; int32 CDarkel::TimeOfFrenzyStart; int32 CDarkel::WeaponType; int32 CDarkel::AmmoInterruptedWeapon; int32 CDarkel::KillsNeeded; int32 CDarkel::InterruptedWeaponType; int32 CDarkel::InterruptedWeaponSelected; /* * TODO: Collect timer/kill counter RGBA colors on top like in Hud/Frontend. * bStandardSoundAndMessages is a completely beta thing, * makes game handle sounds & messages instead of SCM (just like in GTA2) * but it's never been used in the game. Has unused sliding text when frenzy completed etc. */ bool CDarkel::bStandardSoundAndMessages; bool CDarkel::bNeedHeadShot; bool CDarkel::bProperKillFrenzy; uint16 CDarkel::Status; uint16 CDarkel::RegisteredKills[NUM_DEFAULT_MODELS]; int32 CDarkel::ModelToKill; int32 CDarkel::ModelToKill2; int32 CDarkel::ModelToKill3; int32 CDarkel::ModelToKill4; wchar *CDarkel::pStartMessage; uint8 CDarkel::CalcFade(uint32 time, uint32 start, uint32 end) { if (time >= start && time <= end) { if (time >= start + 500) { if (time <= end - 500) return 255; else return 255 * (end - time) / 500; } else return 255 * (time - start) / 500; } else return 0; } void CDarkel::DrawMessages() { if (CReplay::IsPlayingBack()) return; switch (Status) { case KILLFRENZY_ONGOING: { CFont::SetJustifyOff(); CFont::SetBackgroundOff(); #ifdef FIX_BUGS CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 30)); #else CFont::SetCentreSize(SCREEN_WIDTH - 30); #endif CFont::SetCentreOn(); CFont::SetPropOn(); uint32 timePassedSinceStart = CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart; if (bStandardSoundAndMessages) { if (timePassedSinceStart >= 3000 && timePassedSinceStart < 11000) { #ifdef FIX_BUGS CFont::SetScale(SCREEN_SCALE_X(1.3f), SCREEN_SCALE_Y(1.3f)); #else CFont::SetScale(1.3f, 1.3f); #endif CFont::SetJustifyOff(); CFont::SetColor(CRGBA(255, 255, 128, CalcFade(timePassedSinceStart, 3000, 11000))); CFont::SetFontStyle(FONT_STANDARD); if (pStartMessage) { CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, pStartMessage); } } } else { if (timePassedSinceStart < 8000) { #ifdef FIX_BUGS CFont::SetScale(SCREEN_SCALE_X(1.3f), SCREEN_SCALE_Y(1.3f)); #else CFont::SetScale(1.3f, 1.3f); #endif CFont::SetJustifyOff(); CFont::SetColor(CRGBA(255, 255, 128, CalcFade(timePassedSinceStart, 0, 8000))); CFont::SetFontStyle(FONT_STANDARD); if (pStartMessage) { CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, pStartMessage); } } } #ifdef FIX_BUGS CFont::SetScale(SCREEN_SCALE_X(0.75f), SCREEN_SCALE_Y(1.5f)); #else CFont::SetScale(0.75f, 1.5f); #endif CFont::SetCentreOff(); CFont::SetRightJustifyOn(); CFont::SetFontStyle(FONT_HEADING); if (TimeLimit >= 0) { uint32 timeLeft = TimeLimit - (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart); sprintf(gString, "%d:%02d", timeLeft / 60000, timeLeft % 60000 / 1000); AsciiToUnicode(gString, gUString); if (timeLeft > 4000 || CTimer::GetFrameCounter() & 1) { CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(109.0f), gUString); CFont::SetColor(CRGBA(0, 207, 133, 255)); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f), SCREEN_SCALE_Y(108.0f), gUString); } } sprintf(gString, "%d", (KillsNeeded >= 0 ? KillsNeeded : 0)); AsciiToUnicode(gString, gUString); CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(144.0f), gUString); CFont::SetColor(CRGBA(156, 91, 40, 255)); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(34.0f), SCREEN_SCALE_Y(143.0f), gUString); break; } case KILLFRENZY_PASSED: { if (bStandardSoundAndMessages) { uint32 timePassedSinceStart = CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart; if (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart < 5000) { CFont::SetBackgroundOff(); #ifdef FIX_BUGS CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 20)); #else CFont::SetCentreSize(SCREEN_WIDTH - 20); #endif CFont::SetCentreOn(); #ifdef FIX_BUGS CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f)); #else CFont::SetScale(1.5f, 1.5f); #endif CFont::SetJustifyOff(); CFont::SetColor(CRGBA(128, 255, 128, CalcFade(timePassedSinceStart, 0, 5000))); CFont::SetFontStyle(FONT_STANDARD); #ifdef FIX_BUGS int y = SCREEN_HEIGHT / 2 + SCREEN_SCALE_Y(25.0f - timePassedSinceStart * 0.01f); #else int y = (SCREEN_HEIGHT / 2 + 25) - (timePassedSinceStart * 0.01f); #endif CFont::PrintString(SCREEN_WIDTH / 2, y, TheText.Get("KF_3")); } } break; } default: break; } } void CDarkel::Init() { Status = KILLFRENZY_NONE; } uint16 CDarkel::QueryModelsKilledByPlayer(int32 modelId) { return RegisteredKills[modelId]; } bool CDarkel::FrenzyOnGoing() { return Status == KILLFRENZY_ONGOING; } uint16 CDarkel::ReadStatus() { return Status; } void CDarkel::RegisterCarBlownUpByPlayer(CVehicle *vehicle) { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return; #endif if (FrenzyOnGoing()) { int32 model = vehicle->GetModelIndex(); if (ModelToKill == FRENZY_ANY_CAR || ModelToKill == model || ModelToKill2 == model || ModelToKill3 == model || ModelToKill4 == model) { KillsNeeded--; DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_CAR_BLOWN, 0); } } RegisteredKills[vehicle->GetModelIndex()]++; switch (vehicle->GetVehicleAppearance()) { case VEHICLE_APPEARANCE_CAR: case VEHICLE_APPEARANCE_BIKE: CStats::CarsExploded++;; break; case VEHICLE_APPEARANCE_HELI: case VEHICLE_APPEARANCE_PLANE: CStats::HelisDestroyed++; break; case VEHICLE_APPEARANCE_BOAT: CStats::BoatsExploded++; break; } } void CDarkel::RegisterKillByPlayer(CPed *victim, eWeaponType weapon, bool headshot) { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return; #endif if (FrenzyOnGoing() && (weapon == WeaponType || weapon == WEAPONTYPE_EXPLOSION || weapon == WEAPONTYPE_UZI_DRIVEBY && WeaponType == WEAPONTYPE_UZI || weapon == WEAPONTYPE_RAMMEDBYCAR && WeaponType == WEAPONTYPE_RUNOVERBYCAR || weapon == WEAPONTYPE_RUNOVERBYCAR && WeaponType == WEAPONTYPE_RAMMEDBYCAR || weapon == WEAPONTYPE_FLAMETHROWER && WeaponType == WEAPONTYPE_MOLOTOV)) { int32 model = victim->GetModelIndex(); if (ModelToKill == FRENZY_ANY_PED || ModelToKill == model || ModelToKill2 == model || ModelToKill3 == model || ModelToKill4 == model) { if (!bNeedHeadShot || headshot) { KillsNeeded--; DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_KILL, 0); } } } CStats::PeopleKilledByPlayer++; RegisteredKills[victim->GetModelIndex()]++; CStats::PedsKilledOfThisType[victim->bChrisCriminal ? PEDTYPE_CRIMINAL : victim->m_nPedType]++; if (headshot) CStats::HeadsPopped++; CStats::KillsSinceLastCheckpoint++; } void CDarkel::RegisterKillNotByPlayer(CPed* victim, eWeaponType weapontype) { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return; #endif CStats::PeopleKilledByOthers++; } void CDarkel::ResetModelsKilledByPlayer() { for (int i = 0; i < NUM_DEFAULT_MODELS; i++) RegisteredKills[i] = 0; } void CDarkel::ResetOnPlayerDeath() { if (Status != KILLFRENZY_ONGOING) return; CPopulation::m_AllRandomPedsThisType = -1; Status = KILLFRENZY_FAILED; TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds(); DealWithWeaponChangeAtEndOfFrenzy(); } void CDarkel::StartFrenzy(eWeaponType weaponType, int32 time, uint16 kill, int32 modelId0, wchar *text, int32 modelId2, int32 modelId3, int32 modelId4, bool standardSound, bool needHeadShot) { CGameLogic::ClearShortCut(); CGameLogic::RemoveShortCutDropOffPointForMission(); eWeaponType fixedWeapon; if (weaponType == WEAPONTYPE_UZI_DRIVEBY) fixedWeapon = WEAPONTYPE_UZI; else fixedWeapon = weaponType; WeaponType = weaponType; Status = KILLFRENZY_ONGOING; KillsNeeded = kill; ModelToKill = modelId0; ModelToKill2 = modelId2; ModelToKill3 = modelId3; ModelToKill4 = modelId4; pStartMessage = text; if (text == TheText.Get("PAGE_00")) { CDarkel::bProperKillFrenzy = true; CDarkel::pStartMessage = nil; } else bProperKillFrenzy = false; bStandardSoundAndMessages = standardSound; bNeedHeadShot = needHeadShot; TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds(); TimeLimit = time; PreviousTime = time / 1000; CPlayerPed *player = FindPlayerPed(); if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) { InterruptedWeaponSelected = player->GetWeapon()->m_eWeaponType; #if (defined FIX_BUGS || !defined GTA_PS2) player->RemoveWeaponAnims(InterruptedWeaponSelected, -1000.0f); #endif InterruptedWeaponType = player->GetWeapon(player->GetWeaponSlot(fixedWeapon)).m_eWeaponType; AmmoInterruptedWeapon = player->GetWeapon(player->GetWeaponSlot(fixedWeapon)).m_nAmmoTotal; if (InterruptedWeaponType) CModelInfo::GetModelInfo(CWeaponInfo::GetWeaponInfo((eWeaponType)InterruptedWeaponType)->m_nModelId)->AddRef(); #if (!defined FIX_BUGS && defined GTA_PS2) player->RemoveWeaponAnims(InterruptedWeaponSelected, -1000.0f); #endif player->GiveWeapon(fixedWeapon, 30000); player->SetCurrentWeapon(fixedWeapon); player->MakeChangesForNewWeapon(player->m_nSelectedWepSlot); if (FindPlayerVehicle()) { player->SetCurrentWeapon(FindPlayerPed()->m_nSelectedWepSlot); player->SetAmmo(fixedWeapon, Min(player->GetWeapon()->m_nAmmoTotal, CWeaponInfo::GetWeaponInfo(player->GetWeapon()->m_eWeaponType)->m_nAmountofAmmunition)); player->ClearWeaponTarget(); } } if (CDarkel::bStandardSoundAndMessages) DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_START, 0); } void CDarkel::Update() { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return; #endif if (Status != KILLFRENZY_ONGOING) return; int32 FrameTime = TimeLimit - (CTimer::GetTimeInMilliseconds() - TimeOfFrenzyStart); if (FrameTime > 0 || TimeLimit < 0) { DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_ONGOING, FrameTime); int32 PrevTime = FrameTime / 1000; if (PrevTime != PreviousTime) { if (PreviousTime < 12) DMAudio.PlayFrontEndSound(SOUND_CLOCK_TICK, PrevTime); PreviousTime = PrevTime; } } else { CPopulation::m_AllRandomPedsThisType = -1; Status = KILLFRENZY_FAILED; TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds(); DealWithWeaponChangeAtEndOfFrenzy(); if (bStandardSoundAndMessages) DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_FAILED, 0); } if (KillsNeeded <= 0) { CPopulation::m_AllRandomPedsThisType = -1; Status = KILLFRENZY_PASSED; if (bProperKillFrenzy) CStats::AnotherKillFrenzyPassed(); TimeOfFrenzyStart = CTimer::GetTimeInMilliseconds(); FindPlayerPed()->m_pWanted->SetWantedLevel(0); DealWithWeaponChangeAtEndOfFrenzy(); if (bStandardSoundAndMessages) DMAudio.PlayFrontEndSound(SOUND_RAMPAGE_PASSED, 0); } } void CDarkel::DealWithWeaponChangeAtEndOfFrenzy() { eWeaponType fixedWeapon; if (WeaponType == WEAPONTYPE_UZI_DRIVEBY) fixedWeapon = WEAPONTYPE_UZI; else fixedWeapon = (eWeaponType)WeaponType; if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS && InterruptedWeaponType) CModelInfo::GetModelInfo(CWeaponInfo::GetWeaponInfo((eWeaponType)InterruptedWeaponType)->m_nModelId)->RemoveRef(); if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) { int slot = CWeaponInfo::GetWeaponInfo(fixedWeapon)->m_nWeaponSlot; FindPlayerPed()->RemoveWeaponModel(FindPlayerPed()->GetWeapon(slot).GetInfo()->m_nModelId); FindPlayerPed()->GetWeapon(slot).m_eWeaponType = WEAPONTYPE_UNARMED; FindPlayerPed()->GetWeapon(slot).m_nAmmoTotal = 0; FindPlayerPed()->GetWeapon(slot).m_nAmmoInClip = 0; FindPlayerPed()->GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; FindPlayerPed()->RemoveWeaponAnims(fixedWeapon, -1000.0f); CModelInfo::GetModelInfo(CWeaponInfo::GetWeaponInfo(fixedWeapon)->m_nModelId)->RemoveRef(); } CPlayerPed* player = FindPlayerPed(); if (fixedWeapon < WEAPONTYPE_TOTALWEAPONS) { player->m_nSelectedWepSlot = CWeaponInfo::GetWeaponInfo((eWeaponType)InterruptedWeaponSelected)->m_nWeaponSlot; player->GiveWeapon((eWeaponType)InterruptedWeaponType, AmmoInterruptedWeapon, true); } if (FindPlayerVehicle()) { player->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(player->GetWeapon()->m_eWeaponType)->m_nModelId); if (FindPlayerPed()->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_eWeaponType) FindPlayerPed()->m_nSelectedWepSlot = WEAPONSLOT_SUBMACHINEGUN; else FindPlayerPed()->m_nSelectedWepSlot = WEAPONSLOT_UNARMED; player->SetCurrentWeapon(FindPlayerPed()->m_nSelectedWepSlot); player->MakeChangesForNewWeapon(player->m_currentWeapon); player->RemoveDrivebyAnims(); } } ================================================ FILE: src/control/Darkel.h ================================================ #pragma once #include "ModelIndices.h" #include "WeaponType.h" class CVehicle; class CPed; enum { KILLFRENZY_NONE, KILLFRENZY_ONGOING, KILLFRENZY_PASSED, KILLFRENZY_FAILED, }; class CDarkel { private: static int32 TimeLimit; static int32 PreviousTime; static int32 TimeOfFrenzyStart; static int32 WeaponType; static int32 AmmoInterruptedWeapon; static int32 KillsNeeded; static int32 InterruptedWeaponType; static int32 InterruptedWeaponSelected; static bool bStandardSoundAndMessages; static bool bNeedHeadShot; static bool bProperKillFrenzy; static uint16 Status; static uint16 RegisteredKills[NUM_DEFAULT_MODELS]; static int32 ModelToKill; static int32 ModelToKill2; static int32 ModelToKill3; static int32 ModelToKill4; static wchar *pStartMessage; public: static uint8 CalcFade(uint32 time, uint32 min, uint32 max); static void DrawMessages(void); static bool FrenzyOnGoing(); static void Init(); static uint16 QueryModelsKilledByPlayer(int32 modelId); static uint16 ReadStatus(); static void RegisterCarBlownUpByPlayer(CVehicle *vehicle); static void RegisterKillByPlayer(CPed *victim, eWeaponType weapontype, bool headshot = false); static void RegisterKillNotByPlayer(CPed* victim, eWeaponType weapontype); static void ResetModelsKilledByPlayer(); static void ResetOnPlayerDeath(); static void StartFrenzy(eWeaponType weaponType, int32 time, uint16 kill, int32 modelId0, wchar *text, int32 modelId2, int32 modelId3, int32 modelId4, bool standardSound, bool needHeadShot); static void Update(); static void DealWithWeaponChangeAtEndOfFrenzy(); }; ================================================ FILE: src/control/GameLogic.cpp ================================================ #include "common.h" #include "GameLogic.h" #include "Clock.h" #include "Stats.h" #include "Pickups.h" #include "Timer.h" #include "Streaming.h" #include "CutsceneMgr.h" #include "World.h" #include "PlayerPed.h" #include "Wanted.h" #include "Camera.h" #include "Messages.h" #include "CarCtrl.h" #include "Restart.h" #include "Pad.h" #include "References.h" #include "Fire.h" #include "Script.h" #include "Garages.h" #include "Population.h" #include "General.h" #include "DMAudio.h" #include "Radar.h" #include "Pools.h" #include "Hud.h" #include "Particle.h" #include "ColStore.h" #include "Automobile.h" #include "MBlur.h" #include "screendroplets.h" uint8 CGameLogic::ActivePlayers; uint8 CGameLogic::ShortCutState; CAutomobile* CGameLogic::pShortCutTaxi; uint32 CGameLogic::NumAfterDeathStartPoints; CVector CGameLogic::ShortCutStart; float CGameLogic::ShortCutStartOrientation; CVector CGameLogic::ShortCutDestination; float CGameLogic::ShortCutDestinationOrientation; uint32 CGameLogic::ShortCutTimer; CVector CGameLogic::AfterDeathStartPoints[NUM_SHORTCUT_START_POINTS]; float CGameLogic::AfterDeathStartPointOrientation[NUM_SHORTCUT_START_POINTS]; CVector CGameLogic::ShortCutDropOffForMission; float CGameLogic::ShortCutDropOffOrientationForMission; bool CGameLogic::MissionDropOffReadyToBeUsed; #define SHORTCUT_TAXI_COST (9) #define TOTAL_BUSTED_AUDIO (28) void CGameLogic::InitAtStartOfGame() { ActivePlayers = 1; ShortCutState = SHORTCUT_NONE; pShortCutTaxi = nil; NumAfterDeathStartPoints = 0; } void CGameLogic::PassTime(uint32 time) { int32 minutes, hours, days; minutes = time + CClock::GetMinutes(); hours = CClock::GetHours(); for (; minutes >= 60; minutes -= 60) hours++; if (hours > 23) { days = CStats::DaysPassed; for (; hours >= 24; hours -= 24) days++; CStats::DaysPassed = days; } CClock::SetGameClock(hours, minutes); CPickups::PassTime(time * 1000); } void CGameLogic::SortOutStreamingAndMemory(const CVector &pos) { CTimer::Stop(); CStreaming::FlushRequestList(); CStreaming::DeleteRwObjectsAfterDeath(pos); CStreaming::RemoveUnusedModelsInLoadedList(); CGame::DrasticTidyUpMemory(true); CWorld::Players[CWorld::PlayerInFocus].m_pPed->Undress("player"); CStreaming::LoadSceneCollision(pos); CStreaming::LoadScene(pos); CWorld::Players[CWorld::PlayerInFocus].m_pPed->Dress(); CTimer::Update(); } void CGameLogic::Update() { CVector vecRestartPos; float fRestartFloat; if (CCutsceneMgr::IsCutsceneProcessing()) return; UpdateShortCut(); CPlayerInfo &pPlayerInfo = CWorld::Players[CWorld::PlayerInFocus]; switch (pPlayerInfo.m_WBState) { case WBSTATE_PLAYING: if (pPlayerInfo.m_pPed->m_nPedState == PED_DEAD) { pPlayerInfo.m_pPed->ClearAdrenaline(); pPlayerInfo.KillPlayer(); } if (pPlayerInfo.m_pPed->m_nPedState == PED_ARRESTED) { pPlayerInfo.m_pPed->ClearAdrenaline(); pPlayerInfo.ArrestPlayer(); } break; case WBSTATE_WASTED: #ifdef MISSION_REPLAY if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > AddExtraDeathDelay() + 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= AddExtraDeathDelay() + 0x800)) { #else if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= 0x800)) { #endif TheCamera.SetFadeColour(200, 200, 200); TheCamera.Fade(2.0f, FADE_OUT); } #ifdef MISSION_REPLAY if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= AddExtraDeathDelay() + 0x1000) { #else if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= 0x1000) { #endif pPlayerInfo.m_WBState = WBSTATE_PLAYING; if (pPlayerInfo.m_bGetOutOfHospitalFree) { pPlayerInfo.m_bGetOutOfHospitalFree = false; } else { pPlayerInfo.m_nMoney = Max(0, pPlayerInfo.m_nMoney - 100); pPlayerInfo.m_pPed->ClearWeapons(); } if (pPlayerInfo.m_pPed->bInVehicle) { CVehicle *pVehicle = pPlayerInfo.m_pPed->m_pMyVehicle; if (pVehicle != nil) { if (pVehicle->pDriver == pPlayerInfo.m_pPed) { pVehicle->pDriver = nil; if (pVehicle->GetStatus() != STATUS_WRECKED) pVehicle->SetStatus(STATUS_ABANDONED); } else pVehicle->RemovePassenger(pPlayerInfo.m_pPed); } } CEventList::Initialise(); #ifdef SCREEN_DROPLETS ScreenDroplets::Initialise(); #endif CMessages::ClearMessages(); CCarCtrl::ClearInterestingVehicleList(); CWorld::ClearExcitingStuffFromArea(pPlayerInfo.GetPos(), 4000.0f, 1); CRestart::FindClosestHospitalRestartPoint(pPlayerInfo.GetPos(), &vecRestartPos, &fRestartFloat); CRestart::OverrideHospitalLevel = LEVEL_GENERIC; CRestart::OverridePoliceStationLevel = LEVEL_GENERIC; PassTime(720); RestorePlayerStuffDuringResurrection(pPlayerInfo.m_pPed, vecRestartPos, fRestartFloat); AfterDeathArrestSetUpShortCutTaxi(); SortOutStreamingAndMemory(pPlayerInfo.GetPos()); TheCamera.m_fCamShakeForce = 0.0f; TheCamera.SetMotionBlur(0, 0, 0, 0, MOTION_BLUR_NONE); CPad::GetPad(0)->StopShaking(0); CReferences::RemoveReferencesToPlayer(); CPopulation::m_CountDownToPedsAtStart = 10; CCarCtrl::CountDownToCarsAtStart = 10; CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls = PLAYERCONTROL_ENABLED; if (CRestart::bFadeInAfterNextDeath) { TheCamera.SetFadeColour(200, 200, 200); TheCamera.Fade(4.0f, FADE_IN); } else CRestart::bFadeInAfterNextDeath = true; } break; case WBSTATE_BUSTED: #ifdef MISSION_REPLAY if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > AddExtraDeathDelay() + 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= AddExtraDeathDelay() + 0x800)) { #else if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= 0x800)) { #endif TheCamera.SetFadeColour(0, 0, 0); TheCamera.Fade(2.0f, FADE_OUT); } if (!CTheScripts::IsPlayerOnAMission() && pPlayerInfo.m_nBustedAudioStatus == 0) { if (CGeneral::GetRandomNumberInRange(0, 4) == 0) pPlayerInfo.m_nBustedAudioStatus = BUSTEDAUDIO_DONE; else { pPlayerInfo.m_nBustedAudioStatus = BUSTEDAUDIO_LOADING; char name[12]; sprintf(name, pPlayerInfo.m_nCurrentBustedAudio >= 10 ? "bust_%d" : "bust_0%d", pPlayerInfo.m_nCurrentBustedAudio); DMAudio.ClearMissionAudio(0); DMAudio.PreloadMissionAudio(0, name); pPlayerInfo.m_nCurrentBustedAudio = pPlayerInfo.m_nCurrentBustedAudio % TOTAL_BUSTED_AUDIO + 1; } } if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 4000 && pPlayerInfo.m_nBustedAudioStatus == BUSTEDAUDIO_LOADING && DMAudio.GetMissionAudioLoadingStatus(0) == 1) { DMAudio.PlayLoadedMissionAudio(0); pPlayerInfo.m_nBustedAudioStatus = BUSTEDAUDIO_DONE; } #ifdef MISSION_REPLAY if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= AddExtraDeathDelay() + 0x1000) { #else if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= 0x1000) { #endif #ifdef FIX_BUGS pPlayerInfo.m_nBustedAudioStatus = BUSTEDAUDIO_NONE; #endif pPlayerInfo.m_WBState = WBSTATE_PLAYING; int takeMoney; switch (pPlayerInfo.m_pPed->m_pWanted->GetWantedLevel()) { case 0: case 1: takeMoney = 100; break; case 2: takeMoney = 200; break; case 3: takeMoney = 400; break; case 4: takeMoney = 600; break; case 5: takeMoney = 900; break; case 6: takeMoney = 1500; break; } if (pPlayerInfo.m_bGetOutOfJailFree) { pPlayerInfo.m_bGetOutOfJailFree = false; } else { pPlayerInfo.m_nMoney = Max(0, pPlayerInfo.m_nMoney - takeMoney); pPlayerInfo.m_pPed->ClearWeapons(); } if (pPlayerInfo.m_pPed->bInVehicle) { CVehicle *pVehicle = pPlayerInfo.m_pPed->m_pMyVehicle; if (pVehicle != nil) { if (pVehicle->pDriver == pPlayerInfo.m_pPed) { pVehicle->pDriver = nil; if (pVehicle->GetStatus() != STATUS_WRECKED) pVehicle->SetStatus(STATUS_ABANDONED); } else pVehicle->RemovePassenger(pPlayerInfo.m_pPed); } } CEventList::Initialise(); #ifdef SCREEN_DROPLETS ScreenDroplets::Initialise(); #endif CMessages::ClearMessages(); CCarCtrl::ClearInterestingVehicleList(); CWorld::ClearExcitingStuffFromArea(pPlayerInfo.GetPos(), 4000.0f, 1); CRestart::FindClosestPoliceRestartPoint(pPlayerInfo.GetPos(), &vecRestartPos, &fRestartFloat); CRestart::OverrideHospitalLevel = LEVEL_GENERIC; CRestart::OverridePoliceStationLevel = LEVEL_GENERIC; PassTime(720); RestorePlayerStuffDuringResurrection(pPlayerInfo.m_pPed, vecRestartPos, fRestartFloat); AfterDeathArrestSetUpShortCutTaxi(); pPlayerInfo.m_pPed->ClearWeapons(); SortOutStreamingAndMemory(pPlayerInfo.GetPos()); TheCamera.m_fCamShakeForce = 0.0f; TheCamera.SetMotionBlur(0, 0, 0, 0, MOTION_BLUR_NONE); CPad::GetPad(0)->StopShaking(0); CReferences::RemoveReferencesToPlayer(); CPopulation::m_CountDownToPedsAtStart = 10; CCarCtrl::CountDownToCarsAtStart = 10; CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls = PLAYERCONTROL_ENABLED; if (CRestart::bFadeInAfterNextArrest) { TheCamera.SetFadeColour(0, 0, 0); TheCamera.Fade(4.0f, FADE_IN); } else CRestart::bFadeInAfterNextArrest = true; } break; case WBSTATE_FAILED_CRITICAL_MISSION: #ifdef MISSION_REPLAY if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > AddExtraDeathDelay() + 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= AddExtraDeathDelay() + 0x800)) { #else if ((CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime > 0x800) && (CTimer::GetPreviousTimeInMilliseconds() - pPlayerInfo.m_nWBTime <= 0x800)) { #endif TheCamera.SetFadeColour(0, 0, 0); TheCamera.Fade(2.0f, FADE_OUT); } #ifdef MISSION_REPLAY if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= AddExtraDeathDelay() + 0x1000) { #else if (CTimer::GetTimeInMilliseconds() - pPlayerInfo.m_nWBTime >= 0x1000) { #endif pPlayerInfo.m_WBState = WBSTATE_PLAYING; if (pPlayerInfo.m_pPed->bInVehicle) { CVehicle *pVehicle = pPlayerInfo.m_pPed->m_pMyVehicle; if (pVehicle != nil) { if (pVehicle->pDriver == pPlayerInfo.m_pPed) { pVehicle->pDriver = nil; if (pVehicle->GetStatus() != STATUS_WRECKED) pVehicle->SetStatus(STATUS_ABANDONED); } else pVehicle->RemovePassenger(pPlayerInfo.m_pPed); } } CEventList::Initialise(); #ifdef SCREEN_DROPLETS ScreenDroplets::Initialise(); #endif CMessages::ClearMessages(); CCarCtrl::ClearInterestingVehicleList(); CWorld::ClearExcitingStuffFromArea(pPlayerInfo.GetPos(), 4000.0f, 1); CRestart::FindClosestPoliceRestartPoint(pPlayerInfo.GetPos(), &vecRestartPos, &fRestartFloat); CRestart::OverridePoliceStationLevel = LEVEL_GENERIC; CRestart::OverrideHospitalLevel = LEVEL_GENERIC; RestorePlayerStuffDuringResurrection(pPlayerInfo.m_pPed, vecRestartPos, fRestartFloat); SortOutStreamingAndMemory(pPlayerInfo.GetPos()); TheCamera.m_fCamShakeForce = 0.0f; TheCamera.SetMotionBlur(0, 0, 0, 0, MOTION_BLUR_NONE); CPad::GetPad(0)->StopShaking(0); CReferences::RemoveReferencesToPlayer(); CPopulation::m_CountDownToPedsAtStart = 10; CCarCtrl::CountDownToCarsAtStart = 10; CPad::GetPad(CWorld::PlayerInFocus)->DisablePlayerControls = PLAYERCONTROL_ENABLED; TheCamera.SetFadeColour(0, 0, 0); TheCamera.Fade(4.0f, FADE_IN); } break; case 4: return; } } void CGameLogic::RestorePlayerStuffDuringResurrection(CPlayerPed *pPlayerPed, CVector pos, float angle) { ClearShortCut(); CPlayerInfo* pPlayerInfo = pPlayerPed->GetPlayerInfoForThisPlayerPed(); pPlayerPed->m_fHealth = pPlayerInfo->m_nMaxHealth; pPlayerPed->m_fArmour = 0.0f; pPlayerPed->bIsVisible = true; pPlayerPed->m_bloodyFootprintCountOrDeathTime = 0; pPlayerPed->bDoBloodyFootprints = false; pPlayerPed->m_nDrunkenness = 0; pPlayerPed->m_nFadeDrunkenness = 0; CMBlur::ClearDrunkBlur(); pPlayerPed->m_nDrunkCountdown = 0; pPlayerPed->ClearAdrenaline(); pPlayerPed->m_fCurrentStamina = pPlayerPed->m_fMaxStamina; if (pPlayerPed->m_pFire) pPlayerPed->m_pFire->Extinguish(); pPlayerPed->bInVehicle = false; pPlayerPed->m_pMyVehicle = nil; pPlayerPed->m_pVehicleAnim = nil; pPlayerPed->m_pWanted->Reset(); pPlayerPed->bCancelEnteringCar = false; pPlayerPed->RestartNonPartialAnims(); pPlayerInfo->MakePlayerSafe(false); pPlayerPed->bRemoveFromWorld = false; pPlayerPed->ClearWeaponTarget(); pPlayerPed->SetInitialState(); CCarCtrl::ClearInterestingVehicleList(); pPlayerPed->Teleport(pos + CVector(0.0f, 0.0f, 1.0f)); pPlayerPed->SetMoveSpeed(0.0f, 0.0f, 0.0f); pPlayerPed->m_fRotationCur = DEGTORAD(angle); pPlayerPed->m_fRotationDest = pPlayerPed->m_fRotationCur; pPlayerPed->SetHeading(pPlayerPed->m_fRotationCur); CTheScripts::ClearSpaceForMissionEntity(pos, pPlayerPed); CWorld::ClearExcitingStuffFromArea(pos, 4000.0, 1); pPlayerPed->RestoreHeadingRate(); CGame::currArea = AREA_MAIN_MAP; CStreaming::RemoveBuildingsNotInArea(0); TheCamera.SetCameraDirectlyInFrontForFollowPed_CamOnAString(); TheCamera.Restore(); CReferences::RemoveReferencesToPlayer(); CGarages::PlayerArrestedOrDied(); CStats::CheckPointReachedUnsuccessfully(); CWorld::Remove(pPlayerPed); CWorld::Add(pPlayerPed); CHud::ResetWastedText(); CStreaming::StreamZoneModels(pos); clearWaterDrop = true; } void CGameLogic::ClearShortCut() { if (pShortCutTaxi) { if (pShortCutTaxi->VehicleCreatedBy == MISSION_VEHICLE) { pShortCutTaxi->VehicleCreatedBy = RANDOM_VEHICLE; --CCarCtrl::NumMissionCars; ++CCarCtrl::NumRandomCars; } CRadar::ClearBlipForEntity(BLIP_CAR, CPools::GetVehiclePool()->GetIndex(pShortCutTaxi)); pShortCutTaxi = nil; } CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_SHORTCUT_TAXI); } void CGameLogic::SetUpShortCut(CVector vStartPos, float fStartAngle, CVector vEndPos, float fEndAngle) { ClearShortCut(); ShortCutState = SHORTCUT_INIT; ShortCutStart = vStartPos; ShortCutStartOrientation = fStartAngle; ShortCutDestination = vEndPos; ShortCutDestinationOrientation = fEndAngle; CStreaming::RequestModel(MI_KAUFMAN, 0); } void CGameLogic::AbandonShortCutIfTaxiHasBeenMessedWith() { if (!pShortCutTaxi) return; if (pShortCutTaxi->pDriver == nil || pShortCutTaxi->pDriver->DyingOrDead() || pShortCutTaxi->pDriver->GetPedState() == PED_DRAG_FROM_CAR || pShortCutTaxi->pDriver->GetPedState() == PED_ON_FIRE || pShortCutTaxi->pDriver->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE || pShortCutTaxi->m_fHealth < 250.0f || pShortCutTaxi->bRenderScorched) ClearShortCut(); } void CGameLogic::AbandonShortCutIfPlayerMilesAway() { if (!pShortCutTaxi) return; if ((FindPlayerCoors() - pShortCutTaxi->GetPosition()).Magnitude() > 120.0f) ClearShortCut(); } void CGameLogic::UpdateShortCut() { switch (ShortCutState) { case SHORTCUT_INIT: if (!CStreaming::HasModelLoaded(MI_KAUFMAN)) { CStreaming::RequestModel(MI_KAUFMAN, 0); return; } pShortCutTaxi = new CAutomobile(MI_KAUFMAN, RANDOM_VEHICLE); if (!pShortCutTaxi) return; pShortCutTaxi->SetPosition(ShortCutStart); pShortCutTaxi->SetHeading(DEGTORAD(ShortCutStartOrientation)); pShortCutTaxi->PlaceOnRoadProperly(); pShortCutTaxi->SetStatus(STATUS_PHYSICS); pShortCutTaxi->AutoPilot.m_nCarMission = MISSION_STOP_FOREVER; pShortCutTaxi->AutoPilot.m_nCruiseSpeed = 0; pShortCutTaxi->SetUpDriver(); pShortCutTaxi->VehicleCreatedBy = MISSION_VEHICLE; ++CCarCtrl::NumMissionCars; --CCarCtrl::NumRandomCars; CTheScripts::ClearSpaceForMissionEntity(ShortCutStart, pShortCutTaxi); CWorld::Add(pShortCutTaxi); CRadar::SetEntityBlip(BLIP_CAR, CPools::GetVehiclePool()->GetIndex(pShortCutTaxi), 0, BLIP_DISPLAY_MARKER_ONLY); ShortCutState = SHORTCUT_IDLE; break; case SHORTCUT_IDLE: if (FindPlayerPed()->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && FindPlayerPed()->m_carInObjective == pShortCutTaxi) { CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_SHORTCUT_TAXI); FindPlayerPed()->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, pShortCutTaxi); ShortCutState = SHORTCUT_GETTING_IN; } AbandonShortCutIfTaxiHasBeenMessedWith(); AbandonShortCutIfPlayerMilesAway(); break; case SHORTCUT_GETTING_IN: if (pShortCutTaxi->pPassengers[0] == FindPlayerPed() || pShortCutTaxi->pPassengers[1] == FindPlayerPed() || pShortCutTaxi->pPassengers[2] == FindPlayerPed()) { pShortCutTaxi->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; pShortCutTaxi->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2500; TheCamera.SetFadeColour(0, 0, 0); TheCamera.Fade(2.5f, 0); ShortCutState = SHORTCUT_TRANSITION; ShortCutTimer = CTimer::GetTimeInMilliseconds() + 3000; CMessages::AddBigMessage(TheText.Get("TAXI"), 4500, 1); } AbandonShortCutIfTaxiHasBeenMessedWith(); break; case SHORTCUT_TRANSITION: if (CTimer::GetTimeInMilliseconds() > ShortCutTimer) { CTimer::Suspend(); CColStore::RequestCollision(ShortCutDestination); CStreaming::LoadSceneCollision(ShortCutDestination); CStreaming::LoadScene(ShortCutDestination); CTheScripts::ClearSpaceForMissionEntity(ShortCutDestination, pShortCutTaxi); pShortCutTaxi->Teleport(ShortCutDestination); pShortCutTaxi->SetHeading(DEGTORAD(ShortCutDestinationOrientation)); pShortCutTaxi->PlaceOnRoadProperly(); pShortCutTaxi->SetMoveSpeed(pShortCutTaxi->GetForward() * 0.4f); ShortCutTimer = CTimer::GetTimeInMilliseconds() + 1500; TheCamera.SetFadeColour(0, 0, 0); TheCamera.Fade(1.0f, 1); ShortCutState = SHORTCUT_ARRIVING; CTimer::Resume(); } break; case SHORTCUT_ARRIVING: if (CTimer::GetTimeInMilliseconds() > ShortCutTimer) { CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, CWorld::Players[CWorld::PlayerInFocus].m_nMoney - SHORTCUT_TAXI_COST); FindPlayerPed()->SetObjective(OBJECTIVE_LEAVE_CAR, pShortCutTaxi); FindPlayerPed()->m_carInObjective = pShortCutTaxi; ShortCutState = SHORTCUT_GETTING_OUT; } AbandonShortCutIfTaxiHasBeenMessedWith(); break; case SHORTCUT_GETTING_OUT: if (pShortCutTaxi->pPassengers[0] != FindPlayerPed() && pShortCutTaxi->pPassengers[1] != FindPlayerPed() && pShortCutTaxi->pPassengers[2] != FindPlayerPed()) { CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_SHORTCUT_TAXI); pShortCutTaxi->AutoPilot.m_nCarMission = MISSION_CRUISE; pShortCutTaxi->AutoPilot.m_nCruiseSpeed = 18; CCarCtrl::JoinCarWithRoadSystem(pShortCutTaxi); pShortCutTaxi->VehicleCreatedBy = RANDOM_VEHICLE; ++CCarCtrl::NumRandomCars; --CCarCtrl::NumMissionCars; CRadar::ClearBlipForEntity(BLIP_CAR, CPools::GetVehiclePool()->GetIndex(pShortCutTaxi)); ShortCutState = SHORTCUT_NONE; pShortCutTaxi = nil; } AbandonShortCutIfTaxiHasBeenMessedWith(); break; } } void CGameLogic::AddShortCutPointAfterDeath(CVector point, float angle) { if (NumAfterDeathStartPoints >= NUM_SHORTCUT_START_POINTS) return; AfterDeathStartPoints[NumAfterDeathStartPoints] = point; AfterDeathStartPointOrientation[NumAfterDeathStartPoints] = angle; NumAfterDeathStartPoints++; } void CGameLogic::AddShortCutDropOffPointForMission(CVector point, float angle) { ShortCutDropOffForMission = point; ShortCutDropOffOrientationForMission = angle; MissionDropOffReadyToBeUsed = true; } void CGameLogic::RemoveShortCutDropOffPointForMission() { MissionDropOffReadyToBeUsed = false; } void CGameLogic::AfterDeathArrestSetUpShortCutTaxi() { if (!MissionDropOffReadyToBeUsed) return; int nClosestPoint = -1; float fDistanceToPoint = 999999.9f; for (int i = 0; i < NUM_SHORTCUT_START_POINTS; i++) { float dist = (AfterDeathStartPoints[i] - FindPlayerCoors()).Magnitude(); if (dist < fDistanceToPoint) { fDistanceToPoint = dist; nClosestPoint = i; } } if (fDistanceToPoint < 100.0f) SetUpShortCut(AfterDeathStartPoints[nClosestPoint], AfterDeathStartPointOrientation[nClosestPoint], ShortCutDropOffForMission, ShortCutDropOffOrientationForMission); MissionDropOffReadyToBeUsed = false; } void CGameLogic::Save(uint8* buf, uint32* size) { INITSAVEBUF WriteSaveBuf(buf, NumAfterDeathStartPoints); *size += sizeof(NumAfterDeathStartPoints); for (int i = 0; i < NUM_SHORTCUT_START_POINTS; i++) { WriteSaveBuf(buf, AfterDeathStartPoints[i].x); *size += sizeof(AfterDeathStartPoints[i].x); WriteSaveBuf(buf, AfterDeathStartPoints[i].y); *size += sizeof(AfterDeathStartPoints[i].y); WriteSaveBuf(buf, AfterDeathStartPoints[i].z); *size += sizeof(AfterDeathStartPoints[i].z); WriteSaveBuf(buf, AfterDeathStartPointOrientation[i]); *size += sizeof(AfterDeathStartPointOrientation[i]); } VALIDATESAVEBUF(*size) } void CGameLogic::Load(uint8* buf, uint32 size) { INITSAVEBUF NumAfterDeathStartPoints = ReadSaveBuf(buf); for (int i = 0; i < NUM_SHORTCUT_START_POINTS; i++) { AfterDeathStartPoints[i].x = ReadSaveBuf(buf); AfterDeathStartPoints[i].y = ReadSaveBuf(buf); AfterDeathStartPoints[i].z = ReadSaveBuf(buf); AfterDeathStartPointOrientation[i] = ReadSaveBuf(buf); } VALIDATESAVEBUF(size) } ================================================ FILE: src/control/GameLogic.h ================================================ #pragma once class CAutomobile; class CGameLogic { public: enum { SHORTCUT_NONE = 0, SHORTCUT_INIT, SHORTCUT_IDLE, SHORTCUT_GETTING_IN, SHORTCUT_TRANSITION, SHORTCUT_ARRIVING, SHORTCUT_GETTING_OUT }; static void InitAtStartOfGame(); static void PassTime(uint32 time); static void SortOutStreamingAndMemory(const CVector &pos); static void Update(); static void RestorePlayerStuffDuringResurrection(class CPlayerPed *pPlayerPed, CVector pos, float angle); static void ClearShortCut(); static void SetUpShortCut(CVector, float, CVector, float); static void AbandonShortCutIfTaxiHasBeenMessedWith(); static void AbandonShortCutIfPlayerMilesAway(); static void UpdateShortCut(); static void AddShortCutPointAfterDeath(CVector, float); static void AddShortCutDropOffPointForMission(CVector, float); static void RemoveShortCutDropOffPointForMission(); static void AfterDeathArrestSetUpShortCutTaxi(); static void Save(uint8*, uint32*); static void Load(uint8*, uint32); static uint8 ActivePlayers; static uint8 ShortCutState; static CAutomobile* pShortCutTaxi; static uint32 NumAfterDeathStartPoints; static CVector ShortCutStart; static float ShortCutStartOrientation; static CVector ShortCutDestination; static float ShortCutDestinationOrientation; static uint32 ShortCutTimer; static CVector AfterDeathStartPoints[NUM_SHORTCUT_START_POINTS]; static float AfterDeathStartPointOrientation[NUM_SHORTCUT_START_POINTS]; static CVector ShortCutDropOffForMission; static float ShortCutDropOffOrientationForMission; static bool MissionDropOffReadyToBeUsed; }; ================================================ FILE: src/control/Garages.cpp ================================================ #include "common.h" #include "Garages.h" #include "main.h" #include "Bike.h" #include "Boat.h" #include "DMAudio.h" #include "General.h" #include "Font.h" #include "HandlingMgr.h" #include "Hud.h" #include "Messages.h" #include "ModelIndices.h" #include "Pad.h" #include "Particle.h" #include "PlayerPed.h" #include "Replay.h" #include "Stats.h" #include "Streaming.h" #include "Text.h" #include "Timer.h" #include "Vehicle.h" #include "Wanted.h" #include "World.h" #include "VarConsole.h" #define CRUSHER_GARAGE_X1 (1135.5f) #define CRUSHER_GARAGE_Y1 (57.0f) #define CRUSHER_GARAGE_Z1 (-1.0f) #define CRUSHER_GARAGE_X2 (1149.5f) #define CRUSHER_GARAGE_Y2 (63.7f) #define CRUSHER_GARAGE_Z2 (3.5f) #define ROTATED_DOOR_OPEN_SPEED (0.015f) #define ROTATED_DOOR_CLOSE_SPEED (0.02f) #define DEFAULT_DOOR_OPEN_SPEED (0.035f) #define DEFAULT_DOOR_CLOSE_SPEED (0.04f) #define CRUSHER_CRANE_SPEED (0.005f) // Prices #define BOMB_PRICE (500) #define RESPRAY_PRICE (100) // Distances #define DISTANCE_TO_CALL_OFF_CHASE (10.0f) #define DISTANCE_FOR_MRWHOOP_HACK (0.5f) #define DISTANCE_TO_ACTIVATE_GARAGE (8.0f) #define DISTANCE_TO_ACTIVATE_KEEPCAR_GARAGE (17.0f) #define DISTANCE_TO_CLOSE_MISSION_GARAGE (30.0f) #define DISTANCE_TO_CLOSE_COLLECTSPECIFICCARS_GARAGE (25.0f) #define DISTANCE_TO_CLOSE_COLLECTCARS_GARAGE (40.0f) #define DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_ON_FOOT (3.2f) #define DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_IN_CAR (15.0f) #define DISTANCE_TO_FORCE_CLOSE_HIDEOUT_GARAGE (70.0f) #define DISTANCE_TO_OPEN_HIDEOUT_GARAGE_ON_FOOT (2.8f) #define DISTANCE_TO_OPEN_HIDEOUT_GARAGE_IN_CAR (10.0f) #define DISTANCE_TO_SHOW_HIDEOUT_MESSAGE (5.0f) #define DISTANCE_TO_CONSIDER_DOOR_FOR_GARAGE (20.0f) // Time #define TIME_TO_RESPRAY (2000) #define TIME_TO_SETUP_BOMB (2000) #define TIME_TO_CRUSH_CAR (3000) #define TIME_TO_PROCESS_KEEPCAR_GARAGE (2000) // Respray stuff #define FREE_RESPRAY_HEALTH_THRESHOLD (970.0f) #define NUM_PARTICLES_IN_RESPRAY (200) #define RESPRAY_CENTERING_COEFFICIENT (0.4f) // Bomb stuff #define KGS_OF_EXPLOSIVES_IN_BOMB (10) // Collect specific cars stuff #define REWARD_FOR_FIRST_POLICE_CAR (5000) #define REWARD_FOR_FIRST_BANK_VAN (5000) #define MAX_POLICE_CARS_TO_COLLECT (10) #define MAX_BANK_VANS_TO_COLLECT (10) // Collect cars stuff #define MAX_SPEED_TO_SHOW_COLLECTED_MESSAGE (0.03f) #define IMPORT_REWARD (500) #define IMPORT_ALLCARS_REWARD (20500) // Crusher stuff #define CRUSHER_VEHICLE_TEST_SPAN (8) #define CRUSHER_MIN_REWARD (25) #define CRUSHER_MAX_REWARD (125) #define CRUSHER_REWARD_COEFFICIENT (1.0f/500000) // Hideout stuff #define HIDEOUT_DOOR_SPEED_COEFFICIENT (1.7f) #define TIME_BETWEEN_HIDEOUT_MESSAGES (18000) // Camera stuff #define MARGIN_FOR_CAMERA_COLLECTCARS (0.5f) #define MARGIN_FOR_CAMERA_DEFAULT (0.5f) const int32 gaCarsToCollectInCraigsGarages[TOTAL_COLLECTCARS_GARAGES][TOTAL_COLLECTCARS_CARS] = { { MI_LANDSTAL, MI_IDAHO, MI_ESPERANT, MI_STALLION, MI_RANCHER, MI_BLISTAC }, { MI_SABRE, MI_VIRGO, MI_SENTINEL, MI_STRETCH, MI_WASHING, MI_ADMIRAL }, { MI_CHEETAH, MI_INFERNUS, MI_BANSHEE, MI_PHEONIX, MI_COMET, MI_STINGER }, { MI_VOODOO, MI_CUBAN, MI_CADDY, MI_BAGGAGE, MI_MRWHOOP, MI_PIZZABOY } }; const int32 gaCarsToCollectIn60Seconds[] = { MI_CHEETAH, MI_TAXI, MI_ESPERANT, MI_SENTINEL, MI_IDAHO }; int32 CGarages::BankVansCollected; bool CGarages::BombsAreFree; bool CGarages::RespraysAreFree; int32 CGarages::CarsCollected; int32 CGarages::CarTypesCollected[TOTAL_COLLECTCARS_GARAGES]; int32 CGarages::CrushedCarId; uint32 CGarages::LastTimeHelpMessage; int32 CGarages::MessageNumberInString; char CGarages::MessageIDString[MESSAGE_LENGTH]; int32 CGarages::MessageNumberInString2; uint32 CGarages::MessageStartTime; uint32 CGarages::MessageEndTime; uint32 CGarages::NumGarages; bool CGarages::PlayerInGarage; int32 CGarages::PoliceCarsCollected; CStoredCar CGarages::aCarsInSafeHouses[TOTAL_HIDEOUT_GARAGES][NUM_GARAGE_STORED_CARS]; int32 hGarages = AEHANDLE_NONE; CGarage CGarages::aGarages[NUM_GARAGES]; bool CGarages::bCamShouldBeOutisde; #ifndef MASTER bool bPrintNearestObject; #endif void CGarages::Init(void) { #ifndef MASTER VarConsole.Add("Print nearest object", &bPrintNearestObject, true); #endif CrushedCarId = -1; NumGarages = 0; MessageEndTime = 0; MessageStartTime = 0; PlayerInGarage = false; BombsAreFree = false; #ifdef FIX_BUGS RespraysAreFree = false; #endif CarsCollected = 0; BankVansCollected = 0; PoliceCarsCollected = 0; for (int i = 0; i < TOTAL_COLLECTCARS_GARAGES; i++) CarTypesCollected[i] = 0; LastTimeHelpMessage = 0; for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { for (int j = 0; j < TOTAL_HIDEOUT_GARAGES; j++) aCarsInSafeHouses[j][i].Init(); } hGarages = DMAudio.CreateEntity(AUDIOTYPE_GARAGE, (void*)1); if (hGarages >= 0) DMAudio.SetEntityStatus(hGarages, true); } void CGarages::Shutdown(void) { NumGarages = 0; if (hGarages < 0) return; DMAudio.DestroyEntity(hGarages); hGarages = AEHANDLE_NONE; } void CGarages::Update(void) { static uint32 GarageToBeTidied = 0; if (CReplay::IsPlayingBack()) return; #ifdef SECUROM extern uint8 gameProcessPirateCheck; if (gameProcessPirateCheck == 2) return; #endif bCamShouldBeOutisde = false; TheCamera.pToGarageWeAreIn = nil; TheCamera.pToGarageWeAreInForHackAvoidFirstPerson = nil; #ifdef FIX_BUGS for (uint32 i = 0; i < NumGarages; i++) { #else for (int i = 0; i < NUM_GARAGES; i++) { #endif if (aGarages[i].IsUsed()) aGarages[i].Update(); } if ((CTimer::GetFrameCounter() & 0xF) != 0xC) return; #ifdef FIX_BUGS if (++GarageToBeTidied >= NumGarages) #else if (++GarageToBeTidied >= NUM_GARAGES) #endif GarageToBeTidied = 0; if (!aGarages[GarageToBeTidied].IsUsed()) return; if (!aGarages[GarageToBeTidied].IsFar()) aGarages[GarageToBeTidied].TidyUpGarageClose(); else aGarages[GarageToBeTidied].TidyUpGarage(); } int16 CGarages::AddOne(float X1, float Y1, float Z1, float X2, float Y2, float X3, float Y3, float Z2, uint8 type, int32 targetId) { if (NumGarages >= NUM_GARAGES) { assert(0); return NumGarages++; } CGarage* pGarage = &aGarages[NumGarages]; pGarage->m_fInfX = Min(Min(Min(X1, X2), X3), X2 + X3 - X1); pGarage->m_fSupX = Max(Max(X1, X2), X3); pGarage->m_fInfY = Min(Min(Min(Y1, Y2), Y3), Y2 + Y3 - Y1); pGarage->m_fSupY = Max(Max(Y1, Y2), Y3); pGarage->m_vecCorner1 = CVector(X1, Y1, Z1); pGarage->m_fInfZ = Z1; pGarage->m_vDir1 = CVector2D(X2 - X1, Y2 - Y1); pGarage->m_vDir2 = CVector2D(X3 - X1, Y3 - Y1); pGarage->m_fSupZ = Z2; pGarage->m_nMaxStoredCars = NUM_GARAGE_STORED_CARS; pGarage->m_fDir1Len = pGarage->m_vDir1.Magnitude(); pGarage->m_fDir2Len = pGarage->m_vDir2.Magnitude(); pGarage->m_vDir1 /= pGarage->m_fDir1Len; pGarage->m_vDir2 /= pGarage->m_fDir2Len; pGarage->m_pDoor1 = nil; pGarage->m_pDoor2 = nil; pGarage->m_fDoor1Z = Z1; pGarage->m_fDoor2Z = Z1; pGarage->m_eGarageType = type; pGarage->m_bRecreateDoorOnNextRefresh = false; pGarage->m_bRotatedDoor = false; pGarage->m_bCameraFollowsPlayer = false; pGarage->RefreshDoorPointers(true); if (pGarage->m_pDoor1) { pGarage->m_fDoor1Z = pGarage->m_pDoor1->GetPosition().z; pGarage->m_fDoor1X = pGarage->m_pDoor1->GetPosition().x; pGarage->m_fDoor1Y = pGarage->m_pDoor1->GetPosition().y; } if (pGarage->m_pDoor2) { pGarage->m_fDoor2Z = pGarage->m_pDoor2->GetPosition().z; pGarage->m_fDoor2X = pGarage->m_pDoor2->GetPosition().x; pGarage->m_fDoor2Y = pGarage->m_pDoor2->GetPosition().y; } pGarage->m_fDoorHeight = pGarage->m_pDoor1 ? FindDoorHeightForMI(pGarage->m_pDoor1->GetModelIndex()) : 4.0f; pGarage->m_fDoorPos = 0.0f; pGarage->m_eGarageState = GS_FULLYCLOSED; pGarage->m_nTimeToStartAction = 0; pGarage->field_2 = false; pGarage->m_nTargetModelIndex = targetId; pGarage->m_bCollectedCarsState = 0; pGarage->m_bDeactivated = false; pGarage->m_bResprayHappened = false; switch (type) { case GARAGE_MISSION: case GARAGE_COLLECTORSITEMS: case GARAGE_COLLECTSPECIFICCARS: case GARAGE_COLLECTCARS_1: case GARAGE_COLLECTCARS_2: case GARAGE_COLLECTCARS_3: case GARAGE_FORCARTOCOMEOUTOF: case GARAGE_60SECONDS: case GARAGE_MISSION_KEEPCAR: case GARAGE_FOR_SCRIPT_TO_OPEN: case GARAGE_HIDEOUT_ONE: case GARAGE_HIDEOUT_TWO: case GARAGE_HIDEOUT_THREE: case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE: case GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR: case GARAGE_MISSION_KEEPCAR_REMAINCLOSED: case GARAGE_COLLECTCARS_4: case GARAGE_FOR_SCRIPT_TO_OPEN_FOR_CAR: case GARAGE_HIDEOUT_FOUR: case GARAGE_HIDEOUT_FIVE: case GARAGE_HIDEOUT_SIX: case GARAGE_HIDEOUT_SEVEN: case GARAGE_HIDEOUT_EIGHT: case GARAGE_HIDEOUT_NINE: case GARAGE_HIDEOUT_TEN: case GARAGE_HIDEOUT_ELEVEN: case GARAGE_HIDEOUT_TWELVE: pGarage->m_eGarageState = GS_FULLYCLOSED; pGarage->m_fDoorPos = 0.0f; break; case GARAGE_BOMBSHOP1: case GARAGE_BOMBSHOP2: case GARAGE_BOMBSHOP3: case GARAGE_RESPRAY: pGarage->m_eGarageState = GS_OPENED; pGarage->m_fDoorPos = pGarage->m_fDoorHeight; break; case GARAGE_CRUSHER: pGarage->m_eGarageState = GS_OPENED; pGarage->m_fDoorPos = HALFPI; break; default: assert(false); } if (type == GARAGE_CRUSHER) pGarage->UpdateCrusherAngle(); else pGarage->UpdateDoorsHeight(); return NumGarages++; } void CGarages::ChangeGarageType(int16 garage, uint8 type, int32 mi) { CGarage* pGarage = &aGarages[garage]; pGarage->m_eGarageType = type; pGarage->m_nTargetModelIndex = mi; pGarage->m_eGarageState = GS_FULLYCLOSED; } void CGarage::Update() { if (m_eGarageType != GARAGE_CRUSHER) { switch (m_eGarageState) { case GS_FULLYCLOSED: case GS_OPENED: case GS_CLOSING: case GS_OPENING: case GS_OPENEDCONTAINSCAR: case GS_CLOSEDCONTAINSCAR: if (FindPlayerPed() && !m_bCameraFollowsPlayer) { CVehicle* pVehicle = FindPlayerVehicle(); if (IsEntityEntirelyInside3D(FindPlayerPed(), 0.25f)) { TheCamera.pToGarageWeAreIn = this; CGarages::bCamShouldBeOutisde = true; } if (pVehicle) { if (!IsEntityEntirelyOutside(pVehicle, 0.0f)) TheCamera.pToGarageWeAreInForHackAvoidFirstPerson = this; if (pVehicle->GetModelIndex() == MI_MRWHOOP) { if (pVehicle->IsWithinArea( m_fInfX - DISTANCE_FOR_MRWHOOP_HACK, m_fInfY - DISTANCE_FOR_MRWHOOP_HACK, m_fSupX + DISTANCE_FOR_MRWHOOP_HACK, m_fSupY + DISTANCE_FOR_MRWHOOP_HACK)) { TheCamera.pToGarageWeAreIn = this; CGarages::bCamShouldBeOutisde = true; } } } } break; default: break; } } if (m_bDeactivated && m_eGarageState == GS_FULLYCLOSED) return; if (m_bRotatedDoor) { #ifdef GTA_PS2 if (m_eGarageState == GS_OPENING) { if (m_pDoor1) { if (FindPlayerPed()->m_pCurrentPhysSurface == m_pDoor1) m_pDoor1->bUsesCollision = false; } if (m_pDoor2) { if (FindPlayerPed()->m_pCurrentPhysSurface == m_pDoor2) m_pDoor2->bUsesCollision = false; } } else if (m_eGarageState == GS_OPENED) { if (m_pDoor1) m_pDoor1->bUsesCollision = true; if (m_pDoor2) m_pDoor2->bUsesCollision = true; } #else if (m_eGarageState == GS_OPENING || m_eGarageState == GS_OPENED) { if (m_pDoor1) { if (FindPlayerPed()->m_pCurrentPhysSurface == m_pDoor1 || FindPlayerPed()->GetPedState() == PED_JUMP || FindPlayerPed()->GetPedState() == PED_FALL || !FindPlayerPed()->bIsStanding) m_pDoor1->bUsesCollision = false; } if (m_pDoor2) { if (FindPlayerPed()->m_pCurrentPhysSurface == m_pDoor2 || FindPlayerPed()->GetPedState() == PED_JUMP || FindPlayerPed()->GetPedState() == PED_FALL || !FindPlayerPed()->bIsStanding) m_pDoor2->bUsesCollision = false; } } else { if (m_pDoor1) m_pDoor1->bUsesCollision = true; if (m_pDoor2) m_pDoor2->bUsesCollision = true; } #endif } switch (m_eGarageType) { case GARAGE_RESPRAY: switch (m_eGarageState) { case GS_OPENED: if (IsStaticPlayerCarEntirelyInside()) { if (CGarages::IsCarSprayable(FindPlayerVehicle())) { if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney >= RESPRAY_PRICE || CGarages::RespraysAreFree) { m_eGarageState = GS_CLOSING; CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; } else { CGarages::TriggerMessage("GA_3", -1, 4000, -1); // No more freebies. $100 to respray! m_eGarageState = GS_OPENEDCONTAINSCAR; DMAudio.PlayFrontEndSound(SOUND_GARAGE_NO_MONEY, 1); } } else { CGarages::TriggerMessage("GA_1", -1, 4000, -1); // Whoa! I don't touch nothing THAT hot! m_eGarageState = GS_OPENEDCONTAINSCAR; DMAudio.PlayFrontEndSound(SOUND_GARAGE_BAD_VEHICLE, 1); } } if (FindPlayerVehicle()) { if (CalcDistToGarageRectangleSquared(FindPlayerVehicle()->GetPosition().x, FindPlayerVehicle()->GetPosition().y) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) CWorld::CallOffChaseForArea( m_fInfX - DISTANCE_TO_CALL_OFF_CHASE, m_fInfY - DISTANCE_TO_CALL_OFF_CHASE, m_fSupX + DISTANCE_TO_CALL_OFF_CHASE, m_fSupY + DISTANCE_TO_CALL_OFF_CHASE); } break; case GS_CLOSING: if (FindPlayerVehicle()) ThrowCarsNearDoorOutOfGarage(FindPlayerVehicle()); m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == 0.0f) { m_eGarageState = GS_FULLYCLOSED; m_nTimeToStartAction = CTimer::GetTimeInMilliseconds() + TIME_TO_RESPRAY; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); CStats::CheckPointReachedSuccessfully(); } UpdateDoorsHeight(); #ifdef FIX_BUGS if (FindPlayerVehicle() && FindPlayerVehicle()->IsCar()) #else if (FindPlayerVehicle()) #endif ((CAutomobile*)(FindPlayerVehicle()))->m_fFireBlowUpTimer = 0.0f; CWorld::CallOffChaseForArea( m_fInfX - DISTANCE_TO_CALL_OFF_CHASE, m_fInfY - DISTANCE_TO_CALL_OFF_CHASE, m_fSupX + DISTANCE_TO_CALL_OFF_CHASE, m_fSupY + DISTANCE_TO_CALL_OFF_CHASE); break; case GS_FULLYCLOSED: if (CTimer::GetTimeInMilliseconds() > m_nTimeToStartAction) { m_eGarageState = GS_OPENING; DMAudio.PlayFrontEndSound(SOUND_GARAGE_OPENING, 1); bool bTakeMoney = false; if (FindPlayerPed()->m_pWanted->GetWantedLevel() != 0) { bTakeMoney = true; FindPlayerPed()->m_pWanted->Suspend(); } CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; #ifdef FIX_BUGS bool bChangedColour = false; #else bool bChangedColour; #endif if (FindPlayerVehicle() && (FindPlayerVehicle()->IsCar() || FindPlayerVehicle()->IsBike())) { if (FindPlayerVehicle()->m_fHealth < FREE_RESPRAY_HEALTH_THRESHOLD) bTakeMoney = true; FindPlayerVehicle()->m_fHealth = 1000.0f; if (FindPlayerVehicle()->IsCar()) { ((CAutomobile*)(FindPlayerVehicle()))->m_fFireBlowUpTimer = 0.0f; ((CAutomobile*)(FindPlayerVehicle()))->Fix(); } else { ((CBike*)(FindPlayerVehicle()))->m_fFireBlowUpTimer = 0.0f; ((CBike*)(FindPlayerVehicle()))->Fix(); } FindPlayerVehicle()->m_nDoorLock = CARLOCK_UNLOCKED; ++CStats::Sprayings; if (FindPlayerVehicle()->GetUp().z < 0.0f) { FindPlayerVehicle()->GetUp() = -FindPlayerVehicle()->GetUp(); FindPlayerVehicle()->GetRight() = -FindPlayerVehicle()->GetRight(); } bChangedColour = false; #ifdef FIX_BUGS if (!FindPlayerVehicle()->IsCar() || !((CAutomobile*)(FindPlayerVehicle()))->bFixedColour) { #else if (!((CAutomobile*)(FindPlayerVehicle()))->bFixedColour) { #endif uint8 colour1, colour2; uint16 attempt; FindPlayerVehicle()->GetModelInfo()->ChooseVehicleColour(colour1, colour2); for (attempt = 0; attempt < 10; attempt++) { if (colour1 != FindPlayerVehicle()->m_currentColour1 || colour2 != FindPlayerVehicle()->m_currentColour2) break; FindPlayerVehicle()->GetModelInfo()->ChooseVehicleColour(colour1, colour2); } bChangedColour = (attempt < 10); FindPlayerVehicle()->m_currentColour1 = colour1; FindPlayerVehicle()->m_currentColour2 = colour2; if (bChangedColour) { for (int i = 0; i < NUM_PARTICLES_IN_RESPRAY; i++) { CVector pos; pos.x = CGeneral::GetRandomNumberInRange(m_fInfX + 0.5f, m_fSupX - 0.5f); pos.y = CGeneral::GetRandomNumberInRange(m_fInfY + 0.5f, m_fSupY - 0.5f); pos.z = CGeneral::GetRandomNumberInRange(m_fDoor1Z - 3.0f, m_fDoor1Z + 1.0f); CParticle::AddParticle(PARTICLE_GARAGEPAINT_SPRAY, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, CVehicleModelInfo::ms_vehicleColourTable[colour1]); } } } CenterCarInGarage(FindPlayerVehicle()); } if (bTakeMoney) { if (!CGarages::RespraysAreFree) { CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, CWorld::Players[CWorld::PlayerInFocus].m_nMoney - RESPRAY_PRICE); CStats::AutoPaintingBudget += RESPRAY_PRICE; } CGarages::TriggerMessage("GA_2", -1, 4000, -1); // New engine and paint job. The cops won't recognize you! } else if (bChangedColour) { if (CGeneral::GetRandomTrueFalse()) CGarages::TriggerMessage("GA_15", -1, 4000, -1); // Hope you like the new color. else CGarages::TriggerMessage("GA_16", -1, 4000, -1); // Respray is complementary. } m_bResprayHappened = true; } CWorld::CallOffChaseForArea( m_fInfX - DISTANCE_TO_CALL_OFF_CHASE, m_fInfY - DISTANCE_TO_CALL_OFF_CHASE, m_fSupX + DISTANCE_TO_CALL_OFF_CHASE, m_fSupY + DISTANCE_TO_CALL_OFF_CHASE); break; case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENEDCONTAINSCAR; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; case GS_OPENEDCONTAINSCAR: if (IsPlayerOutsideGarage()) m_eGarageState = GS_OPENED; break; //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; case GARAGE_BOMBSHOP1: case GARAGE_BOMBSHOP2: case GARAGE_BOMBSHOP3: switch (m_eGarageState) { case GS_OPENED: if (IsStaticPlayerCarEntirelyInside()) { if (!FindPlayerVehicle() || FindPlayerVehicle()->m_bombType) { CGarages::TriggerMessage("GA_5", -1, 4000, -1); //"Your car is already fitted with a bomb" m_eGarageState = GS_OPENEDCONTAINSCAR; DMAudio.PlayFrontEndSound(SOUND_GARAGE_BOMB_ALREADY_SET, 1); break; } if (!CGarages::BombsAreFree && CWorld::Players[CWorld::PlayerInFocus].m_nMoney < BOMB_PRICE) { CGarages::TriggerMessage("GA_4", -1, 4000, -1); // "Car bombs are $1000 each" - weird that the price is hardcoded in message m_eGarageState = GS_OPENEDCONTAINSCAR; DMAudio.PlayFrontEndSound(SOUND_GARAGE_NO_MONEY, 1); break; } m_eGarageState = GS_CLOSING; CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; } break; case GS_CLOSING: if (FindPlayerVehicle()) ThrowCarsNearDoorOutOfGarage(FindPlayerVehicle()); m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == 0.0f) { m_eGarageState = GS_FULLYCLOSED; m_nTimeToStartAction = CTimer::GetTimeInMilliseconds() + TIME_TO_SETUP_BOMB; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); } UpdateDoorsHeight(); if (m_eGarageType == GARAGE_BOMBSHOP3) CStreaming::RequestModel(MI_BOMB, STREAMFLAGS_DONT_REMOVE); break; case GS_FULLYCLOSED: if (CTimer::GetTimeInMilliseconds() > m_nTimeToStartAction) { if (m_eGarageType != GARAGE_BOMBSHOP3 || CStreaming::HasModelLoaded(MI_BOMB)) { switch (m_eGarageType) { case GARAGE_BOMBSHOP1: DMAudio.PlayFrontEndSound(SOUND_GARAGE_BOMB1_SET, 1); break; case GARAGE_BOMBSHOP2: DMAudio.PlayFrontEndSound(SOUND_GARAGE_BOMB2_SET, 1); break; case GARAGE_BOMBSHOP3: DMAudio.PlayFrontEndSound(SOUND_GARAGE_BOMB3_SET, 1); break; } m_eGarageState = GS_OPENING; if (!CGarages::BombsAreFree) CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, CWorld::Players[CWorld::PlayerInFocus].m_nMoney - BOMB_PRICE); if (FindPlayerVehicle() && (FindPlayerVehicle()->IsCar() || FindPlayerVehicle()->IsBike())) { #if (!defined GTA_PS2 || defined FIX_BUGS) FindPlayerVehicle()->m_bombType = CGarages::GetBombTypeForGarageType(m_eGarageType); FindPlayerVehicle()->m_pBombRigger = FindPlayerPed(); #else // PS2 version contained a bug: CBike was casted to CAutomobile, but due to coincidence it didn't corrupt memory ((CAutomobile*)(FindPlayerVehicle()))->m_bombType = CGarages::GetBombTypeForGarageType(m_eGarageType); ((CAutomobile*)(FindPlayerVehicle()))->m_pBombRigger = FindPlayerPed(); #endif if (m_eGarageType == GARAGE_BOMBSHOP3) CGarages::GivePlayerDetonator(); CStats::KgsOfExplosivesUsed += KGS_OF_EXPLOSIVES_IN_BOMB; } #ifdef DETECT_PAD_INPUT_SWITCH int16 Mode = CPad::IsAffectedByController ? CPad::GetPad(0)->Mode : 0; #else int16 Mode = CPad::GetPad(0)->Mode; #endif switch (m_eGarageType) { case GARAGE_BOMBSHOP1: switch (Mode) { case 0: case 1: case 2: CHud::SetHelpMessage(TheText.Get("GA_6"), false); // Arm with ~h~~k~~PED_FIREWEAPON~ button~w~. Bomb will go off when engine is started. break; case 3: CHud::SetHelpMessage(TheText.Get("GA_6B"), false); // Arm with ~h~~k~~PED_FIREWEAPON~ button~w~. Bomb will go off when engine is started. break; } break; case GARAGE_BOMBSHOP2: switch (Mode) { case 0: case 1: case 2: CHud::SetHelpMessage(TheText.Get("GA_7"), false); // Park it, prime it by pressing the ~h~~k~~PED_FIREWEAPON~ button~w~ and LEG IT! break; case 3: CHud::SetHelpMessage(TheText.Get("GA_7B"), false); // Park it, prime it by pressing the ~h~~k~~PED_FIREWEAPON~ button~w~ and LEG IT! break; } break; case GARAGE_BOMBSHOP3: CHud::SetHelpMessage(TheText.Get("GA_8"), false); // Use the detonator to activate the bomb. break; } CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; } else { CStreaming::RequestModel(MI_BOMB, STREAMFLAGS_DONT_REMOVE); } } break; case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENEDCONTAINSCAR; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; case GS_OPENEDCONTAINSCAR: if (IsPlayerOutsideGarage()) m_eGarageState = GS_OPENED; break; //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; case GARAGE_MISSION: switch (m_eGarageState) { case GS_OPENED: if (((CVector2D)FindPlayerCoors() - CVector2D(GetGarageCenterX(), GetGarageCenterY())).MagnitudeSqr() > SQR(DISTANCE_TO_CLOSE_MISSION_GARAGE)) { if ((CTimer::GetFrameCounter() & 0x1F) == 0 #ifndef GTA_PS2 && (!m_pTarget || IsEntityTouching3D(m_pTarget)) #endif ) { m_eGarageState = GS_CLOSING; m_bClosingWithoutTargetCar = true; } } else if (!FindPlayerVehicle() && m_pTarget && IsEntityEntirelyInside3D(m_pTarget, 0.0f) && IsEntityEntirelyOutside(FindPlayerVehicle() ? (CEntity*)FindPlayerVehicle() : (CEntity*)FindPlayerPed(), 2.0f)) { CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; m_eGarageState = GS_CLOSING; m_bClosingWithoutTargetCar = false; } break; case GS_CLOSING: if (m_pTarget) ThrowCarsNearDoorOutOfGarage(m_pTarget); m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == 0.0f) { DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); if (m_bClosingWithoutTargetCar) m_eGarageState = GS_FULLYCLOSED; else { if (m_pTarget) { m_eGarageState = GS_CLOSEDCONTAINSCAR; DestroyVehicleAndDriverAndPassengers(m_pTarget); m_pTarget = nil; } else { m_eGarageState = GS_FULLYCLOSED; } CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; } } UpdateDoorsHeight(); break; case GS_FULLYCLOSED: if (FindPlayerVehicle() == m_pTarget && m_pTarget) { if (CalcDistToGarageRectangleSquared( FindPlayerVehicle()->GetPosition().x, FindPlayerVehicle()->GetPosition().y) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) m_eGarageState = GS_OPENING; } break; case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; //case GS_OPENEDCONTAINSCAR: //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; case GARAGE_COLLECTSPECIFICCARS: case GARAGE_COLLECTCARS_1: case GARAGE_COLLECTCARS_2: case GARAGE_COLLECTCARS_3: case GARAGE_COLLECTCARS_4: switch (m_eGarageState) { case GS_OPENED: if (FindPlayerVehicle() && DoesCraigNeedThisCar(FindPlayerVehicle()->GetModelIndex())) { m_pTarget = FindPlayerVehicle(); m_pTarget->RegisterReference((CEntity**)&m_pTarget); } if (Abs(FindPlayerCoors().x - GetGarageCenterX()) > DISTANCE_TO_CLOSE_COLLECTCARS_GARAGE || Abs(FindPlayerCoors().y - GetGarageCenterY()) > DISTANCE_TO_CLOSE_COLLECTCARS_GARAGE) { m_eGarageState = GS_CLOSING; m_pTarget = nil; break; } if (m_pTarget && !FindPlayerVehicle() && IsEntityEntirelyInside3D(m_pTarget, 0.0f) && !IsAnyOtherCarTouchingGarage(m_pTarget) && IsEntityEntirelyOutside(FindPlayerPed(), 2.0f)) { #ifdef FIX_BUGS if (!m_pTarget->IsCar() || ((CAutomobile*)(m_pTarget))->Damage.GetEngineStatus() <= ENGINE_STATUS_ON_FIRE && ((CAutomobile*)(m_pTarget))->m_fFireBlowUpTimer == 0.0f) { #else if (((CAutomobile*)(m_pTarget))->Damage.GetEngineStatus() <= ENGINE_STATUS_ON_FIRE && ((CAutomobile*)(m_pTarget))->m_fFireBlowUpTimer == 0.0f) { #endif if (m_pTarget->GetStatus() != STATUS_WRECKED) { CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; m_eGarageState = GS_CLOSING; TheCamera.SetCameraDirectlyBehindForFollowPed_CamOnAString(); } } } break; case GS_CLOSING: if (m_pTarget) ThrowCarsNearDoorOutOfGarage(m_pTarget); m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == 0.0f) { m_eGarageState = GS_FULLYCLOSED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); if (m_pTarget) { MarkThisCarAsCollectedForCraig(m_pTarget->GetModelIndex()); DestroyVehicleAndDriverAndPassengers(m_pTarget); m_pTarget = nil; CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; } } UpdateDoorsHeight(); break; case GS_FULLYCLOSED: if (FindPlayerVehicle() && CalcSmallestDistToGarageDoorSquared( FindPlayerVehicle()->GetPosition().x, FindPlayerVehicle()->GetPosition().y ) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) { if (DoesCraigNeedThisCar(FindPlayerVehicle()->GetModelIndex())) { if (FindPlayerVehicle()->VehicleCreatedBy == MISSION_VEHICLE) CGarages::TriggerMessage("GA_1A", -1, 5000, -1); // Come back when you're not so busy... else m_eGarageState = GS_OPENING; } else { if (HasCraigCollectedThisCar(FindPlayerVehicle()->GetModelIndex())) CGarages::TriggerMessage("GA_20", -1, 5000, -1); // We got more of these than we can shift. Sorry man, no deal. else if (FindPlayerSpeed().Magnitude() < MAX_SPEED_TO_SHOW_COLLECTED_MESSAGE) CGarages::TriggerMessage("GA_19", -1, 5000, -1); // We're not interested in that model. } } m_pTarget = nil; break; case GS_OPENING: if (FindPlayerVehicle() && DoesCraigNeedThisCar(FindPlayerVehicle()->GetModelIndex())) { m_pTarget = FindPlayerVehicle(); m_pTarget->RegisterReference((CEntity**)&m_pTarget); } m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; //case GS_OPENEDCONTAINSCAR: //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; case GARAGE_FORCARTOCOMEOUTOF: switch (m_eGarageState) { case GS_OPENED: if (IsGarageEmpty()) m_eGarageState = GS_CLOSING; break; case GS_CLOSING: m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == 0.0f) { m_eGarageState = GS_FULLYCLOSED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); } if (!IsGarageEmpty()) m_eGarageState = GS_OPENING; break; case GS_FULLYCLOSED: break; case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; //case GS_OPENEDCONTAINSCAR: //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; case GARAGE_CRUSHER: break; case GARAGE_MISSION_KEEPCAR: case GARAGE_MISSION_KEEPCAR_REMAINCLOSED: switch (m_eGarageState) { case GS_OPENED: if (((CVector2D)FindPlayerCoors() - CVector2D(GetGarageCenterX(), GetGarageCenterY())).MagnitudeSqr() > SQR(DISTANCE_TO_CLOSE_MISSION_GARAGE) && !IsAnyOtherCarTouchingGarage(nil)) { m_eGarageState = GS_CLOSING; m_bClosingWithoutTargetCar = true; } else if (m_pTarget && m_pTarget == FindPlayerVehicle() && IsStaticPlayerCarEntirelyInside() && !IsAnyCarBlockingDoor()) { CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = true; m_eGarageState = GS_CLOSING; m_bClosingWithoutTargetCar = false; } break; case GS_CLOSING: if (m_pTarget) ThrowCarsNearDoorOutOfGarage(m_pTarget); m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == 0.0f) { DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); if (m_bClosingWithoutTargetCar) m_eGarageState = GS_FULLYCLOSED; else { if (m_pTarget) { m_eGarageState = GS_CLOSEDCONTAINSCAR; m_nTimeToStartAction = CTimer::GetTimeInMilliseconds() + TIME_TO_PROCESS_KEEPCAR_GARAGE; m_pTarget = nil; } else m_eGarageState = GS_FULLYCLOSED; CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); FindPlayerPed()->m_pWanted->m_bIgnoredByCops = false; } } UpdateDoorsHeight(); break; case GS_FULLYCLOSED: if (FindPlayerVehicle() == m_pTarget && m_pTarget && CalcDistToGarageRectangleSquared( FindPlayerVehicle()->GetPosition().x, FindPlayerVehicle()->GetPosition().y ) < SQR(DISTANCE_TO_ACTIVATE_KEEPCAR_GARAGE)) m_eGarageState = GS_OPENING; break; case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; case GS_CLOSEDCONTAINSCAR: if (m_eGarageType == GARAGE_MISSION_KEEPCAR && CTimer::GetTimeInMilliseconds() > m_nTimeToStartAction) m_eGarageState = GS_OPENING; break; //case GS_OPENEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; case GARAGE_FOR_SCRIPT_TO_OPEN: switch (m_eGarageState) { case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; //case GS_OPENED: //case GS_CLOSING: //case GS_FULLYCLOSED: //case GS_OPENEDCONTAINSCAR: //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE: switch (m_eGarageState) { case GS_CLOSING: m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == 0.0f) { m_eGarageState = GS_FULLYCLOSED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); } UpdateDoorsHeight(); break; case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; //case GS_OPENED: //case GS_FULLYCLOSED: //case GS_OPENEDCONTAINSCAR: //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; case GARAGE_HIDEOUT_ONE: case GARAGE_HIDEOUT_TWO: case GARAGE_HIDEOUT_THREE: case GARAGE_HIDEOUT_FOUR: case GARAGE_HIDEOUT_FIVE: case GARAGE_HIDEOUT_SIX: case GARAGE_HIDEOUT_SEVEN: case GARAGE_HIDEOUT_EIGHT: case GARAGE_HIDEOUT_NINE: case GARAGE_HIDEOUT_TEN: case GARAGE_HIDEOUT_ELEVEN: case GARAGE_HIDEOUT_TWELVE: switch (m_eGarageState) { case GS_OPENED: { float distance = CalcDistToGarageRectangleSquared(FindPlayerCoors().x, FindPlayerCoors().y); // Close car doors either if player is far, or if he is in vehicle and garage is full, // or if player is very very far so that we can remove whatever is blocking garage door without him noticing if ((distance > SQR(DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_IN_CAR) || !FindPlayerVehicle() && distance > SQR(DISTANCE_TO_CLOSE_HIDEOUT_GARAGE_ON_FOOT)) && !IsAnyCarBlockingDoor()) m_eGarageState = GS_CLOSING; else if (FindPlayerVehicle() && CountCarsWithCenterPointWithinGarage(FindPlayerVehicle()) >= FindMaxNumStoredCarsForGarage()) { m_eGarageState = GS_CLOSING; } else if (distance > SQR(DISTANCE_TO_FORCE_CLOSE_HIDEOUT_GARAGE)) { m_eGarageState = GS_CLOSING; RemoveCarsBlockingDoorNotInside(); } break; } case GS_CLOSING: m_fDoorPos = Max(0.0f, m_fDoorPos - HIDEOUT_DOOR_SPEED_COEFFICIENT * (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (!IsPlayerOutsideGarage()) m_eGarageState = GS_OPENING; else if (m_fDoorPos == 0.0f) { DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); m_eGarageState = GS_FULLYCLOSED; StoreAndRemoveCarsForThisHideout(CGarages::aCarsInSafeHouses[CGarages::FindSafeHouseIndexForGarageType(m_eGarageType)], NUM_GARAGE_STORED_CARS); } UpdateDoorsHeight(); break; case GS_FULLYCLOSED: { float distance = CalcDistToGarageRectangleSquared(FindPlayerCoors().x, FindPlayerCoors().y); if (distance < SQR(DISTANCE_TO_OPEN_HIDEOUT_GARAGE_ON_FOOT) || distance < SQR(DISTANCE_TO_OPEN_HIDEOUT_GARAGE_IN_CAR) && FindPlayerVehicle()) { if (FindPlayerVehicle() && CGarages::CountCarsInHideoutGarage(m_eGarageType) >= FindMaxNumStoredCarsForGarage()) { if (m_pDoor1) { if (((CVector2D)FindPlayerVehicle()->GetPosition() - (CVector2D)m_pDoor1->GetPosition()).MagnitudeSqr() < SQR(DISTANCE_TO_SHOW_HIDEOUT_MESSAGE) && CTimer::GetTimeInMilliseconds() - CGarages::LastTimeHelpMessage > TIME_BETWEEN_HIDEOUT_MESSAGES) { if (FindPlayerVehicle()->GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI && FindPlayerVehicle()->GetVehicleAppearance() != VEHICLE_APPEARANCE_PLANE) { CHud::SetHelpMessage(TheText.Get("GA_21"), false); // You cannot store any more cars in this garage. CGarages::LastTimeHelpMessage = CTimer::GetTimeInMilliseconds(); } } } } else if (RestoreCarsForThisHideout(CGarages::aCarsInSafeHouses[CGarages::FindSafeHouseIndexForGarageType(m_eGarageType)])) m_eGarageState = GS_OPENING; } break; } case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + HIDEOUT_DOOR_SPEED_COEFFICIENT * (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; //case GS_OPENEDCONTAINSCAR: //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; case GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR: switch (m_eGarageState) { case GS_OPENED: if (((CVector2D)FindPlayerCoors() - CVector2D(GetGarageCenterX(), GetGarageCenterY())).MagnitudeSqr() > SQR(DISTANCE_TO_CLOSE_MISSION_GARAGE)) { if (m_pTarget && IsEntityEntirelyOutside(m_pTarget, 0.0f) && !IsAnyOtherCarTouchingGarage(nil)) { m_eGarageState = GS_CLOSING; m_bClosingWithoutTargetCar = true; } } break; case GS_CLOSING: if (m_pTarget) ThrowCarsNearDoorOutOfGarage(m_pTarget); m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == 0.0f) { m_eGarageState = GS_FULLYCLOSED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); } UpdateDoorsHeight(); break; case GS_FULLYCLOSED: if (FindPlayerVehicle() == m_pTarget && m_pTarget && CalcDistToGarageRectangleSquared( FindPlayerVehicle()->GetPosition().x, FindPlayerVehicle()->GetPosition().y ) < SQR(DISTANCE_TO_ACTIVATE_GARAGE)) m_eGarageState = GS_OPENING; break; case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; //case GS_OPENEDCONTAINSCAR: //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } break; //case GARAGE_COLLECTORSITEMS: //case GARAGE_60SECONDS: case GARAGE_FOR_SCRIPT_TO_OPEN_FOR_CAR: switch (m_eGarageState) { case GS_OPENED: if (m_pTarget && IsEntityEntirelyInside3D(m_pTarget, 0.0f) && !IsAnyCarBlockingDoor() && IsPlayerOutsideGarage()) { CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_GARAGE); m_eGarageState = GS_CLOSING; m_bClosingWithoutTargetCar = false; } break; case GS_CLOSING: m_fDoorPos = Max(0.0f, m_fDoorPos - (m_bRotatedDoor ? ROTATED_DOOR_CLOSE_SPEED : DEFAULT_DOOR_CLOSE_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == 0.0f) { m_eGarageState = GS_FULLYCLOSED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_CLOSED, 1.0f); CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_GARAGE); } UpdateDoorsHeight(); break; case GS_FULLYCLOSED: break; case GS_OPENING: m_fDoorPos = Min(m_fDoorHeight, m_fDoorPos + (m_bRotatedDoor ? ROTATED_DOOR_OPEN_SPEED : DEFAULT_DOOR_OPEN_SPEED) * CTimer::GetTimeStep()); if (m_fDoorPos == m_fDoorHeight) { m_eGarageState = GS_OPENED; DMAudio.PlayOneShot(hGarages, SOUND_GARAGE_DOOR_OPENED, 1.0f); } UpdateDoorsHeight(); break; //case GS_OPENEDCONTAINSCAR: //case GS_CLOSEDCONTAINSCAR: //case GS_AFTERDROPOFF: default: break; } default: break; } } bool CGarage::IsStaticPlayerCarEntirelyInside() { if (!FindPlayerVehicle()) return false; if (!FindPlayerVehicle()->IsCar() && !FindPlayerVehicle()->IsBike()) return false; if (FindPlayerPed()->GetPedState() != PED_DRIVING) return false; if (FindPlayerPed()->m_objective == OBJECTIVE_LEAVE_CAR) return false; CVehicle* pVehicle = FindPlayerVehicle(); if (pVehicle->GetPosition().x < m_fInfX || pVehicle->GetPosition().x > m_fSupX || pVehicle->GetPosition().y < m_fInfY || pVehicle->GetPosition().y > m_fSupY) return false; if (Abs(pVehicle->GetSpeed().x) > 0.01f || Abs(pVehicle->GetSpeed().y) > 0.01f || Abs(pVehicle->GetSpeed().z) > 0.01f) return false; if (pVehicle->GetSpeed().MagnitudeSqr() > SQR(0.01f)) return false; return IsEntityEntirelyInside3D(pVehicle, 0.0f); } bool CGarage::IsPointInsideGarage(CVector pos) { // is it IsPointInsideGarage(pos, 0.0f)? if (pos.z < m_fInfZ) return false; if (pos.z > m_fSupZ) return false; CVector2D vecToTarget((CVector2D)pos - m_vecCorner1); float dp = DotProduct2D(m_vDir1, vecToTarget); if (dp < 0.0f) return false; if (m_fDir1Len < dp) return false; dp = DotProduct2D(m_vDir2, vecToTarget); if (dp < 0.0f) return false; if (m_fDir2Len < dp) return false; return true; } bool CGarage::IsPointInsideGarage(CVector pos, float m_fMargin) { if (pos.z < m_fInfZ - m_fMargin) return false; if (pos.z > m_fSupZ + m_fMargin) return false; CVector2D vecToTarget((CVector2D)pos - m_vecCorner1); float dp = DotProduct2D(m_vDir1, vecToTarget); if (dp < -m_fMargin) return false; if (m_fDir1Len + m_fMargin < dp) return false; dp = DotProduct2D(m_vDir2, vecToTarget); if (dp < -m_fMargin) return false; if (m_fDir2Len + m_fMargin < dp) return false; return true; } bool CGarage::IsEntityEntirelyInside3D(CEntity* pEntity, float fMargin) { if (pEntity->GetPosition().x < m_fInfX - fMargin || pEntity->GetPosition().x > m_fSupX + fMargin || pEntity->GetPosition().y < m_fInfY - fMargin || pEntity->GetPosition().y > m_fSupY + fMargin || pEntity->GetPosition().z < m_fInfZ - fMargin || pEntity->GetPosition().z > m_fSupZ + fMargin) return false; CColModel* pColModel = pEntity->GetColModel(); for (int i = 0; i < pColModel->numSpheres; i++) { CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center; float radius = pColModel->spheres[i].radius; if (!IsPointInsideGarage(pos, fMargin - radius)) return false; } return true; } bool CGarage::IsEntityEntirelyOutside(CEntity * pEntity, float fMargin) { if (pEntity->GetPosition().x > m_fInfX - fMargin && pEntity->GetPosition().x < m_fSupX + fMargin && pEntity->GetPosition().y > m_fInfY - fMargin && pEntity->GetPosition().y < m_fSupY + fMargin) return false; CColModel* pColModel = pEntity->GetColModel(); for (int i = 0; i < pColModel->numSpheres; i++) { CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center; float radius = pColModel->spheres[i].radius; if (IsPointInsideGarage(pos, fMargin + radius)) return false; } return true; } bool CGarage::IsGarageEmpty() { int16 num; CEntity* pEntities[16]; CWorld::FindObjectsIntersectingCube(CVector(m_fInfX, m_fInfY, m_fInfZ), CVector(m_fSupX, m_fSupY, m_fSupZ), &num, 16, pEntities, false, true, true, false, false); if (num <= 0) return true; for (int i = 0; i < 16; i++) { if (IsEntityTouching3D(pEntities[i])) return false; } return true; } bool CGarage::IsPlayerOutsideGarage() { if (FindPlayerVehicle()) return IsEntityEntirelyOutside(FindPlayerVehicle(), 0.0f); return IsEntityEntirelyOutside(FindPlayerPed(), 0.0f); } bool CGarage::IsEntityTouching3D(CEntity* pEntity) { float radius = pEntity->GetBoundRadius(); if (m_fInfX - radius > pEntity->GetPosition().x || m_fSupX + radius < pEntity->GetPosition().x || m_fInfY - radius > pEntity->GetPosition().y || m_fSupY + radius < pEntity->GetPosition().y || m_fInfZ - radius > pEntity->GetPosition().z || m_fSupZ + radius < pEntity->GetPosition().z) return false; CColModel* pColModel = pEntity->GetColModel(); for (int i = 0; i < pColModel->numSpheres; i++) { CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center; radius = pColModel->spheres[i].radius; if (IsPointInsideGarage(pos, radius)) return true; } return false; } bool CGarage::EntityHasASphereWayOutsideGarage(CEntity * pEntity, float fMargin) { CColModel* pColModel = pEntity->GetColModel(); for (int i = 0; i < pColModel->numSpheres; i++) { CVector pos = pEntity->GetMatrix() * pColModel->spheres[i].center; float radius = pColModel->spheres[i].radius; if (!IsPointInsideGarage(pos, fMargin + radius)) return true; } return false; } bool CGarage::IsAnyOtherCarTouchingGarage(CVehicle * pException) { uint32 i = CPools::GetVehiclePool()->GetSize(); while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle || pVehicle == pException || pVehicle->GetStatus() == STATUS_WRECKED) continue; if (!IsEntityTouching3D(pVehicle)) continue; CColModel* pColModel = pVehicle->GetColModel(); for (int i = 0; i < pColModel->numSpheres; i++) { CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center; float radius = pColModel->spheres[i].radius; if (IsPointInsideGarage(pos, radius)) return true; } } return false; } void CGarage::ThrowCarsNearDoorOutOfGarage(CVehicle* pException) { uint32 i = CPools::GetVehiclePool()->GetSize(); while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle || pVehicle == pException) continue; if (!IsEntityTouching3D(pVehicle)) continue; CColModel* pColModel = pVehicle->GetColModel(); for (int i = 0; i < pColModel->numSpheres; i++) { CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center; float radius = pColModel->spheres[i].radius; if (!IsPointInsideGarage(pos, 0.0f)) { CVector vecDirectionAway(pVehicle->GetPosition().x - GetGarageCenterX(), pVehicle->GetPosition().y - GetGarageCenterY(), 0.0f); vecDirectionAway.Normalise(); pVehicle->AddToMoveSpeed(vecDirectionAway * CTimer::GetTimeStepInSeconds()); } } } } bool CGarage::IsAnyOtherPedTouchingGarage(CPed * pException) { uint32 i = CPools::GetPedPool()->GetSize(); while (i--) { CPed* pPed = CPools::GetPedPool()->GetSlot(i); if (!pPed || pPed == pException) continue; if (!IsEntityTouching3D(pPed)) continue; CColModel* pColModel = pException->GetColModel(); for (int i = 0; i < pColModel->numSpheres; i++) { CVector pos = pPed->GetMatrix() * pColModel->spheres[i].center; float radius = pColModel->spheres[i].radius; if (IsPointInsideGarage(pos, radius)) return true; } } return false; } bool CGarage::IsAnyCarBlockingDoor() { uint32 i = CPools::GetVehiclePool()->GetSize(); while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if (!IsEntityTouching3D(pVehicle)) continue; CColModel* pColModel = pVehicle->GetColModel(); for (int i = 0; i < pColModel->numSpheres; i++) { CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center; float radius = pColModel->spheres[i].radius; if (!IsPointInsideGarage(pos, radius)) return true; } } return false; } int32 CGarage::CountCarsWithCenterPointWithinGarage(CEntity * pException) { int32 total = 0; uint32 i = CPools::GetVehiclePool()->GetSize(); while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle || pVehicle == pException) continue; if (IsPointInsideGarage(pVehicle->GetPosition())) total++; } return total; } void CGarage::RemoveCarsBlockingDoorNotInside() { uint32 i = CPools::GetVehiclePool()->GetSize(); while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if (!IsEntityTouching3D(pVehicle)) continue; if (!IsPointInsideGarage(pVehicle->GetPosition())) { if (!pVehicle->bIsLocked && pVehicle->CanBeDeleted()) { CWorld::Remove(pVehicle); delete pVehicle; #ifndef FIX_BUGS return; #endif } } } } void CGarages::PrintMessages() { if (CTimer::GetTimeInMilliseconds() > MessageStartTime && CTimer::GetTimeInMilliseconds() < MessageEndTime) { CFont::DrawFonts(); CFont::SetScale(SCREEN_SCALE_X(1.2f), SCREEN_SCALE_Y(1.5f)); CFont::SetPropOn(); CFont::SetJustifyOff(); CFont::SetBackgroundOff(); #ifdef FIX_BUGS CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 50)); #else CFont::SetCentreSize(SCREEN_WIDTH - 50); #endif CFont::SetCentreOn(); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetColor(CRGBA(27, 89, 130, 255)); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); float y_offset = SCREEN_SCALE_Y(140.0f); if (MessageNumberInString2 >= 0) { CMessages::InsertNumberInString(TheText.Get(MessageIDString), MessageNumberInString, MessageNumberInString2, -1, -1, -1, -1, gUString); CFont::PrintString(SCREEN_WIDTH / 2, y_offset - SCREEN_SCALE_Y(30.0f), gUString); } else if (MessageNumberInString >= 0) { CMessages::InsertNumberInString(TheText.Get(MessageIDString), MessageNumberInString, -1, -1, -1, -1, -1, gUString); CFont::PrintString(SCREEN_WIDTH / 2, y_offset - SCREEN_SCALE_Y(30.0f), gUString); } else { CFont::PrintString(SCREEN_WIDTH / 2, y_offset, TheText.Get(MessageIDString)); } } } bool CGarages::IsCarSprayable(CVehicle * pVehicle) { switch (pVehicle->GetModelIndex()) { case MI_FIRETRUCK: case MI_AMBULAN: case MI_POLICE: case MI_ENFORCER: case MI_BUS: case MI_RHINO: case MI_BARRACKS: case MI_DODO: case MI_COACH: #ifndef GTA_PS2 case MI_FBIRANCH: #endif return false; default: break; } return true; } void CGarage::UpdateDoorsHeight() { RefreshDoorPointers(false); if (m_pDoor1) { m_pDoor1->GetMatrix().GetPosition().z = m_fDoorPos + m_fDoor1Z; if (m_bRotatedDoor) { CVector pos; pos.x = m_fDoor1X + m_fDoorPos * m_pDoor1->GetForward().y * 5.0f / 6.0f; pos.y = m_fDoor1Y - m_fDoorPos * m_pDoor1->GetForward().x * 5.0f / 6.0f; pos.z = m_pDoor1->GetPosition().z; m_pDoor1->SetPosition(pos); BuildRotatedDoorMatrix(m_pDoor1, m_fDoorPos / m_fDoorHeight); } m_pDoor1->GetMatrix().UpdateRW(); m_pDoor1->UpdateRwFrame(); } if (m_pDoor2) { m_pDoor2->GetMatrix().GetPosition().z = m_fDoorPos + m_fDoor2Z; if (m_bRotatedDoor) { CVector pos; pos.x = m_fDoor2X + m_fDoorPos * m_pDoor2->GetForward().y * 5.0f / 6.0f; pos.y = m_fDoor2Y - m_fDoorPos * m_pDoor2->GetForward().x * 5.0f / 6.0f; pos.z = m_pDoor2->GetPosition().z; m_pDoor2->SetPosition(pos); BuildRotatedDoorMatrix(m_pDoor2, m_fDoorPos / m_fDoorHeight); } m_pDoor2->GetMatrix().UpdateRW(); m_pDoor2->UpdateRwFrame(); } } void CGarage::BuildRotatedDoorMatrix(CEntity * pDoor, float fPosition) { float fAngle = -fPosition * HALFPI; CVector up(-Sin(fAngle) * pDoor->GetForward().y, Sin(fAngle) * pDoor->GetForward().x, Cos(fAngle)); pDoor->GetRight() = CrossProduct(up, pDoor->GetForward()); pDoor->GetUp() = up; } void CGarage::UpdateCrusherAngle() { RefreshDoorPointers(false); m_pDoor2->GetMatrix().SetRotateXOnly(TWOPI - m_fDoorPos); m_pDoor2->GetMatrix().UpdateRW(); m_pDoor2->UpdateRwFrame(); } void CGarage::UpdateCrusherShake(float X, float Y) { RefreshDoorPointers(false); m_pDoor1->GetMatrix().GetPosition().x += X; m_pDoor1->GetMatrix().GetPosition().y += Y; m_pDoor1->GetMatrix().UpdateRW(); m_pDoor1->UpdateRwFrame(); m_pDoor1->GetMatrix().GetPosition().x -= X; m_pDoor1->GetMatrix().GetPosition().y -= Y; m_pDoor2->GetMatrix().GetPosition().x += X; m_pDoor2->GetMatrix().GetPosition().y += Y; m_pDoor2->GetMatrix().UpdateRW(); m_pDoor2->UpdateRwFrame(); m_pDoor2->GetMatrix().GetPosition().x -= X; m_pDoor2->GetMatrix().GetPosition().y -= Y; } void CGarage::RefreshDoorPointers(bool bCreate) { bool bNeedToFindDoorEntities = bCreate || m_bRecreateDoorOnNextRefresh; m_bRecreateDoorOnNextRefresh = false; if (m_pDoor1) { if (m_bDoor1IsDummy) { if (CPools::GetDummyPool()->GetIsFree(CPools::GetDummyPool()->GetJustIndex_NoFreeAssert((CDummy*)m_pDoor1))) bNeedToFindDoorEntities = true; else { if (m_bDoor1PoolIndex != (CPools::GetDummyPool()->GetIndex((CDummy*)m_pDoor1) & 0x7F)) bNeedToFindDoorEntities = true; if (!CGarages::IsModelIndexADoor(m_pDoor1->GetModelIndex())) bNeedToFindDoorEntities = true; } } else { if (CPools::GetObjectPool()->GetIsFree(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert((CObject*)m_pDoor1))) bNeedToFindDoorEntities = true; else { if (m_bDoor1PoolIndex != (CPools::GetObjectPool()->GetIndex((CObject*)m_pDoor1) & 0x7F)) bNeedToFindDoorEntities = true; if (!CGarages::IsModelIndexADoor(m_pDoor1->GetModelIndex())) bNeedToFindDoorEntities = true; } } } if (m_pDoor2) { if (m_bDoor2IsDummy) { if (CPools::GetDummyPool()->GetIsFree(CPools::GetDummyPool()->GetJustIndex_NoFreeAssert((CDummy*)m_pDoor2))) bNeedToFindDoorEntities = true; else { if (m_bDoor2PoolIndex != (CPools::GetDummyPool()->GetIndex((CDummy*)m_pDoor2) & 0x7F)) bNeedToFindDoorEntities = true; if (!CGarages::IsModelIndexADoor(m_pDoor2->GetModelIndex())) bNeedToFindDoorEntities = true; } } else { if (CPools::GetObjectPool()->GetIsFree(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert((CObject*)m_pDoor2))) bNeedToFindDoorEntities = true; else { if (m_bDoor2PoolIndex != (CPools::GetObjectPool()->GetIndex((CObject*)m_pDoor2) & 0x7F)) bNeedToFindDoorEntities = true; if (!CGarages::IsModelIndexADoor(m_pDoor2->GetModelIndex())) bNeedToFindDoorEntities = true; } } } if (bNeedToFindDoorEntities) FindDoorsEntities(); } void CGarages::TriggerMessage(const char* text, int16 num1, uint16 time, int16 num2) { if (strcmp(text, MessageIDString) == 0 && CTimer::GetTimeInMilliseconds() >= MessageStartTime && CTimer::GetTimeInMilliseconds() <= MessageEndTime) { if (CTimer::GetTimeInMilliseconds() - MessageStartTime <= 500) return; MessageStartTime = CTimer::GetTimeInMilliseconds() - 500; MessageEndTime = CTimer::GetTimeInMilliseconds() - 500 + time; } else { strcpy(MessageIDString, text); MessageStartTime = CTimer::GetTimeInMilliseconds(); MessageEndTime = CTimer::GetTimeInMilliseconds() + time; } MessageNumberInString = num1; MessageNumberInString2 = num2; } void CGarages::SetTargetCarForMissonGarage(int16 garage, CVehicle* pVehicle) { assert(garage >= 0 && garage < NUM_GARAGES); if (pVehicle) { aGarages[garage].m_pTarget = pVehicle; aGarages[garage].m_pTarget->RegisterReference((CEntity**)&aGarages[garage].m_pTarget); if (aGarages[garage].m_eGarageState == GS_CLOSEDCONTAINSCAR) aGarages[garage].m_eGarageState = GS_FULLYCLOSED; } else aGarages[garage].m_pTarget = nil; } bool CGarages::HasCarBeenDroppedOffYet(int16 garage) { return aGarages[garage].m_eGarageState == GS_CLOSEDCONTAINSCAR; } void CGarages::DeActivateGarage(int16 garage) { aGarages[garage].m_bDeactivated = true; } void CGarages::ActivateGarage(int16 garage) { aGarages[garage].m_bDeactivated = false; if (aGarages[garage].m_eGarageType == GARAGE_FORCARTOCOMEOUTOF && aGarages[garage].m_eGarageState == GS_FULLYCLOSED) aGarages[garage].m_eGarageState = GS_OPENING; } int32 CGarages::QueryCarsCollected(int16 garage) { return 0; } bool CGarages::HasImportExportGarageCollectedThisCar(int16 garage, int8 car) { return CarTypesCollected[GetCarsCollectedIndexForGarageType(aGarages[garage].m_eGarageType)] & (BIT(car)); } bool CGarages::IsGarageOpen(int16 garage) { return aGarages[garage].IsOpen(); } bool CGarages::IsGarageClosed(int16 garage) { return aGarages[garage].IsClosed(); } bool CGarages::HasThisCarBeenCollected(int16 garage, uint8 id) { return aGarages[garage].m_bCollectedCarsState & BIT(id); } bool CGarage::DoesCraigNeedThisCar(int32 mi) { int ct = CGarages::GetCarsCollectedIndexForGarageType(m_eGarageType); for (int i = 0; i < TOTAL_COLLECTCARS_CARS; i++) { if (mi == gaCarsToCollectInCraigsGarages[ct][i] || (gaCarsToCollectInCraigsGarages[ct][i] == MI_CHEETAH && mi == MI_VICECHEE)) return (CGarages::CarTypesCollected[ct] & BIT(i)) == 0; } return false; } bool CGarage::HasCraigCollectedThisCar(int32 mi) { int ct = CGarages::GetCarsCollectedIndexForGarageType(m_eGarageType); for (int i = 0; i < TOTAL_COLLECTCARS_CARS; i++) { if (mi == gaCarsToCollectInCraigsGarages[ct][i] || (gaCarsToCollectInCraigsGarages[ct][i] == MI_CHEETAH && mi == MI_VICECHEE)) return CGarages::CarTypesCollected[ct] & BIT(i); } return false; } bool CGarage::MarkThisCarAsCollectedForCraig(int32 mi) { int ct = CGarages::GetCarsCollectedIndexForGarageType(m_eGarageType); int index; for (index = 0; index < TOTAL_COLLECTCARS_CARS; index++) { if (mi == gaCarsToCollectInCraigsGarages[ct][index] || (gaCarsToCollectInCraigsGarages[ct][index] == MI_CHEETAH && mi == MI_VICECHEE)) break; } if (index >= TOTAL_COLLECTCARS_CARS) return false; CGarages::CarTypesCollected[ct] |= BIT(index); CWorld::Players[CWorld::PlayerInFocus].m_nMoney += IMPORT_REWARD; for (int i = 0; i < TOTAL_COLLECTCARS_CARS; i++) { if ((CGarages::CarTypesCollected[ct] & BIT(i)) == 0) { CGarages::TriggerMessage("GA_13", -1, 5000, -1); // Delivered like a pro. Complete the list and there'll be a bonus for you. return false; } } CWorld::Players[CWorld::PlayerInFocus].m_nMoney += IMPORT_ALLCARS_REWARD; CGarages::TriggerMessage("GA_14", -1, 5000, -1); // All the cars. NICE! Here's a little something. return true; } void CGarage::OpenThisGarage() { if (m_eGarageState == GS_FULLYCLOSED || m_eGarageState == GS_CLOSING || m_eGarageState == GS_CLOSEDCONTAINSCAR) m_eGarageState = GS_OPENING; } void CGarage::CloseThisGarage() { if (m_eGarageState == GS_OPENED || m_eGarageState == GS_OPENING) m_eGarageState = GS_CLOSING; } float CGarage::CalcDistToGarageRectangleSquared(float X, float Y) { float distX, distY; if (X < m_fInfX) distX = m_fInfX - X; else if (X > m_fSupX) distX = X - m_fSupX; else distX = 0.0f; if (Y < m_fInfY) distY = m_fInfY - Y; else if (Y > m_fSupY) distY = Y - m_fSupY; else distY = 0.0f; return SQR(distX) + SQR(distY); } float CGarage::CalcSmallestDistToGarageDoorSquared(float X, float Y) { float dist1 = 10000000.0f; float dist2 = 10000000.0f; if (m_pDoor1) dist1 = SQR(m_fDoor1X - X) + SQR(m_fDoor1Y - Y); if (m_pDoor2) dist2 = SQR(m_fDoor2X - X) + SQR(m_fDoor2Y - Y); return Min(dist1, dist2); } void CGarage::FindDoorsEntities() { m_pDoor1 = nil; m_pDoor2 = nil; int xstart = Max(0, CWorld::GetSectorIndexX(GetGarageCenterX() - 100.0f)); int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(GetGarageCenterX() + 100.0f)); int ystart = Max(0, CWorld::GetSectorIndexY(GetGarageCenterY() - 100.0f)); int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(GetGarageCenterY() + 100.0f)); assert(xstart <= xend); assert(ystart <= yend); CWorld::AdvanceCurrentScanCode(); for (int y = ystart; y <= yend; y++) { for (int x = xstart; x <= xend; x++) { CSector* s = CWorld::GetSector(x, y); FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_OBJECTS], false); FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_OBJECTS_OVERLAP], false); FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_DUMMIES], true); FindDoorsEntitiesSectorList(s->m_lists[ENTITYLIST_DUMMIES_OVERLAP], true); } } if (m_pDoor1 && m_pDoor2) { CVector2D vecDoor1ToGarage(m_pDoor1->GetPosition().x - GetGarageCenterX(), m_pDoor1->GetPosition().y - GetGarageCenterY()); CVector2D vecDoor2ToGarage(m_pDoor2->GetPosition().x - GetGarageCenterX(), m_pDoor2->GetPosition().y - GetGarageCenterY()); if (DotProduct2D(vecDoor1ToGarage, vecDoor2ToGarage) > 0.0f) { if (vecDoor1ToGarage.MagnitudeSqr() >= vecDoor2ToGarage.MagnitudeSqr()) { m_pDoor1 = m_pDoor2; m_bDoor1IsDummy = m_bDoor2IsDummy; } m_pDoor2 = nil; m_bDoor2IsDummy = false; } } if (m_pDoor1) m_pDoor1->bUsesCollision = true; if (m_pDoor2) m_pDoor2->bUsesCollision = true; } void CGarage::FindDoorsEntitiesSectorList(CPtrList& list, bool dummy) { CPtrNode* node; for (node = list.first; node; node = node->next) { CEntity* pEntity = (CEntity*)node->item; if (pEntity->m_scanCode == CWorld::GetCurrentScanCode()) continue; pEntity->m_scanCode = CWorld::GetCurrentScanCode(); if (!pEntity || !CGarages::IsModelIndexADoor(pEntity->GetModelIndex())) continue; if (!IsPointInsideGarage(pEntity->GetPosition(), 2.0f)) continue; if (!m_pDoor1) { m_pDoor1 = pEntity; m_bDoor1IsDummy = dummy; if (dummy) m_bDoor1PoolIndex = (CPools::GetDummyPool()->GetIndex((CDummy*)pEntity)) & 0x7F; else m_bDoor1PoolIndex = (CPools::GetObjectPool()->GetIndex((CObject*)pEntity)) & 0x7F; continue; } else { m_pDoor2 = pEntity; m_bDoor2IsDummy = dummy; if (dummy) m_bDoor2PoolIndex = (CPools::GetDummyPool()->GetIndex((CDummy*)pEntity)) & 0x7F; else m_bDoor2PoolIndex = (CPools::GetObjectPool()->GetIndex((CObject*)pEntity)) & 0x7F; } } } bool CGarages::HasResprayHappened(int16 garage) { bool result = aGarages[garage].m_bResprayHappened; aGarages[garage].m_bResprayHappened = false; return result; } void CGarages::SetGarageDoorToRotate(int16 garage) { if (aGarages[garage].m_bRotatedDoor) return; aGarages[garage].m_bRotatedDoor = true; aGarages[garage].m_fDoorHeight /= 2.0f; aGarages[garage].m_fDoorHeight -= 0.1f; aGarages[garage].m_fDoorPos = Min(aGarages[garage].m_fDoorHeight, aGarages[garage].m_fDoorPos); aGarages[garage].UpdateDoorsHeight(); } void CGarages::SetLeaveCameraForThisGarage(int16 garage) { aGarages[garage].m_bCameraFollowsPlayer = true; } bool CGarages::IsThisCarWithinGarageArea(int16 garage, CEntity * pCar) { return aGarages[garage].IsEntityEntirelyInside3D(pCar, 0.0f); } bool CGarages::HasCarBeenCrushed(int32 handle) { return CrushedCarId == handle; } void CStoredCar::StoreCar(CVehicle* pVehicle) { m_nModelIndex = pVehicle->GetModelIndex(); m_vecPos = pVehicle->GetPosition(); m_vecAngle = pVehicle->GetForward(); m_nPrimaryColor = pVehicle->m_currentColour1; m_nSecondaryColor = pVehicle->m_currentColour2; m_nRadioStation = pVehicle->m_nRadioStation; m_nVariationA = pVehicle->m_aExtras[0]; m_nVariationB = pVehicle->m_aExtras[1]; m_bBulletproof = pVehicle->bBulletProof; m_bFireproof = pVehicle->bFireProof; m_bExplosionproof = pVehicle->bExplosionProof; m_bCollisionproof = pVehicle->bCollisionProof; m_bMeleeproof = pVehicle->bMeleeProof; if (pVehicle->IsCar() || pVehicle->IsBike()) m_nCarBombType = ((CAutomobile*)pVehicle)->m_bombType; // NB: cast to CAutomobile is original behaviour } CVehicle* CStoredCar::RestoreCar() { CStreaming::RequestModel(m_nModelIndex, STREAMFLAGS_DEPENDENCY); if (!CStreaming::HasModelLoaded(m_nModelIndex)) return nil; #ifdef FIX_BUGS CVehicleModelInfo* pModelInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(m_nModelIndex); assert(pModelInfo); if (pModelInfo->m_numComps != 0) #endif { CVehicleModelInfo::SetComponentsToUse(m_nVariationA, m_nVariationB); } CVehicle* pVehicle; if (CModelInfo::IsBoatModel(m_nModelIndex)) pVehicle = new CBoat(m_nModelIndex, RANDOM_VEHICLE); else if (CModelInfo::IsBikeModel(m_nModelIndex)) { CBike* pBike = new CBike(m_nModelIndex, RANDOM_VEHICLE); pBike->bIsStanding = true; pVehicle = pBike; } else pVehicle = new CAutomobile(m_nModelIndex, RANDOM_VEHICLE); pVehicle->SetPosition(m_vecPos); pVehicle->SetStatus(STATUS_ABANDONED); pVehicle->GetForward() = m_vecAngle; pVehicle->GetRight() = CVector(m_vecAngle.y, -m_vecAngle.x, 0.0f); pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); pVehicle->pDriver = nil; pVehicle->m_currentColour1 = m_nPrimaryColor; pVehicle->m_currentColour2 = m_nSecondaryColor; pVehicle->m_nRadioStation = m_nRadioStation; pVehicle->bFreebies = false; if (pVehicle->IsCar()) { ((CAutomobile*)pVehicle)->m_bombType = m_nCarBombType; #ifdef FIX_BUGS if (m_nCarBombType != CARBOMB_NONE) ((CAutomobile*)pVehicle)->m_pBombRigger = FindPlayerPed(); #endif } pVehicle->bHasBeenOwnedByPlayer = true; pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; pVehicle->bBulletProof = m_bBulletproof; pVehicle->bFireProof = m_bFireproof; pVehicle->bExplosionProof = m_bExplosionproof; pVehicle->bCollisionProof = m_bCollisionproof; pVehicle->bMeleeProof = m_bMeleeproof; return pVehicle; } void CGarage::StoreAndRemoveCarsForThisHideout(CStoredCar* aCars, int32 nMax) { for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) aCars[i].Clear(); int i = CPools::GetVehiclePool()->GetSize(); int index = 0; while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if (IsPointInsideGarage(pVehicle->GetPosition())) { if (pVehicle->VehicleCreatedBy != MISSION_VEHICLE) { if (index < Max(NUM_GARAGE_STORED_CARS, nMax) && !EntityHasASphereWayOutsideGarage(pVehicle, 1.0f)) aCars[index++].StoreCar(pVehicle); CWorld::Players[CWorld::PlayerInFocus].CancelPlayerEnteringCars(pVehicle); CWorld::Remove(pVehicle); delete pVehicle; } } } // why? for (i = index; i < NUM_GARAGE_STORED_CARS; i++) aCars[i].Clear(); } bool CGarage::RestoreCarsForThisHideout(CStoredCar* aCars) { for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { if (aCars[i].HasCar()) { CVehicle* pVehicle = aCars[i].RestoreCar(); if (pVehicle) { CWorld::Add(pVehicle); aCars[i].Clear(); } } } for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { if (aCars[i].HasCar()) return false; } return true; } bool CGarages::IsPointInAGarageCameraZone(CVector point) { #ifdef FIX_BUGS for (uint32 i = 0; i < NumGarages; i++) { #else for (int i = 0; i < NUM_GARAGES; i++) { #endif switch (aGarages[i].m_eGarageType) { case GARAGE_NONE: break; case GARAGE_COLLECTCARS_1: case GARAGE_COLLECTCARS_2: case GARAGE_COLLECTCARS_3: case GARAGE_COLLECTCARS_4: if (aGarages[i].IsPointInsideGarage(point, MARGIN_FOR_CAMERA_COLLECTCARS)) return true; break; default: if (aGarages[i].IsPointInsideGarage(point, MARGIN_FOR_CAMERA_DEFAULT)) return true; break; } } return false; } bool CGarages::CameraShouldBeOutside() { return bCamShouldBeOutisde; } void CGarages::GivePlayerDetonator() { CPlayerPed* pPed = FindPlayerPed(); int slot = CWeaponInfo::GetWeaponInfo(WEAPONTYPE_DETONATOR)->m_nWeaponSlot; pPed->GiveWeapon(WEAPONTYPE_DETONATOR, 1); pPed->GetWeapon(pPed->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_eWeaponState = WEAPONSTATE_READY; pPed->m_nSelectedWepSlot = slot; if (pPed->m_storedWeapon != WEAPONTYPE_UNIDENTIFIED) pPed->m_storedWeapon = WEAPONTYPE_DETONATOR; } float CGarages::FindDoorHeightForMI(int32 mi) { return CModelInfo::GetModelInfo(mi)->GetColModel()->boundingBox.max.z - CModelInfo::GetModelInfo(mi)->GetColModel()->boundingBox.min.z - 0.1f; } void CGarage::TidyUpGarage() { uint32 i = CPools::GetVehiclePool()->GetSize(); #ifdef FIX_BUGS while (i--) { #else while (--i) { #endif CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (pVehicle && (pVehicle->IsCar() || pVehicle->IsBike())) { if (IsPointInsideGarage(pVehicle->GetPosition())) { if (pVehicle->GetStatus() == STATUS_WRECKED || pVehicle->GetUp().z < 0.5f) { CWorld::Remove(pVehicle); delete pVehicle; } } } } } void CGarage::TidyUpGarageClose() { uint32 i = CPools::GetVehiclePool()->GetSize(); #ifdef FIX_BUGS while (i--) { #else while (--i) { #endif CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if ((!pVehicle->IsCar() && !pVehicle->IsBike()) || pVehicle->GetStatus() != STATUS_WRECKED || !IsEntityTouching3D(pVehicle)) continue; bool bRemove = false; if (m_eGarageState != GS_FULLYCLOSED) { CColModel* pColModel = pVehicle->GetColModel(); for (int i = 0; i < pColModel->numSpheres; i++) { CVector pos = pVehicle->GetMatrix() * pColModel->spheres[i].center; float radius = pColModel->spheres[i].radius; if (!IsPointInsideGarage(pos, radius)) bRemove = true; } } else bRemove = true; if (bRemove) { // no MISSION_VEHICLE check??? CWorld::Remove(pVehicle); delete pVehicle; } } } void CGarages::PlayerArrestedOrDied() { static int GarageToBeTidied = 0; // lol #ifdef FIX_BUGS for (uint32 i = 0; i < NumGarages; i++) { #else for (int i = 0; i < NUM_GARAGES; i++) { #endif if (aGarages[i].m_eGarageType != GARAGE_NONE) aGarages[i].PlayerArrestedOrDied(); } MessageEndTime = 0; MessageStartTime = 0; } void CGarage::PlayerArrestedOrDied() { switch (m_eGarageType) { case GARAGE_MISSION: case GARAGE_COLLECTORSITEMS: case GARAGE_COLLECTSPECIFICCARS: case GARAGE_COLLECTCARS_1: case GARAGE_COLLECTCARS_2: case GARAGE_COLLECTCARS_3: case GARAGE_FORCARTOCOMEOUTOF: case GARAGE_60SECONDS: case GARAGE_MISSION_KEEPCAR: case GARAGE_FOR_SCRIPT_TO_OPEN: case GARAGE_HIDEOUT_ONE: case GARAGE_HIDEOUT_TWO: case GARAGE_HIDEOUT_THREE: case GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE: case GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR: case GARAGE_MISSION_KEEPCAR_REMAINCLOSED: case GARAGE_COLLECTCARS_4: case GARAGE_FOR_SCRIPT_TO_OPEN_FOR_CAR: case GARAGE_HIDEOUT_FOUR: case GARAGE_HIDEOUT_FIVE: case GARAGE_HIDEOUT_SIX: case GARAGE_HIDEOUT_SEVEN: case GARAGE_HIDEOUT_EIGHT: case GARAGE_HIDEOUT_NINE: case GARAGE_HIDEOUT_TEN: case GARAGE_HIDEOUT_ELEVEN: case GARAGE_HIDEOUT_TWELVE: switch (m_eGarageState) { case GS_OPENED: case GS_CLOSING: case GS_OPENING: m_eGarageState = GS_CLOSING; break; default: break; } break; case GARAGE_BOMBSHOP1: case GARAGE_BOMBSHOP2: case GARAGE_BOMBSHOP3: case GARAGE_RESPRAY: case GARAGE_CRUSHER: switch (m_eGarageState) { case GS_FULLYCLOSED: case GS_CLOSING: case GS_OPENING: m_eGarageState = GS_OPENING; break; default: break; } break; default: break; } } void CGarage::CenterCarInGarage(CVehicle* pVehicle) { if (IsAnyOtherCarTouchingGarage(FindPlayerVehicle())) return; if (IsAnyOtherPedTouchingGarage(FindPlayerPed())) return; CVector pos = pVehicle->GetPosition(); float garageX = GetGarageCenterX(); float garageY = GetGarageCenterY(); float offsetX = garageX - pos.x; float offsetY = garageY - pos.y; float offsetZ = pos.z - pos.z; float distance = CVector(offsetX, offsetY, offsetZ).Magnitude(); if (distance < RESPRAY_CENTERING_COEFFICIENT) { pVehicle->GetMatrix().GetPosition().x = GetGarageCenterX(); pVehicle->GetMatrix().GetPosition().y = GetGarageCenterY(); } else { pVehicle->GetMatrix().GetPosition().x += offsetX * RESPRAY_CENTERING_COEFFICIENT / distance; pVehicle->GetMatrix().GetPosition().y += offsetY * RESPRAY_CENTERING_COEFFICIENT / distance; } if (!IsEntityEntirelyInside3D(pVehicle, 0.3f)) pVehicle->SetPosition(pos); } void CGarages::CloseHideOutGaragesBeforeSave() { #ifdef FIX_BUGS for (uint32 i = 0; i < NumGarages; i++) { #else for (int i = 0; i < NUM_GARAGES; i++) { #endif if (!IsThisGarageTypeSafehouse(aGarages[i].m_eGarageType)) continue; if (aGarages[i].m_eGarageState != GS_FULLYCLOSED) { aGarages[i].m_eGarageState = GS_FULLYCLOSED; aGarages[i].StoreAndRemoveCarsForThisHideout(aCarsInSafeHouses[FindSafeHouseIndexForGarageType(aGarages[i].m_eGarageType)], NUM_GARAGE_STORED_CARS); aGarages[i].RemoveCarsBlockingDoorNotInside(); aGarages[i].m_fDoorPos = 0.0f; aGarages[i].UpdateDoorsHeight(); } } } int32 CGarages::CountCarsInHideoutGarage(uint8 type) { int32 total = 0; for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { total += aCarsInSafeHouses[FindSafeHouseIndexForGarageType(type)][i].HasCar(); } return total; } bool CGarages::IsPointWithinHideOutGarage(Const CVector& point) { #ifdef FIX_BUGS for (uint32 i = 0; i < NumGarages; i++) { #else for (int i = 0; i < NUM_GARAGES; i++) { #endif switch (aGarages[i].m_eGarageType) { case GARAGE_HIDEOUT_ONE: case GARAGE_HIDEOUT_TWO: case GARAGE_HIDEOUT_THREE: case GARAGE_HIDEOUT_FOUR: case GARAGE_HIDEOUT_FIVE: case GARAGE_HIDEOUT_SIX: case GARAGE_HIDEOUT_SEVEN: case GARAGE_HIDEOUT_EIGHT: case GARAGE_HIDEOUT_NINE: case GARAGE_HIDEOUT_TEN: case GARAGE_HIDEOUT_ELEVEN: case GARAGE_HIDEOUT_TWELVE: if (aGarages[i].IsPointInsideGarage(point)) return true; default: break; } } return false; } bool CGarages::IsPointWithinAnyGarage(Const CVector& point) { #ifdef FIX_BUGS for (uint32 i = 0; i < NumGarages; i++) { #else for (int i = 0; i < NUM_GARAGES; i++) { #endif switch (aGarages[i].m_eGarageType) { case GARAGE_NONE: continue; default: if (aGarages[i].IsPointInsideGarage(point)) return true; } } return false; } void CGarages::SetAllDoorsBackToOriginalHeight() { #ifdef FIX_BUGS for (uint32 i = 0; i < NumGarages; i++) { #else for (int i = 0; i < NUM_GARAGES; i++) { #endif switch (aGarages[i].m_eGarageType) { case GARAGE_NONE: continue; default: aGarages[i].RefreshDoorPointers(true); if (aGarages[i].m_pDoor1) { aGarages[i].m_pDoor1->GetMatrix().GetPosition().x = aGarages[i].m_fDoor1X; aGarages[i].m_pDoor1->GetMatrix().GetPosition().y = aGarages[i].m_fDoor1Y; aGarages[i].m_pDoor1->GetMatrix().GetPosition().z = aGarages[i].m_fDoor1Z; if (aGarages[i].m_pDoor1->IsObject()) ((CObject*)aGarages[i].m_pDoor1)->m_objectMatrix.GetPosition().z = aGarages[i].m_fDoor1Z; if (aGarages[i].m_bRotatedDoor) aGarages[i].BuildRotatedDoorMatrix(aGarages[i].m_pDoor1, 0.0f); aGarages[i].m_pDoor1->GetMatrix().UpdateRW(); aGarages[i].m_pDoor1->UpdateRwFrame(); } if (aGarages[i].m_pDoor2) { aGarages[i].m_pDoor2->GetMatrix().GetPosition().x = aGarages[i].m_fDoor2X; aGarages[i].m_pDoor2->GetMatrix().GetPosition().y = aGarages[i].m_fDoor2Y; aGarages[i].m_pDoor2->GetMatrix().GetPosition().z = aGarages[i].m_fDoor2Z; if (aGarages[i].m_pDoor2->IsObject()) ((CObject*)aGarages[i].m_pDoor2)->m_objectMatrix.GetPosition().z = aGarages[i].m_fDoor2Z; if (aGarages[i].m_bRotatedDoor) aGarages[i].BuildRotatedDoorMatrix(aGarages[i].m_pDoor2, 0.0f); aGarages[i].m_pDoor2->GetMatrix().UpdateRW(); aGarages[i].m_pDoor2->UpdateRwFrame(); } } } } void CGarages::Save(uint8 * buf, uint32 * size) { //INITSAVEBUF *size = 7876; // for some reason it's not actual size again //*size = (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + TOTAL_HIDEOUT_GARAGES * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage)); CloseHideOutGaragesBeforeSave(); WriteSaveBuf(buf, NumGarages); WriteSaveBuf(buf, (uint32)BombsAreFree); WriteSaveBuf(buf, (uint32)RespraysAreFree); WriteSaveBuf(buf, CarsCollected); WriteSaveBuf(buf, BankVansCollected); WriteSaveBuf(buf, PoliceCarsCollected); for (int i = 0; i < TOTAL_COLLECTCARS_GARAGES; i++) WriteSaveBuf(buf, CarTypesCollected[i]); WriteSaveBuf(buf, LastTimeHelpMessage); for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { for (int j = 0; j < TOTAL_HIDEOUT_GARAGES; j++) { WriteSaveBuf(buf, aCarsInSafeHouses[j][i]); } } for (int i = 0; i < NUM_GARAGES; i++) WriteSaveBuf(buf, aGarages[i]); //VALIDATESAVEBUF(*size); } const CStoredCar &CStoredCar::operator=(const CStoredCar & other) { m_nModelIndex = other.m_nModelIndex; m_vecPos = other.m_vecPos; m_vecAngle = other.m_vecAngle; m_bBulletproof = other.m_bBulletproof; m_bFireproof = other.m_bFireproof; m_bExplosionproof = other.m_bExplosionproof; m_bCollisionproof = other.m_bCollisionproof; m_bMeleeproof = other.m_bMeleeproof; m_nPrimaryColor = other.m_nPrimaryColor; m_nSecondaryColor = other.m_nSecondaryColor; m_nRadioStation = other.m_nRadioStation; m_nVariationA = other.m_nVariationA; m_nVariationB = other.m_nVariationB; m_nCarBombType = other.m_nCarBombType; return *this; } void CGarages::Load(uint8* buf, uint32 size) { //INITSAVEBUF assert(size = 7876); //assert(size == (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + TOTAL_HIDEOUT_GARAGES * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage))); CloseHideOutGaragesBeforeSave(); NumGarages = ReadSaveBuf(buf); BombsAreFree = ReadSaveBuf(buf); RespraysAreFree = ReadSaveBuf(buf); CarsCollected = ReadSaveBuf(buf); BankVansCollected = ReadSaveBuf(buf); PoliceCarsCollected = ReadSaveBuf(buf); for (int i = 0; i < TOTAL_COLLECTCARS_GARAGES; i++) CarTypesCollected[i] = ReadSaveBuf(buf); LastTimeHelpMessage = ReadSaveBuf(buf); for (int i = 0; i < NUM_GARAGE_STORED_CARS; i++) { for (int j = 0; j < TOTAL_HIDEOUT_GARAGES; j++) { aCarsInSafeHouses[j][i] = ReadSaveBuf(buf); } } for (int i = 0; i < NUM_GARAGES; i++) { aGarages[i] = ReadSaveBuf(buf); aGarages[i].m_pDoor1 = nil; aGarages[i].m_pDoor2 = nil; aGarages[i].m_pTarget = nil; aGarages[i].m_bRecreateDoorOnNextRefresh = true; aGarages[i].RefreshDoorPointers(true); if (aGarages[i].m_eGarageType == GARAGE_CRUSHER) aGarages[i].UpdateCrusherAngle(); else aGarages[i].UpdateDoorsHeight(); } //VALIDATESAVEBUF(size); MessageEndTime = 0; bCamShouldBeOutisde = false; MessageStartTime = 0; } bool CGarages::IsModelIndexADoor(uint32 id) { return id == MI_GARAGEDOOR2 || id == MI_GARAGEDOOR3 || id == MI_GARAGEDOOR4 || id == MI_GARAGEDOOR5 || id == MI_GARAGEDOOR6 || id == MI_GARAGEDOOR7 || id == MI_GARAGEDOOR9 || id == MI_GARAGEDOOR10 || id == MI_GARAGEDOOR11 || id == MI_GARAGEDOOR12 || id == MI_GARAGEDOOR13 || id == MI_GARAGEDOOR14 || id == MI_GARAGEDOOR15 || id == MI_GARAGEDOOR16 || id == MI_GARAGEDOOR18 || id == MI_GARAGEDOOR19 || id == MI_GARAGEDOOR20 || id == MI_GARAGEDOOR21 || id == MI_GARAGEDOOR22 || id == MI_GARAGEDOOR23 || id == MI_GARAGEDOOR24 || id == MI_GARAGEDOOR25 || id == MI_GARAGEDOOR26; } void CGarages::StopCarFromBlowingUp(CAutomobile* pCar) { pCar->m_fFireBlowUpTimer = 0.0f; pCar->m_fHealth = Max(pCar->m_fHealth, 300.0f); pCar->Damage.SetEngineStatus(Max(pCar->Damage.GetEngineStatus(), 275)); } bool CGarage::Does60SecondsNeedThisCarAtAll(int mi) { for (int i = 0; i < ARRAY_SIZE(gaCarsToCollectIn60Seconds); i++) { if (gaCarsToCollectIn60Seconds[i] == mi) return true; } return false; } bool CGarage::Does60SecondsNeedThisCar(int mi) { for (int i = 0; i < ARRAY_SIZE(gaCarsToCollectIn60Seconds); i++) { if (gaCarsToCollectIn60Seconds[i] == mi) return m_bCollectedCarsState & BIT(i); } return false; } void CGarage::MarkThisCarAsCollectedFor60Seconds(int mi) { for (int i = 0; i < ARRAY_SIZE(gaCarsToCollectIn60Seconds); i++) { if (gaCarsToCollectIn60Seconds[i] == mi) m_bCollectedCarsState |= BIT(i); } } bool CGarage::IsPlayerEntirelyInsideGarage() { return IsEntityEntirelyInside3D(FindPlayerVehicle() ? (CEntity*)FindPlayerVehicle() : (CEntity*)FindPlayerPed(), 0.0f); } ================================================ FILE: src/control/Garages.h ================================================ #pragma once #include "audio_enums.h" #include "Camera.h" #include "config.h" #include "Lists.h" class CVehicle; enum eGarageState { GS_FULLYCLOSED, GS_OPENED, GS_CLOSING, GS_OPENING, GS_OPENEDCONTAINSCAR, GS_CLOSEDCONTAINSCAR, GS_AFTERDROPOFF, }; enum eGarageType { GARAGE_NONE, GARAGE_MISSION, GARAGE_BOMBSHOP1, GARAGE_BOMBSHOP2, GARAGE_BOMBSHOP3, GARAGE_RESPRAY, GARAGE_COLLECTORSITEMS, GARAGE_COLLECTSPECIFICCARS, GARAGE_COLLECTCARS_1, GARAGE_COLLECTCARS_2, GARAGE_COLLECTCARS_3, GARAGE_FORCARTOCOMEOUTOF, GARAGE_60SECONDS, GARAGE_CRUSHER, GARAGE_MISSION_KEEPCAR, GARAGE_FOR_SCRIPT_TO_OPEN, GARAGE_HIDEOUT_ONE, GARAGE_HIDEOUT_TWO, GARAGE_HIDEOUT_THREE, GARAGE_FOR_SCRIPT_TO_OPEN_AND_CLOSE, GARAGE_KEEPS_OPENING_FOR_SPECIFIC_CAR, GARAGE_MISSION_KEEPCAR_REMAINCLOSED, GARAGE_COLLECTCARS_4, GARAGE_FOR_SCRIPT_TO_OPEN_FOR_CAR, GARAGE_HIDEOUT_FOUR, GARAGE_HIDEOUT_FIVE, GARAGE_HIDEOUT_SIX, GARAGE_HIDEOUT_SEVEN, GARAGE_HIDEOUT_EIGHT, GARAGE_HIDEOUT_NINE, GARAGE_HIDEOUT_TEN, GARAGE_HIDEOUT_ELEVEN, GARAGE_HIDEOUT_TWELVE }; enum { TOTAL_COLLECTCARS_GARAGES = 4, TOTAL_HIDEOUT_GARAGES = 12, TOTAL_COLLECTCARS_CARS = 6 }; class CStoredCar { int32 m_nModelIndex; CVector m_vecPos; CVector m_vecAngle; int32 m_bBulletproof : 1; int32 m_bFireproof : 1; int32 m_bExplosionproof : 1; int32 m_bCollisionproof : 1; int32 m_bMeleeproof : 1; int8 m_nPrimaryColor; int8 m_nSecondaryColor; int8 m_nRadioStation; int8 m_nVariationA; int8 m_nVariationB; int8 m_nCarBombType; public: void Init() { m_nModelIndex = 0; } void Clear() { m_nModelIndex = 0; } bool HasCar() { return m_nModelIndex != 0; } const CStoredCar &operator=(const CStoredCar& other); void StoreCar(CVehicle*); CVehicle* RestoreCar(); }; VALIDATE_SIZE(CStoredCar, 0x28); #define SWITCH_GARAGE_DISTANCE_CLOSE 40.0f class CGarage { public: uint8 m_eGarageType; uint8 m_eGarageState; uint8 m_nMaxStoredCars; bool field_2; // unused bool m_bClosingWithoutTargetCar; bool m_bDeactivated; bool m_bResprayHappened; int m_nTargetModelIndex; CEntity *m_pDoor1; CEntity *m_pDoor2; uint8 m_bDoor1PoolIndex; uint8 m_bDoor2PoolIndex; bool m_bDoor1IsDummy; bool m_bDoor2IsDummy; bool m_bRecreateDoorOnNextRefresh; bool m_bRotatedDoor; bool m_bCameraFollowsPlayer; CVector2D m_vecCorner1; float m_fInfZ; CVector2D m_vDir1; CVector2D m_vDir2; float m_fSupZ; float m_fDir1Len; float m_fDir2Len; float m_fInfX; float m_fSupX; float m_fInfY; float m_fSupY; float m_fDoorPos; float m_fDoorHeight; float m_fDoor1X; float m_fDoor1Y; float m_fDoor2X; float m_fDoor2Y; float m_fDoor1Z; float m_fDoor2Z; uint32 m_nTimeToStartAction; uint8 m_bCollectedCarsState; CVehicle *m_pTarget; CStoredCar m_sStoredCar; // not needed void OpenThisGarage(); void CloseThisGarage(); bool IsOpen() { return m_eGarageState == GS_OPENED || m_eGarageState == GS_OPENEDCONTAINSCAR; } bool IsClosed() { return m_eGarageState == GS_FULLYCLOSED; } bool IsUsed() { return m_eGarageType != GARAGE_NONE; } void Update(); float GetGarageCenterX() { return (m_fInfX + m_fSupX) / 2; } float GetGarageCenterY() { return (m_fInfY + m_fSupY) / 2; } bool IsFar() { #ifdef FIX_BUGS return Abs(TheCamera.GetPosition().x - GetGarageCenterX()) > SWITCH_GARAGE_DISTANCE_CLOSE || Abs(TheCamera.GetPosition().y - GetGarageCenterY()) > SWITCH_GARAGE_DISTANCE_CLOSE; #else return Abs(TheCamera.GetPosition().x - m_fInfX) > SWITCH_GARAGE_DISTANCE_CLOSE || Abs(TheCamera.GetPosition().y - m_fInfY) > SWITCH_GARAGE_DISTANCE_CLOSE; #endif } void TidyUpGarageClose(); void TidyUpGarage(); void RefreshDoorPointers(bool); void UpdateCrusherAngle(); void UpdateDoorsHeight(); bool IsEntityEntirelyInside3D(CEntity*, float); bool IsEntityEntirelyOutside(CEntity*, float); float CalcDistToGarageRectangleSquared(float, float); float CalcSmallestDistToGarageDoorSquared(float, float); bool IsAnyOtherCarTouchingGarage(CVehicle* pException); bool IsStaticPlayerCarEntirelyInside(); bool IsPlayerOutsideGarage(); bool IsAnyCarBlockingDoor(); void CenterCarInGarage(CVehicle*); bool DoesCraigNeedThisCar(int32); bool MarkThisCarAsCollectedForCraig(int32); bool HasCraigCollectedThisCar(int32); bool IsGarageEmpty(); void UpdateCrusherShake(float, float); int32 CountCarsWithCenterPointWithinGarage(CEntity* pException); void RemoveCarsBlockingDoorNotInside(); void StoreAndRemoveCarsForThisHideout(CStoredCar*, int32); bool RestoreCarsForThisHideout(CStoredCar*); bool IsEntityTouching3D(CEntity*); bool EntityHasASphereWayOutsideGarage(CEntity*, float); bool IsAnyOtherPedTouchingGarage(CPed* pException); void BuildRotatedDoorMatrix(CEntity*, float); void FindDoorsEntities(); void FindDoorsEntitiesSectorList(CPtrList&, bool); void PlayerArrestedOrDied(); bool Does60SecondsNeedThisCarAtAll(int mi); bool Does60SecondsNeedThisCar(int mi); void MarkThisCarAsCollectedFor60Seconds(int mi); bool IsPlayerEntirelyInsideGarage(); bool IsPointInsideGarage(CVector); bool IsPointInsideGarage(CVector, float); void ThrowCarsNearDoorOutOfGarage(CVehicle*); int32 FindMaxNumStoredCarsForGarage() { return Min(NUM_GARAGE_STORED_CARS, m_nMaxStoredCars); } }; class CGarages { enum { MESSAGE_LENGTH = 8, }; public: static int32 BankVansCollected; static bool BombsAreFree; static bool RespraysAreFree; static int32 CarsCollected; static int32 CarTypesCollected[TOTAL_COLLECTCARS_GARAGES]; static int32 CrushedCarId; static uint32 LastTimeHelpMessage; static int32 MessageNumberInString; static char MessageIDString[MESSAGE_LENGTH]; static int32 MessageNumberInString2; static uint32 MessageStartTime; static uint32 MessageEndTime; static uint32 NumGarages; static bool PlayerInGarage; static int32 PoliceCarsCollected; static CGarage aGarages[NUM_GARAGES]; static CStoredCar aCarsInSafeHouses[TOTAL_HIDEOUT_GARAGES][NUM_GARAGE_STORED_CARS]; static bool bCamShouldBeOutisde; static void Init(void); #ifndef PS2 static void Shutdown(void); #endif static void Update(void); static int16 AddOne(float X1, float Y1, float Z1, float X2, float Y2, float X3, float Y3, float Z2, uint8 type, int32 targetId); static void ChangeGarageType(int16, uint8, int32); static void PrintMessages(void); static void TriggerMessage(const char* text, int16, uint16 time, int16); static void SetTargetCarForMissonGarage(int16, CVehicle*); static bool HasCarBeenDroppedOffYet(int16); static void DeActivateGarage(int16); static void ActivateGarage(int16); static int32 QueryCarsCollected(int16); static bool HasImportExportGarageCollectedThisCar(int16, int8); static bool IsGarageOpen(int16); static bool IsGarageClosed(int16); static bool HasThisCarBeenCollected(int16, uint8); static void OpenGarage(int16 garage) { aGarages[garage].OpenThisGarage(); } static void CloseGarage(int16 garage) { aGarages[garage].CloseThisGarage(); } static bool HasResprayHappened(int16); static void SetGarageDoorToRotate(int16); static void SetLeaveCameraForThisGarage(int16); static bool IsThisCarWithinGarageArea(int16, CEntity*); static bool HasCarBeenCrushed(int32); static bool IsPointInAGarageCameraZone(CVector); static bool CameraShouldBeOutside(void); static void GivePlayerDetonator(void); static void PlayerArrestedOrDied(void); static bool IsPointWithinHideOutGarage(Const CVector&); static bool IsPointWithinAnyGarage(Const CVector&); static void SetAllDoorsBackToOriginalHeight(void); static void Save(uint8* buf, uint32* size); static void Load(uint8* buf, uint32 size); static bool IsModelIndexADoor(uint32 id); static void SetFreeBombs(bool bValue) { BombsAreFree = bValue; } static void SetFreeResprays(bool bValue) { RespraysAreFree = bValue; } static void StopCarFromBlowingUp(CAutomobile*); static void SetMaxNumStoredCarsForGarage(int16 garage, uint8 num) { aGarages[garage].m_nMaxStoredCars = num; } static bool IsCarSprayable(CVehicle*); static float FindDoorHeightForMI(int32); static void CloseHideOutGaragesBeforeSave(void); static int32 CountCarsInHideoutGarage(uint8); static int32 GetBombTypeForGarageType(uint8 type) { return type - GARAGE_BOMBSHOP1 + 1; } static int32 GetCarsCollectedIndexForGarageType(uint8 type) { switch (type) { case GARAGE_COLLECTCARS_1: return 0; case GARAGE_COLLECTCARS_2: return 1; case GARAGE_COLLECTCARS_3: return 2; case GARAGE_COLLECTCARS_4: return 3; default: assert(0); } return 0; } static int32 FindSafeHouseIndexForGarageType(uint8 type) { switch (type) { case GARAGE_HIDEOUT_ONE: return 0; case GARAGE_HIDEOUT_TWO: return 1; case GARAGE_HIDEOUT_THREE: return 2; case GARAGE_HIDEOUT_FOUR: return 3; case GARAGE_HIDEOUT_FIVE: return 4; case GARAGE_HIDEOUT_SIX: return 5; case GARAGE_HIDEOUT_SEVEN: return 6; case GARAGE_HIDEOUT_EIGHT: return 7; case GARAGE_HIDEOUT_NINE: return 8; case GARAGE_HIDEOUT_TEN: return 9; case GARAGE_HIDEOUT_ELEVEN: return 10; case GARAGE_HIDEOUT_TWELVE: return 11; } return -1; } static bool IsThisGarageTypeSafehouse(uint8 type) { return FindSafeHouseIndexForGarageType(type) >= 0; } }; ================================================ FILE: src/control/NameGrid.cpp ================================================ #include "common.h" #include "NameGrid.h" // TODO: reverse mobile code CPlayerName::CPlayerName() { // TODO } void CPlayerName::DisplayName(int) { // TODO } CRow::CRow() { // TODO } void CRow::SetLetter(int, wchar *) { // TODO } CGrid::CGrid() { // TODO } void CGrid::ProcessAnyLeftJustDown() { unk_int2--; } void CGrid::ProcessAnyRightJustDown() { unk_int2++; } void CGrid::ProcessAnyUpJustDown() { unk_int1--; } void CGrid::ProcessAnyDownJustDown() { unk_int1++; } void CGrid::AllDoneMakePlayerName() { // TODO } void CGrid::ProcessDPadCrossJustDown() { // TODO } void CGrid::DisplayGrid() { // TODO } void CGrid::ProcessControllerInput() { // TODO } void CGrid::Process() { ProcessControllerInput(); DisplayGrid(); playerName.DisplayName(2 * playerName.unk_4c); } ================================================ FILE: src/control/NameGrid.h ================================================ #pragma once // TODO: reverse mobile code class CPlayerName { friend class CGrid; float x; float y; wchar unk_8[34]; int unk_4c; public: CPlayerName(); void DisplayName(int); }; class CRow { friend class CGrid; int unk_0; int unk_4; wchar unk_8[20]; int unk_30; public: CRow(); void SetLetter(int, wchar *); }; class CGrid { CRow rows[5]; int unk_int1; int unk_int2; int unk_int3; float unk_float1; float unk_float2; CPlayerName playerName; char unk2[4]; char unk3[4]; public: CGrid(); void ProcessAnyLeftJustDown(); void ProcessAnyRightJustDown(); void ProcessAnyUpJustDown(); void ProcessAnyDownJustDown(); void AllDoneMakePlayerName(); void ProcessDPadCrossJustDown(); void DisplayGrid(); void ProcessControllerInput(); void Process(); }; ================================================ FILE: src/control/OnscreenTimer.cpp ================================================ #include "common.h" #include "DMAudio.h" #include "Hud.h" #include "Replay.h" #include "Timer.h" #include "Script.h" #include "OnscreenTimer.h" #include "Camera.h" void COnscreenTimer::Init() { m_bDisabled = false; for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { m_sCounters[i].m_nCounterOffset = 0; for(uint32 j = 0; j < ARRAY_SIZE(m_sCounters[0].m_aCounterText); j++) { m_sCounters[i].m_aCounterText[j] = 0; } m_sCounters[i].m_nType = COUNTER_DISPLAY_NUMBER; m_sCounters[i].m_bCounterProcessed = false; } for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) { m_sClocks[i].m_nClockOffset = 0; for(uint32 j = 0; j < ARRAY_SIZE(m_sClocks[0].m_aClockText); j++) { m_sClocks[i].m_aClockText[j] = 0; } m_sClocks[i].m_bClockProcessed = false; m_sClocks[i].m_bClockGoingDown = true; } } void COnscreenTimer::Process() { if(!CReplay::IsPlayingBack() && !m_bDisabled) { for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) { m_sClocks[i].Process(); } } } void COnscreenTimer::ProcessForDisplay() { if(CHud::m_Wants_To_Draw_Hud) { m_bProcessed = false; for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) { m_sClocks[i].m_bClockProcessed = false; if (m_sClocks[i].m_nClockOffset != 0) { m_sClocks[i].ProcessForDisplayClock(); m_sClocks[i].m_bClockProcessed = true; m_bProcessed = true; } } for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { m_sCounters[i].m_bCounterProcessed = false; if (m_sCounters[i].m_nCounterOffset != 0) { m_sCounters[i].ProcessForDisplayCounter(); m_sCounters[i].m_bCounterProcessed = true; m_bProcessed = true; } } } } void COnscreenTimer::ClearCounter(uint32 offset) { for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { if(offset == m_sCounters[i].m_nCounterOffset) { m_sCounters[i].m_nCounterOffset = 0; m_sCounters[i].m_aCounterText[0] = 0; m_sCounters[i].m_nType = COUNTER_DISPLAY_NUMBER; m_sCounters[i].m_bCounterProcessed = 0; } } } void COnscreenTimer::ClearClock(uint32 offset) { for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) { if(offset == m_sClocks[i].m_nClockOffset) { m_sClocks[i].m_nClockOffset = 0; m_sClocks[i].m_aClockText[0] = 0; m_sClocks[i].m_bClockProcessed = 0; m_sClocks[i].m_bClockGoingDown = true; } } } void COnscreenTimer::AddCounter(uint32 offset, uint16 type, char* text, uint16 pos) { if (m_sCounters[pos].m_aCounterText[0] != '\0') return; m_sCounters[pos].m_nCounterOffset = offset; if(text) { strncpy(m_sCounters[pos].m_aCounterText, text, ARRAY_SIZE(m_sCounters[0].m_aCounterText)); } else { m_sCounters[pos].m_aCounterText[0] = 0; } m_sCounters[pos].m_nType = type; } void COnscreenTimer::AddClock(uint32 offset, char* text, bool bGoingDown) { // dead code in here uint32 i; for(i = 0; i < NUMONSCREENCLOCKS; i++) { if(m_sClocks[i].m_nClockOffset == 0) { break; } return; } m_sClocks[i].m_nClockOffset = offset; m_sClocks[i].m_bClockGoingDown = bGoingDown; if(text) { strncpy(m_sClocks[i].m_aClockText, text, ARRAY_SIZE(m_sClocks[0].m_aClockText)); } else { m_sClocks[i].m_aClockText[0] = 0; } } void COnscreenTimerEntry::Process() { if(m_nClockOffset == 0) { return; } int32* timerPtr = CTheScripts::GetPointerToScriptVariable(m_nClockOffset); int32 oldTime = *timerPtr; if (m_bClockGoingDown) { int32 newTime = oldTime - int32(CTimer::GetTimeStepInMilliseconds()); *timerPtr = newTime; if (newTime < 0) { *timerPtr = 0; m_bClockProcessed = 0; m_nClockOffset = 0; m_aClockText[0] = 0; } else { int32 oldTimeSeconds = oldTime / 1000; if (oldTimeSeconds < 12 && newTime / 1000 != oldTimeSeconds && !TheCamera.m_WideScreenOn) { DMAudio.PlayFrontEndSound(SOUND_CLOCK_TICK, newTime / 1000); } } } else *timerPtr = oldTime + int32(CTimer::GetTimeStepInMilliseconds()); } void COnscreenTimerEntry::ProcessForDisplayClock() { uint32 time = *CTheScripts::GetPointerToScriptVariable(m_nClockOffset); sprintf(m_aClockBuffer, "%02d:%02d", time / 1000 / 60 % 100, time / 1000 % 60); } void COnscreenCounterEntry::ProcessForDisplayCounter() { uint32 counter = *CTheScripts::GetPointerToScriptVariable(m_nCounterOffset); sprintf(m_aCounterBuffer, "%d", counter); } ================================================ FILE: src/control/OnscreenTimer.h ================================================ #pragma once enum { COUNTER_DISPLAY_NUMBER, COUNTER_DISPLAY_BAR, }; class COnscreenTimerEntry { public: uint32 m_nClockOffset; char m_aClockText[10]; char m_aClockBuffer[40]; bool m_bClockProcessed; bool m_bClockGoingDown; void Process(); void ProcessForDisplayClock(); }; VALIDATE_SIZE(COnscreenTimerEntry, 0x3C); class COnscreenCounterEntry { public: uint32 m_nCounterOffset; char m_aCounterText[10]; uint16 m_nType; char m_aCounterBuffer[40]; bool m_bCounterProcessed; void ProcessForDisplayCounter(); }; VALIDATE_SIZE(COnscreenCounterEntry, 0x3C); class COnscreenTimer { public: COnscreenTimerEntry m_sClocks[NUMONSCREENCLOCKS]; COnscreenCounterEntry m_sCounters[NUMONSCREENCOUNTERS]; bool m_bProcessed; bool m_bDisabled; void Init(); void Process(); void ProcessForDisplay(); void ClearCounter(uint32 offset); void ClearClock(uint32 offset); void AddCounter(uint32 offset, uint16 type, char* text, uint16 pos); void AddClock(uint32 offset, char* text, bool bGoingDown); }; VALIDATE_SIZE(COnscreenTimer, 0xF4); ================================================ FILE: src/control/PathFind.cpp ================================================ #include "common.h" #include "General.h" #include "FileMgr.h" // only needed for empty function #include "Camera.h" #include "Vehicle.h" #include "World.h" #include "Lines.h" // for debug #include "PathFind.h" bool gbShowPedPaths; bool gbShowCarPaths; bool gbShowCarPathsLinks; CPathFind ThePaths; #define MAX_DIST INT16_MAX-1 #define MIN_PED_ROUTE_DISTANCE 23.8f #define NUMTEMPNODES 5000 #define NUMDETACHED_CARS 1024 #define NUMDETACHED_PEDS 1214 #define NUMTEMPEXTERNALNODES 4600 CPathInfoForObject *InfoForTileCars; CPathInfoForObject *InfoForTilePeds; CPathInfoForObject *DetachedInfoForTileCars; CPathInfoForObject *DetachedInfoForTilePeds; CTempNodeExternal *TempExternalNodes; int32 NumTempExternalNodes; int32 NumDetachedPedNodeGroups; int32 NumDetachedCarNodeGroups; bool CPedPath::CalcPedRoute(int8 pathType, CVector position, CVector destination, CVector *pointPoses, int16 *pointsFound, int16 maxPoints) { *pointsFound = 0; CVector vecDistance = destination - position; if (Abs(vecDistance.x) > MIN_PED_ROUTE_DISTANCE || Abs(vecDistance.y) > MIN_PED_ROUTE_DISTANCE || Abs(vecDistance.z) > MIN_PED_ROUTE_DISTANCE) return false; CVector vecPos = (position + destination) * 0.5f; CVector vecSectorStartPos (vecPos.x - 14.0f, vecPos.y - 14.0f, vecPos.z); CVector2D vecSectorEndPos (vecPos.x + 28.0f, vecPos.x + 28.0f); const int16 nodeStartX = (position.x - vecSectorStartPos.x) / 0.7f; const int16 nodeStartY = (position.y - vecSectorStartPos.y) / 0.7f; const int16 nodeEndX = (destination.x - vecSectorStartPos.x) / 0.7f; const int16 nodeEndY = (destination.y - vecSectorStartPos.y) / 0.7f; if (nodeStartX == nodeEndX && nodeStartY == nodeEndY) return false; CPedPathNode pathNodes[40][40]; CPedPathNode pathNodesList[416]; for (int32 x = 0; x < 40; x++) { for (int32 y = 0; y < 40; y++) { pathNodes[x][y].bBlockade = false; pathNodes[x][y].id = INT16_MAX; pathNodes[x][y].nodeIdX = x; pathNodes[x][y].nodeIdY = y; } } CWorld::AdvanceCurrentScanCode(); if (pathType != ROUTE_NO_BLOCKADE) { const int32 nStartX = Max(CWorld::GetSectorIndexX(vecSectorStartPos.x), 0); const int32 nStartY = Max(CWorld::GetSectorIndexY(vecSectorStartPos.y), 0); const int32 nEndX = Min(CWorld::GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X - 1); const int32 nEndY = Min(CWorld::GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y - 1); for (int32 y = nStartY; y <= nEndY; y++) { for (int32 x = nStartX; x <= nEndX; x++) { CSector *pSector = CWorld::GetSector(x, y); AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_VEHICLES], pathNodes, &vecSectorStartPos); AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], pathNodes, &vecSectorStartPos); AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_OBJECTS], pathNodes, &vecSectorStartPos); AddBlockadeSectorList(pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], pathNodes, &vecSectorStartPos); } } } for (int32 i = 0; i < 416; i++) { pathNodesList[i].prev = nil; pathNodesList[i].next = nil; } CPedPathNode *pStartPathNode = &pathNodes[nodeStartX][nodeStartY]; CPedPathNode *pEndPathNode = &pathNodes[nodeEndX][nodeEndY]; pEndPathNode->bBlockade = false; pEndPathNode->id = 0; pEndPathNode->prev = nil; pEndPathNode->next = pathNodesList; pathNodesList[0].prev = pEndPathNode; int32 pathNodeIndex = 0; CPedPathNode *pPreviousNode = nil; for (; pathNodeIndex < 414; pathNodeIndex++) { pPreviousNode = pathNodesList[pathNodeIndex].prev; while (pPreviousNode && pPreviousNode != pStartPathNode) { const uint8 nodeIdX = pPreviousNode->nodeIdX; const uint8 nodeIdY = pPreviousNode->nodeIdY; if (nodeIdX > 0) { AddNodeToPathList(&pathNodes[nodeIdX - 1][nodeIdY], pathNodeIndex + 5, pathNodesList); if (nodeIdY > 0) AddNodeToPathList(&pathNodes[nodeIdX - 1][nodeIdY - 1], pathNodeIndex + 7, pathNodesList); if (nodeIdY < 39) AddNodeToPathList(&pathNodes[nodeIdX - 1][nodeIdY + 1], pathNodeIndex + 7, pathNodesList); } if (nodeIdX < 39) { AddNodeToPathList(&pathNodes[nodeIdX + 1][nodeIdY], pathNodeIndex + 5, pathNodesList); if (nodeIdY > 0) AddNodeToPathList(&pathNodes[nodeIdX + 1][nodeIdY - 1], pathNodeIndex + 7, pathNodesList); if (nodeIdY < 39) AddNodeToPathList(&pathNodes[nodeIdX + 1][nodeIdY + 1], pathNodeIndex + 7, pathNodesList); } if (nodeIdY > 0) AddNodeToPathList(&pathNodes[nodeIdX][nodeIdY - 1], pathNodeIndex + 5, pathNodesList); if (nodeIdY < 39) AddNodeToPathList(&pathNodes[nodeIdX][nodeIdY + 1], pathNodeIndex + 5, pathNodesList); pPreviousNode = pPreviousNode->prev; if (!pPreviousNode) break; } if (pPreviousNode && pPreviousNode == pStartPathNode) break; } if (pathNodeIndex == 414) return false; CPedPathNode *pPathNode = pStartPathNode; for (*pointsFound = 0; pPathNode != pEndPathNode && *pointsFound < maxPoints; ++ *pointsFound) { const uint8 nodeIdX = pPathNode->nodeIdX; const uint8 nodeIdY = pPathNode->nodeIdY; if (nodeIdX > 0 && pathNodes[nodeIdX - 1][nodeIdY].id + 5 == pPathNode->id) pPathNode = &pathNodes[nodeIdX - 1][nodeIdY]; else if (nodeIdX > 39 && pathNodes[nodeIdX + 1][nodeIdY].id + 5 == pPathNode->id) pPathNode = &pathNodes[nodeIdX + 1][nodeIdY]; else if (nodeIdY > 0 && pathNodes[nodeIdX][nodeIdY - 1].id + 5 == pPathNode->id) pPathNode = &pathNodes[nodeIdX][nodeIdY - 1]; else if (nodeIdY > 39 && pathNodes[nodeIdX][nodeIdY + 1].id + 5 == pPathNode->id) pPathNode = &pathNodes[nodeIdX][nodeIdY + 1]; else if (nodeIdX > 0 && nodeIdY > 0 && pathNodes[nodeIdX - 1][nodeIdY - 1].id + 7 == pPathNode->id) pPathNode = &pathNodes[nodeIdX - 1][nodeIdY - 1]; else if (nodeIdX > 0 && nodeIdY < 39 && pathNodes[nodeIdX - 1][nodeIdY + 1].id + 7 == pPathNode->id) pPathNode = &pathNodes[nodeIdX - 1][nodeIdY + 1]; else if (nodeIdX < 39 && nodeIdY > 0 && pathNodes[nodeIdX + 1][nodeIdY - 1].id + 7 == pPathNode->id) pPathNode = &pathNodes[nodeIdX + 1][nodeIdY - 1]; else if (nodeIdX < 39 && nodeIdY < 39 && pathNodes[nodeIdX + 1][nodeIdY + 1].id + 7 == pPathNode->id) pPathNode = &pathNodes[nodeIdX + 1][nodeIdY + 1]; pointPoses[*pointsFound] = vecSectorStartPos; pointPoses[*pointsFound].x += pPathNode->nodeIdX * 0.7f; pointPoses[*pointsFound].y += pPathNode->nodeIdY * 0.7f; } return true; } void CPedPath::AddNodeToPathList(CPedPathNode *pNodeToAdd, int16 id, CPedPathNode *pNodeList) { if (!pNodeToAdd->bBlockade && id < pNodeToAdd->id) { if (pNodeToAdd->id != INT16_MAX) RemoveNodeFromList(pNodeToAdd); AddNodeToList(pNodeToAdd, id, pNodeList); } } void CPedPath::RemoveNodeFromList(CPedPathNode *pNode) { pNode->next->prev = pNode->prev; if (pNode->prev) pNode->prev->next = pNode->next; } void CPedPath::AddNodeToList(CPedPathNode *pNode, int16 index, CPedPathNode *pList) { pNode->prev = pList[index].prev; pNode->next = &pList[index]; if (pList[index].prev) pList[index].prev->next = pNode; pList[index].prev = pNode; pNode->id = index; } void CPedPath::AddBlockadeSectorList(CPtrList& list, CPedPathNode(*pathNodes)[40], CVector *pPosition) { CPtrNode* listNode = list.first; while (listNode) { CEntity* pEntity = (CEntity*)listNode->item; if (pEntity->m_scanCode != CWorld::GetCurrentScanCode() && pEntity->bUsesCollision) { pEntity->m_scanCode = CWorld::GetCurrentScanCode(); AddBlockade(pEntity, pathNodes, pPosition); } listNode = listNode->next; } } void CPedPath::AddBlockade(CEntity *pEntity, CPedPathNode(*pathNodes)[40], CVector *pPosition) { const CBox& boundingBox = pEntity->GetColModel()->boundingBox; const float fBoundMaxY = boundingBox.max.y + 0.3f; const float fBoundMinY = boundingBox.min.y - 0.3f; const float fBoundMaxX = boundingBox.max.x + 0.3f; const float fDistanceX = pPosition->x - pEntity->m_matrix.GetPosition().x; const float fDistanceY = pPosition->y - pEntity->m_matrix.GetPosition().y; const float fBoundRadius = pEntity->GetBoundRadius(); CVector vecBoundCentre; pEntity->GetBoundCentre(vecBoundCentre); if (vecBoundCentre.x + fBoundRadius >= pPosition->x && vecBoundCentre.y + fBoundRadius >= pPosition->y && vecBoundCentre.x - fBoundRadius <= pPosition->x + 28.0f && vecBoundCentre.y - fBoundRadius <= pPosition->y + 28.0f) { for (int16 x = 0; x < 40; x++) { const float pointX = x * 0.7f + fDistanceX; for (int16 y = 0; y < 40; y++) { if (!pathNodes[x][y].bBlockade) { const float pointY = y * 0.7f + fDistanceY; CVector2D point(pointX, pointY); if (fBoundMaxX > Abs(DotProduct2D(point, pEntity->m_matrix.GetRight()))) { float fDotProduct = DotProduct2D(point, pEntity->m_matrix.GetForward()); if (fBoundMaxY > fDotProduct && fBoundMinY < fDotProduct) pathNodes[x][y].bBlockade = true; } } } } } } // Make sure all externals link TO an internal void CPathInfoForObject::SwapConnectionsToBeRightWayRound(void) { int e, i; CPathInfoForObject *tile = this; for(e = 0; e < 12; e++) if(tile[e].type == NodeTypeExtern && tile[e].next < 0) for(i = 0; i < 12; i++) if(tile[i].type == NodeTypeIntern && tile[i].next == e){ tile[e].next = i; tile[i].next = -1; bool tmp = !!tile[e].crossing; tile[e].crossing = tile[i].crossing; tile[i].crossing = tmp; } } void CPathFind::Init(void) { int i; m_numPathNodes = 0; m_numMapObjects = 0; m_numConnections = 0; m_numCarPathLinks = 0; unk = 0; NumTempExternalNodes = 0; for(i = 0; i < NUM_PATHNODES; i++) m_pathNodes[i].distance = MAX_DIST; } void CPathFind::AllocatePathFindInfoMem(int16 numPathGroups) { delete[] InfoForTileCars; InfoForTileCars = nil; delete[] InfoForTilePeds; InfoForTilePeds = nil; // NB: MIAMI doesn't use numPathGroups here but hardcodes PATHNODESIZE InfoForTileCars = new CPathInfoForObject[12*PATHNODESIZE]; memset(InfoForTileCars, 0, 12*PATHNODESIZE*sizeof(CPathInfoForObject)); InfoForTilePeds = new CPathInfoForObject[12*PATHNODESIZE]; memset(InfoForTilePeds, 0, 12*PATHNODESIZE*sizeof(CPathInfoForObject)); delete[] DetachedInfoForTileCars; DetachedInfoForTileCars = nil; delete[] DetachedInfoForTilePeds; DetachedInfoForTilePeds = nil; DetachedInfoForTileCars = new CPathInfoForObject[12*NUMDETACHED_CARS]; memset(DetachedInfoForTileCars, 0, 12*NUMDETACHED_CARS*sizeof(CPathInfoForObject)); DetachedInfoForTilePeds = new CPathInfoForObject[12*NUMDETACHED_PEDS]; memset(DetachedInfoForTilePeds, 0, 12*NUMDETACHED_PEDS*sizeof(CPathInfoForObject)); delete[] TempExternalNodes; TempExternalNodes = nil; TempExternalNodes = new CTempNodeExternal[NUMTEMPEXTERNALNODES]; memset(TempExternalNodes, 0, NUMTEMPEXTERNALNODES*sizeof(CTempNodeExternal)); NumTempExternalNodes = 0; NumDetachedPedNodeGroups = 0; NumDetachedCarNodeGroups = 0; } void CPathFind::RegisterMapObject(CTreadable *mapObject) { m_mapObjects[m_numMapObjects++] = mapObject; } void CPathFind::StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, bool crossing, uint8 spawnRate) { int i; i = id*12 + node; InfoForTilePeds[i].type = type; InfoForTilePeds[i].next = next; InfoForTilePeds[i].x = x/16.0f; InfoForTilePeds[i].y = y/16.0f; InfoForTilePeds[i].z = z/16.0f; InfoForTilePeds[i].width = 8.0f*Min(width, 15.0f); InfoForTilePeds[i].numLeftLanes = 0; InfoForTilePeds[i].numRightLanes = 0; InfoForTilePeds[i].crossing = crossing; InfoForTilePeds[i].speedLimit = 0; InfoForTilePeds[i].roadBlock = false; InfoForTilePeds[i].disabled = false; InfoForTilePeds[i].waterPath = false; InfoForTilePeds[i].onlySmallBoats = false; InfoForTilePeds[i].betweenLevels = false; InfoForTilePeds[i].spawnRate = Min(spawnRate, 15); if(node == 11) InfoForTilePeds[id*12].SwapConnectionsToBeRightWayRound(); } void CPathFind::StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, int8 numLeft, int8 numRight, bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate) { int i; i = id*12 + node; InfoForTileCars[i].type = type; InfoForTileCars[i].next = next; InfoForTileCars[i].x = x/16.0f; InfoForTileCars[i].y = y/16.0f; InfoForTileCars[i].z = z/16.0f; InfoForTileCars[i].width = 8.0f*Min(width, 15.0f); InfoForTileCars[i].numLeftLanes = numLeft; InfoForTileCars[i].numRightLanes = numRight; InfoForTileCars[i].crossing = false; InfoForTileCars[i].speedLimit = 0; InfoForTileCars[i].roadBlock = false; InfoForTileCars[i].disabled = false; InfoForTileCars[i].waterPath = false; InfoForTileCars[i].onlySmallBoats = false; InfoForTileCars[i].betweenLevels = false; InfoForTileCars[i].spawnRate = Min(spawnRate, 15); if(node == 11) InfoForTileCars[id*12].SwapConnectionsToBeRightWayRound(); } void CPathFind::StoreDetachedNodeInfoPed(int32 node, int8 type, int32 next, float x, float y, float z, float width, bool crossing, bool disabled, bool betweenLevels, uint8 spawnRate) { int i; if(NumDetachedPedNodeGroups >= NUMDETACHED_PEDS) return; i = NumDetachedPedNodeGroups*12 + node; DetachedInfoForTilePeds[i].type = type; DetachedInfoForTilePeds[i].next = next; DetachedInfoForTilePeds[i].x = x/16.0f; DetachedInfoForTilePeds[i].y = y/16.0f; DetachedInfoForTilePeds[i].z = z/16.0f; DetachedInfoForTilePeds[i].width = 8.0f*Min(width, 31.0f); DetachedInfoForTilePeds[i].numLeftLanes = 0; DetachedInfoForTilePeds[i].numRightLanes = 0; DetachedInfoForTilePeds[i].crossing = crossing; DetachedInfoForTilePeds[i].speedLimit = 0; DetachedInfoForTilePeds[i].roadBlock = false; DetachedInfoForTilePeds[i].disabled = disabled; DetachedInfoForTilePeds[i].waterPath = false; DetachedInfoForTilePeds[i].onlySmallBoats = false; DetachedInfoForTilePeds[i].betweenLevels = betweenLevels; DetachedInfoForTilePeds[i].spawnRate = Min(spawnRate, 15); if(node == 11){ DetachedInfoForTilePeds[NumDetachedPedNodeGroups*12].SwapConnectionsToBeRightWayRound(); NumDetachedPedNodeGroups++; } } void CPathFind::StoreDetachedNodeInfoCar(int32 node, int8 type, int32 next, float x, float y, float z, float width, int8 numLeft, int8 numRight, bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate, bool onlySmallBoats) { int i; if(NumDetachedCarNodeGroups >= NUMDETACHED_CARS) return; i = NumDetachedCarNodeGroups*12 + node; DetachedInfoForTileCars[i].type = type; DetachedInfoForTileCars[i].next = next; DetachedInfoForTileCars[i].x = x/16.0f; DetachedInfoForTileCars[i].y = y/16.0f; DetachedInfoForTileCars[i].z = z/16.0f; DetachedInfoForTileCars[i].width = 8.0f*Min(width, 15.0f); DetachedInfoForTileCars[i].numLeftLanes = numLeft; DetachedInfoForTileCars[i].numRightLanes = numRight; DetachedInfoForTileCars[i].crossing = false; DetachedInfoForTileCars[i].speedLimit = speedLimit; DetachedInfoForTileCars[i].roadBlock = roadBlock; DetachedInfoForTileCars[i].disabled = disabled; DetachedInfoForTileCars[i].waterPath = waterPath; DetachedInfoForTileCars[i].onlySmallBoats = onlySmallBoats; DetachedInfoForTileCars[i].betweenLevels = betweenLevels; DetachedInfoForTileCars[i].spawnRate = Min(spawnRate, 15); if(node == 11){ DetachedInfoForTileCars[NumDetachedCarNodeGroups*12].SwapConnectionsToBeRightWayRound(); NumDetachedCarNodeGroups++; } } void CPathFind::CalcNodeCoors(float x, float y, float z, int id, CVector *out) { CVector pos; pos.x = x; pos.y = y; pos.z = z; *out = m_mapObjects[id]->GetMatrix() * pos; } bool CPathFind::LoadPathFindData(void) { CFileMgr::SetDir(""); return false; } void CPathFind::PreparePathData(void) { int i, j; int numExtern, numIntern; CTempNode *tempNodes; printf("PreparePathData\n"); if(!CPathFind::LoadPathFindData() && // empty InfoForTileCars && InfoForTilePeds && DetachedInfoForTileCars && DetachedInfoForTilePeds && TempExternalNodes){ tempNodes = new CTempNode[NUMTEMPNODES]; m_numConnections = 0; for(i = 0; i < PATHNODESIZE; i++){ numExtern = 0; numIntern = 0; for(j = 0; j < 12; j++){ if(InfoForTileCars[i*12 + j].type == NodeTypeExtern) numExtern++; if(InfoForTileCars[i*12 + j].type == NodeTypeIntern) numIntern++; } if(numIntern > 1 && numExtern != 2) printf("ILLEGAL BLOCK. MORE THAN 1 INTERNALS AND NOT 2 EXTERNALS (Modelindex:%d)\n", i); } int numExternDetached, numInternDetached; for(i = 0; i < NUMDETACHED_CARS; i++){ numExternDetached = 0; numInternDetached = 0; for(j = 0; j < 12; j++){ if(DetachedInfoForTileCars[i*12 + j].type == NodeTypeExtern) numExternDetached++; if(DetachedInfoForTilePeds[i*12 + j].type == NodeTypeIntern) numInternDetached++; } // no diagnostic here } for(i = 0; i < PATHNODESIZE; i++) for(j = 0; j < 12; j++) if(InfoForTileCars[i*12 + j].type == NodeTypeExtern){ // MIAMI has MI:%d here but no argument for it if(InfoForTileCars[i*12 + j].numLeftLanes < 0) printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i); if(InfoForTileCars[i*12 + j].numRightLanes < 0) printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i); if(InfoForTileCars[i*12 + j].numLeftLanes + InfoForTileCars[i*12 + j].numRightLanes <= 0) printf("ILLEGAL BLOCK. NO LANES IN NODE (Obj:%d)\n", i); } for(i = 0; i < NUMDETACHED_CARS; i++) for(j = 0; j < 12; j++) if(DetachedInfoForTileCars[i*12 + j].type == NodeTypeExtern){ // MI:%d here but no argument for it if(DetachedInfoForTileCars[i*12 + j].numLeftLanes < 0) printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i); if(DetachedInfoForTileCars[i*12 + j].numRightLanes < 0) printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i); if(DetachedInfoForTileCars[i*12 + j].numLeftLanes + DetachedInfoForTileCars[i*12 + j].numRightLanes <= 0) printf("ILLEGAL BLOCK. NO LANES IN NODE (Obj:%d)\n", i); } m_numPathNodes = 0; PreparePathDataForType(PATH_CAR, tempNodes, InfoForTileCars, 1.0f, DetachedInfoForTileCars, NumDetachedCarNodeGroups); m_numCarPathNodes = m_numPathNodes; PreparePathDataForType(PATH_PED, tempNodes, InfoForTilePeds, 1.0f, DetachedInfoForTilePeds, NumDetachedPedNodeGroups); m_numPedPathNodes = m_numPathNodes - m_numCarPathNodes; delete[] tempNodes; CountFloodFillGroups(PATH_CAR); CountFloodFillGroups(PATH_PED); delete[] InfoForTileCars; InfoForTileCars = nil; delete[] InfoForTilePeds; InfoForTilePeds = nil; delete[] DetachedInfoForTileCars; DetachedInfoForTileCars = nil; delete[] DetachedInfoForTilePeds; DetachedInfoForTilePeds = nil; delete[] TempExternalNodes; TempExternalNodes = nil; } printf("Done with PreparePathData\n"); } /* String together connected nodes in a list by a flood fill algorithm */ void CPathFind::CountFloodFillGroups(uint8 type) { int start, end; int i, l; uint16 n; CPathNode *node, *prev; switch(type){ case PATH_CAR: start = 0; end = m_numCarPathNodes; break; case PATH_PED: start = m_numCarPathNodes; end = start + m_numPedPathNodes; break; } for(i = start; i < end; i++) m_pathNodes[i].group = 0; n = 0; for(;;){ n++; if(n > 1500){ for(i = start; m_pathNodes[i].group && i < end; i++); printf("NumNodes:%d Accounted for:%d\n", end - start, i - start); } // Look for unvisited node for(i = start; m_pathNodes[i].group && i < end; i++); if(i == end) break; node = &m_pathNodes[i]; node->SetNext(nil); node->group = n; if(node->numLinks == 0){ if(type == PATH_CAR) printf("Single car node: %f %f %f\n", node->GetX(), node->GetY(), node->GetZ()); else printf("Single ped node: %f %f %f\n", node->GetX(), node->GetY(), node->GetZ()); } while(node){ prev = node; node = node->GetNext(); for(i = 0; i < prev->numLinks; i++){ l = ConnectedNode(prev->firstLink + i); if(m_pathNodes[l].group == 0){ m_pathNodes[l].group = n; if(m_pathNodes[l].group == 0) m_pathNodes[l].group = INT8_MIN; m_pathNodes[l].SetNext(node); node = &m_pathNodes[l]; } } } } m_numGroups[type] = n-1; printf("GraphType:%d. FloodFill groups:%d\n", type, n); } int32 TempListLength; void CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo, float maxdist, CPathInfoForObject *detachednodes, int numDetached) { static CVector CoorsXFormed; int i, j, k; int l1, l2; int start; float posx, posy; float dx, dy, mag; float nearestDist; int nearestId; int oldNumPathNodes, oldNumLinks; float dist; int iseg, jseg; int done, cont; int tileStart; oldNumPathNodes = m_numPathNodes; oldNumLinks = m_numConnections; #define OBJECTINDEX(n) (mapObjIndices[(n)]) int16 *mapObjIndices = new int16[NUM_PATHNODES]; NumTempExternalNodes = 0; // Calculate internal nodes, store them and connect them to defining object for(i = 0; i < m_numMapObjects; i++){ tileStart = m_numPathNodes; start = 12 * m_mapObjects[i]->GetModelIndex(); for(j = 0; j < 12; j++){ if(objectpathinfo[start + j].type == NodeTypeIntern){ CalcNodeCoors( objectpathinfo[start + j].x, objectpathinfo[start + j].y, objectpathinfo[start + j].z, i, &CoorsXFormed); m_pathNodes[m_numPathNodes].SetPosition(CoorsXFormed); OBJECTINDEX(m_numPathNodes) = i; m_pathNodes[m_numPathNodes].width = objectpathinfo[start + j].width; m_pathNodes[m_numPathNodes].speedLimit = objectpathinfo[start + j].speedLimit; m_pathNodes[m_numPathNodes].spawnRate = objectpathinfo[start + j].spawnRate; m_pathNodes[m_numPathNodes].bUseInRoadBlock = objectpathinfo[start + j].roadBlock; m_pathNodes[m_numPathNodes].bDisabled = objectpathinfo[start + j].disabled; m_pathNodes[m_numPathNodes].bWaterPath = objectpathinfo[start + j].waterPath; m_pathNodes[m_numPathNodes].bOnlySmallBoats = objectpathinfo[start + j].onlySmallBoats; m_pathNodes[m_numPathNodes].bBetweenLevels = objectpathinfo[start + j].betweenLevels; m_numPathNodes++; } else if(objectpathinfo[start + j].type == NodeTypeExtern){ CalcNodeCoors( objectpathinfo[start + j].x, objectpathinfo[start + j].y, objectpathinfo[start + j].z, i, &CoorsXFormed); TempExternalNodes[NumTempExternalNodes].pos = CoorsXFormed; assert(objectpathinfo[start + j].next >= 0); TempExternalNodes[NumTempExternalNodes].next = tileStart + objectpathinfo[start + j].next; TempExternalNodes[NumTempExternalNodes].numLeftLanes = objectpathinfo[start + j].numLeftLanes; TempExternalNodes[NumTempExternalNodes].numRightLanes = objectpathinfo[start + j].numRightLanes; TempExternalNodes[NumTempExternalNodes].width = objectpathinfo[start + j].width; TempExternalNodes[NumTempExternalNodes].isCross = !!objectpathinfo[start + j].crossing; NumTempExternalNodes++; } } } // Same thing for detached nodes for(i = 0; i < numDetached; i++){ tileStart = m_numPathNodes; start = 12*i; for(j = 0; j < 12; j++){ if(detachednodes[start + j].type == NodeTypeIntern){ CVector pos; pos.x = detachednodes[start + j].x; pos.y = detachednodes[start + j].y; pos.z = detachednodes[start + j].z; m_pathNodes[m_numPathNodes].SetPosition(pos); mapObjIndices[m_numPathNodes] = -(i+1); m_pathNodes[m_numPathNodes].width = detachednodes[start + j].width; m_pathNodes[m_numPathNodes].speedLimit = detachednodes[start + j].speedLimit; m_pathNodes[m_numPathNodes].spawnRate = detachednodes[start + j].spawnRate; m_pathNodes[m_numPathNodes].bUseInRoadBlock = detachednodes[start + j].roadBlock; m_pathNodes[m_numPathNodes].bDisabled = detachednodes[start + j].disabled; m_pathNodes[m_numPathNodes].bWaterPath = detachednodes[start + j].waterPath; m_pathNodes[m_numPathNodes].bOnlySmallBoats = detachednodes[start + j].onlySmallBoats; m_pathNodes[m_numPathNodes].bBetweenLevels = detachednodes[start + j].betweenLevels; m_numPathNodes++; }else if(detachednodes[start + j].type == NodeTypeExtern){ TempExternalNodes[NumTempExternalNodes].pos.x = detachednodes[start + j].x; TempExternalNodes[NumTempExternalNodes].pos.y = detachednodes[start + j].y; TempExternalNodes[NumTempExternalNodes].pos.z = detachednodes[start + j].z; assert(detachednodes[start + j].next >= 0); TempExternalNodes[NumTempExternalNodes].next = tileStart + detachednodes[start + j].next; TempExternalNodes[NumTempExternalNodes].numLeftLanes = detachednodes[start + j].numLeftLanes; TempExternalNodes[NumTempExternalNodes].numRightLanes = detachednodes[start + j].numRightLanes; TempExternalNodes[NumTempExternalNodes].width = detachednodes[start + j].width; TempExternalNodes[NumTempExternalNodes].isCross = !!detachednodes[start + j].crossing; NumTempExternalNodes++; } } } // Insert external nodes into TempList TempListLength = 0; for(i = 0; i < NumTempExternalNodes; i++){ // find closest unconnected node nearestId = -1; nearestDist = maxdist; for(k = 0; k < TempListLength; k++){ if(tempnodes[k].linkState != 1) continue; dx = tempnodes[k].pos.x - TempExternalNodes[i].pos.x; if(Abs(dx) < nearestDist){ dy = tempnodes[k].pos.y - TempExternalNodes[i].pos.y; if(Abs(dy) < nearestDist){ nearestDist = Max(Abs(dx), Abs(dy)); nearestId = k; } } } if(nearestId < 0){ // None found, add this one to temp list tempnodes[TempListLength].pos = TempExternalNodes[i].pos; // link to connecting internal node tempnodes[TempListLength].link1 = TempExternalNodes[i].next; if(type == PATH_CAR){ tempnodes[TempListLength].numLeftLanes = TempExternalNodes[i].numLeftLanes; tempnodes[TempListLength].numRightLanes = TempExternalNodes[i].numRightLanes; } tempnodes[TempListLength].width = TempExternalNodes[i].width; tempnodes[TempListLength].isCross = TempExternalNodes[i].isCross; tempnodes[TempListLength++].linkState = 1; }else{ // Found nearest, connect it to our neighbour tempnodes[nearestId].link2 = TempExternalNodes[i].next; tempnodes[nearestId].linkState = 2; // collapse this node with nearest we found dx = m_pathNodes[tempnodes[nearestId].link1].GetX() - m_pathNodes[tempnodes[nearestId].link2].GetX(); dy = m_pathNodes[tempnodes[nearestId].link1].GetY() - m_pathNodes[tempnodes[nearestId].link2].GetY(); tempnodes[nearestId].pos = (tempnodes[nearestId].pos + TempExternalNodes[i].pos)*0.5f; mag = Sqrt(dx*dx + dy*dy); tempnodes[nearestId].dirX = dx/mag * 100; tempnodes[nearestId].dirY = dy/mag * 100; tempnodes[nearestId].width = Max(tempnodes[nearestId].width, TempExternalNodes[i].width); if(TempExternalNodes[i].isCross) tempnodes[nearestId].isCross = true; // TODO: is this guaranteed to be false otherwise? // do something when number of lanes doesn't agree if(type == PATH_CAR) if(tempnodes[nearestId].numLeftLanes != 0 && tempnodes[nearestId].numRightLanes != 0 && (TempExternalNodes[i].numLeftLanes == 0 || TempExternalNodes[i].numRightLanes == 0)){ // why switch left and right here? tempnodes[nearestId].numLeftLanes = TempExternalNodes[i].numRightLanes; tempnodes[nearestId].numRightLanes = TempExternalNodes[i].numLeftLanes; } } } // Loop through previously added internal nodes and link them for(i = oldNumPathNodes; i < m_numPathNodes; i++){ // Init link m_pathNodes[i].numLinks = 0; m_pathNodes[i].firstLink = m_numConnections; // See if node connects to external nodes for(j = 0; j < TempListLength; j++){ if(tempnodes[j].linkState != 2) continue; // Add link to other side of the external // NB this clears the flags in MIAMI if(tempnodes[j].link1 == i) m_connections[m_numConnections] = tempnodes[j].link2; else if(tempnodes[j].link2 == i) m_connections[m_numConnections] = tempnodes[j].link1; else continue; dist = (m_pathNodes[i].GetPosition() - m_pathNodes[ConnectedNode(m_numConnections)].GetPosition()).Magnitude(); m_distances[m_numConnections] = Min(dist, 255); if(tempnodes[j].isCross) m_connections[j] |= 0x8000; // crosses road flag if(type == PATH_CAR){ // IMPROVE: use a goto here // Find existing car path link for(k = 0; k < m_numCarPathLinks; k++){ if(m_carPathLinks[k].dirX == tempnodes[j].dirX && m_carPathLinks[k].dirY == tempnodes[j].dirY && m_carPathLinks[k].x == (int)(tempnodes[j].pos.x*8.0f) && m_carPathLinks[k].y == (int)(tempnodes[j].pos.y*8.0f)){ m_carPathConnections[m_numConnections] = k; k = m_numCarPathLinks; } } // k is m_numCarPathLinks+1 if we found one if(k == m_numCarPathLinks){ m_carPathLinks[m_numCarPathLinks].dirX = tempnodes[j].dirX; m_carPathLinks[m_numCarPathLinks].dirY = tempnodes[j].dirY; m_carPathLinks[m_numCarPathLinks].x = tempnodes[j].pos.x*8.0f; m_carPathLinks[m_numCarPathLinks].y = tempnodes[j].pos.y*8.0f; m_carPathLinks[m_numCarPathLinks].trafficLightDirection = false; m_carPathLinks[m_numCarPathLinks].width = tempnodes[j].width; m_carPathLinks[m_numCarPathLinks].pathNodeIndex = i; m_carPathLinks[m_numCarPathLinks].numLeftLanes = tempnodes[j].numLeftLanes; m_carPathLinks[m_numCarPathLinks].numRightLanes = tempnodes[j].numRightLanes; m_carPathLinks[m_numCarPathLinks].trafficLightType = 0; assert(m_numCarPathLinks <= NUM_CARPATHLINKS); m_carPathConnections[m_numConnections] = m_numCarPathLinks++; } } m_pathNodes[i].numLinks++; m_numConnections++; } CPathInfoForObject *tile; if(mapObjIndices[i] < 0){ if(type == PATH_CAR) tile = &DetachedInfoForTileCars[12 * (-1 - mapObjIndices[i])]; else tile = &DetachedInfoForTilePeds[12 * (-1 - mapObjIndices[i])]; }else{ if(type == PATH_CAR) tile = &InfoForTileCars[12 * m_mapObjects[mapObjIndices[i]]->GetModelIndex()]; else tile = &InfoForTilePeds[12 * m_mapObjects[mapObjIndices[i]]->GetModelIndex()]; } // Find i inside path segment iseg = 0; for(j = Max(oldNumPathNodes, i-12); j < i; j++) if(OBJECTINDEX(j) == OBJECTINDEX(i)) iseg++; // Add links to other internal nodes for(j = Max(oldNumPathNodes, i-12); j < Min(m_numPathNodes, i+12); j++){ if(OBJECTINDEX(i) != OBJECTINDEX(j) || i == j) continue; // N.B.: in every path segment, the externals have to be at the end jseg = j-i + iseg; if(tile[iseg].next == jseg || tile[jseg].next == iseg){ // Found a link between i and jConnectionSetCrossesRoad // NB this clears the flags in MIAMI m_connections[m_numConnections] = j; dist = (m_pathNodes[i].GetPosition() - m_pathNodes[j].GetPosition()).Magnitude(); m_distances[m_numConnections] = Min(dist, 255); if(type == PATH_CAR){ posx = (m_pathNodes[i].GetX() + m_pathNodes[j].GetX())*0.5f; posy = (m_pathNodes[i].GetY() + m_pathNodes[j].GetY())*0.5f; dx = m_pathNodes[j].GetX() - m_pathNodes[i].GetX(); dy = m_pathNodes[j].GetY() - m_pathNodes[i].GetY(); mag = Sqrt(dx*dx + dy*dy); dx /= mag; dy /= mag; uint8 width = Max(m_pathNodes[i].width, m_pathNodes[j].width); if(i < j){ dx = -dx; dy = -dy; } // IMPROVE: use a goto here // Find existing car path link for(k = 0; k < m_numCarPathLinks; k++){ if(m_carPathLinks[k].dirX == (int)(dx*100.0f) && m_carPathLinks[k].dirY == (int)(dy*100.0f) && m_carPathLinks[k].x == (int)(posx*8.0f) && m_carPathLinks[k].y == (int)(posy*8.0f)){ m_carPathConnections[m_numConnections] = k; k = m_numCarPathLinks; } } // k is m_numCarPathLinks+1 if we found one if(k == m_numCarPathLinks){ m_carPathLinks[m_numCarPathLinks].dirX = dx*100.0f; m_carPathLinks[m_numCarPathLinks].dirY = dy*100.0f; m_carPathLinks[m_numCarPathLinks].x = posx*8.0f; m_carPathLinks[m_numCarPathLinks].y = posy*8.0f; m_carPathLinks[m_numCarPathLinks].trafficLightDirection = false; m_carPathLinks[m_numCarPathLinks].width = width; m_carPathLinks[m_numCarPathLinks].pathNodeIndex = i; m_carPathLinks[m_numCarPathLinks].numLeftLanes = -1; m_carPathLinks[m_numCarPathLinks].numRightLanes = -1; m_carPathLinks[m_numCarPathLinks].trafficLightType = 0; assert(m_numCarPathLinks <= NUM_CARPATHLINKS); m_carPathConnections[m_numConnections] = m_numCarPathLinks++; } }else{ // Crosses road if(tile[iseg].next == jseg && tile[iseg].crossing || tile[jseg].next == iseg && tile[jseg].crossing) m_connections[m_numConnections] |= 0x8000; // crosses road flag } m_pathNodes[i].numLinks++; m_numConnections++; } } } if(type == PATH_CAR){ done = 0; // Set number of lanes for all nodes somehow // very strange code for(k = 0; !done && k < 12; k++){ done = 1; for(i = 0; i < m_numPathNodes; i++){ if(m_pathNodes[i].numLinks != 2) continue; l1 = m_carPathConnections[m_pathNodes[i].firstLink]; l2 = m_carPathConnections[m_pathNodes[i].firstLink+1]; int8 l1Left = m_carPathLinks[l1].numLeftLanes; int8 l1Right = m_carPathLinks[l1].numRightLanes; int8 l2Left = m_carPathLinks[l2].numLeftLanes; int8 l2Right = m_carPathLinks[l2].numRightLanes; int8 *l1Leftp, *l1Rightp; int8 *l2Leftp, *l2Rightp; if(m_carPathLinks[l1].pathNodeIndex == i){ l1Leftp = &l1Left; l1Rightp = &l1Right; }else{ l1Leftp = &l1Right; l1Rightp = &l1Left; } if(m_carPathLinks[l2].pathNodeIndex == i){ l2Leftp = &l2Left; l2Rightp = &l2Right; }else{ l2Leftp = &l2Right; l2Rightp = &l2Left; } if(*l1Leftp == -1 && *l2Rightp != -1){ *l1Leftp = *l2Rightp; done = 0; } if(*l1Rightp == -1 && *l2Leftp != -1){ *l1Rightp = *l2Leftp; done = 0; } if(*l2Leftp == -1 && *l1Rightp != -1){ *l2Leftp = *l1Rightp; done = 0; } if(*l2Rightp == -1 && *l1Leftp != -1){ *l2Rightp = *l1Leftp; done = 0; } if(*l1Leftp == -1 && *l2Rightp == -1) done = 0; if(*l2Leftp == -1 && *l1Rightp == -1) done = 0; m_carPathLinks[l1].numLeftLanes = l1Left; m_carPathLinks[l1].numRightLanes = l1Right; m_carPathLinks[l2].numLeftLanes = l2Left; m_carPathLinks[l2].numRightLanes = l2Right; } } // Fall back to default values for number of lanes for(i = 0; i < m_numPathNodes; i++) for(j = 0; j < m_pathNodes[i].numLinks; j++){ k = m_carPathConnections[m_pathNodes[i].firstLink + j]; if(m_carPathLinks[k].numLeftLanes == -1) m_carPathLinks[k].numLeftLanes = 0; if(m_carPathLinks[k].numRightLanes == -1) m_carPathLinks[k].numRightLanes = 0; } } // Set flags for car nodes if(type == PATH_CAR){ do{ cont = 0; for(i = 0; i < m_numPathNodes; i++){ // See if node is a dead end, if so, we're not done yet if(!m_pathNodes[i].bDeadEnd){ k = 0; for(j = 0; j < m_pathNodes[i].numLinks; j++) if(!m_pathNodes[ConnectedNode(m_pathNodes[i].firstLink + j)].bDeadEnd) k++; if(k < 2){ m_pathNodes[i].bDeadEnd = true; cont = 1; } } } }while(cont); } // Remove isolated ped nodes if(type == PATH_PED) for(i = oldNumPathNodes; i < m_numPathNodes; i++){ if(m_pathNodes[i].numLinks != 0) continue; // Remove node for(j = i; j < m_numPathNodes-1; j++) m_pathNodes[j] = m_pathNodes[j+1]; // Fix links for(j = oldNumLinks; j < m_numConnections; j++){ int node = ConnectedNode(j); if(node >= i) m_connections[j] = node-1; } i--; m_numPathNodes--; } delete[] mapObjIndices; } float CPathFind::CalcRoadDensity(float x, float y) { int i, j; float density = 0.0f; for(i = 0; i < m_numCarPathNodes; i++){ if(Abs(m_pathNodes[i].GetX() - x) < 80.0f && Abs(m_pathNodes[i].GetY() - y) < 80.0f && m_pathNodes[i].numLinks > 0){ for(j = 0; j < m_pathNodes[i].numLinks; j++){ int next = ConnectedNode(m_pathNodes[i].firstLink + j); float dist = (m_pathNodes[i].GetPosition() - m_pathNodes[next].GetPosition()).Magnitude2D(); next = m_carPathConnections[m_pathNodes[i].firstLink + j]; density += m_carPathLinks[next].numLeftLanes * dist; density += m_carPathLinks[next].numRightLanes * dist; } } } return density/2500.0f; } bool CPathFind::TestForPedTrafficLight(CPathNode *n1, CPathNode *n2) { int i; for(i = 0; i < n1->numLinks; i++) if(&m_pathNodes[ConnectedNode(n1->firstLink + i)] == n2) return ConnectionHasTrafficLight(n1->firstLink + i); return false; } bool CPathFind::TestCrossesRoad(CPathNode *n1, CPathNode *n2) { int i; for(i = 0; i < n1->numLinks; i++) if(&m_pathNodes[ConnectedNode(n1->firstLink + i)] == n2) return ConnectionCrossesRoad(n1->firstLink + i); return false; } void CPathFind::AddNodeToList(CPathNode *node, int32 listId) { int i = listId & 0x1FF; node->SetNext(m_searchNodes[i].GetNext()); node->SetPrev(&m_searchNodes[i]); if(m_searchNodes[i].GetNext()) m_searchNodes[i].GetNext()->SetPrev(node); m_searchNodes[i].SetNext(node); node->distance = listId; } void CPathFind::RemoveNodeFromList(CPathNode *node) { node->GetPrev()->SetNext(node->GetNext()); if(node->GetNext()) node->GetNext()->SetPrev(node->GetPrev()); } void CPathFind::RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n) { int i; if(*n < 2) return; if(DotProduct2D(nodes[1]->GetPosition() - pos, nodes[0]->GetPosition() - pos) < 0.0f){ (*n)--; for(i = 0; i < *n; i++) nodes[i] = nodes[i+1]; } } #ifdef GTA_BRIDGE void CPathFind::SetLinksBridgeLights(float x1, float x2, float y1, float y2, bool enable) { int i; for(i = 0; i < m_numCarPathLinks; i++){ CVector2D pos = m_carPathLinks[i].GetPosition(); if(x1 < pos.x && pos.x < x2 && y1 < pos.y && pos.y < y2) m_carPathLinks[i].bBridgeLights = enable; } } #endif void CPathFind::SwitchOffNodeAndNeighbours(int32 nodeId, bool disable) { int i, next; m_pathNodes[nodeId].bDisabled = disable; if(m_pathNodes[nodeId].numLinks < 3) for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){ next = ConnectedNode(m_pathNodes[nodeId].firstLink + i); if(m_pathNodes[next].bDisabled != disable && m_pathNodes[next].numLinks < 3) SwitchOffNodeAndNeighbours(next, disable); } } void CPathFind::SwitchRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable) { int i; for(i = 0; i < m_numCarPathNodes; i++){ CVector pos = m_pathNodes[i].GetPosition(); if(x1 <= pos.x && pos.x <= x2 && y1 <= pos.y && pos.y <= y2 && z1 <= pos.z && pos.z <= z2 && disable != m_pathNodes[i].bDisabled) SwitchOffNodeAndNeighbours(i, disable); } } void CPathFind::SwitchPedRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable) { int i; for(i = m_numCarPathNodes; i < m_numPathNodes; i++){ CVector pos = m_pathNodes[i].GetPosition(); if(x1 <= pos.x && pos.x <= x2 && y1 <= pos.y && pos.y <= y2 && z1 <= pos.z && pos.z <= z2 && disable != m_pathNodes[i].bDisabled) SwitchOffNodeAndNeighbours(i, disable); } } void CPathFind::SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 mode) { int i; int firstNode, lastNode; // this is NOT PATH_CAR if(type != 0){ firstNode = 0; lastNode = m_numCarPathNodes; }else{ firstNode = m_numCarPathNodes; lastNode = m_numPathNodes; } if(z1 > z2){ float tmp = z2; z2 = z1; z1 = tmp; } // angle of vector from p2 to p1 float angle = CGeneral::GetRadianAngleBetweenPoints(x1, y1, x2, y2) + HALFPI; while(angle < 0.0f) angle += TWOPI; while(angle > TWOPI) angle -= TWOPI; // vector from p1 to p2 CVector2D v12(x2 - x1, y2 - y1); float len12 = v12.Magnitude(); v12 /= len12; // vector from p2 to new point p3 CVector2D v23(Sin(angle)*length, -(Cos(angle)*length)); v23 /= v23.Magnitude(); // obivously just 'length' but whatever bool disable = mode == SWITCH_OFF; for(i = firstNode; i < lastNode; i++){ CVector pos = m_pathNodes[i].GetPosition(); if(pos.z < z1 || pos.z > z2) continue; CVector2D d(pos.x - x1, pos.y - y1); float dot = DotProduct2D(d, v12); if(dot < 0.0f || dot > len12) continue; dot = DotProduct2D(d, v23); if(dot < 0.0f || dot > length) continue; if(m_pathNodes[i].bDisabled != disable) SwitchOffNodeAndNeighbours(i, disable); } } void CPathFind::MarkRoadsBetweenLevelsNodeAndNeighbours(int32 nodeId) { int i, next; m_pathNodes[nodeId].bBetweenLevels = true; if(m_pathNodes[nodeId].numLinks < 3) for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){ next = ConnectedNode(m_pathNodes[nodeId].firstLink + i); if(!m_pathNodes[next].bBetweenLevels && m_pathNodes[next].numLinks < 3) MarkRoadsBetweenLevelsNodeAndNeighbours(next); } } void CPathFind::MarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2) { int i; for(i = 0; i < m_numPathNodes; i++){ CVector pos = m_pathNodes[i].GetPosition(); if(x1 < pos.x && pos.x < x2 && y1 < pos.y && pos.y < y2 && z1 < pos.z && pos.z < z2) MarkRoadsBetweenLevelsNodeAndNeighbours(i); } } void CPathFind::PedMarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2) { int i; for(i = m_numCarPathNodes; i < m_numPathNodes; i++){ CVector pos = m_pathNodes[i].GetPosition(); if(x1 < pos.x && pos.x < x2 && y1 < pos.y && pos.y < y2 && z1 < pos.z && pos.z < z2) MarkRoadsBetweenLevelsNodeAndNeighbours(i); } } int32 CPathFind::FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels, bool ignoreSelected, bool bWaterPath) { int i; int firstNode, lastNode; float dist; float closestDist = 10000.0f; int closestNode = 0; switch(type){ case PATH_CAR: firstNode = 0; lastNode = m_numCarPathNodes; break; case PATH_PED: firstNode = m_numCarPathNodes; lastNode = m_numPathNodes; break; } for(i = firstNode; i < lastNode; i++){ if(ignoreDisabled && m_pathNodes[i].bDisabled) continue; if(ignoreBetweenLevels && m_pathNodes[i].bBetweenLevels) continue; if(ignoreSelected && m_pathNodes[i].bSelected) continue; if(bWaterPath != m_pathNodes[i].bWaterPath) continue; dist = Abs(m_pathNodes[i].GetX() - coors.x) + Abs(m_pathNodes[i].GetY() - coors.y) + 3.0f*Abs(m_pathNodes[i].GetZ() - coors.z); if(dist < closestDist){ closestDist = dist; closestNode = i; } } return closestDist < distLimit ? closestNode : -1; } int32 CPathFind::FindNodeClosestToCoorsFavourDirection(CVector coors, uint8 type, float dirX, float dirY) { int i; int firstNode, lastNode; float dist, dX, dY; NormalizeXY(dirX, dirY); float closestDist = 10000.0f; int closestNode = 0; switch(type){ case PATH_CAR: firstNode = 0; lastNode = m_numCarPathNodes; break; case PATH_PED: firstNode = m_numCarPathNodes; lastNode = m_numPathNodes; break; } for(i = firstNode; i < lastNode; i++){ dX = m_pathNodes[i].GetX() - coors.x; dY = m_pathNodes[i].GetY() - coors.y; dist = Abs(dX) + Abs(dY) + 3.0f*Abs(m_pathNodes[i].GetZ() - coors.z); if(dist < closestDist){ NormalizeXY(dX, dY); dist -= (dX*dirX + dY*dirY - 1.0f)*20.0f; if(dist < closestDist){ closestDist = dist; closestNode = i; } } } return closestNode; } void CPathFind::FindNodePairClosestToCoors(CVector coors, uint8 type, int* node1, int* node2, float* angle, float minDist, float maxDist, bool ignoreDisabled, bool ignoreBetweenLevels, bool bWaterPath) { int i, j; int firstNode, lastNode, connectedNode; float dist; float closestDist = 10000.0f; int closestNode = 0, closestConnectedNode = 0; switch (type) { case PATH_CAR: firstNode = 0; lastNode = m_numCarPathNodes; break; case PATH_PED: firstNode = m_numCarPathNodes; lastNode = m_numPathNodes; break; } for (i = firstNode; i < lastNode; i++) { if (ignoreDisabled && m_pathNodes[i].bDisabled) continue; if (ignoreBetweenLevels && m_pathNodes[i].bBetweenLevels) continue; if (bWaterPath != m_pathNodes[i].bWaterPath) continue; dist = Abs(m_pathNodes[i].GetX() - coors.x) + Abs(m_pathNodes[i].GetY() - coors.y) + 3.0f * Abs(m_pathNodes[i].GetZ() - coors.z); if (dist < closestDist) { for (j = 0; j < m_pathNodes[i].numLinks; j++) { connectedNode = ConnectedNode(m_pathNodes[i].firstLink + j); if (ignoreDisabled && m_pathNodes[connectedNode].bDisabled) continue; if (ignoreBetweenLevels && m_pathNodes[connectedNode].bBetweenLevels) continue; if (bWaterPath != m_pathNodes[connectedNode].bWaterPath) continue; if ((m_pathNodes[connectedNode].GetPosition() - m_pathNodes[i].GetPosition()).Magnitude() > minDist) { closestDist = dist; closestNode = i; closestConnectedNode = connectedNode; } } } } if (closestDist < maxDist) { *node1 = closestNode; *node2 = closestConnectedNode; CVector dir(m_pathNodes[*node2].GetX() - m_pathNodes[*node1].GetX(), m_pathNodes[*node2].GetY() - m_pathNodes[*node1].GetY(), 0.0f); dir.Normalise(); *angle = RADTODEG(Atan2(-dir.x, dir.y)); } else { *node1 = -1; *node2 = -1; *angle = 0.0f; } } int32 CPathFind::FindNthNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels, int N, bool bWaterPath) { int i; int firstNode, lastNode; switch (type) { case PATH_CAR: firstNode = 0; lastNode = m_numCarPathNodes; break; case PATH_PED: firstNode = m_numCarPathNodes; lastNode = m_numPathNodes; break; } for (i = firstNode; i < lastNode; i++) m_pathNodes[i].bSelected = false; for (; N > 0; N--) { i = FindNodeClosestToCoors(coors, type, distLimit, ignoreDisabled, ignoreBetweenLevels, true, bWaterPath); if (i < 0) return -1; m_pathNodes[i].bSelected = true; } return FindNodeClosestToCoors(coors, type, distLimit, ignoreDisabled, ignoreBetweenLevels, true, bWaterPath); } CVector CPathFind::FindNodeCoorsForScript(int32 id) { // the point is to return valid position in case there is a divider in the middle of the road if (!m_pathNodes[id].HasDivider() || m_pathNodes[id].numLinks == 0) return m_pathNodes[id].GetPosition(); CVector2D dir(m_pathNodes[ConnectedNode(m_pathNodes[id].firstLink)].GetX() - m_pathNodes[id].GetX(), m_pathNodes[ConnectedNode(m_pathNodes[id].firstLink)].GetY() - m_pathNodes[id].GetY()); dir.Normalise(); if (dir.x < 0) dir = -dir; return m_pathNodes[id].GetPosition() + CVector(-dir.y, dir.x, 0.0f) * (LANE_WIDTH / 2 + m_pathNodes[id].GetDividerWidth()); } float CPathFind::FindNodeOrientationForCarPlacement(int32 nodeId) { if(m_pathNodes[nodeId].numLinks == 0) return 0.0f; CVector dir = m_pathNodes[ConnectedNode(m_pathNodes[nodeId].firstLink)].GetPosition() - m_pathNodes[nodeId].GetPosition(); dir.z = 0.0f; dir.Normalise(); return RADTODEG(dir.Heading()); } float CPathFind::FindNodeOrientationForCarPlacementFacingDestination(int32 nodeId, float x, float y, bool towards) { int i; CVector targetDir(x - m_pathNodes[nodeId].GetX(), y - m_pathNodes[nodeId].GetY(), 0.0f); targetDir.Normalise(); CVector dir; if(m_pathNodes[nodeId].numLinks == 0) return 0.0f; int bestNode = ConnectedNode(m_pathNodes[nodeId].firstLink); #ifdef FIX_BUGS float bestDot = towards ? -2.0f : 2.0f; #else int bestDot = towards ? -2 : 2; // why int? #endif for(i = 0; i < m_pathNodes[nodeId].numLinks; i++){ dir = m_pathNodes[ConnectedNode(m_pathNodes[nodeId].firstLink + i)].GetPosition() - m_pathNodes[nodeId].GetPosition(); dir.z = 0.0f; dir.Normalise(); float angle = DotProduct2D(dir, targetDir); if(towards){ if(angle > bestDot){ bestDot = angle; bestNode = ConnectedNode(m_pathNodes[nodeId].firstLink + i); } }else{ if(angle < bestDot){ bestDot = angle; bestNode = ConnectedNode(m_pathNodes[nodeId].firstLink + i); } } } dir = m_pathNodes[bestNode].GetPosition() - m_pathNodes[nodeId].GetPosition(); dir.z = 0.0f; dir.Normalise(); return RADTODEG(dir.Heading()); } bool CPathFind::GenerateCarCreationCoors(float x, float y, float dirX, float dirY, float spawnDist, float angleLimit, bool forward, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, bool ignoreDisabled) { int i, j; int node1, node2; float dist1, dist2, d1, d2; if(m_numCarPathNodes == 0) return false; for(i = 0; i < 500; i++){ node1 = (CGeneral::GetRandomNumber()>>3) % m_numCarPathNodes; if(m_pathNodes[node1].bDisabled && !ignoreDisabled) continue; dist1 = Distance2D(m_pathNodes[node1].GetPosition(), x, y); if(dist1 < Max(spawnDist + 70.0f, spawnDist * 1.7f)){ d1 = m_pathNodes[node1].bWaterPath ? (dist1 - spawnDist * 1.5f) : (dist1 - spawnDist); for(j = 0; j < m_pathNodes[node1].numLinks; j++){ node2 = ConnectedNode(m_pathNodes[node1].firstLink + j); if(m_pathNodes[node2].bDisabled && !ignoreDisabled) continue; dist2 = Distance2D(m_pathNodes[node2].GetPosition(), x, y); d2 = m_pathNodes[node2].bWaterPath ? (dist2 - spawnDist * 1.5f) : (dist2 - spawnDist); if(d1*d2 < 0.0f){ // nodes are on different sides of spawn distance float f2 = Abs(d1)/(Abs(d1) + Abs(d2)); float f1 = 1.0f - f2; *pPositionBetweenNodes = f2; CVector pos = m_pathNodes[node1].GetPosition()*f1 + m_pathNodes[node2].GetPosition()*f2; CVector2D dist2d(pos.x - x, pos.y - y); dist2d.Normalise(); // done manually in the game float dot = DotProduct2D(dist2d, CVector2D(dirX, dirY)); if(forward){ if(dot > angleLimit){ *pNode1 = node1; *pNode2 = node2; *pPosition = pos; return true; } }else{ if(dot <= angleLimit){ *pNode1 = node1; *pNode2 = node2; *pPosition = pos; return true; } } } } } } return false; } bool CPathFind::GeneratePedCreationCoors(float x, float y, float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, CMatrix *camMatrix) { int i; int node1, node2; float node1_dist, node2_dist; static int32 node_cnt; if(m_numPedPathNodes == 0) return false; for(i = 0; i < 230; i++){ if (node_cnt++ >= m_numPedPathNodes) node_cnt = 0; node1 = node_cnt + m_numCarPathNodes; node1_dist = Distance2D(m_pathNodes[node1].GetPosition(), x, y); if(node1_dist < maxDist+30.0f){ if(m_pathNodes[node1].numLinks != 0) break; } } if (i >= 230) return false; for(i = 0; i < m_pathNodes[node1].numLinks; i++){ int link = m_pathNodes[node1].firstLink + i; if(ConnectionCrossesRoad(link)) continue; node2 = ConnectedNode(link); if(m_pathNodes[node1].bDisabled || m_pathNodes[node2].bDisabled) continue; node2_dist = Distance2D(m_pathNodes[node2].GetPosition(), x, y); if ((node1_dist < maxDist || node2_dist < maxDist) && (node1_dist > minDistOffScreen || node2_dist > minDistOffScreen)) break; } if(i >= m_pathNodes[node1].numLinks) return false; for(i = 0; i < 5; i++){ float f2 = (CGeneral::GetRandomNumber()&0xFF)/256.0f; float f1 = 1.0f - f2; *pPositionBetweenNodes = f2; CVector pos = m_pathNodes[node1].GetPosition()*f1 + m_pathNodes[node2].GetPosition()*f2; if(Distance2D(pos, x, y) < maxDist+20.0f){ pos.x += ((CGeneral::GetRandomNumber()&0xFF)-128)*0.01f; pos.y += ((CGeneral::GetRandomNumber()&0xFF)-128)*0.01f; float dist = Distance2D(pos, x, y); bool visible; if(camMatrix) visible = TheCamera.IsSphereVisible(pos, 2.0f, camMatrix); else visible = TheCamera.IsSphereVisible(pos, 2.0f); if(!visible){ minDist = minDistOffScreen; maxDist = maxDistOffScreen; } if(visible && (minDist < dist && dist < maxDist) || !visible && (minDistOffScreen < dist && dist < maxDistOffScreen)){ *pNode1 = node1; *pNode2 = node2; *pPosition = pos; bool found; float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z+2.0f, &found); if(!found) return false; if(Abs(groundZ - pos.z) > 3.0f) return false; pPosition->z = groundZ; return true; } } } return false; } void CPathFind::FindNextNodeWandering(uint8 type, CVector coors, CPathNode **lastNode, CPathNode **nextNode, uint8 curDir, uint8 *nextDir) { int i; CPathNode *node; if(lastNode == nil || (node = *lastNode) == nil || (coors - (*lastNode)->GetPosition()).MagnitudeSqr() > 7.0f){ int32 nodeIdx = FindNodeClosestToCoors(coors, type, 999999.88f); node = &m_pathNodes[nodeIdx]; } CVector2D vCurDir(Sin(curDir*PI/4.0f), Cos(curDir * PI / 4.0f)); *nextNode = 0; float bestDot = -999999.0f; for(i = 0; i < node->numLinks; i++){ int next = ConnectedNode(node->firstLink+i); if(!node->bDisabled && m_pathNodes[next].bDisabled) continue; CVector pedCoors = coors; pedCoors.z += 1.0f; CVector nodeCoors = m_pathNodes[next].GetPosition(); nodeCoors.z += 1.0f; if(!CWorld::GetIsLineOfSightClear(pedCoors, nodeCoors, true, false, false, false, false, false)) continue; CVector2D nodeDir = m_pathNodes[next].GetPosition() - node->GetPosition(); nodeDir.Normalise(); float dot = DotProduct2D(nodeDir, vCurDir); if(dot >= bestDot){ *nextNode = &m_pathNodes[next]; bestDot = dot; // direction is 0, 2, 4, 6 for north, east, south, west // this could be done simpler... if(nodeDir.x < 0.0f){ if(2.0f*Abs(nodeDir.y) < -nodeDir.x) *nextDir = 6; // west else if(-2.0f*nodeDir.x < nodeDir.y) *nextDir = 0; // north else if(2.0f*nodeDir.x > nodeDir.y) *nextDir = 4; // south else if(nodeDir.y > 0.0f) *nextDir = 7; // north west else *nextDir = 5; // south west` }else{ if(2.0f*Abs(nodeDir.y) < nodeDir.x) *nextDir = 2; // east else if(2.0f*nodeDir.x < nodeDir.y) *nextDir = 0; // north else if(-2.0f*nodeDir.x > nodeDir.y) *nextDir = 4; // south else if(nodeDir.y > 0.0f) *nextDir = 1; // north east else *nextDir = 3; // south east` } } } if(*nextNode == nil){ *nextDir = 0; *nextNode = node; } } static CPathNode *apNodesToBeCleared[6525]; void CPathFind::DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *pNumNodes, int16 maxNumNodes, CVehicle *vehicle, float *pDist, float distLimit, int32 targetNodeId) { int i, j; // Find target if(targetNodeId < 0) targetNodeId = FindNodeClosestToCoors(target, type, distLimit); if(targetNodeId < 0) { *pNumNodes = 0; if(pDist) *pDist = 100000.0f; return; } // Find start if(startNodeId < 0) startNodeId = FindNodeClosestToCoors(start, type, 999999.88f); if(startNodeId < 0) { *pNumNodes = 0; if(pDist) *pDist = 100000.0f; return; } if(startNodeId == targetNodeId){ *pNumNodes = 0; if(pDist) *pDist = 0.0f; return; } if(m_pathNodes[startNodeId].group != m_pathNodes[targetNodeId].group) { *pNumNodes = 0; if(pDist) *pDist = 100000.0f; return; } for(i = 0; i < ARRAY_SIZE(m_searchNodes); i++) m_searchNodes[i].SetNext(nil); AddNodeToList(&m_pathNodes[targetNodeId], 0); int numNodesToBeCleared = 0; apNodesToBeCleared[numNodesToBeCleared++] = &m_pathNodes[targetNodeId]; // Dijkstra's algorithm // Find distances int numPathsFound = 0; for(i = 0; numPathsFound == 0; i = (i+1) & 0x1FF){ CPathNode *node; for(node = m_searchNodes[i].GetNext(); node; node = node->GetNext()){ if(node == &m_pathNodes[startNodeId]) numPathsFound = 1; for(j = 0; j < node->numLinks; j++){ int next = ConnectedNode(node->firstLink + j); int dist = node->distance + m_distances[node->firstLink + j]; if(dist < m_pathNodes[next].distance){ if(m_pathNodes[next].distance != MAX_DIST) RemoveNodeFromList(&m_pathNodes[next]); if(m_pathNodes[next].distance == MAX_DIST) apNodesToBeCleared[numNodesToBeCleared++] = &m_pathNodes[next]; AddNodeToList(&m_pathNodes[next], dist); } } RemoveNodeFromList(node); } } // Find out whence to start tracing back CPathNode *curNode; curNode = &m_pathNodes[startNodeId]; *pNumNodes = 0; if(pDist) *pDist = m_pathNodes[startNodeId].distance; nodes[(*pNumNodes)++] = curNode; // Trace back to target and update list of nodes while(*pNumNodes < maxNumNodes && curNode != &m_pathNodes[targetNodeId]) for(i = 0; i < curNode->numLinks; i++){ int next = ConnectedNode(curNode->firstLink + i); if(curNode->distance - m_distances[curNode->firstLink + i] == m_pathNodes[next].distance){ curNode = &m_pathNodes[next]; nodes[(*pNumNodes)++] = curNode; i = 29030; // could have used a break... } } for(i = 0; i < numNodesToBeCleared; i++) apNodesToBeCleared[i]->distance = MAX_DIST; } static CPathNode *pNodeList[32]; static int16 DummyResult; static int16 DummyResult2; bool CPathFind::TestCoorsCloseness(CVector target, uint8 type, CVector start) { float dist; if(type == PATH_CAR) DoPathSearch(type, start, -1, target, pNodeList, &DummyResult, 32, nil, &dist, 999999.88f, -1); else DoPathSearch(type, start, -1, target, nil, &DummyResult2, 0, nil, &dist, 50.0f, -1); #ifdef FIX_BUGS // dist has GenerationDistMultiplier as a factor, so our reference dist should have it too if(type == PATH_CAR) return dist < 150.0f*TheCamera.GenerationDistMultiplier; else return dist < 100.0f*TheCamera.GenerationDistMultiplier; #else if(type == PATH_CAR) return dist < 150.0f; else return dist < 100.0f; #endif } void CPathFind::Save(uint8 *buf, uint32 *size) { int i; int n = m_numPathNodes/8 + 1; *size = 2*n; for(i = 0; i < m_numPathNodes; i++) if(m_pathNodes[i].bDisabled) buf[i/8] |= 1 << i%8; else buf[i/8] &= ~(1 << i%8); for(i = 0; i < m_numPathNodes; i++) if(m_pathNodes[i].bBetweenLevels) buf[i/8 + n] |= 1 << i%8; else buf[i/8 + n] &= ~(1 << i%8); } void CPathFind::Load(uint8 *buf, uint32 size) { int i; int n = m_numPathNodes/8 + 1; for(i = 0; i < m_numPathNodes; i++) if(buf[i/8] & (1 << i%8)) m_pathNodes[i].bDisabled = true; else m_pathNodes[i].bDisabled = false; for(i = 0; i < m_numPathNodes; i++) if(buf[i/8 + n] & (1 << i%8)) m_pathNodes[i].bBetweenLevels = true; else m_pathNodes[i].bBetweenLevels = false; #ifdef SECUROM // if pirated game for(i = 0; i < m_numPathNodes; i++) m_pathNodes[i].bDisabled = true; #endif } void CPathFind::DisplayPathData(void) { // Not the function from mobile but my own! int i, j, k; // Draw 50 units around camera CVector pos = TheCamera.GetPosition(); const float maxDist = 50.0f; // Render car path nodes if(gbShowCarPaths) for(i = 0; i < m_numCarPathNodes; i++){ if((m_pathNodes[i].GetPosition() - pos).MagnitudeSqr() > SQR(maxDist)) continue; CVector n1 = m_pathNodes[i].GetPosition(); n1.z += 0.3f; // Draw node itself CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, n1.x, n1.y, n1.z + 1.0f, 0xFFFFFFFF, 0xFFFFFFFF); for(j = 0; j < m_pathNodes[i].numLinks; j++){ k = ConnectedNode(m_pathNodes[i].firstLink + j); CVector n2 = m_pathNodes[k].GetPosition(); n2.z += 0.3f; // Draw links to neighbours CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, n2.x, n2.y, n2.z, 0xFFFFFFFF, 0xFFFFFFFF); } } // Render car path nodes if(gbShowCarPathsLinks) for(i = 0; i < m_numCarPathLinks; i++){ CVector2D n1_2d = m_carPathLinks[i].GetPosition(); if((n1_2d - pos).MagnitudeSqr() > SQR(maxDist)) continue; int ni = m_carPathLinks[i].pathNodeIndex; CVector pn1 = m_pathNodes[ni].GetPosition(); pn1.z += 0.3f; CVector n1(n1_2d.x, n1_2d.y, pn1.z); n1.z += 0.3f; // Draw car node itself CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, n1.x, n1.y, n1.z + 1.0f, 0xFFFFFFFF, 0xFFFFFFFF); CLines::RenderLineWithClipping(n1.x, n1.y, n1.z + 0.5f, n1.x+m_carPathLinks[i].GetDirX(), n1.y+m_carPathLinks[i].GetDirY(), n1.z + 0.5f, 0xFFFFFFFF, 0xFFFFFFFF); // Draw connection to car path node CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, pn1.x, pn1.y, pn1.z, 0xFF0000FF, 0xFFFFFFFF); // traffic light type uint32 col = 0xFF; if((m_carPathLinks[i].trafficLightType&0x7F) == 1) col += 0xFF000000; if((m_carPathLinks[i].trafficLightType&0x7F) == 2) col += 0x00FF0000; if(m_carPathLinks[i].trafficLightType & 0x80) col += 0x0000FF00; CLines::RenderLineWithClipping(n1.x+0.2f, n1.y, n1.z, n1.x+0.2f, n1.y, n1.z + 1.0f, col, col); for(j = 0; j < m_pathNodes[ni].numLinks; j++){ k = m_carPathConnections[m_pathNodes[ni].firstLink + j]; CVector2D n2_2d = m_carPathLinks[k].GetPosition(); int nk = m_carPathLinks[k].pathNodeIndex; CVector pn2 = m_pathNodes[nk].GetPosition(); pn2.z += 0.3f; CVector n2(n2_2d.x, n2_2d.y, pn2.z); n2.z += 0.3f; // Draw links to neighbours CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, n2.x, n2.y, n2.z, 0xFF00FFFF, 0xFF00FFFF); } } // Render ped path nodes if(gbShowPedPaths) for(i = m_numCarPathNodes; i < m_numPathNodes; i++){ if((m_pathNodes[i].GetPosition() - pos).MagnitudeSqr() > SQR(maxDist)) continue; CVector n1 = m_pathNodes[i].GetPosition(); n1.z += 0.3f; // Draw node itself CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, n1.x, n1.y, n1.z + 1.0f, 0xFFFFFFFF, 0xFFFFFFFF); for(j = 0; j < m_pathNodes[i].numLinks; j++){ k = ConnectedNode(m_pathNodes[i].firstLink + j); CVector n2 = m_pathNodes[k].GetPosition(); n2.z += 0.3f; // Draw links to neighbours CLines::RenderLineWithClipping(n1.x, n1.y, n1.z, n2.x, n2.y, n2.z, 0xFFFFFFFF, 0xFFFFFFFF); // Draw connection flags CVector mid = (n1+n2)/2.0f; uint32 col = 0xFF; if(ConnectionCrossesRoad(m_pathNodes[i].firstLink + j)) col += 0x00FF0000; if(ConnectionHasTrafficLight(m_pathNodes[i].firstLink + j)) col += 0xFF000000; CLines::RenderLineWithClipping(mid.x, mid.y, mid.z, mid.x, mid.y, mid.z + 1.0f, col, col); } } } CVector CPathFind::TakeWidthIntoAccountForWandering(CPathNode* nextNode, uint16 random) { CVector pos = nextNode->GetPosition(); float newX = (nextNode->GetPedNodeWidth() * ((random % 16) - 7)) + pos.x; float newY = (nextNode->GetPedNodeWidth() * (((random / 16) % 16) - 7)) + pos.y; return CVector(newX, newY, pos.z); } void CPathFind::TakeWidthIntoAccountForCoors(CPathNode* node1, CPathNode* node2, uint16 random, float* x, float* y) { *x += (Min(node1->width, node2->width) * WIDTH_TO_PED_NODE_WIDTH * ((random % 16) - 7)); *y += (Min(node1->width, node2->width) * WIDTH_TO_PED_NODE_WIDTH * (((random / 16) % 16) - 7)); } CPathNode* CPathFind::GetNode(int16 index) { if(index < 0) return nil; if(index < ARRAY_SIZE(ThePaths.m_searchNodes)) return &ThePaths.m_searchNodes[index]; return &ThePaths.m_pathNodes[index - ARRAY_SIZE(ThePaths.m_searchNodes)]; } int16 CPathFind::GetIndex(CPathNode *node) { if(node == nil) return -1; if(node >= &ThePaths.m_searchNodes[0] && node < &ThePaths.m_searchNodes[ARRAY_SIZE(ThePaths.m_searchNodes)]) return node - ThePaths.m_searchNodes; else return (node - ThePaths.m_pathNodes) + ARRAY_SIZE(ThePaths.m_searchNodes); } ================================================ FILE: src/control/PathFind.h ================================================ #pragma once #include "Treadable.h" class CVehicle; class CPtrList; #define LANE_WIDTH 5.0f #define WIDTH_TO_PED_NODE_WIDTH (31.f/(500.f * 8.f)) enum { NodeTypeExtern = 1, NodeTypeIntern = 2, }; enum { PATH_CAR = 0, PATH_PED = 1, }; enum { SWITCH_OFF = 0, SWITCH_ON = 1, }; enum { ROUTE_ADD_BLOCKADE = 0, ROUTE_NO_BLOCKADE = 1 }; struct CPedPathNode { bool bBlockade; uint8 nodeIdX; uint8 nodeIdY; int16 id; CPedPathNode* prev; CPedPathNode* next; }; VALIDATE_SIZE(CPedPathNode, 0x10); class CPedPath { public: static bool CalcPedRoute(int8 pathType, CVector position, CVector destination, CVector *pointPoses, int16 *pointsFound, int16 maxPoints); static void AddNodeToPathList(CPedPathNode *pNodeToAdd, int16 id, CPedPathNode *pNodeList); static void RemoveNodeFromList(CPedPathNode *pNode); static void AddNodeToList(CPedPathNode *pNode, int16 index, CPedPathNode *pList); static void AddBlockade(CEntity *pEntity, CPedPathNode(*pathNodes)[40], CVector *pPosition); static void AddBlockadeSectorList(CPtrList& list, CPedPathNode(*pathNodes)[40], CVector *pPosition); static void AddBuildingBlockade(CEntity*, CPedPathNode(*)[40], CVector*); static void AddBuildingBlockadeSectorList(CPtrList&, CPedPathNode(*)[40], CVector*); }; struct CPathNode { int16 prevIndex; int16 nextIndex; int16 x; int16 y; int16 z; int16 distance; // in path search int16 firstLink; uint8 width; int8 group; uint8 numLinks : 4; uint8 bDeadEnd : 1; uint8 bDisabled : 1; uint8 bBetweenLevels : 1; uint8 bUseInRoadBlock : 1; uint8 bWaterPath : 1; uint8 bOnlySmallBoats : 1; uint8 bSelected : 1; uint8 speedLimit : 2; //uint8 flagB20 : 1; //uint8 flagB40 : 1; //uint8 flagB80 : 1; uint8 spawnRate : 4; uint8 flagsC : 4; CVector GetPosition(void) { return CVector(x/8.0f, y/8.0f, z/8.0f); } void SetPosition(const CVector &p) { x = p.x*8.0f; y = p.y*8.0f; z = p.z*8.0f; } float GetX(void) { return x/8.0f; } float GetY(void) { return y/8.0f; } float GetZ(void) { return z/8.0f; } bool HasDivider(void) { return width != 0; } float GetDividerWidth(void) { return width/(2*8.0f); } float GetPedNodeWidth(void) { return width*WIDTH_TO_PED_NODE_WIDTH; } CPathNode *GetPrev(void); CPathNode *GetNext(void); void SetPrev(CPathNode *node); void SetNext(CPathNode *node); }; union CConnectionFlags { uint8 flags; struct { uint8 bCrossesRoad : 1; uint8 bTrafficLight : 1; }; }; struct CCarPathLink { int16 x; int16 y; int16 pathNodeIndex; int8 dirX; int8 dirY; int8 numLeftLanes : 3; int8 numRightLanes : 3; uint8 trafficLightDirection : 1; uint8 trafficLightType : 2; uint8 bBridgeLights : 1; // at least in LCS... uint8 width; CVector2D GetPosition(void) { return CVector2D(x/8.0f, y/8.0f); } CVector2D GetDirection(void) { return CVector2D(dirX/100.0f, dirY/100.0f); } float GetX(void) { return x/8.0f; } float GetY(void) { return y/8.0f; } float GetDirX(void) { return dirX/100.0f; } float GetDirY(void) { return dirY/100.0f; } float GetLaneOffset(void) { return width/(2*8.0f*LANE_WIDTH); } float OneWayLaneOffset() { if (numLeftLanes == 0) return 0.5f - 0.5f * numRightLanes; if (numRightLanes == 0) return 0.5f - 0.5f * numLeftLanes; return 0.5f + GetLaneOffset(); } }; // This is what we're reading from the files, only temporary struct CPathInfoForObject { float x; float y; float z; int8 type; int8 next; int8 numLeftLanes; int8 numRightLanes; int8 speedLimit; uint8 width; uint8 crossing : 1; uint8 onlySmallBoats : 1; uint8 roadBlock : 1; uint8 disabled : 1; uint8 waterPath : 1; uint8 betweenLevels : 1; uint8 spawnRate : 4; void CheckIntegrity(void); void SwapConnectionsToBeRightWayRound(void); }; extern CPathInfoForObject *InfoForTileCars; extern CPathInfoForObject *InfoForTilePeds; struct CTempNode { CVector pos; int8 dirX; // *100 int8 dirY; int16 link1; int16 link2; int8 numLeftLanes; int8 numRightLanes; uint8 width; bool isCross; int8 linkState; }; struct CTempNodeExternal // made up name { CVector pos; int16 next; int8 numLeftLanes; int8 numRightLanes; uint8 width; bool isCross; }; // from mobile template class CRoute { T m_node[8]; }; class CPathFind { public: CPathNode m_pathNodes[NUM_PATHNODES]; CCarPathLink m_carPathLinks[NUM_CARPATHLINKS]; CTreadable *m_mapObjects[NUM_MAPOBJECTS]; uint16 m_connections[NUM_PATHCONNECTIONS]; // and flags uint8 m_distances[NUM_PATHCONNECTIONS]; int16 m_carPathConnections[NUM_PATHCONNECTIONS]; int32 m_numPathNodes; int32 m_numCarPathNodes; int32 m_numPedPathNodes; int16 m_numMapObjects; int16 m_numConnections; int32 m_numCarPathLinks; int32 unk; uint8 m_numGroups[2]; CPathNode m_searchNodes[512]; void Init(void); void AllocatePathFindInfoMem(int16 numPathGroups); void RegisterMapObject(CTreadable *mapObject); void StoreNodeInfoPed(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, bool crossing, uint8 spawnRate); void StoreNodeInfoCar(int16 id, int16 node, int8 type, int8 next, int16 x, int16 y, int16 z, float width, int8 numLeft, int8 numRight, bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate); void StoreDetachedNodeInfoPed(int32 node, int8 type, int32 next, float x, float y, float z, float width, bool crossing, bool disabled, bool betweenLevels, uint8 spawnRate); void StoreDetachedNodeInfoCar(int32 node, int8 type, int32 next, float x, float y, float z, float width, int8 numLeft, int8 numRight, bool disabled, bool betweenLevels, uint8 speedLimit, bool roadBlock, bool waterPath, uint8 spawnRate, bool unk); void CalcNodeCoors(float x, float y, float z, int32 id, CVector *out); bool LoadPathFindData(void); void PreparePathData(void); void CountFloodFillGroups(uint8 type); void PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo, float maxdist, CPathInfoForObject *detachednodes, int32 numDetached); bool IsPathObject(int id) { return id < PATHNODESIZE && (InfoForTileCars[id*12].type != 0 || InfoForTilePeds[id*12].type != 0); } float CalcRoadDensity(float x, float y); bool TestForPedTrafficLight(CPathNode *n1, CPathNode *n2); bool TestCrossesRoad(CPathNode *n1, CPathNode *n2); void AddNodeToList(CPathNode *node, int32 listId); void RemoveNodeFromList(CPathNode *node); void RemoveBadStartNode(CVector pos, CPathNode **nodes, int16 *n); void SetLinksBridgeLights(float, float, float, float, bool); void SwitchOffNodeAndNeighbours(int32 nodeId, bool disable); void SwitchRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable); void SwitchPedRoadsOffInArea(float x1, float x2, float y1, float y2, float z1, float z2, bool disable); void SwitchRoadsInAngledArea(float x1, float y1, float z1, float x2, float y2, float z2, float length, uint8 type, uint8 enable); void MarkRoadsBetweenLevelsNodeAndNeighbours(int32 nodeId); void MarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2); void PedMarkRoadsBetweenLevelsInArea(float x1, float x2, float y1, float y2, float z1, float z2); int32 FindNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled = false, bool ignoreBetweenLevels = false, bool ignoreSelected = false, bool bWaterPath = false); int32 FindNodeClosestToCoorsFavourDirection(CVector coors, uint8 type, float dirX, float dirY); void FindNodePairClosestToCoors(CVector coors, uint8 type, int* node1, int* node2, float* angle, float minDist, float maxDist, bool ignoreDisabled = false, bool ignoreBetweenLevels = false, bool bWaterPath = false); int32 FindNthNodeClosestToCoors(CVector coors, uint8 type, float distLimit, bool ignoreDisabled, bool ignoreBetweenLevels, int N, bool bWaterPath = false); CVector FindNodeCoorsForScript(int32 id); float FindNodeOrientationForCarPlacement(int32 nodeId); float FindNodeOrientationForCarPlacementFacingDestination(int32 nodeId, float x, float y, bool towards); bool GenerateCarCreationCoors(float x, float y, float dirX, float dirY, float spawnDist, float angleLimit, bool forward, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, bool ignoreDisabled = false); bool GeneratePedCreationCoors(float x, float y, float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen, CVector *pPosition, int32 *pNode1, int32 *pNode2, float *pPositionBetweenNodes, CMatrix *camMatrix); void FindNextNodeWandering(uint8, CVector, CPathNode**, CPathNode**, uint8, uint8*); void DoPathSearch(uint8 type, CVector start, int32 startNodeId, CVector target, CPathNode **nodes, int16 *numNodes, int16 maxNumNodes, CVehicle *vehicle, float *dist, float distLimit, int32 forcedTargetNode); bool TestCoorsCloseness(CVector target, uint8 type, CVector start); void Save(uint8 *buf, uint32 *size); void Load(uint8 *buf, uint32 size); static CVector TakeWidthIntoAccountForWandering(CPathNode*, uint16); static void TakeWidthIntoAccountForCoors(CPathNode*, CPathNode*, uint16, float*, float*); CPathNode *GetNode(int16 index); int16 GetIndex(CPathNode *node); uint16 ConnectedNode(int id) { return m_connections[id] & 0x3FFF; } bool ConnectionCrossesRoad(int id) { return !!(m_connections[id] & 0x8000); } bool ConnectionHasTrafficLight(int id) { return !!(m_connections[id] & 0x4000); } void ConnectionSetTrafficLight(int id) { m_connections[id] |= 0x4000; } void DisplayPathData(void); // Following methods are present on mobile but are unused. TODO: implement them void SavePathFindData(void); void ComputeRoute(uint8, const CVector&, const CVector&, CRoute&); void RecordNodesClosestToCoors(CVector, uint8, int, CPathNode**, float, bool, bool, bool); void RecordNodesInCircle(const CVector&, float, uint8, int, CPathNode**, bool, bool, bool, bool); void ArrangeOneNodeList(CPathInfoForObject*, int16); void ArrangeNodes(int16); void RegisterMarker(CVector*); void Shutdown(void); }; extern CPathFind ThePaths; inline CPathNode *CPathNode::GetPrev(void) { return ThePaths.GetNode(prevIndex); } inline CPathNode *CPathNode::GetNext(void) { return ThePaths.GetNode(nextIndex); } inline void CPathNode::SetPrev(CPathNode *node) { prevIndex = ThePaths.GetIndex(node); } inline void CPathNode::SetNext(CPathNode *node) { nextIndex = ThePaths.GetIndex(node); } extern bool gbShowPedPaths; extern bool gbShowCarPaths; extern bool gbShowCarPathsLinks; ================================================ FILE: src/control/Phones.cpp ================================================ #include "common.h" #include "Phones.h" #include "Pools.h" #include "ModelIndices.h" #include "Ped.h" #include "Pad.h" #include "Messages.h" #include "Camera.h" #include "World.h" #include "General.h" #include "AudioScriptObject.h" #include "RpAnimBlend.h" #include "AnimBlendAssociation.h" #include "soundlist.h" #ifdef FIX_BUGS #include "Replay.h" #endif CPhoneInfo gPhoneInfo; bool CPhoneInfo::bDisplayingPhoneMessage; // is phone picked up uint32 CPhoneInfo::PhoneEnableControlsTimer; CPhone *CPhoneInfo::pPhoneDisplayingMessages; bool CPhoneInfo::bPickingUpPhone; CPed *CPhoneInfo::pCallBackPed; // ped who picking up the phone (reset after pickup cb) /* Entering phonebooth cutscene, showing messages and triggering these things by checking coordinates happens in here - blue mission marker is cosmetic. Repeated message means after the script set the messages for a particular phone, player can pick the phone again with the same messages appearing, after 60 seconds of last phone pick-up. */ void CPhoneInfo::Update(void) { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return; #endif CPlayerPed *player = FindPlayerPed(); CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus]; if (bDisplayingPhoneMessage && CTimer::GetTimeInMilliseconds() > PhoneEnableControlsTimer) { playerInfo->MakePlayerSafe(false); TheCamera.SetWideScreenOff(); pPhoneDisplayingMessages = nil; bDisplayingPhoneMessage = false; CAnimBlendAssociation *talkAssoc = RpAnimBlendClumpGetAssociation(player->GetClump(), ANIM_STD_PHONE_TALK); if (talkAssoc && talkAssoc->blendAmount > 0.5f) { CAnimBlendAssociation *endAssoc = CAnimManager::BlendAnimation(player->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_OUT, 8.0f); endAssoc->flags &= ~ASSOC_DELETEFADEDOUT; endAssoc->SetFinishCallback(PhonePutDownCB, player); } else { CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_PHONE); if (player->m_nPedState == PED_MAKE_CALL) player->SetPedState(PED_IDLE); } } bool notInCar; CVector playerPos; if (FindPlayerVehicle()) { notInCar = false; playerPos = FindPlayerVehicle()->GetPosition(); } else { notInCar = true; playerPos = player->GetPosition(); } bool phoneRings = false; bool scratchTheCabinet; for(int phoneId = 0; phoneId < m_nScriptPhonesMax; phoneId++) { if (m_aPhones[phoneId].m_visibleToCam) { switch (m_aPhones[phoneId].m_nState) { case PHONE_STATE_ONETIME_MESSAGE_SET: case PHONE_STATE_REPEATED_MESSAGE_SET: case PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE: if (bPickingUpPhone) { scratchTheCabinet = false; phoneRings = false; } else { scratchTheCabinet = (CTimer::GetTimeInMilliseconds() / 1880) % 2 == 1; phoneRings = (CTimer::GetPreviousTimeInMilliseconds() / 1880) % 2 == 1; } if (scratchTheCabinet) { m_aPhones[phoneId].m_pEntity->GetUp().z = (CGeneral::GetRandomNumber() % 1024) / 16000.0f + 1.0f; if (!phoneRings) PlayOneShotScriptObject(SCRIPT_SOUND_PAYPHONE_RINGING, m_aPhones[phoneId].m_pEntity->GetPosition()); } else { m_aPhones[phoneId].m_pEntity->GetUp().z = 1.0f; } m_aPhones[phoneId].m_pEntity->GetMatrix().UpdateRW(); m_aPhones[phoneId].m_pEntity->UpdateRwFrame(); if (notInCar && !bPickingUpPhone && player->IsPedInControl()) { CVector2D distToPhone = playerPos - m_aPhones[phoneId].m_vecPos; if (Abs(distToPhone.x) < 1.0f && Abs(distToPhone.y) < 1.0f) { if (DotProduct2D(distToPhone, m_aPhones[phoneId].m_pEntity->GetForward()) / distToPhone.Magnitude() < -0.85f) { CVector2D distToPhoneObj = playerPos - m_aPhones[phoneId].m_pEntity->GetPosition(); float angleToFace = CGeneral::GetATanOfXY(distToPhoneObj.x, distToPhoneObj.y) + HALFPI; if (angleToFace > TWOPI) angleToFace = angleToFace - TWOPI; player->m_fRotationCur = angleToFace; player->m_fRotationDest = angleToFace; player->SetHeading(angleToFace); player->SetPedState(PED_MAKE_CALL); CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_PHONE); TheCamera.SetWideScreenOn(); playerInfo->MakePlayerSafe(true); CAnimBlendAssociation *phonePickAssoc = CAnimManager::BlendAnimation(player->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_IN, 4.0f); phonePickAssoc->SetFinishCallback(PhonePickUpCB, &m_aPhones[phoneId]); bPickingUpPhone = true; pCallBackPed = player; } } } break; case PHONE_STATE_REPEATED_MESSAGE_STARTED: if (CTimer::GetTimeInMilliseconds() - m_aPhones[phoneId].m_repeatedMessagePickupStart > 60000) m_aPhones[phoneId].m_nState = PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE; break; case PHONE_STATE_9: scratchTheCabinet = (CTimer::GetTimeInMilliseconds() / 1880) % 2 == 1; phoneRings = (CTimer::GetPreviousTimeInMilliseconds() / 1880) % 2 == 1; if (scratchTheCabinet) { m_aPhones[phoneId].m_pEntity->GetUp().z = (CGeneral::GetRandomNumber() % 1024) / 16000.0f + 1.0f; if (!phoneRings) PlayOneShotScriptObject(SCRIPT_SOUND_PAYPHONE_RINGING, m_aPhones[phoneId].m_pEntity->GetPosition()); } else { m_aPhones[phoneId].m_pEntity->GetUp().z = 1.0f; } m_aPhones[phoneId].m_pEntity->GetMatrix().UpdateRW(); m_aPhones[phoneId].m_pEntity->UpdateRwFrame(); break; default: break; } if (CVector2D(TheCamera.GetPosition() - m_aPhones[phoneId].m_vecPos).MagnitudeSqr() > sq(100.0f)) m_aPhones[phoneId].m_visibleToCam = false; } else if (!((CTimer::GetFrameCounter() + m_aPhones[phoneId].m_pEntity->m_randomSeed) % 16)) { if (CVector2D(TheCamera.GetPosition() - m_aPhones[phoneId].m_vecPos).MagnitudeSqr() < sq(60.0f)) m_aPhones[phoneId].m_visibleToCam = true; } } } int CPhoneInfo::FindNearestFreePhone(CVector *pos) { int nearestPhoneId = -1; float nearestPhoneDist = 60.0f; for (int phoneId = 0; phoneId < m_nMax; phoneId++) { if (gPhoneInfo.m_aPhones[phoneId].m_nState == PHONE_STATE_FREE) { float phoneDist = (m_aPhones[phoneId].m_vecPos - *pos).Magnitude2D(); if (phoneDist < nearestPhoneDist) { nearestPhoneDist = phoneDist; nearestPhoneId = phoneId; } } } return nearestPhoneId; } bool CPhoneInfo::PhoneAtThisPosition(CVector pos) { for (int phoneId = 0; phoneId < m_nMax; phoneId++) { if (pos.x == m_aPhones[phoneId].m_vecPos.x && pos.y == m_aPhones[phoneId].m_vecPos.y) return true; } return false; } bool CPhoneInfo::HasMessageBeenDisplayed(int phoneId) { if (bDisplayingPhoneMessage) return false; int state = m_aPhones[phoneId].m_nState; return state == PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE || state == PHONE_STATE_ONETIME_MESSAGE_STARTED || state == PHONE_STATE_REPEATED_MESSAGE_STARTED; } bool CPhoneInfo::IsMessageBeingDisplayed(int phoneId) { return pPhoneDisplayingMessages == &m_aPhones[phoneId]; } void CPhoneInfo::Load(uint8 *buf, uint32 size) { INITSAVEBUF m_nMax = ReadSaveBuf(buf); m_nScriptPhonesMax = ReadSaveBuf(buf); for (int i = 0; i < NUMPHONES; i++) { m_aPhones[i] = ReadSaveBuf(buf); // It's saved as building pool index in save file, convert it to true entity if (m_aPhones[i].m_pEntity) { m_aPhones[i].m_pEntity = CPools::GetBuildingPool()->GetSlot((uintptr)m_aPhones[i].m_pEntity - 1); } } VALIDATESAVEBUF(size) } void CPhoneInfo::SetPhoneMessage_JustOnce(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6) { // If there is at least one message, it should be msg1. if (msg1) { m_aPhones[phoneId].m_apMessages[0] = msg1; m_aPhones[phoneId].m_apMessages[1] = msg2; m_aPhones[phoneId].m_apMessages[2] = msg3; m_aPhones[phoneId].m_apMessages[3] = msg4; m_aPhones[phoneId].m_apMessages[4] = msg5; m_aPhones[phoneId].m_apMessages[5] = msg6; m_aPhones[phoneId].m_nState = PHONE_STATE_ONETIME_MESSAGE_SET; } else { m_aPhones[phoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED; } } void CPhoneInfo::SetPhoneMessage_Repeatedly(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6) { // If there is at least one message, it should be msg1. if (msg1) { m_aPhones[phoneId].m_apMessages[0] = msg1; m_aPhones[phoneId].m_apMessages[1] = msg2; m_aPhones[phoneId].m_apMessages[2] = msg3; m_aPhones[phoneId].m_apMessages[3] = msg4; m_aPhones[phoneId].m_apMessages[4] = msg5; m_aPhones[phoneId].m_apMessages[5] = msg6; m_aPhones[phoneId].m_nState = PHONE_STATE_REPEATED_MESSAGE_SET; } else { m_aPhones[phoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED; } } int CPhoneInfo::GrabPhone(float xPos, float yPos) { // "Grab" doesn't mean picking up the phone, it means allocating some particular phone to // whoever called the 024A opcode first with the position parameters closest to phone. // Same phone won't be available on next run of this function. int nearestPhoneId = -1; CVector pos(xPos, yPos, 0.0f); float nearestPhoneDist = 100.0f; for (int phoneId = m_nScriptPhonesMax; phoneId < m_nMax; phoneId++) { float phoneDistance = (m_aPhones[phoneId].m_vecPos - pos).Magnitude2D(); if (phoneDistance < nearestPhoneDist) { nearestPhoneDist = phoneDistance; nearestPhoneId = phoneId; } } m_aPhones[nearestPhoneId].m_nState = PHONE_STATE_MESSAGE_REMOVED; CPhone oldFirstPhone = m_aPhones[m_nScriptPhonesMax]; m_aPhones[m_nScriptPhonesMax] = m_aPhones[nearestPhoneId]; m_aPhones[nearestPhoneId] = oldFirstPhone; m_nScriptPhonesMax++; return m_nScriptPhonesMax - 1; } void CPhoneInfo::Initialise(void) { CBuildingPool *pool = CPools::GetBuildingPool(); pCallBackPed = nil; bDisplayingPhoneMessage = false; bPickingUpPhone = false; pPhoneDisplayingMessages = nil; m_nMax = 0; m_nScriptPhonesMax = 0; for (int i = pool->GetSize() - 1; i >= 0; i--) { CBuilding *building = pool->GetSlot(i); if (building) { if (building->GetModelIndex() == MI_PHONEBOOTH1) { assert(m_nMax < ARRAY_SIZE(m_aPhones) && "NUMPHONES should be increased"); CPhone *maxPhone = &m_aPhones[m_nMax]; maxPhone->m_nState = PHONE_STATE_FREE; maxPhone->m_vecPos = building->GetPosition(); maxPhone->m_pEntity = building; m_nMax++; } } } } void CPhoneInfo::Save(uint8 *buf, uint32 *size) { *size = sizeof(CPhoneInfo); INITSAVEBUF WriteSaveBuf(buf, m_nMax); WriteSaveBuf(buf, m_nScriptPhonesMax); for(int phoneId = 0; phoneId < NUMPHONES; phoneId++) { CPhone* phone = WriteSaveBuf(buf, m_aPhones[phoneId]); // Convert entity pointer to building pool index while saving if (phone->m_pEntity) { phone->m_pEntity = (CEntity*) (CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)phone->m_pEntity) + 1); } } VALIDATESAVEBUF(*size) } void CPhoneInfo::Shutdown(void) { m_nMax = 0; m_nScriptPhonesMax = 0; } void PhonePutDownCB(CAnimBlendAssociation *assoc, void *arg) { assoc->flags |= ASSOC_DELETEFADEDOUT; assoc->blendDelta = -1000.0f; CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_PHONE); CPed *ped = (CPed*)arg; if (assoc->blendAmount > 0.5f) ped->bUpdateAnimHeading = true; if (ped->m_nPedState == PED_MAKE_CALL) ped->SetPedState(PED_IDLE); } void PhonePickUpCB(CAnimBlendAssociation *assoc, void *arg) { CPhone *phone = (CPhone*)arg; int messagesDisplayTime = 0; for(int i=0; i < 6; i++) { wchar *msg = phone->m_apMessages[i]; if (msg) { CMessages::AddMessage(msg, 3000, 0); messagesDisplayTime += 3000; } } CPhoneInfo::bPickingUpPhone = false; CPhoneInfo::bDisplayingPhoneMessage = true; CPhoneInfo::pPhoneDisplayingMessages = phone; CPhoneInfo::PhoneEnableControlsTimer = CTimer::GetTimeInMilliseconds() + messagesDisplayTime; if (phone->m_nState == PHONE_STATE_ONETIME_MESSAGE_SET) { phone->m_nState = PHONE_STATE_ONETIME_MESSAGE_STARTED; } else { phone->m_nState = PHONE_STATE_REPEATED_MESSAGE_STARTED; phone->m_repeatedMessagePickupStart = CTimer::GetTimeInMilliseconds(); } CPed *ped = CPhoneInfo::pCallBackPed; ped->m_nMoveState = PEDMOVE_STILL; CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE, 8.0f); if (assoc->blendAmount > 0.5f && ped) CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_TALK, 8.0f); CPhoneInfo::pCallBackPed = nil; } ================================================ FILE: src/control/Phones.h ================================================ #pragma once #include "Physical.h" class CPed; class CAnimBlendAssociation; enum PhoneState { PHONE_STATE_FREE, PHONE_STATE_REPORTING_CRIME, // CCivilianPed::ProcessControl sets it but unused PHONE_STATE_2, PHONE_STATE_MESSAGE_REMOVED, PHONE_STATE_ONETIME_MESSAGE_SET, PHONE_STATE_REPEATED_MESSAGE_SET, PHONE_STATE_REPEATED_MESSAGE_SHOWN_ONCE, PHONE_STATE_ONETIME_MESSAGE_STARTED, PHONE_STATE_REPEATED_MESSAGE_STARTED, PHONE_STATE_9 // just rings, picking being handled via script. most of the time game uses this }; class CPhone { public: CVector m_vecPos; wchar *m_apMessages[6]; uint32 m_repeatedMessagePickupStart; CEntity *m_pEntity; // stored as building pool index in save files PhoneState m_nState; bool m_visibleToCam; CPhone() { } ~CPhone() { } }; VALIDATE_SIZE(CPhone, 0x34); class CPhoneInfo { public: static bool bDisplayingPhoneMessage; static uint32 PhoneEnableControlsTimer; static CPhone *pPhoneDisplayingMessages; static bool bPickingUpPhone; static CPed *pCallBackPed; int32 m_nMax; int32 m_nScriptPhonesMax; CPhone m_aPhones[NUMPHONES]; CPhoneInfo() { } ~CPhoneInfo() { } int FindNearestFreePhone(CVector*); bool PhoneAtThisPosition(CVector); bool HasMessageBeenDisplayed(int); bool IsMessageBeingDisplayed(int); void Load(uint8 *buf, uint32 size); void Save(uint8 *buf, uint32 *size); void SetPhoneMessage_JustOnce(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6); void SetPhoneMessage_Repeatedly(int phoneId, wchar *msg1, wchar *msg2, wchar *msg3, wchar *msg4, wchar *msg5, wchar *msg6); int GrabPhone(float, float); void Initialise(void); void Shutdown(void); void Update(void); }; extern CPhoneInfo gPhoneInfo; void PhonePutDownCB(CAnimBlendAssociation *assoc, void *arg); void PhonePickUpCB(CAnimBlendAssociation *assoc, void *arg); ================================================ FILE: src/control/Pickups.cpp ================================================ #include "common.h" #include "main.h" #include "Camera.h" #include "Coronas.h" #include "Darkel.h" #include "Entity.h" #include "Explosion.h" #include "Font.h" #include "Garages.h" #include "General.h" #include "ModelIndices.h" #include "Object.h" #include "Pad.h" #include "Pickups.h" #include "PlayerPed.h" #include "Wanted.h" #include "DMAudio.h" #include "Fire.h" #include "PointLights.h" #include "Pools.h" #ifdef FIX_BUGS #include "Replay.h" #endif #include "Script.h" #include "Shadows.h" #include "SpecialFX.h" #include "Sprite.h" #include "Timer.h" #include "WaterLevel.h" #include "World.h" #include "Hud.h" #include "Messages.h" #include "Streaming.h" CPickup CPickups::aPickUps[NUMPICKUPS]; int16 CPickups::NumMessages; int32 CPickups::aPickUpsCollected[NUMCOLLECTEDPICKUPS]; int16 CPickups::CollectedPickUpIndex; int32 CPickups::PlayerOnWeaponPickup; int32 CollectPickupBuffer; // unused bool CPickups::bPickUpcamActivated; CVehicle *CPickups::pPlayerVehicle; CVector CPickups::StaticCamCoors; uint32 CPickups::StaticCamStartTime; tPickupMessage CPickups::aMessages[NUMPICKUPMESSAGES]; uint16 AmmoForWeapon[WEAPONTYPE_TOTALWEAPONS + 1] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 8, 8, 68, 24, 32, 28, 20, 200, 120, 120, 120, 120, 120, 40, 28, 8, 300, 200, 1000, 1, 400, 36, 0 }; uint16 AmmoForWeapon_OnStreet[WEAPONTYPE_TOTALWEAPONS + 1] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 34, 12, 16, 14, 10, 100, 60, 60, 60, 60, 60, 20, 14, 4, 150, 100, 500, 1, 400, 36, 0 }; uint16 CostOfWeapon[WEAPONTYPE_TOTALWEAPONS + 3] = { 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 1000, 1000, 1000, 500, 8000, 250, 400, 1200, 1250, 1250, 800, 800, 650, 1200, 5000, 400, 10000, 10000, 8000, 8000, 8000, 10000, 1000, 11000, 500, 20, 10, 0 }; struct { uint8 r,g,b; float unk; } aPickupColors[] = { { 128, 128, 128, 1.0f }, { 128, 128, 128, 1.0f }, { 97, 194, 247, 1.0f }, { 97, 194, 247, 1.0f }, { 97, 194, 247, 1.0f }, { 97, 194, 247, 1.0f }, { 97, 194, 247, 1.0f }, { 97, 194, 247, 1.0f }, { 97, 194, 247, 1.0f }, { 97, 194, 247, 1.0f }, { 97, 194, 247, 1.0f }, { 97, 194, 247, 1.0f }, { 27, 89, 130, 1.0f }, { 27, 89, 130, 1.0f }, { 27, 89, 130, 1.0f }, { 27, 89, 130, 1.0f }, { 27, 89, 130, 1.0f }, { 149, 194, 24, 1.0f }, { 149, 194, 24, 1.0f }, { 45, 155, 90, 1.0f }, { 45, 155, 90, 1.0f }, { 45, 155, 90, 1.0f }, { 255, 227, 79, 1.0f }, { 255, 227, 79, 1.0f }, { 255, 227, 79, 1.0f }, { 255, 227, 79, 1.0f }, { 254, 137, 0, 1.0f }, { 254, 137, 0, 1.0f }, { 249, 131, 215, 1.0f }, { 249, 131, 215, 1.0f }, { 164, 40, 178, 1.0f }, { 164, 40, 178, 1.0f }, { 164, 40, 178, 1.0f }, { 164, 40, 178, 1.0f }, { 69, 69, 69, 1.0f }, { 69, 69, 69, 1.0f }, { 69, 69, 69, 1.0f }, { 255, 100, 100, 1.0f }, { 128, 255, 128, 1.0f }, { 100, 100, 255, 1.0f }, { 255, 255, 100, 1.0f }, { 255, 100, 100, 1.0f }, { 100, 255, 100, 1.0f }, { 255, 255, 255, 1.0f } }; void ModifyStringLabelForControlSetting(char *str) { int len = (int)strlen(str); if (len <= 2) return; if (str[len - 2] != '_') return; switch (CPad::GetPad(0)->Mode) { case 0: case 1: str[len - 1] = 'L'; break; case 2: str[len - 1] = 'T'; break; case 3: str[len - 1] = 'C'; break; default: return; } } void CPickup::ExtractAmmoFromPickup(CPlayerPed *player) { eWeaponType weaponType = CPickups::WeaponForModel(m_pObject->GetModelIndex()); if (m_eType == PICKUP_IN_SHOP || !CWeaponInfo::IsWeaponSlotAmmoMergeable(CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot)) return; uint32 ammo = m_nQuantity; if (ammo == 0) { if (!m_bWasAmmoCollected) ammo = AmmoForWeapon_OnStreet[weaponType]; else goto removeAmmo; } player->GrantAmmo(weaponType, ammo); DMAudio.PlayOneShot(player->m_audioEntityId, SOUND_WEAPON_RELOAD, weaponType); // BUG? weapon type as volume, wtf? removeAmmo: m_nQuantity = 0; m_bWasAmmoCollected = true; } void CPickup::Remove() { GetRidOfObjects(); m_bRemoved = true; m_eType = PICKUP_NONE; } CObject * CPickup::GiveUsAPickUpObject(CObject **ppObject, CObject **ppExtraObject, int32 handle, int32 extraHandle) { CObject *&object = *ppObject; CObject *&extraObject = *ppExtraObject; object = extraObject = nil; int32 modelId = -1; if (CModelInfo::GetModelInfo(m_eModelIndex)->GetModelType() == MITYPE_WEAPON) { CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(((CWeaponModelInfo*)CModelInfo::GetModelInfo(m_eModelIndex))->GetWeaponInfo()); modelId = weaponInfo->m_nModelId; if (modelId == m_eModelIndex) modelId = weaponInfo->m_nModel2Id; } if (handle >= 0) { CPools::MakeSureSlotInObjectPoolIsEmpty(handle); if (extraHandle >= 0) CPools::MakeSureSlotInObjectPoolIsEmpty(extraHandle); if (object == nil) object = new(handle) CObject(m_eModelIndex, false); if (extraHandle >= 0 && modelId != -1 && extraObject == nil) extraObject = new(extraHandle) CObject(modelId, false); } else { object = new CObject(m_eModelIndex, false); if (modelId != -1) extraObject = new CObject(modelId, false); } if (object == nil) return nil; object->ObjectCreatedBy = MISSION_OBJECT; object->SetPosition(m_vecPos); object->SetOrientation(0.0f, 0.0f, -HALFPI); object->GetMatrix().UpdateRW(); object->UpdateRwFrame(); object->bAffectedByGravity = false; object->bExplosionProof = true; object->bUsesCollision = false; object->bIsPickup = true; object->bAmmoCollected = m_bWasAmmoCollected; object->bHasPreRenderEffects = true; if (extraObject) { extraObject->ObjectCreatedBy = MISSION_OBJECT; extraObject->SetPosition(m_vecPos); extraObject->SetOrientation(0.0f, 0.0f, -HALFPI); extraObject->GetMatrix().UpdateRW(); extraObject->UpdateRwFrame(); extraObject->bAffectedByGravity = false; extraObject->bExplosionProof = true; extraObject->bUsesCollision = false; extraObject->bIsPickup = true; extraObject->bAmmoCollected = true; extraObject->bHasPreRenderEffects = true; extraObject->m_nBonusValue = 0; extraObject->bPickupObjWithMessage = false; extraObject->bOutOfStock = false; } object->m_nBonusValue = (m_eModelIndex == MI_PICKUP_BONUS || m_eModelIndex == MI_PICKUP_CLOTHES) ? m_nQuantity : 0; switch (m_eType) { case PICKUP_IN_SHOP: object->bPickupObjWithMessage = true; object->bOutOfStock = false; if (m_eModelIndex == MI_PICKUP_HEALTH || m_eModelIndex == MI_PICKUP_ADRENALINE) object->m_nCostValue = 0; else object->m_nCostValue = CostOfWeapon[CPickups::WeaponForModel(m_eModelIndex)]; break; case PICKUP_ON_STREET: case PICKUP_ONCE: case PICKUP_ONCE_TIMEOUT: case PICKUP_COLLECTABLE1: case PICKUP_MONEY: case PICKUP_MINE_INACTIVE: case PICKUP_MINE_ARMED: case PICKUP_NAUTICAL_MINE_INACTIVE: case PICKUP_NAUTICAL_MINE_ARMED: case PICKUP_FLOATINGPACKAGE: case PICKUP_ON_STREET_SLOW: object->bPickupObjWithMessage = false; object->bOutOfStock = false; break; case PICKUP_IN_SHOP_OUT_OF_STOCK: object->bPickupObjWithMessage = false; object->bOutOfStock = true; object->bRenderScorched = true; break; case PICKUP_FLOATINGPACKAGE_FLOATING: default: break; } return object; } bool CPickup::CanBePickedUp(CPlayerPed *player, int playerId) { assert(m_pObject != nil); bool cannotBePickedUp = (m_pObject->GetModelIndex() == MI_PICKUP_BODYARMOUR && player->m_fArmour > CWorld::Players[playerId].m_nMaxArmour - 0.2f) || (m_pObject->GetModelIndex() == MI_PICKUP_HEALTH && player->m_fHealth > CWorld::Players[playerId].m_nMaxHealth - 0.2f) || (m_pObject->GetModelIndex() == MI_PICKUP_BRIBE && player->m_pWanted->GetWantedLevel() == 0) || (m_pObject->GetModelIndex() == MI_PICKUP_KILLFRENZY && (CTheScripts::IsPlayerOnAMission() || CDarkel::FrenzyOnGoing() || !CGame::nastyGame)) || (m_eType == PICKUP_ASSET_REVENUE && m_fRevenue < 10.0f); return !cannotBePickedUp; } bool CPickup::Update(CPlayerPed *player, CVehicle *vehicle, int playerId) { bool result = false; float waterLevel; if (m_pObject) { m_pObject->GetMatrix().GetPosition() = m_vecPos; if (m_pExtraObject) m_pExtraObject->GetMatrix().GetPosition() = m_vecPos; } if (m_eType == PICKUP_ASSET_REVENUE) { uint32 timePassed = CTimer::GetTimeInMilliseconds() - m_nTimer; m_nTimer = CTimer::GetTimeInMilliseconds(); if (Distance(FindPlayerCoors(), m_vecPos) > 10.0f) m_fRevenue += float(timePassed * m_nMoneySpeed) / SQR(1200.0f); m_fRevenue = Min(m_fRevenue, m_nQuantity); m_pObject->m_nCostValue = m_fRevenue < 10 ? 0 : m_fRevenue; } if (m_bRemoved) { if (CTimer::GetTimeInMilliseconds() > m_nTimer) { // respawn pickup if we're far enough float dist = (FindPlayerCoors().x - m_vecPos.x) * (FindPlayerCoors().x - m_vecPos.x) + (FindPlayerCoors().y - m_vecPos.y) * (FindPlayerCoors().y - m_vecPos.y); if (dist > 100.0f || m_eType == PICKUP_IN_SHOP && dist > 2.4f) { m_pObject = GiveUsAPickUpObject(&m_pObject, &m_pExtraObject, -1, -1); if (m_pObject) { CWorld::Add(m_pObject); m_bRemoved = false; } } } return false; } if (!m_pObject) { GiveUsAPickUpObject(&m_pObject, &m_pExtraObject, -1, -1); if (m_pObject) CWorld::Add(m_pObject); if (m_pExtraObject) CWorld::Add(m_pExtraObject); } if (!m_pObject) return false; if (!IsMine()) { // let's check if we touched the pickup bool isPickupTouched = false; if (m_pObject->GetModelIndex() == MI_PICKUP_BRIBE) { if (vehicle != nil) { if (vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 2.0f)) isPickupTouched = true; } else { if (Abs(player->GetPosition().z - m_pObject->GetPosition().z) < 2.0f) { if ((player->GetPosition().x - m_pObject->GetPosition().x) * (player->GetPosition().x - m_pObject->GetPosition().x) + (player->GetPosition().y - m_pObject->GetPosition().y) * (player->GetPosition().y - m_pObject->GetPosition().y) < 1.8f) isPickupTouched = true; } } } else if (m_pObject->GetModelIndex() == MI_PICKUP_CAMERA) { if (vehicle != nil && vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 2.0f)) { isPickupTouched = true; } } else if (vehicle == nil) { if (Abs(player->GetPosition().z - m_pObject->GetPosition().z) < 2.0f) { if ((player->GetPosition().x - m_pObject->GetPosition().x) * (player->GetPosition().x - m_pObject->GetPosition().x) + (player->GetPosition().y - m_pObject->GetPosition().y) * (player->GetPosition().y - m_pObject->GetPosition().y) < 1.8f) isPickupTouched = true; } } if (isPickupTouched) { eWeaponType weaponType = CPickups::WeaponForModel(m_pObject->GetModelIndex()); if (weaponType < WEAPONTYPE_TOTALWEAPONS && CDarkel::FrenzyOnGoing()) { isPickupTouched = false; m_bWasControlMessageShown = false; } else if (weaponType < WEAPONTYPE_TOTALWEAPONS && weaponType != WEAPONTYPE_UNARMED) { uint32 slot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; eWeaponType plrWeaponSlot = FindPlayerPed()->GetWeapon(slot).m_eWeaponType; if (plrWeaponSlot != weaponType) { if (CStreaming::ms_aInfoForModel[m_pObject->GetModelIndex()].m_loadState == STREAMSTATE_LOADED) { if (plrWeaponSlot == WEAPONTYPE_UNARMED || (FindPlayerPed()->GetWeapon(slot).m_nAmmoTotal == 0 && !CWeaponInfo::IsWeaponSlotAmmoMergeable(slot))) { if (CTimer::GetTimeInMilliseconds() - FindPlayerPed()->m_nPadDownPressedInMilliseconds < 1500) { CPickups::PlayerOnWeaponPickup = 6; isPickupTouched = false; } } else { CPickups::PlayerOnWeaponPickup = 6; if (CWeaponInfo::IsWeaponSlotAmmoMergeable(slot)) { if (m_eType == PICKUP_ONCE_TIMEOUT || m_eType == PICKUP_ONCE || m_eType == PICKUP_ON_STREET) { ExtractAmmoFromPickup(player); FindPlayerPed()->GetWeapon(slot).Reload(); } } if (!m_bWasControlMessageShown) { switch (CPad::GetPad(0)->Mode) { case 0: case 1: CHud::SetHelpMessage(TheText.Get("PU_CF1"), false); break; case 2: CHud::SetHelpMessage(TheText.Get("PU_CF3"), false); break; case 3: CHud::SetHelpMessage(TheText.Get("PU_CF4"), false); break; default: break; } m_bWasControlMessageShown = true; } if (CollectPickupBuffer == 0) isPickupTouched = false; if (CTimer::GetTimeInMilliseconds() - FindPlayerPed()->m_nPadDownPressedInMilliseconds < 1500) isPickupTouched = false; } } else isPickupTouched = false; } } } else m_bWasControlMessageShown = false; // if we didn't then we've got nothing to do if (isPickupTouched && CanBePickedUp(player, playerId)) { if (m_pObject->GetModelIndex() != MI_PICKUP_PROPERTY && m_pObject->GetModelIndex() != MI_PICKUP_PROPERTY_FORSALE) CPad::GetPad(0)->StartShake(120, 100); eWeaponType weaponType = CPickups::WeaponForModel(m_pObject->GetModelIndex()); switch (m_eType) { case PICKUP_IN_SHOP: if (CWorld::Players[playerId].m_nMoney < CostOfWeapon[weaponType]) CGarages::TriggerMessage("PU_MONY", -1, 6000, -1); else { CWorld::Players[playerId].m_nMoney -= CostOfWeapon[weaponType]; if (!CPickups::GivePlayerGoodiesWithPickUpMI(m_pObject->GetModelIndex(), playerId)) { if (!player->DoesPlayerWantNewWeapon(weaponType, false)) break; player->GiveWeapon(weaponType, AmmoForWeapon[weaponType]); player->m_nSelectedWepSlot = player->GetWeaponSlot(weaponType); DMAudio.PlayFrontEndSound(SOUND_PICKUP_WEAPON_BOUGHT, m_pObject->GetModelIndex() - MI_GRENADE); } result = true; Remove(); } break; case PICKUP_ON_STREET: case PICKUP_ON_STREET_SLOW: if (!CPickups::GivePlayerGoodiesWithPickUpMI(m_pObject->GetModelIndex(), playerId)) { if (!player->DoesPlayerWantNewWeapon(weaponType, false)) break; if (weaponType != WEAPONTYPE_UNARMED) { player->GiveWeapon(weaponType, m_nQuantity != 0 ? m_nQuantity : (m_bWasAmmoCollected ? 0 : AmmoForWeapon_OnStreet[weaponType]), true); if (player->m_nSelectedWepSlot == player->GetWeaponSlot(WEAPONTYPE_UNARMED)) player->m_nSelectedWepSlot = player->GetWeaponSlot(weaponType); DMAudio.PlayFrontEndSound(SOUND_PICKUP_WEAPON, m_pObject->GetModelIndex() - MI_GRENADE); } else if (m_pObject->GetModelIndex() == MI_PICKUP_CAMERA && vehicle != nil) { DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); CPickups::bPickUpcamActivated = true; CPickups::pPlayerVehicle = FindPlayerVehicle(); CPickups::StaticCamCoors = m_pObject->GetPosition(); CPickups::StaticCamStartTime = CTimer::GetTimeInMilliseconds(); } } if (m_eType == PICKUP_ON_STREET) m_nTimer = CTimer::GetTimeInMilliseconds() + 30000; else if (m_eType == PICKUP_ON_STREET_SLOW) { if (MI_PICKUP_BRIBE == m_pObject->GetModelIndex()) m_nTimer = CTimer::GetTimeInMilliseconds() + 300000; else m_nTimer = CTimer::GetTimeInMilliseconds() + 720000; } result = true; GetRidOfObjects(); m_bRemoved = true; break; case PICKUP_ONCE: case PICKUP_ONCE_TIMEOUT: case PICKUP_ONCE_TIMEOUT_SLOW: if (!CPickups::GivePlayerGoodiesWithPickUpMI(m_pObject->GetModelIndex(), playerId)) { if (!player->DoesPlayerWantNewWeapon(weaponType, false)) { ExtractAmmoFromPickup(player); break; } if (weaponType != WEAPONTYPE_UNARMED) { player->GiveWeapon(weaponType, m_nQuantity != 0 ? m_nQuantity : (m_bWasAmmoCollected ? 0 : AmmoForWeapon[weaponType]), true); if (player->m_nSelectedWepSlot == player->GetWeaponSlot(WEAPONTYPE_UNARMED)) player->m_nSelectedWepSlot = player->GetWeaponSlot(weaponType); } if (MI_PICKUP_SAVEGAME != m_pObject->GetModelIndex()) DMAudio.PlayFrontEndSound(SOUND_PICKUP_WEAPON, m_pObject->GetModelIndex() - MI_GRENADE); } result = true; Remove(); break; case PICKUP_COLLECTABLE1: CWorld::Players[playerId].m_nCollectedPackages++; CWorld::Players[playerId].m_nMoney += 100; if (CWorld::Players[playerId].m_nCollectedPackages == CWorld::Players[playerId].m_nTotalPackages) { printf("All collectables have been picked up\n"); CGarages::TriggerMessage("CO_ALL", -1, 5000, -1); CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 100000; } else CGarages::TriggerMessage("CO_ONE", CWorld::Players[CWorld::PlayerInFocus].m_nCollectedPackages, 5000, CWorld::Players[CWorld::PlayerInFocus].m_nTotalPackages); result = true; Remove(); DMAudio.PlayFrontEndSound(SOUND_PICKUP_HIDDEN_PACKAGE, 0); break; case PICKUP_MONEY: CWorld::Players[playerId].m_nMoney += m_nQuantity; sprintf(gString, "$%d", m_nQuantity); #ifdef MONEY_MESSAGES CMoneyMessages::RegisterOne(m_vecPos + CVector(0.0f, 0.0f, 1.0f), gString, 0, 255, 0, 0.5f, 0.5f); #endif result = true; Remove(); DMAudio.PlayFrontEndSound(SOUND_PICKUP_MONEY, 0); player->Say(SOUND_PED_MUGGING); break; case PICKUP_ASSET_REVENUE: CWorld::Players[CWorld::PlayerInFocus].m_nMoney += m_fRevenue; m_fRevenue = 0.0f; DMAudio.PlayFrontEndSound(SOUND_PICKUP_MONEY, 0); break; case PICKUP_PROPERTY_LOCKED: if (!m_bWasControlMessageShown) { m_bWasControlMessageShown = true; CHud::SetHelpMessage(TheText.Get(m_sTextKey), false); } break; case PICKUP_PROPERTY_FORSALE: ModifyStringLabelForControlSetting(m_sTextKey); CMessages::InsertNumberInString(TheText.Get(m_sTextKey), m_nQuantity, 0, 0, 0, 0, 0, gUString); if (!CHud::IsHelpMessageBeingDisplayed()) CHud::SetHelpMessage(gUString, false); if (CollectPickupBuffer == 0) break; if (CTheScripts::IsPlayerOnAMission()) CHud::SetHelpMessage(TheText.Get("PROP_2"), true); else { if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney >= m_nQuantity) { CWorld::Players[CWorld::PlayerInFocus].m_nMoney -= m_nQuantity; CHud::SetHelpMessage(nil, true); result = true; Remove(); break; } CHud::SetHelpMessage(TheText.Get("PROP_1"), true); } break; default: break; } } } else { switch (m_eType) { case PICKUP_MINE_INACTIVE: if (vehicle != nil && !vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 2.0f)) { m_eType = PICKUP_MINE_ARMED; m_nTimer = CTimer::GetTimeInMilliseconds() + 10000; } break; case PICKUP_NAUTICAL_MINE_INACTIVE: { if (CWaterLevel::GetWaterLevel(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z + 5.0f, &waterLevel, false)) m_pObject->GetMatrix().GetPosition().z = waterLevel + 0.6f; m_pObject->GetMatrix().UpdateRW(); m_pObject->UpdateRwFrame(); bool touched = false; for (int32 i = CPools::GetVehiclePool()->GetSize()-1; i >= 0; i--) { CVehicle *vehicle = CPools::GetVehiclePool()->GetSlot(i); if (vehicle != nil && vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 1.5f)) { touched = true; #ifdef FIX_BUGS break; // added break here #endif } } if (!touched) { m_eType = PICKUP_NAUTICAL_MINE_ARMED; m_nTimer = CTimer::GetTimeInMilliseconds() + 10000; } break; } case PICKUP_NAUTICAL_MINE_ARMED: if (CWaterLevel::GetWaterLevel(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z + 5.0f, &waterLevel, false)) m_pObject->GetMatrix().GetPosition().z = waterLevel + 0.6f; m_pObject->GetMatrix().UpdateRW(); m_pObject->UpdateRwFrame(); // no break here case PICKUP_MINE_ARMED: { bool explode = false; if (CTimer::GetTimeInMilliseconds() > m_nTimer) explode = true; #ifdef FIX_BUGS else// added else here since vehicle lookup is useless #endif { for (int32 i = CPools::GetVehiclePool()->GetSize()-1; i >= 0; i--) { CVehicle *vehicle = CPools::GetVehiclePool()->GetSlot(i); if (vehicle != nil && vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 1.5f)) { explode = true; #ifdef FIX_BUGS break; // added break here #endif } } } if (explode) { CExplosion::AddExplosion(nil, nil, EXPLOSION_MINE, m_pObject->GetPosition(), 0); Remove(); } break; } case PICKUP_FLOATINGPACKAGE: m_pObject->m_vecMoveSpeed.z -= 0.01f * CTimer::GetTimeStep(); m_pObject->GetMatrix().GetPosition() += m_pObject->GetMoveSpeed() * CTimer::GetTimeStep(); m_pObject->GetMatrix().UpdateRW(); m_pObject->UpdateRwFrame(); if (CWaterLevel::GetWaterLevel(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z + 5.0f, &waterLevel, false) && waterLevel >= m_pObject->GetPosition().z) m_eType = PICKUP_FLOATINGPACKAGE_FLOATING; break; case PICKUP_FLOATINGPACKAGE_FLOATING: if (CWaterLevel::GetWaterLevel(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z + 5.0f, &waterLevel, false)) m_pObject->GetMatrix().GetPosition().z = waterLevel; m_pObject->GetMatrix().UpdateRW(); m_pObject->UpdateRwFrame(); if (vehicle != nil && vehicle->IsSphereTouchingVehicle(m_pObject->GetPosition().x, m_pObject->GetPosition().y, m_pObject->GetPosition().z, 2.0f)) { Remove(); result = true; DMAudio.PlayFrontEndSound(SOUND_PICKUP_FLOAT_PACKAGE, 0); } break; default: break; } } if (!m_bRemoved && (m_eType == PICKUP_ONCE_TIMEOUT || m_eType == PICKUP_ONCE_TIMEOUT_SLOW || m_eType == PICKUP_MONEY) && CTimer::GetTimeInMilliseconds() > m_nTimer) Remove(); return result; } void CPickup::ProcessGunShot(CVector *vec1, CVector *vec2) { CColLine line(*vec1, *vec2); if (m_pObject) { CColSphere sphere; sphere.radius = 4.0f; sphere.center = m_pObject->GetPosition(); if (CCollision::TestLineSphere(line, sphere)) { CExplosion::AddExplosion(nil, nil, EXPLOSION_MINE, m_pObject->GetPosition(), 0); CWorld::Remove(m_pObject); delete m_pObject; m_pObject = nil; m_bRemoved = true; m_eType = PICKUP_NONE; } } } void CPickup::GetRidOfObjects() { if (m_pObject) { CWorld::Remove(m_pObject); delete m_pObject; m_pObject = nil; } if (m_pExtraObject) { CWorld::Remove(m_pExtraObject); delete m_pExtraObject; m_pExtraObject = nil; } } void CPickups::Init(void) { NumMessages = 0; for (int i = 0; i < NUMPICKUPS; i++) { aPickUps[i].m_eType = PICKUP_NONE; aPickUps[i].m_nIndex = 1; aPickUps[i].m_pObject = nil; aPickUps[i].m_pExtraObject = nil; } for (int i = 0; i < NUMCOLLECTEDPICKUPS; i++) aPickUpsCollected[i] = 0; CollectedPickUpIndex = 0; } bool CPickups::TestForPickupsInBubble(CVector pos, float range) { for (int i = 0; i < NUMPICKUPS; i++) { if ((aPickUps[i].m_vecPos - pos).Magnitude() < range) return true; } return false; } bool CPickups::TryToMerge_WeaponType(CVector pos, eWeaponType weapon, uint8 type, uint32 quantity, bool unused) { for (int i = 0; i < NUMPICKUPS; i++) { if (aPickUps[i].m_eType == type && aPickUps[i].m_eModelIndex == ModelForWeapon(weapon)) if ((aPickUps[i].m_vecPos - pos).Magnitude() < 7.5f) { aPickUps[i].m_nQuantity += quantity; return true; } } return false; } bool CPickups::IsPickUpPickedUp(int32 pickupId) { for (int i = 0; i < NUMCOLLECTEDPICKUPS; i++) { if (pickupId == aPickUpsCollected[i]) { aPickUpsCollected[i] = 0; return true; } } return false; } void CPickups::PassTime(uint32 time) { for (int i = 0; i < NUMPICKUPS; i++) { if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].m_eType != PICKUP_ASSET_REVENUE) { if (aPickUps[i].m_nTimer <= time) aPickUps[i].m_nTimer = 0; else aPickUps[i].m_nTimer -= time; } } } int32 CPickups::GetActualPickupIndex(int32 index) { if (index == -1) return -1; // doesn't look nice if ((uint16)((index & 0xFFFF0000) >> 16) != aPickUps[(uint16)index].m_nIndex) return -1; return (uint16)index; } bool CPickups::GivePlayerGoodiesWithPickUpMI(int16 modelIndex, int playerIndex) { CPlayerPed *player; if (playerIndex <= 0) player = CWorld::Players[CWorld::PlayerInFocus].m_pPed; else player = CWorld::Players[playerIndex].m_pPed; if (modelIndex == MI_PICKUP_ADRENALINE) { player->m_bAdrenalineActive = true; player->m_nAdrenalineTime = CTimer::GetTimeInMilliseconds() + 20000; player->m_fCurrentStamina = player->m_fMaxStamina; DMAudio.PlayFrontEndSound(SOUND_PICKUP_ADRENALINE, 0); return true; } else if (modelIndex == MI_PICKUP_BODYARMOUR) { player->m_fArmour = CWorld::Players[playerIndex].m_nMaxArmour; DMAudio.PlayFrontEndSound(SOUND_PICKUP_ARMOUR, 0); return true; } else if (modelIndex == MI_PICKUP_INFO) { DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); return true; } else if (modelIndex == MI_PICKUP_HEALTH) { player->m_fHealth = CWorld::Players[playerIndex].m_nMaxHealth; DMAudio.PlayFrontEndSound(SOUND_PICKUP_HEALTH, 0); return true; } else if (modelIndex == MI_PICKUP_BONUS) { DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); return true; } else if (modelIndex == MI_PICKUP_BRIBE) { int32 level = Max(FindPlayerPed()->m_pWanted->GetWantedLevel() - 1, 0); player->SetWantedLevel(level); DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); return true; } else if (modelIndex == MI_PICKUP_KILLFRENZY) { DMAudio.PlayFrontEndSound(SOUND_PICKUP_BONUS, 0); return true; } return false; } void CPickups::RemovePickUp(int32 pickupIndex) { int32 index = GetActualPickupIndex(pickupIndex); if (index == -1) return; if (aPickUps[index].m_pObject) { CWorld::Remove(aPickUps[index].m_pObject); delete aPickUps[index].m_pObject; aPickUps[index].m_pObject = nil; } if (aPickUps[index].m_pExtraObject) { CWorld::Remove(aPickUps[index].m_pExtraObject); delete aPickUps[index].m_pExtraObject; aPickUps[index].m_pExtraObject = nil; } aPickUps[index].m_eType = PICKUP_NONE; aPickUps[index].m_bRemoved = true; } int32 CPickups::GenerateNewOne(CVector pos, uint32 modelIndex, uint8 type, uint32 quantity, uint32 rate, bool highPriority, char* pText) { bool bFreeFound = false; int32 slot = 0; if (type == PICKUP_FLOATINGPACKAGE || type == PICKUP_NAUTICAL_MINE_INACTIVE || highPriority) { for (slot = NUMPICKUPS-1; slot >= 0; slot--) { if (aPickUps[slot].m_eType == PICKUP_NONE) { bFreeFound = true; break; } } } if (!bFreeFound) { for (slot = 0; slot < NUMGENERALPICKUPS; slot++) { if (aPickUps[slot].m_eType == PICKUP_NONE) { bFreeFound = true; break; } } } if (!bFreeFound) { for (slot = 0; slot < NUMGENERALPICKUPS; slot++) { if (aPickUps[slot].m_eType == PICKUP_MONEY) break; } if (slot >= NUMGENERALPICKUPS) { for (slot = 0; slot < NUMGENERALPICKUPS; slot++) { if (aPickUps[slot].m_eType == PICKUP_ONCE_TIMEOUT || aPickUps[slot].m_eType == PICKUP_ONCE_TIMEOUT_SLOW) break; } if (slot >= NUMGENERALPICKUPS) return -1; aPickUps[slot].GetRidOfObjects(); } } if (slot >= NUMPICKUPS) return -1; aPickUps[slot].m_eType = type; aPickUps[slot].m_bRemoved = false; aPickUps[slot].m_nQuantity = quantity; aPickUps[slot].m_nMoneySpeed = rate; aPickUps[slot].m_fRevenue = 0.0f; aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds(); aPickUps[slot].m_bWasAmmoCollected = highPriority; aPickUps[slot].m_bWasControlMessageShown = false; if (type == PICKUP_ONCE_TIMEOUT) aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 20000; else if (type == PICKUP_ONCE_TIMEOUT_SLOW) aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 120000; else if (type == PICKUP_MONEY) aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 30000; else if (type == PICKUP_MINE_INACTIVE || type == PICKUP_MINE_ARMED) { aPickUps[slot].m_eType = PICKUP_MINE_INACTIVE; aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 1500; } else if (type == PICKUP_NAUTICAL_MINE_INACTIVE || type == PICKUP_NAUTICAL_MINE_ARMED) { aPickUps[slot].m_eType = PICKUP_NAUTICAL_MINE_INACTIVE; aPickUps[slot].m_nTimer = CTimer::GetTimeInMilliseconds() + 1500; } aPickUps[slot].m_eModelIndex = modelIndex; if (pText) strncpy(aPickUps[slot].m_sTextKey, pText, 8); else aPickUps[slot].m_sTextKey[0] = '\0'; aPickUps[slot].m_vecPos = pos; aPickUps[slot].m_pObject = aPickUps[slot].GiveUsAPickUpObject(&aPickUps[slot].m_pObject, &aPickUps[slot].m_pExtraObject, -1, -1); if (aPickUps[slot].m_pObject) CWorld::Add(aPickUps[slot].m_pObject); if (aPickUps[slot].m_pExtraObject) CWorld::Add(aPickUps[slot].m_pExtraObject); return GetNewUniquePickupIndex(slot); } int32 CPickups::GenerateNewOne_WeaponType(CVector pos, eWeaponType weaponType, uint8 type, uint32 quantity) { return GenerateNewOne(pos, ModelForWeapon(weaponType), type, quantity); } int32 CPickups::GetNewUniquePickupIndex(int32 slot) { if (aPickUps[slot].m_nIndex >= 0xFFFE) aPickUps[slot].m_nIndex = 1; else aPickUps[slot].m_nIndex++; return slot | (aPickUps[slot].m_nIndex << 16); } int32 CPickups::ModelForWeapon(eWeaponType weaponType) { return CWeaponInfo::GetWeaponInfo(weaponType)->m_nModelId; } eWeaponType CPickups::WeaponForModel(int32 model) { if (model == MI_PICKUP_BODYARMOUR) return WEAPONTYPE_ARMOUR; if (model == MI_PICKUP_HEALTH) return WEAPONTYPE_HEALTH; if (model == MI_PICKUP_ADRENALINE) return WEAPONTYPE_ARMOUR; if (model == -1) return WEAPONTYPE_UNARMED; return ((CWeaponModelInfo*)CModelInfo::GetModelInfo(model))->GetWeaponInfo(); } void CPickups::AddToCollectedPickupsArray(int32 index) { aPickUpsCollected[CollectedPickUpIndex++] = index | (aPickUps[index].m_nIndex << 16); if (CollectedPickUpIndex >= NUMCOLLECTEDPICKUPS) CollectedPickUpIndex = 0; } void CPickups::Update() { #ifdef FIX_BUGS // RIP speedrunning (solution from SA) if (CReplay::IsPlayingBack()) return; #endif #ifdef CAMERA_PICKUP if ( bPickUpcamActivated ) // taken from PS2 { float dist = Distance2D(StaticCamCoors, FindPlayerCoors()); float mult; if ( dist < 10.0f ) mult = 1.0f - (dist / 10.0f ); else mult = 0.0f; CVector pos = StaticCamCoors; pos.z += (pPlayerVehicle->GetColModel()->boundingBox.GetSize().z + 2.0f) * mult; if ( (CTimer::GetTimeInMilliseconds() - StaticCamStartTime) > 750 ) { TheCamera.SetCamPositionForFixedMode(pos, CVector(0.0f, 0.0f, 0.0f)); TheCamera.TakeControl(FindPlayerVehicle(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_SCRIPT); } if ( FindPlayerVehicle() != pPlayerVehicle || Distance(StaticCamCoors, FindPlayerCoors()) > 40.0f || ((CTimer::GetTimeInMilliseconds() - StaticCamStartTime) > 60000) ) { TheCamera.RestoreWithJumpCut(); bPickUpcamActivated = false; } } #endif if (CPad::GetPad(0)->CollectPickupJustDown()) CollectPickupBuffer = 6; else CollectPickupBuffer = Max(0, CollectPickupBuffer - 1); if (PlayerOnWeaponPickup) PlayerOnWeaponPickup = Max(0, PlayerOnWeaponPickup - 1); #define PICKUPS_FRAME_SPAN (6) #ifdef FIX_BUGS for (uint32 i = NUMGENERALPICKUPS * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN) / PICKUPS_FRAME_SPAN; i < NUMGENERALPICKUPS * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1) / PICKUPS_FRAME_SPAN; i++) { #else // BUG: this code can only reach 318 out of 320 pickups for (uint32 i = NUMGENERALPICKUPS / PICKUPS_FRAME_SPAN * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN); i < NUMGENERALPICKUPS / PICKUPS_FRAME_SPAN * (CTimer::GetFrameCounter() % PICKUPS_FRAME_SPAN + 1); i++) { #endif if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) AddToCollectedPickupsArray(i); } #undef PICKUPS_FRAME_SPAN for (uint32 i = NUMGENERALPICKUPS; i < NUMPICKUPS; i++) { if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].Update(FindPlayerPed(), FindPlayerVehicle(), CWorld::PlayerInFocus)) AddToCollectedPickupsArray(i); } } CPickup* CPickups::FindPickUpForThisObject(CEntity *object) { for (uint32 i = 0; i < NUMPICKUPS; i++) { if (aPickUps[i].m_eType != PICKUP_NONE && (aPickUps[i].m_pObject == object || aPickUps[i].m_pExtraObject == object)) { return &aPickUps[i]; } } return &aPickUps[0]; } void CPickups::DoPickUpEffects(CEntity *entity) { CPickup *pickup = FindPickUpForThisObject(entity); if (entity->GetModelIndex() == MI_PICKUP_KILLFRENZY) entity->bDoNotRender = CTheScripts::IsPlayerOnAMission() || CDarkel::FrenzyOnGoing() || !CGame::nastyGame; if (!entity->bDoNotRender) { float s = Sin((float)((CTimer::GetTimeInMilliseconds() + (uintptr)entity) & 0x7FF) * DEGTORAD(360.0f / 0x800)); float modifiedSin = 0.3f * (s + 1.0f); #ifdef FIX_BUGS int16 colorId = 0; #else int16 colorId; #endif bool doInnerGlow = false; bool doOuterGlow = true; if (entity->GetModelIndex() == MI_PICKUP_ADRENALINE || entity->GetModelIndex() == MI_PICKUP_CAMERA) { colorId = WEAPONTYPE_TOTALWEAPONS; doInnerGlow = true; doOuterGlow = false; } else if (entity->GetModelIndex() == MI_PICKUP_BODYARMOUR) { colorId = WEAPONTYPE_ARMOUR; } else if (entity->GetModelIndex() == MI_PICKUP_BRIBE) { doInnerGlow = true; doOuterGlow = false; } else if (entity->GetModelIndex() == MI_PICKUP_INFO || entity->GetModelIndex() == MI_PICKUP_KILLFRENZY) { doInnerGlow = true; doOuterGlow = false; } else if (entity->GetModelIndex() == MI_PICKUP_HEALTH || entity->GetModelIndex() == MI_PICKUP_BONUS) { colorId = WEAPONTYPE_HEALTH; doInnerGlow = true; doOuterGlow = false; } else if (entity->GetModelIndex() == MI_PICKUP_PROPERTY) { doInnerGlow = true; doOuterGlow = false; } else if (entity->GetModelIndex() == MI_PICKUP_PROPERTY_FORSALE) { doInnerGlow = true; doOuterGlow = false; } else if (entity->GetModelIndex() == MI_PICKUP_REVENUE) { doInnerGlow = true; doOuterGlow = false; } else if (entity->GetModelIndex() == MI_PICKUP_SAVEGAME) { doInnerGlow = true; doOuterGlow = false; } else if (entity->GetModelIndex() == MI_PICKUP_CLOTHES) { colorId = WEAPONTYPE_TOTALWEAPONS; doOuterGlow = false; doInnerGlow = true; } else colorId = WeaponForModel(entity->GetModelIndex()); const CVector& pos = pickup->m_vecPos; if (doOuterGlow) { bool corona1 = false; bool corona2 = false; int timerVal = (CTimer::GetTimeInMilliseconds() >> 9) & 7; if (timerVal < 3) corona1 = false; else if (timerVal == 3) corona1 = (CGeneral::GetRandomNumber() & 3) != 0; else corona1 = true; timerVal = (timerVal - 1) & 7; if (timerVal < 3) corona2 = false; else if (timerVal == 3) corona2 = (CGeneral::GetRandomNumber() & 3) != 0; else corona2 = true; if (((CObject*)entity)->bAmmoCollected) { corona2 = false; corona1 = false; } if (corona1) { CCoronas::RegisterCorona((uintptr)entity, aPickupColors[colorId].r * 0.45f, aPickupColors[colorId].g * 0.45f, aPickupColors[colorId].b * 0.45f, 255, pos, 0.76f, 65.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f, false, -0.4f); CShadows::StoreStaticShadow((uintptr)entity, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, aPickupColors[colorId].r * 0.3f, aPickupColors[colorId].g * 0.3f, aPickupColors[colorId].b * 0.3f, 4.0f, 1.0f, 40.0f, false, 0.0f); float radius = (CGeneral::GetRandomNumber() & 0xF) * 0.1f + 3.0f; CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), radius, aPickupColors[colorId].r / 256.0f, aPickupColors[colorId].g / 256.0f, aPickupColors[colorId].b / 256.0f, CPointLights::FOG_NONE, true); } else CCoronas::RegisterCorona((uintptr)entity, 0, 0, 0, 255, pos, 0.57f, 65.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); if (corona2) { CCoronas::RegisterCorona( (uintptr)entity + 1, aPickupColors[colorId].r * 0.55f, aPickupColors[colorId].g * 0.55f, aPickupColors[colorId].b * 0.55f, 255, pos, 0.6f, 65.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f, false, -0.4f); if (!corona1) CShadows::StoreStaticShadow((uintptr)entity, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, aPickupColors[colorId].r * 0.25f, aPickupColors[colorId].g * 0.25f, aPickupColors[colorId].b * 0.25f, 4.0f, 1.0f, 40.0f, false, 0.0f); } else CCoronas::RegisterCorona((uintptr)entity + 1, 0, 0, 0, 255, pos, 0.45f, 65.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } CObject *object = (CObject*)entity; if (object->bPickupObjWithMessage || object->bOutOfStock || object->m_nBonusValue || object->m_nCostValue) { float dist = Distance2D(pos, TheCamera.GetPosition()); const float MAXDIST = 14.0f; if (dist < MAXDIST && NumMessages < NUMPICKUPMESSAGES) { RwV3d vecOut; float fDistX, fDistY; if (CSprite::CalcScreenCoors(entity->GetPosition() + CVector(0.0f, 0.0f, 0.7f), &vecOut, &fDistX, &fDistY, true)) { aMessages[NumMessages].m_pos.x = vecOut.x; aMessages[NumMessages].m_pos.y = vecOut.y; aMessages[NumMessages].m_dist.x = fDistX; aMessages[NumMessages].m_dist.y = fDistY; aMessages[NumMessages].m_weaponType = WeaponForModel(entity->GetModelIndex()); aMessages[NumMessages].m_color.red = aPickupColors[colorId].r; aMessages[NumMessages].m_color.green = aPickupColors[colorId].g; aMessages[NumMessages].m_color.blue = aPickupColors[colorId].b; aMessages[NumMessages].m_color.alpha = (1.0f - dist / MAXDIST) * 128.0f; aMessages[NumMessages].m_bOutOfStock = object->bOutOfStock; aMessages[NumMessages].m_quantity = object->m_nBonusValue; aMessages[NumMessages].money = object->m_nCostValue; NumMessages++; } } } uint32 model = entity->GetModelIndex(); CColModel *colModel = entity->GetColModel(); CVector colLength = colModel->boundingBox.max - colModel->boundingBox.min; float maxDimension = Max(colLength.x, Max(colLength.y, colLength.z)); float scale = (Max(1.f, 1.2f / maxDimension) - 1.0f) * 0.6f + 1.0f; if (model == MI_MINIGUN || model == MI_MINIGUN2) scale = 1.2f; entity->GetMatrix().SetRotateZOnlyScaled((float)(CTimer::GetTimeInMilliseconds() & 0x7FF) * DEGTORAD(360.0f / 0x800), scale); if (entity->GetModelIndex() == MI_MINIGUN2) { CMatrix matrix1; CMatrix matrix2; // unused entity->SetPosition(pickup->m_vecPos); matrix1.SetRotateX(0.0f); matrix1.Rotate(DEGTORAD(4.477f), DEGTORAD(-29.731f), DEGTORAD(-1.064f)); matrix1.Translate(CVector(0.829f, -0.001f, 0.226f)); entity->GetMatrix() *= matrix1; } if (doOuterGlow) { CVector scale(0.0f, 0.0f, 0.0f); if (colLength.x == maxDimension) scale.x = colLength.x; else if (colLength.y == maxDimension) scale.y = colLength.y; else scale.z = colLength.z; for (int i = 0; i < 4; i++) { CVector pos = entity->GetMatrix() * (scale * ((float)i / 3.0f)); CCoronas::RegisterCorona( (uintptr)entity + 8 + i, aPickupColors[colorId].r * 0.15f, aPickupColors[colorId].g * 0.15f, aPickupColors[colorId].b * 0.15f, 255, pos, 1.0f, 65.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f, false, -0.5f); } } if (doInnerGlow) CCoronas::RegisterCorona( #ifdef FIX_BUGS (uintptr)entity + 8 + 4, #else (uintptr)entity + 9, #endif 126, 69, 121, 255, entity->GetPosition(), 1.2f, 50.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); } } void CPickups::DoMineEffects(CEntity *entity) { const CVector &pos = entity->GetPosition(); float dist = Distance(pos, TheCamera.GetPosition()); const float MAXDIST = 20.0f; if (dist < MAXDIST) { float s = Sin((float)((CTimer::GetTimeInMilliseconds() + (uintptr)entity) & 0x1FF) * DEGTORAD(360.0f / 0x200)); int32 red = (MAXDIST - dist) * (0.5f * s + 0.5f) / MAXDIST * 64.0f; CShadows::StoreStaticShadow((uintptr)entity, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, red, 0, 0, 4.0f, 1.0f, 40.0f, false, 0.0f); CCoronas::RegisterCorona((uintptr)entity, red, 0, 0, 255, pos, 0.6f, 60.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } entity->GetMatrix().SetRotateZOnly((float)(CTimer::GetTimeInMilliseconds() & 0x3FF) * DEGTORAD(360.0f / 0x400)); } void CPickups::DoMoneyEffects(CEntity *entity) { const CVector &pos = entity->GetPosition(); float dist = Distance(pos, TheCamera.GetPosition()); const float MAXDIST = 20.0f; if (dist < MAXDIST) { float s = Sin((float)((CTimer::GetTimeInMilliseconds() + (uintptr)entity) & 0x3FF) * DEGTORAD(360.0f / 0x400)); int32 green = (MAXDIST - dist) * (0.2f * s + 0.3f) / MAXDIST * 64.0f; CShadows::StoreStaticShadow((uintptr)entity, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, 0, green, 0, 4.0f, 1.0f, 40.0f, false, 0.0f); CCoronas::RegisterCorona((uintptr)entity, 0, green, 0, 255, pos, 0.4f, 40.0f, CCoronas::TYPE_RING, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } entity->GetMatrix().SetRotateZOnly((float)(CTimer::GetTimeInMilliseconds() & 0x7FF) * DEGTORAD(360.0f / 0x800)); } void CPickups::DoCollectableEffects(CEntity *entity) { const CVector &pos = entity->GetPosition(); float dist = Distance(pos, TheCamera.GetPosition()); const float MAXDIST = 14.0f; if (dist < MAXDIST) { float s = Sin((float)((CTimer::GetTimeInMilliseconds() + (uintptr)entity) & 0x7FF) * DEGTORAD(360.0f / 0x800)); int32 color = (MAXDIST - dist) * (0.5f * s + 0.5f) / MAXDIST * 255.0f; CShadows::StoreStaticShadow((uintptr)entity, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 2.0f, 0.0f, 0.0f, -2.0f, 0, color, color, color, 4.0f, 1.0f, 40.0f, false, 0.0f); CCoronas::RegisterCorona((uintptr)entity, color, color, color, 255, pos, 0.6f, 40.0f, CCoronas::TYPE_HEX, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } entity->GetMatrix().SetRotateZOnly((float)(CTimer::GetTimeInMilliseconds() & 0xFFF) * DEGTORAD(360.0f / 0x1000)); } void CPickups::RenderPickUpText() { wchar *strToPrint; for (int32 i = 0; i < NumMessages; i++) { if (aMessages[i].money != 0) { sprintf(gString, "$%d", aMessages[i].money); AsciiToUnicode(gString, gUString); strToPrint = gUString; } else { switch (aMessages[i].m_quantity) // could use some enum maybe { case 0: if (aMessages[i].m_weaponType == WEAPONTYPE_HEALTH || aMessages[i].m_weaponType == WEAPONTYPE_ARMOUR) { strToPrint = nil; } else { if (aMessages[i].m_bOutOfStock) strToPrint = TheText.Get("STOCK"); else { sprintf(gString, "$%d", CostOfWeapon[aMessages[i].m_weaponType]); AsciiToUnicode(gString, gUString); strToPrint = gUString; } } break; case 1: strToPrint = TheText.Get("OUTFT1"); break; case 2: strToPrint = TheText.Get("OUTFT2"); break; case 3: strToPrint = TheText.Get("OUTFT3"); break; case 4: strToPrint = TheText.Get("OUTFT4"); break; case 5: strToPrint = TheText.Get("OUTFT5"); break; case 6: strToPrint = TheText.Get("OUTFT6"); break; case 7: strToPrint = TheText.Get("OUTFT7"); break; case 8: strToPrint = TheText.Get("OUTFT8"); break; case 9: strToPrint = TheText.Get("OUTFT9"); break; case 10: strToPrint = TheText.Get("OUTFT10"); break; case 11: strToPrint = TheText.Get("OUTFT11"); break; case 12: strToPrint = TheText.Get("OUTFT12"); break; case 13: strToPrint = TheText.Get("OUTFT13"); break; default: break; } } if (strToPrint == nil) continue; CFont::SetPropOn(); CFont::SetBackgroundOff(); #ifdef FIX_BUGS const float MAX_SCALE = SCREEN_WIDTH / DEFAULT_SCREEN_WIDTH; #else float MAX_SCALE = RsGlobal.width / DEFAULT_SCREEN_WIDTH; #endif float fScaleY = aMessages[i].m_dist.y / 30.0f; if (fScaleY > MAX_SCALE) fScaleY = MAX_SCALE; float fScaleX = aMessages[i].m_dist.x / 30.0f; if (fScaleX > MAX_SCALE) fScaleX = MAX_SCALE; CFont::SetScale(fScaleX, fScaleY); // this shouldn't be scaled CFont::SetCentreOn(); CFont::SetCentreSize(SCREEN_WIDTH); CFont::SetJustifyOff(); CFont::SetColor(CRGBA(aMessages[i].m_color.red, aMessages[i].m_color.green, aMessages[i].m_color.blue, aMessages[i].m_color.alpha)); CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_STANDARD); CFont::PrintString(aMessages[i].m_pos.x, aMessages[i].m_pos.y, strToPrint); } NumMessages = 0; } void CPickups::CreateSomeMoney(CVector pos, int money) { bool found; int pickupCount = Min(money / 20 + 1, 7); int moneyPerPickup = money / pickupCount; for (int i = 0; i < pickupCount; i++) { // (CGeneral::GetRandomNumber() % 256) * PI / 128 gives a float up to something TWOPI-ish. pos.x += 1.5f * Sin((CGeneral::GetRandomNumber() % 256) * PI / 128); pos.y += 1.5f * Cos((CGeneral::GetRandomNumber() % 256) * PI / 128); pos.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &found) + 0.5f; if (found) { CPickups::GenerateNewOne(CVector(pos.x, pos.y, pos.z), MI_MONEY, PICKUP_MONEY, moneyPerPickup + (CGeneral::GetRandomNumber() & 3)); } } } void CPickups::RemoveAllPickupsOfACertainWeaponGroupWithNoAmmo(eWeaponType weaponType) { uint32 weaponSlot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; if (CWeaponInfo::IsWeaponSlotAmmoMergeable(weaponSlot)) { for (int slot = 0; slot < NUMPICKUPS; slot++) { if (aPickUps[slot].m_eType == PICKUP_ONCE || aPickUps[slot].m_eType == PICKUP_ONCE_TIMEOUT || aPickUps[slot].m_eType == PICKUP_ONCE_TIMEOUT_SLOW) { if (aPickUps[slot].m_pObject) { if (CWeaponInfo::GetWeaponInfo(WeaponForModel(aPickUps[slot].m_pObject->GetModelIndex()))->m_nWeaponSlot == weaponSlot && aPickUps[slot].m_nQuantity == 0) { CWorld::Remove(aPickUps[slot].m_pObject); delete aPickUps[slot].m_pObject; aPickUps[slot].m_bRemoved = true; aPickUps[slot].m_pObject = nil; aPickUps[slot].m_eType = PICKUP_NONE; } } } } } } void CPickups::DetonateMinesHitByGunShot(CVector *vec1, CVector *vec2) { for (int i = 0; i < NUMGENERALPICKUPS; i++) { if (aPickUps[i].m_eType == PICKUP_NAUTICAL_MINE_ARMED) aPickUps[i].ProcessGunShot(vec1, vec2); } } void CPickups::RemoveUnnecessaryPickups(const CVector& center, float radius) { for (int i = 0; i < NUMPICKUPS; i++) { if (aPickUps[i].m_eType == PICKUP_ONCE_TIMEOUT || aPickUps[i].m_eType == PICKUP_MONEY) { if (Distance(center, aPickUps[i].m_vecPos) < radius) { aPickUps[i].GetRidOfObjects(); aPickUps[i].m_bRemoved = true; aPickUps[i].m_eType = PICKUP_NONE; } } } } void CPickups::Load(uint8 *buf, uint32 size) { INITSAVEBUF for (int32 i = 0; i < NUMPICKUPS; i++) { aPickUps[i] = ReadSaveBuf(buf); if (aPickUps[i].m_eType != PICKUP_NONE) { if (aPickUps[i].m_pObject != nil) aPickUps[i].m_pObject = CPools::GetObjectPool()->GetSlot((uintptr)aPickUps[i].m_pObject - 1); if (aPickUps[i].m_pExtraObject != nil) aPickUps[i].m_pExtraObject = CPools::GetObjectPool()->GetSlot((uintptr)aPickUps[i].m_pExtraObject - 1); } } CollectedPickUpIndex = ReadSaveBuf(buf); ReadSaveBuf(buf); NumMessages = 0; for (uint16 i = 0; i < NUMCOLLECTEDPICKUPS; i++) aPickUpsCollected[i] = ReadSaveBuf(buf); VALIDATESAVEBUF(size) } void CPickups::Save(uint8 *buf, uint32 *size) { *size = sizeof(aPickUps); *size += sizeof(uint16) + sizeof(uint16) + sizeof(aPickUpsCollected); INITSAVEBUF for (int32 i = 0; i < NUMPICKUPS; i++) { CPickup *buf_pickup = WriteSaveBuf(buf, aPickUps[i]); if (buf_pickup->m_eType != PICKUP_NONE) { if (buf_pickup->m_pObject != nil) buf_pickup->m_pObject = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(buf_pickup->m_pObject) + 1); if (buf_pickup->m_pExtraObject != nil) buf_pickup->m_pExtraObject = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(buf_pickup->m_pExtraObject) + 1); } } WriteSaveBuf(buf, CollectedPickUpIndex); WriteSaveBuf(buf, (uint16)0); // possibly was NumMessages for (uint16 i = 0; i < NUMCOLLECTEDPICKUPS; i++) WriteSaveBuf(buf, aPickUpsCollected[i]); VALIDATESAVEBUF(*size) } void CPacManPickup::Update() { } int32 CollectGameState; int16 ThingsToCollect; CPacManPickup CPacManPickups::aPMPickUps[NUMPACMANPICKUPS]; CVector CPacManPickups::LastPickUpCoors; int32 CPacManPickups::PillsEatenInRace; bool CPacManPickups::bPMActive; void CPacManPickups::Init() { } void CPacManPickups::Update() { } void CPacManPickups::GeneratePMPickUps(CVector pos, float scrambleMult, int16 count, uint8 type) { } // diablo porn mission pickups static const CVector aRacePoints1[] = { CVector(913.62219f, -155.13692f, 4.9699469f), CVector(913.92401f, -124.12943f, 4.9692569f), CVector(913.27899f, -93.524231f, 7.4325991f), CVector(912.60852f, -63.15905f, 7.4533591f), CVector(934.22144f, -42.049122f, 7.4511471f), CVector(0.0f, 0.0f, 0.0f), }; void CPacManPickups::GeneratePMPickUpsForRace(int32 race) { } void CPacManPickups::GenerateOnePMPickUp(CVector pos) { } void CPacManPickups::Render() { } void CPacManPickups::ClearPMPickUps() { } void CPacManPickups::StartPacManRace(int32 race) { } void CPacManPickups::StartPacManRecord() { } uint32 CPacManPickups::QueryPowerPillsEatenInRace() { return 0; } void CPacManPickups::ResetPowerPillsEatenInRace() { } void CPacManPickups::CleanUpPacManStuff() { } void CPacManPickups::StartPacManScramble(CVector pos, float scrambleMult, int16 count) { } uint32 CPacManPickups::QueryPowerPillsCarriedByPlayer() { return 0; } void CPacManPickups::ResetPowerPillsCarriedByPlayer() { } void CPed::CreateDeadPedMoney(void) { if (!CGame::nastyGame) return; int mi = GetModelIndex(); if ((mi >= MI_COP && mi <= MI_FIREMAN) || (CharCreatedBy == MISSION_CHAR && !bMoneyHasBeenGivenByScript) || bInVehicle) return; int money = m_nPedMoney; if (money < 10) return; CVector pickupPos = GetPosition(); CPickups::CreateSomeMoney(pickupPos, money); m_nPedMoney = 0; } void CPed::CreateDeadPedWeaponPickups(void) { CVector pickupPos; if (bInVehicle) return; for(int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { eWeaponType weapon = GetWeapon(i).m_eWeaponType; int weaponAmmo = GetWeapon(i).m_nAmmoTotal; if (weapon == WEAPONTYPE_UNARMED || weapon == WEAPONTYPE_DETONATOR || (weaponAmmo == 0 && !GetWeapon(i).IsTypeMelee())) continue; int quantity = Min(weaponAmmo, AmmoForWeapon_OnStreet[weapon] / 2); CreateDeadPedPickupCoors(&pickupPos.x, &pickupPos.y, &pickupPos.z); pickupPos.z += 0.3f; if (!CPickups::TryToMerge_WeaponType(pickupPos, weapon, PICKUP_ONCE_TIMEOUT, quantity, false)) { CPickups::GenerateNewOne_WeaponType(pickupPos, weapon, PICKUP_ONCE_TIMEOUT, Min(weaponAmmo, quantity)); } } ClearWeapons(); } void CPed::CreateDeadPedPickupCoors(float *x, float *y, float *z) { bool found = false; CVector pickupPos; #define NUMBER_OF_ATTEMPTS 32 for (int i = 0; i < NUMBER_OF_ATTEMPTS; i++) { pickupPos = GetPosition(); pickupPos.x = 1.5f * Sin((CGeneral::GetRandomNumber() % 256)/256.0f * TWOPI) + GetPosition().x; pickupPos.y = 1.5f * Cos((CGeneral::GetRandomNumber() % 256)/256.0f * TWOPI) + GetPosition().y; pickupPos.z = CWorld::FindGroundZFor3DCoord(pickupPos.x, pickupPos.y, pickupPos.z, &found) + 0.5f; if (!found) continue; CVector pedPos = GetPosition(); pedPos.z += 0.3f; CVector pedToPickup = pickupPos - pedPos; float distance = pedToPickup.Magnitude(); // outer edge of pickup distance = (distance + 0.4f) / distance; CVector pickupPos2 = pedPos; pickupPos2 += distance * pedToPickup; if ((pickupPos - FindPlayerCoors()).Magnitude2D() > 2.0f || i > NUMBER_OF_ATTEMPTS / 2) { if (i > NUMBER_OF_ATTEMPTS / 2 || !CPickups::TestForPickupsInBubble(pickupPos, 1.3f)) { if (CWorld::GetIsLineOfSightClear(pickupPos2, pedPos, true, i < NUMBER_OF_ATTEMPTS / 2, false, i < NUMBER_OF_ATTEMPTS / 2, false, false, false)) { if (i > NUMBER_OF_ATTEMPTS / 2 || !CWorld::TestSphereAgainstWorld(pickupPos, 1.2f, nil, false, true, false, false, false, false)) { *x = pickupPos.x; *y = pickupPos.y; *z = pickupPos.z; return; } } } } } *x = GetPosition().x; *y = GetPosition().y; *z = GetPosition().z + 0.4f; #undef NUMBER_OF_ATTEMPTS } ================================================ FILE: src/control/Pickups.h ================================================ #pragma once #include "Weapon.h" enum ePickupType { PICKUP_NONE = 0, PICKUP_IN_SHOP, PICKUP_ON_STREET, PICKUP_ONCE, PICKUP_ONCE_TIMEOUT, PICKUP_ONCE_TIMEOUT_SLOW, PICKUP_COLLECTABLE1, PICKUP_IN_SHOP_OUT_OF_STOCK, PICKUP_MONEY, PICKUP_MINE_INACTIVE, PICKUP_MINE_ARMED, PICKUP_NAUTICAL_MINE_INACTIVE, PICKUP_NAUTICAL_MINE_ARMED, PICKUP_FLOATINGPACKAGE, PICKUP_FLOATINGPACKAGE_FLOATING, PICKUP_ON_STREET_SLOW, PICKUP_ASSET_REVENUE, PICKUP_PROPERTY_LOCKED, PICKUP_PROPERTY_FORSALE, PICKUP_NUMOFTYPES }; class CEntity; class CObject; class CVehicle; class CPlayerPed; class CPickup { public: CVector m_vecPos; float m_fRevenue; CObject *m_pObject; CObject *m_pExtraObject; uint32 m_nQuantity; uint32 m_nTimer; uint16 m_nMoneySpeed; int16 m_eModelIndex; uint16 m_nIndex; char m_sTextKey[8]; uint8 m_eType; bool m_bRemoved; uint8 m_bWasAmmoCollected:1; uint8 m_bWasControlMessageShown:1; CObject *GiveUsAPickUpObject(CObject **object, CObject **extraObject, int32 handle, int32 extraHandle); bool Update(CPlayerPed *player, CVehicle *vehicle, int playerId); void GetRidOfObjects(); void ExtractAmmoFromPickup(CPlayerPed *player); void ProcessGunShot(CVector *vec1, CVector *vec2); private: inline bool IsMine() { return m_eType >= PICKUP_MINE_INACTIVE && m_eType <= PICKUP_FLOATINGPACKAGE_FLOATING; } inline bool CanBePickedUp(CPlayerPed *player, int playerId); inline void Remove(); }; VALIDATE_SIZE(CPickup, 0x1C); struct tPickupMessage { CVector2D m_pos; eWeaponType m_weaponType; CVector2D m_dist; CRGBA m_color; uint8 m_bOutOfStock; uint8 m_quantity; uint16 money; }; class CPickups { static int32 aPickUpsCollected[NUMCOLLECTEDPICKUPS]; static int16 CollectedPickUpIndex; static int16 NumMessages; static tPickupMessage aMessages[NUMPICKUPMESSAGES]; public: static int32 PlayerOnWeaponPickup; static void Init(); static void Update(); static void RenderPickUpText(); static void DoCollectableEffects(CEntity *ent); static void DoMoneyEffects(CEntity *ent); static void DoMineEffects(CEntity *ent); static void DoPickUpEffects(CEntity *ent); static int32 GenerateNewOne(CVector pos, uint32 modelIndex, uint8 type, uint32 quantity, uint32 rate = 0, bool highPriority = false, char* pText = nil); static int32 GenerateNewOne_WeaponType(CVector pos, eWeaponType weaponType, uint8 type, uint32 quantity); static void RemovePickUp(int32 pickupIndex); static void AddToCollectedPickupsArray(int32 index); static bool IsPickUpPickedUp(int32 pickupId); static int32 ModelForWeapon(eWeaponType weaponType); static enum eWeaponType WeaponForModel(int32 model); static int32 GetActualPickupIndex(int32 index); static int32 GetNewUniquePickupIndex(int32 slot); static void PassTime(uint32 time); static bool GivePlayerGoodiesWithPickUpMI(int16 modelIndex, int playerIndex); static bool TestForPickupsInBubble(CVector pos, float range); static bool TryToMerge_WeaponType(CVector pos, eWeaponType weapon, uint8 type, uint32 quantity, bool unused); static void CreateSomeMoney(CVector, int); static void DetonateMinesHitByGunShot(CVector *vec1, CVector *vec2); static void RemoveUnnecessaryPickups(const CVector& center, float radius); static void Load(uint8 *buf, uint32 size); static void Save(uint8 *buf, uint32 *size); static CPickup aPickUps[NUMPICKUPS]; // unused static bool bPickUpcamActivated; static CVehicle *pPlayerVehicle; static CVector StaticCamCoors; static uint32 StaticCamStartTime; static void RemoveAllPickupsOfACertainWeaponGroupWithNoAmmo(eWeaponType); static CPickup *FindPickUpForThisObject(CEntity*); }; extern uint16 AmmoForWeapon[WEAPONTYPE_TOTALWEAPONS + 1]; extern uint16 AmmoForWeapon_OnStreet[WEAPONTYPE_TOTALWEAPONS + 1]; extern uint16 CostOfWeapon[WEAPONTYPE_TOTALWEAPONS + 3]; extern int32 CollectPickupBuffer; enum ePacmanPickupType { PACMAN_NONE, PACMAN_SCRAMBLE, PACMAN_RACE, }; class CPacManPickup { public: CVector m_vecPosn; CObject *m_pObject; uint8 m_eType; void Update(); }; class CPacManPickups { friend class CPacManPickup; static CPacManPickup aPMPickUps[NUMPACMANPICKUPS]; static CVector LastPickUpCoors; static int PillsEatenInRace; static bool bPMActive; public: static void Init(void); static void Update(void); static void GeneratePMPickUps(CVector, float, int16, uint8); static void GeneratePMPickUpsForRace(int32); static void GenerateOnePMPickUp(CVector); static void Render(void); static void StartPacManRace(int32); static void StartPacManRecord(void); static uint32 QueryPowerPillsEatenInRace(void); static void ResetPowerPillsEatenInRace(void); static void ClearPMPickUps(void); static void CleanUpPacManStuff(void); static void StartPacManScramble(CVector, float, int16); static uint32 QueryPowerPillsCarriedByPlayer(void); static void ResetPowerPillsCarriedByPlayer(void); }; ================================================ FILE: src/control/PowerPoints.cpp ================================================ #include "common.h" #include "PowerPoints.h" // Some cut beta feature void CPowerPoint::Update() {} void CPowerPoints::Init() {} void CPowerPoints::Update() {} void CPowerPoints::GenerateNewOne(float, float, float, float, float, float, uint8) {} void CPowerPoints::Save(uint8**, uint32*) {} void CPowerPoints::Load(uint8*, uint32) {} ================================================ FILE: src/control/PowerPoints.h ================================================ #pragma once enum { POWERPOINT_NONE = 0, POWERPOINT_HEALTH, POWERPOINT_HIDEOUT_INDUSTRIAL, POWERPOINT_HIDEOUT_COMMERCIAL, POWERPOINT_HIDEOUT_SUBURBAN }; class CPowerPoint { public: void Update(); }; class CPowerPoints { public: static void Init(); static void Update(); static void GenerateNewOne(float, float, float, float, float, float, uint8); static void Save(uint8**, uint32*); static void Load(uint8*, uint32); }; ================================================ FILE: src/control/Record.cpp ================================================ #include "common.h" #include "Record.h" #include "FileMgr.h" #include "Pad.h" #include "Pools.h" #include "Streaming.h" #include "Timer.h" #include "VehicleModelInfo.h" #include "World.h" uint16 CRecordDataForGame::RecordingState; void CRecordDataForGame::Init(void) { RecordingState = STATE_NONE; } void CRecordDataForGame::SaveOrRetrieveDataForThisFrame(void) { } uint8* CRecordDataForGame::PackCurrentPadValues(uint8* buf, CControllerState* os, CControllerState* ns) { return nil; } uint8* CRecordDataForGame::UnPackCurrentPadValues(uint8* buf, uint8 total, CControllerState* state) { return nil; } uint16 CRecordDataForGame::CalcGameChecksum(void) { return 0; } uint8 CRecordDataForChase::Status; void CRecordDataForChase::Init(void) { Status = STATE_NONE; } void CRecordDataForChase::SaveOrRetrieveDataForThisFrame(void) { } void CRecordDataForChase::SaveOrRetrieveCarPositions(void) { } void CRecordDataForChase::StoreInfoForCar(CAutomobile* pCar, CCarStateEachFrame* pState) { } void CRecordDataForChase::RestoreInfoForMatrix(CMatrix& matrix, CCarStateEachFrame* pState) { } void CRecordDataForChase::RestoreInfoForCar(CAutomobile* pCar, CCarStateEachFrame* pState, bool stop) { } void CRecordDataForChase::ProcessControlCars(void) { } bool CRecordDataForChase::ShouldThisPadBeLeftAlone(uint8 pad) { return false; } void CRecordDataForChase::GiveUsACar(int32 mi, CVector pos, float angle, CAutomobile** ppCar, uint8 colour1, uint8 colour2) { } void RemoveUnusedCollision(void) { } void CRecordDataForChase::StartChaseScene(float startTime) { } void CRecordDataForChase::CleanUpChaseScene(void) { } void CRecordDataForChase::SetUpCarsForChaseScene(void) { } void CRecordDataForChase::CleanUpCarsForChaseScene(void) { } void CRecordDataForChase::RemoveCarFromChase(int32 i) { } CVehicle* CRecordDataForChase::TurnChaseCarIntoScriptCar(int32 i) { return nil; } ================================================ FILE: src/control/Record.h ================================================ #pragma once class CAutomobile; class CVehicle; class CControllerState; class CCarStateEachFrame { public: int16 velX; int16 velY; int16 velZ; int8 rightX; int8 rightY; int8 rightZ; int8 forwardX; int8 forwardY; int8 forwardZ; int8 wheel; int8 gas; int8 brake; bool handbrake; CVector pos; }; extern char gString[256]; class CRecordDataForChase { enum { NUM_CHASE_CARS = 20 }; enum { STATE_NONE = 0, STATE_RECORD = 1, STATE_PLAYBACK_INIT = 2, STATE_PLAYBACK = 3, STATE_PLAYBACK_BEFORE_RECORDING = 4 }; static uint8 Status; static int PositionChanges; static uint8 CurrentCar; static CAutomobile*pChaseCars[NUM_CHASE_CARS]; static float AnimTime; static uint32 AnimStartTime; static CCarStateEachFrame* pBaseMemForCar[NUM_CHASE_CARS]; static float TimeMultiplier; static int FId2; public: static bool IsRecording(void) { return Status == STATE_RECORD; } static void Init(void); static void SaveOrRetrieveDataForThisFrame(void); static void SaveOrRetrieveCarPositions(void); static void StoreInfoForCar(CAutomobile*, CCarStateEachFrame*); static void RestoreInfoForMatrix(CMatrix&, CCarStateEachFrame*); static void RestoreInfoForCar(CAutomobile*, CCarStateEachFrame*, bool); static void ProcessControlCars(void); static bool ShouldThisPadBeLeftAlone(uint8 pad); static void GiveUsACar(int32, CVector, float, CAutomobile**, uint8, uint8); static void StartChaseScene(float); static void CleanUpChaseScene(void); static void SetUpCarsForChaseScene(void); static void CleanUpCarsForChaseScene(void); static void RemoveCarFromChase(int32); static CVehicle* TurnChaseCarIntoScriptCar(int32); }; struct tGameBuffer { float m_fTimeStep; uint32 m_nTimeInMilliseconds; uint8 m_nSizeOfPads[2]; uint16 m_nChecksum; uint8 m_ControllerBuffer[116]; }; class CRecordDataForGame { enum { STATE_NONE = 0, STATE_RECORD = 1, STATE_PLAYBACK = 2, }; static uint16 RecordingState; static uint8* pDataBuffer; static uint8* pDataBufferPointer; static int FId; static tGameBuffer pDataBufferForFrame; public: static bool IsRecording() { return RecordingState == STATE_RECORD; } static bool IsPlayingBack() { return RecordingState == STATE_PLAYBACK; } static void SaveOrRetrieveDataForThisFrame(void); static void Init(void); private: static uint16 CalcGameChecksum(void); static uint8* PackCurrentPadValues(uint8*, CControllerState*, CControllerState*); static uint8* UnPackCurrentPadValues(uint8*, uint8, CControllerState*); }; ================================================ FILE: src/control/Remote.cpp ================================================ #include "common.h" #include "Automobile.h" #include "CarCtrl.h" #include "Camera.h" #include "Remote.h" #include "Timer.h" #include "World.h" #include "PlayerInfo.h" #include "Vehicle.h" void CRemote::GivePlayerRemoteControlledCar(float x, float y, float z, float rot, uint16 model) { CAutomobile *car = new CAutomobile(model, MISSION_VEHICLE); bool found; z = car->GetDistanceFromCentreOfMassToBaseOfModel() + CWorld::FindGroundZFor3DCoord(x, y, z + 2.0f, &found); car->GetMatrix().SetRotateZOnly(rot); car->SetPosition(x, y, z); car->SetStatus(STATUS_PLAYER_REMOTE); car->bIsLocked = true; CCarCtrl::JoinCarWithRoadSystem(car); car->AutoPilot.m_nCarMission = MISSION_NONE; car->AutoPilot.m_nTempAction = TEMPACT_NONE; car->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; car->AutoPilot.m_nCruiseSpeed = car->AutoPilot.m_fMaxTrafficSpeed = 9.0f; car->AutoPilot.m_nNextLane = car->AutoPilot.m_nCurrentLane = 0; car->bEngineOn = true; CWorld::Add(car); if (FindPlayerVehicle() != nil) FindPlayerVehicle()->SetStatus(STATUS_PLAYER_DISABLED); CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle = car; CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->RegisterReference((CEntity**)&CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle); if (car->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE || car->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI) { TheCamera.TakeControl(car, CCam::MODE_CAM_ON_A_STRING, INTERPOLATION, CAMCONTROL_SCRIPT); TheCamera.SetZoomValueCamStringScript(0); } else TheCamera.TakeControl(car, CCam::MODE_BEHINDCAR, INTERPOLATION, CAMCONTROL_SCRIPT); } void CRemote::TakeRemoteControlledCarFromPlayer(bool blowUp) { if (CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->VehicleCreatedBy == MISSION_VEHICLE) { CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->VehicleCreatedBy = RANDOM_VEHICLE; CCarCtrl::NumMissionCars--; CCarCtrl::NumRandomCars++; } CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->bIsLocked = false; CWorld::Players[CWorld::PlayerInFocus].m_nTimeLostRemoteCar = CTimer::GetTimeInMilliseconds(); CWorld::Players[CWorld::PlayerInFocus].m_bInRemoteMode = true; CWorld::Players[CWorld::PlayerInFocus].field_D5 = blowUp; CWorld::Players[CWorld::PlayerInFocus].field_D6 = true; } ================================================ FILE: src/control/Remote.h ================================================ #pragma once class CRemote { public: static void GivePlayerRemoteControlledCar(float, float, float, float, uint16); static void TakeRemoteControlledCarFromPlayer(bool blowUp = true); }; ================================================ FILE: src/control/Replay.cpp ================================================ #include "common.h" #ifdef GTA_REPLAY #include "AnimBlendAssocGroup.h" #include "AnimBlendAssociation.h" #include "Bike.h" #include "Boat.h" #include "SpecialFX.h" #include "CarCtrl.h" #include "CivilianPed.h" #include "CopPed.h" #include "Wanted.h" #include "Clock.h" #include "DMAudio.h" #include "Draw.h" #include "Explosion.h" #include "FileMgr.h" #include "Fire.h" #include "Frontend.h" #include "Garages.h" #include "Heli.h" #include "main.h" #include "Matrix.h" #include "ModelIndices.h" #include "ModelInfo.h" #include "Object.h" #include "Pad.h" #include "Particle.h" #include "PedAttractor.h" #include "Phones.h" #include "Pickups.h" #include "Plane.h" #include "Pools.h" #include "Population.h" #include "Projectile.h" #include "ProjectileInfo.h" #include "Replay.h" #include "References.h" #include "Pools.h" #include "RpAnimBlend.h" #include "RwHelper.h" #include "CutsceneMgr.h" #include "Skidmarks.h" #include "Stinger.h" #include "Streaming.h" #include "Timer.h" #include "Train.h" #include "Weather.h" #include "Zones.h" #include "Font.h" #include "Text.h" #include "Camera.h" #include "Radar.h" #include "Fluff.h" #include "WaterCreatures.h" uint8 CReplay::Mode; CAddressInReplayBuffer CReplay::Record; CAddressInReplayBuffer CReplay::Playback; uint8 *CReplay::pBuf0; CAutomobile *CReplay::pBuf1; uint8 *CReplay::pBuf2; CPlayerPed *CReplay::pBuf3; uint8 *CReplay::pBuf4; CCutsceneObject *CReplay::pBuf5; uint8 *CReplay::pBuf6; CPtrNode *CReplay::pBuf7; uint8 *CReplay::pBuf8; CEntryInfoNode *CReplay::pBuf9; uint8 *CReplay::pBuf10; CDummyPed *CReplay::pBuf11; uint8 *CReplay::pRadarBlips; uint8 *CReplay::pStoredCam; uint8 *CReplay::pWorld1; CReference *CReplay::pEmptyReferences; CStoredDetailedAnimationState *CReplay::pPedAnims; uint8 *CReplay::pPickups; uint8 *CReplay::pReferences; uint8 CReplay::BufferStatus[NUM_REPLAYBUFFERS]; uint8 CReplay::Buffers[NUM_REPLAYBUFFERS][REPLAYBUFFERSIZE]; bool CReplay::bPlayingBackFromFile; bool CReplay::bReplayEnabled = true; uint32 CReplay::SlowMotion; uint32 CReplay::FramesActiveLookAroundCam; bool CReplay::bDoLoadSceneWhenDone; CPtrNode* CReplay::WorldPtrList; CPtrNode* CReplay::BigBuildingPtrList; CWanted CReplay::PlayerWanted; CPlayerInfo CReplay::PlayerInfo; uint32 CReplay::Time1; uint32 CReplay::Time2; uint32 CReplay::Time3; uint32 CReplay::Time4; uint32 CReplay::Frame; uint8 CReplay::ClockHours; uint8 CReplay::ClockMinutes; uint16 CReplay::OldWeatherType; uint16 CReplay::NewWeatherType; float CReplay::WeatherInterpolationValue; float CReplay::TimeStepNonClipped; float CReplay::TimeStep; float CReplay::TimeScale; float CReplay::CameraFixedX; float CReplay::CameraFixedY; float CReplay::CameraFixedZ; int32 CReplay::OldRadioStation; int8 CReplay::CameraMode; bool CReplay::bAllowLookAroundCam; float CReplay::LoadSceneX; float CReplay::LoadSceneY; float CReplay::LoadSceneZ; float CReplay::CameraFocusX; float CReplay::CameraFocusY; float CReplay::CameraFocusZ; bool CReplay::bPlayerInRCBuggy; float CReplay::fDistanceLookAroundCam; float CReplay::fBetaAngleLookAroundCam; float CReplay::fAlphaAngleLookAroundCam; int CReplay::ms_nNumCivMale_Stored; int CReplay::ms_nNumCivFemale_Stored; int CReplay::ms_nNumCop_Stored; int CReplay::ms_nNumEmergency_Stored; int CReplay::ms_nNumGang1_Stored; int CReplay::ms_nNumGang2_Stored; int CReplay::ms_nNumGang3_Stored; int CReplay::ms_nNumGang4_Stored; int CReplay::ms_nNumGang5_Stored; int CReplay::ms_nNumGang6_Stored; int CReplay::ms_nNumGang7_Stored; int CReplay::ms_nNumGang8_Stored; int CReplay::ms_nNumGang9_Stored; int CReplay::ms_nNumDummy_Stored; int CReplay::ms_nTotalCarPassengerPeds_Stored; int CReplay::ms_nTotalCivPeds_Stored; int CReplay::ms_nTotalGangPeds_Stored; int CReplay::ms_nTotalPeds_Stored; int CReplay::ms_nTotalMissionPeds_Stored; uint8* CReplay::pGarages; CFire* CReplay::FireArray; uint32 CReplay::NumOfFires; uint8* CReplay::paProjectileInfo; uint8* CReplay::paProjectiles; uint8 CReplay::CurrArea; #ifdef FIX_BUGS int CReplay::nHandleOfPlayerPed[NUMPLAYERS]; #endif static void(*CBArray[])(CAnimBlendAssociation*, void*) = { nil, &CPed::PedGetupCB, &CPed::PedStaggerCB, &CPed::PedEvadeCB, &CPed::FinishDieAnimCB, &CPed::FinishedWaitCB, &CPed::FinishLaunchCB, &CPed::FinishHitHeadCB, &CPed::PedAnimGetInCB, &CPed::PedAnimDoorOpenCB, &CPed::PedAnimPullPedOutCB, &CPed::PedAnimDoorCloseCB, &CPed::PedSetInCarCB, &CPed::PedSetOutCarCB, &CPed::PedAnimAlignCB, &CPed::PedSetDraggedOutCarCB, &CPed::PedAnimStepOutCarCB, &CPed::PedSetInTrainCB, #ifdef GTA_TRAIN &CPed::PedSetOutTrainCB, #endif &CPed::FinishedAttackCB, &CPed::FinishFightMoveCB, &PhonePutDownCB, &PhonePickUpCB, &CPed::PedAnimDoorCloseRollingCB, &CPed::FinishJumpCB, &CPed::PedLandCB, &CPed::RestoreHeadingRateCB, &CPed::PedSetQuickDraggedOutCarPositionCB, &CPed::PedSetDraggedOutCarPositionCB, &CPed::PedSetPreviousStateCB, &CPed::FinishedReloadCB, &CPed::PedSetGetInCarPositionCB, &CPed::PedAnimShuffleCB, &CPed::DeleteSunbatheIdleAnimCB, &StartTalkingOnMobileCB, &FinishTalkingOnMobileCB }; static uint8 FindCBFunctionID(void(*f)(CAnimBlendAssociation*, void*)) { for (int i = 0; i < sizeof(CBArray) / sizeof(*CBArray); i++){ if (CBArray[i] == f) return i; } return 0; } static void(*FindCBFunction(uint8 id))(CAnimBlendAssociation*, void*) { return CBArray[id]; } static void ApplyPanelDamageToCar(uint32 panels, CAutomobile* vehicle, bool flying) { if(vehicle->Damage.GetPanelStatus(VEHPANEL_FRONT_LEFT) != CDamageManager::GetPanelStatus(panels, VEHPANEL_FRONT_LEFT)){ vehicle->Damage.SetPanelStatus(VEHPANEL_FRONT_LEFT, CDamageManager::GetPanelStatus(panels, VEHPANEL_FRONT_LEFT)); vehicle->SetPanelDamage(CAR_WING_LF, VEHPANEL_FRONT_LEFT, flying); } if(vehicle->Damage.GetPanelStatus(VEHPANEL_FRONT_RIGHT) != CDamageManager::GetPanelStatus(panels, VEHPANEL_FRONT_RIGHT)){ vehicle->Damage.SetPanelStatus(VEHPANEL_FRONT_RIGHT, CDamageManager::GetPanelStatus(panels, VEHPANEL_FRONT_RIGHT)); vehicle->SetPanelDamage(CAR_WING_RF, VEHPANEL_FRONT_RIGHT, flying); } if(vehicle->Damage.GetPanelStatus(VEHPANEL_REAR_LEFT) != CDamageManager::GetPanelStatus(panels, VEHPANEL_REAR_LEFT)){ vehicle->Damage.SetPanelStatus(VEHPANEL_REAR_LEFT, CDamageManager::GetPanelStatus(panels, VEHPANEL_REAR_LEFT)); vehicle->SetPanelDamage(CAR_WING_LR, VEHPANEL_REAR_LEFT, flying); } if(vehicle->Damage.GetPanelStatus(VEHPANEL_REAR_RIGHT) != CDamageManager::GetPanelStatus(panels, VEHPANEL_REAR_RIGHT)){ vehicle->Damage.SetPanelStatus(VEHPANEL_REAR_RIGHT, CDamageManager::GetPanelStatus(panels, VEHPANEL_REAR_RIGHT)); vehicle->SetPanelDamage(CAR_WING_RR, VEHPANEL_REAR_RIGHT, flying); } if(vehicle->Damage.GetPanelStatus(VEHPANEL_WINDSCREEN) != CDamageManager::GetPanelStatus(panels, VEHPANEL_WINDSCREEN)){ vehicle->Damage.SetPanelStatus(VEHPANEL_WINDSCREEN, CDamageManager::GetPanelStatus(panels, VEHPANEL_WINDSCREEN)); vehicle->SetPanelDamage(CAR_WINDSCREEN, VEHPANEL_WINDSCREEN, flying); } if(vehicle->Damage.GetPanelStatus(VEHBUMPER_FRONT) != CDamageManager::GetPanelStatus(panels, VEHBUMPER_FRONT)){ vehicle->Damage.SetPanelStatus(VEHBUMPER_FRONT, CDamageManager::GetPanelStatus(panels, VEHBUMPER_FRONT)); vehicle->SetPanelDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT, flying); } if(vehicle->Damage.GetPanelStatus(VEHBUMPER_REAR) != CDamageManager::GetPanelStatus(panels, VEHBUMPER_REAR)){ vehicle->Damage.SetPanelStatus(VEHBUMPER_REAR, CDamageManager::GetPanelStatus(panels, VEHBUMPER_REAR)); vehicle->SetPanelDamage(CAR_BUMP_REAR, VEHBUMPER_REAR, flying); } } void PrintElementsInPtrList(void) { for (CPtrNode* node = CWorld::GetBigBuildingList(LEVEL_GENERIC).first; node; node = node->next) { /* Most likely debug print was present here */ } } void CReplay::Init(void) { pBuf0 = nil; pBuf1 = nil; pBuf2 = nil; pBuf3 = nil; pBuf4 = nil; pBuf5 = nil; pBuf6 = nil; pBuf7 = nil; pBuf8 = nil; pBuf9 = nil; pBuf10 = nil; pBuf11 = nil; pRadarBlips = nil; pStoredCam = nil; pWorld1 = nil; pEmptyReferences = nil; pPedAnims = nil; pPickups = nil; pReferences = nil; Mode = MODE_RECORD; Playback.m_nOffset = 0; Playback.m_pBase = nil; Playback.m_bSlot = 0; Record.m_nOffset = 0; Record.m_pBase = nil; Record.m_bSlot = 0; for (int i = 0; i < NUM_REPLAYBUFFERS; i++) BufferStatus[i] = REPLAYBUFFER_UNUSED; Record.m_bSlot = 0; Record.m_pBase = Buffers[0]; BufferStatus[0] = REPLAYBUFFER_RECORD; Buffers[0][Record.m_nOffset] = REPLAYPACKET_END; bPlayingBackFromFile = false; bReplayEnabled = true; SlowMotion = 1; FramesActiveLookAroundCam = 0; bDoLoadSceneWhenDone = false; MarkEverythingAsNew(); } void CReplay::DisableReplays(void) { bReplayEnabled = false; } void CReplay::EnableReplays(void) { bReplayEnabled = true; } void PlayReplayFromHD(void); void CReplay::Update(void) { if (CCutsceneMgr::IsCutsceneProcessing() || CPad::GetPad(0)->ArePlayerControlsDisabled() || CScriptPaths::IsOneActive() || FrontEndMenuManager.GetIsMenuActive()) { Init(); return; } switch (Mode){ case MODE_RECORD: RecordThisFrame(); break; case MODE_PLAYBACK: PlaybackThisFrame(); break; } if (CDraw::FadeValue || !bReplayEnabled) return; if (Mode == MODE_PLAYBACK){ if (CPad::GetPad(0)->GetFJustDown(0)) FinishPlayback(); } else if (Mode == MODE_RECORD){ if (CPad::GetPad(0)->GetFJustDown(0)) TriggerPlayback(REPLAYCAMMODE_ASSTORED, 0.0f, 0.0f, 0.0f, false); if (CPad::GetPad(0)->GetFJustDown(1)) SaveReplayToHD(); if (CPad::GetPad(0)->GetFJustDown(2)) PlayReplayFromHD(); #ifdef USE_BETA_REPLAY_MODE if (CPad::GetPad(0)->GetFJustDown(3)) TriggerPlaybackLastCoupleOfSeconds(5000, REPLAYCAMMODE_TOPDOWN, 0.0f, 0.0f, 0.0f, 4); #endif } } void CReplay::RecordThisFrame(void) { uint32 memory_required = sizeof(tGeneralPacket) + sizeof(tClockPacket) + sizeof(tWeatherPacket) + sizeof(tTimerPacket) + sizeof(tMiscPacket); CVehiclePool* vehiclesT = CPools::GetVehiclePool(); for (int i = 0; i < vehiclesT->GetSize(); i++) { CVehicle* v = vehiclesT->GetSlot(i); if (v && v->m_rwObject && v->GetModelIndex() != MI_AIRTRAIN && v->GetModelIndex() != MI_TRAIN) { if (v->IsBike()) memory_required += sizeof(tBikeUpdatePacket); else memory_required += sizeof(tVehicleUpdatePacket); } } CPedPool* pedsT = CPools::GetPedPool(); for (int i = 0; i < pedsT->GetSize(); i++) { CPed* p = pedsT->GetSlot(i); if (!p || !p->m_rwObject) continue; if (!p->bHasAlreadyBeenRecorded) { memory_required += sizeof(tPedHeaderPacket); } memory_required += sizeof(tPedUpdatePacket); } for (uint8 i = 0; i < NUMBULLETTRACES; i++) { if (!CBulletTraces::aTraces[i].m_bInUse) continue; memory_required += sizeof(tBulletTracePacket); } memory_required += sizeof(tEndOfFramePacket) + 1; // 1 for Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END; if (Record.m_nOffset + memory_required > REPLAYBUFFERSIZE - 16) GoToNextBlock(); tGeneralPacket* general = (tGeneralPacket*)&Record.m_pBase[Record.m_nOffset]; general->type = REPLAYPACKET_GENERAL; general->camera_pos.CopyOnlyMatrix(TheCamera.GetMatrix()); general->player_pos = FindPlayerCoors(); general->in_rcvehicle = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle ? true : false; Record.m_nOffset += sizeof(*general); tClockPacket* clock = (tClockPacket*)&Record.m_pBase[Record.m_nOffset]; clock->type = REPLAYPACKET_CLOCK; clock->hours = CClock::GetHours(); clock->minutes = CClock::GetMinutes(); Record.m_nOffset += sizeof(*clock); tWeatherPacket* weather = (tWeatherPacket*)&Record.m_pBase[Record.m_nOffset]; weather->type = REPLAYPACKET_WEATHER; weather->old_weather = CWeather::OldWeatherType; weather->new_weather = CWeather::NewWeatherType; weather->interpolation = CWeather::InterpolationValue; Record.m_nOffset += sizeof(*weather); tTimerPacket* timer = (tTimerPacket*)&Record.m_pBase[Record.m_nOffset]; timer->type = REPLAYPACKET_TIMER; timer->timer = CTimer::GetTimeInMilliseconds(); Record.m_nOffset += sizeof(*timer); CVehiclePool* vehicles = CPools::GetVehiclePool(); for (int i = 0; i < vehicles->GetSize(); i++){ CVehicle* v = vehicles->GetSlot(i); if (v && v->m_rwObject && v->GetModelIndex() != MI_AIRTRAIN && v->GetModelIndex() != MI_TRAIN) { if (v->IsBike()) StoreBikeUpdate(v, i); else StoreCarUpdate(v, i); } } CPedPool* peds = CPools::GetPedPool(); for (int i = 0; i < peds->GetSize(); i++) { CPed* p = peds->GetSlot(i); if (!p || !p->m_rwObject) continue; if (!p->bHasAlreadyBeenRecorded){ tPedHeaderPacket* ph = (tPedHeaderPacket*)&Record.m_pBase[Record.m_nOffset]; ph->type = REPLAYPACKET_PED_HEADER; ph->index = i; ph->mi = p->GetModelIndex(); ph->pedtype = p->m_nPedType; Record.m_nOffset += sizeof(*ph); p->bHasAlreadyBeenRecorded = true; } StorePedUpdate(p, i); } for (uint8 i = 0; i < NUMBULLETTRACES; i++){ if (!CBulletTraces::aTraces[i].m_bInUse) continue; tBulletTracePacket* bt = (tBulletTracePacket*)&Record.m_pBase[Record.m_nOffset]; bt->type = REPLAYPACKET_BULLET_TRACES; bt->index = i; bt->inf = CBulletTraces::aTraces[i].m_vecStartPos; bt->sup = CBulletTraces::aTraces[i].m_vecEndPos; Record.m_nOffset += sizeof(*bt); } tMiscPacket* misc = (tMiscPacket*)&Record.m_pBase[Record.m_nOffset]; misc->type = REPLAYPACKET_MISC; misc->cam_shake_start = TheCamera.m_uiCamShakeStart; misc->cam_shake_strength = TheCamera.m_fCamShakeForce; misc->cur_area = CGame::currArea; misc->video_cam = CSpecialFX::bVideoCam; misc->lift_cam = CSpecialFX::bLiftCam; Record.m_nOffset += sizeof(*misc); tEndOfFramePacket* eof = (tEndOfFramePacket*)&Record.m_pBase[Record.m_nOffset]; eof->type = REPLAYPACKET_ENDOFFRAME; Record.m_nOffset += sizeof(*eof); Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END; } void CReplay::GoToNextBlock(void) { Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END; BufferStatus[Record.m_bSlot] = REPLAYBUFFER_PLAYBACK; Record.m_bSlot = (Record.m_bSlot + 1) % NUM_REPLAYBUFFERS; BufferStatus[Record.m_bSlot] = REPLAYBUFFER_RECORD; Record.m_pBase = Buffers[Record.m_bSlot]; Record.m_nOffset = 0; *Record.m_pBase = REPLAYPACKET_END; MarkEverythingAsNew(); } void CReplay::RecordParticle(tParticleType type, const CVector& vecPos, const CVector& vecDir, float fSize, const RwRGBA& color) { if (Record.m_nOffset > REPLAYBUFFERSIZE - 16 - sizeof(tParticlePacket)) GoToNextBlock(); tParticlePacket* pp = (tParticlePacket*)&Record.m_pBase[Record.m_nOffset]; pp->type = REPLAYPACKET_PARTICLE; pp->particle_type = type; pp->pos_x = 4.0f * vecPos.x; pp->pos_y = 4.0f * vecPos.y; pp->pos_z = 4.0f * vecPos.z; pp->dir_x = 120.0f * clamp(vecDir.x, -1.0f, 1.0f); pp->dir_y = 120.0f * clamp(vecDir.y, -1.0f, 1.0f); pp->dir_z = 120.0f * clamp(vecDir.z, -1.0f, 1.0f); pp->size = fSize; pp->r = color.red; pp->g = color.green; pp->b = color.blue; pp->a = color.alpha; Record.m_nOffset += sizeof(tParticlePacket); Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END; } void CReplay::StorePedUpdate(CPed *ped, int id) { tPedUpdatePacket* pp = (tPedUpdatePacket*)&Record.m_pBase[Record.m_nOffset]; pp->type = REPLAYPACKET_PED_UPDATE; pp->index = id; pp->heading = 128.0f / PI * ped->m_fRotationCur; pp->matrix.CompressFromFullMatrix(ped->GetMatrix()); pp->assoc_group_id = ped->m_animGroup; pp->is_visible = ped->bIsVisible; /* Would be more sane to use GetJustIndex(ped->m_pMyVehicle) in following assignment */ if (ped->InVehicle()) pp->vehicle_index = (CPools::GetVehiclePool()->GetIndex(ped->m_pMyVehicle) >> 8) + 1; else pp->vehicle_index = 0; pp->weapon_model = ped->m_wepModelID; StorePedAnimation(ped, &pp->anim_state); Record.m_nOffset += sizeof(tPedUpdatePacket); } void CReplay::StorePedAnimation(CPed *ped, CStoredAnimationState *state) { CAnimBlendAssociation* second; float blend_amount; CAnimBlendAssociation* main = RpAnimBlendClumpGetMainAssociation((RpClump*)ped->m_rwObject, &second, &blend_amount); if (main){ state->animId = main->animId; state->time = 255.0f / 4.0f * clamp(main->currentTime, 0.0f, 4.0f); state->speed = 255.0f / 3.0f * clamp(main->speed, 0.0f, 3.0f); state->groupId = main->groupId; }else{ state->animId = 3; state->time = 0; state->speed = 85; state->groupId = 0; } if (second) { state->secAnimId = second->animId; state->secTime = 255.0f / 4.0f * clamp(second->currentTime, 0.0f, 4.0f); state->secSpeed = 255.0f / 3.0f * clamp(second->speed, 0.0f, 3.0f); state->blendAmount = 255.0f / 2.0f * clamp(blend_amount, 0.0f, 2.0f); state->secGroupId = second->groupId; }else{ state->secAnimId = 0; state->secTime = 0; state->secSpeed = 0; state->blendAmount = 0; state->secGroupId = 0; } CAnimBlendAssociation* partial = RpAnimBlendClumpGetMainPartialAssociation((RpClump*)ped->m_rwObject); if (partial) { state->partAnimId = partial->animId; state->partAnimTime = 255.0f / 4.0f * clamp(partial->currentTime, 0.0f, 4.0f); state->partAnimSpeed = 255.0f / 3.0f * clamp(partial->speed, 0.0f, 3.0f); state->partBlendAmount = 255.0f / 2.0f * clamp(partial->blendAmount, 0.0f, 2.0f); state->partGroupId = partial->groupId; }else{ state->partAnimId = 0; state->partAnimTime = 0; state->partAnimSpeed = 0; state->partBlendAmount = 0; state->partGroupId = 0; } } void CReplay::StoreDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state) { for (int i = 0; i < NUM_MAIN_ANIMS_IN_REPLAY; i++){ CAnimBlendAssociation* assoc = RpAnimBlendClumpGetMainAssociation_N((RpClump*)ped->m_rwObject, i); if (assoc){ state->aAnimId[i] = assoc->animId; state->aCurTime[i] = 255.0f / 4.0f * clamp(assoc->currentTime, 0.0f, 4.0f); state->aSpeed[i] = 255.0f / 3.0f * clamp(assoc->speed, 0.0f, 3.0f); state->aBlendAmount[i] = 255.0f / 2.0f * clamp(assoc->blendAmount, 0.0f, 2.0f); state->aBlendDelta[i] = 127.0f / 32.0f * clamp(assoc->blendDelta, -16.0f, 16.0f); state->aFlags[i] = assoc->flags; state->aGroupId[i] = assoc->groupId; if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH || assoc->callbackType == CAnimBlendAssociation::CB_DELETE) { state->aFunctionCallbackID[i] = FindCBFunctionID(assoc->callback); if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH) state->aFunctionCallbackID[i] |= 0x80; }else{ state->aFunctionCallbackID[i] = 0; } }else{ state->aAnimId[i] = ANIM_STD_NUM; state->aCurTime[i] = 0; state->aSpeed[i] = 85; state->aFunctionCallbackID[i] = 0; state->aFlags[i] = 0; state->aGroupId[i] = 0; } } for (int i = 0; i < NUM_PARTIAL_ANIMS_IN_REPLAY; i++) { CAnimBlendAssociation* assoc = RpAnimBlendClumpGetMainPartialAssociation_N((RpClump*)ped->m_rwObject, i); if (assoc) { state->aAnimId2[i] = assoc->animId; state->aCurTime2[i] = 255.0f / 4.0f * clamp(assoc->currentTime, 0.0f, 4.0f); state->aSpeed2[i] = 255.0f / 3.0f * clamp(assoc->speed, 0.0f, 3.0f); state->aBlendAmount2[i] = 255.0f / 2.0f * clamp(assoc->blendAmount, 0.0f, 2.0f); state->aBlendDelta2[i] = 127.0f / 16.0f * clamp(assoc->blendDelta, -16.0f, 16.0f); state->aFlags2[i] = assoc->flags; state->aGroupId2[i] = assoc->groupId; if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH || assoc->callbackType == CAnimBlendAssociation::CB_DELETE) { state->aFunctionCallbackID2[i] = FindCBFunctionID(assoc->callback); if (assoc->callbackType == CAnimBlendAssociation::CB_FINISH) state->aFunctionCallbackID2[i] |= 0x80; }else{ state->aFunctionCallbackID2[i] = 0; } } else { state->aAnimId2[i] = ANIM_STD_NUM; state->aCurTime2[i] = 0; state->aSpeed2[i] = 85; state->aFunctionCallbackID2[i] = 0; state->aFlags2[i] = 0; state->aGroupId2[i] = 0; } } } void CReplay::ProcessPedUpdate(CPed *ped, float interpolation, CAddressInReplayBuffer *buffer) { tPedUpdatePacket *pp = (tPedUpdatePacket*)&buffer->m_pBase[buffer->m_nOffset]; if (!ped){ printf("Replay:Ped wasn't there\n"); buffer->m_nOffset += sizeof(tPedUpdatePacket); return; } ped->m_fRotationCur = pp->heading * PI / 128.0f; ped->m_fRotationDest = pp->heading * PI / 128.0f; CMatrix ped_matrix; pp->matrix.DecompressIntoFullMatrix(ped_matrix); ped->GetMatrix() = ped->GetMatrix() * CMatrix(1.0f - interpolation); ped->GetMatrix().GetPosition() *= (1.0f - interpolation); ped->GetMatrix() += CMatrix(interpolation) * ped_matrix; if (pp->vehicle_index) { ped->m_pMyVehicle = CPools::GetVehiclePool()->GetSlot(pp->vehicle_index - 1); ped->bInVehicle = true; } else { ped->m_pMyVehicle = nil; ped->bInVehicle = false; } if (pp->assoc_group_id != ped->m_animGroup) { ped->m_animGroup = (AssocGroupId)pp->assoc_group_id; if (ped == FindPlayerPed()) ((CPlayerPed*)ped)->ReApplyMoveAnims(); } ped->bIsVisible = pp->is_visible; if (FramesActiveLookAroundCam && ped->m_nPedType == PEDTYPE_PLAYER1) ped->bIsVisible = true; RetrievePedAnimation(ped, &pp->anim_state); ped->RemoveWeaponModel(-1); if (pp->weapon_model != (uint16)-1) { if (CStreaming::HasModelLoaded(pp->weapon_model)) ped->AddWeaponModel(pp->weapon_model); else CStreaming::RequestModel(pp->weapon_model, 0); } CWorld::Remove(ped); CWorld::Add(ped); buffer->m_nOffset += sizeof(tPedUpdatePacket); } bool HasAnimGroupLoaded(uint8 group) { CAnimBlendAssocGroup* pGroup = &CAnimManager::GetAnimAssocGroups()[group]; return pGroup->animBlock && pGroup->animBlock->isLoaded; } void CReplay::RetrievePedAnimation(CPed *ped, CStoredAnimationState *state) { CAnimBlendAssociation* anim1; if (state->animId <= ANIM_STD_IDLE) anim1 = CAnimManager::BlendAnimation( (RpClump*)ped->m_rwObject, ped->m_animGroup, (AnimationId)state->animId, 100.0f); else if (HasAnimGroupLoaded(state->groupId)) anim1 = CAnimManager::BlendAnimation((RpClump*)ped->m_rwObject, (AssocGroupId)state->groupId, (AnimationId)state->animId, 100.0f); else anim1 = CAnimManager::BlendAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_STD_WALK, 100.0f); anim1->SetCurrentTime(state->time * 4.0f / 255.0f); anim1->speed = state->speed * 3.0f / 255.0f; anim1->SetBlend(1.0f, 1.0f); anim1->callbackType = CAnimBlendAssociation::CB_NONE; if (state->blendAmount && state->secAnimId){ float time = state->secTime * 4.0f / 255.0f; float speed = state->secSpeed * 3.0f / 255.0f; float blend = state->blendAmount * 2.0f / 255.0f; CAnimBlendAssociation* anim2 = CAnimManager::BlendAnimation( (RpClump*)ped->m_rwObject, (state->secAnimId > ANIM_STD_IDLE) ? (AssocGroupId)state->secGroupId : ped->m_animGroup, (AnimationId)state->secAnimId, 100.0f); anim2->SetCurrentTime(time); anim2->speed = speed; anim2->SetBlend(blend, 1.0f); anim2->callbackType = CAnimBlendAssociation::CB_NONE; } RpAnimBlendClumpRemoveAssociations((RpClump*)ped->m_rwObject, 0x10); if (state->partAnimId){ float time = state->partAnimTime * 4.0f / 255.0f; float speed = state->partAnimSpeed * 3.0f / 255.0f; float blend = state->partBlendAmount * 2.0f / 255.0f; if (blend > 0.0f && state->partAnimId != ANIM_STD_IDLE && HasAnimGroupLoaded(state->partGroupId)){ CAnimBlendAssociation* anim3 = CAnimManager::BlendAnimation( (RpClump*)ped->m_rwObject, (AssocGroupId)state->partGroupId, (AnimationId)state->partAnimId, 1000.0f); anim3->SetCurrentTime(time); anim3->speed = speed; anim3->SetBlend(blend, 0.0f); } } } void CReplay::RetrieveDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state) { CAnimBlendAssociation* assoc; for (int i = 0; ((assoc = RpAnimBlendClumpGetMainAssociation_N(ped->GetClump(), i))); i++) assoc->SetBlend(0.0f, -1.0f); for (int i = 0; ((assoc = RpAnimBlendClumpGetMainPartialAssociation_N(ped->GetClump(), i))); i++) assoc->SetBlend(0.0f, -1.0f); for (int i = 0; i < NUM_MAIN_ANIMS_IN_REPLAY; i++) { if (state->aAnimId[i] == ANIM_STD_NUM) continue; CAnimBlendAssociation* anim = CAnimManager::AddAnimation(ped->GetClump(), state->aAnimId[i] > ANIM_STD_IDLE ? (AssocGroupId)state->aGroupId[i] : ped->m_animGroup, (AnimationId)state->aAnimId[i]); anim->SetCurrentTime(state->aCurTime[i] * 4.0f / 255.0f); anim->speed = state->aSpeed[i] * 3.0f / 255.0f; anim->SetBlend(state->aBlendAmount[i] * 2.0f / 255.0f, state->aBlendDelta[i] * 16.0f / 127.0f); anim->flags = state->aFlags[i]; uint8 callback = state->aFunctionCallbackID[i]; if (!callback) anim->callbackType = CAnimBlendAssociation::CB_NONE; else if (callback & 0x80) anim->SetFinishCallback(FindCBFunction(callback & 0x7F), ped); else anim->SetDeleteCallback(FindCBFunction(callback & 0x7F), ped); } for (int i = 0; i < NUM_PARTIAL_ANIMS_IN_REPLAY; i++) { if (state->aAnimId2[i] == ANIM_STD_NUM) continue; CAnimBlendAssociation* anim = CAnimManager::AddAnimation(ped->GetClump(), state->aAnimId2[i] > ANIM_STD_IDLE ? (AssocGroupId)state->aGroupId2[i] : ped->m_animGroup, (AnimationId)state->aAnimId2[i]); anim->SetCurrentTime(state->aCurTime2[i] * 4.0f / 255.0f); anim->speed = state->aSpeed2[i] * 3.0f / 255.0f; anim->SetBlend(state->aBlendAmount2[i] * 2.0f / 255.0f, state->aBlendDelta2[i] * 16.0f / 127.0f); anim->flags = state->aFlags2[i]; uint8 callback = state->aFunctionCallbackID2[i]; if (!callback) anim->callbackType = CAnimBlendAssociation::CB_NONE; else if (callback & 0x80) anim->SetFinishCallback(FindCBFunction(callback & 0x7F), ped); else anim->SetDeleteCallback(FindCBFunction(callback & 0x7F), ped); } } void CReplay::PlaybackThisFrame(void) { static int FrameSloMo = 0; CAddressInReplayBuffer buf = Playback; if (PlayBackThisFrameInterpolation(&buf, 1.0f, nil)){ DMAudio.SetEffectsFadeVol(127); DMAudio.SetMusicFadeVol(127); return; } if (FrameSloMo){ CAddressInReplayBuffer buf_sm = buf; if (PlayBackThisFrameInterpolation(&buf_sm, FrameSloMo * 1.0f / SlowMotion, nil)){ DMAudio.SetEffectsFadeVol(127); DMAudio.SetMusicFadeVol(127); return; } } FrameSloMo = (FrameSloMo + 1) % SlowMotion; if (FrameSloMo == 0) Playback = buf; ProcessLookAroundCam(); DMAudio.SetEffectsFadeVol(0); DMAudio.SetMusicFadeVol(0); } // next two functions are only found in mobile version // most likely they were optimized out for being unused void CReplay::TriggerPlaybackLastCoupleOfSeconds(uint32 start, uint8 cam_mode, float cam_x, float cam_y, float cam_z, uint32 slomo) { if (Mode != MODE_RECORD) return; TriggerPlayback(cam_mode, cam_x, cam_y, cam_z, true); SlowMotion = slomo; bAllowLookAroundCam = false; if (!FastForwardToTime(CTimer::GetTimeInMilliseconds() - start)) Mode = MODE_RECORD; } bool CReplay::FastForwardToTime(uint32 start) { uint32 timer = 0; while (start > timer) if (PlayBackThisFrameInterpolation(&Playback, 1.0f, &timer)) return false; return true; } void CReplay::StoreCarUpdate(CVehicle *vehicle, int id) { tVehicleUpdatePacket* vp = (tVehicleUpdatePacket*)&Record.m_pBase[Record.m_nOffset]; vp->type = REPLAYPACKET_VEHICLE; vp->index = id; vp->matrix.CompressFromFullMatrix(vehicle->GetMatrix()); 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->mi = vehicle->GetModelIndex(); vp->primary_color = vehicle->m_currentColour1; vp->secondary_color = vehicle->m_currentColour2; if (vehicle->GetModelIndex() == MI_RHINO) vp->car_gun = 128.0f / PI * ((CAutomobile*)vehicle)->m_fCarGunLR; else vp->wheel_state = 50.0f * vehicle->m_fSteerAngle; if (vehicle->IsCar()){ CAutomobile* car = (CAutomobile*)vehicle; for (int i = 0; i < 4; i++){ vp->wheel_susp_dist[i] = 50.0f * car->m_aSuspensionSpringRatio[i]; vp->wheel_rotation[i] = 128.0f / PI * car->m_aWheelRotation[i]; } vp->door_angles[0] = 127.0f / PI * car->Doors[2].m_fAngle; vp->door_angles[1] = 127.0f / PI * car->Doors[3].m_fAngle; vp->door_status = 0; for (int i = 0; i < 6; i++){ if (car->Damage.GetDoorStatus(i) == DOOR_STATUS_MISSING) vp->door_status |= BIT(i); } } if (vehicle->GetModelIndex() == MI_SKIMMER) vp->skimmer_speed = 50.0f * ((CBoat*)vehicle)->m_fMovingSpeed; vp->render_scorched = vehicle->bRenderScorched; vp->vehicle_type = vehicle->m_vehType; Record.m_nOffset += sizeof(tVehicleUpdatePacket); } void CReplay::StoreBikeUpdate(CVehicle* vehicle, int id) { CBike* bike = (CBike*)vehicle; tBikeUpdatePacket* vp = (tBikeUpdatePacket*)&Record.m_pBase[Record.m_nOffset]; vp->type = REPLAYPACKET_BIKE; vp->index = id; vp->matrix.CompressFromFullMatrix(vehicle->GetMatrix()); vp->health = vehicle->m_fHealth / 4.0f; /* Not anticipated that health can be > 1000. */ vp->acceleration = vehicle->m_fGasPedal * 100.0f; #ifdef FIX_BUGS // originally it's undefined behaviour - different fields are copied on PC and mobile for (int i = 0; i < 2; i++) vp->wheel_rotation[i] = 128.0f / PI * bike->m_aWheelRotation[i]; for (int i = 0; i < 2; i++) vp->wheel_rotation[i + 2] = 128.0f / PI * bike->m_aWheelSpeed[i]; for (int i = 0; i < 4; i++) vp->wheel_susp_dist[i] = 50.0f * bike->m_aSuspensionSpringRatio[i]; #else for (int i = 0; i < 4; i++) { vp->wheel_susp_dist[i] = 50.0f * bike->m_aSuspensionSpringRatio[i]; vp->wheel_rotation[i] = 128.0f / PI * bike->m_aWheelRotation[i]; } #endif 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; vp->wheel_state = 50.0f * vehicle->m_fSteerAngle; vp->lean_angle = 50.0f * bike->m_fLeanLRAngle; vp->wheel_angle = 50.0f * bike->m_fWheelAngle; Record.m_nOffset += sizeof(tBikeUpdatePacket); } void CReplay::ProcessCarUpdate(CVehicle *vehicle, float interpolation, CAddressInReplayBuffer *buffer) { tVehicleUpdatePacket* vp = (tVehicleUpdatePacket*)&buffer->m_pBase[buffer->m_nOffset]; if (!vehicle){ printf("Replay:Car wasn't there"); return; } CMatrix vehicle_matrix; vp->matrix.DecompressIntoFullMatrix(vehicle_matrix); vehicle->GetMatrix() = vehicle->GetMatrix() * CMatrix(1.0f - interpolation); vehicle->GetMatrix().GetPosition() *= (1.0f - interpolation); vehicle->GetMatrix() += CMatrix(interpolation) * vehicle_matrix; vehicle->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); vehicle->m_fHealth = 4 * vp->health; vehicle->m_fGasPedal = vp->acceleration / 100.0f; if (vehicle->IsCar()) ApplyPanelDamageToCar(vp->panels, (CAutomobile*)vehicle, true); vehicle->m_vecMoveSpeed = CVector(vp->velocityX / 8000.0f, vp->velocityY / 8000.0f, vp->velocityZ / 8000.0f); if (vehicle->GetModelIndex() == MI_RHINO) { ((CAutomobile*)vehicle)->m_fCarGunLR = vp->car_gun * PI / 128.0f; vehicle->m_fSteerAngle = 0.0f; }else{ vehicle->m_fSteerAngle = vp->wheel_state / 50.0f; } if (vehicle->IsCar()) { CAutomobile* car = (CAutomobile*)vehicle; for (int i = 0; i < 4; i++) { car->m_aSuspensionSpringRatio[i] = vp->wheel_susp_dist[i] / 50.0f; car->m_aWheelRotation[i] = vp->wheel_rotation[i] * PI / 128.0f; } car->Doors[DOOR_FRONT_LEFT].m_fAngle = car->Doors[DOOR_FRONT_LEFT].m_fPrevAngle = vp->door_angles[0] * PI / 127.0f; car->Doors[DOOR_FRONT_RIGHT].m_fAngle = car->Doors[DOOR_FRONT_RIGHT].m_fPrevAngle = vp->door_angles[1] * PI / 127.0f; if (vp->door_angles[0]) car->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_SWINGING); if (vp->door_angles[1]) car->Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_SWINGING); if (vp->door_status & 1 && car->Damage.GetDoorStatus(DOOR_BONNET) != DOOR_STATUS_MISSING) { car->Damage.SetDoorStatus(DOOR_BONNET, DOOR_STATUS_MISSING); car->SetDoorDamage(CAR_BONNET, DOOR_BONNET, true); } if (vp->door_status & 2 && car->Damage.GetDoorStatus(DOOR_BOOT) != DOOR_STATUS_MISSING) { car->Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_MISSING); car->SetDoorDamage(CAR_BOOT, DOOR_BOOT, true); } if (vp->door_status & 4 && car->Damage.GetDoorStatus(DOOR_FRONT_LEFT) != DOOR_STATUS_MISSING) { car->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_MISSING); car->SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT, true); } if (vp->door_status & 8 && car->Damage.GetDoorStatus(DOOR_FRONT_RIGHT) != DOOR_STATUS_MISSING) { car->Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_MISSING); car->SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT, true); } if (vp->door_status & 0x10 && car->Damage.GetDoorStatus(DOOR_REAR_LEFT) != DOOR_STATUS_MISSING) { car->Damage.SetDoorStatus(DOOR_REAR_LEFT, DOOR_STATUS_MISSING); car->SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT, true); } if (vp->door_status & 0x20 && car->Damage.GetDoorStatus(DOOR_REAR_RIGHT) != DOOR_STATUS_MISSING) { car->Damage.SetDoorStatus(DOOR_REAR_RIGHT, DOOR_STATUS_MISSING); car->SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT, true); } } vehicle->bEngineOn = true; if (vehicle->IsCar()) ((CAutomobile*)vehicle)->m_nDriveWheelsOnGround = 4; CWorld::Remove(vehicle); CWorld::Add(vehicle); if (vehicle->IsBoat()) ((CBoat*)vehicle)->m_bIsAnchored = false; vehicle->bRenderScorched = vp->render_scorched; if (vehicle->GetModelIndex() == MI_SKIMMER) ((CBoat*)vehicle)->m_fMovingSpeed = vp->skimmer_speed / 50.0f; } void CReplay::ProcessBikeUpdate(CVehicle* vehicle, float interpolation, CAddressInReplayBuffer* buffer) { CBike* bike = (CBike*)vehicle; tBikeUpdatePacket* vp = (tBikeUpdatePacket*)&buffer->m_pBase[buffer->m_nOffset]; if (!vehicle) { printf("Replay:Car wasn't there"); return; } CMatrix vehicle_matrix; vp->matrix.DecompressIntoFullMatrix(vehicle_matrix); vehicle->GetMatrix() = vehicle->GetMatrix() * CMatrix(1.0f - interpolation); vehicle->GetMatrix().GetPosition() *= (1.0f - interpolation); vehicle->GetMatrix() += CMatrix(interpolation) * vehicle_matrix; vehicle->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); vehicle->m_fHealth = 4 * vp->health; vehicle->m_fGasPedal = vp->acceleration / 100.0f; vehicle->m_vecMoveSpeed = CVector(vp->velocityX / 8000.0f, vp->velocityY / 8000.0f, vp->velocityZ / 8000.0f); vehicle->m_fSteerAngle = vp->wheel_state / 50.0f; vehicle->bEngineOn = true; #ifdef FIX_BUGS for (int i = 0; i < 2; i++) bike->m_aWheelRotation[i] = vp->wheel_rotation[i] / (128.0f / PI); for (int i = 0; i < 2; i++) bike->m_aWheelSpeed[i] = vp->wheel_rotation[i + 2] / (128.0f / PI); for (int i = 0; i < 4; i++) bike->m_aSuspensionSpringRatio[i] = vp->wheel_susp_dist[i] / 50.0f; #else for (int i = 0; i < 4; i++) { bike->m_aSuspensionSpringRatio[i] = vp->wheel_susp_dist[i] / 50.0f; bike->m_aWheelRotation[i] = vp->wheel_rotation[i] / (128.0f / PI); } #endif bike->m_fLeanLRAngle = vp->lean_angle / 50.0f; bike->m_fWheelAngle = vp->wheel_angle / 50.0f; bike->bLeanMatrixClean = false; bike->CalculateLeanMatrix(); CWorld::Remove(vehicle); CWorld::Add(vehicle); } bool CReplay::PlayBackThisFrameInterpolation(CAddressInReplayBuffer *buffer, float interpolation, uint32 *pTimer) { CBulletTraces::Init(); float split = 1.0f - interpolation; int ped_min_index = 0; /* Optimization due to peds and vehicles placed in buffer sequentially. */ int vehicle_min_index = 0; /* So next ped can't have pool index less than current. */ for(;;){ uint8* ptr = buffer->m_pBase; uint32 offset = buffer->m_nOffset; uint8 type = ptr[offset]; if (type == REPLAYPACKET_ENDOFFRAME) break; switch (type) { case REPLAYPACKET_END: { int slot = buffer->m_bSlot; if (BufferStatus[slot] == REPLAYBUFFER_RECORD) { FinishPlayback(); return true; } buffer->m_bSlot = (slot + 1) % NUM_REPLAYBUFFERS; buffer->m_nOffset = 0; buffer->m_pBase = Buffers[buffer->m_bSlot]; ped_min_index = 0; vehicle_min_index = 0; break; } case REPLAYPACKET_VEHICLE: { tVehicleUpdatePacket* vp = (tVehicleUpdatePacket*)&ptr[offset]; for (int i = vehicle_min_index; i < vp->index; i++) { CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); if (!v) continue; /* Removing vehicles not present in this frame. */ CWorld::Remove(v); delete v; } vehicle_min_index = vp->index + 1; CVehicle* v = CPools::GetVehiclePool()->GetSlot(vp->index); CVehicle* new_v; if (!v) { int mi = vp->mi; if (CStreaming::ms_aInfoForModel[mi].m_loadState != 1) { CStreaming::RequestModel(mi, 0); } else { switch (vp->vehicle_type) { case VEHICLE_TYPE_CAR: new_v = new(vp->index << 8) CAutomobile(mi, 2); break; case VEHICLE_TYPE_BOAT: new_v = new(vp->index << 8) CBoat(mi, 2); break; case VEHICLE_TYPE_TRAIN: new_v = new(vp->index << 8) CTrain(mi, 2); break; case VEHICLE_TYPE_HELI: new_v = new(vp->index << 8) CHeli(mi, 2); break; case VEHICLE_TYPE_PLANE: new_v = new(vp->index << 8) CPlane(mi, 2); break; case VEHICLE_TYPE_BIKE: // not possible new_v = new(vp->index << 8) CBike(mi, 2); break; } new_v->SetStatus(STATUS_PLAYER_PLAYBACKFROMBUFFER); vp->matrix.DecompressIntoFullMatrix(new_v->GetMatrix()); new_v->m_currentColour1 = vp->primary_color; new_v->m_currentColour2 = vp->secondary_color; CWorld::Add(new_v); } } ProcessCarUpdate(CPools::GetVehiclePool()->GetSlot(vp->index), interpolation, buffer); buffer->m_nOffset += sizeof(tVehicleUpdatePacket); break; } case REPLAYPACKET_BIKE: { tBikeUpdatePacket* vp = (tBikeUpdatePacket*)&ptr[offset]; for (int i = vehicle_min_index; i < vp->index; i++) { CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); if (!v) continue; /* Removing vehicles not present in this frame. */ CWorld::Remove(v); delete v; } vehicle_min_index = vp->index + 1; CVehicle* v = CPools::GetVehiclePool()->GetSlot(vp->index); CVehicle* new_v; if (!v) { int mi = vp->mi; if (CStreaming::ms_aInfoForModel[mi].m_loadState != 1) { CStreaming::RequestModel(mi, 0); } else { new_v = new(vp->index << 8) CBike(mi, 2); new_v->SetStatus(STATUS_PLAYER_PLAYBACKFROMBUFFER); vp->matrix.DecompressIntoFullMatrix(new_v->GetMatrix()); new_v->m_currentColour1 = vp->primary_color; new_v->m_currentColour2 = vp->secondary_color; CWorld::Add(new_v); } } ProcessBikeUpdate(CPools::GetVehiclePool()->GetSlot(vp->index), interpolation, buffer); buffer->m_nOffset += sizeof(tBikeUpdatePacket); break; } case REPLAYPACKET_PED_HEADER: { tPedHeaderPacket* ph = (tPedHeaderPacket*)&ptr[offset]; if (!CPools::GetPedPool()->GetSlot(ph->index)) { if (!CStreaming::HasModelLoaded(ph->mi) || (ph->mi >= MI_SPECIAL01 && ph->mi < MI_LAST_PED)) { CStreaming::RequestModel(ph->mi, 0); } else { CPed* new_p; if (ph->pedtype != PEDTYPE_PLAYER1) new_p = new(ph->index << 8) CCivilianPed((ePedType)ph->pedtype, ph->mi); else new_p = new(ph->index << 8) CPlayerPed(); new_p->SetStatus(STATUS_PLAYER_PLAYBACKFROMBUFFER); new_p->GetMatrix().SetUnity(); CWorld::Add(new_p); } } buffer->m_nOffset += sizeof(tPedHeaderPacket); break; } case REPLAYPACKET_PED_UPDATE: { tPedUpdatePacket* pu = (tPedUpdatePacket*)&ptr[offset]; for (int i = ped_min_index; i < pu->index; i++) { CPed* p = CPools::GetPedPool()->GetSlot(i); if (!p) continue; /* Removing peds not present in this frame. */ CWorld::Remove(p); delete p; } ped_min_index = pu->index + 1; ProcessPedUpdate(CPools::GetPedPool()->GetSlot(pu->index), interpolation, buffer); break; } case REPLAYPACKET_GENERAL: { tGeneralPacket* pg = (tGeneralPacket*)&ptr[offset]; TheCamera.GetMatrix() = TheCamera.GetMatrix() * CMatrix(split); TheCamera.GetMatrix().GetPosition() *= split; TheCamera.GetMatrix() += CMatrix(interpolation) * pg->camera_pos; RwMatrix* pm = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); pm->pos = TheCamera.GetPosition(); pm->at = TheCamera.GetForward(); pm->up = TheCamera.GetUp(); pm->right = TheCamera.GetRight(); CameraFocusX = split * CameraFocusX + interpolation * pg->player_pos.x; CameraFocusY = split * CameraFocusY + interpolation * pg->player_pos.y; CameraFocusZ = split * CameraFocusZ + interpolation * pg->player_pos.z; bPlayerInRCBuggy = pg->in_rcvehicle; buffer->m_nOffset += sizeof(tGeneralPacket); break; } case REPLAYPACKET_CLOCK: { tClockPacket* pc = (tClockPacket*)&ptr[offset]; CClock::SetGameClock(pc->hours, pc->minutes); buffer->m_nOffset += sizeof(tClockPacket); break; } case REPLAYPACKET_WEATHER: { tWeatherPacket* pw = (tWeatherPacket*)&ptr[offset]; CWeather::OldWeatherType = pw->old_weather; CWeather::NewWeatherType = pw->new_weather; CWeather::InterpolationValue = pw->interpolation; buffer->m_nOffset += sizeof(tWeatherPacket); break; } case REPLAYPACKET_ENDOFFRAME: { /* Not supposed to be here. */ assert(false); buffer->m_nOffset++; break; } case REPLAYPACKET_TIMER: { tTimerPacket* pt = (tTimerPacket*)&ptr[offset]; if (pTimer) *pTimer = pt->timer; CTimer::SetTimeInMilliseconds(pt->timer); buffer->m_nOffset += sizeof(tTimerPacket); break; } case REPLAYPACKET_BULLET_TRACES: { tBulletTracePacket* pb = (tBulletTracePacket*)&ptr[offset]; CBulletTraces::aTraces[pb->index].m_bInUse = true; CBulletTraces::aTraces[pb->index].m_vecStartPos = pb->inf; CBulletTraces::aTraces[pb->index].m_vecEndPos = pb->sup; buffer->m_nOffset += sizeof(tBulletTracePacket); break; } case REPLAYPACKET_PARTICLE: { tParticlePacket* pp = (tParticlePacket*)&ptr[offset]; CVector pos(pp->pos_x / 4.0f, pp->pos_y / 4.0f, pp->pos_z / 4.0f); CVector dir(pp->dir_x / 120.0f, pp->dir_y / 120.0f, pp->dir_z / 120.0f); RwRGBA color; color.red = pp->r; color.green = pp->g; color.blue = pp->b; color.alpha = pp->a; CParticle::AddParticle((tParticleType)pp->particle_type, pos, dir, nil, pp->size, color); buffer->m_nOffset += sizeof(tParticlePacket); break; } case REPLAYPACKET_MISC: { tMiscPacket* pm = (tMiscPacket*)&ptr[offset]; TheCamera.m_uiCamShakeStart = pm->cam_shake_start; TheCamera.m_fCamShakeForce = pm->cam_shake_strength; CSpecialFX::bVideoCam = pm->video_cam; CSpecialFX::bLiftCam = pm->lift_cam; CGame::currArea = pm->cur_area; buffer->m_nOffset += sizeof(tMiscPacket); break; } default: break; } } buffer->m_nOffset += 4; for (int i = vehicle_min_index; i < CPools::GetVehiclePool()->GetSize(); i++) { CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); if (!v) continue; /* Removing vehicles not present in this frame. */ CWorld::Remove(v); delete v; } for (int i = ped_min_index; i < CPools::GetPedPool()->GetSize(); i++) { CPed* p = CPools::GetPedPool()->GetSlot(i); if (!p) continue; /* Removing peds not present in this frame. */ CWorld::Remove(p); delete p; } ProcessReplayCamera(); return false; } void CReplay::FinishPlayback(void) { if (Mode != MODE_PLAYBACK) return; EmptyAllPools(); RestoreStuffFromMem(); Mode = MODE_RECORD; if (bDoLoadSceneWhenDone){ CVector v_ls(LoadSceneX, LoadSceneY, LoadSceneZ); CGame::currLevel = CTheZones::GetLevelFromPosition(&v_ls); CCollision::SortOutCollisionAfterLoad(); CStreaming::LoadScene(v_ls); } bDoLoadSceneWhenDone = false; if (bPlayingBackFromFile){ Init(); MarkEverythingAsNew(); } DMAudio.SetEffectsFadeVol(127); DMAudio.SetMusicFadeVol(127); } void CReplay::EmptyReplayBuffer(void) { if (Mode == MODE_PLAYBACK) return; Record.m_nOffset = 0; for (int i = 0; i < NUM_REPLAYBUFFERS; i++){ BufferStatus[i] = REPLAYBUFFER_UNUSED; } Record.m_bSlot = 0; Record.m_pBase = Buffers[0]; BufferStatus[0] = REPLAYBUFFER_RECORD; Record.m_pBase[Record.m_nOffset] = 0; MarkEverythingAsNew(); } void CReplay::ProcessReplayCamera(void) { switch (CameraMode) { case REPLAYCAMMODE_TOPDOWN: { TheCamera.SetPosition(CameraFocusX, CameraFocusY, CameraFocusZ + 15.0f); TheCamera.GetForward() = CVector(0.0f, 0.0f, -1.0f); TheCamera.GetUp() = CVector(0.0f, 1.0f, 0.0f); TheCamera.GetRight() = CVector(1.0f, 0.0f, 0.0f); RwMatrix* pm = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); pm->pos = TheCamera.GetPosition(); pm->at = TheCamera.GetForward(); pm->up = TheCamera.GetUp(); pm->right = TheCamera.GetRight(); break; } case REPLAYCAMMODE_FIXED: { TheCamera.GetMatrix().GetPosition() = CVector(CameraFixedX, CameraFixedY, CameraFixedZ); CVector forward(CameraFocusX - CameraFixedX, CameraFocusY - CameraFixedY, CameraFocusZ - CameraFixedZ); forward.Normalise(); CVector right = CrossProduct(CVector(0.0f, 0.0f, 1.0f), forward); right.Normalise(); CVector up = CrossProduct(forward, right); up.Normalise(); TheCamera.GetMatrix().GetForward() = forward; TheCamera.GetMatrix().GetUp() = up; TheCamera.GetMatrix().GetRight() = right; RwMatrix* pm = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); pm->pos = TheCamera.GetMatrix().GetPosition(); pm->at = TheCamera.GetMatrix().GetForward(); pm->up = TheCamera.GetMatrix().GetUp(); pm->right = TheCamera.GetMatrix().GetRight(); break; } default: break; } TheCamera.m_vecGameCamPos = TheCamera.GetMatrix().GetPosition(); TheCamera.CalculateDerivedValues(); RwMatrixUpdate(RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera))); RwFrameUpdateObjects(RwCameraGetFrame(TheCamera.m_pRwCamera)); } extern CWeaponEffects gCrossHair; void CReplay::TriggerPlayback(uint8 cam_mode, float cam_x, float cam_y, float cam_z, bool load_scene) { if (Mode != MODE_RECORD) return; CameraFixedX = cam_x; CameraFixedY = cam_y; CameraFixedZ = cam_z; Mode = MODE_PLAYBACK; FramesActiveLookAroundCam = 0; CameraMode = cam_mode; bAllowLookAroundCam = true; bPlayingBackFromFile = false; OldRadioStation = DMAudio.GetRadioInCar(); DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); DMAudio.SetEffectsFadeVol(0); DMAudio.SetMusicFadeVol(0); CEscalators::Shutdown(); CWaterCreatures::RemoveAll(); int current; for (current = 0; current < NUM_REPLAYBUFFERS; current++) if (BufferStatus[current] == REPLAYBUFFER_RECORD) break; int first; for (first = (current + 1) % NUM_REPLAYBUFFERS; ; first = (first + 1) % NUM_REPLAYBUFFERS) if (BufferStatus[first] == REPLAYBUFFER_RECORD || BufferStatus[first] == REPLAYBUFFER_PLAYBACK) break; Playback.m_bSlot = first; Playback.m_nOffset = 0; Playback.m_pBase = Buffers[first]; CObject::DeleteAllTempObjectsInArea(CVector(0.0f, 0.0f, 0.0f), 1000000.0f); StoreStuffInMem(); EmptyPedsAndVehiclePools(); SlowMotion = 1; CSkidmarks::Clear(); StreamAllNecessaryCarsAndPeds(); if (load_scene) bDoLoadSceneWhenDone = false; else{ bDoLoadSceneWhenDone = true; LoadSceneX = TheCamera.GetPosition().x; LoadSceneY = TheCamera.GetPosition().y; LoadSceneZ = TheCamera.GetPosition().z; CVector ff_coord; FindFirstFocusCoordinate(&ff_coord); CGame::currLevel = CTheZones::GetLevelFromPosition(&ff_coord); CCollision::SortOutCollisionAfterLoad(); CStreaming::LoadScene(ff_coord); } if (cam_mode == REPLAYCAMMODE_ASSTORED) TheCamera.CarZoomIndicator = CAM_ZOOM_CINEMATIC; gCrossHair.m_bActive = false; CExplosion::ClearAllExplosions(); CPlaneBanners::Init(); #ifndef FIX_BUGS // this doesn't do anything useful and accesses destroyed player ped TheCamera.Restore(); #endif CDraw::SetFOV(70.0f); } void CReplay::StoreStuffInMem(void) { #ifdef FIX_BUGS for (int i = 0; i < NUMPLAYERS; i++) nHandleOfPlayerPed[i] = CPools::GetPedPool()->GetIndex(CWorld::Players[i].m_pPed); #endif int i = CPools::GetPedPool()->GetSize(); while (--i >= 0) { CPed* ped = CPools::GetPedPool()->GetSlot(i); if (!ped) continue; if (ped->m_attractor) GetPedAttractorManager()->DeRegisterPed(ped, ped->m_attractor); } CPools::GetVehiclePool()->Store(pBuf0, pBuf1); CPools::GetPedPool()->Store(pBuf2, pBuf3); CPools::GetObjectPool()->Store(pBuf4, pBuf5); CPools::GetPtrNodePool()->Store(pBuf6, pBuf7); CPools::GetEntryInfoNodePool()->Store(pBuf8, pBuf9); CPools::GetDummyPool()->Store(pBuf10, pBuf11); pWorld1 = new uint8[sizeof(CSector) * NUMSECTORS_X * NUMSECTORS_Y]; memcpy(pWorld1, CWorld::GetSector(0, 0), NUMSECTORS_X * NUMSECTORS_Y * sizeof(CSector)); WorldPtrList = CWorld::GetMovingEntityList().first; // why BigBuildingPtrList = CWorld::GetBigBuildingList(LEVEL_GENERIC).first; pPickups = new uint8[sizeof(CPickup) * NUMPICKUPS]; memcpy(pPickups, CPickups::aPickUps, NUMPICKUPS * sizeof(CPickup)); pReferences = new uint8[(sizeof(CReference) * NUMREFERENCES)]; memcpy(pReferences, CReferences::aRefs, NUMREFERENCES * sizeof(CReference)); pEmptyReferences = CReferences::pEmptyList; pStoredCam = new uint8[sizeof(CCamera)]; memcpy(pStoredCam, &TheCamera, sizeof(CCamera)); pRadarBlips = new uint8[sizeof(sRadarTrace) * NUMRADARBLIPS]; memcpy(pRadarBlips, CRadar::ms_RadarTrace, NUMRADARBLIPS * sizeof(sRadarTrace)); PlayerWanted = *FindPlayerPed()->m_pWanted; PlayerInfo = CWorld::Players[0]; Time1 = CTimer::GetTimeInMilliseconds(); Time2 = CTimer::GetTimeInMillisecondsNonClipped(); Time3 = CTimer::GetPreviousTimeInMilliseconds(); Time4 = CTimer::GetTimeInMillisecondsPauseMode(); Frame = CTimer::GetFrameCounter(); ClockHours = CClock::GetHours(); ClockMinutes = CClock::GetMinutes(); OldWeatherType = CWeather::OldWeatherType; NewWeatherType = CWeather::NewWeatherType; WeatherInterpolationValue = CWeather::InterpolationValue; CurrArea = CGame::currArea; TimeStepNonClipped = CTimer::GetTimeStepNonClipped(); TimeStep = CTimer::GetTimeStep(); TimeScale = CTimer::GetTimeScale(); ms_nNumCivMale_Stored = CPopulation::ms_nNumCivMale; ms_nNumCivFemale_Stored = CPopulation::ms_nNumCivFemale; ms_nNumCop_Stored = CPopulation::ms_nNumCop; ms_nNumEmergency_Stored = CPopulation::ms_nNumEmergency; ms_nNumGang1_Stored = CPopulation::ms_nNumGang1; ms_nNumGang2_Stored = CPopulation::ms_nNumGang2; ms_nNumGang3_Stored = CPopulation::ms_nNumGang3; ms_nNumGang4_Stored = CPopulation::ms_nNumGang4; ms_nNumGang5_Stored = CPopulation::ms_nNumGang5; ms_nNumGang6_Stored = CPopulation::ms_nNumGang6; ms_nNumGang7_Stored = CPopulation::ms_nNumGang7; ms_nNumGang8_Stored = CPopulation::ms_nNumGang8; ms_nNumGang9_Stored = CPopulation::ms_nNumGang9; ms_nNumDummy_Stored = CPopulation::ms_nNumDummy; ms_nTotalCivPeds_Stored = CPopulation::ms_nTotalCivPeds; ms_nTotalGangPeds_Stored = CPopulation::ms_nTotalGangPeds; ms_nTotalPeds_Stored = CPopulation::ms_nTotalPeds; ms_nTotalMissionPeds_Stored = CPopulation::ms_nTotalMissionPeds; int size = CPools::GetPedPool()->GetSize(); pPedAnims = new CStoredDetailedAnimationState[size]; for (int i = 0; i < size; i++) { CPed* ped = CPools::GetPedPool()->GetSlot(i); if (ped) StoreDetailedPedAnimation(ped, &pPedAnims[i]); } pGarages = new uint8[sizeof(CGarages::aGarages)]; memcpy(pGarages, CGarages::aGarages, sizeof(CGarages::aGarages)); FireArray = new CFire[NUM_FIRES]; memcpy(FireArray, gFireManager.m_aFires, sizeof(gFireManager.m_aFires)); NumOfFires = gFireManager.m_nTotalFires; paProjectileInfo = new uint8[sizeof(gaProjectileInfo)]; memcpy(paProjectileInfo, gaProjectileInfo, sizeof(gaProjectileInfo)); paProjectiles = new uint8[sizeof(CProjectileInfo::ms_apProjectile)]; memcpy(paProjectiles, CProjectileInfo::ms_apProjectile, sizeof(CProjectileInfo::ms_apProjectile)); CScriptPaths::Save_ForReplay(); } void CReplay::RestoreStuffFromMem(void) { CPools::GetVehiclePool()->CopyBack(pBuf0, pBuf1); CPools::GetPedPool()->CopyBack(pBuf2, pBuf3); CPools::GetObjectPool()->CopyBack(pBuf4, pBuf5); CPools::GetPtrNodePool()->CopyBack(pBuf6, pBuf7); CPools::GetEntryInfoNodePool()->CopyBack(pBuf8, pBuf9); CPools::GetDummyPool()->CopyBack(pBuf10, pBuf11); memcpy(CWorld::GetSector(0, 0), pWorld1, sizeof(CSector) * NUMSECTORS_X * NUMSECTORS_Y); delete[] pWorld1; pWorld1 = nil; CWorld::GetMovingEntityList().first = WorldPtrList; CWorld::GetBigBuildingList(LEVEL_GENERIC).first = BigBuildingPtrList; memcpy(CPickups::aPickUps, pPickups, sizeof(CPickup) * NUMPICKUPS); delete[] pPickups; pPickups = nil; memcpy(CReferences::aRefs, pReferences, sizeof(CReference) * NUMREFERENCES); delete[] pReferences; pReferences = nil; CReferences::pEmptyList = pEmptyReferences; pEmptyReferences = nil; memcpy(&TheCamera, pStoredCam, sizeof(CCamera)); delete[] pStoredCam; pStoredCam = nil; memcpy(CRadar::ms_RadarTrace, pRadarBlips, sizeof(sRadarTrace) * NUMRADARBLIPS); delete[] pRadarBlips; pRadarBlips = nil; #ifdef FIX_BUGS for (int i = 0; i < NUMPLAYERS; i++) { CPlayerPed* pPlayerPed = (CPlayerPed*)CPools::GetPedPool()->GetAt(nHandleOfPlayerPed[i]); assert(pPlayerPed); CWorld::Players[i].m_pPed = pPlayerPed; pPlayerPed->RegisterReference((CEntity**)&CWorld::Players[i].m_pPed); } #endif FindPlayerPed()->m_pWanted = new CWanted(PlayerWanted); CWorld::Players[0] = PlayerInfo; int i = CPools::GetPedPool()->GetSize(); while (--i >= 0) { CPed* ped = CPools::GetPedPool()->GetSlot(i); if (!ped) continue; int mi = ped->GetModelIndex(); CStreaming::RequestModel(mi, 0); CStreaming::LoadAllRequestedModels(false); ped->m_rwObject = nil; ped->m_modelIndex = -1; ped->SetModelIndex(mi); ped->m_pVehicleAnim = nil; ped->m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, ped); DMAudio.SetEntityStatus(ped->m_audioEntityId, true); CPopulation::UpdatePedCount((ePedType)ped->m_nPedType, false); for (int j = 0; j < TOTAL_WEAPON_SLOTS; j++) { int mi1 = CWeaponInfo::GetWeaponInfo(ped->m_weapons[j].m_eWeaponType)->m_nModelId; if (mi1 != -1) CStreaming::RequestModel(mi1, STREAMFLAGS_DEPENDENCY); int mi2 = CWeaponInfo::GetWeaponInfo(ped->m_weapons[j].m_eWeaponType)->m_nModel2Id; if (mi2 != -1) CStreaming::RequestModel(mi2, STREAMFLAGS_DEPENDENCY); CStreaming::LoadAllRequestedModels(false); ped->m_weapons[j].Initialise(ped->m_weapons[j].m_eWeaponType, ped->m_weapons[j].m_nAmmoTotal); } if (ped->m_wepModelID >= 0) { ped->m_pWeaponModel = nil; if (ped->IsPlayer()) ((CPlayerPed*)ped)->m_pMinigunTopAtomic = nil; ped->AddWeaponModel(ped->m_wepModelID); } if (ped->m_nPedType == PEDTYPE_COP) ((CCopPed*)ped)->m_pStinger = new CStinger; } i = CPools::GetVehiclePool()->GetSize(); while (--i >= 0) { CVehicle* vehicle = CPools::GetVehiclePool()->GetSlot(i); if (!vehicle) continue; int mi = vehicle->GetModelIndex(); CStreaming::RequestModel(mi, 0); CStreaming::LoadAllRequestedModels(false); vehicle->m_rwObject = nil; vehicle->m_modelIndex = -1; vehicle->SetModelIndex(mi); if (vehicle->IsCar()) { CAutomobile* car = (CAutomobile*)vehicle; if (mi == MI_DODO) { RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_LF]), 0); CMatrix tmp1; tmp1.Attach(RwFrameGetMatrix(car->m_aCarNodes[CAR_WHEEL_RF]), false); CMatrix tmp2(RwFrameGetMatrix(car->m_aCarNodes[CAR_WHEEL_LF]), false); tmp1.GetPosition() += CVector(tmp2.GetPosition().x + 0.1f, 0.0f, tmp2.GetPosition().z); tmp1.UpdateRW(); } else if (mi == MI_HUNTER) { RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_LB]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_RB]), 0); } else if (vehicle->IsRealHeli()) { RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_LF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_RF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_LB]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(car->m_aCarNodes[CAR_WHEEL_RB]), 0); } } if (vehicle->IsCar()){ CAutomobile* car = (CAutomobile*)vehicle; int32 panels = car->Damage.m_panelStatus; car->Damage.m_panelStatus = 0; ApplyPanelDamageToCar(panels, car, true); car->SetDoorDamage(CAR_BONNET, DOOR_BONNET, true); car->SetDoorDamage(CAR_BOOT, DOOR_BOOT, true); car->SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT, true); car->SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT, true); car->SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT, true); car->SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT, true); } vehicle->m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, vehicle); DMAudio.SetEntityStatus(vehicle->m_audioEntityId, true); CCarCtrl::UpdateCarCount(vehicle, false); if ((mi == MI_AIRTRAIN || mi == MI_DEADDODO) && vehicle->m_rwObject){ CVehicleModelInfo* info = (CVehicleModelInfo*)CModelInfo::GetModelInfo(mi); if (RwObjectGetType(vehicle->m_rwObject) == rpATOMIC){ vehicle->GetMatrix().Detach(); if (vehicle->m_rwObject){ if (RwObjectGetType(vehicle->m_rwObject) == rpATOMIC){ RwFrame* frame = RpAtomicGetFrame((RpAtomic*)vehicle->m_rwObject); RpAtomicDestroy((RpAtomic*)vehicle->m_rwObject); RwFrameDestroy(frame); } vehicle->m_rwObject = nil; } }else{ vehicle->DeleteRwObject(); int model_id = info->m_wheelId; if (model_id != -1){ if ((vehicle->m_rwObject = CModelInfo::GetModelInfo(model_id)->CreateInstance())){ vehicle->GetMatrix().AttachRW(RwFrameGetMatrix(RpClumpGetFrame((RpClump*)vehicle->m_rwObject)), false); } } } } } PrintElementsInPtrList(); i = CPools::GetObjectPool()->GetSize(); while (--i >= 0) { CObject* object = CPools::GetObjectPool()->GetSlot(i); if (!object) continue; int mi = object->GetModelIndex(); object->m_rwObject = nil; object->m_modelIndex = -1; object->SetModelIndexNoCreate(mi); object->GetMatrix().m_attachment = nil; } i = CPools::GetDummyPool()->GetSize(); while (--i >= 0) { CDummy* dummy = CPools::GetDummyPool()->GetSlot(i); if (!dummy) continue; int mi = dummy->GetModelIndex(); dummy->m_rwObject = nil; dummy->m_modelIndex = -1; dummy->SetModelIndexNoCreate(mi); dummy->GetMatrix().m_attachment = nil; } ++ClockMinutes; CTimer::SetTimeInMilliseconds(Time1); CTimer::SetTimeInMillisecondsNonClipped(Time2); CTimer::SetPreviousTimeInMilliseconds(Time3); CTimer::SetTimeInMillisecondsPauseMode(Time4); CTimer::SetTimeScale(TimeScale); CTimer::SetFrameCounter(Frame); CTimer::SetTimeStep(TimeStep); CTimer::SetTimeStepNonClipped(TimeStepNonClipped); CClock::SetGameClock(ClockHours, ClockMinutes); CWeather::OldWeatherType = OldWeatherType; CWeather::NewWeatherType = NewWeatherType; CWeather::InterpolationValue = WeatherInterpolationValue; CGame::currArea = CurrArea; CPopulation::ms_nNumCivMale = ms_nNumCivMale_Stored; CPopulation::ms_nNumCivFemale = ms_nNumCivFemale_Stored; CPopulation::ms_nNumCop = ms_nNumCop_Stored; CPopulation::ms_nNumEmergency = ms_nNumEmergency_Stored; CPopulation::ms_nNumGang1 = ms_nNumGang1_Stored; CPopulation::ms_nNumGang2 = ms_nNumGang2_Stored; CPopulation::ms_nNumGang3 = ms_nNumGang3_Stored; CPopulation::ms_nNumGang4 = ms_nNumGang4_Stored; CPopulation::ms_nNumGang5 = ms_nNumGang5_Stored; CPopulation::ms_nNumGang6 = ms_nNumGang6_Stored; CPopulation::ms_nNumGang7 = ms_nNumGang7_Stored; CPopulation::ms_nNumGang8 = ms_nNumGang8_Stored; CPopulation::ms_nNumGang9 = ms_nNumGang9_Stored; CPopulation::ms_nNumDummy = ms_nNumDummy_Stored; CPopulation::ms_nTotalCivPeds = ms_nTotalCivPeds_Stored; CPopulation::ms_nTotalGangPeds = ms_nTotalGangPeds_Stored; CPopulation::ms_nTotalPeds = ms_nTotalPeds_Stored; CPopulation::ms_nTotalMissionPeds = ms_nTotalMissionPeds_Stored; for (int i = 0; i < CPools::GetPedPool()->GetSize(); i++) { CPed* ped = CPools::GetPedPool()->GetSlot(i); if (!ped) continue; RetrieveDetailedPedAnimation(ped, &pPedAnims[i]); } delete[] pPedAnims; pPedAnims = nil; memcpy(CGarages::aGarages, pGarages, sizeof(CGarages::aGarages)); delete[] pGarages; pGarages = nil; memcpy(gFireManager.m_aFires, FireArray, sizeof(gFireManager.m_aFires)); delete[] FireArray; FireArray = nil; gFireManager.m_nTotalFires = NumOfFires; memcpy(gaProjectileInfo, paProjectileInfo, sizeof(gaProjectileInfo)); delete[] paProjectileInfo; paProjectileInfo = nil; memcpy(CProjectileInfo::ms_apProjectile, paProjectiles, sizeof(CProjectileInfo::ms_apProjectile)); delete[] paProjectiles; paProjectiles = nil; CScriptPaths::Load_ForReplay(); CExplosion::ClearAllExplosions(); DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); DMAudio.SetRadioInCar(OldRadioStation); DMAudio.ChangeMusicMode(MUSICMODE_GAME); } void CReplay::EmptyPedsAndVehiclePools(void) { int i = CPools::GetVehiclePool()->GetSize(); while (--i >= 0) { CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); if (!v) continue; CWorld::Remove(v); delete v; } i = CPools::GetPedPool()->GetSize(); while (--i >= 0) { CPed* p = CPools::GetPedPool()->GetSlot(i); if (!p) continue; CWorld::Remove(p); delete p; } } void CReplay::EmptyAllPools(void) { EmptyPedsAndVehiclePools(); int i = CPools::GetObjectPool()->GetSize(); while (--i >= 0) { CObject* o = CPools::GetObjectPool()->GetSlot(i); if (!o) continue; CWorld::Remove(o); delete o; } i = CPools::GetDummyPool()->GetSize(); while (--i >= 0) { CDummy* d = CPools::GetDummyPool()->GetSlot(i); if (!d) continue; CWorld::Remove(d); delete d; } } void CReplay::MarkEverythingAsNew(void) { int i = CPools::GetVehiclePool()->GetSize(); while (--i >= 0) { CVehicle* v = CPools::GetVehiclePool()->GetSlot(i); if (!v) continue; v->bHasAlreadyBeenRecorded = false; } i = CPools::GetPedPool()->GetSize(); while (--i >= 0) { CPed* p = CPools::GetPedPool()->GetSlot(i); if (!p) continue; p->bHasAlreadyBeenRecorded = false; } } void CReplay::SaveReplayToHD(void) { CFileMgr::SetDirMyDocuments(); int fw = CFileMgr::OpenFileForWriting("replay.rep"); #ifdef FIX_REPLAY_BUGS if (fw == 0) { #else if (fw < 0){ // BUG? #endif printf("Couldn't open replay.rep for writing"); CFileMgr::SetDir(""); return; } CFileMgr::Write(fw, "gta3_7f", sizeof("gta3_7f")); int current; for (current = 0; current < NUM_REPLAYBUFFERS; current++) if (BufferStatus[current] == REPLAYBUFFER_RECORD) break; int first; for (first = (current + 1) % NUM_REPLAYBUFFERS; ; first = (first + 1) % NUM_REPLAYBUFFERS) if (BufferStatus[first] == REPLAYBUFFER_RECORD || BufferStatus[first] == REPLAYBUFFER_PLAYBACK) break; for(int i = first; ; i = (i + 1) % NUM_REPLAYBUFFERS){ CFileMgr::Write(fw, (char*)Buffers[i], sizeof(Buffers[i])); if (BufferStatus[i] == REPLAYBUFFER_RECORD) break; } CFileMgr::CloseFile(fw); CFileMgr::SetDir(""); } void CReplay::PlayReplayFromHD(void) { CFileMgr::SetDirMyDocuments(); int fr = CFileMgr::OpenFile("replay.rep", "rb"); if (fr == 0) { printf("Couldn't open replay.rep for reading"); #ifdef FIX_REPLAY_BUGS CFileMgr::SetDir(""); #endif return; } CFileMgr::Read(fr, gString, 8); if (strncmp(gString, "gta3_7f", sizeof("gta3_7f"))){ CFileMgr::CloseFile(fr); printf("Wrong file type for replay"); CFileMgr::SetDir(""); return; } int slot; for (slot = 0; CFileMgr::Read(fr, (char*)Buffers[slot], sizeof(Buffers[slot])); slot++) BufferStatus[slot] = REPLAYBUFFER_PLAYBACK; BufferStatus[slot - 1] = REPLAYBUFFER_RECORD; while (slot < NUM_REPLAYBUFFERS) BufferStatus[slot++] = REPLAYBUFFER_UNUSED; CFileMgr::CloseFile(fr); CFileMgr::SetDir(""); TriggerPlayback(REPLAYCAMMODE_ASSTORED, 0.0f, 0.0f, 0.0f, false); bPlayingBackFromFile = true; bAllowLookAroundCam = true; StreamAllNecessaryCarsAndPeds(); } void CReplay::StreamAllNecessaryCarsAndPeds(void) { for (int slot = 0; slot < NUM_REPLAYBUFFERS; slot++) { if (BufferStatus[slot] == REPLAYBUFFER_UNUSED) continue; for (size_t offset = 0; Buffers[slot][offset] != REPLAYPACKET_END; offset += FindSizeOfPacket(Buffers[slot][offset])) { switch (Buffers[slot][offset]) { case REPLAYPACKET_VEHICLE: CStreaming::RequestModel(((tVehicleUpdatePacket*)&Buffers[slot][offset])->mi, 0); break; case REPLAYPACKET_BIKE: CStreaming::RequestModel(((tBikeUpdatePacket*)&Buffers[slot][offset])->mi, 0); break; case REPLAYPACKET_PED_HEADER: CStreaming::RequestModel(((tPedHeaderPacket*)&Buffers[slot][offset])->mi, 0); break; default: break; } } } CStreaming::LoadAllRequestedModels(false); } void CReplay::FindFirstFocusCoordinate(CVector *coord) { *coord = CVector(0.0f, 0.0f, 0.0f); for (int slot = 0; slot < NUM_REPLAYBUFFERS; slot++) { if (BufferStatus[slot] == REPLAYBUFFER_UNUSED) continue; for (size_t offset = 0; Buffers[slot][offset] != REPLAYPACKET_END; offset += FindSizeOfPacket(Buffers[slot][offset])) { if (Buffers[slot][offset] == REPLAYPACKET_GENERAL) { *coord = ((tGeneralPacket*)&Buffers[slot][offset])->player_pos; return; } } } } bool CReplay::ShouldStandardCameraBeProcessed(void) { if (Mode != MODE_PLAYBACK) return true; if (FramesActiveLookAroundCam || bPlayerInRCBuggy) return false; return FindPlayerVehicle() != nil; } void CReplay::ProcessLookAroundCam(void) { if (!bAllowLookAroundCam) return; float x_moved = CPad::NewMouseControllerState.x / 200.0f; float y_moved = CPad::NewMouseControllerState.y / 200.0f; if (x_moved > 0.01f || y_moved > 0.01f) { if (FramesActiveLookAroundCam == 0) fDistanceLookAroundCam = 9.0f; FramesActiveLookAroundCam = 60; } if (bPlayerInRCBuggy) FramesActiveLookAroundCam = 0; if (!FramesActiveLookAroundCam) return; --FramesActiveLookAroundCam; fBetaAngleLookAroundCam += x_moved; if (CPad::NewMouseControllerState.LMB && CPad::NewMouseControllerState.RMB) fDistanceLookAroundCam = Max(3.0f, Min(15.0f, fDistanceLookAroundCam + 2.0f * y_moved)); else fAlphaAngleLookAroundCam = Max(0.1f, Min(1.5f, fAlphaAngleLookAroundCam + y_moved)); CVector camera_pt( fDistanceLookAroundCam * Sin(fBetaAngleLookAroundCam) * Cos(fAlphaAngleLookAroundCam), fDistanceLookAroundCam * Cos(fBetaAngleLookAroundCam) * Cos(fAlphaAngleLookAroundCam), fDistanceLookAroundCam * Sin(fAlphaAngleLookAroundCam) ); CVector focus = CVector(CameraFocusX, CameraFocusY, CameraFocusZ); camera_pt += focus; CColPoint cp; CEntity* pe = nil; if (CWorld::ProcessLineOfSight(focus, camera_pt, cp, pe, true, false, false, false, false, true, true)){ camera_pt = cp.point; CVector direction = focus - cp.point; direction.Normalise(); camera_pt += direction / 4.0f; } CVector forward = focus - camera_pt; forward.Normalise(); CVector right = CrossProduct(CVector(0.0f, 0.0f, 1.0f), forward); right.Normalise(); CVector up = CrossProduct(forward, right); up.Normalise(); TheCamera.GetForward() = forward; TheCamera.GetUp() = up; TheCamera.GetRight() = right; TheCamera.SetPosition(camera_pt); RwMatrix* pm = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); pm->pos = TheCamera.GetPosition(); pm->at = TheCamera.GetForward(); pm->up = TheCamera.GetUp(); pm->right = TheCamera.GetRight(); TheCamera.CalculateDerivedValues(); RwMatrixUpdate(RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera))); RwFrameUpdateObjects(RwCameraGetFrame(TheCamera.m_pRwCamera)); } size_t CReplay::FindSizeOfPacket(uint8 type) { switch (type) { case REPLAYPACKET_END: return 4; case REPLAYPACKET_VEHICLE: return sizeof(tVehicleUpdatePacket); case REPLAYPACKET_BIKE: return sizeof(tBikeUpdatePacket); case REPLAYPACKET_PED_HEADER: return sizeof(tPedHeaderPacket); case REPLAYPACKET_PED_UPDATE: return sizeof(tPedUpdatePacket); case REPLAYPACKET_GENERAL: return sizeof(tGeneralPacket); case REPLAYPACKET_CLOCK: return sizeof(tClockPacket); case REPLAYPACKET_WEATHER: return sizeof(tWeatherPacket); case REPLAYPACKET_ENDOFFRAME: return 4; case REPLAYPACKET_TIMER: return sizeof(tTimerPacket); case REPLAYPACKET_BULLET_TRACES:return sizeof(tBulletTracePacket); case REPLAYPACKET_PARTICLE: return sizeof(tParticlePacket); case REPLAYPACKET_MISC: return sizeof(tMiscPacket); default: assert(false); break; } return 0; } void CReplay::Display() { static int TimeCount = 0; if (Mode == MODE_RECORD) return; TimeCount = (TimeCount + 1) % UINT16_MAX; if ((TimeCount & 0x20) == 0) return; CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f)); CFont::SetJustifyOff(); CFont::SetBackgroundOff(); #ifdef FIX_BUGS CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-20)); #else CFont::SetCentreSize(SCREEN_WIDTH-20); #endif CFont::SetCentreOff(); CFont::SetPropOn(); CFont::SetColor(CRGBA(255, 255, 200, 200)); CFont::SetFontStyle(FONT_STANDARD); if (Mode == MODE_PLAYBACK) CFont::PrintString(SCREEN_WIDTH/15, SCREEN_HEIGHT/10, TheText.Get("REPLAY")); } #endif ================================================ FILE: src/control/Replay.h ================================================ #pragma once #include "Pools.h" #include "World.h" #include "WeaponEffects.h" #include "ParticleType.h" #ifdef FIX_BUGS #ifndef DONT_FIX_REPLAY_BUGS #define FIX_REPLAY_BUGS #endif #endif class CVehicle; struct CReference; struct CAddressInReplayBuffer { uint32 m_nOffset; uint8 *m_pBase; uint8 m_bSlot; }; struct CStoredAnimationState { uint8 animId; uint8 time; uint8 speed; uint8 groupId; uint8 secAnimId; uint8 secTime; uint8 secSpeed; uint8 secGroupId; uint8 blendAmount; uint8 partAnimId; uint8 partAnimTime; uint8 partAnimSpeed; uint8 partBlendAmount; uint8 partGroupId; }; enum { NUM_MAIN_ANIMS_IN_REPLAY = 3, NUM_PARTIAL_ANIMS_IN_REPLAY = 6 }; struct CStoredDetailedAnimationState { uint8 aAnimId[NUM_MAIN_ANIMS_IN_REPLAY]; uint8 aCurTime[NUM_MAIN_ANIMS_IN_REPLAY]; uint8 aSpeed[NUM_MAIN_ANIMS_IN_REPLAY]; uint8 aBlendAmount[NUM_MAIN_ANIMS_IN_REPLAY]; int8 aBlendDelta[NUM_MAIN_ANIMS_IN_REPLAY]; uint8 aFunctionCallbackID[NUM_MAIN_ANIMS_IN_REPLAY]; uint16 aFlags[NUM_MAIN_ANIMS_IN_REPLAY]; uint8 aGroupId[NUM_MAIN_ANIMS_IN_REPLAY]; uint8 aAnimId2[NUM_PARTIAL_ANIMS_IN_REPLAY]; uint8 aCurTime2[NUM_PARTIAL_ANIMS_IN_REPLAY]; uint8 aSpeed2[NUM_PARTIAL_ANIMS_IN_REPLAY]; uint8 aBlendAmount2[NUM_PARTIAL_ANIMS_IN_REPLAY]; int8 aBlendDelta2[NUM_PARTIAL_ANIMS_IN_REPLAY]; uint8 aFunctionCallbackID2[NUM_PARTIAL_ANIMS_IN_REPLAY]; uint16 aFlags2[NUM_PARTIAL_ANIMS_IN_REPLAY]; uint8 aGroupId2[NUM_PARTIAL_ANIMS_IN_REPLAY]; }; #ifdef GTA_REPLAY #define REPLAY_STUB #else #define REPLAY_STUB {} #endif class CReplay { enum { MODE_RECORD = 0, MODE_PLAYBACK = 1 }; enum { REPLAYCAMMODE_ASSTORED = 0, REPLAYCAMMODE_TOPDOWN, REPLAYCAMMODE_FIXED }; enum { REPLAYPACKET_END = 0, REPLAYPACKET_VEHICLE, REPLAYPACKET_BIKE, REPLAYPACKET_PED_HEADER, REPLAYPACKET_PED_UPDATE, REPLAYPACKET_GENERAL, REPLAYPACKET_CLOCK, REPLAYPACKET_WEATHER, REPLAYPACKET_ENDOFFRAME, REPLAYPACKET_TIMER, REPLAYPACKET_BULLET_TRACES, REPLAYPACKET_PARTICLE, REPLAYPACKET_MISC }; enum { REPLAYBUFFER_UNUSED = 0, REPLAYBUFFER_PLAYBACK = 1, REPLAYBUFFER_RECORD = 2 }; enum { NUM_REPLAYBUFFERS = 8, REPLAYBUFFERSIZE = 100000 }; struct tGeneralPacket { uint8 type; bool in_rcvehicle; CMatrix camera_pos; CVector player_pos; }; VALIDATE_SIZE(tGeneralPacket, 88); struct tClockPacket { uint8 type; uint8 hours; uint8 minutes; private: uint8 __align; }; VALIDATE_SIZE(tClockPacket, 4); struct tWeatherPacket { uint8 type; uint8 old_weather; uint8 new_weather; float interpolation; }; VALIDATE_SIZE(tWeatherPacket, 8); struct tTimerPacket { uint8 type; uint32 timer; }; VALIDATE_SIZE(tTimerPacket, 8); struct tPedHeaderPacket { uint8 type; uint8 index; uint16 mi; uint8 pedtype; private: uint8 __align[3]; }; VALIDATE_SIZE(tPedHeaderPacket, 8); struct tBulletTracePacket { uint8 type; uint8 frames; uint8 lifetime; uint8 index; CVector inf; CVector sup; }; VALIDATE_SIZE(tBulletTracePacket, 28); struct tEndOfFramePacket { uint8 type; private: uint8 __align[3]; }; VALIDATE_SIZE(tEndOfFramePacket, 4); struct tPedUpdatePacket { uint8 type; uint8 index; int8 heading; int8 vehicle_index; CStoredAnimationState anim_state; CCompressedMatrixNotAligned matrix; uint16 weapon_model; int8 assoc_group_id; bool is_visible; }; VALIDATE_SIZE(tPedUpdatePacket, 40); struct tVehicleUpdatePacket { uint8 type; uint8 index; uint8 health; uint8 acceleration; CCompressedMatrixNotAligned matrix; int8 door_angles[2]; uint16 mi; uint32 panels; int8 velocityX; int8 velocityY; int8 velocityZ; union { int8 car_gun; int8 wheel_state; }; uint8 wheel_susp_dist[4]; uint8 wheel_rotation[4]; uint8 door_status; uint8 primary_color; uint8 secondary_color; bool render_scorched; int8 skimmer_speed; int8 vehicle_type; }; VALIDATE_SIZE(tVehicleUpdatePacket, 52); struct tBikeUpdatePacket { uint8 type; uint8 index; uint8 health; uint8 acceleration; CCompressedMatrixNotAligned matrix; int8 door_angles[2]; uint16 mi; int8 velocityX; int8 velocityY; int8 velocityZ; int8 wheel_state; uint8 wheel_susp_dist[4]; uint8 wheel_rotation[4]; uint8 primary_color; uint8 secondary_color; int8 lean_angle; int8 wheel_angle; }; VALIDATE_SIZE(tBikeUpdatePacket, 44); struct tParticlePacket { uint8 type; uint8 particle_type; int8 dir_x; int8 dir_y; int8 dir_z; uint8 r; uint8 g; uint8 b; uint8 a; int16 pos_x; int16 pos_y; int16 pos_z; float size; }; VALIDATE_SIZE(tParticlePacket, 20); struct tMiscPacket { uint8 type; uint32 cam_shake_start; float cam_shake_strength; uint8 cur_area; uint8 video_cam : 1; uint8 lift_cam : 1; }; VALIDATE_SIZE(tMiscPacket, 16); private: static uint8 Mode; static CAddressInReplayBuffer Record; static CAddressInReplayBuffer Playback; static uint8* pBuf0; static CAutomobile* pBuf1; static uint8* pBuf2; static CPlayerPed* pBuf3; static uint8* pBuf4; static CCutsceneObject* pBuf5; static uint8* pBuf6; static CPtrNode* pBuf7; static uint8* pBuf8; static CEntryInfoNode* pBuf9; static uint8* pBuf10; static CDummyPed* pBuf11; static uint8* pRadarBlips; static uint8* pStoredCam; static uint8* pWorld1; static CReference* pEmptyReferences; static CStoredDetailedAnimationState* pPedAnims; static uint8* pPickups; static uint8* pReferences; static uint8 BufferStatus[NUM_REPLAYBUFFERS]; static uint8 Buffers[NUM_REPLAYBUFFERS][REPLAYBUFFERSIZE]; static bool bPlayingBackFromFile; static bool bReplayEnabled; static uint32 SlowMotion; static uint32 FramesActiveLookAroundCam; static bool bDoLoadSceneWhenDone; static CPtrNode* WorldPtrList; static CPtrNode* BigBuildingPtrList; static CWanted PlayerWanted; static CPlayerInfo PlayerInfo; static uint32 Time1; static uint32 Time2; static uint32 Time3; static uint32 Time4; static uint32 Frame; static uint8 ClockHours; static uint8 ClockMinutes; static uint16 OldWeatherType; static uint16 NewWeatherType; static float WeatherInterpolationValue; static float TimeStepNonClipped; static float TimeStep; static float TimeScale; static float CameraFixedX; static float CameraFixedY; static float CameraFixedZ; static int32 OldRadioStation; static int8 CameraMode; static bool bAllowLookAroundCam; static float LoadSceneX; static float LoadSceneY; static float LoadSceneZ; static float CameraFocusX; static float CameraFocusY; static float CameraFocusZ; static bool bPlayerInRCBuggy; static float fDistanceLookAroundCam; static float fAlphaAngleLookAroundCam; static float fBetaAngleLookAroundCam; static int ms_nNumCivMale_Stored; static int ms_nNumCivFemale_Stored; static int ms_nNumCop_Stored; static int ms_nNumEmergency_Stored; static int ms_nNumGang1_Stored; static int ms_nNumGang2_Stored; static int ms_nNumGang3_Stored; static int ms_nNumGang4_Stored; static int ms_nNumGang5_Stored; static int ms_nNumGang6_Stored; static int ms_nNumGang7_Stored; static int ms_nNumGang8_Stored; static int ms_nNumGang9_Stored; static int ms_nNumDummy_Stored; static int ms_nTotalCarPassengerPeds_Stored; static int ms_nTotalCivPeds_Stored; static int ms_nTotalGangPeds_Stored; static int ms_nTotalPeds_Stored; static int ms_nTotalMissionPeds_Stored; static uint8* pGarages; static CFire* FireArray; static uint32 NumOfFires; static uint8* paProjectileInfo; static uint8* paProjectiles; static uint8 CurrArea; #ifdef FIX_BUGS static int nHandleOfPlayerPed[NUMPLAYERS]; #endif public: static void Init(void) REPLAY_STUB; static void DisableReplays(void) REPLAY_STUB; static void EnableReplays(void) REPLAY_STUB; static void Update(void) REPLAY_STUB; static void FinishPlayback(void) REPLAY_STUB; static void EmptyReplayBuffer(void) REPLAY_STUB; static void Display(void) REPLAY_STUB; static void TriggerPlayback(uint8 cam_mode, float cam_x, float cam_y, float cam_z, bool load_scene) REPLAY_STUB; static void StreamAllNecessaryCarsAndPeds(void) REPLAY_STUB; static void RecordParticle(tParticleType type, CVector const& vecPos, CVector const& vecDir, float fSize, RwRGBA const& color) REPLAY_STUB; #ifndef GTA_REPLAY static bool ShouldStandardCameraBeProcessed(void) { return true; } static bool IsPlayingBack() { return false; } static bool IsPlayingBackFromFile() { return false; } #else static bool ShouldStandardCameraBeProcessed(void); static bool IsPlayingBack() { return Mode == MODE_PLAYBACK; } static bool IsPlayingBackFromFile() { return bPlayingBackFromFile; } private: static void RecordThisFrame(void); static void StorePedUpdate(CPed *ped, int id); static void StorePedAnimation(CPed *ped, CStoredAnimationState *state); static void StoreDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state); static void ProcessPedUpdate(CPed *ped, float interpolation, CAddressInReplayBuffer *buffer); static void RetrievePedAnimation(CPed *ped, CStoredAnimationState *state); static void RetrieveDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state); static void PlaybackThisFrame(void); static void TriggerPlaybackLastCoupleOfSeconds(uint32, uint8, float, float, float, uint32); static bool FastForwardToTime(uint32); static void StoreCarUpdate(CVehicle *vehicle, int id); static void StoreBikeUpdate(CVehicle* vehicle, int id); static void ProcessCarUpdate(CVehicle *vehicle, float interpolation, CAddressInReplayBuffer *buffer); static void ProcessBikeUpdate(CVehicle* vehicle, float interpolation, CAddressInReplayBuffer* buffer); static bool PlayBackThisFrameInterpolation(CAddressInReplayBuffer *buffer, float interpolation, uint32 *pTimer); static void ProcessReplayCamera(void); static void StoreStuffInMem(void); static void RestoreStuffFromMem(void); static void EmptyPedsAndVehiclePools(void); static void EmptyAllPools(void); static void MarkEverythingAsNew(void); static void SaveReplayToHD(void); static void PlayReplayFromHD(void); // out of class in III PC and later because of SecuROM static void FindFirstFocusCoordinate(CVector *coord); static void ProcessLookAroundCam(void); static size_t FindSizeOfPacket(uint8); static void GoToNextBlock(void); #endif }; ================================================ FILE: src/control/Restart.cpp ================================================ #include "common.h" #include "Restart.h" #include "Zones.h" #include "PathFind.h" uint8 CRestart::OverrideHospitalLevel; uint8 CRestart::OverridePoliceStationLevel; bool CRestart::bFadeInAfterNextArrest; bool CRestart::bFadeInAfterNextDeath; bool CRestart::bOverrideRestart; CVector CRestart::OverridePosition; float CRestart::OverrideHeading; CVector CRestart::HospitalRestartPoints[NUM_RESTART_POINTS]; float CRestart::HospitalRestartHeadings[NUM_RESTART_POINTS]; uint16 CRestart::NumberOfHospitalRestarts; CVector CRestart::PoliceRestartPoints[NUM_RESTART_POINTS]; float CRestart::PoliceRestartHeadings[NUM_RESTART_POINTS]; uint16 CRestart::NumberOfPoliceRestarts; void CRestart::Initialise() { OverridePoliceStationLevel = LEVEL_GENERIC; OverrideHospitalLevel = LEVEL_GENERIC; bFadeInAfterNextArrest = true; bFadeInAfterNextDeath = true; OverrideHeading = 0.0f; OverridePosition = CVector(0.0f, 0.0f, 0.0f); bOverrideRestart = false; NumberOfPoliceRestarts = 0; NumberOfHospitalRestarts = 0; for (int i = 0; i < NUM_RESTART_POINTS; i++) { HospitalRestartPoints[i] = CVector(0.0f, 0.0f, 0.0f); HospitalRestartHeadings[i] = 0.0f; PoliceRestartPoints[i] = CVector(0.0f, 0.0f, 0.0f); PoliceRestartHeadings[i] = 0.0f; } } void CRestart::AddHospitalRestartPoint(const CVector &pos, float heading) { HospitalRestartPoints[NumberOfHospitalRestarts] = pos; HospitalRestartHeadings[NumberOfHospitalRestarts++] = heading; } void CRestart::AddPoliceRestartPoint(const CVector &pos, float heading) { PoliceRestartPoints[NumberOfPoliceRestarts] = pos; PoliceRestartHeadings[NumberOfPoliceRestarts++] = heading; } void CRestart::OverrideNextRestart(const CVector &pos, float heading) { bOverrideRestart = true; OverridePosition = pos; OverrideHeading = heading; } void CRestart::CancelOverrideRestart() { bOverrideRestart = false; } void CRestart::FindClosestHospitalRestartPoint(const CVector &pos, CVector *outPos, float *outHeading) { if (bOverrideRestart) { *outPos = OverridePosition; *outHeading = OverrideHeading; CancelOverrideRestart(); return; } eLevelName curlevel = CTheZones::GetLevelFromPosition(&pos); float fMinDist = SQR(4000.0f); int closestPoint = NUM_RESTART_POINTS; // find closest point on this level for (int i = 0; i < NumberOfHospitalRestarts; i++) { if (CTheZones::GetLevelFromPosition(&HospitalRestartPoints[i]) == (OverrideHospitalLevel != LEVEL_GENERIC ? OverrideHospitalLevel : curlevel)) { float dist = (pos - HospitalRestartPoints[i]).MagnitudeSqr(); if (fMinDist >= dist) { fMinDist = dist; closestPoint = i; } } } // if we didn't find anything, find closest point on any level if (closestPoint == NUM_RESTART_POINTS) { for (int i = 0; i < NumberOfHospitalRestarts; i++) { float dist = (pos - HospitalRestartPoints[i]).MagnitudeSqr(); if (fMinDist >= dist) { fMinDist = dist; closestPoint = i; } } } // if we still didn't find anything, find closest path node if (closestPoint == NUM_RESTART_POINTS) { *outPos = ThePaths.m_pathNodes[ThePaths.FindNodeClosestToCoors(pos, PATH_PED, 999999.9f)].GetPosition(); *outHeading = 0.0f; printf("Couldn't find a hospital restart zone near the player %f %f %f->%f %f %f\n", pos.x, pos.y, pos.z, outPos->x, outPos->y, outPos->z); } else { *outPos = HospitalRestartPoints[closestPoint]; *outHeading = HospitalRestartHeadings[closestPoint]; } } void CRestart::FindClosestPoliceRestartPoint(const CVector &pos, CVector *outPos, float *outHeading) { if (bOverrideRestart) { *outPos = OverridePosition; *outHeading = OverrideHeading; CancelOverrideRestart(); return; } eLevelName curlevel = CTheZones::GetLevelFromPosition(&pos); float fMinDist = SQR(4000.0f); int closestPoint = NUM_RESTART_POINTS; // find closest point on this level for (int i = 0; i < NumberOfPoliceRestarts; i++) { if (CTheZones::GetLevelFromPosition(&PoliceRestartPoints[i]) == (OverridePoliceStationLevel != LEVEL_GENERIC ? OverridePoliceStationLevel : curlevel)) { float dist = (pos - PoliceRestartPoints[i]).MagnitudeSqr(); if (fMinDist >= dist) { fMinDist = dist; closestPoint = i; } } } // if we didn't find anything, find closest point on any level if (closestPoint == NUM_RESTART_POINTS) { for (int i = 0; i < NumberOfPoliceRestarts; i++) { float dist = (pos - PoliceRestartPoints[i]).MagnitudeSqr(); if (fMinDist >= dist) { fMinDist = dist; closestPoint = i; } } } // if we still didn't find anything, find closest path node if (closestPoint == NUM_RESTART_POINTS) { printf("Couldn't find a police restart zone near the player\n"); *outPos = ThePaths.m_pathNodes[ThePaths.FindNodeClosestToCoors(pos, PATH_PED, 999999.9f)].GetPosition(); *outHeading = 0.0f; } else { *outPos = PoliceRestartPoints[closestPoint]; *outHeading = PoliceRestartHeadings[closestPoint]; } } void CRestart::LoadAllRestartPoints(uint8 *buf, uint32 size) { Initialise(); INITSAVEBUF CheckSaveHeader(buf, 'R','S','T','\0', size - SAVE_HEADER_SIZE); for (int i = 0; i < NUM_RESTART_POINTS; i++) { HospitalRestartPoints[i] = ReadSaveBuf(buf); HospitalRestartHeadings[i] = ReadSaveBuf(buf); } for (int i = 0; i < NUM_RESTART_POINTS; i++) { PoliceRestartPoints[i] = ReadSaveBuf(buf); PoliceRestartHeadings[i] = ReadSaveBuf(buf); } NumberOfHospitalRestarts = ReadSaveBuf(buf); NumberOfPoliceRestarts = ReadSaveBuf(buf); bOverrideRestart = ReadSaveBuf(buf); // skip something unused ReadSaveBuf(buf); ReadSaveBuf(buf); OverridePosition = ReadSaveBuf(buf); OverrideHeading = ReadSaveBuf(buf); bFadeInAfterNextDeath = ReadSaveBuf(buf); bFadeInAfterNextArrest = ReadSaveBuf(buf); OverrideHospitalLevel = ReadSaveBuf(buf); OverridePoliceStationLevel = ReadSaveBuf(buf); VALIDATESAVEBUF(size); } void CRestart::SaveAllRestartPoints(uint8 *buf, uint32 *size) { *size = SAVE_HEADER_SIZE + sizeof(HospitalRestartPoints) + sizeof(HospitalRestartHeadings) + sizeof(PoliceRestartPoints) + sizeof(PoliceRestartHeadings) + sizeof(NumberOfHospitalRestarts) + sizeof(NumberOfPoliceRestarts) + sizeof(bOverrideRestart) + sizeof(uint8) + sizeof(uint16) + sizeof(OverridePosition) + sizeof(OverrideHeading) + sizeof(bFadeInAfterNextDeath) + sizeof(bFadeInAfterNextArrest) + sizeof(OverrideHospitalLevel) + sizeof(OverridePoliceStationLevel); // == 292 INITSAVEBUF WriteSaveHeader(buf, 'R','S','T','\0', *size - SAVE_HEADER_SIZE); for (int i = 0; i < NUM_RESTART_POINTS; i++) { WriteSaveBuf(buf, HospitalRestartPoints[i]); WriteSaveBuf(buf, HospitalRestartHeadings[i]); } for (int i = 0; i < NUM_RESTART_POINTS; i++) { WriteSaveBuf(buf, PoliceRestartPoints[i]); WriteSaveBuf(buf, PoliceRestartHeadings[i]); } WriteSaveBuf(buf, NumberOfHospitalRestarts); WriteSaveBuf(buf, NumberOfPoliceRestarts); WriteSaveBuf(buf, bOverrideRestart); WriteSaveBuf(buf, (uint8)0); WriteSaveBuf(buf, (uint16)0); WriteSaveBuf(buf, OverridePosition); WriteSaveBuf(buf, OverrideHeading); WriteSaveBuf(buf, bFadeInAfterNextDeath); WriteSaveBuf(buf, bFadeInAfterNextArrest); WriteSaveBuf(buf, OverrideHospitalLevel); WriteSaveBuf(buf, OverridePoliceStationLevel); VALIDATESAVEBUF(*size); } ================================================ FILE: src/control/Restart.h ================================================ #pragma once #define NUM_RESTART_POINTS 8 class CRestart { public: static void AddPoliceRestartPoint(const CVector&, float); static void AddHospitalRestartPoint(const CVector&, float); static void OverrideNextRestart(const CVector&, float); static void FindClosestHospitalRestartPoint(const CVector &, CVector *, float *); static void FindClosestPoliceRestartPoint(const CVector &, CVector *, float *); static void Initialise(); static void CancelOverrideRestart(); static void LoadAllRestartPoints(uint8 *buf, uint32 size); static void SaveAllRestartPoints(uint8 *buf, uint32 *size); static uint8 OverrideHospitalLevel; static uint8 OverridePoliceStationLevel; static bool bFadeInAfterNextArrest; static bool bFadeInAfterNextDeath; static bool bOverrideRestart; static CVector OverridePosition; static float OverrideHeading; static CVector HospitalRestartPoints[NUM_RESTART_POINTS]; static float HospitalRestartHeadings[NUM_RESTART_POINTS]; static uint16 NumberOfHospitalRestarts; static CVector PoliceRestartPoints[NUM_RESTART_POINTS]; static float PoliceRestartHeadings[NUM_RESTART_POINTS]; static uint16 NumberOfPoliceRestarts; }; ================================================ FILE: src/control/RoadBlocks.cpp ================================================ #include "common.h" #include "RoadBlocks.h" #include "PathFind.h" #include "ModelIndices.h" #include "Streaming.h" #include "World.h" #include "PedPlacement.h" #include "Automobile.h" #include "CopPed.h" #include "VisibilityPlugins.h" #include "PlayerPed.h" #include "Wanted.h" #include "Camera.h" #include "CarCtrl.h" #include "General.h" #include "Object.h" #define ROADBLOCKDIST (90.0f) #define ROADBLOCK_OBJECT_WIDTH (4.0f) int16 CRoadBlocks::NumRoadBlocks; int16 CRoadBlocks::RoadBlockNodes[NUMROADBLOCKS]; bool CRoadBlocks::InOrOut[NUMROADBLOCKS]; CScriptRoadblock CRoadBlocks::aScriptRoadBlocks[NUM_SCRIPT_ROADBLOCKS]; #ifdef SECUROM uint8 roadBlocksPirateCheck = 0; #endif void CRoadBlocks::Init(void) { int i; NumRoadBlocks = 0; for(i = 0; i < ThePaths.m_numCarPathNodes; i++){ if(ThePaths.m_pathNodes[i].bUseInRoadBlock && ThePaths.m_pathNodes[i].numLinks == 2){ if (NumRoadBlocks < NUMROADBLOCKS) { InOrOut[NumRoadBlocks] = true; RoadBlockNodes[NumRoadBlocks] = i; NumRoadBlocks++; } else { #ifndef MASTER printf("Not enough room for the potential roadblocks\n"); #endif // FIX: Don't iterate loop after NUMROADBLOCKS return; } } } ClearScriptRoadBlocks(); } void CRoadBlocks::GenerateRoadBlockCopsForCar(CVehicle* pVehicle, int32 roadBlockType) { static const CVector vecRoadBlockOffets[6] = { CVector(-1.5, 1.8f, 0.0f), CVector(-1.5f, -1.8f, 0.0f), CVector(1.5f, 1.8f, 0.0f), CVector(1.5f, -1.8f, 0.0f), CVector(-1.5f, 0.0f, 0.0f), CVector(1.5, 0.0, 0.0) }; CEntity* pEntityToAttack = (CEntity*)FindPlayerVehicle(); if (!pEntityToAttack) pEntityToAttack = (CEntity*)FindPlayerPed(); CColModel* pPoliceColModel = CModelInfo::GetModelInfo(MI_POLICE)->GetColModel(); float fRadius = pVehicle->GetBoundRadius() / pPoliceColModel->boundingSphere.radius; for (int32 i = 0; i < 2; i++) { const int32 roadBlockIndex = i + 2 * roadBlockType; CVector posForZ = pVehicle->m_matrix * (fRadius * vecRoadBlockOffets[roadBlockIndex]); int32 modelInfoId = MI_COP; eCopType copType = COP_STREET; switch (pVehicle->GetModelIndex()) { case MI_FBIRANCH: modelInfoId = MI_FBI; copType = COP_FBI; break; case MI_ENFORCER: modelInfoId = MI_SWAT; copType = COP_SWAT; break; case MI_BARRACKS: modelInfoId = MI_ARMY; copType = COP_ARMY; break; } if (!CStreaming::HasModelLoaded(modelInfoId)) copType = COP_STREET; CCopPed* pCopPed = new CCopPed(copType); if (copType == COP_STREET) pCopPed->SetCurrentWeapon(WEAPONTYPE_COLT45); CPedPlacement::FindZCoorForPed(&posForZ); pCopPed->SetPosition(posForZ); pCopPed->SetOrientation(0.0f, 0.0f, -HALFPI); pCopPed->m_bIsDisabledCop = true; pCopPed->SetIdle(); pCopPed->bKindaStayInSamePlace = true; pCopPed->bNotAllowedToDuck = false; pCopPed->m_nExtendedRangeTimer = CTimer::GetTimeInMilliseconds() + 10000; pCopPed->m_nRoadblockVeh = pVehicle; pCopPed->m_nRoadblockVeh->RegisterReference((CEntity**)&pCopPed->m_nRoadblockVeh); pCopPed->bCrouchWhenShooting = roadBlockType == 2 ? false : true; if (pEntityToAttack) { pCopPed->SetWeaponLockOnTarget(pEntityToAttack); pCopPed->SetAttack(pEntityToAttack); } pCopPed->m_pMyVehicle = pVehicle; pVehicle->RegisterReference((CEntity**)&pCopPed->m_pMyVehicle); pCopPed->bCullExtraFarAway = true; CVisibilityPlugins::SetClumpAlpha(pCopPed->GetClump(), 0); CWorld::Add(pCopPed); } } void CRoadBlocks::GenerateRoadBlocks(void) { CMatrix tmp1, tmp2; static int16 unk; #ifdef SQUEEZE_PERFORMANCE if (FindPlayerPed()->m_pWanted->m_RoadblockDensity == 0) return; #endif uint32 frame = CTimer::GetFrameCounter() & 0xF; int16 nRoadblockNode = (int16)(NUMROADBLOCKS * frame) / 16; const int16 maxRoadBlocks = (int16)(NUMROADBLOCKS * (frame + 1)) / 16; for (; nRoadblockNode < Min(NumRoadBlocks, maxRoadBlocks); nRoadblockNode++) { int16 node = RoadBlockNodes[nRoadblockNode]; CVector2D vecDistance = FindPlayerCoors() - ThePaths.m_pathNodes[node].GetPosition(); if (vecDistance.x > -ROADBLOCKDIST && vecDistance.x < ROADBLOCKDIST && vecDistance.y > -ROADBLOCKDIST && vecDistance.y < ROADBLOCKDIST && vecDistance.Magnitude() < ROADBLOCKDIST) { if (!InOrOut[nRoadblockNode]) { InOrOut[nRoadblockNode] = true; if (FindPlayerVehicle() && (CGeneral::GetRandomNumber() & 0x7F) < FindPlayerPed()->m_pWanted->m_RoadblockDensity) { CCarPathLink* pLink1 = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[ThePaths.m_pathNodes[node].firstLink]]; CCarPathLink* pLink2 = &ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[ThePaths.m_pathNodes[node].firstLink + 1]]; int lanes = Min(pLink1->numRightLanes + pLink1->numLeftLanes, pLink2->numLeftLanes + pLink2->numRightLanes); float length = LANE_WIDTH * (lanes + 1); CVector forward(pLink2->GetY() - pLink1->GetY(), -(pLink2->GetX() - pLink1->GetX()), 0.0f); forward.Normalise(); if (ThePaths.m_pathNodes[node].HasDivider()) { CreateRoadBlockBetween2Points( ThePaths.m_pathNodes[node].GetPosition() + (length * 0.5f + ThePaths.m_pathNodes[node].GetDividerWidth()) * forward, ThePaths.m_pathNodes[node].GetPosition() + ThePaths.m_pathNodes[node].GetDividerWidth() * forward); CreateRoadBlockBetween2Points( ThePaths.m_pathNodes[node].GetPosition() - ThePaths.m_pathNodes[node].GetDividerWidth() * forward, ThePaths.m_pathNodes[node].GetPosition() - (length * 0.5f + ThePaths.m_pathNodes[node].GetDividerWidth()) * forward); } else { CreateRoadBlockBetween2Points( ThePaths.m_pathNodes[node].GetPosition() + (length * 0.5f) * forward, ThePaths.m_pathNodes[node].GetPosition() - (length * 0.5f) * forward); } } } } else { InOrOut[nRoadblockNode] = false; } } int i = CTimer::GetFrameCounter() & 0xF; if (!aScriptRoadBlocks[i].m_bInUse) return; if ((aScriptRoadBlocks[i].GetPosition() - FindPlayerCoors()).Magnitude() < 100.0f) { CreateRoadBlockBetween2Points(aScriptRoadBlocks[i].m_vInf, aScriptRoadBlocks[i].m_vSup); aScriptRoadBlocks[i].m_bInUse = false; } } void CRoadBlocks::ClearScriptRoadBlocks(void) { for (int i = 0; i < NUM_SCRIPT_ROADBLOCKS; i++) aScriptRoadBlocks[i].m_bInUse = false; } void CRoadBlocks::RegisterScriptRoadBlock(CVector vInf, CVector vSup) { int32 i; for (i = 0; i < NUM_SCRIPT_ROADBLOCKS; i++) { if (!aScriptRoadBlocks[i].m_bInUse) break; } if (i == NUM_SCRIPT_ROADBLOCKS) return; aScriptRoadBlocks[i].m_bInUse = true; aScriptRoadBlocks[i].m_vInf = vInf; aScriptRoadBlocks[i].m_vSup = vSup; } void CRoadBlocks::CreateRoadBlockBetween2Points(CVector point1, CVector point2) { #ifdef SECUROM if (roadBlocksPirateCheck == 0) // if not pirated game // roadBlocksPirateCheck = 1; // else roadBlocksPirateCheck = 2; #endif CMatrix tmp; CVector forward = (point2 - point1); float distBetween = forward.Magnitude(); CVector pos = (point1 + point2) / 2; CVector right(forward.y, -forward.x, 0.0f); forward.Normalise(); right.Normalise(); if (DotProduct(FindPlayerCoors() - pos, right) < 0.0f) { right *= -1.0f; } int32 vehicleId = MI_POLICE; if (FindPlayerPed()->m_pWanted->AreArmyRequired()) vehicleId = MI_BARRACKS; else if (FindPlayerPed()->m_pWanted->AreFbiRequired()) vehicleId = MI_FBICAR; else if (FindPlayerPed()->m_pWanted->AreSwatRequired()) vehicleId = MI_ENFORCER; if (!CStreaming::HasModelLoaded(vehicleId)) vehicleId = MI_POLICE; CColModel* pVehicleColModel = CModelInfo::GetModelInfo(vehicleId)->GetColModel(); float fModelRadius = 2.0f * pVehicleColModel->boundingSphere.radius + 0.25f; int16 numRoadblockVehicles = Min(6, (int16)(distBetween / fModelRadius)); for (int16 i = 0; i < numRoadblockVehicles; i++) { float offset = fModelRadius * (i - numRoadblockVehicles / 2); tmp.SetTranslate(0.0f, 0.0f, 0.0f); tmp.GetRight() = CVector(forward.y, -forward.x, 0.0f); tmp.GetForward() = forward; tmp.GetUp() = CVector(0.0f, 0.0f, 1.0f); tmp.RotateZ(((CGeneral::GetRandomNumber() & 0xFF) - 128.0f) * 0.003f); if (CGeneral::GetRandomNumber() & 1) tmp.RotateZ(((CGeneral::GetRandomNumber() & 0xFF) - 128.0f) * 0.003f + 3.1416f); tmp.SetTranslateOnly(offset * forward + pos); tmp.GetPosition().z += 0.6f; float fModelRadius = CModelInfo::GetModelInfo(vehicleId)->GetColModel()->boundingSphere.radius - 0.25f; int16 colliding = 0; CWorld::FindObjectsKindaColliding(tmp.GetPosition(), fModelRadius, 0, &colliding, 2, nil, false, true, true, false, false); if (!colliding) { CAutomobile* pVehicle = new CAutomobile(vehicleId, RANDOM_VEHICLE); pVehicle->SetStatus(STATUS_ABANDONED); // pVehicle->GetHeightAboveRoad(); // called but return value is ignored? tmp.GetPosition().z += fModelRadius - 0.6f; pVehicle->m_matrix = tmp; pVehicle->PlaceOnRoadProperly(); pVehicle->SetIsStatic(false); pVehicle->m_matrix.UpdateRW(); pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; CCarCtrl::JoinCarWithRoadSystem(pVehicle); pVehicle->bIsLocked = false; pVehicle->AutoPilot.m_nCarMission = MISSION_NONE; pVehicle->AutoPilot.m_nTempAction = TEMPACT_NONE; pVehicle->AutoPilot.m_nNextLane = pVehicle->AutoPilot.m_nCurrentLane = 0; pVehicle->AutoPilot.m_nCruiseSpeed = pVehicle->AutoPilot.m_fMaxTrafficSpeed = 0; pVehicle->bExtendedRange = true; if (pVehicle->UsesSiren() && CGeneral::GetRandomNumber() & 1) pVehicle->m_bSirenOrAlarm = true; if (pVehicle->GetUp().z > 0.94f) { CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), 0); CWorld::Add(pVehicle); pVehicle->bCreateRoadBlockPeds = true; pVehicle->m_nRoadblockType = DotProduct(pVehicle->GetRight(), pVehicle->GetPosition() - FindPlayerCoors()) >= 0.0f; pVehicle->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 7000; } else { delete pVehicle; } } } int numBarriers = distBetween / ROADBLOCK_OBJECT_WIDTH; CStreaming::RequestModel(MI_ROADWORKBARRIER1, STREAMFLAGS_DONT_REMOVE); if (!CStreaming::HasModelLoaded(MI_ROADWORKBARRIER1)) return; for (int i = 0; i < numBarriers; i++) { float offset = ROADBLOCK_OBJECT_WIDTH * (i - numBarriers / 2); tmp.SetTranslate(0.0f, 0.0f, 0.0f); tmp.GetRight() = CVector(forward.y, -forward.x, 0.0f); tmp.GetForward() = forward; tmp.GetUp() = CVector(0.0f, 0.0f, 1.0f); tmp.RotateZ(((CGeneral::GetRandomNumber() & 0xFF) - 128.0f) * 0.003f); tmp.SetTranslateOnly(5.0f * right + offset * forward + pos); tmp.GetPosition().x += (CGeneral::GetRandomNumber() & 0xF) * 0.1f; tmp.GetPosition().y += (CGeneral::GetRandomNumber() & 0xF) * 0.1f; bool found; tmp.GetPosition().z = CWorld::FindGroundZFor3DCoord(tmp.GetPosition().x, tmp.GetPosition().y, tmp.GetPosition().z + 2.0f, &found); if (!found) continue; int16 colliding = 0; CBaseModelInfo* pMI = CModelInfo::GetModelInfo(MI_ROADWORKBARRIER1); tmp.GetPosition().z -= pMI->GetColModel()->boundingBox.min.z; CWorld::FindObjectsKindaColliding(tmp.GetPosition(), pMI->GetColModel()->boundingSphere.radius, 0, &colliding, 2, nil, false, true, true, false, false); if (colliding == 0) { CObject* pObject = new CObject(MI_ROADWORKBARRIER1, true); pObject->GetMatrix() = tmp; pObject->ObjectCreatedBy = TEMP_OBJECT; pObject->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 600000; CWorld::Add(pObject); } } } ================================================ FILE: src/control/RoadBlocks.h ================================================ #pragma once #include "common.h" class CVehicle; class CScriptRoadblock { public: CVector m_vInf; CVector m_vSup; bool m_bInUse; CVector GetPosition() { return (m_vInf + m_vSup) / 2; } }; class CRoadBlocks { public: static int16 NumRoadBlocks; static int16 RoadBlockNodes[NUMROADBLOCKS]; static bool InOrOut[NUMROADBLOCKS]; static CScriptRoadblock aScriptRoadBlocks[NUM_SCRIPT_ROADBLOCKS]; static void Init(void); static void GenerateRoadBlockCopsForCar(CVehicle* pVehicle, int32 roadBlockType); static void GenerateRoadBlocks(void); static void CreateRoadBlockBetween2Points(CVector, CVector); static void RegisterScriptRoadBlock(CVector, CVector); static void ClearScriptRoadBlocks(); }; ================================================ FILE: src/control/SceneEdit.cpp ================================================ #include "common.h" #include "SceneEdit.h" #ifdef GTA_SCENE_EDIT #include "Automobile.h" #include "Camera.h" #include "CarCtrl.h" #include "CivilianPed.h" #include "FileMgr.h" #include "Font.h" #include "ModelIndices.h" #include "ModelInfo.h" #include "Pad.h" #include "Ped.h" #include "Population.h" #include "Text.h" #include "Timecycle.h" #include "Streaming.h" #include "Vehicle.h" #include "WeaponInfo.h" #include "World.h" bool CSceneEdit::m_bEditOn; int32 CSceneEdit::m_bCameraFollowActor; bool CSceneEdit::m_bRecording; CVector CSceneEdit::m_vecCurrentPosition; CVector CSceneEdit::m_vecCamHeading; CVector CSceneEdit::m_vecGotoPosition; int32 CSceneEdit::m_nVehicle; int32 CSceneEdit::m_nVehicle2; int32 CSceneEdit::m_nActor; int32 CSceneEdit::m_nActor2; int32 CSceneEdit::m_nVehiclemodelId; int32 CSceneEdit::m_nPedmodelId; int16 CSceneEdit::m_nCurrentMovieCommand; int16 CSceneEdit::m_nNumActors; int16 CSceneEdit::m_nNumMovieCommands; int16 CSceneEdit::m_nCurrentCommand; int16 CSceneEdit::m_nCurrentVehicle; int16 CSceneEdit::m_nCurrentActor; int16 CSceneEdit::m_nWeaponType; bool CSceneEdit::m_bCommandActive; bool CSceneEdit::m_bActorSelected; bool CSceneEdit::m_bActor2Selected; bool CSceneEdit::m_bVehicleSelected; int16 CSceneEdit::m_nNumVehicles; CPed* CSceneEdit::pActors[NUM_ACTORS_IN_MOVIE]; CVehicle* CSceneEdit::pVehicles[NUM_VEHICLES_IN_MOVIE]; bool CSceneEdit::m_bDrawGotoArrow; CMovieCommand CSceneEdit::Movie[NUM_COMMANDS_IN_MOVIE]; #define SHADOW_OFFSET (2.0f) #define ACTION_MESSAGE_X_RIGHT (60.0f) #define ACTION_MESSAGE_Y (8.0f) #define SELECTED_MESSAGE_X_RIGHT (60.0f) #define SELECTED_MESSAGE_Y (248.0f) #define COMMAND_NAME_X_RIGHT (60.0f) #define COMMAND_NAME_Y (38.0f) #define COMMAND_NAME_HEIGHT (16.0f) #define NUM_COMMANDS_TO_DRAW (9) static const char* pCommandStrings[] = { "do-nothing", "New Actor", "Move Actor", "Select Actor", "Delete Actor", "New Vehicle", "Move Vehicle", "Select Vehicle", "Delete Vehicle", "Give Weapon", "Goto", "Goto (wait)", "Get In Car", "Get Out Car", "Kill", "Flee", "Wait", "Position Camera", "Set Camera Target", "Select Camera Mode", "Save Movie", "Load Movie", "Play Movie", "END" }; #ifdef CHECK_STRUCT_SIZES static_assert(ARRAY_SIZE(pCommandStrings) == CSceneEdit::MOVIE_TOTAL_COMMANDS, "Scene edit: not all commands have names"); #endif static int32 NextValidModelId(int32 mi, int32 step) { int32 result = -1; int32 i = mi; while (result == -1) { i += step; if (i < 0 || i > MODELINFOSIZE) { step = -step; continue; } CBaseModelInfo* pInfo = CModelInfo::GetModelInfo(i); CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)pInfo; if (!pInfo) continue; if (pInfo->GetModelType() == MITYPE_PED #ifdef FIX_BUGS && !(i >= MI_SPECIAL01 && i <= MI_SPECIAL21) #endif || pInfo->GetModelType() == MITYPE_VEHICLE && #ifdef FIX_BUGS (pVehicleInfo->m_vehicleType == VEHICLE_TYPE_CAR || pVehicleInfo->m_vehicleType == VEHICLE_TYPE_BOAT)) #else // && and || priority failure it seems, also crashes on special models pVehicleInfo->m_vehicleType == VEHICLE_TYPE_CAR || pVehicleInfo->m_vehicleType == VEHICLE_TYPE_BOAT) #endif result = i; } return result; } void CSceneEdit::LoadMovie(void) { ReInitialise(); CFileMgr::SetDir("DATA"); int fid = CFileMgr::OpenFile("movie.dat", "r"); #ifdef FIX_BUGS if (fid >= 0) #endif { CFileMgr::Read(fid, (char*)&Movie, sizeof(Movie)); CFileMgr::Read(fid, (char*)&m_nNumMovieCommands, sizeof(m_nNumMovieCommands)); CFileMgr::CloseFile(fid); } CFileMgr::SetDir(""); m_bCommandActive = false; } void CSceneEdit::SaveMovie(void) { CFileMgr::SetDir("DATA"); int fid = CFileMgr::OpenFileForWriting("movie.dat"); if (fid >= 0) { CFileMgr::Write(fid, (char*)&Movie, sizeof(Movie)); CFileMgr::Write(fid, (char*)&m_nNumMovieCommands, sizeof(m_nNumMovieCommands)); CFileMgr::CloseFile(fid); } CFileMgr::SetDir(""); m_bCommandActive = false; } void CSceneEdit::Initialise(void) { m_nActor = -1; m_nActor2 = -1; m_nVehicle = -1; m_nVehicle2 = -1; m_nCurrentCommand = MOVIE_NEW_ACTOR; m_nVehiclemodelId = MI_INFERNUS; m_nPedmodelId = MI_MALE01; m_nNumVehicles = 0; m_nNumActors = 0; m_nNumMovieCommands = 0; m_bCommandActive = false; m_bRecording = true; m_bEditOn = false; for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) pActors[i] = nil; for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) pVehicles[i] = nil; m_vecCamHeading = TheCamera.Cams[TheCamera.ActiveCam].Front; m_vecGotoPosition = CVector(0.0f, 0.0f, 0.0f); m_bCameraFollowActor = false; TheCamera.Cams[TheCamera.ActiveCam].ResetStatics = true; m_bDrawGotoArrow = false; } void CSceneEdit::InitPlayback(void) { m_nVehiclemodelId = MI_INFERNUS; m_nPedmodelId = MI_MALE01; m_bCommandActive = false; m_nNumActors = 0; m_nNumVehicles = 0; m_nActor = -1; m_nActor2 = -1; m_nVehicle = -1; m_nVehicle2 = -1; TheCamera.Cams[TheCamera.ActiveCam].ResetStatics = true; m_vecCamHeading = TheCamera.Cams[TheCamera.ActiveCam].Front; for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { if (pActors[i]) { CPopulation::RemovePed(pActors[i]); pActors[i] = nil; } } m_nCurrentActor = 0; for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) { if (pVehicles[i]) { CWorld::Remove(pVehicles[i]); delete pVehicles[i]; pVehicles[i] = nil; } } m_nCurrentVehicle = 0; m_vecGotoPosition = CVector(0.0f, 0.0f, 0.0f); m_nCurrentMovieCommand = MOVIE_DO_NOTHING; m_bDrawGotoArrow = false; } void CSceneEdit::ReInitialise(void) { m_nVehiclemodelId = MI_INFERNUS; m_nPedmodelId = MI_MALE01; m_nCurrentCommand = MOVIE_NEW_ACTOR; m_bEditOn = true; m_bRecording = true; m_bCommandActive = false; #ifdef FIX_BUGS m_bCameraFollowActor = false; TheCamera.Cams[TheCamera.ActiveCam].ResetStatics = true; // not enough... #endif m_nActor = -1; m_nActor2 = -1; m_nVehicle = -1; m_nVehicle2 = -1; m_nNumMovieCommands = 0; m_nCurrentMovieCommand = MOVIE_DO_NOTHING; m_nNumActors = 0; m_nNumVehicles = 0; for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { if (pActors[i]) { CPopulation::RemovePed(pActors[i]); pActors[i] = nil; } } for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) { if (pVehicles[i]) { CWorld::Remove(pVehicles[i]); delete pVehicles[i]; pVehicles[i] = nil; } } for (int i = 0; i < NUM_COMMANDS_IN_MOVIE; i++) { Movie[i].m_nCommandId = MOVIE_DO_NOTHING; Movie[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f); Movie[i].m_vecCamera = CVector(0.0f, 0.0f, 0.0f); Movie[i].m_nActorId = -1; Movie[i].m_nActor2Id = -1; Movie[i].m_nVehicleId = -1; Movie[i].m_nModelIndex = 0; } m_vecGotoPosition = CVector(0.0f, 0.0f, 0.0f); m_bDrawGotoArrow = false; } void CSceneEdit::Update(void) { if (!m_bEditOn) return; if (m_bRecording) ProcessCommand(); else { if (m_bCameraFollowActor && m_nActor != -1) { if (pActors[m_nActor]->bInVehicle) TheCamera.TakeControl(pActors[m_nActor]->m_pMyVehicle, CCam::MODE_BEHINDCAR, JUMP_CUT, CAMCONTROL_SCRIPT); else TheCamera.TakeControl(pActors[m_nActor], CCam::MODE_FOLLOWPED, JUMP_CUT, CAMCONTROL_SCRIPT); } PlayBack(); } } void CSceneEdit::Draw(void) { char str[200]; wchar wstr[200]; if (TheCamera.m_WideScreenOn) return; #ifndef FIX_BUGS CFont::SetPropOff(); #endif CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(0.8f), SCREEN_SCALE_Y(1.35f)); CFont::SetCentreOn(); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetBackGroundOnlyTextOff(); #ifdef FIX_BUGS CFont::SetFontStyle(FONT_STANDARD); CFont::SetPropOn(); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetDropShadowPosition(1); #else CFont::SetFontStyle(FONT_HEADING); CFont::SetPropOff(); #endif sprintf(str, "Action"); AsciiToUnicode(str, wstr); CFont::SetColor(CRGBA(0, 0, 0, 255)); #ifdef FIX_BUGS CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(ACTION_MESSAGE_X_RIGHT - SHADOW_OFFSET), SCREEN_SCALE_Y(ACTION_MESSAGE_Y + SHADOW_OFFSET), wstr); #else CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-ACTION_MESSAGE_X_RIGHT) + SHADOW_OFFSET, SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-ACTION_MESSAGE_Y) + SHADOW_OFFSET, wstr); #endif CFont::SetColor(CRGBA(193, 164, 120, 255)); #ifdef FIX_BUGS CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(ACTION_MESSAGE_X_RIGHT), SCREEN_SCALE_Y(ACTION_MESSAGE_Y), wstr); #else CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-ACTION_MESSAGE_X_RIGHT), SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-ACTION_MESSAGE_Y), wstr); #endif sprintf(str, "Selected"); AsciiToUnicode(str, wstr); CFont::SetColor(CRGBA(0, 0, 0, 255)); #ifdef FIX_BUGS CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(SELECTED_MESSAGE_X_RIGHT - SHADOW_OFFSET), SCREEN_SCALE_Y(SELECTED_MESSAGE_Y + SHADOW_OFFSET), wstr); #else CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-SELECTED_MESSAGE_X_RIGHT) + SHADOW_OFFSET, SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-SELECTED_MESSAGE_Y) + SHADOW_OFFSET, wstr); #endif CFont::SetColor(CRGBA(193, 164, 120, 255)); #ifdef FIX_BUGS CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(SELECTED_MESSAGE_X_RIGHT), SCREEN_SCALE_Y(SELECTED_MESSAGE_Y), wstr); #else CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-SELECTED_MESSAGE_X_RIGHT), SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-SELECTED_MESSAGE_Y), wstr); #endif CFont::SetCentreOff(); #ifdef FIX_BUGS CFont::SetScale(SCREEN_SCALE_X(0.7f), SCREEN_SCALE_Y(0.7f)); #else CFont::SetScale(0.7f, 0.7f); #endif #ifdef FIX_BUGS CFont::SetFontStyle(FONT_STANDARD); #else CFont::SetFontStyle(FONT_HEADING); #endif CFont::SetColor(CRGBA(0, 0, 0, 255)); for (int i = 0; i < NUM_COMMANDS_TO_DRAW; i++) { int16 nCommandDrawn = m_nCurrentCommand + i - NUM_COMMANDS_TO_DRAW / 2; if (nCommandDrawn >= MOVIE_TOTAL_COMMANDS) nCommandDrawn -= (MOVIE_TOTAL_COMMANDS - 1); if (nCommandDrawn <= MOVIE_DO_NOTHING) nCommandDrawn += (MOVIE_TOTAL_COMMANDS - 1); sprintf(str, "%s", pCommandStrings[nCommandDrawn]); AsciiToUnicode(str, wstr); CFont::SetColor(CRGBA(0, 0, 0, 255)); #ifdef FIX_BUGS CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(COMMAND_NAME_X_RIGHT - SHADOW_OFFSET), SCREEN_SCALE_Y(COMMAND_NAME_Y + SHADOW_OFFSET + i * COMMAND_NAME_HEIGHT), wstr); #else CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-COMMAND_NAME_X_RIGHT) + SHADOW_OFFSET, SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-COMMAND_NAME_Y) + SHADOW_OFFSET + i * COMMAND_NAME_HEIGHT, wstr); #endif if (nCommandDrawn == m_nCurrentCommand) CFont::SetColor(CRGBA(156, 91, 40, 255)); else CFont::SetColor(CRGBA(193, 164, 120, 255)); #ifdef FIX_BUGS CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(COMMAND_NAME_X_RIGHT), SCREEN_SCALE_Y(COMMAND_NAME_Y + i * COMMAND_NAME_HEIGHT), wstr); #else CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH-COMMAND_NAME_X_RIGHT), SCREEN_SCALE_FROM_BOTTOM(DEFAULT_SCREEN_HEIGHT-COMMAND_NAME_Y) + i * COMMAND_NAME_HEIGHT, wstr); #endif } } void CSceneEdit::ProcessCommand(void) { if (!m_bCommandActive) { ClearForNewCommand(); if (CPad::GetPad(1)->GetDPadUpJustDown()) { if (--m_nCurrentCommand == MOVIE_DO_NOTHING) m_nCurrentCommand = MOVIE_END; } if (CPad::GetPad(1)->GetDPadDownJustDown()) { if (++m_nCurrentCommand == MOVIE_TOTAL_COMMANDS) m_nCurrentCommand = MOVIE_NEW_ACTOR; } if (CPad::GetPad(1)->GetTriangleJustDown()) { if (m_nCurrentCommand != MOVIE_DO_NOTHING) m_bCommandActive = true; } return; } switch (m_nCurrentCommand) { case MOVIE_DO_NOTHING: m_bCommandActive = false; break; case MOVIE_NEW_ACTOR: if (m_nActor == -1) { if (m_nNumActors == NUM_ACTORS_IN_MOVIE) break; if (!CStreaming::HasModelLoaded(m_nPedmodelId)) { CStreaming::RequestModel(m_nPedmodelId, 0); #ifdef FIX_BUGS CStreaming::LoadAllRequestedModels(false); // otherwise gets stuck :( #endif break; } CPed* pPed = new CCivilianPed(PEDTYPE_SPECIAL, m_nPedmodelId); pPed->CharCreatedBy = MISSION_CHAR; pPed->SetPosition(m_vecCurrentPosition); pPed->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(pPed); pPed->bUsesCollision = false; pPed->bAffectedByGravity = false; for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { if (pActors[i] == nil) { m_nActor = i; pActors[i] = pPed; break; } } } else { pActors[m_nActor]->SetPosition(m_vecCurrentPosition); pActors[m_nActor]->SetOrientation(0.0f, 0.0f, 0.0f); int32 mi = m_nPedmodelId; if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) mi = NextValidModelId(m_nPedmodelId, -1); else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) mi = NextValidModelId(m_nPedmodelId, 1); if (mi == m_nPedmodelId) { if (CPad::GetPad(1)->GetTriangleJustDown()) { pActors[m_nActor]->bUsesCollision = true; pActors[m_nActor]->bAffectedByGravity = true; ++m_nNumActors; Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_NEW_ACTOR; Movie[m_nNumMovieCommands].m_vecPosition = m_vecCurrentPosition; Movie[m_nNumMovieCommands].m_nModelIndex = m_nPedmodelId; Movie[m_nNumMovieCommands++].m_nActorId = m_nActor; m_nActor = -1; m_bCommandActive = false; } if (CPad::GetPad(1)->GetCircleJustDown()) { CWorld::Remove(pActors[m_nActor]); delete pActors[m_nActor]; pActors[m_nActor] = nil; m_nActor = -1; m_bCommandActive = false; } } else { m_nPedmodelId = mi; if (pActors[m_nActor]) { CWorld::Remove(pActors[m_nActor]); delete pActors[m_nActor]; } pActors[m_nActor] = nil; m_nActor = -1; } } break; case MOVIE_MOVE_ACTOR: SelectActor(); if (m_bCommandActive) break; pActors[m_nActor]->SetPosition(m_vecCurrentPosition); if (CPad::GetPad(1)->GetTriangleJustDown()) { m_bCommandActive = false; #ifndef FIX_BUGS // why? it crashes, also makes no sense pActors[m_nActor] = nil; #endif SelectActor(); } break; case MOVIE_SELECT_ACTOR: SelectActor(); break; case MOVIE_DELETE_ACTOR: SelectActor(); if (m_bActorSelected) { CPopulation::RemovePed(pActors[m_nActor]); m_nCurrentActor = 0; --m_nNumActors; #ifdef FIX_BUGS pActors[m_nActor] = nil; m_nActor = -1; #else m_nActor = -1; pActors[m_nActor] = nil; #endif SelectActor(); m_bCommandActive = false; } else if (CPad::GetPad(1)->GetCircleJustDown()) { m_nActor = -1; m_bCommandActive = false; } break; case MOVIE_NEW_VEHICLE: if (m_nVehicle == -1) { if (m_nNumVehicles == NUM_VEHICLES_IN_MOVIE) break; if (!CStreaming::HasModelLoaded(m_nVehiclemodelId)) { CStreaming::RequestModel(m_nVehiclemodelId, 0); #ifdef FIX_BUGS CStreaming::LoadAllRequestedModels(false); // otherwise gets stuck :( #endif break; } CVehicle* pVehicle = new CAutomobile(m_nVehiclemodelId, MISSION_VEHICLE); pVehicle->SetStatus(STATUS_PHYSICS); pVehicle->SetPosition(m_vecCurrentPosition); pVehicle->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(pVehicle); pVehicle->bUsesCollision = false; pVehicle->bAffectedByGravity = false; for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) { if (pVehicles[i] == nil) { m_nVehicle = i; pVehicles[i] = pVehicle; break; } } } else { pVehicles[m_nVehicle]->SetPosition(m_vecCurrentPosition); pVehicles[m_nVehicle]->SetOrientation(0.0f, 0.0f, 0.0f); int32 mi = m_nVehiclemodelId; if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) mi = NextValidModelId(m_nVehiclemodelId, -1); else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) mi = NextValidModelId(m_nVehiclemodelId, 1); if (mi == m_nVehiclemodelId) { if (CPad::GetPad(1)->GetTriangleJustDown()) { pVehicles[m_nVehicle]->bUsesCollision = true; pVehicles[m_nVehicle]->bAffectedByGravity = true; ++m_nNumVehicles; Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_NEW_VEHICLE; Movie[m_nNumMovieCommands].m_vecPosition = m_vecCurrentPosition; Movie[m_nNumMovieCommands].m_nModelIndex = m_nVehiclemodelId; Movie[m_nNumMovieCommands++].m_nVehicleId = m_nVehicle; m_nVehicle = -1; m_bCommandActive = false; } if (CPad::GetPad(1)->GetCircleJustDown()) { CWorld::Remove(pVehicles[m_nVehicle]); delete pVehicles[m_nVehicle]; pVehicles[m_nVehicle] = nil; m_nVehicle = -1; m_bCommandActive = false; } } else { m_nVehiclemodelId = mi; if (pVehicles[m_nVehicle]) { CWorld::Remove(pVehicles[m_nVehicle]); delete pVehicles[m_nVehicle]; } pVehicles[m_nVehicle] = nil; m_nVehicle = -1; } } break; case MOVIE_MOVE_VEHICLE: SelectVehicle(); if (m_bCommandActive) break; pVehicles[m_nVehicle]->SetPosition(m_vecCurrentPosition); if (CPad::GetPad(1)->GetTriangleJustDown()) { m_bCommandActive = false; #ifndef FIX_BUGS // again, why? works wrong pVehicles[m_nVehicle] = nil; #endif m_nVehicle = -1; } break; case MOVIE_SELECT_VEHICLE: SelectVehicle(); break; case MOVIE_DELETE_VEHICLE: SelectVehicle(); if (m_bVehicleSelected) { CWorld::Remove(pVehicles[m_nVehicle]); delete pVehicles[m_nVehicle]; m_nCurrentVehicle = 0; --m_nNumVehicles; pVehicles[m_nVehicle] = nil; m_nVehicle = -1; SelectVehicle(); m_bCommandActive = false; } else if (CPad::GetPad(1)->GetCircleJustDown()) { pVehicles[m_nVehicle] = nil; m_nVehicle = -1; m_bCommandActive = false; } break; case MOVIE_GIVE_WEAPON: if (m_bActorSelected) { if (SelectWeapon()) { m_bCommandActive = false; Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_GIVE_WEAPON; Movie[m_nNumMovieCommands].m_nActorId = m_nActor; Movie[m_nNumMovieCommands++].m_nModelIndex = m_nWeaponType; } } else { SelectActor(); m_bCommandActive = true; } break; case MOVIE_GOTO: case MOVIE_GOTO_WAIT: if (!m_bActorSelected) { m_bDrawGotoArrow = true; SelectActor(); if (m_nActor == -1) m_bCommandActive = true; } else { m_vecGotoPosition = m_vecCurrentPosition; if (CPad::GetPad(1)->GetTriangleJustDown()) { if (pActors[m_nActor]->bInVehicle) { if (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pActors[m_nActor]->m_pMyVehicle, m_vecGotoPosition, false)) pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; else pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS; pActors[m_nActor]->m_pMyVehicle->SetStatus(STATUS_PHYSICS); pActors[m_nActor]->m_pMyVehicle->bEngineOn = true; pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCruiseSpeed = Max(16, pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCruiseSpeed); pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); TheCamera.TakeControl(pActors[m_nActor]->m_pMyVehicle, CCam::MODE_BEHINDCAR, JUMP_CUT, CAMCONTROL_SCRIPT); } else { pActors[m_nActor]->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, m_vecGotoPosition); TheCamera.TakeControl(pActors[m_nActor], CCam::MODE_FOLLOWPED, JUMP_CUT, CAMCONTROL_SCRIPT); } m_bDrawGotoArrow = false; Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_GOTO; Movie[m_nNumMovieCommands].m_nActorId = m_nActor; Movie[m_nNumMovieCommands++].m_vecPosition = m_vecGotoPosition; } if (!m_bDrawGotoArrow) { if (pActors[m_nActor]->bInVehicle && pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_NONE || !pActors[m_nActor]->bInVehicle && pActors[m_nActor]->m_objective == OBJECTIVE_NONE) { if (pActors[m_nActor]) // if there is something that requires this check the least, it's this one m_vecCamHeading = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetPosition() - TheCamera.Cams[TheCamera.ActiveCam].Source; m_bCommandActive = false; TheCamera.Cams[TheCamera.ActiveCam].Mode = CCam::MODE_FIGHT_CAM_RUNABOUT; m_vecCurrentPosition = pActors[m_nActor]->GetPosition(); pActors[m_nActor]->SetObjective(OBJECTIVE_NONE); if (pActors[m_nActor]->bInVehicle) pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE; } } if (CPad::GetPad(1)->GetCircleJustDown()) { pActors[m_nActor] = nil; m_nActor = -1; m_bCommandActive = false; } } break; case MOVIE_GET_IN_CAR: if (m_bActorSelected) SelectVehicle(); else { SelectActor(); if (m_nActor != -1) m_bCommandActive = true; } if (m_bVehicleSelected) { pActors[m_nActor]->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicles[m_nVehicle]); Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_GET_IN_CAR; Movie[m_nNumMovieCommands].m_nActorId = m_nActor; Movie[m_nNumMovieCommands++].m_nVehicleId = m_nVehicle; m_nVehicle = -1; m_bCommandActive = false; } if (CPad::GetPad(1)->GetCircleJustDown()) { pVehicles[m_nVehicle] = nil; m_nVehicle = -1; pActors[m_nActor] = nil; m_nActor = -1; m_bCommandActive = false; } break; case MOVIE_GET_OUT_CAR: SelectActor(); if (m_bActorSelected) { if (pActors[m_nActor]->bInVehicle) { pActors[m_nActor]->SetObjective(OBJECTIVE_LEAVE_CAR); Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_GET_OUT_CAR; Movie[m_nNumMovieCommands++].m_nActorId = m_nActor; } m_nActor = -1; m_bCommandActive = false; } if (CPad::GetPad(1)->GetCircleJustDown()) { pVehicles[m_nVehicle] = nil; m_nVehicle = -1; pActors[m_nActor] = nil; m_nActor = -1; m_bCommandActive = false; } break; case MOVIE_KILL: if (!m_bActorSelected) { SelectActor(); m_bCommandActive = true; } else if (!m_bActor2Selected) { SelectActor2(); if (m_bActorSelected && m_bActor2Selected && m_nActor != -1 && m_nActor2 != -1 && m_nActor != m_nActor2) { pActors[m_nActor]->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, pActors[m_nActor2]); Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_KILL; Movie[m_nNumMovieCommands].m_nActorId = m_nActor; Movie[m_nNumMovieCommands++].m_nActor2Id = m_nActor2; m_bCommandActive = false; } } if (CPad::GetPad(1)->GetCircleJustDown()) { pActors[m_nActor] = nil; m_nActor = -1; pActors[m_nActor2] = nil; m_nActor2 = -1; m_bCommandActive = false; } break; case MOVIE_FLEE: if (!m_bActorSelected) { SelectActor(); m_bCommandActive = true; } else if (!m_bActor2Selected) { SelectActor2(); if (m_bActorSelected && m_bActor2Selected && m_nActor != -1 && m_nActor2 != -1 && m_nActor != m_nActor2) { pActors[m_nActor]->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pActors[m_nActor2]); Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_FLEE; Movie[m_nNumMovieCommands].m_nActorId = m_nActor; Movie[m_nNumMovieCommands++].m_nActor2Id = m_nActor2; m_bCommandActive = false; } } if (CPad::GetPad(1)->GetCircleJustDown()) { pActors[m_nActor] = nil; m_nActor = -1; pActors[m_nActor2] = nil; m_nActor2 = -1; m_bCommandActive = false; } break; case MOVIE_WAIT: SelectActor(); if (m_bActorSelected) { pActors[m_nActor]->SetObjective(OBJECTIVE_WAIT_ON_FOOT); Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_WAIT; Movie[m_nNumMovieCommands++].m_nActorId = m_nActor; } if (CPad::GetPad(1)->GetCircleJustDown()) { pActors[m_nActor] = nil; m_nActor = -1; m_bCommandActive = false; } break; case MOVIE_POSITION_CAMERA: if (CPad::GetPad(1)->GetTriangleJustDown()) { Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_POSITION_CAMERA; Movie[m_nNumMovieCommands].m_vecPosition = TheCamera.Cams[TheCamera.ActiveCam].Source; Movie[m_nNumMovieCommands++].m_vecCamera = m_vecCamHeading; m_bCommandActive = false; } if (CPad::GetPad(1)->GetCircleJustDown()) { m_bCommandActive = false; } break; case MOVIE_SET_CAMERA_TARGET: if (!m_bActorSelected) { SelectActor(); m_bCommandActive = true; } else { TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity = pActors[m_nActor]; if (CPad::GetPad(1)->GetTriangleJustDown()) { Movie[m_nNumMovieCommands].m_nCommandId = MOVIE_SET_CAMERA_TARGET; Movie[m_nNumMovieCommands++].m_nActorId = m_nActor; m_bCommandActive = false; } } break; case MOVIE_SELECT_CAMERA_MODE: m_bCommandActive = false; break; case MOVIE_SAVE_MOVIE: SaveMovie(); break; case MOVIE_LOAD_MOVIE: LoadMovie(); break; case MOVIE_PLAY_MOVIE: InitPlayback(); LoadMovie(); m_bRecording = false; break; case MOVIE_END: m_bRecording = false; break; default: assert(0); } } void CSceneEdit::PlayBack(void) { m_nCurrentCommand = Movie[m_nCurrentMovieCommand].m_nCommandId; if (m_nCurrentMovieCommand >= m_nNumMovieCommands) { if (CPad::GetPad(1)->GetTriangleJustDown()) { m_nCurrentCommand = MOVIE_DO_NOTHING; m_bRecording = true; ReInitialise(); } return; } switch (m_nCurrentCommand) { case MOVIE_DO_NOTHING: case MOVIE_MOVE_ACTOR: case MOVIE_SELECT_ACTOR: case MOVIE_DELETE_ACTOR: case MOVIE_MOVE_VEHICLE: case MOVIE_SELECT_VEHICLE: case MOVIE_DELETE_VEHICLE: break; case MOVIE_NEW_ACTOR: { m_nPedmodelId = Movie[m_nCurrentMovieCommand].m_nModelIndex; m_vecCurrentPosition = Movie[m_nCurrentMovieCommand].m_vecPosition; if (!CStreaming::HasModelLoaded(m_nPedmodelId)) { CStreaming::RequestModel(m_nPedmodelId, 0); #ifdef FIX_BUGS CStreaming::LoadAllRequestedModels(false); // otherwise gets stuck :( #endif break; } CPed* pPed = new CCivilianPed(PEDTYPE_SPECIAL, m_nPedmodelId); pPed->CharCreatedBy = MISSION_CHAR; CWorld::Add(pPed); pPed->SetPosition(m_vecCurrentPosition); pPed->SetOrientation(0.0f, 0.0f, 0.0f); for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { if (pActors[i] == nil) { m_nActor = i; pActors[i] = pPed; break; } } m_nNumActors++; m_nCurrentMovieCommand++; break; } case MOVIE_NEW_VEHICLE: { m_nVehiclemodelId = Movie[m_nCurrentMovieCommand].m_nModelIndex; m_vecCurrentPosition = Movie[m_nCurrentMovieCommand].m_vecPosition; if (!CStreaming::HasModelLoaded(m_nVehiclemodelId)) { CStreaming::RequestModel(m_nVehiclemodelId, 0); #ifdef FIX_BUGS CStreaming::LoadAllRequestedModels(false); // otherwise gets stuck :( #endif break; } CVehicle* pVehicle = new CAutomobile(m_nVehiclemodelId, MISSION_VEHICLE); pVehicle->SetStatus(STATUS_PHYSICS); pVehicle->SetPosition(m_vecCurrentPosition); pVehicle->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(pVehicle); for (int i = 0; i < NUM_VEHICLES_IN_MOVIE; i++) { if (pVehicles[i] == nil) { m_nVehicle = i; pVehicles[i] = pVehicle; break; } } m_nNumVehicles++; m_nCurrentMovieCommand++; break; } case MOVIE_GIVE_WEAPON: m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; m_nWeaponType = Movie[m_nCurrentMovieCommand].m_nModelIndex; pActors[m_nActor]->GiveWeapon((eWeaponType)m_nWeaponType, 1000); pActors[m_nActor]->AddWeaponModel(CWeaponInfo::GetWeaponInfo(pActors[m_nActor]->GetWeapon()->m_eWeaponType)->m_nModelId); pActors[m_nActor]->SetCurrentWeapon(m_nWeaponType); m_nCurrentMovieCommand++; break; case MOVIE_GOTO: case MOVIE_GOTO_WAIT: m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; m_vecGotoPosition = Movie[m_nCurrentMovieCommand].m_vecPosition; if (pActors[m_nActor]->bInVehicle) { if (pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS && pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission != MISSION_GOTOCOORDS_STRAIGHT) { if ((pActors[m_nActor]->m_pMyVehicle->GetPosition() - m_vecGotoPosition).Magnitude() < 5.0f) { if (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pActors[m_nActor]->m_pMyVehicle, m_vecGotoPosition, false)) pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; else pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS; pActors[m_nActor]->m_pMyVehicle->SetStatus(STATUS_PHYSICS); pActors[m_nActor]->m_pMyVehicle->bEngineOn = true; pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCruiseSpeed = Max(16, pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nCruiseSpeed); pActors[m_nActor]->m_pMyVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); if (m_nCurrentCommand != MOVIE_GOTO_WAIT) ++m_nCurrentMovieCommand; } else ++m_nCurrentMovieCommand; } } else { if (pActors[m_nActor]->m_objective != OBJECTIVE_GOTO_AREA_ON_FOOT) { pActors[m_nActor]->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, m_vecGotoPosition); ++m_nCurrentMovieCommand; } } break; case MOVIE_GET_IN_CAR: m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; if (!pActors[m_nActor]->bInVehicle){ m_nVehicle = Movie[m_nCurrentMovieCommand].m_nVehicleId; pActors[m_nActor]->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicles[m_nVehicle]); } else ++m_nCurrentMovieCommand; break; case MOVIE_GET_OUT_CAR: m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; if (pActors[m_nActor]->bInVehicle) pActors[m_nActor]->SetObjective(OBJECTIVE_LEAVE_CAR); else ++m_nCurrentMovieCommand; break; case MOVIE_KILL: m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; m_nActor2 = Movie[m_nCurrentMovieCommand].m_nActor2Id; pActors[m_nActor]->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, pActors[m_nActor2]); if (pActors[m_nActor2]->GetPedState() == PED_DEAD) ++m_nCurrentMovieCommand; break; case MOVIE_FLEE: m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; m_nActor2 = Movie[m_nCurrentMovieCommand].m_nActor2Id; pActors[m_nActor]->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pActors[m_nActor2]); ++m_nCurrentMovieCommand; break; case MOVIE_WAIT: m_nActor = Movie[m_nCurrentMovieCommand].m_nActorId; pActors[m_nActor]->SetObjective(OBJECTIVE_WAIT_ON_FOOT); ++m_nCurrentMovieCommand; break; case MOVIE_POSITION_CAMERA: TheCamera.Cams[TheCamera.ActiveCam].Source = Movie[m_nCurrentMovieCommand].m_vecPosition; m_vecCamHeading = Movie[m_nCurrentMovieCommand].m_vecCamera; TheCamera.Cams[TheCamera.ActiveCam].Front = m_vecCamHeading; ++m_nCurrentMovieCommand; break; case MOVIE_SET_CAMERA_TARGET: m_bCameraFollowActor = true; TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity = pActors[Movie[m_nNumMovieCommands].m_nActorId]; TheCamera.pTargetEntity = pActors[Movie[m_nNumMovieCommands].m_nActorId]; TheCamera.m_bLookingAtPlayer = false; ++m_nCurrentMovieCommand; break; case MOVIE_SELECT_CAMERA_MODE: m_bCommandActive = false; // this is wrong break; } } void CSceneEdit::ClearForNewCommand(void) { m_nActor = -1; m_nActor2 = -1; m_nVehicle = -1; m_bActorSelected = false; m_bActor2Selected = false; m_bVehicleSelected = false; m_bDrawGotoArrow = false; } void CSceneEdit::SelectActor(void) { m_bActorSelected = false; if (m_nActor != -1) { if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) { CPed* pPed; do { if (--m_nActor < 0) m_nActor = NUM_ACTORS_IN_MOVIE - 1; pPed = pActors[m_nActor]; } while (pPed == nil); TheCamera.Cams[TheCamera.ActiveCam].Source = pPed->GetPosition() - m_vecCamHeading; } else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) { CPed* pPed; do { if (++m_nActor == NUM_ACTORS_IN_MOVIE) m_nActor = 0; pPed = pActors[m_nActor]; } while (pPed == nil); TheCamera.Cams[TheCamera.ActiveCam].Source = pPed->GetPosition() - m_vecCamHeading; } m_vecCurrentPosition = pActors[m_nActor]->GetPosition(); if (CPad::GetPad(1)->GetTriangleJustDown()) { m_bActorSelected = true; m_bCommandActive = false; } else if (CPad::GetPad(1)->GetCircleJustDown()) { m_nActor = -1; } } else if (m_nNumActors != 0) { for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { if (pActors[i] != nil) { m_nActor = i; break; } } TheCamera.Cams[TheCamera.ActiveCam].Source = pActors[m_nActor]->GetPosition() - m_vecCamHeading; if (m_nNumActors == 1) { m_bActorSelected = true; m_bCommandActive = false; } } else { m_bCommandActive = false; } } void CSceneEdit::SelectActor2(void) { m_bActor2Selected = false; if (m_nNumActors <= 1) { m_bCommandActive = false; return; } if (m_nActor2 != -1) { if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) { CPed* pPed; do { if (--m_nActor2 < 0) m_nActor2 = NUM_ACTORS_IN_MOVIE - 1; pPed = pActors[m_nActor2]; } while (pPed == nil || pPed == pActors[m_nActor]); TheCamera.Cams[TheCamera.ActiveCam].Source = pPed->GetPosition() - m_vecCamHeading; } else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) { CPed* pPed; do { if (++m_nActor2 == NUM_ACTORS_IN_MOVIE) m_nActor2 = 0; pPed = pActors[m_nActor2]; } while (pPed == nil || pPed == pActors[m_nActor]); TheCamera.Cams[TheCamera.ActiveCam].Source = pPed->GetPosition() - m_vecCamHeading; } m_vecCurrentPosition = pActors[m_nActor2]->GetPosition(); if (CPad::GetPad(1)->GetTriangleJustDown()) { m_bActor2Selected = true; m_bCommandActive = false; } else if (CPad::GetPad(1)->GetCircleJustDown()) { m_nActor2 = -1; } } else { for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { if (pActors[i] != nil && pActors[m_nActor] != pActors[i] ) { m_nActor2 = i; break; } } TheCamera.Cams[TheCamera.ActiveCam].Source = pActors[m_nActor2]->GetPosition() - m_vecCamHeading; } } void CSceneEdit::SelectVehicle(void) { m_bVehicleSelected = false; if (m_nVehicle != -1) { if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) { CVehicle* pVehicle; do { if (--m_nVehicle < 0) m_nVehicle = NUM_VEHICLES_IN_MOVIE - 1; pVehicle = pVehicles[m_nVehicle]; } while (pVehicle == nil); } else if (CPad::GetPad(1)->GetRightShoulder1JustDown()) { CVehicle* pVehicle; do { if (++m_nVehicle == NUM_VEHICLES_IN_MOVIE) m_nVehicle = 0; pVehicle = pVehicles[m_nVehicle]; } while (pVehicle == nil); } m_vecCurrentPosition = pVehicles[m_nVehicle]->GetPosition(); TheCamera.Cams[TheCamera.ActiveCam].Source = pVehicles[m_nVehicle]->GetPosition() - m_vecCamHeading; if (CPad::GetPad(1)->GetTriangleJustDown()) { m_bVehicleSelected = true; m_bCommandActive = false; } else if (CPad::GetPad(1)->GetCircleJustDown()) { m_nVehicle = -1; } } else if (m_nNumVehicles != 0) { for (int i = 0; i < NUM_ACTORS_IN_MOVIE; i++) { if (pVehicles[i] != nil) { m_nVehicle = i; break; } } } } bool CSceneEdit::SelectWeapon(void) { if (m_nWeaponType == WEAPONTYPE_UNARMED) { m_nWeaponType = WEAPONTYPE_COLT45; return false; } if (CPad::GetPad(1)->GetLeftShoulder1JustDown()) { if (++m_nWeaponType >= WEAPONTYPE_DETONATOR) m_nWeaponType = WEAPONTYPE_BRASSKNUCKLE; pActors[m_nActor]->ClearWeapons(); pActors[m_nActor]->GiveWeapon((eWeaponType)m_nWeaponType, 1000); pActors[m_nActor]->AddWeaponModel(CWeaponInfo::GetWeaponInfo(pActors[m_nActor]->GetWeapon()->m_eWeaponType)->m_nModelId); pActors[m_nActor]->SetCurrentWeapon(m_nWeaponType); } else if (CPad::GetPad(1)->GetRightShoulder1JustDown()){ if (--m_nWeaponType <= WEAPONTYPE_UNARMED) m_nWeaponType = WEAPONTYPE_MINIGUN; pActors[m_nActor]->ClearWeapons(); pActors[m_nActor]->GiveWeapon((eWeaponType)m_nWeaponType, 1000); pActors[m_nActor]->AddWeaponModel(CWeaponInfo::GetWeaponInfo(pActors[m_nActor]->GetWeapon()->m_eWeaponType)->m_nModelId); pActors[m_nActor]->SetCurrentWeapon(m_nWeaponType); } if (CPad::GetPad(1)->GetTriangleJustDown()) { m_bCommandActive = false; return true; } if (CPad::GetPad(1)->GetCircleJustDown()) { pActors[m_nActor]->ClearWeapons(); m_nWeaponType = WEAPONTYPE_UNARMED; m_bCommandActive = false; return false; } return false; } #endif ================================================ FILE: src/control/SceneEdit.h ================================================ #pragma once #ifdef GTA_SCENE_EDIT class CPed; class CVehicle; struct CMovieCommand { int32 m_nCommandId; CVector m_vecPosition; CVector m_vecCamera; int16 m_nActorId; int16 m_nActor2Id; int16 m_nVehicleId; int16 m_nModelIndex; }; class CSceneEdit { public: enum { MOVIE_DO_NOTHING = 0, MOVIE_NEW_ACTOR, MOVIE_MOVE_ACTOR, MOVIE_SELECT_ACTOR, MOVIE_DELETE_ACTOR, MOVIE_NEW_VEHICLE, MOVIE_MOVE_VEHICLE, MOVIE_SELECT_VEHICLE, MOVIE_DELETE_VEHICLE, MOVIE_GIVE_WEAPON, MOVIE_GOTO, MOVIE_GOTO_WAIT, MOVIE_GET_IN_CAR, MOVIE_GET_OUT_CAR, MOVIE_KILL, MOVIE_FLEE, MOVIE_WAIT, MOVIE_POSITION_CAMERA, MOVIE_SET_CAMERA_TARGET, MOVIE_SELECT_CAMERA_MODE, MOVIE_SAVE_MOVIE, MOVIE_LOAD_MOVIE, MOVIE_PLAY_MOVIE, MOVIE_END, MOVIE_TOTAL_COMMANDS }; enum { NUM_ACTORS_IN_MOVIE = 5, NUM_VEHICLES_IN_MOVIE = 5, NUM_COMMANDS_IN_MOVIE = 20 }; static int32 m_bCameraFollowActor; static CVector m_vecCurrentPosition; static CVector m_vecCamHeading; static CVector m_vecGotoPosition; static int32 m_nVehicle; static int32 m_nVehicle2; static int32 m_nActor; static int32 m_nActor2; static int32 m_nVehiclemodelId; static int32 m_nPedmodelId; static int16 m_nCurrentMovieCommand; static int16 m_nCurrentCommand; static int16 m_nCurrentVehicle; static int16 m_nCurrentActor; static bool m_bEditOn; static bool m_bRecording; static bool m_bCommandActive; static bool m_bActorSelected; static bool m_bActor2Selected; static bool m_bVehicleSelected; static int16 m_nNumActors; static int16 m_nNumVehicles; static int16 m_nNumMovieCommands; static int16 m_nWeaponType; static CPed* pActors[NUM_ACTORS_IN_MOVIE]; static CVehicle* pVehicles[NUM_VEHICLES_IN_MOVIE]; static bool m_bDrawGotoArrow; static CMovieCommand Movie[NUM_COMMANDS_IN_MOVIE]; static void LoadMovie(void); static void SaveMovie(void); static void Initialise(void); static void InitPlayback(void); static void ReInitialise(void); static void Update(void); static void Draw(void); static void ProcessCommand(void); static void PlayBack(void); static void ClearForNewCommand(void); static void SelectActor(void); static void SelectActor2(void); static void SelectVehicle(void); static bool SelectWeapon(void); }; #endif ================================================ FILE: src/control/Script.cpp ================================================ #include "common.h" #include "Script.h" #include "ScriptCommands.h" #include "AnimBlendAssociation.h" #include "AudioManager.h" #include "Boat.h" #include "Camera.h" #include "CarCtrl.h" #include "CivilianPed.h" #include "Clock.h" #include "CopPed.h" #include "Debug.h" #include "DMAudio.h" #include "EmergencyPed.h" #include "FileMgr.h" #include "Frontend.h" #include "General.h" #ifdef MISSION_REPLAY #include "GenericGameStorage.h" #endif #include "HandlingMgr.h" #include "Heli.h" #include "Hud.h" #include "Lines.h" #include "Messages.h" #include "Pad.h" #include "Pickups.h" #include "Pools.h" #include "Population.h" #include "Remote.h" #include "Replay.h" #include "Stats.h" #include "Streaming.h" #include "User.h" #include "Wanted.h" #include "Weather.h" #include "Zones.h" #include "main.h" #include "Ropes.h" #include "ColStore.h" #include "Fluff.h" #include "GameLogic.h" #include "MBlur.h" #include "PedRoutes.h" #include "RoadBlocks.h" #include "SpecialFX.h" #include "Timecycle.h" #include "TxdStore.h" #include "Bike.h" #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #include #endif uint8 CTheScripts::ScriptSpace[SIZE_SCRIPT_SPACE]; CRunningScript CTheScripts::ScriptsArray[MAX_NUM_SCRIPTS]; intro_text_line CTheScripts::IntroTextLines[MAX_NUM_INTRO_TEXT_LINES]; intro_script_rectangle CTheScripts::IntroRectangles[MAX_NUM_INTRO_RECTANGLES]; CSprite2d CTheScripts::ScriptSprites[MAX_NUM_SCRIPT_SRPITES]; script_sphere_struct CTheScripts::ScriptSphereArray[MAX_NUM_SCRIPT_SPHERES]; tUsedObject CTheScripts::UsedObjectArray[MAX_NUM_USED_OBJECTS]; int32 CTheScripts::MultiScriptArray[MAX_NUM_MISSION_SCRIPTS]; tBuildingSwap CTheScripts::BuildingSwapArray[MAX_NUM_BUILDING_SWAPS]; CEntity* CTheScripts::InvisibilitySettingArray[MAX_NUM_INVISIBILITY_SETTINGS]; CStoredLine CTheScripts::aStoredLines[MAX_NUM_STORED_LINES]; bool CTheScripts::DbgFlag; uint32 CTheScripts::OnAMissionFlag; int32 CTheScripts::StoreVehicleIndex; bool CTheScripts::StoreVehicleWasRandom; CRunningScript *CTheScripts::pIdleScripts; CRunningScript *CTheScripts::pActiveScripts; int32 CTheScripts::NextFreeCollectiveIndex; int32 CTheScripts::LastRandomPedId; uint16 CTheScripts::NumberOfUsedObjects; bool CTheScripts::bAlreadyRunningAMissionScript; bool CTheScripts::bUsingAMultiScriptFile; uint16 CTheScripts::NumberOfMissionScripts; uint32 CTheScripts::LargestMissionScriptSize; uint32 CTheScripts::MainScriptSize; uint8 CTheScripts::FailCurrentMission; uint16 CTheScripts::NumScriptDebugLines; uint16 CTheScripts::NumberOfIntroRectanglesThisFrame; uint16 CTheScripts::NumberOfIntroTextLinesThisFrame; uint8 CTheScripts::UseTextCommands; CMissionCleanup CTheScripts::MissionCleanUp; CUpsideDownCarCheck CTheScripts::UpsideDownCars; CStuckCarCheck CTheScripts::StuckCars; uint16 CTheScripts::CommandsExecuted; uint16 CTheScripts::ScriptsUpdated; int32 ScriptParams[32]; uint8 CTheScripts::RiotIntensity; uint32 CTheScripts::LastMissionPassedTime; uint16 CTheScripts::NumberOfExclusiveMissionScripts; bool CTheScripts::bPlayerHasMetDebbieHarry; bool CTheScripts::bPlayerIsInTheStatium; #if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) int16 CTheScripts::CardStack[CARDS_IN_DECK * MAX_DECKS]; int16 CTheScripts::CardStackPosition; #endif #ifdef MISSION_REPLAY static const char* nonMissionScripts[] = { "copcar", "ambulan", "taxi", "firetru", "rampage", "t4x4_1", "t4x4_2", "t4x4_3", "rc1", "rc2", "rc3", "rc4", "hj", "usj", "mayhem" }; int AllowMissionReplay; uint32 NextMissionDelay; uint32 MissionStartTime; uint32 WaitForMissionActivate; uint32 WaitForSave; float oldTargetX; float oldTargetY; int missionRetryScriptIndex; bool doingMissionRetry; bool gbTryingPorn4Again; int IsInAmmunation; int MissionSkipLevel; #endif #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #define REGISTER_COMMAND(command, in, out, cond, ovrd, visual) { command, #command, in, out, cond, ovrd, visual } #define INPUT_ARGUMENTS(...) { __VA_ARGS__ ARGTYPE_NONE } #define OUTPUT_ARGUMENTS(...) { __VA_ARGS__ ARGTYPE_NONE } const tScriptCommandData commands[] = { REGISTER_COMMAND(COMMAND_NOP, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WAIT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GOTO, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SHAKE_CAM, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_ADD_VAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_VAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_VAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_VAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_SUB_VAL_FROM_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_VAL_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_VAL_FROM_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_VAL_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_MULT_INT_VAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_FLOAT_VAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_INT_LVAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_FLOAT_LVAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_DIV_INT_VAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_FLOAT_VAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_INT_LVAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_FLOAT_LVAR_BY_VAL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_THAN_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_THAN_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_THAN_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_THAN_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_THAN_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_THAN_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_THAN_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_THAN_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_THAN_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_THAN_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_NOT_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " =="), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, 0, " !="), REGISTER_COMMAND(COMMAND_GOTO_IF_TRUE, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GOTO_IF_FALSE, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_TERMINATE_THIS_SCRIPT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_START_NEW_SCRIPT, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GOSUB, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RETURN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LINE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_PLAYER_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ADD_INT_VAR_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_FLOAT_VAR_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_INT_LVAR_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_INT_VAR_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_FLOAT_VAR_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_INT_LVAR_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +="), REGISTER_COMMAND(COMMAND_SUB_INT_VAR_FROM_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_INT_LVAR_FROM_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_INT_VAR_FROM_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_INT_LVAR_FROM_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -="), REGISTER_COMMAND(COMMAND_MULT_INT_VAR_BY_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_FLOAT_VAR_BY_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_INT_LVAR_BY_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_INT_VAR_BY_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_FLOAT_VAR_BY_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_INT_LVAR_BY_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " *="), REGISTER_COMMAND(COMMAND_DIV_INT_VAR_BY_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_FLOAT_VAR_BY_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_INT_LVAR_BY_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_INT_VAR_BY_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_FLOAT_VAR_BY_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_INT_LVAR_BY_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " /="), REGISTER_COMMAND(COMMAND_ADD_TIMED_VAL_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), REGISTER_COMMAND(COMMAND_ADD_TIMED_VAL_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), REGISTER_COMMAND(COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), REGISTER_COMMAND(COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), REGISTER_COMMAND(COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), REGISTER_COMMAND(COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " +=@"), REGISTER_COMMAND(COMMAND_SUB_TIMED_VAL_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), REGISTER_COMMAND(COMMAND_SUB_TIMED_VAL_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), REGISTER_COMMAND(COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), REGISTER_COMMAND(COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), REGISTER_COMMAND(COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), REGISTER_COMMAND(COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " -=@"), REGISTER_COMMAND(COMMAND_SET_VAR_INT_TO_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_LVAR_INT_TO_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_VAR_FLOAT_TO_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_LVAR_FLOAT_TO_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_VAR_FLOAT_TO_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_LVAR_FLOAT_TO_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_VAR_INT_TO_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_LVAR_INT_TO_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_CSET_VAR_INT_TO_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), REGISTER_COMMAND(COMMAND_CSET_VAR_FLOAT_TO_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), REGISTER_COMMAND(COMMAND_CSET_LVAR_INT_TO_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, 0, " =#"), REGISTER_COMMAND(COMMAND_CSET_LVAR_FLOAT_TO_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, 0, " =#"), REGISTER_COMMAND(COMMAND_CSET_VAR_INT_TO_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), REGISTER_COMMAND(COMMAND_CSET_VAR_FLOAT_TO_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " =#"), REGISTER_COMMAND(COMMAND_CSET_LVAR_INT_TO_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, 0, " =#"), REGISTER_COMMAND(COMMAND_CSET_LVAR_FLOAT_TO_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, 0, " =#"), REGISTER_COMMAND(COMMAND_ABS_VAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ABS"), REGISTER_COMMAND(COMMAND_ABS_LVAR_INT, INPUT_ARGUMENTS(ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, 0, " ABS"), REGISTER_COMMAND(COMMAND_ABS_VAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ABS"), REGISTER_COMMAND(COMMAND_ABS_LVAR_FLOAT, INPUT_ARGUMENTS(ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, 0, " ABS"), REGISTER_COMMAND(COMMAND_GENERATE_RANDOM_FLOAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT,), false, -1, ""), REGISTER_COMMAND(COMMAND_GENERATE_RANDOM_INT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), false, -1, ""), REGISTER_COMMAND(COMMAND_DELETE_CHAR, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CHAR_WANDER_DIR, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CHAR_WANDER_RANGE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CHAR_FOLLOW_PATH, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CHAR_SET_IDLE, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CHAR_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_STILL_ALIVE, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE,), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_PED_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), false, -1, ""), REGISTER_COMMAND(COMMAND_DELETE_CAR, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CAR_GOTO_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CAR_WANDER_RANDOMLY, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CAR_SET_IDLE, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CAR_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_STILL_ALIVE, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE,), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_CRUISE_SPEED, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_DRIVING_STYLE, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_MISSION, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_VEHICLE_HANDLE, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_BOOL,), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SPECIAL_0, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SPECIAL_1, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SPECIAL_2, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SPECIAL_3, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SPECIAL_4, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SPECIAL_5, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SPECIAL_6, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SPECIAL_7, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_PRINTS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_TIME_OF_DAY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TIME_OF_DAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_MINUTES_TO_TIME_OF_DAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT,), OUTPUT_ARGUMENTS(ARGTYPE_INT,), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_POINT_ON_SCREEN, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT,), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DEBUG_ON, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DEBUG_OFF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RETURN_TRUE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RETURN_FALSE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_VAR_INT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_VAR_FLOAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LVAR_INT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LVAR_FLOAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LBRACKET, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RBRACKET, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REPEAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ENDREPEAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IFNOT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ELSE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ENDIF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WHILE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WHILENOT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ENDWHILE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ANDOR, INPUT_ARGUMENTS(ARGTYPE_ANDOR,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LAUNCH_MISSION, INPUT_ARGUMENTS(ARGTYPE_LABEL,), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MISSION_HAS_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STORE_CAR_CHAR_IS_IN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_STORE_CAR_PLAYER_IS_IN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_BUTTON_PRESSED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_PAD_STATE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_DELETE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SCORE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_SCORE_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_STORE_SCORE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GIVE_REMOTE_CONTROLLED_CAR_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ALTER_WANTED_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ALTER_WANTED_LEVEL_NO_DROP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_WANTED_LEVEL_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_WANTED_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_DEATHARREST_STATE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_DEATHARREST_BEEN_EXECUTED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ADD_AMMO_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_AMMO_TO_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_AMMO_TO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STILL_ALIVE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_DEAD, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_DEAD, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_DEAD, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_THREAT_SEARCH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_THREAT_REACTION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_NO_OBJ, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ORDER_DRIVER_OUT_OF_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ORDER_CHAR_TO_DRIVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_PATROL_POINT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_GANGZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_PRESSING_HORN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CHAR_SPOTTED_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ORDER_CHAR_TO_BACKDOOR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_CHAR_TO_GANG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_OBJECTIVE_PASSED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_DRIVE_AGGRESSION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_MAX_DRIVESPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_CHAR_INSIDE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_WARP_PLAYER_FROM_CAR_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MAKE_CHAR_DO_NOTHING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_INVINCIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_INVINCIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_GRAPHIC_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_GRAPHIC_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_PLAYER_BEEN_ARRESTED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_STOP_CHAR_DRIVING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_KILL_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FAVOURITE_CAR_MODEL_FOR_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OCCUPATION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CHANGE_CAR_LOCK, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SHAKE_CAM_WITH_POINT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_REMAP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CAR_JUST_SUNK, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_NO_COLLIDE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_DEAD_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_DEAD_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_TRAILER_ATTACHED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_ON_TRAILER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CAR_GOT_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_PARK, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_PARK_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_KILL_ALL_PASSENGERS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_BULLETPROOF, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_FLAMEPROOF, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_ROCKETPROOF, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CARBOMB_ACTIVE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GIVE_CAR_ALARM, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PUT_CAR_ON_TRAILER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_CRUSHED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_GANG_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_CAR_GENERATOR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_CAR_GENERATOR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_PAGER_MESSAGE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_ONSCREEN_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_ONSCREEN_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_ONSCREEN_COUNTER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_ONSCREEN_COUNTER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ZONE_CAR_INFO, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_GANG_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_DENSITY, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PED_DENSITY, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_POINT_CAMERA_AT_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_POINT_CAMERA_AT_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_POINT_CAMERA_AT_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RESTORE_CAMERA, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SHAKE_PAD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ZONE_PED_INFO, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TIME_SCALE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_IN_AIR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_FIXED_CAMERA_POSITION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_POINT_CAMERA_AT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CAR_OLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CHAR_OLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_OBJECT_OLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_BLIP, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CHANGE_BLIP_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DIM_BLIP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_COORD_OLD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CHANGE_BLIP_SCALE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FADING_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DO_FADE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_FADING_STATUS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ADD_HOSPITAL_RESTART, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_POLICE_RESTART, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_OVERRIDE_NEXT_RESTART, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DRAW_SHADOW, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_PLAYER_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CHAR_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CAR_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_OBJECT_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_TOUCHING_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_TOUCHING_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_AMMO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_AMMO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_AMMO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_CAMERA_SPLINE, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MOVE_CAMERA_ALONG_SPLINE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CAMERA_POSITION_ALONG_SPLINE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_DECLARE_MISSION_FLAG, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DECLARE_MISSION_FLAG_FOR_CONTACT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DECLARE_BASE_BRIEF_ID_FOR_CONTACT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_HEALTH_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_HEALTH_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_HEALTH_GREATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_CONTACT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CHANGE_BLIP_DISPLAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_ONE_OFF_SOUND, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_CONTINUOUS_SOUND, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_SOUND, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_STUCK_ON_ROOF, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ADD_UPSIDEDOWN_CAR_CHECK, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_UPSIDEDOWN_CAR_CHECK, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_WAIT_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GUARD_SPOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GUARD_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_WAIT_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_STOPPED_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_STOPPED_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GIVE_WEAPON_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GIVE_WEAPON_TO_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GIVE_WEAPON_TO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_CONTROL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FORCE_WEATHER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FORCE_WEATHER_NOW, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RELEASE_WEATHER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CURRENT_PLAYER_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CURRENT_CHAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CURRENT_CAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_OBJECT_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_COORDINATES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_GAME_TIMER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_TURN_CHAR_TO_FACE_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_TURN_PLAYER_TO_FACE_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STORE_WANTED_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_STOPPED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_MARK_CHAR_AS_NO_LONGER_NEEDED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MARK_CAR_AS_NO_LONGER_NEEDED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MARK_OBJECT_AS_NO_LONGER_NEEDED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DONT_REMOVE_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DONT_REMOVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DONT_REMOVE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_CHAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_KILL_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_KILL_CHAR_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_LEAVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_DRIVER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_DESTROY_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_DESTROY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_AREA_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_AREA_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GUARD_ATTACK, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_AS_LEADER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_AS_LEADER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LEAVE_GROUP, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FOLLOW_ROUTE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_ROUTE_POINT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_ROADS_ON, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_ROADS_OFF, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_PASSENGERS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_MAXIMUM_NUMBER_OF_PASSENGERS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_DENSITY_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_HEAVY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_CHAR_THREAT_SEARCH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ACTIVATE_CRANE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DEACTIVATE_CRANE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_MAX_WANTED_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SAVE_VAR_INT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SAVE_VAR_FLOAT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_IN_AIR_PROPER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_UPSIDEDOWN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_PLAYER_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CANCEL_OVERRIDE_RESTART, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_POLICE_IGNORE_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_PAGER_MESSAGE_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_START_KILL_FRENZY, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_READ_KILL_FRENZY_STATUS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SQRT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GENERATE_RANDOM_FLOAT_IN_RANGE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GENERATE_RANDOM_INT_IN_RANGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_LOCK_CAR_DOORS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_EXPLODE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_EXPLOSION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_UPRIGHT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_TURN_CHAR_TO_FACE_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_TURN_CHAR_TO_FACE_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_TURN_PLAYER_TO_FACE_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_COORD_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_COORD_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_PICKUP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_PICKUP_BEEN_COLLECTED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_PICKUP, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TAXI_LIGHTS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_BIG_Q, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_NUMBER_BIG_Q, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GARAGE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GARAGE_WITH_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TARGET_CAR_FOR_MISSION_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_IN_MISSION_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_FREE_BOMBS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_POWERPOINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ALL_TAXI_LIGHTS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_ARMED_WITH_ANY_BOMB, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_APPLY_BRAKES_TO_PLAYERS_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_PLAYER_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CHAR_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CAR_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_ARMED_WITH_BOMB, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CHANGE_CAR_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_PED_ROADS_ON, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_PED_ROADS_OFF, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CHAR_LOOK_AT_CHAR_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CHAR_LOOK_AT_PLAYER_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PLAYER_LOOK_AT_CHAR_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STOP_CHAR_LOOKING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STOP_PLAYER_LOOKING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_HELICOPTER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GANG_ATTITUDE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GANG_GANG_ATTITUDE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GANG_PLAYER_ATTITUDE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GANG_PED_MODELS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GANG_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GANG_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_RUN_TO_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_RUN_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_TOUCHING_OBJECT_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_TOUCHING_OBJECT_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_SPECIAL_CHARACTER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_SPECIAL_CHARACTER_LOADED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_FLASH_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FLASH_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FLASH_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_REMOTE_MODE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ARM_CAR_WITH_BOMB, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_PERSONALITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CUTSCENE_OFFSET, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ANIM_GROUP_FOR_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ANIM_GROUP_FOR_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REQUEST_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_MODEL_LOADED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_MARK_MODEL_AS_NO_LONGER_NEEDED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GRAB_PHONE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_REPEATED_PHONE_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PHONE_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_PHONE_DISPLAYED_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_TURN_PHONE_OFF, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DRAW_CORONA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DRAW_LIGHT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STORE_WEATHER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RESTORE_WEATHER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STORE_CLOCK, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RESTORE_CLOCK, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RESTART_CRITICAL_MISSION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_PLAYING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_NO_OBJ, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_WAIT_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GUARD_SPOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GUARD_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_WAIT_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_KILL_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_KILL_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_KILL_CHAR_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_KILL_PLAYER_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_LEAVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_DRIVER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FOLLOW_CAR_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_DESTROY_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_DESTROY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_AREA_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_AREA_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GUARD_ATTACK, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_FOLLOW_ROUTE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_COORD_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_COORD_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_RUN_TO_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_RUN_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_PEDS_IN_AREA_TO_COLL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_PEDS_IN_VEHICLE_TO_COLL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_COLL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_COLL_IN_CARS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_IN_CAR_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_COLL_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_COLL_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_COLL_STOPPED_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_PEDS_IN_COLL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_HEED_THREATS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_HEED_THREATS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CONTROLLER_MODE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAN_RESPRAY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_UNLOAD_SPECIAL_CHARACTER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RESET_NUM_OF_MODELS_KILLED_BY_PLAYER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NUM_OF_MODELS_KILLED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ACTIVATE_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_TAXI_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_OBJECT_NO_OFFSET, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_BOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_AREA_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_GOTO_AREA_ANY_MEANS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_STOPPED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_MESSAGE_WAIT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_PARTICLE_EFFECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_WIDESCREEN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_CONTACT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_ONLY_DAMAGED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_ONLY_DAMAGED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_PROOFS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_PROOFS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DEACTIVATE_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_CARS_COLLECTED_BY_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CAR_BEEN_TAKEN_TO_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_SWAT_REQUIRED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FBI_REQUIRED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ARMY_REQUIRED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_CLOSEST_CHAR_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CLOSEST_CAR_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CAR_GOTO_COORDINATES_ACCURATE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_START_PACMAN_RACE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_START_PACMAN_RECORD, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_POWER_PILLS_EATEN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_PACMAN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_START_PACMAN_SCRAMBLE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_POWER_PILLS_CARRIED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_CARRIED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_ON_SCREEN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_ON_SCREEN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_OBJECT_ON_SCREEN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GOSUB_FILE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_GROUND_Z_FOR_3D_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_START_SCRIPT_FIRE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_SCRIPT_FIRE_EXTINGUISHED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_SCRIPT_FIRE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COMEDY_CONTROLS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_BOAT_GOTO_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_BOAT_STOP, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_SHOOTING_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_SHOOTING_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CURRENT_PLAYER_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CURRENT_CHAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_EATEN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_POWER_PILL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_BOAT_CRUISE_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_RANDOM_CHAR_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_RANDOM_CHAR_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_SHOOTING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_SHOOTING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_MONEY_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_ACCURACY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CAR_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_CUTSCENE, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_CUTSCENE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CUTSCENE_ANIM, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_START_CUTSCENE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CUTSCENE_TIME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CUTSCENE_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_CUTSCENE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RESTORE_CAMERA_JUMPCUT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_COLLECTABLE1, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLLECTABLE1_TOTAL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PROJECTILE_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DESTROY_PROJECTILES_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DROP_MINE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DROP_NAUTICAL_MINE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_SPECIAL_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_CUTSCENE_HEAD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CUTSCENE_HEAD_ANIM, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SIN, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_COS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CAR_FORWARD_X, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CAR_FORWARD_Y, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CHANGE_GARAGE_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ACTIVATE_CRUSHER_CRANE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_2_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_2_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_2_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_3_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_3_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_3_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_4_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_4_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_4_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_5_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_5_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_5_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_6_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_6_NUMBERS_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_6_NUMBERS_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FOLLOW_CHAR_IN_FORMATION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PLAYER_MADE_PROGRESS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PROGRESS_TOTAL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_JUMP_DISTANCE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_JUMP_HEIGHT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_JUMP_FLIPS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_JUMP_SPINS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_JUMP_STUNT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_UNIQUE_JUMP_FOUND, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_UNIQUE_JUMPS_TOTAL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_PASSENGER_DROPPED_OFF_TAXI, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_MONEY_MADE_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_MISSION_GIVEN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_MISSION_PASSED, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_RUNNING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_ALL_SCRIPT_FIRES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_FIRST_CAR_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_SECOND_CAR_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CAR_BEEN_DAMAGED_BY_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_CHARS_GROUP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_PLAYERS_GROUP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_EXPLODE_CHAR_HEAD, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_EXPLODE_PLAYER_HEAD, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ANCHOR_BOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ZONE_GROUP, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_START_CAR_FIRE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_START_CHAR_FIRE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_RESPRAY_HAPPENED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAMERA_ZOOM, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_PICKUP_WITH_AMMO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_RAM_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_BLOCK_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_CATCH_TRAIN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_CATCH_TRAIN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_NEVER_GETS_TIRED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_FAST_RELOAD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_BLEEDING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_FUNNY_SUSPENSION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_BIG_WHEELS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FREE_RESPRAYS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_AREA_OCCUPIED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_START_DRUG_RUN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_DRUG_RUN_BEEN_COMPLETED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_DRUG_PLANE_BEEN_SHOT_DOWN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SAVE_PLAYER_FROM_FIRES, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_TEXT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_SCALE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_JUSTIFY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_CENTRE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_WRAPX, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_CENTRE_SIZE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_BACKGROUND, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_BACKGROUND_COLOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_BACKGROUND_ONLY_TEXT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_PROPORTIONAL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_FONT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_INDUSTRIAL_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_COMMERCIAL_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SUBURBAN_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ROTATE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SLIDE_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_CHAR_ELEGANTLY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_STAY_IN_SAME_PLACE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_NASTY_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_UNDRESS_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DRESS_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_START_CHASE_SCENE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STOP_CHASE_SCENE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_EXPLOSION_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_EXPLOSION_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_START_DRUG_DROP_OFF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_DROP_OFF_PLANE_BEEN_SHOT_DOWN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_FIND_DROP_OFF_PLANE_COORDINATES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_FLOATING_PACKAGE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_PLACE_OBJECT_RELATIVE_TO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MAKE_OBJECT_TARGETTABLE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_ARMOUR_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_ARMOUR_TO_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_OPEN_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLOSE_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WARP_CHAR_FROM_CAR_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_VISIBILITY_OF_CLOSEST_OBJECT_OF_TYPE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CHAR_SPOTTED_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_HAIL_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_OBJECT_BEEN_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_START_KILL_FRENZY_HEADSHOT, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ACTIVATE_MILITARY_CRANE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WARP_PLAYER_INTO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WARP_CHAR_INTO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_CAR_RADIO, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_AUDIO_STREAM, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_2_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_3_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_4_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_5_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_WITH_6_NUMBERS_BIG, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_WAIT_STATE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAMERA_BEHIND_PLAYER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_MOTION_BLUR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_STRING_IN_STRING, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_RANDOM_CHAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_2_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_2_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_3_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_3_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_4_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_4_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_SNIPER_BULLET_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GIVE_PLAYER_DETONATOR, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_COLL_OBJ_STEAL_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_ICECREAM_JINGLE_ON, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_STRING_IN_STRING_NOW, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_STRING_IN_STRING_SOON, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_5_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_5_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_6_REPEATED_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_6_PHONE_MESSAGES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_POINT_OBSCURED_BY_A_MISSION_ENTITY, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_ALL_MODELS_NOW, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_TO_OBJECT_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DRAW_SPRITE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DRAW_RECT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_SPRITE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_TEXTURE_DICTIONARY, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_TEXTURE_DICTIONARY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_DYNAMIC, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_ANIM_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PLAY_MISSION_PASSED_TUNE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FREEZE_ONSCREEN_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_CAR_SIREN, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_PED_ROADS_ON_ANGLED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_PED_ROADS_OFF_ANGLED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_ROADS_ON_ANGLED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_ROADS_OFF_ANGLED, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_WATERTIGHT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_MOVING_PARTICLE_EFFECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_CANT_BE_DRAGGED_OUT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_TURN_CAR_TO_FACE_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CRANE_LIFTING_CAR, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DRAW_SPHERE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_STATUS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_MALE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SCRIPT_NAME, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CHANGE_GARAGE_TYPE_WITH_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FIND_DRUG_PLANE_COORDINATES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SAVE_INT_TO_DEBUG_FILE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SAVE_FLOAT_TO_DEBUG_FILE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SAVE_NEWLINE_TO_DEBUG_FILE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_POLICE_RADIO_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_STRONG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_ROUTE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_RUBBISH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_PARTICLE_EFFECTS_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_STREAMING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_GARAGE_OPEN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_GARAGE_CLOSED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_START_CATALINA_HELI, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CATALINA_HELI_TAKE_OFF, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_CATALINA_HELI, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CATALINA_HELI_BEEN_SHOT_DOWN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SWAP_NEAREST_BUILDING_MODEL, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_WORLD_PROCESSING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_ALL_PLAYER_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GRAB_CATALINA_HELI, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_AREA_OF_CARS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ROTATING_GARAGE_DOOR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SPHERE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_SPHERE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CATALINA_HELI_FLY_AWAY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_EVERYONE_IGNORE_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STORE_CAR_CHAR_IS_IN_NO_SAVE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_STORE_CAR_PLAYER_IS_IN_NO_SAVE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PHONE_DISPLAYING_MESSAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_ONSCREEN_TIMER_WITH_STRING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_ONSCREEN_COUNTER_WITH_STRING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_RANDOM_CAR_FOR_CAR_PARK, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_COLLISION_IN_MEMORY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_WANTED_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_VISIBLY_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DOES_OBJECT_EXIST, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_SCENE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_STUCK_CAR_CHECK, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_STUCK_CAR_CHECK, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_STUCK, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_MISSION_AUDIO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_MISSION_AUDIO_LOADED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_PLAY_MISSION_AUDIO, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_MISSION_AUDIO_FINISHED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_IMPORT_GARAGE_SLOT_BEEN_FILLED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_THIS_PRINT, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_THIS_BIG_PRINT, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_MISSION_AUDIO_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ACTIVATE_SAVE_MENU, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_SAVE_GAME_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_NO_SPECIAL_CAMERA_FOR_THIS_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_PICKUP_OLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLIP_FOR_PICKUP, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SPRITE_BLIP_FOR_PICKUP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PED_DENSITY_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FORCE_RANDOM_PED_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_DRAW_BEFORE_FADE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_COLLECTABLE1S_COLLECTED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_LEAVE_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_SPRITES_DRAW_BEFORE_FADE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TEXT_RIGHT_JUSTIFY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_HELP, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_HELP, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FLASH_HUD_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FLASH_RADAR_BLIP, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_CONTROL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_GENERATE_CARS_AROUND_CAMERA, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_SMALL_PRINTS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_MILITARY_CRANE_COLLECTED_ALL_CARS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_UPSIDEDOWN_CAR_NOT_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CAN_PLAYER_START_MISSION, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_MAKE_PLAYER_SAFE_FOR_CUTSCENE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_USE_TEXT_COMMANDS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_THREAT_FOR_PED_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_THREAT_FOR_PED_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CAR_COLOURS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_ALL_CARS_CAN_BE_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_CAN_BE_DAMAGED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MAKE_PLAYER_UNSAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_BODY_CAST_HEALTH, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHARS_CHATTING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MAKE_PLAYER_SAFE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_STAYS_IN_CURRENT_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_STAYS_IN_CURRENT_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_DRUNK_INPUT_DELAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_MONEY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_INCREASE_CHAR_MONEY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_OFFSET_FROM_OBJECT_IN_WORLD_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_LIFE_SAVED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_CRIMINAL_CAUGHT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_AMBULANCE_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_FIRE_EXTINGUISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_TURN_PHONE_ON, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_LONGEST_DODO_FLIGHT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_OFFSET_FROM_CAR_IN_WORLD_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TOTAL_NUMBER_OF_KILL_FRENZIES, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_BLOW_UP_RC_BUGGY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_CAR_FROM_CHASE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_FRENCH_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_GERMAN_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_MISSION_AUDIO, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FADE_IN_AFTER_NEXT_ARREST, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FADE_IN_AFTER_NEXT_DEATH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GANG_PED_MODEL_PREFERENCE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_USE_PEDNODE_SEEK, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_VEHICLE_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GET_OUT_OF_JAIL_FREE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FREE_HEALTH_CARE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_DOOR_CLOSED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_AND_LAUNCH_MISSION, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_AND_LAUNCH_MISSION_INTERNAL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_DRAW_LAST, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_AMMO_IN_PLAYER_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_AMMO_IN_CHAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_KILL_FRENZY_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_SAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_NEAR_CLIP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_RADIO_CHANNEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_OVERRIDE_HOSPITAL_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_OVERRIDE_POLICE_STATION_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FORCE_RAIN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DOES_GARAGE_CONTAIN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_TRACTION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ARE_MEASUREMENTS_IN_METRES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CONVERT_METRES_TO_FEET, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_MARK_ROADS_BETWEEN_LEVELS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MARK_PED_ROADS_BETWEEN_LEVELS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_AVOID_LEVEL_TRANSITIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_AVOID_LEVEL_TRANSITIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_THREAT_FOR_PED_TYPE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_AREA_OF_CHARS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TOTAL_NUMBER_OF_MISSIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CONVERT_METRES_TO_FEET_INT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_FASTEST_TIME, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_HIGHEST_SCORE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WARP_CHAR_INTO_CAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_PASSENGER_SEAT_FREE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_CHAR_IN_CAR_PASSENGER_SEAT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_IS_CHRIS_CRIMINAL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_START_CREDITS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STOP_CREDITS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ARE_CREDITS_FINISHED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_SINGLE_PARTICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_IGNORE_LEVEL_TRANSITIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CHASE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_START_BOAT_FOAM_ANIMATION, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_UPDATE_BOAT_FOAM_ANIMATION, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_MUSIC_DOES_FADE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_INTRO_IS_PLAYING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_HOOKER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PLAY_END_OF_GAME_TUNE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_STOP_END_OF_GAME_TUNE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CAR_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_SITTING_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_SITTING_IN_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_SCRIPT_FIRE_AUDIO, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ARE_ANY_CAR_CHEATS_ACTIVATED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_SUFFERS_CRITICAL_HITS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_LIFTING_A_PHONE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_SITTING_IN_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_SITTING_IN_ANY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_COLLISION_WITH_SCREEN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_SPLASH_SCREEN, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_IGNORE_LEVEL_TRANSITIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MAKE_CRAIGS_CAR_A_BIT_STRONGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_JAMES_CAR_ON_PATH_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_END_OF_GAME_TUNE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ENABLE_PLAYER_CONTROL_CAMERA, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_ROTATION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_DEBUG_CAMERA_COORDINATES, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_DEBUG_CAMERA_FRONT_VECTOR, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_TARGETTING_ANY_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_TARGETTING_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_TARGETTING_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_TEXT_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_TEXT_WITH_2_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FAIL_CURRENT_MISSION, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CLOSEST_OBJECT_OF_TYPE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_PLACE_OBJECT_RELATIVE_TO_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ALL_OCCUPANTS_OF_CAR_LEAVE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_INTERPOLATION_PARAMETERS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_TOWARDS_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_AWAY_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_DEBUG_CAMERA_POINT_AT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ATTACH_CHAR_TO_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DETACH_CHAR_FROM_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_CHANGE_LANE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_CHAR_LAST_WEAPON_DAMAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_CAR_LAST_WEAPON_DAMAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_RANDOM_COP_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_RANDOM_COP_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_FLEE_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_DRIVER_OF_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_FOLLOWERS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GIVE_REMOTE_CONTROLLED_MODEL_TO_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CURRENT_PLAYER_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CURRENT_CHAR_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_TEMP_ACTION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_HANDBRAKE_TURN_RIGHT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_HANDBRAKE_STOP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_ON_ANY_BIKE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_SNIPER_BULLET_2D, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_SNIPER_BULLET_3D, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_SEATS_IN_MODEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_ON_ANY_BIKE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_LYING_DOWN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CAN_CHAR_SEE_DEAD_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_ENTER_CAR_RANGE_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_THREAT_REACTION_RANGE_MULTIPLIER, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_CEASE_ATTACK_TIMER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_REMOTE_CONTROLLED_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PC_VERSION, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_REPLAY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_REPLAY_PLAYING, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_MODEL_AVAILABLE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SHUT_CHAR_UP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ENABLE_RC_DETONATE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_RANDOM_ROUTE_SEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_ANY_PICKUP_AT_COORDS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_FIRST_PICKUP_COORDS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NEXT_PICKUP_COORDS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_ALL_CHAR_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_PLAYER_GOT_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CHAR_GOT_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_FACING_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_TANK_DETONATE_CARS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_POSITION_OF_ANALOGUE_STICKS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_ON_FIRE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_TYRE_BURST, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_DRIVE_STRAIGHT_AHEAD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_WAIT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_STANDING_ON_A_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_FOOT_DOWN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_FOOT_DOWN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_INITIALISE_OBJECT_PATH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_START_OBJECT_ON_PATH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_PATH_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_PATH_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_OBJECT_DISTANCE_ALONG_PATH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_OBJECT_PATH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HELI_GOTO_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_INT_VAR_EQUAL_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_EQUAL_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_DEAD_CHAR_PICKUP_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_PROTECTION_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_BOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANY_BOAT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_HELI, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANY_HELI, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_PLANE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_ANY_PLANE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_VAR_INT_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_SET_LVAR_INT_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, 0, " ="), REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_THAN_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_THAN_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_CONSTANT_GREATER_THAN_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_CONSTANT_GREATER_THAN_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, 0, " >"), REGISTER_COMMAND(COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_CONSTANT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_VAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_LVAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, 0, " >="), REGISTER_COMMAND(COMMAND_GET_CHAR_WEAPON_IN_SLOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CLOSEST_STRAIGHT_ROAD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_FORWARD_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_AREA_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CUTSCENE_ANIM_TO_LOOP, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MARK_CAR_AS_CONVOY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_RESET_HAVOC_CAUSED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_HAVOC_CAUSED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_SCRIPT_ROADBLOCK, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_ALL_SCRIPT_ROADBLOCKS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_WALK_TO_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PICKUP_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CHAR_BEEN_PHOTOGRAPHED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_AIM_GUN_AT_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_SECURITY_CAMERA, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_FLYING_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_FLYING_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_SONY_CD_BEEN_READ, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_NUMBER_OF_SONY_CDS_READ, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD_OLD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_COORD, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_CLOTHES, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_HELI_ORIENTATION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_HELI_ORIENTATION, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PLANE_GOTO_COORDS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NTH_CLOSEST_CAR_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NTH_CLOSEST_CHAR_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_DRAW_WEAPONSHOP_CORONA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ENABLE_RC_DETONATE_ON_CONTACT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_FREEZE_CHAR_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_DROWNS_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_RECORDS_COLLISIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_OBJECT_COLLIDED_WITH_ANYTHING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_RC_BUGGY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_PHOTOGRAPH_BEEN_TAKEN, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_CHAR_ARMOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_ARMOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_HELI_STABILISER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_STRAIGHT_LINE_DISTANCE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_POP_CAR_BOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SHUT_PLAYER_UP, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_MOOD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REQUEST_COLLISION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_OBJECT_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_LOCATE_OBJECT_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_OBJECT_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR_EVEN_MISSION_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_OBJECT_IN_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_OBJECT_IN_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_CROUCH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_ZONE_CIVILIAN_CAR_INFO, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REQUEST_ANIMATION, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_ANIMATION_LOADED, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_ANIMATION, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_WAITING_FOR_WORLD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_WAITING_FOR_WORLD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_OBJECT_WAITING_FOR_WORLD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_SHUFFLE_INTO_DRIVERS_SEAT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ATTACH_CHAR_TO_OBJECT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_AS_PLAYER_FRIEND, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER_WITH_STRING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SET_PIECE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_EXTRA_COLOURS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_EXTRA_COLOURS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLOSE_CAR_BOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_WHEELIE_STATS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_DISARM_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_BURST_CAR_TYRE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_OBJ_NO_OBJ, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_WEARING, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_CAN_DO_DRIVE_BY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_SPRINT_TO_COORD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_SWAT_ROPE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FIRST_PERSON_CONTROL_CAMERA, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_NEAREST_TYRE_TO_POINT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAR_MODEL_COMPONENTS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SWITCH_LIFT_CAMERA, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLOSE_ALL_CAR_DOORS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_DISTANCE_BETWEEN_COORDS_2D, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_DISTANCE_BETWEEN_COORDS_3D, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_POP_CAR_BOOT_USING_PHYSICS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FIRST_PERSON_WEAPON_CAMERA, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_LEAVING_VEHICLE_TO_DIE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SORT_OUT_OBJECT_COLLISION_WITH_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_MAX_WANTED_LEVEL, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_WANDER_PATH_CLEAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_HELP_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_HELP_FOREVER, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PRINT_HELP_FOREVER_WITH_NUMBER, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_CAN_BE_DAMAGED_BY_MEMBERS_OF_GANG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_AND_LAUNCH_MISSION_EXCLUSIVE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_MISSION_AUDIO_PLAYING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_LOCKED_PROPERTY_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_FORSALE_PROPERTY_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_FREEZE_CAR_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_RADIO_CHANNEL, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_TEXT_WITH_3_NUMBERS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_DROWNING_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_DROWNING_IN_WATER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DISABLE_CUTSCENE_SHADOWS, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_GLASS_BEEN_SHATTERED_NEARBY, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ATTACH_CUTSCENE_OBJECT_TO_BONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ATTACH_CUTSCENE_OBJECT_TO_COMPONENT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_STAY_IN_CAR_WHEN_JACKED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_MISSION_AUDIO_LOADING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_WEAPONS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_PROPERTY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_AUTO_PAINTING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_ANSWERING_MOBILE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_DRUNKENNESS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_PLAYER_DRUNKENNESS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_DRUG_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_PLAYER_DRUG_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_LOAN_SHARK_VISITS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_STORES_KNOCKED_OFF, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_MOVIE_STUNTS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_NUMBER_OF_ASSASSINATIONS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_PIZZAS_DELIVERED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_GARBAGE_PICKUPS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_ICE_CREAMS_SOLD, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TOP_SHOOTING_RANGE_SCORE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SHOOTING_RANGE_RANK, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_MONEY_SPENT_ON_GAMBLING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_MONEY_WON_ON_GAMBLING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_LARGEST_GAMBLING_WIN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_IN_PLAYERS_GROUP_CAN_FIGHT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_CHAR_WAIT_STATE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA_NO_SAVE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CAN_BURST_CAR_TYRES, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_AUTO_AIM, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FIRE_HUNTER_GUN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PROPERTY_AS_OWNED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BLOOD_RING_KILLS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_LONGEST_TIME_IN_BLOOD_RING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_EVERYTHING_FOR_HUGE_CUTSCENE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_TOUCHING_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_TOUCHING_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CHECK_FOR_PED_MODEL_AROUND_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_CHAR_FOLLOW_PATH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_CAN_BE_SHOT_IN_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ATTACH_CUTSCENE_OBJECT_TO_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_MISSION_TEXT, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_TONIGHTS_EVENT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_CHAR_LAST_DAMAGE_ENTITY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_CAR_LAST_DAMAGE_ENTITY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FREEZE_OBJECT_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_HAS_MET_DEBBIE_HARRY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_RIOT_INTENSITY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_IN_ANGLED_AREA_2D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CAR_IN_ANGLED_AREA_3D, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_REMOVE_WEAPON_FROM_CHAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_UP_TAXI_SHORTCUT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_TAXI_SHORTCUT, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_GOTO_CAR_ON_FOOT, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_CLOSEST_WATER_NODE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_PORN_LEAFLET_TO_RUBBISH, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_CLOTHES_PICKUP, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CHANGE_BLIP_THRESHOLD, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MAKE_PLAYER_FIRE_PROOF, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_INCREASE_PLAYER_MAX_HEALTH, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_INCREASE_PLAYER_MAX_ARMOUR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_RANDOM_CHAR_AS_DRIVER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_RANDOM_CHAR_AS_PASSENGER, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_IGNORE_THREATS_BEHIND_OBJECTS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ENSURE_PLAYER_HAS_DRIVE_BY_WEAPON, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MAKE_HELI_COME_CRASHING_DOWN, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_EXPLOSION_NO_SOUND, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_AREA_VISIBLE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WAS_VEHICLE_EVER_POLICE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_NEVER_TARGETTED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_LOAD_UNCOMPRESSED_ANIM, INPUT_ARGUMENTS(ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WAS_CUTSCENE_SKIPPED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_CROUCH_WHEN_THREATENED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_IN_ANY_POLICE_VEHICLE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DOES_CHAR_EXIST, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DOES_VEHICLE_EXIST, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_BLIP_FOR_CONTACT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_CONTACT_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_STUCK, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_ALL_TAXIS_HAVE_NITRO, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_STOP_SHOOT_DONT_SEEK_ENTITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FREEZE_CAR_POSITION_AND_DONT_LOAD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FREEZE_CHAR_POSITION_AND_DONT_LOAD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FREEZE_OBJECT_POSITION_AND_DONT_LOAD_COLLISION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_FADE_AND_JUMPCUT_AFTER_RC_EXPLOSION, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_VIGILANTE_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_ALL_CHAR_ANIMS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_MAXIMUM_NUMBER_OF_CARS_IN_GARAGE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_WANTED_STARS_ARE_FLASHING, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_ALLOW_HURRICANES, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_PLAY_ANNOUNCEMENT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_PLAYER_IS_IN_STADIUM, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_BUS_FARES_COLLECTED_BY_PLAYER, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_OBJ_BUY_ICE_CREAM, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_DISPLAY_RADAR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_BEST_POSITION, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_INFO_ZONE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_STRING, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CLEAR_CHAR_ICE_CREAM_PURCHASE, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_IN_CAR_FIRE_BUTTON_PRESSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CHAR_ATTEMPTED_ATTRACTOR, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_SET_LOAD_COLLISION_FOR_CAR_FLAG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_LOAD_COLLISION_FOR_CHAR_FLAG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_LOAD_COLLISION_FOR_OBJECT_FLAG, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_BIG_GUN_FLASH, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_HAS_CHAR_BOUGHT_ICE_CREAM, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_PROGRESS_PERCENTAGE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_SHORTCUT_PICKUP_POINT, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_SHORTCUT_DROPOFF_POINT_FOR_MISSION, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_ZONE, INPUT_ARGUMENTS(ARGTYPE_STRING, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_UNLOCK_ALL_CAR_DOORS_IN_AREA, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_GANG_ATTACK_PLAYER_WITH_COPS, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_CHAR_FRIGHTENED_IN_JACKED_CAR, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_VEHICLE_TO_FADE_IN, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_ODDJOB_MISSION_PASSED, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_PLAYER_IN_SHORTCUT_TAXI, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_IS_CHAR_DUCKING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_CREATE_DUST_EFFECT_FOR_CUTSCENE_HELI, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_REGISTER_FIRE_LEVEL, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_AUSTRALIAN_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_DISARM_CAR_BOMB, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), #if (defined GTAVC_JP_PATCH || defined SUPPORT_JAPANESE_SCRIPT) REGISTER_COMMAND(COMMAND_IS_JAPANESE_GAME, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), #elif (!defined GTA_PS2) REGISTER_COMMAND(COMMAND_SET_ONSCREEN_COUNTER_FLASH_WHEN_FIRST_DISPLAYED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), #endif #if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) REGISTER_COMMAND(COMMAND_SHUFFLE_CARD_DECKS, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_FETCH_NEXT_CARD, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_OBJECT_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_DEBUG_CAMERA_ON, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_ADD_TO_OBJECT_ROTATION_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_OBJECT_ROTATION_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_OBJECT_STATIC, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_ANGLE_BETWEEN_2D_VECTORS, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_DO_2D_RECTANGLES_COLLIDE, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_OBJECT_ROTATION_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_ADD_VELOCITY_RELATIVE_TO_OBJECT_VELOCITY, INPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_OBJECT_SPEED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), #endif #if (defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT) REGISTER_COMMAND(COMMAND_MARK_CUTSCENE_START, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MARK_CUTSCENE_END, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CUTSCENE_SCROLL, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), #elif (defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) REGISTER_COMMAND(COMMAND_IS_MISSION_SKIP, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_SET_IN_AMMUNATION, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_DO_SAVE_GAME, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_IS_RETRY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(ARGTYPE_INT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_DUMMY, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MARK_CUTSCENE_START, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_MARK_CUTSCENE_END, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CUTSCENE_SCROLL, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), #endif }; #undef REGISTER_COMMAND #undef INPUT_ARGUMENTS #undef OUTPUT_ARGUMENTS static_assert(ARRAY_SIZE(commands) == LAST_SCRIPT_COMMAND, "commands array not filled"); #if SCRIPT_LOG_FILE_LEVEL == 1 || SCRIPT_LOG_FILE_LEVEL == 2 static FILE* dbg_log; #endif static void PrintToLog(const char* format, ...) { va_list va; va_start(va, format); char tmp[1024]; #ifdef _WIN32 vsprintf_s(tmp, 1024, format, va); #else vsprintf(tmp, format, va); #endif va_end(va); #if SCRIPT_LOG_FILE_LEVEL == 1 || SCRIPT_LOG_FILE_LEVEL == 2 if (dbg_log) fwrite(tmp, 1, strlen(tmp), dbg_log); #endif } #endif void FlushLog() { #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #if SCRIPT_LOG_FILE_LEVEL == 1 || SCRIPT_LOG_FILE_LEVEL == 2 if (dbg_log) fflush(dbg_log); #endif #endif } const uint32 CRunningScript::nSaveStructSize = #ifdef COMPATIBLE_SAVES 136; #else sizeof(CRunningScript); #endif CMissionCleanup::CMissionCleanup() { Init(); } void CMissionCleanup::Init() { m_nCount = 0; for (int i = 0; i < MAX_CLEANUP; i++){ m_sEntities[i].type = CLEANUP_UNUSED; m_sEntities[i].id = 0; } } cleanup_entity_struct* CMissionCleanup::FindFree() { for (int i = 0; i < MAX_CLEANUP; i++){ if (m_sEntities[i].type == CLEANUP_UNUSED) return &m_sEntities[i]; } script_assert(0); return nil; } void CMissionCleanup::AddEntityToList(int32 id, uint8 type) { cleanup_entity_struct* pNew = FindFree(); if (!pNew) return; pNew->id = id; pNew->type = type; m_nCount++; } void CMissionCleanup::RemoveEntityFromList(int32 id, uint8 type) { for (int i = 0; i < MAX_CLEANUP; i++){ if (m_sEntities[i].type == type && m_sEntities[i].id == id){ switch (m_sEntities[i].type) { case CLEANUP_CAR: { CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(m_sEntities[i].id); if (pVehicle) { if (pVehicle->bIsStaticWaitingForCollision) { pVehicle->bIsStaticWaitingForCollision = false; if (!pVehicle->GetIsStatic()) pVehicle->AddToMovingList(); } } break; } case CLEANUP_CHAR: { CPed* pPed = CPools::GetPedPool()->GetAt(m_sEntities[i].id); if (pPed) { if (pPed->bIsStaticWaitingForCollision) { pPed->bIsStaticWaitingForCollision = false; if (!pPed->GetIsStatic()) pPed->AddToMovingList(); } } break; } case CLEANUP_OBJECT: { CObject* pObject = CPools::GetObjectPool()->GetAt(m_sEntities[i].id); if (pObject) { if (pObject->bIsStaticWaitingForCollision) { pObject->bIsStaticWaitingForCollision = false; if (!pObject->GetIsStatic()) pObject->AddToMovingList(); } } break; } default: break; } m_sEntities[i].id = 0; m_sEntities[i].type = CLEANUP_UNUSED; m_nCount--; } } } void CMissionCleanup::CheckIfCollisionHasLoadedForMissionObjects() { for (int i = 0; i < MAX_CLEANUP; i++) { switch (m_sEntities[i].type) { case CLEANUP_CAR: { CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(m_sEntities[i].id); if (pVehicle) { if (pVehicle->bIsStaticWaitingForCollision) { if (CColStore::HasCollisionLoaded(pVehicle->GetPosition())) { pVehicle->bIsStaticWaitingForCollision = false; if (!pVehicle->GetIsStatic()) pVehicle->AddToMovingList(); } } } break; } case CLEANUP_CHAR: { CPed* pPed = CPools::GetPedPool()->GetAt(m_sEntities[i].id); if (pPed) { if (pPed->bIsStaticWaitingForCollision) { if (CColStore::HasCollisionLoaded(pPed->GetPosition())) { pPed->bIsStaticWaitingForCollision = false; if (!pPed->GetIsStatic()) pPed->AddToMovingList(); } } } break; } case CLEANUP_OBJECT: { CObject* pObject = CPools::GetObjectPool()->GetAt(m_sEntities[i].id); if (pObject) { if (pObject->bIsStaticWaitingForCollision) { if (CColStore::HasCollisionLoaded(pObject->GetPosition())) { pObject->bIsStaticWaitingForCollision = false; if (!pObject->GetIsStatic()) pObject->AddToMovingList(); } } } break; } default: break; } } } void CMissionCleanup::Process() { CPopulation::m_AllRandomPedsThisType = -1; CPopulation::PedDensityMultiplier = 1.0f; CCarCtrl::CarDensityMultiplier = 1.0f; CPed::nThreatReactionRangeMultiplier = 1; CPed::nEnterCarRangeMultiplier = 1; FindPlayerPed()->m_pWanted->m_fCrimeSensitivity = 1.0f; CRoadBlocks::ClearScriptRoadBlocks(); CRouteNode::Initialise(); if (!CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle) TheCamera.Restore(); TheCamera.SetWideScreenOff(); CSpecialFX::bLiftCam = false; CSpecialFX::bVideoCam = false; CTimeCycle::StopExtraColour(0); for (int i = 0; i < MISSION_AUDIO_SLOTS; i++) DMAudio.ClearMissionAudio(i); CWeather::ReleaseWeather(); for (int i = 0; i < NUM_OF_SPECIAL_CHARS; i++) CStreaming::SetMissionDoesntRequireSpecialChar(i); for (int i = 0; i < NUM_OF_CUTSCENE_OBJECTS; i++) CStreaming::SetMissionDoesntRequireModel(MI_CUTOBJ01 + i); CStreaming::ms_disableStreaming = false; CHud::m_ItemToFlash = -1; CHud::SetHelpMessage(nil, false); CUserDisplay::OnscnTimer.m_bDisabled = false; CTheScripts::RemoveScriptTextureDictionary(); CWorld::Players[0].m_pPed->m_pWanted->m_bIgnoredByCops = false; CWorld::Players[0].m_pPed->m_pWanted->m_bIgnoredByEveryone = false; CWorld::Players[0].MakePlayerSafe(false); CWorld::Players[0].m_pPed->m_nFadeDrunkenness = 1; CWorld::Players[0].m_pPed->m_nDrunkCountdown = 0; CPad::GetPad(0)->SetDrunkInputDelay(0); CWorld::Players[0].m_bDriveByAllowed = true; DMAudio.ShutUpPlayerTalking(0); CVehicle::bDisableRemoteDetonation = false; CVehicle::bDisableRemoteDetonationOnContact = false; CGameLogic::ClearShortCut(); CTheScripts::RiotIntensity = 0; CTheScripts::StoreVehicleIndex = -1; CTheScripts::StoreVehicleWasRandom = true; CTheScripts::UpsideDownCars.Init(); CTheScripts::StuckCars.Init(); for (int i = 0; i < MAX_CLEANUP; i++){ if (m_sEntities[i].type == CLEANUP_UNUSED) continue; switch (m_sEntities[i].type) { case CLEANUP_CAR: { CVehicle* v = CPools::GetVehiclePool()->GetAt(m_sEntities[i].id); if (v) CTheScripts::CleanUpThisVehicle(v); break; } case CLEANUP_CHAR: { CPed* p = CPools::GetPedPool()->GetAt(m_sEntities[i].id); if (p) CTheScripts::CleanUpThisPed(p); break; } case CLEANUP_OBJECT: { CObject* o = CPools::GetObjectPool()->GetAt(m_sEntities[i].id); if (o) CTheScripts::CleanUpThisObject(o); break; } default: break; } RemoveEntityFromList(m_sEntities[i].id, m_sEntities[i].type); } #ifdef SECUROM if ((myrand() & 3) == 2){ // if pirated game CWeather::ForceHurricaneWeather(); } #endif } /* NB: CUpsideDownCarCheck is not used by actual script at all * It has a weird usage: AreAnyCarsUpsideDown would fail any mission * just like death or arrest. */ void CUpsideDownCarCheck::Init() { for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ m_sCars[i].m_nVehicleIndex = -1; m_sCars[i].m_nUpsideDownTimer = 0; } } bool CUpsideDownCarCheck::IsCarUpsideDown(int32 id) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(id); return IsCarUpsideDown(pVehicle); } bool CUpsideDownCarCheck::IsCarUpsideDown(CVehicle* pVehicle) { assert(pVehicle); return pVehicle->GetUp().z <= UPSIDEDOWN_UP_THRESHOLD && pVehicle->GetMoveSpeed().Magnitude() < UPSIDEDOWN_MOVE_SPEED_THRESHOLD && pVehicle->GetTurnSpeed().Magnitude() < UPSIDEDOWN_TURN_SPEED_THRESHOLD; } void CUpsideDownCarCheck::UpdateTimers() { uint32 timeStep = CTimer::GetTimeStepInMilliseconds(); for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ CVehicle* v = CPools::GetVehiclePool()->GetAt(m_sCars[i].m_nVehicleIndex); if (v){ if (IsCarUpsideDown(m_sCars[i].m_nVehicleIndex)) m_sCars[i].m_nUpsideDownTimer += timeStep; else m_sCars[i].m_nUpsideDownTimer = 0; }else{ m_sCars[i].m_nVehicleIndex = -1; m_sCars[i].m_nUpsideDownTimer = 0; } } } bool CUpsideDownCarCheck::AreAnyCarsUpsideDown() { for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ if (m_sCars[i].m_nVehicleIndex >= 0 && m_sCars[i].m_nUpsideDownTimer > UPSIDEDOWN_TIMER_THRESHOLD) return true; } return false; } void CUpsideDownCarCheck::AddCarToCheck(int32 id) { uint16 index = 0; while (index < MAX_UPSIDEDOWN_CAR_CHECKS && m_sCars[index].m_nVehicleIndex >= 0) index++; #ifdef FIX_BUGS if (index >= MAX_UPSIDEDOWN_CAR_CHECKS) return; #endif m_sCars[index].m_nVehicleIndex = id; m_sCars[index].m_nUpsideDownTimer = 0; } void CUpsideDownCarCheck::RemoveCarFromCheck(int32 id) { for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ if (m_sCars[i].m_nVehicleIndex == id){ m_sCars[i].m_nVehicleIndex = -1; m_sCars[i].m_nUpsideDownTimer = 0; } } } bool CUpsideDownCarCheck::HasCarBeenUpsideDownForAWhile(int32 id) { for (int i = 0; i < MAX_UPSIDEDOWN_CAR_CHECKS; i++){ if (m_sCars[i].m_nVehicleIndex == id) return m_sCars[i].m_nUpsideDownTimer > UPSIDEDOWN_TIMER_THRESHOLD; } return false; } void stuck_car_data::Reset() { m_nVehicleIndex = -1; m_vecPos = CVector(-5000.0f, -5000.0f, -5000.0f); m_nLastCheck = -1; m_fRadius = 0.0f; m_nStuckTime = 0; m_bStuck = false; } void CStuckCarCheck::Init() { for (int i = 0; i < MAX_STUCK_CAR_CHECKS; i++) { m_sCars[i].Reset(); } } void CStuckCarCheck::Process() { uint32 timer = CTimer::GetTimeInMilliseconds(); for (int i = 0; i < MAX_STUCK_CAR_CHECKS; i++){ if (m_sCars[i].m_nVehicleIndex < 0) continue; if (timer <= m_sCars[i].m_nStuckTime + m_sCars[i].m_nLastCheck) continue; CVehicle* pv = CPools::GetVehiclePool()->GetAt(m_sCars[i].m_nVehicleIndex); if (!pv){ m_sCars[i].Reset(); continue; } float distance = (pv->GetPosition() - m_sCars[i].m_vecPos).Magnitude(); m_sCars[i].m_bStuck = distance < m_sCars[i].m_fRadius; m_sCars[i].m_vecPos = pv->GetPosition(); m_sCars[i].m_nLastCheck = timer; } } void CStuckCarCheck::AddCarToCheck(int32 id, float radius, uint32 time) { CVehicle* pv = CPools::GetVehiclePool()->GetAt(id); if (!pv) return; int index = 0; while (index < MAX_STUCK_CAR_CHECKS && m_sCars[index].m_nVehicleIndex >= 0) index++; #ifdef FIX_BUGS if (index >= MAX_STUCK_CAR_CHECKS) return; #endif m_sCars[index].m_nVehicleIndex = id; m_sCars[index].m_vecPos = pv->GetPosition(); m_sCars[index].m_nLastCheck = CTimer::GetTimeInMilliseconds(); m_sCars[index].m_fRadius = radius; m_sCars[index].m_nStuckTime = time; m_sCars[index].m_bStuck = false; } void CStuckCarCheck::RemoveCarFromCheck(int32 id) { for (int i = 0; i < MAX_STUCK_CAR_CHECKS; i++){ if (m_sCars[i].m_nVehicleIndex == id){ m_sCars[i].Reset(); } } } bool CStuckCarCheck::HasCarBeenStuckForAWhile(int32 id) { for (int i = 0; i < MAX_STUCK_CAR_CHECKS; i++){ if (m_sCars[i].m_nVehicleIndex == id) return m_sCars[i].m_bStuck; } return false; } void CRunningScript::CollectParameters(uint32* pIp, int16 total) { for (int16 i = 0; i < total; i++){ uint16 varIndex; switch (CTheScripts::Read1ByteFromScript(pIp)) { case ARGUMENT_INT32: case ARGUMENT_FLOAT: ScriptParams[i] = CTheScripts::Read4BytesFromScript(pIp); break; case ARGUMENT_GLOBALVAR: varIndex = CTheScripts::Read2BytesFromScript(pIp); script_assert(varIndex >= 8 && varIndex < CTheScripts::GetSizeOfVariableSpace()); ScriptParams[i] = *((int32*)&CTheScripts::ScriptSpace[varIndex]); break; case ARGUMENT_LOCALVAR: varIndex = CTheScripts::Read2BytesFromScript(pIp); script_assert(varIndex >= 0 && varIndex < ARRAY_SIZE(m_anLocalVariables)); ScriptParams[i] = m_anLocalVariables[varIndex]; break; case ARGUMENT_INT8: ScriptParams[i] = CTheScripts::Read1ByteFromScript(pIp); break; case ARGUMENT_INT16: ScriptParams[i] = CTheScripts::Read2BytesFromScript(pIp); break; default: script_assert(0); break; } } } #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT int CRunningScript::CollectParameterForDebug(char* buf, bool& var) { uint16 varIndex; char tmpstr[24]; var = false; switch (CTheScripts::Read1ByteFromScript(&m_nIp)) { case ARGUMENT_INT32: case ARGUMENT_FLOAT: return CTheScripts::Read4BytesFromScript(&m_nIp); case ARGUMENT_GLOBALVAR: varIndex = CTheScripts::Read2BytesFromScript(&m_nIp); script_assert(varIndex >= 8 && varIndex < CTheScripts::GetSizeOfVariableSpace()); var = true; sprintf(tmpstr, " $%d", varIndex / 4); strcat(buf, tmpstr); return *((int32*)&CTheScripts::ScriptSpace[varIndex]); case ARGUMENT_LOCALVAR: varIndex = CTheScripts::Read2BytesFromScript(&m_nIp); script_assert(varIndex >= 0 && varIndex < ARRAY_SIZE(m_anLocalVariables)); var = true; sprintf(tmpstr, " %d@", varIndex); strcat(buf, tmpstr); return m_anLocalVariables[varIndex]; case ARGUMENT_INT8: return CTheScripts::Read1ByteFromScript(&m_nIp); case ARGUMENT_INT16: return CTheScripts::Read2BytesFromScript(&m_nIp); default: PrintToLog("%s - script assertion failed in CollectParameterForDebug", buf); script_assert(0); break; } return 0; } void CRunningScript::GetStoredParameterForDebug(char* buf) { uint16 varIndex; char tmpstr[24]; switch (CTheScripts::Read1ByteFromScript(&m_nIp)) { case ARGUMENT_GLOBALVAR: varIndex = CTheScripts::Read2BytesFromScript(&m_nIp); sprintf(tmpstr, " $%d", varIndex / 4); strcat(buf, tmpstr); break; case ARGUMENT_LOCALVAR: varIndex = CTheScripts::Read2BytesFromScript(&m_nIp); sprintf(tmpstr, " %d@", varIndex); strcat(buf, tmpstr); break; default: PrintToLog("%s - script_assertion failed in GetStoredParameterForDebug", buf); script_assert(0); } } #endif int32 CRunningScript::CollectNextParameterWithoutIncreasingPC(uint32 ip) { uint32* pIp = &ip; switch (CTheScripts::Read1ByteFromScript(pIp)) { case ARGUMENT_INT32: return CTheScripts::Read4BytesFromScript(pIp); case ARGUMENT_GLOBALVAR: return *((int32*)&CTheScripts::ScriptSpace[(uint16)CTheScripts::Read2BytesFromScript(pIp)]); case ARGUMENT_LOCALVAR: return m_anLocalVariables[CTheScripts::Read2BytesFromScript(pIp)]; case ARGUMENT_INT8: return CTheScripts::Read1ByteFromScript(pIp); case ARGUMENT_INT16: return CTheScripts::Read2BytesFromScript(pIp); case ARGUMENT_FLOAT: return CTheScripts::Read4BytesFromScript(pIp); default: script_assert(0); } return -1; } void CRunningScript::StoreParameters(uint32* pIp, int16 number) { for (int16 i = 0; i < number; i++){ switch (CTheScripts::Read1ByteFromScript(pIp)) { case ARGUMENT_GLOBALVAR: *(int32*)&CTheScripts::ScriptSpace[(uint16)CTheScripts::Read2BytesFromScript(pIp)] = ScriptParams[i]; break; case ARGUMENT_LOCALVAR: m_anLocalVariables[CTheScripts::Read2BytesFromScript(pIp)] = ScriptParams[i]; break; default: script_assert(0); } } } int32 *CRunningScript::GetPointerToScriptVariable(uint32* pIp, int16 type) { switch (CTheScripts::Read1ByteFromScript(pIp)) { case ARGUMENT_GLOBALVAR: script_assert(type == VAR_GLOBAL); return (int32*)&CTheScripts::ScriptSpace[(uint16)CTheScripts::Read2BytesFromScript(pIp)]; case ARGUMENT_LOCALVAR: script_assert(type == VAR_LOCAL); return &m_anLocalVariables[CTheScripts::Read2BytesFromScript(pIp)]; default: script_assert(0); } return nil; } void CRunningScript::Init() { strcpy(m_abScriptName, "noname"); next = prev = nil; SetIP(0); for (int i = 0; i < MAX_STACK_DEPTH; i++) m_anStack[i] = 0; m_nStackPointer = 0; m_nWakeTime = 0; m_bIsActive = false; m_bCondResult = false; m_bIsMissionScript = false; m_bSkipWakeTime = false; for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++) m_anLocalVariables[i] = 0; m_nAndOrState = 0; m_bNotFlag = false; m_bDeatharrestEnabled = true; m_bDeatharrestExecuted = false; m_bMissionFlag = false; } #ifdef USE_DEBUG_SCRIPT_LOADER int scriptToLoad = 0; const char *scriptfile = "main.scm"; #ifdef _WIN32 #include #endif int open_script() { // glfwGetKey doesn't work because of CGame::Initialise is blocking #ifdef _WIN32 if (GetAsyncKeyState('G') & 0x8000) scriptToLoad = 0; if (GetAsyncKeyState('R') & 0x8000) scriptToLoad = 1; if (GetAsyncKeyState('D') & 0x8000) scriptToLoad = 2; #endif switch (scriptToLoad) { case 0: scriptfile = "main.scm"; break; case 1: scriptfile = "freeroam_miami.scm"; break; case 2: scriptfile = "main_d.scm"; break; } return CFileMgr::OpenFile(scriptfile, "rb"); } #endif void CTheScripts::Init() { for (int i = 0; i < SIZE_SCRIPT_SPACE; i++) ScriptSpace[i] = 0; pActiveScripts = pIdleScripts = nil; for (int i = 0; i < MAX_NUM_SCRIPTS; i++){ ScriptsArray[i].Init(); ScriptsArray[i].AddScriptToList(&pIdleScripts); } MissionCleanUp.Init(); UpsideDownCars.Init(); StuckCars.Init(); CFileMgr::SetDir("data"); #ifdef USE_DEBUG_SCRIPT_LOADER int mainf = open_script(); #else int mainf = CFileMgr::OpenFile("main.scm", "rb"); #endif CFileMgr::Read(mainf, (char*)ScriptSpace, SIZE_MAIN_SCRIPT); CFileMgr::CloseFile(mainf); CFileMgr::SetDir(""); StoreVehicleIndex = -1; StoreVehicleWasRandom = true; OnAMissionFlag = 0; LastMissionPassedTime = (uint32)-1; LastRandomPedId = -1; for (int i = 0; i < MAX_NUM_USED_OBJECTS; i++){ memset(&UsedObjectArray[i].name, 0, sizeof(UsedObjectArray[i].name)); UsedObjectArray[i].index = 0; } NumberOfUsedObjects = 0; ReadObjectNamesFromScript(); UpdateObjectIndices(); bAlreadyRunningAMissionScript = false; bUsingAMultiScriptFile = true; for (int i = 0; i < MAX_NUM_MISSION_SCRIPTS; i++) MultiScriptArray[i] = 0; NumberOfExclusiveMissionScripts = 0; NumberOfMissionScripts = 0; LargestMissionScriptSize = 0; MainScriptSize = 0; ReadMultiScriptFileOffsetsFromScript(); FailCurrentMission = 0; DbgFlag = false; NumScriptDebugLines = 0; RiotIntensity = 0; bPlayerHasMetDebbieHarry = false; bPlayerIsInTheStatium = false; for (int i = 0; i < MAX_NUM_SCRIPT_SPHERES; i++){ ScriptSphereArray[i].m_bInUse = false; ScriptSphereArray[i].m_Index = 1; ScriptSphereArray[i].m_Id = 0; ScriptSphereArray[i].m_vecCenter = CVector(0.0f, 0.0f, 0.0f); ScriptSphereArray[i].m_fRadius = 0.0f; } for (int i = 0; i < MAX_NUM_INTRO_TEXT_LINES; i++){ IntroTextLines[i].Reset(); } NumberOfIntroTextLinesThisFrame = 0; UseTextCommands = 0; for (int i = 0; i < MAX_NUM_INTRO_RECTANGLES; i++){ IntroRectangles[i].m_bIsUsed = false; IntroRectangles[i].m_bBeforeFade = false; IntroRectangles[i].m_nTextureId = -1; IntroRectangles[i].m_sRect = CRect(0.0f, 0.0f, 0.0f, 0.0f); IntroRectangles[i].m_sColor = CRGBA(255, 255, 255, 255); } NumberOfIntroRectanglesThisFrame = 0; RemoveScriptTextureDictionary(); for (int i = 0; i < MAX_NUM_BUILDING_SWAPS; i++){ BuildingSwapArray[i].m_pBuilding = nil; BuildingSwapArray[i].m_nNewModel = -1; BuildingSwapArray[i].m_nOldModel = -1; } for (int i = 0; i < MAX_NUM_INVISIBILITY_SETTINGS; i++) InvisibilitySettingArray[i] = nil; #if defined USE_ADVANCED_SCRIPT_DEBUG_OUTPUT && SCRIPT_LOG_FILE_LEVEL == 2 CFileMgr::SetDirMyDocuments(); if (dbg_log) fclose(dbg_log); dbg_log = fopen("SCRDBG.LOG", "w"); static const char* init_msg = "Starting debug script log\n\n"; PrintToLog(init_msg); CFileMgr::SetDir(""); #endif } void CTheScripts::RemoveScriptTextureDictionary() { for (int i = 0; i < ARRAY_SIZE(CTheScripts::ScriptSprites); i++) CTheScripts::ScriptSprites[i].Delete(); int slot = CTxdStore::FindTxdSlot("script"); if (slot != -1) CTxdStore::RemoveTxd(slot); } void CRunningScript::RemoveScriptFromList(CRunningScript** ppScript) { if (prev) prev->next = next; else *ppScript = next; if (next) next->prev = prev; } void CRunningScript::AddScriptToList(CRunningScript** ppScript) { next = *ppScript; prev = nil; if (*ppScript) (*ppScript)->prev = this; *ppScript = this; } CRunningScript* CTheScripts::StartNewScript(uint32 ip) { CRunningScript* pNew = pIdleScripts; script_assert(pNew); pNew->RemoveScriptFromList(&pIdleScripts); pNew->Init(); pNew->SetIP(ip); pNew->AddScriptToList(&pActiveScripts); pNew->m_bIsActive = true; return pNew; } void CTheScripts::Process() { if (CReplay::IsPlayingBack()) return; CommandsExecuted = 0; ScriptsUpdated = 0; float timeStep = CTimer::GetTimeStepInMilliseconds(); UpsideDownCars.UpdateTimers(); StuckCars.Process(); MissionCleanUp.CheckIfCollisionHasLoadedForMissionObjects(); DrawScriptSpheres(); if (FailCurrentMission) --FailCurrentMission; if (UseTextCommands){ for (int i = 0; i < MAX_NUM_INTRO_TEXT_LINES; i++) IntroTextLines[i].Reset(); NumberOfIntroTextLinesThisFrame = 0; for (int i = 0; i < MAX_NUM_INTRO_RECTANGLES; i++){ IntroRectangles[i].m_bIsUsed = false; IntroRectangles[i].m_bBeforeFade = false; } NumberOfIntroRectanglesThisFrame = 0; if (UseTextCommands == 1) UseTextCommands = 0; } #ifdef MISSION_REPLAY static uint32 TimeToWaitTill; switch (AllowMissionReplay) { case 2: AllowMissionReplay = 3; TimeToWaitTill = CTimer::GetTimeInMilliseconds() + (AddExtraDeathDelay() > 1000 ? 4000 : 2500); break; case 3: if (TimeToWaitTill < CTimer::GetTimeInMilliseconds()) AllowMissionReplay = 4; break; case 4: AllowMissionReplay = 5; RetryMission(0, 0); break; case 6: AllowMissionReplay = 7; TimeToWaitTill = CTimer::GetTimeInMilliseconds() + 500; break; case 7: if (TimeToWaitTill < CTimer::GetTimeInMilliseconds()) { AllowMissionReplay = 0; return; } break; } if (WaitForMissionActivate) { if (WaitForMissionActivate > CTimer::GetTimeInMilliseconds()) return; WaitForMissionActivate = 0; WaitForSave = CTimer::GetTimeInMilliseconds() + 3000; } if (WaitForSave && WaitForSave > CTimer::GetTimeInMilliseconds()) WaitForSave = 0; #endif #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #if SCRIPT_LOG_FILE_LEVEL == 1 CFileMgr::SetDirMyDocuments(); dbg_log = fopen("SCRDBG.LOG", "w"); static const char* init_msg = "Starting debug script log\n\n"; PrintToLog(init_msg); CFileMgr::SetDir(""); #endif PrintToLog("------------------------\n"); PrintToLog("CTheScripts::Process started, CTimer::GetTimeInMilliseconds == %u\n", CTimer::GetTimeInMilliseconds()); #endif CRunningScript* script = pActiveScripts; while (script != nil){ CRunningScript* next = script->GetNext(); ++ScriptsUpdated; script->UpdateTimers(timeStep); script->Process(); script = next; if (script && !script->m_bIsActive) script = nil; } DbgFlag = false; #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT PrintToLog("Script processing done, ScriptsUpdated: %d, CommandsExecuted: %d\n", ScriptsUpdated, CommandsExecuted); #if SCRIPT_LOG_FILE_LEVEL == 1 fclose(dbg_log); dbg_log = nil; #endif #endif } CRunningScript* CTheScripts::StartTestScript() { return StartNewScript(0); } bool CTheScripts::IsPlayerOnAMission() { return OnAMissionFlag && *(int32*)&ScriptSpace[OnAMissionFlag] == 1; } void CRunningScript::Process() { #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT PrintToLog("\n\nProcessing script %s (id %d)\n\n", m_abScriptName, this - CTheScripts::ScriptsArray); #endif if (m_bIsMissionScript) DoDeatharrestCheck(); if (m_bMissionFlag && CTheScripts::FailCurrentMission == 1 && m_nStackPointer == 1) SetIP(m_anStack[--m_nStackPointer]); if (CTimer::GetTimeInMilliseconds() >= m_nWakeTime){ while (!ProcessOneCommand()) ; return; } if (!m_bSkipWakeTime) return; if (!CPad::GetPad(0)->GetCrossJustDown()) return; m_nWakeTime = 0; for (int i = 0; i < NUMBIGMESSAGES; i++){ if (CMessages::BIGMessages[i].m_Stack[0].m_pText != nil) CMessages::BIGMessages[i].m_Stack[0].m_nStartTime = 0; } if (CMessages::BriefMessages[0].m_pText != nil) CMessages::BriefMessages[0].m_nStartTime = 0; } int8 CRunningScript::ProcessOneCommand() { int8 retval = -1; ++CTheScripts::CommandsExecuted; int32 command = (uint16)CTheScripts::Read2BytesFromScript(&m_nIp); m_bNotFlag = (command & 0x8000); command &= 0x7FFF; #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT char commandInfo[1024]; uint32 ip = m_nIp; if (command < ARRAY_SIZE(commands)) { script_assert(commands[command].id == command); m_nIp -= 2; sprintf(commandInfo, m_nIp >= SIZE_MAIN_SCRIPT ? "M<%5d> " : "<%6d> ", m_nIp >= SIZE_MAIN_SCRIPT ? m_nIp - SIZE_MAIN_SCRIPT : m_nIp); m_nIp += 2; if (m_bNotFlag) strcat(commandInfo, "NOT "); if (commands[command].position == -1) strcat(commandInfo, commands[command].name + sizeof("COMMAND_") - 1); for (int i = 0; commands[command].input[i] != ARGTYPE_NONE; i++) { char tmp[32]; bool var = false; int value; switch (commands[command].input[i]) { case ARGTYPE_INT: case ARGTYPE_PED_HANDLE: case ARGTYPE_VEHICLE_HANDLE: case ARGTYPE_OBJECT_HANDLE: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, var ? " (%d)" : " %d", value); break; case ARGTYPE_FLOAT: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, var ? " (%.3f)" : " %.3f", *(float*)&value); break; case ARGTYPE_STRING: sprintf(tmp, " '%s'", (const char*)&CTheScripts::ScriptSpace[m_nIp]); m_nIp += KEY_LENGTH_IN_SCRIPT; break; case ARGTYPE_LABEL: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, var ? " (%s(%d))" : " %s(%d)", value >= 0 ? "G" : "L", abs(value)); break; case ARGTYPE_BOOL: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, var ? " (%s)" : " %s", value ? "TRUE" : "FALSE"); break; case ARGTYPE_ANDOR: value = CollectParameterForDebug(commandInfo, var); sprintf(tmp, " %d %ss", (value + 1) % 10, value / 10 == 0 ? "AND" : "OR"); break; default: script_assert(0); } strcat(commandInfo, tmp); if (commands[command].position == i) strcat(commandInfo, commands[command].name_override); } uint32 t = m_nIp; m_nIp = ip; ip = t; } #endif if (command < 100) retval = ProcessCommands0To99(command); else if (command < 200) retval = ProcessCommands100To199(command); else if (command < 300) retval = ProcessCommands200To299(command); else if (command < 400) retval = ProcessCommands300To399(command); else if (command < 500) retval = ProcessCommands400To499(command); else if (command < 600) retval = ProcessCommands500To599(command); else if (command < 700) retval = ProcessCommands600To699(command); else if (command < 800) retval = ProcessCommands700To799(command); else if (command < 900) retval = ProcessCommands800To899(command); else if (command < 1000) retval = ProcessCommands900To999(command); else if (command < 1100) retval = ProcessCommands1000To1099(command); else if (command < 1200) retval = ProcessCommands1100To1199(command); else if (command < 1300) retval = ProcessCommands1200To1299(command); else if (command < 1400) retval = ProcessCommands1300To1399(command); else if (command < 1500) retval = ProcessCommands1400To1499(command); #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT if (command < ARRAY_SIZE(commands)) { if (commands[command].cond || commands[command].output[0] != ARGTYPE_NONE) { strcat(commandInfo, " ->"); if (commands[command].cond) strcat(commandInfo, m_bCondResult ? " TRUE" : " FALSE"); uint32 t = m_nIp; m_nIp = ip; ip = t; for (int i = 0; commands[command].output[i] != ARGTYPE_NONE; i++) { char tmp[32]; switch (commands[command].output[i]) { case ARGTYPE_INT: case ARGTYPE_PED_HANDLE: case ARGTYPE_VEHICLE_HANDLE: case ARGTYPE_OBJECT_HANDLE: GetStoredParameterForDebug(commandInfo); sprintf(tmp, " (%d)", ScriptParams[i]); strcat(commandInfo, tmp); break; case ARGTYPE_FLOAT: GetStoredParameterForDebug(commandInfo); sprintf(tmp, " (%8.3f)", *(float*)&ScriptParams[i]); strcat(commandInfo, tmp); break; default: script_assert(0 && "Script only returns INTs and FLOATs"); } } m_nIp = ip; } PrintToLog("%s\n", commandInfo); if (m_bMissionFlag) { for (int i = 0; commandInfo[i]; i++) { if (commandInfo[i] == '_') commandInfo[i] = ' '; } CDebug::DebugAddText(commandInfo); } } #elif defined USE_BASIC_SCRIPT_DEBUG_OUTPUT if (m_bMissionFlag) { char tmp[128]; sprintf(tmp, "Comm %d Cmp %d", command, m_bCondResult); CDebug::DebugAddText(tmp); } #endif return retval; } int8 CRunningScript::ProcessCommands0To99(int32 command) { float *fScriptVar1; int *nScriptVar1; switch (command) { case COMMAND_NOP: return 0; case COMMAND_WAIT: CollectParameters(&m_nIp, 1); m_nWakeTime = CTimer::GetTimeInMilliseconds() + ScriptParams[0]; m_bSkipWakeTime = false; return 1; case COMMAND_GOTO: CollectParameters(&m_nIp, 1); SetIP(ScriptParams[0] >= 0 ? ScriptParams[0] : SIZE_MAIN_SCRIPT - ScriptParams[0]); /* Known issue: GOTO to 0. It might have been "better" to use > instead of >= */ /* simply because it never makes sense to jump to start of the script */ /* but jumping to start of a custom mission is an issue for simple mission-like scripts */ /* However, it's not an issue for actual mission scripts, because they follow a structure */ /* and never start with a loop. */ return 0; case COMMAND_SHAKE_CAM: CollectParameters(&m_nIp, 1); CamShakeNoPos(&TheCamera, ScriptParams[0] / 1000.0f); return 0; case COMMAND_SET_VAR_INT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *ptr = ScriptParams[0]; return 0; } case COMMAND_SET_VAR_FLOAT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *(float*)ptr = *(float*)&ScriptParams[0]; return 0; } case COMMAND_SET_LVAR_INT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *ptr = ScriptParams[0]; return 0; } case COMMAND_SET_LVAR_FLOAT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *(float*)ptr = *(float*)&ScriptParams[0]; return 0; } case COMMAND_ADD_VAL_TO_INT_VAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *ptr += ScriptParams[0]; return 0; } case COMMAND_ADD_VAL_TO_FLOAT_VAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *(float*)ptr += *(float*)&ScriptParams[0]; return 0; } case COMMAND_ADD_VAL_TO_INT_LVAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *ptr += ScriptParams[0]; return 0; } case COMMAND_ADD_VAL_TO_FLOAT_LVAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *(float*)ptr += *(float*)&ScriptParams[0]; return 0; } case COMMAND_SUB_VAL_FROM_INT_VAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *ptr -= ScriptParams[0]; return 0; } case COMMAND_SUB_VAL_FROM_FLOAT_VAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *(float*)ptr -= *(float*)&ScriptParams[0]; return 0; } case COMMAND_SUB_VAL_FROM_INT_LVAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *ptr -= ScriptParams[0]; return 0; } case COMMAND_SUB_VAL_FROM_FLOAT_LVAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *(float*)ptr -= *(float*)&ScriptParams[0]; return 0; } case COMMAND_MULT_INT_VAR_BY_VAL: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *ptr *= ScriptParams[0]; return 0; } case COMMAND_MULT_FLOAT_VAR_BY_VAL: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *(float*)ptr *= *(float*)&ScriptParams[0]; return 0; } case COMMAND_MULT_INT_LVAR_BY_VAL: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *ptr *= ScriptParams[0]; return 0; } case COMMAND_MULT_FLOAT_LVAR_BY_VAL: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *(float*)ptr *= *(float*)&ScriptParams[0]; return 0; } case COMMAND_DIV_INT_VAR_BY_VAL: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *ptr /= ScriptParams[0]; return 0; } case COMMAND_DIV_FLOAT_VAR_BY_VAL: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *(float*)ptr /= *(float*)&ScriptParams[0]; return 0; } case COMMAND_DIV_INT_LVAR_BY_VAL: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *ptr /= ScriptParams[0]; return 0; } case COMMAND_DIV_FLOAT_LVAR_BY_VAL: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *(float*)ptr /= *(float*)&ScriptParams[0]; return 0; } case COMMAND_IS_INT_VAR_GREATER_THAN_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr > ScriptParams[0]); return 0; } case COMMAND_IS_INT_LVAR_GREATER_THAN_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr > ScriptParams[0]); return 0; } case COMMAND_IS_NUMBER_GREATER_THAN_INT_VAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(ScriptParams[0] > *ptr); return 0; } case COMMAND_IS_NUMBER_GREATER_THAN_INT_LVAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(ScriptParams[0] > *ptr); return 0; } case COMMAND_IS_INT_VAR_GREATER_THAN_INT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*ptr1 > *ptr2); return 0; } case COMMAND_IS_INT_LVAR_GREATER_THAN_INT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*ptr1 > *ptr2); return 0; } case COMMAND_IS_INT_VAR_GREATER_THAN_INT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*ptr1 > *ptr2); return 0; } case COMMAND_IS_INT_LVAR_GREATER_THAN_INT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*ptr1 > *ptr2); return 0; } case COMMAND_IS_FLOAT_VAR_GREATER_THAN_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*(float*)ptr > *(float*)&ScriptParams[0]); return 0; } case COMMAND_IS_FLOAT_LVAR_GREATER_THAN_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*(float*)ptr > *(float*)&ScriptParams[0]); return 0; } case COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_VAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*(float*)&ScriptParams[0] > *(float*)ptr); return 0; } case COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_LVAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*(float*)&ScriptParams[0] > *(float*)ptr); return 0; } case COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*(float*)ptr1 > *(float*)ptr2); return 0; } case COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*(float*)ptr1 > *(float*)ptr2); return 0; } case COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*(float*)ptr1 > *(float*)ptr2); return 0; } case COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*(float*)ptr1 > *(float*)ptr2); return 0; } case COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr >= ScriptParams[0]); return 0; } case COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr >= ScriptParams[0]); return 0; } case COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_VAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(ScriptParams[0] >= *ptr); return 0; } case COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_LVAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(ScriptParams[0] >= *ptr); return 0; } case COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*ptr1 >= *ptr2); return 0; } case COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*ptr1 >= *ptr2); return 0; } case COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*ptr1 >= *ptr2); return 0; } case COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*ptr1 >= *ptr2); return 0; } case COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*(float*)ptr >= *(float*)&ScriptParams[0]); return 0; } case COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*(float*)ptr >= *(float*)&ScriptParams[0]); return 0; } case COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_VAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*(float*)&ScriptParams[0] >= *(float*)ptr); return 0; } case COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_LVAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*(float*)&ScriptParams[0] >= *(float*)ptr); return 0; } case COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*(float*)ptr1 >= *(float*)ptr2); return 0; } case COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*(float*)ptr1 >= *(float*)ptr2); return 0; } case COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*(float*)ptr1 >= *(float*)ptr2); return 0; } case COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*(float*)ptr1 >= *(float*)ptr2); return 0; } case COMMAND_IS_INT_VAR_EQUAL_TO_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr == ScriptParams[0]); return 0; } case COMMAND_IS_INT_LVAR_EQUAL_TO_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr == ScriptParams[0]); return 0; } case COMMAND_IS_INT_VAR_EQUAL_TO_INT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*ptr1 == *ptr2); return 0; } case COMMAND_IS_INT_VAR_EQUAL_TO_INT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*ptr1 == *ptr2); return 0; } case COMMAND_IS_INT_LVAR_EQUAL_TO_INT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*ptr1 == *ptr2); return 0; } //case COMMAND_IS_INT_VAR_NOT_EQUAL_TO_NUMBER: //case COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_NUMBER: //case COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_VAR: //case COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_INT_LVAR: //case COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_LVAR: case COMMAND_IS_FLOAT_VAR_EQUAL_TO_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*(float*)ptr == *(float*)&ScriptParams[0]); return 0; } case COMMAND_IS_FLOAT_LVAR_EQUAL_TO_NUMBER: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*(float*)ptr == *(float*)&ScriptParams[0]); return 0; } case COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_VAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(*(float*)ptr1 == *(float*)ptr2); return 0; } case COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*(float*)ptr1 == *(float*)ptr2); return 0; } case COMMAND_IS_FLOAT_LVAR_EQUAL_TO_FLOAT_LVAR: { int32* ptr1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); int32* ptr2 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(*(float*)ptr1 == *(float*)ptr2); return 0; } //case COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_NUMBER: //case COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_NUMBER: //case COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_VAR: //case COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_FLOAT_LVAR: //case COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_LVAR: /* case COMMAND_GOTO_IF_TRUE: CollectParameters(&m_nIp, 1); if (m_bCondResult) SetIP(ScriptParams[0] >= 0 ? ScriptParams[0] : SIZE_MAIN_SCRIPT - ScriptParams[0]); return 0; */ case COMMAND_GOTO_IF_FALSE: CollectParameters(&m_nIp, 1); if (!m_bCondResult) SetIP(ScriptParams[0] >= 0 ? ScriptParams[0] : SIZE_MAIN_SCRIPT - ScriptParams[0]); /* Check COMMAND_GOTO note. */ return 0; case COMMAND_TERMINATE_THIS_SCRIPT: if (m_bMissionFlag) CTheScripts::bAlreadyRunningAMissionScript = false; RemoveScriptFromList(&CTheScripts::pActiveScripts); AddScriptToList(&CTheScripts::pIdleScripts); m_bIsActive = false; #ifdef MISSION_REPLAY if (m_bMissionFlag) { CPlayerInfo* pPlayerInfo = &CWorld::Players[CWorld::PlayerInFocus]; #if 0 // makeing autosave is pointless and is a bit buggy if (pPlayerInfo->m_pPed->GetPedState() != PED_DEAD && pPlayerInfo->m_WBState == WBSTATE_PLAYING && !m_bDeatharrestExecuted) SaveGameForPause(1); #endif oldTargetX = oldTargetY = 0.0f; if (AllowMissionReplay == 1) AllowMissionReplay = 2; // I am fairly sure they forgot to set return value here } #endif return 1; case COMMAND_START_NEW_SCRIPT: { CollectParameters(&m_nIp, 1); script_assert(ScriptParams[0] >= 0); CRunningScript* pNew = CTheScripts::StartNewScript(ScriptParams[0]); pNew->m_bIsActive = true; int8 type = CTheScripts::Read1ByteFromScript(&m_nIp); float tmp; for (int i = 0; type != ARGUMENT_END; type = CTheScripts::Read1ByteFromScript(&m_nIp), i++) { switch (type) { case ARGUMENT_INT32: pNew->m_anLocalVariables[i] = CTheScripts::Read4BytesFromScript(&m_nIp); break; case ARGUMENT_GLOBALVAR: pNew->m_anLocalVariables[i] = *(int32*)&CTheScripts::ScriptSpace[(uint16)CTheScripts::Read2BytesFromScript(&m_nIp)]; break; case ARGUMENT_LOCALVAR: pNew->m_anLocalVariables[i] = m_anLocalVariables[CTheScripts::Read2BytesFromScript(&m_nIp)]; break; case ARGUMENT_INT8: pNew->m_anLocalVariables[i] = CTheScripts::Read1ByteFromScript(&m_nIp); break; case ARGUMENT_INT16: pNew->m_anLocalVariables[i] = CTheScripts::Read2BytesFromScript(&m_nIp); break; case ARGUMENT_FLOAT: tmp = CTheScripts::ReadFloatFromScript(&m_nIp); pNew->m_anLocalVariables[i] = *(int32*)&tmp; break; default: break; } } return 0; } case COMMAND_GOSUB: CollectParameters(&m_nIp, 1); script_assert(m_nStackPointer < MAX_STACK_DEPTH); m_anStack[m_nStackPointer++] = m_nIp; SetIP(ScriptParams[0] >= 0 ? ScriptParams[0] : SIZE_MAIN_SCRIPT - ScriptParams[0]); return 0; case COMMAND_RETURN: script_assert(m_nStackPointer > 0); /* No more SSU */ SetIP(m_anStack[--m_nStackPointer]); return 0; case COMMAND_LINE: CollectParameters(&m_nIp, 6); /* Something must have been here */ return 0; case COMMAND_CREATE_PLAYER: { CollectParameters(&m_nIp, 4); int32 index = ScriptParams[0]; script_assert(index < NUMPLAYERS); printf("&&&&&&&&&&&&&Creating player: %d\n", index); if (!CStreaming::HasModelLoaded(MI_PLAYER)) { CStreaming::RequestSpecialModel(MI_PLAYER, "player", STREAMFLAGS_DONT_REMOVE | STREAMFLAGS_DEPENDENCY); CStreaming::LoadAllRequestedModels(false); } CPlayerPed::SetupPlayerPed(index); CWorld::Players[index].m_pPed->CharCreatedBy = MISSION_CHAR; CPlayerPed::DeactivatePlayerPed(index); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pos.z += CWorld::Players[index].m_pPed->GetDistanceFromCentreOfMassToBaseOfModel(); CWorld::Players[index].m_pPed->SetPosition(pos); CTheScripts::ClearSpaceForMissionEntity(pos, CWorld::Players[index].m_pPed); CPlayerPed::ReactivatePlayerPed(index); ScriptParams[0] = index; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_PLAYER_COORDINATES: { CVector pos; CollectParameters(&m_nIp, 1); if (CWorld::Players[ScriptParams[0]].m_pPed->bInVehicle) pos = CWorld::Players[ScriptParams[0]].m_pPed->m_pMyVehicle->GetPosition(); else pos = CWorld::Players[ScriptParams[0]].m_pPed->GetPosition(); *(CVector*)&ScriptParams[0] = pos; StoreParameters(&m_nIp, 3); return 0; } case COMMAND_SET_PLAYER_COORDINATES: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[1]; int index = ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CPlayerPed* ped = CWorld::Players[index].m_pPed; if (ped->bInVehicle) { pos.z += ped->m_pMyVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); ped->m_pMyVehicle->Teleport(pos); // removed dumb stuff that was present here CTheScripts::ClearSpaceForMissionEntity(pos, ped->m_pMyVehicle); return 0; } pos.z += ped->GetDistanceFromCentreOfMassToBaseOfModel(); CVector vOldPos = ped->GetPosition(); ped->Teleport(pos); CTheScripts::ClearSpaceForMissionEntity(pos, ped); if (ped) { // great time to check for (int i = 0; i < ped->m_numNearPeds; i++) { CPed* pTestedPed = ped->m_nearPeds[i]; if (!pTestedPed || !IsPedPointerValid(pTestedPed)) continue; if (pTestedPed->m_pedInObjective == ped && pTestedPed->m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) { CVector vFollowerPos = pTestedPed->GetFormationPosition(); CTheScripts::ClearSpaceForMissionEntity(vFollowerPos, ped); bool bFound = false; vFollowerPos.z = CWorld::FindGroundZFor3DCoord(vFollowerPos.x, vFollowerPos.y, vFollowerPos.z + 1.0f, &bFound) + 1.0f; if (bFound) { if (CWorld::GetIsLineOfSightClear(vFollowerPos, ped->GetPosition(), true, false, false, true, false, false)) { pTestedPed->Teleport(vFollowerPos); } } } else if (pTestedPed->m_leader == ped) { CVector vFollowerPos; if (pTestedPed->m_pedFormation) vFollowerPos = pTestedPed->GetFormationPosition(); else vFollowerPos = ped->GetPosition() + pTestedPed->GetPosition() - vOldPos; CTheScripts::ClearSpaceForMissionEntity(vFollowerPos, ped); bool bFound = false; vFollowerPos.z = CWorld::FindGroundZFor3DCoord(vFollowerPos.x, vFollowerPos.y, vFollowerPos.z + 1.0f, &bFound) + 1.0f; if (bFound) { if (CWorld::GetIsLineOfSightClear(vFollowerPos, ped->GetPosition(), true, false, false, true, false, false)) { pTestedPed->Teleport(vFollowerPos); } } } } } return 0; } case COMMAND_IS_PLAYER_IN_AREA_2D: { CollectParameters(&m_nIp, 6); CPlayerPed* ped = CWorld::Players[ScriptParams[0]].m_pPed; float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float x2 = *(float*)&ScriptParams[3]; float y2 = *(float*)&ScriptParams[4]; if (!ped->bInVehicle) UpdateCompareFlag(ped->IsWithinArea(x1, y1, x2, y2)); else UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, x2, y2)); if (ScriptParams[5]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugSquare(x1, y1, x2, y2); return 0; } case COMMAND_IS_PLAYER_IN_AREA_3D: { CollectParameters(&m_nIp, 8); CPlayerPed* ped = CWorld::Players[ScriptParams[0]].m_pPed; float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float z1 = *(float*)&ScriptParams[3]; float x2 = *(float*)&ScriptParams[4]; float y2 = *(float*)&ScriptParams[5]; float z2 = *(float*)&ScriptParams[6]; if (ped->bInVehicle) UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); else UpdateCompareFlag(ped->IsWithinArea(x1, y1, z1, x2, y2, z2)); if (ScriptParams[7]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); return 0; } case COMMAND_ADD_INT_VAR_TO_INT_VAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *nScriptVar1 += *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_ADD_INT_LVAR_TO_INT_VAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *nScriptVar1 += *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_ADD_INT_VAR_TO_INT_LVAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *nScriptVar1 += *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_ADD_INT_LVAR_TO_INT_LVAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *nScriptVar1 += *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_ADD_FLOAT_VAR_TO_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 += *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 += *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_ADD_FLOAT_VAR_TO_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 += *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 += *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_SUB_INT_VAR_FROM_INT_VAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *nScriptVar1 -= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_SUB_INT_LVAR_FROM_INT_LVAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *nScriptVar1 -= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 -= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 -= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; default: script_assert(0); break; } return -1; } int8 CRunningScript::ProcessCommands100To199(int32 command) { float *fScriptVar1; int *nScriptVar1; switch (command) { case COMMAND_SUB_INT_LVAR_FROM_INT_VAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *nScriptVar1 -= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_SUB_INT_VAR_FROM_INT_LVAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *nScriptVar1 -= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 -= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 -= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_MULT_INT_VAR_BY_INT_VAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *nScriptVar1 *= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_MULT_INT_VAR_BY_INT_LVAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *nScriptVar1 *= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_MULT_INT_LVAR_BY_INT_VAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *nScriptVar1 *= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_MULT_INT_LVAR_BY_INT_LVAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *nScriptVar1 *= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_MULT_FLOAT_VAR_BY_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 *= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_MULT_FLOAT_VAR_BY_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 *= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 *= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 *= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_DIV_INT_VAR_BY_INT_VAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *nScriptVar1 /= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_DIV_INT_VAR_BY_INT_LVAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *nScriptVar1 /= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_DIV_INT_LVAR_BY_INT_VAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *nScriptVar1 /= *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_DIV_INT_LVAR_BY_INT_LVAR: nScriptVar1 = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *nScriptVar1 /= *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_DIV_FLOAT_VAR_BY_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 /= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_DIV_FLOAT_VAR_BY_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 /= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 /= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 /= *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_ADD_TIMED_VAL_TO_FLOAT_VAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *(float*)ptr += CTimer::GetTimeStep() * *(float*)&ScriptParams[0]; return 0; } case COMMAND_ADD_TIMED_VAL_TO_FLOAT_LVAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *(float*)ptr += CTimer::GetTimeStep() * *(float*)&ScriptParams[0]; return 0; } case COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 += CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; #ifdef FIX_BUGS case COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR: #else case COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR: #endif fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 += CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; #ifdef FIX_BUGS case COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR: #else case COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR: #endif fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 += CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 += CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_SUB_TIMED_VAL_FROM_FLOAT_VAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *(float*)ptr -= CTimer::GetTimeStep() * *(float*)&ScriptParams[0]; return 0; } case COMMAND_SUB_TIMED_VAL_FROM_FLOAT_LVAR: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *(float*)ptr -= CTimer::GetTimeStep() * *(float*)&ScriptParams[0]; return 0; } case COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_VAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 -= CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; #ifdef FIX_BUGS // in SA it was fixed by reversing their order in enum case COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR: #else case COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR: #endif fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *fScriptVar1 -= CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; #ifdef FIX_BUGS case COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR: #else case COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR: #endif fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 -= CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; case COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_LVAR: fScriptVar1 = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *fScriptVar1 -= CTimer::GetTimeStep() * *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; case COMMAND_SET_VAR_INT_TO_VAR_INT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; } case COMMAND_SET_VAR_INT_TO_LVAR_INT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; } case COMMAND_SET_LVAR_INT_TO_VAR_INT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; } case COMMAND_SET_LVAR_INT_TO_LVAR_INT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; } case COMMAND_SET_VAR_FLOAT_TO_VAR_FLOAT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; } case COMMAND_SET_VAR_FLOAT_TO_LVAR_FLOAT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; } case COMMAND_SET_LVAR_FLOAT_TO_VAR_FLOAT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; } case COMMAND_SET_LVAR_FLOAT_TO_LVAR_FLOAT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; } case COMMAND_CSET_VAR_INT_TO_VAR_FLOAT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; } case COMMAND_CSET_VAR_INT_TO_LVAR_FLOAT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; } case COMMAND_CSET_LVAR_INT_TO_VAR_FLOAT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; } case COMMAND_CSET_LVAR_INT_TO_LVAR_FLOAT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = *(float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; } case COMMAND_CSET_VAR_FLOAT_TO_VAR_INT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; } case COMMAND_CSET_VAR_FLOAT_TO_LVAR_INT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; } case COMMAND_CSET_LVAR_FLOAT_TO_VAR_INT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); return 0; } case COMMAND_CSET_LVAR_FLOAT_TO_LVAR_INT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = *GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); return 0; } case COMMAND_ABS_VAR_INT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = ABS(*ptr); return 0; } case COMMAND_ABS_LVAR_INT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = ABS(*ptr); return 0; } case COMMAND_ABS_VAR_FLOAT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); *ptr = ABS(*ptr); return 0; } case COMMAND_ABS_LVAR_FLOAT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); *ptr = ABS(*ptr); return 0; } case COMMAND_GENERATE_RANDOM_FLOAT: { float* ptr = (float*)GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CGeneral::GetRandomNumber(); CGeneral::GetRandomNumber(); CGeneral::GetRandomNumber(); /* To make it EXTRA random! */ #ifdef FIX_BUGS *ptr = CGeneral::GetRandomNumberInRange(0.0f, 1.0f); #else *ptr = CGeneral::GetRandomNumber() / 65536.0f; /* Between 0 and 0.5 on PC (oh well...), never used in original script. */ #endif return 0; } case COMMAND_GENERATE_RANDOM_INT: *GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL) = CGeneral::GetRandomNumber(); return 0; case COMMAND_CREATE_CHAR: { CollectParameters(&m_nIp, 5); switch (ScriptParams[1]) { case MI_COP: if (ScriptParams[0] == PEDTYPE_COP) ScriptParams[1] = COP_STREET; break; case MI_SWAT: if (ScriptParams[0] == PEDTYPE_COP) ScriptParams[1] = COP_SWAT; break; case MI_FBI: if (ScriptParams[0] == PEDTYPE_COP) ScriptParams[1] = COP_FBI; break; case MI_ARMY: if (ScriptParams[0] == PEDTYPE_COP) ScriptParams[1] = COP_ARMY; break; case MI_MEDIC: if (ScriptParams[0] == PEDTYPE_EMERGENCY) ScriptParams[1] = PEDTYPE_EMERGENCY; break; case MI_FIREMAN: if (ScriptParams[0] == PEDTYPE_FIREMAN) ScriptParams[1] = PEDTYPE_FIREMAN; break; default: break; } CPed* ped; if (ScriptParams[0] == PEDTYPE_COP) ped = new CCopPed((eCopType)ScriptParams[1]); else if (ScriptParams[0] == PEDTYPE_EMERGENCY || ScriptParams[0] == PEDTYPE_FIREMAN) ped = new CEmergencyPed(ScriptParams[1]); else ped = new CCivilianPed((ePedType)ScriptParams[0], ScriptParams[1]); ped->CharCreatedBy = MISSION_CHAR; ped->bRespondsToThreats = false; ped->bAllowMedicsToReviveMe = false; ped->bIsPlayerFriend = false; CVector pos = *(CVector*)&ScriptParams[2]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pos.z += 1.0f; ped->SetPosition(pos); ped->SetOrientation(0.0f, 0.0f, 0.0f); CTheScripts::ClearSpaceForMissionEntity(pos, ped); if (m_bIsMissionScript) ped->bIsStaticWaitingForCollision = true; CWorld::Add(ped); ped->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pos); CPopulation::ms_nTotalMissionPeds++; ScriptParams[0] = CPools::GetPedPool()->GetIndex(ped); StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_DELETE_CHAR: { CollectParameters(&m_nIp, 1); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); CTheScripts::RemoveThisPed(ped); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_CHAR_WANDER_DIR: { CollectParameters(&m_nIp, 2); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(ped); ped->ClearAll(); int8 path = ScriptParams[1]; if (ScriptParams[1] < 0 || ScriptParams[1] > 7) // Max number GetRandomNumberInRange returns is max-1 #ifdef FIX_BUGS path = CGeneral::GetRandomNumberInRange(0, 8); #else path = CGeneral::GetRandomNumberInRange(0, 7); #endif ped->SetWanderPath(path); return 0; } //case COMMAND_CHAR_WANDER_RANGE: case COMMAND_CHAR_FOLLOW_PATH: { CollectParameters(&m_nIp, 6); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(ped); if (ped->GetPedState() == PED_ATTACK || ped->GetPedState() == PED_FIGHT || !ped->IsPedInControl()) return 0; CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = *(float*)&ScriptParams[4]; eMoveState state; switch (ScriptParams[5]) { case 0: state = PEDMOVE_WALK; break; case 1: state = PEDMOVE_RUN; break; default: assert(0); } ped->ClearAll(); ped->m_pathNodeTimer = 0; ped->SetFollowPath(pos, radius, state, nil, nil, 999999); return 0; } case COMMAND_CHAR_SET_IDLE: { CollectParameters(&m_nIp, 1); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(ped); ped->bScriptObjectiveCompleted = false; ped->SetObjective(OBJECTIVE_WAIT_ON_FOOT); return 0; } case COMMAND_GET_CHAR_COORDINATES: { CollectParameters(&m_nIp, 1); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(ped); CVehicle* vehicle; CVector pos; /* Seems a bit clumsy but I'll leave original flow */ if (ped->bInVehicle) vehicle = ped->m_pMyVehicle; else vehicle = nil; if (vehicle) pos = vehicle->GetPosition(); else pos = ped->GetPosition(); *(CVector*)&ScriptParams[0] = pos; StoreParameters(&m_nIp, 3); return 0; } case COMMAND_SET_CHAR_COORDINATES: { CollectParameters(&m_nIp, 4); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(ped); CVehicle* vehicle; if (ped->bInVehicle) vehicle = ped->m_pMyVehicle; else vehicle = nil; CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); // removed dumb stuff again if (!vehicle) { pos.z += ped->GetDistanceFromCentreOfMassToBaseOfModel(); ped->Teleport(pos); CTheScripts::ClearSpaceForMissionEntity(pos, ped); for (int i = 0; i < ped->m_numNearPeds; i++) { CPed* pNearPed = ped->m_nearPeds[i]; if (pNearPed->m_leader == ped) { pNearPed->Teleport(pos); pNearPed->PositionAnyPedOutOfCollision(); } } } else { pos.z += vehicle->GetDistanceFromCentreOfMassToBaseOfModel(); vehicle->Teleport(pos); CTheScripts::ClearSpaceForMissionEntity(pos, vehicle); } return 0; } /* case COMMAND_IS_CHAR_STILL_ALIVE: { CollectParameters(&m_nIp, 1); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); UpdateCompareFlag(ped && ped->GetPedState() != PED_DEAD && ped->GetPedState() != PED_DIE); return 0; } */ case COMMAND_IS_CHAR_IN_AREA_2D: { CollectParameters(&m_nIp, 6); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(ped); CVehicle* vehicle; if (ped->bInVehicle) vehicle = ped->m_pMyVehicle; else vehicle = nil; float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float x2 = *(float*)&ScriptParams[3]; float y2 = *(float*)&ScriptParams[4]; if (vehicle) UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, x2, y2)); else UpdateCompareFlag(ped->IsWithinArea(x1, y1, x2, y2)); if (ScriptParams[5]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugSquare(x1, y1, x2, y2); return 0; } case COMMAND_IS_CHAR_IN_AREA_3D: { CollectParameters(&m_nIp, 8); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(ped); CVehicle* vehicle; if (ped->bInVehicle) vehicle = ped->m_pMyVehicle; else vehicle = nil; float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float z1 = *(float*)&ScriptParams[3]; float x2 = *(float*)&ScriptParams[4]; float y2 = *(float*)&ScriptParams[5]; float z2 = *(float*)&ScriptParams[6]; if (vehicle) UpdateCompareFlag(ped->m_pMyVehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); else UpdateCompareFlag(ped->IsWithinArea(x1, y1, z1, x2, y2, z2)); if (ScriptParams[7]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); return 0; } case COMMAND_CREATE_CAR: { CollectParameters(&m_nIp, 4); int32 handle; if (CModelInfo::IsBoatModel(ScriptParams[0])) { CBoat* boat = new CBoat(ScriptParams[0], MISSION_VEHICLE); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pos.z += boat->GetDistanceFromCentreOfMassToBaseOfModel(); boat->SetPosition(pos); CTheScripts::ClearSpaceForMissionEntity(pos, boat); boat->SetStatus(STATUS_ABANDONED); boat->bIsLocked = true; boat->AutoPilot.m_nCarMission = MISSION_NONE; boat->AutoPilot.m_nTempAction = TEMPACT_NONE; boat->AutoPilot.m_nCruiseSpeed = boat->AutoPilot.m_fMaxTrafficSpeed = 20.0f; if (m_bIsMissionScript) boat->bIsStaticWaitingForCollision = true; CWorld::Add(boat); handle = CPools::GetVehiclePool()->GetIndex(boat); } else { CVehicle* car; if (!CModelInfo::IsBikeModel(ScriptParams[0])) car = new CAutomobile(ScriptParams[0], MISSION_VEHICLE); else { car = new CBike(ScriptParams[0], MISSION_VEHICLE); ((CBike*)(car))->bIsStanding = true; } CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pos.z += car->GetDistanceFromCentreOfMassToBaseOfModel(); car->SetPosition(pos); CTheScripts::ClearSpaceForMissionEntity(pos, car); car->SetStatus(STATUS_ABANDONED); car->bIsLocked = true; CCarCtrl::JoinCarWithRoadSystem(car); car->AutoPilot.m_nCarMission = MISSION_NONE; car->AutoPilot.m_nTempAction = TEMPACT_NONE; car->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; car->AutoPilot.m_nCruiseSpeed = car->AutoPilot.m_fMaxTrafficSpeed = 9.0f; car->AutoPilot.m_nCurrentLane = car->AutoPilot.m_nNextLane = 0; car->bEngineOn = false; car->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pos); car->bHasBeenOwnedByPlayer = true; if (m_bIsMissionScript) car->bIsStaticWaitingForCollision = true; CWorld::Add(car); handle = CPools::GetVehiclePool()->GetIndex(car); } ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(handle, CLEANUP_CAR); return 0; } case COMMAND_DELETE_CAR: { CollectParameters(&m_nIp, 1); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); if (car) { CWorld::Remove(car); CWorld::RemoveReferencesToDeletedObject(car); delete car; } if (m_bIsMissionScript) CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CAR); return 0; } case COMMAND_CAR_GOTO_COORDINATES: { CollectParameters(&m_nIp, 4); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(car); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pos.z += car->GetDistanceFromCentreOfMassToBaseOfModel(); if (CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, pos, false)) car->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_STRAIGHT; else car->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS; car->SetStatus(STATUS_PHYSICS); car->bEngineOn = true; car->AutoPilot.m_nCruiseSpeed = Max(1, car->AutoPilot.m_nCruiseSpeed); car->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); return 0; } case COMMAND_CAR_WANDER_RANDOMLY: { CollectParameters(&m_nIp, 1); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(car); CCarCtrl::JoinCarWithRoadSystem(car); car->AutoPilot.m_nCarMission = MISSION_CRUISE; car->bEngineOn = true; car->AutoPilot.m_nCruiseSpeed = Max(1, car->AutoPilot.m_nCruiseSpeed); car->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); return 0; } case COMMAND_CAR_SET_IDLE: { CollectParameters(&m_nIp, 1); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(car); car->AutoPilot.m_nCarMission = MISSION_NONE; return 0; } case COMMAND_GET_CAR_COORDINATES: { CollectParameters(&m_nIp, 1); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(car); *(CVector*)&ScriptParams[0] = car->GetPosition(); StoreParameters(&m_nIp, 3); return 0; } case COMMAND_SET_CAR_COORDINATES: { CollectParameters(&m_nIp, 4); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(car); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pos.z += car->GetDistanceFromCentreOfMassToBaseOfModel(); car->SetIsStatic(false); /* Again weird usage of virtual functions. */ if (car->IsBoat()) { car->Teleport(pos); CTheScripts::ClearSpaceForMissionEntity(pos, car); } else { car->Teleport(pos); CTheScripts::ClearSpaceForMissionEntity(pos, car); /* May the following be inlined CCarCtrl function? */ switch (car->AutoPilot.m_nCarMission) { case MISSION_CRUISE: CCarCtrl::JoinCarWithRoadSystem(car); break; case MISSION_RAMPLAYER_FARAWAY: case MISSION_RAMPLAYER_CLOSE: case MISSION_BLOCKPLAYER_FARAWAY: case MISSION_BLOCKPLAYER_CLOSE: case MISSION_BLOCKPLAYER_HANDBRAKESTOP: CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, FindPlayerCoors(), false); break; case MISSION_GOTOCOORDS: case MISSION_GOTOCOORDS_STRAIGHT: CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, car->AutoPilot.m_vecDestinationCoors, false); break; case MISSION_GOTOCOORDS_ACCURATE: case MISSION_GOTO_COORDS_STRAIGHT_ACCURATE: CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, car->AutoPilot.m_vecDestinationCoors, false); break; case MISSION_RAMCAR_FARAWAY: case MISSION_RAMCAR_CLOSE: case MISSION_BLOCKCAR_FARAWAY: case MISSION_BLOCKCAR_CLOSE: case MISSION_BLOCKCAR_HANDBRAKESTOP: CCarCtrl::JoinCarWithRoadSystemGotoCoors(car, car->AutoPilot.m_pTargetCar->GetPosition(), false); break; default: break; } } return 0; } /* case COMMAND_IS_CAR_STILL_ALIVE: { CollectParameters(&m_nIp, 1); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); UpdateCompareFlag(car && car->GetStatus() != STATUS_WRECKED && (car->IsBoat() || !car->bIsInWater)); return 0; } */ case COMMAND_SET_CAR_CRUISE_SPEED: { CollectParameters(&m_nIp, 2); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(car); #if defined MISSION_REPLAY && defined SIMPLIER_MISSIONS car->AutoPilot.m_nCruiseSpeed = *(float*)&ScriptParams[1]; if (missionRetryScriptIndex == 40 && car->GetModelIndex() == MI_CHEETAH) // Turismo car->AutoPilot.m_nCruiseSpeed = 8 * car->AutoPilot.m_nCruiseSpeed / 10; car->AutoPilot.m_nCruiseSpeed = Min(car->AutoPilot.m_nCruiseSpeed, 60.0f * car->pHandling->Transmission.fMaxCruiseVelocity); #else car->AutoPilot.m_nCruiseSpeed = Min(*(float*)&ScriptParams[1], 60.0f * car->pHandling->Transmission.fMaxCruiseVelocity); #endif return 0; } case COMMAND_SET_CAR_DRIVING_STYLE: { CollectParameters(&m_nIp, 2); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(car); car->AutoPilot.m_nDrivingStyle = (uint8)ScriptParams[1]; return 0; } case COMMAND_SET_CAR_MISSION: { CollectParameters(&m_nIp, 2); CVehicle* car = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(car); car->AutoPilot.m_nCarMission = (uint8)ScriptParams[1]; car->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); car->bEngineOn = true; return 0; } case COMMAND_IS_CAR_IN_AREA_2D: { CollectParameters(&m_nIp, 6); CVehicle* vehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(vehicle); float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float x2 = *(float*)&ScriptParams[3]; float y2 = *(float*)&ScriptParams[4]; UpdateCompareFlag(vehicle->IsWithinArea(x1, y1, x2, y2)); if (ScriptParams[5]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugSquare(x1, y1, x2, y2); return 0; } case COMMAND_IS_CAR_IN_AREA_3D: { CollectParameters(&m_nIp, 8); CVehicle* vehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(vehicle); float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float z1 = *(float*)&ScriptParams[3]; float x2 = *(float*)&ScriptParams[4]; float y2 = *(float*)&ScriptParams[5]; float z2 = *(float*)&ScriptParams[6]; UpdateCompareFlag(vehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); if (ScriptParams[7]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); return 0; } case COMMAND_SPECIAL_0: case COMMAND_SPECIAL_1: case COMMAND_SPECIAL_2: case COMMAND_SPECIAL_3: case COMMAND_SPECIAL_4: case COMMAND_SPECIAL_5: case COMMAND_SPECIAL_6: case COMMAND_SPECIAL_7: script_assert(0); return 0; case COMMAND_PRINT_BIG: { wchar* key = CTheScripts::GetTextByKeyFromScript(&m_nIp); #ifdef MISSION_REPLAY if (strcmp((char*)&CTheScripts::ScriptSpace[m_nIp], "M_FAIL") == 0 && CanAllowMissionReplay()) AllowMissionReplay = 1; #endif CollectParameters(&m_nIp, 2); CMessages::AddBigMessage(key, ScriptParams[0], ScriptParams[1] - 1); return 0; } case COMMAND_PRINT: { wchar* key = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 2); CMessages::AddMessage(key, ScriptParams[0], ScriptParams[1]); return 0; } case COMMAND_PRINT_NOW: { wchar* key = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 2); CMessages::AddMessageJumpQ(key, ScriptParams[0], ScriptParams[1]); return 0; } /* case COMMAND_PRINT_SOON: { wchar* key = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 2); CMessages::AddMessageSoon(key, ScriptParams[0], ScriptParams[1]); return 0; } */ case COMMAND_CLEAR_PRINTS: CMessages::ClearMessages(); return 0; case COMMAND_GET_TIME_OF_DAY: ScriptParams[0] = CClock::GetHours(); ScriptParams[1] = CClock::GetMinutes(); StoreParameters(&m_nIp, 2); return 0; case COMMAND_SET_TIME_OF_DAY: CollectParameters(&m_nIp, 2); CClock::SetGameClock(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_GET_MINUTES_TO_TIME_OF_DAY: CollectParameters(&m_nIp, 2); ScriptParams[0] = CClock::GetGameClockMinutesUntil(ScriptParams[0], ScriptParams[1]); StoreParameters(&m_nIp, 1); return 0; case COMMAND_IS_POINT_ON_SCREEN: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= -100) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); UpdateCompareFlag(TheCamera.IsSphereVisible(pos, *(float*)&ScriptParams[3])); return 0; } case COMMAND_DEBUG_ON: CTheScripts::DbgFlag = true; return 0; case COMMAND_DEBUG_OFF: CTheScripts::DbgFlag = false; return 0; /* case COMMAND_RETURN_TRUE: UpdateCompareFlag(true); return 0; case COMMAND_RETURN_FALSE: UpdateCompareFlag(false); return 0; */ //case COMMAND_VAR_INT: default: script_assert(0); break; } return -1; } int8 CRunningScript::ProcessCommands200To299(int32 command) { switch (command) { /* Special commands. case COMMAND_VAR_FLOAT: case COMMAND_LVAR_INT: case COMMAND_LVAR_FLOAT: case COMMAND_LBRACKET: case COMMAND_RBRACKET: case COMMAND_REPEAT: case COMMAND_ENDREPEAT: case COMMAND_IF: case COMMAND_IFNOT: case COMMAND_ELSE: case COMMAND_ENDIF: case COMMAND_WHILE: case COMMAND_WHILENOT: case COMMAND_ENDWHILE: */ case COMMAND_ANDOR: CollectParameters(&m_nIp, 1); m_nAndOrState = ScriptParams[0]; if (m_nAndOrState == ANDOR_NONE){ m_bCondResult = false; // pointless }else if (m_nAndOrState >= ANDS_1 && m_nAndOrState <= ANDS_8){ m_bCondResult = true; m_nAndOrState++; }else if (m_nAndOrState >= ORS_1 && m_nAndOrState <= ORS_8){ m_bCondResult = false; m_nAndOrState++; }else{ script_assert(0 && "COMMAND_ANDOR: invalid ANDOR state"); } return 0; case COMMAND_LAUNCH_MISSION: { CollectParameters(&m_nIp, 1); CRunningScript* pNew = CTheScripts::StartNewScript(ScriptParams[0]); pNew->m_bIsMissionScript = true; return 0; } case COMMAND_MISSION_HAS_FINISHED: { if (!m_bIsMissionScript) return 0; CTheScripts::MissionCleanUp.Process(); return 0; } case COMMAND_STORE_CAR_CHAR_IS_IN: { CollectParameters(&m_nIp, 1); CPed* ped = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(ped); CVehicle* pCurrent = nil; if (ped->bInVehicle) { pCurrent = ped->m_pMyVehicle; } script_assert(pCurrent); // GetIndex(0) doesn't look good int handle = CPools::GetVehiclePool()->GetIndex(pCurrent); if (handle != CTheScripts::StoreVehicleIndex && m_bIsMissionScript){ CVehicle* pOld = CPools::GetVehiclePool()->GetAt(CTheScripts::StoreVehicleIndex); if (pOld){ CCarCtrl::RemoveFromInterestingVehicleList(pOld); if (pOld->VehicleCreatedBy == MISSION_VEHICLE && CTheScripts::StoreVehicleWasRandom){ pOld->VehicleCreatedBy = RANDOM_VEHICLE; pOld->bIsLocked = false; CCarCtrl::NumRandomCars++; CCarCtrl::NumMissionCars--; CTheScripts::MissionCleanUp.RemoveEntityFromList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); } } CTheScripts::StoreVehicleIndex = handle; switch (pCurrent->VehicleCreatedBy){ case RANDOM_VEHICLE: pCurrent->VehicleCreatedBy = MISSION_VEHICLE; CCarCtrl::NumMissionCars++; CCarCtrl::NumRandomCars--; CTheScripts::StoreVehicleWasRandom = true; CTheScripts::MissionCleanUp.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); break; case PARKED_VEHICLE: pCurrent->VehicleCreatedBy = MISSION_VEHICLE; CCarCtrl::NumMissionCars++; CCarCtrl::NumParkedCars--; CTheScripts::StoreVehicleWasRandom = true; CTheScripts::MissionCleanUp.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); break; case MISSION_VEHICLE: case PERMANENT_VEHICLE: CTheScripts::StoreVehicleWasRandom = false; break; default: break; } } ScriptParams[0] = CTheScripts::StoreVehicleIndex; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_STORE_CAR_PLAYER_IS_IN: { CollectParameters(&m_nIp, 1); CPed* ped = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(ped); if (!ped->bInVehicle) return 0; // No value written to output variable CVehicle* pCurrent = ped->m_pMyVehicle; script_assert(pCurrent); // Here pCurrent shouldn't be NULL anyway int handle = CPools::GetVehiclePool()->GetIndex(pCurrent); if (handle != CTheScripts::StoreVehicleIndex && m_bIsMissionScript) { CVehicle* pOld = CPools::GetVehiclePool()->GetAt(CTheScripts::StoreVehicleIndex); if (pOld){ CCarCtrl::RemoveFromInterestingVehicleList(pOld); if (pOld->VehicleCreatedBy == MISSION_VEHICLE && CTheScripts::StoreVehicleWasRandom){ pOld->VehicleCreatedBy = RANDOM_VEHICLE; pOld->bIsLocked = false; CCarCtrl::NumRandomCars++; CCarCtrl::NumMissionCars--; CTheScripts::MissionCleanUp.RemoveEntityFromList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); } } CTheScripts::StoreVehicleIndex = handle; switch (pCurrent->VehicleCreatedBy) { case RANDOM_VEHICLE: pCurrent->VehicleCreatedBy = MISSION_VEHICLE; CCarCtrl::NumMissionCars++; CCarCtrl::NumRandomCars--; CTheScripts::StoreVehicleWasRandom = true; CTheScripts::MissionCleanUp.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); break; case PARKED_VEHICLE: pCurrent->VehicleCreatedBy = MISSION_VEHICLE; CCarCtrl::NumMissionCars++; CCarCtrl::NumParkedCars--; CTheScripts::StoreVehicleWasRandom = true; CTheScripts::MissionCleanUp.AddEntityToList(CTheScripts::StoreVehicleIndex, CLEANUP_CAR); break; case MISSION_VEHICLE: case PERMANENT_VEHICLE: CTheScripts::StoreVehicleWasRandom = false; break; default: break; } } ScriptParams[0] = CTheScripts::StoreVehicleIndex; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_IS_CHAR_IN_CAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CVehicle* pCheckedVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); CVehicle* pActualVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; UpdateCompareFlag(pActualVehicle && pActualVehicle == pCheckedVehicle); return 0; } case COMMAND_IS_PLAYER_IN_CAR: { CollectParameters(&m_nIp, 2); CVehicle* pCheckedVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle == pCheckedVehicle); return 0; } case COMMAND_IS_CHAR_IN_MODEL: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CVehicle* pActualVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; UpdateCompareFlag(pActualVehicle && pActualVehicle->GetModelIndex() == ScriptParams[1]); return 0; } case COMMAND_IS_PLAYER_IN_MODEL: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetModelIndex() == ScriptParams[1]); return 0; } case COMMAND_IS_CHAR_IN_ANY_CAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle); return 0; } case COMMAND_IS_PLAYER_IN_ANY_CAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle); return 0; } case COMMAND_IS_BUTTON_PRESSED: { CollectParameters(&m_nIp, 2); UpdateCompareFlag(GetPadState(ScriptParams[0], ScriptParams[1]) != 0); return 0; } /* case COMMAND_GET_PAD_STATE: { CollectParameters(&m_nIp, 1); ScriptParams[0] = GetPadState(ScriptParams[0], ScriptParams[1]); StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_LOCATE_PLAYER_ANY_MEANS_2D: case COMMAND_LOCATE_PLAYER_ON_FOOT_2D: case COMMAND_LOCATE_PLAYER_IN_CAR_2D: case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D: LocatePlayerCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D: case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D: case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D: LocatePlayerCharCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_CHAR_ANY_MEANS_2D: case COMMAND_LOCATE_CHAR_ON_FOOT_2D: case COMMAND_LOCATE_CHAR_IN_CAR_2D: case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D: LocateCharCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D: case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D: case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D: LocateCharCharCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_PLAYER_ANY_MEANS_3D: case COMMAND_LOCATE_PLAYER_ON_FOOT_3D: case COMMAND_LOCATE_PLAYER_IN_CAR_3D: case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D: case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D: case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D: LocatePlayerCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D: case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D: case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D: LocatePlayerCharCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_CHAR_ANY_MEANS_3D: case COMMAND_LOCATE_CHAR_ON_FOOT_3D: case COMMAND_LOCATE_CHAR_IN_CAR_3D: case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D: case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D: case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D: LocateCharCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D: case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D: case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D: LocateCharCharCommand(command, &m_nIp); return 0; case COMMAND_CREATE_OBJECT: { CollectParameters(&m_nIp, 4); int mi = ScriptParams[0] >= 0 ? ScriptParams[0] : CTheScripts::UsedObjectArray[-ScriptParams[0]].index; CObject* pObj = new CObject(mi, false); pObj->ObjectCreatedBy = MISSION_OBJECT; CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pos.z += pObj->GetDistanceFromCentreOfMassToBaseOfModel(); pObj->SetPosition(pos); pObj->SetOrientation(0.0f, 0.0f, 0.0f); pObj->GetMatrix().UpdateRW(); pObj->UpdateRwFrame(); CBaseModelInfo* pModelInfo = CModelInfo::GetModelInfo(mi); if (pModelInfo->IsBuilding() && ((CSimpleModelInfo*)pModelInfo)->m_isBigBuilding) pObj->SetupBigBuilding(); CTheScripts::ClearSpaceForMissionEntity(pos, pObj); CWorld::Add(pObj); ScriptParams[0] = CPools::GetObjectPool()->GetIndex(pObj); StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_OBJECT); return 0; } case COMMAND_DELETE_OBJECT: { CollectParameters(&m_nIp, 1); CObject* pObj = CPools::GetObjectPool()->GetAt(ScriptParams[0]); if (pObj){ CWorld::Remove(pObj); CWorld::RemoveReferencesToDeletedObject(pObj); delete pObj; } if (m_bIsMissionScript) CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_OBJECT); return 0; } case COMMAND_ADD_SCORE: CollectParameters(&m_nIp, 2); CWorld::Players[ScriptParams[0]].m_nMoney += ScriptParams[1]; if (CWorld::Players[ScriptParams[0]].m_nMoney < 0) CWorld::Players[ScriptParams[0]].m_nMoney = 0; return 0; case COMMAND_IS_SCORE_GREATER: CollectParameters(&m_nIp, 2); UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_nMoney > ScriptParams[1]); return 0; case COMMAND_STORE_SCORE: CollectParameters(&m_nIp, 1); ScriptParams[0] = CWorld::Players[ScriptParams[0]].m_nMoney; StoreParameters(&m_nIp, 1); return 0; case COMMAND_GIVE_REMOTE_CONTROLLED_CAR_TO_PLAYER: { CollectParameters(&m_nIp, 5); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRemote::GivePlayerRemoteControlledCar(pos.x, pos.y, pos.z, DEGTORAD(*(float*)&ScriptParams[4]), MI_RCBANDIT); return 0; } case COMMAND_ALTER_WANTED_LEVEL: CollectParameters(&m_nIp, 2); CWorld::Players[ScriptParams[0]].m_pPed->SetWantedLevel(ScriptParams[1]); return 0; case COMMAND_ALTER_WANTED_LEVEL_NO_DROP: CollectParameters(&m_nIp, 2); CWorld::Players[ScriptParams[0]].m_pPed->SetWantedLevelNoDrop(ScriptParams[1]); return 0; case COMMAND_IS_WANTED_LEVEL_GREATER: CollectParameters(&m_nIp, 2); UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_pPed->m_pWanted->GetWantedLevel() > ScriptParams[1]); return 0; case COMMAND_CLEAR_WANTED_LEVEL: CollectParameters(&m_nIp, 1); CWorld::Players[ScriptParams[0]].m_pPed->SetWantedLevel(0); return 0; case COMMAND_SET_DEATHARREST_STATE: CollectParameters(&m_nIp, 1); m_bDeatharrestEnabled = (ScriptParams[0] == 1); return 0; case COMMAND_HAS_DEATHARREST_BEEN_EXECUTED: UpdateCompareFlag(m_bDeatharrestExecuted); return 0; /* case COMMAND_ADD_AMMO_TO_PLAYER: { CollectParameters(&m_nIp, 3); CWorld::Players[ScriptParams[0]].m_pPed->GrantAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); return 0; } */ case COMMAND_ADD_AMMO_TO_CHAR: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->GrantAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); return 0; } //case COMMAND_ADD_AMMO_TO_CAR: //case COMMAND_IS_PLAYER_STILL_ALIVE: case COMMAND_IS_PLAYER_DEAD: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_WBState == WBSTATE_WASTED); return 0; case COMMAND_IS_CHAR_DEAD: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); UpdateCompareFlag(!pPed || pPed->DyingOrDead()); return 0; } case COMMAND_IS_CAR_DEAD: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); UpdateCompareFlag(!pVehicle || pVehicle->GetStatus() == STATUS_WRECKED || pVehicle->bIsDrowning); return 0; } case COMMAND_SET_CHAR_THREAT_SEARCH: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->m_fearFlags |= ScriptParams[1]; return 0; } //case COMMAND_SET_CHAR_THREAT_REACTION: case COMMAND_SET_CHAR_OBJ_NO_OBJ: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bScriptObjectiveCompleted = false; pPed->ClearObjective(); return 0; } //case COMMAND_ORDER_DRIVER_OUT_OF_CAR: //case COMMAND_ORDER_CHAR_TO_DRIVE_CAR: //case COMMAND_ADD_PATROL_POINT: //case COMMAND_IS_PLAYER_IN_GANGZONE: case COMMAND_IS_PLAYER_IN_ZONE: { CollectParameters(&m_nIp, 1); CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; char label[12]; CTheScripts::ReadTextLabelFromScript(&m_nIp, label); int zoneToCheck = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_DEFAULT); if (zoneToCheck != -1) m_nIp += KEY_LENGTH_IN_SCRIPT; /* why only if zone != 1? */ CVector pos = pPlayer->GetPos(); CZone* pZone = CTheZones::GetNavigationZone(zoneToCheck); UpdateCompareFlag(CTheZones::PointLiesWithinZone(&pos, pZone)); return 0; } case COMMAND_IS_PLAYER_PRESSING_HORN: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_pPed->GetPedState() == PED_DRIVING && CPad::GetPad(ScriptParams[0])->GetHorn()); return 0; case COMMAND_HAS_CHAR_SPOTTED_PLAYER: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->OurPedCanSeeThisOne(CWorld::Players[ScriptParams[1]].m_pPed)); return 0; } //case COMMAND_ORDER_CHAR_TO_BACKDOOR: //case COMMAND_ADD_CHAR_TO_GANG: case COMMAND_IS_CHAR_OBJECTIVE_PASSED: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bScriptObjectiveCompleted); return 0; } /* Not implemented. case COMMAND_SET_CHAR_DRIVE_AGGRESSION: case COMMAND_SET_CHAR_MAX_DRIVESPEED: */ case COMMAND_CREATE_CHAR_INSIDE_CAR: { CollectParameters(&m_nIp, 3); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); switch (ScriptParams[2]) { case MI_COP: if (ScriptParams[1] == PEDTYPE_COP) ScriptParams[2] = COP_STREET; break; case MI_SWAT: if (ScriptParams[1] == PEDTYPE_COP) ScriptParams[2] = COP_SWAT; break; case MI_FBI: if (ScriptParams[1] == PEDTYPE_COP) ScriptParams[2] = COP_FBI; break; case MI_ARMY: if (ScriptParams[1] == PEDTYPE_COP) ScriptParams[2] = COP_ARMY; break; case MI_MEDIC: if (ScriptParams[1] == PEDTYPE_EMERGENCY) ScriptParams[2] = PEDTYPE_EMERGENCY; break; case MI_FIREMAN: if (ScriptParams[1] == PEDTYPE_FIREMAN) ScriptParams[2] = PEDTYPE_FIREMAN; break; default: break; } CPed* pPed; if (ScriptParams[1] == PEDTYPE_COP) pPed = new CCopPed((eCopType)ScriptParams[2]); else if (ScriptParams[1] == PEDTYPE_EMERGENCY || ScriptParams[1] == PEDTYPE_FIREMAN) pPed = new CEmergencyPed(ScriptParams[2]); else pPed = new CCivilianPed((ePedType)ScriptParams[1], ScriptParams[2]); pPed->CharCreatedBy = MISSION_CHAR; pPed->bRespondsToThreats = false; pPed->bAllowMedicsToReviveMe = false; pPed->bIsPlayerFriend = false; if (pVehicle->bIsBus) pPed->bRenderPedInCar = false; pPed->SetPosition(pVehicle->GetPosition()); pPed->SetOrientation(0.0f, 0.0f, 0.0f); pPed->SetPedState(PED_DRIVING); CPopulation::ms_nTotalMissionPeds++; script_assert(!pVehicle->pDriver); pVehicle->pDriver = pPed; pVehicle->pDriver->RegisterReference((CEntity**)&pVehicle->pDriver); pPed->m_pMyVehicle = pVehicle; pPed->m_pMyVehicle->RegisterReference((CEntity**)&pPed->m_pMyVehicle); pPed->bInVehicle = true; pVehicle->SetStatus(STATUS_PHYSICS); if (!pVehicle->IsBoat()) pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; pVehicle->bEngineOn = true; pPed->bUsesCollision = false; pPed->AddInCarAnims(pVehicle, true); pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pPed->GetPosition()); CWorld::Add(pPed); ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_WARP_PLAYER_FROM_CAR_TO_COORD: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[1]; CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); if (pPlayer->m_pPed->bInVehicle){ script_assert(pPlayer->m_pPed->m_pMyVehicle); if (pPlayer->m_pPed->m_pMyVehicle->bIsBus) pPlayer->m_pPed->bRenderPedInCar = true; if (pPlayer->m_pPed->m_pMyVehicle->pDriver == pPlayer->m_pPed){ pPlayer->m_pPed->m_pMyVehicle->RemoveDriver(); pPlayer->m_pPed->m_pMyVehicle->SetStatus(STATUS_ABANDONED); pPlayer->m_pPed->m_pMyVehicle->bEngineOn = false; pPlayer->m_pPed->m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; }else{ pPlayer->m_pPed->m_pMyVehicle->RemovePassenger(pPlayer->m_pPed); } } pPlayer->m_pPed->RemoveInCarAnims(); pPlayer->m_pPed->bInVehicle = false; pPlayer->m_pPed->m_pMyVehicle = nil; pPlayer->m_pPed->SetPedState(PED_IDLE); pPlayer->m_pPed->bUsesCollision = true; pPlayer->m_pPed->SetMoveSpeed(0.0f, 0.0f, 0.0f); pPlayer->m_pPed->ReplaceWeaponWhenExitingVehicle(); if (pPlayer->m_pPed->m_pVehicleAnim) pPlayer->m_pPed->m_pVehicleAnim->blendDelta = -1000.0f; pPlayer->m_pPed->m_pVehicleAnim = nil; pPlayer->m_pPed->SetMoveState(PEDMOVE_NONE); CAnimManager::BlendAnimation(pPlayer->m_pPed->GetClump(), pPlayer->m_pPed->m_animGroup, ANIM_STD_IDLE, 1000.0f); pPlayer->m_pPed->RestartNonPartialAnims(); AudioManager.PlayerJustLeftCar(); pos.z += pPlayer->m_pPed->GetDistanceFromCentreOfMassToBaseOfModel(); pPlayer->m_pPed->Teleport(pos); CTheScripts::ClearSpaceForMissionEntity(pos, pPlayer->m_pPed); return 0; } //case COMMAND_MAKE_CHAR_DO_NOTHING: default: script_assert(0); break; } return -1; } #ifdef MISSION_REPLAY bool CRunningScript::CanAllowMissionReplay() { if (AllowMissionReplay) return false; if (CStats::LastMissionPassedName[0] == '\0') return false; for (int i = 0; i < ARRAY_SIZE(nonMissionScripts); i++) { if (strcmp(m_abScriptName, nonMissionScripts[i]) == 0) return false; } return true; } uint32 AddExtraDeathDelay() { if (missionRetryScriptIndex == 63) return 7000; if (missionRetryScriptIndex == 64) return 4000; return 1000; } void RetryMission(int type, int unk) { if (type == 0) { doingMissionRetry = true; FrontEndMenuManager.m_nCurrScreen = 57; // MENUPAGE_MISSION_RETRY FrontEndMenuManager.RequestFrontEndStartUp(); } else if (type == 2) { doingMissionRetry = false; AllowMissionReplay = 6; CTheScripts::MissionCleanUp.Process(); } } #endif #ifdef MISSION_SWITCHER void CTheScripts::SwitchToMission(int32 mission) { for (CRunningScript* pScript = CTheScripts::pActiveScripts; pScript != nil; pScript = pScript->GetNext()) { if (!pScript->m_bIsMissionScript || !pScript->m_bDeatharrestEnabled) { continue; } while (pScript->m_nStackPointer > 0) --pScript->m_nStackPointer; pScript->m_nIp = pScript->m_anStack[pScript->m_nStackPointer]; *(int32*)&CTheScripts::ScriptSpace[CTheScripts::OnAMissionFlag] = 0; pScript->m_nWakeTime = 0; pScript->m_bDeatharrestExecuted = true; while (!pScript->ProcessOneCommand()); CMessages::ClearMessages(); } if (CTheScripts::NumberOfExclusiveMissionScripts > 0 && mission <= UINT16_MAX - 2) return; #ifdef MISSION_REPLAY missionRetryScriptIndex = mission; if (missionRetryScriptIndex == 19) CStats::LastMissionPassedName[0] = '\0'; #endif CTimer::Suspend(); int offset = CTheScripts::MultiScriptArray[mission]; #ifdef USE_DEBUG_SCRIPT_LOADER CFileMgr::ChangeDir("\\data\\"); int handle = CFileMgr::OpenFile(scriptfile, "rb"); CFileMgr::ChangeDir("\\"); #else CFileMgr::ChangeDir("\\"); int handle = CFileMgr::OpenFile("data\\main.scm", "rb"); #endif CFileMgr::Seek(handle, offset, 0); CFileMgr::Read(handle, (const char*)&CTheScripts::ScriptSpace[SIZE_MAIN_SCRIPT], SIZE_MISSION_SCRIPT); CFileMgr::CloseFile(handle); CRunningScript* pMissionScript = CTheScripts::StartNewScript(SIZE_MAIN_SCRIPT); CTimer::Resume(); pMissionScript->m_bIsMissionScript = true; pMissionScript->m_bMissionFlag = true; CTheScripts::bAlreadyRunningAMissionScript = true; CGameLogic::ClearShortCut(); } #endif ================================================ FILE: src/control/Script.h ================================================ #pragma once #include "Font.h" #include "PedType.h" #include "Text.h" #include "Sprite2d.h" class CEntity; class CBuilding; class CPhysical; class CVehicle; class CPed; class CObject; class CPlayerInfo; class CRunningScript; extern int32 ScriptParams[32]; void FlushLog(); #define script_assert(_Expression) FlushLog(); assert(_Expression); #define PICKUP_PLACEMENT_OFFSET (0.5f) #define PED_FIND_Z_OFFSET (5.0f) #define COP_PED_FIND_Z_OFFSET (10.0f) #define UPSIDEDOWN_UP_THRESHOLD (-0.97f) #define UPSIDEDOWN_MOVE_SPEED_THRESHOLD (0.01f) #define UPSIDEDOWN_TURN_SPEED_THRESHOLD (0.02f) #define UPSIDEDOWN_TIMER_THRESHOLD (1000) #define SPHERE_MARKER_R (252) #define SPHERE_MARKER_G (138) #define SPHERE_MARKER_B (242) #define SPHERE_MARKER_A (228) #define SPHERE_MARKER_PULSE_PERIOD 2048 #define SPHERE_MARKER_PULSE_FRACTION 0.1f #ifdef USE_PRECISE_MEASUREMENT_CONVERTION #define MILES_IN_METER (0.000621371192f) #define METERS_IN_FOOT (0.3048f) #define FEET_IN_METER (3.28084f) #else #define MILES_IN_METER (1 / 1670.f) #define METERS_IN_FOOT (0.3f) #define FEET_IN_METER (3.33f) #endif #define KEY_LENGTH_IN_SCRIPT (8) //#define GTA_SCRIPT_COLLECTIVE struct intro_script_rectangle { bool m_bIsUsed; bool m_bBeforeFade; int16 m_nTextureId; CRect m_sRect; CRGBA m_sColor; intro_script_rectangle() { } ~intro_script_rectangle() { } }; VALIDATE_SIZE(intro_script_rectangle, 0x18); enum { SCRIPT_TEXT_MAX_LENGTH = 100 }; struct intro_text_line { float m_fScaleX; float m_fScaleY; CRGBA m_sColor; bool m_bJustify; bool m_bCentered; bool m_bBackground; bool m_bBackgroundOnly; float m_fWrapX; float m_fCenterSize; CRGBA m_sBackgroundColor; bool m_bTextProportional; bool m_bTextBeforeFade; bool m_bRightJustify; int32 m_nFont; float m_fAtX; float m_fAtY; wchar m_Text[SCRIPT_TEXT_MAX_LENGTH]; intro_text_line() { } ~intro_text_line() { } void Reset() { m_fScaleX = 0.48f; m_fScaleY = 1.12f; m_sColor = CRGBA(225, 225, 225, 255); m_bJustify = false; m_bRightJustify = false; m_bCentered = false; m_bBackground = false; m_bBackgroundOnly = false; m_fWrapX = 182.0f; m_fCenterSize = DEFAULT_SCREEN_WIDTH; m_sBackgroundColor = CRGBA(128, 128, 128, 128); m_bTextProportional = true; m_bTextBeforeFade = false; m_nFont = FONT_STANDARD; m_fAtX = 0.0f; m_fAtY = 0.0f; memset(&m_Text, 0, sizeof(m_Text)); } }; VALIDATE_SIZE(intro_text_line, 0x414); struct script_sphere_struct { bool m_bInUse; uint16 m_Index; uint32 m_Id; CVector m_vecCenter; float m_fRadius; script_sphere_struct() { } }; struct CStoredLine { CVector vecInf; CVector vecSup; uint32 color1; uint32 color2; }; enum { CLEANUP_UNUSED = 0, CLEANUP_CAR, CLEANUP_CHAR, CLEANUP_OBJECT }; struct cleanup_entity_struct { uint8 type; int32 id; }; enum { MAX_CLEANUP = 50, MAX_UPSIDEDOWN_CAR_CHECKS = 6, MAX_STUCK_CAR_CHECKS = 16 }; class CMissionCleanup { public: cleanup_entity_struct m_sEntities[MAX_CLEANUP]; uint8 m_nCount; CMissionCleanup(); void Init(); cleanup_entity_struct* FindFree(); void AddEntityToList(int32, uint8); void RemoveEntityFromList(int32, uint8); void Process(); void CheckIfCollisionHasLoadedForMissionObjects(); }; struct upsidedown_car_data { int32 m_nVehicleIndex; uint32 m_nUpsideDownTimer; }; class CUpsideDownCarCheck { upsidedown_car_data m_sCars[MAX_UPSIDEDOWN_CAR_CHECKS]; public: void Init(); bool IsCarUpsideDown(int32); bool IsCarUpsideDown(CVehicle*); void UpdateTimers(); bool AreAnyCarsUpsideDown(); void AddCarToCheck(int32); void RemoveCarFromCheck(int32); bool HasCarBeenUpsideDownForAWhile(int32); }; struct stuck_car_data { int32 m_nVehicleIndex; CVector m_vecPos; int32 m_nLastCheck; float m_fRadius; uint32 m_nStuckTime; bool m_bStuck; stuck_car_data() { } void Reset(); }; class CStuckCarCheck { stuck_car_data m_sCars[MAX_STUCK_CAR_CHECKS]; public: void Init(); void Process(); void AddCarToCheck(int32, float, uint32); void RemoveCarFromCheck(int32); bool HasCarBeenStuckForAWhile(int32); }; enum { ARGUMENT_END = 0, ARGUMENT_INT32, ARGUMENT_GLOBALVAR, ARGUMENT_LOCALVAR, ARGUMENT_INT8, ARGUMENT_INT16, ARGUMENT_FLOAT }; struct tCollectiveData { int32 colIndex; int32 pedIndex; }; enum { USED_OBJECT_NAME_LENGTH = 24 }; struct tUsedObject { char name[USED_OBJECT_NAME_LENGTH]; int32 index; }; struct tBuildingSwap { CBuilding* m_pBuilding; int32 m_nNewModel; int32 m_nOldModel; }; enum { MAX_STACK_DEPTH = 6, NUM_LOCAL_VARS = 16, NUM_TIMERS = 2 }; class CRunningScript { enum { ANDOR_NONE = 0, ANDS_1 = 1, ANDS_2, ANDS_3, ANDS_4, ANDS_5, ANDS_6, ANDS_7, ANDS_8, ORS_1 = 21, ORS_2, ORS_3, ORS_4, ORS_5, ORS_6, ORS_7, ORS_8 }; public: CRunningScript* next; CRunningScript* prev; char m_abScriptName[8]; uint32 m_nIp; uint32 m_anStack[MAX_STACK_DEPTH]; uint16 m_nStackPointer; int32 m_anLocalVariables[NUM_LOCAL_VARS + NUM_TIMERS]; bool m_bIsActive; bool m_bCondResult; bool m_bIsMissionScript; bool m_bSkipWakeTime; uint32 m_nWakeTime; uint16 m_nAndOrState; bool m_bNotFlag; bool m_bDeatharrestEnabled; bool m_bDeatharrestExecuted; bool m_bMissionFlag; public: void SetIP(uint32 ip) { m_nIp = ip; } CRunningScript* GetNext() const { return next; } void Save(uint8*& buf); void Load(uint8*& buf); void UpdateTimers(float timeStep) { m_anLocalVariables[NUM_LOCAL_VARS] += timeStep; m_anLocalVariables[NUM_LOCAL_VARS + 1] += timeStep; } void Init(); void Process(); void RemoveScriptFromList(CRunningScript**); void AddScriptToList(CRunningScript**); static const uint32 nSaveStructSize; void CollectParameters(uint32*, int16); int32 CollectNextParameterWithoutIncreasingPC(uint32); int32* GetPointerToScriptVariable(uint32*, int16); void StoreParameters(uint32*, int16); int8 ProcessOneCommand(); void DoDeatharrestCheck(); void UpdateCompareFlag(bool); int16 GetPadState(uint16, uint16); int8 ProcessCommands0To99(int32); int8 ProcessCommands100To199(int32); int8 ProcessCommands200To299(int32); int8 ProcessCommands300To399(int32); int8 ProcessCommands400To499(int32); int8 ProcessCommands500To599(int32); int8 ProcessCommands600To699(int32); int8 ProcessCommands700To799(int32); int8 ProcessCommands800To899(int32); int8 ProcessCommands900To999(int32); int8 ProcessCommands1000To1099(int32); int8 ProcessCommands1100To1199(int32); int8 ProcessCommands1200To1299(int32); int8 ProcessCommands1300To1399(int32); int8 ProcessCommands1400To1499(int32); void LocatePlayerCommand(int32, uint32*); void LocatePlayerCharCommand(int32, uint32*); void LocatePlayerCarCommand(int32, uint32*); void LocateCharCommand(int32, uint32*); void LocateCharCharCommand(int32, uint32*); void LocateCharCarCommand(int32, uint32*); void LocateCharObjectCommand(int32, uint32*); void LocateCarCommand(int32, uint32*); void LocateSniperBulletCommand(int32, uint32*); void PlayerInAreaCheckCommand(int32, uint32*); void PlayerInAngledAreaCheckCommand(int32, uint32*); void CharInAreaCheckCommand(int32, uint32*); void CarInAreaCheckCommand(int32, uint32*); void LocateObjectCommand(int32, uint32*); void ObjectInAreaCheckCommand(int32, uint32*); #ifdef GTA_SCRIPT_COLLECTIVE void LocateCollectiveCommand(int32, uint32*); void LocateCollectiveCharCommand(int32, uint32*); void LocateCollectiveCarCommand(int32, uint32*); void LocateCollectivePlayerCommand(int32, uint32*); void CollectiveInAreaCheckCommand(int32, uint32*); #endif #ifdef MISSION_REPLAY bool CanAllowMissionReplay(); #endif #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT int CollectParameterForDebug(char* buf, bool& var); void GetStoredParameterForDebug(char* buf); #endif float LimitAngleOnCircle(float angle) { return angle < 0.0f ? angle + 360.0f : angle; } bool ThisIsAValidRandomCop(uint32 mi, int cop, int swat, int fbi, int army, int miami); bool ThisIsAValidRandomPed(uint32 pedtype, int civ, int gang, int criminal); bool CheckDamagedWeaponType(int32 actual, int32 type); }; enum { VAR_LOCAL = 1, VAR_GLOBAL = 2, }; enum { #ifdef PS2 SIZE_MAIN_SCRIPT = 205512, #else SIZE_MAIN_SCRIPT = 225512, #endif SIZE_MISSION_SCRIPT = 35000, SIZE_SCRIPT_SPACE = SIZE_MAIN_SCRIPT + SIZE_MISSION_SCRIPT }; enum { MAX_NUM_SCRIPTS = 128, MAX_NUM_INTRO_TEXT_LINES = 48, MAX_NUM_INTRO_RECTANGLES = 16, MAX_NUM_SCRIPT_SRPITES = 16, MAX_NUM_SCRIPT_SPHERES = 16, MAX_NUM_USED_OBJECTS = 220, MAX_NUM_MISSION_SCRIPTS = 120, MAX_NUM_BUILDING_SWAPS = 25, MAX_NUM_INVISIBILITY_SETTINGS = 20, MAX_NUM_STORED_LINES = 1024 }; class CTheScripts { public: static uint8 ScriptSpace[SIZE_SCRIPT_SPACE]; static CRunningScript ScriptsArray[MAX_NUM_SCRIPTS]; static intro_text_line IntroTextLines[MAX_NUM_INTRO_TEXT_LINES]; static intro_script_rectangle IntroRectangles[MAX_NUM_INTRO_RECTANGLES]; static CSprite2d ScriptSprites[MAX_NUM_SCRIPT_SRPITES]; static script_sphere_struct ScriptSphereArray[MAX_NUM_SCRIPT_SPHERES]; static tUsedObject UsedObjectArray[MAX_NUM_USED_OBJECTS]; static int32 MultiScriptArray[MAX_NUM_MISSION_SCRIPTS]; static tBuildingSwap BuildingSwapArray[MAX_NUM_BUILDING_SWAPS]; static CEntity* InvisibilitySettingArray[MAX_NUM_INVISIBILITY_SETTINGS]; static CStoredLine aStoredLines[MAX_NUM_STORED_LINES]; static bool DbgFlag; static uint32 OnAMissionFlag; static CMissionCleanup MissionCleanUp; static CStuckCarCheck StuckCars; static CUpsideDownCarCheck UpsideDownCars; static int32 StoreVehicleIndex; static bool StoreVehicleWasRandom; static CRunningScript *pIdleScripts; static CRunningScript *pActiveScripts; static int32 NextFreeCollectiveIndex; static int32 LastRandomPedId; static uint16 NumberOfUsedObjects; static bool bAlreadyRunningAMissionScript; static bool bUsingAMultiScriptFile; static uint16 NumberOfMissionScripts; static uint32 LargestMissionScriptSize; static uint32 MainScriptSize; static uint8 FailCurrentMission; static uint16 NumScriptDebugLines; static uint16 NumberOfIntroRectanglesThisFrame; static uint16 NumberOfIntroTextLinesThisFrame; static uint8 UseTextCommands; static uint16 CommandsExecuted; static uint16 ScriptsUpdated; static uint32 LastMissionPassedTime; static uint16 NumberOfExclusiveMissionScripts; #if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) #define CARDS_IN_SUIT (13) #define NUM_SUITS (4) #define MAX_DECKS (6) #define CARDS_IN_DECK (CARDS_IN_SUIT * NUM_SUITS) #define CARDS_IN_STACK (CARDS_IN_DECK * MAX_DECKS) static int16 CardStack[CARDS_IN_STACK]; static int16 CardStackPosition; #endif static bool bPlayerIsInTheStatium; static uint8 RiotIntensity; static bool bPlayerHasMetDebbieHarry; static void Init(); static void Process(); static CRunningScript* StartTestScript(); static bool IsPlayerOnAMission(); static void ClearSpaceForMissionEntity(const CVector&, CEntity*); static void UndoBuildingSwaps(); static void UndoEntityInvisibilitySettings(); static void ScriptDebugLine3D(float x1, float y1, float z1, float x2, float y2, float z2, uint32 col, uint32 col2); static void RenderTheScriptDebugLines(); static void SaveAllScripts(uint8*, uint32*); static void LoadAllScripts(uint8*, uint32); static bool IsDebugOn() { return DbgFlag; }; static void InvertDebugFlag() { DbgFlag = !DbgFlag; } static int32* GetPointerToScriptVariable(int32 offset) { assert(offset >= 8 && offset < CTheScripts::GetSizeOfVariableSpace()); return (int32*)&ScriptSpace[offset]; } static int32 Read4BytesFromScript(uint32* pIp) { int32 retval = ScriptSpace[*pIp + 3] << 24 | ScriptSpace[*pIp + 2] << 16 | ScriptSpace[*pIp + 1] << 8 | ScriptSpace[*pIp]; *pIp += 4; return retval; } static int16 Read2BytesFromScript(uint32* pIp) { int16 retval = ScriptSpace[*pIp + 1] << 8 | ScriptSpace[*pIp]; *pIp += 2; return retval; } static int8 Read1ByteFromScript(uint32* pIp) { int8 retval = ScriptSpace[*pIp]; *pIp += 1; return retval; } static float ReadFloatFromScript(uint32* pIp) { return Read2BytesFromScript(pIp) / 16.0f; } static void ReadTextLabelFromScript(uint32* pIp, char* buf) { strncpy(buf, (const char*)&CTheScripts::ScriptSpace[*pIp], KEY_LENGTH_IN_SCRIPT); } static wchar* GetTextByKeyFromScript(uint32* pIp) { wchar* text = TheText.Get((const char*)&CTheScripts::ScriptSpace[*pIp]); *pIp += KEY_LENGTH_IN_SCRIPT; return text; } static int32 GetSizeOfVariableSpace() { uint32 tmp = 3; return Read4BytesFromScript(&tmp); } static CRunningScript* StartNewScript(uint32); static void CleanUpThisVehicle(CVehicle*); static void CleanUpThisPed(CPed*); static void CleanUpThisObject(CObject*); static bool IsPedStopped(CPed*); static bool IsPlayerStopped(CPlayerInfo*); static bool IsVehicleStopped(CVehicle*); static void PrintListSizes(); static void ReadObjectNamesFromScript(); static void UpdateObjectIndices(); static void ReadMultiScriptFileOffsetsFromScript(); static void DrawScriptSpheres(); static void HighlightImportantArea(uint32, float, float, float, float, float); static void HighlightImportantAngledArea(uint32, float, float, float, float, float, float, float, float, float); static void DrawDebugSquare(float, float, float, float); static void DrawDebugAngledSquare(float, float, float, float, float, float, float, float); static void DrawDebugCube(float, float, float, float, float, float); static void DrawDebugAngledCube(float, float, float, float, float, float, float, float, float, float); static void AddToInvisibilitySwapArray(CEntity*, bool); static void AddToBuildingSwapArray(CBuilding*, int32, int32); static int32 GetActualScriptSphereIndex(int32 index); static int32 AddScriptSphere(int32 id, CVector pos, float radius); static int32 GetNewUniqueScriptSphereIndex(int32 index); static void RemoveScriptSphere(int32 index); static void RemoveScriptTextureDictionary(); public: static void RemoveThisPed(CPed* pPed); static uint32& GetLastMissionPassedTime() { return LastMissionPassedTime; } #ifdef MISSION_SWITCHER static void SwitchToMission(int32 mission); #endif #ifdef GTA_SCRIPT_COLLECTIVE static void AdvanceCollectiveIndex() { if (NextFreeCollectiveIndex == INT32_MAX) NextFreeCollectiveIndex = 0; else NextFreeCollectiveIndex++; } static int AddPedsInVehicleToCollective(int); static int AddPedsInAreaToCollective(float, float, float, float); static int FindFreeSlotInCollectiveArray(); static void SetObjectiveForAllPedsInCollective(int, eObjective, int16, int16); static void SetObjectiveForAllPedsInCollective(int, eObjective, CVector, float); static void SetObjectiveForAllPedsInCollective(int, eObjective, CVector); static void SetObjectiveForAllPedsInCollective(int, eObjective, void*); static void SetObjectiveForAllPedsInCollective(int, eObjective); #endif }; #ifdef USE_DEBUG_SCRIPT_LOADER extern int scriptToLoad; #endif #ifdef MISSION_REPLAY static_assert(false, "Mission replay is not supported"); extern int AllowMissionReplay; extern uint32 WaitForMissionActivate; extern uint32 WaitForSave; extern uint32 MissionStartTime; extern int missionRetryScriptIndex; extern bool doingMissionRetry; extern bool gbTryingPorn4Again; extern int IsInAmmunation; extern int MissionSkipLevel; uint32 AddExtraDeathDelay(); void RetryMission(int, int); #endif #ifdef USE_DEBUG_SCRIPT_LOADER extern int scriptToLoad; #endif ================================================ FILE: src/control/Script2.cpp ================================================ #include "common.h" #include "Script.h" #include "ScriptCommands.h" #include "Camera.h" #include "CarCtrl.h" #include "CarGen.h" #include "CivilianPed.h" #include "CopPed.h" #include "Cranes.h" #include "DMAudio.h" #include "EmergencyPed.h" #include "Garages.h" #include "General.h" #include "Messages.h" #include "Pad.h" #include "PedRoutes.h" #include "Pools.h" #include "Population.h" #include "Radar.h" #include "Restart.h" #include "Shadows.h" #include "User.h" #include "Wanted.h" #include "WaterLevel.h" #include "Weather.h" #include "World.h" #include "Zones.h" int8 CRunningScript::ProcessCommands300To399(int32 command) { switch (command) { //case COMMAND_SET_CHAR_INVINCIBLE: //case COMMAND_SET_PLAYER_INVINCIBLE: //case COMMAND_SET_CHAR_GRAPHIC_TYPE: //case COMMAND_SET_PLAYER_GRAPHIC_TYPE: /* case COMMAND_HAS_PLAYER_BEEN_ARRESTED: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_WBState == WBSTATE_BUSTED); return 0; */ //case COMMAND_STOP_CHAR_DRIVING: //case COMMAND_KILL_CHAR: //case COMMAND_SET_FAVOURITE_CAR_MODEL_FOR_CHAR: //case COMMAND_SET_CHAR_OCCUPATION: /* case COMMAND_CHANGE_CAR_LOCK: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->m_nDoorLock = (eCarLock)ScriptParams[1]; return 0; } case COMMAND_SHAKE_CAM_WITH_POINT: CollectParameters(&m_nIp, 4); TheCamera.CamShake(ScriptParams[0] / 1000.0f, *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3]); return 0; */ case COMMAND_IS_CAR_MODEL: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->GetModelIndex() == ScriptParams[1]); return 0; } //case COMMAND_IS_CAR_REMAP: //case COMMAND_HAS_CAR_JUST_SUNK: //case COMMAND_SET_CAR_NO_COLLIDE: /* case COMMAND_IS_CAR_DEAD_IN_AREA_2D: { CollectParameters(&m_nIp, 6); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float x2 = *(float*)&ScriptParams[3]; float y2 = *(float*)&ScriptParams[4]; UpdateCompareFlag(pVehicle->GetStatus() == STATUS_WRECKED && pVehicle->IsWithinArea(x1, y1, x2, y2)); if (ScriptParams[5]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugSquare(x1, y1, x2, y2); return 0; } case COMMAND_IS_CAR_DEAD_IN_AREA_3D: { CollectParameters(&m_nIp, 8); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float z1 = *(float*)&ScriptParams[3]; float x2 = *(float*)&ScriptParams[4]; float y2 = *(float*)&ScriptParams[5]; float z2 = *(float*)&ScriptParams[6]; UpdateCompareFlag(pVehicle->GetStatus() == STATUS_WRECKED && pVehicle->IsWithinArea(x1, y1, z1, x2, y2, z2)); if (ScriptParams[7]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, (z1 + z2) / 2); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugCube(x1, y1, z1, x2, y2, z2); return 0; } */ //case COMMAND_IS_TRAILER_ATTACHED: //case COMMAND_IS_CAR_ON_TRAILER: //case COMMAND_HAS_CAR_GOT_WEAPON: //case COMMAND_PARK: //case COMMAND_HAS_PARK_FINISHED: //case COMMAND_KILL_ALL_PASSENGERS: //case COMMAND_SET_CAR_BULLETPROOF: //case COMMAND_SET_CAR_FLAMEPROOF: //case COMMAND_SET_CAR_ROCKETPROOF: //case COMMAND_IS_CARBOMB_ACTIVE: //case COMMAND_GIVE_CAR_ALARM: //case COMMAND_PUT_CAR_ON_TRAILER: /* case COMMAND_IS_CAR_CRUSHED: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CGarages::HasCarBeenCrushed(ScriptParams[0])); return 0; */ //case COMMAND_CREATE_GANG_CAR: case COMMAND_CREATE_CAR_GENERATOR: { CollectParameters(&m_nIp, 12); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z > MAP_Z_LOW_LIMIT) pos.z += 0.015f; ScriptParams[0] = CTheCarGenerators::CreateCarGenerator( pos.x, pos.y, pos.z, *(float*)&ScriptParams[3], ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7], ScriptParams[8], ScriptParams[9], ScriptParams[10], ScriptParams[11]); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SWITCH_CAR_GENERATOR: { CollectParameters(&m_nIp, 2); CCarGenerator* pCarGen = &CTheCarGenerators::CarGeneratorArray[ScriptParams[0]]; if (ScriptParams[1] == 0){ pCarGen->SwitchOff(); }else if (ScriptParams[1] <= 100){ pCarGen->SwitchOn(); pCarGen->SetUsesRemaining(ScriptParams[1]); }else{ pCarGen->SwitchOn(); } return 0; } /* case COMMAND_ADD_PAGER_MESSAGE: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 3); CUserDisplay::Pager.AddMessage(text, ScriptParams[0], ScriptParams[1], ScriptParams[2]); return 0; } */ case COMMAND_DISPLAY_ONSCREEN_TIMER: { script_assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); m_nIp++; uint16 offset = CTheScripts::Read2BytesFromScript(&m_nIp); CollectParameters(&m_nIp, 1); CUserDisplay::OnscnTimer.AddClock(offset, nil, ScriptParams[0] != 0); return 0; } case COMMAND_CLEAR_ONSCREEN_TIMER: { script_assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); m_nIp++; CUserDisplay::OnscnTimer.ClearClock((uint16)CTheScripts::Read2BytesFromScript(&m_nIp)); return 0; } case COMMAND_DISPLAY_ONSCREEN_COUNTER: { script_assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); m_nIp++; int16 counter = CTheScripts::Read2BytesFromScript(&m_nIp); CollectParameters(&m_nIp, 1); CUserDisplay::OnscnTimer.AddCounter(counter, ScriptParams[0], nil, 0); return 0; } case COMMAND_CLEAR_ONSCREEN_COUNTER: { script_assert(CTheScripts::ScriptSpace[m_nIp] == ARGUMENT_GLOBALVAR); m_nIp++; CUserDisplay::OnscnTimer.ClearCounter((uint16)CTheScripts::Read2BytesFromScript(&m_nIp)); return 0; } case COMMAND_SET_ZONE_CAR_INFO: { char label[12]; int16 gangDensities[NUM_GANGS] = { 0 }; int i; CTheScripts::ReadTextLabelFromScript(&m_nIp, label); m_nIp += KEY_LENGTH_IN_SCRIPT; CollectParameters(&m_nIp, 12); for (i = 0; i < NUM_GANGS; i++) gangDensities[i] = ScriptParams[i + 2]; int zone = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_INFO); for (int i = 0; i < NUM_GANGS; i++) { if (gangDensities[i] != 0 && CGangs::GetGangInfo(i)->m_nVehicleMI == -1) debug("SET_ZONE_CAR_INFO - Gang %d car ratio should be 0 in %s zone\n", i + 1, label); } if (zone < 0) { debug("Couldn't find zone - %s\n", label); return 0; } while (zone >= 0) { CTheZones::SetZoneCarInfo(zone, ScriptParams[0], ScriptParams[1], ScriptParams[11], gangDensities); zone = CTheZones::FindNextZoneByLabelAndReturnIndex(label, ZONE_INFO); } return 0; } //case COMMAND_IS_CHAR_IN_GANG_ZONE: case COMMAND_IS_CHAR_IN_ZONE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); char label[12]; CTheScripts::ReadTextLabelFromScript(&m_nIp, label); int zone = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_DEFAULT); if (zone != -1) m_nIp += KEY_LENGTH_IN_SCRIPT; CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); UpdateCompareFlag(CTheZones::PointLiesWithinZone(&pos, CTheZones::GetNavigationZone(zone))); return 0; } //case COMMAND_SET_CAR_DENSITY: //case COMMAND_SET_PED_DENSITY: case COMMAND_POINT_CAMERA_AT_PLAYER: { CollectParameters(&m_nIp, 3); // ScriptParams[0] is unused. TheCamera.TakeControl(nil, ScriptParams[1], ScriptParams[2], CAMCONTROL_SCRIPT); return 0; } case COMMAND_POINT_CAMERA_AT_CAR: { CollectParameters(&m_nIp, 3); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); if (pVehicle) TheCamera.TakeControl(pVehicle, ScriptParams[1], ScriptParams[2], CAMCONTROL_SCRIPT); return 0; } case COMMAND_POINT_CAMERA_AT_CHAR: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); if (pPed) TheCamera.TakeControl(pPed, ScriptParams[1], ScriptParams[2], CAMCONTROL_SCRIPT); return 0; } case COMMAND_RESTORE_CAMERA: TheCamera.Restore(); return 0; /* case COMMAND_SHAKE_PAD: CPad::GetPad(ScriptParams[0])->StartShake(ScriptParams[1], ScriptParams[2]); return 0; */ case COMMAND_SET_ZONE_PED_INFO: { char label[12]; CTheScripts::ReadTextLabelFromScript(&m_nIp, label); m_nIp += KEY_LENGTH_IN_SCRIPT; CollectParameters(&m_nIp, 12); int16 zone = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_INFO); if (zone < 0) { debug("Couldn't find zone - %s\n", label); return 0; } while (zone >= 0) { CTheZones::SetZonePedInfo(zone, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7], ScriptParams[8], ScriptParams[9], ScriptParams[10], ScriptParams[11]); zone = CTheZones::FindNextZoneByLabelAndReturnIndex(label, ZONE_INFO); } return 0; } case COMMAND_SET_TIME_SCALE: CollectParameters(&m_nIp, 1); CTimer::SetTimeScale(*(float*)&ScriptParams[0]); return 0; /* case COMMAND_IS_CAR_IN_AIR: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle && pVehicle->IsCar()); CAutomobile* pCar = (CAutomobile*)pVehicle; UpdateCompareFlag(pCar->GetAllWheelsOffGround()); return 0; } */ case COMMAND_SET_FIXED_CAMERA_POSITION: { CollectParameters(&m_nIp, 6); TheCamera.SetCamPositionForFixedMode( CVector(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2]), CVector(*(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5])); return 0; } case COMMAND_POINT_CAMERA_AT_POINT: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); TheCamera.TakeControlNoEntity(pos, ScriptParams[3], CAMCONTROL_SCRIPT); return 0; } case COMMAND_ADD_BLIP_FOR_CAR_OLD: { CollectParameters(&m_nIp, 3); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CRadar::SetEntityBlip(BLIP_CAR, ScriptParams[0], ScriptParams[1], (eBlipDisplay)ScriptParams[2]); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_BLIP_FOR_CHAR_OLD: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CRadar::SetEntityBlip(BLIP_CHAR, ScriptParams[0], ScriptParams[1], (eBlipDisplay)ScriptParams[2]); StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_ADD_BLIP_FOR_OBJECT_OLD: { CollectParameters(&m_nIp, 3); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CRadar::SetEntityBlip(BLIP_OBJECT, ScriptParams[0], ScriptParams[1], (eBlipDisplay)ScriptParams[2]); StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_REMOVE_BLIP: CollectParameters(&m_nIp, 1); CRadar::ClearBlip(ScriptParams[0]); return 0; case COMMAND_CHANGE_BLIP_COLOUR: CollectParameters(&m_nIp, 2); CRadar::ChangeBlipColour(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_DIM_BLIP: CollectParameters(&m_nIp, 2); CRadar::ChangeBlipBrightness(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_ADD_BLIP_FOR_COORD_OLD: { CollectParameters(&m_nIp, 5); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CRadar::SetCoordBlip(BLIP_COORD, pos, ScriptParams[3], (eBlipDisplay)ScriptParams[4]); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_CHANGE_BLIP_SCALE: CollectParameters(&m_nIp, 2); CRadar::ChangeBlipScale(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_SET_FADING_COLOUR: CollectParameters(&m_nIp, 3); TheCamera.SetFadeColour(ScriptParams[0], ScriptParams[1], ScriptParams[2]); return 0; case COMMAND_DO_FADE: CollectParameters(&m_nIp, 2); TheCamera.Fade(ScriptParams[0] / 1000.0f, ScriptParams[1]); return 0; case COMMAND_GET_FADING_STATUS: UpdateCompareFlag(TheCamera.GetFading()); return 0; case COMMAND_ADD_HOSPITAL_RESTART: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; float angle = *(float*)&ScriptParams[3]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRestart::AddHospitalRestartPoint(pos, angle); return 0; } case COMMAND_ADD_POLICE_RESTART: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; float angle = *(float*)&ScriptParams[3]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRestart::AddPoliceRestartPoint(pos, angle); return 0; } case COMMAND_OVERRIDE_NEXT_RESTART: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; float angle = *(float*)&ScriptParams[3]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRestart::OverrideNextRestart(pos, angle); return 0; } /* case COMMAND_DRAW_SHADOW: { CollectParameters(&m_nIp, 10); CVector pos = *(CVector*)&ScriptParams[1]; float angle = *(float*)&ScriptParams[4]; float length = *(float*)&ScriptParams[5]; float x, y; if (angle != 0.0f){ y = cos(angle) * length; x = sin(angle) * length; }else{ y = length; x = 0.0f; } float frontX = -x; float frontY = y; float sideX = y; float sideY = x; CShadows::StoreShadowToBeRendered(ScriptParams[0], &pos, frontX, frontY, sideX, sideY, ScriptParams[6], ScriptParams[7], ScriptParams[8], ScriptParams[9]); return 0; } */ case COMMAND_GET_PLAYER_HEADING: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; float angle = pPed->bInVehicle ? pPed->m_pMyVehicle->GetForward().Heading() : pPed->GetForward().Heading(); angle = RADTODEG(angle); if (angle < 0.0f) angle += 360.0f; if (angle > 360.0f) angle -= 360.0f; *(float*)&ScriptParams[0] = angle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_PLAYER_HEADING: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); if (pPed->bInVehicle) return 0; pPed->m_fRotationDest = pPed->m_fRotationCur = DEGTORAD(*(float*)&ScriptParams[1]); pPed->SetHeading(DEGTORAD(*(float*)&ScriptParams[1])); return 0; } case COMMAND_GET_CHAR_HEADING: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); float angle = pPed->bInVehicle ? pPed->m_pMyVehicle->GetForward().Heading() : pPed->GetForward().Heading(); angle = RADTODEG(angle); if (angle < 0.0f) angle += 360.0f; if (angle > 360.0f) angle -= 360.0f; *(float*)&ScriptParams[0] = angle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_CHAR_HEADING: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (pPed->bInVehicle) return 0; pPed->m_fRotationDest = pPed->m_fRotationCur = DEGTORAD(*(float*)&ScriptParams[1]); pPed->SetHeading(DEGTORAD(*(float*)&ScriptParams[1])); return 0; } case COMMAND_GET_CAR_HEADING: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); float angle = pVehicle->GetForward().Heading(); angle = RADTODEG(angle); if (angle < 0.0f) angle += 360.0f; if (angle > 360.0f) angle -= 360.0f; *(float*)&ScriptParams[0] = angle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_CAR_HEADING: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->SetHeading(DEGTORAD(*(float*)&ScriptParams[1])); return 0; } case COMMAND_GET_OBJECT_HEADING: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); float angle = pObject->GetForward().Heading(); angle = RADTODEG(angle); if (angle < 0.0f) angle += 360.0f; if (angle > 360.0f) angle -= 360.0f; *(float*)&ScriptParams[0] = angle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_OBJECT_HEADING: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CWorld::Remove(pObject); pObject->SetHeading(DEGTORAD(*(float*)&ScriptParams[1])); pObject->GetMatrix().UpdateRW(); pObject->UpdateRwFrame(); CWorld::Add(pObject); return 0; } /* case COMMAND_IS_PLAYER_TOUCHING_OBJECT: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); script_assert(pObject); CPhysical* pEntityToTest = pPed->bInVehicle ? (CPhysical*)pPed->m_pMyVehicle : pPed; UpdateCompareFlag(pEntityToTest->GetHasCollidedWith(pObject)); return 0; } case COMMAND_IS_CHAR_TOUCHING_OBJECT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); script_assert(pObject); CPhysical* pEntityToTest = pPed->bInVehicle ? (CPhysical*)pPed->m_pMyVehicle : pPed; UpdateCompareFlag(pEntityToTest->GetHasCollidedWith(pObject)); return 0; } */ case COMMAND_SET_PLAYER_AMMO: { CollectParameters(&m_nIp, 3); CWorld::Players[0].m_pPed->SetAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); return 0; } /* case COMMAND_SET_CHAR_AMMO: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); pPed->SetAmmo((eWeaponType)ScriptParams[1], ScriptParams[2]); return 0; } */ //case COMMAND_SET_CAR_AMMO: //case COMMAND_LOAD_CAMERA_SPLINE: //case COMMAND_MOVE_CAMERA_ALONG_SPLINE: //case COMMAND_GET_CAMERA_POSITION_ALONG_SPLINE: case COMMAND_DECLARE_MISSION_FLAG: CTheScripts::OnAMissionFlag = (uint16)CTheScripts::Read2BytesFromScript(&++m_nIp); return 0; case COMMAND_DECLARE_MISSION_FLAG_FOR_CONTACT: return 0; //case COMMAND_DECLARE_BASE_BRIEF_ID_FOR_CONTACT: case COMMAND_IS_PLAYER_HEALTH_GREATER: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; UpdateCompareFlag(pPed->m_fHealth > ScriptParams[1]); return 0; } case COMMAND_IS_CHAR_HEALTH_GREATER: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->m_fHealth > ScriptParams[1]); return 0; } case COMMAND_IS_CAR_HEALTH_GREATER: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->m_fHealth > ScriptParams[1]); return 0; } case COMMAND_ADD_BLIP_FOR_CAR: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int handle = CRadar::SetEntityBlip(BLIP_CAR, ScriptParams[0], 0, BLIP_DISPLAY_BOTH); CRadar::ChangeBlipScale(handle, 3); ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_BLIP_FOR_CHAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int handle = CRadar::SetEntityBlip(BLIP_CHAR, ScriptParams[0], 1, BLIP_DISPLAY_BOTH); CRadar::ChangeBlipScale(handle, 3); ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_BLIP_FOR_OBJECT: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int handle = CRadar::SetEntityBlip(BLIP_OBJECT, ScriptParams[0], 6, BLIP_DISPLAY_BOTH); CRadar::ChangeBlipScale(handle, 3); ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_BLIP_FOR_CONTACT_POINT: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int handle = CRadar::SetCoordBlip(BLIP_CONTACT_POINT, pos, 2, BLIP_DISPLAY_BOTH); CRadar::ChangeBlipScale(handle, 3); ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_BLIP_FOR_COORD: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int handle = CRadar::SetCoordBlip(BLIP_COORD, pos, 5, BLIP_DISPLAY_BOTH); CRadar::ChangeBlipScale(handle, 3); ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_CHANGE_BLIP_DISPLAY: CollectParameters(&m_nIp, 2); CRadar::ChangeBlipDisplay(ScriptParams[0], (eBlipDisplay)ScriptParams[1]); return 0; case COMMAND_ADD_ONE_OFF_SOUND: { CollectParameters(&m_nIp, 4); switch (ScriptParams[3]) { case SCRIPT_SOUND_PART_MISSION_COMPLETE: DMAudio.PlayFrontEndSound(SOUND_PART_MISSION_COMPLETE, 0); return 0; case SCRIPT_SOUND_RACE_START_3: DMAudio.PlayFrontEndSound(SOUND_RACE_START_3, 0); return 0; case SCRIPT_SOUND_RACE_START_2: DMAudio.PlayFrontEndSound(SOUND_RACE_START_2, 0); return 0; case SCRIPT_SOUND_RACE_START_1: DMAudio.PlayFrontEndSound(SOUND_RACE_START_1, 0); return 0; case SCRIPT_SOUND_RACE_START_GO: DMAudio.PlayFrontEndSound(SOUND_RACE_START_GO, 0); return 0; case SCRIPT_SOUND_AMMUNATION_BUY_WEAPON: DMAudio.PlayFrontEndSound(SOUND_PICKUP_WEAPON_BOUGHT, 0); return 0; case SCRIPT_SOUND_AMMUNATION_BUY_WEAPON_DENIED: DMAudio.PlayFrontEndSound(SOUND_GARAGE_NO_MONEY, 0); return 0; case SCRIPT_SOUND_IMRAN_ARM_BOMB: DMAudio.PlayFrontEndSound(SOUND_AMMUNATION_IMRAN_ARM_BOMB, 0); return 0; default: break; } if (!DMAudio.IsAudioInitialised()) return 0; cAudioScriptObject* obj = new cAudioScriptObject(); obj->Posn = *(CVector*)&ScriptParams[0]; obj->AudioId = ScriptParams[3]; obj->AudioEntity = AEHANDLE_NONE; DMAudio.CreateOneShotScriptObject(obj); return 0; } case COMMAND_ADD_CONTINUOUS_SOUND: { CollectParameters(&m_nIp, 4); if (DMAudio.IsAudioInitialised()) { cAudioScriptObject* obj = new cAudioScriptObject(); obj->Posn = *(CVector*)&ScriptParams[0]; obj->AudioId = ScriptParams[3]; obj->AudioEntity = DMAudio.CreateLoopingScriptObject(obj); ScriptParams[0] = CPools::GetAudioScriptObjectPool()->GetIndex(obj); } else ScriptParams[0] = -1; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_REMOVE_SOUND: { CollectParameters(&m_nIp, 1); cAudioScriptObject* obj = CPools::GetAudioScriptObjectPool()->GetAt(ScriptParams[0]); if (!obj){ debug("REMOVE_SOUND - Sound doesn't exist\n"); return 0; } DMAudio.DestroyLoopingScriptObject(obj->AudioEntity); delete obj; return 0; } case COMMAND_IS_CAR_STUCK_ON_ROOF: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(CTheScripts::UpsideDownCars.HasCarBeenUpsideDownForAWhile(ScriptParams[0])); return 0; } default: script_assert(0); } return -1; } int8 CRunningScript::ProcessCommands400To499(int32 command) { switch (command) { case COMMAND_ADD_UPSIDEDOWN_CAR_CHECK: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CTheScripts::UpsideDownCars.AddCarToCheck(ScriptParams[0]); return 0; } case COMMAND_REMOVE_UPSIDEDOWN_CAR_CHECK: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); CTheScripts::UpsideDownCars.RemoveCarFromCheck(ScriptParams[0]); return 0; } case COMMAND_SET_CHAR_OBJ_WAIT_ON_FOOT: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_WAIT_ON_FOOT); return 0; } case COMMAND_SET_CHAR_OBJ_FLEE_ON_FOOT_TILL_SAFE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE); return 0; } case COMMAND_SET_CHAR_OBJ_GUARD_SPOT: { CollectParameters(&m_nIp, 4); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_GUARD_SPOT, pos); return 0; } /* case COMMAND_SET_CHAR_OBJ_GUARD_AREA: { CollectParameters(&m_nIp, 5); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); float infX = *(float*)&ScriptParams[1]; float infY = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; if (infX > supX){ infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[1]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[2]; } CVector pos; pos.x = (infX + supX) / 2; pos.y = (infY + supY) / 2; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = Max(pos.x - infX, pos.y - infY); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_GUARD_SPOT, pos, radius); return 0; } case COMMAND_SET_CHAR_OBJ_WAIT_IN_CAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_WAIT_IN_CAR); return 0; } */ case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_2D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D: case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D: PlayerInAreaCheckCommand(command, &m_nIp); return 0; case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_2D: case COMMAND_IS_CHAR_IN_AREA_IN_CAR_2D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_2D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D: case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D: case COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D: CharInAreaCheckCommand(command, &m_nIp); return 0; case COMMAND_IS_CAR_STOPPED_IN_AREA_2D: case COMMAND_IS_CAR_STOPPED_IN_AREA_3D: CarInAreaCheckCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_CAR_2D: case COMMAND_LOCATE_STOPPED_CAR_2D: case COMMAND_LOCATE_CAR_3D: case COMMAND_LOCATE_STOPPED_CAR_3D: LocateCarCommand(command, &m_nIp); return 0; case COMMAND_GIVE_WEAPON_TO_PLAYER: { CollectParameters(&m_nIp, 3); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); pPed->m_nSelectedWepSlot = pPed->GiveWeapon((eWeaponType)ScriptParams[1], ScriptParams[2]); return 0; } case COMMAND_GIVE_WEAPON_TO_CHAR: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->SetCurrentWeapon(pPed->GiveWeapon((eWeaponType)ScriptParams[1], ScriptParams[2])); if (pPed->bInVehicle && pPed->m_pMyVehicle) pPed->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(pPed->m_weapons[pPed->m_currentWeapon].m_eWeaponType)->m_nModelId); return 0; } //case COMMAND_GIVE_WEAPON_TO_CAR: case COMMAND_SET_PLAYER_CONTROL: { CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; if (ScriptParams[1]){ pPlayer->MakePlayerSafe(false); if (strcmp(m_abScriptName, "serg1") == 0) // Four Iron pPlayer->m_pPed->ClearFollowPath(); }else{ pPlayer->MakePlayerSafe(true); } return 0; } case COMMAND_FORCE_WEATHER: CollectParameters(&m_nIp, 1); CWeather::ForceWeather(ScriptParams[0]); return 0; case COMMAND_FORCE_WEATHER_NOW: CollectParameters(&m_nIp, 1); CWeather::ForceWeatherNow(ScriptParams[0]); return 0; case COMMAND_RELEASE_WEATHER: CWeather::ReleaseWeather(); return 0; case COMMAND_SET_CURRENT_PLAYER_WEAPON: { CollectParameters(&m_nIp, 2); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++){ if (pPed->m_weapons[i].m_eWeaponType == ScriptParams[1]) pPed->m_nSelectedWepSlot = i; } return 0; } case COMMAND_SET_CURRENT_CHAR_WEAPON: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { if (pPed->m_weapons[i].m_eWeaponType == ScriptParams[1]) pPed->SetCurrentWeapon(i); } return 0; } //case COMMAND_SET_CURRENT_CAR_WEAPON: case COMMAND_GET_OBJECT_COORDINATES: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); *(CVector*)&ScriptParams[0] = pObject->GetPosition(); StoreParameters(&m_nIp, 3); return 0; } case COMMAND_SET_OBJECT_COORDINATES: { CollectParameters(&m_nIp, 4); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pObject->Teleport(pos); CTheScripts::ClearSpaceForMissionEntity(pos, pObject); return 0; } case COMMAND_GET_GAME_TIMER: ScriptParams[0] = CTimer::GetTimeInMilliseconds(); StoreParameters(&m_nIp, 1); return 0; case COMMAND_TURN_CHAR_TO_FACE_COORD: { CollectParameters(&m_nIp, 4); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle; CVector pos; if (pPed->bInVehicle) pVehicle = pPed->m_pMyVehicle; else pVehicle = nil; if (pVehicle) pos = pVehicle->GetPosition(); else pos = pPed->GetPosition(); float heading = CGeneral::GetATanOfXY(pos.x - *(float*)&ScriptParams[1], pos.y - *(float*)&ScriptParams[2]); heading += HALFPI; if (heading > TWOPI) heading -= TWOPI; if (!pVehicle){ pPed->m_fRotationCur = heading; pPed->m_fRotationDest = heading; pPed->SetHeading(heading); } return 0; } case COMMAND_TURN_PLAYER_TO_FACE_COORD: { CollectParameters(&m_nIp, 4); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); CVehicle* pVehicle; CVector pos; if (pPed->bInVehicle) pVehicle = pPed->m_pMyVehicle; else pVehicle = nil; if (pVehicle) pos = pVehicle->GetPosition(); else pos = pPed->GetPosition(); float heading = CGeneral::GetATanOfXY(pos.x - *(float*)&ScriptParams[1], pos.y - *(float*)&ScriptParams[2]); heading += HALFPI; if (heading > TWOPI) heading -= TWOPI; if (!pVehicle) { pPed->m_fRotationCur = heading; pPed->m_fRotationDest = heading; pPed->SetHeading(heading); } return 0; } case COMMAND_STORE_WANTED_LEVEL: { CollectParameters(&m_nIp, 1); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); ScriptParams[0] = pPed->m_pWanted->GetWantedLevel(); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_IS_CAR_STOPPED: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(CTheScripts::IsVehicleStopped(pVehicle)); return 0; } case COMMAND_MARK_CHAR_AS_NO_LONGER_NEEDED: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CTheScripts::CleanUpThisPed(pPed); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_MARK_CAR_AS_NO_LONGER_NEEDED: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); CTheScripts::CleanUpThisVehicle(pVehicle); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CAR); return 0; } case COMMAND_MARK_OBJECT_AS_NO_LONGER_NEEDED: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); CTheScripts::CleanUpThisObject(pObject); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_OBJECT); return 0; } case COMMAND_DONT_REMOVE_CHAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_DONT_REMOVE_CAR: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CAR); return 0; } case COMMAND_DONT_REMOVE_OBJECT: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_OBJECT); return 0; } case COMMAND_CREATE_CHAR_AS_PASSENGER: { CollectParameters(&m_nIp, 4); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); switch (ScriptParams[2]) { case MI_COP: if (ScriptParams[1] == PEDTYPE_COP) ScriptParams[2] = COP_STREET; break; case MI_SWAT: if (ScriptParams[1] == PEDTYPE_COP) ScriptParams[2] = COP_SWAT; break; case MI_FBI: if (ScriptParams[1] == PEDTYPE_COP) ScriptParams[2] = COP_FBI; break; case MI_ARMY: if (ScriptParams[1] == PEDTYPE_COP) ScriptParams[2] = COP_ARMY; break; case MI_MEDIC: if (ScriptParams[1] == PEDTYPE_EMERGENCY) ScriptParams[2] = PEDTYPE_EMERGENCY; break; case MI_FIREMAN: if (ScriptParams[1] == PEDTYPE_FIREMAN) ScriptParams[2] = PEDTYPE_FIREMAN; break; default: break; } CPed* pPed; if (ScriptParams[1] == PEDTYPE_COP) pPed = new CCopPed((eCopType)ScriptParams[2]); else if (ScriptParams[1] == PEDTYPE_EMERGENCY || ScriptParams[1] == PEDTYPE_FIREMAN) pPed = new CEmergencyPed(ScriptParams[2]); else pPed = new CCivilianPed((ePedType)ScriptParams[1], ScriptParams[2]); pPed->CharCreatedBy = MISSION_CHAR; pPed->bRespondsToThreats = false; pPed->bAllowMedicsToReviveMe = false; pPed->bIsPlayerFriend = false; if (pVehicle->bIsBus) pPed->bRenderPedInCar = false; pPed->SetPosition(pVehicle->GetPosition()); pPed->SetOrientation(0.0f, 0.0f, 0.0f); CPopulation::ms_nTotalMissionPeds++; CWorld::Add(pPed); if (ScriptParams[3] >= 0) pVehicle->AddPassenger(pPed, ScriptParams[3]); else pVehicle->AddPassenger(pPed); pPed->m_pMyVehicle = pVehicle; pPed->m_pMyVehicle->RegisterReference((CEntity**)&pPed->m_pMyVehicle); pPed->bInVehicle = true; pPed->SetPedState(PED_DRIVING); pPed->bUsesCollision = false; pPed->AddInCarAnims(pVehicle, false); pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pPed->GetPosition()); ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_SET_CHAR_OBJ_KILL_CHAR_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, pTarget); return 0; } case COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, pTarget); return 0; } case COMMAND_SET_CHAR_OBJ_KILL_CHAR_ANY_MEANS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_KILL_CHAR_ANY_MEANS, pTarget); return 0; } case COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ANY_MEANS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_KILL_CHAR_ANY_MEANS, pTarget); return 0; } /* case COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, pTarget); return 0; } */ case COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, pTarget); return 0; } case COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pTarget); return 0; } case COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pTarget); return 0; } case COMMAND_SET_CHAR_OBJ_GOTO_CHAR_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT, pTarget); return 0; } case COMMAND_SET_CHAR_OBJ_GOTO_PLAYER_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT, pTarget); return 0; } case COMMAND_SET_CHAR_OBJ_LEAVE_CAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_LEAVE_CAR, pVehicle); return 0; } case COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_PASSENGER: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, pVehicle); return 0; } case COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_DRIVER: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicle); return 0; } //case COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_IN_CAR: //case COMMAND_SET_CHAR_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE: case COMMAND_SET_CHAR_OBJ_DESTROY_OBJECT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_DESTROY_OBJECT, pObject); return 0; } case COMMAND_SET_CHAR_OBJ_DESTROY_CAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_DESTROY_CAR, pVehicle); return 0; } /* case COMMAND_SET_CHAR_OBJ_GOTO_AREA_ON_FOOT: { CollectParameters(&m_nIp, 5); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); float infX = *(float*)&ScriptParams[1]; float infY = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[1]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[2]; } CVector pos; pos.x = (infX + supX) / 2; pos.y = (infY + supY) / 2; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = Max(pos.x - infX, pos.y - infY); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, pos, radius); return 0; } */ //case COMMAND_SET_CHAR_OBJ_GOTO_AREA_IN_CAR: //case COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET: //case COMMAND_SET_CHAR_OBJ_GUARD_ATTACK: case COMMAND_SET_CHAR_AS_LEADER: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); pPed->SetObjective(OBJECTIVE_SET_LEADER, pTarget); return 0; } case COMMAND_SET_PLAYER_AS_LEADER: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CWorld::Players[ScriptParams[1]].m_pPed; pPed->SetObjective(OBJECTIVE_SET_LEADER, pTarget); return 0; } case COMMAND_LEAVE_GROUP: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->ClearLeader(); return 0; } case COMMAND_SET_CHAR_OBJ_FOLLOW_ROUTE: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_FOLLOW_ROUTE, ScriptParams[1], ScriptParams[2]); return 0; } case COMMAND_ADD_ROUTE_POINT: { CollectParameters(&m_nIp, 4); CRouteNode::AddRoutePoint(ScriptParams[0], *(CVector*)&ScriptParams[1]); return 0; } case COMMAND_PRINT_WITH_NUMBER_BIG: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 3); CMessages::AddBigMessageWithNumber(text, ScriptParams[1], ScriptParams[2] - 1, ScriptParams[0], -1, -1, -1, -1, -1); return 0; } case COMMAND_PRINT_WITH_NUMBER: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 3); CMessages::AddMessageWithNumber(text, ScriptParams[1], ScriptParams[2], ScriptParams[0], -1, -1, -1, -1, -1); return 0; } case COMMAND_PRINT_WITH_NUMBER_NOW: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 3); CMessages::AddMessageJumpQWithNumber(text, ScriptParams[1], ScriptParams[2], ScriptParams[0], -1, -1, -1, -1, -1); return 0; } //case COMMAND_PRINT_WITH_NUMBER_SOON: case COMMAND_SWITCH_ROADS_ON: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX){ infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY){ infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ){ infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } ThePaths.SwitchRoadsOffInArea(infX, supX, infY, supY, infZ, supZ, false); return 0; } case COMMAND_SWITCH_ROADS_OFF: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } ThePaths.SwitchRoadsOffInArea(infX, supX, infY, supY, infZ, supZ, true); return 0; } case COMMAND_GET_NUMBER_OF_PASSENGERS: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); ScriptParams[0] = pVehicle->m_nNumPassengers; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_MAXIMUM_NUMBER_OF_PASSENGERS: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); ScriptParams[0] = pVehicle->m_nNumMaxPassengers; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_CAR_DENSITY_MULTIPLIER: { CollectParameters(&m_nIp, 1); CCarCtrl::CarDensityMultiplier = *(float*)&ScriptParams[0]; return 0; } case COMMAND_SET_CAR_HEAVY: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); if (ScriptParams[1] != 0) { pVehicle->bIsHeavy = true; pVehicle->m_fMass = 3.0f * pVehicle->pHandling->fMass; pVehicle->m_fTurnMass = 5.0f * pVehicle->pHandling->fTurnMass; } else { pVehicle->bIsHeavy = false; pVehicle->m_fMass = pVehicle->pHandling->fMass; pVehicle->m_fTurnMass = pVehicle->pHandling->fTurnMass; } return 0; } case COMMAND_CLEAR_CHAR_THREAT_SEARCH: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->m_fearFlags = 0; return 0; } /* case COMMAND_ACTIVATE_CRANE: { CollectParameters(&m_nIp, 10); float infX = *(float*)&ScriptParams[2]; float infY = *(float*)&ScriptParams[3]; float supX = *(float*)&ScriptParams[4]; float supY = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[4]; supX = *(float*)&ScriptParams[2]; } if (infY > supY) { infY = *(float*)&ScriptParams[5]; supY = *(float*)&ScriptParams[3]; } CCranes::ActivateCrane(infX, supX, infY, supY, *(float*)&ScriptParams[6], *(float*)&ScriptParams[7], *(float*)&ScriptParams[8], DEGTORAD(*(float*)&ScriptParams[9]), false, false, *(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); return 0; } case COMMAND_DEACTIVATE_CRANE: { CollectParameters(&m_nIp, 2); CCranes::DeActivateCrane(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); return 0; } */ case COMMAND_SET_MAX_WANTED_LEVEL: { CollectParameters(&m_nIp, 1); CWanted::SetMaximumWantedLevel(ScriptParams[0]); return 0; } //case COMMAND_SAVE_VAR_INT: //case COMMAND_SAVE_VAR_FLOAT: case COMMAND_IS_CAR_IN_AIR_PROPER: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->m_nCollisionRecords == 0); return 0; } default: script_assert(0); } return -1; } ================================================ FILE: src/control/Script3.cpp ================================================ #include "common.h" #include "Script.h" #include "ScriptCommands.h" #include "Boat.h" #include "CarCtrl.h" #include "Clock.h" #include "Coronas.h" #include "Cranes.h" #include "CutsceneMgr.h" #include "Darkel.h" #include "Explosion.h" #include "Fire.h" #include "General.h" #include "Garages.h" #include "Heli.h" #include "Messages.h" #include "Pad.h" #include "ParticleObject.h" #include "Phones.h" #include "Pickups.h" #include "PointLights.h" #include "Population.h" #include "Pools.h" #include "ProjectileInfo.h" #include "Radar.h" #include "Restart.h" #include "Stats.h" #include "Streaming.h" #include "User.h" #include "WaterLevel.h" #include "Weather.h" #include "Zones.h" #include "GameLogic.h" #include "Bike.h" #include "Wanted.h" int8 CRunningScript::ProcessCommands500To599(int32 command) { switch (command) { case COMMAND_IS_CAR_UPSIDEDOWN: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->GetUp().z <= -0.97f); return 0; } case COMMAND_GET_PLAYER_CHAR: { CollectParameters(&m_nIp, 1); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_CANCEL_OVERRIDE_RESTART: CRestart::CancelOverrideRestart(); return 0; case COMMAND_SET_POLICE_IGNORE_PLAYER: { CollectParameters(&m_nIp, 2); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); if (ScriptParams[1]) { pPed->m_pWanted->m_bIgnoredByCops = true; CWorld::StopAllLawEnforcersInTheirTracks(); } else { pPed->m_pWanted->m_bIgnoredByCops = false; } return 0; } /* case COMMAND_ADD_PAGER_MESSAGE_WITH_NUMBER: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 4); CUserDisplay::Pager.AddMessageWithNumber(text, ScriptParams[0], -1, -1, -1, -1, -1, ScriptParams[1], ScriptParams[2], ScriptParams[3]); return 0; } */ case COMMAND_START_KILL_FRENZY: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 8); CDarkel::StartFrenzy((eWeaponType)ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], text, ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7] != 0, false); return 0; } case COMMAND_READ_KILL_FRENZY_STATUS: { ScriptParams[0] = CDarkel::ReadStatus(); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SQRT: { CollectParameters(&m_nIp, 1); *(float*)&ScriptParams[0] = Sqrt(*(float*)&ScriptParams[0]); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_2D: case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_2D: case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_2D: case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D: case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D: case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D: LocatePlayerCarCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_2D: case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_2D: case COMMAND_LOCATE_CHAR_IN_CAR_CAR_2D: case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D: case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D: case COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D: LocateCharCarCommand(command, &m_nIp); return 0; case COMMAND_GENERATE_RANDOM_FLOAT_IN_RANGE: CollectParameters(&m_nIp, 2); *(float*)&ScriptParams[0] = CGeneral::GetRandomNumberInRange(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); StoreParameters(&m_nIp, 1); return 0; case COMMAND_GENERATE_RANDOM_INT_IN_RANGE: CollectParameters(&m_nIp, 2); ScriptParams[0] = CGeneral::GetRandomNumberInRange(ScriptParams[0], ScriptParams[1]); StoreParameters(&m_nIp, 1); return 0; case COMMAND_LOCK_CAR_DOORS: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->m_nDoorLock = (eCarLock)ScriptParams[1]; return 0; } case COMMAND_EXPLODE_CAR: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->BlowUpCar(nil); return 0; } case COMMAND_ADD_EXPLOSION: CollectParameters(&m_nIp, 4); CExplosion::AddExplosion(nil, nil, (eExplosionType)ScriptParams[3], *(CVector*)&ScriptParams[0], 0, true); return 0; case COMMAND_IS_CAR_UPRIGHT: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->GetUp().z >= 0.0f); return 0; } case COMMAND_TURN_CHAR_TO_FACE_CHAR: { CollectParameters(&m_nIp, 2); CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); CVehicle* pVehicle = pSourcePed->bInVehicle ? pSourcePed->m_pMyVehicle : nil; CVector2D sourcePos = pSourcePed->bInVehicle ? pVehicle->GetPosition() : pSourcePed->GetPosition(); CVector2D targetPos = pTargetPed->bInVehicle ? pTargetPed->m_pMyVehicle->GetPosition() : pTargetPed->GetPosition(); float angle = CGeneral::GetATanOfXY(sourcePos.x - targetPos.x, sourcePos.y - targetPos.y) + HALFPI; if (angle > TWOPI) angle -= TWOPI; if (!pVehicle) { pSourcePed->m_fRotationCur = angle; pSourcePed->m_fRotationDest = angle; pSourcePed->SetHeading(angle); } return 0; } case COMMAND_TURN_CHAR_TO_FACE_PLAYER: { CollectParameters(&m_nIp, 2); CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CPed* pTargetPed = CWorld::Players[ScriptParams[1]].m_pPed; CVehicle* pVehicle = pSourcePed->bInVehicle ? pSourcePed->m_pMyVehicle : nil; CVector2D sourcePos = pSourcePed->bInVehicle ? pVehicle->GetPosition() : pSourcePed->GetPosition(); CVector2D targetPos = pTargetPed->bInVehicle ? pTargetPed->m_pMyVehicle->GetPosition() : pTargetPed->GetPosition(); float angle = CGeneral::GetATanOfXY(sourcePos.x - targetPos.x, sourcePos.y - targetPos.y) + HALFPI; if (angle > TWOPI) angle -= TWOPI; if (!pVehicle) { pSourcePed->m_fRotationCur = angle; pSourcePed->m_fRotationDest = angle; pSourcePed->SetHeading(angle); } return 0; } case COMMAND_TURN_PLAYER_TO_FACE_CHAR: { CollectParameters(&m_nIp, 2); CPed* pSourcePed = CWorld::Players[ScriptParams[0]].m_pPed; CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); CVehicle* pVehicle = pSourcePed->bInVehicle ? pSourcePed->m_pMyVehicle : nil; CVector2D sourcePos = pSourcePed->bInVehicle ? pVehicle->GetPosition() : pSourcePed->GetPosition(); CVector2D targetPos = pTargetPed->bInVehicle ? pTargetPed->m_pMyVehicle->GetPosition() : pTargetPed->GetPosition(); float angle = CGeneral::GetATanOfXY(sourcePos.x - targetPos.x, sourcePos.y - targetPos.y) + HALFPI; if (angle > TWOPI) angle -= TWOPI; if (!pVehicle) { pSourcePed->m_fRotationCur = angle; pSourcePed->m_fRotationDest = angle; pSourcePed->SetHeading(angle); } return 0; } case COMMAND_SET_CHAR_OBJ_GOTO_COORD_ON_FOOT: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVector target; target.x = *(float*)&ScriptParams[1]; target.y = *(float*)&ScriptParams[2]; target.z = CWorld::FindGroundZForCoord(target.x, target.y); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, target); return 0; } //case COMMAND_SET_CHAR_OBJ_GOTO_COORD_IN_CAR: case COMMAND_CREATE_PICKUP: { CollectParameters(&m_nIp, 5); int16 model = ScriptParams[0]; if (model < 0) model = CTheScripts::UsedObjectArray[-model].index; CVector pos = *(CVector*)&ScriptParams[2]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CPickups::GenerateNewOne(pos, model, ScriptParams[1], 0); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_HAS_PICKUP_BEEN_COLLECTED: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CPickups::IsPickUpPickedUp(ScriptParams[0]) != 0); return 0; case COMMAND_REMOVE_PICKUP: CollectParameters(&m_nIp, 1); CPickups::RemovePickUp(ScriptParams[0]); return 0; case COMMAND_SET_TAXI_LIGHTS: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); ((CAutomobile*)pVehicle)->SetTaxiLight(ScriptParams[1] != 0); return 0; } case COMMAND_PRINT_BIG_Q: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 2); CMessages::AddBigMessageQ(text, ScriptParams[0], ScriptParams[1] - 1); return 0; } /* case COMMAND_PRINT_WITH_NUMBER_BIG_Q: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 3); CMessages::AddBigMessageWithNumberQ(text, ScriptParams[1], ScriptParams[2] - 1, ScriptParams[0], -1, -1, -1, -1, -1); return 0; } */ case COMMAND_SET_GARAGE: { CollectParameters(&m_nIp, 9); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float X2 = *(float*)&ScriptParams[3]; float Y2 = *(float*)&ScriptParams[4]; float supX = *(float*)&ScriptParams[5]; float supY = *(float*)&ScriptParams[6]; float supZ = *(float*)&ScriptParams[7]; ScriptParams[0] = CGarages::AddOne(infX, infY, infZ, X2, Y2, supX, supY, supZ, ScriptParams[8], 0); StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_SET_GARAGE_WITH_CAR_MODEL: { CollectParameters(&m_nIp, 10); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float X2 = *(float*)&ScriptParams[3]; float Y2 = *(float*)&ScriptParams[4]; float supX = *(float*)&ScriptParams[5]; float supY = *(float*)&ScriptParams[6]; float supZ = *(float*)&ScriptParams[7]; ScriptParams[0] = CGarages::AddOne(infX, infY, infZ, X2, Y2, supX, supY, supZ, ScriptParams[8], ScriptParams[9]); StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_SET_TARGET_CAR_FOR_MISSION_GARAGE: { CollectParameters(&m_nIp, 2); CVehicle* pTarget; if (ScriptParams[1] >= 0) { pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pTarget); } else { pTarget = nil; } CGarages::SetTargetCarForMissonGarage(ScriptParams[0], pTarget); return 0; } case COMMAND_IS_CAR_IN_MISSION_GARAGE: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CGarages::HasCarBeenDroppedOffYet(ScriptParams[0])); return 0; /* case COMMAND_SET_FREE_BOMBS: CollectParameters(&m_nIp, 1); CGarages::SetFreeBombs(ScriptParams[0] != 0); return 0; case COMMAND_SET_POWERPOINT: { CollectParameters(&m_nIp, 7); float f1 = *(float*)&ScriptParams[0]; float f2 = *(float*)&ScriptParams[1]; float f3 = *(float*)&ScriptParams[2]; float f4 = *(float*)&ScriptParams[3]; float f5 = *(float*)&ScriptParams[4]; float f6 = *(float*)&ScriptParams[5]; float temp; if (f1 > f4) { temp = f1; f1 = f4; f4 = temp; } if (f2 > f5) { temp = f2; f2 = f5; f5 = temp; } if (f3 > f6) { temp = f3; f3 = f6; f6 = temp; } CPowerPoints::GenerateNewOne(f1, f2, f3, f4, f5, f6, *(uint8*)&ScriptParams[6]); return 0; } case COMMAND_SET_ALL_TAXI_LIGHTS: CollectParameters(&m_nIp, 1); CAutomobile::SetAllTaxiLights(ScriptParams[0] != 0); return 0; case COMMAND_IS_CAR_ARMED_WITH_ANY_BOMB: { CollectParameters(&m_nIp, 1); CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pCar); script_assert(pCar->m_vehType == VEHICLE_TYPE_CAR); UpdateCompareFlag(pCar->m_bombType != 0); //TODO: enum return 0; } */ case COMMAND_APPLY_BRAKES_TO_PLAYERS_CAR: CollectParameters(&m_nIp, 2); CPad::GetPad(ScriptParams[0])->bApplyBrakes = (ScriptParams[1] != 0); return 0; case COMMAND_SET_PLAYER_HEALTH: { CollectParameters(&m_nIp, 2); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); pPed->m_fHealth = Min(ScriptParams[1], CWorld::Players[ScriptParams[0]].m_nMaxHealth); return 0; } case COMMAND_SET_CHAR_HEALTH: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (ScriptParams[1]) { pPed->m_fHealth = ScriptParams[1]; } else if (pPed->bInVehicle) { pPed->SetDead(); if (!pPed->IsPlayer()) pPed->FlagToDestroyWhenNextProcessed(); } else { pPed->SetDie(); } return 0; } case COMMAND_SET_CAR_HEALTH: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->m_fHealth = ScriptParams[1]; return 0; } case COMMAND_GET_PLAYER_HEALTH: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); ScriptParams[0] = pPed->m_fHealth; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_CHAR_HEALTH: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); ScriptParams[0] = pPed->m_fHealth; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_CAR_HEALTH: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); ScriptParams[0] = pVehicle->m_fHealth; StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_IS_CAR_ARMED_WITH_BOMB: { CollectParameters(&m_nIp, 2); CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pCar); script_assert(pCar->m_vehType == VEHICLE_TYPE_CAR); UpdateCompareFlag(pCar->m_bombType == ScriptParams[1]); return 0; } */ case COMMAND_CHANGE_CAR_COLOUR: { CollectParameters(&m_nIp, 3); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); if (ScriptParams[1] >= 256 || ScriptParams[2] >= 256) debug("CHANGE_CAR_COLOUR - Colours must be less than %d", 256); pVehicle->m_currentColour1 = ScriptParams[1]; pVehicle->m_currentColour2 = ScriptParams[2]; return 0; } case COMMAND_SWITCH_PED_ROADS_ON: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } ThePaths.SwitchPedRoadsOffInArea(infX, supX, infY, supY, infZ, supZ, false); return 0; } case COMMAND_SWITCH_PED_ROADS_OFF: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } ThePaths.SwitchPedRoadsOffInArea(infX, supX, infY, supY, infZ, supZ, true); return 0; } case COMMAND_CHAR_LOOK_AT_CHAR_ALWAYS: { CollectParameters(&m_nIp, 2); CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pSourcePed); CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pTargetPed); pSourcePed->SetLookFlag(pTargetPed, true); pSourcePed->SetLookTimer(60000); return 0; } case COMMAND_CHAR_LOOK_AT_PLAYER_ALWAYS: { CollectParameters(&m_nIp, 2); CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pSourcePed); CPed* pTargetPed = CWorld::Players[ScriptParams[1]].m_pPed; script_assert(pTargetPed); pSourcePed->SetLookFlag(pTargetPed, true); pSourcePed->SetLookTimer(60000); return 0; } case COMMAND_PLAYER_LOOK_AT_CHAR_ALWAYS: { CollectParameters(&m_nIp, 2); CPed* pSourcePed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pSourcePed); CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pTargetPed); pSourcePed->SetLookFlag(pTargetPed, true); pSourcePed->SetLookTimer(60000); return 0; } case COMMAND_STOP_CHAR_LOOKING: { CollectParameters(&m_nIp, 1); CPed* pSourcePed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pSourcePed); pSourcePed->ClearLookFlag(); pSourcePed->bKeepTryingToLook = false; if (pSourcePed->GetPedState() == PED_LOOK_HEADING || pSourcePed->GetPedState() == PED_LOOK_ENTITY) pSourcePed->RestorePreviousState(); return 0; } case COMMAND_STOP_PLAYER_LOOKING: { CollectParameters(&m_nIp, 1); CPed* pSourcePed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pSourcePed); pSourcePed->ClearLookFlag(); pSourcePed->bKeepTryingToLook = false; if (pSourcePed->GetPedState() == PED_LOOK_HEADING || pSourcePed->GetPedState() == PED_LOOK_ENTITY) pSourcePed->RestorePreviousState(); return 0; } /* case COMMAND_SWITCH_HELICOPTER: CollectParameters(&m_nIp, 1); CHeli::ActivateHeli(ScriptParams[0] != 0); return 0; */ //case COMMAND_SET_GANG_ATTITUDE: //case COMMAND_SET_GANG_GANG_ATTITUDE: //case COMMAND_SET_GANG_PLAYER_ATTITUDE: case COMMAND_SET_GANG_PED_MODELS: CollectParameters(&m_nIp, 3); CGangs::SetGangPedModels(ScriptParams[0], ScriptParams[1], ScriptParams[2]); return 0; case COMMAND_SET_GANG_CAR_MODEL: CollectParameters(&m_nIp, 2); CGangs::SetGangVehicleModel(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_SET_GANG_WEAPONS: CollectParameters(&m_nIp, 3); CGangs::SetGangWeapons(ScriptParams[0], (eWeaponType)ScriptParams[1], (eWeaponType)ScriptParams[2]); return 0; /* case COMMAND_SET_CHAR_OBJ_RUN_TO_AREA: { CollectParameters(&m_nIp, 5); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); float infX = *(float*)&ScriptParams[1]; float infY = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[1]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[2]; } CVector pos; pos.x = (infX + supX) / 2; pos.y = (infY + supY) / 2; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = Max(pos.x - infX, pos.y - infY); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_RUN_TO_AREA, pos, radius); return 0; } */ case COMMAND_SET_CHAR_OBJ_RUN_TO_COORD: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVector pos; pos.x = *(float*)&ScriptParams[1]; pos.y = *(float*)&ScriptParams[2]; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_RUN_TO_AREA, pos); return 0; } /* case COMMAND_IS_PLAYER_TOUCHING_OBJECT_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); bool isTouching = false; if (pPed->bInVehicle) isTouching = false; else if (pPed->GetHasCollidedWith(pObject)) isTouching = true; UpdateCompareFlag(isTouching); return 0; } case COMMAND_IS_CHAR_TOUCHING_OBJECT_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); bool isTouching = false; if (pPed->InVehicle()) isTouching = false; else if (pPed->GetHasCollidedWith(pObject)) isTouching = true; UpdateCompareFlag(isTouching); return 0; } */ case COMMAND_LOAD_SPECIAL_CHARACTER: { CollectParameters(&m_nIp, 1); char name[16]; strncpy(name, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) name[i] = tolower(name[i]); CStreaming::RequestSpecialChar(ScriptParams[0] - 1, name, STREAMFLAGS_DEPENDENCY | STREAMFLAGS_SCRIPTOWNED); m_nIp += KEY_LENGTH_IN_SCRIPT; return 0; } case COMMAND_HAS_SPECIAL_CHARACTER_LOADED: { CollectParameters(&m_nIp, 1); UpdateCompareFlag(CStreaming::HasSpecialCharLoaded(ScriptParams[0] - 1)); return 0; } /* case COMMAND_FLASH_CAR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bHasBlip = (ScriptParams[1] != 0); return 0; } case COMMAND_FLASH_CHAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bHasBlip = (ScriptParams[1] != 0); return 0; } case COMMAND_FLASH_OBJECT: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); pObject->bHasBlip = (ScriptParams[1] != 0); return 0; } */ case COMMAND_IS_PLAYER_IN_REMOTE_MODE: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CWorld::Players[ScriptParams[0]].IsPlayerInRemoteMode()); return 0; /* case COMMAND_ARM_CAR_WITH_BOMB: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); ((CAutomobile*)pVehicle)->m_bombType = ScriptParams[1]; ((CAutomobile*)pVehicle)->m_pBombRigger = FindPlayerPed(); return 0; } */ case COMMAND_SET_CHAR_PERSONALITY: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->SetPedStats((ePedStats)ScriptParams[1]); return 0; } case COMMAND_SET_CUTSCENE_OFFSET: CollectParameters(&m_nIp, 3); CCutsceneMgr::SetCutsceneOffset(*(CVector*)&ScriptParams[0]); return 0; case COMMAND_SET_ANIM_GROUP_FOR_CHAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->m_animGroup = (AssocGroupId)ScriptParams[1]; return 0; } /* case COMMAND_SET_ANIM_GROUP_FOR_PLAYER: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); pPed->m_animGroup = (AssocGroupId)ScriptParams[1]; return 0; } */ case COMMAND_REQUEST_MODEL: { CollectParameters(&m_nIp, 1); int model = ScriptParams[0]; if (model < 0) model = CTheScripts::UsedObjectArray[-model].index; CStreaming::RequestModel(model, STREAMFLAGS_DEPENDENCY | STREAMFLAGS_NOFADE | STREAMFLAGS_SCRIPTOWNED); return 0; } case COMMAND_HAS_MODEL_LOADED: { CollectParameters(&m_nIp, 1); int model = ScriptParams[0]; if (model < 0) model = CTheScripts::UsedObjectArray[-model].index; UpdateCompareFlag(CStreaming::HasModelLoaded(model)); return 0; } case COMMAND_MARK_MODEL_AS_NO_LONGER_NEEDED: { CollectParameters(&m_nIp, 1); int model = ScriptParams[0]; if (model < 0) model = CTheScripts::UsedObjectArray[-model].index; CStreaming::SetMissionDoesntRequireModel(model); return 0; } case COMMAND_GRAB_PHONE: { CollectParameters(&m_nIp, 2); ScriptParams[0] = gPhoneInfo.GrabPhone(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_SET_REPEATED_PHONE_MESSAGE: { CollectParameters(&m_nIp, 1); wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text, nil, nil, nil, nil, nil); return 0; } case COMMAND_SET_PHONE_MESSAGE: { CollectParameters(&m_nIp, 1); wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text, nil, nil, nil, nil, nil); return 0; } case COMMAND_HAS_PHONE_DISPLAYED_MESSAGE: { CollectParameters(&m_nIp, 1); UpdateCompareFlag(gPhoneInfo.HasMessageBeenDisplayed(ScriptParams[0])); return 0; } */ case COMMAND_TURN_PHONE_OFF: { CollectParameters(&m_nIp, 1); gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], nil, nil, nil, nil, nil, nil); return 0; } case COMMAND_DRAW_CORONA: { CollectParameters(&m_nIp, 9); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CCoronas::RegisterCorona((uintptr)this + m_nIp, ScriptParams[6], ScriptParams[7], ScriptParams[8], 255, pos, *(float*)&ScriptParams[3], 450.0f, ScriptParams[4], ScriptParams[5], 1, 0, 0, 0.0f); return 0; } case COMMAND_DRAW_LIGHT: { CollectParameters(&m_nIp, 6); CVector pos = *(CVector*)&ScriptParams[0]; CVector unused(0.0f, 0.0f, 0.0f); CPointLights::AddLight(0, *(CVector*)&ScriptParams[0], CVector(0.0f, 0.0f, 0.0f), 12.0f, ScriptParams[3] / 255.0f, ScriptParams[4] / 255.0f, ScriptParams[5] / 255.0f, 0, true); return 0; } //case COMMAND_STORE_WEATHER: //case COMMAND_RESTORE_WEATHER: case COMMAND_STORE_CLOCK: CClock::StoreClock(); return 0; case COMMAND_RESTORE_CLOCK: CClock::RestoreClock(); return 0; /* case COMMAND_RESTART_CRITICAL_MISSION: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRestart::OverrideNextRestart(pos, *(float*)&ScriptParams[3]); if (CWorld::Players[CWorld::PlayerInFocus].m_WBState != WBSTATE_PLAYING) printf("RESTART_CRITICAL_MISSION - Player state is not PLAYING\n"); CWorld::Players[CWorld::PlayerInFocus].PlayerFailedCriticalMission(); return 0; } */ case COMMAND_IS_PLAYER_PLAYING: { CollectParameters(&m_nIp, 1); UpdateCompareFlag(CWorld::Players[ScriptParams[0]].m_WBState == WBSTATE_PLAYING); return 0; } #ifdef GTA_SCRIPT_COLLECTIVE case COMMAND_SET_COLL_OBJ_NO_OBJ: CollectParameters(&m_nIp, 1); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_NONE); return 0; #endif default: script_assert(0); } return -1; } int8 CRunningScript::ProcessCommands600To699(int32 command) { switch (command){ #ifdef GTA_SCRIPT_COLLECTIVE case COMMAND_SET_COLL_OBJ_WAIT_ON_FOOT: CollectParameters(&m_nIp, 1); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_WAIT_ON_FOOT); return 0; case COMMAND_SET_COLL_OBJ_FLEE_ON_FOOT_TILL_SAFE: CollectParameters(&m_nIp, 1); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); return 0; case COMMAND_SET_COLL_OBJ_GUARD_SPOT: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GUARD_AREA, pos); return 0; } case COMMAND_SET_COLL_OBJ_GUARD_AREA: { CollectParameters(&m_nIp, 5); float infX = *(float*)&ScriptParams[1]; float supX = *(float*)&ScriptParams[3]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[1]; } float infY = *(float*)&ScriptParams[2]; float supY = *(float*)&ScriptParams[4]; if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[2]; } CVector pos; pos.x = (infX + supX) / 2; pos.y = (infY + supY) / 2; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = Max(pos.x - infX, pos.y - infY); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GUARD_AREA, pos, radius); return 0; } case COMMAND_SET_COLL_OBJ_WAIT_IN_CAR: CollectParameters(&m_nIp, 1); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_WAIT_IN_CAR); return 0; case COMMAND_SET_COLL_OBJ_KILL_CHAR_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_KILL_CHAR_ON_FOOT, pPed); return 0; } case COMMAND_SET_COLL_OBJ_KILL_PLAYER_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_KILL_CHAR_ON_FOOT, pPed); return 0; } case COMMAND_SET_COLL_OBJ_KILL_CHAR_ANY_MEANS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_KILL_CHAR_ANY_MEANS, pPed); return 0; } case COMMAND_SET_COLL_OBJ_KILL_PLAYER_ANY_MEANS: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_KILL_CHAR_ANY_MEANS, pPed); return 0; } case COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, pPed); return 0; } case COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, pPed); return 0; } case COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pPed); return 0; } case COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, pPed); return 0; } case COMMAND_SET_COLL_OBJ_GOTO_CHAR_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_CHAR_ON_FOOT, pPed); return 0; } case COMMAND_SET_COLL_OBJ_GOTO_PLAYER_ON_FOOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[1]].m_pPed; CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_CHAR_ON_FOOT, pPed); return 0; } case COMMAND_SET_COLL_OBJ_LEAVE_CAR: CollectParameters(&m_nIp, 1); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_LEAVE_CAR); return 0; case COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_PASSENGER: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_ENTER_CAR_AS_PASSENGER, pVehicle); return 0; } case COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_DRIVER: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicle); return 0; } /* case COMMAND_SET_COLL_OBJ_FOLLOW_CAR_IN_CAR: case COMMAND_SET_COLL_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE: case COMMAND_SET_COLL_OBJ_DESTROY_OBJECT: */ case COMMAND_SET_COLL_OBJ_DESTROY_CAR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_DESTROY_CAR, pVehicle); return 0; } case COMMAND_SET_COLL_OBJ_GOTO_AREA_ON_FOOT: { CollectParameters(&m_nIp, 5); float infX = *(float*)&ScriptParams[1]; float supX = *(float*)&ScriptParams[3]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[1]; } float infY = *(float*)&ScriptParams[2]; float supY = *(float*)&ScriptParams[4]; if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[2]; } CVector pos; pos.x = (infX + supX) / 2; pos.y = (infY + supY) / 2; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = Max(pos.x - infX, pos.y - infY); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_AREA_ON_FOOT, pos, radius); return 0; } /* case COMMAND_SET_COLL_OBJ_GOTO_AREA_IN_CAR: case COMMAND_SET_COLL_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET: case COMMAND_SET_COLL_OBJ_GUARD_ATTACK: */ case COMMAND_SET_COLL_OBJ_FOLLOW_ROUTE: CollectParameters(&m_nIp, 3); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_FOLLOW_ROUTE, ScriptParams[1], ScriptParams[2]); return 0; case COMMAND_SET_COLL_OBJ_GOTO_COORD_ON_FOOT: { CollectParameters(&m_nIp, 3); CVector pos(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2], CWorld::FindGroundZForCoord(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2])); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_AREA_ON_FOOT, pos); return 0; } //case COMMAND_SET_COLL_OBJ_GOTO_COORD_IN_CAR: case COMMAND_SET_COLL_OBJ_RUN_TO_AREA: { CollectParameters(&m_nIp, 5); float infX = *(float*)&ScriptParams[1]; float supX = *(float*)&ScriptParams[3]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[1]; } float infY = *(float*)&ScriptParams[2]; float supY = *(float*)&ScriptParams[4]; if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[2]; } CVector pos; pos.x = (infX + supX) / 2; pos.y = (infY + supY) / 2; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = Max(pos.x - infX, pos.y - infY); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_RUN_TO_AREA, pos, radius); return 0; } case COMMAND_SET_COLL_OBJ_RUN_TO_COORD: { CollectParameters(&m_nIp, 3); CVector pos(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2], CWorld::FindGroundZForCoord(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2])); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_RUN_TO_AREA, pos); return 0; } case COMMAND_ADD_PEDS_IN_AREA_TO_COLL: { CollectParameters(&m_nIp, 3); float X = *(float*)&ScriptParams[0]; float Y = *(float*)&ScriptParams[1]; float Z = CWorld::FindGroundZForCoord(X, Y); float radius = *(float*)&ScriptParams[2]; ScriptParams[0] = CTheScripts::AddPedsInAreaToCollective(X, Y, Z, radius); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_PEDS_IN_VEHICLE_TO_COLL: CollectParameters(&m_nIp, 1); ScriptParams[0] = CTheScripts::AddPedsInVehicleToCollective(ScriptParams[0]); StoreParameters(&m_nIp, 1); return 0; case COMMAND_CLEAR_COLL: CollectParameters(&m_nIp, 1); for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { if (CTheScripts::CollectiveArray[i].colIndex == ScriptParams[0]) { CTheScripts::CollectiveArray[i].colIndex = -1; CTheScripts::CollectiveArray[i].pedIndex = 0; } } return 0; case COMMAND_IS_COLL_IN_CARS: { CollectParameters(&m_nIp, 1); bool result = true; for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); if (!pPed) { CTheScripts::CollectiveArray[i].colIndex = -1; CTheScripts::CollectiveArray[i].pedIndex = 0; } else { result = false; break; } } UpdateCompareFlag(result); return 0; } case COMMAND_LOCATE_COLL_ANY_MEANS_2D: case COMMAND_LOCATE_COLL_ON_FOOT_2D: case COMMAND_LOCATE_COLL_IN_CAR_2D: case COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D: LocateCollectiveCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D: case COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D: case COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D: LocateCollectiveCharCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D: case COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D: case COMMAND_LOCATE_COLL_IN_CAR_CAR_2D: LocateCollectiveCarCommand(command, &m_nIp); return 0; case COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D: case COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D: case COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D: LocateCollectivePlayerCommand(command, &m_nIp); return 0; case COMMAND_IS_COLL_IN_AREA_2D: case COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D: case COMMAND_IS_COLL_IN_AREA_IN_CAR_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D: CollectiveInAreaCheckCommand(command, &m_nIp); return 0; case COMMAND_GET_NUMBER_OF_PEDS_IN_COLL: { CollectParameters(&m_nIp, 1); int total = 0; for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); if (!pPed) { CTheScripts::CollectiveArray[i].colIndex = -1; CTheScripts::CollectiveArray[i].pedIndex = 0; } else { total++; } } ScriptParams[0] = total; StoreParameters(&m_nIp, 1); return 0; } #endif case COMMAND_SET_CHAR_HEED_THREATS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bRespondsToThreats = (ScriptParams[1] != 0); return 0; } case COMMAND_SET_PLAYER_HEED_THREATS: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); pPed->bRespondsToThreats = (ScriptParams[1] != 0); return 0; } case COMMAND_GET_CONTROLLER_MODE: #if defined(GTA_PC) && !defined(DETECT_PAD_INPUT_SWITCH) ScriptParams[0] = 0; #else ScriptParams[0] = CPad::IsAffectedByController ? CPad::GetPad(0)->Mode : 0; #endif StoreParameters(&m_nIp, 1); return 0; case COMMAND_SET_CAN_RESPRAY_CAR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); //assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); // they DO call this for bikes, we don't really want to destroy the structure... #ifdef FIX_BUGS if (pVehicle->m_vehType == VEHICLE_TYPE_CAR) #endif ((CAutomobile*)pVehicle)->bFixedColour = (ScriptParams[1] == 0); return 0; } /* case COMMAND_IS_TAXI: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->IsTaxi()); return 0; } */ case COMMAND_UNLOAD_SPECIAL_CHARACTER: CollectParameters(&m_nIp, 1); CStreaming::SetMissionDoesntRequireSpecialChar(ScriptParams[0] - 1); return 0; case COMMAND_RESET_NUM_OF_MODELS_KILLED_BY_PLAYER: CDarkel::ResetModelsKilledByPlayer(); return 0; case COMMAND_GET_NUM_OF_MODELS_KILLED_BY_PLAYER: CollectParameters(&m_nIp, 1); ScriptParams[0] = CDarkel::QueryModelsKilledByPlayer(ScriptParams[0]); StoreParameters(&m_nIp, 1); return 0; /* case COMMAND_ACTIVATE_GARAGE: CollectParameters(&m_nIp, 1); CGarages::ActivateGarage(ScriptParams[0]); return 0; case COMMAND_SWITCH_TAXI_TIMER: { CollectParameters(&m_nIp, 1); if (ScriptParams[0] != 0){ CWorld::Players[CWorld::PlayerInFocus].m_nUnusedTaxiTimer = CTimer::GetTimeInMilliseconds(); CWorld::Players[CWorld::PlayerInFocus].m_bUnusedTaxiThing = true; }else{ CWorld::Players[CWorld::PlayerInFocus].m_bUnusedTaxiThing = false; } return 0; } */ case COMMAND_CREATE_OBJECT_NO_OFFSET: { CollectParameters(&m_nIp, 4); int mi = ScriptParams[0] >= 0 ? ScriptParams[0] : CTheScripts::UsedObjectArray[-ScriptParams[0]].index; CObject* pObj = new CObject(mi, false); ; pObj->ObjectCreatedBy = MISSION_OBJECT; CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pObj->SetPosition(pos); pObj->SetOrientation(0.0f, 0.0f, 0.0f); pObj->GetMatrix().UpdateRW(); pObj->UpdateRwFrame(); CBaseModelInfo* pModelInfo = CModelInfo::GetModelInfo(mi); if (pModelInfo->IsBuilding() && ((CSimpleModelInfo*)pModelInfo)->m_isBigBuilding) pObj->SetupBigBuilding(); CTheScripts::ClearSpaceForMissionEntity(pos, pObj); CWorld::Add(pObj); ScriptParams[0] = CPools::GetObjectPool()->GetIndex(pObj); StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_OBJECT); return 0; } /* case COMMAND_IS_BOAT: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->m_vehType == VEHICLE_TYPE_BOAT); return 0; } case COMMAND_SET_CHAR_OBJ_GOTO_AREA_ANY_MEANS: { CollectParameters(&m_nIp, 5); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); float infX = *(float*)&ScriptParams[1]; float infY = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[1]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[2]; } CVector pos; pos.x = (infX + supX) / 2; pos.y = (infY + supY) / 2; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = Max(pos.x - infX, pos.y - infY); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_GOTO_AREA_ANY_MEANS, pos, radius); return 0; } */ #ifdef GTA_SCRIPT_COLLECTIVE case COMMAND_SET_COLL_OBJ_GOTO_AREA_ANY_MEANS: { CollectParameters(&m_nIp, 5); float infX = *(float*)&ScriptParams[1]; float supX = *(float*)&ScriptParams[3]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[1]; } float infY = *(float*)&ScriptParams[2]; float supY = *(float*)&ScriptParams[4]; if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[2]; } CVector pos; pos.x = (infX + supX) / 2; pos.y = (infY + supY) / 2; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = Max(pos.x - infX, pos.y - infY); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_GOTO_AREA_ANY_MEANS, pos, radius); return 0; } #endif case COMMAND_IS_PLAYER_STOPPED: { CollectParameters(&m_nIp, 1); CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; UpdateCompareFlag(CTheScripts::IsPlayerStopped(pPlayer)); return 0; } /* case COMMAND_IS_CHAR_STOPPED: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); UpdateCompareFlag(CTheScripts::IsPedStopped(pPed)); return 0; } case COMMAND_MESSAGE_WAIT: CollectParameters(&m_nIp, 2); m_nWakeTime = CTimer::GetTimeInMilliseconds() + ScriptParams[0]; if (ScriptParams[1] != 0) m_bSkipWakeTime = true; return 1; case COMMAND_ADD_PARTICLE_EFFECT: { CollectParameters(&m_nIp, 5); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CParticleObject::AddObject(ScriptParams[0], pos, ScriptParams[4] != 0); return 0; } */ case COMMAND_SWITCH_WIDESCREEN: CollectParameters(&m_nIp, 1); if (ScriptParams[0] != 0) TheCamera.SetWideScreenOn(); else TheCamera.SetWideScreenOff(); return 0; /* case COMMAND_ADD_SPRITE_BLIP_FOR_CAR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int id = CRadar::SetEntityBlip(BLIP_CAR, ScriptParams[0], 0, BLIP_DISPLAY_BOTH); CRadar::SetBlipSprite(id, ScriptParams[1]); ScriptParams[0] = id; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_SPRITE_BLIP_FOR_CHAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int id = CRadar::SetEntityBlip(BLIP_CHAR, ScriptParams[0], 1, BLIP_DISPLAY_BOTH); CRadar::SetBlipSprite(id, ScriptParams[1]); ScriptParams[0] = id; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_SPRITE_BLIP_FOR_OBJECT: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int id = CRadar::SetEntityBlip(BLIP_OBJECT, ScriptParams[0], 6, BLIP_DISPLAY_BOTH); CRadar::SetBlipSprite(id, ScriptParams[1]); ScriptParams[0] = id; StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_ADD_SPRITE_BLIP_FOR_CONTACT_POINT: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int id = CRadar::SetCoordBlip(BLIP_CONTACT_POINT, pos, 2, BLIP_DISPLAY_BOTH); CRadar::SetBlipSprite(id, ScriptParams[3]); ScriptParams[0] = id; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_SPRITE_BLIP_FOR_COORD: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int id = CRadar::SetCoordBlip(BLIP_COORD, pos, 5, BLIP_DISPLAY_BOTH); CRadar::SetBlipSprite(id, ScriptParams[3]); ScriptParams[0] = id; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_CHAR_ONLY_DAMAGED_BY_PLAYER: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bOnlyDamagedByPlayer = (ScriptParams[1] != 0); return 0; } case COMMAND_SET_CAR_ONLY_DAMAGED_BY_PLAYER: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bOnlyDamagedByPlayer = (ScriptParams[1] != 0); return 0; } case COMMAND_SET_CHAR_PROOFS: { CollectParameters(&m_nIp, 6); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bBulletProof = (ScriptParams[1] != 0); pPed->bFireProof = (ScriptParams[2] != 0); pPed->bExplosionProof = (ScriptParams[3] != 0); pPed->bCollisionProof = (ScriptParams[4] != 0); pPed->bMeleeProof = (ScriptParams[5] != 0); return 0; } case COMMAND_SET_CAR_PROOFS: { CollectParameters(&m_nIp, 6); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bBulletProof = (ScriptParams[1] != 0); pVehicle->bFireProof = (ScriptParams[2] != 0); pVehicle->bExplosionProof = (ScriptParams[3] != 0); pVehicle->bCollisionProof = (ScriptParams[4] != 0); pVehicle->bMeleeProof = (ScriptParams[5] != 0); return 0; } case COMMAND_IS_PLAYER_IN_ANGLED_AREA_2D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_2D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D: PlayerInAngledAreaCheckCommand(command, &m_nIp); return 0; /* case COMMAND_DEACTIVATE_GARAGE: CollectParameters(&m_nIp, 1); CGarages::DeActivateGarage(ScriptParams[0]); return 0; case COMMAND_GET_NUMBER_OF_CARS_COLLECTED_BY_GARAGE: CollectParameters(&m_nIp, 1); ScriptParams[0] = CGarages::QueryCarsCollected(ScriptParams[0]); StoreParameters(&m_nIp, 1); return 0; case COMMAND_HAS_CAR_BEEN_TAKEN_TO_GARAGE: CollectParameters(&m_nIp, 2); UpdateCompareFlag(CGarages::HasThisCarBeenCollected(ScriptParams[0], ScriptParams[1] - 1)); return 0; */ default: script_assert(0); } return -1; } int8 CRunningScript::ProcessCommands700To799(int32 command) { switch (command){ /* case COMMAND_SET_SWAT_REQUIRED: CollectParameters(&m_nIp, 1); FindPlayerPed()->m_pWanted->m_bSwatRequired = (ScriptParams[0] != 0); return 0; case COMMAND_SET_FBI_REQUIRED: CollectParameters(&m_nIp, 1); FindPlayerPed()->m_pWanted->m_bFbiRequired = (ScriptParams[0] != 0); return 0; case COMMAND_SET_ARMY_REQUIRED: CollectParameters(&m_nIp, 1); FindPlayerPed()->m_pWanted->m_bArmyRequired = (ScriptParams[0] != 0); return 0; */ case COMMAND_IS_CAR_IN_WATER: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); UpdateCompareFlag(pVehicle && pVehicle->bIsInWater); return 0; } case COMMAND_GET_CLOSEST_CHAR_NODE: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CPathNode* pNode = &ThePaths.m_pathNodes[ThePaths.FindNodeClosestToCoors(pos, 1, 999999.9f, true)]; *(CVector*)&ScriptParams[0] = pNode->GetPosition(); StoreParameters(&m_nIp, 3); return 0; } case COMMAND_GET_CLOSEST_CAR_NODE: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); *(CVector*)&ScriptParams[0] = ThePaths.FindNodeCoorsForScript(ThePaths.FindNodeClosestToCoors(pos, 0, 999999.9f, true, true)); StoreParameters(&m_nIp, 3); return 0; } case COMMAND_CAR_GOTO_COORDINATES_ACCURATE: { CollectParameters(&m_nIp, 4); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pos.z += pVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); if (CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, pos, false)) pVehicle->AutoPilot.m_nCarMission = MISSION_GOTO_COORDS_STRAIGHT_ACCURATE; else pVehicle->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_ACCURATE; pVehicle->SetStatus(STATUS_PHYSICS); pVehicle->bEngineOn = true; pVehicle->AutoPilot.m_nCruiseSpeed = Max(1, pVehicle->AutoPilot.m_nCruiseSpeed); pVehicle->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); return 0; } /* case COMMAND_START_PACMAN_RACE: CollectParameters(&m_nIp, 1); CPacManPickups::StartPacManRace(ScriptParams[0]); return 0; case COMMAND_START_PACMAN_RECORD: CPacManPickups::StartPacManRecord(); return 0; case COMMAND_GET_NUMBER_OF_POWER_PILLS_EATEN: ScriptParams[0] = CPacManPickups::QueryPowerPillsEatenInRace(); StoreParameters(&m_nIp, 1); return 0; case COMMAND_CLEAR_PACMAN: CPacManPickups::CleanUpPacManStuff(); return 0; case COMMAND_START_PACMAN_SCRAMBLE: { CollectParameters(&m_nIp, 5); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CPacManPickups::StartPacManScramble(pos, *(float*)&ScriptParams[3], ScriptParams[4]); return 0; } case COMMAND_GET_NUMBER_OF_POWER_PILLS_CARRIED: ScriptParams[0] = CPacManPickups::QueryPowerPillsCarriedByPlayer(); StoreParameters(&m_nIp, 1); return 0; case COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_CARRIED: CPacManPickups::ResetPowerPillsCarriedByPlayer(); return 0; */ case COMMAND_IS_CAR_ON_SCREEN: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(TheCamera.IsSphereVisible(pVehicle->GetBoundCentre(), pVehicle->GetBoundRadius())); return 0; } case COMMAND_IS_CHAR_ON_SCREEN: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(TheCamera.IsSphereVisible(pPed->GetBoundCentre(), pPed->GetBoundRadius())); return 0; } case COMMAND_IS_OBJECT_ON_SCREEN: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); UpdateCompareFlag(TheCamera.IsSphereVisible(pObject->GetBoundCentre(), pObject->GetBoundRadius())); return 0; } /* case COMMAND_GOSUB_FILE: { CollectParameters(&m_nIp, 2); script_assert(m_nStackPointer < MAX_STACK_DEPTH); m_anStack[m_nStackPointer++] = m_nIp; SetIP(ScriptParams[0]); // ScriptParams[1] == filename return 0; } */ case COMMAND_GET_GROUND_Z_FOR_3D_COORD: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; bool success; *(float*)&ScriptParams[0] = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &success); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_START_SCRIPT_FIRE: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); ScriptParams[0] = gFireManager.StartScriptFire(pos, nil, 0.8f, 1); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_IS_SCRIPT_FIRE_EXTINGUISHED: CollectParameters(&m_nIp, 1); UpdateCompareFlag(gFireManager.IsScriptFireExtinguish(ScriptParams[0])); return 0; case COMMAND_REMOVE_SCRIPT_FIRE: CollectParameters(&m_nIp, 1); gFireManager.RemoveScriptFire(ScriptParams[0]); return 0; /* case COMMAND_SET_COMEDY_CONTROLS: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bComedyControls = (ScriptParams[1] != 0); return 0; } */ case COMMAND_BOAT_GOTO_COORDS: { CollectParameters(&m_nIp, 4); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); script_assert(pVehicle->m_vehType == VEHICLE_TYPE_BOAT); CBoat* pBoat = (CBoat*)pVehicle; CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &pos.z, false); pBoat->AutoPilot.m_nCarMission = MISSION_GOTOCOORDS_ASTHECROWSWIMS; pBoat->AutoPilot.m_vecDestinationCoors = pos; pBoat->SetStatus(STATUS_PHYSICS); pBoat->bEngineOn = true; pBoat->AutoPilot.m_nCruiseSpeed = Max(1, pBoat->AutoPilot.m_nCruiseSpeed); pBoat->AutoPilot.m_nAntiReverseTimer = CTimer::GetTimeInMilliseconds(); return 0; } case COMMAND_BOAT_STOP: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); script_assert(pVehicle->m_vehType == VEHICLE_TYPE_BOAT); CBoat* pBoat = (CBoat*)pVehicle; pBoat->AutoPilot.m_nCarMission = MISSION_NONE; pBoat->SetStatus(STATUS_PHYSICS); pBoat->bEngineOn = false; pBoat->AutoPilot.m_nCruiseSpeed = 0; return 0; } case COMMAND_IS_PLAYER_SHOOTING_IN_AREA: { CollectParameters(&m_nIp, 6); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float x2 = *(float*)&ScriptParams[3]; float y2 = *(float*)&ScriptParams[4]; UpdateCompareFlag(pPed->bIsShooting && pPed->IsWithinArea(x1, y1, x2, y2)); if (ScriptParams[5]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugSquare(x1, y1, x2, y2); return 0; } case COMMAND_IS_CHAR_SHOOTING_IN_AREA: { CollectParameters(&m_nIp, 6); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); float x1 = *(float*)&ScriptParams[1]; float y1 = *(float*)&ScriptParams[2]; float x2 = *(float*)&ScriptParams[3]; float y2 = *(float*)&ScriptParams[4]; UpdateCompareFlag(pPed->bIsShooting && pPed->IsWithinArea(x1, y1, x2, y2)); if (ScriptParams[5]) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, x1, y1, x2, y2, MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugSquare(x1, y1, x2, y2); return 0; } case COMMAND_IS_CURRENT_PLAYER_WEAPON: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(ScriptParams[1] == pPed->GetWeapon()->m_eWeaponType); return 0; } case COMMAND_IS_CURRENT_CHAR_WEAPON: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(ScriptParams[1] == pPed->GetWeapon()->m_eWeaponType); return 0; } /* case COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_EATEN: CPacManPickups::ResetPowerPillsEatenInRace(); return 0; case COMMAND_ADD_POWER_PILL: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CPacManPickups::GenerateOnePMPickUp(pos); return 0; } */ case COMMAND_SET_BOAT_CRUISE_SPEED: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); script_assert(pVehicle->m_vehType == VEHICLE_TYPE_BOAT); CBoat* pBoat = (CBoat*)pVehicle; pBoat->AutoPilot.m_nCruiseSpeed = *(float*)&ScriptParams[1]; return 0; } /* case COMMAND_GET_RANDOM_CHAR_IN_AREA: { CollectParameters(&m_nIp, 4); int ped_handle = -1; CVector pos = FindPlayerCoors(); float x1 = *(float*)&ScriptParams[0]; float y1 = *(float*)&ScriptParams[1]; float x2 = *(float*)&ScriptParams[2]; float y2 = *(float*)&ScriptParams[3]; int i = CPools::GetPedPool()->GetSize(); while (--i && ped_handle == -1){ CPed* pPed = CPools::GetPedPool()->GetSlot(i); if (!pPed) continue; if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) continue; if (pPed->CharCreatedBy != RANDOM_CHAR) continue; if (!pPed->IsPedInControl()) continue; if (pPed->bRemoveFromWorld) continue; if (pPed->bFadeOut) continue; // if (pPed->GetModelIndex() == MI_SCUM_WOM || pPed->GetModelIndex() == MI_SCUM_MAN) // continue; if (!ThisIsAValidRandomPed(pPed->m_nPedType)) continue; if (pPed->bIsLeader || pPed->m_leader) continue; if (!pPed->IsWithinArea(x1, y1, x2, y2)) continue; if (pos.z - PED_FIND_Z_OFFSET > pPed->GetPosition().z) continue; if (pos.z + PED_FIND_Z_OFFSET < pPed->GetPosition().z) continue; ped_handle = CPools::GetPedPool()->GetIndex(pPed); CTheScripts::LastRandomPedId = ped_handle; pPed->CharCreatedBy = MISSION_CHAR; pPed->bRespondsToThreats = false; ++CPopulation::ms_nTotalMissionPeds; if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); } ScriptParams[0] = ped_handle; StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_GET_RANDOM_CHAR_IN_ZONE: { char zone[KEY_LENGTH_IN_SCRIPT]; strncpy(zone, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); int nZone = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_DEFAULT); if (nZone != -1) m_nIp += KEY_LENGTH_IN_SCRIPT; CZone* pZone = CTheZones::GetNavigationZone(nZone); CollectParameters(&m_nIp, 3); int ped_handle = -1; CVector pos = FindPlayerCoors(); int i = CPools::GetPedPool()->GetSize(); while (--i && ped_handle == -1) { CPed* pPed = CPools::GetPedPool()->GetSlot(i); if (!pPed) continue; if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) continue; if (pPed->CharCreatedBy != RANDOM_CHAR) continue; if (!pPed->IsPedInControl()) continue; if (pPed->bRemoveFromWorld) continue; if (pPed->bFadeOut) continue; if (pPed->m_nWaitState != WAITSTATE_FALSE) continue; if (!ThisIsAValidRandomPed(pPed->m_nPedType, ScriptParams[0], ScriptParams[1], ScriptParams[2])) continue; if (pPed->bIsLeader || pPed->m_leader) continue; if (!CTheZones::PointLiesWithinZone(&pPed->GetPosition(), pZone)) continue; if (pos.z - PED_FIND_Z_OFFSET > pPed->GetPosition().z) continue; if (pos.z + PED_FIND_Z_OFFSET < pPed->GetPosition().z) continue; bool found; CWorld::FindRoofZFor3DCoord(pos.x, pos.y, pos.z, &found); if (found) continue; ped_handle = CPools::GetPedPool()->GetIndex(pPed); CTheScripts::LastRandomPedId = ped_handle; pPed->CharCreatedBy = MISSION_CHAR; pPed->bRespondsToThreats = false; ++CPopulation::ms_nTotalMissionPeds; if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); } ScriptParams[0] = ped_handle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_IS_PLAYER_IN_TAXI: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->IsTaxi()); return 0; } case COMMAND_IS_PLAYER_SHOOTING: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->bIsShooting); return 0; } case COMMAND_IS_CHAR_SHOOTING: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bIsShooting); return 0; } case COMMAND_CREATE_MONEY_PICKUP: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_MONEY, PICKUP_MONEY, ScriptParams[3]); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_CHAR_ACCURACY: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->m_wepAccuracy = ScriptParams[1]; return 0; } case COMMAND_GET_CAR_SPEED: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); *(float*)&ScriptParams[0] = pVehicle->GetSpeed().Magnitude() * GAME_SPEED_TO_METERS_PER_SECOND; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_LOAD_CUTSCENE: { char name[KEY_LENGTH_IN_SCRIPT]; strncpy(name, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); m_nIp += KEY_LENGTH_IN_SCRIPT; CCutsceneMgr::LoadCutsceneData(name); return 0; } case COMMAND_CREATE_CUTSCENE_OBJECT: { CollectParameters(&m_nIp, 1); CCutsceneObject* pCutObj = CCutsceneMgr::CreateCutsceneObject(ScriptParams[0]); ScriptParams[0] = CPools::GetObjectPool()->GetIndex(pCutObj); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_CUTSCENE_ANIM: { CollectParameters(&m_nIp, 1); char name[KEY_LENGTH_IN_SCRIPT]; CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); strncpy(name, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); m_nIp += KEY_LENGTH_IN_SCRIPT; CCutsceneMgr::SetCutsceneAnim(name, pObject); return 0; } case COMMAND_START_CUTSCENE: CCutsceneMgr::ms_cutsceneLoadStatus = 1; return 0; case COMMAND_GET_CUTSCENE_TIME: ScriptParams[0] = CCutsceneMgr::GetCutsceneTimeInMilleseconds(); StoreParameters(&m_nIp, 1); return 0; case COMMAND_HAS_CUTSCENE_FINISHED: UpdateCompareFlag(CCutsceneMgr::HasCutsceneFinished()); return 0; case COMMAND_CLEAR_CUTSCENE: CCutsceneMgr::DeleteCutsceneData(); return 0; case COMMAND_RESTORE_CAMERA_JUMPCUT: TheCamera.RestoreWithJumpCut(); return 0; case COMMAND_CREATE_COLLECTABLE1: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; CPickups::GenerateNewOne(pos, MI_COLLECTABLE1, PICKUP_COLLECTABLE1, 0); return 0; } case COMMAND_SET_COLLECTABLE1_TOTAL: CollectParameters(&m_nIp, 1); CWorld::Players[CWorld::PlayerInFocus].m_nTotalPackages = ScriptParams[0]; return 0; /* case COMMAND_IS_PROJECTILE_IN_AREA: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } UpdateCompareFlag(CProjectileInfo::IsProjectileInRange(infX, supX, infY, supY, infZ, supZ, false)); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); return 0; } case COMMAND_DESTROY_PROJECTILES_IN_AREA: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } UpdateCompareFlag(CProjectileInfo::IsProjectileInRange(infX, supX, infY, supY, infZ, supZ, true)); if (CTheScripts::DbgFlag) CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); return 0; } case COMMAND_DROP_MINE: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; CPickups::GenerateNewOne(pos, MI_CARMINE, PICKUP_MINE_INACTIVE, 0); return 0; } case COMMAND_DROP_NAUTICAL_MINE: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; CPickups::GenerateNewOne(pos, MI_NAUTICALMINE, PICKUP_MINE_INACTIVE, 0); return 0; } */ case COMMAND_IS_CHAR_MODEL: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(ScriptParams[1] == pPed->GetModelIndex()); return 0; } case COMMAND_LOAD_SPECIAL_MODEL: { CollectParameters(&m_nIp, 1); char name[KEY_LENGTH_IN_SCRIPT]; strncpy(name, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) name[i] = tolower(name[i]); CStreaming::RequestSpecialModel(ScriptParams[0], name, STREAMFLAGS_DEPENDENCY | STREAMFLAGS_SCRIPTOWNED); m_nIp += KEY_LENGTH_IN_SCRIPT; return 0; } //case COMMAND_CREATE_CUTSCENE_HEAD: //case COMMAND_SET_CUTSCENE_HEAD_ANIM: case COMMAND_SIN: CollectParameters(&m_nIp, 1); *(float*)&ScriptParams[0] = Sin(DEGTORAD(*(float*)&ScriptParams[0])); StoreParameters(&m_nIp, 1); return 0; case COMMAND_COS: CollectParameters(&m_nIp, 1); *(float*)&ScriptParams[0] = Cos(DEGTORAD(*(float*)&ScriptParams[0])); StoreParameters(&m_nIp, 1); return 0; case COMMAND_GET_CAR_FORWARD_X: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); float forwardX = pVehicle->GetForward().x / pVehicle->GetForward().Magnitude2D(); *(float*)&ScriptParams[0] = forwardX; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_CAR_FORWARD_Y: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); float forwardY = pVehicle->GetForward().y / pVehicle->GetForward().Magnitude2D(); *(float*)&ScriptParams[0] = forwardY; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_CHANGE_GARAGE_TYPE: CollectParameters(&m_nIp, 2); CGarages::ChangeGarageType(ScriptParams[0], ScriptParams[1], 0); return 0; /* case COMMAND_ACTIVATE_CRUSHER_CRANE: { CollectParameters(&m_nIp, 10); float infX = *(float*)&ScriptParams[2]; float infY = *(float*)&ScriptParams[3]; float supX = *(float*)&ScriptParams[4]; float supY = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[4]; supX = *(float*)&ScriptParams[2]; } if (infY > supY) { infY = *(float*)&ScriptParams[5]; supY = *(float*)&ScriptParams[3]; } CCranes::ActivateCrane(infX, supX, infY, supY, *(float*)&ScriptParams[6], *(float*)&ScriptParams[7], *(float*)&ScriptParams[8], DEGTORAD(*(float*)&ScriptParams[9]), true, false, *(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); return 0; } case COMMAND_PRINT_WITH_2_NUMBERS: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 4); CMessages::AddMessageWithNumber(text, ScriptParams[2], ScriptParams[3], ScriptParams[0], ScriptParams[1], -1, -1, -1, -1); return 0; } */ case COMMAND_PRINT_WITH_2_NUMBERS_NOW: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 4); CMessages::AddMessageJumpQWithNumber(text, ScriptParams[2], ScriptParams[3], ScriptParams[0], ScriptParams[1], -1, -1, -1, -1); return 0; } /* case COMMAND_PRINT_WITH_2_NUMBERS_SOON: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 4); CMessages::AddMessageSoonWithNumber(text, ScriptParams[2], ScriptParams[3], ScriptParams[0], ScriptParams[1], -1, -1, -1, -1); return 0; } */ case COMMAND_PRINT_WITH_3_NUMBERS: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 5); CMessages::AddMessageWithNumber(text, ScriptParams[3], ScriptParams[4], ScriptParams[0], ScriptParams[1], ScriptParams[2], -1, -1, -1); return 0; } /* case COMMAND_PRINT_WITH_3_NUMBERS_NOW: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 5); CMessages::AddMessageJumpQWithNumber(text, ScriptParams[3], ScriptParams[4], ScriptParams[0], ScriptParams[1], ScriptParams[2], -1, -1, -1); return 0; } case COMMAND_PRINT_WITH_3_NUMBERS_SOON: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 5); CMessages::AddMessageSoonWithNumber(text, ScriptParams[3], ScriptParams[4], ScriptParams[0], ScriptParams[1], ScriptParams[2], -1, -1, -1); return 0; } */ case COMMAND_PRINT_WITH_4_NUMBERS: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 6); CMessages::AddMessageWithNumber(text, ScriptParams[4], ScriptParams[5], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], -1, -1); return 0; } /* case COMMAND_PRINT_WITH_4_NUMBERS_NOW: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 6); CMessages::AddMessageJumpQWithNumber(text, ScriptParams[4], ScriptParams[5], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], -1, -1); return 0; } case COMMAND_PRINT_WITH_4_NUMBERS_SOON: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 6); CMessages::AddMessageSoonWithNumber(text, ScriptParams[4], ScriptParams[5], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], -1, -1); return 0; } case COMMAND_PRINT_WITH_5_NUMBERS: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 7); CMessages::AddMessageWithNumber(text, ScriptParams[5], ScriptParams[6], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], -1); return 0; } case COMMAND_PRINT_WITH_5_NUMBERS_NOW: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 7); CMessages::AddMessageJumpQWithNumber(text, ScriptParams[5], ScriptParams[6], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], -1); return 0; } case COMMAND_PRINT_WITH_5_NUMBERS_SOON: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 7); CMessages::AddMessageSoonWithNumber(text, ScriptParams[5], ScriptParams[6], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], -1); return 0; } */ case COMMAND_PRINT_WITH_6_NUMBERS: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 8); CMessages::AddMessageWithNumber(text, ScriptParams[6], ScriptParams[7], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], ScriptParams[5]); return 0; } /* case COMMAND_PRINT_WITH_6_NUMBERS_NOW: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 8); CMessages::AddMessageJumpQWithNumber(text, ScriptParams[6], ScriptParams[7], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], ScriptParams[5]); return 0; } case COMMAND_PRINT_WITH_6_NUMBERS_SOON: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 8); CMessages::AddMessageSoonWithNumber(text, ScriptParams[6], ScriptParams[7], ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], ScriptParams[5]); return 0; } case COMMAND_SET_CHAR_OBJ_FOLLOW_CHAR_IN_FORMATION: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, pTargetPed); pPed->SetFormation((eFormation)ScriptParams[2]); return 0; } */ case COMMAND_PLAYER_MADE_PROGRESS: CollectParameters(&m_nIp, 1); CStats::ProgressMade += ScriptParams[0]; return 0; case COMMAND_SET_PROGRESS_TOTAL: CollectParameters(&m_nIp, 1); CStats::TotalProgressInGame = ScriptParams[0]; if (CGame::germanGame) CStats::TotalProgressInGame -= 2; return 0; case COMMAND_REGISTER_JUMP_DISTANCE: CollectParameters(&m_nIp, 1); CStats::MaximumJumpDistance = Max(CStats::MaximumJumpDistance, *(float*)&ScriptParams[0]); return 0; case COMMAND_REGISTER_JUMP_HEIGHT: CollectParameters(&m_nIp, 1); CStats::MaximumJumpHeight = Max(CStats::MaximumJumpHeight, *(float*)&ScriptParams[0]); return 0; case COMMAND_REGISTER_JUMP_FLIPS: CollectParameters(&m_nIp, 1); CStats::MaximumJumpFlips = Max(CStats::MaximumJumpFlips, ScriptParams[0]); return 0; case COMMAND_REGISTER_JUMP_SPINS: CollectParameters(&m_nIp, 1); CStats::MaximumJumpSpins = Max(CStats::MaximumJumpSpins, ScriptParams[0]); return 0; case COMMAND_REGISTER_JUMP_STUNT: CollectParameters(&m_nIp, 1); CStats::BestStuntJump = Max(CStats::BestStuntJump, ScriptParams[0]); return 0; case COMMAND_REGISTER_UNIQUE_JUMP_FOUND: ++CStats::NumberOfUniqueJumpsFound; return 0; case COMMAND_SET_UNIQUE_JUMPS_TOTAL: CollectParameters(&m_nIp, 1); CStats::TotalNumberOfUniqueJumps = ScriptParams[0]; return 0; case COMMAND_REGISTER_PASSENGER_DROPPED_OFF_TAXI: ++CStats::PassengersDroppedOffWithTaxi; return 0; case COMMAND_REGISTER_MONEY_MADE_TAXI: CollectParameters(&m_nIp, 1); CStats::MoneyMadeWithTaxi += ScriptParams[0]; return 0; case COMMAND_REGISTER_MISSION_GIVEN: ++CStats::MissionsGiven; return 0; case COMMAND_REGISTER_MISSION_PASSED: { char name[KEY_LENGTH_IN_SCRIPT]; strncpy(name, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); m_nIp += KEY_LENGTH_IN_SCRIPT; strncpy(CStats::LastMissionPassedName, name, KEY_LENGTH_IN_SCRIPT); ++CStats::MissionsPassed; CStats::CheckPointReachedSuccessfully(); CTheScripts::LastMissionPassedTime = CTimer::GetTimeInMilliseconds(); CGameLogic::RemoveShortCutDropOffPointForMission(); return 0; } case COMMAND_SET_CHAR_RUNNING: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bIsRunning = (ScriptParams[1] != 0); return 0; } case COMMAND_REMOVE_ALL_SCRIPT_FIRES: gFireManager.RemoveAllScriptFires(); return 0; /* case COMMAND_IS_FIRST_CAR_COLOUR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->m_currentColour1 == ScriptParams[1]); return 0; } case COMMAND_IS_SECOND_CAR_COLOUR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->m_currentColour2 == ScriptParams[1]); return 0; } */ case COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_WEAPON: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); bool result = false; if (!pPed) printf("HAS_CHAR_BEEN_DAMAGED_BY_WEAPON - Character doesn't exist\n"); else { if (ScriptParams[1] == WEAPONTYPE_ANYMELEE || ScriptParams[1] == WEAPONTYPE_ANYWEAPON) result = CheckDamagedWeaponType(pPed->m_lastWepDam, ScriptParams[1]); else result = ScriptParams[1] == pPed->m_lastWepDam; } UpdateCompareFlag(result); return 0; } case COMMAND_HAS_CAR_BEEN_DAMAGED_BY_WEAPON: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); bool result = false; if (!pVehicle) printf("HAS_CAR_BEEN_DAMAGED_BY_WEAPON - Vehicle doesn't exist\n"); else { if (ScriptParams[1] == WEAPONTYPE_ANYMELEE || ScriptParams[1] == WEAPONTYPE_ANYWEAPON) result = CheckDamagedWeaponType(pVehicle->m_nLastWeaponDamage, ScriptParams[1]); else result = ScriptParams[1] == pVehicle->m_nLastWeaponDamage; } UpdateCompareFlag(result); return 0; } case COMMAND_IS_CHAR_IN_CHARS_GROUP: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CPed* pLeader = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pPed); script_assert(pLeader); UpdateCompareFlag(pPed->m_leader == pLeader && !pPed->bWaitForLeaderToComeCloser); return 0; } default: script_assert(0); } return -1; } ================================================ FILE: src/control/Script4.cpp ================================================ #include "common.h" #include "Script.h" #include "ScriptCommands.h" #include "AnimBlendAssociation.h" #include "BulletInfo.h" #include "CarAI.h" #include "CarCtrl.h" #include "CivilianPed.h" #include "Cranes.h" #include "DMAudio.h" #include "Darkel.h" #include "Explosion.h" #include "Fire.h" #include "Frontend.h" #include "Garages.h" #include "General.h" #include "Heli.h" #include "Hud.h" #include "Messages.h" #include "ParticleObject.h" #include "PedRoutes.h" #include "Phones.h" #include "Pickups.h" #include "Plane.h" #include "Pools.h" #include "Population.h" #include "Radar.h" #include "Record.h" #include "RpAnimBlend.h" #include "Rubbish.h" #include "SpecialFX.h" #include "Stats.h" #include "Streaming.h" #include "TxdStore.h" #include "User.h" #include "WaterLevel.h" #include "World.h" #include "Zones.h" #include "Bike.h" #include "Wanted.h" #ifdef FIX_BUGS static bool IsSlideObjectUsedWrongByScript(const CVector& posTarget, const CVector& slideBy) { if (posTarget == CVector(-559.476f, 784.807f, 23.279f) && slideBy == CVector(0.0f, 10.0f, 0.0f)) return true; // G-Spotlight bottom elevator, east side if (posTarget == CVector(-559.476f, 779.64f, 23.279f) && slideBy == CVector(0.0f, 10.0f, 0.0f)) return true; // G-Spotlight bottom elevator, west side if (posTarget == CVector(-553.563f, 790.595f, 97.917f) && slideBy == CVector(0.0f, 10.0f, 0.0f)) return true; // G-Spotlight top elevator, east side if (posTarget == CVector(-553.563f, 785.427f, 97.917f) && slideBy == CVector(0.0f, 10.0f, 0.0f)) return true; // G-Spotlight top elevator, west side if (posTarget == CVector(-866.689f, -572.095f, 15.573f) && slideBy == CVector(0.0f, 0.0f, 4.5f)) return true; // Cherry Popper garage door return false; } #endif int8 CRunningScript::ProcessCommands800To899(int32 command) { CMatrix tmp_matrix; switch (command) { case COMMAND_IS_CHAR_IN_PLAYERS_GROUP: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CPed* pLeader = CWorld::Players[ScriptParams[1]].m_pPed; script_assert(pPed); script_assert(pLeader); UpdateCompareFlag(pPed->m_leader == pLeader && !pPed->bWaitForLeaderToComeCloser); return 0; } case COMMAND_EXPLODE_CHAR_HEAD: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->InflictDamage(nil, WEAPONTYPE_SNIPERRIFLE, 1000.0f, PEDPIECE_HEAD, 0); return 0; } case COMMAND_EXPLODE_PLAYER_HEAD: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); pPed->InflictDamage(nil, WEAPONTYPE_SNIPERRIFLE, 1000.0f, PEDPIECE_HEAD, 0); return 0; } case COMMAND_ANCHOR_BOAT: { CollectParameters(&m_nIp, 2); CBoat* pBoat = (CBoat*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pBoat && pBoat->m_vehType == VEHICLE_TYPE_BOAT); pBoat->m_bIsAnchored = (ScriptParams[1] != 0); return 0; } case COMMAND_SET_ZONE_GROUP: { char zone[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, zone); m_nIp += KEY_LENGTH_IN_SCRIPT; CollectParameters(&m_nIp, 2); int zone_id = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_INFO); if (zone_id < 0) { printf("Couldn't find zone - %s\n", zone); return 0; } while (zone_id >= 0) { CTheZones::SetPedGroup(zone_id, ScriptParams[0], ScriptParams[1]); zone_id = CTheZones::FindNextZoneByLabelAndReturnIndex(zone, ZONE_INFO); } return 0; } case COMMAND_START_CAR_FIRE: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); ScriptParams[0] = gFireManager.StartScriptFire(pVehicle->GetPosition(), pVehicle, 0.8f, 1); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_START_CHAR_FIRE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); ScriptParams[0] = gFireManager.StartScriptFire(pPed->GetPosition(), pPed, 0.8f, 1); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA: { CollectParameters(&m_nIp, 5); int handle = -1; uint32 i = CPools::GetVehiclePool()->GetSize(); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float supX = *(float*)&ScriptParams[2]; float supY = *(float*)&ScriptParams[3]; while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if (pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR && pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_BIKE) continue; if (!pVehicle->bUsesCollision) continue; if (ScriptParams[4] != pVehicle->GetModelIndex() && ScriptParams[4] >= 0) continue; if (pVehicle->VehicleCreatedBy != RANDOM_VEHICLE) continue; if (!pVehicle->IsWithinArea(infX, infY, supX, supY)) continue; handle = CPools::GetVehiclePool()->GetIndex(pVehicle); pVehicle->VehicleCreatedBy = MISSION_VEHICLE; ++CCarCtrl::NumMissionCars; --CCarCtrl::NumRandomCars; if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(handle, CLEANUP_CAR); } ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_ZONE: { char zone[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, zone); int zone_id = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_DEFAULT); if (zone_id != -1) m_nIp += KEY_LENGTH_IN_SCRIPT; CZone* pZone = CTheZones::GetNavigationZone(zone_id); CollectParameters(&m_nIp, 1); int handle = -1; uint32 i = CPools::GetVehiclePool()->GetSize(); while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if (ScriptParams[0] != pVehicle->GetModelIndex() && ScriptParams[0] >= 0) continue; if (pVehicle->VehicleCreatedBy != RANDOM_VEHICLE) continue; if (!CTheZones::PointLiesWithinZone(&pVehicle->GetPosition(), pZone)) continue; handle = CPools::GetVehiclePool()->GetIndex(pVehicle); pVehicle->VehicleCreatedBy = MISSION_VEHICLE; ++CCarCtrl::NumMissionCars; --CCarCtrl::NumRandomCars; if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(handle, CLEANUP_CAR); } ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_HAS_RESPRAY_HAPPENED: { CollectParameters(&m_nIp, 1); UpdateCompareFlag(CGarages::HasResprayHappened(ScriptParams[0])); return 0; } case COMMAND_SET_CAMERA_ZOOM: { CollectParameters(&m_nIp, 1); if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FOLLOWPED) TheCamera.SetZoomValueFollowPedScript(ScriptParams[0]); else if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_CAM_ON_A_STRING) TheCamera.SetZoomValueCamStringScript(ScriptParams[0]); return 0; } case COMMAND_CREATE_PICKUP_WITH_AMMO: { CollectParameters(&m_nIp, 6); int16 model = ScriptParams[0]; if (model < 0) model = CTheScripts::UsedObjectArray[-model].index; CVector pos = *(CVector*)&ScriptParams[3]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CPickups::GenerateNewOne(pos, model, ScriptParams[1], ScriptParams[2]); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_CAR_RAM_CAR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pTarget); CCarAI::TellCarToRamOtherCar(pVehicle, pTarget); return 0; } /* case COMMAND_SET_CAR_BLOCK_CAR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pTarget); CCarAI::TellCarToBlockOtherCar(pVehicle, pTarget); return 0; } case COMMAND_SET_CHAR_OBJ_CATCH_TRAIN: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_CATCH_TRAIN); return 0; } */ #ifdef GTA_SCRIPT_COLLECTIVE case COMMAND_SET_COLL_OBJ_CATCH_TRAIN: CollectParameters(&m_nIp, 1); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_CATCH_TRAIN); return 0; #endif case COMMAND_SET_PLAYER_NEVER_GETS_TIRED: { CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; pPlayer->m_bInfiniteSprint = (ScriptParams[1] != 0); return 0; } case COMMAND_SET_PLAYER_FAST_RELOAD: { CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; pPlayer->m_bFastReload = (ScriptParams[1] != 0); return 0; } case COMMAND_SET_CHAR_BLEEDING: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bPedIsBleeding = (ScriptParams[1] != 0); return 0; } /* case COMMAND_SET_CAR_FUNNY_SUSPENSION: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); // no action return 0; } case COMMAND_SET_CAR_BIG_WHEELS: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); CAutomobile* pCar = (CAutomobile*)pVehicle; pCar->bBigWheels = (ScriptParams[1] != 0); return 0; } */ case COMMAND_SET_FREE_RESPRAYS: CollectParameters(&m_nIp, 1); CGarages::SetFreeResprays(ScriptParams[0] != 0); return 0; case COMMAND_SET_PLAYER_VISIBLE: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); pPed->bIsVisible = (ScriptParams[1] != 0); return 0; } case COMMAND_SET_CHAR_VISIBLE: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bIsVisible = (ScriptParams[1] != 0); return 0; } /* case COMMAND_SET_CAR_VISIBLE: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bIsVisible = (ScriptParams[1] != 0); return 0; } */ case COMMAND_IS_AREA_OCCUPIED: { CollectParameters(&m_nIp, 11); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } int16 total; CWorld::FindObjectsIntersectingCube(CVector(infX, infY, infZ), CVector(supX, supY, supZ), &total, 2, nil, !!ScriptParams[6], !!ScriptParams[7], !!ScriptParams[8], !!ScriptParams[9], !!ScriptParams[10]); UpdateCompareFlag(total > 0); return 0; } /* case COMMAND_START_DRUG_RUN: CPlane::CreateIncomingCesna(); return 0; case COMMAND_HAS_DRUG_RUN_BEEN_COMPLETED: UpdateCompareFlag(CPlane::HasCesnaLanded()); return 0; case COMMAND_HAS_DRUG_PLANE_BEEN_SHOT_DOWN: UpdateCompareFlag(CPlane::HasCesnaBeenDestroyed()); return 0; case COMMAND_SAVE_PLAYER_FROM_FIRES: CollectParameters(&m_nIp, 1); gFireManager.ExtinguishPoint(CWorld::Players[ScriptParams[0]].GetPos(), 3.0f); return 0; */ case COMMAND_DISPLAY_TEXT: { CollectParameters(&m_nIp, 2); wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtX = *(float*)&ScriptParams[0]; CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtY = *(float*)&ScriptParams[1]; uint16 len = CMessages::GetWideStringLength(text); for (uint16 i = 0; i < len; i++) CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_Text[i] = text[i]; for (uint16 i = len; i < SCRIPT_TEXT_MAX_LENGTH; i++) CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_Text[i] = 0; ++CTheScripts::NumberOfIntroTextLinesThisFrame; return 0; } case COMMAND_SET_TEXT_SCALE: { CollectParameters(&m_nIp, 2); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fScaleX = *(float*)&ScriptParams[0]; CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fScaleY = *(float*)&ScriptParams[1]; return 0; } case COMMAND_SET_TEXT_COLOUR: { CollectParameters(&m_nIp, 4); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_sColor = CRGBA(ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3]); return 0; } case COMMAND_SET_TEXT_JUSTIFY: { CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bJustify = (ScriptParams[0] != 0); return 0; } case COMMAND_SET_TEXT_CENTRE: { CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bCentered = (ScriptParams[0] != 0); return 0; } case COMMAND_SET_TEXT_WRAPX: { CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fWrapX = *(float*)&ScriptParams[0]; return 0; } /* case COMMAND_SET_TEXT_CENTRE_SIZE: { CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fCenterSize = *(float*)&ScriptParams[0]; return 0; } */ case COMMAND_SET_TEXT_BACKGROUND: { CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bBackground = (ScriptParams[0] != 0); return 0; } /* case COMMAND_SET_TEXT_BACKGROUND_COLOUR: { CollectParameters(&m_nIp, 4); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_sBackgroundColor = CRGBA(ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3]); return 0; } case COMMAND_SET_TEXT_BACKGROUND_ONLY_TEXT: { CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bBackgroundOnly = (ScriptParams[0] != 0); return 0; } */ case COMMAND_SET_TEXT_PROPORTIONAL: { CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bTextProportional = (ScriptParams[0] != 0); return 0; } /* case COMMAND_SET_TEXT_FONT: { CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_nFont = ScriptParams[0]; return 0; } case COMMAND_INDUSTRIAL_PASSED: CStats::IndustrialPassed = true; DMAudio.PlayRadioAnnouncement(STREAMED_SOUND_ANNOUNCE_COMMERCIAL_OPEN); return 0; case COMMAND_COMMERCIAL_PASSED: CStats::CommercialPassed = true; DMAudio.PlayRadioAnnouncement(STREAMED_SOUND_ANNOUNCE_SUBURBAN_OPEN); return 0; case COMMAND_SUBURBAN_PASSED: CStats::SuburbanPassed = true; return 0; */ case COMMAND_ROTATE_OBJECT: { CollectParameters(&m_nIp, 4); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); float heading = LimitAngleOnCircle( RADTODEG(Atan2(-pObject->GetForward().x, pObject->GetForward().y))); float headingTarget = *(float*)&ScriptParams[1]; #ifdef FIX_BUGS float rotateBy = *(float*)&ScriptParams[2] * CTimer::GetTimeStepFix(); #else float rotateBy = *(float*)&ScriptParams[2]; #endif if (headingTarget == heading) { // using direct comparasion here is fine UpdateCompareFlag(true); return 0; } float angleClockwise = LimitAngleOnCircle(headingTarget - heading); float angleCounterclockwise = LimitAngleOnCircle(heading - headingTarget); float newHeading; if (angleClockwise < angleCounterclockwise) newHeading = rotateBy < angleClockwise ? heading + rotateBy : headingTarget; else newHeading = rotateBy < angleCounterclockwise ? heading - rotateBy : headingTarget; bool obstacleInPath = false; if (ScriptParams[3]) { CVector pos = pObject->GetPosition(); tmp_matrix.SetRotateZ(DEGTORAD(newHeading)); tmp_matrix.GetPosition() += pos; CColModel* pColModel = pObject->GetColModel(); CVector cp1 = tmp_matrix * pColModel->boundingBox.min; CVector cp2 = tmp_matrix * CVector(pColModel->boundingBox.max.x, pColModel->boundingBox.min.y, pColModel->boundingBox.min.z); CVector cp3 = tmp_matrix * CVector(pColModel->boundingBox.min.x, pColModel->boundingBox.max.y, pColModel->boundingBox.min.z); CVector cp4 = tmp_matrix * CVector(pColModel->boundingBox.min.x, pColModel->boundingBox.min.y, pColModel->boundingBox.max.z); int16 collisions; CWorld::FindObjectsIntersectingAngledCollisionBox(pColModel->boundingBox, tmp_matrix, pos, Min(cp1.x, Min(cp2.x, Min(cp3.x, cp4.x))), Min(cp1.y, Min(cp2.y, Min(cp3.y, cp4.y))), Max(cp1.x, Max(cp2.x, Max(cp3.x, cp4.x))), Max(cp1.y, Max(cp2.y, Max(cp3.y, cp4.y))), &collisions, 2, nil, false, true, true, false, false); if (collisions > 0) obstacleInPath = true; } if (obstacleInPath) { UpdateCompareFlag(true); return 0; } pObject->SetHeading(DEGTORAD(newHeading)); pObject->GetMatrix().UpdateRW(); pObject->UpdateRwFrame(); UpdateCompareFlag(newHeading == headingTarget); // using direct comparasion here is fine return 0; } case COMMAND_SLIDE_OBJECT: { CollectParameters(&m_nIp, 8); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CVector pos = pObject->GetPosition(); CVector posTarget = *(CVector*)&ScriptParams[1]; CVector slideBy = *(CVector*)&ScriptParams[4]; #ifdef FIX_BUGS // the check is a hack for original script, where some objects are moved // via SLIDE_OBJECT instead of SET_OBJECT_POSITION // assuming the slide will take exactly one frame, which is true // only without accounting time step (which is a bug) if (!IsSlideObjectUsedWrongByScript(posTarget, slideBy)) slideBy *= CTimer::GetTimeStepFix(); #endif if (posTarget == pos) { // using direct comparasion here is fine UpdateCompareFlag(true); return 0; } CVector posDiff = pos - posTarget; CVector newPosition; if (posDiff.x < 0) newPosition.x = -posDiff.x < slideBy.x ? posTarget.x : pos.x + slideBy.x; else newPosition.x = posDiff.x < slideBy.x ? posTarget.x : pos.x - slideBy.x; if (posDiff.y < 0) newPosition.y = -posDiff.y < slideBy.y ? posTarget.y : pos.y + slideBy.y; else newPosition.y = posDiff.y < slideBy.y ? posTarget.y : pos.y - slideBy.y; if (posDiff.z < 0) newPosition.z = -posDiff.z < slideBy.z ? posTarget.z : pos.z + slideBy.z; else newPosition.z = posDiff.z < slideBy.z ? posTarget.z : pos.z - slideBy.z; bool obstacleInPath = false; if (ScriptParams[7]) { tmp_matrix = pObject->GetMatrix(); tmp_matrix.GetPosition() = newPosition; CColModel* pColModel = pObject->GetColModel(); CVector cp1 = tmp_matrix * pColModel->boundingBox.min; CVector cp2 = tmp_matrix * CVector(pColModel->boundingBox.max.x, pColModel->boundingBox.min.y, pColModel->boundingBox.min.z); CVector cp3 = tmp_matrix * CVector(pColModel->boundingBox.min.x, pColModel->boundingBox.max.y, pColModel->boundingBox.min.z); CVector cp4 = tmp_matrix * CVector(pColModel->boundingBox.min.x, pColModel->boundingBox.min.y, pColModel->boundingBox.max.z); int16 collisions; CWorld::FindObjectsIntersectingAngledCollisionBox(pColModel->boundingBox, tmp_matrix, newPosition, Min(cp1.x, Min(cp2.x, Min(cp3.x, cp4.x))), Min(cp1.y, Min(cp2.y, Min(cp3.y, cp4.y))), Max(cp1.x, Max(cp2.x, Max(cp3.x, cp4.x))), Max(cp1.y, Max(cp2.y, Max(cp3.y, cp4.y))), &collisions, 2, nil, false, true, true, false, false); if (collisions > 0) obstacleInPath = true; } if (obstacleInPath) { UpdateCompareFlag(true); return 0; } pObject->Teleport(newPosition); UpdateCompareFlag(newPosition == posTarget); // using direct comparasion here is fine return 0; } case COMMAND_REMOVE_CHAR_ELEGANTLY: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); if (pPed && pPed->CharCreatedBy == MISSION_CHAR){ CWorld::RemoveReferencesToDeletedObject(pPed); if (pPed->bInVehicle && pPed->m_pMyVehicle) CTheScripts::RemoveThisPed(pPed); else{ pPed->CharCreatedBy = RANDOM_CHAR; pPed->bRespondsToThreats = true; pPed->bScriptObjectiveCompleted = false; pPed->ClearLeader(); --CPopulation::ms_nTotalMissionPeds; pPed->bFadeOut = true; CWorld::RemoveReferencesToDeletedObject(pPed); } } if (m_bIsMissionScript) CTheScripts::MissionCleanUp.RemoveEntityFromList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_SET_CHAR_STAY_IN_SAME_PLACE: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bKindaStayInSamePlace = (ScriptParams[1] != 0); return 0; } /* case COMMAND_IS_NASTY_GAME: UpdateCompareFlag(CGame::nastyGame); return 0; */ case COMMAND_UNDRESS_CHAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); char name[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, name); for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) name[i] = tolower(name[i]); m_nIp += KEY_LENGTH_IN_SCRIPT; pPed->Undress(name); return 0; } case COMMAND_DRESS_CHAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->Dress(); return 0; } /* case COMMAND_START_CHASE_SCENE: CollectParameters(&m_nIp, 1); CTimer::Suspend(); CStreaming::DeleteAllRwObjects(); CRecordDataForChase::StartChaseScene(*(float*)&ScriptParams[0]); CTimer::Resume(); return 0; case COMMAND_STOP_CHASE_SCENE: CRecordDataForChase::CleanUpChaseScene(); return 0; case COMMAND_IS_EXPLOSION_IN_AREA: { CollectParameters(&m_nIp, 7); float infX = *(float*)&ScriptParams[1]; float infY = *(float*)&ScriptParams[2]; float infZ = *(float*)&ScriptParams[3]; float supX = *(float*)&ScriptParams[4]; float supY = *(float*)&ScriptParams[5]; float supZ = *(float*)&ScriptParams[6]; if (infX > supX) { infX = *(float*)&ScriptParams[4]; supX = *(float*)&ScriptParams[1]; } if (infY > supY) { infY = *(float*)&ScriptParams[5]; supY = *(float*)&ScriptParams[2]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[6]; supZ = *(float*)&ScriptParams[3]; } UpdateCompareFlag(CExplosion::TestForExplosionInArea((eExplosionType)ScriptParams[0], infX, supX, infY, supY, infZ, supZ)); return 0; } case COMMAND_IS_EXPLOSION_IN_ZONE: { CollectParameters(&m_nIp, 1); char zone[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, zone); int zone_id = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_DEFAULT); if (zone_id != -1) m_nIp += KEY_LENGTH_IN_SCRIPT; CZone* pZone = CTheZones::GetNavigationZone(zone_id); UpdateCompareFlag(CExplosion::TestForExplosionInArea((eExplosionType)ScriptParams[0], pZone->minx, pZone->maxx, pZone->miny, pZone->maxy, pZone->minz, pZone->maxz)); return 0; } case COMMAND_START_DRUG_DROP_OFF: CPlane::CreateDropOffCesna(); return 0; case COMMAND_HAS_DROP_OFF_PLANE_BEEN_SHOT_DOWN: UpdateCompareFlag(CPlane::HasDropOffCesnaBeenShotDown()); return 0; case COMMAND_FIND_DROP_OFF_PLANE_COORDINATES: { CVector pos = CPlane::FindDropOffCesnaCoordinates(); *(CVector*)&ScriptParams[0] = pos; StoreParameters(&m_nIp, 3); return 0; } case COMMAND_CREATE_FLOATING_PACKAGE: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_FLOATPACKAGE1, PICKUP_FLOATINGPACKAGE, 0); StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_PLACE_OBJECT_RELATIVE_TO_CAR: { CollectParameters(&m_nIp, 5); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pVehicle); CVector offset = *(CVector*)&ScriptParams[2]; CPhysical::PlacePhysicalRelativeToOtherPhysical(pVehicle, pObject, offset); return 0; } case COMMAND_MAKE_OBJECT_TARGETTABLE: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CPlayerPed* pPlayerPed = CWorld::Players[CWorld::PlayerInFocus].m_pPed; script_assert(pPlayerPed); pPlayerPed->MakeObjectTargettable(ScriptParams[0]); return 0; } case COMMAND_ADD_ARMOUR_TO_PLAYER: { CollectParameters(&m_nIp, 2); CPlayerPed* pPlayerPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPlayerPed); pPlayerPed->m_fArmour = clamp(pPlayerPed->m_fArmour + ScriptParams[1], 0.0f, CWorld::Players[ScriptParams[0]].m_nMaxArmour); return 0; } case COMMAND_ADD_ARMOUR_TO_CHAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->m_fArmour = clamp(pPed->m_fArmour + ScriptParams[1], 0.0f, 100.0f); return 0; } case COMMAND_OPEN_GARAGE: { CollectParameters(&m_nIp, 1); CGarages::OpenGarage(ScriptParams[0]); return 0; } case COMMAND_CLOSE_GARAGE: { CollectParameters(&m_nIp, 1); CGarages::CloseGarage(ScriptParams[0]); return 0; } case COMMAND_WARP_CHAR_FROM_CAR_TO_COORD: { CollectParameters(&m_nIp, 4); CPed *pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); if (pPed->bInVehicle){ if (pPed->m_pMyVehicle->bIsBus) pPed->bRenderPedInCar = true; if (pPed->m_pMyVehicle->pDriver == pPed){ pPed->m_pMyVehicle->RemoveDriver(); pPed->m_pMyVehicle->SetStatus(STATUS_ABANDONED); pPed->m_pMyVehicle->bEngineOn = false; pPed->m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; pPed->m_pMyVehicle->SetMoveSpeed(0.0f, 0.0f, -0.00001f); pPed->m_pMyVehicle->SetTurnSpeed(0.0f, 0.0f, 0.0f); }else{ pPed->m_pMyVehicle->RemovePassenger(pPed); } if (pPed->m_vehDoor) { if (pPed->GetPedState() == PED_EXIT_CAR || pPed->GetPedState() == PED_DRAG_FROM_CAR) { uint8 flags = 0; if (pPed->m_pMyVehicle->IsBike()) { if (pPed->m_vehDoor == CAR_DOOR_LF || pPed->m_vehDoor == CAR_DOOR_RF || pPed->m_vehDoor == CAR_WINDSCREEN) flags = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_RF; else if (pPed->m_vehDoor == CAR_DOOR_LR || pPed->m_vehDoor == CAR_DOOR_RR) flags = CAR_DOOR_FLAG_LR | CAR_DOOR_FLAG_RR; } else { switch (pPed->m_vehDoor) { case CAR_DOOR_LF: flags = pPed->m_pMyVehicle->m_nNumMaxPassengers != 0 ? CAR_DOOR_FLAG_LF : CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; break; case CAR_DOOR_LR: flags = pPed->m_pMyVehicle->m_nNumMaxPassengers != 0 ? CAR_DOOR_FLAG_RF : CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; break; case CAR_DOOR_RF: flags = CAR_DOOR_FLAG_RF; break; case CAR_DOOR_RR: flags = CAR_DOOR_FLAG_RR; break; } } pPed->m_pMyVehicle->m_nGettingOutFlags &= ~flags; pPed->m_pMyVehicle->ProcessOpenDoor(pPed->m_vehDoor, ANIM_STD_NUM, 0.0f); } } } pPed->RemoveInCarAnims(); pPed->bInVehicle = false; pPed->m_pMyVehicle = nil; pPed->SetPedState(PED_IDLE); pPed->m_nLastPedState = PED_NONE; pPed->bUsesCollision = true; pPed->SetMoveSpeed(0.0f, 0.0f, 0.0f); pPed->ReplaceWeaponWhenExitingVehicle(); if (pPed->m_pVehicleAnim) pPed->m_pVehicleAnim->blendDelta = -1000.0f; pPed->m_pVehicleAnim = nil; pPed->RestartNonPartialAnims(); pPed->SetMoveState(PEDMOVE_NONE); CAnimManager::BlendAnimation(pPed->GetClump(), pPed->m_animGroup, ANIM_STD_IDLE, 1000.0f); pos.z += pPed->GetDistanceFromCentreOfMassToBaseOfModel(); pPed->Teleport(pos); CTheScripts::ClearSpaceForMissionEntity(pos, pPed); return 0; } case COMMAND_SET_VISIBILITY_OF_CLOSEST_OBJECT_OF_TYPE: { CollectParameters(&m_nIp, 6); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float range = *(float*)&ScriptParams[3]; int mi = ScriptParams[4] < 0 ? CTheScripts::UsedObjectArray[-ScriptParams[4]].index : ScriptParams[4]; int16 total; CEntity* apEntities[16]; CWorld::FindObjectsOfTypeInRange(mi, pos, range, true, &total, 16, apEntities, true, false, false, true, true); if (total == 0) CWorld::FindObjectsOfTypeInRangeSectorList(mi, CWorld::GetBigBuildingList(LEVEL_GENERIC), pos, range, true, &total, 16, apEntities); if (total == 0) CWorld::FindObjectsOfTypeInRangeSectorList(mi, CWorld::GetBigBuildingList(CTheZones::GetLevelFromPosition(&pos)), pos, range, true, &total, 16, apEntities); CEntity* pClosestEntity = nil; float min_dist = 2.0f * range; for (int i = 0; i < total; i++) { float dist = (apEntities[i]->GetPosition() - pos).Magnitude(); if (dist < min_dist) { min_dist = dist; pClosestEntity = apEntities[i]; } } if (pClosestEntity) { pClosestEntity->bIsVisible = (ScriptParams[5] != 0); CTheScripts::AddToInvisibilitySwapArray(pClosestEntity, ScriptParams[5] != 0); } return 0; } /* case COMMAND_HAS_CHAR_SPOTTED_CHAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pTarget); UpdateCompareFlag(pPed->OurPedCanSeeThisOne(pTarget)); return 0; } */ case COMMAND_SET_CHAR_OBJ_HAIL_TAXI: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_HAIL_TAXI); return 0; } case COMMAND_HAS_OBJECT_BEEN_DAMAGED: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); UpdateCompareFlag(pObject->bRenderDamaged || !pObject->bIsVisible); return 0; } /* case COMMAND_START_KILL_FRENZY_HEADSHOT: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 8); CDarkel::StartFrenzy((eWeaponType)ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], text, ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7] != 0, true); return 0; } case COMMAND_ACTIVATE_MILITARY_CRANE: { CollectParameters(&m_nIp, 10); float infX = *(float*)&ScriptParams[2]; float infY = *(float*)&ScriptParams[3]; float supX = *(float*)&ScriptParams[4]; float supY = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[4]; supX = *(float*)&ScriptParams[2]; } if (infY > supY) { infY = *(float*)&ScriptParams[5]; supY = *(float*)&ScriptParams[3]; } CCranes::ActivateCrane(infX, supX, infY, supY, *(float*)&ScriptParams[6], *(float*)&ScriptParams[7], *(float*)&ScriptParams[8], DEGTORAD(*(float*)&ScriptParams[9]), false, true, *(float*)&ScriptParams[0], *(float*)&ScriptParams[1]); return 0; } */ case COMMAND_WARP_PLAYER_INTO_CAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pVehicle); pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicle); pPed->WarpPedIntoCar(pVehicle); return 0; } case COMMAND_WARP_CHAR_INTO_CAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pVehicle); pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, pVehicle); pPed->WarpPedIntoCar(pVehicle); return 0; } //case COMMAND_SWITCH_CAR_RADIO: //case COMMAND_SET_AUDIO_STREAM: case COMMAND_PRINT_WITH_2_NUMBERS_BIG: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 4); CMessages::AddBigMessageWithNumber(text, ScriptParams[2], ScriptParams[3] - 1, ScriptParams[0], ScriptParams[1], -1, -1, -1, -1); return 0; } /* case COMMAND_PRINT_WITH_3_NUMBERS_BIG: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 5); CMessages::AddBigMessageWithNumber(text, ScriptParams[3], ScriptParams[4] - 1, ScriptParams[0], ScriptParams[1], ScriptParams[2], -1, -1, -1); return 0; } case COMMAND_PRINT_WITH_4_NUMBERS_BIG: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 6); CMessages::AddBigMessageWithNumber(text, ScriptParams[4], ScriptParams[5] - 1, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], -1, -1); return 0; } case COMMAND_PRINT_WITH_5_NUMBERS_BIG: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 7); CMessages::AddBigMessageWithNumber(text, ScriptParams[5], ScriptParams[6] - 1, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], -1); return 0; } case COMMAND_PRINT_WITH_6_NUMBERS_BIG: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 8); CMessages::AddBigMessageWithNumber(text, ScriptParams[6], ScriptParams[7] - 1, ScriptParams[0], ScriptParams[1], ScriptParams[2], ScriptParams[3], ScriptParams[4], ScriptParams[5]); return 0; } */ case COMMAND_SET_CHAR_WAIT_STATE: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->SetWaitState((eWaitState)ScriptParams[1], ScriptParams[2] >= 0 ? &ScriptParams[2] : nil); return 0; } case COMMAND_SET_CAMERA_BEHIND_PLAYER: TheCamera.SetCameraDirectlyBehindForFollowPed_CamOnAString(); return 0; /* case COMMAND_SET_MOTION_BLUR: CollectParameters(&m_nIp, 1); TheCamera.SetMotionBlur(0, 0, 0, 0, ScriptParams[0]); return 0; case COMMAND_PRINT_STRING_IN_STRING: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* string = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 2); CMessages::AddMessageWithString(text, ScriptParams[0], ScriptParams[1], string); return 0; } */ case COMMAND_CREATE_RANDOM_CHAR: { CollectParameters(&m_nIp, 3); CZoneInfo zoneinfo; CTheZones::GetZoneInfoForTimeOfDay(&CWorld::Players[CWorld::PlayerInFocus].GetPos(), &zoneinfo); int mi; ePedType pedtype = PEDTYPE_COP; int attempt = 0; while (pedtype != PEDTYPE_CIVMALE && pedtype != PEDTYPE_CIVFEMALE && attempt < 5) { mi = CPopulation::ChooseCivilianOccupation(zoneinfo.pedGroup); if (CModelInfo::GetModelInfo(mi)->GetRwObject()) pedtype = ((CPedModelInfo*)(CModelInfo::GetModelInfo(mi)))->m_pedType; attempt++; } if (!CModelInfo::GetModelInfo(mi)->GetRwObject()) { mi = MI_MALE01; pedtype = ((CPedModelInfo*)(CModelInfo::GetModelInfo(mi)))->m_pedType; } CPed* ped = new CCivilianPed(pedtype, mi); ped->CharCreatedBy = MISSION_CHAR; ped->bRespondsToThreats = false; ped->bAllowMedicsToReviveMe = false; ped->bIsPlayerFriend = false; CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pos.z += 1.0f; ped->SetPosition(pos); ped->SetOrientation(0.0f, 0.0f, 0.0f); CTheScripts::ClearSpaceForMissionEntity(pos, ped); if (m_bIsMissionScript) ped->bIsStaticWaitingForCollision = true; CWorld::Add(ped); ped->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pos); CPopulation::ms_nTotalMissionPeds++; ScriptParams[0] = CPools::GetPedPool()->GetIndex(ped); StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_STEAL_ANY_CAR); return 0; } /* case COMMAND_SET_2_REPEATED_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, nil, nil, nil, nil); return 0; } case COMMAND_SET_2_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, nil, nil, nil, nil); return 0; } case COMMAND_SET_3_REPEATED_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, text3, nil, nil, nil); return 0; } case COMMAND_SET_3_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, text3, nil, nil, nil); return 0; } case COMMAND_SET_4_REPEATED_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, text3, text4, nil, nil); return 0; } case COMMAND_SET_4_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, text3, text4, nil, nil); return 0; } */ case COMMAND_IS_SNIPER_BULLET_IN_AREA: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } UpdateCompareFlag(CBulletInfo::TestForSniperBullet(infX, supX, infY, supY, infZ, supZ)); return 0; } /* case COMMAND_GIVE_PLAYER_DETONATOR: CGarages::GivePlayerDetonator(); return 0; */ #ifdef GTA_SCRIPT_COLLECTIVE case COMMAND_SET_COLL_OBJ_STEAL_ANY_CAR: CollectParameters(&m_nIp, 1); CTheScripts::SetObjectiveForAllPedsInCollective(ScriptParams[0], OBJECTIVE_STEAL_ANY_CAR); return 0; #endif case COMMAND_SET_OBJECT_VELOCITY: { CollectParameters(&m_nIp, 4); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); pObject->SetMoveSpeed(*(CVector*)&ScriptParams[1] * METERS_PER_SECOND_TO_GAME_SPEED); return 0; } case COMMAND_SET_OBJECT_COLLISION: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); pObject->bUsesCollision = (ScriptParams[1] != 0); return 0; } case COMMAND_IS_ICECREAM_JINGLE_ON: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); // Adding this check to correspond to command name. // All original game scripts always assume that the vehicle is actually Mr. Whoopee, // but maybe there are mods that use it as "is alarm activated"? script_assert(pVehicle->GetModelIndex() == MI_MRWHOOP); UpdateCompareFlag(pVehicle->m_bSirenOrAlarm); return 0; } default: script_assert(0); } return -1; } int8 CRunningScript::ProcessCommands900To999(int32 command) { char str[52]; char onscreen_str[KEY_LENGTH_IN_SCRIPT]; switch (command) { case COMMAND_PRINT_STRING_IN_STRING_NOW: { wchar* source = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* pstr = CTheScripts::GetTextByKeyFromScript(&m_nIp); CollectParameters(&m_nIp, 2); CMessages::AddMessageJumpQWithString(source, ScriptParams[0], ScriptParams[1], pstr); return 0; } //case COMMAND_PRINT_STRING_IN_STRING_SOON: /* case COMMAND_SET_5_REPEATED_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text5 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, text3, text4, text5, nil); return 0; } case COMMAND_SET_5_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text5 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, text3, text4, text5, nil); return 0; } case COMMAND_SET_6_REPEATED_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text5 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text6 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_Repeatedly(ScriptParams[0], text1, text2, text3, text4, text5, text6); return 0; } case COMMAND_SET_6_PHONE_MESSAGES: { CollectParameters(&m_nIp, 1); wchar* text1 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text2 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text3 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text4 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text5 = CTheScripts::GetTextByKeyFromScript(&m_nIp); wchar* text6 = CTheScripts::GetTextByKeyFromScript(&m_nIp); gPhoneInfo.SetPhoneMessage_JustOnce(ScriptParams[0], text1, text2, text3, text4, text5, text6); return 0; } */ case COMMAND_IS_POINT_OBSCURED_BY_A_MISSION_ENTITY: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0] - *(float*)&ScriptParams[3]; float supX = *(float*)&ScriptParams[0] + *(float*)&ScriptParams[3]; float infY = *(float*)&ScriptParams[1] - *(float*)&ScriptParams[4]; float supY = *(float*)&ScriptParams[1] + *(float*)&ScriptParams[4]; float infZ = *(float*)&ScriptParams[2] - *(float*)&ScriptParams[5]; float supZ = *(float*)&ScriptParams[2] + *(float*)&ScriptParams[5]; if (infX > supX) { float tmp = infX; infX = supX; supX = tmp; } if (infY > supY) { float tmp = infY; infY = supY; supY = tmp; } if (infZ > supZ) { float tmp = infZ; infZ = supZ; supZ = tmp; } int16 total; CWorld::FindMissionEntitiesIntersectingCube(CVector(infX, infY, infZ), CVector(supX, supY, supZ), &total, 2, nil, true, true, true); UpdateCompareFlag(total > 0); return 0; } case COMMAND_LOAD_ALL_MODELS_NOW: #ifdef FIX_BUGS CTimer::Suspend(); #else CTimer::Stop(); #endif CStreaming::LoadAllRequestedModels(false); #ifdef FIX_BUGS CTimer::Resume(); #else CTimer::Update(); #endif return 0; case COMMAND_ADD_TO_OBJECT_VELOCITY: { CollectParameters(&m_nIp, 4); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); pObject->AddToMoveSpeed(*(CVector*)&ScriptParams[1] * METERS_PER_SECOND_TO_GAME_SPEED); return 0; } case COMMAND_DRAW_SPRITE: { CollectParameters(&m_nIp, 9); CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_bIsUsed = true; CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_nTextureId = ScriptParams[0] - 1; CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_sRect = CRect( *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[1] + *(float*)&ScriptParams[3], *(float*)&ScriptParams[2] + *(float*)&ScriptParams[4]); CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_sColor = CRGBA(ScriptParams[5], ScriptParams[6], ScriptParams[7], ScriptParams[8]); CTheScripts::NumberOfIntroRectanglesThisFrame++; return 0; } case COMMAND_DRAW_RECT: { CollectParameters(&m_nIp, 8); CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_bIsUsed = true; CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_nTextureId = -1; CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_sRect = CRect( *(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[0] + *(float*)&ScriptParams[2], *(float*)&ScriptParams[1] + *(float*)&ScriptParams[3]); CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_sColor = CRGBA(ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7]); CTheScripts::NumberOfIntroRectanglesThisFrame++; return 0; } case COMMAND_LOAD_SPRITE: { CollectParameters(&m_nIp, 1); strncpy(str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) str[i] = tolower(str[i]); m_nIp += KEY_LENGTH_IN_SCRIPT; int slot = CTxdStore::FindTxdSlot("script"); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(slot); CTheScripts::ScriptSprites[ScriptParams[0] - 1].SetTexture(str); CTxdStore::PopCurrentTxd(); return 0; } case COMMAND_LOAD_TEXTURE_DICTIONARY: { strcpy(str, "models\\"); strncpy(str + sizeof("models\\"), (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); strcat(str, ".txd"); m_nIp += KEY_LENGTH_IN_SCRIPT; int slot = CTxdStore::FindTxdSlot("script"); if (slot == -1) slot = CTxdStore::AddTxdSlot("script"); CTxdStore::LoadTxd(slot, str); CTxdStore::AddRef(slot); return 0; } case COMMAND_REMOVE_TEXTURE_DICTIONARY: { CTheScripts::RemoveScriptTextureDictionary(); return 0; } case COMMAND_SET_OBJECT_DYNAMIC: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); if (ScriptParams[1]) { if (pObject->bIsStatic) { pObject->SetIsStatic(false); pObject->AddToMovingList(); } } else { if (!pObject->bIsStatic) { pObject->SetIsStatic(true); pObject->RemoveFromMovingList(); } } return 0; } /* case COMMAND_SET_CHAR_ANIM_SPEED: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CAnimBlendAssociation* pAssoc = RpAnimBlendClumpGetFirstAssociation(pPed->GetClump()); if (pAssoc) pAssoc->speed = *(float*)&ScriptParams[1]; return 0; } */ case COMMAND_PLAY_MISSION_PASSED_TUNE: { CollectParameters(&m_nIp, 1); DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); DMAudio.PlayFrontEndTrack(ScriptParams[0] + STREAMED_SOUND_MISSION_COMPLETED - 1, 0); return 0; } case COMMAND_CLEAR_AREA: { CollectParameters(&m_nIp, 5); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CWorld::ClearExcitingStuffFromArea(pos, *(float*)&ScriptParams[3], ScriptParams[4]); return 0; } case COMMAND_FREEZE_ONSCREEN_TIMER: CollectParameters(&m_nIp, 1); CUserDisplay::OnscnTimer.m_bDisabled = ScriptParams[0] != 0; return 0; case COMMAND_SWITCH_CAR_SIREN: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->m_bSirenOrAlarm = ScriptParams[1] != 0; return 0; } /* case COMMAND_SWITCH_PED_ROADS_ON_ANGLED: { CollectParameters(&m_nIp, 7); ThePaths.SwitchRoadsInAngledArea(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5], *(float*)&ScriptParams[6], 0, 1); return 0; } case COMMAND_SWITCH_PED_ROADS_OFF_ANGLED: CollectParameters(&m_nIp, 7); ThePaths.SwitchRoadsInAngledArea(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5], *(float*)&ScriptParams[6], 0, 0); return 0; case COMMAND_SWITCH_ROADS_ON_ANGLED: CollectParameters(&m_nIp, 7); ThePaths.SwitchRoadsInAngledArea(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5], *(float*)&ScriptParams[6], 1, 1); return 0; case COMMAND_SWITCH_ROADS_OFF_ANGLED: CollectParameters(&m_nIp, 7); ThePaths.SwitchRoadsInAngledArea(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], *(float*)&ScriptParams[5], *(float*)&ScriptParams[6], 1, 0); return 0; */ case COMMAND_SET_CAR_WATERTIGHT: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); if (pVehicle->IsBike()) { CBike* pBike = (CBike*)pVehicle; pBike->bWaterTight = ScriptParams[1] != 0; } else if (pVehicle->IsCar()) { CAutomobile* pCar = (CAutomobile*)pVehicle; pCar->bWaterTight = ScriptParams[1] != 0; } return 0; } case COMMAND_ADD_MOVING_PARTICLE_EFFECT: { CollectParameters(&m_nIp, 12); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float size = Max(0.0f, *(float*)&ScriptParams[7]); eParticleObjectType type = (eParticleObjectType)ScriptParams[0]; RwRGBA color; if (type == POBJECT_SMOKE_TRAIL){ color.alpha = -1; color.red = ScriptParams[8]; color.green = ScriptParams[9]; color.blue = ScriptParams[10]; }else{ color.alpha = color.red = color.blue = color.green = 0; } CVector target = *(CVector*)&ScriptParams[4]; CParticleObject::AddObject(type, pos, target, size, ScriptParams[11], color, 1); return 0; } case COMMAND_SET_CHAR_CANT_BE_DRAGGED_OUT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bDontDragMeOutCar = ScriptParams[1] != 0; return 0; } case COMMAND_TURN_CAR_TO_FACE_COORD: { CollectParameters(&m_nIp, 3); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); const CVector& pos = pVehicle->GetPosition(); float heading = CGeneral::GetATanOfXY(pos.x - *(float*)&ScriptParams[1], pos.y - *(float*)&ScriptParams[2]) + HALFPI; if (heading > TWOPI) heading -= TWOPI; pVehicle->SetHeading(heading); return 0; } /* case COMMAND_IS_CRANE_LIFTING_CAR: { CollectParameters(&m_nIp, 3); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[2]); UpdateCompareFlag(CCranes::IsThisCarPickedUp(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], pVehicle)); return 0; } */ case COMMAND_DRAW_SPHERE: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); C3dMarkers::PlaceMarkerSet((uintptr)this + m_nIp, MARKERTYPE_CYLINDER, pos, *(float*)&ScriptParams[3], SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, SPHERE_MARKER_A, SPHERE_MARKER_PULSE_PERIOD, SPHERE_MARKER_PULSE_FRACTION, 0); return 0; } case COMMAND_SET_CAR_STATUS: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->SetStatus(ScriptParams[1]); return 0; } case COMMAND_IS_CHAR_MALE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->m_nPedType != PEDTYPE_CIVFEMALE && pPed->m_nPedType != PEDTYPE_PROSTITUTE); return 0; } case COMMAND_SCRIPT_NAME: { strncpy(str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) str[i] = tolower(str[i]); m_nIp += KEY_LENGTH_IN_SCRIPT; strncpy(m_abScriptName, str, KEY_LENGTH_IN_SCRIPT); return 0; } /* case COMMAND_CHANGE_GARAGE_TYPE_WITH_CAR_MODEL: { CollectParameters(&m_nIp, 3); CGarages::ChangeGarageType(ScriptParams[0], ScriptParams[1], ScriptParams[2]); return 0; } case COMMAND_FIND_DRUG_PLANE_COORDINATES: *(CVector*)&ScriptParams[0] = CPlane::FindDrugPlaneCoordinates(); StoreParameters(&m_nIp, 3); return 0; */ case COMMAND_SAVE_INT_TO_DEBUG_FILE: // TODO: implement something here CollectParameters(&m_nIp, 1); return 0; case COMMAND_SAVE_FLOAT_TO_DEBUG_FILE: CollectParameters(&m_nIp, 1); return 0; case COMMAND_SAVE_NEWLINE_TO_DEBUG_FILE: return 0; case COMMAND_POLICE_RADIO_MESSAGE: CollectParameters(&m_nIp, 3); DMAudio.PlaySuspectLastSeen(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2]); return 0; case COMMAND_SET_CAR_STRONG: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bTakeLessDamage = ScriptParams[1] != 0; return 0; } case COMMAND_REMOVE_ROUTE: CollectParameters(&m_nIp, 1); CRouteNode::RemoveRoute(ScriptParams[0]); return 0; case COMMAND_SWITCH_RUBBISH: CollectParameters(&m_nIp, 1); CRubbish::SetVisibility(ScriptParams[0] != 0); return 0; case COMMAND_REMOVE_PARTICLE_EFFECTS_IN_AREA: { CollectParameters(&m_nIp, 6); float x1 = *(float*)&ScriptParams[0]; float y1 = *(float*)&ScriptParams[1]; float z1 = *(float*)&ScriptParams[2]; float x2 = *(float*)&ScriptParams[3]; float y2 = *(float*)&ScriptParams[4]; float z2 = *(float*)&ScriptParams[5]; CParticleObject* tmp = CParticleObject::pCloseListHead; while (tmp) { CParticleObject* next = tmp->m_pNext; if (tmp->IsWithinArea(x1, y1, z1, x2, y2, z2)) tmp->RemoveObject(); tmp = next; } tmp = CParticleObject::pFarListHead; while (tmp) { CParticleObject* next = tmp->m_pNext; if (tmp->IsWithinArea(x1, y1, z1, x2, y2, z2)) tmp->RemoveObject(); tmp = next; } return 0; } case COMMAND_SWITCH_STREAMING: CollectParameters(&m_nIp, 1); CStreaming::ms_disableStreaming = ScriptParams[0] == 0; return 0; case COMMAND_IS_GARAGE_OPEN: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CGarages::IsGarageOpen(ScriptParams[0])); return 0; case COMMAND_IS_GARAGE_CLOSED: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CGarages::IsGarageClosed(ScriptParams[0])); return 0; /* case COMMAND_START_CATALINA_HELI: CHeli::StartCatalinaFlyBy(); return 0; case COMMAND_CATALINA_HELI_TAKE_OFF: CHeli::CatalinaTakeOff(); return 0; case COMMAND_REMOVE_CATALINA_HELI: CHeli::RemoveCatalinaHeli(); return 0; case COMMAND_HAS_CATALINA_HELI_BEEN_SHOT_DOWN: UpdateCompareFlag(CHeli::HasCatalinaBeenShotDown()); return 0; */ case COMMAND_SWAP_NEAREST_BUILDING_MODEL: { CollectParameters(&m_nIp, 6); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = *(float*)&ScriptParams[3]; int mi1 = ScriptParams[4] >= 0 ? ScriptParams[4] : CTheScripts::UsedObjectArray[-ScriptParams[4]].index; int mi2 = ScriptParams[5] >= 0 ? ScriptParams[5] : CTheScripts::UsedObjectArray[-ScriptParams[5]].index; int16 total; CEntity* apEntities[16]; CWorld::FindObjectsOfTypeInRange(mi1, pos, radius, true, &total, 16, apEntities, true, false, false, false, false); if (total == 0) CWorld::FindObjectsOfTypeInRangeSectorList(mi1, CWorld::GetBigBuildingList(LEVEL_GENERIC), pos, radius, true, &total, 16, apEntities); if (total == 0) CWorld::FindObjectsOfTypeInRangeSectorList(mi1, CWorld::GetBigBuildingList(CTheZones::GetLevelFromPosition(&pos)), pos, radius, true, &total, 16, apEntities); CEntity* pClosestEntity = nil; float min_dist = 2.0f * radius; for (int i = 0; i < total; i++) { float dist = (apEntities[i]->GetPosition() - pos).Magnitude(); if (dist < min_dist) { min_dist = dist; pClosestEntity = apEntities[i]; } } if (!pClosestEntity) { printf("Failed to find building\n"); return 0; } CBuilding* pReplacedBuilding = ((CBuilding*)pClosestEntity); pReplacedBuilding->ReplaceWithNewModel(mi2); CTheScripts::AddToBuildingSwapArray(pReplacedBuilding, mi1, mi2); return 0; } case COMMAND_SWITCH_WORLD_PROCESSING: CollectParameters(&m_nIp, 1); CWorld::bProcessCutsceneOnly = ScriptParams[0] == 0; return 0; case COMMAND_REMOVE_ALL_PLAYER_WEAPONS: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); pPed->ClearWeapons(); return 0; } /* case COMMAND_GRAB_CATALINA_HELI: { CHeli* pHeli = CHeli::FindPointerToCatalinasHeli(); ScriptParams[0] = pHeli ? CPools::GetVehiclePool()->GetIndex(pHeli) : -1; StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_CLEAR_AREA_OF_CARS: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } CWorld::ClearCarsFromArea(infX, infY, infZ, supX, supY, supZ); return 0; } case COMMAND_SET_ROTATING_GARAGE_DOOR: CollectParameters(&m_nIp, 1); CGarages::SetGarageDoorToRotate(ScriptParams[0]); return 0; case COMMAND_ADD_SPHERE: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float radius = *(float*)&ScriptParams[3]; CTheScripts::GetActualScriptSphereIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CTheScripts::AddScriptSphere((uintptr)this + m_nIp, pos, radius); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_REMOVE_SPHERE: CollectParameters(&m_nIp, 1); CTheScripts::RemoveScriptSphere(ScriptParams[0]); return 0; /* case COMMAND_CATALINA_HELI_FLY_AWAY: CHeli::MakeCatalinaHeliFlyAway(); return 0; */ case COMMAND_SET_EVERYONE_IGNORE_PLAYER: { CollectParameters(&m_nIp, 2); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); if (ScriptParams[1]) { pPed->m_pWanted->m_bIgnoredByEveryone = true; CWorld::StopAllLawEnforcersInTheirTracks(); } else { pPed->m_pWanted->m_bIgnoredByEveryone = false; } return 0; } case COMMAND_STORE_CAR_CHAR_IS_IN_NO_SAVE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_STORE_CAR_PLAYER_IS_IN_NO_SAVE: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); CVehicle* pVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_IS_PHONE_DISPLAYING_MESSAGE: CollectParameters(&m_nIp, 1); UpdateCompareFlag(gPhoneInfo.IsMessageBeingDisplayed(ScriptParams[0])); return 0; */ case COMMAND_DISPLAY_ONSCREEN_TIMER_WITH_STRING: { script_assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); uint16 var = CTheScripts::Read2BytesFromScript(&m_nIp); CollectParameters(&m_nIp, 1); wchar* text = TheText.Get((char*)&CTheScripts::ScriptSpace[m_nIp]); // ??? strncpy(onscreen_str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); m_nIp += KEY_LENGTH_IN_SCRIPT; CUserDisplay::OnscnTimer.AddClock(var, onscreen_str, ScriptParams[0] != 0); return 0; } case COMMAND_DISPLAY_ONSCREEN_COUNTER_WITH_STRING: { script_assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); uint16 var = CTheScripts::Read2BytesFromScript(&m_nIp); CollectParameters(&m_nIp, 1); wchar* text = TheText.Get((char*)&CTheScripts::ScriptSpace[m_nIp]); // ??? strncpy(onscreen_str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); m_nIp += KEY_LENGTH_IN_SCRIPT; CUserDisplay::OnscnTimer.AddCounter(var, ScriptParams[0], onscreen_str, 0); return 0; } case COMMAND_CREATE_RANDOM_CAR_FOR_CAR_PARK: { CollectParameters(&m_nIp, 4); if (CCarCtrl::NumRandomCars >= 30) return 0; int attempts; int model = -1; int index = CGeneral::GetRandomNumberInRange(0, MAXVEHICLESLOADED); for (attempts = 0; attempts < MAXVEHICLESLOADED; attempts++) { if (model != -1) break; model = CStreaming::ms_vehiclesLoaded[index]; if (model == -1) continue; if (CModelInfo::IsCarModel(model) || CModelInfo::IsBikeModel(model)) { switch (model) { case MI_LANDSTAL: case MI_LINERUN: case MI_RIO: case MI_FIRETRUCK: case MI_TRASH: case MI_STRETCH: case MI_VOODOO: case MI_MULE: case MI_AMBULAN: case MI_FBICAR: case MI_MRWHOOP: case MI_BFINJECT: case MI_HUNTER: case MI_POLICE: case MI_ENFORCER: case MI_SECURICA: case MI_PREDATOR: case MI_BUS: case MI_RHINO: case MI_BARRACKS: case MI_CUBAN: case MI_CHOPPER: case MI_ANGEL: case MI_COACH: case MI_RCBANDIT: case MI_ROMERO: case MI_PACKER: case MI_SENTXS: case MI_SQUALO: case MI_SEASPAR: case MI_PIZZABOY: case MI_GANGBUR: case MI_AIRTRAIN: case MI_DEADDODO: case MI_SPEEDER: case MI_REEFER: case MI_TROPIC: case MI_FLATBED: case MI_YANKEE: case MI_CADDY: case MI_ZEBRA: case MI_TOPFUN: case MI_SKIMMER: case MI_RCBARON: case MI_RCRAIDER: case MI_SPARROW: case MI_PATRIOT: case MI_LOVEFIST: case MI_COASTG: case MI_DINGHY: case MI_HERMES: case MI_SABRETUR: case MI_PHEONIX: case MI_WALTON: case MI_COMET: case MI_DELUXO: case MI_BURRITO: case MI_SPAND: case MI_MARQUIS: case MI_BAGGAGE: case MI_KAUFMAN: case MI_MAVERICK: case MI_VCNMAV: case MI_RANCHER: case MI_FBIRANCH: case MI_JETMAX: case MI_HOTRING: case MI_SANDKING: case MI_BLISTAC: case MI_POLMAV: case MI_BOXVILLE: case MI_BENSON: case MI_MESA: case MI_RCGOBLIN: case MI_HOTRINA: case MI_HOTRINB: case MI_BLOODRA: case MI_BLOODRB: case MI_VICECHEE: model = -1; break; case MI_IDAHO: case MI_STINGER: case MI_PEREN: case MI_SENTINEL: case MI_MANANA: case MI_INFERNUS: case MI_PONY: case MI_CHEETAH: case MI_MOONBEAM: case MI_ESPERANT: case MI_TAXI: case MI_WASHING: case MI_BOBCAT: case MI_BANSHEE: case MI_CABBIE: case MI_STALLION: case MI_RUMPO: case MI_ADMIRAL: case MI_PCJ600: case MI_FAGGIO: case MI_FREEWAY: case MI_GLENDALE: case MI_OCEANIC: case MI_SANCHEZ: case MI_SABRE: case MI_REGINA: case MI_VIRGO: case MI_GREENWOO: break; default: printf("CREATE_RANDOM_CAR_FOR_CAR_PARK - Unknown car model %d\n", CStreaming::ms_vehiclesLoaded[index]); model = -1; break; } } else model = -1; if (++index >= 50) index = 0; } if (model == -1) return 0; CVehicle* car; if (CModelInfo::IsBikeModel(model)) { car = new CBike(model, RANDOM_VEHICLE); ((CBike*)(car))->bIsStanding = true; } else car = new CAutomobile(model, RANDOM_VEHICLE); CVector pos = *(CVector*)&ScriptParams[0]; pos.z += car->GetDistanceFromCentreOfMassToBaseOfModel(); car->SetPosition(pos); car->SetHeading(DEGTORAD(*(float*)&ScriptParams[3])); CTheScripts::ClearSpaceForMissionEntity(pos, car); car->SetStatus(STATUS_ABANDONED); car->bIsLocked = false; car->bIsCarParkVehicle = true; CCarCtrl::JoinCarWithRoadSystem(car); car->AutoPilot.m_nCarMission = MISSION_NONE; car->AutoPilot.m_nTempAction = TEMPACT_NONE; car->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; car->AutoPilot.m_nCruiseSpeed = car->AutoPilot.m_fMaxTrafficSpeed = 9.0f; car->AutoPilot.m_nCurrentLane = car->AutoPilot.m_nNextLane = 0; car->bEngineOn = false; car->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pos); CWorld::Add(car); return 0; } /* case COMMAND_IS_COLLISION_IN_MEMORY: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CCollision::ms_collisionInMemory == ScriptParams[0]); return 0; */ case COMMAND_SET_WANTED_MULTIPLIER: CollectParameters(&m_nIp, 1); FindPlayerPed()->m_pWanted->m_fCrimeSensitivity = *(float*)&ScriptParams[0]; return 0; case COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER: TheCamera.SetCameraDirectlyInFrontForFollowPed_CamOnAString(); return 0; /* case COMMAND_IS_CAR_VISIBLY_DAMAGED: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->bIsDamaged); return 0; } */ case COMMAND_DOES_OBJECT_EXIST: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CPools::GetObjectPool()->GetAt(ScriptParams[0])); return 0; case COMMAND_LOAD_SCENE: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; #ifdef FIX_BUGS CTimer::Suspend(); #else CTimer::Stop(); #endif CStreaming::LoadScene(pos); #ifdef FIX_BUGS CTimer::Suspend(); #else CTimer::Update(); #endif return 0; } case COMMAND_ADD_STUCK_CAR_CHECK: { CollectParameters(&m_nIp, 3); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CTheScripts::StuckCars.AddCarToCheck(ScriptParams[0], *(float*)&ScriptParams[1], ScriptParams[2]); return 0; } case COMMAND_REMOVE_STUCK_CAR_CHECK: { CollectParameters(&m_nIp, 1); CTheScripts::StuckCars.RemoveCarFromCheck(ScriptParams[0]); return 0; } case COMMAND_IS_CAR_STUCK: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CTheScripts::StuckCars.HasCarBeenStuckForAWhile(ScriptParams[0])); return 0; case COMMAND_LOAD_MISSION_AUDIO: { CollectParameters(&m_nIp, 1); strncpy(str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) str[i] = tolower(str[i]); m_nIp += KEY_LENGTH_IN_SCRIPT; DMAudio.PreloadMissionAudio(ScriptParams[0] - 1, str); return 0; } case COMMAND_HAS_MISSION_AUDIO_LOADED: { CollectParameters(&m_nIp, 1); UpdateCompareFlag(DMAudio.GetMissionAudioLoadingStatus(ScriptParams[0] - 1) == 1); return 0; } case COMMAND_PLAY_MISSION_AUDIO: CollectParameters(&m_nIp, 1); DMAudio.PlayLoadedMissionAudio(ScriptParams[0] - 1); return 0; case COMMAND_HAS_MISSION_AUDIO_FINISHED: { CollectParameters(&m_nIp, 1); UpdateCompareFlag(DMAudio.IsMissionAudioSampleFinished(ScriptParams[0] - 1)); return 0; } case COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); int node = ThePaths.FindNodeClosestToCoors(pos, 0, 999999.9f, true, true); *(CVector*)&ScriptParams[0] = ThePaths.FindNodeCoorsForScript(node); *(float*)&ScriptParams[3] = ThePaths.FindNodeOrientationForCarPlacement(node); StoreParameters(&m_nIp, 4); return 0; } case COMMAND_HAS_IMPORT_GARAGE_SLOT_BEEN_FILLED: { CollectParameters(&m_nIp, 2); UpdateCompareFlag(CGarages::HasImportExportGarageCollectedThisCar(ScriptParams[0], ScriptParams[1] - 1)); return 0; } case COMMAND_CLEAR_THIS_PRINT: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CMessages::ClearThisPrint(text); return 0; } case COMMAND_CLEAR_THIS_BIG_PRINT: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CMessages::ClearThisBigPrint(text); return 0; } case COMMAND_SET_MISSION_AUDIO_POSITION: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[1]; DMAudio.SetMissionAudioLocation(ScriptParams[0] - 1, pos.x, pos.y, pos.z); return 0; } case COMMAND_ACTIVATE_SAVE_MENU: { CStats::SafeHouseVisits++; FrontEndMenuManager.m_bActivateSaveMenu = true; FindPlayerPed()->SetMoveSpeed(0.0f, 0.0f, 0.0f); FindPlayerPed()->SetTurnSpeed(0.0f, 0.0f, 0.0f); return 0; } case COMMAND_HAS_SAVE_GAME_FINISHED: UpdateCompareFlag(!FrontEndMenuManager.m_bMenuActive && !FrontEndMenuManager.m_bActivateSaveMenu); return 0; case COMMAND_NO_SPECIAL_CAMERA_FOR_THIS_GARAGE: CollectParameters(&m_nIp, 1); CGarages::SetLeaveCameraForThisGarage(ScriptParams[0]); return 0; /* case COMMAND_ADD_BLIP_FOR_PICKUP_OLD: { CollectParameters(&m_nIp, 3); CObject* pObject = CPickups::aPickUps[CPickups::GetActualPickupIndex(ScriptParams[0])].m_pObject; CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CRadar::SetEntityBlip(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(pObject), ScriptParams[1], (eBlipDisplay)ScriptParams[2]); StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_ADD_BLIP_FOR_PICKUP: { CollectParameters(&m_nIp, 1); CObject* pObject = CPickups::aPickUps[CPickups::GetActualPickupIndex(ScriptParams[0])].m_pObject; CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int handle = CRadar::SetEntityBlip(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(pObject), 6, BLIP_DISPLAY_BOTH); CRadar::ChangeBlipScale(handle, 3); ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_ADD_SPRITE_BLIP_FOR_PICKUP: { CollectParameters(&m_nIp, 2); CObject* pObject = CPickups::aPickUps[CPickups::GetActualPickupIndex(ScriptParams[0])].m_pObject; CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int handle = CRadar::SetEntityBlip(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(pObject), 6, BLIP_DISPLAY_BOTH); CRadar::SetBlipSprite(handle, ScriptParams[1]); ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_SET_PED_DENSITY_MULTIPLIER: CollectParameters(&m_nIp, 1); CPopulation::PedDensityMultiplier = *(float*)&ScriptParams[0]; return 0; case COMMAND_FORCE_RANDOM_PED_TYPE: CollectParameters(&m_nIp, 1); CPopulation::m_AllRandomPedsThisType = ScriptParams[0]; return 0; /* case COMMAND_SET_TEXT_DRAW_BEFORE_FADE: CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bTextBeforeFade = ScriptParams[0] != 0; return 0; */ case COMMAND_GET_COLLECTABLE1S_COLLECTED: ScriptParams[0] = CWorld::Players[CWorld::PlayerInFocus].m_nCollectedPackages; StoreParameters(&m_nIp, 1); return 0; case COMMAND_SET_CHAR_OBJ_LEAVE_ANY_CAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_LEAVE_CAR, pPed->m_pMyVehicle); return 0; } case COMMAND_SET_SPRITES_DRAW_BEFORE_FADE: CollectParameters(&m_nIp, 1); CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_bBeforeFade = ScriptParams[0] != 0; return 0; case COMMAND_SET_TEXT_RIGHT_JUSTIFY: CollectParameters(&m_nIp, 1); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bRightJustify = ScriptParams[0] != 0; return 0; case COMMAND_PRINT_HELP: { if (CCamera::m_bUseMouse3rdPerson && ( strcmp((char*)&CTheScripts::ScriptSpace[m_nIp], "HELP15") == 0 || strcmp((char*)&CTheScripts::ScriptSpace[m_nIp], "GUN_2A") == 0 || strcmp((char*)&CTheScripts::ScriptSpace[m_nIp], "GUN_2C") == 0 || strcmp((char*)&CTheScripts::ScriptSpace[m_nIp], "GUN_2D") == 0)) { m_nIp += KEY_LENGTH_IN_SCRIPT; return 0; } wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CHud::SetHelpMessage(text, false); return 0; } case COMMAND_CLEAR_HELP: CHud::SetHelpMessage(nil, false); return 0; case COMMAND_FLASH_HUD_OBJECT: CollectParameters(&m_nIp, 1); CHud::m_ItemToFlash = ScriptParams[0]; return 0; default: script_assert(0); } return -1; } int32 CTheScripts::GetNewUniqueScriptSphereIndex(int32 index) { if (ScriptSphereArray[index].m_Index >= UINT16_MAX - 1) ScriptSphereArray[index].m_Index = 1; else ScriptSphereArray[index].m_Index++; return (uint16)index | ScriptSphereArray[index].m_Index << 16; } int32 CTheScripts::GetActualScriptSphereIndex(int32 index) { if (index == -1) return -1; uint16 check = (uint32)index >> 16; uint16 array_idx = index & (0xFFFF); script_assert(array_idx < ARRAY_SIZE(ScriptSphereArray)); if (check != ScriptSphereArray[array_idx].m_Index) return -1; return array_idx; } void CTheScripts::DrawScriptSpheres() { for (int i = 0; i < MAX_NUM_SCRIPT_SPHERES; i++) { if (ScriptSphereArray[i].m_bInUse) C3dMarkers::PlaceMarkerSet(ScriptSphereArray[i].m_Id, MARKERTYPE_CYLINDER, ScriptSphereArray[i].m_vecCenter, ScriptSphereArray[i].m_fRadius, SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, SPHERE_MARKER_A, SPHERE_MARKER_PULSE_PERIOD, SPHERE_MARKER_PULSE_FRACTION, 0); } } int32 CTheScripts::AddScriptSphere(int32 id, CVector pos, float radius) { int16 i = 0; for (i = 0; i < MAX_NUM_SCRIPT_SPHERES; i++) { if (!ScriptSphereArray[i].m_bInUse) break; } #ifdef FIX_BUGS if (i == MAX_NUM_SCRIPT_SPHERES) return -1; #endif ScriptSphereArray[i].m_bInUse = true; ScriptSphereArray[i].m_Id = id; ScriptSphereArray[i].m_vecCenter = pos; ScriptSphereArray[i].m_fRadius = radius; return GetNewUniqueScriptSphereIndex(i); } void CTheScripts::RemoveScriptSphere(int32 index) { index = GetActualScriptSphereIndex(index); if (index == -1) return; ScriptSphereArray[index].m_bInUse = false; ScriptSphereArray[index].m_Id = 0; } void CTheScripts::AddToBuildingSwapArray(CBuilding* pBuilding, int32 old_model, int32 new_model) { int i = 0; bool found = false; while (i < MAX_NUM_BUILDING_SWAPS && !found) { if (BuildingSwapArray[i].m_pBuilding == pBuilding) found = true; else i++; } if (found) { if (BuildingSwapArray[i].m_nOldModel == new_model) { BuildingSwapArray[i].m_pBuilding = nil; BuildingSwapArray[i].m_nOldModel = BuildingSwapArray[i].m_nNewModel = -1; } else { BuildingSwapArray[i].m_nNewModel = new_model; } } else { i = 0; while (i < MAX_NUM_BUILDING_SWAPS && !found) { if (BuildingSwapArray[i].m_pBuilding == nil) found = true; else i++; } if (found) { BuildingSwapArray[i].m_pBuilding = pBuilding; BuildingSwapArray[i].m_nNewModel = new_model; BuildingSwapArray[i].m_nOldModel = old_model; } } } void CTheScripts::AddToInvisibilitySwapArray(CEntity* pEntity, bool remove) { int i = 0; bool found = false; while (i < MAX_NUM_INVISIBILITY_SETTINGS && !found) { if (InvisibilitySettingArray[i] == pEntity) found = true; else i++; } if (found) { if (remove) InvisibilitySettingArray[i] = nil; } else if (!remove) { i = 0; while (i < MAX_NUM_INVISIBILITY_SETTINGS && !found) { if (InvisibilitySettingArray[i] == nil) found = true; else i++; } if (found) InvisibilitySettingArray[i] = pEntity; } } void CTheScripts::UndoBuildingSwaps() { for (int i = 0; i < MAX_NUM_BUILDING_SWAPS; i++) { if (BuildingSwapArray[i].m_pBuilding) { BuildingSwapArray[i].m_pBuilding->ReplaceWithNewModel(BuildingSwapArray[i].m_nOldModel); BuildingSwapArray[i].m_pBuilding = nil; BuildingSwapArray[i].m_nOldModel = BuildingSwapArray[i].m_nNewModel = -1; } } } void CTheScripts::UndoEntityInvisibilitySettings() { for (int i = 0; i < MAX_NUM_INVISIBILITY_SETTINGS; i++) { if (InvisibilitySettingArray[i]) { InvisibilitySettingArray[i]->bIsVisible = true; InvisibilitySettingArray[i] = nil; } } } ================================================ FILE: src/control/Script5.cpp ================================================ #include "common.h" #include "Script.h" #include "ScriptCommands.h" #include "CarCtrl.h" #include "BulletInfo.h" #include "General.h" #include "Lines.h" #include "Messages.h" #include "Pad.h" #include "Pools.h" #include "Population.h" #include "RpAnimBlend.h" #include "Shadows.h" #include "SpecialFX.h" #include "World.h" #include "main.h" void CRunningScript::UpdateCompareFlag(bool flag) { if (m_bNotFlag) flag = !flag; if (m_nAndOrState == ANDOR_NONE) { m_bCondResult = flag; return; } if (m_nAndOrState >= ANDS_1 && m_nAndOrState <= ANDS_8) { m_bCondResult &= flag; if (m_nAndOrState == ANDS_1) { m_nAndOrState = ANDOR_NONE; return; } } else if (m_nAndOrState >= ORS_1 && m_nAndOrState <= ORS_8) { m_bCondResult |= flag; if (m_nAndOrState == ORS_1) { m_nAndOrState = ANDOR_NONE; return; } } else { return; } m_nAndOrState--; } void CRunningScript::LocatePlayerCommand(int32 command, uint32* pIp) { bool b3D, result, debug, decided = false; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_PLAYER_ANY_MEANS_3D: case COMMAND_LOCATE_PLAYER_ON_FOOT_3D: case COMMAND_LOCATE_PLAYER_IN_CAR_3D: case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D: case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D: case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 8 : 6); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; switch (command) { case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D: case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D: case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D: case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D: if (!CTheScripts::IsPlayerStopped(pPlayerInfo)) { result = false; decided = true; } break; default: break; } X = *(float*)&ScriptParams[1]; Y = *(float*)&ScriptParams[2]; if (b3D) { Z = *(float*)&ScriptParams[3]; dX = *(float*)&ScriptParams[4]; dY = *(float*)&ScriptParams[5]; dZ = *(float*)&ScriptParams[6]; debug = ScriptParams[7]; } else { dX = *(float*)&ScriptParams[3]; dY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } if (!decided) { CVector pos = pPlayerInfo->GetPos(); result = false; bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } if (in_area) { switch (command) { case COMMAND_LOCATE_PLAYER_ANY_MEANS_2D: case COMMAND_LOCATE_PLAYER_ANY_MEANS_3D: case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D: result = true; break; case COMMAND_LOCATE_PLAYER_ON_FOOT_2D: case COMMAND_LOCATE_PLAYER_ON_FOOT_3D: case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D: result = !pPlayerInfo->m_pPed->bInVehicle; break; case COMMAND_LOCATE_PLAYER_IN_CAR_2D: case COMMAND_LOCATE_PLAYER_IN_CAR_3D: case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D: case COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D: result = pPlayerInfo->m_pPed->bInVehicle; break; default: script_assert(false); break; } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocatePlayerCharCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D: case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D: case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 6 : 5); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pTarget); CVector pos = pPlayerInfo->GetPos(); if (pTarget->bInVehicle) { X = pTarget->m_pMyVehicle->GetPosition().x; Y = pTarget->m_pMyVehicle->GetPosition().y; Z = pTarget->m_pMyVehicle->GetPosition().z; } else { X = pTarget->GetPosition().x; Y = pTarget->GetPosition().y; Z = pTarget->GetPosition().z; } dX = *(float*)&ScriptParams[2]; dY = *(float*)&ScriptParams[3]; if (b3D) { dZ = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } else { debug = ScriptParams[4]; } result = false; bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } if (in_area) { switch (command) { case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D: case COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D: result = true; break; case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D: case COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D: result = !pPlayerInfo->m_pPed->bInVehicle; break; case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D: case COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D: result = pPlayerInfo->m_pPed->bInVehicle; break; default: script_assert(false); break; } } UpdateCompareFlag(result); if (debug) #ifdef FIX_BUGS CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); #else CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dX, b3D ? Z : MAP_Z_LOW_LIMIT); #endif if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocatePlayerCarCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D: case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D: case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 6 : 5); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pTarget); CVector pos = pPlayerInfo->GetPos(); X = pTarget->GetPosition().x; Y = pTarget->GetPosition().y; Z = pTarget->GetPosition().z; dX = *(float*)&ScriptParams[2]; dY = *(float*)&ScriptParams[3]; if (b3D) { dZ = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } else { debug = ScriptParams[4]; } result = false; bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } if (in_area) { switch (command) { case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_2D: case COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D: result = true; break; case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_2D: case COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D: result = !pPlayerInfo->m_pPed->bInVehicle; break; case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_2D: case COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D: result = pPlayerInfo->m_pPed->bInVehicle; break; default: script_assert(false); break; } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateCharCommand(int32 command, uint32* pIp) { bool b3D, result, debug, decided = false; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_CHAR_ANY_MEANS_3D: case COMMAND_LOCATE_CHAR_ON_FOOT_3D: case COMMAND_LOCATE_CHAR_IN_CAR_3D: case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D: case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D: case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 8 : 6); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVector pos = pPed->InVehicle() ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); switch (command) { case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D: case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D: case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D: case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D: if (!CTheScripts::IsPedStopped(pPed)) { result = false; decided = true; } break; default: break; } X = *(float*)&ScriptParams[1]; Y = *(float*)&ScriptParams[2]; if (b3D) { Z = *(float*)&ScriptParams[3]; dX = *(float*)&ScriptParams[4]; dY = *(float*)&ScriptParams[5]; dZ = *(float*)&ScriptParams[6]; debug = ScriptParams[7]; } else { dX = *(float*)&ScriptParams[3]; dY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } if (!decided) { result = false; bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } if (in_area) { switch (command) { case COMMAND_LOCATE_CHAR_ANY_MEANS_2D: case COMMAND_LOCATE_CHAR_ANY_MEANS_3D: case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D: result = true; break; case COMMAND_LOCATE_CHAR_ON_FOOT_2D: case COMMAND_LOCATE_CHAR_ON_FOOT_3D: case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D: result = !pPed->bInVehicle; break; case COMMAND_LOCATE_CHAR_IN_CAR_2D: case COMMAND_LOCATE_CHAR_IN_CAR_3D: case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D: case COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateCharCharCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D: case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D: case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 6 : 5); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pTarget); CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); if (pTarget->bInVehicle) { X = pTarget->m_pMyVehicle->GetPosition().x; Y = pTarget->m_pMyVehicle->GetPosition().y; Z = pTarget->m_pMyVehicle->GetPosition().z; } else { X = pTarget->GetPosition().x; Y = pTarget->GetPosition().y; Z = pTarget->GetPosition().z; } dX = *(float*)&ScriptParams[2]; dY = *(float*)&ScriptParams[3]; if (b3D) { dZ = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } else { debug = ScriptParams[4]; } result = false; bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } if (in_area) { switch (command) { case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D: case COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D: result = true; break; case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D: case COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D: result = !pPed->bInVehicle; break; case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D: case COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } UpdateCompareFlag(result); if (debug) #ifdef FIX_BUGS CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); #else CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dX, b3D ? Z : MAP_Z_LOW_LIMIT); #endif if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateCharCarCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D: case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D: case COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 6 : 5); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pTarget); CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); X = pTarget->GetPosition().x; Y = pTarget->GetPosition().y; Z = pTarget->GetPosition().z; dX = *(float*)&ScriptParams[2]; dY = *(float*)&ScriptParams[3]; if (b3D) { dZ = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } else { debug = ScriptParams[4]; } result = false; bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } if (in_area) { switch (command) { case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_2D: case COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D: result = true; break; case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_2D: case COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D: result = !pPed->bInVehicle; break; case COMMAND_LOCATE_CHAR_IN_CAR_CAR_2D: case COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateCharObjectCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D: case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D: case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 6 : 5); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CObject* pTarget = CPools::GetObjectPool()->GetAt(ScriptParams[1]); script_assert(pTarget); CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); X = pTarget->GetPosition().x; Y = pTarget->GetPosition().y; Z = pTarget->GetPosition().z; dX = *(float*)&ScriptParams[2]; dY = *(float*)&ScriptParams[3]; if (b3D) { dZ = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } else { debug = ScriptParams[4]; } result = false; bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } if (in_area) { switch (command) { case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_2D: case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D: result = true; break; case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_2D: case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D: result = !pPed->bInVehicle; break; case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_2D: case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateCarCommand(int32 command, uint32* pIp) { bool b3D, result, debug, decided = false; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_CAR_3D: case COMMAND_LOCATE_STOPPED_CAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 8 : 6); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CVector pos = pVehicle->GetPosition(); switch (command) { case COMMAND_LOCATE_STOPPED_CAR_2D: case COMMAND_LOCATE_STOPPED_CAR_3D: if (!CTheScripts::IsVehicleStopped(pVehicle)) { result = false; decided = true; } break; default: break; } X = *(float*)&ScriptParams[1]; Y = *(float*)&ScriptParams[2]; if (b3D) { Z = *(float*)&ScriptParams[3]; dX = *(float*)&ScriptParams[4]; dY = *(float*)&ScriptParams[5]; dZ = *(float*)&ScriptParams[6]; debug = ScriptParams[7]; } else { dX = *(float*)&ScriptParams[3]; dY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } if (!decided) { result = false; bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } result = in_area; } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateObjectCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_OBJECT_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 8 : 6); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CVector pos = pObject->GetPosition(); X = *(float*)&ScriptParams[1]; Y = *(float*)&ScriptParams[2]; if (b3D) { Z = *(float*)&ScriptParams[3]; dX = *(float*)&ScriptParams[4]; dY = *(float*)&ScriptParams[5]; dZ = *(float*)&ScriptParams[6]; debug = ScriptParams[7]; } else { dX = *(float*)&ScriptParams[3]; dY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } result = false; bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } result = in_area; UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateSniperBulletCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_SNIPER_BULLET_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 7 : 5); X = *(float*)&ScriptParams[0]; Y = *(float*)&ScriptParams[1]; if (b3D) { Z = *(float*)&ScriptParams[2]; dX = *(float*)&ScriptParams[3]; dY = *(float*)&ScriptParams[4]; dZ = *(float*)&ScriptParams[5]; debug = ScriptParams[6]; } else { dX = *(float*)&ScriptParams[2]; dY = *(float*)&ScriptParams[3]; debug = ScriptParams[4]; } result = CBulletInfo::TestForSniperBullet(X - dX, X + dX, Y - dY, Y + dY, b3D ? Z - dZ : -1000.0f, b3D ? Z + dZ : 1000.0f); UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::PlayerInAreaCheckCommand(int32 command, uint32* pIp) { bool b3D, result, debug, decided = false; float infX, infY, infZ, supX, supY, supZ; switch (command) { case COMMAND_IS_PLAYER_IN_AREA_3D: case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 8 : 6); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; switch (command) { case COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D: if (!CTheScripts::IsPlayerStopped(pPlayerInfo)) { result = false; decided = true; } break; default: break; } infX = *(float*)&ScriptParams[1]; infY = *(float*)&ScriptParams[2]; if (b3D) { infZ = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[6]; if (infZ > supZ) { infZ = *(float*)&ScriptParams[6]; supZ = *(float*)&ScriptParams[3]; } debug = ScriptParams[7]; } else { supX = *(float*)&ScriptParams[3]; supY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } if (infX > supX) { float tmp = infX; infX = supX; supX = tmp; } if (infY > supY) { float tmp = infY; infY = supY; supY = tmp; } if (!decided) { CVector pos = pPlayerInfo->GetPos(); result = false; bool in_area; if (b3D) { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y && infZ <= pos.z && supZ >= pos.z; } else { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y; } if (in_area) { switch (command) { case COMMAND_IS_PLAYER_IN_AREA_2D: case COMMAND_IS_PLAYER_IN_AREA_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D: result = true; break; case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D: result = !pPlayerInfo->m_pPed->bInVehicle; break; case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_2D: case COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D: case COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D: result = pPlayerInfo->m_pPed->bInVehicle; break; default: script_assert(false); break; } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); else CTheScripts::DrawDebugSquare(infX, infY, supX, supY); } } void CRunningScript::PlayerInAngledAreaCheckCommand(int32 command, uint32* pIp) { bool b3D, result, debug, decided = false; float infX, infY, infZ, supX, supY, supZ, side2length; switch (command) { case COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 9 : 7); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; switch (command) { case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D: if (!CTheScripts::IsPlayerStopped(pPlayerInfo)) { result = false; decided = true; } break; default: break; } infX = *(float*)&ScriptParams[1]; infY = *(float*)&ScriptParams[2]; if (b3D) { infZ = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[6]; if (infZ > supZ) { infZ = *(float*)&ScriptParams[6]; supZ = *(float*)&ScriptParams[3]; } side2length = *(float*)&ScriptParams[7]; debug = ScriptParams[8]; } else { supX = *(float*)&ScriptParams[3]; supY = *(float*)&ScriptParams[4]; side2length = *(float*)&ScriptParams[5]; debug = ScriptParams[6]; } float initAngle = CGeneral::GetRadianAngleBetweenPoints(infX, infY, supX, supY) + HALFPI; while (initAngle < 0.0f) initAngle += TWOPI; while (initAngle > TWOPI) initAngle -= TWOPI; // it looks like the idea is to use a rectangle using the diagonal of the rectangle as // the side of new rectangle, with "length" being the length of second side float rotatedSupX = supX + side2length * sin(initAngle); float rotatedSupY = supY - side2length * cos(initAngle); float rotatedInfX = infX + side2length * sin(initAngle); float rotatedInfY = infY - side2length * cos(initAngle); float side1X = supX - infX; float side1Y = supY - infY; float side1Length = CVector2D(side1X, side1Y).Magnitude(); float side2X = rotatedInfX - infX; float side2Y = rotatedInfY - infY; float side2Length = CVector2D(side2X, side2Y).Magnitude(); // == side2length? if (!decided) { CVector pos = pPlayerInfo->GetPos(); result = false; float X = pos.x - infX; float Y = pos.y - infY; float positionAlongSide1 = X * side1X / side1Length + Y * side1Y / side1Length; bool in_area = false; if (positionAlongSide1 >= 0.0f && positionAlongSide1 <= side1Length) { float positionAlongSide2 = X * side2X / side2Length + Y * side2Y / side2Length; if (positionAlongSide2 >= 0.0f && positionAlongSide2 <= side2Length) { in_area = !b3D || pos.z >= infZ && pos.z <= supZ; } } if (in_area) { switch (command) { case COMMAND_IS_PLAYER_IN_ANGLED_AREA_2D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D: result = true; break; case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D: result = !pPlayerInfo->m_pPed->bInVehicle; break; case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_2D: case COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D: case COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D: result = pPlayerInfo->m_pPed->bInVehicle; break; default: script_assert(false); break; } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantAngledArea((uintptr)this + m_nIp, infX, infY, supX, supY, rotatedSupX, rotatedSupY, rotatedInfX, rotatedInfY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugAngledCube(infX, infY, infZ, supX, supY, supZ, rotatedSupX, rotatedSupY, rotatedInfX, rotatedInfY); else CTheScripts::DrawDebugAngledSquare(infX, infY, supX, supY, rotatedSupX, rotatedSupY, rotatedInfX, rotatedInfY); } } void CRunningScript::CharInAreaCheckCommand(int32 command, uint32* pIp) { bool b3D, result, debug, decided = false; float infX, infY, infZ, supX, supY, supZ; switch (command) { case COMMAND_IS_CHAR_IN_AREA_3D: case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D: case COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 8 : 6); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVector pos = pPed->InVehicle() ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); switch (command) { case COMMAND_IS_CHAR_STOPPED_IN_AREA_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_2D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D: if (!CTheScripts::IsPedStopped(pPed)) { result = false; decided = true; } break; default: break; } infX = *(float*)&ScriptParams[1]; infY = *(float*)&ScriptParams[2]; if (b3D) { infZ = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[6]; if (infZ > supZ) { infZ = *(float*)&ScriptParams[6]; supZ = *(float*)&ScriptParams[3]; } debug = ScriptParams[7]; } else { supX = *(float*)&ScriptParams[3]; supY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } if (infX > supX) { float tmp = infX; infX = supX; supX = tmp; } if (infY > supY) { float tmp = infY; infY = supY; supY = tmp; } if (!decided) { result = false; bool in_area; if (b3D) { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y && infZ <= pos.z && supZ >= pos.z; } else { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y; } if (in_area) { switch (command) { case COMMAND_IS_CHAR_IN_AREA_2D: case COMMAND_IS_CHAR_IN_AREA_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_2D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_3D: result = true; break; case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_2D: case COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D: result = !pPed->bInVehicle; break; case COMMAND_IS_CHAR_IN_AREA_IN_CAR_2D: case COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D: case COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); else CTheScripts::DrawDebugSquare(infX, infY, supX, supY); } } void CRunningScript::CarInAreaCheckCommand(int32 command, uint32* pIp) { bool b3D, result, debug, decided = false; float infX, infY, infZ, supX, supY, supZ; switch (command) { case COMMAND_IS_CAR_IN_AREA_3D: case COMMAND_IS_CAR_STOPPED_IN_AREA_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 8 : 6); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CVector pos = pVehicle->GetPosition(); switch (command) { case COMMAND_IS_CAR_STOPPED_IN_AREA_3D: case COMMAND_IS_CAR_STOPPED_IN_AREA_2D: if (!CTheScripts::IsVehicleStopped(pVehicle)) { result = false; decided = true; } break; default: break; } infX = *(float*)&ScriptParams[1]; infY = *(float*)&ScriptParams[2]; if (b3D) { infZ = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[6]; if (infZ > supZ) { infZ = *(float*)&ScriptParams[6]; supZ = *(float*)&ScriptParams[3]; } debug = ScriptParams[7]; } else { supX = *(float*)&ScriptParams[3]; supY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } if (infX > supX) { float tmp = infX; infX = supX; supX = tmp; } if (infY > supY) { float tmp = infY; infY = supY; supY = tmp; } if (!decided) { result = false; bool in_area; if (b3D) { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y && infZ <= pos.z && supZ >= pos.z; } else { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y; } if (in_area) { switch (command) { case COMMAND_IS_CAR_IN_AREA_2D: case COMMAND_IS_CAR_IN_AREA_3D: case COMMAND_IS_CAR_STOPPED_IN_AREA_2D: case COMMAND_IS_CAR_STOPPED_IN_AREA_3D: result = true; break; default: script_assert(false); break; } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); else CTheScripts::DrawDebugSquare(infX, infY, supX, supY); } } void CRunningScript::ObjectInAreaCheckCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float infX, infY, infZ, supX, supY, supZ; switch (command) { case COMMAND_IS_OBJECT_IN_AREA_3D: b3D = true; break; default: b3D = false; break; } CollectParameters(pIp, b3D ? 8 : 6); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CVector pos = pObject->GetPosition(); infX = *(float*)&ScriptParams[1]; infY = *(float*)&ScriptParams[2]; if (b3D) { infZ = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[6]; if (infZ > supZ) { infZ = *(float*)&ScriptParams[6]; supZ = *(float*)&ScriptParams[3]; } debug = ScriptParams[7]; } else { supX = *(float*)&ScriptParams[3]; supY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } if (infX > supX) { float tmp = infX; infX = supX; supX = tmp; } if (infY > supY) { float tmp = infY; infY = supY; supY = tmp; } result = false; bool in_area; if (b3D) { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y && infZ <= pos.z && supZ >= pos.z; } else { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y; } if (in_area) { switch (command) { case COMMAND_IS_OBJECT_IN_AREA_2D: case COMMAND_IS_OBJECT_IN_AREA_3D: result = true; break; default: script_assert(false); break; } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); else CTheScripts::DrawDebugSquare(infX, infY, supX, supY); } } void CRunningScript::DoDeatharrestCheck() { if (!m_bDeatharrestEnabled) return; if (!CTheScripts::IsPlayerOnAMission()) return; CPlayerInfo* pPlayer = &CWorld::Players[CWorld::PlayerInFocus]; if (!pPlayer->IsRestartingAfterDeath() && !pPlayer->IsRestartingAfterArrest()) return; #ifdef MISSION_REPLAY if (AllowMissionReplay != 0) return; if (CanAllowMissionReplay()) AllowMissionReplay = 1; #endif script_assert(m_nStackPointer > 0); while (m_nStackPointer > 1) --m_nStackPointer; m_nIp = m_anStack[--m_nStackPointer]; CMessages::ClearSmallMessagesOnly(); *(int32*)&CTheScripts::ScriptSpace[CTheScripts::OnAMissionFlag] = 0; m_bDeatharrestExecuted = true; m_nWakeTime = 0; } int16 CRunningScript::GetPadState(uint16 pad, uint16 button) { CPad* pPad = CPad::GetPad(pad); switch (button) { case 0: return pPad->NewState.LeftStickX; case 1: return pPad->NewState.LeftStickY; case 2: return pPad->NewState.RightStickX; case 3: return pPad->NewState.RightStickY; case 4: return pPad->NewState.LeftShoulder1; case 5: return pPad->NewState.LeftShoulder2; case 6: return pPad->NewState.RightShoulder1; case 7: return pPad->NewState.RightShoulder2; case 8: return pPad->NewState.DPadUp; case 9: return pPad->NewState.DPadDown; case 10: return pPad->NewState.DPadLeft; case 11: return pPad->NewState.DPadRight; case 12: return pPad->NewState.Start; case 13: return pPad->NewState.Select; case 14: return pPad->NewState.Square; case 15: return pPad->NewState.Triangle; case 16: return pPad->NewState.Cross; case 17: return pPad->NewState.Circle; case 18: return pPad->NewState.LeftShock; case 19: return pPad->NewState.RightShock; default: break; } return 0; } #ifdef GTA_SCRIPT_COLLECTIVE void CRunningScript::LocateCollectiveCommand(int32 command, uint32* pIp) { bool b3D, result, debug, decided = false; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_COLL_ANY_MEANS_2D: case COMMAND_LOCATE_COLL_ON_FOOT_2D: case COMMAND_LOCATE_COLL_IN_CAR_2D: case COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D: b3D = false; break; default: b3D = true; break; } CollectParameters(pIp, b3D ? 8 : 6); X = *(float*)&ScriptParams[1]; Y = *(float*)&ScriptParams[2]; if (b3D) { Z = *(float*)&ScriptParams[3]; dX = *(float*)&ScriptParams[4]; dY = *(float*)&ScriptParams[5]; dZ = *(float*)&ScriptParams[6]; debug = ScriptParams[7]; } else { dX = *(float*)&ScriptParams[3]; dY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } result = true; for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) continue; CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); if (!pPed) { CTheScripts::CollectiveArray[i].colIndex = -1; CTheScripts::CollectiveArray[i].pedIndex = 0; continue; } CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); switch (command) { case COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D: if (!CTheScripts::IsPedStopped(pPed)) { result = false; decided = true; } break; default: break; } if (!decided) { bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } result = false; if (in_area) { switch (command) { case COMMAND_LOCATE_COLL_ANY_MEANS_2D: case COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D: result = true; break; case COMMAND_LOCATE_COLL_ON_FOOT_2D: case COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D: result = !pPed->bInVehicle; break; case COMMAND_LOCATE_COLL_IN_CAR_2D: case COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateCollectiveCharCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D: case COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D: case COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D: b3D = false; break; default: b3D = true; break; } CollectParameters(pIp, b3D ? 6 : 5); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pTarget); if (pTarget->bInVehicle) { X = pTarget->m_pMyVehicle->GetPosition().x; Y = pTarget->m_pMyVehicle->GetPosition().y; Z = pTarget->m_pMyVehicle->GetPosition().z; } else { X = pTarget->GetPosition().x; Y = pTarget->GetPosition().y; Z = pTarget->GetPosition().z; } dX = *(float*)&ScriptParams[2]; dY = *(float*)&ScriptParams[3]; if (b3D) { dZ = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } else { debug = ScriptParams[4]; } result = true; for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) continue; CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); if (!pPed) { CTheScripts::CollectiveArray[i].colIndex = -1; CTheScripts::CollectiveArray[i].pedIndex = 0; continue; } CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } result = false; if (in_area) { switch (command) { case COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D: result = true; break; case COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D: result = !pPed->bInVehicle; break; case COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateCollectiveCarCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D: case COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D: case COMMAND_LOCATE_COLL_IN_CAR_CAR_2D: b3D = false; break; default: b3D = true; break; } CollectParameters(pIp, b3D ? 6 : 5); CVehicle* pTarget = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pTarget); X = pTarget->GetPosition().x; Y = pTarget->GetPosition().y; Z = pTarget->GetPosition().z; dX = *(float*)&ScriptParams[2]; dY = *(float*)&ScriptParams[3]; if (b3D) { dZ = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } else { debug = ScriptParams[4]; } result = true; for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) continue; CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); if (!pPed) { CTheScripts::CollectiveArray[i].colIndex = -1; CTheScripts::CollectiveArray[i].pedIndex = 0; continue; } CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } result = false; if (in_area) { switch (command) { case COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D: result = true; break; case COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D: result = !pPed->bInVehicle; break; case COMMAND_LOCATE_COLL_IN_CAR_CAR_2D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::LocateCollectivePlayerCommand(int32 command, uint32* pIp) { bool b3D, result, debug; float X, Y, Z, dX, dY, dZ; switch (command) { case COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D: case COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D: case COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D: b3D = false; break; default: b3D = true; break; } CollectParameters(pIp, b3D ? 6 : 5); CVector pos = CWorld::Players[ScriptParams[1]].GetPos(); X = pos.x; Y = pos.y; Z = pos.z; dX = *(float*)&ScriptParams[2]; dY = *(float*)&ScriptParams[3]; if (b3D) { dZ = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } else { debug = ScriptParams[4]; } result = true; for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) continue; CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); if (!pPed) { CTheScripts::CollectiveArray[i].colIndex = -1; CTheScripts::CollectiveArray[i].pedIndex = 0; continue; } CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); bool in_area; if (b3D) { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y && Z - dZ <= pos.z && Z + dZ >= pos.z; } else { in_area = X - dX <= pos.x && X + dX >= pos.x && Y - dY <= pos.y && Y + dY >= pos.y; } result = false; if (in_area) { switch (command) { case COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D: result = true; break; case COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D: result = !pPed->bInVehicle; break; case COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, X - dX, Y - dY, X + dX, Y + dY, b3D ? Z : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(X - dX, Y - dY, Z - dZ, X + dX, Y + dY, Z + dZ); else CTheScripts::DrawDebugSquare(X - dX, Y - dY, X + dX, Y + dY); } } void CRunningScript::CollectiveInAreaCheckCommand(int32 command, uint32* pIp) { bool b3D, result, debug, decided = false; float infX, infY, infZ, supX, supY, supZ; switch (command) { case COMMAND_IS_COLL_IN_AREA_2D: case COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D: case COMMAND_IS_COLL_IN_AREA_IN_CAR_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D: b3D = false; break; default: b3D = true; break; } CollectParameters(pIp, b3D ? 8 : 6); infX = *(float*)&ScriptParams[1]; infY = *(float*)&ScriptParams[2]; if (b3D) { infZ = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[6]; if (infZ > supZ) { infZ = *(float*)&ScriptParams[6]; supZ = *(float*)&ScriptParams[3]; } debug = ScriptParams[7]; } else { supX = *(float*)&ScriptParams[3]; supY = *(float*)&ScriptParams[4]; debug = ScriptParams[5]; } if (infX > supX) { float tmp = infX; infX = supX; supX = tmp; } if (infY > supY) { float tmp = infY; infY = supY; supY = tmp; } result = true; for (int i = 0; i < MAX_NUM_COLLECTIVES && result; i++) { if (ScriptParams[0] != CTheScripts::CollectiveArray[i].colIndex) continue; CPed* pPed = CPools::GetPedPool()->GetAt(CTheScripts::CollectiveArray[i].pedIndex); if (!pPed) { CTheScripts::CollectiveArray[i].colIndex = -1; CTheScripts::CollectiveArray[i].pedIndex = 0; continue; } CVector pos = pPed->bInVehicle ? pPed->m_pMyVehicle->GetPosition() : pPed->GetPosition(); switch (command) { case COMMAND_IS_COLL_STOPPED_IN_AREA_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D: if (!CTheScripts::IsPedStopped(pPed)) { result = false; decided = true; } break; default: break; } if (!decided) { bool in_area; if (b3D) { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y && infZ <= pos.z && supZ >= pos.z; } else { in_area = infX <= pos.x && supX >= pos.x && infY <= pos.y && supY >= pos.y; } result = false; if (in_area) { switch (command) { case COMMAND_IS_COLL_IN_AREA_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_2D: result = true; break; case COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D: result = !pPed->bInVehicle; break; case COMMAND_IS_COLL_IN_AREA_IN_CAR_2D: case COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D: result = pPed->bInVehicle; break; default: script_assert(false); break; } } } } UpdateCompareFlag(result); if (debug) CTheScripts::HighlightImportantArea((uintptr)this + m_nIp, infX, infY, supX, supY, b3D ? (infZ + supZ) / 2 : MAP_Z_LOW_LIMIT); if (CTheScripts::DbgFlag) { if (b3D) CTheScripts::DrawDebugCube(infX, infY, infZ, supX, supY, supZ); else CTheScripts::DrawDebugSquare(infX, infY, supX, supY); } } #endif bool CRunningScript::CheckDamagedWeaponType(int32 actual, int32 type) { if (actual == -1) return false; if (type == WEAPONTYPE_ANYMELEE) { if (actual <= WEAPONTYPE_CHAINSAW) return true; if (actual - WEAPONTYPE_GRENADE <= WEAPONTYPE_MINIGUN) return false; return false; } if (type != WEAPONTYPE_ANYWEAPON) return false; switch (actual) { case WEAPONTYPE_UNARMED: case WEAPONTYPE_BRASSKNUCKLE: case WEAPONTYPE_SCREWDRIVER: case WEAPONTYPE_GOLFCLUB: case WEAPONTYPE_NIGHTSTICK: case WEAPONTYPE_KNIFE: case WEAPONTYPE_BASEBALLBAT: case WEAPONTYPE_HAMMER: case WEAPONTYPE_CLEAVER: case WEAPONTYPE_MACHETE: case WEAPONTYPE_KATANA: case WEAPONTYPE_CHAINSAW: case WEAPONTYPE_GRENADE: case WEAPONTYPE_DETONATOR_GRENADE: case WEAPONTYPE_TEARGAS: case WEAPONTYPE_MOLOTOV: case WEAPONTYPE_ROCKET: case WEAPONTYPE_COLT45: case WEAPONTYPE_PYTHON: case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_SPAS12_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: case WEAPONTYPE_TEC9: case WEAPONTYPE_UZI: case WEAPONTYPE_SILENCED_INGRAM: case WEAPONTYPE_MP5: case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: case WEAPONTYPE_ROCKETLAUNCHER: case WEAPONTYPE_FLAMETHROWER: case WEAPONTYPE_M60: case WEAPONTYPE_MINIGUN: case WEAPONTYPE_DETONATOR: case WEAPONTYPE_HELICANNON: case WEAPONTYPE_CAMERA: case WEAPONTYPE_EXPLOSION: case WEAPONTYPE_UZI_DRIVEBY: return true; case WEAPONTYPE_HEALTH: case WEAPONTYPE_ARMOUR: case WEAPONTYPE_RAMMEDBYCAR: case WEAPONTYPE_RUNOVERBYCAR: case WEAPONTYPE_DROWNING: case WEAPONTYPE_FALL: case WEAPONTYPE_UNIDENTIFIED: return false; } return false; } void CTheScripts::PrintListSizes() { int active = 0; int idle = 0; for (CRunningScript* pScript = pActiveScripts; pScript; pScript = pScript->GetNext()) active++; for (CRunningScript* pScript = pIdleScripts; pScript; pScript = pScript->GetNext()) idle++; debug("active: %d, idle: %d", active, idle); } uint32 DbgLineColour = 0x0000FFFF; // r = 0, g = 0, b = 255, a = 255 void CTheScripts::DrawDebugSquare(float infX, float infY, float supX, float supY) { CColPoint tmpCP; CEntity* tmpEP; CVector p1, p2, p3, p4; p1 = CVector(infX, infY, -1000.0f); CWorld::ProcessVerticalLine(p1, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); p1.z = 2.0f + tmpCP.point.z; p2 = CVector(supX, supY, -1000.0f); CWorld::ProcessVerticalLine(p2, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); p2.z = 2.0f + tmpCP.point.z; p3 = CVector(infX, supY, -1000.0f); CWorld::ProcessVerticalLine(p3, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); p3.z = 2.0f + tmpCP.point.z; p4 = CVector(supX, infY, -1000.0f); CWorld::ProcessVerticalLine(p4, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); p4.z = 2.0f + tmpCP.point.z; CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(p2.x, p2.y, p2.z, p3.x, p3.y, p3.z, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(p3.x, p3.y, p3.z, p4.x, p4.y, p4.z, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(p4.x, p4.y, p4.z, p1.x, p1.y, p1.z, DbgLineColour, DbgLineColour); } void CTheScripts::DrawDebugAngledSquare(float infX, float infY, float supX, float supY, float rotSupX, float rotSupY, float rotInfX, float rotInfY) { CColPoint tmpCP; CEntity* tmpEP; CVector p1, p2, p3, p4; p1 = CVector(infX, infY, -1000.0f); CWorld::ProcessVerticalLine(p1, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); p1.z = 2.0f + tmpCP.point.z; p2 = CVector(supX, supY, -1000.0f); CWorld::ProcessVerticalLine(p2, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); p2.z = 2.0f + tmpCP.point.z; p3 = CVector(rotSupX, rotSupY, -1000.0f); CWorld::ProcessVerticalLine(p3, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); p3.z = 2.0f + tmpCP.point.z; p4 = CVector(rotInfX, rotInfY, -1000.0f); CWorld::ProcessVerticalLine(p4, 1000.0f, tmpCP, tmpEP, true, false, false, false, true, false, nil); p4.z = 2.0f + tmpCP.point.z; CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(p2.x, p2.y, p2.z, p3.x, p3.y, p3.z, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(p3.x, p3.y, p3.z, p4.x, p4.y, p4.z, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(p4.x, p4.y, p4.z, p1.x, p1.y, p1.z, DbgLineColour, DbgLineColour); } void CTheScripts::DrawDebugCube(float infX, float infY, float infZ, float supX, float supY, float supZ) { CTheScripts::ScriptDebugLine3D(infX, infY, infZ, supX, infY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(supX, infY, infZ, supX, supY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(supX, supY, infZ, infX, supY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(infX, supY, infZ, infX, infY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(infX, infY, supZ, supX, infY, supZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(supX, infY, supZ, supX, supY, supZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(supX, supY, supZ, infX, supY, supZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(infX, supY, supZ, infX, infY, supZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(infX, infY, supZ, infX, infY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(supX, infY, supZ, supX, infY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(supX, supY, supZ, supX, supY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(infX, supY, supZ, infX, supY, infZ, DbgLineColour, DbgLineColour); } void CTheScripts::DrawDebugAngledCube(float infX, float infY, float infZ, float supX, float supY, float supZ, float rotSupX, float rotSupY, float rotInfX, float rotInfY) { CTheScripts::ScriptDebugLine3D(infX, infY, infZ, supX, infY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(supX, infY, infZ, rotSupX, rotSupY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(rotSupX, rotSupY, infZ, rotInfX, rotInfY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(rotInfX, rotInfY, infZ, infX, infY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(infX, infY, supZ, supX, infY, supZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(supX, infY, supZ, rotSupX, rotSupY, supZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(rotSupX, rotSupY, rotInfX, rotInfY, supY, supZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(rotInfX, rotInfY, supZ, infX, infY, supZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(infX, infY, supZ, infX, infY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(supX, infY, supZ, supX, infY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(rotSupX, rotSupY, supZ, rotSupX, rotSupY, infZ, DbgLineColour, DbgLineColour); CTheScripts::ScriptDebugLine3D(rotInfX, rotInfY, supZ, rotInfX, rotInfY, infZ, DbgLineColour, DbgLineColour); } void CTheScripts::ScriptDebugLine3D(float x1, float y1, float z1, float x2, float y2, float z2, uint32 col, uint32 col2) { if (NumScriptDebugLines >= MAX_NUM_STORED_LINES) return; aStoredLines[NumScriptDebugLines].vecInf = CVector(x1, y1, z1); aStoredLines[NumScriptDebugLines].vecSup = CVector(x2, y2, z2); aStoredLines[NumScriptDebugLines].color1 = col; aStoredLines[NumScriptDebugLines++].color2 = col2; } void CTheScripts::RenderTheScriptDebugLines() { RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)1); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)1); for (int i = 0; i < NumScriptDebugLines; i++) { CLines::RenderLineWithClipping( aStoredLines[i].vecInf.x, aStoredLines[i].vecInf.y, aStoredLines[i].vecInf.z, aStoredLines[i].vecSup.x, aStoredLines[i].vecSup.y, aStoredLines[i].vecSup.z, aStoredLines[i].color1, aStoredLines[i].color2); } NumScriptDebugLines = 0; RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)0); } #define SCRIPT_DATA_SIZE sizeof(CTheScripts::OnAMissionFlag) +\ 4 * sizeof(uint32) * MAX_NUM_BUILDING_SWAPS + 2 * sizeof(uint32) * MAX_NUM_INVISIBILITY_SETTINGS + 5 * sizeof(uint32) void CTheScripts::SaveAllScripts(uint8* buf, uint32* size) { INITSAVEBUF uint32 varSpace = GetSizeOfVariableSpace(); uint32 runningScripts = 0; for (CRunningScript* pScript = pActiveScripts; pScript; pScript = pScript->GetNext()) runningScripts++; *size = CRunningScript::nSaveStructSize * runningScripts + varSpace + SCRIPT_DATA_SIZE + SAVE_HEADER_SIZE + 3 * sizeof(uint32); WriteSaveHeader(buf, 'S', 'C', 'R', '\0', *size - SAVE_HEADER_SIZE); WriteSaveBuf(buf, varSpace); for (uint32 i = 0; i < varSpace; i++) WriteSaveBuf(buf, ScriptSpace[i]); #ifdef CHECK_STRUCT_SIZES static_assert(SCRIPT_DATA_SIZE == 968, "CTheScripts::SaveAllScripts"); #endif uint32 script_data_size = SCRIPT_DATA_SIZE; WriteSaveBuf(buf, script_data_size); WriteSaveBuf(buf, OnAMissionFlag); WriteSaveBuf(buf, LastMissionPassedTime); for (uint32 i = 0; i < MAX_NUM_BUILDING_SWAPS; i++) { CBuilding* pBuilding = BuildingSwapArray[i].m_pBuilding; uint32 type, handle; if (!pBuilding) { type = 0; handle = 0; } else if (pBuilding->GetIsATreadable()) { type = 1; handle = CPools::GetTreadablePool()->GetJustIndex_NoFreeAssert((CTreadable*)pBuilding) + 1; } else { type = 2; handle = CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pBuilding) + 1; } WriteSaveBuf(buf, type); WriteSaveBuf(buf, handle); WriteSaveBuf(buf, BuildingSwapArray[i].m_nNewModel); WriteSaveBuf(buf, BuildingSwapArray[i].m_nOldModel); } for (uint32 i = 0; i < MAX_NUM_INVISIBILITY_SETTINGS; i++) { CEntity* pEntity = InvisibilitySettingArray[i]; uint32 type, handle; if (!pEntity) { type = 0; handle = 0; } else { switch (pEntity->GetType()) { case ENTITY_TYPE_BUILDING: if (((CBuilding*)pEntity)->GetIsATreadable()) { type = 1; handle = CPools::GetTreadablePool()->GetJustIndex_NoFreeAssert((CTreadable*)pEntity) + 1; } else { type = 2; handle = CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)pEntity) + 1; } break; case ENTITY_TYPE_OBJECT: type = 3; handle = CPools::GetObjectPool()->GetJustIndex_NoFreeAssert((CObject*)pEntity) + 1; break; case ENTITY_TYPE_DUMMY: type = 4; handle = CPools::GetDummyPool()->GetJustIndex_NoFreeAssert((CDummy*)pEntity) + 1; default: break; } } WriteSaveBuf(buf, type); WriteSaveBuf(buf, handle); } WriteSaveBuf(buf, bUsingAMultiScriptFile); WriteSaveBuf(buf, bPlayerHasMetDebbieHarry); WriteSaveBuf(buf, (uint16)0); WriteSaveBuf(buf, MainScriptSize); WriteSaveBuf(buf, LargestMissionScriptSize); WriteSaveBuf(buf, NumberOfMissionScripts); WriteSaveBuf(buf, NumberOfExclusiveMissionScripts); WriteSaveBuf(buf, runningScripts); for (CRunningScript* pScript = pActiveScripts; pScript; pScript = pScript->GetNext()) pScript->Save(buf); VALIDATESAVEBUF(*size) } void CTheScripts::LoadAllScripts(uint8* buf, uint32 size) { Init(); INITSAVEBUF CheckSaveHeader(buf, 'S', 'C', 'R', '\0', size - SAVE_HEADER_SIZE); uint32 varSpace = ReadSaveBuf(buf); for (uint32 i = 0; i < varSpace; i++) ScriptSpace[i] = ReadSaveBuf(buf); script_assert(ReadSaveBuf(buf) == SCRIPT_DATA_SIZE); OnAMissionFlag = ReadSaveBuf(buf); LastMissionPassedTime = ReadSaveBuf(buf); for (uint32 i = 0; i < MAX_NUM_BUILDING_SWAPS; i++) { uint32 type = ReadSaveBuf(buf); uint32 handle = ReadSaveBuf(buf); switch (type) { case 0: BuildingSwapArray[i].m_pBuilding = nil; break; case 1: BuildingSwapArray[i].m_pBuilding = CPools::GetTreadablePool()->GetSlot(handle - 1); break; case 2: BuildingSwapArray[i].m_pBuilding = CPools::GetBuildingPool()->GetSlot(handle - 1); break; default: script_assert(false); } BuildingSwapArray[i].m_nNewModel = ReadSaveBuf(buf); BuildingSwapArray[i].m_nOldModel = ReadSaveBuf(buf); if (BuildingSwapArray[i].m_pBuilding) BuildingSwapArray[i].m_pBuilding->ReplaceWithNewModel(BuildingSwapArray[i].m_nNewModel); } for (uint32 i = 0; i < MAX_NUM_INVISIBILITY_SETTINGS; i++) { uint32 type = ReadSaveBuf(buf); uint32 handle = ReadSaveBuf(buf); switch (type) { case 0: InvisibilitySettingArray[i] = nil; break; case 1: InvisibilitySettingArray[i] = CPools::GetTreadablePool()->GetSlot(handle - 1); break; case 2: InvisibilitySettingArray[i] = CPools::GetBuildingPool()->GetSlot(handle - 1); break; case 3: InvisibilitySettingArray[i] = CPools::GetObjectPool()->GetSlot(handle - 1); break; case 4: InvisibilitySettingArray[i] = CPools::GetDummyPool()->GetSlot(handle - 1); break; default: script_assert(false); } if (InvisibilitySettingArray[i]) InvisibilitySettingArray[i]->bIsVisible = false; } script_assert(ReadSaveBuf(buf) == bUsingAMultiScriptFile); bPlayerHasMetDebbieHarry = ReadSaveBuf(buf); ReadSaveBuf(buf); script_assert(ReadSaveBuf(buf) == MainScriptSize); script_assert(ReadSaveBuf(buf) == LargestMissionScriptSize); script_assert(ReadSaveBuf(buf) == NumberOfMissionScripts); script_assert(ReadSaveBuf(buf) == NumberOfExclusiveMissionScripts); uint32 runningScripts = ReadSaveBuf(buf); for (uint32 i = 0; i < runningScripts; i++) StartNewScript(0)->Load(buf); VALIDATESAVEBUF(size) } #undef SCRIPT_DATA_SIZE void CRunningScript::Save(uint8*& buf) { #ifdef COMPATIBLE_SAVES SkipSaveBuf(buf, 8); for (int i = 0; i < 8; i++) WriteSaveBuf(buf, m_abScriptName[i]); WriteSaveBuf(buf, m_nIp); #ifdef CHECK_STRUCT_SIZES static_assert(MAX_STACK_DEPTH == 6, "Compatibility loss: MAX_STACK_DEPTH != 6"); #endif for (int i = 0; i < MAX_STACK_DEPTH; i++) WriteSaveBuf(buf, m_anStack[i]); WriteSaveBuf(buf, m_nStackPointer); SkipSaveBuf(buf, 2); #ifdef CHECK_STRUCT_SIZES static_assert(NUM_LOCAL_VARS + NUM_TIMERS == 18, "Compatibility loss: NUM_LOCAL_VARS + NUM_TIMERS != 18"); #endif for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++) WriteSaveBuf(buf, m_anLocalVariables[i]); WriteSaveBuf(buf, m_bIsActive); WriteSaveBuf(buf, m_bCondResult); WriteSaveBuf(buf, m_bIsMissionScript); WriteSaveBuf(buf, m_bSkipWakeTime); WriteSaveBuf(buf, m_nWakeTime); WriteSaveBuf(buf, m_nAndOrState); WriteSaveBuf(buf, m_bNotFlag); WriteSaveBuf(buf, m_bDeatharrestEnabled); WriteSaveBuf(buf, m_bDeatharrestExecuted); WriteSaveBuf(buf, m_bMissionFlag); SkipSaveBuf(buf, 2); #else WriteSaveBuf(buf, *this); #endif } void CRunningScript::Load(uint8*& buf) { #ifdef COMPATIBLE_SAVES SkipSaveBuf(buf, 8); for (int i = 0; i < 8; i++) m_abScriptName[i] = ReadSaveBuf(buf); m_nIp = ReadSaveBuf(buf); #ifdef CHECK_STRUCT_SIZES static_assert(MAX_STACK_DEPTH == 6, "Compatibility loss: MAX_STACK_DEPTH != 6"); #endif for (int i = 0; i < MAX_STACK_DEPTH; i++) m_anStack[i] = ReadSaveBuf(buf); m_nStackPointer = ReadSaveBuf(buf); SkipSaveBuf(buf, 2); #ifdef CHECK_STRUCT_SIZES static_assert(NUM_LOCAL_VARS + NUM_TIMERS == 18, "Compatibility loss: NUM_LOCAL_VARS + NUM_TIMERS != 18"); #endif for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++) m_anLocalVariables[i] = ReadSaveBuf(buf); m_bIsActive = ReadSaveBuf(buf); m_bCondResult = ReadSaveBuf(buf); m_bIsMissionScript = ReadSaveBuf(buf); m_bSkipWakeTime = ReadSaveBuf(buf); m_nWakeTime = ReadSaveBuf(buf); m_nAndOrState = ReadSaveBuf(buf); m_bNotFlag = ReadSaveBuf(buf); m_bDeatharrestEnabled = ReadSaveBuf(buf); m_bDeatharrestExecuted = ReadSaveBuf(buf); m_bMissionFlag = ReadSaveBuf(buf); SkipSaveBuf(buf, 2); #else CRunningScript* n = next; CRunningScript* p = prev; *this = ReadSaveBuf(buf); next = n; prev = p; #endif } void CTheScripts::ClearSpaceForMissionEntity(const CVector& pos, CEntity* pEntity) { static CColPoint aTempColPoints[MAX_COLLISION_POINTS]; int16 entities = 0; CEntity* aEntities[16]; CWorld::FindObjectsKindaColliding(pos, pEntity->GetBoundRadius(), false, &entities, 16, aEntities, false, true, true, false, false); if (entities <= 0) return; for (uint16 i = 0; i < entities; i++) { if (aEntities[i] != pEntity && aEntities[i]->IsPed() && ((CPed*)aEntities[i])->bInVehicle) aEntities[i] = nil; } for (uint16 i = 0; i < entities; i++) { if (aEntities[i] == pEntity || !aEntities[i]) continue; CEntity* pFound = aEntities[i]; int cols; if (pEntity->GetColModel()->numLines <= 0) cols = CCollision::ProcessColModels(pEntity->GetMatrix(), *pEntity->GetColModel(), pFound->GetMatrix(), *pFound->GetColModel(), aTempColPoints, nil, nil); else { float lines[4]; lines[0] = lines[1] = lines[2] = lines[3] = 1.0f; CColPoint tmp[4]; cols = CCollision::ProcessColModels(pEntity->GetMatrix(), *pEntity->GetColModel(), pFound->GetMatrix(), *pFound->GetColModel(), aTempColPoints,tmp, lines); } if (cols <= 0) continue; switch (pFound->GetType()) { case ENTITY_TYPE_VEHICLE: { printf("Will try to delete a vehicle where a mission entity should be\n"); CVehicle* pVehicle = (CVehicle*)pFound; if (pVehicle->bIsLocked || !pVehicle->CanBeDeleted()) break; if (pVehicle->pDriver) { CPopulation::RemovePed(pVehicle->pDriver); pVehicle->pDriver = nil; } for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { if (pVehicle->pPassengers[i]) { CPopulation::RemovePed(pVehicle->pPassengers[i]); pVehicle->pPassengers[i] = 0; pVehicle->m_nNumPassengers--; } } CCarCtrl::RemoveFromInterestingVehicleList(pVehicle); CWorld::Remove(pVehicle); delete pVehicle; break; } case ENTITY_TYPE_PED: { CPed* pPed = (CPed*)pFound; if (pPed->IsPlayer() || !pPed->CanBeDeleted()) break; CPopulation::RemovePed(pPed); printf("Deleted a ped where a mission entity should be\n"); break; } default: break; } } } void CTheScripts::HighlightImportantArea(uint32 id, float x1, float y1, float x2, float y2, float z) { float infX, infY, supX, supY; if (x1 < x2) { infX = x1; supX = x2; } else { infX = x2; supX = x1; } if (y1 < y2) { infY = y1; supY = y2; } else { infY = y2; supY = y1; } CVector center; center.x = (infX + supX) / 2; center.y = (infY + supY) / 2; center.z = (z <= MAP_Z_LOW_LIMIT) ? CWorld::FindGroundZForCoord(center.x, center.y) : z; CShadows::RenderIndicatorShadow(id, 2, gpGoalTex, ¢er, supX - center.x, 0.0f, 0.0f, center.y - supY, 0); } void CTheScripts::HighlightImportantAngledArea(uint32 id, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float z) { float infX, infY, supX, supY, X, Y; X = (x1 + x2) / 2; Y = (y1 + y2) / 2; supX = infX = X; supY = infY = Y; X = (x2 + x3) / 2; Y = (y2 + y3) / 2; infX = Min(infX, X); supX = Max(supX, X); infY = Min(infY, Y); supY = Max(supY, Y); X = (x3 + x4) / 2; Y = (y3 + y4) / 2; infX = Min(infX, X); supX = Max(supX, X); infY = Min(infY, Y); supY = Max(supY, Y); X = (x4 + x1) / 2; Y = (y4 + y1) / 2; infX = Min(infX, X); supX = Max(supX, X); infY = Min(infY, Y); supY = Max(supY, Y); CVector center; center.x = (infX + supX) / 2; center.y = (infY + supY) / 2; center.z = (z <= MAP_Z_LOW_LIMIT) ? CWorld::FindGroundZForCoord(center.x, center.y) : z; CShadows::RenderIndicatorShadow(id, 2, gpGoalTex, ¢er, supX - center.x, 0.0f, 0.0f, center.y - supY, 0); } #ifdef GTA_SCRIPT_COLLECTIVE int CTheScripts::AddPedsInVehicleToCollective(int index) { int colIndex = NextFreeCollectiveIndex; AdvanceCollectiveIndex(); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(index); script_assert(pVehicle); CPed* pDriver = pVehicle->pDriver; if (pDriver && !pDriver->IsPlayer() && pDriver->CharCreatedBy != MISSION_CHAR && pDriver->m_nPedType != PEDTYPE_COP) { int index = FindFreeSlotInCollectiveArray(); if (index > -1) { CollectiveArray[index].colIndex = colIndex; CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pDriver); } } for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { CPed* pPassenger = pVehicle->pPassengers[i]; if (pPassenger && !pPassenger->IsPlayer() && pPassenger->CharCreatedBy != MISSION_CHAR && pPassenger->m_nPedType != PEDTYPE_COP) { int index = FindFreeSlotInCollectiveArray(); if (index > -1) { CollectiveArray[index].colIndex = colIndex; CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pPassenger); } } } return colIndex; } int CTheScripts::AddPedsInAreaToCollective(float x, float y, float z, float radius) { int16 numFound; CEntity* pEntities[64]; int colIndex = NextFreeCollectiveIndex; AdvanceCollectiveIndex(); CWorld::FindObjectsInRange(CVector(x, y, z), radius, true, &numFound, 64, pEntities, false, true, true, false, false); for (int16 i = 0; i < numFound; i++) { if (pEntities[i]->GetType() == ENTITY_TYPE_PED) { CPed* pPed = (CPed*)pEntities[i]; if (pPed && !pPed->IsPlayer() && pPed->CharCreatedBy != MISSION_CHAR && pPed->m_nPedType != PEDTYPE_COP) { int index = FindFreeSlotInCollectiveArray(); if (index > -1) { CollectiveArray[index].colIndex = colIndex; CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pPed); } } } else if (pEntities[i]->GetType() == ENTITY_TYPE_VEHICLE) { CVehicle* pVehicle = (CVehicle*)pEntities[i]; CPed* pDriver = pVehicle->pDriver; if (pDriver && !pDriver->IsPlayer() && pDriver->CharCreatedBy != MISSION_CHAR && pDriver->m_nPedType != PEDTYPE_COP) { int index = FindFreeSlotInCollectiveArray(); if (index > -1) { CollectiveArray[index].colIndex = colIndex; CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pDriver); } } for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { CPed* pPassenger = pVehicle->pPassengers[i]; if (pPassenger && !pPassenger->IsPlayer() && pPassenger->CharCreatedBy != MISSION_CHAR && pPassenger->m_nPedType != PEDTYPE_COP) { int index = FindFreeSlotInCollectiveArray(); if (index > -1) { CollectiveArray[index].colIndex = colIndex; CollectiveArray[index].pedIndex = CPools::GetPedPool()->GetIndex(pPassenger); } } } } } return colIndex; } int CTheScripts::FindFreeSlotInCollectiveArray() { for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { if (CollectiveArray[i].colIndex == -1) return i; } return -1; } void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective, int16 p1, int16 p2) { for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { if (CollectiveArray[i].colIndex == colIndex) { CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); if (pPed == nil) { CollectiveArray[i].colIndex = -1; CollectiveArray[i].pedIndex = 0; } else { pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(objective, p1, p2); } } } } void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective, CVector p1, float p2) { for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { if (CollectiveArray[i].colIndex == colIndex) { CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); if (pPed == nil) { CollectiveArray[i].colIndex = -1; CollectiveArray[i].pedIndex = 0; } else { pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(objective, p1, p2); } } } } void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective, CVector p1) { for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { if (CollectiveArray[i].colIndex == colIndex) { CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); if (pPed == nil) { CollectiveArray[i].colIndex = -1; CollectiveArray[i].pedIndex = 0; } else { pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(objective, p1); } } } } void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective, void* p1) { for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { if (CollectiveArray[i].colIndex == colIndex) { CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); if (pPed == nil) { CollectiveArray[i].colIndex = -1; CollectiveArray[i].pedIndex = 0; } else { pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(objective, p1); } } } } void CTheScripts::SetObjectiveForAllPedsInCollective(int colIndex, eObjective objective) { for (int i = 0; i < MAX_NUM_COLLECTIVES; i++) { if (CollectiveArray[i].colIndex == colIndex) { CPed* pPed = CPools::GetPedPool()->GetAt(CollectiveArray[i].pedIndex); if (pPed == nil) { CollectiveArray[i].colIndex = -1; CollectiveArray[i].pedIndex = 0; } else { pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(objective); } } } } #endif //GTA_SCRIPT_COLLECTIVE bool CTheScripts::IsPedStopped(CPed* pPed) { if (pPed->InVehicle()) return IsVehicleStopped(pPed->m_pMyVehicle); return (pPed->m_nMoveState == PEDMOVE_NONE || pPed->m_nMoveState == PEDMOVE_STILL) && !pPed->bIsInTheAir && !pPed->bIsLanding && pPed->bIsStanding && pPed->m_vecAnimMoveDelta.x == 0.0f && pPed->m_vecAnimMoveDelta.y == 0.0f; } bool CTheScripts::IsPlayerStopped(CPlayerInfo* pPlayer) { CPed* pPed = pPlayer->m_pPed; if (pPed->InVehicle()) return IsVehicleStopped(pPed->m_pMyVehicle); if (RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_RUNSTOP1) || RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_RUNSTOP2) || RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_JUMP_LAUNCH) || RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_JUMP_GLIDE)) return false; return (pPed->m_nMoveState == PEDMOVE_NONE || pPed->m_nMoveState == PEDMOVE_STILL) && !pPed->bIsInTheAir && !pPed->bIsLanding && pPed->bIsStanding && pPed->m_vecAnimMoveDelta.x == 0.0f && pPed->m_vecAnimMoveDelta.y == 0.0f; } bool CTheScripts::IsVehicleStopped(CVehicle* pVehicle) { return 0.01f * CTimer::GetTimeStep() >= pVehicle->m_fDistanceTravelled; } void CTheScripts::RemoveThisPed(CPed* pPed) { if (pPed) { bool bWasMissionPed = pPed->CharCreatedBy == MISSION_CHAR; if (pPed->InVehicle() && pPed->m_pMyVehicle) { if (pPed->m_pMyVehicle->pDriver == pPed) { pPed->m_pMyVehicle->RemoveDriver(); pPed->m_pMyVehicle->SetStatus(STATUS_ABANDONED); if (pPed->m_pMyVehicle->m_nDoorLock == CARLOCK_LOCKED_INITIALLY) pPed->m_pMyVehicle->m_nDoorLock = CARLOCK_UNLOCKED; if (pPed->m_nPedType == PEDTYPE_COP && pPed->m_pMyVehicle->IsLawEnforcementVehicle()) pPed->m_pMyVehicle->ChangeLawEnforcerState(0); } else { pPed->m_pMyVehicle->RemovePassenger(pPed); } } CWorld::RemoveReferencesToDeletedObject(pPed); delete pPed; if (bWasMissionPed) --CPopulation::ms_nTotalMissionPeds; } } void CTheScripts::CleanUpThisPed(CPed* pPed) { if (!pPed) return; if (pPed->CharCreatedBy != MISSION_CHAR) return; pPed->CharCreatedBy = RANDOM_CHAR; if (pPed->m_nPedType == PEDTYPE_PROSTITUTE) pPed->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 30000; if (pPed->InVehicle()) { if (pPed->m_pMyVehicle->pDriver == pPed) { if (pPed->m_pMyVehicle->m_vehType == VEHICLE_TYPE_CAR) { CCarCtrl::JoinCarWithRoadSystem(pPed->m_pMyVehicle); pPed->m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; } } else { if (pPed->m_pMyVehicle->m_vehType == VEHICLE_TYPE_CAR) { pPed->SetObjective(OBJECTIVE_LEAVE_CAR, pPed->m_pMyVehicle); pPed->bWanderPathAfterExitingCar = true; } } } bool flees = false; PedState state; eMoveState ms; if (pPed->m_nPedState == PED_FLEE_ENTITY || pPed->m_nPedState == PED_FLEE_POS) { ms = pPed->m_nMoveState; state = pPed->m_nPedState; flees = true; } pPed->ClearObjective(); pPed->bRespondsToThreats = true; pPed->bScriptObjectiveCompleted = false; pPed->bKindaStayInSamePlace = false; pPed->ClearLeader(); if (pPed->IsPedInControl()) pPed->SetWanderPath(CGeneral::GetRandomNumber() & 7); if (flees) { pPed->SetPedState(state); pPed->SetMoveState(ms); } --CPopulation::ms_nTotalMissionPeds; } void CTheScripts::CleanUpThisVehicle(CVehicle* pVehicle) { if (!pVehicle) return; if (pVehicle->VehicleCreatedBy != MISSION_VEHICLE) return; pVehicle->bIsLocked = false; CCarCtrl::RemoveFromInterestingVehicleList(pVehicle); pVehicle->VehicleCreatedBy = RANDOM_VEHICLE; ++CCarCtrl::NumRandomCars; --CCarCtrl::NumMissionCars; } void CTheScripts::CleanUpThisObject(CObject* pObject) { if (!pObject) return; if (pObject->ObjectCreatedBy != MISSION_OBJECT) return; pObject->ObjectCreatedBy = TEMP_OBJECT; pObject->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000000; pObject->m_nRefModelIndex = -1; pObject->bUseVehicleColours = false; ++CObject::nNoTempObjects; } void CTheScripts::ReadObjectNamesFromScript() { int32 varSpace = GetSizeOfVariableSpace(); uint32 ip = varSpace + 8; NumberOfUsedObjects = Read2BytesFromScript(&ip); ip += 2; for (uint16 i = 0; i < NumberOfUsedObjects; i++) { for (int j = 0; j < USED_OBJECT_NAME_LENGTH; j++) UsedObjectArray[i].name[j] = ScriptSpace[ip++]; UsedObjectArray[i].index = 0; } } void CTheScripts::UpdateObjectIndices() { char name[USED_OBJECT_NAME_LENGTH]; char error[112]; for (int i = 1; i < NumberOfUsedObjects; i++) { bool found = false; for (int j = 0; j < MODELINFOSIZE && !found; j++) { CBaseModelInfo* pModel = CModelInfo::GetModelInfo(j); if (!pModel) continue; strcpy(name, pModel->GetModelName()); #ifdef FIX_BUGS for (int k = 0; k < USED_OBJECT_NAME_LENGTH && name[k]; k++) #else for (int k = 0; k < USED_OBJECT_NAME_LENGTH; k++) #endif name[k] = toupper(name[k]); if (strcmp(name, UsedObjectArray[i].name) == 0) { found = true; UsedObjectArray[i].index = j; } } if (!found) { sprintf(error, "CTheScripts::UpdateObjectIndices - Couldn't find %s", UsedObjectArray[i].name); debug("%s\n", error); } } } void CTheScripts::ReadMultiScriptFileOffsetsFromScript() { int32 varSpace = GetSizeOfVariableSpace(); uint32 ip = varSpace + 3; int32 objectSize = Read4BytesFromScript(&ip); ip = objectSize + 8; MainScriptSize = Read4BytesFromScript(&ip); LargestMissionScriptSize = Read4BytesFromScript(&ip); NumberOfMissionScripts = Read2BytesFromScript(&ip); NumberOfExclusiveMissionScripts = Read2BytesFromScript(&ip); for (int i = 0; i < NumberOfMissionScripts; i++) { MultiScriptArray[i] = Read4BytesFromScript(&ip); } } ================================================ FILE: src/control/Script6.cpp ================================================ #include "common.h" #include "Script.h" #include "ScriptCommands.h" #include "Bike.h" #include "CarCtrl.h" #include "Cranes.h" #include "Credits.h" #include "CutsceneMgr.h" #include "DMAudio.h" #include "FileMgr.h" #include "Fire.h" #include "Frontend.h" #include "Garages.h" #include "General.h" #ifdef MISSION_REPLAY #include "GenericGameStorage.h" #endif #include "Messages.h" #include "Pad.h" #include "Particle.h" #include "Phones.h" #include "Population.h" #include "Pools.h" #include "Record.h" #include "Remote.h" #include "Restart.h" #include "SpecialFX.h" #include "Stats.h" #include "Streaming.h" #include "Weather.h" #include "Zones.h" #include "main.h" #include "GameLogic.h" #include "Sprite.h" #include "CarAI.h" #include "Pickups.h" #include "Fluff.h" #ifdef USE_DEBUG_SCRIPT_LOADER extern const char* scriptfile; #endif bool CRunningScript::ThisIsAValidRandomCop(uint32 mi, int cop, int swat, int fbi, int army, int miami) { switch (mi) { case MI_COP: if (cop) return true; break; case MI_SWAT: if (swat) return true; break; case MI_FBI: if (fbi) return true; break; case MI_ARMY: if (army) return true; break; default: if (mi >= MI_VICE1 && mi <= MI_VICE8 && miami) return true; break; } return false; } bool CRunningScript::ThisIsAValidRandomPed(uint32 pedtype, int civ, int gang, int criminal) { switch (pedtype) { case PEDTYPE_CIVMALE: case PEDTYPE_CIVFEMALE: return civ; case PEDTYPE_GANG1: case PEDTYPE_GANG2: case PEDTYPE_GANG3: case PEDTYPE_GANG4: case PEDTYPE_GANG5: case PEDTYPE_GANG6: case PEDTYPE_GANG7: case PEDTYPE_GANG8: case PEDTYPE_GANG9: return gang; case PEDTYPE_CRIMINAL: case PEDTYPE_PROSTITUTE: return criminal; default: return false; } } int8 CRunningScript::ProcessCommands1000To1099(int32 command) { switch (command) { //case COMMAND_FLASH_RADAR_BLIP: /* case COMMAND_IS_CHAR_IN_CONTROL: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); UpdateCompareFlag(pPed->IsPedInControl()); return 0; } */ case COMMAND_SET_GENERATE_CARS_AROUND_CAMERA: CollectParameters(&m_nIp, 1); CCarCtrl::bCarsGeneratedAroundCamera = (ScriptParams[0] != 0); return 0; case COMMAND_CLEAR_SMALL_PRINTS: CMessages::ClearSmallMessagesOnly(); return 0; /* case COMMAND_HAS_MILITARY_CRANE_COLLECTED_ALL_CARS: UpdateCompareFlag(CCranes::HaveAllCarsBeenCollectedByMilitaryCrane()); return 0; */ case COMMAND_SET_UPSIDEDOWN_CAR_NOT_DAMAGED: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); CAutomobile* pCar = (CAutomobile*)pVehicle; pCar->bNotDamagedUpsideDown = (ScriptParams[1] != 0); return 0; } case COMMAND_CAN_PLAYER_START_MISSION: { CollectParameters(&m_nIp, 1); CPlayerPed* pPlayerPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPlayerPed); UpdateCompareFlag(pPlayerPed->IsPedInControl() || pPlayerPed->m_nPedState == PED_DRIVING); return 0; } case COMMAND_MAKE_PLAYER_SAFE_FOR_CUTSCENE: { CollectParameters(&m_nIp, 1); #ifdef MISSION_REPLAY AllowMissionReplay = 0; SaveGameForPause(3); #endif CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; CPad::GetPad(ScriptParams[0])->SetDisablePlayerControls(PLAYERCONTROL_CUTSCENE); pPlayerInfo->MakePlayerSafe(true); CCutsceneMgr::StartCutsceneProcessing(); return 0; } case COMMAND_USE_TEXT_COMMANDS: CollectParameters(&m_nIp, 1); CTheScripts::UseTextCommands = (ScriptParams[0] != 0) ? 2 : 1; return 0; case COMMAND_SET_THREAT_FOR_PED_TYPE: CollectParameters(&m_nIp, 2); CPedType::AddThreat(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_CLEAR_THREAT_FOR_PED_TYPE: CollectParameters(&m_nIp, 2); CPedType::RemoveThreat(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_GET_CAR_COLOURS: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); ScriptParams[0] = pVehicle->m_currentColour1; ScriptParams[1] = pVehicle->m_currentColour2; StoreParameters(&m_nIp, 2); return 0; } case COMMAND_SET_ALL_CARS_CAN_BE_DAMAGED: CollectParameters(&m_nIp, 1); CWorld::SetAllCarsCanBeDamaged(ScriptParams[0] != 0); if (!ScriptParams[0]) CWorld::ExtinguishAllCarFiresInArea(FindPlayerCoors(), 4000.0f); return 0; case COMMAND_SET_CAR_CAN_BE_DAMAGED: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); pVehicle->bCanBeDamaged = ScriptParams[1] != 0; if (!ScriptParams[1]) pVehicle->ExtinguishCarFire(); return 0; } //case COMMAND_MAKE_PLAYER_UNSAFE: /* case COMMAND_LOAD_COLLISION: { CollectParameters(&m_nIp, 1); CTimer::Stop(); CGame::currLevel = (eLevelName)ScriptParams[0]; ISLAND_LOADING_IS(LOW) { CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); CStreaming::RemoveUnusedBuildings(CGame::currLevel); } CCollision::SortOutCollisionAfterLoad(); ISLAND_LOADING_ISNT(HIGH) { CStreaming::RequestIslands(CGame::currLevel); CStreaming::LoadAllRequestedModels(true); } CTimer::Update(); return 0; } case COMMAND_GET_BODY_CAST_HEALTH: // ScriptParams[0] = CObject::nBodyCastHealth; // StoreParameters(&m_nIp, 1); return 0; */ case COMMAND_SET_CHARS_CHATTING: { CollectParameters(&m_nIp, 3); CPed* pPed1 = CPools::GetPedPool()->GetAt(ScriptParams[0]); CPed* pPed2 = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pPed1 && pPed2); pPed1->SetChat(pPed2, ScriptParams[2]); pPed2->SetChat(pPed1, ScriptParams[2]); return 0; } //case COMMAND_MAKE_PLAYER_SAFE: /* case COMMAND_SET_CAR_STAYS_IN_CURRENT_LEVEL: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); if (ScriptParams[1]) pVehicle->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pVehicle->GetPosition()); else pVehicle->m_nZoneLevel = LEVEL_GENERIC; return 0; } case COMMAND_SET_CHAR_STAYS_IN_CURRENT_LEVEL: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (ScriptParams[1]) pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pPed->GetPosition()); else pPed->m_nZoneLevel = LEVEL_GENERIC; return 0; } */ case COMMAND_SET_DRUNK_INPUT_DELAY: { CollectParameters(&m_nIp, 2); assert(ScriptParams[1] < CPad::DRUNK_STEERING_BUFFER_SIZE); CPad::GetPad(ScriptParams[0])->SetDrunkInputDelay(ScriptParams[1]); return 0; } case COMMAND_SET_CHAR_MONEY: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->m_nPedMoney = ScriptParams[1]; pPed->bMoneyHasBeenGivenByScript = true; return 0; } //case COMMAND_INCREASE_CHAR_MONEY: case COMMAND_GET_OFFSET_FROM_OBJECT_IN_WORLD_COORDS: { CollectParameters(&m_nIp, 4); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CVector result = Multiply3x3(pObject->GetMatrix(), *(CVector*)&ScriptParams[1]) + pObject->GetPosition(); *(CVector*)&ScriptParams[0] = result; StoreParameters(&m_nIp, 3); return 0; } case COMMAND_REGISTER_LIFE_SAVED: CStats::AnotherLifeSavedWithAmbulance(); return 0; case COMMAND_REGISTER_CRIMINAL_CAUGHT: CStats::AnotherCriminalCaught(); return 0; case COMMAND_REGISTER_AMBULANCE_LEVEL: CollectParameters(&m_nIp, 1); CStats::RegisterLevelAmbulanceMission(ScriptParams[0]); return 0; case COMMAND_REGISTER_FIRE_EXTINGUISHED: CStats::AnotherFireExtinguished(); return 0; case COMMAND_TURN_PHONE_ON: CollectParameters(&m_nIp, 1); gPhoneInfo.m_aPhones[ScriptParams[0]].m_nState = PHONE_STATE_9; return 0; /* case COMMAND_REGISTER_LONGEST_DODO_FLIGHT: CollectParameters(&m_nIp, 1); CStats::RegisterLongestFlightInDodo(ScriptParams[0]); return 0; */ case COMMAND_GET_OFFSET_FROM_CAR_IN_WORLD_COORDS: { CollectParameters(&m_nIp, 4); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CVector result = Multiply3x3(pVehicle->GetMatrix(), *(CVector*)&ScriptParams[1]) + pVehicle->GetPosition(); *(CVector*)&ScriptParams[0] = result; StoreParameters(&m_nIp, 3); return 0; } case COMMAND_SET_TOTAL_NUMBER_OF_KILL_FRENZIES: CollectParameters(&m_nIp, 1); CStats::SetTotalNumberKillFrenzies(ScriptParams[0]); return 0; case COMMAND_BLOW_UP_RC_BUGGY: CWorld::Players[CWorld::PlayerInFocus].BlowUpRCBuggy(true); return 0; /* case COMMAND_REMOVE_CAR_FROM_CHASE: CollectParameters(&m_nIp, 1); CRecordDataForChase::RemoveCarFromChase(ScriptParams[0]); return 0; */ case COMMAND_IS_FRENCH_GAME: UpdateCompareFlag(CGame::frenchGame); return 0; case COMMAND_IS_GERMAN_GAME: UpdateCompareFlag(CGame::germanGame); return 0; case COMMAND_CLEAR_MISSION_AUDIO: CollectParameters(&m_nIp, 1); DMAudio.ClearMissionAudio(ScriptParams[0] - 1); return 0; /* case COMMAND_SET_FADE_IN_AFTER_NEXT_ARREST: CollectParameters(&m_nIp, 1); CRestart::bFadeInAfterNextArrest = !!ScriptParams[0]; return 0; case COMMAND_SET_FADE_IN_AFTER_NEXT_DEATH: CollectParameters(&m_nIp, 1); CRestart::bFadeInAfterNextDeath = !!ScriptParams[0]; return 0; case COMMAND_SET_GANG_PED_MODEL_PREFERENCE: CollectParameters(&m_nIp, 2); CGangs::SetGangPedModelOverride(ScriptParams[0], ScriptParams[1]); return 0; */ case COMMAND_SET_CHAR_USE_PEDNODE_SEEK: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (ScriptParams[1]) pPed->m_pNextPathNode = nil; pPed->bUsePedNodeSeek = !!ScriptParams[1]; return 0; } /* case COMMAND_SWITCH_VEHICLE_WEAPONS: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bGunSwitchedOff = !ScriptParams[1]; return 0; } case COMMAND_SET_GET_OUT_OF_JAIL_FREE: CollectParameters(&m_nIp, 2); CWorld::Players[ScriptParams[0]].m_bGetOutOfJailFree = !!ScriptParams[1]; return 0; */ case COMMAND_SET_FREE_HEALTH_CARE: CollectParameters(&m_nIp, 2); CWorld::Players[ScriptParams[0]].m_bGetOutOfHospitalFree = !!ScriptParams[1]; return 0; /* case COMMAND_IS_CAR_DOOR_CLOSED: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(!pVehicle->IsDoorMissing((eDoors)ScriptParams[1]) && pVehicle->IsDoorClosed((eDoors)ScriptParams[1])); return 0; } */ case COMMAND_LOAD_AND_LAUNCH_MISSION: return 0; case COMMAND_LOAD_AND_LAUNCH_MISSION_INTERNAL: { CollectParameters(&m_nIp, 1); if (CTheScripts::NumberOfExclusiveMissionScripts > 0 && ScriptParams[0] <= UINT16_MAX - 2) return 0; #ifdef MISSION_REPLAY missionRetryScriptIndex = ScriptParams[0]; if (missionRetryScriptIndex == 19) CStats::LastMissionPassedName[0] = '\0'; #endif CTimer::Suspend(); int offset = CTheScripts::MultiScriptArray[ScriptParams[0]]; #ifdef USE_DEBUG_SCRIPT_LOADER CFileMgr::ChangeDir("\\data\\"); int handle = CFileMgr::OpenFile(scriptfile, "rb"); CFileMgr::ChangeDir("\\"); #else CFileMgr::ChangeDir("\\"); int handle = CFileMgr::OpenFile("data\\main.scm", "rb"); #endif CFileMgr::Seek(handle, offset, 0); CFileMgr::Read(handle, (const char*)&CTheScripts::ScriptSpace[SIZE_MAIN_SCRIPT], SIZE_MISSION_SCRIPT); CFileMgr::CloseFile(handle); CRunningScript* pMissionScript = CTheScripts::StartNewScript(SIZE_MAIN_SCRIPT); CTimer::Resume(); pMissionScript->m_bIsMissionScript = true; pMissionScript->m_bMissionFlag = true; CTheScripts::bAlreadyRunningAMissionScript = true; CGameLogic::ClearShortCut(); return 0; } case COMMAND_SET_OBJECT_DRAW_LAST: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); pObject->bDrawLast = !!ScriptParams[1]; return 0; } case COMMAND_GET_AMMO_IN_PLAYER_WEAPON: { CollectParameters(&m_nIp, 2); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); ScriptParams[0] = 0; for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { if (pPed->GetWeapon(i).m_eWeaponType == (eWeaponType)ScriptParams[1]) ScriptParams[0] = pPed->GetWeapon(i).m_nAmmoTotal; } StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_GET_AMMO_IN_CHAR_WEAPON: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CWeapon* pWeaponSlot = &pPed->m_weapons[ScriptParams[1]]; if (pWeaponSlot->m_eWeaponType == (eWeaponType)ScriptParams[1]) ScriptParams[0] = pWeaponSlot->m_nAmmoTotal; else ScriptParams[0] = 0; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_REGISTER_KILL_FRENZY_PASSED: CStats::AnotherKillFrenzyPassed(); return 0; case COMMAND_SET_CHAR_SAY: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); switch (ScriptParams[1]) { case SCRIPT_SOUND_CHUNKY_RUN_SHOUT: pPed->Say(SOUND_PED_FLEE_RUN); break; case SCRIPT_SOUND_SECURITY_GUARD_AWAY_SHOUT: pPed->Say(SOUND_PED_FLEE_RUN); break; case SCRIPT_SOUND_SWAT_PED_SHOUT: pPed->Say(SOUND_PED_PURSUIT_SWAT); break; case SCRIPT_SOUND_AMMUNATION_CHAT_1: pPed->Say(SOUND_AMMUNATION_WELCOME_1); break; case SCRIPT_SOUND_AMMUNATION_CHAT_2: pPed->Say(SOUND_AMMUNATION_WELCOME_2); break; case SCRIPT_SOUND_AMMUNATION_CHAT_3: pPed->Say(SOUND_AMMUNATION_WELCOME_3); break; default: break; } return 0; } */ case COMMAND_SET_NEAR_CLIP: CollectParameters(&m_nIp, 1); TheCamera.SetNearClipScript(*(float*)&ScriptParams[0]); return 0; case COMMAND_SET_RADIO_CHANNEL: CollectParameters(&m_nIp, 2); DMAudio.SetRadioChannel(ScriptParams[0], ScriptParams[1]); return 0; /* case COMMAND_OVERRIDE_HOSPITAL_LEVEL: CollectParameters(&m_nIp, 1); CRestart::OverrideHospitalLevel = ScriptParams[0]; return 0; case COMMAND_OVERRIDE_POLICE_STATION_LEVEL: CollectParameters(&m_nIp, 1); CRestart::OverridePoliceStationLevel = ScriptParams[0]; return 0; case COMMAND_FORCE_RAIN: CollectParameters(&m_nIp, 1); CWeather::bScriptsForceRain = !!ScriptParams[0]; return 0; case COMMAND_DOES_GARAGE_CONTAIN_CAR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pVehicle); UpdateCompareFlag(CGarages::IsThisCarWithinGarageArea(ScriptParams[0], pVehicle)); return 0; } */ case COMMAND_SET_CAR_TRACTION: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); float fTraction = *(float*)&ScriptParams[1]; script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR || pVehicle->m_vehType == VEHICLE_TYPE_BIKE); if (pVehicle->m_vehType == VEHICLE_TYPE_CAR) ((CAutomobile*)pVehicle)->m_fTraction = fTraction; else ((CBike*)pVehicle)->m_fTraction = fTraction; return 0; } case COMMAND_ARE_MEASUREMENTS_IN_METRES: #ifdef USE_MEASUREMENTS_IN_METERS UpdateCompareFlag(true); #else UpdateCompareFlag(false); #endif return 0; case COMMAND_CONVERT_METRES_TO_FEET: { CollectParameters(&m_nIp, 1); float fMeterValue = *(float*)&ScriptParams[0]; float fFeetValue = fMeterValue / METERS_IN_FOOT; *(float*)&ScriptParams[0] = fFeetValue; StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_MARK_ROADS_BETWEEN_LEVELS: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } ThePaths.MarkRoadsBetweenLevelsInArea(infX, supX, infY, supY, infZ, supZ); return 0; } case COMMAND_MARK_PED_ROADS_BETWEEN_LEVELS: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } ThePaths.PedMarkRoadsBetweenLevelsInArea(infX, supX, infY, supY, infZ, supZ); return 0; } */ case COMMAND_SET_CAR_AVOID_LEVEL_TRANSITIONS: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->AutoPilot.m_bStayInCurrentLevel = !!ScriptParams[1]; return 0; } /* case COMMAND_SET_CHAR_AVOID_LEVEL_TRANSITIONS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pPed); // not implemented return 0; } case COMMAND_IS_THREAT_FOR_PED_TYPE: CollectParameters(&m_nIp, 2); UpdateCompareFlag(CPedType::IsThreat(ScriptParams[0], ScriptParams[1])); return 0; */ case COMMAND_CLEAR_AREA_OF_CHARS: { CollectParameters(&m_nIp, 6); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float infZ = *(float*)&ScriptParams[2]; float supX = *(float*)&ScriptParams[3]; float supY = *(float*)&ScriptParams[4]; float supZ = *(float*)&ScriptParams[5]; if (infX > supX) { infX = *(float*)&ScriptParams[3]; supX = *(float*)&ScriptParams[0]; } if (infY > supY) { infY = *(float*)&ScriptParams[4]; supY = *(float*)&ScriptParams[1]; } if (infZ > supZ) { infZ = *(float*)&ScriptParams[5]; supZ = *(float*)&ScriptParams[2]; } CWorld::ClearPedsFromArea(infX, infY, infZ, supX, supY, supZ); return 0; } case COMMAND_SET_TOTAL_NUMBER_OF_MISSIONS: CollectParameters(&m_nIp, 1); CStats::SetTotalNumberMissions(CGame::germanGame ? ScriptParams[0] - 2 : ScriptParams[0]); return 0; case COMMAND_CONVERT_METRES_TO_FEET_INT: CollectParameters(&m_nIp, 1); ScriptParams[0] *= FEET_IN_METER; StoreParameters(&m_nIp, 1); return 0; case COMMAND_REGISTER_FASTEST_TIME: CollectParameters(&m_nIp, 2); CStats::RegisterFastestTime(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_REGISTER_HIGHEST_SCORE: CollectParameters(&m_nIp, 2); CStats::RegisterHighestScore(ScriptParams[0], ScriptParams[1]); return 0; //case COMMAND_WARP_CHAR_INTO_CAR_AS_PASSENGER: case COMMAND_IS_CAR_PASSENGER_SEAT_FREE: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(ScriptParams[1] < pVehicle->m_nNumMaxPassengers && pVehicle->pPassengers[ScriptParams[1]] == nil); return 0; } /* case COMMAND_GET_CHAR_IN_CAR_PASSENGER_SEAT: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); script_assert(ScriptParams[1] >= 0 && ScriptParams[1] < ARRAY_SIZE(pVehicle->pPassengers)); CPed* pPassenger = pVehicle->pPassengers[ScriptParams[1]]; ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPassenger); StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_SET_CHAR_IS_CHRIS_CRIMINAL: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bChrisCriminal = !!ScriptParams[1]; return 0; } case COMMAND_START_CREDITS: CCredits::Start(); return 0; case COMMAND_STOP_CREDITS: CCredits::Stop(); return 0; case COMMAND_ARE_CREDITS_FINISHED: UpdateCompareFlag(CCredits::AreCreditsDone()); return 0; case COMMAND_CREATE_SINGLE_PARTICLE: CollectParameters(&m_nIp, 8); CParticle::AddParticle((tParticleType)ScriptParams[0], *(CVector*)&ScriptParams[1], *(CVector*)&ScriptParams[4], nil, *(float*)&ScriptParams[7], 0, 0, 0, 0); return 0; /* case COMMAND_SET_CHAR_IGNORE_LEVEL_TRANSITIONS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (ScriptParams[1]) pPed->m_nZoneLevel = LEVEL_IGNORE; else pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pPed->GetPosition()); return 0; } case COMMAND_GET_CHASE_CAR: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CRecordDataForChase::TurnChaseCarIntoScriptCar(ScriptParams[0]); ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CAR); return 0; } case COMMAND_START_BOAT_FOAM_ANIMATION: CSpecialParticleStuff::StartBoatFoamAnimation(); return 0; case COMMAND_UPDATE_BOAT_FOAM_ANIMATION: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CSpecialParticleStuff::UpdateBoatFoamAnimation(&pObject->GetMatrix()); return 0; } */ case COMMAND_SET_MUSIC_DOES_FADE: CollectParameters(&m_nIp, 1); TheCamera.m_bIgnoreFadingStuffForMusic = (ScriptParams[0] == 0); return 0; /* case COMMAND_SET_INTRO_IS_PLAYING: CollectParameters(&m_nIp, 1); if (ScriptParams[0]) { CGame::playingIntro = true; CStreaming::RemoveCurrentZonesModels(); } else { CGame::playingIntro = false; DMAudio.ChangeMusicMode(MUSICMODE_GAME); int mi; CModelInfo::GetModelInfo("bridgefukb", &mi); CStreaming::RequestModel(mi, STREAMFLAGS_DEPENDENCY); CStreaming::LoadAllRequestedModels(false); } return 0; */ case COMMAND_SET_PLAYER_HOOKER: { CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; if (ScriptParams[1] < 0) { pPlayerInfo->m_pHooker = nil; pPlayerInfo->m_nNextSexFrequencyUpdateTime = 0; pPlayerInfo->m_nNextSexMoneyUpdateTime = 0; } else { CPed* pHooker = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pHooker); pPlayerInfo->m_pHooker = (CCivilianPed*)pHooker; pPlayerInfo->m_nSexFrequency = 1000; pPlayerInfo->m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 1000; pPlayerInfo->m_nNextSexMoneyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000; } return 0; } case COMMAND_PLAY_END_OF_GAME_TUNE: DMAudio.PlayPreloadedCutSceneMusic(); return 0; case COMMAND_STOP_END_OF_GAME_TUNE: DMAudio.StopCutSceneMusic(); DMAudio.ChangeMusicMode(MUSICMODE_GAME); return 0; case COMMAND_GET_CAR_MODEL: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); ScriptParams[0] = pVehicle->GetModelIndex(); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_IS_PLAYER_SITTING_IN_CAR: { CollectParameters(&m_nIp, 2); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); UpdateCompareFlag(pPed->GetPedState() == PED_DRIVING && pPed->m_objective != OBJECTIVE_LEAVE_CAR && pPed->m_pMyVehicle == pVehicle); return 0; } case COMMAND_IS_PLAYER_SITTING_IN_ANY_CAR: { CollectParameters(&m_nIp, 1); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->GetPedState() == PED_DRIVING && pPed->m_objective != OBJECTIVE_LEAVE_CAR); return 0; } /* case COMMAND_SET_SCRIPT_FIRE_AUDIO: CollectParameters(&m_nIp, 2); gFireManager.SetScriptFireAudio(ScriptParams[0], !!ScriptParams[1]); return 0; */ case COMMAND_ARE_ANY_CAR_CHEATS_ACTIVATED: UpdateCompareFlag(CVehicle::bAllDodosCheat || CVehicle::bCheat3 || CVehicle::bHoverCheat || CVehicle::bCheat8 || CVehicle::bCheat9); return 0; case COMMAND_SET_CHAR_SUFFERS_CRITICAL_HITS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bNoCriticalHits = (ScriptParams[1] == 0); return 0; } /* case COMMAND_IS_PLAYER_LIFTING_A_PHONE: { CollectParameters(&m_nIp, 1); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->GetPedState() == PED_MAKE_CALL); return 0; } */ case COMMAND_IS_CHAR_SITTING_IN_CAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pVehicle); UpdateCompareFlag(pPed->GetPedState() == PED_DRIVING && pPed->m_objective != OBJECTIVE_LEAVE_CAR && pPed->m_pMyVehicle == pVehicle); return 0; } case COMMAND_IS_CHAR_SITTING_IN_ANY_CAR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->GetPedState() == PED_DRIVING && pPed->m_objective != OBJECTIVE_LEAVE_CAR); return 0; } case COMMAND_IS_PLAYER_ON_FOOT: { CollectParameters(&m_nIp, 1); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(!pPed->bInVehicle && pPed->m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && pPed->m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER); return 0; } case COMMAND_IS_CHAR_ON_FOOT: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(!pPed->bInVehicle && pPed->m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && pPed->m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER); return 0; } default: script_assert(0); } return -1; } int8 CRunningScript::ProcessCommands1100To1199(int32 command) { char tmp[48]; switch (command) { /* case COMMAND_LOAD_COLLISION_WITH_SCREEN: CollectParameters(&m_nIp, 1); CTimer::Stop(); CGame::currLevel = (eLevelName)ScriptParams[0]; if (CGame::currLevel != CCollision::ms_collisionInMemory) { ISLAND_LOADING_IS(LOW) { DMAudio.SetEffectsFadeVol(0); CPad::StopPadsShaking(); CCollision::LoadCollisionScreen(CGame::currLevel); DMAudio.Service(); } CPopulation::DealWithZoneChange(CCollision::ms_collisionInMemory, CGame::currLevel, false); ISLAND_LOADING_IS(LOW) { CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); CStreaming::RemoveUnusedBuildings(CGame::currLevel); } CCollision::SortOutCollisionAfterLoad(); ISLAND_LOADING_ISNT(HIGH) CStreaming::RequestIslands(CGame::currLevel); ISLAND_LOADING_IS(LOW) CStreaming::RequestBigBuildings(CGame::currLevel); ISLAND_LOADING_ISNT(HIGH) CStreaming::LoadAllRequestedModels(true); ISLAND_LOADING_IS(LOW) DMAudio.SetEffectsFadeVol(127); } CTimer::Update(); return 0; */ case COMMAND_LOAD_SPLASH_SCREEN: CTheScripts::ReadTextLabelFromScript(&m_nIp, tmp); for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) tmp[i] = tolower(tmp[i]); m_nIp += 8; LoadSplash(tmp); return 0; /* case COMMAND_SET_CAR_IGNORE_LEVEL_TRANSITIONS: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); if (ScriptParams[1]) pVehicle->m_nZoneLevel = LEVEL_IGNORE; else pVehicle->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pVehicle->GetPosition()); return 0; } case COMMAND_MAKE_CRAIGS_CAR_A_BIT_STRONGER: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); script_assert(pVehicle->m_vehType == VEHICLE_TYPE_CAR); CAutomobile* pCar = (CAutomobile*)pVehicle; pCar->bMoreResistantToDamage = ScriptParams[1]; return 0; } */ case COMMAND_SET_JAMES_CAR_ON_PATH_TO_PLAYER: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CCarCtrl::JoinCarWithRoadSystemGotoCoors(pVehicle, FindPlayerCoors(), false); return 0; } case COMMAND_LOAD_END_OF_GAME_TUNE: DMAudio.ChangeMusicMode(MUSICMODE_CUTSCENE); printf("Start preload end of game audio\n"); DMAudio.PreloadCutSceneMusic(STREAMED_SOUND_CUTSCENE_FINALE); printf("End preload end of game audio\n"); return 0; /* case COMMAND_ENABLE_PLAYER_CONTROL_CAMERA: CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_CAMERA); return 0; */ case COMMAND_SET_OBJECT_ROTATION: { CollectParameters(&m_nIp, 4); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CWorld::Remove(pObject); pObject->SetOrientation( DEGTORAD(*(float*)&ScriptParams[1]), DEGTORAD(*(float*)&ScriptParams[2]), DEGTORAD(*(float*)&ScriptParams[3])); pObject->GetMatrix().UpdateRW(); pObject->UpdateRwFrame(); CWorld::Add(pObject); return 0; } case COMMAND_GET_DEBUG_CAMERA_COORDINATES: *(CVector*)&ScriptParams[0] = TheCamera.Cams[2].Source; StoreParameters(&m_nIp, 3); return 0; /* case COMMAND_GET_DEBUG_CAMERA_FRONT_VECTOR: *(CVector*)&ScriptParams[0] = TheCamera.Cams[2].Front; StoreParameters(&m_nIp, 3); return 0; case COMMAND_IS_PLAYER_TARGETTING_ANY_CHAR: { CollectParameters(&m_nIp, 1); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); CEntity* pTarget = pPed->m_pPointGunAt; UpdateCompareFlag(pTarget && pTarget->IsPed()); return 0; } */ case COMMAND_IS_PLAYER_TARGETTING_CHAR: { CollectParameters(&m_nIp, 2); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); CPed* pTestedPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pTestedPed); CEntity* pTarget = pPed->m_pPointGunAt; bool bTargetting = pTarget && pTarget->IsPed() && pTarget == pTestedPed; // PC shit static int nCounter = 0; nCounter = Max(0, nCounter - 1); if (!pPed->GetWeapon()->IsTypeMelee() && !bTargetting) { if ((pTestedPed->GetPosition() - TheCamera.GetPosition()).Magnitude() < 10.0f) { CVector vTestedPos(pTestedPed->GetPosition().x, pTestedPed->GetPosition().y, pTestedPed->GetPosition().z + 0.4); CVector vScreenPos; float w, h; if (CSprite::CalcScreenCoors(vTestedPos, &vScreenPos, &w, &h, false)) { CVector2D vCrosshairPosition(CCamera::m_f3rdPersonCHairMultX * RsGlobal.maximumWidth, CCamera::m_f3rdPersonCHairMultY * RsGlobal.maximumHeight); float fScreenDistance = ((CVector2D)vScreenPos - vCrosshairPosition).Magnitude(); if (SCREEN_STRETCH_X(0.45f) > fScreenDistance / w) { CColPoint point; CEntity* entity; if (!CWorld::ProcessLineOfSight(TheCamera.GetPosition() + 2.0f * TheCamera.GetForward(), vTestedPos, point, entity, true, true, true, true, true, false) || entity == pTestedPed) { nCounter += 2; if (nCounter > 20) { bTargetting = true; nCounter = 20; } } } } } } UpdateCompareFlag(bTargetting); return 0; } /* case COMMAND_IS_PLAYER_TARGETTING_OBJECT: { CollectParameters(&m_nIp, 2); CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); CObject* pTestedObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); script_assert(pTestedObject); CEntity* pTarget = pPed->m_pPointGunAt; UpdateCompareFlag(pTarget && pTarget->IsObject() && pTarget == pTestedObject); return 0; } */ case COMMAND_TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME: { CTheScripts::ReadTextLabelFromScript(&m_nIp, tmp); for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) tmp[i] = tolower(tmp[i]); m_nIp += 8; CRunningScript* pScript = CTheScripts::pActiveScripts; while (pScript) { CRunningScript* pNext = pScript->next; if (strcmp(pScript->m_abScriptName, tmp) == 0) { pScript->RemoveScriptFromList(&CTheScripts::pActiveScripts); pScript->AddScriptToList(&CTheScripts::pIdleScripts); } pScript = pNext; } return 0; } case COMMAND_DISPLAY_TEXT_WITH_NUMBER: { CollectParameters(&m_nIp, 2); wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtX = *(float*)&ScriptParams[0]; CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtY = *(float*)&ScriptParams[1]; CollectParameters(&m_nIp, 1); CMessages::InsertNumberInString(text, ScriptParams[0], -1, -1, -1, -1, -1, CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame++].m_Text); return 0; } case COMMAND_DISPLAY_TEXT_WITH_2_NUMBERS: { CollectParameters(&m_nIp, 2); wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtX = *(float*)&ScriptParams[0]; CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_fAtY = *(float*)&ScriptParams[1]; CollectParameters(&m_nIp, 2); CMessages::InsertNumberInString(text, ScriptParams[0], ScriptParams[1], -1, -1, -1, -1, CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame++].m_Text); return 0; } case COMMAND_FAIL_CURRENT_MISSION: CTheScripts::FailCurrentMission = 2; return 0; case COMMAND_GET_CLOSEST_OBJECT_OF_TYPE: { return 0; /* CollectParameters(&m_nIp, 5); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float range = *(float*)&ScriptParams[3]; int mi = ScriptParams[4] < 0 ? CTheScripts::UsedObjectArray[-ScriptParams[4]].index : ScriptParams[4]; int16 total; CEntity* apEntities[16]; CWorld::FindObjectsOfTypeInRange(mi, pos, range, true, &total, 16, apEntities, false, false, false, true, true); CEntity* pClosestEntity = nil; float min_dist = 2.0f * range; for (int i = 0; i < total; i++) { float dist = (apEntities[i]->GetPosition() - pos).Magnitude(); if (dist < min_dist) { min_dist = dist; pClosestEntity = apEntities[i]; } } if (pClosestEntity && pClosestEntity->IsDummy()) { CPopulation::ConvertToRealObject((CDummyObject*)pClosestEntity); CWorld::FindObjectsOfTypeInRange(mi, pos, range, true, &total, 16, apEntities, false, false, false, true, true); pClosestEntity = nil; float min_dist = 2.0f * range; for (int i = 0; i < total; i++) { float dist = (apEntities[i]->GetPosition() - pos).Magnitude(); if (dist < min_dist) { min_dist = dist; pClosestEntity = apEntities[i]; } } if (pClosestEntity->IsDummy()) pClosestEntity = nil; } if (pClosestEntity) { script_assert(pClosestEntity->IsObject()); CObject* pObject = (CObject*)pClosestEntity; pObject->ObjectCreatedBy = MISSION_OBJECT; ScriptParams[0] = CPools::GetObjectPool()->GetIndex(pObject); } else { ScriptParams[0] = -1; } StoreParameters(&m_nIp, 1); return 0; */ } /* case COMMAND_PLACE_OBJECT_RELATIVE_TO_OBJECT: { CollectParameters(&m_nIp, 5); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); CObject* pTarget = CPools::GetObjectPool()->GetAt(ScriptParams[1]); script_assert(pTarget); CVector offset = *(CVector*)&ScriptParams[2]; CPhysical::PlacePhysicalRelativeToOtherPhysical(pTarget, pObject, offset); return 0; } */ case COMMAND_SET_ALL_OCCUPANTS_OF_CAR_LEAVE_CAR: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CCarAI::TellOccupantsToLeaveCar(pVehicle); return 0; } case COMMAND_SET_INTERPOLATION_PARAMETERS: CollectParameters(&m_nIp, 2); TheCamera.SetParametersForScriptInterpolation(*(float*)&ScriptParams[0], 100.0f - *(float*)&ScriptParams[0], ScriptParams[1]); return 0; /* case COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_TOWARDS_POINT: { CollectParameters(&m_nIp, 5); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float destX = *(float*)&ScriptParams[3]; float destY = *(float*)&ScriptParams[4]; int32 nid = ThePaths.FindNodeClosestToCoors(pos, 0, 999999.9f, true, true); CPathNode* pNode = &ThePaths.m_pathNodes[nid]; *(CVector*)&ScriptParams[0] = pNode->GetPosition(); *(float*)&ScriptParams[3] = ThePaths.FindNodeOrientationForCarPlacementFacingDestination(nid, destX, destY, true); StoreParameters(&m_nIp, 4); return 0; } case COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_AWAY_POINT: { CollectParameters(&m_nIp, 5); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); float destX = *(float*)&ScriptParams[3]; float destY = *(float*)&ScriptParams[4]; int32 nid = ThePaths.FindNodeClosestToCoors(pos, 0, 999999.9f, true, true); CPathNode* pNode = &ThePaths.m_pathNodes[nid]; *(CVector*)&ScriptParams[0] = pNode->GetPosition(); *(float*)&ScriptParams[3] = ThePaths.FindNodeOrientationForCarPlacementFacingDestination(nid, destX, destY, false); StoreParameters(&m_nIp, 4); return 0; } */ case COMMAND_GET_DEBUG_CAMERA_POINT_AT: *(CVector*)&ScriptParams[0] = TheCamera.Cams[2].Source + TheCamera.Cams[2].Front; StoreParameters(&m_nIp, 3); return 0; case COMMAND_ATTACH_CHAR_TO_CAR: { CollectParameters(&m_nIp, 8); CPed *pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CVehicle *pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); pPed->AttachPedToEntity(pVehicle, *(CVector*)&ScriptParams[2], ScriptParams[5], DEGTORAD(*(float*)&ScriptParams[6]), (eWeaponType)ScriptParams[7]); return 0; } case COMMAND_DETACH_CHAR_FROM_CAR: { CollectParameters(&m_nIp, 1); CPed *pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); if (pPed && pPed->m_attachedTo) pPed->DettachPedFromEntity(); return 0; } case COMMAND_SET_CAR_CHANGE_LANE: // for some reason changed in SA { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->AutoPilot.m_bStayInFastLane = !ScriptParams[1]; return 0; } case COMMAND_CLEAR_CHAR_LAST_WEAPON_DAMAGE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); if (pPed) pPed->m_lastWepDam = -1; else debug("CLEAR_CHAR_LAST_WEAPON_DAMAGE - Character doesn't exist\n"); return 0; } case COMMAND_CLEAR_CAR_LAST_WEAPON_DAMAGE: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); if (pVehicle) pVehicle->m_nLastWeaponDamage = -1; else debug("CLEAR_CAR_LAST_WEAPON_DAMAGE - Vehicle doesn't exist\n"); return 0; } case COMMAND_GET_RANDOM_COP_IN_AREA: { CollectParameters(&m_nIp, 9); int ped_handle = -1; CVector pos = FindPlayerCoors(); float x1 = *(float*)&ScriptParams[0]; float y1 = *(float*)&ScriptParams[1]; float x2 = *(float*)&ScriptParams[2]; float y2 = *(float*)&ScriptParams[3]; int i = CPools::GetPedPool()->GetSize(); while (--i && ped_handle == -1) { CPed* pPed = CPools::GetPedPool()->GetSlot(i); if (!pPed) continue; if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) continue; if (pPed->m_nPedType != PEDTYPE_COP) continue; if (!ThisIsAValidRandomCop(pPed->GetModelIndex(), ScriptParams[4], ScriptParams[5], ScriptParams[6], ScriptParams[7], ScriptParams[8])) continue; if (pPed->CharCreatedBy != RANDOM_CHAR) continue; if (!pPed->IsPedInControl() && pPed->GetPedState() != PED_DRIVING && pPed->GetPedState() != PED_ABSEIL) continue; if (pPed->bRemoveFromWorld) continue; if (pPed->bFadeOut) continue; if (pPed->bIsLeader || pPed->m_leader) continue; if (!pPed->IsWithinArea(x1, y1, x2, y2)) continue; if (pos.z - COP_PED_FIND_Z_OFFSET > pPed->GetPosition().z) continue; if (pos.z + COP_PED_FIND_Z_OFFSET < pPed->GetPosition().z) continue; ped_handle = CPools::GetPedPool()->GetIndex(pPed); CTheScripts::LastRandomPedId = ped_handle; pPed->CharCreatedBy = MISSION_CHAR; pPed->bRespondsToThreats = false; ++CPopulation::ms_nTotalMissionPeds; if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); } ScriptParams[0] = ped_handle; StoreParameters(&m_nIp, 1); return 0; } /* case COMMAND_GET_RANDOM_COP_IN_ZONE: { char zone[KEY_LENGTH_IN_SCRIPT]; strncpy(zone, (const char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); int nZone = CTheZones::FindZoneByLabelAndReturnIndex(zone, ZONE_DEFAULT); if (nZone != -1) m_nIp += KEY_LENGTH_IN_SCRIPT; CZone* pZone = CTheZones::GetNavigationZone(nZone); int ped_handle = -1; CVector pos = FindPlayerCoors(); int i = CPools::GetPedPool()->GetSize(); while (--i && ped_handle == -1) { CPed* pPed = CPools::GetPedPool()->GetSlot(i); if (!pPed) continue; if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) continue; if (pPed->m_nPedType != PEDTYPE_COP) continue; if (pPed->CharCreatedBy != RANDOM_CHAR) continue; if (!pPed->IsPedInControl() && pPed->GetPedState() != PED_DRIVING) continue; if (pPed->bRemoveFromWorld) continue; if (pPed->bFadeOut) continue; if (pPed->bIsLeader || pPed->m_leader) continue; if (!CTheZones::PointLiesWithinZone(&pPed->GetPosition(), pZone)) continue; if (pos.z - COP_PED_FIND_Z_OFFSET > pPed->GetPosition().z) continue; if (pos.z + COP_PED_FIND_Z_OFFSET < pPed->GetPosition().z) continue; ped_handle = CPools::GetPedPool()->GetIndex(pPed); CTheScripts::LastRandomPedId = ped_handle; pPed->CharCreatedBy = MISSION_CHAR; pPed->bRespondsToThreats = false; ++CPopulation::ms_nTotalMissionPeds; if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); } ScriptParams[0] = ped_handle; StoreParameters(&m_nIp, 1); return 0; } */ case COMMAND_SET_CHAR_OBJ_FLEE_CAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pVehicle); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_FLEE_CAR, pVehicle); return 0; } case COMMAND_GET_DRIVER_OF_CAR: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CPed* pDriver = pVehicle->pDriver; if (pDriver) ScriptParams[0] = CPools::GetPedPool()->GetIndex(pDriver); else ScriptParams[0] = -1; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_NUMBER_OF_FOLLOWERS: { CollectParameters(&m_nIp, 1); CPed* pLeader = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pLeader); int total = 0; int i = CPools::GetPedPool()->GetSize(); while (--i) { CPed* pPed = CPools::GetPedPool()->GetSlot(i); if (!pPed) continue; if (pPed->m_leader == pLeader) total++; } ScriptParams[0] = total; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GIVE_REMOTE_CONTROLLED_MODEL_TO_PLAYER: { CollectParameters(&m_nIp, 6); CVector pos = *(CVector*)&ScriptParams[1]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRemote::GivePlayerRemoteControlledCar(pos.x, pos.y, pos.z, DEGTORAD(*(float*)&ScriptParams[4]), ScriptParams[5]); return 0; } case COMMAND_GET_CURRENT_PLAYER_WEAPON: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); ScriptParams[0] = pPed->GetWeapon()->m_eWeaponType; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_CURRENT_CHAR_WEAPON: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); ScriptParams[0] = pPed->GetWeapon()->m_eWeaponType; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_2D: case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_2D: case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_2D: case COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D: case COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D: case COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D: LocateCharObjectCommand(command, &m_nIp); return 0; case COMMAND_SET_CAR_TEMP_ACTION: { CollectParameters(&m_nIp, 3); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->AutoPilot.m_nTempAction = (uint8)ScriptParams[1]; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + ScriptParams[2]; return 0; } /* case COMMAND_SET_CAR_HANDBRAKE_TURN_RIGHT: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->AutoPilot.m_nTempAction = TEMPACT_HANDBRAKETURNRIGHT; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + ScriptParams[1]; return 0; } case COMMAND_SET_CAR_HANDBRAKE_STOP: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->AutoPilot.m_nTempAction = TEMPACT_HANDBRAKESTRAIGHT; pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + ScriptParams[1]; return 0; } */ case COMMAND_IS_CHAR_ON_ANY_BIKE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle&& pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE); return 0; } /* case COMMAND_LOCATE_SNIPER_BULLET_2D: case COMMAND_LOCATE_SNIPER_BULLET_3D: LocateSniperBulletCommand(command, &m_nIp); return 0; */ case COMMAND_GET_NUMBER_OF_SEATS_IN_MODEL: CollectParameters(&m_nIp, 1); ScriptParams[0] = CVehicleModelInfo::GetMaximumNumberOfPassengersFromNumberOfDoors(ScriptParams[0]) + 1; StoreParameters(&m_nIp, 1); return 0; case COMMAND_IS_PLAYER_ON_ANY_BIKE: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE); return 0; } /* case COMMAND_IS_CHAR_LYING_DOWN: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bFallenDown); return 0; } */ case COMMAND_CAN_CHAR_SEE_DEAD_CHAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); int pedtype = ScriptParams[1]; bool can = false; for (int i = 0; i < pPed->m_numNearPeds; i++) { CPed* pTestPed = pPed->m_nearPeds[i]; if (pTestPed->m_fHealth <= 0.0f && pTestPed->m_nPedType == pedtype && pPed->OurPedCanSeeThisOne(pTestPed)) can = true; } UpdateCompareFlag(can); return 0; } case COMMAND_SET_ENTER_CAR_RANGE_MULTIPLIER: CollectParameters(&m_nIp, 1); CPed::nEnterCarRangeMultiplier = *(float*)&ScriptParams[0]; return 0; case COMMAND_SET_THREAT_REACTION_RANGE_MULTIPLIER: CollectParameters(&m_nIp, 1); CPed::nThreatReactionRangeMultiplier = *(float*)&ScriptParams[0]; return 0; case COMMAND_SET_CHAR_CEASE_ATTACK_TIMER: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->m_ceaseAttackTimer = ScriptParams[1]; return 0; } case COMMAND_GET_REMOTE_CONTROLLED_CAR: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CWorld::Players[ScriptParams[0]].m_pRemoteVehicle; if (pVehicle) ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); else ScriptParams[0] = -1; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_IS_PC_VERSION: UpdateCompareFlag(true); return 0; //case COMMAND_REPLAY: //case COMMAND_IS_REPLAY_PLAYING: case COMMAND_IS_MODEL_AVAILABLE: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CModelInfo::GetModelInfo(ScriptParams[0]) != nil); return 0; case COMMAND_SHUT_CHAR_UP: CollectParameters(&m_nIp, 2); DMAudio.SetPedTalkingStatus(CPools::GetPedPool()->GetAt(ScriptParams[0]), ScriptParams[1] == 0); return 0; case COMMAND_SET_ENABLE_RC_DETONATE: CollectParameters(&m_nIp, 1); CVehicle::bDisableRemoteDetonation = !ScriptParams[0]; return 0; case COMMAND_SET_CAR_RANDOM_ROUTE_SEED: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->m_nRouteSeed = ScriptParams[1]; return 0; } case COMMAND_IS_ANY_PICKUP_AT_COORDS: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; CRunningScript::UpdateCompareFlag(CPickups::TestForPickupsInBubble(pos, 0.5f)); return 0; } case COMMAND_GET_FIRST_PICKUP_COORDS: case COMMAND_GET_NEXT_PICKUP_COORDS: case COMMAND_REMOVE_ALL_CHAR_WEAPONS: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->ClearWeapons(); return 0; } case COMMAND_HAS_PLAYER_GOT_WEAPON: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); bool bFound = false; for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { if (pPed->GetWeapon(i).m_eWeaponType == ScriptParams[1]) { bFound = true; break; } } UpdateCompareFlag(bFound); return 0; } //case COMMAND_HAS_CHAR_GOT_WEAPON: //case COMMAND_IS_PLAYER_FACING_CHAR: case COMMAND_SET_TANK_DETONATE_CARS: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle && pVehicle->m_vehType == VEHICLE_TYPE_CAR); ((CAutomobile*)pVehicle)->bTankDetonateCars = ScriptParams[1]; return 0; } case COMMAND_GET_POSITION_OF_ANALOGUE_STICKS: { CollectParameters(&m_nIp, 1); CPad* pPad = CPad::GetPad(ScriptParams[0]); ScriptParams[0] = pPad->NewState.LeftStickX; ScriptParams[1] = pPad->NewState.LeftStickY; ScriptParams[2] = pPad->NewState.RightStickX; ScriptParams[3] = pPad->NewState.RightStickY; StoreParameters(&m_nIp, 4); return 0; } case COMMAND_IS_CAR_ON_FIRE: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); bool bOnFire = false; if (pVehicle->m_pCarFire) bOnFire = true; if (pVehicle->m_vehType == VEHICLE_TYPE_CAR && ((CAutomobile*)pVehicle)->Damage.GetEngineStatus() >= ENGINE_STATUS_ON_FIRE) bOnFire = true; if (pVehicle->m_fHealth < 250.0f) bOnFire = true; UpdateCompareFlag(bOnFire); return 0; } case COMMAND_IS_CAR_TYRE_BURST: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); bool bIsBurst = false; CBike* pBike = (CBike*)pVehicle; if (pVehicle->IsBike()) { if (ScriptParams[1] == 4) { for (int i = 0; i < 2; i++) { if (pBike->m_wheelStatus[i] == WHEEL_STATUS_BURST) bIsBurst = true; } } else { if (ScriptParams[1] == 2) ScriptParams[1] = 0; if (ScriptParams[1] == 3) ScriptParams[1] = 1; bIsBurst = pBike->m_wheelStatus[ScriptParams[1]] == WHEEL_STATUS_BURST; } } else { CAutomobile* pCar = (CAutomobile*)pVehicle; if (ScriptParams[1] == 4) { for (int i = 0; i < 4; i++) { if (pCar->Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST) bIsBurst = true; } } else bIsBurst = pCar->Damage.GetWheelStatus(ScriptParams[1] == WHEEL_STATUS_BURST); } UpdateCompareFlag(bIsBurst); return 0; } //case COMMAND_SET_CAR_DRIVE_STRAIGHT_AHEAD: //case COMMAND_SET_CAR_WAIT: //case COMMAND_IS_PLAYER_STANDING_ON_A_VEHICLE: //case COMMAND_IS_PLAYER_FOOT_DOWN: //case COMMAND_IS_CHAR_FOOT_DOWN: case COMMAND_INITIALISE_OBJECT_PATH: { CollectParameters(&m_nIp, 2); int32 counter = 0; while (counter < 3 && CScriptPaths::aArray[counter].m_state != SCRIPT_PATH_DISABLED) { counter++; } CScriptPaths::aArray[counter].InitialiseOne(ScriptParams[0], *(float*)&ScriptParams[1]); ScriptParams[0] = counter; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_START_OBJECT_ON_PATH: { CollectParameters(&m_nIp, 2); CObject *pObj = CPools::GetObjectPool()->GetAt(ScriptParams[0]); assert(pObj); CScriptPaths::aArray[ScriptParams[1]].SetObjectToControl(pObj); return 0; } case COMMAND_SET_OBJECT_PATH_SPEED: { CollectParameters(&m_nIp, 2); CScriptPaths::aArray[ScriptParams[0]].m_fSpeed = *(float*)&ScriptParams[1]; return 0; } case COMMAND_SET_OBJECT_PATH_POSITION: { CollectParameters(&m_nIp, 2); CScriptPaths::aArray[ScriptParams[0]].m_fPosition = *(float*)&ScriptParams[1]; return 0; } //case COMMAND_GET_OBJECT_DISTANCE_ALONG_PATH: case COMMAND_CLEAR_OBJECT_PATH: { CollectParameters(&m_nIp, 1); CScriptPaths::aArray[ScriptParams[0]].Clear(); return 0; } case COMMAND_HELI_GOTO_COORDS: { CollectParameters(&m_nIp, 5); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle && pVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI); ((CAutomobile*)pVehicle)->TellHeliToGoToCoors(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3], ScriptParams[4]); return 0; } case COMMAND_IS_INT_VAR_EQUAL_TO_CONSTANT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr == ScriptParams[0]); return 0; } case COMMAND_IS_INT_LVAR_EQUAL_TO_CONSTANT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr == ScriptParams[0]); return 0; } case COMMAND_GET_DEAD_CHAR_PICKUP_COORDS: { CollectParameters(&m_nIp, 1); CPed *pTarget = CPools::GetPedPool()->GetAt(ScriptParams[0]); CVector pos; pTarget->CreateDeadPedPickupCoors(&pos.x, &pos.y, &pos.z); *(CVector*)&ScriptParams[0] = pos; StoreParameters(&m_nIp, 3); return 0; } case COMMAND_CREATE_PROTECTION_PICKUP: { CollectParameters(&m_nIp, 5); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_PICKUP_REVENUE, PICKUP_ASSET_REVENUE, ScriptParams[3], ScriptParams[4]); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_IS_CHAR_IN_ANY_BOAT: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT); return 0; } case COMMAND_IS_PLAYER_IN_ANY_BOAT: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT); return 0; } case COMMAND_IS_CHAR_IN_ANY_HELI: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI); return 0; } case COMMAND_IS_PLAYER_IN_ANY_HELI: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI); return 0; } case COMMAND_IS_CHAR_IN_ANY_PLANE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE); return 0; } case COMMAND_IS_PLAYER_IN_ANY_PLANE: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI); return 0; } case COMMAND_IS_CHAR_IN_WATER: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); UpdateCompareFlag(pPed && pPed->bIsInWater); return 0; } case COMMAND_SET_VAR_INT_TO_CONSTANT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); *ptr = ScriptParams[0]; return 0; } case COMMAND_SET_LVAR_INT_TO_CONSTANT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); *ptr = ScriptParams[0]; return 0; } default: script_assert(0); } return -1; } ================================================ FILE: src/control/Script7.cpp ================================================ #include "common.h" #include "Script.h" #include "ScriptCommands.h" #include "CarCtrl.h" #include "ColStore.h" #include "Coronas.h" #include "CutsceneMgr.h" #include "DMAudio.h" #include "Explosion.h" #include "GameLogic.h" #include "General.h" #include "Glass.h" #include "Fluff.h" #include "Hud.h" #include "MBlur.h" #include "Pad.h" #include "Pickups.h" #include "Pools.h" #include "Population.h" #include "Radar.h" #include "RoadBlocks.h" #include "Ropes.h" #include "SetPieces.h" #include "SpecialFX.h" #include "Stats.h" #include "Streaming.h" #include "Timecycle.h" #include "User.h" #include "World.h" #include "Zones.h" int8 CRunningScript::ProcessCommands1200To1299(int32 command) { switch (command) { case COMMAND_IS_INT_VAR_GREATER_THAN_CONSTANT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr > ScriptParams[0]); return 0; } case COMMAND_IS_INT_LVAR_GREATER_THAN_CONSTANT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr > ScriptParams[0]); return 0; } case COMMAND_IS_CONSTANT_GREATER_THAN_INT_VAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(ScriptParams[0] > *ptr); return 0; } case COMMAND_IS_CONSTANT_GREATER_THAN_INT_LVAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(ScriptParams[0] > *ptr); return 0; } case COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_CONSTANT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr >= ScriptParams[0]); return 0; } case COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_CONSTANT: { int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); CollectParameters(&m_nIp, 1); UpdateCompareFlag(*ptr >= ScriptParams[0]); return 0; } case COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_VAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_GLOBAL); UpdateCompareFlag(ScriptParams[0] >= *ptr); return 0; } case COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_LVAR: { CollectParameters(&m_nIp, 1); int32* ptr = GetPointerToScriptVariable(&m_nIp, VAR_LOCAL); UpdateCompareFlag(ScriptParams[0] >= *ptr); return 0; } case COMMAND_GET_CHAR_WEAPON_IN_SLOT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); ScriptParams[0] = pPed->GetWeapon(ScriptParams[1] - 1).m_eWeaponType; ScriptParams[1] = pPed->GetWeapon(ScriptParams[1] - 1).m_nAmmoTotal; ScriptParams[2] = CPickups::ModelForWeapon((eWeaponType)ScriptParams[0]); StoreParameters(&m_nIp, 3); return 0; } case COMMAND_GET_CLOSEST_STRAIGHT_ROAD: { CollectParameters(&m_nIp, 5); int node1, node2; float angle; ThePaths.FindNodePairClosestToCoors(*(CVector*)&ScriptParams[0], PATH_CAR, &node1, &node2, &angle, *(float*)&ScriptParams[3], *(float*)&ScriptParams[4], true, true); if (node1 == -1) { for (int i = 0; i < 7; i++) ScriptParams[i] = 0; } else { *(CVector*)&ScriptParams[0] = ThePaths.FindNodeCoorsForScript(node1); *(CVector*)&ScriptParams[3] = ThePaths.FindNodeCoorsForScript(node2); *(float*)&ScriptParams[6] = angle; } StoreParameters(&m_nIp, 7); return 0; } case COMMAND_SET_CAR_FORWARD_SPEED: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); float speed = *(float*)&ScriptParams[1] / GAME_SPEED_TO_CARAI_SPEED; pVehicle->SetMoveSpeed(pVehicle->GetForward() * speed); if (pVehicle->IsRealHeli() && pVehicle->IsCar()) ((CAutomobile*)pVehicle)->m_aWheelSpeed[1] = 0.22f; return 0; } case COMMAND_SET_AREA_VISIBLE: CollectParameters(&m_nIp, 1); CGame::currArea = ScriptParams[0]; CStreaming::RemoveBuildingsNotInArea(ScriptParams[0]); return 0; case COMMAND_SET_CUTSCENE_ANIM_TO_LOOP: { char key[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; CCutsceneMgr::SetCutsceneAnimToLoop(key); return 0; } case COMMAND_MARK_CAR_AS_CONVOY_CAR: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bPartOfConvoy = ScriptParams[1]; return 0; } case COMMAND_RESET_HAVOC_CAUSED_BY_PLAYER: { CollectParameters(&m_nIp, 1); CWorld::Players[ScriptParams[0]].m_nHavocLevel = 0; return 0; } case COMMAND_GET_HAVOC_CAUSED_BY_PLAYER: { CollectParameters(&m_nIp, 1); ScriptParams[0] = CWorld::Players[ScriptParams[0]].m_nHavocLevel; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_CREATE_SCRIPT_ROADBLOCK: { CollectParameters(&m_nIp, 6); CRoadBlocks::RegisterScriptRoadBlock(*(CVector*)&ScriptParams[0], *(CVector*)&ScriptParams[3]); return 0; } case COMMAND_CLEAR_ALL_SCRIPT_ROADBLOCKS: { CRoadBlocks::ClearScriptRoadBlocks(); return 0; } case COMMAND_SET_CHAR_OBJ_WALK_TO_CHAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pTargetPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING, pTargetPed); return 0; } //case COMMAND_IS_PICKUP_IN_ZONE: case COMMAND_GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS: { CollectParameters(&m_nIp, 4); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVector result = Multiply3x3(pPed->GetMatrix(), *(CVector*)&ScriptParams[1]) + pPed->GetPosition(); *(CVector*)&ScriptParams[0] = result; StoreParameters(&m_nIp, 3); return 0; } case COMMAND_HAS_CHAR_BEEN_PHOTOGRAPHED: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); bool result = false; if (pPed->bHasBeenPhotographed) { result = true; pPed->bHasBeenPhotographed = false; } UpdateCompareFlag(result); return 0; } case COMMAND_SET_CHAR_OBJ_AIM_GUN_AT_CHAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CPed* pTargetPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); script_assert(pTargetPed); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_AIM_GUN_AT, pTargetPed); return 0; } case COMMAND_SWITCH_SECURITY_CAMERA: { CollectParameters(&m_nIp, 1); CSpecialFX::bVideoCam = ScriptParams[0] != 0; return 0; } //case COMMAND_IS_CHAR_IN_FLYING_VEHICLE: case COMMAND_IS_PLAYER_IN_FLYING_VEHICLE: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && (pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || pPed->m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE)); return 0; } //case COMMAND_HAS_SONY_CD_BEEN_READ: //case COMMAND_GET_NUMBER_OF_SONY_CDS_READ: //case COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD_OLD: //case COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD: case COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_COORD: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int id = CRadar::SetShortRangeCoordBlip(BLIP_COORD, pos, 5, BLIP_DISPLAY_BOTH); CRadar::SetBlipSprite(id, ScriptParams[3]); ScriptParams[0] = id; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_ADD_MONEY_SPENT_ON_CLOTHES: CollectParameters(&m_nIp, 1); CStats::MoneySpentOnFashion(ScriptParams[0]); return 0; case COMMAND_SET_HELI_ORIENTATION: { CollectParameters(&m_nIp, 2); CAutomobile* pHeli = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pHeli && pHeli->IsCar() && pHeli->IsRealHeli()); float fAngle = DEGTORAD(*(float*)&ScriptParams[1] - 90.0f); while (fAngle < 0.0f) fAngle += TWOPI; while (fAngle > TWOPI) fAngle -= TWOPI; pHeli->SetHeliOrientation(fAngle); return 0; } case COMMAND_CLEAR_HELI_ORIENTATION: { CollectParameters(&m_nIp, 1); CAutomobile* pHeli = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pHeli && pHeli->IsCar() && pHeli->IsRealHeli()); pHeli->ClearHeliOrientation(); return 0; } case COMMAND_PLANE_GOTO_COORDS: { CollectParameters(&m_nIp, 5); CAutomobile* pPlane = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pPlane && pPlane->IsCar() && pPlane->IsRealPlane()); pPlane->TellPlaneToGoToCoors(*(float*)&ScriptParams[1], *(float*)&ScriptParams[2], *(float*)&ScriptParams[3], ScriptParams[4]); return 0; } case COMMAND_GET_NTH_CLOSEST_CAR_NODE: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); *(CVector*)&ScriptParams[0] = ThePaths.FindNodeCoorsForScript(ThePaths.FindNthNodeClosestToCoors(pos, 0, 999999.9f, true, true, ScriptParams[3] - 1)); StoreParameters(&m_nIp, 3); return 0; } //case COMMAND_GET_NTH_CLOSEST_CHAR_NODE: case COMMAND_DRAW_WEAPONSHOP_CORONA: { CollectParameters(&m_nIp, 9); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CCoronas::RegisterCorona((uintptr)this + m_nIp, ScriptParams[6], ScriptParams[7], ScriptParams[8], 255, pos, *(float*)&ScriptParams[3], 150.0f, ScriptParams[4], ScriptParams[5], 1, 0, 0, 0.0f, false, 0.2f); return 0; } case COMMAND_SET_ENABLE_RC_DETONATE_ON_CONTACT: { CollectParameters(&m_nIp, 1); CVehicle::bDisableRemoteDetonationOnContact = (ScriptParams[0] == 0); return 0; } case COMMAND_FREEZE_CHAR_POSITION: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bIsFrozen = ScriptParams[1]; return 0; } case COMMAND_SET_CHAR_DROWNS_IN_WATER: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bDrownsInWater = ScriptParams[1]; return 0; } case COMMAND_SET_OBJECT_RECORDS_COLLISIONS: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); pObject->bUseCollisionRecords = ScriptParams[1]; return 0; } case COMMAND_HAS_OBJECT_COLLIDED_WITH_ANYTHING: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); UpdateCompareFlag(pObject->m_nCollisionRecords != 0); return 0; } case COMMAND_REMOVE_RC_BUGGY: { CWorld::Players[CWorld::PlayerInFocus].BlowUpRCBuggy(false); return 0; } //case COMMAND_HAS_PHOTOGRAPH_BEEN_TAKEN: case COMMAND_GET_CHAR_ARMOUR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); ScriptParams[0] = pPed->m_fArmour; StoreParameters(&m_nIp, 1); return 0; } //case COMMAND_SET_CHAR_ARMOUR: case COMMAND_SET_HELI_STABILISER: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bHeliMinimumTilt = ScriptParams[1]; return 0; } case COMMAND_SET_CAR_STRAIGHT_LINE_DISTANCE: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->AutoPilot.m_nSwitchDistance = ScriptParams[1]; return 0; } case COMMAND_POP_CAR_BOOT: { CollectParameters(&m_nIp, 1); CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pCar&& pCar->IsCar()); pCar->PopBoot(); return 0; } case COMMAND_SHUT_PLAYER_UP: { CollectParameters(&m_nIp, 2); DMAudio.ShutUpPlayerTalking(!!ScriptParams[1]); return 0; } case COMMAND_SET_PLAYER_MOOD: { CollectParameters(&m_nIp, 3); DMAudio.SetPlayersMood(ScriptParams[1], ScriptParams[2]); return 0; } case COMMAND_REQUEST_COLLISION: { CollectParameters(&m_nIp, 2); CVector2D pos; pos.x = *(float*)&ScriptParams[0]; pos.y = *(float*)&ScriptParams[1]; CColStore::RequestCollision(pos); return 0; } case COMMAND_LOCATE_OBJECT_2D: case COMMAND_LOCATE_OBJECT_3D: LocateObjectCommand(command, &m_nIp); return 0; case COMMAND_IS_OBJECT_IN_WATER: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); UpdateCompareFlag(pObject->bIsInWater); return 0; } //case COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR_EVEN_MISSION_CAR: case COMMAND_IS_OBJECT_IN_AREA_2D: case COMMAND_IS_OBJECT_IN_AREA_3D: ObjectInAreaCheckCommand(command, &m_nIp); return 0; case COMMAND_SET_CHAR_CROUCH: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (ScriptParams[1]) { pPed->bCrouchWhenShooting = true; pPed->SetDuck(ScriptParams[2], true); } else { pPed->ClearDuck(true); pPed->bCrouchWhenShooting = false; } return 0; } case COMMAND_SET_ZONE_CIVILIAN_CAR_INFO: { char label[12]; int16 carDensities[CCarCtrl::NUM_CAR_CLASSES] = { 0 }; int16 boatDensities[CCarCtrl::NUM_BOAT_CLASSES] = { 0 }; int i; CTheScripts::ReadTextLabelFromScript(&m_nIp, label); m_nIp += KEY_LENGTH_IN_SCRIPT; CollectParameters(&m_nIp, 12); for (i = 0; i < CCarCtrl::NUM_CAR_CLASSES; i++) carDensities[i] = ScriptParams[i + 1]; for (i = 0; i < CCarCtrl::NUM_BOAT_CLASSES; i++) boatDensities[i] = ScriptParams[i + 1 + CCarCtrl::NUM_CAR_CLASSES]; int zone = CTheZones::FindZoneByLabelAndReturnIndex(label, ZONE_INFO); if (zone < 0) { debug("Couldn't find zone - %s\n", label); return 0; } while (zone >= 0) { CTheZones::SetZoneCivilianCarInfo(zone, ScriptParams[0], carDensities, boatDensities); zone = CTheZones::FindNextZoneByLabelAndReturnIndex(label, ZONE_INFO); } return 0; } case COMMAND_REQUEST_ANIMATION: { char key[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; CStreaming::RequestAnim(CAnimManager::GetAnimationBlockIndex(key), STREAMFLAGS_SCRIPTOWNED); return 0; } case COMMAND_HAS_ANIMATION_LOADED: { char key[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; UpdateCompareFlag(CAnimManager::GetAnimationBlock(key)->isLoaded); return 0; } case COMMAND_REMOVE_ANIMATION: { char key[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; CStreaming::RemoveAnim(CAnimManager::GetAnimationBlockIndex(key)); return 0; } case COMMAND_IS_CHAR_WAITING_FOR_WORLD_COLLISION: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bIsStaticWaitingForCollision); return 0; } case COMMAND_IS_CAR_WAITING_FOR_WORLD_COLLISION: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); UpdateCompareFlag(pVehicle->bIsStaticWaitingForCollision); return 0; } case COMMAND_IS_OBJECT_WAITING_FOR_WORLD_COLLISION: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); UpdateCompareFlag(pObject->bIsStaticWaitingForCollision); return 0; } case COMMAND_SET_CHAR_SHUFFLE_INTO_DRIVERS_SEAT: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); pPed->PedShuffle(); return 0; } case COMMAND_ATTACH_CHAR_TO_OBJECT: { CollectParameters(&m_nIp, 8); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[1]); pPed->AttachPedToEntity(pObject, *(CVector*)&ScriptParams[2], ScriptParams[5], DEGTORAD(ScriptParams[6]), (eWeaponType)ScriptParams[7]); return 0; } case COMMAND_SET_CHAR_AS_PLAYER_FRIEND: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bIsPlayerFriend = ScriptParams[2]; return 0; } //case COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER: case COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER_WITH_STRING: { char onscreen_str[12]; script_assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); uint16 var = CTheScripts::Read2BytesFromScript(&m_nIp); CollectParameters(&m_nIp, 2); wchar* text = TheText.Get((char*)&CTheScripts::ScriptSpace[m_nIp]); // ??? strncpy(onscreen_str, (char*)&CTheScripts::ScriptSpace[m_nIp], KEY_LENGTH_IN_SCRIPT); m_nIp += KEY_LENGTH_IN_SCRIPT; CUserDisplay::OnscnTimer.AddCounter(var, ScriptParams[0], onscreen_str, ScriptParams[1] - 1); return 0; } case COMMAND_ADD_SET_PIECE: { CollectParameters(&m_nIp, 13); CSetPieces::AddOne(ScriptParams[0], *(CVector2D*)&ScriptParams[1], *(CVector2D*)&ScriptParams[3], *(CVector2D*)&ScriptParams[5], *(CVector2D*)&ScriptParams[7], *(CVector2D*)&ScriptParams[9], *(CVector2D*)&ScriptParams[11]); return 0; } case COMMAND_SET_EXTRA_COLOURS: { CollectParameters(&m_nIp, 2); CTimeCycle::StartExtraColour(ScriptParams[0]-1, ScriptParams[1] != 0); return 0; } case COMMAND_CLEAR_EXTRA_COLOURS: { CollectParameters(&m_nIp, 1); CTimeCycle::StopExtraColour(ScriptParams[0]); return 0; } //case COMMAND_CLOSE_CAR_BOOT: case COMMAND_GET_WHEELIE_STATS: { CollectParameters(&m_nIp, 1); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; ScriptParams[0] = pPlayerInfo->m_nLastTimeCarSpentOnTwoWheels; *(float*)&ScriptParams[1] = pPlayerInfo->m_nLastDistanceCarTravelledOnTwoWheels; ScriptParams[2] = pPlayerInfo->m_nLastTimeSpentOnWheelie; *(float*)&ScriptParams[3] = pPlayerInfo->m_nLastDistanceTravelledOnWheelie; ScriptParams[4] = pPlayerInfo->m_nLastTimeSpentOnStoppie; *(float*)&ScriptParams[5] = pPlayerInfo->m_nLastDistanceTravelledOnStoppie; StoreParameters(&m_nIp, 6); pPlayerInfo->m_nLastTimeCarSpentOnTwoWheels = 0; pPlayerInfo->m_nLastDistanceCarTravelledOnTwoWheels = 0.0f; pPlayerInfo->m_nLastTimeSpentOnWheelie = 0; pPlayerInfo->m_nLastDistanceTravelledOnWheelie = 0.0f; pPlayerInfo->m_nLastTimeSpentOnStoppie = 0; pPlayerInfo->m_nLastDistanceTravelledOnStoppie = 0.0f; return 0; } //case COMMAND_DISARM_CHAR: case COMMAND_BURST_CAR_TYRE: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); if (pVehicle->IsBike()) { if (ScriptParams[1] == 2) ScriptParams[1] = 0; else if (ScriptParams[1] == 3) ScriptParams[1] = 1; pVehicle->BurstTyre(ScriptParams[1], true); } else { pVehicle->BurstTyre(ScriptParams[1], true); } return 0; } case COMMAND_IS_CHAR_OBJ_NO_OBJ: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->m_prevObjective == OBJECTIVE_NONE && pPed->m_objective == OBJECTIVE_NONE); return 0; } case COMMAND_IS_PLAYER_WEARING: { CollectParameters(&m_nIp, 1); char key[12]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; for (int i = 0; i < KEY_LENGTH_IN_SCRIPT; i++) key[i] = tolower(key[i]); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(strcmp(key, CModelInfo::GetModelInfo(pPed->GetModelIndex())->GetModelName()) == 0); return 0; } case COMMAND_SET_PLAYER_CAN_DO_DRIVE_BY: { CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; pPlayerInfo->m_bDriveByAllowed = ScriptParams[1]; return 0; } case COMMAND_SET_CHAR_OBJ_SPRINT_TO_COORD: { CollectParameters(&m_nIp, 3); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVector pos; pos.x = *(float*)&ScriptParams[1]; pos.y = *(float*)&ScriptParams[2]; pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pPed->bScriptObjectiveCompleted = false; pPed->SetObjective(OBJECTIVE_SPRINT_TO_AREA, pos); return 0; } case COMMAND_CREATE_SWAT_ROPE: { CollectParameters(&m_nIp, 3); CRopes::CreateRopeWithSwatComingDown(*(CVector*)&ScriptParams[0]); return 0; } //case COMMAND_SET_FIRST_PERSON_CONTROL_CAMERA: //case COMMAND_GET_NEAREST_TYRE_TO_POINT: case COMMAND_SET_CAR_MODEL_COMPONENTS: { CollectParameters(&m_nIp, 3); CVehicleModelInfo::SetComponentsToUse(ScriptParams[1], ScriptParams[2]); return 0; } case COMMAND_SWITCH_LIFT_CAMERA: { CollectParameters(&m_nIp, 1); CSpecialFX::bLiftCam = ScriptParams[0] != 0; return 0; } case COMMAND_CLOSE_ALL_CAR_DOORS: { CollectParameters(&m_nIp, 1); CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pCar&& pCar->IsCar()); pCar->CloseAllDoors(); return 0; } case COMMAND_GET_DISTANCE_BETWEEN_COORDS_2D: { CollectParameters(&m_nIp, 4); *(float*)&ScriptParams[0] = (*(CVector2D*)&ScriptParams[0] - *(CVector2D*)&ScriptParams[2]).Magnitude(); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_DISTANCE_BETWEEN_COORDS_3D: { CollectParameters(&m_nIp, 6); *(float*)&ScriptParams[0] = (*(CVector*)&ScriptParams[0] - *(CVector*)&ScriptParams[3]).Magnitude(); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_POP_CAR_BOOT_USING_PHYSICS: { CollectParameters(&m_nIp, 1); CAutomobile* pCar = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pCar && pCar->IsCar()); pCar->PopBootUsingPhysics(); return 0; } //case COMMAND_SET_FIRST_PERSON_WEAPON_CAMERA: case COMMAND_IS_CHAR_LEAVING_VEHICLE_TO_DIE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE); return 0; } case COMMAND_SORT_OUT_OBJECT_COLLISION_WITH_CAR: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); pObject->m_pCollidingEntity = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); return 0; } //case COMMAND_GET_MAX_WANTED_LEVEL: case COMMAND_IS_CHAR_WANDER_PATH_CLEAR: { CollectParameters(&m_nIp, 5); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(CWorld::IsWanderPathClear(pPed->GetPosition(), *(CVector*)&ScriptParams[0], *(float*)&ScriptParams[3], 4)); return 0; } //case COMMAND_PRINT_HELP_WITH_NUMBER: case COMMAND_PRINT_HELP_FOREVER: { wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); CHud::SetHelpMessage(text, false, true); return 0; } //case COMMAND_PRINT_HELP_FOREVER_WITH_NUMBER: default: script_assert(0); } return -1; } int8 CRunningScript::ProcessCommands1300To1399(int32 command) { switch (command) { case COMMAND_SET_CHAR_CAN_BE_DAMAGED_BY_MEMBERS_OF_GANG: { CollectParameters(&m_nIp, 3); CPed* pTarget = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pTarget); uint8 flag = 1 << (uint8)ScriptParams[1]; if (ScriptParams[2]) pTarget->m_gangFlags |= flag; else pTarget->m_gangFlags &= ~flag; return 0; } case COMMAND_LOAD_AND_LAUNCH_MISSION_EXCLUSIVE: return 0; //case COMMAND_IS_MISSION_AUDIO_PLAYING: case COMMAND_CREATE_LOCKED_PROPERTY_PICKUP: { CollectParameters(&m_nIp, 3); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; char key[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; // TheText.Get(key); CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_PICKUP_PROPERTY, PICKUP_PROPERTY_LOCKED, 0, 0, false, key); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_CREATE_FORSALE_PROPERTY_PICKUP: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; char key[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; // TheText.Get(key); CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_PICKUP_PROPERTY_FORSALE, PICKUP_PROPERTY_FORSALE, ScriptParams[3], 0, false, key); StoreParameters(&m_nIp, 1); return 0; } case COMMAND_FREEZE_CAR_POSITION: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bIsFrozen = ScriptParams[1]; pVehicle->bInfiniteMass = ScriptParams[1]; return 0; } case COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CHAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); CPed* pTestedPed = CPools::GetPedPool()->GetAt(ScriptParams[1]); bool result = false; if (pPed) { if (pPed->m_lastDamEntity) { if (pPed->m_lastDamEntity == pTestedPed) result = true; if (pTestedPed->bInVehicle && pPed->m_lastDamEntity == pTestedPed->m_pMyVehicle) result = true; } }else debug("HAS_CHAR_BEEN_DAMAGED_BY_CHAR - First character doesn't exist\n"); UpdateCompareFlag(result); return 0; } //case COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CAR: //case COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CHAR: //case COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CAR: //case COMMAND_GET_RADIO_CHANNEL: //case COMMAND_DISPLAY_TEXT_WITH_3_NUMBERS: //case COMMAND_IS_CAR_DROWNING_IN_WATER: case COMMAND_IS_CHAR_DROWNING_IN_WATER: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); UpdateCompareFlag(pPed && pPed->bIsDrowning); return 0; } case COMMAND_DISABLE_CUTSCENE_SHADOWS: { CCutsceneMgr::DisableCutsceneShadows(); return 0; } case COMMAND_HAS_GLASS_BEEN_SHATTERED_NEARBY: { CollectParameters(&m_nIp, 3); bool shattered = false; if ( CGlass::HasGlassBeenShatteredAtCoors(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2]) ) shattered = true; UpdateCompareFlag(shattered); return 0; } case COMMAND_ATTACH_CUTSCENE_OBJECT_TO_BONE: { CollectParameters(&m_nIp, 3); CCutsceneMgr::AttachObjectToBone(CPools::GetObjectPool()->GetAt(ScriptParams[0]), CPools::GetObjectPool()->GetAt(ScriptParams[1]), ScriptParams[2]); return 0; } case COMMAND_ATTACH_CUTSCENE_OBJECT_TO_COMPONENT: { CollectParameters(&m_nIp, 2); CObject *obj1 = CPools::GetObjectPool()->GetAt(ScriptParams[0]); CObject *obj2 = CPools::GetObjectPool()->GetAt(ScriptParams[1]); char key[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; CCutsceneMgr::AttachObjectToFrame(obj1, obj2, key); return 0; } case COMMAND_SET_CHAR_STAY_IN_CAR_WHEN_JACKED: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bStayInCarOnJack = ScriptParams[1]; return 0; } //case COMMAND_IS_MISSION_AUDIO_LOADING: case COMMAND_ADD_MONEY_SPENT_ON_WEAPONS: CollectParameters(&m_nIp, 1); CStats::MoneySpentOnWeapons(ScriptParams[0]); return 0; case COMMAND_ADD_MONEY_SPENT_ON_PROPERTY: CollectParameters(&m_nIp, 1); CStats::MoneySpentOnProperty(ScriptParams[0]); return 0; //case COMMAND_ADD_MONEY_SPENT_ON_AUTO_PAINTING: case COMMAND_SET_CHAR_ANSWERING_MOBILE: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); if (ScriptParams[1]) pPed->SetAnswerMobile(); else pPed->ClearAnswerMobile(); return 0; } case COMMAND_SET_PLAYER_DRUNKENNESS: { CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; pPlayerInfo->m_pPed->m_nDrunkenness = ScriptParams[1]; pPlayerInfo->m_pPed->m_nFadeDrunkenness = 0; if (pPlayerInfo->m_pPed->m_nDrunkenness == 0) CMBlur::ClearDrunkBlur(); return 0; } //case COMMAND_GET_PLAYER_DRUNKENNESS: //case COMMAND_SET_PLAYER_DRUG_LEVEL: //case COMMAND_GET_PLAYER_DRUG_LEVEL: //case COMMAND_ADD_LOAN_SHARK_VISITS: case COMMAND_ADD_STORES_KNOCKED_OFF: CollectParameters(&m_nIp, 1); CStats::NumOfStoresKnockedOff(ScriptParams[0]); return 0; //case COMMAND_ADD_MOVIE_STUNTS: case COMMAND_ADD_NUMBER_OF_ASSASSINATIONS: CollectParameters(&m_nIp, 1); CStats::NumOfAssassinations(ScriptParams[0]); return 0; case COMMAND_ADD_PIZZAS_DELIVERED: CollectParameters(&m_nIp, 1); CStats::NumOfPizzasDelivered(ScriptParams[0]); return 0; //case COMMAND_ADD_GARBAGE_PICKUPS: case COMMAND_ADD_ICE_CREAMS_SOLD: CollectParameters(&m_nIp, 1); CStats::NumOfIceCreamSold(ScriptParams[0]); return 0; //case COMMAND_SET_TOP_SHOOTING_RANGE_SCORE: //case COMMAND_ADD_SHOOTING_RANGE_RANK: //case COMMAND_ADD_MONEY_SPENT_ON_GAMBLING: //case COMMAND_ADD_MONEY_WON_ON_GAMBLING: //case COMMAND_SET_LARGEST_GAMBLING_WIN: case COMMAND_SET_CHAR_IN_PLAYERS_GROUP_CAN_FIGHT: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bDontFight = !ScriptParams[1]; return 0; } case COMMAND_CLEAR_CHAR_WAIT_STATE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->ClearWaitState(); return 0; } case COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA_NO_SAVE: { CollectParameters(&m_nIp, 5); int handle = -1; uint32 i = CPools::GetVehiclePool()->GetSize(); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float supX = *(float*)&ScriptParams[2]; float supY = *(float*)&ScriptParams[3]; while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if (pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR && pVehicle->GetVehicleAppearance() != VEHICLE_APPEARANCE_BIKE) continue; if (ScriptParams[4] != pVehicle->GetModelIndex() && ScriptParams[4] >= 0) continue; if (pVehicle->VehicleCreatedBy != RANDOM_VEHICLE) continue; if (!pVehicle->IsWithinArea(infX, infY, supX, supY)) continue; handle = CPools::GetVehiclePool()->GetIndex(pVehicle); } ScriptParams[0] = handle; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_CAN_BURST_CAR_TYRES: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); pVehicle->bTyresDontBurst = !ScriptParams[1]; return 0; } case COMMAND_SET_PLAYER_AUTO_AIM: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); pPed->bDoomAim = ScriptParams[1]; return 0; } case COMMAND_FIRE_HUNTER_GUN: { CollectParameters(&m_nIp, 1); CVehicle *pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); if (CTimer::GetTimeInMilliseconds() > pVehicle->m_nGunFiringTime + 150) { CWeapon gun(WEAPONTYPE_HELICANNON, 5000); CVector worldGunPos = (pVehicle->GetMatrix() * vecHunterGunPos) + (CTimer::GetTimeStep() * pVehicle->m_vecMoveSpeed); gun.FireInstantHit(pVehicle, &worldGunPos); gun.AddGunshell(pVehicle, worldGunPos, CVector2D(0.f, 0.1f), 0.025f); DMAudio.PlayOneShot(pVehicle->m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.f); pVehicle->m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); } return 0; } case COMMAND_SET_PROPERTY_AS_OWNED: CollectParameters(&m_nIp, 1); CStats::AddPropertyAsOwned(ScriptParams[0]); return 0; case COMMAND_ADD_BLOOD_RING_KILLS: CollectParameters(&m_nIp, 1); CStats::AddNumBloodRingKills(ScriptParams[0]); return 0; case COMMAND_SET_LONGEST_TIME_IN_BLOOD_RING: CollectParameters(&m_nIp, 1); CStats::LongestTimeInBloodRing(ScriptParams[0]); return 0; case COMMAND_REMOVE_EVERYTHING_FOR_HUGE_CUTSCENE: { CCutsceneMgr::RemoveEverythingFromTheWorldForTheBiggestFuckoffCutsceneEver(); return 0; } case COMMAND_IS_PLAYER_TOUCHING_VEHICLE: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); CPhysical* pTestedEntity = pPed; if (pPed->bInVehicle && pPed->m_pMyVehicle) pTestedEntity = pPed->m_pMyVehicle; UpdateCompareFlag(pTestedEntity->GetHasCollidedWith(pVehicle)); return 0; } //case COMMAND_IS_CHAR_TOUCHING_VEHICLE: case COMMAND_CHECK_FOR_PED_MODEL_AROUND_PLAYER: { CollectParameters(&m_nIp, 6); CVector d1 = CWorld::Players[ScriptParams[0]].GetPos() - *(CVector*)&ScriptParams[1]; CVector d2 = CWorld::Players[ScriptParams[0]].GetPos() + *(CVector*)&ScriptParams[1]; int i = CPools::GetPedPool()->GetSize(); bool result = false; while (i--) { CPed* pPed = CPools::GetPedPool()->GetSlot(i); if (!pPed) continue; if (ScriptParams[4] != pPed->GetModelIndex() && ScriptParams[5] != pPed->GetModelIndex()) continue; if (pPed->IsWithinArea(d1.x, d1.y, d1.z, d2.x, d2.y, d2.z)) result = true; } UpdateCompareFlag(result); return 0; } case COMMAND_CLEAR_CHAR_FOLLOW_PATH: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (pPed->GetPedState() == PED_FOLLOW_PATH) { pPed->RestorePreviousState(); pPed->ClearFollowPath(); } return 0; } case COMMAND_SET_CHAR_CAN_BE_SHOT_IN_VEHICLE: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bCanBeShotInVehicle = ScriptParams[1]; return 0; } case COMMAND_ATTACH_CUTSCENE_OBJECT_TO_VEHICLE: { CollectParameters(&m_nIp, 2); CCutsceneMgr::AttachObjectToParent(CPools::GetObjectPool()->GetAt(ScriptParams[0]), CPools::GetVehiclePool()->GetAt(ScriptParams[1])); return 0; } case COMMAND_LOAD_MISSION_TEXT: { char key[8]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; TheText.LoadMissionText(key); return 0; } case COMMAND_SET_TONIGHTS_EVENT: { CollectParameters(&m_nIp, 1); CScrollBar::TonightsEvent = ScriptParams[0]; return 0; } case COMMAND_CLEAR_CHAR_LAST_DAMAGE_ENTITY: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); if (pPed) pPed->m_lastDamEntity = nil; else debug("CLEAR_CHAR_LAST_DAMAGE_ENTITY - Character doesn't exist\n"); return 0; } //case COMMAND_CLEAR_CAR_LAST_DAMAGE_ENTITY: case COMMAND_FREEZE_OBJECT_POSITION: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); pObject->bIsFrozen = ScriptParams[1]; pObject->bInfiniteMass = ScriptParams[1]; return 0; } case COMMAND_SET_PLAYER_HAS_MET_DEBBIE_HARRY: { CollectParameters(&m_nIp, 1); CTheScripts::bPlayerHasMetDebbieHarry = ScriptParams[0]; return 0; } case COMMAND_SET_RIOT_INTENSITY: { CollectParameters(&m_nIp, 1); CTheScripts::RiotIntensity = ScriptParams[0]; return 0; } //case COMMAND_IS_CAR_IN_ANGLED_AREA_2D: //case COMMAND_IS_CAR_IN_ANGLED_AREA_3D: //case COMMAND_REMOVE_WEAPON_FROM_CHAR: case COMMAND_SET_UP_TAXI_SHORTCUT: { CollectParameters(&m_nIp, 8); CGameLogic::SetUpShortCut( *(CVector*)&ScriptParams[0], *(float*)&ScriptParams[3], *(CVector*)&ScriptParams[4], *(float*)&ScriptParams[7]); return 0; } case COMMAND_CLEAR_TAXI_SHORTCUT: CGameLogic::ClearShortCut(); return 0; //case COMMAND_SET_CHAR_OBJ_GOTO_CAR_ON_FOOT: //case COMMAND_GET_CLOSEST_WATER_NODE: case COMMAND_ADD_PORN_LEAFLET_TO_RUBBISH: CollectParameters(&m_nIp, 1); CStats::PamphletMissionPassed = ScriptParams[0]; return 0; case COMMAND_CREATE_CLOTHES_PICKUP: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y) + PICKUP_PLACEMENT_OFFSET; CPickups::GetActualPickupIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); ScriptParams[0] = CPickups::GenerateNewOne(pos, MI_PICKUP_CLOTHES, PICKUP_ON_STREET, ScriptParams[3]); StoreParameters(&m_nIp, 1); return 0; } //case COMMAND_CHANGE_BLIP_THRESHOLD: case COMMAND_MAKE_PLAYER_FIRE_PROOF: { CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; pPlayerInfo->m_bFireproof = ScriptParams[1]; return 0; } case COMMAND_INCREASE_PLAYER_MAX_HEALTH: { CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; pPlayerInfo->m_nMaxHealth += ScriptParams[1]; pPlayerInfo->m_pPed->m_fHealth = pPlayerInfo->m_nMaxHealth; return 0; } case COMMAND_INCREASE_PLAYER_MAX_ARMOUR: { CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; pPlayerInfo->m_nMaxArmour += ScriptParams[1]; pPlayerInfo->m_pPed->m_fArmour = pPlayerInfo->m_nMaxArmour; return 0; } case COMMAND_CREATE_RANDOM_CHAR_AS_DRIVER: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); CPed* pPed = CPopulation::AddPedInCar(pVehicle, true); pPed->CharCreatedBy = MISSION_CHAR; pPed->bRespondsToThreats = false; pPed->bAllowMedicsToReviveMe = false; pPed->bIsPlayerFriend = false; if (pVehicle->bIsBus) pPed->bRenderPedInCar = false; pPed->SetPosition(pVehicle->GetPosition()); pPed->SetOrientation(0.0f, 0.0f, 0.0f); pPed->SetPedState(PED_DRIVING); pPed->m_pMyVehicle = pVehicle; pPed->m_pMyVehicle->RegisterReference((CEntity**)&pPed->m_pMyVehicle); pVehicle->pDriver = pPed; pVehicle->pDriver->RegisterReference((CEntity**)&pVehicle->pDriver); pPed->bInVehicle = true; pVehicle->SetStatus(STATUS_PHYSICS); if (pVehicle->m_vehType == VEHICLE_TYPE_BOAT) pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; pVehicle->bEngineOn = true; pVehicle->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pVehicle->GetPosition()); CPopulation::ms_nTotalMissionPeds++; ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_CREATE_RANDOM_CHAR_AS_PASSENGER: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); CPed* pPed = CPopulation::AddPedInCar(pVehicle, false); pPed->CharCreatedBy = MISSION_CHAR; pPed->bRespondsToThreats = false; pPed->bAllowMedicsToReviveMe = false; pPed->bIsPlayerFriend = false; if (pVehicle->bIsBus) pPed->bRenderPedInCar = false; pPed->SetPosition(pVehicle->GetPosition()); pPed->SetOrientation(0.0f, 0.0f, 0.0f); CPopulation::ms_nTotalMissionPeds++; pVehicle->m_nZoneLevel = CTheZones::GetLevelFromPosition(&pVehicle->GetPosition()); if (ScriptParams[1] >= 0) pVehicle->AddPassenger(pPed, ScriptParams[1]); else pVehicle->AddPassenger(pPed); pPed->m_pMyVehicle = pVehicle; pPed->m_pMyVehicle->RegisterReference((CEntity**)&pPed->m_pMyVehicle); pPed->bInVehicle = true; pPed->SetPedState(PED_DRIVING); ScriptParams[0] = CPools::GetPedPool()->GetIndex(pPed); StoreParameters(&m_nIp, 1); if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ScriptParams[0], CLEANUP_CHAR); return 0; } case COMMAND_SET_CHAR_IGNORE_THREATS_BEHIND_OBJECTS: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bIgnoreThreatsBehindObjects = ScriptParams[1]; return 0; } case COMMAND_ENSURE_PLAYER_HAS_DRIVE_BY_WEAPON: { CollectParameters(&m_nIp, 2); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); if (pPed->bInVehicle) { if (pPed->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_eWeaponType) { if (pPed->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_nAmmoTotal < ScriptParams[1]) pPed->SetAmmo(pPed->GetWeapon(WEAPONSLOT_SUBMACHINEGUN).m_eWeaponType, ScriptParams[1]); } else { pPed->GiveWeapon(WEAPONTYPE_UZI, ScriptParams[1], true); if (pPed->m_storedWeapon == WEAPONTYPE_UNIDENTIFIED) pPed->m_storedWeapon = pPed->GetWeapon()->m_eWeaponType; pPed->SetCurrentWeapon(WEAPONTYPE_UZI); } } return 0; } case COMMAND_MAKE_HELI_COME_CRASHING_DOWN: { CollectParameters(&m_nIp, 1); CAutomobile* pHeli = (CAutomobile*)CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pHeli && pHeli->IsCar() && pHeli->IsRealHeli()); pHeli->bHeliDestroyed = true; return 0; } case COMMAND_ADD_EXPLOSION_NO_SOUND: { CollectParameters(&m_nIp, 4); CExplosion::AddExplosion(nil, nil, (eExplosionType)ScriptParams[3], *(CVector*)&ScriptParams[0], 0, false); return 0; } case COMMAND_SET_OBJECT_AREA_VISIBLE: { CollectParameters(&m_nIp, 2); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); script_assert(pObject); pObject->m_area = ScriptParams[1]; return 0; } //case COMMAND_WAS_VEHICLE_EVER_POLICE: case COMMAND_SET_CHAR_NEVER_TARGETTED: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bNeverEverTargetThisPed = ScriptParams[1]; return 0; } case COMMAND_LOAD_UNCOMPRESSED_ANIM: { char key[KEY_LENGTH_IN_SCRIPT]; CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; CCutsceneMgr::LoadAnimationUncompressed(key); return 0; } case COMMAND_WAS_CUTSCENE_SKIPPED: { UpdateCompareFlag(CCutsceneMgr::WasCutsceneSkipped()); return 0; } case COMMAND_SET_CHAR_CROUCH_WHEN_THREATENED: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bCrouchWhenScared = true; return 0; } case COMMAND_IS_CHAR_IN_ANY_POLICE_VEHICLE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle && pPed->m_pMyVehicle->IsLawEnforcementVehicle() && pPed->m_pMyVehicle->GetModelIndex() != MI_PREDATOR); return 0; } case COMMAND_DOES_CHAR_EXIST: CollectParameters(&m_nIp, 1); UpdateCompareFlag(CPools::GetPedPool()->GetAt(ScriptParams[0]) != 0); return 0; //case COMMAND_DOES_VEHICLE_EXIST: //case COMMAND_ADD_SHORT_RANGE_BLIP_FOR_CONTACT_POINT: case COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_CONTACT_POINT: { CollectParameters(&m_nIp, 4); CVector pos = *(CVector*)&ScriptParams[0]; if (pos.z <= MAP_Z_LOW_LIMIT) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); int id = CRadar::SetShortRangeCoordBlip(BLIP_COORD, pos, 2, BLIP_DISPLAY_BOTH); CRadar::SetBlipSprite(id, ScriptParams[3]); ScriptParams[0] = id; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_IS_CHAR_STUCK: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->m_nWaitState == WAITSTATE_STUCK); return 0; } case COMMAND_SET_ALL_TAXIS_HAVE_NITRO: { CollectParameters(&m_nIp, 1); CVehicle::bAllTaxisHaveNitro = ScriptParams[0] != 0; return 0; } case COMMAND_SET_CHAR_STOP_SHOOT_DONT_SEEK_ENTITY: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (ScriptParams[1]) { pPed->bKindaStayInSamePlace = true; pPed->bStopAndShoot = true; } else { pPed->bKindaStayInSamePlace = false; pPed->bStopAndShoot = false; } pPed->m_nLastPedState = PED_NONE; return 0; } case COMMAND_FREEZE_CAR_POSITION_AND_DONT_LOAD_COLLISION: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); if (ScriptParams[1]) { pVehicle->bIsFrozen = true; pVehicle->bInfiniteMass = true; if (m_bIsMissionScript) { CWorld::Remove(pVehicle); pVehicle->bIsStaticWaitingForCollision = true; CWorld::Add(pVehicle); } } else { pVehicle->bIsFrozen = false; pVehicle->bInfiniteMass = false; } return 0; } //case COMMAND_FREEZE_CHAR_POSITION_AND_DONT_LOAD_COLLISION: //case COMMAND_FREEZE_OBJECT_POSITION_AND_DONT_LOAD_COLLISION: //case COMMAND_SET_FADE_AND_JUMPCUT_AFTER_RC_EXPLOSION: default: script_assert(0); } return -1; } ================================================ FILE: src/control/Script8.cpp ================================================ #include "common.h" #include "Script.h" #include "ScriptCommands.h" #include "DMAudio.h" #if ((defined GTAVC_JP_PATCH || defined SUPPORT_JAPANESE_SCRIPT) && defined MORE_LANGUAGES) #include "Frontend.h" #endif #include "GameLogic.h" #include "Garages.h" #ifdef MISSION_REPLAY #include "GenericGameStorage.h" #endif #if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) #include "General.h" #include "maths.h" #endif #include "Hud.h" #include "Pad.h" #include "PedAttractor.h" #include "Population.h" #include "Pools.h" #include "RpAnimBlend.h" #include "Stats.h" #include "VisibilityPlugins.h" #include "Wanted.h" #include "WaterLevel.h" #include "World.h" #include "Zones.h" int8 CRunningScript::ProcessCommands1400To1499(int32 command) { switch (command) { case COMMAND_REGISTER_VIGILANTE_LEVEL: CollectParameters(&m_nIp, 1); CStats::RegisterLevelVigilanteMission(ScriptParams[0]); return 0; case COMMAND_CLEAR_ALL_CHAR_ANIMS: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (!pPed->bInVehicle) { pPed->m_pVehicleAnim = nil; pPed->RestartNonPartialAnims(); RpAnimBlendClumpRemoveAllAssociations(pPed->GetClump()); pPed->SetPedState(PED_IDLE); pPed->SetMoveState(PEDMOVE_STILL); pPed->m_nLastPedState = PED_NONE; pPed->ClearAimFlag(); pPed->ClearLookFlag(); pPed->bIsPointingGunAt = false; if (pPed->IsPlayer()) ((CPlayerPed*)pPed)->m_fMoveSpeed = 0.0f; else pPed->m_nStoredMoveState = PEDMOVE_STILL; CAnimManager::AddAnimation(pPed->GetClump(), pPed->m_animGroup, ANIM_STD_IDLE); pPed->bIsPedDieAnimPlaying = false; } return 0; } case COMMAND_SET_MAXIMUM_NUMBER_OF_CARS_IN_GARAGE: CollectParameters(&m_nIp, 2); CGarages::SetMaxNumStoredCarsForGarage(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_WANTED_STARS_ARE_FLASHING: { CWanted* pWanted = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pWanted; UpdateCompareFlag(pWanted->m_nMinWantedLevel - pWanted->GetWantedLevel() > 0); return 0; } case COMMAND_SET_ALLOW_HURRICANES: CollectParameters(&m_nIp, 1); CStats::NoMoreHurricanes = ScriptParams[0]; return 0; case COMMAND_PLAY_ANNOUNCEMENT: { CollectParameters(&m_nIp, 1); DMAudio.PlayRadioAnnouncement(ScriptParams[0] + STREAMED_SOUND_ANNOUNCE_BRIDGE_CLOSED); return 0; } case COMMAND_SET_PLAYER_IS_IN_STADIUM: { CollectParameters(&m_nIp, 1); CTheScripts::bPlayerIsInTheStatium = ScriptParams[0]; return 0; } case COMMAND_GET_BUS_FARES_COLLECTED_BY_PLAYER: { CollectParameters(&m_nIp, 1); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; ScriptParams[0] = pPlayerInfo->m_pPed->m_nLastBusFareCollected; pPlayerInfo->m_pPed->m_nLastBusFareCollected = 0; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_SET_CHAR_OBJ_BUY_ICE_CREAM: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[1]); script_assert(pVehicle); ScriptParams[0] = 0; if (pPed->m_objective == OBJECTIVE_NONE && !pPed->bHasAlreadyUsedAttractor) { C2dEffect* pEffect = (C2dEffect*)GetPedAttractorManager()->GetEffectForIceCreamVan(pVehicle, pPed->GetPosition()); // has to be casted, because inner methods are const if (pEffect) { CVector pos; CPedAttractorManager::ComputeEffectPos(pEffect, pVehicle->GetMatrix(), pos); if ((pPed->GetPosition() - pos).MagnitudeSqr() < SQR(20.0f)) { if (GetPedAttractorManager()->HasEmptySlot(pEffect) && GetPedAttractorManager()->IsApproachable(pEffect, pVehicle->GetMatrix(), 0, pPed)) { if (GetPedAttractorManager()->RegisterPedWithAttractor(pPed, pEffect, pVehicle->GetMatrix())) ScriptParams[0] = 1; } } } } StoreParameters(&m_nIp, 1); return 0; } case COMMAND_DISPLAY_RADAR: CollectParameters(&m_nIp, 1); CHud::m_HideRadar = ScriptParams[0] == 0; return 0; case COMMAND_REGISTER_BEST_POSITION: CollectParameters(&m_nIp, 2); CStats::RegisterBestPosition(ScriptParams[0], ScriptParams[1]); return 0; case COMMAND_IS_PLAYER_IN_INFO_ZONE: { CollectParameters(&m_nIp, 1); CPlayerInfo* pPlayerInfo = &CWorld::Players[ScriptParams[0]]; char key[KEY_LENGTH_IN_SCRIPT]; memset(key, 0, KEY_LENGTH_IN_SCRIPT); CTheScripts::ReadTextLabelFromScript(&m_nIp, key); m_nIp += KEY_LENGTH_IN_SCRIPT; CVector pos = pPlayerInfo->GetPos(); CZone* infoZone = CTheZones::FindInformationZoneForPosition(&pos); UpdateCompareFlag(strncmp(key, infoZone->name, 8) == 0); // original code doesn't seem to be using strncmp in here and compare 2 ints instead return 0; } case COMMAND_CLEAR_CHAR_ICE_CREAM_PURCHASE: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (pPed->m_attractor) GetPedAttractorManager()->DeRegisterPed(pPed, pPed->m_attractor); return 0; } case COMMAND_IS_IN_CAR_FIRE_BUTTON_PRESSED: UpdateCompareFlag(CPad::GetPad(0)->GetCarGunFired()); return 0; case COMMAND_HAS_CHAR_ATTEMPTED_ATTRACTOR: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bHasAlreadyUsedAttractor); return 0; } case COMMAND_SET_LOAD_COLLISION_FOR_CAR_FLAG: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); if (ScriptParams[1]) { pVehicle->bDontLoadCollision = false; if (m_bMissionFlag) { CWorld::Remove(pVehicle); pVehicle->bIsStaticWaitingForCollision = true; CWorld::Add(pVehicle); } } else { pVehicle->bDontLoadCollision = true; if (pVehicle->bIsStaticWaitingForCollision) { pVehicle->bIsStaticWaitingForCollision = false; if (!pVehicle->GetIsStatic()) pVehicle->AddToMovingList(); } } return 0; } case COMMAND_SET_LOAD_COLLISION_FOR_CHAR_FLAG: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); if (ScriptParams[1]) { pPed->bDontLoadCollision = false; if (m_bMissionFlag) { CWorld::Remove(pPed); pPed->bIsStaticWaitingForCollision = true; CWorld::Add(pPed); } } else { pPed->bDontLoadCollision = true; if (pPed->bIsStaticWaitingForCollision) { pPed->bIsStaticWaitingForCollision = false; if (!pPed->GetIsStatic()) pPed->AddToMovingList(); } } return 0; } //case COMMAND_SET_LOAD_COLLISION_FOR_OBJECT_FLAG: case COMMAND_ADD_BIG_GUN_FLASH: { CollectParameters(&m_nIp, 6); CWeapon::AddGunFlashBigGuns(*(CVector*)&ScriptParams[0], *(CVector*)&ScriptParams[3]); return 0; } case COMMAND_HAS_CHAR_BOUGHT_ICE_CREAM: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(pPed->bBoughtIceCream); return 0; } case COMMAND_GET_PROGRESS_PERCENTAGE: *(float*)&ScriptParams[0] = CStats::GetPercentageProgress(); StoreParameters(&m_nIp, 1); return 0; case COMMAND_SET_SHORTCUT_PICKUP_POINT: { CollectParameters(&m_nIp, 4); CGameLogic::AddShortCutPointAfterDeath(*(CVector*)&ScriptParams[0], *(float*)&ScriptParams[3]); return 0; } case COMMAND_SET_SHORTCUT_DROPOFF_POINT_FOR_MISSION: { CollectParameters(&m_nIp, 4); CGameLogic::AddShortCutDropOffPointForMission(*(CVector*)&ScriptParams[0], *(float*)&ScriptParams[3]); return 0; } case COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_AREA: { CollectParameters(&m_nIp, 7); int ped_handle = -1; CVector pos = FindPlayerCoors(); float x1 = *(float*)&ScriptParams[0]; float y1 = *(float*)&ScriptParams[1]; float x2 = *(float*)&ScriptParams[2]; float y2 = *(float*)&ScriptParams[3]; int i = CPools::GetPedPool()->GetSize(); while (--i && ped_handle == -1) { CPed* pPed = CPools::GetPedPool()->GetSlot(i); if (!pPed) continue; if (CTheScripts::LastRandomPedId == CPools::GetPedPool()->GetIndex(pPed)) continue; if (pPed->CharCreatedBy != RANDOM_CHAR) continue; if (!pPed->IsPedInControl()) continue; if (pPed->bRemoveFromWorld) continue; if (pPed->bFadeOut) continue; if (pPed->m_nWaitState != WAITSTATE_FALSE) continue; if (pPed->bHasAlreadyUsedAttractor) continue; if (pPed->m_attractor) continue; if (!ThisIsAValidRandomPed(pPed->m_nPedType, ScriptParams[4], ScriptParams[5], ScriptParams[6])) continue; if (pPed->bIsLeader || pPed->m_leader) continue; if (!pPed->IsWithinArea(x1, y1, x2, y2)) continue; if (pos.z - PED_FIND_Z_OFFSET > pPed->GetPosition().z) continue; if (pos.z + PED_FIND_Z_OFFSET < pPed->GetPosition().z) continue; ped_handle = CPools::GetPedPool()->GetIndex(pPed); CTheScripts::LastRandomPedId = ped_handle; pPed->CharCreatedBy = MISSION_CHAR; pPed->bRespondsToThreats = false; ++CPopulation::ms_nTotalMissionPeds; if (m_bIsMissionScript) CTheScripts::MissionCleanUp.AddEntityToList(ped_handle, CLEANUP_CHAR); } ScriptParams[0] = ped_handle; StoreParameters(&m_nIp, 1); return 0; } //case COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_ZONE: case COMMAND_UNLOCK_ALL_CAR_DOORS_IN_AREA: { CollectParameters(&m_nIp, 4); uint32 i = CPools::GetVehiclePool()->GetSize(); float infX = *(float*)&ScriptParams[0]; float infY = *(float*)&ScriptParams[1]; float supX = *(float*)&ScriptParams[2]; float supY = *(float*)&ScriptParams[3]; while (i--) { CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; if (pVehicle->IsWithinArea(infX, infY, supX, supY)) pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; } return 0; } case COMMAND_SET_GANG_ATTACK_PLAYER_WITH_COPS: CollectParameters(&m_nIp, 2); CGangs::SetWillAttackPlayerWithCops((ePedType)((int)PEDTYPE_GANG1 + ScriptParams[0]), !!ScriptParams[1]); return 0; case COMMAND_SET_CHAR_FRIGHTENED_IN_JACKED_CAR: { CollectParameters(&m_nIp, 2); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); pPed->bHeldHostageInCar = ScriptParams[1]; return 0; } case COMMAND_SET_VEHICLE_TO_FADE_IN: { CollectParameters(&m_nIp, 2); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); script_assert(pVehicle); CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), ScriptParams[1]); return 0; } case COMMAND_REGISTER_ODDJOB_MISSION_PASSED: ++CStats::MissionsPassed; CStats::CheckPointReachedSuccessfully(); CTheScripts::LastMissionPassedTime = CTimer::GetTimeInMilliseconds(); CGameLogic::RemoveShortCutDropOffPointForMission(); return 0; case COMMAND_IS_PLAYER_IN_SHORTCUT_TAXI: { CollectParameters(&m_nIp, 1); CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; script_assert(pPed); UpdateCompareFlag(pPed->bInVehicle && pPed->m_pMyVehicle && pPed->m_pMyVehicle == CGameLogic::pShortCutTaxi); return 0; } case COMMAND_IS_CHAR_DUCKING: { CollectParameters(&m_nIp, 1); CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); script_assert(pPed); UpdateCompareFlag(RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_DUCK_DOWN) != nil); return 0; } case COMMAND_CREATE_DUST_EFFECT_FOR_CUTSCENE_HELI: { CollectParameters(&m_nIp, 3); CObject* pHeli = CPools::GetObjectPool()->GetAt(ScriptParams[0]); bool found = false; float waterLevel = -1000.0f; CVector pos = pHeli->GetPosition(); float radius = *(float*)&ScriptParams[1]; float ground = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &found); if (!CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) waterLevel = 0.0f; if (waterLevel > ground) ground = waterLevel; if (ScriptParams[2] > 8) ScriptParams[2] = 8; CVehicle::HeliDustGenerate(pHeli, (pos.z - ground - 1.0f - radius) * 0.3 + radius, ground, ScriptParams[2]); return 0; } case COMMAND_REGISTER_FIRE_LEVEL: CollectParameters(&m_nIp, 1); CStats::RegisterLevelFireMission(ScriptParams[0]); return 0; case COMMAND_IS_AUSTRALIAN_GAME: UpdateCompareFlag(false); // should we make some check? return 0; case COMMAND_DISARM_CAR_BOMB: { CollectParameters(&m_nIp, 1); CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); if (pVehicle->m_bombType != CARBOMB_NONE) { pVehicle->m_bombType = CARBOMB_NONE; pVehicle->m_pBombRigger = nil; } return 0; } #if (defined GTAVC_JP_PATCH || defined SUPPORT_JAPANESE_SCRIPT) case COMMAND_IS_JAPANESE_GAME: #ifdef MORE_LANGUAGES UpdateCompareFlag(FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_JAPANESE); #elif (defined GTAVC_JP_PATCH) UpdateCompareFlag(true); #else UpdateCompareFlag(false); #endif return 0; #elif (!defined GTA_PS2) case COMMAND_SET_ONSCREEN_COUNTER_FLASH_WHEN_FIRST_DISPLAYED: { script_assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); uint16 var = CTheScripts::Read2BytesFromScript(&m_nIp); CollectParameters(&m_nIp, 1); //CUserDisplay::OnscnTimer.SetCounterFlashWhenFirstDisplayed(var, ScriptParams[0]); return 0; } #endif #if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) case COMMAND_SHUFFLE_CARD_DECKS: { CollectParameters(&m_nIp, 1); script_assert(ScriptParams[0] >= 0 && ScriptParams[0] <= 6); for (int i = 0; i < CARDS_IN_STACK; i++) CTheScripts::CardStack[i] = 0; int16 seq[CARDS_IN_STACK]; for (int i = 0; i < MAX_DECKS * CARDS_IN_DECK; i++) seq[i] = i; int cards_left = CARDS_IN_DECK * ScriptParams[0]; for (int k = 1; k < CARDS_IN_DECK + 1; k++) { for (int deck = 0; deck < ScriptParams[0]; deck++) { int index = CGeneral::GetRandomNumberInRange(0, cards_left); CTheScripts::CardStack[seq[index]] = k; for (int l = index; l < cards_left; l++) { if (l + 1 < CARDS_IN_STACK) seq[l] = seq[l + 1]; else seq[l] = 0; } --cards_left; } } CTheScripts::CardStackPosition = 0; return 0; } case COMMAND_FETCH_NEXT_CARD: { if (CTheScripts::CardStack[CTheScripts::CardStackPosition] == 0) CTheScripts::CardStackPosition = 0; ScriptParams[0] = CTheScripts::CardStack[CTheScripts::CardStackPosition++]; if (CTheScripts::CardStackPosition == CARDS_IN_DECK * MAX_DECKS) CTheScripts::CardStackPosition = 0; StoreParameters(&m_nIp, 1); return 0; } case COMMAND_GET_OBJECT_VELOCITY: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); *(CVector*)&ScriptParams[0] = GAME_SPEED_TO_METERS_PER_SECOND * pObject->GetMoveSpeed(); StoreParameters(&m_nIp, 3); return 0; } case COMMAND_IS_DEBUG_CAMERA_ON: UpdateCompareFlag(TheCamera.WorldViewerBeingUsed); return 0; case COMMAND_ADD_TO_OBJECT_ROTATION_VELOCITY: { CollectParameters(&m_nIp, 4); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); CVector newSpeed = pObject->GetTurnSpeed() + *(CVector*)&ScriptParams[1] / GAME_SPEED_TO_METERS_PER_SECOND; if (pObject->bIsStatic) { pObject->SetIsStatic(false); pObject->AddToMovingList(); } pObject->SetTurnSpeed(newSpeed.x, newSpeed.y, newSpeed.z); return 0; } case COMMAND_SET_OBJECT_ROTATION_VELOCITY: { CollectParameters(&m_nIp, 4); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); CVector newSpeed = *(CVector*)&ScriptParams[1] / GAME_SPEED_TO_METERS_PER_SECOND; if (pObject->bIsStatic) { pObject->SetIsStatic(false); pObject->AddToMovingList(); } pObject->SetTurnSpeed(newSpeed.x, newSpeed.y, newSpeed.z); return 0; } case COMMAND_IS_OBJECT_STATIC: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); UpdateCompareFlag(pObject->GetIsStatic()); return 0; } case COMMAND_GET_ANGLE_BETWEEN_2D_VECTORS: { CollectParameters(&m_nIp, 4); CVector2D v1 = *(CVector2D*)&ScriptParams[0]; CVector2D v2 = *(CVector2D*)&ScriptParams[2]; float c = DotProduct2D(v1, v2) / (v1.Magnitude() * v2.Magnitude()); #ifdef FIX_BUGS // command is a SA leftover where it was fixed to this *(float*)&ScriptParams[0] = RADTODEG(Acos(c)); #else *(float*)&ScriptParams[0] = Acos(c); #endif return 0; } case COMMAND_DO_2D_RECTANGLES_COLLIDE: { CollectParameters(&m_nIp, 8); float infX1 = *(float*)&ScriptParams[0] - *(float*)&ScriptParams[2] * 0.5; // NB: not float float supX1 = *(float*)&ScriptParams[0] + *(float*)&ScriptParams[2] * 0.5; float infX2 = *(float*)&ScriptParams[4] - *(float*)&ScriptParams[6] * 0.5; float supX2 = *(float*)&ScriptParams[4] + *(float*)&ScriptParams[6] * 0.5; float infY1 = *(float*)&ScriptParams[1] - *(float*)&ScriptParams[3] * 0.5; float supY1 = *(float*)&ScriptParams[1] + *(float*)&ScriptParams[3] * 0.5; float infY2 = *(float*)&ScriptParams[5] - *(float*)&ScriptParams[7] * 0.5; float supY2 = *(float*)&ScriptParams[5] + *(float*)&ScriptParams[7] * 0.5; bool collide = true; if (infY2 > supY1) collide = false; if (infY1 > supY2) collide = false; if (infX2 > supX1) collide = false; if (infX1 > supX2) collide = false; UpdateCompareFlag(collide); return 0; } case COMMAND_GET_OBJECT_ROTATION_VELOCITY: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); *(CVector*)&ScriptParams[0] = pObject->GetTurnSpeed() * GAME_SPEED_TO_METERS_PER_SECOND; StoreParameters(&m_nIp, 3); return 0; } case COMMAND_ADD_VELOCITY_RELATIVE_TO_OBJECT_VELOCITY: { CollectParameters(&m_nIp, 4); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); CVector vecAddition = *(CVector*)&ScriptParams[1] * CTimer::GetTimeStep() / GAME_SPEED_TO_METERS_PER_SECOND; if (!pObject->bIsStatic) { CVector vecCurrSpeed = pObject->GetSpeed(); vecCurrSpeed.Normalise(); if (vecCurrSpeed.z != 1.0) { // NB: not float! CVector vx = CrossProduct(vecCurrSpeed, CVector(0.0f, 0.0f, 1.0f)); vx.Normalise(); CVector vz = CrossProduct(vx, vecCurrSpeed); vz.Normalise(); CVector vecNewSpeed = pObject->GetSpeed() + vecAddition.x * vx + vecAddition.y * vecCurrSpeed + vecAddition.z * vecCurrSpeed; if (pObject->bIsStatic) { pObject->SetIsStatic(false); pObject->AddToMovingList(); } pObject->SetMoveSpeed(vecNewSpeed); } } return 0; } case COMMAND_GET_OBJECT_SPEED: { CollectParameters(&m_nIp, 1); CObject* pObject = CPools::GetObjectPool()->GetAt(ScriptParams[0]); *(float*)&ScriptParams[0] = pObject->GetMoveSpeed().Magnitude() * GAME_SPEED_TO_METERS_PER_SECOND; StoreParameters(&m_nIp, 1); return 0; } #endif #if (defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) case COMMAND_IS_MISSION_SKIP: #ifdef MISSION_REPLAY ScriptParams[0] = MissionSkipLevel; #else ScriptParams[0] = 0; #endif StoreParameters(&m_nIp, 1); return 0; case COMMAND_SET_IN_AMMUNATION: CollectParameters(&m_nIp, 1); #ifdef MISSION_REPLAY IsInAmmunation = ScriptParams[0]; #endif return 0; case COMMAND_DO_SAVE_GAME: CollectParameters(&m_nIp, 1); #ifdef MISSION_REPLAY SaveGameForPause(ScriptParams[0]); #endif return 0; case COMMAND_IS_RETRY: #ifdef MISSION_REPLAY if (strcmp(m_abScriptName, "porno4") != 0) ScriptParams[0] = AllowMissionReplay; #ifdef FIX_BUGS else ScriptParams[0] = gbTryingPorn4Again; #else else if (gbTryingPorn4Again) ScriptParams[0] = 1; #endif #else ScriptParams[0] = 0; #endif StoreParameters(&m_nIp, 1); return 0; case COMMAND_DUMMY: return 0; #endif #if (defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) // it is unknown what these commands do but they don't take parameters case COMMAND_MARK_CUTSCENE_START: return 0; case COMMAND_MARK_CUTSCENE_END: return 0; case COMMAND_CUTSCENE_SCROLL: return 0; #endif default: script_assert(0); } return -1; } ================================================ FILE: src/control/ScriptCommands.h ================================================ #pragma once enum { COMMAND_NOP = 0, COMMAND_WAIT, COMMAND_GOTO, COMMAND_SHAKE_CAM, COMMAND_SET_VAR_INT, COMMAND_SET_VAR_FLOAT, COMMAND_SET_LVAR_INT, COMMAND_SET_LVAR_FLOAT, COMMAND_ADD_VAL_TO_INT_VAR, COMMAND_ADD_VAL_TO_FLOAT_VAR, COMMAND_ADD_VAL_TO_INT_LVAR, COMMAND_ADD_VAL_TO_FLOAT_LVAR, COMMAND_SUB_VAL_FROM_INT_VAR, COMMAND_SUB_VAL_FROM_FLOAT_VAR, COMMAND_SUB_VAL_FROM_INT_LVAR, COMMAND_SUB_VAL_FROM_FLOAT_LVAR, COMMAND_MULT_INT_VAR_BY_VAL, COMMAND_MULT_FLOAT_VAR_BY_VAL, COMMAND_MULT_INT_LVAR_BY_VAL, COMMAND_MULT_FLOAT_LVAR_BY_VAL, COMMAND_DIV_INT_VAR_BY_VAL, COMMAND_DIV_FLOAT_VAR_BY_VAL, COMMAND_DIV_INT_LVAR_BY_VAL, COMMAND_DIV_FLOAT_LVAR_BY_VAL, COMMAND_IS_INT_VAR_GREATER_THAN_NUMBER, COMMAND_IS_INT_LVAR_GREATER_THAN_NUMBER, COMMAND_IS_NUMBER_GREATER_THAN_INT_VAR, COMMAND_IS_NUMBER_GREATER_THAN_INT_LVAR, COMMAND_IS_INT_VAR_GREATER_THAN_INT_VAR, COMMAND_IS_INT_LVAR_GREATER_THAN_INT_LVAR, COMMAND_IS_INT_VAR_GREATER_THAN_INT_LVAR, COMMAND_IS_INT_LVAR_GREATER_THAN_INT_VAR, COMMAND_IS_FLOAT_VAR_GREATER_THAN_NUMBER, COMMAND_IS_FLOAT_LVAR_GREATER_THAN_NUMBER, COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_VAR, COMMAND_IS_NUMBER_GREATER_THAN_FLOAT_LVAR, COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_VAR, COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_LVAR, COMMAND_IS_FLOAT_VAR_GREATER_THAN_FLOAT_LVAR, COMMAND_IS_FLOAT_LVAR_GREATER_THAN_FLOAT_VAR, COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_NUMBER, COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_VAR, COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_INT_LVAR, COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_VAR, COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_LVAR, COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_INT_LVAR, COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_INT_VAR, COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_NUMBER, COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_NUMBER, COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_VAR, COMMAND_IS_NUMBER_GREATER_OR_EQUAL_TO_FLOAT_LVAR, COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, COMMAND_IS_FLOAT_VAR_GREATER_OR_EQUAL_TO_FLOAT_LVAR, COMMAND_IS_FLOAT_LVAR_GREATER_OR_EQUAL_TO_FLOAT_VAR, COMMAND_IS_INT_VAR_EQUAL_TO_NUMBER, COMMAND_IS_INT_LVAR_EQUAL_TO_NUMBER, COMMAND_IS_INT_VAR_EQUAL_TO_INT_VAR, COMMAND_IS_INT_LVAR_EQUAL_TO_INT_LVAR, COMMAND_IS_INT_VAR_EQUAL_TO_INT_LVAR, COMMAND_IS_INT_VAR_NOT_EQUAL_TO_NUMBER, COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_NUMBER, COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_VAR, COMMAND_IS_INT_LVAR_NOT_EQUAL_TO_INT_LVAR, COMMAND_IS_INT_VAR_NOT_EQUAL_TO_INT_LVAR, COMMAND_IS_FLOAT_VAR_EQUAL_TO_NUMBER, COMMAND_IS_FLOAT_LVAR_EQUAL_TO_NUMBER, COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_VAR, COMMAND_IS_FLOAT_LVAR_EQUAL_TO_FLOAT_LVAR, COMMAND_IS_FLOAT_VAR_EQUAL_TO_FLOAT_LVAR, COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_NUMBER, COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_NUMBER, COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_VAR, COMMAND_IS_FLOAT_LVAR_NOT_EQUAL_TO_FLOAT_LVAR, COMMAND_IS_FLOAT_VAR_NOT_EQUAL_TO_FLOAT_LVAR, COMMAND_GOTO_IF_TRUE, COMMAND_GOTO_IF_FALSE, COMMAND_TERMINATE_THIS_SCRIPT, COMMAND_START_NEW_SCRIPT, COMMAND_GOSUB, COMMAND_RETURN, COMMAND_LINE, COMMAND_CREATE_PLAYER, COMMAND_GET_PLAYER_COORDINATES, COMMAND_SET_PLAYER_COORDINATES, COMMAND_IS_PLAYER_IN_AREA_2D, COMMAND_IS_PLAYER_IN_AREA_3D, COMMAND_ADD_INT_VAR_TO_INT_VAR, COMMAND_ADD_FLOAT_VAR_TO_FLOAT_VAR, COMMAND_ADD_INT_LVAR_TO_INT_LVAR, COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_LVAR, COMMAND_ADD_INT_VAR_TO_INT_LVAR, COMMAND_ADD_FLOAT_VAR_TO_FLOAT_LVAR, COMMAND_ADD_INT_LVAR_TO_INT_VAR, COMMAND_ADD_FLOAT_LVAR_TO_FLOAT_VAR, COMMAND_SUB_INT_VAR_FROM_INT_VAR, COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_VAR, COMMAND_SUB_INT_LVAR_FROM_INT_LVAR, COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_LVAR, COMMAND_SUB_INT_VAR_FROM_INT_LVAR, COMMAND_SUB_FLOAT_VAR_FROM_FLOAT_LVAR, COMMAND_SUB_INT_LVAR_FROM_INT_VAR, COMMAND_SUB_FLOAT_LVAR_FROM_FLOAT_VAR, COMMAND_MULT_INT_VAR_BY_INT_VAR, COMMAND_MULT_FLOAT_VAR_BY_FLOAT_VAR, COMMAND_MULT_INT_LVAR_BY_INT_LVAR, COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_LVAR, COMMAND_MULT_INT_VAR_BY_INT_LVAR, COMMAND_MULT_FLOAT_VAR_BY_FLOAT_LVAR, COMMAND_MULT_INT_LVAR_BY_INT_VAR, COMMAND_MULT_FLOAT_LVAR_BY_FLOAT_VAR, COMMAND_DIV_INT_VAR_BY_INT_VAR, COMMAND_DIV_FLOAT_VAR_BY_FLOAT_VAR, COMMAND_DIV_INT_LVAR_BY_INT_LVAR, COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_LVAR, COMMAND_DIV_INT_VAR_BY_INT_LVAR, COMMAND_DIV_FLOAT_VAR_BY_FLOAT_LVAR, COMMAND_DIV_INT_LVAR_BY_INT_VAR, COMMAND_DIV_FLOAT_LVAR_BY_FLOAT_VAR, COMMAND_ADD_TIMED_VAL_TO_FLOAT_VAR, COMMAND_ADD_TIMED_VAL_TO_FLOAT_LVAR, COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_VAR, COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_LVAR, COMMAND_ADD_TIMED_FLOAT_LVAR_TO_FLOAT_VAR, COMMAND_ADD_TIMED_FLOAT_VAR_TO_FLOAT_LVAR, COMMAND_SUB_TIMED_VAL_FROM_FLOAT_VAR, COMMAND_SUB_TIMED_VAL_FROM_FLOAT_LVAR, COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_VAR, COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_LVAR, COMMAND_SUB_TIMED_FLOAT_LVAR_FROM_FLOAT_VAR, COMMAND_SUB_TIMED_FLOAT_VAR_FROM_FLOAT_LVAR, COMMAND_SET_VAR_INT_TO_VAR_INT, COMMAND_SET_LVAR_INT_TO_LVAR_INT, COMMAND_SET_VAR_FLOAT_TO_VAR_FLOAT, COMMAND_SET_LVAR_FLOAT_TO_LVAR_FLOAT, COMMAND_SET_VAR_FLOAT_TO_LVAR_FLOAT, COMMAND_SET_LVAR_FLOAT_TO_VAR_FLOAT, COMMAND_SET_VAR_INT_TO_LVAR_INT, COMMAND_SET_LVAR_INT_TO_VAR_INT, COMMAND_CSET_VAR_INT_TO_VAR_FLOAT, COMMAND_CSET_VAR_FLOAT_TO_VAR_INT, COMMAND_CSET_LVAR_INT_TO_VAR_FLOAT, COMMAND_CSET_LVAR_FLOAT_TO_VAR_INT, COMMAND_CSET_VAR_INT_TO_LVAR_FLOAT, COMMAND_CSET_VAR_FLOAT_TO_LVAR_INT, COMMAND_CSET_LVAR_INT_TO_LVAR_FLOAT, COMMAND_CSET_LVAR_FLOAT_TO_LVAR_INT, COMMAND_ABS_VAR_INT, COMMAND_ABS_LVAR_INT, COMMAND_ABS_VAR_FLOAT, COMMAND_ABS_LVAR_FLOAT, COMMAND_GENERATE_RANDOM_FLOAT, COMMAND_GENERATE_RANDOM_INT, COMMAND_CREATE_CHAR, COMMAND_DELETE_CHAR, COMMAND_CHAR_WANDER_DIR, COMMAND_CHAR_WANDER_RANGE, COMMAND_CHAR_FOLLOW_PATH, COMMAND_CHAR_SET_IDLE, COMMAND_GET_CHAR_COORDINATES, COMMAND_SET_CHAR_COORDINATES, COMMAND_IS_CHAR_STILL_ALIVE, COMMAND_IS_CHAR_IN_AREA_2D, COMMAND_IS_CHAR_IN_AREA_3D, COMMAND_CREATE_CAR, COMMAND_DELETE_CAR, COMMAND_CAR_GOTO_COORDINATES, COMMAND_CAR_WANDER_RANDOMLY, COMMAND_CAR_SET_IDLE, COMMAND_GET_CAR_COORDINATES, COMMAND_SET_CAR_COORDINATES, COMMAND_IS_CAR_STILL_ALIVE, COMMAND_SET_CAR_CRUISE_SPEED, COMMAND_SET_CAR_DRIVING_STYLE, COMMAND_SET_CAR_MISSION, COMMAND_IS_CAR_IN_AREA_2D, COMMAND_IS_CAR_IN_AREA_3D, COMMAND_SPECIAL_0, COMMAND_SPECIAL_1, COMMAND_SPECIAL_2, COMMAND_SPECIAL_3, COMMAND_SPECIAL_4, COMMAND_SPECIAL_5, COMMAND_SPECIAL_6, COMMAND_SPECIAL_7, COMMAND_PRINT_BIG, COMMAND_PRINT, COMMAND_PRINT_NOW, COMMAND_PRINT_SOON, COMMAND_CLEAR_PRINTS, COMMAND_GET_TIME_OF_DAY, COMMAND_SET_TIME_OF_DAY, COMMAND_GET_MINUTES_TO_TIME_OF_DAY, COMMAND_IS_POINT_ON_SCREEN, COMMAND_DEBUG_ON, COMMAND_DEBUG_OFF, COMMAND_RETURN_TRUE, COMMAND_RETURN_FALSE, COMMAND_VAR_INT, COMMAND_VAR_FLOAT, COMMAND_LVAR_INT, COMMAND_LVAR_FLOAT, COMMAND_LBRACKET, COMMAND_RBRACKET, COMMAND_REPEAT, COMMAND_ENDREPEAT, COMMAND_IF, COMMAND_IFNOT, COMMAND_ELSE, COMMAND_ENDIF, COMMAND_WHILE, COMMAND_WHILENOT, COMMAND_ENDWHILE, COMMAND_ANDOR, COMMAND_LAUNCH_MISSION, COMMAND_MISSION_HAS_FINISHED, COMMAND_STORE_CAR_CHAR_IS_IN, COMMAND_STORE_CAR_PLAYER_IS_IN, COMMAND_IS_CHAR_IN_CAR, COMMAND_IS_PLAYER_IN_CAR, COMMAND_IS_CHAR_IN_MODEL, COMMAND_IS_PLAYER_IN_MODEL, COMMAND_IS_CHAR_IN_ANY_CAR, COMMAND_IS_PLAYER_IN_ANY_CAR, COMMAND_IS_BUTTON_PRESSED, COMMAND_GET_PAD_STATE, COMMAND_LOCATE_PLAYER_ANY_MEANS_2D, COMMAND_LOCATE_PLAYER_ON_FOOT_2D, COMMAND_LOCATE_PLAYER_IN_CAR_2D, COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_2D, COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_2D, COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_2D, COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_2D, COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_2D, COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_2D, COMMAND_LOCATE_CHAR_ANY_MEANS_2D, COMMAND_LOCATE_CHAR_ON_FOOT_2D, COMMAND_LOCATE_CHAR_IN_CAR_2D, COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_2D, COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_2D, COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_2D, COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_2D, COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_2D, COMMAND_LOCATE_CHAR_IN_CAR_CHAR_2D, COMMAND_LOCATE_PLAYER_ANY_MEANS_3D, COMMAND_LOCATE_PLAYER_ON_FOOT_3D, COMMAND_LOCATE_PLAYER_IN_CAR_3D, COMMAND_LOCATE_STOPPED_PLAYER_ANY_MEANS_3D, COMMAND_LOCATE_STOPPED_PLAYER_ON_FOOT_3D, COMMAND_LOCATE_STOPPED_PLAYER_IN_CAR_3D, COMMAND_LOCATE_PLAYER_ANY_MEANS_CHAR_3D, COMMAND_LOCATE_PLAYER_ON_FOOT_CHAR_3D, COMMAND_LOCATE_PLAYER_IN_CAR_CHAR_3D, COMMAND_LOCATE_CHAR_ANY_MEANS_3D, COMMAND_LOCATE_CHAR_ON_FOOT_3D, COMMAND_LOCATE_CHAR_IN_CAR_3D, COMMAND_LOCATE_STOPPED_CHAR_ANY_MEANS_3D, COMMAND_LOCATE_STOPPED_CHAR_ON_FOOT_3D, COMMAND_LOCATE_STOPPED_CHAR_IN_CAR_3D, COMMAND_LOCATE_CHAR_ANY_MEANS_CHAR_3D, COMMAND_LOCATE_CHAR_ON_FOOT_CHAR_3D, COMMAND_LOCATE_CHAR_IN_CAR_CHAR_3D, COMMAND_CREATE_OBJECT, COMMAND_DELETE_OBJECT, COMMAND_ADD_SCORE, COMMAND_IS_SCORE_GREATER, COMMAND_STORE_SCORE, COMMAND_GIVE_REMOTE_CONTROLLED_CAR_TO_PLAYER, COMMAND_ALTER_WANTED_LEVEL, COMMAND_ALTER_WANTED_LEVEL_NO_DROP, COMMAND_IS_WANTED_LEVEL_GREATER, COMMAND_CLEAR_WANTED_LEVEL, COMMAND_SET_DEATHARREST_STATE, COMMAND_HAS_DEATHARREST_BEEN_EXECUTED, COMMAND_ADD_AMMO_TO_PLAYER, COMMAND_ADD_AMMO_TO_CHAR, COMMAND_ADD_AMMO_TO_CAR, COMMAND_IS_PLAYER_STILL_ALIVE, COMMAND_IS_PLAYER_DEAD, COMMAND_IS_CHAR_DEAD, COMMAND_IS_CAR_DEAD, COMMAND_SET_CHAR_THREAT_SEARCH, COMMAND_SET_CHAR_THREAT_REACTION, COMMAND_SET_CHAR_OBJ_NO_OBJ, COMMAND_ORDER_DRIVER_OUT_OF_CAR, COMMAND_ORDER_CHAR_TO_DRIVE_CAR, COMMAND_ADD_PATROL_POINT, COMMAND_IS_PLAYER_IN_GANGZONE, COMMAND_IS_PLAYER_IN_ZONE, COMMAND_IS_PLAYER_PRESSING_HORN, COMMAND_HAS_CHAR_SPOTTED_PLAYER, COMMAND_ORDER_CHAR_TO_BACKDOOR, COMMAND_ADD_CHAR_TO_GANG, COMMAND_IS_CHAR_OBJECTIVE_PASSED, COMMAND_SET_CHAR_DRIVE_AGGRESSION, COMMAND_SET_CHAR_MAX_DRIVESPEED, COMMAND_CREATE_CHAR_INSIDE_CAR, COMMAND_WARP_PLAYER_FROM_CAR_TO_COORD, COMMAND_MAKE_CHAR_DO_NOTHING, COMMAND_SET_CHAR_INVINCIBLE, COMMAND_SET_PLAYER_INVINCIBLE, COMMAND_SET_CHAR_GRAPHIC_TYPE, COMMAND_SET_PLAYER_GRAPHIC_TYPE, COMMAND_HAS_PLAYER_BEEN_ARRESTED, COMMAND_STOP_CHAR_DRIVING, COMMAND_KILL_CHAR, COMMAND_SET_FAVOURITE_CAR_MODEL_FOR_CHAR, COMMAND_SET_CHAR_OCCUPATION, COMMAND_CHANGE_CAR_LOCK, COMMAND_SHAKE_CAM_WITH_POINT, COMMAND_IS_CAR_MODEL, COMMAND_IS_CAR_REMAP, COMMAND_HAS_CAR_JUST_SUNK, COMMAND_SET_CAR_NO_COLLIDE, COMMAND_IS_CAR_DEAD_IN_AREA_2D, COMMAND_IS_CAR_DEAD_IN_AREA_3D, COMMAND_IS_TRAILER_ATTACHED, COMMAND_IS_CAR_ON_TRAILER, COMMAND_HAS_CAR_GOT_WEAPON, COMMAND_PARK, COMMAND_HAS_PARK_FINISHED, COMMAND_KILL_ALL_PASSENGERS, COMMAND_SET_CAR_BULLETPROOF, COMMAND_SET_CAR_FLAMEPROOF, COMMAND_SET_CAR_ROCKETPROOF, COMMAND_IS_CARBOMB_ACTIVE, COMMAND_GIVE_CAR_ALARM, COMMAND_PUT_CAR_ON_TRAILER, COMMAND_IS_CAR_CRUSHED, COMMAND_CREATE_GANG_CAR, COMMAND_CREATE_CAR_GENERATOR, COMMAND_SWITCH_CAR_GENERATOR, COMMAND_ADD_PAGER_MESSAGE, COMMAND_DISPLAY_ONSCREEN_TIMER, COMMAND_CLEAR_ONSCREEN_TIMER, COMMAND_DISPLAY_ONSCREEN_COUNTER, COMMAND_CLEAR_ONSCREEN_COUNTER, COMMAND_SET_ZONE_CAR_INFO, COMMAND_IS_CHAR_IN_GANG_ZONE, COMMAND_IS_CHAR_IN_ZONE, COMMAND_SET_CAR_DENSITY, COMMAND_SET_PED_DENSITY, COMMAND_POINT_CAMERA_AT_PLAYER, COMMAND_POINT_CAMERA_AT_CAR, COMMAND_POINT_CAMERA_AT_CHAR, COMMAND_RESTORE_CAMERA, COMMAND_SHAKE_PAD, COMMAND_SET_ZONE_PED_INFO, COMMAND_SET_TIME_SCALE, COMMAND_IS_CAR_IN_AIR, COMMAND_SET_FIXED_CAMERA_POSITION, COMMAND_POINT_CAMERA_AT_POINT, COMMAND_ADD_BLIP_FOR_CAR_OLD, COMMAND_ADD_BLIP_FOR_CHAR_OLD, COMMAND_ADD_BLIP_FOR_OBJECT_OLD, COMMAND_REMOVE_BLIP, COMMAND_CHANGE_BLIP_COLOUR, COMMAND_DIM_BLIP, COMMAND_ADD_BLIP_FOR_COORD_OLD, COMMAND_CHANGE_BLIP_SCALE, COMMAND_SET_FADING_COLOUR, COMMAND_DO_FADE, COMMAND_GET_FADING_STATUS, COMMAND_ADD_HOSPITAL_RESTART, COMMAND_ADD_POLICE_RESTART, COMMAND_OVERRIDE_NEXT_RESTART, COMMAND_DRAW_SHADOW, COMMAND_GET_PLAYER_HEADING, COMMAND_SET_PLAYER_HEADING, COMMAND_GET_CHAR_HEADING, COMMAND_SET_CHAR_HEADING, COMMAND_GET_CAR_HEADING, COMMAND_SET_CAR_HEADING, COMMAND_GET_OBJECT_HEADING, COMMAND_SET_OBJECT_HEADING, COMMAND_IS_PLAYER_TOUCHING_OBJECT, COMMAND_IS_CHAR_TOUCHING_OBJECT, COMMAND_SET_PLAYER_AMMO, COMMAND_SET_CHAR_AMMO, COMMAND_SET_CAR_AMMO, COMMAND_LOAD_CAMERA_SPLINE, COMMAND_MOVE_CAMERA_ALONG_SPLINE, COMMAND_GET_CAMERA_POSITION_ALONG_SPLINE, COMMAND_DECLARE_MISSION_FLAG, COMMAND_DECLARE_MISSION_FLAG_FOR_CONTACT, COMMAND_DECLARE_BASE_BRIEF_ID_FOR_CONTACT, COMMAND_IS_PLAYER_HEALTH_GREATER, COMMAND_IS_CHAR_HEALTH_GREATER, COMMAND_IS_CAR_HEALTH_GREATER, COMMAND_ADD_BLIP_FOR_CAR, COMMAND_ADD_BLIP_FOR_CHAR, COMMAND_ADD_BLIP_FOR_OBJECT, COMMAND_ADD_BLIP_FOR_CONTACT_POINT, COMMAND_ADD_BLIP_FOR_COORD, COMMAND_CHANGE_BLIP_DISPLAY, COMMAND_ADD_ONE_OFF_SOUND, COMMAND_ADD_CONTINUOUS_SOUND, COMMAND_REMOVE_SOUND, COMMAND_IS_CAR_STUCK_ON_ROOF, COMMAND_ADD_UPSIDEDOWN_CAR_CHECK, COMMAND_REMOVE_UPSIDEDOWN_CAR_CHECK, COMMAND_SET_CHAR_OBJ_WAIT_ON_FOOT, COMMAND_SET_CHAR_OBJ_FLEE_ON_FOOT_TILL_SAFE, COMMAND_SET_CHAR_OBJ_GUARD_SPOT, COMMAND_SET_CHAR_OBJ_GUARD_AREA, COMMAND_SET_CHAR_OBJ_WAIT_IN_CAR, COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_2D, COMMAND_IS_PLAYER_IN_AREA_IN_CAR_2D, COMMAND_IS_PLAYER_STOPPED_IN_AREA_2D, COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_2D, COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_2D, COMMAND_IS_PLAYER_IN_AREA_ON_FOOT_3D, COMMAND_IS_PLAYER_IN_AREA_IN_CAR_3D, COMMAND_IS_PLAYER_STOPPED_IN_AREA_3D, COMMAND_IS_PLAYER_STOPPED_IN_AREA_ON_FOOT_3D, COMMAND_IS_PLAYER_STOPPED_IN_AREA_IN_CAR_3D, COMMAND_IS_CHAR_IN_AREA_ON_FOOT_2D, COMMAND_IS_CHAR_IN_AREA_IN_CAR_2D, COMMAND_IS_CHAR_STOPPED_IN_AREA_2D, COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_2D, COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_2D, COMMAND_IS_CHAR_IN_AREA_ON_FOOT_3D, COMMAND_IS_CHAR_IN_AREA_IN_CAR_3D, COMMAND_IS_CHAR_STOPPED_IN_AREA_3D, COMMAND_IS_CHAR_STOPPED_IN_AREA_ON_FOOT_3D, COMMAND_IS_CHAR_STOPPED_IN_AREA_IN_CAR_3D, COMMAND_IS_CAR_STOPPED_IN_AREA_2D, COMMAND_IS_CAR_STOPPED_IN_AREA_3D, COMMAND_LOCATE_CAR_2D, COMMAND_LOCATE_STOPPED_CAR_2D, COMMAND_LOCATE_CAR_3D, COMMAND_LOCATE_STOPPED_CAR_3D, COMMAND_GIVE_WEAPON_TO_PLAYER, COMMAND_GIVE_WEAPON_TO_CHAR, COMMAND_GIVE_WEAPON_TO_CAR, COMMAND_SET_PLAYER_CONTROL, COMMAND_FORCE_WEATHER, COMMAND_FORCE_WEATHER_NOW, COMMAND_RELEASE_WEATHER, COMMAND_SET_CURRENT_PLAYER_WEAPON, COMMAND_SET_CURRENT_CHAR_WEAPON, COMMAND_SET_CURRENT_CAR_WEAPON, COMMAND_GET_OBJECT_COORDINATES, COMMAND_SET_OBJECT_COORDINATES, COMMAND_GET_GAME_TIMER, COMMAND_TURN_CHAR_TO_FACE_COORD, COMMAND_TURN_PLAYER_TO_FACE_COORD, COMMAND_STORE_WANTED_LEVEL, COMMAND_IS_CAR_STOPPED, COMMAND_MARK_CHAR_AS_NO_LONGER_NEEDED, COMMAND_MARK_CAR_AS_NO_LONGER_NEEDED, COMMAND_MARK_OBJECT_AS_NO_LONGER_NEEDED, COMMAND_DONT_REMOVE_CHAR, COMMAND_DONT_REMOVE_CAR, COMMAND_DONT_REMOVE_OBJECT, COMMAND_CREATE_CHAR_AS_PASSENGER, COMMAND_SET_CHAR_OBJ_KILL_CHAR_ON_FOOT, COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ON_FOOT, COMMAND_SET_CHAR_OBJ_KILL_CHAR_ANY_MEANS, COMMAND_SET_CHAR_OBJ_KILL_PLAYER_ANY_MEANS, COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE, COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE, COMMAND_SET_CHAR_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS, COMMAND_SET_CHAR_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS, COMMAND_SET_CHAR_OBJ_GOTO_CHAR_ON_FOOT, COMMAND_SET_CHAR_OBJ_GOTO_PLAYER_ON_FOOT, COMMAND_SET_CHAR_OBJ_LEAVE_CAR, COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_PASSENGER, COMMAND_SET_CHAR_OBJ_ENTER_CAR_AS_DRIVER, COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_IN_CAR, COMMAND_SET_CHAR_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE, COMMAND_SET_CHAR_OBJ_DESTROY_OBJECT, COMMAND_SET_CHAR_OBJ_DESTROY_CAR, COMMAND_SET_CHAR_OBJ_GOTO_AREA_ON_FOOT, COMMAND_SET_CHAR_OBJ_GOTO_AREA_IN_CAR, COMMAND_SET_CHAR_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, COMMAND_SET_CHAR_OBJ_GUARD_ATTACK, COMMAND_SET_CHAR_AS_LEADER, COMMAND_SET_PLAYER_AS_LEADER, COMMAND_LEAVE_GROUP, COMMAND_SET_CHAR_OBJ_FOLLOW_ROUTE, COMMAND_ADD_ROUTE_POINT, COMMAND_PRINT_WITH_NUMBER_BIG, COMMAND_PRINT_WITH_NUMBER, COMMAND_PRINT_WITH_NUMBER_NOW, COMMAND_PRINT_WITH_NUMBER_SOON, COMMAND_SWITCH_ROADS_ON, COMMAND_SWITCH_ROADS_OFF, COMMAND_GET_NUMBER_OF_PASSENGERS, COMMAND_GET_MAXIMUM_NUMBER_OF_PASSENGERS, COMMAND_SET_CAR_DENSITY_MULTIPLIER, COMMAND_SET_CAR_HEAVY, COMMAND_CLEAR_CHAR_THREAT_SEARCH, COMMAND_ACTIVATE_CRANE, COMMAND_DEACTIVATE_CRANE, COMMAND_SET_MAX_WANTED_LEVEL, COMMAND_SAVE_VAR_INT, COMMAND_SAVE_VAR_FLOAT, COMMAND_IS_CAR_IN_AIR_PROPER, COMMAND_IS_CAR_UPSIDEDOWN, COMMAND_GET_PLAYER_CHAR, COMMAND_CANCEL_OVERRIDE_RESTART, COMMAND_SET_POLICE_IGNORE_PLAYER, COMMAND_ADD_PAGER_MESSAGE_WITH_NUMBER, COMMAND_START_KILL_FRENZY, COMMAND_READ_KILL_FRENZY_STATUS, COMMAND_SQRT, COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_2D, COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_2D, COMMAND_LOCATE_PLAYER_IN_CAR_CAR_2D, COMMAND_LOCATE_PLAYER_ANY_MEANS_CAR_3D, COMMAND_LOCATE_PLAYER_ON_FOOT_CAR_3D, COMMAND_LOCATE_PLAYER_IN_CAR_CAR_3D, COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_2D, COMMAND_LOCATE_CHAR_ON_FOOT_CAR_2D, COMMAND_LOCATE_CHAR_IN_CAR_CAR_2D, COMMAND_LOCATE_CHAR_ANY_MEANS_CAR_3D, COMMAND_LOCATE_CHAR_ON_FOOT_CAR_3D, COMMAND_LOCATE_CHAR_IN_CAR_CAR_3D, COMMAND_GENERATE_RANDOM_FLOAT_IN_RANGE, COMMAND_GENERATE_RANDOM_INT_IN_RANGE, COMMAND_LOCK_CAR_DOORS, COMMAND_EXPLODE_CAR, COMMAND_ADD_EXPLOSION, COMMAND_IS_CAR_UPRIGHT, COMMAND_TURN_CHAR_TO_FACE_CHAR, COMMAND_TURN_CHAR_TO_FACE_PLAYER, COMMAND_TURN_PLAYER_TO_FACE_CHAR, COMMAND_SET_CHAR_OBJ_GOTO_COORD_ON_FOOT, COMMAND_SET_CHAR_OBJ_GOTO_COORD_IN_CAR, COMMAND_CREATE_PICKUP, COMMAND_HAS_PICKUP_BEEN_COLLECTED, COMMAND_REMOVE_PICKUP, COMMAND_SET_TAXI_LIGHTS, COMMAND_PRINT_BIG_Q, COMMAND_PRINT_WITH_NUMBER_BIG_Q, COMMAND_SET_GARAGE, COMMAND_SET_GARAGE_WITH_CAR_MODEL, COMMAND_SET_TARGET_CAR_FOR_MISSION_GARAGE, COMMAND_IS_CAR_IN_MISSION_GARAGE, COMMAND_SET_FREE_BOMBS, COMMAND_SET_POWERPOINT, COMMAND_SET_ALL_TAXI_LIGHTS, COMMAND_IS_CAR_ARMED_WITH_ANY_BOMB, COMMAND_APPLY_BRAKES_TO_PLAYERS_CAR, COMMAND_SET_PLAYER_HEALTH, COMMAND_SET_CHAR_HEALTH, COMMAND_SET_CAR_HEALTH, COMMAND_GET_PLAYER_HEALTH, COMMAND_GET_CHAR_HEALTH, COMMAND_GET_CAR_HEALTH, COMMAND_IS_CAR_ARMED_WITH_BOMB, COMMAND_CHANGE_CAR_COLOUR, COMMAND_SWITCH_PED_ROADS_ON, COMMAND_SWITCH_PED_ROADS_OFF, COMMAND_CHAR_LOOK_AT_CHAR_ALWAYS, COMMAND_CHAR_LOOK_AT_PLAYER_ALWAYS, COMMAND_PLAYER_LOOK_AT_CHAR_ALWAYS, COMMAND_STOP_CHAR_LOOKING, COMMAND_STOP_PLAYER_LOOKING, COMMAND_SWITCH_HELICOPTER, COMMAND_SET_GANG_ATTITUDE, COMMAND_SET_GANG_GANG_ATTITUDE, COMMAND_SET_GANG_PLAYER_ATTITUDE, COMMAND_SET_GANG_PED_MODELS, COMMAND_SET_GANG_CAR_MODEL, COMMAND_SET_GANG_WEAPONS, COMMAND_SET_CHAR_OBJ_RUN_TO_AREA, COMMAND_SET_CHAR_OBJ_RUN_TO_COORD, COMMAND_IS_PLAYER_TOUCHING_OBJECT_ON_FOOT, COMMAND_IS_CHAR_TOUCHING_OBJECT_ON_FOOT, COMMAND_LOAD_SPECIAL_CHARACTER, COMMAND_HAS_SPECIAL_CHARACTER_LOADED, COMMAND_FLASH_CAR, COMMAND_FLASH_CHAR, COMMAND_FLASH_OBJECT, COMMAND_IS_PLAYER_IN_REMOTE_MODE, COMMAND_ARM_CAR_WITH_BOMB, COMMAND_SET_CHAR_PERSONALITY, COMMAND_SET_CUTSCENE_OFFSET, COMMAND_SET_ANIM_GROUP_FOR_CHAR, COMMAND_SET_ANIM_GROUP_FOR_PLAYER, COMMAND_REQUEST_MODEL, COMMAND_HAS_MODEL_LOADED, COMMAND_MARK_MODEL_AS_NO_LONGER_NEEDED, COMMAND_GRAB_PHONE, COMMAND_SET_REPEATED_PHONE_MESSAGE, COMMAND_SET_PHONE_MESSAGE, COMMAND_HAS_PHONE_DISPLAYED_MESSAGE, COMMAND_TURN_PHONE_OFF, COMMAND_DRAW_CORONA, COMMAND_DRAW_LIGHT, COMMAND_STORE_WEATHER, COMMAND_RESTORE_WEATHER, COMMAND_STORE_CLOCK, COMMAND_RESTORE_CLOCK, COMMAND_RESTART_CRITICAL_MISSION, COMMAND_IS_PLAYER_PLAYING, COMMAND_SET_COLL_OBJ_NO_OBJ, COMMAND_SET_COLL_OBJ_WAIT_ON_FOOT, COMMAND_SET_COLL_OBJ_FLEE_ON_FOOT_TILL_SAFE, COMMAND_SET_COLL_OBJ_GUARD_SPOT, COMMAND_SET_COLL_OBJ_GUARD_AREA, COMMAND_SET_COLL_OBJ_WAIT_IN_CAR, COMMAND_SET_COLL_OBJ_KILL_CHAR_ON_FOOT, COMMAND_SET_COLL_OBJ_KILL_PLAYER_ON_FOOT, COMMAND_SET_COLL_OBJ_KILL_CHAR_ANY_MEANS, COMMAND_SET_COLL_OBJ_KILL_PLAYER_ANY_MEANS, COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_TILL_SAFE, COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_TILL_SAFE, COMMAND_SET_COLL_OBJ_FLEE_CHAR_ON_FOOT_ALWAYS, COMMAND_SET_COLL_OBJ_FLEE_PLAYER_ON_FOOT_ALWAYS, COMMAND_SET_COLL_OBJ_GOTO_CHAR_ON_FOOT, COMMAND_SET_COLL_OBJ_GOTO_PLAYER_ON_FOOT, COMMAND_SET_COLL_OBJ_LEAVE_CAR, COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_PASSENGER, COMMAND_SET_COLL_OBJ_ENTER_CAR_AS_DRIVER, COMMAND_SET_COLL_OBJ_FOLLOW_CAR_IN_CAR, COMMAND_SET_COLL_OBJ_FIRE_AT_OBJECT_FROM_VEHICLE, COMMAND_SET_COLL_OBJ_DESTROY_OBJECT, COMMAND_SET_COLL_OBJ_DESTROY_CAR, COMMAND_SET_COLL_OBJ_GOTO_AREA_ON_FOOT, COMMAND_SET_COLL_OBJ_GOTO_AREA_IN_CAR, COMMAND_SET_COLL_OBJ_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, COMMAND_SET_COLL_OBJ_GUARD_ATTACK, COMMAND_SET_COLL_OBJ_FOLLOW_ROUTE, COMMAND_SET_COLL_OBJ_GOTO_COORD_ON_FOOT, COMMAND_SET_COLL_OBJ_GOTO_COORD_IN_CAR, COMMAND_SET_COLL_OBJ_RUN_TO_AREA, COMMAND_SET_COLL_OBJ_RUN_TO_COORD, COMMAND_ADD_PEDS_IN_AREA_TO_COLL, COMMAND_ADD_PEDS_IN_VEHICLE_TO_COLL, COMMAND_CLEAR_COLL, COMMAND_IS_COLL_IN_CARS, COMMAND_LOCATE_COLL_ANY_MEANS_2D, COMMAND_LOCATE_COLL_ON_FOOT_2D, COMMAND_LOCATE_COLL_IN_CAR_2D, COMMAND_LOCATE_STOPPED_COLL_ANY_MEANS_2D, COMMAND_LOCATE_STOPPED_COLL_ON_FOOT_2D, COMMAND_LOCATE_STOPPED_COLL_IN_CAR_2D, COMMAND_LOCATE_COLL_ANY_MEANS_CHAR_2D, COMMAND_LOCATE_COLL_ON_FOOT_CHAR_2D, COMMAND_LOCATE_COLL_IN_CAR_CHAR_2D, COMMAND_LOCATE_COLL_ANY_MEANS_CAR_2D, COMMAND_LOCATE_COLL_ON_FOOT_CAR_2D, COMMAND_LOCATE_COLL_IN_CAR_CAR_2D, COMMAND_LOCATE_COLL_ANY_MEANS_PLAYER_2D, COMMAND_LOCATE_COLL_ON_FOOT_PLAYER_2D, COMMAND_LOCATE_COLL_IN_CAR_PLAYER_2D, COMMAND_IS_COLL_IN_AREA_2D, COMMAND_IS_COLL_IN_AREA_ON_FOOT_2D, COMMAND_IS_COLL_IN_AREA_IN_CAR_2D, COMMAND_IS_COLL_STOPPED_IN_AREA_2D, COMMAND_IS_COLL_STOPPED_IN_AREA_ON_FOOT_2D, COMMAND_IS_COLL_STOPPED_IN_AREA_IN_CAR_2D, COMMAND_GET_NUMBER_OF_PEDS_IN_COLL, COMMAND_SET_CHAR_HEED_THREATS, COMMAND_SET_PLAYER_HEED_THREATS, COMMAND_GET_CONTROLLER_MODE, COMMAND_SET_CAN_RESPRAY_CAR, COMMAND_IS_TAXI, COMMAND_UNLOAD_SPECIAL_CHARACTER, COMMAND_RESET_NUM_OF_MODELS_KILLED_BY_PLAYER, COMMAND_GET_NUM_OF_MODELS_KILLED_BY_PLAYER, COMMAND_ACTIVATE_GARAGE, COMMAND_SWITCH_TAXI_TIMER, COMMAND_CREATE_OBJECT_NO_OFFSET, COMMAND_IS_BOAT, COMMAND_SET_CHAR_OBJ_GOTO_AREA_ANY_MEANS, COMMAND_SET_COLL_OBJ_GOTO_AREA_ANY_MEANS, COMMAND_IS_PLAYER_STOPPED, COMMAND_IS_CHAR_STOPPED, COMMAND_MESSAGE_WAIT, COMMAND_ADD_PARTICLE_EFFECT, COMMAND_SWITCH_WIDESCREEN, COMMAND_ADD_SPRITE_BLIP_FOR_CAR, COMMAND_ADD_SPRITE_BLIP_FOR_CHAR, COMMAND_ADD_SPRITE_BLIP_FOR_OBJECT, COMMAND_ADD_SPRITE_BLIP_FOR_CONTACT_POINT, COMMAND_ADD_SPRITE_BLIP_FOR_COORD, COMMAND_SET_CHAR_ONLY_DAMAGED_BY_PLAYER, COMMAND_SET_CAR_ONLY_DAMAGED_BY_PLAYER, COMMAND_SET_CHAR_PROOFS, COMMAND_SET_CAR_PROOFS, COMMAND_IS_PLAYER_IN_ANGLED_AREA_2D, COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_2D, COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_2D, COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_2D, COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_2D, COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_2D, COMMAND_IS_PLAYER_IN_ANGLED_AREA_3D, COMMAND_IS_PLAYER_IN_ANGLED_AREA_ON_FOOT_3D, COMMAND_IS_PLAYER_IN_ANGLED_AREA_IN_CAR_3D, COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_3D, COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_ON_FOOT_3D, COMMAND_IS_PLAYER_STOPPED_IN_ANGLED_AREA_IN_CAR_3D, COMMAND_DEACTIVATE_GARAGE, COMMAND_GET_NUMBER_OF_CARS_COLLECTED_BY_GARAGE, COMMAND_HAS_CAR_BEEN_TAKEN_TO_GARAGE, COMMAND_SET_SWAT_REQUIRED, COMMAND_SET_FBI_REQUIRED, COMMAND_SET_ARMY_REQUIRED, COMMAND_IS_CAR_IN_WATER, COMMAND_GET_CLOSEST_CHAR_NODE, COMMAND_GET_CLOSEST_CAR_NODE, COMMAND_CAR_GOTO_COORDINATES_ACCURATE, COMMAND_START_PACMAN_RACE, COMMAND_START_PACMAN_RECORD, COMMAND_GET_NUMBER_OF_POWER_PILLS_EATEN, COMMAND_CLEAR_PACMAN, COMMAND_START_PACMAN_SCRAMBLE, COMMAND_GET_NUMBER_OF_POWER_PILLS_CARRIED, COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_CARRIED, COMMAND_IS_CAR_ON_SCREEN, COMMAND_IS_CHAR_ON_SCREEN, COMMAND_IS_OBJECT_ON_SCREEN, COMMAND_GOSUB_FILE, COMMAND_GET_GROUND_Z_FOR_3D_COORD, COMMAND_START_SCRIPT_FIRE, COMMAND_IS_SCRIPT_FIRE_EXTINGUISHED, COMMAND_REMOVE_SCRIPT_FIRE, COMMAND_SET_COMEDY_CONTROLS, COMMAND_BOAT_GOTO_COORDS, COMMAND_BOAT_STOP, COMMAND_IS_PLAYER_SHOOTING_IN_AREA, COMMAND_IS_CHAR_SHOOTING_IN_AREA, COMMAND_IS_CURRENT_PLAYER_WEAPON, COMMAND_IS_CURRENT_CHAR_WEAPON, COMMAND_CLEAR_NUMBER_OF_POWER_PILLS_EATEN, COMMAND_ADD_POWER_PILL, COMMAND_SET_BOAT_CRUISE_SPEED, COMMAND_GET_RANDOM_CHAR_IN_AREA, COMMAND_GET_RANDOM_CHAR_IN_ZONE, COMMAND_IS_PLAYER_IN_TAXI, COMMAND_IS_PLAYER_SHOOTING, COMMAND_IS_CHAR_SHOOTING, COMMAND_CREATE_MONEY_PICKUP, COMMAND_SET_CHAR_ACCURACY, COMMAND_GET_CAR_SPEED, COMMAND_LOAD_CUTSCENE, COMMAND_CREATE_CUTSCENE_OBJECT, COMMAND_SET_CUTSCENE_ANIM, COMMAND_START_CUTSCENE, COMMAND_GET_CUTSCENE_TIME, COMMAND_HAS_CUTSCENE_FINISHED, COMMAND_CLEAR_CUTSCENE, COMMAND_RESTORE_CAMERA_JUMPCUT, COMMAND_CREATE_COLLECTABLE1, COMMAND_SET_COLLECTABLE1_TOTAL, COMMAND_IS_PROJECTILE_IN_AREA, COMMAND_DESTROY_PROJECTILES_IN_AREA, COMMAND_DROP_MINE, COMMAND_DROP_NAUTICAL_MINE, COMMAND_IS_CHAR_MODEL, COMMAND_LOAD_SPECIAL_MODEL, COMMAND_CREATE_CUTSCENE_HEAD, COMMAND_SET_CUTSCENE_HEAD_ANIM, COMMAND_SIN, COMMAND_COS, COMMAND_GET_CAR_FORWARD_X, COMMAND_GET_CAR_FORWARD_Y, COMMAND_CHANGE_GARAGE_TYPE, COMMAND_ACTIVATE_CRUSHER_CRANE, COMMAND_PRINT_WITH_2_NUMBERS, COMMAND_PRINT_WITH_2_NUMBERS_NOW, COMMAND_PRINT_WITH_2_NUMBERS_SOON, COMMAND_PRINT_WITH_3_NUMBERS, COMMAND_PRINT_WITH_3_NUMBERS_NOW, COMMAND_PRINT_WITH_3_NUMBERS_SOON, COMMAND_PRINT_WITH_4_NUMBERS, COMMAND_PRINT_WITH_4_NUMBERS_NOW, COMMAND_PRINT_WITH_4_NUMBERS_SOON, COMMAND_PRINT_WITH_5_NUMBERS, COMMAND_PRINT_WITH_5_NUMBERS_NOW, COMMAND_PRINT_WITH_5_NUMBERS_SOON, COMMAND_PRINT_WITH_6_NUMBERS, COMMAND_PRINT_WITH_6_NUMBERS_NOW, COMMAND_PRINT_WITH_6_NUMBERS_SOON, COMMAND_SET_CHAR_OBJ_FOLLOW_CHAR_IN_FORMATION, COMMAND_PLAYER_MADE_PROGRESS, COMMAND_SET_PROGRESS_TOTAL, COMMAND_REGISTER_JUMP_DISTANCE, COMMAND_REGISTER_JUMP_HEIGHT, COMMAND_REGISTER_JUMP_FLIPS, COMMAND_REGISTER_JUMP_SPINS, COMMAND_REGISTER_JUMP_STUNT, COMMAND_REGISTER_UNIQUE_JUMP_FOUND, COMMAND_SET_UNIQUE_JUMPS_TOTAL, COMMAND_REGISTER_PASSENGER_DROPPED_OFF_TAXI, COMMAND_REGISTER_MONEY_MADE_TAXI, COMMAND_REGISTER_MISSION_GIVEN, COMMAND_REGISTER_MISSION_PASSED, COMMAND_SET_CHAR_RUNNING, COMMAND_REMOVE_ALL_SCRIPT_FIRES, COMMAND_IS_FIRST_CAR_COLOUR, COMMAND_IS_SECOND_CAR_COLOUR, COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_WEAPON, COMMAND_HAS_CAR_BEEN_DAMAGED_BY_WEAPON, COMMAND_IS_CHAR_IN_CHARS_GROUP, COMMAND_IS_CHAR_IN_PLAYERS_GROUP, COMMAND_EXPLODE_CHAR_HEAD, COMMAND_EXPLODE_PLAYER_HEAD, COMMAND_ANCHOR_BOAT, COMMAND_SET_ZONE_GROUP, COMMAND_START_CAR_FIRE, COMMAND_START_CHAR_FIRE, COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA, COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_ZONE, COMMAND_HAS_RESPRAY_HAPPENED, COMMAND_SET_CAMERA_ZOOM, COMMAND_CREATE_PICKUP_WITH_AMMO, COMMAND_SET_CAR_RAM_CAR, COMMAND_SET_CAR_BLOCK_CAR, COMMAND_SET_CHAR_OBJ_CATCH_TRAIN, COMMAND_SET_COLL_OBJ_CATCH_TRAIN, COMMAND_SET_PLAYER_NEVER_GETS_TIRED, COMMAND_SET_PLAYER_FAST_RELOAD, COMMAND_SET_CHAR_BLEEDING, COMMAND_SET_CAR_FUNNY_SUSPENSION, COMMAND_SET_CAR_BIG_WHEELS, COMMAND_SET_FREE_RESPRAYS, COMMAND_SET_PLAYER_VISIBLE, COMMAND_SET_CHAR_VISIBLE, COMMAND_SET_CAR_VISIBLE, COMMAND_IS_AREA_OCCUPIED, COMMAND_START_DRUG_RUN, COMMAND_HAS_DRUG_RUN_BEEN_COMPLETED, COMMAND_HAS_DRUG_PLANE_BEEN_SHOT_DOWN, COMMAND_SAVE_PLAYER_FROM_FIRES, COMMAND_DISPLAY_TEXT, COMMAND_SET_TEXT_SCALE, COMMAND_SET_TEXT_COLOUR, COMMAND_SET_TEXT_JUSTIFY, COMMAND_SET_TEXT_CENTRE, COMMAND_SET_TEXT_WRAPX, COMMAND_SET_TEXT_CENTRE_SIZE, COMMAND_SET_TEXT_BACKGROUND, COMMAND_SET_TEXT_BACKGROUND_COLOUR, COMMAND_SET_TEXT_BACKGROUND_ONLY_TEXT, COMMAND_SET_TEXT_PROPORTIONAL, COMMAND_SET_TEXT_FONT, COMMAND_INDUSTRIAL_PASSED, COMMAND_COMMERCIAL_PASSED, COMMAND_SUBURBAN_PASSED, COMMAND_ROTATE_OBJECT, COMMAND_SLIDE_OBJECT, COMMAND_REMOVE_CHAR_ELEGANTLY, COMMAND_SET_CHAR_STAY_IN_SAME_PLACE, COMMAND_IS_NASTY_GAME, COMMAND_UNDRESS_CHAR, COMMAND_DRESS_CHAR, COMMAND_START_CHASE_SCENE, COMMAND_STOP_CHASE_SCENE, COMMAND_IS_EXPLOSION_IN_AREA, COMMAND_IS_EXPLOSION_IN_ZONE, COMMAND_START_DRUG_DROP_OFF, COMMAND_HAS_DROP_OFF_PLANE_BEEN_SHOT_DOWN, COMMAND_FIND_DROP_OFF_PLANE_COORDINATES, COMMAND_CREATE_FLOATING_PACKAGE, COMMAND_PLACE_OBJECT_RELATIVE_TO_CAR, COMMAND_MAKE_OBJECT_TARGETTABLE, COMMAND_ADD_ARMOUR_TO_PLAYER, COMMAND_ADD_ARMOUR_TO_CHAR, COMMAND_OPEN_GARAGE, COMMAND_CLOSE_GARAGE, COMMAND_WARP_CHAR_FROM_CAR_TO_COORD, COMMAND_SET_VISIBILITY_OF_CLOSEST_OBJECT_OF_TYPE, COMMAND_HAS_CHAR_SPOTTED_CHAR, COMMAND_SET_CHAR_OBJ_HAIL_TAXI, COMMAND_HAS_OBJECT_BEEN_DAMAGED, COMMAND_START_KILL_FRENZY_HEADSHOT, COMMAND_ACTIVATE_MILITARY_CRANE, COMMAND_WARP_PLAYER_INTO_CAR, COMMAND_WARP_CHAR_INTO_CAR, COMMAND_SWITCH_CAR_RADIO, COMMAND_SET_AUDIO_STREAM, COMMAND_PRINT_WITH_2_NUMBERS_BIG, COMMAND_PRINT_WITH_3_NUMBERS_BIG, COMMAND_PRINT_WITH_4_NUMBERS_BIG, COMMAND_PRINT_WITH_5_NUMBERS_BIG, COMMAND_PRINT_WITH_6_NUMBERS_BIG, COMMAND_SET_CHAR_WAIT_STATE, COMMAND_SET_CAMERA_BEHIND_PLAYER, COMMAND_SET_MOTION_BLUR, COMMAND_PRINT_STRING_IN_STRING, COMMAND_CREATE_RANDOM_CHAR, COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR, COMMAND_SET_2_REPEATED_PHONE_MESSAGES, COMMAND_SET_2_PHONE_MESSAGES, COMMAND_SET_3_REPEATED_PHONE_MESSAGES, COMMAND_SET_3_PHONE_MESSAGES, COMMAND_SET_4_REPEATED_PHONE_MESSAGES, COMMAND_SET_4_PHONE_MESSAGES, COMMAND_IS_SNIPER_BULLET_IN_AREA, COMMAND_GIVE_PLAYER_DETONATOR, COMMAND_SET_COLL_OBJ_STEAL_ANY_CAR, COMMAND_SET_OBJECT_VELOCITY, COMMAND_SET_OBJECT_COLLISION, COMMAND_IS_ICECREAM_JINGLE_ON, COMMAND_PRINT_STRING_IN_STRING_NOW, COMMAND_PRINT_STRING_IN_STRING_SOON, COMMAND_SET_5_REPEATED_PHONE_MESSAGES, COMMAND_SET_5_PHONE_MESSAGES, COMMAND_SET_6_REPEATED_PHONE_MESSAGES, COMMAND_SET_6_PHONE_MESSAGES, COMMAND_IS_POINT_OBSCURED_BY_A_MISSION_ENTITY, COMMAND_LOAD_ALL_MODELS_NOW, COMMAND_ADD_TO_OBJECT_VELOCITY, COMMAND_DRAW_SPRITE, COMMAND_DRAW_RECT, COMMAND_LOAD_SPRITE, COMMAND_LOAD_TEXTURE_DICTIONARY, COMMAND_REMOVE_TEXTURE_DICTIONARY, COMMAND_SET_OBJECT_DYNAMIC, COMMAND_SET_CHAR_ANIM_SPEED, COMMAND_PLAY_MISSION_PASSED_TUNE, COMMAND_CLEAR_AREA, COMMAND_FREEZE_ONSCREEN_TIMER, COMMAND_SWITCH_CAR_SIREN, COMMAND_SWITCH_PED_ROADS_ON_ANGLED, COMMAND_SWITCH_PED_ROADS_OFF_ANGLED, COMMAND_SWITCH_ROADS_ON_ANGLED, COMMAND_SWITCH_ROADS_OFF_ANGLED, COMMAND_SET_CAR_WATERTIGHT, COMMAND_ADD_MOVING_PARTICLE_EFFECT, COMMAND_SET_CHAR_CANT_BE_DRAGGED_OUT, COMMAND_TURN_CAR_TO_FACE_COORD, COMMAND_IS_CRANE_LIFTING_CAR, COMMAND_DRAW_SPHERE, COMMAND_SET_CAR_STATUS, COMMAND_IS_CHAR_MALE, COMMAND_SCRIPT_NAME, COMMAND_CHANGE_GARAGE_TYPE_WITH_CAR_MODEL, COMMAND_FIND_DRUG_PLANE_COORDINATES, COMMAND_SAVE_INT_TO_DEBUG_FILE, COMMAND_SAVE_FLOAT_TO_DEBUG_FILE, COMMAND_SAVE_NEWLINE_TO_DEBUG_FILE, COMMAND_POLICE_RADIO_MESSAGE, COMMAND_SET_CAR_STRONG, COMMAND_REMOVE_ROUTE, COMMAND_SWITCH_RUBBISH, COMMAND_REMOVE_PARTICLE_EFFECTS_IN_AREA, COMMAND_SWITCH_STREAMING, COMMAND_IS_GARAGE_OPEN, COMMAND_IS_GARAGE_CLOSED, COMMAND_START_CATALINA_HELI, COMMAND_CATALINA_HELI_TAKE_OFF, COMMAND_REMOVE_CATALINA_HELI, COMMAND_HAS_CATALINA_HELI_BEEN_SHOT_DOWN, COMMAND_SWAP_NEAREST_BUILDING_MODEL, COMMAND_SWITCH_WORLD_PROCESSING, COMMAND_REMOVE_ALL_PLAYER_WEAPONS, COMMAND_GRAB_CATALINA_HELI, COMMAND_CLEAR_AREA_OF_CARS, COMMAND_SET_ROTATING_GARAGE_DOOR, COMMAND_ADD_SPHERE, COMMAND_REMOVE_SPHERE, COMMAND_CATALINA_HELI_FLY_AWAY, COMMAND_SET_EVERYONE_IGNORE_PLAYER, COMMAND_STORE_CAR_CHAR_IS_IN_NO_SAVE, COMMAND_STORE_CAR_PLAYER_IS_IN_NO_SAVE, COMMAND_IS_PHONE_DISPLAYING_MESSAGE, COMMAND_DISPLAY_ONSCREEN_TIMER_WITH_STRING, COMMAND_DISPLAY_ONSCREEN_COUNTER_WITH_STRING, COMMAND_CREATE_RANDOM_CAR_FOR_CAR_PARK, COMMAND_IS_COLLISION_IN_MEMORY, COMMAND_SET_WANTED_MULTIPLIER, COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER, COMMAND_IS_CAR_VISIBLY_DAMAGED, COMMAND_DOES_OBJECT_EXIST, COMMAND_LOAD_SCENE, COMMAND_ADD_STUCK_CAR_CHECK, COMMAND_REMOVE_STUCK_CAR_CHECK, COMMAND_IS_CAR_STUCK, COMMAND_LOAD_MISSION_AUDIO, COMMAND_HAS_MISSION_AUDIO_LOADED, COMMAND_PLAY_MISSION_AUDIO, COMMAND_HAS_MISSION_AUDIO_FINISHED, COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING, COMMAND_HAS_IMPORT_GARAGE_SLOT_BEEN_FILLED, COMMAND_CLEAR_THIS_PRINT, COMMAND_CLEAR_THIS_BIG_PRINT, COMMAND_SET_MISSION_AUDIO_POSITION, COMMAND_ACTIVATE_SAVE_MENU, COMMAND_HAS_SAVE_GAME_FINISHED, COMMAND_NO_SPECIAL_CAMERA_FOR_THIS_GARAGE, COMMAND_ADD_BLIP_FOR_PICKUP_OLD, COMMAND_ADD_BLIP_FOR_PICKUP, COMMAND_ADD_SPRITE_BLIP_FOR_PICKUP, COMMAND_SET_PED_DENSITY_MULTIPLIER, COMMAND_FORCE_RANDOM_PED_TYPE, COMMAND_SET_TEXT_DRAW_BEFORE_FADE, COMMAND_GET_COLLECTABLE1S_COLLECTED, COMMAND_SET_CHAR_OBJ_LEAVE_ANY_CAR, COMMAND_SET_SPRITES_DRAW_BEFORE_FADE, COMMAND_SET_TEXT_RIGHT_JUSTIFY, COMMAND_PRINT_HELP, COMMAND_CLEAR_HELP, COMMAND_FLASH_HUD_OBJECT, COMMAND_FLASH_RADAR_BLIP, COMMAND_IS_CHAR_IN_CONTROL, COMMAND_SET_GENERATE_CARS_AROUND_CAMERA, COMMAND_CLEAR_SMALL_PRINTS, COMMAND_HAS_MILITARY_CRANE_COLLECTED_ALL_CARS, COMMAND_SET_UPSIDEDOWN_CAR_NOT_DAMAGED, COMMAND_CAN_PLAYER_START_MISSION, COMMAND_MAKE_PLAYER_SAFE_FOR_CUTSCENE, COMMAND_USE_TEXT_COMMANDS, COMMAND_SET_THREAT_FOR_PED_TYPE, COMMAND_CLEAR_THREAT_FOR_PED_TYPE, COMMAND_GET_CAR_COLOURS, COMMAND_SET_ALL_CARS_CAN_BE_DAMAGED, COMMAND_SET_CAR_CAN_BE_DAMAGED, COMMAND_MAKE_PLAYER_UNSAFE, COMMAND_LOAD_COLLISION, COMMAND_GET_BODY_CAST_HEALTH, COMMAND_SET_CHARS_CHATTING, COMMAND_MAKE_PLAYER_SAFE, COMMAND_SET_CAR_STAYS_IN_CURRENT_LEVEL, COMMAND_SET_CHAR_STAYS_IN_CURRENT_LEVEL, COMMAND_SET_DRUNK_INPUT_DELAY, COMMAND_SET_CHAR_MONEY, COMMAND_INCREASE_CHAR_MONEY, COMMAND_GET_OFFSET_FROM_OBJECT_IN_WORLD_COORDS, COMMAND_REGISTER_LIFE_SAVED, COMMAND_REGISTER_CRIMINAL_CAUGHT, COMMAND_REGISTER_AMBULANCE_LEVEL, COMMAND_REGISTER_FIRE_EXTINGUISHED, COMMAND_TURN_PHONE_ON, COMMAND_REGISTER_LONGEST_DODO_FLIGHT, COMMAND_GET_OFFSET_FROM_CAR_IN_WORLD_COORDS, COMMAND_SET_TOTAL_NUMBER_OF_KILL_FRENZIES, COMMAND_BLOW_UP_RC_BUGGY, COMMAND_REMOVE_CAR_FROM_CHASE, COMMAND_IS_FRENCH_GAME, COMMAND_IS_GERMAN_GAME, COMMAND_CLEAR_MISSION_AUDIO, COMMAND_SET_FADE_IN_AFTER_NEXT_ARREST, COMMAND_SET_FADE_IN_AFTER_NEXT_DEATH, COMMAND_SET_GANG_PED_MODEL_PREFERENCE, COMMAND_SET_CHAR_USE_PEDNODE_SEEK, COMMAND_SWITCH_VEHICLE_WEAPONS, COMMAND_SET_GET_OUT_OF_JAIL_FREE, COMMAND_SET_FREE_HEALTH_CARE, COMMAND_IS_CAR_DOOR_CLOSED, COMMAND_LOAD_AND_LAUNCH_MISSION, COMMAND_LOAD_AND_LAUNCH_MISSION_INTERNAL, COMMAND_SET_OBJECT_DRAW_LAST, COMMAND_GET_AMMO_IN_PLAYER_WEAPON, COMMAND_GET_AMMO_IN_CHAR_WEAPON, COMMAND_REGISTER_KILL_FRENZY_PASSED, COMMAND_SET_CHAR_SAY, COMMAND_SET_NEAR_CLIP, COMMAND_SET_RADIO_CHANNEL, COMMAND_OVERRIDE_HOSPITAL_LEVEL, COMMAND_OVERRIDE_POLICE_STATION_LEVEL, COMMAND_FORCE_RAIN, COMMAND_DOES_GARAGE_CONTAIN_CAR, COMMAND_SET_CAR_TRACTION, COMMAND_ARE_MEASUREMENTS_IN_METRES, COMMAND_CONVERT_METRES_TO_FEET, COMMAND_MARK_ROADS_BETWEEN_LEVELS, COMMAND_MARK_PED_ROADS_BETWEEN_LEVELS, COMMAND_SET_CAR_AVOID_LEVEL_TRANSITIONS, COMMAND_SET_CHAR_AVOID_LEVEL_TRANSITIONS, COMMAND_IS_THREAT_FOR_PED_TYPE, COMMAND_CLEAR_AREA_OF_CHARS, COMMAND_SET_TOTAL_NUMBER_OF_MISSIONS, COMMAND_CONVERT_METRES_TO_FEET_INT, COMMAND_REGISTER_FASTEST_TIME, COMMAND_REGISTER_HIGHEST_SCORE, COMMAND_WARP_CHAR_INTO_CAR_AS_PASSENGER, COMMAND_IS_CAR_PASSENGER_SEAT_FREE, COMMAND_GET_CHAR_IN_CAR_PASSENGER_SEAT, COMMAND_SET_CHAR_IS_CHRIS_CRIMINAL, COMMAND_START_CREDITS, COMMAND_STOP_CREDITS, COMMAND_ARE_CREDITS_FINISHED, COMMAND_CREATE_SINGLE_PARTICLE, COMMAND_SET_CHAR_IGNORE_LEVEL_TRANSITIONS, COMMAND_GET_CHASE_CAR, COMMAND_START_BOAT_FOAM_ANIMATION, COMMAND_UPDATE_BOAT_FOAM_ANIMATION, COMMAND_SET_MUSIC_DOES_FADE, COMMAND_SET_INTRO_IS_PLAYING, COMMAND_SET_PLAYER_HOOKER, COMMAND_PLAY_END_OF_GAME_TUNE, COMMAND_STOP_END_OF_GAME_TUNE, COMMAND_GET_CAR_MODEL, COMMAND_IS_PLAYER_SITTING_IN_CAR, COMMAND_IS_PLAYER_SITTING_IN_ANY_CAR, COMMAND_SET_SCRIPT_FIRE_AUDIO, COMMAND_ARE_ANY_CAR_CHEATS_ACTIVATED, COMMAND_SET_CHAR_SUFFERS_CRITICAL_HITS, COMMAND_IS_PLAYER_LIFTING_A_PHONE, COMMAND_IS_CHAR_SITTING_IN_CAR, COMMAND_IS_CHAR_SITTING_IN_ANY_CAR, COMMAND_IS_PLAYER_ON_FOOT, COMMAND_IS_CHAR_ON_FOOT, COMMAND_LOAD_COLLISION_WITH_SCREEN, COMMAND_LOAD_SPLASH_SCREEN, COMMAND_SET_CAR_IGNORE_LEVEL_TRANSITIONS, COMMAND_MAKE_CRAIGS_CAR_A_BIT_STRONGER, COMMAND_SET_JAMES_CAR_ON_PATH_TO_PLAYER, COMMAND_LOAD_END_OF_GAME_TUNE, COMMAND_ENABLE_PLAYER_CONTROL_CAMERA, COMMAND_SET_OBJECT_ROTATION, COMMAND_GET_DEBUG_CAMERA_COORDINATES, COMMAND_GET_DEBUG_CAMERA_FRONT_VECTOR, COMMAND_IS_PLAYER_TARGETTING_ANY_CHAR, COMMAND_IS_PLAYER_TARGETTING_CHAR, COMMAND_IS_PLAYER_TARGETTING_OBJECT, COMMAND_TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, COMMAND_DISPLAY_TEXT_WITH_NUMBER, COMMAND_DISPLAY_TEXT_WITH_2_NUMBERS, COMMAND_FAIL_CURRENT_MISSION, COMMAND_GET_CLOSEST_OBJECT_OF_TYPE, COMMAND_PLACE_OBJECT_RELATIVE_TO_OBJECT, COMMAND_SET_ALL_OCCUPANTS_OF_CAR_LEAVE_CAR, COMMAND_SET_INTERPOLATION_PARAMETERS, COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_TOWARDS_POINT, COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING_AWAY_POINT, COMMAND_GET_DEBUG_CAMERA_POINT_AT, COMMAND_ATTACH_CHAR_TO_CAR, COMMAND_DETACH_CHAR_FROM_CAR, COMMAND_SET_CAR_CHANGE_LANE, COMMAND_CLEAR_CHAR_LAST_WEAPON_DAMAGE, COMMAND_CLEAR_CAR_LAST_WEAPON_DAMAGE, COMMAND_GET_RANDOM_COP_IN_AREA, COMMAND_GET_RANDOM_COP_IN_ZONE, COMMAND_SET_CHAR_OBJ_FLEE_CAR, COMMAND_GET_DRIVER_OF_CAR, COMMAND_GET_NUMBER_OF_FOLLOWERS, COMMAND_GIVE_REMOTE_CONTROLLED_MODEL_TO_PLAYER, COMMAND_GET_CURRENT_PLAYER_WEAPON, COMMAND_GET_CURRENT_CHAR_WEAPON, COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_2D, COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_2D, COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_2D, COMMAND_LOCATE_CHAR_ANY_MEANS_OBJECT_3D, COMMAND_LOCATE_CHAR_ON_FOOT_OBJECT_3D, COMMAND_LOCATE_CHAR_IN_CAR_OBJECT_3D, COMMAND_SET_CAR_TEMP_ACTION, COMMAND_SET_CAR_HANDBRAKE_TURN_RIGHT, COMMAND_SET_CAR_HANDBRAKE_STOP, COMMAND_IS_CHAR_ON_ANY_BIKE, COMMAND_LOCATE_SNIPER_BULLET_2D, COMMAND_LOCATE_SNIPER_BULLET_3D, COMMAND_GET_NUMBER_OF_SEATS_IN_MODEL, COMMAND_IS_PLAYER_ON_ANY_BIKE, COMMAND_IS_CHAR_LYING_DOWN, COMMAND_CAN_CHAR_SEE_DEAD_CHAR, COMMAND_SET_ENTER_CAR_RANGE_MULTIPLIER, COMMAND_SET_THREAT_REACTION_RANGE_MULTIPLIER, COMMAND_SET_CHAR_CEASE_ATTACK_TIMER, COMMAND_GET_REMOTE_CONTROLLED_CAR, COMMAND_IS_PC_VERSION, COMMAND_REPLAY, COMMAND_IS_REPLAY_PLAYING, COMMAND_IS_MODEL_AVAILABLE, COMMAND_SHUT_CHAR_UP, COMMAND_SET_ENABLE_RC_DETONATE, COMMAND_SET_CAR_RANDOM_ROUTE_SEED, COMMAND_IS_ANY_PICKUP_AT_COORDS, COMMAND_GET_FIRST_PICKUP_COORDS, COMMAND_GET_NEXT_PICKUP_COORDS, COMMAND_REMOVE_ALL_CHAR_WEAPONS, COMMAND_HAS_PLAYER_GOT_WEAPON, COMMAND_HAS_CHAR_GOT_WEAPON, COMMAND_IS_PLAYER_FACING_CHAR, COMMAND_SET_TANK_DETONATE_CARS, COMMAND_GET_POSITION_OF_ANALOGUE_STICKS, COMMAND_IS_CAR_ON_FIRE, COMMAND_IS_CAR_TYRE_BURST, COMMAND_SET_CAR_DRIVE_STRAIGHT_AHEAD, COMMAND_SET_CAR_WAIT, COMMAND_IS_PLAYER_STANDING_ON_A_VEHICLE, COMMAND_IS_PLAYER_FOOT_DOWN, COMMAND_IS_CHAR_FOOT_DOWN, COMMAND_INITIALISE_OBJECT_PATH, COMMAND_START_OBJECT_ON_PATH, COMMAND_SET_OBJECT_PATH_SPEED, COMMAND_SET_OBJECT_PATH_POSITION, COMMAND_GET_OBJECT_DISTANCE_ALONG_PATH, COMMAND_CLEAR_OBJECT_PATH, COMMAND_HELI_GOTO_COORDS, COMMAND_IS_INT_VAR_EQUAL_TO_CONSTANT, COMMAND_IS_INT_LVAR_EQUAL_TO_CONSTANT, COMMAND_GET_DEAD_CHAR_PICKUP_COORDS, COMMAND_CREATE_PROTECTION_PICKUP, COMMAND_IS_CHAR_IN_ANY_BOAT, COMMAND_IS_PLAYER_IN_ANY_BOAT, COMMAND_IS_CHAR_IN_ANY_HELI, COMMAND_IS_PLAYER_IN_ANY_HELI, COMMAND_IS_CHAR_IN_ANY_PLANE, COMMAND_IS_PLAYER_IN_ANY_PLANE, COMMAND_IS_CHAR_IN_WATER, COMMAND_SET_VAR_INT_TO_CONSTANT, COMMAND_SET_LVAR_INT_TO_CONSTANT, COMMAND_IS_INT_VAR_GREATER_THAN_CONSTANT, COMMAND_IS_INT_LVAR_GREATER_THAN_CONSTANT, COMMAND_IS_CONSTANT_GREATER_THAN_INT_VAR, COMMAND_IS_CONSTANT_GREATER_THAN_INT_LVAR, COMMAND_IS_INT_VAR_GREATER_OR_EQUAL_TO_CONSTANT, COMMAND_IS_INT_LVAR_GREATER_OR_EQUAL_TO_CONSTANT, COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_VAR, COMMAND_IS_CONSTANT_GREATER_OR_EQUAL_TO_INT_LVAR, COMMAND_GET_CHAR_WEAPON_IN_SLOT, COMMAND_GET_CLOSEST_STRAIGHT_ROAD, COMMAND_SET_CAR_FORWARD_SPEED, COMMAND_SET_AREA_VISIBLE, COMMAND_SET_CUTSCENE_ANIM_TO_LOOP, COMMAND_MARK_CAR_AS_CONVOY_CAR, COMMAND_RESET_HAVOC_CAUSED_BY_PLAYER, COMMAND_GET_HAVOC_CAUSED_BY_PLAYER, COMMAND_CREATE_SCRIPT_ROADBLOCK, COMMAND_CLEAR_ALL_SCRIPT_ROADBLOCKS, COMMAND_SET_CHAR_OBJ_WALK_TO_CHAR, COMMAND_IS_PICKUP_IN_ZONE, COMMAND_GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS, COMMAND_HAS_CHAR_BEEN_PHOTOGRAPHED, COMMAND_SET_CHAR_OBJ_AIM_GUN_AT_CHAR, COMMAND_SWITCH_SECURITY_CAMERA, COMMAND_IS_CHAR_IN_FLYING_VEHICLE, COMMAND_IS_PLAYER_IN_FLYING_VEHICLE, COMMAND_HAS_SONY_CD_BEEN_READ, COMMAND_GET_NUMBER_OF_SONY_CDS_READ, COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD_OLD, COMMAND_ADD_SHORT_RANGE_BLIP_FOR_COORD, COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_COORD, COMMAND_ADD_MONEY_SPENT_ON_CLOTHES, COMMAND_SET_HELI_ORIENTATION, COMMAND_CLEAR_HELI_ORIENTATION, COMMAND_PLANE_GOTO_COORDS, COMMAND_GET_NTH_CLOSEST_CAR_NODE, COMMAND_GET_NTH_CLOSEST_CHAR_NODE, COMMAND_DRAW_WEAPONSHOP_CORONA, COMMAND_SET_ENABLE_RC_DETONATE_ON_CONTACT, COMMAND_FREEZE_CHAR_POSITION, COMMAND_SET_CHAR_DROWNS_IN_WATER, COMMAND_SET_OBJECT_RECORDS_COLLISIONS, COMMAND_HAS_OBJECT_COLLIDED_WITH_ANYTHING, COMMAND_REMOVE_RC_BUGGY, COMMAND_HAS_PHOTOGRAPH_BEEN_TAKEN, COMMAND_GET_CHAR_ARMOUR, COMMAND_SET_CHAR_ARMOUR, COMMAND_SET_HELI_STABILISER, COMMAND_SET_CAR_STRAIGHT_LINE_DISTANCE, COMMAND_POP_CAR_BOOT, COMMAND_SHUT_PLAYER_UP, COMMAND_SET_PLAYER_MOOD, COMMAND_REQUEST_COLLISION, COMMAND_LOCATE_OBJECT_2D, COMMAND_LOCATE_OBJECT_3D, COMMAND_IS_OBJECT_IN_WATER, COMMAND_SET_CHAR_OBJ_STEAL_ANY_CAR_EVEN_MISSION_CAR, COMMAND_IS_OBJECT_IN_AREA_2D, COMMAND_IS_OBJECT_IN_AREA_3D, COMMAND_SET_CHAR_CROUCH, COMMAND_SET_ZONE_CIVILIAN_CAR_INFO, COMMAND_REQUEST_ANIMATION, COMMAND_HAS_ANIMATION_LOADED, COMMAND_REMOVE_ANIMATION, COMMAND_IS_CHAR_WAITING_FOR_WORLD_COLLISION, COMMAND_IS_CAR_WAITING_FOR_WORLD_COLLISION, COMMAND_IS_OBJECT_WAITING_FOR_WORLD_COLLISION, COMMAND_SET_CHAR_SHUFFLE_INTO_DRIVERS_SEAT, COMMAND_ATTACH_CHAR_TO_OBJECT, COMMAND_SET_CHAR_AS_PLAYER_FRIEND, COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER, COMMAND_DISPLAY_NTH_ONSCREEN_COUNTER_WITH_STRING, COMMAND_ADD_SET_PIECE, COMMAND_SET_EXTRA_COLOURS, COMMAND_CLEAR_EXTRA_COLOURS, COMMAND_CLOSE_CAR_BOOT, COMMAND_GET_WHEELIE_STATS, COMMAND_DISARM_CHAR, COMMAND_BURST_CAR_TYRE, COMMAND_IS_CHAR_OBJ_NO_OBJ, COMMAND_IS_PLAYER_WEARING, COMMAND_SET_PLAYER_CAN_DO_DRIVE_BY, COMMAND_SET_CHAR_OBJ_SPRINT_TO_COORD, COMMAND_CREATE_SWAT_ROPE, COMMAND_SET_FIRST_PERSON_CONTROL_CAMERA, COMMAND_GET_NEAREST_TYRE_TO_POINT, COMMAND_SET_CAR_MODEL_COMPONENTS, COMMAND_SWITCH_LIFT_CAMERA, COMMAND_CLOSE_ALL_CAR_DOORS, COMMAND_GET_DISTANCE_BETWEEN_COORDS_2D, COMMAND_GET_DISTANCE_BETWEEN_COORDS_3D, COMMAND_POP_CAR_BOOT_USING_PHYSICS, COMMAND_SET_FIRST_PERSON_WEAPON_CAMERA, COMMAND_IS_CHAR_LEAVING_VEHICLE_TO_DIE, COMMAND_SORT_OUT_OBJECT_COLLISION_WITH_CAR, COMMAND_GET_MAX_WANTED_LEVEL, COMMAND_IS_CHAR_WANDER_PATH_CLEAR, COMMAND_PRINT_HELP_WITH_NUMBER, COMMAND_PRINT_HELP_FOREVER, COMMAND_PRINT_HELP_FOREVER_WITH_NUMBER, COMMAND_SET_CHAR_CAN_BE_DAMAGED_BY_MEMBERS_OF_GANG, COMMAND_LOAD_AND_LAUNCH_MISSION_EXCLUSIVE, COMMAND_IS_MISSION_AUDIO_PLAYING, COMMAND_CREATE_LOCKED_PROPERTY_PICKUP, COMMAND_CREATE_FORSALE_PROPERTY_PICKUP, COMMAND_FREEZE_CAR_POSITION, COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CHAR, COMMAND_HAS_CHAR_BEEN_DAMAGED_BY_CAR, COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CHAR, COMMAND_HAS_CAR_BEEN_DAMAGED_BY_CAR, COMMAND_GET_RADIO_CHANNEL, COMMAND_DISPLAY_TEXT_WITH_3_NUMBERS, COMMAND_IS_CAR_DROWNING_IN_WATER, COMMAND_IS_CHAR_DROWNING_IN_WATER, COMMAND_DISABLE_CUTSCENE_SHADOWS, COMMAND_HAS_GLASS_BEEN_SHATTERED_NEARBY, COMMAND_ATTACH_CUTSCENE_OBJECT_TO_BONE, COMMAND_ATTACH_CUTSCENE_OBJECT_TO_COMPONENT, COMMAND_SET_CHAR_STAY_IN_CAR_WHEN_JACKED, COMMAND_IS_MISSION_AUDIO_LOADING, COMMAND_ADD_MONEY_SPENT_ON_WEAPONS, COMMAND_ADD_MONEY_SPENT_ON_PROPERTY, COMMAND_ADD_MONEY_SPENT_ON_AUTO_PAINTING, COMMAND_SET_CHAR_ANSWERING_MOBILE, COMMAND_SET_PLAYER_DRUNKENNESS, COMMAND_GET_PLAYER_DRUNKENNESS, COMMAND_SET_PLAYER_DRUG_LEVEL, COMMAND_GET_PLAYER_DRUG_LEVEL, COMMAND_ADD_LOAN_SHARK_VISITS, COMMAND_ADD_STORES_KNOCKED_OFF, COMMAND_ADD_MOVIE_STUNTS, COMMAND_ADD_NUMBER_OF_ASSASSINATIONS, COMMAND_ADD_PIZZAS_DELIVERED, COMMAND_ADD_GARBAGE_PICKUPS, COMMAND_ADD_ICE_CREAMS_SOLD, COMMAND_SET_TOP_SHOOTING_RANGE_SCORE, COMMAND_ADD_SHOOTING_RANGE_RANK, COMMAND_ADD_MONEY_SPENT_ON_GAMBLING, COMMAND_ADD_MONEY_WON_ON_GAMBLING, COMMAND_SET_LARGEST_GAMBLING_WIN, COMMAND_SET_CHAR_IN_PLAYERS_GROUP_CAN_FIGHT, COMMAND_CLEAR_CHAR_WAIT_STATE, COMMAND_GET_RANDOM_CAR_OF_TYPE_IN_AREA_NO_SAVE, COMMAND_SET_CAN_BURST_CAR_TYRES, COMMAND_SET_PLAYER_AUTO_AIM, COMMAND_FIRE_HUNTER_GUN, COMMAND_SET_PROPERTY_AS_OWNED, COMMAND_ADD_BLOOD_RING_KILLS, COMMAND_SET_LONGEST_TIME_IN_BLOOD_RING, COMMAND_REMOVE_EVERYTHING_FOR_HUGE_CUTSCENE, COMMAND_IS_PLAYER_TOUCHING_VEHICLE, COMMAND_IS_CHAR_TOUCHING_VEHICLE, COMMAND_CHECK_FOR_PED_MODEL_AROUND_PLAYER, COMMAND_CLEAR_CHAR_FOLLOW_PATH, COMMAND_SET_CHAR_CAN_BE_SHOT_IN_VEHICLE, COMMAND_ATTACH_CUTSCENE_OBJECT_TO_VEHICLE, COMMAND_LOAD_MISSION_TEXT, COMMAND_SET_TONIGHTS_EVENT, COMMAND_CLEAR_CHAR_LAST_DAMAGE_ENTITY, COMMAND_CLEAR_CAR_LAST_DAMAGE_ENTITY, COMMAND_FREEZE_OBJECT_POSITION, COMMAND_SET_PLAYER_HAS_MET_DEBBIE_HARRY, COMMAND_SET_RIOT_INTENSITY, COMMAND_IS_CAR_IN_ANGLED_AREA_2D, COMMAND_IS_CAR_IN_ANGLED_AREA_3D, COMMAND_REMOVE_WEAPON_FROM_CHAR, COMMAND_SET_UP_TAXI_SHORTCUT, COMMAND_CLEAR_TAXI_SHORTCUT, COMMAND_SET_CHAR_OBJ_GOTO_CAR_ON_FOOT, COMMAND_GET_CLOSEST_WATER_NODE, COMMAND_ADD_PORN_LEAFLET_TO_RUBBISH, COMMAND_CREATE_CLOTHES_PICKUP, COMMAND_CHANGE_BLIP_THRESHOLD, COMMAND_MAKE_PLAYER_FIRE_PROOF, COMMAND_INCREASE_PLAYER_MAX_HEALTH, COMMAND_INCREASE_PLAYER_MAX_ARMOUR, COMMAND_CREATE_RANDOM_CHAR_AS_DRIVER, COMMAND_CREATE_RANDOM_CHAR_AS_PASSENGER, COMMAND_SET_CHAR_IGNORE_THREATS_BEHIND_OBJECTS, COMMAND_ENSURE_PLAYER_HAS_DRIVE_BY_WEAPON, COMMAND_MAKE_HELI_COME_CRASHING_DOWN, COMMAND_ADD_EXPLOSION_NO_SOUND, COMMAND_SET_OBJECT_AREA_VISIBLE, COMMAND_WAS_VEHICLE_EVER_POLICE, COMMAND_SET_CHAR_NEVER_TARGETTED, COMMAND_LOAD_UNCOMPRESSED_ANIM, COMMAND_WAS_CUTSCENE_SKIPPED, COMMAND_SET_CHAR_CROUCH_WHEN_THREATENED, COMMAND_IS_CHAR_IN_ANY_POLICE_VEHICLE, COMMAND_DOES_CHAR_EXIST, COMMAND_DOES_VEHICLE_EXIST, COMMAND_ADD_SHORT_RANGE_BLIP_FOR_CONTACT_POINT, COMMAND_ADD_SHORT_RANGE_SPRITE_BLIP_FOR_CONTACT_POINT, COMMAND_IS_CHAR_STUCK, COMMAND_SET_ALL_TAXIS_HAVE_NITRO, COMMAND_SET_CHAR_STOP_SHOOT_DONT_SEEK_ENTITY, COMMAND_FREEZE_CAR_POSITION_AND_DONT_LOAD_COLLISION, COMMAND_FREEZE_CHAR_POSITION_AND_DONT_LOAD_COLLISION, COMMAND_FREEZE_OBJECT_POSITION_AND_DONT_LOAD_COLLISION, COMMAND_SET_FADE_AND_JUMPCUT_AFTER_RC_EXPLOSION, COMMAND_REGISTER_VIGILANTE_LEVEL, COMMAND_CLEAR_ALL_CHAR_ANIMS, COMMAND_SET_MAXIMUM_NUMBER_OF_CARS_IN_GARAGE, COMMAND_WANTED_STARS_ARE_FLASHING, COMMAND_SET_ALLOW_HURRICANES, COMMAND_PLAY_ANNOUNCEMENT, COMMAND_SET_PLAYER_IS_IN_STADIUM, COMMAND_GET_BUS_FARES_COLLECTED_BY_PLAYER, COMMAND_SET_CHAR_OBJ_BUY_ICE_CREAM, COMMAND_DISPLAY_RADAR, COMMAND_REGISTER_BEST_POSITION, COMMAND_IS_PLAYER_IN_INFO_ZONE, COMMAND_CLEAR_CHAR_ICE_CREAM_PURCHASE, COMMAND_IS_IN_CAR_FIRE_BUTTON_PRESSED, COMMAND_HAS_CHAR_ATTEMPTED_ATTRACTOR, COMMAND_SET_LOAD_COLLISION_FOR_CAR_FLAG, COMMAND_SET_LOAD_COLLISION_FOR_CHAR_FLAG, COMMAND_SET_LOAD_COLLISION_FOR_OBJECT_FLAG, COMMAND_ADD_BIG_GUN_FLASH, COMMAND_HAS_CHAR_BOUGHT_ICE_CREAM, COMMAND_GET_PROGRESS_PERCENTAGE, COMMAND_SET_SHORTCUT_PICKUP_POINT, COMMAND_SET_SHORTCUT_DROPOFF_POINT_FOR_MISSION, COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_AREA, COMMAND_GET_RANDOM_ICE_CREAM_CUSTOMER_IN_ZONE, COMMAND_UNLOCK_ALL_CAR_DOORS_IN_AREA, COMMAND_SET_GANG_ATTACK_PLAYER_WITH_COPS, COMMAND_SET_CHAR_FRIGHTENED_IN_JACKED_CAR, COMMAND_SET_VEHICLE_TO_FADE_IN, COMMAND_REGISTER_ODDJOB_MISSION_PASSED, COMMAND_IS_PLAYER_IN_SHORTCUT_TAXI, COMMAND_IS_CHAR_DUCKING, COMMAND_CREATE_DUST_EFFECT_FOR_CUTSCENE_HELI, COMMAND_REGISTER_FIRE_LEVEL, COMMAND_IS_AUSTRALIAN_GAME, COMMAND_DISARM_CAR_BOMB, #if (defined GTAVC_JP_PATCH || defined SUPPORT_JAPANESE_SCRIPT) COMMAND_IS_JAPANESE_GAME, #elif (!defined GTA_PS2) COMMAND_SET_ONSCREEN_COUNTER_FLASH_WHEN_FIRST_DISPLAYED, #endif #if (defined GTA_PC && !defined GTAVC_JP_PATCH || defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT || defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) COMMAND_SHUFFLE_CARD_DECKS, COMMAND_FETCH_NEXT_CARD, COMMAND_GET_OBJECT_VELOCITY, COMMAND_IS_DEBUG_CAMERA_ON, COMMAND_ADD_TO_OBJECT_ROTATION_VELOCITY, COMMAND_SET_OBJECT_ROTATION_VELOCITY, COMMAND_IS_OBJECT_STATIC, COMMAND_GET_ANGLE_BETWEEN_2D_VECTORS, COMMAND_DO_2D_RECTANGLES_COLLIDE, COMMAND_GET_OBJECT_ROTATION_VELOCITY, COMMAND_ADD_VELOCITY_RELATIVE_TO_OBJECT_VELOCITY, COMMAND_GET_OBJECT_SPEED, #endif #if (defined GTA_XBOX || defined SUPPORT_XBOX_SCRIPT) COMMAND_MARK_CUTSCENE_START, COMMAND_MARK_CUTSCENE_END, COMMAND_CUTSCENE_SCROLL, #elif (defined GTA_MOBILE || defined SUPPORT_MOBILE_SCRIPT) COMMAND_IS_MISSION_SKIP, COMMAND_SET_IN_AMMUNATION, COMMAND_DO_SAVE_GAME, COMMAND_IS_RETRY, COMMAND_DUMMY, COMMAND_MARK_CUTSCENE_START, COMMAND_MARK_CUTSCENE_END, COMMAND_CUTSCENE_SCROLL, #endif #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT LAST_SCRIPT_COMMAND #endif }; #ifdef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT enum eScriptArgument { ARGTYPE_NONE = 0, ARGTYPE_INT, ARGTYPE_FLOAT, ARGTYPE_STRING, ARGTYPE_LABEL, ARGTYPE_BOOL, ARGTYPE_PED_HANDLE, ARGTYPE_VEHICLE_HANDLE, ARGTYPE_OBJECT_HANDLE, ARGTYPE_ANDOR }; struct tScriptCommandData { int id; const char name[64]; eScriptArgument input[18]; eScriptArgument output[18]; bool cond; int position; const char name_override[8]; }; #endif ================================================ FILE: src/control/SetPieces.cpp ================================================ #include "common.h" #include "SetPieces.h" #include "Automobile.h" #include "CarAI.h" #include "CopPed.h" #include "GenericGameStorage.h" #include "PlayerPed.h" #include "Timer.h" #include "Vehicle.h" #include "Wanted.h" #include "World.h" #include "VarConsole.h" #define TIME_BETWEEN_SETPIECE_SPAWNS 20000 bool CSetPieces::bDebug; uint32 CSetPieces::NumSetPieces; CSetPiece CSetPieces::aSetPieces[NUM_SETPIECES]; void CSetPieces::Init(void) { bDebug = false; NumSetPieces = 0; #ifndef MASTER VarConsole.Add("Show set pieces", &bDebug, true); #endif } void CSetPieces::AddOne(uint8 type, CVector2D vTriggerInf, CVector2D vTriggerSup, CVector2D vSpawn1, CVector2D vTarget1, CVector2D vSpawn2, CVector2D vTarget2) { if (NumSetPieces >= NUM_SETPIECES) return; aSetPieces[NumSetPieces].m_nType = type; aSetPieces[NumSetPieces].m_vTriggerInf.x = Min(vTriggerInf.x, vTriggerSup.x); aSetPieces[NumSetPieces].m_vTriggerInf.y = Min(vTriggerInf.y, vTriggerSup.y); aSetPieces[NumSetPieces].m_vTriggerSup.x = Max(vTriggerInf.x, vTriggerSup.x); aSetPieces[NumSetPieces].m_vTriggerSup.y = Max(vTriggerInf.y, vTriggerSup.y); aSetPieces[NumSetPieces].m_vSpawn1 = vSpawn1; aSetPieces[NumSetPieces].m_vSpawn2 = vSpawn2; aSetPieces[NumSetPieces].m_vTarget1 = vTarget1; aSetPieces[NumSetPieces].m_vTarget2 = vTarget2; ++NumSetPieces; } void CSetPieces::Update(void) { int nFirst = NumSetPieces * (CTimer::GetFrameCounter() % 8) / 8; int nLast = NumSetPieces * (CTimer::GetFrameCounter() % 8 + 1) / 8; for (int i = nFirst; i < nLast; i++) aSetPieces[i].Update(); #ifndef MASTER // TODO: debug code from mobile #endif // !MASTER } void CSetPieces::Save(uint8* buf, uint32* size) { INITSAVEBUF WriteSaveBuf(buf, NumSetPieces); for (int i = 0; i < NUM_SETPIECES; i++) WriteSaveBuf(buf, aSetPieces[i]); *size = sizeof(NumSetPieces) + NUM_SETPIECES * sizeof(CSetPiece); VALIDATESAVEBUF(*size) } void CSetPieces::Load(uint8* buf, uint32 size) { INITSAVEBUF NumSetPieces = ReadSaveBuf(buf); for (int i = 0; i < NUM_SETPIECES; i++) aSetPieces[i] = ReadSaveBuf(buf); VALIDATESAVEBUF(size) } void CSetPiece::Update(void) { if (m_nLastTimeCreated != 0 && CTimer::GetTimeInMilliseconds() <= m_nLastTimeCreated + TIME_BETWEEN_SETPIECE_SPAWNS) return; CVector pos = FindPlayerCoors(); if (pos.x < m_vTriggerInf.x || pos.x > m_vTriggerSup.x || pos.y < m_vTriggerInf.y || pos.y > m_vTriggerSup.y) return; switch (m_nType) { case SETPIECE_TWOCOPCARSINALLEY: { if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 1 || FindPlayerVehicle()) return; CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); if (!pVehicle1) return; CVehicle* pVehicle2 = TryToGenerateCopCar(m_vSpawn2, m_vTarget2); if (!pVehicle2) { CWorld::Remove(pVehicle1); delete pVehicle1; return; } pVehicle1->SetStatus(STATUS_PHYSICS); pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 4; pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_SLOW_DOWN_FOR_CARS; pVehicle1->AutoPilot.m_nCarMission = MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1; pVehicle1->AutoPilot.m_vecDestinationCoors.x = m_vTarget1.x; pVehicle1->AutoPilot.m_vecDestinationCoors.y = m_vTarget1.y; pVehicle1->AutoPilot.m_vecDestinationCoors.z = 0.0f; pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 25000; CCarAI::AddPoliceCarOccupants(pVehicle1); pVehicle2->SetStatus(STATUS_PHYSICS); pVehicle2->AutoPilot.m_fMaxTrafficSpeed = pVehicle2->AutoPilot.m_nCruiseSpeed = 4; pVehicle2->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_SLOW_DOWN_FOR_CARS; pVehicle2->AutoPilot.m_nCarMission = MISSION_SLOWLY_DRIVE_TOWARDS_PLAYER_1; pVehicle2->AutoPilot.m_vecDestinationCoors.x = m_vTarget2.x; pVehicle2->AutoPilot.m_vecDestinationCoors.y = m_vTarget2.y; pVehicle2->AutoPilot.m_vecDestinationCoors.z = 0.0f; pVehicle2->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 25000; CCarAI::AddPoliceCarOccupants(pVehicle2); m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); break; } case SETPIECE_CARBLOCKINGPLAYERFROMSIDE: { if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 2) return; if (!FindPlayerVehicle()) return; if (DotProduct2D(FindPlayerSpeed(), (CVector2D)FindPlayerCoors() - m_vSpawn1) >= 0.0f) return; CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); if (!pVehicle1) return; pVehicle1->SetStatus(STATUS_PHYSICS); pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; pVehicle1->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FORWARDANDBACK; pVehicle1->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; pVehicle1->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; pVehicle1->SetMoveSpeed(2.0f * pVehicle1->GetForward() / 3.0f); CCarAI::AddPoliceCarOccupants(pVehicle1); m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); return; } case SETPIECE_CARRAMMINGPLAYERFROMSIDE: { if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 2) return; if (!FindPlayerVehicle()) return; if (DotProduct2D(FindPlayerSpeed(), (CVector2D)FindPlayerCoors() - m_vSpawn1) >= 0.0f) return; CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); if (!pVehicle1) return; pVehicle1->SetStatus(STATUS_PHYSICS); pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; pVehicle1->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE; pVehicle1->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; pVehicle1->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; pVehicle1->SetMoveSpeed(2.0f * pVehicle1->GetForward() / 3.0f); CCarAI::AddPoliceCarOccupants(pVehicle1); m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); return; } case SETPIECE_CREATECOPPERONFOOT: { if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 1 || FindPlayerVehicle()) return; CCopPed* pCop = TryToGenerateCopPed(m_vSpawn1); if (!pCop) return; float z = CWorld::FindGroundZForCoord(m_vTarget1.x, m_vTarget1.y); pCop->bScriptObjectiveCompleted = false; pCop->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector(m_vTarget1.x, m_vTarget1.y, z)); pCop->m_nExtendedRangeTimer = CTimer::GetTimeInMilliseconds() + 10000; m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); return; } case SETPIECE_CREATETWOCOPPERSONFOOT: { if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 1 || FindPlayerVehicle()) return; CCopPed* pCop = TryToGenerateCopPed(m_vSpawn1); if (!pCop) return; float z = CWorld::FindGroundZForCoord(m_vTarget1.x, m_vTarget1.y); pCop->bScriptObjectiveCompleted = false; pCop->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector(m_vTarget1.x, m_vTarget1.y, z)); pCop->m_nExtendedRangeTimer = CTimer::GetTimeInMilliseconds() + 10000; CCopPed* pCop2 = TryToGenerateCopPed(m_vSpawn2); if (!pCop2) { CWorld::Remove(pCop); delete pCop; return; } z = CWorld::FindGroundZForCoord(m_vTarget2.x, m_vTarget2.y); pCop2->bScriptObjectiveCompleted = false; pCop2->SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector(m_vTarget2.x, m_vTarget2.y, z)); pCop2->m_nExtendedRangeTimer = CTimer::GetTimeInMilliseconds() + 10000; m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); return; } case SETPIECE_TWOCARSBLOCKINGPLAYERFROMSIDE: { if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 2) return; if (!FindPlayerVehicle()) return; if (DotProduct2D(FindPlayerSpeed(), (CVector2D)FindPlayerCoors() - m_vSpawn1) >= 0.0f) return; CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); if (!pVehicle1) return; pVehicle1->SetStatus(STATUS_PHYSICS); pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; pVehicle1->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FORWARDANDBACK; pVehicle1->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; pVehicle1->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; pVehicle1->SetMoveSpeed(2.0f * pVehicle1->GetForward() / 3.0f); CCarAI::AddPoliceCarOccupants(pVehicle1); CVehicle* pVehicle2 = TryToGenerateCopCar(m_vSpawn2, m_vTarget2); if (!pVehicle2) { CWorld::Remove(pVehicle1); delete pVehicle1; return; } pVehicle2->SetStatus(STATUS_PHYSICS); pVehicle2->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; pVehicle2->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; pVehicle2->AutoPilot.m_nCarMission = MISSION_BLOCKPLAYER_FORWARDANDBACK; pVehicle2->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; pVehicle2->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; pVehicle2->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; pVehicle2->SetMoveSpeed(2.0f * pVehicle2->GetForward() / 3.0f); CCarAI::AddPoliceCarOccupants(pVehicle2); m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); return; } case SETPIECE_TWOCARSRAMMINGPLAYERFROMSIDE: { if (FindPlayerPed()->m_pWanted->GetWantedLevel() < 2) return; if (!FindPlayerVehicle()) return; if (DotProduct2D(FindPlayerSpeed(), (CVector2D)FindPlayerCoors() - m_vSpawn1) >= 0.0f) return; CVehicle* pVehicle1 = TryToGenerateCopCar(m_vSpawn1, m_vTarget1); if (!pVehicle1) return; pVehicle1->SetStatus(STATUS_PHYSICS); pVehicle1->AutoPilot.m_fMaxTrafficSpeed = pVehicle1->AutoPilot.m_nCruiseSpeed = 16; pVehicle1->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; pVehicle1->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE; pVehicle1->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; pVehicle1->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; pVehicle1->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; pVehicle1->SetMoveSpeed(2.0f * pVehicle1->GetForward() / 3.0f); CCarAI::AddPoliceCarOccupants(pVehicle1); CVehicle* pVehicle2 = TryToGenerateCopCar(m_vSpawn2, m_vTarget2); if (!pVehicle2) { CWorld::Remove(pVehicle1); delete pVehicle1; return; } pVehicle2->SetStatus(STATUS_PHYSICS); pVehicle2->AutoPilot.m_fMaxTrafficSpeed = pVehicle2->AutoPilot.m_nCruiseSpeed = 16; pVehicle2->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; pVehicle2->AutoPilot.m_nCarMission = MISSION_RAMCAR_CLOSE; pVehicle2->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; pVehicle2->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 100; pVehicle2->m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 10000; pVehicle2->SetMoveSpeed(2.0f * pVehicle2->GetForward() / 3.0f); CCarAI::AddPoliceCarOccupants(pVehicle2); m_nLastTimeCreated = CTimer::GetTimeInMilliseconds(); return; } } } CVehicle* CSetPiece::TryToGenerateCopCar(CVector2D vSpawn, CVector2D vTarget) { CVehicle* pVehicle = new CAutomobile(MI_POLICE, RANDOM_VEHICLE); CVector pos(vSpawn.x, vSpawn.y, 1000.0f); CColPoint point; CEntity* pEntity; if (CWorld::ProcessVerticalLine(pos, -1000.0f, point, pEntity, true, false, false, false, true, false, nil)) pos.z = point.point.z + pVehicle->GetHeightAboveRoad(); CVector vDirection(vTarget.x - vSpawn.x, vTarget.y - vSpawn.y, 0.0f); vDirection.Normalise(); pVehicle->GetForward() = CVector(vDirection.x, vDirection.y, 0.0f); pVehicle->GetRight() = CVector(vDirection.y, -vDirection.x, 0.0f); pVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); pVehicle->SetPosition(pos); int16 total; CWorld::FindObjectsKindaColliding(pos, pVehicle->GetColModel()->spheres->radius, false, &total, 16, nil, false, true, true, false, false); if (total != 0) { delete pVehicle; return nil; } pVehicle->ChangeLawEnforcerState(true); CWorld::Add(pVehicle); return pVehicle; } CCopPed* CSetPiece::TryToGenerateCopPed(CVector2D vSpawn) { CCopPed* pCop = new CCopPed(COP_STREET); CVector pos(vSpawn.x, vSpawn.y, 1000.0f); CColPoint point; CEntity* pEntity; if (CWorld::ProcessVerticalLine(pos, -1000.0f, point, pEntity, true, false, false, false, true, false, nil)) pos.z = point.point.z + 0.9f; pCop->SetPosition(pos); int16 total; CWorld::FindObjectsKindaColliding(pos, pCop->GetColModel()->spheres->radius, false, &total, 16, nil, false, true, true, false, false); if (total != 0) { delete pCop; return nil; } CWorld::Add(pCop); return pCop; } ================================================ FILE: src/control/SetPieces.h ================================================ #pragma once #include "config.h" class CVehicle; class CCopPed; enum eSetPieceType { SETPIECE_NONE = 0, SETPIECE_TWOCOPCARSINALLEY, SETPIECE_CARBLOCKINGPLAYERFROMSIDE, SETPIECE_CARRAMMINGPLAYERFROMSIDE, SETPIECE_CREATECOPPERONFOOT, SETPIECE_CREATETWOCOPPERSONFOOT, SETPIECE_TWOCARSBLOCKINGPLAYERFROMSIDE, SETPIECE_TWOCARSRAMMINGPLAYERFROMSIDE }; class CSetPiece { public: uint8 m_nType; uint32 m_nLastTimeCreated; CVector2D m_vTriggerInf; CVector2D m_vTriggerSup; CVector2D m_vSpawn1; CVector2D m_vSpawn2; CVector2D m_vTarget1; CVector2D m_vTarget2; CVehicle* TryToGenerateCopCar(CVector2D, CVector2D); CCopPed* TryToGenerateCopPed(CVector2D); void Update(void); }; class CSetPieces { static bool bDebug; static uint32 NumSetPieces; static CSetPiece aSetPieces[NUM_SETPIECES]; public: static void Init(void); static void AddOne(uint8 type, CVector2D, CVector2D, CVector2D, CVector2D, CVector2D, CVector2D); static void Save(uint8*, uint32*); static void Load(uint8*, uint32); static void Update(void); }; ================================================ FILE: src/control/TrafficLights.cpp ================================================ #include "common.h" #include "Camera.h" #include "Clock.h" #include "Coronas.h" #include "General.h" #include "PathFind.h" #include "PointLights.h" #include "Shadows.h" #include "SpecialFX.h" #include "Timecycle.h" #include "Timer.h" #include "TrafficLights.h" #include "Vehicle.h" #include "Weather.h" #include "World.h" bool CTrafficLights::bGreenLightsCheat; void CTrafficLights::DisplayActualLight(CEntity *ent) { if(ent->GetUp().z < 0.96f || ent->bRenderDamaged) return; int phase; if(FindTrafficLightType(ent) == 1) phase = LightForCars1_Visual(); else phase = LightForCars2_Visual(); int i, m = ent->GetModelIndex(); if (MI_TRAFFICLIGHTS == m) { CBaseModelInfo* mi = CModelInfo::GetModelInfo(ent->GetModelIndex()); float x = mi->Get2dEffect(0)->pos.x; float yMin = mi->Get2dEffect(0)->pos.y; float yMax = mi->Get2dEffect(0)->pos.y; float zMin = mi->Get2dEffect(0)->pos.z; float zMax = mi->Get2dEffect(0)->pos.z; for (i = 1; i < 6; i++) { assert(mi->Get2dEffect(i)); yMin = Min(yMin, mi->Get2dEffect(i)->pos.y); yMax = Max(yMax, mi->Get2dEffect(i)->pos.y); zMin = Min(zMin, mi->Get2dEffect(i)->pos.z); zMax = Max(zMax, mi->Get2dEffect(i)->pos.z); } CVector pos1, pos2; uint8 r, g; int id; switch (phase) { case CAR_LIGHTS_GREEN: r = 0; g = 255; pos1 = ent->GetMatrix() * CVector(x, yMax, zMin); pos2 = ent->GetMatrix() * CVector(x, yMin, zMin); id = 0; break; case CAR_LIGHTS_YELLOW: r = 255; g = 128; pos1 = ent->GetMatrix() * CVector(x, yMax, (zMin + zMax) / 2.0f); pos2 = ent->GetMatrix() * CVector(x, yMin, (zMin + zMax) / 2.0f); id = 1; break; case CAR_LIGHTS_RED: r = 255; g = 0; pos1 = ent->GetMatrix() * CVector(x, yMax, zMax); pos2 = ent->GetMatrix() * CVector(x, yMin, zMax); id = 2; break; default: r = 0; g = 0; pos1 = ent->GetMatrix() * CVector(x, yMax, (zMin + zMax) / 2.0f); pos2 = ent->GetMatrix() * CVector(x, yMin, (zMin + zMax) / 2.0f); id = -1; break; } if (CWeather::TrafficLightBrightness > 0.5f) CPointLights::AddLight(CPointLights::LIGHT_POINT, pos1, CVector(0.0f, 0.0f, 0.0f), 8.0f, r / 255.0f, g / 255.0f, 0 / 255.0f, CPointLights::FOG_NORMAL, true); if (CWeather::TrafficLightBrightness > 0.05f) CShadows::StoreStaticShadow((uintptr)ent, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos1, 8.0f, 0.0f, 0.0f, -8.0f, 128, r * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, g * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, 0 * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, 12.0f, 1.0f, 40.0f, false, 0.0f); if (DotProduct(TheCamera.GetForward(), ent->GetForward()) < 0.0f) CCoronas::RegisterCorona((uintptr)ent + id, r * CTimeCycle::GetSpriteBrightness() * 0.7f, g * CTimeCycle::GetSpriteBrightness() * 0.7f, 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, 255, pos1, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); else CCoronas::RegisterCorona((uintptr)ent + id + 3, r * CTimeCycle::GetSpriteBrightness() * 0.7f, g * CTimeCycle::GetSpriteBrightness() * 0.7f, 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, 255, pos2, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); CBrightLights::RegisterOne(pos1, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); CBrightLights::RegisterOne(pos2, ent->GetUp(), -ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); } else if (MI_TRAFFICLIGHTS_VERTICAL == m) { CBaseModelInfo* mi = CModelInfo::GetModelInfo(ent->GetModelIndex()); float x = mi->Get2dEffect(0)->pos.x; float yMin = mi->Get2dEffect(0)->pos.y; float yMax = mi->Get2dEffect(0)->pos.y; float zMin = mi->Get2dEffect(0)->pos.z; float zMax = mi->Get2dEffect(0)->pos.z; for (i = 1; i < 6; i++) { assert(mi->Get2dEffect(i)); yMin = Min(yMin, mi->Get2dEffect(i)->pos.y); yMax = Max(yMax, mi->Get2dEffect(i)->pos.y); zMin = Min(zMin, mi->Get2dEffect(i)->pos.z); zMax = Max(zMax, mi->Get2dEffect(i)->pos.z); } CVector pos1; uint8 r, g; int id; switch (phase) { case CAR_LIGHTS_GREEN: r = 0; g = 255; pos1 = ent->GetMatrix() * mi->Get2dEffect(2)->pos; id = 0; break; case CAR_LIGHTS_YELLOW: r = 255; g = 128; pos1 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; id = 1; break; case CAR_LIGHTS_RED: r = 255; g = 0; pos1 = ent->GetMatrix() * mi->Get2dEffect(0)->pos; id = 2; break; default: r = 0; g = 0; pos1 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; id = -1; break; } CBrightLights::RegisterOne(pos1, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); if (CWeather::TrafficLightBrightness > 0.5f) CPointLights::AddLight(CPointLights::LIGHT_POINT, pos1, CVector(0.0f, 0.0f, 0.0f), 8.0f, r / 255.0f, g / 255.0f, 0 / 255.0f, CPointLights::FOG_NORMAL, true); if (CWeather::TrafficLightBrightness > 0.05f) CShadows::StoreStaticShadow((uintptr)ent, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos1, 8.0f, 0.0f, 0.0f, -8.0f, 128, r * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, g * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, 0 * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, 12.0f, 1.0f, 40.0f, false, 0.0f); if (DotProduct(TheCamera.GetForward(), ent->GetForward()) < 0.0f) CCoronas::RegisterCorona((uintptr)ent + id, r * CTimeCycle::GetSpriteBrightness() * 0.7f, g * CTimeCycle::GetSpriteBrightness() * 0.7f, 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, 255, pos1, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } else if (MI_TRAFFICLIGHTS_MIAMI == m || MI_TRAFFICLIGHTS_TWOVERTICAL == m) { CBaseModelInfo* mi = CModelInfo::GetModelInfo(ent->GetModelIndex()); CVector pos1, pos2; uint8 r, g; int id; if (MI_TRAFFICLIGHTS_MIAMI == m) { switch (phase) { case CAR_LIGHTS_GREEN: r = 0; g = 255; pos1 = ent->GetMatrix() * mi->Get2dEffect(4)->pos; pos2 = ent->GetMatrix() * mi->Get2dEffect(5)->pos; id = 0; break; case CAR_LIGHTS_YELLOW: r = 255; g = 128; pos1 = ent->GetMatrix() * mi->Get2dEffect(2)->pos; pos2 = ent->GetMatrix() * mi->Get2dEffect(3)->pos; id = 1; break; case CAR_LIGHTS_RED: r = 255; g = 0; pos1 = ent->GetMatrix() * mi->Get2dEffect(0)->pos; pos2 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; id = 2; break; default: r = 0; g = 0; pos1 = ent->GetMatrix() * mi->Get2dEffect(2)->pos; pos2 = ent->GetMatrix() * mi->Get2dEffect(3)->pos; id = -1; break; } } else { switch (phase) { case CAR_LIGHTS_GREEN: r = 0; g = 255; pos1 = ent->GetMatrix() * mi->Get2dEffect(2)->pos; pos2 = ent->GetMatrix() * mi->Get2dEffect(5)->pos; id = 0; break; case CAR_LIGHTS_YELLOW: r = 255; g = 128; pos1 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; pos2 = ent->GetMatrix() * mi->Get2dEffect(4)->pos; id = 1; break; case CAR_LIGHTS_RED: r = 255; g = 0; pos1 = ent->GetMatrix() * mi->Get2dEffect(0)->pos; pos2 = ent->GetMatrix() * mi->Get2dEffect(3)->pos; id = 2; break; default: r = 0; g = 0; pos1 = ent->GetMatrix() * mi->Get2dEffect(1)->pos; pos2 = ent->GetMatrix() * mi->Get2dEffect(4)->pos; id = -1; break; } } CVector pos = (pos1 + pos2) / 2; if (id >= 0) { CBrightLights::RegisterOne(pos1, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); CBrightLights::RegisterOne(pos2, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN); } if (CWeather::TrafficLightBrightness > 0.5f) CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), 8.0f, r / 255.0f, g / 255.0f, 0 / 255.0f, CPointLights::FOG_NORMAL, true); if (CWeather::TrafficLightBrightness > 0.05f) CShadows::StoreStaticShadow((uintptr)ent, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 8.0f, 0.0f, 0.0f, -8.0f, 128, r * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, g * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, 0 * CTimeCycle::GetLightOnGroundBrightness() * CWeather::TrafficLightBrightness / 8.0f, 12.0f, 1.0f, 40.0f, false, 0.0f); if (id >= 0) { if (DotProduct(TheCamera.GetForward(), ent->GetForward()) < 0.0f) CCoronas::RegisterCorona((uintptr)ent + id, r * CTimeCycle::GetSpriteBrightness() * 0.7f, g * CTimeCycle::GetSpriteBrightness() * 0.7f, 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, 255, pos1, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); else CCoronas::RegisterCorona((uintptr)ent + id, r * CTimeCycle::GetSpriteBrightness() * 0.7f, g * CTimeCycle::GetSpriteBrightness() * 0.7f, 0 * CTimeCycle::GetSpriteBrightness() * 0.7f, 255, pos2, 1.75f * CTimeCycle::GetSpriteSize(), 50.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } } } bool DoesLineSegmentIntersect(float l1x1, float l1y1, float l1x2, float l1y2, float l2x1, float l2y1, float l2x2, float l2y2) { return ((l2y2 - l1y1) * (l1x2 - l1x1) + (l1x1 - l2x2) * (l1y2 - l1y1)) * ((l2y1 - l1y1) * (l1x2 - l1x1) + (l1x1 - l2x1) * (l1y2 - l1y1)) <= 0.0f && ((l1y2 - l2y1) * (l2x2 - l2x1) + (l2y2 - l2y1) * (l2x1 - l1x2)) * ((l1y1 - l2y1) * (l2x2 - l2x1) + (l2y2 - l2y1) * (l2x1 - l1x1)) <= 0.0f; } void CTrafficLights::ScanForLightsOnMap(void) { int x, y; int i, j, k, l; CPtrNode *node; for(x = 0; x < NUMSECTORS_X; x++) for(y = 0; y < NUMSECTORS_Y; y++){ CPtrList &list = CWorld::GetSector(x, y)->m_lists[ENTITYLIST_DUMMIES]; for(node = list.first; node; node = node->next){ CEntity *light = (CEntity*)node->item; if (!IsTrafficLight(light->GetModelIndex())) continue; CVector pos1 = light->GetMatrix() * CVector(17.0f, 0.0f, 0.0f); CVector pos2 = light->GetMatrix() * CVector(-15.0f, 0.0f, 0.0f); // Check cars for(i = 0; i < ThePaths.m_numCarPathNodes; i++){ if ((ThePaths.m_pathNodes[i].GetPosition() - pos1).MagnitudeSqr() >= SQR(100.0f)) continue; for (j = 0; j < ThePaths.m_pathNodes[i].numLinks; j++){ int con = ThePaths.ConnectedNode(ThePaths.m_pathNodes[i].firstLink + j); if (i < con) { CVector i_pos = ThePaths.m_pathNodes[i].GetPosition(); CVector con_pos = ThePaths.m_pathNodes[con].GetPosition(); if (Abs(pos1.z - (i_pos.z + con_pos.z) / 2) < 10.0f && DoesLineSegmentIntersect(pos1.x, pos1.y, pos2.x, pos2.y, i_pos.x, i_pos.y, con_pos.x, con_pos.y)) { //debug("Setting up light: nodes %f %f %f - %f %f %f, light %f %f %f - %f %f %f\n", i_pos.x, i_pos.y, i_pos.z, con_pos.x, con_pos.y, con_pos.z, pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z); int link = ThePaths.m_carPathConnections[ThePaths.m_pathNodes[i].firstLink + j]; ThePaths.m_carPathLinks[link].trafficLightType = FindTrafficLightType(light); if (ThePaths.m_pathNodes[i].numLinks > ThePaths.m_pathNodes[con].numLinks) con = i; if (ThePaths.m_carPathLinks[link].pathNodeIndex != con) ThePaths.m_carPathLinks[link].trafficLightDirection = true; } } } } // Check peds for(i = ThePaths.m_numCarPathNodes; i < ThePaths.m_numPathNodes; i++){ float dist1, dist2; dist1 = Abs(ThePaths.m_pathNodes[i].GetX() - light->GetPosition().x) + Abs(ThePaths.m_pathNodes[i].GetY() - light->GetPosition().y); if(dist1 < 50.0f){ for(l = 0; l < ThePaths.m_pathNodes[i].numLinks; l++){ j = ThePaths.m_pathNodes[i].firstLink + l; if(ThePaths.ConnectionCrossesRoad(j)){ k = ThePaths.ConnectedNode(j); dist2 = Abs(ThePaths.m_pathNodes[k].GetX() - light->GetPosition().x) + Abs(ThePaths.m_pathNodes[k].GetY() - light->GetPosition().y); if(dist1 < 15.0f || dist2 < 15.0f) ThePaths.ConnectionSetTrafficLight(j); } } } } } } } bool CTrafficLights::ShouldCarStopForLight(CVehicle *vehicle, bool alwaysStop) { int node, type; bool direction; node = vehicle->AutoPilot.m_nNextPathNodeInfo; type = ThePaths.m_carPathLinks[node].trafficLightType; direction = ThePaths.m_carPathLinks[node].trafficLightDirection; if(type){ if((direction || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nNextRouteNode) && (!direction || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nNextRouteNode)) if(alwaysStop || type == 1 && LightForCars1() != CAR_LIGHTS_GREEN || type == 2 && LightForCars2() != CAR_LIGHTS_GREEN){ float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(), ThePaths.m_carPathLinks[node].GetDirection()); if(vehicle->AutoPilot.m_nNextDirection == -1){ if(dist > 0.0f && dist < 8.0f) return true; }else{ if(dist < 0.0f && dist > -8.0f) return true; } } } node = vehicle->AutoPilot.m_nCurrentPathNodeInfo; type = ThePaths.m_carPathLinks[node].trafficLightType; direction = ThePaths.m_carPathLinks[node].trafficLightDirection; if(type){ if((direction || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nCurrentRouteNode) && (!direction || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nCurrentRouteNode)) if(alwaysStop || type == 1 && LightForCars1() != CAR_LIGHTS_GREEN || type == 2 && LightForCars2() != CAR_LIGHTS_GREEN){ float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(), ThePaths.m_carPathLinks[node].GetDirection()); if(vehicle->AutoPilot.m_nCurrentDirection == -1){ if(dist > 0.0f && dist < 8.0f) return true; }else{ if(dist < 0.0f && dist > -8.0f) return true; } } } if(vehicle->GetStatus() == STATUS_PHYSICS){ node = vehicle->AutoPilot.m_nPreviousPathNodeInfo; type = ThePaths.m_carPathLinks[node].trafficLightType; direction = ThePaths.m_carPathLinks[node].trafficLightDirection; if(type){ if((direction || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nPrevRouteNode) && (!direction || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nPrevRouteNode)) if(alwaysStop || type == 1 && LightForCars1() != CAR_LIGHTS_GREEN || type == 2 && LightForCars2() != CAR_LIGHTS_GREEN){ float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(), ThePaths.m_carPathLinks[node].GetDirection()); if(vehicle->AutoPilot.m_nPreviousDirection == -1){ if(dist > 0.0f && dist < 6.0f) return true; }else{ if(dist < 0.0f && dist > -6.0f) return true; } } } } return false; } bool CTrafficLights::ShouldCarStopForBridge(CVehicle *vehicle) { #ifdef GTA_BRIDGE return ThePaths.m_carPathLinks[vehicle->AutoPilot.m_nNextPathNodeInfo].bBridgeLights && !ThePaths.m_carPathLinks[vehicle->AutoPilot.m_nCurrentPathNodeInfo].bBridgeLights; #else return false; #endif } int CTrafficLights::FindTrafficLightType(CEntity *light) { float orientation = RADTODEG(CGeneral::GetATanOfXY(light->GetForward().x, light->GetForward().y)); if((orientation > 60.0f && orientation < 60.0f + 90.0f) || (orientation > 240.0f && orientation < 240.0f + 90.0f)) return 1; return 2; } uint8 CTrafficLights::LightForPeds(void) { uint32 period = CTimer::GetTimeInMilliseconds() % 16384; if(period < 12000) return PED_LIGHTS_DONT_WALK; else if(period < 16384 - 1000) return PED_LIGHTS_WALK; else return PED_LIGHTS_WALK_BLINK; } uint8 CTrafficLights::LightForCars1(void) { if (CWeather::Wind > 1.1f) return CAR_LIGHTS_GREEN; if (bGreenLightsCheat) return CAR_LIGHTS_GREEN; uint32 period = CTimer::GetTimeInMilliseconds() % 16384; if(period < 5000) return CAR_LIGHTS_GREEN; else if(period < 5000 + 1000) return CAR_LIGHTS_YELLOW; else return CAR_LIGHTS_RED; } uint8 CTrafficLights::LightForCars2(void) { if (CWeather::Wind > 1.1f) return CAR_LIGHTS_GREEN; if (bGreenLightsCheat) return CAR_LIGHTS_GREEN; uint32 period = CTimer::GetTimeInMilliseconds() % 16384; if(period < 6000) return CAR_LIGHTS_RED; else if(period < 12000 - 1000) return CAR_LIGHTS_GREEN; else if(period < 12000) return CAR_LIGHTS_YELLOW; else return CAR_LIGHTS_RED; } uint8 CTrafficLights::LightForCars1_Visual(void) { if (CWeather::Wind <= 1.1f) return LightForCars1(); return (CTimer::GetTimeInMilliseconds() & 0x400 ? CAR_LIGHTS_NONE : CAR_LIGHTS_YELLOW); } uint8 CTrafficLights::LightForCars2_Visual(void) { if (CWeather::Wind <= 1.1f) return LightForCars2(); return (CTimer::GetTimeInMilliseconds() & 0x400 ? CAR_LIGHTS_NONE : CAR_LIGHTS_YELLOW); } ================================================ FILE: src/control/TrafficLights.h ================================================ #pragma once class CEntity; class CVehicle; enum { PED_LIGHTS_WALK, PED_LIGHTS_WALK_BLINK, PED_LIGHTS_DONT_WALK, CAR_LIGHTS_GREEN = 0, CAR_LIGHTS_YELLOW, CAR_LIGHTS_RED, CAR_LIGHTS_NONE }; class CTrafficLights { public: static bool bGreenLightsCheat; static void DisplayActualLight(CEntity *ent); static void ScanForLightsOnMap(void); static int FindTrafficLightType(CEntity *light); static uint8 LightForPeds(void); static uint8 LightForCars1(void); static uint8 LightForCars2(void); static uint8 LightForCars1_Visual(void); static uint8 LightForCars2_Visual(void); static bool ShouldCarStopForLight(CVehicle*, bool); static bool ShouldCarStopForBridge(CVehicle*); }; ================================================ FILE: src/core/Accident.cpp ================================================ #include "common.h" #include "Accident.h" #include "Ped.h" #include "Pools.h" #include "World.h" CAccidentManager gAccidentManager; CAccident* CAccidentManager::GetNextFreeAccident() { for (int i = 0; i < NUM_ACCIDENTS; i++) { if (m_aAccidents[i].m_pVictim == nil) return &m_aAccidents[i]; } return nil; } void CAccidentManager::ReportAccident(CPed *ped) { if (!ped->IsPlayer() && ped->CharCreatedBy != MISSION_CHAR && !ped->bRenderScorched && !ped->bBodyPartJustCameOff && ped->bAllowMedicsToReviveMe && !ped->bIsInWater) { for (int i = 0; i < NUM_ACCIDENTS; i++) { if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_pVictim == ped) return; } if (ped->m_pCurrentPhysSurface == nil) { CVector point = ped->GetPosition(); point.z -= 2.0f; CColPoint colPoint; CEntity *pEntity; if (!CWorld::ProcessVerticalLine(point, -100.0f, colPoint, pEntity, true, false, false, false, false, false, nil)) { CAccident *accident = GetNextFreeAccident(); if (accident != nil) { accident->m_pVictim = ped; ped->RegisterReference((CEntity**)&accident->m_pVictim); accident->m_nMedicsPerformingCPR = 0; accident->m_nMedicsAttending = 0; ped->m_lastAccident = accident; WorkToDoForMedics(); } } } } } void CAccidentManager::Update() { #ifdef SQUEEZE_PERFORMANCE // Handled after injury registered. return; #endif int32 e; if (CEventList::GetEvent(EVENT_INJURED_PED, &e)) { CPed *ped = CPools::GetPed(gaEvent[e].entityRef); if (ped) { ReportAccident(ped); CEventList::ClearEvent(e); } } } CAccident* CAccidentManager::FindNearestAccident(CVector vecPos, float *pDistance) { for (int i = 0; i < MAX_MEDICS_TO_ATTEND_ACCIDENT; i++){ int accidentId = -1; float minDistance = 999999; for (int j = 0; j < NUM_ACCIDENTS; j++){ CPed* pVictim = m_aAccidents[j].m_pVictim; if (!pVictim) continue; if (pVictim->CharCreatedBy == MISSION_CHAR) continue; if (pVictim->m_fHealth != 0.0f) continue; if (m_aAccidents[j].m_nMedicsPerformingCPR != i) continue; float distance = (pVictim->GetPosition() - vecPos).Magnitude2D(); if (distance / 2 > pVictim->GetPosition().z - vecPos.z && distance < minDistance){ minDistance = distance; accidentId = j; } } *pDistance = minDistance; if (accidentId != -1) return &m_aAccidents[accidentId]; } return nil; } uint16 CAccidentManager::CountActiveAccidents() { uint16 accidents = 0; for (int i = 0; i < NUM_ACCIDENTS; i++) { if (m_aAccidents[i].m_pVictim) accidents++; } return accidents; } bool CAccidentManager::WorkToDoForMedics() { for (int i = 0; i < NUM_ACCIDENTS; i++) { if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_nMedicsAttending < MAX_MEDICS_TO_ATTEND_ACCIDENT) return true; } return false; } bool CAccidentManager::UnattendedAccidents() { for (int i = 0; i < NUM_ACCIDENTS; i++) { if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_nMedicsAttending == 0) return true; } return false; } ================================================ FILE: src/core/Accident.h ================================================ #pragma once #include "config.h" class CPed; class CAccident { public: CPed *m_pVictim; uint32 m_nMedicsAttending; uint32 m_nMedicsPerformingCPR; CAccident() : m_pVictim(nil), m_nMedicsAttending(0), m_nMedicsPerformingCPR(0) {} }; class CAccidentManager { CAccident m_aAccidents[NUM_ACCIDENTS]; enum { MAX_MEDICS_TO_ATTEND_ACCIDENT = 2 }; public: CAccident *GetNextFreeAccident(); void ReportAccident(CPed *ped); void Update(); CAccident *FindNearestAccident(CVector vecPos, float *pDistance); uint16 CountActiveAccidents(); bool UnattendedAccidents(); bool WorkToDoForMedics(); }; extern CAccidentManager gAccidentManager; ================================================ FILE: src/core/AnimViewer.cpp ================================================ #include "common.h" #include "Font.h" #include "Pad.h" #include "Text.h" #include "main.h" #include "Timer.h" #include "DMAudio.h" #include "FileMgr.h" #include "Streaming.h" #include "TxdStore.h" #include "General.h" #include "Camera.h" #include "Vehicle.h" #include "Bike.h" #include "PlayerSkin.h" #include "PlayerInfo.h" #include "World.h" #include "Renderer.h" #include "AnimManager.h" #include "AnimBlendAssocGroup.h" #include "AnimViewer.h" #include "PlayerPed.h" #include "Pools.h" #include "References.h" #include "PathFind.h" #include "HandlingMgr.h" #include "TempColModels.h" #include "Particle.h" #include "CdStream.h" #include "Messages.h" #include "CarCtrl.h" #include "FileLoader.h" #include "ModelIndices.h" #include "Clock.h" #include "Timecycle.h" #include "RpAnimBlend.h" #include "AnimBlendAssociation.h" #include "Shadows.h" #include "Radar.h" #include "Hud.h" #include "debugmenu.h" int CAnimViewer::animTxdSlot = 0; CEntity *CAnimViewer::pTarget = nil; void CAnimViewer::Render(void) { if (pTarget) { if (pTarget) { #ifdef FIX_BUGS if(pTarget->IsPed()) ((CPed*)pTarget)->UpdateRpHAnim(); #endif pTarget->Render(); CRenderer::RenderOneNonRoad(pTarget); } } } void CAnimViewer::Initialise(void) { // we need messages, messages needs hud, hud needs those int hudSlot = CTxdStore::AddTxdSlot("hud"); CTxdStore::LoadTxd(hudSlot, "MODELS/HUD.TXD"); CHud::m_Wants_To_Draw_Hud = false; animTxdSlot = CTxdStore::AddTxdSlot("generic"); CTxdStore::Create(animTxdSlot); int particleSlot = CTxdStore::AddTxdSlot("particle"); CTxdStore::LoadTxd(particleSlot, "MODELS/PARTICLE.TXD"); CTxdStore::SetCurrentTxd(animTxdSlot); CPools::Initialise(); CReferences::Init(); TheCamera.Init(); TheCamera.SetRwCamera(Scene.camera); TheCamera.Cams[TheCamera.ActiveCam].Distance = 5.0f; ThePaths.Init(); ThePaths.AllocatePathFindInfoMem(4500); CCollision::Init(); CWorld::Initialise(); mod_HandlingManager.Initialise(); CTempColModels::Initialise(); CAnimManager::Initialise(); CModelInfo::Initialise(); CParticle::Initialise(); CCarCtrl::Init(); CPedStats::Initialise(); CMessages::Init(); CdStreamAddImage("MODELS\\GTA3.IMG"); CFileLoader::LoadLevel("DATA\\DEFAULT.DAT"); CFileLoader::LoadLevel("DATA\\ANIMVIEWER.DAT"); CStreaming::Init(); for(int i = 0; i < MODELINFOSIZE; i++) if(CModelInfo::GetModelInfo(i)) CModelInfo::GetModelInfo(i)->ConvertAnimFileIndex(); CStreaming::LoadInitialPeds(); CStreaming::RequestSpecialModel(MI_PLAYER, "player", STREAMFLAGS_DONT_REMOVE); CStreaming::LoadAllRequestedModels(false); CRenderer::Init(); CVehicleModelInfo::LoadVehicleColours(); #ifdef FIX_BUGS CVehicleModelInfo::LoadEnvironmentMaps(); #endif CAnimManager::LoadAnimFiles(); CWorld::PlayerInFocus = 0; CWeapon::InitialiseWeapons(); CPed::Initialise(); CTimer::Initialise(); CClock::Initialise(60000); CTimeCycle::Initialise(); CCarCtrl::Init(); CPlayerPed *player = new CPlayerPed(); player->SetPosition(1000.0f, 1000.0f, 1000.0f); CWorld::Players[0].m_pPed = player; CDraw::SetFOV(120.0f); CDraw::ms_fLODDistance = 500.0f; int fd = CFileMgr::OpenFile("DATA\\SPECIAL.TXT", "r"); char animGroup[32], modelName[32]; if (fd) { for (int lineId = 0; lineId < NUM_OF_SPECIAL_CHARS; lineId++) { if (!CFileMgr::ReadLine(fd, gString, 255)) break; sscanf(gString, "%s %s", modelName, animGroup); int groupId; for (groupId = 0; groupId < NUM_ANIM_ASSOC_GROUPS; groupId++) { if (!strcmp(animGroup, CAnimManager::GetAnimGroupName((AssocGroupId)groupId))) break; } if (groupId != NUM_ANIM_ASSOC_GROUPS) ((CPedModelInfo*)CModelInfo::GetModelInfo(MI_SPECIAL01 + lineId))->m_animGroup = groupId; CStreaming::RequestSpecialChar(lineId, modelName, STREAMFLAGS_DONT_REMOVE); } CFileMgr::CloseFile(fd); } else { // TODO? maybe request some special models here so the thing doesn't crash } // From LCS. idk if needed int vanBlock = CAnimManager::GetAnimationBlockIndex("van"); int bikesBlock = CAnimManager::GetAnimationBlockIndex("bikes"); int bikevBlock = CAnimManager::GetAnimationBlockIndex("bikev"); int bikehBlock = CAnimManager::GetAnimationBlockIndex("bikeh"); int bikedBlock = CAnimManager::GetAnimationBlockIndex("biked"); CStreaming::FlushRequestList(); CStreaming::RequestAnim(vanBlock, STREAMFLAGS_DEPENDENCY); CStreaming::RequestAnim(bikesBlock, STREAMFLAGS_DEPENDENCY); CStreaming::RequestAnim(bikevBlock, STREAMFLAGS_DEPENDENCY); CStreaming::RequestAnim(bikehBlock, STREAMFLAGS_DEPENDENCY); CStreaming::RequestAnim(bikedBlock, STREAMFLAGS_DEPENDENCY); CStreaming::LoadAllRequestedModels(false); CAnimManager::AddAnimBlockRef(vanBlock); CAnimManager::AddAnimBlockRef(bikesBlock); CAnimManager::AddAnimBlockRef(bikevBlock); CAnimManager::AddAnimBlockRef(bikehBlock); CAnimManager::AddAnimBlockRef(bikedBlock); } int LastPedModelId(int modelId) { CBaseModelInfo *model; for(;;){ assert(modelId < MODELINFOSIZE); model = CModelInfo::GetModelInfo(modelId); if (model && model->GetModelType() == MITYPE_PED) break; modelId--; } return modelId; } int FirstCarModelId(int modelId) { CBaseModelInfo *model; for(;;){ assert(modelId < MODELINFOSIZE); model = CModelInfo::GetModelInfo(modelId); if (model && model->GetModelType() == MITYPE_VEHICLE) break; modelId++; } return modelId; } int NextModelId(int modelId, int wantedChange) { // Max. 2 trials wasn't here, it's me that added it. int tryCount = 2; int ogModelId = modelId; while(tryCount != 0) { modelId += wantedChange; if (modelId < 0 || modelId >= MODELINFOSIZE) { tryCount--; wantedChange = -wantedChange; } else if (modelId != 5 && modelId != 6 && modelId != 405) { CBaseModelInfo *model = CModelInfo::GetModelInfo(modelId); if (model) { //int type = model->m_type; return modelId; } } } return ogModelId; } void PlayAnimation(RpClump *clump, AssocGroupId animGroup, AnimationId anim) { CAnimBlendAssociation *currentAssoc = RpAnimBlendClumpGetAssociation(clump, anim); if (currentAssoc && currentAssoc->IsPartial()) delete currentAssoc; RpAnimBlendClumpSetBlendDeltas(clump, ASSOC_PARTIAL, -8.0f); CAnimBlendAssociation *animAssoc = CAnimManager::BlendAnimation(clump, animGroup, anim, 8.0f); animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc->SetCurrentTime(0.0f); animAssoc->SetRun(); } void CAnimViewer::Update(void) { static int modelId = 0; static int animId = 0; static bool reloadIFP = false; AssocGroupId animGroup = ASSOCGRP_STD; int nextModelId = modelId; CBaseModelInfo *modelInfo = CModelInfo::GetModelInfo(modelId); if (modelInfo->GetModelType() == MITYPE_PED) { int animGroup = ((CPedModelInfo*)modelInfo)->m_animGroup; if (animId > ANIM_STD_IDLE) animGroup = ASSOCGRP_STD; if (reloadIFP) { if (pTarget) { CWorld::Remove(pTarget); if (pTarget) delete pTarget; } pTarget = nil; // These calls were inside of LoadIFP function. CAnimManager::Shutdown(); CAnimManager::Initialise(); CAnimManager::LoadAnimFiles(); reloadIFP = false; } } else { animGroup = ASSOCGRP_STD; } CPad::UpdatePads(); CPad* pad = CPad::GetPad(0); #ifdef DEBUGMENU DebugMenuProcess(); #endif CStreaming::UpdateForAnimViewer(); CStreaming::RequestModel(modelId, 0); if (CStreaming::HasModelLoaded(modelId)) { if (!pTarget) { if (modelInfo->GetModelType() == MITYPE_VEHICLE) { CVehicleModelInfo* veh = (CVehicleModelInfo*)modelInfo; if (veh->m_vehicleType == VEHICLE_TYPE_CAR) { pTarget = new CAutomobile(modelId, RANDOM_VEHICLE); } else if (veh->m_vehicleType == VEHICLE_TYPE_BOAT) { pTarget = new CBoat(modelId, RANDOM_VEHICLE); } else if (veh->m_vehicleType == VEHICLE_TYPE_BIKE) { pTarget = new CBike(modelId, RANDOM_VEHICLE); } else { pTarget = new CObject(modelId, true); if (!modelInfo->GetColModel()) { modelInfo->SetColModel(&CTempColModels::ms_colModelWheel1); } } pTarget->SetStatus(STATUS_ABANDONED); } else if (modelInfo->GetModelType() == MITYPE_PED) { pTarget = new CPed(PEDTYPE_CIVMALE); pTarget->SetModelIndex(modelId); } else { pTarget = new CObject(modelId, true); if (!modelInfo->GetColModel()) { modelInfo->SetColModel(&CTempColModels::ms_colModelWheel1); } pTarget->SetStatus(STATUS_ABANDONED); } pTarget->SetPosition(0.0f, 0.0f, 0.0f); CWorld::Add(pTarget); TheCamera.TakeControl(pTarget, CCam::MODE_MODELVIEW, JUMP_CUT, CAMCONTROL_SCRIPT); } if (pTarget->IsVehicle() || pTarget->IsPed() || pTarget->IsObject()) { ((CPhysical*)pTarget)->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); } #ifdef FIX_BUGS // so we don't end up in the water pTarget->GetMatrix().GetPosition().z = 10.0f; #else pTarget->GetMatrix().GetPosition().z = 0.0f; #endif if (modelInfo->GetModelType() == MITYPE_PED) { ((CPed*)pTarget)->bKindaStayInSamePlace = true; // Triangle in mobile if (pad->GetSquareJustDown()) { reloadIFP = true; AsciiToUnicode("IFP reloaded", gUString); CMessages::AddMessage(gUString, 1000, 0); } else if (pad->GetCrossJustDown()) { PlayAnimation(pTarget->GetClump(), animGroup, (AnimationId)animId); AsciiToUnicode("Animation restarted", gUString); CMessages::AddMessage(gUString, 1000, 0); } else if (pad->GetCircleJustDown()) { PlayAnimation(pTarget->GetClump(), animGroup, ANIM_STD_IDLE); AsciiToUnicode("Idle animation playing", gUString); CMessages::AddMessage(gUString, 1000, 0); } else if (pad->GetDPadUpJustDown()) { animId--; if (animId < 0) { animId = ANIM_STD_NUM - 1; } PlayAnimation(pTarget->GetClump(), animGroup, (AnimationId)animId); sprintf(gString, "Current anim: %d", animId); AsciiToUnicode(gString, gUString); CMessages::AddMessage(gUString, 1000, 0); } else if (pad->GetDPadDownJustDown()) { animId = (animId == (ANIM_STD_NUM - 1) ? 0 : animId + 1); PlayAnimation(pTarget->GetClump(), animGroup, (AnimationId)animId); sprintf(gString, "Current anim: %d", animId); AsciiToUnicode(gString, gUString); CMessages::AddMessage(gUString, 1000, 0); } else if (pad->GetStartJustDown()) { } else if (pad->GetLeftShoulder1JustDown()) { nextModelId = FirstCarModelId(modelId); AsciiToUnicode("Switched to vehicles", gUString); CMessages::AddMessage(gUString, 1000, 0); // Originally it was GetPad(1)->LeftShoulder2 } else if (pad->NewState.Triangle) { ((CPedModelInfo *)CModelInfo::GetModelInfo(pTarget->GetModelIndex()))->AnimatePedColModelSkinned(pTarget->GetClump()); AsciiToUnicode("Ped Col model will be animated as long as you hold the button", gUString); CMessages::AddMessage(gUString, 100, 0); } // From LCS if (CAnimManager::GetAnimAssocGroups()[animGroup].numAssociations <= animId) animId = 0; } else if (modelInfo->GetModelType() == MITYPE_VEHICLE) { if (pad->GetLeftShoulder1JustDown()) { nextModelId = LastPedModelId(modelId); AsciiToUnicode("Switched to peds", gUString); CMessages::AddMessage(gUString, 1000, 0); // Start in mobile } else if (pad->GetSquareJustDown()) { CVehicleModelInfo::LoadVehicleColours(); AsciiToUnicode("Carcols.dat reloaded", gUString); CMessages::AddMessage(gUString, 1000, 0); } } } if (pad->GetDPadLeftJustDown()) { nextModelId = NextModelId(modelId, -1); sprintf(gString, "Current model ID: %d", nextModelId); AsciiToUnicode(gString, gUString); CMessages::AddMessage(gUString, 1000, 0); } else if (pad->GetDPadRightJustDown()) { nextModelId = NextModelId(modelId, 1); sprintf(gString, "Current model ID: %d", nextModelId); AsciiToUnicode(gString, gUString); CMessages::AddMessage(gUString, 1000, 0); } // There were extra codes here to let us change model id by 50, but xbox CPad struct is different, so I couldn't port. if (nextModelId != modelId) { modelId = nextModelId; if (pTarget) { CWorld::Remove(pTarget); if (pTarget) delete pTarget; } pTarget = nil; return; } CTimeCycle::Update(); CWorld::Process(); if (pTarget) TheCamera.Process(); } void CAnimViewer::Shutdown(void) { if (CWorld::Players[0].m_pPed) delete CWorld::Players[0].m_pPed; CWorld::ShutDown(); CModelInfo::ShutDown(); CAnimManager::Shutdown(); CTimer::Shutdown(); CStreaming::Shutdown(); CTxdStore::RemoveTxdSlot(animTxdSlot); } ================================================ FILE: src/core/AnimViewer.h ================================================ #pragma once class CAnimViewer { public: static int animTxdSlot; static CEntity *pTarget; static void Initialise(); static void Render(); static void Shutdown(); static void Update(); }; ================================================ FILE: src/core/Cam.cpp ================================================ #include "common.h" #include "main.h" #include "Draw.h" #include "World.h" #include "Vehicle.h" #include "Automobile.h" #include "Boat.h" #include "Bones.h" #include "Ped.h" #include "PlayerPed.h" #include "CopPed.h" #include "RpAnimBlend.h" #include "ControllerConfig.h" #include "Pad.h" #include "Frontend.h" #include "General.h" #include "Timecycle.h" #include "Renderer.h" #include "Shadows.h" #include "Hud.h" #include "ZoneCull.h" #include "SurfaceTable.h" #include "WaterLevel.h" #include "MBlur.h" #include "SceneEdit.h" #include "Debug.h" #include "Camera.h" #include "DMAudio.h" #include "Bike.h" #include "Pickups.h" bool PrintDebugCode = false; int16 DebugCamMode; extern float fRangePlayerRadius; extern float fCloseNearClipLimit; #ifdef FREE_CAM bool CCamera::bFreeCam = false; int nPreviousMode = -1; #endif void CCam::Init(void) { Mode = MODE_FOLLOWPED; Front = CVector(0.0f, 0.0f, -1.0f); Up = CVector(0.0f, 0.0f, 1.0f); Rotating = false; m_iDoCollisionChecksOnFrameNum = 1; m_iDoCollisionCheckEveryNumOfFrames = 9; m_iFrameNumWereAt = 0; m_bCollisionChecksOn = false; m_fRealGroundDist = 0.0f; BetaSpeed = 0.0f; AlphaSpeed = 0.0f; DistanceSpeed = 0.0f; f_max_role_angle = DEGTORAD(5.0f); Distance = 30.0f; DistanceSpeed = 0.0f; m_pLastCarEntered = nil; m_pLastPedLookedAt = nil; ResetStatics = true; Beta = 0.0f; m_fTilt = 0.0f; m_fTiltSpeed = 0.0f; m_bFixingBeta = false; CA_MIN_DISTANCE = 0.0f; CA_MAX_DISTANCE = 0.0f; LookingBehind = false; LookingLeft = false; LookingRight = false; m_fPlayerInFrontSyphonAngleOffSet = DEGTORAD(20.0f); m_fSyphonModeTargetZOffSet = 0.5f; m_fRadiusForDead = 1.5f; DirectionWasLooking = LOOKING_FORWARD; LookBehindCamWasInFront = false; f_Roll = 0.0f; f_rollSpeed = 0.0f; m_fCloseInPedHeightOffset = 0.0f; m_fCloseInPedHeightOffsetSpeed = 0.0f; m_fCloseInCarHeightOffset = 0.0f; m_fCloseInCarHeightOffsetSpeed = 0.0f; m_fPedBetweenCameraHeightOffset = 0.0f; m_fTargetBeta = 0.0f; m_fBufferedTargetBeta = 0.0f; m_fBufferedTargetOrientation = 0.0f; m_fBufferedTargetOrientationSpeed = 0.0f; m_fDimensionOfHighestNearCar = 0.0f; } float PLAYERPED_LEVEL_SMOOTHING_CONST_INV = 0.6f; float PLAYERPED_TREND_SMOOTHING_CONST_INV = 0.8f; void CCam::Process(void) { CVector CameraTarget; float TargetSpeedVar = 0.0f; float TargetOrientation = 0.0f; static CVector SmoothedPos(0.0f, 0.0f, 10000.0f); static CVector SmoothedSpeed(0.0f, 0.0f, 0.0f); if(CamTargetEntity == nil) CamTargetEntity = TheCamera.pTargetEntity; m_iFrameNumWereAt++; if(m_iFrameNumWereAt > m_iDoCollisionCheckEveryNumOfFrames) m_iFrameNumWereAt = 1; m_bCollisionChecksOn = m_iFrameNumWereAt == m_iDoCollisionChecksOnFrameNum; if(m_bCamLookingAtVector){ CameraTarget = m_cvecCamFixedModeVector; }else if(CamTargetEntity->IsVehicle()){ CameraTarget = CamTargetEntity->GetPosition(); if(CamTargetEntity->GetForward().x == 0.0f && CamTargetEntity->GetForward().y == 0.0f) TargetOrientation = 0.0f; else TargetOrientation = CGeneral::GetATanOfXY(CamTargetEntity->GetForward().x, CamTargetEntity->GetForward().y); CVector Fwd(0.0f, 0.0f, 0.0f); Fwd.x = CamTargetEntity->GetForward().x; Fwd.y = CamTargetEntity->GetForward().y; Fwd.Normalise(); float FwdLength = Fwd.Magnitude2D(); if(FwdLength != 0.0f){ Fwd.x /= FwdLength; Fwd.y /= FwdLength; } float FwdSpeedX = ((CVehicle*)CamTargetEntity)->GetMoveSpeed().x * Fwd.x; float FwdSpeedY = ((CVehicle*)CamTargetEntity)->GetMoveSpeed().y * Fwd.y; if(FwdSpeedX + FwdSpeedY > 0.0f) TargetSpeedVar = Min(Sqrt(SQR(FwdSpeedX) + SQR(FwdSpeedY))/0.9f, 1.0f); else TargetSpeedVar = -Min(Sqrt(SQR(FwdSpeedX) + SQR(FwdSpeedY))/1.8f, 0.5f); SpeedVar = 0.895f*SpeedVar + 0.105*TargetSpeedVar; }else{ if(CamTargetEntity == FindPlayerPed()){ // Some fancy smoothing of player position and speed float LevelSmoothing = 1.0f - Pow(PLAYERPED_LEVEL_SMOOTHING_CONST_INV, CTimer::GetTimeStep()); float TrendSmoothing = 1.0f - Pow(PLAYERPED_TREND_SMOOTHING_CONST_INV, CTimer::GetTimeStep()); CVector NewSmoothedPos, NewSmoothedSpeed; if((SmoothedPos - CamTargetEntity->GetPosition()).MagnitudeSqr() > SQR(3.0f) || CTimer::GetTimeStep() < 0.2f || Using3rdPersonMouseCam()){ // Reset values NewSmoothedPos = CamTargetEntity->GetPosition(); NewSmoothedSpeed = CVector(0.0f, 0.0f, 0.0f); }else{ NewSmoothedPos = LevelSmoothing*CamTargetEntity->GetPosition() + (1.0f-LevelSmoothing)*(SmoothedPos + SmoothedSpeed*CTimer::GetTimeStep()); NewSmoothedSpeed = TrendSmoothing*(NewSmoothedPos-SmoothedPos)/CTimer::GetTimeStep() + (1.0f-TrendSmoothing)*SmoothedSpeed; } CameraTarget = NewSmoothedPos; SmoothedPos = NewSmoothedPos; SmoothedSpeed = NewSmoothedSpeed; }else CameraTarget = CamTargetEntity->GetPosition(); if(CamTargetEntity->GetForward().x == 0.0f && CamTargetEntity->GetForward().y == 0.0f) TargetOrientation = 0.0f; else TargetOrientation = CGeneral::GetATanOfXY(CamTargetEntity->GetForward().x, CamTargetEntity->GetForward().y); TargetSpeedVar = 0.0f; SpeedVar = 0.0f; } switch(Mode){ case MODE_TOPDOWN: case MODE_GTACLASSIC: // Process_TopDown(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_BEHINDCAR: Process_BehindCar(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_FOLLOWPED: #ifdef PC_PLAYER_CONTROLS if(CCamera::m_bUseMouse3rdPerson) Process_FollowPedWithMouse(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); else #endif #ifdef FREE_CAM if(CCamera::bFreeCam) Process_FollowPed_Rotation(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); else #endif Process_FollowPed(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; // case MODE_AIMING: case MODE_DEBUG: Process_Debug(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_SNIPER: case MODE_CAMERA: Process_Sniper(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_ROCKETLAUNCHER: Process_Rocket(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_MODELVIEW: Process_ModelView(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; // case MODE_BILL: case MODE_SYPHON: Process_Syphon(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_CIRCLE: // Process_Circle(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; // case MODE_CHEESYZOOM: case MODE_WHEELCAM: Process_WheelCam(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_FIXED: Process_Fixed(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_1STPERSON: Process_1stPerson(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_FLYBY: Process_FlyBy(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_CAM_ON_A_STRING: #ifdef FREE_CAM if(CCamera::bFreeCam) Process_FollowCar_SA(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); else #endif Process_Cam_On_A_String(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; // case MODE_REACTION: // case MODE_FOLLOW_PED_WITH_BIND: // case MODE_CHRIS: case MODE_BEHINDBOAT: #ifdef FREE_CAM if (CCamera::bFreeCam) Process_FollowCar_SA(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); else #endif Process_BehindBoat(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_PLAYER_FALLEN_WATER: Process_Player_Fallen_Water(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; // case MODE_CAM_ON_TRAIN_ROOF: // case MODE_CAM_RUNNING_SIDE_TRAIN: // case MODE_BLOOD_ON_THE_TRACKS: // case MODE_IM_THE_PASSENGER_WOOWOO: case MODE_SYPHON_CRIM_IN_FRONT: Process_Syphon_Crim_In_Front(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_PED_DEAD_BABY: ProcessPedsDeadBaby(); break; // case MODE_PILLOWS_PAPS: // case MODE_LOOK_AT_CARS: case MODE_ARRESTCAM_ONE: ProcessArrestCamOne(); break; case MODE_ARRESTCAM_TWO: ProcessArrestCamTwo(); break; case MODE_M16_1STPERSON: case MODE_HELICANNON_1STPERSON: Process_M16_1stPerson(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_SPECIAL_FIXED_FOR_SYPHON: Process_SpecialFixedForSyphon(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_FIGHT_CAM: Process_Fight_Cam(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_LIGHTHOUSE: Process_LightHouse(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_TOP_DOWN_PED: // Process_TopDownPed(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; case MODE_SNIPER_RUNABOUT: case MODE_ROCKETLAUNCHER_RUNABOUT: case MODE_1STPERSON_RUNABOUT: case MODE_M16_1STPERSON_RUNABOUT: case MODE_FIGHT_CAM_RUNABOUT: Process_1rstPersonPedOnPC(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; #ifdef GTA_SCENE_EDIT case MODE_EDITOR: Process_Editor(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar); break; #endif default: Source = CVector(0.0f, 0.0f, 0.0f); Front = CVector(0.0f, 1.0f, 0.0f); Up = CVector(0.0f, 0.0f, 1.0f); } #ifdef FREE_CAM nPreviousMode = Mode; #endif CVector TargetToCam = Source - m_cvecTargetCoorsForFudgeInter; float DistOnGround = TargetToCam.Magnitude2D(); m_fTrueBeta = CGeneral::GetATanOfXY(TargetToCam.x, TargetToCam.y); m_fTrueAlpha = CGeneral::GetATanOfXY(DistOnGround, TargetToCam.z); if(TheCamera.m_uiTransitionState == 0) KeepTrackOfTheSpeed(Source, m_cvecTargetCoorsForFudgeInter, Up, m_fTrueAlpha, m_fTrueBeta, FOV); // Look Behind, Left, Right LookingBehind = false; LookingLeft = false; LookingRight = false; SourceBeforeLookBehind = Source; if(&TheCamera.Cams[TheCamera.ActiveCam] == this){ if((Mode == MODE_CAM_ON_A_STRING || Mode == MODE_1STPERSON || Mode == MODE_BEHINDBOAT || Mode == MODE_BEHINDCAR) && CamTargetEntity->IsVehicle()){ bool bDisableLR = CamTargetEntity && (((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || CamTargetEntity->GetModelIndex() == MI_RCBARON); if(CPad::GetPad(0)->GetLookBehindForCar()){ LookBehind(); if(DirectionWasLooking != LOOKING_BEHIND) TheCamera.m_bJust_Switched = true; DirectionWasLooking = LOOKING_BEHIND; }else if(bDisableLR){ if(DirectionWasLooking != LOOKING_FORWARD) TheCamera.m_bJust_Switched = true; DirectionWasLooking = LOOKING_FORWARD; }else if(CPad::GetPad(0)->GetLookLeft()){ LookLeft(); if(DirectionWasLooking != LOOKING_LEFT) TheCamera.m_bJust_Switched = true; DirectionWasLooking = LOOKING_LEFT; }else if(CPad::GetPad(0)->GetLookRight()){ LookRight(); if(DirectionWasLooking != LOOKING_RIGHT) TheCamera.m_bJust_Switched = true; DirectionWasLooking = LOOKING_RIGHT; }else{ if(DirectionWasLooking != LOOKING_FORWARD) TheCamera.m_bJust_Switched = true; DirectionWasLooking = LOOKING_FORWARD; } } if(Mode == MODE_FOLLOWPED && CamTargetEntity->IsPed()){ if(CPad::GetPad(0)->GetLookBehindForPed()){ LookBehind(); if(DirectionWasLooking != LOOKING_BEHIND) TheCamera.m_bJust_Switched = true; DirectionWasLooking = LOOKING_BEHIND; }else DirectionWasLooking = LOOKING_FORWARD; } } if(Mode == MODE_SNIPER || Mode == MODE_ROCKETLAUNCHER || Mode == MODE_M16_1STPERSON || Mode == MODE_1STPERSON || Mode == MODE_HELICANNON_1STPERSON || Mode == MODE_CAMERA || GetWeaponFirstPersonOn()) ClipIfPedInFrontOfPlayer(); } // MaxSpeed is a limit of how fast the value is allowed to change. 1.0 = to Target in up to 1ms // Acceleration is how fast the speed will change to MaxSpeed. 1.0 = to MaxSpeed in 1ms void WellBufferMe(float Target, float *CurrentValue, float *CurrentSpeed, float MaxSpeed, float Acceleration, bool IsAngle) { float Delta = Target - *CurrentValue; if(IsAngle){ while(Delta >= PI) Delta -= 2*PI; while(Delta < -PI) Delta += 2*PI; } float TargetSpeed = Delta * MaxSpeed; // Add or subtract absolute depending on sign, genius! // if(TargetSpeed - *CurrentSpeed > 0.0f) // *CurrentSpeed += Acceleration * Abs(TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep(); // else // *CurrentSpeed -= Acceleration * Abs(TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep(); // this is simpler: *CurrentSpeed += Acceleration * (TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep(); // Clamp speed if we overshot if(TargetSpeed < 0.0f && *CurrentSpeed < TargetSpeed) *CurrentSpeed = TargetSpeed; else if(TargetSpeed > 0.0f && *CurrentSpeed > TargetSpeed) *CurrentSpeed = TargetSpeed; *CurrentValue += *CurrentSpeed * Min(10.0f, CTimer::GetTimeStep()); } void MakeAngleLessThan180(float &Angle) { while(Angle >= PI) Angle -= 2*PI; while(Angle < -PI) Angle += 2*PI; } void CCam::ProcessSpecialHeightRoutines(void) { int i; bool StandingOnBoat = false; static bool PreviouslyFailedRoadHeightCheck = false; CVector CamToTarget, CamToPed; float DistOnGround, BetaAngle; CPed *Player; float PedZDist; CColPoint colPoint; CamToTarget = TheCamera.pTargetEntity->GetPosition() - TheCamera.GetGameCamPosition(); DistOnGround = CamToTarget.Magnitude2D(); BetaAngle = CGeneral::GetATanOfXY(CamToTarget.x, CamToTarget.y); m_bTheHeightFixerVehicleIsATrain = false; // CGeneral::GetATanOfXY(TheCamera.GetForward().x, TheCamera.GetForward().y); Player = CWorld::Players[CWorld::PlayerInFocus].m_pPed; if(DistOnGround > 10.0f) DistOnGround = 10.0f; if(CamTargetEntity && CamTargetEntity->IsPed()){ if(FindPlayerPed()->m_pCurSurface && FindPlayerPed()->m_pCurSurface->IsVehicle() && ((CVehicle*)FindPlayerPed()->m_pCurSurface)->IsBoat()) StandingOnBoat = true; float FoundPedZ = -100.0f; // Move up the camera if there is a ped close to it if(Mode == MODE_FOLLOWPED || Mode == MODE_FIGHT_CAM || Mode == MODE_PILLOWS_PAPS){ // Find highest ped close to camera for(i = 0; i < Player->m_numNearPeds; i++){ CPed *nearPed = Player->m_nearPeds[i]; if(nearPed && nearPed->GetPedState() != PED_DEAD){ CamToPed = nearPed->GetPosition() - TheCamera.GetGameCamPosition(); if(Abs(CamToPed.z) < 1.0f){ float DistSq = CamToPed.MagnitudeSqr(); if(DistSq < SQR(2.1f)){ if(nearPed->GetPosition().z > FoundPedZ) FoundPedZ = nearPed->GetPosition().z; }else{ float Dist = Sqrt(DistSq); CamToPed /= Dist; // strange calculation CVector PlayerCamSpeed = DotProduct(Front, Player->m_vecMoveSpeed)*Front; float SpeedDiff = DotProduct(PlayerCamSpeed - nearPed->m_vecMoveSpeed, CamToPed); if(SpeedDiff > 0.01f && (m_fPedBetweenCameraHeightOffset > 0.0f && (Dist-2.1f)/SpeedDiff < 75.0f || m_fPedBetweenCameraHeightOffset <= 0.0f && (Dist-2.1f)/SpeedDiff < 75.0f * 0.1f)) if(nearPed->GetPosition().z > FoundPedZ) FoundPedZ = nearPed->GetPosition().z; } } } } if(FoundPedZ > -99.0f){ float Offset = 0.0f; PedZDist = 0.0f; if(FoundPedZ > Player->GetPosition().z) PedZDist = FoundPedZ - Player->GetPosition().z; if(Mode == MODE_FOLLOWPED){ if(TheCamera.PedZoomIndicator == CAM_ZOOM_1 && ((CPed*)CamTargetEntity)->GetPedState() != PED_ENTER_CAR && ((CPed*)CamTargetEntity)->GetPedState() != PED_CARJACK) Offset = 0.45f + PedZDist; // BUG: overrides this ^ case if(TheCamera.PedZoomIndicator == CAM_ZOOM_2 || TheCamera.PedZoomIndicator == CAM_ZOOM_1) Offset = 0.35f + PedZDist; if(TheCamera.PedZoomIndicator == CAM_ZOOM_3) Offset = 0.25f + PedZDist; m_fPedBetweenCameraHeightOffset = Offset + 1.3f; }else if(Mode == MODE_FIGHT_CAM) m_fPedBetweenCameraHeightOffset = PedZDist + 1.3f + 0.5f; else if(Mode == MODE_PILLOWS_PAPS) m_fPedBetweenCameraHeightOffset = PedZDist + 1.3f + 0.45f; }else{ m_fPedBetweenCameraHeightOffset = 0.0f; } } // Move camera up for vehicles in the way if(m_bCollisionChecksOn && (Mode == MODE_FOLLOWPED || Mode == MODE_FIGHT_CAM)){ bool FoundCar = false; CEntity *vehicle = nil; float TestDist = DistOnGround + 1.25f; float HighestCar = 0.0f; if(m_fDimensionOfHighestNearCar > 0.0f) TestDist += 0.3f; CVector TestBase = CamTargetEntity->GetPosition(); CVector TestPoint; TestBase.z -= 0.15f; TestPoint = TestBase - TestDist * CVector(Cos(BetaAngle), Sin(BetaAngle), 0.0f); if(CWorld::ProcessLineOfSight(CamTargetEntity->GetPosition(), TestPoint, colPoint, vehicle, false, true, false, false, false, false) && vehicle->IsVehicle()){ float height = vehicle->GetColModel()->boundingBox.GetSize().z; FoundCar = true; HighestCar = height; if(((CVehicle*)vehicle)->IsTrain()) m_bTheHeightFixerVehicleIsATrain = true; } TestPoint = TestBase - TestDist * CVector(Cos(BetaAngle+DEGTORAD(28.0f)), Sin(BetaAngle+DEGTORAD(28.0f)), 0.0f); if(CWorld::ProcessLineOfSight(CamTargetEntity->GetPosition(), TestPoint, colPoint, vehicle, false, true, false, false, false, false) && vehicle->IsVehicle()){ float height = vehicle->GetColModel()->boundingBox.GetSize().z; if(FoundCar){ HighestCar = Max(HighestCar, height); }else{ FoundCar = true; HighestCar = height; } if(((CVehicle*)vehicle)->IsTrain()) m_bTheHeightFixerVehicleIsATrain = true; } TestPoint = TestBase - TestDist * CVector(Cos(BetaAngle-DEGTORAD(28.0f)), Sin(BetaAngle-DEGTORAD(28.0f)), 0.0f); if(CWorld::ProcessLineOfSight(CamTargetEntity->GetPosition(), TestPoint, colPoint, vehicle, false, true, false, false, false, false) && vehicle->IsVehicle()){ float height = vehicle->GetColModel()->boundingBox.GetSize().z; if(FoundCar){ HighestCar = Max(HighestCar, height); }else{ FoundCar = true; HighestCar = height; } if(((CVehicle*)vehicle)->IsTrain()) m_bTheHeightFixerVehicleIsATrain = true; } if(FoundCar){ m_fDimensionOfHighestNearCar = HighestCar + 0.1f; if(Mode == MODE_FIGHT_CAM) m_fDimensionOfHighestNearCar += 0.75f; }else m_fDimensionOfHighestNearCar = 0.0f; } } if(StandingOnBoat){ m_fDimensionOfHighestNearCar = 1.0f; m_fPedBetweenCameraHeightOffset = 0.0f; } } void CCam::GetVectorsReadyForRW(void) { CVector right; Up = CVector(0.0f, 0.0f, 1.0f); Front.Normalise(); if(Front.x == 0.0f && Front.y == 0.0f){ Front.x = 0.0001f; Front.y = 0.0001f; } right = CrossProduct(Front, Up); right.Normalise(); Up = CrossProduct(right, Front); } bool CCam::GetBoatLook_L_R_HeightOffset(float &Offset) { if(CamTargetEntity == nil) return false; CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(CamTargetEntity->GetModelIndex()); tBoatHandlingData *handling = mod_HandlingManager.GetBoatPointer(mi->m_handlingId); if(handling){ Offset = handling->fLook_L_R_BehindCamHeight; return true; } return false; // can't happen, we always get a boat pointer back } void CCam::LookBehind(void) { float Dist, DeltaBeta, TargetOrientation, Angle; CVector TargetCoors, TargetFwd, TestCoors; TargetCoors = CamTargetEntity->GetPosition(); Front = CamTargetEntity->GetPosition() - Source; if((Mode == MODE_CAM_ON_A_STRING || Mode == MODE_BEHINDBOAT || Mode == MODE_BEHINDCAR) && CamTargetEntity->IsVehicle()){ LookingBehind = true; Dist = Mode == MODE_CAM_ON_A_STRING ? CA_MAX_DISTANCE : 15.5f; TargetFwd = CamTargetEntity->GetForward(); TargetFwd.Normalise(); TargetOrientation = CGeneral::GetATanOfXY(TargetFwd.x, TargetFwd.y); DeltaBeta = TargetOrientation - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; if(DirectionWasLooking != LOOKING_BEHIND) LookBehindCamWasInFront = DeltaBeta <= -HALFPI || DeltaBeta >= HALFPI; if(LookBehindCamWasInFront) TargetOrientation += PI; Source.x = Dist*Cos(TargetOrientation) + TargetCoors.x; Source.y = Dist*Sin(TargetOrientation) + TargetCoors.y; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); Front = CamTargetEntity->GetPosition() - Source; GetVectorsReadyForRW(); } if(Mode == MODE_1STPERSON && CamTargetEntity->IsVehicle()){ LookingBehind = true; RwCameraSetNearClipPlane(Scene.camera, 0.25f); Front = CamTargetEntity->GetForward(); Front.Normalise(); if(((CVehicle*)CamTargetEntity)->IsBoat()) Source.z -= 0.5f; if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE){ float FrontDist = 1.1f; if(((CVehicle*)CamTargetEntity)->pDriver){ CVector ExtraFwd(0.0f, 0.0f, 0.0f); ((CVehicle*)CamTargetEntity)->pDriver->m_pedIK.GetComponentPosition(ExtraFwd, PED_HEAD); ExtraFwd += ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed*CTimer::GetTimeStep() - CamTargetEntity->GetPosition(); FrontDist += 0.2f + Max(DotProduct(ExtraFwd, CamTargetEntity->GetForward()), 0.0f); } Source += FrontDist*Front; Front = -Front; }else if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI){ Front = -1.0f*CamTargetEntity->GetUp(); Up = CamTargetEntity->GetForward(); Source += 0.25f*Front; }else{ Source += 0.25f*Front; Front = -Front; } } if(CamTargetEntity->IsPed()){ Angle = CGeneral::GetATanOfXY(Source.x - TargetCoors.x, Source.y - TargetCoors.y) + PI; Source.x = 4.5f*Cos(Angle) + TargetCoors.x; Source.y = 4.5f*Sin(Angle) + TargetCoors.y; Source.z = 1.15f + TargetCoors.z; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); Front = TargetCoors - Source; GetVectorsReadyForRW(); } } float BOAT_1STPERSON_L_OFFSETX = 0.7f; float BOAT_1STPERSON_R_OFFSETX = 0.3f; float BOAT_1STPERSON_LR_OFFSETZ = 0.2f; void CCam::LookLeft(void) { float Dist, TargetOrientation; CVector TargetCoors, TargetFwd; if((Mode == MODE_CAM_ON_A_STRING || Mode == MODE_BEHINDBOAT || Mode == MODE_BEHINDCAR) && CamTargetEntity->IsVehicle()){ LookingLeft = true; TargetCoors = CamTargetEntity->GetPosition(); Front = CamTargetEntity->GetPosition() - Source; if(Mode == MODE_CAM_ON_A_STRING) Dist = CA_MAX_DISTANCE; else if(Mode == MODE_BEHINDBOAT){ Dist = 9.0f; float Offset = 0.0f; if(GetBoatLook_L_R_HeightOffset(Offset) && !CCullZones::Cam1stPersonForPlayer()) Source.z = TargetCoors.z + Offset; }else Dist = 9.0f; TargetFwd = CamTargetEntity->GetForward(); TargetFwd.Normalise(); TargetOrientation = CGeneral::GetATanOfXY(TargetFwd.x, TargetFwd.y); Source.x = Dist*Cos(TargetOrientation - HALFPI) + TargetCoors.x; Source.y = Dist*Sin(TargetOrientation - HALFPI) + TargetCoors.y; CColModel *colModel = CamTargetEntity->GetColModel(); CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); CVector TopRight = CamTargetEntity->GetPosition() + CamTargetEntity->GetRight()*colModel->boundingBox.max.x + CamTargetEntity->GetUp()*colModel->boundingBox.max.z; float Height = Min(Max(m_cvecTargetCoorsForFudgeInter.z, TopRight.z)+0.1f, OrigSource.z); Source.z = Max(Height, Source.z); Front = CamTargetEntity->GetPosition() - Source; Front.z += 1.1f; if(Mode == MODE_BEHINDBOAT) Front.z += 1.2f; GetVectorsReadyForRW(); } if(Mode == MODE_1STPERSON && CamTargetEntity->IsVehicle()){ LookingLeft = true; RwCameraSetNearClipPlane(Scene.camera, 0.25f); if(((CVehicle*)CamTargetEntity)->IsBoat()){ if(((CVehicle*)CamTargetEntity)->pDriver){ CVector neck(0.0f, 0.0f, 0.0f); CPed *driver = ((CVehicle*)CamTargetEntity)->pDriver; driver->SetPedPositionInCar(); driver->GetMatrix().UpdateRW(); driver->UpdateRwFrame(); driver->UpdateRpHAnim(); driver->m_pedIK.GetComponentPosition(neck, PED_NECK); Source = neck + BOAT_1STPERSON_L_OFFSETX*CamTargetEntity->GetRight() + BOAT_1STPERSON_LR_OFFSETZ*CamTargetEntity->GetUp(); }else Source.z -= 0.5f; } Up = CamTargetEntity->GetUp(); Up.Normalise(); Front = CamTargetEntity->GetForward(); Front.Normalise(); Front = -CrossProduct(Front, Up); Front.Normalise(); if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) Source -= 1.45f*Front; } } void CCam::LookRight(void) { float Dist, TargetOrientation; CVector TargetCoors, TargetFwd; CColPoint colPoint; if((Mode == MODE_CAM_ON_A_STRING || Mode == MODE_BEHINDBOAT) && CamTargetEntity->IsVehicle()){ LookingRight = true; TargetCoors = CamTargetEntity->GetPosition(); Front = CamTargetEntity->GetPosition() - Source; if(Mode == MODE_CAM_ON_A_STRING) Dist = CA_MAX_DISTANCE; else if(Mode == MODE_BEHINDBOAT){ Dist = 9.0f; float Offset = 0.0f; if(GetBoatLook_L_R_HeightOffset(Offset) && !CCullZones::Cam1stPersonForPlayer()) Source.z = TargetCoors.z + Offset; }else Dist = 9.0f; TargetFwd = CamTargetEntity->GetForward(); TargetFwd.Normalise(); TargetOrientation = CGeneral::GetATanOfXY(TargetFwd.x, TargetFwd.y); Source.x = Dist*Cos(TargetOrientation + HALFPI) + TargetCoors.x; Source.y = Dist*Sin(TargetOrientation + HALFPI) + TargetCoors.y; CColModel *colModel = CamTargetEntity->GetColModel(); CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); CVector TopLeft = CamTargetEntity->GetPosition() + CamTargetEntity->GetRight()*colModel->boundingBox.min.x + CamTargetEntity->GetUp()*colModel->boundingBox.max.z; float Height = Min(Max(m_cvecTargetCoorsForFudgeInter.z, TopLeft.z)+0.1f, OrigSource.z); Source.z = Max(Height, Source.z); Front = CamTargetEntity->GetPosition() - Source; Front.z += 1.1f; if(Mode == MODE_BEHINDBOAT) Front.z += 1.2f; GetVectorsReadyForRW(); } if(Mode == MODE_1STPERSON && CamTargetEntity->IsVehicle()){ LookingRight = true; RwCameraSetNearClipPlane(Scene.camera, 0.25f); if(((CVehicle*)CamTargetEntity)->IsBoat()){ if(((CVehicle*)CamTargetEntity)->pDriver){ CVector neck(0.0f, 0.0f, 0.0f); CPed *driver = ((CVehicle*)CamTargetEntity)->pDriver; driver->SetPedPositionInCar(); driver->GetMatrix().UpdateRW(); driver->UpdateRwFrame(); driver->UpdateRpHAnim(); driver->m_pedIK.GetComponentPosition(neck, PED_NECK); Source = neck + BOAT_1STPERSON_R_OFFSETX*CamTargetEntity->GetRight() + BOAT_1STPERSON_LR_OFFSETZ*CamTargetEntity->GetUp(); }else Source.z -= 0.5f; } Up = CamTargetEntity->GetUp(); Up.Normalise(); Front = CamTargetEntity->GetForward(); Front.Normalise(); Front = CrossProduct(Front, Up); Front.Normalise(); if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) Source -= 1.45f*Front; } } void CCam::ClipIfPedInFrontOfPlayer(void) { float FwdAngle, PedAngle, DeltaAngle, fDist, Near; CVector vDist; CPed *Player; bool found = false; int ped = 0; // unused: TheCamera.pTargetEntity->GetPosition() - TheCamera.GetGameCamPosition(); FwdAngle = CGeneral::GetATanOfXY(TheCamera.GetForward().x, TheCamera.GetForward().y); Player = CWorld::Players[CWorld::PlayerInFocus].m_pPed; while(ped < Player->m_numNearPeds && !found) if(Player->m_nearPeds[ped] && Player->m_nearPeds[ped]->GetPedState() != PED_DEAD) found = true; else ped++; if(found){ vDist = Player->m_nearPeds[ped]->GetPosition() - TheCamera.GetGameCamPosition(); PedAngle = CGeneral::GetATanOfXY(vDist.x, vDist.y); DeltaAngle = FwdAngle - PedAngle; while(DeltaAngle >= PI) DeltaAngle -= 2*PI; while(DeltaAngle < -PI) DeltaAngle += 2*PI; if(Abs(DeltaAngle) < HALFPI){ fDist = vDist.Magnitude2D(); if(fDist < 1.25f){ Near = DEFAULT_NEAR - (1.25f - fDist); if(Near < 0.05f) Near = 0.05f; RwCameraSetNearClipPlane(Scene.camera, Near); } } } } void CCam::KeepTrackOfTheSpeed(const CVector &source, const CVector &target, const CVector &up, const float &alpha, const float &beta, const float &fov) { static CVector PreviousSource = source; static CVector PreviousTarget = target; static CVector PreviousUp = up; static float PreviousBeta = beta; static float PreviousAlpha = alpha; static float PreviousFov = fov; if(TheCamera.m_bJust_Switched){ PreviousSource = source; PreviousTarget = target; PreviousUp = up; } m_cvecSourceSpeedOverOneFrame = source - PreviousSource; m_cvecTargetSpeedOverOneFrame = target - PreviousTarget; m_cvecUpOverOneFrame = up - PreviousUp; m_fFovSpeedOverOneFrame = fov - PreviousFov; m_fBetaSpeedOverOneFrame = beta - PreviousBeta; MakeAngleLessThan180(m_fBetaSpeedOverOneFrame); m_fAlphaSpeedOverOneFrame = alpha - PreviousAlpha; MakeAngleLessThan180(m_fAlphaSpeedOverOneFrame); PreviousSource = source; PreviousTarget = target; PreviousUp = up; PreviousBeta = beta; PreviousAlpha = alpha; PreviousFov = fov; } bool CCam::Using3rdPersonMouseCam(void) { return CCamera::m_bUseMouse3rdPerson && Mode == MODE_FOLLOWPED; } bool CCam::GetWeaponFirstPersonOn(void) { return CamTargetEntity && CamTargetEntity->IsPed() && ((CPed*)CamTargetEntity)->GetWeapon()->m_bAddRotOffset; } bool CCam::IsTargetInWater(const CVector &CamCoors) { if(CamTargetEntity){ float WaterZ = -6000.0f; CWaterLevel::GetWaterLevel(CamTargetEntity->GetPosition(), &WaterZ, false); if(CamTargetEntity->IsPed()){ if(((CPed*)CamTargetEntity)->bIsDrowning || ((CPed*)CamTargetEntity)->bIsInWater && CamTargetEntity->GetPosition().z < WaterZ) return true; }else{ assert(CamTargetEntity->IsVehicle()); if(((CVehicle*)CamTargetEntity)->bIsDrowning || ((CVehicle*)CamTargetEntity)->bIsInWater && CamTargetEntity->GetPosition().z < WaterZ) return true; } } m_vecLastAboveWaterCamPosition = Source; return false; } void CCam::PrintMode(void) { // Doesn't do anything char buf[256]; if(PrintDebugCode){ sprintf(buf, " "); sprintf(buf, " "); sprintf(buf, " "); static Const char *modes[] = { "None", "Top Down", "GTA Classic", "Behind Car", "Follow Ped", "Aiming", "Debug", "Sniper", "Rocket", "Model Viewer", "Bill", "Syphon", "Circle", "Cheesy Zoom", "Wheel", "Fixed", "1st Person", "Fly by", "on a String", "Reaction", "Follow Ped with Bind", "Chris", "Behind Boat", "Player fallen in Water", "Train Roof", "Train Side", "Blood on the tracks", "Passenger", "Syphon Crim in Front", "Dead Baby", "Pillow Paps", "Look at Cars", "Arrest One", "Arrest Two", "M16", "Special fixed for Syphon", "Fight", "Top Down Ped", "Lighthouse", "Sniper run about", "Rocket run about", "1st Person run about", "M16 run about", "Fight run about", "Editor", "Helicannon", "Camera" }; sprintf(buf, "Cam: %s", modes[TheCamera.Cams[TheCamera.ActiveCam].Mode]); CDebug::PrintAt(buf, 2, 5); } if(DebugCamMode != MODE_NONE){ switch(Mode){ case MODE_FOLLOWPED: sprintf(buf, "Debug:- Cam Choice1. No Locking, used as game default"); break; case MODE_REACTION: sprintf(buf, "Debug:- Cam Choice2. Reaction Cam On A String "); sprintf(buf, " Uses Locking Button LeftShoulder 1. "); // lie break; case MODE_FOLLOW_PED_WITH_BIND: sprintf(buf, "Debug:- Cam Choice3. Game ReactionCam with Locking "); sprintf(buf, " Uses Locking Button LeftShoulder 1. "); break; case MODE_CHRIS: sprintf(buf, "Debug:- Cam Choice4. Chris's idea. "); sprintf(buf, " Uses Locking Button LeftShoulder 1. "); sprintf(buf, " Also control the camera using the right analogue stick."); break; } } } // This code is really bad. wtf R*? CVector CCam::DoAverageOnVector(const CVector &vec) { int i; CVector Average = CVector(0.0f, 0.0f, 0.0f); if(ResetStatics){ m_iRunningVectorArrayPos = 0; m_iRunningVectorCounter = 1; } // TODO: make this work with NUMBER_OF_VECTORS_FOR_AVERAGE != 2 if(m_iRunningVectorCounter == 3){ m_arrPreviousVectors[0] = m_arrPreviousVectors[1]; m_arrPreviousVectors[1] = vec; }else m_arrPreviousVectors[m_iRunningVectorArrayPos] = vec; for(i = 0; i <= m_iRunningVectorArrayPos; i++) Average += m_arrPreviousVectors[i]; Average /= i; m_iRunningVectorArrayPos++; m_iRunningVectorCounter++; if(m_iRunningVectorArrayPos >= NUMBER_OF_VECTORS_FOR_AVERAGE) m_iRunningVectorArrayPos = NUMBER_OF_VECTORS_FOR_AVERAGE-1; if(m_iRunningVectorCounter > NUMBER_OF_VECTORS_FOR_AVERAGE+1) m_iRunningVectorCounter = NUMBER_OF_VECTORS_FOR_AVERAGE+1; return Average; } float DefaultAcceleration = 0.045f; float DefaultMaxStep = 0.15f; float fDefaultSpeedStep = 0.025f; float fDefaultSpeedMultiplier = 0.09f; float fDefaultSpeedLimit = 0.15f; float fDefaultSpeedStep4Avoid = 0.02f; float fDefaultSpeedMultiplier4Avoid = 0.05f; float fDefaultSpeedLimit4Avoid = 0.25f; float fAvoidGeomThreshhold = 1.5f; float fMiniGunBetaOffset = 0.3f; void CCam::Process_FollowPed(const CVector &CameraTarget, float TargetOrientation, float, float) { if(!CamTargetEntity->IsPed()) return; CVector TargetCoors, Dist, IdealSource; float Length = 0.0f; static bool PickedASide; static float FixedTargetOrientation = 0.0f; float AngleToGoTo = 0.0f; bool StandingInTrain = false; float ZoomGroundTarget = 0.0f; float ZoomZTarget = 0.0f; static int TimeIndicatedWantedToGoDown = 0; static bool StartedCountingForGoDown = false; static float ZoomGround = 0.0f; static float ZoomGroundSpeed = 0.0f; static float ZoomZ = 0.0f; static float ZoomZSpeed = 0.0f; float DeltaBeta; m_bFixingBeta = false; bBelowMinDist = false; bBehindPlayerDesired = false; FOV = DefaultFOV; if(ResetStatics){ Rotating = false; m_bCollisionChecksOn = true; FixedTargetOrientation = 0.0f; PickedASide = false; StartedCountingForGoDown = false; AngleToGoTo = 0.0f; ZoomGround = 0.0f; ZoomGroundSpeed = 0.0f; ZoomZ = 0.0f; ZoomZSpeed = 0.0f; Distance = 500.0f; } TargetCoors = CameraTarget; // Take speed of thing we're standing on into account CVector GroundMovement(0.0f, 0.0f, 0.0f); CPhysical *ground = (CPhysical*)((CPed*)CamTargetEntity)->m_pCurSurface; if(ground && (ground->IsVehicle() || ground->IsObject())) GroundMovement += ground->GetSpeed(CamTargetEntity->GetPosition() - ground->GetPosition()) * CTimer::GetTimeStep(); Source += GroundMovement; IdealSource = Source; TargetCoors.z += m_fSyphonModeTargetZOffSet; TargetCoors.z = DoAverageOnVector(TargetCoors).z; Dist.x = IdealSource.x - TargetCoors.x; Dist.y = IdealSource.y - TargetCoors.y; Length = Dist.Magnitude2D(); // Cam on a string. With a fixed distance. Zoom in/out is done later. if(Length != 0.0f){ IdealSource = TargetCoors + CVector(Dist.x, Dist.y, 0.0f)/Length * m_fMinRealGroundDist; IdealSource.z += GroundMovement.z; }else IdealSource = TargetCoors + CVector(1.0f, 1.0f, 0.0f); if(TheCamera.m_bUseTransitionBeta && ResetStatics){ CVector VecDistance; IdealSource.x = TargetCoors.x + m_fMinRealGroundDist*Cos(m_fTransitionBeta); IdealSource.y = TargetCoors.y + m_fMinRealGroundDist*Sin(m_fTransitionBeta); Beta = CGeneral::GetATanOfXY(IdealSource.x - TargetCoors.x, IdealSource.y - TargetCoors.y); }else Beta = CGeneral::GetATanOfXY(Source.x - TargetCoors.x, Source.y - TargetCoors.y); if(TheCamera.m_bCamDirectlyBehind){ m_bCollisionChecksOn = true; Beta = TargetOrientation + PI; } if(FindPlayerVehicle()) if(FindPlayerVehicle()->m_vehType == VEHICLE_TYPE_TRAIN) StandingInTrain = true; if(TheCamera.m_bCamDirectlyInFront){ m_bCollisionChecksOn = true; Beta = TargetOrientation; } while(Beta >= PI) Beta -= 2.0f * PI; while(Beta < -PI) Beta += 2.0f * PI; if(TheCamera.PedZoomIndicator == CAM_ZOOM_1 && ((CPed*)CamTargetEntity)->GetPedState() != PED_ENTER_CAR && ((CPed*)CamTargetEntity)->GetPedState() != PED_CARJACK){ ZoomGroundTarget = m_fTargetZoomGroundOne; ZoomZTarget = m_fTargetZoomOneZExtra; }else if(TheCamera.PedZoomIndicator == CAM_ZOOM_2 || TheCamera.PedZoomIndicator == CAM_ZOOM_1){ ZoomGroundTarget = m_fTargetZoomGroundTwo; ZoomZTarget = m_fTargetZoomTwoZExtra; }else if(TheCamera.PedZoomIndicator == CAM_ZOOM_3){ ZoomGroundTarget = m_fTargetZoomGroundThree; ZoomZTarget = m_fTargetZoomThreeZExtra; } if(m_fCloseInPedHeightOffset > 0.00001f){ ZoomGroundTarget = m_fTargetCloseInDist; ZoomZTarget = m_fTargetZoomZCloseIn; } if(ResetStatics){ ZoomGround = ZoomGroundTarget; ZoomZ = ZoomZTarget; } float SpeedStep = fDefaultSpeedStep; float SpeedMultiplier = fDefaultSpeedMultiplier; float SpeedLimit = fDefaultSpeedLimit; bool Shooting = false; CPed *ped = (CPed*)CamTargetEntity; if(ped->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) if(CPad::GetPad(0)->GetWeapon()) Shooting = true; if(ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR || ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT) Shooting = false; // Figure out if and where we want to rotate if(CPad::GetPad(0)->ForceCameraBehindPlayer() && !CPickups::PlayerOnWeaponPickup || Shooting){ // Center cam behind player if(PickedASide){ if(AngleToGoTo == 0.0f){ FixedTargetOrientation = TargetOrientation + PI; if(Shooting && ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) FixedTargetOrientation -= fMiniGunBetaOffset; } Rotating = true; }else{ FixedTargetOrientation = TargetOrientation + PI; Rotating = true; PickedASide = true; if(Shooting && ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) FixedTargetOrientation -= fMiniGunBetaOffset; } }else if(Abs(TheCamera.m_fAvoidTheGeometryProbsTimer) > fAvoidGeomThreshhold && !Rotating ){ if(TheCamera.m_fAvoidTheGeometryProbsTimer < 0.0f) FixedTargetOrientation = TargetOrientation; else FixedTargetOrientation = TargetOrientation + PI; float dist = (Source - TargetCoors).Magnitude(); float mult = dist > 0.1f ? 1.0f/dist : 10.0f; SpeedStep = mult * fDefaultSpeedStep4Avoid; SpeedMultiplier = mult * fDefaultSpeedMultiplier4Avoid; SpeedLimit = mult * fDefaultSpeedLimit4Avoid; } int MoveState = ((CPed*)CamTargetEntity)->m_nMoveState; if(MoveState != PEDMOVE_NONE && MoveState != PEDMOVE_STILL && !(CPad::GetPad(0)->ForceCameraBehindPlayer() && !CPickups::PlayerOnWeaponPickup) && !Shooting){ Rotating = false; if(TheCamera.m_fAvoidTheGeometryProbsTimer <= fAvoidGeomThreshhold) BetaSpeed = 0.0f; } // Now do the Beta rotation float RotDistance = m_fMinRealGroundDist; if(Rotating || TheCamera.m_fAvoidTheGeometryProbsTimer > fAvoidGeomThreshhold){ m_bFixingBeta = true; while(FixedTargetOrientation >= PI) FixedTargetOrientation -= 2*PI; while(FixedTargetOrientation < -PI) FixedTargetOrientation += 2*PI; while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; // This is inlined WellBufferMe - unfortunately modified so we can't just call it { DeltaBeta = FixedTargetOrientation - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; // this is the added bit if(!Rotating){ if(TheCamera.m_nAvoidTheGeometryProbsDirn == -1 && DeltaBeta > 0.0f || TheCamera.m_nAvoidTheGeometryProbsDirn == 1 && DeltaBeta < 0.0f) DeltaBeta *= -1.0f; } float ReqSpeed = DeltaBeta * SpeedMultiplier; // this is also added ReqSpeed = clamp(ReqSpeed, -SpeedLimit, SpeedLimit); // Add or subtract absolute depending on sign, genius! if(ReqSpeed - BetaSpeed > 0.0f) BetaSpeed += SpeedStep * Abs(ReqSpeed - BetaSpeed) * CTimer::GetTimeStep(); else BetaSpeed -= SpeedStep * Abs(ReqSpeed - BetaSpeed) * CTimer::GetTimeStep(); // this would be simpler: // BetaSpeed += SpeedStep * (ReqSpeed - BetaSpeed) * CTimer::ms_fTimeStep; if(ReqSpeed < 0.0f && BetaSpeed < ReqSpeed) BetaSpeed = ReqSpeed; else if(ReqSpeed > 0.0f && BetaSpeed > ReqSpeed) BetaSpeed = ReqSpeed; Beta += BetaSpeed * Min(10.0f, CTimer::GetTimeStep()); } if(ResetStatics){ Beta = FixedTargetOrientation; BetaSpeed = 0.0f; } Source.x = TargetCoors.x + RotDistance * Cos(Beta); Source.y = TargetCoors.y + RotDistance * Sin(Beta); // Check if we can stop rotating DeltaBeta = FixedTargetOrientation - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; if(Abs(DeltaBeta) < DEGTORAD(1.0f) && !bBehindPlayerDesired){ // Stop rotation PickedASide = false; Rotating = false; BetaSpeed = 0.0f; } } if(TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront || StandingInTrain || Rotating || TheCamera.m_bUseTransitionBeta && ResetStatics || Abs(TheCamera.m_fAvoidTheGeometryProbsTimer) > fAvoidGeomThreshhold){ if(TheCamera.m_bUseTransitionBeta){ Beta = m_fTransitionBeta; Source.x = TargetCoors.x + RotDistance * Cos(m_fTransitionBeta); Source.y = TargetCoors.y + RotDistance * Sin(m_fTransitionBeta); } if(TheCamera.m_bCamDirectlyBehind){ Beta = TargetOrientation + PI; Source.x = TargetCoors.x + RotDistance * Cos(Beta); Source.y = TargetCoors.y + RotDistance * Sin(Beta); } if(TheCamera.m_bCamDirectlyInFront){ Beta = TargetOrientation; Source.x = TargetCoors.x + RotDistance * Cos(Beta); Source.y = TargetCoors.y + RotDistance * Sin(Beta); } if(StandingInTrain){ Beta = TargetOrientation + PI; Source.x = TargetCoors.x + RotDistance * Cos(Beta); Source.y = TargetCoors.y + RotDistance * Sin(Beta); m_fDimensionOfHighestNearCar = 0.0f; m_fCamBufferedHeight = 0.0f; m_fCamBufferedHeightSpeed = 0.0f; } if(StandingInTrain){ Beta = TargetOrientation + PI; Source.x = TargetCoors.x + RotDistance * Cos(Beta); Source.y = TargetCoors.y + RotDistance * Sin(Beta); m_fDimensionOfHighestNearCar = 0.0f; m_fCamBufferedHeight = 0.0f; m_fCamBufferedHeightSpeed = 0.0f; } // Beta and Source already set in the rotation code }else{ Source = IdealSource; BetaSpeed = 0.0f; } Source.z = IdealSource.z; // Zoom out camera Front = TargetCoors - Source; Front.Normalise(); WellBufferMe(ZoomGroundTarget, &ZoomGround, &ZoomGroundSpeed, 0.2f, 0.07f, false); WellBufferMe(ZoomZTarget, &ZoomZ, &ZoomZSpeed, 0.2f, 0.07f, false); Source.x -= Front.x*ZoomGround; Source.y -= Front.y*ZoomGround; Source.z += ZoomZ; // Process height offset to avoid peds and cars float TargetZOffSet = Max(m_fDimensionOfHighestNearCar, m_fPedBetweenCameraHeightOffset); float TargetHeight = CameraTarget.z + TargetZOffSet - Source.z; if(TargetHeight > m_fCamBufferedHeight){ // Have to go up if(TargetZOffSet == m_fPedBetweenCameraHeightOffset && TargetZOffSet > m_fCamBufferedHeight) WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.04f, false); else WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.025f, false); StartedCountingForGoDown = false; }else{ // Have to go down if(StartedCountingForGoDown){ if(CTimer::GetTimeInMilliseconds() != TimeIndicatedWantedToGoDown){ if(TargetHeight > 0.0f) WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.01f, false); else WellBufferMe(0.0f, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.01f, false); } }else{ StartedCountingForGoDown = true; TimeIndicatedWantedToGoDown = CTimer::GetTimeInMilliseconds(); } } Source.z += m_fCamBufferedHeight; TargetCoors.z += Min(1.0f, m_fCamBufferedHeight/2.0f); m_cvecTargetCoorsForFudgeInter = TargetCoors; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); float TargetDist = (TargetCoors - Source).Magnitude(); if(TargetDist < Distance) Distance = TargetDist; else{ float f = Pow(0.97f, CTimer::GetTimeStep()); Distance = (1.0f - f)*TargetDist + f*Distance; if(TargetDist > 0.05f) Source = TargetCoors + (Source-TargetCoors)*Distance/TargetDist; float clip = Distance-fRangePlayerRadius; if(clip < RwCameraGetNearClipPlane(Scene.camera)) RwCameraSetNearClipPlane(Scene.camera, Max(clip, fCloseNearClipLimit)); } Front = TargetCoors - Source; m_fRealGroundDist = Front.Magnitude2D(); m_fMinDistAwayFromCamWhenInterPolating = m_fRealGroundDist; Front.Normalise(); GetVectorsReadyForRW(); TheCamera.m_bCamDirectlyBehind = false; TheCamera.m_bCamDirectlyInFront = false; ResetStatics = false; } float fBaseDist = 1.7f; float fAngleDist = 2.0f; float fFalloff = 3.0f; float fStickSens = 0.01f; float fTweakFOV = 1.1f; float fTranslateCamUp = 0.8f; int16 nFadeControlThreshhold = 45; float fDefaultAlphaOrient = -0.22f; float fMouseAvoidGeomReturnRate = 0.92f; void CCam::Process_FollowPedWithMouse(const CVector &CameraTarget, float TargetOrientation, float, float) { FOV = DefaultFOV; if(!CamTargetEntity->IsPed()) return; CVector TargetCoors; float CamDist; CColPoint colPoint; CEntity *entity; if(ResetStatics){ Rotating = false; m_bCollisionChecksOn = true; CPad::GetPad(0)->ClearMouseHistory(); ResetStatics = false; } bool OnTrain = FindPlayerVehicle() && FindPlayerVehicle()->IsTrain(); TargetCoors = CameraTarget; TargetCoors.z += fTranslateCamUp; float AlphaOffset, BetaOffset; if(CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_PLAYERINFO)){ CVector ToCam = Source - TargetCoors; ToCam.Normalise(); if(ToCam.z < -0.9f) BetaOffset = TargetOrientation + PI; else BetaOffset = Atan2(ToCam.y, ToCam.x); BetaOffset -= Beta; AlphaOffset = 0.0f; }else{ // Look around bool UseMouse = false; float MouseX = CPad::GetPad(0)->GetMouseX(); float MouseY = CPad::GetPad(0)->GetMouseY(); float LookLeftRight, LookUpDown; if((MouseX != 0.0f || MouseY != 0.0f) && !CPad::GetPad(0)->ArePlayerControlsDisabled()){ UseMouse = true; LookLeftRight = -2.5f*MouseX; LookUpDown = 4.0f*MouseY; }else{ LookLeftRight = -CPad::GetPad(0)->LookAroundLeftRight(); LookUpDown = CPad::GetPad(0)->LookAroundUpDown(); } if(UseMouse){ BetaOffset = LookLeftRight * TheCamera.m_fMouseAccelHorzntl * FOV/80.0f; AlphaOffset = LookUpDown * TheCamera.m_fMouseAccelVertical * FOV/80.0f; }else{ BetaOffset = LookLeftRight * fStickSens * (1.0f/14.0f) * FOV/80.0f * CTimer::GetTimeStep(); AlphaOffset = LookUpDown * fStickSens * (0.6f/14.0f) * FOV/80.0f * CTimer::GetTimeStep(); } } if(TheCamera.GetFading() && TheCamera.GetFadingDirection() == FADE_IN && nFadeControlThreshhold < CDraw::FadeValue || CDraw::FadeValue > 200 || CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_PLAYERINFO)){ if(Alpha < fDefaultAlphaOrient-0.05f) AlphaOffset = 0.05f; else if(Alpha < fDefaultAlphaOrient) AlphaOffset = fDefaultAlphaOrient - Alpha; else if(Alpha > fDefaultAlphaOrient+0.05f) AlphaOffset = -0.05f; else if(Alpha > fDefaultAlphaOrient) AlphaOffset = fDefaultAlphaOrient - Alpha; else AlphaOffset = 0.0f; } Alpha += AlphaOffset; Beta += BetaOffset; while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; if(Alpha > DEGTORAD(45.0f)) Alpha = DEGTORAD(45.0f); else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); // SA code #ifdef FREE_CAM if((CCamera::bFreeCam && Alpha > 0.0f) || (!CCamera::bFreeCam && Alpha > fBaseDist)) #else if(Alpha > fBaseDist) // comparing an angle against a distance? #endif CamDist = fBaseDist + Cos(Min(Alpha*fFalloff, HALFPI))*fAngleDist; else CamDist = fBaseDist + Cos(Alpha)*fAngleDist; if(TheCamera.m_bUseTransitionBeta) Beta = m_fTransitionBeta; if(TheCamera.m_bCamDirectlyBehind) Beta = TheCamera.m_PedOrientForBehindOrInFront + PI; if(TheCamera.m_bCamDirectlyInFront) Beta = TheCamera.m_PedOrientForBehindOrInFront; if(OnTrain) Beta = TargetOrientation; Front.x = Cos(Alpha) * -Cos(Beta); Front.y = Cos(Alpha) * -Sin(Beta); Front.z = Sin(Alpha); Source = TargetCoors - Front*CamDist; m_cvecTargetCoorsForFudgeInter = TargetCoors; // Clip Source and fix near clip CWorld::pIgnoreEntity = CamTargetEntity; entity = nil; if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, true, true, true, true, false, false, true)){ float PedColDist = (TargetCoors - colPoint.point).Magnitude(); float ColCamDist = CamDist - PedColDist; if(entity->IsPed() && ColCamDist > DEFAULT_NEAR + 0.1f){ // Ped in the way but not clipping through if(CWorld::ProcessLineOfSight(colPoint.point, Source, colPoint, entity, true, true, true, true, false, false, true)){ PedColDist = (TargetCoors - colPoint.point).Magnitude(); Source = colPoint.point; if(PedColDist < DEFAULT_NEAR + 0.3f) RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); }else{ RwCameraSetNearClipPlane(Scene.camera, Min(ColCamDist-0.35f, DEFAULT_NEAR)); } }else{ Source = colPoint.point; if(PedColDist < DEFAULT_NEAR + 0.3f) RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); } } CWorld::pIgnoreEntity = nil; float ViewPlaneHeight = Tan(DEGTORAD(FOV) / 2.0f); float ViewPlaneWidth = ViewPlaneHeight * CDraw::CalculateAspectRatio() * fTweakFOV; float Near = RwCameraGetNearClipPlane(Scene.camera); float radius = ViewPlaneWidth*Near; entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, false); int i = 0; while(entity){ CVector CamToCol = gaTempSphereColPoints[0].point - Source; float frontDist = DotProduct(CamToCol, Front); float dist = (CamToCol - Front*frontDist).Magnitude() / ViewPlaneWidth; // Try to decrease near clip dist = Max(Min(Near, dist), 0.1f); if(dist < Near) RwCameraSetNearClipPlane(Scene.camera, dist); // Move forward a bit if(dist == 0.1f) Source += (TargetCoors - Source)*0.3f; Near = RwCameraGetNearClipPlane(Scene.camera); #ifndef FIX_BUGS // this is wrong...DEGTORAD missing radius = Tan(FOV / 2.0f) * CDraw::CalculateAspectRatio() * fTweakFOV * Near; #else radius = ViewPlaneWidth*Near; #endif // Keep testing entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, false); i++; if(i > 5) entity = nil; } float TargetDist = (TargetCoors - Source).Magnitude(); if(TargetDist < Distance) Distance = TargetDist; else{ float f = Pow(fMouseAvoidGeomReturnRate, CTimer::GetTimeStep()); Distance = (1.0f - f)*TargetDist + f*Distance; if(TargetDist > 0.05f) Source = TargetCoors + (Source-TargetCoors)*Distance/TargetDist; float clip = Distance-fRangePlayerRadius; if(clip < RwCameraGetNearClipPlane(Scene.camera)) RwCameraSetNearClipPlane(Scene.camera, Max(clip, fCloseNearClipLimit)); } TheCamera.m_bCamDirectlyInFront = false; TheCamera.m_bCamDirectlyBehind = false; GetVectorsReadyForRW(); if(((CPed*)CamTargetEntity)->CanStrafeOrMouseControl() && CDraw::FadeValue < 250 && (TheCamera.GetFadingDirection() != FADE_OUT || CDraw::FadeValue <= 100) && !CPad::GetPad(0)->IsPlayerControlsDisabledBy(PLAYERCONTROL_PLAYERINFO)){ float Heading = Front.Heading(); ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Heading; ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Heading; TheCamera.pTargetEntity->SetHeading(Heading); TheCamera.pTargetEntity->GetMatrix().UpdateRW(); } } float fBillsBetaOffset; // made up name, actually in CCam void CCam::Process_BehindCar(const CVector &CameraTarget, float TargetOrientation, float, float) { FOV = DefaultFOV; if(!CamTargetEntity->IsVehicle()) return; CVector TargetCoors = CameraTarget; TargetCoors.z -= 0.2f; CA_MAX_DISTANCE = 9.95f; CA_MIN_DISTANCE = 8.5f; CVector Dist = Source - TargetCoors; float Length = Dist.Magnitude2D(); m_fDistanceBeforeChanges = Length; if(Length < 0.002f) Length = 0.002f; Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y); #ifdef TOGGLEABLE_BETA_FEATURES // This is completely made up but Bill's cam manipulates an angle before calling this // and otherwise calculating Beta doesn't make much sense. Beta += fBillsBetaOffset; fBillsBetaOffset = 0.0f; Dist.x = -Length*Cos(Beta); Dist.y = -Length*Sin(Beta); Source = TargetCoors + Dist; #endif if(Length > CA_MAX_DISTANCE){ Source.x = TargetCoors.x + Dist.x/Length * CA_MAX_DISTANCE; Source.y = TargetCoors.y + Dist.y/Length * CA_MAX_DISTANCE; }else if(Length < CA_MIN_DISTANCE){ Source.x = TargetCoors.x + Dist.x/Length * CA_MIN_DISTANCE; Source.y = TargetCoors.y + Dist.y/Length * CA_MIN_DISTANCE; } TargetCoors.z += 0.8f; Alpha = DEGTORAD(25.0f); Source.z = TargetCoors.z + CA_MAX_DISTANCE*Sin(Alpha); RotCamIfInFrontCar(TargetCoors, TargetOrientation); m_cvecTargetCoorsForFudgeInter = TargetCoors; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, m_cvecTargetCoorsForFudgeInter, Source, FOV); Front = TargetCoors - Source; ResetStatics = false; GetVectorsReadyForRW(); } float ZmOneAlphaOffset[] = { -0.01f, 0.1f, 0.125f, -0.1f, -0.06f }; float ZmTwoAlphaOffset[] = { 0.045f, 0.12f, 0.045f, 0.045f, -0.035f }; float ZmThreeAlphaOffset[] = { 0.005f, 0.005f, 0.15f, 0.005f, 0.12f }; float INIT_RC_HELI_HORI_EXTRA = 6.0f; float INIT_RC_PLANE_HORI_EXTRA = 9.5f; float INIT_RC_HELI_ALPHA_EXTRA = 0.2f; float INIT_RC_PLANE_ALPHA_EXTRA = 0.295f; void CCam::WorkOutCamHeight(const CVector &TargetCoors, float TargetOrientation, float TargetHeight) { if(!CamTargetEntity->IsVehicle()) return; static float AlphaOffset = 0.0; static float AlphaOffsetSpeed = 0.0; static float AlphaDec = 0.0f; bool isHeli = false; bool isBike = false; int appearance = ((CVehicle*)CamTargetEntity)->GetVehicleAppearance(); if(appearance == VEHICLE_APPEARANCE_BIKE) isBike = true; if(appearance == VEHICLE_APPEARANCE_HELI) isHeli = true; int index = 0; TheCamera.GetArrPosForVehicleType(appearance, index); float ExtraOffset = 0.0f; int id = CamTargetEntity->GetModelIndex(); if(id == MI_RCRAIDER || id == MI_RCGOBLIN) ExtraOffset = INIT_RC_HELI_ALPHA_EXTRA; else if(id == MI_RCBARON) ExtraOffset = INIT_RC_PLANE_ALPHA_EXTRA; if(ResetStatics){ AlphaOffset = 0.0f; AlphaOffsetSpeed = 0.0f; AlphaDec = 0.0f; if(TheCamera.CarZoomIndicator == CAM_ZOOM_1) AlphaOffset = ZmOneAlphaOffset[index] + ExtraOffset; else if(TheCamera.CarZoomIndicator == CAM_ZOOM_2) AlphaOffset = ZmTwoAlphaOffset[index] + ExtraOffset; else if(TheCamera.CarZoomIndicator == CAM_ZOOM_3) AlphaOffset = ZmThreeAlphaOffset[index] + ExtraOffset; } if(TheCamera.CarZoomIndicator == CAM_ZOOM_1) WellBufferMe(ZmOneAlphaOffset[index] + ExtraOffset, &AlphaOffset, &AlphaOffsetSpeed, 0.17f, 0.08f, false); else if(TheCamera.CarZoomIndicator == CAM_ZOOM_2) WellBufferMe(ZmTwoAlphaOffset[index] + ExtraOffset, &AlphaOffset, &AlphaOffsetSpeed, 0.17f, 0.08f, false); else if(TheCamera.CarZoomIndicator == CAM_ZOOM_3) WellBufferMe(ZmThreeAlphaOffset[index] + ExtraOffset, &AlphaOffset, &AlphaOffsetSpeed, 0.17f, 0.08f, false); float Length = (Source - TargetCoors).Magnitude2D(); CVector Forward = CamTargetEntity->GetForward(); float CarAlpha = CGeneral::GetATanOfXY(Forward.Magnitude2D(), Forward.z); // this shouldn't be necessary.... while(CarAlpha >= PI) CarAlpha -= 2*PI; while(CarAlpha < -PI) CarAlpha += 2*PI; while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; float DeltaBeta = Beta - TargetOrientation; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; float BehindCarNess = Cos(DeltaBeta); // 1 if behind car, 0 if side, -1 if in front CarAlpha = -CarAlpha * BehindCarNess; float fwdSpeed = DotProduct(((CPhysical*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward())*180.0f; if(CamTargetEntity->GetModelIndex() == MI_FIRETRUCK && CPad::GetPad(0)->GetCarGunFired()){ CarAlpha = DEGTORAD(10.0f); }else if(isHeli){ CarAlpha = 0.0f; float heliFwdZ = CamTargetEntity->GetForward().z; float heliFwdXY = CamTargetEntity->GetForward().Magnitude2D(); float alphaAmount = Min(Abs(fwdSpeed/90.0f), 1.0f); if(heliFwdXY != 0.0f || heliFwdZ != 0.0f) CarAlpha = CGeneral::GetATanOfXY(heliFwdXY, Abs(heliFwdZ)) * alphaAmount; CColPoint point; CEntity *entity = nil; CVector Test = Source; Test.z = TargetCoors.z + 0.2f + Length*Sin(CarAlpha+AlphaOffset) + m_fCloseInCarHeightOffset; if(CWorld::ProcessVerticalLine(Test, CamTargetEntity->GetPosition().z, point, entity, true, false, false, false, false, false, nil)){ float sin = (point.point.z - TargetCoors.z - 0.2f - m_fCloseInCarHeightOffset)/Length; CarAlpha = Asin(clamp(sin, -1.0f, 1.0f)) - AlphaOffset; if(CarAlpha < 0.0f) AlphaOffset += CarAlpha; } } CarAlpha = CGeneral::LimitRadianAngle(CarAlpha); if(CarAlpha < 0.0f) CarAlpha = 0.0f; if(CarAlpha > DEGTORAD(89.0f)) CarAlpha = DEGTORAD(89.0f); if(ResetStatics) Alpha = CarAlpha; float TargetAlpha = Alpha; float DeltaAlpha = CarAlpha - TargetAlpha; while(DeltaAlpha >= PI) DeltaAlpha -= 2*PI; while(DeltaAlpha < -PI) DeltaAlpha += 2*PI; if(Abs(DeltaAlpha) > 0.0f && !TheCamera.m_bVehicleSuspenHigh) TargetAlpha = CarAlpha; if(isBike) WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.09f, 0.04f, true); else if(isHeli) WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.09f, 0.04f, true); else WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.15f, 0.07f, true); Source.z = TargetCoors.z + Sin(Alpha + AlphaOffset)*Length + m_fCloseInCarHeightOffset; AlphaOffset -= AlphaDec; } // Rotate cam behind the car when the car is moving forward bool CCam::RotCamIfInFrontCar(CVector &TargetCoors, float TargetOrientation) { float BetaMaxSpeed = 0.15f; float BetaAcceleration = 0.007f; bool MovingForward = false; float MaxDiffBeta = DEGTORAD(160.0f); CPhysical *phys = (CPhysical*)CamTargetEntity; float ForwardSpeed = DotProduct(phys->GetForward(), phys->GetSpeed(CVector(0.0f, 0.0f, 0.0f))); if(ForwardSpeed > 0.02f) MovingForward = true; if(phys->IsVehicle() && (phys->GetModelIndex() == MI_SPARROW || phys->GetModelIndex() == MI_HUNTER)){ MaxDiffBeta = DEGTORAD(160.0f); BetaMaxSpeed = 0.1f; BetaAcceleration = 0.003f; CVector speed = phys->GetSpeed(CVector(0.0f, 0.0f, 0.0f)); speed.z = 0.0f; if(50.0f*speed.Magnitude() > 3.13f) TargetOrientation = CGeneral::GetATanOfXY(speed.x, speed.y); } float Dist = (Source - TargetCoors).Magnitude2D(); float DeltaBeta = TargetOrientation - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; if(Abs(DeltaBeta) > PI-MaxDiffBeta && MovingForward && TheCamera.m_uiTransitionState == 0) m_bFixingBeta = true; CPad *pad = CPad::GetPad(0); if(!(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight())) if(DirectionWasLooking != LOOKING_FORWARD) TheCamera.m_bCamDirectlyBehind = true; if(!m_bFixingBeta && !TheCamera.m_bUseTransitionBeta && !TheCamera.m_bCamDirectlyBehind && !TheCamera.m_bCamDirectlyInFront) return false; bool SetBeta = false; if(TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront || TheCamera.m_bUseTransitionBeta) if(&TheCamera.Cams[TheCamera.ActiveCam] == this) SetBeta = true; if(m_bFixingBeta || SetBeta){ WellBufferMe(TargetOrientation, &Beta, &BetaSpeed, BetaMaxSpeed, BetaAcceleration, true); if(TheCamera.m_bCamDirectlyBehind && &TheCamera.Cams[TheCamera.ActiveCam] == this) Beta = TargetOrientation; if(TheCamera.m_bCamDirectlyInFront && &TheCamera.Cams[TheCamera.ActiveCam] == this) Beta = TargetOrientation + PI; if(TheCamera.m_bUseTransitionBeta && &TheCamera.Cams[TheCamera.ActiveCam] == this) Beta = m_fTransitionBeta; Source.x = TargetCoors.x - Cos(Beta)*Dist; Source.y = TargetCoors.y - Sin(Beta)*Dist; // Check if we're done DeltaBeta = TargetOrientation - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; if(Abs(DeltaBeta) < DEGTORAD(2.0f)) m_bFixingBeta = false; } TheCamera.m_bCamDirectlyBehind = false; TheCamera.m_bCamDirectlyInFront = false; return true; } float FIRETRUCK_TRACKING_MULT = 0.1f; float fTestShiftHeliCamTarget = 0.6f; float TiltTopSpeed[] = { 0.035f, 0.035f, 0.001f, 0.005f, 0.035f }; float TiltSpeedStep[] = { 0.016f, 0.016f, 0.0002f, 0.0014f, 0.016f }; float TiltOverShoot[] = { 1.05f, 1.05f, 0.0f, 0.0f, 1.0f }; void CCam::Process_Cam_On_A_String(const CVector &CameraTarget, float TargetOrientation, float, float) { if(!CamTargetEntity->IsVehicle()) return; // unused // ((CVehicle*)CamTargetEntity)->GetVehicleAppearance(); FOV = DefaultFOV; if(ResetStatics){ AlphaSpeed = 0.0f; m_fTilt = 0.0f; m_fTiltSpeed = 0.0; } CBaseModelInfo *mi = CModelInfo::GetModelInfo(CamTargetEntity->GetModelIndex()); CVector Dimensions = mi->GetColModel()->boundingBox.max - mi->GetColModel()->boundingBox.min; CVector TargetCoors = CameraTarget; float BaseDist = Dimensions.Magnitude(); if(((CVehicle*)CamTargetEntity)->IsBike()) BaseDist *= 1.45f; if(((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI && CamTargetEntity->GetStatus() != STATUS_PLAYER_REMOTE) TargetCoors += fTestShiftHeliCamTarget * CamTargetEntity->GetUp() * Dimensions.z; else TargetCoors.z += 0.8f*Dimensions.z; Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y); Alpha = CGeneral::LimitRadianAngle(Alpha); Beta = CGeneral::LimitRadianAngle(Beta); if(CamTargetEntity->GetModelIndex() == MI_FIRETRUCK && CPad::GetPad(0)->GetCarGunFired() && ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed.Magnitude2D() < 0.01f){ float TargetBeta = CamTargetEntity->GetForward().Heading() - ((CAutomobile*)CamTargetEntity)->m_fCarGunLR + HALFPI; TargetBeta = CGeneral::LimitRadianAngle(TargetBeta); float DeltaBeta = TargetBeta - Beta; if(DeltaBeta > PI) DeltaBeta -= TWOPI; else if(DeltaBeta < -PI) DeltaBeta += TWOPI; float dist = (TargetCoors - Source).Magnitude(); dist = FIRETRUCK_TRACKING_MULT*dist*clamp(DeltaBeta, -0.8f, 0.8f); Source += dist*CrossProduct(Front, CVector(0.0f, 0.0f, 1.0f)); } m_fDistanceBeforeChanges = (Source - TargetCoors).Magnitude2D(); Cam_On_A_String_Unobscured(TargetCoors, BaseDist); WorkOutCamHeight(TargetCoors, TargetOrientation, Dimensions.z); RotCamIfInFrontCar(TargetCoors, TargetOrientation); FixCamWhenObscuredByVehicle(TargetCoors); m_cvecTargetCoorsForFudgeInter = TargetCoors; CVector OrigSource = Source; if(CWorld::GetIsLineOfSightClear(CamTargetEntity->GetPosition(), m_cvecTargetCoorsForFudgeInter, true, false, false, true, false, false, true)) TheCamera.AvoidTheGeometry(OrigSource, m_cvecTargetCoorsForFudgeInter, Source, FOV); else TheCamera.AvoidTheGeometry(OrigSource, CamTargetEntity->GetPosition(), Source, FOV); Front = TargetCoors - Source; Front.Normalise(); int appearance = ((CVehicle*)CamTargetEntity)->GetVehicleAppearance(); int index = 0; TheCamera.GetArrPosForVehicleType(appearance, index); if(appearance == VEHICLE_APPEARANCE_HELI){ float TargetTilt = DotProduct(Front, ((CVehicle*)CamTargetEntity)->GetSpeed(CVector(0.0f, 0.0f, 0.0f))); CVector UpTarget = CamTargetEntity->GetUp(); UpTarget.Normalise(); int dir = TargetTilt < 0.0f ? -1 : 1; if(m_fTilt != 0.0f) TargetTilt += TiltOverShoot[index]*TargetTilt/m_fTilt * dir; WellBufferMe(TargetTilt, &m_fTilt, &m_fTiltSpeed, TiltTopSpeed[index], TiltSpeedStep[index], false); Up = CVector(0.0f, 0.0f, 1.0f) - (CVector(0.0f, 0.0f, 1.0f) - UpTarget)*m_fTilt; Up.Normalise(); Front.Normalise(); CVector Left = CrossProduct(Up, Front); Up = CrossProduct(Front, Left); Up.Normalise(); }else{ float TargetRoll; if(CPad::GetPad(0)->GetDPadLeft() || CPad::GetPad(0)->GetDPadRight()){ float fwdSpeed = 180.0f*DotProduct(((CVehicle*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward()); if(fwdSpeed > 210.0f) fwdSpeed = 210.0f; if(CPad::GetPad(0)->GetDPadLeft()) TargetRoll = DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle; else TargetRoll = -(DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle); CVector FwdTarget = CamTargetEntity->GetForward(); FwdTarget.Normalise(); float AngleDiff = DotProduct(FwdTarget, Front); AngleDiff = Acos(Min(Abs(AngleDiff), 1.0f)); TargetRoll *= fwdSpeed/210.0f * Sin(AngleDiff); }else{ float fwdSpeed = 180.0f*DotProduct(((CVehicle*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward()); if(fwdSpeed > 210.0f) fwdSpeed = 210.0f; TargetRoll = CPad::GetPad(0)->GetLeftStickX()/128.0f * fwdSpeed/210.0f; CVector FwdTarget = CamTargetEntity->GetForward(); FwdTarget.Normalise(); float AngleDiff = DotProduct(FwdTarget, Front); AngleDiff = Acos(Min(Abs(AngleDiff), 1.0f)); TargetRoll *= (DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle) * Sin(AngleDiff); } WellBufferMe(TargetRoll, &f_Roll, &f_rollSpeed, 0.15f, 0.07f, false); Up = CVector(Cos(f_Roll + HALFPI), 0.0f, Sin(f_Roll + HALFPI)); Up.Normalise(); Front.Normalise(); CVector Left = CrossProduct(Up, Front); Left.Normalise(); Up = CrossProduct(Front, Left); Up.Normalise(); } ResetStatics = false; } // Basic Cam on a string algorithm void CCam::Cam_On_A_String_Unobscured(const CVector &TargetCoors, float BaseDist) { int id = CamTargetEntity->GetModelIndex(); float ExtraDist = 0.0f; if(id == MI_RCRAIDER || id == MI_RCGOBLIN) ExtraDist = INIT_RC_HELI_HORI_EXTRA; else if(id == MI_RCBARON) ExtraDist = INIT_RC_PLANE_HORI_EXTRA; CA_MAX_DISTANCE = BaseDist + 0.1f + TheCamera.CarZoomValueSmooth + ExtraDist; CA_MIN_DISTANCE = Min(BaseDist*0.6f, 3.5f); if(CA_MIN_DISTANCE > CA_MAX_DISTANCE) CA_MIN_DISTANCE = CA_MAX_DISTANCE - 0.05f; CVector Dist = Source - TargetCoors; if(ResetStatics) Source = TargetCoors + Dist*(CA_MAX_DISTANCE + 1.0f); Dist = Source - TargetCoors; float Length = Dist.Magnitude2D(); if(Length < 0.001f){ // This probably shouldn't happen. reset view CVector Forward = CamTargetEntity->GetForward(); Forward.z = 0.0f; Forward.Normalise(); Source = TargetCoors - Forward*CA_MAX_DISTANCE; Dist = Source - TargetCoors; Length = Dist.Magnitude2D(); } if(Length > CA_MAX_DISTANCE){ Source.x = TargetCoors.x + Dist.x/Length * CA_MAX_DISTANCE; Source.y = TargetCoors.y + Dist.y/Length * CA_MAX_DISTANCE; }else if(Length < CA_MIN_DISTANCE){ Source.x = TargetCoors.x + Dist.x/Length * CA_MIN_DISTANCE; Source.y = TargetCoors.y + Dist.y/Length * CA_MIN_DISTANCE; } } void CCam::FixCamWhenObscuredByVehicle(const CVector &TargetCoors) { // BUG? is this never reset static float HeightFixerCarsObscuring = 0.0f; static float HeightFixerCarsObscuringSpeed = 0.0f; CColPoint colPoint; CEntity *entity = nil; float HeightTarget = 0.0f; if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, false, true, false, false, false, false, false)){ CBaseModelInfo *mi = CModelInfo::GetModelInfo(entity->GetModelIndex()); HeightTarget = mi->GetColModel()->boundingBox.max.z + 1.0f + TargetCoors.z - Source.z; if(HeightTarget < 0.0f) HeightTarget = 0.0f; } WellBufferMe(HeightTarget, &HeightFixerCarsObscuring, &HeightFixerCarsObscuringSpeed, 0.2f, 0.025f, false); Source.z += HeightFixerCarsObscuring; } void CCam::Process_TopDown(const CVector &CameraTarget, float TargetOrientation, float SpeedVar, float TargetSpeedVar) { FOV = DefaultFOV; if(!CamTargetEntity->IsVehicle()) return; float Dist; float HeightTarget = 0.0f; static float AdjustHeightTargetMoveBuffer = 0.0f; static float AdjustHeightTargetMoveSpeed = 0.0f; static float NearClipDistance = 1.5f; const float FarClipDistance = 200.0f; CVector TargetFront, Target; CVector TestSource, TestTarget; CColPoint colPoint; CEntity *entity; TargetFront = CameraTarget; TargetFront.x += 18.0f*CamTargetEntity->GetForward().x*SpeedVar; TargetFront.y += 18.0f*CamTargetEntity->GetForward().y*SpeedVar; if(ResetStatics){ AdjustHeightTargetMoveBuffer = 0.0f; AdjustHeightTargetMoveSpeed = 0.0f; } float f = Pow(0.8f, 4.0f); Target = f*CameraTarget + (1.0f-f)*TargetFront; if(Mode == MODE_GTACLASSIC) SpeedVar = TargetSpeedVar; Source = Target + CVector(0.0f, 0.0f, (40.0f*SpeedVar + 30.0f)*0.8f); // What is this? looks horrible if(Mode == MODE_GTACLASSIC) Source.x += (uint8)(100.0f*CameraTarget.x)/500.0f; TestSource = Source; TestTarget = TestSource; TestTarget.z = Target.z; if(CWorld::ProcessLineOfSight(TestTarget, TestSource, colPoint, entity, true, false, false, false, false, false, false)){ if(Source.z < colPoint.point.z+3.0f) HeightTarget = colPoint.point.z+3.0f - Source.z; }else{ TestSource = Source; TestTarget = TestSource; TestTarget.z += 10.0f; if(CWorld::ProcessLineOfSight(TestTarget, TestSource, colPoint, entity, true, false, false, false, false, false, false)) if(Source.z < colPoint.point.z+3.0f) HeightTarget = colPoint.point.z+3.0f - Source.z; } WellBufferMe(HeightTarget, &AdjustHeightTargetMoveBuffer, &AdjustHeightTargetMoveSpeed, 0.2f, 0.02f, false); Source.z += AdjustHeightTargetMoveBuffer; if(RwCameraGetFarClipPlane(Scene.camera) > FarClipDistance) RwCameraSetFarClipPlane(Scene.camera, FarClipDistance); RwCameraSetNearClipPlane(Scene.camera, NearClipDistance); Front = CVector(-0.01f, -0.01f, -1.0f); // look down Front.Normalise(); Dist = (Source - CameraTarget).Magnitude(); m_cvecTargetCoorsForFudgeInter = Dist*Front + Source; Up = CVector(0.0f, 1.0f, 0.0f); ResetStatics = false; } void CCam::AvoidWallsTopDownPed(const CVector &TargetCoors, const CVector &Offset, float *Adjuster, float *AdjusterSpeed, float yDistLimit) { float Target = 0.0f; float MaxSpeed = 0.13f; float Acceleration = 0.015f; float SpeedMult; float dy; CVector TestPoint2; CVector TestPoint1; CColPoint colPoint; CEntity *entity; TestPoint2 = TargetCoors + Offset; TestPoint1 = TargetCoors; TestPoint1.z = TestPoint2.z; if(CWorld::ProcessLineOfSight(TestPoint1, TestPoint2, colPoint, entity, true, false, false, false, false, false, false)){ // What is this even? dy = TestPoint1.y - colPoint.point.y; if(dy > yDistLimit) dy = yDistLimit; SpeedMult = yDistLimit - Abs(dy/yDistLimit); Target = 2.5f; MaxSpeed += SpeedMult*0.3f; Acceleration += SpeedMult*0.03f; } WellBufferMe(Target, Adjuster, AdjusterSpeed, MaxSpeed, Acceleration, false); } void CCam::Process_TopDownPed(const CVector &CameraTarget, float TargetOrientation, float, float) { if(!CamTargetEntity->IsPed()) return; float Dist; float HeightTarget; static int NumPedPosCountsSoFar = 0; static float PedAverageSpeed = 0.0f; static float AdjustHeightTargetMoveBuffer = 0.0f; static float AdjustHeightTargetMoveSpeed = 0.0f; static float PedSpeedSoFar = 0.0f; static float FarClipDistance = 200.0f; static float NearClipDistance = 1.5f; static float TargetAdjusterForSouth = 0.0f; static float TargetAdjusterSpeedForSouth = 0.0f; static float TargetAdjusterForNorth = 0.0f; static float TargetAdjusterSpeedForNorth = 0.0f; static float TargetAdjusterForEast = 0.0f; static float TargetAdjusterSpeedForEast = 0.0f; static float TargetAdjusterForWest = 0.0f; static float TargetAdjusterSpeedForWest = 0.0f; static CVector PreviousPlayerMoveSpeedVec; CVector TargetCoors, PlayerMoveSpeed; CVector TestSource, TestTarget; CColPoint colPoint; CEntity *entity; FOV = DefaultFOV; TargetCoors = CameraTarget; PlayerMoveSpeed = ((CPed*)CamTargetEntity)->GetMoveSpeed(); if(ResetStatics){ PreviousPlayerMoveSpeedVec = PlayerMoveSpeed; AdjustHeightTargetMoveBuffer = 0.0f; AdjustHeightTargetMoveSpeed = 0.0f; NumPedPosCountsSoFar = 0; PedSpeedSoFar = 0.0f; PedAverageSpeed = 0.0f; TargetAdjusterForWest = 0.0f; TargetAdjusterSpeedForWest = 0.0f; TargetAdjusterForEast = 0.0f; TargetAdjusterSpeedForEast = 0.0f; TargetAdjusterForNorth = 0.0f; TargetAdjusterSpeedForNorth = 0.0f; TargetAdjusterForSouth = 0.0f; TargetAdjusterSpeedForSouth = 0.0f; } if(RwCameraGetFarClipPlane(Scene.camera) > FarClipDistance) RwCameraSetFarClipPlane(Scene.camera, FarClipDistance); RwCameraSetNearClipPlane(Scene.camera, NearClipDistance); // Average ped speed NumPedPosCountsSoFar++; PedSpeedSoFar += PlayerMoveSpeed.Magnitude(); if(NumPedPosCountsSoFar == 5){ PedAverageSpeed = 0.4f*PedAverageSpeed + 0.6*(PedSpeedSoFar/5.0f); NumPedPosCountsSoFar = 0; PedSpeedSoFar = 0.0f; } PreviousPlayerMoveSpeedVec = PlayerMoveSpeed; // Zoom out depending on speed if(PedAverageSpeed > 0.01f && PedAverageSpeed <= 0.04f) HeightTarget = 2.5f; else if(PedAverageSpeed > 0.04f && PedAverageSpeed <= 0.145f) HeightTarget = 4.5f; else if(PedAverageSpeed > 0.145f) HeightTarget = 7.0f; else HeightTarget = 0.0f; // Zoom out if locked on target is far away if(FindPlayerPed()->m_pPointGunAt){ Dist = (FindPlayerPed()->m_pPointGunAt->GetPosition() - CameraTarget).Magnitude2D(); if(Dist > 6.0f) HeightTarget = Max(HeightTarget, Dist/22.0f*37.0f); } Source = TargetCoors + CVector(0.0f, -1.0f, 9.0f); // Collision checks entity = nil; TestSource = TargetCoors + CVector(0.0f, -1.0f, 9.0f); TestTarget = TestSource; TestTarget.z = TargetCoors.z; if(CWorld::ProcessLineOfSight(TestTarget, TestSource, colPoint, entity, true, false, false, false, false, false, false)){ if(TargetCoors.z+9.0f+HeightTarget < colPoint.point.z+3.0f) HeightTarget = colPoint.point.z+3.0f - (TargetCoors.z+9.0f); }else{ TestSource = TargetCoors + CVector(0.0f, -1.0f, 9.0f); TestTarget = TestSource; TestSource.z += HeightTarget; TestTarget.z = TestSource.z + 10.0f; if(CWorld::ProcessLineOfSight(TestTarget, TestSource, colPoint, entity, true, false, false, false, false, false, false)){ if(TargetCoors.z+9.0f+HeightTarget < colPoint.point.z+3.0f) HeightTarget = colPoint.point.z+3.0f - (TargetCoors.z+9.0f); } } WellBufferMe(HeightTarget, &AdjustHeightTargetMoveBuffer, &AdjustHeightTargetMoveSpeed, 0.3f, 0.03f, false); Source.z += AdjustHeightTargetMoveBuffer; // Wall checks AvoidWallsTopDownPed(TargetCoors, CVector(0.0f, -3.0f, 3.0f), &TargetAdjusterForSouth, &TargetAdjusterSpeedForSouth, 1.0f); Source.y += TargetAdjusterForSouth; AvoidWallsTopDownPed(TargetCoors, CVector(0.0f, 3.0f, 3.0f), &TargetAdjusterForNorth, &TargetAdjusterSpeedForNorth, 1.0f); Source.y -= TargetAdjusterForNorth; // BUG: east and west flipped AvoidWallsTopDownPed(TargetCoors, CVector(3.0f, 0.0f, 3.0f), &TargetAdjusterForWest, &TargetAdjusterSpeedForWest, 1.0f); Source.x -= TargetAdjusterForWest; AvoidWallsTopDownPed(TargetCoors, CVector(-3.0f, 0.0f, 3.0f), &TargetAdjusterForEast, &TargetAdjusterSpeedForEast, 1.0f); Source.x += TargetAdjusterForEast; TargetCoors.y = Source.y + 1.0f; TargetCoors.y += TargetAdjusterForSouth; TargetCoors.x += TargetAdjusterForEast; TargetCoors.x -= TargetAdjusterForWest; Front = TargetCoors - Source; Front.Normalise(); #ifdef FIX_BUGS if(Front.x == 0.0f && Front.y == 0.0f) Front.y = 0.0001f; #else // someone used = instead of == in the above check by accident Front.x = 0.0f; #endif m_cvecTargetCoorsForFudgeInter = TargetCoors; Up = CrossProduct(Front, CVector(-1.0f, 0.0f, 0.0f)); Up.Normalise(); ResetStatics = false; } void CCam::Process_Rocket(const CVector &CameraTarget, float, float, float) { if(!CamTargetEntity->IsPed()) return; float BackOffset = 0.19f; static bool FailedTestTwelveFramesAgo = false; RwV3d HeadPos; CVector TargetCoors; FOV = DefaultFOV; TargetCoors = CameraTarget; if(ResetStatics){ Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; Alpha = 0.0f; m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; FailedTestTwelveFramesAgo = false; // static DPadVertical unused // static DPadHorizontal unused m_bCollisionChecksOn = true; ResetStatics = false; } if(((CPed*)CamTargetEntity)->bIsDucking) BackOffset = 0.8f; CamTargetEntity->GetMatrix().UpdateRW(); CamTargetEntity->UpdateRwFrame(); CamTargetEntity->UpdateRpHAnim(); ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); Source = HeadPos; Source.z += 0.1f; Source.x -= BackOffset*Cos(m_fInitialPlayerOrientation); Source.y -= BackOffset*Sin(m_fInitialPlayerOrientation); // Look around bool UseMouse = false; float MouseX = CPad::GetPad(0)->GetMouseX(); float MouseY = CPad::GetPad(0)->GetMouseY(); float LookLeftRight, LookUpDown; if(MouseX != 0.0f || MouseY != 0.0f){ UseMouse = true; LookLeftRight = -3.0f*MouseX; LookUpDown = 4.0f*MouseY; }else{ LookLeftRight = -CPad::GetPad(0)->SniperModeLookLeftRight(); LookUpDown = CPad::GetPad(0)->SniperModeLookUpDown(); } if(UseMouse){ Beta += TheCamera.m_fMouseAccelHorzntl * LookLeftRight * FOV/80.0f; Alpha += TheCamera.m_fMouseAccelVertical * LookUpDown * FOV/80.0f; }else{ float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); } while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; Front = TargetCoors - Source; Front.Normalise(); Source += Front*0.4f; if(m_bCollisionChecksOn){ if(!CWorld::GetIsLineOfSightClear(TargetCoors, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else{ CVector TestPoint; TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta + DEGTORAD(35.0f)) + Source.x; TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta + DEGTORAD(35.0f)) + Source.y; TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else{ TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta - DEGTORAD(35.0f)) + Source.x; TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta - DEGTORAD(35.0f)) + Source.y; TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else FailedTestTwelveFramesAgo = false; } } } if(FailedTestTwelveFramesAgo) RwCameraSetNearClipPlane(Scene.camera, 0.4f); Source -= Front*0.4f; GetVectorsReadyForRW(); float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; } float fDuckingBackOffset = 0.5f; float fDuckingRightOffset = 0.18f; void CCam::Process_M16_1stPerson(const CVector &CameraTarget, float, float, float) { if(!CamTargetEntity->IsPed()) return; float BackOffset = 0.3f; static bool FailedTestTwelveFramesAgo = false; RwV3d HeadPos; CVector TargetCoors; bool isAttached = ((CPed*)CamTargetEntity)->IsPlayer() && ((CPed*)CamTargetEntity)->m_attachedTo; FOV = DefaultFOV; TargetCoors = CameraTarget; if(ResetStatics){ if(isAttached) Beta = 0.0f; else Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; Alpha = 0.0f; m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; FailedTestTwelveFramesAgo = false; // static DPadVertical unused // static DPadHorizontal unused m_bCollisionChecksOn = true; ResetStatics = false; } // Look around bool UseMouse = false; float MouseX = CPad::GetPad(0)->GetMouseX(); float MouseY = CPad::GetPad(0)->GetMouseY(); float LookLeftRight, LookUpDown; if(MouseX != 0.0f || MouseY != 0.0f){ UseMouse = true; LookLeftRight = -3.0f*MouseX; LookUpDown = 4.0f*MouseY; }else{ LookLeftRight = -CPad::GetPad(0)->SniperModeLookLeftRight(); LookUpDown = CPad::GetPad(0)->SniperModeLookUpDown(); } if(UseMouse){ Beta += TheCamera.m_fMouseAccelHorzntl * LookLeftRight * FOV/80.0f; Alpha += TheCamera.m_fMouseAccelVertical * LookUpDown * FOV/80.0f; }else if(Mode == MODE_HELICANNON_1STPERSON){ LookLeftRight /= 128.0f; LookUpDown /= 128.0f; Beta += LookLeftRight*Abs(LookLeftRight)*0.56f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); Alpha += LookUpDown*Abs(LookUpDown)*0.48f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); }else{ float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); } if (!isAttached) { while(Beta >= TWOPI) Beta -= TWOPI; while(Beta < 0) Beta += TWOPI; } if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); if(((CPed*)CamTargetEntity)->bIsDucking) BackOffset = 0.8f; if(isAttached){ CMatrix mat, rot; CPed *TargetPed = (CPed*)CamTargetEntity; TargetPed->PositionAttachedPed(); CamTargetEntity->GetMatrix().UpdateRW(); CamTargetEntity->UpdateRwFrame(); CamTargetEntity->UpdateRpHAnim(); HeadPos.x = 0.0f; HeadPos.y = 0.0f; HeadPos.z = 0.0f; TargetPed->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); Source = HeadPos; Source += 0.1f*CamTargetEntity->GetUp(); Source -= BackOffset*CamTargetEntity->GetForward(); if(TargetPed->m_attachRotStep < PI){ if(Beta > TargetPed->m_attachRotStep){ Beta = TargetPed->m_attachRotStep; CAutomobile *heli = (CAutomobile*)TargetPed->m_attachedTo; if(heli->IsVehicle() && heli->IsCar() && heli->IsRealHeli() && heli->m_fHeliOrientation > 0.0f){ float heliOrient = heli->m_fHeliOrientation + CTimer::GetTimeStep()*0.01f; if(heliOrient < 0.0f) heliOrient += TWOPI; else if(heliOrient > TWOPI) heliOrient -= TWOPI; heli->SetHeliOrientation(heliOrient); } }else if(Beta < -TargetPed->m_attachRotStep){ Beta = -TargetPed->m_attachRotStep; CAutomobile *heli = (CAutomobile*)TargetPed->m_attachedTo; if(heli->IsVehicle() && heli->IsCar() && heli->IsRealHeli() && heli->m_fHeliOrientation > 0.0f){ float heliOrient = heli->m_fHeliOrientation - CTimer::GetTimeStep()*0.01f; if(heliOrient < 0.0f) heliOrient += TWOPI; else if(heliOrient > TWOPI) heliOrient -= TWOPI; heli->SetHeliOrientation(heliOrient); } } }else{ while(Beta < -PI) Beta += TWOPI; while(Beta >= PI) Beta -= TWOPI; } mat = TargetPed->m_attachedTo->GetMatrix(); rot.SetRotateX(Alpha); switch(TargetPed->m_attachType){ case 0: rot.RotateZ(Beta); break; case 1: rot.RotateZ(Beta + HALFPI); break; case 2: rot.RotateZ(Beta + PI); break; case 3: rot.RotateZ(Beta - HALFPI); break; } mat = mat * rot; Front = mat.GetForward(); Up = mat.GetUp(); TargetCoors = Source + 3.0f*Front; RwCameraSetNearClipPlane(Scene.camera, 0.4f); float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; }else{ CamTargetEntity->GetMatrix().UpdateRW(); CamTargetEntity->UpdateRwFrame(); CamTargetEntity->UpdateRpHAnim(); HeadPos.x = 0.0f; HeadPos.y = 0.0f; HeadPos.z = 0.0f; ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); Source = HeadPos; Source.z += 0.1f; if(((CPed*)CamTargetEntity)->bIsDucking){ Source.x -= fDuckingBackOffset*CamTargetEntity->GetForward().x; Source.y -= fDuckingBackOffset*CamTargetEntity->GetForward().y; Source.x -= fDuckingRightOffset*CamTargetEntity->GetRight().x; Source.y -= fDuckingRightOffset*CamTargetEntity->GetRight().y; }else{ Source.x -= BackOffset*CamTargetEntity->GetForward().x; Source.y -= BackOffset*CamTargetEntity->GetForward().y; } TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; Front = TargetCoors - Source; Front.Normalise(); Source += Front*0.4f; if(m_bCollisionChecksOn){ if(!CWorld::GetIsLineOfSightClear(TargetCoors, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else{ CVector TestPoint; TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta + DEGTORAD(35.0f)) + Source.x; TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta + DEGTORAD(35.0f)) + Source.y; TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else{ TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta - DEGTORAD(35.0f)) + Source.x; TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta - DEGTORAD(35.0f)) + Source.y; TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else FailedTestTwelveFramesAgo = false; } } } if(FailedTestTwelveFramesAgo) RwCameraSetNearClipPlane(Scene.camera, 0.4f); Source -= Front*0.4f; GetVectorsReadyForRW(); float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; } } float fBike1stPersonOffsetZ = 0.15f; void CCam::Process_1stPerson(const CVector &CameraTarget, float TargetOrientation, float SpeedVar, float TargetSpeedVar) { float BackOffset = 0.3f; static float DontLookThroughWorldFixer = 0.0f; CVector TargetCoors; FOV = DefaultFOV; TargetCoors = CameraTarget; if(CamTargetEntity->m_rwObject == nil) return; if(ResetStatics){ Beta = TargetOrientation; Alpha = 0.0f; m_fInitialPlayerOrientation = TargetOrientation; if(CamTargetEntity->IsPed()){ Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; Alpha = 0.0f; m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; } TheCamera.m_fAvoidTheGeometryProbsTimer = 0.0f; DontLookThroughWorldFixer = 0.0f; } if(CamTargetEntity->IsPed()){ static bool FailedTestTwelveFramesAgo = false; RwV3d HeadPos; TargetCoors = CameraTarget; if(ResetStatics){ Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; Alpha = 0.0f; m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; FailedTestTwelveFramesAgo = false; // static DPadVertical unused // static DPadHorizontal unused m_bCollisionChecksOn = true; ResetStatics = false; } CamTargetEntity->GetMatrix().UpdateRW(); CamTargetEntity->UpdateRwFrame(); CamTargetEntity->UpdateRpHAnim(); ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); Source = HeadPos; Source.z += 0.1f; if(((CPed*)CamTargetEntity)->bIsDucking){ Source.x -= fDuckingBackOffset*CamTargetEntity->GetForward().x; Source.y -= fDuckingBackOffset*CamTargetEntity->GetForward().y; Source.x -= fDuckingRightOffset*CamTargetEntity->GetRight().x; Source.y -= fDuckingRightOffset*CamTargetEntity->GetRight().y; }else{ Source.x -= BackOffset*CamTargetEntity->GetForward().x; Source.y -= BackOffset*CamTargetEntity->GetForward().y; } float LookLeftRight, LookUpDown; LookLeftRight = -CPad::GetPad(0)->LookAroundLeftRight(); LookUpDown = CPad::GetPad(0)->LookAroundUpDown(); float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; Front = TargetCoors - Source; Front.Normalise(); Source += Front*0.4f; if(m_bCollisionChecksOn){ if(!CWorld::GetIsLineOfSightClear(TargetCoors, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else{ CVector TestPoint; TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta + DEGTORAD(35.0f)) + Source.x; TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta + DEGTORAD(35.0f)) + Source.y; TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else{ TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta - DEGTORAD(35.0f)) + Source.x; TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta - DEGTORAD(35.0f)) + Source.y; TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else FailedTestTwelveFramesAgo = false; } } } if(FailedTestTwelveFramesAgo) RwCameraSetNearClipPlane(Scene.camera, 0.4f); Source -= Front*0.4f; GetVectorsReadyForRW(); float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; }else{ assert(CamTargetEntity->IsVehicle()); if(((CVehicle*)CamTargetEntity)->IsBike() && (((CBike*)CamTargetEntity)->bWheelieCam || TheCamera.m_fAvoidTheGeometryProbsTimer > 0.0f)){ if(CPad::GetPad(0)->GetLeftShoulder2() || CPad::GetPad(0)->GetRightShoulder2()){ TheCamera.m_fAvoidTheGeometryProbsTimer = 0.0f; ((CBike*)CamTargetEntity)->bWheelieCam = false; }else if(Process_WheelCam(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar)){ if(((CBike*)CamTargetEntity)->bWheelieCam) TheCamera.m_fAvoidTheGeometryProbsTimer = 50.0f; else{ TheCamera.m_fAvoidTheGeometryProbsTimer -= CTimer::GetTimeStep(); ((CBike*)CamTargetEntity)->bWheelieCam = true; } return; }else{ TheCamera.m_fAvoidTheGeometryProbsTimer = 0.0f; ((CBike*)CamTargetEntity)->bWheelieCam = false; } } CMatrix *matrix = &CamTargetEntity->GetMatrix(); if(((CVehicle*)CamTargetEntity)->IsBike()){ ((CBike*)CamTargetEntity)->CalculateLeanMatrix(); matrix = &((CBike*)CamTargetEntity)->m_leanMatrix; } CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(CamTargetEntity->GetModelIndex()); CVector CamPos = mi->GetFrontSeatPosn(); CamPos.x = 0.0f; CamPos.y += 0.08f; CamPos.z += 0.62f; FOV = 60.0f; Source = Multiply3x3(*matrix, CamPos); Source += CamTargetEntity->GetPosition(); if(((CVehicle*)CamTargetEntity)->IsBoat()) Source.z += 0.5f; else if(((CVehicle*)CamTargetEntity)->IsBike() && ((CVehicle*)CamTargetEntity)->pDriver){ CVector Neck(0.0f, 0.0f, 0.0f); ((CVehicle*)CamTargetEntity)->pDriver->m_pedIK.GetComponentPosition(Neck, PED_NECK); Neck += ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed * CTimer::GetTimeStep(); Source.z = Neck.z + fBike1stPersonOffsetZ; } if(((CVehicle*)CamTargetEntity)->IsUpsideDown()){ if(DontLookThroughWorldFixer < 0.5f) DontLookThroughWorldFixer += 0.03f; else DontLookThroughWorldFixer = 0.5f; }else{ if(DontLookThroughWorldFixer < 0.0f) #ifdef FIX_BUGS DontLookThroughWorldFixer += 0.03f; #else DontLookThroughWorldFixer -= 0.03f; #endif else DontLookThroughWorldFixer = 0.0f; } Source.z += DontLookThroughWorldFixer; Front = matrix->GetForward(); Front.Normalise(); Up = matrix->GetUp(); Up.Normalise(); CVector Right = CrossProduct(Front, Up); Right.Normalise(); Up = CrossProduct(Right, Front); Up.Normalise(); } ResetStatics = false; } static CVector vecHeadCamOffset(0.06f, 0.05f, 0.0f); void CCam::Process_1rstPersonPedOnPC(const CVector&, float TargetOrientation, float, float) { // static int DontLookThroughWorldFixer = 0; // unused static CVector InitialHeadPos; if(Mode != MODE_SNIPER_RUNABOUT) FOV = DefaultFOV; TheCamera.m_1rstPersonRunCloseToAWall = false; if(CamTargetEntity->m_rwObject == nil) return; if(CamTargetEntity->IsPed()){ // static bool FailedTestTwelveFramesAgo = false; // unused CVector HeadPos = vecHeadCamOffset; CVector TargetCoors; ((CPed*)CamTargetEntity)->TransformToNode(HeadPos, PED_HEAD); RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(CamTargetEntity->GetClump()); int32 idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_HEAD)); RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); RwV3dTransformPoints(&HeadPos, &HeadPos, 1, &mats[idx]); RwV3d scl = { 0.0f, 0.0f, 0.0f }; RwMatrixScale(&mats[idx], &scl, rwCOMBINEPRECONCAT); if(ResetStatics){ Beta = TargetOrientation; Alpha = 0.0f; m_fInitialPlayerOrientation = TargetOrientation; if(CamTargetEntity->IsPed()){ // useless check Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; Alpha = 0.0f; m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; // FailedTestTwelveFramesAgo = false; m_bCollisionChecksOn = true; } // DontLookThroughWorldFixer = false; m_vecBufferedPlayerBodyOffset = HeadPos; InitialHeadPos = HeadPos; } m_vecBufferedPlayerBodyOffset.y = HeadPos.y; if(TheCamera.m_bHeadBob){ m_vecBufferedPlayerBodyOffset.x = TheCamera.m_fGaitSwayBuffer * m_vecBufferedPlayerBodyOffset.x + (1.0f-TheCamera.m_fGaitSwayBuffer) * HeadPos.x; m_vecBufferedPlayerBodyOffset.z = TheCamera.m_fGaitSwayBuffer * m_vecBufferedPlayerBodyOffset.z + (1.0f-TheCamera.m_fGaitSwayBuffer) * HeadPos.z; HeadPos = (CamTargetEntity->GetMatrix() * m_vecBufferedPlayerBodyOffset); }else{ float HeadDelta = (HeadPos - InitialHeadPos).Magnitude2D(); CVector Fwd = CamTargetEntity->GetForward(); Fwd.z = 0.0f; Fwd.Normalise(); HeadPos = HeadDelta*1.23f*Fwd + CamTargetEntity->GetPosition(); HeadPos.z += 0.59f; } Source = HeadPos; // unused: // ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(MidPos, PED_MID); // Source - MidPos; // Look around bool UseMouse = false; float MouseX = CPad::GetPad(0)->GetMouseX(); float MouseY = CPad::GetPad(0)->GetMouseY(); float LookLeftRight, LookUpDown; if(MouseX != 0.0f || MouseY != 0.0f){ UseMouse = true; LookLeftRight = -3.0f*MouseX; LookUpDown = 4.0f*MouseY; }else{ LookLeftRight = -CPad::GetPad(0)->LookAroundLeftRight(); LookUpDown = CPad::GetPad(0)->LookAroundUpDown(); } if(UseMouse){ Beta += TheCamera.m_fMouseAccelHorzntl * LookLeftRight * FOV/80.0f; Alpha += TheCamera.m_fMouseAccelVertical * LookUpDown * FOV/80.0f; }else{ float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); } while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); if(((CPed*)CamTargetEntity)->IsPlayer() && ((CPed*)CamTargetEntity)->m_attachedTo){ CPed *pedTarget = ((CPed*)CamTargetEntity); float NewBeta; switch(pedTarget->m_attachType){ case 0: NewBeta = pedTarget->GetForward().Heading() + HALFPI; break; case 1: NewBeta = pedTarget->GetForward().Heading() + PI; break; case 2: NewBeta = pedTarget->GetForward().Heading() - HALFPI; break; case 3: NewBeta = pedTarget->GetForward().Heading(); break; } float BetaOffset = Beta - NewBeta; if(BetaOffset > PI) BetaOffset -= TWOPI; else if(BetaOffset < PI) BetaOffset += TWOPI; BetaOffset = clamp(BetaOffset, -pedTarget->m_attachRotStep, pedTarget->m_attachRotStep); Beta = NewBeta + BetaOffset; } TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; Front = TargetCoors - Source; Front.Normalise(); Source += Front*0.4f; TheCamera.m_AlphaForPlayerAnim1rstPerson = Alpha; GetVectorsReadyForRW(); float Heading = Front.Heading(); ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Heading; ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Heading; TheCamera.pTargetEntity->SetHeading(Heading); TheCamera.pTargetEntity->GetMatrix().UpdateRW(); if(Mode == MODE_SNIPER_RUNABOUT){ // no mouse wheel FOV buffering here like in normal sniper mode if(CPad::GetPad(0)->SniperZoomIn() || CPad::GetPad(0)->SniperZoomOut()){ if(CPad::GetPad(0)->SniperZoomOut()) FOV *= (255.0f*CTimer::GetTimeStep() + 10000.0f) / 10000.0f; else FOV /= (255.0f*CTimer::GetTimeStep() + 10000.0f) / 10000.0f; } TheCamera.SetMotionBlur(180, 255, 180, 120, MOTION_BLUR_SNIPER); if(FOV > DefaultFOV) FOV = DefaultFOV; if(FOV < 15.0f) FOV = 15.0f; } } ResetStatics = false; RwCameraSetNearClipPlane(Scene.camera, 0.05f); } float fCameraNearClipMult = 0.15f; void CCam::Process_Sniper(const CVector &CameraTarget, float TargetOrientation, float, float) { if(!CamTargetEntity->IsPed()) return; float BackOffset = 0.19f; static bool FailedTestTwelveFramesAgo = false; RwV3d HeadPos; CVector TargetCoors; TargetCoors = CameraTarget; static float TargetFOV = 0.0f; if(ResetStatics){ Beta = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; Alpha = 0.0f; m_fInitialPlayerOrientation = ((CPed*)CamTargetEntity)->m_fRotationCur + HALFPI; FailedTestTwelveFramesAgo = false; // static DPadVertical unused // static DPadHorizontal unused m_bCollisionChecksOn = true; FOVSpeed = 0.0f; TargetFOV = FOV; ResetStatics = false; } if(((CPed*)CamTargetEntity)->bIsDucking) BackOffset = 0.8f; CamTargetEntity->GetMatrix().UpdateRW(); CamTargetEntity->UpdateRwFrame(); CamTargetEntity->UpdateRpHAnim(); ((CPed*)CamTargetEntity)->m_pedIK.GetComponentPosition(HeadPos, PED_HEAD); Source = HeadPos; Source.z += 0.1f; if(((CPed*)CamTargetEntity)->bIsDucking){ Source.x -= fDuckingBackOffset*CamTargetEntity->GetForward().x; Source.y -= fDuckingBackOffset*CamTargetEntity->GetForward().y; Source.x -= fDuckingRightOffset*CamTargetEntity->GetRight().x; Source.y -= fDuckingRightOffset*CamTargetEntity->GetRight().y; }else{ Source.x -= BackOffset*CamTargetEntity->GetForward().x; Source.y -= BackOffset*CamTargetEntity->GetForward().y; } // Look around bool UseMouse = false; float MouseX = CPad::GetPad(0)->GetMouseX(); float MouseY = CPad::GetPad(0)->GetMouseY(); float LookLeftRight, LookUpDown; if(MouseX != 0.0f || MouseY != 0.0f){ UseMouse = true; LookLeftRight = -3.0f*MouseX; LookUpDown = 4.0f*MouseY; }else{ LookLeftRight = -CPad::GetPad(0)->SniperModeLookLeftRight(); LookUpDown = CPad::GetPad(0)->SniperModeLookUpDown(); } if(UseMouse){ Beta += TheCamera.m_fMouseAccelHorzntl * LookLeftRight * FOV/80.0f; Alpha += TheCamera.m_fMouseAccelVertical * LookUpDown * FOV/80.0f; }else{ float xdir = LookLeftRight < 0.0f ? -1.0f : 1.0f; float ydir = LookUpDown < 0.0f ? -1.0f : 1.0f; Beta += SQR(LookLeftRight/100.0f)*xdir*0.8f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); Alpha += SQR(LookUpDown/150.0f)*ydir*1.0f/14.0f * FOV/80.0f * CTimer::GetTimeStep(); } while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; if(Alpha > DEGTORAD(60.0f)) Alpha = DEGTORAD(60.0f); else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); TargetCoors.x = 3.0f * Cos(Alpha) * Cos(Beta) + Source.x; TargetCoors.y = 3.0f * Cos(Alpha) * Sin(Beta) + Source.y; TargetCoors.z = 3.0f * Sin(Alpha) + Source.z; UseMouse = false; int ZoomInButton = ControlsManager.GetMouseButtonAssociatedWithAction(PED_SNIPER_ZOOM_IN); int ZoomOutButton = ControlsManager.GetMouseButtonAssociatedWithAction(PED_SNIPER_ZOOM_OUT); if(ZoomInButton == rsMOUSEWHEELUPBUTTON || ZoomInButton == rsMOUSEWHEELDOWNBUTTON || ZoomOutButton == rsMOUSEWHEELUPBUTTON || ZoomOutButton == rsMOUSEWHEELDOWNBUTTON){ if(CPad::GetPad(0)->GetMouseWheelUp() || CPad::GetPad(0)->GetMouseWheelDown()){ if(CPad::GetPad(0)->SniperZoomIn()){ TargetFOV = FOV - 10.0f; UseMouse = true; } if(CPad::GetPad(0)->SniperZoomOut()){ TargetFOV = FOV + 10.0f; UseMouse = true; } } } if((CPad::GetPad(0)->SniperZoomIn() || CPad::GetPad(0)->SniperZoomOut()) && !UseMouse){ if(CPad::GetPad(0)->SniperZoomOut()){ FOV *= (255.0f*CTimer::GetTimeStep() + 10000.0f) / 10000.0f; TargetFOV = FOV; FOVSpeed = 0.0f; }else{ FOV /= (255.0f*CTimer::GetTimeStep() + 10000.0f) / 10000.0f; TargetFOV = FOV; FOVSpeed = 0.0f; } }else{ if(Abs(TargetFOV - FOV) > 0.5f) WellBufferMe(TargetFOV, &FOV, &FOVSpeed, 0.5f, 0.25f, false); else FOVSpeed = 0.0f; } TheCamera.SetMotionBlur(180, 255, 180, 120, MOTION_BLUR_SNIPER); if(FOV > DefaultFOV) FOV = DefaultFOV; if(Mode == MODE_CAMERA){ if(FOV < 3.0f) FOV = 3.0f; }else{ if(FOV < 15.0f) FOV = 15.0f; } Front = TargetCoors - Source; Front.Normalise(); Source += Front*0.4f; if(m_bCollisionChecksOn){ if(!CWorld::GetIsLineOfSightClear(TargetCoors, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else{ CVector TestPoint; TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta + DEGTORAD(35.0f)) + Source.x; TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta + DEGTORAD(35.0f)) + Source.y; TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else{ TestPoint.x = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Cos(Beta - DEGTORAD(35.0f)) + Source.x; TestPoint.y = 3.0f * Cos(Alpha - DEGTORAD(20.0f)) * Sin(Beta - DEGTORAD(35.0f)) + Source.y; TestPoint.z = 3.0f * Sin(Alpha - DEGTORAD(20.0f)) + Source.z; if(!CWorld::GetIsLineOfSightClear(TestPoint, Source, true, true, false, true, false, true, true)){ RwCameraSetNearClipPlane(Scene.camera, 0.4f); FailedTestTwelveFramesAgo = true; }else FailedTestTwelveFramesAgo = false; } } } if(FailedTestTwelveFramesAgo) RwCameraSetNearClipPlane(Scene.camera, 0.4f); else if(Mode == MODE_CAMERA) RwCameraSetNearClipPlane(Scene.camera, ((15.0f - Min(FOV, 15.0f))*fCameraNearClipMult + 1.0f)*DEFAULT_NEAR); Source -= Front*0.4f; GetVectorsReadyForRW(); float Rotation = CGeneral::GetATanOfXY(Front.x, Front.y) - HALFPI; ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Rotation; ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Rotation; } float INIT_SYPHON_GROUND_DIST = 2.419f; float INIT_SYPHON_ALPHA_OFFSET = -DEGTORAD(3.0f); float INIT_SYPHON_DEGREE_OFFSET = -DEGTORAD(30.0f); float FrontOffsetSyphon = -DEGTORAD(25.5f); // unused float INIT_SYPHON_Z_OFFSET = -0.5f; void CCam::Process_Syphon(const CVector &CameraTarget, float, float, float) { FOV = DefaultFOV; if(!CamTargetEntity->IsPed()) return; static bool CameraObscured = false; // unused FailedClippingTestPrevously static float BetaOffset = INIT_SYPHON_DEGREE_OFFSET; // unused AngleToGoTo // unused AngleToGoToSpeed // unused DistBetweenPedAndPlayerPreviouslyOn static float HeightDown = INIT_SYPHON_Z_OFFSET; static float AlphaOffset = INIT_SYPHON_ALPHA_OFFSET; static bool NegateBetaOffset = true; CVector TargetCoors; float fAimingDist; float TargetAlpha; bool StandingOnMovingThing = false; TargetCoors = CameraTarget; AlphaOffset = INIT_SYPHON_ALPHA_OFFSET; float GroundDist = INIT_SYPHON_GROUND_DIST; while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; float NewBeta = CGeneral::GetATanOfXY(TheCamera.m_cvecAimingTargetCoors.x - TargetCoors.x, TheCamera.m_cvecAimingTargetCoors.y - TargetCoors.y) + PI; if(ResetStatics){ BetaOffset = INIT_SYPHON_DEGREE_OFFSET; Beta = CGeneral::GetATanOfXY(Source.x - TargetCoors.x, Source.y - TargetCoors.y); // some unuseds ResetStatics = false; } if(NegateBetaOffset) BetaOffset = -INIT_SYPHON_DEGREE_OFFSET; Beta = NewBeta + BetaOffset; Source = TargetCoors; Source.x += GroundDist*Cos(Beta); Source.y += GroundDist*Sin(Beta); CPhysical *ground = (CPhysical*)((CPed*)CamTargetEntity)->m_pCurSurface; if(ground && (ground->IsVehicle() || ground->IsObject())) StandingOnMovingThing = true; TargetCoors.z += m_fSyphonModeTargetZOffSet; bool PlayerTooClose = false; fAimingDist = (TheCamera.m_cvecAimingTargetCoors - TargetCoors).Magnitude2D(); if(fAimingDist < 6.5f){ fAimingDist = 6.5f; PlayerTooClose = true; } TargetAlpha = CGeneral::GetATanOfXY(fAimingDist, TheCamera.m_cvecAimingTargetCoors.z - TargetCoors.z); if(ResetStatics) // BUG: can never happen Alpha = -TargetAlpha; while(TargetAlpha >= PI) TargetAlpha -= 2*PI; while(TargetAlpha < -PI) TargetAlpha += 2*PI; while(Alpha >= PI) Alpha -= 2*PI; while(Alpha < -PI) Alpha += 2*PI; // inlined if(StandingOnMovingThing) WellBufferMe(-TargetAlpha, &Alpha, &AlphaSpeed, 0.07f/2.0f, 0.015f/2.0f, true); else WellBufferMe(-TargetAlpha, &Alpha, &AlphaSpeed, 0.07f, 0.015f, true); Source.z += GroundDist*Sin(Alpha+AlphaOffset) + GroundDist*0.2f; if(Source.z < TargetCoors.z + HeightDown) Source.z = TargetCoors.z + HeightDown; if(!PlayerTooClose){ CColPoint point; CEntity *entity = nil; CWorld::pIgnoreEntity = CamTargetEntity; if(CWorld::ProcessLineOfSight(TheCamera.m_cvecAimingTargetCoors, Source, point, entity, true, false, false, true, false, false, true)){ CVector TestFront = TheCamera.m_cvecAimingTargetCoors - Source; TestFront.Normalise(); CVector CamToPlayer = CameraTarget - Source; CVector CamToCol = point.point - Source; if(DotProduct(TestFront, CamToCol) > DotProduct(TestFront, CamToPlayer)){ // collision is beyond player float ColDist = (TheCamera.m_cvecAimingTargetCoors - point.point).Magnitude(); CVector PlayerToTarget = TheCamera.m_cvecAimingTargetCoors - CameraTarget; float PlayerToTargetDist = PlayerToTarget.Magnitude(); PlayerToTarget.Normalise(); CVector Center = TheCamera.m_cvecAimingTargetCoors - ColDist*PlayerToTarget; float Radius = (point.point - Center).Magnitude(); if(CWorld::TestSphereAgainstWorld(Center, Radius, nil, true, false, false, true, false, true)){ CVector LineToCol = gaTempSphereColPoints[0].point - Center; LineToCol -= DotProduct(LineToCol, PlayerToTarget)*PlayerToTarget; // unused CVector LineToPrevCol = point.point - Center; LineToPrevCol -= DotProduct(LineToPrevCol, PlayerToTarget)*PlayerToTarget; float LineDist = LineToCol.Magnitude(); float NewBetaOffset = 0.0f; if(LineDist > 0.0f && ColDist > 0.1f){ // scale offset at center to offset at player float DistOffset = LineDist/ColDist * PlayerToTargetDist; // turn into an angle NewBetaOffset = 0.9f*Asin(Min(DistOffset/GroundDist, 1.0f)); } if(NewBetaOffset < BetaOffset){ float Ratio = NewBetaOffset / BetaOffset; BetaOffset = NewBetaOffset; Beta = NewBeta + NewBetaOffset; GroundDist *= Max(Ratio, 0.5f); Source.x = TargetCoors.x + GroundDist*Cos(Beta); Source.y = TargetCoors.y + GroundDist*Sin(Beta); Source.z += (1.0f-Ratio)*0.5f; } } } } CWorld::pIgnoreEntity = nil; } Front = TheCamera.m_cvecAimingTargetCoors - Source; float TargetDistGround = Front.Magnitude2D(); Front.Normalise(); m_cvecTargetCoorsForFudgeInter = Source + TargetDistGround*Front; m_cvecTargetCoorsForFudgeInter.z = TargetCoors.z; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, CameraTarget + CVector(0.0f, 0.0f, 0.75f), Source, FOV); Source.z = OrigSource.z; GetVectorsReadyForRW(); } void CCam::Process_Syphon_Crim_In_Front(const CVector &CameraTarget, float, float, float) { FOV = DefaultFOV; if(!CamTargetEntity->IsPed()) return; CVector TargetCoors = CameraTarget; CVector vDist; float fDist, TargetDist; float zOffset; float AimingAngle; TargetDist = TheCamera.m_fPedZoomValueSmooth * 0.5f + 4.0f; vDist = Source - TargetCoors; fDist = vDist.Magnitude2D(); zOffset = TargetDist - 2.65f; if(zOffset < 0.0f) zOffset = 0.0f; if(zOffset == 0.0f) Source = TargetCoors + CVector(1.0f, 1.0f, zOffset); else Source = TargetCoors + CVector(vDist.x/fDist*TargetDist, vDist.y/fDist*TargetDist, zOffset); AimingAngle = CGeneral::GetATanOfXY(TheCamera.m_cvecAimingTargetCoors.x - TargetCoors.x, TheCamera.m_cvecAimingTargetCoors.y - TargetCoors.y); while(AimingAngle >= PI) AimingAngle -= 2*PI; while(AimingAngle < -PI) AimingAngle += 2*PI; if(ResetStatics){ if(AimingAngle > 0.0f) m_fPlayerInFrontSyphonAngleOffSet = -m_fPlayerInFrontSyphonAngleOffSet; ResetStatics = false; } if(TheCamera.PlayerWeaponMode.Mode == MODE_SYPHON) Beta = AimingAngle + m_fPlayerInFrontSyphonAngleOffSet; Source.x = TargetCoors.x; Source.y = TargetCoors.y; Source.x += Cos(Beta) * TargetDist; Source.y += Sin(Beta) * TargetDist; TargetCoors = CameraTarget; TargetCoors.z += m_fSyphonModeTargetZOffSet; m_cvecTargetCoorsForFudgeInter = TargetCoors; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); Front = TargetCoors - Source; GetVectorsReadyForRW(); } float MAX_HEIGHT_UP = 15.0f; float WATER_Z_ADDITION = 2.75f; float WATER_Z_ADDITION_MIN = 1.5f; float SMALLBOAT_CLOSE_ALPHA_MINUS = 0.2f; float afBoatBetaDiffMult[3] = { 0.15f, 0.07f, 0.01f }; float afBoatBetaSpeedDiffMult[3] = { 0.02f, 0.015f, 0.005f }; void CCam::Process_BehindBoat(const CVector &CameraTarget, float TargetOrientation, float, float) { if(!CamTargetEntity->IsVehicle()){ ResetStatics = false; return; } CVector TargetCoors = CameraTarget; float DeltaBeta = 0.0f; static float TargetWhenChecksWereOn = 0.0f; static float CenterObscuredWhenChecksWereOn = 0.0f; static float WaterZAddition = 2.75f; float WaterLevel = 0.0f; float MaxHeightUp = MAX_HEIGHT_UP; static float WaterLevelBuffered = 0.0f; static float WaterLevelSpeed = 0.0f; float BetaDiffMult = 0.0f; float BetaSpeedDiffMult = 0.0f; Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y); FOV = DefaultFOV; float TargetAlpha = 0.0f; if(ResetStatics){ CenterObscuredWhenChecksWereOn = 0.0f; TargetWhenChecksWereOn = 0.0f; }else if(DirectionWasLooking != LOOKING_FORWARD) Beta = TargetOrientation; if(!CWaterLevel::GetWaterLevelNoWaves(TargetCoors.x, TargetCoors.y, TargetCoors.z, &WaterLevel)) WaterLevel = TargetCoors.z - 0.5f; if(ResetStatics){ WaterLevelBuffered = WaterLevel; WaterLevelSpeed = 0.0f; } WellBufferMe(WaterLevel, &WaterLevelBuffered, &WaterLevelSpeed, 0.2f, 0.07f, false); static float FixerForGoingBelowGround = 0.4f; if(-FixerForGoingBelowGround < TargetCoors.z-WaterLevelBuffered+WATER_Z_ADDITION) WaterLevelBuffered += TargetCoors.z-WaterLevelBuffered+WATER_Z_ADDITION - FixerForGoingBelowGround; CVector BoatDimensions = CamTargetEntity->GetColModel()->boundingBox.GetSize(); float BoatSize = BoatDimensions.Magnitude2D(); int index = 0; TheCamera.GetArrPosForVehicleType(((CVehicle*)CamTargetEntity)->GetVehicleAppearance(), index); if(TheCamera.CarZoomIndicator == CAM_ZOOM_1){ TargetAlpha = ZmOneAlphaOffset[index]; BetaDiffMult = afBoatBetaDiffMult[0]; BetaSpeedDiffMult = afBoatBetaSpeedDiffMult[0]; }else if(TheCamera.CarZoomIndicator == CAM_ZOOM_2){ TargetAlpha = ZmTwoAlphaOffset[index]; BetaDiffMult = afBoatBetaDiffMult[1]; BetaSpeedDiffMult = afBoatBetaSpeedDiffMult[1]; }else if(TheCamera.CarZoomIndicator == CAM_ZOOM_3){ TargetAlpha = ZmThreeAlphaOffset[index]; BetaDiffMult = afBoatBetaDiffMult[2]; BetaSpeedDiffMult = afBoatBetaSpeedDiffMult[2]; } if(TheCamera.CarZoomIndicator == CAM_ZOOM_1 && BoatSize < 10.0f){ TargetAlpha -= SMALLBOAT_CLOSE_ALPHA_MINUS; BoatSize = 10.0f; }else if(CCullZones::Cam1stPersonForPlayer()){ float Water = 0.0f; // useless call //CWaterLevel::GetWaterLevelNoWaves(TargetCoors.x, TargetCoors.y, TargetCoors.z, &Water); Water = (WaterLevel + WATER_Z_ADDITION_MIN - WaterLevelBuffered - WATER_Z_ADDITION)/(BoatDimensions.z/2.0f + MaxHeightUp); TargetAlpha = Asin(clamp(Water, -1.0f, 1.0f)); } if(ResetStatics){ Alpha = TargetAlpha; AlphaSpeed = 0.0f; } WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.15f, 0.07f, true); if(ResetStatics){ Beta = TargetOrientation; DeltaBeta = 0.0f; } // inlined WellBufferMe(TargetOrientation, &Beta, &BetaSpeed, BetaDiffMult * ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed.Magnitude(), BetaSpeedDiffMult, true); Source = (TheCamera.CarZoomValueSmooth+BoatSize) * CVector(-Cos(Beta), -Sin(Beta), 0.0f) + TargetCoors; Source.z = WaterLevelBuffered + WATER_Z_ADDITION + (BoatDimensions.z/2.0f + MaxHeightUp) * Sin(Alpha); m_cvecTargetCoorsForFudgeInter = TargetCoors; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); Front = TargetCoors - Source; Front.Normalise(); float TargetRoll; if(CPad::GetPad(0)->GetDPadLeft() || CPad::GetPad(0)->GetDPadRight()){ #ifdef FIX_BUGS float fwdSpeed = 180.0f*DotProduct(((CVehicle*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward()); if(fwdSpeed > 210.0f) fwdSpeed = 210.0f; #endif if(CPad::GetPad(0)->GetDPadLeft()) TargetRoll = DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle; else TargetRoll = -(DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle); CVector FwdTarget = CamTargetEntity->GetForward(); FwdTarget.Normalise(); float AngleDiff = DotProduct(FwdTarget, Front); AngleDiff = Acos(Min(Abs(AngleDiff), 1.0f)); #ifdef FIX_BUGS TargetRoll *= fwdSpeed/210.0f * Sin(AngleDiff); #else TargetRoll *= Sin(AngleDiff); #endif }else{ float fwdSpeed = 180.0f*DotProduct(((CVehicle*)CamTargetEntity)->m_vecMoveSpeed, CamTargetEntity->GetForward()); if(fwdSpeed > 210.0f) fwdSpeed = 210.0f; TargetRoll = CPad::GetPad(0)->GetLeftStickX()/128.0f * fwdSpeed/210.0f; CVector FwdTarget = CamTargetEntity->GetForward(); FwdTarget.Normalise(); float AngleDiff = DotProduct(FwdTarget, Front); AngleDiff = Acos(Min(Abs(AngleDiff), 1.0f)); TargetRoll *= (DEGTORAD(10.0f)*TiltOverShoot[index] + f_max_role_angle) * Sin(AngleDiff); } WellBufferMe(TargetRoll, &f_Roll, &f_rollSpeed, 0.15f, 0.07f, false); Up = CVector(Cos(f_Roll + HALFPI), 0.0f, Sin(f_Roll + HALFPI)); Up.Normalise(); Front.Normalise(); CVector Left = CrossProduct(Up, Front); Left.Normalise(); Up = CrossProduct(Front, Left); Up.Normalise(); ResetStatics = false; } float FIGHT_HORIZ_DIST = 3.0f; float FIGHT_VERT_DIST = 1.0f; float FIGHT_BETA_ANGLE = 125.0f; void CCam::Process_Fight_Cam(const CVector &CameraTarget, float TargetOrientation, float, float) { if(!CamTargetEntity->IsPed()) return; FOV = DefaultFOV; float HorizDist = FIGHT_HORIZ_DIST; float VertDist = FIGHT_VERT_DIST; float BetaLeft, BetaRight, DeltaBetaLeft, DeltaBetaRight; static bool PreviouslyFailedBuildingChecks = false; float TargetCamHeight; CVector TargetCoors; m_fMinDistAwayFromCamWhenInterPolating = FIGHT_HORIZ_DIST; Front = Source - CameraTarget; if(ResetStatics) Beta = CGeneral::GetATanOfXY(Front.x, Front.y); while(TargetOrientation >= PI) TargetOrientation -= 2*PI; while(TargetOrientation < -PI) TargetOrientation += 2*PI; while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; // Figure out Beta BetaLeft = TargetOrientation - DEGTORAD(FIGHT_BETA_ANGLE); BetaRight = TargetOrientation + DEGTORAD(FIGHT_BETA_ANGLE); DeltaBetaLeft = Beta - BetaLeft; DeltaBetaRight = Beta - BetaRight; while(DeltaBetaLeft >= PI) DeltaBetaLeft -= 2*PI; while(DeltaBetaLeft < -PI) DeltaBetaLeft += 2*PI; while(DeltaBetaRight >= PI) DeltaBetaRight -= 2*PI; while(DeltaBetaRight < -PI) DeltaBetaRight += 2*PI; if(ResetStatics){ if(Abs(DeltaBetaLeft) < Abs(DeltaBetaRight)) m_fTargetBeta = DeltaBetaLeft; else m_fTargetBeta = DeltaBetaRight; m_fBufferedTargetOrientation = TargetOrientation; m_fBufferedTargetOrientationSpeed = 0.0f; m_bCollisionChecksOn = true; BetaSpeed = 0.0f; }else if(CPad::GetPad(0)->WeaponJustDown()){ if(Abs(DeltaBetaLeft) < Abs(DeltaBetaRight)) m_fTargetBeta = DeltaBetaLeft; else m_fTargetBeta = DeltaBetaRight; } WellBufferMe(m_fTargetBeta, &Beta, &BetaSpeed, 0.015f, 0.007f, true); Source = CameraTarget + HorizDist*CVector(Cos(Beta), Sin(Beta), 0.0f); Source.z += VertDist; WellBufferMe(TargetOrientation, &m_fBufferedTargetOrientation, &m_fBufferedTargetOrientationSpeed, 0.07f, 0.004f, true); TargetCoors = CameraTarget + 0.1f*CVector(Cos(m_fBufferedTargetOrientation), Sin(m_fBufferedTargetOrientation), 0.0f); TargetCamHeight = CameraTarget.z - Source.z + Max(m_fPedBetweenCameraHeightOffset, m_fDimensionOfHighestNearCar) + VertDist; if(TargetCamHeight > m_fCamBufferedHeight) WellBufferMe(TargetCamHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.15f, 0.04f, false); else WellBufferMe(0.0f, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.08f, 0.0175f, false); Source.z += m_fCamBufferedHeight; m_cvecTargetCoorsForFudgeInter = TargetCoors; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); Front = TargetCoors - Source; Front.Normalise(); GetVectorsReadyForRW(); ResetStatics = false; } /* // Spline format is this, but game doesn't seem to use any kind of struct: struct Spline { float numFrames; struct { float time; float f[3]; // CVector for Vector spline } frames[1]; // numFrames }; */ // These two functions are pretty ugly #define MS(t) (uint32)((t)*1000.0f) void FindSplinePathPositionFloat(float *out, float *spline, uint32 time, uint32 &marker) { // marker is at time uint32 numFrames = spline[0]; uint32 timeDelta = MS(spline[marker] - spline[marker-4]); uint32 endTime = MS(spline[4*(numFrames-1) + 1]); if(time < endTime){ bool canAdvance = true; if((marker-1)/4 > numFrames){ canAdvance = false; marker = 4*(numFrames-1) + 1; } // skipping over small time deltas apparently? while(timeDelta <= 75 && canAdvance){ marker += 4; if((marker-1)/4 > numFrames){ canAdvance = false; marker = 4*(numFrames-1) + 1; } timeDelta = (spline[marker] - spline[marker-4]) * 1000.0f; } } float a = ((float)time - (float)MS(spline[marker-4])) / (float)MS(spline[marker] - spline[marker-4]); a = clamp(a, 0.0f, 1.0f); float b = 1.0f - a; *out = b*b*b * spline[marker-3] + 3.0f*a*b*b * spline[marker-1] + 3.0f*a*a*b * spline[marker+2] + a*a*a * spline[marker+1]; } void FindSplinePathPositionVector(CVector *out, float *spline, uint32 time, uint32 &marker) { // marker is at time uint32 numFrames = spline[0]; uint32 timeDelta = MS(spline[marker] - spline[marker-10]); uint32 endTime = MS(spline[10*(numFrames-1) + 1]); if(time < endTime){ bool canAdvance = true; if((marker-1)/10 > numFrames){ canAdvance = false; marker = 10*(numFrames-1) + 1; } // skipping over small time deltas apparently? while(timeDelta <= 75 && canAdvance){ marker += 10; if((marker-1)/10 > numFrames){ canAdvance = false; marker = 10*(numFrames-1) + 1; } timeDelta = (spline[marker] - spline[marker-10]) * 1000.0f; } } if((marker-1)/10 > numFrames){ printf("Arraymarker %i \n", marker); printf("Path zero %i \n", numFrames); } float a = ((float)time - (float)MS(spline[marker-10])) / (float)MS(spline[marker] - spline[marker-10]); a = clamp(a, 0.0f, 1.0f); float b = 1.0f - a; out->x = b*b*b * spline[marker-9] + 3.0f*a*b*b * spline[marker-3] + 3.0f*a*a*b * spline[marker+4] + a*a*a * spline[marker+1]; out->y = b*b*b * spline[marker-8] + 3.0f*a*b*b * spline[marker-2] + 3.0f*a*a*b * spline[marker+5] + a*a*a * spline[marker+2]; out->z = b*b*b * spline[marker-7] + 3.0f*a*b*b * spline[marker-1] + 3.0f*a*a*b * spline[marker+6] + a*a*a * spline[marker+3]; *out += TheCamera.m_vecCutSceneOffset; } void CCam::Process_FlyBy(const CVector&, float, float, float) { float UpAngle = 0.0f; static float FirstFOVValue = 0.0f; static float PsuedoFOV; static uint32 ArrayMarkerFOV; static uint32 ArrayMarkerUp; static uint32 ArrayMarkerSource; static uint32 ArrayMarkerFront; if(TheCamera.m_bcutsceneFinished) return; #ifdef FIX_BUGS // this would crash, not nice when cycling debug mode if(TheCamera.m_arrPathArray[0].m_arr_PathData == nil) return; #endif Up = CVector(0.0f, 0.0f, 1.0f); if(TheCamera.m_bStartingSpline) m_fTimeElapsedFloat += CTimer::GetTimeStepNonClippedInMilliseconds(); else{ m_fTimeElapsedFloat = 0.0f; m_uiFinishTime = MS(TheCamera.m_arrPathArray[2].m_arr_PathData[10*((int)TheCamera.m_arrPathArray[2].m_arr_PathData[0]-1) + 1]); TheCamera.m_bStartingSpline = true; FirstFOVValue = TheCamera.m_arrPathArray[0].m_arr_PathData[2]; PsuedoFOV = TheCamera.m_arrPathArray[0].m_arr_PathData[2]; ArrayMarkerFOV = 5; ArrayMarkerUp = 5; ArrayMarkerSource = 11; ArrayMarkerFront = 11; } float fTime = m_fTimeElapsedFloat; uint32 uiFinishTime = m_uiFinishTime; uint32 uiTime = fTime; if(uiTime < uiFinishTime){ TheCamera.m_fPositionAlongSpline = (float) uiTime / uiFinishTime; while(uiTime >= (TheCamera.m_arrPathArray[2].m_arr_PathData[ArrayMarkerSource] - TheCamera.m_arrPathArray[2].m_arr_PathData[1])*1000.0f) ArrayMarkerSource += 10; FindSplinePathPositionVector(&Source, TheCamera.m_arrPathArray[2].m_arr_PathData, uiTime, ArrayMarkerSource); while(uiTime >= (TheCamera.m_arrPathArray[3].m_arr_PathData[ArrayMarkerFront] - TheCamera.m_arrPathArray[3].m_arr_PathData[1])*1000.0f) ArrayMarkerFront += 10; FindSplinePathPositionVector(&Front, TheCamera.m_arrPathArray[3].m_arr_PathData, uiTime, ArrayMarkerFront); while(uiTime >= (TheCamera.m_arrPathArray[1].m_arr_PathData[ArrayMarkerUp] - TheCamera.m_arrPathArray[1].m_arr_PathData[1])*1000.0f) ArrayMarkerUp += 4; FindSplinePathPositionFloat(&UpAngle, TheCamera.m_arrPathArray[1].m_arr_PathData, uiTime, ArrayMarkerUp); UpAngle = DEGTORAD(UpAngle) + HALFPI; Up.x = Cos(UpAngle); Up.z = Sin(UpAngle); while(uiTime >= (TheCamera.m_arrPathArray[0].m_arr_PathData[ArrayMarkerFOV] - TheCamera.m_arrPathArray[0].m_arr_PathData[1])*1000.0f) ArrayMarkerFOV += 4; FindSplinePathPositionFloat(&PsuedoFOV, TheCamera.m_arrPathArray[0].m_arr_PathData, uiTime, ArrayMarkerFOV); m_cvecTargetCoorsForFudgeInter = Front; Front = Front - Source; Front.Normalise(); CVector Left = CrossProduct(Up, Front); Up = CrossProduct(Front, Left); Up.Normalise(); }else if(uiTime >= uiFinishTime){ // end ArrayMarkerSource = (TheCamera.m_arrPathArray[2].m_arr_PathData[0] - 1)*10 + 1; ArrayMarkerFront = (TheCamera.m_arrPathArray[3].m_arr_PathData[0] - 1)*10 + 1; ArrayMarkerUp = (TheCamera.m_arrPathArray[1].m_arr_PathData[0] - 1)*4 + 1; ArrayMarkerFOV = (TheCamera.m_arrPathArray[0].m_arr_PathData[0] - 1)*4 + 1; FindSplinePathPositionVector(&Source, TheCamera.m_arrPathArray[2].m_arr_PathData, uiTime, ArrayMarkerSource); FindSplinePathPositionVector(&Front, TheCamera.m_arrPathArray[3].m_arr_PathData, uiTime, ArrayMarkerFront); FindSplinePathPositionFloat(&UpAngle, TheCamera.m_arrPathArray[1].m_arr_PathData, uiTime, ArrayMarkerUp); UpAngle = DEGTORAD(UpAngle) + HALFPI; Up.x = Cos(UpAngle); Up.z = Sin(UpAngle); FindSplinePathPositionFloat(&PsuedoFOV, TheCamera.m_arrPathArray[0].m_arr_PathData, uiTime, ArrayMarkerFOV); TheCamera.m_fPositionAlongSpline = 1.0f; ArrayMarkerFOV = 0; ArrayMarkerUp = 0; ArrayMarkerSource = 0; ArrayMarkerFront = 0; m_cvecTargetCoorsForFudgeInter = Front; Front = Front - Source; Front.Normalise(); CVector Left = CrossProduct(Up, Front); Up = CrossProduct(Front, Left); Up.Normalise(); } FOV = PsuedoFOV; } CVector vecWheelCamBoatOffset(-0.5f, -0.8f, 0.3f); CVector vecWheelCamBoatOffsetAlt(0.2f, -0.2f, -0.3f); float fWheelCamCarXOffset = 0.33f; float fWheelCamBikeXOffset = 0.2f; bool CCam::Process_WheelCam(const CVector&, float, float, float) { FOV = DefaultFOV; CVector WheelPos; if(CamTargetEntity->IsPed()){ // what? ped with wheels or what? Source = Multiply3x3(CamTargetEntity->GetMatrix(), CVector(-0.3f, -0.5f, 0.1f)); Source += CamTargetEntity->GetPosition(); Front = CVector(1.0f, 0.0f, 0.0f); }else{ WheelPos = CamTargetEntity->GetColModel()->boundingBox.min; WheelPos.x -= 0.33f; WheelPos.y = -2.3f; WheelPos.z = 0.3f; Source = CamTargetEntity->GetMatrix() * WheelPos; Front = CamTargetEntity->GetForward(); } CVector NewUp, Right; if(CamTargetEntity->IsVehicle() && (((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || ((CVehicle*)CamTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE)){ WheelPos.x = -1.55f; Right = CamTargetEntity->GetRight(); NewUp = CamTargetEntity->GetUp(); Source = CamTargetEntity->GetMatrix() * WheelPos; }else if(CamTargetEntity->IsVehicle() && ((CVehicle*)CamTargetEntity)->IsBoat()){ NewUp = CVector(0.0f, 0.0f, 1.0f); Right = CrossProduct(Front, NewUp); Right.Normalise(); NewUp = CrossProduct(Right, Front); NewUp.Normalise(); CVector BoatCamPos(0.0f, 0.0f, 0.0f); if(((CVehicle*)CamTargetEntity)->pDriver){ ((CVehicle*)CamTargetEntity)->pDriver->m_pedIK.GetComponentPosition(BoatCamPos, PED_HEAD); BoatCamPos += ((CVehicle*)CamTargetEntity)->m_vecMoveSpeed * CTimer::GetTimeStep(); BoatCamPos += vecWheelCamBoatOffset.x * Right; BoatCamPos += vecWheelCamBoatOffset.y * CamTargetEntity->GetForward(); BoatCamPos.z += vecWheelCamBoatOffset.z; if(CamTargetEntity->GetModelIndex() == MI_PREDATOR){ BoatCamPos += vecWheelCamBoatOffsetAlt.x * Right; BoatCamPos += vecWheelCamBoatOffsetAlt.y * CamTargetEntity->GetForward(); BoatCamPos.z += vecWheelCamBoatOffsetAlt.z; } Source = BoatCamPos; }else Source.z += 2.0f*vecWheelCamBoatOffset.z; }else if(CamTargetEntity->IsVehicle() && ((CVehicle*)CamTargetEntity)->IsBike()){ NewUp = CVector(0.0f, 0.0f, 1.0f); Right = CrossProduct(Front, NewUp); Right.Normalise(); NewUp = CrossProduct(Right, Front); NewUp.Normalise(); WheelPos.z += fWheelCamCarXOffset - fWheelCamBikeXOffset; Source = CamTargetEntity->GetPosition(); Source += WheelPos.x * CamTargetEntity->GetRight(); Source += WheelPos.y * Front; Source += WheelPos.z * Up; }else{ NewUp = CVector(0.0f, 0.0f, 1.0f); Right = CrossProduct(Front, NewUp); Right.Normalise(); NewUp = CrossProduct(Right, Front); NewUp.Normalise(); } float Roll = Cos((CTimer::GetTimeInMilliseconds()&0x1FFFF)/(float)0x1FFFF * TWOPI); Up = Cos(Roll*0.4f)*NewUp + Sin(Roll*0.4f)*Right; CEntity *entity = nil; CColPoint point; CWorld::pIgnoreEntity = CamTargetEntity; bool blocked = CWorld::ProcessLineOfSight(Source, CamTargetEntity->GetPosition(), point, entity, true, false, false, true, false, false, true); CWorld::pIgnoreEntity = nil; return !blocked; } int BOAT_UNDERWATER_CAM_BLUR = 20; float BOAT_UNDERWATER_CAM_COLORMAG_LIMIT = 10.0f; //--MIAIM: done void CCam::Process_Fixed(const CVector &CameraTarget, float, float, float) { if(DirectionWasLooking != LOOKING_FORWARD) DirectionWasLooking = LOOKING_FORWARD; Source = m_cvecCamFixedModeSource; Front = CameraTarget - Source; Front.Normalise(); m_cvecTargetCoorsForFudgeInter = CameraTarget; GetVectorsReadyForRW(); Up = CVector(0.0f, 0.0f, 1.0f) + m_cvecCamFixedModeUpOffSet; Up.Normalise(); CVector Right = CrossProduct(Front, Up); Right.Normalise(); Up = CrossProduct(Right, Front); FOV = DefaultFOV; if(TheCamera.m_bUseSpecialFovTrain) FOV = TheCamera.m_fFovForTrain; float WaterZ = 0.0f; if(CWaterLevel::GetWaterLevel(Source, &WaterZ, true) && Source.z < WaterZ){ float WaterLum = Sqrt(SQR(CTimeCycle::GetWaterRed()) + SQR(CTimeCycle::GetWaterGreen()) + SQR(CTimeCycle::GetWaterBlue())); if(WaterLum > BOAT_UNDERWATER_CAM_COLORMAG_LIMIT){ float f = BOAT_UNDERWATER_CAM_COLORMAG_LIMIT/WaterLum; TheCamera.SetMotionBlur(CTimeCycle::GetWaterRed()*f, CTimeCycle::GetWaterGreen()*f, CTimeCycle::GetWaterBlue()*f, BOAT_UNDERWATER_CAM_BLUR, MOTION_BLUR_LIGHT_SCENE); }else{ TheCamera.SetMotionBlur(CTimeCycle::GetWaterRed(), CTimeCycle::GetWaterGreen(), CTimeCycle::GetWaterBlue(), BOAT_UNDERWATER_CAM_BLUR, MOTION_BLUR_LIGHT_SCENE); } } #ifdef PC_PLAYER_CONTROLS if(FrontEndMenuManager.m_ControlMethod == CONTROL_STANDARD && Using3rdPersonMouseCam()){ CPed *player = FindPlayerPed(); if(player && player->CanStrafeOrMouseControl()){ float Heading = Front.Heading(); ((CPed*)TheCamera.pTargetEntity)->m_fRotationCur = Heading; ((CPed*)TheCamera.pTargetEntity)->m_fRotationDest = Heading; TheCamera.pTargetEntity->SetHeading(Heading); TheCamera.pTargetEntity->GetMatrix().UpdateRW(); } } #endif } void CCam::Process_LightHouse(const CVector &CameraTarget, float, float, float) { static float Timer; Source = CameraTarget; Source.x = 474.3f; Source.y = -1717.6f; int CamMode; if(CameraTarget.z > 57.0f && (CameraTarget-Source).Magnitude2D() > 3.2f){ // Outside at top if(Timer > 0.0f){ Timer -= CTimer::GetTimeStep(); CamMode = 1; }else{ Timer = -24.0f; CamMode = 2; } }else if(CameraTarget.z > 57.0f){ // Inside at top if(Timer < 0.0f){ Timer += CTimer::GetTimeStep(); CamMode = 2; }else{ Timer = 24.0f; CamMode = 1; } }else{ Timer = 0.0f; CamMode = 0; } if(CamMode == 2){ Source.z = 57.5f; Front = Source - CameraTarget; Front.Normalise(); Source.x = CameraTarget.x - 5.0f*Front.x; Source.y = CameraTarget.y - 5.0f*Front.y; }else if(CamMode == 1){ Front = CameraTarget - Source; Front.Normalise(); Source.x = CameraTarget.x - 2.0f*Front.x; Source.y = CameraTarget.y - 2.0f*Front.y; }else{ Source.z += 4.0f; Front = CameraTarget - Source; Front.Normalise(); Source -= 4.0f*Front; Source.z = Min(Source.z, 55.0f); Front = CameraTarget - Source; } m_cvecTargetCoorsForFudgeInter = CameraTarget; GetVectorsReadyForRW(); Up = CVector(0.0f, 0.0f, 1.0f) + m_cvecCamFixedModeUpOffSet; Up.Normalise(); CVector Right = CrossProduct(Front, Up); Right.Normalise(); Up = CrossProduct(Right, Front); FOV = DefaultFOV; if(TheCamera.m_bUseSpecialFovTrain) // uh, sure... FOV = TheCamera.m_fFovForTrain; } void CCam::Process_Player_Fallen_Water(const CVector &CameraTarget, float TargetOrientation, float, float) { CColPoint colPoint; CEntity *entity = nil; FOV = DefaultFOV; Source = m_vecLastAboveWaterCamPosition; Source.z += 4.0f; m_cvecTargetCoorsForFudgeInter = CameraTarget; Front = CameraTarget - Source; Front.Normalise(); if(CWorld::ProcessLineOfSight(CameraTarget, Source, colPoint, entity, true, false, false, true, false, true, true)) Source = colPoint.point; GetVectorsReadyForRW(); Front = CameraTarget - Source; Front.Normalise(); } void CCam::Process_SpecialFixedForSyphon(const CVector &CameraTarget, float, float, float) { Source = m_cvecCamFixedModeSource; m_cvecTargetCoorsForFudgeInter = CameraTarget; m_cvecTargetCoorsForFudgeInter.z += m_fSyphonModeTargetZOffSet; Front = CameraTarget - Source; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, m_cvecTargetCoorsForFudgeInter, Source, FOV); Front.z += m_fSyphonModeTargetZOffSet; GetVectorsReadyForRW(); Up += m_cvecCamFixedModeUpOffSet; Up.Normalise(); CVector Left = CrossProduct(Up, Front); Left.Normalise(); Front = CrossProduct(Left, Up); Front.Normalise(); FOV = DefaultFOV; } #ifdef IMPROVED_CAMERA #define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k) #define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k) #define CTRLJUSTDOWN(key) \ ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \ (KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) #define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) void CCam::Process_Debug(const CVector&, float, float, float) { static float Speed = 0.0f; static float PanSpeedX = 0.0f; static float PanSpeedY = 0.0f; CVector TargetCoors; RwCameraSetNearClipPlane(Scene.camera, DEFAULT_NEAR); FOV = DefaultFOV; Alpha += DEGTORAD(CPad::GetPad(1)->GetLeftStickY()) / 50.0f; Beta += DEGTORAD(CPad::GetPad(1)->GetLeftStickX()*1.5f) / 19.0f; if(CPad::GetPad(0)->GetLeftMouse()){ Alpha += DEGTORAD(CPad::GetPad(0)->GetMouseY()/2.0f); Beta += DEGTORAD(CPad::GetPad(0)->GetMouseX()/2.0f); } TargetCoors.x = Source.x + Cos(Alpha) * Sin(Beta) * 7.0f; TargetCoors.y = Source.y + Cos(Alpha) * Cos(Beta) * 7.0f; TargetCoors.z = Source.z + Sin(Alpha) * 3.0f; if(Alpha > DEGTORAD(89.5f)) Alpha = DEGTORAD(89.5f); else if(Alpha < DEGTORAD(-89.5f)) Alpha = DEGTORAD(-89.5f); if(CPad::GetPad(1)->GetSquare() || KEYDOWN('W')) Speed += 0.1f; else if(CPad::GetPad(1)->GetCross() || KEYDOWN('S')) Speed -= 0.1f; else Speed = 0.0f; if(Speed > 70.0f) Speed = 70.0f; if(Speed < -70.0f) Speed = -70.0f; if(KEYDOWN(rsRIGHT) || KEYDOWN('D')) PanSpeedX += 0.1f; else if(KEYDOWN(rsLEFT) || KEYDOWN('A')) PanSpeedX -= 0.1f; else PanSpeedX = 0.0f; if(PanSpeedX > 70.0f) PanSpeedX = 70.0f; if(PanSpeedX < -70.0f) PanSpeedX = -70.0f; if(KEYDOWN(rsUP)) PanSpeedY += 0.1f; else if(KEYDOWN(rsDOWN)) PanSpeedY -= 0.1f; else PanSpeedY = 0.0f; if(PanSpeedY > 70.0f) PanSpeedY = 70.0f; if(PanSpeedY < -70.0f) PanSpeedY = -70.0f; Front = TargetCoors - Source; Front.Normalise(); Source = Source + Front*Speed; Up = CVector{ 0.0f, 0.0f, 1.0f }; CVector Right = CrossProduct(Front, Up); Up = CrossProduct(Right, Front); Source = Source + Up*PanSpeedY + Right*PanSpeedX; if(Source.z < -450.0f) Source.z = -450.0f; if(CPad::GetPad(1)->GetRightShoulder2JustDown() || KEYJUSTDOWN(rsENTER)){ if(FindPlayerVehicle()) FindPlayerVehicle()->Teleport(Source); else CWorld::Players[CWorld::PlayerInFocus].m_pPed->SetPosition(Source); } // stay inside sectors while(CWorld::GetSectorX(Source.x) > NUMSECTORS_X-5.0f) Source.x -= 1.0f; while(CWorld::GetSectorX(Source.x) < 5.0f) Source.x += 1.0f; while(CWorld::GetSectorY(Source.y) > NUMSECTORS_X-5.0f) Source.y -= 1.0f; while(CWorld::GetSectorY(Source.y) < 5.0f) Source.y += 1.0f; GetVectorsReadyForRW(); #ifdef FIX_BUGS CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_CAMERA); #else CPad::GetPad(0)->DisablePlayerControls = PLAYERCONTROL_CAMERA; #endif if(CPad::GetPad(1)->GetLeftShockJustDown() && gbBigWhiteDebugLightSwitchedOn) CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &Source, 12.0f, 0.0f, 0.0f, -12.0f, 128, 128, 128, 128, 1000.0f, false, 1.0f, nil, false); if(CHud::m_Wants_To_Draw_Hud){ char str[256]; sprintf(str, "CamX: %f CamY: %f CamZ: %f", Source.x, Source.y, Source.z); sprintf(str, "Frontx: %f, Fronty: %f, Frontz: %f ", Front.x, Front.y, Front.z); sprintf(str, "Look@: %f, Look@: %f, Look@: %f ", Front.x + Source.x, Front.y + Source.y, Front.z + Source.z); } } #else void CCam::Process_Debug(const CVector&, float, float, float) { static float Speed = 0.0f; CVector TargetCoors; RwCameraSetNearClipPlane(Scene.camera, DEFAULT_NEAR); FOV = DefaultFOV; Alpha += DEGTORAD(CPad::GetPad(1)->GetLeftStickY()) / 50.0f; Beta += DEGTORAD(CPad::GetPad(1)->GetLeftStickX()*1.5f) / 19.0f; TargetCoors.x = Source.x + Cos(Alpha) * Sin(Beta) * 7.0f; TargetCoors.y = Source.y + Cos(Alpha) * Cos(Beta) * 7.0f; TargetCoors.z = Source.z + Sin(Alpha) * 3.0f; if(Alpha > DEGTORAD(89.5f)) Alpha = DEGTORAD(89.5f); else if(Alpha < DEGTORAD(-89.5f)) Alpha = DEGTORAD(-89.5f); if(CPad::GetPad(1)->GetSquare() || CPad::GetPad(1)->GetLeftMouse()) Speed += 0.1f; else if(CPad::GetPad(1)->GetCross() || CPad::GetPad(1)->GetRightMouse()) Speed -= 0.1f; else Speed = 0.0f; if(Speed > 70.0f) Speed = 70.0f; if(Speed < -70.0f) Speed = -70.0f; Front = TargetCoors - Source; Front.Normalise(); Source = Source + Front*Speed; if(Source.z < -450.0f) Source.z = -450.0f; if(CPad::GetPad(1)->GetRightShoulder2JustDown()){ if(FindPlayerVehicle()) FindPlayerVehicle()->Teleport(Source); else CWorld::Players[CWorld::PlayerInFocus].m_pPed->SetPosition(Source); } // stay inside sectors while(CWorld::GetSectorX(Source.x) > NUMSECTORS_X-5.0f) Source.x -= 1.0f; while(CWorld::GetSectorX(Source.x) < 5.0f) Source.x += 1.0f; while(CWorld::GetSectorY(Source.y) > NUMSECTORS_X-5.0f) Source.y -= 1.0f; while(CWorld::GetSectorY(Source.y) < 5.0f) Source.y += 1.0f; GetVectorsReadyForRW(); if(CPad::GetPad(1)->GetLeftShockJustDown() && gbBigWhiteDebugLightSwitchedOn) CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &Source, 12.0f, 0.0f, 0.0f, -12.0f, 128, 128, 128, 128, 1000.0f, false, 1.0f, nil, 1.0f); if(CHud::m_Wants_To_Draw_Hud){ char str[256]; sprintf(str, "CamX: %f CamY: %f CamZ: %f", Source.x, Source.y, Source.z); sprintf(str, "Frontx: %f, Fronty: %f, Frontz: %f ", Front.x, Front.y, Front.z); sprintf(str, "Look@: %f, Look@: %f, Look@: %f ", Front.x + Source.x, Front.y + Source.y, Front.z + Source.z); } } #endif #ifdef GTA_SCENE_EDIT void CCam::Process_Editor(const CVector&, float, float, float) { static float Speed = 0.0f; CVector TargetCoors; if(ResetStatics){ Source = CVector(796.0f, -937.0, 40.0f); CamTargetEntity = nil; } ResetStatics = false; RwCameraSetNearClipPlane(Scene.camera, DEFAULT_NEAR); FOV = DefaultFOV; Alpha += DEGTORAD(CPad::GetPad(1)->GetLeftStickY()) / 50.0f; Beta += DEGTORAD(CPad::GetPad(1)->GetLeftStickX()*1.5f) / 19.0f; if(CamTargetEntity && CSceneEdit::m_bCameraFollowActor){ TargetCoors = CamTargetEntity->GetPosition(); }else if(CSceneEdit::m_bRecording){ TargetCoors.x = Source.x + Cos(Alpha) * Sin(Beta) * 7.0f; TargetCoors.y = Source.y + Cos(Alpha) * Cos(Beta) * 7.0f; TargetCoors.z = Source.z + Sin(Alpha) * 7.0f; }else TargetCoors = CSceneEdit::m_vecCamHeading + Source; CSceneEdit::m_vecCurrentPosition = TargetCoors; CSceneEdit::m_vecCamHeading = TargetCoors - Source; if(Alpha > DEGTORAD(89.5f)) Alpha = DEGTORAD(89.5f); else if(Alpha < DEGTORAD(-89.5f)) Alpha = DEGTORAD(-89.5f); if(CPad::GetPad(1)->GetSquare() || CPad::GetPad(1)->GetLeftMouse()) Speed += 0.1f; else if(CPad::GetPad(1)->GetCross() || CPad::GetPad(1)->GetRightMouse()) Speed -= 0.1f; else Speed = 0.0f; if(Speed > 70.0f) Speed = 70.0f; if(Speed < -70.0f) Speed = -70.0f; Front = TargetCoors - Source; Front.Normalise(); Source = Source + Front*Speed; if(Source.z < -450.0f) Source.z = -450.0f; if(CPad::GetPad(1)->GetRightShoulder2JustDown()){ if(FindPlayerVehicle()) FindPlayerVehicle()->Teleport(Source); else CWorld::Players[CWorld::PlayerInFocus].m_pPed->SetPosition(Source); } // stay inside sectors while(CWorld::GetSectorX(Source.x) > NUMSECTORS_X-5.0f) Source.x -= 1.0f; while(CWorld::GetSectorX(Source.x) < 5.0f) Source.x += 1.0f; while(CWorld::GetSectorY(Source.y) > NUMSECTORS_X-5.0f) Source.y -= 1.0f; while(CWorld::GetSectorY(Source.y) < 5.0f) Source.y += 1.0f; GetVectorsReadyForRW(); if(CPad::GetPad(1)->GetLeftShockJustDown() && gbBigWhiteDebugLightSwitchedOn) CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &Source, 12.0f, 0.0f, 0.0f, -12.0f, 128, 128, 128, 128, 1000.0f, false, 1.0f, nil, false); if(CHud::m_Wants_To_Draw_Hud){ char str[256]; sprintf(str, "CamX: %f CamY: %f CamZ: %f", Source.x, Source.y, Source.z); sprintf(str, "Frontx: %f, Fronty: %f, Frontz: %f ", Front.x, Front.y, Front.z); sprintf(str, "Look@: %f, Look@: %f, Look@: %f ", Front.x + Source.x, Front.y + Source.y, Front.z + Source.z); } } #endif void CCam::Process_ModelView(const CVector &CameraTarget, float, float, float) { CVector TargetCoors = CameraTarget; float Angle = Atan2(Front.x, Front.y); FOV = DefaultFOV; Angle += CPad::GetPad(0)->GetLeftStickX()/1280.0f; if(Distance < 10.0f) Distance += CPad::GetPad(0)->GetLeftStickY()/1000.0f; else Distance += CPad::GetPad(0)->GetLeftStickY() * ((Distance - 10.0f)/20.0f + 1.0f) / 1000.0f; #ifdef IMPROVED_CAMERA if(CPad::GetPad(0)->GetLeftMouse()){ Distance += DEGTORAD(CPad::GetPad(0)->GetMouseY()/2.0f); Angle += DEGTORAD(CPad::GetPad(0)->GetMouseX()/2.0f); } #endif if(Distance < 1.5f) Distance = 1.5f; Front.x = Cos(0.3f) * Sin(Angle); Front.y = Cos(0.3f) * Cos(Angle); Front.z = -Sin(0.3f); Source = CameraTarget - Distance*Front; GetVectorsReadyForRW(); } float DEADCAM_HEIGHT_START = 2.0f; float DEADCAM_HEIGHT_RATE = 0.04f; float DEADCAM_WAFT_AMPLITUDE = 2.0f; float DEADCAM_WAFT_RATE = 600.0f; float DEADCAM_WAFT_TILT_AMP = -0.35f; void CCam::ProcessPedsDeadBaby(void) { CVector TargetCoors; CVector CamPos; if(TheCamera.pTargetEntity->IsPed()) ((CPed*)TheCamera.pTargetEntity)->m_pedIK.GetComponentPosition(TargetCoors, PED_MID); else if(TheCamera.pTargetEntity->IsVehicle()){ TargetCoors = TheCamera.pTargetEntity->GetPosition(); TargetCoors.z += TheCamera.pTargetEntity->GetColModel()->boundingBox.max.z; }else return; if(ResetStatics){ TheCamera.m_uiTimeLastChange = CTimer::GetTimeInMilliseconds(); CamPos = TargetCoors; CamPos.z += DEADCAM_HEIGHT_START; float WaterZ = 0.0f; if(CWaterLevel::GetWaterLevelNoWaves(TargetCoors.x, TargetCoors.y, TargetCoors.z, &WaterZ)){ if(WaterZ + 1.5f > CamPos.z) CamPos.z = WaterZ + 1.5f; } CVector Right = CrossProduct(TheCamera.pTargetEntity->GetForward(), CVector(0.0f, 0.0f, 1.0f)); Right.z = 0.0f; Right.Normalise(); Front = TargetCoors - CamPos; Front.Normalise(); Up = CrossProduct(Right, Front); Up.Normalise(); ResetStatics = false; }else{ CamPos = Source; if(CWorld::TestSphereAgainstWorld(CamPos+CVector(0.0f, 0.0f, 0.2f), 0.3f, TheCamera.pTargetEntity, true, true, false, true, false, true) == nil) CamPos.z += DEADCAM_HEIGHT_RATE*CTimer::GetTimeStep(); CVector Right = CrossProduct(TheCamera.pTargetEntity->GetForward(), CVector(0.0f, 0.0f, 1.0f)); Right.z = 0.0f; Right.Normalise(); float Time = CTimer::GetTimeInMilliseconds() - TheCamera.m_uiTimeLastChange; CVector WaftOffset = DEADCAM_WAFT_AMPLITUDE * Min(1000.0f,Time)/1000.0f * Sin(Time/DEADCAM_WAFT_RATE) * Right; CVector WaftPos = TargetCoors + WaftOffset; WaftPos.z = CamPos.z; CVector WaftFront = WaftPos - CamPos; WaftFront.Normalise(); if(CWorld::TestSphereAgainstWorld(CamPos+0.2f*WaftFront, 0.3f, TheCamera.pTargetEntity, true, true, false, true, false, true) == nil) CamPos = WaftPos; Front = CVector(0.0f, 0.0f, -1.0f); Front += Cos(Time/DEADCAM_WAFT_RATE) * DEADCAM_WAFT_TILT_AMP * Min(2000.0f,Time)/2000.0f * Right; Front.Normalise(); Up = CrossProduct(Right, Front); Up.Normalise(); } Source = CamPos; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetCoors, Source, FOV); TheCamera.m_bMoveCamToAvoidGeom = false; } float ARRESTDIST_BEHIND_COP = 5.0f; float ARRESTDIST_RIGHTOF_COP = 3.0f; float ARRESTDIST_ABOVE_COP = 1.4f; // unused float ARRESTDIST_MINFROM_PLAYER = 8.0f; float ARRESTCAM_LAMP_BEST_DIST = 17.0f; float ARRESTCAM_ROTATION_SPEED = 0.1f; float ARRESTCAM_ROTATION_UP = 0.05f; float ARRESTCAM_S_ROTATION_UP = 0.1f; float ARRESTDIST_ALONG_GROUND = 5.0f; float ARRESTDIST_SIDE_GROUND = 10.0f; float ARRESTDIST_ABOVE_GROUND = 0.7f; float ARRESTCAM_LAMPPOST_ROTATEDIST = 10.0f; float ARRESTCAM_LAMPPOST_TRANSLATE = 0.1f; bool CCam::GetLookAlongGroundPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut) { if(Target == nil || Cop == nil) return false; CVector CopToTarget = TargetCoors - Cop->GetPosition(); CopToTarget.z = 0.0f; CopToTarget.Normalise(); SourceOut = TargetCoors + ARRESTDIST_ALONG_GROUND*CopToTarget; CVector Side = CrossProduct(CopToTarget, CVector(0.0f, 0.0f, 1.0f)); SourceOut += ARRESTDIST_SIDE_GROUND*Side; SourceOut.z += 5.0f; bool found = false; float ground = CWorld::FindGroundZFor3DCoord(SourceOut.x, SourceOut.y, SourceOut.z, &found); if(found) SourceOut.z = ground + ARRESTDIST_ABOVE_GROUND; return true; } bool CCam::GetLookFromLampPostPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut) { int i; int16 NumObjects; CEntity *Objects[16]; CEntity *NearestLampPost = nil; CWorld::FindObjectsInRange(TargetCoors, 30.0f, true, &NumObjects, 15, Objects, false, false, false, true, true); float NearestDist = 10000.0f; for(i = 0; i < NumObjects; i++){ if(Objects[i]->GetIsStatic() && Objects[i]->GetUp().z > 0.9f && IsLampPost(Objects[i]->GetModelIndex())){ float Dist = (Objects[i]->GetPosition() - TargetCoors).Magnitude2D(); if(Abs(ARRESTCAM_LAMP_BEST_DIST - Dist) < NearestDist){ CVector TestStart = Objects[i]->GetColModel()->boundingBox.max; TestStart = Objects[i]->GetMatrix() * TestStart; CVector TestEnd = TestStart - TargetCoors; TestEnd.Normalise(); TestEnd += TargetCoors; if(CWorld::GetIsLineOfSightClear(TestStart, TestEnd, true, false, false, false, false, true, true)){ NearestDist = Abs(ARRESTCAM_LAMP_BEST_DIST - Dist); NearestLampPost = Objects[i]; SourceOut = TestStart; } } } } return NearestLampPost != nil; } bool CCam::GetLookOverShoulderPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut) { if(Target == nil || Cop == nil) return false; CVector CopCoors = Cop->GetPosition(); CVector CopToTarget = TargetCoors - CopCoors; CVector Side = CrossProduct(CopToTarget, CVector(0.0f, 0.0f, 1.0f)); Side.Normalise(); CopCoors += ARRESTDIST_RIGHTOF_COP * Side; CopToTarget.Normalise(); if(CopToTarget.z < -0.7071f){ CopToTarget.z = -0.7071f; float GroundDist = CopToTarget.Magnitude2D(); if(GroundDist > 0.0f){ CopToTarget.x *= 0.7071f/GroundDist; CopToTarget.y *= 0.7071f/GroundDist; } CopToTarget.Normalise(); }else{ if(CopToTarget.z > 0.0f){ CopToTarget.z = 0.0f; CopToTarget.Normalise(); } } CopCoors -= ARRESTDIST_BEHIND_COP * CopToTarget; CopToTarget = TargetCoors - CopCoors; float Dist = CopToTarget.Magnitude(); if(Dist < ARRESTDIST_MINFROM_PLAYER && Dist > 0.0f) CopToTarget *= ARRESTDIST_MINFROM_PLAYER/Dist; SourceOut = TargetCoors - CopToTarget; return true; } enum { ARRESTCAM_OVERSHOULDER = 1, ARRESTCAM_ALONGGROUND, ARRESTCAM_ALONGGROUND_RIGHT, ARRESTCAM_ALONGGROUND_RIGHT_UP, ARRESTCAM_ALONGGROUND_LEFT, ARRESTCAM_ALONGGROUND_LEFT_UP, ARRESTCAM_LAMPPOST, }; int nUsingWhichCamera; CPed *pStoredCopPed; bool CCam::ProcessArrestCamOne(void) { CVector TargetPos; CVector CamSource; CPed *cop = nil; FOV = 45.0f; bool foundPos = false; int ArrestModes[5] = { -1, -1, -1, -1, -1 }; if(ResetStatics){ CPed *targetPed = (CPed*)TheCamera.pTargetEntity; nUsingWhichCamera = 0; if(TheCamera.pTargetEntity->IsPed()){ ((CPed*)TheCamera.pTargetEntity)->m_pedIK.GetComponentPosition(TargetPos, PED_MID); if(FindPlayerPed() && FindPlayerPed()->m_pArrestingCop) cop = FindPlayerPed()->m_pArrestingCop; if(cop && CGeneral::GetRandomNumberInRange(0.0f, 1.0f) > 0.5f){ ArrestModes[0] = ARRESTCAM_OVERSHOULDER; ArrestModes[1] = ARRESTCAM_ALONGGROUND; ArrestModes[2] = ARRESTCAM_OVERSHOULDER; ArrestModes[3] = ARRESTCAM_LAMPPOST; }else{ ArrestModes[0] = ARRESTCAM_ALONGGROUND; ArrestModes[1] = ARRESTCAM_OVERSHOULDER; ArrestModes[2] = ARRESTCAM_LAMPPOST; } }else if(TheCamera.pTargetEntity->IsVehicle()){ CVehicle *targetVehicle = (CVehicle*)TheCamera.pTargetEntity; if(targetVehicle->pDriver && targetVehicle->pDriver->IsPlayer()){ targetPed = targetVehicle->pDriver; targetPed->m_pedIK.GetComponentPosition(TargetPos, PED_MID); }else{ targetPed = nil; TargetPos = targetVehicle->GetPosition(); } if(FindPlayerPed() && FindPlayerPed()->m_pArrestingCop) cop = FindPlayerPed()->m_pArrestingCop; if(cop && CGeneral::GetRandomNumberInRange(0.0f, 1.0f) > 0.65f){ ArrestModes[0] = ARRESTCAM_OVERSHOULDER; ArrestModes[1] = ARRESTCAM_LAMPPOST; ArrestModes[2] = ARRESTCAM_ALONGGROUND; ArrestModes[3] = ARRESTCAM_OVERSHOULDER; }else{ ArrestModes[0] = ARRESTCAM_LAMPPOST; ArrestModes[1] = ARRESTCAM_ALONGGROUND; ArrestModes[2] = ARRESTCAM_OVERSHOULDER; } }else return false; for(int i = 0; nUsingWhichCamera == 0 && i < ARRAY_SIZE(ArrestModes) && ArrestModes[i] > 0; i++){ switch(ArrestModes[i]){ case ARRESTCAM_OVERSHOULDER: if(cop){ foundPos = GetLookOverShoulderPos(TheCamera.pTargetEntity, cop, TargetPos, CamSource); pStoredCopPed = cop; cop = nil; }else if(targetPed){ for(int j = 0; j < targetPed->m_numNearPeds; j++){ CPed *nearPed = targetPed->m_nearPeds[j]; if(nearPed->GetPedState() == PED_ARREST_PLAYER) foundPos = GetLookOverShoulderPos(TheCamera.pTargetEntity, nearPed, TargetPos, CamSource); if(foundPos){ pStoredCopPed = nearPed; break; } } } break; case ARRESTCAM_ALONGGROUND: if(cop){ foundPos = GetLookAlongGroundPos(TheCamera.pTargetEntity, cop, TargetPos, CamSource); pStoredCopPed = cop; cop = nil; }else if(targetPed){ for(int j = 0; j < targetPed->m_numNearPeds; j++){ CPed *nearPed = targetPed->m_nearPeds[j]; if(nearPed->GetPedState() == PED_ARREST_PLAYER) foundPos = GetLookAlongGroundPos(TheCamera.pTargetEntity, nearPed, TargetPos, CamSource); if(foundPos){ pStoredCopPed = nearPed; break; } } } break; case ARRESTCAM_LAMPPOST: foundPos = GetLookFromLampPostPos(TheCamera.pTargetEntity, cop, TargetPos, CamSource); break; } if(foundPos){ if(pStoredCopPed) pStoredCopPed->RegisterReference((CEntity**)&pStoredCopPed); nUsingWhichCamera = ArrestModes[i]; if(ArrestModes[i] == ARRESTCAM_ALONGGROUND){ float rnd = CGeneral::GetRandomNumberInRange(0.0f, 5.0f); if(rnd < 1.0f) nUsingWhichCamera = ARRESTCAM_ALONGGROUND; else if(rnd < 2.0f) nUsingWhichCamera = ARRESTCAM_ALONGGROUND_RIGHT; else if(rnd < 3.0f) nUsingWhichCamera = ARRESTCAM_ALONGGROUND_RIGHT_UP; else if(rnd < 4.0f) nUsingWhichCamera = ARRESTCAM_ALONGGROUND_LEFT; else nUsingWhichCamera = ARRESTCAM_ALONGGROUND_LEFT_UP; } }else pStoredCopPed = nil; } Source = CamSource; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetPos, Source, FOV); Front = TargetPos - Source; Front.Normalise(); Up = CVector(0.0f, 0.0f, 1.0f); CVector Right = CrossProduct(Front, Up); Right.Normalise(); Up = CrossProduct(Right, Front); if(nUsingWhichCamera != 0) ResetStatics = false; return true; } if(TheCamera.pTargetEntity->IsPed()){ ((CPed*)TheCamera.pTargetEntity)->m_pedIK.GetComponentPosition(TargetPos, PED_MID); }else if(TheCamera.pTargetEntity->IsVehicle()){ CPed *driver = ((CVehicle*)TheCamera.pTargetEntity)->pDriver; if(driver && driver->IsPlayer()) driver->m_pedIK.GetComponentPosition(TargetPos, PED_MID); else TargetPos = TheCamera.pTargetEntity->GetPosition(); }else return false; if(nUsingWhichCamera == ARRESTCAM_OVERSHOULDER && pStoredCopPed){ foundPos = GetLookOverShoulderPos(TheCamera.pTargetEntity, pStoredCopPed, TargetPos, CamSource); float newZ = Source.z + ARRESTCAM_S_ROTATION_UP*CTimer::GetTimeStep(); if(CamSource.z > newZ) CamSource.z = newZ; }else if(nUsingWhichCamera >= ARRESTCAM_ALONGGROUND_RIGHT && nUsingWhichCamera <= ARRESTCAM_ALONGGROUND_LEFT_UP){ CamSource = Source; Front = TargetPos - CamSource; Front.Normalise(); Up = CVector(0.0f, 0.0f, 1.0f); CVector Right = CrossProduct(Front, Up); if(nUsingWhichCamera == ARRESTCAM_ALONGGROUND_LEFT || nUsingWhichCamera == ARRESTCAM_ALONGGROUND_LEFT_UP) Right *= -1.0f; if(CWorld::TestSphereAgainstWorld(CamSource + 0.5f*Right, 0.4f, TheCamera.pTargetEntity, true, true, false, true, false, true) == nil){ foundPos = true; CamSource += Right*ARRESTCAM_ROTATION_SPEED*CTimer::GetTimeStep(); if(nUsingWhichCamera == ARRESTCAM_ALONGGROUND_RIGHT_UP || nUsingWhichCamera == ARRESTCAM_ALONGGROUND_LEFT_UP){ CamSource.z += ARRESTCAM_ROTATION_UP*CTimer::GetTimeStep(); }else{ bool found = false; float ground = CWorld::FindGroundZFor3DCoord(CamSource.x, CamSource.y, CamSource.z, &found); if(found) CamSource.z = ground + ARRESTDIST_ABOVE_GROUND; } } }else if(nUsingWhichCamera == ARRESTCAM_LAMPPOST){ CamSource = Source; Front = TargetPos - CamSource; Front.z = 0.0f; Front.Normalise(); Up = CVector(0.0f, 0.0f, 1.0f); CVector Right = CrossProduct(Front, Up); Right.Normalise(); Front = TargetPos - CamSource + Right*ARRESTCAM_LAMPPOST_ROTATEDIST; Front.z = 0.0f; Front.Normalise(); if(CWorld::TestSphereAgainstWorld(CamSource + 0.5f*Front, 0.4f, TheCamera.pTargetEntity, true, true, false, true, false, true) == nil){ foundPos = true; CamSource += Front*ARRESTCAM_LAMPPOST_TRANSLATE*CTimer::GetTimeStep(); } } if(foundPos){ Source = CamSource; CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetPos, Source, FOV); Front = TargetPos - Source; Front.Normalise(); Up = CVector(0.0f, 0.0f, 1.0f); CVector Right = CrossProduct(Front, Up); Right.Normalise(); Up = CrossProduct(Right, Front); }else{ CVector OrigSource = Source; TheCamera.AvoidTheGeometry(OrigSource, TargetPos, Source, FOV); } return true; } bool CCam::ProcessArrestCamTwo(void) { CPed *player = CWorld::Players[CWorld::PlayerInFocus].m_pPed; if(!ResetStatics) return true; ResetStatics = false; CVector TargetCoors, ToCamera; float BetaOffset; float SourceX, SourceY; if(&TheCamera.Cams[TheCamera.ActiveCam] == this){ SourceX = TheCamera.Cams[(TheCamera.ActiveCam + 1) % 2].Source.x; SourceY = TheCamera.Cams[(TheCamera.ActiveCam + 1) % 2].Source.y; }else{ SourceX = TheCamera.Cams[TheCamera.ActiveCam].Source.x; SourceY = TheCamera.Cams[TheCamera.ActiveCam].Source.y; } for(int i = 0; i <= 1; i++){ int Dir = i == 0 ? 1 : -1; FOV = 60.0f; TargetCoors = player->GetPosition(); Beta = CGeneral::GetATanOfXY(TargetCoors.x-SourceX, TargetCoors.y-SourceY); BetaOffset = DEGTORAD(Dir*80); Source = TargetCoors + 11.5f*CVector(Cos(Beta+BetaOffset), Sin(Beta+BetaOffset), 0.0f); ToCamera = Source - TargetCoors; ToCamera.Normalise(); TargetCoors.x += 0.4f*ToCamera.x; TargetCoors.y += 0.4f*ToCamera.y; if(CWorld::GetIsLineOfSightClear(Source, TargetCoors, true, true, false, true, false, true, true)){ Source.z += 5.5f; TargetCoors += CVector(-0.8f*ToCamera.x, -0.8f*ToCamera.y, 2.2f); m_cvecTargetCoorsForFudgeInter = TargetCoors; Front = TargetCoors - Source; ResetStatics = false; GetVectorsReadyForRW(); return true; } } return false; } #ifdef FREE_CAM void CCam::Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrientation, float, float) { FOV = DefaultFOV; const float MinDist = 2.0f; const float MaxDist = 2.0f + TheCamera.m_fPedZoomValueSmooth; const float BaseOffset = 0.75f; // base height of camera above target CVector TargetCoors = CameraTarget; TargetCoors.z += m_fSyphonModeTargetZOffSet; TargetCoors = DoAverageOnVector(TargetCoors); TargetCoors.z += BaseOffset; // add offset so alpha evens out to 0 // TargetCoors.z += m_fRoadOffSet; CVector Dist = Source - TargetCoors; CVector ToCam; bool Shooting = false; if(((CPed*)CamTargetEntity)->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) if(CPad::GetPad(0)->GetWeapon()) Shooting = true; if(((CPed*)CamTargetEntity)->GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR || ((CPed*)CamTargetEntity)->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT) Shooting = false; if(ResetStatics){ // Coming out of top down here probably // so keep Beta, reset alpha and calculate vectors Beta = CGeneral::GetATanOfXY(Dist.x, Dist.y); Alpha = 0.0f; Dist = MaxDist*CVector(Cos(Alpha) * Cos(Beta), Cos(Alpha) * Sin(Beta), Sin(Alpha)); Source = TargetCoors + Dist; ResetStatics = false; } // Drag the camera along at the look-down offset float CamDist = Dist.Magnitude(); if(CamDist == 0.0f) Dist = CVector(1.0f, 1.0f, 0.0f); else if(CamDist < MinDist) Dist *= MinDist/CamDist; else if(CamDist > MaxDist) Dist *= MaxDist/CamDist; CamDist = Dist.Magnitude(); // Beta = 0 is looking east, HALFPI is north, &c. // Alpha positive is looking up float GroundDist = Dist.Magnitude2D(); Beta = CGeneral::GetATanOfXY(-Dist.x, -Dist.y); Alpha = CGeneral::GetATanOfXY(GroundDist, -Dist.z); while(Beta >= PI) Beta -= 2.0f*PI; while(Beta < -PI) Beta += 2.0f*PI; while(Alpha >= PI) Alpha -= 2.0f*PI; while(Alpha < -PI) Alpha += 2.0f*PI; // Look around bool UseMouse = false; float MouseX = CPad::GetPad(0)->GetMouseX(); float MouseY = CPad::GetPad(0)->GetMouseY(); float LookLeftRight, LookUpDown; /* if((MouseX != 0.0f || MouseY != 0.0f) && !CPad::GetPad(0)->ArePlayerControlsDisabled()){ UseMouse = true; LookLeftRight = -2.5f*MouseX; LookUpDown = 4.0f*MouseY; }else */ { LookLeftRight = -CPad::GetPad(0)->LookAroundLeftRight(); LookUpDown = CPad::GetPad(0)->LookAroundUpDown(); } float AlphaOffset, BetaOffset; if(UseMouse){ BetaOffset = LookLeftRight * TheCamera.m_fMouseAccelHorzntl * FOV/80.0f; AlphaOffset = LookUpDown * TheCamera.m_fMouseAccelVertical * FOV/80.0f; }else{ BetaOffset = LookLeftRight * fStickSens * (1.0f/20.0f) * FOV/80.0f * CTimer::GetTimeStep(); AlphaOffset = LookUpDown * fStickSens * (0.6f/20.0f) * FOV/80.0f * CTimer::GetTimeStep(); } // Stop centering once stick has been touched if(BetaOffset) Rotating = false; Beta += BetaOffset; Alpha += AlphaOffset; while(Beta >= PI) Beta -= 2.0f*PI; while(Beta < -PI) Beta += 2.0f*PI; if(Alpha > DEGTORAD(45.0f)) Alpha = DEGTORAD(45.0f); else if(Alpha < -DEGTORAD(89.5f)) Alpha = -DEGTORAD(89.5f); float BetaDiff = TargetOrientation+PI - Beta; while(BetaDiff >= PI) BetaDiff -= 2.0f*PI; while(BetaDiff < -PI) BetaDiff += 2.0f*PI; float TargetAlpha = Alpha; // 12deg to account for our little height offset. we're not working on the true alpha here const float AlphaLimitUp = DEGTORAD(15.0f) + DEGTORAD(12.0f); const float AlphaLimitDown = -DEGTORAD(15.0f) + DEGTORAD(12.0f); if(Abs(BetaDiff) < DEGTORAD(25.0f) && ((CPed*)CamTargetEntity)->GetMoveSpeed().Magnitude2D() > 0.01f){ // Limit alpha when player is walking towards camera if(TargetAlpha > AlphaLimitUp) TargetAlpha = AlphaLimitUp; if(TargetAlpha < AlphaLimitDown) TargetAlpha = AlphaLimitDown; } WellBufferMe(TargetAlpha, &Alpha, &AlphaSpeed, 0.2f, 0.1f, true); if(CPad::GetPad(0)->ForceCameraBehindPlayer() || Shooting){ m_fTargetBeta = TargetOrientation; Rotating = true; } if(Rotating){ WellBufferMe(m_fTargetBeta, &Beta, &BetaSpeed, 0.1f, 0.06f, true); float DeltaBeta = m_fTargetBeta - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; if(Abs(DeltaBeta) < 0.06f) Rotating = false; } if(TheCamera.m_bUseTransitionBeta) Beta = CGeneral::GetATanOfXY(-Cos(m_fTransitionBeta), -Sin(m_fTransitionBeta)); if(TheCamera.m_bUseTransitionBeta) Beta = CGeneral::GetATanOfXY(-Cos(m_fTransitionBeta), -Sin(m_fTransitionBeta)); Front = CVector(Cos(Alpha) * Cos(Beta), Cos(Alpha) * Sin(Beta), Sin(Alpha)); Source = TargetCoors - Front*CamDist; TargetCoors.z -= BaseOffset; // now get back to the real target coors again m_cvecTargetCoorsForFudgeInter = TargetCoors; Front = TargetCoors - Source; Front.Normalise(); /* * Handle collisions - taken from FollowPedWithMouse */ CEntity *entity; CColPoint colPoint; // Clip Source and fix near clip CWorld::pIgnoreEntity = CamTargetEntity; entity = nil; if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, true, true, true, true, false, false, true)){ float PedColDist = (TargetCoors - colPoint.point).Magnitude(); float ColCamDist = CamDist - PedColDist; if(entity->IsPed() && ColCamDist > DEFAULT_NEAR + 0.1f){ // Ped in the way but not clipping through if(CWorld::ProcessLineOfSight(colPoint.point, Source, colPoint, entity, true, true, true, true, false, false, true)){ PedColDist = (TargetCoors - colPoint.point).Magnitude(); Source = colPoint.point; if(PedColDist < DEFAULT_NEAR + 0.3f) RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); }else{ RwCameraSetNearClipPlane(Scene.camera, Min(ColCamDist-0.35f, DEFAULT_NEAR)); } }else{ Source = colPoint.point; if(PedColDist < DEFAULT_NEAR + 0.3f) RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); } } CWorld::pIgnoreEntity = nil; float ViewPlaneHeight = Tan(DEGTORAD(FOV) / 2.0f); float ViewPlaneWidth = ViewPlaneHeight * CDraw::CalculateAspectRatio() * fTweakFOV; float Near = RwCameraGetNearClipPlane(Scene.camera); float radius = ViewPlaneWidth*Near; entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, false); int i = 0; while(entity){ CVector CamToCol = gaTempSphereColPoints[0].point - Source; float frontDist = DotProduct(CamToCol, Front); float dist = (CamToCol - Front*frontDist).Magnitude() / ViewPlaneWidth; // Try to decrease near clip dist = Max(Min(Near, dist), 0.1f); if(dist < Near) RwCameraSetNearClipPlane(Scene.camera, dist); // Move forward a bit if(dist == 0.1f) Source += (TargetCoors - Source)*0.3f; // Keep testing Near = RwCameraGetNearClipPlane(Scene.camera); radius = ViewPlaneWidth*Near; entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, false); i++; if(i > 5) entity = nil; } GetVectorsReadyForRW(); } // LCS cam hehe void CCam::Process_FollowCar_SA(const CVector& CameraTarget, float TargetOrientation, float, float) { // Missing things on III CCam static CVector m_aTargetHistoryPosOne; static CVector m_aTargetHistoryPosTwo; static CVector m_aTargetHistoryPosThree; static int m_nCurrentHistoryPoints = 0; static float lastBeta = -9999.0f; static float lastAlpha = -9999.0f; static float stepsLeftToChangeBetaByMouse; static float dontCollideWithCars; static bool alphaCorrected; static float heightIncreaseMult; if (!CamTargetEntity->IsVehicle()) return; CVehicle* car = (CVehicle*)CamTargetEntity; CVector TargetCoors = CameraTarget; uint8 camSetArrPos = 0; // We may need those later bool isPlane = car->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE; bool isHeli = car->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI; bool isBike = car->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE; bool isCar = car->IsCar() && !isPlane && !isHeli && !isBike; CPad* pad = CPad::GetPad(0); // Next direction is non-existent in III uint8 nextDirectionIsForward = !(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight()) && DirectionWasLooking == LOOKING_FORWARD; if (car->GetModelIndex() == MI_FIRETRUCK) { camSetArrPos = 7; } else if (car->GetModelIndex() == MI_RCBANDIT || car->GetModelIndex() == MI_RCBARON) { camSetArrPos = 5; } else if (car->GetModelIndex() == MI_RCGOBLIN || car->GetModelIndex() == MI_RCRAIDER) { camSetArrPos = 6; } else if (car->IsBoat()) { camSetArrPos = 4; } else if (isBike) { camSetArrPos = 1; } else if (isPlane) { camSetArrPos = 3; } else if (isHeli) { camSetArrPos = 2; } // LCS one but index 1(firetruck) moved to last float CARCAM_SET[][15] = { {1.3f, 1.0f, 0.4f, 10.0f, 15.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.8f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // cars {1.1f, 1.0f, 0.1f, 10.0f, 11.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.75f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // bike {1.1f, 1.0f, 0.2f, 10.0f, 15.0f, 0.05f, 0.05f, 0.0f, 0.9f, 0.05f, 0.01f, 0.05f, 1.0f, DEGTORAD(10.0f), DEGTORAD(70.0f)}, // heli (SA values) {1.1f, 3.5f, 0.2f, 10.0f, 25.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(89.0f), DEGTORAD(89.0f)}, // plane (SA values) {0.9f, 1.0f, 0.1f, 10.0f, 15.0f, 0.5f, 1.0f, 0.0f, 0.9f, 0.05f, 0.005f, 0.05f, 1.0f, -0.2f, DEGTORAD(70.0f)}, // boat {1.1f, 1.0f, 0.2f, 10.0f, 5.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // rc cars {1.1f, 1.0f, 0.2f, 10.0f, 5.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(20.0f), DEGTORAD(70.0f)}, // rc heli/planes {1.3f, 1.0f, 0.4f, 10.0f, 15.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.8f, -0.18f, DEGTORAD(40.0f)}, // firetruck... }; // RC Heli/planes use same alpha values with heli/planes (LCS firetruck will fallback to 0) uint8 alphaArrPos = (camSetArrPos > 4 ? (isPlane ? 3 : (isHeli ? 2 : 0)) : camSetArrPos); float zoomModeAlphaOffset = 0.0f; static float ZmOneAlphaOffsetLCS[] = { 0.12f, 0.08f, 0.15f, 0.08f, 0.08f }; static float ZmTwoAlphaOffsetLCS[] = { 0.1f, 0.08f, 0.3f, 0.08f, 0.08f }; static float ZmThreeAlphaOffsetLCS[] = { 0.065f, 0.05f, 0.15f, 0.06f, 0.08f }; if (isHeli && car->GetStatus() == STATUS_PLAYER_REMOTE) zoomModeAlphaOffset = ZmTwoAlphaOffsetLCS[alphaArrPos]; else { switch ((int)TheCamera.CarZoomIndicator) { // near case CAM_ZOOM_1: zoomModeAlphaOffset = ZmOneAlphaOffsetLCS[alphaArrPos]; break; // mid case CAM_ZOOM_2: zoomModeAlphaOffset = ZmTwoAlphaOffsetLCS[alphaArrPos]; break; // far case CAM_ZOOM_3: zoomModeAlphaOffset = ZmThreeAlphaOffsetLCS[alphaArrPos]; break; default: break; } } CColModel* carCol = (CColModel*)car->GetColModel(); float colMaxZ = carCol->boundingBox.max.z; // As opposed to LCS and SA, VC does this: carCol->boundingBox.max.z - carCol->boundingBox.min.z; float approxCarLength = 2.0f * Abs(carCol->boundingBox.min.y); // SA taxi min.y = -2.95, max.z = 0.883502f float newDistance = TheCamera.CarZoomValueSmooth + CARCAM_SET[camSetArrPos][1] + approxCarLength; // Taken from VC CCam::Cam_On_A_String_Unobscured. If we don't this, we will end up seeing the world from the inside of RC Goblin/Raider. // I couldn't find where SA does that. It's possible that they've increased the size of these veh.'s collision bounding box. if (car->m_modelIndex == MI_RCRAIDER || car->m_modelIndex == MI_RCGOBLIN) newDistance += INIT_RC_HELI_HORI_EXTRA; else if (car->m_modelIndex == MI_RCBARON) newDistance += INIT_RC_PLANE_HORI_EXTRA; float minDistForThisCar = approxCarLength * CARCAM_SET[camSetArrPos][3]; if (!isHeli || car->GetStatus() == STATUS_PLAYER_REMOTE) { float radiusToStayOutside = colMaxZ * CARCAM_SET[camSetArrPos][0] - CARCAM_SET[camSetArrPos][2]; if (radiusToStayOutside > 0.0f) { TargetCoors.z += radiusToStayOutside; newDistance += radiusToStayOutside; zoomModeAlphaOffset += 0.3f / newDistance * radiusToStayOutside; } } else { // 0.6f = fTestShiftHeliCamTarget TargetCoors += 0.6f * car->GetUp() * colMaxZ; } if (car->m_modelIndex == MI_RCGOBLIN) zoomModeAlphaOffset += 0.178997f; float minDistForVehType = CARCAM_SET[camSetArrPos][4]; if (TheCamera.CarZoomIndicator == CAM_ZOOM_1 && (camSetArrPos < 2 || camSetArrPos == 7)) { minDistForVehType = minDistForVehType * 0.65f; } float nextDistance = Max(newDistance, minDistForVehType); CA_MAX_DISTANCE = newDistance; CA_MIN_DISTANCE = 3.5f; if (ResetStatics) { FOV = DefaultFOV; } else { if (isCar || isBike) { // 0.4f: CAR_FOV_START_SPEED if (DotProduct(car->GetForward(), car->m_vecMoveSpeed) > 0.4f) FOV += (DotProduct(car->GetForward(), car->m_vecMoveSpeed) - 0.4f) * CTimer::GetTimeStep(); } if (FOV > DefaultFOV) // 0.98f: CAR_FOV_FADE_MULT FOV = Pow(0.98f, CTimer::GetTimeStep()) * (FOV - DefaultFOV) + DefaultFOV; FOV = clamp(FOV, DefaultFOV, DefaultFOV + 30.0f); } // WORKAROUND: I still don't know how looking behind works (m_bCamDirectlyInFront is unused in III, they seem to use m_bUseTransitionBeta) if (pad->GetLookBehindForCar()) if (DirectionWasLooking == LOOKING_FORWARD || !LookingBehind) TheCamera.m_bCamDirectlyInFront = true; // Taken from RotCamIfInFrontCar, because we don't call it anymore if (!(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight())) if (DirectionWasLooking != LOOKING_FORWARD) TheCamera.m_bCamDirectlyBehind = true; // Called when we just entered the car, just started to look behind or returned back from looking left, right or behind if (ResetStatics || TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront) { ResetStatics = false; Rotating = false; m_bCollisionChecksOn = true; if (!TheCamera.m_bJustCameOutOfGarage) { Alpha = 0.0f; Beta = car->GetForward().Heading() - HALFPI; if (TheCamera.m_bCamDirectlyInFront) { Beta += PI; } } BetaSpeed = 0.0; AlphaSpeed = 0.0; Distance = 1000.0; Front.x = -(cos(Beta) * cos(Alpha)); Front.y = -(sin(Beta) * cos(Alpha)); Front.z = sin(Alpha); m_aTargetHistoryPosOne = TargetCoors - nextDistance * Front; m_aTargetHistoryPosTwo = TargetCoors - newDistance * Front; m_nCurrentHistoryPoints = 0; if (!TheCamera.m_bJustCameOutOfGarage) Alpha = -zoomModeAlphaOffset; } Front = TargetCoors - m_aTargetHistoryPosOne; Front.Normalise(); // Code that makes cam rotate around the car float camRightHeading = Front.Heading() - HALFPI; if (camRightHeading < -PI) camRightHeading = camRightHeading + TWOPI; float velocityRightHeading; if (car->m_vecMoveSpeed.Magnitude2D() <= 0.02f) velocityRightHeading = camRightHeading; else velocityRightHeading = car->m_vecMoveSpeed.Heading() - HALFPI; if (velocityRightHeading < camRightHeading - PI) velocityRightHeading = velocityRightHeading + TWOPI; else if (velocityRightHeading > camRightHeading + PI) velocityRightHeading = velocityRightHeading - TWOPI; float betaChangeMult1 = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][10]; float betaChangeLimit = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][11]; float betaChangeMult2 = (car->m_vecMoveSpeed - DotProduct(car->m_vecMoveSpeed, Front) * Front).Magnitude(); float betaChange = Min(1.0f, betaChangeMult1 * betaChangeMult2) * (velocityRightHeading - camRightHeading); if (betaChange <= betaChangeLimit) { if (betaChange < -betaChangeLimit) betaChange = -betaChangeLimit; } else { betaChange = betaChangeLimit; } float targetBeta = camRightHeading + betaChange; if (targetBeta < Beta - HALFPI) targetBeta += TWOPI; else if (targetBeta > Beta + PI) targetBeta -= TWOPI; float carPosChange = (TargetCoors - m_aTargetHistoryPosTwo).Magnitude(); if (carPosChange < newDistance && newDistance > minDistForThisCar) { newDistance = Max(minDistForThisCar, carPosChange); } float maxAlphaAllowed = CARCAM_SET[camSetArrPos][13]; // Originally this is to prevent camera enter into car while we're stopping, but what about moving??? // This is also original LCS and SA bug, or some attempt to fix lag. We'll never know // if (car->m_vecMoveSpeed.MagnitudeSqr() < sq(0.2f)) if (car->GetModelIndex() != MI_FIRETRUCK) if (!isBike || ((CBike*)car)->m_nWheelsOnGround > 3) if (!isHeli && (!isPlane || ((CAutomobile*)car)->m_nWheelsOnGround)) { CVector left = CrossProduct(car->GetForward(), CVector(0.0f, 0.0f, 1.0f)); left.Normalise(); CVector up = CrossProduct(left, car->GetForward()); up.Normalise(); float lookingUp = DotProduct(up, Front); if (lookingUp > 0.0f) { float v88 = Asin(Abs(Sin(Beta - (car->GetForward().Heading() - HALFPI)))); float v200; if (v88 <= Atan2(carCol->boundingBox.max.x, -carCol->boundingBox.min.y)) { v200 = (1.5f - carCol->boundingBox.min.y) / Cos(v88); } else { float a6g = 1.2f + carCol->boundingBox.max.x; v200 = a6g / Cos(Max(0.0f, HALFPI - v88)); } maxAlphaAllowed = Cos(Beta - (car->GetForward().Heading() - HALFPI)) * Atan2(car->GetForward().z, car->GetForward().Magnitude2D()) + Atan2(TargetCoors.z - car->GetPosition().z + car->GetHeightAboveRoad(), v200 * 1.2f); if (isCar && ((CAutomobile*)car)->m_nWheelsOnGround > 1 && Abs(DotProduct(car->m_vecTurnSpeed, car->GetForward())) < 0.05f) { maxAlphaAllowed += Cos(Beta - (car->GetForward().Heading() - HALFPI) + HALFPI) * Atan2(car->GetRight().z, car->GetRight().Magnitude2D()); } } } float targetAlpha = Asin(clamp(Front.z, -1.0f, 1.0f)) - zoomModeAlphaOffset; if (targetAlpha <= maxAlphaAllowed) { if (targetAlpha < -CARCAM_SET[camSetArrPos][14]) targetAlpha = -CARCAM_SET[camSetArrPos][14]; } else { targetAlpha = maxAlphaAllowed; } float maxAlphaBlendAmount = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][6]; float targetAlphaBlendAmount = (1.0f - Pow(CARCAM_SET[camSetArrPos][5], CTimer::GetTimeStep())) * (targetAlpha - Alpha); if (targetAlphaBlendAmount <= maxAlphaBlendAmount) { if (targetAlphaBlendAmount < -maxAlphaBlendAmount) targetAlphaBlendAmount = -maxAlphaBlendAmount; } else { targetAlphaBlendAmount = maxAlphaBlendAmount; } // Using GetCarGun(LR/UD) will give us same unprocessed RightStick value as SA float stickX = -(pad->GetCarGunLeftRight()); float stickY = -pad->GetCarGunUpDown(); // In SA this checks for m_bUseMouse3rdPerson so num2 / num8 do not move camera // when Keyboard & Mouse controls are used. To make it work better with III/VC, check for actual pad state instead if (!CPad::IsAffectedByController && !isCar) stickY = 0.0f; else if (CPad::bInvertLook4Pad) stickY = -stickY; float xMovement = Abs(stickX) * (FOV / 80.0f * 5.f / 70.f) * stickX * 0.007f * 0.007f; float yMovement = Abs(stickY) * (FOV / 80.0f * 3.f / 70.f) * stickY * 0.007f * 0.007f; bool correctAlpha = true; // if (SA checks if we aren't in work car, why?) { if (!isCar || car->GetModelIndex() != MI_VOODOO) { correctAlpha = false; } else { xMovement = 0.0f; yMovement = 0.0f; } // } else // yMovement = 0.0; if (!nextDirectionIsForward) { yMovement = 0.0f; xMovement = 0.0f; } if (camSetArrPos == 0 || camSetArrPos == 7) { // This is not working on cars as SA // Because III/VC doesn't have any buttons tied to LeftStick if you're not in Classic Configuration, using Dodo or using GInput/Pad, so :shrug: if (Abs(pad->GetSteeringUpDown()) > 120.0f) { if (car->pDriver && car->pDriver->m_objective != OBJECTIVE_LEAVE_CAR) { yMovement += Abs(pad->GetSteeringUpDown()) * (FOV / 80.0f * 3.f / 70.f) * pad->GetSteeringUpDown() * 0.007f * 0.007f * 0.5; } } } if (yMovement > 0.0) yMovement = yMovement * 0.5; bool mouseChangesBeta = false; // FIX: Disable mouse movement in drive-by, it's buggy. Original SA bug. if (/*bFreeMouseCam &&*/ CCamera::m_bUseMouse3rdPerson && !pad->ArePlayerControlsDisabled() && nextDirectionIsForward) { float mouseY = pad->GetMouseY() * 2.0f; float mouseX = pad->GetMouseX() * -2.0f; // If you want an ability to toggle free cam while steering with mouse, you can add an OR after DisableMouseSteering. // There was a pad->NewState.m_bVehicleMouseLook in SA, which doesn't exists in III. if ((mouseX != 0.0 || mouseY != 0.0) && (CVehicle::m_bDisableMouseSteering)) { yMovement = mouseY * FOV / 80.0f * TheCamera.m_fMouseAccelHorzntl; // Same as SA, horizontal sensitivity. BetaSpeed = 0.0; AlphaSpeed = 0.0; xMovement = mouseX * FOV / 80.0f * TheCamera.m_fMouseAccelHorzntl; targetAlpha = Alpha; stepsLeftToChangeBetaByMouse = 1.0f * 50.0f; mouseChangesBeta = true; } else if (stepsLeftToChangeBetaByMouse > 0.0f) { // Finish rotation by decreasing speed when we stopped moving mouse BetaSpeed = 0.0; AlphaSpeed = 0.0; yMovement = 0.0; xMovement = 0.0; targetAlpha = Alpha; stepsLeftToChangeBetaByMouse = Max(0.0f, stepsLeftToChangeBetaByMouse - CTimer::GetTimeStep()); mouseChangesBeta = true; } } if (correctAlpha) { if (nPreviousMode != MODE_CAM_ON_A_STRING) alphaCorrected = false; if (!alphaCorrected && Abs(zoomModeAlphaOffset + Alpha) > 0.05f) { yMovement = (-zoomModeAlphaOffset - Alpha) * 0.05f; } else alphaCorrected = true; } float alphaSpeedFromStickY = yMovement * CARCAM_SET[camSetArrPos][12]; float betaSpeedFromStickX = xMovement * CARCAM_SET[camSetArrPos][12]; float newAngleSpeedMaxBlendAmount = CARCAM_SET[camSetArrPos][9]; float angleChangeStep = Pow(CARCAM_SET[camSetArrPos][8], CTimer::GetTimeStep()); float targetBetaWithStickBlendAmount = betaSpeedFromStickX + (targetBeta - Beta) / Max(CTimer::GetTimeStep(), 1.0f); if (targetBetaWithStickBlendAmount < -newAngleSpeedMaxBlendAmount) targetBetaWithStickBlendAmount = -newAngleSpeedMaxBlendAmount; else if (targetBetaWithStickBlendAmount > newAngleSpeedMaxBlendAmount) targetBetaWithStickBlendAmount = newAngleSpeedMaxBlendAmount; float angleChangeStepLeft = 1.0f - angleChangeStep; BetaSpeed = targetBetaWithStickBlendAmount * angleChangeStepLeft + angleChangeStep * BetaSpeed; if (Abs(BetaSpeed) < 0.0001f) BetaSpeed = 0.0f; float betaChangePerFrame; if (mouseChangesBeta) betaChangePerFrame = betaSpeedFromStickX; else betaChangePerFrame = CTimer::GetTimeStep() * BetaSpeed; Beta = betaChangePerFrame + Beta; if (TheCamera.m_bJustCameOutOfGarage) { float invHeading = Atan2(Front.y, Front.x); if (invHeading < 0.0f) invHeading += TWOPI; Beta = invHeading + PI; } Beta = CGeneral::LimitRadianAngle(Beta); if (Beta < 0.0f) Beta += TWOPI; if ((camSetArrPos <= 1 || camSetArrPos == 7) && targetAlpha < Alpha && carPosChange >= newDistance) { if (isCar && ((CAutomobile*)car)->m_nWheelsOnGround > 1 || isBike && ((CBike*)car)->m_nWheelsOnGround > 1) alphaSpeedFromStickY += (targetAlpha - Alpha) * 0.075f; } AlphaSpeed = angleChangeStepLeft * alphaSpeedFromStickY + angleChangeStep * AlphaSpeed; float maxAlphaSpeed = newAngleSpeedMaxBlendAmount; if (alphaSpeedFromStickY > 0.0f) maxAlphaSpeed = maxAlphaSpeed * 0.5; if (AlphaSpeed <= maxAlphaSpeed) { float minAlphaSpeed = -maxAlphaSpeed; if (AlphaSpeed < minAlphaSpeed) AlphaSpeed = minAlphaSpeed; } else { AlphaSpeed = maxAlphaSpeed; } if (Abs(AlphaSpeed) < 0.0001f) AlphaSpeed = 0.0f; float alphaWithSpeedAccounted; if (mouseChangesBeta) { alphaWithSpeedAccounted = alphaSpeedFromStickY + targetAlpha; Alpha += alphaSpeedFromStickY; } else { alphaWithSpeedAccounted = CTimer::GetTimeStep() * AlphaSpeed + targetAlpha; Alpha += targetAlphaBlendAmount; } if (Alpha <= maxAlphaAllowed) { float minAlphaAllowed = -CARCAM_SET[camSetArrPos][14]; if (minAlphaAllowed > Alpha) { Alpha = minAlphaAllowed; AlphaSpeed = 0.0f; } } else { Alpha = maxAlphaAllowed; AlphaSpeed = 0.0f; } // Prevent unsignificant angle changes if (Abs(lastAlpha - Alpha) < 0.0001f) Alpha = lastAlpha; lastAlpha = Alpha; if (Abs(lastBeta - Beta) < 0.0001f) Beta = lastBeta; lastBeta = Beta; Front.x = -(cos(Beta) * cos(Alpha)); Front.y = -(sin(Beta) * cos(Alpha)); Front.z = sin(Alpha); GetVectorsReadyForRW(); TheCamera.m_bCamDirectlyBehind = false; TheCamera.m_bCamDirectlyInFront = false; Source = TargetCoors - newDistance * Front; m_cvecTargetCoorsForFudgeInter = TargetCoors; m_aTargetHistoryPosThree = m_aTargetHistoryPosOne; float nextAlpha = alphaWithSpeedAccounted + zoomModeAlphaOffset; float nextFrontX = -(cos(Beta) * cos(nextAlpha)); float nextFrontY = -(sin(Beta) * cos(nextAlpha)); float nextFrontZ = sin(nextAlpha); m_aTargetHistoryPosOne.x = TargetCoors.x - nextFrontX * nextDistance; m_aTargetHistoryPosOne.y = TargetCoors.y - nextFrontY * nextDistance; m_aTargetHistoryPosOne.z = TargetCoors.z - nextFrontZ * nextDistance; m_aTargetHistoryPosTwo.x = TargetCoors.x - nextFrontX * newDistance; m_aTargetHistoryPosTwo.y = TargetCoors.y - nextFrontY * newDistance; m_aTargetHistoryPosTwo.z = TargetCoors.z - nextFrontZ * newDistance; // SA calls SetColVarsVehicle in here if (nextDirectionIsForward) { // LCS uses exactly the same collision code as FollowPedWithMouse, so we will do so. // This is only in LCS! float timestepFactor = Pow(0.99f, CTimer::GetTimeStep()); dontCollideWithCars = (timestepFactor * dontCollideWithCars) + ((1.0f - timestepFactor) * car->m_vecMoveSpeed.Magnitude()); // Our addition #define IS_TRAFFIC_LIGHT(ent) (ent->IsObject() && IsLightObject(ent->GetModelIndex())) // Clip Source and fix near clip CColPoint colPoint; CEntity* entity; CWorld::pIgnoreEntity = CamTargetEntity; if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, true, dontCollideWithCars < 0.1f, false, true, false, true, true) && !IS_TRAFFIC_LIGHT(entity)){ float PedColDist = (TargetCoors - colPoint.point).Magnitude(); float ColCamDist = newDistance - PedColDist; if(entity->IsPed() && ColCamDist > DEFAULT_NEAR + 0.1f){ // Ped in the way but not clipping through if(CWorld::ProcessLineOfSight(colPoint.point, Source, colPoint, entity, true, dontCollideWithCars < 0.1f, false, true, false, true, true) || IS_TRAFFIC_LIGHT(entity)){ PedColDist = (TargetCoors - colPoint.point).Magnitude(); Source = colPoint.point; if(PedColDist < DEFAULT_NEAR + 0.3f) RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); }else{ RwCameraSetNearClipPlane(Scene.camera, Min(ColCamDist-0.35f, DEFAULT_NEAR)); } }else{ Source = colPoint.point; if(PedColDist < DEFAULT_NEAR + 0.3f) RwCameraSetNearClipPlane(Scene.camera, Max(PedColDist-0.3f, 0.05f)); } } CWorld::pIgnoreEntity = nil; // If we're seeing blue hell due to camera intersects some surface, fix it. // SA and LCS have this unrolled. float ViewPlaneHeight = Tan(DEGTORAD(FOV) / 2.0f); float ViewPlaneWidth = ViewPlaneHeight * CDraw::CalculateAspectRatio() * fTweakFOV; float Near = RwCameraGetNearClipPlane(Scene.camera); float radius = ViewPlaneWidth*Near; entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, true); int i = 0; while(entity){ if (IS_TRAFFIC_LIGHT(entity)) break; CVector CamToCol = gaTempSphereColPoints[0].point - Source; float frontDist = DotProduct(CamToCol, Front); float dist = (CamToCol - Front*frontDist).Magnitude() / ViewPlaneWidth; // Try to decrease near clip dist = Max(Min(Near, dist), 0.1f); if(dist < Near) RwCameraSetNearClipPlane(Scene.camera, dist); // Move forward a bit if(dist == 0.1f) Source += (TargetCoors - Source)*0.3f; // Keep testing Near = RwCameraGetNearClipPlane(Scene.camera); radius = ViewPlaneWidth*Near; entity = CWorld::TestSphereAgainstWorld(Source + Front*Near, radius, nil, true, true, false, true, false, true); i++; if(i > 5) entity = nil; } #undef IS_TRAFFIC_LIGHT } TheCamera.m_bCamDirectlyBehind = false; TheCamera.m_bCamDirectlyInFront = false; // ------- LCS specific part starts if (camSetArrPos == 5 && Source.z < 1.0f) // RC Bandit and Baron Source.z = 1.0f; // CCam::FixSourceAboveWaterLevel if (CameraTarget.z >= -2.0f) { float level = -6000.0; if (CWaterLevel::GetWaterLevelNoWaves(Source.x, Source.y, Source.z, &level)) { if (Source.z < level) Source.z = level; } } Front = TargetCoors - Source; // -------- LCS specific part ends GetVectorsReadyForRW(); // SA // gTargetCoordsForLookingBehind = TargetCoors; // SA code from CAutomobile::TankControl/FireTruckControl. if (car->GetModelIndex() == MI_RHINO || car->GetModelIndex() == MI_FIRETRUCK) { float &carGunLR = ((CAutomobile*)car)->m_fCarGunLR; CVector hi = Multiply3x3(Front, car->GetMatrix()); // III/VC's firetruck turret angle is reversed float angleToFace = (car->GetModelIndex() == MI_FIRETRUCK ? -hi.Heading() : hi.Heading()); if (angleToFace <= carGunLR + PI) { if (angleToFace < carGunLR - PI) angleToFace = angleToFace + TWOPI; } else { angleToFace = angleToFace - TWOPI; } float neededTurn = angleToFace - carGunLR; float turnPerFrame = CTimer::GetTimeStep() * (car->GetModelIndex() == MI_FIRETRUCK ? 0.05f : 0.015f); if (neededTurn <= turnPerFrame) { if (neededTurn < -turnPerFrame) angleToFace = carGunLR - turnPerFrame; } else { angleToFace = turnPerFrame + carGunLR; } if (car->GetModelIndex() == MI_RHINO && carGunLR != angleToFace) { DMAudio.PlayOneShot(car->m_audioEntityId, SOUND_CAR_TANK_TURRET_ROTATE, Abs(angleToFace - carGunLR)); } carGunLR = angleToFace; if (carGunLR < -PI) { carGunLR += TWOPI; } else if (carGunLR > PI) { carGunLR -= TWOPI; } // Because firetruk turret also has Y movement if (car->GetModelIndex() == MI_FIRETRUCK) { float &carGunUD = ((CAutomobile*)car)->m_fCarGunUD; float alphaToFace = Atan2(hi.z, hi.Magnitude2D()) + DEGTORAD(15.0f); float neededAlphaTurn = alphaToFace - carGunUD; float alphaTurnPerFrame = CTimer::GetTimeStep() * 0.02f; if (neededAlphaTurn > alphaTurnPerFrame) { neededTurn = alphaTurnPerFrame; carGunUD = neededTurn + carGunUD; } else { if (neededAlphaTurn >= -alphaTurnPerFrame) { carGunUD = alphaToFace; } else { carGunUD = carGunUD - alphaTurnPerFrame; } } float turretMinY = -DEGTORAD(20.0f); float turretMaxY = DEGTORAD(20.0f); if (turretMinY <= carGunUD) { if (carGunUD > turretMaxY) carGunUD = turretMaxY; } else { carGunUD = turretMinY; } } } } #endif ================================================ FILE: src/core/Camera.cpp ================================================ #include "common.h" #include "main.h" #include "Draw.h" #include "World.h" #include "Vehicle.h" #include "Train.h" #include "Automobile.h" #include "Ped.h" #include "PlayerPed.h" #include "Wanted.h" #include "Pad.h" #include "ControllerConfig.h" #include "General.h" #include "ZoneCull.h" #include "SurfaceTable.h" #include "Particle.h" #include "WaterLevel.h" #include "World.h" #include "Garages.h" #include "Replay.h" #include "CutsceneMgr.h" #include "Renderer.h" #include "Timecycle.h" #include "MBlur.h" #include "Text.h" #include "Hud.h" #include "DMAudio.h" #include "FileMgr.h" #include "Frontend.h" #include "SceneEdit.h" #include "Pools.h" #include "Debug.h" #include "GenericGameStorage.h" #include "Camera.h" enum { // car OBBE_WHEEL, OBBE_1, OBBE_2, OBBE_3, OBBE_1STPERSON, // unused OBBE_5, OBBE_ONSTRING, OBBE_COPCAR, OBBE_COPCAR_WHEEL, // ped OBBE_9, OBBE_10, OBBE_11, OBBE_12, OBBE_13, // heli OBBE_14, OBBE_15, OBBE_16, OBBE_17, OBBE_18, OBBE_19, OBBE_ONSTRING_HELI, OBBE_INVALID }; // abbreviate a few things #define PLAYER (CWorld::Players[CWorld::PlayerInFocus].m_pPed) // NB: removed explicit TheCamera from all functions CCamera TheCamera; #ifdef PC_PLAYER_CONTROLS bool CCamera::m_bUseMouse3rdPerson = true; #else bool CCamera::m_bUseMouse3rdPerson = false; #endif bool bDidWeProcessAnyCinemaCam; static bool bSwitchedToObbeCam; float CCamera::m_fMouseAccelHorzntl; float CCamera::m_fMouseAccelVertical; float CCamera::m_f3rdPersonCHairMultX; float CCamera::m_f3rdPersonCHairMultY; #ifdef IMPROVED_CAMERA #define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k) #define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k) #define CTRLJUSTDOWN(key) \ ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \ (KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) #define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) #endif const float ZOOM_ONE_DISTANCE[] = { -0.6f, 0.05f, -3.2f, 0.05f, -2.41f }; const float ZOOM_TWO_DISTANCE[] = { 1.9f, 1.4f, 0.65f, 1.9f, 6.49f }; const float ZOOM_THREE_DISTANCE[] = { 15.9f, 15.9f, 15.9f, 15.9f, 25.25f }; #ifdef FREE_CAM const float LCS_ZOOM_ONE_DISTANCE[] = { -1.0f, -0.2f, -3.2f, 0.05f, -2.41f }; const float LCS_ZOOM_TWO_DISTANCE[] = { 2.0f, 2.2f, 1.65f, 2.9f, 6.49f }; const float LCS_ZOOM_THREE_DISTANCE[] = { 6.0f, 6.0f, 15.9f, 15.9f, 15.0f }; #endif CCamera::CCamera(void) { Init(); } void CCamera::Init(void) { memset(this, 0, sizeof(CCamera)); // this is fine, no vtable m_pRwCamera = nil; m_bPlayerWasOnBike = false; m_1rstPersonRunCloseToAWall = false; m_fPositionAlongSpline = 0.0f; m_bCameraJustRestored = false; Cams[0].Init(); Cams[1].Init(); Cams[2].Init(); Cams[0].Mode = CCam::MODE_FOLLOWPED; Cams[1].Mode = CCam::MODE_FOLLOWPED; m_bEnable1rstPersonCamCntrlsScript = false; m_bAllow1rstPersonWeaponsCamera = false; m_bVehicleSuspenHigh = false; Cams[0].m_fMinRealGroundDist = 1.85f; // TODO: what weird value is this? Cams[0].m_fTargetCloseInDist = 2.0837801f - Cams[0].m_fMinRealGroundDist; Cams[0].m_fTargetZoomGroundOne = 0.25f; Cams[0].m_fTargetZoomGroundTwo = 1.5f; Cams[0].m_fTargetZoomGroundThree = 4.0f; Cams[0].m_fTargetZoomOneZExtra = -0.14f; Cams[0].m_fTargetZoomTwoZExtra = 0.16f; Cams[0].m_fTargetZoomThreeZExtra = 0.25f; // TODO: another weird value Cams[0].m_fTargetZoomZCloseIn = 0.90040702f; m_bMoveCamToAvoidGeom = false; ClearPlayerWeaponMode(); m_bInATunnelAndABigVehicle = false; m_iModeObbeCamIsInForCar = OBBE_INVALID; Cams[0].CamTargetEntity = nil; Cams[1].CamTargetEntity = nil; Cams[2].CamTargetEntity = nil; Cams[0].m_fCamBufferedHeight = 0.0f; Cams[0].m_fCamBufferedHeightSpeed = 0.0f; Cams[1].m_fCamBufferedHeight = 0.0f; Cams[1].m_fCamBufferedHeightSpeed = 0.0f; Cams[0].m_bCamLookingAtVector = false; Cams[1].m_bCamLookingAtVector = false; Cams[2].m_bCamLookingAtVector = false; Cams[0].m_fPlayerVelocity = 0.0f; Cams[1].m_fPlayerVelocity = 0.0f; Cams[2].m_fPlayerVelocity = 0.0f; m_bHeadBob = false; m_fFractionInterToStopMoving = 0.25f; m_fFractionInterToStopCatchUp = 0.75f; m_fGaitSwayBuffer = 0.85f; m_bScriptParametersSetForInterPol = false; m_uiCamShakeStart = 0; m_fCamShakeForce = 0.0f; m_iModeObbeCamIsInForCar = OBBE_INVALID; m_bIgnoreFadingStuffForMusic = false; m_bWaitForInterpolToFinish = false; pToGarageWeAreIn = nil; pToGarageWeAreInForHackAvoidFirstPerson = nil; m_bPlayerIsInGarage = false; m_bJustCameOutOfGarage = false; m_fNearClipScript = DEFAULT_NEAR; m_bUseNearClipScript = false; m_vecDoingSpecialInterPolation = false; m_bAboveGroundTrainNodesLoaded = false; m_bBelowGroundTrainNodesLoaded = false; m_WideScreenOn = false; m_fFOV_Wide_Screen = 0.0f; m_bRestoreByJumpCut = false; CarZoomIndicator = CAM_ZOOM_2; PedZoomIndicator = CAM_ZOOM_2; CarZoomValueSmooth = 0.0f; m_fPedZoomValueSmooth = 0.0f; pTargetEntity = nil; if(FindPlayerVehicle()) pTargetEntity = FindPlayerVehicle(); else pTargetEntity = CWorld::Players[CWorld::PlayerInFocus].m_pPed; m_bInitialNodeFound = false; m_ScreenReductionPercentage = 0.0f; m_ScreenReductionSpeed = 0.0f; m_WideScreenOn = false; m_bWantsToSwitchWidescreenOff = false; WorldViewerBeingUsed = false; PlayerExhaustion = 1.0f; DebugCamMode = CCam::MODE_NONE; m_PedOrientForBehindOrInFront = 0.0f; if(!FrontEndMenuManager.m_bWantToRestart){ m_bFading = false; CDraw::FadeValue = 0; m_fFLOATingFade = 0.0f; m_bMusicFading = false; m_fTimeToFadeMusic = 0.0f; m_fFLOATingFadeMusic = 0.0f; m_fMouseAccelVertical = 0.003f; m_fMouseAccelHorzntl = 0.0025f; } if(FrontEndMenuManager.m_bWantToRestart) m_fTimeToFadeMusic = 0.0f; m_bStartingSpline = false; m_iTypeOfSwitch = INTERPOLATION; m_bUseScriptZoomValuePed = false; m_bUseScriptZoomValueCar = false; m_fPedZoomValueScript = 0.0f; m_fCarZoomValueScript = 0.0f; m_bUseSpecialFovTrain = false; m_fFovForTrain = 70.0f; // or DefaultFOV from Cam.cpp m_iModeToGoTo = CCam::MODE_FOLLOWPED; m_bJust_Switched = false; m_bUseTransitionBeta = false; m_matrix.SetScale(1.0f); m_bTargetJustBeenOnTrain = false; m_bInitialNoNodeStaticsSet = false; m_uiLongestTimeInMill = 5000; m_uiTimeLastChange = 0; m_uiTimeWeEnteredIdle = 0; m_bIdleOn = false; m_uiTimeWeLeftIdle_StillNoInput = 0; m_uiTimeWeEnteredIdle = 0; LODDistMultiplier = 1.0f; m_bCamDirectlyBehind = false; m_bCamDirectlyInFront = false; m_motionBlur = 0; m_bGarageFixedCamPositionSet = false; SetMotionBlur(255, 255, 255, 0, 0); m_bCullZoneChecksOn = false; m_bFailedCullZoneTestPreviously = false; m_iCheckCullZoneThisNumFrames = 6; m_iZoneCullFrameNumWereAt = 0; m_CameraAverageSpeed = 0.0f; m_CameraSpeedSoFar = 0.0f; m_PreviousCameraPosition = CVector(0.0f, 0.0f, 0.0f); m_iWorkOutSpeedThisNumFrames = 4; m_iNumFramesSoFar = 0; m_bJustInitalised = true; m_uiTransitionState = 0; m_uiTimeTransitionStart = 0; m_bLookingAtPlayer = true; m_f3rdPersonCHairMultX = 0.53f; m_f3rdPersonCHairMultY = 0.4f; m_fAvoidTheGeometryProbsTimer = 0.0f; m_nAvoidTheGeometryProbsDirn = 0; } void CCamera::Process(void) { // static bool InterpolatorNotInitialised = true; // unused static float PlayerMinDist = 1.3f; static bool WasPreviouslyInterSyhonFollowPed = false; // only written float FOV = 0.0f; float oldBeta, newBeta; float deltaBeta = 0.0f; bool lookLRBVehicle = false; CVector CamFront, CamUp, CamRight, CamSource, Target; m_bJust_Switched = false; m_RealPreviousCameraPosition = GetPosition(); // Update target entity if(m_bLookingAtPlayer || m_bTargetJustBeenOnTrain || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) UpdateTargetEntity(); if(pTargetEntity == nil) pTargetEntity = FindPlayerPed(); if(Cams[ActiveCam].CamTargetEntity == nil) Cams[ActiveCam].CamTargetEntity = pTargetEntity; if(Cams[(ActiveCam+1)%2].CamTargetEntity == nil) Cams[(ActiveCam+1)%2].CamTargetEntity = pTargetEntity; CamControl(); if(m_bFading) ProcessFade(); if(m_bMusicFading) ProcessMusicFade(); if(m_WideScreenOn) ProcessWideScreenOn(); #ifndef MASTER #ifdef IMPROVED_CAMERA if(CPad::GetPad(1)->GetCircleJustDown() || CTRLJUSTDOWN('B')){ #else if(CPad::GetPad(1)->GetCircleJustDown()){ #endif WorldViewerBeingUsed = !WorldViewerBeingUsed; if(WorldViewerBeingUsed) InitialiseCameraForDebugMode(); else CPad::m_bMapPadOneToPadTwo = false; } #endif RwCameraSetNearClipPlane(Scene.camera, DEFAULT_NEAR); if(Cams[ActiveCam].Front.x == 0.0f && Cams[ActiveCam].Front.y == 0.0f) oldBeta = 0.0f; else oldBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y); Cams[ActiveCam].Process(); Cams[ActiveCam].ProcessSpecialHeightRoutines(); if(Cams[ActiveCam].Front.x == 0.0f && Cams[ActiveCam].Front.y == 0.0f) newBeta = 0.0f; else newBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y); // Stop transition when it's done if(m_uiTransitionState != 0){ if(CTimer::GetTimeInMilliseconds() > m_uiTransitionDuration+m_uiTimeTransitionStart){ m_uiTransitionState = 0; m_vecDoingSpecialInterPolation = false; m_bWaitForInterpolToFinish = false; } } if(m_bUseNearClipScript) RwCameraSetNearClipPlane(Scene.camera, m_fNearClipScript); deltaBeta = newBeta - oldBeta; while(deltaBeta >= PI) deltaBeta -= 2*PI; while(deltaBeta < -PI) deltaBeta += 2*PI; if(Abs(deltaBeta) > 0.3f) m_bJust_Switched = true; #ifndef MASTER // Debug stuff if(!gbModelViewer) Cams[ActiveCam].PrintMode(); // actually missing in VC if(WorldViewerBeingUsed) Cams[2].Process(); #endif if(Cams[ActiveCam].DirectionWasLooking != LOOKING_FORWARD && pTargetEntity->IsVehicle()) lookLRBVehicle = true; if(m_uiTransitionState != 0 && !lookLRBVehicle){ // Process transition uint32 currentTime = CTimer::GetTimeInMilliseconds() - m_uiTimeTransitionStart; if(currentTime >= m_uiTransitionDuration) currentTime = m_uiTransitionDuration; float fractionInter = (float) currentTime / m_uiTransitionDuration; float fractionInterTarget = (float) currentTime / m_uiTransitionDurationTargetCoors; fractionInterTarget = clamp(fractionInterTarget, 0.0f, 1.0f); // Interpolate target separately if(fractionInterTarget <= m_fFractionInterToStopMovingTarget){ float inter; if(m_fFractionInterToStopMovingTarget == 0.0f) inter = 0.0f; else inter = (m_fFractionInterToStopMovingTarget - fractionInterTarget)/m_fFractionInterToStopMovingTarget; inter = 0.5f - 0.5*Cos(inter*PI); // smooth it m_vecTargetWhenInterPol = m_cvecStartingTargetForInterPol + inter*m_cvecTargetSpeedAtStartInter; Target = m_vecTargetWhenInterPol; }else if(fractionInterTarget > m_fFractionInterToStopMovingTarget){ float inter; if(m_fFractionInterToStopCatchUpTarget == 0.0f) inter = 0.0f; else inter = (fractionInterTarget - m_fFractionInterToStopMovingTarget)/m_fFractionInterToStopCatchUpTarget; inter = 0.5f - 0.5*Cos(inter*PI); // smooth it if(m_fFractionInterToStopMovingTarget == 0.0f) m_vecTargetWhenInterPol = m_cvecStartingTargetForInterPol; Target = m_vecTargetWhenInterPol + inter*(Cams[ActiveCam].m_cvecTargetCoorsForFudgeInter - m_vecTargetWhenInterPol); } if(fractionInter <= m_fFractionInterToStopMoving){ float inter; if(m_fFractionInterToStopMoving == 0.0f) inter = 0.0f; else inter = (m_fFractionInterToStopMoving - fractionInter)/m_fFractionInterToStopMoving; inter = 0.5f - 0.5*Cos(inter*PI); // smooth it m_vecSourceWhenInterPol = m_cvecStartingSourceForInterPol + inter*m_cvecSourceSpeedAtStartInter; if(m_bLookingAtPlayer){ CVector ToCam = m_vecSourceWhenInterPol - Target; if(ToCam.Magnitude2D() < PlayerMinDist){ float beta = CGeneral::GetATanOfXY(ToCam.x, ToCam.y); CamSource.x = Target.x + PlayerMinDist*Cos(beta); CamSource.y = Target.y + PlayerMinDist*Sin(beta); } } m_vecUpWhenInterPol = m_cvecStartingUpForInterPol + inter*m_cvecUpSpeedAtStartInter; m_fFOVWhenInterPol = m_fStartingFOVForInterPol + inter*m_fFOVSpeedAtStartInter; CamSource = m_vecSourceWhenInterPol; CamFront = Target - CamSource; StoreValuesDuringInterPol(CamSource, m_vecTargetWhenInterPol, m_vecUpWhenInterPol, m_fFOVWhenInterPol); CamFront.Normalise(); if(m_bLookingAtPlayer) CamUp = CVector(0.0f, 0.0f, 1.0f); else CamUp = m_vecUpWhenInterPol; CamUp.Normalise(); if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ CamFront.Normalise(); CamRight = CVector(-1.0f, 0.0f, 0.0f); CamUp = CrossProduct(CamFront, CamRight); CamUp.Normalise(); }else{ CamFront.Normalise(); CamUp.Normalise(); CamRight = CrossProduct(CamFront, CamUp); CamRight.Normalise(); CamUp = CrossProduct(CamRight, CamFront); CamUp.Normalise(); } FOV = m_fFOVWhenInterPol; }else if(fractionInter > m_fFractionInterToStopMoving && fractionInter <= 1.0f){ float inter; if(m_fFractionInterToStopCatchUp == 0.0f) inter = 0.0f; else inter = (fractionInter - m_fFractionInterToStopMoving)/m_fFractionInterToStopCatchUp; inter = 0.5f - 0.5*Cos(inter*PI); // smooth it CamSource = m_vecSourceWhenInterPol + inter*(Cams[ActiveCam].Source - m_vecSourceWhenInterPol); if(m_bLookingAtPlayer){ CVector ToCam = m_vecSourceWhenInterPol - Target; if(ToCam.Magnitude2D() < PlayerMinDist){ float beta = CGeneral::GetATanOfXY(ToCam.x, ToCam.y); CamSource.x = Target.x + PlayerMinDist*Cos(beta); CamSource.y = Target.y + PlayerMinDist*Sin(beta); } } FOV = m_fFOVWhenInterPol + inter*(Cams[ActiveCam].FOV - m_fFOVWhenInterPol); CamUp = m_vecUpWhenInterPol + inter*(Cams[ActiveCam].Up - m_vecUpWhenInterPol); deltaBeta = Cams[ActiveCam].m_fTrueBeta - m_fBetaWhenInterPol; MakeAngleLessThan180(deltaBeta); CamFront = Target - CamSource; StoreValuesDuringInterPol(CamSource, Target, CamUp, FOV); CamFront.Normalise(); if(m_bLookingAtPlayer) CamUp = CVector(0.0f, 0.0f, 1.0f); if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ CamFront.Normalise(); CamRight = CVector(-1.0f, 0.0f, 0.0f); CamUp = CrossProduct(CamFront, CamRight); CamUp.Normalise(); }else{ CamFront.Normalise(); CamUp.Normalise(); CamRight = CrossProduct(CamFront, CamUp); CamRight.Normalise(); CamUp = CrossProduct(CamRight, CamFront); CamUp.Normalise(); } #ifndef FIX_BUGS // BUG: FOV was already interpolated but m_fFOVWhenInterPol was not FOV = m_fFOVWhenInterPol; #endif } CVector Dist = CamSource - Target; float DistOnGround = Dist.Magnitude2D(); float Alpha = CGeneral::GetATanOfXY(DistOnGround, Dist.z); float Beta = CGeneral::GetATanOfXY(Dist.x, Dist.y); Cams[ActiveCam].KeepTrackOfTheSpeed(CamSource, Target, CamUp, Alpha, Beta, FOV); }else{ // No transition, take Cam values directly #ifndef MASTER if(WorldViewerBeingUsed){ CamSource = Cams[2].Source; CamFront = Cams[2].Front; CamUp = Cams[2].Up; FOV = Cams[2].FOV; }else #endif { CamSource = Cams[ActiveCam].Source; CamUp = Cams[ActiveCam].Up; if(m_bMoveCamToAvoidGeom){ CamSource += m_vecClearGeometryVec; CamFront = Cams[ActiveCam].m_cvecTargetCoorsForFudgeInter - CamSource; CamFront.Normalise(); CVector Right = CrossProduct(CamFront, CamUp); Right.Normalise(); CamUp = CrossProduct(Right, CamFront); CamUp.Normalise(); }else{ CamFront = Cams[ActiveCam].Front; CamUp = Cams[ActiveCam].Up; } FOV = Cams[ActiveCam].FOV; } WasPreviouslyInterSyhonFollowPed = false; // unused } if(m_uiTransitionState != 0) if(!m_bLookingAtVector && m_bLookingAtPlayer && !CCullZones::CamStairsForPlayer() && !m_bPlayerIsInGarage){ CEntity *entity = nil; CColPoint colPoint; if(CWorld::ProcessLineOfSight(pTargetEntity->GetPosition(), CamSource, colPoint, entity, true, false, false, true, false, true, true)){ CamSource = colPoint.point; RwCameraSetNearClipPlane(Scene.camera, 0.05f); } } if(CMBlur::Drunkness > 0.0f){ static float DrunkAngle; int tableIndex = (int)(DEGTORAD(DrunkAngle)/TWOPI * CParticle::SIN_COS_TABLE_SIZE) & CParticle::SIN_COS_TABLE_SIZE-1; DrunkAngle += 5.0f; #ifndef FIX_BUGS // This just messes up interpolation, probably not what they intended // and multiplying the interpolated FOV is also a bit extreme // so let's not do any of this nonsense Cams[ActiveCam].FOV *= (1.0f + CMBlur::Drunkness); #endif CamSource.x += -0.02f*CMBlur::Drunkness * CParticle::m_CosTable[tableIndex]; CamSource.y += -0.02f*CMBlur::Drunkness * CParticle::m_SinTable[tableIndex]; CamUp.Normalise(); CamUp.x += 0.05f*CMBlur::Drunkness * CParticle::m_CosTable[tableIndex]; CamUp.y += 0.05f*CMBlur::Drunkness * CParticle::m_SinTable[tableIndex]; CamUp.Normalise(); CamFront.Normalise(); CamFront.x += -0.1f*CMBlur::Drunkness * CParticle::m_CosTable[tableIndex]; CamFront.y += -0.1f*CMBlur::Drunkness * CParticle::m_SinTable[tableIndex]; CamFront.Normalise(); CamRight = CrossProduct(CamFront, CamUp); CamRight.Normalise(); CamUp = CrossProduct(CamRight, CamFront); CamUp.Normalise(); } GetMatrix().GetRight() = CrossProduct(CamUp, CamFront); // actually Left GetMatrix().GetForward() = CamFront; GetMatrix().GetUp() = CamUp; GetMatrix().GetPosition() = CamSource; // Process Shake float shakeStrength = m_fCamShakeForce - 0.28f*(CTimer::GetTimeInMilliseconds()-m_uiCamShakeStart)/1000.0f; shakeStrength = clamp(shakeStrength, 0.0f, 2.0f); int shakeRand = CGeneral::GetRandomNumber(); float shakeOffset = shakeStrength*0.1f; GetMatrix().GetPosition().x += shakeOffset * ((shakeRand & 0xF) - 7); GetMatrix().GetPosition().y += shakeOffset * (((shakeRand & 0xF0) >> 4) - 7); GetMatrix().GetPosition().z += shakeOffset * (((shakeRand & 0xF00) >> 8) - 7); if(shakeOffset > 0.0f && m_BlurType != MOTION_BLUR_SNIPER) SetMotionBlurAlpha(Min((int)(shakeStrength*255.0f) + 25, 150)); static bool bExtra1stPrsBlur = false; if(Cams[ActiveCam].Mode == CCam::MODE_1STPERSON && FindPlayerVehicle() && FindPlayerVehicle()->GetUp().z < 0.2f){ SetMotionBlur(230, 230, 230, 215, MOTION_BLUR_LIGHT_SCENE); bExtra1stPrsBlur = true; }else if(bExtra1stPrsBlur){ SetMotionBlur(CTimeCycle::GetBlurRed(), CTimeCycle::GetBlurGreen(), CTimeCycle::GetBlurBlue(), m_motionBlur, MOTION_BLUR_LIGHT_SCENE); bExtra1stPrsBlur = false; } CalculateDerivedValues(); CDraw::SetFOV(FOV); // Set RW camera #ifndef MASTER if(WorldViewerBeingUsed){ RwFrame *frame = RwCameraGetFrame(m_pRwCamera); CVector Source = Cams[2].Source; CVector Front = Cams[2].Front; CVector Up = Cams[2].Up; GetMatrix().GetRight() = CrossProduct(Up, Front); GetMatrix().GetForward() = Front; GetMatrix().GetUp() = Up; GetMatrix().GetPosition() = Source; CDraw::SetFOV(Cams[2].FOV); m_vecGameCamPos = Cams[ActiveCam].Source; *RwMatrixGetPos(RwFrameGetMatrix(frame)) = GetPosition(); *RwMatrixGetAt(RwFrameGetMatrix(frame)) = GetForward(); *RwMatrixGetUp(RwFrameGetMatrix(frame)) = GetUp(); *RwMatrixGetRight(RwFrameGetMatrix(frame)) = GetRight(); RwMatrixUpdate(RwFrameGetMatrix(frame)); RwFrameUpdateObjects(frame); }else #endif { RwFrame *frame = RwCameraGetFrame(m_pRwCamera); m_vecGameCamPos = GetPosition(); *RwMatrixGetPos(RwFrameGetMatrix(frame)) = GetPosition(); *RwMatrixGetAt(RwFrameGetMatrix(frame)) = GetForward(); *RwMatrixGetUp(RwFrameGetMatrix(frame)) = GetUp(); *RwMatrixGetRight(RwFrameGetMatrix(frame)) = GetRight(); RwMatrixUpdate(RwFrameGetMatrix(frame)); RwFrameUpdateObjects(frame); RwFrameOrthoNormalize(frame); } UpdateSoundDistances(); if((CTimer::GetFrameCounter()&0xF) == 3) DistanceToWater = CWaterLevel::CalcDistanceToWater(GetPosition().x, GetPosition().y); // LOD dist if(!CCutsceneMgr::IsRunning() || CCutsceneMgr::UseLodMultiplier()){ LODDistMultiplier = 70.0f/CDraw::GetFOV(); if(GetPosition().z > 55.0f && FindPlayerVehicle() && FindPlayerVehicle()->pHandling->Flags & (HANDLING_IS_HELI|HANDLING_IS_PLANE) || FindPlayerPed()->m_attachedTo){ LODDistMultiplier *= 1.0f + Max((GetPosition().z - 55.0f)/60.0f, 0.0f); float NewNear = DEFAULT_NEAR * (1.0f + Max((GetPosition().z - 55.0f)/60.0f, 0.0f)); if(RwCameraGetNearClipPlane(Scene.camera) >= DEFAULT_NEAR) RwCameraSetNearClipPlane(Scene.camera, NewNear); } if(LODDistMultiplier > 2.2f) LODDistMultiplier = 2.2f; }else LODDistMultiplier = 1.0f; GenerationDistMultiplier = LODDistMultiplier; LODDistMultiplier *= CRenderer::ms_lodDistScale; CDraw::SetNearClipZ(RwCameraGetNearClipPlane(m_pRwCamera)); CDraw::SetFarClipZ(RwCameraGetFarClipPlane(m_pRwCamera)); // Keep track of speed if(m_bJustInitalised || m_bJust_Switched){ m_PreviousCameraPosition = GetPosition(); m_bJustInitalised = false; } m_CameraSpeedSoFar += (GetPosition() - m_PreviousCameraPosition).Magnitude(); m_iNumFramesSoFar++; if(m_iNumFramesSoFar == m_iWorkOutSpeedThisNumFrames){ m_CameraAverageSpeed = m_CameraSpeedSoFar / m_iWorkOutSpeedThisNumFrames; m_CameraSpeedSoFar = 0.0f; m_iNumFramesSoFar = 0; } m_PreviousCameraPosition = GetPosition(); if(Cams[ActiveCam].DirectionWasLooking != LOOKING_FORWARD && Cams[ActiveCam].Mode != CCam::MODE_TOP_DOWN_PED){ Cams[ActiveCam].Source = Cams[ActiveCam].SourceBeforeLookBehind; Orientation += PI; } if(m_uiTransitionState != 0){ int OtherCam = (ActiveCam+1)%2; if(Cams[OtherCam].CamTargetEntity && pTargetEntity && pTargetEntity->IsPed() && !Cams[OtherCam].CamTargetEntity->IsVehicle() && Cams[ActiveCam].Mode != CCam::MODE_TOP_DOWN_PED && Cams[ActiveCam].DirectionWasLooking != LOOKING_FORWARD){ Cams[OtherCam].Source = Cams[ActiveCam%2].SourceBeforeLookBehind; Orientation += PI; } } m_bCameraJustRestored = false; m_bMoveCamToAvoidGeom = false; } void CCamera::CamControl(void) { static bool PlaceForFixedWhenSniperFound = false; static int16 ReqMode; bool switchByJumpCut = false; bool stairs = false; bool boatTarget = false; int PrevMode = Cams[ActiveCam].Mode; CVector targetPos; CVector garageCenter, garageDoorPos1, garageDoorPos2; CVector garageCenterToDoor, garageCamPos; int whichDoor; m_bObbeCinematicPedCamOn = false; m_bObbeCinematicCarCamOn = false; m_bUseTransitionBeta = false; m_bUseSpecialFovTrain = false; m_bJustCameOutOfGarage = false; m_bTargetJustCameOffTrain = false; m_bInATunnelAndABigVehicle = false; m_bJustJumpedOutOf1stPersonBecauseOfTarget = false; bSwitchedToObbeCam = false; if(Cams[ActiveCam].CamTargetEntity == nil && pTargetEntity == nil) pTargetEntity = PLAYER; m_iZoneCullFrameNumWereAt++; if(m_iZoneCullFrameNumWereAt > m_iCheckCullZoneThisNumFrames) m_iZoneCullFrameNumWereAt = 1; m_bCullZoneChecksOn = m_iZoneCullFrameNumWereAt == m_iCheckCullZoneThisNumFrames; if(m_bCullZoneChecksOn) m_bFailedCullZoneTestPreviously = CCullZones::CamCloseInForPlayer(); if(m_bLookingAtPlayer){ CPad::GetPad(0)->DisablePlayerControls &= ~PLAYERCONTROL_CAMERA; FindPlayerPed()->bIsVisible = true; } if(!CTimer::GetIsPaused() && !m_bIdleOn){ float CloseInCarHeightTarget = 0.0f; float CloseInPedHeightTarget = 0.0f; if(m_bTargetJustBeenOnTrain){ // Getting off train if(!pTargetEntity->IsVehicle() || !((CVehicle*)pTargetEntity)->IsTrain()){ Restore(); m_bTargetJustCameOffTrain = true; m_bTargetJustBeenOnTrain = false; SetWideScreenOff(); } } // Vehicle target if(pTargetEntity->IsVehicle()){ #ifdef GTA_TRAIN if(((CVehicle*)pTargetEntity)->IsTrain()){ if(!m_bTargetJustBeenOnTrain){ m_bInitialNodeFound = false; m_bInitialNoNodeStaticsSet = false; } Process_Train_Camera_Control(); }else #endif { if(((CVehicle*)pTargetEntity)->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) boatTarget = true; // Change user selected mode if(CPad::GetPad(0)->CycleCameraModeUpJustDown() && !CReplay::IsPlayingBack() && (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && !m_WideScreenOn){ CarZoomIndicator--; // disable topdown here if(CarZoomIndicator == CAM_ZOOM_TOPDOWN) CarZoomIndicator--; } if(CPad::GetPad(0)->CycleCameraModeDownJustDown() && !CReplay::IsPlayingBack() && (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && !m_WideScreenOn){ CarZoomIndicator++; if(CarZoomIndicator == CAM_ZOOM_TOPDOWN) CarZoomIndicator++; } if(!m_bFailedCullZoneTestPreviously){ if(CarZoomIndicator < CAM_ZOOM_1STPRS) CarZoomIndicator = CAM_ZOOM_CINEMATIC; else if(CarZoomIndicator > CAM_ZOOM_CINEMATIC) CarZoomIndicator = CAM_ZOOM_1STPRS; } if(m_bFailedCullZoneTestPreviously) if(CarZoomIndicator != CAM_ZOOM_1STPRS && CarZoomIndicator != CAM_ZOOM_TOPDOWN) ReqMode = CCam::MODE_CAM_ON_A_STRING; int vehType = ((CVehicle*)pTargetEntity)->m_vehType; if(((CVehicle*)pTargetEntity)->IsBoat() && pTargetEntity->GetModelIndex() == MI_SKIMMER) vehType = VEHICLE_TYPE_CAR; switch(vehType){ case VEHICLE_TYPE_CAR: case VEHICLE_TYPE_BIKE:{ CAttributeZone *stairsZone = nil; if(vehType == VEHICLE_TYPE_BIKE && CCullZones::CamStairsForPlayer()){ stairsZone = CCullZones::FindZoneWithStairsAttributeForPlayer(); if(stairsZone) stairs = true; } if(CGarages::IsPointInAGarageCameraZone(pTargetEntity->GetPosition()) || stairs){ if(!m_bGarageFixedCamPositionSet && m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE){ if(pToGarageWeAreIn || stairsZone){ float ground; bool foundGround; if(pToGarageWeAreIn){ // This is all very strange.... // targetPos = pTargetEntity->GetPosition(); // unused if(pToGarageWeAreIn->m_pDoor1){ whichDoor = 1; garageDoorPos1.x = pToGarageWeAreIn->m_fDoor1X; garageDoorPos1.y = pToGarageWeAreIn->m_fDoor1Y; garageDoorPos1.z = 0.0f; // targetPos.z = 0.0f; // unused // (targetPos - doorPos1).Magnitude(); // unused }else if(pToGarageWeAreIn->m_pDoor2){ whichDoor = 2; #ifdef FIX_BUGS garageDoorPos2.x = pToGarageWeAreIn->m_fDoor2X; garageDoorPos2.y = pToGarageWeAreIn->m_fDoor2Y; garageDoorPos2.z = 0.0f; #endif }else{ whichDoor = 1; garageDoorPos1.x = pTargetEntity->GetPosition().x; garageDoorPos1.y = pTargetEntity->GetPosition().y; #ifdef FIX_BUGS garageDoorPos1.z = 0.0f; #else garageDoorPos2.z = 0.0f; #endif } }else{ assert(stairsZone); whichDoor = 1; garageDoorPos1 = Cams[ActiveCam].Source; garageCenter = CVector((stairsZone->minx+stairsZone->maxx)/2.0f, (stairsZone->miny+stairsZone->maxy)/2.0f, 0.0f); if((garageCenter-garageDoorPos1).Magnitude() > 15.0f){ bool bClearViewOutside = true; CVector dirOutside = pTargetEntity->GetPosition() - garageCenter; dirOutside.z = 0.0f; dirOutside.Normalise(); float zoneDim = stairsZone->maxx - stairsZone->minx; if(zoneDim < stairsZone->maxy - stairsZone->miny) zoneDim = stairsZone->maxy - stairsZone->miny; zoneDim *= 2.0f; CVector posOutside = pTargetEntity->GetPosition() + zoneDim*dirOutside; if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), posOutside, true, false, false, false, false, false, true)){ posOutside = pTargetEntity->GetPosition() - zoneDim*dirOutside; if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), posOutside, true, false, false, false, false, false, true)) bClearViewOutside = false; } if(bClearViewOutside) garageDoorPos1 = posOutside; } } if(pToGarageWeAreIn){ garageCenter.x = pToGarageWeAreIn->GetGarageCenterX(); garageCenter.y = pToGarageWeAreIn->GetGarageCenterY(); garageCenter.z = 0.0f; }else{ garageDoorPos1.z = 0.0f; if(stairsZone == nil) // how can this be true? garageCenter = CVector(pTargetEntity->GetPosition().x, pTargetEntity->GetPosition().y, 0.0f); } if(whichDoor == 1) garageCenterToDoor = garageDoorPos1 - garageCenter; else garageCenterToDoor = garageDoorPos2 - garageCenter; targetPos = pTargetEntity->GetPosition(); ground = CWorld::FindGroundZFor3DCoord(targetPos.x, targetPos.y, targetPos.z, &foundGround); if(!foundGround) ground = targetPos.z - 0.2f; garageCenterToDoor.z = 0.0f; garageCenterToDoor.Normalise(); if(whichDoor == 1){ if(pToGarageWeAreIn == nil && stairsZone){ float zoneDim = stairsZone->maxx - stairsZone->minx; if(zoneDim < stairsZone->maxy - stairsZone->miny) zoneDim = stairsZone->maxy - stairsZone->miny; garageCamPos = garageCenter + (0.7f*zoneDim + 3.75f)*garageCenterToDoor; }else garageCamPos = garageDoorPos1 + 13.0f*garageCenterToDoor; }else garageCamPos = garageDoorPos2 + 13.0f*garageCenterToDoor; garageCamPos.z = ground + 3.1f; SetCamPositionForFixedMode(garageCamPos, CVector(0.0f, 0.0f, 0.0f)); m_bGarageFixedCamPositionSet = true; } } if(CGarages::CameraShouldBeOutside() && m_bGarageFixedCamPositionSet && (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE)){ if(pToGarageWeAreIn){ ReqMode = CCam::MODE_FIXED; m_bPlayerIsInGarage = true; } }else{ if(m_bPlayerIsInGarage){ m_bJustCameOutOfGarage = true; m_bPlayerIsInGarage = false; } ReqMode = CCam::MODE_CAM_ON_A_STRING; } }else{ if(m_bPlayerIsInGarage){ m_bJustCameOutOfGarage = true; m_bPlayerIsInGarage = false; } m_bGarageFixedCamPositionSet = false; ReqMode = CCam::MODE_CAM_ON_A_STRING; } break; } case VEHICLE_TYPE_BOAT: ReqMode = CCam::MODE_BEHINDBOAT; break; default: break; } int vehApp = ((CVehicle*)pTargetEntity)->GetVehicleAppearance(); int vehArrPos = 0; GetArrPosForVehicleType(vehApp, vehArrPos); // Car zoom value if (CarZoomIndicator == CAM_ZOOM_1STPRS && !m_bPlayerIsInGarage) { CarZoomValue = 0.0f; ReqMode = CCam::MODE_1STPERSON; } #ifdef FREE_CAM else if (bFreeCam) { if (CarZoomIndicator == CAM_ZOOM_1) CarZoomValue = LCS_ZOOM_ONE_DISTANCE[vehArrPos]; else if (CarZoomIndicator == CAM_ZOOM_2) CarZoomValue = LCS_ZOOM_TWO_DISTANCE[vehArrPos]; else if (CarZoomIndicator == CAM_ZOOM_3) CarZoomValue = LCS_ZOOM_THREE_DISTANCE[vehArrPos]; } #endif else if (CarZoomIndicator == CAM_ZOOM_1) CarZoomValue = ZOOM_ONE_DISTANCE[vehArrPos]; else if(CarZoomIndicator == CAM_ZOOM_2) CarZoomValue = ZOOM_TWO_DISTANCE[vehArrPos]; else if(CarZoomIndicator == CAM_ZOOM_3) CarZoomValue = ZOOM_THREE_DISTANCE[vehArrPos]; if(CarZoomIndicator == CAM_ZOOM_TOPDOWN && !m_bPlayerIsInGarage){ CarZoomValue = 1.0f; ReqMode = CCam::MODE_TOPDOWN; } // Check if we have to go into first person if(vehType == VEHICLE_TYPE_CAR && !m_bPlayerIsInGarage){ if(CCullZones::Cam1stPersonForPlayer() && pTargetEntity->GetColModel()->boundingBox.GetSize().z >= 3.026f && pToGarageWeAreInForHackAvoidFirstPerson == nil){ ReqMode = CCam::MODE_1STPERSON; m_bInATunnelAndABigVehicle = true; } } if(ReqMode == CCam::MODE_TOPDOWN && (CCullZones::Cam1stPersonForPlayer() || CCullZones::CamNoRain() || CCullZones::PlayerNoRain())) ReqMode = CCam::MODE_1STPERSON; // Smooth zoom value - ugly code if(m_bUseScriptZoomValueCar){ if(CarZoomValueSmooth < m_fCarZoomValueScript){ CarZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); CarZoomValueSmooth = Min(CarZoomValueSmooth, m_fCarZoomValueScript); }else{ CarZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); CarZoomValueSmooth = Max(CarZoomValueSmooth, m_fCarZoomValueScript); } }else if(m_bFailedCullZoneTestPreviously){ CloseInCarHeightTarget = 0.65f; if(CarZoomValueSmooth < -0.65f){ CarZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); CarZoomValueSmooth = Min(CarZoomValueSmooth, -0.65f); }else{ CarZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); CarZoomValueSmooth = Max(CarZoomValueSmooth, -0.65f); } }else{ if(CarZoomValueSmooth < CarZoomValue){ CarZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); CarZoomValueSmooth = Min(CarZoomValueSmooth, CarZoomValue); }else{ CarZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); CarZoomValueSmooth = Max(CarZoomValueSmooth, CarZoomValue); } } WellBufferMe(CloseInCarHeightTarget, &Cams[ActiveCam].m_fCloseInCarHeightOffset, &Cams[ActiveCam].m_fCloseInCarHeightOffsetSpeed, 0.1f, 0.25f, false); // Fallen into water if(Cams[ActiveCam].IsTargetInWater(Cams[ActiveCam].Source) && !boatTarget && !Cams[ActiveCam].CamTargetEntity->IsPed() && pTargetEntity->GetModelIndex() != MI_SKIMMER && pTargetEntity->GetModelIndex() != MI_SEASPAR) ReqMode = CCam::MODE_PLAYER_FALLEN_WATER; } } // Ped target else if(pTargetEntity->IsPed()){ // Change user selected mode if(CPad::GetPad(0)->CycleCameraModeUpJustDown() && !CReplay::IsPlayingBack() && (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && !m_WideScreenOn && !m_bFailedCullZoneTestPreviously && !m_bFirstPersonBeingUsed){ if(FrontEndMenuManager.m_ControlMethod == CONTROL_STANDARD){ if(PedZoomIndicator == CAM_ZOOM_3) PedZoomIndicator = CAM_ZOOM_1; else PedZoomIndicator = CAM_ZOOM_3; }else PedZoomIndicator--; } if(CPad::GetPad(0)->CycleCameraModeDownJustDown() && !CReplay::IsPlayingBack() && (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && !m_WideScreenOn && !m_bFailedCullZoneTestPreviously && !m_bFirstPersonBeingUsed){ if(FrontEndMenuManager.m_ControlMethod == CONTROL_STANDARD){ if(PedZoomIndicator == CAM_ZOOM_3) PedZoomIndicator = CAM_ZOOM_1; else PedZoomIndicator = CAM_ZOOM_3; }else PedZoomIndicator++; } // disabled top down and obbe's cam here if(PedZoomIndicator < CAM_ZOOM_1) PedZoomIndicator = CAM_ZOOM_3; else if(PedZoomIndicator > CAM_ZOOM_3) PedZoomIndicator = CAM_ZOOM_1; ReqMode = CCam::MODE_FOLLOWPED; // Check 1st person mode if((m_bLookingAtPlayer || m_bEnable1rstPersonCamCntrlsScript) && pTargetEntity->IsPed() && (!m_WideScreenOn || m_bEnable1rstPersonCamCntrlsScript) && !Cams[0].Using3rdPersonMouseCam() #ifdef FREE_CAM && (!CCamera::bFreeCam || m_bEnable1rstPersonCamCntrlsScript) #endif ){ // See if we want to enter first person mode if(CPad::GetPad(0)->LookAroundLeftRight() || CPad::GetPad(0)->LookAroundUpDown()){ m_uiFirstPersonCamLastInputTime = CTimer::GetTimeInMilliseconds(); m_bFirstPersonBeingUsed = true; } if(m_bFirstPersonBeingUsed){ // Or if we want to go back to 3rd person if(CPad::GetPad(0)->GetPedWalkLeftRight() || CPad::GetPad(0)->GetPedWalkUpDown() || CPad::GetPad(0)->GetSquare() || CPad::GetPad(0)->GetTriangle() || CPad::GetPad(0)->GetCross() || CPad::GetPad(0)->GetCircle() || CTimer::GetTimeInMilliseconds() - m_uiFirstPersonCamLastInputTime > 2850.0f){ m_bFirstPersonBeingUsed = false; }else if(CPad::GetPad(0)->TargetJustDown()){ m_bFirstPersonBeingUsed = false; m_bJustJumpedOutOf1stPersonBecauseOfTarget = true; } } }else m_bFirstPersonBeingUsed = false; if(!FindPlayerPed()->IsPedInControl() || FindPlayerPed()->m_fMoveSpeed > 0.0f) m_bFirstPersonBeingUsed = false; if(m_bFirstPersonBeingUsed){ ReqMode = CCam::MODE_1STPERSON; CPad::GetPad(0)->DisablePlayerControls |= PLAYERCONTROL_CAMERA; } // Zoom value if(PedZoomIndicator == CAM_ZOOM_1) m_fPedZoomValue = 0.25f; else if(PedZoomIndicator == CAM_ZOOM_2) m_fPedZoomValue = 1.5f; else if(PedZoomIndicator == CAM_ZOOM_3) m_fPedZoomValue = 2.9f; // Smooth zoom value - ugly code if(m_bUseScriptZoomValuePed){ if(m_fPedZoomValueSmooth < m_fPedZoomValueScript){ m_fPedZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); m_fPedZoomValueSmooth = Min(m_fPedZoomValueSmooth, m_fPedZoomValueScript); }else{ m_fPedZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); m_fPedZoomValueSmooth = Max(m_fPedZoomValueSmooth, m_fPedZoomValueScript); } }else if(m_bFailedCullZoneTestPreviously){ static float PedZoomedInVal = 0.5f; CloseInPedHeightTarget = 0.7f; if(m_fPedZoomValueSmooth < PedZoomedInVal){ m_fPedZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); m_fPedZoomValueSmooth = Min(m_fPedZoomValueSmooth, PedZoomedInVal); }else{ m_fPedZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); m_fPedZoomValueSmooth = Max(m_fPedZoomValueSmooth, PedZoomedInVal); } }else{ if(m_fPedZoomValueSmooth < m_fPedZoomValue){ m_fPedZoomValueSmooth += 0.12f * CTimer::GetTimeStep(); m_fPedZoomValueSmooth = Min(m_fPedZoomValueSmooth, m_fPedZoomValue); }else{ m_fPedZoomValueSmooth -= 0.12f * CTimer::GetTimeStep(); m_fPedZoomValueSmooth = Max(m_fPedZoomValueSmooth, m_fPedZoomValue); } if(PedZoomIndicator == CAM_ZOOM_3 && m_fPedZoomValue == 0.0f) m_fPedZoomValueSmooth = m_fPedZoomValue; } WellBufferMe(CloseInPedHeightTarget, &Cams[ActiveCam].m_fCloseInPedHeightOffset, &Cams[ActiveCam].m_fCloseInPedHeightOffsetSpeed, 0.1f, 0.025f, false); // Check if entering fight cam if(!m_bFirstPersonBeingUsed){ if(FindPlayerPed()->GetPedState() == PED_FIGHT && !m_bUseMouse3rdPerson) ReqMode = CCam::MODE_FIGHT_CAM; if(((CPed*)pTargetEntity)->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT && FindPlayerPed()->GetPedState() == PED_ATTACK && !m_bUseMouse3rdPerson) ReqMode = CCam::MODE_FIGHT_CAM; } // Garage cam CAttributeZone *stairsZone = nil; if(CCullZones::CamStairsForPlayer()){ stairsZone = CCullZones::FindZoneWithStairsAttributeForPlayer(); if(stairsZone) stairs = true; } if(CGarages::IsPointInAGarageCameraZone(pTargetEntity->GetPosition()) && !m_bUseMouse3rdPerson || stairs){ if(!m_bGarageFixedCamPositionSet && m_bLookingAtPlayer){ if(pToGarageWeAreIn || stairs){ float ground; bool foundGround; if(pToGarageWeAreIn){ // targetPos = pTargetEntity->GetPosition(); // unused if(pToGarageWeAreIn->m_pDoor1){ whichDoor = 1; garageDoorPos1.x = pToGarageWeAreIn->m_fDoor1X; garageDoorPos1.y = pToGarageWeAreIn->m_fDoor1Y; garageDoorPos1.z = 0.0f; // targetPos.z = 0.0f; // unused // (targetPos - doorPos1).Magnitude(); // unused }else if(pToGarageWeAreIn->m_pDoor2){ whichDoor = 2; #ifdef FIX_BUGS garageDoorPos2.x = pToGarageWeAreIn->m_fDoor2X; garageDoorPos2.y = pToGarageWeAreIn->m_fDoor2Y; garageDoorPos2.z = 0.0f; #endif }else{ whichDoor = 1; garageDoorPos1.x = pTargetEntity->GetPosition().x; garageDoorPos1.y = pTargetEntity->GetPosition().y; #ifdef FIX_BUGS garageDoorPos1.z = 0.0f; #else garageDoorPos2.z = 0.0f; #endif } }else{ whichDoor = 1; garageDoorPos1 = Cams[ActiveCam].Source; if(stairsZone){ // always true garageCenter = CVector((stairsZone->minx+stairsZone->maxx)/2, (stairsZone->miny+stairsZone->maxy)/2, 0.0f); if(pTargetEntity->GetPosition().x > 376.0f && pTargetEntity->GetPosition().x < 383.0f && pTargetEntity->GetPosition().y > -496.0f && pTargetEntity->GetPosition().y < -489.0f && pTargetEntity->GetPosition().z > 11.6f && pTargetEntity->GetPosition().z < 13.6f){ garageDoorPos1 = CVector(382.6f, -489.6f, 13.1f); }else{ bool bClearViewOutside = true; CVector dirOutside = pTargetEntity->GetPosition() - garageCenter; dirOutside.z = 0.0f; dirOutside.Normalise(); float zoneDim = stairsZone->maxx - stairsZone->minx; if(zoneDim < stairsZone->maxy - stairsZone->miny) zoneDim = stairsZone->maxy - stairsZone->miny; zoneDim *= 2.0f; CVector posOutside = pTargetEntity->GetPosition() + zoneDim*dirOutside; if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), posOutside, true, false, false, false, false, false, true)){ posOutside = pTargetEntity->GetPosition() - zoneDim*dirOutside; if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), posOutside, true, false, false, false, false, false, true)) bClearViewOutside = false; } if(bClearViewOutside) garageDoorPos1 = posOutside; } } } if(pToGarageWeAreIn){ garageCenter.x = pToGarageWeAreIn->GetGarageCenterX(); garageCenter.y = pToGarageWeAreIn->GetGarageCenterY(); garageCenter.z = 0.0f; }else{ garageDoorPos1.z = 0.0f; if(!stairs) // how can this be true? garageCenter = CVector(pTargetEntity->GetPosition().x, pTargetEntity->GetPosition().y, 0.0f); } if(whichDoor == 1) garageCenterToDoor = garageDoorPos1 - garageCenter; else garageCenterToDoor = garageDoorPos2 - garageCenter; targetPos = pTargetEntity->GetPosition(); ground = CWorld::FindGroundZFor3DCoord(targetPos.x, targetPos.y, targetPos.z, &foundGround); if(!foundGround) ground = targetPos.z - 0.2f; garageCenterToDoor.z = 0.0f; garageCenterToDoor.Normalise(); if(whichDoor == 1){ if(pToGarageWeAreIn == nil && stairs){ if(stairsZone){ float zoneDim = stairsZone->maxx - stairsZone->minx; if(zoneDim < stairsZone->maxy - stairsZone->miny) zoneDim = stairsZone->maxy - stairsZone->miny; garageCamPos = garageCenter + (0.7f*zoneDim + 3.75f)*garageCenterToDoor; }else // how can this be true? garageCamPos = garageDoorPos1 + 3.75f*garageCenterToDoor; }else garageCamPos = garageDoorPos1 + 13.0f*garageCenterToDoor; }else{ garageCamPos = garageDoorPos2 + 13.0f*garageCenterToDoor; } if(PedZoomIndicator == CAM_ZOOM_TOPDOWN && !stairs){ garageCamPos = garageCenter; garageCamPos.z += FindPlayerPed()->GetPosition().z + 2.1f; if(pToGarageWeAreIn && garageCamPos.z > pToGarageWeAreIn->m_fSupX) // What? garageCamPos.z = pToGarageWeAreIn->m_fSupX; }else garageCamPos.z = ground + 3.1f; SetCamPositionForFixedMode(garageCamPos, CVector(0.0f, 0.0f, 0.0f)); m_bGarageFixedCamPositionSet = true; } } if((CGarages::CameraShouldBeOutside() || stairs) && m_bLookingAtPlayer && m_bGarageFixedCamPositionSet){ if(pToGarageWeAreIn || stairs){ ReqMode = CCam::MODE_FIXED; m_bPlayerIsInGarage = true; } }else{ if(m_bPlayerIsInGarage){ m_bJustCameOutOfGarage = true; m_bPlayerIsInGarage = false; } ReqMode = CCam::MODE_FOLLOWPED; } }else{ if(m_bPlayerIsInGarage){ m_bJustCameOutOfGarage = true; m_bPlayerIsInGarage = false; } m_bGarageFixedCamPositionSet = false; } // Lighthouse if(!m_bFirstPersonBeingUsed && (pTargetEntity->GetPosition() - CVector(474.3f, -1717.6f, 0.0f)).Magnitude2D() < 6.0f) if((pTargetEntity->GetPosition() - CVector(474.3f, -1717.6f, 0.0f)).Magnitude2D() < 3.8f || pTargetEntity->GetPosition().z > 50.0f) if(!Cams[ActiveCam].Using3rdPersonMouseCam()) ReqMode = CCam::MODE_LIGHTHOUSE; // Fallen into water if(Cams[ActiveCam].IsTargetInWater(Cams[ActiveCam].Source) && Cams[ActiveCam].CamTargetEntity->IsPed()) ReqMode = CCam::MODE_PLAYER_FALLEN_WATER; // Set top down if(PedZoomIndicator == CAM_ZOOM_TOPDOWN && !CCullZones::Cam1stPersonForPlayer() && !CCullZones::CamNoRain() && !CCullZones::PlayerNoRain() && !m_bFirstPersonBeingUsed && !m_bPlayerIsInGarage) ReqMode = CCam::MODE_TOP_DOWN_PED; // Weapon mode if(!CPad::GetPad(0)->GetTarget() && PlayerWeaponMode.Mode != CCam::MODE_HELICANNON_1STPERSON) ClearPlayerWeaponMode(); if(m_PlayerMode.Mode != CCam::MODE_NONE) ReqMode = m_PlayerMode.Mode; if(PlayerWeaponMode.Mode != CCam::MODE_NONE && !stairs){ if(PlayerWeaponMode.Mode == CCam::MODE_SNIPER || PlayerWeaponMode.Mode == CCam::MODE_ROCKETLAUNCHER || // game also checks MODE_MODELVIEW here but that does make any sense... PlayerWeaponMode.Mode == CCam::MODE_M16_1STPERSON || PlayerWeaponMode.Mode == CCam::MODE_HELICANNON_1STPERSON || PlayerWeaponMode.Mode == CCam::MODE_CAMERA || Cams[ActiveCam].GetWeaponFirstPersonOn()){ // First person weapon mode if(PLAYER->GetPedState() == PED_SEEK_CAR){ if(ReqMode == CCam::MODE_TOP_DOWN_PED || Cams[ActiveCam].GetWeaponFirstPersonOn()) ReqMode = PlayerWeaponMode.Mode; else ReqMode = CCam::MODE_FOLLOWPED; }else ReqMode = PlayerWeaponMode.Mode; }else if(ReqMode != CCam::MODE_TOP_DOWN_PED && PedZoomIndicator != CAM_ZOOM_3){ // Syphon mode float playerTargetDist; float deadPedDist = 4.0f; static float alivePedDist = 2.0f; // original name lost float pedDist; // actually only used on dead target bool targetDead = false; float camAngle, targetAngle; CVector playerToTarget = m_cvecAimingTargetCoors - pTargetEntity->GetPosition(); CVector playerToCam = Cams[ActiveCam].Source - pTargetEntity->GetPosition(); if(PedZoomIndicator == CAM_ZOOM_1) deadPedDist = 2.25f; if(FindPlayerPed()->m_pPointGunAt){ // BUG: this need not be a ped! if(((CPed*)FindPlayerPed()->m_pPointGunAt)->DyingOrDead()){ targetDead = true; pedDist = deadPedDist; }else pedDist = alivePedDist; playerTargetDist = playerToTarget.Magnitude2D(); camAngle = CGeneral::GetATanOfXY(playerToCam.x, playerToCam.y); targetAngle = CGeneral::GetATanOfXY(playerToTarget.x, playerToTarget.y); ReqMode = PlayerWeaponMode.Mode; // Check whether to start aiming in crim-in-front mode if(Cams[ActiveCam].Mode != CCam::MODE_SYPHON){ float angleDiff = camAngle - targetAngle; while(angleDiff >= PI) angleDiff -= 2*PI; while(angleDiff < -PI) angleDiff += 2*PI; if(Abs(angleDiff) < HALFPI && playerTargetDist < 3.5f && playerToTarget.z > -1.0f) ReqMode = CCam::MODE_SYPHON_CRIM_IN_FRONT; } // Check whether to go to special fixed mode float fixedModeDist = 0.0f; if((ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT || ReqMode == CCam::MODE_SYPHON) && (m_uiTransitionState == 0 || Cams[ActiveCam].Mode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON) && playerTargetDist < pedDist && targetDead){ if(ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT) fixedModeDist = 5.0f; else fixedModeDist = 5.6f; ReqMode = CCam::MODE_SPECIAL_FIXED_FOR_SYPHON; } if(ReqMode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON){ if(!PlaceForFixedWhenSniperFound){ // Find position CEntity *entity; CColPoint colPoint; CVector fixedPos = pTargetEntity->GetPosition(); fixedPos.x += fixedModeDist*Cos(camAngle); fixedPos.y += fixedModeDist*Sin(camAngle); fixedPos.z += 1.15f; if(CWorld::ProcessLineOfSight(pTargetEntity->GetPosition(), fixedPos, colPoint, entity, true, false, false, true, false, true, true)) SetCamPositionForFixedMode(colPoint.point, CVector(0.0f, 0.0f, 0.0f)); else SetCamPositionForFixedMode(fixedPos, CVector(0.0f, 0.0f, 0.0f)); PlaceForFixedWhenSniperFound = true; } }else PlaceForFixedWhenSniperFound = false; } } } } } if(DebugCamMode) ReqMode = DebugCamMode; // Process arrested player static int ThePickedArrestMode; static int LastPedState; bool startArrestCam = false; static bool beingArrested = false; bool stopArrestCam = false; if(PLAYER->GetPedState() == PED_ARRESTED) beingArrested = true; else if(beingArrested){ stopArrestCam = true; beingArrested = false; } if(LastPedState != PED_ARRESTED && PLAYER->GetPedState() == PED_ARRESTED){ if(CarZoomIndicator != CAM_ZOOM_1STPRS || !pTargetEntity->IsVehicle()) startArrestCam = true; }else startArrestCam = false; LastPedState = PLAYER->GetPedState(); if(startArrestCam){ ThePickedArrestMode = CCam::MODE_ARRESTCAM_ONE; ReqMode = CCam::MODE_ARRESTCAM_ONE; Cams[ActiveCam].ResetStatics = true; }else if(PLAYER->GetPedState() == PED_ARRESTED) ReqMode = ThePickedArrestMode; // Process dead player if(PLAYER->GetPedState() == PED_DEAD){ if(Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY) ReqMode = CCam::MODE_PED_DEAD_BABY; else{ bool useArrestCam = false; if(pTargetEntity->IsPed()){ for(int i = 0; i < ((CPed*)pTargetEntity)->m_numNearPeds; i++){ CPed *ped = ((CPed*)pTargetEntity)->m_nearPeds[i]; if(ped && ped->GetPedState() == PED_ARREST_PLAYER) if((ped->GetPosition() - pTargetEntity->GetPosition()).Magnitude() < 4.0f){ ReqMode = CCam::MODE_ARRESTCAM_ONE; Cams[ActiveCam].ResetStatics = true; useArrestCam = true; break; } } } if(!useArrestCam){ ReqMode = CCam::MODE_PED_DEAD_BABY; Cams[ActiveCam].ResetStatics = true; } } } // Restore with a jump cut if(m_bRestoreByJumpCut){ if(ReqMode != CCam::MODE_FOLLOWPED && ReqMode != CCam::MODE_BEHINDCAR && ReqMode != CCam::MODE_CAM_ON_A_STRING && ReqMode != CCam::MODE_M16_1STPERSON && ReqMode != CCam::MODE_SYPHON && ReqMode != CCam::MODE_SYPHON_CRIM_IN_FRONT && ReqMode != CCam::MODE_SPECIAL_FIXED_FOR_SYPHON && ReqMode != CCam::MODE_SNIPER && ReqMode != CCam::MODE_ROCKETLAUNCHER && ReqMode != CCam::MODE_CAMERA && !m_bUseMouse3rdPerson) SetCameraDirectlyBehindForFollowPed_CamOnAString(); ReqMode = m_iModeToGoTo; Cams[ActiveCam].Mode = ReqMode; m_bJust_Switched = true; Cams[ActiveCam].ResetStatics = true; Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; Cams[ActiveCam].CamTargetEntity = pTargetEntity; Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; Cams[ActiveCam].m_bCamLookingAtVector = false; Cams[ActiveCam].m_vecLastAboveWaterCamPosition = Cams[(ActiveCam+1)%2].m_vecLastAboveWaterCamPosition; m_bRestoreByJumpCut = false; Cams[ActiveCam].ResetStatics = true; pTargetEntity->RegisterReference(&pTargetEntity); Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); CarZoomValueSmooth = CarZoomValue; m_fPedZoomValueSmooth = m_fPedZoomValue; m_uiTransitionState = 0; m_vecDoingSpecialInterPolation = false; } if(gbModelViewer) ReqMode = CCam::MODE_MODELVIEW; // Turn on Obbe's cam bool canUseObbeCam = true; if(pTargetEntity){ if(pTargetEntity->IsVehicle()){ if(CarZoomIndicator == CAM_ZOOM_CINEMATIC) m_bObbeCinematicCarCamOn = true; }else{ if(PedZoomIndicator == CAM_ZOOM_CINEMATIC) m_bObbeCinematicPedCamOn = true; } } if(m_bTargetJustBeenOnTrain || ReqMode == CCam::MODE_SYPHON || ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT || ReqMode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON || ReqMode == CCam::MODE_PED_DEAD_BABY || ReqMode == CCam::MODE_ARRESTCAM_ONE || ReqMode == CCam::MODE_ARRESTCAM_TWO || ReqMode == CCam::MODE_FIGHT_CAM || ReqMode == CCam::MODE_PLAYER_FALLEN_WATER || ReqMode == CCam::MODE_SNIPER || ReqMode == CCam::MODE_ROCKETLAUNCHER || ReqMode == CCam::MODE_M16_1STPERSON || ReqMode == CCam::MODE_SNIPER_RUNABOUT || ReqMode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || ReqMode == CCam::MODE_1STPERSON_RUNABOUT || ReqMode == CCam::MODE_M16_1STPERSON_RUNABOUT || ReqMode == CCam::MODE_FIGHT_CAM_RUNABOUT || ReqMode == CCam::MODE_HELICANNON_1STPERSON || ReqMode == CCam::MODE_CAMERA || WhoIsInControlOfTheCamera == CAMCONTROL_SCRIPT || m_bJustCameOutOfGarage || m_bPlayerIsInGarage) canUseObbeCam = false; if(m_bObbeCinematicPedCamOn && canUseObbeCam) ProcessObbeCinemaCameraPed(); else if(m_bObbeCinematicCarCamOn && canUseObbeCam){ if(pTargetEntity->IsVehicle() && ((CVehicle*)pTargetEntity)->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || ((CVehicle*)pTargetEntity)->IsBoat()) ProcessObbeCinemaCameraHeli(); else ProcessObbeCinemaCameraCar(); }else{ if(m_bPlayerIsInGarage && m_bObbeCinematicCarCamOn) switchByJumpCut = true; canUseObbeCam = false; DontProcessObbeCinemaCamera(); } // Start the transition or do a jump cut if(m_bLookingAtPlayer){ // Going into top down modes normally needs a jump cut (but see below) if(ReqMode == CCam::MODE_TOPDOWN || ReqMode == CCam::MODE_1STPERSON || ReqMode == CCam::MODE_TOP_DOWN_PED){ switchByJumpCut = true; } // Going from top down to vehicle else if(ReqMode == CCam::MODE_CAM_ON_A_STRING || ReqMode == CCam::MODE_BEHINDBOAT){ if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED) switchByJumpCut = true; }else if(ReqMode == CCam::MODE_FIXED){ if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN) switchByJumpCut = true; } // Going into Syphon mode if(ReqMode == CCam::MODE_SYPHON || ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT) switchByJumpCut = true; // Top down modes can interpolate between each other if(ReqMode == CCam::MODE_TOPDOWN){ if(Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY) switchByJumpCut = false; }else if(ReqMode == CCam::MODE_TOP_DOWN_PED){ if(Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY) switchByJumpCut = false; } if(ReqMode == CCam::MODE_1STPERSON || ReqMode == CCam::MODE_M16_1STPERSON || ReqMode == CCam::MODE_SNIPER || ReqMode == CCam::MODE_ROCKETLAUNCHER || ReqMode == CCam::MODE_SNIPER_RUNABOUT || ReqMode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || ReqMode == CCam::MODE_1STPERSON_RUNABOUT || ReqMode == CCam::MODE_M16_1STPERSON_RUNABOUT || ReqMode == CCam::MODE_FIGHT_CAM_RUNABOUT || ReqMode == CCam::MODE_HELICANNON_1STPERSON || ReqMode == CCam::MODE_CAMERA || ReqMode == CCam::MODE_ARRESTCAM_ONE || ReqMode == CCam::MODE_ARRESTCAM_TWO){ // Going into any 1st person mode is a jump cut if(pTargetEntity->IsPed()) switchByJumpCut = true; }else if(ReqMode == CCam::MODE_FIXED && m_bPlayerIsInGarage){ // Going from 1st peron mode into garage if(Cams[ActiveCam].Mode == CCam::MODE_SNIPER || Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || stairs || Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_CAMERA){ if(pTargetEntity && pTargetEntity->IsVehicle()) switchByJumpCut = true; } }else if(ReqMode == CCam::MODE_FOLLOWPED){ bool syphonJumpCut = false; if(Cams[ActiveCam].Mode == CCam::MODE_SYPHON || Cams[ActiveCam].Mode == CCam::MODE_SYPHON_CRIM_IN_FRONT) if(!((CPed*)pTargetEntity)->CanWeRunAndFireWithWeapon()) syphonJumpCut = true; if(Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_SNIPER || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || Cams[ActiveCam].Mode == CCam::MODE_ARRESTCAM_ONE || Cams[ActiveCam].Mode == CCam::MODE_ARRESTCAM_TWO || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY || Cams[ActiveCam].Mode == CCam::MODE_PILLOWS_PAPS || Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_CAMERA || Cams[ActiveCam].Mode == CCam::MODE_TOPDOWN || Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || syphonJumpCut || stopArrestCam){ if(!m_bJustCameOutOfGarage){ if(Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_SNIPER || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_CAMERA){ float angle = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y) - HALFPI; ((CPed*)pTargetEntity)->m_fRotationCur = angle; ((CPed*)pTargetEntity)->m_fRotationDest = angle; } m_bUseTransitionBeta = true; switchByJumpCut = true; if(Cams[ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ CVector front = Cams[ActiveCam].Source - FindPlayerPed()->GetPosition(); front.z = 0.0f; front.Normalise(); #ifdef FIX_BUGS // this is almost as bad as the bugged code if(front.x == 0.001f && front.y == 0.001f) front.y = 1.0f; #else // someone used = instead of == in the above check by accident front.x = 0.001f; front.y = 1.0f; #endif Cams[ActiveCam].m_fTransitionBeta = CGeneral::GetATanOfXY(front.x, front.y); }else Cams[ActiveCam].m_fTransitionBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y) + PI; } } }else if(ReqMode == CCam::MODE_FIGHT_CAM){ if(Cams[ActiveCam].Mode == CCam::MODE_1STPERSON) switchByJumpCut = true; }else if(ReqMode == CCam::MODE_LIGHTHOUSE || ReqMode == CCam::MODE_ARRESTCAM_ONE || ReqMode == CCam::MODE_ARRESTCAM_TWO || ReqMode == CCam::MODE_PED_DEAD_BABY) switchByJumpCut = true; else if(Cams[ActiveCam].Mode == CCam::MODE_PED_DEAD_BABY && ReqMode != CCam::MODE_PED_DEAD_BABY) switchByJumpCut = true; if(ReqMode != Cams[ActiveCam].Mode && Cams[ActiveCam].CamTargetEntity == nil) switchByJumpCut = true; if(m_bPlayerIsInGarage && pToGarageWeAreIn){ if(pToGarageWeAreIn->m_eGarageType == GARAGE_BOMBSHOP1 || pToGarageWeAreIn->m_eGarageType == GARAGE_BOMBSHOP2 || pToGarageWeAreIn->m_eGarageType == GARAGE_BOMBSHOP3){ if(pTargetEntity->IsVehicle() && pTargetEntity->GetModelIndex() == MI_MRWHOOP && ReqMode != Cams[ActiveCam].Mode) switchByJumpCut = true; } } #ifdef GTA_SCENE_EDIT if(CSceneEdit::m_bEditOn) ReqMode = CCam::MODE_EDITOR; #endif if((m_uiTransitionState == 0 || switchByJumpCut) && ReqMode != Cams[ActiveCam].Mode){ if(switchByJumpCut){ if(!m_bPlayerIsInGarage || m_bJustCameOutOfGarage){ if(ReqMode != CCam::MODE_FOLLOWPED && ReqMode != CCam::MODE_M16_1STPERSON && ReqMode != CCam::MODE_SNIPER && ReqMode != CCam::MODE_ROCKETLAUNCHER && !m_bUseMouse3rdPerson) SetCameraDirectlyBehindForFollowPed_CamOnAString(); } Cams[ActiveCam].Mode = ReqMode; m_bJust_Switched = true; Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; Cams[ActiveCam].CamTargetEntity = pTargetEntity; Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; Cams[ActiveCam].m_bCamLookingAtVector = m_bLookingAtVector; Cams[ActiveCam].m_vecLastAboveWaterCamPosition = Cams[(ActiveCam+1)%2].m_vecLastAboveWaterCamPosition; CarZoomValueSmooth = CarZoomValue; m_fPedZoomValueSmooth = m_fPedZoomValue; m_uiTransitionState = 0; m_vecDoingSpecialInterPolation = false; m_bStartInterScript = false; Cams[ActiveCam].ResetStatics = true; pTargetEntity->RegisterReference(&pTargetEntity); Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); }else if(!m_bWaitForInterpolToFinish){ StartTransition(ReqMode); pTargetEntity->RegisterReference(&pTargetEntity); Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); } }else if(m_uiTransitionState != 0 && ReqMode != Cams[ActiveCam].Mode){ bool startTransition = true; if(ReqMode == CCam::MODE_FIGHT_CAM || Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM) startTransition = false; if(ReqMode == CCam::MODE_FOLLOWPED && Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM) startTransition = false; if(!m_bWaitForInterpolToFinish && m_bLookingAtPlayer && m_uiTransitionState != 0){ CVector playerDist; playerDist.x = FindPlayerPed()->GetPosition().x - GetPosition().x; playerDist.y = FindPlayerPed()->GetPosition().y - GetPosition().y; playerDist.z = FindPlayerPed()->GetPosition().z - GetPosition().z; // if player is too far away, keep interpolating and don't transition if(pTargetEntity && pTargetEntity->IsPed()){ if(playerDist.Magnitude() > 17.5f && (ReqMode == CCam::MODE_SYPHON || ReqMode == CCam::MODE_SYPHON_CRIM_IN_FRONT)) m_bWaitForInterpolToFinish = true; } } if(m_bWaitForInterpolToFinish) startTransition = false; if(startTransition){ StartTransitionWhenNotFinishedInter(ReqMode); pTargetEntity->RegisterReference(&pTargetEntity); Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); } }else if(ReqMode == CCam::MODE_FIXED && pTargetEntity != Cams[ActiveCam].CamTargetEntity && m_bPlayerIsInGarage){ if(m_uiTransitionState != 0) StartTransitionWhenNotFinishedInter(ReqMode); else StartTransition(ReqMode); pTargetEntity->RegisterReference(&pTargetEntity); Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); } }else{ // not following player bool useWeaponMode = false; bool jumpCutTo1stPrs = false; if(m_bEnable1rstPersonCamCntrlsScript || m_bAllow1rstPersonWeaponsCamera){ if(ReqMode == CCam::MODE_1STPERSON){ if(Cams[ActiveCam].Mode != ReqMode) jumpCutTo1stPrs = true; }else if((PlayerWeaponMode.Mode == CCam::MODE_SNIPER || PlayerWeaponMode.Mode == CCam::MODE_1STPERSON || PlayerWeaponMode.Mode == CCam::MODE_ROCKETLAUNCHER) && CPad::GetPad(0)->GetTarget() && m_bAllow1rstPersonWeaponsCamera){ useWeaponMode = true; jumpCutTo1stPrs = true; }else if(Cams[ActiveCam].Mode != m_iModeToGoTo){ m_bStartInterScript = true; m_iTypeOfSwitch = JUMP_CUT; CPad::GetPad(0)->DisablePlayerControls &= ~PLAYERCONTROL_CAMERA; } } if(m_uiTransitionState == 0 && m_bStartInterScript && m_iTypeOfSwitch == INTERPOLATION){ ReqMode = m_iModeToGoTo; StartTransition(ReqMode); pTargetEntity->RegisterReference(&pTargetEntity); Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); }else if(m_uiTransitionState != 0 && m_bStartInterScript && m_iTypeOfSwitch == INTERPOLATION){ ReqMode = m_iModeToGoTo; StartTransitionWhenNotFinishedInter(ReqMode); pTargetEntity->RegisterReference(&pTargetEntity); Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); }else if(m_bStartInterScript && m_iTypeOfSwitch == JUMP_CUT || jumpCutTo1stPrs){ m_uiTransitionState = 0; m_vecDoingSpecialInterPolation = false; if(m_bEnable1rstPersonCamCntrlsScript && ReqMode == CCam::MODE_1STPERSON) Cams[ActiveCam].Mode = ReqMode; else if(useWeaponMode) Cams[ActiveCam].Mode = PlayerWeaponMode.Mode; else Cams[ActiveCam].Mode = m_iModeToGoTo; m_bJust_Switched = true; Cams[ActiveCam].ResetStatics = true; Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; Cams[ActiveCam].CamTargetEntity = pTargetEntity; Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; Cams[ActiveCam].m_bCamLookingAtVector = m_bLookingAtVector; Cams[ActiveCam].m_vecLastAboveWaterCamPosition = Cams[(ActiveCam+1)%2].m_vecLastAboveWaterCamPosition; m_bJust_Switched = true; pTargetEntity->RegisterReference(&pTargetEntity); Cams[ActiveCam].CamTargetEntity->RegisterReference(&Cams[ActiveCam].CamTargetEntity); CarZoomValueSmooth = CarZoomValue; m_fPedZoomValueSmooth = m_fPedZoomValue; } } m_bStartInterScript = false; if(Cams[ActiveCam].CamTargetEntity == nil) Cams[ActiveCam].CamTargetEntity = pTargetEntity; // Ped visibility if((Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_SNIPER || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_CAMERA) && pTargetEntity->IsPed() || Cams[ActiveCam].Mode == CCam::MODE_FLYBY) FindPlayerPed()->bIsVisible = false; else FindPlayerPed()->bIsVisible = true; bool switchedFromObbe = false; if(!canUseObbeCam && WhoIsInControlOfTheCamera == CAMCONTROL_OBBE){ RestoreWithJumpCut(); switchedFromObbe = true; SetCameraDirectlyBehindForFollowPed_CamOnAString(); } if(PrevMode != Cams[ActiveCam].Mode || switchedFromObbe || Cams[ActiveCam].Mode == CCam::MODE_FOLLOWPED || Cams[ActiveCam].Mode == CCam::MODE_CAM_ON_A_STRING) if(CPad::GetPad(0)->CycleCameraModeJustDown() && !CReplay::IsPlayingBack() && (m_bLookingAtPlayer || WhoIsInControlOfTheCamera == CAMCONTROL_OBBE) && !m_WideScreenOn && (WhoIsInControlOfTheCamera != CAMCONTROL_OBBE || bSwitchedToObbeCam)) DMAudio.PlayFrontEndSound(SOUND_HUD, 0); } // What a mess! void CCamera::UpdateTargetEntity(void) { bool enteringCar = false; bool obbeCam = false; m_bPlayerWasOnBike = false; if(pTargetEntity && pTargetEntity->IsVehicle() && ((CVehicle*)pTargetEntity)->IsBike()) m_bPlayerWasOnBike = true; if(WhoIsInControlOfTheCamera == CAMCONTROL_OBBE){ obbeCam = true; if(m_iModeObbeCamIsInForCar == OBBE_COPCAR_WHEEL || m_iModeObbeCamIsInForCar == OBBE_COPCAR){ if(FindPlayerPed()->GetPedState() != PED_ARRESTED) obbeCam = false; if(FindPlayerVehicle() == nil) pTargetEntity = FindPlayerPed(); } } if((m_bLookingAtPlayer || obbeCam) && m_uiTransitionState == 0 || pTargetEntity == nil || m_bTargetJustBeenOnTrain){ if(FindPlayerVehicle()) pTargetEntity = FindPlayerVehicle(); else{ pTargetEntity = FindPlayerPed(); // this keeps the camera on the player while entering cars if(PLAYER->GetPedState() == PED_ENTER_CAR || PLAYER->GetPedState() == PED_CARJACK || PLAYER->GetPedState() == PED_OPEN_DOOR) enteringCar = true; if(!enteringCar) if(Cams[ActiveCam].CamTargetEntity != pTargetEntity) Cams[ActiveCam].CamTargetEntity = pTargetEntity; } bool cantOpen = true; if(PLAYER){ if(PLAYER->m_pMyVehicle){ if(FindPlayerPed()->m_pMyVehicle->CanPedOpenLocks(PLAYER)) cantOpen = false; }else if(FindPlayerPed()->m_carInObjective && (FindPlayerPed()->GetPedState() == PED_ENTER_CAR || FindPlayerPed()->GetPedState() == PED_CARJACK || FindPlayerPed()->GetPedState() == PED_OPEN_DOOR)){ if(FindPlayerPed()->m_carInObjective->CanPedOpenLocks(FindPlayerPed())) cantOpen = false; } } if(PLAYER->GetPedState() == PED_ENTER_CAR && !cantOpen){ if(!enteringCar && CarZoomIndicator != CAM_ZOOM_1STPRS){ pTargetEntity = PLAYER->m_pMyVehicle; if(PLAYER->m_pMyVehicle == nil) pTargetEntity = PLAYER; } } if((PLAYER->GetPedState() == PED_CARJACK || PLAYER->GetPedState() == PED_OPEN_DOOR) && !cantOpen){ if(!enteringCar && CarZoomIndicator != CAM_ZOOM_1STPRS) pTargetEntity = PLAYER->m_pMyVehicle; if(PLAYER->m_pMyVehicle == nil) pTargetEntity = PLAYER; } if(PLAYER->GetPedState() == PED_EXIT_CAR) pTargetEntity = FindPlayerPed(); if(PLAYER->GetPedState() == PED_DRAG_FROM_CAR) pTargetEntity = FindPlayerPed(); if(pTargetEntity->IsVehicle() && CarZoomIndicator == CAM_ZOOM_1STPRS && FindPlayerPed()->GetPedState() == PED_ARRESTED) pTargetEntity = FindPlayerPed(); } } const float SOUND_DIST = 20.0f; void CCamera::UpdateSoundDistances(void) { CVector center, end; CEntity *entity; CColPoint colPoint; float f; int n; if((Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_SNIPER || Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_CAMERA || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER) && pTargetEntity->IsPed()) center = GetPosition() + 0.5f*GetForward(); else center = GetPosition() + 5.0f*GetForward(); // check up n = CTimer::GetFrameCounter() % 12; if(n == 0){ SoundDistUpAsReadOld = SoundDistUpAsRead; if(CWorld::ProcessVerticalLine(center, center.z+SOUND_DIST, colPoint, entity, true, false, false, false, true, false, nil)) SoundDistUpAsRead = colPoint.point.z - center.z; else SoundDistUpAsRead = SOUND_DIST; } f = (n + 1) / 6.0f; SoundDistUp = (1.0f-f)*SoundDistUpAsReadOld + f*SoundDistUpAsRead; } void CCamera::InitialiseCameraForDebugMode(void) { if(FindPlayerVehicle()) Cams[2].Source = FindPlayerVehicle()->GetPosition(); else if(FindPlayerPed()) Cams[2].Source = FindPlayerPed()->GetPosition(); Cams[2].Alpha = 0.0f; Cams[2].Beta = 0.0f; Cams[2].Mode = CCam::MODE_DEBUG; } void CCamera::CamShake(float strength, float x, float y, float z) { CVector Dist = Cams[ActiveCam].Source - CVector(x, y, z); // a bit complicated... float dist2d = Sqrt(SQR(Dist.x) + SQR(Dist.y)); float dist3d = Sqrt(SQR(dist2d) + SQR(Dist.z)); if(dist3d > 100.0f) dist3d = 100.0f; if(dist3d < 0.0f) dist3d = 0.0f; float mult = 1.0f - dist3d/100.0f; float curForce = mult*(m_fCamShakeForce - (CTimer::GetTimeInMilliseconds() - m_uiCamShakeStart)/1000.0f); strength = mult*strength; if(clamp(curForce, 0.0f, 2.0f) < strength){ m_fCamShakeForce = strength; m_uiCamShakeStart = CTimer::GetTimeInMilliseconds(); } } // This seems to be CCamera::CamShake(float) on PS2 void CamShakeNoPos(CCamera *cam, float strength) { float curForce = cam->m_fCamShakeForce - (CTimer::GetTimeInMilliseconds() - cam->m_uiCamShakeStart)/1000.0f; if(clamp(curForce, 0.0f, 2.0f) < strength){ cam->m_fCamShakeForce = strength; cam->m_uiCamShakeStart = CTimer::GetTimeInMilliseconds(); } } bool bAvoidTest1 = false; bool bAvoidTest2 = false; // unused bool bAvoidTest3 = false; // unused float fRangePlayerRadius = 0.5f; float fCloseNearClipLimit = 0.15f; float fAvoidTweakFOV = 1.15f; float fAvoidProbTimerDamp = 0.9f; void CCamera::AvoidTheGeometry(const CVector &Source, const CVector &TargetPos, CVector &NewSource, float FOV) { float Beta = 0.0f; float Alpha = 0.0f; CVector vDist = TargetPos - Source; m_vecClearGeometryVec = CVector(0.0f, 0.0f, 0.0f); float fDist = vDist.Magnitude(); float fDistOnGround = vDist.Magnitude2D(); if(vDist.x == 0.0f && vDist.y == 0.0f) Beta = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); else Beta = CGeneral::GetATanOfXY(vDist.x, vDist.y); if(fDistOnGround != 0.0f || vDist.z != 0.0f) Alpha = CGeneral::GetATanOfXY(fDistOnGround, vDist.z); CVector Front(Cos(Alpha)*Cos(Beta), Cos(Alpha)*Sin(Beta), Sin(Alpha)); NewSource = TargetPos - Front*fDist; Front.Normalise(); // Clip camera source CColPoint point; CEntity *entity = nil; CWorld::pIgnoreEntity = pTargetEntity; if(CWorld::ProcessLineOfSight(TargetPos, NewSource, point, entity, true, false, false, true, false, false, true)){ CVector ClipPoint1 = point.point; NewSource = point.point; if(!bAvoidTest1){ if(CWorld::ProcessLineOfSight(NewSource, TargetPos, point, entity, false, true, true, true, false, false, true)){ if((NewSource - point.point).Magnitude() < RwCameraGetNearClipPlane(Scene.camera)) NewSource = point.point; else if((NewSource - ClipPoint1).Magnitude() < RwCameraGetNearClipPlane(Scene.camera)) NewSource = ClipPoint1; } } } CWorld::pIgnoreEntity = nil; vDist = TargetPos - NewSource; fDist = vDist.Magnitude(); if(FindPlayerPed()) if(fDist - fRangePlayerRadius < RwCameraGetNearClipPlane(Scene.camera)) RwCameraSetNearClipPlane(Scene.camera, Max(fDist - fRangePlayerRadius, fCloseNearClipLimit)); static float fClearGeomAmount; static float fClearGeomAmountSpeed; float Near = RwCameraGetNearClipPlane(Scene.camera); float ViewPlaneHeight = Tan(DEGTORAD(FOV) / 2.0f); float ViewPlaneWidth = ViewPlaneHeight * CDraw::CalculateAspectRatio() * fAvoidTweakFOV; CVector Center = NewSource + Front*Near; float fClearGeomTarget = 0.0f; if(CWorld::TestSphereAgainstWorld(Center, ViewPlaneWidth, nil, true, false, false, true, false, true)){ CVector CamToCol = gaTempSphereColPoints[0].point - NewSource; float FrontDist = DotProduct(CamToCol, Front); CVector CenterToCol = gaTempSphereColPoints[0].point - Center; if(FrontDist < DEFAULT_NEAR && FrontDist > fCloseNearClipLimit){ if(FrontDist < RwCameraGetNearClipPlane(Scene.camera)) RwCameraSetNearClipPlane(Scene.camera, FrontDist); }else if(FrontDist < fCloseNearClipLimit) RwCameraSetNearClipPlane(Scene.camera, fCloseNearClipLimit); float ColDepth = ViewPlaneWidth - CenterToCol.Magnitude(); // amount of radius in collision CenterToCol.Normalise(); CVector Normal = gaTempSphereColPoints[0].normal; Normal.Normalise(); if(-DotProduct(CenterToCol, Normal) < 0.0f) Normal = -Normal; // always push away from col surface float DistToMove = DotProduct(-ColDepth*CenterToCol, Normal); m_vecClearGeometryVec = DistToMove*Normal; // move source so this point is out of collision if(pTargetEntity && pTargetEntity->IsPed() && RwCameraGetNearClipPlane(Scene.camera) < 2.0f*fCloseNearClipLimit){ float TargetNormalDir = DotProduct(Normal, pTargetEntity->GetForward()); if(TargetNormalDir < 0.0f){ // target looking towards collision if(m_fAvoidTheGeometryProbsTimer < 0.0f) m_fAvoidTheGeometryProbsTimer = 0.0f; m_fAvoidTheGeometryProbsTimer += CTimer::GetTimeStep(); }else if(TargetNormalDir > 0.5f){ // target looking away from collision if(m_fAvoidTheGeometryProbsTimer > 0.0f) m_fAvoidTheGeometryProbsTimer = 0.0f; m_fAvoidTheGeometryProbsTimer -= CTimer::GetTimeStep(); } if(m_nAvoidTheGeometryProbsDirn == 0){ if(CrossProduct(pTargetEntity->GetPosition() - NewSource, Normal).z > 0.0f) m_nAvoidTheGeometryProbsDirn = -1; else m_nAvoidTheGeometryProbsDirn = 1; } } fClearGeomTarget = 1.0f; } m_fAvoidTheGeometryProbsTimer *= Pow(fAvoidProbTimerDamp, CTimer::GetTimeStep()); WellBufferMe(fClearGeomTarget, &fClearGeomAmount, &fClearGeomAmountSpeed, 0.2f, 0.05f, false); m_vecClearGeometryVec *= fClearGeomAmount; m_bMoveCamToAvoidGeom = true; } void CCamera::GetArrPosForVehicleType(int apperance, int &index) { switch(apperance){ case VEHICLE_APPEARANCE_CAR: index = 0; break; case VEHICLE_APPEARANCE_BIKE: index = 1; break; case VEHICLE_APPEARANCE_HELI: index = 2; break; case VEHICLE_APPEARANCE_PLANE: index = 3; break; case VEHICLE_APPEARANCE_BOAT: index = 4; break; } } void CCamera::GetScreenRect(CRect &rect) { rect.left = 0.0f; rect.right = SCREEN_WIDTH; if(m_WideScreenOn #ifdef CUTSCENE_BORDERS_SWITCH && CMenuManager::m_PrefsCutsceneBorders #endif ){ float borderSize = (SCREEN_HEIGHT / 2) * (m_ScreenReductionPercentage / 100.f); rect.top = borderSize - SCREEN_SCALE_Y(22.f); rect.bottom = SCREEN_HEIGHT - borderSize - SCREEN_SCALE_Y(14.f); }else{ rect.top = 0.0f; rect.bottom = SCREEN_HEIGHT; } } void CCamera::TakeControl(CEntity *target, int16 mode, int16 typeOfSwitch, int32 controller) { bool doSwitch = true; if(controller == CAMCONTROL_OBBE && WhoIsInControlOfTheCamera == CAMCONTROL_SCRIPT) doSwitch = false; if(doSwitch){ WhoIsInControlOfTheCamera = controller; if(target){ if(mode == CCam::MODE_NONE){ // Why are we checking the old entity? if(pTargetEntity->IsPed()) mode = CCam::MODE_FOLLOWPED; else if(pTargetEntity->IsVehicle()) mode = CCam::MODE_CAM_ON_A_STRING; } }else if(FindPlayerVehicle()) target = FindPlayerVehicle(); else target = PLAYER; m_bLookingAtVector = false; pTargetEntity = target; m_iModeToGoTo = mode; m_iTypeOfSwitch = typeOfSwitch; m_bLookingAtPlayer = false; m_bStartInterScript = true; } } void CCamera::TakeControlNoEntity(const CVector &position, int16 typeOfSwitch, int32 controller) { bool doSwitch = true; if(controller == CAMCONTROL_OBBE && WhoIsInControlOfTheCamera == CAMCONTROL_SCRIPT) doSwitch = false; if(doSwitch){ WhoIsInControlOfTheCamera = controller; m_bLookingAtVector = true; m_bLookingAtPlayer = false; m_iModeToGoTo = CCam::MODE_FIXED; m_vecFixedModeVector = position; m_iTypeOfSwitch = typeOfSwitch; m_bStartInterScript = true; } } void CCamera::TakeControlWithSpline(int16 typeOfSwitch) { m_iModeToGoTo = CCam::MODE_FLYBY; m_bLookingAtPlayer = false; m_bLookingAtVector = false; m_bcutsceneFinished = false; m_iTypeOfSwitch = typeOfSwitch; m_bStartInterScript = true; }; void CCamera::Restore(void) { m_bLookingAtPlayer = true; m_bLookingAtVector = false; m_iTypeOfSwitch = INTERPOLATION; m_bUseNearClipScript = false; m_iModeObbeCamIsInForCar = OBBE_INVALID; m_fPositionAlongSpline = 0.0; m_bStartingSpline = false; m_bScriptParametersSetForInterPol = false; WhoIsInControlOfTheCamera = CAMCONTROL_GAME; if(FindPlayerVehicle()){ m_iModeToGoTo = CCam::MODE_CAM_ON_A_STRING; pTargetEntity = FindPlayerVehicle(); }else{ m_iModeToGoTo = CCam::MODE_FOLLOWPED; pTargetEntity = PLAYER; } if(PLAYER->GetPedState() == PED_ENTER_CAR || PLAYER->GetPedState() == PED_CARJACK || PLAYER->GetPedState() == PED_OPEN_DOOR){ m_iModeToGoTo = CCam::MODE_CAM_ON_A_STRING; pTargetEntity = PLAYER->m_pSeekTarget; } if(PLAYER->GetPedState() == PED_EXIT_CAR){ m_iModeToGoTo = CCam::MODE_FOLLOWPED; pTargetEntity = PLAYER; } m_bEnable1rstPersonCamCntrlsScript = false; m_bAllow1rstPersonWeaponsCamera = false; m_bUseScriptZoomValuePed = false; m_bUseScriptZoomValueCar = false; m_bStartInterScript = true; m_bCameraJustRestored = true; m_fAvoidTheGeometryProbsTimer = 0.0f; } void CCamera::RestoreWithJumpCut(void) { m_bRestoreByJumpCut = true; m_bLookingAtPlayer = true; m_bLookingAtVector = false; m_iTypeOfSwitch = JUMP_CUT; m_bUseNearClipScript = false; m_iModeObbeCamIsInForCar = OBBE_INVALID; m_fPositionAlongSpline = 0.0; m_bStartingSpline = false; m_bScriptParametersSetForInterPol = false; WhoIsInControlOfTheCamera = CAMCONTROL_GAME; m_bCameraJustRestored = true; m_bEnable1rstPersonCamCntrlsScript = false; m_bAllow1rstPersonWeaponsCamera = false; if(FindPlayerVehicle()){ m_iModeToGoTo = CCam::MODE_CAM_ON_A_STRING; pTargetEntity = FindPlayerVehicle(); }else{ m_iModeToGoTo = CCam::MODE_FOLLOWPED; pTargetEntity = PLAYER; } if(PLAYER->GetPedState() == PED_ENTER_CAR || PLAYER->GetPedState() == PED_CARJACK || PLAYER->GetPedState() == PED_OPEN_DOOR){ m_iModeToGoTo = CCam::MODE_CAM_ON_A_STRING; pTargetEntity = PLAYER->m_pSeekTarget; } if(PLAYER->GetPedState() == PED_EXIT_CAR){ m_iModeToGoTo = CCam::MODE_FOLLOWPED; pTargetEntity = PLAYER; } m_bUseScriptZoomValuePed = false; m_bUseScriptZoomValueCar = false; } void CCamera::SetCamPositionForFixedMode(const CVector &Source, const CVector &UpOffSet) { m_vecFixedModeSource = Source; m_vecFixedModeUpOffSet = UpOffSet; m_bGarageFixedCamPositionSet = false; } void CCamera::StartTransition(int16 newMode) { bool switchFromFixedSyphon = false; bool switchSyphonMode = false; bool switchPedMode = false; bool switchPedToCar = false; bool switchFromFight = false; bool switchBikeToPed = false; bool switchFromFixed = false; bool switch1stPersonToVehicle = false; float betaOffset, targetBeta, camBeta, deltaBeta; int door; bool vehicleVertical; m_bItsOkToLookJustAtThePlayer = false; m_fFractionInterToStopMoving = 0.25f; m_fFractionInterToStopCatchUp = 0.75f; if(Cams[ActiveCam].Mode == CCam::MODE_SYPHON_CRIM_IN_FRONT || Cams[ActiveCam].Mode == CCam::MODE_FOLLOWPED || Cams[ActiveCam].Mode == CCam::MODE_SYPHON || Cams[ActiveCam].Mode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON){ if(newMode == CCam::MODE_SYPHON_CRIM_IN_FRONT || newMode == CCam::MODE_FOLLOWPED || newMode == CCam::MODE_SYPHON || newMode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON) switchPedMode = true; if(newMode == CCam::MODE_CAM_ON_A_STRING) switchPedToCar = true; } if(Cams[ActiveCam].Mode == CCam::MODE_SPECIAL_FIXED_FOR_SYPHON) switchFromFixedSyphon = true; if(Cams[ActiveCam].Mode == CCam::MODE_CAM_ON_A_STRING && newMode == CCam::MODE_FOLLOWPED && m_bPlayerWasOnBike) switchBikeToPed = true; if(Cams[ActiveCam].Mode == CCam::MODE_SYPHON_CRIM_IN_FRONT && newMode == CCam::MODE_SYPHON) switchSyphonMode = true; if(Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM && newMode == CCam::MODE_FOLLOWPED) switchFromFight = true; if(Cams[ActiveCam].Mode == CCam::MODE_FIXED) switchFromFixed = true; m_bUseTransitionBeta = false; if((Cams[ActiveCam].Mode == CCam::MODE_SNIPER || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_SNIPER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_FIGHT_CAM_RUNABOUT || Cams[ActiveCam].Mode == CCam::MODE_HELICANNON_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_CAMERA || Cams[ActiveCam].Mode == CCam::MODE_1STPERSON_RUNABOUT) && pTargetEntity->IsPed()){ float angle = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y) - HALFPI; ((CPed*)pTargetEntity)->m_fRotationCur = angle; ((CPed*)pTargetEntity)->m_fRotationDest = angle; } Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; Cams[ActiveCam].CamTargetEntity = pTargetEntity; Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; Cams[ActiveCam].m_bCamLookingAtVector = m_bLookingAtVector; if(newMode == CCam::MODE_SNIPER || newMode == CCam::MODE_ROCKETLAUNCHER || newMode == CCam::MODE_M16_1STPERSON || newMode == CCam::MODE_SNIPER_RUNABOUT || newMode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || newMode == CCam::MODE_1STPERSON_RUNABOUT || newMode == CCam::MODE_M16_1STPERSON_RUNABOUT || newMode == CCam::MODE_FIGHT_CAM_RUNABOUT || newMode == CCam::MODE_HELICANNON_1STPERSON || newMode == CCam::MODE_CAMERA) Cams[ActiveCam].Alpha = 0.0f; switch(Cams[ActiveCam].Mode) case CCam::MODE_SNIPER_RUNABOUT: case CCam::MODE_ROCKETLAUNCHER_RUNABOUT: case CCam::MODE_1STPERSON_RUNABOUT: case CCam::MODE_M16_1STPERSON_RUNABOUT: case CCam::MODE_FIGHT_CAM_RUNABOUT: case CCam::MODE_CAMERA: if(newMode == CCam::MODE_CAM_ON_A_STRING || newMode == CCam::MODE_BEHINDBOAT) switch1stPersonToVehicle = true; switch(newMode){ case CCam::MODE_BEHINDCAR: Cams[ActiveCam].BetaSpeed = 0.0f; break; case CCam::MODE_BEHINDBOAT: Cams[ActiveCam].BetaSpeed = 0.0f; break; case CCam::MODE_FOLLOWPED: // Getting out of vehicle normally betaOffset = DEGTORAD(55.0f); if(m_bJustCameOutOfGarage){ m_bUseTransitionBeta = true; if(Cams[ActiveCam].Front.x != 0.0f || Cams[ActiveCam].Front.y != 0.0f) Cams[ActiveCam].m_fTransitionBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y) + PI; else Cams[ActiveCam].m_fTransitionBeta = 0.0f; } if(m_bTargetJustCameOffTrain) m_bCamDirectlyInFront = true; if(Cams[ActiveCam].Mode != CCam::MODE_CAM_ON_A_STRING) break; m_bUseTransitionBeta = true; vehicleVertical = false; if(((CPed*)pTargetEntity)->m_carInObjective && ((CPed*)pTargetEntity)->m_carInObjective->GetForward().x == 0.0f && ((CPed*)pTargetEntity)->m_carInObjective->GetForward().y == 0.0f) vehicleVertical = true; if(vehicleVertical){ Cams[ActiveCam].m_fTransitionBeta = 0.0f; break; } camBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y); if(((CPed*)pTargetEntity)->m_carInObjective) targetBeta = CGeneral::GetATanOfXY(((CPed*)pTargetEntity)->m_carInObjective->GetForward().x, ((CPed*)pTargetEntity)->m_carInObjective->GetForward().y); else targetBeta = camBeta; deltaBeta = targetBeta - camBeta; while(deltaBeta >= PI) deltaBeta -= 2*PI; while(deltaBeta < -PI) deltaBeta += 2*PI; deltaBeta = Abs(deltaBeta); door = FindPlayerPed()->m_vehDoor; if(deltaBeta > HALFPI){ if(((CPed*)pTargetEntity)->m_carInObjective){ if(((CPed*)pTargetEntity)->m_carInObjective->IsUpsideDown()){ if(door == CAR_DOOR_LF || door == CAR_DOOR_LR) betaOffset = -DEGTORAD(95.0f); }else{ if(door == CAR_DOOR_RF || door == CAR_DOOR_RR) betaOffset = -DEGTORAD(95.0f); } } Cams[ActiveCam].m_fTransitionBeta = targetBeta + betaOffset; }else{ if(((CPed*)pTargetEntity)->m_carInObjective){ if(((CPed*)pTargetEntity)->m_carInObjective->IsUpsideDown()){ if(door == CAR_DOOR_RF || door == CAR_DOOR_RR) betaOffset = -DEGTORAD(55.0f); else if(door == CAR_DOOR_LF || door == CAR_DOOR_LR) betaOffset = DEGTORAD(95.0f); }else{ if(door == CAR_DOOR_LF || door == CAR_DOOR_LR) betaOffset = -DEGTORAD(55.0f); else if(door == CAR_DOOR_RF || door == CAR_DOOR_RR) betaOffset = DEGTORAD(95.0f); } } Cams[ActiveCam].m_fTransitionBeta = targetBeta + betaOffset + PI; } break; case CCam::MODE_SNIPER: case CCam::MODE_ROCKETLAUNCHER: case CCam::MODE_M16_1STPERSON: case CCam::MODE_SNIPER_RUNABOUT: case CCam::MODE_ROCKETLAUNCHER_RUNABOUT: case CCam::MODE_1STPERSON_RUNABOUT: case CCam::MODE_M16_1STPERSON_RUNABOUT: case CCam::MODE_FIGHT_CAM_RUNABOUT: case CCam::MODE_HELICANNON_1STPERSON: case CCam::MODE_CAMERA: if(FindPlayerVehicle()) Cams[ActiveCam].Beta = Atan2(FindPlayerVehicle()->GetForward().x, FindPlayerVehicle()->GetForward().y); else Cams[ActiveCam].Beta = Atan2(PLAYER->GetForward().x, PLAYER->GetForward().y); break; case CCam::MODE_SYPHON: Cams[ActiveCam].Alpha = 0.0f; Cams[ActiveCam].AlphaSpeed = 0.0f; break; case CCam::MODE_CAM_ON_A_STRING: // Get into vehicle betaOffset = DEGTORAD(57.0f); if(!m_bLookingAtPlayer || m_bJustCameOutOfGarage) break; m_bUseTransitionBeta = true; Cams[ActiveCam].m_fTransitionBeta = CGeneral::GetATanOfXY(Cams[ActiveCam].Front.x, Cams[ActiveCam].Front.y); break; case CCam::MODE_PED_DEAD_BABY: Cams[ActiveCam].Alpha = DEGTORAD(15.0f); break; case CCam::MODE_FIGHT_CAM: Cams[ActiveCam].Beta = 0.0f; Cams[ActiveCam].BetaSpeed = 0.0f; Cams[ActiveCam].Alpha = 0.0f; Cams[ActiveCam].AlphaSpeed = 0.0f; break; } Cams[ActiveCam].Init(); Cams[ActiveCam].Mode = newMode; m_uiTransitionDuration = 1350; if(switchSyphonMode) m_uiTransitionDuration = 1800; else if(switchFromFight) m_uiTransitionDuration = 750; else if(switchPedToCar){ m_fFractionInterToStopMoving = 0.1f; m_fFractionInterToStopCatchUp = 0.9f; m_uiTransitionDuration = 750; }else if(switchFromFixedSyphon){ m_fFractionInterToStopMoving = 0.0f; m_fFractionInterToStopCatchUp = 1.0f; m_uiTransitionDuration = 600; }else if(switchFromFixed){ m_fFractionInterToStopMoving = 0.05f; m_fFractionInterToStopCatchUp = 0.95f; }else if(switchBikeToPed){ m_uiTransitionDuration = 800; }else if(switch1stPersonToVehicle){ m_fFractionInterToStopMoving = 0.0f; m_fFractionInterToStopCatchUp = 1.0f; m_uiTransitionDuration = 1; }else if(switchPedMode){ m_fFractionInterToStopMoving = 0.5f; m_fFractionInterToStopCatchUp = 0.5f; m_uiTransitionDuration = 350; }else m_uiTransitionDuration = 1350; // already set above m_uiTransitionState = 1; m_uiTimeTransitionStart = CTimer::GetTimeInMilliseconds(); m_uiTransitionJUSTStarted = 1; if(m_vecDoingSpecialInterPolation){ m_cvecStartingSourceForInterPol = SourceDuringInter; m_cvecStartingTargetForInterPol = TargetDuringInter; m_cvecStartingUpForInterPol = UpDuringInter; m_fStartingAlphaForInterPol = m_fAlphaDuringInterPol; m_fStartingBetaForInterPol = m_fBetaDuringInterPol; }else{ m_cvecStartingSourceForInterPol = Cams[ActiveCam].Source; m_cvecStartingTargetForInterPol = Cams[ActiveCam].m_cvecTargetCoorsForFudgeInter; m_cvecStartingUpForInterPol = Cams[ActiveCam].Up; m_fStartingAlphaForInterPol = Cams[ActiveCam].m_fTrueAlpha; m_fStartingBetaForInterPol = Cams[ActiveCam].m_fTrueBeta; } Cams[ActiveCam].m_bCamLookingAtVector = m_bLookingAtVector; Cams[ActiveCam].m_cvecCamFixedModeVector = m_vecFixedModeVector; Cams[ActiveCam].m_cvecCamFixedModeSource = m_vecFixedModeSource; Cams[ActiveCam].m_cvecCamFixedModeUpOffSet = m_vecFixedModeUpOffSet; Cams[ActiveCam].Mode = newMode; // already done above Cams[ActiveCam].CamTargetEntity = pTargetEntity; m_uiTransitionState = 1; // these three already done above m_uiTimeTransitionStart = CTimer::GetTimeInMilliseconds(); m_uiTransitionJUSTStarted = 1; m_fStartingFOVForInterPol = Cams[ActiveCam].FOV; m_cvecSourceSpeedAtStartInter = Cams[ActiveCam].m_cvecSourceSpeedOverOneFrame; m_cvecTargetSpeedAtStartInter = Cams[ActiveCam].m_cvecTargetSpeedOverOneFrame; m_cvecUpSpeedAtStartInter = Cams[ActiveCam].m_cvecUpOverOneFrame; m_fAlphaSpeedAtStartInter = Cams[ActiveCam].m_fAlphaSpeedOverOneFrame; m_fBetaSpeedAtStartInter = Cams[ActiveCam].m_fBetaSpeedOverOneFrame; m_fFOVSpeedAtStartInter = Cams[ActiveCam].m_fFovSpeedOverOneFrame; Cams[ActiveCam].ResetStatics = true; if(m_bLookingAtPlayer){ if(switchPedMode) m_uiTransitionDurationTargetCoors = 350; else m_uiTransitionDurationTargetCoors = 600; m_fFractionInterToStopMovingTarget = 0.0f; m_fFractionInterToStopCatchUpTarget = 1.0f; }else{ if(m_bScriptParametersSetForInterPol){ m_fFractionInterToStopMoving = m_fScriptPercentageInterToStopMoving; m_fFractionInterToStopCatchUp = m_fScriptPercentageInterToCatchUp; m_uiTransitionDuration = m_fScriptTimeForInterPolation; } m_uiTransitionDurationTargetCoors = m_uiTransitionDuration; m_fFractionInterToStopMovingTarget = m_fFractionInterToStopMoving; m_fFractionInterToStopCatchUpTarget = m_fFractionInterToStopCatchUp; } } void CCamera::StartTransitionWhenNotFinishedInter(int16 mode) { m_vecDoingSpecialInterPolation = true; StartTransition(mode); } void CCamera::StoreValuesDuringInterPol(CVector &source, CVector &target, CVector &up, float &FOV) { SourceDuringInter = source; TargetDuringInter = target; UpDuringInter = up; FOVDuringInter = FOV; CVector Dist = source - TargetDuringInter; float DistOnGround = Dist.Magnitude2D(); m_fBetaDuringInterPol = CGeneral::GetATanOfXY(Dist.x, Dist.y); m_fAlphaDuringInterPol = CGeneral::GetATanOfXY(DistOnGround, Dist.z); } void CCamera::SetWideScreenOn(void) { m_WideScreenOn = true; } void CCamera::SetWideScreenOff(void) { m_bWantsToSwitchWidescreenOff = m_WideScreenOn; } void CCamera::ProcessWideScreenOn(void) { if(m_bWantsToSwitchWidescreenOff){ m_bWantsToSwitchWidescreenOff = false; m_WideScreenOn = false; m_ScreenReductionPercentage = 0.0f; m_fFOV_Wide_Screen = 0.0f; m_fWideScreenReductionAmount = 0.0f; }else{ m_fFOV_Wide_Screen = 0.3f*Cams[ActiveCam].FOV; m_fWideScreenReductionAmount = 1.0f; m_ScreenReductionPercentage = 30.0f; } } void CCamera::DrawBordersForWideScreen(void) { float bottom, top; if (m_WideScreenOn) { float borderSize = (SCREEN_HEIGHT / 2) * (m_ScreenReductionPercentage / 100.f); top = borderSize - SCREEN_SCALE_Y(22.f); bottom = SCREEN_HEIGHT - borderSize - SCREEN_SCALE_Y(14.f); } else { top = 0.f; bottom = SCREEN_HEIGHT; } if(m_BlurType == MOTION_BLUR_NONE || m_BlurType == MOTION_BLUR_LIGHT_SCENE) SetMotionBlurAlpha(80); // top border CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, top), CRGBA(0, 0, 0, 255)); // bottom border CSprite2d::DrawRect(CRect(0.0f, bottom, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(0, 0, 0, 255)); } bool CCamera::IsItTimeForNewcam(int32 obbeMode, int32 time) { CVehicle *veh; uint32 t = time; // no annoying compiler warnings CVector fwd; if(obbeMode < 0) return true; switch(obbeMode){ case OBBE_WHEEL: veh = FindPlayerVehicle(); if(veh){ if(veh->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) return true; if(veh->GetModelIndex() == MI_RHINO) return true; if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), Cams[ActiveCam].Source, true, false, false, false, false, false, false)) return true; } if(CTimer::GetTimeInMilliseconds() > t+5000) return true; SetNearClipScript(0.6f); return false; case OBBE_1: if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) return true; if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 40.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; // too close if(fwd.Magnitude() < 1.6f) return true; return false; case OBBE_2: if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) return true; if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; if(fwd.Magnitude() < 2.0f) // very close, fix near clip SetNearClipScript(Max(fwd.Magnitude()*0.5f, 0.05f)); // too far and driving away from cam if(fwd.Magnitude() > 29.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; // too close if(fwd.Magnitude() < 2.0f) return true; return false; case OBBE_3: if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 48.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; return false; case OBBE_1STPERSON: return CTimer::GetTimeInMilliseconds() > t+3000; case OBBE_5: if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) return true; if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 38.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; return false; case OBBE_ONSTRING: return CTimer::GetTimeInMilliseconds() > t+3000; case OBBE_COPCAR: return CTimer::GetTimeInMilliseconds() > t+2000 && !FindPlayerVehicle()->GetIsOnScreen(); case OBBE_COPCAR_WHEEL: if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) return true; if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), Cams[ActiveCam].Source, true, false, false, false, false, false, false)) return true; if(CTimer::GetTimeInMilliseconds() > t+1000) return true; SetNearClipScript(0.6f); return false; // Ped modes case OBBE_9: if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 20.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; return false; case OBBE_10: if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 8.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; return false; case OBBE_11: if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 25.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; return false; case OBBE_12: if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 8.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; return false; case OBBE_13: return CTimer::GetTimeInMilliseconds() > t+5000; // Heli modes case OBBE_14: if(FindPlayerVehicle()) if(!CWorld::GetIsLineOfSightClear(pTargetEntity->GetPosition(), Cams[ActiveCam].Source, true, false, false, false, false, false, false)) return true; return CTimer::GetTimeInMilliseconds() > t+8000; case OBBE_15: if(FindPlayerVehicle()){ if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 44.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; // too close if(fwd.Magnitude() < 2.0f) return true; } return false; case OBBE_16: if(FindPlayerVehicle()){ if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 50.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return true; // too close if(fwd.Magnitude() < 3.0f) return true; } return false; case OBBE_17: if(FindPlayerVehicle()){ if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far if(fwd.Magnitude() > 50.0f) return true; // too close if(fwd.Magnitude() < 2.0f) return true; } return false; case OBBE_18: if(FindPlayerVehicle()){ if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; // too far if(fwd.Magnitude() > 57.0f) return true; // too close if(fwd.Magnitude() < 1.0f) return true; } return false; case OBBE_19: if(FindPlayerVehicle()){ if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), m_vecFixedModeSource, true, false, false, false, false, false, false)) return true; fwd = FindPlayerCoors() - m_vecFixedModeSource; fwd.z = 0.0f; // too far if(fwd.Magnitude() > 36.0f) return true; // too close if(fwd.Magnitude() < 2.0f) return true; } return false; case OBBE_ONSTRING_HELI: return CTimer::GetTimeInMilliseconds() > t+5000; default: return false; } } bool CCamera::TryToStartNewCamMode(int obbeMode) { CVehicle *veh; CVector target, camPos, playerSpeed, fwd, fwd2; float angle; float ground; bool foundGround; int i; if(obbeMode < 0) return true; switch(obbeMode){ case OBBE_WHEEL: veh = FindPlayerVehicle(); if(veh == nil || (veh->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) || veh->GetModelIndex() == MI_RHINO) return false; target = Multiply3x3(FindPlayerVehicle()->GetMatrix(), CVector(-1.4f, -2.3f, 0.3f)); target += FindPlayerVehicle()->GetPosition(); if(!CWorld::GetIsLineOfSightClear(veh->GetPosition(), target, true, false, false, false, false, false, false)) return false; TakeControl(veh, CCam::MODE_WHEELCAM, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_1: camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); camPos += 20.0f*playerSpeed; camPos += 3.0f*CVector(playerSpeed.y, -playerSpeed.x, 0.0f); if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) return false; ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); if(foundGround) camPos.z = ground + 1.5f; else{ ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z-5.0f, &foundGround); if(foundGround) camPos.z = ground + 1.5f; } if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; fwd = FindPlayerCoors() - camPos; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 40.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return false; // too close if(fwd.Magnitude() < 2.5f) return true; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_2: if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) return false; camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); camPos += 16.0f*playerSpeed; camPos += 2.5f*CVector(playerSpeed.y, -playerSpeed.x, 0.0f); ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); if(foundGround) camPos.z = ground + 0.5f; else{ ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z-5.0f, &foundGround); if(foundGround) camPos.z = ground + 0.5f; } if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; fwd = FindPlayerCoors() - camPos; fwd.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 29.0f && DotProduct(FindPlayerSpeed(), fwd) > 0.0f) return false; // too close if(fwd.Magnitude() < 2.0f) return true; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_3: camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); camPos += 30.0f*playerSpeed; camPos += 8.0f*CVector(playerSpeed.y, -playerSpeed.x, 0.0f); if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_1STPERSON: TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_5: camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); camPos += 30.0f*playerSpeed; camPos += 6.0f*CVector(playerSpeed.y, -playerSpeed.x, 0.0f); ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); if(foundGround) camPos.z = ground + 3.5f; else{ ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z-5.0f, &foundGround); if(foundGround) camPos.z = ground + 3.5f; } if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_ONSTRING: TakeControl(FindPlayerEntity(), CCam::MODE_CAM_ON_A_STRING, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_COPCAR: #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return false; #endif if(FindPlayerPed()->m_pWanted->GetWantedLevel() < 1) return false; if(FindPlayerVehicle() == nil) return false; if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) return false; i = CPools::GetVehiclePool()->GetSize(); while(--i >= 0){ veh = CPools::GetVehiclePool()->GetSlot(i); if(veh && veh->IsCar() && veh != FindPlayerVehicle() && veh->bIsLawEnforcer){ float dx = veh->GetPosition().x - FindPlayerCoors().x; float dy = veh->GetPosition().y - FindPlayerCoors().y; float dist = (veh->GetPosition() - FindPlayerCoors()).Magnitude(); if(dist < 30.0f){ if(dx*FindPlayerVehicle()->GetForward().x + dy*FindPlayerVehicle()->GetForward().y < 0.0f && veh->GetForward().x*FindPlayerVehicle()->GetForward().x + veh->GetForward().y*FindPlayerVehicle()->GetForward().y > 0.8f){ TakeControl(veh, CCam::MODE_CAM_ON_A_STRING, JUMP_CUT, CAMCONTROL_OBBE); return true; } } } } return false; case OBBE_COPCAR_WHEEL: #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return false; #endif if(FindPlayerPed()->m_pWanted->GetWantedLevel() < 1) return false; if(FindPlayerVehicle() == nil) return false; if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && pTargetEntity->GetModelIndex() != MI_SKIMMER) return false; i = CPools::GetVehiclePool()->GetSize(); while(--i >= 0){ veh = CPools::GetVehiclePool()->GetSlot(i); if(veh && veh->IsCar() && veh != FindPlayerVehicle() && veh->bIsLawEnforcer){ float dx = veh->GetPosition().x - FindPlayerCoors().x; float dy = veh->GetPosition().y - FindPlayerCoors().y; float dist = (veh->GetPosition() - FindPlayerCoors()).Magnitude(); if(dist < 30.0f){ if(dx*FindPlayerVehicle()->GetForward().x + dy*FindPlayerVehicle()->GetForward().y < 0.0f && veh->GetForward().x*FindPlayerVehicle()->GetForward().x + veh->GetForward().y*FindPlayerVehicle()->GetForward().y > 0.8f){ target = Multiply3x3(veh->GetMatrix(), CVector(-1.4f, -2.3f, 0.3f)); target += veh->GetPosition(); if(!CWorld::GetIsLineOfSightClear(veh->GetPosition(), target, true, false, false, false, false, false, false)) return false; TakeControl(veh, CCam::MODE_WHEELCAM, JUMP_CUT, CAMCONTROL_OBBE); return true; } } } } return false; case OBBE_9: camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); camPos += 15.0f*playerSpeed; camPos += CVector(2.0f, 1.0f, 0.0f); ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); if(foundGround) camPos.z = ground + 0.5f; else{ ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z-5.0f, &foundGround); if(foundGround) camPos.z = ground + 0.5f; } if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_10: camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); camPos += 5.0f*playerSpeed; camPos += CVector(2.0f, 1.0f, 0.5f); if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_11: camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); camPos += 20.0f*playerSpeed; camPos += CVector(2.0f, 1.0f, 20.0f); if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_12: camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); camPos += 5.0f*playerSpeed; camPos += CVector(2.0f, 1.0f, 10.5f); if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_13: #ifdef FIX_BUGS TakeControl(FindPlayerEntity(), CCam::MODE_TOP_DOWN_PED, JUMP_CUT, CAMCONTROL_OBBE); #else TakeControl(FindPlayerEntity(), CCam::MODE_TOPDOWN, JUMP_CUT, CAMCONTROL_OBBE); #endif return true; // Heli modes case OBBE_14: veh = FindPlayerVehicle(); if(veh == nil) return false; target = Multiply3x3(FindPlayerVehicle()->GetMatrix(), CVector(-1.4f, -2.3f, 0.3f)); target += FindPlayerVehicle()->GetPosition(); if(!veh->IsBoat() && !CWorld::GetIsLineOfSightClear(veh->GetPosition(), target, true, false, false, false, false, false, false)) return false; TakeControl(veh, CCam::MODE_WHEELCAM, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_15: if(FindPlayerVehicle() == nil) return false; camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); camPos += 34.0f*playerSpeed; camPos.z = FindPlayerCoors().z + 0.5f; if(FindPlayerVehicle()->IsBoat()) camPos.z += 1.0f; if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; fwd = FindPlayerCoors() - camPos; fwd2 = FindPlayerCoors() - camPos; fwd2.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 44.0f && DotProduct(FindPlayerSpeed(), fwd2) > 0.0f) return false; // too close if(fwd.Magnitude() < 3.0f) return true; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_16: if(FindPlayerVehicle() == nil) return false; camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); angle = CGeneral::GetATanOfXY(playerSpeed.x, playerSpeed.y) + DEGTORAD(60.0f); playerSpeed += CVector(Cos(angle), Sin(angle), 0.0f); playerSpeed.Normalise(); camPos += 30.0f*playerSpeed; camPos.z = FindPlayerCoors().z - 5.5f; foundGround = false; ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); if(foundGround) camPos.z = ground + 0.5f; else if(CWaterLevel::GetWaterLevelNoWaves(camPos.x, camPos.y, camPos.z, &ground)){ float waterOffset = 1.0f; if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) waterOffset = -2.0f; if(camPos.z < ground + waterOffset) camPos.z = ground + waterOffset; } if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; fwd = FindPlayerCoors() - camPos; // too far if(fwd.Magnitude() > 50.0f) return false; // too close if(fwd.Magnitude() < 3.0f) return true; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_17: if(FindPlayerVehicle() == nil) return false; camPos = FindPlayerCoors(); playerSpeed = FindPlayerSpeed(); playerSpeed.z = 0.0f; playerSpeed.Normalise(); angle = CGeneral::GetATanOfXY(playerSpeed.x, playerSpeed.y) + DEGTORAD(190.0f); playerSpeed += CVector(Cos(angle), Sin(angle), 0.0f); playerSpeed.Normalise(); camPos += 25.0f*playerSpeed; camPos.z = FindPlayerCoors().z - 1.0f; foundGround = false; ground = CWorld::FindRoofZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); if(foundGround) camPos.z = ground + 0.5f; else if(CWaterLevel::GetWaterLevelNoWaves(camPos.x, camPos.y, camPos.z, &ground)){ float waterOffset = 1.0f; if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) waterOffset = -2.0f; if(camPos.z < ground + waterOffset) camPos.z = ground + waterOffset; } if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; fwd = FindPlayerCoors() - camPos; fwd2 = FindPlayerCoors() - camPos; fwd2.z = 0.0f; // too far and driving away from cam if(fwd.Magnitude() > 50.0f && DotProduct(FindPlayerSpeed(), fwd2) > 0.0f) return false; // too close if(fwd.Magnitude() < 2.0f) return true; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_18: camPos = FindPlayerCoors(); if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) camPos.z += 23.0f; else camPos.z -= 23.0f; playerSpeed = FindPlayerSpeed(); angle = CGeneral::GetATanOfXY(playerSpeed.x, playerSpeed.y) + DEGTORAD(145.0f); playerSpeed += CVector(Cos(angle), Sin(angle), 0.0f); playerSpeed.Normalise(); camPos += 15.0f*playerSpeed; foundGround = false; ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); #ifdef FIX_BUGS if(foundGround) #else if(ground == true) #endif { if(camPos.z < ground) camPos.z = ground + 0.5f; }else if(CWaterLevel::GetWaterLevelNoWaves(camPos.x, camPos.y, camPos.z, &ground)){ float waterOffset = 1.0f; if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) waterOffset = -2.0f; if(camPos.z < ground + waterOffset) camPos.z = ground + waterOffset; } if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; fwd = FindPlayerCoors() - camPos; // too far if(fwd.Magnitude() > 57.0f) return false; // too close if(fwd.Magnitude() < 1.0f) return true; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_19: camPos = FindPlayerCoors(); if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) camPos.z += 4.0f; else camPos.z -= 1.0f; playerSpeed = FindPlayerSpeed(); angle = CGeneral::GetATanOfXY(playerSpeed.x, playerSpeed.y) + DEGTORAD(28.0f); playerSpeed += CVector(Cos(angle), Sin(angle), 0.0f); playerSpeed.Normalise(); camPos += 12.5f*playerSpeed; foundGround = false; ground = CWorld::FindGroundZFor3DCoord(camPos.x, camPos.y, camPos.z+5.0f, &foundGround); #ifdef FIX_BUGS if(foundGround) #else if(ground == true) #endif { if(camPos.z < ground) camPos.z = ground + 0.5f; }else if(CWaterLevel::GetWaterLevelNoWaves(camPos.x, camPos.y, camPos.z, &ground)){ float waterOffset = 1.0f; if(FindPlayerVehicle() && FindPlayerVehicle()->IsBoat()) waterOffset = -2.0f; if(camPos.z < ground + waterOffset) camPos.z = ground + waterOffset; } if(!CWorld::GetIsLineOfSightClear(FindPlayerCoors(), camPos, true, false, false, false, false, false, false)) return false; fwd = FindPlayerCoors() - camPos; // too far if(fwd.Magnitude() > 36.0f) return false; // too close if(fwd.Magnitude() < 2.0f) return true; SetCamPositionForFixedMode(camPos, CVector(0.0f, 0.0f, 0.0f)); TakeControl(FindPlayerEntity(), CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_OBBE); return true; case OBBE_ONSTRING_HELI: TakeControl(FindPlayerEntity(), CCam::MODE_CAM_ON_A_STRING, JUMP_CUT, CAMCONTROL_OBBE); return true; default: return false; } } int32 SequenceOfCarCams[16] = { OBBE_WHEEL, OBBE_COPCAR, OBBE_3, OBBE_1, OBBE_3, OBBE_COPCAR_WHEEL, OBBE_2, OBBE_3, OBBE_COPCAR_WHEEL, OBBE_COPCAR, OBBE_2, OBBE_3, OBBE_5, OBBE_3, OBBE_ONSTRING // actually unused... }; void CCamera::ProcessObbeCinemaCameraCar(void) { static int OldMode = -1; static int32 TimeForNext = 0; int i = 0; if(!bDidWeProcessAnyCinemaCam){ OldMode = -1; bSwitchedToObbeCam = true; } if(!bDidWeProcessAnyCinemaCam || IsItTimeForNewcam(SequenceOfCarCams[OldMode], TimeForNext)){ // This is very strange code... for(OldMode = (OldMode+1) % 14; !TryToStartNewCamMode(SequenceOfCarCams[OldMode]) && i <= 14; OldMode = (OldMode+1) % 14) i++; TimeForNext = CTimer::GetTimeInMilliseconds(); if(i >= 14){ OldMode = 14; TryToStartNewCamMode(SequenceOfCarCams[14]); } } m_iModeObbeCamIsInForCar = OldMode; bDidWeProcessAnyCinemaCam = true; } int32 SequenceOfHeliCams[6] = { OBBE_14, OBBE_15, OBBE_16, OBBE_17, OBBE_18, OBBE_19 }; void CCamera::ProcessObbeCinemaCameraHeli(void) { static int OldMode = -1; static int32 TimeForNext = 0; int i = 0; if(!bDidWeProcessAnyCinemaCam){ OldMode = -1; bSwitchedToObbeCam = true; } if(!bDidWeProcessAnyCinemaCam || IsItTimeForNewcam(SequenceOfHeliCams[OldMode], TimeForNext)){ // This is very strange code... for(OldMode = (OldMode+1) % 6; !TryToStartNewCamMode(SequenceOfCarCams[OldMode]) && i <= 6; OldMode = (OldMode+1) % 6) i++; if(i >= 6){ OldMode = 6; if(Cams[ActiveCam].Mode != CCam::MODE_CAM_ON_A_STRING){ TryToStartNewCamMode(OBBE_ONSTRING_HELI); TimeForNext = CTimer::GetTimeInMilliseconds(); } }else TimeForNext = CTimer::GetTimeInMilliseconds(); } m_iModeObbeCamIsInForCar = OldMode; bDidWeProcessAnyCinemaCam = true; } int32 SequenceOfPedCams[5] = { OBBE_9, OBBE_10, OBBE_11, OBBE_12, OBBE_13 }; void CCamera::ProcessObbeCinemaCameraPed(void) { // static bool bObbePedProcessed = false; // unused static int PedOldMode = -1; static int32 PedTimeForNext = 0; if(!bDidWeProcessAnyCinemaCam) PedOldMode = -1; if(!bDidWeProcessAnyCinemaCam || IsItTimeForNewcam(SequenceOfPedCams[PedOldMode], PedTimeForNext)){ for(PedOldMode = (PedOldMode+1) % 5; !TryToStartNewCamMode(SequenceOfPedCams[PedOldMode]); PedOldMode = (PedOldMode+1) % 5); PedTimeForNext = CTimer::GetTimeInMilliseconds(); } bDidWeProcessAnyCinemaCam = true; } void CCamera::DontProcessObbeCinemaCamera(void) { bDidWeProcessAnyCinemaCam = false; } #ifdef GTA_TRAIN void CCamera::LoadTrainCamNodes(char const *name) { CFileMgr::SetDir("data"); char token[16] = { 0 }; char filename[16] = { 0 }; uint8 *buf; ssize_t bufpos = 0; int field = 0; int tokpos = 0; char c; int i; ssize_t len; strcpy(filename, name); len = (int)strlen(filename); filename[len] = '.'; filename[len+1] = 'd'; filename[len+2] = 'a'; filename[len+3] = 't'; m_uiNumberOfTrainCamNodes = 0; buf = new uint8[20000]; len = CFileMgr::LoadFile(filename, buf, 20000, "r"); for(i = 0; i < MAX_NUM_OF_NODES; i++){ m_arrTrainCamNode[i].m_cvecPointToLookAt = CVector(0.0f, 0.0f, 0.0f); m_arrTrainCamNode[i].m_cvecMinPointInRange = CVector(0.0f, 0.0f, 0.0f); m_arrTrainCamNode[i].m_cvecMaxPointInRange = CVector(0.0f, 0.0f, 0.0f); m_arrTrainCamNode[i].m_fDesiredFOV = 0.0f; m_arrTrainCamNode[i].m_fNearClip = 0.0f; } while(bufpos <= len){ c = buf[bufpos]; switch(c){ case '-': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // case '10': case '11': case '12': case '13': // ahem... token[tokpos++] = c; bufpos++; break; case ',': case ';': // game has the code for this duplicated but we handle both under the same case switch((field+14)%14){ case 0: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecCamPosition.x = atof(token); break; case 1: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecCamPosition.y = atof(token); break; case 2: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecCamPosition.z = atof(token); break; case 3: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecPointToLookAt.x = atof(token); break; case 4: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecPointToLookAt.y = atof(token); break; case 5: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecPointToLookAt.z = atof(token); break; case 6: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMinPointInRange.x = atof(token); break; case 7: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMinPointInRange.y = atof(token); break; case 8: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMinPointInRange.z = atof(token); break; case 9: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMaxPointInRange.x = atof(token); break; case 10: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMaxPointInRange.y = atof(token); break; case 11: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_cvecMaxPointInRange.z = atof(token); break; case 12: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_fDesiredFOV = atof(token); break; case 13: m_arrTrainCamNode[m_uiNumberOfTrainCamNodes].m_fNearClip = atof(token); m_uiNumberOfTrainCamNodes++; break; } field++; bufpos++; memset(token, 0, sizeof(token)); tokpos = 0; break; default: bufpos++; break; } } delete[] buf; CFileMgr::SetDir(""); } void CCamera::Process_Train_Camera_Control(void) { bool found = false; CTrain *target = (CTrain*)pTargetEntity; m_bUseSpecialFovTrain = true; static bool OKtoGoBackToNodeCam = true; // only ever set to true uint32 i; if(target->m_nTrackId == TRACK_ELTRAIN && !m_bAboveGroundTrainNodesLoaded){ m_bAboveGroundTrainNodesLoaded = true; m_bBelowGroundTrainNodesLoaded = false; LoadTrainCamNodes("Train"); m_uiTimeLastChange = CTimer::GetTimeInMilliseconds(); OKtoGoBackToNodeCam = true; m_iCurrentTrainCamNode = 0; } if(target->m_nTrackId == TRACK_SUBWAY && !m_bBelowGroundTrainNodesLoaded){ m_bBelowGroundTrainNodesLoaded = true; m_bAboveGroundTrainNodesLoaded = false; LoadTrainCamNodes("Train2"); m_uiTimeLastChange = CTimer::GetTimeInMilliseconds(); OKtoGoBackToNodeCam = true; m_iCurrentTrainCamNode = 0; } m_bTargetJustBeenOnTrain = true; uint32 node = m_iCurrentTrainCamNode; for(i = 0; i < m_uiNumberOfTrainCamNodes && !found; i++){ if(target->IsWithinArea(m_arrTrainCamNode[node].m_cvecMinPointInRange.x, m_arrTrainCamNode[node].m_cvecMinPointInRange.y, m_arrTrainCamNode[node].m_cvecMinPointInRange.z, m_arrTrainCamNode[node].m_cvecMaxPointInRange.x, m_arrTrainCamNode[node].m_cvecMaxPointInRange.y, m_arrTrainCamNode[node].m_cvecMaxPointInRange.z)){ m_iCurrentTrainCamNode = node; found = true; } node++; if(node >= m_uiNumberOfTrainCamNodes) node = 0; } #ifdef FIX_BUGS // Not really a bug but be nice and respect the debug mode if(DebugCamMode){ TakeControl(target, DebugCamMode, JUMP_CUT, CAMCONTROL_SCRIPT); return; } #endif if(found){ SetWideScreenOn(); if(DotProduct(((CTrain*)pTargetEntity)->GetMoveSpeed(), pTargetEntity->GetForward()) < 0.001f){ TakeControl(FindPlayerPed(), CCam::MODE_FOLLOWPED, JUMP_CUT, CAMCONTROL_SCRIPT); if(target->Doors[0].IsFullyOpen()) SetWideScreenOff(); }else{ SetCamPositionForFixedMode(m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecCamPosition, CVector(0.0f, 0.0f, 0.0f)); if(m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecPointToLookAt.x == 999.0f && m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecPointToLookAt.y == 999.0f && m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecPointToLookAt.z == 999.0f) TakeControl(target, CCam::MODE_FIXED, JUMP_CUT, CAMCONTROL_SCRIPT); else TakeControlNoEntity(m_arrTrainCamNode[m_iCurrentTrainCamNode].m_cvecPointToLookAt, JUMP_CUT, CAMCONTROL_SCRIPT); RwCameraSetNearClipPlane(Scene.camera, m_arrTrainCamNode[m_iCurrentTrainCamNode].m_fNearClip); } }else{ if(DotProduct(((CTrain*)pTargetEntity)->GetMoveSpeed(), pTargetEntity->GetForward()) < 0.001f){ TakeControl(FindPlayerPed(), CCam::MODE_FOLLOWPED, JUMP_CUT, CAMCONTROL_SCRIPT); if(target->Doors[0].IsFullyOpen()) SetWideScreenOff(); } } } #endif void CCamera::LoadPathSplines(int file) { bool reading = true; char c, token[32] = { 0 }; int i, j, n; n = 0; DeleteCutSceneCamDataMemory(); for(i = 0; i < MAX_NUM_OF_SPLINETYPES; i++) m_arrPathArray[i].m_arr_PathData = new float[CCamPathSplines::MAXPATHLENGTH]; // Why is this gone? // for(i = 0; i < MAX_NUM_OF_SPLINETYPES; i++) // for(j = 0; j < CCamPathSplines::MAXPATHLENGTH; j++) // m_arrPathArray[i].m_arr_PathData[j] = 0.0f; m_bStartingSpline = false; i = 0; j = 0; while(reading){ CFileMgr::Read(file, &c, 1); switch(c){ case '\0': reading = false; break; case '+': case '-': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'e': case 'E': token[n++] = c; break; case ',': #ifdef FIX_BUGS if(i < MAX_NUM_OF_SPLINETYPES && j < CCamPathSplines::MAXPATHLENGTH) #endif m_arrPathArray[i].m_arr_PathData[j] = atof(token); j++; memset(token, 0, 32); n = 0; break; case ';': #ifdef FIX_BUGS if(i < MAX_NUM_OF_SPLINETYPES && j < CCamPathSplines::MAXPATHLENGTH) #endif m_arrPathArray[i].m_arr_PathData[j] = atof(token); i++; j = 0; if (i == MAX_NUM_OF_SPLINETYPES) reading = false; memset(token, 0, 32); n = 0; } } } void CCamera::DeleteCutSceneCamDataMemory(void) { int i; for(i = 0; i < MAX_NUM_OF_SPLINETYPES; i++) if(m_arrPathArray[i].m_arr_PathData){ delete[] m_arrPathArray[i].m_arr_PathData; m_arrPathArray[i].m_arr_PathData = nil; } } void CCamera::FinishCutscene(void) { SetPercentAlongCutScene(100.0f); m_fPositionAlongSpline = 1.0f; m_bcutsceneFinished = true; } uint32 CCamera::GetCutSceneFinishTime(void) { int cam = ActiveCam; if (Cams[cam].Mode == CCam::MODE_FLYBY) return Cams[cam].m_uiFinishTime; cam = (cam + 1) % 2; if (Cams[cam].Mode == CCam::MODE_FLYBY) return Cams[cam].m_uiFinishTime; return 0; } void CCamera::SetCamCutSceneOffSet(const CVector &pos) { m_vecCutSceneOffset = pos; }; void CCamera::SetPercentAlongCutScene(float percent) { if(Cams[ActiveCam].Mode == CCam::MODE_FLYBY) Cams[ActiveCam].m_fTimeElapsedFloat = percent/100.0f * Cams[ActiveCam].m_uiFinishTime; else if(Cams[(ActiveCam+1)%2].Mode == CCam::MODE_FLYBY) Cams[(ActiveCam+1)%2].m_fTimeElapsedFloat = percent/100.0f * Cams[(ActiveCam+1)%2].m_uiFinishTime; } void CCamera::SetParametersForScriptInterpolation(float stopMoving, float catchUp, int32 time) { m_fScriptPercentageInterToStopMoving = stopMoving * 0.01f; m_fScriptPercentageInterToCatchUp = catchUp * 0.01f; m_fScriptTimeForInterPolation = time; m_bScriptParametersSetForInterPol = true; } void CCamera::SetZoomValueFollowPedScript(int16 dist) { switch (dist) { case 0: m_fPedZoomValueScript = 0.25f; break; case 1: m_fPedZoomValueScript = 1.5f; break; case 2: m_fPedZoomValueScript = 2.9f; break; default: break; } m_bUseScriptZoomValuePed = true; } void CCamera::SetZoomValueCamStringScript(int16 dist) { if (Cams[ActiveCam].CamTargetEntity->IsVehicle()) { int vehApp = ((CVehicle*)Cams[ActiveCam].CamTargetEntity)->GetVehicleAppearance(); int vehArrPos = 0; GetArrPosForVehicleType(vehApp, vehArrPos); #ifdef FREE_CAM if (bFreeCam) { switch (dist) { case 0: m_fCarZoomValueScript = LCS_ZOOM_ONE_DISTANCE[vehArrPos]; break; case 1: m_fCarZoomValueScript = LCS_ZOOM_TWO_DISTANCE[vehArrPos]; break; case 2: m_fCarZoomValueScript = LCS_ZOOM_THREE_DISTANCE[vehArrPos]; break; default: break; } } else #endif { switch (dist) { case 0: m_fCarZoomValueScript = ZOOM_ONE_DISTANCE[vehArrPos]; break; case 1: m_fCarZoomValueScript = ZOOM_TWO_DISTANCE[vehArrPos]; break; case 2: m_fCarZoomValueScript = ZOOM_THREE_DISTANCE[vehArrPos]; break; default: break; } } m_bUseScriptZoomValueCar = true; } else { switch (dist) { case 0: m_fPedZoomValueScript = 0.25f; break; case 1: m_fPedZoomValueScript = 1.5f; break; case 2: m_fPedZoomValueScript = 2.9f; break; default: break; } m_bUseScriptZoomValuePed = true; } } void CCamera::SetNearClipScript(float clip) { m_fNearClipScript = clip; m_bUseNearClipScript = true; } void CCamera::ProcessFade(void) { if(m_bFading){ if(m_iFadingDirection == FADE_IN){ if(m_fTimeToFadeOut != 0.0f) m_fFLOATingFade -= CTimer::GetTimeStepInSeconds() * 255.0f / m_fTimeToFadeOut; else m_fFLOATingFade = 0.0f; if (m_fFLOATingFade <= 0.0f) { m_bFading = false; m_fFLOATingFade = 0.0f; } }else if(m_iFadingDirection == FADE_OUT){ if(m_fTimeToFadeOut != 0.0f) m_fFLOATingFade += CTimer::GetTimeStepInSeconds() * 255.0f / m_fTimeToFadeOut; else m_fFLOATingFade = 255.0f; if (m_fFLOATingFade >= 255.0f) { m_bFading = false; m_fFLOATingFade = 255.0f; } } CDraw::FadeValue = m_fFLOATingFade; } } void CCamera::ProcessMusicFade(void) { if(m_bMusicFading){ if(m_iMusicFadingDirection == FADE_IN){ if(m_fTimeToFadeMusic == 0.0f) m_fFLOATingFadeMusic = 0.0f; else m_fFLOATingFadeMusic -= 255.0f*CTimer::GetTimeStepInSeconds()/m_fTimeToFadeMusic; if(m_fFLOATingFadeMusic <= 0.0f){ m_bMusicFading = false; m_fFLOATingFadeMusic = 0.0f; } }else if(m_iMusicFadingDirection == FADE_OUT){ if(m_fTimeToFadeMusic == 0.0f) m_fFLOATingFadeMusic = 255.0f; else m_fFLOATingFadeMusic += 255.0f*CTimer::GetTimeStepInSeconds()/m_fTimeToFadeMusic; if(m_fFLOATingFadeMusic >= 255.0f){ m_bMusicFading = false; m_fFLOATingFadeMusic = 255.0f; } } DMAudio.SetEffectsFadeVol(127 - m_fFLOATingFadeMusic/255.0f * 127); DMAudio.SetMusicFadeVol(127 - m_fFLOATingFadeMusic/255.0f * 127); } } void CCamera::Fade(float timeout, int16 direction) { m_bFading = true; m_iFadingDirection = direction; m_fTimeToFadeOut = timeout; m_uiFadeTimeStarted = CTimer::GetTimeInMilliseconds(); if(!m_bIgnoreFadingStuffForMusic){ m_bMusicFading = true; m_iMusicFadingDirection = direction; m_fTimeToFadeMusic = timeout; m_uiFadeTimeStartedMusic = CTimer::GetTimeInMilliseconds(); } } void CCamera::SetFadeColour(uint8 r, uint8 g, uint8 b) { m_FadeTargetIsSplashScreen = r == 2 && g == 2 && b == 2; CDraw::FadeRed = r; CDraw::FadeGreen = g; CDraw::FadeBlue = b; } bool CCamera::GetFading(void) { return m_bFading; } int CCamera::GetFadingDirection(void) { if(m_bFading) return m_iFadingDirection == FADE_IN ? FADE_IN : FADE_OUT; else return FADE_NONE; } int CCamera::GetScreenFadeStatus(void) { if(m_fFLOATingFade == 0.0f) return FADE_0; if(m_fFLOATingFade == 255.0f) return FADE_2; return FADE_1; } void CCamera::RenderMotionBlur(void) { if(m_BlurType == 0) return; CMBlur::MotionBlurRender(m_pRwCamera, m_BlurRed, m_BlurGreen, m_BlurBlue, m_motionBlur, m_BlurType, m_imotionBlurAddAlpha); } void CCamera::SetMotionBlur(int r, int g, int b, int a, int type) { m_BlurRed = r; m_BlurGreen = g; m_BlurBlue = b; m_motionBlur = a; m_BlurType = type; } void CCamera::SetMotionBlurAlpha(int a) { m_imotionBlurAddAlpha = a; } int CCamera::GetLookDirection(void) { if(Cams[ActiveCam].Mode == CCam::MODE_CAM_ON_A_STRING || Cams[ActiveCam].Mode == CCam::MODE_1STPERSON || Cams[ActiveCam].Mode == CCam::MODE_BEHINDBOAT || Cams[ActiveCam].Mode == CCam::MODE_FOLLOWPED) return Cams[ActiveCam].DirectionWasLooking; return LOOKING_FORWARD; } bool CCamera::GetLookingForwardFirstPerson(void) { return Cams[ActiveCam].Mode == CCam::MODE_1STPERSON && Cams[ActiveCam].DirectionWasLooking == LOOKING_FORWARD; } bool CCamera::GetLookingLRBFirstPerson(void) { return Cams[ActiveCam].Mode == CCam::MODE_1STPERSON && Cams[ActiveCam].DirectionWasLooking != LOOKING_FORWARD; } void CCamera::SetCameraDirectlyBehindForFollowPed_CamOnAString(void) { m_bCamDirectlyBehind = true; CPlayerPed *player = FindPlayerPed(); if (player) m_PedOrientForBehindOrInFront = CGeneral::GetATanOfXY(player->GetForward().x, player->GetForward().y); } void CCamera::SetCameraDirectlyInFrontForFollowPed_CamOnAString(void) { m_bCamDirectlyInFront = true; CPlayerPed *player = FindPlayerPed(); if (player) m_PedOrientForBehindOrInFront = CGeneral::GetATanOfXY(player->GetForward().x, player->GetForward().y); } void CCamera::SetNewPlayerWeaponMode(int16 mode, int16 minZoom, int16 maxZoom) { SetMotionBlur(CTimeCycle::GetBlurRed(), CTimeCycle::GetBlurGreen(), CTimeCycle::GetBlurBlue(), m_motionBlur, MOTION_BLUR_LIGHT_SCENE); PlayerWeaponMode.Mode = mode; PlayerWeaponMode.MaxZoom = maxZoom; PlayerWeaponMode.MinZoom = minZoom; PlayerWeaponMode.Duration = 0.0f; } void CCamera::ClearPlayerWeaponMode(void) { SetMotionBlur(CTimeCycle::GetBlurRed(), CTimeCycle::GetBlurGreen(), CTimeCycle::GetBlurBlue(), m_motionBlur, MOTION_BLUR_LIGHT_SCENE); PlayerWeaponMode.Mode = 0; PlayerWeaponMode.MaxZoom = 1; PlayerWeaponMode.MinZoom = -1; PlayerWeaponMode.Duration = 0.0f; } void CCamera::UpdateAimingCoors(CVector const &coors) { m_cvecAimingTargetCoors = coors; } bool CCamera::Find3rdPersonCamTargetVector(float dist, CVector pos, CVector &source, CVector &target) { if(CPad::GetPad(0)->GetLookBehindForPed()){ source = pos; target = dist*Cams[ActiveCam].CamTargetEntity->GetForward() + source; return false; }else{ float angleX = DEGTORAD((m_f3rdPersonCHairMultX-0.5f) * 1.8f * 0.5f * Cams[ActiveCam].FOV * CDraw::GetAspectRatio()); float angleY = DEGTORAD((0.5f-m_f3rdPersonCHairMultY) * 1.8f * 0.5f * Cams[ActiveCam].FOV); source = Cams[ActiveCam].Source; target = Cams[ActiveCam].Front; target += Cams[ActiveCam].Up * Tan(angleY); target += CrossProduct(Cams[ActiveCam].Front, Cams[ActiveCam].Up) * Tan(angleX); target.Normalise(); source += DotProduct(pos - source, target)*target; target = dist*target + source; return true; } } float CCamera::Find3rdPersonQuickAimPitch(void) { float clampedFrontZ = clamp(Cams[ActiveCam].Front.z, -1.0f, 1.0f); float rot = Asin(clampedFrontZ); return -(DEGTORAD(((0.5f - m_f3rdPersonCHairMultY) * 1.8f * 0.5f * Cams[ActiveCam].FOV)) + rot); } bool CCamera::Using1stPersonWeaponMode(void) { switch(PlayerWeaponMode.Mode) case CCam::MODE_SNIPER: case CCam::MODE_M16_1STPERSON: case CCam::MODE_ROCKETLAUNCHER: case CCam::MODE_HELICANNON_1STPERSON: case CCam::MODE_CAMERA: return true; return false; } void CCamera::SetRwCamera(RwCamera *cam) { m_pRwCamera = cam; m_viewMatrix.Attach(RwCameraGetViewMatrix(m_pRwCamera), false); CMBlur::MotionBlurOpen(m_pRwCamera); } void CCamera::CalculateDerivedValues(void) { m_cameraMatrix = Invert(m_matrix); float hfov = DEGTORAD(CDraw::GetScaledFOV()/2.0f); float c = Cos(hfov); float s = Sin(hfov); // right plane m_vecFrustumNormals[0] = CVector(c, -s, 0.0f); // left plane m_vecFrustumNormals[1] = CVector(-c, -s, 0.0f); CDraw::CalculateAspectRatio(); c /= SCREEN_ASPECT_RATIO; s /= SCREEN_ASPECT_RATIO; // bottom plane m_vecFrustumNormals[2] = CVector(0.0f, -s, -c); // top plane m_vecFrustumNormals[3] = CVector(0.0f, -s, c); if(GetForward().x == 0.0f && GetForward().y == 0.0f) GetForward().x = 0.0001f; else Orientation = Atan2(GetForward().x, GetForward().y); CamFrontXNorm = GetForward().x; CamFrontYNorm = GetForward().y; float l = Sqrt(SQR(CamFrontXNorm) + SQR(CamFrontYNorm)); if(l == 0.0f) CamFrontXNorm = 1.0f; else{ CamFrontXNorm /= l; CamFrontYNorm /= l; } } bool CCamera::IsPointVisible(const CVector ¢er, const CMatrix *mat) { #ifdef GTA_PS2 CVuVector c; TransformPoint(c, *mat, center); #else CVector c = center; #ifdef FIX_BUGS c = *mat * center; #else RwV3dTransformPoints(&c, &c, 1, (RwMatrix*)mat); #endif #endif if(c.y < CDraw::GetNearClipZ()) return false; if(c.y > CDraw::GetFarClipZ()) return false; if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > 0.0f) return false; if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > 0.0f) return false; if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > 0.0f) return false; if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > 0.0f) return false; return true; } bool CCamera::IsSphereVisible(const CVector ¢er, float radius, const CMatrix *mat) { #ifdef GTA_PS2 CVuVector c; TransformPoint(c, *mat, center); #else CVector c = center; #ifdef FIX_BUGS c = *mat * center; #else RwV3dTransformPoints(&c, &c, 1, (RwMatrix*)mat); #endif #endif if(c.y + radius < CDraw::GetNearClipZ()) return false; if(c.y - radius > CDraw::GetFarClipZ()) return false; if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > radius) return false; if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > radius) return false; if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > radius) return false; if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > radius) return false; return true; } bool CCamera::IsSphereVisible(const CVector ¢er, float radius) { CMatrix mat = m_cameraMatrix; return IsSphereVisible(center, radius, &mat); } bool #ifdef GTA_PS2 CCamera::IsBoxVisible(CVuVector *box, const CMatrix *mat) #else CCamera::IsBoxVisible(CVector *box, const CMatrix *mat) #endif { int i; int frustumTests[6] = { 0 }; #ifdef GTA_PS2 TransformPoints(box, 8, *mat, box); #else #ifdef FIX_BUGS for (i = 0; i < 8; i++) box[i] = *mat * box[i]; #else RwV3dTransformPoints(box, box, 8, (RwMatrix*)mat); #endif #endif for(i = 0; i < 8; i++){ if(box[i].y < CDraw::GetNearClipZ()) frustumTests[0]++; if(box[i].y > CDraw::GetFarClipZ()) frustumTests[1]++; if(box[i].x*m_vecFrustumNormals[0].x + box[i].y*m_vecFrustumNormals[0].y > 0.0f) frustumTests[2]++; if(box[i].x*m_vecFrustumNormals[1].x + box[i].y*m_vecFrustumNormals[1].y > 0.0f) frustumTests[3]++; // Why not test z? // if(box[i].y*m_vecFrustumNormals[2].y + box[i].z*m_vecFrustumNormals[2].z > 0.0f) frustumTests[4]++; // if(box[i].y*m_vecFrustumNormals[3].y + box[i].z*m_vecFrustumNormals[3].z > 0.0f) frustumTests[5]++; } for(i = 0; i < 6; i++) if(frustumTests[i] == 8) return false; // Box is completely outside of one plane return true; } CCamPathSplines::CCamPathSplines(void) { m_arr_PathData = nil; } ================================================ FILE: src/core/Camera.h ================================================ #pragma once #include "Placeable.h" class CEntity; class CPed; class CAutomobile; class CGarage; extern int16 DebugCamMode; enum { NUMBER_OF_VECTORS_FOR_AVERAGE = 2, MAX_NUM_OF_SPLINETYPES = 4, MAX_NUM_OF_NODES = 800 // for trains }; #define DEFAULT_NEAR (0.9f) enum { CAM_ZOOM_1STPRS, CAM_ZOOM_1, CAM_ZOOM_2, CAM_ZOOM_3, CAM_ZOOM_TOPDOWN, CAM_ZOOM_CINEMATIC, }; const float DefaultFOV = 70.0f; // beta: 80.0f class CCam { public: enum { MODE_NONE = 0, MODE_TOPDOWN, MODE_GTACLASSIC, MODE_BEHINDCAR, MODE_FOLLOWPED, MODE_AIMING, MODE_DEBUG, MODE_SNIPER, MODE_ROCKETLAUNCHER, MODE_MODELVIEW, MODE_BILL, MODE_SYPHON, MODE_CIRCLE, MODE_CHEESYZOOM, MODE_WHEELCAM, MODE_FIXED, MODE_1STPERSON, MODE_FLYBY, MODE_CAM_ON_A_STRING, MODE_REACTION, MODE_FOLLOW_PED_WITH_BIND, MODE_CHRIS, MODE_BEHINDBOAT, MODE_PLAYER_FALLEN_WATER, MODE_CAM_ON_TRAIN_ROOF, MODE_CAM_RUNNING_SIDE_TRAIN, MODE_BLOOD_ON_THE_TRACKS, MODE_IM_THE_PASSENGER_WOOWOO, MODE_SYPHON_CRIM_IN_FRONT, MODE_PED_DEAD_BABY, MODE_PILLOWS_PAPS, MODE_LOOK_AT_CARS, MODE_ARRESTCAM_ONE, MODE_ARRESTCAM_TWO, MODE_M16_1STPERSON, MODE_SPECIAL_FIXED_FOR_SYPHON, MODE_FIGHT_CAM, MODE_TOP_DOWN_PED, MODE_LIGHTHOUSE, MODE_SNIPER_RUNABOUT, MODE_ROCKETLAUNCHER_RUNABOUT, MODE_1STPERSON_RUNABOUT, MODE_M16_1STPERSON_RUNABOUT, MODE_FIGHT_CAM_RUNABOUT, MODE_EDITOR, MODE_HELICANNON_1STPERSON, MODE_CAMERA, }; bool bBelowMinDist; //used for follow ped mode bool bBehindPlayerDesired; //used for follow ped mode bool m_bCamLookingAtVector; bool m_bCollisionChecksOn; bool m_bFixingBeta; //used for camera on a string bool m_bTheHeightFixerVehicleIsATrain; bool LookBehindCamWasInFront; bool LookingBehind; bool LookingLeft; bool LookingRight; bool ResetStatics; //for interpolation type stuff to work bool Rotating; int16 Mode; // CameraMode uint32 m_uiFinishTime; int m_iDoCollisionChecksOnFrameNum; int m_iDoCollisionCheckEveryNumOfFrames; int m_iFrameNumWereAt; int m_iRunningVectorArrayPos; int m_iRunningVectorCounter; int DirectionWasLooking; float f_max_role_angle; //=DEGTORAD(5.0f); float f_Roll; //used for adding a slight roll to the camera in the float f_rollSpeed; float m_fSyphonModeTargetZOffSet; float m_fAmountFractionObscured; float m_fAlphaSpeedOverOneFrame; float m_fBetaSpeedOverOneFrame; float m_fBufferedTargetBeta; float m_fBufferedTargetOrientation; float m_fBufferedTargetOrientationSpeed; float m_fCamBufferedHeight; float m_fCamBufferedHeightSpeed; float m_fCloseInPedHeightOffset; float m_fCloseInPedHeightOffsetSpeed; float m_fCloseInCarHeightOffset; float m_fCloseInCarHeightOffsetSpeed; float m_fDimensionOfHighestNearCar; float m_fDistanceBeforeChanges; float m_fFovSpeedOverOneFrame; float m_fMinDistAwayFromCamWhenInterPolating; float m_fPedBetweenCameraHeightOffset; float m_fPlayerInFrontSyphonAngleOffSet; float m_fRadiusForDead; float m_fRealGroundDist; //used for follow ped mode float m_fTargetBeta; float m_fTimeElapsedFloat; float m_fTilt; float m_fTiltSpeed; float m_fTransitionBeta; float m_fTrueBeta; float m_fTrueAlpha; float m_fInitialPlayerOrientation; //used for first person float Alpha; float AlphaSpeed; float FOV; float FOVSpeed; float Beta; float BetaSpeed; float Distance; float DistanceSpeed; float CA_MIN_DISTANCE; float CA_MAX_DISTANCE; float SpeedVar; float m_fTargetZoomGroundOne; float m_fTargetZoomGroundTwo; float m_fTargetZoomGroundThree; float m_fTargetZoomOneZExtra; float m_fTargetZoomTwoZExtra; float m_fTargetZoomThreeZExtra; float m_fTargetZoomZCloseIn; float m_fMinRealGroundDist; float m_fTargetCloseInDist; CVector m_cvecSourceSpeedOverOneFrame; CVector m_cvecTargetSpeedOverOneFrame; CVector m_cvecUpOverOneFrame; CVector m_cvecTargetCoorsForFudgeInter; CVector m_cvecCamFixedModeVector; CVector m_cvecCamFixedModeSource; CVector m_cvecCamFixedModeUpOffSet; CVector m_vecLastAboveWaterCamPosition; //helper for when the player has gone under the water CVector m_vecBufferedPlayerBodyOffset; // The three vectors that determine this camera for this frame CVector Front; // Direction of looking in CVector Source; // Coors in world space CVector SourceBeforeLookBehind; CVector Up; // Just that CVector m_arrPreviousVectors[NUMBER_OF_VECTORS_FOR_AVERAGE]; // used to average stuff CEntity *CamTargetEntity; float m_fCameraDistance; float m_fIdealAlpha; float m_fPlayerVelocity; CAutomobile *m_pLastCarEntered; // So interpolation works CPed *m_pLastPedLookedAt;// So interpolation works bool m_bFirstPersonRunAboutActive; CCam(void) { Init(); } void Init(void); void Process(void); void ProcessSpecialHeightRoutines(void); void GetVectorsReadyForRW(void); CVector DoAverageOnVector(const CVector &vec); void WorkOutCamHeight(const CVector &TargetCoors, float TargetOrientation, float TargetHeight); bool RotCamIfInFrontCar(CVector &TargetCoors, float TargetOrientation); void Cam_On_A_String_Unobscured(const CVector &TargetCoors, float BaseDist); void FixCamWhenObscuredByVehicle(const CVector &TargetCoors); bool GetBoatLook_L_R_HeightOffset(float &Offset); void LookBehind(void); void LookLeft(void); void LookRight(void); void ClipIfPedInFrontOfPlayer(void); void KeepTrackOfTheSpeed(const CVector &source, const CVector &target, const CVector &up, const float &alpha, const float &beta, const float &fov); bool Using3rdPersonMouseCam(void); bool GetWeaponFirstPersonOn(void); bool IsTargetInWater(const CVector &CamCoors); void AvoidWallsTopDownPed(const CVector &TargetCoors, const CVector &Offset, float *Adjuster, float *AdjusterSpeed, float yDistLimit); void PrintMode(void); void Process_Debug(const CVector&, float, float, float); #ifdef GTA_SCENE_EDIT void Process_Editor(const CVector&, float, float, float); #endif void Process_ModelView(const CVector &CameraTarget, float, float, float); void Process_FollowPed(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_FollowPedWithMouse(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_BehindCar(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_Cam_On_A_String(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_TopDown(const CVector &CameraTarget, float TargetOrientation, float SpeedVar, float TargetSpeedVar); void Process_TopDownPed(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_Rocket(const CVector &CameraTarget, float, float, float); void Process_M16_1stPerson(const CVector &CameraTarget, float, float, float); void Process_1stPerson(const CVector &CameraTarget, float, float, float); void Process_1rstPersonPedOnPC(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_Sniper(const CVector &CameraTarget, float, float, float); void Process_Syphon(const CVector &CameraTarget, float, float, float); void Process_Syphon_Crim_In_Front(const CVector &CameraTarget, float, float, float); void Process_BehindBoat(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_Fight_Cam(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_FlyBy(const CVector&, float, float, float); bool Process_WheelCam(const CVector&, float, float, float); void Process_Fixed(const CVector &CameraTarget, float, float, float); void Process_Player_Fallen_Water(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_SpecialFixedForSyphon(const CVector &CameraTarget, float, float, float); void Process_LightHouse(const CVector &CameraTarget, float, float, float); void ProcessPedsDeadBaby(void); bool ProcessArrestCamOne(void); bool ProcessArrestCamTwo(void); bool GetLookAlongGroundPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut); bool GetLookFromLampPostPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut); bool GetLookOverShoulderPos(CEntity *Target, CPed *Cop, CVector &TargetCoors, CVector &SourceOut); // custom stuff void Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrientation, float, float); void Process_FollowCar_SA(const CVector &CameraTarget, float TargetOrientation, float, float); }; class CCamPathSplines { public: enum {MAXPATHLENGTH=800}; // float m_arr_PathData[MAXPATHLENGTH]; float *m_arr_PathData; CCamPathSplines(void); }; struct CTrainCamNode { CVector m_cvecCamPosition; CVector m_cvecPointToLookAt; CVector m_cvecMinPointInRange; CVector m_cvecMaxPointInRange; float m_fDesiredFOV; float m_fNearClip; }; struct CQueuedMode { int16 Mode; float Duration; int16 MinZoom; int16 MaxZoom; }; enum { LOOKING_BEHIND, LOOKING_LEFT, LOOKING_RIGHT, LOOKING_FORWARD, }; enum { // TODO: find better names FADE_0, // faded in FADE_1, // mid fade FADE_2, // faded out // Direction FADE_OUT = 0, FADE_IN, FADE_NONE }; enum { MOTION_BLUR_NONE = 0, MOTION_BLUR_SNIPER, MOTION_BLUR_LIGHT_SCENE, MOTION_BLUR_SECURITY_CAM, MOTION_BLUR_CUT_SCENE, MOTION_BLUR_INTRO, MOTION_BLUR_INTRO2, MOTION_BLUR_SNIPER_ZOOM, MOTION_BLUR_INTRO3, MOTION_BLUR_INTRO4, }; enum { NONE = 0, INTERPOLATION, JUMP_CUT }; enum { CAMCONTROL_GAME, CAMCONTROL_SCRIPT, CAMCONTROL_OBBE }; class CCamera : public CPlaceable { public: bool m_bAboveGroundTrainNodesLoaded; bool m_bBelowGroundTrainNodesLoaded; bool m_bCamDirectlyBehind; bool m_bCamDirectlyInFront; bool m_bCameraJustRestored; bool m_bcutsceneFinished; bool m_bCullZoneChecksOn; bool m_bFirstPersonBeingUsed; bool m_bJustJumpedOutOf1stPersonBecauseOfTarget; bool m_bIdleOn; bool m_bInATunnelAndABigVehicle; bool m_bInitialNodeFound; bool m_bInitialNoNodeStaticsSet; bool m_bIgnoreFadingStuffForMusic; bool m_bPlayerIsInGarage; bool m_bPlayerWasOnBike; bool m_bJustCameOutOfGarage; bool m_bJustInitalised; bool m_bJust_Switched; bool m_bLookingAtPlayer; bool m_bLookingAtVector; bool m_bMoveCamToAvoidGeom; bool m_bObbeCinematicPedCamOn; bool m_bObbeCinematicCarCamOn; bool m_bRestoreByJumpCut; bool m_bUseNearClipScript; bool m_bStartInterScript; bool m_bStartingSpline; bool m_bTargetJustBeenOnTrain; bool m_bTargetJustCameOffTrain; bool m_bUseSpecialFovTrain; bool m_bUseTransitionBeta; bool m_bUseScriptZoomValuePed; bool m_bUseScriptZoomValueCar; bool m_bWaitForInterpolToFinish; bool m_bItsOkToLookJustAtThePlayer; bool m_bWantsToSwitchWidescreenOff; bool m_WideScreenOn; bool m_1rstPersonRunCloseToAWall; bool m_bHeadBob; bool m_bVehicleSuspenHigh; bool m_bEnable1rstPersonCamCntrlsScript; bool m_bAllow1rstPersonWeaponsCamera; bool m_bFailedCullZoneTestPreviously; bool m_FadeTargetIsSplashScreen; bool WorldViewerBeingUsed; uint8 ActiveCam; uint32 m_uiCamShakeStart; uint32 m_uiFirstPersonCamLastInputTime; uint32 m_uiLongestTimeInMill; uint32 m_uiNumberOfTrainCamNodes; uint8 m_uiTransitionJUSTStarted; uint8 m_uiTransitionState; // 0:one mode 1:transition uint32 m_uiTimeLastChange; uint32 m_uiTimeWeLeftIdle_StillNoInput; uint32 m_uiTimeWeEnteredIdle; uint32 m_uiTimeTransitionStart; uint32 m_uiTransitionDuration; uint32 m_uiTransitionDurationTargetCoors; int m_BlurBlue; int m_BlurGreen; int m_BlurRed; int m_BlurType; int m_iWorkOutSpeedThisNumFrames; int m_iNumFramesSoFar; int m_iCurrentTrainCamNode; int m_motionBlur; int m_imotionBlurAddAlpha; int m_iCheckCullZoneThisNumFrames; int m_iZoneCullFrameNumWereAt; int WhoIsInControlOfTheCamera; float CamFrontXNorm; float CamFrontYNorm; #ifdef FIX_BUGS int32 CarZoomIndicator; #else float CarZoomIndicator; #endif float CarZoomValue; float CarZoomValueSmooth; float DistanceToWater; float FOVDuringInter; float LODDistMultiplier; float GenerationDistMultiplier; float m_fAlphaSpeedAtStartInter; float m_fAlphaWhenInterPol; float m_fAlphaDuringInterPol; float m_fBetaDuringInterPol; float m_fBetaSpeedAtStartInter; float m_fBetaWhenInterPol; float m_fFOVWhenInterPol; float m_fFOVSpeedAtStartInter; float m_fStartingBetaForInterPol; float m_fStartingAlphaForInterPol; float m_PedOrientForBehindOrInFront; float m_CameraAverageSpeed; float m_CameraSpeedSoFar; float m_fCamShakeForce; float m_fCarZoomValueScript; float m_fFovForTrain; float m_fFOV_Wide_Screen; float m_fNearClipScript; float m_fOldBetaDiff; float m_fPedZoomValue; float m_fPedZoomValueScript; float m_fPedZoomValueSmooth; float m_fPositionAlongSpline; float m_ScreenReductionPercentage; float m_ScreenReductionSpeed; float m_AlphaForPlayerAnim1rstPerson; float Orientation; #ifdef FIX_BUGS int32 PedZoomIndicator; #else float PedZoomIndicator; #endif float PlayerExhaustion; float SoundDistUp; float SoundDistUpAsRead; float SoundDistUpAsReadOld; float m_fAvoidTheGeometryProbsTimer; int16 m_nAvoidTheGeometryProbsDirn; float m_fWideScreenReductionAmount; float m_fStartingFOVForInterPol; static float m_fMouseAccelHorzntl;// acceleration multiplier for 1st person controls static float m_fMouseAccelVertical;// acceleration multiplier for 1st person controls static float m_f3rdPersonCHairMultX; static float m_f3rdPersonCHairMultY; CCam Cams[3]; CGarage *pToGarageWeAreIn; CGarage *pToGarageWeAreInForHackAvoidFirstPerson; CQueuedMode m_PlayerMode; CQueuedMode PlayerWeaponMode; CVector m_PreviousCameraPosition; CVector m_RealPreviousCameraPosition; CVector m_cvecAimingTargetCoors; CVector m_vecFixedModeVector; CVector m_vecFixedModeSource; CVector m_vecFixedModeUpOffSet; CVector m_vecCutSceneOffset; CVector m_cvecStartingSourceForInterPol; CVector m_cvecStartingTargetForInterPol; CVector m_cvecStartingUpForInterPol; CVector m_cvecSourceSpeedAtStartInter; CVector m_cvecTargetSpeedAtStartInter; CVector m_cvecUpSpeedAtStartInter; CVector m_vecSourceWhenInterPol; CVector m_vecTargetWhenInterPol; CVector m_vecUpWhenInterPol; CVector m_vecClearGeometryVec; CVector m_vecGameCamPos; CVector SourceDuringInter; CVector TargetDuringInter; CVector UpDuringInter; RwCamera *m_pRwCamera; CEntity *pTargetEntity; CCamPathSplines m_arrPathArray[MAX_NUM_OF_SPLINETYPES]; #ifdef GTA_TRAIN CTrainCamNode m_arrTrainCamNode[MAX_NUM_OF_NODES]; #endif CMatrix m_cameraMatrix; bool m_bGarageFixedCamPositionSet; bool m_vecDoingSpecialInterPolation; bool m_bScriptParametersSetForInterPol; bool m_bFading; bool m_bMusicFading; CMatrix m_viewMatrix; CVector m_vecFrustumNormals[4]; CVector m_vecOldSourceForInter; CVector m_vecOldFrontForInter; CVector m_vecOldUpForInter; float m_vecOldFOVForInter; float m_fFLOATingFade; float m_fFLOATingFadeMusic; float m_fTimeToFadeOut; float m_fTimeToFadeMusic; float m_fFractionInterToStopMoving; float m_fFractionInterToStopCatchUp; float m_fFractionInterToStopMovingTarget; float m_fFractionInterToStopCatchUpTarget; float m_fGaitSwayBuffer; float m_fScriptPercentageInterToStopMoving; float m_fScriptPercentageInterToCatchUp; uint32 m_fScriptTimeForInterPolation; int16 m_iFadingDirection; int m_iModeObbeCamIsInForCar; int16 m_iModeToGoTo; int16 m_iMusicFadingDirection; int16 m_iTypeOfSwitch; uint32 m_uiFadeTimeStarted; uint32 m_uiFadeTimeStartedMusic; static bool m_bUseMouse3rdPerson; #ifdef FREE_CAM static bool bFreeCam; #endif // High level and misc CCamera(void); void Init(void); void Process(void); void CamControl(void); void UpdateTargetEntity(void); void UpdateSoundDistances(void); void InitialiseCameraForDebugMode(void); void CamShake(float strength, float x, float y, float z); bool Get_Just_Switched_Status() { return m_bJust_Switched; } void AvoidTheGeometry(const CVector &Source, const CVector &TargetPos, CVector &NewSource, float FOV); void GetArrPosForVehicleType(int apperance, int &index); void GetScreenRect(CRect &rect); // Who's in control void TakeControl(CEntity *target, int16 mode, int16 typeOfSwitch, int32 controller); void TakeControlNoEntity(const CVector &position, int16 typeOfSwitch, int32 controller); void TakeControlWithSpline(int16 typeOfSwitch); void Restore(void); void RestoreWithJumpCut(void); void SetCamPositionForFixedMode(const CVector &Source, const CVector &UppOffSet); // Transition void StartTransition(int16 mode); void StartTransitionWhenNotFinishedInter(int16 mode); void StoreValuesDuringInterPol(CVector &source, CVector &target, CVector &up, float &FOV); // Widescreen borders void SetWideScreenOn(void); void SetWideScreenOff(void); void ProcessWideScreenOn(void); void DrawBordersForWideScreen(void); // Obbe's cam bool IsItTimeForNewcam(int32 obbeMode, int32 time); bool TryToStartNewCamMode(int32 obbeMode); void DontProcessObbeCinemaCamera(void); void ProcessObbeCinemaCameraCar(void); void ProcessObbeCinemaCameraHeli(void); void ProcessObbeCinemaCameraPed(void); // Train void LoadTrainCamNodes(char const *name); void Process_Train_Camera_Control(void); // Script void LoadPathSplines(int file); void DeleteCutSceneCamDataMemory(void); void FinishCutscene(void); float GetPositionAlongSpline(void) { return m_fPositionAlongSpline; } uint32 GetCutSceneFinishTime(void); void SetCamCutSceneOffSet(const CVector &pos); void SetPercentAlongCutScene(float percent); void SetParametersForScriptInterpolation(float stopMoving, float catchUp, int32 time); void SetZoomValueFollowPedScript(int16 dist); void SetZoomValueCamStringScript(int16 dist); void SetNearClipScript(float); // Fading void ProcessFade(void); void ProcessMusicFade(void); void Fade(float timeout, int16 direction); void SetFadeColour(uint8 r, uint8 g, uint8 b); bool GetFading(void); int GetFadingDirection(void); int GetScreenFadeStatus(void); // Motion blur void RenderMotionBlur(void); void SetMotionBlur(int r, int g, int b, int a, int type); void SetMotionBlurAlpha(int a); // Player looking and aiming int GetLookDirection(void); bool GetLookingForwardFirstPerson(void); bool GetLookingLRBFirstPerson(void); void SetCameraDirectlyInFrontForFollowPed_CamOnAString(void); void SetCameraDirectlyBehindForFollowPed_CamOnAString(void); void SetNewPlayerWeaponMode(int16 mode, int16 minZoom, int16 maxZoom); void ClearPlayerWeaponMode(void); void UpdateAimingCoors(CVector const &coors); bool Find3rdPersonCamTargetVector(float dist, CVector pos, CVector &source, CVector &target); float Find3rdPersonQuickAimPitch(void); bool Using1stPersonWeaponMode(void); // Physical camera void SetRwCamera(RwCamera *cam); const CMatrix& GetCameraMatrix(void) { return m_cameraMatrix; } CVector &GetGameCamPosition(void) { return m_vecGameCamPos; } void CalculateDerivedValues(void); bool IsPointVisible(const CVector ¢er, const CMatrix *mat); bool IsSphereVisible(const CVector ¢er, float radius, const CMatrix *mat); bool IsSphereVisible(const CVector ¢er, float radius); #ifdef GTA_PS2 bool IsBoxVisible(CVuVector *box, const CMatrix *mat); #else bool IsBoxVisible(CVector *box, const CMatrix *mat); #endif }; VALIDATE_SIZE(CCamera, 0xE9D8); extern CCamera TheCamera; void CamShakeNoPos(CCamera*, float); void MakeAngleLessThan180(float &Angle); void WellBufferMe(float Target, float *CurrentValue, float *CurrentSpeed, float MaxSpeed, float Acceleration, bool IsAngle); ================================================ FILE: src/core/CdStream.cpp ================================================ #ifdef _WIN32 #define WITHWINDOWS #include "common.h" #include "CdStream.h" #include "rwcore.h" #include "RwHelper.h" #include "MemoryMgr.h" struct CdReadInfo { uint32 nSectorOffset; uint32 nSectorsToRead; void *pBuffer; char field_C; bool bLocked; bool bReading; int32 nStatus; HANDLE pDoneSemaphore; // used for CdStreamSync HANDLE hFile; OVERLAPPED Overlapped; }; VALIDATE_SIZE(CdReadInfo, 0x30); char gCdImageNames[MAX_CDIMAGES+1][64]; int32 gNumImages; int32 gNumChannels; HANDLE gImgFiles[MAX_CDIMAGES]; HANDLE _gCdStreamThread; HANDLE gCdStreamSema; // released when we have new thing to read(so channel is set) DWORD _gCdStreamThreadId; CdReadInfo *gpReadInfo; Queue gChannelRequestQ; int32 lastPosnRead; BOOL _gbCdStreamOverlapped; BOOL _gbCdStreamAsync; DWORD _gdwCdStreamFlags; DWORD WINAPI CdStreamThread(LPVOID lpThreadParameter); void CdStreamInitThread(void) { SetLastError(0); if ( gNumChannels > 0 ) { for ( int32 i = 0; i < gNumChannels; i++ ) { gpReadInfo[i].pDoneSemaphore = CreateSemaphore(nil, 0, 2, nil); if ( gpReadInfo[i].pDoneSemaphore == nil ) { printf("%s: failed to create sync semaphore\n", "cdvd_stream"); ASSERT(0); return; } } } gChannelRequestQ.items = (int32 *)LocalAlloc(LMEM_ZEROINIT, sizeof(int32) * (gNumChannels + 1)); gChannelRequestQ.head = 0; gChannelRequestQ.tail = 0; gChannelRequestQ.size = gNumChannels + 1; ASSERT(gChannelRequestQ.items != nil ); #ifdef FIX_BUGS gCdStreamSema = CreateSemaphore(nil, 0, 5, nil); #else gCdStreamSema = CreateSemaphore(nil, 0, 5, "CdStream"); #endif if ( gCdStreamSema == nil ) { printf("%s: failed to create stream semaphore\n", "cdvd_stream"); ASSERT(0); return; } _gCdStreamThread = CreateThread(nil, 64*1024/*64KB*/, CdStreamThread, nil, CREATE_SUSPENDED, &_gCdStreamThreadId); if ( _gCdStreamThread == nil ) { printf("%s: failed to create streaming thread\n", "cdvd_stream"); ASSERT(0); return; } SetThreadPriority(_gCdStreamThread, GetThreadPriority(GetCurrentThread()) - 1); ResumeThread(_gCdStreamThread); } void CdStreamInit(int32 numChannels) { DWORD SectorsPerCluster; DWORD BytesPerSector; DWORD NumberOfFreeClusters; DWORD TotalNumberOfClusters; GetDiskFreeSpace(nil, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters); _gdwCdStreamFlags = 0; #ifndef FIX_BUGS // this just slows down streaming if ( BytesPerSector <= CDSTREAM_SECTOR_SIZE ) { _gdwCdStreamFlags |= FILE_FLAG_NO_BUFFERING; debug("Using no buffered loading for streaming\n"); } #endif _gbCdStreamOverlapped = TRUE; _gdwCdStreamFlags |= FILE_FLAG_OVERLAPPED; _gbCdStreamAsync = FALSE; void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, BytesPerSector); ASSERT( pBuffer != nil ); SetLastError(0); gNumImages = 0; gNumChannels = numChannels; gpReadInfo = (CdReadInfo *)LocalAlloc(LMEM_ZEROINIT, sizeof(CdReadInfo) * numChannels); ASSERT( gpReadInfo != nil ); debug("%s: read info %p\n", "cdvd_stream", gpReadInfo); CdStreamAddImage("MODELS\\GTA3.IMG"); int32 nStatus = CdStreamRead(0, pBuffer, 0, 1); CdStreamRemoveImages(); if ( nStatus == STREAM_SUCCESS ) { _gbCdStreamAsync = TRUE; debug("Using async loading for streaming\n"); } else { _gdwCdStreamFlags &= ~FILE_FLAG_OVERLAPPED; _gbCdStreamOverlapped = FALSE; _gbCdStreamAsync = TRUE; debug("Using sync loading for streaming\n"); } CdStreamInitThread(); ASSERT( pBuffer != nil ); RwFreeAlign(pBuffer); } uint32 GetGTA3ImgSize(void) { ASSERT( gImgFiles[0] != nil ); return (uint32)GetFileSize(gImgFiles[0], nil); } void CdStreamShutdown(void) { if ( _gbCdStreamAsync ) { LocalFree(gChannelRequestQ.items); CloseHandle(gCdStreamSema); CloseHandle(_gCdStreamThread); for ( int32 i = 0; i < gNumChannels; i++ ) CloseHandle(gpReadInfo[i].pDoneSemaphore); } LocalFree(gpReadInfo); } int32 CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size) { ASSERT( channel < gNumChannels ); ASSERT( buffer != nil ); lastPosnRead = size + offset; ASSERT( _GET_INDEX(offset) < MAX_CDIMAGES ); HANDLE hImage = gImgFiles[_GET_INDEX(offset)]; ASSERT( hImage != nil ); CdReadInfo *pChannel = &gpReadInfo[channel]; ASSERT( pChannel != nil ); pChannel->hFile = hImage; SetLastError(0); if ( _gbCdStreamAsync ) { if ( pChannel->nSectorsToRead != 0 || pChannel->bReading ) return STREAM_NONE; pChannel->nStatus = STREAM_NONE; pChannel->nSectorOffset = _GET_OFFSET(offset); pChannel->nSectorsToRead = size; pChannel->pBuffer = buffer; pChannel->bLocked = 0; AddToQueue(&gChannelRequestQ, channel); if ( !ReleaseSemaphore(gCdStreamSema, 1, nil) ) printf("Signal Sema Error\n"); return STREAM_SUCCESS; } if ( _gbCdStreamOverlapped ) { ASSERT( channel < gNumChannels ); CdReadInfo *pChannel = &gpReadInfo[channel]; ASSERT( pChannel != nil ); pChannel->Overlapped.Offset = _GET_OFFSET(offset) * CDSTREAM_SECTOR_SIZE; if ( !ReadFile(hImage, buffer, size * CDSTREAM_SECTOR_SIZE, NULL, &pChannel->Overlapped) && GetLastError() != ERROR_IO_PENDING ) return STREAM_NONE; else return STREAM_SUCCESS; } #ifdef BIG_IMG LARGE_INTEGER liDistanceToMove; liDistanceToMove.QuadPart = _GET_OFFSET(offset); liDistanceToMove.QuadPart *= CDSTREAM_SECTOR_SIZE; SetFilePointerEx(hImage, liDistanceToMove, nil, FILE_BEGIN); #else SetFilePointer(hImage, _GET_OFFSET(offset) * CDSTREAM_SECTOR_SIZE, nil, FILE_BEGIN); #endif DWORD NumberOfBytesRead; if ( !ReadFile(hImage, buffer, size * CDSTREAM_SECTOR_SIZE, &NumberOfBytesRead, nil) ) return STREAM_NONE; else return STREAM_SUCCESS; } int32 CdStreamGetStatus(int32 channel) { ASSERT( channel < gNumChannels ); CdReadInfo *pChannel = &gpReadInfo[channel]; ASSERT( pChannel != nil ); if ( _gbCdStreamAsync ) { if ( pChannel->bReading ) return STREAM_READING; if ( pChannel->nSectorsToRead != 0 ) return STREAM_WAITING; if ( pChannel->nStatus != STREAM_NONE ) { int32 status = pChannel->nStatus; pChannel->nStatus = STREAM_NONE; return status; } return STREAM_NONE; } if ( _gbCdStreamOverlapped ) { ASSERT( pChannel->hFile != nil ); if ( WaitForSingleObjectEx(pChannel->hFile, 0, TRUE) == WAIT_OBJECT_0 ) return STREAM_NONE; else return STREAM_READING; } return STREAM_NONE; } int32 CdStreamGetLastPosn(void) { return lastPosnRead; } // wait for channel to finish reading int32 CdStreamSync(int32 channel) { ASSERT( channel < gNumChannels ); CdReadInfo *pChannel = &gpReadInfo[channel]; ASSERT( pChannel != nil ); if ( _gbCdStreamAsync ) { if ( pChannel->nSectorsToRead != 0 ) { pChannel->bLocked = true; ASSERT( pChannel->pDoneSemaphore != nil ); // Deadlock fix 1 #ifdef FIX_BUGS // This is while loop on Posix streamer, for spurious wakeups if (pChannel->bLocked && pChannel->nSectorsToRead != 0){ WaitForSingleObject(pChannel->pDoneSemaphore, INFINITE); } pChannel->bLocked = false; #else WaitForSingleObject(pChannel->pDoneSemaphore, INFINITE); #endif } pChannel->bReading = false; return pChannel->nStatus; } DWORD NumberOfBytesTransferred; if ( _gbCdStreamOverlapped && pChannel->hFile ) { ASSERT(pChannel->hFile != nil ); // Beware: This is blocking call (because of last parameter) if ( GetOverlappedResult(pChannel->hFile, &pChannel->Overlapped, &NumberOfBytesTransferred, TRUE) ) return STREAM_NONE; else return STREAM_ERROR; } return STREAM_NONE; } void AddToQueue(Queue *queue, int32 item) { ASSERT( queue != nil ); ASSERT( queue->items != nil ); queue->items[queue->tail] = item; queue->tail = (queue->tail + 1) % queue->size; if ( queue->head == queue->tail ) debug("Queue is full\n"); } int32 GetFirstInQueue(Queue *queue) { ASSERT( queue != nil ); if ( queue->head == queue->tail ) return -1; ASSERT( queue->items != nil ); return queue->items[queue->head]; } void RemoveFirstInQueue(Queue *queue) { ASSERT( queue != nil ); if ( queue->head == queue->tail ) { debug("Queue is empty\n"); return; } queue->head = (queue->head + 1) % queue->size; } DWORD WINAPI CdStreamThread(LPVOID lpThreadParameter) { debug("Created cdstream thread\n"); while ( true ) { WaitForSingleObject(gCdStreamSema, INFINITE); int32 channel = GetFirstInQueue(&gChannelRequestQ); ASSERT( channel < gNumChannels ); CdReadInfo *pChannel = &gpReadInfo[channel]; ASSERT( pChannel != nil ); pChannel->bReading = true; if ( pChannel->nStatus == STREAM_NONE ) { if ( _gbCdStreamOverlapped ) { pChannel->Overlapped.Offset = pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE; ASSERT(pChannel->hFile != nil ); ASSERT(pChannel->pBuffer != nil ); DWORD NumberOfBytesTransferred; if ( ReadFile(pChannel->hFile, pChannel->pBuffer, pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE, NULL, &pChannel->Overlapped) ) { pChannel->nStatus = STREAM_NONE; } // Beware: This is blocking call (because of last parameter) else if ( GetLastError() == ERROR_IO_PENDING && GetOverlappedResult(pChannel->hFile, &pChannel->Overlapped, &NumberOfBytesTransferred, TRUE) ) { pChannel->nStatus = STREAM_NONE; } else { pChannel->nStatus = STREAM_ERROR; } } else { ASSERT(pChannel->hFile != nil ); ASSERT(pChannel->pBuffer != nil ); SetFilePointer(pChannel->hFile, pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE, nil, FILE_BEGIN); DWORD NumberOfBytesRead; if ( ReadFile(pChannel->hFile, pChannel->pBuffer, pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE, &NumberOfBytesRead, NULL) ) { pChannel->nStatus = STREAM_NONE; } } } RemoveFirstInQueue(&gChannelRequestQ); pChannel->nSectorsToRead = 0; if ( pChannel->bLocked ) { ASSERT( pChannel->pDoneSemaphore != nil ); // Deadlock fix 2 #ifdef FIX_BUGS pChannel->bLocked = 0; #endif ReleaseSemaphore(pChannel->pDoneSemaphore, 1, NULL); } pChannel->bReading = false; } } bool CdStreamAddImage(char const *path) { ASSERT(path != nil); ASSERT(gNumImages < MAX_CDIMAGES); SetLastError(0); gImgFiles[gNumImages] = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, _gdwCdStreamFlags | FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_READONLY, nil); ASSERT( gImgFiles[gNumImages] != nil ); if ( gImgFiles[gNumImages] == NULL ) return false; strcpy(gCdImageNames[gNumImages], path); gNumImages++; return true; } char * CdStreamGetImageName(int32 cd) { ASSERT(cd < MAX_CDIMAGES); if ( gImgFiles[cd] != nil ) return gCdImageNames[cd]; return nil; } void CdStreamRemoveImages(void) { for ( int32 i = 0; i < gNumChannels; i++ ) CdStreamSync(i); for ( int32 i = 0; i < gNumImages; i++ ) { SetLastError(0); CloseHandle(gImgFiles[i]); gImgFiles[i] = nil; } gNumImages = 0; } int32 CdStreamGetNumImages(void) { return gNumImages; } #endif ================================================ FILE: src/core/CdStream.h ================================================ #pragma once #define CDSTREAM_SECTOR_SIZE 2048 #define _GET_INDEX(a) (a >> 24) #define _GET_OFFSET(a) (a & 0xFFFFFF) enum { STREAM_NONE = uint8( 0), STREAM_SUCCESS = uint8( 1), STREAM_READING = uint8(-1), // 0xFF, STREAM_ERROR = uint8(-2), // 0xFE, STREAM_ERROR_NOCD = uint8(-3), // 0xFD, STREAM_ERROR_WRONGCD = uint8(-4), // 0xFC, STREAM_ERROR_OPENCD = uint8(-5), // 0xFB, STREAM_WAITING = uint8(-6) // 0xFA, }; struct Queue { int32 *items; int32 head; int32 tail; int32 size; }; VALIDATE_SIZE(Queue, 0x10); void CdStreamInitThread(void); void CdStreamInit(int32 numChannels); uint32 GetGTA3ImgSize(void); void CdStreamShutdown(void); int32 CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size); int32 CdStreamGetStatus(int32 channel); int32 CdStreamGetLastPosn(void); int32 CdStreamSync(int32 channel); void AddToQueue(Queue *queue, int32 item); int32 GetFirstInQueue(Queue *queue); void RemoveFirstInQueue(Queue *queue); bool CdStreamAddImage(char const *path); char *CdStreamGetImageName(int32 cd); void CdStreamRemoveImages(void); int32 CdStreamGetNumImages(void); #ifdef FLUSHABLE_STREAMING extern bool flushStream[MAX_CDCHANNELS]; #endif ================================================ FILE: src/core/CdStreamPosix.cpp ================================================ #ifndef _WIN32 #include "common.h" #include "crossplatform.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #include "CdStream.h" #include "rwcore.h" #include "MemoryMgr.h" #define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) #define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__) #ifdef FLUSHABLE_STREAMING bool flushStream[MAX_CDCHANNELS]; #endif #ifdef USE_UNNAMED_SEM #define RE3_SEM_OPEN(name, ...) re3_sem_open() sem_t* re3_sem_open(void) { sem_t* sem = (sem_t*)malloc(sizeof(sem_t)); if (sem_init(sem, 0, 1) == -1) { sem = SEM_FAILED; } return sem; } #define RE3_SEM_CLOSE(sem, format, ...) re3_sem_close(sem) void re3_sem_close(sem_t* sem) { sem_destroy(sem); free(sem); } #else #define RE3_SEM_OPEN re3_sem_open sem_t* re3_sem_open(const char* format, ...) { char semName[21]; va_list va; va_start(va, format); vsprintf(semName, format, va); return sem_open(semName, O_CREAT, 0644, 1); } #define RE3_SEM_CLOSE re3_sem_close void re3_sem_close(sem_t* sem, const char* format, ...) { sem_close(sem); char semName[21]; va_list va; va_start(va, format); vsprintf(semName, format, va); sem_unlink(semName); } #endif struct CdReadInfo { uint32 nSectorOffset; uint32 nSectorsToRead; void *pBuffer; bool bLocked; bool bReading; int32 nStatus; #ifdef ONE_THREAD_PER_CHANNEL int8 nThreadStatus; // 0: created 1:priority set up 2:abort now pthread_t pChannelThread; sem_t *pStartSemaphore; #endif sem_t *pDoneSemaphore; // used for CdStreamSync int32 hFile; }; char gCdImageNames[MAX_CDIMAGES+1][64]; int32 gNumImages; int32 gNumChannels; int32 gImgFiles[MAX_CDIMAGES]; // -1: error 0:unused otherwise: fd char *gImgNames[MAX_CDIMAGES]; #ifndef ONE_THREAD_PER_CHANNEL pthread_t _gCdStreamThread; sem_t *gCdStreamSema; // released when we have new thing to read(so channel is set) int8 gCdStreamThreadStatus; // 0: created 1:priority set up 2:abort now Queue gChannelRequestQ; bool _gbCdStreamOverlapped; #endif CdReadInfo *gpReadInfo; int32 lastPosnRead; int _gdwCdStreamFlags; void *CdStreamThread(void* channelId); void CdStreamInitThread(void) { int status; #ifndef ONE_THREAD_PER_CHANNEL gChannelRequestQ.items = (int32 *)calloc(gNumChannels + 1, sizeof(int32)); gChannelRequestQ.head = 0; gChannelRequestQ.tail = 0; gChannelRequestQ.size = gNumChannels + 1; ASSERT(gChannelRequestQ.items != nil ); gCdStreamSema = RE3_SEM_OPEN("/semaphore_cd_stream"); if (gCdStreamSema == SEM_FAILED) { CDTRACE("failed to create stream semaphore"); ASSERT(0); return; } #endif if ( gNumChannels > 0 ) { for ( int32 i = 0; i < gNumChannels; i++ ) { gpReadInfo[i].pDoneSemaphore = RE3_SEM_OPEN("/semaphore_done%d", i); if (gpReadInfo[i].pDoneSemaphore == SEM_FAILED) { CDTRACE("failed to create sync semaphore"); ASSERT(0); return; } #ifdef ONE_THREAD_PER_CHANNEL gpReadInfo[i].pStartSemaphore = RE3_SEM_OPEN("/semaphore_start%d", i); if (gpReadInfo[i].pStartSemaphore == SEM_FAILED) { CDTRACE("failed to create start semaphore"); ASSERT(0); return; } gpReadInfo[i].nThreadStatus = 0; int *channelI = (int*)malloc(sizeof(int)); *channelI = i; status = pthread_create(&gpReadInfo[i].pChannelThread, NULL, CdStreamThread, (void*)channelI); if (status == -1) { CDTRACE("failed to create sync thread"); ASSERT(0); return; } #endif } } #ifndef ONE_THREAD_PER_CHANNEL debug("Using one streaming thread for all channels\n"); gCdStreamThreadStatus = 0; status = pthread_create(&_gCdStreamThread, NULL, CdStreamThread, nil); if (status == -1) { CDTRACE("failed to create sync thread"); ASSERT(0); return; } #else debug("Using separate streaming threads for each channel\n"); #endif } void CdStreamInit(int32 numChannels) { struct statvfs fsInfo; if((statvfs("models/gta3.img", &fsInfo)) < 0) { CDTRACE("can't get filesystem info"); ASSERT(0); return; } #ifdef __linux__ _gdwCdStreamFlags = O_RDONLY | O_NOATIME; #else _gdwCdStreamFlags = O_RDONLY; #endif // People say it's slower /* if ( fsInfo.f_bsize <= CDSTREAM_SECTOR_SIZE ) { _gdwCdStreamFlags |= O_DIRECT; debug("Using no buffered loading for streaming\n"); } */ void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, (RwUInt32)fsInfo.f_bsize); ASSERT( pBuffer != nil ); gNumImages = 0; gNumChannels = numChannels; ASSERT( gNumChannels != 0 ); gpReadInfo = (CdReadInfo *)calloc(numChannels, sizeof(CdReadInfo)); ASSERT( gpReadInfo != nil ); CDDEBUG("read info %p", gpReadInfo); CdStreamInitThread(); ASSERT( pBuffer != nil ); RwFreeAlign(pBuffer); } uint32 GetGTA3ImgSize(void) { ASSERT( gImgFiles[0] > 0 ); struct stat statbuf; char path[PATH_MAX]; realpath(gImgNames[0], path); if (stat(path, &statbuf) == -1) { // Try case-insensitivity char* real = casepath(gImgNames[0], false); if (real) { realpath(real, path); free(real); if (stat(path, &statbuf) != -1) goto ok; } CDTRACE("can't get size of gta3.img"); ASSERT(0); return 0; } ok: return (uint32)statbuf.st_size; } void CdStreamShutdown(void) { // Destroying semaphores and free(gpReadInfo) will be done at threads #ifndef ONE_THREAD_PER_CHANNEL gCdStreamThreadStatus = 2; sem_post(gCdStreamSema); pthread_join(_gCdStreamThread, nil); #else for ( int32 i = 0; i < gNumChannels; i++ ) { gpReadInfo[i].nThreadStatus = 2; sem_post(gpReadInfo[i].pStartSemaphore); pthread_join(gpReadInfo[i].pChannelThread, nil); } #endif } int32 CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size) { ASSERT( channel < gNumChannels ); ASSERT( buffer != nil ); lastPosnRead = size + offset; ASSERT( _GET_INDEX(offset) < MAX_CDIMAGES ); int32 hImage = gImgFiles[_GET_INDEX(offset)]; ASSERT( hImage > 0 ); CdReadInfo *pChannel = &gpReadInfo[channel]; ASSERT( pChannel != nil ); if ( pChannel->nSectorsToRead != 0 || pChannel->bReading ) { if (pChannel->hFile == hImage - 1 && pChannel->nSectorOffset == _GET_OFFSET(offset) && pChannel->nSectorsToRead >= size) return STREAM_SUCCESS; #ifdef FLUSHABLE_STREAMING flushStream[channel] = 1; CdStreamSync(channel); #else return STREAM_NONE; #endif } pChannel->hFile = hImage - 1; pChannel->nStatus = STREAM_NONE; pChannel->nSectorOffset = _GET_OFFSET(offset); pChannel->nSectorsToRead = size; pChannel->pBuffer = buffer; pChannel->bLocked = 0; #ifndef ONE_THREAD_PER_CHANNEL AddToQueue(&gChannelRequestQ, channel); if ( sem_post(gCdStreamSema) != 0 ) printf("Signal Sema Error\n"); #else if ( sem_post(pChannel->pStartSemaphore) != 0 ) printf("Signal Sema Error\n"); #endif return STREAM_SUCCESS; } int32 CdStreamGetStatus(int32 channel) { ASSERT( channel < gNumChannels ); CdReadInfo *pChannel = &gpReadInfo[channel]; ASSERT( pChannel != nil ); #ifdef ONE_THREAD_PER_CHANNEL if (pChannel->nThreadStatus == 2) return STREAM_NONE; #else if (gCdStreamThreadStatus == 2) return STREAM_NONE; #endif if ( pChannel->bReading ) return STREAM_READING; if ( pChannel->nSectorsToRead != 0 ) return STREAM_WAITING; if ( pChannel->nStatus != STREAM_NONE ) { int32 status = pChannel->nStatus; pChannel->nStatus = STREAM_NONE; return status; } return STREAM_NONE; } int32 CdStreamGetLastPosn(void) { return lastPosnRead; } // wait for channel to finish reading int32 CdStreamSync(int32 channel) { ASSERT( channel < gNumChannels ); CdReadInfo *pChannel = &gpReadInfo[channel]; ASSERT( pChannel != nil ); #ifdef FLUSHABLE_STREAMING if (flushStream[channel]) { pChannel->nSectorsToRead = 0; #ifdef ONE_THREAD_PER_CHANNEL pthread_kill(pChannel->pChannelThread, SIGUSR1); if (pChannel->bReading) { pChannel->bLocked = true; #else if (pChannel->bReading) { pChannel->bLocked = true; pthread_kill(_gCdStreamThread, SIGUSR1); #endif while (pChannel->bLocked) sem_wait(pChannel->pDoneSemaphore); } pChannel->bReading = false; flushStream[channel] = false; return STREAM_NONE; } #endif if ( pChannel->nSectorsToRead != 0 ) { pChannel->bLocked = true; while (pChannel->bLocked && pChannel->nSectorsToRead != 0){ sem_wait(pChannel->pDoneSemaphore); } pChannel->bLocked = false; } pChannel->bReading = false; return pChannel->nStatus; } void AddToQueue(Queue *queue, int32 item) { ASSERT( queue != nil ); ASSERT( queue->items != nil ); queue->items[queue->tail] = item; queue->tail = (queue->tail + 1) % queue->size; if ( queue->head == queue->tail ) debug("Queue is full\n"); } int32 GetFirstInQueue(Queue *queue) { ASSERT( queue != nil ); if ( queue->head == queue->tail ) return -1; ASSERT( queue->items != nil ); return queue->items[queue->head]; } void RemoveFirstInQueue(Queue *queue) { ASSERT( queue != nil ); if ( queue->head == queue->tail ) { debug("Queue is empty\n"); return; } queue->head = (queue->head + 1) % queue->size; } void *CdStreamThread(void *param) { debug("Created cdstream thread\n"); #ifndef ONE_THREAD_PER_CHANNEL while (gCdStreamThreadStatus != 2) { sem_wait(gCdStreamSema); int32 channel = GetFirstInQueue(&gChannelRequestQ); // spurious wakeup if (channel == -1) continue; #else int channel = *((int*)param); while (gpReadInfo[channel].nThreadStatus != 2){ sem_wait(gpReadInfo[channel].pStartSemaphore); #endif CdReadInfo *pChannel = &gpReadInfo[channel]; ASSERT( pChannel != nil ); // spurious wakeup or we sent interrupt signal for flushing if(pChannel->nSectorsToRead == 0) continue; pChannel->bReading = true; // Not standard POSIX :shrug: #ifdef __linux__ #ifdef ONE_THREAD_PER_CHANNEL if (gpReadInfo[channel].nThreadStatus == 0){ gpReadInfo[channel].nThreadStatus = 1; #else if (gCdStreamThreadStatus == 0){ gCdStreamThreadStatus = 1; #endif pid_t tid = syscall(SYS_gettid); int ret = setpriority(PRIO_PROCESS, tid, getpriority(PRIO_PROCESS, getpid()) + 1); } #endif if ( pChannel->nStatus == STREAM_NONE ) { ASSERT(pChannel->hFile >= 0); ASSERT(pChannel->pBuffer != nil ); lseek(pChannel->hFile, (size_t)pChannel->nSectorOffset * (size_t)CDSTREAM_SECTOR_SIZE, SEEK_SET); if (read(pChannel->hFile, pChannel->pBuffer, pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE) == -1) { // pChannel->nSectorsToRead == 0 at this point means we wanted to flush channel // STREAM_WAITING is a little hack to make CStreaming not process this data pChannel->nStatus = pChannel->nSectorsToRead == 0 ? STREAM_WAITING : STREAM_ERROR; } else { pChannel->nStatus = STREAM_NONE; } } #ifndef ONE_THREAD_PER_CHANNEL RemoveFirstInQueue(&gChannelRequestQ); #endif pChannel->nSectorsToRead = 0; if ( pChannel->bLocked ) { pChannel->bLocked = 0; sem_post(pChannel->pDoneSemaphore); } pChannel->bReading = false; } char semName[20]; #ifndef ONE_THREAD_PER_CHANNEL for ( int32 i = 0; i < gNumChannels; i++ ) { RE3_SEM_CLOSE(gpReadInfo[i].pDoneSemaphore, "/semaphore_done%d", i); } RE3_SEM_CLOSE(gCdStreamSema, "/semaphore_cd_stream"); free(gChannelRequestQ.items); #else RE3_SEM_CLOSE(gpReadInfo[channel].pStartSemaphore, "/semaphore_start%d", channel); RE3_SEM_CLOSE(gpReadInfo[channel].pDoneSemaphore, "/semaphore_done%d", channel); #endif if (gpReadInfo) free(gpReadInfo); gpReadInfo = nil; pthread_exit(nil); } bool CdStreamAddImage(char const *path) { ASSERT(path != nil); ASSERT(gNumImages < MAX_CDIMAGES); gImgFiles[gNumImages] = open(path, _gdwCdStreamFlags); // Fix case sensitivity and backslashes. if (gImgFiles[gNumImages] == -1) { char* real = casepath(path, false); if (real) { gImgFiles[gNumImages] = open(real, _gdwCdStreamFlags); free(real); } } if ( gImgFiles[gNumImages] == -1 ) { assert(false); return false; } gImgNames[gNumImages] = strdup(path); gImgFiles[gNumImages]++; // because -1: error 0: not used strcpy(gCdImageNames[gNumImages], path); gNumImages++; return true; } char * CdStreamGetImageName(int32 cd) { ASSERT(cd < MAX_CDIMAGES); if ( gImgFiles[cd] > 0) return gCdImageNames[cd]; return nil; } void CdStreamRemoveImages(void) { for ( int32 i = 0; i < gNumChannels; i++ ) { #ifdef FLUSHABLE_STREAMING flushStream[i] = 1; #endif CdStreamSync(i); } for ( int32 i = 0; i < gNumImages; i++ ) { close(gImgFiles[i] - 1); free(gImgNames[i]); gImgFiles[i] = 0; } gNumImages = 0; } int32 CdStreamGetNumImages(void) { return gNumImages; } #endif ================================================ FILE: src/core/Clock.cpp ================================================ #include "common.h" #include "Timer.h" #include "Pad.h" #include "Clock.h" #include "Stats.h" #include "VarConsole.h" _TODO("gbFastTime"); bool gbFastTime; uint8 CClock::ms_nGameClockHours; uint8 CClock::ms_nGameClockMinutes; uint16 CClock::ms_nGameClockSeconds; uint8 CClock::ms_Stored_nGameClockHours; uint8 CClock::ms_Stored_nGameClockMinutes; uint16 CClock::ms_Stored_nGameClockSeconds; uint32 CClock::ms_nMillisecondsPerGameMinute; uint32 CClock::ms_nLastClockTick; bool CClock::ms_bClockHasBeenStored; #ifndef MASTER bool gbFreezeTime; #endif void CClock::Initialise(uint32 scale) { debug("Initialising CClock...\n"); ms_nGameClockHours = 12; ms_nGameClockMinutes = 0; ms_nGameClockSeconds = 0; ms_nMillisecondsPerGameMinute = scale; ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); ms_bClockHasBeenStored = false; debug("CClock ready\n"); #ifndef MASTER VarConsole.Add("Time (hour of day)", &ms_nGameClockHours, 1, 0, 23, true); VarConsole.Add("Freeze time", &gbFreezeTime, true); #endif } void CClock::Update(void) { if(CPad::GetPad(1)->GetRightShoulder1()) { ms_nGameClockMinutes += 8; ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); if(ms_nGameClockMinutes >= 60) { ms_nGameClockHours++; ms_nGameClockMinutes = 0; if(ms_nGameClockHours >= 24) ms_nGameClockHours = 0; } } #ifndef MASTER else if (gbFreezeTime) ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); #endif else if(CTimer::GetTimeInMilliseconds() - ms_nLastClockTick > ms_nMillisecondsPerGameMinute || gbFastTime) { ms_nGameClockMinutes++; ms_nLastClockTick += ms_nMillisecondsPerGameMinute; if ( gbFastTime ) ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); if(ms_nGameClockMinutes >= 60) { ms_nGameClockHours++; ms_nGameClockMinutes = 0; if(ms_nGameClockHours >= 24) { CStats::DaysPassed++; ms_nGameClockHours = 0; } } } ms_nGameClockSeconds = 60 * (CTimer::GetTimeInMilliseconds() - ms_nLastClockTick) / ms_nMillisecondsPerGameMinute; } void CClock::SetGameClock(uint8 h, uint8 m) { while (m >= 60) { m -= 60; h++; } ms_nGameClockMinutes = m; while (h >= 24) h -= 24; ms_nGameClockHours = h; ms_nGameClockSeconds = 0; ms_nLastClockTick = CTimer::GetTimeInMilliseconds(); } int32 CClock::GetGameClockMinutesUntil(uint8 h, uint8 m) { int32 now, then; now = ms_nGameClockHours*60 + ms_nGameClockMinutes; then = h*60 + m; if(then < now) then += 24*60; return then-now; } bool CClock::GetIsTimeInRange(uint8 h1, uint8 h2) { if(h1 > h2) return ms_nGameClockHours >= h1 || ms_nGameClockHours < h2; else return ms_nGameClockHours >= h1 && ms_nGameClockHours < h2; } void CClock::StoreClock(void) { ms_Stored_nGameClockHours = ms_nGameClockHours; ms_Stored_nGameClockMinutes = ms_nGameClockMinutes; ms_Stored_nGameClockSeconds = ms_nGameClockSeconds; ms_bClockHasBeenStored = true; } void CClock::RestoreClock(void) { ms_nGameClockHours = ms_Stored_nGameClockHours; ms_nGameClockMinutes = ms_Stored_nGameClockMinutes; ms_nGameClockSeconds = ms_Stored_nGameClockSeconds; } ================================================ FILE: src/core/Clock.h ================================================ #pragma once class CClock { public: static uint8 ms_nGameClockHours; static uint8 ms_nGameClockMinutes; static uint16 ms_nGameClockSeconds; static uint8 ms_Stored_nGameClockHours; static uint8 ms_Stored_nGameClockMinutes; static uint16 ms_Stored_nGameClockSeconds; static uint32 ms_nMillisecondsPerGameMinute; static uint32 ms_nLastClockTick; static bool ms_bClockHasBeenStored; static void Initialise(uint32 scale); static void Update(void); static void SetGameClock(uint8 h, uint8 m); static int32 GetGameClockMinutesUntil(uint8 h, uint8 m); static bool GetIsTimeInRange(uint8 h1, uint8 h2); static void StoreClock(void); static void RestoreClock(void); static uint8 GetHours(void) { return ms_nGameClockHours; } static uint8 GetMinutes(void) { return ms_nGameClockMinutes; } static int16 GetSeconds(void) { return ms_nGameClockSeconds; } static uint8 &GetHoursRef(void) { return ms_nGameClockHours; } static uint8 &GetMinutesRef(void) { return ms_nGameClockMinutes; } }; ================================================ FILE: src/core/ControllerConfig.cpp ================================================ #define WITHDINPUT #include "common.h" #include "platform.h" #include "crossplatform.h" #include "ControllerConfig.h" #include "Pad.h" #include "FileMgr.h" #include "Text.h" #include "Font.h" #include "Messages.h" #include "Frontend.h" #include "Ped.h" #include "PlayerPed.h" #include "Vehicle.h" #include "World.h" #include "ModelIndices.h" #include "Camera.h" #include "GenericGameStorage.h" CControllerConfigManager ControlsManager; CControllerConfigManager::CControllerConfigManager() { m_bFirstCapture = false; m_bMouseAssociated = false; MakeControllerActionsBlank(); InitDefaultControlConfiguration(); InitialiseControllerActionNameArray(); } void CControllerConfigManager::MakeControllerActionsBlank() { #ifdef LOAD_INI_SETTINGS ms_padButtonsInited = 0; #endif for (int32 i = 0; i < MAX_CONTROLLERTYPES; i++) { for (int32 j = 0; j < MAX_CONTROLLERACTIONS; j++) { ClearSettingsAssociatedWithAction((e_ControllerAction)j, (eControllerType)i); } } } #ifdef RW_GL3 int MapIdToButtonId(int mapId) { switch (mapId) { case GLFW_GAMEPAD_BUTTON_A: // Cross return 2; case GLFW_GAMEPAD_BUTTON_B: // Circle return 1; case GLFW_GAMEPAD_BUTTON_X: // Square return 3; case GLFW_GAMEPAD_BUTTON_Y: // Triangle return 4; case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: return 7; case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: return 8; case GLFW_GAMEPAD_BUTTON_BACK: return 9; case GLFW_GAMEPAD_BUTTON_START: return 12; case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: return 10; case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: return 11; case GLFW_GAMEPAD_BUTTON_DPAD_UP: return 13; case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: return 14; case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: return 15; case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: return 16; // GLFW sends those as axes, so I added them here manually. case 15: // Left trigger return 5; case 16: // Right trigger return 6; default: return 0; } } #endif int32 CControllerConfigManager::GetJoyButtonJustDown() { #ifdef __DINPUT_INCLUDED__ #ifdef FIX_BUGS for (int32 i = 0; i < MAX_BUTTONS; i++) #else for (int32 i = 0; i < JOY_BUTTONS; i++) #endif { if (m_NewState.rgbButtons[i] & 0x80 && !(m_OldState.rgbButtons[i] & 0x80)) return i + 1; } #elif defined RW_GL3 if (m_NewState.isGamepad) { for (int32 i = 0; i < MAX_BUTTONS; i++) { if (m_NewState.mappedButtons[i] && !(m_OldState.mappedButtons[i])) return MapIdToButtonId(i); } } else { for (int32 i = 0; i < Min(m_NewState.numButtons, MAX_BUTTONS); i++) { if (m_NewState.buttons[i] && !(m_OldState.buttons[i])) return i + 1; } } #endif return 0; } void CControllerConfigManager::SaveSettings(int32 file) { if (file) { for (int32 i = 0; i < MAX_CONTROLLERTYPES; i++) { for (int32 j = 0; j < MAX_CONTROLLERACTIONS; j++) { CFileMgr::Write(file, (char *)&ControlsManager.m_aSettings[j][i], sizeof(tControllerConfigBind)); } } } } void CControllerConfigManager::LoadSettings(int32 file) { bool bValid = true; int nVersion = 0; #ifdef BIND_VEHICLE_FIREWEAPON bool skipVehicleFireWeapon = false; #endif if (file) { char buff[29]; CFileMgr::Read(file, buff, sizeof(buff)); if (!strncmp(buff, TopLineEmptyFile, sizeof(TopLineEmptyFile)-1)) bValid = false; else { CFileMgr::Seek(file, 0, 0); CFileMgr::Read(file, (char*)&nVersion, sizeof(nVersion)); } } if (bValid && nVersion >= 3) { ControlsManager.MakeControllerActionsBlank(); #ifdef BIND_VEHICLE_FIREWEAPON skipVehicleFireWeapon = nVersion < 4; // Set the default settings of VEHICLE_FIREWEAPON if (skipVehicleFireWeapon) { SetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, rsPADINS, KEYBOARD); SetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, rsLCTRL, OPTIONAL_EXTRA); if (m_bMouseAssociated) SetMouseButtonAssociatedWithAction(VEHICLE_FIREWEAPON, 1); } #endif for (int32 i = 0; i < MAX_CONTROLLERTYPES; i++) { for (int32 j = 0; j < MAX_CONTROLLERACTIONS; j++) { #ifdef BIND_VEHICLE_FIREWEAPON // Skip file read if (skipVehicleFireWeapon && j == VEHICLE_FIREWEAPON) continue; #endif CFileMgr::Read(file, (char *)&ControlsManager.m_aSettings[j][i], sizeof(tControllerConfigBind)); } } } } void CControllerConfigManager::InitDefaultControlConfiguration() { SetControllerKeyAssociatedWithAction (VEHICLE_LOOKLEFT, rsPADEND, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_LOOKLEFT, 'Q', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (VEHICLE_LOOKRIGHT, rsPADDOWN, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_LOOKRIGHT, 'E', OPTIONAL_EXTRA); if ( _dwOperatingSystemVersion == OS_WIN98 ) SetControllerKeyAssociatedWithAction(VEHICLE_HORN, rsSHIFT, OPTIONAL_EXTRA); // BUG: must be KEYBOARD ? else { SetControllerKeyAssociatedWithAction(VEHICLE_HORN, rsLSHIFT, OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction(VEHICLE_HORN, rsRSHIFT, KEYBOARD); } SetControllerKeyAssociatedWithAction (VEHICLE_HANDBRAKE, rsRCTRL, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_HANDBRAKE, ' ', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (VEHICLE_ENTER_EXIT, rsENTER, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_ENTER_EXIT, 'F', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (VEHICLE_ACCELERATE, rsUP, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_ACCELERATE, 'W', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (VEHICLE_CHANGE_RADIO_STATION, rsINS, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_CHANGE_RADIO_STATION, 'R', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (VEHICLE_BRAKE, rsDOWN, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_BRAKE, 'S', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (TOGGLE_SUBMISSIONS, rsPLUS, KEYBOARD); SetControllerKeyAssociatedWithAction (TOGGLE_SUBMISSIONS, rsCAPSLK, OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (GO_LEFT, rsLEFT, KEYBOARD); SetControllerKeyAssociatedWithAction (GO_LEFT, 'A', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (GO_RIGHT, rsRIGHT, KEYBOARD); SetControllerKeyAssociatedWithAction (GO_RIGHT, 'D', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (GO_FORWARD, rsUP, KEYBOARD); SetControllerKeyAssociatedWithAction (GO_FORWARD, 'W', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (GO_BACK, rsDOWN, KEYBOARD); SetControllerKeyAssociatedWithAction (GO_BACK, 'S', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (NETWORK_TALK, 'T', KEYBOARD); SetControllerKeyAssociatedWithAction (PED_LOOKBEHIND, rsPADEND, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_LOOKBEHIND, rsCAPSLK, OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (PED_DUCK, 'C', KEYBOARD); SetControllerKeyAssociatedWithAction (PED_FIREWEAPON, rsPADINS, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_FIREWEAPON, rsLCTRL, OPTIONAL_EXTRA); #ifdef BIND_VEHICLE_FIREWEAPON SetControllerKeyAssociatedWithAction (VEHICLE_FIREWEAPON, rsPADINS, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_FIREWEAPON, rsLCTRL, OPTIONAL_EXTRA); #endif SetControllerKeyAssociatedWithAction (PED_CYCLE_WEAPON_LEFT, rsPADDEL, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_CYCLE_WEAPON_RIGHT, rsPADENTER, OPTIONAL_EXTRA); // BUG: must be KEYBOARD ? SetControllerKeyAssociatedWithAction (PED_LOCK_TARGET, rsDEL, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_JUMPING, rsRCTRL, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_JUMPING, ' ', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (PED_ANSWER_PHONE, rsTAB, KEYBOARD); if ( _dwOperatingSystemVersion == OS_WIN98 ) SetControllerKeyAssociatedWithAction(PED_SPRINT, rsSHIFT, OPTIONAL_EXTRA); // BUG: must be KEYBOARD ? else { SetControllerKeyAssociatedWithAction(PED_SPRINT, rsLSHIFT, OPTIONAL_EXTRA); #ifndef FIX_BUGS SetControllerKeyAssociatedWithAction(PED_SPRINT, rsRSHIFT, OPTIONAL_EXTRA); // BUG: must be KEYBOARD #else SetControllerKeyAssociatedWithAction(PED_SPRINT, rsRSHIFT, KEYBOARD); #endif } SetControllerKeyAssociatedWithAction (PED_CYCLE_TARGET_LEFT, '[', KEYBOARD); SetControllerKeyAssociatedWithAction (PED_CYCLE_TARGET_RIGHT, ']', OPTIONAL_EXTRA); // BUG: must be KEYBOARD ? SetControllerKeyAssociatedWithAction (PED_CENTER_CAMERA_BEHIND_PLAYER, '#', KEYBOARD); SetControllerKeyAssociatedWithAction (PED_SNIPER_ZOOM_IN, rsPGUP, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_SNIPER_ZOOM_IN, 'Z', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (PED_SNIPER_ZOOM_OUT, rsPGDN, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_SNIPER_ZOOM_OUT, 'X', OPTIONAL_EXTRA); SetControllerKeyAssociatedWithAction (PED_1RST_PERSON_LOOK_LEFT, rsPADLEFT, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_1RST_PERSON_LOOK_RIGHT, rsPADRIGHT, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_1RST_PERSON_LOOK_UP, rsPADUP, KEYBOARD); SetControllerKeyAssociatedWithAction (PED_1RST_PERSON_LOOK_DOWN, rsPAD5, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_TURRETLEFT, rsPADLEFT, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_TURRETRIGHT, rsPAD5, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_TURRETUP, rsPADPGUP, KEYBOARD); SetControllerKeyAssociatedWithAction (VEHICLE_TURRETDOWN, rsPADRIGHT, KEYBOARD); SetControllerKeyAssociatedWithAction (CAMERA_CHANGE_VIEW_ALL_SITUATIONS, rsHOME, KEYBOARD); SetControllerKeyAssociatedWithAction (CAMERA_CHANGE_VIEW_ALL_SITUATIONS, 'V', OPTIONAL_EXTRA); for (int32 i = 0; i < MAX_SIMS; i++) { m_aSimCheckers[i][KEYBOARD] = false; m_aSimCheckers[i][OPTIONAL_EXTRA] = false; m_aSimCheckers[i][MOUSE] = false; m_aSimCheckers[i][JOYSTICK] = false; } } void CControllerConfigManager::InitDefaultControlConfigMouse(CMouseControllerState const &availableButtons) { if (availableButtons.LMB) { m_bMouseAssociated = true; SetMouseButtonAssociatedWithAction(PED_FIREWEAPON, 1); #ifdef BIND_VEHICLE_FIREWEAPON SetMouseButtonAssociatedWithAction(VEHICLE_FIREWEAPON, 1); #endif } if (availableButtons.RMB) { SetMouseButtonAssociatedWithAction(PED_LOCK_TARGET, 3); SetMouseButtonAssociatedWithAction(VEHICLE_HANDBRAKE, 3); } if (availableButtons.MMB) { SetMouseButtonAssociatedWithAction(VEHICLE_LOOKBEHIND, 2); SetMouseButtonAssociatedWithAction(PED_LOOKBEHIND, 2); } if (availableButtons.WHEELUP || availableButtons.WHEELDN) { SetMouseButtonAssociatedWithAction(PED_CYCLE_WEAPON_LEFT, 4); SetMouseButtonAssociatedWithAction(PED_CYCLE_WEAPON_RIGHT, 5); SetMouseButtonAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, 4); SetMouseButtonAssociatedWithAction(PED_SNIPER_ZOOM_IN, 4); SetMouseButtonAssociatedWithAction(PED_SNIPER_ZOOM_OUT, 5); } } #ifdef LOAD_INI_SETTINGS uint32 CControllerConfigManager::ms_padButtonsInited = 0; #endif void CControllerConfigManager::InitDefaultControlConfigJoyPad(uint32 buttons) { #ifdef XINPUT // No manual bindings for you, honey. return; #endif m_bFirstCapture = true; uint32 btn = buttons; if (buttons > 16) btn = 16; #ifdef LOAD_INI_SETTINGS uint32 buttonMin = ms_padButtonsInited; if (buttonMin >= btn) return; ms_padButtonsInited = btn; #define IF_BTN_IN_RANGE(n) \ case n: \ if (n <= buttonMin) \ return; #else #define IF_BTN_IN_RANGE(n) \ case n: #endif // Now we use SDL Game Controller DB #if defined RW_D3D9 || defined RWLIBS if ( AllValidWinJoys.m_aJoys[JOYSTICK1].m_nVendorID == 0x3427 && AllValidWinJoys.m_aJoys[JOYSTICK1].m_nProductID == 0x1190) #else if (0) #endif { //GIC USB Joystick, PS2 Gamepad ? switch (btn) { IF_BTN_IN_RANGE(16) SetControllerKeyAssociatedWithAction(GO_LEFT, 16, JOYSTICK); IF_BTN_IN_RANGE(15) SetControllerKeyAssociatedWithAction(GO_BACK, 15, JOYSTICK); IF_BTN_IN_RANGE(14) SetControllerKeyAssociatedWithAction(GO_RIGHT, 14, JOYSTICK); IF_BTN_IN_RANGE(13) SetControllerKeyAssociatedWithAction(GO_FORWARD, 13, JOYSTICK); IF_BTN_IN_RANGE(12) IF_BTN_IN_RANGE(11) SetControllerKeyAssociatedWithAction(PED_LOOKBEHIND, 11, JOYSTICK); SetControllerKeyAssociatedWithAction(TOGGLE_SUBMISSIONS, 11, JOYSTICK); IF_BTN_IN_RANGE(10) SetControllerKeyAssociatedWithAction(VEHICLE_HORN, 10, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_DUCK, 10, JOYSTICK); IF_BTN_IN_RANGE(9) SetControllerKeyAssociatedWithAction(CAMERA_CHANGE_VIEW_ALL_SITUATIONS, 9, JOYSTICK); IF_BTN_IN_RANGE(8) SetControllerKeyAssociatedWithAction(VEHICLE_HANDBRAKE, 8, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_LOCK_TARGET, 8, JOYSTICK); IF_BTN_IN_RANGE(7) SetControllerKeyAssociatedWithAction(PED_ANSWER_PHONE, 7, JOYSTICK); SetControllerKeyAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, 7, JOYSTICK); IF_BTN_IN_RANGE(6) SetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_RIGHT, 6, JOYSTICK); SetControllerKeyAssociatedWithAction(VEHICLE_LOOKRIGHT, 6, JOYSTICK); IF_BTN_IN_RANGE(5) SetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_LEFT, 5, JOYSTICK); SetControllerKeyAssociatedWithAction(VEHICLE_LOOKLEFT, 5, JOYSTICK); /*******************************************************************************************/ IF_BTN_IN_RANGE(4) SetControllerKeyAssociatedWithAction(VEHICLE_BRAKE, 4, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_JUMPING, 4, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_IN, 4, JOYSTICK); IF_BTN_IN_RANGE(3) SetControllerKeyAssociatedWithAction(VEHICLE_ACCELERATE, 3, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_SPRINT, 3, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_OUT, 3, JOYSTICK); IF_BTN_IN_RANGE(2) SetControllerKeyAssociatedWithAction(PED_FIREWEAPON, 2, JOYSTICK); #ifdef BIND_VEHICLE_FIREWEAPON SetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, 2, JOYSTICK); #endif IF_BTN_IN_RANGE(1) SetControllerKeyAssociatedWithAction(VEHICLE_ENTER_EXIT, 1, JOYSTICK); /*******************************************************************************************/ } } else { switch (btn) { IF_BTN_IN_RANGE(16) SetControllerKeyAssociatedWithAction(GO_LEFT, 16, JOYSTICK); IF_BTN_IN_RANGE(15) SetControllerKeyAssociatedWithAction(GO_BACK, 15, JOYSTICK); IF_BTN_IN_RANGE(14) SetControllerKeyAssociatedWithAction(GO_RIGHT, 14, JOYSTICK); IF_BTN_IN_RANGE(13) SetControllerKeyAssociatedWithAction(GO_FORWARD, 13, JOYSTICK); IF_BTN_IN_RANGE(12) IF_BTN_IN_RANGE(11) SetControllerKeyAssociatedWithAction(PED_LOOKBEHIND, 11, JOYSTICK); SetControllerKeyAssociatedWithAction(TOGGLE_SUBMISSIONS, 11, JOYSTICK); IF_BTN_IN_RANGE(10) SetControllerKeyAssociatedWithAction(VEHICLE_HORN, 10, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_DUCK, 10, JOYSTICK); IF_BTN_IN_RANGE(9) SetControllerKeyAssociatedWithAction(CAMERA_CHANGE_VIEW_ALL_SITUATIONS, 9, JOYSTICK); IF_BTN_IN_RANGE(8) SetControllerKeyAssociatedWithAction(VEHICLE_HANDBRAKE, 8, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_LOCK_TARGET, 8, JOYSTICK); IF_BTN_IN_RANGE(7) SetControllerKeyAssociatedWithAction(PED_ANSWER_PHONE, 7, JOYSTICK); SetControllerKeyAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, 7, JOYSTICK); IF_BTN_IN_RANGE(6) SetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_RIGHT, 6, JOYSTICK); SetControllerKeyAssociatedWithAction(VEHICLE_LOOKRIGHT, 6, JOYSTICK); IF_BTN_IN_RANGE(5) SetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_LEFT, 5, JOYSTICK); SetControllerKeyAssociatedWithAction(VEHICLE_LOOKLEFT, 5, JOYSTICK); /*******************************************************************************************/ IF_BTN_IN_RANGE(4) SetControllerKeyAssociatedWithAction(VEHICLE_ENTER_EXIT, 4, JOYSTICK); IF_BTN_IN_RANGE(3) SetControllerKeyAssociatedWithAction(VEHICLE_BRAKE, 3, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_JUMPING, 3, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_IN, 3, JOYSTICK); IF_BTN_IN_RANGE(2) SetControllerKeyAssociatedWithAction(VEHICLE_ACCELERATE, 2, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_SPRINT, 2, JOYSTICK); SetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_OUT, 2, JOYSTICK); IF_BTN_IN_RANGE(1) SetControllerKeyAssociatedWithAction(PED_FIREWEAPON, 1, JOYSTICK); #ifdef BIND_VEHICLE_FIREWEAPON SetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, 1, JOYSTICK); #endif /*******************************************************************************************/ } } } void CControllerConfigManager::InitialiseControllerActionNameArray() { wchar buf[ACTIONNAME_LENGTH + 2]; #define SETACTIONNAME(name) AsciiToUnicode(#name, buf); CMessages::WideStringCopy(m_aActionNames[name], buf, ACTIONNAME_LENGTH); SETACTIONNAME(PED_LOOKBEHIND); SETACTIONNAME(PED_CYCLE_WEAPON_LEFT); SETACTIONNAME(PED_CYCLE_WEAPON_RIGHT); SETACTIONNAME(PED_LOCK_TARGET); SETACTIONNAME(PED_JUMPING); SETACTIONNAME(PED_SPRINT); SETACTIONNAME(PED_CYCLE_TARGET_LEFT); SETACTIONNAME(PED_CYCLE_TARGET_RIGHT); SETACTIONNAME(PED_LOCK_TARGET); // duplicate SETACTIONNAME(PED_CENTER_CAMERA_BEHIND_PLAYER); SETACTIONNAME(VEHICLE_LOOKBEHIND); SETACTIONNAME(PED_DUCK); SETACTIONNAME(PED_ANSWER_PHONE); SETACTIONNAME(VEHICLE_LOOKLEFT); SETACTIONNAME(VEHICLE_LOOKRIGHT); SETACTIONNAME(VEHICLE_HORN); SETACTIONNAME(VEHICLE_HANDBRAKE); SETACTIONNAME(VEHICLE_ACCELERATE); SETACTIONNAME(VEHICLE_BRAKE); SETACTIONNAME(VEHICLE_CHANGE_RADIO_STATION); SETACTIONNAME(TOGGLE_SUBMISSIONS); SETACTIONNAME(PED_SNIPER_ZOOM_IN); SETACTIONNAME(PED_SNIPER_ZOOM_OUT); SETACTIONNAME(PED_1RST_PERSON_LOOK_LEFT); SETACTIONNAME(PED_1RST_PERSON_LOOK_RIGHT); SETACTIONNAME(PED_1RST_PERSON_LOOK_UP); SETACTIONNAME(PED_1RST_PERSON_LOOK_DOWN); SETACTIONNAME(SHOW_MOUSE_POINTER_TOGGLE); SETACTIONNAME(CAMERA_CHANGE_VIEW_ALL_SITUATIONS); SETACTIONNAME(PED_FIREWEAPON); #ifdef BIND_VEHICLE_FIREWEAPON SETACTIONNAME(VEHICLE_FIREWEAPON); #endif SETACTIONNAME(VEHICLE_ENTER_EXIT); SETACTIONNAME(GO_LEFT); SETACTIONNAME(GO_RIGHT); SETACTIONNAME(GO_FORWARD); SETACTIONNAME(GO_BACK); SETACTIONNAME(VEHICLE_TURRETLEFT); SETACTIONNAME(VEHICLE_TURRETRIGHT); SETACTIONNAME(VEHICLE_TURRETUP); SETACTIONNAME(VEHICLE_TURRETDOWN); SETACTIONNAME(NETWORK_TALK); SETACTIONNAME(TOGGLE_DPAD); SETACTIONNAME(SWITCH_DEBUG_CAM_ON); SETACTIONNAME(TAKE_SCREEN_SHOT); #undef SETACTIONNAME } void CControllerConfigManager::UpdateJoyInConfigMenus_ButtonDown(int32 button, int32 padnumber) { if (button != 0) { CPad *pad = CPad::GetPad(padnumber); if (pad != NULL) { switch (button) { case 16: pad->PCTempJoyState.DPadLeft = 255; break; case 15: pad->PCTempJoyState.DPadDown = 255; break; case 14: pad->PCTempJoyState.DPadRight = 255; break; case 13: pad->PCTempJoyState.DPadUp = 255; break; case 12: #ifndef REGISTER_START_BUTTON if (padnumber == 1) #endif pad->PCTempJoyState.Start = 255; break; case 11: pad->PCTempJoyState.RightShock = 255; break; case 10: pad->PCTempJoyState.LeftShock = 255; break; case 9: pad->PCTempJoyState.Select = 255; break; case 8: pad->PCTempJoyState.RightShoulder1 = 255; break; case 7: pad->PCTempJoyState.LeftShoulder1 = 255; break; case 6: pad->PCTempJoyState.RightShoulder2 = 255; break; case 5: pad->PCTempJoyState.LeftShoulder2 = 255; break; } // Now we use SDL Game Controller DB #if defined RW_D3D9 || defined RWLIBS if (AllValidWinJoys.m_aJoys[JOYSTICK1].m_nVendorID == 0x3427 && AllValidWinJoys.m_aJoys[JOYSTICK1].m_nProductID == 0x1190) #else if (0) #endif { //GIC USB Joystick, PS2 Gamepad ? switch (button) { case 4: pad->PCTempJoyState.Square = 255; break; case 3: pad->PCTempJoyState.Cross = 255; break; case 2: pad->PCTempJoyState.Circle = 255; break; case 1: pad->PCTempJoyState.Triangle = 255; break; } } else { switch (button) { case 4: pad->PCTempJoyState.Triangle = 255; break; case 3: pad->PCTempJoyState.Square = 255; break; case 2: pad->PCTempJoyState.Cross = 255; break; case 1: pad->PCTempJoyState.Circle = 255; break; } } } } } void CControllerConfigManager::AffectControllerStateOn_ButtonDown(int32 button, eControllerType type) { bool process = true; if ((type == KEYBOARD || type == OPTIONAL_EXTRA) && button == rsNULL) process = false; if (type == JOYSTICK && button == 0) process = false; if (type == MOUSE && button == 0) process = false; if (process) { CPad *pad = CPad::GetPad(PAD1); bool firstPerson = false; bool playerDriving = false; if (FindPlayerVehicle() != NULL) { CPlayerPed *plr = FindPlayerPed(); if (plr != NULL) { if (plr->m_nPedState == PED_DRIVING) playerDriving = true; } } int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if ( mode == CCam::MODE_1STPERSON || mode == CCam::MODE_SNIPER || mode == CCam::MODE_ROCKETLAUNCHER || mode == CCam::MODE_CAMERA || mode == CCam::MODE_M16_1STPERSON) { firstPerson = true; } CControllerState *state; switch (type) { case KEYBOARD: case OPTIONAL_EXTRA: state = &CPad::GetPad(PAD1)->PCTempKeyState; break; case JOYSTICK: state = &CPad::GetPad(PAD1)->PCTempJoyState; break; case MOUSE: state = &CPad::GetPad(PAD1)->PCTempMouseState; break; default: break; } if (pad != NULL) { if (playerDriving) { AffectControllerStateOn_ButtonDown_Driving(button, type, *state); AffectControllerStateOn_ButtonDown_VehicleAndThirdPersonOnly(button, type, *state); } else { AffectControllerStateOn_ButtonDown_FirstAndThirdPersonOnly(button, type, *state); if (firstPerson) AffectControllerStateOn_ButtonDown_FirstPersonOnly(button, type, *state); else { AffectControllerStateOn_ButtonDown_ThirdPersonOnly(button, type, *state); AffectControllerStateOn_ButtonDown_VehicleAndThirdPersonOnly(button, type, *state); } } AffectControllerStateOn_ButtonDown_AllStates(button, type, *state); #ifdef REGISTER_START_BUTTON if (button == 12) state->Start = 255; #endif } } } void CControllerConfigManager::AffectControllerStateOn_ButtonDown_Driving(int32 button, eControllerType type, CControllerState &state) { #ifdef BIND_VEHICLE_FIREWEAPON if (button == GetControllerKeyAssociatedWithAction(VEHICLE_FIREWEAPON, type)) state.Circle = 255; #endif if (button == GetControllerKeyAssociatedWithAction(VEHICLE_LOOKBEHIND, type)) { state.LeftShoulder2 = 255; state.RightShoulder2 = 255; } if (button == GetControllerKeyAssociatedWithAction(VEHICLE_LOOKLEFT, type)) state.LeftShoulder2 = 255; if (button == GetControllerKeyAssociatedWithAction(VEHICLE_LOOKRIGHT, type)) state.RightShoulder2 = 255; if (button == GetControllerKeyAssociatedWithAction(VEHICLE_HORN, type)) state.LeftShock = 255; if (button == GetControllerKeyAssociatedWithAction(VEHICLE_HANDBRAKE, type)) state.RightShoulder1 = 255; if (button == GetControllerKeyAssociatedWithAction(VEHICLE_ACCELERATE, type)) state.Cross = 255; if (button == GetControllerKeyAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, type)) state.LeftShoulder1 = 255; if (button == GetControllerKeyAssociatedWithAction(VEHICLE_BRAKE, type)) state.Square = 255; if (button == GetControllerKeyAssociatedWithAction(TOGGLE_SUBMISSIONS, type)) state.RightShock = 255; if (button == GetControllerKeyAssociatedWithAction(VEHICLE_TURRETLEFT, type)) { if (state.RightStickX == 128 || m_aSimCheckers[SIM_X2][type]) { state.RightStickX = 0; m_aSimCheckers[SIM_X2][type] = true; } else { state.RightStickX = -128; } } if (button == GetControllerKeyAssociatedWithAction(VEHICLE_TURRETRIGHT, type)) { if (state.RightStickX == -128 || m_aSimCheckers[SIM_X2][type]) { state.RightStickX = 0; m_aSimCheckers[SIM_X2][type] = true; } else state.RightStickX = 128; } bool isDodo = false; if (FindPlayerVehicle() && (FindPlayerVehicle()->IsVehicle() && ( FindPlayerVehicle()->GetModelIndex() == MI_DODO #ifdef FIX_BUGS || (CVehicle::bAllDodosCheat && !FindPlayerVehicle()->IsRealHeli()) #endif ))) { isDodo = true; } if (button == GetControllerKeyAssociatedWithAction(VEHICLE_TURRETUP, type)) { if (isDodo == true) { if (state.LeftStickY == -128 || m_aSimCheckers[SIM_Y1][type]) // BUG: should be SIM_Y2. SIM_Y1 it's DPAD { state.LeftStickY = 0; m_aSimCheckers[SIM_Y2][type] = true; } else state.LeftStickY = 128; } else if (state.RightStickY == -128 || m_aSimCheckers[SIM_Y2][type]) { state.RightStickY = 0; m_aSimCheckers[SIM_Y2][type] = true; } else { state.RightStickY = 128; } } if (button == GetControllerKeyAssociatedWithAction(VEHICLE_TURRETDOWN, type)) { if (isDodo == true) { if (state.LeftStickY == 128 || m_aSimCheckers[SIM_Y1][type]) // BUG: should be SIM_Y2. SIM_Y1 it's DPAD { state.LeftStickY = 0; m_aSimCheckers[SIM_Y2][type] = true; } else state.LeftStickY = -128; } else if (state.RightStickY == 128 || m_aSimCheckers[SIM_Y2][type]) { state.RightStickY = 0; m_aSimCheckers[SIM_Y2][type] = true; } else state.RightStickY = -128; } } void CControllerConfigManager::AffectControllerStateOn_ButtonDown_FirstPersonOnly(int32 button, eControllerType type, CControllerState &state) { if (button == GetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_IN, type)) state.Square = 255; if (button == GetControllerKeyAssociatedWithAction(PED_SNIPER_ZOOM_OUT, type)) state.Cross = 255; if (button == GetControllerKeyAssociatedWithAction(PED_DUCK, type)) state.LeftShock = 255; } void CControllerConfigManager::AffectControllerStateOn_ButtonDown_ThirdPersonOnly(int32 button, eControllerType type, CControllerState &state) { if (button == GetControllerKeyAssociatedWithAction(PED_LOOKBEHIND, type)) state.RightShock = 255; if (button == GetControllerKeyAssociatedWithAction(PED_JUMPING, type)) state.Square = 255; if (button == GetControllerKeyAssociatedWithAction(PED_ANSWER_PHONE, type)) state.LeftShoulder1 = 255; if (button == GetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_LEFT, type)) state.LeftShoulder2 = 255; if (button == GetControllerKeyAssociatedWithAction(PED_CYCLE_WEAPON_RIGHT, type)) state.RightShoulder2 = 255; if (button == GetControllerKeyAssociatedWithAction(PED_SPRINT, type)) state.Cross = 255; if (button == GetControllerKeyAssociatedWithAction(PED_DUCK, type)) state.LeftShock = 255; if (FrontEndMenuManager.m_ControlMethod == CONTROL_CLASSIC) { if (button == GetControllerKeyAssociatedWithAction(PED_CYCLE_TARGET_LEFT, type)) state.LeftShoulder2 = 255; if (button == GetControllerKeyAssociatedWithAction(PED_CYCLE_TARGET_RIGHT, type)) state.RightShoulder2 = 255; if (button == GetControllerKeyAssociatedWithAction(PED_CENTER_CAMERA_BEHIND_PLAYER, type)) state.LeftShoulder1 = 255; } } void CControllerConfigManager::AffectControllerStateOn_ButtonDown_FirstAndThirdPersonOnly(int32 button, eControllerType type, CControllerState &state) { CPad *pad = CPad::GetPad(PAD1); #ifdef BIND_VEHICLE_FIREWEAPON if (button == GetControllerKeyAssociatedWithAction(PED_FIREWEAPON, type)) state.Circle = 255; #endif if (button == GetControllerKeyAssociatedWithAction(PED_LOCK_TARGET, type)) state.RightShoulder1 = 255; if (button == GetControllerKeyAssociatedWithAction(GO_FORWARD, type)) { if (state.DPadDown || m_aSimCheckers[SIM_Y1][type]) { m_aSimCheckers[SIM_Y1][type] = true; state.DPadDown = 0; state.DPadUp = 0; } else state.DPadUp = 255; } if (button == GetControllerKeyAssociatedWithAction(GO_BACK, type)) { if (state.DPadUp || m_aSimCheckers[SIM_Y1][type]) { m_aSimCheckers[SIM_Y1][type] = true; state.DPadDown = 0; state.DPadUp = 0; } else state.DPadDown = 255; } if (button == GetControllerKeyAssociatedWithAction(PED_1RST_PERSON_LOOK_LEFT, type)) { if (state.RightStickX == 128 || m_aSimCheckers[SIM_X2][type]) { state.RightStickX = 0; m_aSimCheckers[SIM_X2][type] = true; } else { state.RightStickX = -128; } } if (button == GetControllerKeyAssociatedWithAction(PED_1RST_PERSON_LOOK_RIGHT, type)) { if (state.RightStickX == -128 || m_aSimCheckers[SIM_X2][type]) { state.RightStickX = 0; m_aSimCheckers[SIM_X2][type] = true; } else state.RightStickX = 128; } if (FrontEndMenuManager.m_ControlMethod == CONTROL_CLASSIC) { if (button == GetControllerKeyAssociatedWithAction(PED_1RST_PERSON_LOOK_UP, type)) { if (state.RightStickY == -128 || m_aSimCheckers[SIM_Y2][type]) { state.RightStickY = 0; m_aSimCheckers[SIM_Y2][type] = true; } else state.RightStickY = 128; } if (button == GetControllerKeyAssociatedWithAction(PED_1RST_PERSON_LOOK_DOWN, type)) { if (state.RightStickY == 128 || m_aSimCheckers[SIM_Y2][type]) { state.RightStickY = 0; m_aSimCheckers[SIM_Y2][type] = true; } else state.RightStickY = -128; } } } void CControllerConfigManager::AffectControllerStateOn_ButtonDown_AllStates(int32 button, eControllerType type, CControllerState &state) { if (button == GetControllerKeyAssociatedWithAction(CAMERA_CHANGE_VIEW_ALL_SITUATIONS, type)) state.Select = 255; #ifndef BIND_VEHICLE_FIREWEAPON if (button == GetControllerKeyAssociatedWithAction(PED_FIREWEAPON, type)) state.Circle = 255; #endif if (button == GetControllerKeyAssociatedWithAction(GO_LEFT, type)) { if (state.DPadRight || m_aSimCheckers[SIM_X1][type]) { m_aSimCheckers[SIM_X1][type] = true; state.DPadLeft = 0; state.DPadRight = 0; } else state.DPadLeft = 255; } if (button == GetControllerKeyAssociatedWithAction(GO_RIGHT, type)) { if (state.DPadLeft || m_aSimCheckers[SIM_X1][type]) { m_aSimCheckers[SIM_X1][type] = true; state.DPadLeft = 0; state.DPadRight = 0; } else state.DPadRight = 255; } if (button == GetControllerKeyAssociatedWithAction(NETWORK_TALK, type)) state.NetworkTalk = 255; } void CControllerConfigManager::AffectControllerStateOn_ButtonDown_VehicleAndThirdPersonOnly(int32 button, eControllerType type, CControllerState &state) { if (button == GetControllerKeyAssociatedWithAction(VEHICLE_ENTER_EXIT, type)) state.Triangle = 255; } void CControllerConfigManager::UpdateJoyInConfigMenus_ButtonUp(int32 button, int32 padnumber) { if (button!=0) { CPad *pad = CPad::GetPad(padnumber); if (pad != NULL) { switch (button) { case 16: pad->PCTempJoyState.DPadLeft = 0; break; case 15: pad->PCTempJoyState.DPadDown = 0; break; case 14: pad->PCTempJoyState.DPadRight = 0; break; case 13: pad->PCTempJoyState.DPadUp = 0; break; #ifdef REGISTER_START_BUTTON case 12: pad->PCTempJoyState.Start = 0; break; #endif case 11: pad->PCTempJoyState.RightShock = 0; break; case 10: pad->PCTempJoyState.LeftShock = 0; break; case 9: pad->PCTempJoyState.Select = 0; break; case 8: pad->PCTempJoyState.RightShoulder1 = 0; break; case 7: pad->PCTempJoyState.LeftShoulder1 = 0; break; case 6: pad->PCTempJoyState.RightShoulder2 = 0; break; case 5: pad->PCTempJoyState.LeftShoulder2 = 0; break; } // Now we use SDL Game Controller DB #if defined RW_D3D9 || defined RWLIBS if (AllValidWinJoys.m_aJoys[JOYSTICK1].m_nVendorID == 0x3427 && AllValidWinJoys.m_aJoys[JOYSTICK1].m_nProductID == 0x1190) #else if (0) #endif { //GIC USB Joystick, PS2 Gamepad ? switch (button) { case 4: pad->PCTempJoyState.Square = 0; break; case 3: pad->PCTempJoyState.Cross = 0; break; case 2: pad->PCTempJoyState.Circle = 0; break; case 1: pad->PCTempJoyState.Triangle = 0; break; } } else { switch (button) { case 4: pad->PCTempJoyState.Triangle = 0; break; case 3: pad->PCTempJoyState.Square = 0; break; case 2: pad->PCTempJoyState.Cross = 0; break; case 1: pad->PCTempJoyState.Circle = 0; break; } } } } } void CControllerConfigManager::AffectControllerStateOn_ButtonUp(int32 button, eControllerType type) { bool process = true; if ((type == KEYBOARD || type == OPTIONAL_EXTRA) && button == rsNULL) process = false; if (type == JOYSTICK && button == 0) process = false; if (type == MOUSE && button == 0) process = false; CControllerState *state; switch (type) { case KEYBOARD: case OPTIONAL_EXTRA: state = &CPad::GetPad(PAD1)->PCTempKeyState; break; case MOUSE: state = &CPad::GetPad(PAD1)->PCTempMouseState; break; case JOYSTICK: state = &CPad::GetPad(PAD1)->PCTempJoyState; break; default: break; } if (process) { CPad *pad = CPad::GetPad(PAD1); if (pad != NULL) { if (FrontEndMenuManager.GetIsMenuActive()) AffectControllerStateOn_ButtonUp_All_Player_States(button, type, *state); #ifdef REGISTER_START_BUTTON if (button == 12) state->Start = 0; #endif } } } void CControllerConfigManager::AffectControllerStateOn_ButtonUp_All_Player_States(int32 button, eControllerType type, CControllerState &state) { if (button == GetControllerKeyAssociatedWithAction(NETWORK_TALK, type)) state.NetworkTalk = 0; } void CControllerConfigManager::AffectPadFromKeyBoard() { RsKeyCodes kc; _InputTranslateShiftKeyUpDown(&kc); bool processdown = false; if (!CPad::m_bMapPadOneToPadTwo && !FrontEndMenuManager.GetIsMenuActive()) processdown = true; for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) { int32 key = GetControllerKeyAssociatedWithAction((e_ControllerAction)i, KEYBOARD); if (GetIsKeyboardKeyDown((RsKeyCodes)key) && processdown) AffectControllerStateOn_ButtonDown(key, KEYBOARD); int32 extrakey = GetControllerKeyAssociatedWithAction((e_ControllerAction)i, OPTIONAL_EXTRA); if (GetIsKeyboardKeyDown((RsKeyCodes)extrakey) && processdown) AffectControllerStateOn_ButtonDown(extrakey, OPTIONAL_EXTRA); if (!GetIsKeyboardKeyDown((RsKeyCodes)key)) AffectControllerStateOn_ButtonUp(key, KEYBOARD); else if ( !GetIsKeyboardKeyDown((RsKeyCodes)extrakey)) AffectControllerStateOn_ButtonUp(key, OPTIONAL_EXTRA); } } void CControllerConfigManager::AffectPadFromMouse() { bool processdown = false; if (!CPad::m_bMapPadOneToPadTwo && !FrontEndMenuManager.GetIsMenuActive()) processdown = true; for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) { int32 button = GetControllerKeyAssociatedWithAction((e_ControllerAction)i, MOUSE); if (GetIsMouseButtonDown((RsKeyCodes)button) && processdown) AffectControllerStateOn_ButtonDown(button, MOUSE); if (GetIsMouseButtonUp((RsKeyCodes)button)) AffectControllerStateOn_ButtonUp(button, MOUSE); } } void CControllerConfigManager::ClearSimButtonPressCheckers() { for (int32 i = 0; i < MAX_SIMS; i++) { m_aSimCheckers[i][KEYBOARD] = false; m_aSimCheckers[i][OPTIONAL_EXTRA] = false; m_aSimCheckers[i][MOUSE] = false; m_aSimCheckers[i][JOYSTICK] = false; } } bool CControllerConfigManager::GetIsKeyboardKeyDown(RsKeyCodes keycode) { if (keycode < 255) { if (CPad::GetPad(PAD1)->GetChar(keycode)) return true; } for (int32 i = 0; i < 12; i++) { if (i + rsF1 == keycode) { if (CPad::GetPad(PAD1)->GetF(i)) return true; } } switch (keycode) { case rsESC: if (CPad::GetPad(PAD1)->GetEscape()) return true; break; case rsINS: if (CPad::GetPad(PAD1)->GetInsert()) return true; break; case rsDEL: if (CPad::GetPad(PAD1)->GetDelete()) return true; break; case rsHOME: if (CPad::GetPad(PAD1)->GetHome()) return true; break; case rsEND: if (CPad::GetPad(PAD1)->GetEnd()) return true; break; case rsPGUP: if (CPad::GetPad(PAD1)->GetPageUp()) return true; break; case rsPGDN: if (CPad::GetPad(PAD1)->GetPageDown()) return true; break; case rsUP: if (CPad::GetPad(PAD1)->GetUp()) return true; break; case rsDOWN: if (CPad::GetPad(PAD1)->GetDown()) return true; break; case rsLEFT: if (CPad::GetPad(PAD1)->GetLeft()) return true; break; case rsRIGHT: if (CPad::GetPad(PAD1)->GetRight()) return true; break; case rsSCROLL: if (CPad::GetPad(PAD1)->GetScrollLock()) return true; break; case rsPAUSE: if (CPad::GetPad(PAD1)->GetPause()) return true; break; case rsNUMLOCK: if (CPad::GetPad(PAD1)->GetNumLock()) return true; break; case rsDIVIDE: if (CPad::GetPad(PAD1)->GetDivide()) return true; break; case rsTIMES: if (CPad::GetPad(PAD1)->GetTimes()) return true; break; case rsMINUS: if (CPad::GetPad(PAD1)->GetMinus()) return true; break; case rsPLUS: if (CPad::GetPad(PAD1)->GetPlus()) return true; break; case rsPADENTER: if (CPad::GetPad(PAD1)->GetPadEnter()) return true; break; case rsPADDEL: if (CPad::GetPad(PAD1)->GetPadDel()) return true; break; case rsPADEND: if (CPad::GetPad(PAD1)->GetPad1()) return true; break; case rsPADDOWN: if (CPad::GetPad(PAD1)->GetPad2()) return true; break; case rsPADPGDN: if (CPad::GetPad(PAD1)->GetPad3()) return true; break; case rsPADLEFT: if (CPad::GetPad(PAD1)->GetPad4()) return true; break; case rsPAD5: if (CPad::GetPad(PAD1)->GetPad5()) return true; break; case rsPADRIGHT: if (CPad::GetPad(PAD1)->GetPad6()) return true; break; case rsPADHOME: if (CPad::GetPad(PAD1)->GetPad7()) return true; break; case rsPADUP: if (CPad::GetPad(PAD1)->GetPad8()) return true; break; case rsPADPGUP: if (CPad::GetPad(PAD1)->GetPad9()) return true; break; case rsPADINS: if (CPad::GetPad(PAD1)->GetPad0()) return true; break; case rsBACKSP: if (CPad::GetPad(PAD1)->GetBackspace()) return true; break; case rsTAB: if (CPad::GetPad(PAD1)->GetTab()) return true; break; case rsCAPSLK: if (CPad::GetPad(PAD1)->GetCapsLock()) return true; break; case rsENTER: if (CPad::GetPad(PAD1)->GetEnter()) return true; break; case rsLSHIFT: if (CPad::GetPad(PAD1)->GetLeftShift()) return true; break; case rsSHIFT: if (CPad::GetPad(PAD1)->GetShift()) return true; break; case rsRSHIFT: if (CPad::GetPad(PAD1)->GetRightShift()) return true; break; case rsLCTRL: if (CPad::GetPad(PAD1)->GetLeftCtrl()) return true; break; case rsRCTRL: if (CPad::GetPad(PAD1)->GetRightCtrl()) return true; break; case rsLALT: if (CPad::GetPad(PAD1)->GetLeftAlt()) return true; break; case rsRALT: if (CPad::GetPad(PAD1)->GetRightAlt()) return true; break; case rsLWIN: if (CPad::GetPad(PAD1)->GetLeftWin()) return true; break; case rsRWIN: if (CPad::GetPad(PAD1)->GetRightWin()) return true; break; case rsAPPS: if (CPad::GetPad(PAD1)->GetApps()) return true; break; default: break; } return false; } bool CControllerConfigManager::GetIsKeyboardKeyJustDown(RsKeyCodes keycode) { if (keycode < 255) { if (CPad::GetPad(PAD1)->GetCharJustDown(keycode)) return true; } for (int32 i = 0; i < 12; i++) { if (i + rsF1 == keycode) { if (CPad::GetPad(PAD1)->GetFJustDown(i)) return true; } } switch (keycode) { case rsESC: if (CPad::GetPad(PAD1)->GetEscapeJustDown()) return true; break; case rsINS: if (CPad::GetPad(PAD1)->GetInsertJustDown()) return true; break; case rsDEL: if (CPad::GetPad(PAD1)->GetDeleteJustDown()) return true; break; case rsHOME: if (CPad::GetPad(PAD1)->GetHomeJustDown()) return true; break; case rsEND: if (CPad::GetPad(PAD1)->GetEndJustDown()) return true; break; case rsPGUP: if (CPad::GetPad(PAD1)->GetPageUpJustDown()) return true; break; case rsPGDN: if (CPad::GetPad(PAD1)->GetPageDownJustDown()) return true; break; case rsUP: if (CPad::GetPad(PAD1)->GetUpJustDown()) return true; break; case rsDOWN: if (CPad::GetPad(PAD1)->GetDownJustDown()) return true; break; case rsLEFT: if (CPad::GetPad(PAD1)->GetLeftJustDown()) return true; break; case rsRIGHT: if (CPad::GetPad(PAD1)->GetRightJustDown()) return true; break; case rsSCROLL: if (CPad::GetPad(PAD1)->GetScrollLockJustDown()) return true; break; case rsPAUSE: if (CPad::GetPad(PAD1)->GetPauseJustDown()) return true; break; case rsNUMLOCK: if (CPad::GetPad(PAD1)->GetNumLockJustDown()) return true; break; case rsDIVIDE: if (CPad::GetPad(PAD1)->GetDivideJustDown()) return true; break; case rsTIMES: if (CPad::GetPad(PAD1)->GetTimesJustDown()) return true; break; case rsMINUS: if (CPad::GetPad(PAD1)->GetMinusJustDown()) return true; break; case rsPLUS: if (CPad::GetPad(PAD1)->GetPlusJustDown()) return true; break; case rsPADENTER: if (CPad::GetPad(PAD1)->GetPadEnterJustDown()) return true; break; case rsPADDEL: if (CPad::GetPad(PAD1)->GetPadDelJustDown()) return true; break; case rsPADEND: if (CPad::GetPad(PAD1)->GetPad1JustDown()) return true; break; case rsPADDOWN: if (CPad::GetPad(PAD1)->GetPad2JustDown()) return true; break; case rsPADPGDN: if (CPad::GetPad(PAD1)->GetPad3JustDown()) return true; break; case rsPADLEFT: if (CPad::GetPad(PAD1)->GetPad4JustDown()) return true; break; case rsPAD5: if (CPad::GetPad(PAD1)->GetPad5JustDown()) return true; break; case rsPADRIGHT: if (CPad::GetPad(PAD1)->GetPad6JustDown()) return true; break; case rsPADHOME: if (CPad::GetPad(PAD1)->GetPad7JustDown()) return true; break; case rsPADUP: if (CPad::GetPad(PAD1)->GetPad8JustDown()) return true; break; case rsPADPGUP: if (CPad::GetPad(PAD1)->GetPad9JustDown()) return true; break; case rsPADINS: if (CPad::GetPad(PAD1)->GetPad0JustDown()) return true; break; case rsBACKSP: if (CPad::GetPad(PAD1)->GetBackspaceJustDown()) return true; break; case rsTAB: if (CPad::GetPad(PAD1)->GetTabJustDown()) return true; break; case rsCAPSLK: if (CPad::GetPad(PAD1)->GetCapsLockJustDown()) return true; break; case rsENTER: if (CPad::GetPad(PAD1)->GetReturnJustDown()) return true; break; case rsLSHIFT: if (CPad::GetPad(PAD1)->GetLeftShiftJustDown()) return true; break; case rsSHIFT: if (CPad::GetPad(PAD1)->GetShiftJustDown()) return true; break; case rsRSHIFT: if (CPad::GetPad(PAD1)->GetRightShiftJustDown()) return true; break; case rsLCTRL: if (CPad::GetPad(PAD1)->GetLeftCtrlJustDown()) return true; break; case rsRCTRL: if (CPad::GetPad(PAD1)->GetRightCtrlJustDown()) return true; break; case rsLALT: if (CPad::GetPad(PAD1)->GetLeftAltJustDown()) return true; break; case rsRALT: if (CPad::GetPad(PAD1)->GetRightAltJustDown()) return true; break; case rsLWIN: if (CPad::GetPad(PAD1)->GetLeftWinJustDown()) return true; break; case rsRWIN: if (CPad::GetPad(PAD1)->GetRightWinJustDown()) return true; break; case rsAPPS: if (CPad::GetPad(PAD1)->GetAppsJustDown()) return true; break; default: break; } return false; } bool CControllerConfigManager::GetIsMouseButtonDown(RsKeyCodes keycode) { switch (keycode) { case rsMOUSELEFTBUTTON: if (CPad::GetPad(PAD1)->GetLeftMouse()) return true; break; case rsMOUSMIDDLEBUTTON: if (CPad::GetPad(PAD1)->GetMiddleMouse()) return true; break; case rsMOUSERIGHTBUTTON: if (CPad::GetPad(PAD1)->GetRightMouse()) return true; break; case rsMOUSEWHEELUPBUTTON: if (CPad::GetPad(PAD1)->GetMouseWheelUp()) return true; break; case rsMOUSEWHEELDOWNBUTTON: if (CPad::GetPad(PAD1)->GetMouseWheelDown()) return true; break; case rsMOUSEX1BUTTON: if (CPad::GetPad(PAD1)->GetMouseX1()) return true; break; case rsMOUSEX2BUTTON: if (CPad::GetPad(PAD1)->GetMouseX2()) return true; break; default: break; } return false; } bool CControllerConfigManager::GetIsMouseButtonUp(RsKeyCodes keycode) { switch (keycode) { case rsMOUSELEFTBUTTON: if (CPad::GetPad(PAD1)->GetLeftMouseUp()) return true; break; case rsMOUSMIDDLEBUTTON: if (CPad::GetPad(PAD1)->GetMiddleMouseUp()) return true; break; case rsMOUSERIGHTBUTTON: if (CPad::GetPad(PAD1)->GetRightMouseUp()) return true; break; case rsMOUSEWHEELUPBUTTON: if (CPad::GetPad(PAD1)->GetMouseWheelUpUp()) return true; break; case rsMOUSEWHEELDOWNBUTTON: if (CPad::GetPad(PAD1)->GetMouseWheelDownUp()) return true; break; case rsMOUSEX1BUTTON: if (CPad::GetPad(PAD1)->GetMouseX1Up()) return true; break; case rsMOUSEX2BUTTON: if (CPad::GetPad(PAD1)->GetMouseX2Up()) return true; break; default: break; } return false; } #define CLEAR_ACTION_IF_NEEDED(action) \ if (key == GetControllerKeyAssociatedWithAction(action, type))\ ClearSettingsAssociatedWithAction(action, type); void CControllerConfigManager::DeleteMatchingCommonControls(e_ControllerAction action, int32 key, eControllerType type) { if (!GetIsKeyBlank(key, type)) { #ifndef BIND_VEHICLE_FIREWEAPON CLEAR_ACTION_IF_NEEDED(PED_FIREWEAPON); #endif CLEAR_ACTION_IF_NEEDED(GO_LEFT); CLEAR_ACTION_IF_NEEDED(GO_RIGHT); CLEAR_ACTION_IF_NEEDED(CAMERA_CHANGE_VIEW_ALL_SITUATIONS); CLEAR_ACTION_IF_NEEDED(NETWORK_TALK); CLEAR_ACTION_IF_NEEDED(SWITCH_DEBUG_CAM_ON); CLEAR_ACTION_IF_NEEDED(TOGGLE_DPAD); CLEAR_ACTION_IF_NEEDED(TAKE_SCREEN_SHOT); CLEAR_ACTION_IF_NEEDED(SHOW_MOUSE_POINTER_TOGGLE); } } void CControllerConfigManager::DeleteMatching3rdPersonControls(e_ControllerAction action, int32 key, eControllerType type) { if (!GetIsKeyBlank(key, type)) { CLEAR_ACTION_IF_NEEDED(PED_CYCLE_WEAPON_LEFT); CLEAR_ACTION_IF_NEEDED(PED_CYCLE_WEAPON_RIGHT); CLEAR_ACTION_IF_NEEDED(PED_JUMPING); CLEAR_ACTION_IF_NEEDED(PED_SPRINT); CLEAR_ACTION_IF_NEEDED(PED_LOOKBEHIND); CLEAR_ACTION_IF_NEEDED(PED_DUCK); CLEAR_ACTION_IF_NEEDED(PED_ANSWER_PHONE); if (FrontEndMenuManager.m_ControlMethod == CONTROL_CLASSIC) { CLEAR_ACTION_IF_NEEDED(PED_CYCLE_TARGET_LEFT); CLEAR_ACTION_IF_NEEDED(PED_CYCLE_TARGET_RIGHT); CLEAR_ACTION_IF_NEEDED(PED_CENTER_CAMERA_BEHIND_PLAYER); } } } void CControllerConfigManager::DeleteMatching1rst3rdPersonControls(e_ControllerAction action, int32 key, eControllerType type) { if (!GetIsKeyBlank(key, type)) { #ifdef BIND_VEHICLE_FIREWEAPON CLEAR_ACTION_IF_NEEDED(PED_FIREWEAPON); #endif CLEAR_ACTION_IF_NEEDED(PED_LOCK_TARGET); CLEAR_ACTION_IF_NEEDED(GO_FORWARD); CLEAR_ACTION_IF_NEEDED(GO_BACK); if (FrontEndMenuManager.m_ControlMethod == CONTROL_CLASSIC) { CLEAR_ACTION_IF_NEEDED(PED_1RST_PERSON_LOOK_LEFT); CLEAR_ACTION_IF_NEEDED(PED_1RST_PERSON_LOOK_RIGHT); CLEAR_ACTION_IF_NEEDED(PED_1RST_PERSON_LOOK_DOWN); CLEAR_ACTION_IF_NEEDED(PED_1RST_PERSON_LOOK_UP); } } } void CControllerConfigManager::DeleteMatchingVehicleControls(e_ControllerAction action, int32 key, eControllerType type) { if (!GetIsKeyBlank(key, type)) { #ifdef BIND_VEHICLE_FIREWEAPON CLEAR_ACTION_IF_NEEDED(VEHICLE_FIREWEAPON); #endif CLEAR_ACTION_IF_NEEDED(VEHICLE_ACCELERATE); CLEAR_ACTION_IF_NEEDED(VEHICLE_BRAKE); CLEAR_ACTION_IF_NEEDED(VEHICLE_CHANGE_RADIO_STATION); CLEAR_ACTION_IF_NEEDED(VEHICLE_HORN); CLEAR_ACTION_IF_NEEDED(TOGGLE_SUBMISSIONS); CLEAR_ACTION_IF_NEEDED(VEHICLE_HANDBRAKE); CLEAR_ACTION_IF_NEEDED(VEHICLE_LOOKLEFT); CLEAR_ACTION_IF_NEEDED(VEHICLE_LOOKRIGHT); CLEAR_ACTION_IF_NEEDED(VEHICLE_LOOKBEHIND); CLEAR_ACTION_IF_NEEDED(VEHICLE_TURRETLEFT); CLEAR_ACTION_IF_NEEDED(VEHICLE_TURRETRIGHT); CLEAR_ACTION_IF_NEEDED(VEHICLE_TURRETUP); CLEAR_ACTION_IF_NEEDED(VEHICLE_TURRETDOWN); } } void CControllerConfigManager::DeleteMatchingVehicle_3rdPersonControls(e_ControllerAction action, int32 key, eControllerType type) { if (!GetIsKeyBlank(key, type)) { CLEAR_ACTION_IF_NEEDED(VEHICLE_ENTER_EXIT); } } void CControllerConfigManager::DeleteMatching1rstPersonControls(e_ControllerAction action, int32 key, eControllerType type) { if (!GetIsKeyBlank(key, type)) { CLEAR_ACTION_IF_NEEDED(PED_SNIPER_ZOOM_IN); CLEAR_ACTION_IF_NEEDED(PED_SNIPER_ZOOM_OUT); } } #undef CLEAR_ACTION_IF_NEEDED #ifdef RADIO_SCROLL_TO_PREV_STATION #define CHECK_ACTION(action) \ if (key == GetControllerKeyAssociatedWithAction(action, type))\ return true; bool CControllerConfigManager::IsAnyVehicleActionAssignedToMouseKey(int32 key) { const eControllerType type = MOUSE; if (!GetIsKeyBlank(key, type)) { #ifdef BIND_VEHICLE_FIREWEAPON CHECK_ACTION(VEHICLE_FIREWEAPON); #endif CHECK_ACTION(VEHICLE_LOOKBEHIND); CHECK_ACTION(VEHICLE_LOOKLEFT); CHECK_ACTION(VEHICLE_LOOKRIGHT); CHECK_ACTION(VEHICLE_HORN); CHECK_ACTION(VEHICLE_HANDBRAKE); CHECK_ACTION(VEHICLE_ACCELERATE); CHECK_ACTION(VEHICLE_BRAKE); CHECK_ACTION(VEHICLE_CHANGE_RADIO_STATION); CHECK_ACTION(TOGGLE_SUBMISSIONS); CHECK_ACTION(VEHICLE_TURRETLEFT); CHECK_ACTION(VEHICLE_TURRETRIGHT); CHECK_ACTION(VEHICLE_TURRETUP); CHECK_ACTION(VEHICLE_TURRETDOWN); CHECK_ACTION(VEHICLE_ENTER_EXIT); CHECK_ACTION(CAMERA_CHANGE_VIEW_ALL_SITUATIONS); #ifndef BIND_VEHICLE_FIREWEAPON CHECK_ACTION(PED_FIREWEAPON); #endif CHECK_ACTION(GO_LEFT); CHECK_ACTION(GO_RIGHT); CHECK_ACTION(NETWORK_TALK); CHECK_ACTION(SWITCH_DEBUG_CAM_ON); CHECK_ACTION(TOGGLE_DPAD); CHECK_ACTION(TAKE_SCREEN_SHOT); CHECK_ACTION(SHOW_MOUSE_POINTER_TOGGLE); } return false; } #undef CHECK_ACTION #endif void CControllerConfigManager::DeleteMatchingActionInitiators(e_ControllerAction action, int32 key, eControllerType type) { if (!GetIsKeyBlank(key, type)) { switch (GetActionType(action)) { case ACTIONTYPE_1RSTPERSON: DeleteMatchingCommonControls (action, key, type); DeleteMatching1rstPersonControls (action, key, type); DeleteMatching1rst3rdPersonControls (action, key, type); break; case ACTIONTYPE_3RDPERSON: DeleteMatchingCommonControls (action, key, type); DeleteMatching1rst3rdPersonControls (action, key, type); DeleteMatching3rdPersonControls (action, key, type); DeleteMatchingVehicle_3rdPersonControls(action, key, type); break; case ACTIONTYPE_VEHICLE: DeleteMatchingCommonControls (action, key, type); DeleteMatchingVehicleControls (action, key, type); DeleteMatchingVehicle_3rdPersonControls(action, key, type); break; case ACTIONTYPE_VEHICLE_3RDPERSON: DeleteMatchingCommonControls (action, key, type); DeleteMatching1rst3rdPersonControls (action, key, type); DeleteMatching3rdPersonControls (action, key, type); DeleteMatchingVehicleControls (action, key, type); break; case ACTIONTYPE_COMMON: DeleteMatchingCommonControls (action, key, type); DeleteMatching1rstPersonControls (action, key, type); DeleteMatching1rst3rdPersonControls (action, key, type); DeleteMatching3rdPersonControls (action, key, type); DeleteMatchingVehicleControls (action, key, type); DeleteMatchingVehicle_3rdPersonControls(action, key, type); break; case ACTIONTYPE_1RST3RDPERSON: DeleteMatchingCommonControls (action, key, type); DeleteMatching1rstPersonControls (action, key, type); DeleteMatching1rst3rdPersonControls (action, key, type); DeleteMatching3rdPersonControls (action, key, type); DeleteMatchingVehicle_3rdPersonControls(action, key, type); break; default: break; } } } bool CControllerConfigManager::GetIsKeyBlank(int32 key, eControllerType type) { switch (type) { case KEYBOARD: case OPTIONAL_EXTRA: if (key != rsNULL) return false; break; case JOYSTICK: if (key != 0) return false; break; case MOUSE: if (key != 0) return false; break; default: break; } return true; } e_ControllerActionType CControllerConfigManager::GetActionType(e_ControllerAction action) { switch (action) { #ifndef BIND_VEHICLE_FIREWEAPON case PED_FIREWEAPON: #endif case GO_LEFT: case GO_RIGHT: case CAMERA_CHANGE_VIEW_ALL_SITUATIONS: case NETWORK_TALK: case TOGGLE_DPAD: case SWITCH_DEBUG_CAM_ON: case TAKE_SCREEN_SHOT: case SHOW_MOUSE_POINTER_TOGGLE: return ACTIONTYPE_COMMON; break; case PED_CYCLE_WEAPON_RIGHT: case PED_CYCLE_WEAPON_LEFT: case PED_JUMPING: case PED_SPRINT: case PED_LOOKBEHIND: case PED_DUCK: case PED_ANSWER_PHONE: case PED_CYCLE_TARGET_LEFT: case PED_CYCLE_TARGET_RIGHT: case PED_CENTER_CAMERA_BEHIND_PLAYER: return ACTIONTYPE_3RDPERSON; break; #ifdef BIND_VEHICLE_FIREWEAPON case VEHICLE_FIREWEAPON: #endif case VEHICLE_ACCELERATE: case VEHICLE_BRAKE: case VEHICLE_CHANGE_RADIO_STATION: case VEHICLE_HORN: case TOGGLE_SUBMISSIONS: case VEHICLE_HANDBRAKE: case VEHICLE_LOOKLEFT: case VEHICLE_LOOKRIGHT: case VEHICLE_LOOKBEHIND: case VEHICLE_TURRETLEFT: case VEHICLE_TURRETRIGHT: case VEHICLE_TURRETUP: case VEHICLE_TURRETDOWN: return ACTIONTYPE_VEHICLE; break; case VEHICLE_ENTER_EXIT: return ACTIONTYPE_VEHICLE_3RDPERSON; break; #ifdef BIND_VEHICLE_FIREWEAPON case PED_FIREWEAPON: #endif case GO_FORWARD: case GO_BACK: case PED_1RST_PERSON_LOOK_LEFT: case PED_1RST_PERSON_LOOK_RIGHT: case PED_LOCK_TARGET: case PED_1RST_PERSON_LOOK_UP: case PED_1RST_PERSON_LOOK_DOWN: return ACTIONTYPE_1RST3RDPERSON; break; case PED_SNIPER_ZOOM_IN: case PED_SNIPER_ZOOM_OUT: return ACTIONTYPE_1RSTPERSON; break; default: break; } return ACTIONTYPE_NONE; } void CControllerConfigManager::ClearSettingsAssociatedWithAction(e_ControllerAction action, eControllerType type) { switch (type) { case KEYBOARD: m_aSettings[action][type].m_Key = rsNULL; m_aSettings[action][type].m_ContSetOrder = SETORDER_NONE; break; case OPTIONAL_EXTRA: m_aSettings[action][type].m_Key = rsNULL; m_aSettings[action][type].m_ContSetOrder = SETORDER_NONE; break; case MOUSE: m_aSettings[action][type].m_Key = 0; m_aSettings[action][type].m_ContSetOrder = SETORDER_NONE; break; case JOYSTICK: m_aSettings[action][type].m_Key = 0; m_aSettings[action][type].m_ContSetOrder = SETORDER_NONE; break; default: break; } ResetSettingOrder(action); } wchar *CControllerConfigManager::GetControllerSettingTextWithOrderNumber(e_ControllerAction action, eContSetOrder setorder) { for (int i = 0; i < MAX_CONTROLLERTYPES; i++) { if (m_aSettings[action][i].m_ContSetOrder == setorder) { switch (i) { case KEYBOARD: case OPTIONAL_EXTRA: return GetControllerSettingTextKeyBoard(action, (eControllerType)i); case MOUSE: return GetControllerSettingTextMouse (action); case JOYSTICK: return GetControllerSettingTextJoystick(action); default: break; } } } return NULL; } wchar *CControllerConfigManager::GetControllerSettingTextKeyBoard(e_ControllerAction action, eControllerType type) { static wchar ActionText[50]; static wchar NewStringWithNumber[30]; for (int32 i = 0; i < ARRAY_SIZE(ActionText); i++) ActionText[i] = '\0'; if (GetControllerKeyAssociatedWithAction(action, type) != rsNULL) { if ( GetControllerKeyAssociatedWithAction(action, type) >= 0 && GetControllerKeyAssociatedWithAction(action, type) <= 255) { char c = GetControllerKeyAssociatedWithAction(action, type); if (c == ' ') return TheText.Get("FEC_SPC"); // "SPC" else { ActionText[0] = CFont::character_code(c); if (ActionText[0] == '\0') ActionText[0] = CFont::character_code('#'); ActionText[1] = '\0'; return ActionText; } } else { switch (GetControllerKeyAssociatedWithAction(action, type)) { case rsF1: case rsF2: case rsF3: case rsF4: case rsF5: case rsF6: case rsF7: case rsF8: case rsF9: case rsF10: case rsF11: case rsF12: { CMessages::InsertNumberInString(TheText.Get("FEC_FNC"), // "F~1~" GetControllerKeyAssociatedWithAction(action, type) - rsESC, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsINS: { return TheText.Get("FEC_IRT"); // "INS" break; } case rsDEL: { return TheText.Get("FEC_DLL"); // "DEL" break; } case rsHOME: { return TheText.Get("FEC_HME"); // "HOME" break; } case rsEND: { return TheText.Get("FEC_END"); // "END" break; } case rsPGUP: { return TheText.Get("FEC_PGU"); // "PGUP" break; } case rsPGDN: { return TheText.Get("FEC_PGD"); // "PGDN" break; } case rsUP: { return TheText.Get("FEC_UPA"); // "UP" break; } case rsDOWN: { return TheText.Get("FEC_DWA"); // "DOWN" break; } case rsLEFT: { return TheText.Get("FEC_LFA"); // "LEFT" break; } case rsRIGHT: { return TheText.Get("FEC_RFA"); // "RIGHT" break; } case rsDIVIDE: { return TheText.Get("FEC_FWS"); // "NUM /" break; } case rsTIMES: { return TheText.Get("FEC_STR"); // "NUM STAR" break; } case rsPLUS: { return TheText.Get("FEC_PLS"); // "NUM +" break; } case rsMINUS: { return TheText.Get("FEC_MIN"); // "NUM -" break; } case rsPADDEL: { return TheText.Get("FEC_DOT"); // "NUM ." break; } case rsPADEND: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 1, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsPADDOWN: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 2, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsPADPGDN: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 3, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsPADLEFT: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 4, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsPAD5: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 5, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsNUMLOCK: { return TheText.Get("FEC_NLK"); // "NUMLOCK" break; } case rsPADRIGHT: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 6, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsPADHOME: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 7, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsPADUP: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 8, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsPADPGUP: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 9, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsPADINS: { CMessages::InsertNumberInString(TheText.Get("FEC_NMN"), // "NUM~1~" 0, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; break; } case rsPADENTER: { return TheText.Get("FEC_ETR"); // "ENT" break; } case rsSCROLL: { return TheText.Get("FEC_SLK"); // "SCROLL LOCK" break; } case rsPAUSE: { return TheText.Get("FEC_PSB"); // "BREAK" break; } case rsBACKSP: { return TheText.Get("FEC_BSP"); // "BSPACE" break; } case rsTAB: { return TheText.Get("FEC_TAB"); // "TAB" break; } case rsCAPSLK: { return TheText.Get("FEC_CLK"); // "CAPSLOCK" break; } case rsENTER: { return TheText.Get("FEC_RTN"); // "RET" break; } case rsLSHIFT: { return TheText.Get("FEC_LSF"); // "LSHIFT" break; } case rsRSHIFT: { return TheText.Get("FEC_RSF"); // "RSHIFT" break; } case rsLCTRL: { return TheText.Get("FEC_LCT"); // "LCTRL" break; } case rsRCTRL: { return TheText.Get("FEC_RCT"); // "RCTRL" break; } case rsLALT: { return TheText.Get("FEC_LAL"); // "LALT" break; } case rsRALT: { return TheText.Get("FEC_RAL"); // "RALT" break; } case rsLWIN: { return TheText.Get("FEC_LWD"); // "LWIN" break; } case rsRWIN: { return TheText.Get("FEC_RWD"); // "RWIN" break; } case rsAPPS: { return TheText.Get("FEC_WRC"); // "WINCLICK" break; } case rsSHIFT: { return TheText.Get("FEC_SFT"); // "SHIFT" break; } default: break; } } } return NULL; } wchar *CControllerConfigManager::GetControllerSettingTextMouse(e_ControllerAction action) { switch (m_aSettings[action][MOUSE].m_Key) { case 1: return TheText.Get("FEC_MSL"); // LMB break; case 2: return TheText.Get("FEC_MSM"); // MMB break; case 3: return TheText.Get("FEC_MSR"); // RMB break; case 4: return TheText.Get("FEC_MWF"); // WHEEL UP break; case 5: return TheText.Get("FEC_MWB"); // WHEEL DN break; case 6: return TheText.Get("FEC_MXO"); // MXB1 break; case 7: return TheText.Get("FEC_MXT"); // MXB2 break; default: break; } return NULL; } wchar *CControllerConfigManager::GetControllerSettingTextJoystick(e_ControllerAction action) { if (m_aSettings[action][JOYSTICK].m_Key == 0) return NULL; static wchar NewStringWithNumber[30]; CMessages::InsertNumberInString(TheText.Get("FEC_JBO"), // JOY ~1~ m_aSettings[action][JOYSTICK].m_Key, -1, -1, -1, -1, -1, NewStringWithNumber); return NewStringWithNumber; } int32 CControllerConfigManager::GetNumOfSettingsForAction(e_ControllerAction action) { int32 num = 0; if (m_aSettings[action][KEYBOARD].m_Key != rsNULL) num++; if (m_aSettings[action][OPTIONAL_EXTRA].m_Key != rsNULL) num++; if (m_aSettings[action][MOUSE].m_Key != 0) num++; if (m_aSettings[action][JOYSTICK].m_Key != 0) num++; return num; } #ifdef BIND_VEHICLE_FIREWEAPON #define VFB(b) b, #else #define VFB(b) #endif #define CONTROLLER_BUTTONS(T, O, X, Q, L1, L2, L3, R1, R2, R3, SELECT, RSU, RSD, RSL, RSR) \ {{ \ O, /* PED_FIREWEAPON */ \ R2, /* PED_CYCLE_WEAPON_RIGHT */ \ L2, /* PED_CYCLE_WEAPON_LEFT */ \ nil, /* GO_FORWARD */ \ nil, /* GO_BACK */ \ LEFT, /* GO_LEFT */ \ RIGHT, /* GO_RIGHT */ \ Q, /* PED_SNIPER_ZOOM_IN */ \ X, /* PED_SNIPER_ZOOM_OUT */ \ T, /* VEHICLE_ENTER_EXIT */ \ SELECT, /* CAMERA_CHANGE_VIEW_ALL_SITUATIONS */ \ Q, /* PED_JUMPING */ \ X, /* PED_SPRINT */ \ R3, /* PED_LOOKBEHIND */ \ L3, /* PED_DUCK */ \ L1, /* PED_ANSWER_PHONE */ \ VFB(O) /* VEHICLE_FIREWEAPON */ \ X, /* VEHICLE_ACCELERATE */ \ Q, /* VEHICLE_BRAKE */ \ L1, /* VEHICLE_CHANGE_RADIO_STATION */ \ L3, /* VEHICLE_HORN */ \ R3, /* TOGGLE_SUBMISSIONS */ \ R1, /* VEHICLE_HANDBRAKE */ \ nil, /* PED_1RST_PERSON_LOOK_LEFT */ \ nil, /* PED_1RST_PERSON_LOOK_RIGHT */ \ L2, /* VEHICLE_LOOKLEFT */ \ R2, /* VEHICLE_LOOKRIGHT */ \ nil, /* VEHICLE_LOOKBEHIND */ \ RSL, /* VEHICLE_TURRETLEFT */ \ RSR, /* VEHICLE_TURRETRIGHT */ \ UP, /* VEHICLE_TURRETUP */ \ DOWN, /* VEHICLE_TURRETDOWN */ \ L2, /* PED_CYCLE_TARGET_LEFT */ \ R2, /* PED_CYCLE_TARGET_RIGHT */ \ L1, /* PED_CENTER_CAMERA_BEHIND_PLAYER */ \ R1, /* PED_LOCK_TARGET */ \ nil, /* NETWORK_TALK */ \ nil, /* PED_1RST_PERSON_LOOK_UP */ \ nil, /* PED_1RST_PERSON_LOOK_DOWN */ \ nil, /* _CONTROLLERACTION_36 */ \ nil, /* TOGGLE_DPAD */ \ nil, /* SWITCH_DEBUG_CAM_ON */ \ nil, /* TAKE_SCREEN_SHOT */ \ nil, /* UNKNOWN_ACTION */ \ nil, /* SHOW_MOUSE_POINTER_TOGGLE */ \ }, \ { \ O, /* PED_FIREWEAPON */ \ R2, /* PED_CYCLE_WEAPON_RIGHT */ \ L2, /* PED_CYCLE_WEAPON_LEFT */ \ nil, /* GO_FORWARD */ \ nil, /* GO_BACK */ \ LEFT, /* GO_LEFT */ \ RIGHT, /* GO_RIGHT */ \ Q, /* PED_SNIPER_ZOOM_IN */ \ X, /* PED_SNIPER_ZOOM_OUT */ \ T, /* VEHICLE_ENTER_EXIT */ \ SELECT, /* CAMERA_CHANGE_VIEW_ALL_SITUATIONS */ \ Q, /* PED_JUMPING */ \ X, /* PED_SPRINT */ \ R3, /* PED_LOOKBEHIND */ \ L3, /* PED_DUCK */ \ L1, /* PED_ANSWER_PHONE */ \ VFB(O) /* VEHICLE_FIREWEAPON */ \ X, /* VEHICLE_ACCELERATE */ \ Q, /* VEHICLE_BRAKE */ \ SELECT, /* VEHICLE_CHANGE_RADIO_STATION */ \ L1, /* VEHICLE_HORN */ \ R3, /* TOGGLE_SUBMISSIONS */ \ R1, /* VEHICLE_HANDBRAKE */ \ nil, /* PED_1RST_PERSON_LOOK_LEFT */ \ nil, /* PED_1RST_PERSON_LOOK_RIGHT */ \ L2, /* VEHICLE_LOOKLEFT */ \ R2, /* VEHICLE_LOOKRIGHT */ \ nil, /* VEHICLE_LOOKBEHIND */ \ RSL, /* VEHICLE_TURRETLEFT */ \ RSR, /* VEHICLE_TURRETRIGHT */ \ UP, /* VEHICLE_TURRETUP */ \ DOWN, /* VEHICLE_TURRETDOWN */ \ L2, /* PED_CYCLE_TARGET_LEFT */ \ R2, /* PED_CYCLE_TARGET_RIGHT */ \ L1, /* PED_CENTER_CAMERA_BEHIND_PLAYER */ \ R1, /* PED_LOCK_TARGET */ \ nil, /* NETWORK_TALK */ \ nil, /* PED_1RST_PERSON_LOOK_UP */ \ nil, /* PED_1RST_PERSON_LOOK_DOWN */ \ nil, /* _CONTROLLERACTION_36 */ \ nil, /* TOGGLE_DPAD */ \ nil, /* SWITCH_DEBUG_CAM_ON */ \ nil, /* TAKE_SCREEN_SHOT */ \ nil, /* UNKNOWN_ACTION */ \ nil, /* SHOW_MOUSE_POINTER_TOGGLE */ \ }, \ { \ X, /* PED_FIREWEAPON */ \ R2, /* PED_CYCLE_WEAPON_RIGHT */ \ L2, /* PED_CYCLE_WEAPON_LEFT */ \ nil, /* GO_FORWARD */ \ nil, /* GO_BACK */ \ LEFT, /* GO_LEFT */ \ RIGHT, /* GO_RIGHT */ \ T, /* PED_SNIPER_ZOOM_IN */ \ Q, /* PED_SNIPER_ZOOM_OUT */ \ L1, /* VEHICLE_ENTER_EXIT */ \ SELECT, /* CAMERA_CHANGE_VIEW_ALL_SITUATIONS */ \ Q, /* PED_JUMPING */ \ O, /* PED_SPRINT */ \ R3, /* PED_LOOKBEHIND */ \ L3, /* PED_DUCK */ \ T, /* PED_ANSWER_PHONE */ \ VFB(O) /* VEHICLE_FIREWEAPON */ \ X, /* VEHICLE_ACCELERATE */ \ Q, /* VEHICLE_BRAKE */ \ L3, /* VEHICLE_CHANGE_RADIO_STATION */ \ R1, /* VEHICLE_HORN */ \ R3, /* TOGGLE_SUBMISSIONS */ \ T, /* VEHICLE_HANDBRAKE */ \ nil, /* PED_1RST_PERSON_LOOK_LEFT */ \ nil, /* PED_1RST_PERSON_LOOK_RIGHT */ \ L2, /* VEHICLE_LOOKLEFT */ \ R2, /* VEHICLE_LOOKRIGHT */ \ nil, /* VEHICLE_LOOKBEHIND */ \ RSL, /* VEHICLE_TURRETLEFT */ \ RSR, /* VEHICLE_TURRETRIGHT */ \ UP, /* VEHICLE_TURRETUP */ \ DOWN, /* VEHICLE_TURRETDOWN */ \ L2, /* PED_CYCLE_TARGET_LEFT */ \ R2, /* PED_CYCLE_TARGET_RIGHT */ \ T, /* PED_CENTER_CAMERA_BEHIND_PLAYER */ \ R1, /* PED_LOCK_TARGET */ \ nil, /* NETWORK_TALK */ \ nil, /* PED_1RST_PERSON_LOOK_UP */ \ nil, /* PED_1RST_PERSON_LOOK_DOWN */ \ nil, /* _CONTROLLERACTION_36 */ \ nil, /* TOGGLE_DPAD */ \ nil, /* SWITCH_DEBUG_CAM_ON */ \ nil, /* TAKE_SCREEN_SHOT */ \ nil, /* UNKNOWN_ACTION */ \ nil, /* SHOW_MOUSE_POINTER_TOGGLE */ \ }, \ { \ R1, /* PED_FIREWEAPON */ \ R2, /* PED_CYCLE_WEAPON_RIGHT */ \ L2, /* PED_CYCLE_WEAPON_LEFT */ \ nil, /* GO_FORWARD */ \ nil, /* GO_BACK */ \ LEFT, /* GO_LEFT */ \ RIGHT, /* GO_RIGHT */ \ Q, /* PED_SNIPER_ZOOM_IN */ \ X, /* PED_SNIPER_ZOOM_OUT */ \ T, /* VEHICLE_ENTER_EXIT */ \ SELECT, /* CAMERA_CHANGE_VIEW_ALL_SITUATIONS */ \ Q, /* PED_JUMPING */ \ X, /* PED_SPRINT */ \ R3, /* PED_LOOKBEHIND */ \ L3, /* PED_DUCK */ \ O, /* PED_ANSWER_PHONE */ \ VFB(R1) /* VEHICLE_FIREWEAPON */ \ RSU, /* VEHICLE_ACCELERATE */ \ RSD, /* VEHICLE_BRAKE */ \ O, /* VEHICLE_CHANGE_RADIO_STATION */ \ L3, /* VEHICLE_HORN */ \ Q, /* TOGGLE_SUBMISSIONS */ \ L1, /* VEHICLE_HANDBRAKE */ \ nil, /* PED_1RST_PERSON_LOOK_LEFT */ \ nil, /* PED_1RST_PERSON_LOOK_RIGHT */ \ L2, /* VEHICLE_LOOKLEFT */ \ R2, /* VEHICLE_LOOKRIGHT */ \ nil, /* VEHICLE_LOOKBEHIND */ \ RSL, /* VEHICLE_TURRETLEFT */ \ RSR, /* VEHICLE_TURRETRIGHT */ \ UP, /* VEHICLE_TURRETUP */ \ DOWN, /* VEHICLE_TURRETDOWN */ \ L2, /* PED_CYCLE_TARGET_LEFT */ \ R2, /* PED_CYCLE_TARGET_RIGHT */ \ O, /* PED_CENTER_CAMERA_BEHIND_PLAYER */ \ L1, /* PED_LOCK_TARGET */ \ nil, /* NETWORK_TALK */ \ nil, /* PED_1RST_PERSON_LOOK_UP */ \ nil, /* PED_1RST_PERSON_LOOK_DOWN */ \ nil, /* _CONTROLLERACTION_36 */ \ nil, /* TOGGLE_DPAD */ \ nil, /* SWITCH_DEBUG_CAM_ON */ \ nil, /* TAKE_SCREEN_SHOT */ \ nil, /* UNKNOWN_ACTION */ \ nil, /* SHOW_MOUSE_POINTER_TOGGLE */ \ }} #ifdef BUTTON_ICONS #define UP "~U~" #define DOWN "~D~" #define LEFT "~<~" #define RIGHT "~>~" #else #define UP "UP" #define DOWN "DOWN" #define LEFT "LEFT" #define RIGHT "RIGHT" #endif const char *XboxButtons_noIcons[][MAX_CONTROLLERACTIONS] = CONTROLLER_BUTTONS("Y", "B", "A", "X", "LB", "LT", "LS", "RB", "RT", "RS", "BACK", "right stick up", "right stick down", "right stick left", "right stick right"); #ifdef BUTTON_ICONS const char *XboxButtons[][MAX_CONTROLLERACTIONS] = CONTROLLER_BUTTONS("~T~", "~O~", "~X~", "~Q~", "~K~", "~M~", "~A~", "~J~", "~V~", "~C~", "BACK", "~H~", "~L~", "~(~", "~)~"); #endif #if 0 // set 1 for ps2 fonts #define PS2_TRIANGLE "\"" #define PS2_CIRCLE "|" #define PS2_CROSS "/" #define PS2_SQUARE "^" #else #define PS2_TRIANGLE "TRIANGLE" #define PS2_CIRCLE "CIRCLE" #define PS2_CROSS "CROSS" #define PS2_SQUARE "SQUARE" #endif const char *PlayStationButtons_noIcons[][MAX_CONTROLLERACTIONS] = CONTROLLER_BUTTONS(PS2_TRIANGLE, PS2_CIRCLE, PS2_CROSS, PS2_SQUARE, "L1", "L2", "L3", "R1", "R2", "R3", "SELECT", "right stick up", "right stick down", "right stick left", "right stick right"); #ifdef BUTTON_ICONS const char *PlayStationButtons[][MAX_CONTROLLERACTIONS] = CONTROLLER_BUTTONS("~T~", "~O~", "~X~", "~Q~", "~K~", "~M~", "~A~", "~J~", "~V~", "~C~", "SELECT", "~H~", "~L~", "~(~", "~)~"); #endif #undef PS2_TRIANGLE #undef PS2_CIRCLE #undef PS2_CROSS #undef PS2_SQUARE #undef UP #undef DOWN #undef LEFT #undef RIGHT #undef CONTROLLER_BUTTONS #undef VFB void CControllerConfigManager::GetWideStringOfCommandKeys(uint16 action, wchar *text, uint16 leight) { #ifdef DETECT_PAD_INPUT_SWITCH if (CPad::GetPad(0)->IsAffectedByController) { wchar wstr[16]; const char* (*Buttons)[MAX_CONTROLLERACTIONS]; #ifdef BUTTON_ICONS #ifdef GAMEPAD_MENU switch (FrontEndMenuManager.m_PrefsControllerType) { case CMenuManager::CONTROLLER_DUALSHOCK2: case CMenuManager::CONTROLLER_DUALSHOCK3: case CMenuManager::CONTROLLER_DUALSHOCK4: Buttons = CFont::ButtonsSlot != -1 ? PlayStationButtons : PlayStationButtons_noIcons; break; default: #endif Buttons = CFont::ButtonsSlot != -1 ? XboxButtons : XboxButtons_noIcons; #ifdef GAMEPAD_MENU break; } #endif #else switch (FrontEndMenuManager.m_PrefsControllerType) { case CMenuManager::CONTROLLER_DUALSHOCK2: case CMenuManager::CONTROLLER_DUALSHOCK3: case CMenuManager::CONTROLLER_DUALSHOCK4: Buttons = PlayStationButtons_noIcons; break; default: Buttons = XboxButtons_noIcons; break; } #endif assert(Buttons[CPad::GetPad(0)->Mode][action] != nil); // we cannot use these AsciiToUnicode(Buttons[CPad::GetPad(0)->Mode][action], wstr); CMessages::WideStringCopy(text, wstr, leight); return; } #endif int32 nums = GetNumOfSettingsForAction((e_ControllerAction)action); int32 sets = 0; for (int32 i = SETORDER_1; i < MAX_SETORDERS; i++) { wchar *textorder = ControlsManager.GetControllerSettingTextWithOrderNumber((e_ControllerAction)action, (eContSetOrder)i); if (textorder != NULL) { uint16 len = CMessages::GetWideStringLength(text); CMessages::WideStringCopy(&text[len], textorder, leight - len); if (++sets < nums) { if (sets == nums - 1) { // if last - text += ' or '; uint16 pos1 = CMessages::GetWideStringLength(text); text[pos1] = ' '; CMessages::WideStringCopy(&text[pos1 + 1], TheText.Get("FEC_ORR"), // "or" leight - (pos1 + 1)); uint16 pos2 = CMessages::GetWideStringLength(text); text[pos2 + 0] = ' '; text[pos2 + 1] = '\0'; } else { // text += ', '; uint16 pos1 = CMessages::GetWideStringLength(text); text[pos1 + 0] = ','; text[pos1 + 1] = ' '; text[pos1 + 2] = '\0'; uint16 pos2 = CMessages::GetWideStringLength(text); } } } } } int32 CControllerConfigManager::GetControllerKeyAssociatedWithAction(e_ControllerAction action, eControllerType type) { return m_aSettings[action][type].m_Key; } void CControllerConfigManager::UpdateJoyButtonState(int32 padnumber) { for (int32 i = 0; i < MAX_BUTTONS; i++) m_aButtonStates[i] = false; #ifdef __DINPUT_INCLUDED__ for (int32 i = 0; i < MAX_BUTTONS; i++) { if (m_NewState.rgbButtons[i] & 0x80) m_aButtonStates[i] = true; else m_aButtonStates[i] = false; } #elif defined RW_GL3 if (m_NewState.isGamepad) { for (int32 i = 0; i < MAX_BUTTONS; i++) { if (i == GLFW_GAMEPAD_BUTTON_GUIDE) continue; m_aButtonStates[MapIdToButtonId(i)-1] = m_NewState.mappedButtons[i]; } } else { for (int32 i = 0; i < Min(m_NewState.numButtons, MAX_BUTTONS); i++) { m_aButtonStates[i] = m_NewState.buttons[i]; } } #endif } bool CControllerConfigManager::GetIsActionAButtonCombo(e_ControllerAction action) { switch (action) { case VEHICLE_LOOKBEHIND: case PED_CYCLE_TARGET_LEFT: case PED_CYCLE_TARGET_RIGHT: return true; break; default: break; } return false; } wchar *CControllerConfigManager::GetButtonComboText(e_ControllerAction action) { switch (action) { case PED_CYCLE_TARGET_LEFT: return TheText.Get("FEC_PTL"); // Use LockTarget with Weapon Switch Left. break; case PED_CYCLE_TARGET_RIGHT: return TheText.Get("FEC_PTR"); // Use LockTarget with Weapon Switch Right. break; case VEHICLE_LOOKBEHIND: return TheText.Get("FEC_LBC"); // Use Look Left With Look Right. break; default: break; } return NULL; } void CControllerConfigManager::SetControllerKeyAssociatedWithAction(e_ControllerAction action, int32 key, eControllerType type) { ResetSettingOrder(action); int numOfSettings = GetNumOfSettingsForAction(action); m_aSettings[action][type].m_Key = key; m_aSettings[action][type].m_ContSetOrder = numOfSettings + 1; } int32 CControllerConfigManager::GetMouseButtonAssociatedWithAction(e_ControllerAction action) { return m_aSettings[action][MOUSE].m_Key; } void CControllerConfigManager::SetMouseButtonAssociatedWithAction(e_ControllerAction action, int32 button) { int numOfSettings = GetNumOfSettingsForAction(action); m_aSettings[action][MOUSE].m_Key = button; m_aSettings[action][MOUSE].m_ContSetOrder = numOfSettings + 1; } void CControllerConfigManager::ResetSettingOrder(e_ControllerAction action) { int32 conttype = KEYBOARD; for (int32 i = SETORDER_1; i < MAX_SETORDERS; i++) { bool isexist = false; for (int32 j = 0; j < MAX_CONTROLLERTYPES; j++) { if (m_aSettings[action][j].m_ContSetOrder == i) isexist = true; } bool init = false; if (!isexist) { for (int32 k = 0; k < MAX_CONTROLLERTYPES; k++) { int32 setorder = m_aSettings[action][k].m_ContSetOrder; if (setorder > i && setorder != 0) { if (init) { if (setorder < m_aSettings[action][conttype].m_ContSetOrder) conttype = k; } else { init = true; conttype = k; } } } if (init) m_aSettings[action][conttype].m_ContSetOrder = i; } } } ================================================ FILE: src/core/ControllerConfig.h ================================================ #pragma once #if defined RW_D3D9 || defined RWLIBS #define DIRECTINPUT_VERSION 0x0800 #include #endif // based on x-gtasa enum eControllerType { KEYBOARD = 0, OPTIONAL_EXTRA, MOUSE, JOYSTICK, MAX_CONTROLLERTYPES, }; enum e_ControllerAction { PED_FIREWEAPON = 0, PED_CYCLE_WEAPON_RIGHT, PED_CYCLE_WEAPON_LEFT, GO_FORWARD, GO_BACK, GO_LEFT, GO_RIGHT, PED_SNIPER_ZOOM_IN, PED_SNIPER_ZOOM_OUT, VEHICLE_ENTER_EXIT, CAMERA_CHANGE_VIEW_ALL_SITUATIONS, PED_JUMPING, PED_SPRINT, PED_LOOKBEHIND, PED_DUCK, PED_ANSWER_PHONE, #ifdef BIND_VEHICLE_FIREWEAPON VEHICLE_FIREWEAPON, #endif VEHICLE_ACCELERATE, VEHICLE_BRAKE, VEHICLE_CHANGE_RADIO_STATION, VEHICLE_HORN, TOGGLE_SUBMISSIONS, VEHICLE_HANDBRAKE, PED_1RST_PERSON_LOOK_LEFT, PED_1RST_PERSON_LOOK_RIGHT, VEHICLE_LOOKLEFT, VEHICLE_LOOKRIGHT, VEHICLE_LOOKBEHIND, VEHICLE_TURRETLEFT, VEHICLE_TURRETRIGHT, VEHICLE_TURRETUP, VEHICLE_TURRETDOWN, PED_CYCLE_TARGET_LEFT, PED_CYCLE_TARGET_RIGHT, PED_CENTER_CAMERA_BEHIND_PLAYER, PED_LOCK_TARGET, NETWORK_TALK, PED_1RST_PERSON_LOOK_UP, PED_1RST_PERSON_LOOK_DOWN, _CONTROLLERACTION_36, // Unused TOGGLE_DPAD, SWITCH_DEBUG_CAM_ON, TAKE_SCREEN_SHOT, SHOW_MOUSE_POINTER_TOGGLE, UNKNOWN_ACTION, MAX_CONTROLLERACTIONS, }; enum e_ControllerActionType { ACTIONTYPE_1RSTPERSON = 0, ACTIONTYPE_3RDPERSON, ACTIONTYPE_VEHICLE, ACTIONTYPE_VEHICLE_3RDPERSON, ACTIONTYPE_COMMON, ACTIONTYPE_1RST3RDPERSON, ACTIONTYPE_NONE, }; enum eContSetOrder { SETORDER_NONE = 0, SETORDER_1, SETORDER_2, SETORDER_3, SETORDER_4, MAX_SETORDERS, }; enum eSimCheckers { SIM_X1 = 0, SIM_Y1, // DPad SIM_X2, SIM_Y2, // LeftStick MAX_SIMS }; class CMouseControllerState; class CControllerState; #define JOY_BUTTONS 16 #define MAX_BUTTONS (JOY_BUTTONS+1) #define ACTIONNAME_LENGTH 40 #ifdef RW_GL3 struct GlfwJoyState { int8 id; bool isGamepad; uint8 numButtons; uint8* buttons; bool mappedButtons[17]; }; #endif class CControllerConfigManager { public: struct tControllerConfigBind { int32 m_Key; int32 m_ContSetOrder; tControllerConfigBind() { m_Key = 0; m_ContSetOrder = 0; } }; bool m_bFirstCapture; #if defined RW_GL3 GlfwJoyState m_OldState; GlfwJoyState m_NewState; #else DIJOYSTATE2 m_OldState; DIJOYSTATE2 m_NewState; #endif wchar m_aActionNames[MAX_CONTROLLERACTIONS][ACTIONNAME_LENGTH]; bool m_aButtonStates[MAX_BUTTONS]; tControllerConfigBind m_aSettings[MAX_CONTROLLERACTIONS][MAX_CONTROLLERTYPES]; bool m_aSimCheckers[MAX_SIMS][MAX_CONTROLLERTYPES]; bool m_bMouseAssociated; #ifdef LOAD_INI_SETTINGS static uint32 ms_padButtonsInited; #endif CControllerConfigManager(); void MakeControllerActionsBlank(); int32 GetJoyButtonJustDown(); void SaveSettings(int32 file); void LoadSettings(int32 file); void InitDefaultControlConfiguration(); void InitDefaultControlConfigMouse(CMouseControllerState const &availableButtons); void InitDefaultControlConfigJoyPad(uint32 buttons); void InitialiseControllerActionNameArray(); void UpdateJoyInConfigMenus_ButtonDown (int32 button, int32 padnumber); void AffectControllerStateOn_ButtonDown (int32 button, eControllerType type); void AffectControllerStateOn_ButtonDown_Driving (int32 button, eControllerType type, CControllerState &state); void AffectControllerStateOn_ButtonDown_FirstPersonOnly (int32 button, eControllerType type, CControllerState &state); void AffectControllerStateOn_ButtonDown_ThirdPersonOnly (int32 button, eControllerType type, CControllerState &state); void AffectControllerStateOn_ButtonDown_FirstAndThirdPersonOnly (int32 button, eControllerType type, CControllerState &state); void AffectControllerStateOn_ButtonDown_AllStates (int32 button, eControllerType type, CControllerState &state); void AffectControllerStateOn_ButtonDown_VehicleAndThirdPersonOnly(int32 button, eControllerType type, CControllerState &state); void UpdateJoyInConfigMenus_ButtonUp(int32 button, int32 padnumber); void AffectControllerStateOn_ButtonUp(int32 button, eControllerType type); void AffectControllerStateOn_ButtonUp_All_Player_States(int32 button, eControllerType type, CControllerState &state); void AffectPadFromKeyBoard(); void AffectPadFromMouse(); void ClearSimButtonPressCheckers(); bool GetIsKeyboardKeyDown (RsKeyCodes keycode); bool GetIsKeyboardKeyJustDown(RsKeyCodes keycode); bool GetIsMouseButtonDown (RsKeyCodes keycode); bool GetIsMouseButtonUp (RsKeyCodes keycode); void DeleteMatchingCommonControls (e_ControllerAction action, int32 key, eControllerType type); void DeleteMatching3rdPersonControls (e_ControllerAction action, int32 key, eControllerType type); void DeleteMatching1rst3rdPersonControls (e_ControllerAction action, int32 key, eControllerType type); void DeleteMatchingVehicleControls (e_ControllerAction action, int32 key, eControllerType type); void DeleteMatchingVehicle_3rdPersonControls(e_ControllerAction action, int32 key, eControllerType type); void DeleteMatching1rstPersonControls (e_ControllerAction action, int32 key, eControllerType type); void DeleteMatchingActionInitiators (e_ControllerAction action, int32 key, eControllerType type); #ifdef RADIO_SCROLL_TO_PREV_STATION bool IsAnyVehicleActionAssignedToMouseKey(int32 key); #endif bool GetIsKeyBlank(int32 key, eControllerType type); e_ControllerActionType GetActionType(e_ControllerAction action); void ClearSettingsAssociatedWithAction (e_ControllerAction action, eControllerType type); wchar *GetControllerSettingTextWithOrderNumber(e_ControllerAction action, eContSetOrder setorder); wchar *GetControllerSettingTextKeyBoard (e_ControllerAction action, eControllerType type); wchar *GetControllerSettingTextMouse (e_ControllerAction action); wchar *GetControllerSettingTextJoystick (e_ControllerAction action); int32 GetNumOfSettingsForAction(e_ControllerAction action); void GetWideStringOfCommandKeys(uint16 action, wchar *text, uint16 leight); int32 GetControllerKeyAssociatedWithAction(e_ControllerAction action, eControllerType type); void UpdateJoyButtonState(int32 padnumber); bool GetIsActionAButtonCombo (e_ControllerAction action); wchar *GetButtonComboText (e_ControllerAction action); void SetControllerKeyAssociatedWithAction(e_ControllerAction action, int32 key, eControllerType type); int32 GetMouseButtonAssociatedWithAction (e_ControllerAction action); void SetMouseButtonAssociatedWithAction (e_ControllerAction action, int32 button); void ResetSettingOrder (e_ControllerAction action); }; #ifndef RW_GL3 VALIDATE_SIZE(CControllerConfigManager, 0x143C); #endif extern CControllerConfigManager ControlsManager; ================================================ FILE: src/core/Crime.h ================================================ #pragma once enum eCrimeType { CRIME_NONE, CRIME_POSSESSION_GUN, CRIME_HIT_PED, CRIME_HIT_COP, CRIME_SHOOT_PED, CRIME_SHOOT_COP, CRIME_STEAL_CAR, CRIME_RUN_REDLIGHT, CRIME_RECKLESS_DRIVING, CRIME_SPEEDING, CRIME_RUNOVER_PED, CRIME_RUNOVER_COP, CRIME_SHOOT_HELI, CRIME_PED_BURNED, CRIME_COP_BURNED, CRIME_VEHICLE_BURNED, CRIME_DESTROYED_CESSNA, CRIME_EXPLOSION, CRIME_HIT_PED_NASTYWEAPON, CRIME_HIT_COP_NASTYWEAPON, NUM_CRIME_TYPES }; class CCrimeBeingQd { public: eCrimeType m_nType; uint32 m_nId; uint32 m_nTime; CVector m_vecPosn; bool m_bReported; bool m_bPoliceDoesntCare; CCrimeBeingQd() { }; ~CCrimeBeingQd() { }; }; ================================================ FILE: src/core/Debug.cpp ================================================ #include "common.h" #include "RwHelper.h" #include "Debug.h" #include "Lines.h" #include "Font.h" #include "main.h" #include "Text.h" bool gbDebugStuffInRelease = false; #define DEBUG_X_POS (300) #define DEBUG_Y_POS (41) #define DEBUG_LINE_HEIGHT (22) int16 CDebug::ms_nCurrentTextLine; char CDebug::ms_aTextBuffer[MAX_LINES][MAX_STR_LEN]; void CDebug::DebugInitTextBuffer() { ms_nCurrentTextLine = 0; } void CDebug::DebugAddText(const char *str) { int32 i = 0; if (*str != '\0') { while (i < MAX_STR_LEN - 1) { ms_aTextBuffer[ms_nCurrentTextLine][i++] = *(str++); if (*str == '\0') break; } } ms_aTextBuffer[ms_nCurrentTextLine++][i] = '\0'; if (ms_nCurrentTextLine >= MAX_LINES) ms_nCurrentTextLine = 0; } void CDebug::DebugDisplayTextBuffer() { #ifndef MASTER if (gbDebugStuffInRelease) { int32 i = 0; int32 y = DEBUG_Y_POS; #ifdef FIX_BUGS CFont::SetPropOn(); CFont::SetBackgroundOff(); CFont::SetScale(1.0f, 1.0f); CFont::SetCentreOff(); CFont::SetRightJustifyOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_STANDARD); #else // this is not even readable CFont::SetPropOff(); CFont::SetBackgroundOff(); CFont::SetScale(1.0f, 1.0f); CFont::SetCentreOff(); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_STANDARD); CFont::SetPropOff(); #endif do { char *line; while (true) { line = ms_aTextBuffer[(ms_nCurrentTextLine + i++) % MAX_LINES]; if (*line != '\0') break; y += DEBUG_LINE_HEIGHT; if (i == MAX_LINES) { CFont::DrawFonts(); return; } } AsciiToUnicode(line, gUString); CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString(DEBUG_X_POS, y-1, gUString); CFont::SetColor(CRGBA(255, 128, 128, 255)); CFont::PrintString(DEBUG_X_POS+1, y, gUString); y += DEBUG_LINE_HEIGHT; } while (i != MAX_LINES); CFont::DrawFonts(); } #endif } // custom CDebug::ScreenStr CDebug::ms_aScreenStrs[MAX_SCREEN_STRS]; int CDebug::ms_nScreenStrs; void CDebug::DisplayScreenStrings() { int i; CFont::SetPropOn(); CFont::SetBackgroundOff(); CFont::SetScale(1.0f, 1.0f); CFont::SetCentreOff(); CFont::SetRightJustifyOff(); CFont::SetJustifyOff(); CFont::SetRightJustifyWrap(0.0f); CFont::SetWrapx(9999.0f); CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_STANDARD); for(i = 0; i < ms_nScreenStrs; i++){ /* AsciiToUnicode(ms_aScreenStrs[i].str, gUString); CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString(ms_aScreenStrs[i].x, ms_aScreenStrs[i].y, gUString); CFont::SetColor(CRGBA(255, 255, 255, 255)); CFont::PrintString(ms_aScreenStrs[i].x+1, ms_aScreenStrs[i].y+1, gUString); */ ObrsPrintfString(ms_aScreenStrs[i].str, ms_aScreenStrs[i].x, ms_aScreenStrs[i].y); } CFont::DrawFonts(); ms_nScreenStrs = 0; } void CDebug::PrintAt(const char *str, int x, int y) { if(ms_nScreenStrs >= MAX_SCREEN_STRS) return; strncpy(ms_aScreenStrs[ms_nScreenStrs].str, str, 256); ms_aScreenStrs[ms_nScreenStrs].x = x;//*12; ms_aScreenStrs[ms_nScreenStrs].y = y;//*22; ms_nScreenStrs++; } CDebug::Line CDebug::ms_aLines[MAX_DEBUG_LINES]; int CDebug::ms_nLines; void CDebug::AddLine(CVector p1, CVector p2, uint32 c1, uint32 c2) { if(ms_nLines >= MAX_DEBUG_LINES) return; ms_aLines[ms_nLines].p1 = p1; ms_aLines[ms_nLines].p2 = p2; ms_aLines[ms_nLines].c1 = c1; ms_aLines[ms_nLines].c2 = c2; ms_nLines++; } void CDebug::DrawLines(void) { int i; RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); for(i = 0; i < ms_nLines; i++){ Line *l = &ms_aLines[i]; CLines::RenderLineWithClipping(l->p1.x, l->p1.y, l->p1.z, l->p2.x, l->p2.y, l->p2.z, l->c1, l->c2); } ms_nLines = 0; } ================================================ FILE: src/core/Debug.h ================================================ #pragma once class CDebug { enum { MAX_LINES = 15, MAX_STR_LEN = 80, MAX_SCREEN_STRS = 100, MAX_DEBUG_LINES = 100, }; static int16 ms_nCurrentTextLine; static char ms_aTextBuffer[MAX_LINES][MAX_STR_LEN]; // custom struct ScreenStr { int x, y; char str[256]; }; static ScreenStr ms_aScreenStrs[MAX_SCREEN_STRS]; static int ms_nScreenStrs; struct Line { CVector p1, p2; uint32 c1, c2; }; static Line ms_aLines[MAX_DEBUG_LINES]; static int ms_nLines; public: static void DebugInitTextBuffer(); static void DebugDisplayTextBuffer(); static void DebugAddText(const char *str); // custom static void PrintAt(const char *str, int x, int y); static void DisplayScreenStrings(); static void AddLine(CVector p1, CVector p2, uint32 c1, uint32 c2); static void DrawLines(void); }; extern bool gbDebugStuffInRelease; ================================================ FILE: src/core/Directory.cpp ================================================ #include "common.h" #include "General.h" #include "FileMgr.h" #include "Directory.h" CDirectory::CDirectory(int32 maxEntries) : numEntries(0), maxEntries(maxEntries) { entries = new DirectoryInfo[maxEntries]; } CDirectory::~CDirectory(void) { delete[] entries; } void CDirectory::ReadDirFile(const char *filename) { int fd; DirectoryInfo dirinfo; fd = CFileMgr::OpenFile(filename, "rb"); while(CFileMgr::Read(fd, (char*)&dirinfo, sizeof(dirinfo))) AddItem(dirinfo); CFileMgr::CloseFile(fd); } bool CDirectory::WriteDirFile(const char *filename) { int fd; size_t n; fd = CFileMgr::OpenFileForWriting(filename); n = CFileMgr::Write(fd, (char*)entries, numEntries*sizeof(DirectoryInfo)); CFileMgr::CloseFile(fd); return n == numEntries*sizeof(DirectoryInfo); } void CDirectory::AddItem(const DirectoryInfo &dirinfo) { assert(numEntries < maxEntries); #ifdef FIX_BUGS // don't add if already exists uint32 offset, size; if(FindItem(dirinfo.name, offset, size)) return; #endif entries[numEntries++] = dirinfo; } void CDirectory::AddItem(const DirectoryInfo &dirinfo, int32 imgId) { DirectoryInfo di = dirinfo; di.offset |= imgId<<24; AddItem(di); } bool CDirectory::FindItem(const char *name, uint32 &offset, uint32 &size) { int i; for(i = 0; i < numEntries; i++) if(!CGeneral::faststricmp(entries[i].name, name)){ offset = entries[i].offset; size = entries[i].size; return true; } return false; } ================================================ FILE: src/core/Directory.h ================================================ #pragma once class CDirectory { public: struct DirectoryInfo { uint32 offset; uint32 size; char name[24]; }; DirectoryInfo *entries; int32 maxEntries; int32 numEntries; CDirectory(int32 maxEntries); ~CDirectory(void); void ReadDirFile(const char *filename); bool WriteDirFile(const char *filename); void AddItem(const DirectoryInfo &dirinfo); void AddItem(const DirectoryInfo &dirinfo, int32 imgId); bool FindItem(const char *name, uint32 &offset, uint32 &size); }; ================================================ FILE: src/core/EventList.cpp ================================================ #include "common.h" #include "Pools.h" #include "ModelIndices.h" #include "World.h" #include "Wanted.h" #include "EventList.h" #include "Messages.h" #include "Text.h" #include "main.h" #include "Accident.h" int32 CEventList::ms_nFirstFreeSlotIndex; CEvent gaEvent[NUMEVENTS]; enum { EVENT_STATE_0, EVENT_STATE_CANDELETE, EVENT_STATE_CLEAR, }; void CEventList::Initialise(void) { int i; debug("Initialising CEventList..."); for(i = 0; i < NUMEVENTS; i++){ gaEvent[i].type = EVENT_NULL; gaEvent[i].entityType = EVENT_ENTITY_NONE; gaEvent[i].entityRef = 0; gaEvent[i].posn.x = 0.0f; gaEvent[i].posn.y = 0.0f; gaEvent[i].posn.z = 0.0f; gaEvent[i].timeout = 0; gaEvent[i].state = EVENT_STATE_0; } ms_nFirstFreeSlotIndex = 0; } void CEventList::Update(void) { int i; ms_nFirstFreeSlotIndex = 0; for(i = 0; i < NUMEVENTS; i++){ if(gaEvent[i].type == EVENT_NULL) continue; if(CTimer::GetTimeInMilliseconds() > gaEvent[i].timeout || gaEvent[i].state == EVENT_STATE_CANDELETE){ gaEvent[i].type = EVENT_NULL; gaEvent[i].state = EVENT_STATE_0; } if(gaEvent[i].state == EVENT_STATE_CLEAR) gaEvent[i].state = EVENT_STATE_CANDELETE; } } void CEventList::RegisterEvent(eEventType type, eEventEntity entityType, CEntity *ent, CPed *criminal, int32 timeout) { int i; int ref; bool copsDontCare; #ifdef SQUEEZE_PERFORMANCE if (type == EVENT_INJURED_PED) { gAccidentManager.ReportAccident((CPed*)ent); return; } #endif copsDontCare = false; switch(entityType){ case EVENT_ENTITY_PED: ref = CPools::GetPedRef((CPed*)ent); break; case EVENT_ENTITY_VEHICLE: ref = CPools::GetVehicleRef((CVehicle*)ent); break; case EVENT_ENTITY_OBJECT: ref = CPools::GetObjectRef((CObject*)ent); break; default: Error("Undefined entity type, RegisterEvent, EventList.cpp"); ref = 0; break; } // only update time if event exists already for(i = 0; i < NUMEVENTS; i++) if(gaEvent[i].type == type && gaEvent[i].entityType == entityType && gaEvent[i].entityRef == ref){ gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; return; } for(i = ms_nFirstFreeSlotIndex; i < NUMEVENTS; i++) if(gaEvent[i].type == EVENT_NULL){ ms_nFirstFreeSlotIndex = i; break; } if(i < NUMEVENTS){ gaEvent[i].type = type; gaEvent[i].entityType = entityType; gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; gaEvent[i].entityRef = ref; gaEvent[i].posn = ent->GetPosition(); gaEvent[i].criminal = criminal; if(gaEvent[i].criminal) gaEvent[i].criminal->RegisterReference((CEntity**)&gaEvent[i].criminal); if(type == EVENT_GUNSHOT) gaEvent[i].state = EVENT_STATE_CLEAR; else gaEvent[i].state = EVENT_STATE_0; } if(criminal == FindPlayerPed()) ReportCrimeForEvent(type, (intptr)ent, copsDontCare); } void CEventList::RegisterEvent(eEventType type, CVector posn, int32 timeout) { int i; // only update time if event exists already for(i = 0; i < NUMEVENTS; i++) if(gaEvent[i].type == type && gaEvent[i].posn.x == posn.x && gaEvent[i].posn.y == posn.y && gaEvent[i].posn.z == posn.z && gaEvent[i].entityType == EVENT_ENTITY_NONE){ gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; return; } for(i = ms_nFirstFreeSlotIndex; i < NUMEVENTS; i++) if(gaEvent[i].type == EVENT_NULL){ ms_nFirstFreeSlotIndex = i; break; } if(i < NUMEVENTS){ gaEvent[i].type = type; gaEvent[i].entityType = EVENT_ENTITY_NONE; gaEvent[i].timeout = CTimer::GetTimeInMilliseconds() + timeout; gaEvent[i].posn = posn; gaEvent[i].entityRef = 0; if(type == EVENT_GUNSHOT) gaEvent[i].state = EVENT_STATE_CLEAR; else gaEvent[i].state = EVENT_STATE_0; } } bool CEventList::GetEvent(eEventType type, int32 *event) { int i; for(i = 0; i < NUMEVENTS; i++) if(gaEvent[i].type == type){ *event = i; return true; } return false; } void CEventList::ClearEvent(int32 event) { if(gaEvent[event].state != EVENT_STATE_CANDELETE) gaEvent[event].state = EVENT_STATE_CLEAR; } bool CEventList::FindClosestEvent(eEventType type, CVector posn, int32 *event) { int i; float dist; bool found = false; float mindist = 60.0f; for(i = 0; i < NUMEVENTS; i++){ if(gaEvent[i].type != type) continue; dist = (posn - gaEvent[i].posn).Magnitude(); if(dist < mindist){ mindist = dist; found = true; *event = i; } } return found; } void CEventList::ReportCrimeForEvent(eEventType type, intptr crimeId, bool copsDontCare) { eCrimeType crime; switch(type){ case EVENT_ASSAULT: crime = CRIME_HIT_PED; break; case EVENT_RUN_REDLIGHT: crime = CRIME_RUN_REDLIGHT; break; case EVENT_ASSAULT_POLICE: crime = CRIME_HIT_COP; break; case EVENT_GUNSHOT: crime = CRIME_POSSESSION_GUN; break; case EVENT_STEAL_CAR: crime = CRIME_STEAL_CAR; break; case EVENT_HIT_AND_RUN: crime = CRIME_RUNOVER_PED; break; case EVENT_HIT_AND_RUN_COP: crime = CRIME_RUNOVER_COP; break; case EVENT_SHOOT_PED: crime = CRIME_SHOOT_PED; break; case EVENT_SHOOT_COP: crime = CRIME_SHOOT_COP; break; case EVENT_EXPLOSION: crime = CRIME_EXPLOSION; break; case EVENT_PED_SET_ON_FIRE: crime = CRIME_PED_BURNED; break; case EVENT_COP_SET_ON_FIRE: crime = CRIME_COP_BURNED; break; case EVENT_CAR_SET_ON_FIRE: crime = CRIME_VEHICLE_BURNED; break; case EVENT_ASSAULT_NASTYWEAPON: crime = CRIME_HIT_PED_NASTYWEAPON; break; case EVENT_ASSAULT_NASTYWEAPON_POLICE: crime = CRIME_HIT_COP_NASTYWEAPON; break; default: crime = CRIME_NONE; break; } if (crime == CRIME_HIT_PED && IsPedPointerValid((CPed*)crimeId) && FindPlayerPed()->m_pWanted->GetWantedLevel() == 0 && ((CPed*)crimeId)->bBeingChasedByPolice) { if (!((CPed*)crimeId)->DyingOrDead()) { CMessages::AddBigMessage(TheText.Get("GOODBOY"), 5000, 0); CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 50; } return; } if(crime == CRIME_NONE) return; #ifdef FIX_BUGS CVector playerPedCoors = FindPlayerCoors(); #else CVector playerPedCoors = FindPlayerPed()->GetPosition(); #endif CVector playerCoors = FindPlayerCoors(); if(CWanted::WorkOutPolicePresence(playerCoors, 14.0f) != 0 || CGame::germanGame && (crime == CRIME_SHOOT_PED || crime == CRIME_SHOOT_COP || crime == CRIME_COP_BURNED || crime == CRIME_VEHICLE_BURNED)){ FindPlayerPed()->m_pWanted->RegisterCrime_Immediately(crime, playerPedCoors, (uint32)crimeId, copsDontCare); FindPlayerPed()->m_pWanted->SetWantedLevelNoDrop(1); }else FindPlayerPed()->m_pWanted->RegisterCrime(crime, playerPedCoors, (uint32)crimeId, copsDontCare); if(type == EVENT_ASSAULT_POLICE) FindPlayerPed()->SetWantedLevelNoDrop(1); if(type == EVENT_SHOOT_COP || type == EVENT_ASSAULT_NASTYWEAPON_POLICE) FindPlayerPed()->SetWantedLevelNoDrop(2); } ================================================ FILE: src/core/EventList.h ================================================ #pragma once class CEntity; class CPed; enum eEventType { EVENT_NULL, EVENT_ASSAULT, EVENT_RUN_REDLIGHT, EVENT_ASSAULT_POLICE, EVENT_GUNSHOT, EVENT_INJURED_PED, EVENT_DEAD_PED, EVENT_FIRE, EVENT_STEAL_CAR, EVENT_HIT_AND_RUN, EVENT_HIT_AND_RUN_COP, EVENT_SHOOT_PED, EVENT_SHOOT_COP, EVENT_EXPLOSION, EVENT_PED_SET_ON_FIRE, EVENT_COP_SET_ON_FIRE, EVENT_CAR_SET_ON_FIRE, EVENT_ASSAULT_NASTYWEAPON, EVENT_ASSAULT_NASTYWEAPON_POLICE, EVENT_UNK, // Not on SA it seems EVENT_ICECREAM, EVENT_ATM, EVENT_SHOPSTALL, EVENT_SHOPWINDOW, EVENT_LAST_EVENT }; enum eEventEntity { EVENT_ENTITY_NONE, EVENT_ENTITY_PED, EVENT_ENTITY_VEHICLE, EVENT_ENTITY_OBJECT }; struct CEvent { eEventType type; eEventEntity entityType; int32 entityRef; CPed *criminal; CVector posn; uint32 timeout; int32 state; }; class CEventList { static int32 ms_nFirstFreeSlotIndex; public: static void Initialise(void); static void Update(void); static void RegisterEvent(eEventType type, eEventEntity entityType, CEntity *ent, CPed *criminal, int32 timeout); static void RegisterEvent(eEventType type, CVector posn, int32 timeout); static bool GetEvent(eEventType type, int32 *event); static void ClearEvent(int32 event); static bool FindClosestEvent(eEventType type, CVector posn, int32 *event); static void ReportCrimeForEvent(eEventType type, intptr, bool); }; extern CEvent gaEvent[NUMEVENTS]; ================================================ FILE: src/core/FileLoader.cpp ================================================ #include "common.h" #include #include "main.h" #include "General.h" #include "Quaternion.h" #include "ModelInfo.h" #include "ModelIndices.h" #include "TempColModels.h" #include "VisibilityPlugins.h" #include "FileMgr.h" #include "HandlingMgr.h" #include "CarCtrl.h" #include "PedType.h" #include "AnimManager.h" #include "Game.h" #include "RwHelper.h" #include "NodeName.h" #include "TxdStore.h" #include "PathFind.h" #include "ObjectData.h" #include "DummyObject.h" #include "World.h" #include "Zones.h" #include "ZoneCull.h" #include "CdStream.h" #include "FileLoader.h" #include "MemoryHeap.h" #include "Streaming.h" #include "ColStore.h" #include "Occlusion.h" char CFileLoader::ms_line[256]; const char* GetFilename(const char *filename) { char *s = strrchr((char*)filename, '\\'); return s ? s+1 : filename; } void LoadingScreenLoadingFile(const char *filename) { sprintf(gString, "Loading %s", GetFilename(filename)); LoadingScreen("Loading the Game", gString, nil); } void CFileLoader::LoadLevel(const char *filename) { int fd; RwTexDictionary *savedTxd; bool objectsLoaded; char *line; char txdname[64]; savedTxd = RwTexDictionaryGetCurrent(); objectsLoaded = false; if(savedTxd == nil){ savedTxd = RwTexDictionaryCreate(); RwTexDictionarySetCurrent(savedTxd); } fd = CFileMgr::OpenFile(filename, "r"); assert(fd > 0); for(line = LoadLine(fd); line; line = LoadLine(fd)){ if(*line == '#') continue; if(strncmp(line, "EXIT", 4) == 0) break; if(strncmp(line, "IMAGEPATH", 9) == 0){ RwImageSetPath(line + 10); }else if(strncmp(line, "TEXDICTION", 10) == 0){ PUSH_MEMID(MEMID_TEXTURES); strcpy(txdname, line+11); LoadingScreenLoadingFile(txdname); RwTexDictionary *txd = LoadTexDictionary(txdname); AddTexDictionaries(savedTxd, txd); RwTexDictionaryDestroy(txd); POP_MEMID(); }else if(strncmp(line, "COLFILE", 7) == 0){ LoadingScreenLoadingFile(line+10); LoadCollisionFile(line+10, 0); }else if(strncmp(line, "MODELFILE", 9) == 0){ LoadingScreenLoadingFile(line + 10); LoadModelFile(line + 10); }else if(strncmp(line, "HIERFILE", 8) == 0){ LoadingScreenLoadingFile(line + 9); LoadClumpFile(line + 9); }else if(strncmp(line, "IDE", 3) == 0){ LoadingScreenLoadingFile(line + 4); LoadObjectTypes(line + 4); }else if(strncmp(line, "IPL", 3) == 0){ if(!objectsLoaded){ LoadingScreenLoadingFile("Collision"); PUSH_MEMID(MEMID_WORLD); CObjectData::Initialise("DATA\\OBJECT.DAT"); CStreaming::Init(); POP_MEMID(); PUSH_MEMID(MEMID_COLLISION); CColStore::LoadAllCollision(); POP_MEMID(); for(int i = 0; i < MODELINFOSIZE; i++) if(CModelInfo::GetModelInfo(i)) CModelInfo::GetModelInfo(i)->ConvertAnimFileIndex(); objectsLoaded = true; } PUSH_MEMID(MEMID_WORLD); LoadingScreenLoadingFile(line + 4); LoadScene(line + 4); POP_MEMID(); }else if(strncmp(line, "SPLASH", 6) == 0){ #ifndef DISABLE_LOADING_SCREEN LoadSplash(GetRandomSplashScreen()); #endif #ifndef GTA_PS2 }else if(strncmp(line, "CDIMAGE", 7) == 0){ CdStreamAddImage(line + 8); #endif } } CFileMgr::CloseFile(fd); RwTexDictionarySetCurrent(savedTxd); int i; for(i = 1; i < COLSTORESIZE; i++) if(CColStore::GetSlot(i)) CColStore::GetBoundingBox(i).Grow(120.0f); CWorld::RepositionCertainDynamicObjects(); CColStore::RemoveAllCollision(); } char* CFileLoader::LoadLine(int fd) { int i; char *line; if(CFileMgr::ReadLine(fd, ms_line, 256) == false) return nil; for(i = 0; ms_line[i] != '\0'; i++) if(ms_line[i] < ' ' || ms_line[i] == ',') ms_line[i] = ' '; for(line = ms_line; *line <= ' ' && *line != '\0'; line++); return line; } RwTexDictionary* CFileLoader::LoadTexDictionary(const char *filename) { RwTexDictionary *txd; RwStream *stream; txd = nil; stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); debug("Loading texture dictionary file %s\n", filename); if(stream){ if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)) txd = RwTexDictionaryGtaStreamRead(stream); RwStreamClose(stream, nil); } if(txd == nil) txd = RwTexDictionaryCreate(); return txd; } struct ColHeader { uint32 ident; uint32 size; }; void CFileLoader::LoadCollisionFile(const char *filename, uint8 colSlot) { int fd; char modelname[24]; CBaseModelInfo *mi; ColHeader header; PUSH_MEMID(MEMID_COLLISION); debug("Loading collision file %s\n", filename); fd = CFileMgr::OpenFile(filename, "rb"); assert(fd > 0); while(CFileMgr::Read(fd, (char*)&header, sizeof(header))){ assert(header.ident == 'LLOC'); CFileMgr::Read(fd, (char*)work_buff, header.size); memcpy(modelname, work_buff, 24); mi = CModelInfo::GetModelInfo(modelname, nil); if(mi){ if(mi->GetColModel() && mi->DoesOwnColModel()){ LoadCollisionModel(work_buff+24, *mi->GetColModel(), modelname); }else{ CColModel *model = new CColModel; model->level = colSlot; LoadCollisionModel(work_buff+24, *model, modelname); mi->SetColModel(model, true); } }else{ debug("colmodel %s can't find a modelinfo\n", modelname); } } CFileMgr::CloseFile(fd); POP_MEMID(); } bool CFileLoader::LoadCollisionFileFirstTime(uint8 *buffer, uint32 size, uint8 colSlot) { uint32 modelsize; char modelname[24]; CBaseModelInfo *mi; ColHeader *header; int modelIndex; while(size > 8){ header = (ColHeader*)buffer; modelsize = header->size; if(header->ident != 'LLOC') return size-8 < CDSTREAM_SECTOR_SIZE; memcpy(modelname, buffer+8, 24); memcpy(work_buff, buffer+32, modelsize-24); size -= 32 + (modelsize-24); buffer += 32 + (modelsize-24); if(modelsize > 15*1024) debug("colmodel %s is huge, size %d\n", modelname, modelsize); mi = CModelInfo::GetModelInfo(modelname, &modelIndex); if(mi){ CColStore::IncludeModelIndex(colSlot, modelIndex); CColModel *model = new CColModel; model->level = colSlot; LoadCollisionModel(work_buff, *model, modelname); mi->SetColModel(model, true); }else{ debug("colmodel %s can't find a modelinfo\n", modelname); } } return true; } bool CFileLoader::LoadCollisionFile(uint8 *buffer, uint32 size, uint8 colSlot) { uint32 modelsize; char modelname[24]; CBaseModelInfo *mi; ColHeader *header; while(size > 8){ header = (ColHeader*)buffer; modelsize = header->size; if(header->ident != 'LLOC') return size-8 < CDSTREAM_SECTOR_SIZE; memcpy(modelname, buffer+8, 24); memcpy(work_buff, buffer+32, modelsize-24); size -= 32 + (modelsize-24); buffer += 32 + (modelsize-24); if(modelsize > 15*1024) debug("colmodel %s is huge, size %d\n", modelname, modelsize); mi = CModelInfo::GetModelInfo(modelname, CColStore::GetSlot(colSlot)->minIndex, CColStore::GetSlot(colSlot)->maxIndex); if(mi){ if(mi->GetColModel()){ LoadCollisionModel(work_buff, *mi->GetColModel(), modelname); }else{ CColModel *model = new CColModel; model->level = colSlot; LoadCollisionModel(work_buff, *model, modelname); mi->SetColModel(model, true); } }else{ debug("colmodel %s can't find a modelinfo\n", modelname); } } return true; } void CFileLoader::LoadCollisionModel(uint8 *buf, CColModel &model, char *modelname) { int i; model.boundingSphere.radius = *(float*)(buf); model.boundingSphere.center.x = *(float*)(buf+4); model.boundingSphere.center.y = *(float*)(buf+8); model.boundingSphere.center.z = *(float*)(buf+12); model.boundingBox.min.x = *(float*)(buf+16); model.boundingBox.min.y = *(float*)(buf+20); model.boundingBox.min.z = *(float*)(buf+24); model.boundingBox.max.x = *(float*)(buf+28); model.boundingBox.max.y = *(float*)(buf+32); model.boundingBox.max.z = *(float*)(buf+36); model.numSpheres = *(int16*)(buf+40); buf += 44; if(model.numSpheres > 0){ model.spheres = (CColSphere*)RwMalloc(model.numSpheres*sizeof(CColSphere)); REGISTER_MEMPTR(&model.spheres); for(i = 0; i < model.numSpheres; i++){ model.spheres[i].Set(*(float*)buf, *(CVector*)(buf+4), buf[16], buf[17]); buf += 20; } }else model.spheres = nil; model.numLines = *(int16*)buf; buf += 4; if(model.numLines > 0){ //model.lines = (CColLine*)RwMalloc(model.numLines*sizeof(CColLine)); REGISTER_MEMPTR(&model.lines); for(i = 0; i < model.numLines; i++){ //model.lines[i].Set(*(CVector*)buf, *(CVector*)(buf+12)); buf += 24; } }else model.lines = nil; model.numLines = 0; model.lines = nil; model.numBoxes = *(int16*)buf; buf += 4; if(model.numBoxes > 0){ model.boxes = (CColBox*)RwMalloc(model.numBoxes*sizeof(CColBox)); REGISTER_MEMPTR(&model.boxes); for(i = 0; i < model.numBoxes; i++){ model.boxes[i].Set(*(CVector*)buf, *(CVector*)(buf+12), buf[24], buf[25]); buf += 28; } }else model.boxes = nil; int32 numVertices = *(int16*)buf; buf += 4; if(numVertices > 0){ model.vertices = (CompressedVector*)RwMalloc(numVertices*sizeof(CompressedVector)); REGISTER_MEMPTR(&model.vertices); for(i = 0; i < numVertices; i++){ model.vertices[i].Set(*(float*)buf, *(float*)(buf+4), *(float*)(buf+8)); #if 0 if(Abs(*(float*)buf) >= 256.0f || Abs(*(float*)(buf+4)) >= 256.0f || Abs(*(float*)(buf+8)) >= 256.0f) printf("%s:Collision volume too big\n", modelname); #endif buf += 12; } }else model.vertices = nil; model.numTriangles = *(int16*)buf; buf += 4; if(model.numTriangles > 0){ model.triangles = (CColTriangle*)RwMalloc(model.numTriangles*sizeof(CColTriangle)); REGISTER_MEMPTR(&model.triangles); for(i = 0; i < model.numTriangles; i++){ model.triangles[i].Set(*(int32*)buf, *(int32*)(buf+4), *(int32*)(buf+8), buf[12]); buf += 16; } }else model.triangles = nil; } static void GetNameAndLOD(char *nodename, char *name, int *n) { char *underscore = nil; for(char *s = nodename; *s != '\0'; s++){ if(s[0] == '_' && (s[1] == 'l' || s[1] == 'L') && isdigit(s[2])) underscore = s; } if(underscore){ strncpy(name, nodename, underscore - nodename); name[underscore - nodename] = '\0'; *n = atoi(underscore + 2); }else{ strncpy(name, nodename, 24); *n = 0; } } RpAtomic* CFileLoader::FindRelatedModelInfoCB(RpAtomic *atomic, void *data) { CSimpleModelInfo *mi; char *nodename, name[24]; int n; RpClump *clump = (RpClump*)data; nodename = GetFrameNodeName(RpAtomicGetFrame(atomic)); GetNameAndLOD(nodename, name, &n); mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(name, nil); if(mi){ assert(mi->IsSimple()); CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); mi->SetAtomic(n, atomic); RpClumpRemoveAtomic(clump, atomic); RpAtomicSetFrame(atomic, RwFrameCreate()); CVisibilityPlugins::SetAtomicModelInfo(atomic, mi); }else{ debug("Can't find Atomic %s\n", name); } return atomic; } #ifdef LIBRW void InitClump(RpClump *clump) { RpClumpForAllAtomics(clump, ConvertPlatformAtomic, nil); } #else #define InitClump(clump) #endif void CFileLoader::LoadModelFile(const char *filename) { RwStream *stream; RpClump *clump; debug("Loading model file %s\n", filename); stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); if(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)){ clump = RpClumpStreamRead(stream); if(clump){ InitClump(clump); RpClumpForAllAtomics(clump, FindRelatedModelInfoCB, clump); RpClumpDestroy(clump); } } RwStreamClose(stream, nil); } void CFileLoader::LoadClumpFile(const char *filename) { RwStream *stream; RpClump *clump; char *nodename, name[24]; int n; CClumpModelInfo *mi; debug("Loading model file %s\n", filename); stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); while(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)){ clump = RpClumpStreamRead(stream); if(clump){ nodename = GetFrameNodeName(RpClumpGetFrame(clump)); GetNameAndLOD(nodename, name, &n); mi = (CClumpModelInfo*)CModelInfo::GetModelInfo(name, nil); if(mi){ InitClump(clump); assert(mi->IsClump()); mi->SetClump(clump); }else RpClumpDestroy(clump); } } RwStreamClose(stream, nil); } bool CFileLoader::LoadClumpFile(RwStream *stream, uint32 id) { RpClump *clump; CClumpModelInfo *mi; if(!RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)) return false; clump = RpClumpStreamRead(stream); if(clump == nil) return false; InitClump(clump); mi = (CClumpModelInfo*)CModelInfo::GetModelInfo(id); mi->SetClump(clump); return true; } bool CFileLoader::StartLoadClumpFile(RwStream *stream, uint32 id) { if(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)){ printf("Start loading %s\n", CModelInfo::GetModelInfo(id)->GetModelName()); return RpClumpGtaStreamRead1(stream); }else{ printf("FAILED\n"); return false; } } bool CFileLoader::FinishLoadClumpFile(RwStream *stream, uint32 id) { RpClump *clump; CClumpModelInfo *mi; printf("Finish loading %s\n", CModelInfo::GetModelInfo(id)->GetModelName()); clump = RpClumpGtaStreamRead2(stream); if(clump){ InitClump(clump); mi = (CClumpModelInfo*)CModelInfo::GetModelInfo(id); mi->SetClump(clump); return true; }else{ printf("FAILED\n"); return false; } } CSimpleModelInfo *gpRelatedModelInfo; bool CFileLoader::LoadAtomicFile(RwStream *stream, uint32 id) { RpClump *clump; if(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)){ clump = RpClumpStreamRead(stream); if(clump == nil) return false; InitClump(clump); gpRelatedModelInfo = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); RpClumpForAllAtomics(clump, SetRelatedModelInfoCB, clump); RpClumpDestroy(clump); } return true; } RpAtomic* CFileLoader::SetRelatedModelInfoCB(RpAtomic *atomic, void *data) { char *nodename, name[24]; int n; RpClump *clump = (RpClump*)data; nodename = GetFrameNodeName(RpAtomicGetFrame(atomic)); GetNameAndLOD(nodename, name, &n); CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); gpRelatedModelInfo->SetAtomic(n, atomic); RpClumpRemoveAtomic(clump, atomic); RpAtomicSetFrame(atomic, RwFrameCreate()); CVisibilityPlugins::SetAtomicModelInfo(atomic, gpRelatedModelInfo); return atomic; } RpClump* CFileLoader::LoadAtomicFile2Return(const char *filename) { RwStream *stream; RpClump *clump; clump = nil; debug("Loading model file %s\n", filename); stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); if(RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)) clump = RpClumpStreamRead(stream); if(clump) InitClump(clump); RwStreamClose(stream, nil); return clump; } static RwTexture* MoveTexturesCB(RwTexture *texture, void *pData) { RwTexDictionaryAddTexture((RwTexDictionary*)pData, texture); return texture; } void CFileLoader::AddTexDictionaries(RwTexDictionary *dst, RwTexDictionary *src) { RwTexDictionaryForAllTextures(src, MoveTexturesCB, dst); } #define isLine3(l, a, b, c) ((l[0] == a) && (l[1] == b) && (l[2] == c)) #define isLine4(l, a, b, c, d) ((l[0] == a) && (l[1] == b) && (l[2] == c) && (l[3] == d)) void CFileLoader::LoadObjectTypes(const char *filename) { enum { NONE, OBJS, MLO, // unused but enum still has it TOBJ, WEAP, HIER, CARS, PEDS, PATH, TWODFX }; char *line; int fd; int section; int pathIndex; int id, pathType; int minID, maxID; section = NONE; minID = INT32_MAX; maxID = -1; pathIndex = -1; debug("Loading object types from %s...\n", filename); fd = CFileMgr::OpenFile(filename, "rb"); assert(fd > 0); for(line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)){ if(*line == '\0' || *line == '#') continue; if(section == NONE){ if(isLine4(line, 'o','b','j','s')) section = OBJS; else if(isLine4(line, 't','o','b','j')) section = TOBJ; else if(isLine4(line, 'w','e','a','p')) section = WEAP; else if(isLine4(line, 'h','i','e','r')) section = HIER; else if(isLine4(line, 'c','a','r','s')) section = CARS; else if(isLine4(line, 'p','e','d','s')) section = PEDS; else if(isLine4(line, 'p','a','t','h')) section = PATH; else if(isLine4(line, '2','d','f','x')) section = TWODFX; }else if(isLine3(line, 'e','n','d')){ section = NONE; }else switch(section){ case OBJS: id = LoadObject(line); if(id > maxID) maxID = id; if(id < minID) minID = id; break; case TOBJ: id = LoadTimeObject(line); if(id > maxID) maxID = id; if(id < minID) minID = id; break; case WEAP: LoadWeaponObject(line); break; case HIER: LoadClumpObject(line); break; case CARS: LoadVehicleObject(line); break; case PEDS: LoadPedObject(line); break; case PATH: if(pathIndex == -1){ id = LoadPathHeader(line, pathType); pathIndex = 0; }else{ if(pathType == 0) LoadPedPathNode(line, id, pathIndex); else if (pathType == 1) LoadCarPathNode(line, id, pathIndex, false); else if (pathType == 2) LoadCarPathNode(line, id, pathIndex, true); pathIndex++; if(pathIndex == 12) pathIndex = -1; } break; case TWODFX: Load2dEffect(line); break; } } CFileMgr::CloseFile(fd); for(id = minID; id <= maxID; id++){ CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); if(mi && mi->IsBuilding()) mi->SetupBigBuilding(minID, maxID); } } void SetModelInfoFlags(CSimpleModelInfo *mi, uint32 flags) { mi->m_wetRoadReflection = !!(flags & 1); mi->m_noFade = !!(flags & 2); mi->m_drawLast = !!(flags & (4|8)); mi->m_additive = !!(flags & 8); mi->m_isSubway = !!(flags & 0x10); mi->m_ignoreLight = !!(flags & 0x20); mi->m_noZwrite = !!(flags & 0x40); mi->m_noShadows = !!(flags & 0x80); mi->m_ignoreDrawDist = !!(flags & 0x100); mi->m_isCodeGlass = !!(flags & 0x200); mi->m_isArtistGlass = !!(flags & 0x400); } int CFileLoader::LoadObject(const char *line) { int id, numObjs; char model[24], txd[24]; float dist[3]; uint32 flags; int damaged; CSimpleModelInfo *mi; if(sscanf(line, "%d %s %s %d", &id, model, txd, &numObjs) != 4) return 0; // game returns return value switch(numObjs){ case 1: sscanf(line, "%d %s %s %d %f %d", &id, model, txd, &numObjs, &dist[0], &flags); damaged = 0; break; case 2: sscanf(line, "%d %s %s %d %f %f %d", &id, model, txd, &numObjs, &dist[0], &dist[1], &flags); damaged = dist[0] < dist[1] ? // Are distances increasing? 0 : // Yes, no damage model 1; // No, 1 is damaged break; case 3: sscanf(line, "%d %s %s %d %f %f %f %d", &id, model, txd, &numObjs, &dist[0], &dist[1], &dist[2], &flags); damaged = dist[0] < dist[1] ? // Are distances increasing? (dist[1] < dist[2] ? 0 : 2) : // Yes, only 2 can still be a damage model 1; // No, 1 and 2 are damaged break; } mi = CModelInfo::AddSimpleModel(id); mi->SetModelName(model); mi->SetNumAtomics(numObjs); mi->SetLodDistances(dist); SetModelInfoFlags(mi, flags); mi->m_firstDamaged = damaged; mi->SetTexDictionary(txd); MatchModelString(model, id); return id; } int CFileLoader::LoadTimeObject(const char *line) { int id, numObjs; char model[24], txd[24]; float dist[3]; uint32 flags; int timeOn, timeOff; int damaged; CTimeModelInfo *mi, *other; if(sscanf(line, "%d %s %s %d", &id, model, txd, &numObjs) != 4) return 0; // game returns return value switch(numObjs){ case 1: sscanf(line, "%d %s %s %d %f %d %d %d", &id, model, txd, &numObjs, &dist[0], &flags, &timeOn, &timeOff); damaged = 0; break; case 2: sscanf(line, "%d %s %s %d %f %f %d %d %d", &id, model, txd, &numObjs, &dist[0], &dist[1], &flags, &timeOn, &timeOff); damaged = dist[0] < dist[1] ? // Are distances increasing? 0 : // Yes, no damage model 1; // No, 1 is damaged break; case 3: sscanf(line, "%d %s %s %d %f %f %f %d %d %d", &id, model, txd, &numObjs, &dist[0], &dist[1], &dist[2], &flags, &timeOn, &timeOff); damaged = dist[0] < dist[1] ? // Are distances increasing? (dist[1] < dist[2] ? 0 : 2) : // Yes, only 2 can still be a damage model 1; // No, 1 and 2 are damaged break; } mi = CModelInfo::AddTimeModel(id); mi->SetModelName(model); mi->SetNumAtomics(numObjs); mi->SetLodDistances(dist); SetModelInfoFlags(mi, flags); mi->m_firstDamaged = damaged; mi->SetTimes(timeOn, timeOff); mi->SetTexDictionary(txd); other = mi->FindOtherTimeModel(); if(other) other->SetOtherTimeModel(id); MatchModelString(model, id); return id; } int CFileLoader::LoadWeaponObject(const char *line) { int id, numObjs; char model[24], txd[24], animFile[16]; float dist; CWeaponModelInfo *mi; sscanf(line, "%d %s %s %s %d %f", &id, model, txd, animFile, &numObjs, &dist); mi = CModelInfo::AddWeaponModel(id); mi->SetModelName(model); mi->SetNumAtomics(1); mi->m_lodDistances[0] = dist; mi->SetTexDictionary(txd); mi->SetAnimFile(animFile); mi->SetColModel(&CTempColModels::ms_colModelWeapon); MatchModelString(model, id); return id; } void CFileLoader::LoadClumpObject(const char *line) { int id; char model[24], txd[24]; CClumpModelInfo *mi; if(sscanf(line, "%d %s %s", &id, model, txd) == 3){ mi = CModelInfo::AddClumpModel(id); mi->SetModelName(model); mi->SetTexDictionary(txd); mi->SetColModel(&CTempColModels::ms_colModelBBox); } } void CFileLoader::LoadVehicleObject(const char *line) { int id; char model[24], txd[24]; char type[8], handlingId[16], gamename[32], animFile[16], vehclass[12]; uint32 frequency, comprules; int32 level, misc; float wheelScale; CVehicleModelInfo *mi; char *p; sscanf(line, "%d %s %s %s %s %s %s %s %d %d %x %d %f", &id, model, txd, type, handlingId, gamename, animFile, vehclass, &frequency, &level, &comprules, &misc, &wheelScale); mi = CModelInfo::AddVehicleModel(id); mi->SetModelName(model); mi->SetTexDictionary(txd); mi->SetAnimFile(animFile); for(p = gamename; *p; p++) if(*p == '_') *p = ' '; strcpy(mi->m_gameName, gamename); mi->m_level = level; mi->m_compRules = comprules; if(strcmp(type, "car") == 0){ mi->m_wheelId = misc; mi->m_wheelScale = wheelScale; mi->m_vehicleType = VEHICLE_TYPE_CAR; }else if(strcmp(type, "boat") == 0){ mi->m_vehicleType = VEHICLE_TYPE_BOAT; }else if(strcmp(type, "train") == 0){ mi->m_vehicleType = VEHICLE_TYPE_TRAIN; }else if(strcmp(type, "heli") == 0){ mi->m_vehicleType = VEHICLE_TYPE_HELI; }else if(strcmp(type, "plane") == 0){ mi->m_planeLodId = misc; mi->m_wheelScale = 1.0f; mi->m_vehicleType = VEHICLE_TYPE_PLANE; }else if(strcmp(type, "bike") == 0){ mi->m_bikeSteerAngle = misc; mi->m_wheelScale = wheelScale; mi->m_vehicleType = VEHICLE_TYPE_BIKE; }else assert(0); mi->m_handlingId = mod_HandlingManager.GetHandlingId(handlingId); if(strcmp(vehclass, "normal") == 0) mi->m_vehicleClass = CCarCtrl::NORMAL; else if(strcmp(vehclass, "poorfamily") == 0) mi->m_vehicleClass = CCarCtrl::POOR; else if(strcmp(vehclass, "richfamily") == 0) mi->m_vehicleClass = CCarCtrl::RICH; else if(strcmp(vehclass, "executive") == 0) mi->m_vehicleClass = CCarCtrl::EXEC; else if(strcmp(vehclass, "worker") == 0) mi->m_vehicleClass = CCarCtrl::WORKER; else if(strcmp(vehclass, "big") == 0) mi->m_vehicleClass = CCarCtrl::BIG; else if(strcmp(vehclass, "taxi") == 0) mi->m_vehicleClass = CCarCtrl::TAXI; else if(strcmp(vehclass, "moped") == 0) mi->m_vehicleClass = CCarCtrl::MOPED; else if(strcmp(vehclass, "motorbike") == 0) mi->m_vehicleClass = CCarCtrl::MOTORBIKE; else if(strcmp(vehclass, "leisureboat") == 0) mi->m_vehicleClass = CCarCtrl::LEISUREBOAT; else if(strcmp(vehclass, "workerboat") == 0) mi->m_vehicleClass = CCarCtrl::WORKERBOAT; else if(strcmp(vehclass, "ignore") == 0) { mi->m_vehicleClass = -1; return; } CCarCtrl::AddToCarArray(id, mi->m_vehicleClass); mi->m_frequency = frequency; } void CFileLoader::LoadPedObject(const char *line) { int id; char model[24], txd[24]; char pedType[24], pedStats[24], animGroup[24], animFile[16]; int carsCanDrive; CPedModelInfo *mi; int animGroupId; int radio1, radio2; sscanf(line, "%d %s %s %s %s %s %x %s %d %d", &id, model, txd, pedType, pedStats, animGroup, &carsCanDrive, animFile, &radio1, &radio2); mi = CModelInfo::AddPedModel(id); mi->SetModelName(model); mi->SetTexDictionary(txd); mi->SetAnimFile(animFile); mi->SetColModel(&CTempColModels::ms_colModelPed1); mi->m_pedType = CPedType::FindPedType(pedType); mi->m_pedStatType = CPedStats::GetPedStatType(pedStats); for(animGroupId = 0; animGroupId < NUM_ANIM_ASSOC_GROUPS; animGroupId++) if(strcmp(animGroup, CAnimManager::GetAnimGroupName((AssocGroupId)animGroupId)) == 0) break; assert(animGroupId < NUM_ANIM_ASSOC_GROUPS); mi->m_animGroup = animGroupId; mi->m_carsCanDrive = carsCanDrive; mi->radio1 = radio1; mi->radio2 = radio2; } int CFileLoader::LoadPathHeader(const char *line, int &type) { int id; char modelname[32]; sscanf(line, "%d %d %s", &type, &id, modelname); return id; } void CFileLoader::LoadPedPathNode(const char *line, int id, int node) { int type, next, cross, numLeft, numRight, speed, flags; float x, y, z, width, spawnRate; if(sscanf(line, "%d %d %d %f %f %f %f %d %d %d %d %f", &type, &next, &cross, &x, &y, &z, &width, &numLeft, &numRight, &speed, &flags, &spawnRate) != 12) spawnRate = 1.0f; if(id == -1) ThePaths.StoreDetachedNodeInfoPed(node, type, next, x, y, z, width, !!cross, !!(flags&1), !!(flags&4), spawnRate*15.0f); else ThePaths.StoreNodeInfoPed(id, node, type, next, x, y, z, width, !!cross, spawnRate*15.0f); } void CFileLoader::LoadCarPathNode(const char *line, int id, int node, bool waterPath) { int type, next, cross, numLeft, numRight, speed, flags; float x, y, z, width, spawnRate; if(sscanf(line, "%d %d %d %f %f %f %f %d %d %d %d %f", &type, &next, &cross, &x, &y, &z, &width, &numLeft, &numRight, &speed, &flags, &spawnRate) != 12) spawnRate = 1.0f; if(id == -1) ThePaths.StoreDetachedNodeInfoCar(node, type, next, x, y, z, width, numLeft, numRight, !!(flags&1), !!(flags&4), speed, !!(flags&2), waterPath, spawnRate * 15, false); else ThePaths.StoreNodeInfoCar(id, node, type, next, x, y, z, 0, numLeft, numRight, !!(flags&1), !!(flags&4), speed, !!(flags&2), waterPath, spawnRate * 15); } void CFileLoader::Load2dEffect(const char *line) { int id, r, g, b, a, type, ptype; float x, y, z; char corona[32], shadow[32]; int shadowIntens, lightType, roadReflection, flare, flags, probability; CBaseModelInfo *mi; C2dEffect *effect; char *p; sscanf(line, "%d %f %f %f %d %d %d %d %d", &id, &x, &y, &z, &r, &g, &b, &a, &type); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle")); mi = CModelInfo::GetModelInfo(id); effect = CModelInfo::Get2dEffectStore().Alloc(); mi->Add2dEffect(effect); effect->pos = CVector(x, y, z); effect->col = CRGBA(r, g, b, a); effect->type = type; switch(effect->type){ case EFFECT_LIGHT: while(*line++ != '"'); p = corona; while(*line != '"') *p++ = *line++; *p = '\0'; line++; while(*line++ != '"'); p = shadow; while(*line != '"') *p++ = *line++; *p = '\0'; line++; sscanf(line, "%f %f %f %f %d %d %d %d %d", &effect->light.dist, &effect->light.range, &effect->light.size, &effect->light.shadowSize, &shadowIntens, &lightType, &roadReflection, &flare, &flags); effect->light.corona = RwTextureRead(corona, nil); effect->light.shadow = RwTextureRead(shadow, nil); effect->light.shadowIntensity = shadowIntens; effect->light.lightType = lightType; effect->light.roadReflection = roadReflection; effect->light.flareType = flare; if(flags & LIGHTFLAG_FOG_ALWAYS) flags &= ~LIGHTFLAG_FOG_NORMAL; effect->light.flags = flags; break; case EFFECT_PARTICLE: sscanf(line, "%d %f %f %f %d %d %d %d %d %d %f %f %f %f", &id, &x, &y, &z, &r, &g, &b, &a, &type, &effect->particle.particleType, &effect->particle.dir.x, &effect->particle.dir.y, &effect->particle.dir.z, &effect->particle.scale); break; case EFFECT_ATTRACTOR: sscanf(line, "%d %f %f %f %d %d %d %d %d %d %f %f %f %d", &id, &x, &y, &z, &r, &g, &b, &a, &type, &flags, &effect->attractor.dir.x, &effect->attractor.dir.y, &effect->attractor.dir.z, &probability); effect->attractor.type = flags; #ifdef FIX_BUGS effect->attractor.probability = clamp(probability, 0, 255); #else effect->attractor.probability = probability; #endif break; case EFFECT_PED_ATTRACTOR: sscanf(line, "%d %f %f %f %d %d %d %d %d %d %f %f %f %f %f %f", &id, &x, &y, &z, &r, &g, &b, &a, &type, &ptype, &effect->pedattr.queueDir.x, &effect->pedattr.queueDir.y, &effect->pedattr.queueDir.z, &effect->pedattr.useDir.x, &effect->pedattr.useDir.y, &effect->pedattr.useDir.z); effect->pedattr.type = ptype; break; } CTxdStore::PopCurrentTxd(); } void CFileLoader::LoadScene(const char *filename) { enum { NONE, INST, ZONE, CULL, OCCL, PICK, PATH, }; char *line; int fd; int section; int pathType, pathIndex; section = NONE; pathIndex = -1; debug("Creating objects from %s...\n", filename); fd = CFileMgr::OpenFile(filename, "rb"); assert(fd > 0); for(line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)){ if(*line == '\0' || *line == '#') continue; if(section == NONE){ if(isLine4(line, 'i','n','s','t')) section = INST; else if(isLine4(line, 'z','o','n','e')) section = ZONE; else if(isLine4(line, 'c','u','l','l')) section = CULL; else if(isLine4(line, 'p','i','c','k')) section = PICK; else if(isLine4(line, 'p','a','t','h')) section = PATH; else if(isLine4(line, 'o','c','c','l')) section = OCCL; }else if(isLine3(line, 'e','n','d')){ section = NONE; }else switch(section){ case INST: LoadObjectInstance(line); break; case ZONE: LoadZone(line); break; case CULL: LoadCullZone(line); break; case OCCL: LoadOcclusionVolume(line); break; case PICK: // unused LoadPickup(line); break; case PATH: if(pathIndex == -1){ LoadPathHeader(line, pathType); pathIndex = 0; }else{ if(pathType == 0) LoadPedPathNode(line, -1, pathIndex); else if (pathType == 1) LoadCarPathNode(line, -1, pathIndex, false); else if (pathType == 2) LoadCarPathNode(line, -1, pathIndex, true); pathIndex++; if(pathIndex == 12) pathIndex = -1; } break; } } CFileMgr::CloseFile(fd); debug("Finished loading IPL\n"); } void CFileLoader::LoadObjectInstance(const char *line) { int id; char name[24]; RwV3d trans, scale, axis; float angle; CSimpleModelInfo *mi; RwMatrix *xform; CEntity *entity; float area; if(sscanf(line, "%d %s %f %f %f %f %f %f %f %f %f %f %f", &id, name, &area, &trans.x, &trans.y, &trans.z, &scale.x, &scale.y, &scale.z, &axis.x, &axis.y, &axis.z, &angle) != 13){ if(sscanf(line, "%d %s %f %f %f %f %f %f %f %f %f %f", &id, name, &trans.x, &trans.y, &trans.z, &scale.x, &scale.y, &scale.z, &axis.x, &axis.y, &axis.z, &angle) != 12) return; area = 0; } mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); if(mi == nil) return; assert(mi->IsSimple()); if(!CStreaming::IsObjectInCdImage(id)) debug("Not in cdimage %s\n", mi->GetModelName()); angle = -RADTODEG(2.0f * acosf(angle)); xform = RwMatrixCreate(); RwMatrixRotate(xform, &axis, angle, rwCOMBINEREPLACE); RwMatrixTranslate(xform, &trans, rwCOMBINEPOSTCONCAT); if(mi->GetObjectID() == -1){ if(ThePaths.IsPathObject(id)){ entity = new CTreadable; ThePaths.RegisterMapObject((CTreadable*)entity); }else entity = new CBuilding; entity->SetModelIndexNoCreate(id); entity->GetMatrix() = CMatrix(xform); entity->m_level = CTheZones::GetLevelFromPosition(&entity->GetPosition()); entity->m_area = area; if(mi->IsBuilding()){ if(mi->m_isBigBuilding) entity->SetupBigBuilding(); if(mi->m_isSubway) entity->bIsSubway = true; } if(mi->GetLargestLodDistance() < 2.0f) entity->bIsVisible = false; CWorld::Add(entity); CColModel *col = entity->GetColModel(); if(col->numSpheres || col->numBoxes || col->numTriangles){ if(col->level != 0) CColStore::GetBoundingBox(col->level).ContainRect(entity->GetBoundRect()); }else entity->bUsesCollision = false; if(entity->GetPosition().z + col->boundingBox.min.z < 6.0f) entity->bUnderwater = true; }else{ entity = new CDummyObject; entity->SetModelIndexNoCreate(id); entity->GetMatrix() = CMatrix(xform); CWorld::Add(entity); if(IsGlass(entity->GetModelIndex()) && !mi->m_isArtistGlass) entity->bIsVisible = false; entity->m_level = CTheZones::GetLevelFromPosition(&entity->GetPosition()); entity->m_area = area; } RwMatrixDestroy(xform); } void CFileLoader::LoadZone(const char *line) { char name[24]; int type, level; float minx, miny, minz; float maxx, maxy, maxz; if(sscanf(line, "%s %d %f %f %f %f %f %f %d", name, &type, &minx, &miny, &minz, &maxx, &maxy, &maxz, &level) == 9) CTheZones::CreateZone(name, (eZoneType)type, minx, miny, minz, maxx, maxy, maxz, (eLevelName)level); } void CFileLoader::LoadCullZone(const char *line) { CVector pos; float minx, miny, minz; float maxx, maxy, maxz; int flags; int wantedLevelDrop = 0; sscanf(line, "%f %f %f %f %f %f %f %f %f %d %d", &pos.x, &pos.y, &pos.z, &minx, &miny, &minz, &maxx, &maxy, &maxz, &flags, &wantedLevelDrop); CCullZones::AddCullZone(pos, minx, maxx, miny, maxy, minz, maxz, flags, wantedLevelDrop); } // unused void CFileLoader::LoadPickup(const char *line) { int id; float x, y, z; sscanf(line, "%d %f %f %f", &id, &x, &y, &z); } void CFileLoader::LoadOcclusionVolume(const char *line) { float x, y, z; float width, length, height; float angle; sscanf(line, "%f %f %f %f %f %f %f", &x, &y, &z, &width, &length, &height, &angle); COcclusion::AddOne(x, y, z + height/2.0f, width, length, height, angle); } // unused void CFileLoader::ReloadPaths(const char *filename) { enum { NONE, PATH, }; char *line; int section = NONE; int id, pathType, pathIndex = -1; debug("Reloading paths from %s...\n", filename); int fd = CFileMgr::OpenFile(filename, "r"); assert(fd > 0); for (line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)) { if (*line == '\0' || *line == '#') continue; if (section == NONE) { if (isLine4(line, 'p','a','t','h')) { section = PATH; ThePaths.AllocatePathFindInfoMem(4500); } } else if (isLine3(line, 'e','n','d')) { section = NONE; } else { switch (section) { case PATH: if (pathIndex == -1) { id = LoadPathHeader(line, pathType); pathIndex = 0; } else { if(pathType == 0) LoadPedPathNode(line, id, pathIndex); else if (pathType == 1) LoadCarPathNode(line, id, pathIndex, false); else if (pathType == 2) LoadCarPathNode(line, id, pathIndex, true); pathIndex++; if (pathIndex == 12) pathIndex = -1; } break; default: break; } } } CFileMgr::CloseFile(fd); } void CFileLoader::ReloadObjectTypes(const char *filename) { enum { NONE, OBJS, TOBJ, TWODFX }; char *line; int section = NONE; CModelInfo::ReInit2dEffects(); debug("Reloading object types from %s...\n", filename); CFileMgr::ChangeDir("\\DATA\\MAPS\\"); int fd = CFileMgr::OpenFile(filename, "r"); assert(fd > 0); CFileMgr::ChangeDir("\\"); for (line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)) { if (*line == '\0' || *line == '#') continue; if (section == NONE) { if (isLine4(line, 'o','b','j','s')) section = OBJS; else if (isLine4(line, 't','o','b','j')) section = TOBJ; else if (isLine4(line, '2','d','f','x')) section = TWODFX; } else if (isLine3(line, 'e','n','d')) { section = NONE; } else { switch (section) { case OBJS: case TOBJ: ReloadObject(line); break; case TWODFX: Load2dEffect(line); break; default: break; } } } CFileMgr::CloseFile(fd); } void CFileLoader::ReloadObject(const char *line) { int id, numObjs; char model[24], txd[24]; float dist[3]; uint32 flags; CSimpleModelInfo *mi; if(sscanf(line, "%d %s %s %d", &id, model, txd, &numObjs) != 4) return; switch(numObjs){ case 1: sscanf(line, "%d %s %s %d %f %d", &id, model, txd, &numObjs, &dist[0], &flags); break; case 2: sscanf(line, "%d %s %s %d %f %f %d", &id, model, txd, &numObjs, &dist[0], &dist[1], &flags); break; case 3: sscanf(line, "%d %s %s %d %f %f %f %d", &id, model, txd, &numObjs, &dist[0], &dist[1], &dist[2], &flags); break; } mi = (CSimpleModelInfo*) CModelInfo::GetModelInfo(id); if ( #ifdef FIX_BUGS mi && #endif mi->GetModelType() == MITYPE_SIMPLE && !strcmp(mi->GetModelName(), model) && mi->m_numAtomics == numObjs) { mi->SetLodDistances(dist); SetModelInfoFlags(mi, flags); } else { printf("Can't reload %s\n", model); } } // unused mobile function - crashes void CFileLoader::ReLoadScene(const char *filename) { char *line; CFileMgr::ChangeDir("\\DATA\\"); int fd = CFileMgr::OpenFile(filename, "r"); assert(fd > 0); CFileMgr::ChangeDir("\\"); for (line = CFileLoader::LoadLine(fd); line; line = CFileLoader::LoadLine(fd)) { if (*line == '#') continue; if (strncmp(line, "EXIT", 4) == 0) break; if (strncmp(line, "IDE", 3) == 0) { LoadObjectTypes(line + 4); } } CFileMgr::CloseFile(fd); } ================================================ FILE: src/core/FileLoader.h ================================================ #pragma once class CFileLoader { static char ms_line[256]; public: static void LoadLevel(const char *filename); static char *LoadLine(int fd); static RwTexDictionary *LoadTexDictionary(const char *filename); static void LoadCollisionFile(const char *filename, uint8 colSlot); static bool LoadCollisionFileFirstTime(uint8 *buffer, uint32 size, uint8 colSlot); static bool LoadCollisionFile(uint8 *buffer, uint32 size, uint8 colSlot); static void LoadCollisionModel(uint8 *buf, struct CColModel &model, char *name); static void LoadModelFile(const char *filename); static RpAtomic *FindRelatedModelInfoCB(RpAtomic *atomic, void *data); static void LoadClumpFile(const char *filename); static bool LoadClumpFile(RwStream *stream, uint32 id); static bool StartLoadClumpFile(RwStream *stream, uint32 id); static bool FinishLoadClumpFile(RwStream *stream, uint32 id); static bool LoadAtomicFile(RwStream *stream, uint32 id); static RpAtomic *SetRelatedModelInfoCB(RpAtomic *atomic, void *data); static RpClump *LoadAtomicFile2Return(const char *filename); static void AddTexDictionaries(RwTexDictionary *dst, RwTexDictionary *src); static void LoadObjectTypes(const char *filename); static int LoadObject(const char *line); static int LoadTimeObject(const char *line); static int LoadWeaponObject(const char *line); static void LoadClumpObject(const char *line); static void LoadVehicleObject(const char *line); static void LoadPedObject(const char *line); static int LoadPathHeader(const char *line, int &type); static void LoadPedPathNode(const char *line, int id, int node); static void LoadCarPathNode(const char *line, int id, int node, bool waterPath); static void Load2dEffect(const char *line); static void LoadScene(const char *filename); static void LoadObjectInstance(const char *line); static void LoadZone(const char *line); static void LoadCullZone(const char *line); static void LoadPickup(const char *line); static void LoadOcclusionVolume(const char *line); static void ReloadPaths(const char *filename); static void ReloadObjectTypes(const char *filename); static void ReloadObject(const char *line); static void ReLoadScene(const char *filename); // unused mobile function }; ================================================ FILE: src/core/FileMgr.cpp ================================================ #define _CRT_SECURE_NO_WARNINGS #include #ifdef _WIN32 #include #endif #include "common.h" #include "crossplatform.h" #include "FileMgr.h" const char *_psGetUserFilesFolder(); /* * Windows FILE is BROKEN for GTA. * * We need to support mapping between LF and CRLF for text files * but we do NOT want to end the file at the first sight of a SUB character. * So here is a simple implementation of a FILE interface that works like GTA expects. */ struct myFILE { bool isText; FILE *file; }; #define NUMFILES 20 static myFILE myfiles[NUMFILES]; #if !defined(_WIN32) #include #include #include #define _getcwd getcwd // Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen) void mychdir(char const *path) { char* r = casepath(path, false); if (r) { chdir(r); free(r); } else { errno = ENOENT; } } #else #define mychdir chdir #endif /* Force file to open as binary but remember if it was text mode */ static int myfopen(const char *filename, const char *mode) { int fd; char realmode[10], *p; for(fd = 1; fd < NUMFILES; fd++) if(myfiles[fd].file == nil) goto found; return 0; // no free fd found: myfiles[fd].isText = strchr(mode, 'b') == nil; p = realmode; while(*mode) if(*mode != 't' && *mode != 'b') *p++ = *mode++; else mode++; *p++ = 'b'; *p = '\0'; myfiles[fd].file = fcaseopen(filename, realmode); if(myfiles[fd].file == nil) return 0; return fd; } static int myfclose(int fd) { int ret; assert(fd < NUMFILES); if(myfiles[fd].file){ ret = fclose(myfiles[fd].file); myfiles[fd].file = nil; return ret; } return EOF; } static int myfgetc(int fd) { int c; c = fgetc(myfiles[fd].file); if(myfiles[fd].isText && c == 015){ /* translate CRLF to LF */ c = fgetc(myfiles[fd].file); if(c == 012) return c; ungetc(c, myfiles[fd].file); return 015; } return c; } static int myfputc(int c, int fd) { /* translate LF to CRLF */ if(myfiles[fd].isText && c == 012) fputc(015, myfiles[fd].file); return fputc(c, myfiles[fd].file); } static char* myfgets(char *buf, int len, int fd) { int c; char *p; p = buf; len--; // NUL byte while(len--){ c = myfgetc(fd); if(c == EOF){ if(p == buf) return nil; break; } *p++ = c; if(c == '\n') break; } *p = '\0'; return buf; } static size_t myfread(void *buf, size_t elt, size_t n, int fd) { if(myfiles[fd].isText){ unsigned char *p; size_t i; int c; n *= elt; p = (unsigned char*)buf; for(i = 0; i < n; i++){ c = myfgetc(fd); if(c == EOF) break; *p++ = (unsigned char)c; } return i / elt; } return fread(buf, elt, n, myfiles[fd].file); } static size_t myfwrite(void *buf, size_t elt, size_t n, int fd) { if(myfiles[fd].isText){ unsigned char *p; size_t i; int c; n *= elt; p = (unsigned char*)buf; for(i = 0; i < n; i++){ c = *p++; myfputc(c, fd); if(feof(myfiles[fd].file)) // is this right? break; } return i / elt; } return fwrite(buf, elt, n, myfiles[fd].file); } static int myfseek(int fd, long offset, int whence) { return fseek(myfiles[fd].file, offset, whence); } static int myfeof(int fd) { return feof(myfiles[fd].file); // return ferror(myfiles[fd].file); } char CFileMgr::ms_rootDirName[128] = {'\0'}; char CFileMgr::ms_dirName[128]; void CFileMgr::Initialise(void) { _getcwd(ms_rootDirName, 128); strcat(ms_rootDirName, "\\"); } void CFileMgr::ChangeDir(const char *dir) { if(*dir == '\\'){ strcpy(ms_dirName, ms_rootDirName); dir++; } if(*dir != '\0'){ strcat(ms_dirName, dir); // BUG in the game it seems, it's off by one if(dir[strlen(dir)-1] != '\\') strcat(ms_dirName, "\\"); } mychdir(ms_dirName); } void CFileMgr::SetDir(const char *dir) { strcpy(ms_dirName, ms_rootDirName); if(*dir != '\0'){ strcat(ms_dirName, dir); // BUG in the game it seems, it's off by one if(dir[strlen(dir)-1] != '\\') strcat(ms_dirName, "\\"); } mychdir(ms_dirName); } void CFileMgr::SetDirMyDocuments(void) { SetDir(""); // better start at the root if user directory is relative mychdir(_psGetUserFilesFolder()); } ssize_t CFileMgr::LoadFile(const char *file, uint8 *buf, int maxlen, const char *mode) { int fd; ssize_t n, len; fd = myfopen(file, mode); if(fd == 0) return -1; len = 0; do{ n = myfread(buf + len, 1, 0x4000, fd); #ifndef FIX_BUGS if (n < 0) return -1; #endif len += n; assert(len < maxlen); }while(n == 0x4000); buf[len] = 0; myfclose(fd); return len; } int CFileMgr::OpenFile(const char *file, const char *mode) { return myfopen(file, mode); } int CFileMgr::OpenFileForWriting(const char *file) { return OpenFile(file, "wb"); } size_t CFileMgr::Read(int fd, const char *buf, ssize_t len) { return myfread((void*)buf, 1, len, fd); } size_t CFileMgr::Write(int fd, const char *buf, ssize_t len) { return myfwrite((void*)buf, 1, len, fd); } bool CFileMgr::Seek(int fd, int offset, int whence) { return !!myfseek(fd, offset, whence); } bool CFileMgr::ReadLine(int fd, char *buf, int len) { return myfgets(buf, len, fd) != nil; } int CFileMgr::CloseFile(int fd) { return myfclose(fd); } int CFileMgr::GetErrorReadWrite(int fd) { return myfeof(fd); } ================================================ FILE: src/core/FileMgr.h ================================================ #pragma once class CFileMgr { static char ms_rootDirName[128]; static char ms_dirName[128]; public: static void Initialise(void); static void ChangeDir(const char *dir); static void SetDir(const char *dir); static void SetDirMyDocuments(void); static ssize_t LoadFile(const char *file, uint8 *buf, int maxlen, const char *mode); static int OpenFile(const char *file, const char *mode); static int OpenFile(const char *file) { return OpenFile(file, "rb"); } static int OpenFileForWriting(const char *file); static size_t Read(int fd, const char *buf, ssize_t len); static size_t Write(int fd, const char *buf, ssize_t len); static bool Seek(int fd, int offset, int whence); static bool ReadLine(int fd, char *buf, int len); static int CloseFile(int fd); static int GetErrorReadWrite(int fd); static char *GetRootDirName() { return ms_rootDirName; } }; ================================================ FILE: src/core/Fire.cpp ================================================ #include "common.h" #include "Vector.h" #include "PlayerPed.h" #include "Entity.h" #include "PointLights.h" #include "Particle.h" #include "Timer.h" #include "Vehicle.h" #include "Shadows.h" #include "Automobile.h" #include "World.h" #include "General.h" #include "EventList.h" #include "DamageManager.h" #include "Ped.h" #include "Fire.h" #include "GameLogic.h" #include "CarAI.h" CFireManager gFireManager; CFire::CFire() { m_bIsOngoing = false; m_bIsScriptFire = false; m_bPropagationFlag = true; m_bAudioSet = true; m_vecPos = CVector(0.0f, 0.0f, 0.0f); m_nExtinguishTime = 0; m_nStartTime = 0; m_pEntity = nil; m_pSource = nil; m_fStrength = 0.8f; m_fWaterExtinguishCountdown = 1.0f; m_bExtinguishedWithWater = false; } CFire::~CFire() {} void CFire::ProcessFire(void) { float fDamagePlayer; float fDamagePeds; float fDamageVehicle; int16 nRandNumber; float fGreen; float fRed; CVector lightpos; CVector firePos; CPed *ped = (CPed *)m_pEntity; CVehicle *veh = (CVehicle*)m_pEntity; m_fWaterExtinguishCountdown = Min(1.0f, 0.002f * CTimer::GetTimeStep() + m_fWaterExtinguishCountdown); if (m_pEntity) { m_vecPos = m_pEntity->GetPosition(); if (((CPed *)m_pEntity)->IsPed()) { if (ped->m_pFire != this) { Extinguish(); return; } #if defined GTAVC_JP_PATCH && !defined FIX_BUGS if (m_pEntity == CGameLogic::pShortCutTaxi && CGameLogic::ShortCutState == CGameLogic::SHORTCUT_TRANSITION) { Extinguish(); return; } #endif if (ped->m_nMoveState != PEDMOVE_RUN) m_vecPos.z -= 1.0f; if (ped->bInVehicle && ped->m_pMyVehicle) { if (ped->m_pMyVehicle->IsCar()) ped->m_pMyVehicle->m_fHealth = 75.0f; } else if (m_pEntity == (CPed *)FindPlayerPed()) { fDamagePlayer = 1.2f * CTimer::GetTimeStep(); ((CPlayerPed *)m_pEntity)->InflictDamage( (CPlayerPed *)m_pSource, WEAPONTYPE_FLAMETHROWER, fDamagePlayer, PEDPIECE_TORSO, 0); } else { fDamagePeds = 1.2f * CTimer::GetTimeStep(); if (((CPlayerPed *)m_pEntity)->InflictDamage( (CPlayerPed *)m_pSource, WEAPONTYPE_FLAMETHROWER, fDamagePeds, PEDPIECE_TORSO, 0)) { m_pEntity->bRenderScorched = true; } } } else if (m_pEntity->IsVehicle()) { if (veh->m_pCarFire != this) { Extinguish(); return; } #ifdef FIX_BUGS if (m_pEntity == CGameLogic::pShortCutTaxi && CGameLogic::ShortCutState == CGameLogic::SHORTCUT_TRANSITION) { Extinguish(); return; } #endif if (!m_bIsScriptFire) { fDamageVehicle = 1.2f * CTimer::GetTimeStep(); veh->InflictDamage((CVehicle *)m_pSource, WEAPONTYPE_FLAMETHROWER, fDamageVehicle); } } } if (!FindPlayerVehicle() && #ifdef FIX_BUGS FindPlayerPed() && #endif !FindPlayerPed()->m_pFire && !(FindPlayerPed()->bFireProof) && ((FindPlayerPed()->GetPosition() - m_vecPos).MagnitudeSqr() < 2.0f)) { FindPlayerPed()->DoStuffToGoOnFire(); gFireManager.StartFire(FindPlayerPed(), m_pSource, 0.8f, 1); } if (CTimer::GetTimeInMilliseconds() > m_nNextTimeToAddFlames) { m_nNextTimeToAddFlames = CTimer::GetTimeInMilliseconds() + (m_fWaterExtinguishCountdown < 0.3f ? 400 : (m_fWaterExtinguishCountdown < 0.7f ? 200 : 80)); firePos = m_vecPos; if (veh && veh->IsVehicle() && veh->IsCar()) { CVehicleModelInfo *mi = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(veh->GetModelIndex())); CVector ModelInfo = mi->m_positions[CAR_POS_HEADLIGHTS]; ModelInfo = m_pEntity->GetMatrix() * ModelInfo; firePos.x = ModelInfo.x; firePos.y = ModelInfo.y; firePos.z = ModelInfo.z + 0.15f; } CParticle::AddParticle(PARTICLE_CARFLAME, firePos, CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.0125f, 0.1f) * m_fStrength), 0, m_fStrength, 0, 0, 0, 0); CGeneral::GetRandomNumber(); CGeneral::GetRandomNumber(); CGeneral::GetRandomNumber(); /* unsure why these three rands are called */ CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, firePos, CVector(0.0f, 0.0f, 0.0f), 0, 0.0f, 0, 0, 0, 0); } if (CTimer::GetTimeInMilliseconds() < m_nExtinguishTime || m_bIsScriptFire) { if (CTimer::GetTimeInMilliseconds() > m_nStartTime) m_nStartTime = CTimer::GetTimeInMilliseconds() + 400; nRandNumber = CGeneral::GetRandomNumber() & 127; lightpos.x = m_vecPos.x; lightpos.y = m_vecPos.y; lightpos.z = m_vecPos.z + 5.0f; if (!m_pEntity) { CShadows::StoreStaticShadow((uintptr)this, SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &lightpos, 7.0f, 0.0f, 0.0f, -7.0f, 0, nRandNumber / 2, nRandNumber / 2, 0, 10.0f, 1.0f, 40.0f, 0, 0.0f); } fGreen = nRandNumber / 128.f; fRed = nRandNumber / 128.f; CPointLights::AddLight(CPointLights::LIGHT_POINT, m_vecPos, CVector(0.0f, 0.0f, 0.0f), 12.0f, fRed, fGreen, 0.0f, 0, 0); } else { Extinguish(); } } void CFire::ReportThisFire(void) { gFireManager.m_nTotalFires++; CEventList::RegisterEvent(EVENT_FIRE, m_vecPos, 1000); } void CFire::Extinguish(void) { if (m_bIsOngoing) { if (!m_bIsScriptFire) gFireManager.m_nTotalFires--; m_nExtinguishTime = 0; m_bIsOngoing = false; m_bExtinguishedWithWater = false; if (m_pEntity) { if (m_pEntity->IsPed()) { CPed *ped = (CPed*)m_pEntity; if (ped->CanSetPedState()) { if (ped->m_nPedState != PED_DRIVING && ped->m_nPedState != PED_FALL) { if (ped->IsPlayer()) { ped->SetIdle(); } else { ped->m_nLastPedState = PED_NONE; ped->SetWanderPath(0); ped->SetWaitState(WAITSTATE_FINISH_FLEE, 0); } } } ped->m_pFire = nil; } else if (m_pEntity->IsVehicle()) { ((CVehicle *)m_pEntity)->m_pCarFire = nil; } m_pEntity = nil; } } } void CFireManager::StartFire(CVector pos, float size, uint8 propagation) { CFire *fire = GetNextFreeFire(); if (fire) { fire->m_bIsOngoing = true; fire->m_bIsScriptFire = false; fire->m_bPropagationFlag = propagation; fire->m_bAudioSet = true; fire->m_vecPos = pos; fire->m_nExtinguishTime = CTimer::GetTimeInMilliseconds() + 10000; fire->m_nStartTime = CTimer::GetTimeInMilliseconds() + 400; fire->m_pEntity = nil; fire->m_pSource = nil; fire->m_nNextTimeToAddFlames = 0; fire->ReportThisFire(); fire->m_fStrength = size; fire->m_bExtinguishedWithWater = false; } } CFire * CFireManager::StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength, uint8 propagation) { CPed *ped = (CPed *)entityOnFire; CVehicle *veh = (CVehicle *)entityOnFire; if (entityOnFire->IsPed()) { if (ped->m_pFire) return nil; if (!ped->IsPedInControl()) return nil; } else if (entityOnFire->IsVehicle()) { if (veh->m_pCarFire) return nil; if (veh->IsCar() && ((CAutomobile *)veh)->Damage.GetEngineStatus() >= 225) return nil; } CFire *fire = GetNextFreeFire(); if (fire) { if (entityOnFire->IsPed()) { ped->m_pFire = fire; if (ped != FindPlayerPed()) { if (fleeFrom) { ped->SetFlee(fleeFrom, 10000); } else { CVector2D pos = entityOnFire->GetPosition(); ped->SetFlee(pos, 10000); ped->m_fleeFrom = nil; } ped->m_fleeTimer = CTimer::GetTimeInMilliseconds() + 10000; ped->bDrawLast = false; ped->SetMoveState(PEDMOVE_SPRINT); ped->SetMoveAnim(); ped->SetPedState(PED_ON_FIRE); } if (fleeFrom) { if (ped->m_nPedType == PEDTYPE_COP) { CEventList::RegisterEvent(EVENT_COP_SET_ON_FIRE, EVENT_ENTITY_PED, entityOnFire, (CPed *)fleeFrom, 10000); } else { CEventList::RegisterEvent(EVENT_PED_SET_ON_FIRE, EVENT_ENTITY_PED, entityOnFire, (CPed *)fleeFrom, 10000); } } } else { if (entityOnFire->IsVehicle()) { veh->m_pCarFire = fire; if (CModelInfo::IsBikeModel(veh->GetModelIndex()) || CModelInfo::IsCarModel(veh->GetModelIndex())) CCarAI::TellOccupantsToFleeCar(veh); if (fleeFrom) { CEventList::RegisterEvent(EVENT_CAR_SET_ON_FIRE, EVENT_ENTITY_VEHICLE, entityOnFire, (CPed *)fleeFrom, 10000); } } } fire->m_bIsOngoing = true; fire->m_bExtinguishedWithWater = false; fire->m_bIsScriptFire = false; fire->m_vecPos = entityOnFire->GetPosition(); if (entityOnFire && entityOnFire->IsPed() && ped->IsPlayer()) { fire->m_nExtinguishTime = CTimer::GetTimeInMilliseconds() + 3333; } else if (entityOnFire->IsVehicle()) { fire->m_nExtinguishTime = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(4000, 5000); } else { fire->m_nExtinguishTime = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(10000, 11000); } fire->m_nStartTime = CTimer::GetTimeInMilliseconds() + 400; fire->m_pEntity = entityOnFire; entityOnFire->RegisterReference(&fire->m_pEntity); fire->m_pSource = fleeFrom; if (fleeFrom) fleeFrom->RegisterReference(&fire->m_pSource); fire->ReportThisFire(); fire->m_nNextTimeToAddFlames = 0; fire->m_fStrength = strength; fire->m_bPropagationFlag = propagation; fire->m_bAudioSet = true; } return fire; } void CFireManager::Update(void) { for (int i = 0; i < NUM_FIRES; i++) { if (m_aFires[i].m_bIsOngoing) m_aFires[i].ProcessFire(); } } CFire* CFireManager::FindNearestFire(CVector vecPos, float *pDistance) { int fireId = -1; float minDistance = 999999; for (int j = 0; j < NUM_FIRES; j++) { if (!m_aFires[j].m_bIsOngoing) continue; if (m_aFires[j].m_bIsScriptFire) continue; float distance = (m_aFires[j].m_vecPos - vecPos).Magnitude2D(); if (distance < minDistance) { minDistance = distance; fireId = j; } } *pDistance = minDistance; if (fireId != -1) return &m_aFires[fireId]; return nil; } CFire * CFireManager::FindFurthestFire_NeverMindFireMen(CVector coords, float minRange, float maxRange) { int furthestFire = -1; float lastFireDist = 0.0f; float fireDist; for (int i = 0; i < NUM_FIRES; i++) { if (m_aFires[i].m_bIsOngoing && !m_aFires[i].m_bIsScriptFire) { fireDist = (m_aFires[i].m_vecPos - coords).Magnitude2D(); if (fireDist > minRange && fireDist < maxRange && fireDist > lastFireDist) { lastFireDist = fireDist; furthestFire = i; } } } if (furthestFire == -1) return nil; else return &m_aFires[furthestFire]; } CFire * CFireManager::GetNextFreeFire(void) { for (int i = 0; i < NUM_FIRES; i++) { if (!m_aFires[i].m_bIsOngoing && !m_aFires[i].m_bIsScriptFire) return &m_aFires[i]; } return nil; } uint32 CFireManager::GetTotalActiveFires(void) const { return m_nTotalFires; } void CFireManager::ExtinguishPoint(CVector point, float range) { for (int i = 0; i < NUM_FIRES; i++) { if (m_aFires[i].m_bIsOngoing) { if ((point - m_aFires[i].m_vecPos).MagnitudeSqr() < sq(range)) m_aFires[i].Extinguish(); } } } bool CFireManager::ExtinguishPointWithWater(CVector point, float range) { int i; for (i = 0; i < NUM_FIRES;) { if (m_aFires[i].m_bIsOngoing && (point - m_aFires[i].m_vecPos).MagnitudeSqr() < sq(range)) { break; } if (++i >= NUM_FIRES) return false; } CFire *fireToExtinguish = &m_aFires[i]; fireToExtinguish->m_fWaterExtinguishCountdown -= 0.012f * CTimer::GetTimeStep(); CVector steamPos = fireToExtinguish->m_vecPos + CVector((CGeneral::GetRandomNumber() - 128) * 3.1f / 200.f, (CGeneral::GetRandomNumber() - 128) * 3.1f / 200.f, CGeneral::GetRandomNumber() / 200.f); CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, steamPos, CVector(0.f, 0.f, 0.2f), nil, 0.5f); CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, steamPos, CVector(0.f, 0.f, 0.1f), nil, 0.8f); fireToExtinguish->m_bExtinguishedWithWater = true; if (fireToExtinguish->m_fWaterExtinguishCountdown < 0.0f ) fireToExtinguish->Extinguish(); return true; } int32 CFireManager::StartScriptFire(const CVector &pos, CEntity *target, float strength, uint8 propagation) { CFire *fire; CPed *ped = (CPed *)target; CVehicle *veh = (CVehicle *)target; if (target) { if (target->IsPed()) { if (ped->m_pFire) ped->m_pFire->Extinguish(); } else if (target->IsVehicle()) { if (veh->m_pCarFire) veh->m_pCarFire->Extinguish(); if (veh->IsCar() && ((CAutomobile *)veh)->Damage.GetEngineStatus() >= 225) { ((CAutomobile *)veh)->Damage.SetEngineStatus(215); } } } fire = GetNextFreeFire(); fire->m_bIsOngoing = true; fire->m_bIsScriptFire = true; fire->m_bPropagationFlag = propagation; fire->m_bAudioSet = true; fire->m_vecPos = pos; fire->m_nStartTime = CTimer::GetTimeInMilliseconds() + 400; fire->m_pEntity = target; fire->m_bExtinguishedWithWater = false; if (target) target->RegisterReference(&fire->m_pEntity); fire->m_pSource = nil; fire->m_nNextTimeToAddFlames = 0; fire->m_fStrength = strength; fire->m_fWaterExtinguishCountdown = 1.0f; if (target) { if (target->IsPed()) { ped->m_pFire = fire; if (target != (CVehicle *)FindPlayerPed()) { CVector2D pos = target->GetPosition(); ped->SetFlee(pos, 10000); ped->SetMoveAnim(); ped->SetPedState(PED_ON_FIRE); } } else if (target->IsVehicle()) { veh->m_pCarFire = fire; } } return fire - m_aFires; } bool CFireManager::IsScriptFireExtinguish(int16 index) { return !m_aFires[index].m_bIsOngoing; } void CFireManager::RemoveAllScriptFires(void) { for (int i = 0; i < NUM_FIRES; i++) { if (m_aFires[i].m_bIsScriptFire) { RemoveScriptFire(i); } } } void CFireManager::RemoveScriptFire(int16 index) { m_aFires[index].Extinguish(); m_aFires[index].m_bIsScriptFire = false; } void CFireManager::SetScriptFireAudio(int16 index, bool state) { m_aFires[index].m_bAudioSet = state; } ================================================ FILE: src/core/Fire.h ================================================ #pragma once class CEntity; class CFire { public: bool m_bIsOngoing; bool m_bIsScriptFire; bool m_bPropagationFlag; bool m_bAudioSet; CVector m_vecPos; CEntity *m_pEntity; CEntity *m_pSource; uint32 m_nExtinguishTime; uint32 m_nStartTime; uint32 m_nNextTimeToAddFlames; float m_fStrength; float m_fWaterExtinguishCountdown; bool m_bExtinguishedWithWater; CFire(); ~CFire(); void ProcessFire(void); void ReportThisFire(void); void Extinguish(void); }; class CFireManager { enum { MAX_FIREMEN_ATTENDING = 2, }; public: uint32 m_nTotalFires; CFire m_aFires[NUM_FIRES]; void StartFire(CVector pos, float size, uint8 propagation); CFire *StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength, uint8 propagation); void Update(void); CFire *FindFurthestFire_NeverMindFireMen(CVector coords, float minRange, float maxRange); CFire *FindNearestFire(CVector vecPos, float *pDistance); CFire *GetNextFreeFire(void); uint32 GetTotalActiveFires() const; void ExtinguishPoint(CVector point, float range); bool ExtinguishPointWithWater(CVector point, float range); int32 StartScriptFire(const CVector &pos, CEntity *target, float strength, uint8 propagation); bool IsScriptFireExtinguish(int16 index); void RemoveAllScriptFires(void); void RemoveScriptFire(int16 index); void SetScriptFireAudio(int16 index, bool state); }; extern CFireManager gFireManager; ================================================ FILE: src/core/FrontEndControls.cpp ================================================ #include "common.h" #include "main.h" #include "Timer.h" #include "Sprite2d.h" #include "Text.h" #include "Font.h" #include "FrontEndControls.h" #define X SCREEN_SCALE_X #define Y(x) SCREEN_SCALE_Y(float(x)*(float(DEFAULT_SCREEN_HEIGHT)/float(SCREEN_HEIGHT_PAL))) void CPlaceableShText::Draw(float x, float y) { if(m_text == nil) return; if(m_bRightJustify) CFont::SetRightJustifyOn(); if(m_bDropShadow){ CFont::SetDropShadowPosition(m_shadowOffset.x); CFont::SetDropColor(m_shadowColor); } CFont::SetColor(m_color); CFont::PrintString(x+m_position.x, y+m_position.y, m_text); if(m_bDropShadow) CFont::SetDropShadowPosition(0); if(m_bRightJustify) CFont::SetRightJustifyOff(); } void CPlaceableShText::Draw(const CRGBA &color, float x, float y) { if(m_text == nil) return; if(m_bRightJustify) CFont::SetRightJustifyOn(); if(m_bDropShadow){ CFont::SetDropShadowPosition(m_shadowOffset.x); CFont::SetDropColor(m_shadowColor); } CFont::SetColor(color); CFont::PrintString(x+m_position.x, y+m_position.y, m_text); if(m_bDropShadow) CFont::SetDropShadowPosition(0); if(m_bRightJustify) CFont::SetRightJustifyOff(); } void CPlaceableShTextTwoLines::Draw(float x, float y) { if(m_line1.m_text == nil && m_line2.m_text == nil) return; if(m_bRightJustify) CFont::SetRightJustifyOn(); if(m_bDropShadow){ CFont::SetDropShadowPosition(m_shadowOffset.x); CFont::SetDropColor(m_shadowColor); } if(m_line1.m_text){ CFont::SetColor(m_line1.m_color); CFont::PrintString(x+m_line1.m_position.x, y+m_line1.m_position.y, m_line1.m_text); } if(m_line2.m_text){ CFont::SetColor(m_line2.m_color); CFont::PrintString(x+m_line2.m_position.x, y+m_line2.m_position.y, m_line2.m_text); } if(m_bDropShadow) CFont::SetDropShadowPosition(0); if(m_bRightJustify) CFont::SetRightJustifyOff(); } void CPlaceableShTextTwoLines::Draw(const CRGBA &color, float x, float y) { if(m_line1.m_text == nil && m_line2.m_text == nil) return; if(m_bRightJustify) CFont::SetRightJustifyOn(); if(m_bDropShadow){ CFont::SetDropShadowPosition(m_shadowOffset.x); CFont::SetDropColor(m_shadowColor); } if(m_line1.m_text){ CFont::SetColor(color); CFont::PrintString(x+m_line1.m_position.x, y+m_line1.m_position.y, m_line1.m_text); } if(m_line2.m_text){ CFont::SetColor(color); CFont::PrintString(x+m_line2.m_position.x, y+m_line2.m_position.y, m_line2.m_text); } if(m_bDropShadow) CFont::SetDropShadowPosition(0); if(m_bRightJustify) CFont::SetRightJustifyOff(); } void CPlaceableShOption::Draw(const CRGBA &highlightColor, float x, float y, bool bHighlight) { if(bHighlight) CPlaceableShText::Draw(highlightColor, x, y); else if(m_bSelected) CPlaceableShText::Draw(m_selectedColor, x, y); else CPlaceableShText::Draw(x, y); } void CPlaceableShOptionTwoLines::Draw(const CRGBA &highlightColor, float x, float y, bool bHighlight) { if(bHighlight) CPlaceableShTextTwoLines::Draw(highlightColor, x, y); else if(m_bSelected) CPlaceableShTextTwoLines::Draw(m_selectedColor, x, y); else CPlaceableShTextTwoLines::Draw(x, y); } void CPlaceableSprite::Draw(float x, float y) { Draw(m_color, x, y); } void CPlaceableSprite::Draw(const CRGBA &color, float x, float y) { if(m_pSprite) m_pSprite->Draw(CRect(m_position.x+x, m_position.y+y, m_position.x+x + m_size.x, m_position.y+y + m_size.y), color); } void CPlaceableShSprite::Draw(float x, float y) { if(m_bDropShadow) m_shadow.Draw(m_shadow.m_color, m_sprite.m_position.x+x, m_sprite.m_position.y+y); m_sprite.Draw(x, y); } /* * CMenuPictureAndText */ void CMenuPictureAndText::SetNewOldShadowWrapX(bool bWrapX, float newWrapX, float oldWrapX) { m_bWrap = bWrapX; m_wrapX = newWrapX; m_oldWrapx = oldWrapX; } void CMenuPictureAndText::SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale) { m_bSetTextScale = bTextScale; m_textScale = newScale; m_oldTextScale = oldScale; } void CMenuPictureAndText::SetTextsColor(CRGBA const &color) { int i; for(i = 0; i < m_numTexts; i++) m_texts[i].m_color = color; } void CMenuPictureAndText::AddText(wchar *text, float positionX, float positionY, CRGBA const &color, bool bRightJustify) { int i; if(m_numTexts >= 20) return; i = m_numTexts++; m_texts[i].m_text = text; m_texts[i].m_position.x = positionX; m_texts[i].m_position.y = positionY; m_texts[i].m_color = color; m_texts[i].m_bRightJustify = bRightJustify; } void CMenuPictureAndText::AddPicture(CSprite2d *sprite, CSprite2d *shadow, float positionX, float positionY, float width, float height, CRGBA const &color) { int i; if(m_numSprites >= 5) return; i = m_numSprites++; m_sprites[i].m_sprite.m_pSprite = sprite; m_sprites[i].m_shadow.m_pSprite = shadow; m_sprites[i].m_sprite.m_position.x = positionX; m_sprites[i].m_sprite.m_position.y = positionY; m_sprites[i].m_sprite.m_size.x = width; m_sprites[i].m_sprite.m_size.y = height; m_sprites[i].m_shadow.m_size.x = width; m_sprites[i].m_shadow.m_size.y = height; m_sprites[i].m_sprite.m_color = color; } void CMenuPictureAndText::AddPicture(CSprite2d *sprite, float positionX, float positionY, float width, float height, CRGBA const &color) { int i; if(m_numSprites >= 5) return; i = m_numSprites++; m_sprites[i].m_sprite.m_pSprite = sprite; m_sprites[i].m_shadow.m_pSprite = nil; m_sprites[i].m_sprite.m_position.x = positionX; m_sprites[i].m_sprite.m_position.y = positionY; m_sprites[i].m_sprite.m_size.x = width; m_sprites[i].m_sprite.m_size.y = height; m_sprites[i].m_shadow.m_size.x = width; m_sprites[i].m_shadow.m_size.y = height; m_sprites[i].m_sprite.m_color = color; } void CMenuPictureAndText::Draw(CRGBA const &,CRGBA const &, float x, float y) { int i; for(i = 0; i < m_numSprites; i++) m_sprites[i].Draw(m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); for(i = 0; i < m_numTexts; i++) if(m_bWrap) m_texts[i].DrawShWrap(m_position.x+x, m_position.y+y, m_wrapX, m_oldWrapx); else m_texts[i].Draw(m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } void CMenuPictureAndText::SetAlpha(uint8 alpha) { int i; for(i = 0; i < m_numSprites; i++) m_sprites[i].SetAlpha(alpha); for(i = 0; i < m_numTexts; i++) m_texts[i].SetAlpha(alpha); } void CMenuPictureAndText::SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) { int i; for(i = 0; i < 5; i++) m_sprites[i].SetShadows(bDropShadows, shadowColor, shadowOffset); for(i = 0; i < 20; i++) m_texts[i].SetShadows(bDropShadows, shadowColor, shadowOffset); } /* * CMenuMultiChoice */ void CMenuMultiChoice::AddTitle(wchar *text, float positionX, float positionY, bool bRightJustify) { m_title.m_text = text; m_title.SetPosition(positionX, positionY, bRightJustify); } CPlaceableShOption* CMenuMultiChoice::AddOption(wchar *text, float positionX, float positionY, bool bSelected, bool bRightJustify) { if(m_numOptions == NUM_MULTICHOICE_OPTIONS) return nil; m_options[m_numOptions].m_text = text; m_options[m_numOptions].SetPosition(positionX, positionY); m_options[m_numOptions].m_bSelected = bSelected; m_options[m_numOptions].m_bRightJustify = bRightJustify; return &m_options[m_numOptions++]; } void CMenuMultiChoice::SetColors(const CRGBA &title, const CRGBA &normal, const CRGBA &selected) { int i; m_title.SetColor(title); for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].SetColors(normal, selected); } void CMenuMultiChoice::SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale) { m_bSetTextScale = bTextScale; m_textScale = newScale; m_oldTextScale = oldScale; m_bSetTitleTextScale = bTitleTextScale; } void CMenuMultiChoice::Draw(CRGBA const &optionHighlight ,CRGBA const &titleHighlight, float x, float y) { int i; if(m_bSetTextScale && m_bSetTitleTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_cursor == -1) m_title.Draw(m_position.x+x, m_position.y+y); else m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_cursor == -1) for(i = 0; i < m_numOptions; i++) m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); else for(i = 0; i < m_numOptions; i++){ if(i == m_cursor) m_options[i].Draw(optionHighlight, m_position.x+x, m_position.y+y); else m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); } if(m_bSetTextScale){ CFont::DrawFonts(); CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } } void CMenuMultiChoice::DrawNormal(float x, float y) { int i; if(m_bSetTextScale && m_bSetTitleTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); m_title.Draw(m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); for(i = 0; i < m_numOptions; i++) m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); if(m_bSetTextScale){ CFont::DrawFonts(); CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } } void CMenuMultiChoice::DrawHighlighted(CRGBA const &titleHighlight, float x, float y) { int i; if(m_bSetTextScale && m_bSetTitleTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_cursor == -1) m_title.Draw(m_position.x+x, m_position.y+y); else m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); for(i = 0; i < m_numOptions; i++) m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); if(m_bSetTextScale){ CFont::DrawFonts(); CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } } void CMenuMultiChoice::SetAlpha(uint8 alpha) { int i; m_title.SetAlpha(alpha); for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].SetAlpha(alpha); } void CMenuMultiChoice::SetShadows(bool bDropShadows, CRGBA const &shadowColor, CVector2D const &shadowOffset) { int i; m_title.SetShadows(bDropShadows, shadowColor, shadowOffset); for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].SetShadows(bDropShadows, shadowColor, shadowOffset); } bool CMenuMultiChoice::GoNext(void) { if(m_cursor == m_numOptions-1){ m_cursor = -1; return false; }else{ m_cursor++; return true; } } bool CMenuMultiChoice::GoPrev(void) { if(m_cursor == 0){ m_cursor = -1; return false; }else{ m_cursor--; return true; } } void CMenuMultiChoice::SelectCurrentOptionUnderCursor(void) { int i; if(m_cursor == -1) return; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].m_bSelected = false; m_options[m_cursor].m_bSelected = true; } int CMenuMultiChoice::GetMenuSelection(void) { int i; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) if(m_options[i].m_bSelected) return i; return -1; } void CMenuMultiChoice::SetMenuSelection(int selection) { int i; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].m_bSelected = false; m_options[selection%NUM_MULTICHOICE_OPTIONS].m_bSelected = true; } /* * CMenuMultiChoiceTriggered */ void CMenuMultiChoiceTriggered::Initialise(void) { int i; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_triggers[i] = nil; m_defaultCancel = nil; } CPlaceableShOption* CMenuMultiChoiceTriggered::AddOption(wchar *text, float positionX, float positionY, Trigger trigger, bool bSelected, bool bRightJustify) { CPlaceableShOption *option; option = CMenuMultiChoice::AddOption(text, positionX, positionY, bSelected, bRightJustify); if(option) m_triggers[m_numOptions-1] = trigger; return option; } void CMenuMultiChoiceTriggered::SelectCurrentOptionUnderCursor(void) { CMenuMultiChoice::SelectCurrentOptionUnderCursor(); if(m_cursor != -1 && m_triggers[m_cursor] != nil ) m_triggers[m_cursor](this); } void CMenuMultiChoiceTriggered::SelectDefaultCancelAction(void) { if(m_defaultCancel) m_defaultCancel(this); } /* * CMenuMultiChoiceTriggeredAlways */ void CMenuMultiChoiceTriggeredAlways::Draw(CRGBA const &optionHighlight, CRGBA const &titleHighlight, float x, float y) { if(m_alwaysTrigger) m_alwaysTrigger(this); CMenuMultiChoiceTriggered::Draw(optionHighlight, titleHighlight, x, y); } void CMenuMultiChoiceTriggeredAlways::DrawNormal(float x, float y) { if(m_alwaysNormalTrigger) m_alwaysNormalTrigger(this); CMenuMultiChoiceTriggered::DrawNormal(x, y); } void CMenuMultiChoiceTriggeredAlways::DrawHighlighted(CRGBA const &titleHighlight, float x, float y) { if(m_alwaysHighlightTrigger) m_alwaysHighlightTrigger(this); CMenuMultiChoiceTriggered::DrawHighlighted(titleHighlight, x, y); } /* * CMenuMultiChoicePictured */ void CMenuMultiChoicePictured::Initialise(void) { int i; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_bHasSprite[i] = false; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_sprites[i].m_pSprite = nil; } CPlaceableShOption* CMenuMultiChoicePictured::AddOption(CSprite2d *sprite, float positionX, float positionY, const CVector2D &size, bool bSelected) { CPlaceableShOption *option; option = CMenuMultiChoice::AddOption(nil, 0.0f, 0.0f, bSelected, false); if(option){ m_sprites[m_numOptions-1].m_pSprite = sprite; m_sprites[m_numOptions-1].SetPosition(positionX, positionY); m_sprites[m_numOptions-1].m_size = size; m_bHasSprite[m_numOptions-1] = true; } return option; } void CMenuMultiChoicePictured::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) { int i; // The title and all the text CMenuMultiChoice::Draw(optionHighlight, titleHighlight, x, y); CRGBA selectedColor = m_options[0].GetSelectedColor(); CRGBA color = m_options[0].GetColor(); // The sprites if(m_cursor == -1){ for(i = 0; i < m_numOptions; i++) if(m_bHasSprite[i]){ if(m_options[i].m_bSelected) m_sprites[i].Draw(selectedColor, m_position.x+x, m_position.y+y); else m_sprites[i].Draw(color, m_position.x+x, m_position.y+y); } }else{ for(i = 0; i < m_numOptions; i++) if(i == m_cursor){ if(m_bHasSprite[i]) { uint8 color = Max(Max(optionHighlight.r, optionHighlight.g), optionHighlight.b); m_sprites[i].Draw(CRGBA(color, color, color, optionHighlight.a), m_position.x+x, m_position.y+y); } }else{ if(m_bHasSprite[i]){ if(m_options[i].m_bSelected) m_sprites[i].Draw(selectedColor, m_position.x+x, m_position.y+y); else m_sprites[i].Draw(color, m_position.x+x, m_position.y+y); } } } } void CMenuMultiChoicePictured::DrawNormal(float x, float y) { int i; // The title and all the text CMenuMultiChoice::DrawNormal(x, y); CRGBA selectedColor = m_options[0].GetSelectedColor(); CRGBA color = m_options[0].GetColor(); // The sprites for(i = 0; i < m_numOptions; i++) if(m_bHasSprite[i]){ if(m_options[i].m_bSelected) m_sprites[i].Draw(selectedColor, m_position.x+x, m_position.y+y); else m_sprites[i].Draw(color, m_position.x+x, m_position.y+y); } } void CMenuMultiChoicePictured::DrawHighlighted(const CRGBA &titleHighlight, float x, float y) { int i; // The title and all the text CMenuMultiChoice::DrawHighlighted(titleHighlight, x, y); CRGBA selectedColor = m_options[0].GetSelectedColor(); CRGBA color = m_options[0].GetColor(); // The sprites for(i = 0; i < m_numOptions; i++) if(m_bHasSprite[i]){ if(m_options[i].m_bSelected) m_sprites[i].Draw(selectedColor, m_position.x+x, m_position.y+y); else m_sprites[i].Draw(color, m_position.x+x, m_position.y+y); } } void CMenuMultiChoicePictured::SetAlpha(uint8 alpha) { int i; CMenuMultiChoice::SetAlpha(alpha); for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_sprites[i].SetAlpha(alpha); } /* * CMenuMultiChoicePicturedTriggered */ void CMenuMultiChoicePicturedTriggered::Initialise(void) { int i; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_triggers[i] = nil; m_defaultCancel = nil; // missing on PS2 } CPlaceableShOption* CMenuMultiChoicePicturedTriggered::AddOption(CSprite2d *sprite, float positionX, float positionY, const CVector2D &size, Trigger trigger, bool bSelected) { CPlaceableShOption *option; option = CMenuMultiChoicePictured::AddOption(sprite, positionX, positionY, size, bSelected); if(option) m_triggers[m_numOptions-1] = trigger; return option; } void CMenuMultiChoicePicturedTriggered::SelectCurrentOptionUnderCursor(void) { CMenuMultiChoice::SelectCurrentOptionUnderCursor(); if(m_cursor != -1) m_triggers[m_cursor](this); } void CMenuMultiChoicePicturedTriggered::SelectDefaultCancelAction(void) { if(m_defaultCancel) m_defaultCancel(this); } /* * CMenuMultiChoicePicturedTriggeredAnyMove */ void CMenuMultiChoicePicturedTriggeredAnyMove::Initialise(void) { int i; CMenuMultiChoicePicturedTriggered::Initialise(); for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++){ m_moveTab[i].right = -1; m_moveTab[i].left = -1; m_moveTab[i].down = -1; m_moveTab[i].up = -1; } } CPlaceableShOption* CMenuMultiChoicePicturedTriggeredAnyMove::AddOption(CSprite2d *sprite, FEC_MOVETAB *moveTab, float positionX, float positionY, const CVector2D &size, Trigger trigger, bool bSelected) { CPlaceableShOption *option; option = CMenuMultiChoicePicturedTriggered::AddOption(sprite, positionX, positionY, size, trigger, bSelected); if(option && moveTab) m_moveTab[m_numOptions-1] = *moveTab; return option; } bool CMenuMultiChoicePicturedTriggeredAnyMove::GoDown(void) { int move = m_moveTab[m_cursor].down; if(move == -1) return GoNext(); m_cursor = move; return true; } bool CMenuMultiChoicePicturedTriggeredAnyMove::GoUp(void) { int move = m_moveTab[m_cursor].up; if(move == -1) return GoPrev(); m_cursor = move; return true; } bool CMenuMultiChoicePicturedTriggeredAnyMove::GoLeft(void) { int move = m_moveTab[m_cursor].left; if(move == -1) return GoPrev(); m_cursor = move; return true; } bool CMenuMultiChoicePicturedTriggeredAnyMove::GoRight(void) { int move = m_moveTab[m_cursor].right; if(move == -1) return GoNext(); m_cursor = move; return true; } /* * CMenuMultiChoiceTwoLines */ void CMenuMultiChoiceTwoLines::AddTitle(wchar *text, float positionX, float positionY, bool bRightJustify) { m_title.m_text = text; m_title.SetPosition(positionX, positionY, bRightJustify); } CPlaceableShOptionTwoLines* CMenuMultiChoiceTwoLines::AddOption(wchar *text, float positionX, float positionY, bool bSelected, bool bRightJustify) { return AddOption(text, positionX, positionY, nil, 0.0f, 0.0f, bSelected, bRightJustify); } CPlaceableShOptionTwoLines* CMenuMultiChoiceTwoLines::AddOption(wchar *text1, float positionX1, float positionY1, wchar *text2, float positionX2, float positionY2, bool bSelected, bool bRightJustify) { if(m_numOptions == NUM_MULTICHOICE_OPTIONS) return nil; m_options[m_numOptions].m_line1.m_text = text1; m_options[m_numOptions].m_line2.m_text = text2; m_options[m_numOptions].m_line1.SetPosition(positionX1, positionY1); m_options[m_numOptions].m_line2.SetPosition(positionX2, positionY2); m_options[m_numOptions].m_bSelected = bSelected; m_options[m_numOptions].m_bRightJustify = bRightJustify; return &m_options[m_numOptions++]; } void CMenuMultiChoiceTwoLines::SetColors(const CRGBA &title, const CRGBA &normal, const CRGBA &selected) { int i; m_title.SetColor(title); for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].SetColors(normal, selected); } void CMenuMultiChoiceTwoLines::SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale) { m_bSetTextScale = bTextScale; m_textScale = newScale; m_oldTextScale = oldScale; m_bSetTitleTextScale = bTitleTextScale; } void CMenuMultiChoiceTwoLines::Draw(CRGBA const &optionHighlight ,CRGBA const &titleHighlight, float x, float y) { int i; if(m_bSetTextScale && m_bSetTitleTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_cursor == -1) m_title.Draw(m_position.x+x, m_position.y+y); else m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_cursor == -1) for(i = 0; i < m_numOptions; i++) m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); else for(i = 0; i < m_numOptions; i++){ if(i == m_cursor) m_options[i].Draw(optionHighlight, m_position.x+x, m_position.y+y); else m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); } if(m_bSetTextScale){ CFont::DrawFonts(); CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } } void CMenuMultiChoiceTwoLines::DrawNormal(float x, float y) { int i; if(m_bSetTextScale && m_bSetTitleTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); m_title.Draw(m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); for(i = 0; i < m_numOptions; i++) m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); if(m_bSetTextScale){ CFont::DrawFonts(); CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } } void CMenuMultiChoiceTwoLines::DrawHighlighted(CRGBA const &titleHighlight, float x, float y) { int i; if(m_bSetTextScale && m_bSetTitleTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_cursor == -1) m_title.Draw(m_position.x+x, m_position.y+y); else m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); for(i = 0; i < m_numOptions; i++) m_options[i].Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); if(m_bSetTextScale){ CFont::DrawFonts(); CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } } void CMenuMultiChoiceTwoLines::SetAlpha(uint8 alpha) { int i; m_title.SetAlpha(alpha); for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].SetAlpha(alpha); } void CMenuMultiChoiceTwoLines::SetShadows(bool bDropShadows, CRGBA const &shadowColor, CVector2D const &shadowOffset) { int i; m_title.SetShadows(bDropShadows, shadowColor, shadowOffset); for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].SetShadows(bDropShadows, shadowColor, shadowOffset); } bool CMenuMultiChoiceTwoLines::GoNext(void) { if(m_cursor == m_numOptions-1){ m_cursor = -1; return false; }else{ m_cursor++; return true; } } bool CMenuMultiChoiceTwoLines::GoPrev(void) { if(m_cursor == 0){ m_cursor = -1; return false; }else{ m_cursor--; return true; } } void CMenuMultiChoiceTwoLines::SelectCurrentOptionUnderCursor(void) { int i; if(m_cursor == -1) return; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].m_bSelected = false; m_options[m_cursor].m_bSelected = true; } int CMenuMultiChoiceTwoLines::GetMenuSelection(void) { int i; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) if(m_options[i].m_bSelected) return i; return -1; } void CMenuMultiChoiceTwoLines::SetMenuSelection(int selection) { int i; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_options[i].m_bSelected = false; m_options[selection%NUM_MULTICHOICE_OPTIONS].m_bSelected = true; } /* * CMenuMultiChoiceTwoLinesTriggered */ void CMenuMultiChoiceTwoLinesTriggered::Initialise(void) { int i; for(i = 0; i < NUM_MULTICHOICE_OPTIONS; i++) m_triggers[i] = nil; m_defaultCancel = nil; } CPlaceableShOptionTwoLines* CMenuMultiChoiceTwoLinesTriggered::AddOption(wchar *text, float positionX, float positionY, Trigger trigger, bool bSelected, bool bRightJustify) { CPlaceableShOptionTwoLines *option; option = CMenuMultiChoiceTwoLines::AddOption(text, positionX, positionY, bSelected, bRightJustify); if(option) m_triggers[m_numOptions-1] = trigger; return option; } CPlaceableShOptionTwoLines* CMenuMultiChoiceTwoLinesTriggered::AddOption(wchar *text1, float positionX1, float positionY1, wchar *text2, float positionX2, float positionY2, Trigger trigger, bool bSelected, bool bRightJustify) { CPlaceableShOptionTwoLines *option; option = CMenuMultiChoiceTwoLines::AddOption(text1, positionX1, positionY1, text2, positionX2, positionY2, bSelected, bRightJustify); if(option) m_triggers[m_numOptions-1] = trigger; return option; } void CMenuMultiChoiceTwoLinesTriggered::SelectCurrentOptionUnderCursor(void) { CMenuMultiChoiceTwoLines::SelectCurrentOptionUnderCursor(); if(m_cursor != -1) m_triggers[m_cursor](this); } void CMenuMultiChoiceTwoLinesTriggered::SelectDefaultCancelAction(void) { if(m_defaultCancel) m_defaultCancel(this); } /* * CMenuOnOff */ void CMenuOnOff::SetColors(const CRGBA &title, const CRGBA &options) { m_title.SetColors(title, title); m_options[0].SetColor(options); m_options[1].SetColor(options); } void CMenuOnOff::SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale) { m_bSetTextScale = bTextScale; m_textScale = newScale; m_oldTextScale = oldScale; m_bSetTitleTextScale = bTitleTextScale; } void CMenuOnOff::SetOptionPosition(float x, float y, bool bRightJustify) { m_options[0].SetPosition(x, y, bRightJustify); m_options[1].SetPosition(x, y, bRightJustify); } void CMenuOnOff::AddTitle(wchar *text, bool bSelected, float positionX, float positionY, bool bRightJustify) { m_title.m_text = text; m_title.m_bSelected = bSelected; m_title.SetPosition(positionX, positionY, bRightJustify); } void CMenuOnOff::Draw(CRGBA const &optionHighlight, CRGBA const &titleHighlight, float x, float y) { if(m_type == 1){ m_options[0].m_text = TheText.Get("FEM_NO"); m_options[1].m_text = TheText.Get("FEM_YES"); }else if(m_type == 0){ m_options[0].m_text = TheText.Get("FEM_OFF"); m_options[1].m_text = TheText.Get("FEM_ON"); } if(m_bSetTextScale && m_bSetTitleTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_bActive) m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); else m_title.Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_bActive){ if(m_title.m_bSelected) m_options[1].Draw(optionHighlight, m_position.x+x, m_position.y+y); else m_options[0].Draw(optionHighlight, m_position.x+x, m_position.y+y); }else{ if(m_title.m_bSelected) m_options[1].Draw(m_position.x+x, m_position.y+y); else m_options[0].Draw(m_position.x+x, m_position.y+y); } if(m_bSetTextScale) CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } void CMenuOnOff::DrawNormal(float x, float y) { if(m_type == 1){ m_options[0].m_text = TheText.Get("FEM_NO"); m_options[1].m_text = TheText.Get("FEM_YES"); }else if(m_type == 0){ m_options[0].m_text = TheText.Get("FEM_OFF"); m_options[1].m_text = TheText.Get("FEM_ON"); } if(m_bSetTextScale && m_bSetTitleTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); m_title.Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_title.m_bSelected) m_options[1].Draw(m_position.x+x, m_position.y+y); else m_options[0].Draw(m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } void CMenuOnOff::DrawHighlighted(CRGBA const &titleHighlight, float x, float y) { if(m_type == 1){ m_options[0].m_text = TheText.Get("FEM_NO"); m_options[1].m_text = TheText.Get("FEM_YES"); }else if(m_type == 0){ m_options[0].m_text = TheText.Get("FEM_OFF"); m_options[1].m_text = TheText.Get("FEM_ON"); } if(m_bSetTextScale && m_bSetTitleTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_bActive) m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); else m_title.Draw(CRGBA(0,0,0,0), m_position.x+x, m_position.y+y, false); if(m_bSetTextScale) CFont::SetScale(m_textScale.x, m_textScale.y); if(m_title.m_bSelected) m_options[1].Draw(m_position.x+x, m_position.y+y); else m_options[0].Draw(m_position.x+x, m_position.y+y); if(m_bSetTextScale) CFont::SetScale(m_oldTextScale.x, m_oldTextScale.y); } void CMenuOnOff::SetAlpha(uint8 alpha) { m_title.SetAlpha(alpha); m_options[0].SetAlpha(alpha); m_options[1].SetAlpha(alpha); } void CMenuOnOff::SetShadows(bool bDropShadows, CRGBA const &shadowColor, CVector2D const &shadowOffset) { m_title.SetShadows(bDropShadows, shadowColor, shadowOffset); m_options[0].SetShadows(bDropShadows, shadowColor, shadowOffset); m_options[1].SetShadows(bDropShadows, shadowColor, shadowOffset); } /* * CMenuOnOffTriggered */ void CMenuOnOffTriggered::SetOptionPosition(float x, float y, Trigger trigger, bool bRightJustify) { CMenuOnOff::SetOptionPosition(x, y, bRightJustify); if(trigger) m_trigger = trigger; } void CMenuOnOffTriggered::SelectCurrentOptionUnderCursor(void) { CMenuOnOff::SelectCurrentOptionUnderCursor(); if(m_trigger) m_trigger(this); } /* * CMenuSlider */ char CMenuSlider::Buf8[8]; wchar CMenuSlider::Buf16[8]; void CMenuSlider::SetColors(const CRGBA &title, const CRGBA &percentage, const CRGBA &left, const CRGBA &right) { m_title.SetColor(title); m_percentageText.SetColor(percentage); m_colors[0] = left; m_colors[1] = right; } void CMenuSlider::AddTickBox(float positionX, float positionY, float width, float heightLeft, float heightRight) { m_box.SetPosition(positionX, positionY); m_size[0].x = width; m_size[0].y = heightLeft; m_size[1].x = width; m_size[1].y = heightRight; } void CMenuSlider::AddTitle(wchar *text, float positionX, float positionY) { m_title.m_text = text; m_title.SetPosition(positionX, positionY); } static CRGBA SELECTED_TEXT_COLOR_0(255, 182, 48, 255); void CMenuSlider::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) { if(m_bActive){ CRGBA selectionCol = m_colors[0]; if(optionHighlight.red == SELECTED_TEXT_COLOR_0.red && optionHighlight.green == SELECTED_TEXT_COLOR_0.green && optionHighlight.blue == SELECTED_TEXT_COLOR_0.blue && optionHighlight.alpha == SELECTED_TEXT_COLOR_0.alpha) selectionCol = m_colors[1]; if(m_style == 1){ // solid bar CRGBA shadowCol = m_box.GetShadowColor(); float f = m_value/1000.0f; CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); if(m_box.m_bDropShadow) CSprite2d::DrawRect( CRect(boxPos.x + X(m_box.m_shadowOffset.x), boxPos.y + Y(m_box.m_shadowOffset.y), boxPos.x + X(m_box.m_shadowOffset.x) + m_size[0].x, boxPos.y + Y(m_box.m_shadowOffset.y) + m_size[0].y), shadowCol); CSprite2d::DrawRect( CRect(boxPos.x, boxPos.y, boxPos.x + m_size[0].x, boxPos.y + m_size[0].y), m_colors[1]); CSprite2d::DrawRect( CRect(boxPos.x, boxPos.y, boxPos.x + m_size[0].x*f, boxPos.y + m_size[0].y), selectionCol); }else if(m_style == 0){ // ticks... CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); DrawTicks(boxPos, m_size[0], m_size[1].y, m_value/1000.0f, m_colors[0], selectionCol, m_colors[1], m_box.m_bDropShadow, m_box.m_shadowOffset, m_box.GetShadowColor()); } m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); if(m_bDrawPercentage){ sprintf(Buf8, "%d%%", m_value/10); AsciiToUnicode(Buf8, Buf16); m_percentageText.m_text = Buf16; m_percentageText.Draw(optionHighlight, m_position.x+x, m_position.y+y); } }else CMenuSlider::DrawNormal(x, y); } void CMenuSlider::DrawNormal(float x, float y) { if(m_style == 1){ // solid bar CRGBA shadowCol = m_box.GetShadowColor(); float f = m_value/1000.0f; CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); if(m_box.m_bDropShadow) CSprite2d::DrawRect( CRect(boxPos.x + X(m_box.m_shadowOffset.x), boxPos.y + Y(m_box.m_shadowOffset.y), boxPos.x + X(m_box.m_shadowOffset.x) + m_size[0].x, boxPos.y + Y(m_box.m_shadowOffset.y) + m_size[0].y), shadowCol); CSprite2d::DrawRect( CRect(boxPos.x, boxPos.y, boxPos.x + m_size[0].x, boxPos.y + m_size[0].y), m_colors[1]); CSprite2d::DrawRect( CRect(boxPos.x, boxPos.y, boxPos.x + m_size[0].x*f, boxPos.y + m_size[0].y), m_colors[0]); }else if(m_style == 0){ // ticks... CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); DrawTicks(boxPos, m_size[0], m_size[1].y, m_value/1000.0f, m_colors[0], m_colors[1], m_box.m_bDropShadow, m_box.m_shadowOffset, m_box.GetShadowColor()); } m_title.Draw(m_position.x+x, m_position.y+y); if(m_bDrawPercentage){ sprintf(Buf8, "%d%%", m_value/10); AsciiToUnicode(Buf8, Buf16); m_percentageText.m_text = Buf16; m_percentageText.Draw(m_percentageText.GetColor(), m_position.x+x, m_position.y+y); } } void CMenuSlider::DrawHighlighted(const CRGBA &titleHighlight, float x, float y) { if(m_bActive) m_title.Draw(titleHighlight, m_position.x+x, m_position.y+y); else m_title.Draw(m_position.x+x, m_position.y+y); if(m_style == 1){ // solid bar CRGBA shadowCol = m_box.GetShadowColor(); float f = m_value/1000.0f; CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); if(m_box.m_bDropShadow) CSprite2d::DrawRect( CRect(boxPos.x + X(m_box.m_shadowOffset.x), boxPos.y + Y(m_box.m_shadowOffset.y), boxPos.x + X(m_box.m_shadowOffset.x) + m_size[0].x, boxPos.y + Y(m_box.m_shadowOffset.y) + m_size[0].y), shadowCol); CSprite2d::DrawRect( CRect(boxPos.x, boxPos.y, boxPos.x + m_size[0].x, boxPos.y + m_size[0].y), m_colors[1]); CSprite2d::DrawRect( CRect(boxPos.x, boxPos.y, boxPos.x + m_size[0].x*f, boxPos.y + m_size[0].y), m_colors[0]); }else if(m_style == 0){ // ticks... CVector2D boxPos = m_box.m_position + m_position + CVector2D(x,y); DrawTicks(boxPos, m_size[0], m_size[1].y, m_value/1000.0f, m_colors[0], m_colors[1], m_box.m_bDropShadow, m_box.m_shadowOffset, m_box.GetShadowColor()); } if(m_bDrawPercentage){ sprintf(Buf8, "%d%%", m_value/10); AsciiToUnicode(Buf8, Buf16); m_percentageText.m_text = Buf16; m_percentageText.Draw(m_percentageText.GetColor(), m_position.x+x, m_position.y+y); } } void CMenuSlider::DrawTicks(const CVector2D &position, const CVector2D &size, float heightRight, float level, const CRGBA &leftCol, const CRGBA &selCol, const CRGBA &rightCol, bool bShadow, const CVector2D &shadowOffset, const CRGBA &shadowColor) { int i; int numTicks = size.x / X(8.0f); float dy = heightRight - size.y; float stepy = dy / numTicks; int left = level*numTicks; int drewSelection = 0; for(i = 0; i < numTicks; i++){ CRect rect(position.x + X(8.0f)*i, position.y + dy - stepy*i, position.x + X(8.0f)*i + X(4.0f), position.y + dy + size.y); if(bShadow){ CRect shadowRect = rect; shadowRect.left += X(shadowOffset.x); shadowRect.right += X(shadowOffset.x); shadowRect.top += Y(shadowOffset.y); shadowRect.bottom += Y(shadowOffset.y); CSprite2d::DrawRect(shadowRect, shadowColor); } if(i < left) CSprite2d::DrawRect(rect, leftCol); else if(!drewSelection){ CSprite2d::DrawRect(rect, selCol); drewSelection = 1; }else CSprite2d::DrawRect(rect, rightCol); } } void CMenuSlider::DrawTicks(const CVector2D &position, const CVector2D &size, float heightRight, float level, const CRGBA &leftCol, const CRGBA &rightCol, bool bShadow, const CVector2D &shadowOffset, const CRGBA &shadowColor) { int i; int numTicks = size.x / X(8.0f); float dy = heightRight - size.y; float stepy = dy / numTicks; int left = level*numTicks; for(i = 0; i < numTicks; i++){ CRect rect(position.x + X(8.0f)*i, position.y + dy - stepy*i, position.x + X(8.0f)*i + X(4.0f), position.y + dy + size.y); if(bShadow){ CRect shadowRect = rect; shadowRect.left += X(shadowOffset.x); shadowRect.right += X(shadowOffset.x); shadowRect.top += Y(shadowOffset.y); shadowRect.bottom += Y(shadowOffset.y); CSprite2d::DrawRect(shadowRect, shadowColor); } if(i < left) CSprite2d::DrawRect(rect, leftCol); else CSprite2d::DrawRect(rect, rightCol); } } void CMenuSlider::SetAlpha(uint8 alpha) { m_title.SetAlpha(alpha); m_box.SetAlpha(alpha); m_someAlpha = alpha; m_percentageText.SetAlpha(alpha); m_colors[0].alpha = alpha; m_colors[1].alpha = alpha; } void CMenuSlider::SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) { m_title.SetShadows(bDropShadows, shadowColor, shadowOffset); m_box.SetShadows(bDropShadows, shadowColor, shadowOffset); m_percentageText.SetShadows(bDropShadows, shadowColor, shadowOffset); } /* * CMenuSliderTriggered */ void CMenuSliderTriggered::AddTickBox(float positionX, float positionY, float width, float heightLeft, float heightRight, Trigger trigger, Trigger alwaysTrigger) { CMenuSlider::AddTickBox(positionX, positionY, width, heightLeft, heightRight); m_trigger = trigger; m_alwaysTrigger = alwaysTrigger; } void CMenuSliderTriggered::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) { CMenuSlider::Draw(optionHighlight, titleHighlight, x, y); if(m_alwaysTrigger) m_alwaysTrigger(this); } bool CMenuSliderTriggered::GoLeft(void) { CMenuSlider::GoLeft(); if(m_trigger) m_trigger(this); return true; } bool CMenuSliderTriggered::GoRight(void) { CMenuSlider::GoRight(); if(m_trigger) m_trigger(this); return true; } bool CMenuSliderTriggered::GoLeftStill(void) { CMenuSlider::GoLeftStill(); if(m_trigger) m_trigger(this); return true; } bool CMenuSliderTriggered::GoRightStill(void) { CMenuSlider::GoRightStill(); if(m_trigger) m_trigger(this); return true; } /* * CMenuLineLister */ CMenuLineLister::CMenuLineLister(void) : m_numLines(0), m_width(0.0f), m_height(0.0f), m_scrollPosition(0.0f), m_scrollSpeed(1.0f), m_lineSpacing(15.0f), field_10E8(0) { int i; for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ m_lineAlphas[i] = 0; m_lineFade[i] = 0; } } void CMenuLineLister::SetLinesColor(const CRGBA &color) { int i; for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ m_linesLeft[i].SetColor(color); m_linesRight[i].SetColor(color); } } void CMenuLineLister::ResetNumberOfTextLines(void) { int i; m_numLines = 0; for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ m_lineAlphas[i] = 0; m_lineFade[i] = 0; } for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ // note this doesn't clear lines 0-14, probably an oversight GetLeftLine(i)->m_text = nil; GetRightLine(i)->m_text = nil; } } bool CMenuLineLister::AddTextLine(wchar *left, wchar *right) { CPlaceableShText *leftLine, *rightLine; if(m_numLines == NUM_LINELISTER_LINES) return false; leftLine = GetLeftLine(m_numLines); leftLine->m_text = left; leftLine->SetPosition(0.0f, m_lineSpacing*(m_numLines+15)); rightLine = GetRightLine(m_numLines); rightLine->m_text = right; rightLine->SetPosition(leftLine->m_position.x, leftLine->m_position.y); m_numLines++; return true; } void CMenuLineLister::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) { int i, n; m_scrollPosition += m_scrollSpeed; n = m_numLines + 15; if(m_scrollSpeed > 0.0f){ if(m_scrollPosition > n*m_lineSpacing) m_scrollPosition = 0.0f; }else{ if(m_scrollPosition < 0.0f) m_scrollPosition = n*m_lineSpacing; } // this is a weird condition.... for(i = 0; m_scrollPosition < i*m_lineSpacing || m_scrollPosition >= (i+1)*m_lineSpacing; i++); float screenPos = 0.0f; for(; i < n; i++){ CVector2D linePos = m_linesLeft[i].m_position; if(linePos.y+m_position.y - (m_scrollPosition+m_position.y) < Y(64.0f)) m_lineFade[i] = -4.0f*Abs(m_scrollSpeed); else m_lineFade[i] = 4.0f*Abs(m_scrollSpeed); int newAlpha = m_lineAlphas[i] + m_lineFade[i]; if(newAlpha < 0) newAlpha = 0; if(newAlpha > 255) newAlpha = 255; m_lineAlphas[i] = newAlpha; uint8 alpha = m_linesLeft[i].m_shadowColor.alpha; // apply alpha m_linesLeft[i].SetAlpha((alpha*m_lineAlphas[i])>>8); m_linesRight[i].SetAlpha((alpha*m_lineAlphas[i])>>8); m_linesLeft[i].Draw(m_position.x+x, m_position.y+y - m_scrollPosition); CFont::SetRightJustifyOn(); m_linesRight[i].Draw(m_position.x+x + m_width, m_position.y+y - m_scrollPosition); CFont::SetRightJustifyOff(); // restore alpha m_linesLeft[i].SetAlpha(alpha); m_linesRight[i].SetAlpha(alpha); screenPos += m_lineSpacing; if(screenPos >= m_height) break; } m_scrollSpeed = 1.0f; } void CMenuLineLister::SetAlpha(uint8 alpha) { int i; for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ m_linesLeft[i].SetAlpha(alpha); m_linesRight[i].SetAlpha(alpha); } } void CMenuLineLister::SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) { int i; for(i = 0; i < NUM_LINELISTER_LINES_TOTAL; i++){ m_linesLeft[i].SetShadows(bDropShadows, shadowColor, shadowOffset); m_linesRight[i].SetShadows(bDropShadows, shadowColor, shadowOffset); } } /* * CMenuPage */ void CMenuPage::Initialise(void) { int i; m_numControls = 0; m_pCurrentControl = nil; m_cursor = 0; for(i = 0; i < NUM_PAGE_WIDGETS; i++) m_controls[i] = nil; } bool CMenuPage::AddMenu(CMenuBase *widget) { if(m_numControls >= NUM_PAGE_WIDGETS) return false; m_controls[m_numControls] = widget; if(m_numControls == 0){ m_pCurrentControl = widget; m_cursor = 0; } m_numControls++; return true; } bool CMenuPage::IsActiveMenuTwoState(void) { return m_pCurrentControl && m_pCurrentControl->m_bTwoState; } void CMenuPage::ActiveMenuTwoState_SelectNextPosition(void) { int sel; if(m_pCurrentControl == nil || !m_pCurrentControl->m_bTwoState) return; m_pCurrentControl->GoFirst(); sel = m_pCurrentControl->GetMenuSelection(); if(sel == 1) m_pCurrentControl->SelectCurrentOptionUnderCursor(); else if(sel == 0){ if ( m_pCurrentControl ) { if ( !m_pCurrentControl->GoNext() ) m_pCurrentControl->GoFirst(); } m_pCurrentControl->SelectCurrentOptionUnderCursor(); } } void CMenuPage::Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) { int i; for(i = 0; i < m_numControls; i++) if(m_controls[i]){ if(i == m_cursor) m_controls[i]->Draw(optionHighlight, titleHighlight, x, y); else m_controls[i]->DrawNormal(x, y); } } void CMenuPage::DrawHighlighted(const CRGBA &titleHighlight, float x, float y) { int i; for(i = 0; i< m_numControls; i++) if(m_controls[i]){ if(i == m_cursor) m_controls[i]->DrawHighlighted(titleHighlight, x, y); else m_controls[i]->DrawNormal(x, y); } } void CMenuPage::DrawNormal(float x, float y) { int i; for(i = 0; i< m_numControls; i++) if(m_controls[i]) m_controls[i]->DrawNormal(x, y); } void CMenuPage::ActivatePage(void) { m_cursor = 0; if(m_numControls == 0) return; for(;;){ m_pCurrentControl = m_controls[m_cursor]; if(m_pCurrentControl->GoFirst()) return; if(m_cursor == m_numControls-1) m_cursor = 0; else m_cursor++; } } void CMenuPage::SetAlpha(uint8 alpha) { int i; for(i = 0; i< m_numControls; i++) if(m_controls[i]) m_controls[i]->SetAlpha(alpha); } void CMenuPage::SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) { int i; for(i = 0; i< m_numControls; i++) if(m_controls[i]) m_controls[i]->SetShadows(bDropShadows, shadowColor, shadowOffset); } void CMenuPage::GoUpMenuOnPage(void) { if(m_pCurrentControl == nil) return; m_pCurrentControl->DeactivateMenu(); do{ if(m_cursor == 0) m_cursor = m_numControls-1; else m_cursor--; m_pCurrentControl = m_controls[m_cursor]; }while(!m_pCurrentControl->GoLast()); } void CMenuPage::GoDownMenuOnPage(void) { if(m_pCurrentControl == nil) return; m_pCurrentControl->DeactivateMenu(); do{ if(m_cursor == m_numControls-1) m_cursor = 0; else m_cursor++; m_pCurrentControl = m_controls[m_cursor]; }while(!m_pCurrentControl->GoFirst()); } void CMenuPage::GoLeftMenuOnPage(void) { // same as up if(m_pCurrentControl == nil) return; m_pCurrentControl->DeactivateMenu(); do{ if(m_cursor == 0) m_cursor = m_numControls-1; else m_cursor--; m_pCurrentControl = m_controls[m_cursor]; }while(!m_pCurrentControl->GoLast()); } void CMenuPage::GoRightMenuOnPage(void) { // same as right if(m_pCurrentControl == nil) return; m_pCurrentControl->DeactivateMenu(); do{ if(m_cursor == m_numControls-1) m_cursor = 0; else m_cursor++; m_pCurrentControl = m_controls[m_cursor]; }while(!m_pCurrentControl->GoFirst()); } /* * CMenuPageAnyMove */ void CMenuPageAnyMove::Initialise(void) { int i; CMenuPage::Initialise(); for(i = 0; i < NUM_PAGE_WIDGETS; i++){ m_moveTab[i].left = -1; m_moveTab[i].right = -1; m_moveTab[i].up = -1; m_moveTab[i].down = -1; } } bool CMenuPageAnyMove::AddMenu(CMenuBase *widget, FEC_MOVETAB *moveTab) { if(AddMenu(widget)){ m_moveTab[m_numControls-1] = *moveTab; return true; } return false; } void CMenuPageAnyMove::GoUpMenuOnPage(void) { if(m_pCurrentControl == nil) return; m_pCurrentControl->DeactivateMenu(); int move = m_moveTab[m_cursor].up; if(move == -1) CMenuPage::GoUpMenuOnPage(); else{ // BUG: no else in original code m_cursor = move; m_pCurrentControl = m_controls[m_cursor]; m_pCurrentControl->GoLast(); } } void CMenuPageAnyMove::GoDownMenuOnPage(void) { if(m_pCurrentControl == nil) return; m_pCurrentControl->DeactivateMenu(); int move = m_moveTab[m_cursor].down; if(move == -1) CMenuPage::GoDownMenuOnPage(); else{ // BUG: no else in original code m_cursor = move; m_pCurrentControl = m_controls[m_cursor]; m_pCurrentControl->GoLast(); } } void CMenuPageAnyMove::GoLeftMenuOnPage(void) { if(m_pCurrentControl == nil) return; m_pCurrentControl->DeactivateMenu(); int move = m_moveTab[m_cursor].left; if(move == -1) CMenuPage::GoLeftMenuOnPage(); else{ // BUG: no else in original code m_cursor = move; m_pCurrentControl = m_controls[m_cursor]; m_pCurrentControl->GoLast(); } } void CMenuPageAnyMove::GoRightMenuOnPage(void) { if(m_pCurrentControl == nil) return; m_pCurrentControl->DeactivateMenu(); int move = m_moveTab[m_cursor].right; if(move == -1) CMenuPage::GoRightMenuOnPage(); else{ // BUG: no else in original code m_cursor = move; m_pCurrentControl = m_controls[m_cursor]; m_pCurrentControl->GoLast(); } } ================================================ FILE: src/core/FrontEndControls.h ================================================ #pragma once enum { NUM_MULTICHOICE_OPTIONS = 16, // 50 actual lines and 15 for spacing NUM_LINELISTER_LINES = 50, NUM_LINELISTER_LINES_TOTAL = NUM_LINELISTER_LINES + 15, NUM_PAGE_WIDGETS = 10, }; class CTriggerCaller { bool bHasTrigger; void *pTrigger; void (*pFunc)(void *); int field_C; public: CTriggerCaller() : bHasTrigger(false), pFunc(nil) {} void SetTrigger(void *func, void *trigger) { if ( !bHasTrigger ) { pFunc = (void (*)(void *))func; pTrigger = trigger; bHasTrigger = true; } } void CallTrigger(void) { if ( bHasTrigger && pFunc != nil ) pFunc(pTrigger); bHasTrigger = false; pFunc = nil; } bool CanCall() { return bHasTrigger; } }; class CPlaceableText { public: CVector2D m_position; CRGBA m_color; wchar *m_text; CPlaceableText(void) : m_position(0.0f, 0.0f), m_color(255, 255, 255, 255), m_text(nil) {} void SetPosition(float x, float y) { m_position.x = x; m_position.y = y; } void SetColor(const CRGBA &color) { m_color = color; } CRGBA GetColor(void) { return m_color; } void SetAlpha(uint8 alpha) { m_color.alpha = alpha; } }; // No trace of this in the game but it makes the other classes simpler class CPlaceableTextTwoLines { public: CPlaceableText m_line1; CPlaceableText m_line2; void SetColor(const CRGBA &color) { m_line1.SetColor(color); m_line2.SetColor(color); } void SetAlpha(uint8 alpha) { m_line1.SetAlpha(alpha); m_line2.SetAlpha(alpha); } }; // No trace of this in the game but it makes the other classes simpler class CShadowInfo { public: bool m_bRightJustify; bool m_bDropShadow; CRGBA m_shadowColor; CVector2D m_shadowOffset; CShadowInfo(void) : m_bRightJustify(false), m_bDropShadow(false), m_shadowColor(255, 255, 255, 255), m_shadowOffset(-1.0f, -1.0f) {} CRGBA GetShadowColor(void) { return m_shadowColor; } void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset){ m_bDropShadow = bDropShadows; m_shadowColor = shadowColor; m_shadowOffset = shadowOffset; } }; // No trace of this in the game but it makes the other classes simpler class CSelectable { public: bool m_bSelected; CRGBA m_selectedColor; CSelectable(void) : m_bSelected(false) {} CRGBA GetSelectedColor(void) { return m_selectedColor; } }; class CPlaceableShText : public CPlaceableText, public CShadowInfo { public: using CPlaceableText::SetPosition; void SetPosition(float x, float y, bool bRightJustify) { SetPosition(x, y); m_bRightJustify = bRightJustify; } void SetAlpha(uint8 alpha) { m_shadowColor.alpha = alpha; CPlaceableText::SetAlpha(alpha); } void Draw(float x, float y); void Draw(const CRGBA &color, float x, float y); // unused arguments it seems void DrawShWrap(float x, float y, float wrapX, float wrapY) { Draw(x, y); } }; class CPlaceableShTextTwoLines : public CPlaceableTextTwoLines, public CShadowInfo { public: void SetAlpha(uint8 alpha) { m_shadowColor.alpha = alpha; CPlaceableTextTwoLines::SetAlpha(alpha); } void Draw(float x, float y); void Draw(const CRGBA &color, float x, float y); }; class CPlaceableShOption : public CPlaceableShText, public CSelectable { public: void SetColors(const CRGBA &normal, const CRGBA &selection) { CPlaceableShText::SetColor(normal); m_selectedColor = selection; } void SetAlpha(uint8 alpha) { m_selectedColor.alpha = alpha; CPlaceableShText::SetAlpha(alpha); } using CPlaceableShText::Draw; void Draw(const CRGBA &highlightColor, float x, float y, bool bHighlight); }; class CPlaceableShOptionTwoLines : public CPlaceableShTextTwoLines, public CSelectable { public: void SetColors(const CRGBA &normal, const CRGBA &selection) { CPlaceableShTextTwoLines::SetColor(normal); m_selectedColor = selection; } void SetAlpha(uint8 alpha) { m_selectedColor.alpha = alpha; CPlaceableShTextTwoLines::SetAlpha(alpha); } using CPlaceableShTextTwoLines::Draw; void Draw(const CRGBA &highlightColor, float x, float y, bool bHighlight); }; class CPlaceableSprite { public: CSprite2d *m_pSprite; CVector2D m_position; CVector2D m_size; CRGBA m_color; CPlaceableSprite(void) : m_pSprite(nil), m_position(0.0f, 0.0f), m_size(0.0f, 0.0f), m_color(255, 255, 255, 255) {} void SetPosition(float x, float y) { m_position.x = x; m_position.y = y; } void SetAlpha(uint8 alpha) { m_color.alpha = alpha; } void Draw(float x, float y); void Draw(const CRGBA &color, float x, float y); }; class CPlaceableShSprite { public: CPlaceableSprite m_sprite; CPlaceableSprite m_shadow; bool m_bDropShadow; CPlaceableShSprite(void) : m_bDropShadow(false) {} void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset){ m_bDropShadow = bDropShadows; m_shadow.m_color = shadowColor; m_shadow.m_position = shadowOffset; } void SetAlpha(uint8 alpha) { m_sprite.SetAlpha(alpha); m_shadow.SetAlpha(alpha); } void Draw(float x, float y); }; class CMenuBase { public: CVector2D m_position; bool m_bTwoState; CMenuBase(void) : m_position(0.0f, 0.0f), m_bTwoState(false) {} void SetPosition(float x, float y) { m_position.x = x; m_position.y = y; } virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y) = 0; virtual void DrawNormal(float x, float y) = 0; virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y) = 0; virtual void SetAlpha(uint8 alpha) = 0; virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) = 0; virtual bool GoNext(void) = 0; virtual bool GoPrev(void) = 0; virtual bool GoDown(void) = 0; virtual bool GoUp(void) = 0; virtual bool GoDownStill(void) = 0; virtual bool GoUpStill(void) = 0; virtual bool GoLeft(void) = 0; virtual bool GoRight(void) = 0; virtual bool GoLeftStill(void) = 0; virtual bool GoRightStill(void) = 0; virtual bool GoFirst(void) = 0; virtual bool GoLast(void) = 0; virtual void SelectCurrentOptionUnderCursor(void) = 0; virtual void SelectDefaultCancelAction(void) = 0; virtual void ActivateMenu(bool first) = 0; virtual void DeactivateMenu(void) = 0; virtual int GetMenuSelection(void) = 0; virtual void SetMenuSelection(int selection) = 0; }; class CMenuDummy : public CMenuBase { public: bool m_bActive; virtual void Draw(const CRGBA &, const CRGBA &, float x, float y) {} virtual void DrawNormal(float x, float y) {} virtual void DrawHighlighted(const CRGBA &, float x, float y) {} virtual void SetAlpha(uint8 alpha) {} virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset) {} virtual bool GoNext(void) { DeactivateMenu(); return false; } virtual bool GoPrev(void) { DeactivateMenu(); return false; } virtual bool GoDown(void) { return GoNext(); } virtual bool GoUp(void) { return GoPrev(); } virtual bool GoDownStill(void) { return false; } virtual bool GoUpStill(void) { return false; } virtual bool GoLeft(void) { return true; } virtual bool GoRight(void) { return true; } virtual bool GoLeftStill(void) { return true; } virtual bool GoRightStill(void) { return true; } virtual bool GoFirst(void) { ActivateMenu(true); return true; } virtual bool GoLast(void) { ActivateMenu(true); return true; } virtual void SelectCurrentOptionUnderCursor(void) {} virtual void SelectDefaultCancelAction(void) {} virtual void ActivateMenu(bool first) { m_bActive = true; } virtual void DeactivateMenu(void) { m_bActive = false; } virtual int GetMenuSelection(void) { return -1; } virtual void SetMenuSelection(int) {} }; class CMenuPictureAndText : public CMenuBase { public: int m_numSprites; CPlaceableShSprite m_sprites[5]; int m_numTexts; CPlaceableShText m_texts[20]; CVector2D m_oldTextScale; CVector2D m_textScale; bool m_bSetTextScale; float m_wrapX; float m_oldWrapx; bool m_bWrap; // missing some? CMenuPictureAndText(void) : m_numSprites(0), m_numTexts(0), m_bSetTextScale(false), m_bWrap(false) {} void SetNewOldShadowWrapX(bool bWrapX, float newWrapX, float oldWrapX); void SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale); void SetTextsColor(const CRGBA &color); void AddText(wchar *text, float positionX, float positionY, const CRGBA &color, bool bRightJustify); void AddPicture(CSprite2d *sprite, CSprite2d *shadow, float positionX, float positionY, float width, float height, const CRGBA &color); void AddPicture(CSprite2d *sprite, float positionX, float positionY, float width, float height, const CRGBA &color); virtual void Draw(const CRGBA &, const CRGBA &, float x, float y); virtual void DrawNormal(float x, float y) { Draw(CRGBA(0,0,0,0), CRGBA(0,0,0,0), x, y); } virtual void DrawHighlighted(const CRGBA &, float x, float y) { Draw(CRGBA(0,0,0,0), CRGBA(0,0,0,0), x, y); } virtual void SetAlpha(uint8 alpha); virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); virtual bool GoNext(void) { return false; } virtual bool GoPrev(void) { return false; } virtual bool GoDown(void) { return GoNext(); } virtual bool GoUp(void) { return GoPrev(); } virtual bool GoDownStill(void) { return false; } virtual bool GoUpStill(void) { return false; } virtual bool GoLeft(void) { return true; } virtual bool GoRight(void) { return true; } virtual bool GoLeftStill(void) { return true; } virtual bool GoRightStill(void) { return true; } virtual bool GoFirst(void) { return false; } virtual bool GoLast(void) { return false; } virtual void SelectCurrentOptionUnderCursor(void) {} virtual void SelectDefaultCancelAction(void) {} virtual void ActivateMenu(bool first) {} virtual void DeactivateMenu(void) {} virtual int GetMenuSelection(void) { return -1; } virtual void SetMenuSelection(int) {} }; class CMenuMultiChoice : public CMenuBase { public: int m_numOptions; CPlaceableShText m_title; CPlaceableShOption m_options[NUM_MULTICHOICE_OPTIONS]; int m_cursor; CVector2D m_oldTextScale; CVector2D m_textScale; bool m_bSetTextScale; bool m_bSetTitleTextScale; CMenuMultiChoice(void) : m_numOptions(0), m_cursor(-1), m_bSetTextScale(false), m_bSetTitleTextScale(false) {} void AddTitle(wchar *text, float positionX, float positionY, bool bRightJustify); CPlaceableShOption *AddOption(wchar *text, float positionX, float positionY, bool bSelected, bool bRightJustify); void SetColors(const CRGBA &title, const CRGBA &normal, const CRGBA &selected); void SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale); virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); virtual void DrawNormal(float x, float y); virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); virtual void SetAlpha(uint8 alpha); virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); virtual bool GoNext(void); virtual bool GoPrev(void); virtual bool GoDown(void) { return GoNext(); } virtual bool GoUp(void) { return GoPrev(); } virtual bool GoDownStill(void) { return false; } virtual bool GoUpStill(void) { return false; } virtual bool GoLeft(void) { return GoPrev(); } virtual bool GoRight(void) { return GoNext(); } virtual bool GoLeftStill(void) { return true; } virtual bool GoRightStill(void) { return true; } virtual bool GoFirst(void) { m_cursor = 0; return true; } virtual bool GoLast(void) { m_cursor = m_numOptions-1; return true; } virtual void SelectCurrentOptionUnderCursor(void); virtual void SelectDefaultCancelAction(void) {} virtual void ActivateMenu(bool first) { m_cursor = first ? 0 : m_numOptions-1; } virtual void DeactivateMenu(void) { m_cursor = -1; } virtual int GetMenuSelection(void); virtual void SetMenuSelection(int selection); }; class CMenuMultiChoiceTriggered : public CMenuMultiChoice { public: typedef void (*Trigger)(CMenuMultiChoiceTriggered *); Trigger m_triggers[NUM_MULTICHOICE_OPTIONS]; Trigger m_defaultCancel; CMenuMultiChoiceTriggered(void) { Initialise(); } void Initialise(void); CPlaceableShOption *AddOption(wchar *text, float positionX, float positionY, Trigger trigger, bool bSelected, bool bRightJustify); virtual void SelectCurrentOptionUnderCursor(void); virtual void SelectDefaultCancelAction(void); }; class CMenuMultiChoiceTriggeredAlways : public CMenuMultiChoiceTriggered { public: Trigger m_alwaysNormalTrigger; Trigger m_alwaysHighlightTrigger; Trigger m_alwaysTrigger; CMenuMultiChoiceTriggeredAlways(void) : m_alwaysNormalTrigger(nil), m_alwaysHighlightTrigger(nil), m_alwaysTrigger(nil) {} virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); virtual void DrawNormal(float x, float y); virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); }; class CMenuMultiChoicePictured : public CMenuMultiChoice { public: CPlaceableSprite m_sprites[NUM_MULTICHOICE_OPTIONS]; bool m_bHasSprite[NUM_MULTICHOICE_OPTIONS]; CMenuMultiChoicePictured(void) { Initialise(); } void Initialise(void); using CMenuMultiChoice::AddOption; CPlaceableShOption *AddOption(CSprite2d *sprite, float positionX, float positionY, const CVector2D &size, bool bSelected); virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); virtual void DrawNormal(float x, float y); virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); virtual void SetAlpha(uint8 alpha); // unnecessary - same as base class // virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); }; class CMenuMultiChoicePicturedTriggered : public CMenuMultiChoicePictured { public: typedef void (*Trigger)(CMenuMultiChoicePicturedTriggered *); Trigger m_triggers[NUM_MULTICHOICE_OPTIONS]; Trigger m_defaultCancel; CMenuMultiChoicePicturedTriggered(void) { Initialise(); } void Initialise(void); using CMenuMultiChoicePictured::AddOption; CPlaceableShOption *AddOption(CSprite2d *sprite, float positionX, float positionY, const CVector2D &size, Trigger trigger, bool bSelected); virtual void SelectCurrentOptionUnderCursor(void); virtual void SelectDefaultCancelAction(void); }; struct FEC_MOVETAB { int8 right; int8 left; int8 down; int8 up; }; class CMenuMultiChoicePicturedTriggeredAnyMove : public CMenuMultiChoicePicturedTriggered { public: FEC_MOVETAB m_moveTab[NUM_MULTICHOICE_OPTIONS]; CMenuMultiChoicePicturedTriggeredAnyMove(void) { Initialise(); } void Initialise(void); using CMenuMultiChoicePicturedTriggered::AddOption; CPlaceableShOption *AddOption(CSprite2d *sprite, FEC_MOVETAB *moveTab, float positionX, float positionY, const CVector2D &size, Trigger trigger, bool bSelected); virtual bool GoDown(void); virtual bool GoUp(void); virtual bool GoLeft(void); virtual bool GoRight(void); }; // copy of CMenuMultiChoice pretty much except for m_options type class CMenuMultiChoiceTwoLines : public CMenuBase { public: int m_numOptions; CPlaceableShText m_title; CPlaceableShOptionTwoLines m_options[NUM_MULTICHOICE_OPTIONS]; int m_cursor; CVector2D m_oldTextScale; CVector2D m_textScale; bool m_bSetTextScale; bool m_bSetTitleTextScale; CMenuMultiChoiceTwoLines(void) : m_numOptions(0), m_cursor(-1), m_bSetTextScale(false), m_bSetTitleTextScale(false) {} void AddTitle(wchar *text, float positionX, float positionY, bool bRightJustify); CPlaceableShOptionTwoLines *AddOption(wchar *text, float positionX, float positionY, bool bSelected, bool bRightJustify); CPlaceableShOptionTwoLines *AddOption(wchar *text1, float positionX1, float positionY1, wchar *text2, float positionX2, float positionY2, bool bSelected, bool bRightJustify); void SetColors(const CRGBA &title, const CRGBA &normal, const CRGBA &selected); void SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale); virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); virtual void DrawNormal(float x, float y); virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); virtual void SetAlpha(uint8 alpha); virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); virtual bool GoNext(void); virtual bool GoPrev(void); virtual bool GoDown(void) { return GoNext(); } virtual bool GoUp(void) { return GoPrev(); } virtual bool GoDownStill(void) { return true; } virtual bool GoUpStill(void) { return true; } virtual bool GoLeft(void) { return GoPrev(); } virtual bool GoRight(void) { return GoNext(); } virtual bool GoLeftStill(void) { return true; } virtual bool GoRightStill(void) { return true; } virtual bool GoFirst(void) { m_cursor = 0; return true; } virtual bool GoLast(void) { m_cursor = m_numOptions-1; return true; } virtual void SelectCurrentOptionUnderCursor(void); virtual void SelectDefaultCancelAction(void) {} virtual void ActivateMenu(bool first) { m_cursor = first ? 0 : m_numOptions-1; } virtual void DeactivateMenu(void) { m_cursor = -1; } virtual int GetMenuSelection(void); virtual void SetMenuSelection(int selection); }; // copy of CMenuMultiChoiceTriggered except for m_options class CMenuMultiChoiceTwoLinesTriggered : public CMenuMultiChoiceTwoLines { public: typedef void (*Trigger)(CMenuMultiChoiceTwoLinesTriggered *); Trigger m_triggers[NUM_MULTICHOICE_OPTIONS]; Trigger m_defaultCancel; CMenuMultiChoiceTwoLinesTriggered(void) { Initialise(); } void Initialise(void); CPlaceableShOptionTwoLines *AddOption(wchar *text, float positionX, float positionY, Trigger trigger, bool bSelected, bool bRightJustify); CPlaceableShOptionTwoLines *AddOption(wchar *text1, float positionX1, float positionY1, wchar *text2, float positionX2, float positionY2, Trigger trigger, bool bSelected, bool bRightJustify); virtual void SelectCurrentOptionUnderCursor(void); virtual void SelectDefaultCancelAction(void); }; class CMenuOnOff : public CMenuBase { public: CPlaceableShOption m_title; CPlaceableShText m_options[2]; bool m_bActive; bool m_bSetTextScale; bool m_bSetTitleTextScale; CVector2D m_textScale; CVector2D m_oldTextScale; int m_type; // 0: on/off 1: yes/no void SetColors(const CRGBA &title, const CRGBA &options); void SetNewOldTextScale(bool bTextScale, const CVector2D &newScale, const CVector2D &oldScale, bool bTitleTextScale); void SetOptionPosition(float x, float y, bool bRightJustify); void AddTitle(wchar *text, bool bSelected, float positionX, float positionY, bool bRightJustify); virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); virtual void DrawNormal(float x, float y); virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); virtual void SetAlpha(uint8 alpha); virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); virtual bool GoNext(void) { DeactivateMenu(); return false; } virtual bool GoPrev(void) { DeactivateMenu(); return false; } virtual bool GoDown(void) { return GoNext(); } virtual bool GoUp(void) { return GoPrev(); } virtual bool GoDownStill(void) { return false; } virtual bool GoUpStill(void) { return false; } virtual bool GoLeft(void) { SelectCurrentOptionUnderCursor(); return true; } virtual bool GoRight(void) { SelectCurrentOptionUnderCursor(); return true; } virtual bool GoLeftStill(void) { return true; } virtual bool GoRightStill(void) { return true; } virtual bool GoFirst(void) { ActivateMenu(true); return true; } virtual bool GoLast(void) { ActivateMenu(true); return true; } virtual void SelectCurrentOptionUnderCursor(void) { m_title.m_bSelected ^= 1; } virtual void SelectDefaultCancelAction(void) {} virtual void ActivateMenu(bool first) { m_bActive = true; } virtual void DeactivateMenu(void) { m_bActive = false; } virtual int GetMenuSelection(void) { return m_title.m_bSelected; } virtual void SetMenuSelection(int selection) { m_title.m_bSelected = selection; } }; class CMenuOnOffTriggered : public CMenuOnOff { public: typedef void (*Trigger)(CMenuOnOffTriggered *); Trigger m_trigger; void SetOptionPosition(float x, float y, Trigger trigger, bool bRightJustify); virtual void SelectCurrentOptionUnderCursor(void); }; class CMenuSlider : public CMenuBase { public: CPlaceableShText m_title; CPlaceableShText m_box; // not really a text CRGBA m_colors[2]; // left and right CVector2D m_size[2]; // left and right int m_value; CPlaceableShText m_percentageText; bool m_bDrawPercentage; // char field_8D; // char field_8E; // char field_8F; uint8 m_someAlpha; // char field_91; // char field_92; // char field_93; bool m_bActive; int m_style; static char Buf8[8]; static wchar Buf16[8]; CMenuSlider(void) : m_value(0), m_bDrawPercentage(false), m_bActive(false), m_style(0) { AddTickBox(0.0f, 0.0f, 100.0f, 10.0f, 10.0f); //todo } void SetColors(const CRGBA &title, const CRGBA &percentage, const CRGBA &left, const CRGBA &right); void DrawTicks(const CVector2D &position, const CVector2D &size, float heightRight, float level, const CRGBA &leftCol, const CRGBA &selCol, const CRGBA &rightCol, bool bShadow, const CVector2D &shadowOffset, const CRGBA &shadowColor); void DrawTicks(const CVector2D &position, const CVector2D &size, float heightRight, float level, const CRGBA &leftCol, const CRGBA &rightCol, bool bShadow, const CVector2D &shadowOffset, const CRGBA &shadowColor); void AddTickBox(float positionX, float positionY, float width, float heigthLeft, float heightRight); void AddTitle(wchar *text, float positionX, float positionY); virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); virtual void DrawNormal(float x, float y); virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); virtual void SetAlpha(uint8 alpha); virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); virtual bool GoNext(void) { DeactivateMenu(); return false; } virtual bool GoPrev(void) { DeactivateMenu(); return false; } virtual bool GoDown(void) { return GoNext(); } virtual bool GoUp(void) { return GoPrev(); } virtual bool GoDownStill(void) { return false; } virtual bool GoUpStill(void) { return false; } virtual bool GoLeft(void) { if(m_value < 0) m_value = 0; return true; } virtual bool GoRight(void) { if(m_value > 1000) m_value = 1000; return true; } virtual bool GoLeftStill(void) { m_value -= 8; if(m_value < 0) m_value = 0; return true; } virtual bool GoRightStill(void) { m_value += 8; if(m_value > 1000) m_value = 1000; return true; } virtual bool GoFirst(void) { ActivateMenu(true); return true; } virtual bool GoLast(void) { ActivateMenu(true); return true; } virtual void SelectCurrentOptionUnderCursor(void) {} virtual void SelectDefaultCancelAction(void) {} virtual void ActivateMenu(bool first) { m_bActive = true; } virtual void DeactivateMenu(void) { m_bActive = false; } virtual int GetMenuSelection(void) { return m_value/10; } virtual void SetMenuSelection(int selection) { m_value = selection*10; } }; class CMenuSliderTriggered : public CMenuSlider { public: typedef void (*Trigger)(CMenuSliderTriggered *); Trigger m_trigger; Trigger m_alwaysTrigger; CMenuSliderTriggered(void) : m_trigger(nil), m_alwaysTrigger(nil) {} void AddTickBox(float positionX, float positionY, float width, float heigthLeft, float heightRight, Trigger trigger, Trigger alwaysTrigger); virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); virtual bool GoLeft(void); virtual bool GoRight(void); virtual bool GoLeftStill(void); virtual bool GoRightStill(void); }; class CMenuLineLister : public CMenuBase { public: float m_width; float m_height; int m_numLines; CPlaceableShText m_linesLeft[NUM_LINELISTER_LINES_TOTAL]; CPlaceableShText m_linesRight[NUM_LINELISTER_LINES_TOTAL]; uint8 m_lineAlphas[NUM_LINELISTER_LINES_TOTAL]; int8 m_lineFade[NUM_LINELISTER_LINES_TOTAL]; float m_scrollPosition; float m_scrollSpeed; int field_10E8; float m_lineSpacing; CMenuLineLister(void); void SetLinesColor(const CRGBA &color); void ResetNumberOfTextLines(void); bool AddTextLine(wchar *left, wchar *right); CPlaceableShText *GetLeftLine(int i) { return &m_linesLeft[(i%NUM_LINELISTER_LINES) + 15]; }; CPlaceableShText *GetRightLine(int i) { return &m_linesRight[(i%NUM_LINELISTER_LINES) + 15]; }; virtual void Draw(const CRGBA &optionHighlight, const CRGBA &titleHighlight, float x, float y); virtual void DrawNormal(float x, float y) { Draw(CRGBA(0,0,0,0), CRGBA(0,0,0,0), x, y); } virtual void DrawHighlighted(const CRGBA &titleHighlight, float x, float y) { Draw(CRGBA(0,0,0,0), CRGBA(0,0,0,0), x, y); } virtual void SetAlpha(uint8 alpha); virtual void SetShadows(bool bDropShadows, const CRGBA &shadowColor, const CVector2D &shadowOffset); virtual bool GoNext(void) { return false; } virtual bool GoPrev(void) { return false; } virtual bool GoDown(void) { return GoNext(); } virtual bool GoUp(void) { return GoPrev(); } virtual bool GoDownStill(void) { m_scrollSpeed = 0.0f; return true; } virtual bool GoUpStill(void) { m_scrollSpeed *= 6.0f; return true; } virtual bool GoLeft(void) { return true; } virtual bool GoRight(void) { return true; } virtual bool GoLeftStill(void) { return true; } virtual bool GoRightStill(void) { return true; } virtual bool GoFirst(void) { return true; } virtual bool GoLast(void) { return true; } virtual void SelectCurrentOptionUnderCursor(void) {} virtual void SelectDefaultCancelAction(void) {} virtual void ActivateMenu(bool first) {} virtual void DeactivateMenu(void) {} virtual int GetMenuSelection(void) { return -1; } virtual void SetMenuSelection(int selection) {} }; class CMenuPage { public: CMenuBase *m_controls[NUM_PAGE_WIDGETS]; int m_numControls; CMenuBase *m_pCurrentControl; int m_cursor; CMenuPage(void) { Initialise(); } void Initialise(void); bool AddMenu(CMenuBase *widget); bool IsActiveMenuTwoState(void); void ActiveMenuTwoState_SelectNextPosition(void); void Draw(const CRGBA &,const CRGBA &, float, float); void DrawHighlighted(const CRGBA &titleHighlight, float x, float y); void DrawNormal(float x, float y); void ActivatePage(void); void SetAlpha(uint8 alpha); void SetShadows(bool, const CRGBA &, const CVector2D &); void GoPrev(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoPrev()) m_pCurrentControl->GoLast(); } } void GoNext(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoNext()) m_pCurrentControl->GoFirst(); } } void GoLeft(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoLeft()) m_pCurrentControl->GoLast(); } } void GoRight(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoRight()) m_pCurrentControl->GoFirst(); } } void GoUp(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoUp()) m_pCurrentControl->GoLast(); } } void GoDown(void) { if(m_pCurrentControl) { if(!m_pCurrentControl->GoDown()) m_pCurrentControl->GoFirst(); } } void GoLeftStill(void) { if(m_pCurrentControl) m_pCurrentControl->GoLeftStill(); } void GoRightStill(void) { if(m_pCurrentControl) m_pCurrentControl->GoRightStill(); } void GoUpStill(void) { if(m_pCurrentControl) m_pCurrentControl->GoUpStill(); } void GoDownStill(void) { if(m_pCurrentControl) m_pCurrentControl->GoDownStill(); } void SelectDefaultCancelAction(void) { if(m_pCurrentControl) m_pCurrentControl->SelectDefaultCancelAction(); } void SelectCurrentOptionUnderCursor(void) { if(m_pCurrentControl) m_pCurrentControl->SelectCurrentOptionUnderCursor(); } virtual void GoUpMenuOnPage(void); virtual void GoDownMenuOnPage(void); virtual void GoLeftMenuOnPage(void); virtual void GoRightMenuOnPage(void); }; class CMenuPageAnyMove : public CMenuPage { public: FEC_MOVETAB m_moveTab[NUM_PAGE_WIDGETS]; CMenuPageAnyMove(void) { Initialise(); } void Initialise(void); using CMenuPage::AddMenu; bool AddMenu(CMenuBase *widget, FEC_MOVETAB *moveTab); virtual void GoUpMenuOnPage(void); virtual void GoDownMenuOnPage(void); virtual void GoLeftMenuOnPage(void); virtual void GoRightMenuOnPage(void); }; ================================================ FILE: src/core/Frontend.cpp ================================================ #define FORCE_PC_SCALING #define WITHWINDOWS #define WITHDINPUT #include "common.h" #ifndef PS2_MENU #include "crossplatform.h" #include "platform.h" #include "Frontend.h" #include "Font.h" #include "Pad.h" #include "Text.h" #include "main.h" #include "RwHelper.h" #include "Timer.h" #include "Game.h" #include "DMAudio.h" #include "FileMgr.h" #include "Streaming.h" #include "TxdStore.h" #include "General.h" #include "GenericGameStorage.h" #include "Script.h" #include "Camera.h" #include "ControllerConfig.h" #include "Vehicle.h" #include "MBlur.h" #include "PlayerSkin.h" #include "PlayerInfo.h" #include "World.h" #include "Renderer.h" #include "CdStream.h" #include "Radar.h" #include "Stats.h" #include "Messages.h" #include "FileLoader.h" #include "User.h" #include "sampman.h" // Similar story to Hud.cpp: // Game has colors inlined in code. // For easier modification we collect them here: const CRGBA LABEL_COLOR(255, 150, 225, 255); const CRGBA SELECTIONBORDER_COLOR(25, 130, 70, 255); const CRGBA MENUOPTION_COLOR = LABEL_COLOR; const CRGBA SELECTEDMENUOPTION_COLOR = LABEL_COLOR; const CRGBA HEADER_COLOR = LABEL_COLOR; const CRGBA DARKMENUOPTION_COLOR(195, 90, 165, 255); const CRGBA SLIDERON_COLOR(97, 194, 247, 255); const CRGBA SLIDEROFF_COLOR(27, 89, 130, 255); const CRGBA LIST_BACKGROUND_COLOR(49, 101, 148, 130); const CRGBA LIST_OPTION_COLOR(155, 155, 155, 255); const CRGBA RADIO_SELECTOR_COLOR = SLIDEROFF_COLOR; const CRGBA INACTIVE_RADIO_COLOR(100, 100, 255, 100); const CRGBA SCROLLBAR_COLOR = LABEL_COLOR; #define MAP_MIN_SIZE 162.f #define MAP_SIZE_TO_ALLOW_X_MOVE 297.f #define MAX_VISIBLE_LIST_ROW 30 #define SCROLLBAR_MAX_HEIGHT 263.0f // not in end result #define SCROLLABLE_PAGES #define hasNativeList(screen) (screen == MENUPAGE_SKIN_SELECT || screen == MENUPAGE_KEYBOARD_CONTROLS) #ifdef SCROLLABLE_PAGES #define MAX_VISIBLE_OPTION 12 #define MAX_VISIBLE_OPTION_ON_SCREEN (hasNativeList(m_nCurrScreen) ? MAX_VISIBLE_LIST_ROW : MAX_VISIBLE_OPTION) #define SCREEN_HAS_AUTO_SCROLLBAR (m_nTotalListRow > MAX_VISIBLE_OPTION && !hasNativeList(m_nCurrScreen)) int GetOptionCount(int screen) { int i = 0; for (; i < NUM_MENUROWS && aScreens[screen].m_aEntries[i].m_Action != MENUACTION_NOTHING; i++); return i; } #define SETUP_SCROLLING(screen) \ if (!hasNativeList(screen)) { \ m_nTotalListRow = GetOptionCount(screen); \ if (m_nTotalListRow > MAX_VISIBLE_OPTION) { \ m_nSelectedListRow = 0; \ m_nFirstVisibleRowOnList = 0; \ m_nScrollbarTopMargin = 0; \ } \ } #define MINUS_SCROLL_OFFSET - scrollOffset #else #define MAX_VISIBLE_OPTION_ON_SCREEN MAX_VISIBLE_LIST_ROW #define SETUP_SCROLLING(screen) #define MINUS_SCROLL_OFFSET #endif #ifdef TRIANGLE_BACK_BUTTON #define GetBackJustUp GetTriangleJustUp #define GetBackJustDown GetTriangleJustDown #elif defined(CIRCLE_BACK_BUTTON) #define GetBackJustUp GetCircleJustUp #define GetBackJustDown GetCircleJustDown #else #define GetBackJustUp GetSquareJustUp #define GetBackJustDown GetSquareJustDown #endif #ifdef MAP_ENHANCEMENTS CVector2D mapCrosshair; #endif #ifdef CUTSCENE_BORDERS_SWITCH bool CMenuManager::m_PrefsCutsceneBorders = true; #endif bool holdingScrollBar; // *(bool*)0x7039B9; // not original name CMenuManager FrontEndMenuManager; MenuTrapezoid menuBg(CGeneral::GetRandomNumber() % 40 + 65, CGeneral::GetRandomNumber() % 40 + 21, CGeneral::GetRandomNumber() % 40 + 568, CGeneral::GetRandomNumber() % 40 + 44, CGeneral::GetRandomNumber() % 40 + 36, CGeneral::GetRandomNumber() % 40 + 352, CGeneral::GetRandomNumber() % 40 + 593, CGeneral::GetRandomNumber() % 40 + 312); MenuTrapezoid menuOptionHighlight(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); #ifndef MASTER bool CMenuManager::m_PrefsMarketing = false; bool CMenuManager::m_PrefsDisableTutorials = false; #endif // !MASTER #ifdef GAMEPAD_MENU uint32 TimeToStopPadShaking; #endif const char* FrontendFilenames[][2] = { {"background", ""}, {"vc_logo", "vc_logom"}, {"mouse", "mousea"}, {"mapTop01", "mapTop01A"}, {"mapTop02", "mapTop02A"}, {"mapTop03", "mapTop03A"}, {"mapMid01", "mapMid01A"}, {"mapMid02", "mapMid02A"}, {"mapMid03", "mapMid03A"}, {"mapBot01", "mapBot01A"}, {"mapBot02", "mapBot02A"}, {"mapBot03", "mapBot03A"}, {"wildstyle", "wildstyleA"}, {"flash", "flashA"}, {"kchat", "kchatA"}, {"fever", "feverA"}, {"vrock", "vrockA"}, {"vcpr", "vcprA"}, {"espantoso", "espantosoA"}, {"emotion", "emotionA"}, {"wave103", "wave103A"}, {"mp3", "mp3A"}, {"downOff", "buttonA"}, {"downOn", "buttonA"}, {"upOff", "buttonA"}, {"upOn", "buttonA"}, #ifdef GAMEPAD_MENU {"fe_controller", "" }, {"fe_arrows1", "" }, {"fe_arrows2", "" }, {"fe_arrows3", "" }, {"fe_arrows4", "" }, #endif }; #define MENU_X_RIGHT_ALIGNED(x) SCALE_AND_CENTER_X(DEFAULT_SCREEN_WIDTH - (x)) #ifdef ASPECT_RATIO_SCALE // All of the defines below replace the StretchX function. Otherwise use SCREEN_SCALE_X. #define MENU_X_LEFT_ALIGNED(x) SCALE_AND_CENTER_X(x) #define MENU_X(x) SCREEN_SCALE_X(x) #define MENU_Y(y) SCREEN_SCALE_Y(y) #else #define MENU_X_LEFT_ALIGNED(x) StretchX(x) #define MENU_X(x) StretchX(x) #define MENU_Y(y) StretchY(y) #endif #ifdef XBOX_MESSAGE_SCREEN bool CMenuManager::m_bDialogOpen = false; uint32 CMenuManager::m_nDialogHideTimer = 0; PauseModeTime CMenuManager::m_nDialogHideTimerPauseMode = 0; bool CMenuManager::m_bSaveWasSuccessful = false; wchar* CMenuManager::m_pDialogText = nil; #endif #define SET_FONT_FOR_MENU_HEADER \ CFont::SetRightJustifyOn(); \ CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); \ CFont::SetScale(MENU_X(MENUHEADER_WIDTH), MENU_Y(MENUHEADER_HEIGHT)); \ CFont::SetDropShadowPosition(0); #define SET_FONT_FOR_LIST_ITEM \ CFont::SetRightJustifyOff(); \ CFont::SetScale(MENU_X(LISTITEM_X_SCALE), MENU_Y(LISTITEM_Y_SCALE)); \ CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); #define RESET_FONT_FOR_NEW_PAGE \ CFont::SetBackgroundOff(); \ CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT), MENU_Y(MENUACTION_SCALE_MULT)); \ CFont::SetPropOn(); \ CFont::SetCentreOff(); \ CFont::SetJustifyOn(); \ CFont::SetRightJustifyOff(); \ CFont::SetBackGroundOnlyTextOn(); \ CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(MENU_X_MARGIN)); \ CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(MENU_X_MARGIN)); #define ProcessSlider(value, origY, increaseAction, decreaseAction, hoverEndX, onlyWhenHoveringRow) \ do { \ float y = origY MINUS_SCROLL_OFFSET; \ lastActiveBarX = DisplaySlider(MENU_X_LEFT_ALIGNED(MENUSLIDER_X), MENU_Y(y), MENU_Y(MENUSLIDER_SMALLEST_BAR), MENU_Y(MENUSLIDER_BIGGEST_BAR), MENU_X(MENUSLIDER_UNK), value, MENU_X(3.0f)); \ if (i != m_nCurrOption || !itemsAreSelectable) \ break; \ \ if (CheckHover(0, lastActiveBarX - MENU_X(3.0f), MENU_Y(y), MENU_Y(MENUSLIDER_BIGGEST_BAR + y))) { \ m_nHoverOption = decreaseAction; \ break; \ } \ if (!CheckHover(MENU_X(3.0f) + lastActiveBarX, hoverEndX, MENU_Y(y), MENU_Y(MENUSLIDER_BIGGEST_BAR + y))) { \ m_nHoverOption = HOVEROPTION_NOT_HOVERING; \ break; \ } \ m_nHoverOption = increaseAction; \ if (m_nMousePosX < MENU_X_LEFT_ALIGNED(MENUSLIDER_X)) \ m_nHoverOption = HOVEROPTION_NOT_HOVERING; \ \ if (onlyWhenHoveringRow && (m_nMousePosY < MENU_Y(y) || m_nMousePosY > MENU_Y(MENUSLIDER_BIGGEST_BAR + y))) \ m_nHoverOption = HOVEROPTION_NOT_HOVERING; \ } while(0) // --- Functions not in the game/inlined starts inline void CMenuManager::ScrollUpListByOne() { if (m_nSelectedListRow == m_nFirstVisibleRowOnList) { if (m_nFirstVisibleRowOnList > 0) { m_nSelectedListRow--; m_nFirstVisibleRowOnList--; m_nScrollbarTopMargin -= SCROLLBAR_MAX_HEIGHT / m_nTotalListRow; } } else { m_nSelectedListRow--; } } inline void CMenuManager::ScrollDownListByOne() { if (m_nSelectedListRow == m_nFirstVisibleRowOnList + MAX_VISIBLE_OPTION_ON_SCREEN - 1) { if (m_nFirstVisibleRowOnList < m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN) { m_nSelectedListRow++; m_nFirstVisibleRowOnList++; m_nScrollbarTopMargin += SCROLLBAR_MAX_HEIGHT / m_nTotalListRow; } } else { if (m_nSelectedListRow < m_nTotalListRow - 1) { m_nSelectedListRow++; } } } inline void CMenuManager::PageUpList(bool playSoundOnSuccess) { if (m_nTotalListRow > MAX_VISIBLE_OPTION_ON_SCREEN) { if (m_nFirstVisibleRowOnList > 0) { if(playSoundOnSuccess) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); m_nFirstVisibleRowOnList = Max(0, m_nFirstVisibleRowOnList - MAX_VISIBLE_OPTION_ON_SCREEN); m_nSelectedListRow = Min(m_nSelectedListRow, m_nFirstVisibleRowOnList + MAX_VISIBLE_OPTION_ON_SCREEN - 1); } else { m_nFirstVisibleRowOnList = 0; m_nSelectedListRow = 0; } m_nScrollbarTopMargin = (SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) * m_nFirstVisibleRowOnList; } } inline void CMenuManager::PageDownList(bool playSoundOnSuccess) { if (m_nTotalListRow > MAX_VISIBLE_OPTION_ON_SCREEN) { if (m_nFirstVisibleRowOnList < m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN) { if(playSoundOnSuccess) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); m_nFirstVisibleRowOnList = Min(m_nFirstVisibleRowOnList + MAX_VISIBLE_OPTION_ON_SCREEN, m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN); m_nSelectedListRow = Max(m_nSelectedListRow, m_nFirstVisibleRowOnList); } else { m_nFirstVisibleRowOnList = m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN; m_nSelectedListRow = m_nTotalListRow - 1; } m_nScrollbarTopMargin = (SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) * m_nFirstVisibleRowOnList; } } #ifdef CUSTOM_FRONTEND_OPTIONS #define PLUS_LINE_HEIGHT_ON_SCREEN + (aScreens[m_nCurrScreen].layout ? aScreens[m_nCurrScreen].layout->lineHeight : MENU_DEFAULT_LINE_HEIGHT) bool ScreenHasOption(int screen, const char* gxtKey) { for (int i = 0; i < NUM_MENUROWS; i++) { if (strcmp(gxtKey, aScreens[screen].m_aEntries[i].m_EntryName) == 0) return true; } return false; } inline void CMenuManager::ThingsToDoBeforeLeavingPage() { if ((m_nCurrScreen == MENUPAGE_SKIN_SELECT) && strcmp(m_aSkinName, m_PrefsSkinFile) != 0) { CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); } else if (m_nCurrScreen == MENUPAGE_SOUND_SETTINGS) { if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) m_nPrefsAudio3DProviderIndex = DMAudio.GetCurrent3DProviderIndex(); DMAudio.StopFrontEndTrack(); OutputDebugString("FRONTEND AUDIO TRACK STOPPED"); } else if (ScreenHasOption(m_nCurrScreen, "FED_RES")) { m_nDisplayVideoMode = m_nPrefsVideoMode; } if (m_nCurrScreen == MENUPAGE_SKIN_SELECT) { CPlayerSkin::EndFrontendSkinEdit(); } #ifdef SCROLLABLE_PAGES if (SCREEN_HAS_AUTO_SCROLLBAR) { m_nSelectedListRow = 0; m_nFirstVisibleRowOnList = 0; m_nScrollbarTopMargin = 0; } #endif CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption]; if (option.m_Action == MENUACTION_CFO_DYNAMIC) if(option.m_CFODynamic->buttonPressFunc) option.m_CFODynamic->buttonPressFunc(FEOPTION_ACTION_FOCUSLOSS); if (option.m_Action == MENUACTION_CFO_SELECT && option.m_CFOSelect->onlyApplyOnEnter && option.m_CFOSelect->lastSavedValue != option.m_CFOSelect->displayedValue) option.m_CFOSelect->displayedValue = *option.m_CFO->value = option.m_CFOSelect->lastSavedValue; if (aScreens[m_nCurrScreen].returnPrevPageFunc) { aScreens[m_nCurrScreen].returnPrevPageFunc(); } } inline int8 CMenuManager::GetPreviousPageOption() { int8 prevPage = !m_bGameNotLoaded ? aScreens[m_nCurrScreen].m_PreviousPage : (m_nCurrScreen == MENUPAGE_NEW_GAME || m_nCurrScreen == MENUPAGE_OPTIONS || m_nCurrScreen == MENUPAGE_EXIT ? MENUPAGE_START_MENU : aScreens[m_nCurrScreen].m_PreviousPage); if (prevPage == -1) // Game also does same return 0; prevPage = prevPage == MENUPAGE_NONE ? (!m_bGameNotLoaded ? MENUPAGE_PAUSE_MENU : MENUPAGE_START_MENU) : prevPage; for (int i = 0; i < NUM_MENUROWS; i++) { if (aScreens[prevPage].m_aEntries[i].m_Action >= MENUACTION_NOTHING) { // CFO check if (aScreens[prevPage].m_aEntries[i].m_TargetMenu == m_nCurrScreen) { return i; } } } // This shouldn't happen return 0; } #else #define PLUS_LINE_HEIGHT_ON_SCREEN + MENU_DEFAULT_LINE_HEIGHT inline void CMenuManager::ThingsToDoBeforeLeavingPage() { switch (m_nCurrScreen) { case MENUPAGE_SOUND_SETTINGS: if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) m_nPrefsAudio3DProviderIndex = DMAudio.GetCurrent3DProviderIndex(); DMAudio.StopFrontEndTrack(); OutputDebugString("FRONTEND AUDIO TRACK STOPPED"); break; case MENUPAGE_DISPLAY_SETTINGS: m_nDisplayVideoMode = m_nPrefsVideoMode; break; case MENUPAGE_SKIN_SELECT: if (strcmp(m_aSkinName, m_PrefsSkinFile) != 0) CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); CPlayerSkin::EndFrontendSkinEdit(); break; } #ifdef SCROLLABLE_PAGES if (SCREEN_HAS_AUTO_SCROLLBAR) { m_nSelectedListRow = 0; m_nFirstVisibleRowOnList = 0; m_nScrollbarTopMargin = 0; } #endif } inline int8 CMenuManager::GetPreviousPageOption() { return (!m_bGameNotLoaded ? aScreens[m_nCurrScreen].m_ParentEntry : (m_nCurrScreen == MENUPAGE_NEW_GAME ? 0 : (m_nCurrScreen == MENUPAGE_OPTIONS ? 1 : (m_nCurrScreen == MENUPAGE_EXIT ? 2 : aScreens[m_nCurrScreen].m_ParentEntry)))); } #endif // ------ Functions not in the game/inlined ends bool DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); void DoRWStuffEndOfFrame(void); void CMenuManager::SwitchToNewScreen(int8 screen) { bMenuChangeOngoing = true; DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DrawBackground(true); DoRWStuffEndOfFrame(); DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DrawBackground(true); DoRWStuffEndOfFrame(); m_nPrevScreen = m_nCurrScreen; m_ShowEmptyBindingError = false; ResetHelperText(); ThingsToDoBeforeLeavingPage(); if (screen == -2) { int oldScreen = aScreens[m_nCurrScreen].m_PreviousPage; int oldOption = GetPreviousPageOption(); m_nCurrOption = oldOption; m_nCurrScreen = oldScreen; } else if (screen == 0) { m_nCurrScreen = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_TargetMenu; m_nCurrOption = 0; } else { m_nCurrOption = 0; m_nCurrScreen = screen; } SETUP_SCROLLING(m_nCurrScreen) if (hasNativeList(m_nPrevScreen)) m_nTotalListRow = 0; if (m_nCurrScreen == MENUPAGE_CHOOSE_SAVE_SLOT) m_nCurrOption = 8; m_nMenuFadeAlpha = 0; m_nOptionHighlightTransitionBlend = 0; m_LastScreenSwitch = CTimer::GetTimeInMillisecondsPauseMode(); } CMenuManager::CMenuManager() { m_StatsScrollSpeed = 150.0f; m_StatsScrollDirection = 1; m_PrefsSfxVolume = 49; m_PrefsMusicVolume = 49; m_PrefsRadioStation = 0; m_PrefsStereoMono = 1; m_PrefsBrightness = 256; m_PrefsLOD = CRenderer::ms_lodDistScale; m_KeyPressedCode = -1; m_bFrontEnd_ReloadObrTxtGxt = false; m_PrefsMP3BoostVolume = 0; m_PrefsShowSubtitles = 0; m_PrefsShowLegends = 1; #ifdef ASPECT_RATIO_SCALE m_PrefsUseWideScreen = AR_AUTO; #else m_PrefsUseWideScreen = 0; #endif m_PrefsVsync = 0; m_PrefsVsyncDisp = 1; m_PrefsFrameLimiter = 1; m_PrefsLanguage = 0; field_54 = 0; m_PrefsAllowNastyGame = 1; m_PrefsSpeakers = 0; field_8 = 0; m_PrefsUseVibration = 0; m_PrefsShowHud = 1; m_PrefsRadarMode = 0; m_DisplayControllerOnFoot = false; m_bShutDownFrontEndRequested = false; m_bStartUpFrontEndRequested = false; pEditString = nil; pControlEdit = nil; DisplayComboButtonErrMsg = false; m_PrefsDMA = 1; OS_Language = LANG_ENGLISH; m_ControlMethod = CONTROL_STANDARD; #ifdef PC_PLAYER_CONTROLS CCamera::m_bUseMouse3rdPerson = true; #else CCamera::m_bUseMouse3rdPerson = false; #endif m_lastWorking3DAudioProvider = 0; m_nFirstVisibleRowOnList = 0; m_nScrollbarTopMargin = 0.0f; m_nSelectedListRow = 0; m_nSkinsTotal = 0; m_nPrefsAudio3DProviderIndex = AUDIO_PROVIDER_NOT_DETERMINED; m_bGameNotLoaded = true; m_nMousePosX = m_nMouseTempPosX; m_nMousePosY = m_nMouseTempPosY; m_nMouseOldPosX = m_nMousePosX; m_nMouseOldPosY = m_nMousePosY; m_bShowMouse = true; m_nHoverOption = HOVEROPTION_NOT_HOVERING; DMAudio.SetMP3BoostVolume(m_PrefsMP3BoostVolume); m_bMenuActive = false; m_bActivateSaveMenu = false; m_bWantToLoad = false; m_nMenuFadeAlpha = 0; m_OnlySaveMenu = false; m_fMapSize = MENU_Y(162.0f); // Y because of HOR+ m_fMapCenterX = MENU_X_LEFT_ALIGNED(320.0f); m_fMapCenterY = MENU_Y(225.0f); DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); #ifdef NO_ISLAND_LOADING m_PrefsIslandLoading = ISLAND_LOADING_LOW; #endif #ifdef GAMEPAD_MENU m_PrefsControllerType = CONTROLLER_XBOXONE; #endif } void CMenuManager::SetFrontEndRenderStates(void) { RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); } void CMenuManager::Initialise(void) { DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DoRWStuffEndOfFrame(); m_AllowNavigation = false; m_firstStartCounter = -50; // to start from black m_nMenuFadeAlpha = 0; m_nCurrOption = 0; m_nOptionHighlightTransitionBlend = 0; CentreMousePointer(); m_bShowMouse = true; m_fMapSize = MENU_Y(162.0f); // Y because of HOR+ m_fMapCenterX = MENU_X_LEFT_ALIGNED(320.0f); m_fMapCenterY = MENU_Y(225.0f); CPad::StopPadsShaking(); if (!m_OnlySaveMenu) m_nCurrScreen = MENUPAGE_NONE; DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); DMAudio.Service(); DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); #ifdef FIX_BUGS static bool firstTime = true; if (firstTime) { DMAudio.SetRadioInCar(m_PrefsRadioStation); firstTime = false; } else #endif m_PrefsRadioStation = DMAudio.GetRadioInCar(); DMAudio.SetMP3BoostVolume(m_PrefsMP3BoostVolume); if (DMAudio.IsMP3RadioChannelAvailable()) { if (m_PrefsRadioStation < WILDSTYLE || m_PrefsRadioStation > USERTRACK) m_PrefsRadioStation = CGeneral::GetRandomNumber() % (USERTRACK + 1); } else if (m_PrefsRadioStation < WILDSTYLE || m_PrefsRadioStation > WAVE) m_PrefsRadioStation = CGeneral::GetRandomNumber() % (WAVE + 1); CFileMgr::SetDir(""); //CFileMgr::SetDir(""); PcSaveHelper.PopulateSlotInfo(); CTimer::StartUserPause(); } void CMenuManager::CentreMousePointer() { if (SCREEN_WIDTH * 0.5f != 0.0f && 0.0f != SCREEN_HEIGHT * 0.5f) { #if defined RW_D3D9 || defined RWLIBS tagPOINT Point; Point.x = SCREEN_WIDTH / 2; Point.y = SCREEN_HEIGHT / 2; ClientToScreen(PSGLOBAL(window), &Point); SetCursorPos(Point.x, Point.y); #elif defined RW_GL3 glfwSetCursorPos(PSGLOBAL(window), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); #endif PSGLOBAL(lastMousePos.x) = SCREEN_WIDTH / 2; PSGLOBAL(lastMousePos.y) = SCREEN_HEIGHT / 2; } } void CMenuManager::CheckCodesForControls(int typeOfControl) { DisplayComboButtonErrMsg = false; bool invalidKey = false; bool escPressed = false; eControllerType typeToSave; // GetStartOptionsCntrlConfigScreens(); e_ControllerAction action = (e_ControllerAction) m_CurrCntrlAction; if (typeOfControl == KEYBOARD) { if (*pControlEdit == rsESC) { escPressed = true; } else if (*pControlEdit != rsF1 && *pControlEdit != rsF2 && *pControlEdit != rsF3 && *pControlEdit != rsF9 && *pControlEdit != rsLWIN && *pControlEdit != rsRWIN && *pControlEdit != rsRALT) { typeToSave = KEYBOARD; if (ControlsManager.GetControllerKeyAssociatedWithAction(action, KEYBOARD) != rsNULL && *pControlEdit != ControlsManager.GetControllerKeyAssociatedWithAction(action, KEYBOARD)) { typeToSave = OPTIONAL_EXTRA; } } else { invalidKey = true; } } else if (typeOfControl == MOUSE) { typeToSave = MOUSE; } else if (typeOfControl == JOYSTICK) { typeToSave = JOYSTICK; if (ControlsManager.GetIsActionAButtonCombo(action)) DisplayComboButtonErrMsg = true; } #ifdef FIX_BUGS if(!escPressed && !invalidKey) #endif ControlsManager.ClearSettingsAssociatedWithAction(action, typeToSave); if (!DisplayComboButtonErrMsg && !escPressed && !invalidKey) { if (typeOfControl == KEYBOARD) { ControlsManager.DeleteMatchingActionInitiators(action, *pControlEdit, KEYBOARD); ControlsManager.DeleteMatchingActionInitiators(action, *pControlEdit, OPTIONAL_EXTRA); } else if (typeOfControl == MOUSE) { ControlsManager.DeleteMatchingActionInitiators(action, MouseButtonJustClicked, MOUSE); } else if (typeOfControl == JOYSTICK) { ControlsManager.DeleteMatchingActionInitiators(action, JoyButtonJustClicked, JOYSTICK); } if (typeOfControl == KEYBOARD) { ControlsManager.SetControllerKeyAssociatedWithAction(action, *pControlEdit, typeToSave); } else if (typeOfControl == MOUSE) { ControlsManager.SetControllerKeyAssociatedWithAction(action, MouseButtonJustClicked, typeToSave); } else if (typeOfControl == JOYSTICK) { ControlsManager.SetControllerKeyAssociatedWithAction(action, JoyButtonJustClicked, typeToSave); } pControlEdit = nil; m_bWaitingForNewKeyBind = false; m_KeyPressedCode = -1; m_bStartWaitingForKeyBind = false; #ifdef LOAD_INI_SETTINGS SaveINIControllerSettings(); #else SaveSettings(); #endif } } bool CMenuManager::CheckHover(int x1, int x2, int y1, int y2) { return m_nMousePosX > x1 && m_nMousePosX < x2 && m_nMousePosY > y1 && m_nMousePosY < y2; } void CMenuManager::CheckSliderMovement(int value) { switch (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action) { case MENUACTION_BRIGHTNESS: m_PrefsBrightness += value * 24.19f; m_PrefsBrightness = clamp(m_PrefsBrightness, 0, 384); break; case MENUACTION_DRAWDIST: if(value > 0) m_PrefsLOD += ((1.8f - 0.925f) / 16.0f); else m_PrefsLOD -= ((1.8f - 0.925f) / 16.0f); m_PrefsLOD = clamp(m_PrefsLOD, 0.925f, 1.8f); CRenderer::ms_lodDistScale = m_PrefsLOD; break; case MENUACTION_MUSICVOLUME: if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { m_PrefsMusicVolume += value * (128 / 32); m_PrefsMusicVolume = clamp(m_PrefsMusicVolume, 0, 65); DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); } break; case MENUACTION_SFXVOLUME: if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { m_PrefsSfxVolume += value * (128 / 32); m_PrefsSfxVolume = clamp(m_PrefsSfxVolume, 0, 65); DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); } break; case MENUACTION_MP3VOLUMEBOOST: if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { if (DMAudio.IsMP3RadioChannelAvailable()) { m_PrefsMP3BoostVolume += value * (128 / 32); m_PrefsMP3BoostVolume = clamp(m_PrefsMP3BoostVolume, 0, 65); DMAudio.SetMP3BoostVolume(m_PrefsMP3BoostVolume); } } break; case MENUACTION_MOUSESENS: TheCamera.m_fMouseAccelHorzntl += value * 1.0f/200.0f/15.0f; // ??? TheCamera.m_fMouseAccelHorzntl = clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f/3200.0f, 1.0f/200.0f); #ifdef FIX_BUGS TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f; #endif break; default: return; } SaveSettings(); } void CMenuManager::DisplayHelperText(char *text) { if (m_nMenuFadeAlpha != 255) return; // there was a unused static bool static PauseModeTime LastFlash = 0; int32 alpha = 255; CFont::SetRightJustifyOn(); CFont::SetScale(SCREEN_SCALE_X(SMALLESTTEXT_X_SCALE), SCREEN_SCALE_Y(SMALLESTTEXT_Y_SCALE)); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetDropShadowPosition(0); // We're using SCREEN_STRETCH_FROM_RIGHT, because we also stretch black borders if (text) { CFont::SetColor(CRGBA(255, 255, 255, 255)); CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get(text)); return; } if (m_nHelperTextMsgId != 0 && m_nHelperTextMsgId != 1) { if (CTimer::GetTimeInMillisecondsPauseMode() - LastFlash > 10) { LastFlash = CTimer::GetTimeInMillisecondsPauseMode(); m_nHelperTextAlpha -= 2; } if (m_nHelperTextAlpha < 1) ResetHelperText(); alpha = m_nHelperTextAlpha > 255 ? 255 : m_nHelperTextAlpha; } CFont::SetColor(CRGBA(255, 255, 255, alpha)); // TODO: name this cases? switch (m_nHelperTextMsgId) { case 1: CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_APP")); break; case 2: CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_HRD")); break; case 3: CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_RSO")); break; case 4: CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_STS")); break; case 5: CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), TheText.Get("FET_RSC")); break; default: if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_NO) return; if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_MUSICVOLUME || aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_SFXVOLUME) { CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER ? TheText.Get("FEH_NA") : TheText.Get("FET_MIG")); return; } if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_KEYBOARDCTRLS) return; if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_SCREENRES) { CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), m_bGameNotLoaded ? TheText.Get("FET_MIG") : TheText.Get("FEH_NA")); return; } if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_AUDIOHW || aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_SPEAKERCONF) { CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER ? TheText.Get("FEH_NA") : TheText.Get("FET_MIG")); return; } if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_RESTOREDEF) return; if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_MP3VOLUMEBOOST) { CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER ? TheText.Get("FEH_NA") : TheText.Get("FET_MIG")); return; } CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(HELPER_TEXT_RIGHT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(HELPER_TEXT_BOTTOM_MARGIN), m_nCurrScreen != MENUPAGE_STATS ? TheText.Get("FET_MIG") : TheText.Get("FEH_SSA")); break; } } int CMenuManager::DisplaySlider(float x, float y, float mostLeftBarSize, float mostRightBarSize, float rectSize, float progress, float spacing) { CRGBA color; float maxBarHeight; int lastActiveBarX = 0; float curBarX = 0.0f; for (int i = 0; i < 16; i++) { curBarX = i * rectSize/16.0f + x; if (i / 16.0f + 1 / 32.0f < progress) { color = CRGBA(SLIDERON_COLOR.r, SLIDERON_COLOR.g, SLIDERON_COLOR.b, FadeIn(255)); lastActiveBarX = curBarX; } else color = CRGBA(SLIDEROFF_COLOR.r, SLIDEROFF_COLOR.g, SLIDEROFF_COLOR.b, FadeIn(255)); maxBarHeight = Max(mostLeftBarSize, mostRightBarSize); float curBarFreeSpace = ((16 - i) * mostLeftBarSize + i * mostRightBarSize) / 16.0f; float left = curBarX; float top = y + maxBarHeight - curBarFreeSpace; float right = spacing + curBarX; float bottom = y + maxBarHeight; float shadowOffset = SCREEN_SCALE_X(2.0f); CSprite2d::DrawRect(CRect(left + shadowOffset, top + shadowOffset, right + shadowOffset, bottom + shadowOffset), CRGBA(0, 0, 0, FadeIn(200))); // Shadow CSprite2d::DrawRect(CRect(left, top, right, bottom), color); } return lastActiveBarX; } void CMenuManager::DoSettingsBeforeStartingAGame() { #ifdef LEGACY_MENU_OPTIONS if (m_PrefsVsyncDisp != m_PrefsVsync) m_PrefsVsync = m_PrefsVsyncDisp; #endif DMAudio.DestroyAllGameCreatedEntities(); DMAudio.Service(); m_bShutDownFrontEndRequested = true; m_bWantToRestart = true; DMAudio.SetEffectsFadeVol(0); DMAudio.SetMusicFadeVol(0); for (int i = 0; i < NUM_RADIOS; i++) CStats::FavoriteRadioStationList[i] = 0.0f; SwitchMenuOnAndOff(); DMAudio.ResetTimers(CTimer::GetTimeInMilliseconds()); } void CMenuManager::DrawStandardMenus(bool activeScreen) { float nextYToUse = 0.0f; // III leftover, set but unused in VC bool itemsAreSelectable = true; CFont::SetBackgroundOff(); CFont::SetPropOn(); CFont::SetCentreOff(); CFont::SetJustifyOn(); CFont::SetBackGroundOnlyTextOff(); #ifdef CUSTOM_FRONTEND_OPTIONS const int xMargin = aScreens[m_nCurrScreen].layout && aScreens[m_nCurrScreen].layout->xMargin != 0 ? aScreens[m_nCurrScreen].layout->xMargin : MENU_X_MARGIN; #else const int xMargin = MENU_X_MARGIN; #endif CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(xMargin)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(xMargin)); #ifdef ASPECT_RATIO_SCALE CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH)); #else CFont::SetCentreSize(SCREEN_WIDTH); #endif switch (m_nCurrScreen) { case MENUPAGE_CHOOSE_LOAD_SLOT: case MENUPAGE_CHOOSE_DELETE_SLOT: case MENUPAGE_CHOOSE_SAVE_SLOT: CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(38.0f), MENU_Y(85.0f), MENU_X_LEFT_ALIGNED(615.0f), MENU_Y(75.0f), MENU_X_LEFT_ALIGNED(30.0f), MENU_Y(320.0f), MENU_X_LEFT_ALIGNED(605.0f), MENU_Y(330.0f), CRGBA(LIST_BACKGROUND_COLOR.r, LIST_BACKGROUND_COLOR.g, LIST_BACKGROUND_COLOR.b, FadeIn(LIST_BACKGROUND_COLOR.a))); break; case MENUPAGE_SOUND_SETTINGS: PrintRadioSelector(); break; case MENUPAGE_STATS: PrintStats(); break; case MENUPAGE_BRIEFS: PrintBriefs(); break; } // Page name if (aScreens[m_nCurrScreen].m_ScreenName[0] != '\0') { SET_FONT_FOR_MENU_HEADER CFont::SetColor(CRGBA(30, 30, 30, FadeIn(255))); CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X) - MENU_X(7.f), SCREEN_SCALE_Y(MENUHEADER_POS_Y + 7.f), TheText.Get(aScreens[m_nCurrScreen].m_ScreenName)); CFont::SetColor(CRGBA(HEADER_COLOR.r, HEADER_COLOR.g, HEADER_COLOR.b, FadeIn(255))); CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X), SCREEN_SCALE_Y(MENUHEADER_POS_Y), TheText.Get(aScreens[m_nCurrScreen].m_ScreenName)); } // Label wchar *str; if (aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL) { CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(MENULABEL_X_MARGIN)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(MENULABEL_X_MARGIN)); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetScale(MENU_X(BIGTEXT2_X_SCALE), MENU_Y(BIGTEXT2_Y_SCALE)); CFont::SetRightJustifyOff(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, FadeIn(255))); switch (m_nCurrScreen) { case MENUPAGE_LOAD_SLOT_CONFIRM: if (m_bGameNotLoaded) str = TheText.Get("FES_LCG"); else str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); break; case MENUPAGE_DELETE_SLOT_CONFIRM: str = TheText.Get(aScreens[MENUPAGE_DELETE_SLOT_CONFIRM].m_aEntries[0].m_EntryName); break; case MENUPAGE_SAVE_OVERWRITE_CONFIRM: if (Slots[m_nCurrSaveSlot] == SLOT_OK) str = TheText.Get("FESZ_QO"); else if (Slots[m_nCurrSaveSlot] == SLOT_CORRUPTED) str = TheText.Get("FESZ_QZ"); else str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); break; case MENUPAGE_EXIT: if (m_bGameNotLoaded) str = TheText.Get("FEQ_SRW"); else str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); break; default: str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(100.0f), MENU_Y(97.0f), str); CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(xMargin)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(xMargin)); } if (m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) { if (m_bWaitingForNewKeyBind) itemsAreSelectable = false; DrawControllerScreenExtraText(-8.0f, MENU_X_LEFT_ALIGNED(350), MENU_DEFAULT_LINE_HEIGHT); } wchar unicodeTemp[64]; #ifdef ASPECT_RATIO_SCALE char asciiTemp[32]; #endif bool weHaveLabel = aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL; uint8 section = 0; // 0: highlight trapezoid 1: texts while (section < 2) { #endif #ifdef SCROLLABLE_PAGES int firstOption = SCREEN_HAS_AUTO_SCROLLBAR ? m_nFirstVisibleRowOnList : 0; int scrollOffset = aScreens[m_nCurrScreen].m_aEntries[firstOption].m_Y - aScreens[m_nCurrScreen].m_aEntries[0].m_Y; for (int i = firstOption; i < firstOption + MAX_VISIBLE_OPTION && i < NUM_MENUROWS; ++i) { #else for (int i = 0; i < NUM_MENUROWS; ++i) { #endif wchar* rightText = nil; wchar* leftText; if (aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot >= SAVESLOT_1 && aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot <= SAVESLOT_8) { CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetScale(MENU_X(MEDIUMTEXT_X_SCALE), MENU_Y(MEDIUMTEXT_Y_SCALE)); CFont::SetDropShadowPosition(0); } else { CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); } if (aScreens[m_nCurrScreen].m_aEntries[i].m_Align == MENUALIGN_LEFT) { CFont::SetCentreOff(); CFont::SetRightJustifyOff(); } else if (aScreens[m_nCurrScreen].m_aEntries[i].m_Align == MENUALIGN_RIGHT) { CFont::SetCentreOff(); CFont::SetRightJustifyOn(); } else { CFont::SetRightJustifyOff(); CFont::SetCentreOn(); } if (aScreens[m_nCurrScreen].m_aEntries[i].m_X == 0 && aScreens[m_nCurrScreen].m_aEntries[i].m_Y == 0) { if (i == 0 || (i == 1 && weHaveLabel)) { #ifdef CUSTOM_FRONTEND_OPTIONS aScreens[m_nCurrScreen].m_aEntries[i].m_X = (aScreens[m_nCurrScreen].layout ? aScreens[m_nCurrScreen].layout->startX : MENU_DEFAULT_CONTENT_X); aScreens[m_nCurrScreen].m_aEntries[i].m_Y = (aScreens[m_nCurrScreen].layout ? aScreens[m_nCurrScreen].layout->startY : MENU_DEFAULT_CONTENT_Y); #else aScreens[m_nCurrScreen].m_aEntries[i].m_X = MENU_DEFAULT_CONTENT_X; aScreens[m_nCurrScreen].m_aEntries[i].m_Y = MENU_DEFAULT_CONTENT_Y; #endif } else { aScreens[m_nCurrScreen].m_aEntries[i].m_X = aScreens[m_nCurrScreen].m_aEntries[i-1].m_X; aScreens[m_nCurrScreen].m_aEntries[i].m_Y = aScreens[m_nCurrScreen].m_aEntries[i-1].m_Y PLUS_LINE_HEIGHT_ON_SCREEN; } } #ifdef CUSTOM_FRONTEND_OPTIONS else if (aScreens[m_nCurrScreen].m_aEntries[i].m_Y == 0) { aScreens[m_nCurrScreen].m_aEntries[i].m_Y = aScreens[m_nCurrScreen].m_aEntries[i-1].m_Y PLUS_LINE_HEIGHT_ON_SCREEN; } #endif if (aScreens[m_nCurrScreen].m_aEntries[i].m_Action != MENUACTION_LABEL && aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName[0] != '\0') { if (aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot >= SAVESLOT_1 && aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot <= SAVESLOT_8) { CFont::SetRightJustifyOff(); leftText = nil; if (Slots[i] == SLOT_OK) { leftText = GetNameOfSavedGame(i); rightText = GetSavedGameDateAndTime(i); } if (!leftText || leftText[0] == '\0') { sprintf(gString, "FEM_SL%d", i + 1); leftText = TheText.Get(gString); } } else { leftText = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName); } if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) { if (strcmp(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName, "FEO_AUD") == 0) { CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); } } switch (aScreens[m_nCurrScreen].m_aEntries[i].m_Action) { #ifdef GAMEPAD_MENU case MENUACTION_CTRLVIBRATION: if (m_PrefsUseVibration) rightText = TheText.Get("FEM_ON"); else rightText = TheText.Get("FEM_OFF"); break; case MENUACTION_CTRLCONFIG: switch (CPad::GetPad(0)->Mode) { case 0: rightText = TheText.Get("FEC_CF1"); break; case 1: rightText = TheText.Get("FEC_CF2"); break; case 2: rightText = TheText.Get("FEC_CF3"); break; case 3: rightText = TheText.Get("FEC_CF4"); break; } break; // This one is still in enum and ProcessOnOffMenuOptions, but removed from other places case MENUACTION_CTRLDISPLAY: if (m_DisplayControllerOnFoot) rightText = TheText.Get("FEC_ONF"); else rightText = TheText.Get("FEC_INC"); break; #endif case MENUACTION_FRAMESYNC: rightText = TheText.Get(m_PrefsVsyncDisp ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_FRAMELIMIT: rightText = TheText.Get(m_PrefsFrameLimiter ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_TRAILS: rightText = TheText.Get(CMBlur::BlurOn ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_SUBTITLES: rightText = TheText.Get(m_PrefsShowSubtitles ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_WIDESCREEN: #ifndef ASPECT_RATIO_SCALE rightText = TheText.Get(m_PrefsUseWideScreen ? "FEM_ON" : "FEM_OFF"); #else switch (m_PrefsUseWideScreen) { case AR_AUTO: rightText = TheText.Get("FEM_AUT"); break; case AR_4_3: sprintf(asciiTemp, "4:3"); AsciiToUnicode(asciiTemp, unicodeTemp); rightText = unicodeTemp; break; case AR_5_4: sprintf(asciiTemp, "5:4"); AsciiToUnicode(asciiTemp, unicodeTemp); rightText = unicodeTemp; break; case AR_16_10: sprintf(asciiTemp, "16:10"); AsciiToUnicode(asciiTemp, unicodeTemp); rightText = unicodeTemp; break; case AR_16_9: sprintf(asciiTemp, "16:9"); AsciiToUnicode(asciiTemp, unicodeTemp); rightText = unicodeTemp; break; case AR_21_9: sprintf(asciiTemp, "21:9"); AsciiToUnicode(asciiTemp, unicodeTemp); rightText = unicodeTemp; break; } #endif break; case MENUACTION_MUSICVOLUME: case MENUACTION_SFXVOLUME: if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) rightText = TheText.Get("FEA_NAH"); break; case MENUACTION_RADIO: switch (m_PrefsRadioStation) { case WILDSTYLE: rightText = TheText.Get("FEA_FM0"); break; case FLASH_FM: rightText = TheText.Get("FEA_FM1"); break; case KCHAT: rightText = TheText.Get("FEA_FM2"); break; case FEVER: rightText = TheText.Get("FEA_FM3"); break; case V_ROCK: rightText = TheText.Get("FEA_FM4"); break; case VCPR: rightText = TheText.Get("FEA_FM5"); break; case RADIO_ESPANTOSO: rightText = TheText.Get("FEA_FM6"); break; case EMOTION: rightText = TheText.Get("FEA_FM7"); break; case WAVE: rightText = TheText.Get("FEA_FM8"); break; case USERTRACK: rightText = TheText.Get("FEA_MP3"); break; } break; case MENUACTION_LEGENDS: rightText = TheText.Get(m_PrefsShowLegends ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_RADARMODE: switch (m_PrefsRadarMode) { case 0: rightText = TheText.Get("FED_RDM"); break; case 1: rightText = TheText.Get("FED_RDB"); break; case 2: rightText = TheText.Get("FEM_OFF"); break; } break; case MENUACTION_HUD: rightText = TheText.Get(m_PrefsShowHud ? "FEM_ON" : "FEM_OFF"); break; #ifdef LEGACY_MENU_OPTIONS case MENUACTION_SETDBGFLAG: rightText = TheText.Get(CTheScripts::IsDebugOn() ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_SWITCHBIGWHITEDEBUGLIGHT: rightText = TheText.Get(gbBigWhiteDebugLightSwitchedOn ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_COLLISIONPOLYS: rightText = TheText.Get(gbShowCollisionPolys ? "FEM_ON" : "FEM_OFF"); break; #endif case MENUACTION_SHOWHEADBOB: rightText = TheText.Get(TheCamera.m_bHeadBob ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_INVVERT: rightText = TheText.Get(MousePointerStateHelper.bInvertVertically ? "FEM_OFF" : "FEM_ON"); break; case MENUACTION_SCREENRES: AsciiToUnicode(_psGetVideoModeList()[m_nDisplayVideoMode], unicodeTemp); rightText = unicodeTemp; if (!m_bGameNotLoaded) { CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); } break; case MENUACTION_AUDIOHW: if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) rightText = TheText.Get("FEA_NAH"); else if (m_nPrefsAudio3DProviderIndex == -1) rightText = TheText.Get("FEA_ADP"); else { char *rawProvider = DMAudio.Get3DProviderName(m_nPrefsAudio3DProviderIndex); AsciiToUnicode(rawProvider, unicodeTemp); char *provider = UnicodeToAscii(unicodeTemp); // genius strupr(provider); if (!strcmp(provider, "DIRECTSOUND3D HARDWARE SUPPORT")) { strcpy(provider, "DSOUND3D HARDWARE SUPPORT"); } else if (!strcmp(provider, "DIRECTSOUND3D SOFTWARE EMULATION")) { strcpy(provider, "DSOUND3D SOFTWARE EMULATION"); } AsciiToUnicode(provider, unicodeTemp); rightText = unicodeTemp; } break; case MENUACTION_SPEAKERCONF: { if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) rightText = TheText.Get("FEA_NAH"); else { switch (m_PrefsSpeakers) { case 0: rightText = TheText.Get("FEA_2SP"); break; case 1: rightText = TheText.Get("FEA_EAR"); break; case 2: rightText = TheText.Get("FEA_4SP"); break; } } break; } case MENUACTION_CTRLMETHOD: { switch (m_ControlMethod) { case CONTROL_STANDARD: leftText = TheText.Get("FET_STI"); break; case CONTROL_CLASSIC: leftText = TheText.Get("FET_CTI"); break; } break; } case MENUACTION_DYNAMICACOUSTIC: rightText = TheText.Get(m_PrefsDMA ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_MOUSESTEER: rightText = TheText.Get(CVehicle::m_bDisableMouseSteering ? "FEM_OFF" : "FEM_ON"); if (m_ControlMethod == CONTROL_CLASSIC) { CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); } break; case MENUACTION_MP3VOLUMEBOOST: if (!DMAudio.IsMP3RadioChannelAvailable()) { rightText = TheText.Get("FEA_NM3"); } break; #ifdef CUSTOM_FRONTEND_OPTIONS case MENUACTION_CFO_DYNAMIC: case MENUACTION_CFO_SELECT: CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[i]; if (option.m_Action == MENUACTION_CFO_SELECT) { if (option.m_CFOSelect->disableIfGameLoaded && !m_bGameNotLoaded) CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); // To whom manipulate option.m_CFO->value of static options externally (like RestoreDef functions) if (*option.m_CFO->value != option.m_CFOSelect->lastSavedValue) option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *option.m_CFO->value; if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0) option.m_CFOSelect->displayedValue = 0; rightText = TheText.Get(option.m_CFOSelect->rightTexts[option.m_CFOSelect->displayedValue]); } else if (option.m_Action == MENUACTION_CFO_DYNAMIC) { if (option.m_CFODynamic->drawFunc) { bool isOptionDisabled = false; rightText = option.m_CFODynamic->drawFunc(&isOptionDisabled, m_nCurrOption == i); if (isOptionDisabled) CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); } } break; #endif } // Highlight trapezoid if (activeScreen && i == m_nCurrOption && itemsAreSelectable && section == 0) { int leftXMax, rightXMin; // FIX: Let's don't scale those so GetStringWidth can give us unscaled width, which will be handy to other calculations below that's done without scaling in mind, // and scaling will be done eventually. // CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); CFont::SetScale(BIGTEXT_X_SCALE, BIGTEXT_Y_SCALE); wchar *curOptionName = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName); float curOptionWidth = CFont::GetStringWidth(curOptionName, true); if (CFont::Details.centre) { leftXMax = Max(0, aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X - curOptionWidth / 2.f); rightXMin = Min(DEFAULT_SCREEN_WIDTH, curOptionWidth / 2.f + aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X); } else if (!CFont::Details.rightJustify) { leftXMax = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X; rightXMin = Min(DEFAULT_SCREEN_WIDTH, curOptionWidth + aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X); } else { leftXMax = Max(0, aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X - curOptionWidth); rightXMin = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_X; } CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); int action = aScreens[m_nCurrScreen].m_aEntries[i].m_Action; int saveSlot = aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot; if (rightText || action == MENUACTION_DRAWDIST || action == MENUACTION_BRIGHTNESS || action == MENUACTION_MUSICVOLUME || action == MENUACTION_SFXVOLUME || action == MENUACTION_MP3VOLUMEBOOST || action == MENUACTION_MOUSESENS || saveSlot >= SAVESLOT_1 && saveSlot <= SAVESLOT_8) { rightXMin = 600; leftXMax = 40; } int y = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Y MINUS_SCROLL_OFFSET; int topYMax = y; uint32 bottomYMin = y + MENU_DEFAULT_LINE_HEIGHT - 7; // Decreasing is not recommended. Because this actually is dependent to font scale, not line height. // Actually bottomRight and bottomLeft should be exchanged here(although this is original code). // So this shows us either R* didn't use same struct for menu BG and highlight, or they just kept fields as x1,y1 etc. Yikes. if (m_nOptionHighlightTransitionBlend == 0) { if (m_firstStartCounter == 255 && m_nMenuFadeAlpha == 255 && !bMenuChangeOngoing) { CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(menuOptionHighlight.topLeft_x), MENU_Y(menuOptionHighlight.topLeft_y), MENU_X_LEFT_ALIGNED(menuOptionHighlight.topRight_x), MENU_Y(menuOptionHighlight.topRight_y), MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomRight_x), MENU_Y(menuOptionHighlight.bottomRight_y), MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomLeft_x), MENU_Y(menuOptionHighlight.bottomLeft_y), SELECTIONBORDER_COLOR); } menuOptionHighlight.SaveCurrentCoors(); menuOptionHighlight.topLeft_x = leftXMax - 5 - CGeneral::GetRandomNumber() % 10; menuOptionHighlight.topLeft_y = topYMax - CGeneral::GetRandomNumber() % 7; menuOptionHighlight.topRight_x = rightXMin + 5 + CGeneral::GetRandomNumber() % 10; menuOptionHighlight.topRight_y = topYMax - CGeneral::GetRandomNumber() % 7; menuOptionHighlight.bottomLeft_x = rightXMin + 5 + CGeneral::GetRandomNumber() % 10; menuOptionHighlight.bottomLeft_y = bottomYMin + CGeneral::GetRandomNumber() % 7; menuOptionHighlight.bottomRight_x = leftXMax - 5 - CGeneral::GetRandomNumber() % 10; menuOptionHighlight.bottomRight_y = bottomYMin + CGeneral::GetRandomNumber() % 7; menuOptionHighlight.UpdateMultipliers(); menuOptionHighlight.Translate(m_nOptionHighlightTransitionBlend); } else if (m_nOptionHighlightTransitionBlend < 255) { menuOptionHighlight.Translate(m_nOptionHighlightTransitionBlend); if (m_firstStartCounter == 255 && m_nMenuFadeAlpha == 255 && !bMenuChangeOngoing) { CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(menuOptionHighlight.topLeft_x), MENU_Y(menuOptionHighlight.topLeft_y), MENU_X_LEFT_ALIGNED(menuOptionHighlight.topRight_x), MENU_Y(menuOptionHighlight.topRight_y), MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomRight_x), MENU_Y(menuOptionHighlight.bottomRight_y), MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomLeft_x), MENU_Y(menuOptionHighlight.bottomLeft_y), SELECTIONBORDER_COLOR); } } else { m_nOptionHighlightTransitionBlend = 255; menuOptionHighlight.Translate(m_nOptionHighlightTransitionBlend); if (m_firstStartCounter == 255 && m_nMenuFadeAlpha == 255 && !bMenuChangeOngoing) { CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(menuOptionHighlight.topLeft_x), MENU_Y(menuOptionHighlight.topLeft_y), MENU_X_LEFT_ALIGNED(menuOptionHighlight.topRight_x), MENU_Y(menuOptionHighlight.topRight_y), MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomRight_x), MENU_Y(menuOptionHighlight.bottomRight_y), MENU_X_LEFT_ALIGNED(menuOptionHighlight.bottomLeft_x), MENU_Y(menuOptionHighlight.bottomLeft_y), SELECTIONBORDER_COLOR); } } static PauseModeTime lastBlendChange = 0; if (m_nOptionHighlightTransitionBlend <= 255) { static uint32 blendChangeCounter = 0; if (CTimer::GetTimeInMillisecondsPauseMode() - lastBlendChange > 20 #ifndef FIX_HIGH_FPS_BUGS_ON_FRONTEND // Dirty dirty hack || blendChangeCounter > 20 #endif ) { m_nOptionHighlightTransitionBlend += 50; lastBlendChange = CTimer::GetTimeInMillisecondsPauseMode(); blendChangeCounter = 0; } ++blendChangeCounter; } } if (section == 1) { if (leftText) { CFont::PrintString(MENU_X_LEFT_ALIGNED(aScreens[m_nCurrScreen].m_aEntries[i].m_X), MENU_Y(aScreens[m_nCurrScreen].m_aEntries[i].m_Y MINUS_SCROLL_OFFSET), leftText); } if (rightText) { CFont::SetCentreOff(); CFont::SetRightJustifyOn(); if (aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot >= SAVESLOT_1 && aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot <= SAVESLOT_8) { CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetScale(MENU_X(MEDIUMTEXT_X_SCALE), MENU_Y(MEDIUMTEXT_Y_SCALE)); } else { CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); } CFont::PrintString(MENU_X_LEFT_ALIGNED(DEFAULT_SCREEN_WIDTH - RIGHT_ALIGNED_TEXT_RIGHT_MARGIN(xMargin)), MENU_Y(aScreens[m_nCurrScreen].m_aEntries[i].m_Y MINUS_SCROLL_OFFSET), rightText); } if (m_nPrefsAudio3DProviderIndex == DMAudio.GetCurrent3DProviderIndex()) { if (!strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FEA_3DH") && m_nHelperTextMsgId == 1) ResetHelperText(); } if (m_nDisplayVideoMode == m_nPrefsVideoMode) { if (!strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FED_RES") && m_nHelperTextMsgId == 1) ResetHelperText(); } if (m_nPrefsAudio3DProviderIndex != DMAudio.GetCurrent3DProviderIndex()) { if (!strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FEA_3DH")) SetHelperText(1); } if (m_nDisplayVideoMode != m_nPrefsVideoMode) { if (!strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FED_RES")) SetHelperText(1); } if (m_nPrefsAudio3DProviderIndex != DMAudio.GetCurrent3DProviderIndex()) { if (strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FEA_3DH") != 0 #ifdef CUSTOM_FRONTEND_OPTIONS && ScreenHasOption(m_nCurrScreen, "FEA_3DH") #else && m_nCurrScreen == MENUPAGE_SOUND_SETTINGS #endif && m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { m_nPrefsAudio3DProviderIndex = DMAudio.GetCurrent3DProviderIndex(); SetHelperText(3); } } if (m_nDisplayVideoMode != m_nPrefsVideoMode) { if (strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FED_RES") != 0 #ifdef CUSTOM_FRONTEND_OPTIONS && ScreenHasOption(m_nCurrScreen, "FED_RES")) { #else && m_nCurrScreen == MENUPAGE_DISPLAY_SETTINGS) { #endif m_nDisplayVideoMode = m_nPrefsVideoMode; SetHelperText(3); } } // Sliders int lastActiveBarX; switch (aScreens[m_nCurrScreen].m_aEntries[i].m_Action) { case MENUACTION_BRIGHTNESS: ProcessSlider(m_PrefsBrightness / 384.0f, 70.0f, HOVEROPTION_INCREASE_BRIGHTNESS, HOVEROPTION_DECREASE_BRIGHTNESS, SCREEN_WIDTH, true); break; case MENUACTION_DRAWDIST: ProcessSlider((m_PrefsLOD - 0.925f) / 0.875f, 99.0f, HOVEROPTION_INCREASE_DRAWDIST, HOVEROPTION_DECREASE_DRAWDIST, SCREEN_WIDTH, true); break; case MENUACTION_MUSICVOLUME: if(m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) ProcessSlider(m_PrefsMusicVolume / 64.0f, 70.0f, HOVEROPTION_INCREASE_MUSICVOLUME, HOVEROPTION_DECREASE_MUSICVOLUME, SCREEN_WIDTH, true); break; case MENUACTION_SFXVOLUME: if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) ProcessSlider(m_PrefsSfxVolume / 64.0f, 99.0f, HOVEROPTION_INCREASE_SFXVOLUME, HOVEROPTION_DECREASE_SFXVOLUME, SCREEN_WIDTH, true); break; case MENUACTION_MOUSESENS: ProcessSlider(TheCamera.m_fMouseAccelHorzntl * 200.0f, 170.0f, HOVEROPTION_INCREASE_MOUSESENS, HOVEROPTION_DECREASE_MOUSESENS, SCREEN_WIDTH, false); break; case MENUACTION_MP3VOLUMEBOOST: if(m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER && DMAudio.IsMP3RadioChannelAvailable()) ProcessSlider(m_PrefsMP3BoostVolume / 64.f, 128.0f, HOVEROPTION_INCREASE_MP3BOOST, HOVEROPTION_DECREASE_MP3BOOST, SCREEN_WIDTH, true); break; } // Not just unused, but also collides with the bug fix in Font.cpp. Yikes. #ifndef FIX_BUGS nextYToUse += MENU_DEFAULT_LINE_HEIGHT * CFont::GetNumberLines(MENU_X_LEFT_ALIGNED(60.0f), MENU_Y(nextYToUse), leftText); #endif if (aScreens[m_nCurrScreen].m_aEntries[i].m_Action == MENUACTION_RADIO) { nextYToUse += MENURADIO_SELECTOR_HEIGHT + 5.f; // unused } } } } section++; } #ifdef SCROLLABLE_PAGES #define SCROLLBAR_BOTTOM_Y 105.0f // only for background, scrollbar's itself is calculated #define SCROLLBAR_RIGHT_X 26.0f #define SCROLLBAR_WIDTH 9.5f #define SCROLLBAR_TOP_Y 84 if (activeScreen && SCREEN_HAS_AUTO_SCROLLBAR) { // Scrollbar background CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 2), MENU_Y(SCROLLBAR_TOP_Y), MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 2 - SCROLLBAR_WIDTH), SCREEN_SCALE_FROM_BOTTOM(SCROLLBAR_BOTTOM_Y)), CRGBA(30, 30, 30, FadeIn(150))); float scrollbarHeight = SCROLLBAR_MAX_HEIGHT / (m_nTotalListRow / (float) MAX_VISIBLE_OPTION); float scrollbarBottom, scrollbarTop; scrollbarBottom = MENU_Y(SCROLLBAR_TOP_Y - 6 + m_nScrollbarTopMargin + scrollbarHeight); scrollbarTop = MENU_Y(SCROLLBAR_TOP_Y + 2 + m_nScrollbarTopMargin); // Scrollbar shadow CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 4), scrollbarTop, MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 1 - SCROLLBAR_WIDTH), scrollbarBottom + MENU_Y(1.0f)), CRGBA(50, 50, 50, FadeIn(255))); // Scrollbar CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - 4), scrollbarTop, MENU_X_RIGHT_ALIGNED(SCROLLBAR_RIGHT_X - SCROLLBAR_WIDTH), scrollbarBottom), CRGBA(SCROLLBAR_COLOR.r, SCROLLBAR_COLOR.g, SCROLLBAR_COLOR.b, FadeIn(255))); } #endif switch (m_nCurrScreen) { #ifdef GAMEPAD_MENU case MENUPAGE_CONTROLLER_SETTINGS: PrintController(); break; #endif case MENUPAGE_STATS: case MENUPAGE_CONTROLLER_PC: case MENUPAGE_SOUND_SETTINGS: case MENUPAGE_DISPLAY_SETTINGS: case MENUPAGE_MOUSE_CONTROLS: DisplayHelperText(nil); break; case MENUPAGE_OPTIONS: if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_LOADRADIO) DisplayHelperText("FEA_NAH"); break; #ifdef CUSTOM_FRONTEND_OPTIONS default: if (aScreens[m_nCurrScreen].layout) { if (aScreens[m_nCurrScreen].layout->showLeftRightHelper) { DisplayHelperText(nil); } } break; #endif } if (m_nCurrScreen == MENUPAGE_DELETING_IN_PROGRESS) { SmallMessageScreen("FEDL_WR"); } #ifndef XBOX_MESSAGE_SCREEN else if (m_nCurrScreen == MENUPAGE_SAVING_IN_PROGRESS) { SmallMessageScreen("FESZ_WR"); } #endif } int CMenuManager::GetNumOptionsCntrlConfigScreens(void) { int number = 0; switch (m_nCurrScreen) { #ifdef LEGACY_MENU_OPTIONS case MENUPAGE_CONTROLLER_PC_OLD3: number = 2; break; case MENUPAGE_CONTROLLER_DEBUG: number = 4; break; #endif case MENUPAGE_KEYBOARD_CONTROLS: switch (m_ControlMethod) { case CONTROL_STANDARD: number = 27; break; case CONTROL_CLASSIC: number = 32; break; } break; } return number; } void CMenuManager::DrawControllerBound(int32 yStart, int32 xStart, int32 unused, int8 column) { int controllerAction = PED_FIREWEAPON; // GetStartOptionsCntrlConfigScreens(); int numOptions = GetNumOptionsCntrlConfigScreens(); int nextY = MENU_Y(yStart); int bindingMargin = MENU_X(3.0f); float rowHeight; switch (m_ControlMethod) { case CONTROL_STANDARD: rowHeight = CONTSETUP_STANDARD_ROW_HEIGHT; break; case CONTROL_CLASSIC: rowHeight = CONTSETUP_CLASSIC_ROW_HEIGHT; break; default: break; } for (int optionIdx = 0; optionIdx < numOptions; nextY = MENU_Y(++optionIdx * rowHeight + yStart)) { int nextX = xStart; int bindingsForThisOpt = 0; int contSetOrder = SETORDER_1; CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); if (column == CONTSETUP_PED_COLUMN) { switch (optionIdx) { case 0: controllerAction = PED_FIREWEAPON; break; case 1: controllerAction = PED_CYCLE_WEAPON_RIGHT; break; case 2: controllerAction = PED_CYCLE_WEAPON_LEFT; break; case 3: controllerAction = GO_FORWARD; break; case 4: controllerAction = GO_BACK; break; case 5: controllerAction = GO_LEFT; break; case 6: controllerAction = GO_RIGHT; break; case 7: controllerAction = PED_SNIPER_ZOOM_IN; break; case 8: controllerAction = PED_SNIPER_ZOOM_OUT; break; case 9: controllerAction = VEHICLE_ENTER_EXIT; break; case 10: case 11: case 12: case 16: case 20: case 21: case 22: case 23: controllerAction = -1; break; case 13: controllerAction = CAMERA_CHANGE_VIEW_ALL_SITUATIONS; break; case 14: controllerAction = PED_JUMPING; break; case 15: controllerAction = PED_SPRINT; break; case 17: controllerAction = PED_LOCK_TARGET; break; case 18: controllerAction = PED_DUCK; break; case 19: controllerAction = PED_ANSWER_PHONE; break; case 24: controllerAction = PED_LOOKBEHIND; break; case 25: if (m_ControlMethod == CONTROL_STANDARD) controllerAction = -1; else controllerAction = PED_1RST_PERSON_LOOK_LEFT; break; case 26: if (m_ControlMethod == CONTROL_STANDARD) controllerAction = -1; else controllerAction = PED_1RST_PERSON_LOOK_RIGHT; break; case 27: controllerAction = PED_1RST_PERSON_LOOK_UP; break; case 28: controllerAction = PED_1RST_PERSON_LOOK_DOWN; break; case 29: controllerAction = PED_CYCLE_TARGET_LEFT; break; case 30: controllerAction = PED_CYCLE_TARGET_RIGHT; break; case 31: controllerAction = PED_CENTER_CAMERA_BEHIND_PLAYER; break; default: break; } } else if (column == CONTSETUP_VEHICLE_COLUMN) { switch (optionIdx) { case 0: #ifdef BIND_VEHICLE_FIREWEAPON controllerAction = VEHICLE_FIREWEAPON; #else controllerAction = PED_FIREWEAPON; #endif break; case 1: case 2: case 7: case 8: case 14: case 15: case 17: case 18: case 19: case 27: case 28: case 29: case 30: case 31: controllerAction = -1; break; case 3: controllerAction = VEHICLE_ACCELERATE; break; case 4: controllerAction = VEHICLE_BRAKE; break; case 5: controllerAction = GO_LEFT; break; case 6: controllerAction = GO_RIGHT; break; case 9: controllerAction = VEHICLE_ENTER_EXIT; break; case 10: controllerAction = VEHICLE_CHANGE_RADIO_STATION; break; case 11: controllerAction = VEHICLE_HORN; break; case 12: controllerAction = TOGGLE_SUBMISSIONS; break; case 13: controllerAction = CAMERA_CHANGE_VIEW_ALL_SITUATIONS; break; case 16: controllerAction = VEHICLE_HANDBRAKE; break; case 20: controllerAction = VEHICLE_TURRETLEFT; break; case 21: controllerAction = VEHICLE_TURRETRIGHT; break; case 22: controllerAction = VEHICLE_TURRETUP; break; case 23: controllerAction = VEHICLE_TURRETDOWN; break; case 24: controllerAction = -2; break; case 25: controllerAction = VEHICLE_LOOKLEFT; break; case 26: controllerAction = VEHICLE_LOOKRIGHT; break; default: break; } } // Highlight selected column(and make its text black) if (m_nSelectedListRow == optionIdx) { int bgY = m_nSelectedListRow * rowHeight + yStart + 1.0f; if (m_nCurrExLayer == HOVEROPTION_LIST) { if (column == CONTSETUP_PED_COLUMN && m_nSelectedContSetupColumn == CONTSETUP_PED_COLUMN) { #ifdef FIX_BUGS CSprite2d::DrawRect(CRect(nextX, MENU_Y(bgY), nextX + MENU_X(CONTSETUP_BOUND_COLUMN_WIDTH), MENU_Y(bgY + CONTSETUP_BOUND_HIGHLIGHT_HEIGHT)), CRGBA(SELECTIONBORDER_COLOR.r, SELECTIONBORDER_COLOR.g, SELECTIONBORDER_COLOR.b, FadeIn(255))); #else CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(210.0f), MENU_Y(bgY), MENU_X_LEFT_ALIGNED(400.0f), MENU_Y(bgY + CONTSETUP_BOUND_HIGHLIGHT_HEIGHT)), CRGBA(SELECTIONBORDER_COLOR.r, SELECTIONBORDER_COLOR.g, SELECTIONBORDER_COLOR.b, FadeIn(255))); #endif CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); } else if (column == CONTSETUP_VEHICLE_COLUMN && m_nSelectedContSetupColumn == CONTSETUP_VEHICLE_COLUMN) { #ifdef FIX_BUGS CSprite2d::DrawRect(CRect(nextX, MENU_Y(bgY), nextX + MENU_X(CONTSETUP_BOUND_COLUMN_WIDTH), MENU_Y(bgY + CONTSETUP_BOUND_HIGHLIGHT_HEIGHT)), CRGBA(SELECTIONBORDER_COLOR.r, SELECTIONBORDER_COLOR.g, SELECTIONBORDER_COLOR.b, FadeIn(255))); #else CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(410.0f), MENU_Y(bgY), MENU_X_LEFT_ALIGNED(600.0f), MENU_Y(bgY + CONTSETUP_BOUND_HIGHLIGHT_HEIGHT)), CRGBA(SELECTIONBORDER_COLOR.r, SELECTIONBORDER_COLOR.g, SELECTIONBORDER_COLOR.b, FadeIn(255))); #endif CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); } } } // Print bindings, including seperator (-) between them CFont::SetScale(MENU_X(0.25f), MENU_Y(LISTITEM_Y_SCALE)); #ifdef FIX_BUGS for (; contSetOrder < MAX_SETORDERS && controllerAction >= 0; contSetOrder++) { #else for (; contSetOrder < MAX_SETORDERS && controllerAction != -1; contSetOrder++) { #endif wchar *settingText = ControlsManager.GetControllerSettingTextWithOrderNumber((e_ControllerAction)controllerAction, (eContSetOrder)contSetOrder); if (settingText) { ++bindingsForThisOpt; if (bindingsForThisOpt > 1) { wchar *seperator = TheText.Get("FEC_IBT"); CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); CFont::PrintString(nextX, nextY, seperator); nextX += CFont::GetStringWidth(seperator, true) + bindingMargin; } CFont::PrintString(nextX, nextY, settingText); #ifdef MORE_LANGUAGES if (CFont::IsJapanese()) nextX += CFont::GetStringWidth_Jap(settingText) + bindingMargin; else #endif nextX += CFont::GetStringWidth(settingText, true) + bindingMargin; } } if (controllerAction == -1) { CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::PrintString(nextX, nextY, TheText.Get("FEC_NUS")); // not used } else if (controllerAction == -2) { CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::PrintString(nextX, nextY, TheText.Get("FEC_CMP")); // combo: l+r } else if (bindingsForThisOpt == 0) { m_NoEmptyBinding = false; if (m_nSelectedListRow != optionIdx) { CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::PrintString(nextX, nextY, TheText.Get("FEC_UNB")); // unbound } else if (m_bWaitingForNewKeyBind) { if (column != m_nSelectedContSetupColumn) { CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::PrintString(nextX, nextY, TheText.Get("FEC_UNB")); // unbound } } else { if (column != m_nSelectedContSetupColumn) { CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); } CFont::PrintString(nextX, nextY, TheText.Get("FEC_UNB")); // unbound } } if (column == CONTSETUP_PED_COLUMN && m_nSelectedContSetupColumn == CONTSETUP_PED_COLUMN || column == CONTSETUP_VEHICLE_COLUMN && m_nSelectedContSetupColumn == CONTSETUP_VEHICLE_COLUMN) { if (optionIdx == m_nSelectedListRow && controllerAction != -1 && controllerAction != -2) { m_CurrCntrlAction = controllerAction; if (m_bWaitingForNewKeyBind) { static bool showWaitingText = false; if (bindingsForThisOpt > 0) { wchar *seperator = TheText.Get("FEC_IBT"); CFont::PrintString(nextX, nextY, seperator); nextX += CFont::GetStringWidth(seperator, true) + bindingMargin; } static PauseModeTime lastWaitingTextFlash = 0; if (CTimer::GetTimeInMillisecondsPauseMode() - lastWaitingTextFlash > 150) { showWaitingText = !showWaitingText; lastWaitingTextFlash = CTimer::GetTimeInMillisecondsPauseMode(); } if (showWaitingText) { CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); CFont::PrintString(nextX, nextY, TheText.Get("FEC_QUE")); // "???" } if (m_bKeyChangeNotProcessed) DisplayHelperText("FET_CIG"); else DisplayHelperText("FET_RIG"); SET_FONT_FOR_LIST_ITEM m_bKeyIsOK = true; } else { DisplayHelperText("FET_CIG"); SET_FONT_FOR_LIST_ITEM m_bKeyIsOK = false; m_bKeyChangeNotProcessed = false; } } else if (optionIdx == m_nSelectedListRow) { DisplayHelperText("FET_EIG"); SET_FONT_FOR_LIST_ITEM } } } } void CMenuManager::DrawControllerScreenExtraText(int yStart, int xStart, int lineHeight) { int extraTextStart = GetStartOptionsCntrlConfigScreens(); int numOpts = GetNumOptionsCntrlConfigScreens(); int spacing = MENU_X(10.0f); for (int i = extraTextStart; i < extraTextStart + numOpts; i++) { int numTextsPrinted = 0; int nextX = xStart; for (int j = 1; j < 5; j++) { wchar *text = ControlsManager.GetControllerSettingTextWithOrderNumber((e_ControllerAction)i, (eContSetOrder)j); if (text) ++numTextsPrinted; if (text) { // Seperator if (numTextsPrinted > 1) { CFont::PrintString(nextX, MENU_Y(yStart), TheText.Get("FEC_IBT")); nextX = CFont::GetStringWidth(TheText.Get("FEC_IBT"), true) + spacing + nextX; } CFont::PrintString(nextX, MENU_Y(yStart), text); } if (text) nextX = CFont::GetStringWidth(text, true) + spacing + nextX; } if (m_nCurrOption == i - extraTextStart && m_bWaitingForNewKeyBind) { static bool waitingTextVisible = false; // Seperator if (numTextsPrinted > 0) { CFont::PrintString(nextX, MENU_Y(yStart), TheText.Get("FEC_IBT")); nextX = CFont::GetStringWidth(TheText.Get("FEC_IBT"), true) + spacing + nextX; } static PauseModeTime lastStateChange = 0; if (CTimer::GetTimeInMillisecondsPauseMode() - lastStateChange > 150) { waitingTextVisible = !waitingTextVisible; lastStateChange = CTimer::GetTimeInMillisecondsPauseMode(); } if (waitingTextVisible) { CFont::SetColor(CRGBA(255, 255, 0, FadeIn(255))); CFont::PrintString(nextX, MENU_Y(yStart), TheText.Get("FEC_QUE")); CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, FadeIn(255))); } } yStart += lineHeight; } wchar *error = nil; if (DisplayComboButtonErrMsg) error = ControlsManager.GetButtonComboText((e_ControllerAction)(m_nCurrOption + extraTextStart)); if (error) { CFont::SetColor(CRGBA(233, 22, 159, 255)); CFont::PrintString(xStart, MENU_Y(yStart + 10), error); } } void CMenuManager::DrawControllerSetupScreen() { float rowHeight; switch (m_ControlMethod) { case CONTROL_STANDARD: rowHeight = CONTSETUP_STANDARD_ROW_HEIGHT; break; case CONTROL_CLASSIC: rowHeight = CONTSETUP_CLASSIC_ROW_HEIGHT; break; default: break; } RESET_FONT_FOR_NEW_PAGE SET_FONT_FOR_MENU_HEADER // Shadow CFont::SetColor(CRGBA(30, 30, 30, FadeIn(255))); if (m_ControlMethod == CONTROL_STANDARD) CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X) - MENU_X(7.f), SCREEN_SCALE_Y(MENUHEADER_POS_Y + 7.f), TheText.Get("FET_STI")); else if (m_ControlMethod == CONTROL_CLASSIC) CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X) - MENU_X(7.f), SCREEN_SCALE_Y(MENUHEADER_POS_Y + 7.f), TheText.Get("FET_CTI")); // Real header CFont::SetColor(CRGBA(HEADER_COLOR.r, HEADER_COLOR.g, HEADER_COLOR.b, FadeIn(255))); if (m_ControlMethod == CONTROL_STANDARD) CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X), SCREEN_SCALE_Y(MENUHEADER_POS_Y), TheText.Get("FET_STI")); else if (m_ControlMethod == CONTROL_CLASSIC) CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X), SCREEN_SCALE_Y(MENUHEADER_POS_Y), TheText.Get("FET_CTI")); wchar *actionTexts[33]; actionTexts[0] = TheText.Get("FEC_FIR"); actionTexts[1] = TheText.Get("FEC_NWE"); actionTexts[2] = TheText.Get("FEC_PWE"); actionTexts[3] = TheText.Get("FEC_FOR"); actionTexts[4] = TheText.Get("FEC_BAC"); actionTexts[5] = TheText.Get("FEC_LEF"); actionTexts[6] = TheText.Get("FEC_RIG"); actionTexts[7] = TheText.Get("FEC_ZIN"); actionTexts[8] = TheText.Get("FEC_ZOT"); actionTexts[9] = TheText.Get("FEC_EEX"); actionTexts[10] = TheText.Get("FEC_RAD"); actionTexts[11] = TheText.Get("FEC_HRN"); actionTexts[12] = TheText.Get("FEC_SUB"); actionTexts[13] = TheText.Get("FEC_CMR"); actionTexts[14] = TheText.Get("FEC_JMP"); actionTexts[15] = TheText.Get("FEC_SPN"); actionTexts[16] = TheText.Get("FEC_HND"); actionTexts[17] = TheText.Get("FEC_TAR"); actionTexts[18] = TheText.Get("FEC_CRO"); actionTexts[19] = TheText.Get("FEC_ANS"); if (m_ControlMethod == CONTROL_CLASSIC) { actionTexts[20] = TheText.Get("FEC_TFL"); actionTexts[21] = TheText.Get("FEC_TFR"); actionTexts[22] = TheText.Get("FEC_TFU"); actionTexts[23] = TheText.Get("FEC_TFD"); actionTexts[24] = TheText.Get("FEC_LBA"); actionTexts[25] = TheText.Get("FEC_LOL"); actionTexts[26] = TheText.Get("FEC_LOR"); actionTexts[27] = TheText.Get("FEC_LUD"); actionTexts[28] = TheText.Get("FEC_LDU"); actionTexts[29] = TheText.Get("FEC_NTR"); actionTexts[30] = TheText.Get("FEC_PTT"); actionTexts[31] = TheText.Get("FEC_CEN"); actionTexts[32] = nil; } else { actionTexts[20] = TheText.Get("FEC_TFL"); actionTexts[21] = TheText.Get("FEC_TFR"); actionTexts[22] = TheText.Get("FEC_TFU"); actionTexts[23] = TheText.Get("FEC_TFD"); actionTexts[24] = TheText.Get("FEC_LBA"); actionTexts[25] = TheText.Get("FEC_LOL"); actionTexts[26] = TheText.Get("FEC_LOR"); actionTexts[27] = nil; } // Blue panel background CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(CONTSETUP_LIST_LEFT), MENU_Y(CONTSETUP_LIST_TOP), MENU_X_RIGHT_ALIGNED(CONTSETUP_LIST_RIGHT), SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_LIST_BOTTOM)), CRGBA(LIST_BACKGROUND_COLOR.r, LIST_BACKGROUND_COLOR.g, LIST_BACKGROUND_COLOR.b, FadeIn(LIST_BACKGROUND_COLOR.a))); if (m_nCurrExLayer == HOVEROPTION_LIST) CFont::SetColor(CRGBA(SELECTEDMENUOPTION_COLOR.r, SELECTEDMENUOPTION_COLOR.g, SELECTEDMENUOPTION_COLOR.b, FadeIn(255))); else CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); // List header CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT), MENU_Y(MENUACTION_SCALE_MULT)); CFont::SetRightJustifyOff(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::PrintString(MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_2_X), MENU_Y(CONTSETUP_LIST_TOP), TheText.Get("FET_CFT")); CFont::PrintString(MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_3_X), MENU_Y(CONTSETUP_LIST_TOP), TheText.Get("FET_CCR")); CFont::SetDropShadowPosition(0); SET_FONT_FOR_LIST_ITEM int yStart; if (m_ControlMethod == CONTROL_CLASSIC) yStart = CONTSETUP_LIST_TOP + 18; else yStart = CONTSETUP_LIST_TOP + 21; float optionYBottom = yStart + rowHeight; for (int i = 0; i < ARRAY_SIZE(actionTexts); ++i) { wchar *actionText = actionTexts[i]; if (!actionText) break; if (!m_bWaitingForNewKeyBind) { if (m_nMousePosX > MENU_X_LEFT_ALIGNED(CONTSETUP_LIST_LEFT - 10.0f) && m_nMousePosX < MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_3_X + CONTSETUP_BOUND_COLUMN_WIDTH)) { if (m_nMousePosY > MENU_Y(i * rowHeight + yStart) && m_nMousePosY < MENU_Y(i * rowHeight + optionYBottom)) { m_nOptionMouseHovering = i; if (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY) { m_nCurrExLayer = HOVEROPTION_LIST; m_nSelectedListRow = i; // why different number for 3rd column hovering X?? this function is a mess #ifdef FIX_BUGS if (m_nMousePosX > MENU_X_LEFT_ALIGNED(0.0f) && m_nMousePosX < MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_2_X + CONTSETUP_BOUND_COLUMN_WIDTH)) { #else if (m_nMousePosX > MENU_X_LEFT_ALIGNED(0.0f) && m_nMousePosX < MENU_X_LEFT_ALIGNED(370.0f)) { #endif m_nSelectedContSetupColumn = CONTSETUP_PED_COLUMN; #ifdef FIX_BUGS } else if (m_nMousePosX > MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_2_X + CONTSETUP_BOUND_COLUMN_WIDTH) && m_nMousePosX < SCREEN_WIDTH) { #else } else if (m_nMousePosX > MENU_X_LEFT_ALIGNED(370.0f) && m_nMousePosX < SCREEN_WIDTH) { #endif m_nSelectedContSetupColumn = CONTSETUP_VEHICLE_COLUMN; } } // what?? if (m_nHoverOption == HOVEROPTION_SKIN) { if (i == m_nSelectedListRow) { m_nHoverOption = HOVEROPTION_NOT_HOVERING; m_bWaitingForNewKeyBind = true; m_bStartWaitingForKeyBind = true; pControlEdit = &m_KeyPressedCode; } } else m_nHoverOption = HOVEROPTION_NOT_HOVERING; } } } if (m_nSelectedListRow != i) CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); else if (m_nCurrExLayer == HOVEROPTION_LIST) CFont::SetColor(CRGBA(SELECTEDMENUOPTION_COLOR.r, SELECTEDMENUOPTION_COLOR.g, SELECTEDMENUOPTION_COLOR.b, FadeIn(255))); CFont::SetRightJustifyOff(); if (m_PrefsLanguage == LANGUAGE_GERMAN && (i == 20 || i == 21 || i == 22 || i == 23)) CFont::SetScale(MENU_X(0.32f), MENU_Y(LISTITEM_Y_SCALE)); else CFont::SetScale(MENU_X(LISTITEM_X_SCALE), MENU_Y(LISTITEM_Y_SCALE)); CFont::PrintString(MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_1_X), MENU_Y(i * rowHeight + yStart), actionText); } DrawControllerBound(yStart, MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_2_X), rowHeight, CONTSETUP_PED_COLUMN); DrawControllerBound(yStart, MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_3_X), rowHeight, CONTSETUP_VEHICLE_COLUMN); if (!m_bWaitingForNewKeyBind) { CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); if ((m_nMousePosX > MENU_X_RIGHT_ALIGNED(CONTSETUP_BACK_RIGHT) - CFont::GetStringWidth(TheText.Get("FEDS_TB"), true) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(CONTSETUP_BACK_RIGHT) && m_nMousePosY > SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_BACK_BOTTOM) && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_BACK_BOTTOM - CONTSETUP_BACK_HEIGHT)) || m_nCurrExLayer == HOVEROPTION_BACK) { m_nHoverOption = HOVEROPTION_BACK; } else if (m_nMousePosX > MENU_X_LEFT_ALIGNED(CONTSETUP_LIST_LEFT - 10.0f) && m_nMousePosX < MENU_X_LEFT_ALIGNED(CONTSETUP_COLUMN_3_X + CONTSETUP_BOUND_COLUMN_WIDTH) && m_nMousePosY > MENU_Y(CONTSETUP_LIST_TOP - 10.0f) && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_LIST_BOTTOM)) { m_nHoverOption = HOVEROPTION_LIST; } else { m_nHoverOption = HOVEROPTION_NOT_HOVERING; } } // Back button and it's shadow CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); CFont::SetRightJustifyOn(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); CFont::PrintString(MENU_X_RIGHT_ALIGNED(CONTSETUP_BACK_RIGHT - 2.0f), SCREEN_SCALE_FROM_BOTTOM(CONTSETUP_BACK_BOTTOM - 4.0f), TheText.Get("FEDS_TB")); } void CMenuManager::DrawFrontEnd() { CFont::SetAlphaFade(255.0f); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); SetFrontEndRenderStates(); m_NoEmptyBinding = true; if (m_nCurrScreen == MENUPAGE_NONE) { if (m_bGameNotLoaded) { m_nCurrScreen = MENUPAGE_START_MENU; } else { m_nCurrScreen = MENUPAGE_PAUSE_MENU; } SETUP_SCROLLING(m_nCurrScreen) } if (m_nCurrOption == 0 && aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL) m_nCurrOption = 1; if (m_firstStartCounter == 255 && m_nMenuFadeAlpha == 255) bMenuChangeOngoing = false; DrawBackground(false); } void CMenuManager::DrawBackground(bool transitionCall) { if (!m_bSpritesLoaded) return; SetFrontEndRenderStates(); if (m_firstStartCounter < 255) { CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(0, 0, 0, 255)); } if (m_nMenuFadeAlpha != 0) { if (m_nMenuFadeAlpha < 255) { menuBg.Translate(m_nMenuFadeAlpha); SetFrontEndRenderStates(); m_aFrontEndSprites[MENUSPRITE_BACKGROUND].Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, FadeIn(255))); if (m_nCurrScreen == MENUPAGE_MAP) PrintMap(); // Left border CSprite2d::Draw2DPolygon(SCREEN_STRETCH_X(menuBg.bottomLeft_x), SCREEN_STRETCH_Y(menuBg.bottomLeft_y), 0.0f, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.topLeft_x), SCREEN_STRETCH_Y(menuBg.topLeft_y), 0.0f, 0.0f, CRGBA(0, 0, 0, 255)); // Top border CSprite2d::Draw2DPolygon(SCREEN_STRETCH_X(menuBg.topRight_x), SCREEN_STRETCH_Y(menuBg.topRight_y), SCREEN_STRETCH_X(menuBg.topLeft_x), SCREEN_STRETCH_Y(menuBg.topLeft_y), SCREEN_WIDTH, 0.0f, 0.0f, 0.0f, CRGBA(0, 0, 0, 255)); // Bottom border CSprite2d::Draw2DPolygon(SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.bottomRight_x), SCREEN_STRETCH_Y(menuBg.bottomRight_y), SCREEN_STRETCH_X(menuBg.bottomLeft_x), SCREEN_STRETCH_Y(menuBg.bottomLeft_y), CRGBA(0, 0, 0, 255)); // Right border CSprite2d::Draw2DPolygon(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.bottomRight_x), SCREEN_STRETCH_Y(menuBg.bottomRight_y), SCREEN_WIDTH, 0.0f, SCREEN_STRETCH_X(menuBg.topRight_x), SCREEN_STRETCH_Y(menuBg.topRight_y), CRGBA(0, 0, 0, 255)); } else { m_nMenuFadeAlpha = 255; m_firstStartCounter = 255; m_aFrontEndSprites[MENUSPRITE_BACKGROUND].Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, FadeIn(255))); if (m_nCurrScreen == MENUPAGE_MAP) PrintMap(); // Left border CSprite2d::Draw2DPolygon(SCREEN_STRETCH_X(menuBg.bottomLeft_x), SCREEN_STRETCH_Y(menuBg.bottomLeft_y), 0.0f, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.topLeft_x), SCREEN_STRETCH_Y(menuBg.topLeft_y), 0.0f, 0.0f, CRGBA(0, 0, 0, 255)); // Top border CSprite2d::Draw2DPolygon(SCREEN_STRETCH_X(menuBg.topRight_x), SCREEN_STRETCH_Y(menuBg.topRight_y), SCREEN_STRETCH_X(menuBg.topLeft_x), SCREEN_STRETCH_Y(menuBg.topLeft_y), SCREEN_WIDTH, 0.0f, 0.0f, 0.0f, CRGBA(0, 0, 0, 255)); // Bottom border CSprite2d::Draw2DPolygon(SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.bottomRight_x), SCREEN_STRETCH_Y(menuBg.bottomRight_y), SCREEN_STRETCH_X(menuBg.bottomLeft_x), SCREEN_STRETCH_Y(menuBg.bottomLeft_y), CRGBA(0, 0, 0, 255)); // Right border CSprite2d::Draw2DPolygon(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_STRETCH_X(menuBg.bottomRight_x), SCREEN_STRETCH_Y(menuBg.bottomRight_y), SCREEN_WIDTH, 0.0f, SCREEN_STRETCH_X(menuBg.topRight_x), SCREEN_STRETCH_Y(menuBg.topRight_y), CRGBA(0, 0, 0, 255)); } } else { menuBg.SaveCurrentCoors(); switch (m_nCurrScreen) { case MENUPAGE_STATS: menuBg.topLeft_x = 70.0f; menuBg.topLeft_y = 75.0f; menuBg.topRight_x = 550.0f; menuBg.topRight_y = 16.0f; menuBg.bottomLeft_x = 74.0f; menuBg.bottomLeft_y = 354.0f; menuBg.bottomRight_x = 581.0f; menuBg.bottomRight_y = 340.0f; break; case MENUPAGE_SOUND_SETTINGS: menuBg.topLeft_x = 26.0f; menuBg.topLeft_y = 59.0f; menuBg.topRight_x = 629.0f; menuBg.topRight_y = 29.0f; menuBg.bottomLeft_x = 15.0f; menuBg.bottomLeft_y = 438.0f; menuBg.bottomRight_x = 610.0f; menuBg.bottomRight_y = 410.0f; break; case MENUPAGE_SKIN_SELECT: case MENUPAGE_KEYBOARD_CONTROLS: menuBg.topLeft_x = 14.0f; menuBg.topLeft_y = 39.0f; menuBg.topRight_x = 636.0f; menuBg.topRight_y = 29.0f; menuBg.bottomLeft_x = 15.0f; menuBg.bottomLeft_y = 426.0f; menuBg.bottomRight_x = 630.0f; menuBg.bottomRight_y = 398.0f; break; case MENUPAGE_BRIEFS: case MENUPAGE_DISPLAY_SETTINGS: case MENUPAGE_MAP: case MENUPAGE_CHOOSE_LOAD_SLOT: case MENUPAGE_CHOOSE_DELETE_SLOT: case MENUPAGE_CHOOSE_SAVE_SLOT: case MENUPAGE_MOUSE_CONTROLS: menuBg.topLeft_x = 26.0f; menuBg.topLeft_y = 59.0f; menuBg.topRight_x = 629.0f; menuBg.topRight_y = 29.0f; menuBg.bottomLeft_x = 15.0f; menuBg.bottomLeft_y = 426.0f; menuBg.bottomRight_x = 610.0f; menuBg.bottomRight_y = 398.0f; break; default: #ifdef CUSTOM_FRONTEND_OPTIONS if (aScreens[m_nCurrScreen].layout && aScreens[m_nCurrScreen].layout->noInvasiveBorders) { // Taken from the case above menuBg.topLeft_x = 26.0f; menuBg.topLeft_y = 59.0f; menuBg.topRight_x = 629.0f; menuBg.topRight_y = 29.0f; menuBg.bottomLeft_x = 15.0f; menuBg.bottomLeft_y = 426.0f; menuBg.bottomRight_x = 610.0f; menuBg.bottomRight_y = 398.0f; break; } #endif menuBg.topLeft_x = CGeneral::GetRandomNumber() % 40 + 65; menuBg.topLeft_y = CGeneral::GetRandomNumber() % 40 + 21; menuBg.topRight_x = CGeneral::GetRandomNumber() % 40 + 568; menuBg.topRight_y = CGeneral::GetRandomNumber() % 40 + 44; menuBg.bottomLeft_x = CGeneral::GetRandomNumber() % 40 + 36; menuBg.bottomLeft_y = CGeneral::GetRandomNumber() % 40 + 382; menuBg.bottomRight_x = CGeneral::GetRandomNumber() % 40 + 593; menuBg.bottomRight_y = CGeneral::GetRandomNumber() % 40 + 342; break; } menuBg.UpdateMultipliers(); if (m_firstStartCounter == 255) m_nOptionHighlightTransitionBlend = 0; } static PauseModeTime LastFade = 0; if (m_nMenuFadeAlpha < 255) { static uint8 forceFadeInCounter = 0; if (CTimer::GetTimeInMillisecondsPauseMode() - LastFade > 30 #ifndef FIX_HIGH_FPS_BUGS_ON_FRONTEND // Dirty dirty hack || forceFadeInCounter > 30 #endif ) { m_nMenuFadeAlpha += 20; if (m_firstStartCounter < 255) { m_firstStartCounter = Min(m_firstStartCounter + 20, 255); } LastFade = CTimer::GetTimeInMillisecondsPauseMode(); } forceFadeInCounter++; } else if (m_nMenuFadeAlpha > 255) m_nMenuFadeAlpha = 255; if (!transitionCall && m_firstStartCounter == 255) { int actualAlpha = m_nMenuFadeAlpha; if (actualAlpha < 255) { int actualScreen = m_nCurrScreen; SetFrontEndRenderStates(); m_nCurrScreen = m_nPrevScreen; m_nMenuFadeAlpha = 255 - m_nMenuFadeAlpha; switch (m_nCurrScreen) { case MENUPAGE_SKIN_SELECT: DrawPlayerSetupScreen(false); break; case MENUPAGE_KEYBOARD_CONTROLS: DrawControllerSetupScreen(); break; case MENUPAGE_OUTRO: DrawQuitGameScreen(); break; default: DrawStandardMenus(false); break; } m_nCurrScreen = actualScreen; m_nMenuFadeAlpha = actualAlpha; } } switch (m_nCurrScreen) { case MENUPAGE_SKIN_SELECT: DrawPlayerSetupScreen(true); break; case MENUPAGE_KEYBOARD_CONTROLS: DrawControllerSetupScreen(); break; case MENUPAGE_OUTRO: DrawQuitGameScreen(); break; default: DrawStandardMenus(true); break; } CFont::DrawFonts(); SetFrontEndRenderStates(); if (m_nCurrScreen != MENUPAGE_OUTRO) if (m_firstStartCounter == 255) { m_aFrontEndSprites[MENUSPRITE_VCLOGO].Draw(CRect(SCREEN_STRETCH_X(27.0f), MENU_Y(8.0f), SCREEN_STRETCH_X(27.0f) + MENU_X(130.f), MENU_Y(138.0f)), CRGBA(255, 255, 255, 255)); } else { m_aFrontEndSprites[MENUSPRITE_VCLOGO].Draw(CRect(SCREEN_STRETCH_X(27.0f), MENU_Y(8.0f), SCREEN_STRETCH_X(27.0f) + MENU_X(130.f), MENU_Y(138.0f)), CRGBA(255, 255, 255, FadeIn(255))); } if (m_ShowEmptyBindingError) { static PauseModeTime lastBindingError = CTimer::GetTimeInMillisecondsPauseMode(); static bool bindingErrorShown = false; if (bindingErrorShown) { lastBindingError = CTimer::GetTimeInMillisecondsPauseMode(); bindingErrorShown = false; } SmallMessageScreen("FEC_ERI"); CFont::DrawFonts(); if (CTimer::GetTimeInMillisecondsPauseMode() - lastBindingError > 4000) { m_ShowEmptyBindingError = false; bindingErrorShown = true; } } if (m_bShowMouse) { CRect mouse(0.0f, 0.0f, MENU_X(35.0f), MENU_Y(35.0f)); CRect shad(MENU_X(10.0f), MENU_Y(3.0f), MENU_X(45.0f), MENU_Y(38.0f)); mouse.Translate(m_nMousePosX, m_nMousePosY); shad.Translate(m_nMousePosX, m_nMousePosY); m_aFrontEndSprites[MENUSPRITE_MOUSE].Draw(shad, CRGBA(100, 100, 100, 50)); m_aFrontEndSprites[MENUSPRITE_MOUSE].Draw(mouse, CRGBA(255, 255, 255, 255)); } } void CMenuManager::DrawPlayerSetupScreen(bool activeScreen) { RESET_FONT_FOR_NEW_PAGE // lstrcpy's changed with strcpy if (!m_bSkinsEnumerated) { OutputDebugString("Enumerating skin filenames from skins..."); m_pSkinListHead.nextSkin = nil; m_pSelectedSkin = &m_pSkinListHead; m_pSelectedSkin->nextSkin = new tSkinInfo; m_pSelectedSkin = m_pSelectedSkin->nextSkin; m_pSelectedSkin->skinId = 0; strcpy(m_pSelectedSkin->skinNameOriginal, DEFAULT_SKIN_NAME); strcpy(m_pSelectedSkin->skinNameDisplayed, UnicodeToAscii(TheText.Get("FET_DSN"))); int nextSkinId = 1; m_pSelectedSkin->nextSkin = nil; WIN32_FIND_DATA FindFileData; SYSTEMTIME SystemTime; HANDLE handle = FindFirstFile("skins\\*.bmp", &FindFileData); for (int i = 1; handle != INVALID_HANDLE_VALUE && i; i = FindNextFile(handle, &FindFileData)) { if (strcmp(FindFileData.cFileName, DEFAULT_SKIN_NAME) != 0) { m_pSelectedSkin->nextSkin = new tSkinInfo; m_pSelectedSkin = m_pSelectedSkin->nextSkin; m_pSelectedSkin->skinId = nextSkinId; strcpy(m_pSelectedSkin->skinNameOriginal, FindFileData.cFileName); strcpy(m_pSelectedSkin->skinNameDisplayed, FindFileData.cFileName); FileTimeToSystemTime(&FindFileData.ftLastWriteTime, &SystemTime); GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &SystemTime, 0, m_pSelectedSkin->date, 255); ++nextSkinId; m_pSelectedSkin->nextSkin = nil; } } FindClose(handle); m_nSkinsTotal = nextSkinId; char nameTemp[256]; for (m_pSelectedSkin = m_pSkinListHead.nextSkin; m_pSelectedSkin; m_pSelectedSkin = m_pSelectedSkin->nextSkin) { // Drop extension int oldLength = (int)strlen(m_pSelectedSkin->skinNameDisplayed); m_pSelectedSkin->skinNameDisplayed[oldLength - 4] = '\0'; m_pSelectedSkin->skinNameOriginal[oldLength - 4] = '\0'; // Fill to 40 bytes-39 chars, idk why. This is done in sepearate function in game. strncpy(nameTemp, m_pSelectedSkin->skinNameDisplayed, 39); // game doesn't do that, but in our day strncpy to same string is forbidden strncpy(m_pSelectedSkin->skinNameDisplayed, nameTemp, 39); if (oldLength - 4 > 39) m_pSelectedSkin->skinNameDisplayed[39] = '\0'; // Make string lowercase, except first letter strlwr(m_pSelectedSkin->skinNameDisplayed); strncpy(nameTemp, m_pSelectedSkin->skinNameDisplayed, 1); strupr(nameTemp); strncpy(m_pSelectedSkin->skinNameDisplayed, nameTemp, 1); // Change some chars #ifdef FIX_BUGS for (int k = 0; m_pSelectedSkin->skinNameDisplayed[k] != '\0'; ++k) { #else for (int k = 0; m_pSelectedSkin->skinNameOriginal[k] != '\0'; ++k) { #endif if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "_", 1)) strncpy(&m_pSelectedSkin->skinNameDisplayed[k], " ", 1); if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "@", 1)) strncpy(&m_pSelectedSkin->skinNameDisplayed[k], " ", 1); if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "{", 1)) strncpy(&m_pSelectedSkin->skinNameDisplayed[k], "(", 1); if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "}", 1)) strncpy(&m_pSelectedSkin->skinNameDisplayed[k], ")", 1); if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[k], "£", 1)) strncpy(&m_pSelectedSkin->skinNameDisplayed[k], "$", 1); } // Make letters after whitespace uppercase for (int l = 0; m_pSelectedSkin->skinNameDisplayed[l] != '\0'; ++l) { if (!strncmp(&m_pSelectedSkin->skinNameDisplayed[l], " ", 1)) { if (m_pSelectedSkin->skinNameDisplayed[l + 1]) { strncpy(nameTemp, &m_pSelectedSkin->skinNameDisplayed[l + 1], 1); strupr(nameTemp); strncpy(&m_pSelectedSkin->skinNameDisplayed[l + 1], nameTemp, 1); } } } } OutputDebugString("Finished enumerating skin files."); m_bSkinsEnumerated = true; } CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT), MENU_Y(PLAYERSETUP_LIST_TOP), MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM)), CRGBA(LIST_BACKGROUND_COLOR.r, LIST_BACKGROUND_COLOR.g, LIST_BACKGROUND_COLOR.b, FadeIn(LIST_BACKGROUND_COLOR.a))); SET_FONT_FOR_MENU_HEADER CFont::SetColor(CRGBA(30, 30, 30, FadeIn(255))); CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X) - MENU_X(7.f), SCREEN_SCALE_Y(MENUHEADER_POS_Y + 7.f), TheText.Get("FET_PS")); CFont::SetColor(CRGBA(HEADER_COLOR.r, HEADER_COLOR.g, HEADER_COLOR.b, FadeIn(255))); CFont::PrintString(SCREEN_STRETCH_FROM_RIGHT(MENUHEADER_POS_X), SCREEN_SCALE_Y(MENUHEADER_POS_Y), TheText.Get("FET_PS")); // Header (Skin - Date) if (m_nCurrExLayer == HOVEROPTION_LIST) { CFont::SetColor(CRGBA(SELECTEDMENUOPTION_COLOR.r, SELECTEDMENUOPTION_COLOR.g, SELECTEDMENUOPTION_COLOR.b, FadeIn(255))); } else { CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); } CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT), MENU_Y(MENUACTION_SCALE_MULT)); CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_DATE_COLUMN_RIGHT), MENU_Y(PLAYERSETUP_LIST_TOP), TheText.Get("FES_DAT")); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: case LANGUAGE_SPANISH: CFont::SetScale(MENU_X(0.6f), MENU_Y(MENUACTION_SCALE_MULT)); break; default: CFont::SetScale(MENU_X(MENUACTION_SCALE_MULT), MENU_Y(MENUACTION_SCALE_MULT)); break; } CFont::SetRightJustifyOff(); CFont::PrintString(MENU_X_LEFT_ALIGNED(PLAYERSETUP_SKIN_COLUMN_LEFT), MENU_Y(PLAYERSETUP_LIST_TOP), TheText.Get("FES_SKN")); CFont::SetDropShadowPosition(0); // Skin list SET_FONT_FOR_LIST_ITEM if (m_nSkinsTotal > 0) { for (m_pSelectedSkin = m_pSkinListHead.nextSkin; m_pSelectedSkin->skinId != m_nFirstVisibleRowOnList; m_pSelectedSkin = m_pSelectedSkin->nextSkin); int rowTextY = PLAYERSETUP_LIST_BODY_TOP - 1; int orderInVisibles = 0; int rowEndY = PLAYERSETUP_LIST_BODY_TOP + PLAYERSETUP_ROW_HEIGHT + 1; int rowStartY = PLAYERSETUP_LIST_BODY_TOP; for (int rowIdx = m_nFirstVisibleRowOnList; rowIdx < m_nFirstVisibleRowOnList + MAX_VISIBLE_LIST_ROW && m_pSelectedSkin; ) { if (m_nMousePosX > MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT)) { if (m_nMousePosY > MENU_Y(rowStartY) && m_nMousePosY < MENU_Y(rowEndY)) { m_nOptionMouseHovering = rowIdx; if (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY) { m_nCurrExLayer = HOVEROPTION_LIST; } if (m_nHoverOption == HOVEROPTION_SKIN) { if (rowIdx == m_nSelectedListRow) { m_nHoverOption = HOVEROPTION_NOT_HOVERING; if (m_nSkinsTotal > 0) { strcpy(m_PrefsSkinFile, m_aSkinName); CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); SaveSettings(); } } else { m_nCurrExLayer = HOVEROPTION_LIST; m_nSelectedListRow = rowIdx; m_nHoverOption = HOVEROPTION_NOT_HOVERING; } } } } // Preview skin/change color of row when we focused on another row. if (orderInVisibles == m_nSelectedListRow - m_nFirstVisibleRowOnList) { CFont::SetColor(CRGBA(255, 255, 255, FadeIn(255))); static int lastSelectedSkin = -1; if (m_nSelectedListRow != lastSelectedSkin) { strcpy(m_aSkinName, m_pSelectedSkin->skinNameOriginal); CWorld::Players[0].SetPlayerSkin(m_aSkinName); } lastSelectedSkin = m_nSelectedListRow; } else if (!strcmp(m_PrefsSkinFile, m_pSelectedSkin->skinNameOriginal)) { CFont::SetColor(CRGBA(255, 255, 155, FadeIn(255))); } else { CFont::SetColor(CRGBA(LIST_OPTION_COLOR.r, LIST_OPTION_COLOR.g, LIST_OPTION_COLOR.b, FadeIn(LIST_OPTION_COLOR.a))); } wchar unicodeTemp[80]; AsciiToUnicode(m_pSelectedSkin->skinNameDisplayed, unicodeTemp); CFont::SetRightJustifyOff(); CFont::PrintString(MENU_X_LEFT_ALIGNED(PLAYERSETUP_SKIN_COLUMN_LEFT), MENU_Y(rowTextY), unicodeTemp); // If not "Default skin" option if (rowIdx != 0) { char dateTemp[32]; sprintf(dateTemp, "%s", m_pSelectedSkin->date); AsciiToUnicode(dateTemp, unicodeTemp); CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_DATE_COLUMN_RIGHT), MENU_Y(rowTextY), unicodeTemp); } ++orderInVisibles; rowEndY += PLAYERSETUP_ROW_HEIGHT; rowStartY += PLAYERSETUP_ROW_HEIGHT; rowTextY += PLAYERSETUP_ROW_HEIGHT; ++rowIdx; m_pSelectedSkin = m_pSelectedSkin->nextSkin; } // Scrollbar background - it's unchanged since III and still yellowish... CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), MENU_Y(PLAYERSETUP_LIST_TOP), MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2 - PLAYERSETUP_SCROLLBAR_WIDTH), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM)), CRGBA(100, 100, 66, FadeIn(205))); float scrollbarHeight = SCROLLBAR_MAX_HEIGHT / (m_nSkinsTotal / (float) MAX_VISIBLE_LIST_ROW); float scrollbarBottom, scrollbarTop; if (m_nSkinsTotal <= MAX_VISIBLE_LIST_ROW) { scrollbarBottom = SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 4.0f); scrollbarTop = MENU_Y(PLAYERSETUP_LIST_BODY_TOP); // Scrollbar shadow CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 4), scrollbarTop, MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 1 - PLAYERSETUP_SCROLLBAR_WIDTH), scrollbarBottom + MENU_Y(1.0f)), CRGBA(50, 50, 50, FadeIn(255))); } else { #ifdef FIX_BUGS scrollbarBottom = MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 8 + m_nScrollbarTopMargin + scrollbarHeight); scrollbarTop = MENU_Y(PLAYERSETUP_LIST_BODY_TOP + m_nScrollbarTopMargin); #else scrollbarBottom = MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 4 + m_nScrollbarTopMargin + scrollbarHeight - SCROLLBAR_MAX_HEIGHT / m_nSkinsTotal); scrollbarTop = MENU_Y(SCROLLBAR_MAX_HEIGHT / m_nSkinsTotal + PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin); #endif // Scrollbar shadow CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 4), scrollbarTop, MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 1 - PLAYERSETUP_SCROLLBAR_WIDTH), scrollbarBottom + MENU_Y(1.0f)), CRGBA(50, 50, 50, FadeIn(255))); } // Scrollbar CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 4), scrollbarTop, MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH), scrollbarBottom), CRGBA(SCROLLBAR_COLOR.r, SCROLLBAR_COLOR.g, SCROLLBAR_COLOR.b, FadeIn(255))); // FIX: Scroll button dimensions are buggy, because: // 1 - stretches the original image // 2 - leaves gap between button and scrollbar if (m_nHoverOption == HOVEROPTION_CLICKED_SCROLL_UP) { #ifdef FIX_BUGS m_aFrontEndSprites[MENUSPRITE_UPON].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), MENU_Y(PLAYERSETUP_LIST_TOP), MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION), MENU_Y(PLAYERSETUP_LIST_TOP + PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION)), CRGBA(255, 255, 255, FadeIn(255))); #else m_aFrontEndSprites[MENUSPRITE_UPON].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), MENU_Y(PLAYERSETUP_LIST_TOP), MENU_X_RIGHT_ALIGNED(-20.0f), MENU_Y(PLAYERSETUP_LIST_TOP + 58)), CRGBA(255, 255, 255, FadeIn(255))); #endif } else { #ifdef FIX_BUGS m_aFrontEndSprites[MENUSPRITE_UPOFF].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), MENU_Y(PLAYERSETUP_LIST_TOP), MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION), MENU_Y(PLAYERSETUP_LIST_TOP + PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION)), CRGBA(255, 255, 255, FadeIn(255))); #else m_aFrontEndSprites[MENUSPRITE_UPOFF].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), MENU_Y(PLAYERSETUP_LIST_TOP), MENU_X_RIGHT_ALIGNED(-21.0f), MENU_Y(PLAYERSETUP_LIST_TOP + 58)), CRGBA(255, 255, 255, FadeIn(255))); #endif } if (m_nHoverOption == HOVEROPTION_CLICKED_SCROLL_DOWN) { #ifdef FIX_BUGS m_aFrontEndSprites[MENUSPRITE_DOWNON].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1), MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION)), CRGBA(255, 255, 255, FadeIn(255))); #else m_aFrontEndSprites[MENUSPRITE_DOWNON].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2), SCREEN_SCALE_FROM_BOTTOM(141.0f), MENU_X_RIGHT_ALIGNED(-20.0f), SCREEN_SCALE_FROM_BOTTOM(83.0f)), CRGBA(255, 255, 255, FadeIn(255))); #endif } else { #ifdef FIX_BUGS m_aFrontEndSprites[MENUSPRITE_DOWNOFF].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1), MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1 - PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION)), CRGBA(255, 255, 255, FadeIn(255))); #else m_aFrontEndSprites[MENUSPRITE_DOWNOFF].Draw(CRect(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), SCREEN_SCALE_FROM_BOTTOM(141.0f), MENU_X_RIGHT_ALIGNED(-21.0f), SCREEN_SCALE_FROM_BOTTOM(83.0f)), CRGBA(255, 255, 255, FadeIn(255))); #endif } if (activeScreen) CPlayerSkin::RenderFrontendSkinEdit(); // Big apply button if (strcmp(m_aSkinName, m_PrefsSkinFile) != 0) { CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: CFont::SetScale(MENU_X(1.1f), MENU_Y(1.9f)); break; case LANGUAGE_GERMAN: CFont::SetScale(MENU_X(0.85f), MENU_Y(1.9f)); break; case LANGUAGE_ITALIAN: case LANGUAGE_SPANISH: CFont::SetScale(MENU_X(1.4f), MENU_Y(1.9f)); break; default: CFont::SetScale(MENU_X(1.9f), MENU_Y(1.9f)); break; } CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(120))); CFont::SetRightJustifyOff(); CFont::PrintString(MENU_X_LEFT_ALIGNED(24.0f), MENU_Y(220.0f), TheText.Get("FET_APP")); } CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); if ((m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 1) - CFont::GetStringWidth(TheText.Get("FEDS_TB"), true) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 1) && m_nMousePosY > SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 3) && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 26)) || m_nCurrExLayer == HOVEROPTION_BACK) { m_nHoverOption = HOVEROPTION_BACK; } else if ((strcmp(m_aSkinName, m_PrefsSkinFile) != 0 && m_nMousePosX > MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT) && m_nMousePosX < MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT) + CFont::GetStringWidth(TheText.Get("FES_SET"), true) && m_nMousePosY > SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 3) && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 26)) || m_nCurrExLayer == HOVEROPTION_USESKIN) { m_nHoverOption = HOVEROPTION_USESKIN; } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH - 2) && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_TOP) && m_nMousePosY < MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 3)) { if (m_nHoverOption != HOVEROPTION_CLICKED_SCROLL_UP && m_nHoverOption != HOVEROPTION_CLICKED_SCROLL_DOWN) m_nHoverOption = HOVEROPTION_OVER_SCROLL_UP; } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH - 2) && m_nMousePosY > SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1) && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM)) { if (m_nHoverOption != HOVEROPTION_CLICKED_SCROLL_UP && m_nHoverOption != HOVEROPTION_CLICKED_SCROLL_DOWN) m_nHoverOption = HOVEROPTION_OVER_SCROLL_DOWN; } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH - 2) && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 3) #ifdef FIX_BUGS && m_nMousePosY < MENU_Y(PLAYERSETUP_LIST_BODY_TOP + m_nScrollbarTopMargin)) { #else && m_nMousePosY < MENU_Y(SCROLLBAR_MAX_HEIGHT / m_nTotalListRow + PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin)) { #endif m_nHoverOption = HOVEROPTION_PAGEUP; } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 2) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH - 2) #ifdef FIX_BUGS && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 8 + m_nScrollbarTopMargin + scrollbarHeight) #else && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin + scrollbarHeight - SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) #endif && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM + PLAYERSETUP_SCROLLBUTTON_HEIGHT + 1)) { m_nHoverOption = HOVEROPTION_PAGEDOWN; } else if (m_nMousePosX > MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 4) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - PLAYERSETUP_SCROLLBAR_WIDTH) #ifdef FIX_BUGS && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP + m_nScrollbarTopMargin) && m_nMousePosY < MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 8 + m_nScrollbarTopMargin + scrollbarHeight)) { #else && m_nMousePosY > MENU_Y(SCROLLBAR_MAX_HEIGHT / m_nTotalListRow + PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin) && m_nMousePosY < MENU_Y(PLAYERSETUP_LIST_BODY_TOP - 3 + m_nScrollbarTopMargin + scrollbarHeight - SCROLLBAR_MAX_HEIGHT / m_nTotalListRow)) { #endif m_nHoverOption = HOVEROPTION_HOLDING_SCROLLBAR; } else if (m_nMousePosX > MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT) && m_nMousePosX < MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT) && m_nMousePosY > MENU_Y(PLAYERSETUP_LIST_BODY_TOP + 1) && m_nMousePosY < SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM)) { m_nHoverOption = HOVEROPTION_LIST; } else { m_nHoverOption = HOVEROPTION_NOT_HOVERING; } } CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(MENU_X(BIGTEXT_X_SCALE), MENU_Y(BIGTEXT_Y_SCALE)); CFont::SetRightJustifyOn(); CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); // Back button CFont::PrintString(MENU_X_RIGHT_ALIGNED(PLAYERSETUP_LIST_RIGHT - 3), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 5), TheText.Get("FEDS_TB")); CFont::SetRightJustifyOff(); if (!strcmp(m_aSkinName, m_PrefsSkinFile)) { CFont::SetColor(CRGBA(DARKMENUOPTION_COLOR.r, DARKMENUOPTION_COLOR.g, DARKMENUOPTION_COLOR.b, FadeIn(255))); } else { CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); } // Use skin button CFont::PrintString(MENU_X_LEFT_ALIGNED(PLAYERSETUP_LIST_LEFT), SCREEN_SCALE_FROM_BOTTOM(PLAYERSETUP_LIST_BOTTOM - 5), TheText.Get("FES_SET")); CFont::SetDropShadowPosition(0); } int CMenuManager::FadeIn(int alpha) { return Min(m_nMenuFadeAlpha, alpha); } int CMenuManager::GetStartOptionsCntrlConfigScreens() { int number = 0; switch (m_nCurrScreen) { #ifdef LEGACY_MENU_OPTIONS case MENUPAGE_CONTROLLER_PC_OLD3: number = 34; break; case MENUPAGE_CONTROLLER_DEBUG: number = 35; break; #endif case MENUPAGE_KEYBOARD_CONTROLS: number = 0; break; default: break; } return number; } void CMenuManager::InitialiseChangedLanguageSettings() { if (m_bFrontEnd_ReloadObrTxtGxt) { m_bFrontEnd_ReloadObrTxtGxt = false; CTimer::Stop(); TheText.Unload(); TheText.Load(); CTimer::Update(); CGame::frenchGame = false; CGame::germanGame = false; #ifdef MORE_LANGUAGES CGame::russianGame = false; CGame::japaneseGame = false; switch (m_PrefsLanguage) { case LANGUAGE_POLISH: CFont::ReloadFonts(FONT_LANGSET_POLISH); break; case LANGUAGE_RUSSIAN: CFont::ReloadFonts(FONT_LANGSET_RUSSIAN); break; case LANGUAGE_JAPANESE: CFont::ReloadFonts(FONT_LANGSET_JAPANESE); break; default: CFont::ReloadFonts(FONT_LANGSET_EFIGS); break; } #endif switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: CGame::frenchGame = true; break; case LANGUAGE_GERMAN: CGame::germanGame = true; break; #ifdef MORE_LANGUAGES case LANGUAGE_RUSSIAN: CGame::russianGame = true; break; case LANGUAGE_JAPANESE: CGame::japaneseGame = true; break; #endif default: break; } } } void CMenuManager::LoadAllTextures() { if (m_bSpritesLoaded) return; // First icon is hidden behind arrow m_LeftMostRadioX = MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X - MENURADIO_ICON_SIZE); CTimer::Stop(); CStreaming::MakeSpaceFor(350 * CDSTREAM_SECTOR_SIZE); // twice of it in mobile CStreaming::ImGonnaUseStreamingMemory(); CGame::TidyUpMemory(false, true); CTxdStore::PushCurrentTxd(); int frontendTxdSlot1 = CTxdStore::FindTxdSlot("frontend1"); if(frontendTxdSlot1 == -1) frontendTxdSlot1 = CTxdStore::AddTxdSlot("frontend1"); printf("LOAD frontend1\n"); CTxdStore::LoadTxd(frontendTxdSlot1, "MODELS/FRONTEN1.TXD"); CTxdStore::AddRef(frontendTxdSlot1); CTxdStore::SetCurrentTxd(frontendTxdSlot1); for (int i = 0; i < 3; i++) { m_aFrontEndSprites[i].SetTexture(FrontendFilenames[i][0], FrontendFilenames[i][1]); m_aFrontEndSprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); } CTxdStore::PopCurrentTxd(); CStreaming::IHaveUsedStreamingMemory(); if (!m_OnlySaveMenu) { CStreaming::MakeSpaceFor(692 * CDSTREAM_SECTOR_SIZE); // twice of it in mobile CStreaming::ImGonnaUseStreamingMemory(); CTxdStore::PushCurrentTxd(); int frontendTxdSlot2 = CTxdStore::FindTxdSlot("frontend2"); if (frontendTxdSlot2 == -1) frontendTxdSlot2 = CTxdStore::AddTxdSlot("frontend2"); printf("LOAD frontend2\n"); CTxdStore::LoadTxd(frontendTxdSlot2, "MODELS/FRONTEN2.TXD"); CTxdStore::AddRef(frontendTxdSlot2); CTxdStore::SetCurrentTxd(frontendTxdSlot2); #ifdef GAMEPAD_MENU for (int i = 3; i < MENUSPRITE_CONTROLLER; i++) { #else for (int i = 3; i < NUM_MENU_SPRITES; i++) { #endif m_aFrontEndSprites[i].SetTexture(FrontendFilenames[i][0], FrontendFilenames[i][1]); m_aFrontEndSprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); } CTxdStore::PopCurrentTxd(); #ifdef GAMEPAD_MENU LoadController(m_PrefsControllerType); #endif CStreaming::IHaveUsedStreamingMemory(); } m_bSpritesLoaded = true; CTimer::Update(); } void CMenuManager::LoadSettings() { CFileMgr::SetDirMyDocuments(); int fileHandle = CFileMgr::OpenFile("gta_vc.set", "r"); int32 prevLang = m_PrefsLanguage; MousePointerStateHelper.bInvertVertically = true; CMBlur::BlurOn = false; // 50 is silly char headerText[50]; int someVersion = 0; bool fileIsValid = true; if (fileHandle) { CFileMgr::Read(fileHandle, headerText, 29); if (strncmp(headerText, TopLineEmptyFile, sizeof(TopLineEmptyFile) - 1) == 0) { fileIsValid = false; } else { CFileMgr::Seek(fileHandle, 0, 0); CFileMgr::Read(fileHandle, (char*)&someVersion, sizeof(someVersion)); } if (fileIsValid && someVersion >= 3) { ControlsManager.LoadSettings(fileHandle); #ifdef IMPROVED_VIDEOMODE CFileMgr::Read(fileHandle, (char*)&m_nPrefsWidth, sizeof(m_nPrefsWidth)); CFileMgr::Read(fileHandle, (char*)&m_nPrefsHeight, sizeof(m_nPrefsHeight)); CFileMgr::Read(fileHandle, (char*)&m_nPrefsDepth, sizeof(m_nPrefsDepth)); CFileMgr::Read(fileHandle, (char*)&m_nPrefsWindowed, sizeof(m_nPrefsWindowed)); CFileMgr::Read(fileHandle, (char*)&m_nPrefsSubsystem, sizeof(m_nPrefsSubsystem)); if(m_nPrefsWindowed != 0 && m_nPrefsWindowed != 1){ // garbage data from vanilla settings file // let skeleton find something m_nPrefsWidth = 0; m_nPrefsHeight = 0; m_nPrefsDepth = 0; m_nPrefsWindowed = 0; m_nPrefsSubsystem = 0; } m_nSelectedScreenMode = m_nPrefsWindowed; #else CFileMgr::Read(fileHandle, gString, 20); #endif CFileMgr::Read(fileHandle, gString, 20); CFileMgr::Read(fileHandle, gString, 4); CFileMgr::Read(fileHandle, gString, 4); CFileMgr::Read(fileHandle, gString, 1); #ifdef LEGACY_MENU_OPTIONS CFileMgr::Read(fileHandle, (char*)&m_PrefsVsyncDisp, 1); CFileMgr::Read(fileHandle, (char*)&CMBlur::BlurOn, 1); #else CFileMgr::Read(fileHandle, gString, 1); CFileMgr::Read(fileHandle, gString, 1); #endif CFileMgr::Read(fileHandle, (char*)&TheCamera.m_bHeadBob, 1); CFileMgr::Read(fileHandle, (char*)&TheCamera.m_fMouseAccelHorzntl, 4); CFileMgr::Read(fileHandle, (char*)&MousePointerStateHelper.bInvertVertically, 1); CFileMgr::Read(fileHandle, (char*)&CVehicle::m_bDisableMouseSteering, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsSfxVolume, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsMusicVolume, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsMP3BoostVolume, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsRadioStation, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsSpeakers, 1); CFileMgr::Read(fileHandle, (char*)&m_nPrefsAudio3DProviderIndex, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsDMA, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsBrightness, 2); CFileMgr::Read(fileHandle, (char*)&m_PrefsLOD, sizeof(m_PrefsLOD)); CFileMgr::Read(fileHandle, (char*)&m_PrefsShowSubtitles, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsUseWideScreen, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsFrameLimiter, 1); CFileMgr::Read(fileHandle, (char*)&m_nDisplayVideoMode, 1); CFileMgr::Read(fileHandle, m_PrefsSkinFile, 256); CFileMgr::Read(fileHandle, (char*)&m_ControlMethod, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsLanguage, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsShowHud, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsRadarMode, 1); CFileMgr::Read(fileHandle, (char*)&m_PrefsShowLegends, 1); } } CFileMgr::CloseFile(fileHandle); CFileMgr::SetDir(""); #ifdef LOAD_INI_SETTINGS if (LoadINISettings()) { LoadINIControllerSettings(); } // if no reVC.ini, create it, or update it with new values SaveINISettings(); SaveINIControllerSettings(); #endif #ifdef FIX_BUGS TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f; #endif #ifdef PC_PLAYER_CONTROLS CCamera::m_bUseMouse3rdPerson = m_ControlMethod == CONTROL_STANDARD; #endif #ifdef LEGACY_MENU_OPTIONS m_PrefsVsync = m_PrefsVsyncDisp; #endif CRenderer::ms_lodDistScale = m_PrefsLOD; if (m_nPrefsAudio3DProviderIndex == NO_AUDIO_PROVIDER) m_nPrefsAudio3DProviderIndex = -2; m_lastWorking3DAudioProvider = m_nPrefsAudio3DProviderIndex; if (m_PrefsLanguage == prevLang) m_bLanguageLoaded = false; else { m_bLanguageLoaded = true; TheText.Unload(); TheText.Load(); m_bFrontEnd_ReloadObrTxtGxt = true; InitialiseChangedLanguageSettings(); OutputDebugString("The previously saved language is now in use"); } WIN32_FIND_DATA FindFileData; char skinfile[256+16]; // Stack analysis shows 16 bits gap, but I don't trust it. It may very well be MAX_PATH(260). bool SkinFound = false; HANDLE handle = FindFirstFile("skins\\*.bmp", &FindFileData); for (int i = 1; handle != INVALID_HANDLE_VALUE && i; i = FindNextFile(handle, &FindFileData)) { strcpy(skinfile, m_PrefsSkinFile); strcat(skinfile, ".bmp"); if (strcmp(FindFileData.cFileName, skinfile) == 0) SkinFound = true; } FindClose(handle); if (!SkinFound) { OutputDebugString("Default skin set as no other skins are available OR saved skin not found!"); strcpy(m_PrefsSkinFile, DEFAULT_SKIN_NAME); strcpy(m_aSkinName, DEFAULT_SKIN_NAME); } } void CMenuManager::SaveSettings() { #ifndef LOAD_INI_SETTINGS static char RubbishString[48] = "stuffmorestuffevenmorestuff etc"; #ifdef BIND_VEHICLE_FIREWEAPON static int SomeVersion = 4; #else static int SomeVersion = 3; #endif CFileMgr::SetDirMyDocuments(); int fileHandle = CFileMgr::OpenFile("gta_vc.set", "w+"); if (fileHandle) { CFileMgr::Write(fileHandle, (char*)&SomeVersion, sizeof(SomeVersion)); ControlsManager.SaveSettings(fileHandle); #ifdef IMPROVED_VIDEOMODE CFileMgr::Write(fileHandle, (char*)&m_nPrefsWidth, sizeof(m_nPrefsWidth)); CFileMgr::Write(fileHandle, (char*)&m_nPrefsHeight, sizeof(m_nPrefsHeight)); CFileMgr::Write(fileHandle, (char*)&m_nPrefsDepth, sizeof(m_nPrefsDepth)); CFileMgr::Write(fileHandle, (char*)&m_nPrefsWindowed, sizeof(m_nPrefsWindowed)); CFileMgr::Write(fileHandle, (char*)&m_nPrefsSubsystem, sizeof(m_nPrefsSubsystem)); #else CFileMgr::Write(fileHandle, RubbishString, 20); #endif CFileMgr::Write(fileHandle, RubbishString, 20); CFileMgr::Write(fileHandle, RubbishString, 4); CFileMgr::Write(fileHandle, RubbishString, 4); CFileMgr::Write(fileHandle, RubbishString, 1); #ifdef LEGACY_MENU_OPTIONS CFileMgr::Write(fileHandle, (char*)&m_PrefsVsyncDisp, 1); CFileMgr::Write(fileHandle, (char*)&CMBlur::BlurOn, 1); #else CFileMgr::Write(fileHandle, RubbishString, 1); CFileMgr::Write(fileHandle, RubbishString, 1); #endif CFileMgr::Write(fileHandle, (char*)&TheCamera.m_bHeadBob, 1); CFileMgr::Write(fileHandle, (char*)&TheCamera.m_fMouseAccelHorzntl, 4); CFileMgr::Write(fileHandle, (char*)&MousePointerStateHelper.bInvertVertically, 1); CFileMgr::Write(fileHandle, (char*)&CVehicle::m_bDisableMouseSteering, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsSfxVolume, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsMusicVolume, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsMP3BoostVolume, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsRadioStation, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsSpeakers, 1); CFileMgr::Write(fileHandle, (char*)&m_nPrefsAudio3DProviderIndex, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsDMA, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsBrightness, 2); CFileMgr::Write(fileHandle, (char*)&m_PrefsLOD, sizeof(m_PrefsLOD)); CFileMgr::Write(fileHandle, (char*)&m_PrefsShowSubtitles, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsUseWideScreen, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsFrameLimiter, 1); CFileMgr::Write(fileHandle, (char*)&m_nPrefsVideoMode, 1); CFileMgr::Write(fileHandle, m_PrefsSkinFile, 256); CFileMgr::Write(fileHandle, (char*)&m_ControlMethod, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsLanguage, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsShowHud, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsRadarMode, 1); CFileMgr::Write(fileHandle, (char*)&m_PrefsShowLegends, 1); } m_lastWorking3DAudioProvider = m_nPrefsAudio3DProviderIndex; CFileMgr::CloseFile(fileHandle); CFileMgr::SetDir(""); #else m_lastWorking3DAudioProvider = m_nPrefsAudio3DProviderIndex; SaveINISettings(); #endif } void CMenuManager::MessageScreen(const char *text, bool blackBg) { CSprite2d *splash = LoadSplash(nil); if (!DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)) return; CSprite2d::SetRecipNearClip(); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); DefinedState(); // CRGBA unused(255, 255, 255, 255); if (blackBg) { CSprite2d::DrawRect(CRect(0, SCREEN_HEIGHT, SCREEN_WIDTH, 0), CRGBA(0, 0, 0, 255)); } m_nMenuFadeAlpha = 255; SmallMessageScreen(text); CFont::DrawFonts(); DoRWStuffEndOfFrame(); } void CMenuManager::SmallMessageScreen(const char* text) { CFont::SetBackgroundOff(); CFont::SetPropOn(); CFont::SetJustifyOn(); CFont::SetBackGroundOnlyTextOn(); CSprite2d::DrawRect(CRect(SCREEN_STRETCH_X(95.0f), SCREEN_SCALE_FROM_BOTTOM(165.0f), SCREEN_STRETCH_FROM_RIGHT(95.0f), SCREEN_SCALE_Y(115.0f)), CRGBA(50, 50, 50, FadeIn(210))); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetCentreSize(SCREEN_SCALE_X(430.0f)); CFont::SetCentreOn(); CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, FadeIn(255))); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetScale(SCREEN_SCALE_X(SMALLTEXT_X_SCALE), SCREEN_SCALE_Y(SMALLTEXT_Y_SCALE)); int numOfLines = CFont::GetNumberLines(SCREEN_WIDTH / 2.f, SCREEN_SCALE_Y(135.f), TheText.Get(text)); float y; if (numOfLines > 1) y = SCREEN_SCALE_Y(192.f) - numOfLines * SCREEN_SCALE_Y(8.f); else y = SCREEN_SCALE_Y(182.f); CFont::PrintString(SCREEN_WIDTH / 2.f, y, TheText.Get(text)); } void CMenuManager::PrintBriefs() { CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetRightJustifyOff(); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetScale(MENU_X(MEDIUMTEXT_X_SCALE), MENU_Y(MEDIUMTEXT_Y_SCALE)); CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(80.0f)); CFont::SetDropShadowPosition(0); float nextY = BRIEFS_BOTTOM_MARGIN; for (int i = 4; i >= 0; i--) { if (nextY < BRIEFS_TOP_MARGIN) break; tPreviousBrief &brief = CMessages::PreviousBriefs[i]; if (brief.m_pText) { CMessages::InsertNumberInString(brief.m_pText, brief.m_nNumber[0], brief.m_nNumber[1], brief.m_nNumber[2], brief.m_nNumber[3], brief.m_nNumber[4], brief.m_nNumber[5], gUString); CMessages::InsertStringInString(gUString, brief.m_pString); CMessages::InsertPlayerControlKeysInString(gUString); CFont::FilterOutTokensFromString(gUString); nextY -= CFont::GetNumberLines(MENU_X_LEFT_ALIGNED(BRIEFS_LINE_X), nextY, gUString) * BRIEFS_LINE_HEIGHT + BRIEFS_LINE_SPACING; CFont::PrintString(MENU_X_LEFT_ALIGNED(BRIEFS_LINE_X), MENU_Y(nextY), gUString); } } } void CMenuManager::PrintStats() { #ifdef SECUROM static uint8 statsPirateCheck = 0; #endif static float scrollY = 0; int rowNum = CStats::ConstructStatLine(99999); CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(90.0f), MENU_Y(142.0f), MENU_X_LEFT_ALIGNED(543.0f), MENU_Y(142.f), MENU_X_LEFT_ALIGNED(107.0f), MENU_Y(316.f), MENU_X_LEFT_ALIGNED(531.f), MENU_Y(299.f), CRGBA(LIST_BACKGROUND_COLOR.r, LIST_BACKGROUND_COLOR.g, LIST_BACKGROUND_COLOR.b, FadeIn(LIST_BACKGROUND_COLOR.a))); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetPropOn(); CFont::SetDropShadowPosition(0); #ifdef SECUROM if (statsPirateCheck == 0) // if not pirated game // statsPirateCheck = 46; // else statsPirateCheck = 45; #endif if (m_PrefsLanguage == LANGUAGE_AMERICAN) CFont::SetScale(MENU_X(0.43f), MENU_Y(0.75f)); else CFont::SetScale(MENU_X(0.37f), MENU_Y(0.75f)); static PauseModeTime lastCheck = 0; if (CTimer::GetTimeInMillisecondsPauseMode() - lastCheck > 40) { if (m_StatsScrollSpeed > 0.f) { if (m_StatsScrollDirection == 0) scrollY -= MENU_Y(100.f) / m_StatsScrollSpeed; else scrollY += MENU_Y(100.f) / m_StatsScrollSpeed; } lastCheck = CTimer::GetTimeInMillisecondsPauseMode(); } #ifdef SECUROM if (statsPirateCheck == 45) return; #endif float nextYChange, y, alpha; float totalHeight = (rowNum + 7) * STATS_ROW_HEIGHT; for (int row = 0; row < rowNum; ++row) { // Put faded away text at the top back to the bottom, in circular fashion for (y = MENU_Y(STATS_ROW_HEIGHT) * row + MENU_Y(100.f) - scrollY; MENU_Y(STATS_FADING_AREA_LENGTH) > y; y += nextYChange) { nextYChange = MENU_Y(totalHeight); } // Put faded away text at the bottom back to the top while (SCREEN_SCALE_FROM_BOTTOM(STATS_FADING_AREA_LENGTH) < y) { y -= MENU_Y(totalHeight); } alpha = 0.f; // If it's still on screen if (y > MENU_Y(STATS_VISIBLE_START_Y) && y < MENU_Y(STATS_VISIBLE_END_Y)) { CStats::ConstructStatLine(row); // But about to dim from bottom if (y < MENU_Y(STATS_BOTTOM_Y)) { if (y > MENU_Y(STATS_BOTTOM_Y - STATS_FADING_AREA_LENGTH)) alpha = (MENU_Y(STATS_BOTTOM_Y) - y) * 5.f; } // About to dim from top if (y > MENU_Y(STATS_TOP_Y)) { if (y < MENU_Y(STATS_TOP_Y + STATS_FADING_AREA_LENGTH)) alpha = (y - MENU_Y(STATS_TOP_Y)) * 5.f; } // Content if (y >= MENU_Y(STATS_TOP_Y + STATS_FADING_AREA_LENGTH) && y <= MENU_Y(STATS_BOTTOM_Y - STATS_FADING_AREA_LENGTH)) alpha = 255.0f; CFont::SetColor(CRGBA(0, 0, 0, FadeIn(Min(255.f, alpha)))); CFont::SetRightJustifyOff(); CFont::PrintString(MENU_X_LEFT_ALIGNED(STATS_ROW_LEFT_MARGIN), y, gUString); CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_RIGHT_ALIGNED(STATS_ROW_RIGHT_MARGIN), y, gUString2); } } CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetCentreOn(); CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(MENU_X(0.65f), MENU_Y(1.05f)); CFont::PrintString(MENU_X_LEFT_ALIGNED(STATS_RATING_X), MENU_Y(STATS_RATING_Y_1), TheText.Get("CRIMRA")); CFont::SetCentreOff(); CFont::SetRightJustifyOff(); // FIX: Game does that in a weird way, alignment and spacing is now ok sprintf(gString, "(%d)", CStats::FindCriminalRatingNumber()); AsciiToUnicode(gString, gUString); UnicodeStrcpy(gUString2, CStats::FindCriminalRatingString()); UnicodeStrcat(gUString2, gUString); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); #ifndef FIX_BUGS CFont::SetScale(MENU_X(0.5f), MENU_Y(0.9f)); #else CFont::SetScale(MENU_X(SMALLTEXT_X_SCALE), MENU_Y(SMALLTEXT_Y_SCALE)); #endif CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetDropShadowPosition(0); CFont::PrintString(MENU_X_LEFT_ALIGNED(STATS_RATING_X) - CFont::GetStringWidth(gUString2, true) / 2.f, MENU_Y(STATS_RATING_Y_2), gUString2); } void CMenuManager::Process(void) { #ifdef XBOX_MESSAGE_SCREEN ProcessDialogTimer(); #endif if (TheCamera.GetScreenFadeStatus() != FADE_0) return; InitialiseChangedLanguageSettings(); if (m_bMenuActive) { UserInput(); ProcessFileActions(); DMAudio.Service(); #ifdef USE_TEXTURE_POOL // TODO #endif } SwitchMenuOnAndOff(); } #ifdef MAP_ENHANCEMENTS #define ZOOM(x, y, in) \ do { \ if(m_fMapSize >= MENU_Y(1000.0f) && in) \ break; \ float z2 = in? 1.1f : 1.f/1.1f; \ m_fMapCenterX += (x - m_fMapCenterX) * (1.0f - z2); \ m_fMapCenterY += (y - m_fMapCenterY) * (1.0f - z2); \ \ if (m_fMapSize <= MENU_Y(MAP_MIN_SIZE) && !in) \ break; \ \ m_fMapSize *= z2; \ m_fMapCenterX = clamp(m_fMapCenterX, SCREEN_WIDTH/2 - (m_fMapSize - MENU_X(MAP_MIN_SIZE)), m_fMapSize - MENU_X(MAP_MIN_SIZE) + SCREEN_WIDTH/2); \ m_fMapCenterY = clamp(m_fMapCenterY, SCREEN_HEIGHT/2 - (m_fMapSize - MENU_Y(MAP_MIN_SIZE)), m_fMapSize - MENU_Y(MAP_MIN_SIZE) + SCREEN_HEIGHT/2); \ } while(0) #endif // Handles Map, Audio and Stats void CMenuManager::AdditionalOptionInput(bool &goBack) { switch (m_nCurrScreen) { case MENUPAGE_MAP: { static PauseModeTime lastMapTick = 0; // FIX: All those macros were hardcoded values originally. #ifndef MAP_ENHANCEMENTS if (CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelUpJustUp() || CPad::GetPad(0)->GetPageUp() || CPad::GetPad(0)->GetRightShoulder1()) { if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { m_fMapSize = Min(MENU_Y(1000.0f), m_fMapSize + MENU_Y(15.f)); } } if (CPad::GetPad(0)->GetMouseWheelDownJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustUp() || CPad::GetPad(0)->GetPageDown() || CPad::GetPad(0)->GetRightShoulder2()) { if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { if (m_fMapSize > MENU_Y(MAP_MIN_SIZE)) { if (m_fMapCenterY > SCREEN_HEIGHT/2) m_fMapCenterY -= (m_fMapCenterY - SCREEN_HEIGHT/2) / ((m_fMapSize - MENU_Y(MAP_MIN_SIZE)) * 1/15.f); if (m_fMapCenterY < SCREEN_HEIGHT/2) m_fMapCenterY += (SCREEN_HEIGHT/2 - m_fMapCenterY) / ((m_fMapSize - MENU_Y(MAP_MIN_SIZE)) * 1/15.f); if (m_fMapCenterX > SCREEN_WIDTH/2) m_fMapCenterX -= (m_fMapCenterX - SCREEN_WIDTH/2) / ((m_fMapSize - MENU_X(MAP_MIN_SIZE)) * 1/15.f); if (m_fMapCenterX < SCREEN_WIDTH/2) m_fMapCenterX += (SCREEN_WIDTH/2 - m_fMapCenterX) / ((m_fMapSize - MENU_X(MAP_MIN_SIZE)) * 1/15.f); m_fMapSize = Max(MENU_Y(MAP_MIN_SIZE), m_fMapSize - MENU_Y(15.f)); m_fMapCenterX = clamp(m_fMapCenterX, SCREEN_WIDTH/2 - (m_fMapSize - MENU_X(MAP_MIN_SIZE)), m_fMapSize - MENU_X(MAP_MIN_SIZE) + SCREEN_WIDTH/2); m_fMapCenterY = clamp(m_fMapCenterY, SCREEN_HEIGHT/2 - (m_fMapSize - MENU_Y(MAP_MIN_SIZE)), m_fMapSize - MENU_Y(MAP_MIN_SIZE) + SCREEN_HEIGHT/2); } else { m_fMapSize = MENU_Y(MAP_MIN_SIZE); } } } #else // Adding marker if (m_nMenuFadeAlpha == 255) { if (CPad::GetPad(0)->GetRightMouseJustDown() || CPad::GetPad(0)->GetCrossJustDown()) { if (mapCrosshair.y > m_fMapCenterY - m_fMapSize && mapCrosshair.y < m_fMapCenterY + m_fMapSize && mapCrosshair.x > m_fMapCenterX - m_fMapSize && mapCrosshair.x < m_fMapCenterX + m_fMapSize) { // Don't ask me the meanings, I don't know. Found them by trying float diffX = m_fMapCenterX - m_fMapSize, diffY = m_fMapCenterY - m_fMapSize; float x = ((mapCrosshair.x - diffX) / (m_fMapSize * 2)) * (WORLD_SIZE_X / MENU_MAP_WIDTH_SCALE) - (WORLD_SIZE_X / 2 + MENU_MAP_LEFT_OFFSET * MENU_MAP_LENGTH_UNIT); float y = (WORLD_SIZE_Y / 2 - MENU_MAP_TOP_OFFSET * MENU_MAP_LENGTH_UNIT) - ((mapCrosshair.y - diffY) / (m_fMapSize * 2)) * (WORLD_SIZE_Y / MENU_MAP_HEIGHT_SCALE); CRadar::ToggleTargetMarker(x, y); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); } } } if (CPad::GetPad(0)->GetMouseWheelDown() || CPad::GetPad(0)->GetPageDown() || CPad::GetPad(0)->GetRightShoulder2()) { if (CPad::GetPad(0)->GetMouseWheelDown() && m_fMapSize > MENU_X(MAP_SIZE_TO_ALLOW_X_MOVE)) ZOOM(mapCrosshair.x, mapCrosshair.y, false); else ZOOM(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, false); } else if (CPad::GetPad(0)->GetMouseWheelUp() || CPad::GetPad(0)->GetPageUp() || CPad::GetPad(0)->GetRightShoulder1()) { if (CPad::GetPad(0)->GetMouseWheelUp()) ZOOM(mapCrosshair.x, mapCrosshair.y, true); else ZOOM(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, true); } static bool justResetPointer = false; if (CPad::GetPad(0)->GetLeftMouse()) { if (!justResetPointer) { m_fMapCenterX += m_nMousePosX - m_nMouseOldPosX; m_fMapCenterY += m_nMousePosY - m_nMouseOldPosY; m_fMapCenterX = clamp(m_fMapCenterX, SCREEN_WIDTH/2 - (m_fMapSize - MENU_X(MAP_MIN_SIZE)), m_fMapSize - MENU_X(MAP_MIN_SIZE) + SCREEN_WIDTH/2); m_fMapCenterY = clamp(m_fMapCenterY, SCREEN_HEIGHT/2 - (m_fMapSize - MENU_Y(MAP_MIN_SIZE)), m_fMapSize - MENU_Y(MAP_MIN_SIZE) + SCREEN_HEIGHT/2); } justResetPointer = false; } else #undef ZOOM #endif { // This is else block of GetLeftMouse() if MAP_ENHANCEMENTS defined, so all of GetLeftMouse() conditions below being rendered useless. if (CPad::GetPad(0)->GetLeftMouse() && m_nMousePosY < m_nMouseOldPosY || CPad::GetPad(0)->GetUp() || CPad::GetPad(0)->GetDPadUp() || CPad::GetPad(0)->GetAnalogueUpDown() < 0) { if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { if ((m_fMapSize - MENU_Y(MAP_MIN_SIZE)) + SCREEN_HEIGHT/2 > m_fMapCenterY) m_fMapCenterY += MENU_Y(15.f); m_bShowMouse = false; } } if (CPad::GetPad(0)->GetLeftMouse() && m_nMousePosY > m_nMouseOldPosY || CPad::GetPad(0)->GetDown() || CPad::GetPad(0)->GetDPadDown() || CPad::GetPad(0)->GetAnalogueUpDown() > 0) { if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { if (SCREEN_HEIGHT/2 - (m_fMapSize - MENU_Y(MAP_MIN_SIZE)) < m_fMapCenterY) m_fMapCenterY -= MENU_Y(15.f); m_bShowMouse = false; } } if (CPad::GetPad(0)->GetLeftMouse() && m_nMousePosX < m_nMouseOldPosX || CPad::GetPad(0)->GetLeft() || CPad::GetPad(0)->GetDPadLeft() || CPad::GetPad(0)->GetAnalogueLeftRight() < 0) { if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { if (m_fMapSize > MENU_X(MAP_SIZE_TO_ALLOW_X_MOVE) && m_fMapSize - MENU_X(MAP_MIN_SIZE) + SCREEN_WIDTH/2 > m_fMapCenterX) m_fMapCenterX += MENU_X(15.f); m_bShowMouse = false; } } if (CPad::GetPad(0)->GetLeftMouseJustUp()) { // The coordinates in aScreens->MENUPAGE_MAP. if (m_nMousePosX > MENU_X_LEFT_ALIGNED(60.0f) && m_nMousePosX < MENU_X_LEFT_ALIGNED(140.0f)) { if (m_nMousePosY > MENU_Y(375.0f) && m_nMousePosY < MENU_Y(400.0f)) { m_nHoverOption = HOVEROPTION_RANDOM_ITEM; goBack = true; } } } if (CPad::GetPad(0)->GetLeftMouse() && m_nMousePosX > m_nMouseOldPosX || CPad::GetPad(0)->GetRight() || CPad::GetPad(0)->GetDPadRight() || CPad::GetPad(0)->GetAnalogueLeftRight() > 0) { if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) { if (m_fMapSize > MENU_X(MAP_SIZE_TO_ALLOW_X_MOVE) && SCREEN_WIDTH/2 - (m_fMapSize - MENU_X(MAP_MIN_SIZE)) < m_fMapCenterX) m_fMapCenterX -= MENU_X(15.f); m_bShowMouse = false; } } } if (CTimer::GetTimeInMillisecondsPauseMode() - lastMapTick > 10) lastMapTick = CTimer::GetTimeInMillisecondsPauseMode(); #ifndef MAP_ENHANCEMENTS if (CPad::GetPad(0)->GetLeftMouseJustUp()) CentreMousePointer(); #endif if (CPad::GetPad(0)->GetLeftMouse()) { if (m_nMousePosX < SCREEN_STRETCH_X(20.0f) || m_nMousePosX > SCREEN_STRETCH_X(620.0f) || m_nMousePosY < SCREEN_STRETCH_Y(20.0f) || m_nMousePosY > SCREEN_STRETCH_Y(428.0f)) { #ifdef MAP_ENHANCEMENTS justResetPointer = true; #endif CentreMousePointer(); } } if (!CPad::GetPad(0)->GetLeftMouse() && !m_bShowMouse && (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY)) { m_bShowMouse = true; } static bool pressedL = false; if (!CPad::GetPad(0)->GetChar('L') && !CPad::GetPad(0)->GetChar('l')) { pressedL = false; } if (!pressedL) { if (CPad::GetPad(0)->GetChar('L') || CPad::GetPad(0)->GetChar('l')) { m_PrefsShowLegends = !m_PrefsShowLegends; pressedL = true; } } break; } case MENUPAGE_SOUND_SETTINGS: if (CheckHover(MENU_X_LEFT_ALIGNED(177.f), MENU_X_LEFT_ALIGNED(238.f), MENU_Y(MENURADIO_SELECTOR_START_Y - 13.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT - 8.f))) { m_nHoverOption = HOVEROPTION_PREV_RADIO; } if (CheckHover(MENU_X_LEFT_ALIGNED(422.f), MENU_X_LEFT_ALIGNED(491.f), MENU_Y(MENURADIO_SELECTOR_START_Y - 13.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT - 8.f))) { m_nHoverOption = HOVEROPTION_NEXT_RADIO; } break; case MENUPAGE_STATS: { if (CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelUpJustUp() || CPad::GetPad(0)->GetUp() || CPad::GetPad(0)->GetDPadUp() || CPad::GetPad(0)->GetAnalogueUpDown() < 0) { m_StatsScrollSpeed = 20.0f; m_StatsScrollDirection = 0; } else if (CPad::GetPad(0)->GetMouseWheelDownJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustUp() || CPad::GetPad(0)->GetDown() || CPad::GetPad(0)->GetDPadDown() || CPad::GetPad(0)->GetAnalogueUpDown() > 0) { m_StatsScrollSpeed = 20.0f; m_StatsScrollDirection = 1; } else if (CPad::GetPad(0)->GetChar(' ')) { m_StatsScrollSpeed = 0.0f; } else m_StatsScrollSpeed = 150.0f; static bool pressedS = false; if (!CPad::GetPad(0)->GetChar('S') && !CPad::GetPad(0)->GetChar('s')) { pressedS = false; } if (!pressedS) { if (CPad::GetPad(0)->GetChar('S') || CPad::GetPad(0)->GetChar('s')) { ExportStats(); m_nHelperTextMsgId = 4; m_nHelperTextAlpha = 300; pressedS = true; } } break; } } } // Not original name void CMenuManager::ExportStats() { char date[10]; CFileMgr::SetDirMyDocuments(); _strdate(date); wchar *lastMission = TheText.Get(CStats::LastMissionPassedName[0] == '\0' ? "ITBEG" : CStats::LastMissionPassedName); FILE *txtFile = fopen("stats.txt", "w"); if (txtFile) { int statLines = CStats::ConstructStatLine(99999); fprintf(txtFile, "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"); fprintf(txtFile, "\t\t\tGTA VICE CITY %s\n", UnicodeToAscii(TheText.Get("FEH_STA"))); fprintf(txtFile, "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\n"); fprintf(txtFile, "%s: ", UnicodeToAscii(TheText.Get("FES_CMI"))); fprintf(txtFile, "%s\n", UnicodeToAscii(lastMission)); fprintf(txtFile, "%s: ", UnicodeToAscii(TheText.Get("FES_DAT"))); fprintf(txtFile, "%s\n\n\n", date); fprintf(txtFile, "%s ", UnicodeToAscii(TheText.Get("CRIMRA"))); UnicodeStrcpy(gUString, CStats::FindCriminalRatingString()); fprintf(txtFile, "%s (%d)\n\n\n", UnicodeToAscii(gUString), CStats::FindCriminalRatingNumber()); for (int i = 0; i < statLines; ++i) { CStats::ConstructStatLine(i); char *statKey = UnicodeToAscii(gUString); if (statKey[0] != '\0') fprintf(txtFile, "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n%s\n", statKey); char *statValue = UnicodeToAscii(gUString2); for (int j = 0; statValue[j] != '\0'; ++j) { if (statValue[j] == '_') statValue[j] = '\xBA'; // This is degree symbol, but my editors keeps messing up with it so I wrote hex representation } if (statValue) fprintf(txtFile, "%s\n\n", statValue); } fprintf(txtFile, "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n"); } fclose(txtFile); FILE *htmlFile = fopen("stats.html", "w"); if (htmlFile) { int statLines = CStats::ConstructStatLine(99999); fprintf(htmlFile, "Grand Theft Auto Vice City Stats\n"); fprintf(htmlFile, "\n"); fprintf(htmlFile, "\n"); fprintf(htmlFile, " \n"); fprintf(htmlFile, "\n"); fprintf(htmlFile, "\n"); fprintf(htmlFile, " \n"); fprintf(htmlFile, "\n", UnicodeToAscii(lastMission)); fprintf(htmlFile, " \n"); fprintf(htmlFile, " \n"); fprintf(htmlFile, "\n", statKey, rating); for (int k = 0; k < statLines; ++k) { CStats::ConstructStatLine(k); statKey = UnicodeToAscii(gUString); if (statKey[0] != '\0') fprintf(htmlFile, "\n"); fprintf(htmlFile, "\n"); fprintf(htmlFile, "
-------------------------------------------------------------------------
\n"); fprintf(htmlFile, "GRAND THEFT AUTO VICE CITY "); fprintf(htmlFile, "%s
-------------------------------------------------------------------------
 

" "%s: \n", UnicodeToAscii(TheText.Get("FES_DAT"))); fprintf(htmlFile, "%s
%s:
", date, UnicodeToAscii(TheText.Get("FES_CMI"))); fprintf(htmlFile, "%s

%s\n", UnicodeToAscii(TheText.Get("CRIMRA"))); UnicodeStrcpy(gUString, CStats::FindCriminalRatingString()); char *statKey = UnicodeToAscii(gUString); int rating = CStats::FindCriminalRatingNumber(); fprintf(htmlFile, "%s (%d)
\n"); if (statKey[0] != '\0') fprintf(htmlFile, "%s", statKey); else fprintf(htmlFile, " "); fprintf(htmlFile, "
\n"); char *statValue = UnicodeToAscii(gUString2); for (int l = 0; statValue[l] != '\0'; ++l) { if (statValue[l] == '_') statValue[l] = '\xBA'; // This is degree symbol, but my editors keeps messing up with it so I wrote hex representation } if (statValue) fprintf(htmlFile, "%s", statValue); else fprintf(htmlFile, " "); } fprintf(htmlFile, "

"); fprintf(htmlFile, "\n"); fprintf(htmlFile, "\n"); fprintf(htmlFile, "\n"); fprintf(htmlFile, "
rockstargames.com/vicecityrockstargames.com rockstarnorth.com
\n\n"); } fclose(htmlFile); CFileMgr::SetDir(""); } // Original name is unknown void CMenuManager::PrintRadioSelector(void) { static PauseModeTime lastRadioChange = 0; CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(418.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), MENU_X_LEFT_ALIGNED(228.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), MENU_X_LEFT_ALIGNED(428.f), MENU_Y(MENURADIO_SELECTOR_START_Y), MENU_X_LEFT_ALIGNED(238.f), MENU_Y(MENURADIO_SELECTOR_START_Y), CRGBA(RADIO_SELECTOR_COLOR.r, RADIO_SELECTOR_COLOR.g, RADIO_SELECTOR_COLOR.b, FadeIn(180))); int rightMostSprite, rightMostStation; if (DMAudio.IsMP3RadioChannelAvailable()) { rightMostSprite = MENUSPRITE_MP3; rightMostStation = USERTRACK; } else { rightMostSprite = MENUSPRITE_WAVE; rightMostStation = WAVE; } #ifdef THIS_IS_STUPID // First radio if (m_ScrollRadioBy == 1) { if (m_PrefsRadioStation == 1) { m_aFrontEndSprites[rightMostSprite].Draw(m_LeftMostRadioX, MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else if ( m_PrefsRadioStation == 0) { m_aFrontEndSprites[rightMostSprite - 1].Draw(m_LeftMostRadioX, MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else { m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE - 2].Draw(m_LeftMostRadioX, MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } } // Second if (m_PrefsRadioStation == 0) { m_aFrontEndSprites[rightMostSprite].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else { m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE - 1].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } // Third (middle) int prevStation = m_PrefsRadioStation - 1; if (prevStation == rightMostStation) { m_aFrontEndSprites[MENUSPRITE_WILDSTYLE + 1].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 3), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else if ( prevStation == rightMostStation - 1) { m_aFrontEndSprites[MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 3), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else { m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE + 1].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 3), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } // Fifth if (m_ScrollRadioBy == -1) { int prevStation = m_PrefsRadioStation - 1; if (prevStation == rightMostStation) { m_aFrontEndSprites[MENUSPRITE_WILDSTYLE + 4].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else if (prevStation == rightMostStation - 1) { m_aFrontEndSprites[MENUSPRITE_WILDSTYLE + 1].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else if ( prevStation == rightMostStation - 2) { m_aFrontEndSprites[MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else { m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE + 2].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } } // Fourth if (m_ScrollRadioBy == 0) { m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2 - 10.f), MENU_Y(MENURADIO_ICON_Y - 10.f), MENU_X(MENURADIO_ICON_SIZE) + MENU_X(20.f), MENU_Y(MENURADIO_ICON_SIZE) + MENU_Y(20.f), CRGBA(255, 255, 255, FadeIn(255))); } else { if (m_PrefsRadioStation - 1 == rightMostStation) { m_aFrontEndSprites[MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else { m_aFrontEndSprites[m_PrefsRadioStation + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } } #else int first = ((m_PrefsRadioStation - 2) + rightMostStation + 1) % (rightMostStation + 1); int second = ((m_PrefsRadioStation - 1) + rightMostStation + 1) % (rightMostStation + 1); int third = ((m_PrefsRadioStation) + rightMostStation + 1) % (rightMostStation + 1); int fourth = ((m_PrefsRadioStation + 1) + rightMostStation + 1) % (rightMostStation + 1); int fifth = ((m_PrefsRadioStation + 2) + rightMostStation + 1) % (rightMostStation + 1); // First one is only drawn on transition to next if (m_ScrollRadioBy == 1) { m_aFrontEndSprites[first + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX, MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } // Second m_aFrontEndSprites[second + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); // Fourth m_aFrontEndSprites[fourth + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 3), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); // Fifth one is only drawn on transition to prev. if (m_ScrollRadioBy == -1) { m_aFrontEndSprites[fifth + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 4), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } // Middle one(third) is colored differently depending on if it's in transition. // If not in transition then this icon indicates selected radio, and should be on top of all icons. thus drawn last if (m_ScrollRadioBy != 0) { m_aFrontEndSprites[third + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2), MENU_Y(MENURADIO_ICON_Y), MENU_X(MENURADIO_ICON_SIZE), MENU_Y(MENURADIO_ICON_SIZE), CRGBA(INACTIVE_RADIO_COLOR.r, INACTIVE_RADIO_COLOR.g, INACTIVE_RADIO_COLOR.b, FadeIn(INACTIVE_RADIO_COLOR.a))); } else { m_aFrontEndSprites[third + MENUSPRITE_WILDSTYLE].Draw(m_LeftMostRadioX + MENU_X(MENURADIO_ICON_SIZE * 2 - 10.f), MENU_Y(MENURADIO_ICON_Y - 10.f), MENU_X(MENURADIO_ICON_SIZE) + MENU_X(20.f), MENU_Y(MENURADIO_ICON_SIZE) + MENU_Y(20.f), CRGBA(255, 255, 255, FadeIn(255))); } #endif static bool radioChangeRequested = false; static PauseModeTime lastScrollCheck = 0; if (CTimer::GetTimeInMillisecondsPauseMode() - lastScrollCheck > 17) { if (m_ScrollRadioBy == 1) { if (m_LeftMostRadioX > MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X - MENURADIO_ICON_SIZE)) { m_LeftMostRadioX -= MENU_X(6.f); } else { m_ScrollRadioBy = 0; lastRadioChange = CTimer::GetTimeInMillisecondsPauseMode(); radioChangeRequested = true; } } if (m_ScrollRadioBy == -1) { if (m_LeftMostRadioX < MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X - MENURADIO_ICON_SIZE)) { m_LeftMostRadioX += MENU_X(6.f); } else { m_ScrollRadioBy = 0; lastRadioChange = CTimer::GetTimeInMillisecondsPauseMode(); radioChangeRequested = true; } } lastScrollCheck = CTimer::GetTimeInMillisecondsPauseMode(); } // Background behind arrows CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(228.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), MENU_X_LEFT_ALIGNED(168.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), MENU_X_LEFT_ALIGNED(238.f), MENU_Y(MENURADIO_SELECTOR_START_Y), MENU_X_LEFT_ALIGNED(178.f), MENU_Y(MENURADIO_SELECTOR_START_Y), CRGBA(RADIO_SELECTOR_COLOR.r, RADIO_SELECTOR_COLOR.g, RADIO_SELECTOR_COLOR.b, FadeIn(255))); CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(478.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), MENU_X_LEFT_ALIGNED(418.f), MENU_Y(MENURADIO_SELECTOR_START_Y + MENURADIO_SELECTOR_HEIGHT), MENU_X_LEFT_ALIGNED(488.f), MENU_Y(MENURADIO_SELECTOR_START_Y), MENU_X_LEFT_ALIGNED(428.f), MENU_Y(MENURADIO_SELECTOR_START_Y), CRGBA(RADIO_SELECTOR_COLOR.r, RADIO_SELECTOR_COLOR.g, RADIO_SELECTOR_COLOR.b, FadeIn(255))); // Arrows and their shadows CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(216.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 48.f), MENU_X_LEFT_ALIGNED(196.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 36.f), MENU_X_LEFT_ALIGNED(216.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 22.f), MENU_X_LEFT_ALIGNED(196.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 36.f), CRGBA(0, 0, 0, FadeIn(255))); CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(213.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 45.f), MENU_X_LEFT_ALIGNED(193.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 33.f), MENU_X_LEFT_ALIGNED(213.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 19.f), MENU_X_LEFT_ALIGNED(193.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 33.f), CRGBA(97, 194, 247, FadeIn(255))); CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(440.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 48.f), MENU_X_LEFT_ALIGNED(460.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 36.f), MENU_X_LEFT_ALIGNED(440.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 22.f), MENU_X_LEFT_ALIGNED(460.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 36.f), CRGBA(0, 0, 0, FadeIn(255))); CSprite2d::Draw2DPolygon(MENU_X_LEFT_ALIGNED(443.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 45.f), MENU_X_LEFT_ALIGNED(463.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 33.f), MENU_X_LEFT_ALIGNED(443.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 19.f), MENU_X_LEFT_ALIGNED(463.f), MENU_Y(MENURADIO_SELECTOR_START_Y + 33.f), CRGBA(97, 194, 247, FadeIn(255))); if (radioChangeRequested) { if (CTimer::GetTimeInMillisecondsPauseMode() - lastRadioChange > 50) { DMAudio.SetRadioInCar(m_PrefsRadioStation); DMAudio.PlayFrontEndTrack(m_PrefsRadioStation, 1); OutputDebugString("FRONTEND RADIO STATION CHANGED"); lastRadioChange = CTimer::GetTimeInMillisecondsPauseMode(); radioChangeRequested = false; } } } // Original name is unknown void CMenuManager::ProcessList(bool &optionSelected, bool &goBack) { if (m_bWaitingForNewKeyBind) return; if (m_nCurrScreen == MENUPAGE_SKIN_SELECT) { m_nTotalListRow = m_nSkinsTotal; } if (m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) { // GetNumOptionsCntrlConfigScreens would have been a better choice m_nTotalListRow = m_ControlMethod == CONTROL_CLASSIC ? 32 : 27; if (m_nSelectedListRow > m_nTotalListRow) m_nSelectedListRow = m_nTotalListRow - 1; } if (CPad::GetPad(0)->GetEnterJustDown() || CPad::GetPad(0)->GetCrossJustDown()) { m_bShowMouse = 0; optionSelected = true; } if (CPad::GetPad(0)->GetBackspaceJustDown() && m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS && !field_159) { if (m_nCurrExLayer == HOVEROPTION_LIST) { m_nHoverOption = HOVEROPTION_NOT_HOVERING; m_bWaitingForNewKeyBind = true; m_bStartWaitingForKeyBind = true; m_bKeyChangeNotProcessed = true; pControlEdit = &m_KeyPressedCode; } } else { field_159 = false; } static PauseModeTime lastTimeClickedScrollButton = 0; if (CTimer::GetTimeInMillisecondsPauseMode() - lastTimeClickedScrollButton >= 200) { m_bPressedPgUpOnList = false; m_bPressedPgDnOnList = false; m_bPressedUpOnList = false; m_bPressedDownOnList = false; m_bPressedScrollButton = false; lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); } if (CPad::GetPad(0)->GetTabJustDown()) { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); m_bShowMouse = false; switch (m_nCurrExLayer) { case HOVEROPTION_BACK: default: m_nCurrExLayer = HOVEROPTION_LIST; break; case HOVEROPTION_LIST: m_nCurrExLayer = HOVEROPTION_USESKIN; break; case HOVEROPTION_USESKIN: m_nCurrExLayer = HOVEROPTION_BACK; } if (((m_nCurrScreen == MENUPAGE_SKIN_SELECT) && (m_nCurrExLayer == HOVEROPTION_USESKIN)) && strcmp(m_aSkinName, m_PrefsSkinFile) == 0) { m_nCurrExLayer = HOVEROPTION_BACK; } if ((m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) && (m_nCurrExLayer == HOVEROPTION_USESKIN)) { m_nCurrExLayer = HOVEROPTION_BACK; } } bool pressed = false; if (CPad::GetPad(0)->GetUp() || CPad::GetPad(0)->GetAnaloguePadUp() || CPad::GetPad(0)->GetDPadUpJustDown()) { m_bShowMouse = false; pressed = true; } else if (CPad::GetPad(0)->GetMouseWheelUpJustUp()) { m_bShowMouse = true; pressed = true; } // Up if (pressed) { m_nCurrExLayer = HOVEROPTION_LIST; if (!m_bPressedUpOnList) { m_bPressedUpOnList = true; lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); ScrollUpListByOne(); } } else { m_bPressedUpOnList = false; } pressed = false; if (CPad::GetPad(0)->GetDown() || CPad::GetPad(0)->GetAnaloguePadDown() || CPad::GetPad(0)->GetDPadDownJustDown()) { m_bShowMouse = false; pressed = true; } else if (CPad::GetPad(0)->GetMouseWheelDownJustDown()) { m_bShowMouse = true; pressed = true; } // Down if (pressed) { m_nCurrExLayer = HOVEROPTION_LIST; if (!m_bPressedDownOnList) { m_bPressedDownOnList = true; lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); ScrollDownListByOne(); } } else { m_bPressedDownOnList = false; } if (m_nCurrScreen != MENUPAGE_KEYBOARD_CONTROLS) { if (!CPad::GetPad(0)->GetPageUp()) { m_bPressedPgUpOnList = false; } else { m_nCurrExLayer = HOVEROPTION_LIST; if (!m_bPressedPgUpOnList) { m_bPressedPgUpOnList = true; lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); m_bShowMouse = false; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); PageUpList(false); } } if (!CPad::GetPad(0)->GetPageDown()) { m_bPressedPgDnOnList = false; } else { m_nCurrExLayer = HOVEROPTION_LIST; if (!m_bPressedPgDnOnList) { m_bPressedPgDnOnList = true; lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); m_bShowMouse = false; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); PageDownList(false); } } if (CPad::GetPad(0)->GetHome()) { m_nCurrExLayer = HOVEROPTION_LIST; m_bShowMouse = false; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); if (m_nTotalListRow >= MAX_VISIBLE_OPTION_ON_SCREEN) { m_nFirstVisibleRowOnList = 0; } m_nSelectedListRow = 0; m_nScrollbarTopMargin = (SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) * m_nFirstVisibleRowOnList; } if (CPad::GetPad(0)->GetEnd()) { m_nCurrExLayer = HOVEROPTION_LIST; m_bShowMouse = false; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); if (m_nTotalListRow >= MAX_VISIBLE_OPTION_ON_SCREEN) { m_nFirstVisibleRowOnList = m_nTotalListRow - MAX_VISIBLE_OPTION_ON_SCREEN; } m_nSelectedListRow = m_nTotalListRow - 1; m_nScrollbarTopMargin = (SCROLLBAR_MAX_HEIGHT / m_nTotalListRow) * m_nFirstVisibleRowOnList; } } if (CPad::GetPad(0)->GetEscapeJustDown() || CPad::GetPad(0)->GetBackJustDown()) { m_bShowMouse = false; goBack = true; } if (CPad::GetPad(0)->GetLeftMouseJustDown()) { switch (m_nHoverOption) { case HOVEROPTION_BACK: goBack = true; break; case HOVEROPTION_PAGEUP: PageUpList(true); break; case HOVEROPTION_PAGEDOWN: PageDownList(true); break; case HOVEROPTION_USESKIN: if (m_nSkinsTotal > 0) { m_pSelectedSkin = m_pSkinListHead.nextSkin; strcpy(m_PrefsSkinFile, m_aSkinName); CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); SaveSettings(); } } } if (CPad::GetPad(0)->GetLeftMouseJustDown()) { switch (m_nHoverOption) { case HOVEROPTION_OVER_SCROLL_UP: m_nHoverOption = HOVEROPTION_CLICKED_SCROLL_UP; break; case HOVEROPTION_OVER_SCROLL_DOWN: m_nHoverOption = HOVEROPTION_CLICKED_SCROLL_DOWN; break; case HOVEROPTION_LIST: m_nHoverOption = HOVEROPTION_SKIN; } } else if ((CPad::GetPad(0)->GetLeftMouseJustUp()) && ((m_nHoverOption == HOVEROPTION_CLICKED_SCROLL_UP || (m_nHoverOption == HOVEROPTION_CLICKED_SCROLL_DOWN)))) { m_nHoverOption = HOVEROPTION_NOT_HOVERING; } if (!CPad::GetPad(0)->GetLeftMouse()) { holdingScrollBar = false; } else { if ((m_nHoverOption == HOVEROPTION_HOLDING_SCROLLBAR) || holdingScrollBar) { holdingScrollBar = true; // TODO: This part is a bit hard to reverse. Not much code tho assert(0 && "Holding scrollbar isn't done yet"); } else { switch (m_nHoverOption) { case HOVEROPTION_OVER_SCROLL_UP: case HOVEROPTION_CLICKED_SCROLL_UP: if (!m_bPressedScrollButton) { m_bPressedScrollButton = true; lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); ScrollUpListByOne(); } break; case HOVEROPTION_OVER_SCROLL_DOWN: case HOVEROPTION_CLICKED_SCROLL_DOWN: if (!m_bPressedScrollButton) { m_bPressedScrollButton = true; lastTimeClickedScrollButton = CTimer::GetTimeInMillisecondsPauseMode(); ScrollDownListByOne(); } break; default: m_bPressedScrollButton = false; } } } } void CMenuManager::UserInput(void) { bool goBack = false; bool optionSelected = false; bool goUp = false; bool goDown = false; int8 changeValueBy; if (!m_AllowNavigation && m_firstStartCounter == 255) m_AllowNavigation = true; if (!m_bShowMouse && m_nCurrScreen != MENUPAGE_MAP && (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY)) { m_bShowMouse = true; } static int oldOption = -99; oldOption = m_nCurrOption; #ifdef SCROLLABLE_PAGES int firstOption = SCREEN_HAS_AUTO_SCROLLBAR ? m_nFirstVisibleRowOnList : 0; int scrollOffset = aScreens[m_nCurrScreen].m_aEntries[firstOption].m_Y - aScreens[m_nCurrScreen].m_aEntries[0].m_Y; for (int rowToCheck = firstOption; rowToCheck < firstOption + MAX_VISIBLE_OPTION && rowToCheck < NUM_MENUROWS; ++rowToCheck) { #else for (int rowToCheck = 0; rowToCheck < NUM_MENUROWS; ++rowToCheck) { #endif if (aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Action == MENUACTION_NOTHING || aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Action == MENUACTION_LABEL) continue; // unused: CFont::GetStringWidth(TheText.Get(aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_EntryName), true); // So they also wanted the compare X, but they abandoned the idea later on if (m_nMousePosY > MENU_Y(aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Y MINUS_SCROLL_OFFSET) && m_nMousePosY < MENU_Y(aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Y MINUS_SCROLL_OFFSET PLUS_LINE_HEIGHT_ON_SCREEN)) { static int oldScreen = m_nCurrScreen; m_nOptionMouseHovering = rowToCheck; if (m_nMouseOldPosX != m_nMousePosX || m_nMouseOldPosY != m_nMousePosY) { m_nCurrOption = rowToCheck; m_bShowMouse = true; } int action = aScreens[m_nCurrScreen].m_aEntries[rowToCheck].m_Action; if (action != MENUACTION_BRIGHTNESS && action != MENUACTION_DRAWDIST && action != MENUACTION_MUSICVOLUME && action != MENUACTION_SFXVOLUME && action != MENUACTION_MOUSESENS && action != MENUACTION_MP3VOLUMEBOOST) m_nHoverOption = HOVEROPTION_RANDOM_ITEM; break; } if (m_bShowMouse && m_nMenuFadeAlpha == 255) { m_nOptionMouseHovering = oldOption; m_nCurrOption = oldOption; } } if (m_bShowMouse) { if (oldOption != m_nCurrOption) { if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_LABEL) { ++m_nCurrOption; ++m_nOptionMouseHovering; } m_nOptionHighlightTransitionBlend = 0; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); } } m_nMouseOldPosX = m_nMousePosX; m_nMouseOldPosY = m_nMousePosY; m_nMousePosX = m_nMouseTempPosX; m_nMousePosY = m_nMouseTempPosY; if (m_nMousePosX < 0) m_nMousePosX = 0; if (m_nMousePosX > SCREEN_WIDTH) m_nMousePosX = SCREEN_WIDTH; if (m_nMousePosY < 0) m_nMousePosY = 0; if (m_nMousePosY > SCREEN_HEIGHT) m_nMousePosY = SCREEN_HEIGHT; changeValueBy = 0; if (hasNativeList(m_nCurrScreen)) { ProcessList(optionSelected, goBack); } else { AdditionalOptionInput(goBack); if (m_AllowNavigation && (CPad::GetPad(0)->GetDownJustDown() || CPad::GetPad(0)->GetAnaloguePadDown() || CPad::GetPad(0)->GetDPadDownJustDown())) { m_bShowMouse = false; goDown = true; m_nOptionHighlightTransitionBlend = 0; } else if (m_AllowNavigation && (CPad::GetPad(0)->GetUpJustDown() || CPad::GetPad(0)->GetAnaloguePadUp() || CPad::GetPad(0)->GetDPadUpJustDown())) { m_bShowMouse = false; goUp = true; m_nOptionHighlightTransitionBlend = 0; } if ((m_nCurrOption == 0) && (m_nCurrScreen == MENUPAGE_PAUSE_MENU)) { if (CPad::GetPad(0)->GetEnterJustUp() || CPad::GetPad(0)->GetCrossJustUp()) { m_bShowMouse = false; optionSelected = true; } } else { if (CPad::GetPad(0)->GetEnterJustDown() || CPad::GetPad(0)->GetCrossJustDown()) { m_bShowMouse = false; optionSelected = true; } } if (CPad::GetPad(0)->GetLeftMouseJustUp() && m_nCurrScreen != MENUPAGE_MAP) { if (m_nHoverOption == HOVEROPTION_RANDOM_ITEM) optionSelected = true; else if (m_nHoverOption == HOVEROPTION_NEXT_RADIO) ChangeRadioStation(1); else if (m_nHoverOption == HOVEROPTION_PREV_RADIO) ChangeRadioStation(-1); } if (CPad::GetPad(0)->GetLeftMouse()) { switch (m_nHoverOption) { case HOVEROPTION_INCREASE_BRIGHTNESS: case HOVEROPTION_INCREASE_MP3BOOST: case HOVEROPTION_INCREASE_DRAWDIST: case HOVEROPTION_INCREASE_MUSICVOLUME: case HOVEROPTION_INCREASE_SFXVOLUME: case HOVEROPTION_INCREASE_MOUSESENS: CheckSliderMovement(1); break; case HOVEROPTION_DECREASE_BRIGHTNESS: case HOVEROPTION_DECREASE_MP3BOOST: case HOVEROPTION_DECREASE_DRAWDIST: case HOVEROPTION_DECREASE_MUSICVOLUME: case HOVEROPTION_DECREASE_SFXVOLUME: case HOVEROPTION_DECREASE_MOUSESENS: CheckSliderMovement(-1); break; } } #ifdef SCROLLABLE_PAGES if (m_nTotalListRow > MAX_VISIBLE_OPTION) { bool temp = false; m_nSelectedListRow = m_nCurrOption; // ignore detected back/select states, it's our screen's job ProcessList(temp, temp); // and ignore our screen's goUp/Down, now it's ProcessList's job goUp = false; goDown = false; m_nCurrOption = m_nSelectedListRow; if (oldOption != m_nCurrOption) m_nOptionHighlightTransitionBlend = 0; } // Prevent sound on scroll. Mouse wheel is now belongs to us! if (!(m_nTotalListRow > MAX_VISIBLE_OPTION && (CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustDown()))) #endif if (CPad::GetPad(0)->GetLeftMouseJustUp() || CPad::GetPad(0)->GetLeftJustUp() || CPad::GetPad(0)->GetRightJustUp() || CPad::GetPad(0)->GetDPadLeftJustUp() || CPad::GetPad(0)->GetDPadRightJustUp() || CPad::GetPad(0)->GetAnaloguePadLeftJustUp() || CPad::GetPad(0)->GetAnaloguePadRightJustUp() || CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustDown()) { int option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action; if (option == MENUACTION_BRIGHTNESS) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); else if (option == MENUACTION_SFXVOLUME) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_AUDIO_TEST, 0); else if (option == MENUACTION_DRAWDIST || option == MENUACTION_MOUSESENS) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); } if (CPad::GetPad(0)->GetBackJustDown() || CPad::GetPad(0)->GetEscapeJustDown()) { if (m_nCurrScreen != MENUPAGE_START_MENU && m_nCurrScreen != MENUPAGE_PAUSE_MENU && m_nCurrScreen != MENUPAGE_CHOOSE_SAVE_SLOT && m_nCurrScreen != MENUPAGE_SAVE_CHEAT_WARNING && m_nCurrScreen != MENUPAGE_SAVING_IN_PROGRESS && m_nCurrScreen != MENUPAGE_DELETING_IN_PROGRESS && m_nCurrScreen != MENUPAGE_OUTRO) { m_bShowMouse = false; goBack = true; } } if (((goDown) || (goUp)) || (optionSelected)) { goBack = false; } } int curAction = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action; if (CPad::GetPad(0)->GetLeft() || CPad::GetPad(0)->GetPedWalkLeftRight() < 0 || CPad::GetPad(0)->GetDPadLeft()) { static PauseModeTime lastSliderDecrease = 0; if (CTimer::GetTimeInMillisecondsPauseMode() - lastSliderDecrease > 150) { if (curAction == MENUACTION_BRIGHTNESS || curAction == MENUACTION_MUSICVOLUME || curAction == MENUACTION_SFXVOLUME || curAction == MENUACTION_RADIO || curAction == MENUACTION_DRAWDIST || curAction == MENUACTION_MOUSESENS || curAction == MENUACTION_MP3VOLUMEBOOST) changeValueBy = -1; lastSliderDecrease = CTimer::GetTimeInMillisecondsPauseMode(); } } else if (CPad::GetPad(0)->GetRight() || CPad::GetPad(0)->GetPedWalkLeftRight() > 0 || CPad::GetPad(0)->GetDPadRight()) { static PauseModeTime lastSliderIncrease = 0; if (CTimer::GetTimeInMillisecondsPauseMode() - lastSliderIncrease > 150) { if (curAction == MENUACTION_BRIGHTNESS || curAction == MENUACTION_MUSICVOLUME || curAction == MENUACTION_SFXVOLUME || curAction == MENUACTION_RADIO || curAction == MENUACTION_DRAWDIST || curAction == MENUACTION_MOUSESENS || curAction == MENUACTION_MP3VOLUMEBOOST) changeValueBy = 1; lastSliderIncrease = CTimer::GetTimeInMillisecondsPauseMode(); } } #ifdef SCROLLABLE_PAGES if (!SCREEN_HAS_AUTO_SCROLLBAR) #endif { if (CPad::GetPad(0)->GetMouseWheelUpJustDown()) { changeValueBy = 1; } else if (CPad::GetPad(0)->GetMouseWheelDownJustDown()) { changeValueBy = -1; } } if (m_AllowNavigation) { if (CPad::GetPad(0)->GetRightJustDown() || CPad::GetPad(0)->GetAnaloguePadRight() || CPad::GetPad(0)->GetDPadRightJustDown()) { m_bShowMouse = false; changeValueBy = 1; } } if (m_AllowNavigation) { if (CPad::GetPad(0)->GetLeftJustDown() || CPad::GetPad(0)->GetAnaloguePadLeft() || CPad::GetPad(0)->GetDPadLeftJustDown()) { m_bShowMouse = false; changeValueBy = -1; } } if (changeValueBy != 0) { if ((m_nCurrScreen == MENUPAGE_SOUND_SETTINGS || m_nCurrScreen == MENUPAGE_DISPLAY_SETTINGS || m_nCurrScreen == MENUPAGE_CONTROLLER_PC || m_nCurrScreen == MENUPAGE_MOUSE_CONTROLS) && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_NOTHING && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_LABEL && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_YES && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_NO && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_CHANGEMENU && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_KEYBOARDCTRLS && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_GOBACK && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_RESTOREDEF && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_DRAWDIST && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_MOUSESENS && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action != MENUACTION_MP3VOLUMEBOOST) { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); } } ProcessUserInput(goDown, goUp, optionSelected, goBack, changeValueBy); #ifdef CUSTOM_FRONTEND_OPTIONS if (aScreens[m_nCurrScreen].m_aEntries[oldOption].m_Action < MENUACTION_NOTHING) { // CFO check CMenuScreenCustom::CMenuEntry &oldEntry = aScreens[m_nCurrScreen].m_aEntries[oldOption]; if (m_nCurrOption != oldOption) { if (oldEntry.m_Action == MENUACTION_CFO_DYNAMIC) if(oldEntry.m_CFODynamic->buttonPressFunc) oldEntry.m_CFODynamic->buttonPressFunc(FEOPTION_ACTION_FOCUSLOSS); if (oldEntry.m_Action == MENUACTION_CFO_SELECT && oldEntry.m_CFOSelect->onlyApplyOnEnter) { if (oldEntry.m_CFOSelect->displayedValue != oldEntry.m_CFOSelect->lastSavedValue) SetHelperText(3); // Restored original value oldEntry.m_CFOSelect->displayedValue = oldEntry.m_CFOSelect->lastSavedValue = *oldEntry.m_CFO->value; } } else if (oldEntry.m_Action == MENUACTION_CFO_SELECT && oldEntry.m_CFOSelect->onlyApplyOnEnter) { if (oldEntry.m_CFOSelect->displayedValue != *oldEntry.m_CFO->value) SetHelperText(1); // Enter to apply else if (m_nHelperTextMsgId == 1) ResetHelperText(); // Applied } } #endif } void CMenuManager::ProcessUserInput(uint8 goDown, uint8 goUp, uint8 optionSelected, uint8 goBack, int8 changeAmount) { if (m_nCurrScreen == MENUPAGE_OUTRO) return; if (m_bWaitingForNewKeyBind) { if (m_bStartWaitingForKeyBind) m_bStartWaitingForKeyBind = false; else { pControlEdit = CPad::EditCodesForControls(pControlEdit, 1); JoyButtonJustClicked = false; MouseButtonJustClicked = false; if (CPad::GetPad(0)->GetLeftMouseJustDown()) MouseButtonJustClicked = rsMOUSELEFTBUTTON; else if (CPad::GetPad(0)->GetRightMouseJustUp()) MouseButtonJustClicked = rsMOUSERIGHTBUTTON; else if (CPad::GetPad(0)->GetMiddleMouseJustUp()) MouseButtonJustClicked = rsMOUSMIDDLEBUTTON; else if (CPad::GetPad(0)->GetMouseWheelUpJustUp()) MouseButtonJustClicked = rsMOUSEWHEELUPBUTTON; else if (CPad::GetPad(0)->GetMouseWheelDownJustUp()) MouseButtonJustClicked = rsMOUSEWHEELDOWNBUTTON; else if (CPad::GetPad(0)->GetMouseX1JustUp()) MouseButtonJustClicked = rsMOUSEX1BUTTON; else if (CPad::GetPad(0)->GetMouseX2JustUp()) MouseButtonJustClicked = rsMOUSEX2BUTTON; JoyButtonJustClicked = ControlsManager.GetJoyButtonJustDown(); int32 TypeOfControl = KEYBOARD; if (JoyButtonJustClicked) TypeOfControl = JOYSTICK; if (MouseButtonJustClicked) TypeOfControl = MOUSE; if (*pControlEdit != rsNULL) TypeOfControl = KEYBOARD; if (!m_bKeyIsOK) { pControlEdit = nil; m_bWaitingForNewKeyBind = false; m_KeyPressedCode = -1; m_bStartWaitingForKeyBind = false; } else if (!m_bKeyChangeNotProcessed) { if (*pControlEdit != rsNULL || MouseButtonJustClicked || JoyButtonJustClicked) CheckCodesForControls(TypeOfControl); field_159 = true; } else { for (int i = 0; i < 4; i++) ControlsManager.ClearSettingsAssociatedWithAction((e_ControllerAction)m_CurrCntrlAction, (eControllerType)i); m_bKeyIsOK = false; m_bKeyChangeNotProcessed = false; } } } if (pEditString || pControlEdit) return; #ifdef USE_DEBUG_SCRIPT_LOADER if (m_nCurrScreen == MENUPAGE_START_MENU || m_nCurrScreen == MENUPAGE_NEW_GAME || m_nCurrScreen == MENUPAGE_NEW_GAME_RELOAD) { #ifdef RW_GL3 if (glfwGetKey(PSGLOBAL(window), GLFW_KEY_R) == GLFW_PRESS) { scriptToLoad = 1; DoSettingsBeforeStartingAGame(); return; } #elif defined _WIN32 if (GetAsyncKeyState('R') & 0x8000) { scriptToLoad = 1; DoSettingsBeforeStartingAGame(); return; } #endif } #endif int oldOption = m_nCurrOption; if (goDown) { if (m_nCurrScreen != MENUPAGE_MAP) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); m_nCurrOption++; if (m_nCurrOption == NUM_MENUROWS || (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action == MENUACTION_NOTHING)) { m_nCurrOption = 0; } if (oldOption != m_nCurrOption) m_nOptionHighlightTransitionBlend = 0; } if (goUp) { if (m_nCurrScreen != MENUPAGE_MAP) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_HIGHLIGHT_OPTION, 0); if (m_nCurrOption == (aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL)) { while (m_nCurrOption != NUM_MENUROWS - 1 && aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption + 1].m_Action != MENUACTION_NOTHING) { m_nCurrOption++; } } else { m_nCurrOption--; } if (oldOption != m_nCurrOption) m_nOptionHighlightTransitionBlend = 0; } if (optionSelected && m_nMenuFadeAlpha == 255) { if (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_TargetMenu == MENUPAGE_NEW_GAME_RELOAD && m_bGameNotLoaded) { DoSettingsBeforeStartingAGame(); } else if (hasNativeList(m_nCurrScreen)) { switch (m_nCurrExLayer) { case HOVEROPTION_LIST: if (m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) { m_bWaitingForNewKeyBind = true; m_bStartWaitingForKeyBind = true; pControlEdit = &m_KeyPressedCode; } if (m_nCurrScreen == MENUPAGE_SKIN_SELECT) { strcpy(m_PrefsSkinFile, m_aSkinName); CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); m_nCurrExLayer = HOVEROPTION_BACK; SaveSettings(); } m_nHoverOption = HOVEROPTION_NOT_HOVERING; break; case HOVEROPTION_USESKIN: m_nHoverOption = HOVEROPTION_NOT_HOVERING; strcpy(m_PrefsSkinFile, m_aSkinName); CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); m_nCurrExLayer = HOVEROPTION_BACK; SaveSettings(); break; case HOVEROPTION_BACK: default: goBack = true; break; } if (m_nSkinsTotal > 0) { m_pSelectedSkin = m_pSkinListHead.nextSkin; strcpy(m_PrefsSkinFile, m_aSkinName); CWorld::Players[0].SetPlayerSkin(m_PrefsSkinFile); SaveSettings(); } } int option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action; #ifdef FIX_BUGS int currScreen = m_nCurrScreen; int currOption = m_nCurrOption; #endif switch (option) { case MENUACTION_CHANGEMENU: case MENUACTION_YES: case MENUACTION_NO: SwitchToNewScreen(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_TargetMenu); break; case MENUACTION_RADIO: ChangeRadioStation(1); break; case MENUACTION_LANG_ENG: m_PrefsLanguage = LANGUAGE_AMERICAN; m_bFrontEnd_ReloadObrTxtGxt = true; InitialiseChangedLanguageSettings(); SaveSettings(); break; case MENUACTION_LANG_FRE: m_PrefsLanguage = LANGUAGE_FRENCH; m_bFrontEnd_ReloadObrTxtGxt = true; InitialiseChangedLanguageSettings(); SaveSettings(); break; case MENUACTION_LANG_GER: m_PrefsLanguage = LANGUAGE_GERMAN; m_bFrontEnd_ReloadObrTxtGxt = true; InitialiseChangedLanguageSettings(); SaveSettings(); break; case MENUACTION_LANG_ITA: m_PrefsLanguage = LANGUAGE_ITALIAN; m_bFrontEnd_ReloadObrTxtGxt = true; InitialiseChangedLanguageSettings(); SaveSettings(); break; case MENUACTION_LANG_SPA: m_PrefsLanguage = LANGUAGE_SPANISH; m_bFrontEnd_ReloadObrTxtGxt = true; InitialiseChangedLanguageSettings(); SaveSettings(); break; case MENUACTION_CHECKSAVE: { int saveSlot = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_SaveSlot; if (saveSlot >= SAVESLOT_1 && saveSlot <= SAVESLOT_8) { m_nCurrSaveSlot = saveSlot - SAVESLOT_1; if (Slots[m_nCurrSaveSlot] != SLOT_EMPTY && Slots[m_nCurrSaveSlot] != SLOT_CORRUPTED) { if (m_nCurrScreen == MENUPAGE_CHOOSE_LOAD_SLOT) { SwitchToNewScreen(MENUPAGE_LOAD_SLOT_CONFIRM); } else if (m_nCurrScreen == MENUPAGE_CHOOSE_DELETE_SLOT) { SwitchToNewScreen(MENUPAGE_DELETE_SLOT_CONFIRM); } } } break; } case MENUACTION_NEWGAME: DoSettingsBeforeStartingAGame(); break; #ifdef LEGACY_MENU_OPTIONS case MENUACTION_RELOADIDE: CFileLoader::ReloadObjectTypes("GTA3.IDE"); break; #endif case MENUACTION_RESUME_FROM_SAVEZONE: RequestFrontEndShutDown(); break; case MENUACTION_LOADRADIO: if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { SwitchToNewScreen(MENUPAGE_SOUND_SETTINGS); DMAudio.PlayFrontEndTrack(m_PrefsRadioStation, 1); OutputDebugString("STARTED PLAYING FRONTEND AUDIO TRACK"); } break; case MENUACTION_SAVEGAME: { int saveSlot = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_SaveSlot; if (saveSlot >= 2 && saveSlot <= 9) { m_nCurrSaveSlot = m_nCurrOption; SwitchToNewScreen(MENUPAGE_SAVE_OVERWRITE_CONFIRM); } break; } case MENUACTION_RADARMODE: if (++m_PrefsRadarMode > 2) m_PrefsRadarMode = 0; SaveSettings(); break; case MENUACTION_GOBACK: goBack = true; break; case MENUACTION_KEYBOARDCTRLS: SwitchToNewScreen(MENUPAGE_KEYBOARD_CONTROLS); m_nSelectedListRow = 0; m_nCurrExLayer = HOVEROPTION_LIST; break; case MENUACTION_GETKEY: m_CurrCntrlAction = GetStartOptionsCntrlConfigScreens() + m_nCurrOption; m_bKeyIsOK = true; m_bWaitingForNewKeyBind = true; m_bStartWaitingForKeyBind = true; pControlEdit = &m_KeyPressedCode; break; case MENUACTION_CANCELGAME: DMAudio.Service(); SwitchToNewScreen(MENUPAGE_OUTRO); break; case MENUACTION_RESUME: #ifdef LEGACY_MENU_OPTIONS if (m_PrefsVsyncDisp != m_PrefsVsync) { m_PrefsVsync = m_PrefsVsyncDisp; } #endif RequestFrontEndShutDown(); break; case MENUACTION_DONTCANCEL: SwitchToNewScreen(-2); break; case MENUACTION_SCREENRES: if (m_nDisplayVideoMode != m_nPrefsVideoMode) { m_nPrefsVideoMode = m_nDisplayVideoMode; _psSelectScreenVM(m_nPrefsVideoMode); DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); DMAudio.Service(); CentreMousePointer(); m_bShowMouse = true; m_nCurrOption = 5; // TODO(Miami): Because selected option is resetted after res. change. We'll need to revisit that. m_nOptionHighlightTransitionBlend = 0; SaveSettings(); } break; case MENUACTION_AUDIOHW: { int selectedProvider = m_nPrefsAudio3DProviderIndex; if (selectedProvider != NO_AUDIO_PROVIDER) { if (selectedProvider == -1) selectedProvider = m_nPrefsAudio3DProviderIndex = DMAudio.AutoDetect3DProviders(); m_nPrefsAudio3DProviderIndex = DMAudio.SetCurrent3DProvider(m_nPrefsAudio3DProviderIndex); if (selectedProvider != m_nPrefsAudio3DProviderIndex) { SetHelperText(5); } SaveSettings(); } break; } case MENUACTION_SPEAKERCONF: if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { if (--m_PrefsSpeakers < 0) m_PrefsSpeakers = 2; DMAudio.SetSpeakerConfig(m_PrefsSpeakers); SaveSettings(); } break; case MENUACTION_PLAYERSETUP: CPlayerSkin::BeginFrontendSkinEdit(); SwitchToNewScreen(MENUPAGE_SKIN_SELECT); m_bSkinsEnumerated = false; m_nCurrExLayer = HOVEROPTION_LIST; break; case MENUACTION_RESTOREDEF: if (m_nCurrScreen == MENUPAGE_SOUND_SETTINGS) { m_nPrefsAudio3DProviderIndex = DMAudio.AutoDetect3DProviders(); DMAudio.SetCurrent3DProvider(m_nPrefsAudio3DProviderIndex); m_PrefsSfxVolume = 49; m_PrefsMusicVolume = 49; m_PrefsRadioStation = EMOTION; m_PrefsMP3BoostVolume = 0; m_PrefsStereoMono = 1; m_PrefsSpeakers = 0; DMAudio.SetMP3BoostVolume(m_PrefsMP3BoostVolume); DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); DMAudio.SetRadioInCar(m_PrefsRadioStation); DMAudio.PlayFrontEndTrack(m_PrefsRadioStation, 1); SaveSettings(); } else if (m_nCurrScreen == MENUPAGE_DISPLAY_SETTINGS) { m_PrefsBrightness = 256; m_PrefsLOD = 1.2f; #ifdef LEGACY_MENU_OPTIONS m_PrefsVsync = true; #endif CRenderer::ms_lodDistScale = m_PrefsLOD; m_PrefsShowSubtitles = false; #ifdef ASPECT_RATIO_SCALE m_PrefsUseWideScreen = AR_AUTO; #else m_PrefsUseWideScreen = false; #endif m_PrefsShowLegends = true; m_PrefsVsyncDisp = true; m_PrefsFrameLimiter = true; m_PrefsRadarMode = 0; m_PrefsShowHud = true; m_nDisplayVideoMode = m_nPrefsVideoMode; CMBlur::BlurOn = false; #ifdef CUSTOM_FRONTEND_OPTIONS extern void RestoreDefGraphics(int8); extern void RestoreDefDisplay(int8); RestoreDefGraphics(FEOPTION_ACTION_SELECT); RestoreDefDisplay(FEOPTION_ACTION_SELECT); #endif SaveSettings(); } else if (m_nCurrScreen == MENUPAGE_CONTROLLER_PC) { ControlsManager.MakeControllerActionsBlank(); ControlsManager.InitDefaultControlConfiguration(); ControlsManager.InitDefaultControlConfigMouse(MousePointerStateHelper.GetMouseSetUp()); #if !defined RW_GL3 if (AllValidWinJoys.m_aJoys[JOYSTICK1].m_bInitialised) { DIDEVCAPS devCaps; devCaps.dwSize = sizeof(DIDEVCAPS); PSGLOBAL(joy1)->GetCapabilities(&devCaps); ControlsManager.InitDefaultControlConfigJoyPad(devCaps.dwButtons); } #else if (PSGLOBAL(joy1id) != -1 && glfwJoystickPresent(PSGLOBAL(joy1id))) { int count; glfwGetJoystickButtons(PSGLOBAL(joy1id), &count); ControlsManager.InitDefaultControlConfigJoyPad(count); } #endif MousePointerStateHelper.bInvertVertically = true; TheCamera.m_bHeadBob = false; #ifdef FIX_BUGS TheCamera.m_fMouseAccelVertical = 0.003f; #endif TheCamera.m_fMouseAccelHorzntl = 0.0025f; CVehicle::m_bDisableMouseSteering = true; m_ControlMethod = CONTROL_STANDARD; #ifdef PC_PLAYER_CONTROLS TheCamera.m_bUseMouse3rdPerson = true; #else TheCamera.m_bUseMouse3rdPerson = false; #endif SaveSettings(); #ifdef LOAD_INI_SETTINGS SaveINIControllerSettings(); #endif } SetHelperText(2); break; case MENUACTION_CTRLMETHOD: if (m_ControlMethod == CONTROL_CLASSIC) { CCamera::m_bUseMouse3rdPerson = true; m_ControlMethod = CONTROL_STANDARD; } else { CCamera::m_bUseMouse3rdPerson = false; m_ControlMethod = CONTROL_CLASSIC; } SaveSettings(); break; #ifdef CUSTOM_FRONTEND_OPTIONS case MENUACTION_CFO_SELECT: case MENUACTION_CFO_DYNAMIC: CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption]; if (option.m_Action == MENUACTION_CFO_SELECT) { if (option.m_CFOSelect->disableIfGameLoaded && !m_bGameNotLoaded) break; if (!option.m_CFOSelect->onlyApplyOnEnter) { option.m_CFOSelect->displayedValue++; if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0) option.m_CFOSelect->displayedValue = 0; } int8 oldValue = *option.m_CFO->value; *option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue; // Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO // if (option.m_CFOSelect->save) SaveSettings(); if (option.m_CFOSelect->displayedValue != oldValue && option.m_CFOSelect->changeFunc) option.m_CFOSelect->changeFunc(oldValue, option.m_CFOSelect->displayedValue); } else if (option.m_Action == MENUACTION_CFO_DYNAMIC) { if (option.m_CFODynamic->buttonPressFunc) option.m_CFODynamic->buttonPressFunc(FEOPTION_ACTION_SELECT); } break; #endif } ProcessOnOffMenuOptions(); if (!goBack) { #ifdef FIX_BUGS int saveSlot = aScreens[currScreen].m_aEntries[currOption].m_SaveSlot; if (saveSlot >= SAVESLOT_1 && saveSlot <= SAVESLOT_8 && Slots[currOption] != SLOT_OK) #else int saveSlot = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_SaveSlot; if (saveSlot >= SAVESLOT_1 && saveSlot <= SAVESLOT_8 && Slots[m_nCurrOption] != SLOT_OK) #endif DMAudio.PlayFrontEndSound(SOUND_FRONTEND_FAIL, 0); else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); } } if (goBack) { if (m_NoEmptyBinding) { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_BACK, 0); SwitchToNewScreen(-2); if (hasNativeList(m_nCurrScreen)) { m_nTotalListRow = 0; } } else { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_FAIL, 0); m_ShowEmptyBindingError = true; } } if (changeAmount != 0) { switch (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action) { #ifdef GAMEPAD_MENU case MENUACTION_CTRLCONFIG: CPad::GetPad(0)->Mode += changeAmount; if (CPad::GetPad(0)->Mode > 3) CPad::GetPad(0)->Mode = 0; else if (CPad::GetPad(0)->Mode < 0) CPad::GetPad(0)->Mode = 3; SaveSettings(); break; #endif case MENUACTION_RADIO: ChangeRadioStation(changeAmount); break; case MENUACTION_RADARMODE: m_PrefsRadarMode += changeAmount; if (m_PrefsRadarMode < 0) m_PrefsRadarMode = 2; if (m_PrefsRadarMode > 2) m_PrefsRadarMode = 0; break; #ifdef ASPECT_RATIO_SCALE case MENUACTION_WIDESCREEN: if (changeAmount > 0) { m_PrefsUseWideScreen++; if (m_PrefsUseWideScreen > AR_MAX - 1) m_PrefsUseWideScreen = 0; } else { m_PrefsUseWideScreen--; if (m_PrefsUseWideScreen < 0) m_PrefsUseWideScreen = AR_MAX - 1; } SaveSettings(); break; #endif case MENUACTION_SCREENRES: if (m_bGameNotLoaded) { RwChar** videoMods = _psGetVideoModeList(); if (changeAmount > 0) { do { ++m_nDisplayVideoMode; if (m_nDisplayVideoMode >= _psGetNumVideModes()) m_nDisplayVideoMode = 0; } while (!videoMods[m_nDisplayVideoMode]); } else { do { --m_nDisplayVideoMode; if (m_nDisplayVideoMode < 0) m_nDisplayVideoMode = _psGetNumVideModes() - 1; } while (!videoMods[m_nDisplayVideoMode]); } } break; case MENUACTION_AUDIOHW: if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { m_nPrefsAudio3DProviderIndex += changeAmount; bool checkIfForbidden = true; while (checkIfForbidden) { checkIfForbidden = false; if (m_nPrefsAudio3DProviderIndex < -1) m_nPrefsAudio3DProviderIndex = DMAudio.GetNum3DProvidersAvailable() - 1; else if (m_nPrefsAudio3DProviderIndex > DMAudio.GetNum3DProvidersAvailable() - 1) m_nPrefsAudio3DProviderIndex = -1; // what a retarded move... if (m_nPrefsAudio3DProviderIndex != -1) { char* provider = DMAudio.Get3DProviderName(m_nPrefsAudio3DProviderIndex); strupr(provider); if (!strcmp(provider, "MILES FAST 2D POSITIONAL AUDIO")) { m_nPrefsAudio3DProviderIndex += changeAmount; checkIfForbidden = true; } else if (!strcmp(provider, "AUREAL A3D 2.0 (TM)")) { m_nPrefsAudio3DProviderIndex += changeAmount; checkIfForbidden = true; } else if (!strcmp(provider, "AUREAL A3D INTERACTIVE (TM)")) { m_nPrefsAudio3DProviderIndex += changeAmount; checkIfForbidden = true; } } } } break; case MENUACTION_SPEAKERCONF: if (m_nPrefsAudio3DProviderIndex != NO_AUDIO_PROVIDER) { m_PrefsSpeakers -= changeAmount; m_PrefsSpeakers = clamp(m_PrefsSpeakers, 0, 2); DMAudio.SetSpeakerConfig(m_PrefsSpeakers); SaveSettings(); } break; case MENUACTION_CTRLMETHOD: m_ControlMethod = !m_ControlMethod; CCamera::m_bUseMouse3rdPerson = !m_ControlMethod; SaveSettings(); break; #ifdef CUSTOM_FRONTEND_OPTIONS case MENUACTION_CFO_SELECT: case MENUACTION_CFO_DYNAMIC: CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption]; if (option.m_Action == MENUACTION_CFO_SELECT) { if (option.m_CFOSelect->disableIfGameLoaded && !m_bGameNotLoaded) break; if (changeAmount > 0) { option.m_CFOSelect->displayedValue++; if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts) option.m_CFOSelect->displayedValue = 0; } else { option.m_CFOSelect->displayedValue--; if (option.m_CFOSelect->displayedValue < 0) option.m_CFOSelect->displayedValue = option.m_CFOSelect->numRightTexts - 1; } if (!option.m_CFOSelect->onlyApplyOnEnter) { int8 oldValue = *option.m_CFO->value; *option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue; // Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO // if (option.m_CFOSelect->save) SaveSettings(); if (option.m_CFOSelect->displayedValue != oldValue && option.m_CFOSelect->changeFunc) option.m_CFOSelect->changeFunc(oldValue, option.m_CFOSelect->displayedValue); } } else if (option.m_Action == MENUACTION_CFO_DYNAMIC && option.m_CFODynamic->buttonPressFunc) { option.m_CFODynamic->buttonPressFunc(changeAmount > 0 ? FEOPTION_ACTION_RIGHT : FEOPTION_ACTION_LEFT); } break; #endif } CheckSliderMovement(changeAmount); ProcessOnOffMenuOptions(); if (m_nCurrScreen == MENUPAGE_KEYBOARD_CONTROLS) { if (changeAmount < 1) { m_nSelectedContSetupColumn = CONTSETUP_PED_COLUMN; } else { m_nSelectedContSetupColumn = CONTSETUP_VEHICLE_COLUMN; } } } } void CMenuManager::ProcessOnOffMenuOptions() { switch (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action) { #ifdef GAMEPAD_MENU case MENUACTION_CTRLVIBRATION: m_PrefsUseVibration = !m_PrefsUseVibration; if (m_PrefsUseVibration) { CPad::GetPad(0)->StartShake(350, 150); TimeToStopPadShaking = CTimer::GetTimeInMillisecondsPauseMode() + 500; } SaveSettings(); break; #endif case MENUACTION_INVERTPADY: CPad::bInvertLook4Pad = !CPad::bInvertLook4Pad; SaveSettings(); // FIX: Why don't SaveSettings? Because of it's an hidden option? :( break; case MENUACTION_CTRLDISPLAY: m_DisplayControllerOnFoot = !m_DisplayControllerOnFoot; break; case MENUACTION_FRAMESYNC: m_PrefsVsyncDisp = !m_PrefsVsyncDisp; SaveSettings(); // FIX: Again... This makes me very unhappy break; case MENUACTION_FRAMELIMIT: m_PrefsFrameLimiter = !m_PrefsFrameLimiter; SaveSettings(); break; case MENUACTION_TRAILS: CMBlur::BlurOn = !CMBlur::BlurOn; SaveSettings(); break; case MENUACTION_SUBTITLES: m_PrefsShowSubtitles = !m_PrefsShowSubtitles; SaveSettings(); break; #ifndef ASPECT_RATIO_SCALE case MENUACTION_WIDESCREEN: m_PrefsUseWideScreen = !m_PrefsUseWideScreen; SaveSettings(); break; #endif case MENUACTION_LEGENDS: m_PrefsShowLegends = !m_PrefsShowLegends; break; case MENUACTION_HUD: m_PrefsShowHud = !m_PrefsShowHud; SaveSettings(); break; #ifdef LEGACY_MENU_OPTIONS case MENUACTION_SETDBGFLAG: CTheScripts::InvertDebugFlag(); break; case MENUACTION_SWITCHBIGWHITEDEBUGLIGHT: gbBigWhiteDebugLightSwitchedOn = !gbBigWhiteDebugLightSwitchedOn; break; case MENUACTION_COLLISIONPOLYS: gbShowCollisionPolys = !gbShowCollisionPolys; break; #endif case MENUACTION_SHOWHEADBOB: TheCamera.m_bHeadBob = !TheCamera.m_bHeadBob; SaveSettings(); break; case MENUACTION_INVVERT: MousePointerStateHelper.bInvertVertically = !MousePointerStateHelper.bInvertVertically; SaveSettings(); break; case MENUACTION_DYNAMICACOUSTIC: m_PrefsDMA = !m_PrefsDMA; DMAudio.SetDynamicAcousticModelingStatus(m_PrefsDMA); SaveSettings(); break; case MENUACTION_MOUSESTEER: if (m_ControlMethod == CONTROL_STANDARD) { CVehicle::m_bDisableMouseSteering = !CVehicle::m_bDisableMouseSteering; SaveSettings(); } break; } } void CMenuManager::RequestFrontEndShutDown() { m_bShutDownFrontEndRequested = true; } void CMenuManager::RequestFrontEndStartUp() { m_bStartUpFrontEndRequested = true; } void CMenuManager::ResetHelperText() { m_nHelperTextMsgId = 0; m_nHelperTextAlpha = 300; } void CMenuManager::SetHelperText(int text) { m_nHelperTextMsgId = text; m_nHelperTextAlpha = 300; } float CMenuManager::StretchX(float x) { if (SCREEN_WIDTH == DEFAULT_SCREEN_WIDTH) return x; else // We won't make this SCREEN_SCALE, because many cases relies on stretching and we want the code to be portable. // Instead we will use MENU_X_LEFT_ALIGNED or SCREEN_SCALE_X when needed. return SCREEN_STRETCH_X(x); } float CMenuManager::StretchY(float y) { if (SCREEN_HEIGHT == DEFAULT_SCREEN_HEIGHT) return y; else return SCREEN_STRETCH_Y(y); } #ifdef XBOX_MESSAGE_SCREEN void CMenuManager::CloseDialog(void) { // We don't have this on PC GXT :shrug: static wchar* gameSaved = AllocUnicode("Game saved successfully!"); if (m_bSaveWasSuccessful && DialogTextCmp("FESZ_WR")) { m_bSaveWasSuccessful = false; // i don't know where XBOX resets that m_pDialogText = gameSaved; SetDialogTimer(1000); ProcessDialogTimer(); } else { ToggleDialog(false); } } void CMenuManager::ProcessDialogTimer(void) { if (!m_bDialogOpen || m_nDialogHideTimer == 0) return; // Also XBOX has unified time source for in-game/menu, but we don't have that if (m_bMenuActive && CTimer::GetTimeInMilliseconds() > m_nDialogHideTimer || !m_bMenuActive && CTimer::GetTimeInMillisecondsPauseMode() > m_nDialogHideTimerPauseMode) { // This is originally activePage.funcs->closePage() CloseDialog(); } } void CMenuManager::SetDialogTimer(uint32 timer) { // XBOX iterates some page list(actives?) and then sets timer variable of specified page to specified value. We only have dialog right now. // Also XBOX has unified time source for in-game/menu, but we don't have that, thus 2 timer variables... m_nDialogHideTimer = CTimer::GetTimeInMilliseconds() + timer; m_nDialogHideTimerPauseMode = CTimer::GetTimeInMillisecondsPauseMode() + timer; } void CMenuManager::SetDialogText(const char* key) { // There are many things going around here, idk why m_pDialogText = TheText.Get(key); } bool CMenuManager::DialogTextCmp(const char* key) { wchar *value = TheText.Get(key); wchar *i = m_pDialogText; for (; *i != '\0' && *value != '\0'; i++, value++) { if (*i != *value) return false; } return *i == '\0' && *value == '\0'; } void CMenuManager::ToggleDialog(bool toggle) { // This originally calls some mysterious function on enable and close CB on disable, along with decreasing some counter. Which is no use for dialog // XBOX doesn't do that if (toggle) m_nDialogHideTimer = 0; m_bDialogOpen = toggle; } void DrawDialogBg(float offset, uint8 alpha) { CSprite2d::Draw2DPolygon(SCALE_AND_CENTER_X(84.f + offset), MENU_Y(126.f + offset), SCALE_AND_CENTER_X(512.f + offset), MENU_Y(109.f + offset), SCALE_AND_CENTER_X(100.f + offset), MENU_Y(303.f + offset), SCALE_AND_CENTER_X(474.f + offset), MENU_Y(311.f + offset), CRGBA(107, 193, 236, alpha)); CSprite2d::Draw2DPolygon(SCALE_AND_CENTER_X(523.f + offset), MENU_Y(108.f + offset), SCALE_AND_CENTER_X(542.f + offset), MENU_Y(107.f + offset), SCALE_AND_CENTER_X(485.f + offset), MENU_Y(310.f + offset), SCALE_AND_CENTER_X(516.f + offset), MENU_Y(311.f + offset), CRGBA(107, 193, 236, alpha)); } void CMenuManager::DrawOverlays(void) { // This is stripped to show only Dialog box, XBOX does much more in here. if (!m_bDialogOpen) return; DefinedState(); CSprite2d::DrawRect(CRect(0, SCREEN_HEIGHT, SCREEN_WIDTH, 0), CRGBA(0, 0, 0, 160)); // Ofc this is not hardcoded like that on Xbox, it should be a texture DrawDialogBg(20.f, 160); // shadow DrawDialogBg(0.f, 255); CFont::SetBackgroundOff(); CFont::SetPropOn(); CFont::SetJustifyOn(); CFont::SetBackGroundOnlyTextOn(); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetCentreSize(SCREEN_SCALE_X(380.0f)); CFont::SetCentreOn(); CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, 255)); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); // Both of those are 0.9 on Xbox, which is ofcouse wrong... CFont::SetScale(SCREEN_SCALE_X(BIGTEXT_X_SCALE), SCREEN_SCALE_Y(BIGTEXT_Y_SCALE)); int x = SCREEN_WIDTH / 2.f - SCREEN_SCALE_X(30.0f); int y = SCREEN_HEIGHT / 2.f - SCREEN_SCALE_Y(30.0f); int numOfLines = CFont::GetNumberLines(x, y, m_pDialogText); CFont::PrintString(x, y - SCREEN_SCALE_Y(numOfLines / 2.f), m_pDialogText); CFont::DrawFonts(); } #endif void CMenuManager::ProcessFileActions() { switch (m_nCurrScreen) { case MENUPAGE_LOADING_IN_PROGRESS: if (CheckSlotDataValid(m_nCurrSaveSlot)) { #ifdef USE_DEBUG_SCRIPT_LOADER scriptToLoad = 0; #endif #ifdef XBOX_MESSAGE_SCREEN SetDialogText("FELD_WR"); ToggleDialog(true); #else if (!m_bGameNotLoaded) MessageScreen("FELD_WR", true); #endif DoSettingsBeforeStartingAGame(); m_bWantToLoad = true; } else SwitchToNewScreen(MENUPAGE_NEW_GAME); break; case MENUPAGE_DELETING_IN_PROGRESS: { static bool waitedForScreen = false; if (waitedForScreen) { bool SlotPopulated = false; if (PcSaveHelper.DeleteSlot(m_nCurrSaveSlot)) { PcSaveHelper.PopulateSlotInfo(); SlotPopulated = true; } if (SlotPopulated) { SwitchToNewScreen(MENUPAGE_DELETE_SUCCESSFUL); } else { SwitchToNewScreen(MENUPAGE_SAVE_CUSTOM_WARNING); strncpy(aScreens[m_nCurrScreen].m_ScreenName, "FES_DEL", 8); strncpy(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName, "FES_DEE", 8); } waitedForScreen = false; } else if (m_nMenuFadeAlpha >= 255) waitedForScreen = true; break; } case MENUPAGE_SAVING_IN_PROGRESS: { #ifdef XBOX_MESSAGE_SCREEN if (m_bDialogOpen && DialogTextCmp("FESZ_WR")) { PauseModeTime startTime = CTimer::GetTimeInMillisecondsPauseMode(); int8 SaveSlot = PcSaveHelper.SaveSlot(m_nCurrSaveSlot); PcSaveHelper.PopulateSlotInfo(); // Original code, but we don't want redundant saving text if it doesn't #if 0 CTimer::Update(); // not on Xbox, who updates it? // it compensates the lag to show saving text always one second... how cute int dialogDur = Max(1, startTime - CTimer::GetTimeInMillisecondsPauseMode() + 1000); #else int dialogDur = 1; #endif if (SaveSlot) { // error. PC code ToggleDialog(false); SwitchToNewScreen(MENUPAGE_SAVE_CUSTOM_WARNING); strncpy(aScreens[m_nCurrScreen].m_ScreenName, "FET_SG", 8); strncpy(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName, "FES_CMP", 8); } else { m_bSaveWasSuccessful = true; SetDialogTimer(dialogDur); ProcessDialogTimer(); RequestFrontEndShutDown(); } } else { SetDialogText("FESZ_WR"); ToggleDialog(true); } #else static bool waitedForScreen = false; if (waitedForScreen) { int8 SaveSlot = PcSaveHelper.SaveSlot(m_nCurrSaveSlot); PcSaveHelper.PopulateSlotInfo(); if (SaveSlot) { SwitchToNewScreen(MENUPAGE_SAVE_CUSTOM_WARNING); strncpy(aScreens[m_nCurrScreen].m_ScreenName, "FET_SG", 8); strncpy(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName, "FES_CMP", 8); } else SwitchToNewScreen(MENUPAGE_SAVE_SUCCESSFUL); waitedForScreen = false; } else if (m_nMenuFadeAlpha >= 255) waitedForScreen = true; #endif break; } } } void CMenuManager::SwitchMenuOnAndOff() { if (!TheCamera.m_WideScreenOn) { // Reminder: You need REGISTER_START_BUTTON defined to make it work. if ((CPad::GetPad(0)->GetStartJustDown() || CPad::GetPad(0)->GetEscapeJustDown()) && (!m_bMenuActive || m_nCurrScreen == MENUPAGE_PAUSE_MENU || m_nCurrScreen == MENUPAGE_CHOOSE_SAVE_SLOT || m_nCurrScreen == MENUPAGE_SAVE_CHEAT_WARNING) || m_bShutDownFrontEndRequested || m_bStartUpFrontEndRequested #ifdef REGISTER_START_BUTTON || CPad::GetPad(0)->GetStartJustDown() && !m_bGameNotLoaded #endif ) { if (m_nCurrScreen != MENUPAGE_LOADING_IN_PROGRESS #ifdef XBOX_MESSAGE_SCREEN && m_nCurrScreen != MENUPAGE_SAVING_IN_PROGRESS #endif ) { DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DoRWStuffEndOfFrame(); DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DoRWStuffEndOfFrame(); } if (m_bShutDownFrontEndRequested) m_bMenuActive = false; else if (m_bStartUpFrontEndRequested) m_bMenuActive = true; else m_bMenuActive = !m_bMenuActive; if (m_bMenuActive) { if (_InputMouseNeedsExclusive()) { _InputShutdownMouse(); _InputInitialiseMouse(false); } Initialise(); LoadAllTextures(); #ifdef FIX_BUGS CPad::StopPadsShaking(); #endif } else { #ifdef EXTENDED_COLOURFILTER // we always expect CPostFX to be open CMBlur::BlurOn = true; #endif if (CMBlur::BlurOn) CMBlur::MotionBlurOpen(Scene.camera); else CMBlur::MotionBlurClose(); DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DoRWStuffEndOfFrame(); DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DoRWStuffEndOfFrame(); if (_InputMouseNeedsExclusive()) { _InputShutdownMouse(); _InputInitialiseMouse(true); } m_StatsScrollSpeed = 150.0f; #ifdef FIX_BUGS ThingsToDoBeforeLeavingPage(); #endif SaveSettings(); #ifdef LOAD_INI_SETTINGS SaveINIControllerSettings(); #endif pControlEdit = nil; pEditString = nil; DisplayComboButtonErrMsg = false; m_bShutDownFrontEndRequested = false; m_bStartUpFrontEndRequested = false; m_bWaitingForNewKeyBind = false; #ifdef REGISTER_START_BUTTON int16 start1 = CPad::GetPad(0)->PCTempJoyState.Start, start2 = CPad::GetPad(0)->PCTempKeyState.Start, start3 = CPad::GetPad(0)->OldState.Start, start4 = CPad::GetPad(0)->NewState.Start; #endif CPad::GetPad(0)->Clear(false); CPad::GetPad(1)->Clear(false); #ifdef REGISTER_START_BUTTON CPad::GetPad(0)->PCTempJoyState.Start = start1; CPad::GetPad(0)->PCTempKeyState.Start = start2; CPad::GetPad(0)->OldState.Start = start3; CPad::GetPad(0)->NewState.Start = start4; #endif UnloadTextures(); CTimer::EndUserPause(); CTimer::Update(); m_OnlySaveMenu = false; } } } // Just entered the save/safe zone if (m_bActivateSaveMenu) { DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DoRWStuffEndOfFrame(); DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); DoRWStuffEndOfFrame(); m_bActivateSaveMenu = false; m_bMenuActive = true; m_OnlySaveMenu = true; if (_InputMouseNeedsExclusive()) { _InputShutdownMouse(); _InputInitialiseMouse(false); } Initialise(); LoadAllTextures(); if (CPad::bHasPlayerCheated) { m_nCurrScreen = MENUPAGE_SAVE_CHEAT_WARNING; m_nCurrOption = 0; } else { m_nCurrScreen = MENUPAGE_CHOOSE_SAVE_SLOT; m_nCurrOption = 8; } } m_bStartUpFrontEndRequested = false; m_bShutDownFrontEndRequested = false; #ifdef GAMEPAD_MENU // Reset pad shaking. if (TimeToStopPadShaking && TimeToStopPadShaking < CTimer::GetTimeInMillisecondsPauseMode()) { CPad::StopPadsShaking(); TimeToStopPadShaking = 0; } #endif } void CMenuManager::UnloadTextures() { if (m_nCurrScreen == MENUPAGE_SOUND_SETTINGS) DMAudio.StopFrontEndTrack(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); DMAudio.ChangeMusicMode(MUSICMODE_GAME); if (m_bSpritesLoaded) { printf("REMOVE frontend\n"); int frontend = CTxdStore::FindTxdSlot("frontend1"); for (int i = 0; i < 3; ++i) m_aFrontEndSprites[i].Delete(); CTxdStore::RemoveTxd(frontend); if (!m_OnlySaveMenu) { int frontend2 = CTxdStore::FindTxdSlot("frontend2"); for (int i = 3; i < NUM_MENU_SPRITES; ++i) m_aFrontEndSprites[i].Delete(); CTxdStore::RemoveTxd(frontend2); #ifdef GAMEPAD_MENU // Unload controller txd int frontend_controller = CTxdStore::FindTxdSlot("frontend_controller"); if (frontend_controller != -1) CTxdStore::RemoveTxd(frontend_controller); #endif } m_bSpritesLoaded = false; } m_OnlySaveMenu = false; CUserDisplay::PlaceName.ProcessAfterFrontEndShutDown(); } void CMenuManager::WaitForUserCD() { CSprite2d *splash; char *splashscreen = nil; splash = LoadSplash(splashscreen); if (RsGlobal.quit) return; HandleExit(); CPad::UpdatePads(); MessageScreen("NO_PCCD", true); if (CPad::GetPad(0)->GetEscapeJustDown()) { m_bQuitGameNoCD = true; RsEventHandler(rsQUITAPP, nil); } } void CMenuManager::DrawQuitGameScreen(void) { static int32 exitSignalTimer = 0; #ifdef FIX_BUGS int alpha = clamp(m_nMenuFadeAlpha, 0, 255); #else int alpha = m_nMenuFadeAlpha; #endif #ifndef MUCH_SHORTER_OUTRO_SCREEN static PauseModeTime lastTickIncrease = 0; if (alpha == 255 && CTimer::GetTimeInMillisecondsPauseMode() - lastTickIncrease > 10) { exitSignalTimer++; lastTickIncrease = CTimer::GetTimeInMillisecondsPauseMode(); } #else static PauseModeTime firstTick = CTimer::GetTimeInMillisecondsPauseMode(); if (alpha == 255 && CTimer::GetTimeInMillisecondsPauseMode() - firstTick > 750) { exitSignalTimer = 150; } #endif static CSprite2d *splash = nil; if (splash == nil) splash = LoadSplash("OUTRO"); m_aFrontEndSprites[MENUSPRITE_VCLOGO].Draw(CRect(SCREEN_STRETCH_X(28.0f), MENU_Y(8.0f), SCREEN_STRETCH_X(27.0f) + MENU_X(130.f), MENU_Y(138.0f)), CRGBA(255, 255, 255, 255 - alpha)); // Or we can see menu background from sides #ifdef ASPECT_RATIO_SCALE CSprite2d::DrawRect(CRect(0, 0, MENU_X_LEFT_ALIGNED(0.f), SCREEN_HEIGHT), CRGBA(0, 0, 0, alpha)); CSprite2d::DrawRect(CRect(MENU_X_RIGHT_ALIGNED(0.f), 0, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(0, 0, 0, alpha)); #endif splash->Draw(CRect(MENU_X_LEFT_ALIGNED(0.f), 0, MENU_X_RIGHT_ALIGNED(0.f), SCREEN_HEIGHT), CRGBA(255, 255, 255, alpha)); if (alpha == 255 && exitSignalTimer == 150) RsEventHandler(rsQUITAPP, nil); m_bShowMouse = false; m_AllowNavigation = false; } void CMenuManager::PrintMap(void) { m_bMenuMapActive = true; CRadar::InitFrontEndMap(); // Because m_fMapSize is half of the map length(hence * 2), and map consists of 3x3 tiles(hence / 3). float halfTile = m_fMapSize * 2.f / 3.f / 2.f; RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); if (SCREEN_WIDTH >= m_fMapCenterX - m_fMapSize || SCREEN_HEIGHT >= m_fMapCenterY - m_fMapSize) { m_aFrontEndSprites[MENUSPRITE_MAPTOP01].Draw(CRect(m_fMapCenterX - m_fMapSize, m_fMapCenterY - m_fMapSize, m_fMapCenterX - halfTile, m_fMapCenterY - halfTile), CRGBA(255, 255, 255, FadeIn(255))); } if (SCREEN_WIDTH >= m_fMapCenterX - halfTile || SCREEN_HEIGHT >= m_fMapCenterY - m_fMapSize) { m_aFrontEndSprites[MENUSPRITE_MAPTOP02].Draw(CRect(m_fMapCenterX - halfTile, m_fMapCenterY - m_fMapSize, m_fMapCenterX + halfTile, m_fMapCenterY - halfTile), CRGBA(255, 255, 255, FadeIn(255))); } if (SCREEN_WIDTH >= m_fMapCenterX + halfTile || SCREEN_HEIGHT >= m_fMapCenterY - m_fMapSize) { m_aFrontEndSprites[MENUSPRITE_MAPTOP03].Draw(CRect(m_fMapCenterX + halfTile, m_fMapCenterY - m_fMapSize, m_fMapCenterX + m_fMapSize, m_fMapCenterY - halfTile), CRGBA(255, 255, 255, FadeIn(255))); } if (SCREEN_WIDTH >= m_fMapCenterX - m_fMapSize || SCREEN_HEIGHT >= m_fMapCenterY - halfTile) { m_aFrontEndSprites[MENUSPRITE_MAPMID01].Draw(CRect(m_fMapCenterX - m_fMapSize, m_fMapCenterY - halfTile, m_fMapCenterX - halfTile, m_fMapCenterY + halfTile), CRGBA(255, 255, 255, FadeIn(255))); } if (SCREEN_WIDTH >= m_fMapCenterX - halfTile || SCREEN_HEIGHT >= m_fMapCenterY - halfTile) { m_aFrontEndSprites[MENUSPRITE_MAPMID02].Draw(CRect(m_fMapCenterX - halfTile, m_fMapCenterY - halfTile, m_fMapCenterX + halfTile, m_fMapCenterY + halfTile), CRGBA(255, 255, 255, FadeIn(255))); } if (SCREEN_WIDTH >= m_fMapCenterX + halfTile || SCREEN_HEIGHT >= m_fMapCenterY - halfTile) { m_aFrontEndSprites[MENUSPRITE_MAPMID03].Draw(CRect(m_fMapCenterX + halfTile, m_fMapCenterY - halfTile, m_fMapCenterX + m_fMapSize, m_fMapCenterY + halfTile), CRGBA(255, 255, 255, FadeIn(255))); } if (SCREEN_WIDTH >= m_fMapCenterX - m_fMapSize || SCREEN_HEIGHT >= m_fMapCenterY + halfTile) { m_aFrontEndSprites[MENUSPRITE_MAPBOT01].Draw(CRect(m_fMapCenterX - m_fMapSize, m_fMapCenterY + halfTile, m_fMapCenterX - halfTile, m_fMapCenterY + m_fMapSize), CRGBA(255, 255, 255, FadeIn(255))); } if (SCREEN_WIDTH >= m_fMapCenterX - halfTile || SCREEN_HEIGHT >= m_fMapCenterY + halfTile) { m_aFrontEndSprites[MENUSPRITE_MAPBOT02].Draw(CRect(m_fMapCenterX - halfTile, m_fMapCenterY + halfTile, m_fMapCenterX + halfTile, m_fMapCenterY + m_fMapSize), CRGBA(255, 255, 255, FadeIn(255))); } if (SCREEN_WIDTH >= m_fMapCenterX + halfTile || SCREEN_HEIGHT >= m_fMapCenterY + halfTile) { m_aFrontEndSprites[MENUSPRITE_MAPBOT03].Draw(CRect(m_fMapCenterX + halfTile, m_fMapCenterY + halfTile, m_fMapCenterX + m_fMapSize, m_fMapCenterY + m_fMapSize), CRGBA(255, 255, 255, FadeIn(255))); } CRadar::DrawBlips(); if (m_PrefsShowLegends) { CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(40.0f)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(84.0f)); CFont::SetBackGroundOnlyTextOff(); CFont::SetColor(CRGBA(LABEL_COLOR.r, LABEL_COLOR.g, LABEL_COLOR.b, FadeIn(255))); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetCentreOn(); CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(SCREEN_SCALE_X(0.65f), SCREEN_SCALE_Y(0.95f)); int secondColumnStart = (CRadar::MapLegendCounter - 1) / 2; int boxBottom = MENU_Y(100.0f); // + 3, because we want 19*3 px padding for (int i = 0; i < secondColumnStart + 3; i++) { boxBottom += MENU_Y(19.f); } CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(95.0f), MENU_Y(100.0f), MENU_X_LEFT_ALIGNED(555.f), boxBottom), CRGBA(0, 0, 0, FadeIn(190))); CFont::PrintString(MENU_X_LEFT_ALIGNED(320.0f), MENU_Y(102.0f), TheText.Get("FE_MLG")); CFont::SetRightJustifyOff(); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); if (m_PrefsLanguage == LANGUAGE_AMERICAN) CFont::SetScale(SCREEN_SCALE_X(0.55f), SCREEN_SCALE_Y(0.55f)); else CFont::SetScale(SCREEN_SCALE_X(0.45f), SCREEN_SCALE_Y(0.55f)); CFont::SetColor(CRGBA(225, 225, 225, FadeIn(255))); CFont::SetDropShadowPosition(0); int y = MENU_Y(127.0f); int x = MENU_X_LEFT_ALIGNED(160.0f); for (int16 i = 0; i < CRadar::MapLegendCounter; i++) { CRadar::DrawLegend(x, y, CRadar::MapLegendList[i]); if (i == secondColumnStart) { x = MENU_X_LEFT_ALIGNED(350.0f); y = MENU_Y(127.0f); } else { y += MENU_Y(19.0f); } } } #ifdef MAP_ENHANCEMENTS if (m_nMenuFadeAlpha != 255 && !m_bShowMouse) { mapCrosshair.x = SCREEN_WIDTH / 2; mapCrosshair.y = SCREEN_HEIGHT / 2; } else if (m_bShowMouse) { mapCrosshair.x = m_nMousePosX; mapCrosshair.y = m_nMousePosY; } CSprite2d::DrawRect(CRect(mapCrosshair.x - MENU_X(1.0f), 0.0f, mapCrosshair.x + MENU_X(1.0f), SCREEN_HEIGHT), CRGBA(0, 0, 0, 150)); CSprite2d::DrawRect(CRect(0.0f, mapCrosshair.y + MENU_X(1.0f), SCREEN_WIDTH, mapCrosshair.y - MENU_X(1.0f)), CRGBA(0, 0, 0, 150)); #endif m_bMenuMapActive = false; CFont::SetWrapx(MENU_X_RIGHT_ALIGNED(MENU_X_MARGIN)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(MENU_X_MARGIN)); DisplayHelperText("FEH_MPH"); } void CMenuManager::ChangeRadioStation(int8 increaseBy) { if (m_ScrollRadioBy != 0) return; m_PrefsRadioStation += increaseBy; m_ScrollRadioBy = increaseBy; if (m_ScrollRadioBy == 1) { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); m_LeftMostRadioX = MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X); } else { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_ENTER_OR_ADJUST, 0); m_LeftMostRadioX = MENU_X_LEFT_ALIGNED(MENURADIO_ICON_FIRST_X - (2 * MENURADIO_ICON_SIZE)); } if (DMAudio.IsMP3RadioChannelAvailable()) { if (m_PrefsRadioStation < WILDSTYLE) m_PrefsRadioStation = USERTRACK; if (m_PrefsRadioStation > USERTRACK) m_PrefsRadioStation = WILDSTYLE; } else { if (m_PrefsRadioStation < WILDSTYLE) m_PrefsRadioStation = WAVE; if (m_PrefsRadioStation > WAVE) m_PrefsRadioStation = WILDSTYLE; } DMAudio.StopFrontEndTrack(); DMAudio.PlayFrontEndSound(SOUND_RADIO_CHANGE, 0); } #if 0 uint8 CMenuManager::GetNumberOfMenuOptions() { uint8 Rows = -1; for (int i = 0; i < NUM_MENUROWS; i++) { if (aScreens[m_nCurrScreen].m_aEntries[i].m_Action == MENUACTION_NOTHING) break; ++Rows; } return Rows; } #endif #ifdef GAMEPAD_MENU const char* controllerTypesPaths[] = { "MODELS/FRONTEND_DS2.TXD", "MODELS/FRONTEND_DS3.TXD", "MODELS/FRONTEND_DS4.TXD", "MODELS/FRONTEND_X360.TXD", "MODELS/FRONTEND_XONE.TXD", }; void CMenuManager::PrintController(void) { // Don't print anything if controller texture is missing if (!m_aFrontEndSprites[MENUSPRITE_CONTROLLER].m_pTexture) return; const float scale = 0.9f; const float CONTROLLER_SIZE_X = 235.2f; const float CONTROLLER_SIZE_Y = 175.2f; const float CONTROLLER_POS_X = (DEFAULT_SCREEN_WIDTH - CONTROLLER_SIZE_X) / 2.0f; const float CONTROLLER_POS_Y = 220.0f; float centerX = CONTROLLER_POS_X + CONTROLLER_SIZE_X / 2; float centerY = CONTROLLER_POS_Y + CONTROLLER_SIZE_Y / 2; #define X(f) ((f)*scale + centerX) #define Y(f) ((f)*scale + centerY) m_aFrontEndSprites[MENUSPRITE_CONTROLLER].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); if (m_DisplayControllerOnFoot) { if ((int)CTimer::GetTimeInMillisecondsPauseMode() & 0x400) m_aFrontEndSprites[MENUSPRITE_ARROWS1].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); else m_aFrontEndSprites[MENUSPRITE_ARROWS3].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); } else { if ((int)CTimer::GetTimeInMillisecondsPauseMode() & 0x400) m_aFrontEndSprites[MENUSPRITE_ARROWS2].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); else m_aFrontEndSprites[MENUSPRITE_ARROWS4].Draw(MENU_X_LEFT_ALIGNED(X(-CONTROLLER_SIZE_X / 2)), MENU_Y(Y(-CONTROLLER_SIZE_Y / 2)), MENU_X(CONTROLLER_SIZE_X * scale), MENU_Y(CONTROLLER_SIZE_Y * scale), CRGBA(255, 255, 255, FadeIn(255))); } CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.9f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.9f)); // X CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetDropShadowPosition(0); CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); CFont::SetWrapx(SCREEN_WIDTH); float TEXT_L2_X = 85.0f + CONTROLLER_POS_X - centerX, TEXT_L2_Y = -14.0f + CONTROLLER_POS_Y - centerY; float TEXT_L1_X = -4.0f + CONTROLLER_POS_X - centerX, TEXT_L1_Y = 27.0f + CONTROLLER_POS_Y - centerY, TEXT_L1_Y_VEH = 3.0f + TEXT_L1_Y; float TEXT_DPAD_X = -4.0f + CONTROLLER_POS_X - centerX, TEXT_DPAD_Y = 67.0f + CONTROLLER_POS_Y - centerY; float TEXT_LSTICK_X = -4.0f + CONTROLLER_POS_X - centerX, TEXT_LSTICK_Y = 97.0f + CONTROLLER_POS_Y - centerY; float TEXT_SELECT_X = 170.0f + CONTROLLER_POS_X - centerX, TEXT_SELECT_Y = 141.0f + CONTROLLER_POS_Y - centerY; float TEXT_START_X = 130.0f + CONTROLLER_POS_X - centerX, TEXT_START_Y = 128.0f + CONTROLLER_POS_Y - centerY; float TEXT_R2_X = 164.0f + CONTROLLER_POS_X - centerX, TEXT_R2_Y = -14.0f + CONTROLLER_POS_Y - centerY; float TEXT_R1_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_R1_Y = 27.0f + CONTROLLER_POS_Y - centerY; float TEXT_SQUARE_X = 147.0f + CONTROLLER_POS_X - centerX, TEXT_SQUARE_Y = 30.0f + CONTROLLER_POS_Y - centerY; float TEXT_TRIANGLE_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_TRIANGLE_Y = 55.0f + CONTROLLER_POS_Y - centerY; float TEXT_CIRCLE_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_CIRCLE_Y = 67.0f + CONTROLLER_POS_Y - centerY; float TEXT_CROSS_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_CROSS_Y = 80.0f + CONTROLLER_POS_Y - centerY; float TEXT_RSTICK_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_RSTICK_Y = 97.0f + CONTROLLER_POS_Y - centerY; float TEXT_R3_X = 242.0f + CONTROLLER_POS_X - centerX, TEXT_R3_Y = 110.0f + CONTROLLER_POS_Y - centerY; float TEXT_L3_X = 94.0f + CONTROLLER_POS_X - centerX, TEXT_L3_Y = 162.0f + CONTROLLER_POS_Y - centerY; float TEXT_L2R2_X = 120.0f + CONTROLLER_POS_X - centerX, TEXT_L2R2_Y = -4.0f + CONTROLLER_POS_Y - centerY; switch (m_PrefsControllerType) { case CONTROLLER_DUALSHOCK4: TEXT_L1_Y += 7.0f; TEXT_L1_Y_VEH = TEXT_L1_Y; TEXT_R1_Y += 7.0f; TEXT_TRIANGLE_Y -= 1.0f; TEXT_CIRCLE_Y -= 1.0f; TEXT_CROSS_Y -= 1.0f; TEXT_RSTICK_Y -= 4.0f; TEXT_R3_Y -= 4.0f; TEXT_DPAD_Y -= 2.0f; TEXT_LSTICK_Y -= 6.0f; TEXT_L3_X -= 2.0f; break; case CONTROLLER_XBOXONE: TEXT_L2_X -= 2.0f; TEXT_R2_X += 2.0f; TEXT_L1_Y += 15.0f; TEXT_L1_Y_VEH = TEXT_L1_Y; TEXT_R1_Y += 15.0f; TEXT_TRIANGLE_Y += 4.0f; TEXT_CIRCLE_Y += 4.0f; TEXT_CROSS_Y += 4.0f; TEXT_RSTICK_Y += 1.0f; TEXT_R3_Y += 1.0f; TEXT_DPAD_Y += 29.0f; TEXT_LSTICK_Y -= 20.0f; TEXT_L3_X -= 36.0f; TEXT_L2R2_Y += 5.0f; TEXT_SELECT_X += 4.0f; break; case CONTROLLER_XBOX360: TEXT_L2_X += 8.0f; TEXT_R2_X -= 8.0f; TEXT_L1_Y += 15.0f; TEXT_L1_Y_VEH = TEXT_L1_Y; TEXT_R1_Y += 15.0f; TEXT_TRIANGLE_Y += 4.0f; TEXT_CIRCLE_Y += 4.0f; TEXT_CROSS_Y += 4.0f; TEXT_RSTICK_Y += 4.0f; TEXT_R3_Y += 4.0f; TEXT_DPAD_Y += 30.0f; TEXT_LSTICK_Y -= 19.0f; TEXT_L3_X -= 36.0f; TEXT_L2R2_Y += 5.0f; TEXT_SELECT_X += 3.0f; break; }; if (m_DisplayControllerOnFoot) { switch (CPad::GetPad(0)->Mode) { case 0: CFont::SetRightJustifyOn(); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: case LANGUAGE_SPANISH: TEXT_L2_X -= 45.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)) - SCREEN_SCALE_X(85)); break; default: CFont::SetRightJustifyWrap(0); break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); CFont::SetRightJustifyWrap(0); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_LOF")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_MOV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_CR3")); CFont::SetRightJustifyOn(); switch (m_PrefsLanguage) { case LANGUAGE_GERMAN: TEXT_SELECT_X += 20.0f; break; case LANGUAGE_SPANISH: TEXT_SELECT_X += 15.0f; break; default: break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: case LANGUAGE_SPANISH: TEXT_R2_X += 30.0f; CFont::SetJustifyOff(); CFont::SetWrapx(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)) + SCREEN_SCALE_X(120)); break; default: break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); CFont::SetJustifyOn(); CFont::SetWrapx(SCREEN_WIDTH); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR")); CFont::SetRightJustifyOn(); CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_ATT")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); break; case 1: CFont::SetRightJustifyOn(); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: case LANGUAGE_SPANISH: TEXT_L2_X -= 45.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)) - SCREEN_SCALE_X(85)); break; default: CFont::SetRightJustifyWrap(0); break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); CFont::SetRightJustifyWrap(0); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_LOF")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_CAM")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_CR3")); switch (m_PrefsLanguage) { case LANGUAGE_GERMAN: CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_NA")); break; default: CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X - 50)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_NA")); break; } CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: case LANGUAGE_SPANISH: TEXT_R2_X += 30.0f; CFont::SetJustifyOff(); CFont::SetWrapx(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)) + SCREEN_SCALE_X(120)); break; default: break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); CFont::SetJustifyOn(); CFont::SetWrapx(SCREEN_WIDTH); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR")); CFont::SetRightJustifyOn(); CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_ATT")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); break; case 2: CFont::SetRightJustifyOn(); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: case LANGUAGE_SPANISH: TEXT_L2_X -= 45.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)) - SCREEN_SCALE_X(85)); break; default: CFont::SetRightJustifyWrap(0); break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); CFont::SetRightJustifyWrap(0); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_ENV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_MOV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_CR3")); CFont::SetRightJustifyOn(); switch (m_PrefsLanguage) { case LANGUAGE_GERMAN: TEXT_SELECT_X += 20.0f; break; case LANGUAGE_SPANISH: TEXT_SELECT_X += 15.0f; break; default: break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: case LANGUAGE_SPANISH: TEXT_R2_X += 30.0f; CFont::SetJustifyOff(); CFont::SetWrapx(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)) + SCREEN_SCALE_X(120)); break; default: break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); CFont::SetJustifyOn(); CFont::SetWrapx(SCREEN_WIDTH); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_TAR")); CFont::SetRightJustifyOn(); CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_LOF")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_RUN")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ATT")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); break; case 3: CFont::SetRightJustifyOn(); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: case LANGUAGE_SPANISH: TEXT_L2_X -= 45.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)) - SCREEN_SCALE_X(85)); break; default: CFont::SetRightJustifyWrap(0); break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_CWL")); CFont::SetRightJustifyWrap(0); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y)), TheText.Get("FEC_TAR")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_NA")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_MOV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_CR3")); CFont::SetRightJustifyOn(); switch (m_PrefsLanguage) { case LANGUAGE_GERMAN: TEXT_SELECT_X += 20.0f; break; case LANGUAGE_SPANISH: TEXT_SELECT_X += 15.0f; break; default: break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: case LANGUAGE_SPANISH: TEXT_R2_X += 30.0f; CFont::SetJustifyOff(); CFont::SetWrapx(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)) + SCREEN_SCALE_X(120)); break; default: break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_CWR")); CFont::SetJustifyOn(); CFont::SetWrapx(SCREEN_WIDTH); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_ATT")); CFont::SetRightJustifyOn(); CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_JUM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_ENV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_LOF")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_RUN")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_FPC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_LB3")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y + 13.0f)), TheText.Get("FEC_R3")); break; default: return; } } else { CFont::SetCentreOn(); switch (m_PrefsLanguage) { case LANGUAGE_ITALIAN: if (m_PrefsControllerType != CONTROLLER_XBOX360) break; case LANGUAGE_FRENCH: case LANGUAGE_GERMAN: CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); break; default: break; } CFont::PrintString(MENU_X_LEFT_ALIGNED(X(0.0f)), MENU_Y(Y(TEXT_L2R2_Y)), TheText.Get("FEC_LB")); CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.9f), MENU_Y(SMALLESTTEXT_Y_SCALE* scale * 0.9f)); switch (CPad::GetPad(0)->Mode) { case 0: CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_RSC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_VES")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_HO3")); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: TEXT_SELECT_X -= 5.0f; break; case LANGUAGE_GERMAN: TEXT_SELECT_X += 20.0f; break; case LANGUAGE_SPANISH: TEXT_SELECT_X += 15.0f; break; default: break; } CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)) - SCREEN_SCALE_X(80)); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HAB")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3")); CFont::SetRightJustifyOn(); switch (m_PrefsControllerType) { case CONTROLLER_XBOXONE: case CONTROLLER_XBOX360: CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: TEXT_SQUARE_X += 3.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); break; case LANGUAGE_GERMAN: case LANGUAGE_SPANISH: TEXT_SQUARE_X += 18.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); break; default: CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); break; } break; default: switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: TEXT_SQUARE_X -= 15.0f; TEXT_SQUARE_Y += 5.0f; CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); break; case LANGUAGE_GERMAN: TEXT_SQUARE_X -= 15.0f; TEXT_SQUARE_Y += 10.0f; CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE* scale * 0.65f)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); break; case LANGUAGE_SPANISH: TEXT_SQUARE_X += 15.0f; case LANGUAGE_ITALIAN: CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); default: CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(100)); break; } break; } CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA")); break; case 1: CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_HOR")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_CAM")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_NA")); switch (m_PrefsLanguage) { case LANGUAGE_GERMAN: TEXT_SELECT_X += 20.0f; break; case LANGUAGE_SPANISH: TEXT_SELECT_X += 12.0f; break; default: break; } CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)) - SCREEN_SCALE_X(80)); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_RSC")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HAB")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3")); CFont::SetRightJustifyOn(); switch (m_PrefsControllerType) { case CONTROLLER_XBOXONE: case CONTROLLER_XBOX360: CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: TEXT_SQUARE_X += 3.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); break; case LANGUAGE_GERMAN: case LANGUAGE_SPANISH: TEXT_SQUARE_X += 18.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); break; default: CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); break; } break; default: switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: TEXT_SQUARE_X -= 15.0f; TEXT_SQUARE_Y += 5.0f; CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); break; case LANGUAGE_GERMAN: TEXT_SQUARE_X -= 15.0f; TEXT_SQUARE_Y += 10.0f; CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); break; case LANGUAGE_SPANISH: TEXT_SQUARE_X += 15.0f; case LANGUAGE_ITALIAN: CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); default: CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(100)); break; } break; } CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA")); break; case 2: CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_EXV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_VES")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_RS3")); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: TEXT_SELECT_X -= 5.0f; break; case LANGUAGE_GERMAN: TEXT_SELECT_X += 20.0f; break; case LANGUAGE_SPANISH: TEXT_SELECT_X += 15.0f; break; default: break; } CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)) - SCREEN_SCALE_X(80)); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_HOR")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_HAB")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_CAW")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_ACC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_TUC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_SM3")); CFont::SetRightJustifyOn(); switch (m_PrefsControllerType) { case CONTROLLER_XBOXONE: case CONTROLLER_XBOX360: CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: TEXT_SQUARE_X += 3.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); break; case LANGUAGE_GERMAN: case LANGUAGE_SPANISH: TEXT_SQUARE_X += 18.0f; CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(90)); break; default: CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); break; } break; default: switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: TEXT_SQUARE_X -= 15.0f; TEXT_SQUARE_Y += 5.0f; CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); break; case LANGUAGE_GERMAN: TEXT_SQUARE_X -= 15.0f; TEXT_SQUARE_Y += 10.0f; CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(60)); break; case LANGUAGE_SPANISH: TEXT_SQUARE_X += 15.0f; case LANGUAGE_ITALIAN: CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); default: CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)) - SCREEN_SCALE_X(100)); break; } break; } CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_BRA")); break; case 3: CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L2_X)), MENU_Y(Y(TEXT_L2_Y)), TheText.Get("FEC_LL")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L1_X)), MENU_Y(Y(TEXT_L1_Y_VEH)), TheText.Get("FEC_HAB")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_DPAD_X)), MENU_Y(Y(TEXT_DPAD_Y)), TheText.Get("FEC_TUC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_LSTICK_X)), MENU_Y(Y(TEXT_LSTICK_Y)), TheText.Get("FEC_VES")); CFont::SetRightJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_L3_X)), MENU_Y(Y(TEXT_L3_Y)), TheText.Get("FEC_HO3")); switch (m_PrefsLanguage) { case LANGUAGE_FRENCH: TEXT_SELECT_X -= 5.0f; break; case LANGUAGE_GERMAN: TEXT_SELECT_X += 20.0f; break; case LANGUAGE_SPANISH: TEXT_SELECT_X += 15.0f; break; default: break; } CFont::SetRightJustifyWrap(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)) - SCREEN_SCALE_X(80)); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_SELECT_X)), MENU_Y(Y(TEXT_SELECT_Y)), TheText.Get("FEC_CAM")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_START_X)), MENU_Y(Y(TEXT_START_Y)), TheText.Get("FEC_PAU")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R2_X)), MENU_Y(Y(TEXT_R2_Y)), TheText.Get("FEC_LR")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R1_X)), MENU_Y(Y(TEXT_R1_Y)), TheText.Get("FEC_CAW")); CFont::SetJustifyOn(); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_TRIANGLE_X)), MENU_Y(Y(TEXT_TRIANGLE_Y)), TheText.Get("FEC_EXV")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CIRCLE_X)), MENU_Y(Y(TEXT_CIRCLE_Y)), TheText.Get("FEC_RSC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_CROSS_X)), MENU_Y(Y(TEXT_CROSS_Y)), TheText.Get("FEC_NA")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_RSTICK_X)), MENU_Y(Y(TEXT_RSTICK_Y)), TheText.Get("FEC_ACC")); CFont::PrintString(MENU_X_LEFT_ALIGNED(X(TEXT_R3_X)), MENU_Y(Y(TEXT_R3_Y)), TheText.Get("FEC_BRA")); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0); switch (m_PrefsControllerType) { case CONTROLLER_XBOXONE: case CONTROLLER_XBOX360: CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_SMT")); break; default: switch (m_PrefsLanguage) { case LANGUAGE_GERMAN: TEXT_SQUARE_X += 5.0f; case LANGUAGE_FRENCH: case LANGUAGE_ITALIAN: CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE * 2 * scale * 0.65f), MENU_Y(SMALLESTTEXT_Y_SCALE * scale * 0.65f)); CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_SMT")); break; default: CFont::PrintStringFromBottom(MENU_X_LEFT_ALIGNED(X(TEXT_SQUARE_X + 16.0f)), MENU_Y(Y(TEXT_SQUARE_Y)), TheText.Get("FEC_SMT")); break; } break; } break; default: return; } } CFont::SetDropShadowPosition(0); // X #undef X #undef Y } void CMenuManager::LoadController(int8 type) { switch (type) { case CONTROLLER_DUALSHOCK2: case CONTROLLER_DUALSHOCK3: case CONTROLLER_DUALSHOCK4: CFont::LoadButtons("MODELS/PS3BTNS.TXD"); break; default: CFont::LoadButtons("MODELS/X360BTNS.TXD"); break; } // Unload current textures for (int i = MENUSPRITE_CONTROLLER; i <= MENUSPRITE_ARROWS4; i++) m_aFrontEndSprites[i].Delete(); // Unload txd int frontend_controller = CTxdStore::FindTxdSlot("frontend_controller"); if (frontend_controller != -1) CTxdStore::RemoveTxd(frontend_controller); // Find the new txd to load bool bTxdMissing = true; if (controllerTypesPaths[type]) if (int file = CFileMgr::OpenFile(controllerTypesPaths[type])) { CFileMgr::CloseFile(file); bTxdMissing = false; } int txdSlot = -1; if (bTxdMissing) // Not found, fall back to original textures txdSlot = CTxdStore::FindTxdSlot("frontend2"); else { // Found, load txd txdSlot = frontend_controller; if (txdSlot == -1) txdSlot = CTxdStore::AddTxdSlot("frontend_controller"); CTxdStore::LoadTxd(txdSlot, controllerTypesPaths[type]); CTxdStore::AddRef(txdSlot); } assert(txdSlot != -1); // Load new textures CTxdStore::SetCurrentTxd(txdSlot); for (int i = MENUSPRITE_CONTROLLER; i <= MENUSPRITE_ARROWS4; i++) { m_aFrontEndSprites[i].SetTexture(FrontendFilenames[i][0], FrontendFilenames[i][1]); m_aFrontEndSprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); } } #endif // GAMEPAD_MENU #undef GetBackJustUp #undef GetBackJustDown ================================================ FILE: src/core/Frontend.h ================================================ #pragma once #ifdef PS2_MENU #include "Frontend_PS2.h" #else #include "Sprite2d.h" #include "Timer.h" #define MENUHEADER_POS_X 10.0f #define MENUHEADER_POS_Y 10.0f #define MENUHEADER_HEIGHT 2.0f #define MENUHEADER_WIDTH 1.0f #define MENU_X_MARGIN 10.0f #define MENUACTION_SCALE_MULT 0.9f #define MENULABEL_X_MARGIN 80.0f #define MENULABEL_POS_X 100.0f #define MENULABEL_POS_Y 97.0f #define MENU_DEFAULT_CONTENT_X 320 #define MENU_DEFAULT_CONTENT_Y 100 #define MENU_DEFAULT_LINE_HEIGHT 29 #define RIGHT_ALIGNED_TEXT_RIGHT_MARGIN(xMargin) (xMargin + 30.0f) #define MENURADIO_ICON_FIRST_X 238.f #define MENURADIO_ICON_Y 288.0f #define MENURADIO_ICON_SIZE 60.0f #define MENURADIO_SELECTOR_START_Y 285.f // other options should leave room on the screen #define MENURADIO_SELECTOR_HEIGHT 65.f #define MENUSLIDER_X 500.0f #define MENUSLIDER_UNK 100.0f #define MENUSLIDER_SMALLEST_BAR 8.0f #define MENUSLIDER_BIGGEST_BAR 25.0f #define BIGTEXT2_X_SCALE 0.6f // For FONT_STANDARD #define BIGTEXT2_Y_SCALE 1.2f #define BIGTEXT_X_SCALE 0.6f // For FONT_HEADING #define BIGTEXT_Y_SCALE 1.0f #define MEDIUMTEXT_X_SCALE 0.48f // For FONT_STANDARD #define MEDIUMTEXT_Y_SCALE 1.0f #define SMALLTEXT_X_SCALE 0.42f // For FONT_STANDARD #define SMALLTEXT_Y_SCALE 0.9f #define SMALLESTTEXT_X_SCALE 0.3f // For FONT_STANDARD #define SMALLESTTEXT_Y_SCALE 0.7f #define LISTITEM_X_SCALE 0.4f // Only unproportional and commonly used scale for FONT_STANDARD #define LISTITEM_Y_SCALE 0.6f #define HELPER_TEXT_RIGHT_MARGIN MENU_X_MARGIN #define HELPER_TEXT_BOTTOM_MARGIN 18.f #define PLAYERSETUP_LIST_TOP 58.0f #define PLAYERSETUP_LIST_BOTTOM 95.0f #define PLAYERSETUP_LIST_LEFT 200.0f #define PLAYERSETUP_LIST_RIGHT 36.0f #ifdef FIX_BUGS // See the scrollbar button drawing code #define PLAYERSETUP_SCROLLBAR_WIDTH 19.0f #else #define PLAYERSETUP_SCROLLBAR_WIDTH 16.0f #endif #define PLAYERSETUP_SCROLLBUTTON_HEIGHT 17.0f #define PLAYERSETUP_SCROLLBUTTON_TXD_DIMENSION 64 #define PLAYERSETUP_SKIN_COLUMN_LEFT 220.0f #define PLAYERSETUP_DATE_COLUMN_RIGHT 56.0f #define PLAYERSETUP_LIST_BODY_TOP 77 #define PLAYERSETUP_ROW_HEIGHT 9 #define STATS_ROW_HEIGHT 17.0f #define STATS_ROW_LEFT_MARGIN 110.0f #define STATS_ROW_RIGHT_MARGIN 113.0f #define STATS_TOP_Y 135.0f // Just faded in #define STATS_BOTTOM_Y 300.0f // Starts to fade out after that #define STATS_FADING_AREA_LENGTH 50.0f #define STATS_VISIBLE_START_Y (STATS_TOP_Y - 10.f) #define STATS_VISIBLE_END_Y (STATS_BOTTOM_Y + 21.f) #define STATS_RATING_X 320.0f #define STATS_RATING_Y_1 85.0f #define STATS_RATING_Y_2 110.0f #define BRIEFS_TOP_MARGIN 140.0f #define BRIEFS_BOTTOM_MARGIN 280.0f #define BRIEFS_LINE_X 100.0f #define BRIEFS_LINE_HEIGHT 20.0f #define BRIEFS_LINE_SPACING 10.0f #define CONTSETUP_STANDARD_ROW_HEIGHT 10.7f #define CONTSETUP_CLASSIC_ROW_HEIGHT 9.0f #define CONTSETUP_BOUND_HIGHLIGHT_HEIGHT 10 #define CONTSETUP_BOUND_COLUMN_WIDTH 190.0f #define CONTSETUP_LIST_TOP 58.0f #define CONTSETUP_LIST_RIGHT 18.0f #define CONTSETUP_LIST_BOTTOM 78.0f #define CONTSETUP_LIST_LEFT 30.0f #define CONTSETUP_COLUMN_1_X 40.0f #define CONTSETUP_COLUMN_2_X 210.0f #define CONTSETUP_COLUMN_3_X (CONTSETUP_COLUMN_2_X + CONTSETUP_BOUND_COLUMN_WIDTH + 10.0f) #define CONTSETUP_BACK_RIGHT 35.0f #define CONTSETUP_BACK_BOTTOM 82.0f #define CONTSETUP_BACK_HEIGHT 25.0f enum { MENUALIGN_LEFT = 1, MENUALIGN_RIGHT, MENUALIGN_CENTER, }; enum eMenuSprites { MENUSPRITE_BACKGROUND, MENUSPRITE_VCLOGO, MENUSPRITE_MOUSE, MENUSPRITE_MAPTOP01, MENUSPRITE_MAPTOP02, MENUSPRITE_MAPTOP03, MENUSPRITE_MAPMID01, MENUSPRITE_MAPMID02, MENUSPRITE_MAPMID03, MENUSPRITE_MAPBOT01, MENUSPRITE_MAPBOT02, MENUSPRITE_MAPBOT03, MENUSPRITE_WILDSTYLE, MENUSPRITE_FLASH, MENUSPRITE_KCHAT, MENUSPRITE_FEVER, MENUSPRITE_VROCK, MENUSPRITE_VCPR, MENUSPRITE_ESPANTOSO, MENUSPRITE_EMOTION, MENUSPRITE_WAVE, MENUSPRITE_MP3, MENUSPRITE_DOWNOFF, MENUSPRITE_DOWNON, MENUSPRITE_UPOFF, MENUSPRITE_UPON, #ifdef GAMEPAD_MENU MENUSPRITE_CONTROLLER, MENUSPRITE_ARROWS1, MENUSPRITE_ARROWS2, MENUSPRITE_ARROWS3, MENUSPRITE_ARROWS4, #endif NUM_MENU_SPRITES }; enum eSaveSlot { SAVESLOT_NONE, SAVESLOT_0, SAVESLOT_1, SAVESLOT_2, SAVESLOT_3, SAVESLOT_4, SAVESLOT_5, SAVESLOT_6, SAVESLOT_7, SAVESLOT_8, SAVESLOT_LABEL = 36 }; enum eMenuScreen { MENUPAGE_DISABLED = -1, MENUPAGE_STATS = 0, MENUPAGE_NEW_GAME = 1, MENUPAGE_BRIEFS = 2, MENUPAGE_SOUND_SETTINGS = 3, MENUPAGE_DISPLAY_SETTINGS = 4, MENUPAGE_LANGUAGE_SETTINGS = 5, MENUPAGE_MAP = 6, MENUPAGE_NEW_GAME_RELOAD = 7, MENUPAGE_CHOOSE_LOAD_SLOT = 8, MENUPAGE_CHOOSE_DELETE_SLOT = 9, MENUPAGE_LOAD_SLOT_CONFIRM = 10, MENUPAGE_DELETE_SLOT_CONFIRM = 11, MENUPAGE_LOADING_IN_PROGRESS = 12, MENUPAGE_DELETING_IN_PROGRESS = 13, MENUPAGE_DELETE_SUCCESSFUL = 14, MENUPAGE_CHOOSE_SAVE_SLOT = 15, MENUPAGE_SAVE_OVERWRITE_CONFIRM = 16, MENUPAGE_SAVING_IN_PROGRESS = 17, MENUPAGE_SAVE_SUCCESSFUL = 18, MENUPAGE_SAVE_CUSTOM_WARNING = 19, MENUPAGE_SAVE_CHEAT_WARNING = 20, MENUPAGE_SKIN_SELECT = 21, MENUPAGE_SAVE_UNUSED = 22, MENUPAGE_SAVE_FAILED = 23, MENUPAGE_SAVE_FAILED_2 = 24, MENUPAGE_LOAD_FAILED = 25, MENUPAGE_CONTROLLER_PC = 26, MENUPAGE_OPTIONS = 27, MENUPAGE_EXIT = 28, MENUPAGE_START_MENU = 29, MENUPAGE_KEYBOARD_CONTROLS = 30, MENUPAGE_MOUSE_CONTROLS = 31, MENUPAGE_PAUSE_MENU = 32, MENUPAGE_NONE = 33, // Then chooses main menu or pause menu #ifdef GAMEPAD_MENU MENUPAGE_CONTROLLER_SETTINGS, #endif #ifdef LEGACY_MENU_OPTIONS MENUPAGE_DEBUG_MENU, MENUPAGE_CONTROLLER_PC_OLD1, MENUPAGE_CONTROLLER_PC_OLD2, MENUPAGE_CONTROLLER_PC_OLD3, MENUPAGE_CONTROLLER_PC_OLD4, MENUPAGE_CONTROLLER_DEBUG, #endif #ifdef CUSTOM_FRONTEND_OPTIONS #ifdef GRAPHICS_MENU_OPTIONS MENUPAGE_GRAPHICS_SETTINGS, #endif #ifdef DETECT_JOYSTICK_MENU MENUPAGE_DETECT_JOYSTICK, #endif #endif MENUPAGE_OUTRO, // Originally 34, but CFO needs last screen to be empty to count number of menu pages MENUPAGES }; enum eMenuAction { #ifdef CUSTOM_FRONTEND_OPTIONS MENUACTION_CFO_SELECT = -2, MENUACTION_CFO_DYNAMIC = -1, #endif MENUACTION_NOTHING, MENUACTION_LABEL, MENUACTION_YES, MENUACTION_NO, MENUACTION_CHANGEMENU, MENUACTION_INVERTPADY, MENUACTION_CTRLDISPLAY, MENUACTION_FRAMESYNC, MENUACTION_FRAMELIMIT, MENUACTION_TRAILS, MENUACTION_SUBTITLES, MENUACTION_WIDESCREEN, MENUACTION_BRIGHTNESS, MENUACTION_MUSICVOLUME, MENUACTION_SFXVOLUME, MENUACTION_RADIO, MENUACTION_LANG_ENG, MENUACTION_LANG_FRE, MENUACTION_LANG_GER, MENUACTION_LANG_ITA, MENUACTION_LANG_SPA, MENUACTION_POPULATESLOTS_CHANGEMENU, MENUACTION_CHECKSAVE, MENUACTION_NEWGAME, MENUACTION_RESUME_FROM_SAVEZONE, MENUACTION_RELOADIDE, MENUACTION_SETDBGFLAG, MENUACTION_LOADRADIO, MENUACTION_SAVEGAME, MENUACTION_SWITCHBIGWHITEDEBUGLIGHT, MENUACTION_COLLISIONPOLYS, MENUACTION_LEGENDS, MENUACTION_RADARMODE, MENUACTION_HUD, MENUACTION_GOBACK, MENUACTION_KEYBOARDCTRLS, MENUACTION_GETKEY, MENUACTION_SHOWHEADBOB, MENUACTION_UNK38, // MENUACTION_PARSEHEAP? MENUACTION_DEBUGSTREAM? MENUACTION_MEMCARDSAVECONFIRM? MENUACTION_INVVERT, MENUACTION_CANCELGAME, MENUACTION_RESUME, MENUACTION_DONTCANCEL, MENUACTION_SCREENRES, MENUACTION_AUDIOHW, MENUACTION_SPEAKERCONF, MENUACTION_PLAYERSETUP, MENUACTION_RESTOREDEF, MENUACTION_CTRLMETHOD, MENUACTION_DYNAMICACOUSTIC, MENUACTION_MOUSESTEER, MENUACTION_DRAWDIST, MENUACTION_MOUSESENS, MENUACTION_MP3VOLUMEBOOST, #ifdef GAMEPAD_MENU MENUACTION_CTRLVIBRATION, MENUACTION_CTRLCONFIG, #endif }; enum eCheckHover { HOVEROPTION_0, HOVEROPTION_1, HOVEROPTION_RANDOM_ITEM, HOVEROPTION_3, HOVEROPTION_4, HOVEROPTION_5, HOVEROPTION_6, HOVEROPTION_7, HOVEROPTION_8, HOVEROPTION_BACK, // also layer in controller setup and skin menu HOVEROPTION_10, HOVEROPTION_11, HOVEROPTION_OVER_SCROLL_UP, HOVEROPTION_OVER_SCROLL_DOWN, HOVEROPTION_CLICKED_SCROLL_UP, HOVEROPTION_CLICKED_SCROLL_DOWN, HOVEROPTION_HOLDING_SCROLLBAR, HOVEROPTION_PAGEUP, HOVEROPTION_PAGEDOWN, HOVEROPTION_LIST, // also layer in controller setup and skin menu HOVEROPTION_SKIN, HOVEROPTION_USESKIN, // also layer in controller setup and skin menu HOVEROPTION_NEXT_RADIO, HOVEROPTION_PREV_RADIO, HOVEROPTION_INCREASE_BRIGHTNESS, HOVEROPTION_DECREASE_BRIGHTNESS, HOVEROPTION_INCREASE_DRAWDIST, HOVEROPTION_DECREASE_DRAWDIST, HOVEROPTION_INCREASE_MUSICVOLUME, HOVEROPTION_DECREASE_MUSICVOLUME, HOVEROPTION_INCREASE_SFXVOLUME, HOVEROPTION_DECREASE_SFXVOLUME, HOVEROPTION_INCREASE_MOUSESENS, HOVEROPTION_DECREASE_MOUSESENS, HOVEROPTION_INCREASE_MP3BOOST, HOVEROPTION_DECREASE_MP3BOOST, HOVEROPTION_NOT_HOVERING, }; enum { #if defined LEGACY_MENU_OPTIONS || defined CUSTOM_FRONTEND_OPTIONS NUM_MENUROWS = 18, #else NUM_MENUROWS = 12, #endif }; enum eControlMethod { CONTROL_STANDARD = 0, CONTROL_CLASSIC, }; // Why?? enum ControllerSetupColumn { CONTSETUP_PED_COLUMN = 0, CONTSETUP_VEHICLE_COLUMN = 16, }; struct tSkinInfo { int32 skinId; char skinNameDisplayed[256]; char skinNameOriginal[256]; char date[256]; tSkinInfo *nextSkin; }; struct BottomBarOption { char name[8]; int32 screenId; }; #ifndef CUSTOM_FRONTEND_OPTIONS struct CMenuScreen { char m_ScreenName[8]; int32 m_PreviousPage; // eMenuScreen int32 m_ParentEntry; // row struct CMenuEntry { int32 m_Action; // eMenuAction char m_EntryName[8]; int32 m_SaveSlot; // eSaveSlot int32 m_TargetMenu; // eMenuScreen uint16 m_X; uint16 m_Y; uint8 m_Align; } m_aEntries[NUM_MENUROWS]; }; extern CMenuScreen aScreens[MENUPAGES]; #else #include "frontendoption.h" struct CCustomScreenLayout { int startX; // not used at all if first entry has X and Y values int startY; // not used at all if first entry has X and Y values int lineHeight; // used to determine next entry's Y coordinate, if it has 0-0 as coordinates bool showLeftRightHelper; bool noInvasiveBorders; // not needed on pages already handled by game int xMargin; // useful for two part texts - 0/empty = MENU_X_MARGIN }; struct CCFO { int8 *value; const char *saveCat; const char *save; }; struct CCFOSelect : CCFO { char** rightTexts; int8 numRightTexts; bool onlyApplyOnEnter; int8 displayedValue; // only if onlyApplyOnEnter enabled for now int8 lastSavedValue; // only if onlyApplyOnEnter enabled ChangeFunc changeFunc; bool disableIfGameLoaded; CCFOSelect() {}; CCFOSelect(int8* value, const char* saveCat, const char* save, const char** rightTexts, int8 numRightTexts, bool onlyApplyOnEnter, ChangeFunc changeFunc = nil, bool disableIfGameLoaded = false){ this->value = value; if (value) this->lastSavedValue = this->displayedValue = *value; this->saveCat = saveCat; this->save = save; this->rightTexts = (char**)rightTexts; this->numRightTexts = numRightTexts; this->onlyApplyOnEnter = onlyApplyOnEnter; this->changeFunc = changeFunc; this->disableIfGameLoaded = disableIfGameLoaded; } }; struct CCFODynamic : CCFO { DrawFunc drawFunc; ButtonPressFunc buttonPressFunc; CCFODynamic() {}; CCFODynamic(int8* value, const char* saveCat, const char* save, DrawFunc drawFunc, ButtonPressFunc buttonPressFunc){ this->value = value; this->saveCat = saveCat; this->save = save; this->drawFunc = drawFunc; this->buttonPressFunc = buttonPressFunc; } }; struct CMenuScreenCustom { char m_ScreenName[8]; int32 m_PreviousPage; // eMenuScreen CCustomScreenLayout *layout; ReturnPrevPageFunc returnPrevPageFunc; struct CMenuEntry { int32 m_Action; // eMenuAction - below zero is CFO char m_EntryName[8]; struct { union { CCFO *m_CFO; // for initializing CCFOSelect *m_CFOSelect; CCFODynamic *m_CFODynamic; }; int32 m_SaveSlot; // eSaveSlot int32 m_TargetMenu; // eMenuScreen }; uint16 m_X; uint16 m_Y; uint8 m_Align; } m_aEntries[NUM_MENUROWS]; }; extern CMenuScreenCustom aScreens[MENUPAGES]; #endif struct MenuTrapezoid { float topLeft_x; float topLeft_y; float topRight_x; float topRight_y; float bottomLeft_x; float bottomLeft_y; float bottomRight_x; float bottomRight_y; float old_topRight_x; float old_topRight_y; float old_topLeft_x; float old_topLeft_y; float old_bottomLeft_x; float old_bottomLeft_y; float old_bottomRight_x; float old_bottomRight_y; float mult_topRight_x; float mult_topRight_y; float mult_topLeft_x; float mult_topLeft_y; float mult_bottomLeft_x; float mult_bottomLeft_y; float mult_bottomRight_x; float mult_bottomRight_y; MenuTrapezoid(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { topLeft_x = x1; topLeft_y = y1; topRight_x = x2; topRight_y = y2; bottomLeft_x = x3; bottomLeft_y = y3; bottomRight_x = x4; bottomRight_y = y4; }; void SaveCurrentCoors() { old_topLeft_x = topLeft_x; old_topLeft_y = topLeft_y; old_topRight_x = topRight_x; old_topRight_y = topRight_y; old_bottomLeft_x = bottomLeft_x; old_bottomLeft_y = bottomLeft_y; old_bottomRight_x = bottomRight_x; old_bottomRight_y = bottomRight_y; } void Translate(int delta) { bottomRight_x = delta * mult_bottomRight_x + old_bottomRight_x; bottomRight_y = delta * mult_bottomRight_y + old_bottomRight_y; bottomLeft_x = delta * mult_bottomLeft_x + old_bottomLeft_x; bottomLeft_y = delta * mult_bottomLeft_y + old_bottomLeft_y; topRight_x = delta * mult_topRight_x + old_topRight_x; topRight_y = delta * mult_topRight_y + old_topRight_y; topLeft_x = delta * mult_topLeft_x + old_topLeft_x; topLeft_y = delta * mult_topLeft_y + old_topLeft_y; } void UpdateMultipliers() { mult_bottomRight_x = (bottomRight_x - old_bottomRight_x) / 255.0f; mult_bottomRight_y = (bottomRight_y - old_bottomRight_y) / 255.0f; mult_bottomLeft_x = (bottomLeft_x - old_bottomLeft_x) / 255.0f; mult_bottomLeft_y = (bottomLeft_y - old_bottomLeft_y) / 255.0f; mult_topRight_x = (topRight_x - old_topRight_x) / 255.0f; mult_topRight_y = (topRight_y - old_topRight_y) / 255.0f; mult_topLeft_x = (topLeft_x - old_topLeft_x) / 255.0f; mult_topLeft_y = (topLeft_y - old_topLeft_y) / 255.0f; } }; class CMenuManager { public: int8 m_StatsScrollDirection; float m_StatsScrollSpeed; uint8 field_8; bool m_PrefsUseVibration; bool m_PrefsShowHud; int32 m_PrefsRadarMode; bool m_DisplayControllerOnFoot; bool m_bShutDownFrontEndRequested; bool m_bStartUpFrontEndRequested; int32 m_KeyPressedCode; int32 m_PrefsBrightness; float m_PrefsLOD; int8 m_PrefsShowSubtitles; int8 m_PrefsShowLegends; int8 m_PrefsUseWideScreen; int8 m_PrefsVsync; int8 m_PrefsVsyncDisp; int8 m_PrefsFrameLimiter; int8 m_nPrefsAudio3DProviderIndex; int8 m_PrefsSpeakers; int8 m_PrefsDMA; int8 m_PrefsSfxVolume; int8 m_PrefsMusicVolume; int8 m_PrefsRadioStation; uint8 m_PrefsStereoMono; // unused except restore settings int32 m_nCurrOption; bool m_bQuitGameNoCD; bool m_bMenuMapActive; bool m_AllowNavigation; uint8 field_37; bool m_bMenuActive; bool m_bWantToRestart; bool m_bFirstTime; bool m_bActivateSaveMenu; bool m_bWantToLoad; float m_fMapSize; float m_fMapCenterX; float m_fMapCenterY; uint32 OS_Language; int32 m_PrefsLanguage; int32 field_54; int8 m_bLanguageLoaded; uint8 m_PrefsAllowNastyGame; int8 m_PrefsMP3BoostVolume; int8 m_ControlMethod; int32 m_nPrefsVideoMode; int32 m_nDisplayVideoMode; int32 m_nMouseTempPosX; int32 m_nMouseTempPosY; bool m_bGameNotLoaded; int8 m_lastWorking3DAudioProvider; bool m_bFrontEnd_ReloadObrTxtGxt; int32 *pEditString; uint8 field_74[4]; int32 *pControlEdit; bool m_OnlySaveMenu; int32 m_firstStartCounter; CSprite2d m_aFrontEndSprites[NUM_MENU_SPRITES]; bool m_bSpritesLoaded; int32 m_LeftMostRadioX; int32 m_ScrollRadioBy; int32 m_nCurrScreen; int32 m_nPrevScreen; int32 m_nCurrSaveSlot; uint32 m_LastScreenSwitch; int32 m_nMenuFadeAlpha; int32 m_nOptionHighlightTransitionBlend; bool bMenuChangeOngoing; int32 MouseButtonJustClicked; int32 JoyButtonJustClicked; bool DisplayComboButtonErrMsg; bool m_NoEmptyBinding; bool m_ShowEmptyBindingError; int32 m_nHelperTextAlpha; bool m_bPressedPgUpOnList; bool m_bPressedPgDnOnList; bool m_bPressedUpOnList; bool m_bPressedDownOnList; bool m_bPressedScrollButton; uint8 field_129; uint8 field_12A; uint8 field_12B; int32 m_nMousePosX; int32 m_nMousePosY; int32 m_nMouseOldPosX; int32 m_nMouseOldPosY; int32 m_nHoverOption; bool m_bShowMouse; int32 m_nOptionMouseHovering; bool m_bStartWaitingForKeyBind; bool m_bWaitingForNewKeyBind; bool m_bKeyChangeNotProcessed; int32 m_CurrCntrlAction; uint8 field_150; uint8 field_151; uint8 field_152; uint8 field_153; int32 m_nSelectedContSetupColumn; bool m_bKeyIsOK; bool field_159; uint8 m_nCurrExLayer; char m_PrefsSkinFile[256]; char m_aSkinName[256]; uint8 field_35B; int32 m_nHelperTextMsgId; tSkinInfo m_pSkinListHead; tSkinInfo *m_pSelectedSkin; int32 m_nFirstVisibleRowOnList; float m_nScrollbarTopMargin; int32 m_nTotalListRow; int32 m_nSkinsTotal; uint8 field_67C[4]; int32 m_nSelectedListRow; bool m_bSkinsEnumerated; #ifdef IMPROVED_VIDEOMODE int32 m_nPrefsWidth; int32 m_nPrefsHeight; int32 m_nPrefsDepth; int32 m_nPrefsWindowed; int32 m_nPrefsSubsystem; int32 m_nSelectedScreenMode; #endif #ifdef MULTISAMPLING int8 m_nPrefsMSAALevel; int8 m_nDisplayMSAALevel; #endif #ifdef GAMEPAD_MENU enum { CONTROLLER_DUALSHOCK2 = 0, CONTROLLER_DUALSHOCK3, CONTROLLER_DUALSHOCK4, CONTROLLER_XBOX360, CONTROLLER_XBOXONE, }; int8 m_PrefsControllerType; #endif enum LANGUAGE { LANGUAGE_AMERICAN, LANGUAGE_FRENCH, LANGUAGE_GERMAN, LANGUAGE_ITALIAN, LANGUAGE_SPANISH, #ifdef MORE_LANGUAGES LANGUAGE_POLISH, LANGUAGE_RUSSIAN, LANGUAGE_JAPANESE, #endif }; bool GetIsMenuActive() {return !!m_bMenuActive;} #ifdef CUTSCENE_BORDERS_SWITCH static bool m_PrefsCutsceneBorders; #endif #ifndef MASTER static bool m_PrefsMarketing; static bool m_PrefsDisableTutorials; #endif // !MASTER CMenuManager(void); ~CMenuManager(void) { UnloadTextures(); } #ifdef NO_ISLAND_LOADING enum { ISLAND_LOADING_LOW = 0, ISLAND_LOADING_MEDIUM, ISLAND_LOADING_HIGH }; int8 m_PrefsIslandLoading; #define ISLAND_LOADING_IS(p) if (FrontEndMenuManager.m_PrefsIslandLoading == CMenuManager::ISLAND_LOADING_##p) #define ISLAND_LOADING_ISNT(p) if (FrontEndMenuManager.m_PrefsIslandLoading != CMenuManager::ISLAND_LOADING_##p) #else #define ISLAND_LOADING_IS(p) #define ISLAND_LOADING_ISNT(p) #endif #ifdef XBOX_MESSAGE_SCREEN static uint32 m_nDialogHideTimer; static PauseModeTime m_nDialogHideTimerPauseMode; static bool m_bDialogOpen; static wchar *m_pDialogText; static bool m_bSaveWasSuccessful; static void SetDialogText(const char*); static bool DialogTextCmp(const char*); static void ToggleDialog(bool); static void SetDialogTimer(uint32); void ProcessDialogTimer(void); void DrawOverlays(void); void CloseDialog(void); #endif void Initialise(); void PrintMap(); void SetFrontEndRenderStates(); static void CentreMousePointer(); void CheckCodesForControls(int); bool CheckHover(int x1, int x2, int y1, int y2); void CheckSliderMovement(int); void DisplayHelperText(char*); int DisplaySlider(float, float, float, float, float, float, float); void DoSettingsBeforeStartingAGame(); void DrawStandardMenus(bool); void DrawControllerBound(int32, int32, int32, int8); void DrawControllerScreenExtraText(int, int, int); void DrawControllerSetupScreen(); void DrawQuitGameScreen(); void DrawFrontEnd(); void DrawBackground(bool transitionCall); void DrawPlayerSetupScreen(bool); int FadeIn(int alpha); int GetStartOptionsCntrlConfigScreens(); void InitialiseChangedLanguageSettings(); void LoadAllTextures(); void LoadSettings(); void MessageScreen(const char *str, bool); void SmallMessageScreen(const char *str); void PrintBriefs(); static void PrintErrorMessage(); void PrintStats(); void Process(); void ProcessList(bool &optionSelected, bool &goBack); void UserInput(); void ProcessUserInput(uint8, uint8, uint8, uint8, int8); void ChangeRadioStation(int8); void ProcessFileActions(); void ProcessOnOffMenuOptions(); void RequestFrontEndShutDown(); void RequestFrontEndStartUp(); void ResetHelperText(); void SaveSettings(); void SetHelperText(int text); float StretchX(float); float StretchY(float); void SwitchMenuOnAndOff(); void UnloadTextures(); void WaitForUserCD(); int GetNumOptionsCntrlConfigScreens(); void SwitchToNewScreen(int8); void AdditionalOptionInput(bool &goBack); void ExportStats(void); void PrintRadioSelector(void); // New (not in function or inlined in the game) void ThingsToDoBeforeLeavingPage(); void ScrollUpListByOne(); void ScrollDownListByOne(); void PageUpList(bool); void PageDownList(bool); int8 GetPreviousPageOption(); // uint8 GetNumberOfMenuOptions(); #ifdef GAMEPAD_MENU void LoadController(int8 type); void PrintController(void); #endif }; #ifndef IMPROVED_VIDEOMODE VALIDATE_SIZE(CMenuManager, 0x688); #endif extern CMenuManager FrontEndMenuManager; #endif ================================================ FILE: src/core/FrontendTriggers.h ================================================ CTriggerCaller MemCardAccessTriggerCaller; void InitialiseTextsInMenuControllerInCar(CMenuPictureAndText *widget, CMenuManager::CONTRCONFIG cont); void InitialiseTextsInMenuControllerOnFoot(CMenuPictureAndText *widget, CMenuManager::CONTRCONFIG cont); void TriggerSave_BackToMainMenu(CMenuMultiChoiceTriggered *widget); void TriggerSave_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget); void TriggerSave_LoadGameLoadGameSelect(CMenuMultiChoiceTwoLinesTriggered *widget); void TriggerSave_DeleteGameDeleteGameSelect(CMenuMultiChoiceTwoLinesTriggered *widget); void TriggerSaveZone_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget); void TriggerSaveZone_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget); void TriggerSaveZone_SaveSlots(CMenuMultiChoiceTwoLinesTriggered *widget); void DisplayWarningControllerMsg() { if ( CPad::bDisplayNoControllerMessage ) { CSprite2d::DrawRect(CRect(X(20.0f), Y(140.0f), X(620.0f), Y(328.0)), CRGBA(64, 16, 16, 224)); // CRect(20.0f, 160.0f, 620.0f, 374.857117f) CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetScale(X(0.84f), Y(1.26f)); // 1.440000 CFont::SetPropOn(); CFont::SetCentreOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f CPlaceableShText text; text.SetPosition(X(60.0f), Y(180.0f), false); // 205.714294 text.SetColor(CRGBA(152, 152, 152, 255)); text.m_text = TheText.Get("NOCONTE"); // Please re-insert the analog controller (DUALSHOCK@) or analog controller (DUALSHOCK@2) in controller port 1 to continue text.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR*2.0f); text.SetAlpha(255); text.DrawShWrap(0.0f, 0.0f, X(600.0f+SHADOW_VECTOR.x), YF(600.0f)); //TODO check CFont::DrawFonts(); } else if ( CPad::bObsoleteControllerMessage ) { CSprite2d::DrawRect(CRect(X(20.0f), Y(140.0f), X(620.0f), Y(328.0)), CRGBA(64, 16, 16, 224)); // CRect(20.0f, 160.0f, 620.0f, 374.857117f) CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetScale(X(0.84f), Y(1.26f)); // 1.440000 CFont::SetPropOn(); CFont::SetCentreOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f CPlaceableShText text; text.SetPosition(X(60.0f), Y(180.0f), false); // 205.714294 text.SetColor(CRGBA(152, 152, 152, 255)); text.m_text = TheText.Get("WRCONTE"); // Please re-insert the analog controller (DUALSHOCK@) or analog controller (DUALSHOCK@2) in controller port 1 to continue text.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR*2.0f); text.SetAlpha(255); text.DrawShWrap(0.0f, 0.0f, X(600.0f+SHADOW_VECTOR.x), YF(600.0f)); //TODO check CFont::DrawFonts(); } } void TriggerMCSUM_Yes(CMenuMultiChoiceTriggered *widget) { if ( widget ) bMemoryCardStartUpMenus_ExitNow = true; } int32 nStatLinesIndex; wchar aStatLines[50+1][50]; wchar *PrintStatLine(char const *text, void *stat, unsigned char itsFloat, void *stat2) { if (text && stat && nStatLinesIndex < 50) { char line [64]; wchar uline[64]; memset(line, 0, sizeof(line)); memset(uline, 0, sizeof(uline)); if (stat2) { if ( itsFloat ) sprintf(line, " %.2f %s %.2f", *(float*)stat, UnicodeToAscii(TheText.Get("FEST_OO")), *(float*)stat2); else sprintf(line, " %d %s %d", *(int32*)stat, UnicodeToAscii(TheText.Get("FEST_OO")), *(int32*)stat2); } else { if (itsFloat) sprintf(line, " %.2f", *(float*)stat); else sprintf(line, " %d", *(int32*)stat); } wchar *pStatLine = aStatLines[nStatLinesIndex++]; AsciiToUnicode(line, uline); UnicodeStrcpy(pStatLine, uline); return pStatLine; } return nil; } void DisplayMemoryCardAccessMsg(wchar *msg, CRGBA const &color) { CSprite2d::DrawRect(CRect(X(70.0f), Y(100.0f), X(570.0f), Y(270.0f)), color); CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetScale(X(MEMCARD_ACCESS_MSG_SIZE_X), Y(MEMCARD_ACCESS_MSG_SIZE_Y)); CFont::SetPropOn(); CFont::SetJustifyOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(90.0f)); // 550.0f CFont::SetCentreOn(); CFont::SetCentreSize(SCRW-X(180.0f)); // 460.0f CPlaceableShText text; text.SetPosition(X(320.0f), Y(120.0f), false); // 137.142868 text.SetColor(CRGBA(200, 200, 200, 255)); text.m_text = msg; text.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); text.SetAlpha(255); text.Draw(0.0f, 0.0f); CFont::DrawFonts(); DoRWStuffEndOfFrame(); } void FillMenuWithMemCardFileListing(CMenuMultiChoiceTwoLinesTriggered *widget, void (*cancelTrigger)(CMenuMultiChoiceTwoLinesTriggered *), void (*selectTrigger)(CMenuMultiChoiceTwoLinesTriggered *), wchar *text, int32 y, int32 height, int32 offset) { if ( widget ) { int32 selected = 0; if ( bMemoryCardSpecialZone ) selected = widget->m_cursor != -1 ? widget->m_cursor : 0; widget->DeactivateMenu(); // TODO check widget->m_numOptions = 0; widget->AddTitle(nil, 0.0f, 0.0f, 0); TheMemoryCard.PopulateSlotInfo(CARD_ONE); if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS) { widget->AddOption(TheText.Get("FES_CAN"), 0.0f, YF(y), cancelTrigger, 0, 0); FrontEndMenuManager.field_3C = 0; y += offset; char buff[100]; for ( int32 i = 0; i < CMemoryCard::MAX_SLOTS; i++ ) { // SAVE FILE sprintf(buff, "%s %d ", UnicodeToAscii(TheText.Get("FES_SLO")), i+1); AsciiToUnicode(buff, MemoryCard_FileNames[i]); wchar *datetime = nil; switch ( TheMemoryCard.GetInfoOnSpecificSlot(i) ) { case CMemoryCard::SLOT_CORRUPTED: { UnicodeStrcat(MemoryCard_FileNames[i], TheText.Get("FES_ISC")); // IS CORRUPTED datetime = TheMemoryCard.GetDateAndTimeOfSavedGame(i); break; } case CMemoryCard::SLOT_PRESENT: { if ( TheMemoryCard.GetNameOfSavedGame(i) != nil ) { UnicodeStrcpy(MemoryCard_FileNames[i], TheMemoryCard.GetNameOfSavedGame(i)); datetime = TheMemoryCard.GetDateAndTimeOfSavedGame(i); } else { UnicodeStrcpy(MemoryCard_FileNames[i], TheText.Get("FES_SAG")); // PRESENT datetime = TheMemoryCard.GetDateAndTimeOfSavedGame(i); } break; } case CMemoryCard::SLOT_NOTPRESENT: { UnicodeStrcat(MemoryCard_FileNames[i], TheText.Get("FES_ISF")); datetime = TheMemoryCard.GetDateAndTimeOfSavedGame(i); break; } } widget->AddOption(MemoryCard_FileNames[i], 0.0f, YF(y), datetime, 0.0f, YF(float(y)+(0.44f*height)), selectTrigger, 0, 0); y += height; } } else { if ( !gErrorSampleTriggered ) { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_FAIL, 0); gErrorSampleTriggered = true; } // Cancel widget->AddOption(TheText.Get("FES_CAN"), 0.0f, YF(y+(height*2)), cancelTrigger, 0, 0); FrontEndMenuManager.field_3C = 1; y += height; TheMemoryCard.PopulateErrorMessage(); // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. if ( TheMemoryCard.GetErrorMessage()) widget->AddTitle(TheMemoryCard.GetErrorMessage(), 0.0f, YF(y), 0); else widget->AddTitle(TheText.Get("FES_GME"), 0.0f, YF(y), 0); } widget->SetMenuSelection(0); widget->ActivateMenu(1); if ( bMemoryCardSpecialZone ) { widget->GoFirst(); for ( int32 i = 0; i < selected; i++ ) widget->GoNext(); } } } void TriggerSaveZone_FormatFailedOK(CMenuMultiChoiceTriggered *widget) { if ( widget ) pActiveMenuPage = &MenuPageSaveZone_SaveGame; } void TriggerSaveZone_BackToMainMenu(CMenuMultiChoiceTriggered *widget) { bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; pActiveMenuPage = &MenuPageSaveZone_SaveGame; } void TriggerSaveZone_QuitMenu(CMenuMultiChoiceTriggered *widget) { if ( widget ) { FrontEndMenuManager.m_bMenuActive = false; FrontEndMenuManager.m_bInSaveZone = false; CTimer::EndUserPause(); } } void TriggerSaveZone_FormatCard(CMenuMultiChoiceTriggered *widget) { if ( widget ) { FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS) { MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(TheText.Get("FES_AFO"), X(-80.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(5.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } else if ( TheMemoryCard.GetError() != CMemoryCard::ERR_NOFORMAT) { MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(TheMemoryCard.GetErrorMessage(), X(-80.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(15.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } else { if ( !MemCardAccessTriggerCaller.CanCall() ) MemCardAccessTriggerCaller.SetTrigger(TriggerSaveZone_FormatCard, widget); else { // Formatting Memory Card (PS2) in MEMORY CARD slot 1. Please do not remove the Memory Card (PS2), reset or switch off the console. DisplayMemoryCardAccessMsg(TheText.Get("FEFD_WR"), CRGBA(200, 50, 50, 192)); TheMemoryCard.FormatCard(CARD_ONE); if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) pActiveMenuPage = &MenuPageSaveZone_SaveGame; else { TheMemoryCard.PopulateErrorMessage(); wchar *error = TheText.Get("FESZ_FF"); // Format Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. // missing switch if ( !error ) error = TheText.Get("FES_GME"); // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) { FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); pActiveMenuPage = &MenuPageSaveZone_SaveSlots; bMemoryCardSpecialZone = true; bIgnoreTriangleButton = true; pActiveMenuPage->ActivatePage(); } else { TheMemoryCard.PopulateErrorMessage(); // Format Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. wchar *error = TheText.Get("FESZ_FF"); switch ( TheMemoryCard.GetError() ) { case CMemoryCard::ERR_WRITEFULLDEVICE: case CMemoryCard::ERR_DIRFULLDEVICE: case CMemoryCard::ERR_SAVEFAILED: { error = TheMemoryCard.GetErrorMessage(); break; } } // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. if ( !error ) error = TheText.Get("FES_GME"); MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } } } } } void TriggerSaveZone_FormatCardSelect(CMenuMultiChoiceTriggered *widget) { if ( widget ) { FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) { // This Memory Card (PS2) is already formatted. MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(TheText.Get("FES_AFO"), X(-80.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(5.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } else if ( TheMemoryCard.GetError() != CMemoryCard::ERR_NOFORMAT ) { MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(TheMemoryCard.GetErrorMessage(), X(-80.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(15.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } else { // Are you sure you wish to format the Memory Card (PS2) in MEMORY CARD slot 1? MenuSaveZoneQYN_1.m_numTexts = 0; MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QF"), X(-40.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneQYN_2.m_numOptions = 0; MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSaveZone_FormatCard, 0, 0); MenuPageSaveZone_QuestionYesNo.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_QuestionYesNo; } } } void TriggerSaveZone_DeleteSaveGame(CMenuMultiChoiceTriggered *widget) { if ( widget ) { bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; if ( !MemCardAccessTriggerCaller.CanCall() ) MemCardAccessTriggerCaller.SetTrigger(TriggerSaveZone_DeleteSaveGame, widget); else { // Overwriting data. Please do not remove the Memory Card (PS2) in MEMORY CARD slot 1, reset or switch off the console. DisplayMemoryCardAccessMsg(TheText.Get("FESZ_OW"), CRGBA(200, 50, 50, 192)); TheMemoryCard.DeleteSlot(MemoryCardSlotSelected); if ( TheMemoryCard.GetError() != CMemoryCard::NO_ERR_SUCCESS ) { TheMemoryCard.PopulateErrorMessage(); wchar *error = TheText.Get("FES_DEE"); // Deleting Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. // switch missing if ( !error ) error = TheText.Get("FES_GME"); // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } else { TheMemoryCard.SaveSlot(MemoryCardSlotSelected); if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) { // Game saved successfully! MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(TheText.Get("FESZ_L1"), X(-20.0f), YF(10.0f), TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(110.0f), 0.0f, TriggerSaveZone_QuitMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } else { TheMemoryCard.PopulateErrorMessage(); wchar *error = TheText.Get("FESZ_SR"); // Save Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. switch ( TheMemoryCard.GetError() ) { case CMemoryCard::ERR_WRITEFULLDEVICE: case CMemoryCard::ERR_DIRFULLDEVICE: case CMemoryCard::ERR_SAVEFAILED: { error = TheMemoryCard.GetErrorMessage(); break; } } if ( !error ) error = TheText.Get("FES_GME"); // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(120.0f), YF(30.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } } } } } void TriggerSaveZone_SaveGame(CMenuMultiChoiceTriggered *widget) { if ( widget ) { bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; if ( !MemCardAccessTriggerCaller.CanCall() ) MemCardAccessTriggerCaller.SetTrigger(TriggerSaveZone_SaveGame, widget); else { DisplayMemoryCardAccessMsg(TheText.Get("FESZ_WR"), CRGBA(200, 50, 50, 192)); TheMemoryCard.SaveSlot(MemoryCardSlotSelected); if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS ) { // Game saved successfully! MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(TheText.Get("FESZ_L1"), X(-20.0f), YF(10.0f), TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(110.0f), 0.0f, TriggerSaveZone_QuitMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } else { TheMemoryCard.PopulateErrorMessage(); wchar *error = TheText.Get("FESZ_SR"); // Save Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. switch ( TheMemoryCard.GetError() ) { case CMemoryCard::ERR_WRITEFULLDEVICE: case CMemoryCard::ERR_DIRFULLDEVICE: case CMemoryCard::ERR_SAVEFAILED: { error = TheMemoryCard.GetErrorMessage(); break; } } if ( !error ) error = TheText.Get("FES_GME"); // Error Reading Memory Card (PS2) in MEMORY CARD slot 1 please check and try again. MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(error, X(-80.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(120.0f), YF(30.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pActiveMenuPage = &MenuPageSaveZone_Message; } } } } void TriggerSaveZone_SaveSlots(CMenuMultiChoiceTwoLinesTriggered *widget) { if ( widget ) { if ( widget->GetMenuSelection() > 0 ) { MemoryCardSlotSelected = widget->GetMenuSelection() - 1; switch ( TheMemoryCard.GetInfoOnSpecificSlot(MemoryCardSlotSelected) ) { case CMemoryCard::SLOT_PRESENT: case CMemoryCard::SLOT_CORRUPTED: { // Proceed with overwriting this saved game? MenuSaveZoneQYN_1.m_numTexts = 0; MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QO"), X(-40.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneQYN_2.m_numOptions = 0; MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSaveZone_DeleteSaveGame, 0, 0); MenuPageSaveZone_QuestionYesNo.ActivatePage(); bMemoryCardSpecialZone = false; pActiveMenuPage = &MenuPageSaveZone_QuestionYesNo; break; } case CMemoryCard::SLOT_NOTPRESENT: { // PROCEED WITH SAVE ? MenuSaveZoneQYN_1.m_numTexts = 0; MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QS"), X(-40.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneQYN_2.m_numOptions = 0; MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSaveZone_BackToMainMenu, 0, 0); MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSaveZone_SaveGame, 0, 0); MenuPageSaveZone_QuestionYesNo.ActivatePage(); bMemoryCardSpecialZone = false; pActiveMenuPage = &MenuPageSaveZone_QuestionYesNo; break; } } } } } void TriggerSaveZone_SaveGameSelect(CMenuMultiChoiceTriggered *widget) { if ( widget ) { FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); if ( TheMemoryCard.GetError() == CMemoryCard::ERR_NOFORMAT) { gErrorSampleTriggered = false; pActiveMenuPage = &MenuPageSaveZone_FormatCard; } else { bMemoryCardSpecialZone = true; bIgnoreTriangleButton = true; pActiveMenuPage = &MenuPageSaveZone_SaveSlots; } pActiveMenuPage->ActivatePage(); } } void TriggerControls_Vibrations(CMenuOnOffTriggered *widget) { if ( widget ) { CMenuManager::m_PrefsUseVibration = widget->GetMenuSelection(); if ( CMenuManager::m_PrefsUseVibration ) { CPad::GetPad(0)->StartShake(300, 150); TimeToStopPadShaking = CTimer::GetTimeInMillisecondsPauseMode() + 500; } } } void TriggerControls_ContrDisplay(CMenuMultiChoiceTriggeredAlways *widget) { if ( widget ) { int32 conf = MenuControls_1.GetMenuSelection(); int32 i = MenuControls_2.GetMenuSelection(); if ( i == 1 ) { if ( conf == CMenuManager::CONFIG_2 ) MenuPage_Controls.m_controls[0] = &MenuControls_7; else MenuPage_Controls.m_controls[0] = &MenuControls_4; } else if ( i == 0 ) { if ( conf == CMenuManager::CONFIG_2 ) MenuPage_Controls.m_controls[0] = &MenuControls_6; else MenuPage_Controls.m_controls[0] = &MenuControls_3; } } } void TriggerControls_DrawHNContrConfig(CMenuMultiChoiceTriggeredAlways *widget) { if ( widget ) { int32 conf = widget->GetMenuSelection(); InitialiseTextsInMenuControllerOnFoot(&MenuControls_3, (CMenuManager::CONTRCONFIG)conf); InitialiseTextsInMenuControllerInCar (&MenuControls_4, (CMenuManager::CONTRCONFIG)conf); int32 i = MenuControls_2.GetMenuSelection(); if ( i == 1 ) { if ( conf == CMenuManager::CONFIG_2 ) MenuPage_Controls.m_controls[0] = &MenuControls_7; else MenuPage_Controls.m_controls[0] = &MenuControls_4; } else if ( i == 0 ) { if ( conf == CMenuManager::CONFIG_2 ) MenuPage_Controls.m_controls[0] = &MenuControls_6; else MenuPage_Controls.m_controls[0] = &MenuControls_3; } } } void TriggerControls_DrawContrConfig(CMenuMultiChoiceTriggeredAlways *widget) { if ( widget ) { int32 conf = widget->GetMenuSelection(); if ( widget->m_cursor != -1 ) conf = widget->m_cursor; InitialiseTextsInMenuControllerOnFoot(&MenuControls_3, (CMenuManager::CONTRCONFIG)conf); InitialiseTextsInMenuControllerInCar(&MenuControls_4, (CMenuManager::CONTRCONFIG)conf); int32 i = MenuControls_2.GetMenuSelection(); if ( i == 1 ) { if ( conf == CMenuManager::CONFIG_2 ) MenuPage_Controls.m_controls[0] = &MenuControls_7; else MenuPage_Controls.m_controls[0] = &MenuControls_4; } else if ( i == 0 ) { if ( conf == CMenuManager::CONFIG_2 ) MenuPage_Controls.m_controls[0] = &MenuControls_6; else MenuPage_Controls.m_controls[0] = &MenuControls_3; } } } void TriggerControls_ContrConfig(CMenuMultiChoiceTriggered *widget) { if ( widget ) { int32 conf = widget->GetMenuSelection(); InitialiseTextsInMenuControllerOnFoot(&MenuControls_3, (CMenuManager::CONTRCONFIG)conf); InitialiseTextsInMenuControllerInCar(&MenuControls_4, (CMenuManager::CONTRCONFIG)conf); int32 i = MenuControls_2.GetMenuSelection(); if ( i == 1 ) { if ( conf == CMenuManager::CONFIG_2 ) MenuPage_Controls.m_controls[0] = &MenuControls_7; else MenuPage_Controls.m_controls[0] = &MenuControls_4; } else if ( i == 0 ) { if ( conf == CMenuManager::CONFIG_2 ) MenuPage_Controls.m_controls[0] = &MenuControls_6; else MenuPage_Controls.m_controls[0] = &MenuControls_3; } } } void TriggerLanguage_Language(CMenuMultiChoiceTriggered *widget) { if ( widget ) { if ( CMenuManager::m_PrefsLanguage != widget->GetMenuSelection() ) { CMenuManager::m_PrefsLanguage = widget->GetMenuSelection(); FrontEndMenuManager.m_bInitialised = false; bFrontEnd_ReloadObrTxtGxt = true; } } } void TriggerAudio_RadioStation(CMenuMultiChoicePicturedTriggered *widget) { if ( widget ) { if ( CMenuManager::m_PrefsRadioStation != widget->GetMenuSelection() ) { CMenuManager::m_PrefsRadioStation = widget->GetMenuSelection(); DMAudio.PlayFrontEndTrack(CMenuManager::m_PrefsRadioStation, 1); DMAudio.SetRadioInCar(CMenuManager::m_PrefsRadioStation); } } } void TriggerAudio_StereoMono(CMenuMultiChoiceTriggered *widget) { if ( widget ) { if (widget->GetMenuSelection() == 1) { DMAudio.SetMonoMode(true); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MONO, 0); } else { DMAudio.SetMonoMode(false); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_STEREO, 0); } } } void TriggerAudio_MusicVolumeAlways(CMenuSliderTriggered *widget) { ; } void TriggerAudio_SfxVolumeAlways(CMenuSliderTriggered *widget) { if ( widget ) { static bool bTriggerTest = false; CMenuManager::m_PrefsSfxVolume = float(widget->GetMenuSelection()) / 100.0f * 127.0f + 0.5f; if ( CMenuManager::m_PrefsSfxVolume == 102 && !CPad::GetPad(0)->GetDPadLeft()&& !CPad::GetPad(0)->GetDPadRight() ) { if ( bTriggerTest ) { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_AUDIO_TEST, 0); bTriggerTest = false; } } else bTriggerTest = true; FrontEndMenuManager.SetSoundLevelsForMusicMenu(); } } void TriggerAudio_MusicVolume(CMenuSliderTriggered *widget) { if ( widget ) { CMenuManager::m_PrefsMusicVolume = float(widget->GetMenuSelection()) / 100.0f * 127.0f + 0.5f; FrontEndMenuManager.SetSoundLevelsForMusicMenu(); } } void TriggerAudio_SfxVolume(CMenuSliderTriggered *widget) { ; } void TriggerSave_NewGameNewGame(CMenuMultiChoiceTriggered *widget) { FrontEndMenuManager.m_bWantToRestart = true; FrontEndMenuManager.m_bMenuActive = false; FrontEndMenuManager.m_bInSaveZone = false; bIgnoreTriangleButton = false; CTimer::EndUserPause(); FrontEndMenuManager.AnaliseMenuContents(); DMAudio.SetEffectsFadeVol(0); DMAudio.SetMusicFadeVol(0); DMAudio.ResetTimers(CTimer::GetTimeInMilliseconds()); } void TriggerSave_NewGameSelectYes(CMenuMultiChoiceTriggered *widget) { // Are you sure you want to start a new game? All progress since the last save game will be lost. Proceed? MenuSaveZoneQYN_1.m_numTexts = 0; MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QR"), X(-100.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneQYN_2.m_numOptions = 0; MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(30.0f), TriggerSave_BackToMainMenu, 0, 0); MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), YF(10.0f), TriggerSave_NewGameNewGame, 0, 0); MenuPageSaveZone_QuestionYesNo.ActivatePage(); pMenuSave = &MenuPageSaveZone_QuestionYesNo; bIgnoreTriangleButton = true; } void TriggerSave_DeleteGameDeleteGame(CMenuMultiChoiceTriggered *widget) { if ( widget ) { bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; if ( !MemCardAccessTriggerCaller.CanCall() ) MemCardAccessTriggerCaller.SetTrigger(TriggerSave_DeleteGameDeleteGame, widget); else { // Deleting data. Please do not remove the Memory Card (PS2) in MEMORY CARD slot 1, reset or switch off the console. DisplayMemoryCardAccessMsg(TheText.Get("FEDL_WR"), CRGBA(200, 50, 50, 192)); TheMemoryCard.DeleteSlot(MemoryCardSlotSelected); if ( TheMemoryCard.GetError() != CMemoryCard::NO_ERR_SUCCESS) { // Deleting Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(TheText.Get("FES_DEE"), X(-80.0f), YF(20.0f), TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(15.0f), TriggerSave_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pMenuSave = &MenuPageSaveZone_Message; bMemoryCardSpecialZone = false; bIgnoreTriangleButton = true; } else { FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); pMenuSave = &MenuPage_SaveBasic; pMenuSave->ActivatePage(); } } } } void TriggerSave_DeleteGameDeleteGameSelect(CMenuMultiChoiceTwoLinesTriggered *widget) { if ( widget ) { if ( widget->GetMenuSelection() > 0 ) { MemoryCardSlotSelected = widget->GetMenuSelection() - 1; switch ( TheMemoryCard.GetInfoOnSpecificSlot(MemoryCardSlotSelected) ) { case CMemoryCard::SLOT_NOTPRESENT: { break; } case CMemoryCard::SLOT_CORRUPTED: case CMemoryCard::SLOT_PRESENT: { // Proceed with deleting this saved game? MenuSaveZoneQYN_1.m_numTexts = 0; MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QD"), X(-40.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneQYN_2.m_numOptions = 0; MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSave_BackToMainMenu, 0, 0); MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSave_DeleteGameDeleteGame, 0, 0); MenuPageSaveZone_QuestionYesNo.ActivatePage(); pMenuSave = &MenuPageSaveZone_QuestionYesNo; bMemoryCardSpecialZone = false; break; } } } } } void TriggerSave_DeleteGameSelect(CMenuMultiChoiceTriggered *widget) { FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); pMenuSave = &MenuPage_SaveDeleteGame; pMenuSave->ActivatePage(); gErrorSampleTriggered = false; bMemoryCardSpecialZone = true; bIgnoreTriangleButton = true; } void TriggerSave_LoadGameLoadGame(CMenuMultiChoiceTriggered *widget) { if ( widget ) { bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; if ( !MemCardAccessTriggerCaller.CanCall() ) MemCardAccessTriggerCaller.SetTrigger(TriggerSave_LoadGameLoadGame, widget); else { // Loading data. Please do not remove the Memory Card (PS2) in MEMORY CARD slot 1, reset or switch off the console. DisplayMemoryCardAccessMsg(TheText.Get("FELD_WR"), CRGBA(200, 50, 50, 192)); TheMemoryCard.LoadSlotToBuffer(MemoryCardSlotSelected); if ( TheMemoryCard.GetError() == CMemoryCard::NO_ERR_SUCCESS) { FrontEndMenuManager.m_bWantToRestart = true; FrontEndMenuManager.AnaliseMenuContents(); FrontEndMenuManager.m_bMenuActive = false; FrontEndMenuManager.m_bInSaveZone = false; CTimer::EndUserPause(); TheMemoryCard.m_bWantToLoad = true; DMAudio.SetEffectsFadeVol(0); DMAudio.SetMusicFadeVol(0); DMAudio.ResetTimers(CTimer::GetTimeInMilliseconds()); } else { // Load Failed! Check Memory Card (PS2) in MEMORY CARD slot 1 and please try again. MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(TheText.Get("FES_LOE"), X(-80.0f), YF(20.0f), TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), YF(25.0f), TriggerSave_BackToMainMenu, 0, 0); pMenuSave = &MenuPageSaveZone_Message; pMenuSave->ActivatePage(); bMemoryCardSpecialZone = false; bIgnoreTriangleButton = true; } } } } void TriggerSave_LoadGameLoadGameSelect(CMenuMultiChoiceTwoLinesTriggered *widget) { if ( widget ) { if ( widget->GetMenuSelection() > 0 ) { MemoryCardSlotSelected = widget->GetMenuSelection() - 1; switch ( TheMemoryCard.GetInfoOnSpecificSlot(MemoryCardSlotSelected) ) { case CMemoryCard::SLOT_NOTPRESENT: { break; } case CMemoryCard::SLOT_CORRUPTED: { // Load Failed. MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.AddText(TheText.Get("FES_LOF"), X(50.0f), YF(20.0f), TEXT_COLOR, 0); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(100.0f), 0.0f, TriggerSave_BackToMainMenu, 0, 0); MenuPageSaveZone_Message.ActivatePage(); pMenuSave = &MenuPageSaveZone_Message; bMemoryCardSpecialZone = false; break; } case CMemoryCard::SLOT_PRESENT: { // All unsaved progress in your current game will be lost. Proceed with loading? MenuSaveZoneQYN_1.m_numTexts = 0; MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_QL"), X(-40.0f), 0.0f, TEXT_COLOR, 0); MenuSaveZoneQYN_2.m_numOptions = 0; MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), YF(20.0f), TriggerSave_BackToMainMenu, 0, 0); MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSave_LoadGameLoadGame, 0, 0); MenuPageSaveZone_QuestionYesNo.ActivatePage(); pMenuSave = &MenuPageSaveZone_QuestionYesNo; bMemoryCardSpecialZone = false; break; } } } } } void TriggerSave_LoadGameSelect(CMenuMultiChoiceTriggered *widget) { FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); pMenuSave = &MenuPage_SaveLoadGame; pMenuSave->ActivatePage(); gErrorSampleTriggered = false; bMemoryCardSpecialZone = true; bIgnoreTriangleButton = true; } void TriggerSave_BackToMainMenu(CMenuMultiChoiceTriggered *widget) { pMenuSave = &MenuPage_SaveBasic; pMenuSave->ActivatePage(); bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; } void InitialiseTextsInMenuControllerInCar(CMenuPictureAndText *widget, CMenuManager::CONTRCONFIG cont) { if ( widget ) { widget->m_numTexts = 0; switch ( cont ) { case CMenuManager::CONFIG_1: { widget->AddText(TheText.Get("FEC_LL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f widget->AddText(TheText.Get("FEC_RSC"), X(-4.0f), Y(29.0f), PAD_TEXT_COLOR, true); // 33.142860f widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f widget->AddText(TheText.Get("FEC_HO3"), X(84.0f), Y(162.0f), PAD_TEXT_COLOR, false); // 185.142868f widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f widget->AddText(TheText.Get("FEC_LB"), X(68.0f), Y(-6.0f), PAD_TEXT_COLOR, false); // -6.857143f widget->AddText(TheText.Get("FEC_LR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f widget->AddText(TheText.Get("FEC_HAB"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f widget->AddText(TheText.Get("FEC_BRA"), X(155.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f widget->AddText(TheText.Get("FEC_EXV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f widget->AddText(TheText.Get("FEC_CAW"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f widget->AddText(TheText.Get("FEC_ACC"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f widget->AddText(TheText.Get("FEC_TUC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f widget->AddText(TheText.Get("FEC_SM3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f break; } case CMenuManager::CONFIG_2: { widget->AddText(TheText.Get("FEC_LL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f widget->AddText(TheText.Get("FEC_HOR"), X(-4.0f), Y(29.0f), PAD_TEXT_COLOR, true); // 33.142860f widget->AddText(TheText.Get("FEC_CAM"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f widget->AddText(TheText.Get("FEC_NA"), X(84.0f), Y(162.0f), PAD_TEXT_COLOR, false); // 185.142868f widget->AddText(TheText.Get("FEC_RSC"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f widget->AddText(TheText.Get("FEC_LB"), X(68.0f), Y(-6.0f), PAD_TEXT_COLOR, false); // -6.857143f widget->AddText(TheText.Get("FEC_LR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f widget->AddText(TheText.Get("FEC_HAB"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f widget->AddText(TheText.Get("FEC_BRA"), X(155.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f widget->AddText(TheText.Get("FEC_EXV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f widget->AddText(TheText.Get("FEC_CAW"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f widget->AddText(TheText.Get("FEC_ACC"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f widget->AddText(TheText.Get("FEC_TUC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f widget->AddText(TheText.Get("FEC_SM3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f break; } case CMenuManager::CONFIG_3: { widget->AddText(TheText.Get("FEC_LL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f widget->AddText(TheText.Get("FEC_EXV"), X(-4.0f), Y(29.0f), PAD_TEXT_COLOR, true); // 33.142860f widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f widget->AddText(TheText.Get("FEC_RS3"), X(84.0f), Y(162.0f), PAD_TEXT_COLOR, false); // 185.142868f widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f widget->AddText(TheText.Get("FEC_LB"), X(68.0f), Y(-6.0f), PAD_TEXT_COLOR, false); // -6.857143f widget->AddText(TheText.Get("FEC_LR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f widget->AddText(TheText.Get("FEC_HOR"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f widget->AddText(TheText.Get("FEC_BRA"), X(155.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f widget->AddText(TheText.Get("FEC_HAB"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f widget->AddText(TheText.Get("FEC_CAW"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f widget->AddText(TheText.Get("FEC_ACC"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f widget->AddText(TheText.Get("FEC_TUC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f widget->AddText(TheText.Get("FEC_SM3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f break; } case CMenuManager::CONFIG_4: { widget->AddText(TheText.Get("FEC_LL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f widget->AddText(TheText.Get("FEC_HAB"), X(-4.0f), Y(29.0f), PAD_TEXT_COLOR, true); // 33.142860f widget->AddText(TheText.Get("FEC_TUC"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f widget->AddText(TheText.Get("FEC_VES"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f widget->AddText(TheText.Get("FEC_HO3"), X(84.0f), Y(162.0f), PAD_TEXT_COLOR, false); // 185.142868f widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f widget->AddText(TheText.Get("FEC_LB"), X(68.0f), Y(-6.0f), PAD_TEXT_COLOR, false); // -6.857143f widget->AddText(TheText.Get("FEC_LR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f widget->AddText(TheText.Get("FEC_CAW"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f widget->AddText(TheText.Get("FEC_SMT"), X(155.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f widget->AddText(TheText.Get("FEC_EXV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f widget->AddText(TheText.Get("FEC_RSC"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f widget->AddText(TheText.Get("FEC_NA"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f widget->AddText(TheText.Get("FEC_ACC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f widget->AddText(TheText.Get("FEC_BRA"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f break; } } } } void InitialiseTextsInMenuControllerOnFoot(CMenuPictureAndText *widget, CMenuManager::CONTRCONFIG cont) { if ( widget ) { widget->m_numTexts = 0; switch ( cont ) { case CMenuManager::CONFIG_1: { widget->AddText(TheText.Get("FEC_CWL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f widget->AddText(TheText.Get("FEC_LOF"), X(-4.0f), Y(25.0f), PAD_TEXT_COLOR, true); // 28.571430f widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f widget->AddText(TheText.Get("FEC_CWR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f widget->AddText(TheText.Get("FEC_TAR"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f widget->AddText(TheText.Get("FEC_JUM"), X(144.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f widget->AddText(TheText.Get("FEC_ENV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f widget->AddText(TheText.Get("FEC_ATT"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f widget->AddText(TheText.Get("FEC_RUN"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f widget->AddText(TheText.Get("FEC_FPC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f widget->AddText(TheText.Get("FEC_LB3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f widget->AddText(TheText.Get("FEC_R3"), X(238.0f), Y(122.0f), PAD_TEXT_COLOR, false); // 139.428574f break; } case CMenuManager::CONFIG_2: { widget->AddText(TheText.Get("FEC_CWL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f widget->AddText(TheText.Get("FEC_LOF"), X(-4.0f), Y(25.0f), PAD_TEXT_COLOR, true); // 28.571430f widget->AddText(TheText.Get("FEC_CAM"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f widget->AddText(TheText.Get("FEC_NA"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f widget->AddText(TheText.Get("FEC_CWR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f widget->AddText(TheText.Get("FEC_TAR"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f widget->AddText(TheText.Get("FEC_JUM"), X(144.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f widget->AddText(TheText.Get("FEC_ENV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f widget->AddText(TheText.Get("FEC_ATT"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f widget->AddText(TheText.Get("FEC_RUN"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f widget->AddText(TheText.Get("FEC_FPC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f widget->AddText(TheText.Get("FEC_LB3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f widget->AddText(TheText.Get("FEC_R3"), X(238.0f), Y(122.0f), PAD_TEXT_COLOR, false); // 139.428574f break; } case CMenuManager::CONFIG_3: { widget->AddText(TheText.Get("FEC_CWL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f widget->AddText(TheText.Get("FEC_ENV"), X(-4.0f), Y(25.0f), PAD_TEXT_COLOR, true); // 28.571430f widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f widget->AddText(TheText.Get("FEC_CWR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f widget->AddText(TheText.Get("FEC_TAR"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f widget->AddText(TheText.Get("FEC_JUM"), X(144.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f widget->AddText(TheText.Get("FEC_LOF"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f widget->AddText(TheText.Get("FEC_RUN"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f widget->AddText(TheText.Get("FEC_ATT"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f widget->AddText(TheText.Get("FEC_FPC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f widget->AddText(TheText.Get("FEC_LB3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f widget->AddText(TheText.Get("FEC_R3"), X(238.0f), Y(122.0f), PAD_TEXT_COLOR, false); // 139.428574f break; } case CMenuManager::CONFIG_4: { widget->AddText(TheText.Get("FEC_CWL"), X(50.0f), Y(-14.0f), PAD_TEXT_COLOR, true); // -16.0f widget->AddText(TheText.Get("FEC_TAR"), X(-4.0f), Y(25.0f), PAD_TEXT_COLOR, true); // 28.571430f widget->AddText(TheText.Get("FEC_NA"), X(-4.0f), Y(65.0f), PAD_TEXT_COLOR, true); // 74.285721f widget->AddText(TheText.Get("FEC_MOV"), X(-4.0f), Y(97.0f), PAD_TEXT_COLOR, true); // 110.857147f widget->AddText(TheText.Get("FEC_CAM"), X(103.0f), Y(141.0f), PAD_TEXT_COLOR, false); // 161.142868f widget->AddText(TheText.Get("FEC_PAU"), X(130.0f), Y(128.0f), PAD_TEXT_COLOR, false); // 146.285721f widget->AddText(TheText.Get("FEC_CWR"), X(184.0f), Y(-14.0f), PAD_TEXT_COLOR, false); // -16.0f widget->AddText(TheText.Get("FEC_ATT"), X(238.0f), Y(25.0f), PAD_TEXT_COLOR, false); // 28.571430f widget->AddText(TheText.Get("FEC_JUM"), X(144.0f), Y(18.0f), PAD_TEXT_COLOR, true); // 20.571430f widget->AddText(TheText.Get("FEC_ENV"), X(238.0f), Y(52.0f), PAD_TEXT_COLOR, false); // 59.428574f widget->AddText(TheText.Get("FEC_LOF"), X(238.0f), Y(65.0f), PAD_TEXT_COLOR, false); // 74.285721f widget->AddText(TheText.Get("FEC_RUN"), X(238.0f), Y(78.0f), PAD_TEXT_COLOR, false); // 89.142860f widget->AddText(TheText.Get("FEC_FPC"), X(238.0f), Y(94.0f), PAD_TEXT_COLOR, false); // 107.428574f widget->AddText(TheText.Get("FEC_LB3"), X(238.0f), Y(109.0f), PAD_TEXT_COLOR, false); // 124.571434f widget->AddText(TheText.Get("FEC_R3"), X(238.0f), Y(122.0f), PAD_TEXT_COLOR, false); // 139.428574f break; } } } } void TriggerSaveZone_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget) { bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; pActiveMenuPage = &MenuPageSaveZone_SaveGame; } void TriggerSave_BackToMainMenuTwoLines(CMenuMultiChoiceTwoLinesTriggered *widget) { pMenuSave = &MenuPage_SaveBasic; pMenuSave->ActivatePage(); bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; } void SetRandomActiveTextlineColor(uint8 bText) { if ( bMemoryCardSpecialZone ) rgbaATC = SELECTED_TEXT_COLOR; else { bool bSelected = false; bool bHighlignted = false; switch ( FrontEndMenuManager.m_pageState ) { case PAGESTATE_NORMAL: break; case PAGESTATE_HIGHLIGHTED: bHighlignted = true; break; case PAGESTATE_SELECTED: bSelected = true; break; } if ( FrontEndMenuManager.m_bInSaveZone ) bSelected = true; if ( bSelected || bText ) { static uint32 delayTime = 0; static bool bAddVal = true; if ( delayTime < CTimer::GetTimeInMillisecondsPauseMode() ) { delayTime = CTimer::GetTimeInMillisecondsPauseMode() + 200; if ( bAddVal ) rgbaATC = TEXT_COLOR; else rgbaATC = SELECTED_TEXT_COLOR; bAddVal = !bAddVal; } } if ( bHighlignted ) { static uint32 delayTime = 0; static bool bAddVal = true; if ( delayTime < CTimer::GetTimeInMillisecondsPauseMode() ) { delayTime = CTimer::GetTimeInMillisecondsPauseMode() + 200; if ( bAddVal ) rgbaATC = TITLE_TEXT_COLOR; else rgbaATC = MENU_SELECTED_COLOR; bAddVal = !bAddVal; } } } } #ifdef GTA_PC void TriggerDisplay_Trails(CMenuOnOffTriggered *widget) { if ( widget ) { CMenuManager::m_PrefsShowTrails = widget->GetMenuSelection(); CMBlur::BlurOn = CMenuManager::m_PrefsShowTrails; if ( CMBlur::BlurOn ) CMBlur::MotionBlurOpen(Scene.camera); else CMBlur::MotionBlurClose(); } } #endif ================================================ FILE: src/core/Frontend_PS2.cpp ================================================ #include "common.h" #ifdef PS2_MENU #include "platform.h" #include "main.h" #include "Timer.h" #include "Pad.h" #include "Sprite2d.h" #include "Text.h" #include "Font.h" #include "Hud.h" #include "MBlur.h" #include "DMAudio.h" #include "Streaming.h" #include "Camera.h" #include "Credits.h" #include "General.h" #include "TxdStore.h" #include "FileMgr.h" #include "Messages.h" #include "Frontend_PS2.h" #include "Stats.h" #include "Game.h" #include "World.h" #include "PlayerInfo.h" #include "FrontEndControls.h" #include "MemoryCard.h" #define CRect_SZ(x, y, w, h) CRect(x, y, x+w, y+h) wchar MemoryCard_FileNames[8][100+1]; CMenuManager FrontEndMenuManager; // TEMP: put into header bool DoRWStuffStartOfFrame_Horizon(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); bool DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); void DoRWStuffEndOfFrame(void); #define SCRW SCREEN_WIDTH #define SCRH SCREEN_HEIGHT //#define X SCREEN_STRETCH_X //#define Y SCREEN_STRETCH_Y #define X SCREEN_SCALE_X #define Y SCREEN_SCALE_Y #define YF(x) Y(float(x)*(float(DEFAULT_SCREEN_HEIGHT)/float(SCREEN_HEIGHT_PAL))) //#define X(x) ((x)/640.0f*SCRW) //#define Y(y) ((y)/448.0f*SCRH) static float MENU_TEXT_SIZE_X = 0.644f; static float MENU_TEXT_SIZE_Y = 0.84f; //0.96f; float BUTTONTAB_TEXT_SIZE_X = 0.35f; float BUTTONTAB_TEXT_SIZE_Y = 0.7f; //0.8f; float PANEL_TEXT_SIZE_X = 0.8f; float PANEL_TEXT_SIZE_Y = 1.2f; //0.96f/0.7f; //?? float MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; float MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f; //1.28f; CRGBA SELECTED_TEXT_COLOR(255, 182, 48, 255); CRGBA BACKGROUND_SPLASH_COLOR(48, 48, 48, 255); CVector2D CONTR_DESCR_NEW_TEXTSCALE(0.4564f, 0.63f); // 0.72 CVector2D CONFIGS_NEW_TEXTSCALE(0.49f, 0.7f); // 0.8 CVector2D AUDIO_OUTPUT_POS(0.0f, 0.0f); CVector2D AUDIO_RSTATION_POS(154.0f, 0.0f); CVector2D DISPLAY_BRIGHTNESS_POS(0.0f, 0.0f); CRGBA TEXT_COLOR(150, 110, 30, 255); CRGBA PAD_TEXT_COLOR(200, 200, 200, 255); CRGBA CRIM_RATING_TEXT_COLOR(255, 182, 48, 255); CRGBA SCROLL_TEXT_COLOR(150, 110, 30, 255); CRGBA TITLE_TEXT_COLOR(170, 130, 50, 255); CRGBA TEXT_SHADOW_COLOR(0, 0, 0, 255); CVector2D SHADOW_VECTOR(1.0f, 1.0f); CRGBA SLIDER_RIGHT_COLOR(20, 94, 136, 255); CRGBA SLIDER_LEFT_COLOR(86, 196, 255, 255); CRGBA MENU_SELECTED_COLOR(255, 212, 88, 255); CRGBA rgbaATC(96, 96, 96, 255); // active text color. not constant float BUTTONTAB_TEXT_X_SCALES[NUM_PAGES] = { 1.0f }; float PANEL_TEXT_X_SCALES[NUM_PAGES] = { 1.0f }; int32 MemoryCardSlotSelected; uint32 TimeToStopPadShaking; bool bFrontEnd_ReloadObrTxtGxt; bool bMemoryCardStartUpMenus_ExitNow; extern CMenuPage MenuPage_SaveBasic; CMenuPage *pActiveMenuPage; CMenuPage *pMenuSave = &MenuPage_SaveBasic; bool bMemoryCardSpecialZone; bool bIgnoreTriangleButton; bool gErrorSampleTriggered; bool gMusicPlaying; CMenuPage MenuPage_Stats; CMenuLineLister MenuStats_1; CMenuPictureAndText MenuStats_2; // criminal rating CMenuPage MenuPage_Briefs; CMenuPictureAndText MenuBriefs_1; CMenuDummy MenuBriefs_2; CMenuPage MenuPage_SaveBasic; CMenuMultiChoiceTriggered MenuSaveB_1; // "Load Game", "Delete Game", "New Game" CMenuPage MenuPage_SaveNewGame; CMenuPictureAndText MenuSaveNG_1; // "Load Game", "Delete Game", "New Game" CMenuMultiChoiceTriggered MenuSaveNG_2; // "No", "Yes" CMenuPage MenuPage_SaveLoadGame; CMenuPictureAndText MenuSaveLG_1; // "Load Game", "Delete Game", "New Game" CMenuMultiChoiceTwoLinesTriggered MenuSaveLG_2; // save games CMenuPage MenuPage_SaveDeleteGame; CMenuPictureAndText MenuSaveDG_1; // "Load Game", "Delete Game", "New Game" CMenuMultiChoiceTwoLinesTriggered MenuSaveDG_2; // save games CMenuPage MenuPage_Controls; CMenuPictureAndText MenuControls_3; // controller images CMenuPictureAndText MenuControls_6; CMenuPictureAndText MenuControls_4; CMenuPictureAndText MenuControls_7; CMenuMultiChoiceTriggeredAlways MenuControls_1; // "Configuration:" "Setup1", "Setup2", "Setup3", "Setup4" CMenuMultiChoiceTriggered MenuControls_2; // "Controller Display:" "On Foot", "In Car" CMenuOnOffTriggered MenuControls_5; // "Vibration:" CMenuPageAnyMove MenuPage_Audio; CMenuSliderTriggered MenuAudio_1; // "Music Volume" CMenuMultiChoiceTriggered MenuAudio_4; // "Output:" "Stereo", "Mono" CMenuSliderTriggered MenuAudio_2; // "SFX Volume" CMenuMultiChoicePicturedTriggeredAnyMove MenuAudio_3; // "Radio station select:" CMenuPage MenuPage_Display; CMenuSlider MenuDisplay_1; // "Brightness" #ifdef GTA_PC CMenuOnOffTriggered MenuDisplay_2; // "Trails:" #else CMenuOnOff MenuDisplay_2; // "Trails:" #endif CMenuOnOff MenuDisplay_3; // "Subtitles:" CMenuOnOff MenuDisplay_4; // "Wide Screen:" CMenuPage MenuPage_Language; CMenuMultiChoiceTriggered MenuLanguage_1; // "English", "French", "German", "Italian", "Spanish" CMenuPage MenuPageSaveZone_SaveGame; CMenuMultiChoiceTriggered MenuSaveZoneSG_1; // "Save game", "Cancel" CMenuPage MenuPageSaveZone_SaveSlots; CMenuMultiChoiceTwoLinesTriggered MenuSaveZoneSSL_1; // "Cancel" CMenuPage MenuPageSaveZone_SavedSuccessfully; CMenuPictureAndText MenuSaveZoneSS_1; // "Game saved successfully!" "Your saved filename is:" CMenuMultiChoiceTriggered MenuSaveZoneSS_2; // "Quit" CMenuPage MenuPageSaveZone_Message; CMenuPictureAndText MenuSaveZoneMSG_1; // "Save Failed! Check memory card (PS2) in MEMORY CARD slot 1 and please try again." CMenuMultiChoiceTriggered MenuSaveZoneMSG_2; // "OK" CMenuPage MenuPageSaveZone_QuestionYesNo; CMenuPictureAndText MenuSaveZoneQYN_1; // "Save Failed! Check memory card (PS2) in MEMORY CARD slot 1 and please try again." CMenuMultiChoiceTriggered MenuSaveZoneQYN_2; // "Yes", "No" CMenuPage MenuPageSaveZone_FormatCard; CMenuMultiChoiceTriggered MenuSaveZoneFC_1; // "Memory card (PS2) in MEMORY CARD slot 1 is unformatted. Would you like to format memory card (PS2) in MEMORY CARD slot 1?" "No" "Yes" CMenuPage MenuPageSaveZone_ErrorFormat; CMenuMultiChoiceTriggered MenuSaveZoneEF_1; // "Format Failed! Check memory card (PS2) in MEMORY CARD slot 1 and please try again." "OK" VALIDATE_SIZE(CPlaceableText, 0x10); VALIDATE_SIZE(CPlaceableShText, 0x20); VALIDATE_SIZE(CPlaceableShTextTwoLines, 0x30); VALIDATE_SIZE(CPlaceableShOption, 0x28); VALIDATE_SIZE(CPlaceableShOptionTwoLines, 0x38); VALIDATE_SIZE(CPlaceableSprite, 0x18); VALIDATE_SIZE(CPlaceableShSprite, 0x34); VALIDATE_SIZE(CMenuMultiChoice, 0x2CC); VALIDATE_SIZE(CMenuMultiChoiceTriggered, 0x310); VALIDATE_SIZE(CMenuMultiChoiceTwoLines, 0x3CC); VALIDATE_SIZE(CMenuOnOff, 0x90); #include "FrontendTriggers.h" static const char* FrontendFilenames[][2] = { {"fe2_mainpanel_ul", "" }, {"fe2_mainpanel_ur", "" }, {"fe2_mainpanel_dl", "" }, {"fe2_mainpanel_dr", "" }, {"fe2_mainpanel_dr2", "" }, {"fe2_tabactive", "" }, {"fe_iconbrief", "" }, {"fe_iconstats", "" }, {"fe_iconcontrols", "" }, {"fe_iconsave", "" }, {"fe_iconaudio", "" }, {"fe_icondisplay", "" }, {"fe_iconlanguage", "" }, {"fe_controller", "" }, {"fe_controllersh", "" }, {"fe_arrows1", "" }, {"fe_arrows2", "" }, {"fe_arrows3", "" }, {"fe_arrows4", "" }, {"fe_radio1", "" }, {"fe_radio2", "" }, {"fe_radio3", "" }, {"fe_radio4", "" }, {"fe_radio5", "" }, {"fe_radio6", "" }, {"fe_radio7", "" }, {"fe_radio8", "" }, {"fe_radio9", "" }, }; #ifdef CUTSCENE_BORDERS_SWITCH bool CMenuManager::m_PrefsCutsceneBorders = true; #endif int32 CMenuManager::m_PrefsSfxVolume = 102; int32 CMenuManager::m_PrefsMusicVolume = 102; int32 CMenuManager::m_PrefsBrightness = 256; bool CMenuManager::m_PrefsShowTrails = true; bool CMenuManager::m_PrefsShowSubtitles = true; bool CMenuManager::m_PrefsAllowNastyGame = true; int32 CMenuManager::m_PrefsRadioStation = 0; int32 CMenuManager::m_PrefsStereoMono = 0; int8 CMenuManager::m_PrefsUseWideScreen = 0; int32 CMenuManager::m_PrefsLanguage = 0; CMenuManager::CONTRCONFIG CMenuManager::m_PrefsControllerConfig = CONFIG_1; bool CMenuManager::m_PrefsUseVibration = false; #ifdef GTA_PC #include "PlayerSkin.h" int32 CMenuManager::OS_Language = 0; int8 CMenuManager::m_PrefsVsync = 1; int8 CMenuManager::m_PrefsVsyncDisp = 1; int8 CMenuManager::m_PrefsFrameLimiter = 1; int8 CMenuManager::m_PrefsSpeakers; int32 CMenuManager::m_ControlMethod = CONTROL_CLASSIC; int8 CMenuManager::m_PrefsDMA = 1; float CMenuManager::m_PrefsLOD = 1.0f; char CMenuManager::m_PrefsSkinFile[256] = DEFAULT_SKIN_NAME; #ifndef MASTER bool CMenuManager::m_PrefsMarketing; bool CMenuManager::m_PrefsDisableTutorials; #endif // !MASTER #ifdef MENU_MAP bool CMenuManager::bMenuMapActive; float CMenuManager::fMapSize; float CMenuManager::fMapCenterY; float CMenuManager::fMapCenterX; #endif #endif CMenuManager::CMenuManager(void) { int32 i; SetSoundLevelsForMusicMenu(); m_pageState = PAGESTATE_NORMAL; m_currentPage = PAGE_FIRST; m_newPage = PAGE_FIRST; m_bMenuActive = false; m_bSaveMenuActive = false; m_bRenderGameInMenu = false; m_bTexturesLoaded = false; m_nPageLeftTimer = 0; m_nPageRightTimer = 0; m_nChangePageTimer = 0; field_18 = 0; m_fade = 255; m_someAlpha = 255; m_position.x = 0.0f; m_position.y = 0.0f; m_nSlidingDir = SLIDE_TO_BOTTOM; m_nStartPauseTimer = 0; m_nEndPauseTimer = 0; m_bInitialised = false; m_bWantToUpdateContent = false; field_3C = 0; m_bInSaveZone = false; for(i = 0; i < NUM_PAGES; i++){ BUTTONTAB_TEXT_X_SCALES[i] = 1.0f; PANEL_TEXT_X_SCALES[i] = 1.0f; } #ifdef GTA_PC TheCamera.m_bUseMouse3rdPerson = m_ControlMethod == CONTROL_STANDARD; CMBlur::BlurOn = m_PrefsShowTrails; #endif } void CMenuManager::LoadAllTextures(void) { int32 i; if(m_bTexturesLoaded) return; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); DMAudio.Service(); DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); CSprite2d *splash = LoadSplash(nil); if(splash) splash->Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); else // doesn't exist!! CHud::Sprites[19].Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERMIPNEAREST); DoRWStuffEndOfFrame(); CFileMgr::SetDir(""); CFileMgr::SetDir(""); CTimer::Stop(); CStreaming::MakeSpaceFor(60*1024); CStreaming::ImGonnaUseStreamingMemory(); CGame::TidyUpMemory(false, true); int32 slot = CTxdStore::FindTxdSlot("frontend"); if(slot == -1) slot = CTxdStore::AddTxdSlot("frontend"); printf("LOAD frontend\n"); CTxdStore::LoadTxd(slot, "MODELS/FRONTEND.TXD"); CTxdStore::SetCurrentTxd(slot); CStreaming::IHaveUsedStreamingMemory(); CTimer::Update(); for(i = 0; i < NUM_SPRIRES; i++) { m_sprites[i].SetTexture(FrontendFilenames[i][0], FrontendFilenames[i][1]); m_sprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); } m_bTexturesLoaded = true; } void CMenuManager::UnloadTextures(void) { int32 slot; int32 i; if ( !m_bTexturesLoaded ) return; slot = CTxdStore::FindTxdSlot("frontend"); #ifdef FIX_BUGS for(i = 0; i < NUM_SPRIRES; i++) m_sprites[i].Delete(); #endif printf("REMOVE frontend\n"); CTxdStore::RemoveTxd(slot); m_bTexturesLoaded = false; } void CMenuManager::InitialiseMenusOnce(void) { if(m_bInitialised) return; m_bInitialised = true; InitialiseChangedLanguageSettings(); // Normal menu MenuPage_Stats.Initialise(); MenuPage_Briefs.Initialise(); MenuPage_SaveBasic.Initialise(); MenuPage_SaveNewGame.Initialise(); MenuPage_SaveLoadGame.Initialise(); MenuPage_SaveDeleteGame.Initialise(); MenuPage_Controls.Initialise(); MenuPage_Audio.Initialise(); MenuPage_Display.Initialise(); MenuPage_Language.Initialise(); // Save menu MenuPageSaveZone_SaveGame.Initialise(); MenuPageSaveZone_SaveSlots.Initialise(); MenuPageSaveZone_SavedSuccessfully.Initialise(); MenuPageSaveZone_Message.Initialise(); MenuPageSaveZone_QuestionYesNo.Initialise(); MenuPageSaveZone_FormatCard.Initialise(); MenuPageSaveZone_ErrorFormat.Initialise(); /* Stats */ MenuStats_1.ResetNumberOfTextLines(); MenuStats_1.SetPosition(X(75.0f), Y(70.0f)); MenuStats_1.m_width = X(480.0f); MenuStats_1.m_height = Y(274.0f); MenuStats_1.field_10E8 = 0; // unknown MenuStats_1.m_lineSpacing = Y(20.0f); MenuStats_1.m_scrollSpeed = 1.0f; MenuStats_1.SetLinesColor(SCROLL_TEXT_COLOR); MenuStats_1.ResetNumberOfTextLines(); MenuPage_Stats.AddMenu(&MenuStats_1); MenuStats_2.SetPosition(X(75.0f), Y(50.0f)); MenuStats_2.SetTextsColor(CRIM_RATING_TEXT_COLOR); MenuPage_Stats.AddMenu(&MenuStats_2); MenuPage_Stats.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_Stats.ActivatePage(); CVector2D saveGameTextScale(X(0.49f), Y(0.7f)); CVector2D defaultTextScale(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); /* Basic Load/Delete/New Game */ MenuSaveB_1.m_numOptions = 0; MenuSaveB_1.SetPosition(X(220.0f), Y(110.0f)); MenuSaveB_1.AddOption(TheText.Get("FES_LGA"), 0.0f, Y(20.0f), TriggerSave_LoadGameSelect, false, true); MenuSaveB_1.AddOption(TheText.Get("FES_DGA"), 0.0f, Y(40.0f), TriggerSave_DeleteGameSelect, false, true); MenuSaveB_1.AddOption(TheText.Get("FES_NGA"), 0.0f, Y(60.0f), TriggerSave_NewGameSelectYes, false, true); MenuSaveB_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuPage_SaveBasic.AddMenu(&MenuSaveB_1); MenuPage_SaveBasic.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_SaveBasic.ActivatePage(); /* New Game - but unused */ MenuSaveNG_1.m_numTexts = 0; MenuSaveNG_1.SetPosition(X(220.0f), Y(110.0f)); MenuSaveNG_1.AddText(TheText.Get("FES_LGA"), 0.0f, Y(20.0f), TEXT_COLOR, true); MenuSaveNG_1.AddText(TheText.Get("FES_DGA"), 0.0f, Y(40.0f), TEXT_COLOR, true); MenuSaveNG_1.AddText(TheText.Get("FES_NGA"), 0.0f, Y(60.0f), SELECTED_TEXT_COLOR, true); MenuPage_SaveNewGame.AddMenu(&MenuSaveNG_1); MenuSaveNG_2.m_numOptions = 0; MenuSaveNG_2.SetPosition(X(250.0f), Y(170.0f)); MenuSaveNG_2.AddOption(TheText.Get("FEM_NO"), 0.0f, 0.0f, TriggerSave_BackToMainMenu, false, false); MenuSaveNG_2.AddOption(TheText.Get("FEM_YES"), 0.0f, Y(20.0f), TriggerSave_NewGameSelectYes, false, false); MenuSaveNG_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveNG_2.m_defaultCancel = TriggerSave_BackToMainMenu; MenuPage_SaveNewGame.AddMenu(&MenuSaveNG_2); MenuPage_SaveNewGame.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_SaveNewGame.ActivatePage(); /* Load Game */ MenuSaveLG_1.m_numTexts = 0; MenuSaveLG_1.SetPosition(X(220.0f), Y(110.0f)); MenuSaveLG_1.AddText(TheText.Get("FES_LGA"), 0.0f, Y(20.0f), SELECTED_TEXT_COLOR, true); MenuSaveLG_1.AddText(TheText.Get("FES_DGA"), 0.0f, Y(40.0f), TEXT_COLOR, true); MenuSaveLG_1.AddText(TheText.Get("FES_NGA"), 0.0f, Y(60.0f), TEXT_COLOR, true); MenuPage_SaveLoadGame.AddMenu(&MenuSaveLG_1); MenuSaveLG_2.m_numOptions = 0; MenuSaveLG_2.SetPosition(X(250.0f), Y(60.0f)); MenuSaveLG_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveLG_2.m_defaultCancel = TriggerSave_BackToMainMenuTwoLines; MenuSaveLG_2.SetNewOldTextScale(true, saveGameTextScale, defaultTextScale, false); MenuPage_SaveLoadGame.AddMenu(&MenuSaveLG_2); MenuPage_SaveLoadGame.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_SaveLoadGame.ActivatePage(); /* Delete Game */ MenuSaveDG_1.m_numTexts = 0; MenuSaveDG_1.SetPosition(X(220.0f), Y(110.0f)); MenuSaveDG_1.AddText(TheText.Get("FES_LGA"), 0.0f, Y(20.0f), TEXT_COLOR, true); MenuSaveDG_1.AddText(TheText.Get("FES_DGA"), 0.0f, Y(40.0f), SELECTED_TEXT_COLOR, true); MenuSaveDG_1.AddText(TheText.Get("FES_NGA"), 0.0f, Y(60.0f), TEXT_COLOR, true); MenuPage_SaveDeleteGame.AddMenu(&MenuSaveDG_1); MenuSaveDG_2.m_numOptions = 0; MenuSaveDG_2.SetPosition(X(250.0f), Y(60.0f)); MenuSaveDG_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveDG_2.m_defaultCancel = TriggerSave_BackToMainMenuTwoLines; MenuSaveDG_2.SetNewOldTextScale(true, saveGameTextScale, defaultTextScale, false); MenuPage_SaveDeleteGame.AddMenu(&MenuSaveDG_2); MenuPage_SaveDeleteGame.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_SaveDeleteGame.ActivatePage(); CVector2D briefsTextScale(X(0.525f), Y(0.7f)); CVector2D defaultTextScale1(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); /* Briefs */ MenuBriefs_1.m_numTexts = 0; MenuBriefs_1.SetPosition(X(60.0f), Y(60.0f)); MenuBriefs_1.SetTextsColor(TEXT_COLOR); MenuBriefs_1.SetNewOldTextScale(true, briefsTextScale, defaultTextScale1); MenuBriefs_1.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); MenuPage_Briefs.AddMenu(&MenuBriefs_1); MenuPage_Briefs.AddMenu(&MenuBriefs_2); MenuPage_Briefs.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_Briefs.ActivatePage(); CVector2D defaultTextScale2(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); CVector2D defaultTextScale3(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); CVector2D CONTR_DESCR_NEW_TEXTSCALE_scaled(X(CONTR_DESCR_NEW_TEXTSCALE.x), Y(CONTR_DESCR_NEW_TEXTSCALE.y)); CVector2D CONFIGS_NEW_TEXTSCALE_scaled(X(CONFIGS_NEW_TEXTSCALE.x), Y(CONFIGS_NEW_TEXTSCALE.y)); /* Controls */ MenuControls_3.m_numTexts = 0; MenuControls_3.m_numSprites = 0; MenuControls_3.SetPosition(X(170.0f), Y(88.0f)); MenuControls_3.AddPicture(&m_sprites[FE_CONTROLLER], &m_sprites[FE_CONTROLLERSH], 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); MenuControls_3.AddPicture(&m_sprites[FE_ARROWS1], 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); MenuControls_3.SetNewOldTextScale(true, CONTR_DESCR_NEW_TEXTSCALE_scaled, defaultTextScale2); InitialiseTextsInMenuControllerOnFoot(&MenuControls_3, CMenuManager::m_PrefsControllerConfig); MenuControls_3.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuControls_3.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); MenuPage_Controls.AddMenu(&MenuControls_3); MenuControls_6.m_numTexts = 0; MenuControls_6.m_numSprites = 0; MenuControls_6.SetPosition(X(170.0f), Y(88.0f)); MenuControls_6.AddPicture(&m_sprites[FE_CONTROLLER], &m_sprites[FE_CONTROLLERSH], 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); MenuControls_6.AddPicture(&m_sprites[FE_ARROWS3], 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); MenuControls_6.SetNewOldTextScale(true, CONTR_DESCR_NEW_TEXTSCALE_scaled, defaultTextScale2); InitialiseTextsInMenuControllerOnFoot(&MenuControls_6, CMenuManager::CONFIG_2); MenuControls_6.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuControls_6.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); MenuControls_4.m_numTexts = 0; MenuControls_4.m_numSprites = 0; MenuControls_4.SetPosition(X(170.0f), Y(88.0f)); MenuControls_4.AddPicture(&m_sprites[FE_CONTROLLER], &m_sprites[FE_CONTROLLERSH], 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); MenuControls_4.AddPicture(&m_sprites[FE_ARROWS2], 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); MenuControls_4.SetNewOldTextScale(true, CONTR_DESCR_NEW_TEXTSCALE_scaled, defaultTextScale2); InitialiseTextsInMenuControllerInCar(&MenuControls_4, CMenuManager::m_PrefsControllerConfig); MenuControls_4.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuControls_4.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); MenuControls_7.m_numTexts = 0; MenuControls_7.m_numSprites = 0; MenuControls_7.SetPosition(X(170.0f), Y(88.0f)); MenuControls_7.AddPicture(&m_sprites[FE_CONTROLLER], &m_sprites[FE_CONTROLLERSH], 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); MenuControls_7.AddPicture(&m_sprites[FE_ARROWS4], 0.0f, 0.0f, X(235.2f), Y(175.2), CRGBA(255, 255, 255, 255)); MenuControls_7.SetNewOldTextScale(true, CONTR_DESCR_NEW_TEXTSCALE_scaled, defaultTextScale2); InitialiseTextsInMenuControllerInCar(&MenuControls_7, CMenuManager::CONFIG_2); MenuControls_7.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuControls_7.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x), X(600.0f)); MenuControls_1.m_numOptions = 0; MenuControls_1.SetPosition(X(284.0f), Y(290.0f)); MenuControls_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, SELECTED_TEXT_COLOR); MenuControls_1.SetNewOldTextScale(true, CONFIGS_NEW_TEXTSCALE_scaled, defaultTextScale3, false); MenuControls_1.AddTitle(TheText.Get("FEC_CCF"), 0.0f, 0.0f, true); MenuControls_1.AddOption(TheText.Get("FEC_CF1"), X(15.0f), Y(2.0f), TriggerControls_ContrConfig, false, false); MenuControls_1.AddOption(TheText.Get("FEC_CF2"), X(85.0f), Y(2.0f), TriggerControls_ContrConfig, false, false); MenuControls_1.AddOption(TheText.Get("FEC_CF3"), X(155.0f), Y(2.0f), TriggerControls_ContrConfig, false, false); MenuControls_1.AddOption(TheText.Get("FEC_CF4"), X(225.0f), Y(2.0f), TriggerControls_ContrConfig, false, false); MenuPage_Controls.AddMenu(&MenuControls_1); MenuControls_1.m_alwaysTrigger = (CMenuMultiChoiceTriggered::Trigger)TriggerControls_DrawContrConfig; MenuControls_1.m_alwaysHighlightTrigger = (CMenuMultiChoiceTriggered::Trigger)TriggerControls_DrawHNContrConfig; MenuControls_1.m_alwaysNormalTrigger = (CMenuMultiChoiceTriggered::Trigger)TriggerControls_DrawHNContrConfig; MenuControls_2.m_numOptions = 0; MenuControls_2.SetPosition(X(284.0f), Y(310.0f)); MenuControls_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, SELECTED_TEXT_COLOR); MenuControls_2.SetNewOldTextScale(true, CONFIGS_NEW_TEXTSCALE_scaled, defaultTextScale3, false); MenuControls_2.AddTitle(TheText.Get("FEC_CDP"), 0.0f, 0.0f, true); MenuControls_2.AddOption(TheText.Get("FEC_ONF"), X(15.0f), Y(2.0f), (CMenuMultiChoiceTriggered::Trigger)TriggerControls_ContrDisplay, false, false); MenuControls_2.AddOption(TheText.Get("FEC_INC"), X(105.0f), Y(2.0f), (CMenuMultiChoiceTriggered::Trigger)TriggerControls_ContrDisplay, false, false); MenuPage_Controls.AddMenu(&MenuControls_2); MenuControls_2.m_bTwoState = true; MenuControls_2.SetMenuSelection(0); MenuControls_5.SetPosition(X(284.0f), Y(330.0f)); MenuControls_5.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR); MenuControls_5.SetNewOldTextScale(true, CONFIGS_NEW_TEXTSCALE_scaled, defaultTextScale3, false); MenuControls_5.AddTitle(TheText.Get("FEC_VIB"), false, 0.0f, 0.0f, true); MenuControls_5.SetOptionPosition(X(15.0f), Y(2.0f), TriggerControls_Vibrations, false); MenuPage_Controls.AddMenu(&MenuControls_5); MenuPage_Controls.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_Controls.ActivatePage(); /* Audio */ CVector2D audioOutputScale(X(0.49f), Y(0.63f)); CVector2D defaultTextScale4(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); FEC_MOVETAB movetab; MenuAudio_1.SetPosition(X(70.0f), Y(80.0f)); MenuAudio_1.SetColors(TEXT_COLOR, TEXT_COLOR, SLIDER_LEFT_COLOR, SLIDER_RIGHT_COLOR); MenuAudio_1.AddTitle(TheText.Get("FEA_MUS"), 0.0f, 0.0f); MenuAudio_1.AddTickBox(X(15.0f), Y(20.0f), X(150.0f), Y(5.0f), Y(45.0f), TriggerAudio_MusicVolume, TriggerAudio_MusicVolumeAlways); movetab.right = 1; movetab.left = 2; movetab.down = 3; movetab.up = 3; MenuPage_Audio.AddMenu(&MenuAudio_1, &movetab); MenuAudio_4.m_numOptions = 0; MenuAudio_4.SetPosition(X(280.0f), Y(80.0f)); MenuAudio_4.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, SELECTED_TEXT_COLOR); MenuAudio_4.SetNewOldTextScale(true, audioOutputScale, defaultTextScale4, false); MenuAudio_4.AddTitle(TheText.Get("FEA_OUT"), X(AUDIO_OUTPUT_POS.x), Y(AUDIO_OUTPUT_POS.y), false); MenuAudio_4.AddOption(TheText.Get("FEA_ST"), X(-15.0f), Y(30.0f), TriggerAudio_StereoMono, false, false); MenuAudio_4.AddOption(TheText.Get("FEA_MNO"), X(55.0f), Y(30.0f), TriggerAudio_StereoMono, false, false); movetab.right = 2; movetab.left = 0; movetab.down = 3; movetab.up = 3; MenuPage_Audio.AddMenu(&MenuAudio_4, &movetab); MenuAudio_4.m_bTwoState = true; MenuAudio_2.SetPosition(X(410.0f), Y(80.0f)); MenuAudio_2.SetColors(TEXT_COLOR, TEXT_COLOR, SLIDER_LEFT_COLOR, SLIDER_RIGHT_COLOR); MenuAudio_2.AddTitle(TheText.Get("FEA_SFX"), 0.0f, 0.0f); MenuAudio_2.AddTickBox(X(5.0f), Y(20.0f), X(150.0f), Y(5.0f), Y(45.0f), TriggerAudio_SfxVolume, TriggerAudio_SfxVolumeAlways); movetab.right = 0; movetab.left = 1; movetab.down = 3; movetab.up = 3; MenuPage_Audio.AddMenu(&MenuAudio_2, &movetab); MenuAudio_3.m_numOptions = 0; MenuAudio_3.SetPosition(X(50.0f), Y(170.0f)); MenuAudio_3.SetColors(TITLE_TEXT_COLOR, CRGBA(64, 64, 64, 255), CRGBA(250, 250, 250, 255)); MenuAudio_3.AddTitle(TheText.Get("FEA_RSS"), X(AUDIO_RSTATION_POS.x), Y(AUDIO_RSTATION_POS.y), false); // first row movetab.right = 1; movetab.left = 4; movetab.down = 5; movetab.up = 5; MenuAudio_3.AddOption(&m_sprites[FE_RADIO1], &movetab, 0.0f, Y(18.0f), CVector2D(X(96.0f), YF(72.0f)), TriggerAudio_RadioStation, false); movetab.right = 2; movetab.left = 0; movetab.down = 6; movetab.up = 6; MenuAudio_3.AddOption(&m_sprites[FE_RADIO2], &movetab, X(106.0f), Y(20.0f), CVector2D(X(79.2f), YF(81.0f)), TriggerAudio_RadioStation, false); movetab.right = 3; movetab.left = 1; movetab.down = 7; movetab.up = 7; MenuAudio_3.AddOption(&m_sprites[FE_RADIO5], &movetab, X(210.0f), Y(20.0f), CVector2D(X(86.4f), YF(72.0f)), TriggerAudio_RadioStation, false); movetab.right = 4; movetab.left = 2; movetab.down = 8; movetab.up = 8; MenuAudio_3.AddOption(&m_sprites[FE_RADIO7], &movetab, X(324.0f), Y(5.0f), CVector2D(X(115.2f), YF(102.0f)), TriggerAudio_RadioStation, false); movetab.right = 0; movetab.left = 3; movetab.down = 8; movetab.up = 8; MenuAudio_3.AddOption(&m_sprites[FE_RADIO8], &movetab, X(446.0f), Y(5.0f), CVector2D(X(102.96f), YF(101.4f)), TriggerAudio_RadioStation, false); // second row movetab.right = 6; movetab.left = 8; movetab.down = 0; movetab.up = 0; MenuAudio_3.AddOption(&m_sprites[FE_RADIO3], &movetab, X(60.0f), Y(96.0f), CVector2D(X(87.36f), YF(85.8f)), TriggerAudio_RadioStation, false); movetab.right = 7; movetab.left = 5; movetab.down = 1; movetab.up = 1; MenuAudio_3.AddOption(&m_sprites[FE_RADIO4], &movetab, X(130.0f), Y(72.0f), CVector2D(X(129.6f), YF(129.0f)), TriggerAudio_RadioStation, false); movetab.right = 8; movetab.left = 6; movetab.down = 2; movetab.up = 2; MenuAudio_3.AddOption(&m_sprites[FE_RADIO6], &movetab, X(284.0f), Y(108.0f), CVector2D(X(60.0f), YF(60.0f)), TriggerAudio_RadioStation, false); movetab.right = 5; movetab.left = 7; movetab.down = 3; movetab.up = 3; MenuAudio_3.AddOption(&m_sprites[FE_RADIO9], &movetab, X(404.0f), Y(85.0f), CVector2D(X(81.12f), YF(101.4f)), TriggerAudio_RadioStation, false); movetab.right = 2; movetab.left = 0; movetab.down = 1; movetab.up = 1; MenuPage_Audio.AddMenu(&MenuAudio_3, &movetab); MenuPage_Audio.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_Audio.ActivatePage(); /* Display */ MenuDisplay_1.SetPosition(X(240.0f), Y(140.0f)); MenuDisplay_1.SetColors(TEXT_COLOR, TEXT_COLOR, SLIDER_LEFT_COLOR, SLIDER_RIGHT_COLOR); MenuDisplay_1.m_style = 0; // ticks MenuDisplay_1.AddTitle(TheText.Get("FED_BRI"), X(DISPLAY_BRIGHTNESS_POS.x), Y(DISPLAY_BRIGHTNESS_POS.y)); MenuDisplay_1.AddTickBox(X(-30.0f), Y(20.0f), X(200.0f), Y(40.0f), Y(40.0f)); MenuPage_Display.AddMenu(&MenuDisplay_1); MenuDisplay_2.SetPosition(X(290.0f), Y(240.0f)); MenuDisplay_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR); MenuDisplay_2.AddTitle(TheText.Get("FED_TRA"), false, 0.0f, 0.0f, true); #ifdef GTA_PC MenuDisplay_2.SetOptionPosition(X(40.0f), 0.0f, TriggerDisplay_Trails, false); #else MenuDisplay_2.SetOptionPosition(X(40.0f), 0.0f, false); #endif MenuDisplay_2.m_bTwoState = true; MenuPage_Display.AddMenu(&MenuDisplay_2); MenuDisplay_3.SetPosition(X(290.0f), Y(260.0f)); MenuDisplay_3.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR); MenuDisplay_3.AddTitle(TheText.Get("FED_SUB"), false, 0.0f, 0.0f, true); MenuDisplay_3.SetOptionPosition(X(40.0f), 0.0f, false); MenuDisplay_3.m_bTwoState = true; MenuPage_Display.AddMenu(&MenuDisplay_3); MenuDisplay_4.SetPosition(X(290.0f), Y(280.0f)); MenuDisplay_4.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR); MenuDisplay_4.AddTitle(TheText.Get("FED_WIS"), false, 0.0f, 0.0f, true); MenuDisplay_4.SetOptionPosition(X(40.0f), 0.0f, false); MenuDisplay_4.m_bTwoState = true; MenuPage_Display.AddMenu(&MenuDisplay_4); MenuPage_Display.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_Display.ActivatePage(); /* Language */ MenuLanguage_1.m_numOptions = 0; MenuLanguage_1.SetPosition(X(288.0f), Y(160.0f)); MenuLanguage_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, SELECTED_TEXT_COLOR); MenuLanguage_1.AddOption(TheText.Get("FEL_ENG"), 0.0f, 0.0f, TriggerLanguage_Language, false, false); MenuLanguage_1.AddOption(TheText.Get("FEL_FRE"), 0.0f, Y(20.0f), TriggerLanguage_Language, false, false); MenuLanguage_1.AddOption(TheText.Get("FEL_GER"), 0.0f, Y(40.0f), TriggerLanguage_Language, false, false); MenuLanguage_1.AddOption(TheText.Get("FEL_ITA"), 0.0f, Y(60.0f), TriggerLanguage_Language, false, false); MenuLanguage_1.AddOption(TheText.Get("FEL_SPA"), 0.0f, Y(80.0f), TriggerLanguage_Language, false, false); MenuPage_Language.AddMenu(&MenuLanguage_1); MenuPage_Language.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPage_Language.ActivatePage(); /* * Save zone menu */ CVector2D saveGameTextScale2(X(0.49f), Y(0.7f)); CVector2D defaultTextScale5(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); /* Save game */ MenuSaveZoneSG_1.m_numOptions = 0; MenuSaveZoneSG_1.SetPosition(X(200.0f), Y(100.0f)); MenuSaveZoneSG_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveZoneSG_1.AddOption(TheText.Get("FESZ_SA"), 0.0f, Y(20.0f), TriggerSaveZone_SaveGameSelect, false, false); MenuSaveZoneSG_1.AddOption(TheText.Get("FESZ_CA"), 0.0f, Y(40.0f), TriggerSaveZone_QuitMenu, false, false); MenuSaveZoneSG_1.m_defaultCancel = TriggerSaveZone_QuitMenu; MenuPageSaveZone_SaveGame.AddMenu(&MenuSaveZoneSG_1); MenuSaveZoneSG_1.SetMenuSelection(1); MenuPageSaveZone_SaveGame.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPageSaveZone_SaveGame.ActivatePage(); /* Select slot */ MenuSaveZoneSSL_1.m_numOptions = 0; MenuSaveZoneSSL_1.SetPosition(X(160.0f), Y(100.0f)); MenuSaveZoneSSL_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveZoneSSL_1.AddOption(TheText.Get("FESZ_CA"), 0.0f, 0.0f, TriggerSaveZone_BackToMainMenuTwoLines, false, false); MenuSaveZoneSSL_1.SetNewOldTextScale(true, saveGameTextScale2, defaultTextScale5, true); MenuPageSaveZone_SaveSlots.AddMenu(&MenuSaveZoneSSL_1); MenuSaveZoneSSL_1.SetMenuSelection(0); MenuPageSaveZone_SaveSlots.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPageSaveZone_SaveSlots.ActivatePage(); /* Save successful */ MenuSaveZoneSS_1.m_numTexts = 0; MenuSaveZoneSS_1.SetPosition(X(200.0f), Y(100.0f)); MenuSaveZoneSS_1.AddText(TheText.Get("FESZ_L1"), X(-40.0f), 0.0f, TITLE_TEXT_COLOR, false); MenuSaveZoneSS_1.AddText(TheText.Get("FESZ_L2"), X(-40.0f), Y(20.0f), TITLE_TEXT_COLOR, false); // twice this line? MenuSaveZoneSS_1.AddText(TheText.Get("FESZ_L2"), X(-40.0f), Y(40.0f), TEXT_COLOR, false); MenuPageSaveZone_SavedSuccessfully.AddMenu(&MenuSaveZoneSS_1); MenuSaveZoneSS_2.m_numOptions = 0; MenuSaveZoneSS_2.SetPosition(X(200.0f), Y(170.0f)); MenuSaveZoneSS_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveZoneSS_2.AddOption(TheText.Get("FESZ_QU"), X(60.0f), 0.0f, TriggerSaveZone_QuitMenu, false, false); MenuPageSaveZone_SavedSuccessfully.AddMenu(&MenuSaveZoneSS_2); MenuPageSaveZone_SavedSuccessfully.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPageSaveZone_SavedSuccessfully.ActivatePage(); MenuSaveZoneMSG_1.m_numTexts = 0; MenuSaveZoneMSG_1.SetPosition(X(170.0f), Y(130.0f)); MenuSaveZoneMSG_1.AddText(TheText.Get("FESZ_SR"), X(-40.0f), 0.0f, TEXT_COLOR, false); MenuSaveZoneMSG_1.SetTextsColor(TEXT_COLOR); MenuSaveZoneMSG_1.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x-20.0f), X(580.0f)); MenuPageSaveZone_Message.AddMenu(&MenuSaveZoneMSG_1); MenuSaveZoneMSG_2.m_numOptions = 0; MenuSaveZoneMSG_2.SetPosition(X(170.0f), Y(180.0f)); MenuSaveZoneMSG_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveZoneMSG_2.AddOption(TheText.Get("FESZ_OK"), X(40.0f), 0.0f, TriggerSaveZone_QuitMenu, false, false); MenuPageSaveZone_Message.AddMenu(&MenuSaveZoneMSG_2); MenuPageSaveZone_Message.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPageSaveZone_Message.ActivatePage(); MenuSaveZoneQYN_1.m_numTexts = 0; MenuSaveZoneQYN_1.SetPosition(X(170.0f), Y(130.0f)); MenuSaveZoneQYN_1.AddText(TheText.Get("FESZ_SR"), X(-40.0f), 0.0f, TEXT_COLOR, false); MenuSaveZoneQYN_1.SetTextsColor(TEXT_COLOR); MenuSaveZoneQYN_1.SetNewOldShadowWrapX(true, X(600.0f+SHADOW_VECTOR.x-20.0f), X(580.0f)); MenuPageSaveZone_QuestionYesNo.AddMenu(&MenuSaveZoneQYN_1); MenuSaveZoneQYN_2.m_numOptions = 0; MenuSaveZoneQYN_2.SetPosition(X(170.0f), Y(180.0f)); MenuSaveZoneQYN_2.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_YES"), X(80.0f), 0.0f, TriggerSaveZone_QuitMenu, false, false); MenuSaveZoneQYN_2.AddOption(TheText.Get("FEM_NO"), X(80.0f), Y(20.0f), TriggerSaveZone_QuitMenu, false, false); MenuPageSaveZone_QuestionYesNo.AddMenu(&MenuSaveZoneQYN_2); MenuPageSaveZone_QuestionYesNo.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPageSaveZone_QuestionYesNo.ActivatePage(); /* Format card */ MenuSaveZoneFC_1.m_numOptions = 0; MenuSaveZoneFC_1.SetPosition(X(200.0f), Y(100.0f)); MenuSaveZoneFC_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveZoneFC_1.AddTitle(TheText.Get("FESZ_FM"), X(-100.0f), 0.0f, false); MenuSaveZoneFC_1.AddOption(TheText.Get("FEM_NO"), X(40.0f), Y(95.0f), TriggerSaveZone_BackToMainMenu, false, false); MenuSaveZoneFC_1.AddOption(TheText.Get("FEM_YES"), X(40.0f), Y(75.0f), TriggerSaveZone_FormatCardSelect, false, false); MenuSaveZoneFC_1.m_defaultCancel = TriggerSaveZone_FormatCardSelect; MenuPageSaveZone_FormatCard.AddMenu(&MenuSaveZoneFC_1); MenuPageSaveZone_FormatCard.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPageSaveZone_FormatCard.ActivatePage(); /* Format error */ MenuSaveZoneEF_1.m_numOptions = 0; MenuSaveZoneEF_1.SetPosition(X(200.0f), Y(100.0f)); MenuSaveZoneEF_1.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); MenuSaveZoneEF_1.AddTitle(TheText.Get("FESZ_FF"), X(-40.0f), 0.0f, false); MenuSaveZoneEF_1.AddOption(TheText.Get("FESZ_OK"), X(70.0f), Y(20.0f), TriggerSaveZone_FormatFailedOK, false, false); MenuPageSaveZone_ErrorFormat.AddMenu(&MenuSaveZoneEF_1); MenuPageSaveZone_ErrorFormat.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); MenuPageSaveZone_ErrorFormat.ActivatePage(); pActiveMenuPage = &MenuPage_Stats; pActiveMenuPage->ActivatePage(); InitialiseMenuContents(); m_bWantToUpdateContent = false; } void CMenuManager::InitialiseChangedLanguageSettings(void) { if ( bFrontEnd_ReloadObrTxtGxt ) { bFrontEnd_ReloadObrTxtGxt = false; CTimer::Stop(); TheText.Unload(); TheText.Load(); CTimer::Update(); FrontEndMenuManager.AnaliseMenuContents(); CGame::frenchGame = false; CGame::germanGame = false; if ( m_PrefsAllowNastyGame ) CGame::nastyGame = true; for ( int32 i = 0; i < NUM_PAGES; i++ ) { BUTTONTAB_TEXT_X_SCALES[i] = 1.0f; PANEL_TEXT_X_SCALES[i] = 1.0f; } switch ( m_PrefsLanguage ) { case LANGUAGE_AMERICAN: { MENU_TEXT_SIZE_X = 0.644f; MENU_TEXT_SIZE_Y = 0.84f;//0.96f; BUTTONTAB_TEXT_SIZE_X = 0.35f; BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; BUTTONTAB_TEXT_X_SCALES[6] = 0.94f; CONTR_DESCR_NEW_TEXTSCALE.x = 0.4564f; CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; CONFIGS_NEW_TEXTSCALE.x = 0.49f; CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; AUDIO_OUTPUT_POS.x = 0.0f; AUDIO_OUTPUT_POS.y = 0.0f; AUDIO_RSTATION_POS.x = 154.0f; AUDIO_RSTATION_POS.y = 0.0f; DISPLAY_BRIGHTNESS_POS.x = 0.0f; DISPLAY_BRIGHTNESS_POS.y = 0.0f; MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; break; } case LANGUAGE_FRENCH: { CGame::frenchGame = true; if ( m_PrefsAllowNastyGame ) CGame::nastyGame = false; MENU_TEXT_SIZE_X = 0.504f; MENU_TEXT_SIZE_Y = 0.84f;//0.96f; BUTTONTAB_TEXT_SIZE_X = 0.32f; BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; BUTTONTAB_TEXT_X_SCALES[0] = 0.84f; BUTTONTAB_TEXT_X_SCALES[3] = 0.84f; PANEL_TEXT_X_SCALES[1] = 0.8f; CONTR_DESCR_NEW_TEXTSCALE.x = 0.385f; CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; CONFIGS_NEW_TEXTSCALE.x = 0.455f; CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; AUDIO_OUTPUT_POS.x = -15.0f; AUDIO_OUTPUT_POS.y = 0.0f; AUDIO_RSTATION_POS.x = 184.0f; AUDIO_RSTATION_POS.y = 0.0f; DISPLAY_BRIGHTNESS_POS.x = 20.0f; DISPLAY_BRIGHTNESS_POS.y = 0.0f; MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; break; } case LANGUAGE_GERMAN: { CGame::germanGame = true; if ( m_PrefsAllowNastyGame ) CGame::nastyGame = false; MENU_TEXT_SIZE_X = 0.546f; MENU_TEXT_SIZE_Y = 0.84f;//0.96f; BUTTONTAB_TEXT_SIZE_X = 0.32f; BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; CONTR_DESCR_NEW_TEXTSCALE.x = 0.35f; CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; CONFIGS_NEW_TEXTSCALE.x = 0.434f; CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; AUDIO_OUTPUT_POS.x = -15.0f; AUDIO_OUTPUT_POS.y = 0.0f; AUDIO_RSTATION_POS.x = 154.0f; AUDIO_RSTATION_POS.y = 0.0f; DISPLAY_BRIGHTNESS_POS.x = 20.0f; DISPLAY_BRIGHTNESS_POS.y = 0.0f; MEMCARD_ACCESS_MSG_SIZE_X = 0.7f; MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; break; } case LANGUAGE_ITALIAN: { MENU_TEXT_SIZE_X = 0.574f; MENU_TEXT_SIZE_Y = 0.84f;//0.96f; BUTTONTAB_TEXT_SIZE_X = 0.32f; BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; BUTTONTAB_TEXT_X_SCALES[0] = 0.86f; PANEL_TEXT_X_SCALES[1] = 0.9f; CONTR_DESCR_NEW_TEXTSCALE.x = 0.385f; CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; CONFIGS_NEW_TEXTSCALE.x = 0.42f; CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; AUDIO_OUTPUT_POS.x = 10.0f; AUDIO_OUTPUT_POS.y = 0.0f; AUDIO_RSTATION_POS.x = 194.0f; AUDIO_RSTATION_POS.y = 0.0f; DISPLAY_BRIGHTNESS_POS.x = 10.0f; DISPLAY_BRIGHTNESS_POS.y = 0.0f; MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; break; } case LANGUAGE_SPANISH: { MENU_TEXT_SIZE_X = 0.546f; MENU_TEXT_SIZE_Y = 0.84f;//0.96f; BUTTONTAB_TEXT_SIZE_X = 0.35f; BUTTONTAB_TEXT_SIZE_Y = 0.7f;//0.8f; BUTTONTAB_TEXT_X_SCALES[0] = 0.78f; PANEL_TEXT_X_SCALES[1] = 0.95f; CONTR_DESCR_NEW_TEXTSCALE.x = 0.364f; CONTR_DESCR_NEW_TEXTSCALE.y = 0.63f;//0.72f; CONFIGS_NEW_TEXTSCALE.x = 0.455f; CONFIGS_NEW_TEXTSCALE.y = 0.7f;//0.8f; AUDIO_OUTPUT_POS.x = 10.0f; AUDIO_OUTPUT_POS.y = 0.0f; AUDIO_RSTATION_POS.x = 124.0f; AUDIO_RSTATION_POS.y = 0.0f; DISPLAY_BRIGHTNESS_POS.x = 30.0f; DISPLAY_BRIGHTNESS_POS.y = 0.0f; MEMCARD_ACCESS_MSG_SIZE_X = 0.84f; MEMCARD_ACCESS_MSG_SIZE_Y = 1.12f;//1.28f; break; } } } } void CMenuManager::InitialiseMenuContents(void) { if ( m_bWantToUpdateContent == false ) { m_bWantToUpdateContent = true; m_pageState = PAGESTATE_NORMAL; switch ( CPad::GetPad(0)->GetMode() ) { case 3: m_PrefsControllerConfig = CONFIG_4; break; case 2: m_PrefsControllerConfig = CONFIG_3; break; case 1: m_PrefsControllerConfig = CONFIG_2; break; case 0: m_PrefsControllerConfig = CONFIG_1; break; } MenuControls_1.SetMenuSelection(m_PrefsControllerConfig); MenuControls_5.SetMenuSelection(m_PrefsUseVibration); MenuAudio_1.SetMenuSelection(m_PrefsMusicVolume / 127.0f * 100.0f + 0.5f); MenuAudio_2.SetMenuSelection(m_PrefsSfxVolume / 127.0f * 100.0f + 0.5f); MenuAudio_3.SetMenuSelection(m_PrefsRadioStation); MenuAudio_4.SetMenuSelection(m_PrefsStereoMono); MenuDisplay_1.SetMenuSelection(m_PrefsBrightness / 512.0f * 100.0f + 0.5f); #ifdef PS2 m_PrefsShowTrails = BlurOn; #else m_PrefsShowTrails = CMBlur::BlurOn; #endif MenuDisplay_2.SetMenuSelection(m_PrefsShowTrails); MenuDisplay_3.SetMenuSelection(m_PrefsShowSubtitles); MenuDisplay_4.SetMenuSelection(m_PrefsUseWideScreen); MenuLanguage_1.SetMenuSelection(m_PrefsLanguage); FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); MenuBriefs_1.m_numTexts = 0; MenuBriefs_1.AddText(TheText.Get("FEB_PMB"), 0.0f, 0.0f, TITLE_TEXT_COLOR, 0); // Previous Mission Briefs: static wchar StringsToDisplay[NUMPREVIOUSBRIEFS][256]; CRGBA newColor; int32 brierY = 36; for ( int32 i = NUMPREVIOUSBRIEFS-1; i >= 0; i-- ) { tPreviousBrief &brief = CMessages::PreviousBriefs[i]; if (brief.m_pText) { CMessages::InsertNumberInString(brief.m_pText, brief.m_nNumber[0], brief.m_nNumber[1], brief.m_nNumber[2], brief.m_nNumber[3], brief.m_nNumber[4], brief.m_nNumber[5], StringsToDisplay[i]); CMessages::InsertStringInString(StringsToDisplay[i], brief.m_pString); newColor = TEXT_COLOR; FilterOutColorMarkersFromString(StringsToDisplay[i], newColor); if (newColor != TEXT_COLOR) { newColor.r /= 2; newColor.g /= 2; newColor.b /= 2; } MenuBriefs_1.AddText(StringsToDisplay[i], 0.0f, YF((float)brierY), newColor, 0); brierY += 54; } } MenuStats_1.m_scrollPosition = 0.0f; MenuStats_1.ResetNumberOfTextLines(); nStatLinesIndex = 0; #define STAT_HEADER(str) do { MenuStats_1.AddTextLine(TheText.Get(str), nil); } while(0) #define STAT_PARAM(str) do { MenuStats_1.AddTextLine(nil, TheText.Get(str)); } while(0) #define STAT_LINE(str, left, isFloat, right) do { MenuStats_1.AddTextLine(TheText.Get(str), PrintStatLine(str, left, isFloat, right)); } while(0) int32 nTemp; STAT_HEADER("PL_STAT"); int32 percentCompleted = (CStats::TotalProgressInGame == 0 ? 0 : CStats::ProgressMade * 100.0f / (CGame::nastyGame ? CStats::TotalProgressInGame : CStats::TotalProgressInGame - 1)); percentCompleted = Min(percentCompleted, 100); STAT_LINE("PER_COM", &percentCompleted, 0, nil); STAT_LINE("NMISON", &CStats::MissionsGiven, 0, nil); STAT_LINE("FEST_MP", &CStats::MissionsPassed, 0, &CStats::TotalNumberMissions); if ( CGame::nastyGame ) STAT_LINE("FEST_RP", &CStats::NumberKillFrenziesPassed, 0, &CStats::TotalNumberKillFrenzies); CPlayerInfo &player = CWorld::Players[CWorld::PlayerInFocus]; float packagesPercent = 0.0f; if (player.m_nTotalPackages != 0) packagesPercent = player.m_nCollectedPackages * 100.0f / player.m_nTotalPackages; int32 nPackagesPercent = packagesPercent; nTemp = 100; STAT_LINE("PERPIC", &nPackagesPercent, 0, &nTemp); STAT_LINE("NOUNIF", &CStats::NumberOfUniqueJumpsFound, 0, &CStats::TotalNumberOfUniqueJumps); STAT_LINE("DAYSPS", &CStats::DaysPassed, 0, nil); if ( CGame::nastyGame ) { STAT_LINE("PE_WAST", &CStats::PeopleKilledByPlayer, 0, nil); STAT_LINE("PE_WSOT", &CStats::PeopleKilledByOthers, 0, nil); } STAT_LINE("CAR_EXP", &CStats::CarsExploded, 0, nil); STAT_LINE("TM_BUST", &CStats::TimesArrested, 0, nil); STAT_LINE("TM_DED", &CStats::TimesDied, 0, nil); nTemp = CStats::PedsKilledOfThisType[PEDTYPE_GANG9] + CStats::PedsKilledOfThisType[PEDTYPE_GANG8] + CStats::PedsKilledOfThisType[PEDTYPE_GANG7] + CStats::PedsKilledOfThisType[PEDTYPE_GANG6] + CStats::PedsKilledOfThisType[PEDTYPE_GANG5] + CStats::PedsKilledOfThisType[PEDTYPE_GANG4] + CStats::PedsKilledOfThisType[PEDTYPE_GANG3] + CStats::PedsKilledOfThisType[PEDTYPE_GANG2] + CStats::PedsKilledOfThisType[PEDTYPE_GANG1]; STAT_LINE("GNG_WST", &nTemp, 0, nil); nTemp = CStats::PedsKilledOfThisType[PEDTYPE_CRIMINAL]; STAT_LINE("DED_CRI", &nTemp, 0, nil); STAT_LINE("HEL_DST", &CStats::HelisDestroyed, 0, nil); STAT_LINE("KGS_EXP", &CStats::KgsOfExplosivesUsed, 0, nil); if (CStats::LongestFlightInDodo > 0) STAT_LINE("FEST_LF", &CStats::LongestFlightInDodo, 0, nil); STAT_LINE("CAR_CRU", &CStats::CarsCrushed, 0, nil); if (CStats::HighestScores[0] > 0) { STAT_HEADER("FEST_BB"); STAT_LINE("FEST_H0", &CStats::HighestScores[0], 0, nil); } int32 hs = 0; for ( int32 i = 1; i < 5; i++ ) hs += CStats::HighestScores[i]; if (hs > 0) STAT_HEADER("FEST_GC"); if (CStats::HighestScores[1] > 0) STAT_LINE("FEST_H1", &CStats::HighestScores[1], 0, nil); if (CStats::HighestScores[2] > 0) STAT_LINE("FEST_H2", &CStats::HighestScores[2], 0, nil); if (CStats::HighestScores[3] > 0) STAT_LINE("FEST_H3", &CStats::HighestScores[3], 0, nil); if (CStats::HighestScores[4] > 0) STAT_LINE("FEST_H4", &CStats::HighestScores[4], 0, nil); STAT_LINE("FESTDFM", &CStats::DistanceTravelledOnFoot, 0, nil); STAT_LINE("FESTDCM", &CStats::DistanceTravelledByCar, 0, nil); STAT_LINE("DISTBIM", &CStats::DistanceTravelledByBike, 0, nil); STAT_LINE("DISTBOM", &CStats::DistanceTravelledByBoat, 0, nil); STAT_LINE("DISTGOM", &CStats::DistanceTravelledByGolfCart, 0, nil); STAT_LINE("DISTHEM", &CStats::DistanceTravelledByHelicoptor, 0, nil); STAT_LINE("MMRAIN", &CStats::mmRain, 0, nil); nTemp = (int32)CStats::MaximumJumpDistance; STAT_LINE("MXCARDM", &nTemp, 0, nil); nTemp = (int32)CStats::MaximumJumpHeight; STAT_LINE("MXCARJM", &nTemp, 0, nil); STAT_LINE("MXFLIP", &CStats::MaximumJumpFlips, 0, nil); STAT_LINE("MXJUMP", &CStats::MaximumJumpSpins, 0, nil); STAT_HEADER("BSTSTU"); switch (CStats::BestStuntJump) { case 1: STAT_PARAM("INSTUN"); break; case 2: STAT_PARAM("PRINST"); break; case 3: STAT_PARAM("DBINST"); break; case 4: STAT_PARAM("DBPINS"); break; case 5: STAT_PARAM("TRINST"); break; case 6: STAT_PARAM("PRTRST"); break; case 7: STAT_PARAM("QUINST"); break; case 8: STAT_PARAM("PQUINS"); break; default: STAT_PARAM("NOSTUC"); break; } STAT_LINE("PASDRO", &CStats::PassengersDroppedOffWithTaxi, 0, nil); STAT_LINE("MONTAX", &CStats::MoneyMadeWithTaxi, 0, nil); STAT_LINE("FEST_LS", &CStats::LivesSavedWithAmbulance, 0, nil); STAT_LINE("FEST_HA", &CStats::HighestLevelAmbulanceMission, 0, nil); STAT_LINE("FEST_CC", &CStats::CriminalsCaught, 0, nil); STAT_LINE("FEST_FE", &CStats::FiresExtinguished, 0, nil); int32 rnd = ((CGeneral::GetRandomNumber() & 255) + 100) * 2384; STAT_LINE("DAYPLC", &rnd, 0, nil); #undef STAT_LINE MenuStats_2.m_numTexts = 0; MenuStats_2.AddText(TheText.Get("CRIMRA"), 0.0f, 0.0f, CRIM_RATING_TEXT_COLOR, 0); char rating[16]; wchar urating[16]; sprintf(rating, " %d", CStats::FindCriminalRatingNumber()); AsciiToUnicode(rating, urating); wchar *pStatLine = aStatLines[nStatLinesIndex++]; UnicodeStrcpy(pStatLine, CStats::FindCriminalRatingString()); UnicodeStrcat(pStatLine, urating); MenuStats_2.AddText(pStatLine, X(MenuStats_1.m_width), 0.0f, CRIM_RATING_TEXT_COLOR, 1); MenuSaveZoneSG_1.SetMenuSelection(1); MenuSaveZoneFC_1.SetMenuSelection(1); } } void CMenuManager::AnaliseMenuContents(void) { if ( m_bWantToUpdateContent ) { m_bWantToUpdateContent = false; m_PrefsControllerConfig = (CONTRCONFIG)MenuControls_1.GetMenuSelection(); switch ( m_PrefsControllerConfig ) { case CONFIG_4: CPad::GetPad(0)->SetMode(3); break; case CONFIG_3: CPad::GetPad(0)->SetMode(2); break; case CONFIG_2: CPad::GetPad(0)->SetMode(1); break; case CONFIG_1: CPad::GetPad(0)->SetMode(0); break; } m_PrefsUseVibration = MenuControls_5.m_title.m_bSelected; m_PrefsMusicVolume = float(MenuAudio_1.GetMenuSelection())/100.0f*127.0f+0.5f; m_PrefsSfxVolume = float(MenuAudio_2.GetMenuSelection())/100.0f*127.0f+0.5f; m_PrefsRadioStation = MenuAudio_3.GetMenuSelection(); m_PrefsStereoMono = MenuAudio_4.GetMenuSelection(); m_PrefsBrightness = float(MenuDisplay_1.GetMenuSelection()) / 100.0f*512.0f + 0.5f; m_PrefsShowTrails = MenuDisplay_2.GetMenuSelection(); m_PrefsShowSubtitles = MenuDisplay_3.GetMenuSelection(); m_PrefsUseWideScreen = MenuDisplay_4.GetMenuSelection(); #ifdef PS2 BlurOn = m_PrefsShowTrails; #else CMBlur::BlurOn = m_PrefsShowTrails; #endif if ( m_PrefsLanguage != MenuLanguage_1.GetMenuSelection() ) { m_PrefsLanguage = MenuLanguage_1.GetMenuSelection(); m_bInitialised = false; bFrontEnd_ReloadObrTxtGxt = true; } } } void CMenuManager::InitialiseMenuContentsAfterLoadingGame(void) { if ( MenuLanguage_1.GetMenuSelection() != m_PrefsLanguage ) { m_bInitialised = false; bFrontEnd_ReloadObrTxtGxt = true; } } void CMenuManager::DrawFrontEnd(void) { CFont::SetAlphaFade(255.0f); if(m_bInSaveZone) DrawFrontEndSaveZone(); else DrawFrontEndNormal(); if ( MemCardAccessTriggerCaller.CanCall() ) MemCardAccessTriggerCaller.CallTrigger(); DisplayWarningControllerMsg(); } void CMenuManager::DrawFrontEndNormal(void) { CSprite2d::InitPerFrame(); CFont::InitPerFrame(); if ( bMemoryCardSpecialZone ) { static uint8 counter = 0; counter++; if ( (counter & 63 ) == 0 ) { FillMenuWithMemCardFileListing(&MenuSaveLG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_LoadGameLoadGameSelect, nil, 0, 34, 22); FillMenuWithMemCardFileListing(&MenuSaveDG_2, TriggerSave_BackToMainMenuTwoLines, TriggerSave_DeleteGameDeleteGameSelect, nil, 0, 34, 22); } } m_fade = 255; if ( m_nChangePageTimer != 0 && m_nChangePageTimer >= CTimer::GetTimeInMillisecondsPauseMode() ) m_fade = uint32(float(m_nChangePageTimer - CTimer::GetTimeInMillisecondsPauseMode()) / 250.0f * 255.0f); m_someAlpha = 255; m_position.x = 0.0f; m_position.y = 0.0f; if ( m_nStartPauseTimer != 0 && m_nStartPauseTimer >= CTimer::GetTimeInMillisecondsPauseMode() ) { float slide = float(m_nStartPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) / 800.0f; float alpha = 1.0f; if ((m_nStartPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) <= 1600) alpha = float(m_nStartPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) / 400.0f; m_someAlpha = 255 - clamp(alpha, 0.0f, 1.0f) * 255.0f; switch ( m_nSlidingDir ) { case SLIDE_TO_RIGHT: m_position.x = slide * X(700.0f); break; case SLIDE_TO_TOP: m_position.y = -(slide * Y(500.0f)); break; case SLIDE_TO_LEFT: m_position.x = -(slide * X(700.0f)); break; case SLIDE_TO_BOTTOM: m_position.y = slide * Y(500.0f); break; default: m_position.y = slide * Y(500.0f); break; } } if ( m_nEndPauseTimer != 0 && m_nEndPauseTimer >= CTimer::GetTimeInMillisecondsPauseMode() ) { float slide = float(m_nEndPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) / 800.0f; float alpha = float((int32)(m_nEndPauseTimer - CTimer::GetTimeInMillisecondsPauseMode()) + -266) / 533.0f; m_someAlpha = clamp(alpha, 0.0f, 1.0f) * 255.0f; switch ( m_nSlidingDir ) { case SLIDE_TO_TOP: m_position.y = (1.0f - slide) * Y(500.0f); break; case SLIDE_TO_RIGHT: m_position.x = (1.0f - slide) * X(700.0f); break; case SLIDE_TO_LEFT: m_position.x = (1.0f - slide) * X(700.0f); break; case SLIDE_TO_BOTTOM: m_position.y = -((1.0f - slide) * Y(500.0f)); break; default: m_position.y = -((1.0f - slide) * Y(500.0f)); break; } } if ( m_someAlpha < 255 ) m_fade = m_someAlpha; float posX, posY; /* Draw splash */ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); CSprite2d *splash = LoadSplash(nil); if(splash) splash->Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); else // doesn't exist!! CHud::Sprites[19].Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERMIPNEAREST); /* Draw main panel */ RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); CRGBA panelColor(255, 255, 255, m_someAlpha); m_sprites[FE2_MAINPANEL_UL].Draw( CRect(m_position.x, m_position.y, m_position.x+SCRW/2.0f, m_position.y+SCRH/2.0f), panelColor); m_sprites[FE2_MAINPANEL_UR].Draw( CRect(m_position.x+SCRW/2.0f, m_position.y, m_position.x+SCRW, m_position.y+SCRH/2.0f), panelColor); m_sprites[FE2_MAINPANEL_DL].Draw( CRect(m_position.x, m_position.y+SCRH/2.0f, m_position.x+SCRW/2.0f, m_position.y+SCRH), panelColor); m_sprites[FE2_MAINPANEL_DR].Draw( CRect(m_position.x+SCRW/2.0f, m_position.y+SCRH/2.0f, m_position.x+SCRW, m_position.y+SCRH), panelColor); /* Draw icon backdrop */ CRGBA iconColor(255, 255, 255, m_fade*0.75f); float iconX = 48.0f; float iconY = 54.0f; float iconWidth = 540.0f; float iconHeight = 296.0f; int32 sprite = FE_ICONBRIEF; #ifdef PS2_MENU_USEALLPAGEICONS switch(m_currentPage) { case PAGE_STATS: sprite = FE_ICONSTATS; break; case PAGE_LOAD: sprite = FE_ICONSAVE; break; case PAGE_CONTROLS: sprite = FE_ICONCONTROLS; break; case PAGE_BRIEFS: sprite = FE_ICONBRIEF; break; case PAGE_AUDIO: sprite = FE_ICONAUDIO; break; case PAGE_DISPLAY: sprite = FE_ICONDISPLAY; break; case PAGE_LANGUAGE: sprite = FE_ICONLANGUAGE; break; } #else switch(m_currentPage) { case PAGE_STATS: case PAGE_LOAD: case PAGE_CONTROLS: sprite = FE_ICONSTATS; // PS2 has the same texture for stats and brief //sprite = FE_ICONBRIEF; break; case PAGE_BRIEFS: sprite = FE_ICONBRIEF; break; case PAGE_AUDIO: sprite = FE_ICONAUDIO; break; case PAGE_DISPLAY: sprite = FE_ICONDISPLAY; break; case PAGE_LANGUAGE: sprite = FE_ICONLANGUAGE; break; } #endif m_sprites[sprite].Draw( CRect_SZ(m_position.x+X(iconX), m_position.y+Y(iconY), X(iconWidth), Y(iconHeight)), iconColor); /* Overwrite tab buttons if entered page */ bool bOverwriteTab = false; switch ( m_pageState ) { case PAGESTATE_NORMAL: case PAGESTATE_HIGHLIGHTED: break; case PAGESTATE_SELECTED: bOverwriteTab = true; break; } if( bOverwriteTab ) { CRGBA shadow(41, 101, 102, m_someAlpha); CRGBA green(40, 48, 57, m_someAlpha); CSprite2d::DrawRect( CRect_SZ(m_position.x+X(82.0f), m_position.y+Y(408.0f), X(476.0f), Y(18.0f)), shadow); CSprite2d::DrawRect( CRect_SZ(m_position.x+X(82.0f), m_position.y+Y(408.0f), X(476.0f), Y(5.0f)), green); } // stats, load, briefs, controls, audio, display, language /* Shadow of panel on top of tab buttons */ CRGBA panelShadow(96, 96, 96, m_someAlpha*0.375f); CSprite2d::DrawRect(CRect_SZ(m_position.x+X(87.0f), m_position.y+Y(408.0f), X(464.0f), Y(3.0f)), panelShadow); /* Draw second shadow - seems unused */ if ( m_nChangePageTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() < m_nChangePageTimer ) { posX = 0.0f; switch(field_18) { case PAGE_STATS: posX = 88.0f; break; case PAGE_LOAD: posX = 286.0f; break; // actually controls case PAGE_BRIEFS: posX = 154.0f; break; // actually load case PAGE_CONTROLS: posX = 220.0f; break; // actually briefs case PAGE_AUDIO: posX = 352.0f; break; case PAGE_DISPLAY: posX = 418.0f; break; case PAGE_LANGUAGE: posX = 484.0f; break; } CSprite2d::DrawRect(CRect_SZ(m_position.x+X(posX), m_position.y+Y(411.0f), X(65.0f), Y(3.0f)), panelShadow); } /* Active tab */ posX = 0.0f; switch(m_currentPage) { case PAGE_STATS: posX = 88.0f; break; case PAGE_LOAD: posX = 154.0f; break; case PAGE_BRIEFS: posX = 220.0f; break; case PAGE_CONTROLS: posX = 286.0f; break; case PAGE_AUDIO: posX = 352.0f; break; case PAGE_DISPLAY: posX = 418.0f; break; case PAGE_LANGUAGE: posX = 484.0f; break; } // PAL has 465 for 407 here - and actually 406 seems right m_sprites[FE2_TABACTIVE].Draw(CRect_SZ(m_position.x+X(posX), m_position.y+YF(465.0f), X(128.0f), Y(32.0f)), CRGBA(255, 255, 255, m_someAlpha)); /* Draw page title */ posX = m_position.x + X(592.0f); posY = m_position.y + Y(376.0f); CRGBA fontCol1(255, 193, 71, m_someAlpha); CRGBA fontCol2(0, 0, 0, m_someAlpha); CFont::SetFontStyle(FONT_HEADING); CFont::SetBackgroundOff(); CFont::SetScale(X(PANEL_TEXT_SIZE_X), Y(PANEL_TEXT_SIZE_Y)); CFont::SetPropOn(); CFont::SetCentreOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetRightJustifyOn(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f const char *key = nil; switch(m_currentPage) { case PAGE_STATS: key = "FEP_STA"; break; case PAGE_LOAD: key = "FEP_SAV"; break; case PAGE_BRIEFS: key = "FEP_BRI"; break; case PAGE_CONTROLS: key = "FEP_CON"; break; case PAGE_AUDIO: key = "FEP_AUD"; break; case PAGE_DISPLAY: key = "FEP_DIS"; break; case PAGE_LANGUAGE: key = "FEP_LAN"; break; } CFont::SetScale(X(PANEL_TEXT_SIZE_X*PANEL_TEXT_X_SCALES[m_currentPage]), Y(PANEL_TEXT_SIZE_Y)); CFont::SetColor(fontCol1); CFont::PrintString(posX, posY, TheText.Get(key)); CFont::SetColor(fontCol2); CFont::PrintString(posX-X(1.0f), posY-Y(1.0f), TheText.Get(key)); CFont::DrawFonts(); /* Draw controller buttons */ CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetScale(X(0.35f), Y(0.64f)); CFont::SetPropOn(); CFont::SetCentreOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f CFont::SetColor(CRGBA(16, 16, 16, m_someAlpha)); switch(m_currentPage) { case PAGE_STATS: CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(360.0f), TheText.Get("FEDS_ST")); CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(372.0f), TheText.Get("FEDS_AM")); CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(360.0f), TheText.Get("FEDSSC1")); CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(372.0f), TheText.Get("FEDSSC2")); break; case PAGE_BRIEFS: CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(360.0f), TheText.Get("FEDS_ST")); CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(372.0f), TheText.Get("FEDS_AM")); break; case PAGE_LOAD: case PAGE_CONTROLS: case PAGE_AUDIO: case PAGE_DISPLAY: case PAGE_LANGUAGE: { CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(360.0f), TheText.Get("FEDS_SE")); CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(372.0f), TheText.Get("FEDS_BA")); CFont::PrintString(m_position.x+X(52.0f), m_position.y+Y(384.0f), TheText.Get("FEDS_ST")); switch ( m_pageState ) { case PAGESTATE_NORMAL: CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(372.0f), TheText.Get("FEDS_AM")); // <>-CHANGE MENU break; case PAGESTATE_HIGHLIGHTED: case PAGESTATE_SELECTED: { CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(360.0f+3.5f), TheText.Get("FEA_UP")); // ; CFont::PrintString(m_position.x+X(242.0f), m_position.y+Y(384.0f-3.5f), TheText.Get("FEA_DO")); // = CFont::PrintString(m_position.x+X(242.0f-10.0f), m_position.y+Y(372.0f), TheText.Get("FEA_LE")); // < CFont::PrintString(m_position.x+X(242.0f+11.0f), m_position.y+Y(372.0f), TheText.Get("FEA_RI")); // > CFont::PrintString(m_position.x+X(242.0f+20.0f), m_position.y+Y(372.0f), TheText.Get("FEDSAS3")); // - CHANGE SELECTION break; } } break; } } CFont::DrawFonts(); /* Draw tab button texts */ CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::SetPropOn(); CFont::SetCentreOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f switch ( m_pageState ) { case PAGESTATE_NORMAL: { CFont::SetColor(CRGBA(16, 16, 16, m_someAlpha)); CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_STATS]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(92.0f), m_position.y+Y(408.0f), TheText.Get("FEB_STA")); CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_LOAD]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(158.0f), m_position.y+Y(408.0f), TheText.Get("FEB_SAV")); CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_BRIEFS]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(224.0f), m_position.y+Y(408.0f), TheText.Get("FEB_BRI")); CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_CONTROLS]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(290.0f), m_position.y+Y(408.0f), TheText.Get("FEB_CON")); CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_AUDIO]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(356.0f), m_position.y+Y(408.0f), TheText.Get("FEB_AUD")); CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_DISPLAY]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(422.0f), m_position.y+Y(408.0f), TheText.Get("FEB_DIS")); CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_LANGUAGE]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(488.0f), m_position.y+Y(408.0f), TheText.Get("FEB_LAN")); break; } case PAGESTATE_HIGHLIGHTED: case PAGESTATE_SELECTED: { CFont::SetColor(CRGBA(16, 16, 16, m_someAlpha)); switch(m_currentPage) { // PAL has 466 for 408...probably rounded? case PAGE_STATS: CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_STATS]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(92.0f), m_position.y+Y(408.0f), TheText.Get("FEB_STA")); break; case PAGE_LOAD: CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_LOAD]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(158.0f), m_position.y+Y(408.0f), TheText.Get("FEB_SAV")); break; case PAGE_BRIEFS: CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_BRIEFS]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(224.0f), m_position.y+Y(408.0f), TheText.Get("FEB_BRI")); break; case PAGE_CONTROLS: CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_CONTROLS]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(290.0f), m_position.y+Y(408.0f), TheText.Get("FEB_CON")); break; case PAGE_AUDIO: CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_AUDIO]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(356.0f), m_position.y+Y(408.0f), TheText.Get("FEB_AUD")); break; case PAGE_DISPLAY: CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_DISPLAY]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(422.0f), m_position.y+Y(408.0f), TheText.Get("FEB_DIS")); break; case PAGE_LANGUAGE: CFont::SetScale(X(BUTTONTAB_TEXT_SIZE_X*BUTTONTAB_TEXT_X_SCALES[PAGE_LANGUAGE]), Y(BUTTONTAB_TEXT_SIZE_Y)); CFont::PrintString(m_position.x+X(488.0f), m_position.y+Y(408.0f), TheText.Get("FEB_LAN")); break; } break; } } CFont::DrawFonts(); pActiveMenuPage = nil; switch(m_currentPage) { case PAGE_STATS: pActiveMenuPage = &MenuPage_Stats; break; case PAGE_LOAD: pActiveMenuPage = pMenuSave; break; case PAGE_BRIEFS: pActiveMenuPage = &MenuPage_Briefs; break; case PAGE_CONTROLS: pActiveMenuPage = &MenuPage_Controls; break; case PAGE_AUDIO: pActiveMenuPage = &MenuPage_Audio; break; case PAGE_DISPLAY: pActiveMenuPage = &MenuPage_Display; break; case PAGE_LANGUAGE: pActiveMenuPage = &MenuPage_Language; break; } CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetScale(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); CFont::SetPropOn(); CFont::SetCentreOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(40.0f)); // 600.0f CFont::SetRightJustifyWrap(X(38.0f)); if(m_currentPage == PAGE_LANGUAGE) { CFont::SetCentreOn(); CFont::SetCentreSize(SCRW-X(40.0f)); // 600.0f } if ( m_nEndPauseTimer != 0 ) { switch ( m_currentPage ) { case PAGE_LOAD: case PAGE_BRIEFS: case PAGE_CONTROLS: break; default: CFont::SetWrapx(X(1200.0f)); break; } } if(pActiveMenuPage) { pActiveMenuPage->SetAlpha(m_fade); switch ( m_pageState ) { case PAGESTATE_NORMAL: pActiveMenuPage->DrawNormal(m_position.x, m_position.y); break; case PAGESTATE_HIGHLIGHTED: pActiveMenuPage->DrawHighlighted(CRGBA(rgbaATC.r, rgbaATC.g, rgbaATC.b, m_fade), m_position.x, m_position.y); break; case PAGESTATE_SELECTED: pActiveMenuPage->Draw(CRGBA(rgbaATC.r, rgbaATC.g, rgbaATC.b, m_fade), CRGBA(MENU_SELECTED_COLOR.r, MENU_SELECTED_COLOR.g, MENU_SELECTED_COLOR.b, m_fade), m_position.x, m_position.y); break; } } CFont::DrawFonts(); CFont::DrawFonts(); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSWRAP); } void CMenuManager::DrawFrontEndSaveZone(void) { if ( bMemoryCardSpecialZone ) { static uint8 counter = 0; counter++; if ( counter & 63 ) { FillMenuWithMemCardFileListing(&MenuSaveZoneSSL_1, TriggerSaveZone_BackToMainMenuTwoLines, TriggerSaveZone_SaveSlots, nil, 0, 34, 22); if ( TheMemoryCard.GetError() == CMemoryCard::ERR_NOFORMAT ) { pActiveMenuPage = &MenuPageSaveZone_FormatCard; pActiveMenuPage->ActivatePage(); bMemoryCardSpecialZone = false; } } } CSprite2d::InitPerFrame(); CFont::InitPerFrame(); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); m_fade = 255; CSprite2d::DrawRect(CRect(X(50.0f), Y(50.0f), X(590.0f), Y(398.0f)), CRGBA(0, 0, 0, 175)); //CRect(50.0f, 57.142f, 590.0f, 454.857147f) CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetScale(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); CFont::SetPropOn(); CFont::SetCentreOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetRightJustifyWrap(X(70.0f)); CFont::SetWrapx(SCRW-X(70.0f)); // 570.0f if ( pActiveMenuPage ) { pActiveMenuPage->SetAlpha(m_fade); pActiveMenuPage->Draw(CRGBA(rgbaATC.r, rgbaATC.g, rgbaATC.b, m_fade), TITLE_TEXT_COLOR, 0.0f, 0.0f); } CFont::DrawFonts(); CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetScale(X(0.44f), Y(0.68f)); // 0.44f, 0.777143f CFont::SetPropOn(); CFont::SetCentreOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(40.0f)); //600.0f CFont::SetColor(TEXT_COLOR); wchar *text; if ( pActiveMenuPage == &MenuPageSaveZone_FormatCard || pActiveMenuPage == &MenuPageSaveZone_SaveSlots || pActiveMenuPage == &MenuPageSaveZone_SaveGame ) { text = TheText.Get("FEDS_SB"); // / button - SELECT " button - BACK } else { text = TheText.Get("FEDS_SE"); // / button - SELECT } CFont::PrintString(X(180.0f), Y(376.0f), text); // 180.0f, 429.714294f CFont::DrawFonts(); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); } void CMenuManager::DrawMemoryCardStartUpMenus() { CFont::SetAlphaFade(255.0f); bMemoryCardStartUpMenus_ExitNow = false; CMenuPage page; // + 0x40 data CMenuMultiChoiceTriggered MCMenu; MCMenu.SetPosition(X(320.0f), Y(150.0f)); //171.428574f switch ( TheMemoryCard.CheckCardStateAtGameStartUp(CARD_ONE) ) { case CMemoryCard::MCSTATE_NEED_200KB: // 200KB { // There is insufficient space on the Memory Card (PS2) in MEMORY CARD slot 1. At least 200KB is needed to save this application data. Do you wish to start? (YES or NO) MCMenu.AddTitle(TheText.Get("MCGNSP"), 0.0f, 0.0f, 0); break; } case CMemoryCard::MCSTATE_NEED_500KB: // 500KB { // There is insufficient space on the Memory Card (PS2) in MEMORY CARD slot 1. At least 500KB is needed to save this application data. Do you wish to start? (YES or NO) MCMenu.AddTitle(TheText.Get("MCDNSP"), 0.0f, 0.0f, 0); break; } case CMemoryCard::MCSTATE_OK: case CMemoryCard::MCSTATE_NOCARD: { return; break; } } MCMenu.AddOption(TheText.Get("FEM_NO"), X(30.0f), Y(110.0f), nil, 0, 0);// 125.714294f MCMenu.AddOption(TheText.Get("FEM_YES"), X(-30.0f), Y(110.0f), TriggerMCSUM_Yes, 0, 0);// 125.714294f MCMenu.SetColors(TITLE_TEXT_COLOR, TEXT_COLOR, TEXT_COLOR); page.AddMenu(&MCMenu); MCMenu.GoFirst(); page.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); CTimer::Initialise(); CTimer::StartUserPause(); while ( !bMemoryCardStartUpMenus_ExitNow ) { #ifdef GTA_PC HandleExit(); if(RsGlobal.quit) return; #endif #ifdef GTA_PC if ( CPad::GetPad(0)->GetDPadLeftJustDown() ) page.GoLeft(); if ( CPad::GetPad(0)->GetDPadRightJustDown() ) page.GoRight(); if ( CPad::GetPad(0)->GetDPadUpJustDown() ) page.GoDown(); if ( CPad::GetPad(0)->GetDPadDownJustDown() ) page.GoUp(); if ( CPad::GetPad(0)->GetCrossJustDown() || CPad::GetPad(0)->GetEnterJustDown() || CPad::GetPad(0)->GetRightMouseJustDown() ) page.SelectCurrentOptionUnderCursor(); if ( CPad::GetPad(0)->GetCircleJustDown() || CPad::GetPad(0)->GetEscapeJustDown() ) ; #else if ( CPad::GetPad(0)->GetDPadLeftJustDown() ) page.GoLeft(); if ( CPad::GetPad(0)->GetDPadRightJustDown() ) page.GoRight(); if ( CPad::GetPad(0)->GetDPadUpJustDown() ) page.GoDown(); if ( CPad::GetPad(0)->GetDPadDownJustDown() ) page.GoUp(); if ( CPad::GetPad(0)->GetCrossJustDown() ) page.SelectCurrentOptionUnderCursor(); if ( CPad::GetPad(0)->GetCircleJustDown() ) ; #endif static int32 MemCardStatusWaiter = 0; MemCardStatusWaiter++; if ( MemCardStatusWaiter > 120 ) { MemCardStatusWaiter = 0; switch ( TheMemoryCard.CheckCardStateAtGameStartUp(CARD_ONE) ) { case CMemoryCard::MCSTATE_NEED_200KB: { // There is insufficient space on the Memory Card (PS2) in MEMORY CARD slot 1. At least 200KB is needed to save this application data. Do you wish to start? (YES or NO) MCMenu.AddTitle(TheText.Get("MCGNSP"), 0.0f, 0.0f, 0); break; } case CMemoryCard::MCSTATE_NEED_500KB: { // There is insufficient space on the Memory Card (PS2) in MEMORY CARD slot 1. At least 500KB is needed to save this application data. Do you wish to start? (YES or NO) MCMenu.AddTitle(TheText.Get("MCDNSP"), 0.0f, 0.0f, 0); break; } case CMemoryCard::MCSTATE_NOCARD: { // There is no Memory Card (PS2) in MEMORY CARD slot 1. Do you wish to start? (YES or NO) MCMenu.AddTitle(TheText.Get("MCSTNS"), 0.0f, 0.0f, 0); break; } case CMemoryCard::MCSTATE_OK: { bMemoryCardStartUpMenus_ExitNow = true; break; } } } DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); CFont::InitPerFrame(); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); CSprite2d *splash = LoadSplash("splash1"); splash->Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); SetRandomActiveTextlineColor(1); CRGBA col(rgbaATC.r, rgbaATC.g, rgbaATC.b, 255); CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetScale(X(MENU_TEXT_SIZE_X), Y(MENU_TEXT_SIZE_Y)); CFont::SetPropOn(); CFont::SetJustifyOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(60.0f)); // 580.0f CFont::SetCentreOn(); CFont::SetCentreSize(SCRW-X(120.0f)); // 520.0f MCMenu.Draw(col, TITLE_TEXT_COLOR, 0.0f, 0.0f); CFont::DrawFonts(); CFont::SetFontStyle(FONT_BANK); CFont::SetScale(X(0.4f), Y(0.64f)); // 0.731429 CFont::SetPropOn(); CFont::SetCentreOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetWrapx(SCRW-X(60.0f)); // 580.0f CFont::SetColor(TEXT_COLOR); CPlaceableShText text; text.SetPosition(X(240.0f), Y(378.0f), false); // 432.000000 text.SetColor(TEXT_COLOR); text.m_text = TheText.Get("FEDS_SE"); // / button - SELECT text.SetShadows(true, TEXT_SHADOW_COLOR, SHADOW_VECTOR); text.Draw(0.0f, 0.0f); CFont::DrawFonts(); DisplayWarningControllerMsg(); DoRWStuffEndOfFrame(); CPad::UpdatePads(); CTimer::Update(); } CTimer::EndUserPause(); CTimer::Stop(); for ( int32 i = 0; i < 100; i++ ) { #ifdef GTA_PC HandleExit(); #endif DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); CSprite2d *splash = LoadSplash("splash1"); splash->Draw(CRect(0.0f, 0.0f, SCRW, SCRH), BACKGROUND_SPLASH_COLOR); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); DoRWStuffEndOfFrame(); } } void CMenuManager::Process(void) { if ( m_bSaveMenuActive || m_bInSaveZone || TheCamera.GetScreenFadeStatus() == FADE_0 ) { InitialiseMenusOnce(); m_bWantToRestart = false; WorkOutMenuState(false); if ( m_bMenuActive ) { if ( !m_bInSaveZone ) LoadAllTextures(); InitialiseMenuContents(); SetRandomActiveTextlineColor(0); ProcessControllerInput(); } else { AnaliseMenuContents(); pMenuSave = &MenuPage_SaveBasic; m_pageState = PAGESTATE_NORMAL; bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; UnloadTextures(); m_bInSaveZone = false; m_bRenderGameInMenu = false; gErrorSampleTriggered = true; } } } void CMenuManager::WorkOutMenuState(uint8 bExit) { #ifdef GTA_PC bool bIsStartPressed = CPad::GetPad(0)->GetStartJustDown() || (m_pageState == PAGESTATE_NORMAL && CPad::GetPad(0)->GetEscapeJustDown()); #else bool bIsStartPressed = CPad::GetPad(0)->GetStartJustDown(); #endif bool bIsCreditsOrDraw = CCredits::AreCreditsDone() || m_bMenuActive; bool bIsDemoOrDraw = m_bMenuActive || CGame::bDemoMode; if ( (bIsStartPressed && bIsCreditsOrDraw) || bExit || (!bIsDemoOrDraw && CPad::IsNoOrObsolete()) ) { if ( m_nStartPauseTimer == 0 && m_nEndPauseTimer == 0 ) { m_bMenuActive = !m_bMenuActive; if ( !m_bMenuActive ) { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); DMAudio.ChangeMusicMode(MUSICMODE_GAME); gMusicPlaying = false; bMemoryCardSpecialZone = false; bIgnoreTriangleButton = false; m_bMenuActive = true; m_nEndPauseTimer = CTimer::GetTimeInMillisecondsPauseMode() + 800; if ( m_currentPage == PAGE_CONTROLS || m_currentPage == PAGE_BRIEFS || m_currentPage == PAGE_LOAD ) { m_nSlidingDir = CGeneral::GetRandomNumber() & (SLIDE_MAX-1); switch ( m_nSlidingDir ) //m_nSlidingDir &= ~1; { case SLIDE_TO_LEFT: m_nSlidingDir = SLIDE_TO_TOP; break; case SLIDE_TO_RIGHT: m_nSlidingDir = SLIDE_TO_BOTTOM; break; } m_position.y = Y(500.0f); // 571.428589f; } } else { DMAudio.ChangeMusicMode(MUSICMODE_FRONTEND); if ( DMAudio.GetRadioInCar() < 9 ) m_PrefsRadioStation = DMAudio.GetRadioInCar(); else m_PrefsRadioStation = CGeneral::GetRandomNumber() % 9; CTimer::StartUserPause(); CPad::StopPadsShaking(); m_nStartPauseTimer = CTimer::GetTimeInMillisecondsPauseMode() + 800; m_nSlidingDir = CGeneral::GetRandomNumber() & (SLIDE_MAX-1); switch ( m_nSlidingDir ) { case SLIDE_TO_RIGHT: m_position.y = Y(612.5f); break; case SLIDE_TO_LEFT: m_position.y = Y(612.5f); break; case SLIDE_TO_BOTTOM: m_position.y = Y(500.0f); break; case SLIDE_TO_TOP: m_position.y = Y(500.0f); break; default: m_position.y = Y(500.0f); break; } if ( m_currentPage == PAGE_CONTROLS || m_currentPage == PAGE_BRIEFS ) { m_nSlidingDir = CGeneral::GetRandomNumber() & (SLIDE_MAX-1); switch ( m_nSlidingDir ) //m_nSlidingDir &= ~1; { case SLIDE_TO_LEFT: m_nSlidingDir = SLIDE_TO_TOP; break; case SLIDE_TO_RIGHT: m_nSlidingDir = SLIDE_TO_BOTTOM; break; } m_position.y = Y(500.0f); //571.428589f } } } } if ( m_bSaveMenuActive && !m_bInSaveZone && !TheMemoryCard._bunk2) { m_bSaveMenuActive = false; m_bInSaveZone = true; m_bRenderGameInMenu = true; m_bMenuActive = true; CTimer::StartUserPause(); pActiveMenuPage = &MenuPageSaveZone_SaveGame; } if ( m_pageState == PAGESTATE_NORMAL && gMusicPlaying ) { DMAudio.StopFrontEndTrack(); gMusicPlaying = false; } if ( m_nChangePageTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nChangePageTimer ) { m_nChangePageTimer = 0; pMenuSave = &MenuPage_SaveBasic; m_currentPage = m_newPage; } if ( m_nPageLeftTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nPageLeftTimer ) m_nPageLeftTimer = 0; if ( m_nPageRightTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nPageRightTimer ) m_nPageRightTimer = 0; if ( m_nStartPauseTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nStartPauseTimer ) m_nStartPauseTimer = 0; if ( m_nEndPauseTimer != 0 && CTimer::GetTimeInMillisecondsPauseMode() >= m_nEndPauseTimer ) { m_nEndPauseTimer = 0; m_bMenuActive = false; m_bMenuActive = false; m_bInSaveZone = false; CTimer::EndUserPause(); } } void CMenuManager::ProcessControllerInput(void) { if ( TimeToStopPadShaking != 0 && TimeToStopPadShaking < CTimer::GetTimeInMillisecondsPauseMode() ) { CPad::StopPadsShaking(); TimeToStopPadShaking = 0; } #ifdef GTA_PC if ( CPad::GetPad(0)->GetDPadLeft() || CPad::GetPad(0)->GetLeft() ) #else if ( CPad::GetPad(0)->GetDPadLeft() ) #endif { switch ( m_pageState ) { case PAGESTATE_NORMAL: case PAGESTATE_HIGHLIGHTED: break; case PAGESTATE_SELECTED: { if ( pActiveMenuPage ) pActiveMenuPage->GoLeftStill(); break; } } } #ifdef GTA_PC if ( CPad::GetPad(0)->GetDPadRight() || CPad::GetPad(0)->GetRight() ) #else if ( CPad::GetPad(0)->GetDPadRight() ) #endif { switch ( m_pageState ) { case PAGESTATE_NORMAL: case PAGESTATE_HIGHLIGHTED: break; case PAGESTATE_SELECTED: { if ( pActiveMenuPage ) pActiveMenuPage->GoRightStill(); break; } } } #ifdef GTA_PC if ( CPad::GetPad(0)->GetDPadLeftJustDown() || CPad::GetPad(0)->GetLeftJustDown() ) #else if ( CPad::GetPad(0)->GetDPadLeftJustDown() ) #endif ProcessDPadLeftJustDown(); #ifdef GTA_PC if ( CPad::GetPad(0)->GetDPadRightJustDown() || CPad::GetPad(0)->GetRightJustDown() ) #else if ( CPad::GetPad(0)->GetDPadRightJustDown() ) #endif ProcessDPadRightJustDown(); #ifdef GTA_PC if ( CPad::GetPad(0)->GetDPadUp() || CPad::GetPad(0)->GetUp() ) #else if ( CPad::GetPad(0)->GetDPadUp() ) #endif { switch ( m_pageState ) { case PAGESTATE_NORMAL: { if ( m_currentPage == PAGE_STATS ) { if ( pActiveMenuPage ) pActiveMenuPage->GoUpStill(); } break; } case PAGESTATE_HIGHLIGHTED: break; case PAGESTATE_SELECTED: { if ( pActiveMenuPage ) pActiveMenuPage->GoUpStill(); break; } } } #ifdef GTA_PC if ( CPad::GetPad(0)->GetDPadDown() || CPad::GetPad(0)->GetDown() ) #else if ( CPad::GetPad(0)->GetDPadDown() ) #endif { switch ( m_pageState ) { case PAGESTATE_NORMAL: { if ( m_currentPage == PAGE_STATS ) { if ( pActiveMenuPage ) pActiveMenuPage->GoDownStill(); } break; } case PAGESTATE_HIGHLIGHTED: break; case PAGESTATE_SELECTED: { if ( pActiveMenuPage ) pActiveMenuPage->GoDownStill(); break; } } } #ifdef GTA_PC if ( CPad::GetPad(0)->GetDPadUpJustDown() || CPad::GetPad(0)->GetUpJustDown() ) #else if ( CPad::GetPad(0)->GetDPadUpJustDown() ) #endif ProcessDPadUpJustDown(); #ifdef GTA_PC if ( CPad::GetPad(0)->GetDPadDownJustDown() || CPad::GetPad(0)->GetDownJustDown() ) #else if ( CPad::GetPad(0)->GetDPadDownJustDown() ) #endif ProcessDPadDownJustDown(); if ( CPad::GetPad(0)->GetLeftShoulder1JustDown() ) { switch ( m_pageState ) { case PAGESTATE_NORMAL: ProcessDPadLeftJustDown(); break; case PAGESTATE_HIGHLIGHTED: case PAGESTATE_SELECTED: break; } } if ( CPad::GetPad(0)->GetRightShoulder1JustDown() ) { switch ( m_pageState ) { case PAGESTATE_NORMAL: ProcessDPadRightJustDown(); break; case PAGESTATE_HIGHLIGHTED: case PAGESTATE_SELECTED: break; } } #ifdef GTA_PC if ( CPad::GetPad(0)->GetCrossJustDown() || CPad::GetPad(0)->GetEnterJustDown() || CPad::GetPad(0)->GetRightMouseJustDown() ) #else if ( CPad::GetPad(0)->GetCrossJustDown() ) #endif ProcessDPadCrossJustDown(); #ifdef GTA_PC if ( CPad::GetPad(0)->GetTriangleJustDown() || CPad::GetPad(0)->GetBackspaceJustDown() || (m_pageState != PAGESTATE_NORMAL && CPad::GetPad(0)->GetEscapeJustDown()) ) #else if ( CPad::GetPad(0)->GetTriangleJustDown() ) #endif ProcessDPadTriangleJustDown(); } void CMenuManager::ProcessDPadLeftJustDown(void) { if ( m_bInSaveZone ) { if ( pActiveMenuPage ) { pActiveMenuPage->GoLeft(); if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveZoneSSL_1 ) { if ( MenuSaveZoneSSL_1.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } } else { switch ( m_pageState ) { case PAGESTATE_NORMAL: { if ( !bMemoryCardSpecialZone && !m_bInSaveZone ) { if ( m_nChangePageTimer == 0 ) { if ( --m_newPage < PAGE_FIRST ) m_newPage = PAGE_LAST; m_nPageLeftTimer = CTimer::GetTimeInMillisecondsPauseMode() + 300; m_nPageRightTimer = 0; m_nChangePageTimer = CTimer::GetTimeInMillisecondsPauseMode() + 250; field_18 = m_newPage; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); } } break; } case PAGESTATE_HIGHLIGHTED: { if ( pActiveMenuPage ) pActiveMenuPage->GoLeftMenuOnPage(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); break; } case PAGESTATE_SELECTED: { if ( pActiveMenuPage ) { pActiveMenuPage->GoLeft(); if ( m_currentPage == PAGE_AUDIO) { if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_1 ) ; else if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else if ( m_currentPage == PAGE_DISPLAY) { if ( pActiveMenuPage->m_pCurrentControl == &MenuDisplay_1 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else { if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 ) { if ( MenuSaveDG_2.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) { if ( MenuSaveLG_2.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } } break; } } } } void CMenuManager::ProcessDPadRightJustDown(void) { if ( m_bInSaveZone ) { if ( pActiveMenuPage ) { pActiveMenuPage->GoRight(); if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveZoneSSL_1 ) { if ( MenuSaveZoneSSL_1.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } } else { switch ( m_pageState ) { case PAGESTATE_NORMAL: { if ( !bMemoryCardSpecialZone && !m_bInSaveZone ) { if ( m_nChangePageTimer == 0 ) { if ( ++m_newPage > PAGE_LAST ) m_newPage = PAGE_FIRST; m_nPageLeftTimer = 0; m_nPageRightTimer = CTimer::GetTimeInMillisecondsPauseMode() + 300; m_nChangePageTimer = CTimer::GetTimeInMillisecondsPauseMode() + 250; field_18 = m_newPage; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); } } break; } case PAGESTATE_HIGHLIGHTED: { if ( pActiveMenuPage ) pActiveMenuPage->GoRightMenuOnPage(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); break; } case PAGESTATE_SELECTED: { if ( pActiveMenuPage ) { pActiveMenuPage->GoRight(); if ( m_currentPage == PAGE_AUDIO) { if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_1 ) ; else if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else if ( m_currentPage == PAGE_DISPLAY) { if ( pActiveMenuPage->m_pCurrentControl == &MenuDisplay_1 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else { if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 ) { if ( MenuSaveDG_2.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) { if ( MenuSaveLG_2.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } } break; } } } } void CMenuManager::ProcessDPadUpJustDown(void) { if ( m_bInSaveZone ) { if ( pActiveMenuPage ) { pActiveMenuPage->GoUp(); if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveZoneSSL_1 ) { if ( MenuSaveZoneSSL_1.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } } else { switch ( m_pageState ) { case PAGESTATE_NORMAL: break; case PAGESTATE_HIGHLIGHTED: { if ( pActiveMenuPage ) pActiveMenuPage->GoUpMenuOnPage(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); break; } case PAGESTATE_SELECTED: { if ( pActiveMenuPage ) { pActiveMenuPage->GoUp(); if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 ) { if ( MenuSaveDG_2.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) { if ( MenuSaveLG_2.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } break; } } } } void CMenuManager::ProcessDPadDownJustDown(void) { if ( m_bInSaveZone ) { if ( pActiveMenuPage ) { pActiveMenuPage->GoDown(); if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveZoneSSL_1 ) { if ( MenuSaveZoneSSL_1.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } } else { switch ( m_pageState ) { case PAGESTATE_NORMAL: break; case PAGESTATE_HIGHLIGHTED: { if ( pActiveMenuPage ) pActiveMenuPage->GoDownMenuOnPage(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); break; } case PAGESTATE_SELECTED: { if ( pActiveMenuPage ) { pActiveMenuPage->GoDown(); if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 ) { if ( MenuSaveDG_2.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) { if ( MenuSaveLG_2.m_numOptions < 2 ) ; else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NAVIGATION, 0); } break; } } } } void CMenuManager::ProcessDPadTriangleJustDown(void) { if ( pActiveMenuPage ) { pActiveMenuPage->SelectDefaultCancelAction(); if ( m_bMenuActive || m_bInSaveZone ) { if ( bIgnoreTriangleButton ) { if ( m_bInSaveZone ) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_BACK, 0); else if ( pActiveMenuPage->m_pCurrentControl == &MenuSaveDG_2 || pActiveMenuPage->m_pCurrentControl == &MenuSaveLG_2 ) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_BACK, 0); } else if ( !bIgnoreTriangleButton ) { switch ( m_pageState ) { case PAGESTATE_NORMAL: WorkOutMenuState(true); break; case PAGESTATE_HIGHLIGHTED: m_pageState = PAGESTATE_NORMAL; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); break; case PAGESTATE_SELECTED: { m_pageState = PAGESTATE_HIGHLIGHTED; if ( pActiveMenuPage ) { if ( pActiveMenuPage->m_numControls == 1 ) { m_pageState = PAGESTATE_NORMAL; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_BACK, 0); } break; } } } } } else { if ( !bIgnoreTriangleButton ) { switch ( m_pageState ) { case PAGESTATE_NORMAL: WorkOutMenuState(false); break; case PAGESTATE_HIGHLIGHTED: m_pageState = PAGESTATE_NORMAL; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); break; case PAGESTATE_SELECTED: { m_pageState = PAGESTATE_HIGHLIGHTED; if ( pActiveMenuPage ) { if ( pActiveMenuPage->m_numControls == 1 ) { m_pageState = PAGESTATE_NORMAL; DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_NEW_PAGE, 0); } else DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_BACK, 0); } break; } } } } } void CMenuManager::ProcessDPadCrossJustDown(void) { if ( m_bInSaveZone ) { if ( pActiveMenuPage ) pActiveMenuPage->SelectCurrentOptionUnderCursor(); DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); } else { if ( m_currentPage != PAGE_STATS && m_currentPage != PAGE_BRIEFS) { switch ( m_pageState ) { case PAGESTATE_NORMAL: { m_pageState = PAGESTATE_HIGHLIGHTED; if ( pActiveMenuPage ) { if ( pActiveMenuPage->m_numControls == 1 ) m_pageState = PAGESTATE_SELECTED; } switch ( m_currentPage ) { case PAGE_AUDIO: { if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_1 || pActiveMenuPage->m_pCurrentControl == &MenuAudio_2 || pActiveMenuPage->m_pCurrentControl == &MenuAudio_3 || pActiveMenuPage->m_pCurrentControl == &MenuAudio_4 ) { if ( !gMusicPlaying ) { DMAudio.PlayFrontEndTrack(m_PrefsRadioStation, 1); gMusicPlaying = true; } } else { DMAudio.StopFrontEndTrack(); gMusicPlaying = false; } break; } } DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); break; } case PAGESTATE_HIGHLIGHTED: { m_pageState = PAGESTATE_SELECTED; DoHackingMenusAtPageBrowse(); if ( pActiveMenuPage ) { if ( pActiveMenuPage->IsActiveMenuTwoState()) { m_pageState = PAGESTATE_HIGHLIGHTED; pActiveMenuPage->ActiveMenuTwoState_SelectNextPosition(); } } switch ( m_currentPage ) { case PAGE_AUDIO: { if ( pActiveMenuPage->m_pCurrentControl != &MenuAudio_4 ) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); break; } default: { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); DMAudio.StopFrontEndTrack(); gMusicPlaying = false; break; } } break; } case PAGESTATE_SELECTED: { if ( pActiveMenuPage ) { pActiveMenuPage->SelectCurrentOptionUnderCursor(); switch ( m_currentPage ) { case PAGE_AUDIO: { if ( pActiveMenuPage->m_pCurrentControl != &MenuAudio_3 ) m_pageState = PAGESTATE_HIGHLIGHTED; break; } case PAGE_LOAD: case PAGE_LANGUAGE: break; default: m_pageState = PAGESTATE_HIGHLIGHTED; break; } } DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); break; } } } } } void CMenuManager::DoHackingMenusAtPageBrowse(void) { if ( pActiveMenuPage ) { switch ( m_currentPage ) { case PAGE_CONTROLS: { if ( pActiveMenuPage->m_pCurrentControl == &MenuControls_1 ) { int32 sel = MenuControls_1.GetMenuSelection(); MenuControls_1.GoFirst(); for ( int32 i = 0; i < sel; i++ ) MenuControls_1.GoNext(); } break; } case PAGE_AUDIO: { if ( pActiveMenuPage->m_pCurrentControl == &MenuAudio_3 ) { int32 sel = MenuAudio_3.GetMenuSelection(); MenuAudio_3.GoFirst(); for ( int32 i = 0; i < sel; i++ ) MenuAudio_3.GoNext(); } break; } } } } void CMenuManager::SetSoundLevelsForMusicMenu(void) { DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); } void CMenuManager::FilterOutColorMarkersFromString(wchar *string, CRGBA &color) { wchar buf[300]; UnicodeStrcpy(buf, string); wchar *src = buf; wchar *dst = string; while ( *src != '\0' ) { if ( *src == '~' ) { src++; if ( *src == 'l' ) color = CRGBA(0, 0, 0, 255); else if ( *src == 'p' ) color = CRGBA(255, 0, 255, 255); else if ( *src == 'y' ) color = CRGBA(255, 255, 0, 255); else if ( *src == 'w' ) color = CRGBA(255, 255, 255, 255); else if ( *src == 'b' ) color = CRGBA(40, 40, 255, 255); else if ( *src == 'g' ) color = CRGBA(40, 235, 40, 255); else if ( *src == 'r' ) color = CRGBA(255, 0, 0, 255); while ( *src++ != '~' ) ; } else *dst++ = *src++; } *dst = '\0'; } #endif ================================================ FILE: src/core/Frontend_PS2.h ================================================ #pragma once #include "Sprite2d.h" enum { PAGE_STATS, PAGE_LOAD, PAGE_BRIEFS, PAGE_CONTROLS, PAGE_AUDIO, PAGE_DISPLAY, PAGE_LANGUAGE, NUM_PAGES, PAGE_FIRST = PAGE_STATS, PAGE_LAST = PAGE_LANGUAGE, }; enum { PAGESTATE_NORMAL = 0, PAGESTATE_HIGHLIGHTED, PAGESTATE_SELECTED }; enum eFrontendSprites { FE2_MAINPANEL_UL, FE2_MAINPANEL_UR, FE2_MAINPANEL_DL, FE2_MAINPANEL_DR, FE2_MAINPANEL_DR2, FE2_TABACTIVE, FE_ICONBRIEF, FE_ICONSTATS, FE_ICONCONTROLS, FE_ICONSAVE, FE_ICONAUDIO, FE_ICONDISPLAY, FE_ICONLANGUAGE, FE_CONTROLLER, FE_CONTROLLERSH, FE_ARROWS1, FE_ARROWS2, FE_ARROWS3, FE_ARROWS4, FE_RADIO1, FE_RADIO2, FE_RADIO3, FE_RADIO4, FE_RADIO5, FE_RADIO6, FE_RADIO7, FE_RADIO8, FE_RADIO9, NUM_FE_SPRITES }; class CSprite2d; class CVector2D; #ifdef GTA_PC enum eControlMethod { CONTROL_STANDARD = 0, CONTROL_CLASSIC, }; #endif class CMenuManager { public: enum LANGUAGE { LANGUAGE_AMERICAN, LANGUAGE_FRENCH, LANGUAGE_GERMAN, LANGUAGE_ITALIAN, LANGUAGE_SPANISH, #ifdef MORE_LANGUAGES LANGUAGE_POLISH, LANGUAGE_RUSSIAN, LANGUAGE_JAPANESE, #endif }; enum CONTRCONFIG { CONFIG_1 = 0, CONFIG_2, CONFIG_3, CONFIG_4, }; enum { NUM_SPRIRES = 28, }; enum { PAGESTATE_NORMAL = 0, PAGESTATE_HIGHLIGHTED = 1, PAGESTATE_SELELECTED = 2, }; enum { SLIDE_TO_BOTTOM = 0, SLIDE_TO_RIGHT, SLIDE_TO_TOP, SLIDE_TO_LEFT, SLIDE_MAX }; int32 m_currentPage; int32 m_newPage; int32 m_pageState; uint32 m_nPageLeftTimer; uint32 m_nPageRightTimer; uint32 m_nChangePageTimer; int field_18; uint8 m_fade; uint8 m_someAlpha; //char field_1E; // unused ? //char field_1F; // unused ? uint32 m_nStartPauseTimer; uint32 m_nEndPauseTimer; CVector2D m_position; uint8 m_nSlidingDir; //char field_31; // unused ? //char field_32; // unused ? //char field_33; // unused ? bool m_bInitialised; bool m_bWantToUpdateContent; bool m_bMenuActive; bool m_bWantToRestart; //char field_38; //unused ? bool m_bRenderGameInMenu; bool m_bSaveMenuActive; bool m_bInSaveZone; char field_3C; bool m_bTexturesLoaded; //char field_3E; //unused ? //char field_3F; //unused ? CSprite2d m_sprites[NUM_SPRIRES]; static int32 m_PrefsSfxVolume; static int32 m_PrefsMusicVolume; static int32 m_PrefsBrightness; static bool m_PrefsShowTrails; static bool m_PrefsShowSubtitles; static bool m_PrefsAllowNastyGame; static int32 m_PrefsRadioStation; static int32 m_PrefsStereoMono; static int8 m_PrefsUseWideScreen; static int32 m_PrefsLanguage; static CONTRCONFIG m_PrefsControllerConfig; static bool m_PrefsUseVibration; #ifdef CUTSCENE_BORDERS_SWITCH static bool m_PrefsCutsceneBorders; #endif #ifdef GTA_PC bool m_bQuitGameNoCD; int32 m_nMouseTempPosX; int32 m_nMouseTempPosY; int32 m_nPrefsVideoMode; int32 m_nDisplayVideoMode; int8 m_nPrefsAudio3DProviderIndex; static int32 OS_Language; static int8 m_PrefsVsync; static int8 m_PrefsVsyncDisp; static int8 m_PrefsFrameLimiter; static int8 m_PrefsSpeakers; static int32 m_ControlMethod; static int8 m_PrefsDMA; static float m_PrefsLOD; static char m_PrefsSkinFile[256]; #ifndef MASTER static bool m_PrefsMarketing; static bool m_PrefsDisableTutorials; #endif // !MASTER #ifdef MENU_MAP static bool bMenuMapActive; static float fMapSize; static float fMapCenterY; static float fMapCenterX; #endif #ifdef IMPROVED_VIDEOMODE int32 m_nPrefsWidth = 640; int32 m_nPrefsHeight = 480; int32 m_nPrefsDepth = 32; int32 m_nPrefsWindowed = 1; int32 m_nPrefsSubsystem; int32 m_nSelectedScreenMode; #endif void WaitForUserCD() { } #endif bool GetIsMenuActive() {return !!m_bMenuActive;} CMenuManager(void); #ifdef FIX_BUGS ~CMenuManager(void) { UnloadTextures(); } #endif void LoadAllTextures(void); void UnloadTextures(void); void InitialiseMenusOnce(void); void InitialiseChangedLanguageSettings(void); void InitialiseMenuContents(void); void AnaliseMenuContents(void); void InitialiseMenuContentsAfterLoadingGame(void); void DrawFrontEnd(void); void DrawFrontEndNormal(void); void DrawFrontEndSaveZone(void); void DrawMemoryCardStartUpMenus(void); void Process(void); void WorkOutMenuState(uint8 bExit); void ProcessControllerInput(void); void ProcessDPadLeftJustDown(void); void ProcessDPadRightJustDown(void); void ProcessDPadUpJustDown(void); void ProcessDPadDownJustDown(void); void ProcessDPadTriangleJustDown(void); void ProcessDPadCrossJustDown(void); void DoHackingMenusAtPageBrowse(void); void SetSoundLevelsForMusicMenu(void); void FilterOutColorMarkersFromString(wchar *string, CRGBA &color); }; extern CMenuManager FrontEndMenuManager; ================================================ FILE: src/core/Game.cpp ================================================ #include "common.h" #include "platform.h" #include "Game.h" #include "main.h" #include "RwHelper.h" #include "Accident.h" #include "Antennas.h" #include "Bridge.h" #include "CarCtrl.h" #include "CarGen.h" #include "CdStream.h" #include "Clock.h" #include "Clouds.h" #include "Collision.h" #include "Console.h" #include "Coronas.h" #include "Cranes.h" #include "Credits.h" #include "CutsceneMgr.h" #include "DMAudio.h" #include "Darkel.h" #include "Debug.h" #include "EventList.h" #include "FileLoader.h" #include "FileMgr.h" #include "Fire.h" #include "Fluff.h" #include "Font.h" #include "Frontend.h" #include "frontendoption.h" #include "GameLogic.h" #include "Garages.h" #include "GenericGameStorage.h" #include "Glass.h" #include "HandlingMgr.h" #include "Heli.h" #include "Hud.h" #include "IniFile.h" #include "Lights.h" #include "MBlur.h" #include "Messages.h" #include "MemoryCard.h" #include "MemoryHeap.h" #include "Pad.h" #include "Particle.h" #include "ParticleObject.h" #include "PedRoutes.h" #include "Phones.h" #include "Pickups.h" #include "Plane.h" #include "PlayerSkin.h" #include "Population.h" #include "Radar.h" #include "Record.h" #include "References.h" #include "Renderer.h" #include "Replay.h" #include "Restart.h" #include "RoadBlocks.h" #include "Rubbish.h" #include "SceneEdit.h" #include "Script.h" #include "Shadows.h" #include "Skidmarks.h" #include "SetPieces.h" #include "SpecialFX.h" #include "Stats.h" #include "Streaming.h" #include "SurfaceTable.h" #include "TempColModels.h" #include "Timecycle.h" #include "TrafficLights.h" #include "Train.h" #include "TxdStore.h" #include "User.h" #include "VisibilityPlugins.h" #include "WaterCannon.h" #include "WaterLevel.h" #include "Weapon.h" #include "WeaponEffects.h" #include "Weather.h" #include "World.h" #include "ZoneCull.h" #include "Zones.h" #include "Occlusion.h" #include "debugmenu.h" #include "Ropes.h" #include "WindModifiers.h" #include "WaterCreatures.h" #include "postfx.h" #include "custompipes.h" #include "screendroplets.h" #include "VarConsole.h" #ifdef USE_TEXTURE_POOL #include "TexturePools.h" #endif eLevelName CGame::currLevel; int32 CGame::currArea; bool CGame::bDemoMode = true; bool CGame::nastyGame = true; bool CGame::frenchGame; bool CGame::germanGame; bool CGame::noProstitutes; bool CGame::playingIntro; char CGame::aDatFile[32]; #ifdef MORE_LANGUAGES bool CGame::russianGame = false; bool CGame::japaneseGame = false; #endif #ifndef MASTER CVector CGame::PlayerCoords; bool8 CGame::VarUpdatePlayerCoords; #endif int gameTxdSlot; #ifdef SECUROM uint8 gameProcessPirateCheck = 0; #endif bool DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); void DoRWStuffEndOfFrame(void); #ifdef PS2_MENU void MessageScreen(char *msg) { //TODO: stretch_screen CRect rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); CRGBA color(255, 255, 255, 255); DoRWStuffStartOfFrame(50, 50, 50, 0, 0, 0, 255); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); DefinedState(); CSprite2d *splash = LoadSplash(NULL); splash->Draw(rect, color, color, color, color); splash->DrawRect(CRect(SCREEN_SCALE_X(20.0f), SCREEN_SCALE_Y(110.0f), SCREEN_SCALE_X(620.0f), SCREEN_SCALE_Y(300.0f)), CRGBA(50, 50, 50, 192)); CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetWrapx(SCREEN_SCALE_FROM_RIGHT(190.0f)); // 450.0f CFont::SetScale(SCREEN_SCALE_X(1.0f), SCREEN_SCALE_Y(1.0f)); CFont::SetCentreOn(); CFont::SetCentreSize(SCREEN_SCALE_X(450.0f)); CFont::SetJustifyOff(); CFont::SetColor(CRGBA(255, 255, 255, 255)); CFont::SetDropColor(CRGBA(32, 32, 32, 255)); CFont::SetDropShadowPosition(3); CFont::SetPropOn(); CFont::PrintString(SCREEN_SCALE_X(320.0f), SCREEN_SCALE_Y(130.0f), TheText.Get(msg)); CFont::DrawFonts(); DoRWStuffEndOfFrame(); } #endif bool CGame::InitialiseOnceBeforeRW(void) { CFileMgr::Initialise(); CdStreamInit(MAX_CDCHANNELS); debug("size of matrix %d\n", sizeof(CMatrix)); debug("size of placeable %d\n", sizeof(CPlaceable)); debug("size of entity %d\n", sizeof(CEntity)); debug("size of building %d\n", sizeof(CBuilding)); debug("size of dummy %d\n", sizeof(CDummy)); #ifdef EXTENDED_COLOURFILTER CPostFX::InitOnce(); #endif #ifdef CUSTOM_FRONTEND_OPTIONS // Not needed here but may be needed in future // if (numCustomFrontendOptions == 0 && numCustomFrontendScreens == 0) CustomFrontendOptionsPopulate(); #endif return true; } #ifndef LIBRW #ifdef PS2_MATFX void ReplaceMatFxCallback(); #endif // PS2_MATFX #ifdef PS2_ALPHA_TEST void ReplaceAtomicPipeCallback(); #endif // PS2_ALPHA_TEST #endif // !LIBRW bool CGame::InitialiseRenderWare(void) { ValidateVersion(); #ifdef USE_TEXTURE_POOL _TexturePoolsInitialise(); #endif CTxdStore::Initialise(); CVisibilityPlugins::Initialise(); #ifdef GTA_PS2 RpSkySelectTrueTSClipper(TRUE); RpSkySelectTrueTLClipper(TRUE); // PS2ManagerApplyDirectionalLightingCB() uploads the GTA lights // directly without going through RpWorld and all that SetupPS2ManagerDefaultLightingCallback(); PreAllocateRwObjects(); #endif /* Create camera */ Scene.camera = CameraCreate(SCREEN_WIDTH, SCREEN_HEIGHT, TRUE); ASSERT(Scene.camera != nil); if (!Scene.camera) { return (false); } RwCameraSetFarClipPlane(Scene.camera, 2000.0f); RwCameraSetNearClipPlane(Scene.camera, 0.9f); CameraSize(Scene.camera, nil, DEFAULT_VIEWWINDOW, DEFAULT_ASPECT_RATIO); /* Create a world */ RwBBox bbox; bbox.sup.x = bbox.sup.y = bbox.sup.z = 10000.0f; bbox.inf.x = bbox.inf.y = bbox.inf.z = -10000.0f; Scene.world = RpWorldCreate(&bbox); ASSERT(Scene.world != nil); if (!Scene.world) { CameraDestroy(Scene.camera); Scene.camera = nil; return (false); } /* Add the camera to the world */ RpWorldAddCamera(Scene.world, Scene.camera); LightsCreate(Scene.world); CreateDebugFont(); #ifdef LIBRW #ifdef PS2_MATFX rw::MatFX::modulateEnvMap = true; #else rw::MatFX::modulateEnvMap = false; #endif #else #ifdef PS2_MATFX ReplaceMatFxCallback(); #endif // PS2_MATFX #ifdef PS2_ALPHA_TEST ReplaceAtomicPipeCallback(); #endif // PS2_ALPHA_TEST #endif // LIBRW PUSH_MEMID(MEMID_TEXTURES); CFont::Initialise(); CHud::Initialise(); CPlayerSkin::Initialise(); POP_MEMID(); #ifdef EXTENDED_PIPELINES CustomPipes::CustomPipeInit(); // need Scene.world for this #endif #ifdef SCREEN_DROPLETS ScreenDroplets::InitDraw(); #endif return (true); } void CGame::ShutdownRenderWare(void) { #ifdef SCREEN_DROPLETS ScreenDroplets::Shutdown(); #endif #ifdef EXTENDED_PIPELINES CustomPipes::CustomPipeShutdown(); #endif DestroySplashScreen(); CHud::Shutdown(); CFont::Shutdown(); for ( int32 i = 0; i < NUMPLAYERS; i++ ) CWorld::Players[i].DeletePlayerSkin(); CPlayerSkin::Shutdown(); DestroyDebugFont(); /* Destroy world */ LightsDestroy(Scene.world); RpWorldRemoveCamera(Scene.world, Scene.camera); RpWorldDestroy(Scene.world); /* destroy camera */ CameraDestroy(Scene.camera); Scene.world = nil; Scene.camera = nil; CVisibilityPlugins::Shutdown(); #ifdef USE_TEXTURE_POOL _TexturePoolsShutdown(); #endif } bool CGame::InitialiseOnceAfterRW(void) { TheText.Load(); CTimer::Initialise(); CTempColModels::Initialise(); mod_HandlingManager.Initialise(); CSurfaceTable::Initialise("DATA\\SURFACE.DAT"); CPedStats::Initialise(); CTimeCycle::Initialise(); #ifdef GTA_PS2 LoadingScreen("Loading the Game", "Initialising audio", GetRandomSplashScreen()); #endif DMAudio.Initialise(); #ifndef GTA_PS2 if ( DMAudio.GetNum3DProvidersAvailable() == 0 ) FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = NO_AUDIO_PROVIDER; if ( FrontEndMenuManager.m_nPrefsAudio3DProviderIndex == AUDIO_PROVIDER_NOT_DETERMINED || FrontEndMenuManager.m_nPrefsAudio3DProviderIndex == -2 ) { FrontEndMenuManager.m_PrefsSpeakers = 0; FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = DMAudio.AutoDetect3DProviders(); } DMAudio.SetCurrent3DProvider(FrontEndMenuManager.m_nPrefsAudio3DProviderIndex); DMAudio.SetSpeakerConfig(FrontEndMenuManager.m_PrefsSpeakers); DMAudio.SetDynamicAcousticModelingStatus(FrontEndMenuManager.m_PrefsDMA); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); DMAudio.SetEffectsFadeVol(127); DMAudio.SetMusicFadeVol(127); #endif return true; } void CGame::FinalShutdown(void) { CTxdStore::Shutdown(); CPedStats::Shutdown(); CdStreamShutdown(); } bool CGame::Initialise(const char* datFile) { ResetLoadingScreenBar(); strcpy(aDatFile, datFile); #ifdef GTA_PS2 // TODO: upload VU0 collision code here #endif CPools::Initialise(); #ifndef GTA_PS2 CIniFile::LoadIniFile(); #endif #ifdef USE_TEXTURE_POOL _TexturePoolsUnknown(false); #endif currLevel = LEVEL_BEACH; currArea = AREA_MAIN_MAP; PUSH_MEMID(MEMID_TEXTURES); LoadingScreen("Loading the Game", "Loading generic textures", GetRandomSplashScreen()); gameTxdSlot = CTxdStore::AddTxdSlot("generic"); CTxdStore::Create(gameTxdSlot); CTxdStore::AddRef(gameTxdSlot); #ifdef EXTENDED_PIPELINES // for generic fallback CustomPipes::SetTxdFindCallback(); #endif LoadingScreen("Loading the Game", "Loading particles", nil); int particleTxdSlot = CTxdStore::AddTxdSlot("particle"); CTxdStore::LoadTxd(particleTxdSlot, "MODELS/PARTICLE.TXD"); CTxdStore::AddRef(particleTxdSlot); CTxdStore::SetCurrentTxd(gameTxdSlot); LoadingScreen("Loading the Game", "Setup game variables", nil); POP_MEMID(); #ifdef GTA_PS2 CDma::SyncChannel(0, true); #endif CGameLogic::InitAtStartOfGame(); CReferences::Init(); TheCamera.Init(); TheCamera.SetRwCamera(Scene.camera); CDebug::DebugInitTextBuffer(); ThePaths.Init(); ThePaths.AllocatePathFindInfoMem(4500); CScriptPaths::Init(); CWeather::Init(); CCullZones::Init(); COcclusion::Init(); CCollision::Init(); CSetPieces::Init(); CTheZones::Init(); CUserDisplay::Init(); CMessages::Init(); CMessages::ClearAllMessagesDisplayedByGame(); CRecordDataForGame::Init(); CRestart::Initialise(); PUSH_MEMID(MEMID_WORLD); CWorld::Initialise(); POP_MEMID(); PUSH_MEMID(MEMID_TEXTURES); CParticle::Initialise(); POP_MEMID(); PUSH_MEMID(MEMID_ANIMATION); CAnimManager::Initialise(); CCutsceneMgr::Initialise(); POP_MEMID(); PUSH_MEMID(MEMID_CARS); CCarCtrl::Init(); POP_MEMID(); PUSH_MEMID(MEMID_DEF_MODELS); InitModelIndices(); CModelInfo::Initialise(); CPickups::Init(); CTheCarGenerators::Init(); CdStreamAddImage("MODELS\\GTA3.IMG"); CFileLoader::LoadLevel("DATA\\DEFAULT.DAT"); CFileLoader::LoadLevel(datFile); LoadingScreen("Loading the Game", "Add Particles", nil); CWorld::AddParticles(); CVehicleModelInfo::LoadVehicleColours(); CVehicleModelInfo::LoadEnvironmentMaps(); CTheZones::PostZoneCreation(); POP_MEMID(); LoadingScreen("Loading the Game", "Setup paths", nil); ThePaths.PreparePathData(); for (int i = 0; i < NUMPLAYERS; i++) CWorld::Players[i].Clear(); CWorld::Players[0].LoadPlayerSkin(); TestModelIndices(); LoadingScreen("Loading the Game", "Setup water", nil); CWaterLevel::Initialise("DATA\\WATER.DAT"); TheConsole.Init(); CDraw::SetFOV(120.0f); CDraw::ms_fLODDistance = 500.0f; LoadingScreen("Loading the Game", "Setup streaming", nil); CStreaming::LoadInitialVehicles(); CStreaming::LoadInitialPeds(); CStreaming::RequestBigBuildings(LEVEL_GENERIC); CStreaming::LoadAllRequestedModels(false); CStreaming::RemoveIslandsNotUsed(currLevel); printf("Streaming uses %zuK of its memory", CStreaming::ms_memoryUsed / 1024); // original modifier was %d LoadingScreen("Loading the Game", "Load animations", GetRandomSplashScreen()); PUSH_MEMID(MEMID_ANIMATION); CAnimManager::LoadAnimFiles(); POP_MEMID(); CStreaming::LoadInitialWeapons(); CStreaming::LoadAllRequestedModels(0); CPed::Initialise(); CRouteNode::Initialise(); CEventList::Initialise(); #ifdef SCREEN_DROPLETS ScreenDroplets::Initialise(); #endif LoadingScreen("Loading the Game", "Find big buildings", nil); CRenderer::Init(); LoadingScreen("Loading the Game", "Setup game variables", nil); CRadar::Initialise(); CRadar::LoadTextures(); CWeapon::InitialiseWeapons(); LoadingScreen("Loading the Game", "Setup traffic lights", nil); CTrafficLights::ScanForLightsOnMap(); CRoadBlocks::Init(); LoadingScreen("Loading the Game", "Setup game variables", nil); CPopulation::Initialise(); CWorld::PlayerInFocus = 0; CCoronas::Init(); CShadows::Init(); CWeaponEffects::Init(); CSkidmarks::Init(); CAntennas::Init(); CGlass::Init(); gPhoneInfo.Initialise(); #ifdef GTA_SCENE_EDIT CSceneEdit::Initialise(); #endif LoadingScreen("Loading the Game", "Load scripts", nil); PUSH_MEMID(MEMID_SCRIPT); CTheScripts::Init(); CGangs::Initialise(); POP_MEMID(); LoadingScreen("Loading the Game", "Setup game variables", nil); CClock::Initialise(1000); CHeli::InitHelis(); CCranes::InitCranes(); CMovingThings::Init(); CDarkel::Init(); CStats::Init(); CPacManPickups::Init(); CRubbish::Init(); CClouds::Init(); CSpecialFX::Init(); CRopes::Init(); CWaterCannons::Init(); CBridge::Init(); CGarages::Init(); LoadingScreen("Loading the Game", "Position dynamic objects", nil); LoadingScreen("Loading the Game", "Initialise vehicle paths", nil); CTrain::InitTrains(); CPlane::InitPlanes(); CCredits::Init(); CRecordDataForChase::Init(); CReplay::Init(); LoadingScreen("Loading the Game", "Start script", nil); #ifdef PS2_MENU if ( !TheMemoryCard.m_bWantToLoad ) #endif { CTheScripts::StartTestScript(); CTheScripts::Process(); TheCamera.Process(); } LoadingScreen("Loading the Game", "Load scene", nil); CCollision::ms_collisionInMemory = currLevel; for (int i = 0; i < MAX_PADS; i++) CPad::GetPad(i)->Clear(true); #ifdef USE_TEXTURE_POOL _TexturePoolsUnknown(true); #endif #ifndef MASTER PlayerCoords = FindPlayerCoors(); VarConsole.Add("X PLAYER COORD", &PlayerCoords.x, 10.0f, -10000.0f, 10000.0f, true); VarConsole.Add("Y PLAYER COORD", &PlayerCoords.y, 10.0f, -10000.0f, 10000.0f, true); VarConsole.Add("Z PLAYER COORD", &PlayerCoords.z, 10.0f, -10000.0f, 10000.0f, true); VarConsole.Add("UPDATE PLAYER COORD", &VarUpdatePlayerCoords, true); #endif DMAudio.SetStartingTrackPositions(true); DMAudio.ChangeMusicMode(MUSICMODE_GAME); return true; } bool CGame::ShutDown(void) { #ifdef USE_TEXTURE_POOL _TexturePoolsUnknown(false); #endif CReplay::FinishPlayback(); CReplay::EmptyReplayBuffer(); CPlane::Shutdown(); CTrain::Shutdown(); CScriptPaths::Shutdown(); CWaterCreatures::RemoveAll(); CSpecialFX::Shutdown(); CGarages::Shutdown(); CMovingThings::Shutdown(); gPhoneInfo.Shutdown(); CWeapon::ShutdownWeapons(); CPedType::Shutdown(); for (int32 i = 0; i < NUMPLAYERS; i++) { if ( CWorld::Players[i].m_pPed ) { CWorld::Remove(CWorld::Players[i].m_pPed); delete CWorld::Players[i].m_pPed; CWorld::Players[i].m_pPed = nil; } CWorld::Players[i].Clear(); } CRenderer::Shutdown(); CWorld::ShutDown(); DMAudio.DestroyAllGameCreatedEntities(); CModelInfo::ShutDown(); CAnimManager::Shutdown(); CCutsceneMgr::Shutdown(); CVehicleModelInfo::DeleteVehicleColourTextures(); CVehicleModelInfo::ShutdownEnvironmentMaps(); CRadar::Shutdown(); CStreaming::Shutdown(); CTxdStore::GameShutdown(); CCollision::Shutdown(); CWaterLevel::Shutdown(); CRubbish::Shutdown(); CClouds::Shutdown(); CShadows::Shutdown(); CCoronas::Shutdown(); CSkidmarks::Shutdown(); CWeaponEffects::Shutdown(); CParticle::Shutdown(); CPools::ShutDown(); CHud::ReInitialise(); CTxdStore::RemoveTxdSlot(gameTxdSlot); CMBlur::MotionBlurClose(); CdStreamRemoveImages(); #ifdef USE_TEXTURE_POOL _TexturePoolsFinalShutdown(); #endif return true; } void CGame::ReInitGameObjectVariables(void) { CGameLogic::InitAtStartOfGame(); #ifdef PS2_MENU if ( !TheMemoryCard.m_bWantToLoad ) #endif { TheCamera.Init(); TheCamera.SetRwCamera(Scene.camera); } CDebug::DebugInitTextBuffer(); CWeather::Init(); CUserDisplay::Init(); CMessages::Init(); CRestart::Initialise(); CWorld::bDoingCarCollisions = false; CHud::ReInitialise(); CRadar::Initialise(); CCarCtrl::ReInit(); CTimeCycle::Initialise(); CDraw::SetFOV(120.0f); CDraw::ms_fLODDistance = 500.0f; CStreaming::RequestBigBuildings(LEVEL_GENERIC); CStreaming::RemoveIslandsNotUsed(LEVEL_BEACH); CStreaming::RemoveIslandsNotUsed(LEVEL_MAINLAND); CStreaming::LoadAllRequestedModels(false); currArea = AREA_MAIN_MAP; CPed::Initialise(); CEventList::Initialise(); #ifdef SCREEN_DROPLETS ScreenDroplets::Initialise(); #endif CWeapon::InitialiseWeapons(); CPopulation::Initialise(); for (int i = 0; i < NUMPLAYERS; i++) CWorld::Players[i].Clear(); CWorld::PlayerInFocus = 0; CAntennas::Init(); CGlass::Init(); gPhoneInfo.Initialise(); PUSH_MEMID(MEMID_SCRIPT); CTheScripts::Init(); CGangs::Initialise(); POP_MEMID(); CTimer::Initialise(); CClock::Initialise(1000); CTheCarGenerators::Init(); CHeli::InitHelis(); CMovingThings::Init(); CDarkel::Init(); CStats::Init(); CPickups::Init(); CPacManPickups::Init(); CGarages::Init(); CSpecialFX::Init(); CRopes::Init(); CWaterCannons::Init(); CScriptPaths::Init(); CParticle::ReloadConfig(); #ifdef PS2_MENU if ( !TheMemoryCard.m_bWantToLoad ) #else if ( !FrontEndMenuManager.m_bWantToLoad ) #endif { CCranes::InitCranes(); CTheScripts::StartTestScript(); CTheScripts::Process(); TheCamera.Process(); CTrain::InitTrains(); CPlane::InitPlanes(); } for (int32 i = 0; i < MAX_PADS; i++) CPad::GetPad(i)->Clear(true); } void CGame::ReloadIPLs(void) { // Empty and unused } void CGame::ShutDownForRestart(void) { #ifdef USE_TEXTURE_POOL _TexturePoolsUnknown(false); #endif CReplay::FinishPlayback(); CReplay::EmptyReplayBuffer(); DMAudio.DestroyAllGameCreatedEntities(); CMovingThings::Shutdown(); for (int i = 0; i < NUMPLAYERS; i++) CWorld::Players[i].Clear(); CGarages::SetAllDoorsBackToOriginalHeight(); CTheScripts::UndoBuildingSwaps(); CTheScripts::UndoEntityInvisibilitySettings(); CWorld::ClearForRestart(); CGameLogic::ClearShortCut(); CTimer::Shutdown(); CStreaming::ReInit(); CRadar::RemoveRadarSections(); FrontEndMenuManager.UnloadTextures(); CParticleObject::RemoveAllExpireableParticleObjects(); CWaterCreatures::RemoveAll(); CSetPieces::Init(); CPedType::Shutdown(); CSpecialFX::Shutdown(); } void CGame::InitialiseWhenRestarting(void) { CRect rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); CRGBA color(255, 255, 255, 255); CTimer::Initialise(); CSprite2d::SetRecipNearClip(); if (b_FoundRecentSavedGameWantToLoad || FrontEndMenuManager.m_bWantToLoad) { LoadSplash("splash1"); #ifndef XBOX_MESSAGE_SCREEN if (FrontEndMenuManager.m_bWantToLoad) FrontEndMenuManager.MessageScreen("FELD_WR", true); #endif } b_FoundRecentSavedGameWantToLoad = false; TheCamera.Init(); if ( FrontEndMenuManager.m_bWantToLoad == true ) { #ifdef XBOX_MESSAGE_SCREEN FrontEndMenuManager.SetDialogTimer(1000); DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 0); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); FrontEndMenuManager.DrawOverlays(); DoRWStuffEndOfFrame(); #endif RestoreForStartLoad(); } ReInitGameObjectVariables(); if ( FrontEndMenuManager.m_bWantToLoad == true ) { FrontEndMenuManager.m_bWantToLoad = false; InitRadioStationPositionList(); if ( GenericLoad() == true ) { DMAudio.ResetTimers(CTimer::GetTimeInMilliseconds()); CTrain::InitTrains(); CPlane::InitPlanes(); } else { for ( int32 i = 0; i < 50; i++ ) { HandleExit(); FrontEndMenuManager.MessageScreen("FED_LFL", true); // Loading save game has failed. The game will restart now. } TheCamera.SetFadeColour(0, 0, 0); ShutDownForRestart(); CTimer::Stop(); CTimer::Initialise(); FrontEndMenuManager.m_bWantToLoad = false; ReInitGameObjectVariables(); currLevel = LEVEL_GENERIC; CCollision::SortOutCollisionAfterLoad(); } #ifdef XBOX_MESSAGE_SCREEN FrontEndMenuManager.ProcessDialogTimer(); #endif } CTimer::Update(); DMAudio.ChangeMusicMode(MUSICMODE_GAME); #ifdef USE_TEXTURE_POOL _TexturePoolsUnknown(true); #endif } void CGame::Process(void) { CPad::UpdatePads(); #ifdef USE_CUSTOM_ALLOCATOR ProcessTidyUpMemory(); #endif #ifdef DEBUGMENU DebugMenuProcess(); #endif CCutsceneMgr::Update(); if (!CCutsceneMgr::IsCutsceneProcessing() && !CTimer::GetIsCodePaused()) FrontEndMenuManager.Process(); CTheZones::Update(); #ifdef SECUROM if (CTimer::GetTimeInMilliseconds() >= (35 * 60 * 1000) && gameProcessPirateCheck == 0){ // if game not pirated // gameProcessPirateCheck = 1; // else gameProcessPirateCheck = 2; } #endif uint32 startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); CStreaming::Update(); uint32 processTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond() - startTime; CWindModifiers::Number = 0; if (!CTimer::GetIsPaused()) { #ifndef MASTER if (VarUpdatePlayerCoords) { FindPlayerPed()->Teleport(PlayerCoords); VarUpdatePlayerCoords = false; } #endif CSprite2d::SetRecipNearClip(); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); CRecordDataForGame::SaveOrRetrieveDataForThisFrame(); CRecordDataForChase::SaveOrRetrieveDataForThisFrame(); CPad::DoCheats(); CClock::Update(); CWeather::Update(); PUSH_MEMID(MEMID_SCRIPT); CTheScripts::Process(); POP_MEMID(); CCollision::Update(); CScriptPaths::Update(); CTrain::UpdateTrains(); CPlane::UpdatePlanes(); CHeli::UpdateHelis(); CDarkel::Update(); CSkidmarks::Update(); CAntennas::Update(); CGlass::Update(); #ifdef GTA_SCENE_EDIT CSceneEdit::Update(); #endif CSetPieces::Update(); CEventList::Update(); CParticle::Update(); gFireManager.Update(); if (processTime >= 2) { CPopulation::Update(false); } else { uint32 startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); CPopulation::Update(true); processTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond() - startTime; } CWeapon::UpdateWeapons(); if (!CCutsceneMgr::IsRunning()) CTheCarGenerators::Process(); if (!CReplay::IsPlayingBack()) CCranes::UpdateCranes(); CClouds::Update(); CMovingThings::Update(); CWaterCannons::Update(); CUserDisplay::Process(); CReplay::Update(); PUSH_MEMID(MEMID_WORLD); CWorld::Process(); POP_MEMID(); gAccidentManager.Update(); CPacManPickups::Update(); CPickups::Update(); CGarages::Update(); CRubbish::Update(); CSpecialFX::Update(); CRopes::Update(); CTimeCycle::Update(); if (CReplay::ShouldStandardCameraBeProcessed()) TheCamera.Process(); CCullZones::Update(); if (!CReplay::IsPlayingBack()) CGameLogic::Update(); CBridge::Update(); CCoronas::DoSunAndMoon(); CCoronas::Update(); CShadows::UpdateStaticShadows(); CShadows::UpdatePermanentShadows(); gPhoneInfo.Update(); if (!CReplay::IsPlayingBack()) { PUSH_MEMID(MEMID_CARS); if (processTime < 2) CCarCtrl::GenerateRandomCars(); CRoadBlocks::GenerateRoadBlocks(); CCarCtrl::RemoveDistantCars(); CCarCtrl::RemoveCarsIfThePoolGetsFull(); POP_MEMID(); } } #ifdef GTA_PS2 CMemCheck::DoTest(); #endif } #ifdef USE_CUSTOM_ALLOCATOR // TODO(MIAMI) int32 gNumMemMoved; bool MoveMem(void** ptr) { if (*ptr) { gNumMemMoved++; void* newPtr = gMainHeap.MoveMemory(*ptr); if (*ptr != newPtr) { *ptr = newPtr; return true; } } return false; } // Some convenience structs struct SkyDataPrefix { uint32 pktSize1; uint32 data; // pointer to data as read from TXD uint32 pktSize2; uint32 unused; }; struct DMAGIFUpload { uint32 tag1_qwc, tag1_addr; // dmaref uint32 nop1, vif_direct1; uint32 giftag[4]; uint32 gs_bitbltbuf[4]; uint32 tag2_qwc, tag2_addr; // dmaref uint32 nop2, vif_direct2; }; // This is very scary. it depends on the exact memory layout of the DMA chains and whatnot RwTexture* MoveTextureMemoryCB(RwTexture* texture, void* pData) { #ifdef GTA_PS2 bool* pRet = (bool*)pData; RwRaster* raster = RwTextureGetRaster(texture); _SkyRasterExt* rasterExt = RASTEREXTFROMRASTER(raster); if (raster->originalPixels == nil || // the raw data raster->cpPixels == raster->originalPixels || // old format, can't handle it rasterExt->dmaRefCount != 0 && rasterExt->dmaClrCount != 0) return texture; // this is the allocated pointer we will move SkyDataPrefix* prefix = (SkyDataPrefix*)raster->originalPixels; DMAGIFUpload* uploads = (DMAGIFUpload*)(prefix + 1); // We have 4qw for each upload, // i.e. for each buffer width of mip levels, // and the palette if there is one. // NB: this code does NOT support mipmaps! // so we assume two uploads (pixels and palette) // // each upload looks like this: // (DMAcnt; NOP; VIF DIRECT(2)) // giftag (1, A+D) // GS_BITBLTBUF // (DMAref->pixel data; NOP; VIF DIRECT(5)) // the DMArefs are what we have to adjust uintptr dataDiff, upload1Diff, upload2Diff, pixelDiff, paletteDiff; dataDiff = prefix->data - (uintptr)raster->originalPixels; upload1Diff = uploads[0].tag2_addr - (uintptr)raster->originalPixels; if (raster->palette) upload2Diff = uploads[1].tag2_addr - (uintptr)raster->originalPixels; pixelDiff = (uintptr)raster->cpPixels - (uintptr)raster->originalPixels; if (raster->palette) paletteDiff = (uintptr)raster->palette - (uintptr)raster->originalPixels; uint8* newptr = (uint8*)gMainHeap.MoveMemory(raster->originalPixels); if (newptr != raster->originalPixels) { // adjust everything prefix->data = (uintptr)newptr + dataDiff; uploads[0].tag2_addr = (uintptr)newptr + upload1Diff; if (raster->palette) uploads[1].tag2_addr = (uintptr)newptr + upload2Diff; raster->originalPixels = newptr; raster->cpPixels = newptr + pixelDiff; if (raster->palette) raster->palette = newptr + paletteDiff; if (pRet) { *pRet = true; return nil; } } #else // nothing to do here really, everything should be in videomemory #endif return texture; } bool MoveAtomicMemory(RpAtomic* atomic, bool onlyOne) { RpGeometry* geo = RpAtomicGetGeometry(atomic); #if THIS_IS_COMPATIBLE_WITH_GTA3_RW31 if (MoveMem((void**)&geo->triangles) && onlyOne) return true; if (MoveMem((void**)&geo->matList.materials) && onlyOne) return true; if (MoveMem((void**)&geo->preLitLum) && onlyOne) return true; if (MoveMem((void**)&geo->texCoords[0]) && onlyOne) return true; if (MoveMem((void**)&geo->texCoords[1]) && onlyOne) return true; // verts and normals of morph target are allocated together int vertDiff; if (geo->morphTarget->normals) vertDiff = geo->morphTarget->normals - geo->morphTarget->verts; if (MoveMem((void**)&geo->morphTarget->verts)) { if (geo->morphTarget->normals) geo->morphTarget->normals = geo->morphTarget->verts + vertDiff; if (onlyOne) return true; } RpMeshHeader* oldmesh = geo->mesh; if (MoveMem((void**)&geo->mesh)) { // index pointers are allocated together with meshes, // have to relocate those too RpMesh* mesh = (RpMesh*)(geo->mesh + 1); uintptr reloc = (uintptr)geo->mesh - (uintptr)oldmesh; for (int i = 0; i < geo->mesh->numMeshes; i++) mesh[i].indices = (RxVertexIndex*)((uintptr)mesh[i].indices + reloc); if (onlyOne) return true; } #else // we could do something in librw here #endif return false; } bool MoveColModelMemory(CColModel& colModel, bool onlyOne) { #if GTA_VERSION >= GTA3_PS2_160 // hm...should probably only do this if ownsCollisionVolumes // but it doesn't exist on PS2... if (!colModel.ownsCollisionVolumes) return false; #endif if (MoveMem((void**)&colModel.spheres) && onlyOne) return true; if (MoveMem((void**)&colModel.lines) && onlyOne) return true; if (MoveMem((void**)&colModel.boxes) && onlyOne) return true; if (MoveMem((void**)&colModel.vertices) && onlyOne) return true; if (MoveMem((void**)&colModel.triangles) && onlyOne) return true; if (MoveMem((void**)&colModel.trianglePlanes) && onlyOne) return true; return false; } RpAtomic* MoveAtomicMemoryCB(RpAtomic* atomic, void* pData) { bool* pRet = (bool*)pData; if (pRet == nil) MoveAtomicMemory(atomic, false); else if (MoveAtomicMemory(atomic, true)) { *pRet = true; return nil; } return atomic; } bool TidyUpModelInfo(CBaseModelInfo* modelInfo, bool onlyone) { if (modelInfo->GetColModel() && modelInfo->DoesOwnColModel()) if (MoveColModelMemory(*modelInfo->GetColModel(), onlyone)) return true; RwObject* rwobj = modelInfo->GetRwObject(); if (RwObjectGetType(rwobj) == rpATOMIC) if (MoveAtomicMemory((RpAtomic*)rwobj, onlyone)) return true; if (RwObjectGetType(rwobj) == rpCLUMP) { bool ret = false; if (onlyone) RpClumpForAllAtomics((RpClump*)rwobj, MoveAtomicMemoryCB, &ret); else RpClumpForAllAtomics((RpClump*)rwobj, MoveAtomicMemoryCB, nil); if (ret) return true; } if (modelInfo->GetModelType() == MITYPE_PED && ((CPedModelInfo*)modelInfo)->m_hitColModel) if (MoveColModelMemory(*((CPedModelInfo*)modelInfo)->m_hitColModel, onlyone)) return true; return false; } #endif void CGame::DrasticTidyUpMemory(bool flushDraw) { #ifdef USE_CUSTOM_ALLOCATOR bool removedCol = false; TidyUpMemory(true, flushDraw); if (gMainHeap.GetLargestFreeBlock() < 200000 && !playingIntro) { CStreaming::RemoveIslandsNotUsed(LEVEL_INDUSTRIAL); CStreaming::RemoveIslandsNotUsed(LEVEL_COMMERCIAL); CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN); TidyUpMemory(true, flushDraw); } if (gMainHeap.GetLargestFreeBlock() < 200000 && !playingIntro) { CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_GENERIC); TidyUpMemory(true, flushDraw); removedCol = true; } if (gMainHeap.GetLargestFreeBlock() < 200000 && !playingIntro) { CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL); CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL); CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN); TidyUpMemory(true, flushDraw); } if (removedCol) { // different on PS2 CFileLoader::LoadCollisionFromDatFile(CCollision::ms_collisionInMemory); } if (!playingIntro) CStreaming::RequestBigBuildings(currLevel); CStreaming::LoadAllRequestedModels(true); #endif } void CGame::TidyUpMemory(bool moveTextures, bool flushDraw) { #ifdef USE_CUSTOM_ALLOCATOR printf("Largest free block before tidy %d\n", gMainHeap.GetLargestFreeBlock()); if (moveTextures) { if (flushDraw) { #ifdef GTA_PS2 for (int i = 0; i < sweMaxFlips + 1; i++) { #else for (int i = 0; i < 5; i++) { // probably more than needed #endif RwCameraBeginUpdate(Scene.camera); RwCameraEndUpdate(Scene.camera); RwCameraShowRaster(Scene.camera, nil, 0); } } int fontSlot = CTxdStore::FindTxdSlot("fonts"); for (int i = 0; i < TXDSTORESIZE; i++) { if (i == fontSlot || CTxdStore::GetSlot(i) == nil) continue; RwTexDictionary* txd = CTxdStore::GetSlot(i)->texDict; if (txd) RwTexDictionaryForAllTextures(txd, MoveTextureMemoryCB, nil); } } // animations for (int i = 0; i < NUMANIMATIONS; i++) { CAnimBlendHierarchy* anim = CAnimManager::GetAnimation(i); if (anim == nil) continue; // cannot happen anim->MoveMemory(); } // model info for (int i = 0; i < MODELINFOSIZE; i++) { CBaseModelInfo* mi = CModelInfo::GetModelInfo(i); if (mi == nil) continue; TidyUpModelInfo(mi, false); } printf("Largest free block after tidy %d\n", gMainHeap.GetLargestFreeBlock()); #endif } void CGame::ProcessTidyUpMemory(void) { #ifdef USE_CUSTOM_ALLOCATOR static int32 modelIndex = 0; static int32 animIndex = 0; static int32 txdIndex = 0; bool txdReturn = false; RwTexDictionary* txd = nil; gNumMemMoved = 0; // model infos for (int numCleanedUp = 0; numCleanedUp < 10; numCleanedUp++) { CBaseModelInfo* mi; do { mi = CModelInfo::GetModelInfo(modelIndex); modelIndex++; if (modelIndex >= MODELINFOSIZE) modelIndex = 0; } while (mi == nil); if (TidyUpModelInfo(mi, true)) return; } // tex dicts for (int numCleanedUp = 0; numCleanedUp < 3; numCleanedUp++) { if (gNumMemMoved > 80) break; do { #ifdef FIX_BUGS txd = nil; #endif if (CTxdStore::GetSlot(txdIndex)) txd = CTxdStore::GetSlot(txdIndex)->texDict; txdIndex++; if (txdIndex >= TXDSTORESIZE) txdIndex = 0; } while (txd == nil); RwTexDictionaryForAllTextures(txd, MoveTextureMemoryCB, &txdReturn); if (txdReturn) return; } // animations CAnimBlendHierarchy* anim; do { anim = CAnimManager::GetAnimation(animIndex); animIndex++; if (animIndex >= NUMANIMATIONS) animIndex = 0; } while (anim == nil); // always != nil anim->MoveMemory(true); #endif } void CGame::InitAfterFocusLoss() { FrontEndMenuManager.m_nPrefsAudio3DProviderIndex = FrontEndMenuManager.m_lastWorking3DAudioProvider; DMAudio.SetCurrent3DProvider(FrontEndMenuManager.m_lastWorking3DAudioProvider); if (!FrontEndMenuManager.m_bGameNotLoaded && !FrontEndMenuManager.m_bMenuActive) FrontEndMenuManager.m_bStartUpFrontEndRequested = true; } bool CGame::CanSeeWaterFromCurrArea(void) { return currArea == AREA_MAIN_MAP || currArea == AREA_MANSION || currArea == AREA_HOTEL; } bool CGame::CanSeeOutSideFromCurrArea(void) { return currArea == AREA_MAIN_MAP || currArea == AREA_MALL || currArea == AREA_MANSION || currArea == AREA_HOTEL; } ================================================ FILE: src/core/Game.h ================================================ #pragma once enum eLevelName { LEVEL_IGNORE = -1, // beware, this is only used in CPhysical's m_nZoneLevel LEVEL_GENERIC = 0, LEVEL_BEACH, LEVEL_MAINLAND, NUM_LEVELS }; enum eAreaName { AREA_MAIN_MAP, AREA_HOTEL, AREA_MANSION, AREA_BANK, AREA_MALL, AREA_STRIP_CLUB, AREA_LAWYERS, AREA_COFFEE_SHOP, AREA_CONCERT_HALL, AREA_STUDIO, AREA_RIFLE_RANGE, AREA_BIKER_BAR, AREA_POLICE_STATION, AREA_EVERYWHERE, AREA_DIRT, AREA_BLOOD, AREA_OVALRING, AREA_MALIBU_CLUB, AREA_PRINT_WORKS }; class CGame { public: static eLevelName currLevel; static int32 currArea; static bool bDemoMode; static bool nastyGame; static bool frenchGame; static bool germanGame; #ifdef MORE_LANGUAGES static bool russianGame; static bool japaneseGame; #endif static bool noProstitutes; static bool playingIntro; static char aDatFile[32]; #ifndef MASTER static CVector PlayerCoords; static bool8 VarUpdatePlayerCoords; #endif static bool InitialiseOnceBeforeRW(void); static bool InitialiseRenderWare(void); static void ShutdownRenderWare(void); static bool InitialiseOnceAfterRW(void); static void FinalShutdown(void); static bool Initialise(const char *datFile); static bool ShutDown(void); static void ReInitGameObjectVariables(void); static void ReloadIPLs(void); static void ShutDownForRestart(void); static void InitialiseWhenRestarting(void); static void Process(void); static void InitAfterFocusLoss(void); static bool IsInInterior(void) { return currArea != AREA_MAIN_MAP; } static bool CanSeeWaterFromCurrArea(void); static bool CanSeeOutSideFromCurrArea(void); // NB: these do something on PS2 static void TidyUpMemory(bool, bool); static void DrasticTidyUpMemory(bool); static void ProcessTidyUpMemory(void); }; inline bool IsAreaVisible(int area) { return area == CGame::currArea || area == AREA_EVERYWHERE; } ================================================ FILE: src/core/General.h ================================================ #pragma once #include class CGeneral { public: static float GetATanOfXY(float x, float y){ if(x == 0.0f && y == 0.0f) return 0.0f; float xabs = Abs(x); float yabs = Abs(y); if(xabs < yabs){ if(y > 0.0f){ if(x > 0.0f) return 0.5f*PI - Atan2(x / y, 1.0f); else return 0.5f*PI + Atan2(-x / y, 1.0f); }else{ if(x > 0.0f) return 1.5f*PI + Atan2(x / -y, 1.0f); else return 1.5f*PI - Atan2(-x / -y, 1.0f); } }else{ if(y > 0.0f){ if(x > 0.0f) return Atan2(y / x, 1.0f); else return PI - Atan2(y / -x, 1.0f); }else{ if(x > 0.0f) return 2.0f*PI - Atan2(-y / x, 1.0f); else return PI + Atan2(-y / -x, 1.0f); } } } static float LimitAngle(float angle) { float result = angle; while (result >= 180.0f) { result -= 2 * 180.0f; } while (result < -180.0f) { result += 2 * 180.0f; } return result; } static float LimitRadianAngle(float angle) { float result = clamp(angle, -25.0f, 25.0f); while (result >= PI) { result -= 2 * PI; } while (result < -PI) { result += 2 * PI; } return result; } // Returns an angle such that x2/y2 looks at x1/y1 with its forward vector if rotated by that angle static float GetRadianAngleBetweenPoints(float x1, float y1, float x2, float y2) { float x = x2 - x1; float y = y2 - y1; if (y == 0.0f) y = 0.0001f; if (x > 0.0f) { if (y > 0.0f) return PI - Atan2(x / y, 1.0f); else return -Atan2(x / y, 1.0f); } else { if (y > 0.0f) return -(PI + Atan2(x / y, 1.0f)); else return -Atan2(x / y, 1.0f); } } static float GetAngleBetweenPoints(float x1, float y1, float x2, float y2) { return RADTODEG(GetRadianAngleBetweenPoints(x1, y1, x2, y2)); } // should return direction in 0-8 range. fits perfectly to peds' path directions. static int GetNodeHeadingFromVector(float x, float y) { float angle = CGeneral::GetRadianAngleBetweenPoints(x, y, 0.0f, 0.0f); if (angle < 0.0f) angle += TWOPI; angle = DEGTORAD(22.5f) + TWOPI - angle; if (angle >= TWOPI) angle -= TWOPI; return (int)Floor(angle / DEGTORAD(45.0f)); } // Unlike usual string comparison functions, these don't care about greater or lesser static bool faststrcmp(const char *str1, const char *str2) { for (; *str1; str1++, str2++) { if (*str1 != *str2) return true; } return *str2 != '\0'; } static bool faststrncmp(const char *str1, const char *str2, uint32 count) { for(uint32 i = 0; *str1 && i < count; str1++, str2++, i++) { if (*str1 != *str2) return true; } return false; } static bool faststricmp(const char *str1, const char *str2) { for (; *str1; str1++, str2++) { #ifndef ASCII_STRCMP if (toupper(*str1) != toupper(*str2)) #else if (__ascii_toupper(*str1) != __ascii_toupper(*str2)) #endif return true; } return *str2 != '\0'; } static bool SolveQuadratic(float a, float b, float c, float &root1, float &root2) { float discriminant = b * b - 4.f * a * c; if (discriminant < 0.f) return false; float discriminantSqrt = Sqrt(discriminant); root2 = (-b + discriminantSqrt) / (2.f * a); root1 = (-b - discriminantSqrt) / (2.f * a); return true; } // not too sure about all these... static uint16 GetRandomNumber(void) { return myrand() & MYRAND_MAX; } static bool GetRandomTrueFalse(void) { return GetRandomNumber() < MYRAND_MAX / 2; } // Probably don't want to ever reach high static float GetRandomNumberInRange(float low, float high) { return low + (high - low)*(GetRandomNumber()/float(MYRAND_MAX + 1)); } static int32 GetRandomNumberInRange(int32 low, int32 high) { return low + (high - low)*(GetRandomNumber()/float(MYRAND_MAX + 1)); } static void SetRandomSeed(int32 seed) { mysrand(seed); } }; ================================================ FILE: src/core/IniFile.cpp ================================================ #include "common.h" #include "IniFile.h" #include "CarCtrl.h" #include "FileMgr.h" #include "main.h" #include "Population.h" float CIniFile::PedNumberMultiplier = 0.6f; float CIniFile::CarNumberMultiplier = 0.6f; void CIniFile::LoadIniFile() { CFileMgr::SetDir(""); int f = CFileMgr::OpenFile("gta3.ini", "r"); if (f){ CFileMgr::ReadLine(f, gString, 200); sscanf(gString, "%f", &PedNumberMultiplier); PedNumberMultiplier = Min(3.0f, Max(0.5f, PedNumberMultiplier)); CFileMgr::ReadLine(f, gString, 200); sscanf(gString, "%f", &CarNumberMultiplier); CarNumberMultiplier = Min(3.0f, Max(0.5f, CarNumberMultiplier)); CFileMgr::CloseFile(f); } CPopulation::MaxNumberOfPedsInUse = 25.0f * PedNumberMultiplier; CPopulation::MaxNumberOfPedsInUseInterior = 40.0f * PedNumberMultiplier; CCarCtrl::MaxNumberOfCarsInUse = 12.0f * CarNumberMultiplier; } ================================================ FILE: src/core/IniFile.h ================================================ #pragma once class CIniFile { public: static void LoadIniFile(); static float PedNumberMultiplier; static float CarNumberMultiplier; }; ================================================ FILE: src/core/Lists.cpp ================================================ #include "common.h" #include "Pools.h" #include "Lists.h" void* CPtrNode::operator new(size_t){ CPtrNode *node = CPools::GetPtrNodePool()->New(); assert(node); return node; } void CPtrNode::operator delete(void *p, size_t){ CPools::GetPtrNodePool()->Delete((CPtrNode*)p); } void* CEntryInfoNode::operator new(size_t){ CEntryInfoNode *node = CPools::GetEntryInfoNodePool()->New(); assert(node); return node; } void CEntryInfoNode::operator delete(void *p, size_t){ CPools::GetEntryInfoNodePool()->Delete((CEntryInfoNode*)p); } ================================================ FILE: src/core/Lists.h ================================================ #pragma once class CPtrNode { public: void *item; CPtrNode *prev; CPtrNode *next; void *operator new(size_t); void operator delete(void *p, size_t); }; class CPtrList { public: CPtrNode *first; CPtrList(void) { first = nil; } ~CPtrList(void) { Flush(); } CPtrNode *FindItem(void *item){ CPtrNode *node; for(node = first; node; node = node->next) if(node->item == item) return node; return nil; } CPtrNode *InsertNode(CPtrNode *node){ node->prev = nil; node->next = first; if(first) first->prev = node; first = node; return node; } CPtrNode *InsertItem(void *item){ CPtrNode *node = new CPtrNode; node->item = item; InsertNode(node); return node; } void RemoveNode(CPtrNode *node){ if(node == first) first = node->next; if(node->prev) node->prev->next = node->next; if(node->next) node->next->prev = node->prev; } void DeleteNode(CPtrNode *node){ RemoveNode(node); delete node; } void RemoveItem(void *item){ CPtrNode *node, *next; for(node = first; node; node = next){ next = node->next; if(node->item == item) DeleteNode(node); } } void Flush(void){ CPtrNode *node, *next; for(node = first; node; node = next){ next = node->next; DeleteNode(node); } } }; class CSector; // This records in which sector list a Physical is class CEntryInfoNode { public: CPtrList *list; // list in sector CPtrNode *listnode; // node in list CSector *sector; CEntryInfoNode *prev; CEntryInfoNode *next; void *operator new(size_t); void operator delete(void *p, size_t); }; class CEntryInfoList { public: CEntryInfoNode *first; CEntryInfoList(void) { first = nil; } ~CEntryInfoList(void) { Flush(); } CEntryInfoNode *InsertNode(CEntryInfoNode *node){ node->prev = nil; node->next = first; if(first) first->prev = node; first = node; return node; } CEntryInfoNode *InsertItem(CPtrList *list, CPtrNode *listnode, CSector *sect){ CEntryInfoNode *node = new CEntryInfoNode; node->list = list; node->listnode = listnode; node->sector = sect; InsertNode(node); return node; } void RemoveNode(CEntryInfoNode *node){ if(node == first) first = node->next; if(node->prev) node->prev->next = node->next; if(node->next) node->next->prev = node->prev; } void DeleteNode(CEntryInfoNode *node){ RemoveNode(node); delete node; } void Flush(void){ CEntryInfoNode *node, *next; for(node = first; node; node = next){ next = node->next; DeleteNode(node); } } }; ================================================ FILE: src/core/MenuScreens.cpp ================================================ #include "common.h" #include "Frontend.h" #ifdef PC_MENU // Please don't touch this file, except for bug fixing or ports. // Check MenuScreensCustom.cpp #ifndef CUSTOM_FRONTEND_OPTIONS CMenuScreen aScreens[] = { // MENUPAGE_STATS = 0 { "FEH_STA", MENUPAGE_NONE, 3, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 190, 320, MENUALIGN_RIGHT, }, // MENUPAGE_NEW_GAME = 1 { "FEP_STG", MENUPAGE_NONE, 1, MENUACTION_CHANGEMENU, "FES_NGA", SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD, 320, 155, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FES_LOA", SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FES_DEL", SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT, 0, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_BRIEFS = 2 { "FEH_BRI", MENUPAGE_NONE, 4, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 190, 320, MENUALIGN_RIGHT, }, // MENUPAGE_SOUND_SETTINGS = 3 { "FEH_AUD", MENUPAGE_OPTIONS, 1, MENUACTION_MUSICVOLUME, "FEA_MUS", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 40, 76, MENUALIGN_LEFT, MENUACTION_SFXVOLUME, "FEA_SFX", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, MENUACTION_MP3VOLUMEBOOST, "FEA_MPB", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, MENUACTION_AUDIOHW, "FEA_3DH", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, MENUACTION_SPEAKERCONF, "FEA_SPK", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, MENUACTION_DYNAMICACOUSTIC, "FET_DAM", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, MENUACTION_RADIO, "FEA_RSS", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_LEFT, MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 320, 367, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_DISPLAY_SETTINGS = 4 #ifdef LEGACY_MENU_OPTIONS #define Y_OFFSET 50 #else #define Y_OFFSET 0 #endif { "FEH_DIS", MENUPAGE_OPTIONS, 2, MENUACTION_BRIGHTNESS, "FED_BRI", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 78, MENUALIGN_LEFT, MENUACTION_DRAWDIST, "FEM_LOD", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 103, MENUALIGN_LEFT, #ifdef LEGACY_MENU_OPTIONS MENUACTION_FRAMESYNC, "FEM_VSC", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 128, MENUALIGN_LEFT, #endif MENUACTION_FRAMELIMIT, "FEM_FRM", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 128 + Y_OFFSET/2, MENUALIGN_LEFT, #ifdef LEGACY_MENU_OPTIONS MENUACTION_TRAILS, "FED_TRA", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 178, MENUALIGN_LEFT, #endif MENUACTION_SUBTITLES, "FED_SUB", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 153 + Y_OFFSET, MENUALIGN_LEFT, MENUACTION_WIDESCREEN, "FED_WIS", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 178 + Y_OFFSET, MENUALIGN_LEFT, MENUACTION_LEGENDS, "MAP_LEG", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 202 + Y_OFFSET, MENUALIGN_LEFT, MENUACTION_RADARMODE, "FED_RDR", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 228 + Y_OFFSET, MENUALIGN_LEFT, MENUACTION_HUD, "FED_HUD", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 253 + Y_OFFSET, MENUALIGN_LEFT, MENUACTION_SCREENRES, "FED_RES", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 40, 278 + Y_OFFSET, MENUALIGN_LEFT, MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 320, 303 + Y_OFFSET, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 320, 328 + Y_OFFSET, MENUALIGN_CENTER, }, #undef Y_OFFSET // MENUPAGE_LANGUAGE_SETTINGS = 5 { "FEH_LAN", MENUPAGE_OPTIONS, 3, MENUACTION_LANG_ENG, "FEL_ENG", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 320, 132, MENUALIGN_CENTER, MENUACTION_LANG_FRE, "FEL_FRE", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, MENUACTION_LANG_GER, "FEL_GER", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, MENUACTION_LANG_ITA, "FEL_ITA", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, MENUACTION_LANG_SPA, "FEL_SPA", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_MAP = 6 { "FEH_MAP", MENUPAGE_NONE, 2, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 70, 380, MENUALIGN_CENTER, }, // MENUPAGE_NEW_GAME_RELOAD = 7 { "FES_NGA", MENUPAGE_NEW_GAME, 0, MENUACTION_LABEL, "FESZ_QR", SAVESLOT_NONE, 0, 0, 0, 0, MENUACTION_NO, "FEM_NO", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 320, 200, MENUALIGN_CENTER, MENUACTION_NEWGAME, "FEM_YES", SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_CHOOSE_LOAD_SLOT = 8 { "FET_LG", MENUPAGE_NEW_GAME, 1, MENUACTION_CHECKSAVE, "FEM_SL1", SAVESLOT_1, 0, 40, 90, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL2", SAVESLOT_2, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL3", SAVESLOT_3, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL4", SAVESLOT_4, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL5", SAVESLOT_5, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL6", SAVESLOT_6, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL7", SAVESLOT_7, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL8", SAVESLOT_8, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 320, 345, MENUALIGN_CENTER, }, // MENUPAGE_CHOOSE_DELETE_SLOT = 9 { "FES_DEL", MENUPAGE_NEW_GAME, 2, MENUACTION_CHECKSAVE, "FEM_SL1", SAVESLOT_1, 0, 40, 90, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL2", SAVESLOT_2, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL3", SAVESLOT_3, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL4", SAVESLOT_4, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL5", SAVESLOT_5, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL6", SAVESLOT_6, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL7", SAVESLOT_7, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL8", SAVESLOT_8, 0, 0, 0, MENUALIGN_LEFT, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 320, 345, MENUALIGN_CENTER, }, // MENUPAGE_LOAD_SLOT_CONFIRM = 10 { "FET_LG", MENUPAGE_CHOOSE_LOAD_SLOT, 0, MENUACTION_LABEL, "FESZ_QL", SAVESLOT_NONE, 0, 0, 0, 0, MENUACTION_NO, "FEM_NO", SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT, 320, 200, MENUALIGN_CENTER, MENUACTION_YES, "FEM_YES", SAVESLOT_NONE, MENUPAGE_LOADING_IN_PROGRESS, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_DELETE_SLOT_CONFIRM = 11 { "FES_DEL", MENUPAGE_CHOOSE_DELETE_SLOT, 0, MENUACTION_LABEL, "FESZ_QD", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, MENUACTION_NO, "FEM_NO", SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT, 320, 200, MENUALIGN_CENTER, MENUACTION_YES, "FEM_YES", SAVESLOT_NONE, MENUPAGE_DELETING_IN_PROGRESS, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_LOADING_IN_PROGRESS = 12 { "FET_LG", MENUPAGE_CHOOSE_LOAD_SLOT, 0, }, // MENUPAGE_DELETING_IN_PROGRESS = 13 { "FES_DEL", MENUPAGE_CHOOSE_DELETE_SLOT, 0, }, // MENUPAGE_DELETE_SUCCESSFUL = 14 { "FES_DEL", MENUPAGE_NEW_GAME, 0, MENUACTION_LABEL, "FES_DSC", SAVESLOT_NONE, 0, 0, 0, 0, MENUACTION_CHANGEMENU, "FEM_OK", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_CHOOSE_SAVE_SLOT = 15 { "FET_SG", MENUPAGE_DISABLED, 0, MENUACTION_SAVEGAME, "FEM_SL1", SAVESLOT_1, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 40, 90, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL2", SAVESLOT_2, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL3", SAVESLOT_3, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL4", SAVESLOT_4, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL5", SAVESLOT_5, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL6", SAVESLOT_6, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL7", SAVESLOT_7, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL8", SAVESLOT_8, MENUPAGE_SAVE_OVERWRITE_CONFIRM, 0, 0, MENUALIGN_LEFT, MENUACTION_RESUME_FROM_SAVEZONE,"FESZ_CA", SAVESLOT_NONE, 0, 320, 345, MENUALIGN_CENTER, }, // MENUPAGE_SAVE_OVERWRITE_CONFIRM = 16 { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, MENUACTION_LABEL, "FESZ_QZ", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, MENUACTION_NO, "FEM_NO", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 320, 200, MENUALIGN_CENTER, MENUACTION_YES, "FEM_YES", SAVESLOT_NONE, MENUPAGE_SAVING_IN_PROGRESS, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_SAVING_IN_PROGRESS = 17 { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, }, // MENUPAGE_SAVE_SUCCESSFUL = 18 { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, MENUACTION_LABEL, "FES_SSC", SAVESLOT_LABEL, MENUPAGE_NONE, 0, 0, 0, MENUACTION_RESUME_FROM_SAVEZONE, "FEM_OK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_SAVE_CUSTOM_WARNING = 19 { "FET_SG", MENUPAGE_NONE, 0, MENUACTION_LABEL, "", SAVESLOT_NONE, 0, 0, 0, 0, MENUACTION_CHANGEMENU, "FEM_OK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_SAVE_CHEAT_WARNING = 20 { "FET_SG", MENUPAGE_NEW_GAME, 0, MENUACTION_LABEL, "FES_CHE", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, MENUACTION_CHANGEMENU, "FEM_OK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_SKIN_SELECT = 21 { "FET_PS", MENUPAGE_OPTIONS, 4, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_OPTIONS, 0, 0, 0, }, // MENUPAGE_SAVE_UNUSED = 22 { "FET_SG", MENUPAGE_NEW_GAME, 0, MENUACTION_LABEL, "FED_LWR", SAVESLOT_NONE, 0, 0, 0, 0, MENUACTION_CHANGEMENU, "FEC_OKK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 0, 0, 0, }, // MENUPAGE_SAVE_FAILED = 23 { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, MENUACTION_LABEL, "FEC_SVU", SAVESLOT_NONE, 0, 0, 0, 0, MENUACTION_CHANGEMENU, "FEC_OKK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, 0, 0, 0, }, // MENUPAGE_SAVE_FAILED_2 = 24 { "FET_LG", MENUPAGE_CHOOSE_SAVE_SLOT, 0, MENUACTION_LABEL, "FEC_SVU", SAVESLOT_NONE, 0, 0, 0, 0, }, // MENUPAGE_LOAD_FAILED = 25 { "FET_LG", MENUPAGE_NEW_GAME, 0, MENUACTION_LABEL, "FEC_LUN", SAVESLOT_NONE, 0, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 0, 0, 0, }, // MENUPAGE_CONTROLLER_PC = 26 { "FET_CTL", MENUPAGE_OPTIONS, 0, #ifdef PC_PLAYER_CONTROLS MENUACTION_CTRLMETHOD, "FET_STI", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, 320, 150, MENUALIGN_CENTER, MENUACTION_KEYBOARDCTRLS,"FEC_RED", SAVESLOT_NONE, MENUPAGE_KEYBOARD_CONTROLS, 0, 0, MENUALIGN_CENTER, #else MENUACTION_KEYBOARDCTRLS,"FEC_RED", SAVESLOT_NONE, MENUPAGE_KEYBOARD_CONTROLS, 320, 150, MENUALIGN_CENTER, #endif MENUACTION_CHANGEMENU, "FEC_MOU", SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS, 0, 0, MENUALIGN_CENTER, MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, 0, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_OPTIONS = 27 { "FET_OPT", MENUPAGE_NONE, 5, MENUACTION_CHANGEMENU, "FEO_CON", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, 320, 132, MENUALIGN_CENTER, MENUACTION_LOADRADIO, "FEO_AUD", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEO_DIS", SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEO_LAN", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, 0, 0, MENUALIGN_CENTER, MENUACTION_PLAYERSETUP, "FET_PS", SAVESLOT_NONE, MENUPAGE_SKIN_SELECT, 0, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_EXIT = 28 { "FET_QG", MENUPAGE_NONE, 6, MENUACTION_LABEL, "FEQ_SRE", SAVESLOT_NONE, 0, 0, 0, 0, MENUACTION_DONTCANCEL, "FEM_NO", SAVESLOT_NONE, MENUPAGE_NONE, 320, 200, MENUALIGN_CENTER, MENUACTION_CANCELGAME, "FEM_YES", SAVESLOT_NONE, MENUPAGE_NONE, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_START_MENU = 29 { "FEM_MM", MENUPAGE_DISABLED, 0, MENUACTION_CHANGEMENU, "FEP_STG", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 320, 170, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEP_OPT", SAVESLOT_NONE, MENUPAGE_OPTIONS, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEP_QUI", SAVESLOT_NONE, MENUPAGE_EXIT, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_KEYBOARD_CONTROLS = 30 { "FET_STI", MENUPAGE_CONTROLLER_PC, 1, }, // MENUPAGE_MOUSE_CONTROLS = 31 { "FEC_MOU", MENUPAGE_CONTROLLER_PC, 2, MENUACTION_MOUSESENS, "FEC_MSH", SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS, 40, 170, MENUALIGN_LEFT, MENUACTION_INVVERT, "FEC_IVV", SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS, 0, 0, MENUALIGN_LEFT, MENUACTION_MOUSESTEER, "FET_MST", SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS, 0, 0, MENUALIGN_LEFT, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, 0, 320, 260, MENUALIGN_CENTER, }, // MENUPAGE_PAUSE_MENU = 32 { "FET_PAU", MENUPAGE_DISABLED, 0, MENUACTION_RESUME, "FEP_RES", SAVESLOT_NONE, 0, 320, 120, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEH_SGA", SAVESLOT_NONE, MENUPAGE_NEW_GAME, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEH_MAP", SAVESLOT_NONE, MENUPAGE_MAP, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEP_STA", SAVESLOT_NONE, MENUPAGE_STATS, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEH_BRI", SAVESLOT_NONE, MENUPAGE_BRIEFS, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FET_OPT", SAVESLOT_NONE, MENUPAGE_OPTIONS, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEP_QUI", SAVESLOT_NONE, MENUPAGE_EXIT, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_NONE = 33 { "", 0, 0, }, #ifdef LEGACY_MENU_OPTIONS // MENUPAGE_DEBUG_MENU { "FED_DBG", MENUPAGE_NONE, 0, MENUACTION_RELOADIDE, "FED_RID", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, MENUACTION_SETDBGFLAG, "FED_DFL", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, MENUACTION_SWITCHBIGWHITEDEBUGLIGHT, "FED_DLS", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, MENUACTION_COLLISIONPOLYS, "FED_SCP", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, }, // MENUPAGE_CONTROLLER_PC_OLD1 { "FET_CTL", MENUPAGE_CONTROLLER_PC, 0, MENUACTION_GETKEY, "FEC_PLB", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, MENUACTION_GETKEY, "FEC_CWL", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, MENUACTION_GETKEY, "FEC_CWR", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, MENUACTION_GETKEY, "FEC_LKT", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, MENUACTION_GETKEY, "FEC_PJP", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, MENUACTION_GETKEY, "FEC_PSP", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, MENUACTION_GETKEY, "FEC_TLF", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, MENUACTION_GETKEY, "FEC_TRG", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, MENUACTION_GETKEY, "FEC_CCM", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, }, // MENUPAGE_CONTROLLER_PC_OLD2 { "FET_CTL", MENUPAGE_CONTROLLER_PC, 1, }, // MENUPAGE_CONTROLLER_PC_OLD3 { "FET_CTL", MENUPAGE_CONTROLLER_PC, 2, MENUACTION_GETKEY, "FEC_LUP", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3, 0, 0, 0, MENUACTION_GETKEY, "FEC_LDN", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3, 0, 0, 0, MENUACTION_GETKEY, "FEC_SMS", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3, 0, 0, 0, MENUACTION_SHOWHEADBOB, "FEC_GSL", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, }, // MENUPAGE_CONTROLLER_PC_OLD4 { "FET_CTL", MENUPAGE_CONTROLLER_PC, 3, }, // MENUPAGE_CONTROLLER_DEBUG { "FEC_DBG", MENUPAGE_CONTROLLER_PC, 3, MENUACTION_GETKEY, "FEC_TGD", SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG, 0, 0, 0, MENUACTION_GETKEY, "FEC_TDO", SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG, 0, 0, 0, MENUACTION_GETKEY, "FEC_TSS", SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG, 0, 0, 0, MENUACTION_GETKEY, "FEC_SMS", SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, 0, 0, 0, }, #endif // MENUPAGE_OUTRO - Originally 34 { "", 0, 0, }, }; #endif #endif ================================================ FILE: src/core/MenuScreensCustom.cpp ================================================ #include "common.h" #if defined DETECT_JOYSTICK_MENU && defined XINPUT #include #include #if !defined(PSAPI_VERSION) || (PSAPI_VERSION > 1) #pragma comment( lib, "Xinput9_1_0.lib" ) #else #pragma comment( lib, "Xinput.lib" ) #endif #endif #include "platform.h" #include "crossplatform.h" #include "Renderer.h" #include "Frontend.h" #include "Font.h" #include "Camera.h" #include "main.h" #include "MBlur.h" #include "postfx.h" #include "custompipes.h" #include "RwHelper.h" #include "Text.h" #include "Streaming.h" #include "FileLoader.h" #include "Collision.h" #include "ModelInfo.h" #include "Pad.h" #include "ControllerConfig.h" // Menu screens array is at the bottom of the file. #ifdef PC_MENU #ifdef CUSTOM_FRONTEND_OPTIONS #ifdef IMPROVED_VIDEOMODE #define VIDEOMODE_SELECTOR MENUACTION_CFO_SELECT, "FEM_SCF", { new CCFOSelect((int8*)&FrontEndMenuManager.m_nPrefsWindowed, "VideoMode", "Windowed", screenModes, 2, true, ScreenModeAfterChange, true) }, 0, 0, MENUALIGN_LEFT, #else #define VIDEOMODE_SELECTOR #endif #ifdef MULTISAMPLING #define MULTISAMPLING_SELECTOR MENUACTION_CFO_DYNAMIC, "FED_AAS", { new CCFODynamic((int8*)&FrontEndMenuManager.m_nPrefsMSAALevel, "Graphics", "MultiSampling", MultiSamplingDraw, MultiSamplingButtonPress) }, 0, 0, MENUALIGN_LEFT, #else #define MULTISAMPLING_SELECTOR #endif #ifdef CUTSCENE_BORDERS_SWITCH #define CUTSCENE_BORDERS_TOGGLE MENUACTION_CFO_SELECT, "FEM_CSB", { new CCFOSelect((int8 *)&FrontEndMenuManager.m_PrefsCutsceneBorders, "Display", "CutsceneBorders", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, #else #define CUTSCENE_BORDERS_TOGGLE #endif #ifdef FREE_CAM #define FREE_CAM_TOGGLE MENUACTION_CFO_SELECT, "FEC_FRC", { new CCFOSelect((int8*)&TheCamera.bFreeCam, "Display", "FreeCam", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, #else #define FREE_CAM_TOGGLE #endif #ifdef PS2_ALPHA_TEST #define DUALPASS_SELECTOR MENUACTION_CFO_SELECT, "FEM_2PR", { new CCFOSelect((int8*)&gPS2alphaTest, "Graphics", "PS2AlphaTest", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, #else #define DUALPASS_SELECTOR #endif #ifdef NO_ISLAND_LOADING #define ISLAND_LOADING_SELECTOR MENUACTION_CFO_SELECT, "FEM_ISL", { new CCFOSelect((int8*)&FrontEndMenuManager.m_PrefsIslandLoading, "Graphics", "IslandLoading", islandLoadingOpts, ARRAY_SIZE(islandLoadingOpts), true, IslandLoadingAfterChange) }, 0, 0, MENUALIGN_LEFT, #else #define ISLAND_LOADING_SELECTOR #endif #ifdef EXTENDED_COLOURFILTER #define POSTFX_SELECTORS \ MENUACTION_CFO_SELECT, "FED_CLF", { new CCFOSelect((int8*)&CPostFX::EffectSwitch, "Graphics", "ColourFilter", filterNames, ARRAY_SIZE(filterNames), false) }, 0, 0, MENUALIGN_LEFT, \ MENUACTION_CFO_SELECT, "FED_MBL", { new CCFOSelect((int8*)&CPostFX::MotionBlurOn, "Graphics", "MotionBlur", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, #else #define POSTFX_SELECTORS #endif #ifdef INVERT_LOOK_FOR_PAD #define INVERT_PAD_SELECTOR MENUACTION_CFO_SELECT, "FEC_ILU", { new CCFOSelect((int8*)&CPad::bInvertLook4Pad, "Controller", "InvertPad", off_on, 2, false) }, 0, 0, MENUALIGN_LEFT, #else #define INVERT_PAD_SELECTOR #endif #ifdef GAMEPAD_MENU #define SELECT_CONTROLLER_TYPE MENUACTION_CFO_SELECT, "FEC_TYP", { new CCFOSelect((int8*)&FrontEndMenuManager.m_PrefsControllerType, "Controller", "Type", controllerTypes, ARRAY_SIZE(controllerTypes), false, ControllerTypeAfterChange) }, 0, 0, MENUALIGN_LEFT, #else #define SELECT_CONTROLLER_TYPE #endif const char *filterNames[] = { "FEM_NON", "FEM_SIM", "FEM_NRM", "FEM_MOB" }; const char *off_on[] = { "FEM_OFF", "FEM_ON" }; void RestoreDefGraphics(int8 action) { if (action != FEOPTION_ACTION_SELECT) return; #ifdef PS2_ALPHA_TEST gPS2alphaTest = false; #endif #ifdef MULTISAMPLING FrontEndMenuManager.m_nPrefsMSAALevel = FrontEndMenuManager.m_nDisplayMSAALevel = 0; #endif #ifdef NO_ISLAND_LOADING if (!FrontEndMenuManager.m_bGameNotLoaded) { FrontEndMenuManager.m_PrefsIslandLoading = FrontEndMenuManager.ISLAND_LOADING_LOW; CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); CStreaming::RemoveUnusedBuildings(CGame::currLevel); CStreaming::RequestIslands(CGame::currLevel); CStreaming::LoadAllRequestedModels(true); } else FrontEndMenuManager.m_PrefsIslandLoading = FrontEndMenuManager.ISLAND_LOADING_LOW; #endif #ifdef GRAPHICS_MENU_OPTIONS // otherwise Frontend will handle those FrontEndMenuManager.m_PrefsFrameLimiter = true; FrontEndMenuManager.m_PrefsVsyncDisp = true; #ifdef LEGACY_MENU_OPTIONS FrontEndMenuManager.m_PrefsVsync = true; #endif FrontEndMenuManager.m_PrefsUseWideScreen = false; FrontEndMenuManager.m_nDisplayVideoMode = FrontEndMenuManager.m_nPrefsVideoMode; CMBlur::BlurOn = false; FrontEndMenuManager.SaveSettings(); #endif } void RestoreDefDisplay(int8 action) { if (action != FEOPTION_ACTION_SELECT) return; #ifdef CUTSCENE_BORDERS_SWITCH FrontEndMenuManager.m_PrefsCutsceneBorders = true; #endif #ifdef FREE_CAM TheCamera.bFreeCam = false; #endif #ifdef GRAPHICS_MENU_OPTIONS // otherwise Frontend will handle those FrontEndMenuManager.m_PrefsBrightness = 256; FrontEndMenuManager.m_PrefsLOD = 1.2f; CRenderer::ms_lodDistScale = 1.2f; FrontEndMenuManager.m_PrefsShowSubtitles = false; FrontEndMenuManager.m_PrefsShowLegends = true; FrontEndMenuManager.m_PrefsRadarMode = 0; FrontEndMenuManager.m_PrefsShowHud = true; FrontEndMenuManager.SaveSettings(); #endif } #ifdef NO_ISLAND_LOADING const char *islandLoadingOpts[] = { "FEM_LOW", "FEM_MED", "FEM_HIG" }; void IslandLoadingAfterChange(int8 before, int8 after) { if (!FrontEndMenuManager.m_bGameNotLoaded) { if (after > FrontEndMenuManager.ISLAND_LOADING_LOW) { FrontEndMenuManager.m_PrefsIslandLoading = before; // calls below needs previous mode :shrug: if (after == FrontEndMenuManager.ISLAND_LOADING_HIGH) { CStreaming::RemoveIslandsNotUsed(LEVEL_BEACH); CStreaming::RemoveIslandsNotUsed(LEVEL_MAINLAND); } if (before == FrontEndMenuManager.ISLAND_LOADING_LOW) { FrontEndMenuManager.m_PrefsIslandLoading = after; CStreaming::RequestBigBuildings(CGame::currLevel); } else if (before == FrontEndMenuManager.ISLAND_LOADING_HIGH) { FrontEndMenuManager.m_PrefsIslandLoading = after; CStreaming::RequestIslands(CGame::currLevel); } else FrontEndMenuManager.m_PrefsIslandLoading = after; } else { // low CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); CStreaming::RemoveUnusedBuildings(CGame::currLevel); CStreaming::RequestIslands(CGame::currLevel); } CStreaming::LoadAllRequestedModels(true); } FrontEndMenuManager.SetHelperText(0); } #endif #ifndef MULTISAMPLING void GraphicsGoBack() { } #else void GraphicsGoBack() { FrontEndMenuManager.m_nDisplayMSAALevel = FrontEndMenuManager.m_nPrefsMSAALevel; } void MultiSamplingButtonPress(int8 action) { if (action == FEOPTION_ACTION_SELECT) { if (FrontEndMenuManager.m_nDisplayMSAALevel != FrontEndMenuManager.m_nPrefsMSAALevel) { FrontEndMenuManager.m_nPrefsMSAALevel = FrontEndMenuManager.m_nDisplayMSAALevel; _psSelectScreenVM(FrontEndMenuManager.m_nPrefsVideoMode); FrontEndMenuManager.SetHelperText(0); FrontEndMenuManager.SaveSettings(); } } else if (action == FEOPTION_ACTION_LEFT || action == FEOPTION_ACTION_RIGHT) { if (FrontEndMenuManager.m_bGameNotLoaded) { FrontEndMenuManager.m_nDisplayMSAALevel += (action == FEOPTION_ACTION_RIGHT ? 1 : -1); int i = 0; int maxAA = RwD3D8EngineGetMaxMultiSamplingLevels(); while (maxAA != 1) { i++; maxAA >>= 1; } if (FrontEndMenuManager.m_nDisplayMSAALevel < 0) FrontEndMenuManager.m_nDisplayMSAALevel = i; else if (FrontEndMenuManager.m_nDisplayMSAALevel > i) FrontEndMenuManager.m_nDisplayMSAALevel = 0; } } else if (action == FEOPTION_ACTION_FOCUSLOSS) { if (FrontEndMenuManager.m_nDisplayMSAALevel != FrontEndMenuManager.m_nPrefsMSAALevel) { FrontEndMenuManager.m_nDisplayMSAALevel = FrontEndMenuManager.m_nPrefsMSAALevel; FrontEndMenuManager.SetHelperText(3); } } } wchar* MultiSamplingDraw(bool *disabled, bool userHovering) { static wchar unicodeTemp[64]; if (userHovering) { if (FrontEndMenuManager.m_nDisplayMSAALevel == FrontEndMenuManager.m_nPrefsMSAALevel) { if (FrontEndMenuManager.m_nHelperTextMsgId == 1) // Press enter to apply FrontEndMenuManager.ResetHelperText(); } else { FrontEndMenuManager.SetHelperText(1); } } else { if (FrontEndMenuManager.m_nDisplayMSAALevel != FrontEndMenuManager.m_nPrefsMSAALevel) { FrontEndMenuManager.m_nDisplayMSAALevel = FrontEndMenuManager.m_nPrefsMSAALevel; } } if (!FrontEndMenuManager.m_bGameNotLoaded) *disabled = true; switch (FrontEndMenuManager.m_nDisplayMSAALevel) { case 0: return TheText.Get("FEM_OFF"); default: sprintf(gString, "%iX", 1 << (FrontEndMenuManager.m_nDisplayMSAALevel)); AsciiToUnicode(gString, unicodeTemp); return unicodeTemp; } } #endif #ifdef IMPROVED_VIDEOMODE const char* screenModes[] = { "FED_FLS", "FED_WND" }; void ScreenModeAfterChange(int8 before, int8 after) { _psSelectScreenVM(FrontEndMenuManager.m_nPrefsVideoMode); // apply same resolution FrontEndMenuManager.SetHelperText(0); } #endif #ifdef DETECT_JOYSTICK_MENU wchar selectedJoystickUnicode[128]; int cachedButtonNum = -1; wchar* DetectJoystickDraw(bool* disabled, bool userHovering) { #if defined RW_GL3 && !defined LIBRW_SDL2 int numButtons; int found = -1; const char *joyname; if (userHovering) { for (int i = 0; i <= GLFW_JOYSTICK_LAST; i++) { if ((joyname = glfwGetJoystickName(i))) { const uint8* buttons = glfwGetJoystickButtons(i, &numButtons); for (int j = 0; j < numButtons; j++) { if (buttons[j]) { found = i; break; } } if (found != -1) break; } } if (found != -1 && PSGLOBAL(joy1id) != found) { if (PSGLOBAL(joy1id) != -1 && PSGLOBAL(joy1id) != found) PSGLOBAL(joy2id) = PSGLOBAL(joy1id); else PSGLOBAL(joy2id) = -1; strcpy(gSelectedJoystickName, joyname); PSGLOBAL(joy1id) = found; cachedButtonNum = numButtons; } } if (PSGLOBAL(joy1id) == -1) #elif defined XINPUT int found = -1; XINPUT_STATE xstate; memset(&xstate, 0, sizeof(XINPUT_STATE)); if (userHovering) { for (int i = 0; i <= 3; i++) { if (XInputGetState(i, &xstate) == ERROR_SUCCESS) { if (xstate.Gamepad.bLeftTrigger || xstate.Gamepad.bRightTrigger) { found = i; break; } for (int j = XINPUT_GAMEPAD_DPAD_UP; j != XINPUT_GAMEPAD_Y << 1; j = (j << 1)) { if (xstate.Gamepad.wButtons & j) { found = i; break; } } if (found != -1) break; } } if (found != -1 && CPad::XInputJoy1 != found) { // We should never leave pads -1, so we can process them when they're connected and kinda support hotplug. CPad::XInputJoy2 = (CPad::XInputJoy1 == -1 ? (found + 1) % 4 : CPad::XInputJoy1); CPad::XInputJoy1 = found; cachedButtonNum = 0; // fake too, because xinput bypass CControllerConfig } } sprintf(gSelectedJoystickName, "%d", CPad::XInputJoy1); // fake, on xinput we only store gamepad ids(thanks MS) so this is a temp variable to be used below if (CPad::XInputJoy1 == -1) #endif AsciiToUnicode("Not found", selectedJoystickUnicode); else AsciiToUnicode(gSelectedJoystickName, selectedJoystickUnicode); return selectedJoystickUnicode; } void DetectJoystickGoBack() { if (cachedButtonNum != -1) { #ifdef LOAD_INI_SETTINGS ControlsManager.InitDefaultControlConfigJoyPad(cachedButtonNum); SaveINIControllerSettings(); #else // Otherwise no way to save gSelectedJoystickName or ms_padButtonsInited anyway :shrug: Why do you even use this config.?? #endif cachedButtonNum = -1; } } #endif #ifdef GAMEPAD_MENU const char* controllerTypes[] = { "FEC_DS2", "FEC_DS3", "FEC_DS4", "FEC_360", "FEC_ONE" }; void ControllerTypeAfterChange(int8 before, int8 after) { FrontEndMenuManager.LoadController(after); } #endif CMenuScreenCustom aScreens[] = { // MENUPAGE_STATS = 0 { "FEH_STA", MENUPAGE_NONE, nil, nil, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 190, 320, MENUALIGN_RIGHT, }, // MENUPAGE_NEW_GAME = 1 { "FEP_STG", MENUPAGE_NONE, nil, nil, MENUACTION_CHANGEMENU, "FES_NGA", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD}, 320, 155, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FES_LOA", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT}, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FES_DEL", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT}, 0, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_BRIEFS = 2 { "FEH_BRI", MENUPAGE_NONE, nil, nil, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 190, 320, MENUALIGN_RIGHT, }, // MENUPAGE_SOUND_SETTINGS = 3 { "FEH_AUD", MENUPAGE_OPTIONS, nil, nil, MENUACTION_MUSICVOLUME, "FEA_MUS", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 40, 76, MENUALIGN_LEFT, MENUACTION_SFXVOLUME, "FEA_SFX", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_MP3VOLUMEBOOST, "FEA_MPB", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_AUDIOHW, "FEA_3DH", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_SPEAKERCONF, "FEA_SPK", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_DYNAMICACOUSTIC, "FET_DAM", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_RADIO, "FEA_RSS", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_RESTOREDEF, "FET_DEF", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 320, 367, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_DISPLAY_SETTINGS = 4 #ifndef GRAPHICS_MENU_OPTIONS { "FEH_DIS", MENUPAGE_OPTIONS, new CCustomScreenLayout({40, 78, 25, true}), nil, MENUACTION_BRIGHTNESS, "FED_BRI", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_DRAWDIST, "FEM_LOD", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, #ifdef LEGACY_MENU_OPTIONS MENUACTION_FRAMESYNC, "FEM_VSC", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, #endif MENUACTION_FRAMELIMIT, "FEM_FRM", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, #if defined LEGACY_MENU_OPTIONS && !defined EXTENDED_COLOURFILTER MENUACTION_TRAILS, "FED_TRA", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, #endif MENUACTION_SUBTITLES, "FED_SUB", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_WIDESCREEN, "FED_WIS", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_LEGENDS, "MAP_LEG", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_RADARMODE, "FED_RDR", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_HUD, "FED_HUD", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, MENUACTION_SCREENRES, "FED_RES", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_LEFT, VIDEOMODE_SELECTOR MULTISAMPLING_SELECTOR ISLAND_LOADING_SELECTOR DUALPASS_SELECTOR CUTSCENE_BORDERS_TOGGLE FREE_CAM_TOGGLE POSTFX_SELECTORS // re3.cpp inserts here pipeline selectors if neo/neo.txd exists and EXTENDED_PIPELINES defined MENUACTION_RESTOREDEF, "FET_DEF", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 320, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 0, MENUALIGN_CENTER, }, #else { "FEH_DIS", MENUPAGE_OPTIONS, new CCustomScreenLayout({40, 78, 25, true}), nil, MENUACTION_BRIGHTNESS, "FED_BRI", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, MENUACTION_DRAWDIST, "FEM_LOD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, CUTSCENE_BORDERS_TOGGLE FREE_CAM_TOGGLE MENUACTION_LEGENDS, "MAP_LEG", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, MENUACTION_RADARMODE, "FED_RDR", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, MENUACTION_HUD, "FED_HUD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, MENUACTION_SUBTITLES, "FED_SUB", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 0, 0, MENUALIGN_LEFT, MENUACTION_CFO_DYNAMIC, "FET_DEF", { new CCFODynamic(nil, nil, nil, nil, RestoreDefDisplay) }, 320, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", { nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 0, MENUALIGN_CENTER, }, #endif // MENUPAGE_LANGUAGE_SETTINGS = 5 { "FEH_LAN", MENUPAGE_OPTIONS, nil, nil, MENUACTION_LANG_ENG, "FEL_ENG", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 320, 132, MENUALIGN_CENTER, MENUACTION_LANG_FRE, "FEL_FRE", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, MENUACTION_LANG_GER, "FEL_GER", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, MENUACTION_LANG_ITA, "FEL_ITA", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, MENUACTION_LANG_SPA, "FEL_SPA", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_MAP = 6 { "FEH_MAP", MENUPAGE_NONE, nil, nil, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 70, 380, MENUALIGN_CENTER, }, // MENUPAGE_NEW_GAME_RELOAD = 7 { "FES_NGA", MENUPAGE_NEW_GAME, nil, nil, MENUACTION_LABEL, "FESZ_QR", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, MENUACTION_NO, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 320, 200, MENUALIGN_CENTER, MENUACTION_NEWGAME, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD}, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_CHOOSE_LOAD_SLOT = 8 { "FET_LG", MENUPAGE_NEW_GAME, nil, nil, MENUACTION_CHECKSAVE, "FEM_SL1", {nil, SAVESLOT_1, 0}, 40, 90, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL2", {nil, SAVESLOT_2, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL3", {nil, SAVESLOT_3, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL4", {nil, SAVESLOT_4, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL5", {nil, SAVESLOT_5, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL6", {nil, SAVESLOT_6, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL7", {nil, SAVESLOT_7, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL8", {nil, SAVESLOT_8, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 345, MENUALIGN_CENTER, }, // MENUPAGE_CHOOSE_DELETE_SLOT = 9 { "FES_DEL", MENUPAGE_NEW_GAME, nil, nil, MENUACTION_CHECKSAVE, "FEM_SL1", {nil, SAVESLOT_1, 0}, 40, 90, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL2", {nil, SAVESLOT_2, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL3", {nil, SAVESLOT_3, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL4", {nil, SAVESLOT_4, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL5", {nil, SAVESLOT_5, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL6", {nil, SAVESLOT_6, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL7", {nil, SAVESLOT_7, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_CHECKSAVE, "FEM_SL8", {nil, SAVESLOT_8, 0}, 0, 0, MENUALIGN_LEFT, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 345, MENUALIGN_CENTER, }, // MENUPAGE_LOAD_SLOT_CONFIRM = 10 { "FET_LG", MENUPAGE_CHOOSE_LOAD_SLOT, nil, nil, MENUACTION_LABEL, "FESZ_QL", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, MENUACTION_NO, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT}, 320, 200, MENUALIGN_CENTER, MENUACTION_YES, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_LOADING_IN_PROGRESS}, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_DELETE_SLOT_CONFIRM = 11 { "FES_DEL", MENUPAGE_CHOOSE_DELETE_SLOT, nil, nil, MENUACTION_LABEL, "FESZ_QD", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, MENUACTION_NO, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT}, 320, 200, MENUALIGN_CENTER, MENUACTION_YES, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_DELETING_IN_PROGRESS}, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_LOADING_IN_PROGRESS = 12 { "FET_LG", MENUPAGE_CHOOSE_LOAD_SLOT, nil, nil, }, // MENUPAGE_DELETING_IN_PROGRESS = 13 { "FES_DEL", MENUPAGE_CHOOSE_DELETE_SLOT, nil, nil, }, // MENUPAGE_DELETE_SUCCESSFUL = 14 { "FES_DEL", MENUPAGE_NEW_GAME, nil, nil, MENUACTION_LABEL, "FES_DSC", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, MENUACTION_CHANGEMENU, "FEM_OK", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_CHOOSE_SAVE_SLOT = 15 { "FET_SG", MENUPAGE_DISABLED, nil, nil, MENUACTION_SAVEGAME, "FEM_SL1", {nil, SAVESLOT_1, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 40, 90, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL2", {nil, SAVESLOT_2, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL3", {nil, SAVESLOT_3, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL4", {nil, SAVESLOT_4, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL5", {nil, SAVESLOT_5, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL6", {nil, SAVESLOT_6, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL7", {nil, SAVESLOT_7, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, MENUACTION_SAVEGAME, "FEM_SL8", {nil, SAVESLOT_8, MENUPAGE_SAVE_OVERWRITE_CONFIRM}, 0, 0, MENUALIGN_LEFT, MENUACTION_RESUME_FROM_SAVEZONE,"FESZ_CA", {nil, SAVESLOT_NONE, 0}, 320, 345, MENUALIGN_CENTER, }, // MENUPAGE_SAVE_OVERWRITE_CONFIRM = 16 { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, MENUACTION_LABEL, "FESZ_QZ", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, MENUACTION_NO, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 320, 200, MENUALIGN_CENTER, MENUACTION_YES, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_SAVING_IN_PROGRESS}, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_SAVING_IN_PROGRESS = 17 { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, }, // MENUPAGE_SAVE_SUCCESSFUL = 18 { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, MENUACTION_LABEL, "FES_SSC", {nil, SAVESLOT_LABEL, MENUPAGE_NONE}, 0, 0, 0, MENUACTION_RESUME_FROM_SAVEZONE, "FEM_OK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_SAVE_CUSTOM_WARNING = 19 { "FET_SG", MENUPAGE_NONE, nil, nil, MENUACTION_LABEL, "", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, MENUACTION_CHANGEMENU, "FEM_OK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_SAVE_CHEAT_WARNING = 20 { "FET_SG", MENUPAGE_NEW_GAME, nil, nil, MENUACTION_LABEL, "FES_CHE", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, MENUACTION_CHANGEMENU, "FEM_OK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_SKIN_SELECT = 21 { "FET_PS", MENUPAGE_OPTIONS, nil, nil, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_OPTIONS}, 0, 0, 0, }, // MENUPAGE_SAVE_UNUSED = 22 { "FET_SG", MENUPAGE_NEW_GAME, nil, nil, MENUACTION_LABEL, "FED_LWR", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, MENUACTION_CHANGEMENU, "FEC_OKK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 0, 0, 0, }, // MENUPAGE_SAVE_FAILED = 23 { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, MENUACTION_LABEL, "FEC_SVU", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, MENUACTION_CHANGEMENU, "FEC_OKK", {nil, SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT}, 0, 0, 0, }, // MENUPAGE_SAVE_FAILED_2 = 24 { "FET_LG", MENUPAGE_CHOOSE_SAVE_SLOT, nil, nil, MENUACTION_LABEL, "FEC_SVU", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, }, // MENUPAGE_LOAD_FAILED = 25 { "FET_LG", MENUPAGE_NEW_GAME, nil, nil, MENUACTION_LABEL, "FEC_LUN", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 0, 0, 0, }, // MENUPAGE_CONTROLLER_PC = 26 { "FET_CTL", MENUPAGE_OPTIONS, new CCustomScreenLayout({0, 0, MENU_DEFAULT_LINE_HEIGHT, false, false, 150}), nil, #ifdef PC_PLAYER_CONTROLS MENUACTION_CTRLMETHOD, "FET_STI", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC}, 320, 150, MENUALIGN_CENTER, MENUACTION_KEYBOARDCTRLS,"FEC_RED", {nil, SAVESLOT_NONE, MENUPAGE_KEYBOARD_CONTROLS}, 0, 0, MENUALIGN_CENTER, #else MENUACTION_KEYBOARDCTRLS,"FEC_RED", {nil, SAVESLOT_NONE, MENUPAGE_KEYBOARD_CONTROLS}, 320, 150, MENUALIGN_CENTER, #endif #ifdef GAMEPAD_MENU MENUACTION_CHANGEMENU, "FET_AGS", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS}, 0, 0, MENUALIGN_CENTER, #endif #ifdef DETECT_JOYSTICK_MENU MENUACTION_CHANGEMENU, "FEC_JOD", {nil, SAVESLOT_NONE, MENUPAGE_DETECT_JOYSTICK}, 0, 0, MENUALIGN_CENTER, #endif MENUACTION_CHANGEMENU, "FEC_MOU", {nil, SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS}, 0, 0, MENUALIGN_CENTER, MENUACTION_RESTOREDEF, "FET_DEF", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC}, 320, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 0, MENUALIGN_CENTER, }, // MENUPAGE_OPTIONS = 27 { "FET_OPT", MENUPAGE_NONE, nil, nil, MENUACTION_CHANGEMENU, "FEO_CON", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC}, 320, 132, MENUALIGN_CENTER, MENUACTION_LOADRADIO, "FEO_AUD", {nil, SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS}, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEO_DIS", {nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS}, 0, 0, MENUALIGN_CENTER, #ifdef GRAPHICS_MENU_OPTIONS MENUACTION_CHANGEMENU, "FET_GFX", {nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS}, 0, 0, MENUALIGN_CENTER, #endif MENUACTION_CHANGEMENU, "FEO_LAN", {nil, SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS}, 0, 0, MENUALIGN_CENTER, MENUACTION_PLAYERSETUP, "FET_PS", {nil, SAVESLOT_NONE, MENUPAGE_SKIN_SELECT}, 0, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_EXIT = 28 { "FET_QG", MENUPAGE_NONE, nil, nil, MENUACTION_LABEL, "FEQ_SRE", {nil, SAVESLOT_NONE, 0}, 0, 0, 0, MENUACTION_DONTCANCEL, "FEM_NO", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 200, MENUALIGN_CENTER, MENUACTION_CANCELGAME, "FEM_YES", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 225, MENUALIGN_CENTER, }, // MENUPAGE_START_MENU = 29 { "FEM_MM", MENUPAGE_DISABLED, nil, nil, MENUACTION_CHANGEMENU, "FEP_STG", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 320, 170, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEP_OPT", {nil, SAVESLOT_NONE, MENUPAGE_OPTIONS}, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEP_QUI", {nil, SAVESLOT_NONE, MENUPAGE_EXIT}, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_KEYBOARD_CONTROLS = 30 { "FET_STI", MENUPAGE_CONTROLLER_PC, nil, nil, }, // MENUPAGE_MOUSE_CONTROLS = 31 { "FEC_MOU", MENUPAGE_CONTROLLER_PC, nil, nil, MENUACTION_MOUSESENS, "FEC_MSH", {nil, SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS}, 40, 170, MENUALIGN_LEFT, MENUACTION_INVVERT, "FEC_IVV", {nil, SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS}, 0, 0, MENUALIGN_LEFT, #ifndef GAMEPAD_MENU INVERT_PAD_SELECTOR #endif MENUACTION_MOUSESTEER, "FET_MST", {nil, SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS}, 0, 0, MENUALIGN_LEFT, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 0, MENUALIGN_CENTER, //MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, 0}, 320, 260, MENUALIGN_CENTER, // original y }, // MENUPAGE_PAUSE_MENU = 32 { "FET_PAU", MENUPAGE_DISABLED, nil, nil, MENUACTION_RESUME, "FEP_RES", {nil, SAVESLOT_NONE, 0}, 320, 120, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEH_SGA", {nil, SAVESLOT_NONE, MENUPAGE_NEW_GAME}, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEH_MAP", {nil, SAVESLOT_NONE, MENUPAGE_MAP}, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEP_STA", {nil, SAVESLOT_NONE, MENUPAGE_STATS}, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEH_BRI", {nil, SAVESLOT_NONE, MENUPAGE_BRIEFS}, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FET_OPT", {nil, SAVESLOT_NONE, MENUPAGE_OPTIONS}, 0, 0, MENUALIGN_CENTER, MENUACTION_CHANGEMENU, "FEP_QUI", {nil, SAVESLOT_NONE, MENUPAGE_EXIT}, 0, 0, MENUALIGN_CENTER, }, // MENUPAGE_NONE = 33 { "", 0, nil, nil, }, #ifdef GAMEPAD_MENU { "FET_AGS", MENUPAGE_CONTROLLER_PC, new CCustomScreenLayout({40, 78, 25, true, true}), nil, MENUACTION_CTRLCONFIG, "FEC_CCF", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS }, 40, 76, MENUALIGN_LEFT, MENUACTION_CTRLDISPLAY, "FEC_CDP", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS }, 0, 0, MENUALIGN_LEFT, INVERT_PAD_SELECTOR MENUACTION_CTRLVIBRATION, "FEC_VIB", { nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_SETTINGS }, 0, 0, MENUALIGN_LEFT, SELECT_CONTROLLER_TYPE MENUACTION_GOBACK, "FEDS_TB", { nil, SAVESLOT_NONE, MENUPAGE_NONE }, 0, 0, MENUALIGN_LEFT, }, #endif #ifdef LEGACY_MENU_OPTIONS // MENUPAGE_DEBUG_MENU = 18 { "FED_DBG", MENUPAGE_NONE, nil, nil, MENUACTION_RELOADIDE, "FED_RID", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, MENUACTION_SETDBGFLAG, "FED_DFL", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, MENUACTION_SWITCHBIGWHITEDEBUGLIGHT, "FED_DLS", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, MENUACTION_COLLISIONPOLYS, "FED_SCP", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, }, // MENUPAGE_CONTROLLER_PC_OLD1 = 36 { "FET_CTL", MENUPAGE_CONTROLLER_PC, nil, nil, MENUACTION_GETKEY, "FEC_PLB", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, MENUACTION_GETKEY, "FEC_CWL", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, MENUACTION_GETKEY, "FEC_CWR", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, MENUACTION_GETKEY, "FEC_LKT", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, MENUACTION_GETKEY, "FEC_PJP", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, MENUACTION_GETKEY, "FEC_PSP", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, MENUACTION_GETKEY, "FEC_TLF", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, MENUACTION_GETKEY, "FEC_TRG", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, MENUACTION_GETKEY, "FEC_CCM", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD1}, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, }, // MENUPAGE_CONTROLLER_PC_OLD2 = 37 { "FET_CTL", MENUPAGE_CONTROLLER_PC, nil, nil, }, // MENUPAGE_CONTROLLER_PC_OLD3 = 38 { "FET_CTL", MENUPAGE_CONTROLLER_PC, nil, nil, MENUACTION_GETKEY, "FEC_LUP", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3}, 0, 0, 0, MENUACTION_GETKEY, "FEC_LDN", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3}, 0, 0, 0, MENUACTION_GETKEY, "FEC_SMS", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3}, 0, 0, 0, MENUACTION_SHOWHEADBOB, "FEC_GSL", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC_OLD3}, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, }, // MENUPAGE_CONTROLLER_PC_OLD4 = 39 { "FET_CTL", MENUPAGE_CONTROLLER_PC, nil, nil, }, // MENUPAGE_CONTROLLER_DEBUG = 40 { "FEC_DBG", MENUPAGE_CONTROLLER_PC, nil, nil, MENUACTION_GETKEY, "FEC_TGD", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG}, 0, 0, 0, MENUACTION_GETKEY, "FEC_TDO", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG}, 0, 0, 0, MENUACTION_GETKEY, "FEC_TSS", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG}, 0, 0, 0, MENUACTION_GETKEY, "FEC_SMS", {nil, SAVESLOT_NONE, MENUPAGE_CONTROLLER_DEBUG}, 0, 0, 0, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 0, 0, 0, }, #endif #ifdef GRAPHICS_MENU_OPTIONS // MENUPAGE_GRAPHICS_SETTINGS { "FET_GFX", MENUPAGE_OPTIONS, new CCustomScreenLayout({40, 78, 25, true, true}), GraphicsGoBack, MENUACTION_SCREENRES, "FED_RES", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, 0, 0, MENUALIGN_LEFT, MENUACTION_WIDESCREEN, "FED_WIS", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, 0, 0, MENUALIGN_LEFT, VIDEOMODE_SELECTOR #ifdef LEGACY_MENU_OPTIONS MENUACTION_FRAMESYNC, "FEM_VSC", {nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS}, 0, 0, MENUALIGN_LEFT, #endif MENUACTION_FRAMELIMIT, "FEM_FRM", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, 0, 0, MENUALIGN_LEFT, MULTISAMPLING_SELECTOR ISLAND_LOADING_SELECTOR DUALPASS_SELECTOR #ifdef EXTENDED_COLOURFILTER POSTFX_SELECTORS #elif defined LEGACY_MENU_OPTIONS MENUACTION_TRAILS, "FED_TRA", { nil, SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS }, 0, 0, MENUALIGN_LEFT, #endif // re3.cpp inserts here pipeline selectors if neo/neo.txd exists and EXTENDED_PIPELINES defined MENUACTION_CFO_DYNAMIC, "FET_DEF", { new CCFODynamic(nil, nil, nil, nil, RestoreDefGraphics) }, 320, 0, MENUALIGN_CENTER, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 0, MENUALIGN_CENTER, }, #endif #ifdef DETECT_JOYSTICK_MENU // MENUPAGE_DETECT_JOYSTICK { "FEC_JOD", MENUPAGE_CONTROLLER_PC, new CCustomScreenLayout({0, 0, 0, false, false, 30}), DetectJoystickGoBack, MENUACTION_LABEL, "FEC_JPR", { nil, SAVESLOT_NONE, MENUPAGE_NONE }, 0, 0, 0, MENUACTION_CFO_DYNAMIC, "FEC_JDE", { new CCFODynamic(nil, nil, nil, DetectJoystickDraw, nil) }, 80, 200, MENUALIGN_LEFT, MENUACTION_GOBACK, "FEDS_TB", {nil, SAVESLOT_NONE, MENUPAGE_NONE}, 320, 225, MENUALIGN_CENTER, }, #endif // MENUPAGE_OUTRO = 34 { "", 0, nil, nil, }, }; #endif #endif ================================================ FILE: src/core/Pad.cpp ================================================ #define WITHDINPUT #include "common.h" #include "crossplatform.h" #include "platform.h" #ifdef XINPUT #include #if !defined(PSAPI_VERSION) || (PSAPI_VERSION > 1) #pragma comment( lib, "Xinput9_1_0.lib" ) #else #pragma comment( lib, "Xinput.lib" ) #endif #endif #include "Pad.h" #include "ControllerConfig.h" #include "Timer.h" #include "Frontend.h" #include "Camera.h" #include "Game.h" #include "CutsceneMgr.h" #include "Font.h" #include "Hud.h" #include "Text.h" #include "Timer.h" #include "Record.h" #include "World.h" #include "Vehicle.h" #include "Ped.h" #include "Population.h" #include "Record.h" #include "Replay.h" #include "Weather.h" #include "Streaming.h" #include "PathFind.h" #include "Wanted.h" #include "WaterLevel.h" #include "General.h" #include "Fluff.h" #include "Gangs.h" #include "platform.h" #include "Stats.h" #include "CarCtrl.h" #include "TrafficLights.h" #ifdef GTA_PS2 #include "eetypes.h" #include "libpad.h" #endif CPad Pads[MAX_PADS]; #ifdef GTA_PS2 u_long128 pad_dma_buf[scePadDmaBufferMax] __attribute__((aligned(64))); u_long128 pad2_dma_buf[scePadDmaBufferMax] __attribute__((aligned(64))); #endif CMousePointerStateHelper MousePointerStateHelper; bool CPad::bDisplayNoControllerMessage; bool CPad::bObsoleteControllerMessage; bool CPad::bOldDisplayNoControllerMessage; bool CPad::m_bMapPadOneToPadTwo; bool CPad::m_bDebugCamPCOn; bool CPad::bHasPlayerCheated; bool CPad::bInvertLook4Pad; #ifdef GTA_PS2 unsigned char act_direct[6]; unsigned char act_align[6]; #endif CKeyboardState CPad::OldKeyState; CKeyboardState CPad::NewKeyState; CKeyboardState CPad::TempKeyState; char CPad::KeyBoardCheatString[30]; CMouseControllerState CPad::OldMouseControllerState; CMouseControllerState CPad::NewMouseControllerState; CMouseControllerState CPad::PCTempMouseControllerState; #ifdef DETECT_PAD_INPUT_SWITCH bool CPad::IsAffectedByController = false; #endif _TODO("gbFastTime"); extern bool gbFastTime; #ifdef WALLCLIMB_CHEAT extern bool gGravityCheat; #endif void SpecialCarCheats() { if ( !CVehicle::bCheat9 ) { ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_INFERNUS))->m_wheelScale *= 1.3f; ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_CHEETAH))->m_wheelScale *= 1.3f; ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_PHEONIX))->m_wheelScale *= 1.3f; ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_DELUXO))->m_wheelScale *= 1.3f; mod_HandlingManager.GetHandlingData(HANDLING_LANDSTAL)->Transmission.fEngineAcceleration *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_PATRIOT)->Transmission.fEngineAcceleration *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_BOBCAT)->Transmission.fEngineAcceleration *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_BFINJECT)->Transmission.fEngineAcceleration *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_RANCHER)->Transmission.fEngineAcceleration *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_DESPERAD)->Transmission.fEngineAcceleration *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_DELUXO)->Transmission.fEngineAcceleration *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_BAGGAGE)->Transmission.fEngineAcceleration *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_BAGGAGE)->Transmission.fMaxVelocity *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_BAGGAGE)->Transmission.InitGearRatios(); mod_HandlingManager.GetHandlingData(HANDLING_GOLFCART)->Transmission.fEngineAcceleration *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_GOLFCART)->Transmission.fMaxVelocity *= 2.0f; mod_HandlingManager.GetHandlingData(HANDLING_GOLFCART)->Transmission.InitGearRatios(); mod_HandlingManager.GetHandlingData(HANDLING_STINGER)->fCollisionDamageMultiplier *= 0.25f; mod_HandlingManager.GetHandlingData(HANDLING_STINGER)->fMass *= 2.5f; mod_HandlingManager.GetHandlingData(HANDLING_STINGER)->fTurnMass *= 4.0f; mod_HandlingManager.GetHandlingData(HANDLING_BANSHEE)->fCollisionDamageMultiplier *= 0.25f; mod_HandlingManager.GetHandlingData(HANDLING_BANSHEE)->fMass *= 2.5f; mod_HandlingManager.GetHandlingData(HANDLING_BANSHEE)->fTurnMass *= 4.0f; mod_HandlingManager.GetHandlingData(HANDLING_SABRETUR)->fCollisionDamageMultiplier *= 0.25f; mod_HandlingManager.GetHandlingData(HANDLING_SABRETUR)->fMass *= 2.5f; mod_HandlingManager.GetHandlingData(HANDLING_SABRETUR)->fTurnMass *= 4.0f; mod_HandlingManager.GetHandlingData(HANDLING_COMET)->fCollisionDamageMultiplier *= 0.25f; mod_HandlingManager.GetHandlingData(HANDLING_COMET)->fMass *= 2.5f; mod_HandlingManager.GetHandlingData(HANDLING_COMET)->fTurnMass *= 4.0f; mod_HandlingManager.GetHandlingData(HANDLING_DELUXO)->fCollisionDamageMultiplier *= 0.25f; mod_HandlingManager.GetHandlingData(HANDLING_DELUXO)->fMass *= 2.5f; mod_HandlingManager.GetHandlingData(HANDLING_DELUXO)->fTurnMass *= 4.0f; CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CVehicle::bCheat9 = true; CPad::bHasPlayerCheated = true; } } void PickUpChicksCheat() { if ( FindPlayerVehicle() && (FindPlayerVehicle()->IsCar() || FindPlayerVehicle()->IsBike()) ) { CVehicle *vehicle = FindPlayerVehicle(); if ( FindPlayerVehicle()->IsBike() ) { if ( vehicle->pPassengers[0] ) vehicle->pPassengers[0]->SetObjective(OBJECTIVE_LEAVE_CAR, vehicle); } CPed* someoneElse = (CPed*)CWorld::TestSphereAgainstWorld(vehicle->GetPosition(), 6.0f, FindPlayerPed(), false, false, true, false, false, false); if ( someoneElse && someoneElse->m_nPedState != PED_DRIVING ) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); someoneElse->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, vehicle); } } } void WeaponCheat1() { CHud::SetHelpMessage(TheText.Get("CHEAT2"), true); CStreaming::RequestModel(MI_BRASS_KNUCKLES, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_BASEBALL_BAT, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_MOLOTOV, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_COLT45, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_SHOTGUN, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_TEC9, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_RUGER, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_SNIPERRIFLE, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_FLAMETHROWER, STREAMFLAGS_DONT_REMOVE); CStreaming::LoadAllRequestedModels(false); FindPlayerPed()->GiveWeapon(WEAPONTYPE_BRASSKNUCKLE, 1); FindPlayerPed()->GiveWeapon(WEAPONTYPE_BASEBALLBAT, 1); FindPlayerPed()->GiveWeapon(WEAPONTYPE_MOLOTOV, 10); FindPlayerPed()->GiveWeapon(WEAPONTYPE_COLT45, 100); FindPlayerPed()->GiveWeapon(WEAPONTYPE_SHOTGUN, 50); FindPlayerPed()->GiveWeapon(WEAPONTYPE_TEC9, 150); FindPlayerPed()->GiveWeapon(WEAPONTYPE_RUGER, 120); FindPlayerPed()->GiveWeapon(WEAPONTYPE_SNIPERRIFLE, 25); FindPlayerPed()->GiveWeapon(WEAPONTYPE_FLAMETHROWER, 200); CStreaming::SetModelIsDeletable(MI_BRASS_KNUCKLES); CStreaming::SetModelIsDeletable(MI_BASEBALL_BAT); CStreaming::SetModelIsDeletable(MI_MOLOTOV); CStreaming::SetModelIsDeletable(MI_COLT45); CStreaming::SetModelIsDeletable(MI_SHOTGUN); CStreaming::SetModelIsDeletable(MI_TEC9); CStreaming::SetModelIsDeletable(MI_RUGER); CStreaming::SetModelIsDeletable(MI_SNIPERRIFLE); CStreaming::SetModelIsDeletable(MI_FLAMETHROWER); #ifdef MOBILE_IMPROVEMENTS if (FindPlayerVehicle()) { FindPlayerPed()->RemoveWeaponWhenEnteringVehicle(); } #endif } void WeaponCheat2() { CHud::SetHelpMessage(TheText.Get("CHEAT2"), true); CStreaming::RequestModel(MI_KATANA, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_GRENADE, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_BOMB, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_PYTHON, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_STUBBY_SHOTGUN, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_SILENCEDINGRAM, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_M4, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_LASERSCOPE, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_ROCKETLAUNCHER, STREAMFLAGS_DONT_REMOVE); CStreaming::LoadAllRequestedModels(false); #ifdef FIX_BUGS FindPlayerPed()->GiveWeapon(WEAPONTYPE_KATANA, 1); #else FindPlayerPed()->GiveWeapon(WEAPONTYPE_KATANA, 0); #endif FindPlayerPed()->GiveWeapon(WEAPONTYPE_DETONATOR_GRENADE, 10); FindPlayerPed()->GiveWeapon(WEAPONTYPE_PYTHON, 40); FindPlayerPed()->GiveWeapon(WEAPONTYPE_STUBBY_SHOTGUN, 25); FindPlayerPed()->GiveWeapon(WEAPONTYPE_SILENCED_INGRAM, 100); FindPlayerPed()->GiveWeapon(WEAPONTYPE_M4, 150); FindPlayerPed()->GiveWeapon(WEAPONTYPE_LASERSCOPE, 21); FindPlayerPed()->GiveWeapon(WEAPONTYPE_ROCKETLAUNCHER, 5); CStreaming::SetModelIsDeletable(MI_KATANA); CStreaming::SetModelIsDeletable(MI_GRENADE); CStreaming::SetModelIsDeletable(MI_BOMB); CStreaming::SetModelIsDeletable(MI_PYTHON); CStreaming::SetModelIsDeletable(MI_STUBBY_SHOTGUN); CStreaming::SetModelIsDeletable(MI_SILENCEDINGRAM); CStreaming::SetModelIsDeletable(MI_M4); CStreaming::SetModelIsDeletable(MI_LASERSCOPE); CStreaming::SetModelIsDeletable(MI_ROCKETLAUNCHER); #ifdef MOBILE_IMPROVEMENTS if (FindPlayerVehicle()) { FindPlayerPed()->RemoveWeaponWhenEnteringVehicle(); } #endif } void WeaponCheat3() { CHud::SetHelpMessage(TheText.Get("CHEAT2"), true); CStreaming::RequestModel(MI_CHAINSAW, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_GRENADE, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_PYTHON, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_SPAS12_SHOTGUN, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_MP5, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_M4, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_LASERSCOPE, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_MINIGUN, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_MINIGUN2, STREAMFLAGS_DONT_REMOVE); CStreaming::LoadAllRequestedModels(false); #ifdef FIX_BUGS FindPlayerPed()->GiveWeapon(WEAPONTYPE_CHAINSAW, 1); #else FindPlayerPed()->GiveWeapon(WEAPONTYPE_CHAINSAW, 0); #endif FindPlayerPed()->GiveWeapon(WEAPONTYPE_GRENADE, 10); FindPlayerPed()->GiveWeapon(WEAPONTYPE_PYTHON, 40); FindPlayerPed()->GiveWeapon(WEAPONTYPE_SPAS12_SHOTGUN, 30); FindPlayerPed()->GiveWeapon(WEAPONTYPE_MP5, 100); FindPlayerPed()->GiveWeapon(WEAPONTYPE_M4, 150); FindPlayerPed()->GiveWeapon(WEAPONTYPE_LASERSCOPE, 21); FindPlayerPed()->GiveWeapon(WEAPONTYPE_MINIGUN, 500); CStreaming::SetModelIsDeletable(MI_CHAINSAW); CStreaming::SetModelIsDeletable(MI_GRENADE); CStreaming::SetModelIsDeletable(MI_PYTHON); CStreaming::SetModelIsDeletable(MI_SPAS12_SHOTGUN); CStreaming::SetModelIsDeletable(MI_MP5); CStreaming::SetModelIsDeletable(MI_M4); CStreaming::SetModelIsDeletable(MI_LASERSCOPE); CStreaming::SetModelIsDeletable(MI_MINIGUN); CStreaming::SetModelIsDeletable(MI_MINIGUN2); #ifdef MOBILE_IMPROVEMENTS if (FindPlayerVehicle()) { FindPlayerPed()->RemoveWeaponWhenEnteringVehicle(); } #endif } void HealthCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT3"), true); FindPlayerPed()->m_fHealth = CWorld::Players[0].m_nMaxHealth; if (FindPlayerVehicle()) { FindPlayerVehicle()->m_fHealth = 1000.0f; if (FindPlayerVehicle()->m_vehType == VEHICLE_TYPE_CAR) { ((CAutomobile*)FindPlayerVehicle())->Damage.SetEngineStatus(0); for (int32 i = 0; i < 4; i++) ((CAutomobile*)FindPlayerVehicle())->Damage.SetWheelStatus(i, WHEEL_STATUS_OK); } } } // TODO(Miami): this is HELLA different on mobile, although it mostly has debug oriented things like player exiting it's current car and enters spawned one etc. void VehicleCheat(int model) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CStreaming::RequestModel(model, STREAMFLAGS_DONT_REMOVE); CStreaming::LoadAllRequestedModels(false); if (CStreaming::ms_aInfoForModel[model].m_loadState == STREAMSTATE_LOADED) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); if (!(CStreaming::ms_aInfoForModel[model].m_loadState & STREAMFLAGS_DONT_REMOVE)) { CStreaming::SetModelIsDeletable(model); CStreaming::SetModelTxdIsDeletable(model); } int32 node = ThePaths.FindNodeClosestToCoors(FindPlayerCoors(), PATH_CAR, 100.0f); if (node < 0) return; #ifdef FIX_BUGS CAutomobile* vehicle = new CAutomobile(model, RANDOM_VEHICLE); #else CAutomobile* vehicle = new CAutomobile(model, MISSION_VEHICLE); #endif if (vehicle != nil) { CVector pos = ThePaths.m_pathNodes[node].GetPosition(); pos.z += 4.0f; vehicle->SetPosition(pos); vehicle->SetOrientation(0.0f, 0.0f, DEGTORAD(200.0f)); vehicle->SetStatus(STATUS_ABANDONED); vehicle->m_nDoorLock = CARLOCK_UNLOCKED; CWorld::Add(vehicle); } } CStats::CheatedCount += 1000; CPad::bHasPlayerCheated = true; } void BlowUpCarsCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); int i = CPools::GetVehiclePool()->GetSize(); while (i-- > 0) { if (CVehicle *veh = CPools::GetVehiclePool()->GetSlot(i)) veh->BlowUpCar(nil); } } void ChangePlayerCheat() { // I don't know wtf is going on in here... int modelId, anotherModelId; if (FindPlayerPed()->IsPedInControl() && CModelInfo::GetModelInfo("player", nil)) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CPlayerPed *ped = FindPlayerPed(); AssocGroupId AnimGrp = ped->m_animGroup; do { do { modelId = CGeneral::GetRandomNumberInRange(0, MI_PGA); anotherModelId = modelId+1; } while (!CModelInfo::GetModelInfo(anotherModelId)); } while (anotherModelId >= MI_SPECIAL01 && anotherModelId <= MI_SPECIAL04 || modelId == MI_TAXI_D); uint8 flags = CStreaming::ms_aInfoForModel[anotherModelId].m_flags; ped->DeleteRwObject(); CStreaming::RequestModel(anotherModelId, STREAMFLAGS_DEPENDENCY| STREAMFLAGS_DONT_REMOVE); CStreaming::LoadAllRequestedModels(false); ped->m_modelIndex = -1; ped->SetModelIndex(anotherModelId); ped->m_animGroup = AnimGrp; if (modelId != -1) { if (!(flags & STREAMFLAGS_DONT_REMOVE)) CStreaming::SetModelIsDeletable(anotherModelId); } } } void ChangePlayerModel(const char* name) { if (!FindPlayerVehicle()) { FindPlayerPed()->Undress(name); CStreaming::LoadAllRequestedModels(0); FindPlayerPed()->Dress(); } } void MayhemCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); for (int i = PEDTYPE_CIVMALE; i < PEDTYPE_SPECIAL; i++) CPedType::SetThreats(i, PED_FLAG_PLAYER1 | PED_FLAG_PLAYER2 | PED_FLAG_PLAYER3 | PED_FLAG_PLAYER4 | PED_FLAG_CIVMALE | PED_FLAG_CIVFEMALE | PED_FLAG_COP | PED_FLAG_GANG1 | PED_FLAG_GANG2 | PED_FLAG_GANG3 | PED_FLAG_GANG4 | PED_FLAG_GANG5 | PED_FLAG_GANG6 | PED_FLAG_GANG7 | PED_FLAG_GANG8 | PED_FLAG_GANG9 | PED_FLAG_EMERGENCY | PED_FLAG_PROSTITUTE | PED_FLAG_CRIMINAL | PED_FLAG_SPECIAL ); CStats::CheatedCount += 1000; CPad::bHasPlayerCheated = true; } void EverybodyAttacksPlayerCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); for (int i = PEDTYPE_CIVMALE; i < PEDTYPE_SPECIAL; i++) CPedType::AddThreat(i, PED_FLAG_PLAYER1); CStats::CheatedCount += 1000; CPad::bHasPlayerCheated = true; } void WeaponsForAllCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CPopulation::ms_bGivePedsWeapons = !CPopulation::ms_bGivePedsWeapons; CStats::CheatedCount += 1000; CPad::bHasPlayerCheated = true; } void FastTimeCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); if (CTimer::GetTimeScale() < 4.0f) CTimer::SetTimeScale(CTimer::GetTimeScale() * 2.0f); } void SlowTimeCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); if (CTimer::GetTimeScale() > 0.25f) CTimer::SetTimeScale(CTimer::GetTimeScale() * 0.5f); } void MoneyCheat() { CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 250000; CHud::SetHelpMessage(TheText.Get("CHEAT6"), true); } void ArmourCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT4"), true); FindPlayerPed()->m_fArmour = CWorld::Players[0].m_nMaxArmour; } void WantedLevelUpCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT5"), true); FindPlayerPed()->m_pWanted->CheatWantedLevel(Min(FindPlayerPed()->m_pWanted->GetWantedLevel() + 2, 6)); } void WantedLevelDownCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT5"), true); FindPlayerPed()->m_pWanted->CheatWantedLevel(0); } void SunnyWeatherCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); CWeather::ForceWeatherNow(WEATHER_SUNNY); } void ExtraSunnyWeatherCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); CWeather::ForceWeatherNow(WEATHER_EXTRA_SUNNY); } void CloudyWeatherCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); CWeather::ForceWeatherNow(WEATHER_CLOUDY); } void RainyWeatherCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); CWeather::ForceWeatherNow(WEATHER_RAINY); } void FoggyWeatherCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT7"), true); CWeather::ForceWeatherNow(WEATHER_FOGGY); } void FastWeatherCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); gbFastTime = !gbFastTime; } void OnlyRenderWheelsCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CVehicle::bWheelsOnlyCheat = !CVehicle::bWheelsOnlyCheat; CStats::CheatedCount += 1000; CPad::bHasPlayerCheated = true; } void ChittyChittyBangBangCheat() { #ifdef BETTER_ALLCARSAREDODO_CHEAT CHud::SetHelpMessage(TheText.Get(!CVehicle::bAllDodosCheat ? "CHEAT1" : "CHEATOF"), true); #else CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); #endif CVehicle::bAllDodosCheat = !CVehicle::bAllDodosCheat; CStats::CheatedCount += 1000; CPad::bHasPlayerCheated = true; } void StrongGripCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CVehicle::bCheat3 = !CVehicle::bCheat3; CStats::CheatedCount += 1000; CPad::bHasPlayerCheated = true; } void FannyMagnetCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CPed::bFannyMagnetCheat = !CPed::bFannyMagnetCheat; CPad::bHasPlayerCheated = true; } void BlackCarsCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); gbBlackCars = true; gbPinkCars = false; } void PinkCarsCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); gbBlackCars = false; gbPinkCars = true; } void TrafficLightsCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CTrafficLights::bGreenLightsCheat = true; } void MadCarsCheat() { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CCarCtrl::bMadDriversCheat = true; } void NoSeaBedCheat(void) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CWaterLevel::m_bRenderSeaBed = !CWaterLevel::m_bRenderSeaBed; } void RenderWaterLayersCheat(void) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); if ( ++CWaterLevel::m_nRenderWaterLayers > 5 ) CWaterLevel::m_nRenderWaterLayers = 0; } void BackToTheFuture(void) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CVehicle::bHoverCheat = !CVehicle::bHoverCheat; CPad::bHasPlayerCheated = true; } void SuicideCheat(void) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); FindPlayerPed()->InflictDamage(nil, WEAPONTYPE_UNARMED, 1000.0f, PEDPIECE_TORSO, 0); } void DoChicksWithGunsCheat(void) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CStreaming::SetModelIsDeletable(CGangs::GetGangPedModel1(GANG_PLAYER)); CStreaming::SetModelIsDeletable(CGangs::GetGangPedModel2(GANG_PLAYER)); CStreaming::SetModelTxdIsDeletable(CGangs::GetGangPedModel1(GANG_PLAYER)); CStreaming::SetModelTxdIsDeletable(CGangs::GetGangPedModel2(GANG_PLAYER)); CStreaming::RemoveCurrentZonesModels(); CGangs::SetGangPedModels(GANG_PLAYER, MI_HFYBE, MI_WFYBE); CGangs::SetGangWeapons(GANG_PLAYER, WEAPONTYPE_M4, WEAPONTYPE_M4); CStats::CheatedCount += 1000; CPad::bHasPlayerCheated = true; } ////////////////////////////////////////////////////////////////////////// #ifdef KANGAROO_CHEAT void KangarooCheat() { wchar *string; CPed *playerPed = FindPlayerPed(); int m_fMass; if (playerPed->m_ped_flagI80) { string = TheText.Get("CHEATOF"); m_fMass = 70.0f; } else { string = TheText.Get("CHEAT1"); m_fMass = 15.0f; } CHud::SetHelpMessage(string, true); playerPed->m_ped_flagI80 = !playerPed->m_ped_flagI80; playerPed->m_fMass = m_fMass; playerPed->m_fAirResistance = 0.4f / m_fMass; } #endif #ifdef RESTORE_ALLCARSHELI_CHEAT void AllCarsHeliCheat(void) { wchar* string; if (bAllCarCheat) { string = TheText.Get("CHEATOF"); bAllCarCheat = false; } else { string = TheText.Get("CHEAT1"); bAllCarCheat = true; } CHud::SetHelpMessage(string, true); } #endif #ifdef WALLCLIMB_CHEAT void WallClimbingCheat(void) { wchar* string; if (gGravityCheat) { string = TheText.Get("CHEATOF"); gGravityCheat = false; } else { string = TheText.Get("CHEAT1"); gGravityCheat = true; } CHud::SetHelpMessage(string, true); } #endif void FlyingFishCheat(void) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CVehicle::bCheat8 = !CVehicle::bCheat8; CPad::bHasPlayerCheated = true; } void DoShowChaseStatCheat(void) { CHud::SetHelpMessage(TheText.Get("CHEAT1"), true); CStats::ShowChaseStatOnScreen = 1; } bool CControllerState::CheckForInput(void) { return !!RightStickX || !!RightStickY || !!LeftStickX || !!LeftStickY || !!DPadUp || !!DPadDown || !!DPadLeft || !!DPadRight || !!Triangle || !!Cross || !!Circle || !!Square || !!Start || !!Select || !!LeftShoulder1 || !!LeftShoulder2 || !!RightShoulder1 || !!RightShoulder2 || !!LeftShock || !!RightShock; } void CControllerState::Clear(void) { LeftStickX = LeftStickY = RightStickX = RightStickY = 0; LeftShoulder1 = LeftShoulder2 = RightShoulder1 = RightShoulder2 = 0; DPadUp = DPadDown = DPadLeft = DPadRight = 0; Start = Select = 0; Square = Triangle = Cross = Circle = 0; LeftShock = RightShock = 0; NetworkTalk = 0; } void CKeyboardState::Clear() { for ( int32 i = 0; i < ARRAY_SIZE(F); i++ ) F[i] = 0; for ( int32 i = 0; i < ARRAY_SIZE(VK_KEYS); i++ ) VK_KEYS[i] = 0; ESC = INS = DEL = HOME = END = PGUP = PGDN = 0; UP = DOWN = LEFT = RIGHT = 0; NUMLOCK = 0; DIV = MUL = SUB = ADD = 0; DECIMAL = NUM1 = NUM2 = NUM3 = NUM4 = 0; NUM5 = NUM6 = NUM7 = NUM8 = 0; NUM9 = NUM0 = SCROLLLOCK = PAUSE = 0; BACKSP = TAB = CAPSLOCK = EXTENTER = 0; LSHIFT = SHIFT = RSHIFT = LCTRL = RCTRL = LALT = RALT = 0; LWIN = RWIN = APPS = 0; } #ifdef GTA_PS2_STUFF void CPad::Initialise(void) { #ifdef GTA_PS2 scePadInit(0); scePadPortOpen(0, 0, pad_dma_buf ); scePadPortOpen(1, 0, pad2_dma_buf ); #endif for (int i = 0; i < MAX_PADS; i++) { CPad::GetPad(i)->Clear(true); CPad::GetPad(i)->Mode = 0; } bObsoleteControllerMessage = false; bOldDisplayNoControllerMessage = false; bDisplayNoControllerMessage = false; m_bMapPadOneToPadTwo = false; m_bDebugCamPCOn = false; } #endif void CPad::Clear(bool bResetPlayerControls) { NewState.Clear(); OldState.Clear(); PCTempKeyState.Clear(); PCTempJoyState.Clear(); PCTempMouseState.Clear(); NewKeyState.Clear(); OldKeyState.Clear(); TempKeyState.Clear(); NewMouseControllerState.Clear(); OldMouseControllerState.Clear(); PCTempMouseControllerState.Clear(); Phase = 0; ShakeFreq = 0; ShakeDur = 0; for (int32 i = 0; i < DRUNK_STEERING_BUFFER_SIZE; i++) SteeringLeftRightBuffer[i] = 0; DrunkDrivingBufferUsed = 0; if ( bResetPlayerControls ) DisablePlayerControls = PLAYERCONTROL_ENABLED; JustOutOfFrontend = 0; bApplyBrakes = false; for ( int32 i = 0; i < HORNHISTORY_SIZE; i++ ) bHornHistory[i] = false; iCurrHornHistory = 0; for ( int32 i = 0; i < ARRAY_SIZE(CheatString); i++ ) CheatString[i] = ' '; LastTimeTouched = CTimer::GetTimeInMilliseconds(); AverageWeapon = 0; AverageEntries = 0; } uint32 CPad::InputHowLongAgo() { return CTimer::GetTimeInMilliseconds() - LastTimeTouched; } void CPad::ClearMouseHistory() { PCTempMouseControllerState.Clear(); NewMouseControllerState.Clear(); OldMouseControllerState.Clear(); } // unused void CPad::ClearKeyBoardHistory() { NewKeyState.Clear(); OldKeyState.Clear(); TempKeyState.Clear(); } CMouseControllerState::CMouseControllerState() { LMB = 0; RMB = 0; MMB = 0; WHEELUP = 0; WHEELDN = 0; MXB1 = 0; MXB2 = 0; x = 0.0f; y = 0.0f; } void CMouseControllerState::Clear() { LMB = 0; RMB = 0; MMB = 0; WHEELUP = 0; WHEELDN = 0; MXB1 = 0; MXB2 = 0; } CMouseControllerState CMousePointerStateHelper::GetMouseSetUp() { CMouseControllerState state; #if defined RW_D3D9 || defined RWLIBS if ( PSGLOBAL(mouse) == nil ) _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); if ( PSGLOBAL(mouse) != nil ) { DIDEVCAPS devCaps; devCaps.dwSize = sizeof(DIDEVCAPS); PSGLOBAL(mouse)->GetCapabilities(&devCaps); switch ( devCaps.dwButtons ) { case 3: case 4: case 5: case 6: case 7: case 8: state.MMB = true; case 2: state.RMB = true; case 1: state.LMB = true; } if ( devCaps.dwAxes == 3 ) { state.WHEELDN = true; state.WHEELUP = true; } } #else // It seems there is no way to get number of buttons on mouse, so assign all buttons if we have mouse. double xpos = 1.0f, ypos; glfwGetCursorPos(PSGLOBAL(window), &xpos, &ypos); if (xpos != 0.f) { state.MMB = true; state.RMB = true; state.LMB = true; state.WHEELDN = true; state.WHEELUP = true; } #endif return state; } void CPad::UpdateMouse() { #if defined RW_D3D9 || defined RWLIBS if ( IsForegroundApp() ) { if ( PSGLOBAL(mouse) == nil ) _InputInitialiseMouse(!FrontEndMenuManager.m_bMenuActive && _InputMouseNeedsExclusive()); DIMOUSESTATE2 state; if ( PSGLOBAL(mouse) != nil && SUCCEEDED(_InputGetMouseState(&state)) ) { int32 signX = 1; int32 signy = 1; if ( !FrontEndMenuManager.m_bMenuActive ) { if ( MousePointerStateHelper.bInvertVertically ) signy = -1; if ( MousePointerStateHelper.bInvertHorizontally ) signX = -1; } PCTempMouseControllerState.Clear(); PCTempMouseControllerState.x = (float)(signX * state.lX); PCTempMouseControllerState.y = (float)(signy * state.lY); PCTempMouseControllerState.LMB = state.rgbButtons[0] & 128; PCTempMouseControllerState.RMB = state.rgbButtons[1] & 128; PCTempMouseControllerState.MMB = state.rgbButtons[2] & 128; PCTempMouseControllerState.MXB1 = state.rgbButtons[3] & 128; PCTempMouseControllerState.MXB2 = state.rgbButtons[4] & 128; if ( state.lZ > 0 ) PCTempMouseControllerState.WHEELUP = 1; else if ( state.lZ < 0 ) PCTempMouseControllerState.WHEELDN = 1; OldMouseControllerState = NewMouseControllerState; NewMouseControllerState = PCTempMouseControllerState; } } #else if ( IsForegroundApp() && PSGLOBAL(cursorIsInWindow) ) { double xpos = 1.0f, ypos; glfwGetCursorPos(PSGLOBAL(window), &xpos, &ypos); if (xpos == 0.f) return; int32 signX = 1; int32 signy = 1; if (!FrontEndMenuManager.m_bMenuActive) { if (MousePointerStateHelper.bInvertVertically) signy = -1; if (MousePointerStateHelper.bInvertHorizontally) signX = -1; } PCTempMouseControllerState.Clear(); PCTempMouseControllerState.x = (float)(signX * (xpos - PSGLOBAL(lastMousePos.x))); PCTempMouseControllerState.y = (float)(signy * (ypos - PSGLOBAL(lastMousePos.y))); PCTempMouseControllerState.LMB = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_LEFT); PCTempMouseControllerState.RMB = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_RIGHT); PCTempMouseControllerState.MMB = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_MIDDLE); PCTempMouseControllerState.MXB1 = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_4); PCTempMouseControllerState.MXB2 = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_5); if (PSGLOBAL(mouseWheel) > 0) PCTempMouseControllerState.WHEELUP = 1; else if (PSGLOBAL(mouseWheel) < 0) PCTempMouseControllerState.WHEELDN = 1; PSGLOBAL(lastMousePos.x) = xpos; PSGLOBAL(lastMousePos.y) = ypos; PSGLOBAL(mouseWheel) = 0.0f; OldMouseControllerState = NewMouseControllerState; NewMouseControllerState = PCTempMouseControllerState; } #endif } CControllerState CPad::ReconcileTwoControllersInput(CControllerState const &State1, CControllerState const &State2) { static CControllerState ReconState; ReconState.Clear(); #define _RECONCILE_BUTTON(button) \ { 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); } #define _RECONCILE_AXIS_NEGATIVE(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); } #define _FIX_AXIS_DIR(axis) \ { if ( State1.axis > 0 && State2.axis < 0 || State1.axis < 0 && State2.axis > 0 ) ReconState.axis = 0; } #define _FIX_RECON_DIR(pos, neg, axis) \ { if ( (ReconState.pos || ReconState.axis < 0) && (ReconState.neg || ReconState.axis > 0) ) { ReconState.pos = 0; ReconState.neg = 0; ReconState.axis = 0; } } _RECONCILE_BUTTON(LeftShoulder1); _RECONCILE_BUTTON(LeftShoulder2); _RECONCILE_BUTTON(RightShoulder1); _RECONCILE_BUTTON(RightShoulder2); _RECONCILE_BUTTON(Start); _RECONCILE_BUTTON(Select); _RECONCILE_BUTTON(Square); _RECONCILE_BUTTON(Triangle); _RECONCILE_BUTTON(Cross); _RECONCILE_BUTTON(Circle); _RECONCILE_BUTTON(LeftShock); _RECONCILE_BUTTON(RightShock); _RECONCILE_BUTTON(NetworkTalk); _RECONCILE_AXIS(LeftStickX); _RECONCILE_AXIS(LeftStickY); _FIX_AXIS_DIR(LeftStickX); _FIX_AXIS_DIR(LeftStickY); _RECONCILE_AXIS(RightStickX); _RECONCILE_AXIS(RightStickY); _FIX_AXIS_DIR(RightStickX); _FIX_AXIS_DIR(RightStickY); _RECONCILE_BUTTON(DPadUp); _RECONCILE_BUTTON(DPadDown); _RECONCILE_BUTTON(DPadLeft); _RECONCILE_BUTTON(DPadRight); _FIX_RECON_DIR(DPadUp, DPadDown, LeftStickY); _FIX_RECON_DIR(DPadLeft, DPadRight, LeftStickX); return ReconState; #undef _RECONCILE_BUTTON #undef _RECONCILE_AXIS_POSITIVE #undef _RECONCILE_AXIS_NEGATIVE #undef _RECONCILE_AXIS #undef _FIX_AXIS_DIR #undef _FIX_RECON_DIR } void CPad::StartShake(int16 nDur, uint8 nFreq) { if ( !FrontEndMenuManager.m_PrefsUseVibration ) return; if ( CCutsceneMgr::IsRunning() || CGame::playingIntro ) return; if ( nFreq == 0 ) { ShakeDur = 0; ShakeFreq = 0; return; } if ( nDur > ShakeDur ) { ShakeDur = nDur; ShakeFreq = nFreq; } } void CPad::StartShake_Distance(int16 nDur, uint8 nFreq, float fX, float fY, float fZ) { if ( !FrontEndMenuManager.m_PrefsUseVibration ) return; if ( CCutsceneMgr::IsRunning() || CGame::playingIntro ) return; float fDist = ( TheCamera.GetPosition() - CVector(fX, fY, fZ) ).Magnitude(); if ( fDist < 70.0f ) { if ( nFreq == 0 ) { ShakeDur = 0; ShakeFreq = 0; return; } if ( nDur > ShakeDur ) { ShakeDur = nDur; ShakeFreq = nFreq; } } } void CPad::StartShake_Train(float fX, float fY) { if ( !FrontEndMenuManager.m_PrefsUseVibration ) return; if ( CCutsceneMgr::IsRunning() || CGame::playingIntro ) return; if (FindPlayerVehicle() != nil && FindPlayerVehicle()->IsTrain() ) return; float fDist = ( TheCamera.GetPosition() - CVector(fX, fY, 0.0f) ).Magnitude2D(); if ( fDist < 70.0f ) { int32 freq = (int32)((70.0f - fDist) * 70.0f / 70.0f + 30.0f); if ( ShakeDur < 100 ) { ShakeDur = 100; ShakeFreq = freq; } } } #ifdef GTA_PS2_STUFF void CPad::AddToCheatString(char c) { for ( int32 i = ARRAY_SIZE(CheatString) - 2; i >= 0; i-- ) CheatString[i + 1] = CheatString[i]; CheatString[0] = c; #define _CHEATCMP(str) strncmp(str, CheatString, sizeof(str)-1) // "4414LDRULDRU" - R2 R2 L1 R2 LEFT DOWN RIGHT UP LEFT DOWN RIGHT UP if ( !_CHEATCMP("URDLURDL4144") ) WeaponCheat1(); // "4411LDRULDRU" - R2 R2 L1 L1 LEFT DOWN RIGHT UP LEFT DOWN RIGHT UP else if ( !_CHEATCMP("URDLURDL1144") ) MoneyCheat(); // "4412LDRULDRU" - R2 R2 L1 L2 LEFT DOWN RIGHT UP LEFT DOWN RIGHT UP else if ( !_CHEATCMP("URDLURDL2144") ) ArmourCheat(); // "4413LDRULDRU" - R2 R2 L1 R1 LEFT DOWN RIGHT UP LEFT DOWN RIGHT UP else if ( !_CHEATCMP("URDLURDL3144") ) HealthCheat(); // "4414LRLRLR" - R2 R2 L1 R2 LEFT RIGHT LEFT RIGHT LEFT RIGHT else if ( !_CHEATCMP("RLRLRL4144") ) WantedLevelUpCheat(); // "4414UDUDUD" - R2 R2 L1 R2 UP DOWN UP DOWN UP DOWN else if ( !_CHEATCMP("DUDUDU4144") ) WantedLevelDownCheat(); // "1234432T" - L1 L2 R1 R2 R2 R1 L2 TRIANGLE else if ( !_CHEATCMP("T2344321") ) SunnyWeatherCheat(); // "1234432S" - L1 L2 R1 R2 R2 R1 L2 SQUARE else if ( !_CHEATCMP("S2344321") ) CloudyWeatherCheat(); // "1234432C" - L1 L2 R1 R2 R2 R1 L2 CIRCLE else if ( !_CHEATCMP("C2344321") ) RainyWeatherCheat(); // "1234432X" - L1 L2 R1 R2 R2 R1 L2 CROSS else if ( !_CHEATCMP("X2344321") ) FoggyWeatherCheat(); // "CCCCCC321TCT" - CIRCLE CIRCLE CIRCLE CIRCLE CIRCLE CIRCLE R1 L2 L1 TRIANGLE CIRCLE TRIANGLE else if ( !_CHEATCMP("TCT123CCCCCC") ) VehicleCheat(MI_RHINO); // "CCCSSSSS1TCT" - CIRCLE CIRCLE CIRCLE SQUARE SQUARE SQUARE SQUARE SQUARE L1 TRIANGLE CIRCLE TRIANGLE else if ( !_CHEATCMP("TCT1SSSSSCCC") ) FastWeatherCheat(); // "241324TSCT21" - L2 R2 L1 R1 L2 R2 TRIANGLE SQUARE CIRCLE TRIANGLE L2 L1 else if ( !_CHEATCMP("12TCST423142") ) BlowUpCarsCheat(); // "RDLU12ULDR" - RIGHT DOWN LEFT UP L1 L2 UP LEFT DOWN RIGHT else if ( !_CHEATCMP("RDLU21ULDR") ) ChangePlayerCheat(); // "DULUX3421" - DOWN UP LEFT UP CROSS R1 R2 L2 L1 else if ( !_CHEATCMP("1243XULUD") ) MayhemCheat(); // "DULUX3412" - DOWN UP LEFT UP CROSS R1 R2 L1 L2 else if ( !_CHEATCMP("2143XULUD") ) EverybodyAttacksPlayerCheat(); // "43TX21UD" - R2 R1 TRIANGLE CROSS L2 L1 UP DOWN else if ( !_CHEATCMP("DU12XT34") ) WeaponsForAllCheat(); // "TURDS12" - TRIANGLE UP RIGHT DOWN SQUARE L1 L2 else if ( !_CHEATCMP("21SDRUT") ) FastTimeCheat(); // "TURDS34" - TRIANGLE UP RIGHT DOWN SQUARE R1 R2 else if ( !_CHEATCMP("43SDRUT") ) SlowTimeCheat(); // "11S4T1T" - L1 L1 SQUARE R2 TRIANGLE L1 TRIANGLE else if ( !_CHEATCMP("T1T4S11") ) OnlyRenderWheelsCheat(); // "R4C32D13" - RIGHT R2 CIRCLE R1 L2 DOWN L1 R1 else if ( !_CHEATCMP("31D23C4R") ) ChittyChittyBangBangCheat(); // "3141L33T" - R1 L1 R2 L1 LEFT R1 R1 TRIANGLE else if ( !_CHEATCMP("T33L1413") ) StrongGripCheat(); #undef _CHEATCMP } #endif int Cheat_strncmp(char* sourceStr, char* origCheatStr) { #define ccmp(n) if((uint8)sourceStr[i] != (uint8)origCheatStr[i] - n) return 1; int i = 0; while(origCheatStr[i]) { switch(i) { case 0: ccmp(3); break; case 1: ccmp(5); break; case 2: ccmp(7); break; case 3: ccmp(1); break; case 4: ccmp(13); break; case 5: ccmp(27); break; case 6: ccmp(3); break; case 7: ccmp(7); break; case 8: ccmp(1); break; case 9: ccmp(11); break; case 10: ccmp(13); break; case 11: ccmp(8); break; case 12: ccmp(7); break; case 13: ccmp(32); break; case 14: ccmp(13); break; case 15: ccmp(6); break; case 16: ccmp(28); break; case 17: ccmp(19); break; case 18: ccmp(10); break; case 19: ccmp(3); break; case 20: ccmp(3); break; case 21: ccmp(5); break; case 22: ccmp(7); break; case 23: ccmp(1); break; case 24: ccmp(13); break; case 25: ccmp(27); break; case 26: ccmp(3); break; case 27: ccmp(7); break; default: return 1; } i++; } return 0; #undef ccmp } // TODO(Miami): Mobile has changed some of the cheats to include debugging things void CPad::AddToPCCheatString(char c) { for (int32 i = ARRAY_SIZE(KeyBoardCheatString) - 2; i >= 0; i--) KeyBoardCheatString[i + 1] = KeyBoardCheatString[i]; KeyBoardCheatString[0] = c; #define _CHEATCMP(str) strncmp(str, KeyBoardCheatString, sizeof(str)-1) // "THUGSTOOLS" if (!Cheat_strncmp(KeyBoardCheatString, "VQVPanJ\\I_")) { KeyBoardCheatString[0] = ' '; WeaponCheat1(); } // "PROFESSIONALTOOLS" else if (!Cheat_strncmp(KeyBoardCheatString, "VQVPagDUPT`[Lf\\Xl")) { KeyBoardCheatString[0] = ' '; WeaponCheat2(); } // "NUTTERTOOLS" else if (!Cheat_strncmp(KeyBoardCheatString, "VQVPamH[U`[")) { KeyBoardCheatString[0] = ' '; WeaponCheat3(); } // "PRECIOUSPROTECTION" else if (!Cheat_strncmp(KeyBoardCheatString, "QTPUP`WVS[`]ViPKnc")) { KeyBoardCheatString[0] = ' '; ArmourCheat(); } // "ASPIRINE" else if (!Cheat_strncmp(KeyBoardCheatString, "HSPSVkVH")) { KeyBoardCheatString[0] = ' '; HealthCheat(); } // "YOUWONTTAKEMEALIVE" else if (!Cheat_strncmp(KeyBoardCheatString, "H[PMN`PLLLa\\Uod[kl")) { KeyBoardCheatString[0] = ' '; WantedLevelUpCheat(); } // "LEAVEMEALONE" else if (!Cheat_strncmp(KeyBoardCheatString, "HSVMN`PLWLRT")) { KeyBoardCheatString[0] = ' '; WantedLevelDownCheat(); } // "APLEASANTDAY" else if (!Cheat_strncmp(KeyBoardCheatString, "\\FKU[\\VHFW]I")) { KeyBoardCheatString[0] = ' '; SunnyWeatherCheat(); } // "ALOVELYDAY" else if (!Cheat_strncmp(KeyBoardCheatString, "\\FKZY`YVML")) { KeyBoardCheatString[0] = ' '; ExtraSunnyWeatherCheat(); } // "ABITDRIEG" else if (!Cheat_strncmp(KeyBoardCheatString, "JJPSQoLIB")) { KeyBoardCheatString[0] = ' '; CloudyWeatherCheat(); } // "CATSANDDOGS" else if (!Cheat_strncmp(KeyBoardCheatString, "VLVEQiDZULP")) { KeyBoardCheatString[0] = ' '; RainyWeatherCheat(); } // "CANTSEEATHING" else if (!Cheat_strncmp(KeyBoardCheatString, "JSPIa\\HLT_[IJ")) { KeyBoardCheatString[0] = ' '; FoggyWeatherCheat(); } // "PANZER" else if (!Cheat_strncmp(KeyBoardCheatString, "UJaONk")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_RHINO); } // "LIFEISPASSINGMEBY" else if (!Cheat_strncmp(KeyBoardCheatString, "\\GLNTiLZTL][PeSOh")) { KeyBoardCheatString[0] = ' '; FastWeatherCheat(); } // "BIGBANG" else if (!Cheat_strncmp(KeyBoardCheatString, "JSHCTdE")) { KeyBoardCheatString[0] = ' '; BlowUpCarsCheat(); } // "STILLLIKEDRESSINGUP" else if (!Cheat_strncmp(KeyBoardCheatString, "SZNOVnVLSORSPlYReg]")) { KeyBoardCheatString[0] = ' '; ChangePlayerCheat(); } // "FIGHTFIGHTFIGHT" else if (!Cheat_strncmp(KeyBoardCheatString, "WMNJSoKNJQaPNiS")) { KeyBoardCheatString[0] = ' '; MayhemCheat(); } // "NOBODYLIKESME" else if (!Cheat_strncmp(KeyBoardCheatString, "HRZFXdO`EZOWU")) { KeyBoardCheatString[0] = ' '; EverybodyAttacksPlayerCheat(); } // "OURGODGIVENRIGHTTOBEARARMS" else if (!Cheat_strncmp(KeyBoardCheatString, "VRYB_\\HIP_aPNi_TaiSJGTNSbj")) { KeyBoardCheatString[0] = ' '; WeaponsForAllCheat(); } // "ONSPEED" else if (!Cheat_strncmp(KeyBoardCheatString, "GJLQ`iR")) { KeyBoardCheatString[0] = ' '; FastTimeCheat(); } // "BOOOOOORING" else if (!Cheat_strncmp(KeyBoardCheatString, "JSPS\\jRVPZO")) { KeyBoardCheatString[0] = ' '; SlowTimeCheat(); } // "WHEELSAREALLINEED" else if (!Cheat_strncmp(KeyBoardCheatString, "GJLOVgOHF]N[SeRNs")) { KeyBoardCheatString[0] = ' '; OnlyRenderWheelsCheat(); } //COMEFLYWITHME else if (!Cheat_strncmp(KeyBoardCheatString, "HROUVr\\SGPZWJ")) { KeyBoardCheatString[0] = ' '; ChittyChittyBangBangCheat(); } // "GRIPISEVERYTHING" else if (!Cheat_strncmp(KeyBoardCheatString, "JSPIatULWP`QWi_M")) { KeyBoardCheatString[0] = ' '; StrongGripCheat(); } // "CHASESTAT" else if (!Cheat_strncmp(KeyBoardCheatString, "WF[TRnDOD")) { KeyBoardCheatString[0] = ' '; DoShowChaseStatCheat(); } // "CHICKSWITHGUNS" else if (!Cheat_strncmp(KeyBoardCheatString, "VS\\HUoL^TVPQOc")) { KeyBoardCheatString[0] = ' '; DoChicksWithGunsCheat(); } // "ICANTTAKEITANYMORE" else if (!Cheat_strncmp(KeyBoardCheatString, "HWVNfiD[JPXI[t[G_\\")) { KeyBoardCheatString[0] = ' '; SuicideCheat(); } // "GREENLIGHT" else if (!Cheat_strncmp(KeyBoardCheatString, "WMNJYiHLSR")) { KeyBoardCheatString[0] = ' '; TrafficLightsCheat(); } // "MIAMITRAFFIC" else if (!Cheat_strncmp(KeyBoardCheatString, "FNMGNmWPNLVU")) { KeyBoardCheatString[0] = ' '; MadCarsCheat(); } // "AHAIRDRESSERSCAR" else if (!Cheat_strncmp(KeyBoardCheatString, "UFJT_`VZF]QZPaUG")) { KeyBoardCheatString[0] = ' '; PinkCarsCheat(); } // "IWANTITPAINTEDBLACK" else if (!Cheat_strncmp(KeyBoardCheatString, "NHHMO_H[OTNX[iaT]jS")) { KeyBoardCheatString[0] = ' '; BlackCarsCheat(); } // "TRAVELINSTYLE" else if (!Cheat_strncmp(KeyBoardCheatString, "HQ`U`iLSFaNZ[")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_BLOODRA); } // "THELASTRIDE" else if (!Cheat_strncmp(KeyBoardCheatString, "HIPSanDSFSa")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_ROMERO); } // "ROCKANDROLLCAR" else if (!Cheat_strncmp(KeyBoardCheatString, "UFJMYjUKOLXKVr")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_LOVEFIST); } // "RUBBISHCAR" else if (!Cheat_strncmp(KeyBoardCheatString, "UFJI`dEIV]")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_TRASH); } // "GETTHEREQUICKLY" else if (!Cheat_strncmp(KeyBoardCheatString, "\\QRDVpTLSPU\\[eT")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_BLOODRB); } // "GETTHEREFAST" else if (!Cheat_strncmp(KeyBoardCheatString, "WXHGRmHOU_RO")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_SABRETUR); } // "BETTERTHANWALKING" else if (!Cheat_strncmp(KeyBoardCheatString, "JSPLY\\ZUBSaZLtaK^")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_CADDY); } // "GETTHEREFASTINDEED" else if (!Cheat_strncmp(KeyBoardCheatString, "GJLE[dWZBQfZLvRXa[^WHL")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_HOTRINA); } // "GETTHEREAMAZINGLYFAST" else if (!Cheat_strncmp(KeyBoardCheatString, "WXHGfgJUJeNUHe_Kdg^HJ")) { KeyBoardCheatString[0] = ' '; VehicleCheat(MI_HOTRINB); } // LOOKLIKELANCE else if (!Cheat_strncmp(KeyBoardCheatString, "HHUBY`NPMV\\WS")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("igbuddy"); } // IWANTBIGTITS else if (!Cheat_strncmp(KeyBoardCheatString, "VYPUTdE[OLdQ")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("igcandy"); } // MYSONISALAWYER else if (!Cheat_strncmp(KeyBoardCheatString, "UJ`XNgDZJY\\[`m")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("igken"); } // ILOOKLIKEHILARY else if (!Cheat_strncmp(KeyBoardCheatString, "\\WHMVcHRJWXWVlV")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("ighlary"); } // ROCKANDROLLMAN else if (!Cheat_strncmp(KeyBoardCheatString, "QFTMYjUKOLXKVr")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("igjezz"); } // ONEARMEDBANDIT else if (!Cheat_strncmp(KeyBoardCheatString, "WNKON]GLN]NMUo")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("igphil"); } // IDONTHAVETHEMONEYSONNY else if (!Cheat_strncmp(KeyBoardCheatString, "\\SUP`tHUPXRP[ecGdgXRGN")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("igsonny"); } // FOXYLITTLETHING else if (!Cheat_strncmp(KeyBoardCheatString, "JSPIa`O[UTYa_oS")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("igmerc"); } // WELOVEOURDICK else if (!Cheat_strncmp(KeyBoardCheatString, "NHPE_pRLWZYM^")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("igdick"); } // CHEATSHAVEBEENCRACKED else if (!Cheat_strncmp(KeyBoardCheatString, "GJRDNmFUFPOM]aUYpTOKF")) { KeyBoardCheatString[0] = ' '; ChangePlayerModel("igdiaz"); } // DEEPFRIEDMARSBARS else if (!Cheat_strncmp(KeyBoardCheatString, "VWHC`mDTEPVZMpRK")) { KeyBoardCheatString[0] = ' '; gfTommyFatness = 0.26f; } // PROGRAMMER else if (!Cheat_strncmp(KeyBoardCheatString, "UJTNNmJVS[")) { KeyBoardCheatString[0] = ' '; gfTommyFatness = -0.3f; } // SEAWAYS else if (!Cheat_strncmp(KeyBoardCheatString, "V^HXN`V")) { KeyBoardCheatString[0] = ' '; BackToTheFuture(); } // LOADSOFLITTLETHINGS else if (!Cheat_strncmp(KeyBoardCheatString, "VLUJUoHSU_VTMo`J]bV")) { KeyBoardCheatString[0] = ' '; SpecialCarCheats(); } // HOPINGIRL else if (!Cheat_strncmp(KeyBoardCheatString, "OWPH[dSVI")) { KeyBoardCheatString[0] = ' '; PickUpChicksCheat(); } //CERTAINDEATH else if (!Cheat_strncmp(KeyBoardCheatString, "KYHFQiLHU]RK")) { KeyBoardCheatString[0] = ' '; CSmokeTrails::CigOn = !CSmokeTrails::CigOn; } //AIRSHIP else if (!Cheat_strncmp(KeyBoardCheatString, "SNOT_dD")) { KeyBoardCheatString[0] = ' '; FlyingFishCheat(); } //FANNYMAGNET else if (!Cheat_strncmp(KeyBoardCheatString, "WJUHNh\\UOLS")) { KeyBoardCheatString[0] = ' '; FannyMagnetCheat(); } #ifdef KANGAROO_CHEAT // "KANGAROO" if (!_CHEATCMP("OORAGNAK")) KangarooCheat(); #endif #ifndef MASTER // "PEDDEBUG" if (!_CHEATCMP("GUBEDDEP")) CPed::SwitchDebugDisplay(); #endif #ifdef RESTORE_ALLCARSHELI_CHEAT // "CARSAREHELI" if (!_CHEATCMP("ILEHERASRAC")) AllCarsHeliCheat(); #endif #ifdef WALLCLIMB_CHEAT // "SPIDERCAR" if (!_CHEATCMP("RACREDIPS")) WallClimbingCheat(); #endif #if !defined(PC_WATER) && defined(WATER_CHEATS) // SEABEDCHEAT if (!_CHEATCMP("TAEHCDEBAESON")) NoSeaBedCheat(); // WATERLAYERSCHEAT if (!_CHEATCMP("TAEHCSREYALRETAW")) RenderWaterLayersCheat(); #endif #undef _CHEATCMP } #ifdef XINPUT int CPad::XInputJoy1 = 0; int CPad::XInputJoy2 = 1; void CPad::AffectFromXinput(uint32 pad) { pad = pad == 0 ? XInputJoy1 : XInputJoy2; if (pad == -1) // LoadINIControllerSettings can set it to -1 return; XINPUT_STATE xstate; memset(&xstate, 0, sizeof(XINPUT_STATE)); if (XInputGetState(pad, &xstate) == ERROR_SUCCESS) { PCTempJoyState.Circle = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 255 : 0; PCTempJoyState.Cross = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 255 : 0; PCTempJoyState.Square = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 255 : 0; PCTempJoyState.Triangle = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 255 : 0; PCTempJoyState.DPadDown = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 255 : 0; PCTempJoyState.DPadLeft = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? 255 : 0; PCTempJoyState.DPadRight = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? 255 : 0; PCTempJoyState.DPadUp = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 255 : 0; PCTempJoyState.LeftShock = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 255 : 0; PCTempJoyState.LeftShoulder1 = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 255 : 0; PCTempJoyState.LeftShoulder2 = xstate.Gamepad.bLeftTrigger; PCTempJoyState.RightShock = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 255 : 0; PCTempJoyState.RightShoulder1 = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 255 : 0; PCTempJoyState.RightShoulder2 = xstate.Gamepad.bRightTrigger; PCTempJoyState.Select = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 255 : 0; #ifdef REGISTER_START_BUTTON PCTempJoyState.Start = (xstate.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 255 : 0; #endif float lx = (float)xstate.Gamepad.sThumbLX / (float)0x7FFF; float ly = (float)xstate.Gamepad.sThumbLY / (float)0x7FFF; float rx = (float)xstate.Gamepad.sThumbRX / (float)0x7FFF; float ry = (float)xstate.Gamepad.sThumbRY / (float)0x7FFF; if (Abs(lx) > 0.3f || Abs(ly) > 0.3f) { PCTempJoyState.LeftStickX = (int32)(lx * 128.0f); PCTempJoyState.LeftStickY = (int32)(-ly * 128.0f); } if (Abs(rx) > 0.3f || Abs(ry) > 0.3f) { PCTempJoyState.RightStickX = (int32)(rx * 128.0f); PCTempJoyState.RightStickY = (int32)(-ry * 128.0f); } XINPUT_VIBRATION VibrationState; memset(&VibrationState, 0, sizeof(XINPUT_VIBRATION)); uint16 iLeftMotor = (uint16)((float)ShakeFreq / 255.0f * (float)0xffff); uint16 iRightMotor = (uint16)((float)ShakeFreq / 255.0f * (float)0xffff); if (ShakeDur < CTimer::GetTimeStepInMilliseconds()) ShakeDur = 0; else ShakeDur -= CTimer::GetTimeStepInMilliseconds(); if (ShakeDur == 0) ShakeFreq = 0; VibrationState.wLeftMotorSpeed = iLeftMotor; VibrationState.wRightMotorSpeed = iRightMotor; XInputSetState(pad, &VibrationState); } } #endif void CPad::UpdatePads(void) { bool bUpdate = true; GetPad(0)->UpdateMouse(); #ifdef XINPUT GetPad(0)->AffectFromXinput(m_bMapPadOneToPadTwo ? 1 : 0); GetPad(1)->AffectFromXinput(m_bMapPadOneToPadTwo ? 0 : 1); #else CapturePad(0); #endif // Improve keyboard input latency part 1 #ifdef FIX_BUGS OldKeyState = NewKeyState; NewKeyState = TempKeyState; #endif #ifdef DETECT_PAD_INPUT_SWITCH if (GetPad(0)->PCTempJoyState.CheckForInput()) IsAffectedByController = true; else { #endif ControlsManager.ClearSimButtonPressCheckers(); ControlsManager.AffectPadFromKeyBoard(); ControlsManager.AffectPadFromMouse(); #ifdef DETECT_PAD_INPUT_SWITCH } if (IsAffectedByController && (GetPad(0)->PCTempKeyState.CheckForInput() || GetPad(0)->PCTempMouseState.CheckForInput())) IsAffectedByController = false; #endif if ( CReplay::IsPlayingBackFromFile() && !FrontEndMenuManager.m_bMenuActive ) bUpdate = false; if ( bUpdate ) GetPad(0)->Update(0); #ifndef MASTER GetPad(1)->Update(1); #else GetPad(1)->NewState.Clear(); GetPad(1)->OldState.Clear(); #endif // Improve keyboard input latency part 2 #ifndef FIX_BUGS OldKeyState = NewKeyState; NewKeyState = TempKeyState; #endif } void CPad::ProcessPCSpecificStuff(void) { ; } void CPad::Update(int16 pad) { OldState = NewState; #ifdef GTA_PS2 bObsoleteControllerMessage = false; //int iPressureBtn; int id; int ext_id=0; int state; int rterm_id = 0; unsigned short paddata, tpad; unsigned char rdata[32]; state = scePadGetState(pad, 0); switch(Phase) { case 0: if (state != scePadStateStable && state != scePadStateFindCTP1) break; id = scePadInfoMode(pad, 0, InfoModeCurID, 0); if (id==0) break; ext_id = scePadInfoMode(pad, 0, InfoModeCurExID, 0); if (ext_id>0) id = ext_id; switch(id) { case 4: // Digital controller Phase = 40; // Try for analog(dualshock) break; case 7: // Dualshock2 controller Phase = 50; break; default: Phase = 99; break; } break; // Analog Controller (old dualshock) case 40: // Analog Contoller check valid (otherwise fail phase) if (scePadInfoMode(pad, 0, InfoModeIdTable, -1)==0) { Phase = 99; break; } Phase++; case 41: // Analog controller: Request Lock analog mode (asynchronous) if (scePadSetMainMode(pad, 0, 1, 3)==1) Phase++; break; case 42: // Analog controller: Check state of previous request if (scePadGetReqState(pad, 0)==scePadReqStateFaild) { Phase--; } if (scePadGetReqState(pad, 0)==scePadReqStateComplete) { // Lock mode complete Phase=0; // Accept normal dualshock } break; // DualShock 2 Controller case 50: // Analog Contoller check valid (otherwise fail phase) if (scePadInfoMode(pad, 0, InfoModeIdTable, -1)==0) { Phase = 99; break; } Phase++; case 51: // Analog controller: Request Lock analog mode (asynchronous) if (scePadSetMainMode(pad, 0, 1, 3)==1) Phase++; break; case 52: // Analog controller: Check state of previous request if (scePadGetReqState(pad, 0)==scePadReqStateFaild) { Phase--; } if (scePadGetReqState(pad, 0)==scePadReqStateComplete) { // Lock mode complete Phase=0; // Accept normal dualshock } break; case 70: // DualShock 2 check pressure sensitive possible if (scePadInfoPressMode(pad, 0)==1) { Phase = 76; break; } Phase = 99; break; case 76: // DualShock2 enable pressure sensitive mode (asynchronous function) if (scePadEnterPressMode(pad, 0)==1) Phase++; break; case 77: // Dualshock2 check status of request pressure sensitive mode if (scePadGetReqState(pad, 0)==scePadReqStateFaild) Phase--; if (scePadGetReqState(pad, 0)==scePadReqStateComplete) { Phase=80; } break; // DualShock 2 Controller case 80: // Set motors if (scePadInfoAct(pad, 0, -1, 0)==0) { Phase = 99; } act_align[0] = 0; // Offset 0 for motor0 act_align[1] = 1; // Offset 1 for motor1 act_align[2] = 0xff; act_align[3] = 0xff; act_align[4] = 0xff; act_align[5] = 0xff; // Asynchronous function if (scePadSetActAlign(pad, 0, act_align)==0) break; Phase++; break; case 81: if ( scePadGetState(pad, 0) != scePadStateExecCmd ) { Phase = 99; } break; default: if ( state == scePadStateError ) break; if ( state == scePadStateStable || state == scePadStateFindCTP1 ) { if ( ShakeDur ) { ShakeDur = Max(ShakeDur - (int32)CTimer::GetTimeStepInMilliseconds(), 0); if ( ShakeDur == 0 ) { act_direct[0] = 0; act_direct[1] = 0; scePadSetActDirect(pad, 0, act_direct); } else { act_direct[0] = 0; act_direct[1] = (unsigned char) ShakeFreq; scePadSetActDirect(pad, 0, act_direct); } } if (scePadRead( pad, 0, rdata )==0) { NewState.Clear(); break; } if ((rdata[0] == 0)) { paddata = (unsigned short) ( 0xffff ^ ((rdata[2]<<8)|rdata[3]) ); rterm_id = (rdata[1]); if ( (rterm_id>>4) == 7 ) // DUALSHOCK { if (!CRecordDataForGame::IsPlayingBack() && !CRecordDataForChase::ShouldThisPadBeLeftAlone(pad)) { tpad = paddata; NewState.DPadUp = ( tpad & SCE_PADLup ) ? 255 : 0; NewState.DPadDown = ( tpad & SCE_PADLdown ) ? 255 : 0; NewState.DPadLeft = ( tpad & SCE_PADLleft ) ? 255 : 0; NewState.DPadRight = ( tpad & SCE_PADLright ) ? 255 : 0; NewState.Triangle = ( tpad & SCE_PADRup ) ? 255 : 0; NewState.Cross = ( tpad & SCE_PADRdown ) ? 255 : 0; NewState.Square = ( tpad & SCE_PADRleft ) ? 255 : 0; NewState.Circle = ( tpad & SCE_PADRright ) ? 255 : 0; NewState.Start = ( tpad & SCE_PADstart ) ? 255 : 0; NewState.Select = ( tpad & SCE_PADselect ) ? 255 : 0; NewState.LeftShoulder1 = ( tpad & SCE_PADL1 ) ? 255 : 0; NewState.LeftShoulder2 = ( tpad & SCE_PADL2 ) ? 255 : 0; NewState.RightShoulder1 = ( tpad & SCE_PADR1 ) ? 255 : 0; NewState.RightShoulder2 = ( tpad & SCE_PADR2 ) ? 255 : 0; NewState.LeftShock = ( tpad & SCE_PADi ) ? 255 : 0; NewState.RightShock = ( tpad & SCE_PADj ) ? 255 : 0; NewState.RightStickX = (short)rdata[4]; NewState.RightStickY = (short)rdata[5]; NewState.LeftStickX = (short)rdata[6]; NewState.LeftStickY = (short)rdata[7]; #define CLAMP_AXIS(x) (((x) < 43 && (x) >= -42) ? 0 : (((x) > 0) ? (Max((x)-42, 0)*127/85) : Min((x)+42, 0)*127/85)) #define FIX_AXIS(x) CLAMP_AXIS((x)-128) NewState.RightStickX = FIX_AXIS(NewState.RightStickX); NewState.RightStickY = FIX_AXIS(NewState.RightStickY); NewState.LeftStickX = FIX_AXIS(NewState.LeftStickX); NewState.LeftStickY = FIX_AXIS(NewState.LeftStickY); #undef FIX_AXIS #undef CLAMP_AXIS } } else if ( (rterm_id>>4) == 4 ) // Controller (digital) { if ( pad == 0 ) bObsoleteControllerMessage = true; NewState.Clear(); } if ( NewState.IsAnyButtonPressed() ) LastTimeTouched = CTimer::GetTimeInMilliseconds(); break; } if ( ++iCurrHornHistory >= HORNHISTORY_SIZE ) iCurrHornHistory = 0; bHornHistory[iCurrHornHistory] = GetHorn(); NewState.Clear(); return; } break; } if ( pad == 0 ) { bOldDisplayNoControllerMessage = bDisplayNoControllerMessage; if ( state == scePadStateDiscon ) { bDisplayNoControllerMessage = true; Phase = 0; } else bDisplayNoControllerMessage = false; } if ( ++iCurrHornHistory >= HORNHISTORY_SIZE ) iCurrHornHistory = 0; bHornHistory[iCurrHornHistory] = GetHorn(); if ( !bDisplayNoControllerMessage ) CGame::bDemoMode = false; #endif #if (defined GTA_PS2 || defined FIX_BUGS) if (!CRecordDataForGame::IsPlayingBack() && !CRecordDataForChase::ShouldThisPadBeLeftAlone(pad)) #endif { NewState = ReconcileTwoControllersInput(PCTempKeyState, PCTempJoyState); NewState = ReconcileTwoControllersInput(PCTempMouseState, NewState); } PCTempJoyState.Clear(); PCTempKeyState.Clear(); PCTempMouseState.Clear(); ProcessPCSpecificStuff(); if (NewState.CheckForInput()) LastTimeTouched = CTimer::GetTimeInMilliseconds(); if ( ++iCurrHornHistory >= HORNHISTORY_SIZE ) iCurrHornHistory = 0; bHornHistory[iCurrHornHistory] = GetHorn(); for (int32 i = DRUNK_STEERING_BUFFER_SIZE - 2; i >= 0; i--) { SteeringLeftRightBuffer[i + 1] = SteeringLeftRightBuffer[i]; } if ( !bDisplayNoControllerMessage ) CGame::bDemoMode = false; if ( JustOutOfFrontend != 0 ) --JustOutOfFrontend; } void CPad::DoCheats(void) { #ifdef DETECT_PAD_INPUT_SWITCH if (IsAffectedByController) #endif GetPad(0)->DoCheats(0); } void CPad::DoCheats(int16 unk) { #ifdef GTA_PS2_STUFF if ( GetTriangleJustDown() ) AddToCheatString('T'); if ( GetCircleJustDown() ) AddToCheatString('C'); if ( GetCrossJustDown() ) AddToCheatString('X'); if ( GetSquareJustDown() ) AddToCheatString('S'); if ( GetDPadUpJustDown() ) AddToCheatString('U'); if ( GetDPadDownJustDown() ) AddToCheatString('D'); if ( GetDPadLeftJustDown() ) AddToCheatString('L'); if ( GetDPadRightJustDown() ) AddToCheatString('R'); if ( GetLeftShoulder1JustDown() ) AddToCheatString('1'); if ( GetLeftShoulder2JustDown() ) AddToCheatString('2'); if ( GetRightShoulder1JustDown() ) AddToCheatString('3'); if ( GetRightShoulder2JustDown() ) AddToCheatString('4'); #endif } void CPad::StopPadsShaking(void) { GetPad(0)->StopShaking(0); } void CPad::StopShaking(int16 pad) { #ifdef GTA_PS2_STUFF ShakeFreq = 0; ShakeDur = 0; #ifdef GTA_PS2 if ( Phase == 99 ) { act_direct[0] = 0; act_direct[1] = 0; scePadSetActDirect(pad, 0, act_direct); } #endif #endif } CPad *CPad::GetPad(int32 pad) { return &Pads[pad]; } #ifdef DETECT_PAD_INPUT_SWITCH #define CURMODE (IsAffectedByController ? Mode : 0) #else #define CURMODE (Mode) #endif int16 CPad::GetSteeringLeftRight(void) { if ( ArePlayerControlsDisabled() ) return 0; int16 value; switch (CURMODE) { case 0: case 2: { int16 axis = NewState.LeftStickX; int16 dpad = (NewState.DPadRight - NewState.DPadLeft) / 2; if ( Abs(axis) > Abs(dpad) ) value = axis; else value = dpad; SteeringLeftRightBuffer[0] = value; value = SteeringLeftRightBuffer[DrunkDrivingBufferUsed]; break; } case 1: case 3: { SteeringLeftRightBuffer[0] = NewState.LeftStickX; value = SteeringLeftRightBuffer[DrunkDrivingBufferUsed]; break; } default: { value = 0; break; } } return value; } int16 CPad::GetSteeringUpDown(void) { if ( ArePlayerControlsDisabled() ) return 0; switch (CURMODE) { case 0: case 2: { int16 axis = NewState.LeftStickY; int16 dpad = (NewState.DPadDown - NewState.DPadUp) / 2; if ( Abs(axis) > Abs(dpad) ) return axis; else return dpad; break; } case 1: case 3: { return NewState.LeftStickY; break; } } return 0; } int16 CPad::GetCarGunUpDown(void) { if ( ArePlayerControlsDisabled() ) return 0; switch (CURMODE) { case 0: case 1: case 2: { return NewState.RightStickY; break; } case 3: { return (NewState.DPadUp - NewState.DPadDown) / 2; break; } } return 0; } int16 CPad::GetCarGunLeftRight(void) { if ( ArePlayerControlsDisabled() ) return 0; switch (CURMODE) { case 0: case 1: case 2: { return NewState.RightStickX; break; } case 3: { return (NewState.DPadRight - NewState.DPadLeft) / 2; break; } } return 0; } int16 CPad::GetPedWalkLeftRight(void) { if ( ArePlayerControlsDisabled() ) return 0; switch (CURMODE) { case 0: case 2: { int16 axis = NewState.LeftStickX; int16 dpad = (NewState.DPadRight - NewState.DPadLeft) / 2; if ( Abs(axis) > Abs(dpad) ) return axis; else return dpad; break; } case 1: case 3: { return NewState.LeftStickX; break; } } return 0; } int16 CPad::GetPedWalkUpDown(void) { if ( ArePlayerControlsDisabled() ) return 0; switch (CURMODE) { case 0: case 2: { int16 axis = NewState.LeftStickY; int16 dpad = (NewState.DPadDown - NewState.DPadUp) / 2; if ( Abs(axis) > Abs(dpad) ) return axis; else return dpad; break; } case 1: case 3: { return NewState.LeftStickY; break; } } return 0; } int16 CPad::GetAnalogueUpDown(void) { switch (CURMODE) { case 0: case 2: { int16 axis = NewState.LeftStickY; int16 dpad = (NewState.DPadDown - NewState.DPadUp) / 2; if ( Abs(axis) > Abs(dpad) ) return axis; else return dpad; break; } case 1: case 3: { return NewState.LeftStickY; break; } } return 0; } int16 CPad::GetAnalogueLeftRight(void) { switch (CURMODE) { case 0: case 2: { int16 axis = NewState.LeftStickX; int16 dpad = (NewState.DPadRight - NewState.DPadLeft) / 2; if ( Abs(axis) > Abs(dpad) ) return axis; else return dpad; break; } case 1: case 3: { return NewState.LeftStickX; break; } } return 0; } bool CPad::GetLookLeft(void) { if ( ArePlayerControlsDisabled() ) return false; return !!(NewState.LeftShoulder2 && !NewState.RightShoulder2); } bool CPad::GetLookRight(void) { if ( ArePlayerControlsDisabled() ) return false; return !!(NewState.RightShoulder2 && !NewState.LeftShoulder2); } bool CPad::GetLookBehindForCar(void) { if ( ArePlayerControlsDisabled() ) return false; return !!(NewState.RightShoulder2 && NewState.LeftShoulder2); } bool CPad::GetLookBehindForPed(void) { if ( ArePlayerControlsDisabled() ) return false; return !!NewState.RightShock; } bool CPad::GetHorn(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: { return !!NewState.LeftShock; break; } case 1: { return !!NewState.LeftShoulder1; break; } case 2: { return !!NewState.RightShoulder1; break; } case 3: { return !!NewState.LeftShock; break; } } return false; } bool CPad::HornJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: { return !!(NewState.LeftShock && !OldState.LeftShock); break; } case 1: { return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); break; } case 2: { return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); break; } case 3: { return !!(NewState.LeftShock && !OldState.LeftShock); break; } } return false; } bool CPad::GetCarGunFired(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: case 2: { return !!NewState.Circle; break; } case 3: { return !!NewState.RightShoulder1; break; } } return false; } bool CPad::CarGunJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: case 2: { return !!(NewState.Circle && !OldState.Circle); break; } case 3: { return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); break; } } return false; } int16 CPad::GetHandBrake(void) { if ( ArePlayerControlsDisabled() ) return 0; switch (CURMODE) { case 0: case 1: { return NewState.RightShoulder1; break; } case 2: { return NewState.Triangle; break; } case 3: { return NewState.LeftShoulder1; break; } } return 0; } int16 CPad::GetBrake(void) { if ( ArePlayerControlsDisabled() ) return 0; switch (CURMODE) { case 0: case 2: { return NewState.Square; break; } case 1: { return NewState.Square; break; } case 3: { int16 axis = 2 * NewState.RightStickY; if ( axis < 0 ) return 0; else return axis; break; } } return 0; } bool CPad::GetExitVehicle(void) { if ( ArePlayerControlsDisabled() ) return false; if ( JustOutOfFrontend != 0 ) return false; switch (CURMODE) { case 0: case 1: case 3: { return !!NewState.Triangle; break; } case 2: { return !!NewState.LeftShoulder1; break; } } return false; } bool CPad::ExitVehicleJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; if ( JustOutOfFrontend != 0 ) return false; switch (CURMODE) { case 0: case 1: case 3: { return !!(NewState.Triangle && !OldState.Triangle); break; } case 2: { return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); break; } } return false; } int32 CPad::GetWeapon(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: { return NewState.Circle; break; } case 2: { return NewState.Cross; break; } case 3: { return NewState.RightShoulder1; break; } } return false; } bool CPad::WeaponJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: { return !!(NewState.Circle && !OldState.Circle); break; } case 2: { return !!(NewState.Cross && !OldState.Cross); break; } case 3: { return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); break; } } return false; } int16 CPad::GetAccelerate(void) { if ( ArePlayerControlsDisabled() ) return 0; switch (CURMODE) { case 0: case 2: { return NewState.Cross; break; } case 1: { return NewState.Cross; break; } case 3: { int16 axis = -2 * NewState.RightStickY; if ( axis < 0 ) return 0; else return axis; break; } } return 0; } bool CPad::CycleCameraModeJustDown(void) { bool result; switch (CURMODE) { case 0: case 2: case 3: { result = !!(NewState.Select && !OldState.Select); break; } case 1: { result = !!(NewState.DPadUp && !OldState.DPadUp); break; } default: { result = false; break; } } if (!result) { switch (CURMODE) { case 1: { result = !!(NewState.DPadDown && !OldState.DPadDown); break; } default: { result = false; break; } } } return result; } bool CPad::CycleCameraModeUpJustDown(void) { switch (CURMODE) { case 0: case 2: case 3: { return !!(NewState.Select && !OldState.Select); break; } case 1: { return !!(NewState.DPadUp && !OldState.DPadUp); break; } } return false; } bool CPad::CycleCameraModeDownJustDown(void) { switch (CURMODE) { case 0: case 2: case 3: { return false; break; } case 1: { return !!(NewState.DPadDown && !OldState.DPadDown); break; } } return false; } bool CPad::ChangeStationJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: { return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); break; } case 1: { return !!(NewState.Select && !OldState.Select); break; } case 2: { return !!(NewState.LeftShock && !OldState.LeftShock); break; } case 3: { return !!(NewState.Circle && !OldState.Circle); break; } } return false; } bool CPad::CycleWeaponLeftJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; return !!(NewState.LeftShoulder2 && !OldState.LeftShoulder2); } bool CPad::CycleWeaponRightJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; return !!(NewState.RightShoulder2 && !OldState.RightShoulder2); } bool CPad::GetTarget(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: case 2: { return !!NewState.RightShoulder1; break; } case 3: { return !!NewState.LeftShoulder1; break; } } return false; } bool CPad::TargetJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: case 2: { return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); break; } case 3: { return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); break; } } return false; } bool CPad::CollectPickupJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: { return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); break; } case 2: { return !!(NewState.Triangle && !OldState.Triangle); break; } case 3: { return !!(NewState.Circle && !OldState.Circle); break; } } return false; } bool CPad::DuckJustDown(void) { if (ArePlayerControlsDisabled()) return false; return !!(NewState.LeftShock && !OldState.LeftShock); } bool CPad::JumpJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; return !!(NewState.Square && !OldState.Square); } bool CPad::GetSprint(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: case 3: { return !!NewState.Cross; break; } case 2: { return !!NewState.Circle; break; } } return false; } bool CPad::ShiftTargetLeftJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; return !!(NewState.LeftShoulder2 && !OldState.LeftShoulder2); } bool CPad::ShiftTargetRightJustDown(void) { if ( ArePlayerControlsDisabled() ) return false; return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1) || !!(NewState.RightShoulder2 && !OldState.RightShoulder2); } bool CPad::GetAnaloguePadUp(void) { static int16 oldfStickY = 0; int16 leftStickY = CPad::GetPad(0)->GetLeftStickY(); if ( leftStickY < -15 && oldfStickY >= -5 ) { oldfStickY = leftStickY; return true; } else { oldfStickY = leftStickY; return false; } } bool CPad::GetAnaloguePadDown(void) { static int16 oldfStickY = 0; int16 leftStickY = CPad::GetPad(0)->GetLeftStickY(); if ( leftStickY > 15 && oldfStickY <= 5 ) { oldfStickY = leftStickY; return true; } else { oldfStickY = leftStickY; return false; } } bool CPad::GetAnaloguePadLeft(void) { static int16 oldfStickX = 0; int16 leftStickX = CPad::GetPad(0)->GetLeftStickX(); if ( leftStickX < -15 && oldfStickX >= -5 ) { oldfStickX = leftStickX; return true; } else { oldfStickX = leftStickX; return false; } } bool CPad::GetAnaloguePadRight(void) { static int16 oldfStickX = 0; int16 leftStickX = CPad::GetPad(0)->GetLeftStickX(); if ( leftStickX > 15 && oldfStickX <= 5 ) { oldfStickX = leftStickX; return true; } else { oldfStickX = leftStickX; return false; } } bool CPad::GetAnaloguePadLeftJustUp(void) { static int16 oldfStickX = 0; int16 X = GetPad(0)->GetPedWalkLeftRight(); if ( X == 0 && oldfStickX < 0 ) { oldfStickX = 0; return true; } else { oldfStickX = X; return false; } } bool CPad::GetAnaloguePadRightJustUp(void) { static int16 oldfStickX = 0; int16 X = GetPad(0)->GetPedWalkLeftRight(); if ( X == 0 && oldfStickX > 0 ) { oldfStickX = 0; return true; } else { oldfStickX = X; return false; } } bool CPad::ForceCameraBehindPlayer(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: { return !!NewState.LeftShoulder1; break; } case 2: { return !!NewState.Triangle; break; } case 3: { return !!NewState.Circle; break; } } return false; } bool CPad::SniperZoomIn(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: case 3: { return !!NewState.Square; break; } case 2: { return !!NewState.Triangle; break; } } return false; } bool CPad::SniperZoomOut(void) { if ( ArePlayerControlsDisabled() ) return false; switch (CURMODE) { case 0: case 1: case 3: { return !!NewState.Cross; break; } case 2: { return !!NewState.Square; break; } } return false; } #undef CURMODE int16 CPad::SniperModeLookLeftRight(void) { int16 axis = NewState.LeftStickX; int16 dpad = (NewState.DPadRight - NewState.DPadLeft) / 2; if ( Abs(axis) > Abs(dpad) ) { if ( Abs(axis) > 35.0f ) { return (axis > 0.f ? axis - 35.f : axis + 35.f) * (128.f / (128 - 35)); } else { return 0; } } else return dpad; } int16 CPad::SniperModeLookUpDown(void) { int16 axis = NewState.LeftStickY; int16 dpad; #ifdef FIX_BUGS axis = -axis; #endif if (CPad::bInvertLook4Pad) { axis = -axis; dpad = (NewState.DPadDown - NewState.DPadUp) / 2; } else { dpad = (NewState.DPadUp - NewState.DPadDown) / 2; } if ( Abs(axis) > Abs(dpad) ) { if ( Abs(axis) > 35.0f ) { return (axis > 0.f ? axis - 35.f : axis + 35.f) * (128.f / (128 - 35)); } else { return 0; } } else return dpad; } int16 CPad::LookAroundLeftRight(void) { float axis = GetPad(0)->NewState.RightStickX; if ( Abs(axis) > 85 && !GetLookBehindForPed() ) return (int16) ( (axis + ( ( axis > 0 ) ? -85 : 85) ) * (127.0f / 32.0f) ); // 3.96875f else if ( TheCamera.Cams[0].Using3rdPersonMouseCam() && Abs(axis) > 10 ) return (int16) ( (axis + ( ( axis > 0 ) ? -10 : 10) ) * (127.0f / 64.0f) ); // 1.984375f return 0; } int16 CPad::LookAroundUpDown(void) { int16 axis = GetPad(0)->NewState.RightStickY; #ifdef FIX_BUGS axis = -axis; #endif if (CPad::bInvertLook4Pad) axis = -axis; if ( Abs(axis) > 85 && !GetLookBehindForPed() ) return (int16) ( (axis + ( ( axis > 0 ) ? -85 : 85) ) * (127.0f / 32.0f) ); // 3.96875f else if ( TheCamera.Cams[0].Using3rdPersonMouseCam() && Abs(axis) > 40 ) return (int16) ( (axis + ( ( axis > 0 ) ? -40 : 40) ) * (127.0f / 64.0f) ); // 1.984375f return 0; } void CPad::ResetAverageWeapon(void) { AverageWeapon = GetWeapon(); AverageEntries = 1; } void CPad::PrintErrorMessage(void) { if (TheCamera.m_WideScreenOn) return; if ( bDisplayNoControllerMessage && !CGame::playingIntro && !FrontEndMenuManager.m_bMenuActive ) { CSprite2d::DrawRect(CRect(SCREEN_STRETCH_X(20.0f), SCREEN_SCALE_FROM_BOTTOM(130.0f), SCREEN_STRETCH_FROM_RIGHT(20.0f), SCREEN_SCALE_Y(140.0f)), CRGBA(50, 50, 50, 210)); #ifdef FIX_BUGS CFont::SetScale(SCREEN_SCALE_X(0.85f), SCREEN_SCALE_Y(1.0f)); #else CFont::SetScale(0.85f, 1.0f); #endif CFont::SetJustifyOff(); CFont::SetBackgroundOff(); #ifdef FIX_BUGS CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 20)); #else CFont::SetCentreSize(SCREEN_WIDTH - 20); #endif CFont::SetCentreOn(); CFont::SetPropOn(); CFont::SetColor(CRGBA(255, 255, 200, 200)); CFont::SetFontStyle(FONT_STANDARD); CFont::PrintString ( SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(40.0f), TheText.Get("NOCONT") // Please reconnect an analog controller (DUALSHOCK@) or analog controller (DUALSHOCK@2). to controller port 1 to continue ); } else if ( bObsoleteControllerMessage ) { CSprite2d::DrawRect(CRect(SCREEN_STRETCH_X(20.0f), SCREEN_SCALE_FROM_BOTTOM(130.0f), SCREEN_STRETCH_FROM_RIGHT(20.0f), SCREEN_SCALE_Y(140.0f)), CRGBA(50, 50, 50, 210)); #ifdef FIX_BUGS CFont::SetScale(SCREEN_SCALE_X(0.85f), SCREEN_SCALE_Y(1.0f)); #else CFont::SetScale(0.85f, 1.0f); #endif CFont::SetJustifyOff(); CFont::SetBackgroundOff(); #ifdef FIX_BUGS CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH - 20)); #else CFont::SetCentreSize(SCREEN_WIDTH - 20); #endif CFont::SetCentreOn(); CFont::SetPropOn(); CFont::SetColor(CRGBA(255, 255, 200, 200)); CFont::SetFontStyle(FONT_STANDARD); CFont::PrintString ( SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(40.0f), TheText.Get("WRCONT") // The controller connected to controller port 1 is an unsupported controller. Grand Theft Auto III requires an analog controller (DUALSHOCK@) or analog controller (DUALSHOCK@2). ); } } void LittleTest(void) { static int32 Cunt = 0; Cunt++; // ??? } void CPad::ResetCheats(void) { CWeather::ReleaseWeather(); gbFastTime = false; CPopulation::ms_bGivePedsWeapons = false; CTimer::SetTimeScale(1.0f); CVehicle::bWheelsOnlyCheat = false; CVehicle::bAllDodosCheat = false; CVehicle::bCheat3 = false; CVehicle::bCheat4 = false; CVehicle::bCheat5 = false; CVehicle::bAllTaxisHaveNitro = false; CVehicle::bHoverCheat = false; CVehicle::bCheat8 = false; CVehicle::bCheat9 = false; CVehicle::bCheat10 = false; #ifdef RESTORE_ALLCARSHELI_CHEAT bAllCarCheat = false; #endif gbBlackCars = false; gbPinkCars = false; CCarCtrl::bMadDriversCheat = false; CTrafficLights::bGreenLightsCheat = false; CStats::ShowChaseStatOnScreen = 0; CPed::bNastyLimbsCheat = false; CPed::bFannyMagnetCheat = false; CPed::bPedCheat3 = false; } char *CPad::EditString(char *pStr, int32 nSize) { int32 pos = (int32)strlen(pStr); // letters for ( int32 i = 0; i < ('Z' - 'A' + 1); i++ ) { if ( GetPad(0)->GetCharJustDown(i + 'A') && pos < nSize - 1 ) { pStr[pos++] = i + 'A'; pStr[pos] = '\0'; } if ( GetPad(0)->GetCharJustDown(i + 'a') && pos < nSize - 1 ) { pStr[pos++] = i + 'a'; pStr[pos] = '\0'; } } // numbers for ( int32 i = 0; i < ('9' - '0' + 1); i++ ) { if ( GetPad(0)->GetCharJustDown(i + '0') && pos < nSize - 1 ) { pStr[pos++] = i + '0'; pStr[pos] = '\0'; } } // space if ( GetPad(0)->GetCharJustDown(' ') && pos < nSize - 1 ) { pStr[pos++] = ' '; pStr[pos] = '\0'; } // del if ( GetPad(0)->GetDeleteJustDown() || GetPad(0)->GetBackspaceJustDown() ) { if ( pos > 0 ) pStr[pos - 1] = '\0'; } // extenter/up/down if ( GetPad(0)->GetReturnJustDown() || GetPad(0)->GetUpJustDown() || GetPad(0)->GetDownJustDown() ) return nil; return pStr; } int32 *CPad::EditCodesForControls(int32 *pRsKeys, int32 nSize) { *pRsKeys = rsNULL; for ( int32 i = 0; i < 255; i++ ) { if ( GetPad(0)->GetCharJustDown(i) ) *pRsKeys = i; } for ( int32 i = 0; i < 12; i++ ) { if ( GetPad(0)->GetFJustDown(i) ) *pRsKeys = i + rsF1; } if ( GetPad(0)->GetEscapeJustDown() ) *pRsKeys = rsESC; if ( GetPad(0)->GetInsertJustDown() ) *pRsKeys = rsINS; if ( GetPad(0)->GetDeleteJustDown() ) *pRsKeys = rsDEL; if ( GetPad(0)->GetHomeJustDown() ) *pRsKeys = rsHOME; if ( GetPad(0)->GetEndJustDown() ) *pRsKeys = rsEND; if ( GetPad(0)->GetPageUpJustDown() ) *pRsKeys = rsPGUP; if ( GetPad(0)->GetPageDownJustDown() ) *pRsKeys = rsPGDN; if ( GetPad(0)->GetUpJustDown() ) *pRsKeys = rsUP; if ( GetPad(0)->GetDownJustDown() ) *pRsKeys = rsDOWN; if ( GetPad(0)->GetLeftJustDown() ) *pRsKeys = rsLEFT; if ( GetPad(0)->GetRightJustDown() ) *pRsKeys = rsRIGHT; if ( GetPad(0)->GetScrollLockJustDown() ) *pRsKeys = rsSCROLL; if ( GetPad(0)->GetPauseJustDown() ) *pRsKeys = rsPAUSE; if ( GetPad(0)->GetNumLockJustDown() ) *pRsKeys = rsNUMLOCK; if ( GetPad(0)->GetDivideJustDown() ) *pRsKeys = rsDIVIDE; if ( GetPad(0)->GetTimesJustDown() ) *pRsKeys = rsTIMES; if ( GetPad(0)->GetMinusJustDown() ) *pRsKeys = rsMINUS; if ( GetPad(0)->GetPlusJustDown() ) *pRsKeys = rsPLUS; if ( GetPad(0)->GetPadEnterJustDown() ) *pRsKeys = rsPADENTER; if ( GetPad(0)->GetPadDelJustDown() ) *pRsKeys = rsPADDEL; if ( GetPad(0)->GetPad1JustDown() ) *pRsKeys = rsPADEND; if ( GetPad(0)->GetPad2JustDown() ) *pRsKeys = rsPADDOWN; if ( GetPad(0)->GetPad3JustDown() ) *pRsKeys = rsPADPGDN; if ( GetPad(0)->GetPad4JustDown() ) *pRsKeys = rsPADLEFT; if ( GetPad(0)->GetPad5JustDown() ) *pRsKeys = rsPAD5; if ( GetPad(0)->GetPad6JustDown() ) *pRsKeys = rsPADRIGHT; if ( GetPad(0)->GetPad7JustDown() ) *pRsKeys = rsPADHOME; if ( GetPad(0)->GetPad8JustDown() ) *pRsKeys = rsPADUP; if ( GetPad(0)->GetPad9JustDown() ) *pRsKeys = rsPADPGUP; if ( GetPad(0)->GetPad0JustDown() ) *pRsKeys = rsPADINS; if ( GetPad(0)->GetBackspaceJustDown() ) *pRsKeys = rsBACKSP; if ( GetPad(0)->GetTabJustDown() ) *pRsKeys = rsTAB; if ( GetPad(0)->GetCapsLockJustDown() ) *pRsKeys = rsCAPSLK; if ( GetPad(0)->GetReturnJustDown() ) *pRsKeys = rsENTER; if ( GetPad(0)->GetLeftShiftJustDown() ) *pRsKeys = rsLSHIFT; if ( GetPad(0)->GetShiftJustDown() ) *pRsKeys = rsSHIFT; if ( GetPad(0)->GetRightShiftJustDown() ) *pRsKeys = rsRSHIFT; if ( GetPad(0)->GetLeftCtrlJustDown() ) *pRsKeys = rsLCTRL; if ( GetPad(0)->GetRightCtrlJustDown() ) *pRsKeys = rsRCTRL; if ( GetPad(0)->GetLeftAltJustDown() ) *pRsKeys = rsLALT; if ( GetPad(0)->GetRightAltJustDown() ) *pRsKeys = rsRALT; if ( GetPad(0)->GetLeftWinJustDown() ) *pRsKeys = rsLWIN; if ( GetPad(0)->GetRightWinJustDown() ) *pRsKeys = rsRWIN; if ( GetPad(0)->GetAppsJustDown() ) *pRsKeys = rsAPPS; return pRsKeys; } void CPad::FixPadsAfterSave(void) { UpdatePads(); if ( bObsoleteControllerMessage ) { bObsoleteControllerMessage = false; GetPad(0)->Phase = 0; } } ================================================ FILE: src/core/Pad.h ================================================ #pragma once enum { PLAYERCONTROL_ENABLED = 0, PLAYERCONTROL_CAMERA = 1, PLAYERCONTROL_UNK2 = 2, PLAYERCONTROL_GARAGE = 4, PLAYERCONTROL_UNK8 = 8, PLAYERCONTROL_UNK10 = 16, PLAYERCONTROL_PLAYERINFO = 32, PLAYERCONTROL_PHONE = 64, PLAYERCONTROL_CUTSCENE = 128, PLAYERCONTROL_SHORTCUT_TAXI = 256, }; class CControllerState { public: int16 LeftStickX, LeftStickY; int16 RightStickX, RightStickY; int16 LeftShoulder1, LeftShoulder2; int16 RightShoulder1, RightShoulder2; int16 DPadUp, DPadDown, DPadLeft, DPadRight; int16 Start, Select; int16 Square, Triangle, Cross, Circle; int16 LeftShock, RightShock; int16 NetworkTalk; float GetLeftStickX(void) { return LeftStickX/32767.0f; }; float GetLeftStickY(void) { return LeftStickY/32767.0f; }; float GetRightStickX(void) { return RightStickX/32767.0f; }; float GetRightStickY(void) { return RightStickY/32767.0f; }; bool CheckForInput(); void Clear(void); }; VALIDATE_SIZE(CControllerState, 0x2A); class CMouseControllerState { public: //uint32 btns; // bit 0-2 button 1-3 bool LMB; bool RMB; bool MMB; bool WHEELUP; bool WHEELDN; bool MXB1; bool MXB2; char _pad0; float x, y; CMouseControllerState(); void Clear(); }; VALIDATE_SIZE(CMouseControllerState, 0x10); class CMousePointerStateHelper { public: bool bInvertHorizontally; bool bInvertVertically; CMouseControllerState GetMouseSetUp(); }; VALIDATE_SIZE(CMousePointerStateHelper, 0x2); extern CMousePointerStateHelper MousePointerStateHelper; class CKeyboardState { public: int16 F[12]; int16 VK_KEYS[256]; int16 ESC; int16 INS; int16 DEL; int16 HOME; int16 END; int16 PGUP; int16 PGDN; int16 UP; int16 DOWN; int16 LEFT; int16 RIGHT; int16 SCROLLLOCK; int16 PAUSE; int16 NUMLOCK; int16 DIV; int16 MUL; int16 SUB; int16 ADD; int16 ENTER; int16 DECIMAL; int16 NUM1; int16 NUM2; int16 NUM3; int16 NUM4; int16 NUM5; int16 NUM6; int16 NUM7; int16 NUM8; int16 NUM9; int16 NUM0; int16 BACKSP; int16 TAB; int16 CAPSLOCK; int16 EXTENTER; int16 LSHIFT; int16 RSHIFT; int16 SHIFT; int16 LCTRL; int16 RCTRL; int16 LALT; int16 RALT; int16 LWIN; int16 RWIN; int16 APPS; void Clear(); }; VALIDATE_SIZE(CKeyboardState, 0x270); enum { // taken from miss2 PAD1 = 0, PAD2, MAX_PADS }; class CPad { public: enum { HORNHISTORY_SIZE = 5, DRUNK_STEERING_BUFFER_SIZE = 10, }; CControllerState NewState; CControllerState OldState; int16 SteeringLeftRightBuffer[DRUNK_STEERING_BUFFER_SIZE]; int32 DrunkDrivingBufferUsed; CControllerState PCTempKeyState; CControllerState PCTempJoyState; CControllerState PCTempMouseState; // straight out of my IDB int16 Phase; int16 Mode; int16 ShakeDur; uint16 DisablePlayerControls; uint8 ShakeFreq; bool bHornHistory[HORNHISTORY_SIZE]; uint8 iCurrHornHistory; int8 JustOutOfFrontend; int8 bApplyBrakes; char CheatString[12]; int32 LastTimeTouched; int32 AverageWeapon; int32 AverageEntries; #ifdef DETECT_PAD_INPUT_SWITCH static bool IsAffectedByController; #endif CPad() { } ~CPad() { } static bool bDisplayNoControllerMessage; static bool bObsoleteControllerMessage; static bool bOldDisplayNoControllerMessage; static bool m_bMapPadOneToPadTwo; static bool m_bDebugCamPCOn; static bool bHasPlayerCheated; static bool bInvertLook4Pad; static CKeyboardState OldKeyState; static CKeyboardState NewKeyState; static CKeyboardState TempKeyState; static char KeyBoardCheatString[30]; static CMouseControllerState OldMouseControllerState; static CMouseControllerState NewMouseControllerState; static CMouseControllerState PCTempMouseControllerState; #ifdef GTA_PS2_STUFF static void Initialise(void); #endif void Clear(bool bResetPlayerControls); void ClearMouseHistory(); void ClearKeyBoardHistory(); void UpdateMouse(); CControllerState ReconcileTwoControllersInput(CControllerState const &State1, CControllerState const &State2); void StartShake(int16 nDur, uint8 nFreq); void StartShake_Distance(int16 nDur, uint8 nFreq, float fX, float fY, float fz); void StartShake_Train(float fX, float fY); #ifdef GTA_PS2_STUFF void AddToCheatString(char c); #endif void AddToPCCheatString(char c); static void UpdatePads(void); void ProcessPCSpecificStuff(void); void Update(int16 pad); static void DoCheats(void); void DoCheats(int16 unk); static void StopPadsShaking(void); void StopShaking(int16 pad); static CPad *GetPad(int32 pad); int16 GetSteeringLeftRight(void); int16 GetSteeringUpDown(void); int16 GetCarGunUpDown(void); int16 GetCarGunLeftRight(void); int16 GetPedWalkLeftRight(void); int16 GetPedWalkUpDown(void); int16 GetAnalogueUpDown(void); int16 GetAnalogueLeftRight(void); bool GetLookLeft(void); bool GetLookRight(void); bool GetLookBehindForCar(void); bool GetLookBehindForPed(void); bool GetHorn(void); bool HornJustDown(void); bool GetCarGunFired(void); bool CarGunJustDown(void); int16 GetHandBrake(void); int16 GetBrake(void); bool GetExitVehicle(void); bool ExitVehicleJustDown(void); int32 GetWeapon(void); bool WeaponJustDown(void); int16 GetAccelerate(void); bool CycleCameraModeJustDown(void); bool CycleCameraModeUpJustDown(void); bool CycleCameraModeDownJustDown(void); bool ChangeStationJustDown(void); bool CycleWeaponLeftJustDown(void); bool CycleWeaponRightJustDown(void); bool GetTarget(void); bool TargetJustDown(void); bool DuckJustDown(void); bool CollectPickupJustDown(void); bool JumpJustDown(void); bool GetSprint(void); bool ShiftTargetLeftJustDown(void); bool ShiftTargetRightJustDown(void); bool GetAnaloguePadUp(void); bool GetAnaloguePadDown(void); bool GetAnaloguePadLeft(void); bool GetAnaloguePadRight(void); bool GetAnaloguePadLeftJustUp(void); bool GetAnaloguePadRightJustUp(void); bool ForceCameraBehindPlayer(void); bool SniperZoomIn(void); bool SniperZoomOut(void); int16 SniperModeLookLeftRight(void); int16 SniperModeLookUpDown(void); int16 LookAroundLeftRight(void); int16 LookAroundUpDown(void); void ResetAverageWeapon(void); static void FixPadsAfterSave(void); static void PrintErrorMessage(void); static void ResetCheats(void); static char *EditString(char *pStr, int32 nSize); static int32 *EditCodesForControls(int32 *pRsKeys, int32 nSize); uint32 InputHowLongAgo(void); void SetDrunkInputDelay(int32 delay) { DrunkDrivingBufferUsed = delay; } #ifdef XINPUT static int XInputJoy1; static int XInputJoy2; void AffectFromXinput(uint32 pad); #endif // mouse bool GetLeftMouseJustDown() { return !!(NewMouseControllerState.LMB && !OldMouseControllerState.LMB); } bool GetRightMouseJustDown() { return !!(NewMouseControllerState.RMB && !OldMouseControllerState.RMB); } bool GetMiddleMouseJustDown() { return !!(NewMouseControllerState.MMB && !OldMouseControllerState.MMB); } bool GetMouseWheelUpJustDown() { return !!(NewMouseControllerState.WHEELUP && !OldMouseControllerState.WHEELUP); } bool GetMouseWheelDownJustDown() { return !!(NewMouseControllerState.WHEELDN && !OldMouseControllerState.WHEELDN);} bool GetMouseX1JustDown() { return !!(NewMouseControllerState.MXB1 && !OldMouseControllerState.MXB1); } bool GetMouseX2JustDown() { return !!(NewMouseControllerState.MXB2 && !OldMouseControllerState.MXB2); } bool GetLeftMouseJustUp() { return !!(!NewMouseControllerState.LMB && OldMouseControllerState.LMB); } bool GetRightMouseJustUp() { return !!(!NewMouseControllerState.RMB && OldMouseControllerState.RMB); } bool GetMiddleMouseJustUp() { return !!(!NewMouseControllerState.MMB && OldMouseControllerState.MMB); } bool GetMouseWheelUpJustUp() { return !!(!NewMouseControllerState.WHEELUP && OldMouseControllerState.WHEELUP); } bool GetMouseWheelDownJustUp() { return !!(!NewMouseControllerState.WHEELDN && OldMouseControllerState.WHEELDN); } bool GetMouseX1JustUp() { return !!(!NewMouseControllerState.MXB1 && OldMouseControllerState.MXB1); } bool GetMouseX2JustUp() { return !!(!NewMouseControllerState.MXB2 && OldMouseControllerState.MXB2); } bool GetLeftMouse() { return NewMouseControllerState.LMB; } bool GetRightMouse() { return NewMouseControllerState.RMB; } bool GetMiddleMouse() { return NewMouseControllerState.MMB; } bool GetMouseWheelUp() { return NewMouseControllerState.WHEELUP; } bool GetMouseWheelDown() { return NewMouseControllerState.WHEELDN; } bool GetMouseX1() { return NewMouseControllerState.MXB1; } bool GetMouseX2() { return NewMouseControllerState.MXB2; } bool GetLeftMouseUp() { return !OldMouseControllerState.LMB; } bool GetRightMouseUp() { return !OldMouseControllerState.RMB; } bool GetMiddleMouseUp() { return !OldMouseControllerState.MMB; } bool GetMouseWheelUpUp() { return !OldMouseControllerState.WHEELUP; } bool GetMouseWheelDownUp() { return !OldMouseControllerState.WHEELDN; } bool GetMouseX1Up() { return !OldMouseControllerState.MXB1; } bool GetMouseX2Up() { return !OldMouseControllerState.MXB2; } float GetMouseX() { return NewMouseControllerState.x; } float GetMouseY() { return NewMouseControllerState.y; } // keyboard bool GetCharJustDown(int32 c) { return !!(NewKeyState.VK_KEYS[c] && !OldKeyState.VK_KEYS[c]); } bool GetFJustDown(int32 n) { return !!(NewKeyState.F[n] && !OldKeyState.F[n]); } bool GetEscapeJustDown() { return !!(NewKeyState.ESC && !OldKeyState.ESC); } bool GetInsertJustDown() { return !!(NewKeyState.INS && !OldKeyState.INS); } bool GetDeleteJustDown() { return !!(NewKeyState.DEL && !OldKeyState.DEL); } bool GetHomeJustDown() { return !!(NewKeyState.HOME && !OldKeyState.HOME); } bool GetEndJustDown() { return !!(NewKeyState.END && !OldKeyState.END); } bool GetPageUpJustDown() { return !!(NewKeyState.PGUP && !OldKeyState.PGUP); } bool GetPageDownJustDown() { return !!(NewKeyState.PGDN && !OldKeyState.PGDN); } bool GetUpJustDown() { return !!(NewKeyState.UP && !OldKeyState.UP); } bool GetDownJustDown() { return !!(NewKeyState.DOWN && !OldKeyState.DOWN); } bool GetLeftJustDown() { return !!(NewKeyState.LEFT && !OldKeyState.LEFT); } bool GetRightJustDown() { return !!(NewKeyState.RIGHT && !OldKeyState.RIGHT); } bool GetScrollLockJustDown() { return !!(NewKeyState.SCROLLLOCK && !OldKeyState.SCROLLLOCK); } bool GetPauseJustDown() { return !!(NewKeyState.PAUSE && !OldKeyState.PAUSE); } bool GetNumLockJustDown() { return !!(NewKeyState.NUMLOCK && !OldKeyState.NUMLOCK); } bool GetDivideJustDown() { return !!(NewKeyState.DIV && !OldKeyState.DIV); } bool GetTimesJustDown() { return !!(NewKeyState.MUL && !OldKeyState.MUL); } bool GetMinusJustDown() { return !!(NewKeyState.SUB && !OldKeyState.SUB); } bool GetPlusJustDown() { return !!(NewKeyState.ADD && !OldKeyState.ADD); } bool GetPadEnterJustDown() { return !!(NewKeyState.ENTER && !OldKeyState.ENTER); } bool GetPadDelJustDown() { return !!(NewKeyState.DECIMAL && !OldKeyState.DECIMAL); } bool GetPad1JustDown() { return !!(NewKeyState.NUM1 && !OldKeyState.NUM1); } bool GetPad2JustDown() { return !!(NewKeyState.NUM2 && !OldKeyState.NUM2); } bool GetPad3JustDown() { return !!(NewKeyState.NUM3 && !OldKeyState.NUM3); } bool GetPad4JustDown() { return !!(NewKeyState.NUM4 && !OldKeyState.NUM4); } bool GetPad5JustDown() { return !!(NewKeyState.NUM5 && !OldKeyState.NUM5); } bool GetPad6JustDown() { return !!(NewKeyState.NUM6 && !OldKeyState.NUM6); } bool GetPad7JustDown() { return !!(NewKeyState.NUM7 && !OldKeyState.NUM7); } bool GetPad8JustDown() { return !!(NewKeyState.NUM8 && !OldKeyState.NUM8); } bool GetPad9JustDown() { return !!(NewKeyState.NUM9 && !OldKeyState.NUM9); } bool GetPad0JustDown() { return !!(NewKeyState.NUM0 && !OldKeyState.NUM0); } bool GetBackspaceJustDown() { return !!(NewKeyState.BACKSP && !OldKeyState.BACKSP); } bool GetTabJustDown() { return !!(NewKeyState.TAB && !OldKeyState.TAB); } bool GetCapsLockJustDown() { return !!(NewKeyState.CAPSLOCK && !OldKeyState.CAPSLOCK); } bool GetReturnJustDown() { return !!(NewKeyState.EXTENTER && !OldKeyState.EXTENTER); } bool GetLeftShiftJustDown() { return !!(NewKeyState.LSHIFT && !OldKeyState.LSHIFT); } bool GetShiftJustDown() { return !!(NewKeyState.SHIFT && !OldKeyState.SHIFT); } bool GetRightShiftJustDown() { return !!(NewKeyState.RSHIFT && !OldKeyState.RSHIFT); } bool GetLeftCtrlJustDown() { return !!(NewKeyState.LCTRL && !OldKeyState.LCTRL); } bool GetRightCtrlJustDown() { return !!(NewKeyState.RCTRL && !OldKeyState.RCTRL); } bool GetLeftAltJustDown() { return !!(NewKeyState.LALT && !OldKeyState.LALT); } bool GetRightAltJustDown() { return !!(NewKeyState.RALT && !OldKeyState.RALT); } bool GetLeftWinJustDown() { return !!(NewKeyState.LWIN && !OldKeyState.LWIN); } bool GetRightWinJustDown() { return !!(NewKeyState.RWIN && !OldKeyState.RWIN); } bool GetAppsJustDown() { return !!(NewKeyState.APPS && !OldKeyState.APPS); } bool GetEnterJustDown() { return GetPadEnterJustDown() || GetReturnJustDown(); } bool GetAltJustDown() { return GetLeftAltJustDown() || GetRightAltJustDown(); } bool GetLeftJustUp() { return !!(!NewKeyState.LEFT && OldKeyState.LEFT); } bool GetRightJustUp() { return !!(!NewKeyState.RIGHT && OldKeyState.RIGHT); } bool GetEnterJustUp() { return GetPadEnterJustUp() || GetReturnJustUp(); } bool GetReturnJustUp() { return !!(!NewKeyState.EXTENTER && OldKeyState.EXTENTER); } bool GetPadEnterJustUp() { return !!(!NewKeyState.ENTER && OldKeyState.ENTER); } bool GetChar(int32 c) { return NewKeyState.VK_KEYS[c]; } bool GetF(int32 n) { return NewKeyState.F[n]; } bool GetEscape() { return NewKeyState.ESC; } bool GetInsert() { return NewKeyState.INS; } bool GetDelete() { return NewKeyState.DEL; } bool GetHome() { return NewKeyState.HOME; } bool GetEnd() { return NewKeyState.END; } bool GetPageUp() { return NewKeyState.PGUP; } bool GetPageDown() { return NewKeyState.PGDN; } bool GetUp() { return NewKeyState.UP; } bool GetDown() { return NewKeyState.DOWN; } bool GetLeft() { return NewKeyState.LEFT; } bool GetRight() { return NewKeyState.RIGHT; } bool GetScrollLock() { return NewKeyState.SCROLLLOCK; } bool GetPause() { return NewKeyState.PAUSE; } bool GetNumLock() { return NewKeyState.NUMLOCK; } bool GetDivide() { return NewKeyState.DIV; } bool GetTimes() { return NewKeyState.MUL; } bool GetMinus() { return NewKeyState.SUB; } bool GetPlus() { return NewKeyState.ADD; } bool GetPadEnter() { return NewKeyState.ENTER; } // GetEnterJustDown bool GetPadDel() { return NewKeyState.DECIMAL; } bool GetPad1() { return NewKeyState.NUM1; } bool GetPad2() { return NewKeyState.NUM2; } bool GetPad3() { return NewKeyState.NUM3; } bool GetPad4() { return NewKeyState.NUM4; } bool GetPad5() { return NewKeyState.NUM5; } bool GetPad6() { return NewKeyState.NUM6; } bool GetPad7() { return NewKeyState.NUM7; } bool GetPad8() { return NewKeyState.NUM8; } bool GetPad9() { return NewKeyState.NUM9; } bool GetPad0() { return NewKeyState.NUM0; } bool GetBackspace() { return NewKeyState.BACKSP; } bool GetTab() { return NewKeyState.TAB; } bool GetCapsLock() { return NewKeyState.CAPSLOCK; } bool GetEnter() { return NewKeyState.EXTENTER; } bool GetLeftShift() { return NewKeyState.LSHIFT; } bool GetShift() { return NewKeyState.SHIFT; } bool GetRightShift() { return NewKeyState.RSHIFT; } bool GetLeftCtrl() { return NewKeyState.LCTRL; } bool GetRightCtrl() { return NewKeyState.RCTRL; } bool GetLeftAlt() { return NewKeyState.LALT; } bool GetRightAlt() { return NewKeyState.RALT; } bool GetLeftWin() { return NewKeyState.LWIN; } bool GetRightWin() { return NewKeyState.RWIN; } bool GetApps() { return NewKeyState.APPS; } // pad bool GetTriangleJustDown() { return !!(NewState.Triangle && !OldState.Triangle); } bool GetCircleJustDown() { return !!(NewState.Circle && !OldState.Circle); } bool GetCrossJustDown() { return !!(NewState.Cross && !OldState.Cross); } bool GetSquareJustDown() { return !!(NewState.Square && !OldState.Square); } bool GetDPadUpJustDown() { return !!(NewState.DPadUp && !OldState.DPadUp); } bool GetDPadDownJustDown() { return !!(NewState.DPadDown && !OldState.DPadDown); } bool GetDPadLeftJustDown() { return !!(NewState.DPadLeft && !OldState.DPadLeft); } bool GetDPadRightJustDown() { return !!(NewState.DPadRight && !OldState.DPadRight); } bool GetLeftShoulder1JustDown() { return !!(NewState.LeftShoulder1 && !OldState.LeftShoulder1); } bool GetLeftShoulder2JustDown() { return !!(NewState.LeftShoulder2 && !OldState.LeftShoulder2); } bool GetRightShoulder1JustDown() { return !!(NewState.RightShoulder1 && !OldState.RightShoulder1); } bool GetRightShoulder2JustDown() { return !!(NewState.RightShoulder2 && !OldState.RightShoulder2); } bool GetLeftShockJustDown() { return !!(NewState.LeftShock && !OldState.LeftShock); } bool GetRightShockJustDown() { return !!(NewState.RightShock && !OldState.RightShock); } bool GetStartJustDown() { return !!(NewState.Start && !OldState.Start); } bool GetSelectJustDown() { return !!(NewState.Select && !OldState.Select); } bool GetLeftStickXJustDown() { return !!(NewState.LeftStickX && !OldState.LeftStickX); } bool GetLeftStickYJustDown() { return !!(NewState.LeftStickY && !OldState.LeftStickY); } bool GetTriangleJustUp() { return !!(!NewState.Triangle && OldState.Triangle); } bool GetCircleJustUp() { return !!(!NewState.Circle && OldState.Circle); } bool GetCrossJustUp() { return !!(!NewState.Cross && OldState.Cross); } bool GetSquareJustUp() { return !!(!NewState.Square && OldState.Square); } bool GetDPadUpJustUp() { return !!(!NewState.DPadUp && OldState.DPadUp); } bool GetDPadDownJustUp() { return !!(!NewState.DPadDown && OldState.DPadDown); } bool GetDPadLeftJustUp() { return !!(!NewState.DPadLeft && OldState.DPadLeft); } bool GetDPadRightJustUp() { return !!(!NewState.DPadRight && OldState.DPadRight); } bool GetTriangle() { return !!NewState.Triangle; } bool GetCircle() { return !!NewState.Circle; } bool GetCross() { return !!NewState.Cross; } bool GetSquare() { return !!NewState.Square; } bool GetDPadUp() { return !!NewState.DPadUp; } bool GetDPadDown() { return !!NewState.DPadDown; } bool GetDPadLeft() { return !!NewState.DPadLeft; } bool GetDPadRight() { return !!NewState.DPadRight; } bool GetLeftShoulder1(void) { return !!NewState.LeftShoulder1; } bool GetLeftShoulder2(void) { return !!NewState.LeftShoulder2; } bool GetRightShoulder1(void) { return !!NewState.RightShoulder1; } bool GetRightShoulder2(void) { return !!NewState.RightShoulder2; } bool GetStart() { return !!NewState.Start; } bool GetSelect() { return !!NewState.Select; } int16 GetLeftStickX(void) { return NewState.LeftStickX; } int16 GetLeftStickY(void) { return NewState.LeftStickY; } int16 GetRightStickX(void) { return NewState.RightStickX; } int16 GetRightStickY(void) { return NewState.RightStickY; } bool ArePlayerControlsDisabled(void) { return DisablePlayerControls != PLAYERCONTROL_ENABLED; } void SetDisablePlayerControls(uint16 who) { DisablePlayerControls |= who; } void SetEnablePlayerControls(uint16 who) { DisablePlayerControls &= ~who; } bool IsPlayerControlsDisabledBy(uint16 who) { return DisablePlayerControls & who; } int16 GetMode() { return Mode; } void SetMode(int16 mode) { Mode = mode; } static bool IsNoOrObsolete() { return bDisplayNoControllerMessage || bObsoleteControllerMessage; } }; VALIDATE_SIZE(CPad, 0xFC); extern CPad Pads[MAX_PADS]; ================================================ FILE: src/core/Placeable.cpp ================================================ #include "common.h" #include "Placeable.h" CPlaceable::CPlaceable(void) { m_matrix.SetScale(1.0f); } void CPlaceable::SetHeading(float angle) { CVector pos = GetMatrix().GetPosition(); m_matrix.SetRotateZ(angle); GetMatrix().Translate(pos); } bool CPlaceable::IsWithinArea(float x1, float y1, float x2, float y2) { float tmp; if(x1 > x2){ tmp = x1; x1 = x2; x2 = tmp; } if(y1 > y2){ tmp = y1; y1 = y2; y2 = tmp; } return x1 <= GetPosition().x && GetPosition().x <= x2 && y1 <= GetPosition().y && GetPosition().y <= y2; } bool CPlaceable::IsWithinArea(float x1, float y1, float z1, float x2, float y2, float z2) { float tmp; if(x1 > x2){ tmp = x1; x1 = x2; x2 = tmp; } if(y1 > y2){ tmp = y1; y1 = y2; y2 = tmp; } if(z1 > z2){ tmp = z1; z1 = z2; z2 = tmp; } return x1 <= GetPosition().x && GetPosition().x <= x2 && y1 <= GetPosition().y && GetPosition().y <= y2 && z1 <= GetPosition().z && GetPosition().z <= z2; } ================================================ FILE: src/core/Placeable.h ================================================ #pragma once class CPlaceable { public: // disable allocation static void *operator new(size_t); CMatrix m_matrix; CPlaceable(void); const CVector &GetPosition(void) { return m_matrix.GetPosition(); } void SetPosition(float x, float y, float z) { m_matrix.GetPosition().x = x; m_matrix.GetPosition().y = y; m_matrix.GetPosition().z = z; } void SetPosition(const CVector &pos) { m_matrix.GetPosition() = pos; } CVector &GetRight(void) { return m_matrix.GetRight(); } CVector &GetForward(void) { return m_matrix.GetForward(); } CVector &GetUp(void) { return m_matrix.GetUp(); } CMatrix &GetMatrix(void) { return m_matrix; } void SetTransform(RwMatrix *m) { m_matrix = CMatrix(m, false); } void SetHeading(float angle); void SetOrientation(float x, float y, float z){ CVector pos = m_matrix.GetPosition(); m_matrix.SetRotate(x, y, z); m_matrix.Translate(pos); } bool IsWithinArea(float x1, float y1, float x2, float y2); bool IsWithinArea(float x1, float y1, float z1, float x2, float y2, float z2); }; VALIDATE_SIZE(CPlaceable, 0x4C); ================================================ FILE: src/core/PlayerInfo.cpp ================================================ #include "common.h" #include "Automobile.h" #include "Bridge.h" #include "Camera.h" #include "CarCtrl.h" #include "Cranes.h" #include "Darkel.h" #include "Explosion.h" #include "Fire.h" #include "Frontend.h" #include "General.h" #include "HandlingMgr.h" #include "Messages.h" #include "Pad.h" #include "PathFind.h" #include "PlayerInfo.h" #include "PlayerPed.h" #include "PlayerSkin.h" #include "ProjectileInfo.h" #include "Remote.h" #include "Renderer.h" #include "Replay.h" #include "Script.h" #include "SpecialFX.h" #include "Stats.h" #include "Streaming.h" #include "Text.h" #include "Wanted.h" #include "WaterLevel.h" #include "World.h" #include "ZoneCull.h" #include "main.h" #include "Bike.h" #include "Automobile.h" #include "GameLogic.h" CVector lastPlayerPos; void CPlayerInfo::Clear(void) { m_pPed = nil; m_pRemoteVehicle = nil; if (m_pVehicleEx) { m_pVehicleEx->bUsingSpecialColModel = false; m_pVehicleEx = nil; } m_nVisibleMoney = 0; m_nMoney = m_nVisibleMoney; m_WBState = WBSTATE_PLAYING; m_nWBTime = 0; m_nTrafficMultiplier = 0; m_fRoadDensity = 1.0f; m_bInRemoteMode = false; field_D5 = false; field_D6 = false; m_bUnusedTaxiThing = false; m_nUnusedTaxiTimer = 0; m_nCollectedPackages = 0; m_nTotalPackages = 3; m_nTimeLastHealthLoss = 0; m_nTimeLastArmourLoss = 0; m_nNextSexFrequencyUpdateTime = 0; m_nNextSexMoneyUpdateTime = 0; m_nSexFrequency = 0; m_pHooker = nil; m_nTimeTankShotGun = 0; field_EC = 0; m_nUpsideDownCounter = 0; m_nTimeCarSpentOnTwoWheels = 0; m_nDistanceCarTravelledOnTwoWheels = 0; m_nTimeNotFullyOnGround = 0; m_nTimeSpentOnWheelie = 0; m_nDistanceTravelledOnWheelie = 0.0f; m_nTimeSpentOnStoppie = 0; m_nDistanceTravelledOnStoppie = 0.0f; m_nCancelWheelStuntTimer = 0; m_nLastTimeCarSpentOnTwoWheels = 0; m_nLastDistanceCarTravelledOnTwoWheels = 0; m_nLastTimeSpentOnWheelie = 0; m_nLastDistanceTravelledOnWheelie = 0; m_nLastTimeSpentOnStoppie = 0; m_nLastDistanceTravelledOnStoppie = 0; m_bInfiniteSprint = false; m_bFastReload = false; m_bFireproof = false; m_nMaxHealth = m_nMaxArmour = 100; m_bGetOutOfJailFree = false; m_bGetOutOfHospitalFree = false; m_bDriveByAllowed = true; m_nPreviousTimeRewardedForExplosion = 0; m_nExplosionsSinceLastReward = 0; m_nHavocLevel = 0; m_fMediaAttention = 0.0f; m_nCurrentBustedAudio = 1; m_nBustedAudioStatus = BUSTEDAUDIO_NONE; } void CPlayerInfo::Process(void) { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return; #endif // Unused taxi feature. Gives you a dollar for every second with a passenger. Can be toggled via 0x29A opcode. bool startTaxiTimer = true; if (m_bUnusedTaxiThing && m_pPed->bInVehicle) { CVehicle *veh = m_pPed->m_pMyVehicle; if (veh->IsTaxi() && veh->pDriver == m_pPed && veh->m_nNumPassengers != 0) { for (uint32 timePassed = CTimer::GetTimeInMilliseconds() - m_nUnusedTaxiTimer; timePassed >= 1000; m_nUnusedTaxiTimer += 1000) { timePassed -= 1000; ++m_nMoney; } startTaxiTimer = false; } } if (startTaxiTimer) m_nUnusedTaxiTimer = CTimer::GetTimeInMilliseconds(); if (!m_pPed->InVehicle()) { m_nTimeCarSpentOnTwoWheels = 0; m_nTimeNotFullyOnGround = 0; m_nTimeSpentOnWheelie = 0; m_nTimeSpentOnStoppie = 0; m_nCancelWheelStuntTimer = 0; } else if (m_pPed->m_pMyVehicle->IsCar()) { CAutomobile *car = (CAutomobile*)m_pPed->m_pMyVehicle; if (car->m_nWheelsOnGround < 3) m_nTimeNotFullyOnGround += CTimer::GetTimeStepInMilliseconds(); else m_nTimeNotFullyOnGround = 0; if (car->m_aSuspensionSpringRatioPrev[2] == 1.f && car->m_aSuspensionSpringRatioPrev[3] == 1.f) { if (car->m_aSuspensionSpringRatioPrev[0] < 1.0f && car->m_aSuspensionSpringRatioPrev[1] < 1.0f && car->m_fDamageImpulse == 0.0f) { m_nTimeCarSpentOnTwoWheels += CTimer::GetTimeStepInMilliseconds(); m_nDistanceCarTravelledOnTwoWheels += car->m_fDistanceTravelled; m_nCancelWheelStuntTimer = Max(0.0f, m_nCancelWheelStuntTimer - CTimer::GetTimeStepInMilliseconds() * 0.5f); } else { if (m_nTimeCarSpentOnTwoWheels != 0 && m_nCancelWheelStuntTimer < 500) { m_nCancelWheelStuntTimer += CTimer::GetTimeStepInMilliseconds(); } else { if (m_nTimeCarSpentOnTwoWheels >= 2000) { m_nLastTimeCarSpentOnTwoWheels = m_nTimeCarSpentOnTwoWheels; m_nLastDistanceCarTravelledOnTwoWheels = m_nDistanceCarTravelledOnTwoWheels; if (CStats::Longest2Wheel < m_nTimeCarSpentOnTwoWheels / 1000) CStats::Longest2Wheel = m_nTimeCarSpentOnTwoWheels / 1000; if (CStats::Longest2WheelDist < m_nDistanceCarTravelledOnTwoWheels) CStats::Longest2WheelDist = m_nDistanceCarTravelledOnTwoWheels; } m_nTimeCarSpentOnTwoWheels = 0; m_nDistanceCarTravelledOnTwoWheels = 0; m_nCancelWheelStuntTimer = 0; } } } else if (car->m_aSuspensionSpringRatioPrev[0] == 1.0f && car->m_aSuspensionSpringRatioPrev[1] == 1.0f) { #ifdef FIX_BUGS if (car->m_aSuspensionSpringRatioPrev[2] < 1.f #else if (car->m_aSuspensionSpringRatioPrev[1] < 1.f #endif && car->m_aSuspensionSpringRatioPrev[3] < 1.f && 0.0f == car->m_fDamageImpulse) { m_nTimeCarSpentOnTwoWheels += CTimer::GetTimeStepInMilliseconds(); m_nDistanceCarTravelledOnTwoWheels += car->m_fDistanceTravelled; m_nCancelWheelStuntTimer = Max(0.0f, m_nCancelWheelStuntTimer - CTimer::GetTimeStepInMilliseconds() * 0.2f); } else if (m_nTimeCarSpentOnTwoWheels != 0 && m_nCancelWheelStuntTimer < 500) { m_nCancelWheelStuntTimer += CTimer::GetTimeStepInMilliseconds(); } else { if (m_nTimeCarSpentOnTwoWheels >= 2000) { m_nLastTimeCarSpentOnTwoWheels = m_nTimeCarSpentOnTwoWheels; m_nLastDistanceCarTravelledOnTwoWheels = m_nDistanceCarTravelledOnTwoWheels; if (CStats::Longest2Wheel < m_nTimeCarSpentOnTwoWheels / 1000) CStats::Longest2Wheel = m_nTimeCarSpentOnTwoWheels / 1000; if (CStats::Longest2WheelDist < m_nDistanceCarTravelledOnTwoWheels) CStats::Longest2WheelDist = m_nDistanceCarTravelledOnTwoWheels; } m_nTimeCarSpentOnTwoWheels = 0; m_nDistanceCarTravelledOnTwoWheels = 0; m_nCancelWheelStuntTimer = 0; } } else if (m_nTimeCarSpentOnTwoWheels != 0) { if (m_nTimeCarSpentOnTwoWheels >= 2000) { m_nLastTimeCarSpentOnTwoWheels = m_nTimeCarSpentOnTwoWheels; m_nLastDistanceCarTravelledOnTwoWheels = m_nDistanceCarTravelledOnTwoWheels; if (CStats::Longest2Wheel < m_nTimeCarSpentOnTwoWheels / 1000) CStats::Longest2Wheel = m_nTimeCarSpentOnTwoWheels / 1000; if (CStats::Longest2WheelDist < m_nDistanceCarTravelledOnTwoWheels) CStats::Longest2WheelDist = m_nDistanceCarTravelledOnTwoWheels; } m_nTimeCarSpentOnTwoWheels = 0; m_nDistanceCarTravelledOnTwoWheels = 0; m_nCancelWheelStuntTimer = 0; } m_nTimeSpentOnWheelie = 0; m_nTimeSpentOnStoppie = 0; } else if (m_pPed->m_pMyVehicle->IsBike()) { CBike *bike = (CBike*)m_pPed->m_pMyVehicle; if (bike->m_aSuspensionSpringRatioPrev[0] == 1.0f && bike->m_aSuspensionSpringRatioPrev[1] == 1.0f) { if (bike->m_aSuspensionSpringRatioPrev[2] < 1.0f || (bike->m_aSuspensionSpringRatioPrev[3] < 1.0f && 0.0f == bike->m_fDamageImpulse)) { m_nTimeSpentOnWheelie += CTimer::GetTimeStepInMilliseconds(); m_nDistanceTravelledOnWheelie += bike->m_fDistanceTravelled; m_nCancelWheelStuntTimer = Max(0.0f, m_nCancelWheelStuntTimer - CTimer::GetTimeStepInMilliseconds() * 0.2f); } else { if (m_nTimeSpentOnWheelie != 0 && m_nCancelWheelStuntTimer < 500) { m_nCancelWheelStuntTimer += CTimer::GetTimeStepInMilliseconds(); } else { if (m_nTimeSpentOnWheelie >= 5000) { m_nLastTimeSpentOnWheelie = m_nTimeSpentOnWheelie; m_nLastDistanceTravelledOnWheelie = m_nDistanceTravelledOnWheelie; if (CStats::LongestWheelie < m_nTimeSpentOnWheelie / 1000) CStats::LongestWheelie = m_nTimeSpentOnWheelie / 1000; if (CStats::LongestWheelieDist < m_nDistanceTravelledOnWheelie) CStats::LongestWheelieDist = m_nDistanceTravelledOnWheelie; } m_nTimeSpentOnWheelie = 0; m_nDistanceTravelledOnWheelie = 0; m_nCancelWheelStuntTimer = 0; } } } else if (m_nTimeSpentOnWheelie != 0) { if (m_nTimeSpentOnWheelie >= 5000) { m_nLastTimeSpentOnWheelie = m_nTimeSpentOnWheelie; m_nLastDistanceTravelledOnWheelie = m_nDistanceTravelledOnWheelie; if (CStats::LongestWheelie < m_nTimeSpentOnWheelie / 1000) CStats::LongestWheelie = m_nTimeSpentOnWheelie / 1000; if (CStats::LongestWheelieDist < m_nDistanceTravelledOnWheelie) CStats::LongestWheelieDist = m_nDistanceTravelledOnWheelie; } m_nTimeSpentOnWheelie = 0; m_nDistanceTravelledOnWheelie = 0; m_nCancelWheelStuntTimer = 0; } else if (bike->m_aSuspensionSpringRatioPrev[2] == 1.0f && bike->m_aSuspensionSpringRatioPrev[3] == 1.0f && 0.0f == bike->m_fDamageImpulse) { m_nTimeSpentOnStoppie += CTimer::GetTimeStepInMilliseconds(); m_nDistanceTravelledOnStoppie += bike->m_fDistanceTravelled; m_nCancelWheelStuntTimer = Max(0.0f, m_nCancelWheelStuntTimer - CTimer::GetTimeStepInMilliseconds() * 0.2f); } else { if (m_nTimeSpentOnStoppie != 0 && m_nCancelWheelStuntTimer < 500) { m_nCancelWheelStuntTimer += CTimer::GetTimeStepInMilliseconds(); } else { if (m_nTimeSpentOnStoppie >= 2000) { m_nLastTimeSpentOnStoppie = m_nTimeSpentOnStoppie; m_nLastDistanceTravelledOnStoppie = m_nDistanceTravelledOnStoppie; if (CStats::LongestStoppie < m_nTimeSpentOnStoppie / 1000) CStats::LongestStoppie = m_nTimeSpentOnStoppie / 1000; if (CStats::LongestStoppieDist < m_nDistanceTravelledOnStoppie) CStats::LongestStoppieDist = m_nDistanceTravelledOnStoppie; } m_nTimeSpentOnStoppie = 0; m_nDistanceTravelledOnStoppie = 0; m_nCancelWheelStuntTimer = 0; } } m_nTimeCarSpentOnTwoWheels = 0; m_nTimeNotFullyOnGround = 0; } else { m_nTimeCarSpentOnTwoWheels = 0; m_nTimeNotFullyOnGround = 0; m_nTimeSpentOnWheelie = 0; m_nTimeSpentOnStoppie = 0; m_nCancelWheelStuntTimer = 0; } // The effect that makes money counter does while earning/losing money if (m_nVisibleMoney != m_nMoney) { int diff = m_nMoney - m_nVisibleMoney; int diffAbs = Abs(diff); int changeBy; if (diffAbs > 100000) changeBy = 12345; else if (diffAbs > 10000) changeBy = 1234; else if (diffAbs > 1000) changeBy = 123; else if (diffAbs > 50) changeBy = 42; else changeBy = 1; if (diff < 0) m_nVisibleMoney -= changeBy; else m_nVisibleMoney += changeBy; } if (!(CTimer::GetFrameCounter() & 15)) { CVector2D playerPos = m_pPed->bInVehicle ? m_pPed->m_pMyVehicle->GetPosition() : m_pPed->GetPosition(); m_fRoadDensity = ThePaths.CalcRoadDensity(playerPos.x, playerPos.y); } m_fRoadDensity = clamp(m_fRoadDensity, 0.5f, 1.45f); // Because vehicle enter/exit use same key binding. bool enterOrExitVeh; if (m_pPed->bVehExitWillBeInstant && m_pPed->bInVehicle) enterOrExitVeh = CPad::GetPad(0)->ExitVehicleJustDown(); else enterOrExitVeh = CPad::GetPad(0)->GetExitVehicle(); if (enterOrExitVeh && m_pPed->m_nPedState != PED_ANSWER_MOBILE && m_pPed->m_nPedState != PED_SNIPER_MODE && m_pPed->m_nPedState != PED_ROCKET_MODE) { if (m_pPed->bInVehicle) { if (!m_pRemoteVehicle) { CEntity *surfaceBelowVeh = m_pPed->m_pMyVehicle->m_pCurGroundEntity; if (!surfaceBelowVeh || !CBridge::ThisIsABridgeObjectMovingUp(surfaceBelowVeh->GetModelIndex())) { CVehicle *veh = m_pPed->m_pMyVehicle; if (!veh->IsBoat() || veh->m_nDoorLock == CARLOCK_LOCKED_PLAYER_INSIDE) { if (veh->GetStatus() != STATUS_WRECKED && veh->GetStatus() != STATUS_TRAIN_MOVING && veh->m_nDoorLock != CARLOCK_LOCKED_PLAYER_INSIDE) { bool canJumpOff = false; if (veh->m_vehType == VEHICLE_TYPE_BIKE) { canJumpOff = veh->CanPedJumpOffBike(); } else if (veh->pDriver == m_pPed) { canJumpOff = veh->CanPedJumpOutCar(); } if (canJumpOff || veh->m_vecMoveSpeed.Magnitude() < 0.1f) { if (!veh->bIsInWater) m_pPed->SetObjective(OBJECTIVE_LEAVE_CAR, veh); } else if (veh->GetStatus() != STATUS_PLAYER && veh != CGameLogic::pShortCutTaxi) { veh->AutoPilot.m_nTempAction = TEMPACT_WAIT; veh->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 1000; } } } else { m_pPed->SetExitBoat(veh); m_pPed->bTryingToReachDryLand = true; } } } } else { // Enter vehicle if (CPad::GetPad(0)->ExitVehicleJustDown()) { bool weAreOnBoat = false; float lastCloseness = 0.0f; CVehicle *carBelow = nil; CEntity *surfaceBelow = m_pPed->m_pCurrentPhysSurface; if (surfaceBelow && surfaceBelow->IsVehicle()) { carBelow = (CVehicle*)surfaceBelow; if (carBelow->IsBoat() && carBelow->m_modelIndex != MI_SKIMMER) { weAreOnBoat = true; m_pPed->bOnBoat = true; if (carBelow->GetStatus() != STATUS_WRECKED && carBelow->GetUp().z > 0.3f) m_pPed->SetSeekBoatPosition(carBelow); } } // Find closest car if (!weAreOnBoat) { float minX = m_pPed->GetPosition().x - 10.0f; float maxX = 10.0f + m_pPed->GetPosition().x; float minY = m_pPed->GetPosition().y - 10.0f; float maxY = 10.0f + m_pPed->GetPosition().y; int minXSector = CWorld::GetSectorIndexX(minX); if (minXSector < 0) minXSector = 0; int minYSector = CWorld::GetSectorIndexY(minY); if (minYSector < 0) minYSector = 0; int maxXSector = CWorld::GetSectorIndexX(maxX); if (maxXSector > NUMSECTORS_X - 1) maxXSector = NUMSECTORS_X - 1; int maxYSector = CWorld::GetSectorIndexY(maxY); if (maxYSector > NUMSECTORS_Y - 1) maxYSector = NUMSECTORS_Y - 1; CWorld::AdvanceCurrentScanCode(); for (int curY = minYSector; curY <= maxYSector; curY++) { for (int curX = minXSector; curX <= maxXSector; curX++) { CSector *sector = CWorld::GetSector(curX, curY); FindClosestCarSectorList(sector->m_lists[ENTITYLIST_VEHICLES], m_pPed, minX, minY, maxX, maxY, &lastCloseness, &carBelow); FindClosestCarSectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], m_pPed, minX, minY, maxX, maxY, &lastCloseness, &carBelow); } } } // carBelow is now closest vehicle if (carBelow && !weAreOnBoat) { if (carBelow->GetStatus() == STATUS_TRAIN_NOT_MOVING) { m_pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, carBelow); } else if (carBelow->IsBoat()) { if (!carBelow->pDriver) { m_pPed->m_vehDoor = 0; m_pPed->SetEnterCar(carBelow, m_pPed->m_vehDoor); } } else { m_pPed->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, carBelow); } } } } } if (m_bInRemoteMode) { uint32 timeWithoutRemoteCar = CTimer::GetTimeInMilliseconds() - m_nTimeLostRemoteCar; if (CTimer::GetPreviousTimeInMilliseconds() - m_nTimeLostRemoteCar < 1000 && timeWithoutRemoteCar >= 1000 && m_WBState == WBSTATE_PLAYING && field_D6) { TheCamera.SetFadeColour(0, 0, 0); TheCamera.Fade(1.0f, FADE_OUT); } if (timeWithoutRemoteCar > 2000) { if (m_WBState == WBSTATE_PLAYING && field_D6) { TheCamera.RestoreWithJumpCut(); TheCamera.SetFadeColour(0, 0, 0); TheCamera.Fade(1.0f, FADE_IN); TheCamera.Process(); CTimer::Stop(); CCullZones::ForceCullZoneCoors(TheCamera.GetPosition()); CRenderer::RequestObjectsInFrustum(); CStreaming::LoadAllRequestedModels(false); CTimer::Update(); } m_bInRemoteMode = false; CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->bRemoveFromWorld = true; CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle = nil; if (FindPlayerVehicle()) { FindPlayerVehicle()->SetStatus(STATUS_PLAYER); } } } if (!(CTimer::GetFrameCounter() & 31)) { CVehicle *veh = FindPlayerVehicle(); if (veh && m_pPed->bInVehicle && veh->GetUp().z < 0.0f && veh->m_vecMoveSpeed.Magnitude() < 0.05f && (veh->IsCar() || veh->IsBoat()) && !veh->bIsInWater) { if (veh->GetUp().z < -0.5f) { m_nUpsideDownCounter += 2; } else { m_nUpsideDownCounter++; } } else { m_nUpsideDownCounter = 0; } if (m_nUpsideDownCounter > 6 && veh->bCanBeDamaged) { veh->m_fHealth = 249.0f < veh->m_fHealth ? 249.0f : veh->m_fHealth; if (veh->IsCar()) { CAutomobile* car = (CAutomobile*)veh; car->Damage.SetEngineStatus(225); car->m_pSetOnFireEntity = nil; } } } if (FindPlayerVehicle()) { CVehicle *veh = FindPlayerVehicle(); veh->m_nZoneLevel = LEVEL_IGNORE; for (int i = 0; i < ARRAY_SIZE(veh->pPassengers); i++) { if (veh->pPassengers[i]) veh->pPassengers[i]->m_nZoneLevel = LEVEL_GENERIC; } if(veh->m_modelIndex == MI_CADDY) CStats::DistanceTravelledByGolfCart += veh->m_fDistanceTravelled; else { if(veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI) CStats::DistanceTravelledByHelicoptor += veh->m_fDistanceTravelled; if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) CStats::DistanceTravelledByPlane += veh->m_fDistanceTravelled; if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) CStats::DistanceTravelledByCar += veh->m_fDistanceTravelled; if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_BIKE) CStats::DistanceTravelledByBike += veh->m_fDistanceTravelled; if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT) CStats::DistanceTravelledByBoat += veh->m_fDistanceTravelled; if (veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) { if (veh->m_vecMoveSpeed.Magnitude() > 0.2f) { CStats::FlightTime += CTimer::GetTimeStep() * 16.f; // what a weird choice } } } } else { CStats::DistanceTravelledOnFoot += FindPlayerPed()->m_fDistanceTravelled; } if (m_pPed->m_pWanted->GetWantedLevel() && !CTheScripts::IsPlayerOnAMission()) { float maxDelta = 0.0f; static bool movedSignificantly = true; static bool thereIsACarPathNear = true; // there was one more guard without variable's itself??? if (CTimer::GetTimeInMilliseconds() / 20000 != CTimer::GetPreviousTimeInMilliseconds() / 20000) { float posChange = (lastPlayerPos - FindPlayerCoors()).Magnitude(); movedSignificantly = posChange >= 10.0f; lastPlayerPos = FindPlayerCoors(); thereIsACarPathNear = ThePaths.FindNodeClosestToCoors(FindPlayerCoors(), PATH_CAR, 60.0f, true, false, false, false) != 0; } switch (m_pPed->m_pWanted->GetWantedLevel()) { case 1: maxDelta = 31.f; break; case 2: maxDelta = 62.f; break; case 3: maxDelta = 125.f; break; case 4: maxDelta = 250.f; break; case 5: maxDelta = 500.f; break; case 6: maxDelta = 1000.f; break; default: break; } float increaseDelta = maxDelta - m_fMediaAttention; float increaseAttentionBy = CTimer::GetTimeStep() * 0.0001f * increaseDelta; if (increaseAttentionBy < 0.0f || movedSignificantly && thereIsACarPathNear && !CCullZones::NoPolice() && !CCullZones::PoliceAbandonCars() && CGame::currArea == AREA_MAIN_MAP) { m_fMediaAttention += increaseAttentionBy; } } else { m_fMediaAttention = 0.0f; } CStats::HighestChaseValue = Max(m_fMediaAttention, CStats::HighestChaseValue); m_nMoney = Min(999999999, m_nMoney); m_nVisibleMoney = Min(999999999, m_nVisibleMoney); } bool CPlayerInfo::IsPlayerInRemoteMode() { return m_pRemoteVehicle || m_bInRemoteMode; } void CPlayerInfo::SavePlayerInfo(uint8 *buf, uint32 *size) { // Interesting *size = sizeof(CPlayerInfo); #define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); buf += sizeof(data); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMoney); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_WBState); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nWBTime); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nTrafficMultiplier); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_fRoadDensity); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nCollectedPackages); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nTotalPackages); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bInfiniteSprint); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bFastReload); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bFireproof); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMaxHealth); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMaxArmour); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bGetOutOfJailFree); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bGetOutOfHospitalFree); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bDriveByAllowed); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_aPlayerName); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nBustedAudioStatus); CopyToBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nCurrentBustedAudio); #undef CopyToBuf } void CPlayerInfo::LoadPlayerInfo(uint8 *buf, uint32 size) { #define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); buf += sizeof(data); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMoney); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_WBState); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nWBTime); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nTrafficMultiplier); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_fRoadDensity); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nCollectedPackages); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nTotalPackages); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bInfiniteSprint); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bFastReload); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bFireproof); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMaxHealth); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nMaxArmour); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bGetOutOfJailFree); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bGetOutOfHospitalFree); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_bDriveByAllowed); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_aPlayerName); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nBustedAudioStatus); CopyFromBuf(buf, CWorld::Players[CWorld::PlayerInFocus].m_nCurrentBustedAudio) #undef CopyFromBuf } void CPlayerInfo::FindClosestCarSectorList(CPtrList& carList, CPed* ped, float unk1, float unk2, float unk3, float unk4, float* lastCloseness, CVehicle** closestCarOutput) { for (CPtrNode* node = carList.first; node; node = node->next) { CVehicle *car = (CVehicle*)node->item; if(car->m_scanCode != CWorld::GetCurrentScanCode()) { if (!car->bUsesCollision || !car->IsVehicle()) continue; car->m_scanCode = CWorld::GetCurrentScanCode(); if (car->GetStatus() != STATUS_WRECKED && car->GetStatus() != STATUS_TRAIN_MOVING && (car->GetUp().z > 0.3f || (car->IsVehicle() && ((CVehicle*)car)->m_vehType == VEHICLE_TYPE_BIKE))) { CVector carCentre = car->GetBoundCentre(); if (Abs(ped->GetPosition().z - carCentre.z) < 2.0f || car->IsCar() && carCentre.z < ped->GetPosition().z && ped->GetPosition().z - 4.f < carCentre.z) { float dist = (ped->GetPosition() - carCentre).Magnitude2D(); if (dist <= 10.0f && !CCranes::IsThisCarBeingCarriedByAnyCrane(car)) { EvaluateCarPosition(car, ped, dist, lastCloseness, closestCarOutput); } } } } } } // lastCloseness is passed to other calls of this function void CPlayerInfo::EvaluateCarPosition(CEntity *carToTest, CPed *player, float carBoundCentrePedDist, float *lastCloseness, CVehicle **closestCarOutput) { // This dist used for determining the angle to face CVector2D dist(carToTest->GetPosition() - player->GetPosition()); float neededTurn = CGeneral::GetATanOfXY(player->GetForward().x, player->GetForward().y) - CGeneral::GetATanOfXY(dist.x, dist.y); while (neededTurn >= PI) { neededTurn -= 2 * PI; } while (neededTurn < -PI) { neededTurn += 2 * PI; } // This dist used for evaluating cars' distances, weird... // Accounts inverted needed turn (or needed turn in long way) and car dist. float closeness = (1.0f - Abs(neededTurn) / TWOPI) * (10.0f - carBoundCentrePedDist); if (closeness > *lastCloseness) { *lastCloseness = closeness; *closestCarOutput = (CVehicle*)carToTest; } } const CVector & CPlayerInfo::GetPos() { #ifdef FIX_BUGS if (!m_pPed) return TheCamera.GetPosition(); #endif if (m_pPed->InVehicle()) return m_pPed->m_pMyVehicle->GetPosition(); return m_pPed->GetPosition(); } CVector FindPlayerCoors(void) { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return TheCamera.GetPosition(); #endif CPlayerPed *ped = FindPlayerPed(); if(ped->InVehicle()) return ped->m_pMyVehicle->GetPosition(); else return ped->GetPosition(); } const CVector & FindPlayerSpeed(void) { #ifdef FIX_BUGS static CVector vecTmpVector(0.0f, 0.0f, 0.0f); if (CReplay::IsPlayingBack()) return vecTmpVector; #endif CPlayerPed *ped = FindPlayerPed(); if(ped->InVehicle()) return ped->m_pMyVehicle->m_vecMoveSpeed; else return ped->m_vecMoveSpeed; } CVehicle * FindPlayerVehicle(void) { CPlayerPed *ped = FindPlayerPed(); if(ped && ped->InVehicle()) return ped->m_pMyVehicle; return nil; } CEntity * FindPlayerEntity(void) { CPlayerPed *ped = FindPlayerPed(); if(ped->InVehicle()) return ped->m_pMyVehicle; else return ped; } CVehicle * FindPlayerTrain(void) { if(FindPlayerVehicle() && FindPlayerVehicle()->IsTrain()) return FindPlayerVehicle(); else return nil; } CPlayerPed * FindPlayerPed(void) { return CWorld::Players[CWorld::PlayerInFocus].m_pPed; } const CVector & FindPlayerCentreOfWorld(int32 player) { #ifdef FIX_BUGS if(CReplay::IsPlayingBack()) return TheCamera.GetPosition(); #endif if(CCarCtrl::bCarsGeneratedAroundCamera) return TheCamera.GetPosition(); if(CWorld::Players[player].m_pRemoteVehicle) return CWorld::Players[player].m_pRemoteVehicle->GetPosition(); if(FindPlayerVehicle()) return FindPlayerVehicle()->GetPosition(); return CWorld::Players[player].m_pPed->GetPosition(); } const CVector & FindPlayerCentreOfWorld_NoSniperShift(void) { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return TheCamera.GetPosition(); #endif if(CCarCtrl::bCarsGeneratedAroundCamera) return TheCamera.GetPosition(); if(CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle) return CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->GetPosition(); if(FindPlayerVehicle()) return FindPlayerVehicle()->GetPosition(); return FindPlayerPed()->GetPosition(); } float FindPlayerHeading(void) { if(CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle) return CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle->GetForward().Heading(); if(FindPlayerVehicle()) return FindPlayerVehicle()->GetForward().Heading(); return FindPlayerPed()->GetForward().Heading(); } bool CPlayerInfo::IsRestartingAfterDeath() { return m_WBState == WBSTATE_WASTED; } bool CPlayerInfo::IsRestartingAfterArrest() { return m_WBState == WBSTATE_BUSTED; } void CPlayerInfo::KillPlayer() { if (m_WBState != WBSTATE_PLAYING) return; m_WBState = WBSTATE_WASTED; m_nWBTime = CTimer::GetTimeInMilliseconds(); CDarkel::ResetOnPlayerDeath(); CMessages::AddBigMessage(TheText.Get("DEAD"), 4000, 2); CStats::TimesDied++; } void CPlayerInfo::ArrestPlayer() { if (m_WBState != WBSTATE_PLAYING) return; m_WBState = WBSTATE_BUSTED; m_nWBTime = CTimer::GetTimeInMilliseconds(); m_nBustedAudioStatus = BUSTEDAUDIO_NONE; CDarkel::ResetOnPlayerDeath(); CMessages::AddBigMessage(TheText.Get("BUSTED"), 5000, 2); CStats::TimesArrested++; } void CPlayerInfo::PlayerFailedCriticalMission() { if (m_WBState != WBSTATE_PLAYING) return; m_WBState = WBSTATE_FAILED_CRITICAL_MISSION; m_nWBTime = CTimer::GetTimeInMilliseconds(); CDarkel::ResetOnPlayerDeath(); } void CPlayerInfo::CancelPlayerEnteringCars(CVehicle *car) { if (!car || car == m_pPed->m_pMyVehicle) { if (m_pPed->EnteringCar()) m_pPed->QuitEnteringCar(); } if (m_pPed->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || m_pPed->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) m_pPed->ClearObjective(); } void CPlayerInfo::MakePlayerSafe(bool toggle) { if (toggle) { m_pPed->m_pWanted->m_bIgnoredByEveryone = true; CWorld::StopAllLawEnforcersInTheirTracks(); CPad::GetPad(0)->SetDisablePlayerControls(PLAYERCONTROL_PLAYERINFO); CPad::StopPadsShaking(); m_pPed->bBulletProof = true; m_pPed->bFireProof = true; m_pPed->bCollisionProof = true; m_pPed->bMeleeProof = true; m_pPed->bOnlyDamagedByPlayer = true; m_pPed->bExplosionProof = true; m_pPed->m_bCanBeDamaged = false; ((CPlayerPed*)m_pPed)->ClearAdrenaline(); CancelPlayerEnteringCars(nil); gFireManager.ExtinguishPoint(GetPos(), 4000.0f); CExplosion::RemoveAllExplosionsInArea(GetPos(), 4000.0f); CProjectileInfo::RemoveAllProjectiles(); CWorld::SetAllCarsCanBeDamaged(false); CWorld::ExtinguishAllCarFiresInArea(GetPos(), 4000.0f); CReplay::DisableReplays(); } else { m_pPed->m_pWanted->m_bIgnoredByEveryone = false; CPad::GetPad(0)->SetEnablePlayerControls(PLAYERCONTROL_PLAYERINFO); m_pPed->bBulletProof = false; m_pPed->bFireProof = false; m_pPed->bCollisionProof = false; m_pPed->bMeleeProof = false; m_pPed->bOnlyDamagedByPlayer = false; m_pPed->bExplosionProof = false; m_pPed->m_bCanBeDamaged = true; CWorld::SetAllCarsCanBeDamaged(true); CReplay::EnableReplays(); } } void CPlayerInfo::BlowUpRCBuggy(bool actually) { if (!m_pRemoteVehicle || m_pRemoteVehicle->bRemoveFromWorld) return; CRemote::TakeRemoteControlledCarFromPlayer(actually); if (actually) m_pRemoteVehicle->BlowUpCar(FindPlayerPed()); } #ifdef GTA_PC void CPlayerInfo::SetPlayerSkin(const char *skin) { strncpy(m_aSkinName, skin, 32); LoadPlayerSkin(); } void CPlayerInfo::LoadPlayerSkin() { DeletePlayerSkin(); m_pSkinTexture = CPlayerSkin::GetSkinTexture(m_aSkinName); } void CPlayerInfo::DeletePlayerSkin() { if (m_pSkinTexture) { RwTextureDestroy(m_pSkinTexture); m_pSkinTexture = nil; } } #endif ================================================ FILE: src/core/PlayerInfo.h ================================================ #pragma once #include "ColModel.h" enum eWastedBustedState { WBSTATE_PLAYING, WBSTATE_WASTED, WBSTATE_BUSTED, WBSTATE_FAILED_CRITICAL_MISSION, }; enum eBustedAudioState { BUSTEDAUDIO_NONE, BUSTEDAUDIO_LOADING, BUSTEDAUDIO_DONE }; class CEntity; class CPed; class CVehicle; class CPlayerPed; class CCivilianPed; class CPlayerInfo { public: CPlayerPed *m_pPed; CVehicle *m_pRemoteVehicle; CColModel m_ColModel; CVehicle *m_pVehicleEx; // vehicle using the col model above char m_aPlayerName[70]; int32 m_nMoney; int32 m_nVisibleMoney; int32 m_nCollectedPackages; int32 m_nTotalPackages; uint32 m_nLastBumpPlayerCarTimer; uint32 m_nUnusedTaxiTimer; bool m_bUnusedTaxiThing; uint32 m_nNextSexFrequencyUpdateTime; uint32 m_nNextSexMoneyUpdateTime; int32 m_nSexFrequency; CCivilianPed *m_pHooker; int8 m_WBState; // eWastedBustedState uint32 m_nWBTime; bool m_bInRemoteMode; bool field_D5; bool field_D6; uint32 m_nTimeLostRemoteCar; uint32 m_nTimeLastHealthLoss; uint32 m_nTimeLastArmourLoss; uint32 m_nTimeTankShotGun; int32 m_nUpsideDownCounter; int32 field_EC; int32 m_nTimeCarSpentOnTwoWheels; int32 m_nDistanceCarTravelledOnTwoWheels; int32 m_nTimeNotFullyOnGround; int32 m_nTimeSpentOnWheelie; float m_nDistanceTravelledOnWheelie; int32 m_nTimeSpentOnStoppie; float m_nDistanceTravelledOnStoppie; int32 m_nCancelWheelStuntTimer; int32 m_nLastTimeCarSpentOnTwoWheels; int32 m_nLastDistanceCarTravelledOnTwoWheels; int32 m_nLastTimeSpentOnWheelie; int32 m_nLastDistanceTravelledOnWheelie; int32 m_nLastTimeSpentOnStoppie; int32 m_nLastDistanceTravelledOnStoppie; int16 m_nTrafficMultiplier; int16 field_12A; float m_fRoadDensity; uint32 m_nPreviousTimeRewardedForExplosion; uint32 m_nExplosionsSinceLastReward; uint32 m_nHavocLevel; float m_fMediaAttention; bool m_bInfiniteSprint; bool m_bFastReload; bool m_bFireproof; uint8 m_nMaxHealth; uint8 m_nMaxArmour; bool m_bGetOutOfJailFree; bool m_bGetOutOfHospitalFree; bool m_bDriveByAllowed; uint8 m_nBustedAudioStatus; int16 m_nCurrentBustedAudio; #ifdef GTA_PC char m_aSkinName[32]; RwTexture *m_pSkinTexture; #endif void MakePlayerSafe(bool); const CVector &GetPos(); void Process(void); void KillPlayer(void); void ArrestPlayer(void); bool IsPlayerInRemoteMode(void); void PlayerFailedCriticalMission(void); void Clear(void); void BlowUpRCBuggy(bool); void CancelPlayerEnteringCars(CVehicle*); bool IsRestartingAfterDeath(void); bool IsRestartingAfterArrest(void); void EvaluateCarPosition(CEntity*, CPed*, float, float*, CVehicle**); void LoadPlayerInfo(uint8 *buf, uint32 size); void SavePlayerInfo(uint8 *buf, uint32* size); void FindClosestCarSectorList(CPtrList&, CPed*, float, float, float, float, float*, CVehicle**); #ifdef GTA_PC void LoadPlayerSkin(); void SetPlayerSkin(const char *skin); void DeletePlayerSkin(); #endif }; CPlayerPed *FindPlayerPed(void); CVehicle *FindPlayerVehicle(void); CVehicle *FindPlayerTrain(void); CEntity *FindPlayerEntity(void); CVector FindPlayerCoors(void); const CVector &FindPlayerSpeed(void); const CVector &FindPlayerCentreOfWorld(int32 player); const CVector &FindPlayerCentreOfWorld_NoSniperShift(void); float FindPlayerHeading(void); ================================================ FILE: src/core/Pools.cpp ================================================ #include "common.h" #include "Pools.h" #include "Bike.h" #include "Boat.h" #include "CarCtrl.h" #ifdef MISSION_REPLAY #include "GenericGameStorage.h" #endif #include "Population.h" #include "ProjectileInfo.h" #include "Streaming.h" #include "Wanted.h" #include "World.h" #include "MemoryHeap.h" CCPtrNodePool *CPools::ms_pPtrNodePool; CEntryInfoNodePool *CPools::ms_pEntryInfoNodePool; CPedPool *CPools::ms_pPedPool; CVehiclePool *CPools::ms_pVehiclePool; CBuildingPool *CPools::ms_pBuildingPool; CTreadablePool *CPools::ms_pTreadablePool; CObjectPool *CPools::ms_pObjectPool; CDummyPool *CPools::ms_pDummyPool; CAudioScriptObjectPool *CPools::ms_pAudioScriptObjectPool; CColModelPool *CPools::ms_pColModelPool; #if defined GTA_PS2 && !defined MASTER // or USE_CUSTOM_ALLOCATOR // not in VC. perhaps ifdef'ed away #define CHECKMEM(msg) CMemCheck::AllocateMemCheckBlock(msg) #else #define CHECKMEM(msg) #endif void CPools::Initialise(void) { PUSH_MEMID(MEMID_POOLS); CHECKMEM("before pools"); ms_pPtrNodePool = new CCPtrNodePool(NUMPTRNODES, "PtrNode"); CHECKMEM("after CPtrNodePool"); ms_pEntryInfoNodePool = new CEntryInfoNodePool(NUMENTRYINFOS, "EntryInfoNode"); CHECKMEM("after CEntryInfoNodePool"); ms_pPedPool = new CPedPool(NUMPEDS, "Peds"); CHECKMEM("after CPedPool"); ms_pVehiclePool = new CVehiclePool(NUMVEHICLES, "Vehicles"); CHECKMEM("after CVehiclePool"); ms_pBuildingPool = new CBuildingPool(NUMBUILDINGS, "Buildings"); CHECKMEM("after CBuildingPool"); ms_pTreadablePool = new CTreadablePool(NUMTREADABLES, "Treadables"); CHECKMEM("after CTreadablePool"); ms_pObjectPool = new CObjectPool(NUMOBJECTS, "Objects"); CHECKMEM("after CObjectPool"); ms_pDummyPool = new CDummyPool(NUMDUMMIES, "Dummys"); CHECKMEM("after CDummyPool"); ms_pAudioScriptObjectPool = new CAudioScriptObjectPool(NUMAUDIOSCRIPTOBJECTS, "AudioScriptObj"); CHECKMEM("after cAudioScriptObjectPool"); ms_pColModelPool = new CColModelPool(NUMCOLMODELS, "ColModel"); CHECKMEM("after pools"); POP_MEMID(); } void CPools::ShutDown(void) { debug("PtrNodes left %d\n", ms_pPtrNodePool->GetNoOfUsedSpaces()); debug("EntryInfoNodes left %d\n", ms_pEntryInfoNodePool->GetNoOfUsedSpaces()); debug("Peds left %d\n", ms_pPedPool->GetNoOfUsedSpaces()); debug("Vehicles left %d\n", ms_pVehiclePool->GetNoOfUsedSpaces()); debug("Buildings left %d\n", ms_pBuildingPool->GetNoOfUsedSpaces()); debug("Treadables left %d\n", ms_pTreadablePool->GetNoOfUsedSpaces()); debug("Objects left %d\n", ms_pObjectPool->GetNoOfUsedSpaces()); debug("Dummys left %d\n", ms_pDummyPool->GetNoOfUsedSpaces()); debug("AudioScriptObjects left %d\n", ms_pAudioScriptObjectPool->GetNoOfUsedSpaces()); debug("ColModels left %d\n", ms_pColModelPool->GetNoOfUsedSpaces()); printf("Shutdown pool started\n"); delete ms_pPtrNodePool; delete ms_pEntryInfoNodePool; delete ms_pPedPool; delete ms_pVehiclePool; delete ms_pBuildingPool; delete ms_pTreadablePool; delete ms_pObjectPool; delete ms_pDummyPool; delete ms_pAudioScriptObjectPool; delete ms_pColModelPool; printf("Shutdown pool done\n"); } int32 CPools::GetPedRef(CPed *ped) { return ms_pPedPool->GetIndex(ped); } CPed *CPools::GetPed(int32 handle) { return ms_pPedPool->GetAt(handle); } int32 CPools::GetVehicleRef(CVehicle *vehicle) { return ms_pVehiclePool->GetIndex(vehicle); } CVehicle *CPools::GetVehicle(int32 handle) { return ms_pVehiclePool->GetAt(handle); } int32 CPools::GetObjectRef(CObject *object) { return ms_pObjectPool->GetIndex(object); } CObject *CPools::GetObject(int32 handle) { return ms_pObjectPool->GetAt(handle); } void CPools::CheckPoolsEmpty() { assert(ms_pPedPool->GetNoOfUsedSpaces() == 0); assert(ms_pVehiclePool->GetNoOfUsedSpaces() == 0); printf("pools have been cleared\n"); } void CPools::MakeSureSlotInObjectPoolIsEmpty(int32 slot) { if (ms_pObjectPool->GetIsFree(slot)) return; CObject *object = ms_pObjectPool->GetSlot(slot); if (object->ObjectCreatedBy == TEMP_OBJECT) { CWorld::Remove(object); delete object; } else if (!CProjectileInfo::RemoveIfThisIsAProjectile(object)) { // relocate to another slot?? CObject *newObject = new CObject(object->GetModelIndex(), false); CWorld::Remove(object); #if 0 // todo better *newObject = *object; #else memcpy(newObject, object, ms_pObjectPool->GetMaxEntrySize()); #endif CWorld::Add(newObject); object->m_rwObject = nil; delete object; newObject->m_pFirstReference = nil; } } #define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); #define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); void CPools::LoadVehiclePool(uint8* buf, uint32 size) { INITSAVEBUF int nNumCars = ReadSaveBuf(buf); int nNumBoats = ReadSaveBuf(buf); int nNumBikes = ReadSaveBuf(buf); for (int i = 0; i < nNumCars + nNumBoats + nNumBikes; i++) { uint32 type = ReadSaveBuf(buf); int16 model = ReadSaveBuf(buf); CStreaming::RequestModel(model, STREAMFLAGS_DEPENDENCY); CStreaming::LoadAllRequestedModels(false); int32 slot = ReadSaveBuf(buf); CVehicle* pVehicle; #ifdef COMPATIBLE_SAVES if (type == VEHICLE_TYPE_BOAT) pVehicle = new(slot) CBoat(model, RANDOM_VEHICLE); else if (type == VEHICLE_TYPE_CAR) pVehicle = new(slot) CAutomobile(model, RANDOM_VEHICLE); else if (type == VEHICLE_TYPE_BIKE) pVehicle = new(slot) CBike(model, RANDOM_VEHICLE); else assert(0); --CCarCtrl::NumRandomCars; pVehicle->Load(buf); CWorld::Add(pVehicle); #else char* vbuf = new char[Max(CBike::nSaveStructSize, Max(CAutomobile::nSaveStructSize, CBoat::nSaveStructSize))]; if (type == VEHICLE_TYPE_BOAT) { memcpy(vbuf, buf, sizeof(CBoat)); SkipSaveBuf(buf, sizeof(CBoat)); CBoat* pBoat = new(slot) CBoat(model, RANDOM_VEHICLE); pVehicle = pBoat; --CCarCtrl::NumRandomCars; } else if (type == VEHICLE_TYPE_CAR) { memcpy(vbuf, buf, sizeof(CAutomobile)); SkipSaveBuf(buf, sizeof(CAutomobile)); CStreaming::RequestModel(model, 0); // is it needed? CStreaming::LoadAllRequestedModels(false); CAutomobile* pAutomobile = new(slot) CAutomobile(model, RANDOM_VEHICLE); pVehicle = pAutomobile; CCarCtrl::NumRandomCars--; // why? pAutomobile->Damage = ((CAutomobile*)vbuf)->Damage; pAutomobile->SetupDamageAfterLoad(); } else if (type == VEHICLE_TYPE_BIKE) { #ifdef FIX_BUGS memcpy(vbuf, buf, sizeof(CBike)); #else memcpy(vbuf, buf, sizeof(CAutomobile)); #endif SkipSaveBuf(buf, sizeof(CBike)); CBike* pBike = new(slot) CBike(model, RANDOM_VEHICLE); pVehicle = pBike; --CCarCtrl::NumRandomCars; } else assert(0); CVehicle* pBufferVehicle = (CVehicle*)vbuf; pVehicle->GetMatrix() = pBufferVehicle->GetMatrix(); pVehicle->VehicleCreatedBy = pBufferVehicle->VehicleCreatedBy; pVehicle->m_currentColour1 = pBufferVehicle->m_currentColour1; pVehicle->m_currentColour2 = pBufferVehicle->m_currentColour2; pVehicle->m_nAlarmState = pBufferVehicle->m_nAlarmState; pVehicle->m_nNumMaxPassengers = pBufferVehicle->m_nNumMaxPassengers; pVehicle->field_1D0[0] = pBufferVehicle->field_1D0[0]; pVehicle->field_1D0[1] = pBufferVehicle->field_1D0[1]; pVehicle->field_1D0[2] = pBufferVehicle->field_1D0[2]; pVehicle->field_1D0[3] = pBufferVehicle->field_1D0[3]; pVehicle->m_fSteerAngle = pBufferVehicle->m_fSteerAngle; pVehicle->m_fGasPedal = pBufferVehicle->m_fGasPedal; pVehicle->m_fBrakePedal = pBufferVehicle->m_fBrakePedal; pVehicle->bIsLawEnforcer = pBufferVehicle->bIsLawEnforcer; pVehicle->bIsLocked = pBufferVehicle->bIsLocked; pVehicle->bEngineOn = pBufferVehicle->bEngineOn; pVehicle->bIsHandbrakeOn = pBufferVehicle->bIsHandbrakeOn; pVehicle->bLightsOn = pBufferVehicle->bLightsOn; pVehicle->bFreebies = pBufferVehicle->bFreebies; pVehicle->m_fHealth = pBufferVehicle->m_fHealth; pVehicle->m_nCurrentGear = pBufferVehicle->m_nCurrentGear; pVehicle->m_fChangeGearTime = pBufferVehicle->m_fChangeGearTime; pVehicle->m_nTimeOfDeath = pBufferVehicle->m_nTimeOfDeath; #ifdef FIX_BUGS //must be copypaste pVehicle->m_nBombTimer = pBufferVehicle->m_nBombTimer; #else pVehicle->m_nTimeOfDeath = pBufferVehicle->m_nTimeOfDeath; #endif pVehicle->m_nDoorLock = pBufferVehicle->m_nDoorLock; pVehicle->SetStatus(pBufferVehicle->GetStatus()); pVehicle->SetType(pBufferVehicle->GetType()); (pVehicle->GetAddressOfEntityProperties())[0] = (pBufferVehicle->GetAddressOfEntityProperties())[0]; (pVehicle->GetAddressOfEntityProperties())[1] = (pBufferVehicle->GetAddressOfEntityProperties())[1]; pVehicle->AutoPilot = pBufferVehicle->AutoPilot; CCarCtrl::UpdateCarCount(pVehicle, false); CWorld::Add(pVehicle); delete[] vbuf; #endif } VALIDATESAVEBUF(size) } void CPools::SaveVehiclePool(uint8* buf, uint32* size) { INITSAVEBUF int nNumCars = 0; int nNumBoats = 0; int nNumBikes = 0; int nPoolSize = GetVehiclePool()->GetSize(); for (int i = 0; i < nPoolSize; i++) { CVehicle* pVehicle = GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; bool bHasPassenger = false; for (int j = 0; j < ARRAY_SIZE(pVehicle->pPassengers); j++) { if (pVehicle->pPassengers[j]) bHasPassenger = true; } #ifdef MISSION_REPLAY bool bForceSaving = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pMyVehicle == pVehicle && IsQuickSave; #ifdef FIX_BUGS if ((!pVehicle->pDriver && !bHasPassenger) || bForceSaving) { #else if (!pVehicle->pDriver && !bHasPassenger) { #endif if (pVehicle->IsCar() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) ++nNumCars; if (pVehicle->IsBoat() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) ++nNumBoats; if (pVehicle->IsBike() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) ++nNumBoats; #else if (!pVehicle->pDriver && !bHasPassenger) { if (pVehicle->IsCar() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) ++nNumCars; if (pVehicle->IsBoat() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) ++nNumBoats; if (pVehicle->IsBike() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) ++nNumBoats; #endif } } *size = nNumCars * (sizeof(uint32) + sizeof(int16) + sizeof(int32) + CAutomobile::nSaveStructSize) + sizeof(int) + nNumBoats * (sizeof(uint32) + sizeof(int16) + sizeof(int32) + CBoat::nSaveStructSize) + sizeof(int) + nNumBikes * (sizeof(uint32) + sizeof(int16) + sizeof(int32) + CBike::nSaveStructSize) + sizeof(int); WriteSaveBuf(buf, nNumCars); WriteSaveBuf(buf, nNumBoats); WriteSaveBuf(buf, nNumBikes); for (int i = 0; i < nPoolSize; i++) { CVehicle* pVehicle = GetVehiclePool()->GetSlot(i); if (!pVehicle) continue; bool bHasPassenger = false; for (int j = 0; j < ARRAY_SIZE(pVehicle->pPassengers); j++) { if (pVehicle->pPassengers[j]) bHasPassenger = true; } #ifdef MISSION_REPLAY bool bForceSaving = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pMyVehicle == pVehicle && IsQuickSave; #endif #if defined FIX_BUGS && defined MISSION_REPLAY if ((!pVehicle->pDriver && !bHasPassenger) || bForceSaving) { #else if (!pVehicle->pDriver && !bHasPassenger) { #endif #ifdef COMPATIBLE_SAVES #ifdef MISSION_REPLAY if ((pVehicle->IsCar() || pVehicle->IsBoat() || pVehicle->IsBike()) && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) { #else if ((pVehicle->IsCar() || pVehicle->IsBoat() || pVehicle->IsBike()) && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { #endif WriteSaveBuf(buf, pVehicle->m_vehType); WriteSaveBuf(buf, pVehicle->GetModelIndex()); WriteSaveBuf(buf, GetVehicleRef(pVehicle)); pVehicle->Save(buf); } #else #ifdef MISSION_REPLAY if (pVehicle->IsCar() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) { #else if (pVehicle->IsCar() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { #endif WriteSaveBuf(buf, (uint32)pVehicle->m_vehType); WriteSaveBuf(buf, pVehicle->GetModelIndex()); WriteSaveBuf(buf, GetVehicleRef(pVehicle)); memcpy(buf, pVehicle, sizeof(CAutomobile)); SkipSaveBuf(buf, sizeof(CAutomobile)); } #ifdef MISSION_REPLAY if (pVehicle->IsBoat() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) { #else if (pVehicle->IsBoat() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { #endif WriteSaveBuf(buf, (uint32)pVehicle->m_vehType); WriteSaveBuf(buf, pVehicle->GetModelIndex()); WriteSaveBuf(buf, GetVehicleRef(pVehicle)); memcpy(buf, pVehicle, sizeof(CBoat)); SkipSaveBuf(buf, sizeof(CBoat)); } #ifdef MISSION_REPLAY if (pVehicle->IsBike() && (pVehicle->VehicleCreatedBy == MISSION_VEHICLE || bForceSaving)) { #else if (pVehicle->IsBike() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { #endif WriteSaveBuf(buf, (uint32)pVehicle->m_vehType); WriteSaveBuf(buf, pVehicle->GetModelIndex()); WriteSaveBuf(buf, GetVehicleRef(pVehicle)); memcpy(buf, pVehicle, sizeof(CBike)); SkipSaveBuf(buf, sizeof(CBike)); } #endif } } VALIDATESAVEBUF(*size) } void CPools::SaveObjectPool(uint8* buf, uint32* size) { INITSAVEBUF CProjectileInfo::RemoveAllProjectiles(); CObject::DeleteAllTempObjects(); int nObjects = 0; int nPoolSize = GetObjectPool()->GetSize(); for (int i = 0; i < nPoolSize; i++) { CObject* pObject = GetObjectPool()->GetSlot(i); if (!pObject) continue; if (pObject->ObjectCreatedBy == MISSION_OBJECT) ++nObjects; } *size = nObjects * (sizeof(int16) + sizeof(int) + sizeof(CCompressedMatrix) + sizeof(float) + sizeof(CCompressedMatrix) + sizeof(int8) + 7 * sizeof(bool) + sizeof(int16) + + sizeof(int8) * 2 + sizeof(float) + sizeof(int8) + sizeof(int8) + sizeof(uint32) + 2 * sizeof(uint32)) + sizeof(int); CopyToBuf(buf, nObjects); for (int i = 0; i < nPoolSize; i++) { CObject* pObject = GetObjectPool()->GetSlot(i); if (!pObject) continue; if (pObject->ObjectCreatedBy == MISSION_OBJECT) { bool bIsPickup = pObject->bIsPickup; bool bPickupObjWithMessage = pObject->bPickupObjWithMessage; bool bOutOfStock = pObject->bOutOfStock; bool bGlassCracked = pObject->bGlassCracked; bool bGlassBroken = pObject->bGlassBroken; bool bHasBeenDamaged = pObject->bHasBeenDamaged; bool bUseVehicleColours = pObject->bUseVehicleColours; CCompressedMatrix tmp; CopyToBuf(buf, pObject->m_modelIndex); int32 ref = GetObjectRef(pObject); CopyToBuf(buf, ref); tmp.CompressFromFullMatrix(pObject->GetMatrix()); CopyToBuf(buf, tmp); CopyToBuf(buf, pObject->m_fUprootLimit); tmp.CompressFromFullMatrix(pObject->m_objectMatrix); CopyToBuf(buf, tmp); CopyToBuf(buf, pObject->ObjectCreatedBy); CopyToBuf(buf, bIsPickup); CopyToBuf(buf, bPickupObjWithMessage); CopyToBuf(buf, bOutOfStock); CopyToBuf(buf, bGlassCracked); CopyToBuf(buf, bGlassBroken); CopyToBuf(buf, bHasBeenDamaged); CopyToBuf(buf, bUseVehicleColours); CopyToBuf(buf, pObject->m_nCostValue); CopyToBuf(buf, pObject->m_nBonusValue); SkipSaveBuf(buf, 1); CopyToBuf(buf, pObject->m_fCollisionDamageMultiplier); CopyToBuf(buf, pObject->m_nCollisionDamageEffect); CopyToBuf(buf, pObject->m_nSpecialCollisionResponseCases); CopyToBuf(buf, pObject->m_nEndOfLifeTime); #ifdef COMPATIBLE_SAVES pObject->SaveEntityFlags(buf); #else CopyToBuf(buf, (pObject->GetAddressOfEntityProperties())[0]); CopyToBuf(buf, (pObject->GetAddressOfEntityProperties())[1]); #endif } } VALIDATESAVEBUF(*size) } void CPools::LoadObjectPool(uint8* buf, uint32 size) { INITSAVEBUF int nObjects; CopyFromBuf(buf, nObjects); for (int i = 0; i < nObjects; i++) { int16 mi; CopyFromBuf(buf, mi); int ref; CopyFromBuf(buf, ref); char* obuf = new char[sizeof(CObject)]; CObject* pBufferObject = (CObject*)obuf; CCompressedMatrix tmp; CopyFromBuf(buf, tmp); tmp.DecompressIntoFullMatrix(pBufferObject->GetMatrix()); CopyFromBuf(buf, pBufferObject->m_fUprootLimit); CopyFromBuf(buf, tmp); tmp.DecompressIntoFullMatrix(pBufferObject->m_objectMatrix); CopyFromBuf(buf, pBufferObject->ObjectCreatedBy); int8 bitFlag; CopyFromBuf(buf, bitFlag); pBufferObject->bIsPickup = bitFlag; CopyFromBuf(buf, bitFlag); pBufferObject->bPickupObjWithMessage = bitFlag; CopyFromBuf(buf, bitFlag); pBufferObject->bOutOfStock = bitFlag; CopyFromBuf(buf, bitFlag); pBufferObject->bGlassCracked = bitFlag; CopyFromBuf(buf, bitFlag); pBufferObject->bGlassBroken = bitFlag; CopyFromBuf(buf, bitFlag); pBufferObject->bHasBeenDamaged = bitFlag; CopyFromBuf(buf, bitFlag); pBufferObject->bUseVehicleColours = bitFlag; CopyFromBuf(buf, pBufferObject->m_nCostValue); CopyFromBuf(buf, pBufferObject->m_nBonusValue); SkipSaveBuf(buf, 1); CopyFromBuf(buf, pBufferObject->m_fCollisionDamageMultiplier); CopyFromBuf(buf, pBufferObject->m_nCollisionDamageEffect); CopyFromBuf(buf, pBufferObject->m_nSpecialCollisionResponseCases); CopyFromBuf(buf, pBufferObject->m_nEndOfLifeTime); #ifndef COMPATIBLE_SAVES CopyFromBuf(buf, (pBufferObject->GetAddressOfEntityProperties())[0]); CopyFromBuf(buf, (pBufferObject->GetAddressOfEntityProperties())[1]); #endif if (GetObjectPool()->GetSlot(ref >> 8)) CPopulation::ConvertToDummyObject(GetObjectPool()->GetSlot(ref >> 8)); CObject* pObject = new(ref) CObject(mi, false); pObject->GetMatrix() = pBufferObject->GetMatrix(); #ifdef COMPATIBLE_SAVES pObject->LoadEntityFlags(buf); #endif pObject->m_fUprootLimit = pBufferObject->m_fUprootLimit; pObject->m_objectMatrix = pBufferObject->m_objectMatrix; pObject->ObjectCreatedBy = pBufferObject->ObjectCreatedBy; pObject->bIsPickup = pBufferObject->bIsPickup; pObject->bPickupObjWithMessage = pBufferObject->bPickupObjWithMessage; pObject->bOutOfStock = pBufferObject->bOutOfStock; pObject->bGlassCracked = pBufferObject->bGlassCracked; pObject->bGlassBroken = pBufferObject->bGlassBroken; pObject->bHasBeenDamaged = pBufferObject->bHasBeenDamaged; pObject->bUseVehicleColours = pBufferObject->bUseVehicleColours; pObject->m_fCollisionDamageMultiplier = pBufferObject->m_fCollisionDamageMultiplier; pObject->m_nCollisionDamageEffect = pBufferObject->m_nCollisionDamageEffect; pObject->m_nSpecialCollisionResponseCases = pBufferObject->m_nSpecialCollisionResponseCases; pObject->m_nEndOfLifeTime = pBufferObject->m_nEndOfLifeTime; #ifndef COMPATIBLE_SAVES (pObject->GetAddressOfEntityProperties())[0] = (pBufferObject->GetAddressOfEntityProperties())[0]; (pObject->GetAddressOfEntityProperties())[1] = (pBufferObject->GetAddressOfEntityProperties())[1]; #endif pObject->bHasCollided = false; pObject->m_nCostValue = pBufferObject->m_nCostValue; pObject->m_nBonusValue = pBufferObject->m_nBonusValue; CWorld::Add(pObject); delete[] obuf; } VALIDATESAVEBUF(size) } void CPools::SavePedPool(uint8* buf, uint32* size) { INITSAVEBUF int nNumPeds = 0; int nPoolSize = GetPedPool()->GetSize(); for (int i = 0; i < nPoolSize; i++) { CPed* pPed = GetPedPool()->GetSlot(i); if (!pPed) continue; #ifdef MISSION_REPLAY if ((!pPed->bInVehicle || (pPed == CWorld::Players[CWorld::PlayerInFocus].m_pPed && IsQuickSave)) && pPed->m_nPedType == PEDTYPE_PLAYER1) #else if (!pPed->bInVehicle && pPed->m_nPedType == PEDTYPE_PLAYER1) #endif nNumPeds++; } *size = sizeof(int) + nNumPeds * (sizeof(uint32) + sizeof(int16) + sizeof(int) + CPlayerPed::nSaveStructSize + sizeof(CWanted::MaximumWantedLevel) + sizeof(CWanted::nMaximumWantedLevel) + MAX_MODEL_NAME); CopyToBuf(buf, nNumPeds); for (int i = 0; i < nPoolSize; i++) { CPed* pPed = GetPedPool()->GetSlot(i); if (!pPed) continue; #ifdef MISSION_REPLAY if ((!pPed->bInVehicle || (pPed == CWorld::Players[CWorld::PlayerInFocus].m_pPed && IsQuickSave)) && pPed->m_nPedType == PEDTYPE_PLAYER1) { #else if (!pPed->bInVehicle && pPed->m_nPedType == PEDTYPE_PLAYER1) { #endif CopyToBuf(buf, pPed->m_nPedType); CopyToBuf(buf, pPed->m_modelIndex); int32 ref = GetPedRef(pPed); CopyToBuf(buf, ref); #ifdef COMPATIBLE_SAVES pPed->Save(buf); #else memcpy(buf, pPed, sizeof(CPlayerPed)); SkipSaveBuf(buf, sizeof(CPlayerPed)); #endif CopyToBuf(buf, CWanted::MaximumWantedLevel); CopyToBuf(buf, CWanted::nMaximumWantedLevel); memcpy(buf, CModelInfo::GetModelInfo(pPed->GetModelIndex())->GetModelName(), MAX_MODEL_NAME); SkipSaveBuf(buf, MAX_MODEL_NAME); } } VALIDATESAVEBUF(*size); #undef CopyToBuf } void CPools::LoadPedPool(uint8* buf, uint32 size) { INITSAVEBUF int nPeds; CopyFromBuf(buf, nPeds); for (int i = 0; i < nPeds; i++) { uint32 pedtype; int16 model; int ref; CopyFromBuf(buf, pedtype); CopyFromBuf(buf, model); CopyFromBuf(buf, ref); #ifdef COMPATIBLE_SAVES CPed* pPed; char name[MAX_MODEL_NAME]; // Unfortunate hack: player model is stored after ped structure. // It could be avoided by just using "player" because in practice it is always true. memcpy(name, buf + CPlayerPed::nSaveStructSize + 2 * sizeof(int32), MAX_MODEL_NAME); CStreaming::RequestSpecialModel(model, name, STREAMFLAGS_DONT_REMOVE); CStreaming::LoadAllRequestedModels(false); if (pedtype == PEDTYPE_PLAYER1) pPed = new(ref) CPlayerPed(); else assert(0); pPed->Load(buf); if (pedtype == PEDTYPE_PLAYER1) { CopyFromBuf(buf, CWanted::MaximumWantedLevel); CopyFromBuf(buf, CWanted::nMaximumWantedLevel); SkipSaveBuf(buf, MAX_MODEL_NAME); } if (pedtype == PEDTYPE_PLAYER1) { pPed->m_wepAccuracy = 100; CWorld::Players[0].m_pPed = (CPlayerPed*)pPed; } CWorld::Add(pPed); #else char* pbuf = new char[sizeof(CPlayerPed)]; CPlayerPed* pBufferPlayer = (CPlayerPed*)pbuf; CPed* pPed; char name[MAX_MODEL_NAME]; // the code implies that there was idea to load non-player ped if (pedtype == PEDTYPE_PLAYER1) { // always true memcpy(pbuf, buf, sizeof(CPlayerPed)); SkipSaveBuf(buf, sizeof(CPlayerPed)); CopyFromBuf(buf, CWanted::MaximumWantedLevel); CopyFromBuf(buf, CWanted::nMaximumWantedLevel); CopyFromBuf(buf, name); } CStreaming::RequestSpecialModel(model, name, STREAMFLAGS_DONT_REMOVE); CStreaming::LoadAllRequestedModels(false); if (pedtype == PEDTYPE_PLAYER1) { CPlayerPed* pPlayerPed = new(ref) CPlayerPed(); for (int i = 0; i < ARRAY_SIZE(pPlayerPed->m_nTargettableObjects); i++) pPlayerPed->m_nTargettableObjects[i] = pBufferPlayer->m_nTargettableObjects[i]; pPlayerPed->m_fMaxStamina = pBufferPlayer->m_fMaxStamina; pPed = pPlayerPed; } pPed->SetPosition(pBufferPlayer->GetPosition()); pPed->m_fHealth = pBufferPlayer->m_fHealth; pPed->m_fArmour = pBufferPlayer->m_fArmour; pPed->CharCreatedBy = pBufferPlayer->CharCreatedBy; pPed->m_currentWeapon = 0; pPed->m_maxWeaponTypeAllowed = pBufferPlayer->m_maxWeaponTypeAllowed; for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { if (pBufferPlayer->HasWeaponSlot(i)) { int modelId = CWeaponInfo::GetWeaponInfo(pBufferPlayer->GetWeapon(i).m_eWeaponType)->m_nModelId; if (modelId != -1) { CStreaming::RequestModel(modelId, STREAMFLAGS_DEPENDENCY); int modelId2 = CWeaponInfo::GetWeaponInfo(pBufferPlayer->GetWeapon(i).m_eWeaponType)->m_nModel2Id; if (modelId2 != -1) CStreaming::RequestModel(modelId2, STREAMFLAGS_DEPENDENCY); CStreaming::LoadAllRequestedModels(false); } pPed->GiveWeapon(pBufferPlayer->GetWeapon(i).m_eWeaponType, pBufferPlayer->GetWeapon(i).m_nAmmoTotal, false); } } if (pedtype == PEDTYPE_PLAYER1) { pPed->m_wepAccuracy = 100; CWorld::Players[0].m_pPed = (CPlayerPed*)pPed; } CWorld::Add(pPed); delete[] pbuf; #endif } VALIDATESAVEBUF(size) } #undef CopyFromBuf #undef CopyToBuf ================================================ FILE: src/core/Pools.h ================================================ #pragma once #include "templates.h" #include "Lists.h" #include "Treadable.h" #include "Object.h" #include "CutsceneObject.h" #include "PlayerPed.h" #include "Automobile.h" #include "DummyPed.h" #include "AudioScriptObject.h" typedef CPool CCPtrNodePool; typedef CPool CEntryInfoNodePool; typedef CPool CPedPool; typedef CPool CVehiclePool; typedef CPool CBuildingPool; typedef CPool CTreadablePool; typedef CPool CObjectPool; typedef CPool CDummyPool; typedef CPool CAudioScriptObjectPool; typedef CPool CColModelPool; class CPools { static CCPtrNodePool *ms_pPtrNodePool; static CEntryInfoNodePool *ms_pEntryInfoNodePool; static CPedPool *ms_pPedPool; static CVehiclePool *ms_pVehiclePool; static CBuildingPool *ms_pBuildingPool; static CTreadablePool *ms_pTreadablePool; static CObjectPool *ms_pObjectPool; static CDummyPool *ms_pDummyPool; static CAudioScriptObjectPool *ms_pAudioScriptObjectPool; static CColModelPool *ms_pColModelPool; public: static CCPtrNodePool *GetPtrNodePool(void) { return ms_pPtrNodePool; } static CEntryInfoNodePool *GetEntryInfoNodePool(void) { return ms_pEntryInfoNodePool; } static CPedPool *GetPedPool(void) { return ms_pPedPool; } static CVehiclePool *GetVehiclePool(void) { return ms_pVehiclePool; } static CBuildingPool *GetBuildingPool(void) { return ms_pBuildingPool; } static CTreadablePool *GetTreadablePool(void) { return ms_pTreadablePool; } static CObjectPool *GetObjectPool(void) { return ms_pObjectPool; } static CDummyPool *GetDummyPool(void) { return ms_pDummyPool; } static CAudioScriptObjectPool *GetAudioScriptObjectPool(void) { return ms_pAudioScriptObjectPool; } static CColModelPool *GetColModelPool(void) { return ms_pColModelPool; } static void Initialise(void); static void ShutDown(void); static int32 GetPedRef(CPed *ped); static CPed *GetPed(int32 handle); static int32 GetVehicleRef(CVehicle *vehicle); static CVehicle *GetVehicle(int32 handle); static int32 GetObjectRef(CObject *object); static CObject *GetObject(int32 handle); static void CheckPoolsEmpty(); static void MakeSureSlotInObjectPoolIsEmpty(int32 slot); static void LoadObjectPool(uint8 *buf, uint32 size); static void LoadPedPool(uint8 *buf, uint32 size); static void LoadVehiclePool(uint8 *buf, uint32 size); static void SaveObjectPool(uint8 *buf, uint32 *size); static void SavePedPool(uint8 *buf, uint32 *size); static void SaveVehiclePool(uint8 *buf, uint32 *size); }; ================================================ FILE: src/core/Profile.cpp ================================================ #include "common.h" #include "Profile.h" #ifndef MASTER float CProfile::ms_afStartTime[NUM_PROFILES]; float CProfile::ms_afCumulativeTime[NUM_PROFILES]; float CProfile::ms_afEndTime[NUM_PROFILES]; float CProfile::ms_afMaxEndTime[NUM_PROFILES]; float CProfile::ms_afMaxCumulativeTime[NUM_PROFILES]; Const char *CProfile::ms_pProfileString[NUM_PROFILES]; RwRGBA CProfile::ms_aBarColours[NUM_PROFILES]; void CProfile::Initialise() { ms_afMaxEndTime[PROFILE_FRAME_RATE] = 0.0f; ms_afMaxEndTime[PROFILE_PHYSICS] = 0.0f; ms_afMaxEndTime[PROFILE_COLLISION] = 0.0f; ms_afMaxEndTime[PROFILE_PED_AI] = 0.0f; ms_afMaxEndTime[PROFILE_PROCESSING_TIME] = 0.0f; ms_afMaxEndTime[PROFILE_RENDERING_TIME] = 0.0f; ms_afMaxEndTime[PROFILE_TOTAL] = 0.0f; ms_pProfileString[PROFILE_FRAME_RATE] = "Frame rate"; ms_pProfileString[PROFILE_PHYSICS] = "Physics"; ms_pProfileString[PROFILE_COLLISION] = "Collision"; ms_pProfileString[PROFILE_PED_AI] = "Ped AI"; ms_pProfileString[PROFILE_PROCESSING_TIME] = "Processing time"; ms_pProfileString[PROFILE_RENDERING_TIME] = "Rendering time"; ms_pProfileString[PROFILE_TOTAL] = "Total"; ms_afMaxCumulativeTime[PROFILE_FRAME_RATE] = 0.0f; ms_afMaxCumulativeTime[PROFILE_PHYSICS] = 0.0f; ms_afMaxCumulativeTime[PROFILE_COLLISION] = 0.0f; ms_afMaxCumulativeTime[PROFILE_PED_AI] = 0.0f; ms_afMaxCumulativeTime[PROFILE_PROCESSING_TIME] = 0.0f; ms_afMaxCumulativeTime[PROFILE_RENDERING_TIME] = 0.0f; ms_afMaxCumulativeTime[PROFILE_TOTAL] = 0.0f; ms_aBarColours[PROFILE_PHYSICS] = { 0, 127, 255, 255 }; ms_aBarColours[PROFILE_COLLISION] = { 0, 255, 255, 255 }; ms_aBarColours[PROFILE_PED_AI] = { 255, 0, 0, 255 }; ms_aBarColours[PROFILE_PROCESSING_TIME] = { 0, 255, 0, 255 }; ms_aBarColours[PROFILE_RENDERING_TIME] = { 0, 0, 255, 255 }; ms_aBarColours[PROFILE_TOTAL] = { 255, 255, 255, 255 }; } void CProfile::SuspendProfile(eProfile profile) { ms_afEndTime[profile] = -ms_afStartTime[profile]; ms_afCumulativeTime[profile] -= ms_afStartTime[profile]; } void CProfile::ShowResults() { ms_afMaxEndTime[PROFILE_FRAME_RATE] = Max(ms_afMaxEndTime[PROFILE_FRAME_RATE], ms_afEndTime[PROFILE_FRAME_RATE]); ms_afMaxEndTime[PROFILE_PHYSICS] = Max(ms_afMaxEndTime[PROFILE_PHYSICS], ms_afEndTime[PROFILE_PHYSICS]); ms_afMaxEndTime[PROFILE_COLLISION] = Max(ms_afMaxEndTime[PROFILE_COLLISION], ms_afEndTime[PROFILE_COLLISION]); ms_afMaxEndTime[PROFILE_PED_AI] = Max(ms_afMaxEndTime[PROFILE_PED_AI], ms_afEndTime[PROFILE_PED_AI]); ms_afMaxEndTime[PROFILE_PROCESSING_TIME] = Max(ms_afMaxEndTime[PROFILE_PROCESSING_TIME], ms_afEndTime[PROFILE_PROCESSING_TIME]); ms_afMaxEndTime[PROFILE_RENDERING_TIME] = Max(ms_afMaxEndTime[PROFILE_RENDERING_TIME], ms_afEndTime[PROFILE_RENDERING_TIME]); ms_afMaxEndTime[PROFILE_TOTAL] = Max(ms_afMaxEndTime[PROFILE_TOTAL], ms_afEndTime[PROFILE_TOTAL]); ms_afMaxCumulativeTime[PROFILE_FRAME_RATE] = Max(ms_afMaxCumulativeTime[PROFILE_FRAME_RATE], ms_afCumulativeTime[PROFILE_FRAME_RATE]); ms_afMaxCumulativeTime[PROFILE_PHYSICS] = Max(ms_afMaxCumulativeTime[PROFILE_PHYSICS], ms_afCumulativeTime[PROFILE_PHYSICS]); ms_afMaxCumulativeTime[PROFILE_COLLISION] = Max(ms_afMaxCumulativeTime[PROFILE_COLLISION], ms_afCumulativeTime[PROFILE_COLLISION]); ms_afMaxCumulativeTime[PROFILE_PED_AI] = Max(ms_afMaxCumulativeTime[PROFILE_PED_AI], ms_afCumulativeTime[PROFILE_PED_AI]); ms_afMaxCumulativeTime[PROFILE_PROCESSING_TIME] = Max(ms_afMaxCumulativeTime[PROFILE_PROCESSING_TIME], ms_afCumulativeTime[PROFILE_PROCESSING_TIME]); ms_afMaxCumulativeTime[PROFILE_RENDERING_TIME] = Max(ms_afMaxCumulativeTime[PROFILE_RENDERING_TIME], ms_afCumulativeTime[PROFILE_RENDERING_TIME]); ms_afMaxCumulativeTime[PROFILE_TOTAL] = Max(ms_afMaxCumulativeTime[PROFILE_TOTAL], ms_afCumulativeTime[PROFILE_TOTAL]); } #endif ================================================ FILE: src/core/Profile.h ================================================ #pragma once enum eProfile { PROFILE_FRAME_RATE, PROFILE_PHYSICS, PROFILE_COLLISION, PROFILE_PED_AI, PROFILE_PROCESSING_TIME, PROFILE_RENDERING_TIME, PROFILE_TOTAL, NUM_PROFILES, }; class CProfile { static float ms_afStartTime[NUM_PROFILES]; static float ms_afCumulativeTime[NUM_PROFILES]; static float ms_afEndTime[NUM_PROFILES]; static float ms_afMaxEndTime[NUM_PROFILES]; static float ms_afMaxCumulativeTime[NUM_PROFILES]; static Const char *ms_pProfileString[NUM_PROFILES]; static RwRGBA ms_aBarColours[NUM_PROFILES]; public: static void Initialise(); static void SuspendProfile(eProfile profile); static void ShowResults(); }; ================================================ FILE: src/core/Radar.cpp ================================================ #if (!defined(GTA_PS2_STUFF) && defined(RWLIBS)) || defined(__MWERKS__) #define WITHD3D #endif #include "config.h" #include "common.h" #include "RwHelper.h" #include "Radar.h" #include "Camera.h" #include "Hud.h" #include "World.h" #include "Frontend.h" #include "General.h" #include "Vehicle.h" #include "Pools.h" #include "Script.h" #include "TxdStore.h" #include "World.h" #include "Streaming.h" #include "SpecialFX.h" #include "Font.h" float CRadar::m_radarRange; sRadarTrace CRadar::ms_RadarTrace[NUMRADARBLIPS]; CVector2D vec2DRadarOrigin; int32 gRadarTxdIds[64]; CSprite2d CRadar::CentreSprite; CSprite2d CRadar::MapHereSprite; CSprite2d CRadar::NorthSprite; CSprite2d CRadar::AverySprite; CSprite2d CRadar::BikerSprite; CSprite2d CRadar::CortezSprite; CSprite2d CRadar::DiazSprite; CSprite2d CRadar::KentSprite; CSprite2d CRadar::LawyerSprite; CSprite2d CRadar::PhilSprite; CSprite2d CRadar::BikersSprite; CSprite2d CRadar::BoatyardSprite; CSprite2d CRadar::MalibuClubSprite; CSprite2d CRadar::CubansSprite; CSprite2d CRadar::FilmSprite; CSprite2d CRadar::GunSprite; CSprite2d CRadar::HaitiansSprite; CSprite2d CRadar::HardwareSprite; CSprite2d CRadar::SaveHouseSprite; CSprite2d CRadar::StripSprite; CSprite2d CRadar::IceSprite; CSprite2d CRadar::KCabsSprite; CSprite2d CRadar::LovefistSprite; CSprite2d CRadar::PrintworksSprite; CSprite2d CRadar::PropertySprite; CSprite2d CRadar::SunYardSprite; CSprite2d CRadar::SpraySprite; CSprite2d CRadar::TShirtSprite; CSprite2d CRadar::TommySprite; CSprite2d CRadar::PhoneSprite; CSprite2d CRadar::RadioWildstyleSprite; CSprite2d CRadar::RadioFlashSprite; CSprite2d CRadar::RadioKChatSprite; CSprite2d CRadar::RadioFeverSprite; CSprite2d CRadar::RadioVRockSprite; CSprite2d CRadar::RadioVCPRSprite; CSprite2d CRadar::RadioEspantosoSprite; CSprite2d CRadar::RadioEmotionSprite; CSprite2d CRadar::RadioWaveSprite; CSprite2d *CRadar::RadarSprites[RADAR_SPRITE_COUNT] = { nil, &CentreSprite, &MapHereSprite, &NorthSprite, &AverySprite, &BikerSprite, &CortezSprite, &DiazSprite, &KentSprite, &LawyerSprite, &PhilSprite, &BikersSprite, &BoatyardSprite, &MalibuClubSprite, &CubansSprite, &FilmSprite, &GunSprite, &HaitiansSprite, &HardwareSprite, &SaveHouseSprite, &StripSprite, &IceSprite, &KCabsSprite, &LovefistSprite, &PrintworksSprite, &PropertySprite, &SunYardSprite, &SpraySprite, &TShirtSprite, &TommySprite, &PhoneSprite, &RadioWildstyleSprite, &RadioFlashSprite, &RadioKChatSprite, &RadioFeverSprite, &RadioVRockSprite, &RadioVCPRSprite, &RadioEspantosoSprite, &RadioEmotionSprite, &RadioWaveSprite }; // Why this doesn't coincide with world coordinates i don't know #define RADAR_MIN_X (-2000.0f) #define RADAR_MIN_Y (-2000.0f) #define RADAR_MAX_X (2000.0f) #define RADAR_MAX_Y (2000.0f) #define RADAR_SIZE_X (RADAR_MAX_X - RADAR_MIN_X) #define RADAR_SIZE_Y (RADAR_MAX_Y - RADAR_MIN_Y) #define RADAR_NUM_TILES (8) #define RADAR_TILE_SIZE (RADAR_SIZE_X / RADAR_NUM_TILES) static_assert(RADAR_TILE_SIZE == (RADAR_SIZE_Y / RADAR_NUM_TILES), "CRadar: not a square"); #define RADAR_MIN_RANGE (120.0f) #define RADAR_MAX_RANGE (350.0f) #define RADAR_MIN_SPEED (0.3f) #define RADAR_MAX_SPEED (0.9f) CRGBA CRadar::ArrowBlipColour1; CRGBA CRadar::ArrowBlipColour2; int16 CRadar::MapLegendCounter; int16 CRadar::MapLegendList[NUM_MAP_LEGENDS]; #ifdef MAP_ENHANCEMENTS int CRadar::TargetMarkerId = -1; CVector CRadar::TargetMarkerPos; #endif float CRadar::cachedCos; float CRadar::cachedSin; void ClipRadarTileCoords(int32 &x, int32 &y) { if (x < 0) x = 0; if (x > RADAR_NUM_TILES-1) x = RADAR_NUM_TILES-1; if (y < 0) y = 0; if (y > RADAR_NUM_TILES-1) y = RADAR_NUM_TILES-1; } void RequestMapSection(int32 x, int32 y) { ClipRadarTileCoords(x, y); CStreaming::RequestTxd(gRadarTxdIds[x + RADAR_NUM_TILES * y], STREAMFLAGS_DONT_REMOVE | STREAMFLAGS_DEPENDENCY); } void RemoveMapSection(int32 x, int32 y) { if (x >= 0 && x <= RADAR_NUM_TILES - 1 && y >= 0 && y <= RADAR_NUM_TILES - 1) CStreaming::RemoveTxd(gRadarTxdIds[x + RADAR_NUM_TILES * y]); } // Transform from section indices to world coordinates void GetTextureCorners(int32 x, int32 y, CVector2D *out) { x = x - RADAR_NUM_TILES/2; y = -(y - RADAR_NUM_TILES/2); // bottom left out[0].x = RADAR_TILE_SIZE * (x); out[0].y = RADAR_TILE_SIZE * (y - 1); // bottom right out[1].x = RADAR_TILE_SIZE * (x + 1); out[1].y = RADAR_TILE_SIZE * (y - 1); // top right out[2].x = RADAR_TILE_SIZE * (x + 1); out[2].y = RADAR_TILE_SIZE * (y); // top left out[3].x = RADAR_TILE_SIZE * (x); out[3].y = RADAR_TILE_SIZE * (y); } bool IsPointInsideRadar(const CVector2D &point) { if (point.x < -1.0f || point.x > 1.0f) return false; if (point.y < -1.0f || point.y > 1.0f) return false; return true; } // clip line p1,p2 against (-1.0, 1.0) in x and y, set out to clipped point closest to p1 int LineRadarBoxCollision(CVector2D &out, const CVector2D &p1, const CVector2D &p2) { float d1, d2; float t; float x, y; float shortest = 1.0f; int edge = -1; // clip against left edge, x = -1.0 d1 = -1.0f - p1.x; d2 = -1.0f - p2.x; if (d1 * d2 < 0.0f) { // they are on opposite sides, get point of intersection t = d1 / (d1 - d2); y = (p2.y - p1.y)*t + p1.y; if (y >= -1.0f && y <= 1.0f && t <= shortest) { out.x = -1.0f; out.y = y; edge = 3; shortest = t; } } // clip against right edge, x = 1.0 d1 = p1.x - 1.0f; d2 = p2.x - 1.0f; if (d1 * d2 < 0.0f) { // they are on opposite sides, get point of intersection t = d1 / (d1 - d2); y = (p2.y - p1.y)*t + p1.y; if (y >= -1.0f && y <= 1.0f && t <= shortest) { out.x = 1.0f; out.y = y; edge = 1; shortest = t; } } // clip against top edge, y = -1.0 d1 = -1.0f - p1.y; d2 = -1.0f - p2.y; if (d1 * d2 < 0.0f) { // they are on opposite sides, get point of intersection t = d1 / (d1 - d2); x = (p2.x - p1.x)*t + p1.x; if (x >= -1.0f && x <= 1.0f && t <= shortest) { out.y = -1.0f; out.x = x; edge = 0; shortest = t; } } // clip against bottom edge, y = 1.0 d1 = p1.y - 1.0f; d2 = p2.y - 1.0f; if (d1 * d2 < 0.0f) { // they are on opposite sides, get point of intersection t = d1 / (d1 - d2); x = (p2.x - p1.x)*t + p1.x; if (x >= -1.0f && x <= 1.0f && t <= shortest) { out.y = 1.0f; out.x = x; edge = 2; shortest = t; } } return edge; } uint8 CRadar::CalculateBlipAlpha(float dist) { if (FrontEndMenuManager.m_bMenuMapActive) return 255; if (dist <= 1.0f) return 255; if (dist <= 10.0f) return (128.0f * ((dist - 1.0f) / 9.0f)) + ((1.0f - (dist - 1.0f) / 9.0f) * 255.0f); return 128; } void CRadar::ChangeBlipBrightness(int32 i, int32 bright) { int index = GetActualBlipArrayIndex(i); if (index != -1) ms_RadarTrace[index].m_bDim = bright != 1; } void CRadar::ChangeBlipColour(int32 i, int32 color) { int index = GetActualBlipArrayIndex(i); if (index != -1) ms_RadarTrace[index].m_nColor = color; } void CRadar::ChangeBlipDisplay(int32 i, eBlipDisplay display) { int index = GetActualBlipArrayIndex(i); if (index != -1) ms_RadarTrace[index].m_eBlipDisplay = display; } void CRadar::ChangeBlipScale(int32 i, int32 scale) { int index = GetActualBlipArrayIndex(i); if (index != -1) ms_RadarTrace[index].m_wScale = scale; } void CRadar::ClearBlip(int32 i) { int index = GetActualBlipArrayIndex(i); if (index != -1) { SetRadarMarkerState(index, false); ms_RadarTrace[index].m_bInUse = false; ms_RadarTrace[index].m_eBlipType = BLIP_NONE; ms_RadarTrace[index].m_eBlipDisplay = BLIP_DISPLAY_NEITHER; ms_RadarTrace[index].m_eRadarSprite = RADAR_SPRITE_NONE; } } void CRadar::ClearBlipForEntity(eBlipType type, int32 id) { for (int i = 0; i < NUMRADARBLIPS; i++) { if (type == ms_RadarTrace[i].m_eBlipType && id == ms_RadarTrace[i].m_nEntityHandle) { SetRadarMarkerState(i, false); ms_RadarTrace[i].m_bInUse = false; ms_RadarTrace[i].m_eBlipType = BLIP_NONE; ms_RadarTrace[i].m_eBlipDisplay = BLIP_DISPLAY_NEITHER; ms_RadarTrace[i].m_eRadarSprite = RADAR_SPRITE_NONE; } }; } // Why not a proper clipping algorithm? int CRadar::ClipRadarPoly(CVector2D *poly, const CVector2D *rect) { CVector2D corners[4] = { CVector2D( 1.0f, -1.0f ), // top right CVector2D( 1.0f, 1.0f ), // bottom right CVector2D( -1.0f, 1.0f ), // bottom left CVector2D( -1.0f, -1.0f ), // top left }; CVector2D tmp; int i, j, n; int laste, e, e1, e2;; bool inside[4]; for (i = 0; i < 4; i++) inside[i] = IsPointInsideRadar(rect[i]); laste = -1; n = 0; for (i = 0; i < 4; i++) if (inside[i]) { // point is inside, just add poly[n++] = rect[i]; } else { // point is outside but line to this point might be clipped e1 = LineRadarBoxCollision(poly[n], rect[i], rect[(i + 4 - 1) % 4]); if (e1 != -1) { laste = e1; n++; } // and line from this point might be clipped as well e2 = LineRadarBoxCollision(poly[n], rect[i], rect[(i + 1) % 4]); if (e2 != -1) { if (e1 == -1) { // if other line wasn't clipped, i.e. it was complete outside, // we may have to insert another vertex if last clipped line // was on a different edge // find the last intersection if we haven't seen it yet if (laste == -1) for (j = 3; j >= i; j--) { // game uses an if here for j == 0 e = LineRadarBoxCollision(tmp, rect[j], rect[(j + 4 - 1) % 4]); if (e != -1) { laste = e; break; } } assert(laste != -1); // insert corners that were skipped tmp = poly[n]; for (e = laste; e != e2; e = (e + 1) % 4) poly[n++] = corners[e]; poly[n] = tmp; } n++; } } if (n == 0) { // If no points, either the rectangle is completely outside or completely surrounds the radar // no idea what's going on here... float m = (rect[0].y - rect[1].y) / (rect[0].x - rect[1].x); if ((m*rect[3].x - rect[3].y) * (m*rect[0].x - rect[0].y) < 0.0f) { m = (rect[0].y - rect[3].y) / (rect[0].x - rect[3].x); if ((m*rect[1].x - rect[1].y) * (m*rect[0].x - rect[0].y) < 0.0f) { poly[0] = corners[0]; poly[1] = corners[1]; poly[2] = corners[2]; poly[3] = corners[3]; n = 4; } } } return n; } bool CRadar::DisplayThisBlip(int32 counter) { switch (ms_RadarTrace[counter].m_eRadarSprite) { case RADAR_SPRITE_SPRAY: case RADAR_SPRITE_GUN: return true; default: return false; } } void CRadar::Draw3dMarkers() { for (int i = 0; i < NUMRADARBLIPS; i++) { if (ms_RadarTrace[i].m_bInUse) { switch (ms_RadarTrace[i].m_eBlipType) { case BLIP_CAR: { CEntity *entity = CPools::GetVehiclePool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { CVector pos = entity->GetPosition(); pos.z += 1.2f * CModelInfo::GetModelInfo(entity->GetModelIndex())->GetColModel()->boundingBox.max.z + 2.5f; C3dMarkers::PlaceMarker(i | (ms_RadarTrace[i].m_BlipIndex << 16), 1, pos, 2.5f, CARBLIP_MARKER_COLOR_R, CARBLIP_MARKER_COLOR_G, CARBLIP_MARKER_COLOR_B, CARBLIP_MARKER_COLOR_A, 1024, 0.2f, 5); } break; } case BLIP_CHAR: { CEntity *entity = CPools::GetPedPool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); if (entity != nil) { if (((CPed*)entity)->InVehicle()) entity = ((CPed * )entity)->m_pMyVehicle; } if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { CVector pos = entity->GetPosition(); pos.z += 3.0f; C3dMarkers::PlaceMarker(i | (ms_RadarTrace[i].m_BlipIndex << 16), 1, pos, 1.5f, CHARBLIP_MARKER_COLOR_R, CHARBLIP_MARKER_COLOR_G, CHARBLIP_MARKER_COLOR_B, CHARBLIP_MARKER_COLOR_A, 1024, 0.2f, 5); } break; } case BLIP_OBJECT: { CEntity *entity = CPools::GetObjectPool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { CVector pos = entity->GetPosition(); pos.z += CModelInfo::GetModelInfo(entity->GetModelIndex())->GetColModel()->boundingBox.max.z + 1.0f + 1.0f; C3dMarkers::PlaceMarker(i | (ms_RadarTrace[i].m_BlipIndex << 16), 1, pos, 1.0f, OBJECTBLIP_MARKER_COLOR_R, OBJECTBLIP_MARKER_COLOR_G, OBJECTBLIP_MARKER_COLOR_B, OBJECTBLIP_MARKER_COLOR_A, 1024, 0.2f, 5); } break; } case BLIP_COORD: break; case BLIP_CONTACT_POINT: if (!CTheScripts::IsPlayerOnAMission()) { if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) C3dMarkers::PlaceMarkerSet(i | (ms_RadarTrace[i].m_BlipIndex << 16), 4, ms_RadarTrace[i].m_vecPos, 2.0f, COORDBLIP_MARKER_COLOR_R, COORDBLIP_MARKER_COLOR_G, COORDBLIP_MARKER_COLOR_B, COORDBLIP_MARKER_COLOR_A, 2048, 0.2f, 0); } break; } } } } void CRadar::DrawBlips() { if (!TheCamera.m_WideScreenOn && CHud::m_Wants_To_Draw_Hud) { #ifdef SECUROM extern uint8 roadBlocksPirateCheck; if (roadBlocksPirateCheck == 2) return; #endif RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); CVector2D out; CVector2D in = CVector2D(0.0f, 0.0f); TransformRadarPointToScreenSpace(out, in); if (!FrontEndMenuManager.m_bMenuMapActive) { float angle; if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN) angle = PI + FindPlayerHeading(); #ifdef FIX_BUGS else if (TheCamera.GetLookDirection() != LOOKING_FORWARD) angle = FindPlayerHeading() - (PI + (TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetPosition() - TheCamera.Cams[TheCamera.ActiveCam].SourceBeforeLookBehind).Heading()); #endif else angle = FindPlayerHeading() - (PI + TheCamera.GetForward().Heading()); DrawRotatingRadarSprite(&CentreSprite, out.x, out.y, angle, 255); CVector2D vec2d; vec2d.x = vec2DRadarOrigin.x; vec2d.y = M_SQRT2 * m_radarRange + vec2DRadarOrigin.y; TransformRealWorldPointToRadarSpace(in, vec2d); LimitRadarPoint(in); TransformRadarPointToScreenSpace(out, in); DrawRadarSprite(RADAR_SPRITE_NORTH, out.x, out.y, 255); } for(int blipId = 0; blipId < NUMRADARBLIPS; blipId++) { if (!ms_RadarTrace[blipId].m_bInUse) continue; switch (ms_RadarTrace[blipId].m_eBlipType) { case BLIP_CAR: case BLIP_CHAR: case BLIP_OBJECT: if (ms_RadarTrace[blipId].m_eRadarSprite == RADAR_SPRITE_PROPERTY && (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive)) DrawEntityBlip(blipId); break; case BLIP_COORD: case BLIP_CONTACT_POINT: if (ms_RadarTrace[blipId].m_eRadarSprite == RADAR_SPRITE_PHONE && (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive)) DrawCoordBlip(blipId); break; default: break; } } // New in VC: Always draw Hardware/gun/pay'n spray/save blips for(int blipId = 0; blipId < NUMRADARBLIPS; blipId++) { if (!ms_RadarTrace[blipId].m_bInUse) continue; if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SAVE && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_HARDWARE && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SPRAY && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_GUN) continue; switch (ms_RadarTrace[blipId].m_eBlipType) { case BLIP_CAR: case BLIP_CHAR: case BLIP_OBJECT: if (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive) DrawEntityBlip(blipId); break; case BLIP_COORD: case BLIP_CONTACT_POINT: if (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive) DrawCoordBlip(blipId); break; default: break; } } for(int blipId = 0; blipId < NUMRADARBLIPS; blipId++) { if (!ms_RadarTrace[blipId].m_bInUse) continue; switch (ms_RadarTrace[blipId].m_eBlipType) { case BLIP_CAR: case BLIP_CHAR: case BLIP_OBJECT: if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SAVE && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_HARDWARE && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SPRAY && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_PROPERTY && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_GUN && (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive)) DrawEntityBlip(blipId); break; default: break; } } for (int blipId = 0; blipId < NUMRADARBLIPS; blipId++) { if (!ms_RadarTrace[blipId].m_bInUse) continue; switch (ms_RadarTrace[blipId].m_eBlipType) { case BLIP_COORD: case BLIP_CONTACT_POINT: if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SAVE && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_HARDWARE && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_SPRAY && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_PROPERTY && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_GUN && ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_PHONE && (!CTheScripts::bPlayerIsInTheStatium || !FrontEndMenuManager.m_bMenuMapActive)) DrawCoordBlip(blipId); break; default: break; } } if (FrontEndMenuManager.m_bMenuMapActive) { CVector2D in, out; if (!CTheScripts::bPlayerIsInTheStatium) TransformRealWorldPointToRadarSpace(in, FindPlayerCentreOfWorld_NoSniperShift()); else TransformRealWorldPointToRadarSpace(in, CVector2D(-1302.5f, 1332.8f)); LimitRadarPoint(in); TransformRadarPointToScreenSpace(out, in); DrawYouAreHereSprite(out.x, out.y); } } } void CRadar::DrawMap() { if (!TheCamera.m_WideScreenOn && CHud::m_Wants_To_Draw_Hud) { CalculateCachedSinCos(); if (FindPlayerVehicle()) { float speed = FindPlayerSpeed().Magnitude(); if (speed < RADAR_MIN_SPEED) m_radarRange = RADAR_MIN_RANGE; else if (speed < RADAR_MAX_SPEED) m_radarRange = (speed - RADAR_MIN_SPEED)/(RADAR_MAX_SPEED-RADAR_MIN_SPEED) * (RADAR_MAX_RANGE-RADAR_MIN_RANGE) + RADAR_MIN_RANGE; else m_radarRange = RADAR_MAX_RANGE; } else m_radarRange = RADAR_MIN_RANGE; vec2DRadarOrigin = CVector2D(FindPlayerCentreOfWorld_NoSniperShift()); if (FrontEndMenuManager.m_PrefsRadarMode != 1) DrawRadarMap(); } } void CRadar::DrawRadarMap() { DrawRadarMask(); // top left ist (0, 0) int x = Floor((vec2DRadarOrigin.x - RADAR_MIN_X) / RADAR_TILE_SIZE); int y = Ceil((RADAR_NUM_TILES - 1) - (vec2DRadarOrigin.y - RADAR_MIN_Y) / RADAR_TILE_SIZE); StreamRadarSections(x, y); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)FALSE); DrawRadarSection(x - 1, y - 1); DrawRadarSection(x, y - 1); DrawRadarSection(x + 1, y - 1); DrawRadarSection(x - 1, y); DrawRadarSection(x, y); DrawRadarSection(x + 1, y); DrawRadarSection(x - 1, y + 1); DrawRadarSection(x, y + 1); DrawRadarSection(x + 1, y + 1); } void CRadar::DrawRadarMask() { CVector2D corners[4] = { CVector2D(1.0f, -1.0f), CVector2D(1.0f, 1.0f), CVector2D(-1.0f, 1.0f), CVector2D(-1.0, -1.0f) }; RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void*)nil); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); #if !defined(GTA_PS2_STUFF) && defined(RWLIBS) RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS); #else RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); #endif CVector2D out[8]; CVector2D in; // Draw the shape we want to mask out from the radar in four segments for (int i = 0; i < 4; i++) { // First point is always the corner itself in.x = corners[i].x; in.y = corners[i].y; TransformRadarPointToScreenSpace(out[0], in); // Then generate a quarter of the circle for (int j = 0; j < 7; j++) { in.x = corners[i].x * Cos(j * (PI / 2.0f / 6.0f)); in.y = corners[i].y * Sin(j * (PI / 2.0f / 6.0f)); TransformRadarPointToScreenSpace(out[j + 1], in); }; CSprite2d::SetMaskVertices(8, (float *)out); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::GetVertices(), 8); } #if !defined(GTA_PS2_STUFF) && defined(RWLIBS) RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); #endif } void CRadar::DrawRadarSection(int32 x, int32 y) { int i; RwTexDictionary *txd; CVector2D worldPoly[8]; CVector2D radarCorners[4]; CVector2D radarPoly[8]; CVector2D texCoords[8]; CVector2D screenPoly[8]; int numVertices; RwTexture *texture = nil; GetTextureCorners(x, y, worldPoly); ClipRadarTileCoords(x, y); if (!CTheScripts::bPlayerIsInTheStatium && (!(txd = CTxdStore::GetSlot(gRadarTxdIds[x + RADAR_NUM_TILES * y])->texDict) || !(texture = GetFirstTexture(txd)))) return; for (i = 0; i < 4; i++) TransformRealWorldPointToRadarSpace(radarCorners[i], worldPoly[i]); numVertices = ClipRadarPoly(radarPoly, radarCorners); // FIX: can return earlier here // if(numVertices == 0) if (numVertices < 3) return; for (i = 0; i < numVertices; i++) { TransformRadarPointToRealWorldSpace(worldPoly[i], radarPoly[i]); TransformRealWorldToTexCoordSpace(texCoords[i], worldPoly[i], x, y); TransformRadarPointToScreenSpace(screenPoly[i], radarPoly[i]); } if (CTheScripts::bPlayerIsInTheStatium) { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); CSprite2d::SetVertices(numVertices, (float*)screenPoly, (float*)texCoords, CRGBA(204, 204, 204, 255)); } else { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(texture)); CSprite2d::SetVertices(numVertices, (float*)screenPoly, (float*)texCoords, CRGBA(255, 255, 255, 255)); } // check done above now // if(numVertices > 2) RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::GetVertices(), numVertices); } void CRadar::DrawRadarSprite(uint16 sprite, float x, float y, uint8 alpha) { RadarSprites[sprite]->Draw(CRect(x - SCREEN_SCALE_X(8.0f), y - SCREEN_SCALE_Y(8.0f), x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(8.0f)), CRGBA(255, 255, 255, alpha)); if (FrontEndMenuManager.m_bMenuMapActive) { bool alreadyThere = false; for (int i = 0; i < NUM_MAP_LEGENDS; i++) { if (MapLegendList[i] == sprite) alreadyThere = true; } if (!alreadyThere) { MapLegendList[MapLegendCounter] = sprite; MapLegendCounter++; } } } void CRadar::DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int32 alpha) { CVector curPosn[4]; const float sizeX = SCREEN_SCALE_X(8.0f); const float correctedAngle = angle - PI / 4.f; const float sizeY = SCREEN_SCALE_Y(8.0f); for (uint32 i = 0; i < 4; i++) { const float cornerAngle = i * HALFPI + correctedAngle; curPosn[i].x = x + (0.0f * Cos(cornerAngle) + 1.0f * Sin(cornerAngle)) * sizeX; curPosn[i].y = y - (0.0f * Sin(cornerAngle) - 1.0f * Cos(cornerAngle)) * sizeY; } sprite->Draw(curPosn[3].x, curPosn[3].y, curPosn[2].x, curPosn[2].y, curPosn[0].x, curPosn[0].y, curPosn[1].x, curPosn[1].y, CRGBA(255, 255, 255, alpha)); } int32 CRadar::GetActualBlipArrayIndex(int32 i) { if (i == -1) return -1; else if ((i & 0xFFFF0000) >> 16 != ms_RadarTrace[(uint16)i].m_BlipIndex) return -1; else return (uint16)i; } int32 CRadar::GetNewUniqueBlipIndex(int32 i) { if (ms_RadarTrace[i].m_BlipIndex >= UINT16_MAX - 1) ms_RadarTrace[i].m_BlipIndex = 1; else ms_RadarTrace[i].m_BlipIndex++; return i | (ms_RadarTrace[i].m_BlipIndex << 16); } uint32 CRadar::GetRadarTraceColour(uint32 color, bool bright) { uint32 c; switch (color) { case RADAR_TRACE_RED: if (bright) c = 0x712B49FF; else c = 0x7F0000FF; break; case RADAR_TRACE_GREEN: if (bright) c = 0x5FA06AFF; else c = 0x007F00FF; break; case RADAR_TRACE_LIGHT_BLUE: if (bright) c = 0x80A7F3FF; else c = 0x00007FFF; break; case RADAR_TRACE_GRAY: if (bright) c = 0xE1E1E1FF; else c = 0x7F7F7FFF; break; case RADAR_TRACE_YELLOW: if (bright) c = 0xFFFF00FF; else c = 0x7F7F00FF; break; case RADAR_TRACE_MAGENTA: if (bright) c = 0xFF00FFFF; else c = 0x7F007FFF; break; case RADAR_TRACE_CYAN: if (bright) c = 0x00FFFFFF; else c = 0x007F7FFF; break; default: c = color; break; }; return c; } const char* gRadarTexNames[] = { "radar00", "radar01", "radar02", "radar03", "radar04", "radar05", "radar06", "radar07", "radar08", "radar09", "radar10", "radar11", "radar12", "radar13", "radar14", "radar15", "radar16", "radar17", "radar18", "radar19", "radar20", "radar21", "radar22", "radar23", "radar24", "radar25", "radar26", "radar27", "radar28", "radar29", "radar30", "radar31", "radar32", "radar33", "radar34", "radar35", "radar36", "radar37", "radar38", "radar39", "radar40", "radar41", "radar42", "radar43", "radar44", "radar45", "radar46", "radar47", "radar48", "radar49", "radar50", "radar51", "radar52", "radar53", "radar54", "radar55", "radar56", "radar57", "radar58", "radar59", "radar60", "radar61", "radar62", "radar63", }; void CRadar::Initialise() { for (int i = 0; i < NUMRADARBLIPS; i++) { ms_RadarTrace[i].m_BlipIndex = 1; SetRadarMarkerState(i, false); ms_RadarTrace[i].m_bInUse = false; ms_RadarTrace[i].m_bShortRange = false; ms_RadarTrace[i].m_eBlipType = BLIP_NONE; ms_RadarTrace[i].m_eBlipDisplay = BLIP_DISPLAY_NEITHER; ms_RadarTrace[i].m_eRadarSprite = RADAR_SPRITE_NONE; } m_radarRange = 350.0f; for (int i = 0; i < 64; i++) gRadarTxdIds[i] = CTxdStore::FindTxdSlot(gRadarTexNames[i]); } float CRadar::LimitRadarPoint(CVector2D &point) { float dist, invdist; dist = point.Magnitude(); if (FrontEndMenuManager.m_bMenuMapActive) return dist; if (dist > 1.0f) { invdist = 1.0f / dist; point.x *= invdist; point.y *= invdist; } return dist; } void CRadar::LoadAllRadarBlips(uint8 *buf, uint32 size) { Initialise(); INITSAVEBUF CheckSaveHeader(buf, 'R', 'D', 'R', '\0', size - SAVE_HEADER_SIZE); for (int i = 0; i < NUMRADARBLIPS; i++) { ms_RadarTrace[i].m_nColor = ReadSaveBuf(buf); ms_RadarTrace[i].m_Radius = ReadSaveBuf(buf); ms_RadarTrace[i].m_eBlipType = ReadSaveBuf(buf); ms_RadarTrace[i].m_nEntityHandle = ReadSaveBuf(buf); ms_RadarTrace[i].m_vec2DPos.x = ReadSaveBuf(buf); // CVector2D ms_RadarTrace[i].m_vec2DPos.y = ReadSaveBuf(buf); ms_RadarTrace[i].m_vecPos = ReadSaveBuf(buf); ms_RadarTrace[i].m_BlipIndex = ReadSaveBuf(buf); ms_RadarTrace[i].m_bDim = ReadSaveBuf(buf); ms_RadarTrace[i].m_bInUse = ReadSaveBuf(buf); ms_RadarTrace[i].m_bShortRange = ReadSaveBuf(buf); ms_RadarTrace[i].m_unused = ReadSaveBuf(buf); ms_RadarTrace[i].m_wScale = ReadSaveBuf(buf); ms_RadarTrace[i].m_eBlipDisplay = ReadSaveBuf(buf); ms_RadarTrace[i].m_eRadarSprite = ReadSaveBuf(buf); } VALIDATESAVEBUF(size); } void CRadar::SaveAllRadarBlips(uint8 *buf, uint32 *size) { *size = SAVE_HEADER_SIZE + NUMRADARBLIPS * sizeof(sRadarTraceSave); INITSAVEBUF WriteSaveHeader(buf, 'R', 'D', 'R', '\0', *size - SAVE_HEADER_SIZE); #ifdef MAP_ENHANCEMENTS if (TargetMarkerId != -1) { ClearBlip(TargetMarkerId); TargetMarkerId = -1; } #endif for (int i = 0; i < NUMRADARBLIPS; i++) { sRadarTraceSave *saveStruct = (sRadarTraceSave*) buf; saveStruct->m_nColor = ms_RadarTrace[i].m_nColor; saveStruct->m_Radius = ms_RadarTrace[i].m_Radius; saveStruct->m_eBlipType = ms_RadarTrace[i].m_eBlipType; saveStruct->m_nEntityHandle = ms_RadarTrace[i].m_nEntityHandle; saveStruct->m_vec2DPos = ms_RadarTrace[i].m_vec2DPos; saveStruct->m_vecPos = ms_RadarTrace[i].m_vecPos; saveStruct->m_BlipIndex = ms_RadarTrace[i].m_BlipIndex; saveStruct->m_bDim = ms_RadarTrace[i].m_bDim; saveStruct->m_bInUse = ms_RadarTrace[i].m_bInUse; saveStruct->m_bShortRange = ms_RadarTrace[i].m_bShortRange; saveStruct->m_unused = ms_RadarTrace[i].m_unused; saveStruct->m_wScale = ms_RadarTrace[i].m_wScale; saveStruct->m_eBlipDisplay = ms_RadarTrace[i].m_eBlipDisplay; saveStruct->m_eRadarSprite = ms_RadarTrace[i].m_eRadarSprite; SkipSaveBuf(buf, sizeof(sRadarTraceSave)); } VALIDATESAVEBUF(*size); } void CRadar::LoadTextures() { CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("hud")); CentreSprite.SetTexture("radar_centre"); MapHereSprite.SetTexture("arrow"); NorthSprite.SetTexture("radar_north"); AverySprite.SetTexture("radar_avery"); BikerSprite.SetTexture("radar_biker"); CortezSprite.SetTexture("radar_cortez"); DiazSprite.SetTexture("radar_diaz"); KentSprite.SetTexture("radar_kent"); LawyerSprite.SetTexture("radar_lawyer"); PhilSprite.SetTexture("radar_phil"); BikersSprite.SetTexture("bikers"); BoatyardSprite.SetTexture("boatyard"); MalibuClubSprite.SetTexture("club"); CubansSprite.SetTexture("cubans"); FilmSprite.SetTexture("filmstudio"); GunSprite.SetTexture("gun"); HaitiansSprite.SetTexture("haitians"); HardwareSprite.SetTexture("hardware"); SaveHouseSprite.SetTexture("radar_save"); StripSprite.SetTexture("radar_strip"); IceSprite.SetTexture("icecream"); KCabsSprite.SetTexture("kcabs"); LovefistSprite.SetTexture("lovefist"); PrintworksSprite.SetTexture("printworks"); PropertySprite.SetTexture("property"); SunYardSprite.SetTexture("SunYard"); SpraySprite.SetTexture("spray"); TShirtSprite.SetTexture("tshirt"); TommySprite.SetTexture("tommy"); PhoneSprite.SetTexture("phone"); RadioWildstyleSprite.SetTexture("RWildstyle"); RadioFlashSprite.SetTexture("RFlash"); RadioKChatSprite.SetTexture("RKchat"); RadioFeverSprite.SetTexture("RFever"); RadioVRockSprite.SetTexture("RVRock"); RadioVCPRSprite.SetTexture("RVCPR"); RadioEspantosoSprite.SetTexture("REspantoso"); RadioEmotionSprite.SetTexture("REmotion"); RadioWaveSprite.SetTexture("RWave"); CTxdStore::PopCurrentTxd(); } void CRadar::RemoveRadarSections() { for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) RemoveMapSection(i, j); } void CRadar::SetBlipSprite(int32 i, int32 icon) { int index = CRadar::GetActualBlipArrayIndex(i); if (index != -1) { ms_RadarTrace[index].m_eRadarSprite = icon; } } int CRadar::SetCoordBlip(eBlipType type, CVector pos, uint32 color, eBlipDisplay display) { int nextBlip; for (nextBlip = 0; nextBlip < NUMRADARBLIPS; nextBlip++) { if (!ms_RadarTrace[nextBlip].m_bInUse) break; } #ifdef FIX_BUGS if (nextBlip == NUMRADARBLIPS) return -1; #endif ms_RadarTrace[nextBlip].m_eBlipType = type; ms_RadarTrace[nextBlip].m_nColor = RADAR_TRACE_MAGENTA; ms_RadarTrace[nextBlip].m_bDim = true; ms_RadarTrace[nextBlip].m_bInUse = true; ms_RadarTrace[nextBlip].m_bShortRange = false; ms_RadarTrace[nextBlip].m_Radius = 1.0f; ms_RadarTrace[nextBlip].m_vec2DPos = pos; ms_RadarTrace[nextBlip].m_vecPos = pos; ms_RadarTrace[nextBlip].m_nEntityHandle = 0; ms_RadarTrace[nextBlip].m_wScale = 1; ms_RadarTrace[nextBlip].m_eBlipDisplay = display; ms_RadarTrace[nextBlip].m_eRadarSprite = RADAR_SPRITE_NONE; return CRadar::GetNewUniqueBlipIndex(nextBlip); } int CRadar::SetShortRangeCoordBlip(eBlipType type, CVector pos, uint32 color, eBlipDisplay display) { int index = SetCoordBlip(type, pos, color, display); if (index == -1) return -1; ms_RadarTrace[GetActualBlipArrayIndex(index)].m_bShortRange = true; return index; } int CRadar::SetEntityBlip(eBlipType type, int32 handle, uint32 color, eBlipDisplay display) { int nextBlip; for (nextBlip = 0; nextBlip < NUMRADARBLIPS; nextBlip++) { if (!ms_RadarTrace[nextBlip].m_bInUse) break; } #ifdef FIX_BUGS if (nextBlip == NUMRADARBLIPS) return -1; #endif ms_RadarTrace[nextBlip].m_eBlipType = type; ms_RadarTrace[nextBlip].m_nColor = RADAR_TRACE_YELLOW; ms_RadarTrace[nextBlip].m_bDim = true; ms_RadarTrace[nextBlip].m_bInUse = true; ms_RadarTrace[nextBlip].m_bShortRange = false; ms_RadarTrace[nextBlip].m_Radius = 1.0f; ms_RadarTrace[nextBlip].m_nEntityHandle = handle; ms_RadarTrace[nextBlip].m_wScale = 1; ms_RadarTrace[nextBlip].m_eBlipDisplay = display; ms_RadarTrace[nextBlip].m_eRadarSprite = RADAR_SPRITE_NONE; return GetNewUniqueBlipIndex(nextBlip); } void CRadar::SetRadarMarkerState(int32 counter, bool flag) { CEntity *e; switch (ms_RadarTrace[counter].m_eBlipType) { case BLIP_CAR: e = CPools::GetVehiclePool()->GetAt(ms_RadarTrace[counter].m_nEntityHandle); break; case BLIP_CHAR: e = CPools::GetPedPool()->GetAt(ms_RadarTrace[counter].m_nEntityHandle); break; case BLIP_OBJECT: e = CPools::GetObjectPool()->GetAt(ms_RadarTrace[counter].m_nEntityHandle); break; default: return; } if (e) e->bHasBlip = flag; } void CRadar::ShowRadarMarker(CVector pos, uint32 color, float radius) { float f1 = radius * 1.4f; float f2 = radius * 0.5f; CVector p1, p2; p1 = pos + TheCamera.GetUp()*f1; p2 = pos + TheCamera.GetUp()*f2; CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); p1 = pos - TheCamera.GetUp()*f1; p2 = pos - TheCamera.GetUp()*f2; CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); p1 = pos + TheCamera.GetRight()*f1; p2 = pos + TheCamera.GetRight()*f2; CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); p1 = pos - TheCamera.GetRight()*f1; p2 = pos - TheCamera.GetRight()*f2; CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); } void CRadar::ShowRadarTrace(float x, float y, uint32 size, uint8 red, uint8 green, uint8 blue, uint8 alpha) { if (!CHud::m_Wants_To_Draw_Hud || TheCamera.m_WideScreenOn) return; CSprite2d::DrawRect(CRect(x - SCREEN_SCALE_X(size + 1.0f), y - SCREEN_SCALE_Y(size + 1.0f), SCREEN_SCALE_X(size + 1.0f) + x, SCREEN_SCALE_Y(size + 1.0f) + y), CRGBA(0, 0, 0, alpha)); CSprite2d::DrawRect(CRect(x - SCREEN_SCALE_X(size), y - SCREEN_SCALE_Y(size), SCREEN_SCALE_X(size) + x, SCREEN_SCALE_Y(size) + y), CRGBA(red, green, blue, alpha)); } void CRadar::ShowRadarTraceWithHeight(float x, float y, uint32 size, uint8 red, uint8 green, uint8 blue, uint8 alpha, uint8 mode) { if (!CHud::m_Wants_To_Draw_Hud || TheCamera.m_WideScreenOn) return; switch (mode) { case BLIP_MODE_TRIANGULAR_UP: size++; CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(size + 3.0f), y + SCREEN_SCALE_Y(size + 2.0f), x - (SCREEN_SCALE_X(size + 3.0f)), y + SCREEN_SCALE_Y(size + 2.0f), x, y - (SCREEN_SCALE_Y(size + 3.0f)), x, y - (SCREEN_SCALE_Y(size + 3.0f)), CRGBA(0, 0, 0, alpha)); CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(size + 1.0f), y + SCREEN_SCALE_Y(size + 1.0f), x - (SCREEN_SCALE_X(size + 1.0f)), y + SCREEN_SCALE_Y(size + 1.0f), x, y - (SCREEN_SCALE_Y(size + 1.0f)), x, y - (SCREEN_SCALE_Y(size + 1.0f)), CRGBA(red, green, blue, alpha)); break; case BLIP_MODE_TRIANGULAR_DOWN: size++; CSprite2d::Draw2DPolygon(x, y + SCREEN_SCALE_Y(size + 2.0f), x, y + SCREEN_SCALE_Y(size + 3.0f), x + SCREEN_SCALE_X(size + 3.0f), y - (SCREEN_SCALE_Y(size + 2.0f)), x - (SCREEN_SCALE_X(size + 3.0f)), y - (SCREEN_SCALE_Y(size + 2.0f)), CRGBA(0, 0, 0, alpha)); CSprite2d::Draw2DPolygon(x, y + SCREEN_SCALE_Y(size + 1.0f), x, y + SCREEN_SCALE_Y(size + 1.0f), x + SCREEN_SCALE_X(size + 1.0f), y - (SCREEN_SCALE_Y(size + 1.0f)), x - (SCREEN_SCALE_X(size + 1.0f)), y - (SCREEN_SCALE_Y(size + 1.0f)), CRGBA(red, green, blue, alpha)); break; case BLIP_MODE_SQUARE: CSprite2d::DrawRect(CRect(x - SCREEN_SCALE_X(size + 1.0f), y - SCREEN_SCALE_Y(size + 1.0f), SCREEN_SCALE_X(size + 1.0f) + x, SCREEN_SCALE_Y(size + 1.0f) + y), CRGBA(0, 0, 0, alpha)); CSprite2d::DrawRect(CRect(x - SCREEN_SCALE_X(size), y - SCREEN_SCALE_Y(size), SCREEN_SCALE_X(size) + x, SCREEN_SCALE_Y(size) + y), CRGBA(red, green, blue, alpha)); break; } } void CRadar::Shutdown() { CentreSprite.Delete(); MapHereSprite.Delete(); NorthSprite.Delete(); AverySprite.Delete(); BikerSprite.Delete(); CortezSprite.Delete(); DiazSprite.Delete(); KentSprite.Delete(); LawyerSprite.Delete(); PhilSprite.Delete(); BikersSprite.Delete(); BoatyardSprite.Delete(); MalibuClubSprite.Delete(); CubansSprite.Delete(); FilmSprite.Delete(); GunSprite.Delete(); HaitiansSprite.Delete(); HardwareSprite.Delete(); SaveHouseSprite.Delete(); StripSprite.Delete(); IceSprite.Delete(); KCabsSprite.Delete(); LovefistSprite.Delete(); PrintworksSprite.Delete(); PropertySprite.Delete(); SunYardSprite.Delete(); SpraySprite.Delete(); TShirtSprite.Delete(); TommySprite.Delete(); PhoneSprite.Delete(); RadioWildstyleSprite.Delete(); RadioFlashSprite.Delete(); RadioKChatSprite.Delete(); RadioFeverSprite.Delete(); RadioVRockSprite.Delete(); RadioVCPRSprite.Delete(); RadioEspantosoSprite.Delete(); RadioEmotionSprite.Delete(); RadioWaveSprite.Delete(); RemoveRadarSections(); } void CRadar::StreamRadarSections(const CVector &posn) { if (!CStreaming::ms_disableStreaming) StreamRadarSections(Floor((RADAR_MAX_X + posn.x) / RADAR_TILE_SIZE), Ceil((RADAR_NUM_TILES - 1) - (RADAR_MAX_Y + posn.y) / RADAR_TILE_SIZE)); } void CRadar::StreamRadarSections(int32 x, int32 y) { for (int i = 0; i < RADAR_NUM_TILES; ++i) { for (int j = 0; j < RADAR_NUM_TILES; ++j) { if ((i >= x - 1 && i <= x + 1) && (j >= y - 1 && j <= y + 1)) RequestMapSection(i, j); else RemoveMapSection(i, j); }; }; } void CRadar::TransformRealWorldToTexCoordSpace(CVector2D &out, const CVector2D &in, int32 x, int32 y) { out.x = in.x - (x * RADAR_TILE_SIZE + RADAR_MIN_X); out.y = -(in.y - ((RADAR_NUM_TILES - y) * RADAR_TILE_SIZE + RADAR_MIN_Y)); out.x /= RADAR_TILE_SIZE; out.y /= RADAR_TILE_SIZE; } void CRadar::TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in) { float s, c; s = -cachedSin; c = cachedCos; out.x = s * in.y + c * in.x; out.y = c * in.y - s * in.x; out = out * m_radarRange + vec2DRadarOrigin; } // Radar space goes from -1.0 to 1.0 in x and y, top right is (1.0, 1.0) void CRadar::TransformRadarPointToScreenSpace(CVector2D &out, const CVector2D &in) { if (FrontEndMenuManager.m_bMenuMapActive) { out.x = (FrontEndMenuManager.m_fMapCenterX - FrontEndMenuManager.m_fMapSize) + (MENU_MAP_LENGTH / 2 + MENU_MAP_LEFT_OFFSET + in.x) * FrontEndMenuManager.m_fMapSize * MENU_MAP_WIDTH_SCALE * 2.0f / MENU_MAP_LENGTH; out.y = (FrontEndMenuManager.m_fMapCenterY - FrontEndMenuManager.m_fMapSize) + (MENU_MAP_LENGTH / 2 - MENU_MAP_TOP_OFFSET - in.y) * FrontEndMenuManager.m_fMapSize * MENU_MAP_HEIGHT_SCALE * 2.0f / MENU_MAP_LENGTH; } else { #ifdef FIX_BUGS out.x = (in.x + 1.0f) * 0.5f * SCREEN_SCALE_X(RADAR_WIDTH) + SCREEN_SCALE_X(RADAR_LEFT); #else out.x = (in.x + 1.0f) * 0.5f * SCREEN_SCALE_X(RADAR_WIDTH) + RADAR_LEFT; #endif out.y = (1.0f - in.y) * 0.5f * SCREEN_SCALE_Y(RADAR_HEIGHT) + SCREEN_SCALE_FROM_BOTTOM(RADAR_BOTTOM + RADAR_HEIGHT); } } void CRadar::TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D &in) { float s, c; s = cachedSin; c = cachedCos; float x = (in.x - vec2DRadarOrigin.x) * (1.0f / m_radarRange); float y = (in.y - vec2DRadarOrigin.y) * (1.0f / m_radarRange); out.x = s * y + c * x; out.y = c * y - s * x; } void CRadar::CalculateCachedSinCos() { if (/*TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || */ FrontEndMenuManager.m_bMenuMapActive ) { cachedSin = 0.0f; cachedCos = 1.0f; } else if (TheCamera.GetLookDirection() == LOOKING_FORWARD) { cachedSin = Sin(TheCamera.GetForward().Heading()); cachedCos = Cos(TheCamera.GetForward().Heading()); } else { CVector forward; if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON) { forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetForward(); forward.Normalise(); // a bit useless... } else forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetPosition() - TheCamera.Cams[TheCamera.ActiveCam].SourceBeforeLookBehind; cachedSin = Sin(forward.Heading()); cachedCos = Cos(forward.Heading()); } } void CRadar::InitFrontEndMap() { CalculateCachedSinCos(); vec2DRadarOrigin.x = 0.0f; vec2DRadarOrigin.y = 0.0f; m_radarRange = MENU_MAP_LENGTH_UNIT; // just affects the multiplier in TransformRadarPointToScreenSpace for (int i = 0; i < NUM_MAP_LEGENDS; i++) { MapLegendList[i] = RADAR_SPRITE_NONE; } MapLegendCounter = 0; ArrowBlipColour1 = CRGBA(0, 0, 0, 0); ArrowBlipColour2 = CRGBA(0, 0, 0, 0); } void CRadar::DrawYouAreHereSprite(float x, float y) { static PauseModeTime lastChange = 0; static bool show = true; if (show) { if (CTimer::GetTimeInMillisecondsPauseMode() - lastChange > 500) { lastChange = CTimer::GetTimeInMillisecondsPauseMode(); show = !show; } } else { if (CTimer::GetTimeInMillisecondsPauseMode() - lastChange > 200) { lastChange = CTimer::GetTimeInMillisecondsPauseMode(); show = !show; } } if (show) { const float left = x - SCREEN_SCALE_X(8.0f); const float top = y - SCREEN_SCALE_Y(40.0f); const float right = x + SCREEN_SCALE_X(40.0); const float bottom = y + SCREEN_SCALE_Y(8.0f); MapHereSprite.Draw(CRect(left + SCREEN_SCALE_X(2.f), top + SCREEN_SCALE_Y(2.f), right + SCREEN_SCALE_X(2.f), bottom + SCREEN_SCALE_Y(2.f)), CRGBA(0, 0, 0, 255)); MapHereSprite.Draw(CRect(left, top, right, bottom), CRGBA(255, 255, 255, 255)); CFont::SetWrapx(right + SCREEN_SCALE_X(28.0f)); CFont::SetRightJustifyWrap(right); CFont::SetBackGroundOnlyTextOff(); CFont::SetColor(CRGBA(255, 150, 225, 255)); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetCentreOff(); CFont::SetRightJustifyOff(); CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(SCREEN_SCALE_X(0.65f), SCREEN_SCALE_Y(0.95f)); CFont::PrintString(right, top, TheText.Get("MAP_YAH")); CFont::SetDropShadowPosition(0); CFont::DrawFonts(); } MapLegendList[MapLegendCounter++] = RADAR_SPRITE_MAP_HERE; } #ifdef MAP_ENHANCEMENTS void CRadar::ToggleTargetMarker(float x, float y) { if (TargetMarkerId == -1) { int nextBlip; for (nextBlip = 0; nextBlip < NUMRADARBLIPS; nextBlip++) { if (!ms_RadarTrace[nextBlip].m_bInUse) break; } if (nextBlip == NUMRADARBLIPS) return; ms_RadarTrace[nextBlip].m_eBlipType = BLIP_COORD; ms_RadarTrace[nextBlip].m_nColor = RADAR_TRACE_GRAY; ms_RadarTrace[nextBlip].m_bDim = 0; ms_RadarTrace[nextBlip].m_bInUse = 1; ms_RadarTrace[nextBlip].m_Radius = 1.0f; CVector pos(x, y, CWorld::FindGroundZForCoord(x,y)); TargetMarkerPos = pos; ms_RadarTrace[nextBlip].m_vec2DPos = pos; ms_RadarTrace[nextBlip].m_vecPos = pos; ms_RadarTrace[nextBlip].m_nEntityHandle = 0; ms_RadarTrace[nextBlip].m_wScale = 5; ms_RadarTrace[nextBlip].m_eBlipDisplay = BLIP_DISPLAY_BLIP_ONLY; ms_RadarTrace[nextBlip].m_eRadarSprite = RADAR_SPRITE_NONE; TargetMarkerId = CRadar::GetNewUniqueBlipIndex(nextBlip); } else { ClearBlip(TargetMarkerId); TargetMarkerId = -1; } } #endif void CRadar::DrawEntityBlip(int32 blipId) { CVector2D out; CVector2D in; CEntity *blipEntity; switch (ms_RadarTrace[blipId].m_eBlipType) { case BLIP_CAR: blipEntity = CPools::GetVehiclePool()->GetAt(ms_RadarTrace[blipId].m_nEntityHandle); break; case BLIP_CHAR: blipEntity = CPools::GetPedPool()->GetAt(ms_RadarTrace[blipId].m_nEntityHandle); if (blipEntity != nil) { if (((CPed*)blipEntity)->InVehicle()) blipEntity = ((CPed*)blipEntity)->m_pMyVehicle; } break; case BLIP_OBJECT: blipEntity = CPools::GetObjectPool()->GetAt(ms_RadarTrace[blipId].m_nEntityHandle); break; default: break; } if (blipEntity) { uint32 color = GetRadarTraceColour(ms_RadarTrace[blipId].m_nColor, ms_RadarTrace[blipId].m_bDim); if (ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { if (CTheScripts::IsDebugOn()) { ShowRadarMarker(blipEntity->GetPosition(), color, ms_RadarTrace[blipId].m_Radius); ms_RadarTrace[blipId].m_Radius = ms_RadarTrace[blipId].m_Radius - 0.1f; if (ms_RadarTrace[blipId].m_Radius < 1.0f) ms_RadarTrace[blipId].m_Radius = 5.0f; } } if (ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BLIP_ONLY) { TransformRealWorldPointToRadarSpace(in, blipEntity->GetPosition()); float dist = LimitRadarPoint(in); TransformRadarPointToScreenSpace(out, in); if (!ms_RadarTrace[blipId].m_bShortRange || dist <= 1.0f || FrontEndMenuManager.m_bMenuMapActive) { if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_NONE) { DrawRadarSprite(ms_RadarTrace[blipId].m_eRadarSprite, out.x, out.y, CalculateBlipAlpha(dist)); } else { const CVector& pos = FindPlayerCentreOfWorld_NoSniperShift(); const CVector& blipPos = blipEntity->GetPosition(); uint8 mode = BLIP_MODE_TRIANGULAR_UP; if (blipPos.z - pos.z <= 2.0f) { if (blipPos.z - pos.z < -4.0f) mode = BLIP_MODE_TRIANGULAR_DOWN; else mode = BLIP_MODE_SQUARE; } ShowRadarTraceWithHeight(out.x, out.y, ms_RadarTrace[blipId].m_wScale, (uint8)(color >> 24), (uint8)(color >> 16), (uint8)(color >> 8), 255, mode); if (FrontEndMenuManager.m_bMenuMapActive) { bool alreadyThere = false; for (int i = 0; i < NUM_MAP_LEGENDS; i++) { if (MapLegendList[i] == -2) alreadyThere = true; } if (!alreadyThere) { MapLegendList[MapLegendCounter] = -2; MapLegendCounter++; ArrowBlipColour2 = CRGBA((uint8)(color >> 24), (uint8)(color >> 16), (uint8)(color >> 8), 255); } } } } } } } void CRadar::DrawCoordBlip(int32 blipId) { CVector2D out; CVector2D in; if (ms_RadarTrace[blipId].m_eBlipType != BLIP_CONTACT_POINT || !CTheScripts::IsPlayerOnAMission()) { uint32 color = GetRadarTraceColour(ms_RadarTrace[blipId].m_nColor, ms_RadarTrace[blipId].m_bDim); if (ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { if (CTheScripts::IsDebugOn()) { ShowRadarMarker(ms_RadarTrace[blipId].m_vecPos, color, ms_RadarTrace[blipId].m_Radius); ms_RadarTrace[blipId].m_Radius = ms_RadarTrace[blipId].m_Radius - 0.1f; if (ms_RadarTrace[blipId].m_Radius < 1.0f) ms_RadarTrace[blipId].m_Radius = 5.0f; } } if (ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[blipId].m_eBlipDisplay == BLIP_DISPLAY_BLIP_ONLY) { TransformRealWorldPointToRadarSpace(in, ms_RadarTrace[blipId].m_vec2DPos); float dist = LimitRadarPoint(in); TransformRadarPointToScreenSpace(out, in); if (!ms_RadarTrace[blipId].m_bShortRange || dist <= 1.0f || FrontEndMenuManager.m_bMenuMapActive) { if (ms_RadarTrace[blipId].m_eRadarSprite != RADAR_SPRITE_NONE) { DrawRadarSprite(ms_RadarTrace[blipId].m_eRadarSprite, out.x, out.y, CalculateBlipAlpha(dist)); } else { const CVector& pos = FindPlayerCentreOfWorld_NoSniperShift(); const CVector& blipPos = ms_RadarTrace[blipId].m_vecPos; uint8 mode = BLIP_MODE_TRIANGULAR_UP; if (blipPos.z - pos.z <= 2.0f) { if (blipPos.z - pos.z < -4.0f) mode = BLIP_MODE_TRIANGULAR_DOWN; else mode = BLIP_MODE_SQUARE; } ShowRadarTraceWithHeight(out.x, out.y, ms_RadarTrace[blipId].m_wScale, (uint8)(color >> 24), (uint8)(color >> 16), (uint8)(color >> 8), 255, mode); if (FrontEndMenuManager.m_bMenuMapActive) { bool alreadyThere = false; for (int i = 0; i < NUM_MAP_LEGENDS; i++) { if (MapLegendList[i] == -1) alreadyThere = true; } if (!alreadyThere) { MapLegendList[MapLegendCounter] = -1; MapLegendCounter++; ArrowBlipColour1 = CRGBA((uint8)(color >> 24), (uint8)(color >> 16), (uint8)(color >> 8), 255); } } } } } } } void CRadar::DrawLegend(int32 x, int32 y, int32 sprite) { if (sprite < 0) { static PauseModeTime lastChange = 0; static int8 blipMode = 0; CRGBA color; if (sprite == -1) { color = ArrowBlipColour1; } else { color = ArrowBlipColour2; } if (CTimer::GetTimeInMillisecondsPauseMode() - lastChange > 600) { lastChange = CTimer::GetTimeInMillisecondsPauseMode(); if ( blipMode == 2 ) blipMode = 0; else ++blipMode; } switch (blipMode) { case BLIP_MODE_TRIANGULAR_UP: CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(14.0f), y + SCREEN_SCALE_Y(13.0f), x + SCREEN_SCALE_X(2.0f), y + SCREEN_SCALE_Y(13.0f), x + SCREEN_SCALE_X(8.f), y + SCREEN_SCALE_Y(2.0f), x + SCREEN_SCALE_X(8.f), y + SCREEN_SCALE_Y(2.0f), CRGBA(0, 0, 0, 255)); CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(12.0f), y + SCREEN_SCALE_Y(12.0f), x + SCREEN_SCALE_X(4.0f), y + SCREEN_SCALE_Y(12.0f), x + SCREEN_SCALE_X(8.f), y + SCREEN_SCALE_Y(4.0f), x + SCREEN_SCALE_X(8.f), y + SCREEN_SCALE_Y(4.0f), color); break; case BLIP_MODE_TRIANGULAR_DOWN: CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(14.0f), x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(14.0f), x + SCREEN_SCALE_X(2.f), y + SCREEN_SCALE_Y(3.0f), x + SCREEN_SCALE_X(2.f), y + SCREEN_SCALE_Y(3.0f), CRGBA(0, 0, 0, 255)); CSprite2d::Draw2DPolygon(x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(12.0f), x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(12.0f), x + SCREEN_SCALE_X(12.f), y + SCREEN_SCALE_Y(4.0f), x + SCREEN_SCALE_X(4.f), y + SCREEN_SCALE_Y(4.0f), color); break; case BLIP_MODE_SQUARE: CSprite2d::DrawRect(CRect(x + SCREEN_SCALE_X(4.0f), y + SCREEN_SCALE_Y(3.0f), SCREEN_SCALE_X(12.0f) + x, SCREEN_SCALE_Y(12.0f) + y), CRGBA(0, 0, 0, 255)); CSprite2d::DrawRect(CRect(x + SCREEN_SCALE_X(5.0f), y + SCREEN_SCALE_Y(4.0f), SCREEN_SCALE_X(11.0f) + x, SCREEN_SCALE_Y(11.0f) + y), color); break; } } else { RadarSprites[sprite]->Draw(CRect(x, y, x + SCREEN_SCALE_X(16.f), y + SCREEN_SCALE_X(16.f)), CRGBA(255, 255, 255, 255)); } wchar *text; switch ( sprite ) { case RADAR_SPRITE_ENTITY_BLIP: text = TheText.Get("LG_38"); break; case RADAR_SPRITE_COORD_BLIP: text = TheText.Get("LG_35"); break; case RADAR_SPRITE_MAP_HERE: text = TheText.Get("LG_01"); break; case RADAR_SPRITE_AVERY: text = TheText.Get("LG_02"); break; case RADAR_SPRITE_BIKER: text = TheText.Get("LG_03"); break; case RADAR_SPRITE_CORTEZ: text = TheText.Get("LG_04"); break; case RADAR_SPRITE_DIAZ: text = TheText.Get("LG_05"); break; case RADAR_SPRITE_KENT: text = TheText.Get("LG_06"); break; case RADAR_SPRITE_LAWYER: text = TheText.Get("LG_07"); break; case RADAR_SPRITE_PHIL: text = TheText.Get("LG_08"); break; case RADAR_SPRITE_BIKERS: text = TheText.Get("LG_03"); break; case RADAR_SPRITE_BOATYARD: text = TheText.Get("LG_09"); break; case RADAR_SPRITE_MALIBU_CLUB: text = TheText.Get("LG_10"); break; case RADAR_SPRITE_CUBANS: text = TheText.Get("LG_11"); break; case RADAR_SPRITE_FILM: text = TheText.Get("LG_12"); break; case RADAR_SPRITE_GUN: text = TheText.Get("LG_13"); break; case RADAR_SPRITE_HAITIANS: text = TheText.Get("LG_14"); break; case RADAR_SPRITE_HARDWARE: text = TheText.Get("LG_15"); break; case RADAR_SPRITE_SAVE: text = TheText.Get("LG_16"); break; case RADAR_SPRITE_STRIP: text = TheText.Get("LG_37"); break; case RADAR_SPRITE_ICE: text = TheText.Get("LG_17"); break; case RADAR_SPRITE_KCABS: text = TheText.Get("LG_18"); break; case RADAR_SPRITE_LOVEFIST: text = TheText.Get("LG_19"); break; case RADAR_SPRITE_PRINTWORKS: text = TheText.Get("LG_20"); break; case RADAR_SPRITE_PROPERTY: text = TheText.Get("LG_21"); break; case RADAR_SPRITE_SUNYARD: text = TheText.Get("LG_36"); break; case RADAR_SPRITE_SPRAY: text = TheText.Get("LG_22"); break; case RADAR_SPRITE_TSHIRT: text = TheText.Get("LG_23"); break; case RADAR_SPRITE_TOMMY: text = TheText.Get("LG_24"); break; case RADAR_SPRITE_PHONE: text = TheText.Get("LG_25"); break; case RADAR_SPRITE_RADIO_WILDSTYLE: text = TheText.Get("LG_26"); break; case RADAR_SPRITE_RADIO_FLASH: text = TheText.Get("LG_27"); break; case RADAR_SPRITE_RADIO_KCHAT: text = TheText.Get("LG_28"); break; case RADAR_SPRITE_RADIO_FEVER: text = TheText.Get("LG_29"); break; case RADAR_SPRITE_RADIO_VROCK: text = TheText.Get("LG_30"); break; case RADAR_SPRITE_RADIO_VCPR: text = TheText.Get("LG_31"); break; case RADAR_SPRITE_RADIO_ESPANTOSO: text = TheText.Get("LG_32"); break; case RADAR_SPRITE_RADIO_EMOTION: text = TheText.Get("LG_33"); break; case RADAR_SPRITE_RADIO_WAVE: text = TheText.Get("LG_34"); break; default: break; } CFont::PrintString(SCREEN_SCALE_X(20.f) + x, SCREEN_SCALE_Y(3.0f) + y, text); } ================================================ FILE: src/core/Radar.h ================================================ #pragma once #include "Sprite2d.h" #define CARBLIP_MARKER_COLOR_R 252 #define CARBLIP_MARKER_COLOR_G 138 #define CARBLIP_MARKER_COLOR_B 242 #define CARBLIP_MARKER_COLOR_A 255 #define CHARBLIP_MARKER_COLOR_R 252 #define CHARBLIP_MARKER_COLOR_G 138 #define CHARBLIP_MARKER_COLOR_B 242 #define CHARBLIP_MARKER_COLOR_A 255 #define OBJECTBLIP_MARKER_COLOR_R 252 #define OBJECTBLIP_MARKER_COLOR_G 138 #define OBJECTBLIP_MARKER_COLOR_B 242 #define OBJECTBLIP_MARKER_COLOR_A 255 #define COORDBLIP_MARKER_COLOR_R 252 #define COORDBLIP_MARKER_COLOR_G 138 #define COORDBLIP_MARKER_COLOR_B 242 #define COORDBLIP_MARKER_COLOR_A 228 #define NUM_MAP_LEGENDS 75 #define MENU_MAP_LENGTH_UNIT 1190.0f // in game unit #define MENU_MAP_WIDTH_SCALE 1.112f // in game unit (originally 1.112494151260504f) #define MENU_MAP_HEIGHT_SCALE 1.119f // in game unit (originally 1.118714268907563f) #define MENU_MAP_TOP_OFFSET 0.28f // in length unit defined above - ~333 game unit #define MENU_MAP_LEFT_OFFSET 0.185f // in length unit defined above - ~220 game unit #define MENU_MAP_LENGTH (4000.f / MENU_MAP_LENGTH_UNIT) enum eBlipType { BLIP_NONE, BLIP_CAR, BLIP_CHAR, BLIP_OBJECT, BLIP_COORD, BLIP_CONTACT_POINT }; enum eBlipDisplay { BLIP_DISPLAY_NEITHER = 0, BLIP_DISPLAY_MARKER_ONLY = 1, BLIP_DISPLAY_BLIP_ONLY = 2, BLIP_DISPLAY_BOTH = 3, }; enum eRadarSprite { RADAR_SPRITE_ENTITY_BLIP = -2, RADAR_SPRITE_COORD_BLIP = -1, RADAR_SPRITE_NONE = 0, RADAR_SPRITE_CENTRE, RADAR_SPRITE_MAP_HERE, RADAR_SPRITE_NORTH, RADAR_SPRITE_AVERY, RADAR_SPRITE_BIKER, RADAR_SPRITE_CORTEZ, RADAR_SPRITE_DIAZ, RADAR_SPRITE_KENT, RADAR_SPRITE_LAWYER, RADAR_SPRITE_PHIL, RADAR_SPRITE_BIKERS, RADAR_SPRITE_BOATYARD, RADAR_SPRITE_MALIBU_CLUB, RADAR_SPRITE_CUBANS, RADAR_SPRITE_FILM, RADAR_SPRITE_GUN, RADAR_SPRITE_HAITIANS, RADAR_SPRITE_HARDWARE, RADAR_SPRITE_SAVE, RADAR_SPRITE_STRIP, RADAR_SPRITE_ICE, RADAR_SPRITE_KCABS, RADAR_SPRITE_LOVEFIST, RADAR_SPRITE_PRINTWORKS, RADAR_SPRITE_PROPERTY, RADAR_SPRITE_SUNYARD, RADAR_SPRITE_SPRAY, RADAR_SPRITE_TSHIRT, RADAR_SPRITE_TOMMY, RADAR_SPRITE_PHONE, RADAR_SPRITE_RADIO_WILDSTYLE, RADAR_SPRITE_RADIO_FLASH, RADAR_SPRITE_RADIO_KCHAT, RADAR_SPRITE_RADIO_FEVER, RADAR_SPRITE_RADIO_VROCK, RADAR_SPRITE_RADIO_VCPR, RADAR_SPRITE_RADIO_ESPANTOSO, RADAR_SPRITE_RADIO_EMOTION, RADAR_SPRITE_RADIO_WAVE, RADAR_SPRITE_COUNT }; enum { RADAR_TRACE_RED, RADAR_TRACE_GREEN, RADAR_TRACE_LIGHT_BLUE, RADAR_TRACE_GRAY, RADAR_TRACE_YELLOW, RADAR_TRACE_MAGENTA, RADAR_TRACE_CYAN }; enum { BLIP_MODE_TRIANGULAR_UP = 0, BLIP_MODE_TRIANGULAR_DOWN, BLIP_MODE_SQUARE, }; struct sRadarTrace { uint32 m_nColor; uint32 m_eBlipType; // eBlipType int32 m_nEntityHandle; CVector m_vec2DPos; CVector m_vecPos; uint16 m_BlipIndex; bool m_bDim; bool m_bInUse; bool m_bShortRange; bool m_unused; float m_Radius; int16 m_wScale; uint16 m_eBlipDisplay; // eBlipDisplay uint16 m_eRadarSprite; // eRadarSprite }; // Either that was a thing while saving/loading blips, or they added sizes of each field one by one. I want to do the former. #pragma pack(push,1) struct sRadarTraceSave { uint32 m_nColor; float m_Radius; uint32 m_eBlipType; // eBlipType int32 m_nEntityHandle; CVector2D m_vec2DPos; CVector m_vecPos; uint16 m_BlipIndex; bool m_bDim; bool m_bInUse; bool m_bShortRange; bool m_unused; int16 m_wScale; uint16 m_eBlipDisplay; // eBlipDisplay uint16 m_eRadarSprite; // eRadarSprite }; #pragma pack(pop) // Values for screen space #define RADAR_LEFT (40.0f) #define RADAR_BOTTOM (40.0f) #ifdef FIX_RADAR /* The values are from an early screenshot taken before R* broke radar #define RADAR_WIDTH (82.0f) #define RADAR_HEIGHT (82.0f) */ #define RADAR_WIDTH ((CDraw::ms_bFixRadar) ? (82.0f) : (94.0f)) #define RADAR_HEIGHT ((CDraw::ms_bFixRadar) ? (82.0f) : (76.0f)) #else /* broken since forever, someone tried to fix size for 640x512(PAL) http://aap.rockstarvision.com/pics/gta3/ps2screens/gta3_interface.jpg but failed: http://aap.rockstarvision.com/pics/gta3/artwork/gta3_artwork_16.jpg most likely the guy used something like this: int y = 82 * (640.0/512.0)/(640.0/480.0); int x = y * (640.0/512.0); */ #define RADAR_WIDTH (94.0f) #define RADAR_HEIGHT (76.0f) #endif class CRadar { public: static float m_radarRange; static sRadarTrace ms_RadarTrace[NUMRADARBLIPS]; static CSprite2d CentreSprite; static CSprite2d MapHereSprite; static CSprite2d NorthSprite; static CSprite2d AverySprite; static CSprite2d BikerSprite; static CSprite2d CortezSprite; static CSprite2d DiazSprite; static CSprite2d KentSprite; static CSprite2d LawyerSprite; static CSprite2d PhilSprite; static CSprite2d BikersSprite; static CSprite2d BoatyardSprite; static CSprite2d MalibuClubSprite; static CSprite2d CubansSprite; static CSprite2d FilmSprite; static CSprite2d GunSprite; static CSprite2d HaitiansSprite; static CSprite2d HardwareSprite; static CSprite2d SaveHouseSprite; static CSprite2d StripSprite; static CSprite2d IceSprite; static CSprite2d KCabsSprite; static CSprite2d LovefistSprite; static CSprite2d PrintworksSprite; static CSprite2d PropertySprite; static CSprite2d SunYardSprite; static CSprite2d SpraySprite; static CSprite2d TShirtSprite; static CSprite2d TommySprite; static CSprite2d PhoneSprite; static CSprite2d RadioWildstyleSprite; static CSprite2d RadioFlashSprite; static CSprite2d RadioKChatSprite; static CSprite2d RadioFeverSprite; static CSprite2d RadioVRockSprite; static CSprite2d RadioVCPRSprite; static CSprite2d RadioEspantosoSprite; static CSprite2d RadioEmotionSprite; static CSprite2d RadioWaveSprite; static CSprite2d *RadarSprites[RADAR_SPRITE_COUNT]; static float cachedCos; static float cachedSin; static CRGBA ArrowBlipColour1; static CRGBA ArrowBlipColour2; static int16 MapLegendList[NUM_MAP_LEGENDS]; static int16 MapLegendCounter; #ifdef MAP_ENHANCEMENTS static int TargetMarkerId; static CVector TargetMarkerPos; #endif static void InitFrontEndMap(); static void DrawYouAreHereSprite(float, float); #ifdef MAP_ENHANCEMENTS static void ToggleTargetMarker(float, float); #endif static uint8 CalculateBlipAlpha(float dist); static void ChangeBlipBrightness(int32 i, int32 bright); static void ChangeBlipColour(int32 i, int32); static void ChangeBlipDisplay(int32 i, eBlipDisplay display); static void ChangeBlipScale(int32 i, int32 scale); static void ClearBlip(int32 i); static void ClearBlipForEntity(eBlipType type, int32 id); static int ClipRadarPoly(CVector2D *out, const CVector2D *in); static bool DisplayThisBlip(int32 i); static void Draw3dMarkers(); static void DrawBlips(); static void DrawMap(); static void DrawRadarMap(); static void DrawRadarMask(); static void DrawRadarSection(int32 x, int32 y); static void DrawRadarSprite(uint16 sprite, float x, float y, uint8 alpha); static void DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int32 alpha); static int32 GetActualBlipArrayIndex(int32 i); static int32 GetNewUniqueBlipIndex(int32 i); static uint32 GetRadarTraceColour(uint32 color, bool bright); static void Initialise(); static float LimitRadarPoint(CVector2D &point); static void LoadAllRadarBlips(uint8 *buf, uint32 size); static void LoadTextures(); static void RemoveRadarSections(); static void SaveAllRadarBlips(uint8*, uint32*); static void SetBlipSprite(int32 i, int32 icon); static int32 SetCoordBlip(eBlipType type, CVector pos, uint32, eBlipDisplay); static int32 SetEntityBlip(eBlipType type, int32, uint32, eBlipDisplay); static int32 SetShortRangeCoordBlip(eBlipType type, CVector pos, uint32, eBlipDisplay); static void SetRadarMarkerState(int32 i, bool flag); static void ShowRadarMarker(CVector pos, uint32 color, float radius); static void ShowRadarTrace(float x, float y, uint32 size, uint8 red, uint8 green, uint8 blue, uint8 alpha); static void ShowRadarTraceWithHeight(float x, float y, uint32 size, uint8 red, uint8 green, uint8 blue, uint8 alpha, uint8 mode); static void Shutdown(); static void StreamRadarSections(const CVector &posn); static void StreamRadarSections(int32 x, int32 y); static void TransformRealWorldToTexCoordSpace(CVector2D &out, const CVector2D &in, int32 x, int32 y); static void TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in); static void TransformRadarPointToScreenSpace(CVector2D &out, const CVector2D &in); static void TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D &in); static void CalculateCachedSinCos(); static void DrawEntityBlip(int32 blipId); static void DrawCoordBlip(int32 blipId); static void DrawLegend(int32, int32, int32); }; ================================================ FILE: src/core/Range2D.cpp ================================================ #include "common.h" #include "Range2D.h" #include "General.h" CRange2D::CRange2D(CVector2D _min, CVector2D _max) : min(_min), max(_max) {} bool CRange2D::IsInRange(CVector2D vec) { return min.x < vec.x && max.x > vec.x && min.y < vec.y && max.y > vec.y; } void CRange2D::DebugShowRange(float, int) { } CVector2D CRange2D::GetRandomPointInRange() { int distX = Abs(max.x - min.x); int distY = Abs(max.y - min.y); float outX = CGeneral::GetRandomNumber() % distX + min.x; float outY = CGeneral::GetRandomNumber() % distY + min.y; return CVector2D(outX, outY); } ================================================ FILE: src/core/Range2D.h ================================================ #pragma once class CRange2D { CVector2D min, max; public: CRange2D(CVector2D _min, CVector2D _max); bool IsInRange(CVector2D vec); void DebugShowRange(float, int); CVector2D GetRandomPointInRange(); }; ================================================ FILE: src/core/Range3D.cpp ================================================ #include "common.h" #include "Range3D.h" #include "General.h" CRange3D::CRange3D(CVector _min, CVector _max) : min(_min), max(_max) {} bool CRange3D::IsInRange(CVector vec) { return min.x < vec.x && max.x > vec.x && min.y < vec.y && max.y > vec.y && min.z < vec.z && max.z > vec.z; } void CRange3D::DebugShowRange(float, int) { } CVector CRange3D::GetRandomPointInRange() { int distX = Abs(max.x - min.x); int distY = Abs(max.y - min.y); int distZ = Abs(max.z - min.z); float outX = CGeneral::GetRandomNumber() % distX + min.x; float outY = CGeneral::GetRandomNumber() % distY + min.y; float outZ = CGeneral::GetRandomNumber() % distZ + min.z; return CVector(outX, outY, outZ); } ================================================ FILE: src/core/Range3D.h ================================================ #pragma once class CRange3D { CVector min, max; public: CRange3D(CVector _min, CVector _max); bool IsInRange(CVector vec); void DebugShowRange(float, int); CVector GetRandomPointInRange(); }; ================================================ FILE: src/core/References.cpp ================================================ #include "common.h" #include "World.h" #include "Vehicle.h" #include "PlayerPed.h" #include "Pools.h" #include "References.h" CReference CReferences::aRefs[NUMREFERENCES]; CReference *CReferences::pEmptyList; void CReferences::Init(void) { int i; pEmptyList = &aRefs[0]; for(i = 0; i < NUMREFERENCES; i++){ aRefs[i].pentity = nil; aRefs[i].next = &aRefs[i+1]; } aRefs[NUMREFERENCES-1].next = nil; } void CEntity::RegisterReference(CEntity **pent) { if(IsBuilding()) return; CReference *ref; // check if already registered for(ref = m_pFirstReference; ref; ref = ref->next) if(ref->pentity == pent) return; // have to allocate new reference ref = CReferences::pEmptyList; if(ref){ CReferences::pEmptyList = ref->next; ref->pentity = pent; ref->next = m_pFirstReference; m_pFirstReference = ref; return; } return; } // Clean up the reference from *pent -> 'this' void CEntity::CleanUpOldReference(CEntity **pent) { CReference* ref, ** lastnextp; lastnextp = &m_pFirstReference; for (ref = m_pFirstReference; ref; ref = ref->next) { if (ref->pentity == pent) { *lastnextp = ref->next; ref->next = CReferences::pEmptyList; CReferences::pEmptyList = ref; break; } lastnextp = &ref->next; } } // Clear all references to this entity void CEntity::ResolveReferences(void) { CReference *ref; // clear pointers to this entity for(ref = m_pFirstReference; ref; ref = ref->next) if(*ref->pentity == this) *ref->pentity = nil; // free list if(m_pFirstReference){ for(ref = m_pFirstReference; ref->next; ref = ref->next) ; ref->next = CReferences::pEmptyList; CReferences::pEmptyList = m_pFirstReference; m_pFirstReference = nil; } } // Free all references that no longer point to this entity void CEntity::PruneReferences(void) { CReference *ref, *next, **lastnextp; lastnextp = &m_pFirstReference; for(ref = m_pFirstReference; ref; ref = next){ next = ref->next; if(*ref->pentity == this) lastnextp = &ref->next; else{ *lastnextp = ref->next; ref->next = CReferences::pEmptyList; CReferences::pEmptyList = ref; } } } void CReferences::RemoveReferencesToPlayer(void) { if(FindPlayerVehicle()) FindPlayerVehicle()->ResolveReferences(); #ifdef FIX_BUGS if (FindPlayerPed()) { CPlayerPed* pPlayerPed = FindPlayerPed(); FindPlayerPed()->ResolveReferences(); CWorld::Players[CWorld::PlayerInFocus].m_pPed = pPlayerPed; pPlayerPed->RegisterReference((CEntity**)&CWorld::Players[CWorld::PlayerInFocus].m_pPed); } #else if(FindPlayerPed()) FindPlayerPed()->ResolveReferences(); #endif } void CReferences::PruneAllReferencesInWorld(void) { int i; CEntity *e; i = CPools::GetPedPool()->GetSize(); while(--i >= 0){ e = CPools::GetPedPool()->GetSlot(i); if(e) e->PruneReferences(); } i = CPools::GetVehiclePool()->GetSize(); while(--i >= 0){ e = CPools::GetVehiclePool()->GetSlot(i); if(e) e->PruneReferences(); } i = CPools::GetObjectPool()->GetSize(); while(--i >= 0){ e = CPools::GetObjectPool()->GetSlot(i); if(e) e->PruneReferences(); } } ================================================ FILE: src/core/References.h ================================================ #pragma once class CEntity; struct CReference { CReference *next; CEntity **pentity; }; class CReferences { public: static CReference aRefs[NUMREFERENCES]; static CReference *pEmptyList; static void Init(void); static void RemoveReferencesToPlayer(void); static void PruneAllReferencesInWorld(void); }; ================================================ FILE: src/core/Ropes.cpp ================================================ #include "common.h" #include "main.h" #include "Timer.h" #include "ModelIndices.h" #include "Streaming.h" #include "CopPed.h" #include "Population.h" #include "RenderBuffer.h" #include "Camera.h" #include "Ropes.h" CRope CRopes::aRopes[8]; RwImVertexIndex RopeIndices[64] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32 // unused }; void CRope::Update(void) { int i; float step = Pow(0.85f, CTimer::GetTimeStep()); if(!m_bWasRegistered && CTimer::GetTimeInMilliseconds() > m_updateTimer){ m_speed[0].z -= 0.0015f*CTimer::GetTimeStep(); m_pos[0] += m_speed[0]*CTimer::GetTimeStep(); } for(i = 1; i < ARRAY_SIZE(m_pos); i++){ CVector prevPos = m_pos[i]; m_pos[i] += m_speed[i]*step*CTimer::GetTimeStep(); m_pos[i].z -= 0.05f*CTimer::GetTimeStep(); CVector dist = m_pos[i] - m_pos[i-1]; m_pos[i] = m_pos[i-1] + (0.625f/dist.Magnitude())*dist; m_speed[i] = (m_pos[i] - prevPos)/CTimer::GetTimeStep(); } if(!m_bWasRegistered && m_pos[0].z < 0.0f) m_bActive = false; m_bWasRegistered = false; } void CRope::Render(void) { int i; int numVerts = 0; if(!TheCamera.IsSphereVisible(m_pos[16], 20.0f)) return; for(i = 0; i < ARRAY_SIZE(m_pos); i++){ RwIm3DVertexSetRGBA(&TempBufferRenderVertices[i], 128, 128, 128, 100); RwIm3DVertexSetPos(&TempBufferRenderVertices[i], m_pos[i].x, m_pos[i].y, m_pos[i].z); } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); if(RwIm3DTransform(TempBufferRenderVertices, ARRAY_SIZE(m_pos), nil, rwIM3D_VERTEXXYZ|rwIM3D_VERTEXRGBA)){ #ifdef FIX_BUGS RwIm3DRenderIndexedPrimitive(rwPRIMTYPELINELIST, RopeIndices, 2*(ARRAY_SIZE(m_pos)-1)); #else RwIm3DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, RopeIndices, 2*(ARRAY_SIZE(m_pos)-1)); #endif RwIm3DEnd(); } } void CRopes::Init(void) { int i; for(i = 0; i < ARRAY_SIZE(aRopes); i++) aRopes[i].m_bActive = false; } void CRopes::Update(void) { int i; for(i = 0; i < ARRAY_SIZE(aRopes); i++) if(aRopes[i].m_bActive) aRopes[i].Update(); } void CRopes::Render(void) { int i; PUSH_RENDERGROUP("CRopes::Render"); for(i = 0; i < ARRAY_SIZE(aRopes); i++) if(aRopes[i].m_bActive) aRopes[i].Render(); POP_RENDERGROUP(); } bool CRopes::RegisterRope(uintptr id, CVector pos, bool setUpdateTimer) { int i, j; for(i = 0; i < ARRAY_SIZE(aRopes); i++){ if(aRopes[i].m_bActive && aRopes[i].m_id == id){ aRopes[i].m_pos[0] = pos; aRopes[i].m_speed[0] = CVector(0.0f, 0.0f, 0.0f); aRopes[i].m_bWasRegistered = true; return true; } } for(i = 0; i < ARRAY_SIZE(aRopes); i++) if(!aRopes[i].m_bActive){ aRopes[i].m_id = id; aRopes[i].m_pos[0] = pos; aRopes[i].m_speed[0] = CVector(0.0f, 0.0f, 0.0f); aRopes[i].m_unk = false; aRopes[i].m_bWasRegistered = true; aRopes[i].m_updateTimer = setUpdateTimer ? CTimer::GetTimeInMilliseconds() + 20000 : 0; for(j = 1; j < ARRAY_SIZE(aRopes[0].m_pos); j++){ if(j & 1) aRopes[i].m_pos[j] = aRopes[i].m_pos[j-1] + CVector(0.0f, 0.0f, 0.625f); else aRopes[i].m_pos[j] = aRopes[i].m_pos[j-1] - CVector(0.0f, 0.0f, 0.625f); aRopes[i].m_speed[j] = CVector(0.0f, 0.0f, 0.0f); } aRopes[i].m_bActive = true; return true; } return false; } void CRopes::SetSpeedOfTopNode(uintptr id, CVector speed) { int i; for(i = 0; i < ARRAY_SIZE(aRopes); i++) if(aRopes[i].m_bActive && aRopes[i].m_id == id){ aRopes[i].m_speed[0] = speed; return; } } bool CRopes::FindCoorsAlongRope(uintptr id, float t, CVector *coors) { int i, j; float f; for(i = 0; i < ARRAY_SIZE(aRopes); i++) if(aRopes[i].m_bActive && aRopes[i].m_id == id){ t = (ARRAY_SIZE(aRopes[0].m_pos)-1)*clamp(t, 0.0f, 0.999f); j = t; f = t - j; *coors = (1.0f-f)*aRopes[i].m_pos[j] + f*aRopes[i].m_pos[j+1]; return true; } return false; } bool CRopes::CreateRopeWithSwatComingDown(CVector pos) { static uint32 ropeId = 0; if(!CStreaming::HasModelLoaded(MI_SWAT) || !RegisterRope(ropeId+100, pos, true)) return false; CCopPed *swat = (CCopPed*)CPopulation::AddPed(PEDTYPE_COP, COP_HELI_SWAT, pos); swat->bUsesCollision = false; swat->m_pRopeEntity = (CEntity*)1; swat->m_nRopeID = 100 + ropeId; CAnimManager::BlendAnimation(swat->GetClump(), ASSOCGRP_STD, ANIM_STD_ABSEIL, 4.0f); ropeId++; return true; } ================================================ FILE: src/core/Ropes.h ================================================ #pragma once class CRope { public: bool m_bActive; bool m_bWasRegistered; bool m_unk; uintptr m_id; uint32 m_updateTimer; CVector m_pos[32]; CVector m_speed[32]; void Update(void); void Render(void); }; class CRopes { static CRope aRopes[8]; public: static void Init(void); static void Update(void); static void Render(void); static bool RegisterRope(uintptr id, CVector pos, bool setUpdateTimer); static void SetSpeedOfTopNode(uintptr id, CVector speed); static bool FindCoorsAlongRope(uintptr id, float t, CVector *coors); static bool CreateRopeWithSwatComingDown(CVector pos); }; ================================================ FILE: src/core/Stats.cpp ================================================ #include "common.h" #include "Stats.h" #include "Text.h" #include "World.h" #include "Pad.h" #include "DMAudio.h" #include "main.h" #include "Font.h" #include "Frontend.h" #include "audio_enums.h" #include #ifdef USE_PRECISE_MEASUREMENT_CONVERTION #define MILES_IN_METER 0.000621371192f #define FEET_IN_METER 3.28084f #else #define MILES_IN_METER (1 / 1670.f) #define FEET_IN_METER 3.33f #endif int32 CStats::SeagullsKilled; int32 CStats::BoatsExploded; int32 CStats::WantedStarsAttained; int32 CStats::WantedStarsEvaded; int32 CStats::DaysPassed; int32 CStats::HeadsPopped; int32 CStats::CommercialPassed; int32 CStats::IndustrialPassed; int32 CStats::SuburbanPassed; int32 CStats::NumberKillFrenziesPassed; int32 CStats::PeopleKilledByOthers; int32 CStats::HelisDestroyed; int32 CStats::PedsKilledOfThisType[NUM_PEDTYPES]; int32 CStats::TimesDied; int32 CStats::TimesArrested; int32 CStats::KillsSinceLastCheckpoint; float CStats::DistanceTravelledByCar; float CStats::DistanceTravelledByHelicoptor; float CStats::DistanceTravelledByBike; float CStats::DistanceTravelledByBoat; float CStats::DistanceTravelledByPlane; float CStats::DistanceTravelledByGolfCart; float CStats::DistanceTravelledOnFoot; int32 CStats::FlightTime; int32 CStats::TimesDrowned; int32 CStats::PhotosTaken; float CStats::LoanSharks; float CStats::StoresKnockedOff; float CStats::MovieStunts; float CStats::Assassinations; float CStats::PizzasDelivered; float CStats::GarbagePickups; float CStats::IceCreamSold; float CStats::TopShootingRangeScore; float CStats::ShootingRank; float CStats::ProgressMade; float CStats::TotalProgressInGame; int32 CStats::CarsExploded; int32 CStats::PeopleKilledByPlayer; float CStats::MaximumJumpDistance; float CStats::MaximumJumpHeight; int32 CStats::MaximumJumpFlips; int32 CStats::MaximumJumpSpins; int32 CStats::BestStuntJump; int32 CStats::NumberOfUniqueJumpsFound; int32 CStats::TotalNumberOfUniqueJumps; int32 CStats::PassengersDroppedOffWithTaxi; int32 CStats::MoneyMadeWithTaxi; int32 CStats::MissionsGiven; int32 CStats::MissionsPassed; char CStats::LastMissionPassedName[8]; int32 CStats::TotalLegitimateKills; int32 CStats::LivesSavedWithAmbulance; int32 CStats::CriminalsCaught; int32 CStats::HighestLevelAmbulanceMission; int32 CStats::HighestLevelVigilanteMission; int32 CStats::HighestLevelFireMission; int32 CStats::FiresExtinguished; int32 CStats::TotalNumberKillFrenzies; int32 CStats::TotalNumberMissions; int32 CStats::RoundsFiredByPlayer; int32 CStats::KgsOfExplosivesUsed; int32 CStats::BulletsThatHit; int32 CStats::BestTimeBombDefusal; int32 CStats::FastestTimes[CStats::TOTAL_FASTEST_TIMES]; int32 CStats::HighestScores[CStats::TOTAL_HIGHEST_SCORES]; int32 CStats::BestPositions[CStats::TOTAL_BEST_POSITIONS]; bool CStats::PropertyOwned[CStats::TOTAL_PROPERTIES]; int32 CStats::NumPropertyOwned; int32 CStats::PropertyDestroyed; float CStats::HighestChaseValue; int32 CStats::CheatedCount; int32 CStats::ShowChaseStatOnScreen; int32 CStats::PamphletMissionPassed; bool CStats::abSonyCDs[1]; int32 CStats::BloodRingKills; int32 CStats::BloodRingTime; float CStats::FavoriteRadioStationList[NUM_RADIOS]; int32 CStats::Sprayings; float CStats::AutoPaintingBudget; int32 CStats::NoMoreHurricanes; float CStats::FashionBudget; float CStats::PropertyBudget; float CStats::WeaponBudget; int32 CStats::SafeHouseVisits; int32 CStats::TyresPopped; int32 CStats::LongestWheelie; int32 CStats::LongestStoppie; int32 CStats::Longest2Wheel; float CStats::LongestWheelieDist; float CStats::LongestStoppieDist; float CStats::Longest2WheelDist; void CStats::Init() { PeopleKilledByOthers = 0; PeopleKilledByPlayer = 0; CarsExploded = 0; BoatsExploded = 0; RoundsFiredByPlayer = 0; for (int i = 0; i < NUM_PEDTYPES; i++) PedsKilledOfThisType[i] = 0; HelisDestroyed = 0; ProgressMade = 0.0f; KgsOfExplosivesUsed = 0; BulletsThatHit = 0; TyresPopped = 0; HeadsPopped = 0; WantedStarsAttained = 0; WantedStarsEvaded = 0; TimesArrested = 0; TimesDied = 0; DaysPassed = 0; SafeHouseVisits = 0; Sprayings = 0; MaximumJumpDistance = 0; MaximumJumpHeight = 0; MaximumJumpFlips = 0; MaximumJumpSpins = 0; BestStuntJump = 0; NumberOfUniqueJumpsFound = 0; TotalNumberOfUniqueJumps = 0; MissionsGiven = 0; MissionsPassed = 0; PassengersDroppedOffWithTaxi = 0; MoneyMadeWithTaxi = 0; DistanceTravelledOnFoot = 0; DistanceTravelledByCar = 0; DistanceTravelledByBike = 0; DistanceTravelledByBoat = 0; DistanceTravelledByGolfCart = 0; DistanceTravelledByHelicoptor = 0; #ifdef FIX_BUGS DistanceTravelledByPlane = 0; #endif LivesSavedWithAmbulance = 0; CriminalsCaught = 0; HighestLevelVigilanteMission = 0; HighestLevelAmbulanceMission = 0; HighestLevelFireMission = 0; FiresExtinguished = 0; PhotosTaken = 0; NumberKillFrenziesPassed = 0; TotalNumberKillFrenzies = 0; TotalNumberMissions = 0; FlightTime = 0; TimesDrowned = 0; SeagullsKilled = 0; WeaponBudget = 0.0f; FashionBudget = 0.0f; LoanSharks = 0.0f; StoresKnockedOff = 0.0f; MovieStunts = 0.0f; Assassinations = 0.0f; PizzasDelivered = 0.0f; GarbagePickups = 0.0f; IceCreamSold = 0.0f; TopShootingRangeScore = 0.0f; ShootingRank = 0.0f; LongestWheelie = 0; LongestStoppie = 0; Longest2Wheel = 0; LongestWheelieDist = 0.0f; LongestStoppieDist = 0.0f; Longest2WheelDist = 0.0f; PropertyBudget = 0.0f; AutoPaintingBudget = 0.0f; PropertyDestroyed = 0; HighestChaseValue = 0.0f; CheatedCount = 0; for (int i = 0; i < TOTAL_FASTEST_TIMES; i++) FastestTimes[i] = 0; for (int i = 0; i < TOTAL_HIGHEST_SCORES; i++) HighestScores[i] = 0; for (int i = 0; i < TOTAL_BEST_POSITIONS; i++) BestPositions[i] = INT_MAX; KillsSinceLastCheckpoint = 0; TotalLegitimateKills = 0; for (int i = 0; i < ARRAY_SIZE(LastMissionPassedName); i++) LastMissionPassedName[i] = 0; IndustrialPassed = 0; CommercialPassed = 0; SuburbanPassed = 0; PamphletMissionPassed = 0; NoMoreHurricanes = 0; ShowChaseStatOnScreen = 0; for (int i = 0; i < ARRAY_SIZE(abSonyCDs); i++) abSonyCDs[i] = 0; PopulateFavoriteRadioStationList(); NumPropertyOwned = 0; for (int i = 0; i < TOTAL_PROPERTIES; i++) PropertyOwned[i] = false; BloodRingKills = 0; BloodRingTime = 0; } void CStats::RegisterFastestTime(int32 index, int32 time) { assert(index >= 0 && index < TOTAL_FASTEST_TIMES); if (FastestTimes[index] == 0) FastestTimes[index] = time; else FastestTimes[index] = Min(FastestTimes[index], time); } void CStats::RegisterHighestScore(int32 index, int32 score) { assert(index >= 0 && index < TOTAL_HIGHEST_SCORES); HighestScores[index] = Max(HighestScores[index], score); } void CStats::RegisterBestPosition(int32 index, int32 position) { assert(index >= 0 && index < TOTAL_BEST_POSITIONS); BestPositions[index] = Min(BestPositions[index], position); } void CStats::AnotherLifeSavedWithAmbulance() { ++LivesSavedWithAmbulance; } void CStats::AnotherCriminalCaught() { ++CriminalsCaught; } void CStats::RegisterLevelAmbulanceMission(int32 level) { HighestLevelAmbulanceMission = Max(HighestLevelAmbulanceMission, level); } void CStats::RegisterLevelVigilanteMission(int32 level) { HighestLevelVigilanteMission = Max(HighestLevelVigilanteMission, level); } void CStats::RegisterLevelFireMission(int32 level) { HighestLevelFireMission = Max(HighestLevelFireMission, level); } void CStats::AnotherFireExtinguished() { ++FiresExtinguished; } void CStats::AnotherKillFrenzyPassed() { ++NumberKillFrenziesPassed; } void CStats::SetTotalNumberKillFrenzies(int32 total) { TotalNumberKillFrenzies = total; } void CStats::SetTotalNumberMissions(int32 total) { TotalNumberMissions = total; } wchar *CStats::FindCriminalRatingString() { int rating = FindCriminalRatingNumber(); if (rating < 0) { if (rating > -500) return TheText.Get("RATNG53"); if (rating > -2000) return TheText.Get("RATNG54"); if (rating > -4000) return TheText.Get("RATNG55"); if (rating > -6000) return TheText.Get("RATNG56"); return TheText.Get("RATNG57"); } if (rating < 20) return TheText.Get("RATNG1"); if (rating < 50) return TheText.Get("RATNG2"); if (rating < 75) return TheText.Get("RATNG3"); if (rating < 100) return TheText.Get("RATNG4"); if (rating < 120) return TheText.Get("RATNG5"); if (rating < 150) return TheText.Get("RATNG6"); if (rating < 200) return TheText.Get("RATNG7"); if (rating < 240) return TheText.Get("RATNG8"); if (rating < 270) return TheText.Get("RATNG9"); if (rating < 300) return TheText.Get("RATNG10"); if (rating < 335) return TheText.Get("RATNG11"); if (rating < 370) return TheText.Get("RATNG12"); if (rating < 400) return TheText.Get("RATNG13"); if (rating < 450) return TheText.Get("RATNG14"); if (rating < 500) return TheText.Get("RATNG15"); if (rating < 550) return TheText.Get("RATNG16"); if (rating < 600) return TheText.Get("RATNG17"); if (rating < 610) return TheText.Get("RATNG18"); if (rating < 650) return TheText.Get("RATNG19"); if (rating < 700) return TheText.Get("RATNG20"); if (rating < 850) return TheText.Get("RATNG21"); if (rating < 1000) return TheText.Get("RATNG22"); if (rating < 1005) return TheText.Get("RATNG23"); if (rating < 1150) return TheText.Get("RATNG24"); if (rating < 1300) return TheText.Get(TimesArrested > 0 ? "RATNG25" : "RATNG24"); if (rating < 1500) return TheText.Get("RATNG26"); if (rating < 1700) return TheText.Get("RATNG27"); if (rating < 2000) return TheText.Get("RATNG28"); if (rating < 2100) return TheText.Get("RATNG29"); if (rating < 2300) return TheText.Get("RATNG30"); if (rating < 2500) return TheText.Get("RATNG31"); if (rating < 2750) return TheText.Get("RATNG32"); if (rating < 3000) return TheText.Get("RATNG33"); if (rating < 3500) return TheText.Get("RATNG34"); if (rating < 4000) return TheText.Get("RATNG35"); if (rating < 5000) return TheText.Get("RATNG36"); if (rating < 7500) return TheText.Get("RATNG37"); if (rating < 10000) return TheText.Get("RATNG38"); if (rating < 20000) return TheText.Get("RATNG39"); if (rating < 30000) return TheText.Get("RATNG40"); if (rating < 40000) return TheText.Get("RATNG41"); if (rating < 50000) return TheText.Get("RATNG42"); if (rating < 65000) return TheText.Get("RATNG43"); if (rating < 80000) return TheText.Get("RATNG44"); if (rating < 100000) return TheText.Get("RATNG45"); if (rating < 150000) return TheText.Get("RATNG46"); if (rating < 200000) return TheText.Get("RATNG47"); if (rating < 300000) return TheText.Get("RATNG48"); if (rating < 375000) return TheText.Get("RATNG49"); if (rating < 500000) return TheText.Get(FlightTime / 60000 / 60 > 10 ? "RATNG50" : "RATNG49"); if (rating < 1000000) return TheText.Get("RATNG51"); return TheText.Get(CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney > 10000000 ? "RATNG52" : "RATNG51"); } wchar *CStats::FindChaseString(float fMediaLevel) { if (fMediaLevel < 20.0f) return TheText.Get("CHASE1"); if (fMediaLevel < 50.0f) return TheText.Get("CHASE2"); if (fMediaLevel < 75.0f) return TheText.Get("CHASE3"); if (fMediaLevel < 100.0f) return TheText.Get("CHASE4"); if (fMediaLevel < 150.0f) return TheText.Get("CHASE5"); if (fMediaLevel < 200.0f) return TheText.Get("CHASE6"); if (fMediaLevel < 250.0f) return TheText.Get("CHASE7"); if (fMediaLevel < 300.0f) return TheText.Get("CHASE8"); if (fMediaLevel < 350.0f) return TheText.Get("CHASE9"); if (fMediaLevel < 400.0f) return TheText.Get("CHASE10"); if (fMediaLevel < 500.0f) return TheText.Get("CHASE11"); if (fMediaLevel < 600.0f) return TheText.Get("CHASE12"); if (fMediaLevel < 700.0f) return TheText.Get("CHASE13"); if (fMediaLevel < 800.0f) return TheText.Get("CHASE14"); if (fMediaLevel < 900.0f) return TheText.Get("CHASE15"); if (fMediaLevel < 1000.0f) return TheText.Get("CHASE16"); if (fMediaLevel < 1200.0f) return TheText.Get("CHASE17"); if (fMediaLevel < 1400.0f) return TheText.Get("CHASE18"); if (fMediaLevel < 1600.0f) return TheText.Get("CHASE19"); if (fMediaLevel < 1800.0f) return TheText.Get("CHASE20"); return TheText.Get("CHASE21"); } int32 CStats::FindCriminalRatingNumber() { int32 rating; rating = FiresExtinguished + 10 * HighestLevelFireMission + 10 * HighestLevelAmbulanceMission + CriminalsCaught + LivesSavedWithAmbulance + 30 * HelisDestroyed + TotalLegitimateKills - 3 * TimesArrested - 3 * TimesDied + CWorld::Players[CWorld::PlayerInFocus].m_nMoney / 5000; if (CPad::bHasPlayerCheated || CheatedCount > 0) { rating -= CheatedCount; if (rating <= -10000) rating = -10000; } else if (rating <= 0) { rating = 0; } if (RoundsFiredByPlayer > 100) rating += (float)BulletsThatHit / (float)RoundsFiredByPlayer * 500.0f; if (TotalProgressInGame) rating += ProgressMade / TotalProgressInGame * 1000.0f; return rating; } float CStats::GetPercentageProgress() { float percentCompleted = (TotalProgressInGame == 0.f ? 0.f : 100.0f * ProgressMade / (CGame::nastyGame ? TotalProgressInGame : TotalProgressInGame - 1.0f)); return Min(percentCompleted, 100.0f); } void CStats::MoneySpentOnWeapons(int32 money) { WeaponBudget += money; } void CStats::MoneySpentOnProperty(int32 money) { PropertyBudget += money; } void CStats::MoneySpentOnAutoPainting(int32 money) { AutoPaintingBudget += money; } void CStats::MoneySpentOnFashion(int32 money) { FashionBudget += money; } void CStats::NumOfVisitsFromLoanSharks(int32 num) { LoanSharks += num; } void CStats::NumOfStoresKnockedOff(int32 num) { StoresKnockedOff += num; } void CStats::NumOfMovieStunts(int32 num) { MovieStunts += num; } void CStats::NumOfAssassinations(int32 num) { Assassinations += num; } void CStats::NumOfPizzasDelivered(int32 num) { PizzasDelivered += num; } void CStats::NumOfGarbagePickups(int32 num) { GarbagePickups += num; } void CStats::NumOfIceCreamSold(int32 num) { IceCreamSold += num; } void CStats::AddNumBloodRingKills(int32 num) { BloodRingKills += num; } void CStats::LongestTimeInBloodRing(int32 time) { if (BloodRingTime < time) BloodRingTime = time; } void CStats::AddPropertyAsOwned(int32 id) { if (!PropertyOwned[id]) { PropertyOwned[id] = true; ++NumPropertyOwned; } } float CStats::GetFavoriteRadioStationList(int32 station) { return FavoriteRadioStationList[station]; } void CStats::SaveStats(uint8 *buf, uint32 *size) { CheckPointReachedSuccessfully(); uint8 *buf_start = buf; *size = sizeof(PeopleKilledByPlayer) + sizeof(PeopleKilledByOthers) + sizeof(CarsExploded) + sizeof(BoatsExploded) + sizeof(TyresPopped) + sizeof(RoundsFiredByPlayer) + sizeof(PedsKilledOfThisType) + sizeof(HelisDestroyed) + sizeof(ProgressMade) + sizeof(TotalProgressInGame) + sizeof(KgsOfExplosivesUsed) + sizeof(BulletsThatHit) + sizeof(HeadsPopped) + sizeof(WantedStarsAttained) + sizeof(WantedStarsEvaded) + sizeof(TimesArrested) + sizeof(TimesDied) + sizeof(DaysPassed) + sizeof(SafeHouseVisits) + sizeof(Sprayings) + sizeof(MaximumJumpDistance) + sizeof(MaximumJumpHeight) + sizeof(MaximumJumpFlips) + sizeof(MaximumJumpSpins) + sizeof(BestStuntJump) + sizeof(NumberOfUniqueJumpsFound) + sizeof(TotalNumberOfUniqueJumps) + sizeof(MissionsGiven) + sizeof(PassengersDroppedOffWithTaxi) + sizeof(MoneyMadeWithTaxi) + sizeof(IndustrialPassed) + sizeof(CommercialPassed) + sizeof(SuburbanPassed) + sizeof(PamphletMissionPassed) + sizeof(NoMoreHurricanes) + sizeof(DistanceTravelledOnFoot) + sizeof(DistanceTravelledByCar) + sizeof(DistanceTravelledByBike) + sizeof(DistanceTravelledByBoat) + sizeof(DistanceTravelledByGolfCart) + sizeof(DistanceTravelledByHelicoptor) + sizeof(DistanceTravelledByPlane) + sizeof(LivesSavedWithAmbulance) + sizeof(CriminalsCaught) + sizeof(FiresExtinguished) + sizeof(HighestLevelVigilanteMission) + sizeof(HighestLevelAmbulanceMission) + sizeof(HighestLevelFireMission) + sizeof(PhotosTaken) + sizeof(NumberKillFrenziesPassed) + sizeof(TotalNumberKillFrenzies) + sizeof(TotalNumberMissions) + sizeof(FlightTime) + sizeof(TimesDrowned) + sizeof(SeagullsKilled) + sizeof(WeaponBudget) + sizeof(FashionBudget) + sizeof(LoanSharks) + sizeof(StoresKnockedOff) + sizeof(MovieStunts) + sizeof(Assassinations) + sizeof(PizzasDelivered) + sizeof(GarbagePickups) + sizeof(IceCreamSold) + sizeof(TopShootingRangeScore) + sizeof(ShootingRank) + sizeof(LongestWheelie) + sizeof(LongestStoppie) + sizeof(Longest2Wheel) + sizeof(LongestWheelieDist) + sizeof(LongestStoppieDist) + sizeof(Longest2WheelDist) + sizeof(PropertyBudget) + sizeof(AutoPaintingBudget) + sizeof(PropertyDestroyed) + sizeof(NumPropertyOwned) + sizeof(BloodRingKills) + sizeof(BloodRingTime) + sizeof(PropertyOwned) + sizeof(HighestChaseValue) + sizeof(FastestTimes) + sizeof(HighestScores) + sizeof(BestPositions) + sizeof(KillsSinceLastCheckpoint) + sizeof(TotalLegitimateKills) + sizeof(LastMissionPassedName) + sizeof(CheatedCount) + sizeof(FavoriteRadioStationList); #define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); buf += sizeof(data); CopyToBuf(buf, PeopleKilledByPlayer); CopyToBuf(buf, PeopleKilledByOthers); CopyToBuf(buf, CarsExploded); CopyToBuf(buf, BoatsExploded); CopyToBuf(buf, TyresPopped); CopyToBuf(buf, RoundsFiredByPlayer); CopyToBuf(buf, PedsKilledOfThisType); CopyToBuf(buf, HelisDestroyed); CopyToBuf(buf, ProgressMade); CopyToBuf(buf, TotalProgressInGame); CopyToBuf(buf, KgsOfExplosivesUsed); CopyToBuf(buf, BulletsThatHit); CopyToBuf(buf, HeadsPopped); CopyToBuf(buf, WantedStarsAttained); CopyToBuf(buf, WantedStarsEvaded); CopyToBuf(buf, TimesArrested); CopyToBuf(buf, TimesDied); CopyToBuf(buf, DaysPassed); CopyToBuf(buf, SafeHouseVisits); CopyToBuf(buf, Sprayings); CopyToBuf(buf, MaximumJumpDistance); CopyToBuf(buf, MaximumJumpHeight); CopyToBuf(buf, MaximumJumpFlips); CopyToBuf(buf, MaximumJumpSpins); CopyToBuf(buf, BestStuntJump); CopyToBuf(buf, NumberOfUniqueJumpsFound); CopyToBuf(buf, TotalNumberOfUniqueJumps); CopyToBuf(buf, MissionsGiven); CopyToBuf(buf, PassengersDroppedOffWithTaxi); CopyToBuf(buf, MoneyMadeWithTaxi); CopyToBuf(buf, IndustrialPassed); CopyToBuf(buf, CommercialPassed); CopyToBuf(buf, SuburbanPassed); CopyToBuf(buf, PamphletMissionPassed); CopyToBuf(buf, NoMoreHurricanes); CopyToBuf(buf, DistanceTravelledOnFoot); CopyToBuf(buf, DistanceTravelledByCar); CopyToBuf(buf, DistanceTravelledByBike); CopyToBuf(buf, DistanceTravelledByBoat); CopyToBuf(buf, DistanceTravelledByGolfCart); CopyToBuf(buf, DistanceTravelledByHelicoptor); CopyToBuf(buf, DistanceTravelledByPlane); CopyToBuf(buf, LivesSavedWithAmbulance); CopyToBuf(buf, CriminalsCaught); CopyToBuf(buf, FiresExtinguished); CopyToBuf(buf, HighestLevelVigilanteMission); CopyToBuf(buf, HighestLevelAmbulanceMission); CopyToBuf(buf, HighestLevelFireMission); CopyToBuf(buf, PhotosTaken); CopyToBuf(buf, NumberKillFrenziesPassed); CopyToBuf(buf, TotalNumberKillFrenzies); CopyToBuf(buf, TotalNumberMissions); CopyToBuf(buf, FlightTime); CopyToBuf(buf, TimesDrowned); CopyToBuf(buf, SeagullsKilled); CopyToBuf(buf, WeaponBudget); CopyToBuf(buf, FashionBudget); CopyToBuf(buf, LoanSharks); CopyToBuf(buf, StoresKnockedOff); CopyToBuf(buf, MovieStunts); CopyToBuf(buf, Assassinations); CopyToBuf(buf, PizzasDelivered); CopyToBuf(buf, GarbagePickups); CopyToBuf(buf, IceCreamSold); CopyToBuf(buf, TopShootingRangeScore); CopyToBuf(buf, ShootingRank); CopyToBuf(buf, LongestWheelie); CopyToBuf(buf, LongestStoppie); CopyToBuf(buf, Longest2Wheel); CopyToBuf(buf, LongestWheelieDist); CopyToBuf(buf, LongestStoppieDist); CopyToBuf(buf, Longest2WheelDist); CopyToBuf(buf, PropertyBudget); CopyToBuf(buf, AutoPaintingBudget); CopyToBuf(buf, PropertyDestroyed); CopyToBuf(buf, NumPropertyOwned); CopyToBuf(buf, BloodRingKills); CopyToBuf(buf, BloodRingTime); CopyToBuf(buf, PropertyOwned); CopyToBuf(buf, HighestChaseValue); CopyToBuf(buf, FastestTimes); CopyToBuf(buf, HighestScores); CopyToBuf(buf, BestPositions); CopyToBuf(buf, KillsSinceLastCheckpoint); CopyToBuf(buf, TotalLegitimateKills); CopyToBuf(buf, LastMissionPassedName); CopyToBuf(buf, CheatedCount); PopulateFavoriteRadioStationList(); CopyToBuf(buf, FavoriteRadioStationList); assert(buf - buf_start == *size); #undef CopyToBuf } void CStats::LoadStats(uint8 *buf, uint32 size) { uint8* buf_start = buf; #define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); buf += sizeof(data); CopyFromBuf(buf, PeopleKilledByPlayer); CopyFromBuf(buf, PeopleKilledByOthers); CopyFromBuf(buf, CarsExploded); CopyFromBuf(buf, BoatsExploded); CopyFromBuf(buf, TyresPopped); CopyFromBuf(buf, RoundsFiredByPlayer); CopyFromBuf(buf, PedsKilledOfThisType); CopyFromBuf(buf, HelisDestroyed); CopyFromBuf(buf, ProgressMade); CopyFromBuf(buf, TotalProgressInGame); CopyFromBuf(buf, KgsOfExplosivesUsed); CopyFromBuf(buf, BulletsThatHit); CopyFromBuf(buf, HeadsPopped); CopyFromBuf(buf, WantedStarsAttained); CopyFromBuf(buf, WantedStarsEvaded); CopyFromBuf(buf, TimesArrested); CopyFromBuf(buf, TimesDied); CopyFromBuf(buf, DaysPassed); CopyFromBuf(buf, SafeHouseVisits); CopyFromBuf(buf, Sprayings); CopyFromBuf(buf, MaximumJumpDistance); CopyFromBuf(buf, MaximumJumpHeight); CopyFromBuf(buf, MaximumJumpFlips); CopyFromBuf(buf, MaximumJumpSpins); CopyFromBuf(buf, BestStuntJump); CopyFromBuf(buf, NumberOfUniqueJumpsFound); CopyFromBuf(buf, TotalNumberOfUniqueJumps); CopyFromBuf(buf, MissionsGiven); CopyFromBuf(buf, PassengersDroppedOffWithTaxi); CopyFromBuf(buf, MoneyMadeWithTaxi); CopyFromBuf(buf, IndustrialPassed); CopyFromBuf(buf, CommercialPassed); CopyFromBuf(buf, SuburbanPassed); CopyFromBuf(buf, PamphletMissionPassed); CopyFromBuf(buf, NoMoreHurricanes); CopyFromBuf(buf, DistanceTravelledOnFoot); CopyFromBuf(buf, DistanceTravelledByCar); CopyFromBuf(buf, DistanceTravelledByBike); CopyFromBuf(buf, DistanceTravelledByBoat); CopyFromBuf(buf, DistanceTravelledByGolfCart); CopyFromBuf(buf, DistanceTravelledByHelicoptor); CopyFromBuf(buf, DistanceTravelledByPlane); CopyFromBuf(buf, LivesSavedWithAmbulance); CopyFromBuf(buf, CriminalsCaught); CopyFromBuf(buf, FiresExtinguished); CopyFromBuf(buf, HighestLevelVigilanteMission); CopyFromBuf(buf, HighestLevelAmbulanceMission); CopyFromBuf(buf, HighestLevelFireMission); CopyFromBuf(buf, PhotosTaken); CopyFromBuf(buf, NumberKillFrenziesPassed); CopyFromBuf(buf, TotalNumberKillFrenzies); CopyFromBuf(buf, TotalNumberMissions); CopyFromBuf(buf, FlightTime); CopyFromBuf(buf, TimesDrowned); CopyFromBuf(buf, SeagullsKilled); CopyFromBuf(buf, WeaponBudget); CopyFromBuf(buf, FashionBudget); CopyFromBuf(buf, LoanSharks); CopyFromBuf(buf, StoresKnockedOff); CopyFromBuf(buf, MovieStunts); CopyFromBuf(buf, Assassinations); CopyFromBuf(buf, PizzasDelivered); CopyFromBuf(buf, GarbagePickups); CopyFromBuf(buf, IceCreamSold); CopyFromBuf(buf, TopShootingRangeScore); CopyFromBuf(buf, ShootingRank); CopyFromBuf(buf, LongestWheelie); CopyFromBuf(buf, LongestStoppie); CopyFromBuf(buf, Longest2Wheel); CopyFromBuf(buf, LongestWheelieDist); CopyFromBuf(buf, LongestStoppieDist); CopyFromBuf(buf, Longest2WheelDist); CopyFromBuf(buf, PropertyBudget); CopyFromBuf(buf, AutoPaintingBudget); CopyFromBuf(buf, PropertyDestroyed); CopyFromBuf(buf, NumPropertyOwned); CopyFromBuf(buf, BloodRingKills); CopyFromBuf(buf, BloodRingTime); CopyFromBuf(buf, PropertyOwned); CopyFromBuf(buf, HighestChaseValue); CopyFromBuf(buf, FastestTimes); CopyFromBuf(buf, HighestScores); CopyFromBuf(buf, BestPositions); CopyFromBuf(buf, KillsSinceLastCheckpoint); CopyFromBuf(buf, TotalLegitimateKills); CopyFromBuf(buf, LastMissionPassedName); CopyFromBuf(buf, CheatedCount); CopyFromBuf(buf, FavoriteRadioStationList); assert(buf - buf_start == size); #undef CopyFromBuf } void CStats::PopulateFavoriteRadioStationList() { float* pListenTimeArray = DMAudio.GetListenTimeArray(); for (int i = 0; i < NUM_RADIOS; i++) FavoriteRadioStationList[i] = pListenTimeArray[i]; } void CStats::BuildStatLine(Const char *text, void *stat, int displayType, void *stat2, int isTime) { #define STAT_D *(int*)stat #define STAT_F *(float*)stat #define STAT2_D *(int*)stat2 #define STAT2_F *(float*)stat2 if (!text) return; gString2[0] = '\0'; if (isTime == 1) { if (*((int*)stat2) >= 10) sprintf(gString2, " %d:%d", STAT_D, STAT2_D); else sprintf(gString2, " %d:0%d", STAT_D, STAT2_D); } else if (stat2) { #ifdef MORE_LANGUAGES if (CFont::IsJapanese()) { switch (displayType) { case 0: case 4: sprintf(gString2, " %d/%d", STAT_D, STAT2_D); break; case 1: sprintf(gString2, " %.2f/%.2f", STAT_F, STAT2_F); break; case 2: sprintf(gString2, " %d%%/%d%%", STAT_D, STAT2_D); break; case 3: sprintf(gString2, " $%.2f/$%.2f", STAT_F, STAT2_F); break; default: break; } } else #endif { switch (displayType) { case 0: sprintf(gString2, " %d %s %d", STAT_D, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_D); break; case 1: sprintf(gString2, " %.2f %s %.2f", STAT_F, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_F); break; case 2: sprintf(gString2, " %d%% %s %d%%", STAT_D, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_D); break; case 3: sprintf(gString2, " $%.2f %s $%.2f", STAT_F, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_F); break; case 4: sprintf(gString2, " %d_ %s %d_", STAT_D, UnicodeToAscii(TheText.Get("FEST_OO")), STAT2_D); break; default: break; } } } else if (stat) { switch (displayType) { case 0: sprintf(gString2, "%d", STAT_D); break; case 1: sprintf(gString2, "%.2f", STAT_F); break; case 2: sprintf(gString2, "%d%%", STAT_D); break; case 3: sprintf(gString2, "$%.2f", STAT_F); break; case 4: #ifdef MORE_LANGUAGES if (CFont::IsJapanese()) sprintf(gString2, "%d", STAT_D); else #endif sprintf(gString2, "%d_", STAT_D); break; default: break; } } UnicodeStrcpy(gUString, TheText.Get(text)); CFont::FilterOutTokensFromString(gUString); AsciiToUnicode(gString2, gUString2); #undef STAT_D #undef STAT_F #undef STAT2_D #undef STAT2_F } // rowIdx 99999 returns total numbers of rows. otherwise it returns 0. int CStats::ConstructStatLine(int rowIdx) { #define STAT_LINE_1(varType, left, right1, type) \ do { \ if(counter == rowIdx){ \ varType a = right1; \ BuildStatLine(left, &a, type, nil, 0); \ return 0; \ } counter++; \ } while(0) #define STAT_LINE_2(varType, left, right1, type, right2, time) \ do { \ if(counter == rowIdx){ \ varType a = right1; \ varType b = right2; \ BuildStatLine(left, &a, type, &b, time); \ return 0; \ } counter++; \ } while(0) #define TEXT_ON_LEFT_GXT(name) \ do { \ if(counter == rowIdx){ \ BuildStatLine(name, nil, 0, nil, 0); \ return 0; \ } counter++; \ } while(0) #define TEXT_ON_RIGHT(text) \ do { \ if(counter == rowIdx){ \ gUString[0] = '\0'; \ UnicodeStrcpy(gUString2, text); \ return 0; \ } counter++; \ } while(0) #define FASTEST_TIME(id, str) \ do { \ if(FastestTimes[id]) { \ if(counter == rowIdx){ \ int hour = 0, minute; \ for (int i = FastestTimes[id]; i > 59; i -= 60) hour++; \ for (minute = FastestTimes[id]; minute > 59; minute -= 60); \ if (minute < 0) minute = -minute; \ BuildStatLine(str, &hour, 0, &minute, 1); \ return 0; \ } \ counter++; \ } \ } while(0) switch (rowIdx) { case 0: { int percentCompleted = GetPercentageProgress(); BuildStatLine("PER_COM", &percentCompleted, 2, nil, 0); return 0; } case 1: { BuildStatLine("NMISON", &MissionsGiven, 0, nil, 0); return 0; } case 2: { int hour = (CTimer::GetTimeInMilliseconds() / 60000) / 60; int minute = (CTimer::GetTimeInMilliseconds() / 60000) % 60; BuildStatLine("ST_TIME", &hour, 0, &minute, 1); return 0; } case 3: { BuildStatLine("DAYSPS", &DaysPassed, 0, nil, 0); return 0; } case 4: { BuildStatLine("NUMSHV", &SafeHouseVisits, 0, nil, 0); return 0; } } int counter = 5; if (CGame::nastyGame) { STAT_LINE_2(int, "FEST_RP", NumberKillFrenziesPassed, 0, TotalNumberKillFrenzies, 0); } CPlayerInfo &player = CWorld::Players[CWorld::PlayerInFocus]; // Hidden packages shouldn't be shown with percent #ifdef FIX_BUGS STAT_LINE_2(int, "PERPIC", player.m_nCollectedPackages, 0, player.m_nTotalPackages, 0); #else float fPackagesPercent = 0.0f; if (player.m_nTotalPackages != 0) fPackagesPercent = player.m_nCollectedPackages * 100.0f / player.m_nTotalPackages; STAT_LINE_2(int, "PERPIC", fPackagesPercent, 0, 100, 0); #endif if (CGame::nastyGame) { STAT_LINE_1(int, "PE_WAST", PeopleKilledByPlayer, 0); STAT_LINE_1(int, "PE_WSOT", PeopleKilledByOthers, 0); } STAT_LINE_1(int, "CAR_EXP", CarsExploded, 0); STAT_LINE_1(int, "BOA_EXP", BoatsExploded, 0); STAT_LINE_1(int, "HEL_DST", HelisDestroyed, 0); STAT_LINE_1(int, "TYREPOP", TyresPopped, 0); STAT_LINE_1(int, "ST_STAR", WantedStarsAttained, 0); STAT_LINE_1(int, "ST_STGN", WantedStarsEvaded, 0); STAT_LINE_1(int, "TM_BUST", TimesArrested, 0); STAT_LINE_1(int, "TM_DED", TimesDied, 0); #ifdef MORE_LANGUAGES // JP version removed it altogether actually if (!CFont::IsJapanese()) #endif STAT_LINE_1(int, "ST_HEAD", HeadsPopped, 0); static uint32 lastProcessedDay = UINT32_MAX; static uint32 lastPoliceSpending = 0; // What a random stat... if (lastProcessedDay != DaysPassed) { lastProcessedDay = DaysPassed; lastPoliceSpending = (CTimer::GetTimeInMilliseconds() & 255 + 80) * 255.44f; } STAT_LINE_1(float, "DAYPLC", lastPoliceSpending, 3); int mostPatheticGang = 0; int mostKill = 0; for (int i = PEDTYPE_GANG1; i < PEDTYPE_GANG9; ++i) { if (CStats::PedsKilledOfThisType[i] > mostKill) { mostKill = CStats::PedsKilledOfThisType[i]; mostPatheticGang = i; } } if (mostPatheticGang > 0) { TEXT_ON_LEFT_GXT("ST_GANG"); switch (mostPatheticGang) { case PEDTYPE_GANG1: TEXT_ON_RIGHT(TheText.Get("ST_GNG1")); break; case PEDTYPE_GANG2: TEXT_ON_RIGHT(TheText.Get("ST_GNG2")); break; case PEDTYPE_GANG3: TEXT_ON_RIGHT(TheText.Get("ST_GNG3")); break; case PEDTYPE_GANG4: TEXT_ON_RIGHT(TheText.Get("ST_GNG4")); break; case PEDTYPE_GANG5: TEXT_ON_RIGHT(TheText.Get("ST_GNG5")); break; case PEDTYPE_GANG6: TEXT_ON_RIGHT(TheText.Get("ST_GNG6")); break; case PEDTYPE_GANG7: TEXT_ON_RIGHT(TheText.Get("ST_GNG7")); break; case PEDTYPE_GANG8: TEXT_ON_RIGHT(TheText.Get("ST_GNG8")); break; default: break; } } STAT_LINE_1(int, "GNG_WST", PedsKilledOfThisType[PEDTYPE_GANG9] + PedsKilledOfThisType[PEDTYPE_GANG8] + PedsKilledOfThisType[PEDTYPE_GANG7] + PedsKilledOfThisType[PEDTYPE_GANG6] + PedsKilledOfThisType[PEDTYPE_GANG5] + PedsKilledOfThisType[PEDTYPE_GANG4] + PedsKilledOfThisType[PEDTYPE_GANG3] + PedsKilledOfThisType[PEDTYPE_GANG2] + PedsKilledOfThisType[PEDTYPE_GANG1], 0); STAT_LINE_1(int, "DED_CRI", PedsKilledOfThisType[PEDTYPE_CRIMINAL], 0); STAT_LINE_1(int, "KGS_EXP", KgsOfExplosivesUsed, 0); STAT_LINE_1(int, "BUL_FIR", RoundsFiredByPlayer, 0); STAT_LINE_1(int, "BUL_HIT", BulletsThatHit, 0); ; STAT_LINE_1(int, "ACCURA", RoundsFiredByPlayer == 0 ? 0 : (BulletsThatHit * 100.0f / (float)RoundsFiredByPlayer), 2); switch (FrontEndMenuManager.m_PrefsLanguage) { case CMenuManager::LANGUAGE_AMERICAN: #ifndef USE_MEASUREMENTS_IN_METERS STAT_LINE_1(float, "FEST_DF", DistanceTravelledOnFoot * MILES_IN_METER, 1); STAT_LINE_1(float, "FEST_DC", DistanceTravelledByCar * MILES_IN_METER, 1); STAT_LINE_1(float, "DISTBIK", DistanceTravelledByBike * MILES_IN_METER, 1); STAT_LINE_1(float, "DISTBOA", DistanceTravelledByBoat * MILES_IN_METER, 1); STAT_LINE_1(float, "DISTGOL", DistanceTravelledByGolfCart * MILES_IN_METER, 1); STAT_LINE_1(float, "DISTHEL", DistanceTravelledByHelicoptor * MILES_IN_METER, 1); #ifdef FIX_BUGS STAT_LINE_1(float, "TOT_DIS", (DistanceTravelledOnFoot + DistanceTravelledByCar + DistanceTravelledByBoat + DistanceTravelledByBike + DistanceTravelledByGolfCart + DistanceTravelledByHelicoptor + DistanceTravelledByPlane) * MILES_IN_METER, 1); STAT_LINE_1(float, "MXCARD", MaximumJumpDistance * FEET_IN_METER, 1); STAT_LINE_1(float, "MXCARJ", MaximumJumpHeight * FEET_IN_METER, 1); #else STAT_LINE_1(float, "TOT_DIS", (DistanceTravelledOnFoot + DistanceTravelledByCar + DistanceTravelledByBoat + DistanceTravelledByBike + DistanceTravelledByGolfCart + DistanceTravelledByHelicoptor) * MILES_IN_METER, 1); #endif break; #endif case CMenuManager::LANGUAGE_FRENCH: case CMenuManager::LANGUAGE_GERMAN: case CMenuManager::LANGUAGE_ITALIAN: case CMenuManager::LANGUAGE_SPANISH: #ifdef MORE_LANGUAGES case CMenuManager::LANGUAGE_POLISH: case CMenuManager::LANGUAGE_RUSSIAN: case CMenuManager::LANGUAGE_JAPANESE: #endif STAT_LINE_1(float, "FESTDFM", DistanceTravelledOnFoot, 1); STAT_LINE_1(float, "FESTDCM", DistanceTravelledByCar, 1); STAT_LINE_1(float, "DISTBIM", DistanceTravelledByBike, 1); STAT_LINE_1(float, "DISTBOM", DistanceTravelledByBoat, 1); STAT_LINE_1(float, "DISTGOM", DistanceTravelledByGolfCart, 1); STAT_LINE_1(float, "DISTHEM", DistanceTravelledByHelicoptor, 1); #ifdef FIX_BUGS STAT_LINE_1(float, "TOTDISM", DistanceTravelledOnFoot + DistanceTravelledByCar + DistanceTravelledByBoat + DistanceTravelledByBike + DistanceTravelledByGolfCart + DistanceTravelledByHelicoptor + DistanceTravelledByPlane, 1); STAT_LINE_1(float, "MXCARDM", MaximumJumpDistance, 1); STAT_LINE_1(float, "MXCARJM", MaximumJumpHeight, 1); #else STAT_LINE_1(float, "TOTDISM", DistanceTravelledOnFoot + DistanceTravelledByCar + DistanceTravelledByBoat + DistanceTravelledByGolfCart + DistanceTravelledByHelicoptor, 1); #endif break; default: break; } // They were selecting the unit according to language in III, but they deleted the feet code in VC. Weird #ifndef FIX_BUGS STAT_LINE_1(float, "MXCARDM", MaximumJumpDistance, 1); STAT_LINE_1(float, "MXCARJM", MaximumJumpHeight, 1); #endif STAT_LINE_1(int, "MXFLIP", MaximumJumpFlips, 0); STAT_LINE_2(int, "NOUNIF", NumberOfUniqueJumpsFound, 0, TotalNumberOfUniqueJumps, 0); STAT_LINE_1(int, "MXJUMP", MaximumJumpSpins, 4); TEXT_ON_LEFT_GXT("BSTSTU"); switch (BestStuntJump) { case 1: TEXT_ON_RIGHT(TheText.Get("INSTUN")); break; case 2: TEXT_ON_RIGHT(TheText.Get("PRINST")); break; case 3: TEXT_ON_RIGHT(TheText.Get("DBINST")); break; case 4: TEXT_ON_RIGHT(TheText.Get("DBPINS")); break; case 5: TEXT_ON_RIGHT(TheText.Get("TRINST")); break; case 6: TEXT_ON_RIGHT(TheText.Get("PRTRST")); break; case 7: TEXT_ON_RIGHT(TheText.Get("QUINST")); break; case 8: TEXT_ON_RIGHT(TheText.Get("PQUINS")); break; default: TEXT_ON_RIGHT(TheText.Get("NOSTUC")); break; } STAT_LINE_1(int, "ST_WHEE", LongestWheelie, 0); STAT_LINE_1(float, "ST_WHED", LongestWheelieDist, 1); STAT_LINE_1(int, "ST_STOP", LongestStoppie, 0); STAT_LINE_1(float, "ST_STOD", LongestStoppieDist, 1); STAT_LINE_1(int, "ST_2WHE", Longest2Wheel, 0); STAT_LINE_1(float, "ST_2WHD", Longest2WheelDist, 1); if (LoanSharks > 0.0f) STAT_LINE_1(int, "ST_LOAN", LoanSharks, 0); STAT_LINE_1(int, "FEST_CC", CriminalsCaught, 0); STAT_LINE_1(int, "FEST_HV", HighestLevelVigilanteMission, 0); STAT_LINE_1(int, "PASDRO", PassengersDroppedOffWithTaxi, 0); STAT_LINE_1(float, "MONTAX", MoneyMadeWithTaxi, 3); STAT_LINE_1(int, "FEST_LS", LivesSavedWithAmbulance, 0); STAT_LINE_1(int, "FEST_HA", HighestLevelAmbulanceMission, 0); STAT_LINE_1(int, "FEST_FE", FiresExtinguished, 0); STAT_LINE_1(int, "FIRELVL", HighestLevelFireMission, 0); STAT_LINE_2(int, "ST_STOR", StoresKnockedOff, 0, 15, 0); if (MovieStunts > 0.0f) STAT_LINE_1(int, "ST_MOVI", MovieStunts, 0); STAT_LINE_2(int, "ST_ASSI", Assassinations, 0, 5, 0); if (PhotosTaken > 0) STAT_LINE_1(int, "ST_PHOT", PhotosTaken, 0); if (PizzasDelivered > 0.0f) STAT_LINE_1(int, "ST_PIZZ", PizzasDelivered, 0); if (GarbagePickups > 0.0f) STAT_LINE_1(int, "ST_GARB", GarbagePickups, 0); if (IceCreamSold > 0.0f) STAT_LINE_1(int, "ST_ICEC", IceCreamSold, 0); if (HighestScores[1]) STAT_LINE_1(int, "STHC_02", HighestScores[1], 0); FASTEST_TIME(0, "STFT_01"); FASTEST_TIME(1, "STFT_02"); FASTEST_TIME(2, "STFT_03"); FASTEST_TIME(3, "STFT_04"); FASTEST_TIME(4, "STFT_05"); FASTEST_TIME(5, "STFT_06"); FASTEST_TIME(6, "STFT_07"); FASTEST_TIME(7, "STFT_08"); FASTEST_TIME(8, "STFT_09"); FASTEST_TIME(9, "STFT_10"); FASTEST_TIME(10, "STFT_11"); FASTEST_TIME(11, "STFT_12"); FASTEST_TIME(12, "STFT_13"); FASTEST_TIME(13, "STFT_14"); FASTEST_TIME(14, "STFT_15"); FASTEST_TIME(15, "STFT_16"); FASTEST_TIME(16, "STFT_17"); FASTEST_TIME(17, "STFT_18"); FASTEST_TIME(18, "STFT_19"); FASTEST_TIME(19, "STFT_20"); FASTEST_TIME(22, "STFT_23"); if (HighestScores[0]) STAT_LINE_1(int, "STHC_01", HighestScores[0], 0); if (HighestScores[3]) STAT_LINE_1(int, "STHC_04", HighestScores[3], 0); if (HighestScores[2]) STAT_LINE_1(int, "STHC_03", HighestScores[2], 0); if (BestPositions[0] != INT_MAX) STAT_LINE_1(int, "STHC_05", BestPositions[0], 0); FASTEST_TIME(20, "STFT_21"); if (FastestTimes[21]) STAT_LINE_1(float, "STFT_22", FastestTimes[21] / 1000, 1); if (TopShootingRangeScore > 0.0f) STAT_LINE_1(int, "TOP_SHO", TopShootingRangeScore, 0); if (ShootingRank > 0.0f) STAT_LINE_1(int, "SHO_RAN", ShootingRank, 0); int flightMinute = (FlightTime / 60000) % 60; int flightHour = (FlightTime / 60000) / 60; STAT_LINE_2(int, "ST_FTIM", flightHour, 0, flightMinute, 1); // We always have pilot rank if we flew more then 5 minutes #ifndef FIX_BUGS if (flightHour != 0) TEXT_ON_LEFT_GXT("ST_PRAN"); #endif #ifdef FIX_BUGS #define FL_TIME_MORE_THAN(hour, minute) (flightHour > hour || (flightHour == hour && flightMinute >= minute)) #else #define FL_TIME_MORE_THAN(hour, minute) (flightHour > hour || flightMinute >= minute) #endif if (FL_TIME_MORE_THAN(0,5)) { #ifdef FIX_BUGS TEXT_ON_LEFT_GXT("ST_PRAN"); #endif if (!FL_TIME_MORE_THAN(0,10)) TEXT_ON_RIGHT(TheText.Get("ST_PR01")); else if (!FL_TIME_MORE_THAN(0,20)) TEXT_ON_RIGHT(TheText.Get("ST_PR02")); else if (!FL_TIME_MORE_THAN(0,30)) TEXT_ON_RIGHT(TheText.Get("ST_PR03")); else if (!FL_TIME_MORE_THAN(1,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR04")); else if (!FL_TIME_MORE_THAN(1,30)) TEXT_ON_RIGHT(TheText.Get("ST_PR05")); else if (!FL_TIME_MORE_THAN(2,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR06")); else if (!FL_TIME_MORE_THAN(2,30)) TEXT_ON_RIGHT(TheText.Get("ST_PR07")); else if (!FL_TIME_MORE_THAN(3,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR08")); else if (!FL_TIME_MORE_THAN(3,30)) TEXT_ON_RIGHT(TheText.Get("ST_PR09")); else if (!FL_TIME_MORE_THAN(4,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR10")); else if (!FL_TIME_MORE_THAN(5,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR11")); else if (!FL_TIME_MORE_THAN(10,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR12")); else if (!FL_TIME_MORE_THAN(20,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR13")); else if (!FL_TIME_MORE_THAN(25,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR14")); else if (!FL_TIME_MORE_THAN(30,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR15")); else if (!FL_TIME_MORE_THAN(49,2)) TEXT_ON_RIGHT(TheText.Get("ST_PR16")); else if (!FL_TIME_MORE_THAN(50,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR17")); else if (!FL_TIME_MORE_THAN(100,0)) TEXT_ON_RIGHT(TheText.Get("ST_PR18")); else TEXT_ON_RIGHT(TheText.Get("ST_PR19")); } #undef FL_TIME_MORE_THAN if (BloodRingKills > 0) STAT_LINE_1(int, "ST_BRK", BloodRingKills, 0); if (BloodRingTime > 0) STAT_LINE_1(int, "ST_LTBR", BloodRingTime, 0); STAT_LINE_1(int, "ST_DRWN", TimesDrowned, 0); if (SeagullsKilled > 0) STAT_LINE_1(int, "SEAGULL", SeagullsKilled, 0); bool playerHatesRadio = true; float* pListenTimeArray = DMAudio.GetListenTimeArray(); for (int i = 0; i < NUM_RADIOS; i++) { FavoriteRadioStationList[i] = pListenTimeArray[i]; if (FavoriteRadioStationList[i] != 0.0) // double playerHatesRadio = false; } if (!playerHatesRadio) { // Most listened TEXT_ON_LEFT_GXT("FST_MFR"); float mostListenTime = FavoriteRadioStationList[0]; int mostListenedRadio = 0; for (int i = 0; i < NUM_RADIOS; i++) { if (FavoriteRadioStationList[i] > mostListenTime) { mostListenTime = FavoriteRadioStationList[i]; mostListenedRadio = i; } } switch (mostListenedRadio) { case WILDSTYLE: TEXT_ON_RIGHT(TheText.Get("FEA_FM0")); break; case FLASH_FM: TEXT_ON_RIGHT(TheText.Get("FEA_FM1")); break; case KCHAT: TEXT_ON_RIGHT(TheText.Get("FEA_FM2")); break; case FEVER: TEXT_ON_RIGHT(TheText.Get("FEA_FM3")); break; case V_ROCK: TEXT_ON_RIGHT(TheText.Get("FEA_FM4")); break; case VCPR: TEXT_ON_RIGHT(TheText.Get("FEA_FM5")); break; case RADIO_ESPANTOSO: TEXT_ON_RIGHT(TheText.Get("FEA_FM6")); break; case EMOTION: TEXT_ON_RIGHT(TheText.Get("FEA_FM7")); break; case WAVE: TEXT_ON_RIGHT(TheText.Get("FEA_FM8")); break; case USERTRACK: TEXT_ON_RIGHT(TheText.Get("FEA_MP3")); break; default: TEXT_ON_RIGHT(TheText.Get("FEA_FM8")); // heh break; } // Least listened TEXT_ON_LEFT_GXT("FST_LFR"); float leastListenTime = FavoriteRadioStationList[0]; int leastListenedRadio = 0; for (int i = 0; i < NUM_RADIOS; i++) { #ifdef FIX_BUGS if (!DMAudio.IsMP3RadioChannelAvailable() && i == USERTRACK) continue; #endif if (FavoriteRadioStationList[i] < leastListenTime) { leastListenTime = FavoriteRadioStationList[i]; leastListenedRadio = i; } } #ifndef FIX_BUGS if (!DMAudio.IsMP3RadioChannelAvailable() && leastListenedRadio == USERTRACK) leastListenedRadio = WAVE; #endif switch (leastListenedRadio) { case WILDSTYLE: TEXT_ON_RIGHT(TheText.Get("FEA_FM0")); break; case FLASH_FM: TEXT_ON_RIGHT(TheText.Get("FEA_FM1")); break; case KCHAT: TEXT_ON_RIGHT(TheText.Get("FEA_FM2")); break; case FEVER: TEXT_ON_RIGHT(TheText.Get("FEA_FM3")); break; case V_ROCK: TEXT_ON_RIGHT(TheText.Get("FEA_FM4")); break; case VCPR: TEXT_ON_RIGHT(TheText.Get("FEA_FM5")); break; case RADIO_ESPANTOSO: TEXT_ON_RIGHT(TheText.Get("FEA_FM6")); break; case EMOTION: TEXT_ON_RIGHT(TheText.Get("FEA_FM7")); break; case WAVE: TEXT_ON_RIGHT(TheText.Get("FEA_FM8")); break; case USERTRACK: TEXT_ON_RIGHT(TheText.Get("FEA_MP3")); break; default: TEXT_ON_RIGHT(TheText.Get("FEA_FM8")); // heh break; } } STAT_LINE_1(int, "SPRAYIN", Sprayings, 0); STAT_LINE_1(float, "ST_WEAP", WeaponBudget, 3); STAT_LINE_1(float, "ST_FASH", FashionBudget, 3); STAT_LINE_1(float, "ST_PROP", PropertyBudget, 3); STAT_LINE_1(float, "ST_AUTO", AutoPaintingBudget, 3); STAT_LINE_1(float, "ST_DAMA", PropertyBudget, 3); if (NumPropertyOwned > 0) { STAT_LINE_1(int, "PROPOWN", NumPropertyOwned, 0); if (PropertyOwned[0]) TEXT_ON_RIGHT(TheText.Get("STPR_1")); if (PropertyOwned[1]) TEXT_ON_RIGHT(TheText.Get("STPR_2")); if (PropertyOwned[2]) TEXT_ON_RIGHT(TheText.Get("STPR_3")); if (PropertyOwned[3]) TEXT_ON_RIGHT(TheText.Get("STPR_4")); if (PropertyOwned[4]) TEXT_ON_RIGHT(TheText.Get("STPR_5")); if (PropertyOwned[5]) TEXT_ON_RIGHT(TheText.Get("STPR_6")); if (PropertyOwned[6]) TEXT_ON_RIGHT(TheText.Get("STPR_7")); if (PropertyOwned[7]) TEXT_ON_RIGHT(TheText.Get("STPR_8")); if (PropertyOwned[8]) TEXT_ON_RIGHT(TheText.Get("STPR_9")); if (PropertyOwned[9]) TEXT_ON_RIGHT(TheText.Get("STPR_10")); if (PropertyOwned[10]) TEXT_ON_RIGHT(TheText.Get("STPR_11")); if (PropertyOwned[11]) TEXT_ON_RIGHT(TheText.Get("STPR_12")); if (PropertyOwned[12]) TEXT_ON_RIGHT(TheText.Get("STPR_13")); if (PropertyOwned[13]) TEXT_ON_RIGHT(TheText.Get("STPR_14")); if (PropertyOwned[14]) TEXT_ON_RIGHT(TheText.Get("STPR_15")); } STAT_LINE_1(int, "CHASE", HighestChaseValue, 0); TEXT_ON_RIGHT(FindChaseString(HighestChaseValue)); return counter; #undef STAT_LINE_1 #undef STAT_LINE_2 #undef TEXT_ON_LEFT_GXT #undef TEXT_ON_RIGHT #undef FASTEST_TIME } ================================================ FILE: src/core/Stats.h ================================================ #pragma once #include "PedType.h" #include "audio_enums.h" class CStats { public: enum { TOTAL_FASTEST_TIMES = 23, TOTAL_HIGHEST_SCORES = 5, TOTAL_BEST_POSITIONS = 1, TOTAL_PROPERTIES = 15 }; static int32 SeagullsKilled; static int32 DaysPassed; static int32 HeadsPopped; static int32 CommercialPassed; static int32 IndustrialPassed; static int32 SuburbanPassed; static int32 NumberKillFrenziesPassed; static int32 PeopleKilledByOthers; static int32 HelisDestroyed; static int32 PedsKilledOfThisType[NUM_PEDTYPES]; static int32 TimesDied; static int32 TimesArrested; static int32 KillsSinceLastCheckpoint; static float DistanceTravelledByCar; static float DistanceTravelledByHelicoptor; static float DistanceTravelledByBike; static float DistanceTravelledByBoat; static float DistanceTravelledByPlane; static float DistanceTravelledByGolfCart; static float DistanceTravelledOnFoot; static int32 FlightTime; static int32 TimesDrowned; static int32 PhotosTaken; static float LoanSharks; static float StoresKnockedOff; static float MovieStunts; static float Assassinations; static float PizzasDelivered; static float GarbagePickups; static float IceCreamSold; static float TopShootingRangeScore; static float ShootingRank; static int32 CarsExploded; static int32 BoatsExploded; static int32 WantedStarsAttained; static int32 WantedStarsEvaded; static int32 PeopleKilledByPlayer; static float ProgressMade; static float TotalProgressInGame; static float MaximumJumpDistance; static float MaximumJumpHeight; static int32 MaximumJumpFlips; static int32 MaximumJumpSpins; static int32 BestStuntJump; static int32 NumberOfUniqueJumpsFound; static int32 TotalNumberOfUniqueJumps; static int32 PassengersDroppedOffWithTaxi; static int32 MoneyMadeWithTaxi; static int32 MissionsGiven; static int32 MissionsPassed; static char LastMissionPassedName[8]; static int32 TotalLegitimateKills; static int32 LivesSavedWithAmbulance; static int32 CriminalsCaught; static int32 HighestLevelAmbulanceMission; static int32 HighestLevelVigilanteMission; static int32 HighestLevelFireMission; static int32 FiresExtinguished; static int32 TotalNumberKillFrenzies; static int32 TotalNumberMissions; static int32 RoundsFiredByPlayer; static int32 KgsOfExplosivesUsed; static int32 BulletsThatHit; static int32 BestTimeBombDefusal; static int32 FastestTimes[TOTAL_FASTEST_TIMES]; static int32 HighestScores[TOTAL_HIGHEST_SCORES]; static int32 BestPositions[TOTAL_BEST_POSITIONS]; static bool PropertyOwned[TOTAL_PROPERTIES]; static int32 NumPropertyOwned; static int32 PropertyDestroyed; static float HighestChaseValue; static int32 CheatedCount; static int32 ShowChaseStatOnScreen; static int32 PamphletMissionPassed; static bool abSonyCDs[1]; static int32 BloodRingKills; static int32 BloodRingTime; static float FavoriteRadioStationList[NUM_RADIOS]; static int32 Sprayings; static float AutoPaintingBudget; static int32 NoMoreHurricanes; static float FashionBudget; static float PropertyBudget; static float WeaponBudget; static int32 SafeHouseVisits; static int32 TyresPopped; static int32 LongestWheelie; static int32 LongestStoppie; static int32 Longest2Wheel; static float LongestWheelieDist; static float LongestStoppieDist; static float Longest2WheelDist; public: static void Init(void); static void RegisterFastestTime(int32, int32); static void RegisterHighestScore(int32, int32); static void RegisterBestPosition(int32, int32); static void AnotherLifeSavedWithAmbulance(); static void AnotherCriminalCaught(); static void RegisterLevelAmbulanceMission(int32); static void RegisterLevelVigilanteMission(int32); static void RegisterLevelFireMission(int32); static void AnotherFireExtinguished(); static wchar *FindCriminalRatingString(); static wchar *FindChaseString(float fMediaLevel); static void AnotherKillFrenzyPassed(); static void SetTotalNumberKillFrenzies(int32); static void SetTotalNumberMissions(int32); static void CheckPointReachedSuccessfully() { TotalLegitimateKills += KillsSinceLastCheckpoint; KillsSinceLastCheckpoint = 0; }; static void CheckPointReachedUnsuccessfully() { KillsSinceLastCheckpoint = 0; }; static int32 FindCriminalRatingNumber(); static void SaveStats(uint8 *buf, uint32 *size); static void LoadStats(uint8 *buf, uint32 size); static float GetPercentageProgress(); static void MoneySpentOnWeapons(int32); static void MoneySpentOnProperty(int32); static void MoneySpentOnAutoPainting(int32); static void MoneySpentOnFashion(int32); static void NumOfVisitsFromLoanSharks(int32); static void NumOfStoresKnockedOff(int32); static void NumOfMovieStunts(int32); static void NumOfAssassinations(int32); static void NumOfPizzasDelivered(int32); static void NumOfGarbagePickups(int32); static void NumOfIceCreamSold(int32); static void AddNumBloodRingKills(int32); static void LongestTimeInBloodRing(int32); static void AddPropertyAsOwned(int32); static void PopulateFavoriteRadioStationList(); static float GetFavoriteRadioStationList(int32); static void BuildStatLine(Const char *, void *, int, void *, int); static int ConstructStatLine(int); }; ================================================ FILE: src/core/Streaming.cpp ================================================ #include "common.h" #include "General.h" #include "Pad.h" #include "Hud.h" #include "Text.h" #include "Clock.h" #include "Renderer.h" #include "ModelInfo.h" #include "TxdStore.h" #include "ModelIndices.h" #include "Pools.h" #include "Wanted.h" #include "Directory.h" #include "RwHelper.h" #include "World.h" #include "Entity.h" #include "FileMgr.h" #include "FileLoader.h" #include "Zones.h" #include "Radar.h" #include "Camera.h" #include "Record.h" #include "CarCtrl.h" #include "Population.h" #include "Gangs.h" #include "CutsceneMgr.h" #include "CdStream.h" #include "Streaming.h" #include "Replay.h" #include "main.h" #include "ColStore.h" #include "DMAudio.h" #include "Script.h" #include "MemoryMgr.h" #include "MemoryHeap.h" #include "Font.h" #include "Frontend.h" #include "VarConsole.h" bool CStreaming::ms_disableStreaming; bool CStreaming::ms_bLoadingBigModel; int32 CStreaming::ms_numModelsRequested; CStreamingInfo CStreaming::ms_aInfoForModel[NUMSTREAMINFO]; CStreamingInfo CStreaming::ms_startLoadedList; CStreamingInfo CStreaming::ms_endLoadedList; CStreamingInfo CStreaming::ms_startRequestedList; CStreamingInfo CStreaming::ms_endRequestedList; int32 CStreaming::ms_oldSectorX; int32 CStreaming::ms_oldSectorY; int32 CStreaming::ms_streamingBufferSize; #ifndef ONE_THREAD_PER_CHANNEL int8 *CStreaming::ms_pStreamingBuffer[2]; #else int8 *CStreaming::ms_pStreamingBuffer[4]; #endif size_t CStreaming::ms_memoryUsed; CStreamingChannel CStreaming::ms_channel[2]; int32 CStreaming::ms_channelError; int32 CStreaming::ms_numVehiclesLoaded; int32 CStreaming::ms_numPedsLoaded; int32 CStreaming::ms_vehiclesLoaded[MAXVEHICLESLOADED]; int32 CStreaming::ms_lastVehicleDeleted; bool CStreaming::ms_bIsPedFromPedGroupLoaded[NUMMODELSPERPEDGROUP]; CDirectory *CStreaming::ms_pExtraObjectsDir; int32 CStreaming::ms_numPriorityRequests; int32 CStreaming::ms_currentPedGrp; int32 CStreaming::ms_currentPedLoading; int32 CStreaming::ms_lastCullZone; uint16 CStreaming::ms_loadedGangs; uint16 CStreaming::ms_loadedGangCars; int32 CStreaming::ms_imageOffsets[NUMCDIMAGES]; int32 CStreaming::ms_lastImageRead; int32 CStreaming::ms_imageSize; size_t CStreaming::ms_memoryAvailable; int32 desiredNumVehiclesLoaded = 12; CEntity *pIslandLODmainlandEntity; CEntity *pIslandLODbeachEntity; int32 islandLODmainland; int32 islandLODbeach; #ifndef MASTER bool gbPrintStats; bool gbPrintVehiclesInMemory; // TODO bool gbPrintStreamingBuffer; // TODO #endif bool CStreamingInfo::GetCdPosnAndSize(uint32 &posn, uint32 &size) { if(m_size == 0) return false; posn = m_position; size = m_size; return true; } void CStreamingInfo::SetCdPosnAndSize(uint32 posn, uint32 size) { m_position = posn; m_size = size; } void CStreamingInfo::AddToList(CStreamingInfo *link) { // Insert this after link m_next = link->m_next; m_prev = link; link->m_next = this; m_next->m_prev = this; } void CStreamingInfo::RemoveFromList(void) { m_next->m_prev = m_prev; m_prev->m_next = m_next; m_next = nil; m_prev = nil; } void CStreaming::Init2(void) { int i; for(i = 0; i < NUMSTREAMINFO; i++){ ms_aInfoForModel[i].m_loadState = STREAMSTATE_NOTLOADED; ms_aInfoForModel[i].m_next = nil; ms_aInfoForModel[i].m_prev = nil; ms_aInfoForModel[i].m_nextID = -1; ms_aInfoForModel[i].m_size = 0; ms_aInfoForModel[i].m_position = 0; } ms_channelError = -1; // init lists ms_startLoadedList.m_next = &ms_endLoadedList; ms_startLoadedList.m_prev = nil; ms_endLoadedList.m_prev = &ms_startLoadedList; ms_endLoadedList.m_next = nil; ms_startRequestedList.m_next = &ms_endRequestedList; ms_startRequestedList.m_prev = nil; ms_endRequestedList.m_prev = &ms_startRequestedList; ms_endRequestedList.m_next = nil; // init misc ms_oldSectorX = 0; ms_oldSectorY = 0; ms_streamingBufferSize = 0; ms_disableStreaming = false; ms_memoryUsed = 0; ms_bLoadingBigModel = false; // init channels ms_channel[0].state = CHANNELSTATE_IDLE; ms_channel[1].state = CHANNELSTATE_IDLE; for(i = 0; i < 4; i++){ ms_channel[0].streamIds[i] = -1; ms_channel[0].offsets[i] = -1; ms_channel[1].streamIds[i] = -1; ms_channel[1].offsets[i] = -1; } // init stream info, mark things that are already loaded for(i = 0; i < MODELINFOSIZE; i++){ CBaseModelInfo *mi = CModelInfo::GetModelInfo(i); if(mi && mi->GetRwObject()){ ms_aInfoForModel[i].m_loadState = STREAMSTATE_LOADED; ms_aInfoForModel[i].m_flags = STREAMFLAGS_DONT_REMOVE; if(mi->IsSimple()) ((CSimpleModelInfo*)mi)->m_alpha = 255; } } for(i = 0; i < TXDSTORESIZE; i++) if(CTxdStore::GetSlot(i) && CTxdStore::GetSlot(i)->texDict) ms_aInfoForModel[i + STREAM_OFFSET_TXD].m_loadState = STREAMSTATE_LOADED; for(i = 0; i < MAXVEHICLESLOADED; i++) ms_vehiclesLoaded[i] = -1; ms_numVehiclesLoaded = 0; ms_numPedsLoaded = 8; for(i = 0; i < ARRAY_SIZE(ms_bIsPedFromPedGroupLoaded); i++) ms_bIsPedFromPedGroupLoaded[i] = false; ms_pExtraObjectsDir = new CDirectory(EXTRADIRSIZE); ms_numPriorityRequests = 0; ms_currentPedGrp = -1; ms_lastCullZone = -1; // unused because RemoveModelsNotVisibleFromCullzone is gone ms_loadedGangs = 0; ms_currentPedLoading = NUMMODELSPERPEDGROUP; // unused, whatever it is LoadCdDirectory(); // allocate streaming buffers if(ms_streamingBufferSize & 1) ms_streamingBufferSize++; #ifndef ONE_THREAD_PER_CHANNEL ms_pStreamingBuffer[0] = (int8*)RwMallocAlign(ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE); ms_streamingBufferSize /= 2; ms_pStreamingBuffer[1] = ms_pStreamingBuffer[0] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; #else ms_pStreamingBuffer[0] = (int8*)RwMallocAlign(ms_streamingBufferSize*2*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE); ms_streamingBufferSize /= 2; ms_pStreamingBuffer[1] = ms_pStreamingBuffer[0] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; ms_pStreamingBuffer[2] = ms_pStreamingBuffer[1] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; ms_pStreamingBuffer[3] = ms_pStreamingBuffer[2] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; #endif debug("Streaming buffer size is %d sectors", ms_streamingBufferSize); // PC only, figure out how much memory we got #ifdef GTA_PC #define MB (1024*1024) #ifdef FIX_BUGS // do what gta3 does extern size_t _dwMemAvailPhys; ms_memoryAvailable = (_dwMemAvailPhys - 10*MB)/2; if(ms_memoryAvailable < 65*MB) ms_memoryAvailable = 65*MB; desiredNumVehiclesLoaded = (int32)((ms_memoryAvailable / MB - 65) / 3 + 12); if(desiredNumVehiclesLoaded > MAXVEHICLESLOADED) desiredNumVehiclesLoaded = MAXVEHICLESLOADED; #else ms_memoryAvailable = 65 * MB; desiredNumVehiclesLoaded = 25; debug("Memory allocated to Streaming is %zuMB", ms_memoryAvailable/MB); // original modifier was %d #endif #undef MB #endif // find island LODs pIslandLODmainlandEntity = nil; pIslandLODbeachEntity = nil; islandLODmainland = -1; islandLODbeach = -1; CModelInfo::GetModelInfo("IslandLODmainland", &islandLODmainland); CModelInfo::GetModelInfo("IslandLODbeach", &islandLODbeach); #ifndef MASTER VarConsole.Add("Streaming Debug", &gbPrintStats, true); VarConsole.Add("Streaming Vehicle Debug", &gbPrintVehiclesInMemory, true); VarConsole.Add("Printf Streaming Buffer contents", &gbPrintStreamingBuffer, true); #endif } void CStreaming::Init(void) { #ifdef USE_TXD_CDIMAGE if(!CanVideoCardDoDXT()){ int txdHandle = CFileMgr::OpenFile("MODELS\\TXD.IMG", "r"); if (txdHandle) CFileMgr::CloseFile(txdHandle); if (!CheckVideoCardCaps() && txdHandle) { CdStreamAddImage("MODELS\\TXD.IMG"); CStreaming::Init2(); } else { CStreaming::Init2(); if (CreateTxdImageForVideoCard()) { CStreaming::Shutdown(); CdStreamAddImage("MODELS\\TXD.IMG"); CStreaming::Init2(); } } } else CStreaming::Init2(); #else CStreaming::Init2(); #endif } void CStreaming::ReInit(void) { int i; CStreaming::FlushRequestList(); CStreaming::DeleteAllRwObjects(); CStreaming::RemoveAllUnusedModels(); for(i = 0; i < MODELINFOSIZE; i++) if(CModelInfo::GetModelInfo(i) && ms_aInfoForModel[i].m_flags & STREAMFLAGS_SCRIPTOWNED) SetMissionDoesntRequireModel(i); CStreaming::ms_disableStreaming = false; } void CStreaming::Shutdown(void) { RwFreeAlign(ms_pStreamingBuffer[0]); ms_streamingBufferSize = 0; if(ms_pExtraObjectsDir) { delete ms_pExtraObjectsDir; #ifdef FIX_BUGS ms_pExtraObjectsDir = nil; #endif } } #ifndef MASTER uint64 timeProcessingTXD; uint64 timeProcessingDFF; #endif void CStreaming::Update(void) { CStreamingInfo *si, *prev; bool requestedSubway = false; #ifndef MASTER timeProcessingTXD = 0; timeProcessingDFF = 0; #endif UpdateMemoryUsed(); if(ms_channelError != -1){ RetryLoadFile(ms_channelError); return; } if(CTimer::GetIsPaused()) return; LoadBigBuildingsWhenNeeded(); if(!ms_disableStreaming && TheCamera.GetPosition().z < 55.0f) AddModelsToRequestList(TheCamera.GetPosition(), 0); DeleteFarAwayRwObjects(TheCamera.GetPosition()); if(!ms_disableStreaming && !CCutsceneMgr::IsCutsceneProcessing() && ms_numModelsRequested < 5 && !CRenderer::m_loadingPriority && CGame::currArea == AREA_MAIN_MAP && !CReplay::IsPlayingBack()){ StreamVehiclesAndPeds(); StreamZoneModels(FindPlayerCoors()); } LoadRequestedModels(); if(CWorld::Players[0].m_pRemoteVehicle){ CColStore::AddCollisionNeededAtPosn(FindPlayerCoors()); CColStore::LoadCollision(CWorld::Players[0].m_pRemoteVehicle->GetPosition()); CColStore::EnsureCollisionIsInMemory(CWorld::Players[0].m_pRemoteVehicle->GetPosition()); }else{ CColStore::LoadCollision(FindPlayerCoors()); CColStore::EnsureCollisionIsInMemory(FindPlayerCoors()); } // TODO: PrintRequestList //if (CPad::GetPad(1)->GetLeftShoulder2JustDown() && CPad::GetPad(1)->GetRightShoulder1() && CPad::GetPad(1)->GetRightShoulder2()) // PrintRequestList(); for(si = ms_endRequestedList.m_prev; si != &ms_startRequestedList; si = prev){ prev = si->m_prev; if((si->m_flags & (STREAMFLAGS_KEEP_IN_MEMORY|STREAMFLAGS_PRIORITY)) == 0) RemoveModel(si - ms_aInfoForModel); } } void CStreaming::LoadCdDirectory(void) { char dirname[132]; int i; #ifdef GTA_PC ms_imageOffsets[0] = 0; ms_imageOffsets[1] = -1; ms_imageOffsets[2] = -1; ms_imageOffsets[3] = -1; ms_imageOffsets[4] = -1; ms_imageOffsets[5] = -1; ms_imageSize = GetGTA3ImgSize(); // PS2 uses CFileMgr::GetCdFile on all IMG files to fill the array #endif i = CdStreamGetNumImages(); while(i-- >= 1){ strcpy(dirname, CdStreamGetImageName(i)); strncpy(strrchr(dirname, '.') + 1, "DIR", 3); LoadCdDirectory(dirname, i); } ms_lastImageRead = 0; ms_imageSize /= CDSTREAM_SECTOR_SIZE; } void CStreaming::LoadCdDirectory(const char *dirname, int n) { int fd, lastID, imgSelector; int modelId; CDirectory::DirectoryInfo direntry; char *dot; lastID = -1; fd = CFileMgr::OpenFile(dirname, "rb"); assert(fd > 0); imgSelector = n<<24; assert(sizeof(direntry) == 32); while(CFileMgr::Read(fd, (char*)&direntry, sizeof(direntry))){ bool bAddToStreaming = false; if(direntry.size > (uint32)ms_streamingBufferSize) ms_streamingBufferSize = direntry.size; direntry.name[23] = '\0'; dot = strchr(direntry.name, '.'); if(dot == nil || dot-direntry.name > 20){ debug("%s is too long\n", direntry.name); lastID = -1; continue; } *dot = '\0'; if(strncasecmp(dot+1, "DFF", 3) == 0){ if(CModelInfo::GetModelInfo(direntry.name, &modelId)){ bAddToStreaming = true; }else{ #ifdef FIX_BUGS // remember which cdimage this came from ms_pExtraObjectsDir->AddItem(direntry, n); #else ms_pExtraObjectsDir->AddItem(direntry); #endif lastID = -1; } }else if(strncasecmp(dot+1, "TXD", 3) == 0){ modelId = CTxdStore::FindTxdSlot(direntry.name); if(modelId == -1) modelId = CTxdStore::AddTxdSlot(direntry.name); modelId += STREAM_OFFSET_TXD; bAddToStreaming = true; }else if(strncasecmp(dot+1, "COL", 3) == 0){ modelId = CColStore::FindColSlot(direntry.name); if(modelId == -1) modelId = CColStore::AddColSlot(direntry.name); modelId += STREAM_OFFSET_COL; bAddToStreaming = true; }else if(strncasecmp(dot+1, "IFP", 3) == 0){ modelId = CAnimManager::RegisterAnimBlock(direntry.name); modelId += STREAM_OFFSET_ANIM; bAddToStreaming = true; }else{ *dot = '.'; lastID = -1; } if(bAddToStreaming){ if(ms_aInfoForModel[modelId].GetCdSize()){ debug("%s.%s appears more than once in %s\n", direntry.name, dot+1, dirname); lastID = -1; }else{ direntry.offset |= imgSelector; ms_aInfoForModel[modelId].SetCdPosnAndSize(direntry.offset, direntry.size); if(lastID != -1) ms_aInfoForModel[lastID].m_nextID = modelId; lastID = modelId; } } } CFileMgr::CloseFile(fd); } static char* GetObjectName(int streamId) { static char objname[32]; if(streamId < STREAM_OFFSET_TXD) sprintf(objname, "%s.dff", CModelInfo::GetModelInfo(streamId)->GetModelName()); else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL) sprintf(objname, "%s.txd", CTxdStore::GetTxdName(streamId-STREAM_OFFSET_TXD)); else if(streamId >= STREAM_OFFSET_COL && streamId < STREAM_OFFSET_ANIM) sprintf(objname, "%s.col", CColStore::GetColName(streamId-STREAM_OFFSET_COL)); else{ assert(streamId < NUMSTREAMINFO); sprintf(objname, "%s.ifp", CAnimManager::GetAnimationBlock(streamId-STREAM_OFFSET_ANIM)->name); } return objname; } #ifdef USE_CUSTOM_ALLOCATOR RpAtomic* RegisterAtomicMemPtrsCB(RpAtomic *atomic, void *data) { // empty because we expect models to be pre-instanced return atomic; } #endif bool CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) { RwMemory mem; RwStream *stream; int cdsize; uint32 startTime, endTime, timeDiff; CBaseModelInfo *mi; bool success; startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); cdsize = ms_aInfoForModel[streamId].GetCdSize(); mem.start = (uint8*)buf; mem.length = cdsize * CDSTREAM_SECTOR_SIZE; stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); if(streamId < STREAM_OFFSET_TXD){ // Model mi = CModelInfo::GetModelInfo(streamId); // Txd and anim have to be loaded int animId = mi->GetAnimFileIndex(); #ifdef FIX_BUGS if(!HasTxdLoaded(mi->GetTxdSlot()) || #else // texDict will exist even if only first part has loaded if(CTxdStore::GetSlot(mi->GetTxdSlot())->texDict == nil || #endif animId != -1 && !CAnimManager::GetAnimationBlock(animId)->isLoaded){ RemoveModel(streamId); ReRequestModel(streamId); RwStreamClose(stream, &mem); return false; } // Set Txd and anims to use CTxdStore::AddRef(mi->GetTxdSlot()); #if GTA_VERSION > GTAVC_PS2 if(animId != -1) CAnimManager::AddAnimBlockRef(animId); #endif PUSH_MEMID(MEMID_STREAM_MODELS); CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); if(mi->IsSimple()){ success = CFileLoader::LoadAtomicFile(stream, streamId); // TODO(MIAMI)? complain if file is not pre-instanced. we hardly are interested in that } else if (mi->GetModelType() == MITYPE_VEHICLE) { // load vehicles in two parts CModelInfo::GetModelInfo(streamId)->AddRef(); success = CFileLoader::StartLoadClumpFile(stream, streamId); if(success) ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_STARTED; }else{ success = CFileLoader::LoadClumpFile(stream, streamId); #ifdef USE_CUSTOM_ALLOCATOR if(success) RpClumpForAllAtomics((RpClump*)mi->GetRwObject(), RegisterAtomicMemPtrsCB, nil); #endif } POP_MEMID(); UpdateMemoryUsed(); // Txd and anims no longer needed unless we only read part of the file if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ CTxdStore::RemoveRefWithoutDelete(mi->GetTxdSlot()); #if GTA_VERSION > GTAVC_PS2 if(animId != -1) CAnimManager::RemoveAnimBlockRefWithoutDelete(animId); #endif } if(!success){ debug("Failed to load %s\n", CModelInfo::GetModelInfo(streamId)->GetModelName()); RemoveModel(streamId); ReRequestModel(streamId); RwStreamClose(stream, &mem); return false; } }else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL){ // Txd if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY) == 0 && !IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)){ RemoveModel(streamId); RwStreamClose(stream, &mem); return false; } PUSH_MEMID(MEMID_STREAM_TEXUTRES); if(ms_bLoadingBigModel || cdsize > 200){ success = CTxdStore::StartLoadTxd(streamId - STREAM_OFFSET_TXD, stream); if(success) ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_STARTED; }else success = CTxdStore::LoadTxd(streamId - STREAM_OFFSET_TXD, stream); POP_MEMID(); UpdateMemoryUsed(); if(!success){ debug("Failed to load %s.txd\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD)); RemoveModel(streamId); ReRequestModel(streamId); RwStreamClose(stream, &mem); return false; } }else if(streamId >= STREAM_OFFSET_COL && streamId < STREAM_OFFSET_ANIM){ PUSH_MEMID(MEMID_STREAM_COLLISION); bool success = CColStore::LoadCol(streamId-STREAM_OFFSET_COL, mem.start, mem.length); POP_MEMID(); if(!success){ debug("Failed to load %s.col\n", CColStore::GetColName(streamId - STREAM_OFFSET_COL)); RemoveModel(streamId); ReRequestModel(streamId); RwStreamClose(stream, &mem); return false; } }else if(streamId >= STREAM_OFFSET_ANIM){ assert(streamId < NUMSTREAMINFO); if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY) == 0 && !AreAnimsUsedByRequestedModels(streamId - STREAM_OFFSET_ANIM)){ RemoveModel(streamId); RwStreamClose(stream, &mem); return false; } PUSH_MEMID(MEMID_STREAM_ANIMATION); CAnimManager::LoadAnimFile(stream, true, nil); CAnimManager::CreateAnimAssocGroups(); POP_MEMID(); } RwStreamClose(stream, &mem); if(streamId < STREAM_OFFSET_TXD){ // Model // Vehicles and Peds not in loaded list if (mi->GetModelType() != MITYPE_VEHICLE && mi->GetModelType() != MITYPE_PED) { CSimpleModelInfo *smi = (CSimpleModelInfo*)mi; // Set fading for some objects if(mi->IsSimple() && !smi->m_isBigBuilding){ if(ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_NOFADE) smi->m_alpha = 255; else smi->m_alpha = 0; } if(CanRemoveModel(streamId)) ms_aInfoForModel[streamId].AddToList(&ms_startLoadedList); } }else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL || streamId >= STREAM_OFFSET_ANIM){ assert(streamId < NUMSTREAMINFO); // Txd and anims if(CanRemoveModel(streamId)) ms_aInfoForModel[streamId].AddToList(&ms_startLoadedList); } // Mark objects as loaded if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; #ifndef USE_CUSTOM_ALLOCATOR ms_memoryUsed += ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; #endif } endTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); timeDiff = endTime - startTime; if(timeDiff > 5) debug("%s took %d ms\n", GetObjectName(streamId), timeDiff); return true; } bool CStreaming::FinishLoadingLargeFile(int8 *buf, int32 streamId) { RwMemory mem; RwStream *stream; uint32 startTime, endTime, timeDiff; CBaseModelInfo *mi; bool success; startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ if(streamId < STREAM_OFFSET_TXD) CModelInfo::GetModelInfo(streamId)->RemoveRef(); return false; } mem.start = (uint8*)buf; mem.length = ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); if(streamId < STREAM_OFFSET_TXD){ // Model mi = CModelInfo::GetModelInfo(streamId); PUSH_MEMID(MEMID_STREAM_MODELS); CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); success = CFileLoader::FinishLoadClumpFile(stream, streamId); if(success){ #ifdef USE_CUSTOM_ALLOCATOR RpClumpForAllAtomics((RpClump*)mi->GetRwObject(), RegisterAtomicMemPtrsCB, nil); #endif success = AddToLoadedVehiclesList(streamId); } POP_MEMID(); mi->RemoveRef(); CTxdStore::RemoveRefWithoutDelete(mi->GetTxdSlot()); #if GTA_VERSION > GTAVC_PS2 if(mi->GetAnimFileIndex() != -1) CAnimManager::RemoveAnimBlockRefWithoutDelete(mi->GetAnimFileIndex()); #endif }else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL){ // Txd CTxdStore::AddRef(streamId - STREAM_OFFSET_TXD); PUSH_MEMID(MEMID_STREAM_TEXUTRES); success = CTxdStore::FinishLoadTxd(streamId - STREAM_OFFSET_TXD, stream); POP_MEMID(); CTxdStore::RemoveRefWithoutDelete(streamId - STREAM_OFFSET_TXD); }else{ assert(0 && "invalid streamId"); } RwStreamClose(stream, &mem); ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; #ifndef USE_CUSTOM_ALLOCATOR ms_memoryUsed += ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; #endif if(!success){ RemoveModel(streamId); ReRequestModel(streamId); UpdateMemoryUsed(); return false; } UpdateMemoryUsed(); endTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); timeDiff = endTime - startTime; if(timeDiff > 5) debug("%s took %d ms\n", GetObjectName(streamId), timeDiff); return true; } void CStreaming::RequestModel(int32 id, int32 flags) { CSimpleModelInfo *mi; if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){ // updgrade to priority if(flags & STREAMFLAGS_PRIORITY && !ms_aInfoForModel[id].IsPriority()){ ms_numPriorityRequests++; ms_aInfoForModel[id].m_flags |= STREAMFLAGS_PRIORITY; } }else if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_NOTLOADED){ flags &= ~STREAMFLAGS_PRIORITY; } ms_aInfoForModel[id].m_flags |= flags; if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ // Already loaded, only check changed flags if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOFADE && id < STREAM_OFFSET_TXD){ mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); if(mi->IsSimple()) mi->m_alpha = 255; } // reinsert into list if(ms_aInfoForModel[id].m_next){ ms_aInfoForModel[id].RemoveFromList(); if(CanRemoveModel(id)) ms_aInfoForModel[id].AddToList(&ms_startLoadedList); } }else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED || ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ // how can this be true again? if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED){ if(id < STREAM_OFFSET_TXD){ mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); RequestTxd(mi->GetTxdSlot(), flags); int anim = mi->GetAnimFileIndex(); if(anim != -1) RequestAnim(anim, STREAMFLAGS_DEPENDENCY); } ms_aInfoForModel[id].AddToList(&ms_startRequestedList); ms_numModelsRequested++; if(flags & STREAMFLAGS_PRIORITY) ms_numPriorityRequests++; } ms_aInfoForModel[id].m_loadState = STREAMSTATE_INQUEUE; ms_aInfoForModel[id].m_flags = flags; } } #define BIGBUILDINGFLAGS STREAMFLAGS_DONT_REMOVE void CStreaming::RequestBigBuildings(eLevelName level) { int i, n; CBuilding *b; n = CPools::GetBuildingPool()->GetSize()-1; for(i = n; i >= 0; i--){ b = CPools::GetBuildingPool()->GetSlot(i); if(b && b->bIsBIGBuilding #ifdef NO_ISLAND_LOADING && (((FrontEndMenuManager.m_PrefsIslandLoading != CMenuManager::ISLAND_LOADING_LOW) && (b != pIslandLODmainlandEntity) && (b != pIslandLODbeachEntity)) || (b->m_level == level)) #else && b->m_level == level #endif ) if(!b->bStreamBIGBuilding) RequestModel(b->GetModelIndex(), BIGBUILDINGFLAGS); } RequestIslands(level); } void CStreaming::RequestBigBuildings(eLevelName level, const CVector &pos) { int i, n; CBuilding *b; n = CPools::GetBuildingPool()->GetSize()-1; for(i = n; i >= 0; i--){ b = CPools::GetBuildingPool()->GetSlot(i); if(b && b->bIsBIGBuilding #ifdef NO_ISLAND_LOADING && (((FrontEndMenuManager.m_PrefsIslandLoading != CMenuManager::ISLAND_LOADING_LOW) && (b != pIslandLODmainlandEntity) && (b != pIslandLODbeachEntity) ) || (b->m_level == level)) #else && b->m_level == level #endif ) if(b->bStreamBIGBuilding){ if(CRenderer::ShouldModelBeStreamed(b, pos)) RequestModel(b->GetModelIndex(), 0); }else RequestModel(b->GetModelIndex(), BIGBUILDINGFLAGS); } RequestIslands(level); } void CStreaming::InstanceBigBuildings(eLevelName level, const CVector &pos) { int i, n; CBuilding *b; n = CPools::GetBuildingPool()->GetSize()-1; for(i = n; i >= 0; i--){ b = CPools::GetBuildingPool()->GetSlot(i); if(b && b->bIsBIGBuilding && b->m_level == level && b->bStreamBIGBuilding && b->m_rwObject == nil) if(CRenderer::ShouldModelBeStreamed(b, pos)) b->CreateRwObject(); } } void CStreaming::InstanceLoadedModelsInSectorList(CPtrList &list) { CPtrNode *node; CEntity *e; for(node = list.first; node; node = node->next) { e = (CEntity *)node->item; if(IsAreaVisible(e->m_area) && e->m_rwObject == nil) e->CreateRwObject(); } } void CStreaming::InstanceLoadedModels(const CVector &pos) { int minX = CWorld::GetSectorIndexX(pos.x - 80.0f); if(minX <= 0) minX = 0; int minY = CWorld::GetSectorIndexY(pos.y - 80.0f); if(minY <= 0) minY = 0; int maxX = CWorld::GetSectorIndexX(pos.x + 80.0f); if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; int maxY = CWorld::GetSectorIndexY(pos.y + 80.0f); if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; int x, y; for(y = minY; y <= maxY; y++){ for(x = minX; x <= maxX; x++){ CSector *sector = CWorld::GetSector(x, y); InstanceLoadedModelsInSectorList(sector->m_lists[ENTITYLIST_BUILDINGS]); InstanceLoadedModelsInSectorList(sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); InstanceLoadedModelsInSectorList(sector->m_lists[ENTITYLIST_OBJECTS]); InstanceLoadedModelsInSectorList(sector->m_lists[ENTITYLIST_DUMMIES]); } } } void CStreaming::RequestIslands(eLevelName level) { ISLAND_LOADING_ISNT(HIGH) switch(level){ case LEVEL_MAINLAND: if(islandLODbeach != -1) RequestModel(islandLODbeach, BIGBUILDINGFLAGS); break; case LEVEL_BEACH: if(islandLODmainland != -1) RequestModel(islandLODmainland, BIGBUILDINGFLAGS); break; default: break; } } static char *IGnames[] = { "player", "player2", "player3", "player4", "player5", "player6", "player7", "player8", "player9", "play10", "play11", "igken", "igcandy", "igsonny", "igbuddy", "igjezz", "ighlary", "igphil", "igmerc", "igdick", "igdiaz", "" }; static char *CSnames[] = { "csplay", "csplay2", "csplay3", "csplay4", "csplay5", "csplay6", "csplay7", "csplay8", "csplay9", "csplay10", "csplay11", "csken", "cscandy", "cssonny", "csbuddy", "csjezz", "cshlary", "csphil", "csmerc", "csdick", "csdiaz", "" }; void CStreaming::RequestSpecialModel(int32 modelId, const char *modelName, int32 flags) { CBaseModelInfo *mi; int txdId; char oldName[48]; uint32 pos, size; int i, n; mi = CModelInfo::GetModelInfo(modelId); if(strncasecmp("CSPlay", modelName, 6) == 0){ char *curname = CModelInfo::GetModelInfo(MI_PLAYER)->GetModelName(); for(int i = 0; CSnames[i][0]; i++){ if(strcasecmp(curname, IGnames[i]) == 0){ modelName = CSnames[i]; break; } } } if(!CGeneral::faststrcmp(mi->GetModelName(), modelName)){ // Already have the correct name, just request it RequestModel(modelId, flags); return; } if(mi->GetNumRefs() > 0){ n = CPools::GetPedPool()->GetSize()-1; for(i = n; i >= 0 && mi->GetNumRefs() > 0; i--){ CPed *ped = CPools::GetPedPool()->GetSlot(i); if(ped && ped->GetModelIndex() == modelId && !ped->IsPlayer() && ped->CanBeDeletedEvenInVehicle()) CTheScripts::RemoveThisPed(ped); } n = CPools::GetObjectPool()->GetSize()-1; for(i = n; i >= 0 && mi->GetNumRefs() > 0; i--){ CObject *obj = CPools::GetObjectPool()->GetSlot(i); if(obj && obj->GetModelIndex() == modelId && obj->CanBeDeleted()){ CWorld::Remove(obj); CWorld::RemoveReferencesToDeletedObject(obj); delete obj; } } } strcpy(oldName, mi->GetModelName()); mi->SetModelName(modelName); // What exactly is going on here? if(CModelInfo::GetModelInfo(oldName, nil)){ txdId = CTxdStore::FindTxdSlot(oldName); if(txdId != -1 && CTxdStore::GetSlot(txdId)->texDict){ CTxdStore::AddRef(txdId); RemoveModel(modelId); CTxdStore::RemoveRefWithoutDelete(txdId); }else RemoveModel(modelId); }else RemoveModel(modelId); bool found = ms_pExtraObjectsDir->FindItem(modelName, pos, size); assert(found); mi->ClearTexDictionary(); if(CTxdStore::FindTxdSlot(modelName) == -1) mi->SetTexDictionary("generic"); else mi->SetTexDictionary(modelName); ms_aInfoForModel[modelId].SetCdPosnAndSize(pos, size); RequestModel(modelId, flags); } void CStreaming::RequestSpecialChar(int32 charId, const char *modelName, int32 flags) { RequestSpecialModel(charId + MI_SPECIAL01, modelName, flags); } bool CStreaming::HasSpecialCharLoaded(int32 id) { return HasModelLoaded(id + MI_SPECIAL01); } void CStreaming::SetMissionDoesntRequireSpecialChar(int32 id) { return SetMissionDoesntRequireModel(id + MI_SPECIAL01); } void CStreaming::DecrementRef(int32 id) { ms_numModelsRequested--; if(ms_aInfoForModel[id].IsPriority()){ ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_PRIORITY; ms_numPriorityRequests--; } } void CStreaming::RemoveModel(int32 id) { int i; if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED) return; if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ if(id < STREAM_OFFSET_TXD) CModelInfo::GetModelInfo(id)->DeleteRwObject(); else if(id >= STREAM_OFFSET_TXD && id < STREAM_OFFSET_COL) CTxdStore::RemoveTxd(id - STREAM_OFFSET_TXD); else if(id >= STREAM_OFFSET_COL && id < STREAM_OFFSET_ANIM) CColStore::RemoveCol(id - STREAM_OFFSET_COL); else if(id >= STREAM_OFFSET_ANIM){ assert(id < NUMSTREAMINFO); CAnimManager::RemoveAnimBlock(id - STREAM_OFFSET_ANIM); } ms_memoryUsed -= ms_aInfoForModel[id].GetCdSize()*CDSTREAM_SECTOR_SIZE; } if(ms_aInfoForModel[id].m_next){ // Remove from list, model is neither loaded nor requested if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE) DecrementRef(id); ms_aInfoForModel[id].RemoveFromList(); }else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_READING){ for(i = 0; i < 4; i++){ if(ms_channel[0].streamIds[i] == id) ms_channel[0].streamIds[i] = -1; if(ms_channel[1].streamIds[i] == id) ms_channel[1].streamIds[i] = -1; } } if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_STARTED){ if(id < STREAM_OFFSET_TXD) RpClumpGtaCancelStream(); else if(id >= STREAM_OFFSET_TXD && id < STREAM_OFFSET_COL) CTxdStore::RemoveTxd(id - STREAM_OFFSET_TXD); else if(id >= STREAM_OFFSET_COL && id < STREAM_OFFSET_ANIM) CColStore::RemoveCol(id - STREAM_OFFSET_COL); else if(id >= STREAM_OFFSET_ANIM){ assert(id < NUMSTREAMINFO); CAnimManager::RemoveAnimBlock(id - STREAM_OFFSET_ANIM); } } ms_aInfoForModel[id].m_loadState = STREAMSTATE_NOTLOADED; } void CStreaming::RemoveUnusedBuildings(eLevelName level) { if(level != LEVEL_BEACH) RemoveBuildings(LEVEL_BEACH); if(level != LEVEL_MAINLAND) RemoveBuildings(LEVEL_MAINLAND); } void CStreaming::RemoveBuildings(eLevelName level) { int i, n; CEntity *e; CBaseModelInfo *mi; n = CPools::GetBuildingPool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetBuildingPool()->GetSlot(i); if(e && e->m_level == level){ mi = CModelInfo::GetModelInfo(e->GetModelIndex()); if(!e->bImBeingRendered){ e->DeleteRwObject(); if (mi->GetNumRefs() == 0) RemoveModel(e->GetModelIndex()); } } } n = CPools::GetTreadablePool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetTreadablePool()->GetSlot(i); if(e && e->m_level == level){ mi = CModelInfo::GetModelInfo(e->GetModelIndex()); if(!e->bImBeingRendered){ e->DeleteRwObject(); if (mi->GetNumRefs() == 0) RemoveModel(e->GetModelIndex()); } } } n = CPools::GetObjectPool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetObjectPool()->GetSlot(i); if(e && e->m_level == level){ mi = CModelInfo::GetModelInfo(e->GetModelIndex()); if(!e->bImBeingRendered && ((CObject*)e)->ObjectCreatedBy == GAME_OBJECT){ e->DeleteRwObject(); if (mi->GetNumRefs() == 0) RemoveModel(e->GetModelIndex()); } } } n = CPools::GetDummyPool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetDummyPool()->GetSlot(i); if(e && e->m_level == level){ mi = CModelInfo::GetModelInfo(e->GetModelIndex()); if(!e->bImBeingRendered){ e->DeleteRwObject(); if (mi->GetNumRefs() == 0) RemoveModel(e->GetModelIndex()); } } } } void CStreaming::RemoveBuildingsNotInArea(int32 area) { int i, n; CEntity *e; n = CPools::GetBuildingPool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetBuildingPool()->GetSlot(i); if(e && e->m_rwObject && !IsAreaVisible(area) && (!e->bIsBIGBuilding || e->bStreamBIGBuilding)){ if(e->bIsBIGBuilding) RequestModel(e->GetModelIndex(), 0); if(!e->bImBeingRendered) e->DeleteRwObject(); } } n = CPools::GetTreadablePool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetTreadablePool()->GetSlot(i); if(e && e->m_rwObject && !IsAreaVisible(area) && (!e->bIsBIGBuilding || e->bStreamBIGBuilding)){ if(e->bIsBIGBuilding) RequestModel(e->GetModelIndex(), 0); if(!e->bImBeingRendered) e->DeleteRwObject(); } } n = CPools::GetObjectPool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetObjectPool()->GetSlot(i); if(e && e->m_rwObject && !IsAreaVisible(area) && (!e->bIsBIGBuilding || e->bStreamBIGBuilding)){ if(e->bIsBIGBuilding) RequestModel(e->GetModelIndex(), 0); if(!e->bImBeingRendered) e->DeleteRwObject(); } } n = CPools::GetDummyPool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetDummyPool()->GetSlot(i); if(e && e->m_rwObject && !IsAreaVisible(area) && (!e->bIsBIGBuilding || e->bStreamBIGBuilding)){ if(e->bIsBIGBuilding) RequestModel(e->GetModelIndex(), 0); if(!e->bImBeingRendered) e->DeleteRwObject(); } } } void CStreaming::RemoveUnusedBigBuildings(eLevelName level) { ISLAND_LOADING_IS(LOW) { if(level != LEVEL_BEACH) RemoveBigBuildings(LEVEL_BEACH); if(level != LEVEL_MAINLAND) RemoveBigBuildings(LEVEL_MAINLAND); } RemoveIslandsNotUsed(level); } void DeleteIsland(CEntity *island) { if(island == nil) return; if(island->bImBeingRendered) debug("Didn't delete island because it was being rendered\n"); else{ island->DeleteRwObject(); CStreaming::RemoveModel(island->GetModelIndex()); } } void CStreaming::RemoveIslandsNotUsed(eLevelName level) { int i; if(pIslandLODmainlandEntity == nil) for(i = CPools::GetBuildingPool()->GetSize()-1; i >= 0; i--){ CBuilding *building = CPools::GetBuildingPool()->GetSlot(i); if(building == nil) continue; if(building->GetModelIndex() == islandLODmainland) pIslandLODmainlandEntity = building; if(building->GetModelIndex() == islandLODbeach) pIslandLODbeachEntity = building; } #ifdef NO_ISLAND_LOADING if(FrontEndMenuManager.m_PrefsIslandLoading == CMenuManager::ISLAND_LOADING_HIGH) { DeleteIsland(pIslandLODmainlandEntity); DeleteIsland(pIslandLODbeachEntity); } else #endif switch(level){ case LEVEL_MAINLAND: DeleteIsland(pIslandLODmainlandEntity); break; case LEVEL_BEACH: DeleteIsland(pIslandLODbeachEntity); break; } } void CStreaming::RemoveBigBuildings(eLevelName level) { int i, n; CEntity *e; CBaseModelInfo *mi; n = CPools::GetBuildingPool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetBuildingPool()->GetSlot(i); if(e && e->bIsBIGBuilding && e->m_level == level){ mi = CModelInfo::GetModelInfo(e->GetModelIndex()); if(!e->bImBeingRendered){ e->DeleteRwObject(); if (mi->GetNumRefs() == 0) RemoveModel(e->GetModelIndex()); } } } } bool CStreaming::RemoveLoadedVehicle(void) { int i, id; for(i = 0; i < MAXVEHICLESLOADED; i++){ ms_lastVehicleDeleted++; if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) ms_lastVehicleDeleted = 0; id = ms_vehiclesLoaded[ms_lastVehicleDeleted]; if(id != -1 && CanRemoveModel(id) && CModelInfo::GetModelInfo(id)->GetNumRefs() == 0 && ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED) goto found; } return false; found: RemoveModel(ms_vehiclesLoaded[ms_lastVehicleDeleted]); ms_numVehiclesLoaded--; ms_vehiclesLoaded[ms_lastVehicleDeleted] = -1; CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); if (pVehicleInfo->m_vehicleClass != -1) CCarCtrl::RemoveFromLoadedVehicleArray(id, pVehicleInfo->m_vehicleClass); return true; } bool CStreaming::RemoveLeastUsedModel(uint32 excludeMask) { CStreamingInfo *si; int streamId; for(si = ms_endLoadedList.m_prev; si != &ms_startLoadedList; si = si->m_prev){ if(si->m_flags & excludeMask) continue; streamId = si - ms_aInfoForModel; if(streamId < STREAM_OFFSET_TXD){ if (CModelInfo::GetModelInfo(streamId)->GetNumRefs() == 0) { RemoveModel(streamId); return true; } }else if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL){ if(CTxdStore::GetNumRefs(streamId - STREAM_OFFSET_TXD) == 0 && !IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)){ RemoveModel(streamId); return true; } }else if(streamId >= STREAM_OFFSET_ANIM){ assert(streamId < NUMSTREAMINFO); if(CAnimManager::GetNumRefsToAnimBlock(streamId - STREAM_OFFSET_ANIM) == 0 && !AreAnimsUsedByRequestedModels(streamId - STREAM_OFFSET_ANIM)){ RemoveModel(streamId); return true; } } } return (ms_numVehiclesLoaded > 7 || CGame::currArea != AREA_MAIN_MAP && ms_numVehiclesLoaded > 4) && RemoveLoadedVehicle(); } void CStreaming::RemoveAllUnusedModels(void) { int i; for(i = 0; i < MAXVEHICLESLOADED; i++) RemoveLoadedVehicle(); for(i = NUM_DEFAULT_MODELS; i < MODELINFOSIZE; i++){ if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED && CModelInfo::GetModelInfo(i)->GetNumRefs() == 0) { RemoveModel(i); ms_aInfoForModel[i].m_loadState = STREAMSTATE_NOTLOADED; } } } void CStreaming::RemoveUnusedModelsInLoadedList(void) { // empty } bool CStreaming::RemoveLoadedZoneModel(void) { int i; if(ms_currentPedGrp == -1) return false; for(i = 0; i < NUMMODELSPERPEDGROUP; i++){ int mi = CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]; if(mi != -1 && ms_bIsPedFromPedGroupLoaded[i] && HasModelLoaded(mi) && CanRemoveModel(mi) && CModelInfo::GetModelInfo(mi)->GetNumRefs() == 0){ RemoveModel(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]); ms_numPedsLoaded--; ms_bIsPedFromPedGroupLoaded[i] = false; return true; } } return false; } bool CStreaming::IsTxdUsedByRequestedModels(int32 txdId) { CStreamingInfo *si; int streamId; int i; for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = si->m_next){ streamId = si - ms_aInfoForModel; if(streamId < STREAM_OFFSET_TXD && CModelInfo::GetModelInfo(streamId)->GetTxdSlot() == txdId) return true; } for(i = 0; i < 4; i++){ streamId = ms_channel[0].streamIds[i]; if(streamId != -1 && streamId < STREAM_OFFSET_TXD && CModelInfo::GetModelInfo(streamId)->GetTxdSlot() == txdId) return true; streamId = ms_channel[1].streamIds[i]; if(streamId != -1 && streamId < STREAM_OFFSET_TXD && CModelInfo::GetModelInfo(streamId)->GetTxdSlot() == txdId) return true; } return false; } bool CStreaming::AreAnimsUsedByRequestedModels(int32 animId) { CStreamingInfo *si; int streamId; int i; for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = si->m_next){ streamId = si - ms_aInfoForModel; if(streamId < STREAM_OFFSET_TXD && CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex() == animId) return true; } for(i = 0; i < 4; i++){ streamId = ms_channel[0].streamIds[i]; if(streamId != -1 && streamId < STREAM_OFFSET_TXD && CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex() == animId) return true; streamId = ms_channel[1].streamIds[i]; if(streamId != -1 && streamId < STREAM_OFFSET_TXD && CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex() == animId) return true; } return false; } int32 CStreaming::GetAvailableVehicleSlot(void) { int i; for(i = 0; i < MAXVEHICLESLOADED; i++) if(ms_vehiclesLoaded[i] == -1) return i; return -1; } bool CStreaming::AddToLoadedVehiclesList(int32 modelId) { int i; int id; if(ms_numVehiclesLoaded < desiredNumVehiclesLoaded){ // still room for vehicles for(i = 0; i < MAXVEHICLESLOADED; i++){ if(ms_vehiclesLoaded[ms_lastVehicleDeleted] == -1) break; ms_lastVehicleDeleted++; if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) ms_lastVehicleDeleted = 0; } assert(ms_vehiclesLoaded[ms_lastVehicleDeleted] == -1); ms_numVehiclesLoaded++; }else{ // find vehicle we can remove for(i = 0; i < MAXVEHICLESLOADED; i++){ id = ms_vehiclesLoaded[ms_lastVehicleDeleted]; if(id != -1 && CanRemoveModel(id) && CModelInfo::GetModelInfo(id)->GetNumRefs() == 0) goto found; ms_lastVehicleDeleted++; if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) ms_lastVehicleDeleted = 0; } id = -1; found: if(id == -1){ // didn't find anything, try a free slot id = GetAvailableVehicleSlot(); if(id == -1) return false; // still no luck ms_lastVehicleDeleted = id; // this is more than we wanted actually ms_numVehiclesLoaded++; } else{ RemoveModel(id); CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); if (pVehicleInfo->m_vehicleClass != -1) CCarCtrl::RemoveFromLoadedVehicleArray(id, pVehicleInfo->m_vehicleClass); } } ms_vehiclesLoaded[ms_lastVehicleDeleted++] = modelId; if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) ms_lastVehicleDeleted = 0; CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(modelId); if (pVehicleInfo->m_vehicleClass != -1) CCarCtrl::AddToLoadedVehicleArray(modelId, pVehicleInfo->m_vehicleClass, pVehicleInfo->m_frequency); return true; } bool CStreaming::IsObjectInCdImage(int32 id) { uint32 posn, size; return ms_aInfoForModel[id].GetCdPosnAndSize(posn, size); } void CStreaming::SetModelIsDeletable(int32 id) { ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_DONT_REMOVE; if ((id >= STREAM_OFFSET_TXD && id < STREAM_OFFSET_COL || CModelInfo::GetModelInfo(id)->GetModelType() != MITYPE_VEHICLE) && (ms_aInfoForModel[id].m_flags & STREAMFLAGS_SCRIPTOWNED) == 0){ if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_LOADED) RemoveModel(id); else if(ms_aInfoForModel[id].m_next == nil) ms_aInfoForModel[id].AddToList(&ms_startLoadedList); } } void CStreaming::SetModelTxdIsDeletable(int32 id) { SetModelIsDeletable(CModelInfo::GetModelInfo(id)->GetTxdSlot() + STREAM_OFFSET_TXD); } void CStreaming::SetMissionDoesntRequireModel(int32 id) { ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_SCRIPTOWNED; if ((id >= STREAM_OFFSET_TXD || CModelInfo::GetModelInfo(id)->GetModelType() != MITYPE_VEHICLE) && (ms_aInfoForModel[id].m_flags & STREAMFLAGS_DONT_REMOVE) == 0){ if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_LOADED) RemoveModel(id); else if(ms_aInfoForModel[id].m_next == nil) ms_aInfoForModel[id].AddToList(&ms_startLoadedList); } } void CStreaming::LoadInitialPeds(void) { RequestModel(MI_COP, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_MALE01, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_TAXI_D, STREAMFLAGS_DONT_REMOVE); } void CStreaming::LoadInitialWeapons(void) { CStreaming::RequestModel(MI_NIGHTSTICK, STREAMFLAGS_DONT_REMOVE); CStreaming::RequestModel(MI_MISSILE, STREAMFLAGS_DONT_REMOVE); } void CStreaming::LoadInitialVehicles(void) { ms_numVehiclesLoaded = 0; ms_lastVehicleDeleted = 0; RequestModel(MI_POLICE, STREAMFLAGS_DONT_REMOVE); } void CStreaming::StreamVehiclesAndPeds(void) { int i, model; static int timeBeforeNextLoad = 0; static int modelQualityClass = 0; if(CRecordDataForGame::IsRecording() || CRecordDataForGame::IsPlayingBack() #ifdef FIX_BUGS || CReplay::IsPlayingBack() #endif ) return; if(FindPlayerPed()->m_pWanted->AreSwatRequired()){ RequestModel(MI_ENFORCER, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_SWAT, STREAMFLAGS_DONT_REMOVE); }else{ SetModelIsDeletable(MI_ENFORCER); if(!HasModelLoaded(MI_ENFORCER)) SetModelIsDeletable(MI_SWAT); } if(FindPlayerPed()->m_pWanted->AreFbiRequired()){ RequestModel(MI_FBIRANCH, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_FBI, STREAMFLAGS_DONT_REMOVE); }else{ SetModelIsDeletable(MI_FBIRANCH); if(!HasModelLoaded(MI_FBIRANCH)) SetModelIsDeletable(MI_FBI); } if(FindPlayerPed()->m_pWanted->AreArmyRequired()){ RequestModel(MI_RHINO, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_BARRACKS, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_ARMY, STREAMFLAGS_DONT_REMOVE); }else{ SetModelIsDeletable(MI_RHINO); SetModelIsDeletable(MI_BARRACKS); if(!HasModelLoaded(MI_RHINO) && !HasModelLoaded(MI_BARRACKS)) SetModelIsDeletable(MI_ARMY); } if(FindPlayerPed()->m_pWanted->NumOfHelisRequired() > 0) RequestModel(MI_CHOPPER, STREAMFLAGS_DONT_REMOVE); else SetModelIsDeletable(MI_CHOPPER); if (FindPlayerPed()->m_pWanted->AreMiamiViceRequired()) { SetModelIsDeletable(MI_VICE1); SetModelIsDeletable(MI_VICE2); SetModelIsDeletable(MI_VICE3); SetModelIsDeletable(MI_VICE4); SetModelIsDeletable(MI_VICE5); SetModelIsDeletable(MI_VICE6); SetModelIsDeletable(MI_VICE7); SetModelIsDeletable(MI_VICE8); RequestModel(MI_VICECHEE, STREAMFLAGS_DONT_REMOVE); if(CPopulation::NumMiamiViceCops == 0) switch (CCarCtrl::MiamiViceCycle) { case 0: RequestModel(MI_VICE1, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_VICE2, STREAMFLAGS_DONT_REMOVE); break; case 1: RequestModel(MI_VICE3, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_VICE4, STREAMFLAGS_DONT_REMOVE); break; case 2: RequestModel(MI_VICE5, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_VICE6, STREAMFLAGS_DONT_REMOVE); break; case 3: RequestModel(MI_VICE7, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_VICE8, STREAMFLAGS_DONT_REMOVE); break; } } else { SetModelIsDeletable(MI_VICECHEE); SetModelIsDeletable(MI_VICE1); SetModelIsDeletable(MI_VICE2); SetModelIsDeletable(MI_VICE3); SetModelIsDeletable(MI_VICE4); SetModelIsDeletable(MI_VICE5); SetModelIsDeletable(MI_VICE6); SetModelIsDeletable(MI_VICE7); SetModelIsDeletable(MI_VICE8); } if(timeBeforeNextLoad >= 0) timeBeforeNextLoad--; else if(ms_numVehiclesLoaded <= desiredNumVehiclesLoaded){ CZoneInfo zone; CVector coors = FindPlayerCoors(); CTheZones::GetZoneInfoForTimeOfDay(&coors, &zone); int32 maxReq = -1; int32 mostRequestedRating = 0; for(i = 0; i < CCarCtrl::TOTAL_CUSTOM_CLASSES; i++){ if(CCarCtrl::NumRequestsOfCarRating[i] > maxReq && ((i == 0 && zone.carThreshold[0] != 0) || (i != 0 && zone.carThreshold[i] != zone.carThreshold[i-1]))) { maxReq = CCarCtrl::NumRequestsOfCarRating[i]; mostRequestedRating = i; } } model = CCarCtrl::ChooseCarModelToLoad(mostRequestedRating); if(!HasModelLoaded(model)){ RequestModel(model, STREAMFLAGS_DEPENDENCY); timeBeforeNextLoad = 350; } CCarCtrl::NumRequestsOfCarRating[mostRequestedRating] = 0; } } void CStreaming::StreamZoneModels(const CVector &pos) { int i, j; uint16 gangsToLoad, gangCarsToLoad, bit; CZoneInfo info; static int timeBeforeNextLoad = 0; CTheZones::GetZoneInfoForTimeOfDay(&pos, &info); if(info.pedGroup != ms_currentPedGrp){ // unload pevious group if(ms_currentPedGrp != -1) for(i = 0; i < NUMMODELSPERPEDGROUP; i++){ ms_bIsPedFromPedGroupLoaded[i] = false; if(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i] != -1){ SetModelIsDeletable(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]); SetModelTxdIsDeletable(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]); } } ms_currentPedGrp = info.pedGroup; for(i = 0; i < MAXZONEPEDSLOADED; i++){ do j = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); while(ms_bIsPedFromPedGroupLoaded[j]); ms_bIsPedFromPedGroupLoaded[j] = true; if(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[j] != -1) RequestModel(CPopulation::ms_pPedGroups[ms_currentPedGrp].models[j], STREAMFLAGS_DEPENDENCY); } ms_numPedsLoaded = MAXZONEPEDSLOADED; timeBeforeNextLoad = 300; } if(timeBeforeNextLoad >= 0) timeBeforeNextLoad--; else{ // Switch a ped int oldMI; // Find a ped to unload for(i = 0; i < NUMMODELSPERPEDGROUP; i++) if(ms_bIsPedFromPedGroupLoaded[i]){ oldMI = CPopulation::ms_pPedGroups[ms_currentPedGrp].models[i]; if(oldMI != -1 && CModelInfo::GetModelInfo(oldMI)->GetNumRefs() == 0) break; } // And load a new one if(i != NUMMODELSPERPEDGROUP || ms_numPedsLoaded < MAXZONEPEDSLOADED){ do j = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); while(ms_bIsPedFromPedGroupLoaded[j]); if(ms_numPedsLoaded == MAXZONEPEDSLOADED) ms_bIsPedFromPedGroupLoaded[i] = false; ms_bIsPedFromPedGroupLoaded[j] = true; int newMI = CPopulation::ms_pPedGroups[ms_currentPedGrp].models[j]; if(newMI != oldMI){ RequestModel(newMI, STREAMFLAGS_DEPENDENCY); debug("Request Ped %s\n", CModelInfo::GetModelInfo(newMI)->GetModelName()); if(ms_numPedsLoaded == MAXZONEPEDSLOADED){ SetModelIsDeletable(oldMI); SetModelTxdIsDeletable(oldMI); debug("Remove Ped %s\n", CModelInfo::GetModelInfo(oldMI)->GetModelName()); }else ms_numPedsLoaded++; timeBeforeNextLoad = 300; } } } RequestModel(MI_MALE01, STREAMFLAGS_DONT_REMOVE); RequestModel(MI_TAXI_D, STREAMFLAGS_DONT_REMOVE); gangsToLoad = 0; gangCarsToLoad = 0; if(info.gangPedThreshold[0] != info.copPedThreshold) gangsToLoad = 1; for(i = 1; i < NUM_GANGS; i++) if(info.gangPedThreshold[i] != info.gangPedThreshold[i-1]) gangsToLoad |= 1< offset + ms_imageSize){ // last read position is not in last image for(i = 0; i < NUMCDIMAGES; i++){ off = ms_imageOffsets[i]; if(off == -1) continue; if((uint32)lastPosn > (uint32)off) // after start of image, get distance from end // negative if before end! dist = lastPosn - (off + ms_imageSize); else // before image, get offset to start // this will never be negative dist = off - lastPosn; if(dist < mindist){ img = i; mindist = dist; } } assert(img >= 0); offset = ms_imageOffsets[img]; ms_lastImageRead = img; } return offset; } inline bool ModelNotLoaded(int32 modelId) { CStreamingInfo *si = &CStreaming::ms_aInfoForModel[modelId]; return si->m_loadState != STREAMSTATE_LOADED && si->m_loadState != STREAMSTATE_READING; } inline bool TxdNotLoaded(int32 txdId) { return ModelNotLoaded(txdId + STREAM_OFFSET_TXD); } inline bool AnimNotLoaded(int32 animId) { return animId != -1 && ModelNotLoaded(animId + STREAM_OFFSET_ANIM); } // Find stream id of next requested file in cdimage int32 CStreaming::GetNextFileOnCd(int32 lastPosn, bool priority) { CStreamingInfo *si, *next; int streamId; uint32 posn, size; int streamIdFirst, streamIdNext; uint32 posnFirst, posnNext; streamIdFirst = -1; streamIdNext = -1; posnFirst = UINT32_MAX; posnNext = UINT32_MAX; for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = next){ next = si->m_next; streamId = si - ms_aInfoForModel; // only priority requests if there are any if(priority && ms_numPriorityRequests != 0 && !si->IsPriority()) continue; // request Txds or anims if necessary if(streamId < STREAM_OFFSET_TXD){ int txdId = CModelInfo::GetModelInfo(streamId)->GetTxdSlot(); if(TxdNotLoaded(txdId)){ ReRequestTxd(txdId); continue; } int animId = CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex(); if(AnimNotLoaded(animId)){ ReRequestAnim(animId); continue; } }else if(streamId >= STREAM_OFFSET_ANIM && CCutsceneMgr::IsCutsceneProcessing()) continue; if(ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)){ if(posn < posnFirst){ // find first requested file in image streamIdFirst = streamId; posnFirst = posn; } if(posn < posnNext && posn >= (uint32)lastPosn){ // find first requested file after last read position streamIdNext = streamId; posnNext = posn; } }else{ // empty file DecrementRef(streamId); si->RemoveFromList(); si->m_loadState = STREAMSTATE_LOADED; } } // wrap around if(streamIdNext == -1) streamIdNext = streamIdFirst; if(streamIdNext == -1 && ms_numPriorityRequests != 0){ // try non-priority files ms_numPriorityRequests = 0; streamIdNext = GetNextFileOnCd(lastPosn, false); } return streamIdNext; } /* * Streaming buffer size is half of the largest file. * Files larger than the buffer size can only be loaded by channel 0, * which then uses both buffers, while channel 1 is idle. * ms_bLoadingBigModel is set to true to indicate this state. */ // Make channel read from disc void CStreaming::RequestModelStream(int32 ch) { int lastPosn, imgOffset, streamId; int totalSize; uint32 posn, size, unused; int i; int haveBigFile, havePed; lastPosn = CdStreamGetLastPosn(); imgOffset = GetCdImageOffset(lastPosn); streamId = GetNextFileOnCd(lastPosn - imgOffset, true); // remove Txds and Anims that aren't requested anymore while(streamId != -1){ if(ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY) break; if(streamId >= STREAM_OFFSET_TXD && streamId < STREAM_OFFSET_COL){ if(IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)) break; }else if(streamId >= STREAM_OFFSET_ANIM){ assert(streamId < NUMSTREAMINFO); if(AreAnimsUsedByRequestedModels(streamId - STREAM_OFFSET_ANIM)) break; }else break; RemoveModel(streamId); // so try next file ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size); streamId = GetNextFileOnCd(posn + size, true); } if(streamId == -1) return; ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size); if(size > (uint32)ms_streamingBufferSize){ // Can only load big models on channel 0, and 1 has to be idle if(ch == 1 || ms_channel[1].state != CHANNELSTATE_IDLE) return; ms_bLoadingBigModel = true; } // Load up to 4 adjacent files haveBigFile = 0; havePed = 0; totalSize = 0; for(i = 0; i < 4; i++){ // no more files we can read if(streamId == -1 || ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_INQUEUE) break; // also stop at non-priority files ms_aInfoForModel[streamId].GetCdPosnAndSize(unused, size); if(ms_numPriorityRequests != 0 && !ms_aInfoForModel[streamId].IsPriority()) break; // Can't load certain combinations of files together if(streamId < STREAM_OFFSET_TXD){ if (havePed && CModelInfo::GetModelInfo(streamId)->GetModelType() == MITYPE_PED || haveBigFile && CModelInfo::GetModelInfo(streamId)->GetModelType() == MITYPE_VEHICLE || TxdNotLoaded(CModelInfo::GetModelInfo(streamId)->GetTxdSlot()) || AnimNotLoaded(CModelInfo::GetModelInfo(streamId)->GetAnimFileIndex())) break; }else{ if(haveBigFile && size > 200) break; } // Now add the file ms_channel[ch].streamIds[i] = streamId; ms_channel[ch].offsets[i] = totalSize; totalSize += size; // To big for buffer, remove again if(totalSize > ms_streamingBufferSize && i > 0){ totalSize -= size; break; } if(streamId < STREAM_OFFSET_TXD){ if (CModelInfo::GetModelInfo(streamId)->GetModelType() == MITYPE_PED) havePed = 1; if (CModelInfo::GetModelInfo(streamId)->GetModelType() == MITYPE_VEHICLE) haveBigFile = 1; }else{ if(size > 200) haveBigFile = 1; } ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_READING; ms_aInfoForModel[streamId].RemoveFromList(); DecrementRef(streamId); streamId = ms_aInfoForModel[streamId].m_nextID; } // clear remaining slots for(; i < 4; i++) ms_channel[ch].streamIds[i] = -1; // Now read the data assert(!(ms_bLoadingBigModel && ch == 1)); // this would clobber the buffer if(CdStreamRead(ch, ms_pStreamingBuffer[ch], imgOffset+posn, totalSize) == STREAM_NONE) debug("FUCKFUCKFUCK\n"); ms_channel[ch].state = CHANNELSTATE_READING; ms_channel[ch].field24 = 0; ms_channel[ch].size = totalSize; ms_channel[ch].position = imgOffset+posn; ms_channel[ch].numTries = 0; } // Load data previously read from disc bool CStreaming::ProcessLoadingChannel(int32 ch) { int status; int i, id, cdsize; status = CdStreamGetStatus(ch); if(status != STREAM_NONE){ // busy if(status != STREAM_READING && status != STREAM_WAITING){ ms_channelError = ch; ms_channel[ch].state = CHANNELSTATE_ERROR; ms_channel[ch].status = status; } return false; } if(ms_channel[ch].state == CHANNELSTATE_STARTED){ ms_channel[ch].state = CHANNELSTATE_IDLE; FinishLoadingLargeFile(&ms_pStreamingBuffer[ch][ms_channel[ch].offsets[0]*CDSTREAM_SECTOR_SIZE], ms_channel[ch].streamIds[0]); ms_channel[ch].streamIds[0] = -1; }else{ ms_channel[ch].state = CHANNELSTATE_IDLE; for(i = 0; i < 4; i++){ id = ms_channel[ch].streamIds[i]; if(id == -1) continue; cdsize = ms_aInfoForModel[id].GetCdSize(); if(id < STREAM_OFFSET_TXD && CModelInfo::GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && ms_numVehiclesLoaded >= desiredNumVehiclesLoaded && !RemoveLoadedVehicle() && (CanRemoveModel(id) || GetAvailableVehicleSlot() == -1)){ // can't load vehicle RemoveModel(id); if(!CanRemoveModel(id)) ReRequestModel(id); else if(CTxdStore::GetNumRefs(CModelInfo::GetModelInfo(id)->GetTxdSlot()) == 0) RemoveTxd(CModelInfo::GetModelInfo(id)->GetTxdSlot()); }else{ MakeSpaceFor(cdsize * CDSTREAM_SECTOR_SIZE); ConvertBufferToObject(&ms_pStreamingBuffer[ch][ms_channel[ch].offsets[i]*CDSTREAM_SECTOR_SIZE], id); if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_STARTED){ // queue for second part ms_channel[ch].state = CHANNELSTATE_STARTED; ms_channel[ch].offsets[0] = ms_channel[ch].offsets[i]; ms_channel[ch].streamIds[0] = id; if(i != 0) ms_channel[ch].streamIds[i] = -1; }else ms_channel[ch].streamIds[i] = -1; } } } if(ms_bLoadingBigModel && ms_channel[ch].state != CHANNELSTATE_STARTED){ ms_bLoadingBigModel = false; // reset channel 1 after loading a big model for(i = 0; i < 4; i++) ms_channel[1].streamIds[i] = -1; ms_channel[1].state = CHANNELSTATE_IDLE; } return true; } void CStreaming::RetryLoadFile(int32 ch) { Const char *key; CPad::StopPadsShaking(); if(ms_channel[ch].numTries >= 3){ switch(ms_channel[ch].status){ case STREAM_ERROR_NOCD: key = "NOCD"; break; case STREAM_ERROR_OPENCD: key = "OPENCD"; break; case STREAM_ERROR_WRONGCD: key = "WRONGCD"; break; default: key = "CDERROR"; break; } CHud::SetMessage(TheText.Get(key)); CTimer::SetCodePause(true); } switch(ms_channel[ch].state){ case CHANNELSTATE_ERROR: ms_channel[ch].numTries++; if (CdStreamGetStatus(ch) == STREAM_READING || CdStreamGetStatus(ch) == STREAM_WAITING) break; case CHANNELSTATE_IDLE: CdStreamRead(ch, ms_pStreamingBuffer[ch], ms_channel[ch].position, ms_channel[ch].size); ms_channel[ch].state = CHANNELSTATE_READING; ms_channel[ch].field24 = -600; break; case CHANNELSTATE_READING: if(ProcessLoadingChannel(ch)){ ms_channelError = -1; CTimer::SetCodePause(false); } break; } } void CStreaming::LoadRequestedModels(void) { static int currentChannel = 0; // We can't read with channel 1 while channel 0 is using its buffer if(ms_bLoadingBigModel) currentChannel = 0; // We have data, load if(ms_channel[currentChannel].state == CHANNELSTATE_READING || ms_channel[currentChannel].state == CHANNELSTATE_STARTED) ProcessLoadingChannel(currentChannel); if(ms_channelError == -1){ // Channel is idle, read more data if(ms_channel[currentChannel].state == CHANNELSTATE_IDLE) RequestModelStream(currentChannel); // Switch channel if(ms_channel[currentChannel].state != CHANNELSTATE_STARTED) currentChannel = 1 - currentChannel; } } // Let's load models in 4 threads; when one of them becomes idle, process the file, and fill thread with another file. Unfortunately processing models are still single-threaded. // Currently only supported on POSIX streamer. // WIP - some files are loaded swapped (CdStreamPosix problem?) #if 0 //def ONE_THREAD_PER_CHANNEL void CStreaming::LoadAllRequestedModels(bool priority) { static bool bInsideLoadAll = false; int imgOffset, streamId, status; int i; uint32 posn, size; if(bInsideLoadAll) return; bInsideLoadAll = true; FlushChannels(); imgOffset = GetCdImageOffset(CdStreamGetLastPosn()); int streamIds[ARRAY_SIZE(ms_pStreamingBuffer)]; int streamSizes[ARRAY_SIZE(ms_pStreamingBuffer)]; int streamPoses[ARRAY_SIZE(ms_pStreamingBuffer)]; int readOrder[4] = {-1}; // Channel IDs ordered by read time int readI = 0; int processI = 0; bool first = true; // All those "first" checks are because of variables aren't initialized in first pass. while (true) { for (int i=0; i (uint32)ms_streamingBufferSize) { if (i + 1 == ARRAY_SIZE(ms_pStreamingBuffer)) break; else if (!first && streamIds[i+1] != -1) continue; } else { // Buffer of current channel is part of a "big file", pass if (i != 0 && streamIds[i-1] != -1 && streamSizes[i-1] > (uint32)ms_streamingBufferSize) continue; } ms_aInfoForModel[streamId].RemoveFromList(); DecrementRef(streamId); streamIds[i] = streamId; streamSizes[i] = size; streamPoses[i] = posn; if (!first) assert(readOrder[readI] == -1); //printf("read: order %d, ch %d, id %d, size %d\n", readI, i, streamId, size); CdStreamRead(i, ms_pStreamingBuffer[i], imgOffset+posn, size); readOrder[readI] = i; if (first && readI+1 != ARRAY_SIZE(readOrder)) readOrder[readI+1] = -1; readI = (readI + 1) % ARRAY_SIZE(readOrder); } else { ms_aInfoForModel[streamId].RemoveFromList(); DecrementRef(streamId); ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; streamIds[i] = -1; } } else { streamIds[i] = -1; break; } } first = false; int nextChannel = readOrder[processI]; // Now start processing if (nextChannel == -1 || streamIds[nextChannel] == -1) break; //printf("process: order %d, ch %d, id %d\n", processI, nextChannel, streamIds[nextChannel]); // Try again on error while (CdStreamSync(nextChannel) != STREAM_NONE) { CdStreamRead(nextChannel, ms_pStreamingBuffer[nextChannel], imgOffset+streamPoses[nextChannel], streamSizes[nextChannel]); } ms_aInfoForModel[streamIds[nextChannel]].m_loadState = STREAMSTATE_READING; MakeSpaceFor(streamSizes[nextChannel] * CDSTREAM_SECTOR_SIZE); ConvertBufferToObject(ms_pStreamingBuffer[nextChannel], streamIds[nextChannel]); if(ms_aInfoForModel[streamIds[nextChannel]].m_loadState == STREAMSTATE_STARTED) FinishLoadingLargeFile(ms_pStreamingBuffer[nextChannel], streamIds[nextChannel]); if(streamIds[nextChannel] < STREAM_OFFSET_TXD){ CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(streamIds[nextChannel]); if(mi->IsSimple()) mi->m_alpha = 255; } streamIds[nextChannel] = -1; readOrder[processI] = -1; processI = (processI + 1) % ARRAY_SIZE(readOrder); } ms_bLoadingBigModel = false; for(i = 0; i < 4; i++){ ms_channel[1].streamIds[i] = -1; ms_channel[1].offsets[i] = -1; } ms_channel[1].state = CHANNELSTATE_IDLE; bInsideLoadAll = false; } #else void CStreaming::LoadAllRequestedModels(bool priority) { static bool bInsideLoadAll = false; int imgOffset, streamId, status; int i; uint32 posn, size; int numRequests = 4*ms_numModelsRequested; if(bInsideLoadAll) return; bInsideLoadAll = true; if(priority) numRequests = ms_numPriorityRequests; FlushChannels(); imgOffset = GetCdImageOffset(CdStreamGetLastPosn()); while(ms_endRequestedList.m_prev != &ms_startRequestedList && numRequests > 0){ numRequests--; streamId = GetNextFileOnCd(0, priority); if(streamId == -1) break; ms_aInfoForModel[streamId].RemoveFromList(); ms_channel[0].streamIds[0] = streamId; DecrementRef(streamId); if(ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)){ do status = CdStreamRead(0, ms_pStreamingBuffer[0], imgOffset+posn, size); while(CdStreamSync(0) || status == STREAM_NONE); ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_READING; MakeSpaceFor(size * CDSTREAM_SECTOR_SIZE); ConvertBufferToObject(ms_pStreamingBuffer[0], streamId); if(ms_aInfoForModel[streamId].m_loadState == STREAMSTATE_STARTED) FinishLoadingLargeFile(ms_pStreamingBuffer[0], streamId); if(streamId < STREAM_OFFSET_TXD){ CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(streamId); if(mi->IsSimple()) mi->m_alpha = 255; } }else{ // empty ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; } } ms_bLoadingBigModel = false; for(i = 0; i < 4; i++){ ms_channel[1].streamIds[i] = -1; ms_channel[1].offsets[i] = -1; } ms_channel[1].state = CHANNELSTATE_IDLE; bInsideLoadAll = false; } #endif void CStreaming::FlushChannels(void) { if(ms_channel[1].state == CHANNELSTATE_STARTED) ProcessLoadingChannel(1); if(ms_channel[0].state == CHANNELSTATE_READING){ CdStreamSync(0); ProcessLoadingChannel(0); } if(ms_channel[0].state == CHANNELSTATE_STARTED) ProcessLoadingChannel(0); if(ms_channel[1].state == CHANNELSTATE_READING){ CdStreamSync(1); ProcessLoadingChannel(1); } if(ms_channel[1].state == CHANNELSTATE_STARTED) ProcessLoadingChannel(1); } void CStreaming::FlushRequestList(void) { CStreamingInfo *si, *next; for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = next){ next = si->m_next; RemoveModel(si - ms_aInfoForModel); } #ifdef FLUSHABLE_STREAMING if(ms_channel[0].state == CHANNELSTATE_READING) { flushStream[0] = 1; } if(ms_channel[1].state == CHANNELSTATE_READING) { flushStream[1] = 1; } #endif FlushChannels(); } void CStreaming::ImGonnaUseStreamingMemory(void) { PUSH_MEMID(MEMID_STREAM); } void CStreaming::IHaveUsedStreamingMemory(void) { POP_MEMID(); UpdateMemoryUsed(); } void CStreaming::UpdateMemoryUsed(void) { #ifdef USE_CUSTOM_ALLOCATOR ms_memoryUsed = gMainHeap.GetMemoryUsed(MEMID_STREAM) + gMainHeap.GetMemoryUsed(MEMID_STREAM_MODELS) + gMainHeap.GetMemoryUsed(MEMID_STREAM_TEXUTRES) + gMainHeap.GetMemoryUsed(MEMID_STREAM_COLLISION) + gMainHeap.GetMemoryUsed(MEMID_STREAM_ANIMATION); #endif } #define STREAM_DIST 80.0f void CStreaming::AddModelsToRequestList(const CVector &pos, int32 flags) { float xmin, xmax, ymin, ymax; int ixmin, ixmax, iymin, iymax; int ix, iy; int dx, dy, d; CSector *sect; xmin = pos.x - STREAM_DIST; ymin = pos.y - STREAM_DIST; xmax = pos.x + STREAM_DIST; ymax = pos.y + STREAM_DIST; ixmin = CWorld::GetSectorIndexX(xmin); if(ixmin < 0) ixmin = 0; ixmax = CWorld::GetSectorIndexX(xmax); if(ixmax >= NUMSECTORS_X) ixmax = NUMSECTORS_X-1; iymin = CWorld::GetSectorIndexY(ymin); if(iymin < 0) iymin = 0; iymax = CWorld::GetSectorIndexY(ymax); if(iymax >= NUMSECTORS_Y) iymax = NUMSECTORS_Y-1; CWorld::AdvanceCurrentScanCode(); for(iy = iymin; iy <= iymax; iy++){ dy = iy - CWorld::GetSectorIndexY(pos.y); for(ix = ixmin; ix <= ixmax; ix++){ if(CRenderer::m_loadingPriority && ms_numModelsRequested > 5) return; dx = ix - CWorld::GetSectorIndexX(pos.x); d = dx*dx + dy*dy; sect = CWorld::GetSector(ix, iy); if(d <= 0){ ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], flags); ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], flags); ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], flags); ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], flags); }else if(d <= 3*3){ ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], pos.x, pos.y, xmin, ymin, xmax, ymax, flags); ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], pos.x, pos.y, xmin, ymin, xmax, ymax, flags); ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], pos.x, pos.y, xmin, ymin, xmax, ymax, flags); ProcessEntitiesInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], pos.x, pos.y, xmin, ymin, xmax, ymax, flags); } } } } void CStreaming::ProcessEntitiesInSectorList(CPtrList &list, float x, float y, float xmin, float ymin, float xmax, float ymax, int32 flags) { CPtrNode *node; CEntity *e; float lodDistSq; CVector2D pos; for(node = list.first; node; node = node->next){ e = (CEntity*)node->item; if(e->m_scanCode == CWorld::GetCurrentScanCode()) continue; e->m_scanCode = CWorld::GetCurrentScanCode(); if(!e->bStreamingDontDelete && IsAreaVisible(e->m_area) && !e->bDontStream && e->bIsVisible){ CTimeModelInfo *mi = (CTimeModelInfo*)CModelInfo::GetModelInfo(e->GetModelIndex()); if (mi->GetModelType() != MITYPE_TIME || CClock::GetIsTimeInRange(mi->GetTimeOn(), mi->GetTimeOff())) { lodDistSq = sq(mi->GetLargestLodDistance()); lodDistSq = Min(lodDistSq, sq(STREAM_DIST)); pos = CVector2D(e->GetPosition()); if(xmin < pos.x && pos.x < xmax && ymin < pos.y && pos.y < ymax && (CVector2D(x, y) - pos).MagnitudeSqr() < lodDistSq) RequestModel(e->GetModelIndex(), flags); } } } } void CStreaming::ProcessEntitiesInSectorList(CPtrList &list, int32 flags) { CPtrNode *node; CEntity *e; for(node = list.first; node; node = node->next){ e = (CEntity*)node->item; if(e->m_scanCode == CWorld::GetCurrentScanCode()) continue; e->m_scanCode = CWorld::GetCurrentScanCode(); if(!e->bStreamingDontDelete && IsAreaVisible(e->m_area) && !e->bDontStream && e->bIsVisible){ CTimeModelInfo *mi = (CTimeModelInfo*)CModelInfo::GetModelInfo(e->GetModelIndex()); if (mi->GetModelType() != MITYPE_TIME || CClock::GetIsTimeInRange(mi->GetTimeOn(), mi->GetTimeOff())) RequestModel(e->GetModelIndex(), flags); } } } void CStreaming::DeleteFarAwayRwObjects(const CVector &pos) { int posx, posy; int x, y; int r, i; CSector *sect; posx = CWorld::GetSectorIndexX(pos.x); posy = CWorld::GetSectorIndexY(pos.y); // Move oldSectorX/Y to new sector and delete RW objects in its "wake" for every step: // O is the old sector, <- is the direction in which we move it, // X are the sectors we delete RW objects from (except we go up to 10) // X // X X // X X X // X X X // <- O X X X // X X X // X X X // X X // X while(posx != ms_oldSectorX){ if(posx < ms_oldSectorX){ for(r = 2; r <= 10; r++){ x = ms_oldSectorX + r; if(x < 0) continue; if(x >= NUMSECTORS_X) break; for(i = -r; i <= r; i++){ y = ms_oldSectorY + i; if(y < 0) continue; if(y >= NUMSECTORS_Y) break; sect = CWorld::GetSector(x, y); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); DeleteRwObjectsInOverlapSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], ms_oldSectorX, ms_oldSectorY); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); } } ms_oldSectorX--; }else{ for(r = 2; r <= 10; r++){ x = ms_oldSectorX - r; if(x < 0) break; if(x >= NUMSECTORS_X) continue; for(i = -r; i <= r; i++){ y = ms_oldSectorY + i; if(y < 0) continue; if(y >= NUMSECTORS_Y) break; sect = CWorld::GetSector(x, y); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); DeleteRwObjectsInOverlapSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], ms_oldSectorX, ms_oldSectorY); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); } } ms_oldSectorX++; } } while(posy != ms_oldSectorY){ if(posy < ms_oldSectorY){ for(r = 2; r <= 10; r++){ y = ms_oldSectorY + r; if(y < 0) continue; if(y >= NUMSECTORS_Y) break; for(i = -r; i <= r; i++){ x = ms_oldSectorX + i; if(x < 0) continue; if(x >= NUMSECTORS_X) break; sect = CWorld::GetSector(x, y); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); DeleteRwObjectsInOverlapSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], ms_oldSectorX, ms_oldSectorY); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); } } ms_oldSectorY--; }else{ for(r = 2; r <= 10; r++){ y = ms_oldSectorY - r; if(y < 0) break; if(y >= NUMSECTORS_Y) continue; for(i = -r; i <= r; i++){ x = ms_oldSectorX + i; if(x < 0) continue; if(x >= NUMSECTORS_X) break; sect = CWorld::GetSector(x, y); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); DeleteRwObjectsInOverlapSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], ms_oldSectorX, ms_oldSectorY); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); } } ms_oldSectorY++; } } } void CStreaming::DeleteAllRwObjects(void) { int x, y; CSector *sect; for(x = 0; x < NUMSECTORS_X; x++) for(y = 0; y < NUMSECTORS_Y; y++){ sect = CWorld::GetSector(x, y); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS_OVERLAP]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES_OVERLAP]); } } void CStreaming::DeleteRwObjectsAfterDeath(const CVector &pos) { int ix, iy; int x, y; CSector *sect; ix = CWorld::GetSectorIndexX(pos.x); iy = CWorld::GetSectorIndexY(pos.y); for(x = 0; x < NUMSECTORS_X; x++) for(y = 0; y < NUMSECTORS_Y; y++) if(Abs(ix - x) > 3.0f && Abs(iy - y) > 3.0f){ sect = CWorld::GetSector(x, y); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_OBJECTS_OVERLAP]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES]); DeleteRwObjectsInSectorList(sect->m_lists[ENTITYLIST_DUMMIES_OVERLAP]); } } void CStreaming::DeleteRwObjectsBehindCamera(size_t mem) { int ix, iy; int x, y; int xmin, xmax, ymin, ymax; int inc; CSector *sect; if(ms_memoryUsed < mem) return; ix = CWorld::GetSectorIndexX(TheCamera.GetPosition().x); iy = CWorld::GetSectorIndexY(TheCamera.GetPosition().y); if(Abs(TheCamera.GetForward().x) > Abs(TheCamera.GetForward().y)){ // looking west/east ymin = Max(iy - 10, 0); ymax = Min(iy + 10, NUMSECTORS_Y - 1); assert(ymin <= ymax); // Delete a block of sectors that we know is behind the camera if(TheCamera.GetForward().x > 0.0f){ // looking east xmax = Max(ix - 2, 0); xmin = Max(ix - 10, 0); inc = 1; }else{ // looking west xmax = Min(ix + 2, NUMSECTORS_X - 1); xmin = Min(ix + 10, NUMSECTORS_X - 1); inc = -1; } for(y = ymin; y <= ymax; y++){ for(x = xmin; x != xmax; x += inc){ sect = CWorld::GetSector(x, y); if(DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) return; } } while(RemoveLoadedZoneModel()) if(ms_memoryUsed < mem) return; // Now a block that intersects with the camera's frustum if(TheCamera.GetForward().x > 0.0f){ // looking east xmax = Max(ix + 10, 0); xmin = Max(ix - 2, 0); inc = 1; }else{ // looking west xmax = Min(ix - 10, NUMSECTORS_X - 1); xmin = Min(ix + 2, NUMSECTORS_X - 1); inc = -1; } for(y = ymin; y <= ymax; y++){ for(x = xmin; x != xmax; x += inc){ sect = CWorld::GetSector(x, y); if(DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) return; } } // As last resort, delete objects from the last step more aggressively for(y = ymin; y <= ymax; y++){ for(x = xmax; x != xmin; x -= inc){ sect = CWorld::GetSector(x, y); if(DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) return; } } }else{ // looking north/south xmin = Max(ix - 10, 0); xmax = Min(ix + 10, NUMSECTORS_X - 1); assert(xmin <= xmax); // Delete a block of sectors that we know is behind the camera if(TheCamera.GetForward().y > 0.0f){ // looking north ymax = Max(iy - 2, 0); ymin = Max(iy - 10, 0); inc = 1; }else{ // looking south ymax = Min(iy + 2, NUMSECTORS_Y - 1); ymin = Min(iy + 10, NUMSECTORS_Y - 1); inc = -1; } for(x = xmin; x <= xmax; x++){ for(y = ymin; y != ymax; y += inc){ sect = CWorld::GetSector(x, y); if(DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) return; } } while(RemoveLoadedZoneModel()) if(ms_memoryUsed < mem) return; // Now a block that intersects with the camera's frustum if(TheCamera.GetForward().y > 0.0f){ // looking north ymax = Max(iy + 10, 0); ymin = Max(iy - 2, 0); inc = 1; }else{ // looking south ymax = Min(iy - 10, NUMSECTORS_Y - 1); ymin = Min(iy + 2, NUMSECTORS_Y - 1); inc = -1; } for(x = xmin; x <= xmax; x++){ for(y = ymin; y != ymax; y += inc){ sect = CWorld::GetSector(x, y); if(DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || DeleteRwObjectsNotInFrustumInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) return; } } // this is gone in mobile together with RemoveReferencedTxds // if(RemoveReferencedTxds(mem)) // return; // As last resort, delete objects from the last step more aggressively for(x = xmin; x <= xmax; x++){ for(y = ymax; y != ymin; y -= inc){ sect = CWorld::GetSector(x, y); if(DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_BUILDINGS], mem) || DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_DUMMIES], mem) || DeleteRwObjectsBehindCameraInSectorList(sect->m_lists[ENTITYLIST_OBJECTS], mem)) return; } } } while(ms_memoryUsed >= mem && RemoveLeastUsedModel(0)); } void CStreaming::DeleteRwObjectsInSectorList(CPtrList &list) { CPtrNode *node; CEntity *e; for(node = list.first; node; node = node->next){ e = (CEntity*)node->item; if(!e->bStreamingDontDelete && !e->bImBeingRendered) e->DeleteRwObject(); } } void CStreaming::DeleteRwObjectsInOverlapSectorList(CPtrList &list, int32 x, int32 y) { CPtrNode *node; CEntity *e; for(node = list.first; node; node = node->next){ e = (CEntity*)node->item; if(e->m_rwObject && !e->bStreamingDontDelete && !e->bImBeingRendered){ // Now this is pretty weird... if(Abs(CWorld::GetSectorIndexX(e->GetPosition().x) - x) >= 1.6f) // { e->DeleteRwObject(); // return; // BUG? // } else // FIX? if(Abs(CWorld::GetSectorIndexY(e->GetPosition().y) - y) >= 1.6f) e->DeleteRwObject(); } } } bool CStreaming::DeleteRwObjectsBehindCameraInSectorList(CPtrList &list, size_t mem) { CPtrNode *node; CEntity *e; for(node = list.first; node; node = node->next){ e = (CEntity*)node->item; if(!e->bStreamingDontDelete && !e->bImBeingRendered && e->m_rwObject && ms_aInfoForModel[e->GetModelIndex()].m_next && FindPlayerPed()->m_pCurSurface != e){ e->DeleteRwObject(); if (CModelInfo::GetModelInfo(e->GetModelIndex())->GetNumRefs() == 0) { RemoveModel(e->GetModelIndex()); if(ms_memoryUsed < mem) return true; } } } return false; } bool CStreaming::DeleteRwObjectsNotInFrustumInSectorList(CPtrList &list, size_t mem) { CPtrNode *node; CEntity *e; for(node = list.first; node; node = node->next){ e = (CEntity*)node->item; if(!e->bStreamingDontDelete && !e->bImBeingRendered && e->m_rwObject && (!e->IsVisible() || e->bOffscreen) && ms_aInfoForModel[e->GetModelIndex()].m_next){ e->DeleteRwObject(); if (CModelInfo::GetModelInfo(e->GetModelIndex())->GetNumRefs() == 0) { RemoveModel(e->GetModelIndex()); if(ms_memoryUsed < mem) return true; } } } return false; } void CStreaming::MakeSpaceFor(int32 size) { #ifdef FIX_BUGS #define MB (1024 * 1024) if(ms_memoryAvailable == 0) { extern size_t _dwMemAvailPhys; ms_memoryAvailable = (_dwMemAvailPhys - 10 * MB) / 2; if(ms_memoryAvailable < 65 * MB) ms_memoryAvailable = 65 * MB; } #undef MB #endif while(ms_memoryUsed >= ms_memoryAvailable - size) if(!RemoveLeastUsedModel(STREAMFLAGS_20)){ DeleteRwObjectsBehindCamera(ms_memoryAvailable - size); return; } } void CStreaming::LoadScene(const CVector &pos) { CStreamingInfo *si, *prev; eLevelName level; level = CTheZones::GetLevelFromPosition(&pos); debug("Start load scene\n"); for(si = ms_endRequestedList.m_prev; si != &ms_startRequestedList; si = prev){ prev = si->m_prev; if((si->m_flags & (STREAMFLAGS_KEEP_IN_MEMORY|STREAMFLAGS_PRIORITY)) == 0) RemoveModel(si - ms_aInfoForModel); } CRenderer::m_loadingPriority = false; DeleteAllRwObjects(); if(level == LEVEL_GENERIC) level = CGame::currLevel; CGame::currLevel = level; RemoveUnusedBigBuildings(level); RequestBigBuildings(level, pos); RequestBigBuildings(LEVEL_GENERIC, pos); RemoveIslandsNotUsed(level); LoadAllRequestedModels(false); InstanceBigBuildings(level, pos); InstanceBigBuildings(LEVEL_GENERIC, pos); AddModelsToRequestList(pos, STREAMFLAGS_20); CRadar::StreamRadarSections(pos); if (!CGame::IsInInterior()) { for (int i = 0; i < 5; i++) { CZoneInfo zone; CTheZones::GetZoneInfoForTimeOfDay(&pos, &zone); int32 model = CCarCtrl::ChooseCarModelToLoad(CCarCtrl::ChooseCarRating(&zone)); CStreaming::RequestModel(model, STREAMFLAGS_DEPENDENCY); } } LoadAllRequestedModels(false); InstanceLoadedModels(pos); for(int i = 0; i < NUMSTREAMINFO; i++) ms_aInfoForModel[i].m_flags &= ~STREAMFLAGS_20; debug("End load scene\n"); } void CStreaming::LoadSceneCollision(const CVector &pos) { CColStore::LoadCollision(pos); CStreaming::LoadAllRequestedModels(false); } void CStreaming::MemoryCardSave(uint8 *buf, uint32 *size) { int i; *size = NUM_DEFAULT_MODELS; for(i = 0; i < NUM_DEFAULT_MODELS; i++) if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED) buf[i] = ms_aInfoForModel[i].m_flags; else buf[i] = 0xFF; } void CStreaming::MemoryCardLoad(uint8 *buf, uint32 size) { uint32 i; assert(size == NUM_DEFAULT_MODELS); for(i = 0; i < size; i++) if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED) if(buf[i] != 0xFF) ms_aInfoForModel[i].m_flags = buf[i]; } void CStreaming::UpdateForAnimViewer(void) { if (CStreaming::ms_channelError == -1) { CStreaming::AddModelsToRequestList(CVector(0.0f, 0.0f, 0.0f), 0); CStreaming::LoadRequestedModels(); // original modifier was %d sprintf(gString, "Requested %d, memory size %zuK\n", CStreaming::ms_numModelsRequested, 2 * CStreaming::ms_memoryUsed); } else { CStreaming::RetryLoadFile(CStreaming::ms_channelError); } } void CStreaming::PrintStreamingBufferState() { char str[128]; wchar wstr[128]; uint32 offset, size; CTimer::Stop(); int i = 0; while (i < NUMSTREAMINFO) { while (true) { int j = 0; DoRWStuffStartOfFrame(50, 50, 50, 0, 0, 0, 255); CPad::UpdatePads(); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); DefinedState(); CRect unusedRect(0, 0, RsGlobal.maximumWidth, RsGlobal.maximumHeight); CRGBA unusedColor(255, 255, 255, 255); CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetWrapx(DEFAULT_SCREEN_WIDTH); CFont::SetScale(0.5f, 0.75f); CFont::SetCentreOff(); CFont::SetCentreSize(DEFAULT_SCREEN_WIDTH); CFont::SetJustifyOff(); CFont::SetColor(CRGBA(200, 200, 200, 200)); CFont::SetBackGroundOnlyTextOff(); int modelIndex = i; if (modelIndex < NUMSTREAMINFO) { int y = 24; for ( ; j < 34 && modelIndex < NUMSTREAMINFO; modelIndex++) { CStreamingInfo *streamingInfo = &ms_aInfoForModel[modelIndex]; CBaseModelInfo *modelInfo = CModelInfo::GetModelInfo(modelIndex); if (streamingInfo->m_loadState != STREAMSTATE_LOADED || !streamingInfo->GetCdPosnAndSize(offset, size)) continue; if (modelIndex >= STREAM_OFFSET_TXD) sprintf(str, "txd %s, refs %d, size %dK, flags 0x%x", CTxdStore::GetTxdName(modelIndex - STREAM_OFFSET_TXD), CTxdStore::GetNumRefs(modelIndex - STREAM_OFFSET_TXD), 2 * size, streamingInfo->m_flags); else sprintf(str, "model %d,%s, refs%d, size%dK, flags%x", modelIndex, modelInfo->GetModelName(), modelInfo->GetNumRefs(), 2 * size, streamingInfo->m_flags); AsciiToUnicode(str, wstr); CFont::PrintString(24.0f, y, wstr); y += 12; j++; } } if (CPad::GetPad(1)->GetCrossJustDown()) i = modelIndex; if (!CPad::GetPad(1)->GetTriangleJustDown()) break; i = 0; CFont::DrawFonts(); DoRWStuffEndOfFrame(); } CFont::DrawFonts(); DoRWStuffEndOfFrame(); } CTimer::Update(); } ================================================ FILE: src/core/Streaming.h ================================================ #pragma once #include "Game.h" enum { STREAM_OFFSET_TXD = MODELINFOSIZE, STREAM_OFFSET_COL = STREAM_OFFSET_TXD+TXDSTORESIZE, STREAM_OFFSET_ANIM = STREAM_OFFSET_COL+COLSTORESIZE, NUMSTREAMINFO = STREAM_OFFSET_ANIM+NUMANIMBLOCKS }; enum StreamFlags { STREAMFLAGS_DONT_REMOVE = 0x01, STREAMFLAGS_SCRIPTOWNED = 0x02, STREAMFLAGS_DEPENDENCY = 0x04, // Is this right? STREAMFLAGS_PRIORITY = 0x08, STREAMFLAGS_NOFADE = 0x10, STREAMFLAGS_20 = 0x20, // TODO(MIAMI): what's this STREAMFLAGS_CANT_REMOVE = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED, STREAMFLAGS_KEEP_IN_MEMORY = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED|STREAMFLAGS_DEPENDENCY, }; enum StreamLoadState { STREAMSTATE_NOTLOADED = 0, STREAMSTATE_LOADED = 1, STREAMSTATE_INQUEUE = 2, STREAMSTATE_READING = 3, // channel is reading STREAMSTATE_STARTED = 4, // first part loaded }; enum ChannelState { CHANNELSTATE_IDLE = 0, CHANNELSTATE_READING = 1, CHANNELSTATE_STARTED = 2, CHANNELSTATE_ERROR = 3, }; class CStreamingInfo { public: CStreamingInfo *m_next; CStreamingInfo *m_prev; uint8 m_loadState; uint8 m_flags; int16 m_nextID; uint32 m_position; uint32 m_size; bool GetCdPosnAndSize(uint32 &posn, uint32 &size); void SetCdPosnAndSize(uint32 posn, uint32 size); void AddToList(CStreamingInfo *link); void RemoveFromList(void); uint32 GetCdSize(void) { return m_size; } bool IsPriority(void) { return !!(m_flags & STREAMFLAGS_PRIORITY); } }; struct CStreamingChannel { int32 streamIds[4]; int32 offsets[4]; int32 state; int32 field24; int32 position; int32 size; int32 numTries; int32 status; // from CdStream }; class CDirectory; class CPtrList; class CStreaming { public: static bool ms_disableStreaming; static bool ms_bLoadingBigModel; static int32 ms_numModelsRequested; static CStreamingInfo ms_aInfoForModel[NUMSTREAMINFO]; static CStreamingInfo ms_startLoadedList; static CStreamingInfo ms_endLoadedList; static CStreamingInfo ms_startRequestedList; static CStreamingInfo ms_endRequestedList; static int32 ms_oldSectorX; static int32 ms_oldSectorY; static int32 ms_streamingBufferSize; #ifndef ONE_THREAD_PER_CHANNEL static int8 *ms_pStreamingBuffer[2]; #else static int8 *ms_pStreamingBuffer[4]; #endif static size_t ms_memoryUsed; static CStreamingChannel ms_channel[2]; static int32 ms_channelError; static int32 ms_numVehiclesLoaded; static int32 ms_numPedsLoaded; static int32 ms_vehiclesLoaded[MAXVEHICLESLOADED]; static int32 ms_lastVehicleDeleted; static bool ms_bIsPedFromPedGroupLoaded[NUMMODELSPERPEDGROUP]; static CDirectory *ms_pExtraObjectsDir; static int32 ms_numPriorityRequests; static int32 ms_currentPedGrp; static int32 ms_lastCullZone; static uint16 ms_loadedGangs; static uint16 ms_loadedGangCars; static int32 ms_currentPedLoading; static int32 ms_imageOffsets[NUMCDIMAGES]; static int32 ms_lastImageRead; static int32 ms_imageSize; static size_t ms_memoryAvailable; static void Init(void); static void Init2(void); static void ReInit(void); static void Shutdown(void); static void Update(void); static void LoadCdDirectory(void); static void LoadCdDirectory(const char *dirname, int32 n); static bool ConvertBufferToObject(int8 *buf, int32 streamId); static bool FinishLoadingLargeFile(int8 *buf, int32 streamId); static bool HasModelLoaded(int32 id) { return ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED; } static bool HasTxdLoaded(int32 id) { return HasModelLoaded(id+STREAM_OFFSET_TXD); } static bool HasColLoaded(int32 id) { return HasModelLoaded(id+STREAM_OFFSET_COL); } static bool HasAnimLoaded(int32 id) { return HasModelLoaded(id+STREAM_OFFSET_ANIM); } static bool CanRemoveModel(int32 id) { return (ms_aInfoForModel[id].m_flags & STREAMFLAGS_CANT_REMOVE) == 0; } static bool CanRemoveTxd(int32 id) { return CanRemoveModel(id+STREAM_OFFSET_TXD); } static bool CanRemoveCol(int32 id) { return CanRemoveModel(id+STREAM_OFFSET_COL); } static bool CanRemoveAnim(int32 id) { return CanRemoveModel(id+STREAM_OFFSET_ANIM); } static void RequestModel(int32 model, int32 flags); static void ReRequestModel(int32 model) { RequestModel(model, ms_aInfoForModel[model].m_flags); } static void RequestTxd(int32 txd, int32 flags) { RequestModel(txd + STREAM_OFFSET_TXD, flags); } static void ReRequestTxd(int32 txd) { ReRequestModel(txd + STREAM_OFFSET_TXD); } static void RequestCol(int32 col, int32 flags) { RequestModel(col + STREAM_OFFSET_COL, flags); } static void ReRequestCol(int32 col) { ReRequestModel(col + STREAM_OFFSET_COL); } static void RequestAnim(int32 col, int32 flags) { RequestModel(col + STREAM_OFFSET_ANIM, flags); } static void ReRequestAnim(int32 col) { ReRequestModel(col + STREAM_OFFSET_ANIM); } static void RequestBigBuildings(eLevelName level); static void RequestBigBuildings(eLevelName level, const CVector &pos); static void InstanceBigBuildings(eLevelName level, const CVector &pos); static void InstanceLoadedModelsInSectorList(CPtrList &list); static void InstanceLoadedModels(const CVector &pos); static void RequestIslands(eLevelName level); static void RequestSpecialModel(int32 modelId, const char *modelName, int32 flags); static void RequestSpecialChar(int32 charId, const char *modelName, int32 flags); static bool HasSpecialCharLoaded(int32 id); static void SetMissionDoesntRequireSpecialChar(int32 id); static void DecrementRef(int32 id); static void RemoveModel(int32 id); static void RemoveTxd(int32 id) { RemoveModel(id + STREAM_OFFSET_TXD); } static void RemoveCol(int32 id) { RemoveModel(id + STREAM_OFFSET_COL); } static void RemoveAnim(int32 id) { RemoveModel(id + STREAM_OFFSET_ANIM); } static void RemoveUnusedBuildings(eLevelName level); static void RemoveBuildings(eLevelName level); static void RemoveBuildingsNotInArea(int32 area); static void RemoveUnusedBigBuildings(eLevelName level); static void RemoveIslandsNotUsed(eLevelName level); static void RemoveBigBuildings(eLevelName level); static bool RemoveLoadedVehicle(void); static bool RemoveLeastUsedModel(uint32 excludeMask); static void RemoveAllUnusedModels(void); static void RemoveUnusedModelsInLoadedList(void); static bool RemoveLoadedZoneModel(void); static int32 GetAvailableVehicleSlot(void); static bool IsTxdUsedByRequestedModels(int32 txdId); static bool AreAnimsUsedByRequestedModels(int32 animId); static bool AddToLoadedVehiclesList(int32 modelId); static bool IsObjectInCdImage(int32 id); static void SetModelIsDeletable(int32 id); static void SetModelTxdIsDeletable(int32 id); static void SetMissionDoesntRequireModel(int32 id); static void LoadInitialPeds(void); static void LoadInitialWeapons(void); static void LoadInitialVehicles(void); static void StreamVehiclesAndPeds(void); static void StreamZoneModels(const CVector &pos); static void RemoveCurrentZonesModels(void); static void LoadBigBuildingsWhenNeeded(void); static int32 GetCdImageOffset(int32 lastPosn); static int32 GetNextFileOnCd(int32 position, bool priority); static void RequestModelStream(int32 ch); static bool ProcessLoadingChannel(int32 ch); static void RetryLoadFile(int32 ch); static void LoadRequestedModels(void); static void LoadAllRequestedModels(bool priority); static void FlushChannels(void); static void FlushRequestList(void); static void MakeSpaceFor(int32 size); static void ImGonnaUseStreamingMemory(void); static void IHaveUsedStreamingMemory(void); static void UpdateMemoryUsed(void); static void AddModelsToRequestList(const CVector &pos, int32 flags); static void ProcessEntitiesInSectorList(CPtrList &list, float x, float y, float xmin, float ymin, float xmax, float ymax, int32 flags); static void ProcessEntitiesInSectorList(CPtrList &list, int32 flags); static void DeleteFarAwayRwObjects(const CVector &pos); static void DeleteAllRwObjects(void); static void DeleteRwObjectsAfterDeath(const CVector &pos); static void DeleteRwObjectsBehindCamera(size_t mem); // originally signed static void DeleteRwObjectsInSectorList(CPtrList &list); static void DeleteRwObjectsInOverlapSectorList(CPtrList &list, int32 x, int32 y); static bool DeleteRwObjectsBehindCameraInSectorList(CPtrList &list, size_t mem); // originally signed static bool DeleteRwObjectsNotInFrustumInSectorList(CPtrList &list, size_t mem); // originally signed static void LoadScene(const CVector &pos); static void LoadSceneCollision(const CVector &pos); static void MemoryCardSave(uint8 *buffer, uint32 *length); static void MemoryCardLoad(uint8 *buffer, uint32 length); static void UpdateForAnimViewer(void); static void PrintStreamingBufferState(); }; ================================================ FILE: src/core/SurfaceTable.cpp ================================================ #include "common.h" #include "main.h" #include "FileMgr.h" #include "Weather.h" #include "Collision.h" #include "SurfaceTable.h" float CSurfaceTable::ms_aAdhesiveLimitTable[NUMADHESIVEGROUPS][NUMADHESIVEGROUPS]; void CSurfaceTable::Initialise(Const char *filename) { int lineno, fieldno; char *line; char surfname[256]; float adhesiveLimit; CFileMgr::SetDir(""); CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r"); line = (char*)work_buff; for(lineno = 0; lineno < NUMADHESIVEGROUPS; lineno++){ // skip white space and comments while(*line == ' ' || *line == '\t' || *line == '\n' || *line == '\r' || *line == ';'){ if(*line == ';'){ while(*line != '\n' && *line != '\r') line++; }else line++; } sscanf(line, "%s", surfname); // skip what we just read while(!(*line == ' ' || *line == '\t' || *line == ',')) line++; for(fieldno = 0; fieldno <= lineno; fieldno++){ // skip white space while(*line == ' ' || *line == '\t' || *line == ',') line++; adhesiveLimit = 0.0f; if(*line != '-') sscanf(line, "%f", &adhesiveLimit); // skip what we just read while(!(*line == ' ' || *line == '\t' || *line == ',' || *line == '\n')) line++; ms_aAdhesiveLimitTable[lineno][fieldno] = adhesiveLimit; ms_aAdhesiveLimitTable[fieldno][lineno] = adhesiveLimit; } } } int CSurfaceTable::GetAdhesionGroup(uint8 surfaceType) { switch(surfaceType){ case SURFACE_DEFAULT: return ADHESIVE_ROAD; case SURFACE_TARMAC: return ADHESIVE_ROAD; case SURFACE_GRASS: return ADHESIVE_LOOSE; case SURFACE_GRAVEL: return ADHESIVE_LOOSE; case SURFACE_MUD_DRY: return ADHESIVE_HARD; case SURFACE_PAVEMENT: return ADHESIVE_ROAD; case SURFACE_CAR: return ADHESIVE_HARD; case SURFACE_GLASS: return ADHESIVE_HARD; case SURFACE_TRANSPARENT_CLOTH: return ADHESIVE_HARD; case SURFACE_GARAGE_DOOR: return ADHESIVE_HARD; case SURFACE_CAR_PANEL: return ADHESIVE_HARD; case SURFACE_THICK_METAL_PLATE: return ADHESIVE_HARD; case SURFACE_SCAFFOLD_POLE: return ADHESIVE_HARD; case SURFACE_LAMP_POST: return ADHESIVE_HARD; case SURFACE_FIRE_HYDRANT: return ADHESIVE_HARD; case SURFACE_GIRDER: return ADHESIVE_HARD; case SURFACE_METAL_CHAIN_FENCE: return ADHESIVE_HARD; case SURFACE_PED: return ADHESIVE_RUBBER; case SURFACE_SAND: return ADHESIVE_SAND; case SURFACE_WATER: return ADHESIVE_WET; case SURFACE_WOOD_CRATES: return ADHESIVE_ROAD; case SURFACE_WOOD_BENCH: return ADHESIVE_ROAD; case SURFACE_WOOD_SOLID: return ADHESIVE_ROAD; case SURFACE_RUBBER: return ADHESIVE_RUBBER; case SURFACE_PLASTIC: return ADHESIVE_HARD; case SURFACE_HEDGE: return ADHESIVE_LOOSE; case SURFACE_STEEP_CLIFF: return ADHESIVE_LOOSE; case SURFACE_CONTAINER: return ADHESIVE_HARD; case SURFACE_NEWS_VENDOR: return ADHESIVE_HARD; case SURFACE_WHEELBASE: return ADHESIVE_RUBBER; case SURFACE_CARDBOARDBOX: return ADHESIVE_LOOSE; case SURFACE_TRANSPARENT_STONE: return ADHESIVE_HARD; case SURFACE_METAL_GATE: return ADHESIVE_HARD; case SURFACE_SAND_BEACH: return ADHESIVE_SAND; case SURFACE_CONCRETE_BEACH: return ADHESIVE_ROAD; default: return ADHESIVE_ROAD; } } float CSurfaceTable::GetWetMultiplier(uint8 surfaceType) { switch(surfaceType){ case SURFACE_DEFAULT: case SURFACE_TARMAC: case SURFACE_MUD_DRY: case SURFACE_PAVEMENT: case SURFACE_TRANSPARENT_CLOTH: case SURFACE_WOOD_CRATES: case SURFACE_WOOD_BENCH: case SURFACE_WOOD_SOLID: case SURFACE_HEDGE: case SURFACE_CARDBOARDBOX: case SURFACE_TRANSPARENT_STONE: case SURFACE_CONCRETE_BEACH: return 1.0f - CWeather::WetRoads*0.25f; case SURFACE_GRASS: case SURFACE_CAR: case SURFACE_GLASS: case SURFACE_GARAGE_DOOR: case SURFACE_CAR_PANEL: case SURFACE_THICK_METAL_PLATE: case SURFACE_SCAFFOLD_POLE: case SURFACE_LAMP_POST: case SURFACE_FIRE_HYDRANT: case SURFACE_GIRDER: case SURFACE_METAL_CHAIN_FENCE: case SURFACE_PED: case SURFACE_RUBBER: case SURFACE_PLASTIC: case SURFACE_STEEP_CLIFF: case SURFACE_CONTAINER: case SURFACE_NEWS_VENDOR: case SURFACE_WHEELBASE: case SURFACE_METAL_GATE: return 1.0f - CWeather::WetRoads*0.4f; case SURFACE_SAND: case SURFACE_SAND_BEACH: return 1.0f + CWeather::WetRoads*0.5f; default: return 1.0f; } } float CSurfaceTable::GetAdhesiveLimit(CColPoint &colpoint) { return ms_aAdhesiveLimitTable[GetAdhesionGroup(colpoint.surfaceB)][GetAdhesionGroup(colpoint.surfaceA)]; } bool CSurfaceTable::IsSoftLanding(uint8 surf) { return surf == SURFACE_GRASS || surf == SURFACE_SAND || surf == SURFACE_SAND_BEACH; } ================================================ FILE: src/core/SurfaceTable.h ================================================ #pragma once enum eSurfaceType { SURFACE_DEFAULT, SURFACE_TARMAC, SURFACE_GRASS, SURFACE_GRAVEL, SURFACE_MUD_DRY, SURFACE_PAVEMENT, SURFACE_CAR, SURFACE_GLASS, SURFACE_TRANSPARENT_CLOTH, SURFACE_GARAGE_DOOR, SURFACE_CAR_PANEL, SURFACE_THICK_METAL_PLATE, SURFACE_SCAFFOLD_POLE, SURFACE_LAMP_POST, SURFACE_FIRE_HYDRANT, SURFACE_GIRDER, SURFACE_METAL_CHAIN_FENCE, SURFACE_PED, SURFACE_SAND, SURFACE_WATER, SURFACE_WOOD_CRATES, SURFACE_WOOD_BENCH, SURFACE_WOOD_SOLID, SURFACE_RUBBER, SURFACE_PLASTIC, SURFACE_HEDGE, SURFACE_STEEP_CLIFF, SURFACE_CONTAINER, SURFACE_NEWS_VENDOR, SURFACE_WHEELBASE, SURFACE_CARDBOARDBOX, SURFACE_TRANSPARENT_STONE, SURFACE_METAL_GATE, SURFACE_SAND_BEACH, SURFACE_CONCRETE_BEACH, }; enum { ADHESIVE_RUBBER, ADHESIVE_HARD, ADHESIVE_ROAD, ADHESIVE_LOOSE, ADHESIVE_SAND, ADHESIVE_WET, NUMADHESIVEGROUPS }; struct CColPoint; inline bool IsSeeThrough(uint8 surfType) { switch(surfType) case SURFACE_GLASS: case SURFACE_TRANSPARENT_CLOTH: case SURFACE_METAL_CHAIN_FENCE: case SURFACE_TRANSPARENT_STONE: case SURFACE_SCAFFOLD_POLE: return true; return false; } // I think the necessity of this function is really a bug inline bool IsSeeThroughVertical(uint8 surfType) { switch(surfType) case SURFACE_GLASS: case SURFACE_TRANSPARENT_CLOTH: return true; return false; } inline bool IsShootThrough(uint8 surfType) { switch(surfType) case SURFACE_TRANSPARENT_CLOTH: case SURFACE_METAL_CHAIN_FENCE: case SURFACE_TRANSPARENT_STONE: case SURFACE_SCAFFOLD_POLE: return true; return false; } class CSurfaceTable { static float ms_aAdhesiveLimitTable[NUMADHESIVEGROUPS][NUMADHESIVEGROUPS]; public: static void Initialise(Const char *filename); static int GetAdhesionGroup(uint8 surfaceType); static float GetWetMultiplier(uint8 surfaceType); static float GetAdhesiveLimit(CColPoint &colpoint); static bool IsSoftLanding(uint8 surf); }; ================================================ FILE: src/core/TimeStep.cpp ================================================ #include "TimeStep.h" float CTimeStep::ms_fTimeScale = 1.0f; float CTimeStep::ms_fFramesPerUpdate = 1.0f; float CTimeStep::ms_fTimeStep = 1.0f; ================================================ FILE: src/core/TimeStep.h ================================================ #pragma once // Pretty sure this class is not used by the game class CTimeStep { public: static float ms_fTimeScale; static float ms_fFramesPerUpdate; static float ms_fTimeStep; }; ================================================ FILE: src/core/Timer.cpp ================================================ #define WITHWINDOWS #include "common.h" #include "crossplatform.h" #include "DMAudio.h" #include "Record.h" #include "Timer.h" #include "SpecialFX.h" uint32 CTimer::m_snTimeInMilliseconds; PauseModeTime CTimer::m_snTimeInMillisecondsPauseMode = 1; uint32 CTimer::m_snTimeInMillisecondsNonClipped; uint32 CTimer::m_snPreviousTimeInMilliseconds; uint32 CTimer::m_FrameCounter; float CTimer::ms_fTimeScale; float CTimer::ms_fTimeStep; float CTimer::ms_fTimeStepNonClipped; bool CTimer::m_UserPause; bool CTimer::m_CodePause; uint32 _nCyclesPerMS = 1; #ifdef _WIN32 LARGE_INTEGER _oldPerfCounter; LARGE_INTEGER perfSuspendCounter; #define RsTimerType uint32 #else #define RsTimerType double #endif RsTimerType oldPcTimer; RsTimerType suspendPcTimer; uint32 suspendDepth; #ifdef FIX_HIGH_FPS_BUGS_ON_FRONTEND double frameTime; #endif void CTimer::Initialise(void) { debug("Initialising CTimer...\n"); ms_fTimeScale = 1.0f; ms_fTimeStep = 1.0f; suspendDepth = 0; m_UserPause = false; m_CodePause = false; m_snTimeInMillisecondsNonClipped = 0; m_snPreviousTimeInMilliseconds = 0; m_snTimeInMilliseconds = 1; #ifdef _WIN32 LARGE_INTEGER perfFreq; if ( QueryPerformanceFrequency(&perfFreq) ) { OutputDebugString("Performance counter available\n"); _nCyclesPerMS = uint32(perfFreq.QuadPart / 1000); QueryPerformanceCounter(&_oldPerfCounter); } else #endif { OutputDebugString("Performance counter not available, using millesecond timer\n"); _nCyclesPerMS = 0; oldPcTimer = RsTimer(); } m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds; m_FrameCounter = 0; DMAudio.ResetTimers(m_snPreviousTimeInMilliseconds); debug("CTimer ready\n"); } void CTimer::Shutdown(void) { ; } void CTimer::Update(void) { m_snPreviousTimeInMilliseconds = m_snTimeInMilliseconds; #ifdef _WIN32 if ( (double)_nCyclesPerMS != 0.0 ) { LARGE_INTEGER pc; QueryPerformanceCounter(&pc); int32 updInCycles = (pc.LowPart - _oldPerfCounter.LowPart); // & 0x7FFFFFFF; pointless _oldPerfCounter = pc; float updInCyclesScaled = GetIsPaused() ? updInCycles : updInCycles * ms_fTimeScale; // We need that real frame time to fix transparent menu bug. #ifndef FIX_HIGH_FPS_BUGS_ON_FRONTEND double #endif frameTime = updInCyclesScaled / (double)_nCyclesPerMS; m_snTimeInMillisecondsPauseMode = m_snTimeInMillisecondsPauseMode + frameTime; if ( GetIsPaused() ) ms_fTimeStep = 0.0f; else { m_snTimeInMilliseconds = m_snTimeInMilliseconds + frameTime; m_snTimeInMillisecondsNonClipped = m_snTimeInMillisecondsNonClipped + frameTime; ms_fTimeStep = frameTime / 1000.0f * 50.0f; } } else #endif { RsTimerType timer = RsTimer(); RsTimerType updInMs = timer - oldPcTimer; // We need that real frame time to fix transparent menu bug. #ifndef FIX_HIGH_FPS_BUGS_ON_FRONTEND double #endif frameTime = (double)updInMs * ms_fTimeScale; oldPcTimer = timer; m_snTimeInMillisecondsPauseMode = m_snTimeInMillisecondsPauseMode + frameTime; if ( GetIsPaused() ) ms_fTimeStep = 0.0f; else { m_snTimeInMilliseconds = m_snTimeInMilliseconds + frameTime; m_snTimeInMillisecondsNonClipped = m_snTimeInMillisecondsNonClipped + frameTime; ms_fTimeStep = frameTime / 1000.0f * 50.0f; } } if ( ms_fTimeStep < 0.01f && !GetIsPaused() && !CSpecialFX::bSnapShotActive) ms_fTimeStep = 0.01f; ms_fTimeStepNonClipped = ms_fTimeStep; if ( !CRecordDataForGame::IsPlayingBack() ) { ms_fTimeStep = Min(3.0f, ms_fTimeStep); if ( (m_snTimeInMilliseconds - m_snPreviousTimeInMilliseconds) > 60 ) m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds + 60; } if ( CRecordDataForChase::IsRecording() ) { ms_fTimeStep = 1.0f; m_snTimeInMilliseconds = m_snPreviousTimeInMilliseconds + 16; } m_FrameCounter++; } void CTimer::Suspend(void) { if ( ++suspendDepth > 1 ) return; #ifdef _WIN32 if ( (double)_nCyclesPerMS != 0.0 ) QueryPerformanceCounter(&perfSuspendCounter); else #endif suspendPcTimer = RsTimer(); } void CTimer::Resume(void) { if ( --suspendDepth != 0 ) return; #ifdef _WIN32 if ( (double)_nCyclesPerMS != 0.0 ) { LARGE_INTEGER pc; QueryPerformanceCounter(&pc); _oldPerfCounter.LowPart += pc.LowPart - perfSuspendCounter.LowPart; } else #endif oldPcTimer += RsTimer() - suspendPcTimer; } uint32 CTimer::GetCyclesPerMillisecond(void) { #ifdef _WIN32 if (_nCyclesPerMS != 0) return _nCyclesPerMS; else #endif return 1; } uint32 CTimer::GetCurrentTimeInCycles(void) { #ifdef _WIN32 if ( _nCyclesPerMS != 0 ) { LARGE_INTEGER pc; QueryPerformanceCounter(&pc); return (pc.LowPart - _oldPerfCounter.LowPart); // & 0x7FFFFFFF; pointless } else #endif return RsTimer() - oldPcTimer; } bool CTimer::GetIsSlowMotionActive(void) { return ms_fTimeScale < 1.0f; } void CTimer::Stop(void) { m_snPreviousTimeInMilliseconds = m_snTimeInMilliseconds; } void CTimer::StartUserPause(void) { m_UserPause = true; } void CTimer::EndUserPause(void) { m_UserPause = false; } uint32 CTimer::GetCyclesPerFrame() { return 20; } ================================================ FILE: src/core/Timer.h ================================================ #pragma once #ifdef FIX_HIGH_FPS_BUGS_ON_FRONTEND #define PauseModeTime double #else #define PauseModeTime uint32 #endif class CTimer { static uint32 m_snTimeInMilliseconds; static PauseModeTime m_snTimeInMillisecondsPauseMode; static uint32 m_snTimeInMillisecondsNonClipped; static uint32 m_snPreviousTimeInMilliseconds; static uint32 m_FrameCounter; static float ms_fTimeScale; static float ms_fTimeStep; static float ms_fTimeStepNonClipped; public: static bool m_UserPause; static bool m_CodePause; static const float &GetTimeStep(void) { return ms_fTimeStep; } static void SetTimeStep(float ts) { ms_fTimeStep = ts; } static float GetTimeStepInSeconds() { return ms_fTimeStep / 50.0f; } static uint32 GetTimeStepInMilliseconds() { return ms_fTimeStep / 50.0f * 1000.0f; } static const float &GetTimeStepNonClipped(void) { return ms_fTimeStepNonClipped; } static float GetTimeStepNonClippedInSeconds(void) { return ms_fTimeStepNonClipped / 50.0f; } static float GetTimeStepNonClippedInMilliseconds(void) { return ms_fTimeStepNonClipped / 50.0f * 1000.0f; } static void SetTimeStepNonClipped(float ts) { ms_fTimeStepNonClipped = ts; } static const uint32 &GetFrameCounter(void) { return m_FrameCounter; } static void SetFrameCounter(uint32 fc) { m_FrameCounter = fc; } static const uint32 &GetTimeInMilliseconds(void) { return m_snTimeInMilliseconds; } static void SetTimeInMilliseconds(uint32 t) { m_snTimeInMilliseconds = t; } static uint32 GetTimeInMillisecondsNonClipped(void) { return m_snTimeInMillisecondsNonClipped; } static void SetTimeInMillisecondsNonClipped(uint32 t) { m_snTimeInMillisecondsNonClipped = t; } static PauseModeTime GetTimeInMillisecondsPauseMode(void) { return m_snTimeInMillisecondsPauseMode; } static void SetTimeInMillisecondsPauseMode(uint32 t) { m_snTimeInMillisecondsPauseMode = t; } static uint32 GetPreviousTimeInMilliseconds(void) { return m_snPreviousTimeInMilliseconds; } static void SetPreviousTimeInMilliseconds(uint32 t) { m_snPreviousTimeInMilliseconds = t; } static const float &GetTimeScale(void) { return ms_fTimeScale; } static void SetTimeScale(float ts) { ms_fTimeScale = ts; } static uint32 GetCyclesPerFrame(); static bool GetIsPaused() { return m_UserPause || m_CodePause; } static bool GetIsUserPaused() { return m_UserPause; } static bool GetIsCodePaused() { return m_CodePause; } static void SetCodePause(bool pause) { m_CodePause = pause; } static void Initialise(void); static void Shutdown(void); static void Update(void); static void Suspend(void); static void Resume(void); static uint32 GetCyclesPerMillisecond(void); static uint32 GetCurrentTimeInCycles(void); static bool GetIsSlowMotionActive(void); static void Stop(void); static void StartUserPause(void); static void EndUserPause(void); friend bool GenericLoad(void); friend bool GenericSave(int file); friend class CMemoryCard; #ifdef FIX_BUGS static float GetDefaultTimeStep(void) { return 50.0f / 30.0f; } static float GetTimeStepFix(void) { return GetTimeStep() / GetDefaultTimeStep(); } #endif }; #ifdef FIX_HIGH_FPS_BUGS_ON_FRONTEND extern double frameTime; #endif ================================================ FILE: src/core/User.cpp ================================================ #include "common.h" #include "GameLogic.h" #include "Hud.h" #include "PlayerPed.h" #include "Replay.h" #include "Text.h" #include "User.h" #include "Vehicle.h" #include "World.h" #include "Zones.h" CPlaceName CUserDisplay::PlaceName; COnscreenTimer CUserDisplay::OnscnTimer; CPager CUserDisplay::Pager; CCurrentVehicle CUserDisplay::CurrentVehicle; CPlaceName::CPlaceName() { Init(); } void CPlaceName::Init() { m_pZone = nil; m_pZone2 = nil; m_nAdditionalTimer = 0; } void CPlaceName::Process() { CVector pos = CWorld::Players[CWorld::PlayerInFocus].GetPos(); CZone *navigZone = CTheZones::FindSmallestNavigationZoneForPosition(&pos, false, true); CZone *defaultZone = CTheZones::FindSmallestNavigationZoneForPosition(&pos, true, false); if (navigZone == nil) m_pZone = nil; if (defaultZone == nil) m_pZone2 = nil; if (navigZone == m_pZone) { if (defaultZone == m_pZone2 || m_pZone != nil) { if (navigZone != nil || defaultZone != nil) { if (m_nAdditionalTimer != 0) m_nAdditionalTimer--; } else { m_nAdditionalTimer = 0; m_pZone = nil; m_pZone2 = nil; } } else { m_pZone2 = defaultZone; m_nAdditionalTimer = 250; } } else { m_pZone = navigZone; m_nAdditionalTimer = 250; } Display(); } void CPlaceName::Display() { wchar *text; if (m_pZone != nil) text = m_pZone->GetTranslatedName(); else if (m_pZone2 != nil) text = m_pZone2->GetTranslatedName(); #ifdef FIX_BUGS else text = nil; #endif CHud::SetZoneName(text); } void CPlaceName::ProcessAfterFrontEndShutDown(void) { CHud::m_pLastZoneName = nil; CHud::m_ZoneState = 0; m_nAdditionalTimer = 250; } CCurrentVehicle::CCurrentVehicle() { Init(); } void CCurrentVehicle::Init() { m_pCurrentVehicle = nil; } void CCurrentVehicle::Process() { if (CWorld::Players[CWorld::PlayerInFocus].m_pPed->InVehicle()) m_pCurrentVehicle = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_pMyVehicle; else m_pCurrentVehicle = nil; Display(); } void CCurrentVehicle::Display() { wchar *text = nil; if (m_pCurrentVehicle != nil && m_pCurrentVehicle != CGameLogic::pShortCutTaxi) text = TheText.Get(((CVehicleModelInfo*)CModelInfo::GetModelInfo(m_pCurrentVehicle->GetModelIndex()))->m_gameName); CHud::SetVehicleName(text); } void CUserDisplay::Init() { PlaceName.Init(); OnscnTimer.Init(); Pager.Init(); CurrentVehicle.Init(); } void CUserDisplay::Process() { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return; #endif PlaceName.Process(); OnscnTimer.Process(); Pager.Process(); CurrentVehicle.Process(); } ================================================ FILE: src/core/User.h ================================================ #pragma once #include "Pager.h" #include "OnscreenTimer.h" class CZone; class CVehicle; class CPlaceName { CZone *m_pZone; CZone *m_pZone2; int16 m_nAdditionalTimer; public: CPlaceName(); void Init(); void Process(); void Display(); void ProcessAfterFrontEndShutDown(); }; class CCurrentVehicle { CVehicle *m_pCurrentVehicle; public: CCurrentVehicle(); void Init(); void Process(); void Display(); }; class CUserDisplay { public: static CPlaceName PlaceName; static COnscreenTimer OnscnTimer; static CPager Pager; static CCurrentVehicle CurrentVehicle; static void Init(); static void Process(); }; ================================================ FILE: src/core/Wanted.cpp ================================================ #include "common.h" #include "Pools.h" #include "ModelIndices.h" #include "Timer.h" #include "World.h" #include "ZoneCull.h" #include "Darkel.h" #include "DMAudio.h" #include "CopPed.h" #include "Wanted.h" #include "General.h" #include "Stats.h" int32 CWanted::MaximumWantedLevel = 6; int32 CWanted::nMaximumWantedLevel = 9600; void CWanted::Initialise() { m_nChaos = 0; m_nMinChaos = 0; m_nLastUpdateTime = 0; m_nLastWantedLevelChange = 0; m_nLastTimeSuspended = 0; m_CurrentCops = 0; m_MaxCops = 0; m_MaximumLawEnforcerVehicles = 0; m_RoadblockDensity = 0; m_bIgnoredByCops = false; m_bIgnoredByEveryone = false; m_bSwatRequired = false; m_bFbiRequired = false; m_bArmyRequired = false; m_fCrimeSensitivity = 1.0f; m_nWantedLevel = 0; m_nMinWantedLevel = 0; m_CopsBeatingSuspect = 0; for (int i = 0; i < ARRAY_SIZE(m_pCops); i++) m_pCops[i] = nil; ClearQdCrimes(); } bool CWanted::AreMiamiViceRequired() { return m_nWantedLevel >= 3; } bool CWanted::AreSwatRequired() { return m_nWantedLevel == 4 || m_bSwatRequired; } bool CWanted::AreFbiRequired() { return m_nWantedLevel == 5 || m_bFbiRequired; } bool CWanted::AreArmyRequired() { return m_nWantedLevel == 6 || m_bArmyRequired; } int32 CWanted::NumOfHelisRequired() { if (m_bIgnoredByCops || m_bIgnoredByEveryone) return 0; switch (m_nWantedLevel) { case 3: case 4: return 1; case 5: case 6: return 1; default: return 0; } } void CWanted::SetWantedLevel(int32 level) { if (level > MaximumWantedLevel) level = MaximumWantedLevel; ClearQdCrimes(); switch (level) { case 0: m_nChaos = 0; break; case 1: m_nChaos = 70; break; case 2: m_nChaos = 200; break; case 3: m_nChaos = 570; break; case 4: m_nChaos = 1220; break; case 5: m_nChaos = 2420; break; case 6: m_nChaos = 4820; break; default: break; } UpdateWantedLevel(); } void CWanted::SetWantedLevelNoDrop(int32 level) { if (m_nWantedLevel < m_nMinWantedLevel) SetWantedLevel(m_nMinWantedLevel); if (level > m_nWantedLevel) SetWantedLevel(level); } void CWanted::CheatWantedLevel(int32 level) { SetWantedLevel(level); UpdateWantedLevel(); } void CWanted::SetMaximumWantedLevel(int32 level) { switch(level){ case 0: nMaximumWantedLevel = 0; MaximumWantedLevel = 0; break; case 1: nMaximumWantedLevel = 115; MaximumWantedLevel = 1; break; case 2: nMaximumWantedLevel = 365; MaximumWantedLevel = 2; break; case 3: nMaximumWantedLevel = 875; MaximumWantedLevel = 3; break; case 4: nMaximumWantedLevel = 1800; MaximumWantedLevel = 4; break; case 5: nMaximumWantedLevel = 3600; MaximumWantedLevel = 5; break; case 6: nMaximumWantedLevel = 7200; MaximumWantedLevel = 6; break; } } void CWanted::RegisterCrime(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare) { AddCrimeToQ(type, id, coors, false, policeDoesntCare); } void CWanted::RegisterCrime_Immediately(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare) { #ifdef FIX_SIGNIFICANT_BUGS if(!AddCrimeToQ(type, id, coors, true, policeDoesntCare)) #else if(!AddCrimeToQ(type, id, coors, false, policeDoesntCare)) #endif ReportCrimeNow(type, coors, policeDoesntCare); } void CWanted::ClearQdCrimes() { for (int i = 0; i < 16; i++) m_aCrimes[i].m_nType = CRIME_NONE; } // returns whether the crime had been reported already bool CWanted::AddCrimeToQ(eCrimeType type, int32 id, const CVector &coors, bool reported, bool policeDoesntCare) { int i; for(i = 0; i < 16; i++) if(m_aCrimes[i].m_nType == type && m_aCrimes[i].m_nId == id){ if(m_aCrimes[i].m_bReported) return true; if(reported) m_aCrimes[i].m_bReported = reported; return false; } for(i = 0; i < 16; i++) if(m_aCrimes[i].m_nType == CRIME_NONE) break; if(i < 16){ m_aCrimes[i].m_nType = type; m_aCrimes[i].m_nId = id; m_aCrimes[i].m_vecPosn = coors; m_aCrimes[i].m_nTime = CTimer::GetTimeInMilliseconds(); m_aCrimes[i].m_bReported = reported; m_aCrimes[i].m_bPoliceDoesntCare = policeDoesntCare; } return false; } void CWanted::ReportCrimeNow(eCrimeType type, const CVector &coors, bool policeDoesntCare) { float sensitivity, chaos; int wantedLevelDrop; if(CDarkel::FrenzyOnGoing()) sensitivity = m_fCrimeSensitivity*0.3f; else sensitivity = m_fCrimeSensitivity; wantedLevelDrop = Min(CCullZones::GetWantedLevelDrop(), 100); chaos = (1.0f - wantedLevelDrop/100.0f) * sensitivity; if (policeDoesntCare) chaos *= 0.333f; switch(type){ case CRIME_POSSESSION_GUN: break; case CRIME_HIT_PED: m_nChaos += 5.0f*chaos; break; case CRIME_HIT_COP: m_nChaos += 45.0f*chaos; break; case CRIME_SHOOT_PED: m_nChaos += 30.0f*chaos; break; case CRIME_SHOOT_COP: m_nChaos += 80.0f*chaos; break; case CRIME_STEAL_CAR: m_nChaos += 15.0f*chaos; break; case CRIME_RUN_REDLIGHT: m_nChaos += 10.0f*chaos; break; case CRIME_RECKLESS_DRIVING: m_nChaos += 5.0f*chaos; break; case CRIME_SPEEDING: m_nChaos += 5.0f*chaos; break; case CRIME_RUNOVER_PED: m_nChaos += 18.0f*chaos; break; case CRIME_RUNOVER_COP: m_nChaos += 80.0f*chaos; break; case CRIME_SHOOT_HELI: m_nChaos += 400.0f*chaos; break; case CRIME_PED_BURNED: m_nChaos += 20.0f*chaos; break; case CRIME_COP_BURNED: m_nChaos += 80.0f*chaos; break; case CRIME_VEHICLE_BURNED: m_nChaos += 20.0f*chaos; break; case CRIME_DESTROYED_CESSNA: m_nChaos += 500.0f*chaos; break; case CRIME_EXPLOSION: m_nChaos += 25.0f * chaos; break; case CRIME_HIT_PED_NASTYWEAPON: m_nChaos += 35.0f * chaos; break; case CRIME_HIT_COP_NASTYWEAPON: m_nChaos += 100.0f * chaos; break; default: // Error("Undefined crime type, RegisterCrime, Crime.cpp"); // different file for some reason Error("Undefined crime type, RegisterCrime, Wanted.cpp"); } m_nChaos = Max(m_nChaos, m_nMinChaos); DMAudio.ReportCrime(type, coors); UpdateWantedLevel(); } void CWanted::UpdateWantedLevel() { int32 CurrWantedLevel = m_nWantedLevel; if (m_nChaos > nMaximumWantedLevel) m_nChaos = nMaximumWantedLevel; if (m_nChaos >= 0 && m_nChaos < 50) { if (m_nWantedLevel == 1) ++CStats::WantedStarsEvaded; m_nWantedLevel = 0; m_MaximumLawEnforcerVehicles = 0; m_MaxCops = 0; m_RoadblockDensity = 0; } else if (m_nChaos >= 50 && m_nChaos < 180) { CStats::WantedStarsAttained += 1 - m_nWantedLevel; m_nWantedLevel = 1; m_MaximumLawEnforcerVehicles = 1; m_MaxCops = 1; m_RoadblockDensity = 0; } else if (m_nChaos >= 180 && m_nChaos < 550) { CStats::WantedStarsAttained += 2 - m_nWantedLevel; m_nWantedLevel = 2; m_MaximumLawEnforcerVehicles = 2; m_MaxCops = 3; m_RoadblockDensity = 0; } else if (m_nChaos >= 550 && m_nChaos < 1200) { CStats::WantedStarsAttained += 3 - m_nWantedLevel; m_nWantedLevel = 3; m_MaximumLawEnforcerVehicles = 2; m_MaxCops = 4; m_RoadblockDensity = 12; } else if (m_nChaos >= 1200 && m_nChaos < 2400) { CStats::WantedStarsAttained += 4 - m_nWantedLevel; m_nWantedLevel = 4; m_MaximumLawEnforcerVehicles = 2; m_MaxCops = 6; m_RoadblockDensity = 18; } else if (m_nChaos >= 2400 && m_nChaos < 4800) { CStats::WantedStarsAttained += 5 - m_nWantedLevel; m_nWantedLevel = 5; m_MaximumLawEnforcerVehicles = 3; m_MaxCops = 8; m_RoadblockDensity = 24; } else if (m_nChaos >= 4800) { CStats::WantedStarsAttained += 6 - m_nWantedLevel; m_nWantedLevel = 6; m_MaximumLawEnforcerVehicles = 3; m_MaxCops = 10; m_RoadblockDensity = 30; } if (CurrWantedLevel != m_nWantedLevel) m_nLastWantedLevelChange = CTimer::GetTimeInMilliseconds(); } int32 CWanted::WorkOutPolicePresence(CVector posn, float radius) { int i; CPed *ped; CVehicle *vehicle; int numPolice = 0; i = CPools::GetPedPool()->GetSize(); while(--i >= 0){ ped = CPools::GetPedPool()->GetSlot(i); if(ped && IsPolicePedModel(ped->GetModelIndex()) && (posn - ped->GetPosition()).Magnitude() < radius) numPolice++; } i = CPools::GetVehiclePool()->GetSize(); while(--i >= 0){ vehicle = CPools::GetVehiclePool()->GetSlot(i); if(vehicle && vehicle->bIsLawEnforcer && IsPoliceVehicleModel(vehicle->GetModelIndex()) && vehicle != FindPlayerVehicle() && vehicle->GetStatus() != STATUS_ABANDONED && vehicle->GetStatus() != STATUS_WRECKED && (posn - vehicle->GetPosition()).Magnitude() < radius) numPolice++; } return numPolice; } void CWanted::Update(void) { if (CTimer::GetTimeInMilliseconds() > m_nLastTimeSuspended + 20000) { m_nMinChaos = 0; m_nMinWantedLevel = 0; } if (CTimer::GetTimeInMilliseconds() - m_nLastUpdateTime > 1000) { if (m_nWantedLevel > 1) { m_nLastUpdateTime = CTimer::GetTimeInMilliseconds(); } else { float radius = 18.0f; CVector playerPos = FindPlayerCoors(); if (WorkOutPolicePresence(playerPos, radius) == 0) { m_nLastUpdateTime = CTimer::GetTimeInMilliseconds(); m_nChaos = Max(0, m_nChaos - 1); UpdateWantedLevel(); } } UpdateCrimesQ(); bool orderMessedUp = false; int currCopNum = 0; bool foundEmptySlot = false; for (int i = 0; i < ARRAY_SIZE(m_pCops); i++) { if (m_pCops[i]) { ++currCopNum; if (foundEmptySlot) orderMessedUp = true; } else { foundEmptySlot = true; } } if (currCopNum != m_CurrentCops) { printf("CopPursuit total messed up: re-setting\n"); m_CurrentCops = currCopNum; } if (orderMessedUp) { printf("CopPursuit pointer list messed up: re-sorting\n"); bool fixed = true; for (int i = 0; i < ARRAY_SIZE(m_pCops); i++) { if (!m_pCops[i]) { for (int j = i; j < ARRAY_SIZE(m_pCops); j++) { if (m_pCops[j]) { m_pCops[i] = m_pCops[j]; m_pCops[j] = nil; fixed = false; break; } } if (fixed) break; } } } } } void CWanted::ResetPolicePursuit(void) { for(int i = 0; i < ARRAY_SIZE(m_pCops); i++) { CCopPed *cop = m_pCops[i]; if (!cop) continue; cop->m_bIsInPursuit = false; cop->m_objective = OBJECTIVE_NONE; cop->m_prevObjective = OBJECTIVE_NONE; cop->m_nLastPedState = PED_NONE; if (!cop->DyingOrDead()) { cop->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); } m_pCops[i] = nil; } m_CurrentCops = 0; } void CWanted::Reset(void) { ResetPolicePursuit(); Initialise(); } void CWanted::UpdateCrimesQ(void) { for(int i = 0; i < ARRAY_SIZE(m_aCrimes); i++) { CCrimeBeingQd &crime = m_aCrimes[i]; if (crime.m_nType != CRIME_NONE) { if (CTimer::GetTimeInMilliseconds() > crime.m_nTime + 500 && !crime.m_bReported) { ReportCrimeNow(crime.m_nType, crime.m_vecPosn, crime.m_bPoliceDoesntCare); crime.m_bReported = true; } if (CTimer::GetTimeInMilliseconds() > crime.m_nTime + 10000) crime.m_nType = CRIME_NONE; } } } void CWanted::Suspend(void) { CStats::WantedStarsEvaded += m_nWantedLevel; m_nMinChaos = m_nChaos; m_nMinWantedLevel = m_nWantedLevel; m_nLastTimeSuspended = CTimer::GetTimeInMilliseconds(); m_nChaos = 0; m_nWantedLevel = 0; ResetPolicePursuit(); } ================================================ FILE: src/core/Wanted.h ================================================ #pragma once #include "Crime.h" class CEntity; class CCopPed; class CWanted { public: int32 m_nChaos; int32 m_nMinChaos; int32 m_nLastUpdateTime; uint32 m_nLastWantedLevelChange; uint32 m_nLastTimeSuspended; float m_fCrimeSensitivity; uint8 m_CurrentCops; uint8 m_MaxCops; uint8 m_MaximumLawEnforcerVehicles; uint8 m_CopsBeatingSuspect; int16 m_RoadblockDensity; uint8 m_bIgnoredByCops : 1; uint8 m_bIgnoredByEveryone : 1; uint8 m_bSwatRequired : 1; uint8 m_bFbiRequired : 1; uint8 m_bArmyRequired : 1; int32 m_nWantedLevel; int32 m_nMinWantedLevel; CCrimeBeingQd m_aCrimes[16]; CCopPed *m_pCops[10]; static int32 MaximumWantedLevel; static int32 nMaximumWantedLevel; public: void Initialise(); bool AreMiamiViceRequired(); bool AreSwatRequired(); bool AreFbiRequired(); bool AreArmyRequired(); int32 NumOfHelisRequired(); void SetWantedLevel(int32); void SetWantedLevelNoDrop(int32 level); int32 GetWantedLevel() { return m_nWantedLevel; } void CheatWantedLevel(int32 level); void RegisterCrime(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare); void RegisterCrime_Immediately(eCrimeType type, const CVector &coors, uint32 id, bool policeDoesntCare); void ClearQdCrimes(); bool AddCrimeToQ(eCrimeType type, int32 id, const CVector &pos, bool reported, bool policeDoesntCare); void ReportCrimeNow(eCrimeType type, const CVector &coors, bool policeDoesntCare); void UpdateWantedLevel(); void Reset(); void ResetPolicePursuit(); void UpdateCrimesQ(); void Update(); void Suspend(); bool IsIgnored(void) { return m_bIgnoredByCops || m_bIgnoredByEveryone; } static int32 WorkOutPolicePresence(CVector posn, float radius); static void SetMaximumWantedLevel(int32 level); }; VALIDATE_SIZE(CWanted, 0x204); ================================================ FILE: src/core/World.cpp ================================================ #include "common.h" #include "Camera.h" #include "CarCtrl.h" #include "CopPed.h" #include "CutsceneMgr.h" #include "DMAudio.h" #include "EventList.h" #include "Explosion.h" #include "Fire.h" #include "Garages.h" #include "Glass.h" #include "Messages.h" #include "ModelIndices.h" #include "ParticleObject.h" #include "Pickups.h" #include "Population.h" #include "ProjectileInfo.h" #include "Record.h" #include "References.h" #include "Replay.h" #include "RpAnimBlend.h" #include "Shadows.h" #include "TempColModels.h" #include "WaterLevel.h" #include "World.h" #define OBJECT_REPOSITION_OFFSET_Z 2.0f CColPoint gaTempSphereColPoints[MAX_COLLISION_POINTS]; CPtrList CWorld::ms_bigBuildingsList[NUM_LEVELS]; CPtrList CWorld::ms_listMovingEntityPtrs; CSector CWorld::ms_aSectors[NUMSECTORS_Y][NUMSECTORS_X]; uint16 CWorld::ms_nCurrentScanCode; uint8 CWorld::PlayerInFocus; CPlayerInfo CWorld::Players[NUMPLAYERS]; bool CWorld::bNoMoreCollisionTorque; CEntity *CWorld::pIgnoreEntity; bool CWorld::bIncludeDeadPeds; bool CWorld::bSecondShift; bool CWorld::bForceProcessControl; bool CWorld::bProcessCutsceneOnly; bool CWorld::bDoingCarCollisions; bool CWorld::bIncludeCarTyres; bool CWorld::bIncludeBikers; CColPoint CWorld::m_aTempColPts[MAX_COLLISION_POINTS]; void CWorld::Initialise() { pIgnoreEntity = nil; bDoingCarCollisions = false; bSecondShift = false; bNoMoreCollisionTorque = false; bProcessCutsceneOnly = false; bIncludeDeadPeds = false; bForceProcessControl = false; bIncludeCarTyres = false; bIncludeBikers = false; } void CWorld::Add(CEntity *ent) { if(ent->IsVehicle() || ent->IsPed()) DMAudio.SetEntityStatus(((CPhysical *)ent)->m_audioEntityId, true); if(ent->bIsBIGBuilding) ms_bigBuildingsList[ent->m_level].InsertItem(ent); else ent->Add(); if(ent->IsBuilding() || ent->IsDummy()) return; if(!ent->GetIsStatic()) ((CPhysical *)ent)->AddToMovingList(); } void CWorld::Remove(CEntity *ent) { if(ent->IsVehicle() || ent->IsPed()) DMAudio.SetEntityStatus(((CPhysical *)ent)->m_audioEntityId, false); if(ent->bIsBIGBuilding) ms_bigBuildingsList[ent->m_level].RemoveItem(ent); else ent->Remove(); if(ent->IsBuilding() || ent->IsDummy()) return; if(!ent->GetIsStatic()) ((CPhysical *)ent)->RemoveFromMovingList(); } void CWorld::ClearScanCodes(void) { CPtrNode *node; for(int i = 0; i < NUMSECTORS_Y; i++) for(int j = 0; j < NUMSECTORS_X; j++) { CSector *s = &ms_aSectors[i][j]; for(node = s->m_lists[ENTITYLIST_BUILDINGS].first; node; node = node->next) ((CEntity *)node->item)->m_scanCode = 0; for(node = s->m_lists[ENTITYLIST_VEHICLES].first; node; node = node->next) ((CEntity *)node->item)->m_scanCode = 0; for(node = s->m_lists[ENTITYLIST_PEDS].first; node; node = node->next) ((CEntity *)node->item)->m_scanCode = 0; for(node = s->m_lists[ENTITYLIST_OBJECTS].first; node; node = node->next) ((CEntity *)node->item)->m_scanCode = 0; for(node = s->m_lists[ENTITYLIST_DUMMIES].first; node; node = node->next) ((CEntity *)node->item)->m_scanCode = 0; } } void CWorld::ClearExcitingStuffFromArea(const CVector &pos, float radius, bool bRemoveProjectilesAndTidyUpShadows) { CPedPool *pedPool = CPools::GetPedPool(); for(int32 i = 0; i < pedPool->GetSize(); i++) { CPed *pPed = pedPool->GetSlot(i); if(pPed && !pPed->IsPlayer() && pPed->CanBeDeleted() && CVector2D(pPed->GetPosition() - pos).MagnitudeSqr() < SQR(radius)) { CPopulation::RemovePed(pPed); } } CVehiclePool *VehiclePool = CPools::GetVehiclePool(); for(int32 i = 0; i < VehiclePool->GetSize(); i++) { CVehicle *pVehicle = VehiclePool->GetSlot(i); if(pVehicle && CVector2D(pVehicle->GetPosition() - pos).MagnitudeSqr() < SQR(radius) && !pVehicle->bIsLocked && pVehicle->CanBeDeleted()) { if(pVehicle->pDriver) { CPopulation::RemovePed(pVehicle->pDriver); pVehicle->pDriver = nil; } for(int32 j = 0; j < pVehicle->m_nNumMaxPassengers; ++j) { if(pVehicle->pPassengers[j]) { CPopulation::RemovePed(pVehicle->pPassengers[j]); pVehicle->pPassengers[j] = nil; --pVehicle->m_nNumPassengers; } } CCarCtrl::RemoveFromInterestingVehicleList(pVehicle); Remove(pVehicle); delete pVehicle; } } CObject::DeleteAllTempObjectsInArea(pos, radius); gFireManager.ExtinguishPoint(pos, radius); ExtinguishAllCarFiresInArea(pos, radius); CExplosion::RemoveAllExplosionsInArea(pos, radius); if(bRemoveProjectilesAndTidyUpShadows) { CProjectileInfo::RemoveAllProjectiles(); CShadows::TidyUpShadows(); } CPickups::RemoveUnnecessaryPickups(pos, radius); } bool CWorld::CameraToIgnoreThisObject(CEntity *ent) { if(CGarages::IsModelIndexADoor(ent->GetModelIndex())) return false; return ((CObject *)ent)->m_bCameraToAvoidThisObject != 1; } bool CWorld::ProcessLineOfSight(const CVector &point1, const CVector &point2, CColPoint &point, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough) { int x, xstart, xend; int y, ystart, yend; int y1, y2; float dist; AdvanceCurrentScanCode(); entity = nil; dist = 1.0f; xstart = GetSectorIndexX(point1.x); ystart = GetSectorIndexY(point1.y); xend = GetSectorIndexX(point2.x); yend = GetSectorIndexY(point2.y); #define LOSARGS \ CColLine(point1, point2), point, dist, entity, checkBuildings, checkVehicles, checkPeds, checkObjects, \ checkDummies, ignoreSeeThrough, ignoreSomeObjects, ignoreShootThrough if(xstart == xend && ystart == yend) { // Only one sector return ProcessLineOfSightSector(*GetSector(xstart, ystart), LOSARGS); } else if(xstart == xend) { // Only step in y if(ystart < yend) for(y = ystart; y <= yend; y++) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); else for(y = ystart; y >= yend; y--) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); return dist < 1.0f; } else if(ystart == yend) { // Only step in x if(xstart < xend) for(x = xstart; x <= xend; x++) ProcessLineOfSightSector(*GetSector(x, ystart), LOSARGS); else for(x = xstart; x >= xend; x--) ProcessLineOfSightSector(*GetSector(x, ystart), LOSARGS); return dist < 1.0f; } else { if(point1.x < point2.x) { // Step from left to right float m = (point2.y - point1.y) / (point2.x - point1.x); y1 = ystart; y2 = GetSectorIndexY((GetWorldX(xstart + 1) - point1.x) * m + point1.y); if(y1 < y2) for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); else for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); for(x = xstart + 1; x < xend; x++) { y1 = y2; y2 = GetSectorIndexY((GetWorldX(x + 1) - point1.x) * m + point1.y); if(y1 < y2) for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(x, y), LOSARGS); else for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(x, y), LOSARGS); } y1 = y2; y2 = yend; if(y1 < y2) for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(xend, y), LOSARGS); else for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(xend, y), LOSARGS); } else { // Step from right to left float m = (point2.y - point1.y) / (point2.x - point1.x); y1 = ystart; y2 = GetSectorIndexY((GetWorldX(xstart) - point1.x) * m + point1.y); if(y1 < y2) for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); else for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(xstart, y), LOSARGS); for(x = xstart - 1; x > xend; x--) { y1 = y2; y2 = GetSectorIndexY((GetWorldX(x) - point1.x) * m + point1.y); if(y1 < y2) for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(x, y), LOSARGS); else for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(x, y), LOSARGS); } y1 = y2; y2 = yend; if(y1 < y2) for(y = y1; y <= y2; y++) ProcessLineOfSightSector(*GetSector(xend, y), LOSARGS); else for(y = y1; y >= y2; y--) ProcessLineOfSightSector(*GetSector(xend, y), LOSARGS); } return dist < 1.0f; } #undef LOSARGS } bool CWorld::ProcessLineOfSightSector(CSector §or, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough) { float mindist = dist; bool deadPeds = !!bIncludeDeadPeds; bool bikers = !!bIncludeBikers; bIncludeDeadPeds = false; bIncludeBikers = false; if(checkBuildings) { ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_BUILDINGS], line, point, mindist, entity, ignoreSeeThrough, false, ignoreShootThrough); ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_BUILDINGS_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, false, ignoreShootThrough); } if(checkVehicles) { ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_VEHICLES], line, point, mindist, entity, ignoreSeeThrough, false, ignoreShootThrough); ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_VEHICLES_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, false, ignoreShootThrough); } if(checkPeds) { if(deadPeds) bIncludeDeadPeds = true; if(bikers) bIncludeBikers = true; ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_PEDS], line, point, mindist, entity, ignoreSeeThrough, false, ignoreShootThrough); ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_PEDS_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, false, ignoreShootThrough); bIncludeDeadPeds = false; bIncludeBikers = false; } if(checkObjects) { ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_OBJECTS], line, point, mindist, entity, ignoreSeeThrough, ignoreSomeObjects, ignoreShootThrough); ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_OBJECTS_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, ignoreSomeObjects, ignoreShootThrough); } if(checkDummies) { ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_DUMMIES], line, point, mindist, entity, ignoreSeeThrough, false, ignoreShootThrough); ProcessLineOfSightSectorList(sector.m_lists[ENTITYLIST_DUMMIES_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, false, ignoreShootThrough); } bIncludeDeadPeds = deadPeds; bIncludeBikers = bikers; if(mindist < dist) { dist = mindist; return true; } else return false; } bool CWorld::ProcessLineOfSightSectorList(CPtrList &list, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough) { bool deadPeds = false; bool bikers = false; bool carTyres = false; float mindist = dist; CPtrNode *node; CEntity *e; CColModel *colmodel; CColModel tyreCol; CColSphere tyreSpheres[6]; CColPoint tyreColPoint; float tyreDist; if(bIncludeCarTyres && list.first && ((CEntity*)list.first->item)->IsVehicle()){ carTyres = true; tyreCol.numTriangles = 0; tyreCol.numBoxes = 0; tyreCol.numLines = 0; tyreCol.spheres = tyreSpheres; tyreCol.numSpheres = ARRAY_SIZE(tyreSpheres); } if(list.first && bIncludeDeadPeds && ((CEntity *)list.first->item)->IsPed()) deadPeds = true; if(list.first && bIncludeBikers && ((CEntity *)list.first->item)->IsPed()) bikers = true; for(node = list.first; node; node = node->next) { e = (CEntity *)node->item; if(e->m_scanCode != GetCurrentScanCode() && e != pIgnoreEntity && (e->bUsesCollision || deadPeds || bikers) && !(ignoreSomeObjects && CameraToIgnoreThisObject(e))) { colmodel = nil; tyreDist = mindist; e->m_scanCode = GetCurrentScanCode(); if(e->IsPed()) { if(e->bUsesCollision || deadPeds && ((CPed *)e)->m_nPedState == PED_DEAD || bikers && ((CPed*)e)->InVehicle() && (((CPed*)e)->m_pMyVehicle->IsBike() || ((CPed*)e)->m_pMyVehicle->IsBoat())) { colmodel = ((CPedModelInfo *)CModelInfo::GetModelInfo(e->GetModelIndex()))->AnimatePedColModelSkinned(e->GetClump()); } else colmodel = nil; } else if(e->bUsesCollision) colmodel = CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel(); if(colmodel && CCollision::ProcessLineOfSight(line, e->GetMatrix(), *colmodel, point, mindist, ignoreSeeThrough, ignoreShootThrough)) entity = e; if(carTyres && ((CVehicle*)e)->SetUpWheelColModel(&tyreCol) && CCollision::ProcessLineOfSight(line, e->GetMatrix(), tyreCol, tyreColPoint, tyreDist, false, ignoreShootThrough)){ float dp1 = DotProduct(line.p1 - line.p0, e->GetRight()); float dp2 = DotProduct(point.point - e->GetPosition(), e->GetRight()); if(tyreDist < mindist || dp1 < -0.85f && dp2 > 0.0f || dp1 > 0.85f && dp2 < 0.0f){ mindist = tyreDist; point = tyreColPoint; entity = e; } } } } tyreCol.spheres = nil; if(mindist < dist) { dist = mindist; return true; } else return false; } bool CWorld::ProcessVerticalLine(const CVector &point1, float z2, CColPoint &point, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, CStoredCollPoly *poly) { AdvanceCurrentScanCode(); CVector point2(point1.x, point1.y, z2); int secX = GetSectorIndexX(point1.x); int secY = GetSectorIndexY(point1.y); secX = clamp(secX, 0, NUMSECTORS_X-1); secY = clamp(secY, 0, NUMSECTORS_Y-1); return ProcessVerticalLineSector(*GetSector(secX, secY), CColLine(point1, point2), point, entity, checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, ignoreSeeThrough, poly); } bool CWorld::ProcessVerticalLineSector(CSector §or, const CColLine &line, CColPoint &point, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, CStoredCollPoly *poly) { float mindist = 1.0f; if(checkBuildings) { ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_BUILDINGS], line, point, mindist, entity, ignoreSeeThrough, poly); ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_BUILDINGS_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, poly); } if(checkVehicles) { ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_VEHICLES], line, point, mindist, entity, ignoreSeeThrough, poly); ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_VEHICLES_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, poly); } if(checkPeds) { ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_PEDS], line, point, mindist, entity, ignoreSeeThrough, poly); ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_PEDS_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, poly); } if(checkObjects) { ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_OBJECTS], line, point, mindist, entity, ignoreSeeThrough, poly); ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_OBJECTS_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, poly); } if(checkDummies) { ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_DUMMIES], line, point, mindist, entity, ignoreSeeThrough, poly); ProcessVerticalLineSectorList(sector.m_lists[ENTITYLIST_DUMMIES_OVERLAP], line, point, mindist, entity, ignoreSeeThrough, poly); } return mindist < 1.0f; } bool CWorld::ProcessVerticalLineSectorList(CPtrList &list, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, bool ignoreSeeThrough, CStoredCollPoly *poly) { float mindist = dist; CPtrNode *node; CEntity *e; CColModel *colmodel; for(node = list.first; node; node = node->next) { e = (CEntity *)node->item; if(e->m_scanCode != GetCurrentScanCode() && e->bUsesCollision) { e->m_scanCode = GetCurrentScanCode(); colmodel = CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel(); if(CCollision::ProcessVerticalLine(line, e->GetMatrix(), *colmodel, point, mindist, ignoreSeeThrough, false, poly)) entity = e; } } if(mindist < dist) { dist = mindist; return true; } else return false; } bool CWorld::GetIsLineOfSightClear(const CVector &point1, const CVector &point2, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects) { int x, xstart, xend; int y, ystart, yend; int y1, y2; AdvanceCurrentScanCode(); xstart = GetSectorIndexX(point1.x); ystart = GetSectorIndexY(point1.y); xend = GetSectorIndexX(point2.x); yend = GetSectorIndexY(point2.y); #define LOSARGS \ CColLine(point1, point2), checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, \ ignoreSeeThrough, ignoreSomeObjects if(xstart == xend && ystart == yend) { // Only one sector return GetIsLineOfSightSectorClear(*GetSector(xstart, ystart), LOSARGS); } else if(xstart == xend) { // Only step in y if(ystart < yend) { for(y = ystart; y <= yend; y++) if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; } else { for(y = ystart; y >= yend; y--) if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; } } else if(ystart == yend) { // Only step in x if(xstart < xend) { for(x = xstart; x <= xend; x++) if(!GetIsLineOfSightSectorClear(*GetSector(x, ystart), LOSARGS)) return false; } else { for(x = xstart; x >= xend; x--) if(!GetIsLineOfSightSectorClear(*GetSector(x, ystart), LOSARGS)) return false; } } else { if(point1.x < point2.x) { // Step from left to right float m = (point2.y - point1.y) / (point2.x - point1.x); y1 = ystart; y2 = GetSectorIndexY((GetWorldX(xstart + 1) - point1.x) * m + point1.y); if(y1 < y2) { for(y = y1; y <= y2; y++) if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; } else { for(y = y1; y >= y2; y--) if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; } for(x = xstart + 1; x < xend; x++) { y1 = y2; y2 = GetSectorIndexY((GetWorldX(x + 1) - point1.x) * m + point1.y); if(y1 < y2) { for(y = y1; y <= y2; y++) if(!GetIsLineOfSightSectorClear(*GetSector(x, y), LOSARGS)) return false; } else { for(y = y1; y >= y2; y--) if(!GetIsLineOfSightSectorClear(*GetSector(x, y), LOSARGS)) return false; } } y1 = y2; y2 = yend; if(y1 < y2) { for(y = y1; y <= y2; y++) if(!GetIsLineOfSightSectorClear(*GetSector(xend, y), LOSARGS)) return false; } else { for(y = y1; y >= y2; y--) if(!GetIsLineOfSightSectorClear(*GetSector(xend, y), LOSARGS)) return false; } } else { // Step from right to left float m = (point2.y - point1.y) / (point2.x - point1.x); y1 = ystart; y2 = GetSectorIndexY((GetWorldX(xstart) - point1.x) * m + point1.y); if(y1 < y2) { for(y = y1; y <= y2; y++) if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; } else { for(y = y1; y >= y2; y--) if(!GetIsLineOfSightSectorClear(*GetSector(xstart, y), LOSARGS)) return false; } for(x = xstart - 1; x > xend; x--) { y1 = y2; y2 = GetSectorIndexY((GetWorldX(x) - point1.x) * m + point1.y); if(y1 < y2) { for(y = y1; y <= y2; y++) if(!GetIsLineOfSightSectorClear(*GetSector(x, y), LOSARGS)) return false; } else { for(y = y1; y >= y2; y--) if(!GetIsLineOfSightSectorClear(*GetSector(x, y), LOSARGS)) return false; } } y1 = y2; y2 = yend; if(y1 < y2) { for(y = y1; y <= y2; y++) if(!GetIsLineOfSightSectorClear(*GetSector(xend, y), LOSARGS)) return false; } else { for(y = y1; y >= y2; y--) if(!GetIsLineOfSightSectorClear(*GetSector(xend, y), LOSARGS)) return false; } } } return true; #undef LOSARGS } bool CWorld::GetIsLineOfSightSectorClear(CSector §or, const CColLine &line, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects) { if(checkBuildings) { if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_BUILDINGS], line, ignoreSeeThrough)) return false; if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_BUILDINGS_OVERLAP], line, ignoreSeeThrough)) return false; } if(checkVehicles) { if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_VEHICLES], line, ignoreSeeThrough)) return false; if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_VEHICLES_OVERLAP], line, ignoreSeeThrough)) return false; } if(checkPeds) { if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_PEDS], line, ignoreSeeThrough)) return false; if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_PEDS_OVERLAP], line, ignoreSeeThrough)) return false; } if(checkObjects) { if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_OBJECTS], line, ignoreSeeThrough, ignoreSomeObjects)) return false; if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_OBJECTS_OVERLAP], line, ignoreSeeThrough, ignoreSomeObjects)) return false; } if(checkDummies) { if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_DUMMIES], line, ignoreSeeThrough)) return false; if(!GetIsLineOfSightSectorListClear(sector.m_lists[ENTITYLIST_DUMMIES_OVERLAP], line, ignoreSeeThrough)) return false; } return true; } bool CWorld::GetIsLineOfSightSectorListClear(CPtrList &list, const CColLine &line, bool ignoreSeeThrough, bool ignoreSomeObjects) { CPtrNode *node; CEntity *e; CColModel *colmodel; for(node = list.first; node; node = node->next) { e = (CEntity *)node->item; if(e->m_scanCode != GetCurrentScanCode() && e->bUsesCollision) { e->m_scanCode = GetCurrentScanCode(); if(e != pIgnoreEntity && !(ignoreSomeObjects && CameraToIgnoreThisObject(e))) { colmodel = CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel(); if(CCollision::TestLineOfSight(line, e->GetMatrix(), *colmodel, ignoreSeeThrough, false)) return false; } } } return true; } void CWorld::FindObjectsInRangeSectorList(CPtrList &list, Const CVector ¢re, float radius, bool ignoreZ, int16 *numObjects, int16 lastObject, CEntity **objects) { float radiusSqr = radius * radius; float objDistSqr; for(CPtrNode *node = list.first; node; node = node->next) { CEntity *object = (CEntity *)node->item; if(object->m_scanCode != GetCurrentScanCode()) { object->m_scanCode = GetCurrentScanCode(); CVector diff = centre - object->GetPosition(); if(ignoreZ) objDistSqr = diff.MagnitudeSqr2D(); else objDistSqr = diff.MagnitudeSqr(); if(objDistSqr < radiusSqr && *numObjects < lastObject) { if(objects) { objects[*numObjects] = object; } (*numObjects)++; } } } } void CWorld::FindObjectsInRange(Const CVector ¢re, float radius, bool ignoreZ, int16 *numObjects, int16 lastObject, CEntity **objects, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies) { int minX = GetSectorIndexX(centre.x - radius); if(minX <= 0) minX = 0; int minY = GetSectorIndexY(centre.y - radius); if(minY <= 0) minY = 0; int maxX = GetSectorIndexX(centre.x + radius); if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; int maxY = GetSectorIndexY(centre.y + radius); if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; AdvanceCurrentScanCode(); *numObjects = 0; for(int curY = minY; curY <= maxY; curY++) { for(int curX = minX; curX <= maxX; curX++) { CSector *sector = GetSector(curX, curY); if(checkBuildings) { FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_BUILDINGS], centre, radius, ignoreZ, numObjects, lastObject, objects); FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], centre, radius, ignoreZ, numObjects, lastObject, objects); } if(checkVehicles) { FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_VEHICLES], centre, radius, ignoreZ, numObjects, lastObject, objects); FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], centre, radius, ignoreZ, numObjects, lastObject, objects); } if(checkPeds) { FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_PEDS], centre, radius, ignoreZ, numObjects, lastObject, objects); FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_PEDS_OVERLAP], centre, radius, ignoreZ, numObjects, lastObject, objects); } if(checkObjects) { FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_OBJECTS], centre, radius, ignoreZ, numObjects, lastObject, objects); FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], centre, radius, ignoreZ, numObjects, lastObject, objects); } if(checkDummies) { FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_DUMMIES], centre, radius, ignoreZ, numObjects, lastObject, objects); FindObjectsInRangeSectorList(sector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], centre, radius, ignoreZ, numObjects, lastObject, objects); } } } } void CWorld::FindObjectsOfTypeInRangeSectorList(uint32 modelId, CPtrList &list, const CVector &position, float radius, bool bCheck2DOnly, int16 *nEntitiesFound, int16 maxEntitiesToFind, CEntity **aEntities) { for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; if(pEntity->m_scanCode != GetCurrentScanCode()) { pEntity->m_scanCode = GetCurrentScanCode(); if (modelId == pEntity->GetModelIndex()) { float fMagnitude = 0.0f; if(bCheck2DOnly) fMagnitude = (position - pEntity->GetPosition()).MagnitudeSqr2D(); else fMagnitude = (position - pEntity->GetPosition()).MagnitudeSqr(); if(fMagnitude < radius * radius && *nEntitiesFound < maxEntitiesToFind) { if(aEntities) aEntities[*nEntitiesFound] = pEntity; ++*nEntitiesFound; } } } } } void CWorld::FindObjectsOfTypeInRange(uint32 modelId, const CVector &position, float radius, bool bCheck2DOnly, int16 *nEntitiesFound, int16 maxEntitiesToFind, CEntity **aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies) { AdvanceCurrentScanCode(); *nEntitiesFound = 0; const CVector2D vecSectorStartPos(position.x - radius, position.y - radius); const CVector2D vecSectorEndPos(position.x + radius, position.y + radius); const int32 nStartX = Max(GetSectorIndexX(vecSectorStartPos.x), 0); const int32 nStartY = Max(GetSectorIndexY(vecSectorStartPos.y), 0); #ifdef FIX_BUGS const int32 nEndX = Min(GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X - 1); const int32 nEndY = Min(GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y - 1); #else const int32 nEndX = Min(GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X); const int32 nEndY = Min(GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y); #endif for(int32 y = nStartY; y <= nEndY; y++) { for(int32 x = nStartX; x <= nEndX; x++) { CSector *pSector = GetSector(x, y); if(bBuildings) { FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_BUILDINGS], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); } if(bVehicles) { FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_VEHICLES], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); } if(bPeds) { FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_PEDS], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); } if(bObjects) { FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_OBJECTS], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); } if(bDummies) { FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_DUMMIES], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsOfTypeInRangeSectorList( modelId, pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], position, radius, bCheck2DOnly, nEntitiesFound, maxEntitiesToFind, aEntities); } } } } CEntity * CWorld::TestSphereAgainstWorld(CVector centre, float radius, CEntity *entityToIgnore, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSomeObjects) { CEntity *foundE = nil; int minX = GetSectorIndexX(centre.x - radius); if(minX <= 0) minX = 0; int minY = GetSectorIndexY(centre.y - radius); if(minY <= 0) minY = 0; int maxX = GetSectorIndexX(centre.x + radius); #ifdef FIX_BUGS if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; #else if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; #endif int maxY = GetSectorIndexY(centre.y + radius); #ifdef FIX_BUGS if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; #else if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; #endif AdvanceCurrentScanCode(); for(int curY = minY; curY <= maxY; curY++) { for(int curX = minX; curX <= maxX; curX++) { CSector *sector = GetSector(curX, curY); if(checkBuildings) { foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_BUILDINGS], centre, radius, entityToIgnore, false); if(foundE) return foundE; foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], centre, radius, entityToIgnore, false); if(foundE) return foundE; } if(checkVehicles) { foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_VEHICLES], centre, radius, entityToIgnore, false); if(foundE) return foundE; foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], centre, radius, entityToIgnore, false); if(foundE) return foundE; } if(checkPeds) { foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_PEDS], centre, radius, entityToIgnore, false); if(foundE) return foundE; foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_PEDS_OVERLAP], centre, radius, entityToIgnore, false); if(foundE) return foundE; } if(checkObjects) { foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_OBJECTS], centre, radius, entityToIgnore, ignoreSomeObjects); if(foundE) return foundE; foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], centre, radius, entityToIgnore, ignoreSomeObjects); if(foundE) return foundE; } if(checkDummies) { foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_DUMMIES], centre, radius, entityToIgnore, false); if(foundE) return foundE; foundE = TestSphereAgainstSectorList(sector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], centre, radius, entityToIgnore, false); if(foundE) return foundE; } } } return foundE; } CEntity * CWorld::TestSphereAgainstSectorList(CPtrList &list, CVector spherePos, float radius, CEntity *entityToIgnore, bool ignoreSomeObjects) { static CColModel OurColModel; CColSphere sphere; OurColModel.boundingSphere.center.x = 0.0f; OurColModel.boundingSphere.center.y = 0.0f; OurColModel.boundingSphere.center.z = 0.0f; OurColModel.boundingSphere.radius = radius; OurColModel.boundingBox.min.x = -radius; OurColModel.boundingBox.min.y = -radius; OurColModel.boundingBox.min.z = -radius; OurColModel.boundingBox.max.x = radius; OurColModel.boundingBox.max.y = radius; OurColModel.boundingBox.max.z = radius; OurColModel.numSpheres = 1; sphere.Set(radius, CVector(0.0f, 0.0f, 0.0f)); OurColModel.spheres = &sphere; OurColModel.numLines = 0; OurColModel.numBoxes = 0; OurColModel.numTriangles = 0; OurColModel.ownsCollisionVolumes = false; CMatrix sphereMat; sphereMat.SetTranslate(spherePos); for(CPtrNode *node = list.first; node; node = node->next) { CEntity *e = (CEntity *)node->item; if(e->m_scanCode != GetCurrentScanCode()) { e->m_scanCode = GetCurrentScanCode(); if(e != entityToIgnore && e->bUsesCollision && !(ignoreSomeObjects && CameraToIgnoreThisObject(e))) { CVector diff = spherePos - e->GetBoundCentre(); float distance = diff.Magnitude(); if(e->GetBoundRadius() + radius > distance) { CColModel *eCol = CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel(); int collidedSpheres = CCollision::ProcessColModels(sphereMat, OurColModel, e->GetMatrix(), *eCol, gaTempSphereColPoints, nil, nil); if(collidedSpheres != 0 || (e->IsVehicle() && ((CVehicle *)e)->m_vehType == VEHICLE_TYPE_CAR && e->GetModelIndex() != MI_DODO && radius + eCol->boundingBox.max.x > distance)) { return e; } } } } } return nil; } float CWorld::FindGroundZForCoord(float x, float y) { CColPoint point; CEntity *ent; if(ProcessVerticalLine(CVector(x, y, 1000.0f), -1000.0f, point, ent, true, false, false, false, true, false, nil)) return point.point.z; else return 20.0f; } float CWorld::FindGroundZFor3DCoord(float x, float y, float z, bool *found) { CColPoint point; CEntity *ent; if(ProcessVerticalLine(CVector(x, y, z), -1000.0f, point, ent, true, false, false, false, false, false, nil)) { if(found) *found = true; return point.point.z; } else { if(found) *found = false; return 0.0f; } } float CWorld::FindRoofZFor3DCoord(float x, float y, float z, bool *found) { CColPoint point; CEntity *ent; if(ProcessVerticalLine(CVector(x, y, z), 1000.0f, point, ent, true, false, false, false, true, false, nil)) { if(found) *found = true; return point.point.z; } else { if(found == nil) printf("THERE IS NO MAP BELOW THE FOLLOWING COORS:%f %f %f. (FindGroundZFor3DCoord)\n", x, y, z); if(found) *found = false; return 20.0f; } } void CWorld::RemoveReferencesToDeletedObject(CEntity *pDeletedObject) { int32 i = CPools::GetPedPool()->GetSize(); while(--i >= 0) { CPed *pPed = CPools::GetPedPool()->GetSlot(i); if(pPed && pPed != pDeletedObject) { pPed->RemoveRefsToEntity(pDeletedObject); if(pPed->m_pCurrentPhysSurface == pDeletedObject) pPed->m_pCurrentPhysSurface = nil; } } i = CPools::GetVehiclePool()->GetSize(); while(--i >= 0) { CVehicle *pVehicle = CPools::GetVehiclePool()->GetSlot(i); if(pVehicle && pVehicle != pDeletedObject) { pVehicle->RemoveRefsToEntity(pDeletedObject); pVehicle->RemoveRefsToVehicle(pDeletedObject); } } i = CPools::GetObjectPool()->GetSize(); while(--i >= 0) { CObject *pObject = CPools::GetObjectPool()->GetSlot(i); if(pObject && pObject != pDeletedObject) { pObject->RemoveRefsToEntity(pDeletedObject); } } } void CWorld::FindObjectsKindaColliding(const CVector &position, float radius, bool bCheck2DOnly, int16 *nCollidingEntities, int16 maxEntitiesToFind, CEntity **aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies) { AdvanceCurrentScanCode(); *nCollidingEntities = 0; const CVector2D vecSectorStartPos(position.x - radius, position.y - radius); const CVector2D vecSectorEndPos(position.x + radius, position.y + radius); const int32 nStartX = Max(GetSectorIndexX(vecSectorStartPos.x), 0); const int32 nStartY = Max(GetSectorIndexY(vecSectorStartPos.y), 0); #ifdef FIX_BUGS const int32 nEndX = Min(GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X - 1); const int32 nEndY = Min(GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y - 1); #else const int32 nEndX = Min(GetSectorIndexX(vecSectorEndPos.x), NUMSECTORS_X); const int32 nEndY = Min(GetSectorIndexY(vecSectorEndPos.y), NUMSECTORS_Y); #endif for(int32 y = nStartY; y <= nEndY; y++) { for(int32 x = nStartX; x <= nEndX; x++) { CSector *pSector = GetSector(x, y); if(bBuildings) { FindObjectsKindaCollidingSectorList( pSector->m_lists[ENTITYLIST_BUILDINGS], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); FindObjectsKindaCollidingSectorList( pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); } if(bVehicles) { FindObjectsKindaCollidingSectorList( pSector->m_lists[ENTITYLIST_VEHICLES], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); FindObjectsKindaCollidingSectorList( pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); } if(bPeds) { FindObjectsKindaCollidingSectorList(pSector->m_lists[ENTITYLIST_PEDS], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); FindObjectsKindaCollidingSectorList( pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); } if(bObjects) { FindObjectsKindaCollidingSectorList( pSector->m_lists[ENTITYLIST_OBJECTS], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); FindObjectsKindaCollidingSectorList( pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); } if(bDummies) { FindObjectsKindaCollidingSectorList( pSector->m_lists[ENTITYLIST_DUMMIES], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); FindObjectsKindaCollidingSectorList( pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], position, radius, bCheck2DOnly, nCollidingEntities, maxEntitiesToFind, aEntities); } } } } void CWorld::FindObjectsKindaCollidingSectorList(CPtrList &list, const CVector &position, float radius, bool bCheck2DOnly, int16 *nCollidingEntities, int16 maxEntitiesToFind, CEntity **aEntities) { for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; if(pEntity->m_scanCode != GetCurrentScanCode()) { pEntity->m_scanCode = GetCurrentScanCode(); float fMagnitude = 0.0f; if(bCheck2DOnly) fMagnitude = (position - pEntity->GetPosition()).Magnitude2D(); else fMagnitude = (position - pEntity->GetPosition()).Magnitude(); if(pEntity->GetBoundRadius() + radius > fMagnitude && *nCollidingEntities < maxEntitiesToFind) { if(aEntities) aEntities[*nCollidingEntities] = pEntity; ++*nCollidingEntities; } } } } void CWorld::FindObjectsIntersectingCube(const CVector &vecStartPos, const CVector &vecEndPos, int16 *nIntersecting, int16 maxEntitiesToFind, CEntity **aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies) { AdvanceCurrentScanCode(); *nIntersecting = 0; const int32 nStartX = Max(GetSectorIndexX(vecStartPos.x), 0); const int32 nStartY = Max(GetSectorIndexY(vecStartPos.y), 0); #ifdef FIX_BUGS const int32 nEndX = Min(GetSectorIndexX(vecStartPos.x), NUMSECTORS_X - 1); const int32 nEndY = Min(GetSectorIndexY(vecStartPos.y), NUMSECTORS_Y - 1); #else const int32 nEndX = Min(GetSectorIndexX(vecStartPos.x), NUMSECTORS_X); const int32 nEndY = Min(GetSectorIndexY(vecStartPos.y), NUMSECTORS_Y); #endif for(int32 y = nStartY; y <= nEndY; y++) { for(int32 x = nStartX; x <= nEndX; x++) { CSector *pSector = GetSector(x, y); if(bBuildings) { FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_BUILDINGS], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); FindObjectsIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); } if(bVehicles) { FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_VEHICLES], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); FindObjectsIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); } if(bPeds) { FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_PEDS], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); } if(bObjects) { FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_OBJECTS], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); FindObjectsIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); } if(bDummies) { FindObjectsIntersectingCubeSectorList(pSector->m_lists[ENTITYLIST_DUMMIES], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); FindObjectsIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities); } } } } void CWorld::FindObjectsIntersectingCubeSectorList(CPtrList &list, const CVector &vecStartPos, const CVector &vecEndPos, int16 *nIntersecting, int16 maxEntitiesToFind, CEntity **aEntities) { for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; if(pEntity->m_scanCode != GetCurrentScanCode()) { pEntity->m_scanCode = GetCurrentScanCode(); float fRadius = pEntity->GetBoundRadius(); const CVector &entityPos = pEntity->GetPosition(); if(fRadius + entityPos.x >= vecStartPos.x && entityPos.x - fRadius <= vecEndPos.x && fRadius + entityPos.y >= vecStartPos.y && entityPos.y - fRadius <= vecEndPos.y && fRadius + entityPos.z >= vecStartPos.z && entityPos.z - fRadius <= vecEndPos.z && *nIntersecting < maxEntitiesToFind) { if(aEntities) aEntities[*nIntersecting] = pEntity; ++*nIntersecting; } } } } void CWorld::FindObjectsIntersectingAngledCollisionBox(const CBox &boundingBox, const CMatrix &matrix, const CVector &position, float fStartX, float fStartY, float fEndX, float fEndY, int16 *nEntitiesFound, int16 maxEntitiesToFind, CEntity **aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies) { AdvanceCurrentScanCode(); *nEntitiesFound = 0; const int32 nStartX = Max(GetSectorIndexX(fStartX), 0); const int32 nStartY = Max(GetSectorIndexY(fStartY), 0); #ifdef FIX_BUGS const int32 nEndX = Min(GetSectorIndexX(fEndX), NUMSECTORS_X - 1); const int32 nEndY = Min(GetSectorIndexY(fEndY), NUMSECTORS_Y - 1); #else const int32 nEndX = Min(GetSectorIndexX(fEndX), NUMSECTORS_X); const int32 nEndY = Min(GetSectorIndexY(fEndY), NUMSECTORS_Y); #endif for(int32 y = nStartY; y <= nEndY; y++) { for(int32 x = nStartX; x <= nEndX; x++) { CSector *pSector = GetSector(x, y); if(bBuildings) { FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_BUILDINGS], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); } if(bVehicles) { FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_VEHICLES], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); } if(bPeds) { FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_PEDS], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); } if(bObjects) { FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_OBJECTS], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); } if(bDummies) { FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_DUMMIES], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); FindObjectsIntersectingAngledCollisionBoxSectorList( pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP], boundingBox, matrix, position, nEntitiesFound, maxEntitiesToFind, aEntities); } } } } void CWorld::FindObjectsIntersectingAngledCollisionBoxSectorList(CPtrList &list, const CBox &boundingBox, const CMatrix &matrix, const CVector &position, int16 *nEntitiesFound, int16 maxEntitiesToFind, CEntity **aEntities) { for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; if(pEntity->m_scanCode != GetCurrentScanCode()) { pEntity->m_scanCode = GetCurrentScanCode(); CColSphere sphere; CVector vecDistance = pEntity->GetPosition() - position; sphere.radius = pEntity->GetBoundRadius(); sphere.center = Multiply3x3(vecDistance, matrix); if(CCollision::TestSphereBox(sphere, boundingBox) && *nEntitiesFound < maxEntitiesToFind) { if(aEntities) aEntities[*nEntitiesFound] = pEntity; ++*nEntitiesFound; } } } } void CWorld::FindMissionEntitiesIntersectingCube(const CVector &vecStartPos, const CVector &vecEndPos, int16 *nIntersecting, int16 maxEntitiesToFind, CEntity **aEntities, bool bVehicles, bool bPeds, bool bObjects) { AdvanceCurrentScanCode(); *nIntersecting = 0; const int32 nStartX = Max(GetSectorIndexX(vecStartPos.x), 0); const int32 nStartY = Max(GetSectorIndexY(vecStartPos.y), 0); #ifdef FIX_BUGS const int32 nEndX = Min(GetSectorIndexX(vecEndPos.x), NUMSECTORS_X - 1); const int32 nEndY = Min(GetSectorIndexY(vecEndPos.y), NUMSECTORS_Y - 1); #else const int32 nEndX = Min(GetSectorIndexX(vecEndPos.x), NUMSECTORS_X); const int32 nEndY = Min(GetSectorIndexY(vecEndPos.y), NUMSECTORS_Y); #endif for(int32 y = nStartY; y <= nEndY; y++) { for(int32 x = nStartX; x <= nEndX; x++) { CSector *pSector = GetSector(x, y); if(bVehicles) { FindMissionEntitiesIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_VEHICLES], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities, true, false); FindMissionEntitiesIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities, true, false); } if(bPeds) { FindMissionEntitiesIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_PEDS], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities, false, true); FindMissionEntitiesIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities, false, true); } if(bObjects) { FindMissionEntitiesIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_OBJECTS], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities, false, false); FindMissionEntitiesIntersectingCubeSectorList( pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], vecStartPos, vecEndPos, nIntersecting, maxEntitiesToFind, aEntities, false, false); } } } } void CWorld::FindMissionEntitiesIntersectingCubeSectorList(CPtrList &list, const CVector &vecStartPos, const CVector &vecEndPos, int16 *nIntersecting, int16 maxEntitiesToFind, CEntity **aEntities, bool bIsVehicleList, bool bIsPedList) { for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; if(pEntity->m_scanCode != GetCurrentScanCode()) { pEntity->m_scanCode = GetCurrentScanCode(); bool bIsMissionEntity = false; if(bIsVehicleList) bIsMissionEntity = ((CVehicle *)pEntity)->VehicleCreatedBy == MISSION_VEHICLE; else if(bIsPedList) bIsMissionEntity = ((CPed *)pEntity)->CharCreatedBy == MISSION_CHAR; else bIsMissionEntity = ((CObject *)pEntity)->ObjectCreatedBy == MISSION_OBJECT; float fRadius = pEntity->GetBoundRadius(); const CVector &entityPos = pEntity->GetPosition(); if(bIsMissionEntity && fRadius + entityPos.x >= vecStartPos.x && entityPos.x - fRadius <= vecEndPos.x && fRadius + entityPos.y >= vecStartPos.y && entityPos.y - fRadius <= vecEndPos.y && fRadius + entityPos.z >= vecStartPos.z && entityPos.z - fRadius <= vecEndPos.z && *nIntersecting < maxEntitiesToFind) { if(aEntities) aEntities[*nIntersecting] = pEntity; ++*nIntersecting; } } } } void CWorld::ClearCarsFromArea(float x1, float y1, float z1, float x2, float y2, float z2) { CVehiclePool *pVehiclePool = CPools::GetVehiclePool(); for(int32 i = 0; i < pVehiclePool->GetSize(); i++) { CVehicle *pVehicle = CPools::GetVehiclePool()->GetSlot(i); if(pVehicle) { const CVector &position = pVehicle->GetPosition(); if(position.x >= x1 && position.x <= x2 && position.y >= y1 && position.y <= y2 && position.z >= z1 && position.z <= z2 && !pVehicle->bIsLocked && pVehicle->CanBeDeleted()) { if(pVehicle->pDriver) { CPopulation::RemovePed(pVehicle->pDriver); pVehicle->pDriver = nil; } for(int32 j = 0; j < pVehicle->m_nNumMaxPassengers; ++j) { if(pVehicle->pPassengers[j]) { CPopulation::RemovePed(pVehicle->pPassengers[j]); pVehicle->pPassengers[j] = nil; --pVehicle->m_nNumPassengers; } } CCarCtrl::RemoveFromInterestingVehicleList(pVehicle); Remove(pVehicle); delete pVehicle; } } } } void CWorld::ClearPedsFromArea(float x1, float y1, float z1, float x2, float y2, float z2) { CPedPool *pPedPool = CPools::GetPedPool(); for(int32 i = 0; i < pPedPool->GetSize(); i++) { CPed *pPed = CPools::GetPedPool()->GetSlot(i); if(pPed) { const CVector &position = pPed->GetPosition(); if(!pPed->IsPlayer() && pPed->CanBeDeleted() && position.x >= x1 && position.x <= x2 && position.y >= y1 && position.y <= y2 && position.z >= z1 && position.z <= z2) { CPopulation::RemovePed(pPed); } } } } void CWorld::CallOffChaseForArea(float x1, float y1, float x2, float y2) { AdvanceCurrentScanCode(); float fStartX = x1 - 10.0f; float fStartY = y1 - 10.0f; float fEndX = x2 + 10.0f; float fEndY = y2 + 10.0f; const int32 nStartX = Max(GetSectorIndexX(fStartX), 0); const int32 nStartY = Max(GetSectorIndexY(fStartY), 0); #ifdef FIX_BUGS const int32 nEndX = Min(GetSectorIndexX(fEndX), NUMSECTORS_X - 1); const int32 nEndY = Min(GetSectorIndexY(fEndY), NUMSECTORS_Y - 1); #else const int32 nEndX = Min(GetSectorIndexX(fEndX), NUMSECTORS_X); const int32 nEndY = Min(GetSectorIndexY(fEndY), NUMSECTORS_Y); #endif for(int32 y = nStartY; y <= nEndY; y++) { for(int32 x = nStartX; x <= nEndX; x++) { CSector *pSector = GetSector(x, y); CallOffChaseForAreaSectorListVehicles(pSector->m_lists[ENTITYLIST_VEHICLES], x1, y1, x2, y2, fStartX, fStartY, fEndX, fEndY); CallOffChaseForAreaSectorListVehicles(pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], x1, y1, x2, y2, fStartX, fStartY, fEndX, fEndY); CallOffChaseForAreaSectorListPeds(pSector->m_lists[ENTITYLIST_PEDS], x1, y1, x2, y2); CallOffChaseForAreaSectorListPeds(pSector->m_lists[ENTITYLIST_PEDS_OVERLAP], x1, y1, x2, y2); } } } void CWorld::CallOffChaseForAreaSectorListVehicles(CPtrList &list, float x1, float y1, float x2, float y2, float fStartX, float fStartY, float fEndX, float fEndY) { for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { CVehicle *pVehicle = (CVehicle *)pNode->item; if(pVehicle->m_scanCode != GetCurrentScanCode()) { pVehicle->m_scanCode = GetCurrentScanCode(); const CVector &vehiclePos = pVehicle->GetPosition(); uint8 carMission = pVehicle->AutoPilot.m_nCarMission; if(pVehicle != FindPlayerVehicle() && vehiclePos.x > fStartX && vehiclePos.x < fEndX && vehiclePos.y > fStartY && vehiclePos.y < fEndY && pVehicle->bIsLawEnforcer && (carMission == MISSION_RAMPLAYER_FARAWAY || carMission == MISSION_RAMPLAYER_CLOSE || carMission == MISSION_BLOCKPLAYER_FARAWAY || carMission == MISSION_BLOCKPLAYER_CLOSE)) { pVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; CColModel *pColModel = pVehicle->GetColModel(); bool bInsideSphere = false; for(int32 i = 0; i < pColModel->numSpheres; i++) { CVector pos = pVehicle->m_matrix * pColModel->spheres[i].center; float fRadius = pColModel->spheres[i].radius; if(pos.x + fRadius > x1 && pos.x - fRadius < x2 && pos.y + fRadius > y1 && pos.y - fRadius < y2) bInsideSphere = true; // Maybe break the loop when bInsideSphere is set to true? } if(bInsideSphere) { if(pVehicle->GetPosition().x <= (x1 + x2) * 0.5f) pVehicle->m_vecMoveSpeed.x = Min(pVehicle->m_vecMoveSpeed.x, 0.0f); else pVehicle->m_vecMoveSpeed.x = Max(pVehicle->m_vecMoveSpeed.x, 0.0f); if(pVehicle->GetPosition().y <= (y1 + y2) * 0.5f) pVehicle->m_vecMoveSpeed.y = Min(pVehicle->m_vecMoveSpeed.y, 0.0f); else pVehicle->m_vecMoveSpeed.y = Max(pVehicle->m_vecMoveSpeed.y, 0.0f); } } } } } void CWorld::CallOffChaseForAreaSectorListPeds(CPtrList &list, float x1, float y1, float x2, float y2) { for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { CPed *pPed = (CPed *)pNode->item; const CVector &pedPos = pPed->GetPosition(); if(pPed->m_scanCode != GetCurrentScanCode()) { pPed->m_scanCode = GetCurrentScanCode(); if(pPed != FindPlayerPed() && pPed->m_leader != FindPlayerPed() && pedPos.x > x1 && pedPos.x < x2 && pedPos.y > y1 && pedPos.y < y2 && (pPed->m_pedInObjective == FindPlayerPed() || pPed->m_carInObjective && pPed->m_carInObjective == FindPlayerVehicle()) && pPed->m_nPedState != PED_DEAD && pPed->m_nPedState != PED_DIE && (pPed->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || pPed->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || pPed->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS)) { if(pPed->IsPedInControl()) { if(pPed->m_nPedType == PEDTYPE_COP) ((CCopPed *)pPed)->ClearPursuit(); else pPed->SetIdle(); pPed->SetObjective(OBJECTIVE_NONE); } else { pPed->m_prevObjective = OBJECTIVE_NONE; pPed->m_nLastPedState = PED_IDLE; } } } } } void CWorld::RemoveEntityInsteadOfProcessingIt(CEntity *ent) { if(ent->IsPed()) { if(FindPlayerPed() == ent) Remove(ent); else CPopulation::RemovePed((CPed *)ent); } else { Remove(ent); delete ent; } } void CWorld::RemoveFallenPeds(void) { int poolSize = CPools::GetPedPool()->GetSize(); for(int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { CPed *ped = CPools::GetPedPool()->GetSlot(poolIndex); if(ped) { if(ped->GetPosition().z < MAP_Z_LOW_LIMIT) { if(ped->CharCreatedBy != RANDOM_CHAR || ped->IsPlayer()) { int closestNode = ThePaths.FindNodeClosestToCoors(ped->GetPosition(), PATH_PED, 999999.9f, false, false); CVector newPos = ThePaths.m_pathNodes[closestNode].GetPosition(); newPos.z += 2.0f; ped->Teleport(newPos); ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); } else { CPopulation::RemovePed(ped); } } } } } void CWorld::RemoveFallenCars(void) { int poolSize = CPools::GetVehiclePool()->GetSize(); for(int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); if(veh) { if(veh->GetPosition().z < MAP_Z_LOW_LIMIT) { if(veh->VehicleCreatedBy == MISSION_VEHICLE && !veh->bRenderScorched || veh == FindPlayerVehicle() || (veh->pDriver && veh->pDriver->IsPlayer())) { int closestNode = ThePaths.FindNodeClosestToCoors(veh->GetPosition(), PATH_CAR, 999999.9f, false, false); CVector newPos = ThePaths.m_pathNodes[closestNode].GetPosition(); newPos.z += 3.0f; veh->Teleport(newPos); veh->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); } else if(veh->VehicleCreatedBy == RANDOM_VEHICLE || veh->VehicleCreatedBy == PARKED_VEHICLE) { Remove(veh); delete veh; } } } } } void CWorld::StopAllLawEnforcersInTheirTracks(void) { int poolSize = CPools::GetVehiclePool()->GetSize(); for(int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); if(veh) { if(veh->bIsLawEnforcer) veh->SetMoveSpeed(0.0f, 0.0f, 0.0f); } } } void CWorld::SetAllCarsCanBeDamaged(bool toggle) { int poolSize = CPools::GetVehiclePool()->GetSize(); for(int poolIndex = 0; poolIndex < poolSize; poolIndex++) { CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); if(veh) veh->bCanBeDamaged = toggle; } } void CWorld::ExtinguishAllCarFiresInArea(CVector point, float range) { int poolSize = CPools::GetVehiclePool()->GetSize(); for(int poolIndex = 0; poolIndex < poolSize; poolIndex++) { CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); if(veh) { if((point - veh->GetPosition()).MagnitudeSqr() < sq(range)) veh->ExtinguishCarFire(); } } } inline void AddSteamsFromGround(CPtrList& list) { CPtrNode* pNode = list.first; while (pNode) { ((CEntity*)pNode->item)->AddSteamsFromGround(nil); pNode = pNode->next; } } void CWorld::AddParticles(void) { for(int32 y = 0; y < NUMSECTORS_Y; y++) { for(int32 x = 0; x < NUMSECTORS_X; x++) { CSector *pSector = GetSector(x, y); AddSteamsFromGround(pSector->m_lists[ENTITYLIST_BUILDINGS]); AddSteamsFromGround(pSector->m_lists[ENTITYLIST_DUMMIES]); } } } void CWorld::ShutDown(void) { for(int i = 0; i < NUMSECTORS_X * NUMSECTORS_Y; i++) { CSector *pSector = GetSector(i % NUMSECTORS_X, i / NUMSECTORS_Y); for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_VEHICLES].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_PEDS].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_OBJECTS].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_DUMMIES].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } #ifndef FIX_BUGS pSector->m_lists[ENTITYLIST_BUILDINGS].Flush(); pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].Flush(); pSector->m_lists[ENTITYLIST_DUMMIES].Flush(); pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].Flush(); #endif } for(int32 i = 0; i < NUM_LEVELS; i++) { for(CPtrNode *pNode = ms_bigBuildingsList[i].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; // Maybe remove from world here? delete pEntity; } ms_bigBuildingsList[i].Flush(); } for(int i = 0; i < NUMSECTORS_X * NUMSECTORS_Y; i++) { CSector *pSector = GetSector(i % NUMSECTORS_X, i / NUMSECTORS_Y); #ifdef FIX_BUGS pSector->m_lists[ENTITYLIST_BUILDINGS].Flush(); pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].Flush(); pSector->m_lists[ENTITYLIST_DUMMIES].Flush(); pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].Flush(); #endif if(pSector->m_lists[ENTITYLIST_BUILDINGS].first) { sprintf(gString, "Building list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); pSector->m_lists[ENTITYLIST_BUILDINGS].Flush(); } if(pSector->m_lists[ENTITYLIST_DUMMIES].first) { sprintf(gString, "Dummy list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); pSector->m_lists[ENTITYLIST_DUMMIES].Flush(); } if(pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].first) { sprintf(gString, "Building overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].Flush(); } if(pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP].first) { sprintf(gString, "Vehicle overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); pSector->m_lists[ENTITYLIST_VEHICLES_OVERLAP].Flush(); } if(pSector->m_lists[ENTITYLIST_PEDS_OVERLAP].first) { sprintf(gString, "Ped overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); pSector->m_lists[ENTITYLIST_PEDS_OVERLAP].Flush(); } if(pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP].first) { sprintf(gString, "Object overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); pSector->m_lists[ENTITYLIST_OBJECTS_OVERLAP].Flush(); } if(pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].first) { sprintf(gString, "Dummy overlap list %d,%d not empty\n", i % NUMSECTORS_X, i / NUMSECTORS_Y); pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].Flush(); } } ms_listMovingEntityPtrs.Flush(); } void CWorld::ClearForRestart(void) { if(CCutsceneMgr::HasLoaded()) CCutsceneMgr::DeleteCutsceneData(); CProjectileInfo::RemoveAllProjectiles(); CObject::DeleteAllTempObjects(); CObject::DeleteAllMissionObjects(); CPopulation::ConvertAllObjectsToDummyObjects(); for(int i = 0; i < NUMSECTORS_X * NUMSECTORS_Y; i++) { CSector *pSector = GetSector(i % NUMSECTORS_X, i / NUMSECTORS_Y); for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_PEDS].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } for(CPtrNode *pNode = GetBigBuildingList(LEVEL_GENERIC).first; pNode; pNode = pNode->next) { CVehicle *pVehicle = (CVehicle *)pNode->item; if(pVehicle && pVehicle->IsVehicle() && pVehicle->IsPlane()) { Remove(pVehicle); delete pVehicle; } } for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_VEHICLES].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } } CPools::CheckPoolsEmpty(); } void CWorld::RepositionCertainDynamicObjects() { int32 i = CPools::GetDummyPool()->GetSize(); while(--i >= 0) { CDummy *dummy = CPools::GetDummyPool()->GetSlot(i); if(dummy) { RepositionOneObject(dummy); } } } void CWorld::RepositionOneObject(CEntity *pEntity) { int16 modelId = pEntity->GetModelIndex(); if (modelId == MI_PARKINGMETER || modelId == MI_PHONEBOOTH1 || modelId == MI_WASTEBIN || modelId == MI_BIN || modelId == MI_POSTBOX1 || modelId == MI_NEWSSTAND || modelId == MI_TRAFFICCONE || modelId == MI_DUMP1 || modelId == MI_ROADWORKBARRIER1 || modelId == MI_BUSSIGN1 || modelId == MI_NOPARKINGSIGN1 || modelId == MI_PHONESIGN || modelId == MI_FIRE_HYDRANT || modelId == MI_BOLLARDLIGHT || modelId == MI_PARKTABLE || modelId == MI_PARKINGMETER2 || modelId == MI_TELPOLE02 || modelId == MI_PARKBENCH || modelId == MI_BARRIER1 || IsTreeModel(modelId) ) { CVector& position = pEntity->GetMatrix().GetPosition(); CColModel* pColModel = pEntity->GetColModel(); float fBoundingBoxMinZ = pColModel->boundingBox.min.z; float fHeight = pColModel->boundingBox.max.z - pColModel->boundingBox.min.z; if (fHeight < OBJECT_REPOSITION_OFFSET_Z) fHeight = OBJECT_REPOSITION_OFFSET_Z; position.z = FindGroundZFor3DCoord(position.x, position.y, position.z + fHeight, nil) - fBoundingBoxMinZ; pEntity->m_matrix.UpdateRW(); pEntity->UpdateRwFrame(); } else if(IsLightThatNeedsRepositioning(modelId)) { CVector position = pEntity->GetMatrix().GetPosition(); CColModel* pColModel = pEntity->GetColModel(); float fBoundingBoxMinZ = pColModel->boundingBox.min.z; float fHeight = pColModel->boundingBox.max.z - pColModel->boundingBox.min.z; if (fHeight < OBJECT_REPOSITION_OFFSET_Z) fHeight = OBJECT_REPOSITION_OFFSET_Z; if (pColModel->numBoxes == 1) position = pEntity->GetMatrix() * CVector( (pColModel->boxes[0].min.x + pColModel->boxes[0].max.x) / 2, (pColModel->boxes[0].min.y + pColModel->boxes[0].max.y) / 2, pColModel->boxes[0].min.z); else if (pColModel->numSpheres > 0) { position.z = 1000.0f; for (int i = 0; i < pColModel->numSpheres; i++) { if (pColModel->spheres[i].center.z < position.z) position = pColModel->spheres[i].center; } if (position.z < 1000.0f) position = pEntity->GetMatrix() * position; } pEntity->GetMatrix().GetPosition().z = FindGroundZFor3DCoord(position.x, position.y, pEntity->GetMatrix().GetPosition().z + fHeight, nil) - fBoundingBoxMinZ; pEntity->GetMatrix().UpdateRW(); pEntity->UpdateRwFrame(); } if(modelId == MI_BUOY) { bool bFound = true; const CVector &position = pEntity->GetPosition(); float fGroundZ = FindGroundZFor3DCoord(position.x, position.y, position.z + OBJECT_REPOSITION_OFFSET_Z, &bFound); CColModel *pColModel = pEntity->GetColModel(); float fHeight = pColModel->boundingBox.max.z - pColModel->boundingBox.min.z; pEntity->GetMatrix().GetPosition().z = 0.2f * fHeight + 6.0f - 0.5f * fHeight; } } void CWorld::SetCarsOnFire(float x, float y, float z, float radius, CEntity *reason) { int poolSize = CPools::GetVehiclePool()->GetSize(); for(int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { CVehicle *veh = CPools::GetVehiclePool()->GetSlot(poolIndex); if(veh && veh->GetStatus() != STATUS_WRECKED && !veh->m_pCarFire && !veh->bFireProof) { if(Abs(veh->GetPosition().z - z) < 5.0f && Abs(veh->GetPosition().x - x) < radius && Abs(veh->GetPosition().y - y) < radius) gFireManager.StartFire(veh, reason, 0.8f, true); } } } void CWorld::SetPedsChoking(float x, float y, float z, float radius, CEntity* reason) { int32 poolSize = CPools::GetPedPool()->GetSize(); for (int32 i = poolSize - 1; i >= 0; i--) { CPed* pPed = CPools::GetPedPool()->GetSlot(i); // suspicious copypaste if (pPed && pPed->m_nPedState != PED_DEAD && !pPed->bInVehicle && !pPed->m_pFire && !pPed->bFireProof && pPed->CharCreatedBy != MISSION_CHAR) { if (Abs(pPed->GetPosition().z - z) < 5.0f && Abs(pPed->GetPosition().x - x) < radius && Abs(pPed->GetPosition().y - y) < radius) { if (!pPed->IsPlayer()) pPed->SetFlee(CVector2D(x, y), 10000); #ifdef FIX_BUGS pPed->InflictDamage(reason, WEAPONTYPE_TEARGAS, 1.0f, PEDPIECE_TORSO, 0); #else pPed->InflictDamage(nil, WEAPONTYPE_TEARGAS, 1.0f, PEDPIECE_TORSO, 0); #endif } } } } void CWorld::SetPedsOnFire(float x, float y, float z, float radius, CEntity *reason) { int32 poolSize = CPools::GetPedPool()->GetSize(); for(int32 i = poolSize - 1; i >= 0; i--) { CPed *pPed = CPools::GetPedPool()->GetSlot(i); if(pPed && pPed->m_nPedState != PED_DEAD && !pPed->bInVehicle && !pPed->m_pFire && !pPed->bFireProof) { if(Abs(pPed->GetPosition().z - z) < 5.0f && Abs(pPed->GetPosition().x - x) < radius && Abs(pPed->GetPosition().y - y) < radius) gFireManager.StartFire(pPed, reason, 0.8f, true); } } } void CWorld::RemoveStaticObjects() { for(int i = 0; i < NUMSECTORS_X * NUMSECTORS_Y; i++) { CSector *pSector = GetSector(i % NUMSECTORS_X, i / NUMSECTORS_Y); for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_OBJECTS].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } for(CPtrNode *pNode = pSector->m_lists[ENTITYLIST_DUMMIES].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; Remove(pEntity); delete pEntity; } pSector->m_lists[ENTITYLIST_BUILDINGS].Flush(); pSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].Flush(); pSector->m_lists[ENTITYLIST_DUMMIES].Flush(); pSector->m_lists[ENTITYLIST_DUMMIES_OVERLAP].Flush(); } } void CWorld::Process(void) { if(!(CTimer::GetFrameCounter() & 63)) CReferences::PruneAllReferencesInWorld(); if(bProcessCutsceneOnly) { for(int i = 0; i < NUMCUTSCENEOBJECTS; i++) { CCutsceneObject *csObj = CCutsceneMgr::GetCutsceneObject(i); if(csObj && csObj->m_entryInfoList.first) { if(csObj->m_rwObject && RwObjectGetType(csObj->m_rwObject) == rpCLUMP && RpAnimBlendClumpGetFirstAssociation(csObj->GetClump())) { if (csObj->IsObject()) RpAnimBlendClumpUpdateAnimations(csObj->GetClump(), CTimer::GetTimeStepNonClippedInSeconds()); else { if (!csObj->bOffscreen) csObj->bOffscreen = !csObj->GetIsOnScreen(); RpAnimBlendClumpUpdateAnimations(csObj->GetClump(), CTimer::GetTimeStepInSeconds(), !csObj->bOffscreen); } } csObj->ProcessControl(); csObj->ProcessCollision(); csObj->GetMatrix().UpdateRW(); csObj->UpdateRwFrame(); } } CRecordDataForChase::ProcessControlCars(); CRecordDataForChase::SaveOrRetrieveCarPositions(); } else { for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CEntity *movingEnt = (CEntity *)node->item; if(!movingEnt->bRemoveFromWorld && movingEnt->m_rwObject && RwObjectGetType(movingEnt->m_rwObject) == rpCLUMP && RpAnimBlendClumpGetFirstAssociation(movingEnt->GetClump())) { if (movingEnt->IsObject()) RpAnimBlendClumpUpdateAnimations(movingEnt->GetClump(), CTimer::GetTimeStepNonClippedInSeconds()); else { if (!movingEnt->bOffscreen) movingEnt->bOffscreen = !movingEnt->GetIsOnScreen(); RpAnimBlendClumpUpdateAnimations(movingEnt->GetClump(), CTimer::GetTimeStepInSeconds(), !movingEnt->bOffscreen); } } } for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CPhysical *movingEnt = (CPhysical *)node->item; if(movingEnt->bRemoveFromWorld) { RemoveEntityInsteadOfProcessingIt(movingEnt); } else { movingEnt->ProcessControl(); if(movingEnt->GetIsStatic()) { movingEnt->RemoveFromMovingList(); } } } bForceProcessControl = true; for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CPhysical *movingEnt = (CPhysical *)node->item; if(movingEnt->bWasPostponed) { if(movingEnt->bRemoveFromWorld) { RemoveEntityInsteadOfProcessingIt(movingEnt); } else { movingEnt->ProcessControl(); if(movingEnt->GetIsStatic()) { movingEnt->RemoveFromMovingList(); } } } } bForceProcessControl = false; if(CReplay::IsPlayingBack()) { for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CEntity *movingEnt = (CEntity *)node->item; movingEnt->bIsInSafePosition = true; movingEnt->GetMatrix().UpdateRW(); movingEnt->UpdateRwFrame(); } } else { bNoMoreCollisionTorque = false; for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CEntity *movingEnt = (CEntity *)node->item; if(!movingEnt->bIsInSafePosition) { movingEnt->ProcessCollision(); movingEnt->GetMatrix().UpdateRW(); movingEnt->UpdateRwFrame(); } } bNoMoreCollisionTorque = true; for(int i = 0; i < 4; i++) { for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CEntity *movingEnt = (CEntity *)node->item; if(!movingEnt->bIsInSafePosition) { movingEnt->ProcessCollision(); movingEnt->GetMatrix().UpdateRW(); movingEnt->UpdateRwFrame(); } } } for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CEntity *movingEnt = (CEntity *)node->item; if(!movingEnt->bIsInSafePosition) { movingEnt->bIsStuck = true; movingEnt->ProcessCollision(); movingEnt->GetMatrix().UpdateRW(); movingEnt->UpdateRwFrame(); if(!movingEnt->bIsInSafePosition) { movingEnt->bIsStuck = true; } } } bSecondShift = false; for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CEntity *movingEnt = (CEntity *)node->item; if(!movingEnt->bIsInSafePosition) { movingEnt->ProcessShift(); movingEnt->GetMatrix().UpdateRW(); movingEnt->UpdateRwFrame(); if(!movingEnt->bIsInSafePosition) { movingEnt->bIsStuck = true; } } } bSecondShift = true; for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CPhysical *movingEnt = (CPhysical *)node->item; if(!movingEnt->bIsInSafePosition) { movingEnt->ProcessShift(); movingEnt->GetMatrix().UpdateRW(); movingEnt->UpdateRwFrame(); if(!movingEnt->bIsInSafePosition) { movingEnt->bIsStuck = true; if(movingEnt->GetStatus() == STATUS_PLAYER) { printf("STUCK: Final Step: Player Entity %d Is Stuck\n", movingEnt->GetModelIndex()); movingEnt->m_vecMoveSpeed *= Pow(0.707f, CTimer::GetTimeStep()); movingEnt->ApplyMoveSpeed(); movingEnt->ApplyTurnSpeed(); } } } } } for(CPtrNode *node = ms_listMovingEntityPtrs.first; node; node = node->next) { CPed *movingPed = (CPed *)node->item; if(movingPed->IsPed()) { if(movingPed->bInVehicle && movingPed->m_nPedState != PED_EXIT_TRAIN || movingPed->EnteringCar()) { CVehicle *movingCar = movingPed->m_pMyVehicle; if(movingCar) { #ifdef GTA_TRAIN if(movingCar->IsTrain()) { movingPed->SetPedPositionInTrain(); } else #endif { switch(movingPed->m_nPedState) { case PED_ENTER_CAR: case PED_CARJACK: movingPed->EnterCar(); break; case PED_DRAG_FROM_CAR: movingPed->BeingDraggedFromCar(); break; case PED_EXIT_CAR: movingPed->ExitCar(); break; case PED_ARRESTED: if(movingPed->m_nLastPedState == PED_DRAG_FROM_CAR) { movingPed->BeingDraggedFromCar(); break; } // fall through default: movingPed->SetPedPositionInCar(); break; } } movingPed->GetMatrix().UpdateRW(); movingPed->UpdateRwFrame(); } else { movingPed->bInVehicle = false; movingPed->QuitEnteringCar(); } } else if (movingPed->m_attachedTo) { movingPed->PositionAttachedPed(); movingPed->GetMatrix().UpdateRW(); movingPed->UpdateRwFrame(); } } } CMessages::Process(); Players[PlayerInFocus].Process(); CRecordDataForChase::SaveOrRetrieveCarPositions(); if((CTimer::GetFrameCounter() & 7) == 1) { RemoveFallenPeds(); } else if((CTimer::GetFrameCounter() & 7) == 5) { RemoveFallenCars(); } } } void CWorld::TriggerExplosion(const CVector &position, float fRadius, float fPower, CEntity *pCreator, bool bProcessVehicleBombTimer) { CVector2D vecStartPos(position.x - fRadius, position.y - fRadius); CVector2D vecEndPos(position.x + fRadius, position.y + fRadius); const int32 nStartX = Max(GetSectorIndexX(vecStartPos.x), 0); const int32 nStartY = Max(GetSectorIndexY(vecStartPos.y), 0); const int32 nEndX = Min(GetSectorIndexX(vecEndPos.x), NUMSECTORS_X - 1); const int32 nEndY = Min(GetSectorIndexY(vecEndPos.y), NUMSECTORS_Y - 1); for(int32 y = nStartY; y <= nEndY; y++) { for(int32 x = nStartX; x <= nEndX; x++) { CSector *pSector = GetSector(x, y); TriggerExplosionSectorList(pSector->m_lists[ENTITYLIST_VEHICLES], position, fRadius, fPower, pCreator, bProcessVehicleBombTimer); TriggerExplosionSectorList(pSector->m_lists[ENTITYLIST_PEDS], position, fRadius, fPower, pCreator, bProcessVehicleBombTimer); TriggerExplosionSectorList(pSector->m_lists[ENTITYLIST_OBJECTS], position, fRadius, fPower, pCreator, bProcessVehicleBombTimer); } } } void CWorld::TriggerExplosionSectorList(CPtrList &list, const CVector &position, float fRadius, float fPower, CEntity *pCreator, bool bProcessVehicleBombTimer) { for(CPtrNode *pNode = list.first; pNode; pNode = pNode->next) { CPhysical *pEntity = (CPhysical *)pNode->item; CVector vecDistance = pEntity->GetPosition() - position; float fMagnitude = vecDistance.Magnitude(); if(fRadius > fMagnitude) { CWeapon::BlowUpExplosiveThings(pEntity); CPed *pPed = (CPed *)pEntity; CObject *pObject = (CObject *)pEntity; CVehicle *pVehicle = (CVehicle *)pEntity; if(!pEntity->bExplosionProof && (!pEntity->IsPed() || !pPed->bInVehicle)) { if(pEntity->GetIsStatic()) { if(pEntity->IsObject()) { if (fPower > pObject->m_fUprootLimit || IsFence(pObject->GetModelIndex())) { if (IsGlass(pObject->GetModelIndex())) { CGlass::WindowRespondsToExplosion(pObject, position); } else { pObject->SetIsStatic(false); pObject->AddToMovingList(); int16 modelId = pEntity->GetModelIndex(); if(modelId != MI_FIRE_HYDRANT || pObject->bHasBeenDamaged) { if(pEntity->IsObject() && modelId != MI_EXPLODINGBARREL && modelId != MI_PETROLPUMP && modelId != MI_PETROLPUMP2) pObject->bHasBeenDamaged = true; } else { CVector pos = pEntity->GetPosition(); pos.z -= 0.5f; CParticleObject::AddObject(POBJECT_FIRE_HYDRANT, pos, true); pObject->bHasBeenDamaged = true; } } } if(pEntity->GetIsStatic()) { float fDamageMultiplier = (fRadius - fMagnitude) * 2.0f / fRadius; float fDamage = 300.0f * Min(fDamageMultiplier, 1.0f); pObject->ObjectDamage(fDamage); } } else { pEntity->SetIsStatic(false); pEntity->AddToMovingList(); } } if(!pEntity->GetIsStatic()) { float fDamageMultiplier = Min((fRadius - fMagnitude) * 2.0f / fRadius, 1.0f); CVector vecForceDir = vecDistance * (fPower * pEntity->m_fMass / 1400.0f * fDamageMultiplier / Max(fMagnitude, 0.01f)); vecForceDir.z = Max(vecForceDir.z, 0.0f); if(pEntity == FindPlayerPed()) vecForceDir.z = Min(vecForceDir.z, 1.0f); pEntity->ApplyMoveForce(vecForceDir); if(!pEntity->bPedPhysics) { float fBoundRadius = pEntity->GetBoundRadius(); float fDistanceZ = position.z - pEntity->GetPosition().z; float fPointZ = fBoundRadius; if(Max(fDistanceZ, -fBoundRadius) < fBoundRadius) { if(fDistanceZ <= -fBoundRadius) fPointZ = -fBoundRadius; else fPointZ = fDistanceZ; } pEntity->ApplyTurnForce(vecForceDir.x, vecForceDir.y, vecForceDir.z, 0.0f, 0.0f, fPointZ); } switch(pEntity->GetType()) { case ENTITY_TYPE_VEHICLE: if(pEntity->GetStatus() == STATUS_SIMPLE) { pEntity->SetStatus(STATUS_PHYSICS); CCarCtrl::SwitchVehicleToRealPhysics(pVehicle); } pVehicle->InflictDamage(pCreator, WEAPONTYPE_EXPLOSION, 1100.0f * fDamageMultiplier); if(bProcessVehicleBombTimer) { if(pVehicle->m_nBombTimer) pVehicle->m_nBombTimer /= 10; } break; case ENTITY_TYPE_PED: { int8 direction = pPed->GetLocalDirection(-vecForceDir); pPed->bIsStanding = false; pPed->ApplyMoveForce(0.0, 0.0, 2.0f); float fDamage = 250.0f * fDamageMultiplier; pPed->InflictDamage(pCreator, WEAPONTYPE_EXPLOSION, fDamage, PEDPIECE_TORSO, direction); if(pPed->m_nPedState != PED_DIE) pPed->SetFall(2000, (AnimationId)(direction + ANIM_STD_HIGHIMPACT_FRONT), 0); if(pCreator && pCreator->IsPed()) { eEventType eventType = EVENT_SHOOT_PED; if(pPed->m_nPedType == PEDTYPE_COP) eventType = EVENT_SHOOT_COP; CEventList::RegisterEvent(eventType, EVENT_ENTITY_PED, pEntity, (CPed *)pCreator, 10000); pPed->RegisterThreatWithGangPeds(pCreator); } break; } case ENTITY_TYPE_OBJECT: pObject->ObjectDamage(300.0f * fDamageMultiplier); break; default: break; } } } } } } void CWorld::UseDetonator(CEntity *pEntity) { int32 i = CPools::GetVehiclePool()->GetSize(); while(--i >= 0) { #ifdef FIX_BUGS CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i); #else CAutomobile *pVehicle = (CAutomobile *)CPools::GetVehiclePool()->GetSlot(i); #endif if(pVehicle && pVehicle->m_bombType == CARBOMB_REMOTE && pVehicle->m_pBombRigger == pEntity) { pVehicle->m_bombType = CARBOMB_NONE; pVehicle->m_nBombTimer = 500; pVehicle->m_pBlowUpEntity = pVehicle->m_pBombRigger; if(pVehicle->m_pBlowUpEntity) pVehicle->m_pBlowUpEntity->RegisterReference(&pVehicle->m_pBlowUpEntity); } } CProjectileInfo::RemoveDetonatorProjectiles(); } bool CWorld::IsWanderPathClear(CVector const& point1, CVector const& point2, float distance, int maxSteps) { if (Abs(point1.z - point2.z) > distance) return false; if (!GetIsLineOfSightClear(point1, point2, true, false, false, false, false, false, false)) return false; CVector vecBetween = point2 - point1; uint32 nSteps = Max(vecBetween.Magnitude(), maxSteps); if (nSteps == 0) return true; vecBetween.Normalise(); uint32 step = 1; for (step = 1; step < nSteps; step++) { CVector posThisStep = point1 + vecBetween * step; float level; if (!CWaterLevel::GetWaterLevel(posThisStep, &level, false)) continue; posThisStep.z = level; AdvanceCurrentScanCode(); CVector vecCheckedPos(posThisStep.x, posThisStep.y, Max(point1.z, point2.z)); CColPoint colpoint; CEntity* entity; if (!ProcessVerticalLineSector(*GetSector(GetSectorIndexX(posThisStep.x), GetSectorIndexY(posThisStep.y)), CColLine(posThisStep, vecCheckedPos), colpoint, entity, true, false, false, false, false, false, nil)) return false; } CVector posThisStep = point1; AdvanceCurrentScanCode(); CVector vecCheckedPos(posThisStep.x, posThisStep.y, point1.z - 5.0f); CColPoint colpoint; CEntity* entity; if (!ProcessVerticalLineSector(*GetSector(GetSectorIndexX(posThisStep.x), GetSectorIndexY(posThisStep.y)), CColLine(posThisStep, vecCheckedPos), colpoint, entity, true, false, false, false, false, false, nil)) return false; float heightNextStep = colpoint.point.z + 0.5f; for (step = 1; step < nSteps; step++) { CVector posThisStep = point1 + vecBetween * step; posThisStep.z = heightNextStep; AdvanceCurrentScanCode(); CVector vecCheckedPos(posThisStep.x, posThisStep.y, heightNextStep - 2.0f); if (!ProcessVerticalLineSector(*GetSector(GetSectorIndexX(posThisStep.x), GetSectorIndexY(posThisStep.y)), CColLine(posThisStep, vecCheckedPos), colpoint, entity, true, false, false, false, false, false, nil)) return false; if (Abs(colpoint.point.z - heightNextStep) > 1.0f) return false; heightNextStep = colpoint.point.z + 0.5f; } return true; } ================================================ FILE: src/core/World.h ================================================ #pragma once #include "Game.h" #include "Lists.h" #include "PlayerInfo.h" #include "Collision.h" /* Sectors span from -2400 to 1600 in x and -2000 to 2000 y. * With 80x80 sectors, each is 50x50 units. */ #define SECTOR_SIZE_X (50.0f) #define SECTOR_SIZE_Y (50.0f) #define NUMSECTORS_X (80) #define NUMSECTORS_Y (80) #define WORLD_SIZE_X (NUMSECTORS_X * SECTOR_SIZE_X) #define WORLD_SIZE_Y (NUMSECTORS_Y * SECTOR_SIZE_Y) #define WORLD_MIN_X (-2400.0f) #define WORLD_MIN_Y (-2000.0f) #define WORLD_MAX_X (WORLD_MIN_X + WORLD_SIZE_X) #define WORLD_MAX_Y (WORLD_MIN_Y + WORLD_SIZE_Y) #define MAP_Z_LOW_LIMIT -100.0f enum { ENTITYLIST_BUILDINGS, ENTITYLIST_BUILDINGS_OVERLAP, ENTITYLIST_OBJECTS, ENTITYLIST_OBJECTS_OVERLAP, ENTITYLIST_VEHICLES, ENTITYLIST_VEHICLES_OVERLAP, ENTITYLIST_PEDS, ENTITYLIST_PEDS_OVERLAP, ENTITYLIST_DUMMIES, ENTITYLIST_DUMMIES_OVERLAP, NUMSECTORENTITYLISTS }; class CSector { public: CPtrList m_lists[NUMSECTORENTITYLISTS]; }; VALIDATE_SIZE(CSector, 0x28); class CWorld { static CPtrList ms_bigBuildingsList[NUM_LEVELS]; static CPtrList ms_listMovingEntityPtrs; static CSector ms_aSectors[NUMSECTORS_Y][NUMSECTORS_X]; static uint16 ms_nCurrentScanCode; public: static uint8 PlayerInFocus; static CPlayerInfo Players[NUMPLAYERS]; static CEntity *pIgnoreEntity; static bool bIncludeDeadPeds; static bool bNoMoreCollisionTorque; static bool bSecondShift; static bool bForceProcessControl; static bool bProcessCutsceneOnly; static bool bDoingCarCollisions; static bool bIncludeCarTyres; static bool bIncludeBikers; static CColPoint m_aTempColPts[MAX_COLLISION_POINTS]; static void Remove(CEntity *entity); static void Add(CEntity *entity); static CSector *GetSector(int x, int y) { if (x > NUMSECTORS_X - 1 || y > NUMSECTORS_Y - 1) return &ms_aSectors[0][0]; return &ms_aSectors[y][x]; } static CPtrList &GetBigBuildingList(eLevelName i) { return ms_bigBuildingsList[i]; } static CPtrList &GetMovingEntityList(void) { return ms_listMovingEntityPtrs; } static uint16 GetCurrentScanCode(void) { return ms_nCurrentScanCode; } static void AdvanceCurrentScanCode(void){ if(++CWorld::ms_nCurrentScanCode == 0){ CWorld::ClearScanCodes(); CWorld::ms_nCurrentScanCode = 1; } } static void ClearScanCodes(void); static void ClearExcitingStuffFromArea(const CVector &pos, float radius, bool bRemoveProjectilesAndTidyUpShadows); static bool CameraToIgnoreThisObject(CEntity *ent); static bool ProcessLineOfSight(const CVector &point1, const CVector &point2, CColPoint &point, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects = false, bool ignoreShootThrough = false); static bool ProcessLineOfSightSector(CSector §or, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough); static bool ProcessLineOfSightSectorList(CPtrList &list, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, bool ignoreSeeThrough, bool ignoreSomeObjects, bool ignoreShootThrough); static bool ProcessVerticalLine(const CVector &point1, float z2, CColPoint &point, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, CStoredCollPoly *poly); static bool ProcessVerticalLineSector(CSector §or, const CColLine &line, CColPoint &point, CEntity *&entity, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, CStoredCollPoly *poly); static bool ProcessVerticalLineSectorList(CPtrList &list, const CColLine &line, CColPoint &point, float &dist, CEntity *&entity, bool ignoreSeeThrough, CStoredCollPoly *poly); static bool GetIsLineOfSightClear(const CVector &point1, const CVector &point2, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects = false); static bool GetIsLineOfSightSectorClear(CSector §or, const CColLine &line, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects = false); static bool GetIsLineOfSightSectorListClear(CPtrList &list, const CColLine &line, bool ignoreSeeThrough, bool ignoreSomeObjects = false); static CEntity *TestSphereAgainstWorld(CVector centre, float radius, CEntity *entityToIgnore, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSomeObjects); static CEntity *TestSphereAgainstSectorList(CPtrList&, CVector, float, CEntity*, bool); static void FindObjectsInRangeSectorList(CPtrList &list, Const CVector ¢re, float radius, bool ignoreZ, int16 *numObjects, int16 lastObject, CEntity **objects); static void FindObjectsInRange(Const CVector ¢re, float radius, bool ignoreZ, int16 *numObjects, int16 lastObject, CEntity **objects, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies); static void FindObjectsOfTypeInRangeSectorList(uint32 modelId, CPtrList& list, const CVector& position, float radius, bool bCheck2DOnly, int16* nEntitiesFound, int16 maxEntitiesToFind, CEntity** aEntities); static void FindObjectsOfTypeInRange(uint32 modelId, const CVector& position, float radius, bool bCheck2DOnly, int16* nEntitiesFound, int16 maxEntitiesToFind, CEntity** aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies); static float FindGroundZForCoord(float x, float y); static float FindGroundZFor3DCoord(float x, float y, float z, bool *found); static float FindRoofZFor3DCoord(float x, float y, float z, bool *found); static void RemoveReferencesToDeletedObject(CEntity*); static void FindObjectsKindaColliding(const CVector& position, float radius, bool bCheck2DOnly, int16* nCollidingEntities, int16 maxEntitiesToFind, CEntity** aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies); static void FindObjectsKindaCollidingSectorList(CPtrList& list, const CVector& position, float radius, bool bCheck2DOnly, int16* nCollidingEntities, int16 maxEntitiesToFind, CEntity** aEntities); static void FindObjectsIntersectingCube(const CVector& vecStartPos, const CVector& vecEndPos, int16* nIntersecting, int16 maxEntitiesToFind, CEntity** aEntities, bool bBuildings, bool bVehicles, bool bPeds, bool bObjects, bool bDummies); static void FindObjectsIntersectingCubeSectorList(CPtrList& list, const CVector& vecStartPos, const CVector& vecEndPos, int16* nIntersecting, int16 maxEntitiesToFind, CEntity** aEntities); static void FindObjectsIntersectingAngledCollisionBox(const CBox &, const CMatrix &, const CVector &, float, float, float, float, int16*, int16, CEntity **, bool, bool, bool, bool, bool); static void FindObjectsIntersectingAngledCollisionBoxSectorList(CPtrList& list, const CBox& boundingBox, const CMatrix& matrix, const CVector& position, int16* nEntitiesFound, int16 maxEntitiesToFind, CEntity** aEntities); static void FindMissionEntitiesIntersectingCube(const CVector& vecStartPos, const CVector& vecEndPos, int16* nIntersecting, int16 maxEntitiesToFind, CEntity** aEntities, bool bVehicles, bool bPeds, bool bObjects); static void FindMissionEntitiesIntersectingCubeSectorList(CPtrList& list, const CVector& vecStartPos, const CVector& vecEndPos, int16* nIntersecting, int16 maxEntitiesToFind, CEntity** aEntities, bool bIsVehicleList, bool bIsPedList); static void ClearCarsFromArea(float x1, float y1, float z1, float x2, float y2, float z2); static void ClearPedsFromArea(float x1, float y1, float z1, float x2, float y2, float z2); static void CallOffChaseForArea(float x1, float y1, float x2, float y2); static void CallOffChaseForAreaSectorListVehicles(CPtrList& list, float x1, float y1, float x2, float y2, float fStartX, float fStartY, float fEndX, float fEndY); static void CallOffChaseForAreaSectorListPeds(CPtrList& list, float x1, float y1, float x2, float y2); static bool IsWanderPathClear(CVector const&, CVector const&, float, int); static float GetSectorX(float f) { return ((f - WORLD_MIN_X)/SECTOR_SIZE_X); } static float GetSectorY(float f) { return ((f - WORLD_MIN_Y)/SECTOR_SIZE_Y); } static int GetSectorIndexX(float f) { return (int)GetSectorX(f); } static int GetSectorIndexY(float f) { return (int)GetSectorY(f); } static float GetWorldX(int x) { return x*SECTOR_SIZE_X + WORLD_MIN_X; } static float GetWorldY(int y) { return y*SECTOR_SIZE_Y + WORLD_MIN_Y; } static void RemoveEntityInsteadOfProcessingIt(CEntity* ent); static void RemoveFallenPeds(); static void RemoveFallenCars(); static void StopAllLawEnforcersInTheirTracks(); static void SetAllCarsCanBeDamaged(bool); static void ExtinguishAllCarFiresInArea(CVector, float); static void SetCarsOnFire(float x, float y, float z, float radius, CEntity* reason); static void SetPedsChoking(float x, float y, float z, float radius, CEntity* reason); static void SetPedsOnFire(float x, float y, float z, float radius, CEntity* reason); static void Initialise(); static void AddParticles(); static void ShutDown(); static void ClearForRestart(void); static void RepositionCertainDynamicObjects(); static void RepositionOneObject(CEntity* pEntity); static void RemoveStaticObjects(); static void Process(); static void TriggerExplosion(const CVector& position, float fRadius, float fPower, CEntity* pCreator, bool bProcessVehicleBombTimer); static void TriggerExplosionSectorList(CPtrList& list, const CVector& position, float fRadius, float fPower, CEntity* pCreator, bool bProcessVehicleBombTimer); static void UseDetonator(CEntity *pEntity); // NB: following functions are unused (TODO?) static void CastShadow(float, float, float, float); static void CastShadowSectorList(CPtrList&, float, float, float, float); static void FindLowestZForCoord(float, float); static void CheckBlockListIntegrity(void); static void ProcessVerticalLineSectorList_FillGlobeColPoints(CPtrList&, const CColLine&, CEntity*&, bool, CStoredCollPoly*); static void ProcessVerticalLineSector_FillGlobeColPoints(CSector&, const CColLine&, CEntity*&, bool, bool, bool, bool, bool, bool, CStoredCollPoly*); static void ProcessVerticalLine_FillGlobeColPoints(const CVector&, float, CEntity*&, bool, bool, bool, bool, bool, bool, CStoredCollPoly*); static void PrintCarChanges(void); static void TestForBuildingsOnTopOfEachOther(CPtrList&); static void TestForBuildingsOnTopOfEachOther(void); static void TestForUnusedModels(CPtrList&, int*); static void TestForUnusedModels(void); static void HandleCollisionZoneChange(eLevelName, eLevelName); static void DoZoneTestForChaser(class CPhysical*); static void FindPlayerSlotWithPedPointer(void*); }; extern CColPoint gaTempSphereColPoints[MAX_COLLISION_POINTS]; ================================================ FILE: src/core/ZoneCull.cpp ================================================ #include "common.h" #include "Building.h" #include "Treadable.h" #include "Train.h" #include "Pools.h" #include "Timer.h" #include "Camera.h" #include "World.h" #include "FileMgr.h" #include "ZoneCull.h" #include "Zones.h" int32 CCullZones::NumAttributeZones; CAttributeZone CCullZones::aAttributeZones[NUMATTRIBZONES]; int32 CCullZones::CurrentWantedLevelDrop_Player; int32 CCullZones::CurrentFlags_Camera; int32 CCullZones::CurrentFlags_Player; bool CCullZones::bCurrentSubwayIsInvisible; bool CCullZones::bAtBeachForAudio; void CCullZones::Init(void) { NumAttributeZones = 0; CurrentWantedLevelDrop_Player = 0; CurrentFlags_Camera = 0; CurrentFlags_Player = 0; bCurrentSubwayIsInvisible = false; } void CCullZones::Update(void) { bool invisible; switch(CTimer::GetFrameCounter() & 7){ case 0: case 4: UpdateAtBeachForAudio(); break; case 2: /* Update camera attributes */ CurrentFlags_Camera = FindAttributesForCoors(TheCamera.GetGameCamPosition(), nil); invisible = (CurrentFlags_Camera & ATTRZONE_SUBWAYVISIBLE) == 0; if(invisible != bCurrentSubwayIsInvisible){ MarkSubwayAsInvisible(!invisible); bCurrentSubwayIsInvisible = invisible; } break; case 6: /* Update player attributes */ CurrentFlags_Player = FindAttributesForCoors(FindPlayerCoors(), &CurrentWantedLevelDrop_Player); break; } } // TODO? put somewhere else? bool IsPointWithinArbitraryArea(float px, float py, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { if((px-x1)*(x2-x1) - (py-y1)*(y2-y1) < 0.0f) return false; if((px-x2)*(x3-x2) - (py-y2)*(y3-y2) < 0.0f) return false; if((px-x3)*(x4-x3) - (py-y3)*(y4-y3) < 0.0f) return false; if((px-x4)*(x1-x4) - (py-y4)*(y1-y4) < 0.0f) return false; return true; } void CCullZones::UpdateAtBeachForAudio(void) { bAtBeachForAudio = IsPointWithinArbitraryArea(TheCamera.GetPosition().x, TheCamera.GetPosition().y, 400.0f, -1644.4f, 751.9f, 1267.8f, 971.9f, 1216.2f, 840.0f, -1744.0f); } void CCullZones::ForceCullZoneCoors(CVector coors) { } int32 CCullZones::FindAttributesForCoors(CVector coors, int32 *wantedLevel) { int i; int32 attribs; if (wantedLevel) *wantedLevel = 0; attribs = 0; for(i = 0; i < NumAttributeZones; i++) if(coors.x >= aAttributeZones[i].minx && coors.x <= aAttributeZones[i].maxx && coors.y >= aAttributeZones[i].miny && coors.y <= aAttributeZones[i].maxy && coors.z >= aAttributeZones[i].minz && coors.z <= aAttributeZones[i].maxz){ attribs |= aAttributeZones[i].attributes; if(wantedLevel) *wantedLevel = Max(*wantedLevel, aAttributeZones[i].wantedLevel); } return attribs; } CAttributeZone* CCullZones::FindZoneWithStairsAttributeForPlayer(void) { int i; CVector coors; coors = FindPlayerCoors(); for(i = 0; i < NumAttributeZones; i++) if(aAttributeZones[i].attributes & ATTRZONE_STAIRS && coors.x >= aAttributeZones[i].minx && coors.x <= aAttributeZones[i].maxx && coors.y >= aAttributeZones[i].miny && coors.y <= aAttributeZones[i].maxy && coors.z >= aAttributeZones[i].minz && coors.z <= aAttributeZones[i].maxz) return &aAttributeZones[i]; return nil; } void CCullZones::MarkSubwayAsInvisible(bool visible) { int i, n; CEntity *e; CVehicle *v; n = CPools::GetBuildingPool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetBuildingPool()->GetSlot(i); if(e && e->bIsSubway) e->bIsVisible = visible; } n = CPools::GetTreadablePool()->GetSize()-1; for(i = n; i >= 0; i--){ e = CPools::GetTreadablePool()->GetSlot(i); if(e && e->bIsSubway) e->bIsVisible = visible; } n = CPools::GetVehiclePool()->GetSize()-1; for(i = n; i >= 0; i--){ v = CPools::GetVehiclePool()->GetSlot(i); if(v && v->IsTrain() && ((CTrain*)v)->m_nTrackId != TRACK_ELTRAIN) v->bIsVisible = visible; } } void CCullZones::AddCullZone(CVector const &position, float minx, float maxx, float miny, float maxy, float minz, float maxz, uint16 flag, int16 wantedLevel) { CAttributeZone *attrib; assert(NumAttributeZones < NUMATTRIBZONES); attrib = &aAttributeZones[NumAttributeZones++]; attrib->minx = minx; attrib->maxx = maxx; attrib->miny = miny; attrib->maxy = maxy; attrib->minz = minz; attrib->maxz = maxz; attrib->attributes = flag; attrib->wantedLevel = wantedLevel; } ================================================ FILE: src/core/ZoneCull.h ================================================ class CEntity; enum eZoneAttribs { ATTRZONE_CAMCLOSEIN = 1, ATTRZONE_STAIRS = 2, ATTRZONE_1STPERSON = 4, ATTRZONE_NORAIN = 8, ATTRZONE_NOPOLICE = 0x10, ATTRZONE_NOTCULLZONE = 0x20, ATTRZONE_DOINEEDCOLLISION = 0x40, ATTRZONE_SUBWAYVISIBLE = 0x80, ATTRZONE_POLICEABANDONCARS = 0x100, ATTRZONE_ROOMFORAUDIO = 0x200, ATTRZONE_WATERFUDGE = 0x400, }; struct CAttributeZone { int16 minx; int16 maxx; int16 miny; int16 maxy; int16 minz; int16 maxz; int16 attributes; int16 wantedLevel; }; class CCullZones { public: static int32 NumAttributeZones; static CAttributeZone aAttributeZones[NUMATTRIBZONES]; static int32 CurrentWantedLevelDrop_Player; static int32 CurrentFlags_Camera; static int32 CurrentFlags_Player; static bool bCurrentSubwayIsInvisible; static bool bAtBeachForAudio; static void Init(void); static void Update(void); static void UpdateAtBeachForAudio(void); static void ForceCullZoneCoors(CVector coors); static int32 FindAttributesForCoors(CVector coors, int32 *wantedLevel); static CAttributeZone *FindZoneWithStairsAttributeForPlayer(void); static void MarkSubwayAsInvisible(bool visible); static void AddCullZone(CVector const &position, float minx, float maxx, float miny, float maxy, float minz, float maxz, uint16 flag, int16 wantedLevel); static bool CamCloseInForPlayer(void) { return (CurrentFlags_Player & ATTRZONE_CAMCLOSEIN) != 0; } static bool CamStairsForPlayer(void) { return (CurrentFlags_Player & ATTRZONE_STAIRS) != 0; } static bool Cam1stPersonForPlayer(void) { return (CurrentFlags_Player & ATTRZONE_1STPERSON) != 0; } static bool NoPolice(void) { return (CurrentFlags_Player & ATTRZONE_NOPOLICE) != 0; } static bool DoINeedToLoadCollision(void) { return (CurrentFlags_Player & ATTRZONE_DOINEEDCOLLISION) != 0; } static bool PlayerNoRain(void) { return (CurrentFlags_Player & ATTRZONE_NORAIN) != 0; } static bool CamNoRain(void) { return (CurrentFlags_Camera & ATTRZONE_NORAIN) != 0; } static bool PoliceAbandonCars(void) { return (CurrentFlags_Camera & ATTRZONE_POLICEABANDONCARS) != 0; } static bool InRoomForAudio(void) { return (CurrentFlags_Camera & ATTRZONE_ROOMFORAUDIO) != 0; } static bool WaterFudge(void) { return (CurrentFlags_Camera & ATTRZONE_WATERFUDGE) != 0; } static int32 GetWantedLevelDrop(void) { return CurrentWantedLevelDrop_Player; } }; ================================================ FILE: src/core/Zones.cpp ================================================ #include "common.h" #include #include "Zones.h" #include "Clock.h" #include "Text.h" #include "World.h" #include "Timer.h" eLevelName CTheZones::m_CurrLevel; int16 CTheZones::FindIndex; uint16 CTheZones::NumberOfAudioZones; int16 CTheZones::AudioZoneArray[NUMAUDIOZONES]; uint16 CTheZones::TotalNumberOfMapZones; uint16 CTheZones::TotalNumberOfInfoZones; uint16 CTheZones::TotalNumberOfNavigationZones; CZone CTheZones::InfoZoneArray[NUMINFOZONES]; CZone CTheZones::MapZoneArray[NUMMAPZONES]; CZone CTheZones::NavigationZoneArray[NUMNAVIGZONES]; uint16 CTheZones::TotalNumberOfZoneInfos; CZoneInfo CTheZones::ZoneInfoArray[2*NUMINFOZONES]; #define SWAPF(a, b) { float t; t = a; a = b; b = t; } wchar* CZone::GetTranslatedName(void) { return TheText.Get(name); } void CTheZones::Init(void) { int i, j; for(i = 0; i < NUMAUDIOZONES; i++) AudioZoneArray[i] = -1; NumberOfAudioZones = 0; for(i = 0; i < NUMNAVIGZONES; i++) memset(&NavigationZoneArray[i], 0, sizeof(CZone)); for(i = 0; i < NUMINFOZONES; i++) memset(&InfoZoneArray[i], 0, sizeof(CZone)); int x = 1000/9; for(i = 0; i < 2*NUMINFOZONES; i++){ // Cars ZoneInfoArray[i].carDensity = 10; ZoneInfoArray[i].carThreshold[0] = x; ZoneInfoArray[i].carThreshold[1] = ZoneInfoArray[i].carThreshold[0] + x; ZoneInfoArray[i].carThreshold[2] = ZoneInfoArray[i].carThreshold[1] + x; ZoneInfoArray[i].carThreshold[3] = ZoneInfoArray[i].carThreshold[2] + x; ZoneInfoArray[i].carThreshold[4] = ZoneInfoArray[i].carThreshold[3] + x; ZoneInfoArray[i].carThreshold[5] = ZoneInfoArray[i].carThreshold[4] + x; ZoneInfoArray[i].carThreshold[6] = ZoneInfoArray[i].carThreshold[5] + x; ZoneInfoArray[i].carThreshold[7] = ZoneInfoArray[i].carThreshold[6] + x; ZoneInfoArray[i].carThreshold[8] = 1000; ZoneInfoArray[i].boatThreshold[0] = 500; ZoneInfoArray[i].boatThreshold[1] = 1000; // What's going on here? this looks more like density ZoneInfoArray[i].copThreshold = 50; for(j = 0; j < NUM_GANGS; j++) ZoneInfoArray[i].gangThreshold[j] = ZoneInfoArray[i].copThreshold; // Peds ZoneInfoArray[i].pedDensity = 12; // What's going on here? this looks more like density ZoneInfoArray[i].copPedThreshold = 50; for(j = 0; j < NUM_GANGS; j++) ZoneInfoArray[i].gangPedThreshold[j] = ZoneInfoArray[i].copPedThreshold; ZoneInfoArray[i].pedGroup = 0; } TotalNumberOfZoneInfos = 1; // why 1? TotalNumberOfNavigationZones = 1; TotalNumberOfInfoZones = 1; strcpy(InfoZoneArray[0].name, "CITYINF"); InfoZoneArray[0].minx = -2400.0f; InfoZoneArray[0].miny = -2000.0f; InfoZoneArray[0].minz = -500.0f; InfoZoneArray[0].maxx = 1600.0f; InfoZoneArray[0].maxy = 2000.0f; InfoZoneArray[0].maxz = 500.0f; InfoZoneArray[0].level = LEVEL_GENERIC; InfoZoneArray[0].type = ZONE_INFO; strcpy(NavigationZoneArray[0].name, "VICE_C"); NavigationZoneArray[0].minx = -2400.0f; NavigationZoneArray[0].miny = -2000.0f; NavigationZoneArray[0].minz = -500.0f; NavigationZoneArray[0].maxx = 1600.0f; NavigationZoneArray[0].maxy = 2000.0f; NavigationZoneArray[0].maxz = 500.0f; NavigationZoneArray[0].level = LEVEL_GENERIC; NavigationZoneArray[0].type = ZONE_DEFAULT; m_CurrLevel = LEVEL_GENERIC; for(i = 0; i < NUMMAPZONES; i++){ memset(&MapZoneArray[i], 0, sizeof(CZone)); MapZoneArray[i].type = ZONE_MAPZONE; } TotalNumberOfMapZones = 1; strcpy(MapZoneArray[0].name, "THEMAP"); MapZoneArray[0].minx = -2400.0f; MapZoneArray[0].miny = -2000.0f; MapZoneArray[0].minz = -500.0f; MapZoneArray[0].maxx = 1600.0f; MapZoneArray[0].maxy = 2000.0f; MapZoneArray[0].maxz = 500.0f; MapZoneArray[0].level = LEVEL_GENERIC; } void CTheZones::Update(void) { #ifdef SQUEEZE_PERFORMANCE if (CTimer::GetFrameCounter() % 5 != 0) return; #endif CVector pos; pos = FindPlayerCoors(); m_CurrLevel = GetLevelFromPosition(&pos); } void CTheZones::CreateZone(char *name, eZoneType type, float minx, float miny, float minz, float maxx, float maxy, float maxz, eLevelName level) { char tmpname[24]; char *p; if(minx > maxx) SWAPF(minx, maxx); if(miny > maxy) SWAPF(miny, maxy); if(minz > maxz) SWAPF(minz, maxz); // make upper case for(p = name; *p; p++) if(islower(*p)) *p = toupper(*p); strncpy(tmpname, name, 7); tmpname[7] = '\0'; // add zone switch(type){ case ZONE_DEFAULT: case ZONE_NAVIG: assert(TotalNumberOfNavigationZones < NUMNAVIGZONES); strcpy(NavigationZoneArray[TotalNumberOfNavigationZones].name, tmpname); NavigationZoneArray[TotalNumberOfNavigationZones].type = type; NavigationZoneArray[TotalNumberOfNavigationZones].minx = minx; NavigationZoneArray[TotalNumberOfNavigationZones].miny = miny; NavigationZoneArray[TotalNumberOfNavigationZones].minz = minz; NavigationZoneArray[TotalNumberOfNavigationZones].maxx = maxx; NavigationZoneArray[TotalNumberOfNavigationZones].maxy = maxy; NavigationZoneArray[TotalNumberOfNavigationZones].maxz = maxz; NavigationZoneArray[TotalNumberOfNavigationZones].level = level; TotalNumberOfNavigationZones++; break; case ZONE_INFO: assert(TotalNumberOfInfoZones < NUMINFOZONES); strcpy(InfoZoneArray[TotalNumberOfInfoZones].name, tmpname); InfoZoneArray[TotalNumberOfInfoZones].type = type; InfoZoneArray[TotalNumberOfInfoZones].minx = minx; InfoZoneArray[TotalNumberOfInfoZones].miny = miny; InfoZoneArray[TotalNumberOfInfoZones].minz = minz; InfoZoneArray[TotalNumberOfInfoZones].maxx = maxx; InfoZoneArray[TotalNumberOfInfoZones].maxy = maxy; InfoZoneArray[TotalNumberOfInfoZones].maxz = maxz; InfoZoneArray[TotalNumberOfInfoZones].level = level; InfoZoneArray[TotalNumberOfInfoZones].zoneinfoDay = TotalNumberOfZoneInfos++; InfoZoneArray[TotalNumberOfInfoZones].zoneinfoNight = TotalNumberOfZoneInfos++; TotalNumberOfInfoZones++; break; case ZONE_MAPZONE: assert(TotalNumberOfMapZones < NUMMAPZONES); strcpy(MapZoneArray[TotalNumberOfMapZones].name, tmpname); MapZoneArray[TotalNumberOfMapZones].type = type; MapZoneArray[TotalNumberOfMapZones].minx = minx; MapZoneArray[TotalNumberOfMapZones].miny = miny; MapZoneArray[TotalNumberOfMapZones].minz = minz; MapZoneArray[TotalNumberOfMapZones].maxx = maxx; MapZoneArray[TotalNumberOfMapZones].maxy = maxy; MapZoneArray[TotalNumberOfMapZones].maxz = maxz; MapZoneArray[TotalNumberOfMapZones].level = level; TotalNumberOfMapZones++; break; } } void CTheZones::PostZoneCreation(void) { int i; for(i = 1; i < TotalNumberOfNavigationZones; i++) InsertZoneIntoZoneHierarchy(&NavigationZoneArray[i]); InitialiseAudioZoneArray(); #ifndef MASTER CheckZonesForOverlap(); #endif } void CTheZones::CheckZonesForOverlap(void) { int i, j; char str[116]; for(i = 1; i < TotalNumberOfInfoZones; i++){ ZoneIsEntirelyContainedWithinOtherZone(&InfoZoneArray[i], &InfoZoneArray[0]); for(j = 1; j < TotalNumberOfInfoZones; j++) if(i != j && ZoneIsEntirelyContainedWithinOtherZone(&InfoZoneArray[i], &InfoZoneArray[j])) sprintf(str, "Info zone %s contains %s\n", InfoZoneArray[j].name, InfoZoneArray[i].name); } } void CTheZones::InsertZoneIntoZoneHierarchy(CZone *zone) { zone->child = nil; zone->parent = nil; zone->next = nil; InsertZoneIntoZoneHierRecursive(zone, &NavigationZoneArray[0]); } bool CTheZones::InsertZoneIntoZoneHierRecursive(CZone *inner, CZone *outer) { int n; CZone *child, *next, *insert; // return false if inner was not inserted into outer if(outer == nil || !ZoneIsEntirelyContainedWithinOtherZone(inner, outer)) return false; // try to insert inner into children of outer for(child = outer->child; child; child = child->next) if(InsertZoneIntoZoneHierRecursive(inner, child)) return true; // insert inner as child of outer // count number of outer's children contained within inner n = 0; for(child = outer->child; child; child = child->next) if(ZoneIsEntirelyContainedWithinOtherZone(child, inner)) n++; inner->next = outer->child; inner->parent = outer; outer->child = inner; // move children from outer to inner if(n){ insert = inner; for(child = inner->next; child; child = next){ next = child->next; if(ZoneIsEntirelyContainedWithinOtherZone(child,inner)){ insert->next = child->next; child->parent = inner; child->next = inner->child; inner->child = child; }else insert = child; } } return true; } bool CTheZones::ZoneIsEntirelyContainedWithinOtherZone(CZone *inner, CZone *outer) { char tmp[100]; if(inner->minx < outer->minx || inner->maxx > outer->maxx || inner->miny < outer->miny || inner->maxy > outer->maxy || inner->minz < outer->minz || inner->maxz > outer->maxz){ CVector vmin(inner->minx, inner->miny, inner->minz); if(PointLiesWithinZone(&vmin, outer)) sprintf(tmp, "Overlapping zones %s and %s\n", inner->name, outer->name); CVector vmax(inner->maxx, inner->maxy, inner->maxz); if(PointLiesWithinZone(&vmax, outer)) sprintf(tmp, "Overlapping zones %s and %s\n", inner->name, outer->name); return false; } return true; } bool CTheZones::PointLiesWithinZone(const CVector *v, CZone *zone) { return zone->minx <= v->x && v->x <= zone->maxx && zone->miny <= v->y && v->y <= zone->maxy && zone->minz <= v->z && v->z <= zone->maxz; } eLevelName CTheZones::GetLevelFromPosition(CVector const *v) { int i; // char tmp[116]; // if(!PointLiesWithinZone(v, &MapZoneArray[0])) // sprintf(tmp, "x = %.3f y= %.3f z = %.3f\n", v.x, v.y, v.z); for(i = 1; i < TotalNumberOfMapZones; i++) if(PointLiesWithinZone(v, &MapZoneArray[i])) return MapZoneArray[i].level; return MapZoneArray[0].level; } CZone* CTheZones::FindInformationZoneForPosition(const CVector *v) { int i; // char tmp[116]; // if(!PointLiesWithinZone(v, &InfoZoneArray[0])) // sprintf(tmp, "x = %.3f y= %.3f z = %.3f\n", v.x, v.y, v.z); for(i = 1; i < TotalNumberOfInfoZones; i++) if(PointLiesWithinZone(v, &InfoZoneArray[i])) return &InfoZoneArray[i]; return &InfoZoneArray[0]; } CZone* CTheZones::FindSmallestNavigationZoneForPosition(const CVector *v, bool findDefault, bool findNavig) { CZone *best = nil; if(findDefault && NavigationZoneArray[0].type == ZONE_DEFAULT || findNavig && NavigationZoneArray[0].type == ZONE_NAVIG) best = &NavigationZoneArray[0]; // zone to test next CZone *zone = NavigationZoneArray[0].child; while(zone) // if in zone, descent into children if(PointLiesWithinZone(v, zone)){ if(findDefault && zone->type == ZONE_DEFAULT || findNavig && zone->type == ZONE_NAVIG) best = zone; zone = zone->child; // otherwise try next zone }else zone = zone->next; return best; } int16 CTheZones::FindZoneByLabelAndReturnIndex(char *name, eZoneType type) { char str[8]; memset(str, 0, 8); strncpy(str, name, 8); switch(type){ case ZONE_DEFAULT: case ZONE_NAVIG: for(FindIndex = 0; FindIndex < TotalNumberOfNavigationZones; FindIndex++) if(strcmp(GetNavigationZone(FindIndex)->name, name) == 0) return FindIndex; break; case ZONE_INFO: for(FindIndex = 0; FindIndex < TotalNumberOfInfoZones; FindIndex++) if(strcmp(GetInfoZone(FindIndex)->name, name) == 0) return FindIndex; break; case ZONE_MAPZONE: for(FindIndex = 0; FindIndex < TotalNumberOfMapZones; FindIndex++) if(strcmp(GetMapZone(FindIndex)->name, name) == 0) return FindIndex; break; } return -1; } int16 CTheZones::FindNextZoneByLabelAndReturnIndex(char *name, eZoneType type) { char str[8]; ++FindIndex; memset(str, 0, 8); strncpy(str, name, 8); switch(type){ case ZONE_DEFAULT: case ZONE_NAVIG: for(; FindIndex < TotalNumberOfNavigationZones; FindIndex++) if(strcmp(GetNavigationZone(FindIndex)->name, name) == 0) return FindIndex; break; case ZONE_INFO: for(; FindIndex < TotalNumberOfInfoZones; FindIndex++) if(strcmp(GetInfoZone(FindIndex)->name, name) == 0) return FindIndex; break; case ZONE_MAPZONE: for(; FindIndex < TotalNumberOfMapZones; FindIndex++) if(strcmp(GetMapZone(FindIndex)->name, name) == 0) return FindIndex; break; } return -1; } CZoneInfo* CTheZones::GetZoneInfo(const CVector *v, uint8 day) { CZone *zone; zone = FindInformationZoneForPosition(v); if(zone == nil) return &ZoneInfoArray[0]; return &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; } void CTheZones::GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info) { CZoneInfo *day, *night; float d, n; int i; day = GetZoneInfo(pos, 1); night = GetZoneInfo(pos, 0); if(CClock::GetIsTimeInRange(8, 19)) *info = *day; else if(CClock::GetIsTimeInRange(22, 5)) *info = *night; else{ if(CClock::GetIsTimeInRange(19, 22)){ n = (CClock::GetHours() - 19) / 3.0f; assert(n >= 0.0f && n <= 1.0f); d = 1.0f - n; }else{ d = (CClock::GetHours() - 5) / 3.0f; assert(d >= 0.0f && d <= 1.0f); n = 1.0f - d; } info->carDensity = day->carDensity * d + night->carDensity * n; for(i = 0; i < ARRAY_SIZE(info->carThreshold); i++) info->carThreshold[i] = day->carThreshold[i] * d + night->carThreshold[i] * n; for(i = 0; i < ARRAY_SIZE(info->boatThreshold); i++) info->boatThreshold[i] = day->boatThreshold[i] * d + night->boatThreshold[i] * n; for(i = 0; i < ARRAY_SIZE(info->gangThreshold); i++) info->gangThreshold[i] = day->gangThreshold[i] * d + night->gangThreshold[i] * n; info->copThreshold = day->copThreshold * d + night->copThreshold * n; info->pedDensity = day->pedDensity * d + night->pedDensity * n; info->copPedThreshold = day->copPedThreshold * d + night->copPedThreshold * n; for(i = 0; i < ARRAY_SIZE(info->gangPedThreshold); i++) info->gangPedThreshold[i] = day->gangPedThreshold[i] * d + night->gangPedThreshold[i] * n; } if(CClock::GetIsTimeInRange(5, 19)) info->pedGroup = day->pedGroup; else info->pedGroup = night->pedGroup; } void CTheZones::SetZoneCarInfo(uint16 zoneid, uint8 day, int16 carDensity, int16 copCarDensity, const int16 *gangCarDensities) { CZone *zone; CZoneInfo *info; zone = GetInfoZone(zoneid); info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; info->carDensity = carDensity; info->copThreshold = copCarDensity; info->gangThreshold[0] = gangCarDensities[0] + copCarDensity; info->gangThreshold[1] = gangCarDensities[1] + info->gangThreshold[0]; info->gangThreshold[2] = gangCarDensities[2] + info->gangThreshold[1]; info->gangThreshold[3] = gangCarDensities[3] + info->gangThreshold[2]; info->gangThreshold[4] = gangCarDensities[4] + info->gangThreshold[3]; info->gangThreshold[5] = gangCarDensities[5] + info->gangThreshold[4]; info->gangThreshold[6] = gangCarDensities[6] + info->gangThreshold[5]; info->gangThreshold[7] = gangCarDensities[7] + info->gangThreshold[6]; info->gangThreshold[8] = gangCarDensities[8] + info->gangThreshold[7]; } void CTheZones::SetZoneCivilianCarInfo(uint16 zoneid, uint8 day, const int16* carDensities, const int16* boatDensities) { CZone* zone; CZoneInfo* info; zone = GetInfoZone(zoneid); info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; info->carThreshold[0] = carDensities[0]; for (int i = 1; i < CCarCtrl::NUM_CAR_CLASSES; i++) info->carThreshold[i] = carDensities[i] + info->carThreshold[i-1]; info->boatThreshold[0] = boatDensities[0]; for (int i = 1; i < CCarCtrl::NUM_BOAT_CLASSES; i++) info->boatThreshold[i] = boatDensities[i] + info->boatThreshold[i - 1]; } void CTheZones::SetZonePedInfo(uint16 zoneid, uint8 day, int16 pedDensity, int16 gang0Density, int16 gang1Density, int16 gang2Density, int16 gang3Density, int16 gang4Density, int16 gang5Density, int16 gang6Density, int16 gang7Density, int16 gang8Density, int16 copDensity) { CZone *zone; CZoneInfo *info; zone = GetInfoZone(zoneid); info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight]; info->pedDensity = pedDensity; info->copPedThreshold = copDensity; info->gangPedThreshold[0] = gang0Density; info->gangPedThreshold[1] = gang1Density; info->gangPedThreshold[2] = gang2Density; info->gangPedThreshold[3] = gang3Density; info->gangPedThreshold[4] = gang4Density; info->gangPedThreshold[5] = gang5Density; info->gangPedThreshold[6] = gang6Density; info->gangPedThreshold[7] = gang7Density; info->gangPedThreshold[8] = gang8Density; info->gangPedThreshold[0] += info->copPedThreshold; info->gangPedThreshold[1] += info->gangPedThreshold[0]; info->gangPedThreshold[2] += info->gangPedThreshold[1]; info->gangPedThreshold[3] += info->gangPedThreshold[2]; info->gangPedThreshold[4] += info->gangPedThreshold[3]; info->gangPedThreshold[5] += info->gangPedThreshold[4]; info->gangPedThreshold[6] += info->gangPedThreshold[5]; info->gangPedThreshold[7] += info->gangPedThreshold[6]; info->gangPedThreshold[8] += info->gangPedThreshold[7]; } // unused void CTheZones::SetCarDensity(uint16 zoneid, uint8 day, uint16 cardensity) { CZone *zone; zone = GetInfoZone(zoneid); ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight].carDensity = cardensity; } // unused void CTheZones::SetPedDensity(uint16 zoneid, uint8 day, uint16 peddensity) { CZone *zone; zone = GetInfoZone(zoneid); ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight].pedDensity = peddensity; } void CTheZones::SetPedGroup(uint16 zoneid, uint8 day, uint16 pedgroup) { CZone *zone; zone = GetInfoZone(zoneid); ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight].pedGroup = pedgroup; } int16 CTheZones::FindAudioZone(CVector *pos) { int i; for(i = 0; i < NumberOfAudioZones; i++) if(PointLiesWithinZone(pos, GetAudioZone(i))) return i; return -1; } void CTheZones::AddZoneToAudioZoneArray(CZone *zone) { int i, z; if(zone->type != ZONE_DEFAULT) return; /* This is a bit stupid */ z = -1; for(i = 0; i < NUMNAVIGZONES; i++) if(&NavigationZoneArray[i] == zone) z = i; assert(NumberOfAudioZones < NUMAUDIOZONES); AudioZoneArray[NumberOfAudioZones++] = z; } void CTheZones::InitialiseAudioZoneArray(void) { bool gonext; CZone *zone; gonext = false; zone = &NavigationZoneArray[0]; // Go deep first, // set gonext when backing up a level to visit the next child while(zone) if(gonext){ AddZoneToAudioZoneArray(zone); if(zone->next){ gonext = false; zone = zone->next; }else zone = zone->parent; }else if(zone->child) zone = zone->child; else{ AddZoneToAudioZoneArray(zone); if(zone->next) zone = zone->next; else{ gonext = true; zone = zone->parent; } } } void CTheZones::SaveAllZones(uint8 *buffer, uint32 *size) { INITSAVEBUF int i; #define CZONE_SAVE_SIZE (sizeof(char)*8+sizeof(float)+sizeof(float)+sizeof(float)+sizeof(float)+sizeof(float)+sizeof(float)+sizeof(eZoneType)+sizeof(eLevelName)+sizeof(int16)+sizeof(int16)+sizeof(int32)+sizeof(int32)+sizeof(int32)) *size = SAVE_HEADER_SIZE + sizeof(m_CurrLevel) + sizeof(FindIndex) + sizeof(int16) // padding + CZONE_SAVE_SIZE * ARRAY_SIZE(NavigationZoneArray) + CZONE_SAVE_SIZE * ARRAY_SIZE(InfoZoneArray) + sizeof(ZoneInfoArray) + sizeof(TotalNumberOfNavigationZones) + sizeof(TotalNumberOfInfoZones) + sizeof(TotalNumberOfZoneInfos) + sizeof(int16) // padding + CZONE_SAVE_SIZE * ARRAY_SIZE(MapZoneArray) + sizeof(AudioZoneArray) + sizeof(TotalNumberOfMapZones) + sizeof(NumberOfAudioZones); #undef CZONE_SAVE_SIZE uint32 length = 0; WriteSaveHeaderWithLength(buffer, length, 'Z', 'N', 'S', '\0', *size - SAVE_HEADER_SIZE); WriteSaveBuf(buffer, length, m_CurrLevel); WriteSaveBuf(buffer, length, FindIndex); WriteSaveBuf(buffer, length, (int16)0); // padding for(i = 0; i < ARRAY_SIZE(NavigationZoneArray); i++) SaveOneZone(&NavigationZoneArray[i], &buffer, &length, ZONE_NAVIG); for(i = 0; i < ARRAY_SIZE(InfoZoneArray); i++) SaveOneZone(&InfoZoneArray[i], &buffer, &length, ZONE_INFO); for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++) WriteSaveBuf(buffer, length, ZoneInfoArray[i]); WriteSaveBuf(buffer, length, TotalNumberOfNavigationZones); WriteSaveBuf(buffer, length, TotalNumberOfInfoZones); WriteSaveBuf(buffer, length, TotalNumberOfZoneInfos); WriteSaveBuf(buffer, length, (int16)0); // padding for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++) SaveOneZone(&MapZoneArray[i], &buffer, &length, ZONE_MAPZONE); for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++) WriteSaveBuf(buffer, length, AudioZoneArray[i]); WriteSaveBuf(buffer, length, TotalNumberOfMapZones); WriteSaveBuf(buffer, length, NumberOfAudioZones); VALIDATESAVEBUF(*size) } void CTheZones::SaveOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType) { WriteSaveBuf(*buffer, *length, *(uint32*)&zone->name[0]); WriteSaveBuf(*buffer, *length, *(uint32*)&zone->name[4]); WriteSaveBuf(*buffer, *length, zone->minx); WriteSaveBuf(*buffer, *length, zone->miny); WriteSaveBuf(*buffer, *length, zone->minz); WriteSaveBuf(*buffer, *length, zone->maxx); WriteSaveBuf(*buffer, *length, zone->maxy); WriteSaveBuf(*buffer, *length, zone->maxz); WriteSaveBuf(*buffer, *length, zone->type); WriteSaveBuf(*buffer, *length, zone->level); WriteSaveBuf(*buffer, *length, zone->zoneinfoDay); WriteSaveBuf(*buffer, *length, zone->zoneinfoNight); int32 zoneId; zoneId = GetIndexForZonePointer(zone->child); WriteSaveBuf(*buffer, *length, zoneId); zoneId = GetIndexForZonePointer(zone->parent); WriteSaveBuf(*buffer, *length, zoneId); zoneId = GetIndexForZonePointer(zone->next); WriteSaveBuf(*buffer, *length, zoneId); } void CTheZones::LoadAllZones(uint8 *buffer, uint32 size) { INITSAVEBUF int i; uint32 length = 0; CheckSaveHeaderWithLength(buffer, length, 'Z', 'N', 'S', '\0', size - SAVE_HEADER_SIZE); m_CurrLevel = ReadSaveBuf(buffer, length); FindIndex = ReadSaveBuf(buffer, length); ReadSaveBuf(buffer, length); for(i = 0; i < ARRAY_SIZE(NavigationZoneArray); i++) LoadOneZone(&NavigationZoneArray[i], &buffer, &length, ZONE_NAVIG); for (i = 0; i < ARRAY_SIZE(InfoZoneArray); i++) LoadOneZone(&InfoZoneArray[i], &buffer, &length, ZONE_INFO); for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++) ZoneInfoArray[i] = ReadSaveBuf(buffer, length); TotalNumberOfNavigationZones = ReadSaveBuf(buffer, length); TotalNumberOfInfoZones = ReadSaveBuf(buffer, length); TotalNumberOfZoneInfos = ReadSaveBuf(buffer, length); ReadSaveBuf(buffer, length); for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++) LoadOneZone(&MapZoneArray[i], &buffer, &length, ZONE_MAPZONE); for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++) AudioZoneArray[i] = ReadSaveBuf(buffer, length); TotalNumberOfMapZones = ReadSaveBuf(buffer, length); NumberOfAudioZones = ReadSaveBuf(buffer, length); VALIDATESAVEBUF(size) } void CTheZones::LoadOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType) { *(uint32*)&zone->name[0] = ReadSaveBuf(*buffer, *length); *(uint32*)&zone->name[4] = ReadSaveBuf(*buffer, *length); zone->minx = ReadSaveBuf(*buffer, *length); zone->miny = ReadSaveBuf(*buffer, *length); zone->minz = ReadSaveBuf(*buffer, *length); zone->maxx = ReadSaveBuf(*buffer, *length); zone->maxy = ReadSaveBuf(*buffer, *length); zone->maxz = ReadSaveBuf(*buffer, *length); zone->type = ReadSaveBuf(*buffer, *length); zone->level = ReadSaveBuf(*buffer, *length); zone->zoneinfoDay = ReadSaveBuf(*buffer, *length); zone->zoneinfoNight = ReadSaveBuf(*buffer, *length); int32 zoneId; zoneId = ReadSaveBuf(*buffer, *length); zone->child = GetPointerForZoneIndex(zoneId); zoneId = ReadSaveBuf(*buffer, *length); zone->parent = GetPointerForZoneIndex(zoneId); zoneId = ReadSaveBuf(*buffer, *length); zone->next = GetPointerForZoneIndex(zoneId); } ================================================ FILE: src/core/Zones.h ================================================ #pragma once #include "Game.h" #include "Gangs.h" #include "CarCtrl.h" enum eZoneType { ZONE_DEFAULT, ZONE_NAVIG, ZONE_INFO, ZONE_MAPZONE, }; class CZone { public: char name[8]; float minx; float miny; float minz; float maxx; float maxy; float maxz; eZoneType type; eLevelName level; int16 zoneinfoDay; int16 zoneinfoNight; CZone *child; CZone *parent; CZone *next; wchar *GetTranslatedName(void); }; class CZoneInfo { public: // Car data int16 carDensity; int16 carThreshold[CCarCtrl::NUM_CAR_CLASSES]; int16 boatThreshold[CCarCtrl::NUM_BOAT_CLASSES]; int16 gangThreshold[NUM_GANGS]; int16 copThreshold; // Ped data uint16 pedDensity; uint16 gangPedThreshold[NUM_GANGS]; uint16 copPedThreshold; uint16 pedGroup; }; class CTheZones { static int16 FindIndex; static uint16 NumberOfAudioZones; static int16 AudioZoneArray[NUMAUDIOZONES]; static uint16 TotalNumberOfMapZones; static uint16 TotalNumberOfInfoZones; static uint16 TotalNumberOfNavigationZones; static CZone InfoZoneArray[NUMINFOZONES]; static CZone MapZoneArray[NUMMAPZONES]; static CZone NavigationZoneArray[NUMNAVIGZONES]; static uint16 TotalNumberOfZoneInfos; static CZoneInfo ZoneInfoArray[2*NUMINFOZONES]; public: static eLevelName m_CurrLevel; static void Init(void); static void Update(void); static void CreateZone(char *name, eZoneType type, float minx, float miny, float minz, float maxx, float maxy, float maxz, eLevelName level); static CZone *GetInfoZone(uint16 i) { return &InfoZoneArray[i]; } static CZone *GetNavigationZone(uint16 i) { return &NavigationZoneArray[i]; } static CZone *GetMapZone(uint16 i) { return &MapZoneArray[i]; } static CZone *GetAudioZone(uint16 i) { return &NavigationZoneArray[AudioZoneArray[i]]; } static void PostZoneCreation(void); static void CheckZonesForOverlap(void); static void InsertZoneIntoZoneHierarchy(CZone *zone); static bool InsertZoneIntoZoneHierRecursive(CZone *z1, CZone *z2); static bool ZoneIsEntirelyContainedWithinOtherZone(CZone *z1, CZone *z2); static bool PointLiesWithinZone(const CVector *v, CZone *zone); static eLevelName GetLevelFromPosition(const CVector *v); static CZone *FindInformationZoneForPosition(const CVector *v); static CZone *FindSmallestNavigationZoneForPosition(const CVector *v, bool findDefault, bool findNavig); static int16 FindZoneByLabelAndReturnIndex(char *name, eZoneType type); static int16 FindNextZoneByLabelAndReturnIndex(char *name, eZoneType type); static CZoneInfo *GetZoneInfo(const CVector *v, uint8 day); static void GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info); static void SetZoneCarInfo(uint16 zoneid, uint8 day, int16 carDensity, int16 copCarDensity, const int16 *gangCarDensities /*[NUMGANGS]*/); static void SetZoneCivilianCarInfo(uint16 zoneid, uint8 day, const int16* carDensities, const int16* boatDensities); static void SetZonePedInfo(uint16 zoneid, uint8 day, int16 pedDensity, int16 gang0Density, int16 gang1Density, int16 gang2Density, int16 gang3Density, int16 gang4Density, int16 gang5Density, int16 gang6Density, int16 gang7Density, int16 gang8Density, int16 copDensity); static void SetCarDensity(uint16 zoneid, uint8 day, uint16 cardensity); static void SetPedDensity(uint16 zoneid, uint8 day, uint16 peddensity); static void SetPedGroup(uint16 zoneid, uint8 day, uint16 pedgroup); static int16 FindAudioZone(CVector *pos); static CZone *GetPointerForZoneIndex(ssize_t i) { return i == -1 ? nil : &NavigationZoneArray[i]; } static ssize_t GetIndexForZonePointer(CZone *zone) { return zone == nil ? -1 : zone - NavigationZoneArray; } static void AddZoneToAudioZoneArray(CZone *zone); static void InitialiseAudioZoneArray(void); static void SaveAllZones(uint8 *buffer, uint32 *length); static void SaveOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType); static void LoadAllZones(uint8 *buffer, uint32 length); static void LoadOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType); }; ================================================ FILE: src/core/common.h ================================================ #pragma once #define _CRT_SECURE_NO_WARNINGS #define _USE_MATH_DEFINES #pragma warning(disable: 4244) // int to float #pragma warning(disable: 4800) // int to bool #pragma warning(disable: 4838) // narrowing conversion #pragma warning(disable: 4996) // POSIX names #ifdef __MWERKS__ #define __STDC_LIMIT_MACROS // so we get UINT32_MAX etc #endif #include #include #include #ifdef __MWERKS__ #define AUDIO_MSS #define RWLIBS // codewarrior doesn't support project level defines - so not even this is enough, but still catches most ifdefs #endif #if !defined RW_D3D9 && defined LIBRW #undef WITHD3D #undef WITHDINPUT #endif #if (defined WITHD3D && !defined LIBRW) #define WITHWINDOWS #endif #if defined _WIN32 && defined WITHWINDOWS && !defined _INC_WINDOWS #include #endif #ifdef WITHD3D #ifdef LIBRW #define WITH_D3D // librw includes d3d9 itself via this right now #else #ifndef USE_D3D9 #include #else #include #endif #endif #endif #ifdef WITHDINPUT #define DIRECTINPUT_VERSION 0x0800 #include #endif #include #include // gotta put this somewhere #ifdef LIBRW #define STREAMPOS(str) ((str)->tell()) #define STREAMFILE(str) (((rw::StreamFile*)(str))->file) #define HIERNODEINFO(hier) ((hier)->nodeInfo) #define HIERNODEID(hier, i) ((hier)->nodeInfo[i].id) #define HANIMFRAME(anim, i) ((RwUInt8*)(anim)->keyframes + (i)*(anim)->interpInfo->animKeyFrameSize) #else #define RWHALFPIXEL // always d3d #define STREAMPOS(str) ((str)->Type.memory.position) #define STREAMFILE(str) ((str)->Type.file.fpFile) #define HIERNODEINFO(hier) ((hier)->pNodeInfo) #define HIERNODEID(hier, i) ((hier)->pNodeInfo[i].nodeID) #define HANIMFRAME(anim, i) ((RwUInt8*)(anim)->pFrames + (i)*(anim)->interpInfo->keyFrameSize) #define RpHAnimStdInterpFrame RpHAnimStdKeyFrame #endif #ifdef RWHALFPIXEL #define HALFPX (0.5f) #else #define HALFPX (0.0f) #endif #define rwVENDORID_ROCKSTAR 0x0253F2 #define Max(a,b) ((a) > (b) ? (a) : (b)) #define Min(a,b) ((a) < (b) ? (a) : (b)) // Use this to add const that wasn't there in the original code #define Const const typedef uint8_t uint8; typedef int8_t int8; typedef uint16_t uint16; typedef int16_t int16; #ifndef __MWERKS__ typedef uint32_t uint32; typedef int32_t int32; #else typedef unsigned int uint32; typedef int int32; #endif typedef uintptr_t uintptr; typedef intptr_t intptr; typedef uint64_t uint64; typedef int64_t int64; // hardcode ucs-2 typedef uint16_t wchar; typedef uint8 bool8; typedef uint16 bool16; typedef uint32 bool32; #if defined(_MSC_VER) || defined (__MWERKS__) typedef ptrdiff_t ssize_t; #endif #ifndef nil #define nil NULL #endif #include "config.h" #include #include #ifdef __GNUC__ #define TYPEALIGN(n) __attribute__ ((aligned (n))) #else #ifdef _MSC_VER #define TYPEALIGN(n) __declspec(align(n)) #else #define TYPEALIGN(n) // unknown compiler...ignore #endif #endif #define ALIGNPTR(p) (void*)((((uintptr)(void*)p) + sizeof(void*)-1) & ~(sizeof(void*)-1)) // PDP-10 like byte functions #define MASK(p, s) (((1<<(s))-1) << (p)) inline uint32 dpb(uint32 b, uint32 p, uint32 s, uint32 w) { uint32 m = MASK(p,s); return (w & ~m) | ((b<>p & (1<r == right.r && this->g == right.g && this->b == right.b && this->a == right.a; } bool operator !=(const CRGBA &right) { return !(*this == right); } CRGBA &operator =(const CRGBA &right) { this->r = right.r; this->g = right.g; this->b = right.b; this->a = right.a; return *this; } #ifdef RWCORE_H operator RwRGBA &(void) { return rwRGBA; } operator RwRGBA *(void) { return &rwRGBA; } operator RwRGBA (void) const { return rwRGBA; } CRGBA &operator =(const RwRGBA &right) { this->r = right.red; this->g = right.green; this->b = right.blue; this->a = right.alpha; return *this; } #endif }; #if (defined(_MSC_VER)) extern int strcasecmp(const char *str1, const char *str2); extern int strncasecmp(const char *str1, const char *str2, size_t len); #endif extern wchar *AllocUnicode(const char*src); #define clamp(v, low, high) ((v)<(low) ? (low) : (v)>(high) ? (high) : (v)) #define clamp2(v, center, radius) ((v) < (center) ? Max(v, center - radius) : Min(v, center + radius)) inline float sq(float x) { return x*x; } #define SQR(x) ((x) * (x)) #ifdef __MWERKS__ #define M_E 2.71828182845904523536 // e #define M_LOG2E 1.44269504088896340736 // log2(e) #define M_LOG10E 0.434294481903251827651 // log10(e) #define M_LN2 0.693147180559945309417 // ln(2) #define M_LN10 2.30258509299404568402 // ln(10) #define M_PI 3.14159265358979323846 // pi #define M_PI_2 1.57079632679489661923 // pi/2 #define M_PI_4 0.785398163397448309616 // pi/4 #define M_1_PI 0.318309886183790671538 // 1/pi #define M_2_PI 0.636619772367581343076 // 2/pi #define M_2_SQRTPI 1.12837916709551257390 // 2/sqrt(pi) #define M_SQRT2 1.41421356237309504880 // sqrt(2) #define M_SQRT1_2 0.707106781186547524401 // 1/sqrt(2) #endif #define PI (float)M_PI #define TWOPI (PI*2) #define HALFPI (PI/2) #define DEGTORAD(x) ((x) * PI / 180.0f) #define RADTODEG(x) ((x) * 180.0f / PI) #ifdef USE_PS2_RAND #define MYRAND_MAX 65535 #else #define MYRAND_MAX 32767 #endif int myrand(void); void mysrand(unsigned int seed); void re3_debug(const char *format, ...); void re3_trace(const char *filename, unsigned int lineno, const char *func, const char *format, ...); void re3_assert(const char *expr, const char *filename, unsigned int lineno, const char *func); void re3_usererror(const char *format, ...); #define DEBUGBREAK() __debugbreak(); // Switch to enable development messages. #if 1 #define DEV(f, ...) #else #define DEV(f, ...) re3_debug("[DEV]: " f, ## __VA_ARGS__) #endif #ifdef __MWERKS__ void debug(char *f, ...); void Error(char *f, ...); __inline__ void TRACE(char *f, ...) { } // this is re3 only, and so the function needs to be inline - this way no call actually gets placed // USERERROR only gets used in oal builds ... once #else #define debug(f, ...) re3_debug("[DBG]: " f, ## __VA_ARGS__) #define Error(f, ...) re3_debug("[ERROR]: " f, ## __VA_ARGS__) #ifndef MASTER #define TRACE(f, ...) re3_trace(__FILE__, __LINE__, __FUNCTION__, f, ## __VA_ARGS__) #define USERERROR(f, ...) re3_usererror(f, ## __VA_ARGS__) #else #define TRACE(f, ...) #define USERERROR(f, ...) #endif #endif #ifndef MASTER #define assert(_Expression) (void)( (!!(_Expression)) || (re3_assert(#_Expression, __FILE__, __LINE__, __FUNCTION__), 0) ) #else #define assert(_Expression) (_Expression) #endif #define ASSERT assert #ifdef __MWERKS__ #define static_assert(bool_constexpr, message) #endif #define _TODO(x) #define _TODOCONST(x) (x) #ifdef CHECK_STRUCT_SIZES #define VALIDATE_SIZE(struc, size) static_assert(sizeof(struc) == size, "Invalid structure size of " #struc) #else #define VALIDATE_SIZE(struc, size) #endif #define VALIDATE_OFFSET(struc, member, offset) static_assert(offsetof(struc, member) == offset, "The offset of " #member " in " #struc " is not " #offset "...") #define PERCENT(x, p) ((float(x) * (float(p) / 100.0f))) #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) #define BIT(num) (1<<(num)) #define ABS(a) (((a) < 0) ? (-(a)) : (a)) #define norm(value, min, max) (((value) < (min)) ? 0 : (((value) > (max)) ? 1 : (((value) - (min)) / ((max) - (min))))) #define lerp(norm, min, max) ( (norm) * ((max) - (min)) + (min) ) #define STRINGIFY(x) #x #define STR(x) STRINGIFY(x) #define CONCAT_(x,y) x##y #define CONCAT(x,y) CONCAT_(x,y) #ifdef DEBUGMENU // Tweaking stuff for debugmenu #define TWEAKPATH ___tw___TWEAKPATH #define SETTWEAKPATH(path) static const char *___tw___TWEAKPATH = path; #define TWEAKFUNC(v) static CTweakFunc CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), TWEAKPATH); #define TWEAKFUNCN(v, name) static CTweakFunc CONCAT(___tw___tweak, __COUNTER__)(&v, name, TWEAKPATH); #define TWEAKBOOL(v) static CTweakBool CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), TWEAKPATH); #define TWEAKBOOLN(v, name) static CTweakBool CONCAT(___tw___tweak, __COUNTER__)(&v, name, TWEAKPATH); #define TWEAKINT32(v, lower, upper, step) static CTweakInt32 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); #define TWEAKINT32N(v, lower, upper, step, name) static CTweakInt32 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); #define TWEAKUINT32(v, lower, upper, step) static CTweakUInt32 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); #define TWEAKUINT32N(v, lower, upper, step, name) static CTweakUInt32 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); #define TWEAKINT16(v, lower, upper, step) static CTweakInt16 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); #define TWEAKINT16N(v, lower, upper, step, name) static CTweakInt16 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); #define TWEAKUINT16(v, lower, upper, step) static CTweakUInt16 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); #define TWEAKUINT16N(v, lower, upper, step, name) static CTweakUInt16 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); #define TWEAKINT8(v, lower, upper, step) static CTweakInt8 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); #define TWEAKINT8N(v, lower, upper, step, name) static CTweakInt8 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); #define TWEAKUINT8(v, lower, upper, step) static CTweakUInt8 CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); #define TWEAKUINT8N(v, lower, upper, step, name) static CTweakUInt8 CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); #define TWEAKFLOAT(v, lower, upper, step) static CTweakFloat CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, step, TWEAKPATH); #define TWEAKFLOATN(v, lower, upper, step, name) static CTweakFloat CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, step, TWEAKPATH); #define TWEAKSWITCH(v, lower, upper, str, f) static CTweakSwitch CONCAT(___tw___tweak, __COUNTER__)(&v, STR(v), lower, upper, str, f, TWEAKPATH); #define TWEAKSWITCHN(v, lower, upper, str, f, name) static CTweakSwitch CONCAT(___tw___tweak, __COUNTER__)(&v, name, lower, upper, str, f, TWEAKPATH); // interface class CTweakVar { public: virtual void AddDBG(const char *path) = 0; }; class CTweakVars { public: static void Add(CTweakVar *var); static void AddDBG(const char *path); }; class CTweakFunc : public CTweakVar { const char *m_pPath, *m_pVarName; void (*m_pFunc)(); public: CTweakFunc(void (*pFunc)(), const char *strName, const char *strPath) : m_pPath(strPath), m_pVarName(strName), m_pFunc(pFunc) { CTweakVars::Add(this); } void AddDBG(const char *path); }; class CTweakBool : public CTweakVar { const char *m_pPath, *m_pVarName; bool *m_pBoolVar; public: CTweakBool(bool *pBool, const char *strName, const char *strPath) : m_pPath(strPath), m_pVarName(strName), m_pBoolVar(pBool) { CTweakVars::Add(this); } void AddDBG(const char *path); }; class CTweakSwitch : public CTweakVar { const char *m_pPath, *m_pVarName; void *m_pIntVar; int32 m_nMin, m_nMax; const char **m_aStr; void (*m_pFunc)(); public: CTweakSwitch(void *pInt, const char *strName, int32 nMin, int32 nMax, const char **aStr, void (*pFunc)(), const char *strPath) : m_pPath(strPath), m_pVarName(strName), m_pIntVar(pInt), m_nMin(nMin), m_nMax(nMax), m_aStr(aStr) { CTweakVars::Add(this); } void AddDBG(const char *path); }; #define _TWEEKCLASS(name, type) \ class name : public CTweakVar \ { \ public: \ const char *m_pPath, *m_pVarName; \ type *m_pIntVar, m_nLoawerBound, m_nUpperBound, m_nStep; \ \ name(type *pInt, const char *strName, type nLower, type nUpper, type nStep, \ const char *strPath) \ : m_pPath(strPath), m_pVarName(strName), m_pIntVar(pInt), \ m_nLoawerBound(nLower), m_nUpperBound(nUpper), m_nStep(nStep) \ \ { \ CTweakVars::Add(this); \ } \ \ void AddDBG(const char *path); \ }; _TWEEKCLASS(CTweakInt8, int8); _TWEEKCLASS(CTweakUInt8, uint8); _TWEEKCLASS(CTweakInt16, int16); _TWEEKCLASS(CTweakUInt16, uint16); _TWEEKCLASS(CTweakInt32, int32); _TWEEKCLASS(CTweakUInt32, uint32); _TWEEKCLASS(CTweakFloat, float); #undef _TWEEKCLASS #endif #ifdef VALIDATE_SAVE_SIZE extern int32 _saveBufCount; #define INITSAVEBUF _saveBufCount = 0; #define VALIDATESAVEBUF(b) assert(_saveBufCount == b); #else #define INITSAVEBUF #define VALIDATESAVEBUF(b) #endif inline void SkipSaveBuf(uint8 *&buf, int32 skip) { buf += skip; #ifdef VALIDATE_SAVE_SIZE _saveBufCount += skip; #endif } inline void SkipSaveBuf(uint8*& buf, uint32 &length, int32 skip) { buf += skip; length += skip; #ifdef VALIDATE_SAVE_SIZE _saveBufCount += skip; #endif } template inline const T ReadSaveBuf(uint8 *&buf) { T &value = *(T*)buf; SkipSaveBuf(buf, sizeof(T)); return value; } template inline const T ReadSaveBuf(uint8 *&buf, uint32 &length) { T &value = *(T*)buf; SkipSaveBuf(buf, length, sizeof(T)); return value; } template inline T *WriteSaveBuf(uint8 *&buf, const T &value) { T *p = (T*)buf; *p = value; SkipSaveBuf(buf, sizeof(T)); return p; } template inline T *WriteSaveBuf(uint8 *&buf, uint32 &length, const T &value) { T *p = (T*)buf; *p = value; SkipSaveBuf(buf, length, sizeof(T)); return p; } #define SAVE_HEADER_SIZE (4*sizeof(char)+sizeof(uint32)) #define WriteSaveHeader(buf,a,b,c,d,size) \ WriteSaveBuf(buf, a);\ WriteSaveBuf(buf, b);\ WriteSaveBuf(buf, c);\ WriteSaveBuf(buf, d);\ WriteSaveBuf(buf, size); #define WriteSaveHeaderWithLength(buf,len,a,b,c,d,size) \ WriteSaveBuf(buf, len, a);\ WriteSaveBuf(buf, len, b);\ WriteSaveBuf(buf, len, c);\ WriteSaveBuf(buf, len, d);\ WriteSaveBuf(buf, len, size); #define CheckSaveHeader(buf,a,b,c,d,size)\ assert(ReadSaveBuf(buf) == a);\ assert(ReadSaveBuf(buf) == b);\ assert(ReadSaveBuf(buf) == c);\ assert(ReadSaveBuf(buf) == d);\ assert(ReadSaveBuf(buf) == size); #define CheckSaveHeaderWithLength(buf,len,a,b,c,d,size)\ assert(ReadSaveBuf(buf,len) == a);\ assert(ReadSaveBuf(buf,len) == b);\ assert(ReadSaveBuf(buf,len) == c);\ assert(ReadSaveBuf(buf,len) == d);\ assert(ReadSaveBuf(buf,len) == size); void cprintf(char*, ...); ================================================ FILE: src/core/config.h ================================================ #pragma once // disables (most) stuff that wasn't in original gta-vc.exe - check section at the bottom of this file //#define VANILLA_DEFINES enum Config { NUMPLAYERS = 1, NUMCDIMAGES = 6, // gta3.img duplicates (not used on PC) MAX_CDIMAGES = 8, // additional cdimages MAX_CDCHANNELS = 5, MODELINFOSIZE = 6500, // 4900 on PS2 TXDSTORESIZE = 1385, COLSTORESIZE = 31, EXTRADIRSIZE = 256, CUTSCENEDIRSIZE = 512, SIMPLEMODELSIZE = 3885, TIMEMODELSIZE = 385, CLUMPMODELSIZE = 5, WEAPONMODELSIZE = 37, PEDMODELSIZE = 130, VEHICLEMODELSIZE = 110, TWODFXSIZE = 1210, MAXVEHICLESLOADED = 50, // 70 on mobile NUMOBJECTINFO = 210, // Pool sizes NUMPTRNODES = 50000, NUMENTRYINFOS = 3200, NUMPEDS = 140, NUMVEHICLES = 110, NUMBUILDINGS = 7000, NUMTREADABLES = 1, NUMOBJECTS = 460, NUMDUMMIES = 2340, NUMAUDIOSCRIPTOBJECTS = 192, NUMCOLMODELS = 4400, NUMCUTSCENEOBJECTS = 50, // not a pool in VC NUMANIMBLOCKS = 35, NUMANIMATIONS = 450, NUMTEMPOBJECTS = 40, // Path data NUM_PATHNODES = 9650, NUM_CARPATHLINKS = 3500, NUM_MAPOBJECTS = 1250, NUM_PATHCONNECTIONS = 20400, // Link list lengths NUMALPHALIST = 20, NUMBOATALPHALIST = 20, NUMALPHAENTITYLIST = 200, NUMALPHAUNTERWATERENTITYLIST = 30, NUMCOLCACHELINKS = 50, NUMREFERENCES = 800, // Zones NUMAUDIOZONES = 14, NUMINFOZONES = 169, NUMMAPZONES = 39, NUMNAVIGZONES = 20, // Cull zones NUMATTRIBZONES = 704, NUMOCCLUSIONVOLUMES = 350, NUMACTIVEOCCLUDERS = 48, PATHNODESIZE = 4500, NUMWEATHERS = 7, NUMHOURS = 24, NUMEXTRADIRECTIONALS = 4, NUMANTENNAS = 8, NUMCORONAS = 56, NUMPOINTLIGHTS = 32, NUM3DMARKERS = 32, NUMBRIGHTLIGHTS = 32, NUMSHINYTEXTS = 32, NUMMONEYMESSAGES = 16, NUMPICKUPMESSAGES = 16, NUMBULLETTRACES = 16, NUMMBLURSTREAKS = 4, NUMSKIDMARKS = 32, NUMONSCREENCLOCKS = 1, NUMONSCREENCOUNTERS = 3, NUMRADARBLIPS = 75, NUMGENERALPICKUPS = 320, NUMSCRIPTEDPICKUPS = 16, NUMPICKUPS = NUMGENERALPICKUPS + NUMSCRIPTEDPICKUPS, NUMCOLLECTEDPICKUPS = 20, NUMPACMANPICKUPS = 256, NUMEVENTS = 64, NUM_CARGENS = 185, NUM_PATH_NODES_IN_AUTOPILOT = 8, NUM_ACCIDENTS = 20, NUM_FIRES = 40, NUM_GARAGES = 32, NUM_PROJECTILES = 32, NUM_GLASSPANES = 45, NUM_GLASSENTITIES = 32, NUM_WATERCANNONS = 3, NUMPEDROUTES = 200, NUMPHONES = 50, NUMPEDGROUPS = 67, NUMMODELSPERPEDGROUP = 16, MAXZONEPEDSLOADED = 8, NUMSHOTINFOS = 100, NUMROADBLOCKS = 300, NUM_SCRIPT_ROADBLOCKS = 16, NUMVISIBLEENTITIES = 2000, NUMINVISIBLEENTITIES = 150, NUM_AUDIOENTITY_EVENTS = 4, NUM_PED_COMMENTS_BANKS = 2, NUM_PED_COMMENTS_SLOTS = 20, NUM_SOUNDS_SAMPLES_BANKS = 2, NUM_SOUNDS_SAMPLES_SLOTS = 27, NUM_AUDIOENTITIES = 250, NUM_AUDIO_REFLECTIONS = 8, NUM_SCRIPT_MAX_ENTITIES = 40, NUM_GARAGE_STORED_CARS = 4, NUM_CRANES = 8, NUM_ESCALATORS = 22, NUM_WATER_CREATURES = 8, NUM_EXPLOSIONS = 48, NUM_SETPIECES = 96, NUM_SHORTCUT_START_POINTS = 16 }; // We don't expect to compile for PS2 or Xbox // but it might be interesting for documentation purposes #define GTA_PC //#define GTA_PS2 //#define GTA_XBOX // This enables things from the PS2 version on PC #define GTA_PS2_STUFF // This is enabled for all released games. // any debug stuff that isn't left in any game is not in FINAL //#define FINAL // This is enabled for all released games except mobile // any debug stuff that is only left in mobile, is not in MASTER //#define MASTER // once and for all: // pc: FINAL & MASTER // mobile: FINAL // MASTER builds must be FINAL #ifdef MASTER #define FINAL #endif // Version defines #define GTAVC_PS2 400 #define GTAVC_PC_10 410 #define GTAVC_PC_11 411 #define GTAVC_PC_JAP 412 // TODO? maybe something for xbox or android? #define GTA_VERSION GTAVC_PC_11 // TODO(MIAMI): someone ought to find and check out uses of these defines: //#define GTA3_STEAM_PATCH //#define GTAVC_JP_PATCH // quality of life fixes that should also be in FINAL #define NASTY_GAME // nasty game for all languages #define NO_CDCHECK // those infamous texts #define DRAW_GAME_VERSION_TEXT #ifdef DRAW_GAME_VERSION_TEXT // unlike R* development builds, ours has runtime switch on debug menu & .ini, and disabled as default. #define USE_OUR_VERSIONING // If you disable this then game will fetch version from peds.col, as R* did while in development #endif // Memory allocation and compression // #define USE_CUSTOM_ALLOCATOR // use CMemoryHeap for allocation. use with care, not finished yet //#define COMPRESSED_COL_VECTORS // use compressed vectors for collision vertices //#define ANIM_COMPRESSION // only keep most recently used anims uncompressed #if defined GTA_PS2 # define GTA_PS2_STUFF # define RANDOMSPLASH //# define USE_CUSTOM_ALLOCATOR # define VU_COLLISION #elif defined GTA_PC # ifdef GTA_PS2_STUFF # define USE_PS2_RAND # define RANDOMSPLASH // use random splash as on PS2 # define PS2_MATFX # endif # define PC_PLAYER_CONTROLS // mouse player/cam mode # define GTA_REPLAY # define GTA_SCENE_EDIT #elif defined GTA_XBOX #endif #ifdef VU_COLLISION #define COMPRESSED_COL_VECTORS // currently need compressed vectors in this code #endif #ifdef MASTER // only in master builds #undef DRAW_GAME_VERSION_TEXT #else // not in master builds #define VALIDATE_SAVE_SIZE #define DEBUGMENU #endif #ifdef FINAL // in all games # define USE_MY_DOCUMENTS // use my documents directory for user files #else // not in any game # define CHATTYSPLASH // print what the game is loading # define TIMEBARS // print debug timers #endif #define FIX_BUGS // fixes bugs that we've came across during reversing. You can undefine this only on release builds. #define MORE_LANGUAGES // Add more translations to the game #define COMPATIBLE_SAVES // this allows changing structs while keeping saves compatible #define LOAD_INI_SETTINGS // as the name suggests. fundamental for CUSTOM_FRONTEND_OPTIONS #define FIX_HIGH_FPS_BUGS_ON_FRONTEND #define NO_MOVIES // add option to disable intro videos #if defined(__LP64__) || defined(_WIN64) #define FIX_BUGS_64 // Must have fixes to be able to run 64 bit build #endif #define ASCII_STRCMP // use faster ascii str comparisons #if !defined _WIN32 || defined __MWERKS__ || defined __MINGW32__ || defined VANILLA_DEFINES #undef ASCII_STRCMP #endif // Just debug menu entries #ifdef DEBUGMENU #define RELOADABLES // some debug menu options to reload TXD files #define MISSION_SWITCHER // from debug menu #endif // Rendering/display #define ASPECT_RATIO_SCALE // Not just makes everything scale with aspect ratio, also adds support for all aspect ratios #define PROPER_SCALING // use original DEFAULT_SCREEN_WIDTH/DEFAULT_SCREEN_HEIGHT from PS2 instead of PC(R* changed HEIGHT here to make radar look better, but broke other hud elements aspect ratio). #define DEFAULT_NATIVE_RESOLUTION // Set default video mode to your native resolution (fixes Windows 10 launch) #define USE_TXD_CDIMAGE // generate and load textures from txd.img #define PS2_ALPHA_TEST // emulate ps2 alpha test #define IMPROVED_VIDEOMODE // save and load videomode parameters instead of a magic number #define DISABLE_LOADING_SCREEN // disable the loading screen which vastly improves the loading time #define DISABLE_VSYNC_ON_TEXTURE_CONVERSION // make texture conversion work faster by disabling vsync #define ANISOTROPIC_FILTERING // set all textures to max anisotropic filtering //#define USE_TEXTURE_POOL #ifdef LIBRW #define EXTENDED_COLOURFILTER // more options for colour filter (replaces mblur) #define EXTENDED_PIPELINES // custom render pipelines (includes Neo) #define SCREEN_DROPLETS // neo water droplets #define NEW_RENDERER // leeds-like world rendering, needs librw #endif #define FIX_SPRITES // fix sprites aspect ratio(moon, coronas, particle etc) #ifndef EXTENDED_COLOURFILTER #undef SCREEN_DROPLETS // we need the backbuffer for this effect #endif // Water & Particle // #define PC_WATER #define WATER_CHEATS //#define USE_CUTSCENE_SHADOW_FOR_PED #define DISABLE_CUTSCENE_SHADOWS // Pad #if !defined(RW_GL3) && defined(_WIN32) #define XINPUT #endif #if defined XINPUT || (defined RW_GL3 && !defined LIBRW_SDL2 && !defined __SWITCH__) #define DETECT_JOYSTICK_MENU // Then we'll expect user to enter Controller->Detect joysticks if his joystick isn't detected at the start. #endif #define DETECT_PAD_INPUT_SWITCH // Adds automatic switch of pad related stuff between controller and kb/m #define KANGAROO_CHEAT #define RESTORE_ALLCARSHELI_CHEAT #define BETTER_ALLCARSAREDODO_CHEAT #define WALLCLIMB_CHEAT #define REGISTER_START_BUTTON #define BIND_VEHICLE_FIREWEAPON // Adds ability to rebind fire key for 'in vehicle' controls #define BUTTON_ICONS // use textures to show controller buttons // Hud, frontend and radar #define PC_MENU #define FIX_RADAR // use radar size from early version before R* broke it #define RADIO_OFF_TEXT // Won't work without FIX_BUGS #ifndef PC_MENU # define PS2_MENU //# define PS2_MENU_USEALLPAGEICONS #else # define MAP_ENHANCEMENTS // Adding waypoint and better mouse support # ifdef XINPUT # define GAMEPAD_MENU // Add gamepad menu # endif # define TRIANGLE_BACK_BUTTON //# define CIRCLE_BACK_BUTTON #define LEGACY_MENU_OPTIONS // i.e. frame sync(vsync) #define MUCH_SHORTER_OUTRO_SCREEN // #define XBOX_MESSAGE_SCREEN // Blue background, no "saved successfully press OK" screen etc. # define CUSTOM_FRONTEND_OPTIONS # ifdef CUSTOM_FRONTEND_OPTIONS # define GRAPHICS_MENU_OPTIONS // otherwise Display settings will be scrollable # define NO_ISLAND_LOADING // disable loadscreen between islands via loading all island data at once, consumes more memory and CPU # define CUTSCENE_BORDERS_SWITCH # define MULTISAMPLING // adds MSAA option # define INVERT_LOOK_FOR_PAD // enable the hidden option # endif #endif // Script #define USE_DEBUG_SCRIPT_LOADER // Loads main.scm by default. Hold R for main_freeroam.scm and D for main_d.scm #define USE_MEASUREMENTS_IN_METERS // makes game use meters instead of feet in script #define USE_PRECISE_MEASUREMENT_CONVERTION // makes game convert feet to meeters more precisely #define SUPPORT_JAPANESE_SCRIPT //#define SUPPORT_XBOX_SCRIPT //#define SUPPORT_MOBILE_SCRIPT #if (defined SUPPORT_XBOX_SCRIPT && defined SUPPORT_MOBILE_SCRIPT) static_assert(false, "SUPPORT_XBOX_SCRIPT and SUPPORT_MOBILE_SCRIPT are mutually exclusive"); #endif #ifdef PC_MENU //#define MISSION_REPLAY // mobile feature #endif //#define SIMPLIER_MISSIONS // apply simplifications from mobile #define USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #define SCRIPT_LOG_FILE_LEVEL 0 // 0 == no log, 1 == overwrite every frame, 2 == full log #if SCRIPT_LOG_FILE_LEVEL == 0 #undef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #endif #ifndef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #define USE_BASIC_SCRIPT_DEBUG_OUTPUT #endif #ifdef MASTER #undef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #undef USE_BASIC_SCRIPT_DEBUG_OUTPUT #endif // Replay //#define DONT_FIX_REPLAY_BUGS // keeps various bugs in CReplay, some of which are fairly cool! //#define USE_BETA_REPLAY_MODE // adds another replay mode, a few seconds slomo (caution: buggy!) // Vehicles #define EXPLODING_AIRTRAIN // can blow up jumbo jet with rocket launcher #define CPLANE_ROTORS // make the rotors of the NPC police heli rotate // Pickups //#define MONEY_MESSAGES #define CAMERA_PICKUP // Peds #define CANCELLABLE_CAR_ENTER // Camera #define IMPROVED_CAMERA // Better Debug cam, and maybe more in the future #define FREE_CAM // Rotating cam // Audio #define RADIO_SCROLL_TO_PREV_STATION // Won't work without FIX_BUGS #define AUDIO_CACHE // cache sound lengths to speed up the cold boot //#define PS2_AUDIO_PATHS // changes audio paths for cutscenes and radio to PS2 paths (needs vbdec on MSS builds) //#define AUDIO_OAL_USE_SNDFILE // use libsndfile to decode WAVs instead of our internal decoder #define AUDIO_OAL_USE_MPG123 // use mpg123 to support mp3 files #ifdef AUDIO_OPUS #define AUDIO_OAL_USE_OPUS // enable support of opus files //#define OPUS_AUDIO_PATHS // (not supported on VC yet) changes audio paths to opus paths (doesn't work if AUDIO_OAL_USE_OPUS isn't enabled) //#define OPUS_SFX // enable if your sfx.raw is encoded with opus (doesn't work if AUDIO_OAL_USE_OPUS isn't enabled) #ifndef AUDIO_OAL_USE_OPUS #undef OPUS_AUDIO_PATHS #undef OPUS_SFX #endif #endif // Streaming #if !defined(_WIN32) && !defined(__SWITCH__) //#define ONE_THREAD_PER_CHANNEL // Don't use if you're not on SSD/Flash - also not utilized too much right now(see commented LoadAllRequestedModels in Streaming.cpp) #define FLUSHABLE_STREAMING // Make it possible to interrupt reading when processing file isn't needed anymore. #endif #define BIG_IMG // Not complete - allows to read larger img files //#define SQUEEZE_PERFORMANCE #ifdef SQUEEZE_PERFORMANCE #undef PS2_ALPHA_TEST #undef NO_ISLAND_LOADING #endif // ------- #if defined __MWERKS__ || defined VANILLA_DEFINES #define FINAL #undef CHATTYSPLASH #undef TIMEBARS #define MASTER #undef VALIDATE_SAVE_SIZE #undef NO_MOVIES #undef DEBUGMENU #undef DRAW_GAME_VERSION_TEXT //#undef NASTY_GAME //#undef NO_CDCHECK #undef GTA_PS2_STUFF #undef USE_PS2_RAND #undef RANDOMSPLASH #undef PS2_MATFX #undef FIX_BUGS #define THIS_IS_STUPID #undef MORE_LANGUAGES #undef COMPATIBLE_SAVES #undef LOAD_INI_SETTINGS #undef FIX_HIGH_FPS_BUGS_ON_FRONTEND #undef ASPECT_RATIO_SCALE #undef PROPER_SCALING //#undef DEFAULT_NATIVE_RESOLUTION #undef PS2_ALPHA_TEST #undef IMPROVED_VIDEOMODE #undef DISABLE_LOADING_SCREEN #undef DISABLE_VSYNC_ON_TEXTURE_CONVERSION #undef FIX_SPRITES #define PC_WATER #undef WATER_CHEATS #undef USE_CUTSCENE_SHADOW_FOR_PED #undef DISABLE_CUTSCENE_SHADOWS #undef XINPUT #undef DETECT_PAD_INPUT_SWITCH #undef KANGAROO_CHEAT #undef RESTORE_ALLCARSHELI_CHEAT #undef BETTER_ALLCARSAREDODO_CHEAT #undef WALLCLIMB_CHEAT #undef REGISTER_START_BUTTON #undef BIND_VEHICLE_FIREWEAPON #undef BUTTON_ICONS #undef FIX_RADAR #undef RADIO_OFF_TEXT #undef MAP_ENHANCEMENTS #undef GAMEPAD_MENU #undef MUCH_SHORTER_OUTRO_SCREEN #undef CUSTOM_FRONTEND_OPTIONS #undef GRAPHICS_MENU_OPTIONS #undef NO_ISLAND_LOADING #undef CUTSCENE_BORDERS_SWITCH #undef MULTISAMPLING #undef INVERT_LOOK_FOR_PAD #undef USE_DEBUG_SCRIPT_LOADER #undef USE_MEASUREMENTS_IN_METERS #undef USE_PRECISE_MEASUREMENT_CONVERTION #undef SUPPORT_JAPANESE_SCRIPT #undef MISSION_REPLAY #undef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT #undef USE_BASIC_SCRIPT_DEBUG_OUTPUT #define DONT_FIX_REPLAY_BUGS #undef EXPLODING_AIRTRAIN #undef CPLANE_ROTORS #undef CAMERA_PICKUP #undef CANCELLABLE_CAR_ENTER #undef IMPROVED_CAMERA #undef FREE_CAM #undef BIG_IMG #undef RADIO_SCROLL_TO_PREV_STATION #endif ================================================ FILE: src/core/main.cpp ================================================ #include "common.h" #include #include "rpmatfx.h" #include "rphanim.h" #include "rpskin.h" #include "rtbmp.h" #include "rtpng.h" #ifdef ANISOTROPIC_FILTERING #include "rpanisot.h" #endif #include "main.h" #include "CdStream.h" #include "General.h" #include "RwHelper.h" #include "Clouds.h" #include "Draw.h" #include "Sprite2d.h" #include "Renderer.h" #include "Coronas.h" #include "WaterLevel.h" #include "Weather.h" #include "Glass.h" #include "WaterCannon.h" #include "SpecialFX.h" #include "Shadows.h" #include "Skidmarks.h" #include "Antennas.h" #include "Rubbish.h" #include "Particle.h" #include "Pickups.h" #include "WeaponEffects.h" #include "PointLights.h" #include "Fluff.h" #include "Replay.h" #include "Camera.h" #include "World.h" #include "Ped.h" #include "Font.h" #include "Pad.h" #include "Hud.h" #include "User.h" #include "Messages.h" #include "Darkel.h" #include "Garages.h" #include "MusicManager.h" #include "VisibilityPlugins.h" #include "NodeName.h" #include "DMAudio.h" #include "CutsceneMgr.h" #include "Lights.h" #include "Credits.h" #include "ZoneCull.h" #include "Timecycle.h" #include "TxdStore.h" #include "FileMgr.h" #include "Text.h" #include "RpAnimBlend.h" #include "Frontend.h" #include "AnimViewer.h" #include "Script.h" #include "PathFind.h" #include "Debug.h" #include "Console.h" #include "timebars.h" #include "GenericGameStorage.h" #include "MemoryCard.h" #include "MemoryHeap.h" #include "SceneEdit.h" #include "debugmenu.h" #include "Clock.h" #include "Occlusion.h" #include "Ropes.h" #include "postfx.h" #include "custompipes.h" #include "screendroplets.h" #include "VarConsole.h" #ifdef USE_OUR_VERSIONING #include "GitSHA1.h" #endif GlobalScene Scene; uint8 work_buff[55000]; char gString[256]; char gString2[512]; wchar gUString[256]; wchar gUString2[256]; float FramesPerSecond = 30.0f; bool gbPrintShite = false; bool gbModelViewer; #ifdef TIMEBARS bool gbShowTimebars; #endif #ifdef DRAW_GAME_VERSION_TEXT bool gbDrawVersionText; // Our addition, we think it was always enabled on !MASTER builds #endif #ifdef NO_MOVIES bool gbNoMovies; #endif volatile int32 frameCount; RwRGBA gColourTop; bool gameAlreadyInitialised; float NumberOfChunksLoaded; #define TOTALNUMCHUNKS 95.0f bool g_SlowMode = false; char version_name[64]; void GameInit(void); void SystemInit(void); void TheGame(void); #ifdef DEBUGMENU void DebugMenuPopulate(void); #endif #ifndef FINAL bool gbPrintMemoryUsage; #endif #ifdef GTA_PS2 #define WANT_TO_LOAD TheMemoryCard.m_bWantToLoad #define FOUND_GAME_TO_LOAD TheMemoryCard.b_FoundRecentSavedGameWantToLoad #else #define WANT_TO_LOAD FrontEndMenuManager.m_bWantToLoad #define FOUND_GAME_TO_LOAD b_FoundRecentSavedGameWantToLoad #endif #ifdef NEW_RENDERER bool gbNewRenderer; #endif #ifdef FIX_BUGS // need to clear stencil for mblur fx. no idea why it works in the original game // also for clearing out water rects in new renderer #define CLEARMODE (rwCAMERACLEARZ | rwCAMERACLEARSTENCIL) #else #define CLEARMODE (rwCAMERACLEARZ) #endif bool bDisplayNumOfAtomicsRendered = false; bool bDisplayPosn = false; #ifdef __MWERKS__ void debug(char *fmt, ...) { #ifndef MASTER // TODO put something here #endif } void Error(char *fmt, ...) { #ifndef MASTER // TODO put something here #endif } #endif void ValidateVersion() { int32 file = CFileMgr::OpenFile("models\\coll\\peds.col", "rb"); char buff[128]; if ( file != -1 ) { CFileMgr::Seek(file, 100, SEEK_SET); for ( int i = 0; i < 128; i++ ) { CFileMgr::Read(file, &buff[i], sizeof(char)); buff[i] -= 23; if ( buff[i] == '\0' ) break; CFileMgr::Seek(file, 99, SEEK_CUR); } if ( !strncmp(buff, "grandtheftauto3", 15) ) { strncpy(version_name, &buff[15], 64); CFileMgr::CloseFile(file); return; } } LoadingScreen("Invalid version", NULL, NULL); while(true) { ; } } bool DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha) { CRGBA TopColor(TopRed, TopGreen, TopBlue, Alpha); CRGBA BottomColor(BottomRed, BottomGreen, BottomBlue, Alpha); CDraw::CalculateAspectRatio(); CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); CVisibilityPlugins::SetRenderWareCamera(Scene.camera); RwCameraClear(Scene.camera, &TopColor.rwRGBA, CLEARMODE); if(!RsCameraBeginUpdate(Scene.camera)) return false; CSprite2d::InitPerFrame(); if(Alpha != 0) CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), BottomColor, BottomColor, TopColor, TopColor); return true; } bool DoRWStuffStartOfFrame_Horizon(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha) { CDraw::CalculateAspectRatio(); CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); CVisibilityPlugins::SetRenderWareCamera(Scene.camera); RwCameraClear(Scene.camera, &gColourTop, CLEARMODE); if(!RsCameraBeginUpdate(Scene.camera)) return false; TheCamera.m_viewMatrix.Update(); CClouds::RenderBackground(TopRed, TopGreen, TopBlue, BottomRed, BottomGreen, BottomBlue, Alpha); return true; } // This is certainly a very useful function void DoRWRenderHorizon(void) { CClouds::RenderHorizon(); } void DoFade(void) { if(CTimer::GetIsPaused()) return; #ifdef PS2_MENU if(TheMemoryCard.JustLoadedDontFadeInYet){ TheMemoryCard.JustLoadedDontFadeInYet = false; TheMemoryCard.TimeStartedCountingForFade = CTimer::GetTimeInMilliseconds(); } #else if(JustLoadedDontFadeInYet){ JustLoadedDontFadeInYet = false; TimeStartedCountingForFade = CTimer::GetTimeInMilliseconds(); } #endif #ifdef PS2_MENU if(TheMemoryCard.StillToFadeOut){ if(CTimer::GetTimeInMilliseconds() - TheMemoryCard.TimeStartedCountingForFade > TheMemoryCard.TimeToStayFadedBeforeFadeOut){ TheMemoryCard.StillToFadeOut = false; #else if(StillToFadeOut){ if(CTimer::GetTimeInMilliseconds() - TimeStartedCountingForFade > TimeToStayFadedBeforeFadeOut){ StillToFadeOut = false; #endif TheCamera.Fade(3.0f, FADE_IN); TheCamera.ProcessFade(); TheCamera.ProcessMusicFade(); }else{ TheCamera.SetFadeColour(0, 0, 0); TheCamera.Fade(0.0f, FADE_OUT); TheCamera.ProcessFade(); } } if(CDraw::FadeValue != 0 || FrontEndMenuManager.m_PrefsBrightness < 256){ CSprite2d *splash = LoadSplash(nil); CRGBA fadeColor; CRect rect; int fadeValue = CDraw::FadeValue; float brightness = Min(FrontEndMenuManager.m_PrefsBrightness, 256); if(brightness <= 50) brightness = 50; if(FrontEndMenuManager.m_bMenuActive) brightness = 256; if(TheCamera.m_FadeTargetIsSplashScreen) fadeValue = 0; float fade = fadeValue + 256 - brightness; if(fade == 0){ fadeColor.r = 0; fadeColor.g = 0; fadeColor.b = 0; fadeColor.a = 0; }else{ fadeColor.r = fadeValue * CDraw::FadeRed / fade; fadeColor.g = fadeValue * CDraw::FadeGreen / fade; fadeColor.b = fadeValue * CDraw::FadeBlue / fade; int alpha = 255 - brightness*(256 - fadeValue)/256; if(alpha < 0) alpha = 0; fadeColor.a = alpha; } TheCamera.GetScreenRect(rect); CSprite2d::DrawRect(rect, fadeColor); if(CDraw::FadeValue != 0 && TheCamera.m_FadeTargetIsSplashScreen){ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); fadeColor.r = 255; fadeColor.g = 255; fadeColor.b = 255; fadeColor.a = CDraw::FadeValue; splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), fadeColor, fadeColor, fadeColor, fadeColor); } } } bool RwGrabScreen(RwCamera *camera, RwChar *filename) { char temp[255]; RwImage *pImage = RsGrabScreen(camera); bool result = true; if (pImage == nil) return false; strcpy(temp, CFileMgr::GetRootDirName()); strcat(temp, filename); #ifndef LIBRW if (RtBMPImageWrite(pImage, &temp[0]) == nil) #else if (RtPNGImageWrite(pImage, &temp[0]) == nil) #endif result = false; RwImageDestroy(pImage); return result; } #define TILE_WIDTH 576 #define TILE_HEIGHT 432 void DoRWStuffEndOfFrame(void) { CDebug::DisplayScreenStrings(); // custom CDebug::DebugDisplayTextBuffer(); FlushObrsPrintfs(); RwCameraEndUpdate(Scene.camera); RsCameraShowRaster(Scene.camera); #ifndef MASTER char s[48]; #ifdef THIS_IS_STUPID if (CPad::GetPad(1)->GetLeftShockJustDown()) { // try using both controllers for this thing... crazy bastards if (CPad::GetPad(0)->GetRightStickY() > 0) { sprintf(s, "screen%d%d.ras", CClock::ms_nGameClockHours, CClock::ms_nGameClockMinutes); // TODO //RtTileRender(Scene.camera, TILE_WIDTH * 2, TILE_HEIGHT * 2, TILE_WIDTH, TILE_HEIGHT, &NewTileRendererCB, nil, s); } else { sprintf(s, "screen%d%d.bmp", CClock::ms_nGameClockHours, CClock::ms_nGameClockMinutes); RwGrabScreen(Scene.camera, s); } } #else if (CPad::GetPad(1)->GetLeftShockJustDown() || CPad::GetPad(0)->GetFJustDown(11)) { sprintf(s, "screen_%11lld.png", time(nil)); RwGrabScreen(Scene.camera, s); } #endif #endif // !MASTER } static RwBool PluginAttach(void) { if( !RpWorldPluginAttach() ) { printf("Couldn't attach world plugin\n"); return FALSE; } if( !RpSkinPluginAttach() ) { printf("Couldn't attach RpSkin plugin\n"); return FALSE; } #ifndef LIBRW if (!RtAnimInitialize()) { return FALSE; } #endif if( !RpHAnimPluginAttach() ) { printf("Couldn't attach RpHAnim plugin\n"); return FALSE; } if( !NodeNamePluginAttach() ) { printf("Couldn't attach node name plugin\n"); return FALSE; } if( !CVisibilityPlugins::PluginAttach() ) { printf("Couldn't attach visibility plugins\n"); return FALSE; } if( !RpAnimBlendPluginAttach() ) { printf("Couldn't attach RpAnimBlend plugin\n"); return FALSE; } if( !RpMatFXPluginAttach() ) { printf("Couldn't attach RpMatFX plugin\n"); return FALSE; } #ifdef ANISOTROPIC_FILTERING RpAnisotPluginAttach(); #endif #ifdef EXTENDED_PIPELINES CustomPipes::CustomPipeRegister(); #endif return TRUE; } #ifdef GTA_PS2 #define NUM_PREALLOC_ATOMICS 1800 #define NUM_PREALLOC_CLUMPS 80 #define NUM_PREALLOC_FRAMES 2600 #define NUM_PREALLOC_GEOMETRIES 850 #define NUM_PREALLOC_TEXDICTS 121 #define NUM_PREALLOC_TEXTURES 1700 #define NUM_PREALLOC_MATERIALS 2600 bool preAlloc; void PreAllocateRwObjects(void) { int i; PUSH_MEMID(MEMID_PRE_ALLOC); void **tmp = new void*[0x8000]; preAlloc = true; for(i = 0; i < NUM_PREALLOC_ATOMICS; i++) tmp[i] = RpAtomicCreate(); for(i = 0; i < NUM_PREALLOC_ATOMICS; i++) RpAtomicDestroy((RpAtomic*)tmp[i]); for(i = 0; i < NUM_PREALLOC_CLUMPS; i++) tmp[i] = RpClumpCreate(); for(i = 0; i < NUM_PREALLOC_CLUMPS; i++) RpClumpDestroy((RpClump*)tmp[i]); for(i = 0; i < NUM_PREALLOC_FRAMES; i++) tmp[i] = RwFrameCreate(); for(i = 0; i < NUM_PREALLOC_FRAMES; i++) RwFrameDestroy((RwFrame*)tmp[i]); for(i = 0; i < NUM_PREALLOC_GEOMETRIES; i++) tmp[i] = RpGeometryCreate(0, 0, 0); for(i = 0; i < NUM_PREALLOC_GEOMETRIES; i++) RpGeometryDestroy((RpGeometry*)tmp[i]); for(i = 0; i < NUM_PREALLOC_TEXDICTS; i++) tmp[i] = RwTexDictionaryCreate(); for(i = 0; i < NUM_PREALLOC_TEXDICTS; i++) RwTexDictionaryDestroy((RwTexDictionary*)tmp[i]); for(i = 0; i < NUM_PREALLOC_TEXTURES; i++) tmp[i] = RwTextureCreate(RwRasterCreate(0, 0, 0, 0)); for(i = 0; i < NUM_PREALLOC_TEXDICTS; i++) RwTextureDestroy((RwTexture*)tmp[i]); for(i = 0; i < NUM_PREALLOC_MATERIALS; i++) tmp[i] = RpMaterialCreate(); for(i = 0; i < NUM_PREALLOC_MATERIALS; i++) RpMaterialDestroy((RpMaterial*)tmp[i]); delete[] tmp; preAlloc = false; POP_MEMID(); } #endif static RwBool Initialise3D(void *param) { PUSH_MEMID(MEMID_RENDER); #ifndef MASTER VarConsole.Add("Display number of atomics rendered", &bDisplayNumOfAtomicsRendered, true); VarConsole.Add("Display posn and framerate", &bDisplayPosn, true); #endif if (RsRwInitialize(param)) { POP_MEMID(); #ifdef DEBUGMENU DebugMenuInit(); DebugMenuPopulate(); #endif // !DEBUGMENU return CGame::InitialiseRenderWare(); } POP_MEMID(); return (FALSE); } static void Terminate3D(void) { CGame::ShutdownRenderWare(); #ifdef DEBUGMENU DebugMenuShutdown(); #endif // !DEBUGMENU RsRwTerminate(); return; } CSprite2d splash; int splashTxdId = -1; CSprite2d* LoadSplash(const char *name) { RwTexDictionary *txd; char filename[140]; RwTexture *tex = nil; if(name == nil) return &splash; if(splashTxdId == -1) splashTxdId = CTxdStore::AddTxdSlot("splash"); txd = CTxdStore::GetSlot(splashTxdId)->texDict; if(txd) tex = RwTexDictionaryFindNamedTexture(txd, name); // if texture is found, splash was already set up below if(tex == nil){ CFileMgr::SetDir("TXD\\"); sprintf(filename, "%s.txd", name); if(splash.m_pTexture) splash.Delete(); if(txd) CTxdStore::RemoveTxd(splashTxdId); CTxdStore::LoadTxd(splashTxdId, filename); CTxdStore::AddRef(splashTxdId); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(splashTxdId); splash.SetTexture(name); CTxdStore::PopCurrentTxd(); CFileMgr::SetDir(""); } return &splash; } void DestroySplashScreen(void) { splash.Delete(); if(splashTxdId != -1) CTxdStore::RemoveTxdSlot(splashTxdId); splashTxdId = -1; } Const char* GetRandomSplashScreen(void) { int index; static int index2 = 0; static char splashName[128]; static int splashIndex[12] = { 1, 2, 3, 4, 5, 11, 6, 8, 9, 10, 7, 12 }; index = splashIndex[2*index2 + CGeneral::GetRandomNumberInRange(0, 2)]; index2++; if(index2 == 6) index2 = 0; sprintf(splashName, "loadsc%d", index); return splashName; } Const char* GetLevelSplashScreen(int level) { static Const char *splashScreens[4] = { nil, "splash1", "splash2", "splash3", }; return splashScreens[level]; } void ResetLoadingScreenBar() { NumberOfChunksLoaded = 0.0f; } void LoadingScreen(const char *str1, const char *str2, const char *splashscreen) { CSprite2d *splash; #ifdef DISABLE_LOADING_SCREEN if (str1 && str2) return; #endif #ifndef RANDOMSPLASH splashscreen = "LOADSC0"; #endif splash = LoadSplash(splashscreen); #ifndef GTA_PS2 if(RsGlobal.quit) return; #endif if(DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)){ CSprite2d::SetRecipNearClip(); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); DefinedState(); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, 255)); if(str1){ NumberOfChunksLoaded += 1; #ifndef RANDOMSPLASH float hpos = SCREEN_SCALE_X(40); float length = SCREEN_WIDTH - SCREEN_SCALE_X(80); float top = SCREEN_HEIGHT - SCREEN_SCALE_Y(14); float bottom = top + SCREEN_SCALE_Y(5); #else float hpos = SCREEN_STRETCH_X(40); float length = SCREEN_STRETCH_X(440); // this is rather weird float top = SCREEN_STRETCH_Y(407.4f - 7.0f/3.0f); float bottom = SCREEN_STRETCH_Y(407.4f + 7.0f/3.0f); #endif CSprite2d::DrawRect(CRect(hpos-1.0f, top-1.0f, hpos+length+1.0f, bottom+1.0f), CRGBA(40, 53, 68, 255)); CSprite2d::DrawRect(CRect(hpos, top, hpos+length, bottom), CRGBA(155, 50, 125, 255)); length *= NumberOfChunksLoaded/TOTALNUMCHUNKS; CSprite2d::DrawRect(CRect(hpos, top, hpos+length, bottom), CRGBA(255, 150, 225, 255)); // this is done by the game but is unused CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(2), SCREEN_SCALE_Y(2)); CFont::SetPropOn(); CFont::SetRightJustifyOn(); CFont::SetDropShadowPosition(1); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetFontStyle(FONT_HEADING); #ifdef CHATTYSPLASH // my attempt static wchar tmpstr[80]; float yscale = SCREEN_SCALE_Y(0.9f); top -= 45*yscale; CFont::SetScale(SCREEN_SCALE_X(0.75f), yscale); CFont::SetPropOn(); CFont::SetRightJustifyOff(); CFont::SetFontStyle(FONT_STANDARD); CFont::SetColor(CRGBA(255, 255, 255, 255)); AsciiToUnicode(str1, tmpstr); CFont::PrintString(hpos, top, tmpstr); top += 22*yscale; if (str2) { AsciiToUnicode(str2, tmpstr); CFont::PrintString(hpos, top, tmpstr); } #endif } CFont::DrawFonts(); DoRWStuffEndOfFrame(); } } void LoadingIslandScreen(const char *levelName) { CSprite2d *splash; splash = LoadSplash(nil); if(!DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)) return; CSprite2d::SetRecipNearClip(); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); DefinedState(); CRGBA col = CRGBA(255, 255, 255, 255); CRGBA col2 = CRGBA(0, 0, 0, 255); CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), col2); splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), col, col, col, col); CFont::DrawFonts(); DoRWStuffEndOfFrame(); } void ProcessSlowMode(void) { int16 lX = CPad::GetPad(0)->NewState.LeftStickX; int16 lY = CPad::GetPad(0)->NewState.LeftStickY; int16 rX = CPad::GetPad(0)->NewState.RightStickX; int16 rY = CPad::GetPad(0)->NewState.RightStickY; int16 L1 = CPad::GetPad(0)->NewState.LeftShoulder1; int16 L2 = CPad::GetPad(0)->NewState.LeftShoulder2; int16 R1 = CPad::GetPad(0)->NewState.RightShoulder1; int16 R2 = CPad::GetPad(0)->NewState.RightShoulder2; int16 up = CPad::GetPad(0)->NewState.DPadUp; int16 down = CPad::GetPad(0)->NewState.DPadDown; int16 left = CPad::GetPad(0)->NewState.DPadLeft; int16 right = CPad::GetPad(0)->NewState.DPadRight; int16 start = CPad::GetPad(0)->NewState.Start; int16 select = CPad::GetPad(0)->NewState.Select; int16 square = CPad::GetPad(0)->NewState.Square; int16 triangle = CPad::GetPad(0)->NewState.Triangle; int16 cross = CPad::GetPad(0)->NewState.Cross; int16 circle = CPad::GetPad(0)->NewState.Circle; int16 L3 = CPad::GetPad(0)->NewState.LeftShock; int16 R3 = CPad::GetPad(0)->NewState.RightShock; int16 networktalk = CPad::GetPad(0)->NewState.NetworkTalk; int16 stop = true; do { if ( CPad::GetPad(1)->GetSelectJustDown() || CPad::GetPad(1)->GetStart() ) break; if ( stop ) { CTimer::Stop(); stop = false; } CPad::UpdatePads(); RwCameraBeginUpdate(Scene.camera); RwCameraEndUpdate(Scene.camera); } while (!CPad::GetPad(1)->GetSelectJustDown() && !CPad::GetPad(1)->GetStart()); CPad::GetPad(0)->OldState.LeftStickX = lX; CPad::GetPad(0)->OldState.LeftStickY = lY; CPad::GetPad(0)->OldState.RightStickX = rX; CPad::GetPad(0)->OldState.RightStickY = rY; CPad::GetPad(0)->OldState.LeftShoulder1 = L1; CPad::GetPad(0)->OldState.LeftShoulder2 = L2; CPad::GetPad(0)->OldState.RightShoulder1 = R1; CPad::GetPad(0)->OldState.RightShoulder2 = R2; CPad::GetPad(0)->OldState.DPadUp = up; CPad::GetPad(0)->OldState.DPadDown = down; CPad::GetPad(0)->OldState.DPadLeft = left; CPad::GetPad(0)->OldState.DPadRight = right; CPad::GetPad(0)->OldState.Start = start; CPad::GetPad(0)->OldState.Select = select; CPad::GetPad(0)->OldState.Square = square; CPad::GetPad(0)->OldState.Triangle = triangle; CPad::GetPad(0)->OldState.Cross = cross; CPad::GetPad(0)->OldState.Circle = circle; CPad::GetPad(0)->OldState.LeftShock = L3; CPad::GetPad(0)->OldState.RightShock = R3; CPad::GetPad(0)->OldState.NetworkTalk = networktalk; CPad::GetPad(0)->NewState.LeftStickX = lX; CPad::GetPad(0)->NewState.LeftStickY = lY; CPad::GetPad(0)->NewState.RightStickX = rX; CPad::GetPad(0)->NewState.RightStickY = rY; CPad::GetPad(0)->NewState.LeftShoulder1 = L1; CPad::GetPad(0)->NewState.LeftShoulder2 = L2; CPad::GetPad(0)->NewState.RightShoulder1 = R1; CPad::GetPad(0)->NewState.RightShoulder2 = R2; CPad::GetPad(0)->NewState.DPadUp = up; CPad::GetPad(0)->NewState.DPadDown = down; CPad::GetPad(0)->NewState.DPadLeft = left; CPad::GetPad(0)->NewState.DPadRight = right; CPad::GetPad(0)->NewState.Start = start; CPad::GetPad(0)->NewState.Select = select; CPad::GetPad(0)->NewState.Square = square; CPad::GetPad(0)->NewState.Triangle = triangle; CPad::GetPad(0)->NewState.Cross = cross; CPad::GetPad(0)->NewState.Circle = circle; CPad::GetPad(0)->NewState.LeftShock = L3; CPad::GetPad(0)->NewState.RightShock = R3; CPad::GetPad(0)->NewState.NetworkTalk = networktalk; } float FramesPerSecondCounter; int32 FrameSamples; #ifndef MASTER struct tZonePrint { char name[11]; char area[5]; CRect rect; }; tZonePrint ZonePrint[] = { { "DOWNTOWN", "GM", CRect(-1500.0f, 1500.0f, -300.0f, 980.0f)}, { "DOWNTOWS", "KB", CRect(-1200.0f, 980.0f, -300.0f, 435.0f)}, { "GOLF", "NT", CRect(-300.0f, 660.0f, 320.0f, -255.0f)}, { "LITTLEHA", "AG", CRect(-1250.0f, -310.0f, -746.0f, -926.0f)}, { "HAITI", "CJ", CRect(-1355.0f, 30.0f, -637.0f, -304.0f)}, { "HAITIN", "SM", CRect(-1355.0f, 435.0f, -637.0f, 30.0f)}, { "DOCKS", "AW", CRect(-1122.0f, -926.0f, -609.0f, -1575.0f)}, { "AIRPORT", "NT", CRect(-2000.0f, 200.0f, -871.0f, -2000.0f)}, { "STARISL", "CJ", CRect(-724.0f, -320.0f, -40.0f, -380.0f)}, { "CENT.ISLA", "NT", CRect(-163.0f, 1260.0f, 120.0f, 830.0f)}, { "MALL", "AW", CRect( 300.0f, 1266.0f, 483.0f, 995.0f)}, { "MANSION", "KB", CRect(-724.0f, -500.0f, -40.0f, -670.0f)}, { "NBEACH", "AS", CRect( 120.0f, 1340.0f, 900.0f, 600.0f)}, { "NBEACHBT", "AS", CRect( 200.0f, 680.0f, 660.0f, -50.0f)}, { "NBEACHW", "AS", CRect(-93.0f, 80.0f, 410.0f, -680.0f)}, { "OCEANDRV", "AC", CRect( 200.0f, -964.0f, 955.0f, -1797.0f)}, { "OCEANDN", "WS", CRect( 400.0f, 50.0f, 955.0f, -964.0f)}, { "WASHINGTN", "AC", CRect(-320.0f, -487.0f, 500.0f, -1200.0f)}, { "WASHINBTM", "AC", CRect(-255.0f, -1200.0f, 500.0f, -1690.0f)} }; void PrintMemoryUsage(void) { // little hack if(CPools::GetPtrNodePool() == nil) return; // Style taken from LCS, modified for III // CFont::SetFontStyle(FONT_PAGER); CFont::SetFontStyle(FONT_BANK); CFont::SetBackgroundOff(); CFont::SetWrapx(640.0f); // CFont::SetScale(0.5f, 0.75f); CFont::SetScale(0.4f, 0.75f); CFont::SetCentreOff(); CFont::SetCentreSize(640.0f); CFont::SetJustifyOff(); CFont::SetPropOn(); CFont::SetColor(CRGBA(200, 200, 200, 200)); CFont::SetBackGroundOnlyTextOff(); CFont::SetDropShadowPosition(0); float y; #ifdef USE_CUSTOM_ALLOCATOR y = 24.0f; sprintf(gString, "Total: %d blocks, %d bytes", gMainHeap.m_totalBlocksUsed, gMainHeap.m_totalMemUsed); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Game: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_GAME), gMainHeap.GetMemoryUsed(MEMID_GAME)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "World: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_WORLD), gMainHeap.GetMemoryUsed(MEMID_WORLD)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Render: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_RENDER), gMainHeap.GetMemoryUsed(MEMID_RENDER)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "PreAlloc: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_PRE_ALLOC), gMainHeap.GetMemoryUsed(MEMID_PRE_ALLOC)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Default Models: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_DEF_MODELS), gMainHeap.GetMemoryUsed(MEMID_DEF_MODELS)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Textures: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_TEXTURES), gMainHeap.GetMemoryUsed(MEMID_TEXTURES)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Streaming: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM), gMainHeap.GetMemoryUsed(MEMID_STREAM)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Streamed Models: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_MODELS), gMainHeap.GetMemoryUsed(MEMID_STREAM_MODELS)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Streamed LODs: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_LODS), gMainHeap.GetMemoryUsed(MEMID_STREAM_LODS)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Streamed Textures: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_TEXUTRES), gMainHeap.GetMemoryUsed(MEMID_STREAM_TEXUTRES)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Streamed Collision: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_COLLISION), gMainHeap.GetMemoryUsed(MEMID_STREAM_COLLISION)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Streamed Animation: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_STREAM_ANIMATION), gMainHeap.GetMemoryUsed(MEMID_STREAM_ANIMATION)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Ped Attr: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_PED_ATTR), gMainHeap.GetMemoryUsed(MEMID_PED_ATTR)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Animation: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_ANIMATION), gMainHeap.GetMemoryUsed(MEMID_ANIMATION)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Pools: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_POOLS), gMainHeap.GetMemoryUsed(MEMID_POOLS)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Collision: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_COLLISION), gMainHeap.GetMemoryUsed(MEMID_COLLISION)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Game Process: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_GAME_PROCESS), gMainHeap.GetMemoryUsed(MEMID_GAME_PROCESS)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Script: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_SCRIPT), gMainHeap.GetMemoryUsed(MEMID_SCRIPT)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; sprintf(gString, "Cars: %d blocks, %d bytes", gMainHeap.GetBlocksUsed(MEMID_CARS), gMainHeap.GetMemoryUsed(MEMID_CARS)); AsciiToUnicode(gString, gUString); CFont::PrintString(24.0f, y, gUString); y += 12.0f; #endif y = 132.0f; AsciiToUnicode("Pools usage:", gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; sprintf(gString, "PtrNode: %d/%d", CPools::GetPtrNodePool()->GetNoOfUsedSpaces(), CPools::GetPtrNodePool()->GetSize()); AsciiToUnicode(gString, gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; sprintf(gString, "EntryInfoNode: %d/%d", CPools::GetEntryInfoNodePool()->GetNoOfUsedSpaces(), CPools::GetEntryInfoNodePool()->GetSize()); AsciiToUnicode(gString, gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; sprintf(gString, "Ped: %d/%d", CPools::GetPedPool()->GetNoOfUsedSpaces(), CPools::GetPedPool()->GetSize()); AsciiToUnicode(gString, gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; sprintf(gString, "Vehicle: %d/%d", CPools::GetVehiclePool()->GetNoOfUsedSpaces(), CPools::GetVehiclePool()->GetSize()); AsciiToUnicode(gString, gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; sprintf(gString, "Building: %d/%d", CPools::GetBuildingPool()->GetNoOfUsedSpaces(), CPools::GetBuildingPool()->GetSize()); AsciiToUnicode(gString, gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; sprintf(gString, "Treadable: %d/%d", CPools::GetTreadablePool()->GetNoOfUsedSpaces(), CPools::GetTreadablePool()->GetSize()); AsciiToUnicode(gString, gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; sprintf(gString, "Object: %d/%d", CPools::GetObjectPool()->GetNoOfUsedSpaces(), CPools::GetObjectPool()->GetSize()); AsciiToUnicode(gString, gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; sprintf(gString, "Dummy: %d/%d", CPools::GetDummyPool()->GetNoOfUsedSpaces(), CPools::GetDummyPool()->GetSize()); AsciiToUnicode(gString, gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; sprintf(gString, "AudioScriptObjects: %d/%d", CPools::GetAudioScriptObjectPool()->GetNoOfUsedSpaces(), CPools::GetAudioScriptObjectPool()->GetSize()); AsciiToUnicode(gString, gUString); CFont::PrintString(400.0f, y, gUString); y += 12.0f; } void DisplayGameDebugText() { static bool bDisplayCheatStr = false; // custom #ifndef FINAL { SETTWEAKPATH("Debug"); TWEAKBOOL(bDisplayPosn); TWEAKBOOL(bDisplayCheatStr); } if(gbPrintMemoryUsage) PrintMemoryUsage(); #endif char str[200]; wchar ustr[200]; #ifdef DRAW_GAME_VERSION_TEXT wchar ver[200]; if(gbDrawVersionText) // This realtime switch is our thing { #ifdef USE_OUR_VERSIONING char verA[200]; sprintf(verA, #if defined _WIN32 "Win " #elif defined __linux__ "Linux " #elif defined __APPLE__ "Mac OS X " #elif defined __FreeBSD__ "FreeBSD " #else "Posix-compliant " #endif #if defined __LP64__ || defined _WIN64 "64-bit " #else "32-bit " #endif #if defined RW_D3D9 "D3D9 " #elif defined RWLIBS "D3D8 " #elif defined RW_GL3 "OpenGL " #endif #if defined AUDIO_OAL "OAL " #elif defined AUDIO_MSS "MSS " #endif #if defined _DEBUG || defined DEBUG "DEBUG " #endif "%.8s", g_GIT_SHA1); AsciiToUnicode(verA, ver); CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.7f)); #else AsciiToUnicode(version_name, ver); CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.5f)); #endif CFont::SetPropOn(); CFont::SetBackgroundOff(); CFont::SetFontStyle(FONT_STANDARD); CFont::SetCentreOff(); CFont::SetRightJustifyOff(); CFont::SetWrapx(SCREEN_WIDTH); CFont::SetJustifyOff(); CFont::SetBackGroundOnlyTextOff(); CFont::SetColor(CRGBA(255, 108, 0, 255)); CFont::PrintString(SCREEN_SCALE_X(10.0f), SCREEN_SCALE_Y(10.0f), ver); } #endif // #ifdef DRAW_GAME_VERSION_TEXT FrameSamples++; #ifdef FIX_HIGH_FPS_BUGS_ON_FRONTEND FramesPerSecondCounter += frameTime / 1000.f; // convert to seconds FramesPerSecond = FrameSamples / FramesPerSecondCounter; #else FramesPerSecondCounter += 1000.0f / (CTimer::GetTimeStepNonClippedInSeconds() * 1000.0f); FramesPerSecond = FramesPerSecondCounter / FrameSamples; #endif if ( FrameSamples > 30 ) { FramesPerSecondCounter = 0.0f; FrameSamples = 0; } if ( bDisplayPosn ) { CVector pos = FindPlayerCoors(); int32 ZoneId = ARRAY_SIZE(ZonePrint)-1; // no zone for ( int32 i = 0; i < ARRAY_SIZE(ZonePrint)-1; i++ ) { if ( pos.x > ZonePrint[i].rect.left && pos.x < ZonePrint[i].rect.right && pos.y > ZonePrint[i].rect.bottom && pos.y < ZonePrint[i].rect.top ) { ZoneId = i; } } //NOTE: fps should be 30, but its 29 due to different fp2int conversion sprintf(str, "X:%4.0f Y:%4.0f Z:%4.0f F-%d %s-%s", pos.x, pos.y, pos.z, (int32)FramesPerSecond, ZonePrint[ZoneId].name, ZonePrint[ZoneId].area); AsciiToUnicode(str, ustr); CFont::SetPropOn(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(0.6f), SCREEN_SCALE_Y(0.8f)); CFont::SetCentreOff(); CFont::SetRightJustifyOff(); CFont::SetJustifyOff(); CFont::SetBackGroundOnlyTextOff(); CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); CFont::SetFontStyle(FONT_STANDARD); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetDropShadowPosition(2); CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString(41.0f, 41.0f, ustr); CFont::SetColor(CRGBA(205, 205, 0, 255)); CFont::PrintString(40.0f, 40.0f, ustr); } // custom if (bDisplayCheatStr) { sprintf(str, "%s", CPad::KeyBoardCheatString); AsciiToUnicode(str, ustr); CFont::SetPropOn(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(0.6f), SCREEN_SCALE_Y(0.8f)); CFont::SetCentreOn(); CFont::SetBackGroundOnlyTextOff(); CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); CFont::SetFontStyle(FONT_STANDARD); CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.5f)+2.f, SCREEN_SCALE_FROM_BOTTOM(20.0f)+2.f, ustr); CFont::SetColor(CRGBA(255, 150, 225, 255)); CFont::PrintString(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.5f), SCREEN_SCALE_FROM_BOTTOM(20.0f), ustr); } } #endif #ifdef NEW_RENDERER bool gbRenderRoads = true; bool gbRenderEverythingBarRoads = true; bool gbRenderFadingInUnderwaterEntities = true; bool gbRenderFadingInEntities = true; bool gbRenderWater = true; bool gbRenderBoats = true; bool gbRenderVehicles = true; bool gbRenderWorld0 = true; bool gbRenderWorld1 = true; bool gbRenderWorld2 = true; void MattRenderScene(void) { // this calls CMattRenderer::Render /// CWorld::AdvanceCurrentScanCode(); // CMattRenderer::ResetRenderStates /// CRenderer::ClearForFrame(); // before ConstructRenderList // CClock::CalcEnvMapTimeMultiplicator RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); CWaterLevel::RenderWater(); // actually CMattRenderer::RenderWater // CClock::ms_EnvMapTimeMultiplicator = 1.0f; // cWorldStream::ClearDynamics /// CRenderer::ConstructRenderList(); // before PreRender if(gbRenderWorld0) CRenderer::RenderWorld(0); // roads // CMattRenderer::ResetRenderStates /// CRenderer::PreRender(); // has to be called before BeginUpdate because of cutscene shadows CCoronas::RenderReflections(); if(gbRenderWorld1) CRenderer::RenderWorld(1); // opaque if(gbRenderRoads) CRenderer::RenderRoads(); CRenderer::RenderPeds(); // not sure where to put these since LCS has no underwater entities if(gbRenderBoats) CRenderer::RenderBoats(); if(gbRenderFadingInUnderwaterEntities) CRenderer::RenderFadingInUnderwaterEntities(); RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); if(gbRenderWater) CRenderer::RenderTransparentWater(); if(gbRenderEverythingBarRoads) CRenderer::RenderEverythingBarRoads(); // seam fixer // moved this: // CRenderer::RenderFadingInEntities(); } void RenderScene_new(void) { PUSH_RENDERGROUP("RenderScene_new"); CClouds::Render(); DoRWRenderHorizon(); MattRenderScene(); DefinedState(); // CMattRenderer::ResetRenderStates // moved CRenderer::RenderBoats to before transparent water POP_RENDERGROUP(); } // TODO bool FredIsInFirstPersonCam(void) { return false; } void RenderEffects_new(void) { PUSH_RENDERGROUP("RenderEffects_new"); CShadows::RenderStaticShadows(); // CRenderer::GenerateEnvironmentMap CShadows::RenderStoredShadows(); CSkidmarks::Render(); CRubbish::Render(); // these aren't really effects DefinedState(); if(FredIsInFirstPersonCam()){ DefinedState(); C3dMarkers::Render(); // normally rendered in CSpecialFX::Render() if(gbRenderWorld2) CRenderer::RenderWorld(2); // transparent if(gbRenderVehicles) CRenderer::RenderVehicles(); }else{ // flipped these two, seems to give the best result if(gbRenderWorld2) CRenderer::RenderWorld(2); // transparent if(gbRenderVehicles) CRenderer::RenderVehicles(); } // better render these after transparent world if(gbRenderFadingInEntities) CRenderer::RenderFadingInEntities(); // actual effects here CGlass::Render(); // CMattRenderer::ResetRenderStates DefinedState(); CCoronas::RenderSunReflection(); CWeather::RenderRainStreaks(); // CWeather::AddSnow CWaterCannons::Render(); CAntennas::Render(); CSpecialFX::Render(); CRopes::Render(); CCoronas::Render(); CParticle::Render(); CPacManPickups::Render(); CWeaponEffects::Render(); CPointLights::RenderFogEffect(); CMovingThings::Render(); CRenderer::RenderFirstPersonVehicle(); POP_RENDERGROUP(); } #endif void RenderScene(void) { #ifdef NEW_RENDERER if(gbNewRenderer){ RenderScene_new(); return; } #endif PUSH_RENDERGROUP("RenderScene"); CClouds::Render(); DoRWRenderHorizon(); CRenderer::RenderRoads(); CCoronas::RenderReflections(); CRenderer::RenderEverythingBarRoads(); RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); CWaterLevel::RenderWater(); CRenderer::RenderBoats(); CRenderer::RenderFadingInUnderwaterEntities(); RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); CWaterLevel::RenderTransparentWater(); CRenderer::RenderFadingInEntities(); RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); CWeather::RenderRainStreaks(); CCoronas::RenderSunReflection(); POP_RENDERGROUP(); } void RenderDebugShit(void) { PUSH_RENDERGROUP("RenderDebugShit"); CTheScripts::RenderTheScriptDebugLines(); #ifndef FINAL if(gbShowCollisionLines) CRenderer::RenderCollisionLines(); ThePaths.DisplayPathData(); CDebug::DrawLines(); DefinedState(); #endif POP_RENDERGROUP(); } void RenderEffects(void) { #ifdef NEW_RENDERER if(gbNewRenderer){ RenderEffects_new(); return; } #endif PUSH_RENDERGROUP("RenderEffects"); CGlass::Render(); CWaterCannons::Render(); CSpecialFX::Render(); CRopes::Render(); CShadows::RenderStaticShadows(); CShadows::RenderStoredShadows(); CSkidmarks::Render(); CAntennas::Render(); CRubbish::Render(); CCoronas::Render(); CParticle::Render(); CPacManPickups::Render(); CWeaponEffects::Render(); CPointLights::RenderFogEffect(); CMovingThings::Render(); CRenderer::RenderFirstPersonVehicle(); POP_RENDERGROUP(); } void Render2dStuff(void) { PUSH_RENDERGROUP("Render2dStuff"); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); CReplay::Display(); CPickups::RenderPickUpText(); if(TheCamera.m_WideScreenOn #ifdef CUTSCENE_BORDERS_SWITCH && CMenuManager::m_PrefsCutsceneBorders #endif ) TheCamera.DrawBordersForWideScreen(); CPed *player = FindPlayerPed(); int weaponType = 0; if(player) weaponType = player->GetWeapon()->m_eWeaponType; bool firstPersonWeapon = false; int cammode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if(cammode == CCam::MODE_SNIPER || cammode == CCam::MODE_SNIPER_RUNABOUT || cammode == CCam::MODE_ROCKETLAUNCHER || cammode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || cammode == CCam::MODE_CAMERA) firstPersonWeapon = true; // Draw black border for sniper and rocket launcher if((weaponType == WEAPONTYPE_SNIPERRIFLE || weaponType == WEAPONTYPE_ROCKETLAUNCHER || weaponType == WEAPONTYPE_LASERSCOPE) && firstPersonWeapon){ CRGBA black(0, 0, 0, 255); // top and bottom strips if (weaponType == WEAPONTYPE_ROCKETLAUNCHER) { CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(180)), black); CSprite2d::DrawRect(CRect(0.0f, SCREEN_HEIGHT / 2 + SCREEN_SCALE_Y(170), SCREEN_WIDTH, SCREEN_HEIGHT), black); } else { CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT / 2 - SCREEN_SCALE_Y(210)), black); CSprite2d::DrawRect(CRect(0.0f, SCREEN_HEIGHT / 2 + SCREEN_SCALE_Y(210), SCREEN_WIDTH, SCREEN_HEIGHT), black); } CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREEN_WIDTH / 2 - SCREEN_SCALE_X(210), SCREEN_HEIGHT), black); CSprite2d::DrawRect(CRect(SCREEN_WIDTH / 2 + SCREEN_SCALE_X(210), 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), black); } MusicManager.DisplayRadioStationName(); TheConsole.Display(); #ifdef GTA_SCENE_EDIT if(CSceneEdit::m_bEditOn) CSceneEdit::Draw(); else #endif CHud::Draw(); CSpecialFX::Render2DFXs(); CUserDisplay::OnscnTimer.ProcessForDisplay(); CMessages::Display(); CDarkel::DrawMessages(); CGarages::PrintMessages(); CPad::PrintErrorMessage(); CFont::DrawFonts(); #ifndef MASTER COcclusion::Render(); #endif #ifdef DEBUGMENU DebugMenuRender(); #endif POP_RENDERGROUP(); } void RenderMenus(void) { if (FrontEndMenuManager.m_bMenuActive) { PUSH_RENDERGROUP("RenderMenus"); FrontEndMenuManager.DrawFrontEnd(); POP_RENDERGROUP(); } #ifndef MASTER else VarConsole.Check(); #endif } void Render2dStuffAfterFade(void) { PUSH_RENDERGROUP("Render2dStuffAfterFade"); #ifndef MASTER DisplayGameDebugText(); #endif #ifdef MOBILE_IMPROVEMENTS if (CDraw::FadeValue != 0) #endif CHud::DrawAfterFade(); CFont::DrawFonts(); CCredits::Render(); POP_RENDERGROUP(); } void Idle(void *arg) { CTimer::Update(); tbInit(); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); PUSH_MEMID(MEMID_GAME_PROCESS); CPointLights::InitPerFrame(); tbStartTimer(0, "CGame::Process"); CGame::Process(); tbEndTimer("CGame::Process"); POP_MEMID(); tbStartTimer(0, "DMAudio.Service"); DMAudio.Service(); tbEndTimer("DMAudio.Service"); if(CGame::bDemoMode && CTimer::GetTimeInMilliseconds() > (3*60 + 30)*1000 && !CCutsceneMgr::IsCutsceneProcessing()){ WANT_TO_LOAD = false; FrontEndMenuManager.m_bWantToRestart = true; return; } if(FrontEndMenuManager.m_bWantToRestart || FOUND_GAME_TO_LOAD) { return; } SetLightsWithTimeOfDayColour(Scene.world); if(arg == nil) return; PUSH_MEMID(MEMID_RENDER); if(!FrontEndMenuManager.m_bMenuActive && TheCamera.GetScreenFadeStatus() != FADE_2) { // This is from SA, but it's nice for windowed mode #if defined(GTA_PC) && !defined(RW_GL3) RwV2d pos; pos.x = SCREEN_WIDTH / 2.0f; pos.y = SCREEN_HEIGHT / 2.0f; RsMouseSetPos(&pos); #endif tbStartTimer(0, "CnstrRenderList"); #ifdef PC_WATER CWaterLevel::PreCalcWaterGeometry(); #endif #ifdef NEW_RENDERER if(gbNewRenderer){ CWorld::AdvanceCurrentScanCode(); // don't think this is even necessary CRenderer::ClearForFrame(); } #endif CRenderer::ConstructRenderList(); tbEndTimer("CnstrRenderList"); tbStartTimer(0, "PreRender"); CRenderer::PreRender(); tbEndTimer("PreRender"); #ifdef FIX_BUGS RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); // TODO: temp? this fixes OpenGL render but there should be a better place for this // This has to be done BEFORE RwCameraBeginUpdate RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip()); RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart()); #endif if(CWeather::LightningFlash && !CCullZones::CamNoRain()){ if(!DoRWStuffStartOfFrame_Horizon(255, 255, 255, 255, 255, 255, 255)) goto popret; }else{ if(!DoRWStuffStartOfFrame_Horizon(CTimeCycle::GetSkyTopRed(), CTimeCycle::GetSkyTopGreen(), CTimeCycle::GetSkyTopBlue(), CTimeCycle::GetSkyBottomRed(), CTimeCycle::GetSkyBottomGreen(), CTimeCycle::GetSkyBottomBlue(), 255)) goto popret; } DefinedState(); #ifndef FIX_BUGS RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip()); RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart()); #endif tbStartTimer(0, "RenderScene"); RenderScene(); tbEndTimer("RenderScene"); #ifdef EXTENDED_PIPELINES CustomPipes::EnvMapRender(); #endif RenderDebugShit(); RenderEffects(); if((TheCamera.m_BlurType == MOTION_BLUR_NONE || TheCamera.m_BlurType == MOTION_BLUR_LIGHT_SCENE) && TheCamera.m_ScreenReductionPercentage > 0.0f) TheCamera.SetMotionBlurAlpha(150); #ifdef SCREEN_DROPLETS CPostFX::GetBackBuffer(Scene.camera); ScreenDroplets::Process(); ScreenDroplets::Render(); #endif tbStartTimer(0, "RenderMotionBlur"); TheCamera.RenderMotionBlur(); tbEndTimer("RenderMotionBlur"); tbStartTimer(0, "Render2dStuff"); Render2dStuff(); tbEndTimer("Render2dStuff"); }else{ CDraw::CalculateAspectRatio(); #ifdef ASPECT_RATIO_SCALE CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); #else CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, DEFAULT_ASPECT_RATIO); #endif CVisibilityPlugins::SetRenderWareCamera(Scene.camera); RwCameraClear(Scene.camera, &gColourTop, CLEARMODE); if(!RsCameraBeginUpdate(Scene.camera)) goto popret; } tbStartTimer(0, "RenderMenus"); RenderMenus(); tbEndTimer("RenderMenus"); #ifdef PS2_MENU if ( TheMemoryCard.m_bWantToLoad ) goto popret; #endif tbStartTimer(0, "DoFade"); DoFade(); tbEndTimer("DoFade"); tbStartTimer(0, "Render2dStuff-Fade"); Render2dStuffAfterFade(); tbEndTimer("Render2dStuff-Fade"); // CCredits::Render(); // They added it to function above and also forgot it here #ifdef XBOX_MESSAGE_SCREEN FrontEndMenuManager.DrawOverlays(); #endif if (gbShowTimebars) tbDisplay(); DoRWStuffEndOfFrame(); POP_MEMID(); // MEMID_RENDER if(g_SlowMode) ProcessSlowMode(); return; popret: POP_MEMID(); // MEMID_RENDER } void FrontendIdle(void) { CDraw::CalculateAspectRatio(); CTimer::Update(); CSprite2d::SetRecipNearClip(); // this should be on InitialiseRenderWare according to PS2 asm. seems like a bug fix CSprite2d::InitPerFrame(); CFont::InitPerFrame(); CPad::UpdatePads(); FrontEndMenuManager.Process(); if(RsGlobal.quit) return; CameraSize(Scene.camera, nil, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); CVisibilityPlugins::SetRenderWareCamera(Scene.camera); RwCameraClear(Scene.camera, &gColourTop, CLEARMODE); if(!RsCameraBeginUpdate(Scene.camera)) return; DefinedState(); // seems redundant, but breaks resolution change. RenderMenus(); #ifdef XBOX_MESSAGE_SCREEN FrontEndMenuManager.DrawOverlays(); #endif DoFade(); Render2dStuffAfterFade(); CFont::DrawFonts(); DoRWStuffEndOfFrame(); } void InitialiseGame(void) { LoadingScreen(nil, nil, "loadsc0"); CGame::Initialise("DATA\\GTA_VC.DAT"); } RsEventStatus AppEventHandler(RsEvent event, void *param) { switch( event ) { case rsINITIALIZE: { CGame::InitialiseOnceBeforeRW(); return RsInitialize() ? rsEVENTPROCESSED : rsEVENTERROR; } case rsCAMERASIZE: { CameraSize(Scene.camera, (RwRect *)param, SCREEN_VIEWWINDOW, DEFAULT_ASPECT_RATIO); return rsEVENTPROCESSED; } case rsRWINITIALIZE: { return Initialise3D(param) ? rsEVENTPROCESSED : rsEVENTERROR; } case rsRWTERMINATE: { Terminate3D(); return rsEVENTPROCESSED; } case rsTERMINATE: { CGame::FinalShutdown(); return rsEVENTPROCESSED; } case rsPLUGINATTACH: { return PluginAttach() ? rsEVENTPROCESSED : rsEVENTERROR; } case rsINPUTDEVICEATTACH: { AttachInputDevices(); return rsEVENTPROCESSED; } case rsIDLE: { Idle(param); return rsEVENTPROCESSED; } case rsFRONTENDIDLE: { FrontendIdle(); return rsEVENTPROCESSED; } case rsACTIVATE: { param ? DMAudio.ReacquireDigitalHandle() : DMAudio.ReleaseDigitalHandle(); return rsEVENTPROCESSED; } default: { return rsEVENTNOTPROCESSED; } } } #ifndef MASTER void TheModelViewer(void) { #if (defined(GTA_PS2) || defined(GTA_XBOX)) //TODO #else // This is not original. Because; // 1- We want 2D things to be initalized, whereas original AnimViewer doesn't use them. my additions marked with X // 2- VC Mobile code run it like main function(as opposed to III and LCS), so it has it's own loop inside it, but our func. already called in a loop. CDraw::CalculateAspectRatio(); // X CAnimViewer::Update(); SetLightsWithTimeOfDayColour(Scene.world); CRenderer::ConstructRenderList(); DoRWStuffStartOfFrame(CTimeCycle::GetSkyTopRed()*0.5f, CTimeCycle::GetSkyTopGreen()*0.5f, CTimeCycle::GetSkyTopBlue()*0.5f, CTimeCycle::GetSkyBottomRed(), CTimeCycle::GetSkyBottomGreen(), CTimeCycle::GetSkyBottomBlue(), 255); CSprite2d::SetRecipNearClip(); // X CSprite2d::InitPerFrame(); // X CFont::InitPerFrame(); // X DefinedState(); CVisibilityPlugins::InitAlphaEntityList(); CAnimViewer::Render(); Render2dStuff(); // X DoRWStuffEndOfFrame(); CTimer::Update(); #endif } #endif #ifdef GTA_PS2 void TheGame(void) { printf("Into TheGame!!!\n"); PUSH_MEMID(MEMID_GAME); // NB: not popped CTimer::Initialise(); CGame::Initialise("DATA\\GTA3.DAT"); Const char *splash = GetRandomSplashScreen(); // inlined here LoadingScreen("Starting Game", NULL, splash); #ifdef GTA_PS2 // TODO(MIAMI): not checked yet if ( TheMemoryCard.CheckCardInserted(CARD_ONE) == CMemoryCard::NO_ERR_SUCCESS && TheMemoryCard.ChangeDirectory(CARD_ONE, TheMemoryCard.Cards[CARD_ONE].dir) && TheMemoryCard.FindMostRecentFileName(CARD_ONE, TheMemoryCard.MostRecentFile) == true && TheMemoryCard.CheckDataNotCorrupt(TheMemoryCard.MostRecentFile)) { strcpy(TheMemoryCard.LoadFileName, TheMemoryCard.MostRecentFile); TheMemoryCard.b_FoundRecentSavedGameWantToLoad = true; if (FrontEndMenuManager.m_PrefsLanguage != TheMemoryCard.GetLanguageToLoad()) { FrontEndMenuManager.m_PrefsLanguage = TheMemoryCard.GetLanguageToLoad(); TheText.Unload(); TheText.Load(); } CGame::currLevel = TheMemoryCard.GetLevelToLoad(); } #else //TODO #endif while (true) { if (FOUND_GAME_TO_LOAD) { Const char *splash1 = GetLevelSplashScreen(CGame::currLevel); LoadSplash(splash1); } WANT_TO_LOAD = false; CTimer::Update(); while (!(FrontEndMenuManager.m_bWantToRestart || FOUND_GAME_TO_LOAD)) { CSprite2d::InitPerFrame(); CFont::InitPerFrame(); PUSH_MEMID(MEMID_GAME_PROCESS) CPointLights::InitPerFrame(); CGame::Process(); POP_MEMID(); DMAudio.Service(); if (CGame::bDemoMode && CTimer::GetTimeInMilliseconds() > (3*60 + 30)*1000 && !CCutsceneMgr::IsCutsceneProcessing()) { WANT_TO_LOAD = false; FrontEndMenuManager.m_bWantToRestart = true; break; } if (FrontEndMenuManager.m_bWantToRestart || FOUND_GAME_TO_LOAD) break; SetLightsWithTimeOfDayColour(Scene.world); PUSH_MEMID(MEMID_RENDER); CRenderer::ConstructRenderList(); if ((!FrontEndMenuManager.m_bMenuActive || FrontEndMenuManager.m_bRenderGameInMenu == true) && TheCamera.GetScreenFadeStatus() != FADE_2 ) { CRenderer::PreRender(); // TODO(MIAMI): something ps2all specific #ifdef FIX_BUGS // This has to be done BEFORE RwCameraBeginUpdate RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip()); RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart()); #endif if (CWeather::LightningFlash && !CCullZones::CamNoRain()) DoRWStuffStartOfFrame_Horizon(255, 255, 255, 255, 255, 255, 255); else DoRWStuffStartOfFrame_Horizon(CTimeCycle::GetSkyTopRed(), CTimeCycle::GetSkyTopGreen(), CTimeCycle::GetSkyTopBlue(), CTimeCycle::GetSkyBottomRed(), CTimeCycle::GetSkyBottomGreen(), CTimeCycle::GetSkyBottomBlue(), 255); DefinedState(); #ifndef FIX_BUGS RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip()); RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart()); #endif RenderScene(); RenderDebugShit(); RenderEffects(); if ((TheCamera.m_BlurType == MOTION_BLUR_NONE || TheCamera.m_BlurType == MOTION_BLUR_LIGHT_SCENE) && TheCamera.m_ScreenReductionPercentage > 0.0f) TheCamera.SetMotionBlurAlpha(150); TheCamera.RenderMotionBlur(); Render2dStuff(); } else { CameraSize(Scene.camera, NULL, SCREEN_VIEWWINDOW, SCREEN_ASPECT_RATIO); CVisibilityPlugins::SetRenderWareCamera(Scene.camera); RwCameraClear(Scene.camera, &gColourTop, CLEARMODE); RsCameraBeginUpdate(Scene.camera); } RenderMenus(); if (WANT_TO_LOAD) { POP_MEMID(); // MEMID_RENDER break; } DoFade(); Render2dStuffAfterFade(); CCredits::Render(); DoRWStuffEndOfFrame(); while (frameCount < 2) ; frameCount = 0; CTimer::Update(); POP_MEMID(): // MEMID_RENDER if (g_SlowMode) ProcessSlowMode(); } CPad::ResetCheats(); CPad::StopPadsShaking(); DMAudio.ChangeMusicMode(MUSICMODE_DISABLE); CGame::ShutDownForRestart(); CTimer::Stop(); if (FrontEndMenuManager.m_bWantToRestart || FOUND_GAME_TO_LOAD) { if (FOUND_GAME_TO_LOAD) { FrontEndMenuManager.m_bWantToRestart = true; WANT_TO_LOAD = true; } CGame::InitialiseWhenRestarting(); DMAudio.ChangeMusicMode(MUSICMODE_GAME); FrontEndMenuManager.m_bWantToRestart = false; continue; } break; } DMAudio.Terminate(); } void SystemInit() { #ifdef USE_CUSTOM_ALLOCATOR InitMemoryMgr(); #endif #ifdef GTA_PS2 CFileMgr::InitCdSystem(); char path[256]; sprintf(path, "cdrom0:\\%s%s;1", "SYSTEM\\", "IOPRP241.IMG"); sceSifInitRpc(0); while ( !sceSifRebootIop(path) ) ; while( !sceSifSyncIop() ) ; sceSifInitRpc(0); CFileMgr::InitCdSystem(); sceFsReset(); CFileMgr::InitCd(); char modulepath[256]; strcpy(modulepath, "cdrom0:\\"); strcat(modulepath, "SYSTEM\\"); strcat(modulepath, "SIO2MAN.IRX"); LoadModule(modulepath); strcpy(modulepath, "cdrom0:\\"); strcat(modulepath, "SYSTEM\\"); strcat(modulepath, "PADMAN.IRX"); LoadModule(modulepath); strcpy(modulepath, "cdrom0:\\"); strcat(modulepath, "SYSTEM\\"); strcat(modulepath, "LIBSD.IRX"); LoadModule(modulepath); strcpy(modulepath, "cdrom0:\\"); strcat(modulepath, "SYSTEM\\"); strcat(modulepath, "SDRDRV.IRX"); LoadModule(modulepath); strcpy(modulepath, "cdrom0:\\"); strcat(modulepath, "SYSTEM\\"); strcat(modulepath, "MCMAN.IRX"); LoadModule(modulepath); strcpy(modulepath, "cdrom0:\\"); strcat(modulepath, "SYSTEM\\"); strcat(modulepath, "MCSERV.IRX"); LoadModule(modulepath); strcpy(modulepath, "cdrom0:\\"); strcat(modulepath, "SYSTEM\\"); strcat(modulepath, "CDSTREAM.IRX"); LoadModule(modulepath); strcpy(modulepath, "cdrom0:\\"); strcat(modulepath, "SYSTEM\\"); strcat(modulepath, "SAMPMAN2.IRX"); LoadModule(modulepath); #endif #ifdef GTA_PS2 ThreadParam param; param.entry = &IdleThread; param.stack = idleThreadStack; param.stackSize = 2048; param.initPriority = 127; param.gpReg = &_gp; int thread = CreateThread(¶m); StartThread(thread, NULL); #else // #endif #ifdef GTA_PS2_STUFF CPad::Initialise(); #endif CPad::GetPad(0)->Mode = 0; CGame::frenchGame = false; CGame::germanGame = false; CGame::nastyGame = true; FrontEndMenuManager.m_PrefsAllowNastyGame = true; #ifdef GTA_PS2 // TODO(MIAMI): this code probably went elsewhere? int32 lang = sceScfGetLanguage(); if ( lang == SCE_ITALIAN_LANGUAGE ) FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_ITALIAN; else if ( lang == SCE_SPANISH_LANGUAGE ) FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_SPANISH; else if ( lang == SCE_GERMAN_LANGUAGE ) { CGame::germanGame = true; CGame::nastyGame = false; FrontEndMenuManager.m_PrefsAllowNastyGame = false; FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_GERMAN; } else if ( lang == SCE_FRENCH_LANGUAGE ) { CGame::frenchGame = true; CGame::nastyGame = false; FrontEndMenuManager.m_PrefsAllowNastyGame = false; FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_FRENCH; } else FrontEndMenuManager.m_PrefsLanguage = LANGUAGE_AMERICAN; FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame(); #else // #endif #ifdef GTA_PS2 TheMemoryCard.Init(); #endif } int VBlankCounter(int ca) { frameCount++; ExitHandler(); return 0; } // linked against by RW! extern "C" void WaitVBlank(void) { int32 startFrame = frameCount; while(startFrame == frameCount); } void GameInit(bool onlyRW) { if(onlyRW) { #ifdef GTA_PS2 Initialise3D(nil); #else Initialise3D(nil); //TODO: window parameter #endif gameAlreadyInitialised = true; } else { if ( !gameAlreadyInitialised ) #ifdef GTA_PS2 Initialise3D(nil); #else Initialise3D(nil); //TODO: window parameter #endif } #ifdef GTA_PS2 char *files[] = { "\\ANIM\\CUTS.IMG;1", "\\ANIM\\CUTS.DIR;1", "\\ANIM\\PED.IFP;1", "\\MODELS\\FRONTEN1.TXD;1", "\\MODELS\\FRONTEN2.TXD;1", "\\MODELS\\FONTS.TXD;1", "\\MODELS\\HUD.TXD;1", "\\MODELS\\PARTICLE.TXD;1", "\\MODELS\\MISC.TXD;1", "\\MODELS\\GENERIC.TXD;1", "\\MODELS\\GTA3.DIR;1", // TODO: japanese? #ifdef GTA_PAL "\\TEXT\\ENGLISH.GXT;1", "\\TEXT\\FRENCH.GXT;1", "\\TEXT\\GERMAN.GXT;1", "\\TEXT\\ITALIAN.GXT;1", "\\TEXT\\SPANISH.GXT;1", #else "\\TEXT\\AMERICAN.GXT;1", #endif "\\MODELS\\COLL\\GENERIC.COL;1", "\\MODELS\\COLL\\VEHICLES.COL;1", "\\MODELS\\COLL\\PEDS.COL;1", "\\MODELS\\COLL\\WEAPONS.COL;1", "\\MODELS\\GENERIC\\AIR_VLO.DFF;1", "\\MODELS\\GENERIC\\WHEELS.DFF;1", "\\MODELS\\GENERIC\\ARROW.DFF;1", "\\MODELS\\GENERIC\\ZONECYLB.DFF;1", "\\DATA\\HANDLING.CFG;1", "\\DATA\\SURFACE.DAT;1", "\\DATA\\PEDSTATS.DAT;1", "\\DATA\\TIMECYC.DAT;1", "\\DATA\\PARTICLE.CFG;1", "\\DATA\\DEFAULT.DAT;1", "\\DATA\\DEFAULT.IDE;1", "\\DATA\\GTA_VC.DAT;1", "\\DATA\\OBJECT.DAT;1", "\\DATA\\MAP.ZON;1", "\\DATA\\NAVIG.ZON;1", "\\DATA\\INFO.ZON;1", "\\DATA\\WATERPRO.DAT;1", "\\DATA\\MAIN.SCM;1", "\\DATA\\CARCOLS.DAT;1", "\\DATA\\PED.DAT;1", "\\DATA\\FISTFITE.DAT;1", "\\DATA\\WEAPON.DAT;1", "\\DATA\\PEDGRP.DAT;1", "\\DATA\\PATHS\\FLIGHT.DAT;1", "\\DATA\\PATHS\\FLIGHT2.DAT;1", "\\DATA\\PATHS\\FLIGHT3.DAT;1", "\\DATA\\PATHS\\SPATH0.DAT;1", "\\DATA\\MAPS\\LITTLEHA\\LITTLEHA.IDE;1", "\\DATA\\MAPS\\DOWNTOWN\\DOWNTOWN.IDE;1", "\\DATA\\MAPS\\DOWNTOWS\\DOWNTOWS.IDE;1", "\\DATA\\MAPS\\DOCKS\\DOCKS.IDE;1", "\\DATA\\MAPS\\WASHINTN\\WASHINTN.IDE;1", "\\DATA\\MAPS\\WASHINTS\\WASHINTS.IDE;1", "\\DATA\\MAPS\\OCEANDRV\\OCEANDRV.IDE;1", "\\DATA\\MAPS\\OCEANDN\\OCEANDN.IDE;1", "\\DATA\\MAPS\\GOLF\\GOLF.IDE;1", "\\DATA\\MAPS\\BRIDGE\\BRIDGE.IDE;1", "\\DATA\\MAPS\\STARISL\\STARISL.IDE;1", "\\DATA\\MAPS\\NBEACHBT\\NBEACHBT.IDE;1", "\\DATA\\MAPS\\NBEACHW\\NBEACHW.IDE;1", "\\DATA\\MAPS\\NBEACH\\NBEACH.IDE;1", "\\DATA\\MAPS\\BANK\\BANK.IDE;1", "\\DATA\\MAPS\\MALL\\MALL.IDE;1", "\\DATA\\MAPS\\YACHT\\YACHT.IDE;1", "\\DATA\\MAPS\\CISLAND\\CISLAND.IDE;1", "\\DATA\\MAPS\\CLUB\\CLUB.IDE;1", "\\DATA\\MAPS\\HOTEL\\HOTEL.IDE;1", "\\DATA\\MAPS\\LAWYERS\\LAWYERS.IDE;1", "\\DATA\\MAPS\\STRIPCLB\\STRIPCLB.IDE;1", "\\DATA\\MAPS\\AIRPORT\\AIRPORT.IDE;1", "\\DATA\\MAPS\\HAITI\\HAITI.IDE;1", "\\DATA\\MAPS\\HAITIN\\HAITIN.IDE;1", "\\DATA\\MAPS\\CONCERTH\\CONCERTH.IDE;1", "\\DATA\\MAPS\\MANSION\\MANSION.IDE;1", "\\DATA\\MAPS\\ISLANDSF\\ISLANDSF.IDE;1", "\\DATA\\MAPS\\LITTLEHA\\LITTLEHA.IPL;1", "\\DATA\\MAPS\\DOWNTOWN\\DOWNTOWN.IPL;1", "\\DATA\\MAPS\\DOWNTOWS\\DOWNTOWS.IPL;1", "\\DATA\\MAPS\\DOCKS\\DOCKS.IPL;1", "\\DATA\\MAPS\\WASHINTN\\WASHINTN.IPL;1", "\\DATA\\MAPS\\WASHINTS\\WASHINTS.IPL;1", "\\DATA\\MAPS\\OCEANDRV\\OCEANDRV.IPL;1", "\\DATA\\MAPS\\OCEANDN\\OCEANDN.IPL;1", "\\DATA\\MAPS\\GOLF\\GOLF.IPL;1", "\\DATA\\MAPS\\BRIDGE\\BRIDGE.IPL;1", "\\DATA\\MAPS\\STARISL\\STARISL.IPL;1", "\\DATA\\MAPS\\NBEACHBT\\NBEACHBT.IPL;1", "\\DATA\\MAPS\\NBEACH\\NBEACH.IPL;1", "\\DATA\\MAPS\\NBEACHW\\NBEACHW.IPL;1", "\\DATA\\MAPS\\CISLAND\\CISLAND.IPL;1", "\\DATA\\MAPS\\AIRPORT\\AIRPORT.IPL;1", "\\DATA\\MAPS\\HAITI\\HAITI.IPL;1", "\\DATA\\MAPS\\HAITIN\\HAITIN.IPL;1", "\\DATA\\MAPS\\ISLANDSF\\ISLANDSF.IPL;1", "\\DATA\\MAPS\\BANK\\BANK.IPL;1", "\\DATA\\MAPS\\MALL\\MALL.IPL;1", "\\DATA\\MAPS\\YACHT\\YACHT.IPL;1", "\\DATA\\MAPS\\CLUB\\CLUB.IPL;1", "\\DATA\\MAPS\\HOTEL\\HOTEL.IPL;1", "\\DATA\\MAPS\\LAWYERS\\LAWYERS.IPL;1", "\\DATA\\MAPS\\STRIPCLB\\STRIPCLB.IPL;1", "\\DATA\\MAPS\\CONCERTH\\CONCERTH.IPL;1", "\\DATA\\MAPS\\MANSION\\MANSION.IPL;1", "\\DATA\\MAPS\\GENERIC.IDE;1", "\\DATA\\OCCLU.IPL;1", "\\DATA\\MAPS\\PATHS.IPL;1", "\\TXD\\LOADSC0.TXD;1", "\\TXD\\LOADSC1.TXD;1", "\\TXD\\LOADSC2.TXD;1", "\\TXD\\LOADSC3.TXD;1", "\\TXD\\LOADSC4.TXD;1", "\\TXD\\LOADSC5.TXD;1", "\\TXD\\LOADSC6.TXD;1", "\\TXD\\LOADSC7.TXD;1", "\\TXD\\LOADSC8.TXD;1", "\\TXD\\LOADSC9.TXD;1", "\\TXD\\LOADSC10.TXD;1", "\\TXD\\LOADSC11.TXD;1", "\\TXD\\LOADSC12.TXD;1", "\\TXD\\LOADSC13.TXD;1", "\\TXD\\SPLASH1.TXD;1" }; for ( int32 i = 0; i < ARRAY_SIZE(files); i++ ) SkyRegisterFileOnCd([i]); #endif #ifdef GTA_PS2 AddIntcHandler(INTC_VBLANK_S, VBlankCounter, 0); #endif #ifdef GTA_PS2 sceCdCLOCK rtc; sceCdReadClock(&rtc); uint32 seed = rtc.minute + rtc.day; uint32 seed2 = (seed << 4)-seed; uint32 seed3 = (seed2 << 4)-seed2; srand ((seed3<<4)+rtc.second); #else //TODO: mysrand(); #endif // gameAlreadyInitialised = true; // why is this gone? } } int32 SkipAllMPEGs; int32 gMemoryStickLoadOK; void PlayIntroMPEGs() { #ifdef GTA_PS2 if (gameAlreadyInitialised) RpSkySuspend(); InitMPEGPlayer(); float skipTime; // wrong type, should be int #ifdef GTA_PAL if(gMemoryStickLoadOK) skipTime = 2500000; else skipTime = 5300000; if(!SkipAllMPEGs) PlayMPEG("cdrom0:\\MOVIES\\VCPAL.PSS;1", false, unk); if(!SkipAllMPEGs){ SkipAllMPEGs = true; PlayMPEG("cdrom0:\\MOVIES\\VICEPAL.PSS;1", true, 0); } #else if(gMemoryStickLoadOK) skipTime = 2750000; else skipTime = 5500000; if(!SkipAllMPEGs) PlayMPEG("cdrom0:\\MOVIES\\VCNTSC.PSS;1", false, unk); if(!SkipAllMPEGs){ SkipAllMPEGs = true; PlayMPEG("cdrom0:\\MOVIES\\VICE.PSS;1", true, 0); } #endif ShutdownMPEGPlayer(); if ( gameAlreadyInitialised ) RpSkyResume(); #else //TODO #endif } int main(int argc, char *argv[]) { #ifdef __MWERKS__ mwInit(); // metrowerks initialisation #endif SystemInit(); if(RsEventHandler(rsINITIALIZE, nil) == rsEVENTERROR) return 0; #ifdef GTA_PS2 int32 r = TheMemoryCard.CheckCardStateAtGameStartUp(CARD_ONE); if ( r == CMemoryCard::ERR_DIRNOENTRY || r == CMemoryCard::ERR_NOFORMAT ) { GameInit(true); TheText.Unload(); TheText.Load(); CFont::Initialise(); FrontEndMenuManager.DrawMemoryCardStartUpMenus(); }else if(r == CMemoryCard::ERR_OPENNOENTRY) gMemoryStickLoadOK = false; else if(r == CMemoryCard::ERR_NONE) gMemoryStickLoadOK = true; #endif PlayIntroMPEGs(); GameInit(false); frameCount = 0; while(frameCount < 100); CGame::InitialiseOnceAfterRW(); TheGame(); #if 0 // maybe ifndef FINAL or MASTER? CGame::ShutDown(); RwEngineStop(); RwEngineClose(); RwEngineTerm(); #ifdef __MWERKS__ mwExit(); // metrowerks shutdown #endif #endif return 0; } #endif ================================================ FILE: src/core/main.h ================================================ #pragma once #ifndef FINAL // defined in RwHelpder.cpp void PushRendergroup(const char *name); void PopRendergroup(void); #define PUSH_RENDERGROUP(str) PushRendergroup(str) #define POP_RENDERGROUP() PopRendergroup() #else #define PUSH_RENDERGROUP(str) #define POP_RENDERGROUP() #endif struct GlobalScene { RpWorld *world; RwCamera *camera; }; extern GlobalScene Scene; extern uint8 work_buff[55000]; extern char gString[256]; extern char gString2[512]; extern wchar gUString[256]; extern wchar gUString2[256]; extern bool gbPrintShite; extern bool gbModelViewer; #ifdef TIMEBARS extern bool gbShowTimebars; #else #define gbShowTimebars false #endif #ifndef FINAL extern bool gbPrintMemoryUsage; #endif class CSprite2d; bool DoRWStuffStartOfFrame(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); bool DoRWStuffStartOfFrame_Horizon(int16 TopRed, int16 TopGreen, int16 TopBlue, int16 BottomRed, int16 BottomGreen, int16 BottomBlue, int16 Alpha); void DoRWStuffEndOfFrame(void); void PreAllocateRwObjects(void); void InitialiseGame(void); void LoadingScreen(const char *str1, const char *str2, const char *splashscreen); void LoadingIslandScreen(const char *levelName); CSprite2d *LoadSplash(const char *name); void DestroySplashScreen(void); Const char *GetLevelSplashScreen(int level); Const char *GetRandomSplashScreen(void); void LittleTest(void); void ValidateVersion(); void ResetLoadingScreenBar(void); #ifndef MASTER void TheModelViewer(void); #endif #ifdef LOAD_INI_SETTINGS bool LoadINISettings(); void SaveINISettings(); void LoadINIControllerSettings(); void SaveINIControllerSettings(); #endif #ifdef NEW_RENDERER extern bool gbNewRenderer; bool FredIsInFirstPersonCam(void); #endif #ifdef DRAW_GAME_VERSION_TEXT extern bool gbDrawVersionText; #endif #ifdef NO_MOVIES extern bool gbNoMovies; #endif ================================================ FILE: src/core/obrstr.cpp ================================================ #include "common.h" #include "Debug.h" #include "obrstr.h" char obrstr[128]; char obrstr2[128]; void ObrInt(int32 n1) { IntToStr(n1, obrstr); CDebug::DebugAddText(obrstr); } void ObrInt2(int32 n1, int32 n2) { IntToStr(n1, obrstr); strcat(obrstr, " "); IntToStr(n2, obrstr2); strcat(obrstr, obrstr2); CDebug::DebugAddText(obrstr); } void ObrInt3(int32 n1, int32 n2, int32 n3) { IntToStr(n1, obrstr); strcat(obrstr, " "); IntToStr(n2, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n3, obrstr2); strcat(obrstr, obrstr2); CDebug::DebugAddText(obrstr); } void ObrInt4(int32 n1, int32 n2, int32 n3, int32 n4) { IntToStr(n1, obrstr); strcat(obrstr, " "); IntToStr(n2, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n3, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n4, obrstr2); strcat(obrstr, obrstr2); CDebug::DebugAddText(obrstr); } void ObrInt5(int32 n1, int32 n2, int32 n3, int32 n4, int32 n5) { IntToStr(n1, obrstr); strcat(obrstr, " "); IntToStr(n2, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n3, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n4, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n5, obrstr2); strcat(obrstr, obrstr2); CDebug::DebugAddText(obrstr); } void ObrInt6(int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) { IntToStr(n1, obrstr); strcat(obrstr, " "); IntToStr(n2, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n3, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n4, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n5, obrstr2); strcat(obrstr, obrstr2); strcat(obrstr, " "); IntToStr(n6, obrstr2); strcat(obrstr, obrstr2); CDebug::DebugAddText(obrstr); } void IntToStr(int32 inNum, char *outStr) { bool isNeg = inNum < 0; if (isNeg) { inNum = -inNum; *outStr = '-'; } int16 digits = 1; if (inNum > 9) { int32 _inNum = inNum; do { digits++; _inNum /= 10; } while (_inNum > 9); } int32 strSize = digits; if (isNeg) strSize++; char *pStr = &outStr[strSize]; int32 i = 0; do { *(pStr-- - 1) = (inNum % 10) + '0'; inNum /= 10; } while (++i < strSize); outStr[strSize] = '\0'; } ================================================ FILE: src/core/obrstr.h ================================================ #pragma once void ObrInt(int32 n1); void ObrInt2(int32 n1, int32 n2); void ObrInt3(int32 n1, int32 n2, int32 n3); void ObrInt4(int32 n1, int32 n2, int32 n3, int32 n4); void ObrInt5(int32 n1, int32 n2, int32 n3, int32 n4, int32 n5); void ObrInt6(int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); void IntToStr(int32 inNum, char *outStr); ================================================ FILE: src/core/re3.cpp ================================================ #include #define WITHWINDOWS #include "common.h" #if defined DETECT_JOYSTICK_MENU && defined XINPUT #include #if !defined(PSAPI_VERSION) || (PSAPI_VERSION > 1) #pragma comment( lib, "Xinput9_1_0.lib" ) #else #pragma comment( lib, "Xinput.lib" ) #endif #endif #include "Renderer.h" #include "Occlusion.h" #include "Credits.h" #include "Camera.h" #include "Weather.h" #include "Timecycle.h" #include "Clock.h" #include "World.h" #include "Vehicle.h" #include "ModelIndices.h" #include "Streaming.h" #include "Boat.h" #include "Heli.h" #include "Automobile.h" #include "Bike.h" #include "Console.h" #include "Debug.h" #include "Hud.h" #include "SceneEdit.h" #include "Pad.h" #include "PlayerPed.h" #include "Radar.h" #include "debugmenu.h" #include "Frontend.h" #include "WaterLevel.h" #include "main.h" #include "Script.h" #include "MBlur.h" #include "postfx.h" #include "custompipes.h" #include "MemoryHeap.h" #include "FileMgr.h" #include "Camera.h" #include "MBlur.h" #include "ControllerConfig.h" #ifdef DETECT_JOYSTICK_MENU #include "crossplatform.h" #endif #ifndef _WIN32 #include "assert.h" #include #endif #include #ifdef RWLIBS extern "C" int vsprintf(char* const _Buffer, char const* const _Format, va_list _ArgList); #endif #ifdef USE_PS2_RAND unsigned long long 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; } #ifdef CUSTOM_FRONTEND_OPTIONS #include "frontendoption.h" #ifdef MORE_LANGUAGES void LangPolSelect(int8 action) { if (action == FEOPTION_ACTION_SELECT) { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_POLISH; FrontEndMenuManager.m_bFrontEnd_ReloadObrTxtGxt = true; FrontEndMenuManager.InitialiseChangedLanguageSettings(); FrontEndMenuManager.SaveSettings(); } } void LangRusSelect(int8 action) { if (action == FEOPTION_ACTION_SELECT) { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_RUSSIAN; FrontEndMenuManager.m_bFrontEnd_ReloadObrTxtGxt = true; FrontEndMenuManager.InitialiseChangedLanguageSettings(); FrontEndMenuManager.SaveSettings(); } } void LangJapSelect(int8 action) { if (action == FEOPTION_ACTION_SELECT) { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_JAPANESE; FrontEndMenuManager.m_bFrontEnd_ReloadObrTxtGxt = true; FrontEndMenuManager.InitialiseChangedLanguageSettings(); FrontEndMenuManager.SaveSettings(); } } #endif void CustomFrontendOptionsPopulate(void) { // Moved to an array in MenuScreensCustom.cpp, but APIs are still available. see frontendoption.h int fd; // These work only if we have neo folder, so they're dynamically added #ifdef EXTENDED_PIPELINES const char *vehPipelineNames[] = { "FED_MFX", "FED_NEO" }; const char *off_on[] = { "FEM_OFF", "FEM_ON" }; fd = CFileMgr::OpenFile("neo/neo.txd","r"); if (fd) { #ifdef GRAPHICS_MENU_OPTIONS FrontendOptionSetCursor(MENUPAGE_GRAPHICS_SETTINGS, -3, false); FrontendOptionAddSelect("FED_VPL", 0, 0, MENUALIGN_LEFT, vehPipelineNames, ARRAY_SIZE(vehPipelineNames), (int8*)&CustomPipes::VehiclePipeSwitch, false, nil, "Graphics", "VehiclePipeline"); FrontendOptionAddSelect("FED_PRM", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::RimlightEnable, false, nil, "Graphics", "NeoRimLight"); FrontendOptionAddSelect("FED_WLM", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::LightmapEnable, false, nil, "Graphics", "NeoLightMaps"); FrontendOptionAddSelect("FED_RGL", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::GlossEnable, false, nil, "Graphics", "NeoRoadGloss"); #else FrontendOptionSetCursor(MENUPAGE_DISPLAY_SETTINGS, -3, false); FrontendOptionAddSelect("FED_VPL", 0, 0, MENUALIGN_LEFT, vehPipelineNames, ARRAY_SIZE(vehPipelineNames), (int8*)&CustomPipes::VehiclePipeSwitch, false, nil, "Graphics", "VehiclePipeline"); FrontendOptionAddSelect("FED_PRM", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::RimlightEnable, false, nil, "Graphics", "NeoRimLight"); FrontendOptionAddSelect("FED_WLM", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::LightmapEnable, false, nil, "Graphics", "NeoLightMaps"); FrontendOptionAddSelect("FED_RGL", 0, 0, MENUALIGN_LEFT, off_on, 2, (int8*)&CustomPipes::GlossEnable, false, nil, "Graphics", "NeoRoadGloss"); #endif CFileMgr::CloseFile(fd); } #endif // Add outsourced language translations, if files are found #ifdef MORE_LANGUAGES int fd2; FrontendOptionSetCursor(MENUPAGE_LANGUAGE_SETTINGS, 5, false); #if 0 if (fd = CFileMgr::OpenFile("text/polish.gxt")) { if (fd2 = CFileMgr::OpenFile("models/fonts_p.txd")) { FrontendOptionAddDynamic("FEL_POL", 0, 0, MENUALIGN_CENTER, nil, nil, LangPolSelect, nil, nil); CFileMgr::CloseFile(fd2); } CFileMgr::CloseFile(fd); } #endif if (fd = CFileMgr::OpenFile("text/russian.gxt")) { if (fd2 = CFileMgr::OpenFile("models/fonts_r.txd")) { FrontendOptionAddDynamic("FEL_RUS", 0, 0, MENUALIGN_CENTER, nil, nil, LangRusSelect, nil, nil); CFileMgr::CloseFile(fd2); } CFileMgr::CloseFile(fd); } #if 0 if (fd = CFileMgr::OpenFile("text/japanese.gxt")) { if (fd2 = CFileMgr::OpenFile("models/fonts_j.txd")) { FrontendOptionAddDynamic("FEL_JAP", 0, 0, MENUALIGN_CENTER, nil, nil, LangJapSelect, nil, nil); CFileMgr::CloseFile(fd2); } CFileMgr::CloseFile(fd); } #endif #endif } #endif #ifdef LOAD_INI_SETTINGS #include "ini_parser.hpp" linb::ini cfg; bool ReadIniIfExists(const char *cat, const char *key, uint32 *out) { std::string strval = cfg.get(cat, key, "\xBA"); const char *value = strval.c_str(); char *endPtr; if (value && value[0] != '\xBA') { *out = strtoul(value, &endPtr, 0); return true; } return false; } bool ReadIniIfExists(const char *cat, const char *key, bool *out) { std::string strval = cfg.get(cat, key, "\xBA"); const char *value = strval.c_str(); char *endPtr; if (value && value[0] != '\xBA') { *out = strtoul(value, &endPtr, 0); return true; } return false; } bool ReadIniIfExists(const char *cat, const char *key, int32 *out) { std::string strval = cfg.get(cat, key, "\xBA"); const char *value = strval.c_str(); char *endPtr; if (value && value[0] != '\xBA') { *out = strtol(value, &endPtr, 0); return true; } return false; } bool ReadIniIfExists(const char *cat, const char *key, int8 *out) { std::string strval = cfg.get(cat, key, "\xBA"); const char *value = strval.c_str(); char *endPtr; if (value && value[0] != '\xBA') { *out = strtol(value, &endPtr, 0); return true; } return false; } bool ReadIniIfExists(const char *cat, const char *key, float *out) { std::string strval = cfg.get(cat, key, "\xBA"); const char *value = strval.c_str(); if (value && value[0] != '\xBA') { *out = atof(value); return true; } return false; } bool ReadIniIfExists(const char *cat, const char *key, char *out, int size) { std::string strval = cfg.get(cat, key, "\xBA"); const char *value = strval.c_str(); if (value && value[0] != '\xBA') { strncpy(out, value, size); return true; } return false; } void StoreIni(const char *cat, const char *key, uint32 val) { char temp[10]; sprintf(temp, "%u", val); cfg.set(cat, key, temp); } void StoreIni(const char *cat, const char *key, uint8 val) { char temp[10]; sprintf(temp, "%u", (uint32)val); cfg.set(cat, key, temp); } void StoreIni(const char *cat, const char *key, int32 val) { char temp[10]; sprintf(temp, "%d", val); cfg.set(cat, key, temp); } void StoreIni(const char *cat, const char *key, int8 val) { char temp[10]; sprintf(temp, "%d", (int32)val); cfg.set(cat, key, temp); } void StoreIni(const char *cat, const char *key, float val) { char temp[10]; sprintf(temp, "%f", val); cfg.set(cat, key, temp); } void StoreIni(const char *cat, const char *key, char *val, int size) { cfg.set(cat, key, val); } const char *iniControllerActions[] = { "PED_FIREWEAPON", "PED_CYCLE_WEAPON_RIGHT", "PED_CYCLE_WEAPON_LEFT", "GO_FORWARD", "GO_BACK", "GO_LEFT", "GO_RIGHT", "PED_SNIPER_ZOOM_IN", "PED_SNIPER_ZOOM_OUT", "VEHICLE_ENTER_EXIT", "CAMERA_CHANGE_VIEW_ALL_SITUATIONS", "PED_JUMPING", "PED_SPRINT", "PED_LOOKBEHIND", "PED_DUCK", "PED_ANSWER_PHONE", #ifdef BIND_VEHICLE_FIREWEAPON "VEHICLE_FIREWEAPON", #endif "VEHICLE_ACCELERATE", "VEHICLE_BRAKE", "VEHICLE_CHANGE_RADIO_STATION", "VEHICLE_HORN", "TOGGLE_SUBMISSIONS", "VEHICLE_HANDBRAKE", "PED_1RST_PERSON_LOOK_LEFT", "PED_1RST_PERSON_LOOK_RIGHT", "VEHICLE_LOOKLEFT", "VEHICLE_LOOKRIGHT", "VEHICLE_LOOKBEHIND", "VEHICLE_TURRETLEFT", "VEHICLE_TURRETRIGHT", "VEHICLE_TURRETUP", "VEHICLE_TURRETDOWN", "PED_CYCLE_TARGET_LEFT", "PED_CYCLE_TARGET_RIGHT", "PED_CENTER_CAMERA_BEHIND_PLAYER", "PED_LOCK_TARGET", "NETWORK_TALK", "PED_1RST_PERSON_LOOK_UP", "PED_1RST_PERSON_LOOK_DOWN", "_CONTROLLERACTION_36", "TOGGLE_DPAD", "SWITCH_DEBUG_CAM_ON", "TAKE_SCREEN_SHOT", "SHOW_MOUSE_POINTER_TOGGLE", "UNKNOWN_ACTION" }; const char *iniControllerTypes[] = { "kbd:", "2ndKbd:", "mouse:", "joy:" }; const char *iniMouseButtons[] = {"LEFT","MIDDLE","RIGHT","WHLUP","WHLDOWN","X1","X2"}; const char *iniKeyboardButtons[] = {"ESC","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12", "INS","DEL","HOME","END","PGUP","PGDN","UP","DOWN","LEFT","RIGHT","DIVIDE","TIMES","PLUS","MINUS","PADDEL", "PADEND","PADDOWN","PADPGDN","PADLEFT","PAD5","NUMLOCK","PADRIGHT","PADHOME","PADUP","PADPGUP","PADINS", "PADENTER", "SCROLL","PAUSE","BACKSP","TAB","CAPSLK","ENTER","LSHIFT","RSHIFT","SHIFT","LCTRL","RCTRL","LALT", "RALT", "LWIN", "RWIN", "APPS", "NULL"}; void LoadINIControllerSettings() { #ifdef DETECT_JOYSTICK_MENU #ifdef XINPUT int storedJoy1 = -1; if (ReadIniIfExists("Controller", "JoystickName", &storedJoy1)) { CPad::XInputJoy1 = -1; CPad::XInputJoy2 = -1; XINPUT_STATE xstate; memset(&xstate, 0, sizeof(XINPUT_STATE)); // Firstly confirm & set joy 1 if (XInputGetState(storedJoy1, &xstate) == ERROR_SUCCESS) { CPad::XInputJoy1 = storedJoy1; } for (int i = 0; i <= 3; i++) { if (XInputGetState(i, &xstate) == ERROR_SUCCESS) { if (CPad::XInputJoy1 == -1) CPad::XInputJoy1 = i; else if (CPad::XInputJoy2 == -1 && i != CPad::XInputJoy1) CPad::XInputJoy2 = i; } } // There is no plug event on XInput, so let's leave XInputJoy1/2 as 0/1 respectively, and hotplug will be possible. if (CPad::XInputJoy1 == -1) { CPad::XInputJoy1 = 0; CPad::XInputJoy2 = 1; } else if (CPad::XInputJoy2 == -1) { CPad::XInputJoy2 = (CPad::XInputJoy1 + 1) % 4; } } #else ReadIniIfExists("Controller", "JoystickName", gSelectedJoystickName, 128); #endif #endif // force to default GTA behaviour (never overwrite bindings on joy change/initialization) if user init'ed/set bindings before we introduced that if (!ReadIniIfExists("Controller", "PadButtonsInited", &ControlsManager.ms_padButtonsInited)) { ControlsManager.ms_padButtonsInited = cfg.category_size("Bindings") != 0 ? 16 : 0; } for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) { char value[128]; if (ReadIniIfExists("Bindings", iniControllerActions[i], value, 128)) { for (int32 j = 0; j < MAX_CONTROLLERTYPES; j++){ ControlsManager.ClearSettingsAssociatedWithAction((e_ControllerAction)i, (eControllerType)j); } for (char *binding = strtok(value,", "); binding != nil; binding = strtok(nil, ", ")) { int contType = -1; for (int32 k = 0; k < ARRAY_SIZE(iniControllerTypes); k++) { int len = strlen(iniControllerTypes[k]); if (strncmp(binding, iniControllerTypes[k], len) == 0) { contType = k; binding += len; break; } } if (contType == -1) continue; int contKey; if (contType == JOYSTICK) { char *temp; contKey = strtol(binding, &temp, 0); } else if (contType == KEYBOARD || contType == OPTIONAL_EXTRA) { if (strlen(binding) == 1) { contKey = binding[0]; } else if(strcmp(binding, "SPC") == 0) { contKey = ' '; } else { for (int32 k = 0; k < ARRAY_SIZE(iniKeyboardButtons); k++) { if(strcmp(binding, iniKeyboardButtons[k]) == 0) { contKey = 1000 + k; break; } } } } else if (contType == MOUSE) { for (int32 k = 0; k < ARRAY_SIZE(iniMouseButtons); k++) { if(strcmp(binding, iniMouseButtons[k]) == 0) { contKey = 1 + k; break; } } } ControlsManager.SetControllerKeyAssociatedWithAction((e_ControllerAction)i, contKey, (eControllerType)contType); } } } } void SaveINIControllerSettings() { for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) { char value[128] = { '\0' }; // upper limit should've been GetNumOfSettingsForAction(i), but sadly even R* doesn't use it's own system correctly, and there are gaps between orders. for (int32 j = SETORDER_1; j < MAX_SETORDERS; j++){ // We respect the m_ContSetOrder, and join/implode/order the bindings according to that; using comma as seperator. for (int32 k = 0; k < MAX_CONTROLLERTYPES; k++){ if (ControlsManager.m_aSettings[i][k].m_ContSetOrder == j) { char next[32]; if (k == JOYSTICK) { snprintf(next, 32, "%s%d,", iniControllerTypes[k], ControlsManager.m_aSettings[i][k].m_Key); } else if (k == KEYBOARD || k == OPTIONAL_EXTRA) { if (ControlsManager.m_aSettings[i][k].m_Key == ' ') snprintf(next, 32, "%sSPC,", iniControllerTypes[k]); else if (ControlsManager.m_aSettings[i][k].m_Key < 256) snprintf(next, 32, "%s%c,", iniControllerTypes[k], ControlsManager.m_aSettings[i][k].m_Key); else snprintf(next, 32, "%s%s,", iniControllerTypes[k], iniKeyboardButtons[ControlsManager.m_aSettings[i][k].m_Key - 1000]); } else if (k == MOUSE) { snprintf(next, 32, "%s%s,", iniControllerTypes[k], iniMouseButtons[ControlsManager.m_aSettings[i][k].m_Key - 1]); } strcat(value, next); break; } } } int len = strlen(value); if (len > 0) value[len - 1] = '\0'; // to remove comma StoreIni("Bindings", iniControllerActions[i], value, 128); } #ifdef DETECT_JOYSTICK_MENU #ifdef XINPUT StoreIni("Controller", "JoystickName", CPad::XInputJoy1); #else StoreIni("Controller", "JoystickName", gSelectedJoystickName, 128); #endif #endif StoreIni("Controller", "PadButtonsInited", ControlsManager.ms_padButtonsInited); cfg.write_file("reVC.ini"); } bool LoadINISettings() { if (!cfg.load_file("reVC.ini")) return false; #ifdef IMPROVED_VIDEOMODE ReadIniIfExists("VideoMode", "Width", &FrontEndMenuManager.m_nPrefsWidth); ReadIniIfExists("VideoMode", "Height", &FrontEndMenuManager.m_nPrefsHeight); ReadIniIfExists("VideoMode", "Depth", &FrontEndMenuManager.m_nPrefsDepth); ReadIniIfExists("VideoMode", "Subsystem", &FrontEndMenuManager.m_nPrefsSubsystem); // Windowed mode is loaded below in CUSTOM_FRONTEND_OPTIONS section #else ReadIniIfExists("Graphics", "VideoMode", &FrontEndMenuManager.m_nDisplayVideoMode); #endif ReadIniIfExists("Controller", "HeadBob1stPerson", &TheCamera.m_bHeadBob); ReadIniIfExists("Controller", "HorizantalMouseSens", &TheCamera.m_fMouseAccelHorzntl); ReadIniIfExists("Controller", "InvertMouseVertically", &MousePointerStateHelper.bInvertVertically); ReadIniIfExists("Controller", "DisableMouseSteering", &CVehicle::m_bDisableMouseSteering); ReadIniIfExists("Controller", "Vibration", &FrontEndMenuManager.m_PrefsUseVibration); ReadIniIfExists("Audio", "SfxVolume", &FrontEndMenuManager.m_PrefsSfxVolume); ReadIniIfExists("Audio", "MusicVolume", &FrontEndMenuManager.m_PrefsMusicVolume); ReadIniIfExists("Audio", "MP3BoostVolume", &FrontEndMenuManager.m_PrefsMP3BoostVolume); ReadIniIfExists("Audio", "Radio", &FrontEndMenuManager.m_PrefsRadioStation); ReadIniIfExists("Audio", "SpeakerType", &FrontEndMenuManager.m_PrefsSpeakers); ReadIniIfExists("Audio", "Provider", &FrontEndMenuManager.m_nPrefsAudio3DProviderIndex); ReadIniIfExists("Audio", "DynamicAcoustics", &FrontEndMenuManager.m_PrefsDMA); ReadIniIfExists("Display", "Brightness", &FrontEndMenuManager.m_PrefsBrightness); ReadIniIfExists("Display", "DrawDistance", &FrontEndMenuManager.m_PrefsLOD); ReadIniIfExists("Display", "Subtitles", &FrontEndMenuManager.m_PrefsShowSubtitles); ReadIniIfExists("Graphics", "AspectRatio", &FrontEndMenuManager.m_PrefsUseWideScreen); ReadIniIfExists("Graphics", "FrameLimiter", &FrontEndMenuManager.m_PrefsFrameLimiter); #ifdef LEGACY_MENU_OPTIONS ReadIniIfExists("Graphics", "VSync", &FrontEndMenuManager.m_PrefsVsyncDisp); ReadIniIfExists("Graphics", "Trails", &CMBlur::BlurOn); #endif ReadIniIfExists("General", "SkinFile", FrontEndMenuManager.m_PrefsSkinFile, 256); ReadIniIfExists("Controller", "Method", &FrontEndMenuManager.m_ControlMethod); ReadIniIfExists("General", "Language", &FrontEndMenuManager.m_PrefsLanguage); ReadIniIfExists("Display", "ShowHud", &FrontEndMenuManager.m_PrefsShowHud); ReadIniIfExists("Display", "RadarMode", &FrontEndMenuManager.m_PrefsRadarMode); ReadIniIfExists("Display", "ShowLegends", &FrontEndMenuManager.m_PrefsShowLegends); #ifdef EXTENDED_COLOURFILTER ReadIniIfExists("CustomPipesValues", "PostFXIntensity", &CPostFX::Intensity); #endif #ifdef EXTENDED_PIPELINES ReadIniIfExists("CustomPipesValues", "NeoVehicleShininess", &CustomPipes::VehicleShininess); ReadIniIfExists("CustomPipesValues", "NeoVehicleSpecularity", &CustomPipes::VehicleSpecularity); ReadIniIfExists("CustomPipesValues", "RimlightMult", &CustomPipes::RimlightMult); ReadIniIfExists("CustomPipesValues", "LightmapMult", &CustomPipes::LightmapMult); ReadIniIfExists("CustomPipesValues", "GlossMult", &CustomPipes::GlossMult); #endif ReadIniIfExists("Rendering", "BackfaceCulling", &gBackfaceCulling); #ifdef NEW_RENDERER ReadIniIfExists("Rendering", "NewRenderer", &gbNewRenderer); #endif #ifdef PROPER_SCALING ReadIniIfExists("Draw", "ProperScaling", &CDraw::ms_bProperScaling); #endif #ifdef FIX_RADAR ReadIniIfExists("Draw", "FixRadar", &CDraw::ms_bFixRadar); #endif #ifdef FIX_SPRITES ReadIniIfExists("Draw", "FixSprites", &CDraw::ms_bFixSprites); #endif #ifdef DRAW_GAME_VERSION_TEXT ReadIniIfExists("General", "DrawVersionText", &gbDrawVersionText); #endif #ifdef NO_MOVIES ReadIniIfExists("General", "NoMovies", &gbNoMovies); #endif #ifdef CUSTOM_FRONTEND_OPTIONS bool migrate = cfg.category_size("FrontendOptions") != 0; for (int i = 0; i < MENUPAGES; i++) { for (int j = 0; j < NUM_MENUROWS; j++) { CMenuScreenCustom::CMenuEntry &option = aScreens[i].m_aEntries[j]; if (option.m_Action == MENUACTION_NOTHING) break; // CFO check if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) { // CFO only supports saving uint8 right now // Migrate from old .ini to new .ini if (migrate && ReadIniIfExists("FrontendOptions", option.m_CFO->save, option.m_CFO->value)) cfg.remove("FrontendOptions", option.m_CFO->save); else ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, option.m_CFO->value); if (option.m_Action == MENUACTION_CFO_SELECT) { option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue = *option.m_CFO->value; } } } } #endif return true; } void SaveINISettings() { #ifdef IMPROVED_VIDEOMODE StoreIni("VideoMode", "Width", FrontEndMenuManager.m_nPrefsWidth); StoreIni("VideoMode", "Height", FrontEndMenuManager.m_nPrefsHeight); StoreIni("VideoMode", "Depth", FrontEndMenuManager.m_nPrefsDepth); StoreIni("VideoMode", "Subsystem", FrontEndMenuManager.m_nPrefsSubsystem); // Windowed mode is loaded below in CUSTOM_FRONTEND_OPTIONS section #else StoreIni("Graphics", "VideoMode", FrontEndMenuManager.m_nDisplayVideoMode); #endif StoreIni("Controller", "HeadBob1stPerson", TheCamera.m_bHeadBob); StoreIni("Controller", "HorizantalMouseSens", TheCamera.m_fMouseAccelHorzntl); StoreIni("Controller", "InvertMouseVertically", MousePointerStateHelper.bInvertVertically); StoreIni("Controller", "DisableMouseSteering", CVehicle::m_bDisableMouseSteering); StoreIni("Controller", "Vibration", FrontEndMenuManager.m_PrefsUseVibration); StoreIni("Audio", "SfxVolume", FrontEndMenuManager.m_PrefsSfxVolume); StoreIni("Audio", "MusicVolume", FrontEndMenuManager.m_PrefsMusicVolume); StoreIni("Audio", "MP3BoostVolume", FrontEndMenuManager.m_PrefsMP3BoostVolume); StoreIni("Audio", "Radio", FrontEndMenuManager.m_PrefsRadioStation); StoreIni("Audio", "SpeakerType", FrontEndMenuManager.m_PrefsSpeakers); StoreIni("Audio", "Provider", FrontEndMenuManager.m_nPrefsAudio3DProviderIndex); StoreIni("Audio", "DynamicAcoustics", FrontEndMenuManager.m_PrefsDMA); StoreIni("Display", "Brightness", FrontEndMenuManager.m_PrefsBrightness); StoreIni("Display", "DrawDistance", FrontEndMenuManager.m_PrefsLOD); StoreIni("Display", "Subtitles", FrontEndMenuManager.m_PrefsShowSubtitles); StoreIni("Graphics", "AspectRatio", FrontEndMenuManager.m_PrefsUseWideScreen); #ifdef LEGACY_MENU_OPTIONS StoreIni("Graphics", "VSync", FrontEndMenuManager.m_PrefsVsyncDisp); StoreIni("Graphics", "Trails", CMBlur::BlurOn); #endif StoreIni("Graphics", "FrameLimiter", FrontEndMenuManager.m_PrefsFrameLimiter); StoreIni("General", "SkinFile", FrontEndMenuManager.m_PrefsSkinFile, 256); StoreIni("Controller", "Method", FrontEndMenuManager.m_ControlMethod); StoreIni("General", "Language", FrontEndMenuManager.m_PrefsLanguage); StoreIni("Display", "ShowHud", FrontEndMenuManager.m_PrefsShowHud); StoreIni("Display", "RadarMode", FrontEndMenuManager.m_PrefsRadarMode); StoreIni("Display", "ShowLegends", FrontEndMenuManager.m_PrefsShowLegends); #ifdef EXTENDED_COLOURFILTER StoreIni("CustomPipesValues", "PostFXIntensity", CPostFX::Intensity); #endif #ifdef EXTENDED_PIPELINES StoreIni("CustomPipesValues", "NeoVehicleShininess", CustomPipes::VehicleShininess); StoreIni("CustomPipesValues", "NeoVehicleSpecularity", CustomPipes::VehicleSpecularity); StoreIni("CustomPipesValues", "RimlightMult", CustomPipes::RimlightMult); StoreIni("CustomPipesValues", "LightmapMult", CustomPipes::LightmapMult); StoreIni("CustomPipesValues", "GlossMult", CustomPipes::GlossMult); #endif StoreIni("Rendering", "BackfaceCulling", gBackfaceCulling); #ifdef NEW_RENDERER StoreIni("Rendering", "NewRenderer", gbNewRenderer); #endif #ifdef PROPER_SCALING StoreIni("Draw", "ProperScaling", CDraw::ms_bProperScaling); #endif #ifdef FIX_RADAR StoreIni("Draw", "FixRadar", CDraw::ms_bFixRadar); #endif #ifdef FIX_SPRITES StoreIni("Draw", "FixSprites", CDraw::ms_bFixSprites); #endif #ifdef DRAW_GAME_VERSION_TEXT StoreIni("General", "DrawVersionText", gbDrawVersionText); #endif #ifdef NO_MOVIES StoreIni("General", "NoMovies", gbNoMovies); #endif #ifdef CUSTOM_FRONTEND_OPTIONS for (int i = 0; i < MENUPAGES; i++) { for (int j = 0; j < NUM_MENUROWS; j++) { CMenuScreenCustom::CMenuEntry &option = aScreens[i].m_aEntries[j]; if (option.m_Action == MENUACTION_NOTHING) break; if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) { // Beware: CFO only supports saving uint8 right now StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *option.m_CFO->value); } } } #endif cfg.write_file("reVC.ini"); } #endif #ifdef DEBUGMENU void WeaponCheat1(); void WeaponCheat2(); void WeaponCheat3(); void HealthCheat(); void VehicleCheat(int model); 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 SpecialCarCheats(); void PickUpChicksCheat(); 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; if(!CModelInfo::IsBoatModel(id)){ node = ThePaths.FindNodeClosestToCoors(playerpos, 0, 100.0f, false, false); if(node < 0) return; } CVehicle *v; if(CModelInfo::IsBoatModel(id)) v = new CBoat(id, RANDOM_VEHICLE); else if(CModelInfo::IsBikeModel(id)) v = new CBike(id, RANDOM_VEHICLE); else v = new CAutomobile(id, RANDOM_VEHICLE); v->bHasBeenOwnedByPlayer = true; if(carCol1) DebugMenuEntrySetAddress(carCol1, &v->m_currentColour1); if(carCol2) DebugMenuEntrySetAddress(carCol2, &v->m_currentColour2); if(CModelInfo::IsBoatModel(id)) v->SetPosition(TheCamera.GetPosition() + TheCamera.GetForward()*15.0f); else v->SetPosition(ThePaths.m_pathNodes[node].GetPosition()); v->GetMatrix().GetPosition().z += 4.0f; v->SetOrientation(0.0f, 0.0f, 3.49f); v->SetStatus(STATUS_ABANDONED); v->m_nDoorLock = CARLOCK_UNLOCKED; CWorld::Add(v); } } static void FixCar(void) { CVehicle *veh = FindPlayerVehicle(); if(veh == nil) return; veh->m_fHealth = 1000.0f; if(veh->IsCar()){ ((CAutomobile*)veh)->Damage.SetEngineStatus(0); ((CAutomobile*)veh)->Fix(); }else if(veh->IsBike()){ ((CBike*)veh)->Fix(); } } #ifdef MAP_ENHANCEMENTS static void TeleportToWaypoint(void) { if (FindPlayerVehicle()) { if (CRadar::TargetMarkerId != -1) FindPlayerVehicle()->Teleport(CRadar::TargetMarkerPos + CVector(0.0f, 0.0f, FindPlayerVehicle()->GetColModel()->boundingSphere.center.z)); } else if(CRadar::TargetMarkerId != -1) FindPlayerPed()->Teleport(CRadar::TargetMarkerPos + CVector(0.0f, 0.0f, FEET_OFFSET)); } #endif static void SwitchCarCollision(void) { if (FindPlayerVehicle() && FindPlayerVehicle()->IsCar()) FindPlayerVehicle()->bUsesCollision = !FindPlayerVehicle()->bUsesCollision; } 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 void ResetCamStatics(void) { TheCamera.Cams[TheCamera.ActiveCam].ResetStatics = true; } #ifdef MISSION_SWITCHER int8 nextMissionToSwitch = 0; static void SwitchToMission(void) { CTheScripts::SwitchToMission(nextMissionToSwitch); } #endif static const char *carnames[] = { "landstal", "idaho", "stinger", "linerun", "peren", "sentinel", "rio", "firetruk", "trash", "stretch", "manana", "infernus", "voodoo", "pony", "mule", "cheetah", "ambulan", "fbicar", "moonbeam", "esperant", "taxi", "washing", "bobcat", "mrwhoop", "bfinject", "hunter", "police", "enforcer", "securica", "banshee", "predator", "bus", "rhino", "barracks", "cuban", "chopper", "angel", "coach", "cabbie", "stallion", "rumpo", "rcbandit", "romero", "packer", "sentxs", "admiral", "squalo", "seaspar", "pizzaboy", "gangbur", "airtrain", "deaddodo", "speeder", "reefer", "tropic", "flatbed", "yankee", "caddy", "zebra", "topfun", "skimmer", "pcj600", "faggio", "freeway", "rcbaron", "rcraider", "glendale", "oceanic", "sanchez", "sparrow", "patriot", "lovefist", "coastg", "dinghy", "hermes", "sabre", "sabretur", "pheonix", "walton", "regina", "comet", "deluxo", "burrito", "spand", "marquis", "baggage", "kaufman", "maverick", "vcnmav", "rancher", "fbiranch", "virgo", "greenwoo", "jetmax", "hotring", "sandking", "blistac", "polmav", "boxville", "benson", "mesa", "rcgoblin", "hotrina", "hotrinb", "bloodra", "bloodrb", "vicechee" }; static CTweakVar** TweakVarsList; static int TweakVarsListSize = -1; static bool bAddTweakVarsNow = false; static const char *pTweakVarsDefaultPath = NULL; void CTweakVars::Add(CTweakVar *var) { if(TweakVarsListSize == -1) { TweakVarsList = (CTweakVar**)malloc(64 * sizeof(CTweakVar*)); TweakVarsListSize = 0; } if(TweakVarsListSize > 63) TweakVarsList = (CTweakVar**) realloc(TweakVarsList, (TweakVarsListSize + 1) * sizeof(CTweakVar*)); TweakVarsList[TweakVarsListSize++] = var; // TweakVarsList.push_back(var); if ( bAddTweakVarsNow ) var->AddDBG(pTweakVarsDefaultPath); } void CTweakVars::AddDBG(const char *path) { pTweakVarsDefaultPath = path; for(int i = 0; i < TweakVarsListSize; ++i) TweakVarsList[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 switchWeather(void) { CWeather::StreamAfterRainTimer = 0; } void DebugMenuPopulate(void) { if(1){ static const char *weathers[] = { "Sunny", "Cloudy", "Rainy", "Foggy", "Extrasunny", "Stormy" }; static const char *extracols[] = { "1 - Malibu club", "2 - Strib club", "3 - Hotel", "4 - Bank", "5 - Police HQ", "6 - Mall", "7 - Rifle Range", "8 - Mansion", "9 - Dirt ring", "10 - Blood ring", "11 - Hot ring", "12 - Concert hall", "13 - Auntie Poulets", "14 - Intro at docks", "15 - Biker bar", "16 - Intro cafe", "17 - Studio", "18", "19", "20", "21", "22", "23", "24" }; 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, switchWeather, 1, 0, 5, weathers); DebugMenuEntrySetWrap(e, true); e = DebugMenuAddVar("Time & Weather", "New Weather", (int16*)&CWeather::NewWeatherType, switchWeather, 1, 0, 5, weathers); DebugMenuEntrySetWrap(e, true); DebugMenuAddVarBool32("Time & Weather", "Extracolours On", &CTimeCycle::m_bExtraColourOn, nil); DebugMenuAddVar("Time & Weather", "Extracolour", &CTimeCycle::m_ExtraColour, nil, 1, 0, 23, extracols); DebugMenuAddVar("Time & Weather", "Time scale", (float*)&CTimer::GetTimeScale(), nil, 0.1f, 0.0f, 10.0f); DebugMenuAddCmd("Cheats", "Weapon set 1", WeaponCheat1); DebugMenuAddCmd("Cheats", "Weapon set 2", WeaponCheat2); DebugMenuAddCmd("Cheats", "Weapon set 3", WeaponCheat3); DebugMenuAddCmd("Cheats", "Money", MoneyCheat); DebugMenuAddCmd("Cheats", "Health", HealthCheat); DebugMenuAddCmd("Cheats", "Wanted level up", WantedLevelUpCheat); DebugMenuAddCmd("Cheats", "Wanted level down", WantedLevelDownCheat); DebugMenuAddCmd("Cheats", "Tank", []() { VehicleCheat(MI_TAXI); }); 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", "Special car", SpecialCarCheats); DebugMenuAddCmd("Cheats", "Pickup chicks", PickUpChicksCheat); static int spawnCarId = MI_LANDSTAL; e = DebugMenuAddVar("Spawn", "Spawn Car ID", &spawnCarId, nil, 1, MI_LANDSTAL, MI_VICECHEE, carnames); DebugMenuEntrySetWrap(e, true); DebugMenuAddCmd("Spawn", "Spawn Car", [](){ if(spawnCarId == MI_CHOPPER || spawnCarId == MI_AIRTRAIN || spawnCarId == MI_DEADDODO) 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 Phoenix", [](){ SpawnCar(MI_PHEONIX); }); DebugMenuAddCmd("Spawn", "Spawn Banshee", [](){ SpawnCar(MI_BANSHEE); }); DebugMenuAddCmd("Spawn", "Spawn Esperanto", [](){ SpawnCar(MI_ESPERANT); }); DebugMenuAddCmd("Spawn", "Spawn Stallion", [](){ SpawnCar(MI_STALLION); }); DebugMenuAddCmd("Spawn", "Spawn Admiral", [](){ SpawnCar(MI_ADMIRAL); }); DebugMenuAddCmd("Spawn", "Spawn Washington", [](){ SpawnCar(MI_WASHING); }); DebugMenuAddCmd("Spawn", "Spawn Taxi", [](){ SpawnCar(MI_TAXI); }); DebugMenuAddCmd("Spawn", "Spawn Police", [](){ SpawnCar(MI_POLICE); }); DebugMenuAddCmd("Spawn", "Spawn Enforcer", [](){ SpawnCar(MI_ENFORCER); }); DebugMenuAddCmd("Spawn", "Spawn Cuban", [](){ SpawnCar(MI_CUBAN); }); DebugMenuAddCmd("Spawn", "Spawn Voodoo", [](){ SpawnCar(MI_VOODOO); }); DebugMenuAddCmd("Spawn", "Spawn BF injection", [](){ SpawnCar(MI_BFINJECT); }); DebugMenuAddCmd("Spawn", "Spawn Maverick", [](){ SpawnCar(MI_MAVERICK); }); DebugMenuAddCmd("Spawn", "Spawn VCN Maverick", [](){ SpawnCar(MI_VCNMAV); }); DebugMenuAddCmd("Spawn", "Spawn Sparrow", [](){ SpawnCar(MI_SPARROW); }); DebugMenuAddCmd("Spawn", "Spawn Sea Sparrow", [](){ SpawnCar(MI_SEASPAR); }); DebugMenuAddCmd("Spawn", "Spawn Hunter", [](){ SpawnCar(MI_HUNTER); }); DebugMenuAddCmd("Spawn", "Spawn Rhino", [](){ SpawnCar(MI_RHINO); }); DebugMenuAddCmd("Spawn", "Spawn Firetruck", [](){ SpawnCar(MI_FIRETRUCK); }); DebugMenuAddCmd("Spawn", "Spawn Predator", [](){ SpawnCar(MI_PREDATOR); }); DebugMenuAddCmd("Spawn", "Spawn PCJ 600", [](){ SpawnCar(MI_PCJ600); }); DebugMenuAddCmd("Spawn", "Spawn Faggio", [](){ SpawnCar(MI_FAGGIO); }); DebugMenuAddCmd("Spawn", "Spawn Freeway", [](){ SpawnCar(MI_FREEWAY); }); DebugMenuAddCmd("Spawn", "Spawn Squalo", [](){ SpawnCar(MI_SQUALO); }); DebugMenuAddCmd("Spawn", "Spawn Skimmer", [](){ SpawnCar(MI_SKIMMER); }); DebugMenuAddVarBool8("Render", "Draw hud", &CHud::m_Wants_To_Draw_Hud, nil); #ifdef PROPER_SCALING DebugMenuAddVarBool8("Render", "Proper Scaling", &CDraw::ms_bProperScaling, nil); #endif #ifdef FIX_RADAR DebugMenuAddVarBool8("Render", "Fix Radar", &CDraw::ms_bFixRadar, nil); #endif #ifdef FIX_SPRITES DebugMenuAddVarBool8("Render", "Fix Sprites", &CDraw::ms_bFixSprites, nil); #endif DebugMenuAddVarBool8("Render", "Backface Culling", &gBackfaceCulling, nil); DebugMenuAddVarBool8("Render", "PS2 Alpha test Emu", &gPS2alphaTest, nil); DebugMenuAddVarBool8("Render", "Frame limiter", &FrontEndMenuManager.m_PrefsFrameLimiter, nil); DebugMenuAddVarBool8("Render", "VSynch", &FrontEndMenuManager.m_PrefsVsync, nil); DebugMenuAddVar("Render", "Max FPS", &RsGlobal.maxFPS, nil, 1, 1, 1000, nil); #ifdef NEW_RENDERER DebugMenuAddVarBool8("Render", "New Renderer", &gbNewRenderer, nil); extern bool gbRenderRoads; extern bool gbRenderEverythingBarRoads; extern bool gbRenderFadingInUnderwaterEntities; extern bool gbRenderFadingInEntities; extern bool gbRenderWater; extern bool gbRenderBoats; extern bool gbRenderVehicles; extern bool gbRenderWorld0; extern bool gbRenderWorld1; extern bool gbRenderWorld2; DebugMenuAddVarBool8("Debug Render", "gbRenderRoads", &gbRenderRoads, nil); DebugMenuAddVarBool8("Debug Render", "gbRenderEverythingBarRoads", &gbRenderEverythingBarRoads, nil); DebugMenuAddVarBool8("Debug Render", "gbRenderFadingInUnderwaterEntities", &gbRenderFadingInUnderwaterEntities, nil); DebugMenuAddVarBool8("Debug Render", "gbRenderFadingInEntities", &gbRenderFadingInEntities, nil); DebugMenuAddVarBool8("Debug Render", "gbRenderWater", &gbRenderWater, nil); DebugMenuAddVarBool8("Debug Render", "gbRenderBoats", &gbRenderBoats, nil); DebugMenuAddVarBool8("Debug Render", "gbRenderVehicles", &gbRenderVehicles, nil); DebugMenuAddVarBool8("Debug Render", "gbRenderWorld0", &gbRenderWorld0, nil); DebugMenuAddVarBool8("Debug Render", "gbRenderWorld1", &gbRenderWorld1, nil); DebugMenuAddVarBool8("Debug Render", "gbRenderWorld2", &gbRenderWorld2, nil); #endif #ifdef EXTENDED_COLOURFILTER static const char *filternames[] = { "None", "Simple", "Normal", "Mobile" }; e = DebugMenuAddVar("Render", "Colourfilter", &CPostFX::EffectSwitch, nil, 1, CPostFX::POSTFX_OFF, CPostFX::POSTFX_MOBILE, filternames); DebugMenuEntrySetWrap(e, true); DebugMenuAddVar("Render", "Intensity", &CPostFX::Intensity, nil, 0.05f, 0, 10.0f); DebugMenuAddVarBool8("Render", "Blur", &CPostFX::BlurOn, nil); DebugMenuAddVarBool8("Render", "Motion Blur", &CPostFX::MotionBlurOn, nil); #endif DebugMenuAddVar("Render", "Drunkness", &CMBlur::Drunkness, nil, 0.05f, 0, 1.0f); #ifndef MASTER DebugMenuAddVarBool8("Render", "Occlusion debug", &bDispayOccDebugStuff, nil); #endif #ifdef EXTENDED_PIPELINES static const char *vehpipenames[] = { "MatFX", "Neo" }; e = DebugMenuAddVar("Render", "Vehicle Pipeline", &CustomPipes::VehiclePipeSwitch, nil, 1, CustomPipes::VEHICLEPIPE_MATFX, CustomPipes::VEHICLEPIPE_NEO, vehpipenames); DebugMenuEntrySetWrap(e, true); DebugMenuAddVar("Render", "Neo Vehicle Shininess", &CustomPipes::VehicleShininess, nil, 0.1f, 0, 1.0f); DebugMenuAddVar("Render", "Neo Vehicle Specularity", &CustomPipes::VehicleSpecularity, nil, 0.1f, 0, 1.0f); DebugMenuAddVarBool8("Render", "Neo Ped Rim light enable", &CustomPipes::RimlightEnable, nil); DebugMenuAddVar("Render", "Mult", &CustomPipes::RimlightMult, nil, 0.1f, 0, 1.0f); DebugMenuAddVarBool8("Render", "Neo World Lightmaps enable", &CustomPipes::LightmapEnable, nil); DebugMenuAddVar("Render", "Mult", &CustomPipes::LightmapMult, nil, 0.1f, 0, 1.0f); DebugMenuAddVarBool8("Render", "Neo Road Gloss enable", &CustomPipes::GlossEnable, nil); DebugMenuAddVar("Render", "Mult", &CustomPipes::GlossMult, nil, 0.1f, 0, 1.0f); #endif DebugMenuAddVarBool8("Debug Render", "Show Ped Paths", &gbShowPedPaths, nil); DebugMenuAddVarBool8("Debug Render", "Show Car Paths", &gbShowCarPaths, nil); DebugMenuAddVarBool8("Debug Render", "Show Car Path Links", &gbShowCarPathsLinks, nil); DebugMenuAddVarBool8("Debug Render", "Show Collision Lines", &gbShowCollisionLines, nil); DebugMenuAddVarBool8("Debug Render", "Show Collision Polys", &gbShowCollisionPolys, nil); DebugMenuAddVarBool8("Debug Render", "Don't render Buildings", &gbDontRenderBuildings, nil); DebugMenuAddVarBool8("Debug Render", "Don't render Big Buildings", &gbDontRenderBigBuildings, nil); DebugMenuAddVarBool8("Debug Render", "Don't render Peds", &gbDontRenderPeds, nil); DebugMenuAddVarBool8("Debug Render", "Don't render Vehicles", &gbDontRenderVehicles, nil); DebugMenuAddVarBool8("Debug Render", "Don't render Objects", &gbDontRenderObjects, nil); DebugMenuAddVarBool8("Debug Render", "Don't Render Water", &gbDontRenderWater, nil); #ifdef DRAW_GAME_VERSION_TEXT DebugMenuAddVarBool8("Debug", "Version Text", &gbDrawVersionText, nil); #endif DebugMenuAddVarBool8("Debug", "Show DebugStuffInRelease", &gbDebugStuffInRelease, nil); #ifdef TIMEBARS DebugMenuAddVarBool8("Debug", "Show Timebars", &gbShowTimebars, nil); #endif #ifndef FINAL DebugMenuAddVarBool8("Debug", "Use debug render groups", &bDebugRenderGroups, nil); DebugMenuAddVarBool8("Debug", "Print Memory Usage", &gbPrintMemoryUsage, nil); #ifdef USE_CUSTOM_ALLOCATOR DebugMenuAddCmd("Debug", "Parse Heap", ParseHeap); #endif #endif DebugMenuAddVarBool8("Debug", "pad 1 -> pad 2", &CPad::m_bMapPadOneToPadTwo, nil); #ifdef GTA_SCENE_EDIT DebugMenuAddVarBool8("Debug", "Edit on", &CSceneEdit::m_bEditOn, nil); #endif //DebugMenuAddCmd("Debug", "Start Credits", CCredits::Start); //DebugMenuAddCmd("Debug", "Stop Credits", CCredits::Stop); #ifdef RELOADABLES // maybe put it back if we have more to reload // DebugMenuAddCmd("Reload", "HUD.TXD", CHud::ReloadTXD); #endif #ifdef MAP_ENHANCEMENTS DebugMenuAddCmd("Game", "Teleport to map waypoint", TeleportToWaypoint); #endif DebugMenuAddCmd("Game", "Fix Car", FixCar); DebugMenuAddCmd("Game", "Place Car on Road", PlaceOnRoad); DebugMenuAddCmd("Game", "Switch car collision", SwitchCarCollision); DebugMenuAddCmd("Game", "Toggle Comedy Controls", ToggleComedy); #ifdef MISSION_SWITCHER DebugMenuEntry *missionEntry; static const char* missions[] = { "Initial", "Intro", "An Old Friend", "The Party", "Back Alley Brawl", "Jury Fury", "Riot", "Treacherous Swine", "Mall Shootout", "Guardian Angels", "Sir, Yes Sir!", "All Hands On Deck!", "The Chase", "Phnom Penh '86", "The Fastest Boat", "Supply & Demand", "Rub Out", "Death Row", "Four Iron", "Demolition Man", "Two Bit Hit", "No Escape?", "The Shootist", "The Driver", "The Job", "Gun Runner", "Boomshine Saigon", "Recruitment Drive", "Dildo Dodo", "Martha's Mug Shot", "G-spotlight", "Shakedown", "Bar Brawl", "Cop Land", "Spilling the Beans", "Hit the Courier", "Printworks Buy", "Sunshine Autos", "Interglobal Films Buy", "Cherry Popper Icecreams Buy", "Kaufman Cabs Buy", "Malibu Club Buy", "The Boatyard Buy", "Pole Position Club Buy", "El Swanko Casa Buy", "Links View Apartment Buy", "Hyman Condo Buy", "Ocean Heighs Aprt. Buy", "1102 Washington Street Buy", "Vice Point Buy", "Skumole Shack Buy", "Cap the Collector", "Keep your Friends Close...", "Alloy Wheels of Steel", "Messing with the Man", "Hog Tied", "Stunt Boat Challenge", "Cannon Fodder", "Naval Engagement", "Trojan Voodoo", "Juju Scramble", "Bombs Away!", "Dirty Lickin's", "Love Juice", "Psycho Killer", "Publicity Tour", "Weapon Range", "Road Kill", "Waste the Wife", "Autocide", "Check Out at the Check In", "Loose Ends", "V.I.P.", "Friendly Rivalry", "Cabmaggedon", "TAXI DRIVER", "PARAMEDIC", "FIREFIGHTER", "VIGILANTE", "HOTRING", "BLOODRING", "DIRTRING", "Sunshine Autos Races", "Distribution", "Downtown Chopper Checkpoint", "Ocean Beach Chopper Checkpoint", "Vice Point Chopper Checkpoint", "Little Haiti Chopper Checkpoint", "Trial by Dirt", "Test Track", "PCJ Playground", "Cone Crazy", "PIZZA BOY", "RC Raider Pickup", "RC Bandit Race", "RC Baron Race", "Checkpoint Charlie" }; missionEntry = DebugMenuAddVar("Game", "Select mission", &nextMissionToSwitch, nil, 1, 0, ARRAY_SIZE(missions) - 1, missions); DebugMenuEntrySetWrap(missionEntry, true); DebugMenuAddCmd("Game", "Start selected mission ", SwitchToMission); #endif extern bool PrintDebugCode; extern int16 DebugCamMode; DebugMenuAddVarBool8("Cam", "Use mouse Cam", &CCamera::m_bUseMouse3rdPerson, nil); #ifdef FREE_CAM DebugMenuAddVarBool8("Cam", "Free Cam", &CCamera::bFreeCam, nil); #endif DebugMenuAddVarBool8("Cam", "Print Debug Code", &PrintDebugCode, nil); DebugMenuAddVar("Cam", "Cam Mode", &DebugCamMode, nil, 1, 0, CCam::MODE_EDITOR, nil); DebugMenuAddCmd("Cam", "Normal", []() { DebugCamMode = 0; }); DebugMenuAddCmd("Cam", "Reset Statics", ResetCamStatics); CTweakVars::AddDBG("Debug"); } } #endif #ifndef __MWERKS__ #ifndef MASTER const int re3_buffsize = 1024; static char re3_buff[re3_buffsize]; #endif #ifndef MASTER void re3_assert(const char *expr, const char *filename, unsigned int lineno, const char *func) { #ifdef _WIN32 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, "REVC 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(); #else // TODO printf("\nREVC ASSERT FAILED\n\tFile: %s\n\tLine: %d\n\tFunction: %s\n\tExpression: %s\n",filename,lineno,func,expr); assert(false); #endif } #endif void re3_debug(const char *format, ...) { #ifndef MASTER va_list va; va_start(va, format); #ifdef _WIN32 vsprintf_s(re3_buff, re3_buffsize, format, va); #else vsprintf(re3_buff, format, va); #endif va_end(va); printf("%s", re3_buff); CDebug::DebugAddText(re3_buff); #endif } #ifndef MASTER void re3_trace(const char *filename, unsigned int lineno, const char *func, const char *format, ...) { char buff[re3_buffsize *2]; va_list va; va_start(va, format); #ifdef _WIN32 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); #else vsprintf(re3_buff, format, va); va_end(va); sprintf(buff, "[%s.%s:%d]: %s", filename, func, lineno, re3_buff); #endif OutputDebugString(buff); } void re3_usererror(const char *format, ...) { va_list va; va_start(va, format); #ifdef _WIN32 vsprintf_s(re3_buff, re3_buffsize, format, va); va_end(va); ::MessageBoxA(nil, re3_buff, "REVC Error!", MB_OK|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL); raise(SIGABRT); _exit(3); #else vsprintf(re3_buff, format, va); printf("\nREVC Error!\n\t%s\n",re3_buff); assert(false); #endif } #endif #endif #ifdef VALIDATE_SAVE_SIZE int32 _saveBufCount; #endif ================================================ FILE: src/core/templates.h ================================================ #pragma once template class CStore { public: int32 allocPtr; T store[n]; T *Alloc(void){ if(allocPtr >= n){ printf("Size of this thing:%d needs increasing\n", n); assert(0); } return &store[allocPtr++]; } void Clear(void){ allocPtr = 0; } int32 GetIndex(T *item){ assert(item >= &store[0]); assert(item < &store[n]); return item - store; } T *GetItem(int32 index){ assert(index >= 0); assert(index < n); return &store[index]; } }; #define POOLFLAG_ID 0x7f #define POOLFLAG_ISFREE 0x80 template class CPool { U *m_entries; uint8 *m_flags; int32 m_size; int32 m_allocPtr; public: CPool(int32 size, const char *name){ m_entries = (U*)new uint8[sizeof(U)*size]; m_flags = new uint8[size]; m_size = size; m_allocPtr = -1; for(int i = 0; i < size; i++){ SetId(i, 0); SetIsFree(i, true); } } int GetId(int i) const { return m_flags[i] & POOLFLAG_ID; } bool GetIsFree(int i) const { return !!(m_flags[i] & POOLFLAG_ISFREE); } void SetId(int i, int id) { m_flags[i] = (m_flags[i] & POOLFLAG_ISFREE) | (id & POOLFLAG_ID); } void SetIsFree(int i, bool isFree) { if (isFree) m_flags[i] |= POOLFLAG_ISFREE; else m_flags[i] &= ~POOLFLAG_ISFREE; } ~CPool() { Flush(); } void Flush() { if (m_size > 0) { delete[] (uint8*)m_entries; delete[] m_flags; m_entries = nil; m_flags = nil; m_size = 0; m_allocPtr = 0; } } int32 GetSize(void) const { return m_size; } T *New(void){ bool wrapped = false; do #ifdef FIX_BUGS if (++m_allocPtr >= m_size) { m_allocPtr = 0; if (wrapped) return nil; wrapped = true; } #else if(++m_allocPtr == m_size){ if(wrapped) return nil; wrapped = true; m_allocPtr = 0; } #endif while(!GetIsFree(m_allocPtr)); SetIsFree(m_allocPtr, false); SetId(m_allocPtr, GetId(m_allocPtr)+1); return (T*)&m_entries[m_allocPtr]; } T *New(int32 handle){ T *entry = (T*)&m_entries[handle>>8]; SetNotFreeAt(handle); return entry; } void SetNotFreeAt(int32 handle){ int idx = handle>>8; SetIsFree(idx, false); SetId(idx, handle & POOLFLAG_ID); for(m_allocPtr = 0; m_allocPtr < m_size; m_allocPtr++) if(GetIsFree(m_allocPtr)) return; } void Delete(T *entry){ int i = GetJustIndex(entry); SetIsFree(i, true); if(i < m_allocPtr) m_allocPtr = i; } T *GetSlot(int i){ return GetIsFree(i) ? nil : (T*)&m_entries[i]; } T *GetAt(int handle){ #ifdef FIX_BUGS if (handle == -1) return nil; #endif return m_flags[handle>>8] == (handle & 0xFF) ? (T*)&m_entries[handle >> 8] : nil; } int32 GetIndex(T* entry) { int i = GetJustIndex_NoFreeAssert(entry); return m_flags[i] + (i<<8); } int32 GetJustIndex(T* entry) { int index = GetJustIndex_NoFreeAssert(entry); assert((U*)entry == (U*)&m_entries[index]); // cast is unsafe - check required assert(!GetIsFree(index)); return index; } int32 GetJustIndex_NoFreeAssert(T* entry) { int index = ((U*)entry - m_entries); // Please don't add unsafe assert here, because at least one func. use this to check if entity is ped or vehicle. return index; } int32 GetNoOfUsedSpaces(void) const { int i; int n = 0; for(i = 0; i < m_size; i++) if(!GetIsFree(i)) n++; return n; } void ClearStorage(uint8 *&flags, U *&entries){ delete[] flags; delete[] (uint8*)entries; flags = nil; entries = nil; } uint32 GetMaxEntrySize() const { return sizeof(U); } void CopyBack(uint8 *&flags, U *&entries){ memcpy(m_flags, flags, sizeof(uint8)*m_size); memcpy(m_entries, entries, sizeof(U)*m_size); debug("Size copied:%d (%d)\n", sizeof(U)*m_size, m_size); m_allocPtr = 0; ClearStorage(flags, entries); debug("CopyBack:%d (/%d)\n", GetNoOfUsedSpaces(), m_size); /* Assumed inlining */ } void Store(uint8 *&flags, U *&entries){ flags = (uint8*)new uint8[sizeof(uint8)*m_size]; entries = (U*)new uint8[sizeof(U)*m_size]; memcpy(flags, m_flags, sizeof(uint8)*m_size); memcpy(entries, m_entries, sizeof(U)*m_size); debug("Stored:%d (/%d)\n", GetNoOfUsedSpaces(), m_size); /* Assumed inlining */ } int32 GetNoOfFreeSpaces() const { return GetSize() - GetNoOfUsedSpaces(); } }; template class CLink { public: T item; CLink *prev; CLink *next; void Insert(CLink *link){ link->next = this->next; this->next->prev = link; link->prev = this; this->next = link; } void Remove(void){ this->prev->next = this->next; this->next->prev = this->prev; } }; template class CLinkList { public: CLink head, tail; CLink freeHead, freeTail; CLink *links; void Init(int n){ links = new CLink[n]; head.next = &tail; tail.prev = &head; freeHead.next = &freeTail; freeTail.prev = &freeHead; while(n--) freeHead.Insert(&links[n]); } void Shutdown(void){ delete[] links; links = nil; } void Clear(void){ while(head.next != &tail) Remove(head.next); } CLink *Insert(T const &item){ CLink *node = freeHead.next; if(node == &freeTail) return nil; node->item = item; node->Remove(); // remove from free list head.Insert(node); return node; } CLink *InsertSorted(T const &item){ CLink *sort; for(sort = head.next; sort != &tail; sort = sort->next) if(sort->item.sort >= item.sort) break; CLink *node = freeHead.next; if(node == &freeTail) return nil; node->item = item; node->Remove(); // remove from free list sort->prev->Insert(node); return node; } void Remove(CLink *link){ link->Remove(); // remove from list freeHead.Insert(link); // insert into free list } int32 Count(void){ int n = 0; CLink *lnk; for(lnk = head.next; lnk != &tail; lnk = lnk->next) n++; return n; } }; ================================================ FILE: src/core/timebars.cpp ================================================ #include "common.h" #ifndef MASTER #include "Font.h" #include "Frontend.h" #include "Timer.h" #include "Text.h" #define MAX_TIMERS (50) #define MAX_MS_COLLECTED (40) // enables frame time output #define FRAMETIME struct sTimeBar { char name[20]; float startTime; float endTime; int32 unk; }; struct { sTimeBar Timers[MAX_TIMERS]; uint32 count; } TimerBar; float MaxTimes[MAX_TIMERS]; float MaxFrameTime; uint32 curMS; uint32 msCollected[MAX_MS_COLLECTED]; #ifdef FRAMETIME float FrameInitTime; #endif void tbInit() { TimerBar.count = 0; uint32 i = CTimer::GetFrameCounter() & 0x7F; if (i == 0) { do MaxTimes[i++] = 0.0f; while (i != MAX_TIMERS); #ifdef FRAMETIME MaxFrameTime = 0.0f; #endif } #ifdef FRAMETIME FrameInitTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame(); #endif } void tbStartTimer(int32 unk, Const char *name) { strcpy(TimerBar.Timers[TimerBar.count].name, name); TimerBar.Timers[TimerBar.count].unk = unk; TimerBar.Timers[TimerBar.count].startTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame(); TimerBar.count++; } void tbEndTimer(Const char* name) { uint32 n = 1500; for (uint32 i = 0; i < TimerBar.count; i++) { if (strcmp(name, TimerBar.Timers[i].name) == 0) n = i; } assert(n != 1500); TimerBar.Timers[n].endTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame(); } float Diag_GetFPS() { return 39000.0f / (msCollected[(curMS - 1) % MAX_MS_COLLECTED] - msCollected[curMS % MAX_MS_COLLECTED]); } void tbDisplay() { char temp[200]; wchar wtemp[200]; #ifdef FRAMETIME float FrameEndTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame(); #endif msCollected[(curMS++) % MAX_MS_COLLECTED] = RsTimer(); CFont::SetBackgroundOff(); CFont::SetBackgroundColor(CRGBA(0, 0, 0, 128)); CFont::SetScale(0.48f, 1.12f); CFont::SetCentreOff(); CFont::SetJustifyOff(); CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); CFont::SetRightJustifyOff(); CFont::SetPropOn(); CFont::SetFontStyle(FONT_STANDARD); sprintf(temp, "FPS: %.2f", Diag_GetFPS()); AsciiToUnicode(temp, wtemp); CFont::SetColor(CRGBA(255, 255, 255, 255)); if (!CMenuManager::m_PrefsMarketing || !CMenuManager::m_PrefsDisableTutorials) { CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * (4.0f / DEFAULT_SCREEN_HEIGHT), wtemp); #ifndef FINAL // Timers output (my own implementation) for (uint32 i = 0; i < TimerBar.count; i++) { MaxTimes[i] = Max(MaxTimes[i], TimerBar.Timers[i].endTime - TimerBar.Timers[i].startTime); sprintf(temp, "%s: %.2f", &TimerBar.Timers[i].name[0], MaxTimes[i]); AsciiToUnicode(temp, wtemp); CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (i + 2)) / DEFAULT_SCREEN_HEIGHT), wtemp); } #ifdef FRAMETIME MaxFrameTime = Max(MaxFrameTime, FrameEndTime - FrameInitTime); sprintf(temp, "Frame Time: %.2f", MaxFrameTime); AsciiToUnicode(temp, wtemp); CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (TimerBar.count + 4)) / DEFAULT_SCREEN_HEIGHT), wtemp); #endif // FRAMETIME #endif // !FINAL } } #endif // !MASTER ================================================ FILE: src/core/timebars.h ================================================ #pragma once #ifdef TIMEBARS void tbInit(); void tbStartTimer(int32, Const char*); void tbEndTimer(Const char*); void tbDisplay(); #else #define tbInit() #define tbStartTimer(a, b) #define tbEndTimer(a) #define tbDisplay() #endif ================================================ FILE: src/entities/Dummy.cpp ================================================ #include "common.h" #include "Pools.h" #include "World.h" #include "Dummy.h" void *CDummy::operator new(size_t sz) { return CPools::GetDummyPool()->New(); } void CDummy::operator delete(void *p, size_t sz) { CPools::GetDummyPool()->Delete((CDummy*)p); } void CDummy::Add(void) { int x, xstart, xmid, xend; int y, ystart, ymid, yend; CSector *s; CPtrList *list; CRect bounds = GetBoundRect(); xstart = CWorld::GetSectorIndexX(bounds.left); xend = CWorld::GetSectorIndexX(bounds.right); xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); ystart = CWorld::GetSectorIndexY(bounds.top); yend = CWorld::GetSectorIndexY(bounds.bottom); ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); assert(xstart >= 0); assert(xend < NUMSECTORS_X); assert(ystart >= 0); assert(yend < NUMSECTORS_Y); for(y = ystart; y <= yend; y++) for(x = xstart; x <= xend; x++){ s = CWorld::GetSector(x, y); if(x == xmid && y == ymid) list = &s->m_lists[ENTITYLIST_DUMMIES]; else list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP]; CPtrNode *node = list->InsertItem(this); assert(node); m_entryInfoList.InsertItem(list, node, s); } } void CDummy::Remove(void) { CEntryInfoNode *node, *next; for(node = m_entryInfoList.first; node; node = next){ next = node->next; node->list->DeleteNode(node->listnode); m_entryInfoList.DeleteNode(node); } } bool IsDummyPointerValid(CDummy* pDummy) { if (!pDummy) return false; int index = CPools::GetDummyPool()->GetJustIndex_NoFreeAssert(pDummy); #ifdef FIX_BUGS if (index < 0 || index >= CPools::GetDummyPool()->GetSize()) #else if (index < 0 || index > CPools::GetDummyPool()->GetSize()) #endif return false; return pDummy->m_entryInfoList.first; } ================================================ FILE: src/entities/Dummy.h ================================================ #pragma once #include "Lists.h" #include "Entity.h" class CDummy : public CEntity { public: CEntryInfoList m_entryInfoList; CDummy(void) { m_type = ENTITY_TYPE_DUMMY; } void Add(void); void Remove(void); static void *operator new(size_t); static void operator delete(void*, size_t); }; bool IsDummyPointerValid(CDummy* pDummy); ================================================ FILE: src/entities/Entity.cpp ================================================ #include "common.h" #include "General.h" #include "RwHelper.h" #include "ModelIndices.h" #include "Timer.h" #include "Entity.h" #include "Object.h" #include "World.h" #include "Camera.h" #include "Glass.h" #include "Weather.h" #include "Timecycle.h" #include "TrafficLights.h" #include "Coronas.h" #include "PointLights.h" #include "Shadows.h" #include "Pickups.h" #include "SpecialFX.h" #include "TxdStore.h" #include "Zones.h" #include "MemoryHeap.h" #include "Bones.h" #include "Debug.h" #include "Ped.h" #include "Dummy.h" #include "WindModifiers.h" int gBuildings; CEntity::CEntity(void) { m_type = ENTITY_TYPE_NOTHING; m_status = STATUS_ABANDONED; bUsesCollision = false; bCollisionProcessed = false; bIsStatic = false; bHasContacted = false; bPedPhysics = false; bIsStuck = false; bIsInSafePosition = false; bUseCollisionRecords = false; bWasPostponed = false; bExplosionProof = false; bIsVisible = true; bHasCollided = false; bRenderScorched = false; bHasBlip = false; bIsBIGBuilding = false; bStreamBIGBuilding = false; bRenderDamaged = false; bBulletProof = false; bFireProof = false; bCollisionProof = false; bMeleeProof = false; bOnlyDamagedByPlayer = false; bStreamingDontDelete = false; bRemoveFromWorld = false; bHasHitWall = false; bImBeingRendered = false; bTouchingWater = false; bIsSubway = false; bDrawLast = false; bNoBrightHeadLights = false; bDoNotRender = false; bDistanceFade = false; m_flagE1 = false; m_flagE2 = false; bOffscreen = false; bIsStaticWaitingForCollision = false; bDontStream = false; bUnderwater = false; bHasPreRenderEffects = false; m_scanCode = 0; m_modelIndex = -1; m_rwObject = nil; m_area = AREA_MAIN_MAP; m_randomSeed = CGeneral::GetRandomNumber(); m_pFirstReference = nil; } CEntity::~CEntity(void) { DeleteRwObject(); ResolveReferences(); } void CEntity::SetModelIndex(uint32 id) { m_modelIndex = id; bHasPreRenderEffects = HasPreRenderEffects(); CreateRwObject(); } void CEntity::SetModelIndexNoCreate(uint32 id) { m_modelIndex = id; bHasPreRenderEffects = HasPreRenderEffects(); } void CEntity::CreateRwObject(void) { CBaseModelInfo *mi; mi = CModelInfo::GetModelInfo(m_modelIndex); PUSH_MEMID(MEMID_WORLD); m_rwObject = mi->CreateInstance(); POP_MEMID(); if(m_rwObject){ if(IsBuilding()) gBuildings++; if(RwObjectGetType(m_rwObject) == rpATOMIC) m_matrix.AttachRW(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic*)m_rwObject)), false); else if(RwObjectGetType(m_rwObject) == rpCLUMP) m_matrix.AttachRW(RwFrameGetMatrix(RpClumpGetFrame((RpClump*)m_rwObject)), false); mi->AddRef(); } } void CEntity::AttachToRwObject(RwObject *obj) { m_rwObject = obj; if(m_rwObject){ if(RwObjectGetType(m_rwObject) == rpATOMIC) m_matrix.Attach(RwFrameGetMatrix(RpAtomicGetFrame((RpAtomic*)m_rwObject)), false); else if(RwObjectGetType(m_rwObject) == rpCLUMP) m_matrix.Attach(RwFrameGetMatrix(RpClumpGetFrame((RpClump*)m_rwObject)), false); CModelInfo::GetModelInfo(m_modelIndex)->AddRef(); } } void CEntity::DetachFromRwObject(void) { if(m_rwObject) CModelInfo::GetModelInfo(m_modelIndex)->RemoveRef(); m_rwObject = nil; m_matrix.Detach(); } RpAtomic* AtomicRemoveAnimFromSkinCB(RpAtomic *atomic, void *data) { if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic))){ RpHAnimHierarchy *hier = RpSkinAtomicGetHAnimHierarchy(atomic); #ifdef LIBRW if(hier && hier->interpolator->currentAnim){ RpHAnimAnimationDestroy(hier->interpolator->currentAnim); hier->interpolator->currentAnim = nil; } #else if(hier && hier->currentAnim){ RpHAnimAnimationDestroy(hier->currentAnim->pCurrentAnim); hier->currentAnim = nil; } #endif } return atomic; } void CEntity::DeleteRwObject(void) { RwFrame *f; m_matrix.Detach(); if(m_rwObject){ if(RwObjectGetType(m_rwObject) == rpATOMIC){ f = RpAtomicGetFrame((RpAtomic*)m_rwObject); RpAtomicDestroy((RpAtomic*)m_rwObject); RwFrameDestroy(f); }else if(RwObjectGetType(m_rwObject) == rpCLUMP){ if(IsClumpSkinned((RpClump*)m_rwObject)) RpClumpForAllAtomics((RpClump*)m_rwObject, AtomicRemoveAnimFromSkinCB, nil); RpClumpDestroy((RpClump*)m_rwObject); } m_rwObject = nil; CModelInfo::GetModelInfo(m_modelIndex)->RemoveRef(); if(IsBuilding()) gBuildings--; } } CRect CEntity::GetBoundRect(void) { CRect rect; CVector v; CColModel *col = CModelInfo::GetModelInfo(m_modelIndex)->GetColModel(); rect.ContainPoint(m_matrix * col->boundingBox.min); rect.ContainPoint(m_matrix * col->boundingBox.max); v = col->boundingBox.min; v.x = col->boundingBox.max.x; rect.ContainPoint(m_matrix * v); v = col->boundingBox.max; v.x = col->boundingBox.min.x; rect.ContainPoint(m_matrix * v); return rect; } CVector CEntity::GetBoundCentre(void) { CVector v; GetBoundCentre(v); return v; } void CEntity::GetBoundCentre(CVector &out) { out = m_matrix * CModelInfo::GetModelInfo(m_modelIndex)->GetColModel()->boundingSphere.center; } float CEntity::GetBoundRadius(void) { return CModelInfo::GetModelInfo(m_modelIndex)->GetColModel()->boundingSphere.radius; } void CEntity::UpdateRwFrame(void) { if(m_rwObject) RwFrameUpdateObjects((RwFrame*)rwObjectGetParent(m_rwObject)); } void CEntity::UpdateRpHAnim(void) { if(IsClumpSkinned(GetClump())){ RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump()); RpHAnimHierarchyUpdateMatrices(hier); #if 0 int i; char buf[256]; if(this == (CEntity*)FindPlayerPed()) for(i = 0; i < hier->numNodes; i++){ RpHAnimStdInterpFrame *kf = (RpHAnimStdInterpFrame*)rpHANIMHIERARCHYGETINTERPFRAME(hier, i); sprintf(buf, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %d %s", kf->q.imag.x, kf->q.imag.y, kf->q.imag.z, kf->q.real, kf->t.x, kf->t.y, kf->t.z, HIERNODEID(hier, i), ConvertBoneTag2BoneName(HIERNODEID(hier, i))); CDebug::PrintAt(buf, 10, 1+i*3); RwMatrix *m = &RpHAnimHierarchyGetMatrixArray(hier)[i]; sprintf(buf, "%6.3f %6.3f %6.3f %6.3f", m->right.x, m->up.x, m->at.x, m->pos.x); CDebug::PrintAt(buf, 80, 1+i*3+0); sprintf(buf, "%6.3f %6.3f %6.3f %6.3f", m->right.y, m->up.y, m->at.y, m->pos.y); CDebug::PrintAt(buf, 80, 1+i*3+1); sprintf(buf, "%6.3f %6.3f %6.3f %6.3f", m->right.z, m->up.z, m->at.z, m->pos.z); CDebug::PrintAt(buf, 80, 1+i*3+2); } void RenderSkeleton(RpHAnimHierarchy *hier); RenderSkeleton(hier); #endif } } bool CEntity::HasPreRenderEffects(void) { return IsTreeModel(GetModelIndex()) || GetModelIndex() == MI_COLLECTABLE1 || GetModelIndex() == MI_MONEY || GetModelIndex() == MI_CARMINE || GetModelIndex() == MI_NAUTICALMINE || GetModelIndex() == MI_BRIEFCASE || GetModelIndex() == MI_GRENADE || GetModelIndex() == MI_MOLOTOV || GetModelIndex() == MI_MISSILE || GetModelIndex() == MI_BEACHBALL || IsGlass(GetModelIndex()) || IsObject() && ((CObject*)this)->bIsPickup || IsLightWithPreRenderEffects(GetModelIndex()); } void CEntity::PreRender(void) { if (CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects() != 0) ProcessLightsForEntity(); if(!bHasPreRenderEffects) return; switch(m_type){ case ENTITY_TYPE_BUILDING: if(IsTreeModel(GetModelIndex())){ float dist = (TheCamera.GetPosition() - GetPosition()).Magnitude2D(); CObject::fDistToNearestTree = Min(CObject::fDistToNearestTree, dist); ModifyMatrixForTreeInWind(); } break; case ENTITY_TYPE_OBJECT: if(GetModelIndex() == MI_COLLECTABLE1){ CPickups::DoCollectableEffects(this); GetMatrix().UpdateRW(); UpdateRwFrame(); }else if(GetModelIndex() == MI_MONEY){ CPickups::DoMoneyEffects(this); GetMatrix().UpdateRW(); UpdateRwFrame(); }else if(GetModelIndex() == MI_NAUTICALMINE || GetModelIndex() == MI_CARMINE || GetModelIndex() == MI_BRIEFCASE){ if(((CObject*)this)->bIsPickup){ CPickups::DoMineEffects(this); GetMatrix().UpdateRW(); UpdateRwFrame(); } }else if(GetModelIndex() == MI_MISSILE){ CVector pos = GetPosition(); float flicker = (CGeneral::GetRandomNumber() & 0xF)/(float)0x10; CShadows::StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos, 8.0f, 0.0f, 0.0f, -8.0f, 255, 200.0f*flicker, 160.0f*flicker, 120.0f*flicker, 20.0f, false, 1.0f, nil, false); CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), 8.0f, 1.0f*flicker, 0.8f*flicker, 0.6f*flicker, CPointLights::FOG_NONE, true); CCoronas::RegisterCorona((uintptr)this, 255.0f*flicker, 220.0f*flicker, 190.0f*flicker, 255, pos, 6.0f*flicker, 80.0f, gpCoronaTexture[CCoronas::TYPE_STAR], CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); }else if(IsGlass(GetModelIndex())){ PreRenderForGlassWindow(); }else if (((CObject*)this)->bIsPickup) { CPickups::DoPickUpEffects(this); GetMatrix().UpdateRW(); UpdateRwFrame(); } else if (GetModelIndex() == MI_GRENADE) { CMotionBlurStreaks::RegisterStreak((uintptr)this, 100, 100, 100, GetPosition() - 0.07f * TheCamera.GetRight(), GetPosition() + 0.07f * TheCamera.GetRight()); } else if (GetModelIndex() == MI_MOLOTOV) { CMotionBlurStreaks::RegisterStreak((uintptr)this, 0, 100, 0, GetPosition() - 0.07f * TheCamera.GetRight(), GetPosition() + 0.07f * TheCamera.GetRight()); }else if(GetModelIndex() == MI_BEACHBALL){ CVector pos = GetPosition(); CShadows::StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowPedTex, &pos, 0.4f, 0.0f, 0.0f, -0.4f, CTimeCycle::GetShadowStrength(), CTimeCycle::GetShadowStrength(), CTimeCycle::GetShadowStrength(), CTimeCycle::GetShadowStrength(), 20.0f, false, 1.0f, nil, false); } // fall through case ENTITY_TYPE_DUMMY: if(GetModelIndex() == MI_TRAFFICLIGHTS){ CTrafficLights::DisplayActualLight(this); CShadows::StoreShadowForPole(this, 2.957f, 0.147f, 0.0f, 16.0f, 0.4f, 0); }else if(GetModelIndex() == MI_TRAFFICLIGHTS_VERTICAL){ CTrafficLights::DisplayActualLight(this); }else if(GetModelIndex() == MI_TRAFFICLIGHTS_MIAMI){ CTrafficLights::DisplayActualLight(this); CShadows::StoreShadowForPole(this, 4.819f, 1.315f, 0.0f, 16.0f, 0.4f, 0); }else if(GetModelIndex() == MI_TRAFFICLIGHTS_TWOVERTICAL){ CTrafficLights::DisplayActualLight(this); CShadows::StoreShadowForPole(this, 7.503f, 0.0f, 0.0f, 16.0f, 0.4f, 0); }else if(GetModelIndex() == MI_SINGLESTREETLIGHTS1) CShadows::StoreShadowForPole(this, 0.744f, 0.0f, 0.0f, 16.0f, 0.4f, 0); else if(GetModelIndex() == MI_SINGLESTREETLIGHTS2) CShadows::StoreShadowForPole(this, 0.043f, 0.0f, 0.0f, 16.0f, 0.4f, 0); else if(GetModelIndex() == MI_SINGLESTREETLIGHTS3) CShadows::StoreShadowForPole(this, 1.143f, 0.145f, 0.0f, 16.0f, 0.4f, 0); else if(GetModelIndex() == MI_DOUBLESTREETLIGHTS) CShadows::StoreShadowForPole(this, 0.0f, -0.048f, 0.0f, 16.0f, 0.4f, 0); break; } } void CEntity::Render(void) { if(m_rwObject){ bImBeingRendered = true; if(RwObjectGetType(m_rwObject) == rpATOMIC) RpAtomicRender((RpAtomic*)m_rwObject); else RpClumpRender((RpClump*)m_rwObject); bImBeingRendered = false; } } bool CEntity::GetIsTouching(CVector const ¢er, float radius) { return sq(GetBoundRadius()+radius) > (GetBoundCentre()-center).MagnitudeSqr(); } bool CEntity::IsVisible(void) { return m_rwObject && bIsVisible && GetIsOnScreen(); } bool CEntity::IsVisibleComplex(void) { return m_rwObject && bIsVisible && GetIsOnScreenComplex(); } bool CEntity::GetIsOnScreen(void) { return TheCamera.IsSphereVisible(GetBoundCentre(), GetBoundRadius(), &TheCamera.GetCameraMatrix()); } bool CEntity::GetIsOnScreenComplex(void) { #ifdef GTA_PS2 CVuVector boundBox[8]; #else CVector boundBox[8]; #endif if(TheCamera.IsPointVisible(GetBoundCentre(), &TheCamera.GetCameraMatrix())) return true; CRect rect = GetBoundRect(); CColModel *colmodel = CModelInfo::GetModelInfo(m_modelIndex)->GetColModel(); float z = GetPosition().z; float minz = z + colmodel->boundingBox.min.z; float maxz = z + colmodel->boundingBox.max.z; boundBox[0].x = rect.left; boundBox[0].y = rect.bottom; boundBox[0].z = minz; boundBox[1].x = rect.left; boundBox[1].y = rect.top; boundBox[1].z = minz; boundBox[2].x = rect.right; boundBox[2].y = rect.bottom; boundBox[2].z = minz; boundBox[3].x = rect.right; boundBox[3].y = rect.top; boundBox[3].z = minz; boundBox[4].x = rect.left; boundBox[4].y = rect.bottom; boundBox[4].z = maxz; boundBox[5].x = rect.left; boundBox[5].y = rect.top; boundBox[5].z = maxz; boundBox[6].x = rect.right; boundBox[6].y = rect.bottom; boundBox[6].z = maxz; boundBox[7].x = rect.right; boundBox[7].y = rect.top; boundBox[7].z = maxz; return TheCamera.IsBoxVisible(boundBox, &TheCamera.GetCameraMatrix()); } void CEntity::Add(void) { int x, xstart, xmid, xend; int y, ystart, ymid, yend; CSector *s; CPtrList *list; CRect bounds = GetBoundRect(); xstart = CWorld::GetSectorIndexX(bounds.left); xend = CWorld::GetSectorIndexX(bounds.right); xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); ystart = CWorld::GetSectorIndexY(bounds.top); yend = CWorld::GetSectorIndexY(bounds.bottom); ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); assert(xstart >= 0); assert(xend < NUMSECTORS_X); assert(ystart >= 0); assert(yend < NUMSECTORS_Y); for(y = ystart; y <= yend; y++) for(x = xstart; x <= xend; x++){ s = CWorld::GetSector(x, y); if(x == xmid && y == ymid) switch(m_type){ case ENTITY_TYPE_BUILDING: list = &s->m_lists[ENTITYLIST_BUILDINGS]; break; case ENTITY_TYPE_VEHICLE: list = &s->m_lists[ENTITYLIST_VEHICLES]; break; case ENTITY_TYPE_PED: list = &s->m_lists[ENTITYLIST_PEDS]; break; case ENTITY_TYPE_OBJECT: list = &s->m_lists[ENTITYLIST_OBJECTS]; break; case ENTITY_TYPE_DUMMY: list = &s->m_lists[ENTITYLIST_DUMMIES]; break; }else switch(m_type){ case ENTITY_TYPE_BUILDING: list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]; break; case ENTITY_TYPE_VEHICLE: list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]; break; case ENTITY_TYPE_PED: list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP]; break; case ENTITY_TYPE_OBJECT: list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP]; break; case ENTITY_TYPE_DUMMY: list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP]; break; } list->InsertItem(this); } } void CEntity::Remove(void) { int x, xstart, xmid, xend; int y, ystart, ymid, yend; CSector *s; CPtrList *list; CRect bounds = GetBoundRect(); xstart = CWorld::GetSectorIndexX(bounds.left); xend = CWorld::GetSectorIndexX(bounds.right); xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); ystart = CWorld::GetSectorIndexY(bounds.top); yend = CWorld::GetSectorIndexY(bounds.bottom); ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); assert(xstart >= 0); assert(xend < NUMSECTORS_X); assert(ystart >= 0); assert(yend < NUMSECTORS_Y); for(y = ystart; y <= yend; y++) for(x = xstart; x <= xend; x++){ s = CWorld::GetSector(x, y); if(x == xmid && y == ymid) switch(m_type){ case ENTITY_TYPE_BUILDING: list = &s->m_lists[ENTITYLIST_BUILDINGS]; break; case ENTITY_TYPE_VEHICLE: list = &s->m_lists[ENTITYLIST_VEHICLES]; break; case ENTITY_TYPE_PED: list = &s->m_lists[ENTITYLIST_PEDS]; break; case ENTITY_TYPE_OBJECT: list = &s->m_lists[ENTITYLIST_OBJECTS]; break; case ENTITY_TYPE_DUMMY: list = &s->m_lists[ENTITYLIST_DUMMIES]; break; }else switch(m_type){ case ENTITY_TYPE_BUILDING: list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]; break; case ENTITY_TYPE_VEHICLE: list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]; break; case ENTITY_TYPE_PED: list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP]; break; case ENTITY_TYPE_OBJECT: list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP]; break; case ENTITY_TYPE_DUMMY: list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP]; break; } list->RemoveItem(this); } } float CEntity::GetDistanceFromCentreOfMassToBaseOfModel(void) { return -CModelInfo::GetModelInfo(m_modelIndex)->GetColModel()->boundingBox.min.z; } void CEntity::SetupBigBuilding(void) { CSimpleModelInfo *mi; mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(m_modelIndex); bIsBIGBuilding = true; bStreamingDontDelete = true; bUsesCollision = false; m_level = CTheZones::GetLevelFromPosition(&GetPosition()); if(mi->m_lodDistances[0] <= 2000.0f) bStreamBIGBuilding = true; if(mi->m_lodDistances[0] > 2500.0f || mi->m_ignoreDrawDist) m_level = LEVEL_GENERIC; else if(m_level == LEVEL_GENERIC) printf("%s isn't in a level\n", mi->GetModelName()); } float WindTabel[] = { 1.0f, 0.5f, 0.2f, 0.7f, 0.4f, 1.0f, 0.5f, 0.3f, 0.2f, 0.1f, 0.7f, 0.6f, 0.3f, 1.0f, 0.5f, 0.2f, }; void CEntity::ModifyMatrixForTreeInWind(void) { uint16 t; float f; float strength, flutter; if(CTimer::GetIsPaused()) return; CMatrix mat(GetMatrix().m_attachment); if(CWeather::Wind >= 0.5){ t = m_randomSeed + 8*CTimer::GetTimeInMilliseconds(); f = (t & 0xFFF)/(float)0x1000; flutter = f * WindTabel[(t>>12)+1 & 0xF] + (1.0f - f) * WindTabel[(t>>12) & 0xF] + 1.0f; strength = -0.015f*CWeather::Wind; }else if(CWeather::Wind >= 0.2){ t = (uintptr)this + CTimer::GetTimeInMilliseconds(); f = (t & 0xFFF)/(float)0x1000; flutter = Sin(f * 6.28f); strength = -0.008f; }else{ t = (uintptr)this + CTimer::GetTimeInMilliseconds(); f = (t & 0xFFF)/(float)0x1000; flutter = Sin(f * 6.28f); strength = -0.005f; } mat.GetUp().x = strength * flutter; if(IsPalmTreeModel(GetModelIndex())) mat.GetUp().x += -0.07f*CWeather::Wind; mat.GetUp().y = mat.GetUp().x; CWindModifiers::FindWindModifier(GetPosition(), &mat.GetUp().x, &mat.GetUp().y); mat.UpdateRW(); UpdateRwFrame(); } float BannerWindTabel[] = { 0.0f, 0.3f, 0.6f, 0.85f, 0.99f, 0.97f, 0.65f, 0.15f, -0.1f, 0.0f, 0.35f, 0.57f, 0.55f, 0.35f, 0.45f, 0.67f, 0.73f, 0.45f, 0.25f, 0.35f, 0.35f, 0.11f, 0.13f, 0.21f, 0.28f, 0.28f, 0.22f, 0.1f, 0.0f, -0.1f, -0.17f, -0.12f }; // unused void CEntity::ModifyMatrixForBannerInWind(void) { uint16 t; float f; float strength, flutter; CVector right, up; if(CTimer::GetIsPaused()) return; if(CWeather::Wind < 0.1f) strength = 0.2f; else if(CWeather::Wind < 0.8f) strength = 0.43f; else strength = 0.66f; t = ((int)(GetMatrix().GetPosition().x + GetMatrix().GetPosition().y) << 10) + 16*CTimer::GetTimeInMilliseconds(); f = (t & 0x7FF)/(float)0x800; flutter = f * BannerWindTabel[(t>>11)+1 & 0x1F] + (1.0f - f) * BannerWindTabel[(t>>11) & 0x1F]; flutter *= strength; right = CrossProduct(GetForward(), GetUp()); right.z = 0.0f; right.Normalise(); up = right * flutter; up.z = Sqrt(sq(1.0f) - sq(flutter)); GetRight() = CrossProduct(GetForward(), up); GetUp() = up; GetMatrix().UpdateRW(); UpdateRwFrame(); } void CEntity::PreRenderForGlassWindow(void) { if(((CSimpleModelInfo*)CModelInfo::GetModelInfo(m_modelIndex))->m_isArtistGlass) return; CGlass::AskForObjectToBeRenderedInGlass(this); bIsVisible = false; } /* 0x487A10 - SetAtomicAlphaCB 0x4879E0 - SetClumpAlphaCB */ RpMaterial* SetAtomicAlphaCB(RpMaterial *material, void *data) { ((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data; return material; } RpAtomic* SetClumpAlphaCB(RpAtomic *atomic, void *data) { RpGeometry *geometry = RpAtomicGetGeometry(atomic); RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); RpGeometryForAllMaterials(geometry, SetAtomicAlphaCB, (void*)data); return atomic; } void CEntity::SetRwObjectAlpha(int32 alpha) { if (m_rwObject != nil) { switch (RwObjectGetType(m_rwObject)) { case rpATOMIC: { RpGeometry *geometry = RpAtomicGetGeometry((RpAtomic*)m_rwObject); RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); RpGeometryForAllMaterials(geometry, SetAtomicAlphaCB, (void*)alpha); break; } case rpCLUMP: RpClumpForAllAtomics((RpClump*)m_rwObject, SetClumpAlphaCB, (void*)alpha); break; } } } bool IsEntityPointerValid(CEntity* pEntity) { if (!pEntity) return false; switch (pEntity->GetType()) { case ENTITY_TYPE_NOTHING: return false; case ENTITY_TYPE_BUILDING: return IsBuildingPointerValid((CBuilding*)pEntity); case ENTITY_TYPE_VEHICLE: return IsVehiclePointerValid((CVehicle*)pEntity); case ENTITY_TYPE_PED: return IsPedPointerValid((CPed*)pEntity); case ENTITY_TYPE_OBJECT: return IsObjectPointerValid((CObject*)pEntity); case ENTITY_TYPE_DUMMY: return IsDummyPointerValid((CDummy*)pEntity); } return false; } #ifdef COMPATIBLE_SAVES void CEntity::SaveEntityFlags(uint8*& buf) { uint32 tmp = 0; tmp |= (m_type & (BIT(3) - 1)); tmp |= (m_status & (BIT(5) - 1)) << 3; if (bUsesCollision) tmp |= BIT(8); if (bCollisionProcessed) tmp |= BIT(9); if (bIsStatic) tmp |= BIT(10); if (bHasContacted) tmp |= BIT(11); if (bPedPhysics) tmp |= BIT(12); if (bIsStuck) tmp |= BIT(13); if (bIsInSafePosition) tmp |= BIT(14); if (bUseCollisionRecords) tmp |= BIT(15); if (bWasPostponed) tmp |= BIT(16); if (bExplosionProof) tmp |= BIT(17); if (bIsVisible) tmp |= BIT(18); if (bHasCollided) tmp |= BIT(19); if (bRenderScorched) tmp |= BIT(20); if (bHasBlip) tmp |= BIT(21); if (bIsBIGBuilding) tmp |= BIT(22); if (bStreamBIGBuilding) tmp |= BIT(23); if (bRenderDamaged) tmp |= BIT(24); if (bBulletProof) tmp |= BIT(25); if (bFireProof) tmp |= BIT(26); if (bCollisionProof) tmp |= BIT(27); if (bMeleeProof) tmp |= BIT(28); if (bOnlyDamagedByPlayer) tmp |= BIT(29); if (bStreamingDontDelete) tmp |= BIT(30); if (bRemoveFromWorld) tmp |= BIT(31); WriteSaveBuf(buf, tmp); tmp = 0; if (bHasHitWall) tmp |= BIT(0); if (bImBeingRendered) tmp |= BIT(1); if (bTouchingWater) tmp |= BIT(2); if (bIsSubway) tmp |= BIT(3); if (bDrawLast) tmp |= BIT(4); if (bNoBrightHeadLights) tmp |= BIT(5); if (bDoNotRender) tmp |= BIT(6); if (bDistanceFade) tmp |= BIT(7); if (m_flagE1) tmp |= BIT(8); if (m_flagE2) tmp |= BIT(9); if (bOffscreen) tmp |= BIT(10); if (bIsStaticWaitingForCollision) tmp |= BIT(11); if (bDontStream) tmp |= BIT(12); if (bUnderwater) tmp |= BIT(13); if (bHasPreRenderEffects) tmp |= BIT(14); WriteSaveBuf(buf, tmp); } void CEntity::LoadEntityFlags(uint8*& buf) { uint32 tmp = ReadSaveBuf(buf); m_type = (tmp & ((BIT(3) - 1))); m_status = ((tmp >> 3) & (BIT(5) - 1)); bUsesCollision = !!(tmp & BIT(8)); bCollisionProcessed = !!(tmp & BIT(9)); bIsStatic = !!(tmp & BIT(10)); bHasContacted = !!(tmp & BIT(11)); bPedPhysics = !!(tmp & BIT(12)); bIsStuck = !!(tmp & BIT(13)); bIsInSafePosition = !!(tmp & BIT(14)); bUseCollisionRecords = !!(tmp & BIT(15)); bWasPostponed = !!(tmp & BIT(16)); bExplosionProof = !!(tmp & BIT(17)); bIsVisible = !!(tmp & BIT(18)); bHasCollided = !!(tmp & BIT(19)); bRenderScorched = !!(tmp & BIT(20)); bHasBlip = !!(tmp & BIT(21)); bIsBIGBuilding = !!(tmp & BIT(22)); bStreamBIGBuilding = !!(tmp & BIT(23)); bRenderDamaged = !!(tmp & BIT(24)); bBulletProof = !!(tmp & BIT(25)); bFireProof = !!(tmp & BIT(26)); bCollisionProof = !!(tmp & BIT(27)); bMeleeProof = !!(tmp & BIT(28)); bOnlyDamagedByPlayer = !!(tmp & BIT(29)); bStreamingDontDelete = !!(tmp & BIT(30)); bRemoveFromWorld = !!(tmp & BIT(31)); tmp = ReadSaveBuf(buf); bHasHitWall = !!(tmp & BIT(0)); bImBeingRendered = !!(tmp & BIT(1)); bTouchingWater = !!(tmp & BIT(2)); bIsSubway = !!(tmp & BIT(3)); bDrawLast = !!(tmp & BIT(4)); bNoBrightHeadLights = !!(tmp & BIT(5)); bDoNotRender = !!(tmp & BIT(6)); bDistanceFade = !!(tmp & BIT(7)); m_flagE1 = !!(tmp & BIT(8)); m_flagE2 = !!(tmp & BIT(9)); bOffscreen = !!(tmp & BIT(10)); bIsStaticWaitingForCollision = !!(tmp & BIT(11)); bDontStream = !!(tmp & BIT(12)); bUnderwater = !!(tmp & BIT(13)); bHasPreRenderEffects = !!(tmp & BIT(14)); } #endif ================================================ FILE: src/entities/Entity.h ================================================ #pragma once #include "ModelInfo.h" #include "Placeable.h" struct CReference; class CPtrList; enum eEntityType { ENTITY_TYPE_NOTHING = 0, ENTITY_TYPE_BUILDING, ENTITY_TYPE_VEHICLE, ENTITY_TYPE_PED, ENTITY_TYPE_OBJECT, ENTITY_TYPE_DUMMY, }; enum eEntityStatus { STATUS_PLAYER, STATUS_PLAYER_PLAYBACKFROMBUFFER, STATUS_SIMPLE, STATUS_PHYSICS, STATUS_ABANDONED, STATUS_WRECKED, STATUS_TRAIN_MOVING, STATUS_TRAIN_NOT_MOVING, STATUS_HELI, STATUS_PLANE, STATUS_PLAYER_REMOTE, STATUS_PLAYER_DISABLED, STATUS_GHOST }; class CEntity : public CPlaceable { public: RwObject *m_rwObject; protected: uint32 m_type : 3; private: uint32 m_status : 5; public: // flagsA uint32 bUsesCollision : 1; // does entity use collision uint32 bCollisionProcessed : 1; // has object been processed by a ProcessEntityCollision function uint32 bIsStatic : 1; // is entity static uint32 bHasContacted : 1; // has entity processed some contact forces uint32 bPedPhysics : 1; uint32 bIsStuck : 1; // is entity stuck uint32 bIsInSafePosition : 1; // is entity in a collision free safe position uint32 bUseCollisionRecords : 1; // flagsB uint32 bWasPostponed : 1; // was entity control processing postponed uint32 bExplosionProof : 1; uint32 bIsVisible : 1; //is the entity visible uint32 bHasCollided : 1; uint32 bRenderScorched : 1; uint32 bHasBlip : 1; uint32 bIsBIGBuilding : 1; // Set if this entity is a big building uint32 bStreamBIGBuilding : 1; // set when draw dist <= 2000 // flagsC uint32 bRenderDamaged : 1; // use damaged LOD models for objects with applicable damage uint32 bBulletProof : 1; uint32 bFireProof : 1; uint32 bCollisionProof : 1; uint32 bMeleeProof : 1; uint32 bOnlyDamagedByPlayer : 1; uint32 bStreamingDontDelete : 1; // Dont let the streaming remove this uint32 bRemoveFromWorld : 1; // remove this entity next time it should be processed // flagsD uint32 bHasHitWall : 1; // has collided with a building (changes subsequent collisions) uint32 bImBeingRendered : 1; // don't delete me because I'm being rendered uint32 bTouchingWater : 1; // used by cBuoyancy::ProcessBuoyancy uint32 bIsSubway : 1; // set when subway, but maybe different meaning? uint32 bDrawLast : 1; // draw object last uint32 bNoBrightHeadLights : 1; uint32 bDoNotRender : 1; //-- only applies to CObjects apparently uint32 bDistanceFade : 1; // Fade entity because it is far away // flagsE uint32 m_flagE1 : 1; uint32 m_flagE2 : 1; uint32 bOffscreen : 1; // offscreen flag. This can only be trusted when it is set to true uint32 bIsStaticWaitingForCollision : 1; // this is used by script created entities - they are static until the collision is loaded below them uint32 bDontStream : 1; // tell the streaming not to stream me uint32 bUnderwater : 1; // this object is underwater change drawing order uint32 bHasPreRenderEffects : 1; // Object has a prerender effects attached to it uint16 m_scanCode; uint16 m_randomSeed; int16 m_modelIndex; int8 m_level; int8 m_area; CReference *m_pFirstReference; public: uint8 GetType() const { return m_type; } void SetType(uint8 type) { m_type = type; } uint8 GetStatus() const { return m_status; } void SetStatus(uint8 status) { m_status = status; } CColModel *GetColModel(void) { return CModelInfo::GetModelInfo(m_modelIndex)->GetColModel(); } bool GetIsStatic(void) const { return bIsStatic || bIsStaticWaitingForCollision; } void SetIsStatic(bool state) { bIsStatic = state; } #ifdef COMPATIBLE_SAVES void SaveEntityFlags(uint8*& buf); void LoadEntityFlags(uint8*& buf); #else uint32* GetAddressOfEntityProperties() { /* AWFUL */ return (uint32*)((char*)&m_rwObject + sizeof(m_rwObject)); } #endif CEntity(void); virtual ~CEntity(void); virtual void Add(void); virtual void Remove(void); virtual void SetModelIndex(uint32 id); virtual void SetModelIndexNoCreate(uint32 id); virtual void CreateRwObject(void); virtual void DeleteRwObject(void); virtual CRect GetBoundRect(void); virtual void ProcessControl(void) {} virtual void ProcessCollision(void) {} virtual void ProcessShift(void) {} virtual void Teleport(CVector v) {} virtual void PreRender(void); virtual void Render(void); virtual bool SetupLighting(void); virtual void RemoveLighting(bool); virtual void FlagToDestroyWhenNextProcessed(void) {} bool IsBuilding(void) { return m_type == ENTITY_TYPE_BUILDING; } bool IsVehicle(void) { return m_type == ENTITY_TYPE_VEHICLE; } bool IsPed(void) { return m_type == ENTITY_TYPE_PED; } bool IsObject(void) { return m_type == ENTITY_TYPE_OBJECT; } bool IsDummy(void) { return m_type == ENTITY_TYPE_DUMMY; } RpAtomic *GetAtomic(void) { assert(RwObjectGetType(m_rwObject) == rpATOMIC); return (RpAtomic*)m_rwObject; } RpClump *GetClump(void) { assert(RwObjectGetType(m_rwObject) == rpCLUMP); return (RpClump*)m_rwObject; } void GetBoundCentre(CVector &out); CVector GetBoundCentre(void); float GetBoundRadius(void); float GetDistanceFromCentreOfMassToBaseOfModel(void); bool GetIsTouching(CVector const ¢er, float r); bool GetIsOnScreen(void); bool GetIsOnScreenComplex(void); bool IsVisible(void); bool IsVisibleComplex(void); bool IsEntityOccluded(void); int16 GetModelIndex(void) const { return m_modelIndex; } void UpdateRwFrame(void); void SetupBigBuilding(void); bool HasPreRenderEffects(void); void AttachToRwObject(RwObject *obj); void DetachFromRwObject(void); void RegisterReference(CEntity **pent); void ResolveReferences(void); void PruneReferences(void); void CleanUpOldReference(CEntity **pent); void UpdateRpHAnim(void); void PreRenderForGlassWindow(void); void AddSteamsFromGround(CVector *unused); void ModifyMatrixForTreeInWind(void); void ModifyMatrixForBannerInWind(void); void ProcessLightsForEntity(void); void SetRwObjectAlpha(int32 alpha); }; bool IsEntityPointerValid(CEntity*); ================================================ FILE: src/entities/Physical.cpp ================================================ #include "common.h" #include "World.h" #include "General.h" #include "Timer.h" #include "Stats.h" #include "ModelIndices.h" #include "Treadable.h" #include "Vehicle.h" #include "Ped.h" #include "Object.h" #include "Glass.h" #include "ParticleObject.h" #include "Particle.h" #include "SurfaceTable.h" #include "PathFind.h" #include "CarCtrl.h" #include "DMAudio.h" #include "Automobile.h" #include "Bike.h" #include "Pickups.h" #include "Physical.h" #ifdef WALLCLIMB_CHEAT bool gGravityCheat; #endif CPhysical::CPhysical(void) { int i; #ifdef FIX_BUGS m_nLastTimeCollided = 0; #endif m_fForceMultiplier = 1.0f; 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); m_vecMoveSpeedAvg = CVector(0.0f, 0.0f, 0.0f); m_vecTurnSpeedAvg = CVector(0.0f, 0.0f, 0.0f); m_movingListNode = nil; m_nStaticFrames = 0; m_nCollisionRecords = 0; for(i = 0; i < 6; i++) m_aCollisionRecords[i] = nil; m_bIsVehicleBeingShifted = false; bJustCheckCollision = false; m_nDamagePieceType = 0; m_fDamageImpulse = 0.0f; m_pDamageEntity = nil; m_vecDamageNormal = CVector(0.0f, 0.0f, 0.0f); bUsesCollision = true; m_audioEntityId = -5; m_phys_unused1 = 100.0f; m_vecCentreOfMass = CVector(0.0f, 0.0f, 0.0f); m_phys_unused2 = 0; bIsHeavy = false; bAffectedByGravity = true; bInfiniteMass = false; m_phy_flagA08 = false; bIsInWater = false; bHitByTrain = false; bSkipLineCol = false; m_fDistanceTravelled = 0.0f; m_phy_flagA20 = false; #ifdef FIX_BUGS m_nSurfaceTouched = SURFACE_DEFAULT; #endif m_nZoneLevel = LEVEL_GENERIC; bIsFrozen = false; bDontLoadCollision = false; } CPhysical::~CPhysical(void) { m_entryInfoList.Flush(); } void CPhysical::Add(void) { int x, xstart, xmid, xend; int y, ystart, ymid, yend; CSector *s; CPtrList *list; CRect bounds = GetBoundRect(); xstart = CWorld::GetSectorIndexX(bounds.left); xend = CWorld::GetSectorIndexX(bounds.right); xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); ystart = CWorld::GetSectorIndexY(bounds.top); yend = CWorld::GetSectorIndexY(bounds.bottom); ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); assert(xstart >= 0); assert(xend < NUMSECTORS_X); assert(ystart >= 0); assert(yend < NUMSECTORS_Y); for(y = ystart; y <= yend; y++) for(x = xstart; x <= xend; x++){ s = CWorld::GetSector(x, y); if(x == xmid && y == ymid) switch(m_type){ case ENTITY_TYPE_VEHICLE: list = &s->m_lists[ENTITYLIST_VEHICLES]; break; case ENTITY_TYPE_PED: list = &s->m_lists[ENTITYLIST_PEDS]; break; case ENTITY_TYPE_OBJECT: list = &s->m_lists[ENTITYLIST_OBJECTS]; break; default: assert(0); }else switch(m_type){ case ENTITY_TYPE_VEHICLE: list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]; break; case ENTITY_TYPE_PED: list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP]; break; case ENTITY_TYPE_OBJECT: list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP]; break; default: assert(0); } CPtrNode *node = list->InsertItem(this); assert(node); m_entryInfoList.InsertItem(list, node, s); } } void CPhysical::Remove(void) { CEntryInfoNode *node, *next; for(node = m_entryInfoList.first; node; node = next){ next = node->next; node->list->DeleteNode(node->listnode); m_entryInfoList.DeleteNode(node); } } void CPhysical::RemoveAndAdd(void) { int x, xstart, xmid, xend; int y, ystart, ymid, yend; CSector *s; CPtrList *list; CRect bounds = GetBoundRect(); xstart = CWorld::GetSectorIndexX(bounds.left); xend = CWorld::GetSectorIndexX(bounds.right); xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f); ystart = CWorld::GetSectorIndexY(bounds.top); yend = CWorld::GetSectorIndexY(bounds.bottom); ymid = CWorld::GetSectorIndexY((bounds.top + bounds.bottom)/2.0f); assert(xstart >= 0); assert(xend < NUMSECTORS_X); assert(ystart >= 0); assert(yend < NUMSECTORS_Y); // we'll try to recycle nodes from here CEntryInfoNode *next = m_entryInfoList.first; for(y = ystart; y <= yend; y++) for(x = xstart; x <= xend; x++){ s = CWorld::GetSector(x, y); if(x == xmid && y == ymid) switch(m_type){ case ENTITY_TYPE_VEHICLE: list = &s->m_lists[ENTITYLIST_VEHICLES]; break; case ENTITY_TYPE_PED: list = &s->m_lists[ENTITYLIST_PEDS]; break; case ENTITY_TYPE_OBJECT: list = &s->m_lists[ENTITYLIST_OBJECTS]; break; }else switch(m_type){ case ENTITY_TYPE_VEHICLE: list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]; break; case ENTITY_TYPE_PED: list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP]; break; case ENTITY_TYPE_OBJECT: list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP]; break; } if(next){ // If we still have old nodes, use them next->list->RemoveNode(next->listnode); list->InsertNode(next->listnode); next->list = list; next->sector = s; next = next->next; }else{ CPtrNode *node = list->InsertItem(this); m_entryInfoList.InsertItem(list, node, s); } } // Remove old nodes we no longer need CEntryInfoNode *node; for(node = next; node; node = next){ next = node->next; node->list->DeleteNode(node->listnode); m_entryInfoList.DeleteNode(node); } } CRect CPhysical::GetBoundRect(void) { CVector center; float radius; GetBoundCentre(center); radius = GetBoundRadius(); return CRect(center.x-radius, center.y-radius, center.x+radius, center.y+radius); } // --MIAMI: Proof-read once void CPhysical::AddToMovingList(void) { if (!bIsStaticWaitingForCollision) m_movingListNode = CWorld::GetMovingEntityList().InsertItem(this); } // --MIAMI: Proof-read once void CPhysical::RemoveFromMovingList(void) { if(m_movingListNode){ CWorld::GetMovingEntityList().DeleteNode(m_movingListNode); m_movingListNode = nil; } } void CPhysical::SetDamagedPieceRecord(uint16 piece, float impulse, CEntity *entity, CVector dir) { m_nDamagePieceType = piece; m_fDamageImpulse = impulse; m_pDamageEntity = entity; entity->RegisterReference(&m_pDamageEntity); m_vecDamageNormal = dir; } // --MIAMI: Proof-read once void CPhysical::AddCollisionRecord(CEntity *ent) { AddCollisionRecord_Treadable(ent); this->bHasCollided = true; ent->bHasCollided = true; this->m_nLastTimeCollided = CTimer::GetTimeInMilliseconds(); if(IsVehicle() && ent->IsVehicle()){ if(((CVehicle*)this)->m_nAlarmState == -1) ((CVehicle*)this)->m_nAlarmState = 15000; if(((CVehicle*)ent)->m_nAlarmState == -1) ((CVehicle*)ent)->m_nAlarmState = 15000; } if(bUseCollisionRecords){ int i; for(i = 0; i < m_nCollisionRecords; i++) if(m_aCollisionRecords[i] == ent) return; if(m_nCollisionRecords < PHYSICAL_MAX_COLLISIONRECORDS) m_aCollisionRecords[m_nCollisionRecords++] = ent; } } // --MIAMI: Proof-read once void CPhysical::AddCollisionRecord_Treadable(CEntity *ent) { if(ent->IsBuilding() && ((CBuilding*)ent)->GetIsATreadable()){ } } // --MIAMI: Proof-read once bool CPhysical::GetHasCollidedWith(CEntity *ent) { int i; if(bUseCollisionRecords) for(i = 0; i < m_nCollisionRecords; i++) if(m_aCollisionRecords[i] == ent) return true; return false; } // --MIAMI: Proof-read once void CPhysical::RemoveRefsToEntity(CEntity *ent) { int i = 0, j; while (i < m_nCollisionRecords){ if(m_aCollisionRecords[i] == ent){ for(j = i; j < m_nCollisionRecords-1; j++) m_aCollisionRecords[j] = m_aCollisionRecords[j+1]; m_nCollisionRecords--; } else i++; } } // --MIAMI: Proof-read once void CPhysical::PlacePhysicalRelativeToOtherPhysical(CPhysical *other, CPhysical *phys, CVector localPos) { CVector worldPos = other->GetMatrix() * localPos; float step = 0.9f * CTimer::GetTimeStep(); CVector pos = other->m_vecMoveSpeed*step + worldPos; CWorld::Remove(phys); phys->GetMatrix() = other->GetMatrix(); phys->SetPosition(pos); phys->m_vecMoveSpeed = other->m_vecMoveSpeed; phys->GetMatrix().UpdateRW(); phys->UpdateRwFrame(); CWorld::Add(phys); } // --MIAMI: Proof-read once int32 CPhysical::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) { int32 numSpheres = CCollision::ProcessColModels( GetMatrix(), *GetColModel(), ent->GetMatrix(), *ent->GetColModel(), colpoints, nil, nil); // No Lines allowed! if(numSpheres > 0){ AddCollisionRecord(ent); if(!ent->IsBuilding()) // Can't this catch dummies too? ((CPhysical*)ent)->AddCollisionRecord(this); if(ent->IsBuilding() || ent->GetIsStatic()) this->bHasHitWall = true; } return numSpheres; } // --MIAMI: Proof-read once void CPhysical::ProcessControl(void) { if(!IsPed()) bIsInWater = false; bHasContacted = false; bIsInSafePosition = false; bWasPostponed = false; bHasHitWall = false; if(GetStatus() == STATUS_SIMPLE) return; m_nCollisionRecords = 0; bHasCollided = false; m_nDamagePieceType = 0; m_fDamageImpulse = 0.0f; m_pDamageEntity = nil; if(!bIsStuck){ if(IsObject() || IsPed() && !bPedPhysics){ m_vecMoveSpeedAvg = (m_vecMoveSpeedAvg + m_vecMoveSpeed)/2.0f; m_vecTurnSpeedAvg = (m_vecTurnSpeedAvg + m_vecTurnSpeed)/2.0f; float step = CTimer::GetTimeStep() * 0.003f; if(m_vecMoveSpeedAvg.MagnitudeSqr() < step*step && m_vecTurnSpeedAvg.MagnitudeSqr() < step*step){ m_nStaticFrames++; if(m_nStaticFrames > 10){ m_nStaticFrames = 10; SetIsStatic(true); m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); m_vecMoveFriction = m_vecMoveSpeed; m_vecTurnFriction = m_vecTurnSpeed; return; } }else m_nStaticFrames = 0; } } ApplyGravity(); ApplyFriction(); ApplyAirResistance(); } /* * Some quantities (german in parens): * * acceleration: distance/time^2: a * velocity: distance/time: v (GTA: speed) * momentum (impuls): velocity*mass: p * impulse (kraftstoss): delta momentum, force*time: J * * angular equivalents: * velocity -> angular velocity (GTA: turn speed) * momentum -> angular momentum (drehimpuls): L = r cross p * force -> torque (drehmoment): tau = r cross F * mass -> moment of inertia, angular mass (drehmoment, drehmasse): I = L/omega (GTA: turn mass) */ CVector CPhysical::GetSpeed(const CVector &r) { return m_vecMoveSpeed + m_vecMoveFriction + CrossProduct(m_vecTurnFriction + m_vecTurnSpeed, r); } // --MIAMI: Proof-read once void CPhysical::ApplyMoveSpeed(void) { if(bIsFrozen) m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); else GetMatrix().Translate(m_vecMoveSpeed * CTimer::GetTimeStep()); } // --MIAMI: Proof-read once void CPhysical::ApplyTurnSpeed(void) { if(bIsFrozen){ m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); }else{ // Move the coordinate axes by their speed // Note that this denormalizes the matrix CVector turnvec = m_vecTurnSpeed*CTimer::GetTimeStep(); GetRight() += CrossProduct(turnvec, GetRight()); GetForward() += CrossProduct(turnvec, GetForward()); GetUp() += CrossProduct(turnvec, GetUp()); } } // --MIAMI: Proof-read once void CPhysical::ApplyMoveForce(float jx, float jy, float jz) { m_vecMoveSpeed += CVector(jx, jy, jz)*(1.0f/m_fMass); } // --MIAMI: Proof-read once void CPhysical::ApplyTurnForce(float jx, float jy, float jz, float px, float py, float pz) { CVector com = Multiply3x3(m_matrix, m_vecCentreOfMass); CVector turnimpulse = CrossProduct(CVector(px, py, pz)-com, CVector(jx, jy, jz)); m_vecTurnSpeed += turnimpulse*(1.0f/m_fTurnMass); } void CPhysical::ApplyFrictionMoveForce(float jx, float jy, float jz) { m_vecMoveFriction += CVector(jx, jy, jz)*(1.0f/m_fMass); } // --MIAMI: Proof-read once void CPhysical::ApplyFrictionTurnForce(float jx, float jy, float jz, float px, float py, float pz) { CVector com = Multiply3x3(m_matrix, m_vecCentreOfMass); CVector turnimpulse = CrossProduct(CVector(px, py, pz)-com, CVector(jx, jy, jz)); m_vecTurnFriction += turnimpulse*(1.0f/m_fTurnMass); } // --MIAMI: Proof-read once bool CPhysical::ApplySpringCollision(float springConst, CVector &springDir, CVector &point, float springRatio, float bias) { float compression = 1.0f - springRatio; if(compression > 0.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); } return true; } // --MIAMI: Proof-read once bool CPhysical::ApplySpringCollisionAlt(float springConst, CVector &springDir, CVector &point, float springRatio, float bias, CVector &forceDir) { float compression = 1.0f - springRatio; if(compression > 0.0f){ if(DotProduct(springDir, forceDir) > 0.0f) forceDir *= -1.0f; float step = Min(CTimer::GetTimeStep(), 3.0f); float impulse = GRAVITY*m_fMass*step * springConst * compression * bias*2.0f; if(bIsHeavy) impulse *= 0.75f; ApplyMoveForce(forceDir*impulse); ApplyTurnForce(forceDir*impulse, point); } return true; } // --MIAMI: Proof-read once // What exactly is speed? bool CPhysical::ApplySpringDampening(float damping, CVector &springDir, CVector &point, CVector &speed) { float speedA = DotProduct(speed, springDir); float speedB = DotProduct(GetSpeed(point), springDir); float step = Min(CTimer::GetTimeStep(), 3.0f); float impulse = -damping * (speedA + speedB)/2.0f * m_fMass * step * 0.53f; if(bIsHeavy) impulse *= 2.0f; // what is this? float a = m_fTurnMass / ((point.MagnitudeSqr() + 1.0f) * 2.0f * m_fMass); a = Min(a, 1.0f); float b = Abs(impulse / (speedB * m_fMass)); if(a < b) impulse *= a/b; ApplyMoveForce(springDir*impulse); ApplyTurnForce(springDir*impulse, point); return true; } void CPhysical::ApplyGravity(void) { if (!bAffectedByGravity) return; #ifdef WALLCLIMB_CHEAT if (gGravityCheat && this == FindPlayerVehicle()) { static CVector gravityUp(0.0f, 0.0f, 1.0f), surfaceUp(0.0f, 0.0f, 1.0f); CVector belowCar = GetPosition() - 2.0f*GetUp(); CColPoint point; CEntity* entity; if (CWorld::ProcessLineOfSight(GetPosition(), belowCar, point, entity, true, false, false, false, false, false)) surfaceUp = point.normal; else surfaceUp = CVector(0.0f, 0.0f, 1.0f); float t = clamp(CTimer::GetTimeStep() * 0.5f, 0.05f, 0.8f); gravityUp = gravityUp * (1.0f - t) + surfaceUp * t; if (gravityUp.MagnitudeSqr() < 0.1f) gravityUp = CVector(0.0f, 0.0f, 1.0f); else gravityUp.Normalise(); m_vecMoveSpeed -= GRAVITY * CTimer::GetTimeStep() * gravityUp; return; } #endif m_vecMoveSpeed.z -= GRAVITY * CTimer::GetTimeStep(); } void CPhysical::ApplyFriction(void) { m_vecMoveSpeed += m_vecMoveFriction; m_vecTurnSpeed += m_vecTurnFriction; m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); } // --MIAMI: Proof-read once void CPhysical::ApplyAirResistance(void) { if(m_fAirResistance > 0.1f){ float f = Pow(m_fAirResistance, CTimer::GetTimeStep()); m_vecMoveSpeed *= f; m_vecTurnSpeed *= f; }else if(GetStatus() != STATUS_GHOST){ float f = Pow(1.0f/Abs(1.0f + m_fAirResistance*0.5f*m_vecMoveSpeed.MagnitudeSqr()), CTimer::GetTimeStep()); m_vecMoveSpeed *= f; m_vecTurnSpeed *= 0.99f; } } bool CPhysical::ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, float &impulseB) { float eA, eB; CPhysical *A = this; CObject *Bobj = (CObject*)B; bool foo = false; // TODO: what does this mean? bool ispedcontactA = false; bool ispedcontactB = false; float massFactorA; if(B->bPedPhysics){ massFactorA = 10.0f; if(B->IsPed() && ((CPed*)B)->m_pCurrentPhysSurface == A) ispedcontactA = true; }else massFactorA = A->bIsHeavy ? 2.0f : 1.0f; float massFactorB; if(A->bPedPhysics){ if(A->IsPed() && ((CPed*)A)->IsPlayer() && B->IsVehicle() && (B->GetStatus() == STATUS_ABANDONED || B->GetStatus() == STATUS_WRECKED || A->bHasHitWall)) massFactorB = 1.0f/(Max(B->m_fMass - 2000.0f, 0.0f)/5000.0f + 1.0f); else massFactorB = 10.0f; if(A->IsPed() && ((CPed*)A)->m_pCurrentPhysSurface == B) ispedcontactB = true; }else massFactorB = B->bIsHeavy ? 2.0f : 1.0f; if(B->bInfiniteMass && !B->m_phy_flagA08){ ispedcontactB = false; foo = true; } float speedA, speedB; if(B->GetIsStatic() && !foo){ if(A->bPedPhysics){ speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); if(speedA < 0.0f){ if(B->IsObject()){ impulseA = -speedA * A->m_fMass; impulseB = impulseA; if(impulseA > Bobj->m_fUprootLimit){ if(IsGlass(B->GetModelIndex())) CGlass::WindowRespondsToCollision(B, impulseA, A->m_vecMoveSpeed, colpoint.point, false); else if(!B->bInfiniteMass){ B->SetIsStatic(false); CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 2; CStats::PropertyDestroyed += CGeneral::GetRandomNumberInRange(30, 60); } }else{ if(IsGlass(B->GetModelIndex())) CGlass::WindowRespondsToSoftCollision(B, impulseA); if(!A->bInfiniteMass) A->ApplyMoveForce(colpoint.GetNormal() * (1.0f + A->m_fElasticity) * impulseA); return true; } }else if(!B->bInfiniteMass) B->SetIsStatic(false); if(B->bInfiniteMass){ impulseA = -speedA * A->m_fMass; impulseB = 0.0f; if(!A->bInfiniteMass) A->ApplyMoveForce(colpoint.normal*(1.0f + A->m_fElasticity)*impulseA); return true; } } }else{ CVector pointposA = colpoint.point - A->GetPosition(); speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal); if(speedA < 0.0f){ if(B->IsObject()){ if(A->bHasHitWall) eA = -1.0f; else eA = -(1.0f + A->m_fElasticity); impulseA = eA * speedA * A->GetMass(pointposA, colpoint.normal); impulseB = impulseA; if(Bobj->m_nCollisionDamageEffect && impulseA > 20.0f){ Bobj->ObjectDamage(impulseA); if(!B->bUsesCollision){ if(!A->bInfiniteMass){ A->ApplyMoveForce(colpoint.normal*0.2f*impulseA); A->ApplyTurnForce(colpoint.normal*0.2f*impulseA, pointposA); } return false; } } if((impulseA > Bobj->m_fUprootLimit || A->bIsStuck) && !B->bInfiniteMass){ if(IsGlass(B->GetModelIndex())) CGlass::WindowRespondsToCollision(B, impulseA, A->m_vecMoveSpeed, colpoint.point, false); else B->SetIsStatic(false); int16 model = B->GetModelIndex(); if(model == MI_FIRE_HYDRANT && !Bobj->bHasBeenDamaged){ CParticleObject::AddObject(POBJECT_FIRE_HYDRANT, B->GetPosition() - CVector(0.0f, 0.0f, 0.5f), true); Bobj->bHasBeenDamaged = true; }else if(model == MI_PARKINGMETER || model == MI_PARKINGMETER2){ CPickups::CreateSomeMoney(GetPosition(), CGeneral::GetRandomNumber()%100); Bobj->bHasBeenDamaged = true; }else if(B->IsObject() && !IsExplosiveThingModel(model)) Bobj->bHasBeenDamaged = true; }else{ if(IsGlass(B->GetModelIndex())) CGlass::WindowRespondsToSoftCollision(B, impulseA); CVector f = colpoint.GetNormal() * impulseA; if(A->IsVehicle() && colpoint.normal.z < 0.7f) f.z *= 0.3f; if(!A->bInfiniteMass){ A->ApplyMoveForce(f); if(!A->IsVehicle() || !CWorld::bNoMoreCollisionTorque) A->ApplyTurnForce(f, pointposA); } return true; } }else if(!B->bInfiniteMass) B->SetIsStatic(false); } } if(B->GetIsStatic()) return false; if(!B->bInfiniteMass && !B->m_phy_flagA08) B->AddToMovingList(); } // B is not static if(A->bPedPhysics && B->bPedPhysics){ // negative if A is moving towards B speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); // positive if B is moving towards A float speedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal); bool affectB = false; float mA = A->m_fMass;; float mB = B->m_fMass;; float speedSum; if(((CPed*)A)->GetPedState() == PED_FOLLOW_PATH){ affectB = true; speedSum = (2.0f*mA*speedA + mB*speedB)/(2.0f*mA + mB); }else{ speedSum = Max(speedB, 0.0f); } if(speedA < speedSum){ if(A->bHasHitWall) eA = speedSum; else eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; impulseA = (eA-speedA) * mA; if(!A->bInfiniteMass) A->ApplyMoveForce(colpoint.normal*impulseA); if(affectB && speedB < speedSum){ if(B->bHasHitWall) eB = speedSum; else eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; impulseB = -(eB-speedB) * mB; if(!B->bInfiniteMass) B->ApplyMoveForce(colpoint.normal*-impulseB); } return true; } }else if(A->bPedPhysics){ CVector pointposB = colpoint.point - B->GetPosition(); speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); speedB = DotProduct(B->GetSpeed(pointposB), colpoint.normal); float mA = A->m_fMass*massFactorA; float mB = B->GetMassTweak(pointposB, colpoint.normal, massFactorB); float speedSum; if(foo) speedSum = speedB; else speedSum = (mB*speedB + mA*speedA)/(mA + mB); if(speedA < speedSum){ if(A->bHasHitWall) eA = speedSum; else eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; if(B->bHasHitWall) eB = speedSum; else eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; impulseA = (eA - speedA) * mA; impulseB = -(eB - speedB) * mB; CVector fA = colpoint.normal*(impulseA/massFactorA); CVector fB = colpoint.normal*(-impulseB/massFactorB); if(!A->bInfiniteMass){ if(fA.z < 0.0f) fA.z = 0.0f; if(ispedcontactB){ fA.x *= 2.0f; fA.y *= 2.0f; } A->ApplyMoveForce(fA); } if(!B->bInfiniteMass && !ispedcontactB){ B->ApplyMoveForce(fB); B->ApplyTurnForce(fB, pointposB); } return true; } }else if(B->bPedPhysics){ CVector pointposA = colpoint.point - A->GetPosition(); speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal); speedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal); float mA = A->GetMassTweak(pointposA, colpoint.normal, massFactorA); float mB = B->m_fMass*massFactorB; float speedSum = (mB*speedB + mA*speedA)/(mA + mB); if(speedA < speedSum){ if(A->bHasHitWall) eA = speedSum; else eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; if(B->bHasHitWall) eB = speedSum; else eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; impulseA = (eA - speedA) * mA; impulseB = -(eB - speedB) * mB; CVector fA = colpoint.normal*(impulseA/massFactorA); CVector fB = colpoint.normal*(-impulseB/massFactorB); if(!A->bInfiniteMass && !ispedcontactA){ if(fA.z < 0.0f) fA.z = 0.0f; A->ApplyMoveForce(fA); A->ApplyTurnForce(fA, pointposA); } if(!B->bInfiniteMass){ if(fB.z < 0.0f){ fB.z = 0.0f; if(Abs(speedA) < 0.01f) fB *= 0.5f; } if(ispedcontactA){ fB.x *= 2.0f; fB.y *= 2.0f; } B->ApplyMoveForce(fB); } return true; } }else{ CVector pointposA = colpoint.point - A->GetPosition(); CVector pointposB = colpoint.point - B->GetPosition(); speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal); speedB = DotProduct(B->GetSpeed(pointposB), colpoint.normal); float mA = A->GetMassTweak(pointposA, colpoint.normal, massFactorA); float mB = B->GetMassTweak(pointposB, colpoint.normal, massFactorB); float speedSum = (mB*speedB + mA*speedA)/(mA + mB); if(speedA < speedSum){ if(A->bHasHitWall) eA = speedSum; else eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; if(B->bHasHitWall) eB = speedSum; else eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f; impulseA = (eA - speedA) * mA; impulseB = -(eB - speedB) * mB; CVector fA = colpoint.normal*(impulseA/massFactorA); CVector fB = colpoint.normal*(-impulseB/massFactorB); if(A->IsVehicle() && !A->bHasHitWall){ fA.x *= 1.4f; fA.y *= 1.4f; if(colpoint.normal.z < 0.7f) fA.z *= 0.3f; if(A->GetStatus() == STATUS_PLAYER) pointposA *= 0.8f; if(CWorld::bNoMoreCollisionTorque){ A->ApplyFrictionMoveForce(fA*-0.3f); A->ApplyFrictionTurnForce(fA*-0.3f, pointposA); } } if(B->IsVehicle() && !B->bHasHitWall){ fB.x *= 1.4f; fB.y *= 1.4f; if(-colpoint.normal.z < 0.7f) fB.z *= 0.3f; if(B->GetStatus() == STATUS_PLAYER) pointposB *= 0.8f; if(CWorld::bNoMoreCollisionTorque){ #ifdef FIX_BUGS B->ApplyFrictionMoveForce(fB*-0.3f); B->ApplyFrictionTurnForce(fB*-0.3f, pointposB); #else A->ApplyFrictionMoveForce(fB*-0.3f); A->ApplyFrictionTurnForce(fB*-0.3f, pointposB); #endif } } if(!A->bInfiniteMass){ A->ApplyMoveForce(fA); A->ApplyTurnForce(fA, pointposA); } if(!B->bInfiniteMass){ if(B->bIsInSafePosition) B->UnsetIsInSafePosition(); B->ApplyMoveForce(fB); B->ApplyTurnForce(fB, pointposB); } return true; } } return false; } bool CPhysical::ApplyCollision(CColPoint &colpoint, float &impulse) { float speed; if(bPedPhysics){ speed = DotProduct(m_vecMoveSpeed, colpoint.normal); if(speed < 0.0f){ impulse = -speed * m_fMass; ApplyMoveForce(colpoint.normal*impulse); return true; } }else{ CVector pointpos = colpoint.point - GetPosition(); speed = DotProduct(GetSpeed(pointpos), colpoint.normal); if(speed < 0.0f){ float mass = GetMass(pointpos, colpoint.normal); impulse = -(m_fElasticity + 1.0f) * speed * mass; CVector f = colpoint.normal*impulse; if(IsVehicle()){ f.x *= 1.4f; f.y *= 1.4f; if(colpoint.normal.z < 0.7f) f.z *= 0.3f; } if(!bInfiniteMass){ ApplyMoveForce(f); if(!IsVehicle() || !CWorld::bNoMoreCollisionTorque) ApplyTurnForce(f, pointpos); } return true; } } return false; } bool CPhysical::ApplyCollisionAlt(CEntity *B, CColPoint &colpoint, float &impulse, CVector &moveSpeed, CVector &turnSpeed) { float normalSpeed; CVector speed; CVector vImpulse; if(GetModelIndex() == MI_BEACHBALL && B != (CEntity*)FindPlayerPed()) ((CObject*)this)->m_nBeachballBounces = 0; if(bPedPhysics){ normalSpeed = DotProduct(m_vecMoveSpeed, colpoint.normal); if(normalSpeed < 0.0f){ impulse = -normalSpeed * m_fMass; ApplyMoveForce(colpoint.normal * impulse); return true; } }else{ CVector pointpos = colpoint.point - GetPosition(); speed = GetSpeed(pointpos); normalSpeed = DotProduct(speed, colpoint.normal); if(normalSpeed < 0.0f){ int16 elasticityType = 0; float mass = GetMass(pointpos, colpoint.normal); float minspeed = GRAVITY * CTimer::GetTimeStep(); if(IsObject()) elasticityType = 1; else if(IsVehicle() && !bIsInWater){ if(((CVehicle*)this)->IsBike() && (GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED)){ minspeed *= 1.3f; elasticityType = 3; }else if(((CVehicle*)this)->IsBoat()){ minspeed *= 1.2f; elasticityType = 4; }else if(GetUp().z < -0.3f){ minspeed *= 1.1f; elasticityType = 2; } } if(elasticityType == 1 && !bHasContacted && Abs(m_vecMoveSpeed.x) < minspeed && Abs(m_vecMoveSpeed.y) < minspeed && Abs(m_vecMoveSpeed.z) < minspeed*2.0f) impulse = -0.98f * normalSpeed * mass; if(elasticityType == 3 && Abs(m_vecMoveSpeed.x) < minspeed && Abs(m_vecMoveSpeed.y) < minspeed && Abs(m_vecMoveSpeed.z) < minspeed*2.0f) impulse = -0.8f * normalSpeed * mass; else if(elasticityType == 2 && Abs(m_vecMoveSpeed.x) < minspeed && Abs(m_vecMoveSpeed.y) < minspeed && Abs(m_vecMoveSpeed.z) < minspeed*2.0f) impulse = -0.92f * normalSpeed * mass; else if(elasticityType == 4 && Abs(m_vecMoveSpeed.x) < minspeed && Abs(m_vecMoveSpeed.y) < minspeed && Abs(m_vecMoveSpeed.z) < minspeed*2.0f) impulse = -0.8f * normalSpeed * mass; else if(IsVehicle() && ((CVehicle*)this)->IsBoat() && colpoint.surfaceB == SURFACE_WOOD_SOLID && colpoint.normal.z < 0.5f) impulse = -(2.0f * m_fElasticity + 1.0f) * normalSpeed * mass; else impulse = -(m_fElasticity + 1.0f) * normalSpeed * mass; // ApplyMoveForce vImpulse = colpoint.normal*impulse; if(IsVehicle()){ if(!bHasHitWall || !(m_vecMoveSpeed.MagnitudeSqr() > 0.1 || !(B->IsBuilding() || ((CPhysical*)B)->bInfiniteMass))) moveSpeed += vImpulse * 1.2f * (1.0f/m_fMass); else moveSpeed += vImpulse * (1.0f/m_fMass); vImpulse *= 0.8f; }else moveSpeed += vImpulse * (1.0f/m_fMass); // ApplyTurnForce CVector com = Multiply3x3(m_matrix, m_vecCentreOfMass); CVector turnimpulse = CrossProduct(pointpos-com, vImpulse); turnSpeed += turnimpulse*(1.0f/m_fTurnMass); return true; } } return false; } // --MIAMI: Proof-read once bool CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint) { CVector speedA, speedB; float normalSpeedA, normalSpeedB; CVector vOtherSpeedA, vOtherSpeedB; float fOtherSpeedA, fOtherSpeedB; float speedSum; CVector frictionDir; float impulseA, impulseB; float impulseLimit; CPhysical *A = this; if(A->bPedPhysics && B->bPedPhysics){ normalSpeedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); normalSpeedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal); vOtherSpeedA = A->m_vecMoveSpeed - colpoint.normal*normalSpeedA; vOtherSpeedB = B->m_vecMoveSpeed - colpoint.normal*normalSpeedB; fOtherSpeedA = vOtherSpeedA.Magnitude(); fOtherSpeedB = vOtherSpeedB.Magnitude(); #ifdef FIX_BUGS // division by 0 frictionDir = vOtherSpeedA; frictionDir.Normalise(); #else frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA); #endif speedSum = (B->m_fMass*fOtherSpeedB + A->m_fMass*fOtherSpeedA)/(B->m_fMass + A->m_fMass); if(fOtherSpeedA > speedSum){ impulseA = (speedSum - fOtherSpeedA) * A->m_fMass; impulseB = (speedSum - fOtherSpeedB) * B->m_fMass; impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); if(impulseA < -impulseLimit) impulseA = -impulseLimit; #ifdef FIX_BUGS if(impulseB > impulseLimit) impulseB = impulseLimit; #else if(impulseA < -impulseLimit) impulseA = -impulseLimit; // duplicate #endif A->ApplyFrictionMoveForce(frictionDir*impulseA); B->ApplyFrictionMoveForce(frictionDir*impulseB); return true; } }else if(A->bPedPhysics){ if(B->IsVehicle()) return false; CVector pointposB = colpoint.point - B->GetPosition(); speedB = B->GetSpeed(pointposB); normalSpeedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal); normalSpeedB = DotProduct(speedB, colpoint.normal); vOtherSpeedA = A->m_vecMoveSpeed - colpoint.normal*normalSpeedA; vOtherSpeedB = speedB - colpoint.normal*normalSpeedB; fOtherSpeedA = vOtherSpeedA.Magnitude(); fOtherSpeedB = vOtherSpeedB.Magnitude(); #ifdef FIX_BUGS // division by 0 frictionDir = vOtherSpeedA; frictionDir.Normalise(); #else frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA); #endif float massB = B->GetMass(pointposB, frictionDir); speedSum = (massB*fOtherSpeedB + A->m_fMass*fOtherSpeedA)/(massB + A->m_fMass); if(fOtherSpeedA > speedSum){ impulseA = (speedSum - fOtherSpeedA) * A->m_fMass; impulseB = (speedSum - fOtherSpeedB) * massB; impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseB > impulseLimit) impulseB = impulseLimit; A->ApplyFrictionMoveForce(frictionDir*impulseA); B->ApplyFrictionMoveForce(frictionDir*impulseB); B->ApplyFrictionTurnForce(frictionDir*impulseB, pointposB); return true; } }else if(B->bPedPhysics){ if(A->IsVehicle()) return false; CVector pointposA = colpoint.point - A->GetPosition(); speedA = A->GetSpeed(pointposA); normalSpeedA = DotProduct(speedA, colpoint.normal); normalSpeedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal); vOtherSpeedA = speedA - colpoint.normal*normalSpeedA; vOtherSpeedB = B->m_vecMoveSpeed - colpoint.normal*normalSpeedB; fOtherSpeedA = vOtherSpeedA.Magnitude(); fOtherSpeedB = vOtherSpeedB.Magnitude(); #ifdef FIX_BUGS // division by 0 frictionDir = vOtherSpeedA; frictionDir.Normalise(); #else frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA); #endif float massA = A->GetMass(pointposA, frictionDir); speedSum = (B->m_fMass*fOtherSpeedB + massA*fOtherSpeedA)/(B->m_fMass + massA); if(fOtherSpeedA > speedSum){ impulseA = (speedSum - fOtherSpeedA) * massA; impulseB = (speedSum - fOtherSpeedB) * B->m_fMass; impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseB > impulseLimit) impulseB = impulseLimit; A->ApplyFrictionMoveForce(frictionDir*impulseA); A->ApplyFrictionTurnForce(frictionDir*impulseA, pointposA); B->ApplyFrictionMoveForce(frictionDir*impulseB); return true; } }else{ CVector pointposA = colpoint.point - A->GetPosition(); CVector pointposB = colpoint.point - B->GetPosition(); speedA = A->GetSpeed(pointposA); speedB = B->GetSpeed(pointposB); normalSpeedA = DotProduct(speedA, colpoint.normal); normalSpeedB = DotProduct(speedB, colpoint.normal); vOtherSpeedA = speedA - colpoint.normal*normalSpeedA; vOtherSpeedB = speedB - colpoint.normal*normalSpeedB; fOtherSpeedA = vOtherSpeedA.Magnitude(); fOtherSpeedB = vOtherSpeedB.Magnitude(); #ifdef FIX_BUGS // division by 0 frictionDir = vOtherSpeedA; frictionDir.Normalise(); #else frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA); #endif float massA = A->GetMass(pointposA, frictionDir); float massB = B->GetMass(pointposB, frictionDir); speedSum = (massB*fOtherSpeedB + massA*fOtherSpeedA)/(massB + massA); if(fOtherSpeedA > speedSum){ impulseA = (speedSum - fOtherSpeedA) * massA; impulseB = (speedSum - fOtherSpeedB) * massB; impulseLimit = adhesiveLimit*CTimer::GetTimeStep(); if(impulseA < -impulseLimit) impulseA = -impulseLimit; if(impulseB > impulseLimit) impulseB = impulseLimit; A->ApplyFrictionMoveForce(frictionDir*impulseA); A->ApplyFrictionTurnForce(frictionDir*impulseA, pointposA); B->ApplyFrictionMoveForce(frictionDir*impulseB); B->ApplyFrictionTurnForce(frictionDir*impulseB, pointposB); return true; } } return false; } // --MIAMI: Proof-read once bool CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint) { CVector speed; float normalSpeed; CVector vOtherSpeed; float fOtherSpeed; CVector frictionDir; float fImpulse; float impulseLimit; if(bPedPhysics){ normalSpeed = DotProduct(m_vecMoveSpeed, colpoint.normal); vOtherSpeed = m_vecMoveSpeed - colpoint.normal*normalSpeed; fOtherSpeed = vOtherSpeed.Magnitude(); if(fOtherSpeed > 0.0f){ #ifdef FIX_BUGS // division by 0 frictionDir = vOtherSpeed; frictionDir.Normalise(); #else frictionDir = vOtherSpeed * (1.0f/fOtherSpeed); #endif // not really impulse but speed // maybe use ApplyFrictionMoveForce instead? fImpulse = -fOtherSpeed; impulseLimit = adhesiveLimit*CTimer::GetTimeStep() / m_fMass; if(fImpulse < -impulseLimit) fImpulse = -impulseLimit; CVector vImpulse = frictionDir*fImpulse; m_vecMoveFriction += CVector(vImpulse.x, vImpulse.y, 0.0f); return true; } }else{ CVector pointpos = colpoint.point - GetPosition(); speed = GetSpeed(pointpos); normalSpeed = DotProduct(speed, colpoint.normal); vOtherSpeed = speed - colpoint.normal*normalSpeed; fOtherSpeed = vOtherSpeed.Magnitude(); if(fOtherSpeed > 0.0f){ #ifdef FIX_BUGS // division by 0 frictionDir = vOtherSpeed; frictionDir.Normalise(); #else frictionDir = vOtherSpeed * (1.0f/fOtherSpeed); #endif fImpulse = -fOtherSpeed * m_fMass; impulseLimit = adhesiveLimit*CTimer::GetTimeStep() * 1.5; if(fImpulse < -impulseLimit) fImpulse = -impulseLimit; ApplyFrictionMoveForce(frictionDir*fImpulse); ApplyFrictionTurnForce(frictionDir*fImpulse, pointpos); if(fOtherSpeed > 0.1f && colpoint.surfaceB != SURFACE_GRASS && colpoint.surfaceB != SURFACE_MUD_DRY && CSurfaceTable::GetAdhesionGroup(colpoint.surfaceA) == ADHESIVE_HARD){ CVector v = frictionDir * fOtherSpeed * 0.25f; for(int i = 0; i < 4; i++) CParticle::AddParticle(PARTICLE_SPARK_SMALL, colpoint.point, v); } return true; } } return false; } // --MIAMI: Proof-read once bool CPhysical::ProcessShiftSectorList(CPtrList *lists) { int i, j; CPtrList *list; CPtrNode *node; CPhysical *A, *B; CObject *Bobj; bool canshift; CVector center; float radius; int numCollisions; int mostColliding; CColPoint colpoints[MAX_COLLISION_POINTS]; CVector shift = CVector(0.0f, 0.0f, 0.0f); bool doShift = false; CEntity *boat = nil; bool skipShift; A = this; A->GetBoundCentre(center); radius = A->GetBoundRadius(); for(i = 0; i <= ENTITYLIST_PEDS_OVERLAP; i++){ list = &lists[i]; for(node = list->first; node; node = node->next){ B = (CPhysical*)node->item; Bobj = (CObject*)B; skipShift = false; if(B->IsBuilding() || B->IsObject() && B->bInfiniteMass) canshift = true; else canshift = A->IsPed() && B->IsObject() && B->GetIsStatic() && !Bobj->bHasBeenDamaged; if(B == A || B->m_scanCode == CWorld::GetCurrentScanCode() || !B->bUsesCollision || (A->bHasHitWall && !canshift) || !B->GetIsTouching(center, radius)) continue; // This could perhaps be done a bit nicer if(B->IsBuilding()) skipShift = false; else if(IsLightWithoutShift(A->GetModelIndex()) && (B->IsVehicle() || B->IsPed()) && A->GetUp().z < 0.66f) skipShift = true; else if((A->IsVehicle() || A->IsPed()) && B->GetUp().z < 0.66f && IsLightWithoutShift(B->GetModelIndex())) skipShift = true; else if(A->IsObject() && B->IsVehicle()){ CObject *Aobj = (CObject*)A; if(Aobj->ObjectCreatedBy != TEMP_OBJECT && !Aobj->bHasBeenDamaged && Aobj->GetIsStatic()){ if(Aobj->m_pCollidingEntity == B) Aobj->m_pCollidingEntity = nil; }else if(Aobj->m_pCollidingEntity != B){ CMatrix inv; CVector size = CModelInfo::GetModelInfo(A->GetModelIndex())->GetColModel()->boundingBox.GetSize(); size = A->GetMatrix() * size; if(size.z < B->GetPosition().z || (Invert(B->GetMatrix(), inv) * size).z < 0.0f){ skipShift = true; Aobj->m_pCollidingEntity = B; } } else skipShift = true; }else if(B->IsObject() && A->IsVehicle()){ CObject *Bobj = (CObject*)B; if(Bobj->ObjectCreatedBy != TEMP_OBJECT && !Bobj->bHasBeenDamaged && Bobj->GetIsStatic()){ if(Bobj->m_pCollidingEntity == A) Bobj->m_pCollidingEntity = nil; }else if(Bobj->m_pCollidingEntity != A){ CMatrix inv; CVector size = CModelInfo::GetModelInfo(B->GetModelIndex())->GetColModel()->boundingBox.GetSize(); size = B->GetMatrix() * size; if(size.z < A->GetPosition().z || (Invert(A->GetMatrix(), inv) * size).z < 0.0f) skipShift = true; } else skipShift = true; }else if(IsBodyPart(A->GetModelIndex()) && B->IsPed()) skipShift = true; else if(A->IsPed() && IsBodyPart(B->GetModelIndex())) skipShift = true; else if(A->IsPed() && ((CPed*)A)->m_pCollidingEntity == B || B->IsPed() && ((CPed*)B)->m_pCollidingEntity == A || A->GetModelIndex() == MI_RCBANDIT && B->IsVehicle() || B->GetModelIndex() == MI_RCBANDIT && (A->IsPed() || A->IsVehicle())) skipShift = true; if(skipShift) continue; B->m_scanCode = CWorld::GetCurrentScanCode(); numCollisions = A->ProcessEntityCollision(B, colpoints); if(numCollisions <= 0) continue; mostColliding = 0; for(j = 1; j < numCollisions; j++) if (colpoints[j].GetDepth() > colpoints[mostColliding].GetDepth()) mostColliding = j; if(CWorld::bSecondShift) for(j = 0; j < numCollisions; j++) shift += colpoints[j].GetNormal() * colpoints[j].GetDepth() * 1.5f / numCollisions; else for(j = 0; j < numCollisions; j++) shift += colpoints[j].GetNormal() * colpoints[j].GetDepth() * 1.2f / numCollisions; if(A->IsVehicle() && B->IsVehicle()){ 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)); shift += dir * colpoints[mostColliding].GetDepth() * 0.5f; }else if(A->IsPed() && B->IsVehicle() && ((CVehicle*)B)->IsBoat()){ CVector dir = colpoints[mostColliding].GetNormal(); float f = Min(Abs(dir.z), 0.9f); dir.z = 0.0f; dir.Normalise(); shift += dir * colpoints[mostColliding].GetDepth() / (1.0f - f); boat = B; }else if(B->IsPed() && A->IsVehicle() && ((CVehicle*)A)->IsBoat()){ CVector dir = colpoints[mostColliding].GetNormal() * -1.0f; float f = Min(Abs(dir.z), 0.9f); dir.z = 0.0f; dir.Normalise(); B->GetMatrix().Translate(dir * colpoints[mostColliding].GetDepth() / (1.0f - f)); // BUG? how can that ever happen? A is a Ped if(B->IsVehicle()) B->ProcessEntityCollision(A, colpoints); }else{ if(CWorld::bSecondShift) shift += colpoints[mostColliding].GetNormal() * colpoints[mostColliding].GetDepth() * 0.4f; else shift += colpoints[mostColliding].GetNormal() * colpoints[mostColliding].GetDepth() * 0.2f; } doShift = true; } } if(!doShift) return false; GetMatrix().Translate(shift); if(boat) ProcessEntityCollision(boat, colpoints); return true; } // --MIAMI: Proof-read once bool CPhysical::ProcessCollisionSectorList_SimpleCar(CPtrList *lists) { static CColPoint aColPoints[MAX_COLLISION_POINTS]; float radius; CVector center; int listtype; CPhysical *A, *B; int numCollisions; int i; float impulseA = -1.0f; float impulseB = -1.0f; A = (CPhysical*)this; if(!A->bUsesCollision) return false; radius = A->GetBoundRadius(); A->GetBoundCentre(center); for(listtype = 3; listtype >= 0; listtype--){ // Go through vehicles and objects CPtrList *list; switch(listtype){ case 0: list = &lists[ENTITYLIST_VEHICLES]; break; case 1: list = &lists[ENTITYLIST_VEHICLES_OVERLAP]; break; case 2: list = &lists[ENTITYLIST_OBJECTS]; break; case 3: list = &lists[ENTITYLIST_OBJECTS_OVERLAP]; break; } // Find first collision in list CPtrNode *listnode; for(listnode = list->first; listnode; listnode = listnode->next){ B = (CPhysical*)listnode->item; if(B != A && !(B->IsObject() && ((CObject*)B)->bIsStreetLight && B->GetUp().z < 0.66f) && B->m_scanCode != CWorld::GetCurrentScanCode() && B->bUsesCollision && B->GetIsTouching(center, radius)){ B->m_scanCode = CWorld::GetCurrentScanCode(); numCollisions = A->ProcessEntityCollision(B, aColPoints); if(numCollisions > 0) goto collision; } } } // no collision return false; collision: if(A->bHasContacted && B->bHasContacted){ for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) continue; if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(impulseB > B->m_fDamageImpulse) B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); 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)); } }else if(A->bHasContacted){ CVector savedMoveFriction = A->m_vecMoveFriction; CVector savedTurnFriction = A->m_vecTurnFriction; A->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); A->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); A->bHasContacted = false; for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) continue; if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(impulseB > B->m_fDamageImpulse) B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); 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)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; B->bHasContacted = true; } } if(!A->bHasContacted){ A->bHasContacted = true; A->m_vecMoveFriction = savedMoveFriction; A->m_vecTurnFriction = savedTurnFriction; } }else if(B->bHasContacted){ CVector savedMoveFriction = B->m_vecMoveFriction; CVector savedTurnFriction = B->m_vecTurnFriction; B->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); B->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); B->bHasContacted = false; for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) continue; if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(impulseB > B->m_fDamageImpulse) B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); 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)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; B->bHasContacted = true; } } if(!B->bHasContacted){ B->bHasContacted = true; B->m_vecMoveFriction = savedMoveFriction; B->m_vecTurnFriction = savedTurnFriction; } }else{ for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) continue; if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(impulseB > B->m_fDamageImpulse) B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); 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)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; B->bHasContacted = true; } } } if(B->GetStatus() == STATUS_SIMPLE){ B->SetStatus(STATUS_PHYSICS); if(B->IsVehicle()) CCarCtrl::SwitchVehicleToRealPhysics((CVehicle*)B); } return true; } // --MIAMI: Proof-read once bool CPhysical::ProcessCollisionSectorList(CPtrList *lists) { static CColPoint aColPoints[MAX_COLLISION_POINTS]; float radius; CVector center; CPtrList *list; CPhysical *A, *B; CObject *Aobj, *Bobj; CPed *Aped, *Bped; int numCollisions; int numResponses; int i, j; bool skipCollision, altcollision; bool ret = false; float impulseA = -1.0f; float impulseB = -1.0f; A = (CPhysical*)this; Aobj = (CObject*)A; Aped = (CPed*)A; radius = A->GetBoundRadius(); A->GetBoundCentre(center); for(j = 0; j <= ENTITYLIST_PEDS_OVERLAP; j++){ list = &lists[j]; CPtrNode *listnode; for(listnode = list->first; listnode; listnode = listnode->next){ B = (CPhysical*)listnode->item; Bobj = (CObject*)B; Bped = (CPed*)B; bool isTouching = true; if(!B->bUsesCollision || B->m_scanCode == CWorld::GetCurrentScanCode() || B == A || !(isTouching = B->GetIsTouching(center, radius))){ if(!isTouching){ if(A->IsObject() && Aobj->m_pCollidingEntity == B) Aobj->m_pCollidingEntity = nil; else if(B->IsObject() && Bobj->m_pCollidingEntity == A) Bobj->m_pCollidingEntity = nil; else if(A->IsPed() && Aped->m_pCollidingEntity == B) Aped->m_pCollidingEntity = nil; else if(B->IsPed() && Bped->m_pCollidingEntity == A) Bped->m_pCollidingEntity = nil; } continue; } A->bSkipLineCol = false; skipCollision = false; altcollision = false; if(B->IsBuilding()) skipCollision = false; else if(A->IsObject() && Aobj->bIsStreetLight && (B->IsVehicle() || B->IsPed()) && A->GetUp().z < 0.66f){ skipCollision = true; A->bSkipLineCol = true; Aobj->m_pCollidingEntity = B; }else if(B->IsObject() && Bobj->bIsStreetLight && (A->IsVehicle() || A->IsPed()) && B->GetUp().z < 0.66f){ skipCollision = true; A->bSkipLineCol = true; Bobj->m_pCollidingEntity = A; }else if(A->IsObject() && B->IsVehicle()){ if(A->GetModelIndex() == MI_CAR_BUMPER) skipCollision = true; else if(Aobj->ObjectCreatedBy == TEMP_OBJECT || Aobj->bHasBeenDamaged || !Aobj->GetIsStatic()){ if(Aobj->m_pCollidingEntity == B) skipCollision = true; else if(Aobj->m_nCollisionDamageEffect < DAMAGE_EFFECT_SMASH_COMPLETELY){ CMatrix inv; CVector size = CModelInfo::GetModelInfo(A->GetModelIndex())->GetColModel()->boundingBox.GetSize(); size = A->GetMatrix() * size; if(size.z < B->GetPosition().z || (Invert(B->GetMatrix(), inv) * size).z < 0.0f){ skipCollision = true; Aobj->m_pCollidingEntity = B; } } } }else if(B->IsObject() && A->IsVehicle()){ if(B->GetModelIndex() == MI_CAR_BUMPER) skipCollision = true; else if(Bobj->ObjectCreatedBy == TEMP_OBJECT || Bobj->bHasBeenDamaged || !Bobj->GetIsStatic()){ if(Bobj->m_pCollidingEntity == A) skipCollision = true; else if(Bobj->m_nCollisionDamageEffect < DAMAGE_EFFECT_SMASH_COMPLETELY){ CMatrix inv; CVector size = CModelInfo::GetModelInfo(B->GetModelIndex())->GetColModel()->boundingBox.GetSize(); size = B->GetMatrix() * size; if(size.z < A->GetPosition().z || (Invert(A->GetMatrix(), inv) * size).z < 0.0f){ skipCollision = true; } } } }else if(A->GetModelIndex() == MI_GRENADE && B->IsPed() && A->GetPosition().z < B->GetPosition().z){ skipCollision = true; }else if(B->GetModelIndex() == MI_GRENADE && A->IsPed() && B->GetPosition().z < A->GetPosition().z){ skipCollision = true; A->bSkipLineCol = true; }else if(A->IsPed() && Aped->m_pCollidingEntity == B){ skipCollision = true; if(!Aped->bKnockedUpIntoAir || Aped->bKnockedOffBike) A->bSkipLineCol = true; }else if(B->IsPed() && Bped->m_pCollidingEntity == A){ skipCollision = true; A->bSkipLineCol = true; }else if(A->GetModelIndex() == MI_RCBANDIT && (B->IsPed() || B->IsVehicle()) || B->GetModelIndex() == MI_RCBANDIT && (A->IsPed() || A->IsVehicle())){ skipCollision = true; A->bSkipLineCol = true; }else if(A->IsPed() && B->IsObject() && Bobj->m_fUprootLimit > 0.0f) altcollision = true; if(!A->bUsesCollision || skipCollision){ B->m_scanCode = CWorld::GetCurrentScanCode(); numCollisions = A->ProcessEntityCollision(B, aColPoints); if(A->bJustCheckCollision && numCollisions > 0) return true; if(numCollisions == 0 && A == (CEntity*)FindPlayerPed() && Aped->m_pCollidingEntity == B) Aped->m_pCollidingEntity = nil; }else if(B->IsBuilding() || B->bIsStuck || B->m_phy_flagA08 || altcollision){ // This is the case where B doesn't move B->m_scanCode = CWorld::GetCurrentScanCode(); numCollisions = A->ProcessEntityCollision(B, aColPoints); if(numCollisions <= 0) continue; CVector moveSpeed = CVector(0.0f, 0.0f, 0.0f); CVector turnSpeed = CVector(0.0f, 0.0f, 0.0f); float maxImpulseA = 0.0f; numResponses = 0; if(A->bHasContacted){ for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollisionAlt(B, aColPoints[i], impulseA, moveSpeed, turnSpeed)) continue; numResponses++; if(impulseA > maxImpulseA) maxImpulseA = impulseA; if(A->IsVehicle()){ if(!(((CVehicle*)A)->IsBoat() && aColPoints[i].surfaceB == SURFACE_WOOD_SOLID) && impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_SAND) aColPoints[i].surfaceB = SURFACE_SAND; float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); if(A->GetUp().z < -0.6f && Abs(A->m_vecMoveSpeed.x) < 0.05f && Abs(A->m_vecMoveSpeed.y) < 0.05f) DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, 0.1f*impulseA, Max(turnSpeedDiff, moveSpeedDiff)); else DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); }else{ if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); } } }else{ for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollisionAlt(B, aColPoints[i], impulseA, moveSpeed, turnSpeed)) continue; numResponses++; if(impulseA > maxImpulseA) maxImpulseA = impulseA; float adhesion = CSurfaceTable::GetAdhesiveLimit(aColPoints[i]) / numCollisions; if(A->IsVehicle()){ if(((CVehicle*)A)->IsBoat() && aColPoints[i].surfaceB == SURFACE_WOOD_SOLID) adhesion = 0.0f; else if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_SAND) aColPoints[i].surfaceB = SURFACE_SAND; float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); if(A->GetUp().z < -0.6f && Abs(A->m_vecMoveSpeed.x) < 0.05f && Abs(A->m_vecMoveSpeed.y) < 0.05f) DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, 0.1f*impulseA, Max(turnSpeedDiff, moveSpeedDiff)); else DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); if(A->GetModelIndex() == MI_RCBANDIT) adhesion *= 0.2f; else if(((CVehicle*)A)->IsBoat()){ if(aColPoints[i].normal.z > 0.6f){ if(CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_LOOSE || CSurfaceTable::GetAdhesionGroup(aColPoints[i].surfaceB) == ADHESIVE_SAND) adhesion *= 3.0f; }else adhesion = 0.0f; }else if(A->GetStatus() == STATUS_WRECKED) adhesion *= 3.0f; else if(A->GetUp().z > 0.3f) adhesion = 0.0f; else adhesion *= Min(5.0f, 0.03f*impulseA + 1.0f); }else{ if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); float turnSpeedDiff = A->m_vecTurnSpeed.MagnitudeSqr(); float moveSpeedDiff = A->m_vecMoveSpeed.MagnitudeSqr(); DMAudio.ReportCollision(A, B, aColPoints[i].surfaceA, aColPoints[i].surfaceB, impulseA, Max(turnSpeedDiff, moveSpeedDiff)); } if(A->ApplyFriction(adhesion, aColPoints[i])) A->bHasContacted = true; } } if(numResponses){ m_vecMoveSpeed += moveSpeed / numResponses; m_vecTurnSpeed += turnSpeed / numResponses; if(!CWorld::bNoMoreCollisionTorque && A->GetStatus() == STATUS_PLAYER && A->IsVehicle() && Abs(A->m_vecMoveSpeed.x) > 0.2f && Abs(A->m_vecMoveSpeed.y) > 0.2f){ A->m_vecMoveFriction.x += moveSpeed.x * -0.3f / numCollisions; A->m_vecMoveFriction.y += moveSpeed.y * -0.3f / numCollisions; A->m_vecTurnFriction += turnSpeed * -0.3f / numCollisions; } if(B->IsObject() && Bobj->m_nCollisionDamageEffect && maxImpulseA > 20.0f) Bobj->ObjectDamage(maxImpulseA); if(!CWorld::bSecondShift) return true; ret = true; } }else{ // B can move B->m_scanCode = CWorld::GetCurrentScanCode(); numCollisions = A->ProcessEntityCollision(B, aColPoints); if(numCollisions <= 0) continue; float maxImpulseA = 0.0f; float maxImpulseB = 0.0f; if(A->bHasContacted && B->bHasContacted){ for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) continue; if(impulseA > maxImpulseA) maxImpulseA = impulseA; if(impulseB > maxImpulseB) maxImpulseB = impulseB; if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(impulseB > B->m_fDamageImpulse) B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); 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)); } }else if(A->bHasContacted){ CVector savedMoveFriction = A->m_vecMoveFriction; CVector savedTurnFriction = A->m_vecTurnFriction; A->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); A->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); A->bHasContacted = false; for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) continue; if(impulseA > maxImpulseA) maxImpulseA = impulseA; if(impulseB > maxImpulseB) maxImpulseB = impulseB; if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(impulseB > B->m_fDamageImpulse) B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); 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)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; B->bHasContacted = true; } } if(!A->bHasContacted){ A->bHasContacted = true; A->m_vecMoveFriction = savedMoveFriction; A->m_vecTurnFriction = savedTurnFriction; } }else if(B->bHasContacted){ CVector savedMoveFriction = B->m_vecMoveFriction; CVector savedTurnFriction = B->m_vecTurnFriction; B->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); B->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); B->bHasContacted = false; for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) continue; if(impulseA > maxImpulseA) maxImpulseA = impulseA; if(impulseB > maxImpulseB) maxImpulseB = impulseB; if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(impulseB > B->m_fDamageImpulse) B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); 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)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; B->bHasContacted = true; } } if(!B->bHasContacted){ B->bHasContacted = true; B->m_vecMoveFriction = savedMoveFriction; B->m_vecTurnFriction = savedTurnFriction; } }else{ for(i = 0; i < numCollisions; i++){ if(!A->ApplyCollision(B, aColPoints[i], impulseA, impulseB)) continue; if(impulseA > maxImpulseA) maxImpulseA = impulseA; if(impulseB > maxImpulseB) maxImpulseB = impulseB; if(impulseA > A->m_fDamageImpulse) A->SetDamagedPieceRecord(aColPoints[i].pieceA, impulseA, B, aColPoints[i].normal); if(impulseB > B->m_fDamageImpulse) B->SetDamagedPieceRecord(aColPoints[i].pieceB, impulseB, A, aColPoints[i].normal); 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)); if(A->ApplyFriction(B, CSurfaceTable::GetAdhesiveLimit(aColPoints[i])/numCollisions, aColPoints[i])){ A->bHasContacted = true; B->bHasContacted = true; } } } if(B->IsPed() && A->IsVehicle() && (!Bped->IsPlayer() || B->bHasHitWall && A->m_vecMoveSpeed.MagnitudeSqr() > SQR(0.05f))) Bped->KillPedWithCar((CVehicle*)A, maxImpulseB); else if(B->GetModelIndex() == MI_TRAIN && A->IsPed() && (!Aped->IsPlayer() || A->bHasHitWall)) Aped->KillPedWithCar((CVehicle*)B, maxImpulseA*2.0f); else if(B->IsObject() && B->bUsesCollision && A->IsVehicle()){ // BUG? not impulseA? if(Bobj->m_nCollisionDamageEffect && maxImpulseB > 20.0f) Bobj->ObjectDamage(maxImpulseB); else if(Bobj->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY){ CMatrix inv; CVector size = CModelInfo::GetModelInfo(B->GetModelIndex())->GetColModel()->boundingBox.GetSize(); size = B->GetMatrix() * size; if(size.z < A->GetPosition().z || (Invert(A->GetMatrix(), inv) * size).z < 0.0f) Bobj->ObjectDamage(50.0f); } }else if(A->IsObject() && A->bUsesCollision && B->IsVehicle()){ if(Aobj->m_nCollisionDamageEffect && maxImpulseB > 20.0f) Aobj->ObjectDamage(maxImpulseB); #ifdef FIX_BUGS else if(Aobj->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY){ #else else if(Bobj->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY){ #endif CMatrix inv; CVector size = CModelInfo::GetModelInfo(A->GetModelIndex())->GetColModel()->boundingBox.GetSize(); size = A->GetMatrix() * size; if(size.z < B->GetPosition().z || (Invert(B->GetMatrix(), inv) * size).z < 0.0f) Aobj->ObjectDamage(50.0f); } } if(B->GetStatus() == STATUS_SIMPLE){ B->SetStatus(STATUS_PHYSICS); if(B->IsVehicle()) CCarCtrl::SwitchVehicleToRealPhysics((CVehicle*)B); } if(!CWorld::bSecondShift) return true; ret = true; } } } return ret; } bool CPhysical::CheckCollision(void) { CEntryInfoNode *node; bCollisionProcessed = false; CWorld::AdvanceCurrentScanCode(); for(node = m_entryInfoList.first; node; node = node->next) if(ProcessCollisionSectorList(node->sector->m_lists)) return true; return false; } bool CPhysical::CheckCollision_SimpleCar(void) { CEntryInfoNode *node; bCollisionProcessed = false; CWorld::AdvanceCurrentScanCode(); for(node = m_entryInfoList.first; node; node = node->next) if(ProcessCollisionSectorList_SimpleCar(node->sector->m_lists)) return true; return false; } float PHYSICAL_SHIFT_SPEED_DAMP = 0.707f; // --MIAMI: Proof-read once void CPhysical::ProcessShift(void) { m_fDistanceTravelled = 0.0f; if(GetStatus() == STATUS_SIMPLE){ bIsStuck = false; bIsInSafePosition = true; RemoveAndAdd(); }else{ CPhysical *surf; if(bHasHitWall && (IsPed() && (surf = ((CPed*)this)->m_pCurrentPhysSurface, surf == nil || !surf->bInfiniteMass || surf->m_phy_flagA08) || CWorld::bSecondShift)){ m_vecMoveSpeed *= Pow(PHYSICAL_SHIFT_SPEED_DAMP, CTimer::GetTimeStep()); m_vecTurnSpeed *= Pow(PHYSICAL_SHIFT_SPEED_DAMP, CTimer::GetTimeStep()); } CMatrix matrix(GetMatrix()); ApplyMoveSpeed(); ApplyTurnSpeed(); GetMatrix().Reorthogonalise(); CWorld::AdvanceCurrentScanCode(); if(IsVehicle()) m_bIsVehicleBeingShifted = true; CEntryInfoNode *node; bool hasshifted = false; for(node = m_entryInfoList.first; node; node = node->next) hasshifted |= ProcessShiftSectorList(node->sector->m_lists); m_bIsVehicleBeingShifted = false; if(hasshifted){ CWorld::AdvanceCurrentScanCode(); bool hadCollision = false; for(node = m_entryInfoList.first; node; node = node->next) if(ProcessCollisionSectorList(node->sector->m_lists)){ if(!CWorld::bSecondShift){ GetMatrix() = matrix; return; } hadCollision = true; } if(hadCollision){ GetMatrix() = matrix; return; } } bIsStuck = false; bIsInSafePosition = true; m_fDistanceTravelled = (GetPosition() - matrix.GetPosition()).Magnitude(); RemoveAndAdd(); } } // x is the number of units (m) we would like to step #define NUMSTEPS(x) ceil(Sqrt(distSq) * (1.0f/(x))) float HIGHSPEED_ELASTICITY_MULT_PED = 2.0f; float HIGHSPEED_ELASTICITY_MULT_COPCAR = 2.0f; void CPhysical::ProcessCollision(void) { int i; CPed *ped = (CPed*)this; m_fDistanceTravelled = 0.0f; m_bIsVehicleBeingShifted = false; bSkipLineCol = false; if(!bUsesCollision){ bIsStuck = false; bIsInSafePosition = true; RemoveAndAdd(); return; } if(GetStatus() == STATUS_SIMPLE){ if(CheckCollision_SimpleCar() && GetStatus() == STATUS_SIMPLE){ SetStatus(STATUS_PHYSICS); if(IsVehicle()) CCarCtrl::SwitchVehicleToRealPhysics((CVehicle*)this); } bIsStuck = false; bIsInSafePosition = true; RemoveAndAdd(); return; } // Save current state CMatrix savedMatrix(GetMatrix()); float savedElasticity = m_fElasticity; CVector savedMoveSpeed = m_vecMoveSpeed; float savedTimeStep = CTimer::GetTimeStep(); int8 n = 1; // The number of steps we divide the time step into float step = 0.0f; // divided time step float distSq = m_vecMoveSpeed.MagnitudeSqr() * sq(CTimer::GetTimeStep()); if(IsPed() && (distSq >= sq(0.3f) || ped->IsPlayer())){ if(ped->IsPlayer()){ if(ped->m_pCurrentPhysSurface) n = Max(NUMSTEPS(0.15f), 4.0f); else n = Max(NUMSTEPS(0.3f), 2.0f); }else n = NUMSTEPS(0.45f); step = savedTimeStep / n; if(!ped->IsPlayer()) ped->m_fElasticity *= HIGHSPEED_ELASTICITY_MULT_PED; }else if(IsVehicle() && distSq >= sq(0.4f)){ if(GetStatus() == STATUS_PLAYER) n = NUMSTEPS(0.2f); else n = distSq > 0.32f ? NUMSTEPS(0.3f) : NUMSTEPS(0.4f); step = savedTimeStep / n; CVector bbox = GetColModel()->boundingBox.GetSize(); float relDistX = Abs(DotProduct(m_vecMoveSpeed, GetRight())) * CTimer::GetTimeStep() / bbox.x; float relDistY = Abs(DotProduct(m_vecMoveSpeed, GetForward())) * CTimer::GetTimeStep() / bbox.y; float relDistZ = Abs(DotProduct(m_vecMoveSpeed, GetUp())) * CTimer::GetTimeStep() / bbox.z; if(Max(relDistX, Max(relDistY, relDistZ)) < 1.0f){ // check if we can get away with simplified processing ApplyMoveSpeed(); ApplyTurnSpeed(); GetMatrix().Reorthogonalise(); bSkipLineCol = false; m_bIsVehicleBeingShifted = false; bJustCheckCollision = true; bUsesCollision = false; if(!CheckCollision()){ bJustCheckCollision = false; bUsesCollision = true; if(IsVehicle()) ((CVehicle*)this)->bVehicleColProcessed = true; bHitByTrain = false; m_fDistanceTravelled = (GetPosition() - savedMatrix.GetPosition()).Magnitude(); bSkipLineCol = false; bIsStuck = false; bIsInSafePosition = true; m_fElasticity = savedElasticity; RemoveAndAdd(); return; } bJustCheckCollision = false; bUsesCollision = true; GetMatrix() = savedMatrix; m_vecMoveSpeed = savedMoveSpeed; if(IsVehicle() && ((CVehicle*)this)->bIsLawEnforcer) m_fElasticity *= HIGHSPEED_ELASTICITY_MULT_COPCAR; } }else if(IsObject() && ((CObject*)this)->ObjectCreatedBy != TEMP_OBJECT){ int responsecase = ((CObject*)this)->m_nSpecialCollisionResponseCases; if(responsecase == COLLRESPONSE_LAMPOST){ CVector speedUp = CVector(0.0f, 0.0f, 0.0f); CVector speedDown = CVector(0.0f, 0.0f, 0.0f); CColModel *colModel = GetColModel(); speedUp.z = colModel->boundingBox.max.z; speedDown.z = colModel->boundingBox.min.z; speedUp = Multiply3x3(GetMatrix(), speedUp); speedDown = Multiply3x3(GetMatrix(), speedDown); speedUp = GetSpeed(speedUp); speedDown = GetSpeed(speedDown); distSq = Max(speedUp.MagnitudeSqr(), speedDown.MagnitudeSqr()) * sq(CTimer::GetTimeStep()); if(distSq >= sq(0.3f)){ n = NUMSTEPS(0.3f); step = savedTimeStep / n; } }else if(responsecase == COLLRESPONSE_UNKNOWN5){ if(distSq >= 0.009f){ n = NUMSTEPS(0.09f); step = savedTimeStep / n; } }else if(responsecase == COLLRESPONSE_SMALLBOX || responsecase == COLLRESPONSE_FENCEPART){ if(distSq >= sq(0.15f)){ n = NUMSTEPS(0.15f); step = savedTimeStep / n; } }else{ if(distSq >= sq(0.3f)){ n = NUMSTEPS(0.3f); step = savedTimeStep / n; } } } for(i = 1; i < n; i++){ CTimer::SetTimeStep(i * step); ApplyMoveSpeed(); ApplyTurnSpeed(); // TODO: get rid of copy paste? if(CheckCollision()){ if(IsPed() && m_vecMoveSpeed.z == 0.0f && !ped->bWasStanding && ped->bIsStanding) savedMatrix.GetPosition().z = GetPosition().z; GetMatrix() = savedMatrix; CTimer::SetTimeStep(savedTimeStep); m_fElasticity = savedElasticity; return; } if(IsPed() && m_vecMoveSpeed.z == 0.0f && !ped->bWasStanding && ped->bIsStanding) savedMatrix.GetPosition().z = GetPosition().z; GetMatrix() = savedMatrix; CTimer::SetTimeStep(savedTimeStep); if(IsVehicle()){ CVehicle *veh = (CVehicle*)this; if(veh->m_vehType == VEHICLE_TYPE_CAR){ CAutomobile *car = (CAutomobile*)this; car->m_aSuspensionSpringRatio[0] = 1.0f; car->m_aSuspensionSpringRatio[1] = 1.0f; car->m_aSuspensionSpringRatio[2] = 1.0f; car->m_aSuspensionSpringRatio[3] = 1.0f; }else if(veh->m_vehType == VEHICLE_TYPE_BIKE){ CBike *bike = (CBike*)this; bike->m_aSuspensionSpringRatio[0] = 1.0f; bike->m_aSuspensionSpringRatio[1] = 1.0f; bike->m_aSuspensionSpringRatio[2] = 1.0f; bike->m_aSuspensionSpringRatio[3] = 1.0f; } } } ApplyMoveSpeed(); ApplyTurnSpeed(); GetMatrix().Reorthogonalise(); m_bIsVehicleBeingShifted = false; bSkipLineCol = false; if(!m_vecMoveSpeed.IsZero() || !m_vecTurnSpeed.IsZero() || bHitByTrain || GetStatus() == STATUS_PLAYER || IsVehicle() && ((CVehicle*)this)->bRestingOnPhysical || IsPed() && ped->IsPlayer()){ if(IsVehicle()) ((CVehicle*)this)->bVehicleColProcessed = true; if(CheckCollision()){ GetMatrix() = savedMatrix; m_fElasticity = savedElasticity; return; } } bHitByTrain = false; m_fDistanceTravelled = (GetPosition() - savedMatrix.GetPosition()).Magnitude(); bSkipLineCol = false; bIsStuck = false; bIsInSafePosition = true; m_fElasticity = savedElasticity; RemoveAndAdd(); } ================================================ FILE: src/entities/Physical.h ================================================ #pragma once #include "Lists.h" #include "Timer.h" #include "Entity.h" enum { PHYSICAL_MAX_COLLISIONRECORDS = 6 }; #define GRAVITY (0.008f) class CTreadable; class CPhysical : public CEntity { public: // The not properly indented fields haven't been checked properly yet int32 m_audioEntityId; float m_phys_unused1; uint32 m_nLastTimeCollided; CVector m_vecMoveSpeed; // velocity CVector m_vecTurnSpeed; // angular velocity CVector m_vecMoveFriction; CVector m_vecTurnFriction; CVector m_vecMoveSpeedAvg; CVector m_vecTurnSpeedAvg; float m_fMass; float m_fTurnMass; // moment of inertia float m_fForceMultiplier; float m_fAirResistance; float m_fElasticity; float m_fBuoyancy; CVector m_vecCentreOfMass; CEntryInfoList m_entryInfoList; CPtrNode *m_movingListNode; int8 m_phys_unused2; uint8 m_nStaticFrames; uint8 m_nCollisionRecords; CEntity *m_aCollisionRecords[PHYSICAL_MAX_COLLISIONRECORDS]; float m_fDistanceTravelled; // damaged piece float m_fDamageImpulse; CEntity *m_pDamageEntity; CVector m_vecDamageNormal; int16 m_nDamagePieceType; uint8 bIsHeavy : 1; uint8 bAffectedByGravity : 1; uint8 bInfiniteMass : 1; uint8 m_phy_flagA08 : 1; uint8 bIsInWater : 1; uint8 m_phy_flagA20 : 1; // unused uint8 bHitByTrain : 1; uint8 bSkipLineCol : 1; uint8 bIsFrozen : 1; uint8 bDontLoadCollision : 1; uint8 m_bIsVehicleBeingShifted : 1; // wrong name - also used on but never set for peds uint8 bJustCheckCollision : 1; // just see if there is a collision uint8 m_nSurfaceTouched; int8 m_nZoneLevel; CPhysical(void); ~CPhysical(void); // from CEntity void Add(void); void Remove(void); CRect GetBoundRect(void); void ProcessControl(void); void ProcessShift(void); void ProcessCollision(void); virtual int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints); void RemoveAndAdd(void); void AddToMovingList(void); void RemoveFromMovingList(void); void SetDamagedPieceRecord(uint16 piece, float impulse, CEntity *entity, CVector dir); void AddCollisionRecord(CEntity *ent); void AddCollisionRecord_Treadable(CEntity *ent); bool GetHasCollidedWith(CEntity *ent); void RemoveRefsToEntity(CEntity *ent); static void PlacePhysicalRelativeToOtherPhysical(CPhysical *other, CPhysical *phys, CVector localPos); // get speed of point p relative to entity center CVector GetSpeed(const CVector &r); CVector GetSpeed(void) { return GetSpeed(CVector(0.0f, 0.0f, 0.0f)); } float GetMass(const CVector &pos, const CVector &dir) { return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/m_fTurnMass + 1.0f/m_fMass); } float GetMassTweak(const CVector &pos, const CVector &dir, float t) { return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/(m_fTurnMass*t) + 1.0f/(m_fMass*t)); } void UnsetIsInSafePosition(void) { m_vecMoveSpeed *= -1.0f; m_vecTurnSpeed *= -1.0f; ApplyTurnSpeed(); ApplyMoveSpeed(); m_vecMoveSpeed *= -1.0f; m_vecTurnSpeed *= -1.0f; bIsInSafePosition = false; } const CVector &GetMoveSpeed() { return m_vecMoveSpeed; } void SetMoveSpeed(float x, float y, float z) { m_vecMoveSpeed.x = x; m_vecMoveSpeed.y = y; m_vecMoveSpeed.z = z; } void SetMoveSpeed(const CVector& speed) { m_vecMoveSpeed = speed; } void AddToMoveSpeed(float x, float y, float z) { m_vecMoveSpeed.x += x; m_vecMoveSpeed.y += y; m_vecMoveSpeed.z += z; } void AddToMoveSpeed(const CVector& addition) { m_vecMoveSpeed += addition; } void AddToMoveSpeed(const CVector2D& addition) { m_vecMoveSpeed += CVector(addition.x, addition.y, 0.0f); } const CVector &GetTurnSpeed() { return m_vecTurnSpeed; } void SetTurnSpeed(float x, float y, float z) { m_vecTurnSpeed.x = x; m_vecTurnSpeed.y = y; m_vecTurnSpeed.z = z; } const CVector &GetCenterOfMass() { return m_vecCentreOfMass; } void SetCenterOfMass(float x, float y, float z) { m_vecCentreOfMass.x = x; m_vecCentreOfMass.y = y; m_vecCentreOfMass.z = z; } void ApplyMoveSpeed(void); void ApplyTurnSpeed(void); // Force actually means Impulse here void ApplyMoveForce(float jx, float jy, float jz); void ApplyMoveForce(const CVector &j) { ApplyMoveForce(j.x, j.y, j.z); } // j(x,y,z) is direction of force, p(x,y,z) is point relative to model center where force is applied void ApplyTurnForce(float jx, float jy, float jz, float px, float py, float pz); // j is direction of force, p is point relative to model center where force is applied void ApplyTurnForce(const CVector &j, const CVector &p) { ApplyTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); } void ApplyFrictionMoveForce(float jx, float jy, float jz); void ApplyFrictionMoveForce(const CVector &j) { ApplyFrictionMoveForce(j.x, j.y, j.z); } void ApplyFrictionTurnForce(float jx, float jy, float jz, float rx, float ry, float rz); void ApplyFrictionTurnForce(const CVector &j, const CVector &p) { ApplyFrictionTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); } // springRatio: 1.0 fully extended, 0.0 fully compressed bool ApplySpringCollision(float springConst, CVector &springDir, CVector &point, float springRatio, float bias); bool ApplySpringCollisionAlt(float springConst, CVector &springDir, CVector &point, float springRatio, float bias, CVector &forceDir); bool ApplySpringDampening(float damping, CVector &springDir, CVector &point, CVector &speed); void ApplyGravity(void); void ApplyFriction(void); void ApplyAirResistance(void); bool ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, float &impulseB); bool ApplyCollision(CColPoint &colpoint, float &impulse); bool ApplyCollisionAlt(CEntity *B, CColPoint &colpoint, float &impulse, CVector &moveSpeed, CVector &turnSpeed); bool ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint); bool ApplyFriction(float adhesiveLimit, CColPoint &colpoint); bool ProcessShiftSectorList(CPtrList *ptrlists); bool ProcessCollisionSectorList_SimpleCar(CPtrList *lists); bool ProcessCollisionSectorList(CPtrList *lists); bool CheckCollision(void); bool CheckCollision_SimpleCar(void); }; ================================================ FILE: src/extras/GitSHA1.cpp.in ================================================ #define GIT_SHA1 "@GIT_SHA1@" const char* g_GIT_SHA1 = GIT_SHA1; ================================================ FILE: src/extras/GitSHA1.h ================================================ extern const char* g_GIT_SHA1; ================================================ FILE: src/extras/arrow.inc ================================================ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ================================================ FILE: src/extras/cursor.inc ================================================ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, ================================================ FILE: src/extras/custompipes.cpp ================================================ #define WITHD3D #include "common.h" #ifdef EXTENDED_PIPELINES #include "main.h" #include "RwHelper.h" #include "Lights.h" #include "Timecycle.h" #include "FileMgr.h" #include "Clock.h" #include "Weather.h" #include "TxdStore.h" #include "Renderer.h" #include "World.h" #include "custompipes.h" #ifndef LIBRW #error "Need librw for EXTENDED_PIPELINES" #endif namespace CustomPipes { rw::int32 CustomMatOffset; void* CustomMatCtor(void *object, int32, int32) { CustomMatExt *ext = GetCustomMatExt((rw::Material*)object); ext->glossTex = nil; ext->haveGloss = false; return object; } void* CustomMatCopy(void *dst, void *src, int32, int32) { CustomMatExt *srcext = GetCustomMatExt((rw::Material*)src); CustomMatExt *dstext = GetCustomMatExt((rw::Material*)dst); dstext->glossTex = srcext->glossTex; dstext->haveGloss = srcext->haveGloss; return dst; } rw::TexDictionary *neoTxd; bool bRenderingEnvMap; int32 EnvMapSize = 128; rw::Camera *EnvMapCam; rw::Texture *EnvMapTex; rw::Texture *EnvMaskTex; static rw::RWDEVICE::Im2DVertex EnvScreenQuad[4]; static int16 QuadIndices[6] = { 0, 1, 2, 0, 2, 3 }; static rw::Camera* CreateEnvMapCam(rw::World *world) { rw::Raster *fbuf = rw::Raster::create(EnvMapSize, EnvMapSize, 0, rw::Raster::CAMERATEXTURE); if(fbuf){ rw::Raster *zbuf = rw::Raster::create(EnvMapSize, EnvMapSize, 0, rw::Raster::ZBUFFER); if(zbuf){ rw::Frame *frame = rw::Frame::create(); if(frame){ rw::Camera *cam = rw::Camera::create(); if(cam){ cam->frameBuffer = fbuf; cam->zBuffer = zbuf; cam->setFrame(frame); cam->setNearPlane(0.1f); cam->setFarPlane(250.0f); rw::V2d vw = { 2.0f, 2.0f }; cam->setViewWindow(&vw); world->addCamera(cam); EnvMapTex = rw::Texture::create(fbuf); EnvMapTex->setFilter(rw::Texture::LINEAR); frame->matrix.right.x = -1.0f; frame->matrix.up.y = -1.0f; frame->matrix.update(); return cam; } frame->destroy(); } zbuf->destroy(); } fbuf->destroy(); } return nil; } static void DestroyCam(rw::Camera *cam) { if(cam == nil) return; if(cam->frameBuffer){ cam->frameBuffer->destroy(); cam->frameBuffer = nil; } if(cam->zBuffer){ cam->zBuffer->destroy(); cam->zBuffer = nil; } rw::Frame *f = cam->getFrame(); if(f){ cam->setFrame(nil); f->destroy(); } cam->world->removeCamera(cam); cam->destroy(); } void RenderEnvMapScene(void) { CRenderer::RenderRoads(); CRenderer::RenderEverythingBarRoads(); CRenderer::RenderFadingInEntities(); } void EnvMapRender(void) { if(VehiclePipeSwitch != VEHICLEPIPE_NEO) return; RwCameraEndUpdate(Scene.camera); // Neo does this differently, but i'm not quite convinced it's much better rw::V3d camPos = FindPlayerCoors(); EnvMapCam->getFrame()->matrix.pos = camPos; EnvMapCam->getFrame()->transform(&EnvMapCam->getFrame()->matrix, rw::COMBINEREPLACE); rw::RGBA skycol; skycol.red = CTimeCycle::GetSkyBottomRed(); skycol.green = CTimeCycle::GetSkyBottomGreen(); skycol.blue = CTimeCycle::GetSkyBottomBlue(); skycol.alpha = 255; EnvMapCam->clear(&skycol, rwCAMERACLEARZ|rwCAMERACLEARIMAGE); RwCameraBeginUpdate(EnvMapCam); bRenderingEnvMap = true; RenderEnvMapScene(); bRenderingEnvMap = false; if(EnvMaskTex){ rw::SetRenderState(rw::VERTEXALPHA, TRUE); rw::SetRenderState(rw::SRCBLEND, rw::BLENDZERO); rw::SetRenderState(rw::DESTBLEND, rw::BLENDSRCCOLOR); rw::SetRenderStatePtr(rw::TEXTURERASTER, EnvMaskTex->raster); rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, EnvScreenQuad, 4, QuadIndices, 6); rw::SetRenderState(rw::SRCBLEND, rw::BLENDSRCALPHA); rw::SetRenderState(rw::DESTBLEND, rw::BLENDINVSRCALPHA); } RwCameraEndUpdate(EnvMapCam); RwCameraBeginUpdate(Scene.camera); // debug env map // rw::SetRenderStatePtr(rw::TEXTURERASTER, EnvMapTex->raster); // rw::im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST, EnvScreenQuad, 4, QuadIndices, 6); } static void EnvMapInit(void) { if(neoTxd) EnvMaskTex = neoTxd->find("CarReflectionMask"); EnvMapCam = CreateEnvMapCam(Scene.world); int width = EnvMapCam->frameBuffer->width; int height = EnvMapCam->frameBuffer->height; float screenZ = RwIm2DGetNearScreenZ(); float recipZ = 1.0f/EnvMapCam->nearPlane; EnvScreenQuad[0].setScreenX(0.0f); EnvScreenQuad[0].setScreenY(0.0f); EnvScreenQuad[0].setScreenZ(screenZ); EnvScreenQuad[0].setCameraZ(EnvMapCam->nearPlane); EnvScreenQuad[0].setRecipCameraZ(recipZ); EnvScreenQuad[0].setColor(255, 255, 255, 255); EnvScreenQuad[0].setU(0.0f, recipZ); EnvScreenQuad[0].setV(0.0f, recipZ); EnvScreenQuad[1].setScreenX(0.0f); EnvScreenQuad[1].setScreenY(height); EnvScreenQuad[1].setScreenZ(screenZ); EnvScreenQuad[1].setCameraZ(EnvMapCam->nearPlane); EnvScreenQuad[1].setRecipCameraZ(recipZ); EnvScreenQuad[1].setColor(255, 255, 255, 255); EnvScreenQuad[1].setU(0.0f, recipZ); EnvScreenQuad[1].setV(1.0f, recipZ); EnvScreenQuad[2].setScreenX(width); EnvScreenQuad[2].setScreenY(height); EnvScreenQuad[2].setScreenZ(screenZ); EnvScreenQuad[2].setCameraZ(EnvMapCam->nearPlane); EnvScreenQuad[2].setRecipCameraZ(recipZ); EnvScreenQuad[2].setColor(255, 255, 255, 255); EnvScreenQuad[2].setU(1.0f, recipZ); EnvScreenQuad[2].setV(1.0f, recipZ); EnvScreenQuad[3].setScreenX(width); EnvScreenQuad[3].setScreenY(0.0f); EnvScreenQuad[3].setScreenZ(screenZ); EnvScreenQuad[3].setCameraZ(EnvMapCam->nearPlane); EnvScreenQuad[3].setRecipCameraZ(recipZ); EnvScreenQuad[3].setColor(255, 255, 255, 255); EnvScreenQuad[3].setU(1.0f, recipZ); EnvScreenQuad[3].setV(0.0f, recipZ); } static void EnvMapShutdown(void) { EnvMapTex->raster = nil; EnvMapTex->destroy(); EnvMapTex = nil; DestroyCam(EnvMapCam); EnvMapCam = nil; } /* * Tweak values */ #define INTERP_SETUP \ int h1 = CClock::GetHours(); \ int h2 = (h1+1)%24; \ int w1 = CWeather::OldWeatherType; \ int w2 = CWeather::NewWeatherType; \ float timeInterp = (CClock::GetSeconds()/60.0f + CClock::GetMinutes())/60.0f; \ float c0 = (1.0f-timeInterp)*(1.0f-CWeather::InterpolationValue); \ float c1 = timeInterp*(1.0f-CWeather::InterpolationValue); \ float c2 = (1.0f-timeInterp)*CWeather::InterpolationValue; \ float c3 = timeInterp*CWeather::InterpolationValue; #define INTERP(v) v[h1][w1]*c0 + v[h2][w1]*c1 + v[h1][w2]*c2 + v[h2][w2]*c3; #define INTERPF(v,f) v[h1][w1].f*c0 + v[h2][w1].f*c1 + v[h1][w2].f*c2 + v[h2][w2].f*c3; InterpolatedFloat::InterpolatedFloat(float init) { curInterpolator = 61; // compared against second for(int h = 0; h < 24; h++) for(int w = 0; w < NUMWEATHERS; w++) data[h][w] = init; } void InterpolatedFloat::Read(char *s, int line, int field) { sscanf(s, "%f", &data[line][field]); } float InterpolatedFloat::Get(void) { if(curInterpolator != CClock::GetSeconds()){ INTERP_SETUP curVal = INTERP(data); curInterpolator = CClock::GetSeconds(); } return curVal; } InterpolatedColor::InterpolatedColor(const Color &init) { curInterpolator = 61; // compared against second for(int h = 0; h < 24; h++) for(int w = 0; w < NUMWEATHERS; w++) data[h][w] = init; } void InterpolatedColor::Read(char *s, int line, int field) { int r, g, b, a; sscanf(s, "%i, %i, %i, %i", &r, &g, &b, &a); data[line][field] = Color(r/255.0f, g/255.0f, b/255.0f, a/255.0f); } Color InterpolatedColor::Get(void) { if(curInterpolator != CClock::GetSeconds()){ INTERP_SETUP curVal.r = INTERPF(data, r); curVal.g = INTERPF(data, g); curVal.b = INTERPF(data, b); curVal.a = INTERPF(data, a); curInterpolator = CClock::GetSeconds(); } return curVal; } void InterpolatedLight::Read(char *s, int line, int field) { int r, g, b, a; sscanf(s, "%i, %i, %i, %i", &r, &g, &b, &a); data[line][field] = Color(r/255.0f, g/255.0f, b/255.0f, a/100.0f); } char* ReadTweakValueTable(char *fp, InterpolatedValue &interp) { char buf[24], *p; int c; int line, field; line = 0; c = *fp++; while(c != '\0' && line < 24){ field = 0; if(c != '\0' && c != '#'){ while(c != '\0' && c != '\n' && field < NUMWEATHERS){ p = buf; while(c != '\0' && c == '\t') c = *fp++; *p++ = c; while(c = *fp++, c != '\0' && c != '\t' && c != '\n') *p++ = c; *p++ = '\0'; interp.Read(buf, line, field); field++; } line++; } while(c != '\0' && c != '\n') c = *fp++; c = *fp++; } return fp-1; } /* * Neo Vehicle pipe */ int32 VehiclePipeSwitch = VEHICLEPIPE_MATFX; float VehicleShininess = 1.0f; float VehicleSpecularity = 1.0f; InterpolatedFloat Fresnel(0.4f); InterpolatedFloat Power(18.0f); InterpolatedLight DiffColor(Color(0.0f, 0.0f, 0.0f, 0.0f)); InterpolatedLight SpecColor(Color(0.7f, 0.7f, 0.7f, 1.0f)); rw::ObjPipeline *vehiclePipe; void AttachVehiclePipe(rw::Atomic *atomic) { atomic->pipeline = vehiclePipe; } void AttachVehiclePipe(rw::Clump *clump) { FORLIST(lnk, clump->atomics) AttachVehiclePipe(rw::Atomic::fromClump(lnk)); } /* * Neo World pipe */ bool LightmapEnable; float LightmapMult = 1.0f; InterpolatedFloat WorldLightmapBlend(1.0f); rw::ObjPipeline *worldPipe; void AttachWorldPipe(rw::Atomic *atomic) { atomic->pipeline = worldPipe; } void AttachWorldPipe(rw::Clump *clump) { FORLIST(lnk, clump->atomics) AttachWorldPipe(rw::Atomic::fromClump(lnk)); } /* * Neo Gloss pipe */ bool GlossEnable; float GlossMult = 1.0f; rw::ObjPipeline *glossPipe; rw::Texture* GetGlossTex(rw::Material *mat) { if(neoTxd == nil) return nil; CustomMatExt *ext = GetCustomMatExt(mat); if(!ext->haveGloss){ char glossname[128]; strcpy(glossname, mat->texture->name); strcat(glossname, "_gloss"); ext->glossTex = neoTxd->find(glossname); ext->haveGloss = true; } return ext->glossTex; } void AttachGlossPipe(rw::Atomic *atomic) { atomic->pipeline = glossPipe; } void AttachGlossPipe(rw::Clump *clump) { FORLIST(lnk, clump->atomics) AttachWorldPipe(rw::Atomic::fromClump(lnk)); } /* * Neo Rim pipes */ bool RimlightEnable; float RimlightMult = 1.0f; InterpolatedColor RampStart(Color(0.0f, 0.0f, 0.0f, 1.0f)); InterpolatedColor RampEnd(Color(1.0f, 1.0f, 1.0f, 1.0f)); InterpolatedFloat Offset(0.5f); InterpolatedFloat Scale(1.5f); InterpolatedFloat Scaling(2.0f); rw::ObjPipeline *rimPipe; rw::ObjPipeline *rimSkinPipe; void AttachRimPipe(rw::Atomic *atomic) { if(rw::Skin::get(atomic->geometry)) atomic->pipeline = rimSkinPipe; else atomic->pipeline = rimPipe; } void AttachRimPipe(rw::Clump *clump) { FORLIST(lnk, clump->atomics) AttachRimPipe(rw::Atomic::fromClump(lnk)); } /* * High level stuff */ void CustomPipeInit(void) { RwStream *stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, "neo/neo.txd"); if(stream == nil) printf("Error: couldn't open 'neo/neo.txd'\n"); else{ if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)) neoTxd = RwTexDictionaryGtaStreamRead(stream); RwStreamClose(stream, nil); } EnvMapInit(); CreateVehiclePipe(); CreateWorldPipe(); CreateGlossPipe(); CreateRimLightPipes(); } void CustomPipeShutdown(void) { DestroyVehiclePipe(); DestroyWorldPipe(); DestroyGlossPipe(); DestroyRimLightPipes(); EnvMapShutdown(); if(neoTxd){ neoTxd->destroy(); neoTxd = nil; } } void CustomPipeRegister(void) { #ifdef RW_OPENGL CustomPipeRegisterGL(); #endif CustomMatOffset = rw::Material::registerPlugin(sizeof(CustomMatExt), MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x80), CustomMatCtor, nil, CustomMatCopy); } // Load textures from generic as fallback rw::TexDictionary *genericTxd; rw::Texture *(*defaultFindCB)(const char *name); static rw::Texture* customFindCB(const char *name) { rw::Texture *res = defaultFindCB(name); if(res == nil) res = genericTxd->find(name); return res; } void SetTxdFindCallback(void) { int slot = CTxdStore::FindTxdSlot("generic"); CTxdStore::AddRef(slot); // TODO: function for this genericTxd = CTxdStore::GetSlot(slot)->texDict; assert(genericTxd); if(defaultFindCB == nil) defaultFindCB = rw::Texture::findCB; rw::Texture::findCB = customFindCB; } } #endif ================================================ FILE: src/extras/custompipes.h ================================================ #pragma once #ifdef LIBRW #ifdef EXTENDED_PIPELINES namespace CustomPipes { extern rw::TexDictionary *neoTxd; struct CustomMatExt { rw::Texture *glossTex; bool haveGloss; }; extern rw::int32 CustomMatOffset; inline CustomMatExt *GetCustomMatExt(rw::Material *mat) { return PLUGINOFFSET(CustomMatExt, mat, CustomMatOffset); } struct Color { float r, g, b, a; Color(void) {} Color(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) {} }; class InterpolatedValue { public: virtual void Read(char *s, int line, int field) = 0; }; class InterpolatedFloat : public InterpolatedValue { public: float data[24][NUMWEATHERS]; float curInterpolator; float curVal; InterpolatedFloat(float init); void Read(char *s, int line, int field); float Get(void); }; class InterpolatedColor : public InterpolatedValue { public: Color data[24][NUMWEATHERS]; float curInterpolator; Color curVal; InterpolatedColor(const Color &init); void Read(char *s, int line, int field); Color Get(void); }; class InterpolatedLight : public InterpolatedColor { public: InterpolatedLight(const Color &init) : InterpolatedColor(init) {} void Read(char *s, int line, int field); }; char *ReadTweakValueTable(char *fp, InterpolatedValue &interp); void CustomPipeRegister(void); void CustomPipeRegisterGL(void); void CustomPipeInit(void); void CustomPipeShutdown(void); void SetTxdFindCallback(void); extern bool bRenderingEnvMap; extern int32 EnvMapSize; extern rw::Camera *EnvMapCam; extern rw::Texture *EnvMapTex; extern rw::Texture *EnvMaskTex; void EnvMapRender(void); enum { VEHICLEPIPE_MATFX, VEHICLEPIPE_NEO }; extern int32 VehiclePipeSwitch; extern float VehicleShininess; extern float VehicleSpecularity; extern InterpolatedFloat Fresnel; extern InterpolatedFloat Power; extern InterpolatedLight DiffColor; extern InterpolatedLight SpecColor; extern rw::ObjPipeline *vehiclePipe; void CreateVehiclePipe(void); void DestroyVehiclePipe(void); void AttachVehiclePipe(rw::Atomic *atomic); void AttachVehiclePipe(rw::Clump *clump); extern bool LightmapEnable; extern float LightmapMult; extern InterpolatedFloat WorldLightmapBlend; extern rw::ObjPipeline *worldPipe; void CreateWorldPipe(void); void DestroyWorldPipe(void); void AttachWorldPipe(rw::Atomic *atomic); void AttachWorldPipe(rw::Clump *clump); extern bool GlossEnable; extern float GlossMult; extern rw::ObjPipeline *glossPipe; void CreateGlossPipe(void); void DestroyGlossPipe(void); void AttachGlossPipe(rw::Atomic *atomic); void AttachGlossPipe(rw::Clump *clump); rw::Texture *GetGlossTex(rw::Material *mat); extern bool RimlightEnable; extern float RimlightMult; extern InterpolatedColor RampStart; extern InterpolatedColor RampEnd; extern InterpolatedFloat Offset; extern InterpolatedFloat Scale; extern InterpolatedFloat Scaling; extern rw::ObjPipeline *rimPipe; extern rw::ObjPipeline *rimSkinPipe; void CreateRimLightPipes(void); void DestroyRimLightPipes(void); void AttachRimPipe(rw::Atomic *atomic); void AttachRimPipe(rw::Clump *clump); } #endif namespace WorldRender{ extern int numBlendInsts[3]; void AtomicFirstPass(RpAtomic *atomic, int pass); void AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha); void RenderBlendPass(int pass); } #endif ================================================ FILE: src/extras/custompipes_d3d9.cpp ================================================ #define WITHD3D #include "common.h" #ifdef RW_D3D9 #include "main.h" #include "RwHelper.h" #include "Lights.h" #include "Timecycle.h" #include "FileMgr.h" #include "Clock.h" #include "Weather.h" #include "TxdStore.h" #include "Renderer.h" #include "World.h" #include "custompipes.h" #ifdef EXTENDED_PIPELINES #ifndef LIBRW #error "Need librw for EXTENDED_PIPELINES" #endif extern RwTexture *gpWhiteTexture; // from vehicle model info namespace CustomPipes { enum { // rim pipe VSLOC_boneMatrices = rw::d3d::VSLOC_afterLights, VSLOC_viewVec = VSLOC_boneMatrices + 64*3, VSLOC_rampStart, VSLOC_rampEnd, VSLOC_rimData, // gloss pipe VSLOC_eye = rw::d3d::VSLOC_afterLights, VSLOC_reflProps, VSLOC_specLights }; /* * Neo Vehicle pipe */ static void *neoVehicle_VS; static void *neoVehicle_PS; void uploadSpecLights(void) { struct VsLight { rw::RGBAf color; float pos[4]; // unused rw::V3d dir; float power; } specLights[1 + NUMEXTRADIRECTIONALS]; memset(specLights, 0, sizeof(specLights)); for(int i = 0; i < 1+NUMEXTRADIRECTIONALS; i++) specLights[i].power = 1.0f; float power = Power.Get(); Color speccol = SpecColor.Get(); specLights[0].color.red = speccol.r; specLights[0].color.green = speccol.g; specLights[0].color.blue = speccol.b; specLights[0].dir = pDirect->getFrame()->getLTM()->at; specLights[0].power = power; for(int i = 0; i < NUMEXTRADIRECTIONALS; i++){ if(pExtraDirectionals[i]->getFlags() & rw::Light::LIGHTATOMICS){ specLights[1+i].color = pExtraDirectionals[i]->color; specLights[1+i].dir = pExtraDirectionals[i]->getFrame()->getLTM()->at; specLights[1+i].power = power*2.0f; } } rw::d3d::d3ddevice->SetVertexShaderConstantF(VSLOC_specLights, (float*)&specLights, 3*(1 + NUMEXTRADIRECTIONALS)); } void vehicleRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; // TODO: make this less of a kludge if(VehiclePipeSwitch == VEHICLEPIPE_MATFX){ matFXGlobals.pipelines[rw::platform]->render(atomic); return; } int vsBits; rw::uint32 flags = atomic->geometry->flags; setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); setIndices(header->indexBuffer); setVertexDeclaration(header->vertexDeclaration); vsBits = lightingCB_Shader(atomic); uploadSpecLights(); uploadMatrices(atomic->getFrame()->getLTM()); setVertexShader(neoVehicle_VS); V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos; d3ddevice->SetVertexShaderConstantF(VSLOC_eye, (float*)&eyePos, 1); float reflProps[4]; reflProps[0] = Fresnel.Get(); reflProps[1] = SpecColor.Get().a; d3d::setTexture(1, EnvMapTex); SetRenderState(SRCBLEND, BLENDONE); InstanceData *inst = header->inst; for(rw::uint32 i = 0; i < header->numMeshes; i++){ Material *m = inst->material; SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); reflProps[2] = m->surfaceProps.specular * VehicleShininess; reflProps[3] = m->surfaceProps.specular == 0.0f ? 0.0f : VehicleSpecularity; d3ddevice->SetVertexShaderConstantF(VSLOC_reflProps, reflProps, 1); setMaterial(flags, m->color, m->surfaceProps); if(m->texture) d3d::setTexture(0, m->texture); else d3d::setTexture(0, gpWhiteTexture); setPixelShader(neoVehicle_PS); drawInst(header, inst); inst++; } d3d::setTexture(1, nil); SetRenderState(SRCBLEND, BLENDSRCALPHA); } void CreateVehiclePipe(void) { if(CFileMgr::LoadFile("neo/carTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) printf("Error: couldn't open 'neo/carTweakingTable.dat'\n"); else{ char *fp = (char*)work_buff; fp = ReadTweakValueTable(fp, Fresnel); fp = ReadTweakValueTable(fp, Power); fp = ReadTweakValueTable(fp, DiffColor); fp = ReadTweakValueTable(fp, SpecColor); } #include "shaders/obj/neoVehicle_VS.inc" neoVehicle_VS = rw::d3d::createVertexShader(neoVehicle_VS_cso); assert(neoVehicle_VS); #include "shaders/obj/neoVehicle_PS.inc" neoVehicle_PS = rw::d3d::createPixelShader(neoVehicle_PS_cso); assert(neoVehicle_PS); rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); pipe->instanceCB = rw::d3d9::defaultInstanceCB; pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB; pipe->renderCB = vehicleRenderCB; vehiclePipe = pipe; } void DestroyVehiclePipe(void) { rw::d3d::destroyVertexShader(neoVehicle_VS); neoVehicle_VS = nil; rw::d3d::destroyPixelShader(neoVehicle_PS); neoVehicle_PS = nil; ((rw::d3d9::ObjPipeline*)vehiclePipe)->destroy(); vehiclePipe = nil; } /* * Neo World pipe */ static void *neoWorld_VS; static void *neoWorldVC_PS; static void worldRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; if(!LightmapEnable){ defaultRenderCB_Shader(atomic, header); return; } int vsBits; setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); setIndices(header->indexBuffer); setVertexDeclaration(header->vertexDeclaration); vsBits = lightingCB_Shader(atomic); uploadMatrices(atomic->getFrame()->getLTM()); float lightfactor[4]; InstanceData *inst = header->inst; for(rw::uint32 i = 0; i < header->numMeshes; i++){ Material *m = inst->material; if(MatFX::getEffects(m) == MatFX::DUAL){ setVertexShader(neoWorld_VS); MatFX *matfx = MatFX::get(m); Texture *dualtex = matfx->getDualTexture(); if(dualtex == nil) goto notex; d3d::setTexture(1, dualtex); lightfactor[0] = lightfactor[1] = lightfactor[2] = WorldLightmapBlend.Get()*LightmapMult; }else{ notex: setVertexShader(default_amb_VS); d3d::setTexture(1, nil); lightfactor[0] = lightfactor[1] = lightfactor[2] = 0.0f; } lightfactor[3] = m->color.alpha/255.0f; d3d::setTexture(0, m->texture); d3ddevice->SetPixelShaderConstantF(1, lightfactor, 1); SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); RGBA color = { 255, 255, 255, m->color.alpha }; setMaterial(color, m->surfaceProps); if(m->texture) d3d::setTexture(0, m->texture); else d3d::setTexture(0, gpWhiteTexture); setPixelShader(neoWorldVC_PS); drawInst(header, inst); inst++; } d3d::setTexture(1, nil); } void CreateWorldPipe(void) { if(CFileMgr::LoadFile("neo/worldTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) printf("Error: couldn't open 'neo/worldTweakingTable.dat'\n"); else ReadTweakValueTable((char*)work_buff, WorldLightmapBlend); #include "shaders/obj/default_UV2_VS.inc" neoWorld_VS = rw::d3d::createVertexShader(default_UV2_VS_cso); assert(neoWorld_VS); #include "shaders/obj/neoWorldVC_PS.inc" neoWorldVC_PS = rw::d3d::createPixelShader(neoWorldVC_PS_cso); assert(neoWorldVC_PS); rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); pipe->instanceCB = rw::d3d9::defaultInstanceCB; pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB; pipe->renderCB = worldRenderCB; worldPipe = pipe; } void DestroyWorldPipe(void) { rw::d3d::destroyVertexShader(neoWorld_VS); neoWorld_VS = nil; rw::d3d::destroyPixelShader(neoWorldVC_PS); neoWorldVC_PS = nil; ((rw::d3d9::ObjPipeline*)worldPipe)->destroy(); worldPipe = nil; } /* * Neo Gloss pipe */ static void *neoGloss_VS; static void *neoGloss_PS; static void glossRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) { worldRenderCB(atomic, header); using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; if(!GlossEnable) return; setVertexShader(neoGloss_VS); setPixelShader(neoGloss_PS); V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos; d3ddevice->SetVertexShaderConstantF(VSLOC_eye, (float*)&eyePos, 1); d3ddevice->SetPixelShaderConstantF(1, (float*)&GlossMult, 1); SetRenderState(VERTEXALPHA, TRUE); SetRenderState(SRCBLEND, BLENDONE); SetRenderState(DESTBLEND, BLENDONE); SetRenderState(ZWRITEENABLE, FALSE); SetRenderState(ALPHATESTFUNC, ALPHAALWAYS); InstanceData *inst = header->inst; for(rw::uint32 i = 0; i < header->numMeshes; i++){ Material *m = inst->material; if(m->texture){ Texture *tex = GetGlossTex(m); if(tex){ d3d::setTexture(0, tex); drawInst(header, inst); } } inst++; } SetRenderState(ZWRITEENABLE, TRUE); SetRenderState(ALPHATESTFUNC, ALPHAGREATEREQUAL); SetRenderState(SRCBLEND, BLENDSRCALPHA); SetRenderState(DESTBLEND, BLENDINVSRCALPHA); } void CreateGlossPipe(void) { #include "shaders/obj/neoGloss_VS.inc" neoGloss_VS = rw::d3d::createVertexShader(neoGloss_VS_cso); assert(neoGloss_VS); #include "shaders/obj/neoGloss_PS.inc" neoGloss_PS = rw::d3d::createPixelShader(neoGloss_PS_cso); assert(neoGloss_PS); rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); pipe->instanceCB = rw::d3d9::defaultInstanceCB; pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB; pipe->renderCB = glossRenderCB; glossPipe = pipe; } void DestroyGlossPipe(void) { rw::d3d::destroyVertexShader(neoGloss_VS); neoGloss_VS = nil; rw::d3d::destroyPixelShader(neoGloss_PS); neoGloss_PS = nil; ((rw::d3d9::ObjPipeline*)glossPipe)->destroy(); glossPipe = nil; } /* * Neo Rim pipes */ static void *neoRim_VS; static void *neoRimSkin_VS; static void uploadRimData(bool enable) { using namespace rw; using namespace rw::d3d; V3d viewVec = rw::engine->currentCamera->getFrame()->getLTM()->at; d3ddevice->SetVertexShaderConstantF(VSLOC_viewVec, (float*)&viewVec, 1); float rimData[4]; rimData[0] = Offset.Get(); rimData[1] = Scale.Get(); if(enable) rimData[2] = Scaling.Get()*RimlightMult; else rimData[2] = 0.0f; rimData[3] = 0.0f; d3ddevice->SetVertexShaderConstantF(VSLOC_rimData, rimData, 1); Color col = RampStart.Get(); d3ddevice->SetVertexShaderConstantF(VSLOC_rampStart, (float*)&col, 1); col = RampEnd.Get(); d3ddevice->SetVertexShaderConstantF(VSLOC_rampEnd, (float*)&col, 1); } static void rimRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; if(!RimlightEnable){ defaultRenderCB_Shader(atomic, header); return; } int vsBits; rw::uint32 flags = atomic->geometry->flags; setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); setIndices(header->indexBuffer); setVertexDeclaration(header->vertexDeclaration); vsBits = lightingCB_Shader(atomic); uploadMatrices(atomic->getFrame()->getLTM()); setVertexShader(neoRim_VS); uploadRimData(atomic->geometry->flags & Geometry::LIGHT); InstanceData *inst = header->inst; for(rw::uint32 i = 0; i < header->numMeshes; i++){ Material *m = inst->material; SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); setMaterial(flags, m->color, m->surfaceProps); if(m->texture){ d3d::setTexture(0, m->texture); setPixelShader(default_tex_PS); }else setPixelShader(default_PS); drawInst(header, inst); inst++; } } static void rimSkinRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; if(!RimlightEnable){ skinRenderCB(atomic, header); return; } int vsBits; rw::uint32 flags = atomic->geometry->flags; setStreamSource(0, (IDirect3DVertexBuffer9*)header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); setIndices((IDirect3DIndexBuffer9*)header->indexBuffer); setVertexDeclaration((IDirect3DVertexDeclaration9*)header->vertexDeclaration); vsBits = lightingCB_Shader(atomic); uploadMatrices(atomic->getFrame()->getLTM()); uploadSkinMatrices(atomic); setVertexShader(neoRimSkin_VS); uploadRimData(atomic->geometry->flags & Geometry::LIGHT); InstanceData *inst = header->inst; for(rw::uint32 i = 0; i < header->numMeshes; i++){ Material *m = inst->material; SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); setMaterial(flags, m->color, m->surfaceProps); if(inst->material->texture){ d3d::setTexture(0, m->texture); setPixelShader(default_tex_PS); }else setPixelShader(default_PS); drawInst(header, inst); inst++; } } void CreateRimLightPipes(void) { if(CFileMgr::LoadFile("neo/rimTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) printf("Error: couldn't open 'neo/rimTweakingTable.dat'\n"); else{ char *fp = (char*)work_buff; fp = ReadTweakValueTable(fp, RampStart); fp = ReadTweakValueTable(fp, RampEnd); fp = ReadTweakValueTable(fp, Offset); fp = ReadTweakValueTable(fp, Scale); fp = ReadTweakValueTable(fp, Scaling); } #include "shaders/obj/neoRim_VS.inc" neoRim_VS = rw::d3d::createVertexShader(neoRim_VS_cso); assert(neoRim_VS); #include "shaders/obj/neoRimSkin_VS.inc" neoRimSkin_VS = rw::d3d::createVertexShader(neoRimSkin_VS_cso); assert(neoRimSkin_VS); rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); pipe->instanceCB = rw::d3d9::defaultInstanceCB; pipe->uninstanceCB = rw::d3d9::defaultUninstanceCB; pipe->renderCB = rimRenderCB; rimPipe = pipe; pipe = rw::d3d9::ObjPipeline::create(); pipe->instanceCB = rw::d3d9::skinInstanceCB; pipe->uninstanceCB = nil; pipe->renderCB = rimSkinRenderCB; rimSkinPipe = pipe; } void DestroyRimLightPipes(void) { rw::d3d::destroyVertexShader(neoRim_VS); neoRim_VS = nil; rw::d3d::destroyVertexShader(neoRimSkin_VS); neoRimSkin_VS = nil; ((rw::d3d9::ObjPipeline*)rimPipe)->destroy(); rimPipe = nil; ((rw::d3d9::ObjPipeline*)rimSkinPipe)->destroy(); rimSkinPipe = nil; } } #endif #ifdef NEW_RENDERER #ifndef LIBRW #error "Need librw for NEW_PIPELINES" #endif namespace WorldRender { struct BuildingInst { rw::RawMatrix combinedMat; rw::d3d9::InstanceDataHeader *instHeader; uint8 fadeAlpha; bool lighting; }; BuildingInst blendInsts[3][2000]; int numBlendInsts[3]; static RwRGBAReal black; static void SetMatrix(BuildingInst *building, rw::Matrix *worldMat) { using namespace rw; RawMatrix world, worldview; Camera *cam = engine->currentCamera; convMatrix(&world, worldMat); RawMatrix::mult(&worldview, &world, &cam->devView); RawMatrix::mult(&building->combinedMat, &worldview, &cam->devProj); } static bool IsTextureTransparent(RwTexture *tex) { if(tex == nil || tex->raster == nil) return false; return PLUGINOFFSET(rw::d3d::D3dRaster, tex->raster, rw::d3d::nativeRasterOffset)->hasAlpha; } // Render all opaque meshes and put atomics that needs blending // into the deferred list. void AtomicFirstPass(RpAtomic *atomic, int pass) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; atomic->getPipeline()->instance(atomic); building->instHeader = (d3d9::InstanceDataHeader*)atomic->geometry->instData; assert(building->instHeader != nil); assert(building->instHeader->platform == PLATFORM_D3D9); building->fadeAlpha = 255; building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); rw::uint32 flags = atomic->geometry->flags; bool setupDone = false; bool defer = false; SetMatrix(building, atomic->getFrame()->getLTM()); InstanceData *inst = building->instHeader->inst; for(rw::uint32 i = 0; i < building->instHeader->numMeshes; i++, inst++){ Material *m = inst->material; if(inst->vertexAlpha || m->color.alpha != 255 || IsTextureTransparent(m->texture)){ defer = true; continue; } // alright we're rendering this atomic if(!setupDone){ setStreamSource(0, building->instHeader->vertexStream[0].vertexBuffer, 0, building->instHeader->vertexStream[0].stride); setIndices(building->instHeader->indexBuffer); setVertexDeclaration(building->instHeader->vertexDeclaration); setVertexShader(default_amb_VS); d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4); if(building->lighting) setAmbient(pAmbient->color); else setAmbient(black); setupDone = true; } setMaterial(flags, m->color, m->surfaceProps); if(m->texture){ d3d::setTexture(0, m->texture); setPixelShader(default_tex_PS); }else setPixelShader(default_PS); drawInst(building->instHeader, inst); } if(defer) numBlendInsts[pass]++; } void AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; atomic->getPipeline()->instance(atomic); building->instHeader = (d3d9::InstanceDataHeader*)atomic->geometry->instData; assert(building->instHeader != nil); assert(building->instHeader->platform == PLATFORM_D3D9); building->fadeAlpha = fadeAlpha; building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); SetMatrix(building, atomic->getFrame()->getLTM()); numBlendInsts[pass]++; } void RenderBlendPass(int pass) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; setVertexShader(default_amb_VS); int i; for(i = 0; i < numBlendInsts[pass]; i++){ BuildingInst *building = &blendInsts[pass][i]; setStreamSource(0, building->instHeader->vertexStream[0].vertexBuffer, 0, building->instHeader->vertexStream[0].stride); setIndices(building->instHeader->indexBuffer); setVertexDeclaration(building->instHeader->vertexDeclaration); d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4); if(building->lighting) setAmbient(pAmbient->color); else setAmbient(black); InstanceData *inst = building->instHeader->inst; for(rw::uint32 j = 0; j < building->instHeader->numMeshes; j++, inst++){ Material *m = inst->material; if(!inst->vertexAlpha && m->color.alpha == 255 && !IsTextureTransparent(m->texture) && building->fadeAlpha == 255) continue; // already done this one rw::RGBA color = m->color; color.alpha = (color.alpha * building->fadeAlpha)/255; setMaterial(color, m->surfaceProps); // always modulate here if(m->texture){ d3d::setTexture(0, m->texture); setPixelShader(default_tex_PS); }else setPixelShader(default_PS); drawInst(building->instHeader, inst); } } } } #endif #endif ================================================ FILE: src/extras/custompipes_gl.cpp ================================================ #include "common.h" #ifdef RW_OPENGL #include "main.h" #include "RwHelper.h" #include "Lights.h" #include "Timecycle.h" #include "FileMgr.h" #include "Clock.h" #include "Weather.h" #include "TxdStore.h" #include "Renderer.h" #include "World.h" #include "custompipes.h" #ifdef EXTENDED_PIPELINES #ifndef LIBRW #error "Need librw for EXTENDED_PIPELINES" #endif namespace CustomPipes { static int32 u_viewVec; static int32 u_rampStart; static int32 u_rampEnd; static int32 u_rimData; static int32 u_lightMap; static int32 u_eye; static int32 u_reflProps; static int32 u_specDir; static int32 u_specColor; #define U(i) currentShader->uniformLocations[i] /* * Neo Vehicle pipe */ rw::gl3::Shader *neoVehicleShader; static void uploadSpecLights(void) { using namespace rw::gl3; rw::RGBAf colors[1 + NUMEXTRADIRECTIONALS]; struct { rw::V3d dir; float power; } dirs[1 + NUMEXTRADIRECTIONALS]; memset(colors, 0, sizeof(colors)); memset(dirs, 0, sizeof(dirs)); for(int i = 0; i < 1+NUMEXTRADIRECTIONALS; i++) dirs[i].power = 1.0f; float power = Power.Get(); Color speccol = SpecColor.Get(); colors[0].red = speccol.r; colors[0].green = speccol.g; colors[0].blue = speccol.b; dirs[0].dir = pDirect->getFrame()->getLTM()->at; dirs[0].power = power; for(int i = 0; i < NUMEXTRADIRECTIONALS; i++){ if(pExtraDirectionals[i]->getFlags() & rw::Light::LIGHTATOMICS){ colors[1+i] = pExtraDirectionals[i]->color; dirs[1+i].dir = pExtraDirectionals[i]->getFrame()->getLTM()->at; dirs[1+i].power = power*2.0f; } } glUniform4fv(U(u_specDir), 1 + NUMEXTRADIRECTIONALS, (float*)&dirs); glUniform4fv(U(u_specColor), 1 + NUMEXTRADIRECTIONALS, (float*)&colors); } static void vehicleRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) { using namespace rw; using namespace rw::gl3; // TODO: make this less of a kludge if(VehiclePipeSwitch == VEHICLEPIPE_MATFX){ matFXGlobals.pipelines[rw::platform]->render(atomic); return; } Material *m; rw::uint32 flags = atomic->geometry->flags; setWorldMatrix(atomic->getFrame()->getLTM()); lightingCB(atomic); setupVertexInput(header); InstanceData *inst = header->inst; rw::int32 n = header->numMeshes; neoVehicleShader->use(); V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos; glUniform3fv(U(u_eye), 1, (float*)&eyePos); uploadSpecLights(); float reflProps[4]; reflProps[0] = Fresnel.Get(); reflProps[1] = SpecColor.Get().a; setTexture(1, EnvMapTex); SetRenderState(SRCBLEND, BLENDONE); while(n--){ m = inst->material; setMaterial(flags, m->color, m->surfaceProps); setTexture(0, m->texture); rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF); reflProps[2] = m->surfaceProps.specular * VehicleShininess; reflProps[3] = m->surfaceProps.specular == 0.0f ? 0.0f : VehicleSpecularity; glUniform4fv(U(u_reflProps), 1, reflProps); drawInst(header, inst); inst++; } setTexture(1, nil); SetRenderState(SRCBLEND, BLENDSRCALPHA); teardownVertexInput(header); } void CreateVehiclePipe(void) { using namespace rw; using namespace rw::gl3; if(CFileMgr::LoadFile("neo/carTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) printf("Error: couldn't open 'neo/carTweakingTable.dat'\n"); else{ char *fp = (char*)work_buff; fp = ReadTweakValueTable(fp, Fresnel); fp = ReadTweakValueTable(fp, Power); fp = ReadTweakValueTable(fp, DiffColor); fp = ReadTweakValueTable(fp, SpecColor); } { #include "shaders/obj/neoVehicle_frag.inc" #include "shaders/obj/neoVehicle_vert.inc" const char *vs[] = { shaderDecl, header_vert_src, neoVehicle_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, neoVehicle_frag_src, nil }; neoVehicleShader = Shader::create(vs, fs); assert(neoVehicleShader); } rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create(); pipe->instanceCB = rw::gl3::defaultInstanceCB; pipe->uninstanceCB = nil; pipe->renderCB = vehicleRenderCB; vehiclePipe = pipe; } void DestroyVehiclePipe(void) { neoVehicleShader->destroy(); neoVehicleShader = nil; ((rw::gl3::ObjPipeline*)vehiclePipe)->destroy(); vehiclePipe = nil; } /* * Neo World pipe */ rw::gl3::Shader *neoWorldShader; static void worldRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) { using namespace rw; using namespace rw::gl3; if(!LightmapEnable){ gl3::defaultRenderCB(atomic, header); return; } Material *m; setWorldMatrix(atomic->getFrame()->getLTM()); lightingCB(atomic); setupVertexInput(header); InstanceData *inst = header->inst; rw::int32 n = header->numMeshes; neoWorldShader->use(); float lightfactor[4]; while(n--){ m = inst->material; if(MatFX::getEffects(m) == MatFX::DUAL){ MatFX *matfx = MatFX::get(m); Texture *dualtex = matfx->getDualTexture(); if(dualtex == nil) goto notex; setTexture(1, dualtex); lightfactor[0] = lightfactor[1] = lightfactor[2] = WorldLightmapBlend.Get()*LightmapMult; }else{ notex: setTexture(1, nil); lightfactor[0] = lightfactor[1] = lightfactor[2] = 0.0f; } lightfactor[3] = m->color.alpha/255.0f; glUniform4fv(U(u_lightMap), 1, lightfactor); RGBA color = { 255, 255, 255, m->color.alpha }; setMaterial(color, m->surfaceProps); setTexture(0, m->texture); rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF); drawInst(header, inst); inst++; } setTexture(1, nil); teardownVertexInput(header); } void CreateWorldPipe(void) { using namespace rw; using namespace rw::gl3; if(CFileMgr::LoadFile("neo/worldTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) printf("Error: couldn't open 'neo/worldTweakingTable.dat'\n"); else ReadTweakValueTable((char*)work_buff, WorldLightmapBlend); { #include "shaders/obj/neoWorldVC_frag.inc" #include "shaders/obj/default_UV2_vert.inc" const char *vs[] = { shaderDecl, header_vert_src, default_UV2_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, neoWorldVC_frag_src, nil }; neoWorldShader = Shader::create(vs, fs); assert(neoWorldShader); } rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create(); pipe->instanceCB = rw::gl3::defaultInstanceCB; pipe->uninstanceCB = nil; pipe->renderCB = worldRenderCB; worldPipe = pipe; } void DestroyWorldPipe(void) { neoWorldShader->destroy(); neoWorldShader = nil; ((rw::gl3::ObjPipeline*)worldPipe)->destroy(); worldPipe = nil; } /* * Neo Gloss pipe */ rw::gl3::Shader *neoGlossShader; static void glossRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) { using namespace rw; using namespace rw::gl3; worldRenderCB(atomic, header); if(!GlossEnable) return; Material *m; setupVertexInput(header); InstanceData *inst = header->inst; rw::int32 n = header->numMeshes; neoGlossShader->use(); V3d eyePos = rw::engine->currentCamera->getFrame()->getLTM()->pos; glUniform3fv(U(u_eye), 1, (float*)&eyePos); float reflProps[4]; reflProps[0] = GlossMult; reflProps[1] = 0.0f; reflProps[2] = 0.0f; reflProps[3] = 0.0f; glUniform4fv(U(u_reflProps), 1, reflProps); SetRenderState(VERTEXALPHA, TRUE); SetRenderState(SRCBLEND, BLENDONE); SetRenderState(DESTBLEND, BLENDONE); SetRenderState(ZWRITEENABLE, FALSE); SetRenderState(ALPHATESTFUNC, ALPHAALWAYS); while(n--){ m = inst->material; RGBA color = { 255, 255, 255, m->color.alpha }; setMaterial(color, m->surfaceProps); if(m->texture){ Texture *tex = GetGlossTex(m); if(tex){ setTexture(0, tex); drawInst(header, inst); } } inst++; } SetRenderState(ZWRITEENABLE, TRUE); SetRenderState(ALPHATESTFUNC, ALPHAGREATEREQUAL); SetRenderState(SRCBLEND, BLENDSRCALPHA); SetRenderState(DESTBLEND, BLENDINVSRCALPHA); teardownVertexInput(header); } void CreateGlossPipe(void) { using namespace rw; using namespace rw::gl3; { #include "shaders/obj/neoGloss_frag.inc" #include "shaders/obj/neoGloss_vert.inc" const char *vs[] = { shaderDecl, header_vert_src, neoGloss_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, neoGloss_frag_src, nil }; neoGlossShader = Shader::create(vs, fs); assert(neoGlossShader); } rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create(); pipe->instanceCB = rw::gl3::defaultInstanceCB; pipe->uninstanceCB = nil; pipe->renderCB = glossRenderCB; glossPipe = pipe; } void DestroyGlossPipe(void) { neoGlossShader->destroy(); neoGlossShader = nil; ((rw::gl3::ObjPipeline*)glossPipe)->destroy(); glossPipe = nil; } /* * Neo Rim pipes */ rw::gl3::Shader *neoRimShader; rw::gl3::Shader *neoRimSkinShader; static void uploadRimData(bool enable) { using namespace rw; using namespace rw::gl3; V3d viewVec = rw::engine->currentCamera->getFrame()->getLTM()->at; glUniform3fv(U(u_viewVec), 1, (float*)&viewVec); float rimData[4]; rimData[0] = Offset.Get(); rimData[1] = Scale.Get(); if(enable) rimData[2] = Scaling.Get()*RimlightMult; else rimData[2] = 0.0f; rimData[3] = 0.0f; glUniform3fv(U(u_rimData), 1, rimData); Color col = RampStart.Get(); glUniform4fv(U(u_rampStart), 1, (float*)&col); col = RampEnd.Get(); glUniform4fv(U(u_rampEnd), 1, (float*)&col); } static void rimSkinRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) { using namespace rw; using namespace rw::gl3; if(!RimlightEnable){ gl3::skinRenderCB(atomic, header); return; } Material *m; rw::uint32 flags = atomic->geometry->flags; setWorldMatrix(atomic->getFrame()->getLTM()); lightingCB(atomic); setupVertexInput(header); InstanceData *inst = header->inst; rw::int32 n = header->numMeshes; neoRimSkinShader->use(); uploadRimData(atomic->geometry->flags & Geometry::LIGHT); uploadSkinMatrices(atomic); while(n--){ m = inst->material; setMaterial(flags, m->color, m->surfaceProps); setTexture(0, m->texture); rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF); drawInst(header, inst); inst++; } teardownVertexInput(header); } static void rimRenderCB(rw::Atomic *atomic, rw::gl3::InstanceDataHeader *header) { using namespace rw; using namespace rw::gl3; if(!RimlightEnable){ gl3::defaultRenderCB(atomic, header); return; } Material *m; rw::uint32 flags = atomic->geometry->flags; setWorldMatrix(atomic->getFrame()->getLTM()); lightingCB(atomic); setupVertexInput(header); InstanceData *inst = header->inst; rw::int32 n = header->numMeshes; neoRimShader->use(); uploadRimData(atomic->geometry->flags & Geometry::LIGHT); while(n--){ m = inst->material; setMaterial(flags, m->color, m->surfaceProps); setTexture(0, m->texture); rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF); drawInst(header, inst); inst++; } teardownVertexInput(header); } void CreateRimLightPipes(void) { using namespace rw::gl3; if(CFileMgr::LoadFile("neo/rimTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) printf("Error: couldn't open 'neo/rimTweakingTable.dat'\n"); else{ char *fp = (char*)work_buff; fp = ReadTweakValueTable(fp, RampStart); fp = ReadTweakValueTable(fp, RampEnd); fp = ReadTweakValueTable(fp, Offset); fp = ReadTweakValueTable(fp, Scale); fp = ReadTweakValueTable(fp, Scaling); } { #include "shaders/obj/simple_frag.inc" #include "shaders/obj/neoRimSkin_vert.inc" const char *vs[] = { shaderDecl, header_vert_src, neoRimSkin_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil }; neoRimSkinShader = Shader::create(vs, fs); assert(neoRimSkinShader); } { #include "shaders/obj/simple_frag.inc" #include "shaders/obj/neoRim_vert.inc" const char *vs[] = { shaderDecl, header_vert_src, neoRim_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil }; neoRimShader = Shader::create(vs, fs); assert(neoRimShader); } rw::gl3::ObjPipeline *pipe = rw::gl3::ObjPipeline::create(); pipe->instanceCB = rw::gl3::defaultInstanceCB; pipe->uninstanceCB = nil; pipe->renderCB = rimRenderCB; rimPipe = pipe; pipe = rw::gl3::ObjPipeline::create(); pipe->instanceCB = rw::gl3::skinInstanceCB; pipe->uninstanceCB = nil; pipe->renderCB = rimSkinRenderCB; rimSkinPipe = pipe; } void DestroyRimLightPipes(void) { neoRimShader->destroy(); neoRimShader = nil; neoRimSkinShader->destroy(); neoRimSkinShader = nil; ((rw::gl3::ObjPipeline*)rimPipe)->destroy(); rimPipe = nil; ((rw::gl3::ObjPipeline*)rimSkinPipe)->destroy(); rimSkinPipe = nil; } void CustomPipeRegisterGL(void) { u_viewVec = rw::gl3::registerUniform("u_viewVec"); u_rampStart = rw::gl3::registerUniform("u_rampStart"); u_rampEnd = rw::gl3::registerUniform("u_rampEnd"); u_rimData = rw::gl3::registerUniform("u_rimData"); u_lightMap = rw::gl3::registerUniform("u_lightMap"); u_eye = rw::gl3::registerUniform("u_eye"); u_reflProps = rw::gl3::registerUniform("u_reflProps"); u_specDir = rw::gl3::registerUniform("u_specDir"); u_specColor = rw::gl3::registerUniform("u_specColor"); } } #endif #ifdef NEW_RENDERER #ifndef LIBRW #error "Need librw for NEW_PIPELINES" #endif namespace WorldRender { struct BuildingInst { rw::Matrix matrix; rw::gl3::InstanceDataHeader *instHeader; uint8 fadeAlpha; bool lighting; }; BuildingInst blendInsts[3][2000]; int numBlendInsts[3]; static RwRGBAReal black; static bool IsTextureTransparent(RwTexture *tex) { if(tex == nil || tex->raster == nil) return false; return PLUGINOFFSET(rw::gl3::Gl3Raster, tex->raster, rw::gl3::nativeRasterOffset)->hasAlpha; } // Render all opaque meshes and put atomics that needs blending // into the deferred list. void AtomicFirstPass(RpAtomic *atomic, int pass) { using namespace rw; using namespace rw::gl3; BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; atomic->getPipeline()->instance(atomic); building->instHeader = (gl3::InstanceDataHeader*)atomic->geometry->instData; assert(building->instHeader != nil); assert(building->instHeader->platform == PLATFORM_GL3); building->fadeAlpha = 255; building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); rw::uint32 flags = atomic->geometry->flags; WorldLights lights; lights.numAmbients = 1; lights.numDirectionals = 0; lights.numLocals = 0; if(building->lighting) lights.ambient = pAmbient->color; else lights.ambient = black; bool setupDone = false; bool defer = false; building->matrix = *atomic->getFrame()->getLTM(); InstanceData *inst = building->instHeader->inst; for(rw::uint32 i = 0; i < building->instHeader->numMeshes; i++, inst++){ Material *m = inst->material; if(inst->vertexAlpha || m->color.alpha != 255 || IsTextureTransparent(m->texture)){ defer = true; continue; } // alright we're rendering this atomic if(!setupDone){ defaultShader->use(); setWorldMatrix(&building->matrix); setupVertexInput(building->instHeader); setLights(&lights); setupDone = true; } setMaterial(flags, m->color, m->surfaceProps); setTexture(0, m->texture); drawInst(building->instHeader, inst); } teardownVertexInput(building->instHeader); if(defer) numBlendInsts[pass]++; } void AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha) { using namespace rw; using namespace rw::gl3; BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; atomic->getPipeline()->instance(atomic); building->instHeader = (gl3::InstanceDataHeader*)atomic->geometry->instData; assert(building->instHeader != nil); assert(building->instHeader->platform == PLATFORM_GL3); building->fadeAlpha = fadeAlpha; building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); building->matrix = *atomic->getFrame()->getLTM(); numBlendInsts[pass]++; } void RenderBlendPass(int pass) { using namespace rw; using namespace rw::gl3; defaultShader->use(); WorldLights lights; lights.numAmbients = 1; lights.numDirectionals = 0; lights.numLocals = 0; int i; for(i = 0; i < numBlendInsts[pass]; i++){ BuildingInst *building = &blendInsts[pass][i]; setupVertexInput(building->instHeader); setWorldMatrix(&building->matrix); if(building->lighting) lights.ambient = pAmbient->color; else lights.ambient = black; setLights(&lights); InstanceData *inst = building->instHeader->inst; for(rw::uint32 j = 0; j < building->instHeader->numMeshes; j++, inst++){ Material *m = inst->material; if(!inst->vertexAlpha && m->color.alpha == 255 && !IsTextureTransparent(m->texture) && building->fadeAlpha == 255) continue; // already done this one rw::RGBA color = m->color; color.alpha = (color.alpha * building->fadeAlpha)/255; setMaterial(color, m->surfaceProps); // always modulate here setTexture(0, m->texture); drawInst(building->instHeader, inst); } teardownVertexInput(building->instHeader); } } } #endif #endif ================================================ FILE: src/extras/debugmenu.cpp ================================================ #include "common.h" #ifdef DEBUGMENU #include "RwHelper.h" #include "Pad.h" #include "ControllerConfig.h" #include "Timer.h" #include "rtcharse.h" #include "re3_inttypes.h" #include "debugmenu.h" #include #ifdef _WIN32 #define snprintf _snprintf #define strdup _strdup #endif // Font stuff struct Pt { int x, y; }; enum MenuFontStyle { MENUFONT_NORMAL, MENUFONT_SEL_ACTIVE, MENUFONT_SEL_INACTIVE, MENUFONT_MOUSE }; RtCharset *fontStyles[4]; RtCharsetDesc fontDesc; int fontscale = 1; // not supported right now Pt fontGetStringSize(const char *s) { Pt sz = { 0, 0 }; int x; char c; sz.y = fontDesc.height*fontscale; // always assume one line; x = 0; while(c = *s++){ if(c == '\n'){ sz.y += fontDesc.height*fontscale; if(x > sz.x) sz.x = x; x = 0; }else x += fontDesc.width*fontscale; } if(x > sz.x) sz.x = x; return sz; } Pt fontPrint(const char *s, float x, float y, int style) { RtCharsetPrintBuffered(fontStyles[style], s, x, y, false); return fontGetStringSize(s); } int fontGetLen(int len) { return len*fontDesc.width*fontscale; } void createMenuFont(void) { OpenCharsetSafe(); RwRGBA fg_normal = { 255, 255, 255, 255 }; RwRGBA bg_normal = { 255, 255, 255, 0 }; fontStyles[MENUFONT_NORMAL] = RtCharsetCreate(&fg_normal, &bg_normal); assert(fontStyles[MENUFONT_NORMAL]); RwRGBA fg_sel_active = { 200, 200, 200, 255 }; RwRGBA bg_sel_active = { 132, 132, 132, 255 }; fontStyles[MENUFONT_SEL_ACTIVE] = RtCharsetCreate(&fg_sel_active, &bg_sel_active); assert(fontStyles[MENUFONT_SEL_ACTIVE]); RwRGBA fg_sel_inactive = { 200, 200, 200, 255 }; RwRGBA bg_sel_inactive = { 200, 200, 200, 0 }; fontStyles[MENUFONT_SEL_INACTIVE] = RtCharsetCreate(&fg_sel_inactive, &bg_sel_inactive); assert(fontStyles[MENUFONT_SEL_INACTIVE]); RwRGBA fg_mouse = { 255, 255, 255, 255 }; RwRGBA bg_mouse = { 132, 132, 132, 255 }; fontStyles[MENUFONT_MOUSE] = RtCharsetCreate(&fg_mouse, &bg_mouse); assert(fontStyles[MENUFONT_MOUSE]); RtCharsetGetDesc(fontStyles[MENUFONT_NORMAL], &fontDesc); } void destroyMenuFont(void) { RtCharsetDestroy(fontStyles[MENUFONT_NORMAL]); fontStyles[MENUFONT_NORMAL] = nil; RtCharsetDestroy(fontStyles[MENUFONT_SEL_ACTIVE]); fontStyles[MENUFONT_SEL_ACTIVE] = nil; RtCharsetDestroy(fontStyles[MENUFONT_SEL_INACTIVE]); fontStyles[MENUFONT_SEL_INACTIVE] = nil; RtCharsetDestroy(fontStyles[MENUFONT_MOUSE]); fontStyles[MENUFONT_MOUSE] = nil; } enum EntryType { MENUEMPTY = 0, MENUSUB, MENUVAR, MENUVAR_INT, MENUVAR_FLOAT, MENUVAR_CMD, MENUSCROLL // dummy }; struct Menu { Menu *parent; RwRect r; MenuEntry *entries; int numEntries; int maxNameWidth, maxValWidth; MenuEntry *findEntry(const char *entryname); void insertEntrySorted(MenuEntry *entry); void appendEntry(MenuEntry *entry); bool isScrollingUp, isScrollingDown; int scrollStart; int numVisible; RwRect scrollUpR, scrollDownR; void scroll(int off); int selection; MenuEntry *selectedEntry; // updated by update void changeSelection(int newsel); void changeSelection(MenuEntry *e); void update(void); void draw(void); Menu(void){ memset(this, 0, sizeof(Menu)); } ~Menu(void); }; extern Menu toplevel; struct MenuEntry_Sub : MenuEntry { Menu *submenu; MenuEntry_Sub(const char *name, Menu *menu); ~MenuEntry_Sub(void) { delete submenu; } }; struct MenuEntry_Var : MenuEntry { int maxvallen; int vartype; bool wrapAround; virtual void processInput(bool mouseOver, bool selected) = 0; int getValWidth(void) { return maxvallen; } virtual void getValStr(char *str, int len) = 0; MenuEntry_Var(const char *name, int type); }; struct MenuEntry_Int : MenuEntry_Var { virtual void setStrings(const char **strings) = 0; virtual int findStringLen(void) = 0; MenuEntry_Int(const char *name); }; #define INTTYPES \ X(Int8, int8, 4, "%4" PRId8) \ X(Int16, int16, 6, "%6" PRId16) \ X(Int32, int32, 11, "%11" PRId32) \ X(Int64, int64, 21, "%21" PRId64) \ X(UInt8, uint8, 4, "%4" PRIu8) \ X(UInt16, uint16, 6, "%6" PRIu16) \ X(UInt32, uint32, 11, "%11" PRIu32) \ X(UInt64, uint64, 21, "%21" PRIu64) #define FLOATTYPES \ X(Float32, float, 11, "%11.3f") \ X(Float64, double, 11, "%11.3lf") #define X(NAME, TYPE, MAXLEN, FMT) \ struct MenuEntry_##NAME : MenuEntry_Int \ { \ TYPE *variable; \ TYPE lowerBound, upperBound; \ TYPE step; \ TriggerFunc triggerFunc; \ const char *fmt; \ const char **strings; \ \ void processInput(bool mouseOver, bool selected); \ void getValStr(char *str, int len); \ \ void setStrings(const char **strings); \ int findStringLen(void); \ MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings); \ }; INTTYPES #undef X #define X(NAME, TYPE, MAXLEN, FMT) \ struct MenuEntry_##NAME : MenuEntry_Var \ { \ TYPE *variable; \ TYPE lowerBound, upperBound; \ TYPE step; \ TriggerFunc triggerFunc; \ const char *fmt; \ \ void processInput(bool mouseOver, bool selected); \ void getValStr(char *str, int len); \ \ MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound); \ }; FLOATTYPES #undef X struct MenuEntry_Cmd : MenuEntry_Var { TriggerFunc triggerFunc; void processInput(bool mouseOver, bool selected); void getValStr(char *str, int len); MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc); }; Menu *findMenu(const char *name); #define MUHKEYS \ X(leftjustdown, rsLEFT) \ X(rightjustdown, rsRIGHT) \ X(upjustdown, rsUP) \ X(downjustdown, rsDOWN) \ X(pgupjustdown, rsPGUP) \ X(pgdnjustdown, rsPGDN) #define MUHBUTTONS \ X(button1justdown, 1) \ X(button2justdown, 2) \ X(button3justdown, 3) #define REPEATDELAY 700 #define REPEATINTERVAL 50 #define X(var, keycode) static int var; MUHKEYS #undef X static int downtime; static int repeattime; static int lastkeydown; static int *keyptr; static int buttondown[3]; static int lastbuttondown; static int *buttonptr; static int button1justdown, button2justdown, button3justdown; static float mouseX, mouseY; static int menuOn; static int menuInitialized; static int screenWidth, screenHeight; static RwRaster *cursor, *arrow; static int firstBorder = 10; static int topBorder = 40; //10; static int leading = 4; static int gap = 10; static int minwidth = 100; void drawMouse(void); void drawArrow(RwRect r, int direction, int style); Menu toplevel; Menu *activeMenu = &toplevel; Menu *deepestMenu = &toplevel; Menu *mouseOverMenu; MenuEntry *mouseOverEntry; MenuEntry scrollUpEntry("SCROLLUP"), scrollDownEntry("SCROLLDOWN"); // dummies #define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k) #define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k) #define CTRLJUSTDOWN(key) \ ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \ (KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) #define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key)) bool isMouseInRect(RwRect r) { return (mouseX >= r.x && mouseX < r.x+r.w && mouseY >= r.y && mouseY < r.y+r.h); } /* * MenuEntry */ MenuEntry::MenuEntry(const char *name) { this->type = MENUEMPTY; this->name = strdup(name); this->next = nil; this->menu = nil; } MenuEntry_Sub::MenuEntry_Sub(const char *name, Menu *menu) : MenuEntry(name) { this->type = MENUSUB; this->submenu = menu; } MenuEntry_Var::MenuEntry_Var(const char *name, int vartype) : MenuEntry(name) { this->type = MENUVAR; this->vartype = vartype; this->maxvallen = 0; this->wrapAround = false; } /* * ***************************** * MenuEntry_Int * ***************************** */ MenuEntry_Int::MenuEntry_Int(const char *name) : MenuEntry_Var(name, MENUVAR_INT) { } #define X(NAME, TYPE, MAXLEN, FMT) \ int \ MenuEntry_##NAME::findStringLen(void){ \ TYPE i; \ int len, maxlen = 0; \ for(i = this->lowerBound; i <= this->upperBound; i++){ \ len = strlen(this->strings[i-this->lowerBound]); \ if(len > maxlen) \ maxlen = len; \ } \ return maxlen; \ } \ void \ MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \ { \ TYPE v, oldv; \ int overflow = 0; \ int underflow = 0; \ \ v = *this->variable; \ oldv = v; \ \ if((selected && leftjustdown) || (mouseOver && button3justdown)){ \ v -= this->step; \ if(v > oldv) \ underflow = 1; \ } \ if((selected && rightjustdown) || (mouseOver && button1justdown)){ \ v += this->step; \ if(v < oldv) \ overflow = 1; \ } \ if(this->wrapAround){ \ if(v > this->upperBound || overflow) v = this->lowerBound; \ if(v < this->lowerBound || underflow) v = this->upperBound; \ }else{ \ if(v > this->upperBound || overflow) v = this->upperBound; \ if(v < this->lowerBound || underflow) v = this->lowerBound; \ } \ \ *this->variable = v; \ if(oldv != v && this->triggerFunc) \ this->triggerFunc(); \ } \ void \ MenuEntry_##NAME::getValStr(char *str, int len) \ { \ static char tmp[20]; \ if(this->strings){ \ snprintf(tmp, 20, "%%%ds", this->maxvallen); \ if(*this->variable < this->lowerBound || *this->variable > this->upperBound){ \ snprintf(str, len, "ERROR"); \ return; \ } \ snprintf(str, len, tmp, this->strings[*this->variable-this->lowerBound]); \ }else \ snprintf(str, len, this->fmt, *this->variable); \ } \ void \ MenuEntry_##NAME::setStrings(const char **strings) \ { \ this->strings = strings; \ if(this->strings) \ this->maxvallen = findStringLen(); \ } \ MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \ : MenuEntry_Int(name) \ { \ this->variable = ptr; \ this->step = step; \ this->lowerBound = lowerBound; \ this->upperBound = upperBound; \ this->triggerFunc = triggerFunc; \ this->maxvallen = MAXLEN; \ this->fmt = FMT; \ this->setStrings(strings); \ } INTTYPES #undef X /* * ***************************** * MenuEntry_Float * ***************************** */ #define X(NAME, TYPE, MAXLEN, FMT) \ MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \ : MenuEntry_Var(name, MENUVAR_FLOAT) \ { \ this->variable = ptr; \ this->step = step; \ this->lowerBound = lowerBound; \ this->upperBound = upperBound; \ this->triggerFunc = triggerFunc; \ this->maxvallen = MAXLEN; \ this->fmt = FMT; \ } \ void \ MenuEntry_##NAME::getValStr(char *str, int len) \ { \ snprintf(str, len, this->fmt, *this->variable); \ } \ void \ MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \ { \ float v, oldv; \ int overflow = 0; \ int underflow = 0; \ \ v = *this->variable; \ oldv = v; \ \ if((selected && leftjustdown) || (mouseOver && button3justdown)){ \ v -= this->step; \ if(v > oldv) \ underflow = 1; \ } \ if((selected && rightjustdown) || (mouseOver && button1justdown)){ \ v += this->step; \ if(v < oldv) \ overflow = 1; \ } \ if(this->wrapAround){ \ if(v > this->upperBound || overflow) v = this->lowerBound; \ if(v < this->lowerBound || underflow) v = this->upperBound; \ }else{ \ if(v > this->upperBound || overflow) v = this->upperBound; \ if(v < this->lowerBound || underflow) v = this->lowerBound; \ } \ \ *this->variable = v; \ if(oldv != v && this->triggerFunc) \ this->triggerFunc(); \ } FLOATTYPES #undef X /* * ***************************** * MenuEntry_Cmd * ***************************** */ void MenuEntry_Cmd::processInput(bool mouseOver, bool selected) { // Don't execute on button3 if(this->triggerFunc && (selected && (leftjustdown || rightjustdown) || (mouseOver && button1justdown))) this->triggerFunc(); } void MenuEntry_Cmd::getValStr(char *str, int len) { strncpy(str, "<", len); } MenuEntry_Cmd::MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc) : MenuEntry_Var(name, MENUVAR_CMD) { this->maxvallen = 1; this->triggerFunc = triggerFunc; } /* * ***************************** * Menu * ***************************** */ void Menu::scroll(int off) { if(isScrollingUp && off < 0) scrollStart += off; if(isScrollingDown && off > 0) scrollStart += off; if(scrollStart < 0) scrollStart = 0; if(scrollStart > numEntries-numVisible) scrollStart = numEntries-numVisible; } void Menu::changeSelection(int newsel){ selection = newsel; if(selection < 0) selection = 0; if(selection >= numEntries) selection = numEntries-1; if(selection < scrollStart) scrollStart = selection; if(selection >= scrollStart+numVisible) scrollStart = selection-numVisible+1; } void Menu::changeSelection(MenuEntry *sel) { MenuEntry *e; int i = 0; for(e = this->entries; e; e = e->next){ if(e == sel){ this->selection = i; this->selectedEntry = sel; break; } i++; } } MenuEntry* Menu::findEntry(const char *entryname) { MenuEntry *m; for(m = this->entries; m; m = m->next) if(strcmp(entryname, m->name) == 0) return m; return nil; } void Menu::insertEntrySorted(MenuEntry *entry) { MenuEntry **mp; int cmp; for(mp = &this->entries; *mp; mp = &(*mp)->next){ cmp = strcmp(entry->name, (*mp)->name); if(cmp == 0) return; if(cmp < 0) break; } entry->next = *mp; *mp = entry; entry->menu = this; this->numEntries++; } void Menu::appendEntry(MenuEntry *entry) { MenuEntry **mp; for(mp = &this->entries; *mp; mp = &(*mp)->next); entry->next = *mp; *mp = entry; entry->menu = this; this->numEntries++; } void Menu::update(void) { int i; int x, y; Pt sz; MenuEntry *e; int onscreen; x = this->r.x; y = this->r.y + 18; int end = this->r.y+this->r.h - 18; this->numVisible = 0; deepestMenu = this; int bottomy = end; onscreen = 1; i = 0; this->maxNameWidth = 0; this->maxValWidth = 0; this->isScrollingUp = this->scrollStart > 0; this->isScrollingDown = false; this->selectedEntry = nil; for(e = this->entries; e; e = e->next){ sz = fontGetStringSize(e->name); e->r.x = x; e->r.y = y; e->r.w = sz.x; e->r.h = sz.y; if(i == this->selection) this->selectedEntry = e; if(i >= this->scrollStart) y += sz.y + leading*fontscale; if(y >= end){ this->isScrollingDown = true; onscreen = 0; }else bottomy = y; if(i >= this->scrollStart && onscreen) this->numVisible++; if(e->type == MENUVAR){ int valwidth = fontGetLen(((MenuEntry_Var*)e)->getValWidth()); if(valwidth > maxValWidth) maxValWidth = valwidth; } if(e->r.w > maxNameWidth) maxNameWidth = e->r.w; i++; } if(this->r.w < maxNameWidth + maxValWidth + gap*fontscale) this->r.w = maxNameWidth + maxValWidth + gap*fontscale; this->scrollUpR = this->r; this->scrollUpR.h = 16; this->scrollDownR = this->scrollUpR; this->scrollDownR.y = bottomy; // Update active submenu if(this->selectedEntry && this->selectedEntry->type == MENUSUB){ Menu *submenu = ((MenuEntry_Sub*)this->selectedEntry)->submenu; submenu->r.x = this->r.x+this->r.w + 10; submenu->r.y = this->r.y; submenu->r.w = minwidth; // update menu will expand submenu->r.h = this->r.h; submenu->update(); } } void Menu::draw(void) { static char val[100]; int i; MenuEntry *e; i = 0; for(e = this->entries; e; e = e->next){ if(i >= this->scrollStart+this->numVisible) break; if(i >= this->scrollStart){ int style = MENUFONT_NORMAL; if(i == this->selection) style = this == activeMenu ? MENUFONT_SEL_ACTIVE : MENUFONT_SEL_INACTIVE; if(style != MENUFONT_SEL_ACTIVE && e == mouseOverEntry) style = MENUFONT_MOUSE; fontPrint(e->name, e->r.x, e->r.y, style); if(e->type == MENUVAR){ int valw = fontGetLen(((MenuEntry_Var*)e)->getValWidth()); ((MenuEntry_Var*)e)->getValStr(val, 100); fontPrint(val, e->r.x+this->r.w-valw, e->r.y, style); } } i++; } if(this->isScrollingUp) drawArrow(this->scrollUpR, -1, isMouseInRect(this->scrollUpR)); if(this->isScrollingDown) drawArrow(this->scrollDownR, 1, isMouseInRect(this->scrollDownR)); if(this->selectedEntry && this->selectedEntry->type == MENUSUB) ((MenuEntry_Sub*)this->selectedEntry)->submenu->draw(); } Menu::~Menu(void) { MenuEntry *e, *next; for(e = entries; e; e = next){ next = e->next; delete e; } memset(this, 0, sizeof(Menu)); } Menu* findMenu(const char *name) { Menu *m; MenuEntry *e; char *tmppath = strdup(name); char *next, *curname; curname = tmppath; next = curname; m = &toplevel; while(*next){ curname = next; while(*next){ if(*next == '|'){ *next++ = '\0'; break; } next++; } e = m->findEntry(curname); if(e){ // return an error if the entry exists but isn't a menu if(e->type != MENUSUB){ free(tmppath); return nil; } m = ((MenuEntry_Sub*)e)->submenu; }else{ // Create submenus that don't exist yet Menu *submenu = new Menu(); submenu->parent = m; MenuEntry *me = new MenuEntry_Sub(curname, submenu); // Don't sort submenus outside the toplevel menu if(m == &toplevel) m->insertEntrySorted(me); else m->appendEntry(me); m = submenu; } } free(tmppath); return m; } /* * **************** * debug menu * **************** */ static uint8 cursorPx[] = { #include "cursor.inc" }; static uint8 arrowPx[] = { #include "arrow.inc" }; void DebugMenuInit(void) { createMenuFont(); RwInt32 w, h, d, flags; RwImage *img = RwImageCreate(16, 16, 32); assert(img); RwImageSetPixels(img, cursorPx); RwImageSetStride(img, RwImageGetWidth(img)*4); RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags); cursor = RwRasterCreate(w, h, d, flags); cursor = RwRasterSetFromImage(cursor, img); assert(cursor); RwImageDestroy(img); img = RwImageCreate(32, 16, 32); assert(img); RwImageSetPixels(img, arrowPx); RwImageSetStride(img, RwImageGetWidth(img)*4); RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags); arrow = RwRasterCreate(w, h, d, flags); arrow = RwRasterSetFromImage(arrow, img); assert(arrow); RwImageDestroy(img); menuInitialized = true; } void DebugMenuShutdown(void) { if(menuInitialized){ destroyMenuFont(); RwRasterDestroy(cursor); cursor = nil; RwRasterDestroy(arrow); arrow = nil; toplevel.~Menu(); new (&toplevel) Menu(); activeMenu = &toplevel; deepestMenu = &toplevel; mouseOverMenu = nil; mouseOverEntry = nil; } menuInitialized = false; } void processInput(void) { int shift = KEYDOWN(rsRSHIFT) || KEYDOWN(rsLSHIFT); #define X(var, keycode) var = KEYJUSTDOWN(keycode); MUHKEYS #undef X // Implement auto-repeat #define X(var, keycode) \ if(var){ \ repeattime = downtime = CTimer::GetTimeInMilliseconds(); \ lastkeydown = keycode; \ keyptr = &var; \ } MUHKEYS #undef X if(lastkeydown){ if(KEYDOWN(lastkeydown)){ int curtime = CTimer::GetTimeInMilliseconds(); if(curtime - downtime > REPEATDELAY){ if(curtime - repeattime > REPEATINTERVAL){ repeattime = curtime; *keyptr = 1; } } }else{ lastkeydown = 0; } } // Also for mouse buttons #define X(var, num) \ if(var){ \ repeattime = downtime = CTimer::GetTimeInMilliseconds(); \ lastbuttondown = num; \ buttonptr = &var; \ } MUHBUTTONS #undef X if(lastbuttondown){ if(buttondown[lastbuttondown-1]){ int curtime = CTimer::GetTimeInMilliseconds(); if(curtime - downtime > REPEATDELAY){ if(curtime - repeattime > REPEATINTERVAL){ repeattime = curtime; *buttonptr = 1; } } }else{ lastbuttondown = 0; } } // Walk through all visible menus and figure out which one the mouse is over mouseOverMenu = nil; mouseOverEntry = nil; Menu *menu; for(menu = deepestMenu; menu; menu = menu->parent) if(isMouseInRect(menu->r)){ mouseOverMenu = menu; break; } if(mouseOverMenu){ // Walk all visibile entries and figure out which one the mouse is over MenuEntry *e; int i = 0; for(e = mouseOverMenu->entries; e; e = e->next){ if(i >= mouseOverMenu->scrollStart+mouseOverMenu->numVisible) break; if(i >= mouseOverMenu->scrollStart){ RwRect r = e->r; r.w = mouseOverMenu->r.w; // span the whole menu if(isMouseInRect(r)){ mouseOverEntry = e; break; } } i++; } if(mouseOverMenu->isScrollingUp && isMouseInRect(mouseOverMenu->scrollUpR)){ mouseOverEntry = &scrollUpEntry; mouseOverEntry->r = mouseOverMenu->scrollUpR; mouseOverEntry->menu = mouseOverMenu; mouseOverEntry->type = MENUSCROLL; } if(mouseOverMenu->isScrollingDown && isMouseInRect(mouseOverMenu->scrollDownR)){ mouseOverEntry = &scrollDownEntry; mouseOverEntry->r = mouseOverMenu->scrollDownR; mouseOverEntry->menu = mouseOverMenu; mouseOverEntry->type = MENUSCROLL; } } if(pgupjustdown) activeMenu->scroll(shift ? -5 : -1); if(pgdnjustdown) activeMenu->scroll(shift ? 5 : 1); if(downjustdown) activeMenu->changeSelection(activeMenu->selection + (shift ? 5 : 1)); if(upjustdown) activeMenu->changeSelection(activeMenu->selection - (shift ? 5 : 1)); if(CPad::NewMouseControllerState.WHEELUP){ if(mouseOverMenu) activeMenu = mouseOverMenu; activeMenu->scroll(shift ? -5 : -1); } if(CPad::NewMouseControllerState.WHEELDN){ if(mouseOverMenu) activeMenu = mouseOverMenu; activeMenu->scroll(shift ? 5 : 1); } if(mouseOverEntry == &scrollUpEntry){ if(button1justdown){ activeMenu = mouseOverEntry->menu; activeMenu->scroll(shift ? -5 : -1); } } if(mouseOverEntry == &scrollDownEntry){ if(button1justdown){ activeMenu = mouseOverEntry->menu; activeMenu->scroll(shift ? 5 : 1); } } // Have to call this before processInput below because menu entry can change if((button1justdown || button3justdown) && mouseOverEntry){ activeMenu = mouseOverEntry->menu; activeMenu->changeSelection(mouseOverEntry); } if(KEYJUSTDOWN(rsENTER)){ if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUSUB) activeMenu = ((MenuEntry_Sub*)activeMenu->selectedEntry)->submenu; }else if(KEYJUSTDOWN(rsBACKSP)){ if(activeMenu->parent) activeMenu = activeMenu->parent; }else{ if(mouseOverEntry && mouseOverEntry->type == MENUVAR) ((MenuEntry_Var*)mouseOverEntry)->processInput(true, mouseOverEntry == activeMenu->selectedEntry); if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUVAR && mouseOverEntry != activeMenu->selectedEntry) ((MenuEntry_Var*)activeMenu->selectedEntry)->processInput(false, true); } } void updateMouse(void) { CPad *pad = CPad::GetPad(0); int dirX = 1; int dirY = 1; if(MousePointerStateHelper.bInvertHorizontally) dirX = -1; if(MousePointerStateHelper.bInvertVertically) dirY = -1; mouseX += pad->NewMouseControllerState.x*dirX; mouseY += pad->NewMouseControllerState.y*dirY; if(mouseX < 0.0f) mouseX = 0.0f; if(mouseY < 0.0f) mouseY = 0.0f; if(mouseX >= screenWidth) mouseX = screenWidth; if(mouseY >= screenHeight) mouseY = screenHeight; button1justdown = pad->NewMouseControllerState.LMB && !pad->OldMouseControllerState.LMB; button2justdown = pad->NewMouseControllerState.MMB && !pad->OldMouseControllerState.MMB; button3justdown = pad->NewMouseControllerState.RMB && !pad->OldMouseControllerState.RMB; buttondown[0] = pad->NewMouseControllerState.LMB; buttondown[1] = pad->NewMouseControllerState.MMB; buttondown[2] = pad->NewMouseControllerState.RMB; // Zero the mouse position so the camera won't move pad->NewMouseControllerState.x = 0.0f; pad->NewMouseControllerState.y = 0.0f; } void DebugMenuProcess(void) { // We only process some input here CPad *pad = CPad::GetPad(0); if(CTRLJUSTDOWN('M')) menuOn = !menuOn; if(!menuOn) return; pad->DisablePlayerControls = 1; // TODO: this could happen earlier if(!menuInitialized) DebugMenuInit(); updateMouse(); } void DebugMenuRender(void) { if(!menuOn) return; RwRenderStateSet(rwRENDERSTATEZTESTENABLE, 0); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, 0); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEFOGENABLE, 0); RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); RwCamera *cam = RwCameraGetCurrentCamera(); screenWidth = RwRasterGetWidth(RwCameraGetRaster(cam)); screenHeight = RwRasterGetHeight(RwCameraGetRaster(cam)); // if(screenHeight > 1080) // fontscale = 2; // else fontscale = 1; Pt sz; sz = fontPrint("Debug Menu", firstBorder*fontscale+30, topBorder, 0); toplevel.r.x = firstBorder*fontscale; toplevel.r.y = topBorder + sz.y + 10; toplevel.r.w = minwidth; // update menu will expand toplevel.r.h = screenHeight - 10 - toplevel.r.y; toplevel.update(); toplevel.draw(); processInput(); RtCharsetBufferFlush(); drawMouse(); } void drawArrow(RwRect r, int direction, int style) { static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 }; static RwIm2DVertex arrowVerts[4]; RwCamera *cam = RwCameraGetCurrentCamera(); float recipz = 1.0f/RwCameraGetNearClipPlane(cam); int width = RwRasterGetWidth(arrow); int height = RwRasterGetHeight(arrow); int left = r.x + (r.w - width)/2; int right = left + width; int top = r.y; int bottom = r.y+r.h; float umin = HALFPX / width; float vmin = HALFPX / height; float umax = (width + HALFPX) / width; float vmax = (height + HALFPX) / height; if(direction < 0){ vmin = (height - HALFPX) / height; vmax = -HALFPX / height; } if(style){ RwIm2DVertexSetScreenX(&arrowVerts[0], r.x); RwIm2DVertexSetScreenY(&arrowVerts[0], r.y-1); RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[0], 132, 132, 132, 255); RwIm2DVertexSetScreenX(&arrowVerts[1], r.x+r.w); RwIm2DVertexSetScreenY(&arrowVerts[1], r.y-1); RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[1], 132, 132, 132, 255); RwIm2DVertexSetScreenX(&arrowVerts[2], r.x); RwIm2DVertexSetScreenY(&arrowVerts[2], r.y+r.h+1); RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[2], 132, 132, 132, 255); RwIm2DVertexSetScreenX(&arrowVerts[3], r.x+r.w); RwIm2DVertexSetScreenY(&arrowVerts[3], r.y+r.h+1); RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[3], 132, 132, 132, 255); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6); } RwIm2DVertexSetScreenX(&arrowVerts[0], left); RwIm2DVertexSetScreenY(&arrowVerts[0], top); RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[0], 255, 255, 255, 255); RwIm2DVertexSetU(&arrowVerts[0], umin, recipz); RwIm2DVertexSetV(&arrowVerts[0], vmin, recipz); RwIm2DVertexSetScreenX(&arrowVerts[1], right); RwIm2DVertexSetScreenY(&arrowVerts[1], top); RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[1], 255, 255, 255, 255); RwIm2DVertexSetU(&arrowVerts[1], umax, recipz); RwIm2DVertexSetV(&arrowVerts[1], vmin, recipz); RwIm2DVertexSetScreenX(&arrowVerts[2], left); RwIm2DVertexSetScreenY(&arrowVerts[2], bottom); RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[2], 255, 255, 255, 255); RwIm2DVertexSetU(&arrowVerts[2], umin, recipz); RwIm2DVertexSetV(&arrowVerts[2], vmax, recipz); RwIm2DVertexSetScreenX(&arrowVerts[3], right); RwIm2DVertexSetScreenY(&arrowVerts[3], bottom); RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz); RwIm2DVertexSetIntRGBA(&arrowVerts[3], 255, 255, 255, 255); RwIm2DVertexSetU(&arrowVerts[3], umax, recipz); RwIm2DVertexSetV(&arrowVerts[3], vmax, recipz); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, arrow); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6); } void drawMouse(void) { static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 }; static RwIm2DVertex vertices[4]; RwIm2DVertex *vert; RwCamera *cam; cam = RwCameraGetCurrentCamera(); float x = mouseX; float y = mouseY; float w = RwRasterGetWidth(cursor); float h = RwRasterGetHeight(cursor); float recipz = 1.0f/RwCameraGetNearClipPlane(cam); float umin = HALFPX / w; float vmin = HALFPX / h; float umax = (w + HALFPX) / w; float vmax = (h + HALFPX) / h; vert = vertices; RwIm2DVertexSetScreenX(vert, x); RwIm2DVertexSetScreenY(vert, y); RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(vert, recipz); RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); RwIm2DVertexSetU(vert, umin, recipz); RwIm2DVertexSetV(vert, vmin, recipz); vert++; RwIm2DVertexSetScreenX(vert, x+w); RwIm2DVertexSetScreenY(vert, y); RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(vert, recipz); RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); RwIm2DVertexSetU(vert, umax, recipz); RwIm2DVertexSetV(vert, vmin, recipz); vert++; RwIm2DVertexSetScreenX(vert, x); RwIm2DVertexSetScreenY(vert, y+h); RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(vert, recipz); RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); RwIm2DVertexSetU(vert, umin, recipz); RwIm2DVertexSetV(vert, vmax, recipz); vert++; RwIm2DVertexSetScreenX(vert, x+w); RwIm2DVertexSetScreenY(vert, y+h); RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(vert, recipz); RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255); RwIm2DVertexSetU(vert, umax, recipz); RwIm2DVertexSetV(vert, vmax, recipz); vert++; RwRenderStateSet(rwRENDERSTATETEXTURERASTER, cursor); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, vertices, 4, indices, 6); } /* * Generate interfaces */ #define X(NAME, TYPE, unused1, unused2) \ MenuEntry* \ DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \ { \ Menu *m = findMenu(path); \ if(m == nil) \ return nil; \ MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound, strings); \ m->appendEntry(e); \ return e; \ } INTTYPES #undef X #define X(NAME, TYPE, unused1, unused2) \ MenuEntry* \ DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \ { \ Menu *m = findMenu(path); \ if(m == nil) \ return nil; \ MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound); \ m->appendEntry(e); \ return e; \ } FLOATTYPES #undef X MenuEntry* \ DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc) { Menu *m = findMenu(path); if(m == nil) return nil; MenuEntry *e = new MenuEntry_Cmd(name, triggerFunc); m->appendEntry(e); return e; } void DebugMenuEntrySetWrap(MenuEntry *e, bool wrap) { if(e && e->type == MENUVAR) ((MenuEntry_Var*)e)->wrapAround = wrap; } void DebugMenuEntrySetStrings(MenuEntry *e, const char **strings) { if(e && e->type == MENUVAR_INT) ((MenuEntry_Int*)e)->setStrings(strings); } void DebugMenuEntrySetAddress(MenuEntry *e, void *addr) { if(e && e->type == MENUVAR){ MenuEntry_Var *ev = (MenuEntry_Var*)e; // HACK - we know the variable field is at the same address // for all int/float classes. let's hope it stays that way. if(ev->vartype = MENUVAR_INT) ((MenuEntry_Int32*)e)->variable = (int32*)addr; else if(ev->vartype = MENUVAR_FLOAT) ((MenuEntry_Float32*)e)->variable = (float*)addr; } } #endif ================================================ FILE: src/extras/debugmenu.h ================================================ #pragma once #ifdef DEBUGMENU typedef void (*TriggerFunc)(void); struct Menu; struct MenuEntry { int type; const char *name; MenuEntry *next; RwRect r; Menu *menu; MenuEntry(const char *name); virtual ~MenuEntry(void) { free((void*)name); } }; typedef MenuEntry DebugMenuEntry; MenuEntry *DebugMenuAddInt8(const char *path, const char *name, int8 *ptr, TriggerFunc triggerFunc, int8 step, int8 lowerBound, int8 upperBound, const char **strings); MenuEntry *DebugMenuAddInt16(const char *path, const char *name, int16 *ptr, TriggerFunc triggerFunc, int16 step, int16 lowerBound, int16 upperBound, const char **strings); MenuEntry *DebugMenuAddInt32(const char *path, const char *name, int32 *ptr, TriggerFunc triggerFunc, int32 step, int32 lowerBound, int32 upperBound, const char **strings); MenuEntry *DebugMenuAddInt64(const char *path, const char *name, int64 *ptr, TriggerFunc triggerFunc, int64 step, int64 lowerBound, int64 upperBound, const char **strings); MenuEntry *DebugMenuAddUInt8(const char *path, const char *name, uint8 *ptr, TriggerFunc triggerFunc, uint8 step, uint8 lowerBound, uint8 upperBound, const char **strings); MenuEntry *DebugMenuAddUInt16(const char *path, const char *name, uint16 *ptr, TriggerFunc triggerFunc, uint16 step, uint16 lowerBound, uint16 upperBound, const char **strings); MenuEntry *DebugMenuAddUInt32(const char *path, const char *name, uint32 *ptr, TriggerFunc triggerFunc, uint32 step, uint32 lowerBound, uint32 upperBound, const char **strings); MenuEntry *DebugMenuAddUInt64(const char *path, const char *name, uint64 *ptr, TriggerFunc triggerFunc, uint64 step, uint64 lowerBound, uint64 upperBound, const char **strings); MenuEntry *DebugMenuAddFloat32(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound); MenuEntry *DebugMenuAddFloat64(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound); MenuEntry *DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc); void DebugMenuEntrySetWrap(MenuEntry *e, bool wrap); void DebugMenuEntrySetStrings(MenuEntry *e, const char **strings); void DebugMenuEntrySetAddress(MenuEntry *e, void *addr); void DebugMenuInit(void); void DebugMenuShutdown(void); void DebugMenuProcess(void); void DebugMenuRender(void); // Some overloads for simplicity inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings) { return DebugMenuAddInt8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); } inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings) { return DebugMenuAddInt16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); } inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings) { return DebugMenuAddInt32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); } inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings) { return DebugMenuAddInt64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); } inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings) { return DebugMenuAddUInt8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); } inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings) { return DebugMenuAddUInt16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); } inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings) { return DebugMenuAddUInt32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); } inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings) { return DebugMenuAddUInt64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); } inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound) { return DebugMenuAddFloat32(path, name, ptr, triggerFunc, step, lowerBound, upperBound); } inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound) { return DebugMenuAddFloat64(path, name, ptr, triggerFunc, step, lowerBound, upperBound); } inline DebugMenuEntry *DebugMenuAddVarBool32(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc) { static const char *boolstr[] = { "Off", "On" }; DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr); DebugMenuEntrySetWrap(e, true); return e; } inline DebugMenuEntry *DebugMenuAddVarBool16(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc) { static const char *boolstr[] = { "Off", "On" }; DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr); DebugMenuEntrySetWrap(e, true); return e; } inline DebugMenuEntry *DebugMenuAddVarBool8(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc) { static const char *boolstr[] = { "Off", "On" }; DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr); DebugMenuEntrySetWrap(e, true); return e; } inline DebugMenuEntry *DebugMenuAddVarBool8(const char *path, const char *name, bool *ptr, TriggerFunc triggerFunc) { return DebugMenuAddVarBool8(path, name, (int8_t*)ptr, triggerFunc); } #endif ================================================ FILE: src/extras/frontendoption.cpp ================================================ #include "common.h" #ifdef CUSTOM_FRONTEND_OPTIONS #include "Frontend.h" #include "Text.h" int lastOgScreen = MENUPAGES; // means no new pages int numCustomFrontendOptions = 0; int numCustomFrontendScreens = 0; int optionCursor = -2; int currentMenu; bool optionOverwrite = false; void GoBack() { FrontEndMenuManager.SwitchToNewScreen(-1); } uint8 GetNumberOfMenuOptions(int screen) { uint8 Rows = 0; for (int i = 0; i < NUM_MENUROWS; i++) { if (aScreens[screen].m_aEntries[i].m_Action == MENUACTION_NOTHING) break; ++Rows; } return Rows; } uint8 GetLastMenuScreen() { int8 page = -1; for (int i = 0; i < MENUPAGES; i++) { if (strcmp(aScreens[i].m_ScreenName, "") == 0 && aScreens[i].m_PreviousPage == MENUPAGE_NONE) break; ++page; } return page; } int8 RegisterNewScreen(const char *name, int prevPage, ReturnPrevPageFunc returnPrevPageFunc) { if (lastOgScreen == MENUPAGES) lastOgScreen = GetLastMenuScreen(); numCustomFrontendScreens++; int id = lastOgScreen + numCustomFrontendScreens; assert(id < MENUPAGES && "No room for new custom frontend screens! Increase MENUPAGES"); strncpy(aScreens[id].m_ScreenName, name, 8); aScreens[id].m_PreviousPage = prevPage; aScreens[id].returnPrevPageFunc = returnPrevPageFunc; return id; } int8 RegisterNewOption() { numCustomFrontendOptions++; uint8 numOptions = GetNumberOfMenuOptions(currentMenu); uint8 curIdx; if (optionCursor < 0) { optionCursor = curIdx = numOptions + optionCursor + 1; } else curIdx = optionCursor; if (!optionOverwrite) { if (aScreens[currentMenu].m_aEntries[curIdx].m_Action != MENUACTION_NOTHING) { for (int i = numOptions - 1; i >= curIdx; i--) { memcpy(&aScreens[currentMenu].m_aEntries[i + 1], &aScreens[currentMenu].m_aEntries[i], sizeof(CMenuScreenCustom::CMenuEntry)); } } } optionCursor++; return curIdx; } void FrontendOptionSetCursor(int screen, int8 option, bool overwrite) { currentMenu = screen; optionCursor = option; optionOverwrite = overwrite; } void FrontendOptionAddBuiltinAction(const char* gxtKey, uint16 x, uint16 y, uint8 align, int action, int targetMenu, int saveSlot) { int8 screenOptionOrder = RegisterNewOption(); CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder]; // We can't use custom text on those :shrug: switch (action) { case MENUACTION_SCREENRES: strcpy(option.m_EntryName, "FED_RES"); break; case MENUACTION_AUDIOHW: strcpy(option.m_EntryName, "FEA_3DH"); break; default: strncpy(option.m_EntryName, gxtKey, 8); break; } option.m_X = x; option.m_Y = y; option.m_Align = align; option.m_Action = action; option.m_SaveSlot = saveSlot; option.m_TargetMenu = targetMenu; } void FrontendOptionAddSelect(const char* gxtKey, uint16 x, uint16 y, uint8 align, const char** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, const char* saveCat, const char* saveName, bool disableIfGameLoaded) { int8 screenOptionOrder = RegisterNewOption(); CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder]; option.m_Action = MENUACTION_CFO_SELECT; option.m_X = x; option.m_Y = y; option.m_Align = align; strncpy(option.m_EntryName, gxtKey, 8); option.m_CFOSelect = new CCFOSelect(); option.m_CFOSelect->rightTexts = (char**)malloc(numRightTexts * sizeof(char*)); memcpy(option.m_CFOSelect->rightTexts, rightTexts, numRightTexts * sizeof(char*)); option.m_CFOSelect->numRightTexts = numRightTexts; option.m_CFOSelect->value = var; if (var) { option.m_CFOSelect->displayedValue = *var; option.m_CFOSelect->lastSavedValue = *var; } option.m_CFOSelect->saveCat = saveCat; option.m_CFOSelect->save = saveName; option.m_CFOSelect->onlyApplyOnEnter = onlyApplyOnEnter; option.m_CFOSelect->changeFunc = changeFunc; option.m_CFOSelect->disableIfGameLoaded = disableIfGameLoaded; } void FrontendOptionAddDynamic(const char* gxtKey, uint16 x, uint16 y, uint8 align, DrawFunc drawFunc, int8 *var, ButtonPressFunc buttonPressFunc, const char* saveCat, const char* saveName) { int8 screenOptionOrder = RegisterNewOption(); CMenuScreenCustom::CMenuEntry &option = aScreens[currentMenu].m_aEntries[screenOptionOrder]; option.m_Action = MENUACTION_CFO_DYNAMIC; option.m_X = x; option.m_Y = y; option.m_Align = align; strncpy(option.m_EntryName, gxtKey, 8); option.m_CFODynamic = new CCFODynamic(); option.m_CFODynamic->drawFunc = drawFunc; option.m_CFODynamic->buttonPressFunc = buttonPressFunc; option.m_CFODynamic->value = var; option.m_CFODynamic->saveCat = saveCat; option.m_CFODynamic->save = saveName; } // lineHeight = 0 means game will use MENU_DEFAULT_LINE_HEIGHT uint8 FrontendScreenAdd(const char* gxtKey, int prevPage, int lineHeight, bool showLeftRightHelper, ReturnPrevPageFunc returnPrevPageFunc) { uint8 screenOrder = RegisterNewScreen(gxtKey, prevPage, returnPrevPageFunc); CCustomScreenLayout *screen = new CCustomScreenLayout(); aScreens[screenOrder].layout = screen; screen->lineHeight = lineHeight; screen->showLeftRightHelper = showLeftRightHelper; return screenOrder; } #endif ================================================ FILE: src/extras/frontendoption.h ================================================ #pragma once #include "common.h" #ifdef CUSTOM_FRONTEND_OPTIONS // ! There are 2 ways to use CFO, // 1st; by adding a new option to the array in MenuScreensCustom.cpp and passing attributes/CBs to it // 2nd; by calling the functions listed at the bottom of this file. // -- Option types // // Static/select: You allocate the variable, pass it to function and game sets it from user input among the strings given to function, // optionally you can add post-change event via ChangeFunc(only called on enter if onlyApplyOnEnter set, or set immediately) // You can store the option in an INI file if you pass the key(as a char array) to corresponding parameter. // // Dynamic: Passing variable to function is only needed if you want to store it, otherwise you should do // all the operations with ButtonPressFunc, this includes allocating the variable. // Left-side text is passed while creating and static, but ofc right-side text is dynamic - // you should return it in DrawFunc, which is called on every draw. // // Built-in action: As the name suggests, any action that game has built-in. But as an extra you can set the option text, // -- Returned via ButtonPressFunc() action param. #define FEOPTION_ACTION_LEFT 0 #define FEOPTION_ACTION_RIGHT 1 #define FEOPTION_ACTION_SELECT 2 #define FEOPTION_ACTION_FOCUSLOSS 3 // -- Callbacks // pretty much in everything I guess, and optional in all of them typedef void (*ReturnPrevPageFunc)(); // for static options typedef void (*ChangeFunc)(int8 before, int8 after); // called after updating the value. // only called on enter if onlyApplyOnEnter set, otherwise called on every value change // for dynamic options typedef wchar* (*DrawFunc)(bool* disabled, bool userHovering); // you must return a pointer for right text. // you can also set *disabled if you want to gray it out. typedef void (*ButtonPressFunc)(int8 action); // see FEOPTION_ACTIONs above // -- Internal things void CustomFrontendOptionsPopulate(); extern int lastOgScreen; // for reloading extern int numCustomFrontendOptions; extern int numCustomFrontendScreens; // -- To be used in ButtonPressFunc / ChangeFunc(this one would be weird): void GoBack(void); uint8 GetNumberOfMenuOptions(int screen); // !!! We're now moved to MenuScreensCustom.cpp, which houses an array that keeps all original+custom options. // But you can still use the APIs below, and manipulate aScreens while in game. // Limits: // The code relies on that you won't use more then NUM_MENUROWS(18) options on one page, and won't exceed the MENUPAGES of pages. // Also congrats if you can make 18 options visible at once. // Texts: // All text parameters accept char[8] GXT key. // Execute direction: // All of the calls below eventually manipulate the aScreens array, so keep in mind to add/replace options in order, // i.e. don't set cursor to 8 first and then 3. // -- Placing the cursor to append/overwrite option // // Done via FrontendOptionSetCursor(screen, position, overwrite = false), parameters explained below: // Screen: as the name suggests. Also accepts the screen IDs returned from FrontendScreenAdd. // Option: if positive, next AddOption call will put the option to there and progress the cursor. // if negative, cursor will be placed on bottom-(pos+1), so -1 means the very bottom, -2 means before the back button etc. // Overwrite: Use to overwrite the options, not appending a new one. AddOption calls will still progress the cursor. void FrontendOptionSetCursor(int screen, int8 option, bool overwrite = false); // var is optional in AddDynamic, enables you to save them in an INI file(also needs passing char array to saveCat and saveKey param. obv), otherwise pass nil/0 void FrontendOptionAddBuiltinAction(const char* gxtKey, uint16 x, uint16 y, uint8 align, int action, int targetMenu = MENUPAGE_NONE, int saveSlot = SAVESLOT_NONE); void FrontendOptionAddSelect(const char* gxtKey, uint16 x, uint16 y, uint8 align, const char** rightTexts, int8 numRightTexts, int8 *var, bool onlyApplyOnEnter, ChangeFunc changeFunc, const char* saveCat = nil, const char* saveKey = nil, bool disableIfGameLoaded = false); void FrontendOptionAddDynamic(const char* gxtKey, uint16 x, uint16 y, uint8 align, DrawFunc rightTextDrawFunc, int8 *var, ButtonPressFunc buttonPressFunc, const char* saveCat = nil, const char* saveKey = nil); // lineHeight = 0 means game will use MENU_DEFAULT_LINE_HEIGHT uint8 FrontendScreenAdd(const char* gxtKey, int prevPage, int lineHeight, bool showLeftRightHelper, ReturnPrevPageFunc returnPrevPageFunc = nil); #endif ================================================ FILE: src/extras/ini_parser.hpp ================================================ /* * Copyright (c) 2013-2015 Denilson das Mercês Amorim * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. * */ #ifndef LINB_INI_PARSER_HPP #define LINB_INI_PARSER_HPP /* * STL-like INI Container */ #include // for std::string #include // for std::map #include // for std::FILE #include // for std::find_if #include // for std::function namespace linb { template< class CharT = char, /* Not compatible with other type here, since we're using C streams */ class StringType = std::basic_string, class KeyContainer = std::map, class SectionContainer = std::map > class basic_ini { public: typedef CharT char_type; typedef StringType string_type; typedef KeyContainer key_container; typedef SectionContainer section_container; // Typedef container values types typedef typename section_container::value_type value_type; typedef typename section_container::key_type key_type; typedef typename section_container::mapped_type mapped_type; // Typedef common types typedef typename section_container::size_type size_type; typedef typename section_container::difference_type difference_type; // Typedef iterators typedef typename section_container::iterator iterator; typedef typename section_container::const_iterator const_iterator; typedef typename section_container::reverse_iterator reverse_iterator; typedef typename section_container::const_reverse_iterator const_reverse_iterator; // typedef References and pointers typedef typename section_container::reference reference; typedef typename section_container::const_reference const_reference; typedef typename section_container::pointer pointer; typedef typename section_container::const_pointer const_pointer; private: section_container data; public: basic_ini() { } basic_ini(const char_type* filename) { this->read_file(filename); } /* Iterator methods */ iterator begin() { return data.begin(); } const_iterator begin() const { return data.begin(); } iterator end() { return data.end(); } const_iterator end() const { return data.end(); } const_iterator cbegin() const { return data.cbegin(); } const_iterator cend() const { return data.cend(); } /* Reverse iterator methods */ reverse_iterator rbegin() { return data.rbegin(); } const_reverse_iterator rbegin() const { return data.rbegin(); } reverse_iterator rend() { return data.rend(); } const_reverse_iterator rend() const { return data.rend(); } const_reverse_iterator crbegin() const { return data.crbegin(); } const_reverse_iterator crend() const { return data.crend(); } /* Acessing index methods */ mapped_type& operator[](const string_type& sect) { return data[sect]; } mapped_type& operator[](string_type&& sect) { return data[std::forward(sect)]; } mapped_type& at( const string_type& sect) { return data.at(sect); } const mapped_type& at(const string_type& sect) const { return data.at(sect); } /* Capacity information */ bool empty() const { return data.empty(); } size_type size() const { return data.size(); } size_type max_size() const { return data.max_size(); } /* Modifiers */ void clear() { return data.clear(); } /* Lookup */ size_type count(const string_type& sect) { return data.count(sect); } iterator find(const string_type& sect) { return data.find(sect); } /* Gets a value from the specified section & key, default_value is returned if the sect & key doesn't exist */ string_type get(const string_type& sect, const key_type& key, const string_type& default_value) { auto it = this->find(sect); if(it != this->end()) { auto itv = it->second.find(key); if(itv != it->second.end()) return itv->second; } return default_value; } /* Sets the value of a value in the ini */ void set(const string_type& sect, const key_type& key, const string_type& value) { (*this)[sect][key] = value; // no emplace since overwrite! } /* Too lazy to continue this container... If you need more methods, just add it */ // re3 void remove(const string_type& sect, const key_type& key) { auto it = this->find(sect); if(it != this->end()) { it->second.erase(key); } } int category_size(const string_type& sect) { auto it = this->find(sect); if(it != this->end()) { return it->second.size(); } return 0; } #if 1 bool read_file(const char_type* filename) { /* Using C stream in a STL-like container, funny? */ if(FILE* f = fopen(filename, "r")) { key_container* keys = nullptr; char_type buf[2048]; string_type line; string_type key; string_type value; string_type null_string; size_type pos; // Trims an string auto trim = [](string_type& s, bool trimLeft, bool trimRight) -> string_type& { if(s.size()) { // Ignore UTF-8 BOM while(s.size() >= 3 && s[0] == (char)(0xEF) && s[1] == (char)(0xBB) && s[2] == (char)(0xBF)) s.erase(s.begin(), s.begin() + 3); if(trimLeft) s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::function(::isspace)))); if(trimRight) s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::function(::isspace))).base(), s.end()); } return s; }; // Start parsing while(fgets(buf, sizeof(buf), f)) { // What a thing, reading into a char buffer and then putting in the string... line = buf; // Find comment and remove anything after it from the line if((pos = line.find_first_of(';')) != line.npos) line.erase(pos); // Trim the string, and if it gets empty, skip this line if(trim(line, true, true).empty()) continue; // Find section name if(line.front() == '[' && line.back() == ']') { pos = line.length() - 1; //line.find_first_of(']'); if(pos != line.npos) { trim(key.assign(line, 1, pos-1), true, true); keys = &data[std::move(key)]; // Create section } else keys = nullptr; } else { // Find key and value positions pos = line.find_first_of('='); if(pos == line.npos) { // There's only the key key = line; // No need for trim, line is already trimmed value.clear(); } else { // There's the key and the value trim(key.assign(line, 0, pos), false, true); // trim the right trim(value.assign(line, pos + 1, line.npos), true, false); // trim the left } // Put the key/value into the current keys object, or into the section "" if no section has been found #if __cplusplus >= 201103L || _MSC_VER >= 1800 (keys ? *keys : data[null_string]).emplace(std::move(key), std::move(value)); #else (keys ? *keys : data[null_string])[key] = value; key.clear(); value.clear(); #endif } } fclose(f); return true; } return false; } /* * Dumps the content of this container into an ini file */ bool write_file(const char_type* filename) { if(FILE* f = fopen(filename, "w")) { bool first = true; for(auto& sec : this->data) { fprintf(f, first? "[%s]\n" : "\n[%s]\n", sec.first.c_str()); first = false; for(auto& kv : sec.second) { if(kv.second.empty()) fprintf(f, "%s\n", kv.first.c_str()); else fprintf(f, "%s = %s\n", kv.first.c_str(), kv.second.c_str()); } } fclose(f); return true; } return false; } /* */ bool load_file(const char_type* filename) { return read_file(filename); } bool load_file(const StringType& filename) { return load_file(filename.c_str()); } bool write_file(const StringType& filename) { return write_file(filename.c_str()); } #endif }; /* Use default basic_ini * * Limitations: * * Not unicode aware * * Case sensitive * * Sections must have unique keys */ typedef basic_ini<> ini; } #endif ================================================ FILE: src/extras/postfx.cpp ================================================ #define WITHD3D #include "common.h" #ifdef EXTENDED_COLOURFILTER #ifndef LIBRW #error "Need librw for EXTENDED_COLOURFILTER" #endif #include "main.h" #include "RwHelper.h" #include "Camera.h" #include "MBlur.h" #include "postfx.h" RwRaster *CPostFX::pFrontBuffer; RwRaster *CPostFX::pBackBuffer; bool CPostFX::bJustInitialised; int CPostFX::EffectSwitch = POSTFX_NORMAL; bool CPostFX::BlurOn = false; bool CPostFX::MotionBlurOn = false; static RwIm2DVertex Vertex[4]; static RwIm2DVertex Vertex2[4]; static RwImVertexIndex Index[6] = { 0, 1, 2, 0, 2, 3 }; #ifdef RW_D3D9 void *colourfilterVC_PS; void *contrast_PS; #endif #ifdef RW_OPENGL int32 u_blurcolor; int32 u_contrastAdd; int32 u_contrastMult; rw::gl3::Shader *colourFilterVC; rw::gl3::Shader *contrast; #endif void CPostFX::InitOnce(void) { #ifdef RW_OPENGL u_blurcolor = rw::gl3::registerUniform("u_blurcolor"); u_contrastAdd = rw::gl3::registerUniform("u_contrastAdd"); u_contrastMult = rw::gl3::registerUniform("u_contrastMult"); #endif } void CPostFX::Open(RwCamera *cam) { uint32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1); uint32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1); uint32 depth = RwRasterGetDepth(RwCameraGetRaster(cam)); pFrontBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE); pBackBuffer = RwRasterCreate(width, height, depth, rwRASTERTYPECAMERATEXTURE); bJustInitialised = true; float zero, xmax, ymax; if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){ zero = HALFPX; xmax = width + HALFPX; ymax = height + HALFPX; }else{ zero = -HALFPX; xmax = width - HALFPX; ymax = height - HALFPX; } RwIm2DVertexSetScreenX(&Vertex[0], zero); RwIm2DVertexSetScreenY(&Vertex[0], zero); RwIm2DVertexSetScreenZ(&Vertex[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex[0], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex[0], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex[1], zero); RwIm2DVertexSetScreenY(&Vertex[1], ymax); RwIm2DVertexSetScreenZ(&Vertex[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex[1], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex[1], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex[2], xmax); RwIm2DVertexSetScreenY(&Vertex[2], ymax); RwIm2DVertexSetScreenZ(&Vertex[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex[2], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex[2], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex[3], xmax); RwIm2DVertexSetScreenY(&Vertex[3], zero); RwIm2DVertexSetScreenZ(&Vertex[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex[3], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex[3], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex2[0], zero + 2.0f); RwIm2DVertexSetScreenY(&Vertex2[0], zero + 2.0f); RwIm2DVertexSetScreenZ(&Vertex2[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex2[0], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex2[0], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex2[0], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex2[1], 2.0f); RwIm2DVertexSetScreenY(&Vertex2[1], ymax + 2.0f); RwIm2DVertexSetScreenZ(&Vertex2[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex2[1], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex2[1], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex2[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex2[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex2[1], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex2[2], xmax + 2.0f); RwIm2DVertexSetScreenY(&Vertex2[2], ymax + 2.0f); RwIm2DVertexSetScreenZ(&Vertex2[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex2[2], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex2[2], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex2[2], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex2[3], xmax + 2.0f); RwIm2DVertexSetScreenY(&Vertex2[3], zero + 2.0f); RwIm2DVertexSetScreenZ(&Vertex2[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex2[3], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex2[3], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex2[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex2[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex2[3], 255, 255, 255, 255); #ifdef RW_D3D9 #include "shaders/obj/colourfilterVC_PS.inc" colourfilterVC_PS = rw::d3d::createPixelShader(colourfilterVC_PS_cso); #include "shaders/obj/contrastPS.inc" contrast_PS = rw::d3d::createPixelShader(contrastPS_cso); #endif #ifdef RW_OPENGL using namespace rw::gl3; { #include "shaders/obj/im2d_vert.inc" #include "shaders/obj/colourfilterVC_frag.inc" const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, colourfilterVC_frag_src, nil }; colourFilterVC = Shader::create(vs, fs); assert(colourFilterVC); } { #include "shaders/obj/im2d_vert.inc" #include "shaders/obj/contrast_frag.inc" const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, contrast_frag_src, nil }; contrast = Shader::create(vs, fs); assert(contrast); } #endif } void CPostFX::Close(void) { if(pFrontBuffer){ RwRasterDestroy(pFrontBuffer); pFrontBuffer = nil; } if(pBackBuffer){ RwRasterDestroy(pBackBuffer); pBackBuffer = nil; } #ifdef RW_D3D9 if(colourfilterVC_PS){ rw::d3d::destroyPixelShader(colourfilterVC_PS); colourfilterVC_PS = nil; } if(contrast_PS){ rw::d3d::destroyPixelShader(contrast_PS); contrast_PS = nil; } #endif #ifdef RW_OPENGL if(colourFilterVC){ colourFilterVC->destroy(); colourFilterVC = nil; } if(contrast){ contrast->destroy(); contrast = nil; } #endif } void CPostFX::RenderOverlayBlur(RwCamera *cam, int32 r, int32 g, int32 b, int32 a) { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwIm2DVertexSetIntRGBA(&Vertex[0], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex[1], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex[2], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex[3], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex2[0], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex2[1], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex2[2], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex2[3], r*2, g*2, b*2, 30); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, BlurOn ? Vertex2 : Vertex, 4, Index, 6); RwIm2DVertexSetIntRGBA(&Vertex2[0], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex2[1], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex2[2], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex2[3], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, BlurOn ? Vertex2 : Vertex, 4, Index, 6); } void CPostFX::RenderOverlaySniper(RwCamera *cam, int32 r, int32 g, int32 b, int32 a) { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, 80); RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, 80); RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, 80); RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, 80); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); } float CPostFX::Intensity = 1.0f; void CPostFX::RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a) { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pBackBuffer); if(EffectSwitch == POSTFX_MOBILE){ float mult[3], add[3]; mult[0] = (r-64)/256.0f + 1.4f; mult[1] = (g-64)/256.0f + 1.4f; mult[2] = (b-64)/256.0f + 1.4f; add[0] = r/1536.f - 0.05f; add[1] = g/1536.f - 0.05f; add[2] = b/1536.f - 0.05f; #ifdef RW_D3D9 rw::d3d::d3ddevice->SetPixelShaderConstantF(10, mult, 1); rw::d3d::d3ddevice->SetPixelShaderConstantF(11, add, 1); rw::d3d::im2dOverridePS = contrast_PS; #endif #ifdef RW_OPENGL rw::gl3::im2dOverrideShader = contrast; contrast->use(); glUniform3fv(contrast->uniformLocations[u_contrastMult], 1, mult); glUniform3fv(contrast->uniformLocations[u_contrastAdd], 1, add); #endif }else{ float f = Intensity; float blurcolors[4]; blurcolors[0] = r*f/255.0f; blurcolors[1] = g*f/255.0f; blurcolors[2] = b*f/255.0f; blurcolors[3] = 30/255.0f; #ifdef RW_D3D9 rw::d3d::d3ddevice->SetPixelShaderConstantF(10, blurcolors, 1); rw::d3d::im2dOverridePS = colourfilterVC_PS; #endif #ifdef RW_OPENGL rw::gl3::im2dOverrideShader = colourFilterVC; colourFilterVC->use(); glUniform4fv(colourFilterVC->uniformLocations[u_blurcolor], 1, blurcolors); #endif } RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); #ifdef RW_D3D9 rw::d3d::im2dOverridePS = nil; #endif #ifdef RW_OPENGL rw::gl3::im2dOverrideShader = nil; #endif } void CPostFX::RenderMotionBlur(RwCamera *cam, uint32 blur) { if(blur == 0) return; RwRenderStateSet(rwRENDERSTATETEXTURERASTER, pFrontBuffer); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, blur); RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, blur); RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, blur); RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, blur); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); } bool CPostFX::NeedBackBuffer(void) { // Current frame -- needed for non-blur effect switch(EffectSwitch){ case POSTFX_OFF: case POSTFX_SIMPLE: // no actual rendering here return false; case POSTFX_NORMAL: if(MotionBlurOn) return false; else return true; case POSTFX_MOBILE: return true; } return false; } bool CPostFX::NeedFrontBuffer(int32 type) { // Last frame -- needed for motion blur if(CMBlur::Drunkness > 0.0f) return true; if(type == MOTION_BLUR_SNIPER) return true; switch(EffectSwitch){ case POSTFX_OFF: case POSTFX_SIMPLE: // no actual rendering here return false; case POSTFX_NORMAL: if(MotionBlurOn) return true; else return false; case POSTFX_MOBILE: return false; } return false; } void CPostFX::GetBackBuffer(RwCamera *cam) { RwRasterPushContext(pBackBuffer); RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0); RwRasterPopContext(); } void CPostFX::Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha) { PUSH_RENDERGROUP("CPostFX::Render"); if(pFrontBuffer == nil) Open(cam); assert(pFrontBuffer); assert(pBackBuffer); if(type == MOTION_BLUR_LIGHT_SCENE){ SmoothColor(red, green, blue, blur); red = AvgRed; green = AvgGreen; blue = AvgBlue; blur = AvgAlpha; } if(NeedBackBuffer()) GetBackBuffer(cam); DefinedState(); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); if(type == MOTION_BLUR_SNIPER){ if(!bJustInitialised) RenderOverlaySniper(cam, red, green, blue, blur); }else switch(EffectSwitch){ case POSTFX_OFF: case POSTFX_SIMPLE: // no actual rendering here break; case POSTFX_NORMAL: if(MotionBlurOn){ if(!bJustInitialised) RenderOverlayBlur(cam, red, green, blue, blur); }else{ RenderOverlayShader(cam, red, green, blue, blur); } break; case POSTFX_MOBILE: RenderOverlayShader(cam, red, green, blue, blur); break; } if(!bJustInitialised) RenderMotionBlur(cam, 175.0f * CMBlur::Drunkness); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); if(NeedFrontBuffer(type)){ RwRasterPushContext(pFrontBuffer); RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0); RwRasterPopContext(); bJustInitialised = false; }else bJustInitialised = true; POP_RENDERGROUP(); } int CPostFX::PrevRed[NUMAVERAGE], CPostFX::AvgRed; int CPostFX::PrevGreen[NUMAVERAGE], CPostFX::AvgGreen; int CPostFX::PrevBlue[NUMAVERAGE], CPostFX::AvgBlue; int CPostFX::PrevAlpha[NUMAVERAGE], CPostFX::AvgAlpha; int CPostFX::Next; int CPostFX::NumValues; // This is rather annoying...the blur color can flicker slightly // which becomes very visible when amplified by the shader void CPostFX::SmoothColor(uint32 red, uint32 green, uint32 blue, uint32 alpha) { PrevRed[Next] = red; PrevGreen[Next] = green; PrevBlue[Next] = blue; PrevAlpha[Next] = alpha; Next = (Next+1) % NUMAVERAGE; NumValues = Min(NumValues+1, NUMAVERAGE); AvgRed = 0; AvgGreen = 0; AvgBlue = 0; AvgAlpha = 0; for(int i = 0; i < NumValues; i++){ AvgRed += PrevRed[i]; AvgGreen += PrevGreen[i]; AvgBlue += PrevBlue[i]; AvgAlpha += PrevAlpha[i]; } AvgRed /= NumValues; AvgGreen /= NumValues; AvgBlue /= NumValues; AvgAlpha /= NumValues; } #endif ================================================ FILE: src/extras/postfx.h ================================================ #pragma once #ifdef EXTENDED_COLOURFILTER class CPostFX { public: enum { POSTFX_OFF, POSTFX_SIMPLE, POSTFX_NORMAL, POSTFX_MOBILE }; static RwRaster *pFrontBuffer; static RwRaster *pBackBuffer; static bool bJustInitialised; static int EffectSwitch; static bool BlurOn; // or use CMblur for that? static bool MotionBlurOn; // or use CMblur for that? static float Intensity; // smooth blur color enum { NUMAVERAGE = 20 }; static int PrevRed[NUMAVERAGE], AvgRed; static int PrevGreen[NUMAVERAGE], AvgGreen; static int PrevBlue[NUMAVERAGE], AvgBlue; static int PrevAlpha[NUMAVERAGE], AvgAlpha; static int Next; static int NumValues; static void InitOnce(void); static void Open(RwCamera *cam); static void Close(void); static void RenderOverlayBlur(RwCamera *cam, int32 r, int32 g, int32 b, int32 a); static void RenderOverlaySniper(RwCamera *cam, int32 r, int32 g, int32 b, int32 a); static void RenderOverlayShader(RwCamera *cam, int32 r, int32 g, int32 b, int32 a); static void RenderMotionBlur(RwCamera *cam, uint32 blur); static void Render(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha); static void SmoothColor(uint32 red, uint32 green, uint32 blue, uint32 alpha); static bool NeedBackBuffer(void); static bool NeedFrontBuffer(int32 type); static void GetBackBuffer(RwCamera *cam); static bool UseBlurColours(void) { return EffectSwitch != POSTFX_SIMPLE; } }; #endif ================================================ FILE: src/extras/re3_inttypes.h ================================================ #define PRId8 "hhd" #define PRId16 "hd" #define PRId32 "ld" #define PRId64 "lld" #define PRIdFAST8 "hhd" #define PRIdFAST16 "hd" #define PRIdFAST32 "ld" #define PRIdFAST64 "lld" #define PRIdLEAST8 "hhd" #define PRIdLEAST16 "hd" #define PRIdLEAST32 "ld" #define PRIdLEAST64 "lld" #define PRIdMAX "lld" #define PRIdPTR "lld" #define PRIi8 "hhi" #define PRIi16 "hi" #define PRIi32 "li" #define PRIi64 "lli" #define PRIiFAST8 "hhi" #define PRIiFAST16 "hi" #define PRIiFAST32 "li" #define PRIiFAST64 "lli" #define PRIiLEAST8 "hhi" #define PRIiLEAST16 "hi" #define PRIiLEAST32 "li" #define PRIiLEAST64 "lli" #define PRIiMAX "lli" #define PRIiPTR "lli" #define PRIo8 "hho" #define PRIo16 "ho" #define PRIo32 "lo" #define PRIo64 "llo" #define PRIoFAST8 "hho" #define PRIoFAST16 "ho" #define PRIoFAST32 "lo" #define PRIoFAST64 "llo" #define PRIoLEAST8 "hho" #define PRIoLEAST16 "ho" #define PRIoLEAST32 "lo" #define PRIoLEAST64 "llo" #define PRIoMAX "llo" #define PRIoPTR "llo" #define PRIu8 "hhu" #define PRIu16 "hu" #define PRIu32 "lu" #define PRIu64 "llu" #define PRIuFAST8 "hhu" #define PRIuFAST16 "hu" #define PRIuFAST32 "lu" #define PRIuFAST64 "llu" #define PRIuLEAST8 "hhu" #define PRIuLEAST16 "hu" #define PRIuLEAST32 "lu" #define PRIuLEAST64 "llu" #define PRIuMAX "llu" #define PRIuPTR "llu" #define PRIx8 "hhx" #define PRIx16 "hx" #define PRIx32 "lx" #define PRIx64 "llx" #define PRIxFAST8 "hhx" #define PRIxFAST16 "hx" #define PRIxFAST32 "lx" #define PRIxFAST64 "llx" #define PRIxLEAST8 "hhx" #define PRIxLEAST16 "hx" #define PRIxLEAST32 "lx" #define PRIxLEAST64 "llx" #define PRIxMAX "llx" #define PRIxPTR "llx" #define PRIX8 "hhX" #define PRIX16 "hX" #define PRIX32 "lX" #define PRIX64 "llX" #define PRIXFAST8 "hhX" #define PRIXFAST16 "hX" #define PRIXFAST32 "lX" #define PRIXFAST64 "llX" #define PRIXLEAST8 "hhX" #define PRIXLEAST16 "hX" #define PRIXLEAST32 "lX" #define PRIXLEAST64 "llX" #define PRIXMAX "llX" #define PRIXPTR "llX" /* SCAN FORMAT MACROS */ #define SCNd8 "hhd" #define SCNd16 "hd" #define SCNd32 "ld" #define SCNd64 "lld" #define SCNdFAST8 "hhd" #define SCNdFAST16 "hd" #define SCNdFAST32 "ld" #define SCNdFAST64 "lld" #define SCNdLEAST8 "hhd" #define SCNdLEAST16 "hd" #define SCNdLEAST32 "ld" #define SCNdLEAST64 "lld" #define SCNdMAX "lld" #define SCNdPTR "lld" #define SCNi8 "hhi" #define SCNi16 "hi" #define SCNi32 "li" #define SCNi64 "lli" #define SCNiFAST8 "hhi" #define SCNiFAST16 "hi" #define SCNiFAST32 "li" #define SCNiFAST64 "lli" #define SCNiLEAST8 "hhi" #define SCNiLEAST16 "hi" #define SCNiLEAST32 "li" #define SCNiLEAST64 "lli" #define SCNiMAX "lli" #define SCNiPTR "lli" #define SCNo8 "hho" #define SCNo16 "ho" #define SCNo32 "lo" #define SCNo64 "llo" #define SCNoFAST8 "hho" #define SCNoFAST16 "ho" #define SCNoFAST32 "lo" #define SCNoFAST64 "llo" #define SCNoLEAST8 "hho" #define SCNoLEAST16 "ho" #define SCNoLEAST32 "lo" #define SCNoLEAST64 "llo" #define SCNoMAX "llo" #define SCNoPTR "llo" #define SCNu8 "hhu" #define SCNu16 "hu" #define SCNu32 "lu" #define SCNu64 "llu" #define SCNuFAST8 "hhu" #define SCNuFAST16 "hu" #define SCNuFAST32 "lu" #define SCNuFAST64 "llu" #define SCNuLEAST8 "hhu" #define SCNuLEAST16 "hu" #define SCNuLEAST32 "lu" #define SCNuLEAST64 "llu" #define SCNuMAX "llu" #define SCNuPTR "llu" #define SCNx8 "hhx" #define SCNx16 "hx" #define SCNx32 "lx" #define SCNx64 "llx" #define SCNxFAST8 "hhx" #define SCNxFAST16 "hx" #define SCNxFAST32 "lx" #define SCNxFAST64 "llx" #define SCNxLEAST8 "hhx" #define SCNxLEAST16 "hx" #define SCNxLEAST32 "lx" #define SCNxLEAST64 "llx" #define SCNxMAX "llx" #define SCNxPTR "llx" #define SCNX8 "hhX" #define SCNX16 "hX" #define SCNX32 "lX" #define SCNX64 "llX" #define SCNXFAST8 "hhX" #define SCNXFAST16 "hX" #define SCNXFAST32 "lX" #define SCNXFAST64 "llX" #define SCNXLEAST8 "hhX" #define SCNXLEAST16 "hX" #define SCNXLEAST32 "lX" #define SCNXLEAST64 "llX" #define SCNXMAX "llX" #define SCNXPTR "llX" ================================================ FILE: src/extras/screendroplets.cpp ================================================ #define WITHD3D #include "common.h" #ifdef SCREEN_DROPLETS #ifndef LIBRW #error "Need librw for SCREEN_DROPLETS" #endif #include "General.h" #include "main.h" #include "RwHelper.h" #include "Timer.h" #include "Camera.h" #include "World.h" #include "ZoneCull.h" #include "Weather.h" #include "ParticleObject.h" #include "Pad.h" #include "RenderBuffer.h" #include "custompipes.h" #include "postfx.h" #include "screendroplets.h" // for 640 #define MAXSIZE 15 #define MINSIZE 4 int ScreenDroplets::ms_initialised; RwTexture *ScreenDroplets::ms_maskTex; RwTexture *ScreenDroplets::ms_screenTex; bool ScreenDroplets::ms_enabled = true; bool ScreenDroplets::ms_movingEnabled = true; ScreenDroplets::ScreenDrop ScreenDroplets::ms_drops[ScreenDroplets::MAXDROPS]; int ScreenDroplets::ms_numDrops; ScreenDroplets::ScreenDropMoving ScreenDroplets::ms_dropsMoving[ScreenDroplets::MAXDROPSMOVING]; int ScreenDroplets::ms_numDropsMoving; CVector ScreenDroplets::ms_prevCamUp; CVector ScreenDroplets::ms_prevCamPos; CVector ScreenDroplets::ms_camMoveDelta; float ScreenDroplets::ms_camMoveDist; CVector ScreenDroplets::ms_screenMoveDelta; float ScreenDroplets::ms_screenMoveDist; float ScreenDroplets::ms_camUpAngle; int ScreenDroplets::ms_splashDuration; CParticleObject *ScreenDroplets::ms_splashObject; struct Im2DVertexUV2 : rw::RWDEVICE::Im2DVertex { rw::float32 u2, v2; }; #ifdef RW_D3D9 static void *screenDroplet_PS; #endif #ifdef RW_GL3 static rw::gl3::Shader *screenDroplet; #endif // platform specific static void openim2d_uv2(void); static void closeim2d_uv2(void); static void RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices); static Im2DVertexUV2 VertexBuffer[TEMPBUFFERVERTSIZE]; void ScreenDroplets::Initialise(void) { Clear(); ms_splashDuration = -1; ms_splashObject = nil; } // Create white circle mask for rain drops static RwTexture* CreateDropMask(int32 size) { RwImage *img = RwImageCreate(size, size, 32); RwImageAllocatePixels(img); uint8 *pixels = RwImageGetPixels(img); int32 stride = RwImageGetStride(img); for(int y = 0; y < size; y++){ float yf = ((y + 0.5f)/size - 0.5f)*2.0f; for(int x = 0; x < size; x++){ float xf = ((x + 0.5f)/size - 0.5f)*2.0f; memset(&pixels[y*stride + x*4], xf*xf + yf*yf < 1.0f ? 0xFF : 0x00, 4); } } int32 width, height, depth, format; RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &width, &height, &depth, &format); RwRaster *ras = RwRasterCreate(width, height, depth, format); RwRasterSetFromImage(ras, img); RwImageDestroy(img); return RwTextureCreate(ras); } void ScreenDroplets::InitDraw(void) { ms_maskTex = CreateDropMask(64); ms_screenTex = RwTextureCreate(nil); RwTextureSetFilterMode(ms_screenTex, rwFILTERLINEAR); openim2d_uv2(); #ifdef RW_D3D9 #include "shaders/obj/screenDroplet_PS.inc" screenDroplet_PS = rw::d3d::createPixelShader(screenDroplet_PS_cso); #endif #ifdef RW_GL3 using namespace rw::gl3; { #include "shaders/obj/im2d_UV2_vert.inc" #include "shaders/obj/screenDroplet_frag.inc" const char *vs[] = { shaderDecl, header_vert_src, im2d_UV2_vert_src, nil }; const char *fs[] = { shaderDecl, header_frag_src, screenDroplet_frag_src, nil }; screenDroplet = Shader::create(vs, fs); assert(screenDroplet); } #endif ms_initialised = 1; } void ScreenDroplets::Shutdown(void) { if(ms_maskTex){ RwTextureDestroy(ms_maskTex); ms_maskTex = nil; } if(ms_screenTex){ RwTextureSetRaster(ms_screenTex, nil); RwTextureDestroy(ms_screenTex); ms_screenTex = nil; } #ifdef RW_D3D9 if(screenDroplet_PS){ rw::d3d::destroyPixelShader(screenDroplet_PS); screenDroplet_PS = nil; } #endif #ifdef RW_GL3 if(screenDroplet){ screenDroplet->destroy(); screenDroplet = nil; } #endif closeim2d_uv2(); } void ScreenDroplets::Process(void) { ProcessCameraMovement(); SprayDrops(); ProcessMoving(); Fade(); } static void FlushBuffer(void) { if(TempBufferIndicesStored){ RenderIndexedPrimitive_UV2(rwPRIMTYPETRILIST, VertexBuffer, TempBufferVerticesStored, TempBufferRenderIndexList, TempBufferIndicesStored); TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; } } static int StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, Im2DVertexUV2 **vertexStart) { if(TempBufferIndicesStored + numIndices >= TEMPBUFFERINDEXSIZE || TempBufferVerticesStored + numVertices >= TEMPBUFFERVERTSIZE) FlushBuffer(); *indexStart = &TempBufferRenderIndexList[TempBufferIndicesStored]; *vertexStart = &VertexBuffer[TempBufferVerticesStored]; int vertOffset = TempBufferVerticesStored; TempBufferIndicesStored += numIndices; TempBufferVerticesStored += numVertices; return vertOffset; } void ScreenDroplets::Render(void) { ScreenDrop *drop; DefinedState(); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(ms_maskTex)); RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwTextureSetRaster(ms_screenTex, CPostFX::pBackBuffer); #ifdef RW_D3D9 rw::d3d::im2dOverridePS = screenDroplet_PS; rw::d3d::setTexture(1, ms_screenTex); #endif #ifdef RW_GL3 rw::gl3::im2dOverrideShader = screenDroplet; rw::gl3::setTexture(1, ms_screenTex); #endif RenderBuffer::ClearRenderBuffer(); for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++) if(drop->active) AddToRenderList(drop); FlushBuffer(); #ifdef RW_D3D9 rw::d3d::im2dOverridePS = nil; rw::d3d::setTexture(1, nil); #endif #ifdef RW_GL3 rw::gl3::im2dOverrideShader = nil; rw::gl3::setTexture(1, nil); #endif RwRenderStateSet(rwRENDERSTATEFOGENABLE, FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, FALSE); } void ScreenDroplets::AddToRenderList(ScreenDroplets::ScreenDrop *drop) { static float xy[] = { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f }; static float uv[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f }; int i; RwImVertexIndex *indices; Im2DVertexUV2 *verts; int first = StartStoring(6, 4, &indices, &verts); float scale = 0.5f*SCREEN_SCALE_X(drop->size); float screenz = RwIm2DGetNearScreenZ(); float z = RwCameraGetNearClipPlane(Scene.camera); float recipz = 1.0f/z; float magSize = SCREEN_SCALE_Y(drop->magnification*(300.0f-40.0f) + 40.0f); float ul = drop->x - magSize; float vt = drop->y - magSize; float ur = drop->x + magSize; float vb = drop->y + magSize; ul = Max(ul, 0.0f)/RwRasterGetWidth(CPostFX::pBackBuffer); vt = Max(vt, 0.0f)/RwRasterGetHeight(CPostFX::pBackBuffer); ur = Min(ur, SCREEN_WIDTH)/RwRasterGetWidth(CPostFX::pBackBuffer); vb = Min(vb, SCREEN_HEIGHT)/RwRasterGetHeight(CPostFX::pBackBuffer); for(i = 0; i < 4; i++){ RwIm2DVertexSetScreenX(&verts[i], drop->x + xy[i*2]*scale); RwIm2DVertexSetScreenY(&verts[i], drop->y + xy[i*2+1]*scale); RwIm2DVertexSetScreenZ(&verts[i], screenz); RwIm2DVertexSetCameraZ(&verts[i], z); RwIm2DVertexSetRecipCameraZ(&verts[i], recipz); RwIm2DVertexSetIntRGBA(&verts[i], drop->color.r, drop->color.g, drop->color.b, drop->color.a); RwIm2DVertexSetU(&verts[i], uv[i*2], recipz); RwIm2DVertexSetV(&verts[i], uv[i*2+1], recipz); verts[i].u2 = i < 2 ? ul : ur; verts[i].v2 = i % 3 ? vt : vb; } indices[0] = first + 0; indices[1] = first + 1; indices[2] = first + 2; indices[3] = first + 2; indices[4] = first + 3; indices[5] = first + 0; } void ScreenDroplets::Clear(void) { ScreenDrop *drop; for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++) drop->active = false; ms_numDrops = 0; } ScreenDroplets::ScreenDrop* ScreenDroplets::NewDrop(float x, float y, float size, float lifetime, bool fades, int r, int g, int b) { ScreenDrop *drop; int i; for(i = 0, drop = ms_drops; i < MAXDROPS; i++, drop++) if(!ms_drops[i].active) goto found; return nil; found: ms_numDrops++; drop->x = x; drop->y = y; drop->size = size; drop->magnification = (MAXSIZE - size + 1.0f) / (MAXSIZE - MINSIZE + 1.0f); drop->fades = fades; drop->active = true; drop->color.r = r; drop->color.g = g; drop->color.b = b; drop->color.a = 255; drop->time = 0.0f; drop->lifetime = lifetime; return drop; } void ScreenDroplets::SetMoving(ScreenDroplets::ScreenDrop *drop) { ScreenDropMoving *moving; for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++) if(moving->drop == nil) goto found; return; found: ms_numDropsMoving++; moving->drop = drop; moving->dist = 0.0f; } void ScreenDroplets::FillScreen(int n) { float x, y, size; ScreenDrop *drop; if(!ms_initialised) return; ms_numDrops = 0; for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++){ drop->active = false; if(drop < &ms_drops[n]){ x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH; y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT; size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE); NewDrop(x, y, size, 2000.0f, true); } } } void ScreenDroplets::FillScreenMoving(float amount, bool isBlood) { int n = (ms_screenMoveDelta.z > 5.0f ? 1.5f : 1.0f)*amount*20.0f; float x, y, size; ScreenDrop *drop; while(n--) if(ms_numDrops < MAXDROPS && ms_numDropsMoving < MAXDROPSMOVING){ x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH; y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT; size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE); drop = nil; if(isBlood) drop = NewDrop(x, y, size, 2000.0f, true, 255, 0, 0); else drop = NewDrop(x, y, size, 2000.0f, true); if(drop) SetMoving(drop); } } void ScreenDroplets::RegisterSplash(CParticleObject *pobj) { CVector dist = pobj->GetPosition() - ms_prevCamPos; if(dist.MagnitudeSqr() < 50.0f){ // 20 originally ms_splashDuration = 14; ms_splashObject = pobj; } } void ScreenDroplets::ProcessCameraMovement(void) { RwMatrix *camMat = RwFrameGetMatrix(RwCameraGetFrame(Scene.camera)); CVector camPos = camMat->pos; CVector camUp = camMat->at; ms_camMoveDelta = camPos - ms_prevCamPos; ms_camMoveDist = ms_camMoveDelta.Magnitude(); ms_prevCamUp = camUp; ms_prevCamPos = camPos; ms_screenMoveDelta.x = -RwV3dDotProduct(&camMat->right, &ms_camMoveDelta); ms_screenMoveDelta.y = RwV3dDotProduct(&camMat->up, &ms_camMoveDelta); ms_screenMoveDelta.z = RwV3dDotProduct(&camMat->at, &ms_camMoveDelta); ms_screenMoveDelta *= 10.0f; ms_screenMoveDist = ms_screenMoveDelta.Magnitude2D(); uint16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; bool isTopDown = mode == CCam::MODE_TOPDOWN || mode == CCam::MODE_GTACLASSIC || mode == CCam::MODE_TOP_DOWN_PED; bool isLookingInDirection = FindPlayerVehicle() && mode == CCam::MODE_1STPERSON && (CPad::GetPad(0)->GetLookBehindForCar() || CPad::GetPad(0)->GetLookLeft() || CPad::GetPad(0)->GetLookRight()); ms_enabled = !isTopDown && !isLookingInDirection; ms_movingEnabled = !isTopDown && !isLookingInDirection; // 0 when looking stright up, 180 when looking up or down ms_camUpAngle = RADTODEG(Acos(clamp(camUp.z, -1.0f, 1.0f))); } void ScreenDroplets::SprayDrops(void) { bool noRain = CCullZones::PlayerNoRain() || CCullZones::CamNoRain(); if(!noRain && CWeather::Rain > 0.0f && ms_enabled){ // 180 when looking stright up, 0 when looking up or down float angle = 180.0f - ms_camUpAngle; angle = Max(angle, 40.0f); // want at least some rain FillScreenMoving((angle - 40.0f) / 150.0f * CWeather::Rain * 0.5f); } int i; for(i = 0; i < MAX_AUDIOHYDRANTS; i++){ CAudioHydrant *hyd = CAudioHydrant::Get(i); if (hyd->pParticleObject){ CVector dist = hyd->pParticleObject->GetPosition() - ms_prevCamPos; if(dist.MagnitudeSqr() > 40.0f || DotProduct(dist, ms_prevCamUp) < 0.0f) continue; FillScreenMoving(1.0f); } } static int ndrops[] = { 125, 250, 500, 1000, 1000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if(ms_splashDuration >= 0){ if(ms_numDrops < MAXDROPS) { float numDropMult = 1.0f; if(ms_splashObject){ float dist = (ms_splashObject->GetPosition() - ms_prevCamPos).Magnitude(); numDropMult = 1.0f - (dist - 5.0f)/15.0f; if(numDropMult < 0) numDropMult = 0.0f; // fix } int n = ndrops[ms_splashDuration] * numDropMult; while(n--) if(ms_numDrops < MAXDROPS){ float x = CGeneral::GetRandomNumber() % (int)SCREEN_WIDTH; float y = CGeneral::GetRandomNumber() % (int)SCREEN_HEIGHT; float size = CGeneral::GetRandomNumberInRange(MINSIZE, MAXSIZE); NewDrop(x, y, size, 10000.0f, false); } } ms_splashDuration--; } } void ScreenDroplets::NewTrace(ScreenDroplets::ScreenDropMoving *moving) { if(ms_numDrops < MAXDROPS){ moving->dist = 0.0f; NewDrop(moving->drop->x, moving->drop->y, MINSIZE, 500.0f, true, moving->drop->color.r, moving->drop->color.g, moving->drop->color.b); } } void ScreenDroplets::MoveDrop(ScreenDroplets::ScreenDropMoving *moving) { ScreenDrop *drop = moving->drop; if(!ms_movingEnabled) return; if(!drop->active){ moving->drop = nil; ms_numDropsMoving--; return; } if(ms_screenMoveDelta.z > 0.0f && ms_camMoveDist > 0.3f){ if(ms_screenMoveDist > 0.5f && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON){ // movement when camera turns moving->dist += ms_screenMoveDist; if(moving->dist > 20.0f && drop->color.a > 100) NewTrace(moving); drop->x -= ms_screenMoveDelta.x; drop->y += ms_screenMoveDelta.y; }else{ // movement out of center float d = ms_screenMoveDelta.z*0.2f; float dx, dy, sum; dx = drop->x - SCREEN_WIDTH*0.5f + ms_screenMoveDelta.x; if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON) dy = drop->y - SCREEN_HEIGHT*1.2f - ms_screenMoveDelta.y; else dy = drop->y - SCREEN_HEIGHT*0.5f - ms_screenMoveDelta.y; sum = fabs(dx) + fabs(dy); if(sum > 0.001f){ dx /= sum; dy /= sum; } moving->dist += d; if(moving->dist > 20.0f && drop->color.a > 100) NewTrace(moving); drop->x += dx * d; drop->y += dy * d; } if(drop->x < 0.0f || drop->y < 0.0f || drop->x > SCREEN_WIDTH || drop->y > SCREEN_HEIGHT){ moving->drop = nil; ms_numDropsMoving--; } } } void ScreenDroplets::ProcessMoving(void) { ScreenDropMoving *moving; if(!ms_movingEnabled) return; for(moving = ms_dropsMoving; moving < &ms_dropsMoving[MAXDROPSMOVING]; moving++) if(moving->drop) MoveDrop(moving); } void ScreenDroplets::Fade(void) { ScreenDrop *drop; for(drop = &ms_drops[0]; drop < &ms_drops[MAXDROPS]; drop++) if(drop->active) drop->Fade(); } void ScreenDroplets::ScreenDrop::Fade(void) { int delta = CTimer::GetTimeStepInMilliseconds(); time += delta; if(time < lifetime){ color.a = 255 - time/lifetime*255; }else if(fades){ ScreenDroplets::ms_numDrops--; active = false; } } /* * Im2D with two uv coors */ #ifdef RW_D3D9 // stolen from RW, not in a public header namespace rw { namespace d3d { void addDynamicVB(uint32 length, uint32 fvf, IDirect3DVertexBuffer9 **buf); // NB: don't share this pointer void removeDynamicVB(IDirect3DVertexBuffer9 **buf); void addDynamicIB(uint32 length, IDirect3DIndexBuffer9 **buf); // NB: don't share this pointer void removeDynamicIB(IDirect3DIndexBuffer9 **buf); } } // different than im2d #define NUMINDICES 1024 #define NUMVERTICES 1024 static int primTypeMap[] = { D3DPT_POINTLIST, // invalid D3DPT_LINELIST, D3DPT_LINESTRIP, D3DPT_TRIANGLELIST, D3DPT_TRIANGLESTRIP, D3DPT_TRIANGLEFAN, D3DPT_POINTLIST, // actually not supported! }; // end of stolen stuff static IDirect3DVertexDeclaration9 *im2ddecl_uv2; static IDirect3DVertexBuffer9 *im2dvertbuf_uv2; static IDirect3DIndexBuffer9 *im2dindbuf_uv2; void openim2d_uv2(void) { using namespace rw; using namespace d3d; D3DVERTEXELEMENT9 elements[5] = { { 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 }, { 0, offsetof(Im2DVertexUV2, color), D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }, { 0, offsetof(Im2DVertexUV2, u), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, { 0, offsetof(Im2DVertexUV2, u2), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 }, D3DDECL_END() }; assert(im2ddecl_uv2 == nil); im2ddecl_uv2 = (IDirect3DVertexDeclaration9*)d3d9::createVertexDeclaration((d3d9::VertexElement*)elements); assert(im2ddecl_uv2); assert(im2dvertbuf_uv2 == nil); im2dvertbuf_uv2 = (IDirect3DVertexBuffer9*)createVertexBuffer(NUMVERTICES*sizeof(Im2DVertexUV2), 0, true); assert(im2dvertbuf_uv2); addDynamicVB(NUMVERTICES*sizeof(Im2DVertexUV2), 0, &im2dvertbuf_uv2); assert(im2dindbuf_uv2 == nil); im2dindbuf_uv2 = (IDirect3DIndexBuffer9*)createIndexBuffer(NUMINDICES*sizeof(rw::uint16), true); assert(im2dindbuf_uv2); addDynamicIB(NUMINDICES*sizeof(rw::uint16), &im2dindbuf_uv2); } void closeim2d_uv2(void) { using namespace rw; using namespace d3d; d3d9::destroyVertexDeclaration(im2ddecl_uv2); im2ddecl_uv2 = nil; removeDynamicVB(&im2dvertbuf_uv2); destroyVertexBuffer(im2dvertbuf_uv2); im2dvertbuf_uv2 = nil; removeDynamicIB(&im2dindbuf_uv2); destroyIndexBuffer(im2dindbuf_uv2); im2dindbuf_uv2 = nil; } void RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices) { using namespace rw; using namespace d3d; if(numVertices > NUMVERTICES || numIndices > NUMINDICES){ // TODO: error return; } rw::uint16 *lockedindices = lockIndices(im2dindbuf_uv2, 0, numIndices*sizeof(rw::uint16), D3DLOCK_DISCARD); memcpy(lockedindices, indices, numIndices*sizeof(rw::uint16)); unlockIndices(im2dindbuf_uv2); rw::uint8 *lockedvertices = lockVertices(im2dvertbuf_uv2, 0, numVertices*sizeof(Im2DVertexUV2), D3DLOCK_DISCARD); memcpy(lockedvertices, vertices, numVertices*sizeof(Im2DVertexUV2)); unlockVertices(im2dvertbuf_uv2); setStreamSource(0, im2dvertbuf_uv2, 0, sizeof(Im2DVertexUV2)); setIndices(im2dindbuf_uv2); setVertexDeclaration(im2ddecl_uv2); if(im2dOverridePS) setPixelShader(im2dOverridePS); else if(engine->device.getRenderState(TEXTURERASTER)) setPixelShader(im2d_tex_PS); else setPixelShader(im2d_PS); d3d::flushCache(); rw::uint32 primCount = 0; switch(primType){ case PRIMTYPELINELIST: primCount = numIndices/2; break; case PRIMTYPEPOLYLINE: primCount = numIndices-1; break; case PRIMTYPETRILIST: primCount = numIndices/3; break; case PRIMTYPETRISTRIP: primCount = numIndices-2; break; case PRIMTYPETRIFAN: primCount = numIndices-2; break; case PRIMTYPEPOINTLIST: primCount = numIndices; break; } d3ddevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)primTypeMap[primType], 0, 0, numVertices, 0, primCount); } #endif #ifdef RW_GL3 // different than im2d #define NUMINDICES 1024 #define NUMVERTICES 1024 static rw::gl3::AttribDesc im2d_UV2_attribDesc[4] = { { rw::gl3::ATTRIB_POS, GL_FLOAT, GL_FALSE, 4, sizeof(Im2DVertexUV2), 0 }, { rw::gl3::ATTRIB_COLOR, GL_UNSIGNED_BYTE, GL_TRUE, 4, sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, r) }, { rw::gl3::ATTRIB_TEXCOORDS0, GL_FLOAT, GL_FALSE, 2, sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u) }, { rw::gl3::ATTRIB_TEXCOORDS1, GL_FLOAT, GL_FALSE, 2, sizeof(Im2DVertexUV2), offsetof(Im2DVertexUV2, u2) } }; static int primTypeMap[] = { GL_POINTS, // invalid GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_POINTS }; static int32 u_xform; uint32 im2D_UV2_Vbo, im2D_UV2_Ibo; #ifdef RW_GL_USE_VAOS uint32 im2D_UV2_Vao; #endif void openim2d_uv2(void) { u_xform = rw::gl3::registerUniform("u_xform"); // this doesn't add a new one, so it's safe glGenBuffers(1, &im2D_UV2_Ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW); glGenBuffers(1, &im2D_UV2_Vbo); glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo); glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW); #ifdef RW_GL_USE_VAOS glGenVertexArrays(1, &im2D_UV2_Vao); glBindVertexArray(im2D_UV2_Vao); setAttribPointers(im2d_UV2_attribDesc, 4); #endif } void closeim2d_uv2(void) { glDeleteBuffers(1, &im2D_UV2_Ibo); glDeleteBuffers(1, &im2D_UV2_Vbo); #ifdef RW_GL_USE_VAOS glDeleteVertexArrays(1, &im2D_UV2_Vao); #endif } void RenderIndexedPrimitive_UV2(RwPrimitiveType primType, Im2DVertexUV2 *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices) { using namespace rw; using namespace gl3; GLfloat xform[4]; Camera *cam; cam = (Camera*)engine->currentCamera; #ifdef RW_GL_USE_VAOS glBindVertexArray(im2D_UV2_Vao); #endif glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2D_UV2_Ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUMINDICES*2, nil, GL_STREAM_DRAW); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numIndices*2, indices); glBindBuffer(GL_ARRAY_BUFFER, im2D_UV2_Vbo); glBufferData(GL_ARRAY_BUFFER, NUMVERTICES*sizeof(Im2DVertexUV2), nil, GL_STREAM_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices*sizeof(Im2DVertexUV2), vertices); xform[0] = 2.0f/cam->frameBuffer->width; xform[1] = -2.0f/cam->frameBuffer->height; xform[2] = -1.0f; xform[3] = 1.0f; if(im2dOverrideShader) im2dOverrideShader->use(); else assert(0);//im2dShader->use(); #ifndef RW_GL_USE_VAOS setAttribPointers(im2d_UV2_attribDesc, 4); #endif glUniform4fv(currentShader->uniformLocations[u_xform], 1, xform); flushCache(); glDrawElements(primTypeMap[primType], numIndices, GL_UNSIGNED_SHORT, nil); #ifndef RW_GL_USE_VAOS disableAttribPointers(im2d_UV2_attribDesc, 4); #endif } #endif #endif ================================================ FILE: src/extras/screendroplets.h ================================================ #pragma once #ifdef SCREEN_DROPLETS class CParticleObject; class ScreenDroplets { public: enum { MAXDROPS = 2000, MAXDROPSMOVING = 700 }; class ScreenDrop { public: float x, y, time; // shorts on xbox (short float?) float size, magnification, lifetime; // " CRGBA color; bool active; bool fades; void Fade(void); }; struct ScreenDropMoving { ScreenDrop *drop; float dist; }; static int ms_initialised; static RwTexture *ms_maskTex; static RwTexture *ms_screenTex; static bool ms_enabled; static bool ms_movingEnabled; static ScreenDrop ms_drops[MAXDROPS]; static int ms_numDrops; static ScreenDropMoving ms_dropsMoving[MAXDROPSMOVING]; static int ms_numDropsMoving; static CVector ms_prevCamUp; static CVector ms_prevCamPos; static CVector ms_camMoveDelta; static float ms_camMoveDist; static CVector ms_screenMoveDelta; static float ms_screenMoveDist; static float ms_camUpAngle; static int ms_splashDuration; static CParticleObject *ms_splashObject; static void Initialise(void); static void InitDraw(void); static void Shutdown(void); static void Process(void); static void Render(void); static void AddToRenderList(ScreenDrop *drop); static void Clear(void); static ScreenDrop *NewDrop(float x, float y, float size, float lifetime, bool fades, int r = 255, int g = 255, int b = 255); static void SetMoving(ScreenDroplets::ScreenDrop *drop); static void FillScreen(int n); static void FillScreenMoving(float amount, bool isBlood = false); static void RegisterSplash(CParticleObject *pobj); static void ProcessCameraMovement(void); static void SprayDrops(void); static void NewTrace(ScreenDroplets::ScreenDropMoving *moving); static void MoveDrop(ScreenDropMoving *moving); static void ProcessMoving(void); static void Fade(void); }; #endif ================================================ FILE: src/extras/shaders/colourfilterVC.frag ================================================ uniform sampler2D tex0; uniform vec4 u_blurcolor; FSIN vec4 v_color; FSIN vec2 v_tex0; FSIN float v_fog; void main(void) { float a = u_blurcolor.a; vec4 doublec = clamp(u_blurcolor*2.0, 0.0, 1.0); vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); vec4 prev = dst; for(int i = 0; i < 5; i++){ vec4 tmp = dst*(1.0-a) + prev*doublec*a; tmp += prev*u_blurcolor; tmp += prev*u_blurcolor; prev = clamp(tmp, 0.0, 1.0); } vec4 color; color.rgb = prev.rgb; color.a = 1.0; FRAGCOLOR(color); } ================================================ FILE: src/extras/shaders/colourfilterVC_PS.hlsl ================================================ sampler2D tex : register(s0); float4 blurcol : register(c10); //float4 blurcols[10] : register(c15); float4 main(in float2 texcoord : TEXCOORD0) : COLOR0 { float a = blurcol.a; float4 doublec = saturate(blurcol*2); float4 dst = tex2D(tex, texcoord.xy); float4 prev = dst; for(int i = 0; i < 5; i++){ // float4 doublec = saturate(blurcol*2); float4 tmp = dst*(1-a) + prev*doublec*a; tmp += prev*blurcol; tmp += prev*blurcol; prev = saturate(tmp); } prev.a = 1.0; return prev; } ================================================ FILE: src/extras/shaders/contrast.frag ================================================ uniform sampler2D tex0; uniform vec3 u_contrastAdd; uniform vec3 u_contrastMult; FSIN vec4 v_color; FSIN vec2 v_tex0; FSIN float v_fog; void main(void) { vec4 dst = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); vec4 color; color.rgb = dst.rgb*u_contrastMult + u_contrastAdd; color.a = 1.0; FRAGCOLOR(color); } ================================================ FILE: src/extras/shaders/contrastPS.hlsl ================================================ struct PS_INPUT { float4 position : POSITION; float3 texcoord0 : TEXCOORD0; float4 color : COLOR0; }; uniform float3 contrastMult : register(c10); uniform float3 contrastAdd : register(c11); sampler2D tex : register(s0); float4 main(PS_INPUT In) : COLOR { float4 dst = tex2D(tex, In.texcoord0.xy); dst.rgb = dst.rgb*contrastMult + contrastAdd; dst.a = 1.0; return dst; } ================================================ FILE: src/extras/shaders/default_UV2.vert ================================================ VSIN(ATTRIB_POS) vec3 in_pos; VSOUT vec4 v_color; VSOUT vec2 v_tex0; VSOUT vec2 v_tex1; VSOUT float v_fog; void main(void) { vec4 Vertex = u_world * vec4(in_pos, 1.0); gl_Position = u_proj * u_view * Vertex; vec3 Normal = mat3(u_world) * in_normal; v_tex0 = in_tex0; v_tex1 = in_tex1; v_color = in_color; v_color.rgb += u_ambLight.rgb*surfAmbient; v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse; v_color = clamp(v_color, 0.0, 1.0); v_color *= u_matColor; v_fog = DoFog(gl_Position.w); } ================================================ FILE: src/extras/shaders/default_UV2_VS.hlsl ================================================ #include "standardConstants.h" struct VS_in { float4 Position : POSITION; float3 Normal : NORMAL; float2 TexCoord : TEXCOORD0; float2 TexCoord1 : TEXCOORD1; float4 Prelight : COLOR0; }; struct VS_out { float4 Position : POSITION; float3 TexCoord0 : TEXCOORD0; // also fog float2 TexCoord1 : TEXCOORD1; float4 Color : COLOR0; }; VS_out main(in VS_in input) { VS_out output; output.Position = mul(combinedMat, input.Position); float3 Vertex = mul(worldMat, input.Position).xyz; float3 Normal = mul(normalMat, input.Normal); output.TexCoord0.xy = input.TexCoord; output.TexCoord1.xy = input.TexCoord1; output.Color = input.Prelight; output.Color.rgb += ambientLight.rgb * surfAmbient; int i; #ifdef DIRECTIONALS for(i = 0; i < numDirLights; i++) output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse; #endif #ifdef POINTLIGHTS for(i = 0; i < numPointLights; i++) output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse; #endif #ifdef SPOTLIGHTS for(i = 0; i < numSpotLights; i++) output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse; #endif // PS2 clamps before material color output.Color = clamp(output.Color, 0.0, 1.0); output.Color *= matCol; output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); return output; } ================================================ FILE: src/extras/shaders/im2d.vert ================================================ uniform vec4 u_xform; VSIN(ATTRIB_POS) vec4 in_pos; VSOUT vec4 v_color; VSOUT vec2 v_tex0; VSOUT float v_fog; void main(void) { gl_Position = in_pos; gl_Position.w = 1.0; gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw; v_fog = DoFog(gl_Position.z); gl_Position.xyz *= gl_Position.w; v_color = in_color; v_tex0 = in_tex0; } ================================================ FILE: src/extras/shaders/im2d_UV2.vert ================================================ uniform vec4 u_xform; VSIN(ATTRIB_POS) vec4 in_pos; VSOUT vec4 v_color; VSOUT vec2 v_tex0; VSOUT vec2 v_tex1; VSOUT float v_fog; void main(void) { gl_Position = in_pos; gl_Position.w = 1.0; gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw; v_fog = DoFog(gl_Position.z); gl_Position.xyz *= gl_Position.w; v_color = in_color; v_tex0 = in_tex0; v_tex1 = in_tex1; } ================================================ FILE: src/extras/shaders/lighting.h ================================================ struct Light { float4 color; // and radius float4 position; // and -cos(angle) float4 direction; // and falloff clamp }; float3 DoDirLight(Light L, float3 N) { float l = max(0.0, dot(N, -L.direction.xyz)); return l*L.color.xyz; } float3 DoDirLightSpec(Light L, float3 N, float3 V, float power) { return pow(saturate(dot(N, normalize(V + -L.direction.xyz))), power)*L.color.xyz; } float3 DoPointLight(Light L, float3 V, float3 N) { // As on PS2 float3 dir = V - L.position.xyz; float dist = length(dir); float atten = max(0.0, (1.0 - dist/L.color.w)); float l = max(0.0, dot(N, -normalize(dir))); return l*L.color.xyz*atten; } float3 DoSpotLight(Light L, float3 V, float3 N) { // As on PS2 float3 dir = V - L.position.xyz; float dist = length(dir); float atten = max(0.0, (1.0 - dist/L.color.w)); dir /= dist; float l = max(0.0, dot(N, -dir)); float pcos = dot(dir, L.direction.xyz); // cos to point float ccos = -L.position.w; // cos of cone float falloff = (pcos-ccos)/(1.0-ccos); if(falloff < 0) // outside of cone l = 0; l *= max(falloff, L.direction.w); // falloff clamp return l*L.color.xyz*atten; } ================================================ FILE: src/extras/shaders/make_glsl.sh ================================================ #!sh for i in *.vert; do echo $i ./makeinc_glsl.sh $i done for i in *.frag; do echo $i ./makeinc_glsl.sh $i done ================================================ FILE: src/extras/shaders/make_hlsl.cmd ================================================ @echo off for %%f in (*PS.hlsl) do "%DXSDK_DIR%\Utilities\bin\x86\fxc.exe" /T ps_2_0 /nologo /E main /Fo obj\%%~nf.cso %%f for %%f in (*VS.hlsl) do "%DXSDK_DIR%\Utilities\bin\x86\fxc.exe" /T vs_2_0 /nologo /E main /Fo obj\%%~nf.cso %%f ================================================ FILE: src/extras/shaders/makeinc_glsl.sh ================================================ #!sh ext=${1##*.} name=${1%.*} (echo "const char *${name}_${ext}_src =";\ sed 's/..*/"&\\n"/' $1;\ echo ';') > obj/${name}_${ext}.inc ================================================ FILE: src/extras/shaders/makeinc_hlsl.sh ================================================ #!sh cd obj for i in *cso; do (echo -n 'static ' xxd -i $i | grep -v '_len = ') > ${i%cso}inc done ================================================ FILE: src/extras/shaders/neoGloss.frag ================================================ uniform sampler2D tex0; uniform vec4 u_reflProps; #define glossMult (u_reflProps.x) FSIN vec3 v_normal; FSIN vec3 v_light; FSIN vec2 v_tex0; FSIN float v_fog; void main(void) { vec4 color = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); vec3 n = 2.0*v_normal-1.0; // unpack vec3 v = 2.0*v_light-1.0; // float s = dot(n, v); color = s*s*s*s*s*s*s*s*color*v_fog*glossMult; DoAlphaTest(color.a); FRAGCOLOR(color); } ================================================ FILE: src/extras/shaders/neoGloss.vert ================================================ uniform vec3 u_eye; VSIN(ATTRIB_POS) vec3 in_pos; VSOUT vec3 v_normal; VSOUT vec3 v_light; VSOUT vec2 v_tex0; VSOUT float v_fog; void main(void) { vec4 Vertex = u_world * vec4(in_pos, 1.0); gl_Position = u_proj * u_view * Vertex; vec3 Normal = mat3(u_world) * in_normal; v_tex0 = in_tex0; vec3 viewVec = normalize(u_eye - Vertex.xyz); vec3 Light = normalize(viewVec - u_lightDirection[0].xyz); v_normal = 0.5*(1.0 + vec3(0.0, 0.0, 1.0)); // compress v_light = 0.5*(1.0 + Light); // v_fog = DoFog(gl_Position.w); } ================================================ FILE: src/extras/shaders/neoGloss_PS.hlsl ================================================ sampler2D tex0 : register(s0); float glossMult : register(c1); struct VS_out { float4 Position : POSITION; float3 TexCoord0 : TEXCOORD0; float3 Normal : COLOR0; float3 Light : COLOR1; }; float4 main(VS_out input) : COLOR { float4 color = tex2D(tex0, input.TexCoord0.xy); float3 n = 2.0*input.Normal-1.0; // unpack float3 v = 2.0*input.Light-1.0; // float s = dot(n, v); return s*s*s*s*s*s*s*s*color*input.TexCoord0.z*glossMult; } ================================================ FILE: src/extras/shaders/neoGloss_VS.hlsl ================================================ #include "standardConstants.h" struct VS_in { float4 Position : POSITION; float2 TexCoord : TEXCOORD0; }; struct VS_out { float4 Position : POSITION; float3 TexCoord0 : TEXCOORD0; float3 Normal : COLOR0; float3 Light : COLOR1; }; float3 eye : register(c41); VS_out main(in VS_in input) { VS_out output; output.Position = mul(combinedMat, input.Position); float3 Vertex = mul(worldMat, input.Position).xyz; output.TexCoord0.xy = input.TexCoord; float3 viewVec = normalize(eye - Vertex); float3 Light = normalize(viewVec - lights[0].direction.xyz); output.Normal = 0.5*(1.0 + float3(0.0, 0.0, 1.0)); // compress output.Light = 0.5*(1.0 + Light); // output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); return output; } ================================================ FILE: src/extras/shaders/neoRim.vert ================================================ uniform vec3 u_viewVec; uniform vec4 u_rampStart; uniform vec4 u_rampEnd; uniform vec3 u_rimData; VSIN(ATTRIB_POS) vec3 in_pos; VSOUT vec4 v_color; VSOUT vec2 v_tex0; VSOUT float v_fog; void main(void) { vec4 Vertex = u_world * vec4(in_pos, 1.0); gl_Position = u_proj * u_view * Vertex; vec3 Normal = mat3(u_world) * in_normal; v_tex0 = in_tex0; v_color = in_color; v_color.rgb += u_ambLight.rgb*surfAmbient; v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse; // rim light float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec); vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0); v_color.rgb += rimlight.rgb; v_color = clamp(v_color, 0.0, 1.0); v_color *= u_matColor; v_fog = DoFog(gl_Position.w); } ================================================ FILE: src/extras/shaders/neoRimSkin.vert ================================================ uniform mat4 u_boneMatrices[64]; uniform vec3 u_viewVec; uniform vec4 u_rampStart; uniform vec4 u_rampEnd; uniform vec3 u_rimData; VSIN(ATTRIB_POS) vec3 in_pos; VSOUT vec4 v_color; VSOUT vec2 v_tex0; VSOUT float v_fog; void main(void) { vec3 SkinVertex = vec3(0.0, 0.0, 0.0); vec3 SkinNormal = vec3(0.0, 0.0, 0.0); for(int i = 0; i < 4; i++){ SkinVertex += (u_boneMatrices[int(in_indices[i])] * vec4(in_pos, 1.0)).xyz * in_weights[i]; SkinNormal += (mat3(u_boneMatrices[int(in_indices[i])]) * in_normal) * in_weights[i]; } vec4 Vertex = u_world * vec4(SkinVertex, 1.0); gl_Position = u_proj * u_view * Vertex; vec3 Normal = mat3(u_world) * SkinNormal; v_tex0 = in_tex0; v_color = in_color; v_color.rgb += u_ambLight.rgb*surfAmbient; v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse; // rim light float f = u_rimData.x - u_rimData.y*dot(Normal, u_viewVec); vec4 rimlight = clamp(mix(u_rampEnd, u_rampStart, f)*u_rimData.z, 0.0, 1.0); v_color.rgb += rimlight.rgb; v_color = clamp(v_color, 0.0, 1.0); v_color *= u_matColor; v_fog = DoFog(gl_Position.z); } ================================================ FILE: src/extras/shaders/neoRimSkin_VS.hlsl ================================================ #include "standardConstants.h" float4x3 boneMatrices[64] : register(c41); struct VS_in { float4 Position : POSITION; float3 Normal : NORMAL; float2 TexCoord : TEXCOORD0; float4 Prelight : COLOR0; float4 Weights : BLENDWEIGHT; int4 Indices : BLENDINDICES; }; struct VS_out { float4 Position : POSITION; float3 TexCoord0 : TEXCOORD0; // also fog float4 Color : COLOR0; }; float3 viewVec : register(c233); float4 rampStart : register(c234); float4 rampEnd : register(c235); float3 rimData : register(c236); VS_out main(in VS_in input) { VS_out output; int j; float3 SkinVertex = float3(0.0, 0.0, 0.0); float3 SkinNormal = float3(0.0, 0.0, 0.0); for(j = 0; j < 4; j++){ SkinVertex += mul(input.Position, boneMatrices[input.Indices[j]]).xyz * input.Weights[j]; SkinNormal += mul(input.Normal, (float3x3)boneMatrices[input.Indices[j]]).xyz * input.Weights[j]; } output.Position = mul(combinedMat, float4(SkinVertex, 1.0)); float3 Vertex = mul(worldMat, float4(SkinVertex, 1.0)).xyz; float3 Normal = mul(normalMat, SkinNormal); output.TexCoord0.xy = input.TexCoord; output.Color = input.Prelight; output.Color.rgb += ambientLight.rgb * surfAmbient; int i; //#ifdef DIRECTIONALS for(i = 0; i < numDirLights; i++) output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse; //#endif //#ifdef POINTLIGHTS // for(i = 0; i < numPointLights; i++) // output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse; //#endif //#ifdef SPOTLIGHTS // for(i = 0; i < numSpotLights; i++) // output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse; //#endif // rim light float f = rimData.x - rimData.y*dot(Normal, viewVec); float4 rimlight = saturate(lerp(rampEnd, rampStart, f)*rimData.z); output.Color.xyz += rimlight.xyz; // PS2 clamps before material color output.Color = clamp(output.Color, 0.0, 1.0); output.Color *= matCol; output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); return output; } ================================================ FILE: src/extras/shaders/neoRim_VS.hlsl ================================================ #include "standardConstants.h" struct VS_in { float4 Position : POSITION; float3 Normal : NORMAL; float2 TexCoord : TEXCOORD0; float4 Prelight : COLOR0; }; struct VS_out { float4 Position : POSITION; float3 TexCoord0 : TEXCOORD0; // also fog float4 Color : COLOR0; }; float3 viewVec : register(c233); float4 rampStart : register(c234); float4 rampEnd : register(c235); float3 rimData : register(c236); VS_out main(in VS_in input) { VS_out output; output.Position = mul(combinedMat, input.Position); float3 Vertex = mul(worldMat, input.Position).xyz; float3 Normal = mul(normalMat, input.Normal); output.TexCoord0.xy = input.TexCoord; output.Color = input.Prelight; output.Color.rgb += ambientLight.rgb * surfAmbient; int i; //#ifdef DIRECTIONALS for(i = 0; i < numDirLights; i++) output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse; //#endif //#ifdef POINTLIGHTS // for(i = 0; i < numPointLights; i++) // output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse; //#endif //#ifdef SPOTLIGHTS // for(i = 0; i < numSpotLights; i++) // output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse; //#endif // rim light float f = rimData.x - rimData.y*dot(Normal, viewVec); float4 rimlight = saturate(lerp(rampEnd, rampStart, f)*rimData.z); output.Color.xyz += rimlight.xyz; // PS2 clamps before material color output.Color = clamp(output.Color, 0.0, 1.0); output.Color *= matCol; output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); return output; } ================================================ FILE: src/extras/shaders/neoVehicle.frag ================================================ uniform sampler2D tex0; uniform sampler2D tex1; FSIN vec4 v_color; FSIN vec4 v_reflcolor; FSIN vec2 v_tex0; FSIN vec2 v_tex1; FSIN float v_fog; void main(void) { vec4 pass1 = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); vec3 envmap = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)).rgb; pass1.rgb = mix(pass1.rgb, envmap, v_reflcolor.a); pass1.rgb = mix(u_fogColor.rgb, pass1.rgb, v_fog); // pass1.rgb += v_reflcolor.rgb * v_fog; vec3 pass2 = v_reflcolor.rgb * v_fog; vec4 color; color.rgb = pass1.rgb*pass1.a + pass2; color.a = pass1.a; // color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog); DoAlphaTest(color.a); FRAGCOLOR(color); } ================================================ FILE: src/extras/shaders/neoVehicle.vert ================================================ uniform vec3 u_eye; uniform vec4 u_reflProps; uniform vec4 u_specDir[5]; uniform vec4 u_specColor[5]; #define fresnel (u_reflProps.x) #define lightStrength (u_reflProps.y) // speclight alpha #define shininess (u_reflProps.z) #define specularity (u_reflProps.w) VSIN(ATTRIB_POS) vec3 in_pos; VSOUT vec4 v_color; VSOUT vec4 v_reflcolor; VSOUT vec2 v_tex0; VSOUT vec2 v_tex1; VSOUT float v_fog; vec3 DoDirLightSpec(vec3 Ldir, vec3 Lcol, vec3 N, vec3 V, float power) { return pow(clamp(dot(N, normalize(V + -Ldir)), 0.0, 1.0), power)*Lcol; } void main(void) { vec4 Vertex = u_world * vec4(in_pos, 1.0); gl_Position = u_proj * u_view * Vertex; vec3 Normal = mat3(u_world) * in_normal; vec3 viewVec = normalize(u_eye - Vertex.xyz); v_tex0 = in_tex0; v_color = in_color; v_color.rgb += u_ambLight.rgb*surfAmbient; v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse*lightStrength; v_color = clamp(v_color, 0.0, 1.0); v_color *= u_matColor; // reflect V along Normal vec3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec; v_tex1 = uv2.xy*0.5 + 0.5; float b = 1.0 - clamp(dot(viewVec, Normal), 0.0, 1.0); v_reflcolor = vec4(0.0, 0.0, 0.0, 1.0); v_reflcolor.a = mix(b*b*b*b*b, 1.0, fresnel)*shininess; for(int i = 0; i < 5; i++) v_reflcolor.rgb += DoDirLightSpec(u_specDir[i].xyz, u_specColor[i].rgb, Normal, viewVec, u_specDir[i].w)*specularity*lightStrength; v_fog = DoFog(gl_Position.w); } ================================================ FILE: src/extras/shaders/neoVehicle_PS.hlsl ================================================ struct VS_out { float4 Position : POSITION; float3 TexCoord0 : TEXCOORD0; float2 TexCoord1 : TEXCOORD1; float4 Color : COLOR0; float4 ReflColor : COLOR1; }; sampler2D tex0 : register(s0); sampler2D tex1 : register(s1); float4 fogColor : register(c0); float4 main(VS_out input) : COLOR { float4 pass1 = input.Color; //#ifdef TEX pass1 *= tex2D(tex0, input.TexCoord0.xy); //#endif float3 envmap = tex2D(tex1, input.TexCoord1).rgb; pass1.rgb = lerp(pass1.rgb, envmap, input.ReflColor.a); // pass1.rgb = envmap; // pass1.rgb *= input.ReflColor.a; pass1.rgb = lerp(fogColor.rgb, pass1.rgb, input.TexCoord0.z); // pass1.rgb += input.ReflColor.rgb * input.TexCoord0.z; float3 pass2 = input.ReflColor.rgb*input.TexCoord0.z; float4 color; color.rgb = pass1.rgb*pass1.a + pass2; color.a = pass1.a; return color; } ================================================ FILE: src/extras/shaders/neoVehicle_VS.hlsl ================================================ #include "standardConstants.h" struct VS_in { float4 Position : POSITION; float3 Normal : NORMAL; float2 TexCoord : TEXCOORD0; float4 Prelight : COLOR0; }; struct VS_out { float4 Position : POSITION; float3 TexCoord0 : TEXCOORD0; // also fog float2 TexCoord1 : TEXCOORD1; float4 Color : COLOR0; float4 ReflColor : COLOR1; }; float3 eye : register(c41); float4 reflProps : register(c42); Light specLights[5] : register(c43); #define fresnel (reflProps.x) #define lightStrength (reflProps.y) // speclight alpha #define shininess (reflProps.z) #define specularity (reflProps.w) VS_out main(in VS_in input) { VS_out output; output.Position = mul(combinedMat, input.Position); float3 Vertex = mul(worldMat, input.Position).xyz; float3 Normal = mul(normalMat, input.Normal); float3 viewVec = normalize(eye - Vertex); output.TexCoord0.xy = input.TexCoord; output.Color = input.Prelight; output.Color.rgb += ambientLight.rgb * surfAmbient*lightStrength; int i; for(i = 0; i < numDirLights; i++) output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse*lightStrength; // PS2 clamps before material color output.Color = clamp(output.Color, 0.0, 1.0); output.Color *= matCol; // reflect V along Normal float3 uv2 = Normal*dot(viewVec, Normal)*2.0 - viewVec; output.TexCoord1 = uv2.xy*0.5 + 0.5; float b = 1.0 - saturate(dot(viewVec, Normal)); output.ReflColor = float4(0.0, 0.0, 0.0, 1.0); output.ReflColor.a = lerp(b*b*b*b*b, 1.0, fresnel)*shininess; //Light mainLight = lights[0]; for(i = 0; i < 5; i++) output.ReflColor.xyz += DoDirLightSpec(specLights[i], Normal, viewVec, specLights[i].direction.w)*specularity*lightStrength; output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0); return output; } ================================================ FILE: src/extras/shaders/neoWorldVC.frag ================================================ uniform sampler2D tex0; uniform sampler2D tex1; uniform vec4 u_lightMap; FSIN vec4 v_color; FSIN vec2 v_tex0; FSIN vec2 v_tex1; FSIN float v_fog; void main(void) { vec4 t0 = texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); vec4 t1 = texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)); vec4 color; color = t0*v_color*(1.0 + u_lightMap*(t1-1.0)); color.a = v_color.a*t0.a*u_lightMap.a; color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog); DoAlphaTest(color.a); FRAGCOLOR(color); } ================================================ FILE: src/extras/shaders/neoWorldVC_PS.hlsl ================================================ sampler2D Diffuse : register(s0); sampler2D Light : register(s1); float4 fogColor : register(c0); float4 lm : register(c1); struct PS_INPUT { float4 Color : COLOR0; float3 Tex0 : TEXCOORD0; float2 Tex1 : TEXCOORD1; }; float4 main(PS_INPUT IN) : COLOR { float4 t0 = tex2D(Diffuse, IN.Tex0.xy); float4 t1 = tex2D(Light, IN.Tex1); float4 col = t0*IN.Color*(1 + lm*(t1-1)); col.a = IN.Color.a*t0.a*lm.a; col.rgb = lerp(fogColor.rgb, col.rgb, IN.Tex0.z); return col; } ================================================ FILE: src/extras/shaders/screenDroplet.frag ================================================ uniform sampler2D tex0; uniform sampler2D tex1; FSIN vec4 v_color; FSIN vec2 v_tex0; FSIN vec2 v_tex1; FSIN float v_fog; void main(void) { vec4 color; color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); color *= texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y)); FRAGCOLOR(color); } ================================================ FILE: src/extras/shaders/screenDroplet_PS.hlsl ================================================ struct VS_out { float4 Position : POSITION; float2 TexCoord0 : TEXCOORD0; float2 TexCoord1 : TEXCOORD1; float4 Color : COLOR0; }; sampler2D tex0 : register(s0); sampler2D tex1 : register(s1); float4 main(VS_out input) : COLOR { float4 color = input.Color; color *= tex2D(tex0, input.TexCoord0.xy); color *= tex2D(tex1, input.TexCoord1.xy); return color; } ================================================ FILE: src/extras/shaders/simple.frag ================================================ uniform sampler2D tex0; FSIN vec4 v_color; FSIN vec2 v_tex0; FSIN float v_fog; void main(void) { vec4 color; color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y)); color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog); DoAlphaTest(color.a); FRAGCOLOR(color); } ================================================ FILE: src/extras/shaders/standardConstants.h ================================================ float4x4 combinedMat : register(c0); float4x4 worldMat : register(c4); float3x3 normalMat : register(c8); float4 matCol : register(c12); float4 surfProps : register(c13); float4 fogData : register(c14); float4 ambientLight : register(c15); #define surfAmbient (surfProps.x) #define surfSpecular (surfProps.y) #define surfDiffuse (surfProps.z) #define fogStart (fogData.x) #define fogEnd (fogData.y) #define fogRange (fogData.z) #define fogDisable (fogData.w) #include "lighting.h" int numDirLights : register(i0); int numPointLights : register(i1); int numSpotLights : register(i2); int4 firstLight : register(c16); Light lights[8] : register(c17); #define firstDirLight (firstLight.x) #define firstPointLight (firstLight.y) #define firstSpotLight (firstLight.z) ================================================ FILE: src/fakerw/fake.cpp ================================================ #define _CRT_SECURE_NO_WARNINGS #define WITH_D3D // not WITHD3D, so it's librw define #include #include #include #include #include #include #include #ifndef _WIN32 #include "crossplatform.h" #endif using namespace rw; RwUInt8 RwObjectGetType(const RwObject *obj) { return obj->type; } RwFrame* rwObjectGetParent(const RwObject *obj) { return (RwFrame*)obj->parent; } void *RwMalloc(size_t size) { return engine->memfuncs.rwmalloc(size, 0); } void *RwCalloc(size_t numObj, size_t sizeObj) { void *mem = RwMalloc(numObj*sizeObj); if(mem) memset(mem, 0, numObj*sizeObj); return mem; } void RwFree(void *mem) { engine->memfuncs.rwfree(mem); } //RwReal RwV3dNormalize(RwV3d * out, const RwV3d * in); RwReal RwV3dLength(const RwV3d * in) { return length(*in); } //RwReal RwV2dLength(const RwV2d * in); //RwReal RwV2dNormalize(RwV2d * out, const RwV2d * in); //void RwV2dAssign(RwV2d * out, const RwV2d * ina); //void RwV2dAdd(RwV2d * out, const RwV2d * ina, const RwV2d * inb); //void RwV2dLineNormal(RwV2d * out, const RwV2d * ina, const RwV2d * inb); //void RwV2dSub(RwV2d * out, const RwV2d * ina, const RwV2d * inb); //void RwV2dPerp(RwV2d * out, const RwV2d * in); //void RwV2dScale(RwV2d * out, const RwV2d * in, RwReal scalar); //RwReal RwV2dDotProduct(const RwV2d * ina, const RwV2d * inb); //void RwV3dAssign(RwV3d * out, const RwV3d * ina); void RwV3dAdd(RwV3d * out, const RwV3d * ina, const RwV3d * inb) { *out = add(*ina, *inb); } void RwV3dSub(RwV3d * out, const RwV3d * ina, const RwV3d * inb) { *out = sub(*ina, *inb); } void RwV3dScale(RwV3d * out, const RwV3d * in, RwReal scalar) { *out = scale(*in, scalar); } void RwV3dIncrementScaled(RwV3d * out, const RwV3d * in, RwReal scalar) { *out = add(*out, scale(*in, scalar)); } void RwV3dNegate(RwV3d * out, const RwV3d * in) { *out = neg(*in); } RwReal RwV3dDotProduct(const RwV3d * ina, const RwV3d * inb) { return dot(*ina, *inb); } //void RwV3dCrossProduct(RwV3d * out, const RwV3d * ina, const RwV3d * inb); RwV3d *RwV3dTransformPoints(RwV3d * pointsOut, const RwV3d * pointsIn, RwInt32 numPoints, const RwMatrix * matrix) { V3d::transformPoints(pointsOut, pointsIn, numPoints, matrix); return pointsOut; } //RwV3d *RwV3dTransformVectors(RwV3d * vectorsOut, const RwV3d * vectorsIn, RwInt32 numPoints, const RwMatrix * matrix); RwBool RwMatrixDestroy(RwMatrix *mpMat) { mpMat->destroy(); return true; } RwMatrix *RwMatrixCreate(void) { return Matrix::create(); } void RwMatrixCopy(RwMatrix * dstMatrix, const RwMatrix * srcMatrix) { *dstMatrix = *srcMatrix; } void RwMatrixSetIdentity(RwMatrix * matrix) { matrix->setIdentity(); } RwMatrix *RwMatrixMultiply(RwMatrix * matrixOut, const RwMatrix * MatrixIn1, const RwMatrix * matrixIn2); RwMatrix *RwMatrixTransform(RwMatrix * matrix, const RwMatrix * transform, RwOpCombineType combineOp) { matrix->transform(transform, (rw::CombineOp)combineOp); return matrix; } //RwMatrix *RwMatrixOrthoNormalize(RwMatrix * matrixOut, const RwMatrix * matrixIn); RwMatrix *RwMatrixInvert(RwMatrix * matrixOut, const RwMatrix * matrixIn) { Matrix::invert(matrixOut, matrixIn); return matrixOut; } RwMatrix *RwMatrixScale(RwMatrix * matrix, const RwV3d * scale, RwOpCombineType combineOp) { matrix->scale(scale, (rw::CombineOp)combineOp); return matrix; } RwMatrix *RwMatrixTranslate(RwMatrix * matrix, const RwV3d * translation, RwOpCombineType combineOp) { matrix->translate(translation, (rw::CombineOp)combineOp); return matrix; } RwMatrix *RwMatrixRotate(RwMatrix * matrix, const RwV3d * axis, RwReal angle, RwOpCombineType combineOp) { matrix->rotate(axis, angle, (rw::CombineOp)combineOp); return matrix; } //RwMatrix *RwMatrixRotateOneMinusCosineSine(RwMatrix * matrix, const RwV3d * unitAxis, RwReal oneMinusCosine, RwReal sine, RwOpCombineType combineOp); //const RwMatrix *RwMatrixQueryRotate(const RwMatrix * matrix, RwV3d * unitAxis, RwReal * angle, RwV3d * center); RwV3d *RwMatrixGetRight(RwMatrix * matrix) { return &matrix->right; } RwV3d *RwMatrixGetUp(RwMatrix * matrix) { return &matrix->up; } RwV3d *RwMatrixGetAt(RwMatrix * matrix) { return &matrix->at; } RwV3d *RwMatrixGetPos(RwMatrix * matrix) { return &matrix->pos; } RwMatrix *RwMatrixUpdate(RwMatrix * matrix) { matrix->update(); return matrix; } //RwMatrix *RwMatrixOptimize(RwMatrix * matrix, const RwMatrixTolerance *tolerance); RwFrame *RwFrameForAllObjects(RwFrame * frame, RwObjectCallBack callBack, void *data) { FORLIST(lnk, frame->objectList) if(callBack(&ObjectWithFrame::fromFrame(lnk)->object, data) == nil) break; return frame; } RwFrame *RwFrameTranslate(RwFrame * frame, const RwV3d * v, RwOpCombineType combine) { frame->translate(v, (CombineOp)combine); return frame; } RwFrame *RwFrameRotate(RwFrame * frame, const RwV3d * axis, RwReal angle, RwOpCombineType combine) { frame->rotate(axis, angle, (CombineOp)combine); return frame; } RwFrame *RwFrameScale(RwFrame * frame, const RwV3d * v, RwOpCombineType combine) { frame->scale(v, (CombineOp)combine); return frame; } RwFrame *RwFrameTransform(RwFrame * frame, const RwMatrix * m, RwOpCombineType combine) { frame->transform(m, (CombineOp)combine); return frame; } //TODO: actually implement this! RwFrame *RwFrameOrthoNormalize(RwFrame * frame) { return frame; } RwFrame *RwFrameSetIdentity(RwFrame * frame) { frame->matrix.setIdentity(); frame->updateObjects(); return frame; } //RwFrame *RwFrameCloneHierarchy(RwFrame * root); //RwBool RwFrameDestroyHierarchy(RwFrame * frame); RwFrame *RwFrameForAllChildren(RwFrame * frame, RwFrameCallBack callBack, void *data) { return frame->forAllChildren(callBack, data); } RwFrame *RwFrameRemoveChild(RwFrame * child) { child->removeChild(); return child; } RwFrame *RwFrameAddChild(RwFrame * parent, RwFrame * child) { parent->addChild(child); return parent; } RwFrame *RwFrameGetParent(const RwFrame * frame) { return frame->getParent(); } //RwFrame *RwFrameGetRoot(const RwFrame * frame); RwMatrix *RwFrameGetLTM(RwFrame * frame) { return frame->getLTM(); } RwMatrix *RwFrameGetMatrix(RwFrame * frame) { return &frame->matrix; } RwFrame *RwFrameUpdateObjects(RwFrame * frame) { frame->updateObjects(); return frame; } RwFrame *RwFrameCreate(void) { return rw::Frame::create(); } //RwBool RwFrameInit(RwFrame *frame); //RwBool RwFrameDeInit(RwFrame *frame); RwBool RwFrameDestroy(RwFrame * frame) { frame->destroy(); return true; } //void _rwFrameInit(RwFrame *frame); //void _rwFrameDeInit(RwFrame *frame); //RwBool RwFrameDirty(const RwFrame * frame); //RwInt32 RwFrameCount(RwFrame * frame); //RwBool RwFrameSetStaticPluginsSize(RwInt32 size); RwInt32 RwFrameRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB) { return Frame::registerPlugin(size, pluginID, constructCB, destructCB, (CopyConstructor)copyCB); } //RwInt32 RwFrameGetPluginOffset(RwUInt32 pluginID); //RwBool RwFrameValidatePlugins(const RwFrame * frame); //RwFrame *_rwFrameCloneAndLinkClones(RwFrame * root); //RwFrame *_rwFramePurgeClone(RwFrame *root); RwInt32 RwFrameRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB) { return Frame::registerPluginStream(pluginID, readCB, (StreamWrite)writeCB, (StreamGetSize)getSizeCB); } rwFrameList *rwFrameListDeinitialize(rwFrameList *frameList) { rwFree(frameList->frames); frameList->frames = nil; return frameList; } rwFrameList *rwFrameListStreamRead(RwStream *stream, rwFrameList *fl) { return fl->streamRead(stream); } RwCamera *RwCameraBeginUpdate(RwCamera * camera) { camera->beginUpdate(); return camera; } RwCamera *RwCameraEndUpdate(RwCamera * camera) { camera->endUpdate(); return camera; } RwCamera *RwCameraClear(RwCamera * camera, RwRGBA * colour, RwInt32 clearMode) { camera->clear(colour, clearMode); return camera; } // WARNING: ignored argument RwCamera *RwCameraShowRaster(RwCamera * camera, void *pDev, RwUInt32 flags) { camera->showRaster(flags); return camera; } RwBool RwCameraDestroy(RwCamera * camera) { camera->destroy(); return true; } RwCamera *RwCameraCreate(void) { return rw::Camera::create(); } RwCamera *RwCameraClone(RwCamera * camera) { return camera->clone(); } RwCamera *RwCameraSetViewOffset(RwCamera *camera, const RwV2d *offset) { camera->setViewOffset(offset); return camera; } RwCamera *RwCameraSetViewWindow(RwCamera *camera, const RwV2d *viewWindow) { camera->setViewWindow(viewWindow); return camera; } RwCamera *RwCameraSetProjection(RwCamera *camera, RwCameraProjection projection) { camera->projection = projection; return camera; } RwCamera *RwCameraSetNearClipPlane(RwCamera *camera, RwReal nearClip) { camera->setNearPlane(nearClip); return camera; } RwCamera *RwCameraSetFarClipPlane(RwCamera *camera, RwReal farClip) { camera->setFarPlane(farClip); return camera; } RwInt32 RwCameraRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwCameraGetPluginOffset(RwUInt32 pluginID); RwBool RwCameraValidatePlugins(const RwCamera * camera); RwFrustumTestResult RwCameraFrustumTestSphere(const RwCamera * camera, const RwSphere * sphere) { return (RwFrustumTestResult)camera->frustumTestSphere(sphere); } const RwV2d *RwCameraGetViewOffset(const RwCamera *camera) { return &camera->viewOffset; } RwCamera *RwCameraSetRaster(RwCamera *camera, RwRaster *raster) { camera->frameBuffer = raster; return camera; } RwRaster *RwCameraGetRaster(const RwCamera *camera) { return camera->frameBuffer; } RwCamera *RwCameraSetZRaster(RwCamera *camera, RwRaster *zRaster) { camera->zBuffer = zRaster; return camera; } RwRaster *RwCameraGetZRaster(const RwCamera *camera) { return camera->zBuffer; } RwReal RwCameraGetNearClipPlane(const RwCamera *camera) { return camera->nearPlane; } RwReal RwCameraGetFarClipPlane(const RwCamera *camera) { return camera->farPlane; } RwCamera *RwCameraSetFogDistance(RwCamera *camera, RwReal fogDistance) { camera->fogPlane = fogDistance; return camera; } RwReal RwCameraGetFogDistance(const RwCamera *camera) { return camera->fogPlane; } RwCamera *RwCameraGetCurrentCamera(void) { return rw::engine->currentCamera; } RwCameraProjection RwCameraGetProjection(const RwCamera *camera); const RwV2d *RwCameraGetViewWindow(const RwCamera *camera) { return &camera->viewWindow; } RwMatrix *RwCameraGetViewMatrix(RwCamera *camera) { return &camera->viewMatrix; } RwCamera *RwCameraSetFrame(RwCamera *camera, RwFrame *frame) { camera->setFrame(frame); return camera; } RwFrame *RwCameraGetFrame(const RwCamera *camera) { return camera->getFrame(); } RwImage *RwImageCreate(RwInt32 width, RwInt32 height, RwInt32 depth) { return Image::create(width, height, depth); } RwBool RwImageDestroy(RwImage * image) { image->destroy(); return true; } RwImage *RwImageAllocatePixels(RwImage * image) { image->allocate(); return image; } RwImage *RwImageFreePixels(RwImage * image) { image->free(); return image; } RwImage *RwImageCopy(RwImage * destImage, const RwImage * sourceImage); RwImage *RwImageResize(RwImage * image, RwInt32 width, RwInt32 height); RwImage *RwImageApplyMask(RwImage * image, const RwImage * mask); RwImage *RwImageMakeMask(RwImage * image); RwImage *RwImageReadMaskedImage(const RwChar * imageName, const RwChar * maskname); RwImage *RwImageRead(const RwChar * imageName); RwImage *RwImageWrite(RwImage * image, const RwChar * imageName); RwChar *RwImageGetPath(void); const RwChar *RwImageSetPath(const RwChar * path) { Image::setSearchPath(path); return path; } RwImage *RwImageSetStride(RwImage * image, RwInt32 stride) { image->stride = stride; return image; } RwImage *RwImageSetPixels(RwImage * image, RwUInt8 * pixels) { image->pixels = pixels; return image; } RwImage *RwImageSetPalette(RwImage * image, RwRGBA * palette) { image->palette = (uint8*)palette; return image; } RwInt32 RwImageGetWidth(const RwImage * image) { return image->width; } RwInt32 RwImageGetHeight(const RwImage * image) { return image->height; } RwInt32 RwImageGetDepth(const RwImage * image) { return image->depth; } RwInt32 RwImageGetStride(const RwImage * image) { return image->stride; } RwUInt8 *RwImageGetPixels(const RwImage * image) { return image->pixels; } RwRGBA *RwImageGetPalette(const RwImage * image) { return (RwRGBA*)image->palette; } RwUInt32 RwRGBAToPixel(RwRGBA * rgbIn, RwInt32 rasterFormat); RwRGBA *RwRGBASetFromPixel(RwRGBA * rgbOut, RwUInt32 pixelValue, RwInt32 rasterFormat); RwBool RwImageSetGamma(RwReal gammaValue); RwReal RwImageGetGamma(void); RwImage *RwImageGammaCorrect(RwImage * image); RwRGBA *RwRGBAGammaCorrect(RwRGBA * rgb); RwInt32 RwImageRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwImageGetPluginOffset(RwUInt32 pluginID); RwBool RwImageValidatePlugins(const RwImage * image); //RwBool RwImageRegisterImageFormat(const RwChar * extension, RwImageCallBackRead imageRead, RwImageCallBackWrite imageWrite); const RwChar *RwImageFindFileType(const RwChar * imageName); RwInt32 RwImageStreamGetSize(const RwImage * image); RwImage *RwImageStreamRead(RwStream * stream); const RwImage *RwImageStreamWrite(const RwImage * image, RwStream * stream); RwImage *RwImageFindRasterFormat(RwImage *ipImage,RwInt32 nRasterType, RwInt32 *npWidth,RwInt32 *npHeight, RwInt32 *npDepth,RwInt32 *npFormat) { return Raster::imageFindRasterFormat(ipImage, nRasterType, npWidth, npHeight, npDepth, npFormat) ? ipImage : nil; } RwRaster *RwRasterCreate(RwInt32 width, RwInt32 height, RwInt32 depth, RwInt32 flags) { return Raster::create(width, height, depth, flags); } RwBool RwRasterDestroy(RwRaster * raster) { raster->destroy(); return true; } RwInt32 RwRasterGetWidth(const RwRaster *raster) { return raster->width; } RwInt32 RwRasterGetHeight(const RwRaster *raster) { return raster->height; } RwInt32 RwRasterGetStride(const RwRaster *raster); RwInt32 RwRasterGetDepth(const RwRaster *raster) { return raster->depth; } RwInt32 RwRasterGetFormat(const RwRaster *raster); RwInt32 RwRasterGetType(const RwRaster *raster); RwRaster *RwRasterGetParent(const RwRaster *raster) { return raster->parent; } RwRaster *RwRasterGetOffset(RwRaster *raster, RwInt16 *xOffset, RwInt16 *yOffset); RwInt32 RwRasterGetNumLevels(RwRaster * raster); RwRaster *RwRasterSubRaster(RwRaster * subRaster, RwRaster * raster, RwRect * rect); RwRaster *RwRasterRenderFast(RwRaster * raster, RwInt32 x, RwInt32 y) { return raster->renderFast(x, y) ? raster : nil; } RwRaster *RwRasterRender(RwRaster * raster, RwInt32 x, RwInt32 y); RwRaster *RwRasterRenderScaled(RwRaster * raster, RwRect * rect); RwRaster *RwRasterPushContext(RwRaster * raster) { return Raster::pushContext(raster); } RwRaster *RwRasterPopContext(void) { return Raster::popContext(); } RwRaster *RwRasterGetCurrentContext(void) { return Raster::getCurrentContext(); } RwBool RwRasterClear(RwInt32 pixelValue); RwBool RwRasterClearRect(RwRect * rpRect, RwInt32 pixelValue); RwRaster *RwRasterShowRaster(RwRaster * raster, void *dev, RwUInt32 flags); RwUInt8 *RwRasterLock(RwRaster * raster, RwUInt8 level, RwInt32 lockMode); RwRaster *RwRasterUnlock(RwRaster * raster); RwUInt8 *RwRasterLockPalette(RwRaster * raster, RwInt32 lockMode); RwRaster *RwRasterUnlockPalette(RwRaster * raster); RwInt32 RwRasterRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwRasterGetPluginOffset(RwUInt32 pluginID); RwBool RwRasterValidatePlugins(const RwRaster * raster); RwRaster *RwRasterSetFromImage(RwRaster *raster, RwImage *image) { return raster->setFromImage(image); } RwTexture *RwTextureCreate(RwRaster * raster) { return Texture::create(raster); } RwBool RwTextureDestroy(RwTexture * texture) { texture->destroy(); return true; } RwTexture *RwTextureAddRef(RwTexture *texture) { texture->addRef(); return texture; } // TODO RwBool RwTextureSetMipmapping(RwBool enable) { return true; } RwBool RwTextureGetMipmapping(void); // TODO RwBool RwTextureSetAutoMipmapping(RwBool enable) { return true; } RwBool RwTextureGetAutoMipmapping(void); RwBool RwTextureSetMipmapGenerationCallBack(RwTextureCallBackMipmapGeneration callback); RwTextureCallBackMipmapGeneration RwTextureGetMipmapGenerationCallBack(void); RwBool RwTextureSetMipmapNameCallBack(RwTextureCallBackMipmapName callback); RwTextureCallBackMipmapName RwTextureGetMipmapNameCallBack(void); RwBool RwTextureGenerateMipmapName(RwChar * name, RwChar * maskName, RwUInt8 mipLevel, RwInt32 format); RwBool RwTextureRasterGenerateMipmaps(RwRaster * raster, RwImage * image); RwTextureCallBackRead RwTextureGetReadCallBack(void); RwBool RwTextureSetReadCallBack(RwTextureCallBackRead fpCallBack); RwTexture *RwTextureSetName(RwTexture * texture, const RwChar * name) { strncpy(texture->name, name, 32); return texture; } RwTexture *RwTextureSetMaskName(RwTexture * texture, const RwChar * maskName); RwChar *RwTextureGetName(RwTexture *texture) { return texture->name; } RwChar *RwTextureGetMaskName(RwTexture *texture); RwTexture *RwTextureSetRaster(RwTexture * texture, RwRaster * raster) { texture->raster = raster; return texture; } RwTexture *RwTextureRead(const RwChar * name, const RwChar * maskName) { return Texture::read(name, maskName); } RwRaster *RwTextureGetRaster(const RwTexture *texture) { return texture->raster; } RwInt32 RwTextureRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwTextureGetPluginOffset(RwUInt32 pluginID); RwBool RwTextureValidatePlugins(const RwTexture * texture); RwTexDictionary *RwTextureGetDictionary(RwTexture *texture); RwTexture *RwTextureSetFilterMode(RwTexture *texture, RwTextureFilterMode filtering) { texture->setFilter((Texture::FilterMode)filtering); return texture; } RwTextureFilterMode RwTextureGetFilterMode(const RwTexture *texture); RwTexture *RwTextureSetAddressing(RwTexture *texture, RwTextureAddressMode addressing) { texture->setAddressU((Texture::Addressing)addressing); texture->setAddressV((Texture::Addressing)addressing); return texture; } RwTexture *RwTextureSetAddressingU(RwTexture *texture, RwTextureAddressMode addressing) { texture->setAddressU((Texture::Addressing)addressing); return texture; } RwTexture *RwTextureSetAddressingV(RwTexture *texture, RwTextureAddressMode addressing) { texture->setAddressV((Texture::Addressing)addressing); return texture; } RwTextureAddressMode RwTextureGetAddressing(const RwTexture *texture); RwTextureAddressMode RwTextureGetAddressingU(const RwTexture *texture); RwTextureAddressMode RwTextureGetAddressingV(const RwTexture *texture); // TODO void _rwD3D8TexDictionaryEnableRasterFormatConversion(bool enable) { } // hack for reading native textures RwBool rwNativeTextureHackRead(RwStream *stream, RwTexture **tex, RwInt32 size) { *tex = Texture::streamReadNative(stream); #ifdef LIBRW (*tex)->raster = rw::Raster::convertTexToCurrentPlatform((*tex)->raster); #endif return *tex != nil; } RwTexDictionary *RwTexDictionaryCreate(void) { return TexDictionary::create(); } RwBool RwTexDictionaryDestroy(RwTexDictionary * dict) { dict->destroy(); return true; } RwTexture *RwTexDictionaryAddTexture(RwTexDictionary * dict, RwTexture * texture) { dict->addFront(texture); return texture; } //RwTexture *RwTexDictionaryRemoveTexture(RwTexture * texture); RwTexture *RwTexDictionaryFindNamedTexture(RwTexDictionary * dict, const RwChar * name) { return dict->find(name); } RwTexDictionary *RwTexDictionaryGetCurrent(void) { return TexDictionary::getCurrent(); } RwTexDictionary *RwTexDictionarySetCurrent(RwTexDictionary * dict) { TexDictionary::setCurrent(dict); return dict; } const RwTexDictionary *RwTexDictionaryForAllTextures(const RwTexDictionary * dict, RwTextureCallBack fpCallBack, void *pData) { FORLIST(lnk, ((RwTexDictionary*)dict)->textures) if(fpCallBack(Texture::fromDict(lnk), pData) == nil) break; return dict; } RwBool RwTexDictionaryForAllTexDictionaries(RwTexDictionaryCallBack fpCallBack, void *pData); RwInt32 RwTexDictionaryRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwTexDictionaryGetPluginOffset(RwUInt32 pluginID); RwBool RwTexDictionaryValidatePlugins(const RwTexDictionary * dict); RwUInt32 RwTexDictionaryStreamGetSize(const RwTexDictionary *texDict); RwTexDictionary *RwTexDictionaryStreamRead(RwStream *stream); const RwTexDictionary *RwTexDictionaryStreamWrite(const RwTexDictionary *texDict, RwStream *stream) { ((RwTexDictionary*)texDict)->streamWrite(stream); return texDict; } RwStream *RwStreamOpen(RwStreamType type, RwStreamAccessType accessType, const void *pData) { StreamFile *file; StreamMemory *mem; RwMemory *memargs; const char *mode; switch(accessType){ case rwSTREAMREAD: mode = "rb"; break; case rwSTREAMWRITE: mode = "wb"; break; case rwSTREAMAPPEND: mode = "ab"; break; default: return nil; } // oh god this is horrible. librw streams really need fixing switch(type){ case rwSTREAMFILENAME:{ StreamFile fakefile; file = rwNewT(StreamFile, 1, 0); memcpy(file, &fakefile, sizeof(StreamFile)); #ifndef _WIN32 char *r = casepath((char*)pData); if (r) { if (file->open((char*)r, mode)) { free(r); return file; } free(r); } else #endif { if (file->open((char*)pData, mode)) return file; } rwFree(file); return nil; } case rwSTREAMMEMORY:{ StreamMemory fakemem; memargs = (RwMemory*)pData; mem = rwNewT(StreamMemory, 1, 0); memcpy(mem, &fakemem, sizeof(StreamMemory)); if(mem->open(memargs->start, memargs->length)) return mem; rwFree(mem); return nil; } default: assert(0 && "unknown type"); return nil; } } RwBool RwStreamClose(RwStream * stream, void *pData) { stream->close(); rwFree(stream); return true; } RwUInt32 RwStreamRead(RwStream * stream, void *buffer, RwUInt32 length) { return stream->read8(buffer, length); } RwStream *RwStreamWrite(RwStream * stream, const void *buffer, RwUInt32 length) { stream->write8(buffer, length); return stream; } RwStream *RwStreamSkip(RwStream * stream, RwUInt32 offset) { stream->seek(offset); return stream; } RwBool RwStreamFindChunk(RwStream *stream, RwUInt32 type, RwUInt32 *lengthOut, RwUInt32 *versionOut) { return findChunk(stream, type, lengthOut, versionOut); } void RwIm2DVertexSetCameraX(RwIm2DVertex *vert, RwReal camx) { } void RwIm2DVertexSetCameraY(RwIm2DVertex *vert, RwReal camy) { } void RwIm2DVertexSetCameraZ(RwIm2DVertex *vert, RwReal camz) { vert->setCameraZ(camz); } void RwIm2DVertexSetRecipCameraZ(RwIm2DVertex *vert, RwReal recipz) { vert->setRecipCameraZ(recipz); } void RwIm2DVertexSetScreenX(RwIm2DVertex *vert, RwReal scrnx) { vert->setScreenX(scrnx); } void RwIm2DVertexSetScreenY(RwIm2DVertex *vert, RwReal scrny) { vert->setScreenY(scrny); } void RwIm2DVertexSetScreenZ(RwIm2DVertex *vert, RwReal scrnz) { vert->setScreenZ(scrnz); } void RwIm2DVertexSetU(RwIm2DVertex *vert, RwReal texU, RwReal recipz) { vert->setU(texU, recipz); } void RwIm2DVertexSetV(RwIm2DVertex *vert, RwReal texV, RwReal recipz) { vert->setV(texV, recipz); } void RwIm2DVertexSetIntRGBA(RwIm2DVertex *vert, RwUInt8 red, RwUInt8 green, RwUInt8 blue, RwUInt8 alpha) { vert->setColor(red, green, blue, alpha); } RwReal RwIm2DGetNearScreenZ(void) { return im2d::GetNearZ(); } RwReal RwIm2DGetFarScreenZ(void) { return im2d::GetFarZ(); } RwBool RwIm2DRenderLine(RwIm2DVertex *vertices, RwInt32 numVertices, RwInt32 vert1, RwInt32 vert2) { im2d::RenderLine(vertices, numVertices, vert1, vert2); return true; } RwBool RwIm2DRenderTriangle(RwIm2DVertex *vertices, RwInt32 numVertices, RwInt32 vert1, RwInt32 vert2, RwInt32 vert3 ) { im2d::RenderTriangle(vertices, numVertices, vert1, vert2, vert3); return true; } RwBool RwIm2DRenderPrimitive(RwPrimitiveType primType, RwIm2DVertex *vertices, RwInt32 numVertices) { im2d::RenderPrimitive((PrimitiveType)primType, vertices, numVertices); return true; } RwBool RwIm2DRenderIndexedPrimitive(RwPrimitiveType primType, RwIm2DVertex *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices) { im2d::RenderIndexedPrimitive((PrimitiveType)primType, vertices, numVertices, indices, numIndices); return true; } void RwIm3DVertexSetPos(RwIm3DVertex *vert, RwReal x, RwReal y, RwReal z) { vert->setX(x); vert->setY(y); vert->setZ(z); } void RwIm3DVertexSetU(RwIm3DVertex *vert, RwReal u) { vert->setU(u); } void RwIm3DVertexSetV(RwIm3DVertex *vert, RwReal v) { vert->setV(v); } void RwIm3DVertexSetRGBA(RwIm3DVertex *vert, RwUInt8 r, RwUInt8 g, RwUInt8 b, RwUInt8 a) { vert->setColor(r, g, b, a); } void *RwIm3DTransform(RwIm3DVertex *pVerts, RwUInt32 numVerts, RwMatrix *ltm, RwUInt32 flags) { im3d::Transform(pVerts, numVerts, ltm, flags); return pVerts; } RwBool RwIm3DEnd(void) { im3d::End(); return true; } RwBool RwIm3DRenderLine(RwInt32 vert1, RwInt32 vert2) { RwImVertexIndex indices[2]; indices[0] = vert1; indices[1] = vert2; im3d::RenderIndexedPrimitive((PrimitiveType)PRIMTYPELINELIST, indices, 2); return true; } RwBool RwIm3DRenderTriangle(RwInt32 vert1, RwInt32 vert2, RwInt32 vert3); RwBool RwIm3DRenderIndexedPrimitive(RwPrimitiveType primType, RwImVertexIndex *indices, RwInt32 numIndices) { im3d::RenderIndexedPrimitive((PrimitiveType)primType, indices, numIndices); return true; } RwBool RwIm3DRenderPrimitive(RwPrimitiveType primType); RwBool RwRenderStateGet(RwRenderState state, void *value) { uint32 *uival = (uint32*)value; uint32 fog; switch(state){ case rwRENDERSTATETEXTURERASTER: *(void**)value = GetRenderStatePtr(TEXTURERASTER); return true; case rwRENDERSTATETEXTUREADDRESS: *uival = GetRenderState(TEXTUREADDRESS); return true; case rwRENDERSTATETEXTUREADDRESSU: *uival = GetRenderState(TEXTUREADDRESSU); return true; case rwRENDERSTATETEXTUREADDRESSV: *uival = GetRenderState(TEXTUREADDRESSV); return true; case rwRENDERSTATETEXTUREPERSPECTIVE: *uival = 1; return true; case rwRENDERSTATEZTESTENABLE: *uival = GetRenderState(ZTESTENABLE); return true; case rwRENDERSTATESHADEMODE: *uival = rwSHADEMODEGOURAUD; return true; case rwRENDERSTATEZWRITEENABLE: *uival = GetRenderState(ZWRITEENABLE); return true; case rwRENDERSTATETEXTUREFILTER: *uival = GetRenderState(TEXTUREFILTER); return true; case rwRENDERSTATESRCBLEND: *uival = GetRenderState(SRCBLEND); return true; case rwRENDERSTATEDESTBLEND: *uival = GetRenderState(DESTBLEND); return true; case rwRENDERSTATEVERTEXALPHAENABLE: *uival = GetRenderState(VERTEXALPHA); return true; case rwRENDERSTATEBORDERCOLOR: *uival = 0; return true; case rwRENDERSTATEFOGENABLE: *uival = GetRenderState(FOGENABLE); return true; case rwRENDERSTATEFOGCOLOR: // have to swap R and B here fog = GetRenderState(FOGCOLOR); *uival = (fog>>16)&0xFF; *uival |= (fog&0xFF)<<16; *uival |= fog&0xFF00; *uival |= fog&0xFF000000; return true; case rwRENDERSTATEFOGTYPE: *uival = rwFOGTYPELINEAR; return true; case rwRENDERSTATEFOGDENSITY: *(float*)value = 1.0f; return true; case rwRENDERSTATECULLMODE: *uival = GetRenderState(CULLMODE); return true; // all unsupported case rwRENDERSTATEFOGTABLE: case rwRENDERSTATEALPHAPRIMITIVEBUFFER: case rwRENDERSTATESTENCILENABLE: case rwRENDERSTATESTENCILFAIL: case rwRENDERSTATESTENCILZFAIL: case rwRENDERSTATESTENCILPASS: case rwRENDERSTATESTENCILFUNCTION: case rwRENDERSTATESTENCILFUNCTIONREF: case rwRENDERSTATESTENCILFUNCTIONMASK: case rwRENDERSTATESTENCILFUNCTIONWRITEMASK: default: return false; } } RwBool RwRenderStateSet(RwRenderState state, void *value) { uint32 uival = (uintptr)value; uint32 fog; switch(state){ case rwRENDERSTATETEXTURERASTER: SetRenderStatePtr(TEXTURERASTER, value); return true; case rwRENDERSTATETEXTUREADDRESS: SetRenderState(TEXTUREADDRESS, uival); return true; case rwRENDERSTATETEXTUREADDRESSU: SetRenderState(TEXTUREADDRESSU, uival); return true; case rwRENDERSTATETEXTUREADDRESSV: SetRenderState(TEXTUREADDRESSV, uival); return true; case rwRENDERSTATETEXTUREPERSPECTIVE: return true; case rwRENDERSTATEZTESTENABLE: SetRenderState(ZTESTENABLE, uival); return true; case rwRENDERSTATESHADEMODE: return true; case rwRENDERSTATEZWRITEENABLE: SetRenderState(ZWRITEENABLE, uival); return true; case rwRENDERSTATETEXTUREFILTER: SetRenderState(TEXTUREFILTER, uival); return true; case rwRENDERSTATESRCBLEND: SetRenderState(SRCBLEND, uival); return true; case rwRENDERSTATEDESTBLEND: SetRenderState(DESTBLEND, uival); return true; case rwRENDERSTATEVERTEXALPHAENABLE: SetRenderState(VERTEXALPHA, uival); return true; case rwRENDERSTATEBORDERCOLOR: return true; case rwRENDERSTATEFOGENABLE: SetRenderState(FOGENABLE, uival); return true; case rwRENDERSTATEFOGCOLOR: // have to swap R and B here fog = (uival>>16)&0xFF; fog |= (uival&0xFF)<<16; fog |= uival&0xFF00; fog |= uival&0xFF000000; SetRenderState(FOGCOLOR, fog); return true; case rwRENDERSTATEFOGTYPE: return true; case rwRENDERSTATEFOGDENSITY: return true; case rwRENDERSTATEFOGTABLE: return true; case rwRENDERSTATEALPHAPRIMITIVEBUFFER: return true; case rwRENDERSTATECULLMODE: SetRenderState(CULLMODE, uival); return true; // all unsupported case rwRENDERSTATESTENCILENABLE: case rwRENDERSTATESTENCILFAIL: case rwRENDERSTATESTENCILZFAIL: case rwRENDERSTATESTENCILPASS: case rwRENDERSTATESTENCILFUNCTION: case rwRENDERSTATESTENCILFUNCTIONREF: case rwRENDERSTATESTENCILFUNCTIONMASK: case rwRENDERSTATESTENCILFUNCTIONWRITEMASK: default: return true; } } static rw::MemoryFunctions gMemfuncs; static void *(*real_malloc)(size_t size); static void *(*real_realloc)(void *mem, size_t newSize); static void *mallocWrap(size_t sz, uint32 hint) { if(sz == 0) return nil; return real_malloc(sz); } static void *reallocWrap(void *p, size_t sz, uint32 hint) { return real_realloc(p, sz); } // WARNING: unused parameters RwBool RwEngineInit(RwMemoryFunctions *memFuncs, RwUInt32 initFlags, RwUInt32 resArenaSize) { if(memFuncs){ real_malloc = memFuncs->rwmalloc; real_realloc = memFuncs->rwrealloc; gMemfuncs.rwmalloc = mallocWrap; gMemfuncs.rwrealloc = reallocWrap; gMemfuncs.rwfree = memFuncs->rwfree; Engine::init(&gMemfuncs); }else{ Engine::init(nil); } return true; } // TODO: this is platform dependent RwBool RwEngineOpen(RwEngineOpenParams *initParams) { static EngineOpenParams openParams; #ifdef RW_D3D9 openParams.window = (HWND)initParams->displayID; #else openParams = *(EngineOpenParams*)initParams->displayID; #endif return Engine::open(&openParams); } RwBool RwEngineStart(void) { rw::d3d::isP8supported = false; return Engine::start(); } RwBool RwEngineStop(void) { Engine::stop(); return true; } RwBool RwEngineClose(void) { Engine::close(); return true; } RwBool RwEngineTerm(void) { Engine::term(); return true; } RwInt32 RwEngineRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor initCB, RwPluginObjectDestructor termCB); RwInt32 RwEngineGetPluginOffset(RwUInt32 pluginID); RwInt32 RwEngineGetNumSubSystems(void) { return Engine::getNumSubSystems(); } RwSubSystemInfo *RwEngineGetSubSystemInfo(RwSubSystemInfo *subSystemInfo, RwInt32 subSystemIndex) { return Engine::getSubSystemInfo(subSystemInfo, subSystemIndex); } RwInt32 RwEngineGetCurrentSubSystem(void) { return Engine::getCurrentSubSystem(); } RwBool RwEngineSetSubSystem(RwInt32 subSystemIndex) { return Engine::setSubSystem(subSystemIndex); } RwInt32 RwEngineGetNumVideoModes(void) { return Engine::getNumVideoModes(); } RwVideoMode *RwEngineGetVideoModeInfo(RwVideoMode *modeinfo, RwInt32 modeIndex) { return Engine::getVideoModeInfo(modeinfo, modeIndex); } RwInt32 RwEngineGetCurrentVideoMode(void) { return Engine::getCurrentVideoMode(); } RwBool RwEngineSetVideoMode(RwInt32 modeIndex) { return Engine::setVideoMode(modeIndex); } RwInt32 RwEngineGetTextureMemorySize(void); RwInt32 RwEngineGetMaxTextureSize(void); // TODO void RwD3D8EngineSetRefreshRate(RwUInt32 refreshRate) {} RwBool RwD3D8DeviceSupportsDXTTexture(void) { return true; } void RwD3D8EngineSetMultiSamplingLevels(RwUInt32 level) { Engine::setMultiSamplingLevels(level); } RwUInt32 RwD3D8EngineGetMaxMultiSamplingLevels(void) { return Engine::getMaxMultiSamplingLevels(); } RpMaterial *RpMaterialCreate(void) { return Material::create(); } RwBool RpMaterialDestroy(RpMaterial *material) { material->destroy(); return true; } //RpMaterial *RpMaterialClone(RpMaterial *material); RpMaterial *RpMaterialSetTexture(RpMaterial *material, RwTexture *texture) { material->setTexture(texture); return material; } //RpMaterial *RpMaterialAddRef(RpMaterial *material); RwTexture *RpMaterialGetTexture(const RpMaterial *material) { return material->texture; } RpMaterial *RpMaterialSetColor(RpMaterial *material, const RwRGBA *color) { material->color = *color; return material; } const RwRGBA *RpMaterialGetColor(const RpMaterial *material) { return &material->color; } RpMaterial *RpMaterialSetSurfaceProperties(RpMaterial *material, const RwSurfaceProperties *surfaceProperties); const RwSurfaceProperties *RpMaterialGetSurfaceProperties(const RpMaterial *material) { return &material->surfaceProps; } //RwInt32 RpMaterialRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); //RwInt32 RpMaterialRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); //RwInt32 RpMaterialSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); //RwInt32 RpMaterialGetPluginOffset(RwUInt32 pluginID); //RwBool RpMaterialValidatePlugins(const RpMaterial *material); //RwUInt32 RpMaterialStreamGetSize(const RpMaterial *material); //RpMaterial *RpMaterialStreamRead(RwStream *stream); //const RpMaterial *RpMaterialStreamWrite(const RpMaterial *material, RwStream *stream); //RpMaterialChunkInfo *_rpMaterialChunkInfoRead(RwStream *stream, RpMaterialChunkInfo *materialChunkInfo, RwInt32 *bytesRead); RwReal RpLightGetRadius(const RpLight *light) { return light->radius; } //const RwRGBAReal *RpLightGetColor(const RpLight *light); RpLight *RpLightSetFrame(RpLight *light, RwFrame *frame) { light->setFrame(frame); return light; } RwFrame *RpLightGetFrame(const RpLight *light) { return light->getFrame(); } //RpLightType RpLightGetType(const RpLight *light); RpLight *RpLightSetFlags(RpLight *light, RwUInt32 flags) { light->setFlags(flags); return light; } //RwUInt32 RpLightGetFlags(const RpLight *light); RpLight *RpLightCreate(RwInt32 type) { return rw::Light::create(type); } RwBool RpLightDestroy(RpLight *light) { light->destroy(); return true; } RpLight *RpLightSetRadius(RpLight *light, RwReal radius) { light->radius = radius; return light; } RpLight *RpLightSetColor(RpLight *light, const RwRGBAReal *color) { light->setColor(color->red, color->green, color->blue); return light; } //RwReal RpLightGetConeAngle(const RpLight *light); //RpLight *RpLightSetConeAngle(RpLight * ight, RwReal angle); //RwUInt32 RpLightStreamGetSize(const RpLight *light); //RpLight *RpLightStreamRead(RwStream *stream); //const RpLight *RpLightStreamWrite(const RpLight *light, RwStream *stream); //RpLightChunkInfo *_rpLightChunkInfoRead(RwStream *stream, RpLightChunkInfo *lightChunkInfo, RwInt32 *bytesRead); //RwInt32 RpLightRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); //RwInt32 RpLightRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); //RwInt32 RpLightSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); //RwInt32 RpLightGetPluginOffset(RwUInt32 pluginID); //RwBool RpLightValidatePlugins(const RpLight * light); RpGeometry *RpGeometryCreate(RwInt32 numVert, RwInt32 numTriangles, RwUInt32 format) { return Geometry::create(numVert, numTriangles, format); } RwBool RpGeometryDestroy(RpGeometry *geometry) { geometry->destroy(); return true; } RpGeometry *_rpGeometryAddRef(RpGeometry *geometry); RpGeometry *RpGeometryLock(RpGeometry *geometry, RwInt32 lockMode) { geometry->lock(lockMode); return geometry; } RpGeometry *RpGeometryUnlock(RpGeometry *geometry) { geometry->unlock(); return geometry; } RpGeometry *RpGeometryTransform(RpGeometry *geometry, const RwMatrix *matrix); RpGeometry *RpGeometryCreateSpace(RwReal radius); RpMorphTarget *RpMorphTargetSetBoundingSphere(RpMorphTarget *morphTarget, const RwSphere *boundingSphere) { morphTarget->boundingSphere = *boundingSphere; return morphTarget; } RwSphere *RpMorphTargetGetBoundingSphere(RpMorphTarget *morphTarget) { return &morphTarget->boundingSphere; } const RpMorphTarget *RpMorphTargetCalcBoundingSphere(const RpMorphTarget *morphTarget, RwSphere *boundingSphere) { *boundingSphere = morphTarget->calculateBoundingSphere(); return morphTarget; } RwInt32 RpGeometryAddMorphTargets(RpGeometry *geometry, RwInt32 mtcount) { RwInt32 n = geometry->numMorphTargets; geometry->addMorphTargets(mtcount); return n; } RwInt32 RpGeometryAddMorphTarget(RpGeometry *geometry) { return RpGeometryAddMorphTargets(geometry, 1); } RpGeometry *RpGeometryRemoveMorphTarget(RpGeometry *geometry, RwInt32 morphTarget); RwInt32 RpGeometryGetNumMorphTargets(const RpGeometry *geometry); RpMorphTarget *RpGeometryGetMorphTarget(const RpGeometry *geometry, RwInt32 morphTarget) { return &geometry->morphTargets[morphTarget]; } RwRGBA *RpGeometryGetPreLightColors(const RpGeometry *geometry) { return geometry->colors; } RwTexCoords *RpGeometryGetVertexTexCoords(const RpGeometry *geometry, RwTextureCoordinateIndex uvIndex) { if(uvIndex == rwNARWTEXTURECOORDINATEINDEX) return nil; return geometry->texCoords[uvIndex-rwTEXTURECOORDINATEINDEX0]; } RwInt32 RpGeometryGetNumTexCoordSets(const RpGeometry *geometry) { return geometry->numTexCoordSets; } RwInt32 RpGeometryGetNumVertices (const RpGeometry *geometry) { return geometry->numVertices; } RwV3d *RpMorphTargetGetVertices(const RpMorphTarget *morphTarget) { return morphTarget->vertices; } RwV3d *RpMorphTargetGetVertexNormals(const RpMorphTarget *morphTarget) { return morphTarget->normals; } RpTriangle *RpGeometryGetTriangles(const RpGeometry *geometry) { return geometry->triangles; } RwInt32 RpGeometryGetNumTriangles(const RpGeometry *geometry) { return geometry->numTriangles; } RpMaterial *RpGeometryGetMaterial(const RpGeometry *geometry, RwInt32 matNum) { return geometry->matList.materials[matNum]; } const RpGeometry *RpGeometryTriangleSetVertexIndices(const RpGeometry *geometry, RpTriangle *triangle, RwUInt16 vert1, RwUInt16 vert2, RwUInt16 vert3) { triangle->v[0] = vert1; triangle->v[1] = vert2; triangle->v[2] = vert3; return geometry; } RpGeometry *RpGeometryTriangleSetMaterial(RpGeometry *geometry, RpTriangle *triangle, RpMaterial *material) { int id = geometry->matList.findIndex(material); if(id < 0) id = geometry->matList.appendMaterial(material); if(id < 0) return nil; triangle->matId = id; return geometry; } const RpGeometry *RpGeometryTriangleGetVertexIndices(const RpGeometry *geometry, const RpTriangle *triangle, RwUInt16 *vert1, RwUInt16 *vert2, RwUInt16 *vert3); RpMaterial *RpGeometryTriangleGetMaterial(const RpGeometry *geometry, const RpTriangle *triangle); RwInt32 RpGeometryGetNumMaterials(const RpGeometry *geometry); RpGeometry *RpGeometryForAllMaterials(RpGeometry *geometry, RpMaterialCallBack fpCallBack, void *pData) { int i; for(i = 0; i < geometry->matList.numMaterials; i++) if(fpCallBack(geometry->matList.materials[i], pData) == nil) break; return geometry; } //const RpGeometry *RpGeometryForAllMeshes(const RpGeometry *geometry, RpMeshCallBack fpCallBack, void *pData); RwInt32 RpGeometryRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RpGeometryRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); RwInt32 RpGeometrySetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); RwInt32 RpGeometryGetPluginOffset(RwUInt32 pluginID); RwBool RpGeometryValidatePlugins(const RpGeometry *geometry); RwUInt32 RpGeometryStreamGetSize(const RpGeometry *geometry); const RpGeometry *RpGeometryStreamWrite(const RpGeometry *geometry, RwStream *stream); RpGeometry *RpGeometryStreamRead(RwStream *stream) { return Geometry::streamRead(stream); } //RpGeometryChunkInfo *_rpGeometryChunkInfoRead(RwStream *stream, RpGeometryChunkInfo *geometryChunkInfo, RwInt32 *bytesRead); RwUInt32 RpGeometryGetFlags(const RpGeometry *geometry) { return geometry->flags; } RpGeometry *RpGeometrySetFlags(RpGeometry *geometry, RwUInt32 flags) { geometry->flags = flags; return geometry; } const RwSurfaceProperties *_rpGeometryGetSurfaceProperties(const RpGeometry *geometry); RpGeometry *_rpGeometrySetSurfaceProperties(RpGeometry *geometry, const RwSurfaceProperties *surfaceProperties); RwFrame *RpClumpGetFrame(const RpClump * clump) { return clump->getFrame(); } RpClump *RpClumpSetFrame(RpClump * clump, RwFrame * frame) { clump->setFrame(frame); return clump; } RpClump *RpClumpForAllAtomics(RpClump * clump, RpAtomicCallBack callback, void *pData) { FORLIST(lnk, clump->atomics) if(callback(Atomic::fromClump(lnk), pData) == nil) break; return clump; } RpClump *RpClumpForAllLights(RpClump * clump, RpLightCallBack callback, void *pData); RpClump *RpClumpForAllCameras(RpClump * clump, RwCameraCallBack callback, void *pData); //RpClump *RpClumpCreateSpace(const RwV3d * position, RwReal radius); RpClump *RpClumpRender(RpClump * clump) { clump->render(); return clump; } RpClump *RpClumpRemoveAtomic(RpClump * clump, RpAtomic * atomic) { clump->removeAtomic(atomic); return clump; } RpClump *RpClumpAddAtomic(RpClump * clump, RpAtomic * atomic) { clump->addAtomic(atomic); return clump; } //RpClump *RpClumpRemoveLight(RpClump * clump, RpLight * light); //RpClump *RpClumpAddLight(RpClump * clump, RpLight * light); //RpClump *RpClumpRemoveCamera(RpClump * clump, RwCamera * camera); //RpClump *RpClumpAddCamera(RpClump * clump, RwCamera * camera); RwBool RpClumpDestroy(RpClump * clump) { clump->destroy(); return true; } RpClump *RpClumpCreate(void) { return rw::Clump::create(); } RpClump *RpClumpClone(RpClump * clump) { return clump->clone(); } //RpClump *RpClumpSetCallBack(RpClump * clump, RpClumpCallBack callback); //RpClumpCallBack RpClumpGetCallBack(const RpClump * clump); RwInt32 RpClumpGetNumAtomics(RpClump * clump) { return clump->countAtomics(); } //RwInt32 RpClumpGetNumLights(RpClump * clump); //RwInt32 RpClumpGetNumCameras(RpClump * clump); RpClump *RpClumpStreamRead(RwStream * stream) { return rw::Clump::streamRead(stream); } //RpClump *RpClumpStreamWrite(RpClump * clump, RwStream * stream); RwInt32 RpClumpRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB) { return Clump::registerPlugin(size, pluginID, constructCB, destructCB, (CopyConstructor)copyCB); } RwInt32 RpClumpRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB) { return Clump::registerPluginStream(pluginID, readCB, (StreamWrite)writeCB, (StreamGetSize)getSizeCB); } //RwInt32 RpClumpSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); //RwInt32 RpClumpGetPluginOffset(RwUInt32 pluginID); //RwBool RpClumpValidatePlugins(const RpClump * clump); RpAtomic *RpAtomicCreate(void) { return rw::Atomic::create(); } RwBool RpAtomicDestroy(RpAtomic * atomic) { atomic->destroy(); return true; } RpAtomic *RpAtomicClone(RpAtomic * atomic) { return atomic->clone(); } RpAtomic *RpAtomicSetFrame(RpAtomic * atomic, RwFrame * frame) { atomic->setFrame(frame); return atomic; } RpAtomic *RpAtomicSetGeometry(RpAtomic * atomic, RpGeometry * geometry, RwUInt32 flags) { atomic->setGeometry(geometry, flags); return atomic; } RwFrame *RpAtomicGetFrame(const RpAtomic * atomic) { return atomic->getFrame(); } RpAtomic *RpAtomicSetFlags(RpAtomic * atomic, RwUInt32 flags) { atomic->setFlags(flags); return atomic; } RwUInt32 RpAtomicGetFlags(const RpAtomic * atomic) { return atomic->getFlags(); } RwSphere *RpAtomicGetBoundingSphere(RpAtomic * atomic) { return &atomic->boundingSphere; } RpAtomic *RpAtomicRender(RpAtomic * atomic) { atomic->render(); return atomic; } RpClump *RpAtomicGetClump(const RpAtomic * atomic) { return atomic->clump; } //RpInterpolator *RpAtomicGetInterpolator(RpAtomic * atomic); RpGeometry *RpAtomicGetGeometry(const RpAtomic * atomic) { return atomic->geometry; } // WARNING: illegal cast void RpAtomicSetRenderCallBack(RpAtomic * atomic, RpAtomicCallBackRender callback) { atomic->setRenderCB((Atomic::RenderCB)callback); } RpAtomicCallBackRender RpAtomicGetRenderCallBack(const RpAtomic * atomic) { return (RpAtomicCallBackRender)atomic->renderCB; } //RwBool RpAtomicInstance(RpAtomic *atomic); //RwUInt32 RpAtomicStreamGetSize(RpAtomic * atomic); //RpAtomic *RpAtomicStreamRead(RwStream * stream); //RpAtomic *RpAtomicStreamWrite(RpAtomic * atomic, RwStream * stream); RwInt32 RpAtomicRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB) { return Atomic::registerPlugin(size, pluginID, constructCB, destructCB, (CopyConstructor)copyCB); } //RwInt32 RpAtomicRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); //RwInt32 RpAtomicSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); //RwInt32 RpAtomicSetStreamRightsCallBack(RwUInt32 pluginID, RwPluginDataChunkRightsCallBack rightsCB); //RwInt32 RpAtomicGetPluginOffset(RwUInt32 pluginID); //RwBool RpAtomicValidatePlugins(const RpAtomic * atomic); RpAtomic *AtomicDefaultRenderCallBack(RpAtomic * atomic) { Atomic::defaultRenderCB(atomic); return atomic; } // TODO: this is extremely simplified RpWorld *RpWorldCreate(RwBBox * boundingBox) { return World::create(); } RwBool RpWorldDestroy(RpWorld * world) { world->destroy(); return true; } RwBool RpWorldPluginAttach(void) { registerMeshPlugin(); registerNativeDataPlugin(); registerAtomicRightsPlugin(); registerMaterialRightsPlugin(); // not sure if this goes here rw::xbox::registerVertexFormatPlugin(); return true; } RpWorld *RpWorldRemoveCamera(RpWorld *world, RwCamera *camera) { world->removeCamera(camera); return world; } RpWorld *RpWorldAddCamera(RpWorld *world, RwCamera *camera) { world->addCamera(camera); return world; } RpWorld *RwCameraGetWorld(const RwCamera *camera); RpWorld *RpWorldRemoveAtomic(RpWorld *world, RpAtomic *atomic); RpWorld *RpWorldAddAtomic(RpWorld *world, RpAtomic *atomic); RpWorld *RpAtomicGetWorld(const RpAtomic *atomic); RpWorld *RpWorldAddClump(RpWorld *world, RpClump *clump); RpWorld *RpWorldRemoveClump(RpWorld *world, RpClump *clump); RpWorld *RpClumpGetWorld(const RpClump *clump); RpWorld *RpWorldAddLight(RpWorld *world, RpLight *light) { world->addLight(light); return world; } RpWorld *RpWorldRemoveLight(RpWorld *world, RpLight *light) { world->removeLight(light); return world; } RpWorld *RpLightGetWorld(const RpLight *light); RwCamera *RwCameraForAllClumpsInFrustum(RwCamera *camera, void *data); RwCamera *RwCameraForAllClumpsNotInFrustum(RwCamera *camera, RwInt32 numClumps, void *data); RwBool RpMatFXPluginAttach( void ) { registerMatFXPlugin(); return true; } RpAtomic *RpMatFXAtomicEnableEffects( RpAtomic *atomic ) { MatFX::enableEffects(atomic); return atomic; } RpMatFXMaterialFlags RpMatFXMaterialGetEffects( const RpMaterial *material ){ return (RpMatFXMaterialFlags)MatFX::getEffects(material); } RpMaterial *RpMatFXMaterialSetEffects( RpMaterial *material, RpMatFXMaterialFlags flags ) { MatFX::setEffects(material, (uint32)flags); return material; } RpMaterial *RpMatFXMaterialSetupEnvMap( RpMaterial *material, RwTexture *texture, RwFrame *frame, RwBool useFrameBufferAlpha, RwReal coef ) { MatFX *mfx = MatFX::get(material); mfx->setEnvTexture(texture); mfx->setEnvFrame(frame); mfx->setEnvCoefficient(coef); return material; } RpMaterial *RpMatFXMaterialSetEnvMapFrame( RpMaterial *material, RwFrame *frame ) { MatFX *mfx = MatFX::get(material); mfx->setEnvFrame(frame); return material; } RpMaterial *RpMatFXMaterialSetEnvMapFrameBufferAlpha( RpMaterial *material, RwBool useFrameBufferAlpha ) { MatFX *mfx = MatFX::get(material); mfx->setEnvFBAlpha(useFrameBufferAlpha); return material; } RpMaterial *RpMatFXMaterialSetEnvMapCoefficient( RpMaterial *material, RwReal coef ) { MatFX *mfx = MatFX::get(material); mfx->setEnvCoefficient(coef); return material; } RwReal RpMatFXMaterialGetEnvMapCoefficient( const RpMaterial *material ) { MatFX *mfx = MatFX::get(material); return mfx->getEnvCoefficient(); } RwBool RpHAnimPluginAttach(void) { registerHAnimPlugin(); return true; } RwInt32 RpHAnimFrameGetID(RwFrame *frame) { return HAnimData::get(frame)->id; } RwInt32 RpHAnimIDGetIndex(RpHAnimHierarchy *hierarchy, RwInt32 ID) { return hierarchy->getIndex(ID); } RwBool RpHAnimFrameSetHierarchy(RwFrame *frame, RpHAnimHierarchy *hierarchy) { HAnimData::get(frame)->hierarchy = hierarchy; return true; } RpHAnimHierarchy *RpHAnimFrameGetHierarchy(RwFrame *frame) { return HAnimHierarchy::get(frame); } RpHAnimHierarchy *RpHAnimHierarchySetFlags(RpHAnimHierarchy *hierarchy, RpHAnimHierarchyFlag flags) { hierarchy->flags = flags; return hierarchy; } RwBool RpHAnimHierarchySetCurrentAnim(RpHAnimHierarchy *hierarchy, RpHAnimAnimation *anim) { hierarchy->interpolator->setCurrentAnim(anim); return true; } RwBool RpHAnimHierarchyAddAnimTime(RpHAnimHierarchy *hierarchy, RwReal time) { hierarchy->interpolator->addTime(time); return true; } RwMatrix *RpHAnimHierarchyGetMatrixArray(RpHAnimHierarchy *hierarchy) { return hierarchy->matrices; } RwBool RpHAnimHierarchyUpdateMatrices(RpHAnimHierarchy *hierarchy) { hierarchy->updateMatrices(); return true; } RpHAnimAnimation *RpHAnimAnimationCreate(RwInt32 typeID, RwInt32 numFrames, RwInt32 flags, RwReal duration) { return Animation::create(AnimInterpolatorInfo::find(typeID), numFrames, flags, duration); } RpHAnimAnimation *RpHAnimAnimationDestroy(RpHAnimAnimation *animation) { animation->destroy(); return animation; } RpHAnimAnimation *RpHAnimAnimationStreamRead(RwStream *stream) { return Animation::streamRead(stream); } RwBool RpSkinPluginAttach(void) { registerSkinPlugin(); return true; } RwUInt32 RpSkinGetNumBones( RpSkin *skin ) { return skin->numBones; } const RwMatrixWeights *RpSkinGetVertexBoneWeights( RpSkin *skin ) { return (RwMatrixWeights*)skin->weights; } const RwUInt32 *RpSkinGetVertexBoneIndices( RpSkin *skin ) { return (RwUInt32*)skin->indices; } const RwMatrix *RpSkinGetSkinToBoneMatrices( RpSkin *skin ) { return (const RwMatrix*)skin->inverseMatrices; } RpSkin *RpSkinGeometryGetSkin( RpGeometry *geometry ) { return Skin::get(geometry); } RpAtomic *RpSkinAtomicSetHAnimHierarchy( RpAtomic *atomic, RpHAnimHierarchy *hierarchy ) { Skin::setHierarchy(atomic, hierarchy); return atomic; } RpHAnimHierarchy *RpSkinAtomicGetHAnimHierarchy( const RpAtomic *atomic ) { return Skin::getHierarchy(atomic); } RwImage * RtBMPImageWrite(RwImage *image, const RwChar *imageName) { #ifndef _WIN32 char *r = casepath(imageName); if (r) { rw::writeBMP(image, r); free(r); } else { rw::writeBMP(image, imageName); } #else rw::writeBMP(image, imageName); #endif return image; } RwImage * RtBMPImageRead(const RwChar *imageName) { #ifndef _WIN32 RwImage *image; char *r = casepath(imageName); if (r) { image = rw::readBMP(r); free(r); } else { image = rw::readBMP(imageName); } return image; #else return rw::readBMP(imageName); #endif } RwImage * RtPNGImageWrite(RwImage *image, const RwChar *imageName) { #ifndef _WIN32 char *r = casepath(imageName); if (r) { rw::writePNG(image, r); free(r); } else { rw::writePNG(image, imageName); } #else rw::writePNG(image, imageName); #endif return image; } RwImage * RtPNGImageRead(const RwChar *imageName) { #ifndef _WIN32 RwImage *image; char *r = casepath(imageName); if (r) { image = rw::readPNG(r); free(r); } else { image = rw::readPNG(imageName); } return image; #else return rw::readPNG(imageName); #endif } #include "rtquat.h" RtQuat *RtQuatRotate(RtQuat * quat, const RwV3d * axis, RwReal angle, RwOpCombineType combineOp) { return (RtQuat*)((rw::Quat*)quat)->rotate(axis, angle/180.0f*3.14159f, (CombineOp)combineOp); } void RtQuatConvertToMatrix(const RtQuat * const qpQuat, RwMatrix * const mpMatrix) { mpMatrix->rotate(*(rw::Quat*)qpQuat, COMBINEREPLACE); } #include "rtcharse.h" RwBool RtCharsetOpen(void) { return Charset::open(); } void RtCharsetClose(void) { return Charset::close(); } RtCharset *RtCharsetPrint(RtCharset * charSet, const RwChar * string, RwInt32 x, RwInt32 y) { charSet->print(string, x, y, true); return charSet; } RtCharset *RtCharsetPrintBuffered(RtCharset * charSet, const RwChar * string, RwInt32 x, RwInt32 y, RwBool hideSpaces) { charSet->printBuffered(string, x, y, hideSpaces); return charSet; } RwBool RtCharsetBufferFlush(void) { Charset::flushBuffer(); return true; } RtCharset *RtCharsetSetColors(RtCharset * charSet, const RwRGBA * foreGround, const RwRGBA * backGround) { return charSet->setColors(foreGround, backGround); } RtCharset *RtCharsetGetDesc(RtCharset * charset, RtCharsetDesc * desc) { *desc = charset->desc; return charset; } RtCharset *RtCharsetCreate(const RwRGBA * foreGround, const RwRGBA * backGround) { return Charset::create(foreGround, backGround); } RwBool RtCharsetDestroy(RtCharset * charSet) { charSet->destroy(); return true; } #include RwInt8 RpAnisotGetMaxSupportedMaxAnisotropy(void) { return rw::getMaxSupportedMaxAnisotropy(); } RwTexture *RpAnisotTextureSetMaxAnisotropy(RwTexture *tex, RwInt8 val) { tex->setMaxAnisotropy(val); return tex; } RwInt8 RpAnisotTextureGetMaxAnisotropy(RwTexture *tex) { return tex->getMaxAnisotropy(); } RwBool RpAnisotPluginAttach(void) { rw::registerAnisotropyPlugin(); return true; } ================================================ FILE: src/fakerw/rpanisot.h ================================================ #pragma once RwInt8 RpAnisotGetMaxSupportedMaxAnisotropy(void); RwTexture *RpAnisotTextureSetMaxAnisotropy(RwTexture *tex, RwInt8 val); RwInt8 RpAnisotTextureGetMaxAnisotropy(RwTexture *tex); RwBool RpAnisotPluginAttach(void); ================================================ FILE: src/fakerw/rphanim.h ================================================ #pragma once #include "rtquat.h" //struct RpHAnimHierarchy; typedef rw::HAnimHierarchy RpHAnimHierarchy; //struct RpHAnimAnimation; typedef rw::Animation RpHAnimAnimation; #define rpHANIMSTDKEYFRAMETYPEID 0x1 // same as rw::HAnimKeyFrame, but we need RtQuat in this one struct RpHAnimStdKeyFrame { RpHAnimStdKeyFrame *prevFrame; RwReal time; RtQuat q; RwV3d t; }; // same story, this one only exists in later RW versions // but we need it for 64 bit builds because offset and size differs! struct RpHAnimStdInterpFrame { RpHAnimStdKeyFrame *keyFrame1; RpHAnimStdKeyFrame *keyFrame2; RtQuat q; RwV3d t; }; enum RpHAnimHierarchyFlag { rpHANIMHIERARCHYSUBHIERARCHY = rw::HAnimHierarchy::SUBHIERARCHY, rpHANIMHIERARCHYNOMATRICES = rw::HAnimHierarchy::NOMATRICES, rpHANIMHIERARCHYUPDATEMODELLINGMATRICES = rw::HAnimHierarchy::UPDATEMODELLINGMATRICES, rpHANIMHIERARCHYUPDATELTMS = rw::HAnimHierarchy::UPDATELTMS, rpHANIMHIERARCHYLOCALSPACEMATRICES = rw::HAnimHierarchy::LOCALSPACEMATRICES }; #define rpHANIMPOPPARENTMATRIX rw::HAnimHierarchy::POP #define rpHANIMPUSHPARENTMATRIX rw::HAnimHierarchy::PUSH RwBool RpHAnimPluginAttach(void); RwBool RpHAnimFrameSetID(RwFrame *frame, RwInt32 id); RwInt32 RpHAnimFrameGetID(RwFrame *frame); RwInt32 RpHAnimIDGetIndex(RpHAnimHierarchy *hierarchy, RwInt32 ID); RwBool RpHAnimFrameSetHierarchy(RwFrame *frame, RpHAnimHierarchy *hierarchy); RpHAnimHierarchy *RpHAnimFrameGetHierarchy(RwFrame *frame); RpHAnimHierarchy *RpHAnimHierarchySetFlags(RpHAnimHierarchy *hierarchy, RpHAnimHierarchyFlag flags); RpHAnimHierarchyFlag RpHAnimHierarchyGetFlags(RpHAnimHierarchy *hierarchy); RwBool RpHAnimHierarchySetCurrentAnim(RpHAnimHierarchy *hierarchy, RpHAnimAnimation *anim); RwBool RpHAnimHierarchySetCurrentAnimTime(RpHAnimHierarchy *hierarchy, RwReal time); RwBool RpHAnimHierarchySubAnimTime(RpHAnimHierarchy *hierarchy, RwReal time); RwBool RpHAnimHierarchyAddAnimTime(RpHAnimHierarchy *hierarchy, RwReal time); RwMatrix *RpHAnimHierarchyGetMatrixArray(RpHAnimHierarchy *hierarchy); RwBool RpHAnimHierarchyUpdateMatrices(RpHAnimHierarchy *hierarchy); #define rpHANIMHIERARCHYGETINTERPFRAME( hierarchy, nodeIndex ) \ ( (void *)( ( (RwUInt8 *)&(hierarchy->interpolator[1]) + \ ((nodeIndex) * \ hierarchy->interpolator->currentInterpKeyFrameSize) ) ) ) RpHAnimAnimation *RpHAnimAnimationCreate(RwInt32 typeID, RwInt32 numFrames, RwInt32 flags, RwReal duration); RpHAnimAnimation *RpHAnimAnimationDestroy(RpHAnimAnimation *animation); RpHAnimAnimation *RpHAnimAnimationStreamRead(RwStream *stream); ================================================ FILE: src/fakerw/rpmatfx.h ================================================ #pragma once enum RpMatFXMaterialFlags { rpMATFXEFFECTNULL = rw::MatFX::NOTHING, rpMATFXEFFECTBUMPMAP = rw::MatFX::BUMPMAP, rpMATFXEFFECTENVMAP = rw::MatFX::ENVMAP, rpMATFXEFFECTBUMPENVMAP = rw::MatFX::BUMPENVMAP, rpMATFXEFFECTDUAL = rw::MatFX::DUAL, rpMATFXEFFECTMAX, rpMATFXNUMEFFECTS = rpMATFXEFFECTMAX - 1, }; RwBool RpMatFXPluginAttach( void ); RpAtomic *RpMatFXAtomicEnableEffects( RpAtomic *atomic ); RwBool RpMatFXAtomicQueryEffects( RpAtomic *atomic ); //RpWorldSector *RpMatFXWorldSectorEnableEffects( RpWorldSector *worldSector ); //RwBool RpMatFXWorldSectorQueryEffects( RpWorldSector *worldSector ); RpMaterial *RpMatFXMaterialSetEffects( RpMaterial *material, RpMatFXMaterialFlags flags ); RpMaterial *RpMatFXMaterialSetupBumpMap( RpMaterial *material, RwTexture *texture, RwFrame *frame, RwReal coef ); RpMaterial *RpMatFXMaterialSetupEnvMap( RpMaterial *material, RwTexture *texture, RwFrame *frame, RwBool useFrameBufferAlpha, RwReal coef ); RpMaterial *RpMatFXMaterialSetupDualTexture( RpMaterial *material, RwTexture *texture, RwBlendFunction srcBlendMode, RwBlendFunction dstBlendMode ); RpMatFXMaterialFlags RpMatFXMaterialGetEffects( const RpMaterial *material ); RpMaterial *RpMatFXMaterialSetBumpMapTexture( RpMaterial *material, RwTexture *texture ); RpMaterial *RpMatFXMaterialSetBumpMapFrame( RpMaterial *material, RwFrame *frame ); RpMaterial *RpMatFXMaterialSetBumpMapCoefficient( RpMaterial *material, RwReal coef ); RwTexture *RpMatFXMaterialGetBumpMapTexture( const RpMaterial *material ); RwTexture *RpMatFXMaterialGetBumpMapBumpedTexture( const RpMaterial *material ); RwFrame *RpMatFXMaterialGetBumpMapFrame( const RpMaterial *material ); RwReal RpMatFXMaterialGetBumpMapCoefficient( const RpMaterial *material ); RpMaterial *RpMatFXMaterialSetEnvMapTexture( RpMaterial *material, RwTexture *texture ); RpMaterial *RpMatFXMaterialSetEnvMapFrame( RpMaterial *material, RwFrame *frame ); RpMaterial *RpMatFXMaterialSetEnvMapFrameBufferAlpha( RpMaterial *material, RwBool useFrameBufferAlpha ); RpMaterial *RpMatFXMaterialSetEnvMapCoefficient( RpMaterial *material, RwReal coef ); RwTexture *RpMatFXMaterialGetEnvMapTexture( const RpMaterial *material ); RwFrame *RpMatFXMaterialGetEnvMapFrame( const RpMaterial *material ); RwBool RpMatFXMaterialGetEnvMapFrameBufferAlpha( const RpMaterial *material ); RwReal RpMatFXMaterialGetEnvMapCoefficient( const RpMaterial *material ); RpMaterial *RpMatFXMaterialSetDualTexture( RpMaterial *material, RwTexture *texture ); RpMaterial *RpMatFXMaterialSetDualBlendModes( RpMaterial *material, RwBlendFunction srcBlendMode, RwBlendFunction dstBlendMode ); RwTexture *RpMatFXMaterialGetDualTexture( const RpMaterial *material ); const RpMaterial *RpMatFXMaterialGetDualBlendModes( const RpMaterial *material, RwBlendFunction *srcBlendMode, RwBlendFunction *dstBlendMode ); ================================================ FILE: src/fakerw/rpskin.h ================================================ #pragma once #include //struct RpSkin; typedef rw::Skin RpSkin; struct RwMatrixWeights { RwReal w0; RwReal w1; RwReal w2; RwReal w3; }; RwBool RpSkinPluginAttach(void); RwUInt32 RpSkinGetNumBones( RpSkin *skin ); const RwMatrixWeights *RpSkinGetVertexBoneWeights( RpSkin *skin ); const RwUInt32 *RpSkinGetVertexBoneIndices( RpSkin *skin ); const RwMatrix *RpSkinGetSkinToBoneMatrices( RpSkin *skin ); RpSkin *RpSkinGeometryGetSkin( RpGeometry *geometry ); RpAtomic *RpSkinAtomicSetHAnimHierarchy( RpAtomic *atomic, RpHAnimHierarchy *hierarchy ); RpHAnimHierarchy *RpSkinAtomicGetHAnimHierarchy( const RpAtomic *atomic ); ================================================ FILE: src/fakerw/rpworld.h ================================================ #pragma once #define rpATOMIC rw::Atomic::ID #define rpCLUMP rw::Clump::ID /* *********************************************** * * RpMaterial * *********************************************** */ //struct RpMaterial; typedef rw::Material RpMaterial; typedef RpMaterial *(*RpMaterialCallBack)(RpMaterial *material, void *data); RpMaterial *RpMaterialCreate(void); RwBool RpMaterialDestroy(RpMaterial *material); RpMaterial *RpMaterialClone(RpMaterial *material); RpMaterial *RpMaterialSetTexture(RpMaterial *material, RwTexture *texture); RpMaterial *RpMaterialAddRef(RpMaterial *material); RwTexture *RpMaterialGetTexture(const RpMaterial *material); RpMaterial *RpMaterialSetColor(RpMaterial *material, const RwRGBA *color); const RwRGBA *RpMaterialGetColor(const RpMaterial *material); RpMaterial *RpMaterialSetSurfaceProperties(RpMaterial *material, const RwSurfaceProperties *surfaceProperties); const RwSurfaceProperties *RpMaterialGetSurfaceProperties(const RpMaterial *material); RwInt32 RpMaterialRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RpMaterialRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); RwInt32 RpMaterialSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); RwInt32 RpMaterialGetPluginOffset(RwUInt32 pluginID); RwBool RpMaterialValidatePlugins(const RpMaterial *material); RwUInt32 RpMaterialStreamGetSize(const RpMaterial *material); RpMaterial *RpMaterialStreamRead(RwStream *stream); const RpMaterial *RpMaterialStreamWrite(const RpMaterial *material, RwStream *stream); //RpMaterialChunkInfo *_rpMaterialChunkInfoRead(RwStream *stream, RpMaterialChunkInfo *materialChunkInfo, RwInt32 *bytesRead); /* *********************************************** * * RpLight * *********************************************** */ //struct RpLight; typedef rw::Light RpLight; enum RpLightType { rpNALIGHTTYPE = 0, rpLIGHTDIRECTIONAL, rpLIGHTAMBIENT, rpLIGHTPOINT = 0x80, rpLIGHTSPOT, rpLIGHTSPOTSOFT, }; enum RpLightFlag { rpLIGHTLIGHTATOMICS = 0x01, rpLIGHTLIGHTWORLD = 0x02, }; typedef RpLight *(*RpLightCallBack) (RpLight * light, void *data); RwReal RpLightGetRadius(const RpLight *light); const RwRGBAReal *RpLightGetColor(const RpLight *light); RpLight *RpLightSetFrame(RpLight *light, RwFrame *frame); RwFrame *RpLightGetFrame(const RpLight *light); RpLightType RpLightGetType(const RpLight *light); RpLight *RpLightSetFlags(RpLight *light, RwUInt32 flags); RwUInt32 RpLightGetFlags(const RpLight *light); RpLight *RpLightCreate(RwInt32 type); RwBool RpLightDestroy(RpLight *light); RpLight *RpLightSetRadius(RpLight *light, RwReal radius); RpLight *RpLightSetColor(RpLight *light, const RwRGBAReal *color); RwReal RpLightGetConeAngle(const RpLight *light); RpLight *RpLightSetConeAngle(RpLight * ight, RwReal angle); RwUInt32 RpLightStreamGetSize(const RpLight *light); RpLight *RpLightStreamRead(RwStream *stream); const RpLight *RpLightStreamWrite(const RpLight *light, RwStream *stream); //RpLightChunkInfo *_rpLightChunkInfoRead(RwStream *stream, RpLightChunkInfo *lightChunkInfo, RwInt32 *bytesRead); RwInt32 RpLightRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RpLightRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); RwInt32 RpLightSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); RwInt32 RpLightGetPluginOffset(RwUInt32 pluginID); RwBool RpLightValidatePlugins(const RpLight * light); /* *********************************************** * * RpGeometry * *********************************************** */ typedef rw::Triangle RpTriangle; //struct RpGeometry; typedef rw::Geometry RpGeometry; //struct RpMorphTarget; typedef rw::MorphTarget RpMorphTarget; enum RpGeometryFlag { rpGEOMETRYTRISTRIP = rw::Geometry::TRISTRIP, rpGEOMETRYPOSITIONS = rw::Geometry::POSITIONS, rpGEOMETRYTEXTURED = rw::Geometry::TEXTURED, rpGEOMETRYPRELIT = rw::Geometry::PRELIT, rpGEOMETRYNORMALS = rw::Geometry::NORMALS, rpGEOMETRYLIGHT = rw::Geometry::LIGHT, rpGEOMETRYMODULATEMATERIALCOLOR = rw::Geometry::MODULATE, rpGEOMETRYTEXTURED2 = rw::Geometry::TEXTURED2, rpGEOMETRYNATIVE = rw::Geometry::NATIVE, rpGEOMETRYNATIVEINSTANCE = rw::Geometry::NATIVEINSTANCE, rpGEOMETRYFLAGSMASK = 0x000000FF, rpGEOMETRYNATIVEFLAGSMASK = 0x0F000000, }; enum RpGeometryLockMode { rpGEOMETRYLOCKPOLYGONS = rw::Geometry::LOCKPOLYGONS, rpGEOMETRYLOCKVERTICES = rw::Geometry::LOCKVERTICES, rpGEOMETRYLOCKNORMALS = rw::Geometry::LOCKNORMALS, rpGEOMETRYLOCKPRELIGHT = rw::Geometry::LOCKPRELIGHT, rpGEOMETRYLOCKTEXCOORDS = rw::Geometry::LOCKTEXCOORDS, rpGEOMETRYLOCKTEXCOORDS1 = rw::Geometry::LOCKTEXCOORDS1, rpGEOMETRYLOCKTEXCOORDS2 = rw::Geometry::LOCKTEXCOORDS2, rpGEOMETRYLOCKTEXCOORDS3 = rw::Geometry::LOCKTEXCOORDS3, rpGEOMETRYLOCKTEXCOORDS4 = rw::Geometry::LOCKTEXCOORDS4, rpGEOMETRYLOCKTEXCOORDS5 = rw::Geometry::LOCKTEXCOORDS5, rpGEOMETRYLOCKTEXCOORDS6 = rw::Geometry::LOCKTEXCOORDS6, rpGEOMETRYLOCKTEXCOORDS7 = rw::Geometry::LOCKTEXCOORDS7, rpGEOMETRYLOCKTEXCOORDS8 = rw::Geometry::LOCKTEXCOORDS8, rpGEOMETRYLOCKTEXCOORDSALL = rw::Geometry::LOCKTEXCOORDSALL, rpGEOMETRYLOCKALL = rw::Geometry::LOCKALL }; RpGeometry *RpGeometryCreate(RwInt32 numVert, RwInt32 numTriangles, RwUInt32 format); RwBool RpGeometryDestroy(RpGeometry *geometry); RpGeometry *_rpGeometryAddRef(RpGeometry *geometry); RpGeometry *RpGeometryLock(RpGeometry *geometry, RwInt32 lockMode); RpGeometry *RpGeometryUnlock(RpGeometry *geometry); RpGeometry *RpGeometryTransform(RpGeometry *geometry, const RwMatrix *matrix); RpGeometry *RpGeometryCreateSpace(RwReal radius); RpMorphTarget *RpMorphTargetSetBoundingSphere(RpMorphTarget *morphTarget, const RwSphere *boundingSphere); RwSphere *RpMorphTargetGetBoundingSphere(RpMorphTarget *morphTarget); const RpMorphTarget *RpMorphTargetCalcBoundingSphere(const RpMorphTarget *morphTarget, RwSphere *boundingSphere); RwInt32 RpGeometryAddMorphTargets(RpGeometry *geometry, RwInt32 mtcount); RwInt32 RpGeometryAddMorphTarget(RpGeometry *geometry); RpGeometry *RpGeometryRemoveMorphTarget(RpGeometry *geometry, RwInt32 morphTarget); RwInt32 RpGeometryGetNumMorphTargets(const RpGeometry *geometry); RpMorphTarget *RpGeometryGetMorphTarget(const RpGeometry *geometry, RwInt32 morphTarget); RwRGBA *RpGeometryGetPreLightColors(const RpGeometry *geometry); RwTexCoords *RpGeometryGetVertexTexCoords(const RpGeometry *geometry, RwTextureCoordinateIndex uvIndex); RwInt32 RpGeometryGetNumTexCoordSets(const RpGeometry *geometry); RwInt32 RpGeometryGetNumVertices (const RpGeometry *geometry); RwV3d *RpMorphTargetGetVertices(const RpMorphTarget *morphTarget); RwV3d *RpMorphTargetGetVertexNormals(const RpMorphTarget *morphTarget); RpTriangle *RpGeometryGetTriangles(const RpGeometry *geometry); RwInt32 RpGeometryGetNumTriangles(const RpGeometry *geometry); RpMaterial *RpGeometryGetMaterial(const RpGeometry *geometry, RwInt32 matNum); const RpGeometry *RpGeometryTriangleSetVertexIndices(const RpGeometry *geometry, RpTriangle *triangle, RwUInt16 vert1, RwUInt16 vert2, RwUInt16 vert3); RpGeometry *RpGeometryTriangleSetMaterial(RpGeometry *geometry, RpTriangle *triangle, RpMaterial *material); const RpGeometry *RpGeometryTriangleGetVertexIndices(const RpGeometry *geometry, const RpTriangle *triangle, RwUInt16 *vert1, RwUInt16 *vert2, RwUInt16 *vert3); RpMaterial *RpGeometryTriangleGetMaterial(const RpGeometry *geometry, const RpTriangle *triangle); RwInt32 RpGeometryGetNumMaterials(const RpGeometry *geometry); RpGeometry *RpGeometryForAllMaterials(RpGeometry *geometry, RpMaterialCallBack fpCallBack, void *pData); //const RpGeometry *RpGeometryForAllMeshes(const RpGeometry *geometry, RpMeshCallBack fpCallBack, void *pData); RwInt32 RpGeometryRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RpGeometryRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); RwInt32 RpGeometrySetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); RwInt32 RpGeometryGetPluginOffset(RwUInt32 pluginID); RwBool RpGeometryValidatePlugins(const RpGeometry *geometry); RwUInt32 RpGeometryStreamGetSize(const RpGeometry *geometry); const RpGeometry *RpGeometryStreamWrite(const RpGeometry *geometry, RwStream *stream); RpGeometry *RpGeometryStreamRead(RwStream *stream); //RpGeometryChunkInfo *_rpGeometryChunkInfoRead(RwStream *stream, RpGeometryChunkInfo *geometryChunkInfo, RwInt32 *bytesRead); RwUInt32 RpGeometryGetFlags(const RpGeometry *geometry); RpGeometry *RpGeometrySetFlags(RpGeometry *geometry, RwUInt32 flags); const RwSurfaceProperties *_rpGeometryGetSurfaceProperties(const RpGeometry *geometry); RpGeometry *_rpGeometrySetSurfaceProperties(RpGeometry *geometry, const RwSurfaceProperties *surfaceProperties); /* *********************************************** * * RpAtomic and RpClump * *********************************************** */ //struct RpAtomic; typedef rw::Atomic RpAtomic; enum RpAtomicFlag { rpATOMICCOLLISIONTEST = 0x01, rpATOMICRENDER = 0x04, }; enum RpAtomicSetGeomFlag { rpATOMICSAMEBOUNDINGSPHERE = 0x01, }; typedef RpAtomic *(*RpAtomicCallBack) (RpAtomic * atomic, void *data); typedef RpAtomic *(*RpAtomicCallBackRender) (RpAtomic * atomic); //struct RpClump; typedef rw::Clump RpClump; struct RpClumpChunkInfo { RwInt32 numAtomics; RwInt32 numLights; RwInt32 numCameras; }; typedef RpClump *(*RpClumpCallBack) (RpClump * clump, void *data); RpAtomic *AtomicDefaultRenderCallBack(RpAtomic * atomic); //void _rpAtomicResyncInterpolatedSphere(RpAtomic * atomic); //const RwSphere *RpAtomicGetWorldBoundingSphere(RpAtomic * atomic); RwFrame *RpClumpGetFrame(const RpClump * clump); RpClump *RpClumpSetFrame(RpClump * clump, RwFrame * frame); RpClump *RpClumpForAllAtomics(RpClump * clump, RpAtomicCallBack callback, void *pData); RpClump *RpClumpForAllLights(RpClump * clump, RpLightCallBack callback, void *pData); RpClump *RpClumpForAllCameras(RpClump * clump, RwCameraCallBack callback, void *pData); RpClump *RpClumpCreateSpace(const RwV3d * position, RwReal radius); RpClump *RpClumpRender(RpClump * clump); RpClump *RpClumpRemoveAtomic(RpClump * clump, RpAtomic * atomic); RpClump *RpClumpAddAtomic(RpClump * clump, RpAtomic * atomic); RpClump *RpClumpRemoveLight(RpClump * clump, RpLight * light); RpClump *RpClumpAddLight(RpClump * clump, RpLight * light); RpClump *RpClumpRemoveCamera(RpClump * clump, RwCamera * camera); RpClump *RpClumpAddCamera(RpClump * clump, RwCamera * camera); RwBool RpClumpDestroy(RpClump * clump); RpClump *RpClumpCreate(void); RpClump *RpClumpClone(RpClump * clump); RpClump *RpClumpSetCallBack(RpClump * clump, RpClumpCallBack callback); RpClumpCallBack RpClumpGetCallBack(const RpClump * clump); RwInt32 RpClumpGetNumAtomics(RpClump * clump); RwInt32 RpClumpGetNumLights(RpClump * clump); RwInt32 RpClumpGetNumCameras(RpClump * clump); RwUInt32 RpClumpStreamGetSize(RpClump * clump); RpClump *RpClumpStreamRead(RwStream * stream); RpClump *RpClumpStreamWrite(RpClump * clump, RwStream * stream); RwInt32 RpClumpRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RpClumpRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); RwInt32 RpClumpSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); RwInt32 RpClumpGetPluginOffset(RwUInt32 pluginID); RwBool RpClumpValidatePlugins(const RpClump * clump); RpAtomic *RpAtomicCreate(void); RwBool RpAtomicDestroy(RpAtomic * atomic); RpAtomic *RpAtomicClone(RpAtomic * atomic); RpAtomic *RpAtomicSetFrame(RpAtomic * atomic, RwFrame * frame); RpAtomic *RpAtomicSetGeometry(RpAtomic * atomic, RpGeometry * geometry, RwUInt32 flags); RwFrame *RpAtomicGetFrame(const RpAtomic * atomic); RpAtomic *RpAtomicSetFlags(RpAtomic * atomic, RwUInt32 flags); RwUInt32 RpAtomicGetFlags(const RpAtomic * atomic); RwSphere *RpAtomicGetBoundingSphere(RpAtomic * atomic); RpAtomic *RpAtomicRender(RpAtomic * atomic); RpClump *RpAtomicGetClump(const RpAtomic * atomic); //RpInterpolator *RpAtomicGetInterpolator(RpAtomic * atomic); RpGeometry *RpAtomicGetGeometry(const RpAtomic * atomic); void RpAtomicSetRenderCallBack(RpAtomic * atomic, RpAtomicCallBackRender callback); RpAtomicCallBackRender RpAtomicGetRenderCallBack(const RpAtomic * atomic); RwBool RpAtomicInstance(RpAtomic *atomic); RwUInt32 RpAtomicStreamGetSize(RpAtomic * atomic); RpAtomic *RpAtomicStreamRead(RwStream * stream); RpAtomic *RpAtomicStreamWrite(RpAtomic * atomic, RwStream * stream); RwInt32 RpAtomicRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RpAtomicRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); RwInt32 RpAtomicSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); RwInt32 RpAtomicSetStreamRightsCallBack(RwUInt32 pluginID, RwPluginDataChunkRightsCallBack rightsCB); RwInt32 RpAtomicGetPluginOffset(RwUInt32 pluginID); RwBool RpAtomicValidatePlugins(const RpAtomic * atomic); //RwInt32 RpInterpolatorGetEndMorphTarget(const RpInterpolator * interpolator); //RwInt32 RpInterpolatorGetStartMorphTarget(const RpInterpolator * interpolator); //RwReal RpInterpolatorGetValue(const RpInterpolator * interpolator); //RwReal RpInterpolatorGetScale(const RpInterpolator * interpolator); //RpInterpolator *RpInterpolatorSetEndMorphTarget(RpInterpolator * interpolator, RwInt32 morphTarget, RpAtomic * atomic); //RpInterpolator *RpInterpolatorSetStartMorphTarget(RpInterpolator * interpolator, RwInt32 morphTarget, RpAtomic * atomic); //RpInterpolator *RpInterpolatorSetValue(RpInterpolator * interpolator, RwReal value, RpAtomic *atomic); //RpInterpolator *RpInterpolatorSetScale(RpInterpolator * interpolator, RwReal scale, RpAtomic *atomic); RpClump *RpLightGetClump(const RpLight *light); RpClump *RwCameraGetClump(const RwCamera *camera); /* *********************************************** * * RpWorld * *********************************************** */ //struct RpWorld; typedef rw::World RpWorld; RwBool RpWorldDestroy(RpWorld * world); RpWorld *RpWorldCreate(RwBBox * boundingBox); RwBool RpWorldPluginAttach(void); RpWorld *RpWorldRemoveCamera(RpWorld *world, RwCamera *camera); RpWorld *RpWorldAddCamera(RpWorld *world, RwCamera *camera); RpWorld *RwCameraGetWorld(const RwCamera *camera); RpWorld *RpWorldRemoveAtomic(RpWorld *world, RpAtomic *atomic); RpWorld *RpWorldAddAtomic(RpWorld *world, RpAtomic *atomic); RpWorld *RpAtomicGetWorld(const RpAtomic *atomic); RpWorld *RpWorldAddClump(RpWorld *world, RpClump *clump); RpWorld *RpWorldRemoveClump(RpWorld *world, RpClump *clump); RpWorld *RpClumpGetWorld(const RpClump *clump); RpWorld *RpWorldAddLight(RpWorld *world, RpLight *light); RpWorld *RpWorldRemoveLight(RpWorld *world, RpLight *light); RpWorld *RpLightGetWorld(const RpLight *light); RwCamera *RwCameraForAllClumpsInFrustum(RwCamera *camera, void *data); RwCamera *RwCameraForAllClumpsNotInFrustum(RwCamera *camera, RwInt32 numClumps, void *data); //RwCamera *RwCameraForAllSectorsInFrustum(RwCamera *camera, RpWorldSectorCallBack callBack, void *pData); //RpLight *RpLightForAllWorldSectors(RpLight *light, RpWorldSectorCallBack callback, void *data); //RpAtomic *RpAtomicForAllWorldSectors(RpAtomic *atomic, RpWorldSectorCallBack callback, void *data); //RpWorldSector *RpWorldSectorForAllAtomics(RpWorldSector *sector, RpAtomicCallBack callback, void *data); //RpWorldSector *RpWorldSectorForAllCollisionAtomics(RpWorldSector *sector, RpAtomicCallBack callback, void *data); //RpWorldSector *RpWorldSectorForAllLights(RpWorldSector *sector, RpLightCallBack callback, void *data); ================================================ FILE: src/fakerw/rtbmp.h ================================================ #pragma once RwImage *RtBMPImageWrite(RwImage * image, const RwChar * imageName); RwImage *RtBMPImageRead(const RwChar * imageName); ================================================ FILE: src/fakerw/rtcharse.h ================================================ #pragma once typedef rw::Charset RtCharset; typedef rw::Charset::Desc RtCharsetDesc; RwBool RtCharsetOpen(void); void RtCharsetClose(void); RtCharset *RtCharsetPrint(RtCharset * charSet, const RwChar * string, RwInt32 x, RwInt32 y); RtCharset *RtCharsetPrintBuffered(RtCharset * charSet, const RwChar * string, RwInt32 x, RwInt32 y, RwBool hideSpaces); RwBool RtCharsetBufferFlush(void); RtCharset *RtCharsetSetColors(RtCharset * charSet, const RwRGBA * foreGround, const RwRGBA * backGround); RtCharset *RtCharsetGetDesc(RtCharset * charset, RtCharsetDesc * desc); RtCharset *RtCharsetCreate(const RwRGBA * foreGround, const RwRGBA * backGround); RwBool RtCharsetDestroy(RtCharset * charSet); ================================================ FILE: src/fakerw/rtpng.h ================================================ #pragma once RwImage *RtPNGImageWrite(RwImage * image, const RwChar * imageName); RwImage *RtPNGImageRead(const RwChar * imageName); ================================================ FILE: src/fakerw/rtquat.h ================================================ #pragma once // Same layout as rw::Quat but with ugly imag,real separation which i don't want in librw struct RtQuat { rw::V3d imag; rw::float32 real; }; RwBool RtQuatConvertFromMatrix(RtQuat * const qpQuat, const RwMatrix * const mpMatrix); RtQuat *RtQuatRotate(RtQuat * quat, const RwV3d * axis, RwReal angle, RwOpCombineType combineOp); const RtQuat *RtQuatQueryRotate(const RtQuat *quat, RwV3d * unitAxis, RwReal * angle); RwV3d *RtQuatTransformVectors(RwV3d * vectorsOut, const RwV3d * vectorsIn, const RwInt32 numPoints, const RtQuat *quat); void RtQuatConvertToMatrix(const RtQuat * const qpQuat, RwMatrix * const mpMatrix); ================================================ FILE: src/fakerw/rwcore.h ================================================ #pragma once #define RWCORE_H // needed by CVector #include #include /* *********************************************** * * RwIm2D and RwIm3D * *********************************************** */ typedef rw::RWDEVICE::Im2DVertex RwIm2DVertex; typedef rw::RWDEVICE::Im3DVertex RwIm3DVertex; typedef RwUInt16 RwImVertexIndex; enum RwIm3DTransformFlags { rwIM3D_VERTEXUV = rw::im3d::VERTEXUV, rwIM3D_ALLOPAQUE = rw::im3d::ALLOPAQUE, rwIM3D_NOCLIP = rw::im3d::NOCLIP, rwIM3D_VERTEXXYZ = rw::im3d::VERTEXXYZ, rwIM3D_VERTEXRGBA = rw::im3d::VERTEXRGBA, }; void RwIm2DVertexSetCameraX(RwIm2DVertex *vert, RwReal camx); void RwIm2DVertexSetCameraY(RwIm2DVertex *vert, RwReal camy); void RwIm2DVertexSetCameraZ(RwIm2DVertex *vert, RwReal camz); void RwIm2DVertexSetRecipCameraZ(RwIm2DVertex *vert, RwReal recipz); void RwIm2DVertexSetScreenX(RwIm2DVertex *vert, RwReal scrnx); void RwIm2DVertexSetScreenY(RwIm2DVertex *vert, RwReal scrny); void RwIm2DVertexSetScreenZ(RwIm2DVertex *vert, RwReal scrnz); void RwIm2DVertexSetU(RwIm2DVertex *vert, RwReal texU, RwReal recipz); void RwIm2DVertexSetV(RwIm2DVertex *vert, RwReal texV, RwReal recipz); void RwIm2DVertexSetIntRGBA(RwIm2DVertex *vert, RwUInt8 red, RwUInt8 green, RwUInt8 blue, RwUInt8 alpha); RwReal RwIm2DGetNearScreenZ(void); RwReal RwIm2DGetFarScreenZ(void); RwBool RwIm2DRenderLine(RwIm2DVertex *vertices, RwInt32 numVertices, RwInt32 vert1, RwInt32 vert2); RwBool RwIm2DRenderTriangle(RwIm2DVertex *vertices, RwInt32 numVertices, RwInt32 vert1, RwInt32 vert2, RwInt32 vert3 ); RwBool RwIm2DRenderPrimitive(RwPrimitiveType primType, RwIm2DVertex *vertices, RwInt32 numVertices); RwBool RwIm2DRenderIndexedPrimitive(RwPrimitiveType primType, RwIm2DVertex *vertices, RwInt32 numVertices, RwImVertexIndex *indices, RwInt32 numIndices); void RwIm3DVertexSetPos(RwIm3DVertex *vert, RwReal x, RwReal y, RwReal z); void RwIm3DVertexSetU(RwIm3DVertex *vert, RwReal u); void RwIm3DVertexSetV(RwIm3DVertex *vert, RwReal v); void RwIm3DVertexSetRGBA(RwIm3DVertex *vert, RwUInt8 r, RwUInt8 g, RwUInt8 b, RwUInt8 a); void *RwIm3DTransform(RwIm3DVertex *pVerts, RwUInt32 numVerts, RwMatrix *ltm, RwUInt32 flags); RwBool RwIm3DEnd(void); RwBool RwIm3DRenderLine(RwInt32 vert1, RwInt32 vert2); RwBool RwIm3DRenderTriangle(RwInt32 vert1, RwInt32 vert2, RwInt32 vert3); RwBool RwIm3DRenderIndexedPrimitive(RwPrimitiveType primType, RwImVertexIndex *indices, RwInt32 numIndices); RwBool RwIm3DRenderPrimitive(RwPrimitiveType primType); /* *********************************************** * * RwRaster * *********************************************** */ //struct RwRaster; typedef rw::Raster RwRaster; enum RwRasterType { rwRASTERTYPENORMAL = rw::Raster::NORMAL, rwRASTERTYPEZBUFFER = rw::Raster::ZBUFFER, rwRASTERTYPECAMERA = rw::Raster::CAMERA, rwRASTERTYPETEXTURE = rw::Raster::TEXTURE, rwRASTERTYPECAMERATEXTURE = rw::Raster::CAMERATEXTURE, rwRASTERTYPEMASK = 0x07, rwRASTERDONTALLOCATE = rw::Raster::DONTALLOCATE, }; enum RwRasterFormat { rwRASTERFORMATDEFAULT = rw::Raster::DEFAULT, rwRASTERFORMAT1555 = rw::Raster::C1555, rwRASTERFORMAT565 = rw::Raster::C565, rwRASTERFORMAT4444 = rw::Raster::C4444, rwRASTERFORMATLUM8 = rw::Raster::LUM8, rwRASTERFORMAT8888 = rw::Raster::C8888, rwRASTERFORMAT888 = rw::Raster::C888, rwRASTERFORMAT16 = rw::Raster::D16, rwRASTERFORMAT24 = rw::Raster::D24, rwRASTERFORMAT32 = rw::Raster::D32, rwRASTERFORMAT555 = rw::Raster::C555, rwRASTERFORMATAUTOMIPMAP = rw::Raster::AUTOMIPMAP, rwRASTERFORMATPAL8 = rw::Raster::PAL8, rwRASTERFORMATPAL4 = rw::Raster::PAL4, rwRASTERFORMATMIPMAP = rw::Raster::MIPMAP, rwRASTERFORMATPIXELFORMATMASK = 0x0f00, rwRASTERFORMATMASK = 0xff00 }; enum RwRasterFlipMode { rwRASTERFLIPDONTWAIT = 0, rwRASTERFLIPWAITVSYNC = 1, }; RwRaster *RwRasterCreate(RwInt32 width, RwInt32 height, RwInt32 depth, RwInt32 flags); RwBool RwRasterDestroy(RwRaster * raster); RwInt32 RwRasterGetWidth(const RwRaster *raster); RwInt32 RwRasterGetHeight(const RwRaster *raster); RwInt32 RwRasterGetStride(const RwRaster *raster); RwInt32 RwRasterGetDepth(const RwRaster *raster); RwInt32 RwRasterGetFormat(const RwRaster *raster); RwInt32 RwRasterGetType(const RwRaster *raster); RwRaster *RwRasterGetParent(const RwRaster *raster); RwRaster *RwRasterGetOffset(RwRaster *raster, RwInt16 *xOffset, RwInt16 *yOffset); RwInt32 RwRasterGetNumLevels(RwRaster * raster); RwRaster *RwRasterSubRaster(RwRaster * subRaster, RwRaster * raster, RwRect * rect); RwRaster *RwRasterRenderFast(RwRaster * raster, RwInt32 x, RwInt32 y); RwRaster *RwRasterRender(RwRaster * raster, RwInt32 x, RwInt32 y); RwRaster *RwRasterRenderScaled(RwRaster * raster, RwRect * rect); RwRaster *RwRasterPushContext(RwRaster * raster); RwRaster *RwRasterPopContext(void); RwRaster *RwRasterGetCurrentContext(void); RwBool RwRasterClear(RwInt32 pixelValue); RwBool RwRasterClearRect(RwRect * rpRect, RwInt32 pixelValue); RwRaster *RwRasterShowRaster(RwRaster * raster, void *dev, RwUInt32 flags); RwUInt8 *RwRasterLock(RwRaster * raster, RwUInt8 level, RwInt32 lockMode); RwRaster *RwRasterUnlock(RwRaster * raster); RwUInt8 *RwRasterLockPalette(RwRaster * raster, RwInt32 lockMode); RwRaster *RwRasterUnlockPalette(RwRaster * raster); RwInt32 RwRasterRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwRasterGetPluginOffset(RwUInt32 pluginID); RwBool RwRasterValidatePlugins(const RwRaster * raster); /* *********************************************** * * RwImage * *********************************************** */ //struct RwImage; typedef rw::Image RwImage; RwImage *RwImageCreate(RwInt32 width, RwInt32 height, RwInt32 depth); RwBool RwImageDestroy(RwImage * image); RwImage *RwImageAllocatePixels(RwImage * image); RwImage *RwImageFreePixels(RwImage * image); RwImage *RwImageCopy(RwImage * destImage, const RwImage * sourceImage); RwImage *RwImageResize(RwImage * image, RwInt32 width, RwInt32 height); RwImage *RwImageApplyMask(RwImage * image, const RwImage * mask); RwImage *RwImageMakeMask(RwImage * image); RwImage *RwImageReadMaskedImage(const RwChar * imageName, const RwChar * maskname); RwImage *RwImageRead(const RwChar * imageName); RwImage *RwImageWrite(RwImage * image, const RwChar * imageName); RwChar *RwImageGetPath(void); const RwChar *RwImageSetPath(const RwChar * path); RwImage *RwImageSetStride(RwImage * image, RwInt32 stride); RwImage *RwImageSetPixels(RwImage * image, RwUInt8 * pixels); RwImage *RwImageSetPalette(RwImage * image, RwRGBA * palette); RwInt32 RwImageGetWidth(const RwImage * image); RwInt32 RwImageGetHeight(const RwImage * image); RwInt32 RwImageGetDepth(const RwImage * image); RwInt32 RwImageGetStride(const RwImage * image); RwUInt8 *RwImageGetPixels(const RwImage * image); RwRGBA *RwImageGetPalette(const RwImage * image); RwUInt32 RwRGBAToPixel(RwRGBA * rgbIn, RwInt32 rasterFormat); RwRGBA *RwRGBASetFromPixel(RwRGBA * rgbOut, RwUInt32 pixelValue, RwInt32 rasterFormat); RwBool RwImageSetGamma(RwReal gammaValue); RwReal RwImageGetGamma(void); RwImage *RwImageGammaCorrect(RwImage * image); RwRGBA *RwRGBAGammaCorrect(RwRGBA * rgb); RwInt32 RwImageRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwImageGetPluginOffset(RwUInt32 pluginID); RwBool RwImageValidatePlugins(const RwImage * image); //RwBool RwImageRegisterImageFormat(const RwChar * extension, RwImageCallBackRead imageRead, RwImageCallBackWrite imageWrite); const RwChar *RwImageFindFileType(const RwChar * imageName); RwInt32 RwImageStreamGetSize(const RwImage * image); RwImage *RwImageStreamRead(RwStream * stream); const RwImage *RwImageStreamWrite(const RwImage * image, RwStream * stream); /* *********************************************** * * RwTexture * *********************************************** */ //struct RwTexture; typedef rw::Texture RwTexture; //struct RwTexDictionary; typedef rw::TexDictionary RwTexDictionary; typedef RwTexture *(*RwTextureCallBackRead)(const RwChar *name, const RwChar *maskName); typedef RwTexture *(*RwTextureCallBack)(RwTexture *texture, void *pData); typedef RwTexDictionary *(*RwTexDictionaryCallBack)(RwTexDictionary *dict, void *data); typedef RwRaster *(*RwTextureCallBackMipmapGeneration)(RwRaster * raster, RwImage * image); typedef RwBool (*RwTextureCallBackMipmapName)(RwChar *name, RwChar *maskName, RwUInt8 mipLevel, RwInt32 format); RwTexture *RwTextureCreate(RwRaster * raster); RwBool RwTextureDestroy(RwTexture * texture); RwTexture *RwTextureAddRef(RwTexture *texture); RwBool RwTextureSetMipmapping(RwBool enable); RwBool RwTextureGetMipmapping(void); RwBool RwTextureSetAutoMipmapping(RwBool enable); RwBool RwTextureGetAutoMipmapping(void); RwBool RwTextureSetMipmapGenerationCallBack(RwTextureCallBackMipmapGeneration callback); RwTextureCallBackMipmapGeneration RwTextureGetMipmapGenerationCallBack(void); RwBool RwTextureSetMipmapNameCallBack(RwTextureCallBackMipmapName callback); RwTextureCallBackMipmapName RwTextureGetMipmapNameCallBack(void); RwBool RwTextureGenerateMipmapName(RwChar * name, RwChar * maskName, RwUInt8 mipLevel, RwInt32 format); RwBool RwTextureRasterGenerateMipmaps(RwRaster * raster, RwImage * image); RwTextureCallBackRead RwTextureGetReadCallBack(void); RwBool RwTextureSetReadCallBack(RwTextureCallBackRead fpCallBack); RwTexture *RwTextureSetName(RwTexture * texture, const RwChar * name); RwTexture *RwTextureSetMaskName(RwTexture * texture, const RwChar * maskName); RwChar *RwTextureGetName(RwTexture *texture); RwChar *RwTextureGetMaskName(RwTexture *texture); RwTexture *RwTextureSetRaster(RwTexture * texture, RwRaster * raster); RwTexture *RwTextureRead(const RwChar * name, const RwChar * maskName); RwRaster *RwTextureGetRaster(const RwTexture *texture); RwInt32 RwTextureRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwTextureGetPluginOffset(RwUInt32 pluginID); RwBool RwTextureValidatePlugins(const RwTexture * texture); RwTexDictionary *RwTextureGetDictionary(RwTexture *texture); RwTexture *RwTextureSetFilterMode(RwTexture *texture, RwTextureFilterMode filtering); RwTextureFilterMode RwTextureGetFilterMode(const RwTexture *texture); RwTexture *RwTextureSetAddressing(RwTexture *texture, RwTextureAddressMode addressing); RwTexture *RwTextureSetAddressingU(RwTexture *texture, RwTextureAddressMode addressing); RwTexture *RwTextureSetAddressingV(RwTexture *texture, RwTextureAddressMode addressing); RwTextureAddressMode RwTextureGetAddressing(const RwTexture *texture); RwTextureAddressMode RwTextureGetAddressingU(const RwTexture *texture); RwTextureAddressMode RwTextureGetAddressingV(const RwTexture *texture); void _rwD3D8TexDictionaryEnableRasterFormatConversion(bool enable); // hack for reading native textures RwBool rwNativeTextureHackRead(RwStream *stream, RwTexture **tex, RwInt32 size); RwTexDictionary *RwTexDictionaryCreate(void); RwBool RwTexDictionaryDestroy(RwTexDictionary * dict); RwTexture *RwTexDictionaryAddTexture(RwTexDictionary * dict, RwTexture * texture); RwTexture *RwTexDictionaryRemoveTexture(RwTexture * texture); RwTexture *RwTexDictionaryFindNamedTexture(RwTexDictionary * dict, const RwChar * name); RwTexDictionary *RwTexDictionaryGetCurrent(void); RwTexDictionary *RwTexDictionarySetCurrent(RwTexDictionary * dict); const RwTexDictionary *RwTexDictionaryForAllTextures(const RwTexDictionary * dict, RwTextureCallBack fpCallBack, void *pData); RwBool RwTexDictionaryForAllTexDictionaries(RwTexDictionaryCallBack fpCallBack, void *pData); RwInt32 RwTexDictionaryRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwTexDictionaryGetPluginOffset(RwUInt32 pluginID); RwBool RwTexDictionaryValidatePlugins(const RwTexDictionary * dict); RwUInt32 RwTexDictionaryStreamGetSize(const RwTexDictionary *texDict); RwTexDictionary *RwTexDictionaryStreamRead(RwStream *stream); const RwTexDictionary *RwTexDictionaryStreamWrite(const RwTexDictionary *texDict, RwStream *stream); /* RwImage/RwRaster */ RwImage *RwImageSetFromRaster(RwImage *image, RwRaster *raster); RwRaster *RwRasterSetFromImage(RwRaster *raster, RwImage *image); RwRGBA *RwRGBAGetRasterPixel(RwRGBA *rgbOut, RwRaster *raster, RwInt32 x, RwInt32 y); RwRaster *RwRasterRead(const RwChar *filename); RwRaster *RwRasterReadMaskedRaster(const RwChar *filename, const RwChar *maskname); RwImage *RwImageFindRasterFormat(RwImage *ipImage,RwInt32 nRasterType, RwInt32 *npWidth,RwInt32 *npHeight, RwInt32 *npDepth,RwInt32 *npFormat); /* *********************************************** * * RwFrame * *********************************************** */ //struct RwFrame; typedef rw::Frame RwFrame; typedef RwFrame *(*RwFrameCallBack)(RwFrame *frame, void *data); RwFrame *RwFrameForAllObjects(RwFrame * frame, RwObjectCallBack callBack, void *data); RwFrame *RwFrameTranslate(RwFrame * frame, const RwV3d * v, RwOpCombineType combine); RwFrame *RwFrameRotate(RwFrame * frame, const RwV3d * axis, RwReal angle, RwOpCombineType combine); RwFrame *RwFrameScale(RwFrame * frame, const RwV3d * v, RwOpCombineType combine); RwFrame *RwFrameTransform(RwFrame * frame, const RwMatrix * m, RwOpCombineType combine); RwFrame *RwFrameOrthoNormalize(RwFrame * frame); RwFrame *RwFrameSetIdentity(RwFrame * frame); RwFrame *RwFrameCloneHierarchy(RwFrame * root); RwBool RwFrameDestroyHierarchy(RwFrame * frame); RwFrame *RwFrameForAllChildren(RwFrame * frame, RwFrameCallBack callBack, void *data); RwFrame *RwFrameRemoveChild(RwFrame * child); RwFrame *RwFrameAddChild(RwFrame * parent, RwFrame * child); RwFrame *RwFrameGetParent(const RwFrame * frame); RwFrame *RwFrameGetRoot(const RwFrame * frame); RwMatrix *RwFrameGetLTM(RwFrame * frame); RwMatrix *RwFrameGetMatrix(RwFrame * frame); RwFrame *RwFrameUpdateObjects(RwFrame * frame); RwFrame *RwFrameCreate(void); RwBool RwFrameInit(RwFrame *frame); RwBool RwFrameDeInit(RwFrame *frame); RwBool RwFrameDestroy(RwFrame * frame); void _rwFrameInit(RwFrame *frame); void _rwFrameDeInit(RwFrame *frame); RwBool RwFrameDirty(const RwFrame * frame); RwInt32 RwFrameCount(RwFrame * frame); RwBool RwFrameSetStaticPluginsSize(RwInt32 size); RwInt32 RwFrameRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwFrameGetPluginOffset(RwUInt32 pluginID); RwBool RwFrameValidatePlugins(const RwFrame * frame); RwFrame *_rwFrameCloneAndLinkClones(RwFrame * root); RwFrame *_rwFramePurgeClone(RwFrame *root); RwInt32 RwFrameRegisterPluginStream(RwUInt32 pluginID, RwPluginDataChunkReadCallBack readCB, RwPluginDataChunkWriteCallBack writeCB, RwPluginDataChunkGetSizeCallBack getSizeCB); RwInt32 RwFrameSetStreamAlwaysCallBack(RwUInt32 pluginID, RwPluginDataChunkAlwaysCallBack alwaysCB); typedef rw::FrameList_ rwFrameList; rwFrameList *rwFrameListInitialize(rwFrameList *frameList, RwFrame *frame); RwBool rwFrameListFindFrame(const rwFrameList *frameList, const RwFrame *frame, RwInt32 *npIndex); rwFrameList *rwFrameListDeinitialize(rwFrameList *frameList); RwUInt32 rwFrameListStreamGetSize(const rwFrameList *frameList); rwFrameList *rwFrameListStreamRead(RwStream *stream, rwFrameList *fl); const rwFrameList *rwFrameListStreamWrite(const rwFrameList *frameList, RwStream *stream); typedef rw::BBox RwBBox; /* *********************************************** * * RwCamera * *********************************************** */ //struct RwCamera; typedef rw::Camera RwCamera; typedef RwCamera *(*RwCameraCallBack)(RwCamera *camera, void *data); enum RwCameraClearMode { rwCAMERACLEARIMAGE = 0x1, rwCAMERACLEARZ = 0x2, rwCAMERACLEARSTENCIL = 0x4 }; enum RwCameraProjection { rwNACAMERAPROJECTION = 0, rwPERSPECTIVE = 1, rwPARALLEL = 2 }; enum RwFrustumTestResult { rwSPHEREOUTSIDE = 0, rwSPHEREBOUNDARY = 1, rwSPHEREINSIDE = 2 }; RwCamera *RwCameraBeginUpdate(RwCamera * camera); RwCamera *RwCameraEndUpdate(RwCamera * camera); RwCamera *RwCameraClear(RwCamera * camera, RwRGBA * colour, RwInt32 clearMode); RwCamera *RwCameraShowRaster(RwCamera * camera, void *pDev, RwUInt32 flags); RwBool RwCameraDestroy(RwCamera * camera); RwCamera *RwCameraCreate(void); RwCamera *RwCameraClone(RwCamera * camera); RwCamera *RwCameraSetViewOffset(RwCamera *camera, const RwV2d *offset); RwCamera *RwCameraSetViewWindow(RwCamera *camera, const RwV2d *viewWindow); RwCamera *RwCameraSetProjection(RwCamera *camera, RwCameraProjection projection); RwCamera *RwCameraSetNearClipPlane(RwCamera *camera, RwReal nearClip); RwCamera *RwCameraSetFarClipPlane(RwCamera *camera, RwReal farClip); RwInt32 RwCameraRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor constructCB, RwPluginObjectDestructor destructCB, RwPluginObjectCopy copyCB); RwInt32 RwCameraGetPluginOffset(RwUInt32 pluginID); RwBool RwCameraValidatePlugins(const RwCamera * camera); RwFrustumTestResult RwCameraFrustumTestSphere(const RwCamera * camera, const RwSphere * sphere); const RwV2d *RwCameraGetViewOffset(const RwCamera *camera); RwCamera *RwCameraSetRaster(RwCamera *camera, RwRaster *raster); RwRaster *RwCameraGetRaster(const RwCamera *camera); RwCamera *RwCameraSetZRaster(RwCamera *camera, RwRaster *zRaster); RwRaster *RwCameraGetZRaster(const RwCamera *camera); RwReal RwCameraGetNearClipPlane(const RwCamera *camera); RwReal RwCameraGetFarClipPlane(const RwCamera *camera); RwCamera *RwCameraSetFogDistance(RwCamera *camera, RwReal fogDistance); RwReal RwCameraGetFogDistance(const RwCamera *camera); RwCamera *RwCameraGetCurrentCamera(void); RwCameraProjection RwCameraGetProjection(const RwCamera *camera); const RwV2d *RwCameraGetViewWindow(const RwCamera *camera); RwMatrix *RwCameraGetViewMatrix(RwCamera *camera); RwCamera *RwCameraSetFrame(RwCamera *camera, RwFrame *frame); RwFrame *RwCameraGetFrame(const RwCamera *camera); /* * * D3D-engine specific stuff * */ void RwD3D8EngineSetRefreshRate(RwUInt32 refreshRate); RwBool RwD3D8DeviceSupportsDXTTexture(void); void RwD3D8EngineSetMultiSamplingLevels(RwUInt32 level); RwUInt32 RwD3D8EngineGetMaxMultiSamplingLevels(void); ================================================ FILE: src/fakerw/rwplcore.h ================================================ #pragma once typedef rw::int8 RwInt8; typedef rw::int16 RwInt16; typedef rw::int32 RwInt32; typedef rw::uint8 RwUInt8; typedef rw::uint16 RwUInt16; typedef rw::uint32 RwUInt32; typedef rw::float32 RwReal; typedef char RwChar; typedef RwInt32 RwBool; #define __RWUNUSED__ #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE !FALSE #endif // used for unicode #define RWSTRING(x) x typedef rw::V2d RwV2d; typedef rw::V3d RwV3d; typedef rw::Rect RwRect; typedef rw::Sphere RwSphere; enum RwTextureCoordinateIndex { rwNARWTEXTURECOORDINATEINDEX = 0, rwTEXTURECOORDINATEINDEX0, rwTEXTURECOORDINATEINDEX1, rwTEXTURECOORDINATEINDEX2, rwTEXTURECOORDINATEINDEX3, rwTEXTURECOORDINATEINDEX4, rwTEXTURECOORDINATEINDEX5, rwTEXTURECOORDINATEINDEX6, rwTEXTURECOORDINATEINDEX7, }; typedef rw::TexCoords RwTexCoords; typedef rw::SurfaceProperties RwSurfaceProperties; #define RWRGBALONG(r,g,b,a) \ ((RwUInt32) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))) #define MAKECHUNKID(vendorID, chunkID) (((vendorID & 0xFFFFFF) << 8) | (chunkID & 0xFF)) enum RwCorePluginID { rwID_NAOBJECT = 0x00, rwID_STRUCT = 0x01, rwID_STRING = 0x02, rwID_EXTENSION = 0x03, rwID_CAMERA = 0x05, rwID_TEXTURE = 0x06, rwID_MATERIAL = 0x07, rwID_MATLIST = 0x08, rwID_ATOMICSECT = 0x09, rwID_PLANESECT = 0x0A, rwID_WORLD = 0x0B, rwID_SPLINE = 0x0C, rwID_MATRIX = 0x0D, rwID_FRAMELIST = 0x0E, rwID_GEOMETRY = 0x0F, rwID_CLUMP = 0x10, rwID_LIGHT = 0x12, rwID_UNICODESTRING = 0x13, rwID_ATOMIC = 0x14, rwID_TEXTURENATIVE = 0x15, rwID_TEXDICTIONARY = 0x16, rwID_ANIMDATABASE = 0x17, rwID_IMAGE = 0x18, rwID_SKINANIMATION = 0x19, rwID_GEOMETRYLIST = 0x1A, rwID_HANIMANIMATION = 0x1B, rwID_TEAM = 0x1C, rwID_CROWD = 0x1D, rwID_DMORPHANIMATION = 0x1E, rwID_RIGHTTORENDER = 0x1f, rwID_MTEFFECTNATIVE = 0x20, rwID_MTEFFECTDICT = 0x21, rwID_TEAMDICTIONARY = 0x22, rwID_PITEXDICTIONARY = 0x23, rwID_TOC = 0x24, rwID_PRTSTDGLOBALDATA = 0x25, /* Insert before MAX and increment MAX */ rwID_COREPLUGINIDMAX = 0x26, }; /* *********************************************** * * RwObject * *********************************************** */ //struct RwObject; typedef rw::Object RwObject; typedef rw::Frame RwFrame; typedef RwObject *(*RwObjectCallBack)(RwObject *object, void *data); RwUInt8 RwObjectGetType(const RwObject *obj); RwFrame* rwObjectGetParent(const RwObject *obj); #define rwsprintf sprintf #define rwvsprintf vsprintf #define rwstrcpy strcpy #define rwstrncpy strncpy #define rwstrcat strcat #define rwstrncat strncat #define rwstrrchr strrchr #define rwstrchr strchr #define rwstrstr strstr #define rwstrcmp strcmp #define rwstricmp stricmp #define rwstrlen strlen #define rwstrupr strupr #define rwstrlwr strlwr #define rwstrtok strtok #define rwsscanf sscanf /* *********************************************** * * Memory * *********************************************** */ struct RwMemoryFunctions { // NB: from RW 3.6 on the allocating functions take // a hint parameter! void *(*rwmalloc)(size_t size); void (*rwfree)(void *mem); void *(*rwrealloc)(void *mem, size_t newSize); void *(*rwcalloc)(size_t numObj, size_t sizeObj); }; void *RwMalloc(size_t size); void RwFree(void *mem); void *RwRealloc(void *mem, size_t newSize); void *RwCalloc(size_t numObj, size_t sizeObj); /* *********************************************** * * RwStream * *********************************************** */ //struct RwStream; typedef rw::Stream RwStream; struct RwMemory { RwUInt8 *start; RwUInt32 length; }; enum RwStreamType { rwNASTREAM = 0, rwSTREAMFILE, rwSTREAMFILENAME, rwSTREAMMEMORY, rwSTREAMCUSTOM }; enum RwStreamAccessType { rwNASTREAMACCESS = 0, rwSTREAMREAD, rwSTREAMWRITE, rwSTREAMAPPEND }; RwStream *RwStreamOpen(RwStreamType type, RwStreamAccessType accessType, const void *pData); RwBool RwStreamClose(RwStream * stream, void *pData); RwUInt32 RwStreamRead(RwStream * stream, void *buffer, RwUInt32 length); RwStream *RwStreamWrite(RwStream * stream, const void *buffer, RwUInt32 length); RwStream *RwStreamSkip(RwStream * stream, RwUInt32 offset); /* *********************************************** * * Plugin Registry * *********************************************** */ #define RWPLUGINOFFSET(_type, _base, _offset) \ ((_type *)((RwUInt8 *)(_base) + (_offset))) typedef RwStream *(*RwPluginDataChunkWriteCallBack)(RwStream *stream, RwInt32 binaryLength, const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); typedef RwStream *(*RwPluginDataChunkReadCallBack)(RwStream *stream, RwInt32 binaryLength, void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); typedef RwInt32(*RwPluginDataChunkGetSizeCallBack)(const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); typedef RwBool(*RwPluginDataChunkAlwaysCallBack)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); typedef RwBool(*RwPluginDataChunkRightsCallBack)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject, RwUInt32 extraData); typedef void *(*RwPluginObjectConstructor)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); typedef void *(*RwPluginObjectCopy)(void *dstObject, const void *srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject); typedef void *(*RwPluginObjectDestructor)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject); /* *********************************************** * * RwMatrix * *********************************************** */ typedef rw::Matrix RwMatrix; enum RwOpCombineType { rwCOMBINEREPLACE = rw::COMBINEREPLACE, rwCOMBINEPRECONCAT = rw::COMBINEPRECONCAT, rwCOMBINEPOSTCONCAT = rw::COMBINEPOSTCONCAT }; enum RwMatrixType { rwMATRIXTYPENORMAL = rw::Matrix::TYPENORMAL, rwMATRIXTYPEORTHOGANAL = rw::Matrix::TYPEORTHOGONAL, rwMATRIXTYPEORTHONORMAL = rw::Matrix::TYPEORTHONORMAL, rwMATRIXTYPEMASK = 0x00000003, }; typedef rw::Matrix::Tolerance RwMatrixTolerance; RwBool RwMatrixDestroy(RwMatrix *mpMat); RwMatrix *RwMatrixCreate(void); void RwMatrixCopy(RwMatrix * dstMatrix, const RwMatrix * srcMatrix); void RwMatrixSetIdentity(RwMatrix * matrix); RwMatrix *RwMatrixMultiply(RwMatrix * matrixOut, const RwMatrix * MatrixIn1, const RwMatrix * matrixIn2); RwMatrix *RwMatrixTransform(RwMatrix * matrix, const RwMatrix * transform, RwOpCombineType combineOp); RwMatrix *RwMatrixOrthoNormalize(RwMatrix * matrixOut, const RwMatrix * matrixIn); RwMatrix *RwMatrixInvert(RwMatrix * matrixOut, const RwMatrix * matrixIn); RwMatrix *RwMatrixScale(RwMatrix * matrix, const RwV3d * scale, RwOpCombineType combineOp); RwMatrix *RwMatrixTranslate(RwMatrix * matrix, const RwV3d * translation, RwOpCombineType combineOp); RwMatrix *RwMatrixRotate(RwMatrix * matrix, const RwV3d * axis, RwReal angle, RwOpCombineType combineOp); RwMatrix *RwMatrixRotateOneMinusCosineSine(RwMatrix * matrix, const RwV3d * unitAxis, RwReal oneMinusCosine, RwReal sine, RwOpCombineType combineOp); const RwMatrix *RwMatrixQueryRotate(const RwMatrix * matrix, RwV3d * unitAxis, RwReal * angle, RwV3d * center); RwV3d *RwMatrixGetRight(RwMatrix * matrix); RwV3d *RwMatrixGetUp(RwMatrix * matrix); RwV3d *RwMatrixGetAt(RwMatrix * matrix); RwV3d *RwMatrixGetPos(RwMatrix * matrix); RwMatrix *RwMatrixUpdate(RwMatrix * matrix); RwMatrix *RwMatrixOptimize(RwMatrix * matrix, const RwMatrixTolerance *tolerance); /* *********************************************** * * RwRGBA * *********************************************** */ typedef rw::RGBA RwRGBA; typedef rw::RGBAf RwRGBAReal; inline void RwRGBAAssign(RwRGBA *target, const RwRGBA *source) { *target = *source; } RwReal RwV3dNormalize(RwV3d * out, const RwV3d * in); RwReal RwV3dLength(const RwV3d * in); RwReal RwV2dLength(const RwV2d * in); RwReal RwV2dNormalize(RwV2d * out, const RwV2d * in); void RwV2dAssign(RwV2d * out, const RwV2d * ina); void RwV2dAdd(RwV2d * out, const RwV2d * ina, const RwV2d * inb); void RwV2dLineNormal(RwV2d * out, const RwV2d * ina, const RwV2d * inb); void RwV2dSub(RwV2d * out, const RwV2d * ina, const RwV2d * inb); void RwV2dPerp(RwV2d * out, const RwV2d * in); void RwV2dScale(RwV2d * out, const RwV2d * in, RwReal scalar); RwReal RwV2dDotProduct(const RwV2d * ina, const RwV2d * inb); void RwV3dAssign(RwV3d * out, const RwV3d * ina); void RwV3dAdd(RwV3d * out, const RwV3d * ina, const RwV3d * inb); void RwV3dSub(RwV3d * out, const RwV3d * ina, const RwV3d * inb); void RwV3dScale(RwV3d * out, const RwV3d * in, RwReal scalar); void RwV3dIncrementScaled(RwV3d * out, const RwV3d * in, RwReal scalar); void RwV3dNegate(RwV3d * out, const RwV3d * in); RwReal RwV3dDotProduct(const RwV3d * ina, const RwV3d * inb); void RwV3dCrossProduct(RwV3d * out, const RwV3d * ina, const RwV3d * inb); RwV3d *RwV3dTransformPoints(RwV3d * pointsOut, const RwV3d * pointsIn, RwInt32 numPoints, const RwMatrix * matrix); RwV3d *RwV3dTransformVectors(RwV3d * vectorsOut, const RwV3d * vectorsIn, RwInt32 numPoints, const RwMatrix * matrix); /* *********************************************** * * Render States * *********************************************** */ // not librw because we don't support all of them (yet?) - mapping in wrapper functions enum RwRenderState { rwRENDERSTATENARENDERSTATE = 0, rwRENDERSTATETEXTURERASTER, rwRENDERSTATETEXTUREADDRESS, rwRENDERSTATETEXTUREADDRESSU, rwRENDERSTATETEXTUREADDRESSV, rwRENDERSTATETEXTUREPERSPECTIVE, rwRENDERSTATEZTESTENABLE, rwRENDERSTATESHADEMODE, rwRENDERSTATEZWRITEENABLE, rwRENDERSTATETEXTUREFILTER, rwRENDERSTATESRCBLEND, rwRENDERSTATEDESTBLEND, rwRENDERSTATEVERTEXALPHAENABLE, rwRENDERSTATEBORDERCOLOR, rwRENDERSTATEFOGENABLE, rwRENDERSTATEFOGCOLOR, rwRENDERSTATEFOGTYPE, rwRENDERSTATEFOGDENSITY, rwRENDERSTATEFOGTABLE, rwRENDERSTATEALPHAPRIMITIVEBUFFER, rwRENDERSTATECULLMODE, rwRENDERSTATESTENCILENABLE, rwRENDERSTATESTENCILFAIL, rwRENDERSTATESTENCILZFAIL, rwRENDERSTATESTENCILPASS, rwRENDERSTATESTENCILFUNCTION, rwRENDERSTATESTENCILFUNCTIONREF, rwRENDERSTATESTENCILFUNCTIONMASK, rwRENDERSTATESTENCILFUNCTIONWRITEMASK }; // not supported - we only do gouraud enum RwShadeMode { rwSHADEMODENASHADEMODE = 0, rwSHADEMODEFLAT, rwSHADEMODEGOURAUD }; enum RwBlendFunction { rwBLENDNABLEND = 0, rwBLENDZERO = rw::BLENDZERO, rwBLENDONE = rw::BLENDONE, rwBLENDSRCCOLOR = rw::BLENDSRCCOLOR, rwBLENDINVSRCCOLOR = rw::BLENDINVSRCCOLOR, rwBLENDSRCALPHA = rw::BLENDSRCALPHA, rwBLENDINVSRCALPHA = rw::BLENDINVSRCALPHA, rwBLENDDESTALPHA = rw::BLENDDESTALPHA, rwBLENDINVDESTALPHA = rw::BLENDINVDESTALPHA, rwBLENDDESTCOLOR = rw::BLENDDESTCOLOR, rwBLENDINVDESTCOLOR = rw::BLENDINVDESTCOLOR, rwBLENDSRCALPHASAT = rw::BLENDSRCALPHASAT }; // unsupported - we only need linear enum RwFogType { rwFOGTYPENAFOGTYPE = 0, rwFOGTYPELINEAR, rwFOGTYPEEXPONENTIAL, rwFOGTYPEEXPONENTIAL2 }; enum RwTextureFilterMode { rwFILTERNAFILTERMODE = 0, rwFILTERNEAREST = rw::Texture::NEAREST, rwFILTERLINEAR = rw::Texture::LINEAR, rwFILTERMIPNEAREST = rw::Texture::MIPNEAREST, rwFILTERMIPLINEAR = rw::Texture::MIPLINEAR, rwFILTERLINEARMIPNEAREST = rw::Texture::LINEARMIPNEAREST, rwFILTERLINEARMIPLINEAR = rw::Texture::LINEARMIPLINEAR }; enum RwTextureAddressMode { rwTEXTUREADDRESSNATEXTUREADDRESS = 0, rwTEXTUREADDRESSWRAP = rw::Texture::WRAP, rwTEXTUREADDRESSMIRROR = rw::Texture::MIRROR, rwTEXTUREADDRESSCLAMP = rw::Texture::CLAMP, rwTEXTUREADDRESSBORDER = rw::Texture::BORDER }; enum RwCullMode { rwCULLMODENACULLMODE = 0, rwCULLMODECULLNONE = rw::CULLNONE, rwCULLMODECULLBACK = rw::CULLBACK, rwCULLMODECULLFRONT = rw::CULLFRONT }; enum RwPrimitiveType { rwPRIMTYPENAPRIMTYPE = rw::PRIMTYPENONE, rwPRIMTYPELINELIST = rw::PRIMTYPELINELIST, rwPRIMTYPEPOLYLINE = rw::PRIMTYPEPOLYLINE, rwPRIMTYPETRILIST = rw::PRIMTYPETRILIST, rwPRIMTYPETRISTRIP = rw::PRIMTYPETRISTRIP, rwPRIMTYPETRIFAN = rw::PRIMTYPETRIFAN, rwPRIMTYPEPOINTLIST = rw::PRIMTYPEPOINTLIST }; RwBool RwRenderStateGet(RwRenderState state, void *value); RwBool RwRenderStateSet(RwRenderState state, void *value); /* *********************************************** * * Engine * *********************************************** */ struct RwEngineOpenParams { void *displayID; }; typedef rw::SubSystemInfo RwSubSystemInfo; enum RwVideoModeFlag { rwVIDEOMODEEXCLUSIVE = rw::VIDEOMODEEXCLUSIVE, /* rwVIDEOMODEINTERLACE = 0x2, rwVIDEOMODEFFINTERLACE = 0x4, rwVIDEOMODEFSAA0 = 0x8, rwVIDEOMODEFSAA1 = 0x10 */ }; typedef rw::VideoMode RwVideoMode; #if 0 struct RwFileFunctions { rwFnFexist rwfexist; /**< Pointer to fexist function */ rwFnFopen rwfopen; /**< Pointer to fopen function */ rwFnFclose rwfclose; /**< Pointer to fclose function */ rwFnFread rwfread; /**< Pointer to fread function */ rwFnFwrite rwfwrite; /**< Pointer to fwrite function */ rwFnFgets rwfgets; /**< Pointer to fgets function */ rwFnFputs rwfputs; /**< Pointer to puts function */ rwFnFeof rwfeof; /**< Pointer to feof function */ rwFnFseek rwfseek; /**< Pointer to fseek function */ rwFnFflush rwfflush; /**< Pointer to fflush function */ rwFnFtell rwftell; /**< Pointer to ftell function */ }; RwFileFunctions *RwOsGetFileInterface(void); #endif RwBool RwEngineInit(RwMemoryFunctions *memFuncs, RwUInt32 initFlags, RwUInt32 resArenaSize); RwInt32 RwEngineRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor initCB, RwPluginObjectDestructor termCB); RwInt32 RwEngineGetPluginOffset(RwUInt32 pluginID); RwBool RwEngineOpen(RwEngineOpenParams *initParams); RwBool RwEngineStart(void); RwBool RwEngineStop(void); RwBool RwEngineClose(void); RwBool RwEngineTerm(void); RwInt32 RwEngineGetNumSubSystems(void); RwSubSystemInfo *RwEngineGetSubSystemInfo(RwSubSystemInfo *subSystemInfo, RwInt32 subSystemIndex); RwInt32 RwEngineGetCurrentSubSystem(void); RwBool RwEngineSetSubSystem(RwInt32 subSystemIndex); RwInt32 RwEngineGetNumVideoModes(void); RwVideoMode *RwEngineGetVideoModeInfo(RwVideoMode *modeinfo, RwInt32 modeIndex); RwInt32 RwEngineGetCurrentVideoMode(void); RwBool RwEngineSetVideoMode(RwInt32 modeIndex); RwInt32 RwEngineGetTextureMemorySize(void); RwInt32 RwEngineGetMaxTextureSize(void); /* *********************************************** * * Binary stream * *********************************************** */ RwBool RwStreamFindChunk(RwStream *stream, RwUInt32 type, RwUInt32 *lengthOut, RwUInt32 *versionOut); ================================================ FILE: src/math/Matrix.cpp ================================================ #include "common.h" CMatrix::CMatrix(void) { m_attachment = nil; m_hasRwMatrix = false; } CMatrix::CMatrix(CMatrix const &m) { m_attachment = nil; m_hasRwMatrix = false; *this = m; } CMatrix::CMatrix(RwMatrix *matrix, bool owner) { m_attachment = nil; Attach(matrix, owner); } CMatrix::~CMatrix(void) { if (m_hasRwMatrix && m_attachment) RwMatrixDestroy(m_attachment); } void CMatrix::Attach(RwMatrix *matrix, bool owner) { #ifdef FIX_BUGS if (m_attachment && m_hasRwMatrix) #else if (m_hasRwMatrix && m_attachment) #endif RwMatrixDestroy(m_attachment); m_attachment = matrix; m_hasRwMatrix = owner; Update(); } void CMatrix::AttachRW(RwMatrix *matrix, bool owner) { if (m_hasRwMatrix && m_attachment) RwMatrixDestroy(m_attachment); m_attachment = matrix; m_hasRwMatrix = owner; UpdateRW(); } void CMatrix::Detach(void) { if (m_hasRwMatrix && m_attachment) RwMatrixDestroy(m_attachment); m_attachment = nil; } void CMatrix::Update(void) { GetRight() = m_attachment->right; GetForward() = m_attachment->up; GetUp() = m_attachment->at; GetPosition() = m_attachment->pos; } void CMatrix::UpdateRW(void) { if (m_attachment) { m_attachment->right = GetRight(); m_attachment->up = GetForward(); m_attachment->at = GetUp(); m_attachment->pos = GetPosition(); RwMatrixUpdate(m_attachment); } } void CMatrix::operator=(CMatrix const &rhs) { memcpy(this, &rhs, sizeof(f)); if (m_attachment) UpdateRW(); } void CMatrix::CopyOnlyMatrix(const CMatrix &other) { memcpy(this, &other, sizeof(f)); } CMatrix & CMatrix::operator+=(CMatrix const &rhs) { GetRight() += rhs.GetRight(); GetForward() += rhs.GetForward(); GetUp() += rhs.GetUp(); GetPosition() += rhs.GetPosition(); return *this; } void CMatrix::SetUnity(void) { rx = 1.0f; ry = 0.0f; rz = 0.0f; fx = 0.0f; fy = 1.0f; fz = 0.0f; ux = 0.0f; uy = 0.0f; uz = 1.0f; px = 0.0f; py = 0.0f; pz = 0.0f; } void CMatrix::ResetOrientation(void) { rx = 1.0f; ry = 0.0f; rz = 0.0f; fx = 0.0f; fy = 1.0f; fz = 0.0f; ux = 0.0f; uy = 0.0f; uz = 1.0f; } void CMatrix::SetScale(float s) { rx = s; ry = 0.0f; rz = 0.0f; fx = 0.0f; fy = s; fz = 0.0f; ux = 0.0f; uy = 0.0f; uz = s; px = 0.0f; py = 0.0f; pz = 0.0f; } void CMatrix::SetTranslate(float x, float y, float z) { rx = 1.0f; ry = 0.0f; rz = 0.0f; fx = 0.0f; fy = 1.0f; fz = 0.0f; ux = 0.0f; uy = 0.0f; uz = 1.0f; px = x; py = y; pz = z; } void CMatrix::SetRotateXOnly(float angle) { float c = Cos(angle); float s = Sin(angle); rx = 1.0f; ry = 0.0f; rz = 0.0f; fx = 0.0f; fy = c; fz = s; ux = 0.0f; uy = -s; uz = c; } void CMatrix::SetRotateYOnly(float angle) { float c = Cos(angle); float s = Sin(angle); rx = c; ry = 0.0f; rz = -s; fx = 0.0f; fy = 1.0f; fz = 0.0f; ux = s; uy = 0.0f; uz = c; } void CMatrix::SetRotateZOnly(float angle) { float c = Cos(angle); float s = Sin(angle); rx = c; ry = s; rz = 0.0f; fx = -s; fy = c; fz = 0.0f; ux = 0.0f; uy = 0.0f; uz = 1.0f; } void CMatrix::SetRotateX(float angle) { SetRotateXOnly(angle); px = 0.0f; py = 0.0f; pz = 0.0f; } void CMatrix::SetRotateY(float angle) { SetRotateYOnly(angle); px = 0.0f; py = 0.0f; pz = 0.0f; } void CMatrix::SetRotateZ(float angle) { SetRotateZOnly(angle); px = 0.0f; py = 0.0f; pz = 0.0f; } void CMatrix::SetRotate(float xAngle, float yAngle, float zAngle) { float cX = Cos(xAngle); float sX = Sin(xAngle); float cY = Cos(yAngle); float sY = Sin(yAngle); float cZ = Cos(zAngle); float sZ = Sin(zAngle); rx = cZ * cY - (sZ * sX) * sY; ry = (cZ * sX) * sY + sZ * cY; rz = -cX * sY; fx = -sZ * cX; fy = cZ * cX; fz = sX; ux = (sZ * sX) * cY + cZ * sY; uy = sZ * sY - (cZ * sX) * cY; uz = cX * cY; px = 0.0f; py = 0.0f; pz = 0.0f; } void CMatrix::RotateX(float x) { float c = Cos(x); float s = Sin(x); float ry = this->ry; float rz = this->rz; float uy = this->fy; float uz = this->fz; float ay = this->uy; float az = this->uz; float py = this->py; float pz = this->pz; this->ry = c * ry - s * rz; this->rz = c * rz + s * ry; this->fy = c * uy - s * uz; this->fz = c * uz + s * uy; this->uy = c * ay - s * az; this->uz = c * az + s * ay; this->py = c * py - s * pz; this->pz = c * pz + s * py; } void CMatrix::RotateY(float y) { float c = Cos(y); float s = Sin(y); float rx = this->rx; float rz = this->rz; float ux = this->fx; float uz = this->fz; float ax = this->ux; float az = this->uz; float px = this->px; float pz = this->pz; this->rx = c * rx + s * rz; this->rz = c * rz - s * rx; this->fx = c * ux + s * uz; this->fz = c * uz - s * ux; this->ux = c * ax + s * az; this->uz = c * az - s * ax; this->px = c * px + s * pz; this->pz = c * pz - s * px; } void CMatrix::RotateZ(float z) { float c = Cos(z); float s = Sin(z); float ry = this->ry; float rx = this->rx; float uy = this->fy; float ux = this->fx; float ay = this->uy; float ax = this->ux; float py = this->py; float px = this->px; this->rx = c * rx - s * ry; this->ry = c * ry + s * rx; this->fx = c * ux - s * uy; this->fy = c * uy + s * ux; this->ux = c * ax - s * ay; this->uy = c * ay + s * ax; this->px = c * px - s * py; this->py = c * py + s * px; } void CMatrix::Rotate(float x, float y, float z) { float cX = Cos(x); float sX = Sin(x); float cY = Cos(y); float sY = Sin(y); float cZ = Cos(z); float sZ = Sin(z); float rx = this->rx; float ry = this->ry; float rz = this->rz; float ux = this->fx; float uy = this->fy; float uz = this->fz; float ax = this->ux; float ay = this->uy; float az = this->uz; float px = this->px; float py = this->py; float pz = this->pz; float x1 = cZ * cY - (sZ * sX) * sY; float x2 = (cZ * sX) * sY + sZ * cY; float x3 = -cX * sY; float y1 = -sZ * cX; float y2 = cZ * cX; float y3 = sX; float z1 = (sZ * sX) * cY + cZ * sY; float z2 = sZ * sY - (cZ * sX) * cY; float z3 = cX * cY; this->rx = x1 * rx + y1 * ry + z1 * rz; this->ry = x2 * rx + y2 * ry + z2 * rz; this->rz = x3 * rx + y3 * ry + z3 * rz; this->fx = x1 * ux + y1 * uy + z1 * uz; this->fy = x2 * ux + y2 * uy + z2 * uz; this->fz = x3 * ux + y3 * uy + z3 * uz; this->ux = x1 * ax + y1 * ay + z1 * az; this->uy = x2 * ax + y2 * ay + z2 * az; this->uz = x3 * ax + y3 * ay + z3 * az; this->px = x1 * px + y1 * py + z1 * pz; this->py = x2 * px + y2 * py + z2 * pz; this->pz = x3 * px + y3 * py + z3 * pz; } CMatrix & CMatrix::operator*=(CMatrix const &rhs) { // TODO: VU0 code *this = *this * rhs; return *this; } void CMatrix::Reorthogonalise(void) { CVector &r = GetRight(); CVector &f = GetForward(); CVector &u = GetUp(); u = CrossProduct(r, f); u.Normalise(); r = CrossProduct(f, u); r.Normalise(); f = CrossProduct(u, r); } CMatrix operator*(const CMatrix &m1, const CMatrix &m2) { // TODO: VU0 code CMatrix out; out.rx = m1.rx * m2.rx + m1.fx * m2.ry + m1.ux * m2.rz; out.ry = m1.ry * m2.rx + m1.fy * m2.ry + m1.uy * m2.rz; out.rz = m1.rz * m2.rx + m1.fz * m2.ry + m1.uz * m2.rz; out.fx = m1.rx * m2.fx + m1.fx * m2.fy + m1.ux * m2.fz; out.fy = m1.ry * m2.fx + m1.fy * m2.fy + m1.uy * m2.fz; out.fz = m1.rz * m2.fx + m1.fz * m2.fy + m1.uz * m2.fz; out.ux = m1.rx * m2.ux + m1.fx * m2.uy + m1.ux * m2.uz; out.uy = m1.ry * m2.ux + m1.fy * m2.uy + m1.uy * m2.uz; out.uz = m1.rz * m2.ux + m1.fz * m2.uy + m1.uz * m2.uz; out.px = m1.rx * m2.px + m1.fx * m2.py + m1.ux * m2.pz + m1.px; out.py = m1.ry * m2.px + m1.fy * m2.py + m1.uy * m2.pz + m1.py; out.pz = m1.rz * m2.px + m1.fz * m2.py + m1.uz * m2.pz + m1.pz; return out; } CMatrix & Invert(const CMatrix &src, CMatrix &dst) { // TODO: VU0 code dst.f[3][0] = dst.f[3][1] = dst.f[3][2] = 0.0f; dst.f[0][0] = src.f[0][0]; dst.f[0][1] = src.f[1][0]; dst.f[0][2] = src.f[2][0]; dst.f[1][0] = src.f[0][1]; dst.f[1][1] = src.f[1][1]; dst.f[1][2] = src.f[2][1]; dst.f[2][0] = src.f[0][2]; dst.f[2][1] = src.f[1][2]; dst.f[2][2] = src.f[2][2]; dst.f[3][0] += dst.f[0][0] * src.f[3][0]; dst.f[3][1] += dst.f[0][1] * src.f[3][0]; dst.f[3][2] += dst.f[0][2] * src.f[3][0]; dst.f[3][0] += dst.f[1][0] * src.f[3][1]; dst.f[3][1] += dst.f[1][1] * src.f[3][1]; dst.f[3][2] += dst.f[1][2] * src.f[3][1]; dst.f[3][0] += dst.f[2][0] * src.f[3][2]; dst.f[3][1] += dst.f[2][1] * src.f[3][2]; dst.f[3][2] += dst.f[2][2] * src.f[3][2]; dst.f[3][0] = -dst.f[3][0]; dst.f[3][1] = -dst.f[3][1]; dst.f[3][2] = -dst.f[3][2]; return dst; } void CMatrix::CopyToRwMatrix(RwMatrix* matrix) { matrix->right = GetRight(); matrix->up = GetForward(); matrix->at = GetUp(); matrix->pos = GetPosition(); RwMatrixUpdate(matrix); } CMatrix Invert(const CMatrix &matrix) { CMatrix inv; return Invert(matrix, inv); } void CCompressedMatrixNotAligned::CompressFromFullMatrix(CMatrix &other) { m_rightX = 127.0f * other.GetRight().x; m_rightY = 127.0f * other.GetRight().y; m_rightZ = 127.0f * other.GetRight().z; m_upX = 127.0f * other.GetForward().x; m_upY = 127.0f * other.GetForward().y; m_upZ = 127.0f * other.GetForward().z; m_vecPos = other.GetPosition(); } void CCompressedMatrixNotAligned::DecompressIntoFullMatrix(CMatrix &other) { other.GetRight().x = m_rightX / 127.0f; other.GetRight().y = m_rightY / 127.0f; other.GetRight().z = m_rightZ / 127.0f; other.GetForward().x = m_upX / 127.0f; other.GetForward().y = m_upY / 127.0f; other.GetForward().z = m_upZ / 127.0f; other.GetUp() = CrossProduct(other.GetRight(), other.GetForward()); other.GetPosition() = m_vecPos; other.Reorthogonalise(); } ================================================ FILE: src/math/Matrix.h ================================================ #pragma once class CMatrix { public: #ifdef GTA_PS2 union { float f[4][4]; struct { float rx, ry, rz; RwMatrix *m_attachment; float fx, fy, fz; bool m_hasRwMatrix; // are we the owner? float ux, uy, uz, uw; float px, py, pz, pw; }; }; #else union { float f[4][4]; struct { float rx, ry, rz, rw; float fx, fy, fz, fw; float ux, uy, uz, uw; float px, py, pz, pw; }; }; RwMatrix *m_attachment; bool m_hasRwMatrix; // are we the owner? #endif CMatrix(void); CMatrix(CMatrix const &m); CMatrix(RwMatrix *matrix, bool owner = false); CMatrix(float scale){ m_attachment = nil; m_hasRwMatrix = false; SetScale(scale); } ~CMatrix(void); void Attach(RwMatrix *matrix, bool owner = false); void AttachRW(RwMatrix *matrix, bool owner = false); void Detach(void); void Update(void); void UpdateRW(void); void operator=(CMatrix const &rhs); CMatrix &operator+=(CMatrix const &rhs); CMatrix &operator*=(CMatrix const &rhs); CVector &GetPosition(void) { return *(CVector*)&px; } CVector &GetRight(void) { return *(CVector*)℞ } CVector &GetForward(void) { return *(CVector*)&fx; } CVector &GetUp(void) { return *(CVector*)&ux; } const CVector &GetPosition(void) const { return *(CVector*)&px; } const CVector &GetRight(void) const { return *(CVector*)℞ } const CVector &GetForward(void) const { return *(CVector*)&fx; } const CVector &GetUp(void) const { return *(CVector*)&ux; } void SetTranslate(float x, float y, float z); void SetTranslate(const CVector &trans){ SetTranslate(trans.x, trans.y, trans.z); } void Translate(float x, float y, float z){ px += x; py += y; pz += z; } void Translate(const CVector &trans){ Translate(trans.x, trans.y, trans.z); } void SetScale(float s); void Scale(float scale) { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) f[i][j] *= scale; } void Scale(float sx, float sy, float sz) { for (int i = 0; i < 3; i++){ f[i][0] *= sx; f[i][1] *= sy; f[i][2] *= sz; } } void SetRotateXOnly(float angle); void SetRotateYOnly(float angle); void SetRotateZOnly(float angle); void SetRotateZOnlyScaled(float angle, float scale) { float c = Cos(angle); float s = Sin(angle); rx = c * scale; ry = s * scale; rz = 0.0f; fx = -s * scale; fy = c * scale; fz = 0.0f; ux = 0.0f; uy = 0.0f; uz = scale; } void SetRotateX(float angle); void SetRotateY(float angle); void SetRotateZ(float angle); void SetRotate(float xAngle, float yAngle, float zAngle); void Rotate(float x, float y, float z); void RotateX(float x); void RotateY(float y); void RotateZ(float z); void Reorthogonalise(void); void CopyOnlyMatrix(const CMatrix &other); void SetUnity(void); void ResetOrientation(void); void CopyToRwMatrix(RwMatrix* matrix); void SetTranslateOnly(float x, float y, float z) { px = x; py = y; pz = z; } void SetTranslateOnly(const CVector& pos) { SetTranslateOnly(pos.x, pos.y, pos.z); } void CheckIntegrity(){} }; CMatrix &Invert(const CMatrix &src, CMatrix &dst); CMatrix Invert(const CMatrix &matrix); CMatrix operator*(const CMatrix &m1, const CMatrix &m2); inline CVector MultiplyInverse(const CMatrix &mat, const CVector &vec) { CVector v(vec.x - mat.px, vec.y - mat.py, vec.z - mat.pz); return CVector( mat.rx * v.x + mat.ry * v.y + mat.rz * v.z, mat.fx * v.x + mat.fy * v.y + mat.fz * v.z, mat.ux * v.x + mat.uy * v.y + mat.uz * v.z); } class CCompressedMatrixNotAligned { CVector m_vecPos; int8 m_rightX; int8 m_rightY; int8 m_rightZ; int8 m_upX; int8 m_upY; int8 m_upZ; public: void CompressFromFullMatrix(CMatrix &other); void DecompressIntoFullMatrix(CMatrix &other); }; class CCompressedMatrix : public CCompressedMatrixNotAligned { int _alignment; // no clue what would this align to }; ================================================ FILE: src/math/Quaternion.cpp ================================================ #include "common.h" #include "Quaternion.h" void CQuaternion::Normalise(void) { float sq = MagnitudeSqr(); if (sq == 0.0f) w = 1.0f; else { float invsqrt = RecipSqrt(sq); x *= invsqrt; y *= invsqrt; z *= invsqrt; w *= invsqrt; } } void CQuaternion::Slerp(const CQuaternion &q1, const CQuaternion &q2, float theta, float invSin, float t) { if (theta == 0.0f) *this = q2; else { float w1, w2; if (theta > PI / 2) { theta = PI - theta; w1 = Sin((1.0f - t) * theta) * invSin; w2 = -Sin(t * theta) * invSin; } else { w1 = Sin((1.0f - t) * theta) * invSin; w2 = Sin(t * theta) * invSin; } // TODO: VU0 code *this = w1 * q1 + w2 * q2; } } void CQuaternion::Multiply(const CQuaternion &q1, const CQuaternion &q2) { x = (q2.z * q1.y) - (q1.z * q2.y) + (q1.x * q2.w) + (q2.x * q1.w); y = (q2.x * q1.z) - (q1.x * q2.z) + (q1.y * q2.w) + (q2.y * q1.w); z = (q2.y * q1.x) - (q1.y * q2.x) + (q1.z * q2.w) + (q2.z * q1.w); w = (q2.w * q1.w) - (q2.x * q1.x) - (q2.y * q1.y) - (q2.z * q1.z); } void CQuaternion::Get(RwV3d *axis, float *angle) { *angle = Acos(w); float s = Sin(*angle); axis->x = x * (1.0f / s); axis->y = y * (1.0f / s); axis->z = z * (1.0f / s); } void CQuaternion::Set(RwV3d *axis, float angle) { float halfCos = Cos(angle * 0.5f); float halfSin = Sin(angle * 0.5f); x = axis->x * halfSin; y = axis->y * halfSin; z = axis->z * halfSin; w = halfCos; } void CQuaternion::Get(RwMatrix *matrix) { float x2 = x + x; float y2 = y + y; float z2 = z + z; float x_2x = x * x2; float x_2y = x * y2; float x_2z = x * z2; float y_2y = y * y2; float y_2z = y * z2; float z_2z = z * z2; float w_2x = w * x2; float w_2y = w * y2; float w_2z = w * z2; matrix->right.x = 1.0f - (y_2y + z_2z); matrix->up.x = x_2y - w_2z; matrix->at.x = x_2z + w_2y; matrix->right.y = x_2y + w_2z; matrix->up.y = 1.0f - (x_2x + z_2z); matrix->at.y = y_2z - w_2x; matrix->right.z = x_2z - w_2y; matrix->up.z = y_2z + w_2x; matrix->at.z = 1.0f - (x_2x + y_2y); } void CQuaternion::Set(const RwMatrix &matrix) { float f, s, m; f = matrix.up.y + matrix.right.x + matrix.at.z; if (f >= 0.0f) { s = Sqrt(f + 1.0f); w = 0.5f * s; m = 0.5f / s; x = (matrix.up.z - matrix.at.y) * m; y = (matrix.at.x - matrix.right.z) * m; z = (matrix.right.y - matrix.up.x) * m; return; } f = matrix.right.x - matrix.up.y - matrix.at.z; if (f >= 0.0f) { s = Sqrt(f + 1.0f); x = 0.5f * s; m = 0.5f / s; y = (matrix.up.x + matrix.right.y) * m; z = (matrix.at.x + matrix.right.z) * m; w = (matrix.up.z - matrix.at.y) * m; return; } f = matrix.up.y - matrix.right.x - matrix.at.z; if (f >= 0.0f) { s = Sqrt(f + 1.0f); y = 0.5f * s; m = 0.5f / s; w = (matrix.at.x - matrix.right.z) * m; x = (matrix.up.x - matrix.right.y) * m; z = (matrix.at.y + matrix.up.z) * m; return; } f = matrix.at.z - (matrix.up.y + matrix.right.x); s = Sqrt(f + 1.0f); z = 0.5f * s; m = 0.5f / s; w = (matrix.right.y - matrix.up.x) * m; x = (matrix.at.x + matrix.right.z) * m; y = (matrix.at.y + matrix.up.z) * m; } void CQuaternion::Get(float *f1, float *f2, float *f3) { RwMatrix matrix; Get(&matrix); *f3 = Atan2(matrix.right.y, matrix.up.y); if (*f3 < 0.0f) *f3 += TWOPI; float s = Sin(*f3); float c = Cos(*f3); *f1 = Atan2(-matrix.at.y, s * matrix.right.y + c * matrix.up.y); if (*f1 < 0.0f) *f1 += TWOPI; *f2 = Atan2(-(matrix.right.z * c - matrix.up.z * s), matrix.right.x * c - matrix.up.x * s); if (*f2 < 0.0f) *f2 += TWOPI; } void CQuaternion::Set(float f1, float f2, float f3) { float c1 = Cos(f1 * 0.5f); float c2 = Cos(f2 * 0.5f); float c3 = Cos(f3 * 0.5f); float s1 = Sin(f1 * 0.5f); float s2 = Sin(f2 * 0.5f); float s3 = Sin(f3 * 0.5f); x = ((c2 * c1) * s3) - ((s2 * s1) * c3); y = ((s1 * c2) * c3) + ((s2 * c1) * s3); z = ((s2 * c1) * c3) - ((s1 * c2) * s3); w = ((c2 * c1) * c3) + ((s2 * s1) * s3); } ================================================ FILE: src/math/Quaternion.h ================================================ #pragma once // TODO: actually implement this class CQuaternion { public: float x, y, z, w; CQuaternion(void) {} CQuaternion(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} float Magnitude(void) const { return Sqrt(x*x + y*y + z*z + w*w); } float MagnitudeSqr(void) const { return x*x + y*y + z*z + w*w; } void Normalise(void); void Multiply(const CQuaternion &q1, const CQuaternion &q2); void Invert(void){ // Conjugate would have been a better name x = -x; y = -y; z = -z; } const CQuaternion &operator+=(CQuaternion const &right) { x += right.x; y += right.y; z += right.z; w += right.w; return *this; } const CQuaternion &operator-=(CQuaternion const &right) { x -= right.x; y -= right.y; z -= right.z; w -= right.w; return *this; } const CQuaternion &operator*=(float right) { x *= right; y *= right; z *= right; w *= right; return *this; } const CQuaternion &operator/=(float right) { x /= right; y /= right; z /= right; w /= right; return *this; } CQuaternion operator-() const { return CQuaternion(-x, -y, -z, -w); } void Slerp(const CQuaternion &q1, const CQuaternion &q2, float theta, float invSin, float t); void Get(RwV3d *axis, float *angle); void Set(RwV3d *axis, float angle); void Get(RwMatrix *matrix); void Set(const RwMatrix &matrix); void Set(float f1, float f2, float f3); void Get(float *f1, float *f2, float *f3); }; inline float DotProduct(const CQuaternion &q1, const CQuaternion &q2) { return q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; } inline CQuaternion operator+(const CQuaternion &left, const CQuaternion &right) { return CQuaternion(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); } inline CQuaternion operator-(const CQuaternion &left, const CQuaternion &right) { return CQuaternion(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); } inline CQuaternion operator*(const CQuaternion &left, float right) { return CQuaternion(left.x * right, left.y * right, left.z * right, left.w * right); } inline CQuaternion operator*(float left, const CQuaternion &right) { return CQuaternion(left * right.x, left * right.y, left * right.z, left * right.w); } inline CQuaternion operator/(const CQuaternion &left, float right) { return CQuaternion(left.x / right, left.y / right, left.z / right, left.w / right); } ================================================ FILE: src/math/Rect.cpp ================================================ #include "common.h" CRect::CRect(void) { left = 1000000.0f; top = 1000000.0f; right = -1000000.0f; bottom = -1000000.0f; } CRect::CRect(float l, float t, float r, float b) { left = l; top = t; right = r; bottom = b; } ================================================ FILE: src/math/Rect.h ================================================ #pragma once class CRect { public: float left; // x min float bottom; // y max float right; // x max float top; // y min CRect(void); CRect(float l, float t, float r, float b); void ContainPoint(CVector const &v){ if(v.x < left) left = v.x; if(v.x > right) right = v.x; if(v.y < top) top = v.y; if(v.y > bottom) bottom = v.y; } void ContainRect(const CRect &r){ if(r.left < left) left = r.left; if(r.right > right) right = r.right; if(r.top < top) top = r.top; if(r.bottom > bottom) bottom = r.bottom; } bool IsPointInside(const CVector2D &p){ return p.x >= left && p.x <= right && p.y >= top && p.y <= bottom; } bool IsPointInside(const CVector2D &p, float extraRadius){ return p.x >= left-extraRadius && p.x <= right+extraRadius && p.y >= top-extraRadius && p.y <= bottom+extraRadius; } void Translate(float x, float y){ left += x; right += x; bottom += y; top += y; } void Grow(float r) { left -= r; right += r; top -= r; bottom += r; } void Grow(float l, float r) { left -= l; top -= l; right += r; bottom += r; } void Grow(float l, float r, float t, float b) { left -= l; top -= t; right += r; bottom += b; } float GetWidth(void) { return right - left; } float GetHeight(void) { return bottom - top; } }; ================================================ FILE: src/math/Vector.cpp ================================================ #include "common.h" void CVector::Normalise(void) { float sq = MagnitudeSqr(); if (sq > 0.0f) { float invsqrt = RecipSqrt(sq); x *= invsqrt; y *= invsqrt; z *= invsqrt; } else x = 1.0f; } CVector CrossProduct(const CVector &v1, const CVector &v2) { return CVector(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); } CVector Multiply3x3(const CMatrix &mat, const CVector &vec) { // TODO: VU0 code return CVector(mat.rx * vec.x + mat.fx * vec.y + mat.ux * vec.z, mat.ry * vec.x + mat.fy * vec.y + mat.uy * vec.z, mat.rz * vec.x + mat.fz * vec.y + mat.uz * vec.z); } CVector Multiply3x3(const CVector &vec, const CMatrix &mat) { return CVector(mat.rx * vec.x + mat.ry * vec.y + mat.rz * vec.z, mat.fx * vec.x + mat.fy * vec.y + mat.fz * vec.z, mat.ux * vec.x + mat.uy * vec.y + mat.uz * vec.z); } CVector operator*(const CMatrix &mat, const CVector &vec) { // TODO: VU0 code return CVector(mat.rx * vec.x + mat.fx * vec.y + mat.ux * vec.z + mat.px, mat.ry * vec.x + mat.fy * vec.y + mat.uy * vec.z + mat.py, mat.rz * vec.x + mat.fz * vec.y + mat.uz * vec.z + mat.pz); } ================================================ FILE: src/math/Vector.h ================================================ #pragma once class CVector : public RwV3d { public: CVector(void) {} CVector(float x, float y, float z) { this->x = x; this->y = y; this->z = z; } CVector(const RwV3d &v) { x = v.x; y = v.y; z = v.z; } // (0,1,0) means no rotation. So get right vector and its atan float Heading(void) const { return Atan2(-x, y); } float Magnitude(void) const { return Sqrt(x*x + y*y + z*z); } float MagnitudeSqr(void) const { return x*x + y*y + z*z; } float Magnitude2D(void) const { return Sqrt(x*x + y*y); } float MagnitudeSqr2D(void) const { return x*x + y*y; } void Normalise(void); void Normalise2D(void) { float sq = MagnitudeSqr2D(); float invsqrt = RecipSqrt(sq); x *= invsqrt; y *= invsqrt; } const CVector &operator+=(CVector const &right) { x += right.x; y += right.y; z += right.z; return *this; } const CVector &operator-=(CVector const &right) { x -= right.x; y -= right.y; z -= right.z; return *this; } const CVector &operator*=(float right) { x *= right; y *= right; z *= right; return *this; } const CVector &operator/=(float right) { x /= right; y /= right; z /= right; return *this; } CVector operator-() const { return CVector(-x, -y, -z); } const bool operator==(CVector const &right) const { return x == right.x && y == right.y && z == right.z; } const bool operator!=(CVector const &right) const { return x != right.x || y != right.y || z != right.z; } bool IsZero(void) const { return x == 0.0f && y == 0.0f && z == 0.0f; } }; inline CVector operator+(const CVector &left, const CVector &right) { return CVector(left.x + right.x, left.y + right.y, left.z + right.z); } inline CVector operator-(const CVector &left, const CVector &right) { return CVector(left.x - right.x, left.y - right.y, left.z - right.z); } inline CVector operator*(const CVector &left, float right) { return CVector(left.x * right, left.y * right, left.z * right); } inline CVector operator*(float left, const CVector &right) { return CVector(left * right.x, left * right.y, left * right.z); } inline CVector operator/(const CVector &left, float right) { return CVector(left.x / right, left.y / right, left.z / right); } inline float DotProduct(const CVector &v1, const CVector &v2) { return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; } CVector CrossProduct(const CVector &v1, const CVector &v2); inline float Distance(const CVector &v1, const CVector &v2) { return (v2 - v1).Magnitude(); } inline float Distance2D(const CVector &v1, const CVector &v2) { float x = v2.x - v1.x; float y = v2.y - v1.y; return Sqrt(x*x + y*y); } class CMatrix; CVector Multiply3x3(const CMatrix &mat, const CVector &vec); CVector Multiply3x3(const CVector &vec, const CMatrix &mat); CVector operator*(const CMatrix &mat, const CVector &vec); ================================================ FILE: src/math/Vector2D.h ================================================ #pragma once class CVector2D { public: float x, y; CVector2D(void) {} CVector2D(float x, float y) : x(x), y(y) {} CVector2D(const CVector &v) : x(v.x), y(v.y) {} float Heading(void) const { return Atan2(-x, y); } float Magnitude(void) const { return Sqrt(x*x + y*y); } float MagnitudeSqr(void) const { return x*x + y*y; } void Normalise(void) { float sq = MagnitudeSqr(); if(sq > 0.0f){ float invsqrt = RecipSqrt(sq); x *= invsqrt; y *= invsqrt; }else x = 1.0f; } const CVector2D &operator+=(CVector2D const &right) { x += right.x; y += right.y; return *this; } const CVector2D &operator-=(CVector2D const &right) { x -= right.x; y -= right.y; return *this; } const CVector2D &operator*=(float right) { x *= right; y *= right; return *this; } const CVector2D &operator/=(float right) { x /= right; y /= right; return *this; } CVector2D operator-(const CVector2D &rhs) const { return CVector2D(x-rhs.x, y-rhs.y); } CVector2D operator+(const CVector2D &rhs) const { return CVector2D(x+rhs.x, y+rhs.y); } CVector2D operator/(float t) const { return CVector2D(x/t, y/t); } CVector2D operator-() const { return CVector2D(-x, -y); } }; inline float DotProduct2D(const CVector2D &v1, const CVector2D &v2) { return v1.x*v2.x + v1.y*v2.y; } inline float CrossProduct2D(const CVector2D &v1, const CVector2D &v2) { return v1.x*v2.y - v1.y*v2.x; } inline float Distance2D(const CVector2D &v, float x, float y) { return Sqrt((v.x-x)*(v.x-x) + (v.y-y)*(v.y-y)); } inline float DistanceSqr2D(const CVector2D &v, float x, float y) { return (v.x-x)*(v.x-x) + (v.y-y)*(v.y-y); } inline void NormalizeXY(float &x, float &y) { float l = Sqrt(x*x + y*y); if(l != 0.0f){ x /= l; y /= l; }else x = 1.0f; } inline CVector2D operator*(const CVector2D &left, float right) { return CVector2D(left.x * right, left.y * right); } inline CVector2D operator*(float left, const CVector2D &right) { return CVector2D(left * right.x, left * right.y); } ================================================ FILE: src/math/VuVector.h ================================================ #pragma once class TYPEALIGN(16) CVuVector : public CVector { public: float w; CVuVector(void) {} CVuVector(float x, float y, float z) : CVector(x, y, z) {} CVuVector(float x, float y, float z, float w) : CVector(x, y, z), w(w) {} CVuVector(const CVector &v) : CVector(v.x, v.y, v.z) {} CVuVector(const RwV3d &v) : CVector(v) {} /* void Normalise(void) { float sq = MagnitudeSqr(); // TODO: VU0 code if(sq > 0.0f){ float invsqrt = RecipSqrt(sq); x *= invsqrt; y *= invsqrt; z *= invsqrt; }else x = 1.0f; } */ }; void TransformPoint(CVuVector &out, const CMatrix &mat, const CVuVector &in); void TransformPoint(CVuVector &out, const CMatrix &mat, const RwV3d &in); void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const RwV3d *in, int stride); void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const CVuVector *in); ================================================ FILE: src/math/math.cpp ================================================ #include "common.h" #include "VuVector.h" // TODO: move more stuff into here void TransformPoint(CVuVector &out, const CMatrix &mat, const CVuVector &in) { #ifdef GTA_PS2 __asm__ __volatile__("\n\ lqc2 vf01,0x0(%2)\n\ lqc2 vf02,0x0(%1)\n\ lqc2 vf03,0x10(%1)\n\ lqc2 vf04,0x20(%1)\n\ lqc2 vf05,0x30(%1)\n\ vmulax.xyz ACC, vf02,vf01\n\ vmadday.xyz ACC, vf03,vf01\n\ vmaddaz.xyz ACC, vf04,vf01\n\ vmaddw.xyz vf06,vf05,vf00\n\ sqc2 vf06,0x0(%0)\n\ ": : "r" (&out) , "r" (&mat) ,"r" (&in): "memory"); #else out = mat * in; #endif } void TransformPoint(CVuVector &out, const CMatrix &mat, const RwV3d &in) { #ifdef GTA_PS2 __asm__ __volatile__("\n\ ldr $8,0x0(%2)\n\ ldl $8,0x7(%2)\n\ lw $9,0x8(%2)\n\ pcpyld $10,$9,$8\n\ qmtc2 $10,vf01\n\ lqc2 vf02,0x0(%1)\n\ lqc2 vf03,0x10(%1)\n\ lqc2 vf04,0x20(%1)\n\ lqc2 vf05,0x30(%1)\n\ vmulax.xyz ACC, vf02,vf01\n\ vmadday.xyz ACC, vf03,vf01\n\ vmaddaz.xyz ACC, vf04,vf01\n\ vmaddw.xyz vf06,vf05,vf00\n\ sqc2 vf06,0x0(%0)\n\ ": : "r" (&out) , "r" (&mat) ,"r" (&in): "memory"); #else out = mat * in; #endif } void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const RwV3d *in, int stride) { #ifdef GTA_PS3 __asm__ __volatile__("\n\ paddub $3,%4,$0\n\ lqc2 vf02,0x0(%2)\n\ lqc2 vf03,0x10(%2)\n\ lqc2 vf04,0x20(%2)\n\ lqc2 vf05,0x30(%2)\n\ ldr $8,0x0(%3)\n\ ldl $8,0x7(%3)\n\ lw $9,0x8(%3)\n\ pcpyld $10,$9,$8\n\ qmtc2 $10,vf01\n\ 1: vmulax.xyz ACC, vf02,vf01\n\ vmadday.xyz ACC, vf03,vf01\n\ vmaddaz.xyz ACC, vf04,vf01\n\ vmaddw.xyz vf06,vf05,vf00\n\ add %3,%3,$3\n\ ldr $8,0x0(%3)\n\ ldl $8,0x7(%3)\n\ lw $9,0x8(%3)\n\ pcpyld $10,$9,$8\n\ qmtc2 $10,vf01\n\ addi %1,%1,-1\n\ addiu %0,%0,0x10\n\ sqc2 vf06,-0x10(%0)\n\ bnez %1,1b\n\ ": : "r" (out) , "r" (n), "r" (&mat), "r" (in), "r" (stride): "memory"); #else while(n--){ *out = mat * *in; in = (RwV3d*)((uint8*)in + stride); out++; } #endif } void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const CVuVector *in) { #ifdef GTA_PS2 __asm__ __volatile__("\n\ lqc2 vf02,0x0(%2)\n\ lqc2 vf03,0x10(%2)\n\ lqc2 vf04,0x20(%2)\n\ lqc2 vf05,0x30(%2)\n\ lqc2 vf01,0x0(%3)\n\ nop\n\ 1: vmulax.xyz ACC, vf02,vf01\n\ vmadday.xyz ACC, vf03,vf01\n\ vmaddaz.xyz ACC, vf04,vf01\n\ vmaddw.xyz vf06,vf05,vf00\n\ lqc2 vf01,0x10(%3)\n\ addiu %3,%3,0x10\n\ addi %1,%1,-1\n\ addiu %0,%0,0x10\n\ sqc2 vf06,-0x10(%0)\n\ bnez %1,1b\n\ ": : "r" (out) , "r" (n), "r" (&mat) ,"r" (in): "memory"); #else while(n--){ *out = mat * *in; in++; out++; } #endif } ================================================ FILE: src/math/maths.h ================================================ #pragma once // wrapper around float versions of functions // in gta they are in CMaths but that makes the code rather noisy inline float Sin(float x) { return sinf(x); } inline float Asin(float x) { return asinf(x); } inline float Cos(float x) { return cosf(x); } inline float Acos(float x) { return acosf(x); } inline float Tan(float x) { return tanf(x); } inline float Atan(float x) { return atanf(x); } inline float Atan2(float y, float x) { return atan2f(y, x); } inline float Abs(float x) { return fabs(x); } inline float Sqrt(float x) { return sqrtf(x); } inline float RecipSqrt(float x, float y) { return x/Sqrt(y); } inline float RecipSqrt(float x) { return RecipSqrt(1.0f, x); } inline float Pow(float x, float y) { return powf(x, y); } inline float Floor(float x) { return floorf(x); } inline float Ceil(float x) { return ceilf(x); } ================================================ FILE: src/modelinfo/BaseModelInfo.cpp ================================================ #include "common.h" #include "templates.h" #include "TxdStore.h" #include "2dEffect.h" #include "BaseModelInfo.h" #include "ModelInfo.h" #include "ColModel.h" CBaseModelInfo::CBaseModelInfo(ModelInfoType type) { m_colModel = nil; m_2dEffectsID = -1; m_objectId = -1; m_refCount = 0; m_txdSlot = -1; m_type = type; m_num2dEffects = 0; m_bOwnsColModel = false; } void CBaseModelInfo::Shutdown(void) { DeleteCollisionModel(); DeleteRwObject(); m_2dEffectsID = -1; m_num2dEffects = 0; m_txdSlot = -1; } void CBaseModelInfo::DeleteCollisionModel(void) { if(m_colModel && m_bOwnsColModel){ if(m_colModel) delete m_colModel; m_colModel = nil; } } void CBaseModelInfo::AddRef(void) { m_refCount++; AddTexDictionaryRef(); } void CBaseModelInfo::RemoveRef(void) { m_refCount--; RemoveTexDictionaryRef(); } void CBaseModelInfo::SetTexDictionary(const char *name) { int slot = CTxdStore::FindTxdSlot(name); if(slot == -1) slot = CTxdStore::AddTxdSlot(name); m_txdSlot = slot; } void CBaseModelInfo::AddTexDictionaryRef(void) { CTxdStore::AddRef(m_txdSlot); } void CBaseModelInfo::RemoveTexDictionaryRef(void) { CTxdStore::RemoveRef(m_txdSlot); } void CBaseModelInfo::Init2dEffects(void) { m_2dEffectsID = -1; m_num2dEffects = 0; } void CBaseModelInfo::Add2dEffect(C2dEffect *fx) { if(m_2dEffectsID >= 0) m_num2dEffects++; else{ m_2dEffectsID = CModelInfo::Get2dEffectStore().GetIndex(fx); m_num2dEffects = 1; } } C2dEffect* CBaseModelInfo::Get2dEffect(int n) { if(m_2dEffectsID >= 0) return CModelInfo::Get2dEffectStore().GetItem(m_2dEffectsID+n); else return nil; } ================================================ FILE: src/modelinfo/BaseModelInfo.h ================================================ #pragma once struct CColModel; #define MAX_MODEL_NAME (21) enum ModelInfoType { MITYPE_NA, MITYPE_SIMPLE, MITYPE_MLO, // unused but still in enum MITYPE_TIME, MITYPE_WEAPON, MITYPE_CLUMP, MITYPE_VEHICLE, MITYPE_PED, MITYPE_XTRACOMPS, // unused but still in enum MITYPE_HAND // xbox and mobile }; class C2dEffect; class CBaseModelInfo { protected: char m_name[MAX_MODEL_NAME]; uint8 m_type; uint8 m_num2dEffects; bool m_bOwnsColModel; CColModel *m_colModel; int16 m_2dEffectsID; int16 m_objectId; uint16 m_refCount; int16 m_txdSlot; public: CBaseModelInfo(ModelInfoType type); virtual ~CBaseModelInfo() {} virtual void Shutdown(void); virtual void DeleteRwObject(void) = 0; virtual RwObject *CreateInstance(void) = 0; virtual RwObject *CreateInstance(RwMatrix *) = 0; virtual RwObject *GetRwObject(void) = 0; virtual void SetAnimFile(const char *file) {} virtual void ConvertAnimFileIndex(void) {} virtual int GetAnimFileIndex(void) { return -1; } // one day it becomes virtual uint8 GetModelType() const { return m_type; } bool IsBuilding(void) { return m_type == MITYPE_SIMPLE || m_type == MITYPE_TIME; } bool IsSimple(void) { return m_type == MITYPE_SIMPLE || m_type == MITYPE_TIME || m_type == MITYPE_WEAPON; } bool IsClump(void) { return m_type == MITYPE_CLUMP || m_type == MITYPE_PED || m_type == MITYPE_VEHICLE; } char *GetModelName(void) { return m_name; } void SetModelName(const char *name) { strncpy(m_name, name, MAX_MODEL_NAME); } void SetColModel(CColModel *col, bool owns = false){ m_colModel = col; m_bOwnsColModel = owns; } CColModel *GetColModel(void) { return m_colModel; } bool DoesOwnColModel(void) { return m_bOwnsColModel; } void DeleteCollisionModel(void); void ClearTexDictionary(void) { m_txdSlot = -1; } int16 GetObjectID(void) { return m_objectId; } void SetObjectID(int16 id) { m_objectId = id; } int16 GetTxdSlot(void) { return m_txdSlot; } void AddRef(void); void RemoveRef(void); void SetTexDictionary(const char *name); void AddTexDictionaryRef(void); void RemoveTexDictionaryRef(void); void Init2dEffects(void); void Add2dEffect(C2dEffect *fx); C2dEffect *Get2dEffect(int n); uint8 GetNum2dEffects() const { return m_num2dEffects; } uint16 GetNumRefs() const { return m_refCount; } }; ================================================ FILE: src/modelinfo/ClumpModelInfo.cpp ================================================ #include "common.h" #include "RwHelper.h" #include "General.h" #include "NodeName.h" #include "VisibilityPlugins.h" #include "ModelInfo.h" #include "AnimManager.h" void CClumpModelInfo::DeleteRwObject(void) { if(m_clump){ RpClumpDestroy(m_clump); m_clump = nil; RemoveTexDictionaryRef(); if(GetAnimFileIndex() != -1) CAnimManager::RemoveAnimBlockRef(GetAnimFileIndex()); } } static RpAtomic* SetHierarchyForSkinAtomic(RpAtomic *atomic, void *data) { RpSkinAtomicSetHAnimHierarchy(atomic, (RpHAnimHierarchy*)data); return nil; } RwObject* CClumpModelInfo::CreateInstance(void) { if(m_clump == nil) return nil; RpClump *clone = RpClumpClone(m_clump); if(IsClumpSkinned(clone)){ RpHAnimHierarchy *hier; RpHAnimAnimation *anim; hier = GetAnimHierarchyFromClump(clone); assert(hier); RpClumpForAllAtomics(clone, SetHierarchyForSkinAtomic, hier); anim = HAnimAnimationCreateForHierarchy(hier); RpHAnimHierarchySetCurrentAnim(hier, anim); RpHAnimHierarchySetFlags(hier, (RpHAnimHierarchyFlag)(rpHANIMHIERARCHYUPDATEMODELLINGMATRICES|rpHANIMHIERARCHYUPDATELTMS)); } return (RwObject*)clone; } RwObject* CClumpModelInfo::CreateInstance(RwMatrix *m) { if(m_clump){ RpClump *clump = (RpClump*)CreateInstance(); *RwFrameGetMatrix(RpClumpGetFrame(clump)) = *m; return (RwObject*)clump; } return nil; } RpAtomic* CClumpModelInfo::SetAtomicRendererCB(RpAtomic *atomic, void *data) { CVisibilityPlugins::SetAtomicRenderCallback(atomic, (RpAtomicCallBackRender)data); return atomic; } void CClumpModelInfo::SetClump(RpClump *clump) { m_clump = clump; CVisibilityPlugins::SetClumpModelInfo(m_clump, this); AddTexDictionaryRef(); if(GetAnimFileIndex() != -1) CAnimManager::AddAnimBlockRef(GetAnimFileIndex()); if(IsClumpSkinned(clump)){ int i; RpHAnimHierarchy *hier; RpAtomic *skinAtomic; RpSkin *skin; hier = GetAnimHierarchyFromClump(clump); assert(hier); RpClumpForAllAtomics(clump, SetHierarchyForSkinAtomic, hier); skinAtomic = GetFirstAtomic(clump); assert(skinAtomic); skin = RpSkinGeometryGetSkin(RpAtomicGetGeometry(skinAtomic)); // ignore const for(i = 0; i < RpGeometryGetNumVertices(RpAtomicGetGeometry(skinAtomic)); i++){ RwMatrixWeights *weights = (RwMatrixWeights*)&RpSkinGetVertexBoneWeights(skin)[i]; float sum = weights->w0 + weights->w1 + weights->w2 + weights->w3; weights->w0 /= sum; weights->w1 /= sum; weights->w2 /= sum; weights->w3 /= sum; } RpHAnimHierarchySetFlags(hier, (RpHAnimHierarchyFlag)(rpHANIMHIERARCHYUPDATEMODELLINGMATRICES|rpHANIMHIERARCHYUPDATELTMS)); } } void CClumpModelInfo::SetAnimFile(const char *file) { if(strcasecmp(file, "null") == 0) return; m_animFileName = new char[strlen(file)+1]; strcpy(m_animFileName, file); } void CClumpModelInfo::ConvertAnimFileIndex(void) { if(m_animFileIndex != -1){ // we have a string pointer in that union int32 index = CAnimManager::GetAnimationBlockIndex(m_animFileName); delete[] m_animFileName; m_animFileIndex = index; } } void CClumpModelInfo::SetFrameIds(RwObjectNameIdAssocation *assocs) { int32 i; RwObjectNameAssociation objname; for(i = 0; assocs[i].name; i++) if((assocs[i].flags & CLUMP_FLAG_NO_HIERID) == 0){ objname.frame = nil; objname.name = assocs[i].name; RwFrameForAllChildren(RpClumpGetFrame(m_clump), FindFrameFromNameWithoutIdCB, &objname); if(objname.frame) CVisibilityPlugins::SetFrameHierarchyId(objname.frame, assocs[i].hierId); } } RwFrame* CClumpModelInfo::FindFrameFromIdCB(RwFrame *frame, void *data) { RwObjectIdAssociation *assoc = (RwObjectIdAssociation*)data; if(CVisibilityPlugins::GetFrameHierarchyId(frame) == assoc->id){ assoc->frame = frame; return nil; } RwFrameForAllChildren(frame, FindFrameFromIdCB, assoc); return assoc->frame ? nil : frame; } // unused RwFrame* CClumpModelInfo::FindFrameFromNameCB(RwFrame *frame, void *data) { RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data; if(!CGeneral::faststricmp(GetFrameNodeName(frame), assoc->name)){ assoc->frame = frame; return nil; } RwFrameForAllChildren(frame, FindFrameFromNameCB, assoc); return assoc->frame ? nil : frame; } RwFrame* CClumpModelInfo::FindFrameFromNameWithoutIdCB(RwFrame *frame, void *data) { RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data; if(CVisibilityPlugins::GetFrameHierarchyId(frame) == 0 && !CGeneral::faststricmp(GetFrameNodeName(frame), assoc->name)){ assoc->frame = frame; return nil; } RwFrameForAllChildren(frame, FindFrameFromNameWithoutIdCB, assoc); return assoc->frame ? nil : frame; } RwFrame* CClumpModelInfo::FillFrameArrayCB(RwFrame *frame, void *data) { int32 id; RwFrame **frames = (RwFrame**)data; id = CVisibilityPlugins::GetFrameHierarchyId(frame); if(id > 0) frames[id] = frame; RwFrameForAllChildren(frame, FillFrameArrayCB, data); return frame; } void CClumpModelInfo::FillFrameArray(RpClump *clump, RwFrame **frames) { RwFrameForAllChildren(RpClumpGetFrame(clump), FillFrameArrayCB, frames); } RwFrame* CClumpModelInfo::GetFrameFromId(RpClump *clump, int32 id) { RwObjectIdAssociation assoc; assoc.id = id; assoc.frame = nil; RwFrameForAllChildren(RpClumpGetFrame(clump), FindFrameFromIdCB, &assoc); return assoc.frame; } ================================================ FILE: src/modelinfo/ClumpModelInfo.h ================================================ #pragma once #include "BaseModelInfo.h" struct RwObjectNameIdAssocation { const char *name; int32 hierId; uint32 flags; }; struct RwObjectNameAssociation { const char *name; RwFrame *frame; }; struct RwObjectIdAssociation { int32 id; RwFrame *frame; }; enum { CLUMP_FLAG_NO_HIERID = 0x1, }; class CClumpModelInfo : public CBaseModelInfo { public: RpClump *m_clump; union { int32 m_animFileIndex; char *m_animFileName; }; CClumpModelInfo(void) : CBaseModelInfo(MITYPE_CLUMP) { m_animFileIndex = -1; } CClumpModelInfo(ModelInfoType id) : CBaseModelInfo(id) { m_animFileIndex = -1; } ~CClumpModelInfo() {} void DeleteRwObject(void); RwObject *CreateInstance(void); RwObject *CreateInstance(RwMatrix *); RwObject *GetRwObject(void) { return (RwObject*)m_clump; } virtual void SetClump(RpClump *); virtual void SetAnimFile(const char *file); virtual void ConvertAnimFileIndex(void); virtual int GetAnimFileIndex(void) { return m_animFileIndex; } static RpAtomic *SetAtomicRendererCB(RpAtomic *atomic, void *data); void SetFrameIds(RwObjectNameIdAssocation *assocs); static RwFrame *FindFrameFromNameCB(RwFrame *frame, void *data); static RwFrame *FindFrameFromNameWithoutIdCB(RwFrame *frame, void *data); static RwFrame *FindFrameFromIdCB(RwFrame *frame, void *data); static void FillFrameArray(RpClump *clump, RwFrame **frames); static RwFrame *FillFrameArrayCB(RwFrame *frame, void *data); static RwFrame *GetFrameFromId(RpClump *clump, int32 id); }; //static_assert(sizeof(CClumpModelInfo) == 0x34, "CClumpModelInfo: error"); ================================================ FILE: src/modelinfo/ModelIndices.cpp ================================================ #include "common.h" #include "General.h" #include "ModelIndices.h" #define X(name, var) int16 var = -1; MODELINDICES #undef X void InitModelIndices(void) { #define X(name, var) var = -1; MODELINDICES #undef X } void MatchModelString(const char *modelname, int16 id) { #define X(name, var) \ if(!CGeneral::faststrcmp(name, modelname)){ \ var = id; \ return; \ } MODELINDICES #undef X } void TestModelIndices(void) { ; } ================================================ FILE: src/modelinfo/ModelIndices.h ================================================ #pragma once #include "ModelInfo.h" #define MODELINDICES \ X("fire_hydrant", MI_FIRE_HYDRANT) \ X("phonesign", MI_PHONESIGN) \ X("noparkingsign1", MI_NOPARKINGSIGN1) \ X("bussign1", MI_BUSSIGN1) \ X("roadworkbarrier1", MI_ROADWORKBARRIER1) \ X("dump1", MI_DUMP1) \ X("trafficcone", MI_TRAFFICCONE) \ X("newsstand1", MI_NEWSSTAND) \ X("postbox1", MI_POSTBOX1) \ X("bin1", MI_BIN) \ X("wastebin", MI_WASTEBIN) \ X("phonebooth1", MI_PHONEBOOTH1) \ X("parkingmeter", MI_PARKINGMETER) \ X("parkingmeterg", MI_PARKINGMETER2) \ X("mall_fans", MI_MALLFAN) \ X("htl_fan_rotate_nt", MI_HOTELFAN_NIGHT) \ X("htl_fan_rotate_dy", MI_HOTELFAN_DAY) \ X("hotroomfan", MI_HOTROOMFAN) \ X("trafficlight1", MI_TRAFFICLIGHTS) \ X("MTraffic4", MI_TRAFFICLIGHTS_VERTICAL) \ X("MTraffic1", MI_TRAFFICLIGHTS_MIAMI) \ X("MTraffic2", MI_TRAFFICLIGHTS_TWOVERTICAL) \ X("lamppost1", MI_SINGLESTREETLIGHTS1) \ X("lamppost2", MI_SINGLESTREETLIGHTS2) \ X("lamppost3", MI_SINGLESTREETLIGHTS3) \ X("doublestreetlght1", MI_DOUBLESTREETLIGHTS) \ X("Streetlamp1", MI_STREETLAMP1) \ X("Streetlamp2", MI_STREETLAMP2) \ X("veg_tree3", MI_TREE2) \ X("veg_treea1", MI_TREE3) \ X("veg_treeb1", MI_TREE6) \ X("veg_treea3", MI_TREE8) \ X("doc_crane_cab0", MODELID_CRANE_1) \ X("doc_crane_cab01", MODELID_CRANE_2) \ X("doc_crane_cab02", MODELID_CRANE_3) \ X("doc_crane_cab03", MODELID_CRANE_4) \ X("boatcranelg0", MODELID_CRANE_5) \ X("LODnetopa0", MODELID_CRANE_6) \ X("package1", MI_COLLECTABLE1) \ X("Money", MI_MONEY) \ X("barrel1", MI_CARMINE) \ X("dk_paynspraydoor", MI_GARAGEDOOR2) \ X("dk_waretankdoor1", MI_GARAGEDOOR3) \ X("hav_garagedoor1", MI_GARAGEDOOR4) \ X("hav_garagedoor02", MI_GARAGEDOOR5) \ X("hav_garagedoor03", MI_GARAGEDOOR6) \ X("hav_garagedoor04", MI_GARAGEDOOR7) \ X("lh_showdoor03", MI_GARAGEDOOR9) \ X("lh_showdoor1", MI_GARAGEDOOR10) \ X("lhtankdoor", MI_GARAGEDOOR11) \ X("nbtgardoor", MI_GARAGEDOOR12) \ X("dk_camjonesdoor", MI_GARAGEDOOR13) \ X("nbtgardoor02", MI_GARAGEDOOR14) \ X("dt_savedra", MI_GARAGEDOOR15) \ X("dt_savedrb", MI_GARAGEDOOR16) \ X("dk_bombdoor", MI_GARAGEDOOR18) \ X("haiwshpnsdoor", MI_GARAGEDOOR19) \ X("wshpnsdoor", MI_GARAGEDOOR20) \ X("nbecpnsdoor", MI_GARAGEDOOR21) \ X("nbtgardoor03", MI_GARAGEDOOR22) \ X("dt_savedrc", MI_GARAGEDOOR23) \ X("dt_savedrd", MI_GARAGEDOOR24) \ X("man_frntstepGD", MI_GARAGEDOOR25) \ X("svegrgedoor", MI_GARAGEDOOR26) \ X("barrel2", MI_NAUTICALMINE) \ X("briefcase", MI_BRIEFCASE) \ X("wglasssmash", MI_GLASS1) \ X("glassfx_composh", MI_GLASS8) \ X("barrel4", MI_EXPLODINGBARREL) \ X("adrenaline", MI_PICKUP_ADRENALINE) \ X("bodyarmour", MI_PICKUP_BODYARMOUR) \ X("info", MI_PICKUP_INFO) \ X("health", MI_PICKUP_HEALTH) \ X("bonus", MI_PICKUP_BONUS) \ X("bribe", MI_PICKUP_BRIBE) \ X("killfrenzy", MI_PICKUP_KILLFRENZY) \ X("camerapickup", MI_PICKUP_CAMERA) \ X("bigdollar", MI_PICKUP_REVENUE) \ X("pickupsave", MI_PICKUP_SAVEGAME) \ X("property_locked", MI_PICKUP_PROPERTY) \ X("property_fsale", MI_PICKUP_PROPERTY_FORSALE) \ X("clothesp", MI_PICKUP_CLOTHES) \ X("bollardlight", MI_BOLLARDLIGHT) \ X("bar_barrier10", MI_FENCE) \ X("bar_barrier12", MI_FENCE2) \ X("petrolpump", MI_PETROLPUMP) \ X("washgaspump", MI_PETROLPUMP2) \ X("bouy", MI_BUOY) \ X("parktable1", MI_PARKTABLE) \ X("lamppost1", MI_LAMPPOST1) \ X("veg_palm04", MI_VEG_PALM01) \ X("veg_palwee02", MI_VEG_PALM02) \ X("veg_palmkbb11", MI_VEG_PALM03) \ X("veg_palmkb4", MI_VEG_PALM04) \ X("veg_palm02", MI_VEG_PALM05) \ X("veg_palmkb3", MI_VEG_PALM06) \ X("veg_palmbig14", MI_VEG_PALM07) \ X("veg_palm01", MI_VEG_PALM08) \ X("mlamppost", MI_MLAMPPOST) \ X("roadworkbarrier1", MI_BARRIER1) \ X("littleha_police", MI_LITTLEHA_POLICE) \ X("telgrphpole02", MI_TELPOLE02) \ X("trafficlight1", MI_TRAFFICLIGHT01) \ X("parkbench1", MI_PARKBENCH) \ X("plc_stinger", MI_PLC_STINGER) \ X("od_lightbeam", MI_LIGHTBEAM) \ X("ap_radar1_01", MI_AIRPORTRADAR) \ X("rcbomb", MI_RCBOMB) \ X("beachball", MI_BEACHBALL) \ X("sandcastle1", MI_SANDCASTLE1) \ X("sandcastle2", MI_SANDCASTLE2) \ X("jellyfish", MI_JELLYFISH) \ X("jellyfish01", MI_JELLYFISH01) \ X("fish1single", MI_FISH1SINGLE) \ X("fish1s", MI_FISH1S) \ X("fish2single", MI_FISH2SINGLE) \ X("fish2s", MI_FISH2S) \ X("fish3single", MI_FISH3SINGLE) \ X("fish3s", MI_FISH3S) \ X("turtle", MI_TURTLE) \ X("dolphin", MI_DOLPHIN) \ X("shark", MI_SHARK) \ X("submarine", MI_SUBMARINE) \ X("Esc_step", MI_ESCALATORSTEP) \ X("lounge_wood_up", MI_LOUNGE_WOOD_UP) \ X("lounge_towel_up", MI_LOUNGE_TOWEL_UP) \ X("lounge_wood_dn", MI_LOUNGE_WOOD_DN) \ X("lotion", MI_LOTION) \ X("beachtowel01", MI_BEACHTOWEL01) \ X("beachtowel02", MI_BEACHTOWEL02) \ X("beachtowel03", MI_BEACHTOWEL03) \ X("beachtowel04", MI_BEACHTOWEL04) \ X("blimp_night", MI_BLIMP_NIGHT) \ X("blimp_day", MI_BLIMP_DAY) \ X("yt_main_body", MI_YT_MAIN_BODY) \ X("yt_main_body2", MI_YT_MAIN_BODY2) #define X(name, var) extern int16 var; MODELINDICES #undef X // and some hardcoded ones // expand as needed enum { MI_PLAYER = 0, MI_COP, MI_SWAT, MI_FBI, MI_ARMY, MI_MEDIC, MI_FIREMAN, MI_MALE01, MI_HFYST = 9, MI_HFOST, MI_HMYST, MI_HMOST, MI_HFYRI, MI_HFORI, MI_HMYRI, MI_HMORI, MI_HFYBE, MI_HFOBE, MI_HMYBE, MI_HMOBE, MI_HFYBU, MI_HFYMD, MI_HFYCG, MI_HFYPR, MI_HFOTR, MI_HMOTR, MI_HMYAP, MI_HMOCA, MI_TAXI_D = MI_HMOCA, MI_BMODK, MI_BMYKR, MI_BFYST, MI_BFOST, MI_BMYST, MI_BMOST, MI_BFYRI, MI_BFORI, MI_BMYRI, MI_BFYBE, MI_BMYBE, MI_BFOBE, MI_BMOBE, MI_BMYBU, MI_BFYPR, MI_BFOTR, MI_BMOTR, MI_BMYPI, MI_BMYBB, MI_WMYCR, MI_WFYST, MI_WFOST, MI_WMYST, MI_WMOST, MI_WFYRI, MI_WFORI, MI_WMYRI, MI_WMORI, MI_WFYBE, MI_WMYBE, MI_WFOBE, MI_WMOBE, MI_WMYCW, MI_WMYGO, MI_WFOGO, MI_WMOGO, MI_WFYLG, MI_WMYLG, MI_WFYBU, MI_WMYBU, MI_WMOBU, MI_WFYPR, MI_WFOTR, MI_WMOTR, MI_WMYPI, MI_WMOCA, MI_WFYJG, MI_WMYJG, MI_WFYSK, MI_WMYSK, MI_WFYSH, MI_WFOSH, MI_JFOTO, MI_JMOTO, MI_CBA,// = 83, MI_CBB, MI_HNA, MI_HNB, MI_SGA, MI_SGB, MI_CLA, MI_CLB, MI_GDA, MI_GDB, MI_BKA, MI_BKB, MI_PGA, MI_PGB, MI_VICE1, MI_VICE2, MI_VICE3, MI_VICE4, MI_VICE5, MI_VICE6, MI_VICE7, MI_VICE8, MI_WFYG1, MI_WFYG2,// = 106, // last regular ped // three more peds possible MI_SPECIAL01 = 109, MI_SPECIAL02, MI_SPECIAL03, MI_SPECIAL04, MI_SPECIAL05, MI_SPECIAL06, MI_SPECIAL07, MI_SPECIAL08, MI_SPECIAL09, MI_SPECIAL10, MI_SPECIAL11, MI_SPECIAL12, MI_SPECIAL13, MI_SPECIAL14, MI_SPECIAL15, MI_SPECIAL16, MI_SPECIAL17, MI_SPECIAL18, MI_SPECIAL19, MI_SPECIAL20, MI_SPECIAL21,// = 129, MI_LAST_PED = MI_SPECIAL21, MI_FIRST_VEHICLE, MI_LANDSTAL = MI_FIRST_VEHICLE, MI_IDAHO, MI_STINGER, MI_LINERUN, MI_PEREN, MI_SENTINEL, MI_RIO, MI_FIRETRUCK, MI_TRASH, MI_STRETCH, MI_MANANA, MI_INFERNUS, MI_VOODOO, MI_PONY, MI_MULE, MI_CHEETAH, MI_AMBULAN, MI_FBICAR, MI_MOONBEAM, MI_ESPERANT, MI_TAXI, MI_WASHING, MI_BOBCAT, MI_MRWHOOP, MI_BFINJECT, MI_HUNTER, MI_POLICE, MI_ENFORCER, MI_SECURICA, MI_BANSHEE, MI_PREDATOR, MI_BUS, MI_RHINO, MI_BARRACKS, MI_CUBAN, MI_CHOPPER, MI_ANGEL, MI_COACH, MI_CABBIE, MI_STALLION, MI_RUMPO, MI_RCBANDIT, MI_ROMERO, MI_PACKER, MI_SENTXS, MI_ADMIRAL, MI_SQUALO, MI_SEASPAR, MI_PIZZABOY, MI_GANGBUR, MI_AIRTRAIN, MI_DEADDODO, MI_SPEEDER, MI_REEFER, MI_TROPIC, MI_FLATBED, MI_YANKEE, MI_CADDY, MI_ZEBRA, MI_TOPFUN, MI_SKIMMER, MI_PCJ600, MI_FAGGIO, MI_FREEWAY, MI_RCBARON, MI_RCRAIDER, MI_GLENDALE, MI_OCEANIC, MI_SANCHEZ, MI_SPARROW, MI_PATRIOT, MI_LOVEFIST, MI_COASTG, MI_DINGHY, MI_HERMES, MI_SABRE, MI_SABRETUR, MI_PHEONIX, MI_WALTON, MI_REGINA, MI_COMET, MI_DELUXO, MI_BURRITO, MI_SPAND, MI_MARQUIS, MI_BAGGAGE, MI_KAUFMAN, MI_MAVERICK, MI_VCNMAV, MI_RANCHER, MI_FBIRANCH, MI_VIRGO, MI_GREENWOO, MI_JETMAX, MI_HOTRING, MI_SANDKING, MI_BLISTAC, MI_POLMAV, MI_BOXVILLE, MI_BENSON, MI_MESA, MI_RCGOBLIN, MI_HOTRINA, MI_HOTRINB, MI_BLOODRA, MI_BLOODRB, MI_VICECHEE, // HACK MI_TRAIN = -1, MI_DODO = -2, MI_LAST_VEHICLE = MI_VICECHEE, MI_WHEEL_RIM, MI_WHEEL_OFFROAD, MI_WHEEL_TRUCK, MI_CAR_DOOR,// = 240, MI_CAR_BUMPER, MI_CAR_PANEL, MI_CAR_BONNET, MI_CAR_BOOT, MI_CAR_WHEEL, MI_BODYPARTA, MI_BODYPARTB, MI_WHEEL_SPORT = 250, MI_WHEEL_SALOON, MI_WHEEL_LIGHTVAN, MI_WHEEL_CLASSIC, MI_WHEEL_ALLOY, MI_WHEEL_LIGHTTRUCK, MI_WHEEL_SMALLCAR, MI_AIRTRAIN_VLO, // = 257, MI_MOBILE, MI_BRASS_KNUCKLES, // 259 MI_SCREWDRIVER, MI_GOLFCLUB, MI_NIGHTSTICK, MI_KNIFE, MI_BASEBALL_BAT, MI_HAMMER, MI_MEAT_CLEAVER, MI_MACHETE, MI_KATANA, MI_CHAINSAW, MI_GRENADE, MI_TEARGAS, MI_MOLOTOV, MI_MISSILE, MI_COLT45, MI_PYTHON, MI_RUGER, MI_SHOTGUN, MI_SPAS12_SHOTGUN, MI_STUBBY_SHOTGUN, MI_M4, MI_TEC9, MI_UZI, MI_SILENCEDINGRAM, MI_MP5, MI_SNIPERRIFLE, MI_LASERSCOPE, MI_ROCKETLAUNCHER, MI_FLAMETHROWER, MI_M60, MI_MINIGUN, MI_BOMB, MI_CAMERA, MI_FINGERS, MI_MINIGUN2, MI_CUTOBJ01,// = 295, MI_CUTOBJ02, MI_CUTOBJ03, MI_CUTOBJ04, MI_CUTOBJ05, NUM_DEFAULT_MODELS,// = 300 }; enum{ NUM_OF_SPECIAL_CHARS = 21, NUM_OF_CUTSCENE_OBJECTS = 5 }; void InitModelIndices(void); void MatchModelString(const char *name, int16 id); void TestModelIndices(void); inline bool IsGlass(int16 id) { CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); return mi->IsBuilding() && (mi->m_isCodeGlass || mi->m_isArtistGlass); } inline bool IsTrafficLight(int16 id) { return id == MI_TRAFFICLIGHTS || id == MI_TRAFFICLIGHTS_VERTICAL || id == MI_TRAFFICLIGHTS_MIAMI || id == MI_TRAFFICLIGHTS_TWOVERTICAL; } inline bool IsLightWithoutShift(int16 id) { return id == MI_TRAFFICLIGHTS || id == MI_SINGLESTREETLIGHTS1 || id == MI_SINGLESTREETLIGHTS2 || id == MI_SINGLESTREETLIGHTS3 || id == MI_DOUBLESTREETLIGHTS; } inline bool IsLightWithPreRenderEffects(int16 id) { return IsTrafficLight(id) || id == MI_SINGLESTREETLIGHTS1 || id == MI_SINGLESTREETLIGHTS2 || id == MI_SINGLESTREETLIGHTS3 || id == MI_DOUBLESTREETLIGHTS; } inline bool IsLightThatNeedsRepositioning(int16 id) { return id == MI_SINGLESTREETLIGHTS1 || id == MI_SINGLESTREETLIGHTS2 || id == MI_SINGLESTREETLIGHTS3 || id == MI_TRAFFICLIGHTS_MIAMI || id == MI_TRAFFICLIGHTS_TWOVERTICAL || id == MI_MLAMPPOST || id == MI_STREETLAMP1 || id == MI_STREETLAMP2; } inline bool IsLightObject(int16 id) { return id == MI_TRAFFICLIGHTS_MIAMI || id == MI_MLAMPPOST || id == MI_SINGLESTREETLIGHTS1 || id == MI_SINGLESTREETLIGHTS2 || id == MI_SINGLESTREETLIGHTS3 || id == MI_DOUBLESTREETLIGHTS || id == MI_TRAFFICLIGHTS_TWOVERTICAL; } inline bool IsLampPost(int16 id) { return id == MI_SINGLESTREETLIGHTS1 || id == MI_SINGLESTREETLIGHTS2 || id == MI_SINGLESTREETLIGHTS3 || id == MI_BOLLARDLIGHT || id == MI_MLAMPPOST || id == MI_STREETLAMP1 || id == MI_STREETLAMP2 || id == MI_TELPOLE02 || id == MI_TRAFFICLIGHTS_MIAMI || id == MI_TRAFFICLIGHTS_TWOVERTICAL; } inline bool IsBodyPart(int16 id) { return id == MI_BODYPARTA || id == MI_BODYPARTB; } inline bool IsPedModel(int16 id) { return id >= MI_PLAYER && id <= MI_LAST_PED; } inline bool IsPalmTreeModel(int16 id) { return id == MI_VEG_PALM01 || id == MI_VEG_PALM02 || id == MI_VEG_PALM03 || id == MI_VEG_PALM04 || id == MI_VEG_PALM05 || id == MI_VEG_PALM06 || id == MI_VEG_PALM07 || id == MI_VEG_PALM08; } inline bool IsTreeModel(int16 id) { return id == MI_TREE2 || id == MI_TREE3 || id == MI_TREE6 || id == MI_TREE8 || IsPalmTreeModel(id); } inline bool IsPolicePedModel(int16 id) { return id == MI_COP || id == MI_SWAT || id == MI_FBI || id == MI_ARMY; } inline bool IsPoliceVehicleModel(int16 id) { return id == MI_CHOPPER || id == MI_PREDATOR || id == MI_POLICE || id == MI_ENFORCER; } inline bool IsExplosiveThingModel(int16 id) { return id == MI_EXPLODINGBARREL || id == MI_PETROLPUMP || id == MI_PETROLPUMP2; } inline bool IsFence(int16 id) { return id == MI_FENCE || id == MI_FENCE2; } ================================================ FILE: src/modelinfo/ModelInfo.cpp ================================================ #include "common.h" #include "General.h" #include "TempColModels.h" #include "ModelIndices.h" #include "ModelInfo.h" CBaseModelInfo *CModelInfo::ms_modelInfoPtrs[MODELINFOSIZE]; CStore CModelInfo::ms_simpleModelStore; CStore CModelInfo::ms_timeModelStore; CStore CModelInfo::ms_weaponModelStore; CStore CModelInfo::ms_clumpModelStore; CStore CModelInfo::ms_pedModelStore; CStore CModelInfo::ms_vehicleModelStore; CStore CModelInfo::ms_2dEffectStore; void CModelInfo::Initialise(void) { int i; CSimpleModelInfo *m; debug("sizeof SimpleModelStore %d\n", sizeof(ms_simpleModelStore)); debug("sizeof TimeModelStore %d\n", sizeof(ms_timeModelStore)); debug("sizeof WeaponModelStore %d\n", sizeof(ms_weaponModelStore)); debug("sizeof ClumpModelStore %d\n", sizeof(ms_clumpModelStore)); debug("sizeof VehicleModelStore %d\n", sizeof(ms_vehicleModelStore)); debug("sizeof PedModelStore %d\n", sizeof(ms_pedModelStore)); debug("sizeof 2deffectsModelStore %d\n", sizeof(ms_2dEffectStore)); for(i = 0; i < MODELINFOSIZE; i++) ms_modelInfoPtrs[i] = nil; ms_2dEffectStore.Clear(); ms_simpleModelStore.Clear(); ms_timeModelStore.Clear(); ms_weaponModelStore.Clear(); ms_clumpModelStore.Clear(); ms_pedModelStore.Clear(); ms_vehicleModelStore.Clear(); m = AddSimpleModel(MI_CAR_DOOR); m->SetColModel(&CTempColModels::ms_colModelDoor1); m->SetTexDictionary("generic"); m->SetNumAtomics(1); m->m_lodDistances[0] = 80.0f; m = AddSimpleModel(MI_CAR_BUMPER); m->SetColModel(&CTempColModels::ms_colModelBumper1); m->SetTexDictionary("generic"); m->SetNumAtomics(1); m->m_lodDistances[0] = 80.0f; m = AddSimpleModel(MI_CAR_PANEL); m->SetColModel(&CTempColModels::ms_colModelPanel1); m->SetTexDictionary("generic"); m->SetNumAtomics(1); m->m_lodDistances[0] = 80.0f; m = AddSimpleModel(MI_CAR_BONNET); m->SetColModel(&CTempColModels::ms_colModelBonnet1); m->SetTexDictionary("generic"); m->SetNumAtomics(1); m->m_lodDistances[0] = 80.0f; m = AddSimpleModel(MI_CAR_BOOT); m->SetColModel(&CTempColModels::ms_colModelBoot1); m->SetTexDictionary("generic"); m->SetNumAtomics(1); m->m_lodDistances[0] = 80.0f; m = AddSimpleModel(MI_CAR_WHEEL); m->SetColModel(&CTempColModels::ms_colModelWheel1); m->SetTexDictionary("generic"); m->SetNumAtomics(1); m->m_lodDistances[0] = 80.0f; m = AddSimpleModel(MI_BODYPARTA); m->SetColModel(&CTempColModels::ms_colModelBodyPart1); m->SetTexDictionary("generic"); m->SetNumAtomics(1); m->m_lodDistances[0] = 80.0f; m = AddSimpleModel(MI_BODYPARTB); m->SetColModel(&CTempColModels::ms_colModelBodyPart2); m->SetTexDictionary("generic"); m->SetNumAtomics(1); m->m_lodDistances[0] = 80.0f; } void CModelInfo::ShutDown(void) { int i; for(i = 0; i < ms_simpleModelStore.allocPtr; i++) ms_simpleModelStore.store[i].Shutdown(); for(i = 0; i < ms_timeModelStore.allocPtr; i++) ms_timeModelStore.store[i].Shutdown(); for(i = 0; i < ms_weaponModelStore.allocPtr; i++) ms_weaponModelStore.store[i].Shutdown(); for(i = 0; i < ms_clumpModelStore.allocPtr; i++) ms_clumpModelStore.store[i].Shutdown(); for(i = 0; i < ms_vehicleModelStore.allocPtr; i++) ms_vehicleModelStore.store[i].Shutdown(); for(i = 0; i < ms_pedModelStore.allocPtr; i++) ms_pedModelStore.store[i].Shutdown(); for(i = 0; i < ms_2dEffectStore.allocPtr; i++) ms_2dEffectStore.store[i].Shutdown(); ms_2dEffectStore.Clear(); ms_simpleModelStore.Clear(); ms_timeModelStore.Clear(); ms_weaponModelStore.Clear(); ms_pedModelStore.Clear(); ms_clumpModelStore.Clear(); ms_vehicleModelStore.Clear(); } CSimpleModelInfo* CModelInfo::AddSimpleModel(int id) { CSimpleModelInfo *modelinfo; modelinfo = CModelInfo::ms_simpleModelStore.Alloc(); CModelInfo::ms_modelInfoPtrs[id] = modelinfo; modelinfo->Init(); return modelinfo; } CTimeModelInfo* CModelInfo::AddTimeModel(int id) { CTimeModelInfo *modelinfo; modelinfo = CModelInfo::ms_timeModelStore.Alloc(); CModelInfo::ms_modelInfoPtrs[id] = modelinfo; modelinfo->Init(); return modelinfo; } CWeaponModelInfo* CModelInfo::AddWeaponModel(int id) { CWeaponModelInfo *modelinfo; modelinfo = CModelInfo::ms_weaponModelStore.Alloc(); CModelInfo::ms_modelInfoPtrs[id] = modelinfo; modelinfo->Init(); return modelinfo; } CClumpModelInfo* CModelInfo::AddClumpModel(int id) { CClumpModelInfo *modelinfo; modelinfo = CModelInfo::ms_clumpModelStore.Alloc(); CModelInfo::ms_modelInfoPtrs[id] = modelinfo; modelinfo->m_clump = nil; return modelinfo; } CPedModelInfo* CModelInfo::AddPedModel(int id) { CPedModelInfo *modelinfo; modelinfo = CModelInfo::ms_pedModelStore.Alloc(); CModelInfo::ms_modelInfoPtrs[id] = modelinfo; modelinfo->m_clump = nil; return modelinfo; } CVehicleModelInfo* CModelInfo::AddVehicleModel(int id) { CVehicleModelInfo *modelinfo; modelinfo = CModelInfo::ms_vehicleModelStore.Alloc(); CModelInfo::ms_modelInfoPtrs[id] = modelinfo; modelinfo->m_clump = nil; modelinfo->m_vehicleType = -1; modelinfo->m_wheelId = -1; modelinfo->m_materials1[0] = nil; modelinfo->m_materials2[0] = nil; modelinfo->m_bikeSteerAngle = 999.99f; return modelinfo; } CBaseModelInfo* CModelInfo::GetModelInfo(const char *name, int *id) { CBaseModelInfo *modelinfo; for(int i = 0; i < MODELINFOSIZE; i++){ modelinfo = CModelInfo::ms_modelInfoPtrs[i]; if(modelinfo && !CGeneral::faststricmp(modelinfo->GetModelName(), name)){ if(id) *id = i; return modelinfo; } } return nil; } CBaseModelInfo* CModelInfo::GetModelInfo(const char *name, int minIndex, int maxIndex) { if (minIndex > maxIndex) return 0; CBaseModelInfo *modelinfo; for(int i = minIndex; i <= maxIndex; i++){ modelinfo = CModelInfo::ms_modelInfoPtrs[i]; if(modelinfo && !CGeneral::faststricmp(modelinfo->GetModelName(), name)) return modelinfo; } return nil; } bool CModelInfo::IsBoatModel(int32 id) { return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_BOAT; } bool CModelInfo::IsBikeModel(int32 id) { return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_BIKE; } bool CModelInfo::IsCarModel(int32 id) { return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_CAR; } bool CModelInfo::IsHeliModel(int32 id) { return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_HELI; } bool CModelInfo::IsPlaneModel(int32 id) { return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE && ((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_PLANE; } void CModelInfo::ReInit2dEffects() { ms_2dEffectStore.Clear(); for (int i = 0; i < MODELINFOSIZE; i++) { if (ms_modelInfoPtrs[i]) ms_modelInfoPtrs[i]->Init2dEffects(); } } ================================================ FILE: src/modelinfo/ModelInfo.h ================================================ #pragma once #include "2dEffect.h" #include "SimpleModelInfo.h" #include "TimeModelInfo.h" #include "WeaponModelInfo.h" #include "ClumpModelInfo.h" #include "PedModelInfo.h" #include "VehicleModelInfo.h" #include "templates.h" class CModelInfo { static CBaseModelInfo *ms_modelInfoPtrs[MODELINFOSIZE]; static CStore ms_simpleModelStore; static CStore ms_timeModelStore; static CStore ms_weaponModelStore; static CStore ms_clumpModelStore; static CStore ms_pedModelStore; static CStore ms_vehicleModelStore; static CStore ms_2dEffectStore; public: static void Initialise(void); static void ShutDown(void); static CSimpleModelInfo *AddSimpleModel(int id); static CTimeModelInfo *AddTimeModel(int id); static CWeaponModelInfo *AddWeaponModel(int id); static CClumpModelInfo *AddClumpModel(int id); static CPedModelInfo *AddPedModel(int id); static CVehicleModelInfo *AddVehicleModel(int id); static CStore &Get2dEffectStore(void) { return ms_2dEffectStore; } static CBaseModelInfo *GetModelInfo(const char *name, int *id); static CBaseModelInfo *GetModelInfo(int id){ return ms_modelInfoPtrs[id]; } static CBaseModelInfo *GetModelInfo(const char *name, int minIndex, int maxIndex); static bool IsBoatModel(int32 id); static bool IsBikeModel(int32 id); static bool IsCarModel(int32 id); static bool IsHeliModel(int32 id); static bool IsPlaneModel(int32 id); static void ReInit2dEffects(); }; ================================================ FILE: src/modelinfo/PedModelInfo.cpp ================================================ #include "common.h" #include "RwHelper.h" #include "General.h" #include "Bones.h" #include "SurfaceTable.h" #include "Ped.h" #include "NodeName.h" #include "VisibilityPlugins.h" #include "ModelInfo.h" #include "custompipes.h" void CPedModelInfo::DeleteRwObject(void) { CClumpModelInfo::DeleteRwObject(); if(m_hitColModel) delete m_hitColModel; m_hitColModel = nil; } // leftover... RwObjectNameIdAssocation CPedModelInfo::m_pPedIds[PED_NODE_MAX] = { { "Smid", PED_MID, 0, }, // that is strange... { "Shead", PED_HEAD, 0, }, { "Supperarml", PED_UPPERARML, 0, }, { "Supperarmr", PED_UPPERARMR, 0, }, { "SLhand", PED_HANDL, 0, }, { "SRhand", PED_HANDR, 0, }, { "Supperlegl", PED_UPPERLEGL, 0, }, { "Supperlegr", PED_UPPERLEGR, 0, }, { "Sfootl", PED_FOOTL, 0, }, { "Sfootr", PED_FOOTR, 0, }, { "Slowerlegr", PED_LOWERLEGR, 0, }, { nil, 0, 0, }, }; void CPedModelInfo::SetClump(RpClump *clump) { #ifdef EXTENDED_PIPELINES CustomPipes::AttachRimPipe(clump); #endif CClumpModelInfo::SetClump(clump); SetFrameIds(m_pPedIds); // not needed in VC actually if(m_hitColModel == nil) CreateHitColModelSkinned(clump); RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPedCB); if(strcmp(GetModelName(), "player") == 0) RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB); } struct ColNodeInfo { Const char *name; int pedNode; int pieceType; float x, z; float radius; }; #define NUMPEDINFONODES 10 ColNodeInfo m_pColNodeInfos[NUMPEDINFONODES] = { { nil, PED_HEAD, PEDPIECE_HEAD, 0.0f, 0.05f, 0.15f }, { nil, PED_MID, PEDPIECE_TORSO, 0.0f, 0.15f, 0.2f }, { nil, PED_MID, PEDPIECE_TORSO, 0.0f, -0.05f, 0.25f }, { nil, PED_MID, PEDPIECE_MID, 0.0f, -0.25f, 0.25f }, { nil, PED_UPPERARML, PEDPIECE_LEFTARM, 0.03f, -0.05f, 0.16f }, { nil, PED_UPPERARMR, PEDPIECE_RIGHTARM, -0.03f, -0.05f, 0.16f }, { nil, PED_LOWERLEGL, PEDPIECE_LEFTLEG, 0.0f, 0.15f, 0.2f }, { nil, PED_LOWERLEGR, PEDPIECE_RIGHTLEG, 0.0f, 0.15f, 0.2f }, { nil, PED_FOOTL, PEDPIECE_LEFTLEG, 0.0f, 0.15f, 0.15f }, { nil, PED_FOOTR, PEDPIECE_RIGHTLEG, 0.0f, 0.15f, 0.15f }, }; void CPedModelInfo::CreateHitColModelSkinned(RpClump *clump) { RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump); CColModel *colmodel = new CColModel; CColSphere *spheres = (CColSphere*)RwMalloc(NUMPEDINFONODES*sizeof(CColSphere)); RwFrame *root = RpClumpGetFrame(m_clump); RwMatrix *invmat = RwMatrixCreate(); RwMatrix *mat = RwMatrixCreate(); RwMatrixInvert(invmat, RwFrameGetMatrix(RpClumpGetFrame(clump))); for(int i = 0; i < NUMPEDINFONODES; i++){ *mat = *invmat; int id = ConvertPedNode2BoneTag(m_pColNodeInfos[i].pedNode); // this is wrong, wtf R* ??? int idx = RpHAnimIDGetIndex(hier, id); // This doesn't really work as the positions are not initialized yet RwMatrixTransform(mat, &RpHAnimHierarchyGetMatrixArray(hier)[idx], rwCOMBINEPRECONCAT); RwV3d pos = { 0.0f, 0.0f, 0.0f }; RwV3dTransformPoints(&pos, &pos, 1, mat); spheres[i].center = pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z); spheres[i].radius = m_pColNodeInfos[i].radius; spheres[i].surface = SURFACE_PED; spheres[i].piece = m_pColNodeInfos[i].pieceType; } RwMatrixDestroy(invmat); RwMatrixDestroy(mat); colmodel->spheres = spheres; colmodel->numSpheres = NUMPEDINFONODES; colmodel->boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f)); colmodel->boundingBox.Set(CVector(-0.5f, -0.5f, -1.2f), CVector(0.5f, 0.5f, 1.2f)); colmodel->level = LEVEL_GENERIC; m_hitColModel = colmodel; } CColModel* CPedModelInfo::AnimatePedColModelSkinned(RpClump *clump) { if(m_hitColModel == nil){ CreateHitColModelSkinned(clump); return m_hitColModel; } RwMatrix *invmat, *mat; CColSphere *spheres = m_hitColModel->spheres; RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump); invmat = RwMatrixCreate(); mat = RwMatrixCreate(); RwMatrixInvert(invmat, RwFrameGetMatrix(RpClumpGetFrame(clump))); for(int i = 0; i < NUMPEDINFONODES; i++){ *mat = *invmat; int id = ConvertPedNode2BoneTag(m_pColNodeInfos[i].pedNode); int idx = RpHAnimIDGetIndex(hier, id); RwMatrixTransform(mat, &RpHAnimHierarchyGetMatrixArray(hier)[idx], rwCOMBINEPRECONCAT); RwV3d pos = { 0.0f, 0.0f, 0.0f }; RwV3dTransformPoints(&pos, &pos, 1, mat); spheres[i].center = pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z); } RwMatrixDestroy(invmat); RwMatrixDestroy(mat); return m_hitColModel; } CColModel* CPedModelInfo::AnimatePedColModelSkinnedWorld(RpClump *clump) { if(m_hitColModel == nil) CreateHitColModelSkinned(clump); CColSphere *spheres = m_hitColModel->spheres; RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump); RwMatrix *mat; for(int i = 0; i < NUMPEDINFONODES; i++){ int id = ConvertPedNode2BoneTag(m_pColNodeInfos[i].pedNode); int idx = RpHAnimIDGetIndex(hier, id); mat = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwV3d pos = { 0.0f, 0.0f, 0.0f }; RwV3dTransformPoints(&pos, &pos, 1, mat); spheres[i].center = pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z); } return m_hitColModel; } ================================================ FILE: src/modelinfo/PedModelInfo.h ================================================ #pragma once #include "ClumpModelInfo.h" #include "ColModel.h" #include "PedType.h" enum PedNode { PED_TORSO = 0, // has no bone! PED_MID, PED_HEAD, PED_UPPERARML, PED_UPPERARMR, PED_HANDL, PED_HANDR, PED_UPPERLEGL, PED_UPPERLEGR, PED_FOOTL, PED_FOOTR, PED_LOWERLEGR, PED_LOWERLEGL, PED_FOREARML, PED_FOREARMR, PED_CLAVICLEL, PED_CLAVICLER, PED_NECK, PED_NODE_MAX }; class CPedModelInfo : public CClumpModelInfo { public: uint32 m_animGroup; ePedType m_pedType; ePedStats m_pedStatType; uint32 m_carsCanDrive; CColModel *m_hitColModel; int8 radio1, radio2; static RwObjectNameIdAssocation m_pPedIds[PED_NODE_MAX]; CPedModelInfo(void) : CClumpModelInfo(MITYPE_PED) { m_hitColModel = nil; } ~CPedModelInfo(void) { delete m_hitColModel; } void DeleteRwObject(void); void SetClump(RpClump *); void CreateHitColModelSkinned(RpClump *clump); CColModel *GetHitColModel(void) { return m_hitColModel; } CColModel *AnimatePedColModelSkinned(RpClump *clump); CColModel *AnimatePedColModelSkinnedWorld(RpClump *clump); }; ================================================ FILE: src/modelinfo/SimpleModelInfo.cpp ================================================ #include "common.h" #include "General.h" #include "Camera.h" #include "Renderer.h" #include "ModelInfo.h" #include "AnimManager.h" #include "custompipes.h" void CSimpleModelInfo::DeleteRwObject(void) { int i; RwFrame *f; for(i = 0; i < m_numAtomics; i++) if(m_atomics[i]){ f = RpAtomicGetFrame(m_atomics[i]); RpAtomicDestroy(m_atomics[i]); RwFrameDestroy(f); m_atomics[i] = nil; RemoveTexDictionaryRef(); if(GetAnimFileIndex() != -1) CAnimManager::RemoveAnimBlockRef(GetAnimFileIndex()); } } RwObject* CSimpleModelInfo::CreateInstance(void) { RpAtomic *atomic; if(m_atomics[0] == nil) return nil; atomic = RpAtomicClone(m_atomics[0]); RpAtomicSetFrame(atomic, RwFrameCreate()); return (RwObject*)atomic; } RwObject* CSimpleModelInfo::CreateInstance(RwMatrix *matrix) { RpAtomic *atomic; RwFrame *frame; if(m_atomics[0] == nil) return nil; atomic = RpAtomicClone(m_atomics[0]); frame = RwFrameCreate(); *RwFrameGetMatrix(frame) = *matrix; RpAtomicSetFrame(atomic, frame); return (RwObject*)atomic; } void CSimpleModelInfo::Init(void) { m_atomics[0] = nil; m_atomics[1] = nil; m_atomics[2] = nil; m_numAtomics = 0; m_firstDamaged = 0; m_wetRoadReflection = 0; m_isDamaged = 0; m_isBigBuilding = 0; m_noFade = 0; m_drawLast = 0; m_additive = 0; m_isSubway = 0; m_ignoreLight = 0; m_noZwrite = 0; m_noShadows = 0; m_ignoreDrawDist = 0; m_isCodeGlass = 0; m_isArtistGlass = 0; } void CSimpleModelInfo::SetAtomic(int n, RpAtomic *atomic) { AddTexDictionaryRef(); m_atomics[n] = atomic; if(GetAnimFileIndex() != -1) CAnimManager::AddAnimBlockRef(GetAnimFileIndex()); RpGeometry *geo = RpAtomicGetGeometry(atomic); if(m_ignoreLight) RpGeometrySetFlags(geo, RpGeometryGetFlags(geo) & ~rpGEOMETRYLIGHT); if(RpGeometryGetFlags(geo) & rpGEOMETRYNORMALS && RpGeometryGetNumTriangles(geo) > 200) debug("%s has %d polys\n", m_name, RpGeometryGetNumTriangles(geo)); #ifdef EXTENDED_PIPELINES if(m_wetRoadReflection) CustomPipes::AttachGlossPipe(atomic); else CustomPipes::AttachWorldPipe(atomic); #endif } void CSimpleModelInfo::SetLodDistances(float *dist) { m_lodDistances[0] = dist[0]; m_lodDistances[1] = dist[1]; m_lodDistances[2] = dist[2]; } void CSimpleModelInfo::IncreaseAlpha(void) { if(m_alpha >= 0xEF) m_alpha = 0xFF; else m_alpha += 0x10; } float CSimpleModelInfo::GetLodDistance(int i) { return m_lodDistances[i] * TheCamera.LODDistMultiplier; } float CSimpleModelInfo::GetNearDistance(void) { return m_lodDistances[2] * TheCamera.LODDistMultiplier; } float CSimpleModelInfo::GetLargestLodDistance(void) { float d; if(m_firstDamaged == 0 || m_isDamaged) d = m_lodDistances[m_numAtomics-1]; else d = m_lodDistances[m_firstDamaged-1]; return d * TheCamera.LODDistMultiplier; } RpAtomic* CSimpleModelInfo::GetAtomicFromDistance(float dist) { int i; i = 0; if(m_isDamaged) i = m_firstDamaged; for(; i < m_numAtomics; i++) if(dist < m_lodDistances[i] * TheCamera.LODDistMultiplier) return m_atomics[i]; return nil; } RpAtomic* CSimpleModelInfo::GetFirstAtomicFromDistance(float dist) { if(dist < m_lodDistances[0] * TheCamera.LODDistMultiplier) return m_atomics[0]; return nil; } void CSimpleModelInfo::FindRelatedModel(int32 minID, int32 maxID) { int i; CBaseModelInfo *mi; for(i = minID; i <= maxID; i++){ mi = CModelInfo::GetModelInfo(i); if(mi && mi != this && !CGeneral::faststrcmp(GetModelName()+3, mi->GetModelName()+3)){ assert(mi->IsSimple()); this->SetRelatedModel((CSimpleModelInfo*)mi); return; } } } #define NEAR_DRAW_DIST 0.0f // 100.0f in liberty city void CSimpleModelInfo::SetupBigBuilding(int32 minID, int32 maxID) { CSimpleModelInfo *related; if(m_lodDistances[0] > LOD_DISTANCE && GetRelatedModel() == nil){ m_isBigBuilding = 1; FindRelatedModel(minID, maxID); related = GetRelatedModel(); if(related){ m_lodDistances[2] = related->GetLargestLodDistance()/TheCamera.LODDistMultiplier; if(m_drawLast){ m_drawLast = false; debug("%s was draw last\n", GetModelName()); } }else m_lodDistances[2] = NEAR_DRAW_DIST; } } ================================================ FILE: src/modelinfo/SimpleModelInfo.h ================================================ #pragma once #include "BaseModelInfo.h" class CSimpleModelInfo : public CBaseModelInfo { public: // atomics[2] is often a pointer to the non-LOD modelinfo RpAtomic *m_atomics[3]; // m_lodDistances[2] holds the near distance for LODs float m_lodDistances[3]; uint8 m_numAtomics; uint8 m_alpha; uint16 m_firstDamaged : 2; // 0: no damage model // 1: 1 and 2 are damage models // 2: 2 is damage model uint16 m_wetRoadReflection : 1; uint16 m_isDamaged : 1; uint16 m_isBigBuilding : 1; uint16 m_noFade : 1; uint16 m_drawLast : 1; uint16 m_additive : 1; uint16 m_isSubway : 1; uint16 m_ignoreLight : 1; uint16 m_noZwrite : 1; uint16 m_noShadows : 1; uint16 m_ignoreDrawDist : 1; uint16 m_isCodeGlass : 1; uint16 m_isArtistGlass : 1; CSimpleModelInfo(void) : CBaseModelInfo(MITYPE_SIMPLE) {} CSimpleModelInfo(ModelInfoType id) : CBaseModelInfo(id) {} ~CSimpleModelInfo() {} void DeleteRwObject(void); RwObject *CreateInstance(void); RwObject *CreateInstance(RwMatrix *); RwObject *GetRwObject(void) { return (RwObject*)m_atomics[0]; } virtual void SetAtomic(int n, RpAtomic *atomic); void Init(void); void IncreaseAlpha(void); void SetLodDistances(float *dist); float GetLodDistance(int i); float GetNearDistance(void); float GetLargestLodDistance(void); RpAtomic *GetAtomicFromDistance(float dist); RpAtomic *GetFirstAtomicFromDistance(float dist); void FindRelatedModel(int32 minID, int32 maxID); void SetupBigBuilding(int32 minID, int32 maxID); void SetNumAtomics(int n) { m_numAtomics = n; } CSimpleModelInfo *GetRelatedModel(void){ return (CSimpleModelInfo*)m_atomics[2]; } void SetRelatedModel(CSimpleModelInfo *m){ m_atomics[2] = (RpAtomic*)m; } }; //static_assert(sizeof(CSimpleModelInfo) == 0x4C, "CSimpleModelInfo: error"); ================================================ FILE: src/modelinfo/TimeModelInfo.cpp ================================================ #include "common.h" #include "Camera.h" #include "ModelInfo.h" #include "General.h" CTimeModelInfo* CTimeModelInfo::FindOtherTimeModel(void) { char name[40]; char *p; int i; strcpy(name, GetModelName()); // change _nt to _dy if(p = strstr(name, "_nt")) strncpy(p, "_dy", 4); // change _dy to _nt else if(p = strstr(name, "_dy")) strncpy(p, "_nt", 4); else return nil; for(i = 0; i < MODELINFOSIZE; i++){ CBaseModelInfo *mi = CModelInfo::GetModelInfo(i); if (mi && mi->GetModelType() == MITYPE_TIME && !CGeneral::faststrncmp(name, mi->GetModelName(), MAX_MODEL_NAME)){ m_otherTimeModelID = i; return (CTimeModelInfo*)mi; } } return nil; } ================================================ FILE: src/modelinfo/TimeModelInfo.h ================================================ #pragma once #include "SimpleModelInfo.h" class CTimeModelInfo : public CSimpleModelInfo { int32 m_timeOn; int32 m_timeOff; int32 m_otherTimeModelID; public: CTimeModelInfo(void) : CSimpleModelInfo(MITYPE_TIME) { m_otherTimeModelID = -1; } int32 GetTimeOn(void) { return m_timeOn; } int32 GetTimeOff(void) { return m_timeOff; } void SetTimes(int32 on, int32 off) { m_timeOn = on; m_timeOff = off; } int32 GetOtherTimeModel(void) { return m_otherTimeModelID; } void SetOtherTimeModel(int32 other) { m_otherTimeModelID = other; } CTimeModelInfo *FindOtherTimeModel(void); }; //static_assert(sizeof(CTimeModelInfo) == 0x58, "CTimeModelInfo: error"); ================================================ FILE: src/modelinfo/VehicleModelInfo.cpp ================================================ #include "common.h" #include #include "RwHelper.h" #include "General.h" #include "NodeName.h" #include "TxdStore.h" #include "Weather.h" #include "HandlingMgr.h" #include "VisibilityPlugins.h" #include "FileMgr.h" #include "World.h" #include "Vehicle.h" #include "Automobile.h" #include "Boat.h" #include "Train.h" #include "Plane.h" #include "Heli.h" #include "Bike.h" #include "ModelIndices.h" #include "ModelInfo.h" #include "custompipes.h" int8 CVehicleModelInfo::ms_compsToUse[2] = { -2, -2 }; int8 CVehicleModelInfo::ms_compsUsed[2]; RwRGBA CVehicleModelInfo::ms_vehicleColourTable[256]; RwTexture *CVehicleModelInfo::ms_colourTextureTable[256]; RwTexture *gpWhiteTexture; RwFrame *pMatFxIdentityFrame; enum { VEHICLE_FLAG_COLLAPSE = 0x2, VEHICLE_FLAG_ADD_WHEEL = 0x4, VEHICLE_FLAG_POS = 0x8, VEHICLE_FLAG_DOOR = 0x10, VEHICLE_FLAG_LEFT = 0x20, VEHICLE_FLAG_RIGHT = 0x40, VEHICLE_FLAG_FRONT = 0x80, VEHICLE_FLAG_REAR = 0x100, VEHICLE_FLAG_COMP = 0x200, VEHICLE_FLAG_DRAWLAST = 0x400, VEHICLE_FLAG_WINDSCREEN = 0x800, VEHICLE_FLAG_ANGLECULL = 0x1000, VEHICLE_FLAG_REARDOOR = 0x2000, VEHICLE_FLAG_FRONTDOOR = 0x4000, }; RwObjectNameIdAssocation carIds[] = { { "wheel_rf_dummy", CAR_WHEEL_RF, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_ADD_WHEEL }, { "wheel_rm_dummy", CAR_WHEEL_RM, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_ADD_WHEEL }, { "wheel_rb_dummy", CAR_WHEEL_RB, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_ADD_WHEEL }, { "wheel_lf_dummy", CAR_WHEEL_LF, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_ADD_WHEEL }, { "wheel_lm_dummy", CAR_WHEEL_LM, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_ADD_WHEEL }, { "wheel_lb_dummy", CAR_WHEEL_LB, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_ADD_WHEEL }, { "bump_front_dummy", CAR_BUMP_FRONT, VEHICLE_FLAG_FRONT | VEHICLE_FLAG_COLLAPSE }, { "bonnet_dummy", CAR_BONNET, VEHICLE_FLAG_COLLAPSE }, { "wing_rf_dummy", CAR_WING_RF, VEHICLE_FLAG_COLLAPSE }, { "wing_rr_dummy", CAR_WING_RR, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_COLLAPSE }, { "door_rf_dummy", CAR_DOOR_RF, VEHICLE_FLAG_FRONTDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE }, { "door_rr_dummy", CAR_DOOR_RR, VEHICLE_FLAG_REARDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_REAR | VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE }, { "wing_lf_dummy", CAR_WING_LF, VEHICLE_FLAG_COLLAPSE }, { "wing_lr_dummy", CAR_WING_LR, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE }, { "door_lf_dummy", CAR_DOOR_LF, VEHICLE_FLAG_FRONTDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_LEFT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE }, { "door_lr_dummy", CAR_DOOR_LR, VEHICLE_FLAG_REARDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_REAR | VEHICLE_FLAG_LEFT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE }, { "boot_dummy", CAR_BOOT, VEHICLE_FLAG_REAR | VEHICLE_FLAG_COLLAPSE }, { "bump_rear_dummy", CAR_BUMP_REAR, VEHICLE_FLAG_REAR | VEHICLE_FLAG_COLLAPSE }, { "windscreen_dummy", CAR_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_FRONT | VEHICLE_FLAG_COLLAPSE }, { "ped_frontseat", CAR_POS_FRONTSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "ped_backseat", CAR_POS_BACKSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "headlights", CAR_POS_HEADLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "taillights", CAR_POS_TAILLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "exhaust", CAR_POS_EXHAUST, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "extra1", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra2", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra3", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra4", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra5", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra6", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { nil, 0, 0 } }; RwObjectNameIdAssocation boatIds[] = { { "boat_moving_hi", BOAT_MOVING, 0 }, { "boat_rudder_hi", BOAT_RUDDER, 0 }, { "boat_flap_left", BOAT_FLAP_LEFT, 0 }, { "boat_flap_right", BOAT_FLAP_RIGHT, 0 }, { "boat_rearflap_left", BOAT_REARFLAP_LEFT, 0 }, { "boat_rearflap_right", BOAT_REARFLAP_RIGHT, 0 }, #ifdef FIX_BUGS // let's just accept both { "windscreen", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, { "windscreen_hi_ok", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, #else #ifdef GTA_PS2 { "windscreen", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, #else { "windscreen_hi_ok", BOAT_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST }, #endif #endif { "ped_frontseat", BOAT_POS_FRONTSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { nil, 0, 0 } }; RwObjectNameIdAssocation trainIds[] = { { "door_lhs_dummy", TRAIN_DOOR_LHS, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE }, { "door_rhs_dummy", TRAIN_DOOR_RHS, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE }, { "light_front", TRAIN_POS_LIGHT_FRONT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "light_rear", TRAIN_POS_LIGHT_REAR, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "ped_left_entry", TRAIN_POS_LEFT_ENTRY, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "ped_mid_entry", TRAIN_POS_MID_ENTRY, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "ped_right_entry", TRAIN_POS_RIGHT_ENTRY, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { nil, 0, 0 } }; RwObjectNameIdAssocation heliIds[] = { { "chassis_dummy", HELI_CHASSIS, VEHICLE_FLAG_COLLAPSE }, { "toprotor", HELI_TOPROTOR, 0 }, { "backrotor", HELI_BACKROTOR, 0 }, { "tail", HELI_TAIL, 0 }, { "topknot", HELI_TOPKNOT, 0 }, { "skid_left", HELI_SKID_LEFT, 0 }, { "skid_right", HELI_SKID_RIGHT, 0 }, { nil, 0, 0 } }; RwObjectNameIdAssocation planeIds[] = { { "wheel_front_dummy", PLANE_WHEEL_FRONT, 0 }, { "wheel_rear_dummy", PLANE_WHEEL_READ, 0 }, { "light_tailplane", PLANE_POS_LIGHT_TAIL, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "light_left", PLANE_POS_LIGHT_LEFT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "light_right", PLANE_POS_LIGHT_RIGHT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { nil, 0, 0 } }; RwObjectNameIdAssocation bikeIds[] = { { "chassis_dummy", BIKE_CHASSIS, 0 }, { "forks_front", BIKE_FORKS_FRONT, 0 }, { "forks_rear", BIKE_FORKS_REAR, 0 }, { "wheel_front", BIKE_WHEEL_FRONT, 0 }, { "wheel_rear", BIKE_WHEEL_REAR, 0 }, { "mudguard", BIKE_MUDGUARD, 0 }, { "handlebars", BIKE_HANDLEBARS, 0 }, { "ped_frontseat", CAR_POS_FRONTSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "ped_backseat", CAR_POS_BACKSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "headlights", CAR_POS_HEADLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "taillights", CAR_POS_TAILLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "exhaust", CAR_POS_EXHAUST, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID }, { "extra1", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra2", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra3", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra4", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra5", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { "extra6", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID }, { nil, 0, 0 } }; RwObjectNameIdAssocation *CVehicleModelInfo::ms_vehicleDescs[] = { carIds, boatIds, trainIds, heliIds, planeIds, bikeIds }; bool gbBlackCars; bool gbPinkCars; CVehicleModelInfo::CVehicleModelInfo(void) : CClumpModelInfo(MITYPE_VEHICLE) { int32 i; for(i = 0; i < NUM_VEHICLE_POSITIONS; i++){ m_positions[i].x = 0.0f; m_positions[i].y = 0.0f; m_positions[i].z = 0.0f; } m_numColours = 0; m_animFileIndex = -1; } void CVehicleModelInfo::DeleteRwObject(void) { int32 i; RwFrame *f; for(i = 0; i < m_numComps; i++){ f = RpAtomicGetFrame(m_comps[i]); RpAtomicDestroy(m_comps[i]); RwFrameDestroy(f); } m_numComps = 0; CClumpModelInfo::DeleteRwObject(); } RwObject* CVehicleModelInfo::CreateInstance(void) { RpClump *clump; RpAtomic *atomic; RwFrame *clumpframe, *f; int32 comp1, comp2; clump = (RpClump*)CClumpModelInfo::CreateInstance(); if(m_numComps != 0){ clumpframe = RpClumpGetFrame(clump); comp1 = ChooseComponent(); if(comp1 != -1 && m_comps[comp1]){ atomic = RpAtomicClone(m_comps[comp1]); f = RwFrameCreate(); RwFrameTransform(f, RwFrameGetMatrix(RpAtomicGetFrame(m_comps[comp1])), rwCOMBINEREPLACE); RpAtomicSetFrame(atomic, f); RpClumpAddAtomic(clump, atomic); RwFrameAddChild(clumpframe, f); } ms_compsUsed[0] = comp1; comp2 = ChooseSecondComponent(); if(comp2 != -1 && m_comps[comp2]){ atomic = RpAtomicClone(m_comps[comp2]); f = RwFrameCreate(); RwFrameTransform(f, RwFrameGetMatrix(RpAtomicGetFrame(m_comps[comp2])), rwCOMBINEREPLACE); RpAtomicSetFrame(atomic, f); RpClumpAddAtomic(clump, atomic); RwFrameAddChild(clumpframe, f); } ms_compsUsed[1] = comp2; }else{ ms_compsUsed[0] = -1; ms_compsUsed[1] = -1; } return (RwObject*)clump; } void CVehicleModelInfo::SetClump(RpClump *clump) { CClumpModelInfo::SetClump(clump); SetAtomicRenderCallbacks(); SetFrameIds(ms_vehicleDescs[m_vehicleType]); PreprocessHierarchy(); FindEditableMaterialList(); SetEnvironmentMap(); } void CVehicleModelInfo::SetAnimFile(const char *file) { if(strcasecmp(file, "null") == 0) return; m_animFileName = new char[strlen(file)+1]; strcpy(m_animFileName, file); } void CVehicleModelInfo::ConvertAnimFileIndex(void) { if(m_animFileIndex != -1){ // we have a string pointer in that union int32 index = CAnimManager::GetAnimationBlockIndex(m_animFileName); delete[] m_animFileName; m_animFileIndex = index; } } RwFrame* CVehicleModelInfo::CollapseFramesCB(RwFrame *frame, void *data) { RwFrameForAllChildren(frame, CollapseFramesCB, data); RwFrameForAllObjects(frame, MoveObjectsCB, data); RwFrameDestroy(frame); return frame; } RwObject* CVehicleModelInfo::MoveObjectsCB(RwObject *object, void *data) { RpAtomicSetFrame((RpAtomic*)object, (RwFrame*)data); return object; } RpAtomic* CVehicleModelInfo::HideDamagedAtomicCB(RpAtomic *atomic, void *data) { if(strstr(GetFrameNodeName(RpAtomicGetFrame(atomic)), "_dam")){ RpAtomicSetFlags(atomic, 0); CVisibilityPlugins::SetAtomicFlag(atomic, ATOMIC_FLAG_DAM); }else if(strstr(GetFrameNodeName(RpAtomicGetFrame(atomic)), "_ok")) CVisibilityPlugins::SetAtomicFlag(atomic, ATOMIC_FLAG_OK); return atomic; } RpAtomic* CVehicleModelInfo::HideAllComponentsAtomicCB(RpAtomic *atomic, void *data) { if(CVisibilityPlugins::GetAtomicId(atomic) & (uintptr)data) RpAtomicSetFlags(atomic, 0); else RpAtomicSetFlags(atomic, rpATOMICRENDER); return atomic; } RpMaterial* CVehicleModelInfo::HasAlphaMaterialCB(RpMaterial *material, void *data) { if(RpMaterialGetColor(material)->alpha != 0xFF){ *(bool*)data = true; return nil; } return material; } RpAtomic* CVehicleModelInfo::SetAtomicRendererCB(RpAtomic *atomic, void *data) { RpClump *clump; char *name; bool alpha; clump = (RpClump*)data; name = GetFrameNodeName(RpAtomicGetFrame(atomic)); alpha = false; RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); if(strstr(name, "_hi") || !CGeneral::faststrncmp(name, "extra", 5)) { if(alpha || strncmp(name, "windscreen", 10) == 0) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB); }else if(strstr(name, "_lo")){ RpClumpRemoveAtomic(clump, atomic); RpAtomicDestroy(atomic); return atomic; // BUG: nil in gta }else if(strstr(name, "_vlo")) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); HideDamagedAtomicCB(atomic, nil); return atomic; } RpAtomic* CVehicleModelInfo::SetAtomicRendererCB_BigVehicle(RpAtomic *atomic, void *data) { char *name; bool alpha; name = GetFrameNodeName(RpAtomicGetFrame(atomic)); alpha = false; RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); if(strstr(name, "_hi") || !CGeneral::faststrncmp(name, "extra", 5)) { if(alpha) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_BigVehicle); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB_BigVehicle); }else if(strstr(name, "_lo")){ if(alpha) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleLowDetailAlphaCB_BigVehicle); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleLowDetailCB_BigVehicle); }else if(strstr(name, "_vlo")) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); HideDamagedAtomicCB(atomic, nil); return atomic; } RpAtomic* CVehicleModelInfo::SetAtomicRendererCB_Train(RpAtomic *atomic, void *data) { char *name; bool alpha; name = GetFrameNodeName(RpAtomicGetFrame(atomic)); alpha = false; RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); if(strstr(name, "_hi")){ if(alpha) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderTrainHiDetailAlphaCB); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderTrainHiDetailCB); }else if(strstr(name, "_vlo")) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); HideDamagedAtomicCB(atomic, nil); return atomic; } RpAtomic* CVehicleModelInfo::SetAtomicRendererCB_Boat(RpAtomic *atomic, void *data) { RpClump *clump; char *name; bool alpha; clump = (RpClump*)data; name = GetFrameNodeName(RpAtomicGetFrame(atomic)); alpha = false; RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); if(strcmp(name, "boat_hi") == 0 || !CGeneral::faststrncmp(name, "extra", 5)) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB_Boat); else if(strstr(name, "_hi")){ if(alpha) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_Boat); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB); }else if(strstr(name, "_lo")){ RpClumpRemoveAtomic(clump, atomic); RpAtomicDestroy(atomic); return atomic; // BUG: not done by gta }else if(strstr(name, "_vlo")) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleLoDetailCB_Boat); else{ if(alpha) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_Boat); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); } HideDamagedAtomicCB(atomic, nil); return atomic; } RpAtomic* CVehicleModelInfo::SetAtomicRendererCB_Heli(RpAtomic *atomic, void *data) { char *name; name = GetFrameNodeName(RpAtomicGetFrame(atomic)); if(strncmp(name, "toprotor", 8) == 0) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleRotorAlphaCB); else if(strncmp(name, "rearrotor", 9) == 0) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleTailRotorAlphaCB); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); return atomic; } RpAtomic* CVehicleModelInfo::SetAtomicRendererCB_RealHeli(RpAtomic *atomic, void *data) { RpClump *clump; char *name; bool alpha; clump = (RpClump*)data; name = GetFrameNodeName(RpAtomicGetFrame(atomic)); alpha = false; RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha); if(strncmp(name, "toprotor", 8) == 0) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleRotorAlphaCB); else if(strncmp(name, "rearrotor", 9) == 0) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleTailRotorAlphaCB); else if(strstr(name, "_hi") || !CGeneral::faststrncmp(name, "extra", 5)) { if(alpha || strncmp(name, "windscreen", 10) == 0) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB); }else if(strstr(name, "_lo")){ RpClumpRemoveAtomic(clump, atomic); RpAtomicDestroy(atomic); return atomic; // BUG: nil in gta }else if(strstr(name, "_vlo")) CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB); else CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); HideDamagedAtomicCB(atomic, nil); return atomic; } void CVehicleModelInfo::SetAtomicRenderCallbacks(void) { #ifdef GTA_TRAIN if(m_vehicleType == VEHICLE_TYPE_TRAIN) RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_Train, nil); else #endif if(m_vehicleType == VEHICLE_TYPE_HELI) RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_Heli, nil); else if(m_vehicleType == VEHICLE_TYPE_PLANE) RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_BigVehicle, nil); else if(m_vehicleType == VEHICLE_TYPE_BOAT) RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_Boat, m_clump); else if(mod_HandlingManager.GetHandlingData((tVehicleType)m_handlingId)->Flags & HANDLING_IS_HELI) RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_RealHeli, m_clump); else RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, m_clump); } RwObject* CVehicleModelInfo::SetAtomicFlagCB(RwObject *object, void *data) { RpAtomic *atomic = (RpAtomic*)object; assert(RwObjectGetType(object) == rpATOMIC); CVisibilityPlugins::SetAtomicFlag(atomic, (uintptr)data); return object; } RwObject* CVehicleModelInfo::ClearAtomicFlagCB(RwObject *object, void *data) { RpAtomic *atomic = (RpAtomic*)object; assert(RwObjectGetType(object) == rpATOMIC); CVisibilityPlugins::ClearAtomicFlag(atomic, (uintptr)data); return object; } RwObject* GetOkAndDamagedAtomicCB(RwObject *object, void *data) { RpAtomic *atomic = (RpAtomic*)object; if(CVisibilityPlugins::GetAtomicId(atomic) & ATOMIC_FLAG_OK) ((RpAtomic**)data)[0] = atomic; else if(CVisibilityPlugins::GetAtomicId(atomic) & ATOMIC_FLAG_DAM) ((RpAtomic**)data)[1] = atomic; return object; } void CVehicleModelInfo::PreprocessHierarchy(void) { int32 i; RwObjectNameIdAssocation *desc; RwFrame *f; RpAtomic *atomic; RwV3d *rwvec; desc = ms_vehicleDescs[m_vehicleType]; m_numDoors = 0; m_numComps = 0; for(i = 0; desc[i].name; i++){ RwObjectNameAssociation assoc; if((desc[i].flags & (VEHICLE_FLAG_COMP|VEHICLE_FLAG_POS)) == 0) continue; assoc.frame = nil; assoc.name = desc[i].name; RwFrameForAllChildren(RpClumpGetFrame(m_clump), FindFrameFromNameWithoutIdCB, &assoc); if(assoc.frame == nil) continue; if(desc[i].flags & VEHICLE_FLAG_DOOR) m_numDoors++; if(desc[i].flags & VEHICLE_FLAG_POS){ f = assoc.frame; rwvec = &m_positions[desc[i].hierId]; *rwvec = *RwMatrixGetPos(RwFrameGetMatrix(f)); for(f = RwFrameGetParent(f); f; f = RwFrameGetParent(f)) RwV3dTransformPoints(rwvec, rwvec, 1, RwFrameGetMatrix(f)); RwFrameDestroy(assoc.frame); }else{ atomic = (RpAtomic*)GetFirstObject(assoc.frame); RpClumpRemoveAtomic(m_clump, atomic); RwFrameRemoveChild(assoc.frame); SetVehicleComponentFlags(assoc.frame, desc[i].flags); m_comps[m_numComps++] = atomic; } } for(i = 0; desc[i].name; i++){ RwObjectIdAssociation assoc; if(desc[i].flags & (VEHICLE_FLAG_COMP|VEHICLE_FLAG_POS)) continue; assoc.frame = nil; assoc.id = desc[i].hierId; RwFrameForAllChildren(RpClumpGetFrame(m_clump), FindFrameFromIdCB, &assoc); if(assoc.frame == nil) continue; if(desc[i].flags & VEHICLE_FLAG_DOOR) m_numDoors++; if(desc[i].flags & VEHICLE_FLAG_COLLAPSE){ RpAtomic *okdam[2] = { nil, nil }; RwFrameForAllChildren(assoc.frame, CollapseFramesCB, assoc.frame); RwFrameUpdateObjects(assoc.frame); RwFrameForAllObjects(assoc.frame, GetOkAndDamagedAtomicCB, okdam); if(okdam[0] && okdam[1]) RpAtomicSetRenderCallBack(okdam[1], RpAtomicGetRenderCallBack(okdam[0])); } SetVehicleComponentFlags(assoc.frame, desc[i].flags); if(desc[i].flags & VEHICLE_FLAG_ADD_WHEEL){ if(m_wheelId == -1) RwFrameDestroy(assoc.frame); else{ RwV3d scale; atomic = (RpAtomic*)CModelInfo::GetModelInfo(m_wheelId)->CreateInstance(); RwFrameDestroy(RpAtomicGetFrame(atomic)); RpAtomicSetFrame(atomic, assoc.frame); RpClumpAddAtomic(m_clump, atomic); CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderWheelAtomicCB); scale.x = m_wheelScale; scale.y = m_wheelScale; scale.z = m_wheelScale; RwFrameScale(assoc.frame, &scale, rwCOMBINEPRECONCAT); } } } } void CVehicleModelInfo::SetVehicleComponentFlags(RwFrame *frame, uint32 flags) { tHandlingData *handling; handling = mod_HandlingManager.GetHandlingData((tVehicleType)m_handlingId); #define SETFLAGS(f) RwFrameForAllObjects(frame, SetAtomicFlagCB, (void*)(f)) if(flags & VEHICLE_FLAG_WINDSCREEN){ if(this == CModelInfo::GetModelInfo(MI_RHINO)) return; SETFLAGS(ATOMIC_FLAG_WINDSCREEN); } if(flags & VEHICLE_FLAG_ANGLECULL) SETFLAGS(ATOMIC_FLAG_ANGLECULL); if(flags & VEHICLE_FLAG_FRONT) SETFLAGS(ATOMIC_FLAG_FRONT); else if(flags & VEHICLE_FLAG_REAR && (handling->Flags & HANDLING_IS_VAN || (flags & (VEHICLE_FLAG_LEFT|VEHICLE_FLAG_RIGHT)) == 0)) SETFLAGS(ATOMIC_FLAG_REAR); else if(flags & VEHICLE_FLAG_LEFT) SETFLAGS(ATOMIC_FLAG_LEFT); else if(flags & VEHICLE_FLAG_RIGHT) SETFLAGS(ATOMIC_FLAG_RIGHT); if(flags & VEHICLE_FLAG_REARDOOR) SETFLAGS(ATOMIC_FLAG_REARDOOR); else if(flags & VEHICLE_FLAG_FRONTDOOR) SETFLAGS(ATOMIC_FLAG_FRONTDOOR); if(flags & VEHICLE_FLAG_DRAWLAST) SETFLAGS(ATOMIC_FLAG_DRAWLAST); } #define COMPRULE_RULE(comprule) (((comprule) >> 12) & 0xF) #define COMPRULE_COMPS(comprule) ((comprule) & 0xFFF) #define COMPRULE_COMPN(comps, n) (((comps) >> 4*(n)) & 0xF) #define COMPRULE2_RULE(comprule) (((comprule) >> (12+16)) & 0xF) #define COMPRULE2_COMPS(comprule) ((comprule >> 16) & 0xFFF) #define COMPRULE2_COMPN(comps, n) (((comps >> 16) >> 4*(n)) & 0xF) bool IsValidCompRule(int rule) { if(rule == 2) return CWeather::OldWeatherType == WEATHER_RAINY || CWeather::NewWeatherType == WEATHER_RAINY; return true; } int32 CountCompsInRule(int comps) { int32 n; for(n = 0; comps != 0; comps >>= 4) if((comps & 0xF) != 0xF) n++; return n; } int32 ChooseComponent(int32 rule, int32 comps) { int32 n; switch(rule){ // identical cases.... case 1: n = CGeneral::GetRandomNumberInRange(0, CountCompsInRule(comps)); return COMPRULE_COMPN(comps, n); case 2: // only valid in rain n = CGeneral::GetRandomNumberInRange(0, CountCompsInRule(comps)); return COMPRULE_COMPN(comps, n); case 3: n = CGeneral::GetRandomNumberInRange(0, 1+CountCompsInRule(comps)); if(n != 0) return COMPRULE_COMPN(comps, n-1); return -1; case 4: #ifdef FIX_BUGS return CGeneral::GetRandomNumberInRange(0, 6); #else return CGeneral::GetRandomNumberInRange(0, 5); #endif } return -1; } int32 GetListOfComponentsNotUsedByRules(uint32 comprules, int32 numComps, int32 *comps) { int32 i, n; int32 unused[6] = { 0, 1, 2, 3, 4, 5 }; // first comprule if(COMPRULE_RULE(comprules) && IsValidCompRule(COMPRULE_RULE(comprules))) for(i = 0; i < 3; i++){ n = COMPRULE_COMPN(comprules, i); if(n != 0xF) unused[n] = 0xF; } // second comprule comprules >>= 16; if(COMPRULE_RULE(comprules) && IsValidCompRule(COMPRULE_RULE(comprules))) for(i = 0; i < 3; i++){ n = COMPRULE_COMPN(comprules, i); if(n != 0xF) unused[n] = 0xF; } n = 0; for(i = 0; i < numComps; i++) if(unused[i] != 0xF) comps[n++] = unused[i]; return n; } int32 wheelIds[] = { CAR_WHEEL_LF, CAR_WHEEL_LB, CAR_WHEEL_RF, CAR_WHEEL_RB }; void CVehicleModelInfo::GetWheelPosn(int32 n, CVector &pos) { RwMatrix *m = RwFrameGetMatrix(GetFrameFromId(m_clump, wheelIds[n])); pos.x = RwMatrixGetPos(m)->x; pos.y = RwMatrixGetPos(m)->y; pos.z = RwMatrixGetPos(m)->z; } int32 CVehicleModelInfo::ChooseComponent(void) { int32 comp; int32 comps[8]; int32 n; comp = -1; if(ms_compsToUse[0] == -2){ if(COMPRULE_RULE(m_compRules) && IsValidCompRule(COMPRULE_RULE(m_compRules))) comp = ::ChooseComponent(COMPRULE_RULE(m_compRules), COMPRULE_COMPS(m_compRules)); else if(CGeneral::GetRandomNumberInRange(0, 3) < 2){ n = GetListOfComponentsNotUsedByRules(m_compRules, m_numComps, comps); if(n) comp = comps[(int)CGeneral::GetRandomNumberInRange(0, n)]; } }else{ comp = ms_compsToUse[0]; ms_compsToUse[0] = -2; } return comp; } int32 CVehicleModelInfo::ChooseSecondComponent(void) { int32 comp; int32 comps[8]; int32 n; comp = -1; if(ms_compsToUse[1] == -2){ if(COMPRULE2_RULE(m_compRules) && IsValidCompRule(COMPRULE2_RULE(m_compRules))) comp = ::ChooseComponent(COMPRULE2_RULE(m_compRules), COMPRULE2_COMPS(m_compRules)); else if(COMPRULE_RULE(m_compRules) && IsValidCompRule(COMPRULE_RULE(m_compRules)) && CGeneral::GetRandomNumberInRange(0, 3) < 2){ n = GetListOfComponentsNotUsedByRules(m_compRules, m_numComps, comps); if(n) comp = comps[(int)CGeneral::GetRandomNumberInRange(0, n)]; } }else{ comp = ms_compsToUse[1]; ms_compsToUse[1] = -2; } return comp; } struct editableMatCBData { CVehicleModelInfo *vehicle; int32 numMats1; int32 numMats2; }; RpMaterial* CVehicleModelInfo::GetEditableMaterialListCB(RpMaterial *material, void *data) { RwRGBA white = { 255, 255, 255, 255 }; const RwRGBA *col; editableMatCBData *cbdata; cbdata = (editableMatCBData*)data; col = RpMaterialGetColor(material); if(col->red == 0x3C && col->green == 0xFF && col->blue == 0){ cbdata->vehicle->m_materials1[cbdata->numMats1++] = material; RpMaterialSetColor(material, &white); }else if(col->red == 0xFF && col->green == 0 && col->blue == 0xAF){ cbdata->vehicle->m_materials2[cbdata->numMats2++] = material; RpMaterialSetColor(material, &white); } return material; } RpAtomic* CVehicleModelInfo::GetEditableMaterialListCB(RpAtomic *atomic, void *data) { RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), GetEditableMaterialListCB, data); return atomic; } static int maxFirstMaterials; static int maxSecondMaterials; void CVehicleModelInfo::FindEditableMaterialList(void) { editableMatCBData cbdata; int32 i; cbdata.vehicle = this; cbdata.numMats1 = 0; cbdata.numMats2 = 0; RpClumpForAllAtomics(m_clump, GetEditableMaterialListCB, &cbdata); for(i = 0; i < m_numComps; i++) GetEditableMaterialListCB(m_comps[i], &cbdata); m_materials1[cbdata.numMats1] = nil; m_materials2[cbdata.numMats2] = nil; maxFirstMaterials = Max(maxFirstMaterials, cbdata.numMats1); maxSecondMaterials = Max(maxSecondMaterials, cbdata.numMats2); m_currentColour1 = -1; m_currentColour2 = -1; } void CVehicleModelInfo::SetVehicleColour(uint8 c1, uint8 c2) { RwRGBA col, *colp; RpMaterial **matp; if(c1 != m_currentColour1){ col = ms_vehicleColourTable[c1]; for(matp = m_materials1; *matp; matp++){ colp = (RwRGBA*)RpMaterialGetColor(*matp); // get rid of const colp->red = col.red; colp->green = col.green; colp->blue = col.blue; } m_currentColour1 = c1; } if(c2 != m_currentColour2){ col = ms_vehicleColourTable[c2]; for(matp = m_materials2; *matp; matp++){ colp = (RwRGBA*)RpMaterialGetColor(*matp); // get rid of const colp->red = col.red; colp->green = col.green; colp->blue = col.blue; } m_currentColour2 = c2; } } void CVehicleModelInfo::ChooseVehicleColour(uint8 &col1, uint8 &col2) { if(m_numColours == 0 || gbBlackCars){ col1 = 0; col2 = 0; }else if(gbPinkCars){ col1 = 68; col2 = 68; }else{ m_lastColorVariation = (m_lastColorVariation+1) % m_numColours; col1 = m_colours1[m_lastColorVariation]; col2 = m_colours2[m_lastColorVariation]; if(m_numColours > 1){ CVehicle *veh = FindPlayerVehicle(); if(veh && CModelInfo::GetModelInfo(veh->GetModelIndex()) == this && veh->m_currentColour1 == col1 && veh->m_currentColour2 == col2){ m_lastColorVariation = (m_lastColorVariation+1) % m_numColours; col1 = m_colours1[m_lastColorVariation]; col2 = m_colours2[m_lastColorVariation]; } } } } void CVehicleModelInfo::AvoidSameVehicleColour(uint8 *col1, uint8 *col2) { int i, n; if(gbBlackCars){ *col1 = 0; *col2 = 0; }else if(gbPinkCars){ *col1 = 68; *col2 = 68; }else{ if(m_numColours > 1) for(i = 0; i < 8; i++){ if(*col1 != m_lastColour1 || *col2 != m_lastColour2) break; n = CGeneral::GetRandomNumberInRange(0, m_numColours); *col1 = m_colours1[n]; *col2 = m_colours2[n]; } m_lastColour1 = *col1; m_lastColour2 = *col2; } } // unused RwTexture* CreateCarColourTexture(uint8 r, uint8 g, uint8 b) { RwImage *img; RwRaster *ras; RwTexture *tex; RwUInt8 *pixels; RwInt32 width, height, depth, format; img = RwImageCreate(2, 2, 32); pixels = (RwUInt8*)RwMalloc(2*2*4); pixels[0] = r; pixels[1] = g; pixels[2] = b; pixels[3] = 0xFF; pixels[4] = r; pixels[5] = g; pixels[6] = b; pixels[7] = 0xFF; pixels[8] = r; pixels[9] = g; pixels[10] = b; pixels[11] = 0xFF; pixels[12] = r; pixels[13] = g; pixels[14] = b; pixels[15] = 0xFF; RwImageSetPixels(img, pixels); RwImageSetStride(img, 8); RwImageSetPalette(img, nil); RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &width, &height, &depth, &format); ras = RwRasterCreate(width, height, depth, format); RwRasterSetFromImage(ras, img); RwImageDestroy(img); RwFree(pixels); tex = RwTextureCreate(ras); RwTextureGetName(tex)[0] = '@'; return tex; } void CVehicleModelInfo::LoadVehicleColours(void) { int fd; int i; char line[1024]; int start, end; int section, numCols; enum { NONE, COLOURS, CARS }; int r, g, b; char name[64]; int colors[16]; int n; CFileMgr::ChangeDir("\\DATA\\"); fd = CFileMgr::OpenFile("CARCOLS.DAT", "r"); CFileMgr::ChangeDir("\\"); for(i = 0; i < 256; i++) ms_colourTextureTable[i] = nil; section = 0; numCols = 0; while(CFileMgr::ReadLine(fd, line, sizeof(line))){ // find first valid character in line for(start = 0; ; start++) if(line[start] > ' ' || line[start] == '\0' || line[start] == '\n') break; // find end of line for(end = start; ; end++){ if(line[end] == '\0' || line[end] == '\n') break; if(line[end] == ',' || line[end] == '\r') line[end] = ' '; } line[end] = '\0'; // empty line if(line[start] == '#' || line[start] == '\0') continue; if(section == NONE){ if(line[start] == 'c' && line[start + 1] == 'o' && line[start + 2] == 'l') section = COLOURS; if(line[start] == 'c' && line[start + 1] == 'a' && line[start + 2] == 'r') section = CARS; }else if(line[start] == 'e' && line[start + 1] == 'n' && line[start + 2] == 'd'){ section = NONE; }else if(section == COLOURS){ sscanf(&line[start], // BUG: games doesn't add start "%d %d %d", &r, &g, &b); ms_vehicleColourTable[numCols].red = r; ms_vehicleColourTable[numCols].green = g; ms_vehicleColourTable[numCols].blue = b; ms_vehicleColourTable[numCols].alpha = 0xFF; numCols++; }else if(section == CARS){ n = sscanf(&line[start], // BUG: games doesn't add start "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", name, &colors[0], &colors[1], &colors[2], &colors[3], &colors[4], &colors[5], &colors[6], &colors[7], &colors[8], &colors[9], &colors[10], &colors[11], &colors[12], &colors[13], &colors[14], &colors[15]); CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(name, nil); assert(mi); mi->m_numColours = (n-1)/2; for(i = 0; i < mi->m_numColours; i++){ mi->m_colours1[i] = colors[i*2 + 0]; mi->m_colours2[i] = colors[i*2 + 1]; } } } CFileMgr::CloseFile(fd); } void CVehicleModelInfo::DeleteVehicleColourTextures(void) { int i; for(i = 0; i < 256; i++){ if(ms_colourTextureTable[i]){ RwTextureDestroy(ms_colourTextureTable[i]); ms_colourTextureTable[i] = nil; } } } RpMaterial* CVehicleModelInfo::GetMatFXEffectMaterialCB(RpMaterial *material, void *data) { if(RpMatFXMaterialGetEffects(material) == rpMATFXEFFECTNULL) return material; *(int*)data = RpMatFXMaterialGetEffects(material); return nil; } RpMaterial* CVehicleModelInfo::SetDefaultEnvironmentMapCB(RpMaterial *material, void *data) { if(RpMatFXMaterialGetEffects(material) == rpMATFXEFFECTENVMAP){ RpMatFXMaterialSetEnvMapFrame(material, pMatFxIdentityFrame); if(RpMaterialGetTexture(material) == nil) RpMaterialSetTexture(material, gpWhiteTexture); RpMatFXMaterialSetEffects(material, rpMATFXEFFECTENVMAP); #ifndef PS2_MATFX float coef = RpMatFXMaterialGetEnvMapCoefficient(material); coef *= 0.25f; // Tone down a bit for PC RpMatFXMaterialSetEnvMapCoefficient(material, coef); #endif } return material; } RpAtomic* CVehicleModelInfo::SetEnvironmentMapCB(RpAtomic *atomic, void *data) { int fx; RpGeometry *geo; geo = RpAtomicGetGeometry(atomic); fx = rpMATFXEFFECTNULL; RpGeometryForAllMaterials(geo, GetMatFXEffectMaterialCB, &fx); if(fx != rpMATFXEFFECTNULL){ RpMatFXAtomicEnableEffects(atomic); RpGeometryForAllMaterials(geo, SetDefaultEnvironmentMapCB, data); } return atomic; } void CVehicleModelInfo::SetEnvironmentMap(void) { CSimpleModelInfo *wheelmi; int32 i; if(pMatFxIdentityFrame == nil){ RwV3d axis = { 1.0f, 0.0f, 0.0f }; pMatFxIdentityFrame = RwFrameCreate(); RwMatrixRotate(RwFrameGetMatrix(pMatFxIdentityFrame), &axis, 60.0f, rwCOMBINEREPLACE); RwFrameUpdateObjects(pMatFxIdentityFrame); RwFrameGetLTM(pMatFxIdentityFrame); } RpClumpForAllAtomics(m_clump, SetEnvironmentMapCB, nil); if(m_wheelId != -1){ wheelmi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(m_wheelId); for(i = 0; i < wheelmi->m_numAtomics; i++) SetEnvironmentMapCB(wheelmi->m_atomics[i], nil); } #ifdef EXTENDED_PIPELINES CustomPipes::AttachVehiclePipe(m_clump); #endif } void CVehicleModelInfo::LoadEnvironmentMaps(void) { int32 txdslot; txdslot = CTxdStore::FindTxdSlot("particle"); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(txdslot); if(gpWhiteTexture == nil){ gpWhiteTexture = RwTextureRead("white", nil); RwTextureGetName(gpWhiteTexture)[0] = '@'; RwTextureSetFilterMode(gpWhiteTexture, rwFILTERLINEAR); } CTxdStore::PopCurrentTxd(); } void CVehicleModelInfo::ShutdownEnvironmentMaps(void) { RwTextureDestroy(gpWhiteTexture); gpWhiteTexture = nil; RwFrameDestroy(pMatFxIdentityFrame); pMatFxIdentityFrame = nil; } int CVehicleModelInfo::GetMaximumNumberOfPassengersFromNumberOfDoors(int id) { int n; switch(id){ case MI_TRAIN: n = 3; break; case MI_FIRETRUCK: n = 2; break; case MI_HUNTER: n = 1; break; default: n = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(id))->m_numDoors; } if(n == 0) return id == MI_RCBANDIT || id == MI_PIZZABOY || id == MI_BAGGAGE ? 0 : 1; if(id == MI_COACH) return 8; return n - 1; } ================================================ FILE: src/modelinfo/VehicleModelInfo.h ================================================ #pragma once #include "ClumpModelInfo.h" enum { NUM_FIRST_MATERIALS = 24, NUM_SECOND_MATERIALS = 20, NUM_VEHICLE_COLOURS = 8, }; enum { ATOMIC_FLAG_NONE = 0x0, ATOMIC_FLAG_OK = 0x1, ATOMIC_FLAG_DAM = 0x2, ATOMIC_FLAG_LEFT = 0x4, ATOMIC_FLAG_RIGHT = 0x8, ATOMIC_FLAG_FRONT = 0x10, ATOMIC_FLAG_REAR = 0x20, ATOMIC_FLAG_DRAWLAST = 0x40, ATOMIC_FLAG_WINDSCREEN = 0x80, ATOMIC_FLAG_ANGLECULL = 0x100, ATOMIC_FLAG_REARDOOR = 0x200, ATOMIC_FLAG_FRONTDOOR = 0x400, ATOMIC_FLAG_NOCULL = 0x800, }; enum eVehicleType { VEHICLE_TYPE_CAR, VEHICLE_TYPE_BOAT, VEHICLE_TYPE_TRAIN, VEHICLE_TYPE_HELI, VEHICLE_TYPE_PLANE, VEHICLE_TYPE_BIKE, NUM_VEHICLE_TYPES }; enum eCarPositions { CAR_POS_HEADLIGHTS, CAR_POS_TAILLIGHTS, CAR_POS_FRONTSEAT, CAR_POS_BACKSEAT, CAR_POS_EXHAUST }; enum eBoatPositions { BOAT_POS_FRONTSEAT }; enum eTrainPositions { TRAIN_POS_LIGHT_FRONT, TRAIN_POS_LIGHT_REAR, TRAIN_POS_LEFT_ENTRY, TRAIN_POS_MID_ENTRY, TRAIN_POS_RIGHT_ENTRY }; enum ePlanePositions { PLANE_POS_LIGHT_LEFT, PLANE_POS_LIGHT_RIGHT, PLANE_POS_LIGHT_TAIL, }; enum { NUM_VEHICLE_POSITIONS = 5 }; class CVehicleModelInfo : public CClumpModelInfo { public: uint8 m_lastColour1; uint8 m_lastColour2; char m_gameName[10]; int32 m_vehicleType; float m_wheelScale; union { int16 m_wheelId; int16 m_planeLodId; }; int16 m_handlingId; int8 m_numDoors; int8 m_vehicleClass; int8 m_level; int8 m_numComps; int16 m_frequency; CVector m_positions[NUM_VEHICLE_POSITIONS]; uint32 m_compRules; float m_bikeSteerAngle; RpMaterial *m_materials1[NUM_FIRST_MATERIALS]; RpMaterial *m_materials2[NUM_SECOND_MATERIALS]; uint8 m_colours1[NUM_VEHICLE_COLOURS]; uint8 m_colours2[NUM_VEHICLE_COLOURS]; uint8 m_numColours; uint8 m_lastColorVariation; uint8 m_currentColour1; uint8 m_currentColour2; RpAtomic *m_comps[6]; // This is stupid, CClumpModelInfo already has it! union { int32 m_animFileIndex; char *m_animFileName; }; static int8 ms_compsToUse[2]; static int8 ms_compsUsed[2]; static RwRGBA ms_vehicleColourTable[256]; static RwTexture *ms_colourTextureTable[256]; static RwObjectNameIdAssocation *ms_vehicleDescs[NUM_VEHICLE_TYPES]; CVehicleModelInfo(void); void DeleteRwObject(void); RwObject *CreateInstance(void); void SetClump(RpClump *); void SetAnimFile(const char *file); void ConvertAnimFileIndex(void); int GetAnimFileIndex(void) { return m_animFileIndex; } static RwFrame *CollapseFramesCB(RwFrame *frame, void *data); static RwObject *MoveObjectsCB(RwObject *object, void *data); static RpAtomic *HideDamagedAtomicCB(RpAtomic *atomic, void *data); static RpAtomic *HideAllComponentsAtomicCB(RpAtomic *atomic, void *data); static RpMaterial *HasAlphaMaterialCB(RpMaterial *material, void *data); static RpAtomic *SetAtomicRendererCB(RpAtomic *atomic, void *data); static RpAtomic *SetAtomicRendererCB_BigVehicle(RpAtomic *atomic, void *data); static RpAtomic *SetAtomicRendererCB_Train(RpAtomic *atomic, void *data); static RpAtomic *SetAtomicRendererCB_Boat(RpAtomic *atomic, void *data); static RpAtomic *SetAtomicRendererCB_Heli(RpAtomic *atomic, void *data); static RpAtomic *SetAtomicRendererCB_RealHeli(RpAtomic *atomic, void *data); void SetAtomicRenderCallbacks(void); static RwObject *SetAtomicFlagCB(RwObject *object, void *data); static RwObject *ClearAtomicFlagCB(RwObject *atomic, void *data); void SetVehicleComponentFlags(RwFrame *frame, uint32 flags); void PreprocessHierarchy(void); void GetWheelPosn(int32 n, CVector &pos); const CVector &GetFrontSeatPosn(void) { return m_vehicleType == VEHICLE_TYPE_BOAT ? m_positions[BOAT_POS_FRONTSEAT] : m_positions[CAR_POS_FRONTSEAT]; } int32 ChooseComponent(void); int32 ChooseSecondComponent(void); static RpMaterial *GetEditableMaterialListCB(RpMaterial *material, void *data); static RpAtomic *GetEditableMaterialListCB(RpAtomic *atomic, void *data); void FindEditableMaterialList(void); void SetVehicleColour(uint8 c1, uint8 c2); void ChooseVehicleColour(uint8 &col1, uint8 &col2); void AvoidSameVehicleColour(uint8 *col1, uint8 *col2); static void LoadVehicleColours(void); static void DeleteVehicleColourTextures(void); static RpAtomic *SetEnvironmentMapCB(RpAtomic *atomic, void *data); static RpMaterial *SetDefaultEnvironmentMapCB(RpMaterial *material, void *data); static RpMaterial *GetMatFXEffectMaterialCB(RpMaterial *material, void *data); void SetEnvironmentMap(void); static void LoadEnvironmentMaps(void); static void ShutdownEnvironmentMaps(void); static int GetMaximumNumberOfPassengersFromNumberOfDoors(int id); static void SetComponentsToUse(int8 c1, int8 c2) { ms_compsToUse[0] = c1; ms_compsToUse[1] = c2; } }; extern bool gbBlackCars; extern bool gbPinkCars; ================================================ FILE: src/modelinfo/WeaponModelInfo.cpp ================================================ #include "common.h" #include "ModelInfo.h" #include "AnimManager.h" #include "VisibilityPlugins.h" void CWeaponModelInfo::SetAnimFile(const char *file) { if(strcasecmp(file, "null") == 0) return; m_animFileName = new char[strlen(file)+1]; strcpy(m_animFileName, file); } void CWeaponModelInfo::ConvertAnimFileIndex(void) { if(m_animFileIndex != -1){ // we have a string pointer in that union int32 index = CAnimManager::GetAnimationBlockIndex(m_animFileName); delete[] m_animFileName; m_animFileIndex = index; } } void CWeaponModelInfo::Init(void) { CSimpleModelInfo::Init(); SetWeaponInfo(0); } void CWeaponModelInfo::SetWeaponInfo(int32 weaponId) { m_atomics[2] = (RpAtomic*)weaponId; } eWeaponType CWeaponModelInfo::GetWeaponInfo(void) { return (eWeaponType)(uintptr)m_atomics[2]; } void CWeaponModelInfo::SetAtomic(int n, RpAtomic *atomic) { CSimpleModelInfo::SetAtomic(n, atomic); CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderWeaponCB); } ================================================ FILE: src/modelinfo/WeaponModelInfo.h ================================================ #pragma once #include "SimpleModelInfo.h" #include "WeaponType.h" class CWeaponModelInfo : public CSimpleModelInfo { union { int32 m_animFileIndex; char *m_animFileName; }; public: CWeaponModelInfo(void) : CSimpleModelInfo(MITYPE_WEAPON) { m_animFileIndex = -1; } virtual void SetAnimFile(const char *file); virtual void ConvertAnimFileIndex(void); virtual int GetAnimFileIndex(void) { return m_animFileIndex; } virtual void SetAtomic(int n, RpAtomic *atomic); void Init(void); void SetWeaponInfo(int32 weaponId); eWeaponType GetWeaponInfo(void); }; ================================================ FILE: src/objects/CutsceneObject.cpp ================================================ #include "common.h" #include "main.h" #include "RwHelper.h" #include "Lights.h" #include "PointLights.h" #include "RpAnimBlend.h" #include "AnimBlendClumpData.h" #include "Bones.h" #include "Renderer.h" #include "ModelIndices.h" #include "Shadows.h" #include "Timecycle.h" #include "CutsceneShadow.h" #include "CutsceneObject.h" #include "ModelIndices.h" #include "RpAnimBlend.h" CCutsceneObject::CCutsceneObject(void) { SetStatus(STATUS_SIMPLE); bUsesCollision = false; bStreamingDontDelete = true; ObjectCreatedBy = CUTSCENE_OBJECT; m_fMass = 1.0f; m_fTurnMass = 1.0f; m_pAttachTo = nil; m_pAttachmentObject = nil; m_pShadow = nil; } CCutsceneObject::~CCutsceneObject(void) { if ( m_pShadow ) { delete m_pShadow; m_pShadow = nil; } } void CCutsceneObject::SetModelIndex(uint32 id) { CEntity::SetModelIndex(id); assert(RwObjectGetType(m_rwObject) == rpCLUMP); RpAnimBlendClumpInit((RpClump*)m_rwObject); (*RPANIMBLENDCLUMPDATA(m_rwObject))->velocity3d = &m_vecMoveSpeed; (*RPANIMBLENDCLUMPDATA(m_rwObject))->frames[0].flag |= AnimBlendFrameData::VELOCITY_EXTRACTION_3D; } void CCutsceneObject::CreateShadow(void) { if ( IsPedModel(GetModelIndex()) ) { m_pShadow = new CCutsceneShadow(); if (!m_pShadow->IsInitialized()) m_pShadow->Create(m_rwObject, 6, true, 4, true); } } void CCutsceneObject::ProcessControl(void) { CPhysical::ProcessControl(); if ( m_pAttachTo ) { if ( m_pAttachmentObject ) GetMatrix() = CMatrix((RwMatrix*)m_pAttachTo); else GetMatrix() = CMatrix(RwFrameGetLTM((RwFrame*)m_pAttachTo)); } else { if(CTimer::GetTimeStep() < 1/100.0f) m_vecMoveSpeed *= 100.0f; else m_vecMoveSpeed *= 1.0f/CTimer::GetTimeStep(); ApplyMoveSpeed(); } } static RpMaterial* MaterialSetAlpha(RpMaterial *material, void *data) { ((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data; return material; } void CCutsceneObject::PreRender(void) { if ( m_pAttachTo ) { if ( m_pAttachmentObject ) { m_pAttachmentObject->UpdateRpHAnim(); GetMatrix() = CMatrix((RwMatrix*)m_pAttachTo); } else GetMatrix() = CMatrix(RwFrameGetLTM((RwFrame*)m_pAttachTo)); if ( RwObjectGetType(m_rwObject) == rpCLUMP && IsClumpSkinned(GetClump()) ) { RpAtomic *atomic = GetFirstAtomic(GetClump()); atomic->boundingSphere.center = (*RPANIMBLENDCLUMPDATA(GetClump()))->frames[0].hanimFrame->t; } } if ( RwObjectGetType(m_rwObject) == rpCLUMP ) UpdateRpHAnim(); if(IsPedModel(GetModelIndex())) { if ( m_pShadow == nil ) { CShadows::StoreShadowForPedObject(this, CTimeCycle::m_fShadowDisplacementX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowDisplacementY[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowFrontX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowFrontY[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowSideX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowSideY[CTimeCycle::m_CurrentStoredValue]); } else { if ( m_pShadow->IsInitialized() ) m_pShadow->UpdateForCutscene(); CShadows::StoreShadowForCutscenePedObject(this, CTimeCycle::m_fShadowDisplacementX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowDisplacementY[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowFrontX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowFrontY[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowSideX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowSideY[CTimeCycle::m_CurrentStoredValue]); } // For some reason xbox/android limbs are transparent here... RpGeometry *geometry = RpAtomicGetGeometry(GetFirstAtomic(GetClump())); RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); RpGeometryForAllMaterials(geometry, MaterialSetAlpha, (void*)255); } } void CCutsceneObject::Render(void) { SetCullMode(rwCULLMODECULLNONE); CObject::Render(); SetCullMode(rwCULLMODECULLBACK); } bool CCutsceneObject::SetupLighting(void) { ActivateDirectional(); SetAmbientColoursForPedsCarsAndObjects(); if(bRenderScorched){ WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); }else{ CVector coors = GetPosition(); float lighting = CPointLights::GenerateLightsAffectingObject(&coors); if(lighting != 1.0f){ SetAmbientAndDirectionalColours(lighting); return true; } } return false; } void CCutsceneObject::RemoveLighting(bool reset) { CRenderer::RemoveVehiclePedLights(this, reset); } ================================================ FILE: src/objects/CutsceneObject.h ================================================ #pragma once #include "Object.h" class CCutsceneShadow; class CCutsceneObject : public CObject { public: CCutsceneShadow *m_pShadow; void *m_pAttachTo; CObject *m_pAttachmentObject; CCutsceneObject(void); ~CCutsceneObject(void); void SetModelIndex(uint32 id); void CreateShadow(void); void ProcessControl(void); void PreRender(void); void Render(void); bool SetupLighting(void); void RemoveLighting(bool reset); }; ================================================ FILE: src/objects/DummyObject.cpp ================================================ #include "common.h" #include "DummyObject.h" #include "Pools.h" CDummyObject::CDummyObject(CObject *obj) { SetModelIndexNoCreate(obj->GetModelIndex()); if(obj->m_rwObject) AttachToRwObject(obj->m_rwObject); obj->DetachFromRwObject(); m_level = obj->m_level; m_area = obj->m_area; } ================================================ FILE: src/objects/DummyObject.h ================================================ #pragma once #include "Dummy.h" class CObject; class CDummyObject : public CDummy { public: CDummyObject(void) {} CDummyObject(CObject *obj); }; ================================================ FILE: src/objects/Object.cpp ================================================ #include "common.h" #include "main.h" #include "Lights.h" #include "Pools.h" #include "Radar.h" #include "Object.h" #include "DummyObject.h" #include "Particle.h" #include "General.h" #include "ObjectData.h" #include "World.h" #include "Floater.h" #include "soundlist.h" #include "WaterLevel.h" #include "Timecycle.h" #include "Stats.h" #include "SpecialFX.h" #define BEACHBALL_MAX_SCORE 250 // the proportion of the ball speed compared to the player speed when it hits the player #define BEACHBALL_SPEED_PROPORTION 0.4f int16 CObject::nNoTempObjects; //int16 CObject::nBodyCastHealth = 1000; float CObject::fDistToNearestTree; void *CObject::operator new(size_t sz) { return CPools::GetObjectPool()->New(); } void *CObject::operator new(size_t sz, int handle) { return CPools::GetObjectPool()->New(handle);}; void CObject::operator delete(void *p, size_t sz) { CPools::GetObjectPool()->Delete((CObject*)p); } void CObject::operator delete(void *p, int handle) { CPools::GetObjectPool()->Delete((CObject*)p); } CObject::CObject(void) { m_type = ENTITY_TYPE_OBJECT; m_fUprootLimit = 0.0f; m_nCollisionDamageEffect = 0; m_nSpecialCollisionResponseCases = COLLRESPONSE_NONE; m_bCameraToAvoidThisObject = false; ObjectCreatedBy = UNKNOWN_OBJECT; m_nEndOfLifeTime = 0; // m_nRefModelIndex = -1; // duplicate // bUseVehicleColours = false; // duplicate m_colour2 = 0; m_colour1 = m_colour2; m_nBonusValue = 0; m_nCostValue = 0; bIsPickup = false; bPickupObjWithMessage = false; bOutOfStock = false; bGlassCracked = false; bGlassBroken = false; bHasBeenDamaged = false; m_nRefModelIndex = -1; bUseVehicleColours = false; // bIsStreetLight = false; // duplicate m_pCurSurface = nil; m_pCollidingEntity = nil; m_nBeachballBounces = 0; bIsStreetLight = false; m_area = AREA_EVERYWHERE; } CObject::CObject(int32 mi, bool createRW) { if (createRW) SetModelIndex(mi); else SetModelIndexNoCreate(mi); Init(); } CObject::CObject(CDummyObject *dummy) { SetModelIndexNoCreate(dummy->GetModelIndex()); if (dummy->m_rwObject) AttachToRwObject(dummy->m_rwObject); else GetMatrix() = dummy->GetMatrix(); m_objectMatrix = dummy->GetMatrix(); dummy->DetachFromRwObject(); Init(); m_level = dummy->m_level; m_area = dummy->m_area; } CObject::~CObject(void) { CRadar::ClearBlipForEntity(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(this)); if (m_nRefModelIndex != -1) CModelInfo::GetModelInfo(m_nRefModelIndex)->RemoveRef(); if (ObjectCreatedBy == TEMP_OBJECT && nNoTempObjects != 0) nNoTempObjects--; } void CObject::ProcessControl(void) { CVector point, impulse; if (m_nCollisionDamageEffect) ObjectDamage(m_fDamageImpulse); CPhysical::ProcessControl(); if (mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)) { bIsInWater = true; SetIsStatic(false); ApplyMoveForce(impulse); ApplyTurnForce(impulse, point); float fTimeStep = Pow(0.97f, CTimer::GetTimeStep()); m_vecMoveSpeed *= fTimeStep; m_vecTurnSpeed *= fTimeStep; } int16 mi = GetModelIndex(); if ((mi == MI_EXPLODINGBARREL || mi == MI_PETROLPUMP || mi == MI_PETROLPUMP2) && bHasBeenDamaged && bIsVisible && (CGeneral::GetRandomNumber() & 0x1F) == 10) { bExplosionProof = true; bIsVisible = false; bUsesCollision = false; bAffectedByGravity = false; m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); } if (mi == MI_RCBOMB) { float fTurnForce = -(m_fTurnMass / 20.0f); CPhysical::ApplyTurnForce(m_vecMoveSpeed * fTurnForce, -GetForward()); float fScalar = 1.0f - m_vecMoveSpeed.MagnitudeSqr() / 5.0f; float fScalarTimed = Pow(fScalar, CTimer::GetTimeStep()); m_vecMoveSpeed *= fScalarTimed; } if (mi == MI_BEACHBALL) { float fTimeStep = Pow(0.95f, CTimer::GetTimeStep()); float fPreviousVecSpeedMag = m_vecMoveSpeed.Magnitude2D(); m_vecMoveSpeed.x *= fTimeStep; m_vecMoveSpeed.y *= fTimeStep; m_vecMoveSpeed.z += fPreviousVecSpeedMag - m_vecMoveSpeed.Magnitude2D(); if (!FindPlayerVehicle()) { CVector distance; distance.x = FindPlayerCoors().x - GetPosition().x; distance.y = FindPlayerCoors().y - GetPosition().y; distance.z = FindPlayerCoors().z - GetPosition().z; if (distance.z > 0.0 && distance.z < 1.5f && distance.Magnitude2D() < 1.0f) { CVector playerSpeed = FindPlayerSpeed(); if (fPreviousVecSpeedMag < 0.05f && playerSpeed.Magnitude() > 0.1f) { playerSpeed.z = 0.0f; playerSpeed.Normalise(); playerSpeed.z = 0.3f; m_vecMoveSpeed = CVector( playerSpeed.x * BEACHBALL_SPEED_PROPORTION, playerSpeed.y * BEACHBALL_SPEED_PROPORTION, 0.3f * BEACHBALL_SPEED_PROPORTION ); PlayOneShotScriptObject(SCRIPT_SOUND_HIT_BALL, GetPosition()); m_vecTurnSpeed += CVector( ((CGeneral::GetRandomNumber() % 16) - 7) / 10.0f, ((CGeneral::GetRandomNumber() % 16) - 7) / 10.0f, 0.0f); if (m_nBeachballBounces > 0) { m_nBeachballBounces++; } if (m_nBeachballBounces > 0) { sprintf(gString, "%d", m_nBeachballBounces); CMoneyMessages::RegisterOne(GetPosition(), gString, 255, 50, 0, 0.6f, 0.5f); CStats::RegisterHighestScore(3, m_nBeachballBounces); } } } if (distance.z > -1.05 && distance.z < -0.6 && m_vecMoveSpeed.z < 0.0f && distance.Magnitude2D() < 0.9f) { m_vecMoveSpeed.x += (CGeneral::GetRandomNumber() % 8 - 3) / 100.0f; m_vecMoveSpeed.y += (CGeneral::GetRandomNumber() % 8 - 3) / 100.0f; m_vecMoveSpeed.z = Max(m_vecMoveSpeed.z + 0.3f, 0.2f); PlayOneShotScriptObject(SCRIPT_SOUND_HIT_BALL, GetPosition()); m_vecTurnSpeed.x += (CGeneral::GetRandomNumber() % 16 - 7) / 10.0f; m_vecTurnSpeed.y += (CGeneral::GetRandomNumber() % 16 - 7) / 10.0f; m_nBeachballBounces++; m_nBeachballBounces = Min(m_nBeachballBounces, BEACHBALL_MAX_SCORE); sprintf(gString, "%d", m_nBeachballBounces); CMoneyMessages::RegisterOne(GetPosition(), gString, 255, 50, 0, 0.6f, 0.5f); CStats::RegisterHighestScore(3, m_nBeachballBounces); } } } if (bIsBIGBuilding) { bIsInSafePosition = true; } } void CObject::Teleport(CVector vecPos) { CWorld::Remove(this); m_matrix.GetPosition() = vecPos; m_matrix.UpdateRW(); UpdateRwFrame(); CWorld::Add(this); } void CObject::Render(void) { if (bDoNotRender) return; if (m_nRefModelIndex != -1 && ObjectCreatedBy == TEMP_OBJECT && bUseVehicleColours) { CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(m_nRefModelIndex); assert(mi->GetModelType() == MITYPE_VEHICLE); mi->SetVehicleColour(m_colour1, m_colour2); } float red = (0.8f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj()) * 165.75f; float green = (0.8f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj()) * 165.75f; float blue = (0.8f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj()) * 165.75f; red = clamp(red, 0.0f, 255.0f); green = clamp(green, 0.0f, 255.0f); blue = clamp(blue, 0.0f, 255.0f); int alpha = CGeneral::GetRandomNumberInRange(196, 225); RwRGBA color = { (uint8)red, (uint8)green, (uint8)blue, (uint8)alpha }; if (this->GetModelIndex() == MI_YT_MAIN_BODY) { float moveSpeedMagnitude = this->GetMoveSpeed().Magnitude(); if (moveSpeedMagnitude > 0.0f) { float scaleMax = GetColModel()->boundingBox.max.y * 0.85f; CVector dir = this->GetMoveSpeed() + 0.3f * this->GetRight() - 0.5f * this->GetForward(); dir.z += 0.05f * moveSpeedMagnitude; CVector pos = scaleMax * this->GetForward() + 2.25f * this->GetRight() + this->GetPosition(); float fWaterLevel; CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); pos.z = fWaterLevel + 0.75f; CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 1.2f * moveSpeedMagnitude, color, CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); float scaleMin = GetColModel()->boundingBox.min.y; dir = this->GetMoveSpeed() - 0.5f * this->GetForward(); dir.z += 0.05f * moveSpeedMagnitude; pos = scaleMin * this->GetForward() + 4.5f * this->GetRight() + this->GetPosition(); CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); pos.z = fWaterLevel + 0.55f; CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); pos = scaleMin * 1.1f * this->GetForward() + 2.25f * this->GetRight() + this->GetPosition(); CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); pos.z = fWaterLevel + 0.55f; CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); pos = scaleMin * 1.1f * this->GetForward() - 0.05f * this->GetRight() + this->GetPosition(); CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); pos.z = fWaterLevel + 0.55f; CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); } } if (this->GetModelIndex() == MI_YT_MAIN_BODY2) { float moveSpeedMagnitude = this->GetMoveSpeed().Magnitude(); if (moveSpeedMagnitude > 0.0f) { float scaleMax = GetColModel()->boundingBox.max.y * 0.85f; CVector dir = this->GetMoveSpeed() - 0.3f * this->GetRight() - 0.5f * this->GetForward(); dir.z += 0.05f * moveSpeedMagnitude; CVector pos = scaleMax * this->GetForward() - 2.25f * this->GetRight() + this->GetPosition(); float fWaterLevel; CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); pos.z = fWaterLevel + 0.75f; CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 1.2f * moveSpeedMagnitude, color, CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); float scaleMin = GetColModel()->boundingBox.min.y; dir = this->GetMoveSpeed() - 0.5f * this->GetForward(); dir.z += 0.05f * moveSpeedMagnitude; pos = scaleMin * this->GetForward() - 4.5f * this->GetRight() + this->GetPosition(); CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); pos.z = fWaterLevel + 0.55f; CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); pos = scaleMin * 1.1f * this->GetForward() - 2.25f * this->GetRight() + this->GetPosition(); CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &fWaterLevel, true); pos.z = fWaterLevel + 0.55f; CParticle::AddParticle(PARTICLE_BOAT_SPLASH, pos, dir, nil, 0.9f, color, CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, 0); } } CEntity::Render(); } bool CObject::SetupLighting(void) { if (bRenderScorched) { WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); return true; } else if (bIsPickup) { SetFullAmbient(); return true; } else if (bIsWeapon) { ActivateDirectional(); SetAmbientColoursForPedsCarsAndObjects(); return true; } return false; } void CObject::RemoveLighting(bool reset) { if (reset) { SetAmbientColours(); DeActivateDirectional(); } } void CObject::ObjectDamage(float amount) { if (!m_nCollisionDamageEffect || !bUsesCollision) return; static int8 nFrameGen = 0; bool bBodyCastDamageEffect = false; #if 0 if (GetModelIndex() == MI_BODYCAST) { if (amount > 50.0f) nBodyCastHealth = (int16)(nBodyCastHealth - 0.5f * amount); if (nBodyCastHealth < 0) nBodyCastHealth = 0; if (nBodyCastHealth < 200) bBodyCastDamageEffect = true; amount = 0.0f; } #endif if ((amount * m_fCollisionDamageMultiplier > 150.0f || bBodyCastDamageEffect) && m_nCollisionDamageEffect) { const CVector &vecPos = m_matrix.GetPosition(); const float fDirectionZ = 0.0002f * amount; switch (m_nCollisionDamageEffect) { case DAMAGE_EFFECT_CHANGE_MODEL: bRenderDamaged = true; return; case DAMAGE_EFFECT_SPLIT_MODEL: return; case DAMAGE_EFFECT_SMASH_AND_DAMAGE_TRAFFICLIGHTS: { static RwRGBA debrisColor = { 0xc8,0xc8,0xc8,0xff }; if (bRenderDamaged) { break; } bRenderDamaged = true; CVector min = 0.85f * GetColModel()->boundingBox.min; CVector max = 0.85f * GetColModel()->boundingBox.max; min.z = max.z; min = GetMatrix() * min; max = GetMatrix() * max; CVector temp = (max - min) * 0.02f; for (int32 i = 0; i < 50; i++) { CVector vecDir = CVector( CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(0.10f, 0.25f) ); ++nFrameGen; int32 currentFrame = nFrameGen & 3; CVector pos = min + temp * (float)i; float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); float fColorFactor = CGeneral::GetRandomNumberInRange(0.6f, 1.2f); RwRGBA color = debrisColor; color.red *= fColorFactor; color.green *= fColorFactor; int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-0.40f, 0.40f); CParticle::AddParticle(PARTICLE_CAR_DEBRIS, pos, vecDir, nil, fSize, color, nRotationSpeed, 0, currentFrame, 0); } PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, min); break; } case DAMAGE_EFFECT_CHANGE_THEN_SMASH: { if (!bRenderDamaged) { bRenderDamaged = true; return; } // fall through } case DAMAGE_EFFECT_SMASH_COMPLETELY: { bIsVisible = false; bUsesCollision = false; if (!GetIsStatic()) { RemoveFromMovingList(); } SetIsStatic(true); bExplosionProof = true; SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); break; } case DAMAGE_EFFECT_SMASH_CARDBOARD_COMPLETELY: case DAMAGE_EFFECT_SMASH_YELLOW_TARGET_COMPLETELY: { bIsVisible = false; bUsesCollision = false; if (!GetIsStatic()) { RemoveFromMovingList(); } SetIsStatic(true); bExplosionProof = true; SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); const RwRGBA color = { 96, 48, 0, 255 }; for (int32 i = 0; i < 25; i++) { CVector vecDir = CVector( CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ ); ++nFrameGen; int32 currentFrame = nFrameGen & 3; RwRGBA randomColor = color; switch (m_nCollisionDamageEffect) { case DAMAGE_EFFECT_SMASH_CARDBOARD_COMPLETELY: { float fRandom = CGeneral::GetRandomNumberInRange(0.01f, 1.0f); randomColor.red *= fRandom; randomColor.green *= fRandom; randomColor.blue *= fRandom; break; } case DAMAGE_EFFECT_SMASH_YELLOW_TARGET_COMPLETELY: { randomColor.red = 0xff; randomColor.green = 0xfc; break; } } float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, randomColor, nRotationSpeed, 0, currentFrame, 0); } PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_2, vecPos); break; } case DAMAGE_EFFECT_SMASH_WOODENBOX_COMPLETELY: { bIsVisible = false; bUsesCollision = false; if (!GetIsStatic()) { RemoveFromMovingList(); } SetIsStatic(true); bExplosionProof = true; SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); static const RwRGBA color = { 128, 128, 128, 255 }; CVector position = GetPosition(); for (int32 i = 0; i < 45; i++) { CVector vecDir = CVector( CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ ); ++nFrameGen; int32 currentFrame = nFrameGen & 3; float fRandom = CGeneral::GetRandomNumberInRange(0.5f, 1.0f); RwRGBA randomColor = { uint8(color.red * fRandom), uint8(color.green * fRandom), uint8(color.blue * fRandom), color.alpha }; float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, randomColor, nRotationSpeed, 0, currentFrame, 0); } PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_1, vecPos); break; } case DAMAGE_EFFECT_SMASH_TRAFFICCONE_COMPLETELY: case DAMAGE_EFFECT_BURST_BEACHBALL: { bIsVisible = false; bUsesCollision = false; if (!GetIsStatic()) { RemoveFromMovingList(); } SetIsStatic(true); bExplosionProof = true; SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); const RwRGBA color1 = { 200, 0, 0, 255 }; const RwRGBA color2 = { 200, 200, 200, 255 }; for (int32 i = 0; i < 10; i++) { CVector vecDir = CVector( CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ ); ++nFrameGen; int32 currentFrame = nFrameGen & 3; RwRGBA color = color2; if (nFrameGen & 1) color = color1; float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, color, nRotationSpeed, 0, currentFrame, 0); } if (m_nCollisionDamageEffect == DAMAGE_EFFECT_BURST_BEACHBALL) { PlayOneShotScriptObject(SCRIPT_SOUND_HIT_BALL, vecPos); } else { PlayOneShotScriptObject(SCRIPT_SOUND_TIRE_COLLISION, vecPos); } break; } case DAMAGE_EFFECT_SMASH_BARPOST_COMPLETELY: { bIsVisible = false; bUsesCollision = false; if (!GetIsStatic()) { RemoveFromMovingList(); } SetIsStatic(true); bExplosionProof = true; SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); const RwRGBA color1 = { 200, 0, 0, 255 }; const RwRGBA color2 = { 200, 200, 200, 255 }; SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); for (int32 i = 0; i < 32; i++) { CVector vecDir = CVector( CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ ); ++nFrameGen; int32 currentFrame = nFrameGen & 3; const RwRGBA &color = nFrameGen & 1 ? color1 : color2; float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, color, nRotationSpeed, 0, currentFrame, 0); } PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, vecPos); break; } case DAMAGE_EFFECT_SMASH_NEWSTANDNEW1: case DAMAGE_EFFECT_SMASH_NEWSTANDNEW2: case DAMAGE_EFFECT_SMASH_NEWSTANDNEW3: case DAMAGE_EFFECT_SMASH_NEWSTANDNEW4: case DAMAGE_EFFECT_SMASH_NEWSTANDNEW5: { bIsVisible = false; bUsesCollision = false; if (!GetIsStatic()) { RemoveFromMovingList(); } SetIsStatic(true); bExplosionProof = true; SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); CRGBA possibleColor1; CRGBA possibleColor2; switch (m_nCollisionDamageEffect) { case DAMAGE_EFFECT_SMASH_NEWSTANDNEW1: possibleColor1 = CRGBA(0xC0, 0x3E, 0xC, 0xFF); possibleColor2 = possibleColor1; break; case DAMAGE_EFFECT_SMASH_NEWSTANDNEW2: possibleColor1 = CRGBA(0xA3, 0x36, 0x21, 0xFF); possibleColor2 = possibleColor1; break; case DAMAGE_EFFECT_SMASH_NEWSTANDNEW3: possibleColor1 = CRGBA(0x12, 0x31, 0x24, 0xFF); possibleColor2 = possibleColor1; break; case DAMAGE_EFFECT_SMASH_NEWSTANDNEW4: possibleColor1 = CRGBA(0xC0, 0xC8, 0xBE, 0xFF); possibleColor2 = CRGBA(0x10, 0x57, 0x85, 0xFF); break; case DAMAGE_EFFECT_SMASH_NEWSTANDNEW5: possibleColor1 = CRGBA(0xD0, 0x94, 0x1B, 0xFF); possibleColor2 = possibleColor1; break; } for (int32 i = 0; i < 16; i++) { CVector vecDir( CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(0.10f, 0.15f) + fDirectionZ ); float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); ++nFrameGen; int32 nCurFrame = nFrameGen & 0x3; CRGBA &selectedColor = nFrameGen & 0x1 ? possibleColor1 : possibleColor2; CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, selectedColor, nRotationSpeed, 0, nCurFrame, 0); if (!(i % 7)) { static CRGBA secondParticleColors[4] = { CRGBA(0xA0, 0x60, 0x60, 0xFF), CRGBA(0x60, 0xA0, 0x60, 0xFF), CRGBA(0x60, 0x60, 0xA0, 0xFF), CRGBA(0xA0, 0xA0, 0xA0, 0xFF) }; vecDir *= 0.5f; CRGBA &secondParticleColor = secondParticleColors[nFrameGen & 3]; int32 nSecondRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); CParticle::AddParticle(PARTICLE_DEBRIS, vecPos, vecDir, nil, 0.1f, secondParticleColor, nSecondRotationSpeed, 0, 1, 0); } } PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, vecPos); break; } case DAMAGE_EFFECT_SMASH_VEGPALM: { static RwRGBA primaryColor1 = { 0x39, 0x4D, 0x29, 0xff }; static RwRGBA primaryColor2 = { 0x94, 0x7D, 0x73, 0xff }; bIsVisible = false; bUsesCollision = false; if (!GetIsStatic()) { RemoveFromMovingList(); } SetIsStatic(true); bExplosionProof = true; SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); float fRadius = GetColModel()->boundingSphere.radius; for (int32 i = 0; i < 32; i++) { CVector particleDir = CVector( CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), CGeneral::GetRandomNumberInRange(-0.05f, 0.05f) + fDirectionZ ); CVector particlePos = vecPos; particlePos.z += CGeneral::GetRandomNumberInRange(0.0f, 1.0f) * fRadius; ++nFrameGen; int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); int32 nCurFrame = nFrameGen & 0x3; float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); RwRGBA& particleColor = nFrameGen & 1 ? primaryColor1 : primaryColor2; CParticle::AddParticle(PARTICLE_CAR_DEBRIS, particlePos, particleDir, nil, fSize, particleColor, nRotationSpeed, 0, nCurFrame, 0); if ((i % 7) == 0) { static RwRGBA secondaryColor = { 0x9A, 0x99, 0x99, 0x3E }; CParticle::AddParticle(PARTICLE_DEBRIS, particlePos, particleDir, nil, 0.3, secondaryColor, nRotationSpeed, 0, 0, 0); } } PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_2, vecPos); break; } case DAMAGE_EFFECT_SMASH_BLACKBAG: case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD: case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_TOWEL: { bIsVisible = false; bUsesCollision = false; if (!GetIsStatic()) { RemoveFromMovingList(); } SetIsStatic(true); bExplosionProof = true; SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); CRGBA possibleColor1; CRGBA possibleColor2; switch (m_nCollisionDamageEffect) { case DAMAGE_EFFECT_SMASH_BLACKBAG: possibleColor1 = CRGBA(0, 0, 0, 0xFF); possibleColor2 = possibleColor1; break; case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD: possibleColor1 = CRGBA(0x8F, 0x8A, 0x8C, 0xFF); possibleColor2 = CRGBA(0x73, 0x75, 0x7B, 0xFF); break; case DAMAGE_EFFECT_SMASH_BEACHLOUNGE_TOWEL: possibleColor1 = CRGBA(0x52, 0x92, 0x4A, 0xFF); possibleColor2 = CRGBA(0xCE, 0xCF, 0xCE, 0xFF); break; } for (int32 i = 0; i < 16; i++) { CVector vecDir( CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(-0.35f, 0.35f), CGeneral::GetRandomNumberInRange(0.10f, 0.25f) + fDirectionZ ); ++nFrameGen; int32 nCurFrame = nFrameGen & 0x3; CRGBA &selectedColor = nFrameGen & 0x1 ? possibleColor1 : possibleColor2; float fSize = CGeneral::GetRandomNumberInRange(0.02f, 0.20f); int32 nRotationSpeed = CGeneral::GetRandomNumberInRange(-40, 40); CParticle::AddParticle(PARTICLE_CAR_DEBRIS, vecPos, vecDir, nil, fSize, selectedColor, nRotationSpeed, 0, nCurFrame, 0); } if (m_nCollisionDamageEffect == DAMAGE_EFFECT_SMASH_BLACKBAG) { PlayOneShotScriptObject(SCRIPT_SOUND_BOX_DESTROYED_2, vecPos); } else if (m_nCollisionDamageEffect == DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD) { PlayOneShotScriptObject(SCRIPT_SOUND_METAL_COLLISION, vecPos); } break; } default: DEV("Unhandled collision damage effect id: %d\n", m_nCollisionDamageEffect); return; } } } void CObject::RefModelInfo(int32 modelId) { m_nRefModelIndex = modelId; CModelInfo::GetModelInfo(modelId)->AddRef(); } void CObject::Init(void) { m_type = ENTITY_TYPE_OBJECT; CObjectData::SetObjectData(GetModelIndex(), *this); m_nEndOfLifeTime = 0; ObjectCreatedBy = GAME_OBJECT; SetIsStatic(true); bIsPickup = false; bPickupObjWithMessage = false; bOutOfStock = false; bGlassCracked = false; bGlassBroken = false; bHasBeenDamaged = false; bUseVehicleColours = false; m_nRefModelIndex = -1; m_colour1 = 0; m_colour2 = 0; m_nBonusValue = 0; bIsWeapon = false; m_nCostValue = 0; m_pCollidingEntity = nil; CColPoint point; CEntity *outEntity = nil; const CVector &vecPos = m_matrix.GetPosition(); if (CWorld::ProcessVerticalLine(vecPos, vecPos.z - 10.0f, point, outEntity, true, false, false, false, false, false, nil)) m_pCurSurface = outEntity; else m_pCurSurface = nil; if (GetModelIndex() == MI_BUOY) bTouchingWater = true; if (CModelInfo::GetModelInfo(GetModelIndex())->GetModelType() == MITYPE_WEAPON) bIsWeapon = true; bIsStreetLight = IsLightObject(GetModelIndex()); m_area = AREA_EVERYWHERE; } bool CObject::CanBeDeleted(void) { switch (ObjectCreatedBy) { case GAME_OBJECT: return true; case MISSION_OBJECT: return false; case TEMP_OBJECT: return true; case CUTSCENE_OBJECT: return false; case CONTROLLED_SUB_OBJECT: return false; default: return true; } } void CObject::DeleteAllMissionObjects() { CObjectPool *objectPool = CPools::GetObjectPool(); for (int32 i = 0; i < objectPool->GetSize(); i++) { CObject *pObject = objectPool->GetSlot(i); if (pObject && pObject->ObjectCreatedBy == MISSION_OBJECT) { CWorld::Remove(pObject); delete pObject; } } } void CObject::DeleteAllTempObjects() { CObjectPool *objectPool = CPools::GetObjectPool(); for (int32 i = 0; i < objectPool->GetSize(); i++) { CObject *pObject = objectPool->GetSlot(i); if (pObject && pObject->ObjectCreatedBy == TEMP_OBJECT) { CWorld::Remove(pObject); delete pObject; } } } void CObject::DeleteAllTempObjectsInArea(CVector point, float fRadius) { CObjectPool *objectPool = CPools::GetObjectPool(); for (int32 i = 0; i < objectPool->GetSize(); i++) { CObject *pObject = objectPool->GetSlot(i); if (pObject && pObject->ObjectCreatedBy == TEMP_OBJECT && (point - pObject->GetPosition()).MagnitudeSqr() < SQR(fRadius)) { CWorld::Remove(pObject); delete pObject; } } } bool IsObjectPointerValid(CObject *pObject) { if (!pObject) return false; int index = CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pObject); #ifdef FIX_BUGS if (index < 0 || index >= CPools::GetObjectPool()->GetSize()) #else if (index < 0 || index > CPools::GetObjectPool()->GetSize()) #endif return false; return pObject->bIsBIGBuilding || pObject->m_entryInfoList.first; } ================================================ FILE: src/objects/Object.h ================================================ #pragma once #include "Physical.h" enum { UNKNOWN_OBJECT = 0, GAME_OBJECT = 1, MISSION_OBJECT = 2, TEMP_OBJECT = 3, CUTSCENE_OBJECT = 4, CONTROLLED_SUB_OBJECT = 5, }; enum CollisionSpecialResponseCase { COLLRESPONSE_NONE, COLLRESPONSE_LAMPOST, COLLRESPONSE_SMALLBOX, COLLRESPONSE_BIGBOX, COLLRESPONSE_FENCEPART, COLLRESPONSE_UNKNOWN5 }; enum CollisionDamageEffect { DAMAGE_EFFECT_NONE, DAMAGE_EFFECT_CHANGE_MODEL, DAMAGE_EFFECT_SPLIT_MODEL, DAMAGE_EFFECT_SMASH_AND_DAMAGE_TRAFFICLIGHTS, DAMAGE_EFFECT_SMASH_COMPLETELY = 20, DAMAGE_EFFECT_CHANGE_THEN_SMASH, DAMAGE_EFFECT_SMASH_CARDBOARD_COMPLETELY = 50, DAMAGE_EFFECT_SMASH_YELLOW_TARGET_COMPLETELY = 51, DAMAGE_EFFECT_SMASH_WOODENBOX_COMPLETELY = 60, DAMAGE_EFFECT_SMASH_TRAFFICCONE_COMPLETELY = 70, DAMAGE_EFFECT_SMASH_BARPOST_COMPLETELY = 80, DAMAGE_EFFECT_SMASH_NEWSTANDNEW1 = 91, DAMAGE_EFFECT_SMASH_NEWSTANDNEW2 = 92, DAMAGE_EFFECT_SMASH_NEWSTANDNEW3 = 93, DAMAGE_EFFECT_SMASH_NEWSTANDNEW4 = 94, DAMAGE_EFFECT_SMASH_NEWSTANDNEW5 = 95, DAMAGE_EFFECT_SMASH_BLACKBAG = 100, DAMAGE_EFFECT_SMASH_VEGPALM = 110, DAMAGE_EFFECT_BURST_BEACHBALL = 120, DAMAGE_EFFECT_SMASH_BEACHLOUNGE_WOOD = 131, DAMAGE_EFFECT_SMASH_BEACHLOUNGE_TOWEL = 132, }; class CVehicle; class CDummyObject; class CObject : public CPhysical { public: CMatrix m_objectMatrix; float m_fUprootLimit; int8 ObjectCreatedBy; uint8 bIsPickup : 1; uint8 bAmmoCollected : 1; uint8 bPickupObjWithMessage : 1; uint8 bOutOfStock : 1; uint8 bGlassCracked : 1; uint8 bGlassBroken : 1; uint8 bHasBeenDamaged : 1; uint8 bUseVehicleColours : 1; uint8 bIsWeapon : 1; uint8 bIsStreetLight : 1; int8 m_nBonusValue; uint16 m_nCostValue; float m_fCollisionDamageMultiplier; uint8 m_nCollisionDamageEffect; uint8 m_nSpecialCollisionResponseCases; bool m_bCameraToAvoidThisObject; uint8 m_nBeachballBounces; uint32 m_obj_unused1; uint32 m_nEndOfLifeTime; int16 m_nRefModelIndex; CEntity *m_pCurSurface; CEntity *m_pCollidingEntity; int8 m_colour1, m_colour2; static int16 nNoTempObjects; static float fDistToNearestTree; 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); CObject(void); CObject(int32, bool); CObject(CDummyObject*); ~CObject(void); void ProcessControl(void); void Teleport(CVector vecPos); void Render(void); bool SetupLighting(void); void RemoveLighting(bool reset); void ObjectDamage(float amount); void RefModelInfo(int32 modelId); void Init(void); bool CanBeDeleted(void); static void DeleteAllMissionObjects(); static void DeleteAllTempObjects(); static void DeleteAllTempObjectsInArea(CVector point, float fRadius); }; bool IsObjectPointerValid(CObject* pObject); ================================================ FILE: src/objects/ObjectData.cpp ================================================ #include "common.h" #include "main.h" #include "ModelInfo.h" #include "Object.h" #include "FileMgr.h" #include "ObjectData.h" CObjectInfo CObjectData::ms_aObjectInfo[NUMOBJECTINFO]; // Another ugly file reader void CObjectData::Initialise(const char *filename) { char *p, *lp; char line[1024], name[256]; int id; float percentSubmerged; int damageEffect, responseCase, camAvoid; CBaseModelInfo *mi; ms_aObjectInfo[0].m_fMass = 99999.0f; ms_aObjectInfo[0].m_fTurnMass = 99999.0f; ms_aObjectInfo[0].m_fAirResistance = 0.99f; ms_aObjectInfo[0].m_fElasticity = 0.1f; ms_aObjectInfo[0].m_fBuoyancy = GRAVITY * ms_aObjectInfo[0].m_fMass * 2.0f; ms_aObjectInfo[0].m_fUprootLimit = 0.0f; ms_aObjectInfo[0].m_fCollisionDamageMultiplier = 1.0f; ms_aObjectInfo[0].m_nCollisionDamageEffect = 0; ms_aObjectInfo[0].m_nSpecialCollisionResponseCases = 0; ms_aObjectInfo[0].m_bCameraToAvoidThisObject = false; ms_aObjectInfo[1].m_fMass = 99999.0f; ms_aObjectInfo[1].m_fTurnMass = 99999.0f; ms_aObjectInfo[1].m_fAirResistance = 0.99f; ms_aObjectInfo[1].m_fElasticity = 0.1f; ms_aObjectInfo[1].m_fBuoyancy = ms_aObjectInfo[0].m_fBuoyancy; ms_aObjectInfo[1].m_fUprootLimit = 0.0f; ms_aObjectInfo[1].m_fCollisionDamageMultiplier = 1.0f; ms_aObjectInfo[1].m_nCollisionDamageEffect = 0; ms_aObjectInfo[1].m_nSpecialCollisionResponseCases = 0; ms_aObjectInfo[1].m_bCameraToAvoidThisObject = true; ms_aObjectInfo[2].m_fMass = 99999.0f; ms_aObjectInfo[2].m_fTurnMass = 99999.0f; ms_aObjectInfo[2].m_fAirResistance = 0.99f; ms_aObjectInfo[2].m_fElasticity = 0.1f; ms_aObjectInfo[2].m_fBuoyancy = ms_aObjectInfo[0].m_fBuoyancy; ms_aObjectInfo[2].m_fUprootLimit = 0.0f; ms_aObjectInfo[2].m_fCollisionDamageMultiplier = 1.0f; ms_aObjectInfo[2].m_nCollisionDamageEffect = 0; ms_aObjectInfo[2].m_bCameraToAvoidThisObject = false; ms_aObjectInfo[2].m_nSpecialCollisionResponseCases = 4; ms_aObjectInfo[3].m_fMass = 99999.0f; ms_aObjectInfo[3].m_fTurnMass = 99999.0f; ms_aObjectInfo[3].m_fAirResistance = 0.99f; ms_aObjectInfo[3].m_fElasticity = 0.1f; ms_aObjectInfo[3].m_fBuoyancy = ms_aObjectInfo[0].m_fBuoyancy; ms_aObjectInfo[3].m_fUprootLimit = 0.0f; ms_aObjectInfo[3].m_fCollisionDamageMultiplier = 1.0f; ms_aObjectInfo[3].m_nCollisionDamageEffect = 0; ms_aObjectInfo[3].m_nSpecialCollisionResponseCases = 4; ms_aObjectInfo[3].m_bCameraToAvoidThisObject = true; CFileMgr::SetDir(""); CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r"); id = 4; p = (char*)work_buff; while(*p != '*'){ // skip over white space and comments while(*p == ' ' || *p == '\n' || *p == '\r' || *p == ';') if(*p == ';') while(*p != '\n' && *p != '*') p++; else p++; if(*p == '*') break; // read one line lp = line; while(*p != '\n' && *p != '*'){ *lp++ = *p == ',' ? ' ' : *p; p++; } if(*p == '\n') p++; #ifdef FIX_BUGS *lp = '\0'; // FIX: game wrote '\n' here #else *lp = '\n'; #endif assert(id < NUMOBJECTINFO); sscanf(line, "%s %f %f %f %f %f %f %f %d %d %d", name, &ms_aObjectInfo[id].m_fMass, &ms_aObjectInfo[id].m_fTurnMass, &ms_aObjectInfo[id].m_fAirResistance, &ms_aObjectInfo[id].m_fElasticity, &percentSubmerged, &ms_aObjectInfo[id].m_fUprootLimit, &ms_aObjectInfo[id].m_fCollisionDamageMultiplier, &damageEffect, &responseCase, &camAvoid); ms_aObjectInfo[id].m_fBuoyancy = 100.0f/percentSubmerged * GRAVITY *ms_aObjectInfo[id].m_fMass; ms_aObjectInfo[id].m_nCollisionDamageEffect = damageEffect; ms_aObjectInfo[id].m_nSpecialCollisionResponseCases = responseCase; ms_aObjectInfo[id].m_bCameraToAvoidThisObject = camAvoid; mi = CModelInfo::GetModelInfo(name, nil); if (mi) { if (ms_aObjectInfo[0].m_fMass != ms_aObjectInfo[id].m_fMass || ms_aObjectInfo[0].m_fCollisionDamageMultiplier != ms_aObjectInfo[id].m_fCollisionDamageMultiplier || ms_aObjectInfo[0].m_nCollisionDamageEffect != ms_aObjectInfo[id].m_nCollisionDamageEffect || ((ms_aObjectInfo[0].m_nSpecialCollisionResponseCases != ms_aObjectInfo[id].m_nSpecialCollisionResponseCases) && (ms_aObjectInfo[2].m_nSpecialCollisionResponseCases != ms_aObjectInfo[id].m_nSpecialCollisionResponseCases))) { mi->SetObjectID(id++); } else if (ms_aObjectInfo[0].m_nSpecialCollisionResponseCases == ms_aObjectInfo[id].m_nSpecialCollisionResponseCases) { if (ms_aObjectInfo[0].m_bCameraToAvoidThisObject == ms_aObjectInfo[id].m_bCameraToAvoidThisObject) mi->SetObjectID(0); else mi->SetObjectID(1); } else if (ms_aObjectInfo[2].m_bCameraToAvoidThisObject == ms_aObjectInfo[id].m_bCameraToAvoidThisObject) mi->SetObjectID(2); else mi->SetObjectID(3); } else debug("CObjectData: Cannot find object %s\n", name); } } void CObjectData::SetObjectData(int32 modelId, CObject &object) { CObjectInfo *objinfo; if(CModelInfo::GetModelInfo(modelId)->GetObjectID() == -1) return; objinfo = &ms_aObjectInfo[CModelInfo::GetModelInfo(modelId)->GetObjectID()]; object.m_fMass = objinfo->m_fMass; object.m_fTurnMass = objinfo->m_fTurnMass; object.m_fAirResistance = objinfo->m_fAirResistance; object.m_fElasticity = objinfo->m_fElasticity; object.m_fBuoyancy = objinfo->m_fBuoyancy; object.m_fUprootLimit = objinfo->m_fUprootLimit; object.m_fCollisionDamageMultiplier = objinfo->m_fCollisionDamageMultiplier; object.m_nCollisionDamageEffect = objinfo->m_nCollisionDamageEffect; object.m_nSpecialCollisionResponseCases = objinfo->m_nSpecialCollisionResponseCases; object.m_bCameraToAvoidThisObject = objinfo->m_bCameraToAvoidThisObject; if(object.m_fMass >= 99998.0f){ object.bInfiniteMass = true; object.m_phy_flagA08 = true; object.bAffectedByGravity = false; object.bExplosionProof = true; } } ================================================ FILE: src/objects/ObjectData.h ================================================ #pragma once class CObject; class CObjectInfo { public: float m_fMass; float m_fTurnMass; float m_fAirResistance; float m_fElasticity; float m_fBuoyancy; float m_fUprootLimit; float m_fCollisionDamageMultiplier; uint8 m_nCollisionDamageEffect; uint8 m_nSpecialCollisionResponseCases; bool m_bCameraToAvoidThisObject; }; VALIDATE_SIZE(CObjectInfo, 0x20); class CObjectData { static CObjectInfo ms_aObjectInfo[NUMOBJECTINFO]; public: static void Initialise(const char *filename); static void SetObjectData(int32 modelId, CObject &object); }; ================================================ FILE: src/objects/ParticleObject.cpp ================================================ #include "common.h" #include "ParticleObject.h" #include "Timer.h" #include "General.h" #include "ParticleMgr.h" #include "Particle.h" #include "Camera.h" #include "Game.h" #include "DMAudio.h" #include "screendroplets.h" CParticleObject gPObjectArray[MAX_PARTICLEOBJECTS]; CParticleObject *CParticleObject::pCloseListHead; CParticleObject *CParticleObject::pFarListHead; CParticleObject *CParticleObject::pUnusedListHead; CAudioHydrant List[MAX_AUDIOHYDRANTS]; CAudioHydrant *CAudioHydrant::Get(int n) { return &List[n]; } bool CAudioHydrant::Add(CParticleObject *particleobject) { for ( int32 i = 0; i < MAX_AUDIOHYDRANTS; i++ ) { if ( List[i].AudioEntity == AEHANDLE_NONE ) { List[i].AudioEntity = DMAudio.CreateEntity(AUDIOTYPE_FIREHYDRANT, particleobject); if ( AEHANDLE_IS_FAILED(List[i].AudioEntity) ) return false; DMAudio.SetEntityStatus(List[i].AudioEntity, true); List[i].pParticleObject = particleobject; return true; } } return false; } void CAudioHydrant::Remove(CParticleObject *particleobject) { for ( int32 i = 0; i < MAX_AUDIOHYDRANTS; i++ ) { if ( List[i].pParticleObject == particleobject ) { DMAudio.DestroyEntity(List[i].AudioEntity); List[i].AudioEntity = AEHANDLE_NONE; List[i].pParticleObject = nil; } } } CParticleObject::CParticleObject() : CPlaceable(), m_nFrameCounter(0), m_nState(POBJECTSTATE_INITIALISED), m_pNext(nil), m_pPrev(nil), m_nRemoveTimer(0) { ; } CParticleObject::~CParticleObject() { } void CParticleObject::Initialise() { pCloseListHead = nil; pFarListHead = nil; pUnusedListHead = &gPObjectArray[0]; for ( int32 i = 0; i < MAX_PARTICLEOBJECTS; i++ ) { if ( i == 0 ) gPObjectArray[i].m_pPrev = nil; else gPObjectArray[i].m_pPrev = &gPObjectArray[i - 1]; if ( i == MAX_PARTICLEOBJECTS-1 ) gPObjectArray[i].m_pNext = nil; else gPObjectArray[i].m_pNext = &gPObjectArray[i + 1]; gPObjectArray[i].m_nState = POBJECTSTATE_FREE; } } CParticleObject * CParticleObject::AddObject(uint16 type, CVector const &pos, uint8 remove) { CRGBA color(0, 0, 0, 0); CVector target(0.0f, 0.0f, 0.0f); return AddObject(type, pos, target, 0.0f, 0, color, remove); } CParticleObject * CParticleObject::AddObject(uint16 type, CVector const &pos, float size, uint8 remove) { CRGBA color(0, 0, 0, 0); CVector target(0.0f, 0.0f, 0.0f); return AddObject(type, pos, target, size, 0, color, remove); } CParticleObject * CParticleObject::AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint8 remove) { CRGBA color(0, 0, 0, 0); return AddObject(type, pos, target, size, 0, color, remove); } CParticleObject * CParticleObject::AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, RwRGBA const &color, uint8 remove) { CParticleObject *pobj = pUnusedListHead; if ( pobj == nil ) { printf("Error: No particle objects available!\n"); return nil; } MoveToList(&pUnusedListHead, &pCloseListHead, pobj); pobj->m_nState = POBJECTSTATE_UPDATE_CLOSE; pobj->m_Type = (eParticleObjectType)type; pobj->SetPosition(pos); pobj->m_vecTarget = target; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 0; pobj->m_nFrameCounter = 0; pobj->m_bRemove = remove; pobj->m_pParticle = nil; if ( lifeTime != 0 ) pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds() + lifeTime; else pobj->m_nRemoveTimer = 0; if ( color.alpha != 0 ) pobj->m_Color = color; else pobj->m_Color.alpha = 0; pobj->m_fSize = size; pobj->m_fRandVal = 0.0f; switch ( type ) { case POBJECT_PAVEMENT_STEAM: { pobj->m_ParticleType = PARTICLE_STEAM_NY; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 3; pobj->m_nCreationChance = 8; break; } case POBJECT_PAVEMENT_STEAM_SLOWMOTION: { pobj->m_ParticleType = PARTICLE_STEAM_NY_SLOWMOTION; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 8; break; } case POBJECT_WALL_STEAM: { pobj->m_ParticleType = PARTICLE_STEAM_NY; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 3; pobj->m_nCreationChance = 8; break; } case POBJECT_WALL_STEAM_SLOWMOTION: { pobj->m_ParticleType = PARTICLE_STEAM_NY_SLOWMOTION; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 8; break; } case POBJECT_DARK_SMOKE: { pobj->m_ParticleType = PARTICLE_STEAM_NY; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 3; pobj->m_nCreationChance = 8; pobj->m_Color = CRGBA(16, 16, 16, 255); break; } case POBJECT_WATER_FOUNTAIN_VERT: { pobj->m_ParticleType = PARTICLE_WATER_HYDRANT; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 0; pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.1f); break; } case POBJECT_WATER_FOUNTAIN_HORIZ: { pobj->m_ParticleType = PARTICLE_WATER_HYDRANT; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 0; break; } case POBJECT_FIRE_HYDRANT: { pobj->m_ParticleType = PARTICLE_WATER_HYDRANT; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 0; pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.3f); pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds() + 5000; CAudioHydrant::Add(pobj); break; } case POBJECT_CAR_WATER_SPLASH: case POBJECT_PED_WATER_SPLASH: { pobj->m_ParticleType = PARTICLE_CAR_SPLASH; pobj->m_nNumEffectCycles = 0; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 0; #ifdef SCREEN_DROPLETS ScreenDroplets::RegisterSplash(pobj); #endif break; } case POBJECT_SPLASHES_AROUND: { pobj->m_ParticleType = PARTICLE_SPLASH; pobj->m_nNumEffectCycles = 15; pobj->m_nSkipFrames = 2; pobj->m_nCreationChance = 0; break; } case POBJECT_SMALL_FIRE: { pobj->m_ParticleType = PARTICLE_FLAME; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 2; pobj->m_nCreationChance = 2; pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); break; } case POBJECT_BIG_FIRE: { pobj->m_ParticleType = PARTICLE_FLAME; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 2; pobj->m_nCreationChance = 4; pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); break; } case POBJECT_DRY_ICE: { pobj->m_ParticleType = PARTICLE_SMOKE; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 0; pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); break; } case POBJECT_DRY_ICE_SLOWMOTION: { pobj->m_ParticleType = PARTICLE_SMOKE_SLOWMOTION; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 0; pobj->m_vecTarget = CVector(0.0f, 0.0f, 0.0f); break; } case POBJECT_FIRE_TRAIL: { pobj->m_ParticleType = PARTICLE_EXPLOSION_MEDIUM; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 3; pobj->m_nCreationChance = 2; pobj->m_fRandVal = 0.01f; break; } case POBJECT_SMOKE_TRAIL: { pobj->m_ParticleType = PARTICLE_FIREBALL_SMOKE; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 2; pobj->m_fRandVal = 0.02f; break; } case POBJECT_FIREBALL_AND_SMOKE: { pobj->m_ParticleType = PARTICLE_FLAME; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 0; pobj->m_fRandVal = 0.1f; break; } case POBJECT_ROCKET_TRAIL: { pobj->m_ParticleType = PARTICLE_FLAME; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 2; pobj->m_nCreationChance = 8; pobj->m_fRandVal = 0.1f; break; } case POBJECT_EXPLOSION_ONCE: { pobj->m_ParticleType = PARTICLE_EXPLOSION_LARGE; pobj->m_nNumEffectCycles = 1; pobj->m_nSkipFrames = 1; pobj->m_nCreationChance = 0; pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds(); break; } } return pobj; } CParticleObject * CParticleObject::AddObject(tParticleType type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, uint8 numEffectCycles, uint8 skipFrames, uint16 creationChance, uint8 remove) { CParticleObject *pobj = pUnusedListHead; ASSERT(pobj != nil); if ( pobj == nil ) { printf("Error: No particle objects available!\n"); return nil; } MoveToList(&pUnusedListHead, &pCloseListHead, pobj); pobj->m_nState = POBJECTSTATE_UPDATE_CLOSE; pobj->m_Type = (eParticleObjectType)-1; pobj->m_ParticleType = type; pobj->SetPosition(pos); pobj->m_vecTarget = target; pobj->m_nNumEffectCycles = numEffectCycles; pobj->m_nSkipFrames = skipFrames; pobj->m_nCreationChance = creationChance; pobj->m_nFrameCounter = 0; pobj->m_bRemove = remove; if ( lifeTime != 0 ) pobj->m_nRemoveTimer = CTimer::GetTimeInMilliseconds() + lifeTime; else pobj->m_nRemoveTimer = 0; pobj->m_Color.alpha = 0; pobj->m_fSize = size; pobj->m_fRandVal = 0.0f; return pobj; } void CParticleObject::RemoveObject(void) { switch ( this->m_nState ) { case POBJECTSTATE_UPDATE_CLOSE: { MoveToList(&pCloseListHead, &pUnusedListHead, this); this->m_nState = POBJECTSTATE_FREE; break; } case POBJECTSTATE_UPDATE_FAR: { MoveToList(&pFarListHead, &pUnusedListHead, this); this->m_nState = POBJECTSTATE_FREE; break; } } } void CParticleObject::UpdateAll(void) { { CParticleObject *pobj = pCloseListHead; CParticleObject *nextpobj; if ( pobj != nil ) { do { nextpobj = pobj->m_pNext; pobj->UpdateClose(); pobj = nextpobj; } while ( nextpobj != nil ); } } { int32 frame = CTimer::GetFrameCounter() & 31; int32 counter = 0; CParticleObject *pobj = pFarListHead; CParticleObject *nextpobj; if ( pobj != nil ) { do { nextpobj = pobj->m_pNext; if ( counter == frame ) { pobj->UpdateFar(); frame += 32; } counter++; pobj = nextpobj; } while ( nextpobj != nil ); } } } void CParticleObject::UpdateClose(void) { if ( !CGame::playingIntro ) { if ( (this->GetPosition() - TheCamera.GetPosition()).MagnitudeSqr2D() > SQR(100.0f) ) { if ( this->m_bRemove ) { if ( this->m_Type == POBJECT_FIRE_HYDRANT ) CAudioHydrant::Remove(this); MoveToList(&pCloseListHead, &pUnusedListHead, this); this->m_nState = POBJECTSTATE_FREE; } else { MoveToList(&pCloseListHead, &pFarListHead, this); this->m_nState = POBJECTSTATE_UPDATE_FAR; } return; } } if ( ++this->m_nFrameCounter >= this->m_nSkipFrames ) { this->m_nFrameCounter = 0; int32 randVal; if ( this->m_nCreationChance != 0 ) randVal = CGeneral::GetRandomNumber() % this->m_nCreationChance; if ( this->m_nCreationChance == 0 || randVal == 0 && this->m_nCreationChance < 0 || randVal != 0 && this->m_nCreationChance > 0) { switch ( this->m_Type ) { case POBJECT_SMALL_FIRE: { CVector pos = this->GetPosition(); CVector vel = this->m_vecTarget; float size = this->m_fSize; CVector flamevel; flamevel.x = vel.x; flamevel.y = vel.y; flamevel.z = CGeneral::GetRandomNumberInRange(0.0125f*size, 0.1f*size); CParticle::AddParticle(PARTICLE_FLAME, pos, flamevel, nil, size); CVector possmoke = pos; possmoke.x += CGeneral::GetRandomNumberInRange(0.625f*-size, size*0.625f); possmoke.y += CGeneral::GetRandomNumberInRange(0.625f*-size, size*0.625f); possmoke.z += CGeneral::GetRandomNumberInRange(0.625f* size, size*2.5f); CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, possmoke, vel); break; } case POBJECT_BIG_FIRE: { CVector pos = this->GetPosition(); CVector vel = this->m_vecTarget; float size = this->m_fSize; float s = 0.7f*size; CVector flamevel; flamevel.x = vel.x; flamevel.y = vel.y; flamevel.z = CGeneral::GetRandomNumberInRange(0.0125f*s, 0.1f*s); float flamesize = 0.8f*size; CParticle::AddParticle(PARTICLE_FLAME, pos, flamevel, nil, flamesize); for ( int32 i = 0; i < 4; i++ ) { CVector smokepos = pos; smokepos.x += CGeneral::GetRandomNumberInRange(0.625f*-size, 0.625f*size); smokepos.y += CGeneral::GetRandomNumberInRange(0.625f*-size, 0.625f*size); smokepos.z += CGeneral::GetRandomNumberInRange(0.625f* size, 3.5f *size); CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, smokepos, vel); } break; } case POBJECT_FIREBALL_AND_SMOKE: { if ( this->m_pParticle == nil ) { CVector pos = this->GetPosition(); CVector vel = this->m_vecTarget; float size = this->m_fSize; CVector expvel = 1.2f*vel; float expsize = 1.2f*size; this->m_pParticle = CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, expvel, nil, expsize); } else { CVector pos = this->GetPosition(); // this->m_pParticle->m_vecPosition ? CVector vel = this->m_vecTarget; float size = this->m_fSize; CVector veloffset = 0.35f*vel; CVector fireballvel = vel; for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) { fireballvel.x += CGeneral::GetRandomNumberInRange(-veloffset.x, veloffset.x); fireballvel.y += CGeneral::GetRandomNumberInRange(-veloffset.y, veloffset.y); fireballvel.z += CGeneral::GetRandomNumberInRange(-veloffset.z, veloffset.z); CParticle::AddParticle(PARTICLE_FIREBALL_SMOKE, pos, fireballvel, nil, size); } } break; } case POBJECT_ROCKET_TRAIL: { if ( this->m_pParticle == nil ) { CVector pos = this->GetPosition(); CVector vel = this->m_vecTarget; float size = this->m_fSize; this->m_pParticle = CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, vel, nil, size); } else { CVector pos = this->m_pParticle->m_vecPosition; CVector vel = this->m_vecTarget; float size = this->m_fSize; float fireballsize = size * 1.5f; CVector fireballvel = vel * -0.8f; for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) { CParticle::AddParticle(PARTICLE_FIREBALL_SMOKE, pos, fireballvel, nil, fireballsize); } } break; } case POBJECT_FIRE_TRAIL: { for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) { CVector vel = this->m_vecTarget; if ( vel.x != 0.0f ) vel.x += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); if ( vel.y != 0.0f ) vel.y += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); if ( vel.z != 0.0f ) vel.z += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); CParticle::AddParticle(this->m_ParticleType, this->GetPosition(), vel, nil, this->m_fSize, CGeneral::GetRandomNumberInRange(-6.0f, 6.0f)); } break; } case POBJECT_PED_WATER_SPLASH: { CRGBA colorsmoke(255, 255, 255, 196); CVector pos = this->GetPosition(); CVector vel = this->m_vecTarget; for ( int32 i = 0; i < 3; i++ ) { int32 angle = 90 * i; float fCos = CParticle::Cos(angle); float fSin = CParticle::Sin(angle); CVector splashpos; CVector splashvel; splashpos = pos + CVector(0.75f*fCos, 0.75f*fSin, 0.0f); splashvel = vel + CVector(0.05f*fCos, 0.05f*fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); splashpos = pos + CVector(0.75f*fCos, 0.75f*-fSin, 0.0f); splashvel = vel + CVector(0.05f*fCos, 0.05f*-fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); splashpos = pos + CVector(0.75f*-fCos, 0.75f*fSin, 0.0f); splashvel = vel + CVector(0.05f*-fCos, 0.05f*fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); splashpos = pos + CVector(0.75f*-fCos, 0.75f*-fSin, 0.0f); splashvel = vel + CVector(0.05f*-fCos, 0.05f*-fSin, CGeneral::GetRandomNumberInRange(0.04f, 0.08f)); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.8f), colorsmoke); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.5f), this->m_Color); } for ( int32 i = 0; i < 1; i++ ) { int32 angle = 180 * (i + 1); float fCos = CParticle::Cos(angle); float fSin = CParticle::Sin(angle); CVector splashpos; CVector splashvel; splashpos = pos + CVector(0.5f*fCos, 0.5f*fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); splashpos = pos + CVector(0.5f*fCos, 0.5f*-fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); splashpos = pos + CVector(0.5f*-fCos, 0.5f*fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); splashpos = pos + CVector(0.5f*-fCos, 0.5f*-fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.25f, 0.25f) * -fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.05f, 0.25f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.4f, 1.0f), this->m_Color); } break; } case POBJECT_CAR_WATER_SPLASH: { CRGBA colorsmoke(255, 255, 255, 196); CVector pos = this->GetPosition(); CVector vel = this->m_vecTarget; float size = CGeneral::GetRandomNumberInRange(1.0f, 2.5f); for ( int32 i = 0; i < 3; i++ ) { int32 angle = 90 * i; float fCos = CParticle::Cos(angle); float fSin = CParticle::Sin(angle); CVector splashpos; CVector splashvel; splashpos = pos + CVector(2.0f*fCos, 2.0f*fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); splashpos = pos + CVector(2.0f*fCos, 2.0f*-fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); splashpos = pos + CVector(2.0f*-fCos, 2.0f*fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); splashpos = pos + CVector(2.0f*-fCos, 2.0f*-fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f) * -fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.01f, 0.03f); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, splashpos, splashvel, nil, size, colorsmoke); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); } for ( int32 i = 0; i < 1; i++ ) { int32 angle = 180 * (i + 1); float fCos = CParticle::Cos(angle); float fSin = CParticle::Sin(angle); CVector splashpos; CVector splashvel; splashpos = pos + CVector(1.25f*fCos, 1.25f*fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); splashpos = pos + CVector(1.25f*fCos, 1.25f*-fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); splashpos = pos + CVector(1.25f*-fCos, 1.25f*fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); splashpos = pos + CVector(1.25f*-fCos, 1.25f*-fSin, 0.0f); splashvel = vel; splashvel.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fCos; splashvel.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * -fSin; splashvel.z += CGeneral::GetRandomNumberInRange(0.26f, 0.53f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, 0.0f, this->m_Color); } break; } case POBJECT_SPLASHES_AROUND: { CVector pos = this->GetPosition(); float size = this->m_fSize; for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) { CVector splashpos = pos; splashpos.x += CGeneral::GetRandomNumberInRange(-size, size); splashpos.y += CGeneral::GetRandomNumberInRange(-size, size); if ( CGeneral::GetRandomNumber() & 1 ) { CParticle::AddParticle(PARTICLE_RAIN_SPLASH, splashpos, CVector(0.0f, 0.0f, 0.0f), nil, 0.1f, this->m_Color); } else { CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, splashpos, CVector(0.0f, 0.0f, 0.0f), nil, 0.12f, this->m_Color); } } break; } case POBJECT_FIRE_HYDRANT: { CVector pos = this->GetPosition(); CVector vel = this->m_vecTarget; if ( (TheCamera.GetPosition() - pos).Magnitude() > 5.0f ) { for ( int32 i = 0; i < 1; i++ ) { int32 angle = 180 * i; float fCos = CParticle::Cos(angle); float fSin = CParticle::Sin(angle); CVector splashpos, splashvel; splashpos = pos + CVector(0.01f*fCos, 0.01f*fSin, 0.0f); splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); splashpos = pos + CVector(0.01f*fCos, 0.01f*-fSin, 0.0f); splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); splashpos = pos + CVector(0.01f*-fCos, 0.01f*fSin, 0.0f); splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); splashpos = pos + CVector(0.01f*-fCos, 0.01f*-fSin, 0.0f); splashvel = vel + CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.005f, 0.0075f), this->m_Color, 0, 0, 1, 300); } for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) { CParticle::AddParticle(this->m_ParticleType, pos, vel, nil, 0.0f, this->m_Color); } } break; } case POBJECT_WATER_FOUNTAIN_VERT: { CVector pos = this->GetPosition(); CVector vel = this->m_vecTarget; for ( int32 i = 0; i < 2; i++ ) { int32 angle = 180 * i; float fCos = CParticle::Cos(angle); float fSin = CParticle::Sin(angle); CVector splashpos, splashvel; splashpos = pos + CVector(0.015f*fCos, 0.015f*fSin, 0.0f); splashvel = vel + CVector(0.015f*fCos, 0.015f*fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); splashpos = pos + CVector(0.015f*fCos, 0.015f*-fSin, 0.0f); splashvel = vel + CVector(0.015f*fCos, 0.015f*-fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); splashpos = pos + CVector(0.015f*-fCos, 0.015f*fSin, 0.0f); splashvel = vel + CVector(0.015f*-fCos, 0.015f*fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); splashpos = pos + CVector(0.015f*-fCos, 0.015f*-fSin, 0.0f); splashvel = vel + CVector(0.015f*-fCos, 0.015f*-fSin, CGeneral::GetRandomNumberInRange(0.004f, 0.008f)); CParticle::AddParticle(PARTICLE_SPLASH, splashpos, splashvel, nil, CGeneral::GetRandomNumberInRange(0.001f, 0.005f), this->m_Color, 0, 0, 1, 1000); } break; } case POBJECT_WATER_FOUNTAIN_HORIZ: { CVector pos = this->GetPosition(); CVector vel = this->m_vecTarget; for ( int32 i = 0; i < 3; i++ ) { CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, vel, nil, 0.001f, this->m_Color, 0, 0, 1, 1000); } break; } default: { if ( this->m_fRandVal != 0.0f ) { for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) { CVector vel = this->m_vecTarget; if ( vel.x != 0.0f ) vel.x += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); if ( vel.y != 0.0f ) vel.y += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); if ( vel.z != 0.0f ) vel.z += CGeneral::GetRandomNumberInRange(-this->m_fRandVal, this->m_fRandVal); CParticle::AddParticle(this->m_ParticleType, this->GetPosition(), vel, nil, this->m_fSize, this->m_Color); } } else { for ( int32 i = 0; i < this->m_nNumEffectCycles; i++ ) { CParticle::AddParticle(this->m_ParticleType, this->GetPosition(), this->m_vecTarget, nil, this->m_fSize, this->m_Color); } } break; } } } } if ( this->m_nRemoveTimer != 0 && this->m_nRemoveTimer < CTimer::GetTimeInMilliseconds() ) { MoveToList(&pCloseListHead, &pUnusedListHead, this); this->m_nState = POBJECTSTATE_FREE; if ( this->m_Type == POBJECT_FIRE_HYDRANT ) CAudioHydrant::Remove(this); } } void CParticleObject::UpdateFar(void) { if ( this->m_nRemoveTimer != 0 && this->m_nRemoveTimer < CTimer::GetTimeInMilliseconds() ) { MoveToList(&pFarListHead, &pUnusedListHead, this); this->m_nState = POBJECTSTATE_FREE; if ( this->m_Type == POBJECT_FIRE_HYDRANT ) CAudioHydrant::Remove(this); } CVector2D dist = this->GetPosition() - TheCamera.GetPosition(); if ( dist.MagnitudeSqr() < SQR(100.0f)/*10000.0f*/ ) { MoveToList(&pFarListHead, &pCloseListHead, this); this->m_nState = POBJECTSTATE_UPDATE_CLOSE; } } bool CParticleObject::SaveParticle(uint8 *buffer, uint32 *length) { ASSERT( buffer != nil ); ASSERT( length != nil ); int32 numObjects = 0; for ( CParticleObject *p = pCloseListHead; p != nil; p = p->m_pNext ) ++numObjects; for ( CParticleObject *p = pFarListHead; p != nil; p = p->m_pNext ) ++numObjects; *(int32 *)buffer = numObjects; buffer += sizeof(int32); int32 objectsLength = sizeof(CParticleObject) * (numObjects + 1); int32 dataLength = objectsLength + sizeof(int32); for ( CParticleObject *p = pCloseListHead; p != nil; p = p->m_pNext ) { #if 0 // todo better *(CParticleObject*)buffer = *p; #else memcpy(buffer, p, sizeof(CParticleObject)); #endif buffer += sizeof(CParticleObject); } for ( CParticleObject *p = pFarListHead; p != nil; p = p->m_pNext ) { #if 0 // todo better *(CParticleObject*)buffer = *p; #else memcpy(buffer, p, sizeof(CParticleObject)); #endif buffer += sizeof(CParticleObject); } *length = dataLength; return true; } bool CParticleObject::LoadParticle(uint8 *buffer, uint32 length) { ASSERT( buffer != nil ); RemoveAllParticleObjects(); int32 numObjects = *(int32 *)buffer; buffer += sizeof(int32); if ( length != sizeof(CParticleObject) * (numObjects + 1) + sizeof(int32) ) return false; if ( numObjects == 0 ) return true; int32 i = 0; while ( i < numObjects ) { CParticleObject *dst = pUnusedListHead; CParticleObject *src = (CParticleObject *)buffer; buffer += sizeof(CParticleObject); if ( dst == nil ) return false; MoveToList(&pUnusedListHead, &pCloseListHead, dst); dst->m_nState = POBJECTSTATE_UPDATE_CLOSE; dst->m_Type = src->m_Type; dst->m_ParticleType = src->m_ParticleType; dst->SetPosition(src->GetPosition()); dst->m_vecTarget = src->m_vecTarget; dst->m_nFrameCounter = src->m_nFrameCounter; dst->m_bRemove = src->m_bRemove; dst->m_pParticle = nil; dst->m_nRemoveTimer = src->m_nRemoveTimer; dst->m_Color = src->m_Color; dst->m_fSize = src->m_fSize; dst->m_fRandVal = src->m_fRandVal; dst->m_nNumEffectCycles = src->m_nNumEffectCycles; dst->m_nSkipFrames = src->m_nSkipFrames; dst->m_nCreationChance = src->m_nCreationChance; i++; } return true; } void CParticleObject::RemoveAllExpireableParticleObjects(void) { { CParticleObject *pobj = pCloseListHead; CParticleObject *nextpobj; if ( pobj != nil ) { do { nextpobj = pobj->m_pNext; if ( pobj->m_nRemoveTimer != 0 ) { MoveToList(&pCloseListHead, &pUnusedListHead, pobj); pobj->m_nState = POBJECTSTATE_FREE; } pobj = nextpobj; } while ( nextpobj != nil ); } } { CParticleObject *pobj = pFarListHead; CParticleObject *nextpobj; if ( pobj != nil ) { do { nextpobj = pobj->m_pNext; if ( pobj->m_nRemoveTimer != 0 ) { MoveToList(&pFarListHead, &pUnusedListHead, pobj); pobj->m_nState = POBJECTSTATE_FREE; } pobj = nextpobj; } while ( nextpobj != nil ); } } } void CParticleObject::RemoveAllParticleObjects(void) { pUnusedListHead = &gPObjectArray[0]; pCloseListHead = nil; pFarListHead = nil; for ( int32 i = 0; i < MAX_PARTICLEOBJECTS; i++ ) { if ( i == 0 ) gPObjectArray[i].m_pPrev = nil; else gPObjectArray[i].m_pPrev = &gPObjectArray[i - 1]; if ( i == MAX_PARTICLEOBJECTS-1 ) gPObjectArray[i].m_pNext = nil; else gPObjectArray[i].m_pNext = &gPObjectArray[i + 1]; gPObjectArray[i].m_nState = POBJECTSTATE_FREE; } } void CParticleObject::MoveToList(CParticleObject **from, CParticleObject **to, CParticleObject *obj) { ASSERT( from != nil ); ASSERT( to != nil ); ASSERT( obj != nil ); if ( obj->m_pPrev == nil ) { *from = obj->m_pNext; if ( *from ) (*from)->m_pPrev = nil; } else { if ( obj->m_pNext == nil ) obj->m_pPrev->m_pNext = nil; else { obj->m_pNext->m_pPrev = obj->m_pPrev; obj->m_pPrev->m_pNext = obj->m_pNext; } } obj->m_pNext = *to; obj->m_pPrev = nil; *to = obj; if ( obj->m_pNext ) obj->m_pNext->m_pPrev = obj; } ================================================ FILE: src/objects/ParticleObject.h ================================================ #pragma once #include "AudioManager.h" #include "ParticleType.h" #include "Placeable.h" #define MAX_PARTICLEOBJECTS 70 #define MAX_AUDIOHYDRANTS 8 enum eParticleObjectType { POBJECT_PAVEMENT_STEAM = 0, POBJECT_PAVEMENT_STEAM_SLOWMOTION, POBJECT_WALL_STEAM, POBJECT_WALL_STEAM_SLOWMOTION, POBJECT_DARK_SMOKE, POBJECT_FIRE_HYDRANT, POBJECT_CAR_WATER_SPLASH, POBJECT_PED_WATER_SPLASH, POBJECT_SPLASHES_AROUND, POBJECT_SMALL_FIRE, POBJECT_BIG_FIRE, POBJECT_DRY_ICE, POBJECT_DRY_ICE_SLOWMOTION, POBJECT_WATER_FOUNTAIN_VERT, POBJECT_WATER_FOUNTAIN_HORIZ, POBJECT_FIRE_TRAIL, POBJECT_SMOKE_TRAIL, POBJECT_FIREBALL_AND_SMOKE, POBJECT_ROCKET_TRAIL, POBJECT_EXPLOSION_ONCE, POBJECT_CATALINAS_GUNFLASH, POBJECT_CATALINAS_SHOTGUNFLASH, }; enum eParticleObjectState { POBJECTSTATE_INITIALISED = 0, POBJECTSTATE_UPDATE_CLOSE, POBJECTSTATE_UPDATE_FAR, POBJECTSTATE_FREE, }; class CParticle; class CParticleObject : public CPlaceable { public: CParticleObject *m_pNext; CParticleObject *m_pPrev; CParticle *m_pParticle; uint32 m_nRemoveTimer; eParticleObjectType m_Type; tParticleType m_ParticleType; uint8 m_nNumEffectCycles; uint8 m_nSkipFrames; uint16 m_nFrameCounter; uint16 m_nState; CVector m_vecTarget; float m_fRandVal; float m_fSize; CRGBA m_Color; uint8 m_bRemove; int8 m_nCreationChance; static CParticleObject *pCloseListHead; static CParticleObject *pFarListHead; static CParticleObject *pUnusedListHead; CParticleObject(); ~CParticleObject(); static void Initialise(void); static CParticleObject *AddObject(uint16 type, CVector const &pos, uint8 remove); static CParticleObject *AddObject(uint16 type, CVector const &pos, float size, uint8 remove); static CParticleObject *AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint8 remove); static CParticleObject *AddObject(uint16 type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, RwRGBA const &color, uint8 remove); static CParticleObject *AddObject(tParticleType type, CVector const &pos, CVector const &target, float size, uint32 lifeTime, uint8 numEffectCycles, uint8 skipFrames, uint16 creationChance, uint8 remove); void RemoveObject(void); static void UpdateAll(void); void UpdateClose(void); void UpdateFar(void); static bool SaveParticle(uint8 *buffer, uint32 *length); static bool LoadParticle(uint8 *buffer, uint32 length); static void RemoveAllExpireableParticleObjects(void); static void RemoveAllParticleObjects(void); static void MoveToList(CParticleObject **from, CParticleObject **to, CParticleObject *obj); }; extern CParticleObject gPObjectArray[MAX_PARTICLEOBJECTS]; class CAudioHydrant { public: int32 AudioEntity; CParticleObject *pParticleObject; CAudioHydrant() : AudioEntity(AEHANDLE_NONE), pParticleObject(nil) { } static bool Add (CParticleObject *particleobject); static void Remove(CParticleObject *particleobject); static CAudioHydrant *Get(int n); // for neo screen droplets }; ================================================ FILE: src/objects/Projectile.cpp ================================================ #include "common.h" #include "Projectile.h" CProjectile::CProjectile(int32 model) : CObject() { m_fMass = 1.0f; m_fTurnMass = 1.0f; m_fAirResistance = 0.99999f; m_fElasticity = 0.75f; m_fBuoyancy = GRAVITY * m_fMass * 0.1f; bExplosionProof = true; SetModelIndex(model); ObjectCreatedBy = MISSION_OBJECT; } ================================================ FILE: src/objects/Projectile.h ================================================ #pragma once #include "Object.h" class CProjectile : public CObject { public: CProjectile(int32); }; ================================================ FILE: src/objects/Stinger.cpp ================================================ #include "common.h" #include "Stinger.h" #include "CopPed.h" #include "ModelIndices.h" #include "RpAnimBlend.h" #include "World.h" #include "Automobile.h" #include "Bike.h" #include "Particle.h" #include "AnimBlendAssociation.h" #include "General.h" uint32 NumOfStingerSegments; /* -- CStingerSegment -- */ CStingerSegment::CStingerSegment() { m_fMass = 1.0f; m_fTurnMass = 1.0f; m_fAirResistance = 0.99999f; m_fElasticity = 0.75f; m_fBuoyancy = GRAVITY * m_fMass * 0.1f; bExplosionProof = true; SetModelIndex(MI_PLC_STINGER); ObjectCreatedBy = CONTROLLED_SUB_OBJECT; NumOfStingerSegments++; } CStingerSegment::~CStingerSegment() { NumOfStingerSegments--; } /* -- CStinger -- */ CStinger::CStinger() { bIsDeployed = false; } void CStinger::Init(CPed *pPed) { int32 i; pOwner = pPed; for (i = 0; i < NUM_STINGER_SEGMENTS; i++) { pSpikes[i] = new CStingerSegment; pSpikes[i]->bUsesCollision = false; } bIsDeployed = true; m_vPos = pPed->GetPosition(); m_vPos.z -= 1.0f; m_fMax_Z = Atan2(-pPed->GetForward().x, pPed->GetForward().y) + HALFPI; for (i = 0; i < NUM_STINGER_SEGMENTS; i++) { pSpikes[i]->SetOrientation(0.0f, 0.0f, Atan2(-pPed->GetForward().x, pPed->GetForward().y)); pSpikes[i]->SetPosition(m_vPos); } CVector2D fwd2d(pPed->GetForward().x, pPed->GetForward().y); for (i = 0; i < ARRAY_SIZE(m_vPositions); i++) m_vPositions[i] = fwd2d * 1.8f * Sin(DEGTORAD(i)); m_nSpikeState = STINGERSTATE_NONE; m_nTimeOfDeploy = CTimer::GetTimeInMilliseconds(); } void CStinger::Remove() { if (!bIsDeployed) return; for (int32 i = 0; i < NUM_STINGER_SEGMENTS; i++) { CStingerSegment *spikeSegment = pSpikes[i]; if (spikeSegment->m_entryInfoList.first != nil) spikeSegment->bRemoveFromWorld = true; else delete spikeSegment; } bIsDeployed = false; } void CStinger::Deploy(CPed *pPed) { if (NumOfStingerSegments < NUM_STINGER_SEGMENTS*2 && !pPed->bInVehicle && pPed->IsPedInControl()) { if (!bIsDeployed && RpAnimBlendClumpGetAssociation(pPed->GetClump(), ANIM_STD_THROW_UNDER) == nil) { Init(pPed); pPed->SetPedState(PED_DEPLOY_STINGER); CAnimManager::AddAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_THROW_UNDER); } } } void CStinger::CheckForBurstTyres() { CVector firstPos = pSpikes[0]->GetPosition(); firstPos.z += 0.2f; CVector lastPos = pSpikes[NUM_STINGER_SEGMENTS - 1]->GetPosition(); lastPos.z += 0.2f; float dist = (lastPos - firstPos).Magnitude(); if (dist < 0.1f) return; CVehicle *vehsInRange[16]; int16 numObjects; CEntity entity; CWorld::FindObjectsInRange((lastPos + firstPos) / 2.0f, dist, true, &numObjects, 15, (CEntity**)vehsInRange, false, true, false, false, false); for (int32 i = 0; i < numObjects; i++) { CAutomobile *pAutomobile = nil; CBike *pBike = nil; if (vehsInRange[i]->IsCar()) pAutomobile = (CAutomobile*)vehsInRange[i]; else if (vehsInRange[i]->IsBike()) pBike = (CBike*)vehsInRange[i]; if (pAutomobile == nil && pBike == nil) continue; float maxWheelDistToSpike = sq(((CVehicleModelInfo*)CModelInfo::GetModelInfo(vehsInRange[i]->GetModelIndex()))->m_wheelScale + 0.1f); for (int wheelId = 0; wheelId < 4; wheelId++) { if ((pAutomobile != nil && pAutomobile->m_aSuspensionSpringRatioPrev[wheelId] < 1.0f) || (pBike != nil && pBike->m_aSuspensionSpringRatioPrev[wheelId] < 1.0f)) { CVector vecWheelPos; if (pAutomobile != nil) vecWheelPos = pAutomobile->m_aWheelColPoints[wheelId].point; else if (pBike != nil) vecWheelPos = pBike->m_aWheelColPoints[wheelId].point; for (int32 spike = 0; spike < NUM_STINGER_SEGMENTS; spike++) { if ((pSpikes[spike]->GetPosition() - vecWheelPos).Magnitude() < maxWheelDistToSpike) { if (pBike) { if (wheelId < 2) vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LF, true); else vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LR, true); } else { switch (wheelId) { case 0: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LF, true); break; case 1: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_LR, true); break; case 2: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_RF, true); break; case 3: vehsInRange[i]->BurstTyre(CAR_PIECE_WHEEL_RR, true); break; } } vecWheelPos.z += 0.15f; for (int j = 0; j < 4; j++) CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, vecWheelPos, vehsInRange[i]->GetRight() * 0.1f); } } } } } } void CStinger::Process() { switch (m_nSpikeState) { case STINGERSTATE_NONE: if (pOwner != nil && !pOwner->bInVehicle && pOwner->GetPedState() == PED_DEPLOY_STINGER && RpAnimBlendClumpGetAssociation(pOwner->GetClump(), ANIM_STD_THROW_UNDER)->currentTime > 0.39f) { m_nSpikeState = STINGERSTATE_DEPLOYING; for (int i = 0; i < NUM_STINGER_SEGMENTS; i++) CWorld::Add(pSpikes[i]); pOwner->SetIdle(); } break; case STINGERSTATE_DEPLOYED: if (pOwner != nil && pOwner->m_nPedType == PEDTYPE_COP) ((CCopPed*)pOwner)->m_bThrowsSpikeTrap = false; break; case STINGERSTATE_UNDEPLOYING: if (CTimer::GetTimeInMilliseconds() > m_nTimeOfDeploy + 2500) m_nSpikeState = STINGERSTATE_REMOVE; // no break case STINGERSTATE_DEPLOYING: if (m_nSpikeState == STINGERSTATE_DEPLOYING && CTimer::GetTimeInMilliseconds() > m_nTimeOfDeploy + 2500) m_nSpikeState = STINGERSTATE_DEPLOYED; else { float progress = (CTimer::GetTimeInMilliseconds() - m_nTimeOfDeploy) / 2500.0f; if (m_nSpikeState != STINGERSTATE_DEPLOYING) progress = 1.0f - progress; float degangle = progress * ARRAY_SIZE(m_vPositions); float angle1 = m_fMax_Z + DEGTORAD(degangle); float angle2 = m_fMax_Z - DEGTORAD(degangle); int pos = clamp(degangle, 0, ARRAY_SIZE(m_vPositions)-1); CVector2D pos2d = m_vPositions[pos]; CVector pos3d = m_vPos; CColPoint colPoint; CEntity *pEntity; if (CWorld::ProcessVerticalLine(CVector(pos3d.x, pos3d.y, pos3d.z - 10.0f), pos3d.z, colPoint, pEntity, true, false, false, false, true, false, nil)) pos3d.z = colPoint.point.z + 0.15f; angle1 = CGeneral::LimitRadianAngle(angle1); angle2 = CGeneral::LimitRadianAngle(angle2); for (int spike = 0; spike < NUM_STINGER_SEGMENTS; spike++) { if (CWorld::TestSphereAgainstWorld(pos3d + CVector(pos2d.x, pos2d.y, 0.6f), 0.3f, nil, true, false, false, true, false, false)) pos2d = CVector2D(0.0f, 0.0f); if (spike % 2 == 0) { pSpikes[spike]->SetOrientation(0.0f, 0.0f, angle1); pos3d.x += pos2d.x; pos3d.y += pos2d.y; } else { pSpikes[spike]->SetOrientation(0.0f, 0.0f, angle2); } pSpikes[spike]->SetPosition(pos3d); } } break; case STINGERSTATE_REMOVE: Remove(); break; } CheckForBurstTyres(); } ================================================ FILE: src/objects/Stinger.h ================================================ #pragma once #include "Object.h" class CStingerSegment : public CObject { public: CStingerSegment(); ~CStingerSegment(); }; #define NUM_STINGER_SEGMENTS (12) enum { STINGERSTATE_NONE = 0, STINGERSTATE_DEPLOYING, STINGERSTATE_DEPLOYED, STINGERSTATE_UNDEPLOYING, STINGERSTATE_REMOVE, }; class CStinger { public: bool bIsDeployed; uint32 m_nTimeOfDeploy; CVector m_vPos; float m_fMax_Z; float m_fMin_Z; CVector2D m_vPositions[60]; CStingerSegment *pSpikes[NUM_STINGER_SEGMENTS]; class CPed *pOwner; uint8 m_nSpikeState; CStinger(); void Init(CPed *pPed); void Remove(); void Deploy(CPed *pPed); void CheckForBurstTyres(); void Process(); }; ================================================ FILE: src/peds/CivilianPed.cpp ================================================ #include "common.h" #include "CivilianPed.h" #include "Phones.h" #include "General.h" #include "PlayerPed.h" #include "Wanted.h" #include "DMAudio.h" #include "World.h" #include "Vehicle.h" #include "SurfaceTable.h" #include "Weather.h" #include "PedAttractor.h" #include "Object.h" #include "CarCtrl.h" #ifndef _WIN32 #include #endif CCivilianPed::CCivilianPed(ePedType pedtype, uint32 mi) : CPed(pedtype) { SetModelIndex(mi); for (int i = 0; i < ARRAY_SIZE(m_nearPeds); i++) { m_nearPeds[i] = nil; } m_bLookForVacantCars = false; if (pedtype == PEDTYPE_CRIMINAL) m_bLookForVacantCars = true; m_nLookForVacantCarsCounter = 0; m_bJustStoleACar = false; m_bStealCarEvenIfThereIsSomeoneInIt = false; for (int i = 0; i < ARRAY_SIZE(m_nStealWishList); i++) { #ifdef FIX_BUGS uint32 randomCarModel = CGeneral::GetRandomNumberInRange(MI_LANDSTAL, MI_LAST_VEHICLE); #else uint32 randomCarModel = CGeneral::GetRandomNumberInRange(MI_LANDSTAL, MI_LANDSTAL + VEHICLEMODELSIZE); #endif if (CModelInfo::IsCarModel(randomCarModel) || CModelInfo::IsBikeModel(randomCarModel)) m_nStealWishList[i] = randomCarModel; else m_nStealWishList[i] = MI_CHEETAH; } m_nAttractorCycleState = 0; m_bAttractorUnk = (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < 1.25f); } void CCivilianPed::CivilianAI(void) { if (CTimer::GetTimeInMilliseconds() <= m_fleeTimer || m_objective != OBJECTIVE_NONE && !bRespondsToThreats || !IsPedInControl()) { if (m_objective == OBJECTIVE_GUARD_SPOT) return; if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) { if (m_pedInObjective && m_pedInObjective->IsPlayer()) return; } if (CTimer::GetTimeInMilliseconds() <= m_lookTimer) return; ScanForDelayedResponseThreats(); if (!m_threatFlags || CTimer::GetTimeInMilliseconds() <= m_threatCheckTimer) return; CheckThreatValidity(); uint32 closestThreatFlag = m_threatFlags; m_threatFlags = 0; m_threatCheckTimer = 0; if (closestThreatFlag == PED_FLAG_EXPLOSION) { float angleToFace = CGeneral::GetRadianAngleBetweenPoints( m_eventOrThreat.x, m_eventOrThreat.y, GetPosition().x, GetPosition().y); SetLookFlag(angleToFace, true); SetLookTimer(500); } else if (closestThreatFlag == PED_FLAG_GUN) { SetLookFlag(m_threatEntity, true); SetLookTimer(500); } return; } ScanForDelayedResponseThreats(); if (!m_threatFlags || CTimer::GetTimeInMilliseconds() <= m_threatCheckTimer) return; CheckThreatValidity(); uint32 closestThreatFlag = m_threatFlags; m_threatFlags = 0; m_threatCheckTimer = 0; if (closestThreatFlag == PED_FLAG_GUN) { if (!m_threatEntity || !m_threatEntity->IsPed()) return; CPed *threatPed = (CPed*)m_threatEntity; float threatDistSqr = (m_threatEntity->GetPosition() - GetPosition()).MagnitudeSqr2D(); if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared) { SetDuck(10000, true); SetLookFlag(m_threatEntity, false); SetLookTimer(500); return; } if (m_pedStats->m_fear <= m_pedStats->m_lawfulness) { if (m_pedStats->m_temper <= m_pedStats->m_fear) { if (!threatPed->IsPlayer() || !RunToReportCrime(CRIME_POSSESSION_GUN)) { if (threatDistSqr < sq(30.0f)) { bMakeFleeScream = true; SetFindPathAndFlee(m_threatEntity, 10000); } else { SetFindPathAndFlee(m_threatEntity->GetPosition(), 5000, true); } } } else if (m_objective != OBJECTIVE_NONE || GetWeapon()->IsTypeMelee()) { SetFindPathAndFlee(m_threatEntity, 5000); if (threatDistSqr < sq(20.0f)) { SetMoveState(PEDMOVE_RUN); bMakeFleeScream = true; } else { SetMoveState(PEDMOVE_WALK); } } else if (threatPed->IsPlayer() && IsGangMember() && bCanAttackPlayerWithCops) { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); } else if (threatPed->IsPlayer() && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) { SetFindPathAndFlee(m_threatEntity, 5000); if (threatDistSqr < sq(30.0f)) { SetMoveState(PEDMOVE_RUN); } else { SetMoveState(PEDMOVE_WALK); } } else { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); } } else { if (threatDistSqr < sq(30.0f)) { bMakeFleeScream = true; SetFindPathAndFlee(m_threatEntity, 10000); SetMoveState(PEDMOVE_SPRINT); } else { bMakeFleeScream = false; SetFindPathAndFlee(m_threatEntity, 5000); SetMoveState(PEDMOVE_RUN); } } SetLookFlag(m_threatEntity, false); SetLookTimer(500); } else if (closestThreatFlag == PED_FLAG_DEADPEDS) { float eventDistSqr = (m_pEventEntity->GetPosition() - GetPosition()).MagnitudeSqr2D(); if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared && eventDistSqr < sq(5.0f)) { SetDuck(10000, true); } else if (((CPed*)m_pEventEntity)->bIsDrowning || IsGangMember() && m_nPedType == ((CPed*)m_pEventEntity)->m_nPedType) { if (eventDistSqr < sq(5.0f)) { SetFindPathAndFlee(m_pEventEntity, 2000); SetMoveState(PEDMOVE_RUN); } } else if (IsGangMember() || eventDistSqr > sq(5.0f)) { bool investigateDeadPed = true; CEntity *killerOfDeadPed = ((CPed*)m_pEventEntity)->m_threatEntity; if (killerOfDeadPed && killerOfDeadPed->IsPed()) { CVector killerPos = killerOfDeadPed->GetPosition(); CVector deadPedPos = m_pEventEntity->GetPosition(); if (CVector2D(killerPos - deadPedPos).MagnitudeSqr() < sq(10.0f)) investigateDeadPed = false; } if (investigateDeadPed) SetInvestigateEvent(EVENT_DEAD_PED, CVector2D(m_pEventEntity->GetPosition()), 1.0f, 20000, 0.0f); } else { SetFindPathAndFlee(m_pEventEntity, 5000); SetMoveState(PEDMOVE_RUN); } } else if (closestThreatFlag == PED_FLAG_EXPLOSION) { CVector2D eventDistVec = m_eventOrThreat - GetPosition(); float eventDistSqr = eventDistVec.MagnitudeSqr(); if (CharCreatedBy == MISSION_CHAR && bCrouchWhenScared && eventDistSqr < sq(20.0f)) { SetDuck(10000, true); } else if (eventDistSqr < sq(20.0f)) { bMakeFleeScream = true; SetFlee(m_eventOrThreat, 2000); float angleToFace = CGeneral::GetRadianAngleBetweenPoints( m_eventOrThreat.x, m_eventOrThreat.y, GetPosition().x, GetPosition().y); SetLookFlag(angleToFace, true); SetLookTimer(500); } else if (eventDistSqr < sq(40.0f)) { if (bGonnaInvestigateEvent) { if (CharCreatedBy != MISSION_CHAR && !IsGangMember()) SetInvestigateEvent(EVENT_EXPLOSION, m_eventOrThreat, 6.0f, 30000, 0.0f); } else { float eventHeading = CGeneral::GetRadianAngleBetweenPoints(eventDistVec.x, eventDistVec.y, 0.0f, 0.0f); eventHeading = CGeneral::LimitRadianAngle(eventHeading); if (eventHeading < 0.0f) eventHeading = eventHeading + TWOPI; SetWanderPath(eventHeading / 8.0f); } } } else { if (m_threatEntity && m_threatEntity->IsPed()) { CPed *threatPed = (CPed*)m_threatEntity; if (m_pedStats->m_fear <= 100 - threatPed->m_pedStats->m_temper && threatPed->m_nPedType != PEDTYPE_COP) { if (threatPed->GetWeapon()->IsTypeMelee() || !GetWeapon()->IsTypeMelee()) { if (threatPed->IsPlayer() && IsGangMember() && bCanAttackPlayerWithCops) { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); } else if (threatPed->IsPlayer() && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) { if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) { SetFindPathAndFlee(m_threatEntity, 10000); } } else { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); } } } else { SetFindPathAndFlee(m_threatEntity, 10000, true); } } } } void CCivilianPed::ProcessControl(void) { if (CharCreatedBy == UNK_CHAR) return; CPed::ProcessControl(); if (bWasPostponed) return; if (DyingOrDead()) return; GetWeapon()->Update(m_audioEntityId, nil); switch (m_nPedState) { case PED_WANDER_RANGE: case PED_WANDER_PATH: if (IsVisible()) ScanForInterestingStuff(); break; case PED_SEEK_ENTITY: if (!m_pSeekTarget) { RestorePreviousState(); break; } m_vecSeekPos = m_pSeekTarget->GetPosition(); // fall through case PED_SEEK_POS: if (Seek()) { if ((m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA || IsUseAttractorObjective(m_objective)) && m_pNextPathNode) { m_pNextPathNode = nil; } else if (bRunningToPhone) { if (gPhoneInfo.m_aPhones[m_phoneId].m_nState != PHONE_STATE_FREE) { RestorePreviousState(); m_phoneId = -1; } else { gPhoneInfo.m_aPhones[m_phoneId].m_nState = PHONE_STATE_REPORTING_CRIME; SetPedState(PED_FACE_PHONE); } } else if (m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { if (m_pedInObjective && m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) { if (m_moved.Magnitude() == 0.0f) { if (m_pedInObjective->m_nMoveState == PEDMOVE_STILL) m_fRotationDest = m_pedInObjective->m_fRotationCur; } } else if (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT && m_pedInObjective && m_pedInObjective->m_nMoveState != PEDMOVE_STILL) { SetMoveState(m_pedInObjective->m_nMoveState); } else if (m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA || IsUseAttractorObjective(m_objective)) { SetIdle(); } else { RestorePreviousState(); } } } break; case PED_FACE_PHONE: if (FacePhone()) SetPedState(PED_MAKE_CALL); break; case PED_MAKE_CALL: if (MakePhonecall()) SetWanderPath(CGeneral::GetRandomNumber() & 7); break; case PED_MUG: Mug(); break; case PED_SOLICIT: Solicit(); break; case PED_UNKNOWN: { int pedsInSameState = 0; Idle(); for (int i = 0; i < m_numNearPeds; ++i) { CPed *nearPed = m_nearPeds[i]; if (nearPed->m_nPedType == m_nPedType && nearPed->m_nPedState == PED_UNKNOWN) { ++pedsInSameState; } } if (pedsInSameState < 5) { for (int j = 0; j < m_numNearPeds; ++j) { CPed *nearPed = m_nearPeds[j]; if (nearPed->m_nPedType == m_nPedType && nearPed->m_nPedState == PED_WANDER_PATH) { nearPed->SetPedState(PED_UNKNOWN); } } } break; } case PED_DRIVING: if (m_nPedType != PEDTYPE_PROSTITUTE) break; if (CWorld::Players[CWorld::PlayerInFocus].m_pHooker != this) break; if (CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime) { if (m_nPedState == PED_DRIVING && m_pMyVehicle->pDriver && m_pMyVehicle->pDriver->IsPlayer() && m_pMyVehicle->pDriver->m_nPedState == PED_DRIVING) { CColPoint foundCol; CEntity* foundEnt; CWorld::ProcessVerticalLine(m_pMyVehicle->GetPosition(), -100.0f, foundCol, foundEnt, true, false, false, false, false, false, nil); if (m_pMyVehicle->m_vecMoveSpeed.MagnitudeSqr() < sq(0.01f) && foundCol.surfaceB != SURFACE_DEFAULT && foundCol.surfaceB != SURFACE_TARMAC && foundCol.surfaceB != SURFACE_PAVEMENT) { if (m_pMyVehicle->CarHasRoof()) { m_pMyVehicle->ApplyTurnForce(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(-0.8f, -1.2f) * m_fMass, GetPosition().x - m_pMyVehicle->GetPosition().x, GetPosition().y - m_pMyVehicle->GetPosition().y, 0.0f); DMAudio.PlayOneShot(m_pMyVehicle->m_audioEntityId, SOUND_CAR_JERK, 0.0f); m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_BEFORESEX); Say(SOUND_PED_PLAYER_BEFORESEX); int playerSexFrequency = CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency; if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney >= 10 && playerSexFrequency > 250) { CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + playerSexFrequency; if (playerSexFrequency >= 350) { CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = Max(250, playerSexFrequency - 30); } else { CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = Max(250, playerSexFrequency - 10); } m_pMyVehicle->pDriver->m_fHealth = Min(CWorld::Players[0].m_nMaxHealth + 25.0f, 1.0f + m_pMyVehicle->pDriver->m_fHealth); if (CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency == 250) CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000; } else { bWanderPathAfterExitingCar = true; CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil; ClearLeader(); SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX); } } else { bWanderPathAfterExitingCar = true; CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil; m_pMyVehicle->pDriver->m_fHealth = CWorld::Players[0].m_nMaxHealth + 25.0f; ClearLeader(); SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX); } } else { CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000; int playerSexFrequency = CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency; if (playerSexFrequency >= 1000 || playerSexFrequency <= 250) CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 1200; else CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 250; } } else { bWanderPathAfterExitingCar = true; CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil; ClearLeader(); SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); } } if (CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime) { int playerMoney = CWorld::Players[CWorld::PlayerInFocus].m_nMoney; if (playerMoney <= 1) { CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 250; } else { CWorld::Players[CWorld::PlayerInFocus].m_nMoney = Max(0, playerMoney - 1); } CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime = CTimer::GetTimeInMilliseconds() + 1000; } break; default: break; } if (IsPedInControl()) CivilianAI(); if (CharCreatedBy == RANDOM_CHAR) { EnterVacantNearbyCars(); UseNearbyAttractors(); } if (CTimer::GetTimeInMilliseconds() > m_timerUnused) { m_stateUnused = 0; m_timerUnused = 0; } if (m_moved.Magnitude() > 0.0f) Avoid(); } bool CPed::RunToReportCrime(eCrimeType crimeToReport) { // They changed true into false to make this function unusable. So running to phone actually starts but first frame after that cancels it. if (m_nPedState == PED_SEEK_POS) return false; CVector pos = GetPosition(); int phoneId = gPhoneInfo.FindNearestFreePhone(&pos); if (phoneId == -1) return false; CPhone* phone = &gPhoneInfo.m_aPhones[phoneId]; if (phone->m_nState != PHONE_STATE_FREE) return false; bRunningToPhone = true; SetSeek(phone->m_vecPos, 0.3f); SetMoveState(PEDMOVE_RUN); m_phoneId = phoneId; m_crimeToReportOnPhone = crimeToReport; return true; } const int32 gFrequencyOfAttractorAttempt = 11; const float gDistanceToSeekAttractors = 50.0f; const float gMaxDistanceToAttract = 10.0f; /* Probably this was inlined */ void CCivilianPed::FindNearbyAttractorsSectorList(CPtrList& list, float& minDistance, C2dEffect*& pClosestAttractor, CEntity*& pAttractorEntity) { for (CPtrNode* pNode = list.first; pNode != nil; pNode = pNode->next) { CEntity* pEntity = (CEntity*)pNode->item; if (pEntity->IsObject() && (!pEntity->GetIsStatic() || ((CObject*)pEntity)->bHasBeenDamaged)) continue; CBaseModelInfo* pModelInfo = CModelInfo::GetModelInfo(pEntity->GetModelIndex()); for (int i = 0; i < pModelInfo->GetNum2dEffects(); i++) { C2dEffect* pEffect = pModelInfo->Get2dEffect(i); if (pEffect->type != EFFECT_PED_ATTRACTOR) continue; if (!IsAttractedTo(pEffect->pedattr.type)) continue; CVector pos; CPedAttractorManager::ComputeEffectPos(pEffect, pEntity->GetMatrix(), pos); if ((pos - GetPosition()).MagnitudeSqr() < minDistance) { CPedAttractorManager* pManager = GetPedAttractorManager(); if (pManager->HasEmptySlot(pEffect) && pManager->IsApproachable(pEffect, pEntity->GetMatrix(), 0, this)) { pClosestAttractor = pEffect; pAttractorEntity = pEntity; minDistance = (pos - GetPosition()).MagnitudeSqr(); } } } } } void CCivilianPed::UseNearbyAttractors() { if (CWeather::Rain < 0.2f && !m_bAttractorUnk) return; if (HasAttractor()) return; if (m_nAttractorCycleState != gFrequencyOfAttractorAttempt) { m_nAttractorCycleState++; return; } m_nAttractorCycleState = 0; if (!IsPedInControl()) return; if (m_nPedState == PED_FLEE_ENTITY) return; float left = GetPosition().x - gDistanceToSeekAttractors; float right = GetPosition().x + gDistanceToSeekAttractors; float top = GetPosition().y - gDistanceToSeekAttractors; float bottom = GetPosition().y + gDistanceToSeekAttractors; int xstart = Max(0, CWorld::GetSectorIndexX(left)); int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); int ystart = Max(0, CWorld::GetSectorIndexY(top)); int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); assert(xstart <= xend); assert(ystart <= yend); float minDistance = SQR(gMaxDistanceToAttract); C2dEffect* pClosestAttractor = nil; CEntity* pAttractorEntity = nil; for (int y = ystart; y <= yend; y++) { for (int x = xstart; x <= xend; x++) { CSector* s = CWorld::GetSector(x, y); FindNearbyAttractorsSectorList(s->m_lists[ENTITYLIST_BUILDINGS], minDistance, pClosestAttractor, pAttractorEntity); FindNearbyAttractorsSectorList(s->m_lists[ENTITYLIST_OBJECTS], minDistance, pClosestAttractor, pAttractorEntity); } } if (pClosestAttractor) GetPedAttractorManager()->RegisterPedWithAttractor(this, pClosestAttractor, pAttractorEntity->GetMatrix()); } bool CCivilianPed::IsAttractedTo(int8 type) { switch (type) { case ATTRACTOR_ATM: return true; case ATTRACTOR_SEAT: return true; case ATTRACTOR_STOP: return true; case ATTRACTOR_PIZZA: return true; case ATTRACTOR_SHELTER: return CWeather::Rain >= 0.2f; case ATTRACTOR_ICECREAM: return false; } return false; } void CCivilianPed::EnterVacantNearbyCars(void) { if (!m_bLookForVacantCars) return; if (m_bJustStoleACar && bInVehicle && m_carInObjective == m_pMyVehicle) { m_bJustStoleACar = false; m_pMyVehicle->SetStatus(STATUS_PHYSICS); m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 10; m_pMyVehicle->bEngineOn = true; } else if (!bHasAlreadyStoleACar) { if (m_nLookForVacantCarsCounter == 8) { m_nLookForVacantCarsCounter = 0; if (IsPedInControl() && m_objective == OBJECTIVE_NONE) { CVehicle *foundCar = nil; float closestDist = FLT_MAX; int minX = CWorld::GetSectorIndexX(GetPosition().x - 10.0f); if (minX < 0) minX = 0; int minY = CWorld::GetSectorIndexY(GetPosition().y - 10.0f); if (minY < 0) minY = 0; int maxX = CWorld::GetSectorIndexX(GetPosition().x + 10.0f); if (maxX > NUMSECTORS_X - 1) maxX = NUMSECTORS_X - 1; int maxY = CWorld::GetSectorIndexY(GetPosition().y + 10.0f); if (maxY > NUMSECTORS_Y - 1) maxY = NUMSECTORS_Y - 1; for (int curY = minY; curY <= maxY; curY++) { for (int curX = minX; curX <= maxX; curX++) { CSector* sector = CWorld::GetSector(curX, curY); for (CPtrNode* node = sector->m_lists[ENTITYLIST_VEHICLES].first; node; node = node->next) { CVehicle* veh = (CVehicle*)node->item; if (veh && veh->IsCar()) { // Looks like PARKED_VEHICLE condition isn't there in Mobile. if (veh->VehicleCreatedBy == RANDOM_VEHICLE || veh->VehicleCreatedBy == PARKED_VEHICLE) { if (IsOnStealWishList(veh->GetModelIndex()) && !veh->IsLawEnforcementVehicle() && (m_bStealCarEvenIfThereIsSomeoneInIt || !veh->pDriver && !veh->m_nNumPassengers) && !veh->m_nNumGettingIn && !veh->m_nGettingInFlags && !veh->m_nGettingOutFlags && !veh->m_pCarFire && veh->m_fHealth > 800.0f && !veh->IsUpsideDown() && !veh->IsOnItsSide() && veh->CanPedEnterCar()) { float dist = (GetPosition() - veh->GetPosition()).MagnitudeSqr(); if (dist < sq(10.0f) && dist < closestDist && veh->IsClearToDriveAway()) { foundCar = veh; closestDist = dist; } } } } } } } if (foundCar) { m_bJustStoleACar = true; bHasAlreadyStoleACar = true; CCarCtrl::JoinCarWithRoadSystem(foundCar); SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, foundCar); SetObjectiveTimer(10000); } } } else { ++m_nLookForVacantCarsCounter; } } } bool CCivilianPed::IsOnStealWishList(int32 model) { for (int i = 0; i < ARRAY_SIZE(m_nStealWishList); i++) { if (model == m_nStealWishList[i]) { return true; } } return false; } ================================================ FILE: src/peds/CivilianPed.h ================================================ #pragma once #include "Ped.h" class CCivilianPed : public CPed { bool m_bLookForVacantCars; uint32 m_nLookForVacantCarsCounter; bool m_bStealCarEvenIfThereIsSomeoneInIt; // unused bool m_bJustStoleACar; uint32 m_nStealWishList[16]; bool m_bAttractorUnk; int32 m_nAttractorCycleState; public: CCivilianPed(ePedType, uint32); ~CCivilianPed(void) { } void CivilianAI(void); void ProcessControl(void); void UseNearbyAttractors(void); void FindNearbyAttractorsSectorList(CPtrList&, float&, C2dEffect*&, CEntity*&); bool IsAttractedTo(int8); void EnterVacantNearbyCars(void); bool IsOnStealWishList(int32); }; //VALIDATE_SIZE(CCivilianPed, 0x53C); ================================================ FILE: src/peds/CopPed.cpp ================================================ #include "common.h" #include "World.h" #include "PlayerPed.h" #include "CopPed.h" #include "Wanted.h" #include "DMAudio.h" #include "ModelIndices.h" #include "Vehicle.h" #include "RpAnimBlend.h" #include "AnimBlendAssociation.h" #include "General.h" #include "ZoneCull.h" #include "PathFind.h" #include "RoadBlocks.h" #include "CarCtrl.h" #include "Renderer.h" #include "Camera.h" #include "PedPlacement.h" #include "Ropes.h" #include "Stinger.h" CCopPed::CCopPed(eCopType copType, int32 modifier) : CPed(PEDTYPE_COP) { m_nCopType = copType; switch (copType) { case COP_STREET: SetModelIndex(MI_COP); GiveWeapon(WEAPONTYPE_NIGHTSTICK, 1000, true); GiveDelayedWeapon(WEAPONTYPE_COLT45, 1000); m_currentWeapon = WEAPONTYPE_UNARMED; m_fArmour = 0.0f; m_wepSkills = 208; /* TODO: what is this? seems unused */ m_wepAccuracy = 60; break; case COP_FBI: SetModelIndex(MI_FBI); GiveDelayedWeapon(WEAPONTYPE_MP5, 1000); SetCurrentWeapon(WEAPONTYPE_MP5); m_fArmour = 100.0f; m_wepSkills = 176; /* TODO: what is this? seems unused */ m_wepAccuracy = 76; break; case COP_SWAT: case COP_HELI_SWAT: SetModelIndex(MI_SWAT); GiveDelayedWeapon(WEAPONTYPE_UZI, 1000); SetCurrentWeapon(WEAPONTYPE_UZI); m_fArmour = 50.0f; m_wepSkills = 32; /* TODO: what is this? seems unused */ m_wepAccuracy = 68; break; case COP_ARMY: SetModelIndex(MI_ARMY); GiveDelayedWeapon(WEAPONTYPE_MP5, 1000); SetCurrentWeapon(WEAPONTYPE_MP5); m_fArmour = 100.0f; m_wepSkills = 32; /* TODO: what is this? seems unused */ m_wepAccuracy = 84; break; case COP_MIAMIVICE: switch (modifier) { case 0: SetModelIndex(MI_VICE1); break; case 1: SetModelIndex(MI_VICE2); break; case 2: SetModelIndex(MI_VICE3); break; case 3: SetModelIndex(MI_VICE4); break; case 4: SetModelIndex(MI_VICE5); break; case 5: SetModelIndex(MI_VICE6); break; case 6: SetModelIndex(MI_VICE7); break; case 7: SetModelIndex(MI_VICE8); break; default: assert(0); break; } GiveDelayedWeapon(WEAPONTYPE_UZI, 1000); SetCurrentWeapon(WEAPONTYPE_UZI); m_fArmour = 100.0f; m_wepSkills = 176; m_wepAccuracy = 76; break; } m_bIsInPursuit = false; field_5FE = 1; m_bIsDisabledCop = false; m_attackTimer = 0; m_bBeatingSuspect = false; m_bStopAndShootDisabledZone = false; m_bDragsPlayerFromCar = false; m_bZoneDisabled = false; field_628 = -1; m_nRoadblockVeh = nil; m_bThrowsSpikeTrap = false; m_pRopeEntity = nil; m_fAbseilPos = 0.0f; m_nHassleTimer = 0; field_61C = 0; field_624 = 0; m_pStinger = new CStinger; SetWeaponLockOnTarget(nil); } CCopPed::~CCopPed() { ClearPursuit(); m_pStinger->Remove(); delete m_pStinger; } // Parameter should always be CPlayerPed, but it seems they considered making civilians arrestable at some point void CCopPed::SetArrestPlayer(CPed *player) { if (!IsPedInControl() || !player) return; player->Say(SOUND_PED_PLAYER_REACTTOCOP); Say(SOUND_PED_ARREST_COP); if (player->EnteringCar()) { if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) return; // why? player->bGonnaKillTheCarJacker = true; // Genius FindPlayerPed()->m_bCanBeDamaged = false; ((CPlayerPed*)player)->m_pArrestingCop = this; this->RegisterReference((CEntity**) &((CPlayerPed*)player)->m_pArrestingCop); } else if (player->m_nPedState != PED_DIE && player->m_nPedState != PED_DEAD && player->m_nPedState != PED_ARRESTED) { player->m_nLastPedState = player->m_nPedState; player->SetPedState(PED_ARRESTED); FindPlayerPed()->m_bCanBeDamaged = false; ((CPlayerPed*)player)->m_pArrestingCop = this; this->RegisterReference((CEntity**) &((CPlayerPed*)player)->m_pArrestingCop); } SetPedState(PED_ARREST_PLAYER); SetObjective(OBJECTIVE_NONE); m_prevObjective = OBJECTIVE_NONE; bIsPointingGunAt = false; m_pSeekTarget = player; m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); SetCurrentWeapon(WEAPONTYPE_COLT45); if (player->InVehicle()) { player->m_pMyVehicle->m_nNumGettingIn = 0; player->m_pMyVehicle->m_nGettingInFlags = 0; player->m_pMyVehicle->bIsHandbrakeOn = true; player->m_pMyVehicle->SetStatus(STATUS_PLAYER_DISABLED); } if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED || GetWeapon()->m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) SetCurrentWeapon(WEAPONTYPE_COLT45); } void CCopPed::ClearPursuit(void) { CPlayerPed *player = FindPlayerPed(); if (!player) return; CWanted *wanted = player->m_pWanted; int ourCopId = 0; bool foundMyself = false; int biggestCopId = 0; if (!m_bIsInPursuit) return; m_bIsInPursuit = false; for (int i = 0; i < Max(wanted->m_MaxCops, wanted->m_CurrentCops); ++i) { if (!foundMyself && wanted->m_pCops[i] == this) { wanted->m_pCops[i] = nil; --wanted->m_CurrentCops; foundMyself = true; ourCopId = i; biggestCopId = i; } else { if (wanted->m_pCops[i]) biggestCopId = i; } } if (foundMyself && biggestCopId > ourCopId) { wanted->m_pCops[ourCopId] = wanted->m_pCops[biggestCopId]; wanted->m_pCops[biggestCopId] = nil; } m_objective = OBJECTIVE_NONE; m_prevObjective = OBJECTIVE_NONE; m_nLastPedState = PED_NONE; bIsRunning = false; bNotAllowedToDuck = false; bKindaStayInSamePlace = false; m_bStopAndShootDisabledZone = false; m_bDragsPlayerFromCar = false; m_bZoneDisabled = false; ClearObjective(); if (IsPedInControl()) { if (!m_pMyVehicle || wanted->GetWantedLevel() != 0) { if (m_pMyVehicle && (m_pMyVehicle->GetPosition() - GetPosition()).MagnitudeSqr() < sq(5.0f)) { m_nLastPedState = PED_IDLE; SetSeek((CEntity*)m_pMyVehicle, 2.5f); } else { m_nLastPedState = PED_WANDER_PATH; SetFindPathAndFlee(FindPlayerPed()->GetPosition(), 10000, true); } } else { SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); } } } // TODO: I don't know why they needed that parameter. void CCopPed::SetPursuit(bool ignoreCopLimit) { if (CTimer::GetTimeInMilliseconds() < field_61C) return; CWanted *wanted = FindPlayerPed()->m_pWanted; if (m_bIsInPursuit || !IsPedInControl()) return; if (wanted->m_CurrentCops < wanted->m_MaxCops || ignoreCopLimit) { for (int i = 0; i < wanted->m_MaxCops; ++i) { if (!wanted->m_pCops[i]) { m_bIsInPursuit = true; ++wanted->m_CurrentCops; wanted->m_pCops[i] = this; break; } } if (m_bIsInPursuit) { ClearObjective(); m_prevObjective = OBJECTIVE_NONE; SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, FindPlayerPed()); SetObjectiveTimer(0); bNotAllowedToDuck = true; bIsRunning = true; m_bStopAndShootDisabledZone = false; } } } void CCopPed::ArrestPlayer(void) { m_pVehicleAnim = nil; CPed *suspect = (CPed*)m_pSeekTarget; if (suspect) { if (suspect->CanSetPedState()) suspect->SetPedState(PED_ARRESTED); if (suspect->bInVehicle && m_pMyVehicle && suspect->m_pMyVehicle == m_pMyVehicle) { // BUG? I will never understand why they used LINE_UP_TO_CAR_2... LineUpPedWithCar(LINE_UP_TO_CAR_2); } if (suspect && (suspect->m_nPedState == PED_ARRESTED || suspect->DyingOrDead() || suspect->EnteringCar())) { CAnimBlendAssociation *arrestAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ARREST); if (!arrestAssoc || arrestAssoc->blendDelta < 0.0f) CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ARREST, 4.0f); CVector suspMidPos; suspect->m_pedIK.GetComponentPosition(suspMidPos, PED_MID); m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(suspMidPos.x, suspMidPos.y, GetPosition().x, GetPosition().y); m_fRotationCur = m_fRotationDest; SetOrientation(0.0f, 0.0f, m_fRotationCur); } else { ClearPursuit(); } } else { ClearPursuit(); } } void CCopPed::ScanForCrimes(void) { CVehicle *playerVeh = FindPlayerVehicle(); // Look for car alarms if (playerVeh && playerVeh->IsCar()) { if (playerVeh->IsAlarmOn()) { if ((playerVeh->GetPosition() - GetPosition()).MagnitudeSqr() < sq(20.0f)) FindPlayerPed()->SetWantedLevelNoDrop(1); } } // Look for stolen cop cars if (!m_bIsInPursuit) { CPlayerPed *player = FindPlayerPed(); if ((m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) && player->m_pWanted->GetWantedLevel() == 0) { if (player->m_pMyVehicle #ifdef FIX_BUGS && m_pMyVehicle == player->m_pMyVehicle #endif && player->m_pMyVehicle->bIsLawEnforcer) player->SetWantedLevelNoDrop(1); } } } void CCopPed::CopAI(void) { CWanted *wanted = FindPlayerPed()->m_pWanted; int wantedLevel = wanted->GetWantedLevel(); CPhysical *playerOrHisVeh = FindPlayerVehicle() ? (CPhysical*)FindPlayerVehicle() : (CPhysical*)FindPlayerPed(); if (wanted->m_bIgnoredByEveryone || wanted->m_bIgnoredByCops) { if (m_nPedState != PED_ARREST_PLAYER) ClearPursuit(); return; } if (CCullZones::NoPolice() && m_bIsInPursuit && !m_bIsDisabledCop) { if (bHitSomethingLastFrame) { m_bZoneDisabled = true; m_bIsDisabledCop = true; bKindaStayInSamePlace = true; bIsRunning = false; bNotAllowedToDuck = false; bCrouchWhenShooting = false; SetIdle(); ClearObjective(); ClearPursuit(); m_prevObjective = OBJECTIVE_NONE; m_nLastPedState = PED_NONE; SetAttackTimer(0); // Safe distance for disabled zone? Or to just make game easier? if (m_fDistanceToTarget > 15.0f) m_bStopAndShootDisabledZone = true; } } else if (m_bZoneDisabled && !CCullZones::NoPolice()) { m_bZoneDisabled = false; m_bIsDisabledCop = false; m_bStopAndShootDisabledZone = false; bKindaStayInSamePlace = false; bCrouchWhenShooting = false; bDuckAndCover = false; ClearPursuit(); } if (wantedLevel > 0) { if (!m_bIsDisabledCop) { // Turn and shoot the player's vehicle, if possible if (!m_bIsInPursuit && !GetWeapon()->IsTypeMelee() && FindPlayerVehicle() && m_fDistanceToTarget < CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_fRange) { if (FindPlayerVehicle()->m_vecMoveSpeed.Magnitude2D() > 0.1f) { CVector2D distToVeh = GetPosition() - FindPlayerVehicle()->GetPosition(); distToVeh.Normalise(); CVector2D vehSpeed = FindPlayerVehicle()->m_vecMoveSpeed; vehSpeed.Normalise(); if (DotProduct2D(distToVeh, vehSpeed) > 0.8f) { SetLookFlag(playerOrHisVeh, true); SetMoveState(PEDMOVE_STILL); if (TurnBody()) { SetAttack(FindPlayerVehicle()); SetShootTimer(CGeneral::GetRandomNumberInRange(500.0f, 1000.0f)); SetAttackTimer(CGeneral::GetRandomNumberInRange(200.0f, 300.0f)); } } else if (m_nPedState == PED_ATTACK) RestorePreviousState(); } } if (!m_bIsInPursuit || wanted->m_CurrentCops > wanted->m_MaxCops) { CCopPed *copFarthestToTarget = nil; float copFarthestToTargetDist = m_fDistanceToTarget; int oldCopNum = wanted->m_CurrentCops; int maxCops = wanted->m_MaxCops; for (int i = 0; i < Max(maxCops, oldCopNum); i++) { CCopPed *cop = wanted->m_pCops[i]; if (cop && cop->m_fDistanceToTarget > copFarthestToTargetDist) { copFarthestToTargetDist = cop->m_fDistanceToTarget; copFarthestToTarget = wanted->m_pCops[i]; } } if (m_bIsInPursuit) { if (copFarthestToTarget && oldCopNum > maxCops) { if (copFarthestToTarget == this && m_fDistanceToTarget > 10.0f) { ClearPursuit(); } else if(copFarthestToTargetDist > 10.0f) copFarthestToTarget->ClearPursuit(); } } else { if (oldCopNum < maxCops) { SetPursuit(true); } else { if (m_fDistanceToTarget <= 10.0f || copFarthestToTarget && m_fDistanceToTarget < copFarthestToTargetDist) { if (copFarthestToTarget && copFarthestToTargetDist > 10.0f) copFarthestToTarget->ClearPursuit(); SetPursuit(true); } } } } else SetPursuit(false); if (!m_bIsInPursuit) return; if (wantedLevel > 1 && GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED) SetCurrentWeapon(WEAPONTYPE_COLT45); else if (wantedLevel == 1 && GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED && !FindPlayerPed()->m_pCurrentPhysSurface) { // i.e. if player is on top of car, cop will still use colt45. SetCurrentWeapon(GetWeaponSlot(WEAPONTYPE_NIGHTSTICK) >= 0 ? WEAPONTYPE_NIGHTSTICK : WEAPONTYPE_UNARMED); } if (m_bBeatingSuspect && GetWeapon()->m_eWeaponType == WEAPONTYPE_NIGHTSTICK) Say(SOUND_PED_PULLOUTWEAPON); if (FindPlayerVehicle()) { if (m_bBeatingSuspect) { --wanted->m_CopsBeatingSuspect; m_bBeatingSuspect = false; } if (m_fDistanceToTarget * FindPlayerSpeed().Magnitude() > 4.0f) ClearPursuit(); } return; } SetCurrentWeapon(WEAPONTYPE_COLT45); CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); float weaponRange = weaponInfo->m_fRange; SetLookFlag(playerOrHisVeh, true); TurnBody(); if (!bIsDucking || bCrouchWhenShooting && GetCrouchFireAnim(weaponInfo)) { if (m_attackTimer >= CTimer::GetTimeInMilliseconds()) { if (m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT && !m_bZoneDisabled) { CVector targetDist = playerOrHisVeh->GetPosition() - GetPosition(); if (m_fDistanceToTarget > 30.0f) { if (bIsDucking) ClearDuck(); // Target is coming onto us if (DotProduct(playerOrHisVeh->m_vecMoveSpeed, targetDist) > 0.0f) { m_bIsDisabledCop = false; bKindaStayInSamePlace = false; bNotAllowedToDuck = false; bDuckAndCover = false; SetPursuit(false); SetObjective(OBJECTIVE_KILL_CHAR_ANY_MEANS, FindPlayerPed()); } } else if (m_fDistanceToTarget < 5.0f && (!FindPlayerVehicle() || FindPlayerVehicle()->m_vecMoveSpeed.MagnitudeSqr() < sq(1.f/200.f))) { m_bIsDisabledCop = false; bKindaStayInSamePlace = false; bNotAllowedToDuck = false; bDuckAndCover = false; } else { float dotProd; if (m_nRoadblockVeh) { dotProd = DotProduct2D(playerOrHisVeh->GetPosition() - m_nRoadblockVeh->GetPosition(), GetPosition() - m_nRoadblockVeh->GetPosition()); } else dotProd = -1.0f; if(dotProd < 0.0f) { if (bIsDucking) ClearDuck(); m_bIsDisabledCop = false; bKindaStayInSamePlace = false; bNotAllowedToDuck = false; bCrouchWhenShooting = false; bDuckAndCover = false; SetPursuit(false); } else { bIsPointingGunAt = true; } } } } else { if (m_fDistanceToTarget < weaponRange) { CVector gunPos = weaponInfo->m_vecFireOffset; TransformToNode(gunPos, PED_HANDR); CColPoint foundCol; CEntity *foundEnt; if (!CWorld::ProcessLineOfSight(gunPos, playerOrHisVeh->GetPosition(), foundCol, foundEnt, false, true, false, false, true, false, false) || foundEnt && foundEnt == playerOrHisVeh) { SetWeaponLockOnTarget(playerOrHisVeh); SetAttack(playerOrHisVeh); SetShootTimer(CGeneral::GetRandomNumberInRange(500, 1000)); } SetAttackTimer(CGeneral::GetRandomNumberInRange(200, 300)); } SetMoveState(PEDMOVE_STILL); } } } else { if (!m_bIsDisabledCop || m_bZoneDisabled) { if (m_nPedState != PED_AIM_GUN) { if (m_bIsInPursuit) ClearPursuit(); if (IsPedInControl()) { // Entering the vehicle if (m_pMyVehicle && !bInVehicle) { if (m_pMyVehicle->IsLawEnforcementVehicle()) { if (m_pMyVehicle->pDriver) { if (m_pMyVehicle->pDriver->m_nPedType == PEDTYPE_COP) { if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_pMyVehicle); } else if (m_pMyVehicle->pDriver->IsPlayer()) { FindPlayerPed()->SetWantedLevelNoDrop(1); } } else if (m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); } } else { m_pMyVehicle = nil; ClearObjective(); SetWanderPath(CGeneral::GetRandomNumber() & 7); } } else { if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && m_objective != OBJECTIVE_HASSLE_CHAR && CharCreatedBy == RANDOM_CHAR) { for (int i = 0; i < m_numNearPeds; i++) { CPed *nearPed = m_nearPeds[i]; if (nearPed->CharCreatedBy == RANDOM_CHAR) { if ((nearPed->m_nPedType == PEDTYPE_CRIMINAL || nearPed->IsGangMember()) && nearPed->IsPedInControl()) { bool anotherCopChasesHim = false; if (nearPed->m_nPedState == PED_FLEE_ENTITY) { if (nearPed->m_fleeFrom && nearPed->m_fleeFrom->IsPed() && ((CPed*)nearPed->m_fleeFrom)->m_nPedType == PEDTYPE_COP) { anotherCopChasesHim = true; } } if (!anotherCopChasesHim) { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, nearPed); nearPed->SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, this); nearPed->bBeingChasedByPolice = true; return; } } else { if (nearPed->m_nPedType != PEDTYPE_COP && !nearPed->IsPlayer() && nearPed->IsPedInControl() && m_nHassleTimer < CTimer::GetTimeInMilliseconds()) { if (nearPed->m_objective == OBJECTIVE_NONE && nearPed->m_nPedState == PED_WANDER_PATH && !nearPed->m_pLookTarget && nearPed->m_lookTimer < CTimer::GetTimeInMilliseconds()) { if ((GetPosition() - nearPed->GetPosition()).MagnitudeSqr() < sq(5.0f)) { if (CWorld::GetIsLineOfSightClear(GetPosition(), nearPed->GetPosition(), true, false, false, false, false, false, false)) { Say(SOUND_PED_COP_REACTION); SetObjective(OBJECTIVE_HASSLE_CHAR, nearPed); nearPed->SetObjective(OBJECTIVE_WAIT_ON_FOOT_FOR_COP, this); m_nHassleTimer = CTimer::GetTimeInMilliseconds() + 100000; } } } } } } } } } } } } else { if (m_bIsInPursuit && m_nPedState != PED_AIM_GUN) ClearPursuit(); m_bIsDisabledCop = false; bKindaStayInSamePlace = false; bNotAllowedToDuck = false; bCrouchWhenShooting = false; bDuckAndCover = false; if (bIsDucking) ClearDuck(); if (m_pMyVehicle) SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); } } } void CCopPed::ProcessControl(void) { if (m_nCopType == COP_HELI_SWAT) ProcessHeliSwat(); CPed::ProcessControl(); if (m_bThrowsSpikeTrap) { if (CGame::currArea != AREA_MALL) ProcessStingerCop(); return; } if (m_pStinger && m_pStinger->bIsDeployed && m_pStinger->m_nSpikeState == STINGERSTATE_DEPLOYED && CGame::currArea != AREA_MALL) m_pStinger->Process(); if (bWasPostponed) return; if (m_nPedState == PED_DEAD) { ClearPursuit(); m_objective = OBJECTIVE_NONE; return; } if (m_nPedState == PED_DIE) return; if (m_nPedState == PED_ARREST_PLAYER) { ArrestPlayer(); return; } GetWeapon()->Update(m_audioEntityId, nil); if (m_moved.Magnitude() > 0.0f) Avoid(); CPhysical *playerOrHisVeh = FindPlayerVehicle() ? (CPhysical*)FindPlayerVehicle() : (CPhysical*)FindPlayerPed(); CPlayerPed *player = FindPlayerPed(); m_fDistanceToTarget = (playerOrHisVeh->GetPosition() - GetPosition()).Magnitude(); if (player->m_nPedState == PED_ARRESTED || player->DyingOrDead()) { if (m_fDistanceToTarget < 5.0f) { SetArrestPlayer(player); return; } if (IsPedInControl()) SetIdle(); } if (m_bIsInPursuit) { if (player->m_nPedState != PED_ARRESTED && !player->DyingOrDead()) { if (player->m_pWanted->m_CurrentCops == 1) { Say(SOUND_PED_COP_ALONE); } else { int numCopsNear = 0; for (int i = 0; i < player->m_numNearPeds; ++i) { CPed *nearPed = player->m_nearPeds[i]; if (nearPed->m_nPedType == PEDTYPE_COP && nearPed->m_nPedState != PED_DEAD) ++numCopsNear; } if (numCopsNear <= 3) { Say(SOUND_PED_COP_LITTLECOPSAROUND); if (!player->bInVehicle) { CVector distToPlayer = player->GetPosition() - GetPosition(); if (distToPlayer.MagnitudeSqr() < sq(20.0f)) { player->Say(SOUND_PED_PLAYER_FARFROMCOPS); if (player->m_nPedState != PED_ATTACK && player->m_nPedState != PED_AIM_GUN) { player->SetLookFlag(this, false); player->SetLookTimer(1000); } } } } else if ((CGeneral::GetRandomNumber() % 16) == 1) { Say(SOUND_PED_COP_MANYCOPSAROUND); } } } } if (IsPedInControl()) { CopAI(); /* switch (m_nCopType) { case COP_FBI: CopAI(); break; case COP_SWAT: CopAI(); break; case COP_ARMY: CopAI(); break; default: CopAI(); break; } */ } else if (InVehicle()) { if (m_pMyVehicle->pDriver == this && m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_NONE && CanPedDriveOff() && m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE) { CCarCtrl::JoinCarWithRoadSystem(m_pMyVehicle); m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 17; } } if (IsPedInControl() || m_nPedState == PED_DRIVING) ScanForCrimes(); // They may have used goto to jump here in case of PED_ATTACK. if (m_nPedState == PED_IDLE || m_nPedState == PED_ATTACK) { if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT && player && player->EnteringCar() && m_fDistanceToTarget < 1.3f) { SetArrestPlayer(player); } } else { if (m_nPedState == PED_SEEK_POS) { if (player->m_nPedState == PED_ARRESTED) { SetIdle(); SetLookFlag(player, false); SetLookTimer(1000); RestorePreviousObjective(); } else { if (player->m_pMyVehicle && player->m_pMyVehicle->m_nNumGettingIn != 0) { m_distanceToCountSeekDone = 1.3f; } if (!bDuckAndCover && Seek()) { CVehicle *playerVeh = FindPlayerVehicle(); if (!playerVeh && player && player->EnteringCar()) { SetArrestPlayer(player); } else if (1.5f + GetPosition().z <= m_vecSeekPos.z || GetPosition().z - 0.3f >= m_vecSeekPos.z) { SetMoveState(PEDMOVE_STILL); } else if (playerVeh && playerVeh->CanPedEnterCar() && playerVeh->m_nNumGettingIn == 0) { SetCarJack(playerVeh); } } } } else if (m_nPedState == PED_SEEK_ENTITY) { if (!m_pSeekTarget) { RestorePreviousState(); } else { m_vecSeekPos = m_pSeekTarget->GetPosition(); if (Seek()) { if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT && m_fDistanceToTarget < 2.5f && player) { if (player->m_nPedState == PED_ARRESTED || player->m_nPedState == PED_ENTER_CAR || (player->m_nPedState == PED_CARJACK && m_fDistanceToTarget < 1.3f)) { SetArrestPlayer(player); } else RestorePreviousState(); } else { RestorePreviousState(); } } } } } if (m_pPointGunAt) Say(SOUND_PED_COP_UNK_129); if (m_bStopAndShootDisabledZone) { bool dontShoot = false; if (GetIsOnScreen()) { if (((CTimer::GetFrameCounter() + m_randomSeed) & 0x1F) == 17) { CEntity* foundBuilding = nil; CColPoint foundCol; CVector lookPos = GetPosition() + CVector(0.0f, 0.0f, 0.7f); CVector camPos = TheCamera.GetGameCamPosition(); CWorld::ProcessLineOfSight(camPos, lookPos, foundCol, foundBuilding, true, false, false, false, false, false, false); // He's at least 15.0 far, in disabled zone, collided into somewhere (that's why m_bStopAndShootDisabledZone set), // and now has building on front of him. He's stupid, we don't need him. if (foundBuilding) { FlagToDestroyWhenNextProcessed(); dontShoot = true; } } } else { FlagToDestroyWhenNextProcessed(); dontShoot = true; } if (!dontShoot) { bStopAndShoot = true; bKindaStayInSamePlace = true; bIsPointingGunAt = true; SetAttack(m_pedInObjective); } } if (field_624 >= 2 && m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT) { CVector centre = GetPosition() + CVector(0.f, 0.f, 0.65f); if (CWorld::TestSphereAgainstWorld(centre, 0.35f, this, true, false, false, false, false, false)) { field_624 = 0; m_bStopAndShootDisabledZone = true; ClearPursuit(); SetObjective(OBJECTIVE_NONE); SetWanderPath(CGeneral::GetRandomNumberInRange(0,8)); field_61C = CTimer::GetTimeInMilliseconds() + 30000; } else { field_624 = 0; if (GetWeapon()->IsTypeMelee()) { // TODO(Miami): enum for (int i = 3; i < 7; i++) { if (HasWeaponSlot(i)) { SetCurrentWeapon(i); break; } } SetMoveState(PEDMOVE_STILL); bStopAndShoot = true; } } } else if (CTimer::GetTimeStep() / 100.f <= m_fDistanceTravelled) field_624 = 0; } void CCopPed::ProcessHeliSwat(void) { CVector bestPos = GetPosition(); SetPedState(PED_ABSEIL); CPedPlacement::FindZCoorForPed(&bestPos); if (GetPosition().z - 2.0f >= bestPos.z && m_pRopeEntity) { m_fAbseilPos += 0.003f * CTimer::GetTimeStep(); m_vecMoveSpeed.z = -0.03f; m_vecTurnSpeed = CVector(0.f, 0.f, (m_randomSeed % 32) * 0.003f - 0.05f); CPhysical::ApplyTurnSpeed(); GetMatrix().Reorthogonalise(); CVector posOnRope; if (CRopes::FindCoorsAlongRope(m_nRopeID, m_fAbseilPos, &posOnRope)) { SetPosition(posOnRope); } else { bUsesCollision = true; m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); SetPedState(PED_IDLE); m_nCopType = COP_SWAT; SetInTheAir(); bKnockedUpIntoAir = true; } Say(SOUND_PED_COP_HELIPILOTPHRASE); } else { bUsesCollision = true; m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); SetPedState(PED_IDLE); m_nCopType = COP_SWAT; SetInTheAir(); bKnockedUpIntoAir = true; } } void CCopPed::ProcessStingerCop(void) { if (m_pStinger->bIsDeployed || FindPlayerVehicle() && (FindPlayerVehicle()->IsCar() || FindPlayerVehicle()->IsBike())) { if (m_pStinger->bIsDeployed) { m_pStinger->Process(); } else { CVector2D vehDist = GetPosition() - FindPlayerVehicle()->GetPosition(); CVector2D dirVehGoing = FindPlayerVehicle()->m_vecMoveSpeed; if (vehDist.MagnitudeSqr() < sq(30.0f)) { if (dirVehGoing.MagnitudeSqr() > 0.0f) { vehDist.Normalise(); dirVehGoing.Normalise(); if (DotProduct2D(vehDist, dirVehGoing) > 0.8f) { float angle = (CrossProduct2D(vehDist, dirVehGoing - vehDist) < 0.0f ? FindPlayerVehicle()->GetForward().Heading() - HALFPI : HALFPI + FindPlayerVehicle()->GetForward().Heading()); SetHeading(angle); m_fRotationCur = angle; m_fRotationDest = angle; m_pStinger->Deploy(this); } } } } } else { ClearPursuit(); } } ================================================ FILE: src/peds/CopPed.h ================================================ #pragma once #include "Ped.h" enum eCopType { COP_STREET = 0, COP_FBI = 1, COP_SWAT = 2, COP_HELI_SWAT = 3, COP_ARMY = 4, COP_MIAMIVICE = 5 }; class CCopPed : public CPed { public: CVehicle* m_nRoadblockVeh; float m_fDistanceToTarget; bool m_bIsInPursuit; bool m_bIsDisabledCop; int8 field_5FE; bool m_bBeatingSuspect; bool m_bStopAndShootDisabledZone; bool m_bDragsPlayerFromCar; bool m_bZoneDisabled; float m_fAbseilPos; eCopType m_nCopType; bool m_bThrowsSpikeTrap; CEntity *m_pRopeEntity; // CHeli or 1 uintptr m_nRopeID; uint32 m_nHassleTimer; uint32 field_61C; class CStinger *m_pStinger; int32 field_624; int8 field_628; CCopPed(eCopType, int32 modifier = 0); ~CCopPed(); void ClearPursuit(void); void ProcessControl(void); void SetArrestPlayer(CPed*); void SetPursuit(bool); void ArrestPlayer(void); void ScanForCrimes(void); void CopAI(void); void ProcessHeliSwat(void); void ProcessStingerCop(void); }; VALIDATE_SIZE(CCopPed, 0x62C); ================================================ FILE: src/peds/DummyPed.h ================================================ #pragma once #include "Dummy.h" // actually unused class CDummyPed : CDummy { int32 pedType; int32 unknown; }; ================================================ FILE: src/peds/EmergencyPed.cpp ================================================ #include "common.h" #include "EmergencyPed.h" #include "DMAudio.h" #include "ModelIndices.h" #include "Vehicle.h" #include "Fire.h" #include "General.h" #include "CarCtrl.h" #include "Accident.h" CEmergencyPed::CEmergencyPed(uint32 type) : CPed(type) { switch (type){ case PEDTYPE_EMERGENCY: SetModelIndex(MI_MEDIC); m_pRevivedPed = nil; field_1360 = 0; break; case PEDTYPE_FIREMAN: SetModelIndex(MI_FIREMAN); m_pRevivedPed = nil; break; default: break; } m_nEmergencyPedState = EMERGENCY_PED_READY; m_pAttendedAccident = nil; m_bStartedToCPR = false; } bool CEmergencyPed::InRange(CPed *victim) { if (!m_pMyVehicle) return true; if ((m_pMyVehicle->GetPosition() - victim->GetPosition()).Magnitude() > 30.0f) return false; return true; } void CEmergencyPed::ProcessControl(void) { CPed::ProcessControl(); if (bWasPostponed) return; if(!DyingOrDead()) { GetWeapon()->Update(m_audioEntityId, nil); if (IsPedInControl() && m_moved.Magnitude() > 0.0f) Avoid(); switch (m_nPedState) { case PED_SEEK_POS: Seek(); break; case PED_SEEK_ENTITY: if (m_pSeekTarget) { m_vecSeekPos = m_pSeekTarget->GetPosition(); Seek(); } else { ClearSeek(); } break; default: break; } switch (m_nPedType) { case PEDTYPE_EMERGENCY: if (IsPedInControl() || m_nPedState == PED_DRIVING) MedicAI(); break; case PEDTYPE_FIREMAN: if (IsPedInControl()) FiremanAI(); break; default: return; } } } // This function was buggy and incomplete in both III and VC, firemen had to be in 5.0m range of fire etc. etc. // Copied some code from MedicAI to make it work. void CEmergencyPed::FiremanAI(void) { float fireDist; CFire *nearestFire; switch (m_nEmergencyPedState) { case EMERGENCY_PED_READY: nearestFire = gFireManager.FindNearestFire(GetPosition(), &fireDist); if (nearestFire) { SetPedState(PED_NONE); SetSeek(nearestFire->m_vecPos, 1.0f); SetMoveState(PEDMOVE_RUN); m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; m_pAttendedFire = nearestFire; #ifdef FIX_BUGS bIsRunning = true; #endif } break; case EMERGENCY_PED_DETERMINE_NEXT_STATE: nearestFire = gFireManager.FindNearestFire(GetPosition(), &fireDist); if (nearestFire && nearestFire != m_pAttendedFire) { SetPedState(PED_NONE); SetSeek(nearestFire->m_vecPos, 1.0f); SetMoveState(PEDMOVE_RUN); #ifdef FIX_BUGS bIsRunning = true; m_pAttendedFire = nearestFire; } else if (!nearestFire) { #else m_pAttendedFire = nearestFire; } else { #endif m_nEmergencyPedState = EMERGENCY_PED_STOP; } // "Extinguish" the fire (Will overwrite the stop decision above if the attended and nearest fires are same) if (fireDist < 5.0f) { SetIdle(); m_nEmergencyPedState = EMERGENCY_PED_STAND_STILL; } break; case EMERGENCY_PED_STAND_STILL: if (!m_pAttendedFire->m_bIsOngoing) m_nEmergencyPedState = EMERGENCY_PED_STOP; // Leftover // fireDist = 30.0f; nearestFire = gFireManager.FindNearestFire(GetPosition(), &fireDist); if (nearestFire) { #ifdef FIX_BUGS if(nearestFire != m_pAttendedFire && (nearestFire->m_vecPos - GetPosition()).Magnitude() < 30.0f) #endif m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; } Say(SOUND_PED_EXTINGUISHING_FIRE); break; case EMERGENCY_PED_STOP: #ifdef FIX_BUGS bIsRunning = false; #endif SetPedState(PED_NONE); SetWanderPath(CGeneral::GetRandomNumber() & 7); m_pAttendedFire = nil; m_nEmergencyPedState = EMERGENCY_PED_READY; SetMoveState(PEDMOVE_WALK); break; default: break; } } void CEmergencyPed::MedicAI(void) { float distToEmergency; if (!bInVehicle && IsPedInControl()) { ScanForDelayedResponseThreats(); if (m_threatFlags && CTimer::GetTimeInMilliseconds() > m_threatCheckTimer) { CheckThreatValidity(); m_threatFlags = 0; m_threatCheckTimer = 0; if (m_threatEntity && m_threatEntity->IsPed() && ((CPed*)m_threatEntity)->IsPlayer()) { if (((CPed*)m_threatEntity)->GetWeapon()->IsTypeMelee()) { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); } else { SetFlee(m_threatEntity, 6000); Say(SOUND_PED_FLEE_SPRINT); } return; } } } if (InVehicle()) { if (m_pMyVehicle->IsCar() && m_objective != OBJECTIVE_LEAVE_CAR) { if (gAccidentManager.FindNearestAccident(m_pMyVehicle->GetPosition(), &distToEmergency) && distToEmergency < 25.0f && m_pMyVehicle->m_vecMoveSpeed.Magnitude() < 0.01f) { m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE; SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); Say(SOUND_PED_LEAVE_VEHICLE); } else if (m_pMyVehicle->pDriver == this && m_nPedState == PED_DRIVING && m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_NONE && !(CGeneral::GetRandomNumber() & 31)) { bool waitUntilMedicEntersCar = false; for (int i = 0; i < m_numNearPeds; ++i) { CPed *nearPed = m_nearPeds[i]; if (nearPed->m_nPedType == PEDTYPE_EMERGENCY) { if ((nearPed->m_nPedState == PED_SEEK_CAR || nearPed->m_nPedState == PED_ENTER_CAR) && nearPed->m_pMyVehicle == m_pMyVehicle) { waitUntilMedicEntersCar = true; break; } } } if (!waitUntilMedicEntersCar) { CCarCtrl::JoinCarWithRoadSystem(m_pMyVehicle); m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; m_pMyVehicle->m_bSirenOrAlarm = 0; m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 12; m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_SLOW_DOWN_FOR_CARS; if (m_pMyVehicle->bIsAmbulanceOnDuty) { m_pMyVehicle->bIsAmbulanceOnDuty = false; --CCarCtrl::NumAmbulancesOnDuty; } } } } } CVector headPos, midPos; CAccident *nearestAccident; if (IsPedInControl()) { switch (m_nEmergencyPedState) { case EMERGENCY_PED_READY: nearestAccident = gAccidentManager.FindNearestAccident(GetPosition(), &distToEmergency); field_1360 = 0; if (nearestAccident) { m_pRevivedPed = nearestAccident->m_pVictim; m_pRevivedPed->RegisterReference((CEntity**)&m_pRevivedPed); m_pRevivedPed->m_pedIK.GetComponentPosition(midPos, PED_MID); m_pRevivedPed->m_pedIK.GetComponentPosition(headPos, PED_HEAD); SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector((headPos + midPos) * 0.5f)); bIsRunning = true; m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; m_pAttendedAccident = nearestAccident; ++m_pAttendedAccident->m_nMedicsAttending; } else { if (m_pMyVehicle) { if (!bInVehicle) { if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_pMyVehicle->pDriver || m_pMyVehicle->m_nGettingInFlags) { CPed* driver = m_pMyVehicle->pDriver; if (driver && driver->m_nPedType != PEDTYPE_EMERGENCY && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, driver); } else if (m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER && m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_pMyVehicle); } } else { SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); } } } else if (m_nPedState != PED_WANDER_PATH) { SetWanderPath(CGeneral::GetRandomNumber() & 7); } } break; case EMERGENCY_PED_DETERMINE_NEXT_STATE: nearestAccident = gAccidentManager.FindNearestAccident(GetPosition(), &distToEmergency); if (nearestAccident) { if (nearestAccident != m_pAttendedAccident || m_nPedState != PED_SEEK_POS) { m_pRevivedPed = nearestAccident->m_pVictim; m_pRevivedPed->RegisterReference((CEntity**)&m_pRevivedPed); if (!InRange(m_pRevivedPed)) { m_nEmergencyPedState = EMERGENCY_PED_STOP; break; } m_pRevivedPed->m_pedIK.GetComponentPosition(midPos, PED_MID); m_pRevivedPed->m_pedIK.GetComponentPosition(headPos, PED_HEAD); SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, CVector((headPos + midPos) * 0.5f)); bIsRunning = true; --m_pAttendedAccident->m_nMedicsAttending; ++nearestAccident->m_nMedicsAttending; m_pAttendedAccident = nearestAccident; } } else { m_nEmergencyPedState = EMERGENCY_PED_STOP; bIsRunning = false; } if (distToEmergency < 5.0f) { if (m_pRevivedPed->m_pFire) { bIsRunning = false; SetMoveState(PEDMOVE_STILL); } else if (distToEmergency < 4.5f) { bIsRunning = false; SetMoveState(PEDMOVE_WALK); if (distToEmergency < 1.0f || distToEmergency < 4.5f && m_pAttendedAccident->m_nMedicsPerformingCPR) { m_nEmergencyPedState = EMERGENCY_PED_START_CPR; } } } break; case EMERGENCY_PED_START_CPR: if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f || m_pRevivedPed->bFadeOut) { m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; } else { m_pRevivedPed->m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds(); SetMoveState(PEDMOVE_STILL); SetPedState(PED_CPR); m_nLastPedState = PED_CPR; SetLookFlag(m_pRevivedPed, 0); SetLookTimer(500); Say(SOUND_PED_HEALING); if (m_pAttendedAccident->m_nMedicsPerformingCPR) { SetIdle(); m_nEmergencyPedState = EMERGENCY_PED_STAND_STILL; } else { m_nEmergencyPedState = EMERGENCY_PED_FACE_TO_PATIENT; m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_MEDIC, ANIM_MEDIC_CPR, 4.0f); bIsDucking = true; } SetLookTimer(2000); ++m_pAttendedAccident->m_nMedicsPerformingCPR; m_bStartedToCPR = true; } break; case EMERGENCY_PED_FACE_TO_PATIENT: if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f) m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; else { m_pRevivedPed->m_pedIK.GetComponentPosition(midPos, PED_MID); m_pRevivedPed->m_pedIK.GetComponentPosition(headPos, PED_HEAD); midPos = (headPos + midPos) * 0.5f; m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( midPos.x, midPos.y, GetPosition().x, GetPosition().y); m_fRotationDest = CGeneral::LimitAngle(m_fRotationDest); m_pLookTarget = m_pRevivedPed; m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); TurnBody(); if (Abs(m_fRotationCur - m_fRotationDest) < DEGTORAD(45.0f)) m_nEmergencyPedState = EMERGENCY_PED_PERFORM_CPR; else m_fRotationCur = (m_fRotationCur + m_fRotationDest) * 0.5f; } break; case EMERGENCY_PED_PERFORM_CPR: if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f) { m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; break; } m_pRevivedPed->m_pedIK.GetComponentPosition(midPos, PED_MID); m_pRevivedPed->m_pedIK.GetComponentPosition(headPos, PED_HEAD); midPos = (headPos + midPos) * 0.5f; m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( midPos.x, midPos.y, GetPosition().x, GetPosition().y); m_fRotationDest = CGeneral::LimitAngle(m_fRotationDest); m_pLookTarget = m_pRevivedPed; m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); TurnBody(); if (CTimer::GetTimeInMilliseconds() <= m_lookTimer) { SetMoveState(PEDMOVE_STILL); break; } m_nEmergencyPedState = EMERGENCY_PED_STOP_CPR; SetPedState(PED_NONE); SetMoveState(PEDMOVE_WALK); m_pVehicleAnim = nil; if (!m_pRevivedPed->bBodyPartJustCameOff) { m_pRevivedPed->m_fHealth = 100.0f; m_pRevivedPed->SetPedState(PED_NONE); m_pRevivedPed->m_nLastPedState = PED_WANDER_PATH; m_pRevivedPed->SetGetUp(); m_pRevivedPed->bUsesCollision = true; m_pRevivedPed->SetMoveState(PEDMOVE_WALK); m_pRevivedPed->RestartNonPartialAnims(); m_pRevivedPed->bIsPedDieAnimPlaying = false; m_pRevivedPed->bKnockedUpIntoAir = false; m_pRevivedPed->m_pCollidingEntity = nil; m_pRevivedPed->bKnockedOffBike = false; m_pRevivedPed->Say(SOUND_PED_ACCIDENTREACTION1); } break; case EMERGENCY_PED_STOP_CPR: m_nEmergencyPedState = EMERGENCY_PED_STOP; bIsDucking = true; break; case EMERGENCY_PED_STAND_STILL: if (!m_pRevivedPed || m_pRevivedPed->m_fHealth > 0.0f) m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; else { if (!m_pAttendedAccident->m_pVictim) m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; if (!m_pAttendedAccident->m_nMedicsPerformingCPR) m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; if (gAccidentManager.UnattendedAccidents()) m_nEmergencyPedState = EMERGENCY_PED_DETERMINE_NEXT_STATE; } break; case EMERGENCY_PED_STOP: m_bStartedToCPR = false; SetPedState(PED_NONE); if (m_pAttendedAccident) { m_pAttendedAccident->m_pVictim = nil; --m_pAttendedAccident->m_nMedicsAttending; m_pAttendedAccident = nil; } SetWanderPath(CGeneral::GetRandomNumber() & 7); m_pRevivedPed = nil; m_nEmergencyPedState = EMERGENCY_PED_READY; SetMoveState(PEDMOVE_WALK); break; default: break; } } } ================================================ FILE: src/peds/EmergencyPed.h ================================================ #pragma once #include "Ped.h" class CAccident; class CFire; enum EmergencyPedState { EMERGENCY_PED_READY = 0x0, EMERGENCY_PED_DETERMINE_NEXT_STATE = 0x1, // you can set that anytime you want EMERGENCY_PED_START_CPR = 0x2, EMERGENCY_PED_FLAG_4 = 0x4, // unused EMERGENCY_PED_FLAG_8 = 0x8, // unused EMERGENCY_PED_FACE_TO_PATIENT = 0x10, // for CPR EMERGENCY_PED_PERFORM_CPR = 0x20, EMERGENCY_PED_STOP_CPR = 0x40, EMERGENCY_PED_STAND_STILL = 0x80, // waiting colleagues for medics, "extinguishing" fire for firemen EMERGENCY_PED_STOP = 0x100, }; class CEmergencyPed : public CPed { public: CPed *m_pRevivedPed; EmergencyPedState m_nEmergencyPedState; CAccident *m_pAttendedAccident; CFire *m_pAttendedFire; bool m_bStartedToCPR; // set but unused int32 field_1360; // set to 0 but unused CEmergencyPed(uint32); ~CEmergencyPed() { } bool InRange(CPed*); void ProcessControl(void); void FiremanAI(void); void MedicAI(void); }; //VALIDATE_SIZE(CEmergencyPed, 0x554); ================================================ FILE: src/peds/Gangs.cpp ================================================ #include "common.h" #include "ModelIndices.h" #include "Gangs.h" #include "General.h" #include "Streaming.h" #include "Weapon.h" CGangInfo CGangs::Gang[NUM_GANGS]; bool CGangs::GangAttackWithCops[NUM_GANGS]; CGangInfo::CGangInfo() : m_nVehicleMI(-1), m_nPedModel1MI(-1), m_nPedModel2MI(-1), m_nPedModelOverride(-1), m_Weapon1(WEAPONTYPE_UNARMED), m_Weapon2(WEAPONTYPE_UNARMED) {} void CGangs::Initialise(void) { SetGangPedModels(GANG_CUBAN, MI_CBA, MI_CBB); SetGangPedModels(GANG_HAITIAN, MI_HNA, MI_HNB); SetGangPedModels(GANG_STREET, MI_SGA, MI_SGB); SetGangPedModels(GANG_DIAZ, MI_CLA, MI_CLB); SetGangPedModels(GANG_SECURITY, MI_GDA, MI_GDB); SetGangPedModels(GANG_BIKER, MI_BKA, MI_BKB); SetGangPedModels(GANG_PLAYER, MI_PGA, MI_PGB); SetGangPedModels(GANG_GOLFER, MI_WFOGO, MI_WMOGO); SetGangVehicleModel(GANG_CUBAN, MI_CUBAN); SetGangVehicleModel(GANG_HAITIAN, MI_VOODOO); SetGangVehicleModel(GANG_STREET, MI_GANGBUR); SetGangVehicleModel(GANG_DIAZ, -1); SetGangVehicleModel(GANG_SECURITY, -1); SetGangVehicleModel(GANG_BIKER, MI_ANGEL); SetGangVehicleModel(GANG_PLAYER, -1); SetGangVehicleModel(GANG_GOLFER, MI_CADDY); SetGangWeapons(GANG_GOLFER, WEAPONTYPE_GOLFCLUB, WEAPONTYPE_GOLFCLUB); #ifdef FIX_BUGS for (int i = 0; i < NUM_GANGS; i++) SetGangPedModelOverride(i, -1); #endif } bool CGangs::HaveGangModelsLoaded(int16 gang) { CGangInfo* pGangInfo = GetGangInfo(gang); return CStreaming::HasModelLoaded(pGangInfo->m_nPedModel1MI) && CStreaming::HasModelLoaded(pGangInfo->m_nPedModel2MI); } void CGangs::SetGangPedModels(int16 gang, int32 mi1, int32 mi2) { GetGangInfo(gang)->m_nPedModel1MI = mi1; GetGangInfo(gang)->m_nPedModel2MI = mi2; } void CGangs::SetWillAttackPlayerWithCops(ePedType type, bool will) { if (type >= PEDTYPE_GANG1 && type <= PEDTYPE_GANG9) GangAttackWithCops[type - PEDTYPE_GANG1] = will; } bool CGangs::GetWillAttackPlayerWithCops(ePedType type) { if (type >= PEDTYPE_GANG1 && type <= PEDTYPE_GANG9) return GangAttackWithCops[type - PEDTYPE_GANG1]; return false; } int32 CGangs::ChooseGangPedModel(int16 gang) { CGangInfo* pGangInfo = GetGangInfo(gang); if (pGangInfo->m_nPedModelOverride != -1 || CGeneral::GetRandomTrueFalse()) return pGangInfo->m_nPedModel1MI; else return pGangInfo->m_nPedModel2MI; } void CGangs::SetGangVehicleModel(int16 gang, int32 model) { GetGangInfo(gang)->m_nVehicleMI = model; } void CGangs::SetGangWeapons(int16 gang, int32 weapon1, int32 weapon2) { CGangInfo *gi = GetGangInfo(gang); gi->m_Weapon1 = weapon1; gi->m_Weapon2 = weapon2; } void CGangs::SetGangPedModelOverride(int16 gang, int8 ovrd) { GetGangInfo(gang)->m_nPedModelOverride = ovrd; } int8 CGangs::GetGangPedModelOverride(int16 gang) { return GetGangInfo(gang)->m_nPedModelOverride; } void CGangs::SaveAllGangData(uint8 *buf, uint32 *size) { INITSAVEBUF *size = SAVE_HEADER_SIZE + sizeof(Gang); WriteSaveHeader(buf, 'G','N','G','\0', *size - SAVE_HEADER_SIZE); for (int i = 0; i < NUM_GANGS; i++) WriteSaveBuf(buf, Gang[i]); VALIDATESAVEBUF(*size); } void CGangs::LoadAllGangData(uint8 *buf, uint32 size) { Initialise(); INITSAVEBUF CheckSaveHeader(buf, 'G','N','G','\0', size - SAVE_HEADER_SIZE); for (int i = 0; i < NUM_GANGS; i++) Gang[i] = ReadSaveBuf(buf); VALIDATESAVEBUF(size); } ================================================ FILE: src/peds/Gangs.h ================================================ #pragma once #include "PedType.h" struct CGangInfo { int32 m_nVehicleMI; int32 m_nPedModel1MI; int32 m_nPedModel2MI; int8 m_nPedModelOverride; int32 m_Weapon1; int32 m_Weapon2; CGangInfo(); }; VALIDATE_SIZE(CGangInfo, 0x10); enum { GANG_CUBAN = 0, GANG_HAITIAN, GANG_STREET, GANG_DIAZ, GANG_SECURITY, GANG_BIKER, GANG_PLAYER, GANG_GOLFER, GANG_9, NUM_GANGS }; class CGangs { public: static void Initialise(void); static void SetGangVehicleModel(int16, int32); static void SetGangWeapons(int16, int32, int32); static void SetGangPedModelOverride(int16, int8); static int8 GetGangPedModelOverride(int16); static void SaveAllGangData(uint8 *, uint32 *); static void LoadAllGangData(uint8 *, uint32); static void SetGangPedModels(int16, int32, int32); static void SetWillAttackPlayerWithCops(ePedType type, bool will); static bool GetWillAttackPlayerWithCops(ePedType type); static int32 ChooseGangPedModel(int16); static bool HaveGangModelsLoaded(int16 gang); static int32 GetGangPedModel1(int16 gang) { return Gang[gang].m_nPedModel1MI; } static int32 GetGangPedModel2(int16 gang) { return Gang[gang].m_nPedModel2MI; } static int32 GetGangVehicleModel(int16 gang) { return Gang[gang].m_nVehicleMI; } static CGangInfo *GetGangInfo(int16 gang) { return &Gang[gang]; } private: static CGangInfo Gang[NUM_GANGS]; static bool GangAttackWithCops[NUM_GANGS]; }; ================================================ FILE: src/peds/Ped.cpp ================================================ #include "common.h" #include "main.h" #include "Pools.h" #include "Particle.h" #include "RpAnimBlend.h" #include "Bones.h" #include "Ped.h" #include "AnimBlendAssociation.h" #include "Fire.h" #include "DMAudio.h" #include "General.h" #include "VisibilityPlugins.h" #include "HandlingMgr.h" #include "Replay.h" #include "Radar.h" #include "PedPlacement.h" #include "Shadows.h" #include "Weather.h" #include "ZoneCull.h" #include "Population.h" #include "Pad.h" #include "Phones.h" #include "TrafficLights.h" #include "CopPed.h" #include "Script.h" #include "CarCtrl.h" #include "Garages.h" #include "WaterLevel.h" #include "Timecycle.h" #include "ParticleObject.h" #include "Floater.h" #include "Range2D.h" #include "Streaming.h" #include "PedAttractor.h" #include "GameLogic.h" #include "Bike.h" #include "WindModifiers.h" #include "CutsceneShadow.h" #include "Clock.h" #include "Wanted.h" CPed *gapTempPedList[50]; uint16 gnNumTempPedList; static CColPoint aTempPedColPts[MAX_COLLISION_POINTS]; uint16 CPed::nThreatReactionRangeMultiplier = 1; uint16 CPed::nEnterCarRangeMultiplier = 1; bool CPed::bNastyLimbsCheat; bool CPed::bFannyMagnetCheat; bool CPed::bPedCheat3; CVector2D CPed::ms_vec2DFleePosition; void *CPed::operator new(size_t sz) { return CPools::GetPedPool()->New(); } void *CPed::operator new(size_t sz, int handle) { return CPools::GetPedPool()->New(handle); } void CPed::operator delete(void *p, size_t sz) { CPools::GetPedPool()->Delete((CPed*)p); } void CPed::operator delete(void *p, int handle) { CPools::GetPedPool()->Delete((CPed*)p); } float gfTommyFatness = 1.0f; CPed::CPed(uint32 pedType) : m_pedIK(this) { #ifdef USE_CUTSCENE_SHADOW_FOR_PED m_pRTShadow = nil; #endif m_vecAnimMoveDelta.x = 0.0f; m_vecAnimMoveDelta.y = 0.0f; m_fHealth = 100.0f; m_fArmour = 0.0f; m_nPedType = pedType; m_lastSoundStart = 0; m_soundStart = 0; m_lastQueuedSound = SOUND_NO_SOUND; m_queuedSound = SOUND_NO_SOUND; m_canTalk = true; m_type = ENTITY_TYPE_PED; bPedPhysics = true; bUseCollisionRecords = true; m_objective = OBJECTIVE_NONE; m_prevObjective = OBJECTIVE_NONE; #ifdef FIX_BUGS m_objectiveTimer = 0; #endif CharCreatedBy = RANDOM_CHAR; m_leader = nil; m_pedInObjective = nil; m_attractorHeading = 0.0f; m_carInObjective = nil; m_attractorHeading = 0.0f; bInVehicle = false; m_pMyVehicle = nil; m_pVehicleAnim = nil; m_vecOffsetSeek.x = 0.0f; m_vecOffsetSeek.y = 0.0f; m_vecOffsetSeek.z = 0.0f; m_attractor = nil; m_positionInQueue = -1; m_pedFormation = FORMATION_UNDEFINED; m_collidingThingTimer = 0; m_nPedStateTimer = 0; m_actionX = 0.0f; m_actionY = 0.0f; m_phoneTalkTimer = 0; m_stateUnused = 0; m_leaveCarTimer = 0; m_getUpTimer = 0; m_attackTimer = 0; m_timerUnused = 0; m_lookTimer = 0; m_chatTimer = 0; m_shootTimer = 0; m_carJackTimer = 0; m_duckAndCoverTimer = 0; m_moved = CVector2D(0.0f, 0.0f); m_fRotationCur = 0.0f; m_headingRate = 15.0f; m_fRotationDest = 0.0f; m_vehDoor = CAR_DOOR_LF; m_walkAroundType = 0; m_pCurrentPhysSurface = nil; m_vecOffsetFromPhysSurface = CVector(0.0f, 0.0f, 0.0f); m_pSeekTarget = nil; m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); m_wepSkills = 0; m_distanceToCountSeekDone = 1.0f; m_acceptableHeadingOffset = 0.1f; m_followPathDestPos = CVector(0.f, 0.f, 0.f); m_followPathAbortDist = 0.0f; m_followPathMoveState = PEDMOVE_NONE; bRunningToPhone = false; m_phoneId = -1; m_lastAccident = 0; m_fleeFrom = nil; m_fleeFromPos = CVector2D(0.0f, 0.0f); m_fleeTimer = 0; m_threatEx = nil; m_vecSpotToGuard = CVector(0.0f, 0.0f, 0.0f); m_radiusToGuard = 0.0f; m_nWaitState = WAITSTATE_FALSE; m_nWaitTimer = 0; m_pCollidingEntity = nil; m_nPedState = PED_IDLE; m_nLastPedState = PED_NONE; m_nMoveState = PEDMOVE_STILL; #ifdef FIX_BUGS m_nPrevMoveState = PEDMOVE_NONE; #endif m_nStoredMoveState = PEDMOVE_NONE; m_pFire = nil; m_pPointGunAt = nil; m_pLookTarget = nil; m_fLookDirection = 0.0f; m_pCurSurface = nil; m_wanderRangeBounds = nil; for (int i = 0; i < ARRAY_SIZE(m_pathNodesToGo); i++) { m_pathNodesToGo[i] = nil; } m_nNumPathNodes = 0; m_nCurPathNodeId = 0; m_nPathDir = 0; m_pLastPathNode = nil; m_pNextPathNode = nil; m_followPathWalkAroundEnt = nil; m_followPathTargetEnt = nil; m_pathNodeTimer = 0; m_pCurPathNode = nil; m_threatFlags = 0; m_threatCheckTimer = 0; m_threatCheckInterval = CGeneral::GetRandomNumberInRange(250, 1000); m_routeLastPoint = -1; m_routeStartPoint = 0; m_routePointsPassed = 0; m_routeType = 0; m_fMass = 70.0f; m_fTurnMass = 100.0f; m_fAirResistance = 0.4f / m_fMass; m_fElasticity = 0.05f; m_ceaseAttackTimer = 0; m_bodyPartBleeding = -1; bIsStanding = false; bWasStanding = false; bIsAttacking = false; bIsPointingGunAt = false; bIsLooking = false; bKeepTryingToLook = false; bIsRestoringLook = false; bIsAimingGun = false; bIsRestoringGun = false; bCanPointGunAtTarget = false; bIsTalking = false; bIsInTheAir = false; bIsLanding = false; bIsRunning = false; bHitSomethingLastFrame = false; bVehEnterDoorIsBlocked = false; bCanPedEnterSeekedCar = false; bRespondsToThreats = true; bRenderPedInCar = true; bChangedSeat = false; bUpdateAnimHeading = false; bBodyPartJustCameOff = false; bIsShooting = false; bFindNewNodeAfterStateRestore = false; bGonnaInvestigateEvent = false; bPedIsBleeding = false; bStopAndShoot = false; bIsPedDieAnimPlaying = false; bUsePedNodeSeek = false; bObjectiveCompleted = false; bScriptObjectiveCompleted = false; bKindaStayInSamePlace = false; bBeingChasedByPolice = false; bNotAllowedToDuck = false; bCrouchWhenShooting = false; bIsDucking = false; bGetUpAnimStarted = false; bDoBloodyFootprints = false; bFleeAfterExitingCar = false; bWanderPathAfterExitingCar = false; bIsLeader = false; bDontDragMeOutCar = false; bWillBeQuickJacked = false; bCancelEnteringCar = false; bObstacleShowedUpDuringKillObjective = false; bDuckAndCover = false; bStillOnValidPoly = false; bAllowMedicsToReviveMe = true; bResetWalkAnims = false; bStartWanderPathOnFoot = false; bOnBoat = false; bBusJacked = false; bGonnaKillTheCarJacker = false; bFadeOut = false; bKnockedUpIntoAir = false; bHitSteepSlope = false; bCullExtraFarAway = false; bClearObjective = false; bTryingToReachDryLand = false; bCollidedWithMyVehicle = false; bRichFromMugging = false; bChrisCriminal = false; bShakeFist = false; bNoCriticalHits = false; bVehExitWillBeInstant = false; bHasAlreadyBeenRecorded = false; bFallenDown = false; bDontAcceptIKLookAts = false; bReachedAttractorHeadingTarget = false; bTurnedAroundOnAttractor = false; #ifdef KANGAROO_CHEAT m_ped_flagI80 = false; #endif bHasAlreadyUsedAttractor = false; bHasAlreadyStoleACar = false; bCarPassenger = false; bFleeWhenStanding = false; bGotUpOfMyOwnAccord = false; bMiamiViceCop = false; bMoneyHasBeenGivenByScript = false; bHasBeenPhotographed = false; bIsDrowning = false; bDrownsInWater = true; bWaitForLeaderToComeCloser = false; bHeldHostageInCar = false; bIsPlayerFriend = true; bHeadStuckInCollision = false; bDeadPedInFrontOfCar = false; m_gangFlags = 0xFF; bStayInCarOnJack = false; bDontFight = false; bDoomAim = true; bCanBeShotInVehicle = true; bPushedAlongByCar = false; bRemoveMeWhenIGotIntoCar = false; bIgnoreThreatsBehindObjects = false; bNeverEverTargetThisPed = false; bCrouchWhenScared = false; bKnockedOffBike = false; b158_8 = false; bCollectBusFare = false; bBoughtIceCream = false; bDonePositionOutOfCollision = false; bCanAttackPlayerWithCops = false; if (CGeneral::GetRandomNumber() & 3) bHasACamera = false; else bHasACamera = true; if (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) <= 0.95f) bCanGiveUpSunbathing = false; else bCanGiveUpSunbathing = true; m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, this); DMAudio.SetEntityStatus(m_audioEntityId, true); m_fearFlags = CPedType::GetThreats(m_nPedType); m_threatEntity = nil; m_eventOrThreat = CVector2D(0.0f, 0.0f); m_pEventEntity = nil; m_fAngleToEvent = 0.0f; m_numNearPeds = 0; for (int i = 0; i < ARRAY_SIZE(m_nearPeds); i++) { m_nearPeds[i] = nil; } m_maxWeaponTypeAllowed = WEAPONTYPE_UNARMED; m_currentWeapon = WEAPONTYPE_UNARMED; m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; m_delayedWeapon = WEAPONTYPE_UNIDENTIFIED; for(int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { CWeapon &weapon = GetWeapon(i); weapon.m_eWeaponType = WEAPONTYPE_UNARMED; weapon.m_eWeaponState = WEAPONSTATE_READY; weapon.m_nAmmoInClip = 0; weapon.m_nAmmoTotal = 0; weapon.m_nTimer = 0; } m_curFightMove = m_lastFightMove = FIGHTMOVE_IDLE; GiveWeapon(WEAPONTYPE_UNARMED, 0, true); m_wepAccuracy = 60; m_lastWepDam = -1; m_lastDamEntity = nil; m_attachedTo = nil; m_attachWepAmmo = 0; m_collPoly.valid = false; m_fCollisionSpeed = 0.0f; m_wepModelID = -1; uint16 random = CGeneral::GetRandomNumber(); m_nPedMoney = random % 25; if (m_nPedMoney == 23) m_nPedMoney = 400; m_bleedCounter = 0; m_nExtendedRangeTimer = 0; m_vehicleInAccident = nil; m_attractor = nil; m_positionInQueue = -1; m_pWeaponModel = nil; m_delayedSoundID = -1; m_delayedSoundTimer = 0; CPopulation::UpdatePedCount((ePedType)m_nPedType, false); m_lastComment = UINT32_MAX; } CPed::~CPed(void) { #ifdef USE_CUTSCENE_SHADOW_FOR_PED if ( m_pRTShadow ) delete m_pRTShadow; #endif CWorld::Remove(this); if (m_attractor) GetPedAttractorManager()->DeRegisterPed(this, m_attractor); CRadar::ClearBlipForEntity(BLIP_CHAR, CPools::GetPedPool()->GetIndex(this)); if (InVehicle()){ uint8 door_flag = GetCarDoorFlag(m_vehDoor); if (m_pMyVehicle->pDriver == this) m_pMyVehicle->pDriver = nil; else { // FIX: Passenger counter now being decreased after removing ourself from vehicle. m_pMyVehicle->RemovePassenger(this); } if (m_nPedState == PED_EXIT_CAR || m_nPedState == PED_DRAG_FROM_CAR) m_pMyVehicle->m_nGettingOutFlags &= ~door_flag; bInVehicle = false; m_pMyVehicle = nil; } else if (EnteringCar()) { QuitEnteringCar(); } if (m_pFire) m_pFire->Extinguish(); ClearWeapons(); if (bCarPassenger) CPopulation::ms_nTotalCarPassengerPeds--; if (bMiamiViceCop) CPopulation::NumMiamiViceCops--; CPopulation::UpdatePedCount((ePedType)m_nPedType, true); DMAudio.DestroyEntity(m_audioEntityId); } void CPed::Initialise(void) { debug("Initialising CPed...\n"); CPedType::Initialise(); LoadFightData(); SetAnimOffsetForEnterOrExitVehicle(); debug("CPed ready\n"); } void CPed::SetModelIndex(uint32 mi) { CEntity::SetModelIndex(mi); RpAnimBlendClumpInit(GetClump()); RpAnimBlendClumpFillFrameArray(GetClump(), m_pFrames); CPedModelInfo *modelInfo = (CPedModelInfo *)CModelInfo::GetModelInfo(GetModelIndex()); SetPedStats(modelInfo->m_pedStatType); m_headingRate = m_pedStats->m_headingChangeRate; m_animGroup = (AssocGroupId) modelInfo->m_animGroup; CAnimManager::AddAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE); if (!CanUseTorsoWhenLooking()) m_pedIK.m_flags |= CPedIK::LOOKAROUND_HEAD_ONLY; (*RPANIMBLENDCLUMPDATA(m_rwObject))->velocity2d = &m_vecAnimMoveDelta; if(modelInfo->GetHitColModel() == nil) modelInfo->CreateHitColModelSkinned(GetClump()); UpdateRpHAnim(); #ifdef USE_CUTSCENE_SHADOW_FOR_PED if (!m_pRTShadow) { m_pRTShadow = new CCutsceneShadow; m_pRTShadow->Create(m_rwObject, 10, 1, 1, 1); //m_pRTShadow->Create(m_rwObject, 8, 0, 0, 0); } #endif } void CPed::SetPedStats(ePedStats pedStat) { m_pedStats = CPedStats::ms_apPedStats[pedStat]; } void CPed::DeleteRwObject() { CEntity::DeleteRwObject(); } void CPed::BuildPedLists(void) { if (((CTimer::GetFrameCounter() + m_randomSeed) % 16) == 0) { CVector centre = CEntity::GetBoundCentre(); int deadsRegistered = 0; CRect rect(centre.x - 20.f * nThreatReactionRangeMultiplier, centre.y - 20.f * nThreatReactionRangeMultiplier, centre.x + 20.f * nThreatReactionRangeMultiplier, centre.y + 20.f * nThreatReactionRangeMultiplier); int xstart = CWorld::GetSectorIndexX(rect.left); int ystart = CWorld::GetSectorIndexY(rect.top); int xend = CWorld::GetSectorIndexX(rect.right); int yend = CWorld::GetSectorIndexY(rect.bottom); gnNumTempPedList = 0; for(int y = ystart; y <= yend; y++) { for(int x = xstart; x <= xend; x++) { for (CPtrNode *pedPtrNode = CWorld::GetSector(x,y)->m_lists[ENTITYLIST_PEDS].first; pedPtrNode; pedPtrNode = pedPtrNode->next) { CPed *ped = (CPed*)pedPtrNode->item; if (ped != this && (!ped->bInVehicle || (ped->m_pMyVehicle && ped->m_pMyVehicle->IsBike()))) { if (nThreatReactionRangeMultiplier * 30.0f > (ped->GetPosition() - GetPosition()).Magnitude2D()) { if (ped->m_nPedState == PED_DEAD) { if (deadsRegistered > 3) continue; deadsRegistered++; } gapTempPedList[gnNumTempPedList] = ped; gnNumTempPedList++; assert(gnNumTempPedList < ARRAY_SIZE(gapTempPedList)); } } } } } gapTempPedList[gnNumTempPedList] = nil; SortPeds(gapTempPedList, 0, gnNumTempPedList - 1); for (m_numNearPeds = 0; m_numNearPeds < ARRAY_SIZE(m_nearPeds); m_numNearPeds++) { CPed *ped = gapTempPedList[m_numNearPeds]; if (!ped) break; m_nearPeds[m_numNearPeds] = ped; } for (int pedToClear = m_numNearPeds; pedToClear < ARRAY_SIZE(m_nearPeds); pedToClear++) m_nearPeds[pedToClear] = nil; } for(int i = 0; i < ARRAY_SIZE(m_nearPeds); ) { bool removePed = false; if (m_nearPeds[i]) { if (m_nearPeds[i]->IsPointerValid()) { float distSqr = (GetPosition() - m_nearPeds[i]->GetPosition()).MagnitudeSqr2D(); if (distSqr > sq(nThreatReactionRangeMultiplier * 30.f)) { removePed = true; } } else { removePed = true; } } if (removePed) { // If we arrive here, the ped we're checking isn't "near", so we should remove it. for (int j = i; j < ARRAY_SIZE(m_nearPeds) - 1; j++) { m_nearPeds[j] = m_nearPeds[j + 1]; m_nearPeds[j + 1] = nil; } // Above loop won't work on last slot, so we need to empty it. m_nearPeds[ARRAY_SIZE(m_nearPeds) - 1] = nil; m_numNearPeds--; } else i++; } } bool CPed::OurPedCanSeeThisOne(CEntity *target, bool shootablesDoBlock) { CColPoint colpoint; CEntity *ent; CVector2D dist = CVector2D(target->GetPosition()) - CVector2D(GetPosition()); // Check if target is behind ped if (DotProduct2D(dist, CVector2D(GetForward())) < 0.0f) return false; // Check if target is too far away if (dist.Magnitude() >= 40.0f) return false; // Check line of sight from head return !CWorld::ProcessLineOfSight(GetPosition() + CVector(0.f, 0.f, 1.f), target->GetPosition() + CVector(0.f, 0.f, 1.f), colpoint, ent, true, false, false, shootablesDoBlock, false, false, false, shootablesDoBlock); } // Some kind of binary sort void CPed::SortPeds(CPed **list, int min, int max) { if (min >= max) return; CVector leftDiff, rightDiff; CVector middleDiff = GetPosition() - list[(max + min) / 2]->GetPosition(); float middleDist = middleDiff.Magnitude(); int left = max; int right = min; while(right <= left){ float rightDist, leftDist; do { rightDiff = GetPosition() - list[right]->GetPosition(); rightDist = rightDiff.Magnitude(); } while (middleDist > rightDist && ++right); do { leftDiff = GetPosition() - list[left]->GetPosition(); leftDist = leftDiff.Magnitude(); } while (middleDist < leftDist && left--); if (right <= left) { CPed *ped = list[right]; list[right] = list[left]; list[left] = ped; right++; left--; } } SortPeds(list, min, left); SortPeds(list, right, max); } void CPed::SetMoveState(eMoveState state) { m_nMoveState = state; } void CPed::SetMoveAnim(void) { if (m_nStoredMoveState == m_nMoveState || !IsPedInControl() || m_attachedTo) return; if (m_nMoveState == PEDMOVE_NONE) { m_nStoredMoveState = PEDMOVE_NONE; return; } AssocGroupId animGroupToUse; if (m_leader && m_leader->IsPlayer()) animGroupToUse = ASSOCGRP_PLAYER; else animGroupToUse = m_animGroup; CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_BLOCK); if (!animAssoc) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); if (animAssoc && m_nPedState == PED_FIGHT) return; if (animAssoc) { CAnimBlendAssociation *idleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); if (!idleAssoc || idleAssoc->blendDelta <= 0.0f) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_IDLE, 8.0f); } } } if (!animAssoc) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); if (animAssoc) if (m_nWaitState == WAITSTATE_STUCK || m_nWaitState == WAITSTATE_FINISH_FLEE) return; if (animAssoc) { CAnimBlendAssociation *idleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); if (!idleAssoc || idleAssoc->blendDelta <= 0.0f) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_IDLE, 4.0f); } } } if (!animAssoc) { m_nStoredMoveState = m_nMoveState; if (m_nMoveState == PEDMOVE_WALK || m_nMoveState == PEDMOVE_RUN || m_nMoveState == PEDMOVE_SPRINT) { for (CAnimBlendAssociation *assoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_PARTIAL); assoc; assoc = RpAnimBlendGetNextAssociation(assoc, ASSOC_PARTIAL)) { if (!(assoc->flags & ASSOC_FADEOUTWHENDONE)) { assoc->blendDelta = -2.0f; assoc->flags |= ASSOC_DELETEFADEDOUT; } } ClearAimFlag(); ClearLookFlag(); } switch (m_nMoveState) { case PEDMOVE_STILL: animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_IDLE, 4.0f); break; case PEDMOVE_WALK: animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_WALK, 1.0f); break; case PEDMOVE_RUN: if (m_nPedState == PED_FLEE_ENTITY) { animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_RUN, 3.0f); } else { animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_RUN, 1.0f); } break; case PEDMOVE_SPRINT: animAssoc = CAnimManager::BlendAnimation(GetClump(), animGroupToUse, ANIM_STD_RUNFAST, 1.0f); break; default: break; } if (animAssoc) { if (m_leader) { CAnimBlendAssociation *walkAssoc = RpAnimBlendClumpGetAssociation(m_leader->GetClump(), ANIM_STD_WALK); if (!walkAssoc) walkAssoc = RpAnimBlendClumpGetAssociation(m_leader->GetClump(), ANIM_STD_RUN); if (!walkAssoc) walkAssoc = RpAnimBlendClumpGetAssociation(m_leader->GetClump(), ANIM_STD_RUNFAST); if (walkAssoc) { animAssoc->speed = walkAssoc->speed; } else { if (CharCreatedBy == MISSION_CHAR) animAssoc->speed = 1.0f; else animAssoc->speed = 1.2f - m_randomSeed * 0.4f / MYRAND_MAX; } } else { if (CharCreatedBy == MISSION_CHAR) animAssoc->speed = 1.0f; else animAssoc->speed = 1.2f - m_randomSeed * 0.4f / MYRAND_MAX; } } } } void CPed::StopNonPartialAnims(void) { CAnimBlendAssociation *assoc; for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { if (!assoc->IsPartial()) assoc->flags &= ~ASSOC_RUNNING; } } void CPed::RestartNonPartialAnims(void) { CAnimBlendAssociation *assoc; for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { if (!assoc->IsPartial()) assoc->SetRun(); } } void CPed::SetStoredState(void) { if (m_nLastPedState != PED_NONE || !CanPedReturnToState()) return; if (m_nPedState == PED_WANDER_PATH) { bFindNewNodeAfterStateRestore = true; if (m_nMoveState == PEDMOVE_NONE || m_nMoveState == PEDMOVE_STILL) m_nMoveState = PEDMOVE_WALK; } if (m_nPedState != PED_IDLE) { m_nLastPedState = m_nPedState; if (m_nMoveState >= m_nPrevMoveState) m_nPrevMoveState = m_nMoveState; } } void CPed::RestorePreviousState(void) { if (!CanSetPedState() || m_nPedState == PED_FALL) return; if (m_nPedState == PED_GETUP && !bGetUpAnimStarted) return; if (InVehicle()) { SetPedState(PED_DRIVING); m_nLastPedState = PED_NONE; } else { if (m_nLastPedState == PED_NONE) { if (!IsPlayer() && CharCreatedBy != MISSION_CHAR && m_objective == OBJECTIVE_NONE) { if (SetWanderPath(CGeneral::GetRandomNumber() & 7) != 0) return; } SetIdle(); return; } switch (m_nLastPedState) { case PED_IDLE: SetIdle(); break; case PED_WANDER_PATH: SetPedState(PED_WANDER_PATH); bIsRunning = false; if (bFindNewNodeAfterStateRestore) { if (m_pNextPathNode) { CVector nextNode = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); CVector diff = nextNode - GetPosition(); if (diff.MagnitudeSqr() < sq(7.0f)) { SetMoveState(PEDMOVE_WALK); break; } } } SetWanderPath(m_nPedState == PED_FOLLOW_PATH ? m_nPathDir : CGeneral::GetRandomNumber() & 7); break; default: SetPedState(m_nLastPedState); SetMoveState((eMoveState) m_nPrevMoveState); break; } m_nLastPedState = PED_NONE; } } uint32 CPed::ScanForThreats(void) { int fearFlags = m_fearFlags; CVector ourPos = GetPosition(); float closestPedDist = 60.0f; CVector2D explosionPos = GetPosition(); if (fearFlags & PED_FLAG_EXPLOSION && CheckForExplosions(explosionPos)) { m_eventOrThreat = explosionPos; return PED_FLAG_EXPLOSION; } if (fearFlags & PED_FLAG_GUN) { CPed *shooter = CheckForGunShots(); if (shooter && (m_nPedType != shooter->m_nPedType || m_nPedType == PEDTYPE_CIVMALE || m_nPedType == PEDTYPE_CIVFEMALE)) { if (!IsGangMember()) { m_threatEntity = shooter; m_threatEntity->RegisterReference((CEntity**)&m_threatEntity); return PED_FLAG_GUN; } if (CPedType::GetFlag(shooter->m_nPedType) & fearFlags || m_nPedType == PEDTYPE_GANG5) { if (m_threatEntity) m_threatEntity->CleanUpOldReference(&m_threatEntity); m_threatEntity = shooter; m_threatEntity->RegisterReference((CEntity**)&m_threatEntity); return CPedType::GetFlag(shooter->m_nPedType); } } } CPed *deadPed; if (fearFlags & PED_FLAG_DEADPEDS && CharCreatedBy != MISSION_CHAR && (deadPed = CheckForDeadPeds()) != nil && (deadPed->GetPosition() - ourPos).MagnitudeSqr() < sq(20.0f) #ifdef FIX_BUGS && !deadPed->bIsInWater #endif ) { m_pEventEntity = deadPed; m_pEventEntity->RegisterReference((CEntity **) &m_pEventEntity); return PED_FLAG_DEADPEDS; } else { uint32 flagsOfNearPed = 0; CPed *pedToFearFrom = nil; bool weSawOurEnemy = false; bool weMaySeeOurEnemy = false; float closestEnemyDist = 60.0f; if ((CTimer::GetFrameCounter() + (uint8)m_randomSeed + 16) & 4) { for (int i = 0; i < m_numNearPeds; ++i) { if (CharCreatedBy == RANDOM_CHAR && m_nearPeds[i]->CharCreatedBy == MISSION_CHAR && !m_nearPeds[i]->IsPlayer()) { continue; } // BUG: Putting this here will result in returning the flags of farthest ped to us, since m_nearPeds is sorted by distance. // Fixed at the bottom of the function. flagsOfNearPed = CPedType::GetFlag(m_nearPeds[i]->m_nPedType); if (flagsOfNearPed & fearFlags) { if (m_nearPeds[i]->m_fHealth > 0.0f) { if (OurPedCanSeeThisOne(m_nearPeds[i], !!bIgnoreThreatsBehindObjects)) { if (m_nearPeds[i]->m_nPedState == PED_ATTACK) { if (m_nearPeds[i]->m_pedInObjective == this) { float enemyDistSqr = (m_nearPeds[i]->GetPosition() - ourPos).MagnitudeSqr2D(); if (sq(closestEnemyDist) > enemyDistSqr) { float enemyDist = Sqrt(enemyDistSqr); weSawOurEnemy = true; closestPedDist = enemyDist; closestEnemyDist = enemyDist; pedToFearFrom = m_nearPeds[i]; } } } else { float nearPedDistSqr = (m_nearPeds[i]->GetPosition() - ourPos).MagnitudeSqr2D(); if (sq(closestPedDist) > nearPedDistSqr && !weSawOurEnemy) { closestPedDist = Sqrt(nearPedDistSqr); pedToFearFrom = m_nearPeds[i]; } } } else if (!weSawOurEnemy) { CPed *nearPed = m_nearPeds[i]; if (nearPed->m_nPedState == PED_ATTACK) { CColPoint foundCol; CEntity *foundEnt; // We don't see him yet but he's behind a ped, vehicle or object if (!CWorld::ProcessLineOfSight(ourPos, nearPed->GetPosition(), foundCol, foundEnt, true, false, false, !!bIgnoreThreatsBehindObjects, false, false, false)) { if (nearPed->m_pedInObjective == this) { float enemyDistSqr = (m_nearPeds[i]->GetPosition() - ourPos).MagnitudeSqr2D(); if (sq(closestEnemyDist) > enemyDistSqr) { float enemyDist = Sqrt(enemyDistSqr); weMaySeeOurEnemy = true; closestPedDist = enemyDist; closestEnemyDist = enemyDist; pedToFearFrom = m_nearPeds[i]; } } else if (!nearPed->GetWeapon()->IsTypeMelee() && !weMaySeeOurEnemy) { float nearPedDistSqr = (m_nearPeds[i]->GetPosition() - ourPos).MagnitudeSqr2D(); if (sq(closestPedDist) > nearPedDistSqr) { weMaySeeOurEnemy = true; closestPedDist = Sqrt(nearPedDistSqr); pedToFearFrom = m_nearPeds[i]; } } } } } } } } } int16 lastVehicle; CEntity* vehicles[8]; CWorld::FindObjectsInRange(ourPos, 20.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); CVehicle* foundVeh = nil; for (int i = 0; i < lastVehicle; i++) { CVehicle* nearVeh = (CVehicle*)vehicles[i]; CPed *driver = nearVeh->pDriver; if (driver) { // BUG: Same bug as above. Fixed at the bottom of function. flagsOfNearPed = CPedType::GetFlag(driver->m_nPedType); if (flagsOfNearPed & fearFlags) { if (driver->m_fHealth > 0.0f && OurPedCanSeeThisOne(nearVeh->pDriver)) { // FIX: Taken from VC #ifdef FIX_BUGS float driverDistSqr = (driver->GetPosition() - ourPos).MagnitudeSqr2D(); #else float driverDistSqr = (CVector2D(ourPos) - explosionPos).MagnitudeSqr(); #endif if (sq(closestPedDist) > driverDistSqr) { closestPedDist = Sqrt(driverDistSqr); pedToFearFrom = nearVeh->pDriver; } } } } } m_threatEntity = pedToFearFrom; if (m_threatEntity) m_threatEntity->RegisterReference((CEntity **) &m_threatEntity); #ifdef FIX_BUGS if (pedToFearFrom) flagsOfNearPed = CPedType::GetFlag(((CPed*)m_threatEntity)->m_nPedType); else flagsOfNearPed = 0; #endif return flagsOfNearPed; } } void CPed::ScanForDelayedResponseThreats(void) { if (m_threatFlags) return; m_threatEntity = nil; m_pEventEntity = nil; m_threatFlags = ScanForThreats(); if (m_threatFlags) { if (m_threatEntity || m_pEventEntity) { m_threatCheckTimer = CTimer::GetTimeInMilliseconds() + m_threatCheckInterval; return; } m_threatFlags = 0; } m_threatCheckTimer = 0; } void CPed::CheckThreatValidity(void) { if (m_threatEntity && !IsEntityPointerValid(m_threatEntity)) { m_threatFlags = 0; m_threatEntity = nil; } if (m_pEventEntity && !IsEntityPointerValid(m_pEventEntity)) { m_threatFlags = 0; m_pEventEntity = nil; } if (!m_threatEntity && !m_pEventEntity) m_threatFlags = 0; } bool CPed::CanUseTorsoWhenLooking(void) { if (m_nPedState != PED_DRIVING && m_nPedState != PED_DRAG_FROM_CAR && !bIsDucking) { if (m_animGroup != ASSOCGRP_SEXYWOMAN && m_animGroup != ASSOCGRP_WOMAN) return true; } return false; } void CPed::SetLookFlag(float direction, bool keepTryingToLook, bool cancelPrevious) { if (m_lookTimer < CTimer::GetTimeInMilliseconds() || cancelPrevious) { bIsLooking = true; bIsRestoringLook = false; m_fLookDirection = direction; m_pLookTarget = nil; m_lookTimer = 0; bKeepTryingToLook = keepTryingToLook; if (CanUseTorsoWhenLooking()) { m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; } } } void CPed::SetLookFlag(CEntity *target, bool keepTryingToLook, bool cancelPrevious) { if (m_lookTimer < CTimer::GetTimeInMilliseconds() || cancelPrevious) { bIsLooking = true; bIsRestoringLook = false; m_pLookTarget = target; m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); m_fLookDirection = 999999.0f; m_lookTimer = 0; bKeepTryingToLook = keepTryingToLook; if (CanUseTorsoWhenLooking()) { m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; } } } void CPed::ClearLookFlag(void) { if (bIsLooking) { bIsLooking = false; bIsRestoringLook = true; bShakeFist = false; if (CanUseTorsoWhenLooking()) m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; if (IsPlayer()) m_lookTimer = CTimer::GetTimeInMilliseconds() + 2000; else m_lookTimer = CTimer::GetTimeInMilliseconds() + 4000; if (m_nPedState == PED_LOOK_HEADING || m_nPedState == PED_LOOK_ENTITY) { ClearLook(); } } } void CPed::MoveHeadToLook(void) { CVector lookPos; if (m_lookTimer && CTimer::GetTimeInMilliseconds() > m_lookTimer) { ClearLookFlag(); } if (bIsLooking || bIsRestoringLook) if (!CanUseTorsoWhenLooking()) m_pedIK.m_flags |= CPedIK::LOOKAROUND_HEAD_ONLY; if (m_pLookTarget) { if (m_pLookTarget->IsPed()) { ((CPed*)m_pLookTarget)->m_pedIK.GetComponentPosition(lookPos, PED_MID); } else { lookPos = m_pLookTarget->GetPosition(); } if (!m_pedIK.LookAtPosition(lookPos)) { if (!bKeepTryingToLook) { ClearLookFlag(); } return; } if (!bShakeFist || bIsAimingGun || bIsRestoringGun) return; if (m_nPedState == PED_ANSWER_MOBILE) return; if (m_lookTimer - CTimer::GetTimeInMilliseconds() >= 1000) return; bool handFreeToMove = false; AnimationId animToPlay = ANIM_STD_NUM; if (!GetWeapon()->IsType2Handed() && GetWeapon()->m_eWeaponType != WEAPONTYPE_ROCKETLAUNCHER) handFreeToMove = true; if (IsPlayer() && handFreeToMove) { if (m_pLookTarget->IsPed()) { #ifdef FIX_BUGS if (m_pedStats->m_temper > 49 || ((CPed*)m_pLookTarget)->m_nPedType == PEDTYPE_COP) #else if (m_pedStats->m_temper < 49 || ((CPed*)m_pLookTarget)->m_nPedType == PEDTYPE_COP) #endif animToPlay = ANIM_STD_PARTIAL_FUCKU; else if(m_pedStats->m_temper < 47) animToPlay = ANIM_STD_PARTIAL_PUNCH; } else { if (m_pedStats->m_temper > 49 || m_pLookTarget->GetModelIndex() == MI_POLICE) animToPlay = ANIM_STD_PARTIAL_FUCKU; } } else if (handFreeToMove && (CGeneral::GetRandomNumber() & 1)) { animToPlay = ANIM_STD_PARTIAL_FUCKU; } if (animToPlay != ANIM_STD_NUM) { CAnimBlendAssociation *newAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay, 4.0f); if (newAssoc) { newAssoc->flags |= ASSOC_FADEOUTWHENDONE; newAssoc->flags |= ASSOC_DELETEFADEDOUT; } } bShakeFist = false; } else if (m_fLookDirection == 999999.0f) { ClearLookFlag(); } else if (!m_pedIK.LookInDirection(m_fLookDirection, 0.0f)) { if (!bKeepTryingToLook) ClearLookFlag(); } } void CPed::RestoreHeadPosition(void) { if(!CanUseTorsoWhenLooking()) m_pedIK.m_flags |= CPedIK::LOOKAROUND_HEAD_ONLY; if (m_pedIK.RestoreLookAt()) { bIsRestoringLook = false; if(CanUseTorsoWhenLooking()) m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; } } void CPed::SetAimFlag(float angle) { bIsAimingGun = true; bIsRestoringGun = false; m_fLookDirection = angle; m_lookTimer = 0; m_pLookTarget = nil; m_pSeekTarget = nil; if (bIsDucking) m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; if (CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) m_pedIK.m_flags |= CPedIK::AIMS_WITH_ARM; else m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; } void CPed::SetAimFlag(CEntity *to) { bIsAimingGun = true; bIsRestoringGun = false; if (m_pLookTarget) m_pLookTarget->CleanUpOldReference(&m_pLookTarget); m_pLookTarget = to; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); if (m_pSeekTarget) m_pSeekTarget->CleanUpOldReference(&m_pSeekTarget); m_pSeekTarget = to; m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); m_lookTimer = 0; } void CPed::ClearAimFlag(void) { if (bIsAimingGun) { bIsAimingGun = false; bIsRestoringGun = true; m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; m_lookTimer = 0; } if (IsPlayer()) ((CPlayerPed*)this)->m_fFPSMoveHeading = 0.0f; } void CPed::AimGun(void) { CVector vector; if (IsPlayer() && bIsDucking) m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; if (m_pSeekTarget) { if (m_pSeekTarget->IsPed()) { ((CPed*)m_pSeekTarget)->m_pedIK.GetComponentPosition(vector, PED_MID); } else { vector = m_pSeekTarget->GetPosition(); } if (!IsPlayer()) Say(SOUND_PED_ATTACK); bCanPointGunAtTarget = m_pedIK.PointGunAtPosition(vector); if (m_pLookTarget != m_pSeekTarget) { SetLookFlag(m_pSeekTarget, true, true); } } else { if (IsPlayer()) { bCanPointGunAtTarget = m_pedIK.PointGunInDirection(m_fLookDirection, ((CPlayerPed*)this)->m_fFPSMoveHeading); } else { bCanPointGunAtTarget = m_pedIK.PointGunInDirection(m_fLookDirection, 0.0f); } } } void CPed::RestoreGunPosition(void) { if (bIsLooking) { m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; bIsRestoringGun = false; } else if (m_pedIK.RestoreGunPosn()) { bIsRestoringGun = false; } else { if (IsPlayer()) ((CPlayerPed*)this)->m_fFPSMoveHeading = 0.0f; } } bool CPed::CanWeRunAndFireWithWeapon(void) { return CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM); } void CPed::ScanForInterestingStuff(void) { if (!IsPedInControl()) return; if (m_objective != OBJECTIVE_NONE) return; if (CharCreatedBy == MISSION_CHAR) return; LookForSexyPeds(); LookForSexyCars(); if (LookForInterestingNodes()) return; if (m_nPedType == PEDTYPE_CRIMINAL && m_carJackTimer < CTimer::GetTimeInMilliseconds()) { // Find a car to steal or a ped to mug if we haven't already decided to steal a car if (CGeneral::GetRandomNumber() % 100 < 10) { int mostExpensiveVehAround = -1; int bestMonetaryValue = 0; CVector pos = GetPosition(); int16 lastVehicle; CEntity *vehicles[8]; CWorld::FindObjectsInRange(pos, 10.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for (int i = 0; i < lastVehicle; i++) { CVehicle* veh = (CVehicle*)vehicles[i]; if (veh->VehicleCreatedBy != MISSION_VEHICLE) { if (veh->m_vecMoveSpeed.Magnitude() <= 0.1f && veh->IsVehicleNormal() && veh->IsCar() && bestMonetaryValue < veh->pHandling->nMonetaryValue) { mostExpensiveVehAround = i; bestMonetaryValue = veh->pHandling->nMonetaryValue; } } } if (bestMonetaryValue > 2000 && mostExpensiveVehAround != -1 && vehicles[mostExpensiveVehAround]) { SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, vehicles[mostExpensiveVehAround]); m_carJackTimer = CTimer::GetTimeInMilliseconds() + 5000; return; } m_carJackTimer = CTimer::GetTimeInMilliseconds() + 5000; } else if (m_objective != OBJECTIVE_MUG_CHAR && !(CGeneral::GetRandomNumber() & 7)) { CPed *charToMug = nil; for (int i = 0; i < m_numNearPeds; ++i) { CPed *nearPed = m_nearPeds[i]; if ((nearPed->GetPosition() - GetPosition()).MagnitudeSqr() > sq(7.0f)) break; if ((nearPed->m_nPedType == PEDTYPE_CIVFEMALE || nearPed->m_nPedType == PEDTYPE_CIVMALE || nearPed->m_nPedType == PEDTYPE_CRIMINAL || nearPed->m_nPedType == PEDTYPE_UNUSED1 || nearPed->m_nPedType == PEDTYPE_PROSTITUTE) && nearPed->CharCreatedBy != MISSION_CHAR && nearPed->IsPedShootable() && nearPed->m_objective != OBJECTIVE_MUG_CHAR) { charToMug = nearPed; break; } } if (charToMug) SetObjective(OBJECTIVE_MUG_CHAR, charToMug); m_carJackTimer = CTimer::GetTimeInMilliseconds() + 5000; } } if (m_nPedState == PED_WANDER_PATH) { if (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < 0.5f) { if (CTimer::GetTimeInMilliseconds() > m_chatTimer) { for (int i = 0; i < m_numNearPeds; i ++) { if (m_nearPeds[i] && m_nearPeds[i]->m_nPedState == PED_WANDER_PATH) { if ((GetPosition() - m_nearPeds[i]->GetPosition()).Magnitude() < 1.8f && CanSeeEntity(m_nearPeds[i]) && m_nearPeds[i]->CanSeeEntity(this) && WillChat(m_nearPeds[i])) { int time = CGeneral::GetRandomNumber() % 4000 + 10000; SetChat(m_nearPeds[i], time); m_nearPeds[i]->SetChat(this, time); } } } } } else { m_chatTimer = CTimer::GetTimeInMilliseconds() + 200; } } } bool CPed::WillChat(CPed *stranger) { if (m_pNextPathNode && m_pLastPathNode) { if (m_pNextPathNode != m_pLastPathNode && ThePaths.TestCrossesRoad(m_pNextPathNode, m_pLastPathNode)) { return false; } } if (m_nSurfaceTouched == SURFACE_TARMAC) return false; if (stranger == this) return false; if (m_nPedType == stranger->m_nPedType) return true; if (m_nPedType == PEDTYPE_CRIMINAL) return false; if (stranger->m_nPedType == PEDTYPE_COP) return false; if (stranger->IsPlayer()) return false; if ((IsGangMember() || stranger->IsGangMember()) && m_nPedType != stranger->m_nPedType) return false; return true; } void CPed::CalculateNewVelocity(void) { if (IsPedInControl()) { float headAmount = DEGTORAD(m_headingRate) * CTimer::GetTimeStep(); m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); float limitedRotDest = CGeneral::LimitRadianAngle(m_fRotationDest); if (m_fRotationCur - PI > limitedRotDest) { limitedRotDest += 2 * PI; } else if(PI + m_fRotationCur < limitedRotDest) { limitedRotDest -= 2 * PI; } float neededTurn = limitedRotDest - m_fRotationCur; if (neededTurn <= headAmount) { if (neededTurn > (-headAmount)) m_fRotationCur += neededTurn; else m_fRotationCur -= headAmount; } else { m_fRotationCur += headAmount; } } CVector2D forward(Sin(m_fRotationCur), Cos(m_fRotationCur)); m_moved.x = CrossProduct2D(m_vecAnimMoveDelta, forward); // (m_vecAnimMoveDelta.x * Cos(m_fRotationCur)) + -Sin(m_fRotationCur) * m_vecAnimMoveDelta.y; m_moved.y = DotProduct2D(m_vecAnimMoveDelta, forward); // m_vecAnimMoveDelta.y* Cos(m_fRotationCur) + (m_vecAnimMoveDelta.x * Sin(m_fRotationCur)); if (CTimer::GetTimeStep() >= 0.01f) { m_moved = m_moved * (1 / CTimer::GetTimeStep()); } else { m_moved = m_moved * (1 / 100.0f); } if ((!TheCamera.Cams[TheCamera.ActiveCam].GetWeaponFirstPersonOn() && !TheCamera.Cams[0].Using3rdPersonMouseCam()) || FindPlayerPed() != this || !CanStrafeOrMouseControl()) { if (FindPlayerPed() == this) FindPlayerPed()->m_fWalkAngle = 0.0f; return; } float walkAngle = WorkOutHeadingForMovingFirstPerson(m_fRotationCur); float pedSpeed = m_moved.Magnitude(); float localWalkAngle = CGeneral::LimitRadianAngle(walkAngle - m_fRotationCur); if (localWalkAngle < -0.5f * PI) { localWalkAngle += PI; } else if (localWalkAngle > 0.5f * PI) { localWalkAngle -= PI; } // Interestingly this part is responsible for diagonal walking. if (localWalkAngle > -DEGTORAD(50.0f) && localWalkAngle < DEGTORAD(50.0f)) { TheCamera.Cams[TheCamera.ActiveCam].m_fPlayerVelocity = pedSpeed; m_moved = CVector2D(-Sin(walkAngle), Cos(walkAngle)) * pedSpeed; } CAnimBlendAssociation *idleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); CAnimBlendAssociation *fightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); if(!fightAssoc) fightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); if(!fightAssoc) fightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); if ((!idleAssoc || idleAssoc->blendAmount < 0.5f) && !fightAssoc && !bIsDucking) { LimbOrientation newUpperLegs; newUpperLegs.yaw = localWalkAngle; if (newUpperLegs.yaw < -DEGTORAD(100.0f)) { newUpperLegs.yaw += PI; } else if (newUpperLegs.yaw > DEGTORAD(100.0f)) { newUpperLegs.yaw -= PI; } if (newUpperLegs.yaw > -DEGTORAD(50.0f) && newUpperLegs.yaw < DEGTORAD(50.0f)) { newUpperLegs.pitch = 0.1f; RwV3d Xaxis = { 1.0f, 0.0f, 0.0f }; RwV3d Zaxis = { 0.0f, 0.0f, 1.0f }; RtQuatRotate(&m_pFrames[PED_UPPERLEGL]->hanimFrame->q, &Zaxis, RADTODEG(newUpperLegs.pitch), rwCOMBINEPOSTCONCAT); RtQuatRotate(&m_pFrames[PED_UPPERLEGL]->hanimFrame->q, &Xaxis, RADTODEG(newUpperLegs.yaw), rwCOMBINEPOSTCONCAT); RtQuatRotate(&m_pFrames[PED_UPPERLEGR]->hanimFrame->q, &Zaxis, RADTODEG(newUpperLegs.pitch), rwCOMBINEPOSTCONCAT); RtQuatRotate(&m_pFrames[PED_UPPERLEGR]->hanimFrame->q, &Xaxis, RADTODEG(newUpperLegs.yaw), rwCOMBINEPOSTCONCAT); bDontAcceptIKLookAts = true; } } } float CPed::WorkOutHeadingForMovingFirstPerson(float offset) { if (!IsPlayer()) return 0.0f; CPad *pad0 = CPad::GetPad(0); float leftRight = pad0->GetPedWalkLeftRight(); float upDown = pad0->GetPedWalkUpDown(); float &angle = ((CPlayerPed*)this)->m_fWalkAngle; if (upDown != 0.0f) { angle = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown); } else { if (leftRight < 0.0f) angle = HALFPI; else if (leftRight > 0.0f) angle = -HALFPI; } return CGeneral::LimitRadianAngle(offset + angle); } void CPed::UpdatePosition(void) { if (CReplay::IsPlayingBack() || !bIsStanding || m_attachedTo) return; CVector2D velocityChange; SetHeading(m_fRotationCur); if (m_pCurrentPhysSurface) { CVector2D velocityOfSurface; if (!IsPlayer() && m_pCurrentPhysSurface->IsVehicle() && ((CVehicle*)m_pCurrentPhysSurface)->IsBoat()) { // It seems R* didn't like m_vecOffsetFromPhysSurface for boats CVector offsetToSurface = GetPosition() - m_pCurrentPhysSurface->GetPosition(); offsetToSurface.z -= FEET_OFFSET; CVector surfaceMoveVelocity = m_pCurrentPhysSurface->m_vecMoveSpeed; CVector surfaceTurnVelocity = CrossProduct(m_pCurrentPhysSurface->m_vecTurnSpeed, offsetToSurface); // Also we use that weird formula instead of friction if it's boat float slideMult = -m_pCurrentPhysSurface->m_vecTurnSpeed.MagnitudeSqr(); velocityOfSurface = slideMult * offsetToSurface * CTimer::GetTimeStep() + (surfaceTurnVelocity + surfaceMoveVelocity); m_vecMoveSpeed.z = slideMult * offsetToSurface.z * CTimer::GetTimeStep() + (surfaceTurnVelocity.z + surfaceMoveVelocity.z); } else { velocityOfSurface = m_pCurrentPhysSurface->GetSpeed(m_vecOffsetFromPhysSurface); } // Reminder: m_moved is displacement from walking/running. velocityChange = m_moved + velocityOfSurface - m_vecMoveSpeed; m_fRotationCur += m_pCurrentPhysSurface->m_vecTurnSpeed.z * CTimer::GetTimeStep(); m_fRotationDest += m_pCurrentPhysSurface->m_vecTurnSpeed.z * CTimer::GetTimeStep(); } else if (m_nSurfaceTouched == SURFACE_STEEP_CLIFF && (m_vecDamageNormal.x != 0.0f || m_vecDamageNormal.y != 0.0f)) { // Ped got damaged by steep slope m_vecMoveSpeed = CVector(0.0f, 0.0f, -0.001f); // some kind of CVector2D reactionForce = m_vecDamageNormal; reactionForce.Normalise(); velocityChange = 0.02f * reactionForce + m_moved; float reactionAndVelocityDotProd = DotProduct2D(reactionForce, velocityChange); // they're in same direction if (reactionAndVelocityDotProd < 0.0f) { velocityChange -= reactionAndVelocityDotProd * reactionForce; } } else { velocityChange = m_moved - m_vecMoveSpeed; } // Take time step into account if (m_pCurrentPhysSurface && (!m_pCurrentPhysSurface->bInfiniteMass || m_pCurrentPhysSurface->m_phy_flagA08)) { float speedChange = velocityChange.Magnitude(); float changeMult = speedChange; if (m_nPedState == PED_DIE && m_pCurrentPhysSurface->IsVehicle()) { changeMult = 0.002f * CTimer::GetTimeStep(); } else if (!(m_pCurrentPhysSurface->IsVehicle() && ((CVehicle*)m_pCurrentPhysSurface)->IsBoat())) { changeMult = 0.01f * CTimer::GetTimeStep(); } if (speedChange > changeMult) { velocityChange = velocityChange * (changeMult / speedChange); } } m_vecMoveSpeed.x += velocityChange.x; m_vecMoveSpeed.y += velocityChange.y; } void CPed::CalculateNewOrientation(void) { if (CReplay::IsPlayingBack() || !IsPedInControl()) return; SetHeading(m_fRotationCur); } void CPed::ClearAll(void) { if (!IsPedInControl() && m_nPedState != PED_DEAD) return; SetPedState(PED_NONE); SetMoveState(PEDMOVE_NONE); m_pSeekTarget = nil; m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); m_fleeFromPos = CVector2D(0.0f, 0.0f); m_fleeFrom = nil; m_fleeTimer = 0; m_threatEx = nil; bUsesCollision = true; ClearPointGunAt(); bIsPointingGunAt = false; bRenderPedInCar = true; bKnockedUpIntoAir = false; bKnockedOffBike = false; m_pCollidingEntity = nil; } void CPed::ProcessBuoyancy(void) { float buoyancyLevel = 1.1f; static uint32 nGenerateRaindrops = 0; static uint32 nGenerateWaterCircles = 0; CRGBA color; if (bInVehicle) return; CVector buoyancyPoint; CVector buoyancyImpulse; if (DyingOrDead()) buoyancyLevel = 1.8f; if (mod_Buoyancy.ProcessBuoyancy(this, GRAVITY * m_fMass * buoyancyLevel, &buoyancyPoint, &buoyancyImpulse)) { bTouchingWater = true; CEntity *entity; CColPoint point; if (CWorld::ProcessVerticalLine(GetPosition(), GetPosition().z - 3.0f, point, entity, false, true, false, false, false, false, nil) && entity->IsVehicle() && ((CVehicle*)entity)->IsBoat() && !entity->bRenderScorched) { bIsInWater = false; return; } color.r = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed()) * 127.5f; color.g = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue()) * 127.5f; color.b = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen()) * 127.5f; color.a = CGeneral::GetRandomNumberInRange(48.0f, 96.0f); bIsInWater = true; ApplyMoveForce(buoyancyImpulse); if (!DyingOrDead()) { if (bTryingToReachDryLand) { if (buoyancyImpulse.z / m_fMass > GRAVITY * 0.4f * CTimer::GetTimeStep()) { bTryingToReachDryLand = false; CVector pos = GetPosition(); if (PlacePedOnDryLand()) { if (m_fHealth > 20.0f) InflictDamage(nil, WEAPONTYPE_DROWNING, 15.0f, PEDPIECE_TORSO, false); if (bIsInTheAir) { RpAnimBlendClumpSetBlendDeltas(GetClump(), ASSOC_PARTIAL, -1000.0f); bIsInTheAir = false; } pos.z = pos.z - 0.8f; CParticleObject::AddObject(POBJECT_PED_WATER_SPLASH, pos, CVector(0.0f, 0.0f, 0.0f), 0.0f, 50, color, true); m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); SetPedState(PED_IDLE); return; } } } } float speedMult = 0.0f; if (buoyancyImpulse.z / m_fMass > GRAVITY * CTimer::GetTimeStep() || mod_Buoyancy.m_waterlevel > GetPosition().z + 0.6f) { speedMult = pow(0.9f, CTimer::GetTimeStep()); m_vecMoveSpeed.x *= speedMult; m_vecMoveSpeed.y *= speedMult; m_vecMoveSpeed.z *= speedMult; bIsStanding = false; bIsDrowning = true; InflictDamage(nil, WEAPONTYPE_DROWNING, 3.0f * CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); } if (buoyancyImpulse.z / m_fMass > GRAVITY * 0.25f * CTimer::GetTimeStep()) { if (speedMult == 0.0f) { speedMult = pow(0.9f, CTimer::GetTimeStep()); } m_vecMoveSpeed.x *= speedMult; m_vecMoveSpeed.y *= speedMult; if (m_vecMoveSpeed.z >= -0.1f) { if (m_vecMoveSpeed.z < -0.04f) m_vecMoveSpeed.z = -0.02f; } else { m_vecMoveSpeed.z = -0.01f; DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLASH, 0.0f); CVector aBitForward = 2.2f * m_vecMoveSpeed + GetPosition(); float level = 0.0f; if (CWaterLevel::GetWaterLevel(aBitForward, &level, false)) aBitForward.z = level; CParticleObject::AddObject(POBJECT_PED_WATER_SPLASH, aBitForward, CVector(0.0f, 0.0f, 0.1f), 0.0f, 200, color, true); nGenerateRaindrops = CTimer::GetTimeInMilliseconds() + 80; nGenerateWaterCircles = CTimer::GetTimeInMilliseconds() + 100; } } if (nGenerateWaterCircles && CTimer::GetTimeInMilliseconds() >= nGenerateWaterCircles) { CVector pos = GetPosition(); float level = 0.0f; if (CWaterLevel::GetWaterLevel(pos, &level, false)) pos.z = level; if (pos.z != 0.0f) { nGenerateWaterCircles = 0; for(int i = 0; i < 4; i++) { pos.x += CGeneral::GetRandomNumberInRange(-0.75f, 0.75f); pos.y += CGeneral::GetRandomNumberInRange(-0.75f, 0.75f); CParticle::AddParticle(PARTICLE_RAIN_SPLASH_BIGGROW, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, color, 0, 0, 0, 0); } } } if (nGenerateRaindrops && CTimer::GetTimeInMilliseconds() >= nGenerateRaindrops) { CVector pos = GetPosition(); float level = 0.0f; if (CWaterLevel::GetWaterLevel(pos, &level, false)) pos.z = level; if (pos.z >= 0.0f) { pos.z += 0.25f; nGenerateRaindrops = 0; CParticleObject::AddObject(POBJECT_SPLASHES_AROUND, pos, CVector(0.0f, 0.0f, 0.0f), 4.5f, 1500, CRGBA(0,0,0,0), true); } } } else bTouchingWater = false; } void CPed::ProcessControl(void) { CColPoint foundCol; CEntity *foundEnt = nil; if (CTimer::GetFrameCounter() + m_randomSeed % 32 == 0) PruneReferences(); int alpha = CVisibilityPlugins::GetClumpAlpha(GetClump()); if (!bFadeOut) { if (alpha < 255) { alpha += 16; if (alpha > 255) alpha = 255; } } else { alpha -= 8; if (alpha < 0) alpha = 0; } CVisibilityPlugins::SetClumpAlpha(GetClump(), alpha); bIsShooting = false; bDonePositionOutOfCollision = false; BuildPedLists(); bIsInWater = false; bIsDrowning = false; ProcessBuoyancy(); if (m_nPedState != PED_ARRESTED) { if (m_nPedState == PED_DEAD) { DeadPedMakesTyresBloody(); if (CGame::nastyGame && !bIsInWater) { uint32 remainingBloodyFpTime = CTimer::GetTimeInMilliseconds() - m_bloodyFootprintCountOrDeathTime; float timeDependentDist; if (remainingBloodyFpTime >= 2000) { if (remainingBloodyFpTime <= 7000) timeDependentDist = (remainingBloodyFpTime - 2000) / 5000.0f * 0.75f; else timeDependentDist = 0.75f; } else { timeDependentDist = 0.0f; } for (int i = 0; i < m_numNearPeds; ++i) { CPed *nearPed = m_nearPeds[i]; if (!nearPed->DyingOrDead()) { CVector dist = nearPed->GetPosition() - GetPosition(); if (dist.MagnitudeSqr() < sq(timeDependentDist)) { nearPed->m_bloodyFootprintCountOrDeathTime = 200; nearPed->bDoBloodyFootprints = true; if (nearPed->IsPlayer()) { if (!nearPed->bIsLooking && nearPed->m_nPedState != PED_ATTACK) { int16 camMode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if (camMode != CCam::MODE_SNIPER && camMode != CCam::MODE_CAMERA && camMode != CCam::MODE_ROCKETLAUNCHER && camMode != CCam::MODE_M16_1STPERSON && camMode != CCam::MODE_1STPERSON && camMode != CCam::MODE_HELICANNON_1STPERSON && !TheCamera.Cams[TheCamera.ActiveCam].GetWeaponFirstPersonOn()) { nearPed->SetLookFlag(this, true); nearPed->SetLookTimer(500); } } } } } } if (remainingBloodyFpTime > 2000) { CVector bloodPos = GetPosition(); if (remainingBloodyFpTime - 2000 >= 5000) { if (!m_deadBleeding) { CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, 0.75f, 0.0f, 0.0f, -0.75f, 255, 255, 0, 0, 4.0f, 40000, 1.0f); m_deadBleeding = true; } } else { CShadows::StoreStaticShadow( (uintptr)this + 17, SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, (remainingBloodyFpTime - 2000) / 5000.0f * 0.75f, 0.0f, 0.0f, (remainingBloodyFpTime - 2000) / 5000.0f * -0.75f, 255, 255, 0, 0, 4.0f, 1.0f, 40.0f, false, 0.0f); } } } if (ServiceTalkingWhenDead()) ServiceTalking(); if (bIsInWater) { bIsStanding = false; bWasStanding = false; CPhysical::ProcessControl(); } return; } bWasStanding = false; if (bIsStanding) { if (!CWorld::bForceProcessControl) { if (m_pCurrentPhysSurface && m_pCurrentPhysSurface->bIsInSafePosition) { bWasPostponed = true; return; } } } if (!IsPedInControl() || m_nWaitState != WAITSTATE_FALSE || 0.01f * CTimer::GetTimeStep() <= m_fDistanceTravelled || (m_nStoredMoveState != PEDMOVE_WALK && m_nStoredMoveState != PEDMOVE_RUN && m_nStoredMoveState != PEDMOVE_SPRINT)) m_panicCounter = 0; else if (m_panicCounter < 50) ++m_panicCounter; if (m_fHealth <= 1.0f && m_nPedState <= PED_STATES_NO_AI && !bIsInTheAir && !bIsLanding) SetDie(); if (bIsStanding) bPushedAlongByCar = false; bCollidedWithMyVehicle = false; CEntity *collidingEnt = m_pDamageEntity; if (!bUsesCollision || ((!collidingEnt || m_fDamageImpulse <= 0.0f) && (!IsPlayer() || !bIsStuck)) || m_nPedState == PED_DIE) { bHitSomethingLastFrame = false; if (m_nPedStateTimer <= 500 && bIsInTheAir) { if (m_nPedStateTimer) m_nPedStateTimer--; } else if (m_nPedStateTimer < 1001) { m_nPedStateTimer = 0; } } else if (!GetPedAttractorManager()->IsInQueue(this, m_attractor)) { if (m_panicCounter == 50 && IsPedInControl()) { SetWaitState(WAITSTATE_STUCK, nil); // Leftover /* if (m_nPedType < PEDTYPE_COP) { } else { } */ } else if (collidingEnt) { switch (collidingEnt->GetType()) { case ENTITY_TYPE_BUILDING: case ENTITY_TYPE_OBJECT: { CBaseModelInfo *collidingModel = CModelInfo::GetModelInfo(collidingEnt->GetModelIndex()); CColModel *collidingCol = collidingModel->GetColModel(); if (collidingEnt->IsObject() && ((CObject*)collidingEnt)->m_nSpecialCollisionResponseCases != COLLRESPONSE_FENCEPART || collidingCol->boundingBox.max.x < 3.0f && collidingCol->boundingBox.max.y < 3.0f) { if (!IsPlayer()) { SetDirectionToWalkAroundObject(collidingEnt); break; } } if (IsPlayer()) { bHitSomethingLastFrame = true; break; } float angleToFaceWhenHit = CGeneral::GetRadianAngleBetweenPoints( GetPosition().x, GetPosition().y, m_vecDamageNormal.x + GetPosition().x, m_vecDamageNormal.y + GetPosition().y); float neededTurn = Abs(m_fRotationCur - angleToFaceWhenHit); if (neededTurn > PI) neededTurn = TWOPI - neededTurn; float oldDestRot = CGeneral::LimitRadianAngle(m_fRotationDest); if (m_nPedState == PED_FOLLOW_PATH) { if (DotProduct(m_vecDamageNormal, GetForward()) < -0.866f && CanPedJumpThis(collidingEnt, &m_vecDamageNormal)) { SetJump(); } break; } if (m_pedInObjective && (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT)) { if (m_pedInObjective->IsPlayer() && (neededTurn < DEGTORAD(20.0f) || m_panicCounter > 10)) { if (CanPedJumpThis(collidingEnt)) { SetJump(); } else if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT) { if (m_nPedType == PEDTYPE_COP && m_nWaitState != WAITSTATE_LOOK_ABOUT) ((CCopPed*)this)->field_624++; SetWaitState(WAITSTATE_LOOK_ABOUT, nil); } else { SetWaitState(WAITSTATE_PLAYANIM_TAXI, nil); m_headingRate = 0.0f; SetLookFlag(m_pedInObjective, true); SetLookTimer(3000); Say(SOUND_PED_TAXI_CALL); } } else { if (m_pLookTarget) m_pLookTarget->CleanUpOldReference(&m_pLookTarget); m_pLookTarget = m_pedInObjective; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); TurnBody(); } } else { if (m_nPedType != PEDTYPE_COP && neededTurn < DEGTORAD(15.0f) && m_nWaitState == WAITSTATE_FALSE) { if ((m_nStoredMoveState == PEDMOVE_RUN || m_nStoredMoveState == PEDMOVE_SPRINT) && m_vecDamageNormal.z < 0.3f) { CAnimBlendAssociation *runAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUN); if (!runAssoc) runAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNFAST); if (runAssoc && runAssoc->blendAmount > 0.9f && runAssoc->IsRunning()) { SetWaitState(WAITSTATE_HITWALL, nil); } } } if (m_nPedState == PED_FLEE_POS) { CVector2D fleePos = collidingEnt->GetPosition(); uint32 oldFleeTimer = m_fleeTimer; SetFlee(fleePos, 5000); if (oldFleeTimer != m_fleeTimer) m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 500; } else { if (m_nPedState == PED_FLEE_ENTITY && (neededTurn < DEGTORAD(25.0f) || m_panicCounter > 10)) { m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 2500; m_collidingEntityWhileFleeing = collidingEnt; m_collidingEntityWhileFleeing->RegisterReference((CEntity **) &m_collidingEntityWhileFleeing); if (m_nWaitState != WAITSTATE_HITWALL) SetWaitState(WAITSTATE_TURN180, nil); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 5000; Flee(); } else { if (neededTurn < DEGTORAD(60.0f)) { CVector posToHead = m_vecDamageNormal * 4.0f; posToHead.z = 0.0f; posToHead += GetPosition(); int closestNodeId = ThePaths.FindNodeClosestToCoors(posToHead, PATH_PED, 999999.9f, false, false); float angleToFace; if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS) { if (m_nPedState != PED_SEEK_POS && m_nPedState != PED_SEEK_CAR) { if (m_nPedState == PED_WANDER_PATH) { m_pNextPathNode = &ThePaths.m_pathNodes[closestNodeId]; CVector bestCoords = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); angleToFace = CGeneral::GetRadianAngleBetweenPoints( bestCoords.x, bestCoords.y, GetPosition().x, GetPosition().y); } else if (m_nPedState == PED_FOLLOW_PATH) { CVector bestCoords = m_pathNodesToGo[m_nCurPathNodeId]->GetPosition(); angleToFace = CGeneral::GetRadianAngleBetweenPoints( bestCoords.x, bestCoords.y, GetPosition().x, GetPosition().y); } else { if (ThePaths.m_pathNodes[closestNodeId].GetX() == 0.0f || ThePaths.m_pathNodes[closestNodeId].GetY() == 0.0f) { posToHead = (3.0f * m_vecDamageNormal) + GetPosition(); posToHead.x += (CGeneral::GetRandomNumber() % 512) / 250.0f - 1.0f; posToHead.y += (CGeneral::GetRandomNumber() % 512) / 250.0f - 1.0f; } else { posToHead.x = ThePaths.m_pathNodes[closestNodeId].GetX(); posToHead.y = ThePaths.m_pathNodes[closestNodeId].GetY(); posToHead.z = ThePaths.m_pathNodes[closestNodeId].GetZ(); } angleToFace = CGeneral::GetRadianAngleBetweenPoints( posToHead.x, posToHead.y, GetPosition().x, GetPosition().y); if (m_nPedState != PED_FOLLOW_PATH) m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 500; } } else { angleToFace = CGeneral::GetRadianAngleBetweenPoints( ThePaths.m_pathNodes[closestNodeId].GetX(), ThePaths.m_pathNodes[closestNodeId].GetY(), GetPosition().x, GetPosition().y); CVector2D distToNode = ThePaths.m_pathNodes[closestNodeId].GetPosition() - GetPosition(); CVector2D distToSeekPos = m_vecSeekPos - GetPosition(); if (DotProduct2D(distToNode, distToSeekPos) < 0.0f) { m_fRotationCur = m_fRotationDest; break; } } } else { float angleToFaceAwayDamage = CGeneral::GetRadianAngleBetweenPoints( m_vecDamageNormal.x, m_vecDamageNormal.y, 0.0f, 0.0f); if (angleToFaceAwayDamage < m_fRotationCur) angleToFaceAwayDamage += TWOPI; float neededTurn = angleToFaceAwayDamage - m_fRotationCur; if (neededTurn <= PI) { angleToFace = 0.5f * neededTurn + m_fRotationCur; m_fRotationCur += DEGTORAD(m_pedStats->m_headingChangeRate) * 2.0f; } else { angleToFace = m_fRotationCur - (TWOPI - neededTurn) * 0.5f; m_fRotationCur -= DEGTORAD(m_pedStats->m_headingChangeRate) * 2.0f; } m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 200; if (m_nPedType == PEDTYPE_COP) { if (m_pedInObjective) { float angleToLookCriminal = CGeneral::GetRadianAngleBetweenPoints( m_pedInObjective->GetPosition().x, m_pedInObjective->GetPosition().y, GetPosition().x, GetPosition().y); angleToLookCriminal = CGeneral::LimitRadianAngle(angleToLookCriminal); angleToFace = CGeneral::LimitRadianAngle(angleToFace); if (angleToLookCriminal < angleToFace) angleToLookCriminal += TWOPI; float neededTurnToCriminal = angleToLookCriminal - angleToFace; if (neededTurnToCriminal > DEGTORAD(150.0f) && neededTurnToCriminal < DEGTORAD(210.0f)) { ((CCopPed*)this)->m_bStopAndShootDisabledZone = true; } } } } m_fRotationDest = CGeneral::LimitRadianAngle(angleToFace); if (m_fRotationCur - PI > m_fRotationDest) { m_fRotationDest += TWOPI; } else if (PI + m_fRotationCur < m_fRotationDest) { m_fRotationDest -= TWOPI; } if (oldDestRot == m_fRotationDest && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 200; m_fRotationDest += HALFPI; } } } } } if (m_nPedState != PED_WANDER_PATH && m_nPedState != PED_FLEE_ENTITY) m_pNextPathNode = nil; bHitSomethingLastFrame = true; break; } case ENTITY_TYPE_VEHICLE: { CVehicle* collidingVeh = ((CVehicle*)collidingEnt); float collidingVehSpeedSqr = collidingVeh->m_vecMoveSpeed.MagnitudeSqr(); if (collidingVeh == m_pMyVehicle) bCollidedWithMyVehicle = true; float oldHealth = m_fHealth; bool playerSufferSound = false; if (collidingVehSpeedSqr <= 1.0f / 400.0f) { if (IsPedInControl() && (!IsPlayer() || m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA || m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || IsUseAttractorObjective(m_objective))) { if (collidingVeh != m_pCurrentPhysSurface || IsPlayer()) { if (!bVehEnterDoorIsBlocked) { if (collidingVeh->GetStatus() != STATUS_PLAYER || CharCreatedBy == MISSION_CHAR) { if (m_nPedState == PED_SEEK_CAR) { SetDirectionToWalkAroundObject(collidingVeh); CWorld::Players[CWorld::PlayerInFocus].m_nLastBumpPlayerCarTimer = m_nPedStateTimer; } else { SetDirectionToWalkAroundVehicle(collidingVeh); } } else { if (CTimer::GetTimeInMilliseconds() >= CWorld::Players[CWorld::PlayerInFocus].m_nLastBumpPlayerCarTimer || m_nPedStateTimer >= CTimer::GetTimeInMilliseconds()) { if (m_nPedState == PED_SEEK_CAR) { SetDirectionToWalkAroundObject(collidingVeh); CWorld::Players[CWorld::PlayerInFocus].m_nLastBumpPlayerCarTimer = m_nPedStateTimer; } else { SetDirectionToWalkAroundVehicle(collidingVeh); } } else if (m_fleeFrom != collidingVeh) { SetFlee(collidingVeh, 4000); bUsePedNodeSeek = false; SetMoveState(PEDMOVE_WALK); } } } } else { float angleLeftToCompleteTurn = Abs(m_fRotationCur - m_fRotationDest); if (angleLeftToCompleteTurn < 0.01f && CanPedJumpThis(collidingVeh)) { SetJump(); } } } else if (IsPlayer() && !bIsInTheAir) { if (IsPedInControl() && ((CPlayerPed*)this)->m_fMoveSpeed == 0.0f && !bIsLooking && CTimer::GetTimeInMilliseconds() > m_lookTimer && collidingVeh->pDriver) { ((CPlayerPed*)this)->AnnoyPlayerPed(false); SetLookFlag(collidingVeh, true); SetLookTimer(1300); eWeaponType weaponType = GetWeapon()->m_eWeaponType; uint32 weaponSlot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; if (weaponType == WEAPONTYPE_UNARMED || weaponSlot == 3 || weaponSlot == 5 || weaponSlot == 1) { bShakeFist = true; } } else { SetLookFlag(collidingVeh, true); SetLookTimer(500); } } } else { float adjustedImpulse = m_fDamageImpulse; if (IsPlayer()) { if (bIsStanding) { float forwardVecAndDamageDirDotProd = DotProduct(m_vecAnimMoveDelta.y * GetForward(), m_vecDamageNormal); if (forwardVecAndDamageDirDotProd < 0.0f) { adjustedImpulse = forwardVecAndDamageDirDotProd * m_fMass + m_fDamageImpulse; if (adjustedImpulse < 0.0f) adjustedImpulse = 0.0f; } } } if (m_fMass / 20.0f < adjustedImpulse) DMAudio.PlayOneShot(collidingVeh->m_audioEntityId, SOUND_CAR_PED_COLLISION, adjustedImpulse); if (IsPlayer()) { if (adjustedImpulse > 20.0f) adjustedImpulse = 20.0f; if (adjustedImpulse > 5.0f) { if (adjustedImpulse <= 13.0f) playerSufferSound = true; else Say(SOUND_PED_DAMAGE); } CColModel* collidingCol = CModelInfo::GetModelInfo(collidingVeh->m_modelIndex)->GetColModel(); CVector colMinVec = collidingCol->boundingBox.min; CVector colMaxVec = collidingCol->boundingBox.max; CVector vehColCenterDist = collidingVeh->GetMatrix() * ((colMinVec + colMaxVec) * 0.5f) - GetPosition(); // TLVC = To look vehicle center float angleToVehFront = collidingVeh->GetForward().Heading(); float angleDiffFromLookingFrontTLVC = angleToVehFront - vehColCenterDist.Heading(); angleDiffFromLookingFrontTLVC = CGeneral::LimitRadianAngle(angleDiffFromLookingFrontTLVC); // I don't know why do we use that float vehTopRightHeading = Atan2(colMaxVec.x - colMinVec.x, colMaxVec.y - colMinVec.y); CVector vehDist = GetPosition() - collidingVeh->GetPosition(); vehDist.Normalise(); float vehRightVecAndSpeedDotProd; if (Abs(angleDiffFromLookingFrontTLVC) >= vehTopRightHeading && Abs(angleDiffFromLookingFrontTLVC) < PI - vehTopRightHeading) { if (angleDiffFromLookingFrontTLVC <= 0.0f) { vehRightVecAndSpeedDotProd = DotProduct(collidingVeh->GetRight(), collidingVeh->m_vecMoveSpeed); // vehRightVecAndSpeedDotProd < 0.1f = Vehicle being overturned or spinning to it's right? if (collidingVehSpeedSqr > 1.0f / 100.0f && vehRightVecAndSpeedDotProd < 0.1f) { // Car's right faces towards us and isn't coming directly to us if (DotProduct(collidingVeh->GetRight(), GetForward()) < 0.0f && DotProduct(vehDist, collidingVeh->m_vecMoveSpeed) > 0.0f) { SetEvasiveStep(collidingVeh, 1); } } } else { vehRightVecAndSpeedDotProd = DotProduct(-1.0f * collidingVeh->GetRight(), collidingVeh->m_vecMoveSpeed); if (collidingVehSpeedSqr > 1.0f / 100.0f && vehRightVecAndSpeedDotProd < 0.1f) { if (DotProduct(collidingVeh->GetRight(), GetForward()) > 0.0f && DotProduct(vehDist, collidingVeh->m_vecMoveSpeed) > 0.0f) { SetEvasiveStep(collidingVeh, 1); } } } } else { vehRightVecAndSpeedDotProd = DotProduct(vehDist, collidingVeh->m_vecMoveSpeed); } if (vehRightVecAndSpeedDotProd <= 0.1f) { if (m_nPedState != PED_FIGHT) { SetLookFlag(collidingVeh, true); SetLookTimer(700); } } else { bIsStanding = false; CVector2D collidingEntMoveDir = -collidingVeh->m_vecMoveSpeed; int dir = GetLocalDirection(collidingEntMoveDir); SetFall(1000, (AnimationId)(dir + ANIM_STD_HIGHIMPACT_FRONT), false); float damage; if (collidingVeh->m_modelIndex == MI_TRAIN) { damage = 50.0f; } else { damage = 20.0f; } InflictDamage(collidingVeh, WEAPONTYPE_RAMMEDBYCAR, damage, PEDPIECE_TORSO, dir); Say(SOUND_PED_DAMAGE); } } else { KillPedWithCar(collidingVeh, m_fDamageImpulse); } if (m_pCollidingEntity != collidingEnt) bPushedAlongByCar = true; } if (m_fHealth < oldHealth && playerSufferSound) Say(SOUND_PED_HIT); break; } case ENTITY_TYPE_PED: { CollideWithPed((CPed*)collidingEnt); if (((CPed*)collidingEnt)->IsPlayer()) { CPlayerPed *player = ((CPlayerPed*)collidingEnt); Say(SOUND_PED_CHAT); if (m_nMoveState > PEDMOVE_STILL && player->IsPedInControl()) { if (player->m_fMoveSpeed < 1.0f) { if (!player->bIsLooking) { if (CTimer::GetTimeInMilliseconds() > player->m_lookTimer) { player->AnnoyPlayerPed(false); player->SetLookFlag(this, true); player->SetLookTimer(1300); eWeaponType weaponType = player->GetWeapon()->m_eWeaponType; uint32 weaponSlot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; if (weaponType == WEAPONTYPE_UNARMED || weaponSlot == 3 || weaponSlot == 5 || weaponSlot == 1) { player->bShakeFist = true; } } } } } } break; } default: break; } } CVector forceDir; if (!bIsInTheAir && m_nPedState != PED_JUMP && m_fDamageImpulse > 0.0f) { forceDir = m_vecDamageNormal; forceDir.z = 0.0f; if (!bIsStanding) { forceDir *= 4.0f; } else { forceDir *= 0.5f; } ApplyMoveForce(forceDir); } if ((bIsInTheAir && !DyingOrDead()) || (!bIsStanding && !bWasStanding && m_nPedState == PED_FALL)) { if (m_nPedStateTimer > 0 && m_nPedStateTimer <= 1000) { forceDir = GetPosition() - m_vecHitLastPos; } else { m_nPedStateTimer = 0; m_vecHitLastPos = GetPosition(); forceDir = CVector(0.0f, 0.0f, 0.0f); } CVector offsetToCheck; m_nPedStateTimer++; float adjustedTs = Max(CTimer::GetTimeStep(), 0.01f); CPad *pad0 = CPad::GetPad(0); if ((m_nPedStateTimer <= 50.0f / (4.0f * adjustedTs) || m_nPedStateTimer * 0.01f <= forceDir.MagnitudeSqr()) && (m_nCollisionRecords <= 1 || m_nPedStateTimer <= 50.0f / (2.0f * adjustedTs) || m_nPedStateTimer * 1.0f / 250.0f <= Abs(forceDir.z))) { if (m_nCollisionRecords == 1 && m_aCollisionRecords[0] != nil && m_aCollisionRecords[0]->IsBuilding() && m_nPedStateTimer > 50.0f / (2.0f * adjustedTs) && m_nPedStateTimer * 1.0f / 250.0f > Abs(forceDir.z)) { offsetToCheck.x = -forceDir.y; offsetToCheck.z = 1.0f; offsetToCheck.y = forceDir.x; offsetToCheck.Normalise(); CVector posToCheck = GetPosition() + offsetToCheck; // These are either obstacle or ground to land, I don't know which one. float obstacleForFlyingZ, obstacleForFlyingOtherDirZ; CColPoint obstacleForFlying, obstacleForFlyingOtherDir; // Check is there any room for being knocked up in reverse direction of force if (CWorld::ProcessVerticalLine(posToCheck, -20.0f, obstacleForFlying, foundEnt, true, false, false, false, false, false, nil)) { obstacleForFlyingZ = obstacleForFlying.point.z; } else { obstacleForFlyingZ = 500.0f; } posToCheck = GetPosition() - offsetToCheck; // Now check for direction of force this time if (CWorld::ProcessVerticalLine(posToCheck, -20.0f, obstacleForFlyingOtherDir, foundEnt, true, false, false, false, false, false, nil)) { obstacleForFlyingOtherDirZ = obstacleForFlyingOtherDir.point.z; } else { obstacleForFlyingOtherDirZ = 501.0f; } uint8 flyDir = 0; float feetZ = GetPosition().z - FEET_OFFSET; #ifdef FIX_BUGS if (obstacleForFlyingZ > feetZ && obstacleForFlyingOtherDirZ < 501.0f) flyDir = 1; else if (obstacleForFlyingOtherDirZ > feetZ && obstacleForFlyingZ < 500.0f) flyDir = 2; #else if ((obstacleForFlyingZ > feetZ && obstacleForFlyingOtherDirZ < 500.0f) || (obstacleForFlyingZ > feetZ && obstacleForFlyingOtherDirZ > feetZ)) flyDir = 1; else if (obstacleForFlyingOtherDirZ > feetZ && obstacleForFlyingZ < 499.0f) flyDir = 2; #endif if (flyDir != 0 && !bHeadStuckInCollision) { SetPosition((flyDir == 2 ? obstacleForFlyingOtherDir.point : obstacleForFlying.point)); GetMatrix().GetPosition().z += FEET_OFFSET; GetMatrix().UpdateRW(); SetLanding(); bIsStanding = true; } if (obstacleForFlyingZ < obstacleForFlyingOtherDirZ) { offsetToCheck *= -1.0f; } offsetToCheck.z = 1.0f; forceDir = 4.0f * offsetToCheck; forceDir.z = 4.0f; ApplyMoveForce(forceDir); // What was that for?? It pushes player inside of collision sometimes and kills him. #ifdef FIX_BUGS if (!IsPlayer()) #endif GetMatrix().GetPosition() += 0.25f * offsetToCheck; m_fRotationCur = CGeneral::GetRadianAngleBetweenPoints(offsetToCheck.x, offsetToCheck.y, 0.0f, 0.0f); m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); m_fRotationDest = m_fRotationCur; SetHeading(m_fRotationCur); if (m_nPedState != PED_FALL && !bIsPedDieAnimPlaying) { SetFall(1000, ANIM_STD_HIGHIMPACT_BACK, true); } bIsInTheAir = false; } else if (m_vecDamageNormal.z > 0.4f) { if (m_nPedState == PED_JUMP) { if (m_nWaitTimer <= 2000) { if (m_nWaitTimer < 1000) m_nWaitTimer += CTimer::GetTimeStep() * 0.02f * 1000.0f; } else { m_nWaitTimer = 0; } } forceDir = m_vecDamageNormal; forceDir.z = 0.0f; forceDir.Normalise(); if (m_nPedState != PED_JUMP || m_nWaitTimer >= 300) { ApplyMoveForce(2.0f * forceDir); } else { ApplyMoveForce(-4.0f * forceDir); } } } else if ((CTimer::GetFrameCounter() + m_randomSeed % 256 + 3) & 7) { if (IsPlayer() && m_nPedState != PED_JUMP && pad0->JumpJustDown()) { int16 padWalkX = pad0->GetPedWalkLeftRight(); int16 padWalkY = pad0->GetPedWalkUpDown(); if (Abs(padWalkX) > 0.0f || Abs(padWalkY) > 0.0f) { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -padWalkX, padWalkY); m_fRotationDest -= TheCamera.Orientation; m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); m_fRotationCur = m_fRotationDest; SetHeading(m_fRotationCur); } SetJump(); m_nPedStateTimer = 0; m_vecHitLastPos = GetPosition(); // Why? forceDir is unused after this point. forceDir = CVector(0.0f, 0.0f, 0.0f); } else if (IsPlayer()) { int16 padWalkX = pad0->GetPedWalkLeftRight(); int16 padWalkY = pad0->GetPedWalkUpDown(); if (Abs(padWalkX) > 0.0f || Abs(padWalkY) > 0.0f) { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -padWalkX, padWalkY); m_fRotationDest -= TheCamera.Orientation; m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); m_fRotationCur = m_fRotationDest; SetHeading(m_fRotationCur); } CAnimBlendAssociation *jumpAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_JUMP_GLIDE); if (!jumpAssoc) jumpAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_GLIDE); if (jumpAssoc) { jumpAssoc->blendDelta = -3.0f; jumpAssoc->flags |= ASSOC_DELETEFADEDOUT; } if (m_nPedState == PED_JUMP) m_nPedState = PED_IDLE; } else { CAnimBlendAssociation *jumpAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_JUMP_GLIDE); if (!jumpAssoc) jumpAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_GLIDE); if (jumpAssoc) { jumpAssoc->blendDelta = -3.0f; jumpAssoc->flags |= ASSOC_DELETEFADEDOUT; } } } else { offsetToCheck = GetPosition(); offsetToCheck.z += 0.5f; if (CWorld::ProcessVerticalLine(offsetToCheck, GetPosition().z - FEET_OFFSET, foundCol, foundEnt, true, true, false, true, false, false, nil)) { if (!bHeadStuckInCollision || FEET_OFFSET + foundCol.point.z < GetPosition().z) { GetMatrix().GetPosition().z = FEET_OFFSET + foundCol.point.z; GetMatrix().UpdateRW(); if (bHeadStuckInCollision) bHeadStuckInCollision = false; } SetLanding(); bIsStanding = true; } } } else if (m_nPedStateTimer < 1001) { m_nPedStateTimer = 0; } } if (bIsDucking) Duck(); if (bStartWanderPathOnFoot) { if (IsPedInControl()) { ClearAll(); SetWanderPath(m_nPathDir); bStartWanderPathOnFoot = false; } else if (m_nPedState == PED_DRIVING) { bWanderPathAfterExitingCar = true; SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); bStartWanderPathOnFoot = false; } } if (!bIsStanding && m_vecMoveSpeed.z > 0.25) { float airResistance = Pow(0.95f, CTimer::GetTimeStep()); m_vecMoveSpeed *= airResistance; } if (IsPlayer() || !bIsStanding || m_vecMoveSpeed.x != 0.0f || m_vecMoveSpeed.y != 0.0f || m_vecMoveSpeed.z != 0.0f || (m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL) || m_vecAnimMoveDelta.x != 0.0f || m_vecAnimMoveDelta.y != 0.0f || m_nPedState == PED_JUMP || bIsInTheAir || m_pCurrentPhysSurface) { CPhysical::ProcessControl(); } else { bHasContacted = false; bIsInSafePosition = false; bWasPostponed = false; bHasHitWall = false; m_nCollisionRecords = 0; bHasCollided = 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); } if (m_nPedState != PED_DIE || bIsPedDieAnimPlaying) { RequestDelayedWeapon(); PlayFootSteps(); if (m_nPedState != PED_DEAD) { CalculateNewVelocity(); CalculateNewOrientation(); } UpdatePosition(); if (IsPedInControl() && !bIsStanding && !m_pDamageEntity) { if (m_attachedTo) { bIsInTheAir = false; } else if (CheckIfInTheAir()) { SetInTheAir(); bHeadStuckInCollision = false; } } if (bHeadStuckInCollision) { CVector posToCheck = GetPosition(); posToCheck.z += 0.9f; if (!CWorld::TestSphereAgainstWorld(posToCheck, 0.2f, this, true, true, false, true, false, false)) bHeadStuckInCollision = false; } ProcessObjective(); if (!bIsAimingGun) { if (bIsRestoringGun) RestoreGunPosition(); } else { AimGun(); } if (bIsLooking) { MoveHeadToLook(); } else if (bIsRestoringLook) { RestoreHeadPosition(); } if (bIsInTheAir) InTheAir(); if (bUpdateAnimHeading) { if (m_nPedState != PED_GETUP && m_nPedState != PED_FALL) { m_fRotationCur -= HALFPI; m_fRotationDest = m_fRotationCur; bUpdateAnimHeading = false; } } if (m_nWaitState != WAITSTATE_FALSE) Wait(); #ifdef CANCELLABLE_CAR_ENTER static bool cancelJack = false; if (IsPlayer()) { if (EnteringCar() && m_pVehicleAnim) { CPad *pad = CPad::GetPad(0); if (!pad->ArePlayerControlsDisabled()) { int vehAnim = m_pVehicleAnim->animId; int16 padWalkX = pad->GetPedWalkLeftRight(); int16 padWalkY = pad->GetPedWalkUpDown(); if (Abs(padWalkX) > 0.0f || Abs(padWalkY) > 0.0f) { if (vehAnim == ANIM_STD_CAR_OPEN_DOOR_LHS || vehAnim == ANIM_STD_CAR_OPEN_DOOR_RHS || vehAnim == ANIM_STD_COACH_OPEN_LHS || vehAnim == ANIM_STD_COACH_OPEN_RHS || vehAnim == ANIM_STD_VAN_OPEN_DOOR_REAR_LHS || vehAnim == ANIM_STD_VAN_OPEN_DOOR_REAR_RHS) { if (!m_pMyVehicle->pDriver) { cancelJack = false; bCancelEnteringCar = true; } else cancelJack = true; } else if (vehAnim == ANIM_STD_QUICKJACK && m_pVehicleAnim->GetTimeLeft() > 0.75f) { cancelJack = true; } else if (vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LO_LHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LO_RHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_RHS) { bCancelEnteringCar = true; cancelJack = false; } } if (cancelJack && vehAnim == ANIM_STD_QUICKJACK && m_pVehicleAnim->GetTimeLeft() > 0.75f && m_pVehicleAnim->GetTimeLeft() < 0.78f) { cancelJack = false; QuitEnteringCar(); RestorePreviousObjective(); } if (cancelJack && (vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LO_LHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_LO_RHS || vehAnim == ANIM_STD_CAR_PULL_OUT_PED_RHS)) { cancelJack = false; bCancelEnteringCar = true; } } } else cancelJack = false; } #endif switch (m_nPedState) { case PED_IDLE: Idle(); break; case PED_LOOK_ENTITY: case PED_LOOK_HEADING: Look(); break; case PED_WANDER_RANGE: // III has these in here(and they were unused): /* WanderRange(); CheckAroundForPossibleCollisions(); */ break; case PED_WANDER_PATH: WanderPath(); break; case PED_ENTER_CAR: case PED_CARJACK: { break; } case PED_FLEE_POS: ms_vec2DFleePosition = m_fleeFromPos; Flee(); break; case PED_FLEE_ENTITY: if (!m_fleeFrom) { bMakeFleeScream = false; SetIdle(); break; } if (CTimer::GetTimeInMilliseconds() <= m_nPedStateTimer) break; ms_vec2DFleePosition = m_fleeFrom->GetPosition(); Flee(); break; case PED_FOLLOW_PATH: FollowPath(); break; case PED_PAUSE: Pause(); break; case PED_ATTACK: Attack(); break; case PED_FIGHT: Fight(); break; case PED_CHAT: Chat(); break; case PED_AIM_GUN: if (m_pPointGunAt && m_pPointGunAt->IsPed() #ifdef FIX_BUGS && !GetWeapon()->IsTypeMelee() #endif && ((CPed*)m_pPointGunAt)->CanSeeEntity(this, CAN_SEE_ENTITY_ANGLE_THRESHOLD * 2)) { ((CPed*)m_pPointGunAt)->ReactToPointGun(this); } PointGunAt(); break; case PED_SEEK_CAR: SeekCar(); break; case PED_SEEK_IN_BOAT: SeekBoatPosition(); break; case PED_BUY_ICECREAM: BuyIceCream(); break; case PED_INVESTIGATE: InvestigateEvent(); break; case PED_ON_FIRE: if (IsPlayer()) break; if (CTimer::GetTimeInMilliseconds() <= m_fleeTimer) { if (m_pFire) { if (m_fleeFrom) { ms_vec2DFleePosition = m_fleeFrom->GetPosition(); } else { ms_vec2DFleePosition = m_fleeFromPos; } Flee(); } else { m_nLastPedState = PED_NONE; SetWanderPath(0); SetWaitState(WAITSTATE_FINISH_FLEE, 0); } } else { if (m_pFire) m_pFire->Extinguish(); } break; case PED_ANSWER_MOBILE: AnswerMobile(); break; case PED_FALL: Fall(); break; case PED_GETUP: SetGetUp(); break; #ifdef GTA_TRAIN case PED_ENTER_TRAIN: EnterTrain(); break; case PED_EXIT_TRAIN: ExitTrain(); break; #endif case PED_DRIVING: { if (!m_pMyVehicle) { bInVehicle = false; FlagToDestroyWhenNextProcessed(); return; } #ifdef CAR_AIRBREAK if (IsPlayer()) { CPad* pad = CPad::GetPad(0); if (!pad->ArePlayerControlsDisabled()) { if (pad->GetHorn()) { float c = Cos(m_fRotationCur); float s = Sin(m_fRotationCur); m_pMyVehicle->GetRight() = CVector(1.0f, 0.0f, 0.0f); m_pMyVehicle->GetForward() = CVector(0.0f, 1.0f, 0.0f); m_pMyVehicle->GetUp() = CVector(0.0f, 0.0f, 1.0f); if (pad->GetAccelerate()) { m_pMyVehicle->ApplyMoveForce(GetForward() * 30.0f); } else if (pad->GetBrake()) { m_pMyVehicle->ApplyMoveForce(-GetForward() * 30.0f); } else { int16 lr = pad->GetSteeringLeftRight(); if (lr < 0) { //m_pMyVehicle->ApplyTurnForce(20.0f * -GetRight(), GetForward()); m_pMyVehicle->ApplyMoveForce(-GetRight() * 30.0f); } else if (lr > 0) { m_pMyVehicle->ApplyMoveForce(GetRight() * 30.0f); } else { m_pMyVehicle->ApplyMoveForce(0.0f, 0.0f, 50.0f); } } } } } #endif if (m_pMyVehicle->pDriver == this) { DriveVehicle(); if (!m_pMyVehicle) return; } else { LookForSexyPeds(); LookForSexyCars(); } if (!IsPlayer() && m_pMyVehicle->IsBoat() && FindPlayerPed()->m_pCurrentPhysSurface == m_pMyVehicle && (CharCreatedBy != MISSION_CHAR || !bIsPlayerFriend)) { SetObjective(OBJECTIVE_KILL_CHAR_ON_BOAT, FindPlayerPed()); Say(SOUND_PED_CAR_JACKED); } break; } case PED_DIE: Die(); break; case PED_HANDS_UP: if (m_pedStats->m_flags & STAT_GUN_PANIC) { if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HANDSCOWER)) { CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSCOWER); Say(SOUND_PED_HANDS_COWER); } } else if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HANDSUP)) { CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSUP); Say(SOUND_PED_HANDS_UP); } break; default: break; } SetMoveAnim(); if (bPedIsBleeding || m_bleedCounter != 0) { if (CGame::nastyGame) { if (m_bleedCounter != 0) m_bleedCounter--; if (!(CTimer::GetFrameCounter() & 3)) { CVector cameraDist = GetPosition() - TheCamera.GetPosition(); if (cameraDist.MagnitudeSqr() < sq(50.0f)) { float length = (CGeneral::GetRandomNumber() & 127) * 0.0015f + 0.15f; CVector bloodPos( ((CGeneral::GetRandomNumber() & 127) - 64) * 0.007f, ((CGeneral::GetRandomNumber() & 127) - 64) * 0.007f, 1.0f); bloodPos += GetPosition(); CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, length, 0.0f, 0.0f, -length, 255, 255, 0, 0, 4.0f, (CGeneral::GetRandomNumber() & 4095) + 2000, 1.0f); } } } } ServiceTalking(); if (bInVehicle && !m_pMyVehicle) bInVehicle = false; if (bHeldHostageInCar) { if (m_pMyVehicle && m_pMyVehicle->pDriver && m_pMyVehicle->pDriver->IsPlayer()) { Say(SOUND_PED_FLEE_SPRINT); } } if (m_delayedSoundID >= 0 && CTimer::GetTimeInMilliseconds() > m_delayedSoundTimer) { Say(m_delayedSoundID); m_delayedSoundID = -1; m_delayedSoundTimer = 0; } if (bFannyMagnetCheat && m_nPedType == PEDTYPE_CIVFEMALE && m_pedStats->m_sexiness > 40 && !m_leader) { SetLeader(FindPlayerPed()); } } else { if (bIsStanding && (!m_pCurrentPhysSurface || IsPlayer()) || bIsInWater || !bUsesCollision) { SetDead(); } m_pCurrentPhysSurface = nil; } } else ServiceTalking(); } int32 CPed::ProcessEntityCollision(CEntity *collidingEnt, CColPoint *collidingPoints) { bool collidedWithBoat = false; bool belowTorsoCollided = false; float gravityEffect = -0.15f * CTimer::GetTimeStep(); CColPoint intersectionPoint; CColLine ourLine; CColModel *ourCol = CModelInfo::GetModelInfo(GetModelIndex())->GetColModel(); CColModel *hisCol = CModelInfo::GetModelInfo(collidingEnt->GetModelIndex())->GetColModel(); if (!bUsesCollision && !bJustCheckCollision) return false; if (collidingEnt->IsVehicle() && ((CVehicle*)collidingEnt)->IsBoat()) collidedWithBoat = true; // ofc we're not vehicle if (!m_bIsVehicleBeingShifted && !bSkipLineCol && !collidingEnt->IsPed()) { if (!bCollisionProcessed) { m_pCurrentPhysSurface = nil; if (bIsStanding) { bIsStanding = false; bWasStanding = true; } bCollisionProcessed = true; m_fCollisionSpeed += m_vecMoveSpeed.Magnitude2D() * CTimer::GetTimeStep(); bStillOnValidPoly = false; if (IsPlayer() || m_fCollisionSpeed >= 1.0f && (m_fCollisionSpeed >= 2.0f || m_nPedState != PED_WANDER_PATH)) { m_collPoly.valid = false; m_fCollisionSpeed = 0.0f; bHitSteepSlope = false; } else { CVector pos = GetPosition(); float potentialGroundZ = GetPosition().z - FEET_OFFSET; if (bWasStanding) { pos.z += -0.25f; potentialGroundZ += gravityEffect; } if (CCollision::IsStoredPolyStillValidVerticalLine(pos, potentialGroundZ, intersectionPoint, &m_collPoly)) { bStillOnValidPoly = true; if(!bHeadStuckInCollision || FEET_OFFSET + intersectionPoint.point.z < GetPosition().z) { GetMatrix().GetPosition().z = FEET_OFFSET + intersectionPoint.point.z; if (bHeadStuckInCollision) bHeadStuckInCollision = false; } m_vecMoveSpeed.z = 0.0f; bIsStanding = true; } else { m_collPoly.valid = false; m_fCollisionSpeed = 0.0f; bHitSteepSlope = false; } } } if (!bStillOnValidPoly) { CVector potentialCenter = GetPosition(); potentialCenter.z = GetPosition().z - 0.52f; // 0.52f should be a ped's approx. radius float totalRadiusWhenCollided = collidingEnt->GetBoundRadius() + 0.52f - gravityEffect; if (bWasStanding) { if (collidedWithBoat) { potentialCenter.z += 2.0f * gravityEffect; totalRadiusWhenCollided += Abs(gravityEffect); } else { potentialCenter.z += gravityEffect; } } if (sq(totalRadiusWhenCollided) > (potentialCenter - collidingEnt->GetBoundCentre()).MagnitudeSqr()) { ourLine.p0 = GetPosition(); ourLine.p1 = GetPosition(); ourLine.p1.z = GetPosition().z - FEET_OFFSET; if (bWasStanding) { ourLine.p1.z = ourLine.p1.z + gravityEffect; ourLine.p0.z = ourLine.p0.z + -0.25f; } float minDist = 1.0f; belowTorsoCollided = CCollision::ProcessVerticalLine(ourLine, collidingEnt->GetMatrix(), *hisCol, intersectionPoint, minDist, false, false, &m_collPoly); if (collidedWithBoat && bWasStanding && !belowTorsoCollided) { ourLine.p0.z = ourLine.p1.z; ourLine.p1.z = ourLine.p1.z + gravityEffect; belowTorsoCollided = CCollision::ProcessVerticalLine(ourLine, collidingEnt->GetMatrix(), *hisCol, intersectionPoint, minDist, false, false, &m_collPoly); } if (belowTorsoCollided) { if (!bIsStanding || FEET_OFFSET + intersectionPoint.point.z > GetPosition().z || collidedWithBoat && 3.12f + intersectionPoint.point.z > GetPosition().z) { if (!collidingEnt->IsVehicle() && !collidingEnt->IsObject()) { m_pCurSurface = collidingEnt; collidingEnt->RegisterReference((CEntity**)&m_pCurSurface); bTryingToReachDryLand = false; bOnBoat = false; } else { m_pCurrentPhysSurface = (CPhysical*)collidingEnt; collidingEnt->RegisterReference((CEntity**)&m_pCurrentPhysSurface); m_vecOffsetFromPhysSurface = intersectionPoint.point - collidingEnt->GetPosition(); m_pCurSurface = collidingEnt; collidingEnt->RegisterReference((CEntity**)&m_pCurSurface); m_collPoly.valid = false; if (collidingEnt->IsVehicle() && ((CVehicle*)collidingEnt)->IsBoat()) { bOnBoat = true; } else { bOnBoat = false; } } if (!bHeadStuckInCollision || FEET_OFFSET + intersectionPoint.point.z < GetPosition().z) { GetMatrix().GetPosition().z = FEET_OFFSET + intersectionPoint.point.z; if (bHeadStuckInCollision) bHeadStuckInCollision = false; } m_nSurfaceTouched = intersectionPoint.surfaceB; if (m_nSurfaceTouched == SURFACE_STEEP_CLIFF) { bHitSteepSlope = true; m_vecDamageNormal = intersectionPoint.normal; } } float upperSpeedLimit = 0.33f; float lowerSpeedLimit = -0.25f; float speed = m_vecMoveSpeed.Magnitude2D(); if (m_nPedState == PED_IDLE) { upperSpeedLimit *= 2.0f; lowerSpeedLimit *= 1.5f; } CAnimBlendAssociation *fallAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL); if (!bWasStanding && speed > upperSpeedLimit && (!bPushedAlongByCar || m_vecMoveSpeed.z < lowerSpeedLimit) && m_pCollidingEntity != collidingEnt) { float damage = 100.0f * Max(speed - 0.25f, 0.0f); float damage2 = damage; if (m_vecMoveSpeed.z < -0.25f) damage += (-0.25f - m_vecMoveSpeed.z) * 150.0f; uint8 dir = 2; // from backward if (m_vecMoveSpeed.x > 0.01f || m_vecMoveSpeed.x < -0.01f || m_vecMoveSpeed.y > 0.01f || m_vecMoveSpeed.y < -0.01f) { CVector2D offset = -m_vecMoveSpeed; dir = GetLocalDirection(offset); } if (CSurfaceTable::IsSoftLanding(intersectionPoint.surfaceB)) damage *= 0.5f; InflictDamage(collidingEnt, WEAPONTYPE_FALL, damage, PEDPIECE_TORSO, dir); if (IsPlayer() && damage2 > 5.0f) Say(SOUND_PED_LAND); } else if (!bWasStanding && fallAnim && -0.016f * CTimer::GetTimeStep() > m_vecMoveSpeed.z) { InflictDamage(collidingEnt, WEAPONTYPE_FALL, 15.0f, PEDPIECE_TORSO, 2); } m_vecMoveSpeed.z = 0.0f; bIsStanding = true; } else { bOnBoat = false; } } } } int ourCollidedSpheres = CCollision::ProcessColModels(GetMatrix(), *ourCol, collidingEnt->GetMatrix(), *hisCol, collidingPoints, nil, nil); if (ourCollidedSpheres > 0 || belowTorsoCollided) { AddCollisionRecord(collidingEnt); if (!collidingEnt->IsBuilding()) ((CPhysical*)collidingEnt)->AddCollisionRecord(this); if (ourCollidedSpheres > 0 && (collidingEnt->IsBuilding() || collidingEnt->GetIsStatic())) { bHasHitWall = true; } } if (collidingEnt->IsBuilding() || collidingEnt->GetIsStatic()) { if (bWasStanding) { CVector sphereNormal; float normalLength; for(int sphere = 0; sphere < ourCollidedSpheres; sphere++) { sphereNormal = collidingPoints[sphere].normal; if (sphereNormal.z >= -1.0f || !IsPlayer()) { normalLength = sphereNormal.Magnitude2D(); if (normalLength != 0.0f) { sphereNormal.x = sphereNormal.x / normalLength; sphereNormal.y = sphereNormal.y / normalLength; } } else { float speed = m_vecMoveSpeed.Magnitude2D(); sphereNormal.x = -m_vecMoveSpeed.x / Max(0.001f, speed); sphereNormal.y = -m_vecMoveSpeed.y / Max(0.001f, speed); GetMatrix().GetPosition().z -= 0.05f; bHeadStuckInCollision = true; } sphereNormal.Normalise(); collidingPoints[sphere].normal = sphereNormal; if (collidingPoints[sphere].surfaceB == SURFACE_STEEP_CLIFF) bHitSteepSlope = true; } } } return ourCollidedSpheres; } static void particleProduceFootSplash(CPed *ped, CVector const &pos, float size, int times) { for (int i = 0; i < times; i++) { CVector adjustedPos = pos; adjustedPos.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); adjustedPos.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); CVector direction = ped->GetForward() * -0.05f; CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, adjustedPos, direction, nil, size, CRGBA(32, 32, 32, 32), 0, 0, CGeneral::GetRandomNumber() & 1, 200); } } static void particleProduceFootDust(CPed *ped, CVector const &pos, float size, int times) { switch (ped->m_nSurfaceTouched) { case SURFACE_TARMAC: case SURFACE_GRAVEL: case SURFACE_PAVEMENT: case SURFACE_SAND: case SURFACE_SAND_BEACH: case SURFACE_CONCRETE_BEACH: for (int i = 0; i < times; ++i) { CVector adjustedPos = pos; adjustedPos.x += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); adjustedPos.y += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); // ?? CGeneral::GetRandomNumber(); CGeneral::GetRandomNumber(); CParticle::AddParticle(PARTICLE_PEDFOOT_DUST, adjustedPos, CVector(0.0f, 0.0f, 0.0f), nil, size, CRGBA(0, 0, 0, 0), 0, 0, 0, 0); } break; default: break; } } void CPed::PlayFootSteps(void) { CAnimBlendAssociation *assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); CAnimBlendAssociation *walkRunAssoc = nil; float walkRunAssocBlend = 0.0f, idleAssocBlend = 0.0f; bool isSkater = m_pedStats == CPedStats::ms_apPedStats[PEDSTAT_SKATER]; CVector footPosL(0.0f, 0.0f, 0.0f), footPosR(0.0f, 0.0f, 0.0f); bool footPosLok = false, footPosRok = false; if (bDoBloodyFootprints) { if (m_bloodyFootprintCountOrDeathTime > 0 && m_bloodyFootprintCountOrDeathTime < 300) { m_bloodyFootprintCountOrDeathTime--; if (m_bloodyFootprintCountOrDeathTime == 0) bDoBloodyFootprints = false; } } if (!bIsStanding) return; for (; assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { if (assoc->flags & ASSOC_WALK) { walkRunAssoc = assoc; walkRunAssocBlend += assoc->blendAmount; } else if ((assoc->flags & ASSOC_NOWALK) == 0) { idleAssocBlend += assoc->blendAmount; } } if (walkRunAssoc && walkRunAssocBlend > 0.5f && idleAssocBlend < 1.0f) { float stepStart = 1.f / 15.f; float stepEnd = walkRunAssoc->hierarchy->totalLength / 2.0f + stepStart; float currentTime = walkRunAssoc->currentTime; if (isSkater) { // both are unused static float stepStartSection = 1.0f; static float animSections = 15.f; float moveStart, soundVolume, skateTime; if (walkRunAssoc->animId == ANIM_STD_WALK) { moveStart = 0.0f; skateTime = 8.f / 15.f; } else { moveStart = 0.0f; skateTime = 5.f / 15.f; } switch (CSurfaceTable::GetAdhesionGroup(m_nSurfaceTouched)) { case ADHESIVE_LOOSE: if (CGeneral::GetRandomNumber() % 128) { m_vecAnimMoveDelta *= 0.5f; } else { SetFall(0, ANIM_STD_HIGHIMPACT_BACK, false); } soundVolume = 0.5f; break; case ADHESIVE_SAND: if (CGeneral::GetRandomNumber() % 64) { m_vecAnimMoveDelta *= 0.2f; } else { SetFall(0, ANIM_STD_HIGHIMPACT_BACK, false); } soundVolume = 0.2f; break; case ADHESIVE_WET: m_vecAnimMoveDelta *= 0.3f; soundVolume = 0.2f; break; default: soundVolume = 1.f; break; } if (soundVolume > 0.2f && currentTime > moveStart && currentTime - walkRunAssoc->timeStep <= moveStart) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_SKATING, ((int)(127.f * soundVolume) | (walkRunAssoc->animId << 8))); } else if (soundVolume > 0.2f) { if (currentTime > skateTime && currentTime - walkRunAssoc->timeStep <= skateTime) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_SKATING, ((int)(127.f * soundVolume) | (walkRunAssoc->animId << 8))); } } } else { int stepPart = 0; // This section is shortened/optimized for sanity. if (currentTime >= stepStart && currentTime - walkRunAssoc->timeStep < stepStart) stepPart = 1; else if (currentTime >= stepEnd && currentTime - walkRunAssoc->timeStep < stepEnd) stepPart = 2; if (stepPart != 0) { CVector adjustedFootPos; if (stepPart == 1) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_STEP_START, 1.0f); TransformToNode(footPosL, PED_FOOTL); footPosLok = true; adjustedFootPos = footPosL; } else { DMAudio.PlayOneShot(m_audioEntityId, SOUND_STEP_END, 1.0f); TransformToNode(footPosR, PED_FOOTR); footPosRok = true; adjustedFootPos = footPosR; } CVector forward = GetForward(); adjustedFootPos.z -= 0.1f; adjustedFootPos += 0.2f * forward; if (bDoBloodyFootprints) { CVector2D top(forward * 0.26f); CVector2D right(GetRight() * (stepPart == 1 ? 0.14f : 0.1f)); CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &adjustedFootPos, top.x, top.y, right.x, right.y, 255, 255, 0, 0, 4.0f, 3000, 1.0f); if (m_bloodyFootprintCountOrDeathTime <= 20) { m_bloodyFootprintCountOrDeathTime = 0; bDoBloodyFootprints = false; } else { m_bloodyFootprintCountOrDeathTime -= 20; } } if (m_nSurfaceTouched == SURFACE_SAND || m_nSurfaceTouched == SURFACE_SAND_BEACH) { CVector2D top(forward * -0.26f); CVector2D right(GetRight() * (stepPart == 1 ? 0.1f : 0.14f)); CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpShadowPedTex, &adjustedFootPos, top.x, top.y, right.x, right.y, 120, 250, 250, 50, 4.0f, 5000, 1.0f); } if (CWeather::Rain <= 0.1f || CCullZones::CamNoRain() || CCullZones::PlayerNoRain()) { if (IsPlayer()) particleProduceFootDust(this, adjustedFootPos, 0.0f, 4); } else if (stepPart == 2) { particleProduceFootSplash(this, adjustedFootPos, 0.15f, 4); } } } } if (IsPlayer() && !walkRunAssoc && bIsLanding) { if (!footPosLok) TransformToNode(footPosL, PED_FOOTL); CVector forward = GetForward(); CVector adjustedFootPosL = footPosL; adjustedFootPosL.z -= 0.1f; adjustedFootPosL += 0.2f * forward; if (bDoBloodyFootprints) { CVector2D top(forward * 0.26f); CVector2D right(GetRight() * 0.14f); CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &adjustedFootPosL, top.x, top.y, right.x, right.y, 255, 255, 0, 0, 4.0f, 3000, 1.0f); if (m_bloodyFootprintCountOrDeathTime <= 20) { m_bloodyFootprintCountOrDeathTime = 0; bDoBloodyFootprints = false; } else { m_bloodyFootprintCountOrDeathTime -= 20; } } if (!isSkater) { if (m_nSurfaceTouched == SURFACE_SAND || m_nSurfaceTouched == SURFACE_SAND_BEACH) { CVector2D top(forward * -0.26f); CVector2D right(GetRight() * 0.14f); CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpShadowPedTex, &adjustedFootPosL, top.x, top.y, right.x, right.y, 120, 250, 250, 50, 4.0f, 5000, 1.0f); } } if(!footPosRok) TransformToNode(footPosR, PED_FOOTR); CVector adjustedFootPosR = footPosR; adjustedFootPosR.z -= 0.1f; adjustedFootPosR += 0.2f * forward; if (bDoBloodyFootprints) { CVector2D top(forward * 0.26f); CVector2D right(GetRight() * 0.1f); CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &adjustedFootPosR, top.x, top.y, right.x, right.y, 255, 255, 0, 0, 4.0f, 3000, 1.0f); if (m_bloodyFootprintCountOrDeathTime <= 20) { m_bloodyFootprintCountOrDeathTime = 0; bDoBloodyFootprints = false; } else { m_bloodyFootprintCountOrDeathTime -= 20; } } if (!isSkater) { if (m_nSurfaceTouched == SURFACE_SAND || m_nSurfaceTouched == SURFACE_SAND_BEACH) { CVector2D top(forward * -0.26f); CVector2D right(GetRight() * 0.14f); CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpShadowPedTex, &adjustedFootPosR, top.x, top.y, right.x, right.y, 120, 250, 250, 50, 4.0f, 5000, 1.0f); } } } if (m_nSurfaceTouched == SURFACE_WATER) { CRGBA rubberSmokeColor(255, 255, 255, 196); float pedSpeed = CVector2D(m_vecMoveSpeed).Magnitude(); if (pedSpeed > 0.03f && CTimer::GetFrameCounter() % 2 == 0 && pedSpeed > 0.13f) { float particleSize = pedSpeed * 2.0f; if (particleSize < 0.25f) particleSize = 0.25f; if (particleSize > 0.75f) particleSize = 0.75f; CVector particlePos = GetPosition() + GetForward() * 0.3f; particlePos.z -= 1.2f; CVector particleDir = m_vecMoveSpeed * -0.75f; particleDir.z = CGeneral::GetRandomNumberInRange(0.01f, 0.03f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, particlePos, particleDir, nil, 0.5f * particleSize, CRGBA(0,0,0,0), 0, 0, 0, 0); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, particlePos, particleDir, nil, particleSize, rubberSmokeColor, 0, 0, 0, 0); } if (m_nPedState == PED_JUMP) { CVector particlePos = GetPosition(); particlePos.z -= 0.1f; CVector particleDir(0.0f, 0.075f, 0.0f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, particlePos, particleDir, nil, 0.005f, CRGBA(0, 0, 0, 0), 0, 0, 0, 0); particleDir.x += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); particleDir.y += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); particleDir.z -= CGeneral::GetRandomNumberInRange(0.025f, 0.05f); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, particlePos, particleDir, nil, 0.5f, rubberSmokeColor, 0, 0, 0, 0); } } } // Actually GetLocalDirectionTo(Turn/Look) int CPed::GetLocalDirection(const CVector2D &posOffset) { int direction; float angle; for (angle = posOffset.Heading() - m_fRotationCur + DEGTORAD(45.0f); angle < 0.0f; angle += TWOPI); for (direction = RADTODEG(angle)/90.0f; direction > 3; direction -= 4); // 0-forward, 1-left, 2-backward, 3-right. return direction; } bool CPed::SetDirectionToWalkAroundVehicle(CVehicle* veh) { return SetFollowPath(m_vecSeekPos, 0.0f, m_nMoveState, veh, m_pedInObjective, m_nMoveState == PEDMOVE_WALK ? 2000 : 250); } void CPed::SetDirectionToWalkAroundObject(CEntity *obj) { float distLimitForTimer = 8.0f; CColModel *objCol = CModelInfo::GetModelInfo(obj->GetModelIndex())->GetColModel(); CVector objColMin = objCol->boundingBox.min; CVector objColMax = objCol->boundingBox.max; CVector objColCenter = (objColMin + objColMax) / 2.0f; CMatrix objMat(obj->GetMatrix()); float dirToSet = obj->GetForward().Heading(); bool goingToEnterCarAndItsVan = false; bool goingToEnterCar = false; bool objUpsideDown = false; float checkIntervalInDist = (objColMax.y - objColMin.y) * 0.1f; float checkIntervalInTime; if (m_nMoveState == PEDMOVE_NONE || m_nMoveState == PEDMOVE_STILL) return; if (CharCreatedBy != MISSION_CHAR && obj->GetModelIndex() == MI_PHONEBOOTH1) { bool isRunning = m_nMoveState == PEDMOVE_RUN || m_nMoveState == PEDMOVE_SPRINT; SetFindPathAndFlee(obj, 5000, !isRunning); return; } CVector2D adjustedColMin(objColMin.x - 0.35f, objColMin.y - 0.35f); CVector2D adjustedColMax(objColMax.x + 0.35f, objColMax.y + 0.35f); checkIntervalInDist = Max(checkIntervalInDist, 0.5f); checkIntervalInDist = Min(checkIntervalInDist, (objColMax.z - objColMin.z) / 2.0f); checkIntervalInDist = Min(checkIntervalInDist, (adjustedColMax.x - adjustedColMin.x) / 2.0f); if (objMat.GetUp().z < 0.0f) objUpsideDown = true; if (obj->GetModelIndex() != MI_TRAFFICLIGHTS && obj->GetModelIndex() != MI_SINGLESTREETLIGHTS1 && obj->GetModelIndex() != MI_SINGLESTREETLIGHTS2) { objColCenter = obj->GetMatrix() * objColCenter; } else { checkIntervalInDist = 0.4f; if (objMat.GetUp().z <= 0.57f) { // Specific calculations for traffic lights, didn't get a bit. adjustedColMin.x = 1.2f * (adjustedColMin.x < adjustedColMin.y ? adjustedColMin.x : adjustedColMin.y); adjustedColMax.x = 1.2f * (adjustedColMax.x > adjustedColMax.y ? adjustedColMax.x : adjustedColMax.y); adjustedColMin.y = 1.2f * objColMin.z; adjustedColMax.y = 1.2f * objColMax.z; dirToSet = objMat.GetUp().Heading(); objMat.SetUnity(); objMat.RotateZ(dirToSet); objMat.GetPosition() += obj->GetPosition(); objColCenter = obj->GetPosition(); } else { objColCenter.x = adjustedColMax.x - 0.25f; objColCenter = obj->GetMatrix() * objColCenter; distLimitForTimer = 0.75f; } objUpsideDown = false; } float oldRotDest = m_fRotationDest; float angleToFaceObjCenter = (objColCenter - GetPosition()).Heading(); float angleDiffBtwObjCenterAndForward = CGeneral::LimitRadianAngle(dirToSet - angleToFaceObjCenter); float objTopRightHeading = Atan2(adjustedColMax.x - adjustedColMin.x, adjustedColMax.y - adjustedColMin.y); if (IsPlayer()) { if (FindPlayerPed()->m_fMoveSpeed <= 0.0f) checkIntervalInTime = 0.0f; else checkIntervalInTime = 2.0f / FindPlayerPed()->m_fMoveSpeed; } else { switch (m_nMoveState) { case PEDMOVE_WALK: checkIntervalInTime = 2.0f; break; case PEDMOVE_RUN: checkIntervalInTime = 0.5f; break; case PEDMOVE_SPRINT: checkIntervalInTime = 0.5f; break; default: checkIntervalInTime = 0.0f; break; } } if (m_pSeekTarget == obj && obj->IsVehicle()) { if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || m_objective == OBJECTIVE_SOLICIT_VEHICLE) { goingToEnterCar = true; if (IsPlayer()) checkIntervalInTime = 0.0f; if (((CVehicle*)obj)->bIsVan) goingToEnterCarAndItsVan = true; } } int entityOnTopLeftOfObj = 0; int entityOnBottomLeftOfObj = 0; int entityOnTopRightOfObj = 0; int entityOnBottomRightOfObj = 0; if (CTimer::GetTimeInMilliseconds() > m_collidingThingTimer || m_collidingEntityWhileFleeing != obj) { bool collidingThingChanged = true; CEntity *obstacle; if (!obj->IsVehicle() || objUpsideDown) { collidingThingChanged = false; } else { float adjustedCheckInterval = 0.7f * checkIntervalInDist; CVector posToCheck; // Top left of obj posToCheck.x = adjustedColMin.x + adjustedCheckInterval; posToCheck.y = adjustedColMax.y - adjustedCheckInterval; posToCheck.z = 0.0f; posToCheck = objMat * posToCheck; posToCheck.z += 0.6f; obstacle = CWorld::TestSphereAgainstWorld(posToCheck, checkIntervalInDist, obj, true, true, false, true, false, false); if (obstacle) { if (obstacle->IsBuilding()) { entityOnTopLeftOfObj = 1; } else if (obstacle->IsVehicle()) { entityOnTopLeftOfObj = 2; } else { entityOnTopLeftOfObj = 3; } } // Top right of obj posToCheck.x = adjustedColMax.x - adjustedCheckInterval; posToCheck.y = adjustedColMax.y - adjustedCheckInterval; posToCheck.z = 0.0f; posToCheck = objMat * posToCheck; posToCheck.z += 0.6f; obstacle = CWorld::TestSphereAgainstWorld(posToCheck, checkIntervalInDist, obj, true, true, false, true, false, false); if (obstacle) { if (obstacle->IsBuilding()) { entityOnTopRightOfObj = 1; } else if (obstacle->IsVehicle()) { entityOnTopRightOfObj = 2; } else { entityOnTopRightOfObj = 3; } } // Bottom right of obj posToCheck.x = adjustedColMax.x - adjustedCheckInterval; posToCheck.y = adjustedColMin.y + adjustedCheckInterval; posToCheck.z = 0.0f; posToCheck = objMat * posToCheck; posToCheck.z += 0.6f; obstacle = CWorld::TestSphereAgainstWorld(posToCheck, checkIntervalInDist, obj, true, true, false, true, false, false); if (obstacle) { if (obstacle->IsBuilding()) { entityOnBottomRightOfObj = 1; } else if (obstacle->IsVehicle()) { entityOnBottomRightOfObj = 2; } else { entityOnBottomRightOfObj = 3; } } // Bottom left of obj posToCheck.x = adjustedColMin.x + adjustedCheckInterval; posToCheck.y = adjustedColMin.y + adjustedCheckInterval; posToCheck.z = 0.0f; posToCheck = objMat * posToCheck; posToCheck.z += 0.6f; obstacle = CWorld::TestSphereAgainstWorld(posToCheck, checkIntervalInDist, obj, true, true, false, true, false, false); if (obstacle) { if (obstacle->IsBuilding()) { entityOnBottomLeftOfObj = 1; } else if (obstacle->IsVehicle()) { entityOnBottomLeftOfObj = 2; } else { entityOnBottomLeftOfObj = 3; } } } if (!entityOnTopLeftOfObj && !entityOnTopRightOfObj) { CVector topLeftCorner(adjustedColMin.x - 0.3f, adjustedColMax.y + 0.3f, 0.0f); topLeftCorner = objMat * topLeftCorner; CVector topRightCorner(adjustedColMax.x + 0.3f, adjustedColMax.y + 0.3f, 0.0f); topRightCorner = objMat * topRightCorner; CColPoint foundCol; CEntity *foundEnt; if (CWorld::ProcessLineOfSight(topLeftCorner, topRightCorner, foundCol, foundEnt, true, true, false, true, false, false, false, false)) { switch (foundEnt->GetType()) { case ENTITY_TYPE_VEHICLE: entityOnTopRightOfObj = 2; entityOnTopLeftOfObj = 2; break; case ENTITY_TYPE_BUILDING: entityOnTopRightOfObj = 1; entityOnTopLeftOfObj = 1; break; case ENTITY_TYPE_OBJECT: entityOnTopRightOfObj = 3; entityOnTopLeftOfObj = 3; break; } } } if (!entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { CVector bottomRightCorner(adjustedColMax.x + 0.3f, adjustedColMin.y - 0.3f, 0.0f); bottomRightCorner = objMat * bottomRightCorner; CVector bottomLeftCorner(adjustedColMin.x - 0.3f, adjustedColMin.y - 0.3f, 0.0f); bottomLeftCorner = objMat * bottomLeftCorner; CColPoint foundCol; CEntity* foundEnt; if (CWorld::ProcessLineOfSight(bottomRightCorner, bottomLeftCorner, foundCol, foundEnt, true, true, false, true, false, false, false, false)) { switch (foundEnt->GetType()) { case ENTITY_TYPE_VEHICLE: entityOnBottomLeftOfObj = 2; entityOnBottomRightOfObj = 2; break; case ENTITY_TYPE_BUILDING: entityOnBottomLeftOfObj = 1; entityOnBottomRightOfObj = 1; break; case ENTITY_TYPE_OBJECT: entityOnBottomLeftOfObj = 3; entityOnBottomRightOfObj = 3; break; } } } if (entityOnTopLeftOfObj && entityOnTopRightOfObj && entityOnBottomRightOfObj && entityOnBottomLeftOfObj) { collidingThingChanged = false; entityOnTopLeftOfObj = 0; entityOnBottomLeftOfObj = 0; entityOnTopRightOfObj = 0; entityOnBottomRightOfObj = 0; } if (!collidingThingChanged) { m_walkAroundType = 0; } else { if (Abs(angleDiffBtwObjCenterAndForward) >= objTopRightHeading) { if (PI - objTopRightHeading >= Abs(angleDiffBtwObjCenterAndForward)) { if ((angleDiffBtwObjCenterAndForward <= 0.0f || objUpsideDown) && (angleDiffBtwObjCenterAndForward < 0.0f || !objUpsideDown)) { if (goingToEnterCar && (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR)) { m_walkAroundType = 0; } else { if (CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) >= 0.0f) { if (entityOnBottomRightOfObj == 1 || entityOnBottomRightOfObj && !entityOnTopLeftOfObj && !entityOnTopRightOfObj) { m_walkAroundType = 1; } else if (entityOnBottomLeftOfObj == 1 || entityOnBottomLeftOfObj && !entityOnTopLeftOfObj && !entityOnTopRightOfObj) { m_walkAroundType = 1; } } else { if (entityOnTopRightOfObj == 1 || entityOnTopRightOfObj && !entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { m_walkAroundType = 4; } else if (entityOnTopLeftOfObj == 1 || entityOnTopLeftOfObj && !entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { m_walkAroundType = 4; } } } } else { if (goingToEnterCar && (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR)) { m_walkAroundType = 0; } else { if (CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) <= 0.0f) { if (entityOnBottomLeftOfObj == 1 || entityOnBottomLeftOfObj && !entityOnTopLeftOfObj && !entityOnTopRightOfObj) { m_walkAroundType = 2; } else if (entityOnBottomRightOfObj == 1 || entityOnBottomRightOfObj && !entityOnTopLeftOfObj && !entityOnTopRightOfObj) { m_walkAroundType = 2; } } else { if (entityOnTopLeftOfObj == 1 || entityOnTopLeftOfObj && !entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { m_walkAroundType = 3; } else if (entityOnTopRightOfObj == 1 || entityOnTopRightOfObj && !entityOnBottomRightOfObj && !entityOnBottomLeftOfObj) { m_walkAroundType = 3; } } } } } else if (goingToEnterCar && (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) || CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) < 0.0f) { if (entityOnTopLeftOfObj == 1 || entityOnTopLeftOfObj && !entityOnTopRightOfObj && !entityOnBottomRightOfObj) { m_walkAroundType = 3; } } else if (entityOnTopRightOfObj == 1 || entityOnTopRightOfObj && !entityOnTopLeftOfObj && !entityOnBottomLeftOfObj) { m_walkAroundType = 4; } } else if (goingToEnterCar && (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) || CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) > 0.0f) { if (entityOnBottomLeftOfObj == 1 || entityOnBottomLeftOfObj && !entityOnTopRightOfObj && !entityOnBottomRightOfObj) { m_walkAroundType = 2; } } else if (entityOnBottomRightOfObj == 1 || entityOnBottomRightOfObj && !entityOnTopLeftOfObj && !entityOnBottomLeftOfObj) { m_walkAroundType = 1; } else { m_walkAroundType = 0; } } } m_collidingEntityWhileFleeing = obj; m_collidingEntityWhileFleeing->RegisterReference((CEntity**) &m_collidingEntityWhileFleeing); // TODO: This random may need to be changed. m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 512 + CGeneral::GetRandomNumber(); CVector localPosToHead; if (Abs(angleDiffBtwObjCenterAndForward) < objTopRightHeading) { if (goingToEnterCar) { if (goingToEnterCarAndItsVan) { if (m_vehDoor == CAR_DOOR_LR || m_vehDoor == CAR_DOOR_RR) return; } if (m_vehDoor != CAR_DOOR_LF && m_vehDoor != CAR_DOOR_LR && (!entityOnBottomRightOfObj || entityOnBottomLeftOfObj)) { m_fRotationDest = CGeneral::LimitRadianAngle(dirToSet - HALFPI); localPosToHead.x = adjustedColMax.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMin.y; } else { m_fRotationDest = CGeneral::LimitRadianAngle(HALFPI + dirToSet); localPosToHead.x = adjustedColMin.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMin.y; } } else { if (m_walkAroundType != 1 && m_walkAroundType != 4 && (m_walkAroundType || CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) <= 0.0f)) { m_fRotationDest = CGeneral::LimitRadianAngle(dirToSet - HALFPI); localPosToHead.x = adjustedColMax.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMin.y; } else { m_fRotationDest = CGeneral::LimitRadianAngle(HALFPI + dirToSet); localPosToHead.x = adjustedColMin.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMin.y; } } } else { if (PI - objTopRightHeading >= Abs(angleDiffBtwObjCenterAndForward)) { if (angleDiffBtwObjCenterAndForward <= 0.0f) { if (!goingToEnterCar || !goingToEnterCarAndItsVan || m_vehDoor != CAR_DOOR_LR && m_vehDoor != CAR_DOOR_RR) { if (goingToEnterCar) { if (m_vehDoor == CAR_DOOR_RF || (m_vehDoor == CAR_DOOR_RR && !goingToEnterCarAndItsVan)) return; } if (m_walkAroundType == 4 || m_walkAroundType == 3 || !m_walkAroundType && CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) > 0.0f) { m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); localPosToHead.x = adjustedColMax.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMin.y; } else { m_fRotationDest = dirToSet; localPosToHead.x = adjustedColMax.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMax.y; } } else { m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); localPosToHead.x = adjustedColMax.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMin.y; } } else if (goingToEnterCar && goingToEnterCarAndItsVan && (m_vehDoor == CAR_DOOR_LR || m_vehDoor == CAR_DOOR_RR)) { m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); localPosToHead.x = adjustedColMin.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMin.y; } else { if (goingToEnterCar) { if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR && !goingToEnterCarAndItsVan) return; } if (m_walkAroundType == 1 || m_walkAroundType == 2 || !m_walkAroundType && CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) > 0.0f) { m_fRotationDest = dirToSet; localPosToHead.x = adjustedColMin.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMax.y; } else { m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); localPosToHead.x = adjustedColMin.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMin.y; } } } else { if (goingToEnterCar && (!goingToEnterCarAndItsVan || m_vehDoor != CAR_DOOR_LR && m_vehDoor != CAR_DOOR_RR)) { if (m_vehDoor != CAR_DOOR_LF && m_vehDoor != CAR_DOOR_LR && (!entityOnTopRightOfObj || entityOnTopLeftOfObj)) { m_fRotationDest = CGeneral::LimitRadianAngle(dirToSet - HALFPI); localPosToHead.x = adjustedColMax.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMax.y; } else { m_fRotationDest = CGeneral::LimitRadianAngle(HALFPI + dirToSet); localPosToHead.x = adjustedColMin.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMax.y; } } else { if (m_walkAroundType == 2 || m_walkAroundType == 3 || !m_walkAroundType && CGeneral::LimitRadianAngle(m_fRotationDest - angleToFaceObjCenter) > 0.0f) { m_fRotationDest = CGeneral::LimitRadianAngle(dirToSet - HALFPI); localPosToHead.x = adjustedColMax.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMax.y; } else { m_fRotationDest = CGeneral::LimitRadianAngle(HALFPI + dirToSet); localPosToHead.x = adjustedColMin.x; localPosToHead.z = 0.0f; localPosToHead.y = adjustedColMax.y; } } } } if (objUpsideDown) localPosToHead.x = localPosToHead.x * -1.0f; localPosToHead = objMat * localPosToHead; m_actionX = localPosToHead.x; m_actionY = localPosToHead.y; localPosToHead -= GetPosition(); m_fRotationDest = CGeneral::LimitRadianAngle(localPosToHead.Heading()); if (m_fRotationDest != m_fRotationCur && bHitSomethingLastFrame) { if (m_fRotationDest == oldRotDest) { m_fRotationDest = oldRotDest; } else { m_fRotationDest = CGeneral::LimitRadianAngle(PI + dirToSet); } } float dist = localPosToHead.Magnitude2D(); if (dist < 0.5f) dist = 0.5f; if (dist > distLimitForTimer) dist = distLimitForTimer; m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 280.0f * dist * checkIntervalInTime; } bool CPed::IsPedInControl(void) { return m_nPedState <= PED_STATES_NO_AI && !bIsInTheAir && !bIsLanding && m_fHealth > 0.0f; } bool CPed::IsPedShootable(void) { return m_nPedState <= PED_STATES_NO_ST; } bool CPed::UseGroundColModel(void) { return m_nPedState == PED_FALL || m_nPedState == PED_DIVE_AWAY || m_nPedState == PED_DIE || m_nPedState == PED_DEAD; } bool CPed::CanPedReturnToState(void) { return m_nPedState <= PED_STATES_NO_AI && m_nPedState != PED_AIM_GUN && m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT && m_nPedState != PED_STEP_AWAY && m_nPedState != PED_SNIPER_MODE && m_nPedState != PED_LOOK_ENTITY; } bool CPed::CanSetPedState(void) { return !DyingOrDead() && m_nPedState != PED_ARRESTED && !EnteringCar() && m_nPedState != PED_STEAL_CAR; } bool CPed::CanStrafeOrMouseControl(void) { #ifdef FREE_CAM if (CCamera::bFreeCam) return false; #endif return m_nPedState == PED_NONE || m_nPedState == PED_IDLE || m_nPedState == PED_FLEE_POS || m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_ATTACK || m_nPedState == PED_FIGHT || m_nPedState == PED_AIM_GUN || m_nPedState == PED_JUMP || m_nPedState == PED_ANSWER_MOBILE; } void CPed::PedSetPreviousStateCB(CAnimBlendAssociation* assoc, void* arg) { CPed* ped = (CPed*)arg; ped->RestorePreviousState(); ped->m_pVehicleAnim = nil; } void CPed::PedGetupCB(CAnimBlendAssociation* animAssoc, void* arg) { CPed* ped = (CPed*)arg; if (ped->m_nPedState == PED_GETUP) RpAnimBlendClumpSetBlendDeltas(ped->GetClump(), ASSOC_PARTIAL, -1000.0f); ped->bFallenDown = false; animAssoc->blendDelta = -1000.0f; if (ped->m_nPedState == PED_GETUP) ped->RestorePreviousState(); if (ped->bFleeWhenStanding && ped->m_threatEx) { ped->SetFlee(ped->m_threatEx, 10000); ped->Say(SOUND_PED_FLEE_SPRINT); ped->bFleeWhenStanding = false; ped->m_threatEx = nil; } else if (ped->bGotUpOfMyOwnAccord) { ped->SetObjective(OBJECTIVE_NONE); ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.f, 8.f)); ped->bGotUpOfMyOwnAccord = false; } else { if (ped->m_nPedState != PED_FLEE_POS && ped->m_nPedState != PED_FLEE_ENTITY) ped->SetMoveState(PEDMOVE_STILL); else ped->SetMoveState(PEDMOVE_RUN); ped->SetMoveAnim(); } ped->bGetUpAnimStarted = false; } void CPed::PedLandCB(CAnimBlendAssociation* animAssoc, void* arg) { CPed* ped = (CPed*)arg; animAssoc->blendDelta = -1000.0f; ped->bIsLanding = false; if (ped->m_nPedState == PED_JUMP) ped->RestorePreviousState(); } void CPed::PedStaggerCB(CAnimBlendAssociation* animAssoc, void* arg) { /* CPed *ped = (CPed*)arg; if (ped->m_nPedState == PED_STAGGER) // nothing */ } void CPed::PedSetOutCarCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; CVehicle *veh = ped->m_pMyVehicle; bool startedToRun = false; ped->bUsesCollision = true; ped->m_actionX = 0.0f; ped->m_actionY = 0.0f; ped->bVehExitWillBeInstant = false; if (veh && veh->IsBoat()) ped->ApplyMoveSpeed(); if (ped->m_objective == OBJECTIVE_LEAVE_CAR) ped->RestorePreviousObjective(); else if (ped->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) { ped->m_fHealth = 0.0f; ped->SetDie(ANIM_STD_HIT_FLOOR, 4.0f, 0.5f); } ped->bInVehicle = false; if (veh && (veh->IsCar() || veh->IsBike())) { CWorld::pIgnoreEntity = veh; if (CWorld::TestSphereAgainstWorld(ped->GetPosition() - CVector(0.f, 0.f, 0.2f), 0.4f, veh, true, true, false, false, false, false) || CWorld::TestSphereAgainstWorld(ped->GetPosition() + CVector(0.f, 0.f, 0.2f), 0.4f, veh, true, true, false, false, false, false) || !CWorld::GetIsLineOfSightClear(veh->GetPosition(), ped->GetPosition(), true, false, false, true, false, false, false)) { CWorld::pIgnoreEntity = nil; ped->PositionPedOutOfCollision(); } CWorld::pIgnoreEntity = nil; } if (ped->m_nPedState == PED_EXIT_CAR) { if (ped->m_nPedType == PEDTYPE_COP) { ped->SetIdle(); if (((CCopPed*)ped)->m_nCopType == COP_MIAMIVICE && ped->m_pMyVehicle && ped->m_pMyVehicle->pDriver == ped) { DMAudio.PlayOneShot(ped->m_audioEntityId, SOUND_PED_MIAMIVICE_EXITING_CAR, 0.f); } } else ped->RestorePreviousState(); veh = ped->m_pMyVehicle; if (ped->bFleeAfterExitingCar && veh) { ped->bFleeAfterExitingCar = false; ped->SetFlee(veh->GetPosition(), 12000); ped->bUsePedNodeSeek = true; ped->m_pNextPathNode = nil; if (CGeneral::GetRandomNumber() & 1 || ped->m_pedStats->m_fear > 70) { ped->SetMoveState(PEDMOVE_SPRINT); ped->Say(SOUND_PED_FLEE_SPRINT); } else { ped->SetMoveState(PEDMOVE_RUN); ped->Say(SOUND_PED_FLEE_RUN); } startedToRun = true; // This is not a good way to do this... ped->m_nLastPedState = PED_WANDER_PATH; } else if (ped->bWanderPathAfterExitingCar) { ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); ped->bWanderPathAfterExitingCar = false; if (ped->m_nPedType == PEDTYPE_PROSTITUTE) ped->SetObjectiveTimer(30000); ped->m_nLastPedState = PED_NONE; } else if (ped->bGonnaKillTheCarJacker) { // Kill objective is already given at this point. ped->bGonnaKillTheCarJacker = false; if (ped->m_pedInObjective) { if (!(CGeneral::GetRandomNumber() & 1) && ped->m_nPedType != PEDTYPE_COP && (!ped->m_pedInObjective->IsPlayer() || !CTheScripts::IsPlayerOnAMission())) { ped->ClearObjective(); ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, veh); } ped->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 1500; } int waitTime = 1500; ped->SetWaitState(WAITSTATE_PLAYANIM_COWER, &waitTime); ped->SetMoveState(PEDMOVE_RUN); startedToRun = true; } else if (ped->m_objective == OBJECTIVE_NONE && ped->CharCreatedBy != MISSION_CHAR && ped->m_nPedState == PED_IDLE && !ped->IsPlayer()) { ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); } } else if (ped->m_nPedState == PED_DRIVING) { ped->m_nPedState = PED_IDLE; } if (animAssoc) animAssoc->blendDelta = -1000.0f; ped->RestartNonPartialAnims(); ped->m_pVehicleAnim = nil; ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); veh = ped->m_pMyVehicle; if (veh) { if (ped->m_nPedType == PEDTYPE_PROSTITUTE) { if (veh->pDriver) { if (veh->pDriver->IsPlayer() && ped->CharCreatedBy == RANDOM_CHAR) { CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime = 0; CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = 0; CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil; CWorld::Players[CWorld::PlayerInFocus].m_nMoney -= 100; if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney < 0) CWorld::Players[CWorld::PlayerInFocus].m_nMoney = 0; } } } if (veh && veh->IsBike()) // BUG? veh->m_nGettingOutFlags &= ~GetBikeDoorFlag(ped->m_vehDoor); else veh->m_nGettingOutFlags &= ~GetCarDoorFlag(ped->m_vehDoor); if (veh->pDriver == ped) { veh->RemoveDriver(); #ifndef FIX_BUGS // RemoveDriver does it anyway veh->SetStatus(STATUS_ABANDONED); #endif if (veh->m_nDoorLock == CARLOCK_LOCKED_INITIALLY) veh->m_nDoorLock = CARLOCK_UNLOCKED; if (ped->m_nPedType == PEDTYPE_COP && veh->IsLawEnforcementVehicle()) veh->ChangeLawEnforcerState(false); if (veh->IsBike()) { if (Abs(veh->m_vecMoveSpeed.x) < 0.1 && Abs(veh->m_vecMoveSpeed.y) < 0.1f) { ((CBike*)veh)->bIsStanding = true; } } } else { veh->RemovePassenger(ped); } if (veh->bIsBus && !veh->IsUpsideDown() && !veh->IsOnItsSide()) { float angleAfterExit; if (ped->m_vehDoor == CAR_DOOR_LF) { angleAfterExit = HALFPI + veh->GetForward().Heading(); } else { angleAfterExit = veh->GetForward().Heading() - HALFPI; } ped->SetHeading(angleAfterExit); ped->m_fRotationDest = angleAfterExit; ped->m_fRotationCur = angleAfterExit; if (!ped->bBusJacked) ped->SetMoveState(PEDMOVE_WALK); } if (CGarages::IsPointWithinAnyGarage(ped->GetPosition())) veh->bLightsOn = false; } if (ped->IsPlayer()) AudioManager.PlayerJustLeftCar(); ped->ReplaceWeaponWhenExitingVehicle(); ped->bOnBoat = false; if (ped->bBusJacked) { ped->SetFall(1500, ANIM_STD_HIGHIMPACT_BACK, false); ped->bBusJacked = false; } ped->m_nStoredMoveState = PEDMOVE_NONE; if (!ped->IsPlayer()) { // It's a shame... #ifdef FIX_BUGS int createdBy = ped->CharCreatedBy; #else int createdBy = !ped->CharCreatedBy; #endif if (createdBy == MISSION_CHAR && !startedToRun) ped->SetMoveState(PEDMOVE_WALK); } ped->bHeldHostageInCar = false; } void CPed::PedSetDraggedOutCarCB(CAnimBlendAssociation *dragAssoc, void *arg) { CAnimBlendAssociation *quickJackedAssoc; CVehicle *vehicle; CPed *ped = (CPed*)arg; uint8 exitFlags = 0; quickJackedAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_STD_QUICKJACKED); if (dragAssoc && dragAssoc->animId == ANIM_BIKE_HIT && ped->m_pMyVehicle) { if (ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_RF) { CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_FALLOFF, 100.0f); ped->m_pMyVehicle->m_nGettingOutFlags &= ~(CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF); } else { CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_FALLBACK, 100.0f); ped->m_pMyVehicle->m_nGettingOutFlags &= ~(CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR); } ((CBike*)ped->m_pMyVehicle)->KnockOffRider(WEAPONTYPE_UNIDENTIFIED, 0, ped, true); return; } if (ped->m_nPedState != PED_ARRESTED) { ped->m_nLastPedState = PED_NONE; if (dragAssoc) dragAssoc->blendDelta = -1000.0f; } ped->RestartNonPartialAnims(); ped->m_pVehicleAnim = nil; ped->m_pSeekTarget = nil; vehicle = ped->m_pMyVehicle; if (vehicle && vehicle->IsBike()) exitFlags = GetBikeDoorFlagInclJumpInFromFront(ped->m_vehDoor); else exitFlags = GetCarDoorFlag(ped->m_vehDoor); if (vehicle) vehicle->m_nGettingOutFlags &= ~exitFlags; if (vehicle) { if (vehicle->pDriver == ped) { vehicle->RemoveDriver(); if (vehicle->m_nDoorLock == CARLOCK_LOCKED_INITIALLY) vehicle->m_nDoorLock = CARLOCK_UNLOCKED; if (ped->m_nPedType == PEDTYPE_COP && vehicle->IsLawEnforcementVehicle()) vehicle->ChangeLawEnforcerState(false); } else { vehicle->RemovePassenger(ped); } } ped->bInVehicle = false; if (ped->IsPlayer()) AudioManager.PlayerJustLeftCar(); if (ped->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) { dragAssoc->SetDeleteCallback(PedSetDraggedOutCarPositionCB, ped); ped->m_fHealth = 0.0f; ped->SetDie(ANIM_STD_HIT_FLOOR, 1000.0f, 0.5f); return; } if (quickJackedAssoc) { dragAssoc->SetDeleteCallback(PedSetQuickDraggedOutCarPositionCB, ped); } else { dragAssoc->SetDeleteCallback(PedSetDraggedOutCarPositionCB, ped); if (ped->CanSetPedState()) CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP, 1000.0f); } ped->ReplaceWeaponWhenExitingVehicle(); ped->m_nStoredMoveState = PEDMOVE_NONE; ped->bVehExitWillBeInstant = false; } void CPed::PedSetInCarCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; CVehicle *veh = ped->m_pMyVehicle; // Pointless code if (!veh) return; // Situation of entering car as a driver while there is already a driver exiting atm. CPed *driver = veh->pDriver; if (driver && driver->m_nPedState == PED_DRIVING && !veh->bIsBus && driver->m_objective == OBJECTIVE_LEAVE_CAR && (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK)) { if (!ped->IsPlayer() && (ped->CharCreatedBy != MISSION_CHAR || driver->IsPlayer())) { ped->QuitEnteringCar(); return; } if (driver->CharCreatedBy == MISSION_CHAR) { PedSetOutCarCB(nil, veh->pDriver); if (driver->m_pMyVehicle) { driver->PositionPedOutOfCollision(); } else { driver->m_pMyVehicle = veh; driver->PositionPedOutOfCollision(); driver->m_pMyVehicle = nil; } veh->pDriver = nil; } else { driver->SetDead(); driver->FlagToDestroyWhenNextProcessed(); veh->pDriver = nil; } } if (ped->bRemoveMeWhenIGotIntoCar) { ped->bRemoveMeWhenIGotIntoCar = false; ped->bRemoveFromWorld = true; } if (ped->bCollectBusFare) { ped->bCollectBusFare = false; if (FindPlayerPed()) FindPlayerPed()->m_nLastBusFareCollected += 5; } if (!ped->IsNotInWreckedVehicle() || ped->DyingOrDead()) return; ped->bInVehicle = true; if (ped->m_nPedType == PEDTYPE_PROSTITUTE) { if (veh->pDriver) { if (veh->pDriver->IsPlayer() && ped->CharCreatedBy == RANDOM_CHAR) { CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency = 1000; CWorld::Players[CWorld::PlayerInFocus].m_nNextSexMoneyUpdateTime = CTimer::GetTimeInMilliseconds() + 1000; CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000; CWorld::Players[CWorld::PlayerInFocus].m_pHooker = (CCivilianPed*)ped; } } } if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK) veh->bIsBeingCarJacked = false; if (veh->m_nNumGettingIn) --veh->m_nNumGettingIn; if (ped->IsPlayer() && ((CPlayerPed*)ped)->m_bAdrenalineActive) ((CPlayerPed*)ped)->ClearAdrenaline(); if (veh->IsBoat()) { if (ped->IsPlayer()) { CCarCtrl::RegisterVehicleOfInterest(veh); if (veh->GetStatus() == STATUS_SIMPLE) { veh->m_vecMoveSpeed = CVector(0.0f, 0.0f, -0.00001f); veh->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); } veh->SetStatus(STATUS_PLAYER); AudioManager.PlayerJustGotInCar(); } veh->SetDriver(ped); if (!veh->bEngineOn) veh->bEngineOn = true; ped->SetPedState(PED_DRIVING); ped->StopNonPartialAnims(); ped->RemoveWeaponWhenEnteringVehicle(); return; } if (ped->m_pVehicleAnim) ped->m_pVehicleAnim->blendDelta = -1000.0f; ped->bDoBloodyFootprints = false; if (veh->m_nAlarmState == -1) veh->m_nAlarmState = 15000; if (ped->IsPlayer()) { if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || veh->IsBike()) { if (veh->GetStatus() == STATUS_SIMPLE) { veh->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); veh->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); } veh->SetStatus(STATUS_PLAYER); } AudioManager.PlayerJustGotInCar(); } else if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { if (veh->GetStatus() == STATUS_SIMPLE) { veh->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); veh->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); } veh->SetStatus(STATUS_PHYSICS); } if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { for (int i = 0; i < veh->m_nNumMaxPassengers; ++i) { CPed *passenger = veh->pPassengers[i]; if (passenger && !passenger->bStayInCarOnJack && !passenger->bHeldHostageInCar && (passenger->m_leader != ped || !ped->bIsLeader)) { passenger->SetObjective(OBJECTIVE_LEAVE_CAR, veh); passenger->m_leaveCarTimer = CTimer::GetTimeInMilliseconds(); } } } if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK) { veh->SetDriver(ped); if (veh->VehicleCreatedBy == PARKED_VEHICLE) { veh->VehicleCreatedBy = RANDOM_VEHICLE; ++CCarCtrl::NumRandomCars; --CCarCtrl::NumParkedCars; } if (veh->bIsAmbulanceOnDuty) { veh->bIsAmbulanceOnDuty = false; --CCarCtrl::NumAmbulancesOnDuty; } if (veh->bIsFireTruckOnDuty) { veh->bIsFireTruckOnDuty = false; --CCarCtrl::NumFiretrucksOnDuty; } if (ped->m_nPedType == PEDTYPE_COP && veh->IsLawEnforcementVehicle()) veh->ChangeLawEnforcerState(true); if (!veh->bEngineOn) { veh->bEngineOn = true; DMAudio.PlayOneShot(ped->m_audioEntityId, SOUND_CAR_ENGINE_START, 1.0f); } if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && ped->CharCreatedBy == RANDOM_CHAR && ped != FindPlayerPed() && ped->m_nPedType != PEDTYPE_EMERGENCY) { CCarCtrl::JoinCarWithRoadSystem(veh); veh->AutoPilot.m_nCarMission = MISSION_CRUISE; veh->AutoPilot.m_nTempAction = TEMPACT_NONE; veh->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; veh->AutoPilot.m_nCruiseSpeed = 25; } ped->SetPedState(PED_DRIVING); if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { if (ped->m_prevObjective == OBJECTIVE_RUN_TO_AREA || ped->m_prevObjective == OBJECTIVE_GOTO_CHAR_ON_FOOT || ped->m_prevObjective == OBJECTIVE_SPRINT_TO_AREA || ped->m_prevObjective == OBJECTIVE_KILL_CHAR_ON_FOOT) ped->m_prevObjective = OBJECTIVE_NONE; ped->RestorePreviousObjective(); } } else { bool slowDown = false; if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && veh->pDriver && ped->CharCreatedBy == RANDOM_CHAR) slowDown = true; // VC also has a dead condition in here. if (veh->IsBike()) { veh->AddPassenger(ped, 0); } else if (veh->bIsBus) { veh->AddPassenger(ped); } else { switch (ped->m_vehDoor) { case CAR_DOOR_RF: veh->AddPassenger(ped, 0); break; case CAR_DOOR_RR: veh->AddPassenger(ped, 2); break; case CAR_DOOR_LR: veh->AddPassenger(ped, 1); break; default: veh->AddPassenger(ped); break; } } ped->SetPedState(PED_DRIVING); if (ped->m_prevObjective == OBJECTIVE_RUN_TO_AREA || ped->m_prevObjective == OBJECTIVE_GOTO_CHAR_ON_FOOT || ped->m_prevObjective == OBJECTIVE_SPRINT_TO_AREA || ped->m_prevObjective == OBJECTIVE_KILL_CHAR_ON_FOOT) ped->m_prevObjective = OBJECTIVE_NONE; ped->RestorePreviousObjective(); // VC has conditional OBJECTIVE_LEAVE_CAR here, which runs if it entered the first dead condition. if(slowDown) veh->AutoPilot.m_nCruiseSpeed = 17; } int8 doorFlag; if (veh->IsBike()) { doorFlag = GetBikeDoorFlagInclJumpInFromFront(ped->m_vehDoor); } else { doorFlag = GetEnterCarDoorFlag(ped->m_vehDoor, veh->m_nNumMaxPassengers); } veh->m_nGettingInFlags &= ~doorFlag; if (veh->bIsBus && !veh->m_nGettingInFlags) ((CAutomobile*)veh)->SetBusDoorTimer(1000, 1); switch (ped->m_objective) { case OBJECTIVE_KILL_CHAR_ON_FOOT: case OBJECTIVE_KILL_CHAR_ANY_MEANS: case OBJECTIVE_LEAVE_CAR: case OBJECTIVE_FOLLOW_CAR_IN_CAR: case OBJECTIVE_GOTO_AREA_ANY_MEANS: case OBJECTIVE_GOTO_AREA_ON_FOOT: case OBJECTIVE_RUN_TO_AREA: case OBJECTIVE_GOTO_SEAT_ON_FOOT: case OBJECTIVE_GOTO_ATM_ON_FOOT: case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: case OBJECTIVE_GOTO_PIZZA_ON_FOOT: case OBJECTIVE_GOTO_SHELTER_ON_FOOT: case OBJECTIVE_SPRINT_TO_AREA: case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: break; default: ped->SetObjective(OBJECTIVE_NONE); } ped->AddInCarAnims(veh, veh->pDriver == ped); if (veh->bIsBus) ped->bRenderPedInCar = false; // FIX: RegisterVehicleOfInterest not just registers the vehicle, but also updates register time. So remove the IsThisVehicleInteresting check. #ifndef FIX_BUGS if (ped->IsPlayer() && !CCarCtrl::IsThisVehicleInteresting(veh) && veh->VehicleCreatedBy != MISSION_VEHICLE) { #else if (ped->IsPlayer() && veh->VehicleCreatedBy != MISSION_VEHICLE) { #endif CCarCtrl::RegisterVehicleOfInterest(veh); if (!veh->bHasBeenOwnedByPlayer && veh->VehicleCreatedBy != MISSION_VEHICLE) CEventList::RegisterEvent(EVENT_STEAL_CAR, EVENT_ENTITY_VEHICLE, veh, ped, 1500); veh->bHasBeenOwnedByPlayer = true; } ped->bChangedSeat = true; } bool CPed::CanBeDeleted(void) { if (bInVehicle) return false; switch (CharCreatedBy) { case RANDOM_CHAR: return true; case MISSION_CHAR: return false; case UNK_CHAR: return false; default: return true; } } bool CPed::CanBeDeletedEvenInVehicle(void) { switch (CharCreatedBy) { case RANDOM_CHAR: return true; case MISSION_CHAR: return false; case UNK_CHAR: return false; default: return true; } } void CPed::AddWeaponModel(int id) { if (id != -1) { if (m_pWeaponModel) RemoveWeaponModel(-1); m_pWeaponModel = (RpAtomic*)CModelInfo::GetModelInfo(id)->CreateInstance(); CModelInfo::GetModelInfo(id)->AddRef(); m_wepModelID = id; if (IsPlayer() && id == MI_MINIGUN) ((CPlayerPed*)this)->m_pMinigunTopAtomic = (RpAtomic*)CModelInfo::GetModelInfo(MI_MINIGUN2)->CreateInstance(); } } static RwObject* RemoveAllModelCB(RwObject *object, void *data) { RpAtomic *atomic = (RpAtomic*)object; if (CVisibilityPlugins::GetAtomicModelInfo(atomic)) { RpClumpRemoveAtomic(RpAtomicGetClump(atomic), atomic); RpAtomicDestroy(atomic); } return object; } void CPed::RemoveWeaponModel(int modelId) { // modelId is not used!! This function just removes the current weapon. if(m_pWeaponModel){ if (modelId == -1 || CVisibilityPlugins::GetAtomicModelInfo(m_pWeaponModel) == CModelInfo::GetModelInfo(modelId)) { CVisibilityPlugins::GetAtomicModelInfo(m_pWeaponModel)->RemoveRef(); RwFrame* frm = RpAtomicGetFrame(m_pWeaponModel); RpAtomicDestroy(m_pWeaponModel); RwFrameDestroy(frm); m_pWeaponModel = nil; } } if (IsPlayer() && (modelId == -1 || modelId == MI_MINIGUN)) { RpAtomic* &atm = ((CPlayerPed*)this)->m_pMinigunTopAtomic; if (atm) { RwFrame *frm = RpAtomicGetFrame(atm); RpAtomicDestroy(atm); RwFrameDestroy(frm); atm = nil; } } m_wepModelID = -1; } void CPed::RequestDelayedWeapon() { if (m_delayedWeapon != WEAPONTYPE_UNIDENTIFIED) { int modelId1 = CWeaponInfo::GetWeaponInfo(m_delayedWeapon)->m_nModelId; int modelId2 = CWeaponInfo::GetWeaponInfo(m_delayedWeapon)->m_nModel2Id; if (modelId1 != -1) CStreaming::RequestModel(modelId1, STREAMFLAGS_DEPENDENCY); if (modelId2 != -1) CStreaming::RequestModel(modelId2, STREAMFLAGS_DEPENDENCY); if ((modelId1 == -1 || CStreaming::HasModelLoaded(modelId1)) && (modelId2 == -1 || CStreaming::HasModelLoaded(modelId2))) { GiveWeapon(m_delayedWeapon, m_delayedWeaponAmmo, 1); m_delayedWeapon = WEAPONTYPE_UNIDENTIFIED; } } } void CPed::GiveDelayedWeapon(eWeaponType weapon, uint32 ammo) { m_delayedWeapon = weapon; m_delayedWeaponAmmo = ammo; if (m_delayedWeapon != WEAPONTYPE_UNIDENTIFIED) { int modelId1 = CWeaponInfo::GetWeaponInfo(m_delayedWeapon)->m_nModelId; int modelId2 = CWeaponInfo::GetWeaponInfo(m_delayedWeapon)->m_nModel2Id; if (modelId1 != -1) CStreaming::RequestModel(modelId1, STREAMFLAGS_DEPENDENCY); if (modelId2 != -1) CStreaming::RequestModel(modelId2, STREAMFLAGS_DEPENDENCY); if ((modelId1 == -1 || CStreaming::HasModelLoaded(modelId1)) && (modelId2 == -1 || CStreaming::HasModelLoaded(modelId2))) { GiveWeapon(m_delayedWeapon, m_delayedWeaponAmmo, true); m_delayedWeapon = WEAPONTYPE_UNIDENTIFIED; } } } int32 CPed::GiveWeapon(eWeaponType weaponType, uint32 ammo, bool unused) { int slot = GetWeaponSlot(weaponType); if (m_weapons[slot].m_eWeaponType == weaponType) { GetWeapon(slot).m_nAmmoTotal += ammo; if (weaponType < WEAPONTYPE_TOTALWEAPONS && weaponType > WEAPONTYPE_UNARMED && CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType] >= 0) { // Looks like abandoned idea. This block never runs, ms_aMaxAmmoForWeapon is always -1. GetWeapon(slot).m_nAmmoTotal = Min(CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType], GetWeapon(slot).m_nAmmoTotal); } else { GetWeapon(slot).m_nAmmoTotal = Min(99999, GetWeapon(slot).m_nAmmoTotal); } GetWeapon(slot).Reload(); if (GetWeapon(slot).m_eWeaponState == WEAPONSTATE_OUT_OF_AMMO && GetWeapon(slot).m_nAmmoTotal > 0) GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; } else { if (HasWeaponSlot(slot)) { if (CWeaponInfo::IsWeaponSlotAmmoMergeable(slot)) ammo += GetWeapon(slot).m_nAmmoTotal; RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(GetWeapon(slot).m_eWeaponType)->m_nModelId); GetWeapon(slot).Shutdown(); } GetWeapon(slot).Initialise(weaponType, ammo); if (slot == m_currentWeapon && !bInVehicle) { AddWeaponModel(CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nModelId); } } if (GetWeapon(slot).m_eWeaponState != WEAPONSTATE_OUT_OF_AMMO) GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; return slot; } int CPed::GetWeaponSlot(eWeaponType weaponType) { return CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; } void CPed::SetCurrentWeapon(int slot) { if (slot == -1) return; CWeaponInfo* weaponInfo; if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); RemoveWeaponModel(weaponInfo->m_nModelId); } m_currentWeapon = slot; if (FindPlayerPed() && IsPlayer()) ((CPlayerPed*)this)->m_nSelectedWepSlot = m_currentWeapon; if (HasWeaponSlot(slot)) { weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); AddWeaponModel(weaponInfo->m_nModelId); } } void CPed::SetCurrentWeapon(eWeaponType weaponType) { SetCurrentWeapon(CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot); } void CPed::GrantAmmo(eWeaponType weaponType, uint32 ammo) { int slot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; if (slot == -1) return; GetWeapon(slot).m_nAmmoTotal += ammo; if (weaponType < WEAPONTYPE_TOTALWEAPONS && weaponType > WEAPONTYPE_UNARMED && CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType] >= 0) { // Looks like abandoned idea. This block never runs, ms_aMaxAmmoForWeapon is always -1. GetWeapon(slot).m_nAmmoTotal = Min(CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType], GetWeapon(slot).m_nAmmoTotal); } else { GetWeapon(slot).m_nAmmoTotal = Min(99999, GetWeapon(slot).m_nAmmoTotal); } if (GetWeapon(slot).m_eWeaponState == WEAPONSTATE_OUT_OF_AMMO && GetWeapon(slot).m_nAmmoTotal > 0) GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; } void CPed::SetAmmo(eWeaponType weaponType, uint32 ammo) { int slot = CWeaponInfo::GetWeaponInfo(weaponType)->m_nWeaponSlot; if (slot == -1) return; GetWeapon(slot).m_nAmmoTotal = ammo; if (weaponType < WEAPONTYPE_TOTALWEAPONS && weaponType > WEAPONTYPE_UNARMED && CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType] >= 0) { // Looks like abandoned idea. This block never runs, ms_aMaxAmmoForWeapon is always -1. GetWeapon(slot).m_nAmmoTotal = Min(CWeaponInfo::ms_aMaxAmmoForWeapon[weaponType], GetWeapon(slot).m_nAmmoTotal); } else { GetWeapon(slot).m_nAmmoTotal = Min(99999, GetWeapon(slot).m_nAmmoTotal); } int32 newClip = GetWeapon(slot).m_nAmmoTotal; if (newClip >= GetWeapon(slot).m_nAmmoInClip) newClip = GetWeapon(slot).m_nAmmoInClip; GetWeapon(slot).m_nAmmoInClip = newClip; if (GetWeapon(slot).m_eWeaponState == WEAPONSTATE_OUT_OF_AMMO && GetWeapon(slot).m_nAmmoTotal > 0) GetWeapon(slot).m_eWeaponState = WEAPONSTATE_READY; } void CPed::ClearWeapons(void) { RemoveWeaponModel(-1); for (int i = 0; i < ARRAY_SIZE(m_weapons); i++) { GetWeapon(i).Shutdown(); } SetCurrentWeapon(WEAPONTYPE_UNARMED); } void CPed::RemoveWeaponWhenEnteringVehicle(void) { if (IsPlayer() && HasWeaponSlot(5) && GetWeapon(5).m_nAmmoTotal > 0 && ((CPlayerPed*)this)->GetPlayerInfoForThisPlayerPed()->m_bDriveByAllowed) { if (m_storedWeapon == WEAPONTYPE_UNIDENTIFIED) m_storedWeapon = GetWeapon()->m_eWeaponType; SetCurrentWeapon(GetWeapon(5).m_eWeaponType); } else { CWeaponInfo *ourWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); RemoveWeaponModel(ourWeapon->m_nModelId); } } void CPed::ReplaceWeaponWhenExitingVehicle(void) { eWeaponType weaponType = GetWeapon()->m_eWeaponType; // If it's Uzi, we may have stored weapon. Uzi is the only gun we can use in car. if (IsPlayer() && GetWeaponSlot(weaponType) == WEAPONSLOT_SUBMACHINEGUN) { if (m_storedWeapon != WEAPONTYPE_UNIDENTIFIED) { SetCurrentWeapon(m_storedWeapon); m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; } } else { AddWeaponModel(CWeaponInfo::GetWeaponInfo(weaponType)->m_nModelId); } } void CPed::PreRender(void) { CShadows::StoreShadowForPed(this, CTimeCycle::m_fShadowDisplacementX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowDisplacementY[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowFrontX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowFrontY[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowSideX[CTimeCycle::m_CurrentStoredValue], CTimeCycle::m_fShadowSideY[CTimeCycle::m_CurrentStoredValue]); UpdateRpHAnim(); bool bIsWindModifierTurnedOn = false; float fAnyDirectionShift = 1.0f; bool bIsPedDrivingBikeOrOpenTopCar = false; if (IsPlayer() && CWindModifiers::FindWindModifier(GetPosition(), &fAnyDirectionShift, &fAnyDirectionShift) && !CCullZones::PlayerNoRain() && GetPedState() != PED_DRIVING) bIsWindModifierTurnedOn = true; if (GetPedState() == PED_DRIVING && m_pMyVehicle) { if (m_pMyVehicle->m_vehType == VEHICLE_TYPE_BIKE || (m_pMyVehicle->m_vehType == VEHICLE_TYPE_CAR && m_pMyVehicle->IsOpenTopCar())) bIsPedDrivingBikeOrOpenTopCar = true; } if (bIsWindModifierTurnedOn || bIsPedDrivingBikeOrOpenTopCar) { float fWindMult = 0.0f; if (bIsPedDrivingBikeOrOpenTopCar) { fWindMult = DotProduct(m_pMyVehicle->m_vecMoveSpeed, GetForward()); if (fWindMult > 0.4f) { float volume = (fWindMult - 0.4f) / 0.6f; DMAudio.PlayOneShot(m_audioEntityId, SOUND_SHIRT_WIND_FLAP, volume); } } if (bIsWindModifierTurnedOn) fWindMult = Min(fWindMult, Abs(fAnyDirectionShift - 1.0f)); RpHAnimHierarchy* hier = GetAnimHierarchyFromSkinClump(GetClump()); int32 idx; RwV3d scale; float fScaleOffset; fScaleOffset = fWindMult * 0.2f; scale.x = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); scale.y = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); scale.z = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_NECK)); RwMatrix* neck = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(neck, &scale, rwCOMBINEPRECONCAT); fScaleOffset = fWindMult * 0.1f; scale.x = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); scale.y = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); scale.z = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_CLAVICLEL)); RwMatrix* clavicleL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(clavicleL, &scale, rwCOMBINEPRECONCAT); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_CLAVICLER)); RwMatrix* clavicleR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(clavicleR, &scale, rwCOMBINEPRECONCAT); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_MID)); RwMatrix* mid = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(mid, &scale, rwCOMBINEPRECONCAT); fScaleOffset = fWindMult * 0.2f; scale.x = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); scale.y = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); scale.z = CGeneral::GetRandomNumberInRange(1.0f - fScaleOffset, 1.0f + fScaleOffset); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERARML)); RwMatrix* upperArmL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(upperArmL, &scale, rwCOMBINEPRECONCAT); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERARMR)); RwMatrix* upperArmR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(upperArmR, &scale, rwCOMBINEPRECONCAT); } if (bBodyPartJustCameOff && m_bodyPartBleeding == PED_HEAD) { // scale head to 0 if shot off RpHAnimHierarchy* hier = GetAnimHierarchyFromSkinClump(GetClump()); int32 idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_HEAD)); RwMatrix* head = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwV3d zero = { 0.0f, 0.0f, 0.0f }; RwMatrixScale(head, &zero, rwCOMBINEPRECONCAT); } if (IsPlayer() && gfTommyFatness != 1.0f) { RpHAnimHierarchy* hier = GetAnimHierarchyFromSkinClump(GetClump()); int32 idx; RwV3d scale; scale.x = 1.0f; scale.y = 1.0f + gfTommyFatness * 0.7f; scale.z = 1.0f + gfTommyFatness * 0.7f; idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_HEAD)); RwMatrix* head = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(head, &scale, rwCOMBINEPRECONCAT); scale.y = 1.0f + gfTommyFatness * 0.2f; scale.z = 1.0f + gfTommyFatness * 0.2f; idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_NECK)); RwMatrix* neck = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(neck, &scale, rwCOMBINEPRECONCAT); scale.y = 1.0f + gfTommyFatness * 0.5f; scale.z = 1.0f + gfTommyFatness * 0.5f; idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_MID)); RwMatrix* mid = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(mid, &scale, rwCOMBINEPRECONCAT); scale.y = 1.0f + gfTommyFatness; scale.z = 1.0f + gfTommyFatness; idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERLEGL)); RwMatrix* upperLegL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(upperLegL, &scale, rwCOMBINEPRECONCAT); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERLEGR)); RwMatrix* upperLegR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(upperLegR, &scale, rwCOMBINEPRECONCAT); scale.y = 1.0f + gfTommyFatness * 0.5f; scale.z = 1.0f + gfTommyFatness * 0.5f; idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_LOWERLEGR)); RwMatrix* lowerLegR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(lowerLegR, &scale, rwCOMBINEPRECONCAT); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_LOWERLEGL)); RwMatrix* lowerLegL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(lowerLegL, &scale, rwCOMBINEPRECONCAT); scale.y = 1.0f + gfTommyFatness * 0.23f; scale.z = 1.0f + gfTommyFatness * 0.23f; idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_FOOTL)); RwMatrix* footL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(footL, &scale, rwCOMBINEPRECONCAT); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_FOOTR)); RwMatrix* footR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(footR, &scale, rwCOMBINEPRECONCAT); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERARML)); RwMatrix* upperArmL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(upperArmL, &scale, rwCOMBINEPRECONCAT); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_UPPERARMR)); RwMatrix* upperArmR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(upperArmR, &scale, rwCOMBINEPRECONCAT); scale.y = 1.0f + gfTommyFatness * 0.2f; scale.z = 1.0f + gfTommyFatness * 0.2f; idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_FOREARML)); RwMatrix* foreArmL = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(foreArmL, &scale, rwCOMBINEPRECONCAT); idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_FOREARMR)); RwMatrix* foreArmR = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwMatrixScale(foreArmR, &scale, rwCOMBINEPRECONCAT); } if (bBodyPartJustCameOff && bIsPedDieAnimPlaying && m_bodyPartBleeding != -1 && (CTimer::GetFrameCounter() & 7) > 3) { CVector bloodDir(0.0f, 0.0f, 0.0f); CVector bloodPos(0.0f, 0.0f, 0.0f); TransformToNode(bloodPos, m_bodyPartBleeding); switch (m_bodyPartBleeding) { case PED_HEAD: bloodDir = 0.1f * GetUp(); break; case PED_UPPERARML: bloodDir = 0.04f * GetUp() - 0.04f * GetRight(); break; case PED_UPPERARMR: bloodDir = 0.04f * GetUp() - 0.04f * GetRight(); break; case PED_UPPERLEGL: bloodDir = 0.04f * GetUp() + 0.05f * GetForward(); break; case PED_UPPERLEGR: bloodDir = 0.04f * GetUp() + 0.05f * GetForward(); break; default: bloodDir = CVector(0.0f, 0.0f, 0.0f); break; } for(int i = 0; i < 4; i++) CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, bloodDir, nil, 0.0f, 0, 0, 0, 0); } if (CWeather::Rain > 0.3f && TheCamera.SoundDistUp > 15.0f) { if ((TheCamera.GetPosition() - GetPosition()).Magnitude() < 25.0f) { bool doSplashUp = true; CColModel *ourCol = CModelInfo::GetModelInfo(GetModelIndex())->GetColModel(); CVector speed = FindPlayerSpeed(); if (Abs(speed.x) <= 0.05f && Abs(speed.y) <= 0.05f) { if (!OnGround() && m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT) { if (!IsPedHeadAbovePos(0.3f) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED)) { doSplashUp = false; } } else doSplashUp = false; } else doSplashUp = false; if (doSplashUp && ourCol->numSpheres > 0) { for(int i = 0; i < ourCol->numSpheres; i++) { CColSphere *sphere = &ourCol->spheres[i]; CVector splashPos; switch (sphere->piece) { case PEDPIECE_LEFTARM: case PEDPIECE_RIGHTARM: case PEDPIECE_HEAD: splashPos = GetMatrix() * ourCol->spheres[i].center; splashPos.z += 0.7f * sphere->radius; splashPos.x += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f); splashPos.y += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f); CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, splashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 1, 0); break; default: break; } } } } } } CVector vecTestTemp(-1.0f, -1.0f, -1.0f); void CPed::Render(void) { if (bInVehicle && m_pMyVehicle && m_nPedState != PED_EXIT_CAR && m_nPedState != PED_DRAG_FROM_CAR) { if (!bRenderPedInCar) return; if (!m_pMyVehicle->IsBike() && !IsPlayer()) { float camDistSq = (TheCamera.GetPosition() - GetPosition()).MagnitudeSqr(); if (camDistSq > SQR((m_pMyVehicle->IsBoat() ? 40.0f : 25.0f) * TheCamera.LODDistMultiplier)) return; } } CEntity::Render(); if(m_pWeaponModel){ RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump()); int idx = RpHAnimIDGetIndex(hier, m_pFrames[PED_HANDR]->nodeID); RwMatrix *mat = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwFrame *frame = RpAtomicGetFrame(m_pWeaponModel); *RwFrameGetMatrix(frame) = *mat; RwFrameUpdateObjects(frame); RpAtomicRender(m_pWeaponModel); if (IsPlayer()) { CPlayerPed *player = (CPlayerPed*)this; if (player->m_pMinigunTopAtomic) { frame = RpAtomicGetFrame(player->m_pMinigunTopAtomic); *RwFrameGetMatrix(frame) = *mat; player->m_fGunSpinAngle = player->m_fGunSpinSpeed * CTimer::GetTimeStep() + player->m_fGunSpinAngle; if (player->m_fGunSpinAngle > TWOPI) player->m_fGunSpinAngle -= TWOPI; CMatrix mgTopMat, localAdjMat; mgTopMat.Attach(RwFrameGetMatrix(frame)); localAdjMat.SetRotateX(player->m_fGunSpinAngle); localAdjMat.Rotate(DEGTORAD(-4.477f)* vecTestTemp.x, DEGTORAD(29.731f) * vecTestTemp.y, DEGTORAD(1.064f) * vecTestTemp.z); localAdjMat.GetPosition() += CVector(0.829f, -0.001f, 0.226f); mgTopMat = mgTopMat * localAdjMat; mgTopMat.UpdateRW(); RwFrameUpdateObjects(frame); RpAtomicRender(player->m_pMinigunTopAtomic); } } } } void CPed::CheckAroundForPossibleCollisions(void) { CVector ourCentre, objCentre; CEntity *objects[8]; int16 maxObject; if (CTimer::GetTimeInMilliseconds() <= m_nPedStateTimer) return; GetBoundCentre(ourCentre); CWorld::FindObjectsInRange(ourCentre, 10.0f, true, &maxObject, 6, objects, false, true, false, true, false); for (int i = 0; i < maxObject; i++) { CEntity *object = objects[i]; if (bRunningToPhone) { if (gPhoneInfo.PhoneAtThisPosition(object->GetPosition())) break; } object->GetBoundCentre(objCentre); float radius = object->GetBoundRadius(); if (radius > 4.5f || radius < 1.0f) radius = 1.0f; // Developers gave up calculating Z diff. later according to asm. float diff = CVector(ourCentre - objCentre).MagnitudeSqr2D(); if (sq(radius + 1.0f) > diff) m_fRotationDest += DEGTORAD(22.5f); } } void CPed::SetIdle(void) { if (m_nPedState != PED_IDLE && m_nPedState != PED_MUG && m_nPedState != PED_FLEE_ENTITY) { if (m_nPedState == PED_AIM_GUN) ClearPointGunAt(); SetPedState(PED_IDLE); SetMoveState(PEDMOVE_STILL); m_nLastPedState = PED_NONE; } if (m_nWaitState == WAITSTATE_FALSE) { m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2000, 4000); } } void CPed::Idle(void) { CVehicle *veh = m_pMyVehicle; if (veh && veh->m_nGettingOutFlags && m_vehDoor) { if (veh->m_nGettingOutFlags & GetCarDoorFlag(m_vehDoor)) { if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { CVector doorPos = GetPositionToOpenCarDoor(veh, m_vehDoor); CVector doorDist = GetPosition() - doorPos; if (doorDist.MagnitudeSqr() < sq(0.5f)) { SetMoveState(PEDMOVE_WALK); return; } } } } if (m_nMoveState != PEDMOVE_STILL && !IsPlayer()) SetMoveState(PEDMOVE_STILL); m_moved = CVector2D(0.0f, 0.0f); } void CPed::ClearPause(void) { RestorePreviousState(); } void CPed::Pause(void) { m_moved = CVector2D(0.0f, 0.0f); if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer) ClearPause(); } void CPed::SetFall(int extraTime, AnimationId animId, uint8 evenIfNotInControl) { if (m_attachedTo) return; if (!IsPedInControl() && (!evenIfNotInControl || DyingOrDead())) return; ClearLookFlag(); ClearAimFlag(); SetStoredState(); SetPedState(PED_FALL); CAnimBlendAssociation *fallAssoc = nil; if (animId == ANIM_STD_NUM) { if (IsPlayer()) { fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_LHS); if (!fallAssoc) fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_RHS); } } else { fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), animId); if (fallAssoc) { fallAssoc->SetCurrentTime(0.0f); fallAssoc->blendAmount = 0.0f; fallAssoc->blendDelta = 8.0f; fallAssoc->SetRun(); } else { fallAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animId, 8.0f); } if (animId == ANIM_STD_BIKE_FALLBACK) fallAssoc->SetCurrentTime(0.4f); } if (extraTime == -1) { m_getUpTimer = UINT32_MAX; } else if (fallAssoc) { if (IsPlayer()) { if (fallAssoc->animId == ANIM_STD_ROLLOUT_LHS || fallAssoc->animId == ANIM_STD_ROLLOUT_RHS) { m_getUpTimer = 1000.0f * fallAssoc->hierarchy->totalLength + CTimer::GetTimeInMilliseconds() - 1000.0f * fallAssoc->currentTime + 100.0f; } else { m_getUpTimer = 1000.0f * fallAssoc->hierarchy->totalLength + CTimer::GetTimeInMilliseconds() + 500.0f; } } else { m_getUpTimer = 1000.0f * fallAssoc->hierarchy->totalLength + CTimer::GetTimeInMilliseconds() + extraTime + ((m_randomSeed + CTimer::GetFrameCounter()) % 1000); } } else { m_getUpTimer = extraTime + CTimer::GetTimeInMilliseconds() + 1000 + ((m_randomSeed + CTimer::GetFrameCounter()) % 1000); } bFallenDown = true; } void CPed::ClearFall(void) { SetGetUp(); } void CPed::Fall(void) { if (m_getUpTimer != UINT32_MAX && CTimer::GetTimeInMilliseconds() > m_getUpTimer && bIsStanding) ClearFall(); CAnimBlendAssociation *firstPartialAssoc; CAnimBlendAssociation *fallAssoc; if (IsPlayer() && (bKnockedUpIntoAir || bKnockedOffBike) && !bIsStanding) { firstPartialAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_PARTIAL); // What??? if (firstPartialAssoc && (firstPartialAssoc->animId == ANIM_STD_FALL_ONBACK || firstPartialAssoc->animId == ANIM_STD_FALL_ONFRONT)) fallAssoc = firstPartialAssoc; else fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_ONBACK); if (!fallAssoc) fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_ONFRONT); if (!fallAssoc && firstPartialAssoc && 0.8f * firstPartialAssoc->hierarchy->totalLength < firstPartialAssoc->currentTime) { if (firstPartialAssoc->flags & ASSOC_FRONTAL) { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_ONFRONT, 8.0f); } else { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_ONBACK, 8.0f); } } else if (fallAssoc && fallAssoc->blendAmount > 0.3f && fallAssoc->blendDelta >= 0.0f) { float time = fallAssoc->currentTime; if (time > 0.667f && time - fallAssoc->timeStep <= 0.667f) { fallAssoc->SetCurrentTime(0.0f); fallAssoc->SetRun(); } } } else if ((bKnockedUpIntoAir || bKnockedOffBike) && bIsStanding && !bWasStanding) { fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_ONBACK); if (!fallAssoc) fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_ONFRONT); if (fallAssoc) { bKnockedUpIntoAir = false; bKnockedOffBike = false; fallAssoc->speed = 3.0f; if (IsPlayer()) Say(SOUND_PED_LAND); } else { firstPartialAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_PARTIAL); if (firstPartialAssoc && !firstPartialAssoc->IsRunning()) { bKnockedUpIntoAir = false; bKnockedOffBike = false; } } } } bool CPed::CheckIfInTheAir(void) { if (bInVehicle) return false; CVector pos = GetPosition(); CColPoint foundColPoint; CEntity *foundEntity; float startZ = pos.z - 1.54f; bool foundGround = CWorld::ProcessVerticalLine(pos, startZ, foundColPoint, foundEntity, true, true, false, true, false, false, nil); if (!foundGround && m_nPedState != PED_JUMP) { pos.z -= FEET_OFFSET; if (CWorld::TestSphereAgainstWorld(pos, 0.15f, this, true, false, false, false, false, false)) foundGround = true; } return !foundGround; } void CPed::SetInTheAir(void) { if (bIsInTheAir) return; bIsInTheAir = true; CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_GLIDE, 4.0f); if (m_nPedState == PED_ATTACK) { ClearAttack(); ClearPointGunAt(); } else if (m_nPedState == PED_FIGHT) { EndFight(ENDFIGHT_FAST); } } void CPed::InTheAir(void) { CColPoint foundCol; CEntity *foundEnt; CVector ourPos = GetPosition(); CVector bitBelow = GetPosition(); bitBelow.z -= 4.04f; if (m_vecMoveSpeed.z < 0.0f && !bIsPedDieAnimPlaying) { if (!DyingOrDead()) { if (CWorld::ProcessLineOfSight(ourPos, bitBelow, foundCol, foundEnt, true, true, false, true, false, false, false)) { if (GetPosition().z - foundCol.point.z < 1.3f || bIsStanding) SetLanding(); } else if (m_nPedState != PED_ABSEIL && !RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL)) { if (m_vecMoveSpeed.z < -0.1f) CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL, 4.0f); } } } } void CPed::SetLanding(void) { if (DyingOrDead()) return; CAnimBlendAssociation *fallAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL); CAnimBlendAssociation *landAssoc; if (fallAssoc && bIsDrowning) return; RpAnimBlendClumpSetBlendDeltas(GetClump(), ASSOC_PARTIAL, -1000.0f); if (fallAssoc || m_nPedType == PEDTYPE_COP && bKnockedUpIntoAir) { landAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_COLLAPSE); DMAudio.PlayOneShot(m_audioEntityId, SOUND_FALL_COLLAPSE, 1.0f); if (IsPlayer()) Say(SOUND_PED_LAND); if (m_nPedType == PEDTYPE_COP) { if (bKnockedUpIntoAir) bKnockedUpIntoAir = false; } } else { landAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FALL_LAND); DMAudio.PlayOneShot(m_audioEntityId, SOUND_FALL_LAND, 1.0f); } landAssoc->SetFinishCallback(PedLandCB, this); bIsInTheAir = false; bIsLanding = true; } void CPed::SetGetUp(void) { if (m_nPedState == PED_GETUP && bGetUpAnimStarted) return; if (!CanSetPedState()) return; if (m_fHealth >= 1.0f || IsPedHeadAbovePos(-0.3f)) { if (bUpdateAnimHeading) { m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); m_fRotationCur -= HALFPI; bUpdateAnimHeading = false; } if (m_nPedState != PED_GETUP) { SetStoredState(); SetPedState(PED_GETUP); } CVehicle *collidingVeh = (CVehicle*)m_pCollidingEntity; CVehicle *veh = (CVehicle*)CPedPlacement::IsPositionClearOfCars(&GetPosition()); if (veh && veh->m_vehType != VEHICLE_TYPE_BIKE && veh != m_attachedTo || collidingVeh && collidingVeh->IsVehicle() && collidingVeh->m_vehType != VEHICLE_TYPE_BIKE && ((uint8)(CTimer::GetFrameCounter() + m_randomSeed + 5) % 8 || CCollision::ProcessColModels(GetMatrix(), *GetColModel(), collidingVeh->GetMatrix(), *collidingVeh->GetColModel(), aTempPedColPts, nil, nil) > 0)) { bGetUpAnimStarted = false; if (IsPlayer()) InflictDamage(nil, WEAPONTYPE_RUNOVERBYCAR, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); else if(CPad::GetPad(0)->ArePlayerControlsDisabled()) InflictDamage(nil, WEAPONTYPE_RUNOVERBYCAR, 1000.0f, PEDPIECE_TORSO, 0); return; } bGetUpAnimStarted = true; m_pCollidingEntity = nil; bKnockedUpIntoAir = false; bKnockedOffBike = false; CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNFAST); if (animAssoc) { if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUN)) { CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_RUN, 8.0f); } else { CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); } animAssoc->flags |= ASSOC_DELETEFADEDOUT; } if (m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) { m_headingRate = 0.0f; // Seemingly they planned to use different getup anims for different conditions, but sadly in final game all getup anims(GETUP1, GETUP2, GETUP3) are same... if (bFleeWhenStanding && m_threatEx) animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP, 1000.0f); else animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP, 1000.0f); } else if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP_FRONT, 1000.0f); else animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GET_UP, 1000.0f); animAssoc->SetFinishCallback(PedGetupCB, this); } else { m_fHealth = 0.0f; SetDie(ANIM_STD_NUM, 4.0f, 0.0f); } } void CPed::Mug(void) { if (m_pSeekTarget && m_pSeekTarget->IsPed()) { if (CTimer::GetTimeInMilliseconds() <= m_attackTimer - 2000) { if ((m_pSeekTarget->GetPosition() - GetPosition()).Magnitude() > 3.0f) m_wepSkills = 50; Say(SOUND_PED_MUGGING); ((CPed*)m_pSeekTarget)->Say(SOUND_PED_ROBBED); } else { SetWanderPath(CGeneral::GetRandomNumber() & 7); SetFlee(m_pSeekTarget, 20000); } } else { SetIdle(); } } // Unused void CPed::SetLook(float direction) { if (IsPedInControl()) { SetStoredState(); SetPedState(PED_LOOK_HEADING); SetLookFlag(direction, false); } } void CPed::SetLook(CEntity* to) { if (IsPedInControl()) { SetStoredState(); SetPedState(PED_LOOK_ENTITY); SetLookFlag(to, false); } } void CPed::SetLookTimer(int time) { if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { m_lookTimer = CTimer::GetTimeInMilliseconds() + time; } } void CPed::SetAttackTimer(uint32 time) { if (CTimer::GetTimeInMilliseconds() > m_attackTimer) m_attackTimer = Max(m_shootTimer, CTimer::GetTimeInMilliseconds()) + time; } void CPed::SetShootTimer(uint32 time) { if (CTimer::GetTimeInMilliseconds() > m_shootTimer) { m_shootTimer = CTimer::GetTimeInMilliseconds() + time; } } void CPed::ClearLook(void) { RestorePreviousState(); ClearLookFlag(); } void CPed::Look(void) { TurnBody(); } bool CPed::TurnBody(void) { bool turnDone = true; if (m_pLookTarget) m_fLookDirection = CGeneral::GetRadianAngleBetweenPoints( m_pLookTarget->GetPosition().x, m_pLookTarget->GetPosition().y, GetPosition().x, GetPosition().y); float limitedLookDir = CGeneral::LimitRadianAngle(m_fLookDirection); float currentRot = m_fRotationCur; if (currentRot - PI > limitedLookDir) limitedLookDir += 2 * PI; else if (PI + currentRot < limitedLookDir) limitedLookDir -= 2 * PI; float neededTurn = currentRot - limitedLookDir; m_fRotationDest = limitedLookDir; if (Abs(neededTurn) > 0.05f) { turnDone = false; currentRot -= neededTurn * 0.2f; } m_fRotationCur = currentRot; m_fLookDirection = limitedLookDir; return turnDone; } void CPed::SetSeek(CVector pos, float distanceToCountDone) { if (!IsPedInControl() || (m_nPedState == PED_SEEK_POS && m_vecSeekPos.x == pos.x && m_vecSeekPos.y == pos.y) || m_nPedState == PED_FOLLOW_PATH) return; if (!CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) { ClearPointGunAt(); } if (m_nPedState != PED_SEEK_POS) SetStoredState(); SetPedState(PED_SEEK_POS); m_distanceToCountSeekDone = distanceToCountDone; m_vecSeekPos = pos; } void CPed::SetSeek(CEntity *seeking, float distanceToCountDone) { if (!IsPedInControl()) return; if (m_nPedState == PED_SEEK_ENTITY && m_pSeekTarget == seeking) return; if (!seeking || m_nPedState == PED_FOLLOW_PATH) return; if (m_nPedState != PED_SEEK_ENTITY) SetStoredState(); SetPedState(PED_SEEK_ENTITY); m_distanceToCountSeekDone = distanceToCountDone; m_pSeekTarget = seeking; m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); SetMoveState(PEDMOVE_STILL); } void CPed::ClearSeek(void) { SetIdle(); bRunningToPhone = false; } bool CPed::Seek(void) { float distanceToCountItDone = m_distanceToCountSeekDone; eMoveState nextMove = PEDMOVE_NONE; if (m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { if (m_nPedState != PED_EXIT_TRAIN && m_nPedState != PED_ENTER_TRAIN && m_nPedState != PED_SEEK_IN_BOAT && m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && m_objective != OBJECTIVE_SOLICIT_VEHICLE && !bDuckAndCover) { if ((!m_pedInObjective || !m_pedInObjective->bInVehicle) && (CTimer::GetFrameCounter() + m_randomSeed + 60) % 32 == 0) { CEntity *obstacle = CWorld::TestSphereAgainstWorld(m_vecSeekPos, 0.4f, nil, false, true, false, false, false, false); if (obstacle) { if (!obstacle->IsVehicle() || ((CVehicle*)obstacle)->IsCar()) { distanceToCountItDone = 2.5f; } else { CVehicleModelInfo *vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(obstacle->GetModelIndex()); float yLength = vehModel->GetColModel()->boundingBox.max.y - vehModel->GetColModel()->boundingBox.min.y; distanceToCountItDone = yLength * 0.55f; } } } } } if (!m_pSeekTarget && m_nPedState == PED_SEEK_ENTITY) ClearSeek(); if (m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && !m_pedInObjective) { m_objective = OBJECTIVE_NONE; ClearObjective(); SetWanderPath(0); return false; } float seekPosDist = (m_vecSeekPos - GetPosition()).Magnitude2D(); if (seekPosDist < 2.0f || m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT) { if (m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) { if (m_pedInObjective->m_nMoveState != PEDMOVE_STILL) nextMove = m_pedInObjective->m_nMoveState; } else nextMove = PEDMOVE_WALK; } else if (m_objective != OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) { if (m_objective == OBJECTIVE_SPRINT_TO_AREA) nextMove = PEDMOVE_SPRINT; else if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS || m_objective == OBJECTIVE_RUN_TO_AREA || bIsRunning) nextMove = PEDMOVE_RUN; else nextMove = PEDMOVE_WALK; } else if (seekPosDist <= 2.0f) { if (m_pedInObjective->m_nMoveState != PEDMOVE_STILL) nextMove = m_pedInObjective->m_nMoveState; } else { nextMove = PEDMOVE_RUN; } if (m_nPedState == PED_SEEK_ENTITY) { if (m_pSeekTarget->IsPed()) { if (((CPed*)m_pSeekTarget)->bInVehicle) distanceToCountItDone += 2.0f; } } CVector *nextNode = SeekFollowingPath(); if (nextNode || seekPosDist >= distanceToCountItDone) { if (bIsRunning && nextMove != PEDMOVE_SPRINT) nextMove = PEDMOVE_RUN; if (CTimer::GetTimeInMilliseconds() <= m_nPedStateTimer) { if (m_actionX != 0.0f && m_actionY != 0.0f) { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( m_actionX, m_actionY, GetPosition().x, GetPosition().y); float neededTurn = Abs(m_fRotationDest - m_fRotationCur); if (neededTurn > PI) neededTurn = TWOPI - neededTurn; if (neededTurn > HALFPI) { if (seekPosDist >= 1.0f) { if (seekPosDist < 2.0f) { if (bIsRunning) nextMove = PEDMOVE_RUN; else nextMove = PEDMOVE_WALK; } } else { nextMove = PEDMOVE_STILL; } } CVector2D moveDist(GetPosition().x - m_actionX, GetPosition().y - m_actionY); if (moveDist.Magnitude() < 0.5f) { m_nPedStateTimer = 0; m_actionX = 0.f; m_actionY = 0.f; } } } else { if (nextNode) { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( nextNode->x, nextNode->y, GetPosition().x, GetPosition().y); } else { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( m_vecSeekPos.x, m_vecSeekPos.y, GetPosition().x, GetPosition().y); } float neededTurn = Abs(m_fRotationDest - m_fRotationCur); if (neededTurn > PI) neededTurn = TWOPI - neededTurn; if (neededTurn > HALFPI) { if (seekPosDist >= 1.0f && neededTurn <= DEGTORAD(135.0f)) { if (seekPosDist < 2.0f) nextMove = PEDMOVE_WALK; } else { nextMove = PEDMOVE_STILL; } } } if (((m_nPedState == PED_FLEE_POS || m_nPedState == PED_FLEE_ENTITY) && m_nMoveState < nextMove) || (m_nPedState != PED_FLEE_POS && m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_FOLLOW_PATH && m_objective != OBJECTIVE_GOTO_CHAR_ON_FOOT && m_nWaitState == WAITSTATE_FALSE)) { SetMoveState(nextMove); } SetMoveAnim(); return false; } if ((m_objective != OBJECTIVE_FOLLOW_CHAR_IN_FORMATION || m_pedInObjective->m_nMoveState == PEDMOVE_STILL) && m_nMoveState != PEDMOVE_STILL) { m_nPedStateTimer = 0; m_actionX = 0.f; m_actionY = 0.f; } if (m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA || m_objective == OBJECTIVE_GOTO_AREA_ANY_MEANS || IsUseAttractorObjective(m_objective)) { if (m_pNextPathNode) m_pNextPathNode = nil; else bScriptObjectiveCompleted = true; bUsePedNodeSeek = true; } return true; } void CPed::SetFlee(CVector2D const &from, int time) { if (CTimer::GetTimeInMilliseconds() < m_nPedStateTimer || !IsPedInControl() || bKindaStayInSamePlace) return; if (m_nPedState != PED_FLEE_ENTITY) { SetStoredState(); SetPedState(PED_FLEE_POS); SetMoveState(PEDMOVE_RUN); m_fleeFromPos = from; } bUsePedNodeSeek = true; m_pNextPathNode = nil; m_fleeTimer = CTimer::GetTimeInMilliseconds() + time; float angleToFace = CGeneral::GetRadianAngleBetweenPoints( GetPosition().x, GetPosition().y, from.x, from.y); m_fRotationDest = CGeneral::LimitRadianAngle(angleToFace); if (m_fRotationCur - PI > m_fRotationDest) { m_fRotationDest += 2 * PI; } else if (PI + m_fRotationCur < m_fRotationDest) { m_fRotationDest -= 2 * PI; } } void CPed::SetFlee(CEntity *fleeFrom, int time) { if (!IsPedInControl() || bKindaStayInSamePlace || !fleeFrom) return; SetStoredState(); SetPedState(PED_FLEE_ENTITY); bUsePedNodeSeek = true; SetMoveState(PEDMOVE_RUN); m_fleeFrom = fleeFrom; m_fleeFrom->RegisterReference((CEntity **) &m_fleeFrom); if (time <= 0) m_fleeTimer = 0; else m_fleeTimer = CTimer::GetTimeInMilliseconds() + time; float angleToFace = CGeneral::GetRadianAngleBetweenPoints( GetPosition().x, GetPosition().y, fleeFrom->GetPosition().x, fleeFrom->GetPosition().y); m_fRotationDest = CGeneral::LimitRadianAngle(angleToFace); if (m_fRotationCur - PI > m_fRotationDest) { m_fRotationDest += 2 * PI; } else if (PI + m_fRotationCur < m_fRotationDest) { m_fRotationDest -= 2 * PI; } } void CPed::ClearFlee(void) { RestorePreviousState(); bUsePedNodeSeek = false; m_chatTimer = 0; m_fleeTimer = 0; } void CPed::Flee(void) { if (CTimer::GetTimeInMilliseconds() > m_fleeTimer && m_fleeTimer) { bool mayFinishFleeing = true; if (m_nPedState == PED_FLEE_ENTITY) { if ((CVector2D(GetPosition()) - ms_vec2DFleePosition).MagnitudeSqr() < sq(30.0f)) mayFinishFleeing = false; } if (mayFinishFleeing) { bMakeFleeScream = false; eMoveState moveState = m_nMoveState; ClearFlee(); if (m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE || m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS) { bBeingChasedByPolice = false; RestorePreviousObjective(); } if ((m_nPedState == PED_IDLE || m_nPedState == PED_WANDER_PATH) && CGeneral::GetRandomNumber() & 1) { SetWaitState(moveState <= PEDMOVE_WALK ? WAITSTATE_CROSS_ROAD_LOOK : WAITSTATE_FINISH_FLEE, nil); } return; } m_fleeTimer = CTimer::GetTimeInMilliseconds() + 5000; } if (bMakeFleeScream && !((CTimer::GetFrameCounter() + m_randomSeed) & 7)) { Say(SOUND_PED_FLEE_SPRINT); bMakeFleeScream = false; } if (bUsePedNodeSeek) { CPathNode *realLastNode = nil; uint8 nextDirection = 0; uint8 curDirectionShouldBe = 9; // means not defined yet if (m_nPedStateTimer < CTimer::GetTimeInMilliseconds() && m_collidingThingTimer < CTimer::GetTimeInMilliseconds()) { if (m_pNextPathNode && CTimer::GetTimeInMilliseconds() > m_chatTimer) { curDirectionShouldBe = CGeneral::GetNodeHeadingFromVector(GetPosition().x - ms_vec2DFleePosition.x, GetPosition().y - ms_vec2DFleePosition.y); if (m_nPathDir < curDirectionShouldBe) m_nPathDir += 8; int dirDiff = m_nPathDir - curDirectionShouldBe; if (dirDiff > 2 && dirDiff < 6) { realLastNode = nil; m_pLastPathNode = m_pNextPathNode; m_pNextPathNode = nil; } } if (m_pNextPathNode) { m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); if (m_nMoveState == PEDMOVE_RUN) bIsRunning = true; eMoveState moveState = m_nMoveState; if (Seek()) { realLastNode = m_pLastPathNode; m_pLastPathNode = m_pNextPathNode; m_pNextPathNode = nil; } bIsRunning = false; SetMoveState(moveState); } } if (!m_pNextPathNode) { if (curDirectionShouldBe == 9) { curDirectionShouldBe = CGeneral::GetNodeHeadingFromVector(GetPosition().x - ms_vec2DFleePosition.x, GetPosition().y - ms_vec2DFleePosition.y); } ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, curDirectionShouldBe, &nextDirection); if (curDirectionShouldBe < nextDirection) curDirectionShouldBe += 8; if (m_pNextPathNode && m_pNextPathNode != realLastNode && m_pNextPathNode != m_pLastPathNode && curDirectionShouldBe - nextDirection != 4) { m_nPathDir = nextDirection; m_chatTimer = CTimer::GetTimeInMilliseconds() + 2000; } else { bUsePedNodeSeek = false; SetMoveState(PEDMOVE_RUN); Flee(); } } return; } if ((m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_ON_FIRE) && m_nPedStateTimer < CTimer::GetTimeInMilliseconds()) { float angleToFleeFromPos = CGeneral::GetRadianAngleBetweenPoints( GetPosition().x, GetPosition().y, ms_vec2DFleePosition.x, ms_vec2DFleePosition.y); m_fRotationDest = CGeneral::LimitRadianAngle(angleToFleeFromPos); if (m_fRotationCur - PI > m_fRotationDest) m_fRotationDest += TWOPI; else if (PI + m_fRotationCur < m_fRotationDest) m_fRotationDest -= TWOPI; } if (CTimer::GetTimeInMilliseconds() >= m_collidingThingTimer) return; if (!m_collidingEntityWhileFleeing) return; double collidingThingPriorityMult = (double)(m_collidingThingTimer - CTimer::GetTimeInMilliseconds()) * 2.0 / 2500; if (collidingThingPriorityMult <= 1.5) { double angleToFleeEntity = CGeneral::GetRadianAngleBetweenPoints( GetPosition().x, GetPosition().y, m_collidingEntityWhileFleeing->GetPosition().x, m_collidingEntityWhileFleeing->GetPosition().y); angleToFleeEntity = CGeneral::LimitRadianAngle(angleToFleeEntity); double angleToFleeCollidingThing = CGeneral::GetRadianAngleBetweenPoints( m_vecDamageNormal.x, m_vecDamageNormal.y, 0.0f, 0.0f); angleToFleeCollidingThing = CGeneral::LimitRadianAngle(angleToFleeCollidingThing); if (angleToFleeEntity - PI > angleToFleeCollidingThing) angleToFleeCollidingThing += TWOPI; else if (PI + angleToFleeEntity < angleToFleeCollidingThing) angleToFleeCollidingThing -= TWOPI; if (collidingThingPriorityMult <= 1.0f) { // Range [0.0, 1.0] float angleToFleeBoth = (angleToFleeCollidingThing + angleToFleeEntity) * 0.5f; if (m_fRotationDest - PI > angleToFleeBoth) angleToFleeBoth += TWOPI; else if (PI + m_fRotationDest < angleToFleeBoth) angleToFleeBoth -= TWOPI; m_fRotationDest = (1.0f - collidingThingPriorityMult) * m_fRotationDest + collidingThingPriorityMult * angleToFleeBoth; } else { // Range (1.0, 1.5] double adjustedMult = (collidingThingPriorityMult - 1.0f) * 2.0f; m_fRotationDest = angleToFleeEntity * (1.0 - adjustedMult) + adjustedMult * angleToFleeCollidingThing; } } else { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( m_vecDamageNormal.x, m_vecDamageNormal.y, 0.0f, 0.0f); m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); } m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); if (m_fRotationCur - PI > m_fRotationDest) m_fRotationDest += TWOPI; else if (PI + m_fRotationCur < m_fRotationDest) m_fRotationDest -= TWOPI; } // "Wander range" state is unused in game, and you can't use it without SetWanderRange anyway void CPed::WanderRange(void) { bool arrived = Seek(); if (arrived) { Idle(); if ((m_randomSeed + 3 * CTimer::GetFrameCounter()) % 1000 > 997) { CVector2D newCoords2D = m_wanderRangeBounds->GetRandomPointInRange(); SetSeek(CVector(newCoords2D.x, newCoords2D.y, GetPosition().z), 2.5f); } } } bool CPed::SetWanderPath(int8 pathStateDest) { uint8 nextPathState; if (IsPlayer()) return false; if (IsPedInControl()) { if (bKindaStayInSamePlace) { SetIdle(); return false; } else { m_nPathDir = pathStateDest; if (pathStateDest == 0) pathStateDest = CGeneral::GetRandomNumberInRange(1, 7); ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, m_nPathDir, &nextPathState); // Circular loop until we find a node for current m_nPathDir while (!m_pNextPathNode) { m_nPathDir = (m_nPathDir+1) % 8; // We're at where we started and couldn't find any node if (m_nPathDir == pathStateDest) { ClearAll(); SetIdle(); return false; } ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, m_nPathDir, &nextPathState); } // We did it, save next path state and return true m_nPathDir = nextPathState; SetPedState(PED_WANDER_PATH); SetMoveState(PEDMOVE_WALK); bIsRunning = false; return true; } } else { m_nPathDir = pathStateDest; bStartWanderPathOnFoot = true; return false; } } void CPed::WanderPath(void) { if (!m_pNextPathNode) { printf("THIS SHOULDN@T HAPPEN TOO OFTEN\n"); SetIdle(); return; } if (m_nWaitState == WAITSTATE_FALSE) { if (m_nMoveState == PEDMOVE_STILL || m_nMoveState == PEDMOVE_NONE) SetMoveState(PEDMOVE_WALK); } m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); m_vecSeekPos.z += 1.0f; // Only returns true when ped is stuck(not stopped) I think, then we should assign new direction or wait state to him. if (!Seek()) return; CPathNode *previousLastNode = m_pLastPathNode; uint8 randVal = (m_randomSeed + 3 * CTimer::GetFrameCounter()) % 100; // We don't prefer 180-degree turns in normal situations uint8 dirWeWouldntPrefer = m_nPathDir; if (dirWeWouldntPrefer <= 3) dirWeWouldntPrefer += 4; else dirWeWouldntPrefer -= 4; CPathNode *nodeWeWouldntPrefer = nil; uint8 dirToSet = 9; // means undefined uint8 dirWeWouldntPrefer2 = 9; // means undefined uint8 tryCount = 0; if (randVal <= 90) { if (randVal > 80) { m_nPathDir += 2; m_nPathDir %= 8; } } else { m_nPathDir -= 2; if (m_nPathDir < 0) m_nPathDir += 8; } m_pLastPathNode = m_pNextPathNode; ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, m_nPathDir, &dirToSet); if (((CPedModelInfo*)CModelInfo::GetModelInfo(m_modelIndex))->m_pedStatType == PEDSTAT_SKATER) { if (m_pNextPathNode) { CVector unpacked(m_pNextPathNode->GetPosition() / 8.f); if (!CPopulation::IsSkateable(unpacked)) m_pNextPathNode = nil; } } // NB: SetWanderPath checks for m_nPathDir == dirToStartWith, this one checks for tryCount > 7 while (!m_pNextPathNode) { tryCount++; m_nPathDir = (m_nPathDir + 1) % 8; // We're at where we started and couldn't find any node if (tryCount > 7) { if (!nodeWeWouldntPrefer) { ClearAll(); SetIdle(); // Probably this text carried over here after copy-pasting this loop from early version of SetWanderPath. Error("Can't find valid path node, SetWanderPath, Ped.cpp"); return; } m_pNextPathNode = nodeWeWouldntPrefer; dirToSet = dirWeWouldntPrefer2; } else { ThePaths.FindNextNodeWandering(PATH_PED, GetPosition(), &m_pLastPathNode, &m_pNextPathNode, m_nPathDir, &dirToSet); if (m_pNextPathNode) { if (dirToSet == dirWeWouldntPrefer) { nodeWeWouldntPrefer = m_pNextPathNode; dirWeWouldntPrefer2 = dirToSet; m_pNextPathNode = nil; } } if (((CPedModelInfo*)CModelInfo::GetModelInfo(m_modelIndex))->m_pedStatType == PEDSTAT_SKATER) { if (m_pNextPathNode) { CVector unpacked(m_pNextPathNode->GetPosition() / 8.f); if (!CPopulation::IsSkateable(unpacked)) m_pNextPathNode = nil; } } } } m_nPathDir = dirToSet; if (m_pLastPathNode == m_pNextPathNode) { m_pNextPathNode = previousLastNode; SetWaitState(WAITSTATE_DOUBLEBACK, nil); Say(SOUND_PED_WAIT_DOUBLEBACK); } else if (ThePaths.TestForPedTrafficLight(m_pLastPathNode, m_pNextPathNode)) { SetWaitState(WAITSTATE_TRAFFIC_LIGHTS, nil); } else if (ThePaths.TestCrossesRoad(m_pLastPathNode, m_pNextPathNode)) { SetWaitState(WAITSTATE_CROSS_ROAD, nil); } else if (m_pNextPathNode == previousLastNode) { SetWaitState(WAITSTATE_DOUBLEBACK, nil); Say(SOUND_PED_WAIT_DOUBLEBACK); } } void CPed::Avoid(void) { CPed *nearestPed; if(m_pedStats->m_temper > m_pedStats->m_fear && m_pedStats->m_temper > 50) return; if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { if (m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL) { nearestPed = m_nearPeds[0]; if (nearestPed && nearestPed->m_nPedState != PED_DEAD && nearestPed != m_pSeekTarget && nearestPed != m_pedInObjective) { // Check if this ped wants to avoid the nearest one if (CPedType::GetAvoid(m_nPedType) & CPedType::GetFlag(nearestPed->m_nPedType)) { // Further codes checks whether the distance between us and ped will be equal or below 1.0, if we walk up to him by 1.25 meters. // If so, we want to avoid it, so we turn our body 45 degree and look to somewhere else. // Game converts from radians to degress and back again here, doesn't make much sense CVector2D forward(-Sin(m_fRotationCur), Cos(m_fRotationCur)); forward.Normalise(); // this is kinda pointless // Move forward 1.25 meters CVector2D testPosition = CVector2D(GetPosition()) + forward*1.25f; // Get distance to ped we want to avoid CVector2D distToPed = CVector2D(nearestPed->GetPosition()) - testPosition; if (distToPed.Magnitude() <= 1.0f && OurPedCanSeeThisOne((CEntity*)nearestPed)) { m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 500 + (m_randomSeed + 3 * CTimer::GetFrameCounter()) % 1000 / 5; m_fRotationDest += DEGTORAD(45.0f); if (!bIsLooking) { SetLookFlag(nearestPed, false); SetLookTimer(CGeneral::GetRandomNumberInRange(500, 800)); } } } } } } } CVector* CPed::SeekFollowingPath(void) { static CVector vecNextPathNode; if (m_nCurPathNodeId >= m_nNumPathNodes || m_nNumPathNodes == 0) return nil; vecNextPathNode = m_pathNodesToGo[m_nCurPathNodeId]->GetPosition(); if ((vecNextPathNode - GetPosition()).Magnitude2D() < m_distanceToCountSeekDone) { m_nCurPathNodeId++; if (m_nCurPathNodeId < m_nNumPathNodes) m_pCurPathNode = m_pathNodesToGo[m_nCurPathNodeId]; } if (m_nCurPathNodeId == m_nNumPathNodes) return nil; else return &vecNextPathNode; } bool CPed::SetFollowPath(CVector dest, float radius, eMoveState state, CEntity* walkAroundEnt, CEntity* targetEnt, int time) { if (m_nPedState == PED_FOLLOW_PATH) { bool stopFollow = false; if (walkAroundEnt && walkAroundEnt != m_followPathWalkAroundEnt || !walkAroundEnt && m_followPathWalkAroundEnt || targetEnt && targetEnt != m_followPathTargetEnt || !targetEnt && m_followPathTargetEnt) { stopFollow = true; } else if (targetEnt) { if ((targetEnt->GetPosition() - m_followPathDestPos).MagnitudeSqr() > 1.f) stopFollow = true; } else if (!walkAroundEnt && !targetEnt) { if ((dest - m_followPathDestPos).MagnitudeSqr() > 1.f) stopFollow = true; } if (!stopFollow) return false; } m_pathNodeTimer = CTimer::GetTimeInMilliseconds() + time; m_followPathWalkAroundEnt = walkAroundEnt; m_followPathTargetEnt = targetEnt; m_distanceToCountSeekDone = 0.5f; bool weHaveTargetPed = targetEnt && targetEnt->IsPed(); bool useDestVec = !weHaveTargetPed; if (useDestVec) m_followPathDestPos = dest; else m_followPathDestPos = targetEnt->GetPosition(); if (targetEnt && m_nPedState == PED_SEEK_POS) { m_followPathDestPos = m_vecSeekPos; } m_followPathAbortDist = radius > 0.f ? radius : 20.f; bool useGivenPedMove = state == PEDMOVE_RUN || state == PEDMOVE_WALK; if (useGivenPedMove) m_followPathMoveState = state; else m_followPathMoveState = PEDMOVE_WALK; if (m_followPathWalkAroundEnt) return SetFollowPathDynamic(); else return SetFollowPathStatic(); } bool CPed::SetFollowPathStatic(void) { ClearFollowPath(); if (sq(m_followPathAbortDist) > (GetPosition() - m_followPathDestPos).MagnitudeSqr() && CWorld::IsWanderPathClear(GetPosition(), m_followPathDestPos, 0.5f, 4)) { RestorePreviousState(); if (m_objective == OBJECTIVE_NONE) { if (m_followPathMoveState == PEDMOVE_RUN) SetObjective(OBJECTIVE_RUN_TO_AREA, m_followPathDestPos); else SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, m_followPathDestPos); } SetPedState(PED_NONE); } else { ThePaths.DoPathSearch(PATH_PED, GetPosition(), -1, m_followPathDestPos, m_pathNodesToGo, &m_nNumPathNodes, ARRAY_SIZE(m_pathNodesToGo), nil, nil, 999999.9f, -1); if (m_nNumPathNodes != 0) { if (m_nNumPathNodes > 0 && m_pathNodesToGo[0] != m_pCurPathNode) { for (int i = 0; i < ARRAY_SIZE(m_pathNodesToGo) - 1; i++) { m_pathNodesToGo[i] = m_pathNodesToGo[i+1]; } --m_nNumPathNodes; } for (int i = 0; i < m_nNumPathNodes; ++i) { CVector nodePos = m_pathNodesToGo[i]->GetPosition(); if (sq(m_followPathAbortDist) > (nodePos - m_followPathDestPos).MagnitudeSqr() && CWorld::IsWanderPathClear(nodePos, m_followPathDestPos, 0.5f, 4)) { m_nNumPathNodes = i + 1; break; } } m_nCurPathNodeId = 0; if (m_pCurPathNode) { for (int j = 0; j < m_nNumPathNodes; ++j) { if (m_pathNodesToGo[j] == m_pCurPathNode) { m_nCurPathNodeId = j; break; } } } m_pCurPathNode = m_pathNodesToGo[m_nCurPathNodeId]; PedState oldLastState = m_nLastPedState; m_nLastPedState = PED_NONE; SetStoredState(); if (m_nLastPedState == PED_NONE) m_nLastPedState = oldLastState; SetPedState(PED_FOLLOW_PATH); SetMoveState(m_followPathMoveState); } else { RestorePreviousState(); if (m_objective == OBJECTIVE_NONE) { if (m_followPathMoveState == PEDMOVE_RUN) SetObjective(OBJECTIVE_RUN_TO_AREA, m_followPathDestPos); else SetObjective(OBJECTIVE_GOTO_AREA_ON_FOOT, m_followPathDestPos); } SetPedState(PED_NONE); } } return true; } bool CPed::SetFollowPathDynamic(void) { CVector colBoxMin = m_followPathWalkAroundEnt->GetColModel()->boundingBox.min + CVector(-0.35f, -0.35f, 0.f); CVector colBoxMax = m_followPathWalkAroundEnt->GetColModel()->boundingBox.max + CVector(0.35f, 0.35f, 0.f); CVector colCornerOffsets[4]; // BL, BR, TR, TL colCornerOffsets[0] = CVector(colBoxMin.x, colBoxMin.y, 0.f); colCornerOffsets[1] = CVector(colBoxMax.x, colBoxMin.y, 0.f); colCornerOffsets[2] = CVector(colBoxMax.x, colBoxMax.y, 0.f); colCornerOffsets[3] = CVector(colBoxMin.x, colBoxMax.y, 0.f); if (m_followPathWalkAroundEnt->IsVehicle() && ((CVehicle*)m_followPathWalkAroundEnt)->IsUpsideDown()) { CVector old0 = colCornerOffsets[0]; colCornerOffsets[0] = colCornerOffsets[1]; colCornerOffsets[1] = old0; CVector old2 = colCornerOffsets[2]; colCornerOffsets[2] = colCornerOffsets[3]; colCornerOffsets[3] = old2; } CVector colCornerPos[4]; // global. again BL, BR, TR, TL float dotProdCorrection[4]; CVector colBoxPlaneNormal[4]; for (int i=0; i<4; i++) { colCornerPos[i] = m_followPathWalkAroundEnt->GetMatrix() * colCornerOffsets[i]; colCornerPos[i].z = GetPosition().z; } CVector prevColCorner = colCornerPos[3]; // top left CVector *curCornerPos; CVector fwdToNextCorner; for (int i=0; i<4; i++) { curCornerPos = &colCornerPos[i]; fwdToNextCorner = *curCornerPos - prevColCorner; fwdToNextCorner.Normalise(); colBoxPlaneNormal[i] = CrossProduct(fwdToNextCorner, CVector(0.f, 0.f, 1.f)); dotProdCorrection[i] = -DotProduct(prevColCorner, colBoxPlaneNormal[i]); // yes, dp with global coord, as if in distance to plane calculation prevColCorner = *curCornerPos; } bool weReGoingGreat = false; CVector startVecCandidate = GetPosition(); CVector targetVecCandidate = m_followPathDestPos; CVector dirToGo = targetVecCandidate - startVecCandidate; dirToGo.Normalise(); CVector ourPos = startVecCandidate; for (int i=0; i<4; i++) { CVector curPlaneNormal = colBoxPlaneNormal[i]; float minusGlobalCornerPos = dotProdCorrection[i]; float startVecDistToPlane = DotProduct(curPlaneNormal, startVecCandidate) + minusGlobalCornerPos; #define FRONT_OF_PLANE 1 #define ON_THE_PLANE 0 #define BEHIND_THE_PLANE -1 int8 startVecStatus; int8 targetVecStatus; if (startVecDistToPlane > 0.1f) startVecStatus = FRONT_OF_PLANE; else if (startVecDistToPlane < -0.1f) startVecStatus = BEHIND_THE_PLANE; else startVecStatus = ON_THE_PLANE; float targetVecDistToPlane = DotProduct(curPlaneNormal, targetVecCandidate) + minusGlobalCornerPos; if (targetVecDistToPlane > 0.1f) targetVecStatus = FRONT_OF_PLANE; else if (targetVecDistToPlane < -0.1f) targetVecStatus = BEHIND_THE_PLANE; else targetVecStatus = ON_THE_PLANE; if (startVecStatus == BEHIND_THE_PLANE || targetVecStatus == BEHIND_THE_PLANE) { if (startVecStatus == BEHIND_THE_PLANE && targetVecStatus == FRONT_OF_PLANE) { targetVecCandidate = -(DotProduct(ourPos, curPlaneNormal) + minusGlobalCornerPos) / DotProduct(dirToGo, curPlaneNormal) * dirToGo + ourPos; } else if (startVecStatus == FRONT_OF_PLANE && targetVecStatus == BEHIND_THE_PLANE) { startVecCandidate = -(DotProduct(ourPos, curPlaneNormal) + minusGlobalCornerPos) / DotProduct(dirToGo, curPlaneNormal) * dirToGo + ourPos; } } else { weReGoingGreat = true; if (startVecStatus == ON_THE_PLANE) startVecCandidate += (0.1f - startVecDistToPlane) * curPlaneNormal; if (targetVecStatus == ON_THE_PLANE) targetVecCandidate += (0.1f - targetVecDistToPlane) * curPlaneNormal; } #undef FRONT_OF_PLANE #undef ON_THE_PLANE #undef BEHIND_THE_PLANE } if (!weReGoingGreat) { CVector avgOfColPoints = (colCornerPos[0] + colCornerPos[1] + colCornerPos[2] + colCornerPos[3]) / 4.f; float radius = 0.0f; // Find radius of col box of the entity we follow for (int i=0; i<4; i++) { float cornerDist = (colCornerPos[i] - avgOfColPoints).MagnitudeSqr(); if (cornerDist > radius) radius = cornerDist; } CColSphere followedEntSphere; followedEntSphere.Set(Sqrt(radius) * 1.1f, avgOfColPoints, 0, 0); CVector distToDest = m_followPathDestPos - GetPosition(); distToDest.z = 0.f; if (distToDest.Magnitude() == 0.0f) return false; distToDest.Normalise(); // Entity we follow doesn't go toward destination anymore, abort the following. if (!followedEntSphere.IntersectRay(GetPosition(), distToDest, startVecCandidate, targetVecCandidate)) { m_pathNodeTimer = 0; if (m_nPedState == PED_FOLLOW_PATH) RestorePreviousState(); return false; } } int lastPlaneBehindUs = -1; int lastPlaneInFrontOfUs = -1; CVector oldstartVecCandidate = startVecCandidate; CVector oldDirToGo = targetVecCandidate - startVecCandidate; oldDirToGo.Normalise(); // At least one plane should be between target and us. for (int i=0; i<4; i++) { CVector curPlaneNormal = colBoxPlaneNormal[i]; float minusGlobalCornerPos = dotProdCorrection[i]; float startVecDistToPlane = DotProduct(curPlaneNormal, startVecCandidate) + minusGlobalCornerPos; float targetVecDistToPlane = DotProduct(curPlaneNormal, targetVecCandidate) + minusGlobalCornerPos; if (startVecDistToPlane > 0.0f && targetVecDistToPlane < 0.0f) { lastPlaneInFrontOfUs = i; startVecCandidate = -(DotProduct(oldstartVecCandidate, curPlaneNormal) + minusGlobalCornerPos) / DotProduct(oldDirToGo, curPlaneNormal) * oldDirToGo + oldstartVecCandidate; } else if (startVecDistToPlane < 0.0f && targetVecDistToPlane > 0.0f) { lastPlaneBehindUs = i; targetVecCandidate = -(DotProduct(oldstartVecCandidate, curPlaneNormal) + minusGlobalCornerPos) / DotProduct(oldDirToGo, curPlaneNormal) * oldDirToGo + oldstartVecCandidate; } } CVector destsVariant1[5]; CVector destsVariant2[5]; // If not, followed entity diverged from route and we should abort the following. if (lastPlaneBehindUs >= 0 && lastPlaneInFrontOfUs >= 0) { int planeInFrontCircular = (lastPlaneInFrontOfUs + 4) % -4; int planeInFrontCircularMinusOne = (lastPlaneInFrontOfUs + 3) % -4; int planeInBehindCircular = (lastPlaneBehindUs + 4) % -4; int planeInBehindCircularMinusOne = (lastPlaneBehindUs + 3) % -4; destsVariant1[0] = GetPosition(); destsVariant1[1] = colCornerPos[planeInFrontCircularMinusOne]; int destsVar1LastNode = 2; for(; planeInFrontCircularMinusOne != planeInBehindCircular; destsVar1LastNode++) { planeInFrontCircularMinusOne = (planeInFrontCircularMinusOne + 3) % -4; destsVariant1[destsVar1LastNode] = colCornerPos[planeInFrontCircularMinusOne]; } destsVariant1[destsVar1LastNode] = m_followPathDestPos; destsVariant2[0] = GetPosition(); destsVariant2[1] = colCornerPos[planeInFrontCircular]; int destsVar2LastNode = 2; for (; planeInFrontCircular != planeInBehindCircularMinusOne; destsVar2LastNode++) { planeInFrontCircular = (planeInFrontCircular + 5) % -4; destsVariant2[destsVar2LastNode] = colCornerPos[planeInFrontCircular]; } destsVariant2[destsVar2LastNode] = m_followPathDestPos; CEntity *foundEnt1 = nil; int dests1isOk = true; int nodeToStopDestsVar1 = destsVar1LastNode + 1; CVector avgOfColPoints2 = (colCornerPos[0] + colCornerPos[1] + colCornerPos[2] + colCornerPos[3]) / 4.f; CVector prevDestVar1 = destsVariant1[0]; for (int i = 1; i < destsVar1LastNode + 1; i++) { CVector *curDestVar1 = &destsVariant1[i]; CVector routeNormalHalf = *curDestVar1 - prevDestVar1; routeNormalHalf.z = 0.f; routeNormalHalf.Normalise(); routeNormalHalf *= 0.5f; float oldX = -routeNormalHalf.x; routeNormalHalf.z = 0.0f; routeNormalHalf.x = routeNormalHalf.y; routeNormalHalf.y = oldX; if (DotProduct(*curDestVar1 - avgOfColPoints2, routeNormalHalf) < 0.0f) routeNormalHalf *= -1.f; CColPoint foundCol; bool foundObstacle = CWorld::ProcessLineOfSight(prevDestVar1, *curDestVar1, foundCol, foundEnt1, true, true, true, true, false, false, false, false); if (!foundObstacle) foundObstacle = CWorld::ProcessLineOfSight(prevDestVar1 + routeNormalHalf, *curDestVar1 + routeNormalHalf, foundCol, foundEnt1, true, true, true, true, false, false, false, false); if (foundObstacle) { if (foundEnt1 == m_followPathWalkAroundEnt || foundEnt1 == this || foundEnt1 == m_pSeekTarget) { foundEnt1 = nil; } else { if (!foundEnt1->IsPed()) { dests1isOk = false; nodeToStopDestsVar1 = i; break; } if (((CPed*)foundEnt1)->m_nPedState == PED_IDLE) { dests1isOk = false; nodeToStopDestsVar1 = i; break; } if (DotProduct(*curDestVar1 - prevDestVar1, foundEnt1->GetForward()) < 0.f) { dests1isOk = false; nodeToStopDestsVar1 = i; break; } if (((CPed*)foundEnt1)->m_pedInObjective == this) { dests1isOk = false; nodeToStopDestsVar1 = i; break; } } } prevDestVar1 = *curDestVar1; } CEntity *foundEnt2 = nil; int dests2isOk = true; int nodeToStopDestsVar2 = destsVar2LastNode + 1; CVector prevDestVar2 = destsVariant2[0]; for (int i = 1; i < destsVar2LastNode + 1; i++) { CVector *curDestVar2 = &destsVariant2[i]; CVector routeNormalHalf = *curDestVar2 - prevDestVar2; routeNormalHalf.z = 0.f; routeNormalHalf.Normalise(); routeNormalHalf *= 0.5f; float oldX = -routeNormalHalf.x; routeNormalHalf.z = 0.0f; routeNormalHalf.x = routeNormalHalf.y; routeNormalHalf.y = oldX; if (DotProduct(*curDestVar2 - avgOfColPoints2, routeNormalHalf) < 0.0f) routeNormalHalf *= -1.f; CColPoint foundCol; bool foundObstacle = CWorld::ProcessLineOfSight(prevDestVar2, *curDestVar2, foundCol, foundEnt2, true, true, true, true, false, false, false, false); if (!foundObstacle) foundObstacle = CWorld::ProcessLineOfSight(prevDestVar2 + routeNormalHalf, *curDestVar2 + routeNormalHalf, foundCol, foundEnt2, true, true, true, true, false, false, false, false); if (foundObstacle) { if (foundEnt2 == m_followPathWalkAroundEnt || foundEnt2 == this || foundEnt2 == m_pSeekTarget) { foundEnt2 = 0; } else { if (!foundEnt2->IsPed()) { dests2isOk = false; nodeToStopDestsVar2 = i; break; } if (((CPed*)foundEnt2)->m_nPedState == PED_IDLE) { dests2isOk = false; nodeToStopDestsVar2 = i; break; } if (DotProduct(*curDestVar2 - prevDestVar2, foundEnt2->GetForward()) < 0.f) { dests2isOk = false; nodeToStopDestsVar2 = i; break; } if (((CPed*)foundEnt2)->m_pedInObjective == this) { dests2isOk = false; nodeToStopDestsVar2 = i; break; } } } prevDestVar2 = *curDestVar2; } float destTotalLengthVar1 = 0.0f; for(int i=0; i < destsVar1LastNode; i++){ destTotalLengthVar1 += (destsVariant1[i + 1] - destsVariant1[i]).Magnitude(); } float destTotalLengthVar2 = 0.0f; for (int i = 0; i < destsVar2LastNode; i++) { destTotalLengthVar2 += (destsVariant2[i + 1] - destsVariant2[i]).Magnitude(); } int destVariantToUse; if (dests1isOk && dests2isOk) { if (destTotalLengthVar1 < destTotalLengthVar2) destVariantToUse = 1; else destVariantToUse = 2; } else if (dests1isOk) { destVariantToUse = 1; } else if (dests2isOk) { destVariantToUse = 2; } else if (nodeToStopDestsVar1 == 1 && nodeToStopDestsVar2 > 1) { destVariantToUse = 2; } else if (nodeToStopDestsVar1 > 1 && nodeToStopDestsVar2 == 1) { destVariantToUse = 1; } else if (foundEnt1 == foundEnt2) { if (destTotalLengthVar1 < destTotalLengthVar2) destVariantToUse = 1; else destVariantToUse = 2; } else if (foundEnt1->GetColModel()->boundingSphere.radius >= foundEnt2->GetColModel()->boundingSphere.radius) { destVariantToUse = 2; } else { destVariantToUse = 1; } if (destVariantToUse == 1) { ClearFollowPath(); for (int i = 1; i < destsVar1LastNode; i++) { CPathNode* nextNode = &m_pathNodeObjPool[m_nNumPathNodes]; nextNode->SetPosition(destsVariant1[i]); m_pathNodesToGo[m_nNumPathNodes++] = nextNode; } } else if (destVariantToUse == 2) { ClearFollowPath(); for (int i = 1; i < destsVar2LastNode; i++) { CPathNode *nextNode = &m_pathNodeObjPool[m_nNumPathNodes]; nextNode->SetPosition(destsVariant2[i]); m_pathNodesToGo[m_nNumPathNodes++] = nextNode; } } if (m_nNumPathNodes != 0) { PedState oldLastState = m_nLastPedState; m_nLastPedState = PED_NONE; SetStoredState(); if (m_nLastPedState == PED_NONE) m_nLastPedState = oldLastState; SetPedState(PED_FOLLOW_PATH); SetMoveState(m_followPathMoveState); return true; } else { m_pathNodeTimer = 0; if (m_nPedState == PED_FOLLOW_PATH) RestorePreviousState(); return false; } } else { m_pathNodeTimer = 0; if (m_nPedState == PED_FOLLOW_PATH) RestorePreviousState(); return false; } } void CPed::ClearFollowPath() { for (int i = 0; i < ARRAY_SIZE(m_pathNodesToGo); i++) { m_pathNodesToGo[i] = nil; } m_nNumPathNodes = 0; m_nCurPathNodeId = 0; } void CPed::FollowPath(void) { m_pCurPathNode = m_pathNodesToGo[m_nCurPathNodeId]; if (m_pathNodeTimer > 0 && CTimer::GetTimeInMilliseconds() > m_pathNodeTimer) { RestorePreviousState(); ClearFollowPath(); m_pathNodeTimer = 0; } else { if (m_pathNodesToGo[m_nCurPathNodeId]) { m_vecSeekPos.x = m_pathNodesToGo[m_nCurPathNodeId]->GetPosition().x; m_vecSeekPos.y = m_pathNodesToGo[m_nCurPathNodeId]->GetPosition().y; m_vecSeekPos.z = GetPosition().z; if (Seek()) { if (m_nCurPathNodeId == m_nNumPathNodes) { RestorePreviousState(); ClearFollowPath(); SetFollowPath(m_followPathDestPos, m_followPathAbortDist, m_followPathMoveState, m_followPathWalkAroundEnt, m_followPathTargetEnt, m_pathNodeTimer - CTimer::GetTimeInMilliseconds()); } } } else { RestorePreviousState(); ClearFollowPath(); m_pathNodeTimer = 0; } } } void CPed::SetEvasiveStep(CEntity *reason, uint8 animType) { AnimationId stepAnim; if (m_nPedState == PED_STEP_AWAY || !IsPedInControl() || ((IsPlayer() || !bRespondsToThreats) && animType == 0)) return; float angleToFace = CGeneral::GetRadianAngleBetweenPoints( reason->GetPosition().x, reason->GetPosition().y, GetPosition().x, GetPosition().y); angleToFace = CGeneral::LimitRadianAngle(angleToFace); m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); float neededTurn = Abs(angleToFace - m_fRotationCur); bool vehPressedHorn = false; if (neededTurn > PI) neededTurn = TWOPI - neededTurn; CVehicle *veh = (CVehicle*)reason; if (reason->IsVehicle() && veh->IsCar()) { if (veh->m_nCarHornTimer != 0) { vehPressedHorn = true; if (!IsPlayer()) animType = 1; } } if (neededTurn <= DEGTORAD(90.0f) || veh->GetModelIndex() == MI_RCBANDIT || vehPressedHorn || animType != 0) { SetLookFlag(veh, true); if ((CGeneral::GetRandomNumber() & 1) && veh->GetModelIndex() != MI_RCBANDIT && animType == 0) { stepAnim = ANIM_STD_HAILTAXI; } else { float vehDirection = CGeneral::GetRadianAngleBetweenPoints( veh->m_vecMoveSpeed.x, veh->m_vecMoveSpeed.y, 0.0f, 0.0f); // Let's turn our back to the "reason" angleToFace += PI; if (angleToFace > PI) angleToFace -= TWOPI; // We don't want to run towards car's direction float dangerZone = angleToFace - vehDirection; dangerZone = CGeneral::LimitRadianAngle(dangerZone); // So, add or subtract 90deg (jump to left/right) according to that if (dangerZone > 0.0f) angleToFace = vehDirection - HALFPI; else angleToFace = vehDirection + HALFPI; stepAnim = ANIM_STD_NUM; if (animType == 0 || animType == 1) stepAnim = ANIM_STD_EVADE_STEP; else if (animType == 2) stepAnim = ANIM_STD_HANDSCOWER; } if (!RpAnimBlendClumpGetAssociation(GetClump(), stepAnim)) { CAnimBlendAssociation *stepAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, stepAnim, 8.0f); stepAssoc->flags &= ~ASSOC_DELETEFADEDOUT; stepAssoc->SetFinishCallback(PedEvadeCB, this); if (animType == 0) Say(SOUND_PED_EVADE); m_fRotationCur = CGeneral::LimitRadianAngle(angleToFace); ClearAimFlag(); SetStoredState(); SetPedState(PED_STEP_AWAY); } } } void CPed::SetEvasiveDive(CPhysical *reason, uint8 onlyRandomJump) { if (!IsPedInControl() || !bRespondsToThreats) return; CAnimBlendAssociation *animAssoc; float angleToFace, neededTurn; bool handsUp = false; angleToFace = m_fRotationCur; CVehicle *veh = (CVehicle*) reason; if (reason->IsVehicle() && veh->m_vehType == VEHICLE_TYPE_CAR && veh->m_nCarHornTimer != 0 && !IsPlayer()) { onlyRandomJump = true; } if (onlyRandomJump) { if (reason) { // Simple version of my bug fix below. Doesn't calculate "danger zone", selects jump direction randomly. // Also doesn't include random hands up, sound etc. Only used on player ped and peds running from gun shots. float vehDirection = CGeneral::GetRadianAngleBetweenPoints( veh->m_vecMoveSpeed.x, veh->m_vecMoveSpeed.y, 0.0f, 0.0f); angleToFace = (CGeneral::GetRandomNumber() & 1) * PI + (-0.5f*PI) + vehDirection; angleToFace = CGeneral::LimitRadianAngle(angleToFace); } } else { if (IsPlayer()) { ((CPlayerPed*)this)->m_nEvadeAmount = 5; ((CPlayerPed*)this)->m_pEvadingFrom = reason; reason->RegisterReference((CEntity**) &((CPlayerPed*)this)->m_pEvadingFrom); return; } angleToFace = CGeneral::GetRadianAngleBetweenPoints( reason->GetPosition().x, reason->GetPosition().y, GetPosition().x, GetPosition().y); angleToFace = CGeneral::LimitRadianAngle(angleToFace); m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); // FIX: Peds no more select dive direction randomly. Taken from SetEvasiveStep, last if statement inverted #ifdef FIX_BUGS float vehDirection = CGeneral::GetRadianAngleBetweenPoints( veh->m_vecMoveSpeed.x, veh->m_vecMoveSpeed.y, 0.0f, 0.0f); // Let's turn our back to the "reason" angleToFace += PI; if (angleToFace > PI) angleToFace -= 2 * PI; // We don't want to dive towards car's direction float dangerZone = angleToFace - vehDirection; dangerZone = CGeneral::LimitRadianAngle(dangerZone); // So, add or subtract 90deg (jump to left/right) according to that if (dangerZone > 0.0f) angleToFace = 0.5f * PI + vehDirection; else angleToFace = vehDirection - 0.5f * PI; #endif neededTurn = Abs(angleToFace - m_fRotationCur); if (neededTurn > PI) neededTurn = 2 * PI - neededTurn; if (neededTurn <= 0.5f*PI) { if (CGeneral::GetRandomNumber() & 1) handsUp = true; } else { if (CGeneral::GetRandomNumber() & 7) return; } // VC's primitve solution to dive direction problem, see above for better one. This part doesn't exist on III at all #ifndef FIX_BUGS angleToFace += HALFPI; if (CGeneral::GetRandomNumber() & 1) angleToFace -= PI; #endif Say(SOUND_PED_EVADE); } if (handsUp || !IsPlayer() && m_pedStats->m_flags & STAT_NO_DIVE) { m_fRotationCur = angleToFace; ClearLookFlag(); ClearAimFlag(); SetLookFlag(reason, true); animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HANDSUP); if (animAssoc) return; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSUP, 8.0f); animAssoc->flags &= ~ASSOC_DELETEFADEDOUT; animAssoc->SetFinishCallback(PedEvadeCB, this); SetStoredState(); SetPedState(PED_STEP_AWAY); } else { m_fRotationCur = angleToFace; ClearLookFlag(); ClearAimFlag(); SetStoredState(); SetPedState(PED_DIVE_AWAY); animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_EVADE_DIVE, 8.0f); animAssoc->SetFinishCallback(PedEvadeCB, this); } if (reason->IsVehicle() && m_nPedType == PEDTYPE_COP) { if (veh->pDriver && veh->pDriver->IsPlayer()) { CWanted *wanted = FindPlayerPed()->m_pWanted; wanted->RegisterCrime_Immediately(CRIME_RECKLESS_DRIVING, GetPosition(), (uintptr)this, false); wanted->RegisterCrime_Immediately(CRIME_SPEEDING, GetPosition(), (uintptr)this, false); } } } void CPed::PedEvadeCB(CAnimBlendAssociation* animAssoc, void* arg) { CPed* ped = (CPed*)arg; if (!animAssoc) { ped->ClearLookFlag(); if (ped->m_nPedState == PED_DIVE_AWAY || ped->m_nPedState == PED_STEP_AWAY) ped->RestorePreviousState(); } else if (animAssoc->animId == ANIM_STD_EVADE_DIVE) { ped->bUpdateAnimHeading = true; ped->ClearLookFlag(); if (ped->m_nPedState == PED_DIVE_AWAY) { ped->m_getUpTimer = CTimer::GetTimeInMilliseconds() + 1; ped->SetPedState(PED_FALL); } animAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } else if (animAssoc->flags & ASSOC_FADEOUTWHENDONE) { ped->ClearLookFlag(); if (ped->m_nPedState == PED_DIVE_AWAY || ped->m_nPedState == PED_STEP_AWAY) ped->RestorePreviousState(); } else if (ped->m_nPedState != PED_ARRESTED) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; if (animAssoc->blendDelta >= 0.0f) animAssoc->blendDelta = -4.0f; ped->ClearLookFlag(); if (ped->m_nPedState == PED_DIVE_AWAY || ped->m_nPedState == PED_STEP_AWAY) { ped->RestorePreviousState(); } } } void CPed::SetDie(AnimationId animId, float delta, float speed) { if (m_attractor) GetPedAttractorManager()->DeRegisterPed(this, m_attractor); CPlayerPed *player = FindPlayerPed(); if (player == this) { if (!player->m_bCanBeDamaged) return; } m_threatEntity = nil; if (DyingOrDead()) return; CAnimBlendAssociation *dieAssoc = nil; if (m_nPedState == PED_FALL || m_nPedState == PED_GETUP) delta *= 0.5f; SetStoredState(); ClearAll(); m_fHealth = 0.0f; if (m_nPedState == PED_DRIVING) { if (!IsPlayer() && (!m_pMyVehicle || !m_pMyVehicle->IsBike())) FlagToDestroyWhenNextProcessed(); } else if (bInVehicle) { if (m_pVehicleAnim) m_pVehicleAnim->blendDelta = -1000.0f; } else if (EnteringCar()) { QuitEnteringCar(); } SetPedState(PED_DIE); if (animId == ANIM_STD_NUM) { bIsPedDieAnimPlaying = false; } else { dieAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animId, delta); if (speed > 0.0f) dieAssoc->speed = speed; dieAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; if (dieAssoc->IsRunning()) { dieAssoc->SetFinishCallback(FinishDieAnimCB, this); bIsPedDieAnimPlaying = true; } } Say(SOUND_PED_DEATH); if (m_nLastPedState == PED_ENTER_CAR || m_nLastPedState == PED_CARJACK) QuitEnteringCar(); if (!bInVehicle) StopNonPartialAnims(); m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds(); if (!CGame::nastyGame && animId == ANIM_STD_HIT_FLOOR) { if (dieAssoc) { dieAssoc->SetCurrentTime(dieAssoc->hierarchy->totalLength - 0.01f); dieAssoc->SetRun(); } } } void CPed::FinishDieAnimCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; if (ped->bIsPedDieAnimPlaying) ped->bIsPedDieAnimPlaying = false; } void CPed::SetDead(void) { if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DROWN)) bUsesCollision = false; m_fHealth = 0.0f; if (m_nPedState == PED_DRIVING) bIsVisible = false; SetPedState(PED_DEAD); m_pVehicleAnim = nil; m_pCollidingEntity = nil; CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); RemoveWeaponModel(weapon->m_nModelId); m_currentWeapon = WEAPONTYPE_UNARMED; CEventList::RegisterEvent(EVENT_INJURED_PED, EVENT_ENTITY_PED, this, nil, 250); if (this != FindPlayerPed()) { RemoveWeaponAnims(0, -1000.0f); CreateDeadPedWeaponPickups(); CreateDeadPedMoney(); } m_bloodyFootprintCountOrDeathTime = CTimer::GetTimeInMilliseconds(); m_deadBleeding = false; bDoBloodyFootprints = false; bVehExitWillBeInstant = false; CEventList::RegisterEvent(EVENT_DEAD_PED, EVENT_ENTITY_PED, this, nil, 1000); } void CPed::Die(void) { // UNUSED: This is a perfectly empty function. } void CPed::SetChat(CEntity *chatWith, uint32 time) { if (m_nPedState != PED_CHAT) { m_nLastPedState = PED_NONE; SetStoredState(); } SetPedState(PED_CHAT); SetMoveState(PEDMOVE_STILL); m_lookTimer = 0; SetLookFlag(chatWith, true); m_chatTimer = CTimer::GetTimeInMilliseconds() + time; m_lookTimer = CTimer::GetTimeInMilliseconds() + 3000; } void CPed::Chat(void) { // We're already looking to our partner if (bIsLooking && TurnBody()) ClearLookFlag(); if (!m_pLookTarget || !m_pLookTarget->IsPed()) { ClearChat(); return; } CPed *partner = (CPed*) m_pLookTarget; if (partner->m_nPedState != PED_CHAT) { ClearChat(); m_chatTimer = CTimer::GetTimeInMilliseconds() + 30000; if (partner->m_pedInObjective) { if (partner->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || partner->m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE) ReactToAttack(partner->m_pedInObjective); } return; } if (bIsTalking) { if (CGeneral::GetRandomNumber() < 512) { CAnimBlendAssociation *chatAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); if (chatAssoc) { chatAssoc->blendDelta = -4.0f; chatAssoc->flags |= ASSOC_DELETEFADEDOUT; } bIsTalking = false; } else Say(SOUND_PED_CHAT); } else { if (CGeneral::GetRandomNumber() < 20 && !RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_IDLE)) { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_XPRESS_SCRATCH, 4.0f); } if (!bIsTalking && !RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_IDLE)) { CAnimBlendAssociation *chatAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CHAT, 4.0f); float chatTime = CGeneral::GetRandomNumberInRange(0.0f, 3.0f); chatAssoc->SetCurrentTime(chatTime); bIsTalking = true; Say(SOUND_PED_CHAT); } } if (m_chatTimer && CTimer::GetTimeInMilliseconds() > m_chatTimer) { ClearChat(); m_chatTimer = CTimer::GetTimeInMilliseconds() + 30000; } } void CPed::ClearChat(void) { CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } bIsTalking = false; ClearLookFlag(); RestorePreviousState(); if (m_objective == OBJECTIVE_BUY_ICE_CREAM) { bBoughtIceCream = true; SetObjective(OBJECTIVE_NONE); SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); } } bool CPed::FacePhone(void) { // FIX: This function was broken since it's left unused early in development. #ifdef FIX_BUGS float phoneDir = CGeneral::GetRadianAngleBetweenPoints( gPhoneInfo.m_aPhones[m_phoneId].m_vecPos.x, gPhoneInfo.m_aPhones[m_phoneId].m_vecPos.y, GetPosition().x, GetPosition().y); SetLookFlag(phoneDir, false); bool turnDone = TurnBody(); if (turnDone) { SetIdle(); ClearLookFlag(); m_phoneTalkTimer = CTimer::GetTimeInMilliseconds() + 10000; } return turnDone; #else float currentRot = RADTODEG(m_fRotationCur); float phoneDir = CGeneral::GetRadianAngleBetweenPoints( gPhoneInfo.m_aPhones[m_phoneId].m_vecPos.x, gPhoneInfo.m_aPhones[m_phoneId].m_vecPos.y, GetPosition().x, GetPosition().y); SetLookFlag(phoneDir, false); phoneDir = CGeneral::LimitAngle(phoneDir); m_moved = CVector2D(0.0f, 0.0f); if (currentRot - 180.0f > phoneDir) phoneDir += 2 * 180.0f; else if (180.0f + currentRot < phoneDir) phoneDir -= 2 * 180.0f; float neededTurn = currentRot - phoneDir; if (Abs(neededTurn) <= 0.75f) { SetIdle(); ClearLookFlag(); m_phoneTalkTimer = CTimer::GetTimeInMilliseconds() + 10000; return true; } else { m_fRotationCur = DEGTORAD(currentRot - neededTurn * 0.2f); return false; } #endif } bool CPed::MakePhonecall(void) { if (CTimer::GetTimeInMilliseconds() <= m_phoneTalkTimer) return false; SetIdle(); gPhoneInfo.m_aPhones[m_phoneId].m_nState = PHONE_STATE_FREE; m_phoneId = -1; return true; } void StartTalkingOnMobileCB(CAnimBlendAssociation* assoc, void* arg) { CPed* ped = (CPed*)arg; if (ped->m_nPedState == PED_ANSWER_MOBILE) CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_TALK, 4.0f); } void FinishTalkingOnMobileCB(CAnimBlendAssociation *assoc, void *arg) { CPed *ped = (CPed*)arg; if (ped->m_storedWeapon != WEAPONTYPE_UNIDENTIFIED) { ped->RemoveWeaponModel(MI_MOBILE); ped->SetCurrentWeapon(ped->m_storedWeapon); ped->m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; } ped->m_lookTimer = 0; } void CPed::SetAnswerMobile(void) { if (m_nPedState != PED_ANSWER_MOBILE && !DyingOrDead()) { SetPedState(PED_ANSWER_MOBILE); RemoveWeaponAnims(GetWeapon()->m_eWeaponType, -4.0f); CAnimBlendAssociation *assoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_IN, 4.0f); assoc->SetFinishCallback(StartTalkingOnMobileCB, this); m_lookTimer = INT32_MAX; if (m_storedWeapon == WEAPONTYPE_UNIDENTIFIED) m_storedWeapon = GetWeapon()->m_eWeaponType; RemoveWeaponModel(-1); } } void CPed::ClearAnswerMobile(void) { if (m_nLastPedState == PED_ANSWER_MOBILE) m_nLastPedState = PED_NONE; if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_TALK)) { CAnimBlendAssociation *assoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_OUT, 8.0f); assoc->SetFinishCallback(FinishTalkingOnMobileCB, this); } else FinishTalkingOnMobileCB(nil, this); if (m_nPedState == PED_ANSWER_MOBILE) { m_nPedState = PED_IDLE; RestorePreviousState(); m_pVehicleAnim = nil; } } void CPed::AnswerMobile(void) { if (!IsPedInControl()) return; CAnimBlendAssociation *phoneInAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_IN); CAnimBlendAssociation *phoneOutAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_OUT); CAnimBlendAssociation *phoneTalkAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_TALK); if (phoneInAssoc || phoneTalkAssoc || phoneOutAssoc) { if (phoneInAssoc) { if (phoneInAssoc->currentTime >= 0.85f && !m_pWeaponModel) { CBaseModelInfo *phoneModel = CModelInfo::GetModelInfo(MI_MOBILE); m_pWeaponModel = (RpAtomic*)phoneModel->CreateInstance(); phoneModel->AddRef(); m_wepModelID = MI_MOBILE; // They copied AddWeaponModel and forgot that here // bool unused = IsPlayer(); } } else if (phoneOutAssoc) { if (phoneOutAssoc->currentTime >= 0.5f && phoneOutAssoc->currentTime - phoneOutAssoc->timeStep < 0.5f) { RemoveWeaponModel(MI_MOBILE); SetCurrentWeapon(m_storedWeapon); m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; } } } else { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PHONE_TALK, 4.0f); } } void CPed::Teleport(CVector pos) { CWorld::Remove(this); SetPosition(pos); bIsStanding = false; m_nPedStateTimer = 0; m_actionX = 0.0f; m_actionY = 0.0f; m_pDamageEntity = nil; CWorld::Add(this); } void CPed::SetSeekCar(CVehicle *car, uint32 doorNode) { if (m_nPedState == PED_SEEK_CAR) return; if (!CanSetPedState() || m_nPedState == PED_DRIVING) return; SetStoredState(); m_pSeekTarget = car; m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); m_carInObjective = car; m_carInObjective->RegisterReference((CEntity**) &m_carInObjective); m_pMyVehicle = car; m_pMyVehicle->RegisterReference((CEntity**) &m_pMyVehicle); // m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); m_vehDoor = doorNode; m_distanceToCountSeekDone = 0.5f; SetPedState(PED_SEEK_CAR); } void CPed::SeekCar(void) { CVehicle *vehToSeek = m_carInObjective; CVector dest(0.0f, 0.0f, 0.0f); if (!vehToSeek) { RestorePreviousState(); return; } if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) { if (!vehToSeek->IsBike() && m_vehDoor && m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { if (IsRoomToBeCarJacked()) { dest = GetPositionToOpenCarDoor(vehToSeek, m_vehDoor); } else if (m_nPedType == PEDTYPE_COP) { dest = GetPositionToOpenCarDoor(vehToSeek, CAR_DOOR_RF); } else { SetMoveState(PEDMOVE_STILL); } } else GetNearestDoor(vehToSeek, dest); } else { if (m_carJackTimer > CTimer::GetTimeInMilliseconds()) { SetMoveState(PEDMOVE_STILL); return; } if (vehToSeek->GetModelIndex() == MI_COACH) { GetNearestDoor(vehToSeek, dest); } else { if (vehToSeek->IsTrain()) { if (vehToSeek->GetStatus() != STATUS_TRAIN_NOT_MOVING) { RestorePreviousObjective(); RestorePreviousState(); return; } if (!GetNearestTrainDoor(vehToSeek, dest)) { RestorePreviousObjective(); RestorePreviousState(); return; } } else { if (!GetNearestPassengerDoor(vehToSeek, dest)) { if (vehToSeek->m_nNumPassengers == vehToSeek->m_nNumMaxPassengers) { RestorePreviousObjective(); RestorePreviousState(); } else { SetMoveState(PEDMOVE_STILL); } bVehEnterDoorIsBlocked = true; return; } bVehEnterDoorIsBlocked = false; } } } if (dest.x == 0.0f && dest.y == 0.0f) { #ifdef FIX_BUGS if ((!IsPlayer() && CharCreatedBy != MISSION_CHAR) || vehToSeek->VehicleCreatedBy != MISSION_VEHICLE || vehToSeek->pDriver || !vehToSeek->CanPedOpenLocks(this)) { #else if ((!IsPlayer() && CharCreatedBy != MISSION_CHAR) || vehToSeek->VehicleCreatedBy != MISSION_VEHICLE || vehToSeek->pDriver) { #endif RestorePreviousState(); if (IsPlayer()) { ClearObjective(); } else if (CharCreatedBy == RANDOM_CHAR) { m_carJackTimer = CTimer::GetTimeInMilliseconds() + 30000; } SetMoveState(PEDMOVE_STILL); TheCamera.ClearPlayerWeaponMode(); CCarCtrl::RemoveFromInterestingVehicleList(vehToSeek); return; } dest = vehToSeek->GetPosition(); if (bCollidedWithMyVehicle) { WarpPedIntoCar(m_pMyVehicle); return; } } bool foundBetterPosToSeek = PossiblyFindBetterPosToSeekCar(&dest, vehToSeek); m_vecSeekPos = dest; float distToDestSqr = (m_vecSeekPos - GetPosition()).MagnitudeSqr(); if (bIsRunning || vehToSeek->pDriver && distToDestSqr > sq(2.0f) && (Abs(vehToSeek->m_vecMoveSpeed.x) > 0.01f || Abs(vehToSeek->m_vecMoveSpeed.y) > 0.01f)) SetMoveState(PEDMOVE_RUN); else if (distToDestSqr < sq(2.0f)) SetMoveState(PEDMOVE_WALK); if (distToDestSqr >= 1.0f) bCanPedEnterSeekedCar = false; else if (2.0f * vehToSeek->GetColModel()->boundingBox.max.x > distToDestSqr) bCanPedEnterSeekedCar = true; if (vehToSeek->m_nGettingInFlags & GetCarDoorFlag(m_vehDoor)) bVehEnterDoorIsBlocked = true; else bVehEnterDoorIsBlocked = false; // Arrived to the car if (Seek()) { if (!foundBetterPosToSeek) { if (1.6f + GetPosition().z > dest.z && GetPosition().z - 0.5f < dest.z) { #ifdef GTA_TRAIN if (vehToSeek->IsTrain()) { SetEnterTrain(vehToSeek, m_vehDoor); } else #endif { m_fRotationCur = m_fRotationDest; if (!bVehEnterDoorIsBlocked) { vehToSeek->SetIsStatic(false); if (m_objective == OBJECTIVE_SOLICIT_VEHICLE) { SetSolicit(1000); } else if (m_objective == OBJECTIVE_BUY_ICE_CREAM) { SetBuyIceCream(); } else if (vehToSeek->m_nNumGettingIn < vehToSeek->m_nNumMaxPassengers + 1 && vehToSeek->CanPedEnterCar()) { switch (vehToSeek->GetStatus()) { case STATUS_PLAYER: case STATUS_SIMPLE: case STATUS_PHYSICS: case STATUS_PLAYER_DISABLED: if (vehToSeek->IsBike()) { if ((!m_leader || m_leader != vehToSeek->pDriver) && ((m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_WINDSCREEN) && vehToSeek->pDriver || (m_vehDoor == CAR_DOOR_LR || m_vehDoor == CAR_DOOR_RR) && vehToSeek->pPassengers[0])) { SetCarJack(vehToSeek); } else { SetEnterCar(vehToSeek, m_vehDoor); } } else if (!vehToSeek->bIsBus && (!m_leader || m_leader != vehToSeek->pDriver) && (m_vehDoor == CAR_DOOR_LF && vehToSeek->pDriver || m_vehDoor == CAR_DOOR_RF && vehToSeek->pPassengers[0] || m_vehDoor == CAR_DOOR_LR && vehToSeek->pPassengers[1] || m_vehDoor == CAR_DOOR_RR && vehToSeek->pPassengers[2])) { SetCarJack(vehToSeek); if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && m_vehDoor != CAR_DOOR_LF) vehToSeek->pDriver->bFleeAfterExitingCar = true; } else { SetEnterCar(vehToSeek, m_vehDoor); } break; case STATUS_ABANDONED: if (vehToSeek->IsBike()) { if ((m_vehDoor == CAR_DOOR_LR || m_vehDoor == CAR_DOOR_RR) && vehToSeek->pPassengers[0]) { if (vehToSeek->pPassengers[0]->bDontDragMeOutCar) { if (IsPlayer()) SetEnterCar(vehToSeek, m_vehDoor); } else { SetCarJack(vehToSeek); } } else { SetEnterCar(vehToSeek, m_vehDoor); } } else if (m_vehDoor == CAR_DOOR_RF && vehToSeek->pPassengers[0]) { if (vehToSeek->pPassengers[0]->bDontDragMeOutCar) { if (IsPlayer()) SetEnterCar(vehToSeek, m_vehDoor); } else { SetCarJack(vehToSeek); } } else { SetEnterCar(vehToSeek, m_vehDoor); } break; case STATUS_WRECKED: SetIdle(); break; default: return; } } else { RestorePreviousState(); } } else { SetMoveState(PEDMOVE_STILL); } } } } } } bool CPed::CheckForExplosions(CVector2D &area) { int event = 0; if (CEventList::FindClosestEvent(EVENT_EXPLOSION, GetPosition(), &event)) { area.x = gaEvent[event].posn.x; area.y = gaEvent[event].posn.y; CEntity *actualEntity = nil; switch (gaEvent[event].entityType) { case EVENT_ENTITY_PED: actualEntity = CPools::GetPed(gaEvent[event].entityRef); break; case EVENT_ENTITY_VEHICLE: actualEntity = CPools::GetVehicle(gaEvent[event].entityRef); break; case EVENT_ENTITY_OBJECT: actualEntity = CPools::GetObject(gaEvent[event].entityRef); break; default: break; } if (actualEntity) { m_pEventEntity = actualEntity; m_pEventEntity->RegisterReference((CEntity **) &m_pEventEntity); bGonnaInvestigateEvent = true; } else bGonnaInvestigateEvent = false; CEventList::ClearEvent(event); return true; } else if (CEventList::FindClosestEvent(EVENT_FIRE, GetPosition(), &event)) { area.x = gaEvent[event].posn.x; area.y = gaEvent[event].posn.y; CEventList::ClearEvent(event); bGonnaInvestigateEvent = false; return true; } bGonnaInvestigateEvent = false; return false; } CPed * CPed::CheckForGunShots(void) { int event; if (CEventList::FindClosestEvent(EVENT_GUNSHOT, GetPosition(), &event)) { if (gaEvent[event].entityType == EVENT_ENTITY_PED) { // Probably due to we don't want peds to go gunshot area? (same on VC) bGonnaInvestigateEvent = false; return CPools::GetPed(gaEvent[event].entityRef); } } bGonnaInvestigateEvent = false; return nil; } CPed * CPed::CheckForDeadPeds(void) { int event; if (CEventList::FindClosestEvent(EVENT_DEAD_PED, GetPosition(), &event)) { int pedHandle = gaEvent[event].entityRef; if (pedHandle && gaEvent[event].entityType == EVENT_ENTITY_PED) { bGonnaInvestigateEvent = true; return CPools::GetPed(pedHandle); } } bGonnaInvestigateEvent = false; return nil; } bool CPed::IsPlayer(void) const { #if 0 return m_nPedType == PEDTYPE_PLAYER1; // Original #else // We still have those in enum, so let's also check for them. return m_nPedType == PEDTYPE_PLAYER1 || m_nPedType == PEDTYPE_PLAYER2 || m_nPedType == PEDTYPE_PLAYER3 || m_nPedType == PEDTYPE_PLAYER4; #endif } bool CPed::IsGangMember(void) const { return m_nPedType >= PEDTYPE_GANG1 && m_nPedType <= PEDTYPE_GANG9; } bool IsPedPointerValid(CPed* pPed) { if (!IsPedPointerValid_NotInWorld(pPed)) return false; if (pPed->bInVehicle && pPed->m_pMyVehicle) return IsEntityPointerValid(pPed->m_pMyVehicle); return pPed->m_entryInfoList.first || pPed == FindPlayerPed(); } bool IsPedPointerValid_NotInWorld(CPed* pPed) { if (!pPed) return false; int index = CPools::GetPedPool()->GetJustIndex_NoFreeAssert(pPed); #ifdef FIX_BUGS if (index < 0 || index >= NUMPEDS) #else if (index < 0 || index > NUMPEDS) #endif return false; return true; } bool CPed::IsPointerValid(void) { int pedIndex = CPools::GetPedPool()->GetIndex(this) >> 8; if (pedIndex < 0 || pedIndex >= NUMPEDS) return false; if (m_entryInfoList.first || FindPlayerPed() == this) return true; return false; } void CPed::SetPedPositionInCar(void) { bool notYet = false; if (CReplay::IsPlayingBack()) return; if (bChangedSeat) { if (m_pMyVehicle->IsBike()) { if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_JUMPON_LHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_JUMPON_RHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_KICK)) { LineUpPedWithCar(LINE_UP_TO_CAR_START); return; } bChangedSeat = false; } else { if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_GET_IN_LHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_GET_IN_LO_LHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_CLOSE_DOOR_LHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_CLOSE_DOOR_LO_LHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SHUFFLE_RHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SHUFFLE_LO_RHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_VAN_GET_IN_REAR_LHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_VAN_GET_IN_REAR_RHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_COACH_GET_IN_LHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_COACH_GET_IN_RHS) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_JUMP_IN_LO_LHS)) { notYet = true; } } if (notYet) { LineUpPedWithCar(LINE_UP_TO_CAR_START); bChangedSeat = false; return; } } CVehicleModelInfo *vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(m_pMyVehicle->GetModelIndex()); CMatrix newMat(m_pMyVehicle->GetMatrix()); CVector seatPos; if (m_pMyVehicle->pDriver == this) { seatPos = vehModel->GetFrontSeatPosn(); if (!m_pMyVehicle->IsBoat() && !m_pMyVehicle->IsBike()) seatPos.x = -seatPos.x; } else if (m_pMyVehicle->pPassengers[0] == this) { seatPos = m_pMyVehicle->IsBike() ? vehModel->m_positions[CAR_POS_BACKSEAT]: vehModel->GetFrontSeatPosn(); } else if (m_pMyVehicle->pPassengers[1] == this) { seatPos = vehModel->m_positions[CAR_POS_BACKSEAT]; seatPos.x = -seatPos.x; } else { if (m_pMyVehicle->pPassengers[2] == this) { seatPos = vehModel->m_positions[CAR_POS_BACKSEAT]; } else { seatPos = vehModel->GetFrontSeatPosn(); } } if (m_pMyVehicle->IsBike()) { ((CBike*)m_pMyVehicle)->CalculateLeanMatrix(); newMat = ((CBike*)m_pMyVehicle)->m_leanMatrix; } newMat.GetPosition() += Multiply3x3(newMat, seatPos); // Already done below (SetTranslate(0.0f, 0.0f, 0.0f)) // tempMat.SetUnity(); // Rear seats on vans don't face to front, so rotate them HALFPI. if (m_pMyVehicle->bIsVan) { CMatrix tempMat; if (m_pMyVehicle->pPassengers[1] == this) { m_fRotationCur = m_pMyVehicle->GetForward().Heading() - HALFPI; tempMat.SetTranslate(0.0f, 0.0f, 0.0f); tempMat.RotateZ(-HALFPI); tempMat.Translate(0.0f, 0.6f, 0.0f); newMat = newMat * tempMat; } else if (m_pMyVehicle->pPassengers[2] == this) { m_fRotationCur = m_pMyVehicle->GetForward().Heading() + HALFPI; tempMat.SetTranslate(0.0f, 0.0f, 0.0f); tempMat.RotateZ(HALFPI); newMat = newMat * tempMat; } else { m_fRotationCur = m_pMyVehicle->GetForward().Heading(); } } else { m_fRotationCur = m_pMyVehicle->GetForward().Heading(); } GetMatrix() = newMat; } void CPed::LookForSexyPeds(void) { if ((!IsPedInControl() && m_nPedState != PED_DRIVING) || m_lookTimer >= CTimer::GetTimeInMilliseconds() || m_nPedType != PEDTYPE_CIVMALE) return; for (int i = 0; i < m_numNearPeds; i++) { if (CanSeeEntity(m_nearPeds[i])) { if ((GetPosition() - m_nearPeds[i]->GetPosition()).Magnitude() < 10.0f) { CPed *nearPed = m_nearPeds[i]; if ((nearPed->m_pedStats->m_sexiness > m_pedStats->m_sexiness) && nearPed->m_nPedType == PEDTYPE_CIVFEMALE) { SetLookFlag(nearPed, true); m_lookTimer = CTimer::GetTimeInMilliseconds() + 4000; Say(SOUND_PED_CHAT_SEXY); return; } } } } m_lookTimer = CTimer::GetTimeInMilliseconds() + 10000; } void CPed::LookForSexyCars(void) { CEntity *vehicles[8]; CVehicle *veh; int foundVehId = 0; int bestPriceYet = 0; int16 lastVehicle; if (!IsPedInControl() && m_nPedState != PED_DRIVING) return; if (m_lookTimer < CTimer::GetTimeInMilliseconds()) { CWorld::FindObjectsInRange(GetPosition(), 10.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for (int vehId = 0; vehId < lastVehicle; vehId++) { veh = (CVehicle*)vehicles[vehId]; if (veh != m_pMyVehicle && bestPriceYet < veh->pHandling->nMonetaryValue) { foundVehId = vehId; bestPriceYet = veh->pHandling->nMonetaryValue; } } if (lastVehicle > 0 && bestPriceYet > 40000) SetLookFlag(vehicles[foundVehId], false); m_lookTimer = CTimer::GetTimeInMilliseconds() + 10000; } } bool CPed::LookForInterestingNodes(void) { CBaseModelInfo *model; CPtrNode *ptrNode; CVector effectDist; C2dEffect *effect; CMatrix *objMat; if ((CTimer::GetFrameCounter() + (m_randomSeed % 256)) & 7 || CTimer::GetTimeInMilliseconds() <= m_chatTimer) { return false; } bool found = false; uint8 randVal = CGeneral::GetRandomNumber() % 256; int minX = CWorld::GetSectorIndexX(GetPosition().x - CHECK_NEARBY_THINGS_MAX_DIST); if (minX < 0) minX = 0; int minY = CWorld::GetSectorIndexY(GetPosition().y - CHECK_NEARBY_THINGS_MAX_DIST); if (minY < 0) minY = 0; int maxX = CWorld::GetSectorIndexX(GetPosition().x + CHECK_NEARBY_THINGS_MAX_DIST); #ifdef FIX_BUGS if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; #else if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; #endif int maxY = CWorld::GetSectorIndexY(GetPosition().y + CHECK_NEARBY_THINGS_MAX_DIST); #ifdef FIX_BUGS if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; #else if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; #endif for (int curY = minY; curY <= maxY && !found; curY++) { for (int curX = minX; curX <= maxX && !found; curX++) { CSector *sector = CWorld::GetSector(curX, curY); for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES].first; ptrNode && !found; ptrNode = ptrNode->next) { CVehicle *veh = (CVehicle*)ptrNode->item; model = veh->GetModelInfo(); if (model->GetNum2dEffects() != 0) { for (int e = 0; e < model->GetNum2dEffects(); e++) { effect = model->Get2dEffect(e); if (effect->type == EFFECT_ATTRACTOR && effect->attractor.probability >= randVal) { objMat = &veh->GetMatrix(); CVector effectPos = veh->GetMatrix() * effect->pos; effectDist = effectPos - GetPosition(); if (effectDist.MagnitudeSqr() < sq(8.0f)) { found = true; break; } } } } } for (ptrNode = sector->m_lists[ENTITYLIST_OBJECTS].first; ptrNode && !found; ptrNode = ptrNode->next) { CObject *obj = (CObject*)ptrNode->item; model = CModelInfo::GetModelInfo(obj->GetModelIndex()); if (model->GetNum2dEffects() != 0) { for (int e = 0; e < model->GetNum2dEffects(); e++) { effect = model->Get2dEffect(e); if (effect->type == EFFECT_ATTRACTOR && effect->attractor.probability >= randVal) { objMat = &obj->GetMatrix(); CVector effectPos = obj->GetMatrix() * effect->pos; effectDist = effectPos - GetPosition(); if (effectDist.MagnitudeSqr() < sq(8.0f)) { found = true; break; } } } } } for (ptrNode = sector->m_lists[ENTITYLIST_BUILDINGS].first; ptrNode && !found; ptrNode = ptrNode->next) { CBuilding *building = (CBuilding*)ptrNode->item; model = CModelInfo::GetModelInfo(building->GetModelIndex()); if (model->GetNum2dEffects() != 0) { for (int e = 0; e < model->GetNum2dEffects(); e++) { effect = model->Get2dEffect(e); if (effect->type == EFFECT_ATTRACTOR && effect->attractor.probability >= randVal) { objMat = &building->GetMatrix(); CVector effectPos = building->GetMatrix() * effect->pos; effectDist = effectPos - GetPosition(); if (effectDist.MagnitudeSqr() < sq(8.0f)) { found = true; break; } } } } } for (ptrNode = sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP].first; ptrNode && !found; ptrNode = ptrNode->next) { CBuilding *building = (CBuilding*)ptrNode->item; model = CModelInfo::GetModelInfo(building->GetModelIndex()); if (model->GetNum2dEffects() != 0) { for (int e = 0; e < model->GetNum2dEffects(); e++) { effect = model->Get2dEffect(e); if (effect->type == EFFECT_ATTRACTOR && effect->attractor.probability >= randVal) { objMat = &building->GetMatrix(); CVector effectPos = building->GetMatrix() * effect->pos; effectDist = effectPos - GetPosition(); if (effectDist.MagnitudeSqr() < sq(8.0f)) { found = true; break; } } } } } } } if (!found) return false; CVector effectFrontLocal = Multiply3x3(*objMat, effect->attractor.dir); float angleToFace = CGeneral::GetRadianAngleBetweenPoints(effectFrontLocal.x, effectFrontLocal.y, 0.0f, 0.0f); randVal = CGeneral::GetRandomNumber() % 256; if (randVal <= m_randomSeed % 256) { m_chatTimer = CTimer::GetTimeInMilliseconds() + 2000; SetLookFlag(angleToFace, true); SetLookTimer(1000); return false; } CVector2D effectPos = *objMat * effect->pos; switch (effect->attractor.type) { case ATTRACTORTYPE_ICECREAM: SetInvestigateEvent(EVENT_ICECREAM, effectPos, 0.1f, 15000, angleToFace); break; case ATTRACTORTYPE_STARE: SetInvestigateEvent(EVENT_SHOPSTALL, effectPos, 1.0f, CGeneral::GetRandomNumberInRange(8000, 10 * effect->attractor.probability + 8500), angleToFace); break; default: return true; } return true; } void PlayRandomAnimationsFromAnimBlock(CPed* ped, AssocGroupId animGroup, uint32 first, uint32 amount) { if (!ped->IsPedInControl()) return; const char *groupName = CAnimManager::GetAnimGroupName(animGroup); CAnimBlock *animBlock = CAnimManager::GetAnimationBlock(groupName); CAnimBlendAssociation *assoc; for (assoc = RpAnimBlendClumpGetFirstAssociation(ped->GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { int first = animBlock->firstIndex; int index = assoc->hierarchy - CAnimManager::GetAnimation(0); if (index >= first && index < first + animBlock->numAnims) { break; } } if (CTimer::GetTimeInMilliseconds() > ped->m_nWaitTimer && assoc) assoc->flags &= ~ASSOC_REPEAT; if (!assoc || assoc->blendDelta < 0.0f) { int selectedAnimOffset; do selectedAnimOffset = CGeneral::GetRandomNumberInRange(0, amount); while (assoc && first + selectedAnimOffset == assoc->animId); assoc = CAnimManager::BlendAnimation(ped->GetClump(), animGroup, (AnimationId)(first + selectedAnimOffset), 3.0f); assoc->SetFinishCallback(CPed::FinishedWaitCB, ped); if (assoc->flags & ASSOC_REPEAT) ped->m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(3000, 8000); else ped->m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 8000; } } void CPed::ClearWaitState(void) { CAnimBlendAssociation *assoc; switch (m_nWaitState) { case WAITSTATE_PLAYANIM_CHAT: case WAITSTATE_SIT_DOWN: case WAITSTATE_SIT_DOWN_RVRS: case WAITSTATE_SIT_UP: case WAITSTATE_SIT_IDLE: case WAITSTATE_USE_ATM: if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) { if (m_nWaitState == WAITSTATE_USE_ATM) { assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ATM); if (assoc) assoc->blendDelta = -8.0f; if (m_attractor) GetPedAttractorManager()->DeRegisterPed(this, m_attractor); } else if (m_nWaitState == WAITSTATE_PLAYANIM_CHAT) { assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); if (assoc) assoc->blendDelta = -8.0f; if (m_attractor) GetPedAttractorManager()->DeRegisterPed(this, m_attractor); } else if (m_nWaitState == WAITSTATE_SIT_DOWN || m_nWaitState == WAITSTATE_SIT_DOWN_RVRS || m_nWaitState == WAITSTATE_SIT_IDLE || m_nWaitState == WAITSTATE_SIT_UP) { switch (m_nWaitState) { case WAITSTATE_SIT_DOWN: assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_SEAT_DOWN); if (assoc) assoc->blendDelta = -8.0f; break; case WAITSTATE_SIT_IDLE: assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_SEAT_IDLE); if (assoc) assoc->blendDelta = -8.0f; break; case WAITSTATE_SIT_UP: assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_SEAT_UP); if (assoc) assoc->blendDelta = -8.0f; break; default: break; } if (m_attractor) GetPedAttractorManager()->DeRegisterPed(this, m_attractor); } } break; case WAITSTATE_RIOT: { CAnimBlock* riotAnimBlock = CAnimManager::GetAnimationBlock("riot"); for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { int first = riotAnimBlock->firstIndex; int index = assoc->hierarchy - CAnimManager::GetAnimation(0); if (index >= first && index < first + riotAnimBlock->numAnims) { assoc->blendDelta = -1000.0f; } } break; } case WAITSTATE_FAST_FALL: if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HIGHIMPACT_FRONT)) SetGetUp(); break; case WAITSTATE_BOMBER: assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DETONATE); if (assoc) assoc->blendDelta = -8.0f; break; case WAITSTATE_STRIPPER: { CAnimBlock* stripAnimBlock = CAnimManager::GetAnimationBlock("strip"); for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { int first = stripAnimBlock->firstIndex; int index = assoc->hierarchy - CAnimManager::GetAnimation(0); if (index >= first && index < first + stripAnimBlock->numAnims) { assoc->blendDelta = -1000.0f; } } break; } case WAITSTATE_LANCESITTING: assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_SUNBATHE_IDLE); if (assoc) assoc->blendDelta = -8.0f; break; case WAITSTATE_PLAYANIM_HANDSUP_SIMPLE: assoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HANDSUP); if (assoc) assoc->blendDelta = -8.0f; break; default: break; } m_nWaitState = WAITSTATE_FALSE; } void CPed::SetWaitState(eWaitState state, void *time) { AnimationId waitAnim = ANIM_STD_NUM; CAnimBlendAssociation *animAssoc; if (!IsPedInControl()) return; if (m_nWaitState == WAITSTATE_RIOT && state != WAITSTATE_FALSE) return; if (state != m_nWaitState) FinishedWaitCB(nil, this); switch (state) { case WAITSTATE_TRAFFIC_LIGHTS: m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 500; SetMoveState(PEDMOVE_STILL); break; case WAITSTATE_CROSS_ROAD: m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 1000; CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_HBHB, 4.0f); break; case WAITSTATE_CROSS_ROAD_LOOK: CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROADCROSS, 8.0f); if (time) m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; else m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2000,5000); break; case WAITSTATE_LOOK_PED: case WAITSTATE_LOOK_SHOP: case WAITSTATE_LOOK_ACCIDENT: case WAITSTATE_FACEOFF_GANG: case WAITSTATE_RIOT: case WAITSTATE_STRIPPER: break; case WAITSTATE_DOUBLEBACK: m_headingRate = 0.0f; m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 3500; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_HBHB, 4.0f); #ifdef FIX_BUGS animAssoc->SetFinishCallback(RestoreHeadingRateCB, this); #endif break; case WAITSTATE_HITWALL: m_headingRate = 2.0f; m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_WALL, 16.0f); animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc->flags |= ASSOC_FADEOUTWHENDONE; animAssoc->SetDeleteCallback(FinishedWaitCB, this); if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == RANDOM_CHAR && m_nPedState == PED_SEEK_CAR) { ClearObjective(); RestorePreviousState(); m_carJackTimer = CTimer::GetTimeInMilliseconds() + 30000; } break; case WAITSTATE_TURN180: m_headingRate = 0.0f; m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_TURN180, 4.0f); animAssoc->SetFinishCallback(FinishedWaitCB, this); break; case WAITSTATE_SURPRISE: m_headingRate = 0.0f; m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_WALL, 4.0f); animAssoc->SetFinishCallback(FinishedWaitCB, this); break; case WAITSTATE_STUCK: SetMoveState(PEDMOVE_STILL); SetMoveAnim(); m_headingRate = 0.0f; m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 4.0f); #ifdef FIX_BUGS animAssoc->SetFinishCallback(RestoreHeadingRateCB, this); #endif // Random char as passenger? Cop, medic etc.? if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == RANDOM_CHAR && m_nPedState == PED_SEEK_CAR) { ClearObjective(); RestorePreviousState(); m_carJackTimer = CTimer::GetTimeInMilliseconds() + 30000; } break; case WAITSTATE_LOOK_ABOUT: SetMoveState(PEDMOVE_STILL); SetMoveAnim(); m_headingRate = 0.0f; m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_HBHB, 4.0f); #ifdef FIX_BUGS animAssoc->SetFinishCallback(RestoreHeadingRateCB, this); #endif break; case WAITSTATE_PLAYANIM_COWER: waitAnim = ANIM_STD_HANDSCOWER; case WAITSTATE_PLAYANIM_HANDSUP: if (waitAnim == ANIM_STD_NUM) waitAnim = ANIM_STD_HANDSUP; case WAITSTATE_PLAYANIM_HANDSCOWER: if (waitAnim == ANIM_STD_NUM) waitAnim = ANIM_STD_HANDSCOWER; m_headingRate = 0.0f; if (time) m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; else m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 3000; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, waitAnim, 4.0f); animAssoc->SetDeleteCallback(FinishedWaitCB, this); break; case WAITSTATE_PLAYANIM_DUCK: waitAnim = ANIM_STD_DUCK_DOWN; case WAITSTATE_PLAYANIM_TAXI: if (waitAnim == ANIM_STD_NUM) waitAnim = ANIM_STD_HAILTAXI; case WAITSTATE_PLAYANIM_CHAT: if (waitAnim == ANIM_STD_NUM) waitAnim = ANIM_STD_CHAT; if (time) m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; else m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 3000; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, waitAnim, 4.0f); animAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc->SetDeleteCallback(FinishedWaitCB, this); break; case WAITSTATE_FINISH_FLEE: SetMoveState(PEDMOVE_STILL); SetMoveAnim(); m_headingRate = 0.0f; m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2500; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 4.0f); #ifdef FIX_BUGS animAssoc->SetFinishCallback(RestoreHeadingRateCB, this); #endif break; case WAITSTATE_SIT_DOWN: animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_SEAT_DOWN, 4.0f); animAssoc->SetFinishCallback(FinishedWaitCB, this); m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 100000; break; case WAITSTATE_SIT_UP: animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_SEAT_UP, 4.0f); animAssoc->SetFinishCallback(FinishedWaitCB, this); m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 100000; break; case WAITSTATE_SIT_IDLE: animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_SEAT_IDLE, 128.f); animAssoc->SetFinishCallback(FinishedWaitCB, this); if (time) m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; else m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(25000, 30000); break; case WAITSTATE_USE_ATM: animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ATM, 4.0f); animAssoc->SetFinishCallback(FinishedWaitCB, this); if (time) m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; else m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 100000; break; case WAITSTATE_SUN_BATHE_IDLE: m_headingRate = 0.0f; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_SUNBATHE, ANIM_SUNBATHE_IDLE, 4.0f); animAssoc->SetDeleteCallback(DeleteSunbatheIdleAnimCB, this); m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(50000, 100000); break; case WAITSTATE_FAST_FALL: SetFall(-1, ANIM_STD_HIGHIMPACT_FRONT, true); break; case WAITSTATE_BOMBER: CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_DETONATE, 4.0f); m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; break; case WAITSTATE_GROUND_ATTACK: { CWeaponInfo* currentWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); if (!currentWeapon) break; if (GetFireAnimGround(currentWeapon, false)) { if (!RpAnimBlendClumpGetAssociation(GetClump(), GetFireAnimGround(currentWeapon, false))) { m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; CAnimBlendAssociation* newAnim = CAnimManager::BlendAnimation(GetClump(), currentWeapon->m_AnimToPlay, GetFireAnimGround(currentWeapon, false), 8.0f); newAnim->SetDeleteCallback(FinishedWaitCB, this); } } break; } case WAITSTATE_LANCESITTING: CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_LANCE, ANIM_SUNBATHE_IDLE, 4.0f); break; case WAITSTATE_PLAYANIM_HANDSUP_SIMPLE: animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSUP, 4.0f); animAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc->SetDeleteCallback(FinishedWaitCB, this); m_nWaitTimer = CTimer::GetTimeInMilliseconds() + *(int*)time; break; default: ClearWaitState(); RestoreHeadingRate(); return; } m_nWaitState = state; } void CPed::Wait(void) { AnimationId mustHaveAnim = ANIM_STD_NUM; CAnimBlendAssociation *animAssoc; CPed *pedWeLook; if (DyingOrDead()) { ClearWaitState(); RestoreHeadingRate(); return; } switch (m_nWaitState) { case WAITSTATE_TRAFFIC_LIGHTS: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { if (CTrafficLights::LightForPeds() == PED_LIGHTS_WALK) { ClearWaitState(); SetMoveState(PEDMOVE_WALK); } } break; case WAITSTATE_CROSS_ROAD: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { if (CGeneral::GetRandomNumber() & 1 || !m_nWaitTimer) ClearWaitState(); else SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, nil); animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } } break; case WAITSTATE_CROSS_ROAD_LOOK: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { ClearWaitState(); animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } } break; case WAITSTATE_DOUBLEBACK: if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) { uint32 timeLeft = m_nWaitTimer - CTimer::GetTimeInMilliseconds(); if (timeLeft < 2500 && timeLeft > 2000) { m_nWaitTimer -= 500; CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_XPRESS_SCRATCH, 4.0f); } } else { ClearWaitState(); SetMoveState(PEDMOVE_WALK); } break; case WAITSTATE_HITWALL: if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) { if (m_collidingThingTimer > CTimer::GetTimeInMilliseconds()) { m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 2500; } } else { ClearWaitState(); } break; case WAITSTATE_TURN180: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { ClearWaitState(); m_fRotationCur = m_fRotationCur + PI; if (m_nPedState == PED_INVESTIGATE) ClearInvestigateEvent(); } if (m_collidingThingTimer > CTimer::GetTimeInMilliseconds()) { m_collidingThingTimer = CTimer::GetTimeInMilliseconds() + 2500; } break; case WAITSTATE_SURPRISE: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HIT_WALL)) { animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_XPRESS_SCRATCH, 4.0f); animAssoc->SetFinishCallback(FinishedWaitCB, this); m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 5000; } else { ClearWaitState(); } } break; case WAITSTATE_STUCK: if (CTimer::GetTimeInMilliseconds() <= m_nWaitTimer) break; animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_TURN180); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); if (animAssoc) { if (animAssoc->IsPartial()) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } else { animAssoc->flags |= ASSOC_DELETEFADEDOUT; CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); } if (animAssoc->animId == ANIM_STD_TURN180) { m_fRotationCur = CGeneral::LimitRadianAngle(PI + m_fRotationCur); ClearWaitState(); SetMoveState(PEDMOVE_WALK); m_nStoredMoveState = PEDMOVE_NONE; m_panicCounter = 0; return; } } AnimationId animToPlay; switch (CGeneral::GetRandomNumber() & 3) { case 0: animToPlay = ANIM_STD_ROADCROSS; break; case 1: animToPlay = ANIM_STD_IDLE_TIRED; break; case 2: animToPlay = ANIM_STD_XPRESS_SCRATCH; break; case 3: animToPlay = ANIM_STD_TURN180; break; default: break; } animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay, 4.0f); if (animToPlay == ANIM_STD_TURN180) animAssoc->SetFinishCallback(FinishedWaitCB, this); m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(1500, 5000); break; case WAITSTATE_LOOK_ABOUT: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { ClearWaitState(); animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } } break; case WAITSTATE_PLAYANIM_HANDSUP: mustHaveAnim = ANIM_STD_HANDSUP; case WAITSTATE_PLAYANIM_HANDSCOWER: if (mustHaveAnim == ANIM_STD_NUM) mustHaveAnim = ANIM_STD_HANDSCOWER; animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim); pedWeLook = (CPed*) m_pLookTarget; if ((!m_pLookTarget || !m_pLookTarget->IsPed() || pedWeLook->m_pPointGunAt) && m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_ATTACK && CTimer::GetTimeInMilliseconds() <= m_nWaitTimer && animAssoc) { if (pedWeLook) TurnBody(); } else { ClearWaitState(); m_nWaitTimer = 0; if (m_pLookTarget && m_pLookTarget->IsPed()) { if (m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_ATTACK) { if (bCrouchWhenScared) { if (bIsDucking) { ClearDuck(false); SetDuck(10000, true); } } else if (m_pedStats->m_fear <= 100 - pedWeLook->m_pedStats->m_temper) { if (GetWeapon()->IsTypeMelee()) { if(m_pedStats->m_flags & STAT_GUN_PANIC) { SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pLookTarget); if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_FLEE_POS) { bUsePedNodeSeek = true; m_pNextPathNode = nil; } if (m_nMoveState != PEDMOVE_RUN) SetMoveState(PEDMOVE_WALK); if (m_nPedType != PEDTYPE_COP) { ProcessObjective(); SetMoveState(PEDMOVE_WALK); } } else { SetObjective(OBJECTIVE_NONE); SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); } } else { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_pLookTarget); SetObjectiveTimer(20000); } } else { SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pLookTarget); if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_FLEE_POS) { bUsePedNodeSeek = true; m_pNextPathNode = nil; } SetMoveState(PEDMOVE_RUN); Say(SOUND_PED_FLEE_RUN); } } } animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim); if (animAssoc) { animAssoc->blendDelta = -4.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } } break; case WAITSTATE_PLAYANIM_COWER: mustHaveAnim = ANIM_STD_HANDSCOWER; case WAITSTATE_PLAYANIM_DUCK: if (mustHaveAnim == ANIM_STD_NUM) mustHaveAnim = ANIM_STD_DUCK_DOWN; case WAITSTATE_PLAYANIM_TAXI: if (mustHaveAnim == ANIM_STD_NUM) mustHaveAnim = ANIM_STD_HAILTAXI; case WAITSTATE_PLAYANIM_CHAT: if (mustHaveAnim == ANIM_STD_NUM) mustHaveAnim = ANIM_STD_CHAT; if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), mustHaveAnim); if (animAssoc) { animAssoc->blendDelta = -4.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } if (m_attractor && m_objective == OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN) { GetPedAttractorManager()->BroadcastDeparture(this, m_attractor); bBoughtIceCream = true; } ClearWaitState(); } else if (m_nWaitState == WAITSTATE_PLAYANIM_TAXI) { if (m_pedInObjective) { if (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT) { if (m_pLookTarget) m_pLookTarget->CleanUpOldReference(&m_pLookTarget); m_pLookTarget = m_pedInObjective; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); TurnBody(); } } } break; case WAITSTATE_FINISH_FLEE: animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); if (animAssoc) { if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); int timer = 2000; ClearWaitState(); SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &timer); } } else { ClearWaitState(); } break; case WAITSTATE_SIT_DOWN: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { ClearWaitState(); SetWaitState(WAITSTATE_SIT_IDLE, 0); } break; //case WAITSTATE_SIT_DOWN_RVRS: case WAITSTATE_SIT_UP: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { if (m_attractor) GetPedAttractorManager()->BroadcastDeparture(this, m_attractor); ClearWaitState(); if (bFleeWhenStanding) { if (m_threatEx) { SetFlee(m_threatEx, 10000); bFleeWhenStanding = false; m_threatEx = nil; Say(SOUND_PED_FLEE_SPRINT); } } } break; case WAITSTATE_SIT_IDLE: if (bTurnedAroundOnAttractor) { m_fRotationCur += PI; m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); m_fRotationDest = m_fRotationCur; bTurnedAroundOnAttractor = false; } if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { ClearWaitState(); SetWaitState(WAITSTATE_SIT_UP, 0); } else { if (m_fleeFrom && m_fleeFrom->IsVehicle()) { m_pNextPathNode = nil; m_threatEx = m_threatEntity; bFleeWhenStanding = true; ClearWaitState(); SetWaitState(WAITSTATE_SIT_UP, 0); } else { uint32 threatFlag = ScanForThreats(); if (threatFlag == PED_FLAG_GUN || threatFlag == PED_FLAG_EXPLOSION || threatFlag == PED_FLAG_DEADPEDS) { m_pNextPathNode = nil; m_threatEx = m_threatEntity; bFleeWhenStanding = true; ClearWaitState(); SetWaitState(WAITSTATE_SIT_UP, 0); } } } break; case WAITSTATE_USE_ATM: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { if (m_attractor) GetPedAttractorManager()->BroadcastDeparture(this, m_attractor); ClearWaitState(); } break; case WAITSTATE_SUN_BATHE_IDLE: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer && bCanGiveUpSunbathing) { m_pNextPathNode = nil; bGotUpOfMyOwnAccord = true; SetGetUp(); ClearWaitState(); } else if (CWeather::Rain <= 0.1f) { if (CClock::GetHours() <= 18 || CGeneral::GetRandomNumberInRange(0.f, 1.0f) < 0.005f) { uint32 threatFlag = ScanForThreats(); if (threatFlag == PED_FLAG_GUN || threatFlag == PED_FLAG_EXPLOSION || threatFlag == PED_FLAG_DEADPEDS) { // Get up in case of danger m_pNextPathNode = nil; m_threatEx = m_threatEntity; bFleeWhenStanding = true; SetGetUp(); ClearWaitState(); } CPlayerPed *player = FindPlayerPed(); if (player) { // Get up if player coming towards us with a car if (player->InVehicle()){ CVector vehSpeedPerSec = player->m_pMyVehicle->m_vecMoveSpeed * GAME_SPEED_TO_METERS_PER_SECOND; CVector vehPos = player->m_pMyVehicle->GetPosition(); CVector ourPos = GetPosition(); float timeUntilVehReachPed = DotProduct(ourPos - vehPos, vehSpeedPerSec) / vehSpeedPerSec.MagnitudeSqr(); if (timeUntilVehReachPed > 0.0 && timeUntilVehReachPed < 8.0f) { if ((ourPos - (timeUntilVehReachPed * vehSpeedPerSec + vehPos)).Magnitude() < 5.0f) { m_pNextPathNode = nil; m_threatEx = player; bFleeWhenStanding = true; SetGetUp(); ClearWaitState(); } } } } } else { m_pNextPathNode = nil; bGotUpOfMyOwnAccord = true; SetGetUp(); ClearWaitState(); } } else { m_pNextPathNode = nil; bGotUpOfMyOwnAccord = true; SetGetUp(); ClearWaitState(); } break; case WAITSTATE_RIOT: if (m_nPedState == PED_FLEE_ENTITY || m_nPedState == PED_ATTACK) { ClearWaitState(); break; } PlayRandomAnimationsFromAnimBlock(this, ASSOCGRP_RIOT, ANIM_RIOT_ANGRY, ANIM_RIOT_FUCKYOU - ANIM_RIOT_ANGRY + 1); if (IsPedInControl() && CGeneral::GetRandomNumberInRange(0.f,1.f) < 0.25f && CPopulation::CanJeerAtStripper(m_modelIndex)) { for (int i = 0; i < m_numNearPeds; ++i) { CPed *nearPed = m_nearPeds[i]; if (nearPed) { if ((GetPosition() - nearPed->GetPosition()).MagnitudeSqr() < sq(10.f)) { for (int anim = ANIM_STRIP_A; anim <= ANIM_STRIP_G; anim++) { if (RpAnimBlendClumpGetAssociation(nearPed->GetClump(), anim)) Say(SOUND_PED_149); } } } } } break; case WAITSTATE_BOMBER: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) ClearWaitState(); break; case WAITSTATE_STRIPPER: PlayRandomAnimationsFromAnimBlock(this, ASSOCGRP_STRIP, ANIM_STRIP_A, ANIM_STRIP_G - ANIM_STRIP_A + 1); break; case WAITSTATE_PLAYANIM_HANDSUP_SIMPLE: if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) ClearWaitState(); break; default: break; } if(!m_nWaitState) RestoreHeadingRate(); } void CPed::DeleteSunbatheIdleAnimCB(CAnimBlendAssociation *assoc, void *arg) { CPed *ped = (CPed*) arg; if (CTimer::GetTimeInMilliseconds() <= ped->m_nWaitTimer && !ped->bGotUpOfMyOwnAccord && !ped->bFleeWhenStanding && !ped->m_threatEx) { ped->m_pNextPathNode = nil; ped->bFleeWhenStanding = true; ped->m_threatEx = FindPlayerPed(); ped->SetGetUp(); ped->ClearWaitState(); } ped->m_nWaitTimer = 0; ped->RestoreHeadingRate(); ped->Wait(); } void CPed::FinishedWaitCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; ped->m_nWaitTimer = 0; ped->RestoreHeadingRate(); ped->Wait(); } void CPed::RestoreHeadingRate(void) { m_headingRate = m_pedStats->m_headingChangeRate; } void CPed::RestoreHeadingRateCB(CAnimBlendAssociation *assoc, void *arg) { ((CPed*)arg)->RestoreHeadingRate(); } void CPed::FlagToDestroyWhenNextProcessed(void) { bRemoveFromWorld = true; if (!InVehicle()) return; if (m_pMyVehicle->pDriver == this){ m_pMyVehicle->pDriver = nil; if (IsPlayer() && m_pMyVehicle->GetStatus() != STATUS_WRECKED) m_pMyVehicle->SetStatus(STATUS_ABANDONED); }else{ m_pMyVehicle->RemovePassenger(this); } bInVehicle = false; m_pMyVehicle = nil; if (CharCreatedBy == MISSION_CHAR) SetPedState(PED_DEAD); else SetPedState(PED_NONE); m_pVehicleAnim = nil; } void CPed::SetSolicit(uint32 time) { if (m_nPedState == PED_SOLICIT || !IsPedInControl() || !m_carInObjective) return; if (CharCreatedBy != MISSION_CHAR && m_carInObjective->m_nNumGettingIn == 0 && CTimer::GetTimeInMilliseconds() < m_objectiveTimer) { if (m_vehDoor == CAR_DOOR_LF) { m_fRotationDest = m_carInObjective->GetForward().Heading() - HALFPI; } else { m_fRotationDest = m_carInObjective->GetForward().Heading() + HALFPI; } if (Abs(m_fRotationDest - m_fRotationCur) < HALFPI) { m_chatTimer = CTimer::GetTimeInMilliseconds() + time; if(!m_carInObjective->bIsVan && !m_carInObjective->bIsBus) m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_HOOKERTALK, 4.0f); SetPedState(PED_SOLICIT); } } } void CPed::Solicit(void) { if (m_chatTimer >= CTimer::GetTimeInMilliseconds() && m_carInObjective) { CVector doorPos = GetPositionToOpenCarDoor(m_carInObjective, m_vehDoor, 0.0f); Say(SOUND_PED_SOLICIT); if (FindPlayerVehicle() == m_carInObjective) { FindPlayerPed()->Say(SOUND_PED_SOLICIT); } SetMoveState(PEDMOVE_STILL); // Game uses GetAngleBetweenPoints and converts it to radian m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( doorPos.x, doorPos.y, GetPosition().x, GetPosition().y); if (m_fRotationDest < 0.0f) { m_fRotationDest += TWOPI; } else if (m_fRotationDest > TWOPI) { m_fRotationDest -= TWOPI; } if ((GetPosition() - doorPos).MagnitudeSqr() <= 1.0f) return; CAnimBlendAssociation *talkAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_HOOKERTALK); if (talkAssoc) { talkAssoc->blendDelta = -1000.0f; talkAssoc->flags |= ASSOC_DELETEFADEDOUT; } RestorePreviousState(); RestorePreviousObjective(); SetObjectiveTimer(10000); } else if (!m_carInObjective) { RestorePreviousState(); RestorePreviousObjective(); SetObjectiveTimer(10000); } else if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney <= 100) { m_carInObjective = nil; } else { m_pVehicleAnim = nil; SetLeader(m_carInObjective->pDriver); Say(SOUND_PED_SOLICIT); } } void CPed::SetBuyIceCream(void) { if (m_nPedState == PED_BUY_ICECREAM || !IsPedInControl()) return; if (!m_carInObjective) return; SetPedState(PED_BUY_ICECREAM); } void CPed::BuyIceCream(void) { if (m_carInObjective) { CPed *driver = m_carInObjective->pDriver; if (driver && CTimer::GetTimeInMilliseconds() > m_chatTimer) { SetChat(driver, 8000); driver->SetChat(this, 8000); return; } SetObjective(OBJECTIVE_NONE); SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); } else { SetObjective(OBJECTIVE_NONE); SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); } } bool CPed::PossiblyFindBetterPosToSeekCar(CVector *pos, CVehicle *veh) { bool foundIt = false; CVector helperPos = GetPosition(); helperPos.z = pos->z - 0.5f; CVector foundPos = *pos; foundPos.z -= 0.5f; // If there is another car between target car and us. if (CWorld::TestSphereAgainstWorld((foundPos + helperPos) / 2.0f, 0.25f, veh, false, true, false, false, false, false)) { CColModel *vehCol = veh->GetModelInfo()->GetColModel(); CVector *colMin = &vehCol->boundingBox.min; CVector *colMax = &vehCol->boundingBox.max; CVector leftRearPos = CVector(colMin->x - 0.5f, colMin->y - 0.5f, 0.0f); CVector rightRearPos = CVector(0.5f + colMax->x, colMin->y - 0.5f, 0.0f); CVector leftFrontPos = CVector(colMin->x - 0.5f, 0.5f + colMax->y, 0.0f); CVector rightFrontPos = CVector(0.5f + colMax->x, 0.5f + colMax->y, 0.0f); leftRearPos = veh->GetMatrix() * leftRearPos; rightRearPos = veh->GetMatrix() * rightRearPos; leftFrontPos = veh->GetMatrix() * leftFrontPos; rightFrontPos = veh->GetMatrix() * rightFrontPos; // Makes helperPos veh-ped distance vector. helperPos -= veh->GetPosition(); // ?!? I think it's absurd to use this unless another function like SeekCar finds next pos. with it and we're trying to simulate it's behaviour. // On every run it returns another pos. for ped, with same distance to the veh. // Sequence of positions are not guaranteed, it depends on global pos. (So sometimes it returns positions to make ped draw circle, sometimes don't) helperPos = veh->GetMatrix() * helperPos; float vehForwardHeading = veh->GetForward().Heading(); // I'm absolutely not sure about these namings. // NTVF = needed turn if we're looking to vehicle front and wanna look to... float potentialLrHeading = Atan2(leftRearPos.x - helperPos.x, leftRearPos.y - helperPos.y); float NTVF_LR = CGeneral::LimitRadianAngle(potentialLrHeading - vehForwardHeading); float potentialRrHeading = Atan2(rightRearPos.x - helperPos.x, rightRearPos.y - helperPos.y); float NTVF_RR = CGeneral::LimitRadianAngle(potentialRrHeading - vehForwardHeading); float potentialLfHeading = Atan2(leftFrontPos.x - helperPos.x, leftFrontPos.y - helperPos.y); float NTVF_LF = CGeneral::LimitRadianAngle(potentialLfHeading - vehForwardHeading); float potentialRfHeading = Atan2(rightFrontPos.x - helperPos.x, rightFrontPos.y - helperPos.y); float NTVF_RF = CGeneral::LimitRadianAngle(potentialRfHeading - vehForwardHeading); bool canHeadToLr = NTVF_LR <= -PI || NTVF_LR >= -HALFPI; bool canHeadToRr = NTVF_RR <= HALFPI || NTVF_RR >= PI; bool canHeadToLf = NTVF_LF >= 0.0f || NTVF_LF <= -HALFPI; bool canHeadToRf = NTVF_RF <= 0.0f || NTVF_RF >= HALFPI; // Only order of conditions are different among enterTypes. if (m_vehDoor == CAR_DOOR_RR) { if (canHeadToRr) { foundPos = rightRearPos; foundIt = true; } else if (canHeadToRf) { foundPos = rightFrontPos; foundIt = true; } else if (canHeadToLr) { foundPos = leftRearPos; foundIt = true; } else if (canHeadToLf) { foundPos = leftFrontPos; foundIt = true; } } else if(m_vehDoor == CAR_DOOR_RF) { if (canHeadToRf) { foundPos = rightFrontPos; foundIt = true; } else if (canHeadToRr) { foundPos = rightRearPos; foundIt = true; } else if (canHeadToLf) { foundPos = leftFrontPos; foundIt = true; } else if (canHeadToLr) { foundPos = leftRearPos; foundIt = true; } } else if (m_vehDoor == CAR_DOOR_LF) { if (canHeadToLf) { foundPos = leftFrontPos; foundIt = true; } else if (canHeadToLr) { foundPos = leftRearPos; foundIt = true; } else if (canHeadToRf) { foundPos = rightFrontPos; foundIt = true; } else if (canHeadToRr) { foundPos = rightRearPos; foundIt = true; } } else if (m_vehDoor == CAR_DOOR_LR) { if (canHeadToLr) { foundPos = leftRearPos; foundIt = true; } else if (canHeadToLf) { foundPos = leftFrontPos; foundIt = true; } else if (canHeadToRr) { foundPos = rightRearPos; foundIt = true; } else if (canHeadToRf) { foundPos = rightFrontPos; foundIt = true; } } } if (!foundIt) return false; helperPos = GetPosition() - foundPos; helperPos.z = 0.0f; if (helperPos.MagnitudeSqr() <= sq(0.5f)) return false; pos->x = foundPos.x; pos->y = foundPos.y; return true; } void CPed::SetLeader(CEntity *leader) { m_leader = (CPed*)leader; if (m_leader) { m_leader->bIsLeader = true; m_leader->RegisterReference((CEntity**)&m_leader); } } bool CPed::CanPedJumpThis(CEntity *unused, CVector *damageNormal) { if (m_nSurfaceTouched == SURFACE_WATER) return true; CVector pos = GetPosition(); CVector forwardOffset = GetForward(); if (damageNormal && damageNormal->z > 0.17f) { if (damageNormal->z > 0.9f) return false; CColModel *ourCol = GetColModel(); pos.z = ourCol->spheres->center.z - ourCol->spheres->radius * damageNormal->z + pos.z; pos.z = pos.z + 0.05f; float collPower = damageNormal->Magnitude2D(); if (damageNormal->z > 0.5f) { CVector invDamageNormal(-damageNormal->x, -damageNormal->y, 0.0f); invDamageNormal *= 1.0f / collPower; CVector estimatedJumpDist = invDamageNormal + collPower * invDamageNormal * ourCol->spheres->radius; forwardOffset = estimatedJumpDist * Min(2.0f / collPower, 4.0f); } else { forwardOffset += collPower * ourCol->spheres->radius * forwardOffset; } } else { pos.z -= 0.15f; } CVector forwardPos = pos + forwardOffset; return CWorld::GetIsLineOfSightClear(pos, forwardPos, true, false, false, true, false, false, false); } void CPed::SetJump(void) { if (!bInVehicle && m_nPedState != PED_JUMP && !RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_JUMP_LAUNCH) && (m_nSurfaceTouched != SURFACE_STEEP_CLIFF || DotProduct(GetForward(), m_vecDamageNormal) >= 0.0f)) { SetStoredState(); SetPedState(PED_JUMP); CAnimBlendAssociation *jumpAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_JUMP_LAUNCH, 8.0f); jumpAssoc->SetFinishCallback(FinishLaunchCB, this); m_fRotationDest = m_fRotationCur; } } void CPed::FinishLaunchCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; if (ped->m_nPedState != PED_JUMP) return; CVector forward(0.09f * ped->GetForward() + ped->GetPosition()); forward.z += CModelInfo::GetModelInfo(ped->GetModelIndex())->GetColModel()->spheres[2].center.z + 0.35f; CEntity *obstacle = CWorld::TestSphereAgainstWorld(forward, 0.25f, nil, true, true, false, true, false, false); if (!obstacle) { // Forward of forward forward += 0.15f * ped->GetForward(); forward.z += 0.15f; obstacle = CWorld::TestSphereAgainstWorld(forward, 0.25f, nil, true, true, false, true, false, false); } if (!obstacle && CCullZones::CamStairsForPlayer() && CCullZones::FindZoneWithStairsAttributeForPlayer()) obstacle = ped; if (obstacle) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; CAnimBlendAssociation *handsCoverAssoc = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_WALL, 8.0f); handsCoverAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; handsCoverAssoc->SetFinishCallback(FinishHitHeadCB, ped); ped->bIsLanding = true; return; } float velocityFromAnim = 0.1f; CAnimBlendAssociation *sprintAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_STD_RUNFAST); if (sprintAssoc) { velocityFromAnim = 0.05f * sprintAssoc->blendAmount + 0.17f; } else { CAnimBlendAssociation *runAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), ANIM_STD_RUN); if (runAssoc) { velocityFromAnim = 0.07f * runAssoc->blendAmount + 0.1f; } } if (ped->IsPlayer() || ped->m_pedInObjective && ped->m_pedInObjective->IsPlayer()) ped->ApplyMoveForce(0.0f, 0.0f, 8.5f); else ped->ApplyMoveForce(0.0f, 0.0f, 4.5f); if (sq(velocityFromAnim) > ped->m_vecMoveSpeed.MagnitudeSqr2D() || ped->m_pCurrentPhysSurface) { #ifdef FREE_CAM if (TheCamera.Cams[0].Using3rdPersonMouseCam() && !CCamera::bFreeCam) { #else if (TheCamera.Cams[0].Using3rdPersonMouseCam()) { #endif float fpsAngle = ped->WorkOutHeadingForMovingFirstPerson(ped->m_fRotationCur); ped->m_vecMoveSpeed.x = -velocityFromAnim * Sin(fpsAngle); ped->m_vecMoveSpeed.y = velocityFromAnim * Cos(fpsAngle); } else { ped->m_vecMoveSpeed.x = -velocityFromAnim * Sin(ped->m_fRotationCur); ped->m_vecMoveSpeed.y = velocityFromAnim * Cos(ped->m_fRotationCur); } if (ped->m_pCurrentPhysSurface) { ped->m_vecMoveSpeed.x += ped->m_pCurrentPhysSurface->m_vecMoveSpeed.x; ped->m_vecMoveSpeed.y += ped->m_pCurrentPhysSurface->m_vecMoveSpeed.y; } } ped->bIsStanding = false; ped->bIsInTheAir = true; animAssoc->blendDelta = -1000.0f; CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_JUMP_GLIDE); if (ped->bDoBloodyFootprints) { CVector bloodPos(0.0f, 0.0f, 0.0f); ped->TransformToNode(bloodPos, PED_FOOTL); bloodPos.z -= 0.1f; bloodPos += 0.2f * ped->GetForward(); CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, 0.26f * ped->GetForward().x, 0.26f * ped->GetForward().y, 0.14f * ped->GetRight().x, 0.14f * ped->GetRight().y, 255, 255, 0, 0, 4.0f, 3000, 1.0f); bloodPos = CVector(0.0f, 0.0f, 0.0f); ped->TransformToNode(bloodPos, PED_FOOTR); bloodPos.z -= 0.1f; bloodPos += 0.2f * ped->GetForward(); CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &bloodPos, 0.26f * ped->GetForward().x, 0.26f * ped->GetForward().y, 0.14f * ped->GetRight().x, 0.14f * ped->GetRight().y, 255, 255, 0, 0, 4.0f, 3000, 1.0f); if (ped->m_bloodyFootprintCountOrDeathTime <= 40) { ped->m_bloodyFootprintCountOrDeathTime = 0; ped->bDoBloodyFootprints = false; } else { ped->m_bloodyFootprintCountOrDeathTime -= 40; } } } void CPed::FinishJumpCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; ped->bResetWalkAnims = true; ped->bIsLanding = false; animAssoc->blendDelta = -1000.0f; } void CPed::FinishHitHeadCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; if (animAssoc) { animAssoc->blendDelta = -4.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } if (ped->m_nPedState == PED_JUMP) ped->RestorePreviousState(); ped->bIsLanding = false; } bool CPed::CanPedDriveOff(void) { if (m_nPedState != PED_DRIVING || m_lookTimer > CTimer::GetTimeInMilliseconds()) return false; for (int i = 0; i < m_numNearPeds; i++) { CPed *nearPed = m_nearPeds[i]; if (nearPed->m_nPedType == m_nPedType && nearPed->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && nearPed->m_carInObjective == m_carInObjective) { m_lookTimer = CTimer::GetTimeInMilliseconds() + 1000; return false; } } return true; } void CPed::SetRadioStation(void) { CPedModelInfo* modelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); if (IsPlayer() || !m_pMyVehicle || m_pMyVehicle->pDriver != this) return; if (GetModelIndex() != MI_PGA && GetModelIndex() != MI_PGB) { if (m_pMyVehicle->m_nRadioStation != modelInfo->radio1 && m_pMyVehicle->m_nRadioStation != modelInfo->radio2) { if (CGeneral::GetRandomTrueFalse()) m_pMyVehicle->m_nRadioStation = modelInfo->radio1; else m_pMyVehicle->m_nRadioStation = modelInfo->radio2; } } else { m_pMyVehicle->m_nRadioStation = DMAudio.GetFavouriteRadioStation(); } } void CPed::WarpPedIntoCar(CVehicle *car) { bInVehicle = true; m_pMyVehicle = car; m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); m_carInObjective = car; m_carInObjective->RegisterReference((CEntity **) &m_carInObjective); SetPedState(PED_DRIVING); bUsesCollision = false; bIsInTheAir = false; bVehExitWillBeInstant = true; if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { car->SetDriver(this); car->pDriver->RegisterReference((CEntity **) &car->pDriver); } else if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) { if (car->IsBike() && !car->pPassengers[0]) { car->pPassengers[0] = this; car->pPassengers[0]->RegisterReference((CEntity**) &car->pPassengers[0]); } for (int i = 0; i < 4; i++) { if (!car->pPassengers[i]) { car->pPassengers[i] = this; car->pPassengers[i]->RegisterReference((CEntity **) &car->pPassengers[i]); break; } } } else return; if (IsPlayer()) { car->SetStatus(STATUS_PLAYER); AudioManager.PlayerJustGotInCar(); CCarCtrl::RegisterVehicleOfInterest(car); } else { car->SetStatus(STATUS_PHYSICS); } CWorld::Remove(this); SetPosition(car->GetPosition()); CWorld::Add(this); if (car->bIsAmbulanceOnDuty) { car->bIsAmbulanceOnDuty = false; --CCarCtrl::NumAmbulancesOnDuty; } if (car->bIsFireTruckOnDuty) { car->bIsFireTruckOnDuty = false; --CCarCtrl::NumFiretrucksOnDuty; } if (!car->bEngineOn) { car->bEngineOn = true; DMAudio.PlayOneShot(car->m_audioEntityId, SOUND_CAR_ENGINE_START, 1.0f); } RpAnimBlendClumpSetBlendDeltas(GetClump(), ASSOC_PARTIAL, -1000.0f); AddInCarAnims(car, car->pDriver == this); RemoveWeaponWhenEnteringVehicle(); if (car->bIsBus) bRenderPedInCar = false; bChangedSeat = true; } bool CPed::HasAttractor(void) { return m_attractor != nil; } void CPed::SetNewAttraction(CPedAttractor* pAttractor, const CVector& pos, float heading, float time, int32 qid) { if (!m_attractor) m_attractor = pAttractor; if (m_attractor != pAttractor) return; switch (pAttractor->GetEffect()->pedattr.type) { case ATTRACTOR_ATM: SetObjective(OBJECTIVE_GOTO_ATM_ON_FOOT, heading, pos); break; case ATTRACTOR_SEAT: SetObjective(OBJECTIVE_GOTO_SEAT_ON_FOOT, heading, pos); break; case ATTRACTOR_STOP: SetObjective(OBJECTIVE_GOTO_BUS_STOP_ON_FOOT, heading, pos); break; case ATTRACTOR_PIZZA: SetObjective(OBJECTIVE_GOTO_PIZZA_ON_FOOT, heading, pos); break; case ATTRACTOR_SHELTER: SetObjective(OBJECTIVE_GOTO_SHELTER_ON_FOOT, heading, pos); break; case ATTRACTOR_ICECREAM: SetObjective(OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT, heading, pos); break; default: return; } SetObjectiveTimer(time); m_positionInQueue = qid; } void CPed::AttachPedToEntity(CEntity *ent, CVector offset, uint16 type, float rot, eWeaponType weapon) { if (!ent || bInVehicle) return; m_attachedTo = ent; m_attachedTo->RegisterReference(&m_attachedTo); m_vecAttachOffset = offset; m_attachType = type; m_attachRotStep = rot; if (IsPlayer()) { bUsesCollision = false; } else if (ent->IsVehicle()) { m_pCollidingEntity = ent; } if (IsPlayer()) { m_objective = OBJECTIVE_NONE; m_prevObjective = OBJECTIVE_NONE; } SetStoredState(); SetPedState(PED_IDLE); CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 1000.0f); if (m_storedWeapon == WEAPONTYPE_UNIDENTIFIED) { m_storedWeapon = GetWeapon()->m_eWeaponType; m_attachWepAmmo = GetWeapon()->m_nAmmoTotal; } if (IsPlayer()) { GiveWeapon(weapon, 30000, 1); #ifndef FIX_BUGS ((CPlayerPed*)this)->m_nSelectedWepSlot = weapon; #else ((CPlayerPed*)this)->m_nSelectedWepSlot = GetWeaponSlot(weapon); #endif ((CPlayerPed*)this)->MakeChangesForNewWeapon(weapon); TheCamera.SetNewPlayerWeaponMode(CCam::MODE_HELICANNON_1STPERSON, 0, 0); SetPedState(PED_SNIPER_MODE); } else { GiveWeapon(weapon, 30000, true); SetCurrentWeapon(weapon); } PositionAttachedPed(); } void CPed::DettachPedFromEntity(void) { CEntity* pVehicleAttachedTo = m_attachedTo; m_attachedTo = nil; if (m_nPedState == PED_DIE) { m_pCollidingEntity = pVehicleAttachedTo; ApplyMoveForce(pVehicleAttachedTo->GetForward() * -4.0f + CVector(0.0f, 0.0f, 4.0f)); bIsStanding = false; } else if (m_nPedState != PED_DEAD) { RestorePreviousState(); CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 1000.0f); bUsesCollision = true; if (m_storedWeapon != WEAPONTYPE_UNIDENTIFIED) { GetWeapon()->m_nAmmoInClip = 0; GetWeapon()->m_nAmmoTotal = 0; SetCurrentWeapon(m_storedWeapon); GetWeapon()->m_nAmmoTotal = m_attachWepAmmo; m_storedWeapon = WEAPONTYPE_UNIDENTIFIED; } } } void CPed::PositionAttachedPed() { if(!m_attachedTo) return; CMatrix rotMatrix, targetMat; targetMat = m_attachedTo->GetMatrix(); targetMat.GetPosition() += Multiply3x3(m_attachedTo->GetMatrix(), m_vecAttachOffset); float objAngle = m_attachedTo->GetForward().Heading(); if (!IsPlayer()) { float targetAngle = objAngle; switch (m_attachType) { case 1: targetAngle += HALFPI; break; case 2: targetAngle += PI; break; case 3: targetAngle -= HALFPI; break; default: break; } targetAngle = CGeneral::LimitRadianAngle(targetAngle); m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); float neededTurn = m_fRotationCur - targetAngle; if (neededTurn > PI) neededTurn -= TWOPI; else if (neededTurn < -PI) neededTurn += TWOPI; if (neededTurn > m_attachRotStep) m_fRotationCur = CGeneral::LimitRadianAngle(targetAngle + m_attachRotStep); else if (-m_attachRotStep > neededTurn) m_fRotationCur = CGeneral::LimitRadianAngle(targetAngle - m_attachRotStep); else m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); } rotMatrix.SetRotateZ(m_fRotationCur - objAngle); targetMat = targetMat * rotMatrix; GetMatrix() = targetMat; if (m_attachedTo->IsVehicle() || m_attachedTo->IsObject()) { m_vecMoveSpeed = ((CPhysical*)m_attachedTo)->m_vecMoveSpeed; m_vecTurnSpeed = ((CPhysical*)m_attachedTo)->m_vecTurnSpeed; } } void CPed::Undress(const char* name) { int mi = GetModelIndex(); CAnimBlendAssociation* pAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PHONE_OUT); if (pAnim) FinishTalkingOnMobileCB(pAnim, this); DeleteRwObject(); if (IsPlayer()) mi = MI_PLAYER; CStreaming::RequestSpecialModel(mi, name, STREAMFLAGS_DEPENDENCY | STREAMFLAGS_SCRIPTOWNED); CWorld::Remove(this); } void CPed::Dress(void) { int mi = GetModelIndex(); m_modelIndex = -1; SetModelIndex(mi); m_nPedState = PED_IDLE; m_nLastPedState = PED_NONE; m_objective = OBJECTIVE_NONE; m_prevObjective = OBJECTIVE_NONE; m_nWaitState = WAITSTATE_FALSE; CWorld::Add(this); RestoreHeadingRate(); } void CPed::Say(uint16 audio, int32 time) { if (m_delayedSoundID == -1) { m_delayedSoundID = audio; m_delayedSoundTimer = CTimer::GetTimeInMilliseconds() + time; } } #ifdef COMPATIBLE_SAVES #define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); #define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); void CPed::Save(uint8*& buf) { SkipSaveBuf(buf, 52); CopyToBuf(buf, GetPosition().x); CopyToBuf(buf, GetPosition().y); CopyToBuf(buf, GetPosition().z); SkipSaveBuf(buf, 288); CopyToBuf(buf, CharCreatedBy); SkipSaveBuf(buf, 499); CopyToBuf(buf, m_fHealth); CopyToBuf(buf, m_fArmour); SkipSaveBuf(buf, 172); for (int i = 0; i < 10; i++) // has to be hardcoded m_weapons[i].Save(buf); SkipSaveBuf(buf, 252); } void CPed::Load(uint8*& buf) { SkipSaveBuf(buf, 52); CopyFromBuf(buf, GetMatrix().GetPosition().x); CopyFromBuf(buf, GetMatrix().GetPosition().y); CopyFromBuf(buf, GetMatrix().GetPosition().z); SkipSaveBuf(buf, 288); CopyFromBuf(buf, CharCreatedBy); SkipSaveBuf(buf, 499); CopyFromBuf(buf, m_fHealth); CopyFromBuf(buf, m_fArmour); SkipSaveBuf(buf, 172); m_currentWeapon = WEAPONTYPE_UNARMED; CWeapon bufWeapon; for (int i = 0; i < 10; i++) { // has to be hardcoded bufWeapon.Load(buf); if (bufWeapon.m_eWeaponType != WEAPONTYPE_UNARMED) { int modelId = CWeaponInfo::GetWeaponInfo(bufWeapon.m_eWeaponType)->m_nModelId; if (modelId != -1) { CStreaming::RequestModel(modelId, STREAMFLAGS_DEPENDENCY); int modelId2 = CWeaponInfo::GetWeaponInfo(bufWeapon.m_eWeaponType)->m_nModel2Id; if (modelId2 != -1) CStreaming::RequestModel(modelId2, STREAMFLAGS_DEPENDENCY); CStreaming::LoadAllRequestedModels(false); } GiveWeapon(bufWeapon.m_eWeaponType, bufWeapon.m_nAmmoTotal, false); } } SkipSaveBuf(buf, 252); } #undef CopyFromBuf #undef CopyToBuf #endif ================================================ FILE: src/peds/Ped.h ================================================ #pragma once #include "RwHelper.h" #include "AnimManager.h" #include "Crime.h" #include "EventList.h" #include "PedIK.h" #include "PedType.h" #include "Physical.h" #include "Weapon.h" #include "WeaponInfo.h" #include "PathFind.h" #include "Collision.h" #define FEET_OFFSET 1.04f #define CHECK_NEARBY_THINGS_MAX_DIST 15.0f #define ENTER_CAR_MAX_DIST 30.0f #define CAN_SEE_ENTITY_ANGLE_THRESHOLD DEGTORAD(60.0f) class CAccident; class CObject; class CFire; struct AnimBlendFrameData; class CAnimBlendAssociation; class CPedAttractor; struct PedAudioData { int m_nFixedDelayTime; int m_nOverrideFixedDelayTime; int m_nOverrideMaxRandomDelayTime; int m_nMaxRandomDelayTime; }; enum { ATTACK_IN_PROGRESS, CANT_ATTACK, WATCH_UNTIL_HE_DISAPPEARS, }; enum eFormation { FORMATION_UNDEFINED, FORMATION_REAR, FORMATION_REAR_LEFT, FORMATION_REAR_RIGHT, FORMATION_FRONT_LEFT, FORMATION_FRONT_RIGHT, FORMATION_LEFT, FORMATION_RIGHT, FORMATION_FRONT }; enum FightState { FIGHTSTATE_MOVE_FINISHED = -2, FIGHTSTATE_JUST_ATTACKED, FIGHTSTATE_NO_MOVE, FIGHTSTATE_1 }; enum { ENDFIGHT_NORMAL, ENDFIGHT_WITH_A_STEP, ENDFIGHT_FAST }; enum PedRouteType { PEDROUTE_STOP_WHEN_DONE = 1, PEDROUTE_GO_BACKWARD_WHEN_DONE, PEDROUTE_GO_TO_START_WHEN_DONE }; enum FightMoveHitLevel { HITLEVEL_NULL, HITLEVEL_GROUND, HITLEVEL_LOW, HITLEVEL_MEDIUM, HITLEVEL_HIGH }; struct FightMove { AnimationId animId; float startFireTime; float endFireTime; float comboFollowOnTime; float strikeRadius; float extendReachMultiplier; uint8 hitLevel; // FightMoveHitLevel uint8 damage; uint8 flags; }; // TODO: This is eFightState on mobile. enum PedFightMoves { FIGHTMOVE_NULL, // Attacker FIGHTMOVE_STDPUNCH, FIGHTMOVE_IDLE, FIGHTMOVE_SHUFFLE_F, FIGHTMOVE_KNEE, FIGHTMOVE_PUNCHHOOK, FIGHTMOVE_PUNCHJAB, FIGHTMOVE_PUNCH, FIGHTMOVE_LONGKICK, FIGHTMOVE_ROUNDHOUSE, // Directionals FIGHTMOVE_FWDLEFT, FIGHTMOVE_FWDRIGHT, FIGHTMOVE_BACKKICK, FIGHTMOVE_BACKFLIP, FIGHTMOVE_BACKLEFT, FIGHTMOVE_BACKRIGHT, FIGHTMOVE_RIGHTSWEEP, // Special FIGHTMOVE_GROUNDKICK, // Opponent FIGHTMOVE_HITFRONT, FIGHTMOVE_HITBACK, FIGHTMOVE_HITRIGHT, FIGHTMOVE_HITLEFT, FIGHTMOVE_HITBODY, FIGHTMOVE_HITCHEST, FIGHTMOVE_HITHEAD, FIGHTMOVE_HITBIGSTEP, FIGHTMOVE_HITONFLOOR, FIGHTMOVE_HITBEHIND, FIGHTMOVE_MELEE1, FIGHTMOVE_MELEE2, FIGHTMOVE_MELEE3, FIGHTMOVE_IDLE2NORM, NUM_FIGHTMOVES }; enum ePedPieceTypes { PEDPIECE_TORSO, PEDPIECE_MID, PEDPIECE_LEFTARM, PEDPIECE_RIGHTARM, PEDPIECE_LEFTLEG, PEDPIECE_RIGHTLEG, PEDPIECE_HEAD, }; enum eWaitState { WAITSTATE_FALSE, WAITSTATE_TRAFFIC_LIGHTS, WAITSTATE_CROSS_ROAD, WAITSTATE_CROSS_ROAD_LOOK, WAITSTATE_LOOK_PED, WAITSTATE_LOOK_SHOP, WAITSTATE_LOOK_ACCIDENT, WAITSTATE_FACEOFF_GANG, WAITSTATE_DOUBLEBACK, WAITSTATE_HITWALL, WAITSTATE_TURN180, WAITSTATE_SURPRISE, WAITSTATE_STUCK, WAITSTATE_LOOK_ABOUT, WAITSTATE_PLAYANIM_DUCK, WAITSTATE_PLAYANIM_COWER, WAITSTATE_PLAYANIM_TAXI, WAITSTATE_PLAYANIM_HANDSUP, WAITSTATE_PLAYANIM_HANDSCOWER, WAITSTATE_PLAYANIM_CHAT, WAITSTATE_FINISH_FLEE, WAITSTATE_SIT_DOWN, WAITSTATE_SIT_DOWN_RVRS, WAITSTATE_SIT_UP, WAITSTATE_SIT_IDLE, WAITSTATE_USE_ATM, WAITSTATE_SUN_BATHE_PRE, WAITSTATE_SUN_BATHE_DOWN, WAITSTATE_SUN_BATHE_IDLE, WAITSTATE_RIOT, WAITSTATE_FAST_FALL, WAITSTATE_BOMBER, WAITSTATE_STRIPPER, WAITSTATE_GROUND_ATTACK, WAITSTATE_LANCESITTING, WAITSTATE_PLAYANIM_HANDSUP_SIMPLE, }; enum eObjective { OBJECTIVE_NONE, OBJECTIVE_WAIT_ON_FOOT, OBJECTIVE_WAIT_ON_FOOT_FOR_COP, OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE, OBJECTIVE_GUARD_SPOT, OBJECTIVE_GUARD_AREA, OBJECTIVE_WAIT_IN_CAR, OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT, OBJECTIVE_KILL_CHAR_ON_FOOT, OBJECTIVE_KILL_CHAR_ANY_MEANS, OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, OBJECTIVE_GOTO_CHAR_ON_FOOT, OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING, OBJECTIVE_HASSLE_CHAR, OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, OBJECTIVE_LEAVE_CAR, OBJECTIVE_ENTER_CAR_AS_PASSENGER, OBJECTIVE_ENTER_CAR_AS_DRIVER, OBJECTIVE_FOLLOW_CAR_IN_CAR, OBJECTIVE_FIRE_AT_OBJECT_FROM_VEHICLE, OBJECTIVE_DESTROY_OBJECT, OBJECTIVE_DESTROY_CAR, OBJECTIVE_GOTO_AREA_ANY_MEANS, OBJECTIVE_GOTO_AREA_ON_FOOT, OBJECTIVE_RUN_TO_AREA, OBJECTIVE_GOTO_AREA_IN_CAR, OBJECTIVE_FOLLOW_CAR_ON_FOOT_WITH_OFFSET, OBJECTIVE_GUARD_ATTACK, OBJECTIVE_SET_LEADER, OBJECTIVE_FOLLOW_ROUTE, OBJECTIVE_SOLICIT_VEHICLE, OBJECTIVE_HAIL_TAXI, OBJECTIVE_CATCH_TRAIN, OBJECTIVE_BUY_ICE_CREAM, OBJECTIVE_STEAL_ANY_CAR, OBJECTIVE_STEAL_ANY_MISSION_CAR, OBJECTIVE_MUG_CHAR, OBJECTIVE_LEAVE_CAR_AND_DIE, OBJECTIVE_GOTO_SEAT_ON_FOOT, OBJECTIVE_GOTO_ATM_ON_FOOT, OBJECTIVE_FLEE_CAR, OBJECTIVE_SUN_BATHE, OBJECTIVE_GOTO_BUS_STOP_ON_FOOT, OBJECTIVE_GOTO_PIZZA_ON_FOOT, OBJECTIVE_GOTO_SHELTER_ON_FOOT, OBJECTIVE_AIM_GUN_AT, OBJECTIVE_WANDER, OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER, OBJECTIVE_SPRINT_TO_AREA, OBJECTIVE_KILL_CHAR_ON_BOAT, OBJECTIVE_SOLICIT_FOOT, OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP, OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT, OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN, OBJ_55, OBJ_56, OBJ_57, OBJ_58, OBJ_59 }; enum { RANDOM_CHAR = 1, MISSION_CHAR, UNK_CHAR, }; enum PedLineUpPhase { LINE_UP_TO_CAR_START, LINE_UP_TO_CAR_END, LINE_UP_TO_CAR_2, // Buggy. Used for cops arresting you from passenger door LINE_UP_TO_CAR_FALL }; enum PedOnGroundState { NO_PED, PED_IN_FRONT_OF_ATTACKER, PED_ON_THE_FLOOR, PED_DEAD_ON_THE_FLOOR }; enum PointBlankNecessity { NO_POINT_BLANK_PED, POINT_BLANK_FOR_WANTED_PED, POINT_BLANK_FOR_SOMEONE_ELSE }; enum PedState { PED_NONE, PED_IDLE, PED_LOOK_ENTITY, PED_LOOK_HEADING, PED_WANDER_RANGE, PED_WANDER_PATH, PED_SEEK_POS, PED_SEEK_ENTITY, PED_FLEE_POS, PED_FLEE_ENTITY, PED_PURSUE, PED_FOLLOW_PATH, PED_SNIPER_MODE, PED_ROCKET_MODE, PED_DUMMY, PED_PAUSE, PED_ATTACK, PED_FIGHT, PED_FACE_PHONE, PED_MAKE_CALL, PED_CHAT, PED_MUG, PED_AIM_GUN, PED_AI_CONTROL, PED_SEEK_CAR, PED_SEEK_IN_BOAT, PED_FOLLOW_ROUTE, PED_CPR, PED_SOLICIT, PED_BUY_ICECREAM, PED_INVESTIGATE, PED_STEP_AWAY, PED_ON_FIRE, PED_SUN_BATHE, PED_FLASH, PED_JOG, PED_ANSWER_MOBILE, PED_UNKNOWN, // Same with IDLE, but also infects up to 5 peds with same pedType and WANDER_PATH, so they become stone too. HANG_OUT in Fire_Head's idb PED_STATES_NO_AI, PED_ABSEIL, PED_SIT, PED_JUMP, PED_FALL, PED_GETUP, PED_STAGGER, PED_DIVE_AWAY, PED_STATES_NO_ST, PED_ENTER_TRAIN, PED_EXIT_TRAIN, PED_ARREST_PLAYER, PED_DRIVING, PED_PASSENGER, PED_TAXI_PASSENGER, PED_OPEN_DOOR, PED_DIE, PED_DEAD, PED_CARJACK, PED_DRAG_FROM_CAR, PED_ENTER_CAR, PED_STEAL_CAR, PED_EXIT_CAR, PED_HANDS_UP, PED_ARRESTED, PED_DEPLOY_STINGER }; enum eMoveState { PEDMOVE_NONE, PEDMOVE_STILL, PEDMOVE_WALK, PEDMOVE_JOG, PEDMOVE_RUN, PEDMOVE_SPRINT, PEDMOVE_THROWN }; extern float gfTommyFatness; class CVehicle; class CPed : public CPhysical { public: #ifdef USE_CUTSCENE_SHADOW_FOR_PED class CCutsceneShadow *m_pRTShadow; #endif // 0x128 CStoredCollPoly m_collPoly; float m_fCollisionSpeed; // cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CPed.h from R* uint32 bIsStanding : 1; uint32 bWasStanding : 1; uint32 bIsAttacking : 1; // doesn't reset after fist fight uint32 bIsPointingGunAt : 1; uint32 bIsLooking : 1; uint32 bKeepTryingToLook : 1; // if we can't look somewhere due to unreachable angles uint32 bIsRestoringLook : 1; uint32 bIsAimingGun : 1; uint32 bIsRestoringGun : 1; uint32 bCanPointGunAtTarget : 1; uint32 bIsTalking : 1; uint32 bIsInTheAir : 1; uint32 bIsLanding : 1; uint32 bIsRunning : 1; // on some conditions uint32 bHitSomethingLastFrame : 1; uint32 bVehEnterDoorIsBlocked : 1; // because someone else enters/exits from there uint32 bCanPedEnterSeekedCar : 1; uint32 bRespondsToThreats : 1; uint32 bRenderPedInCar : 1; uint32 bChangedSeat : 1; uint32 bUpdateAnimHeading : 1; uint32 bBodyPartJustCameOff : 1; uint32 bIsShooting : 1; uint32 bFindNewNodeAfterStateRestore : 1; uint32 bHasACamera : 1; // does ped possess a camera to document accidents involves fire/explosion uint32 bGonnaInvestigateEvent : 1; uint32 bPedIsBleeding : 1; uint32 bStopAndShoot : 1; // Ped cannot reach target to attack with fist, need to use gun uint32 bIsPedDieAnimPlaying : 1; uint32 bUsePedNodeSeek : 1; uint32 bObjectiveCompleted : 1; uint32 bScriptObjectiveCompleted : 1; uint32 bKindaStayInSamePlace : 1; uint32 bBeingChasedByPolice : 1; uint32 bNotAllowedToDuck : 1; uint32 bCrouchWhenShooting : 1; uint32 bIsDucking : 1; uint32 bGetUpAnimStarted : 1; uint32 bDoBloodyFootprints : 1; uint32 bFleeAfterExitingCar : 1; uint32 bWanderPathAfterExitingCar : 1; uint32 bIsLeader : 1; uint32 bDontDragMeOutCar : 1; // unfinished feature uint32 m_ped_flagF8 : 1; uint32 bWillBeQuickJacked : 1; uint32 bCancelEnteringCar : 1; // after door is opened or couldn't be opened due to it's locked uint32 bObstacleShowedUpDuringKillObjective : 1; uint32 bDuckAndCover : 1; uint32 bStillOnValidPoly : 1; // set if the polygon the ped is on is still valid for collision uint32 bAllowMedicsToReviveMe : 1; uint32 bResetWalkAnims : 1; uint32 bStartWanderPathOnFoot : 1; // exits the car if he's in it, reset after path found uint32 bOnBoat : 1; // not just driver, may be just standing uint32 bBusJacked : 1; uint32 bGonnaKillTheCarJacker : 1; // only set when car is jacked from right door and when arrested by police uint32 bFadeOut : 1; uint32 bKnockedUpIntoAir : 1; // has ped been knocked up into the air by a car collision uint32 bHitSteepSlope : 1; // has ped collided/is standing on a steep slope (surface type) uint32 bCullExtraFarAway : 1; // special ped only gets culled if it's extra far away (for roadblocks) uint32 bClearObjective : 1; uint32 bTryingToReachDryLand : 1; // has ped just exited boat and trying to get to dry land uint32 bCollidedWithMyVehicle : 1; uint32 bRichFromMugging : 1; // ped has lots of cash cause they've been mugging people uint32 bChrisCriminal : 1; // Is a criminal as killed during Chris' police mission (should be counted as such) uint32 bShakeFist : 1; // test shake hand at look entity uint32 bNoCriticalHits : 1; // if set, limbs won't came off uint32 bVehExitWillBeInstant : 1; uint32 bHasAlreadyBeenRecorded : 1; uint32 bFallenDown : 1; uint32 bDontAcceptIKLookAts : 1; uint32 bReachedAttractorHeadingTarget : 1; uint32 bTurnedAroundOnAttractor : 1; uint32 bHasAlreadyUsedAttractor : 1; uint32 bHasAlreadyStoleACar : 1; uint32 bCarPassenger : 1; uint32 bFleeWhenStanding : 1; uint32 bGotUpOfMyOwnAccord : 1; uint32 bMiamiViceCop : 1; uint32 bMoneyHasBeenGivenByScript : 1; // uint32 bHasBeenPhotographed : 1; // uint32 bIsDrowning : 1; uint32 bDrownsInWater : 1; uint32 bWaitForLeaderToComeCloser : 1; uint32 bHeldHostageInCar : 1; uint32 bIsPlayerFriend : 1; uint32 bHeadStuckInCollision : 1; uint32 bDeadPedInFrontOfCar : 1; uint32 bStayInCarOnJack : 1; uint32 bDontFight : 1; uint32 bDoomAim : 1; uint32 bCanBeShotInVehicle : 1; uint32 bCanGiveUpSunbathing : 1; uint32 bMakeFleeScream : 1; uint32 bPushedAlongByCar : 1; uint32 bRemoveMeWhenIGotIntoCar : 1; uint32 bIgnoreThreatsBehindObjects : 1; uint32 bNeverEverTargetThisPed : 1; uint32 bCrouchWhenScared : 1; uint32 bKnockedOffBike : 1; uint32 b158_8 : 1; uint32 bCollectBusFare : 1; uint32 bBoughtIceCream : 1; uint32 bDonePositionOutOfCollision : 1; uint32 bCanAttackPlayerWithCops : 1; #ifdef KANGAROO_CHEAT // our own flags uint32 m_ped_flagI80 : 1; // KANGAROO_CHEAT define makes use of this as cheat toggle #endif uint8 m_gangFlags; uint8 m_unused15D; // these 3 can't be padding but had to actually have been members ... uint8 m_unused15E; uint8 m_unused15F; uint8 CharCreatedBy; eObjective m_objective; eObjective m_prevObjective; CPed *m_pedInObjective; CVehicle *m_carInObjective; CVector m_nextRoutePointPos; float m_attractorHeading; CPed *m_leader; eFormation m_pedFormation; uint32 m_fearFlags; CEntity *m_threatEntity; CVector2D m_eventOrThreat; uint32 m_eventType; CEntity* m_pEventEntity; float m_fAngleToEvent; AnimBlendFrameData *m_pFrames[PED_NODE_MAX]; RpAtomic *m_pWeaponModel; AssocGroupId m_animGroup; CAnimBlendAssociation *m_pVehicleAnim; CVector2D m_vecAnimMoveDelta; CVector m_vecOffsetSeek; CPedIK m_pedIK; float m_actionX; float m_actionY; uint32 m_nPedStateTimer; PedState m_nPedState; PedState m_nLastPedState; eMoveState m_nMoveState; int32 m_nStoredMoveState; int32 m_nPrevMoveState; eWaitState m_nWaitState; uint32 m_nWaitTimer; CPathNode* m_pathNodesToGo[8]; int16 m_nNumPathNodes; int16 m_nCurPathNodeId; CEntity* m_followPathWalkAroundEnt; CEntity* m_followPathTargetEnt; uint32 m_pathNodeTimer; CPathNode m_pathNodeObjPool[8]; CPathNode* m_pCurPathNode; int8 m_nPathDir; CPathNode* m_pLastPathNode; CPathNode* m_pNextPathNode; CVector m_followPathDestPos; float m_followPathAbortDist; eMoveState m_followPathMoveState; float m_fHealth; float m_fArmour; uint32 m_nExtendedRangeTimer; int16 m_routeLastPoint; uint16 m_routeStartPoint; int16 m_routePointsPassed; int16 m_routeType; // See PedRouteType int16 m_routePointsBeingPassed; CVector2D m_moved; float m_fRotationCur; float m_fRotationDest; float m_headingRate; uint16 m_vehDoor; int16 m_walkAroundType; CPhysical *m_pCurrentPhysSurface; CVector m_vecOffsetFromPhysSurface; CEntity *m_pCurSurface; CVector m_vecSeekPos; CEntity *m_pSeekTarget; CVehicle *m_pMyVehicle; bool bInVehicle; float m_distanceToCountSeekDone; float m_acceptableHeadingOffset; CVehicle* m_vehicleInAccident; CPedAttractor* m_attractor; int32 m_positionInQueue; bool bRunningToPhone; int16 m_phoneId; eCrimeType m_crimeToReportOnPhone; uint32 m_phoneTalkTimer; CAccident *m_lastAccident; uint32 m_nPedType; CPedStats *m_pedStats; CVector2D m_fleeFromPos; CEntity *m_fleeFrom; uint32 m_fleeTimer; CEntity* m_threatEx; // TODO(Miami): What is this? CEntity* m_collidingEntityWhileFleeing; uint32 m_collidingThingTimer; CEntity *m_pCollidingEntity; uint8 m_stateUnused; uint32 m_timerUnused; class CRange2D *m_wanderRangeBounds; CWeapon m_weapons[TOTAL_WEAPON_SLOTS]; eWeaponType m_storedWeapon; eWeaponType m_delayedWeapon; uint32 m_delayedWeaponAmmo; uint8 m_currentWeapon; // eWeaponType uint8 m_maxWeaponTypeAllowed; // eWeaponType uint8 m_wepSkills; uint8 m_wepAccuracy; CEntity *m_pPointGunAt; CVector m_vecHitLastPos; uint32 m_curFightMove; uint32 m_lastFightMove; uint8 m_fightButtonPressure; int8 m_fightState; bool m_takeAStepAfterAttack; uint8 m_bleedCounter; CFire *m_pFire; CEntity *m_pLookTarget; float m_fLookDirection; int32 m_wepModelID; uint32 m_leaveCarTimer; uint32 m_getUpTimer; uint32 m_lookTimer; uint32 m_chatTimer; uint32 m_attackTimer; uint32 m_shootTimer; // shooting is a part of attack uint32 m_carJackTimer; uint32 m_objectiveTimer; uint32 m_duckTimer; uint32 m_duckAndCoverTimer; uint32 m_bloodyFootprintCountOrDeathTime; // Death time when bDoBloodyFootprints is false. Weird decision uint32 m_shotTime; uint32 m_ceaseAttackTimer; uint8 m_panicCounter; bool m_deadBleeding; int8 m_bodyPartBleeding; // PedNode, but -1 if there isn't CPed *m_nearPeds[10]; uint16 m_numNearPeds; uint16 m_nPedMoney; int8 m_lastWepDam; CEntity *m_lastDamEntity; CEntity *m_attachedTo; CVector m_vecAttachOffset; uint16 m_attachType; float m_attachRotStep; uint32 m_attachWepAmmo; uint32 m_threatFlags; uint32 m_threatCheckTimer; uint32 m_threatCheckInterval; int32 m_delayedSoundID; uint32 m_delayedSoundTimer; uint32 m_lastSoundStart; uint32 m_soundStart; uint16 m_lastQueuedSound; uint16 m_queuedSound; bool m_canTalk; uint32 m_lastComment; CVector m_vecSpotToGuard; float m_radiusToGuard; 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); CPed(uint32 pedType); ~CPed(void); void DeleteRwObject(); void SetModelIndex(uint32 mi); void ProcessControl(void); void Teleport(CVector); void PreRender(void); void Render(void); bool SetupLighting(void); void RemoveLighting(bool); void FlagToDestroyWhenNextProcessed(void); int32 ProcessEntityCollision(CEntity*, CColPoint*); virtual void SetMoveAnim(void); void AddWeaponModel(int id); void AimGun(void); void KillPedWithCar(CVehicle *veh, float impulse); void Say(uint16 audio); void Say(uint16 audio, int32 time); void SetLookFlag(CEntity* target, bool keepTryingToLook, bool cancelPrevious = false); void SetLookFlag(float direction, bool keepTryingToLook, bool cancelPrevious = false); void SetLookTimer(int time); void SetDie(AnimationId anim = ANIM_STD_KO_FRONT, float arg1 = 4.0f, float arg2 = 0.0f); void SetDead(void); void ApplyHeadShot(eWeaponType weaponType, CVector pos, bool evenOnPlayer); void RemoveBodyPart(PedNode nodeId, int8 direction); bool OurPedCanSeeThisOne(CEntity *target, bool shootablesDoBlock = false); void Avoid(void); void Attack(void); void ClearAimFlag(void); void ClearLookFlag(void); void RestorePreviousState(void); void ClearAttack(void); bool IsPedHeadAbovePos(float zOffset); void RemoveWeaponModel(int modelId); void SetCurrentWeapon(eWeaponType weaponType); void SetCurrentWeapon(int weapon); void Duck(void); void ClearDuck(bool = false); void ClearPointGunAt(void); void BeingDraggedFromCar(void); void RestartNonPartialAnims(void); void LineUpPedWithCar(PedLineUpPhase phase); void SetPedPositionInCar(void); void PlayFootSteps(void); void QuitEnteringCar(void); void BuildPedLists(void); int32 GiveWeapon(eWeaponType weaponType, uint32 ammo, bool unused = true); void CalculateNewOrientation(void); float WorkOutHeadingForMovingFirstPerson(float); void CalculateNewVelocity(void); bool CanSeeEntity(CEntity*, float threshold = CAN_SEE_ENTITY_ANGLE_THRESHOLD); void RestorePreviousObjective(void); void SetIdle(void); #ifdef _MSC_VER __declspec(noinline) // workaround for a compiler bug, hooray MS :P #endif void SetObjective(eObjective, void*); void SetObjective(eObjective); void SetObjective(eObjective, int16, int16); void SetObjective(eObjective, CVector); void SetObjective(eObjective, float, const CVector&); void ClearChat(void); void InformMyGangOfAttack(CEntity*); void ReactToAttack(CEntity*); void SetDuck(uint32, bool = false); void RegisterThreatWithGangPeds(CEntity*); bool TurnBody(void); void Chat(void); void CheckAroundForPossibleCollisions(void); void SetSeek(CVector, float); void SetSeek(CEntity*, float); bool MakePhonecall(void); bool FacePhone(void); CPed *CheckForDeadPeds(void); bool CheckForExplosions(CVector2D &area); CPed *CheckForGunShots(void); uint8 CheckForPointBlankPeds(CPed*); bool CheckIfInTheAir(void); void ClearAll(void); void SetPointGunAt(CEntity*); bool Seek(void); bool SetWanderPath(int8); bool SetFollowPath(CVector dest, float radius, eMoveState state, CEntity*, CEntity*, int); bool SetFollowPathStatic(void); bool SetFollowPathDynamic(void); void ClearAttackByRemovingAnim(void); void SetStoredState(void); void StopNonPartialAnims(void); bool InflictDamage(CEntity*, eWeaponType, float, ePedPieceTypes, uint8); void ClearFlee(void); void ClearFall(void); void SetGetUp(void); void ClearInvestigateEvent(void); void ClearLeader(void); void ClearLook(void); void ClearObjective(void); void ClearPause(void); void ClearSeek(void); void ClearWeapons(void); void RestoreGunPosition(void); void RestoreHeadingRate(void); void SetAimFlag(CEntity* to); void SetAimFlag(float angle); void SetAmmo(eWeaponType weaponType, uint32 ammo); void SetEvasiveStep(CEntity*, uint8); void GrantAmmo(eWeaponType, uint32); void SetEvasiveDive(CPhysical*, uint8); void SetAttack(CEntity*); void StartFightAttack(uint8); void SetWaitState(eWaitState, void*); bool FightStrike(CVector&, bool); void FightHitPed(CPed*, CVector&, CVector&, int16); int32 ChooseAttackPlayer(uint8, bool); int32 ChooseAttackAI(uint8, bool); int GetLocalDirection(const CVector2D &); void StartFightDefend(uint8, uint8, uint8); void PlayHitSound(CPed*); void SetFall(int, AnimationId, uint8); void SetFlee(CEntity*, int); void SetFlee(CVector2D const &, int); void RemoveDrivebyAnims(void); void RemoveInCarAnims(void); void CollideWithPed(CPed*); void SetDirectionToWalkAroundObject(CEntity*); bool SetDirectionToWalkAroundVehicle(CVehicle*); void RemoveWeaponAnims(int, float); void CreateDeadPedMoney(void); void CreateDeadPedWeaponPickups(void); void CreateDeadPedPickupCoors(float *x, float *y, float *z); void SetAttackTimer(uint32); void SetBeingDraggedFromCar(CVehicle*, uint32, bool); void SetRadioStation(void); void SetBuyIceCream(void); void SetChat(CEntity*, uint32); void DeadPedMakesTyresBloody(void); void MakeTyresMuddySectorList(CPtrList&); bool DuckAndCover(void); void EndFight(uint8); void EnterCar(void); uint8 GetNearestTrainPedPosition(CVehicle*, CVector&); uint8 GetNearestTrainDoor(CVehicle*, CVector&); void ExitCar(void); void Fight(void); bool FindBestCoordsFromNodes(CVector, CVector*); void Wait(void); void ProcessObjective(void); CVector *SeekFollowingPath(void); void Flee(void); void FollowPath(void); CVector GetFormationPosition(void); void GetNearestDoor(CVehicle*, CVector&); bool GetNearestPassengerDoor(CVehicle*, CVector&); int GetNextPointOnRoute(void); int GetWeaponSlot(eWeaponType); bool CanWeRunAndFireWithWeapon(void); void GoToNearestDoor(CVehicle*); bool HaveReachedNextPointOnRoute(float); void Idle(void); void InTheAir(void); void SetLanding(void); void InvestigateEvent(void); bool IsPedDoingDriveByShooting(void); bool IsRoomToBeCarJacked(void); void SetInvestigateEvent(eEventType, CVector2D, float, uint16, float); bool LookForInterestingNodes(void); void LookForSexyCars(void); void LookForSexyPeds(void); void Mug(void); void MoveHeadToLook(void); void Pause(void); void ProcessBuoyancy(void); void ServiceTalking(void); void SetJump(void); void WanderPath(void); void ReactToPointGun(CEntity*); void SeekCar(void); bool PositionPedOutOfCollision(void); bool PositionAnyPedOutOfCollision(void); bool RunToReportCrime(eCrimeType); bool PlacePedOnDryLand(void); bool PossiblyFindBetterPosToSeekCar(CVector*, CVehicle*); void UpdateFromLeader(void); uint32 ScanForThreats(void); void SetEnterCar(CVehicle*, uint32); bool WarpPedToNearEntityOffScreen(CEntity*); void SetExitCar(CVehicle*, uint32); void SetFormation(eFormation); bool WillChat(CPed*); void SetEnterCar_AllClear(CVehicle*, uint32, uint32); void SetSolicit(uint32 time); void ScanForInterestingStuff(void); void WarpPedIntoCar(CVehicle*); void SetCarJack(CVehicle*); bool WarpPedToNearLeaderOffScreen(void); void Solicit(void); void SetExitBoat(CVehicle*); void ClearFollowPath(); void GiveDelayedWeapon(eWeaponType weapon, uint32 ammo); void RequestDelayedWeapon(); void AddInCarAnims(CVehicle* car, bool isDriver); bool CanBeDamagedByThisGangMember(CPed*); void AnswerMobile(void); void BuyIceCream(void); void CheckThreatValidity(void); void ClearAnswerMobile(void); void SetAnswerMobile(void); void AttachPedToEntity(CEntity*, CVector, uint16, float, eWeaponType); void DettachPedFromEntity(); void PedShuffle(); void DriveVehicle(); void PositionAttachedPed(); bool CanUseTorsoWhenLooking(); void ScanForDelayedResponseThreats(); // Static methods static CVector GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset); static CVector GetPositionToOpenCarDoor(CVehicle *veh, uint32 component, float seatPosMult); static CVector GetPositionToOpenCarDoor(CVehicle* veh, uint32 component); static void Initialise(void); static void SetAnimOffsetForEnterOrExitVehicle(void); static void LoadFightData(void); // Callbacks static void PedGetupCB(CAnimBlendAssociation *assoc, void *arg); static void PedStaggerCB(CAnimBlendAssociation *assoc, void *arg); static void PedEvadeCB(CAnimBlendAssociation *assoc, void *arg); static void FinishDieAnimCB(CAnimBlendAssociation *assoc, void *arg); static void FinishedWaitCB(CAnimBlendAssociation *assoc, void *arg); static void FinishLaunchCB(CAnimBlendAssociation *assoc, void *arg); static void FinishHitHeadCB(CAnimBlendAssociation *assoc, void *arg); static void PedAnimGetInCB(CAnimBlendAssociation *assoc, void *arg); static void PedAnimDoorOpenCB(CAnimBlendAssociation *assoc, void *arg); static void PedAnimPullPedOutCB(CAnimBlendAssociation *assoc, void *arg); static void PedAnimDoorCloseCB(CAnimBlendAssociation *assoc, void *arg); static void PedSetInCarCB(CAnimBlendAssociation *assoc, void *arg); static void PedSetOutCarCB(CAnimBlendAssociation *assoc, void *arg); static void PedAnimAlignCB(CAnimBlendAssociation *assoc, void *arg); static void PedSetDraggedOutCarCB(CAnimBlendAssociation *assoc, void *arg); static void PedAnimStepOutCarCB(CAnimBlendAssociation *assoc, void *arg); static void PedSetInTrainCB(CAnimBlendAssociation *assoc, void *arg); #ifdef GTA_TRAIN static void PedSetOutTrainCB(CAnimBlendAssociation *assoc, void *arg); #endif static void FinishedAttackCB(CAnimBlendAssociation *assoc, void *arg); static void FinishedReloadCB(CAnimBlendAssociation *assoc, void *arg); static void FinishFightMoveCB(CAnimBlendAssociation *assoc, void *arg); static void PedAnimDoorCloseRollingCB(CAnimBlendAssociation *assoc, void *arg); static void FinishJumpCB(CAnimBlendAssociation *assoc, void *arg); static void PedLandCB(CAnimBlendAssociation *assoc, void *arg); static void RestoreHeadingRateCB(CAnimBlendAssociation *assoc, void *arg); static void PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation *assoc, void *arg); static void PedSetDraggedOutCarPositionCB(CAnimBlendAssociation *assoc, void *arg); static void DeleteSunbatheIdleAnimCB(CAnimBlendAssociation *assoc, void *arg); static void PedSetPreviousStateCB(CAnimBlendAssociation *assoc, void *arg); static void PedAnimShuffleCB(CAnimBlendAssociation *assoc, void *arg); static void PedSetGetInCarPositionCB(CAnimBlendAssociation* assoc, void* arg); bool IsPlayer(void) const; bool IsFemale(void) { return m_nPedType == PEDTYPE_CIVFEMALE || m_nPedType == PEDTYPE_PROSTITUTE; } bool UseGroundColModel(void); bool CanSetPedState(void); bool IsPedInControl(void); bool CanPedDriveOff(void); bool CanBeDeleted(void); bool CanBeDeletedEvenInVehicle(void); bool CanStrafeOrMouseControl(void); bool CanPedReturnToState(void); void SetMoveState(eMoveState); bool IsTemporaryObjective(eObjective objective); void SetObjectiveTimer(int); bool SelectGunIfArmed(void); bool IsPointerValid(void); void SortPeds(CPed**, int, int); void ForceStoredObjective(eObjective); void SetStoredObjective(void); void SetLeader(CEntity* leader); void SetPedStats(ePedStats); bool IsGangMember(void) const; void Die(void); #ifdef GTA_TRAIN void EnterTrain(void); void ExitTrain(void); void SetExitTrain(CVehicle*); void SetPedPositionInTrain(void); void LineUpPedWithTrain(void); void SetEnterTrain(CVehicle*, uint32); #endif void Fall(void); bool IsPedShootable(void); void Look(void); void SetInTheAir(void); void RestoreHeadPosition(void); void PointGunAt(void); bool ServiceTalkingWhenDead(void); void SetShootTimer(uint32); void SetSeekCar(CVehicle*, uint32); void SetSeekBoatPosition(CVehicle*); void WanderRange(void); void SetFollowRoute(int16, int16); void SeekBoatPosition(void); void UpdatePosition(void); CObject *SpawnFlyingComponent(int, int8); void SetCarJack_AllClear(CVehicle*, uint32, uint32); bool CanPedJumpThis(CEntity *unused, CVector *damageNormal = nil); void SetNewAttraction(CPedAttractor* pAttractor, const CVector& pos, float, float, int); void ClearWaitState(void); void Undress(const char*); void Dress(void); int32 KillCharOnFootMelee(CVector&, CVector&, CVector&); int32 KillCharOnFootArmed(CVector&, CVector&, CVector&); void SetLook(CEntity* to); void SetLook(float direction); bool HasWeaponSlot(uint8 slot) { return m_weapons[slot].m_eWeaponType != WEAPONTYPE_UNARMED; } CWeapon& GetWeapon(uint8 slot) { return m_weapons[slot]; } CWeapon *GetWeapon(void) { return &m_weapons[m_currentWeapon]; } PedState GetPedState(void) { return m_nPedState; } void SetPedState(PedState state) { if (GetPedState() == PED_FOLLOW_PATH && state != PED_FOLLOW_PATH) ClearFollowPath(); m_nPedState = state; } bool Dead(void) { return m_nPedState == PED_DEAD; } bool Dying(void) { return m_nPedState == PED_DIE; } bool DyingOrDead(void) { return m_nPedState == PED_DIE || m_nPedState == PED_DEAD; } bool OnGround(void) { return m_nPedState == PED_FALL || m_nPedState == PED_DIE || m_nPedState == PED_DEAD; } bool OnGroundOrGettingUp(void) { return OnGround() || m_nPedState == PED_GETUP; } bool Driving(void) { return m_nPedState == PED_DRIVING; } bool InVehicle(void) { return bInVehicle && m_pMyVehicle; } // True when ped is sitting/standing in vehicle, not in enter/exit state. bool EnteringCar(void) { return m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK; } bool HasAttractor(void); bool IsUseAttractorObjective(eObjective obj) { return obj == OBJECTIVE_GOTO_ATM_ON_FOOT || obj == OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT || obj == OBJECTIVE_GOTO_PIZZA_ON_FOOT || obj == OBJECTIVE_GOTO_SEAT_ON_FOOT || obj == OBJECTIVE_GOTO_SHELTER_ON_FOOT || obj == OBJECTIVE_GOTO_BUS_STOP_ON_FOOT; } void ReplaceWeaponWhenExitingVehicle(void); void RemoveWeaponWhenEnteringVehicle(void); bool IsNotInWreckedVehicle() { return m_pMyVehicle != nil && ((CEntity*)m_pMyVehicle)->GetStatus() != STATUS_WRECKED; } // My names. Inlined in VC AnimationId GetFireAnimNotDucking(CWeaponInfo* weapon) { if (m_nPedType == PEDTYPE_COP && weapon->IsFlagSet(WEAPONFLAG_COP3_RD)) return Get3rdFireAnim(weapon); else return GetPrimaryFireAnim(weapon); } static AnimationId Get3rdFireAnim(CWeaponInfo* weapon) { if (weapon->IsFlagSet(WEAPONFLAG_COP3_RD)) return ANIM_WEAPON_FIRE_3RD; else return (AnimationId)0; } static AnimationId GetFireAnimGround(CWeaponInfo* weapon, bool kickFloorIfNone = true) { if (weapon->IsFlagSet(WEAPONFLAG_GROUND_2ND)) return ANIM_WEAPON_CROUCHFIRE; else if (weapon->IsFlagSet(WEAPONFLAG_GROUND_3RD)) return ANIM_WEAPON_FIRE_3RD; else if (kickFloorIfNone) return ANIM_STD_KICKGROUND; else return (AnimationId)0; } static AnimationId GetPrimaryFireAnim(CWeaponInfo* weapon) { if (weapon->IsFlagSet(WEAPONFLAG_ANIMDETONATE)) return ANIM_STD_DETONATE; else return ANIM_WEAPON_FIRE; } static AnimationId GetCrouchReloadAnim(CWeaponInfo* weapon) { if (weapon->IsFlagSet(WEAPONFLAG_RELOAD)) return ANIM_WEAPON_CROUCHRELOAD; else return (AnimationId)0; } static AnimationId GetCrouchFireAnim(CWeaponInfo* weapon) { if (weapon->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) return ANIM_WEAPON_CROUCHFIRE; else return (AnimationId)0; } static AnimationId GetReloadAnim(CWeaponInfo* weapon) { if (weapon->IsFlagSet(WEAPONFLAG_RELOAD)) return ANIM_WEAPON_RELOAD; else return (AnimationId)0; } static AnimationId GetFightIdleWithMeleeAnim(CWeaponInfo* weapon) { if (weapon->IsFlagSet(WEAPONFLAG_FIGHTMODE)) return ANIM_MELEE_IDLE_FIGHTMODE; else return (AnimationId)0; } static AnimationId GetFinishingAttackAnim(CWeaponInfo* weapon) { if (weapon->IsFlagSet(WEAPONFLAG_FINISH_3RD)) return ANIM_MELEE_ATTACK_FINISH; else return (AnimationId)0; } static AnimationId GetSecondFireAnim(CWeaponInfo* weapon) { if (weapon->IsFlagSet(WEAPONFLAG_USE_2ND)) return ANIM_WEAPON_FIRE_2ND; else return (AnimationId)0; } static AnimationId GetMeleeStartAnim(CWeaponInfo* weapon) { if (weapon->IsFlagSet(WEAPONFLAG_PARTIALATTACK)) return ANIM_MELEE_ATTACK_START; else return (AnimationId)0; } static AnimationId GetThrowAnim(CWeaponInfo *weapon) { if (weapon->IsFlagSet(WEAPONFLAG_THROW)) return ANIM_THROWABLE_START_THROW; else return (AnimationId)0; } // -- // My additions, because there were many, many instances of that. inline void SetFindPathAndFlee(CEntity *fleeFrom, int time, bool walk = false) { SetFlee(fleeFrom, time); bUsePedNodeSeek = true; m_pNextPathNode = nil; if (walk) SetMoveState(PEDMOVE_WALK); } inline void SetFindPathAndFlee(CVector2D const &from, int time, bool walk = false) { SetFlee(from, time); bUsePedNodeSeek = true; m_pNextPathNode = nil; if (walk) SetMoveState(PEDMOVE_WALK); } // -- inline void SetWeaponLockOnTarget(CEntity *target) { if (m_pPointGunAt) m_pPointGunAt->CleanUpOldReference(&m_pPointGunAt); m_pPointGunAt = (CPed*)target; if (target) ((CEntity*)target)->RegisterReference(&m_pPointGunAt); } // Using this to abstract nodes of skinned and non-skinned meshes CVector GetNodePosition(int32 node) { RwV3d pos = { 0.0f, 0.0f, 0.0f }; RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump()); int32 idx = RpHAnimIDGetIndex(hier, m_pFrames[node]->nodeID); RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); pos = mats[idx].pos; return pos; } void TransformToNode(CVector &pos, int32 node) { RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(GetClump()); int32 idx = RpHAnimIDGetIndex(hier, m_pFrames[node]->nodeID); RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); RwV3dTransformPoints(&pos, &pos, 1, &mats[idx]); } // set by 0482:set_threat_reaction_range_multiplier opcode static uint16 nThreatReactionRangeMultiplier; // set by 0481:set_enter_car_range_multiplier opcode static uint16 nEnterCarRangeMultiplier; static bool bNastyLimbsCheat; static bool bFannyMagnetCheat; static bool bPedCheat3; static CVector2D ms_vec2DFleePosition; #ifndef MASTER // Mobile things void DebugDrawPedDestination(CPed *, int, int); void DebugDrawPedDesiredHeading(CPed *, int, int); void DebugDrawCollisionRadius(float, float, float, float, int); void DebugDrawVisionRange(CVector, float); void DebugDrawVisionSimple(CVector, float); void DebugDrawLook(); void DebugDrawPedPsyche(); void DebugDrawDebugLines(); static void SwitchDebugDisplay(void); static int GetDebugDisplay(void); void DebugDrawLookAtPoints(); void DebugRenderOnePedText(void); void DebugRenderClosePedText(); #endif #ifdef COMPATIBLE_SAVES virtual void Save(uint8*& buf); virtual void Load(uint8*& buf); #endif }; void FinishTalkingOnMobileCB(CAnimBlendAssociation* assoc, void* arg); void StartTalkingOnMobileCB(CAnimBlendAssociation* assoc, void* arg); void PlayRandomAnimationsFromAnimBlock(CPed* ped, AssocGroupId animGroup, uint32 first, uint32 amount); VALIDATE_SIZE(CPed, 0x5F4); bool IsPedPointerValid(CPed*); bool IsPedPointerValid_NotInWorld(CPed*); ================================================ FILE: src/peds/PedAI.cpp ================================================ #include "common.h" #include "main.h" #include "Particle.h" #include "RpAnimBlend.h" #include "Ped.h" #include "Wanted.h" #include "AnimBlendAssociation.h" #include "DMAudio.h" #include "General.h" #include "HandlingMgr.h" #include "Replay.h" #include "Camera.h" #include "PedPlacement.h" #include "ZoneCull.h" #include "Pad.h" #include "Pickups.h" #include "Train.h" #include "PedRoutes.h" #include "CopPed.h" #include "Script.h" #include "CarCtrl.h" #include "WaterLevel.h" #include "CarAI.h" #include "Zones.h" #include "Cranes.h" #include "PedAttractor.h" #include "Bike.h" #include "Weather.h" #include "GameLogic.h" #include "Streaming.h" CVector vecPedCarDoorAnimOffset; CVector vecPedCarDoorLoAnimOffset; CVector vecPedVanRearDoorAnimOffset; CVector vecPedQuickDraggedOutCarAnimOffset; CVector vecPedDraggedOutCarAnimOffset; CVector vecPedTrainDoorAnimOffset; CVector vecPedStdBikeJumpRhsAnimOffset; CVector vecPedVespaBikeJumpRhsAnimOffset; CVector vecPedHarleyBikeJumpRhsAnimOffset; CVector vecPedDirtBikeJumpRhsAnimOffset; CVector vecPedBikeKickAnimOffset; void CPed::SetObjectiveTimer(int time) { if (time == 0) { m_objectiveTimer = 0; } else if (CTimer::GetTimeInMilliseconds() > m_objectiveTimer) { m_objectiveTimer = CTimer::GetTimeInMilliseconds() + time; } } void CPed::SetStoredObjective(void) { if (m_objective == m_prevObjective) return; switch (m_objective) { case OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE: case OBJECTIVE_KILL_CHAR_ON_FOOT: case OBJECTIVE_KILL_CHAR_ANY_MEANS: case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: case OBJECTIVE_GOTO_CHAR_ON_FOOT: case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: case OBJECTIVE_HASSLE_CHAR: case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: case OBJECTIVE_LEAVE_CAR: case OBJECTIVE_ENTER_CAR_AS_PASSENGER: case OBJECTIVE_ENTER_CAR_AS_DRIVER: case OBJECTIVE_GOTO_AREA_ON_FOOT: case OBJECTIVE_RUN_TO_AREA: case OBJECTIVE_GOTO_SEAT_ON_FOOT: case OBJECTIVE_GOTO_ATM_ON_FOOT: case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: case OBJECTIVE_GOTO_PIZZA_ON_FOOT: case OBJECTIVE_GOTO_SHELTER_ON_FOOT: case OBJECTIVE_SPRINT_TO_AREA: case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: return; default: m_prevObjective = m_objective; } } void CPed::ForceStoredObjective(eObjective objective) { if (objective != OBJECTIVE_ENTER_CAR_AS_DRIVER && objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) { m_prevObjective = m_objective; return; } switch (m_objective) { case OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE: case OBJECTIVE_KILL_CHAR_ON_FOOT: case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: case OBJECTIVE_GOTO_CHAR_ON_FOOT: case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: case OBJECTIVE_HASSLE_CHAR: case OBJECTIVE_ENTER_CAR_AS_PASSENGER: case OBJECTIVE_ENTER_CAR_AS_DRIVER: case OBJECTIVE_GOTO_AREA_ON_FOOT: case OBJECTIVE_RUN_TO_AREA: case OBJECTIVE_GOTO_SEAT_ON_FOOT: case OBJECTIVE_GOTO_ATM_ON_FOOT: case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: case OBJECTIVE_GOTO_PIZZA_ON_FOOT: case OBJECTIVE_GOTO_SHELTER_ON_FOOT: case OBJECTIVE_SPRINT_TO_AREA: case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: return; default: m_prevObjective = m_objective; } } bool CPed::IsTemporaryObjective(eObjective objective) { return objective == OBJECTIVE_LEAVE_CAR || objective == OBJECTIVE_SET_LEADER || objective == OBJECTIVE_LEAVE_CAR_AND_DIE || objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER; } void CPed::SetObjective(eObjective newObj) { if (DyingOrDead() || m_attachedTo) return; if (newObj == OBJECTIVE_NONE) { if ((m_objective == OBJECTIVE_LEAVE_CAR || m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) && !IsPlayer() && !IsPedInControl()) { bStartWanderPathOnFoot = true; } else { m_objective = OBJECTIVE_NONE; m_prevObjective = OBJECTIVE_NONE; } } else if (m_prevObjective != newObj || m_prevObjective == OBJECTIVE_NONE) { SetObjectiveTimer(0); if (m_objective == newObj) return; if (IsTemporaryObjective(m_objective)) { m_prevObjective = newObj; } else { if (m_objective != newObj) SetStoredObjective(); m_objective = newObj; } bObjectiveCompleted = false; switch (newObj) { case OBJECTIVE_NONE: m_prevObjective = OBJECTIVE_NONE; break; case OBJECTIVE_HAIL_TAXI: m_nWaitTimer = 0; SetIdle(); SetMoveState(PEDMOVE_STILL); break; default: break; } } } void CPed::SetObjective(eObjective newObj, void *entity) { if (DyingOrDead()) return; if (m_prevObjective == newObj && m_prevObjective != OBJECTIVE_NONE) return; if (entity == this) return; if (m_attachedTo && newObj != OBJECTIVE_KILL_CHAR_ON_FOOT && newObj != OBJECTIVE_KILL_CHAR_ANY_MEANS && newObj != OBJECTIVE_DESTROY_OBJECT && newObj != OBJECTIVE_DESTROY_CAR) return; if (m_objective == newObj) { switch (newObj) { case OBJECTIVE_KILL_CHAR_ON_FOOT: case OBJECTIVE_KILL_CHAR_ANY_MEANS: case OBJECTIVE_GOTO_CHAR_ON_FOOT: case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: case OBJECTIVE_HASSLE_CHAR: case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: case OBJECTIVE_GOTO_AREA_ANY_MEANS: case OBJECTIVE_GUARD_ATTACK: case OBJECTIVE_KILL_CHAR_ON_BOAT: case OBJECTIVE_SOLICIT_FOOT: if (m_pedInObjective == entity) return; break; case OBJECTIVE_LEAVE_CAR: case OBJECTIVE_FLEE_CAR: case OBJECTIVE_LEAVE_CAR_AND_DIE: return; case OBJECTIVE_ENTER_CAR_AS_PASSENGER: case OBJECTIVE_ENTER_CAR_AS_DRIVER: case OBJECTIVE_DESTROY_CAR: case OBJECTIVE_SOLICIT_VEHICLE: case OBJECTIVE_BUY_ICE_CREAM: if (m_carInObjective == entity) return; if (newObj == OBJECTIVE_BUY_ICE_CREAM && bBoughtIceCream) return; break; case OBJECTIVE_SET_LEADER: if (m_leader == entity) return; break; case OBJECTIVE_AIM_GUN_AT: if (m_pedInObjective == entity) return; break; default: break; } } else { if (newObj != OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT && (newObj == OBJECTIVE_LEAVE_CAR || newObj == OBJECTIVE_LEAVE_CAR_AND_DIE) && !bInVehicle) return; } bObjectiveCompleted = false; ClearPointGunAt(); m_objectiveTimer = 0; if (IsTemporaryObjective(m_objective) && !IsTemporaryObjective(newObj)) { m_prevObjective = newObj; } else { if (m_objective != newObj) { if (IsTemporaryObjective(newObj)) ForceStoredObjective(newObj); else SetStoredObjective(); } m_objective = newObj; } switch (newObj) { case OBJECTIVE_WAIT_ON_FOOT_FOR_COP: m_pedInObjective = (CPed*)entity; m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); SetIdle(); SetLook(m_pedInObjective); break; case OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT: // In this special case, entity parameter isn't CEntity, but int. SetObjectiveTimer((uintptr)entity); break; case OBJECTIVE_KILL_CHAR_ON_FOOT: case OBJECTIVE_KILL_CHAR_ANY_MEANS: case OBJECTIVE_MUG_CHAR: case OBJECTIVE_KILL_CHAR_ON_BOAT: m_pNextPathNode = nil; bUsePedNodeSeek = false; if (m_pedInObjective) m_pedInObjective->CleanUpOldReference((CEntity**)&m_pedInObjective); if (m_pLookTarget) m_pLookTarget->CleanUpOldReference(&m_pLookTarget); m_pLookTarget = (CEntity*)entity; m_pedInObjective = (CPed*)entity; m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); // m_pLookTarget = (CEntity*)entity; // duplicate m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); break; case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: case OBJECTIVE_GOTO_CHAR_ON_FOOT: case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: case OBJECTIVE_HASSLE_CHAR: case OBJECTIVE_GUARD_ATTACK: if (m_pedInObjective) m_pedInObjective->CleanUpOldReference((CEntity**)&m_pedInObjective); m_pedInObjective = (CPed*)entity; m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); break; case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: m_pedInObjective = (CPed*)entity; m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); m_pedFormation = FORMATION_REAR; break; case OBJECTIVE_LEAVE_CAR: case OBJECTIVE_LEAVE_CAR_AND_DIE: case OBJECTIVE_FLEE_CAR: m_carInObjective = (CVehicle*)entity; m_carInObjective->RegisterReference((CEntity **)&m_carInObjective); if (m_carInObjective->bIsBus && m_leaveCarTimer == 0) { for (int i = 0; i < m_carInObjective->m_nNumMaxPassengers; i++) { if (m_carInObjective->pPassengers[i] == this) { m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 1200 * i; break; } } } break; case OBJECTIVE_DESTROY_OBJECT: SetWeaponLockOnTarget((CEntity*)entity); break; case OBJECTIVE_ENTER_CAR_AS_PASSENGER: case OBJECTIVE_ENTER_CAR_AS_DRIVER: if (m_nMoveState == PEDMOVE_STILL) SetMoveState(PEDMOVE_RUN); if (((CVehicle*)entity)->IsBoat() && !IsPlayer() && m_pCurrentPhysSurface != entity) { RestorePreviousObjective(); break; } // fall through case OBJECTIVE_DESTROY_CAR: case OBJECTIVE_SOLICIT_VEHICLE: case OBJECTIVE_BUY_ICE_CREAM: m_carInObjective = (CVehicle*)entity; m_carInObjective->RegisterReference((CEntity**)&m_carInObjective); m_pSeekTarget = m_carInObjective; m_pSeekTarget->RegisterReference((CEntity**)&m_pSeekTarget); m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); if (newObj == OBJECTIVE_SOLICIT_VEHICLE) { m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000; } else if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER && CharCreatedBy == MISSION_CHAR && (m_carInObjective->GetStatus() == STATUS_PLAYER_DISABLED || CPad::GetPad(CWorld::PlayerInFocus)->ArePlayerControlsDisabled())) { SetObjectiveTimer(14000); } else { m_objectiveTimer = 0; } break; case OBJECTIVE_SET_LEADER: SetLeader((CEntity*)entity); RestorePreviousObjective(); break; case OBJECTIVE_AIM_GUN_AT: m_pedInObjective = (CPed*)entity; m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); break; case OBJECTIVE_SOLICIT_FOOT: m_pedInObjective = (CPed*)entity; m_pedInObjective->RegisterReference((CEntity**)&m_pedInObjective); break; default: break; } } // Only used in 01E1: SET_CHAR_OBJ_FOLLOW_ROUTE opcode // IDA fails very badly in here, puts a fake loop and ignores SetFollowRoute call... void CPed::SetObjective(eObjective newObj, int16 routePoint, int16 routeType) { if (DyingOrDead()) return; if (m_prevObjective == newObj && m_prevObjective != OBJECTIVE_NONE) return; if (m_objective == newObj && newObj == OBJECTIVE_FOLLOW_ROUTE && m_routeLastPoint == routePoint && m_routeType == routeType) return; ClearPointGunAt(); SetObjectiveTimer(0); bObjectiveCompleted = false; if (IsTemporaryObjective(m_objective)) { m_prevObjective = newObj; } else { if (m_objective != newObj) SetStoredObjective(); m_objective = newObj; } if (newObj == OBJECTIVE_FOLLOW_ROUTE) { SetFollowRoute(routePoint, routeType); } } void CPed::SetObjective(eObjective newObj, CVector dest) { if (DyingOrDead()) return; if (m_prevObjective != OBJECTIVE_NONE && m_prevObjective == newObj) return; if (m_objective == newObj) { if (newObj == OBJECTIVE_GOTO_AREA_ANY_MEANS || newObj == OBJECTIVE_GOTO_AREA_ON_FOOT || newObj == OBJECTIVE_RUN_TO_AREA || newObj == OBJECTIVE_SPRINT_TO_AREA) { if (m_nextRoutePointPos == dest) return; } else if (newObj == OBJECTIVE_GUARD_SPOT) { if (m_vecSpotToGuard == dest) return; } } ClearPointGunAt(); m_objectiveTimer = 0; bObjectiveCompleted = false; switch (newObj) { case OBJECTIVE_GUARD_SPOT: m_vecSpotToGuard = dest; m_radiusToGuard = 5.0f; SetMoveState(PEDMOVE_STILL); break; case OBJECTIVE_GUARD_AREA: case OBJECTIVE_WAIT_IN_CAR: case OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT: case OBJECTIVE_KILL_CHAR_ON_FOOT: case OBJECTIVE_KILL_CHAR_ANY_MEANS: case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: case OBJECTIVE_GOTO_CHAR_ON_FOOT: case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: case OBJECTIVE_HASSLE_CHAR: case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: case OBJECTIVE_LEAVE_CAR: case OBJECTIVE_ENTER_CAR_AS_PASSENGER: case OBJECTIVE_ENTER_CAR_AS_DRIVER: case OBJECTIVE_FOLLOW_CAR_IN_CAR: case OBJECTIVE_FIRE_AT_OBJECT_FROM_VEHICLE: case OBJECTIVE_DESTROY_OBJECT: case OBJECTIVE_DESTROY_CAR: case OBJECTIVE_GOTO_AREA_IN_CAR: case OBJECTIVE_FOLLOW_CAR_ON_FOOT_WITH_OFFSET: case OBJECTIVE_GUARD_ATTACK: case OBJECTIVE_SET_LEADER: case OBJECTIVE_FOLLOW_ROUTE: case OBJECTIVE_SOLICIT_VEHICLE: case OBJECTIVE_HAIL_TAXI: case OBJECTIVE_CATCH_TRAIN: case OBJECTIVE_BUY_ICE_CREAM: case OBJECTIVE_STEAL_ANY_CAR: case OBJECTIVE_STEAL_ANY_MISSION_CAR: case OBJECTIVE_MUG_CHAR: case OBJECTIVE_LEAVE_CAR_AND_DIE: case OBJECTIVE_FLEE_CAR: case OBJECTIVE_SUN_BATHE: case OBJECTIVE_AIM_GUN_AT: case OBJECTIVE_WANDER: case OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER: case OBJECTIVE_KILL_CHAR_ON_BOAT: case OBJECTIVE_SOLICIT_FOOT: case OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP: break; case OBJECTIVE_GOTO_AREA_ANY_MEANS: case OBJECTIVE_GOTO_AREA_ON_FOOT: case OBJECTIVE_GOTO_SEAT_ON_FOOT: case OBJECTIVE_GOTO_ATM_ON_FOOT: case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: case OBJECTIVE_GOTO_PIZZA_ON_FOOT: case OBJECTIVE_GOTO_SHELTER_ON_FOOT: case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: bIsRunning = false; m_pNextPathNode = nil; m_nextRoutePointPos = dest; m_vecSeekPos = m_nextRoutePointPos; m_distanceToCountSeekDone = 0.5f; if (newObj == OBJECTIVE_GOTO_ATM_ON_FOOT) { m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); } if (newObj == OBJECTIVE_GOTO_SEAT_ON_FOOT) { m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); } if (newObj == OBJECTIVE_GOTO_BUS_STOP_ON_FOOT) { m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); } if (newObj == OBJECTIVE_GOTO_PIZZA_ON_FOOT) { m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); } if (newObj == OBJECTIVE_GOTO_SHELTER_ON_FOOT) { bIsRunning = true; m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); } if (newObj == OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT) { bIsRunning = true; m_distanceToCountSeekDone = m_attractor->GetDistanceToCountSeekDone(); m_acceptableHeadingOffset = m_attractor->GetAcceptableHeading(); } bUsePedNodeSeek = false; if (sq(m_distanceToCountSeekDone) > (m_nextRoutePointPos - GetPosition()).MagnitudeSqr2D()) { if (!IsUseAttractorObjective(m_objective)) return; if (Abs(m_fRotationCur - m_attractorHeading) < m_acceptableHeadingOffset) return; } break; case OBJECTIVE_RUN_TO_AREA: case OBJECTIVE_SPRINT_TO_AREA: bIsRunning = true; m_pNextPathNode = nil; m_nextRoutePointPos = dest; m_vecSeekPos = m_nextRoutePointPos; m_distanceToCountSeekDone = 0.5f; bUsePedNodeSeek = true; if (sq(m_distanceToCountSeekDone) > (m_nextRoutePointPos - GetPosition()).MagnitudeSqr2D()) return; break; default: break; } if (IsTemporaryObjective(m_objective)) { m_prevObjective = newObj; } else { if (m_objective != newObj) SetStoredObjective(); m_objective = newObj; } } void CPed::SetObjective(eObjective newObj, float heading, const CVector& pos) { switch (newObj) { case OBJECTIVE_GOTO_SEAT_ON_FOOT: case OBJECTIVE_GOTO_ATM_ON_FOOT: case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: case OBJECTIVE_GOTO_PIZZA_ON_FOOT: case OBJECTIVE_GOTO_SHELTER_ON_FOOT: case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: ClearPointGunAt(); SetObjective(newObj, pos); m_attractorHeading = heading; } } void CPed::ClearObjective(void) { if (IsPedInControl() || m_nPedState == PED_DRIVING) { m_objective = OBJECTIVE_NONE; m_pedInObjective = nil; m_carInObjective = nil; if (m_nPedState == PED_DRIVING && m_pMyVehicle) { if (m_pMyVehicle->pDriver != this) { if(!IsPlayer()) bWanderPathAfterExitingCar = true; SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); } m_nLastPedState = PED_NONE; } else { SetIdle(); SetMoveState(PEDMOVE_STILL); } } else { bClearObjective = true; } } void CPed::ClearLeader(void) { if (!m_leader) return; m_leader = nil; if (IsPedInControl()) { SetObjective(OBJECTIVE_NONE); if (CharCreatedBy == MISSION_CHAR) { SetIdle(); } else { SetWanderPath(CGeneral::GetRandomNumberInRange(0,8)); } } else if (m_objective != OBJECTIVE_NONE) { bClearObjective = true; } } void CPed::UpdateFromLeader(void) { if (CTimer::GetTimeInMilliseconds() <= m_objectiveTimer) return; if (!m_leader) return; CVector leaderDist; if (m_leader->InVehicle()) leaderDist = m_leader->m_pMyVehicle->GetPosition() - GetPosition(); else leaderDist = m_leader->GetPosition() - GetPosition(); if (leaderDist.Magnitude() > 30.0f) { if (bWaitForLeaderToComeCloser) { if (IsPedInControl()) { SetObjective(OBJECTIVE_NONE); SetIdle(); SetMoveState(PEDMOVE_STILL); } return; } bWaitForLeaderToComeCloser = true; } else bWaitForLeaderToComeCloser = false; if (IsPedInControl()) { if (m_nWaitState == WAITSTATE_PLAYANIM_TAXI) WarpPedToNearLeaderOffScreen(); if (m_leader->m_nPedState == PED_DEAD) { SetLeader(nil); SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); return; } if (!m_leader->bInVehicle) { if (m_leader->m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { if (bInVehicle) { if (m_objective != OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT && m_objective != OBJECTIVE_LEAVE_CAR) SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); return; } if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) { RestorePreviousObjective(); RestorePreviousState(); } } if (m_nPedType == PEDTYPE_PROSTITUTE && CharCreatedBy == RANDOM_CHAR) { SetLeader(nil); return; } } if (!bInVehicle && m_leader->bInVehicle && m_leader->m_nPedState == PED_DRIVING) { if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER) { if (m_leader->m_pMyVehicle->m_nNumPassengers < m_leader->m_pMyVehicle->m_nNumMaxPassengers) SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_leader->m_pMyVehicle); } } else if (m_leader->m_objective == OBJECTIVE_NONE || (m_leader->IsPlayer() && m_leader->m_objective == OBJECTIVE_WAIT_ON_FOOT) || m_objective == m_leader->m_objective) { if (m_leader->m_nPedState == PED_ATTACK && !bDontFight) { CEntity *lookTargetOfLeader = m_leader->m_pLookTarget; if (lookTargetOfLeader && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && lookTargetOfLeader->IsPed() && lookTargetOfLeader != this) { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, lookTargetOfLeader); SetObjectiveTimer(8000); SetLookFlag(m_leader->m_pLookTarget, false); SetLookTimer(500); } } else { if (IsPedInControl() && m_nPedState != PED_ATTACK) { if (m_leader->m_objective == OBJECTIVE_NONE && m_objective == OBJECTIVE_NONE && m_leader->m_nPedState == PED_CHAT && m_nPedState == PED_CHAT) { SetObjective(OBJECTIVE_NONE); } else { SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT, m_leader); SetObjectiveTimer(0); } } if (m_nPedState == PED_IDLE && m_leader->IsPlayer() && !bDontFight) { if (ScanForThreats() && m_threatEntity) { m_pLookTarget = m_threatEntity; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); TurnBody(); if (m_attackTimer < CTimer::GetTimeInMilliseconds() && !GetWeapon()->IsTypeMelee()) { SetWeaponLockOnTarget(m_threatEntity); SetAttack(m_threatEntity); } } } } } else { switch (m_leader->m_objective) { case OBJECTIVE_WAIT_ON_FOOT: case OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE: case OBJECTIVE_WAIT_IN_CAR: case OBJECTIVE_FOLLOW_ROUTE: SetObjective(m_leader->m_objective); m_objectiveTimer = m_leader->m_objectiveTimer; break; case OBJECTIVE_GUARD_SPOT: SetObjective(OBJECTIVE_GUARD_SPOT, m_leader->m_vecSpotToGuard); m_objectiveTimer = m_leader->m_objectiveTimer; break; case OBJECTIVE_KILL_CHAR_ON_FOOT: case OBJECTIVE_KILL_CHAR_ANY_MEANS: case OBJECTIVE_GOTO_CHAR_ON_FOOT: case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: case OBJECTIVE_HASSLE_CHAR: if (m_leader->m_pedInObjective) { SetObjective(m_leader->m_objective, m_leader->m_pedInObjective); m_objectiveTimer = m_leader->m_objectiveTimer; } break; case OBJECTIVE_ENTER_CAR_AS_PASSENGER: case OBJECTIVE_ENTER_CAR_AS_DRIVER: if (m_leader->m_carInObjective) { m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 150; SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_leader->m_carInObjective); return; } break; case OBJECTIVE_GUARD_ATTACK: return; case OBJECTIVE_HAIL_TAXI: m_leader = nil; SetObjective(OBJECTIVE_NONE); break; default: SetObjective(OBJECTIVE_GOTO_CHAR_ON_FOOT, m_leader); SetObjectiveTimer(0); break; } } } else if (bInVehicle) { if ((!m_leader->bInVehicle || m_leader->m_nPedState == PED_EXIT_CAR) && m_objective != OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT) { switch (m_leader->m_objective) { case OBJECTIVE_ENTER_CAR_AS_PASSENGER: case OBJECTIVE_ENTER_CAR_AS_DRIVER: if (m_pMyVehicle == m_leader->m_pMyVehicle || m_pMyVehicle == m_leader->m_carInObjective) break; // fall through default: if (m_pMyVehicle && m_objective != OBJECTIVE_LEAVE_CAR) { m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 250; SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); } break; } } } } void CPed::RestorePreviousObjective(void) { if (m_objective == OBJECTIVE_NONE) return; if (m_objective != OBJECTIVE_LEAVE_CAR && m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER && m_objective != OBJECTIVE_ENTER_CAR_AS_DRIVER && m_nPedState != PED_CARJACK) m_pedInObjective = nil; if (m_objective == OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT) { m_objective = OBJECTIVE_NONE; if (m_pMyVehicle) SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); } else { m_objective = m_prevObjective; m_prevObjective = OBJECTIVE_NONE; } bObjectiveCompleted = false; } void CPed::ProcessObjective(void) { if (bClearObjective && (IsPedInControl() || m_nPedState == PED_DRIVING)) { ClearObjective(); bClearObjective = false; } UpdateFromLeader(); CVector carOrOurPos; CVector targetCarOrHisPos; CVector distWithTarget; if (m_objective != OBJECTIVE_NONE && (IsPedInControl() || m_nPedState == PED_DRIVING)) { if (bInVehicle) { if (!m_pMyVehicle) { bInVehicle = false; return; } carOrOurPos = m_pMyVehicle->GetPosition(); } else { carOrOurPos = GetPosition(); } if (m_pedInObjective) { if (m_pedInObjective->InVehicle() && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { targetCarOrHisPos = m_pedInObjective->m_pMyVehicle->GetPosition(); } else { targetCarOrHisPos = m_pedInObjective->GetPosition(); } distWithTarget = targetCarOrHisPos - carOrOurPos; } else if (m_carInObjective) { targetCarOrHisPos = m_carInObjective->GetPosition(); distWithTarget = targetCarOrHisPos - carOrOurPos; } switch (m_objective) { case OBJECTIVE_WAIT_ON_FOOT: if (GetPedState() == PED_DRIVING) m_objective = OBJECTIVE_NONE; else { SetIdle(); if (m_attractor) { if (m_objectiveTimer && CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { GetPedAttractorManager()->BroadcastDeparture(this, m_attractor); m_objectiveTimer = 0; } } else { m_objective = OBJECTIVE_NONE; SetMoveState(PEDMOVE_STILL); } } break; case OBJECTIVE_WAIT_ON_FOOT_FOR_COP: if (!m_pedInObjective) { m_objective = OBJECTIVE_NONE; SetWanderPath(CGeneral::GetRandomNumberInRange(0.f, 8.f)); } else if (m_pedInObjective->m_nPedType == PEDTYPE_COP && m_pedInObjective->m_nPedState == PED_DEAD) { m_objective = OBJECTIVE_NONE; SetWanderPath(CGeneral::GetRandomNumberInRange(0.f, 8.f)); m_pedInObjective = nil; } break; case OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE: if (m_leaveCarTimer >= CTimer::GetTimeInMilliseconds()) break; if (InVehicle()) { SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); bFleeAfterExitingCar = true; } else if (m_nPedState != PED_FLEE_POS) { CVector2D fleePos = GetPosition(); SetFlee(fleePos, 10000); bUsePedNodeSeek = true; m_pNextPathNode = nil; } break; case OBJECTIVE_GUARD_SPOT: { distWithTarget = m_vecSpotToGuard - GetPosition(); if (m_pedInObjective) { SetLookFlag(m_pedInObjective, true); m_pLookTarget = m_pedInObjective; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); TurnBody(); } float distWithTargetSc = distWithTarget.Magnitude(); if (2.0f * m_radiusToGuard >= distWithTargetSc) { if (m_pedInObjective && m_pedInObjective->m_nPedState != PED_DEAD) { if (distWithTargetSc <= m_radiusToGuard) SetIdle(); else { CVector seekPos = m_vecSpotToGuard; SetSeek(seekPos, m_radiusToGuard); } } else if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { int threatType = ScanForThreats(); SetLookTimer(CGeneral::GetRandomNumberInRange(500, 1500)); // Second condition is pointless and isn't there in Mobile. if (threatType == PED_FLAG_GUN || (threatType == PED_FLAG_EXPLOSION && m_threatEntity) || m_threatEntity) { if (m_threatEntity->IsPed()) SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, m_threatEntity); } } } else { CVector seekPos = m_vecSpotToGuard; SetSeek(seekPos, m_radiusToGuard); } break; } case OBJECTIVE_WAIT_IN_CAR: SetPedState(PED_DRIVING); break; case OBJECTIVE_WAIT_IN_CAR_THEN_GET_OUT: SetPedState(PED_DRIVING); break; case OBJECTIVE_KILL_CHAR_ANY_MEANS: { if (m_pedInObjective) { if (m_pedInObjective->IsPlayer() && CharCreatedBy != MISSION_CHAR && m_nPedType != PEDTYPE_COP && FindPlayerPed()->m_pWanted->m_CurrentCops != 0 && !bKindaStayInSamePlace) { SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); break; } if (InVehicle()) { if (distWithTarget.Magnitude() >= 20.0f || m_pMyVehicle->m_vecMoveSpeed.MagnitudeSqr() >= sq(0.02f)) { if (((m_pMyVehicle->pDriver == this && m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_NONE) || m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE) && !m_pMyVehicle->m_nGettingInFlags) { m_pMyVehicle->SetStatus(STATUS_PHYSICS); m_pMyVehicle->AutoPilot.m_nPrevRouteNode = 0; if (m_nPedType == PEDTYPE_COP) { m_pMyVehicle->AutoPilot.m_nCruiseSpeed = (FindPlayerPed()->m_pWanted->GetWantedLevel() * 0.1f + 0.6f) * (GAME_SPEED_TO_CARAI_SPEED * m_pMyVehicle->pHandling->Transmission.fMaxCruiseVelocity); m_pMyVehicle->AutoPilot.m_nCarMission = CCarAI::FindPoliceCarMissionForWantedLevel(); } else { m_pMyVehicle->AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * m_pMyVehicle->pHandling->Transmission.fMaxCruiseVelocity * 0.8f; m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_FARAWAY; } m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; } } else { bool targetHasVeh = m_pedInObjective->bInVehicle; if (!targetHasVeh || targetHasVeh && m_pedInObjective->m_pMyVehicle->CanPedExitCar(false)) { m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE; SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); } } break; } if (distWithTarget.Magnitude() > 30.0f && !bKindaStayInSamePlace) { if (m_pMyVehicle) { m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); } else { float closestVehDist = 60.0f; int16 lastVehicle; CEntity* vehicles[8]; CWorld::FindObjectsInRange(GetPosition(), ENTER_CAR_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); CVehicle *foundVeh = nil; for(int i = 0; i < lastVehicle; i++) { CVehicle *nearVeh = (CVehicle*)vehicles[i]; /* Not used. CVector vehSpeed = nearVeh->GetSpeed(); CVector ourSpeed = GetSpeed(); */ CVector vehDistVec = nearVeh->GetPosition() - GetPosition(); if (vehDistVec.Magnitude() < closestVehDist && m_pedInObjective->m_pMyVehicle != nearVeh && nearVeh->CanPedOpenLocks(this) && nearVeh->m_fHealth > 250.f) { foundVeh = nearVeh; closestVehDist = vehDistVec.Magnitude(); } } m_pMyVehicle = foundVeh; if (m_pMyVehicle) { m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); } else if (!GetIsOnScreen()) { CVector ourPos = GetPosition(); int closestNode = ThePaths.FindNodeClosestToCoors(ourPos, PATH_CAR, 20.0f); if (closestNode >= 0) { int16 colliding; CWorld::FindObjectsKindaColliding( ThePaths.m_pathNodes[closestNode].GetPosition(), 10.0f, true, &colliding, 2, nil, false, true, true, false, false); if (!colliding) { CZoneInfo zoneInfo; int chosenCarClass; CTheZones::GetZoneInfoForTimeOfDay(&ourPos, &zoneInfo); int chosenModel = CCarCtrl::ChooseModel(&zoneInfo, &chosenCarClass); CVehicle *newVeh = nil; if (chosenModel != -1) { if (CModelInfo::IsBikeModel(chosenModel)) { newVeh = new CBike(chosenModel, RANDOM_VEHICLE); } else { newVeh = new CAutomobile(chosenModel, RANDOM_VEHICLE); } } if (newVeh) { newVeh->GetMatrix().GetPosition() = ThePaths.m_pathNodes[closestNode].GetPosition(); newVeh->GetMatrix().GetPosition().z += 4.0f; newVeh->SetHeading(DEGTORAD(200.0f)); newVeh->SetStatus(STATUS_ABANDONED); newVeh->m_nDoorLock = CARLOCK_UNLOCKED; CWorld::Add(newVeh); m_pMyVehicle = newVeh; if (m_pMyVehicle) { m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); } } } } } } break; } } else { ClearLookFlag(); bObjectiveCompleted = true; } } case OBJECTIVE_KILL_CHAR_ON_FOOT: { if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT && InVehicle()) { SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); break; } if (!m_pedInObjective || m_pedInObjective->DyingOrDead()) { bObjectiveCompleted = true; ClearLookFlag(); SetMoveAnim(); break; } if (m_pedInObjective) { int status; if (GetWeapon()->IsTypeMelee()) status = KillCharOnFootMelee(carOrOurPos, targetCarOrHisPos, distWithTarget); else status = KillCharOnFootArmed(carOrOurPos, targetCarOrHisPos, distWithTarget); if (status == WATCH_UNTIL_HE_DISAPPEARS) return; if (status == CANT_ATTACK) break; } SetMoveAnim(); break; } case OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE: case OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS: { if (InVehicle()) { if (m_nPedState == PED_DRIVING) SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); } else if (m_nPedState != PED_FLEE_ENTITY) { int time; if (m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS) time = 0; else time = 6000; SetFindPathAndFlee(m_pedInObjective, time); if (m_pedStats == CPedStats::ms_apPedStats[PEDSTAT_FIREMAN]) bMakeFleeScream = true; } break; } case OBJECTIVE_GOTO_CHAR_ON_FOOT: case OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING: case OBJECTIVE_HASSLE_CHAR: { if (m_pedInObjective) { float safeDistance = 2.0f; if (m_pedInObjective->bInVehicle) safeDistance = 3.0f; if (m_objective == OBJECTIVE_HASSLE_CHAR) safeDistance = 1.0f; float distWithTargetSc = distWithTarget.Magnitude(); if (m_nPedStateTimer < CTimer::GetTimeInMilliseconds()) { if (distWithTargetSc <= safeDistance) { bScriptObjectiveCompleted = true; if (m_nPedState != PED_ATTACK) { SetIdle(); if (m_pLookTarget) m_pLookTarget->CleanUpOldReference(&m_pLookTarget); m_pLookTarget = m_pedInObjective; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); TurnBody(); } if (distWithTargetSc > 2.0f) SetMoveState(m_pedInObjective->m_nMoveState); else SetMoveState(PEDMOVE_STILL); if (m_objective == OBJECTIVE_HASSLE_CHAR) { Say(SOUND_PED_COP_REACTION); m_pedInObjective->Say(SOUND_PED_UNK_126); m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 3000; m_pedInObjective->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 3000; SetObjective(OBJECTIVE_WANDER); m_pedInObjective->SetObjective(OBJECTIVE_WANDER); CVector2D dist = GetPosition() - m_pedInObjective->GetPosition(); m_nPathDir = CGeneral::GetNodeHeadingFromVector(dist.x, dist.y); m_pedInObjective->m_nPathDir = CGeneral::GetNodeHeadingFromVector(-dist.x, -dist.y); } } else { SetSeek(m_pedInObjective, safeDistance); if (distWithTargetSc >= 5.0f) { if (m_leader && m_leader->m_nMoveState == PEDMOVE_SPRINT) SetMoveState(PEDMOVE_SPRINT); else SetMoveState(PEDMOVE_RUN); } else { if (m_leader && m_leader->m_nMoveState != PEDMOVE_STILL && m_leader->m_nMoveState != PEDMOVE_NONE) { if (m_leader->IsPlayer()) { if (distWithTargetSc >= 3.0f && FindPlayerPed()->m_fMoveSpeed >= 1.3f) SetMoveState(PEDMOVE_RUN); else SetMoveState(PEDMOVE_WALK); } else { SetMoveState(m_leader->m_nMoveState); } } else if (distWithTargetSc <= 3.0f) { SetMoveState(PEDMOVE_WALK); } else { SetMoveState(PEDMOVE_RUN); } } } if (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT_WALKING && m_nMoveState > PEDMOVE_STILL) SetMoveState(PEDMOVE_WALK); } } else { SetObjective(OBJECTIVE_NONE); } break; } case OBJECTIVE_FOLLOW_CHAR_IN_FORMATION: { if (m_pedInObjective) { CVector posToGo = GetFormationPosition(); distWithTarget = posToGo - carOrOurPos; SetSeek(posToGo, 1.0f); if (distWithTarget.Magnitude() <= 3.0f) { SetSeek(posToGo, 1.0f); if (m_pedInObjective && m_pedInObjective->m_nMoveState != PEDMOVE_STILL) SetMoveState(m_pedInObjective->m_nMoveState); } else { SetSeek(posToGo, 1.0f); SetMoveState(PEDMOVE_RUN); } } else { SetObjective(OBJECTIVE_NONE); } break; } case OBJECTIVE_ENTER_CAR_AS_PASSENGER: { if (m_carInObjective) { if (!bInVehicle && m_carInObjective->m_nNumPassengers >= m_carInObjective->m_nNumMaxPassengers) { RestorePreviousObjective(); RestorePreviousState(); if (IsPedInControl()) m_pMyVehicle = nil; break; } if (m_prevObjective == OBJECTIVE_HAIL_TAXI && !((CAutomobile*)m_carInObjective)->bTaxiLight) { RestorePreviousObjective(); ClearObjective(); SetWanderPath(CGeneral::GetRandomNumber() & 7); bIsRunning = false; break; } if (m_objectiveTimer && m_objectiveTimer < CTimer::GetTimeInMilliseconds()) { if (!EnteringCar()) { bool foundSeat = false; if (m_carInObjective->IsBike()) { if (!m_carInObjective->pPassengers[0] && !(m_carInObjective->m_nGettingInFlags & (CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR))) { m_vehDoor = CAR_DOOR_RR; foundSeat = true; } } else { if (m_carInObjective->pPassengers[0] || m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_RF) { if (m_carInObjective->pPassengers[1] || m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_LR) { if (m_carInObjective->pPassengers[2] || m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_RR) { foundSeat = false; } else { m_vehDoor = CAR_DOOR_RR; foundSeat = true; } } else { m_vehDoor = CAR_DOOR_LR; foundSeat = true; } } else { m_vehDoor = CAR_DOOR_RF; foundSeat = true; } } for (int i = 2; i < m_carInObjective->m_nNumMaxPassengers; ++i) { if (!m_carInObjective->pPassengers[i] && !(m_carInObjective->m_nGettingInFlags & CAR_DOOR_FLAG_RF)) { m_vehDoor = CAR_DOOR_RF; foundSeat = true; } } if (foundSeat) { SetPosition(GetPositionToOpenCarDoor(m_carInObjective, m_vehDoor)); SetEnterCar(m_carInObjective, m_vehDoor); } } m_objectiveTimer = 0; } } // fall through } case OBJECTIVE_ENTER_CAR_AS_DRIVER: { if (!m_carInObjective || bInVehicle) { if (bInVehicle && m_pMyVehicle != m_carInObjective) { SetExitCar(m_pMyVehicle, 0); } else { bObjectiveCompleted = true; bScriptObjectiveCompleted = true; RestorePreviousState(); } } else { if (m_leaveCarTimer > CTimer::GetTimeInMilliseconds()) { SetMoveState(PEDMOVE_STILL); break; } if (IsPedInControl()) { if (m_prevObjective == OBJECTIVE_KILL_CHAR_ANY_MEANS) { if (distWithTarget.Magnitude() < 20.0f) { RestorePreviousObjective(); RestorePreviousState(); return; } if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { if (m_carInObjective->pDriver && !IsPlayer()) { if (m_carInObjective->pDriver->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS && m_carInObjective->pDriver != m_pedInObjective) { SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_carInObjective); m_carInObjective->bIsBeingCarJacked = false; } } } } else if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) { if (m_carInObjective->pDriver && (CharCreatedBy != MISSION_CHAR || m_carInObjective->pDriver->CharCreatedBy != RANDOM_CHAR) ) { if (m_carInObjective->pDriver->m_nPedType == m_nPedType) { SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_carInObjective); m_carInObjective->bIsBeingCarJacked = false; } } } if (m_carInObjective->IsUpsideDown() && m_carInObjective->m_vehType != VEHICLE_TYPE_BIKE) { RestorePreviousObjective(); RestorePreviousState(); return; } if (!m_carInObjective->IsBoat() || m_nPedState == PED_SEEK_IN_BOAT) { if (m_nPedState != PED_SEEK_CAR) SetSeekCar(m_carInObjective, 0); } else { SetSeekBoatPosition(m_carInObjective); } if (m_nMoveState == PEDMOVE_STILL && !bVehEnterDoorIsBlocked) SetMoveState(PEDMOVE_RUN); if (m_carInObjective && m_carInObjective->m_fHealth > 0.0f) { distWithTarget = m_carInObjective->GetPosition() - GetPosition(); if (!bInVehicle) { if (nEnterCarRangeMultiplier * ENTER_CAR_MAX_DIST < distWithTarget.Magnitude()) { if (!m_carInObjective->pDriver && !m_carInObjective->GetIsOnScreen() && !GetIsOnScreen()) WarpPedToNearEntityOffScreen(m_carInObjective); if (CharCreatedBy != MISSION_CHAR || m_prevObjective == OBJECTIVE_KILL_CHAR_ANY_MEANS || IsPlayer() && !CPad::GetPad(0)->ArePlayerControlsDisabled()) { RestorePreviousObjective(); RestorePreviousState(); if (IsPedInControl()) m_pMyVehicle = nil; } else { SetIdle(); SetMoveState(PEDMOVE_STILL); } } } } else if (!bInVehicle) { RestorePreviousObjective(); RestorePreviousState(); if (IsPedInControl()) m_pMyVehicle = nil; } } } break; } case OBJECTIVE_DESTROY_OBJECT: if (m_pPointGunAt) { if (m_nPedState != PED_ATTACK) SetAttack(m_pPointGunAt); } else { bScriptObjectiveCompleted = true; RestorePreviousObjective(); } break; case OBJECTIVE_DESTROY_CAR: { if (!m_carInObjective) { ClearLookFlag(); bObjectiveCompleted = true; break; } float distWithTargetSc = distWithTarget.Magnitude(); CWeaponInfo *wepInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); float wepRange = wepInfo->m_fRange; m_pLookTarget = m_carInObjective; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); m_pSeekTarget = m_carInObjective; m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); TurnBody(); if (m_carInObjective->m_fHealth <= 0.0f) { ClearLookFlag(); bScriptObjectiveCompleted = true; break; } if (m_attackTimer < CTimer::GetTimeInMilliseconds() && distWithTargetSc < wepRange) { // I hope so CVector ourHead = GetMatrix() * CVector(0.5f, 0.0f, 0.6f); CVector maxShotPos = m_carInObjective->GetPosition() - ourHead; maxShotPos *= wepInfo->m_fRange / maxShotPos.Magnitude(); maxShotPos += ourHead; CColPoint foundCol; CEntity *foundEnt; CWorld::bIncludeDeadPeds = true; CWorld::ProcessLineOfSight(ourHead, maxShotPos, foundCol, foundEnt, true, true, true, true, false, true, false); CWorld::bIncludeDeadPeds = false; if (foundEnt == m_carInObjective) { SetAttack(m_carInObjective); SetWeaponLockOnTarget(m_carInObjective); SetShootTimer(CGeneral::GetRandomNumberInRange(500, 2000)); if (distWithTargetSc > 10.0f && !bKindaStayInSamePlace) { SetAttackTimer(CGeneral::GetRandomNumberInRange(2000, 5000)); } else { SetAttackTimer(CGeneral::GetRandomNumberInRange(50, 300)); SetMoveState(PEDMOVE_STILL); } } } else if (m_nPedState != PED_ATTACK && !bKindaStayInSamePlace) { if (wepRange <= 5.0f) { if (Abs(distWithTarget.x) > wepRange || Abs(distWithTarget.y) > wepRange || (distWithTarget.z > -1.0f && distWithTarget.z < 0.3)) { SetSeek(m_carInObjective, 3.0f); SetMoveState(PEDMOVE_RUN); } else { SetIdle(); } } else { float safeDistance = wepRange * 0.25f; SetSeek(m_carInObjective, safeDistance); SetMoveState(PEDMOVE_RUN); } } SetLookFlag(m_carInObjective, false); TurnBody(); break; } case OBJECTIVE_GOTO_AREA_ON_FOOT: case OBJECTIVE_RUN_TO_AREA: case OBJECTIVE_SPRINT_TO_AREA: { if (InVehicle()) { SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); } else { distWithTarget = m_nextRoutePointPos - GetPosition(); distWithTarget.z = 0.0f; if (sq(m_distanceToCountSeekDone) >= distWithTarget.MagnitudeSqr()) { bObjectiveCompleted = true; bScriptObjectiveCompleted = true; SetMoveState(PEDMOVE_STILL); } else if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer || m_nPedState != PED_SEEK_POS) { if (bUsePedNodeSeek) { CVector bestCoords(0.0f, 0.0f, 0.0f); m_vecSeekPos = m_nextRoutePointPos; if (!m_pNextPathNode) { bool found = FindBestCoordsFromNodes(m_vecSeekPos, &bestCoords); if (m_pNextPathNode) { // Because it already does that if it finds better coords. if (!found) { bestCoords = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); } if ((bestCoords - GetPosition()).Magnitude2D() < m_distanceToCountSeekDone) { m_pNextPathNode = nil; bUsePedNodeSeek = false; } } } if (m_pNextPathNode) m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); } CVector seekPos = m_vecSeekPos; SetSeek(m_vecSeekPos, m_distanceToCountSeekDone); } } break; } case OBJECTIVE_GUARD_ATTACK: { if (m_pedInObjective) { SetLookFlag(m_pedInObjective, true); m_pLookTarget = m_pedInObjective; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); m_lookTimer = m_attackTimer; TurnBody(); float distWithTargetSc = distWithTarget.Magnitude(); if (distWithTargetSc >= 20.0f) { RestorePreviousObjective(); } else if (m_attackTimer < CTimer::GetTimeInMilliseconds()) { if (m_nPedState != PED_SEEK_ENTITY && distWithTargetSc >= 2.0f) { SetSeek(m_pedInObjective, 1.0f); } else { SetAttack(m_pedInObjective); SetShootTimer(CGeneral::GetRandomNumberInRange(500.0f, 1500.0f)); } SetAttackTimer(1000); } } else { RestorePreviousObjective(); } break; } case OBJECTIVE_FOLLOW_ROUTE: if (HaveReachedNextPointOnRoute(1.0f)) { int nextPoint = GetNextPointOnRoute(); m_nextRoutePointPos = CRouteNode::GetPointPosition(nextPoint); } else { CVector seekPos = m_nextRoutePointPos; SetSeek(seekPos, 0.8f); } break; case OBJECTIVE_SOLICIT_VEHICLE: if (m_carInObjective) { if (m_objectiveTimer <= CTimer::GetTimeInMilliseconds()) { if (!bInVehicle) { SetObjective(OBJECTIVE_NONE); SetWanderPath(CGeneral::GetRandomNumber() & 7); m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000; if (IsPedInControl()) m_pMyVehicle = nil; } } else { if (m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_SOLICIT) SetSeekCar(m_carInObjective, 0); } } else { RestorePreviousObjective(); RestorePreviousState(); if (IsPedInControl()) m_pMyVehicle = nil; } break; case OBJECTIVE_HAIL_TAXI: if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HAILTAXI) && CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { Say(SOUND_PED_TAXI_WAIT); CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HAILTAXI, 4.0f); m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; } break; case OBJECTIVE_CATCH_TRAIN: { if (m_carInObjective) { SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, m_carInObjective); } else { CVehicle* trainToEnter = nil; float closestCarDist = CHECK_NEARBY_THINGS_MAX_DIST; CVector pos = GetPosition(); int16 lastVehicle; CEntity* vehicles[8]; CWorld::FindObjectsInRange(pos, 10.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for (int i = 0; i < lastVehicle; i++) { CVehicle* nearVeh = (CVehicle*)vehicles[i]; if (nearVeh->IsTrain()) { CVector vehDistVec = GetPosition() - nearVeh->GetPosition(); float vehDist = vehDistVec.Magnitude(); if (vehDist < closestCarDist && m_pedInObjective->m_pMyVehicle != nearVeh) { trainToEnter = nearVeh; closestCarDist = vehDist; } } } if (trainToEnter) { m_carInObjective = trainToEnter; m_carInObjective->RegisterReference((CEntity **) &m_carInObjective); } } break; } case OBJECTIVE_BUY_ICE_CREAM: if (m_carInObjective) { if (m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_BUY_ICECREAM && m_nPedState != PED_CHAT) SetSeekCar(m_carInObjective, 0); } break; case OBJECTIVE_STEAL_ANY_CAR: case OBJECTIVE_STEAL_ANY_MISSION_CAR: { if (bInVehicle) { bScriptObjectiveCompleted = true; RestorePreviousObjective(); } else if (m_carJackTimer < CTimer::GetTimeInMilliseconds()) { CVehicle *carToSteal = nil; float closestCarDist = nEnterCarRangeMultiplier * ENTER_CAR_MAX_DIST; CVector pos = GetPosition(); int16 lastVehicle; CEntity *vehicles[8]; CWorld::FindObjectsInRange(pos, closestCarDist, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for(int i = 0; i < lastVehicle; i++) { CVehicle *nearVeh = (CVehicle*)vehicles[i]; if (m_objective == OBJECTIVE_STEAL_ANY_MISSION_CAR || nearVeh->VehicleCreatedBy != MISSION_VEHICLE) { if (nearVeh->m_vecMoveSpeed.Magnitude() <= 0.1f) { if (nearVeh->CanPedOpenLocks(this)) { CVector vehDistVec = GetPosition() - nearVeh->GetPosition(); float vehDist = vehDistVec.Magnitude(); if (vehDist < closestCarDist) { carToSteal = nearVeh; closestCarDist = vehDist; } } } } } if (carToSteal) { SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, carToSteal); m_carJackTimer = CTimer::GetTimeInMilliseconds() + 5000; } else { RestorePreviousObjective(); RestorePreviousState(); } } break; } case OBJECTIVE_MUG_CHAR: { if (m_pedInObjective) { if (m_pedInObjective->IsPlayer() || m_pedInObjective->bInVehicle || m_pedInObjective->m_fHealth <= 0.0f) { ClearObjective(); return; } if (m_pedInObjective->m_nMoveState > PEDMOVE_WALK) { ClearObjective(); return; } if (m_pedInObjective->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT && m_pedInObjective->m_pedInObjective == this) { SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, m_pedInObjective); SetMoveState(PEDMOVE_SPRINT); return; } if (m_pedInObjective->m_nPedState == PED_FLEE_ENTITY && m_fleeFrom == this || m_pedInObjective->m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE && m_pedInObjective->m_pedInObjective == this) { ClearObjective(); SetFindPathAndFlee(m_pedInObjective, 15000, true); return; } float distWithTargetScSqr = distWithTarget.MagnitudeSqr(); if (distWithTargetScSqr <= sq(10.0f)) { if (distWithTargetScSqr <= sq(1.4f)) { CAnimBlendAssociation *reloadAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_PARTIAL_FUCKU); m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( m_pedInObjective->GetPosition().x, m_pedInObjective->GetPosition().y, GetPosition().x, GetPosition().y); if (reloadAssoc || !m_pedInObjective->IsPedShootable()) { if (reloadAssoc && (!reloadAssoc->IsRunning() || reloadAssoc->GetProgress() > 0.8f)) { CAnimBlendAssociation *punchAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PARTIAL_PUNCH, 8.0f); punchAssoc->flags |= ASSOC_DELETEFADEDOUT; punchAssoc->flags |= ASSOC_FADEOUTWHENDONE; CVector2D offset(distWithTarget.x, distWithTarget.y); int dir = m_pedInObjective->GetLocalDirection(offset); m_pedInObjective->StartFightDefend(dir, HITLEVEL_HIGH, 5); m_pedInObjective->ReactToAttack(this); m_pedInObjective->Say(SOUND_PED_ROBBED); Say(SOUND_PED_MUGGING); bRichFromMugging = true; // FIX: ClearObjective() clears m_pedInObjective, so get it before call CPed *victim = m_pedInObjective; ClearObjective(); if (victim->m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT || victim->m_pedInObjective != this) { SetFindPathAndFlee(victim, 15000, true); m_nLastPedState = PED_WANDER_PATH; } else { SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, victim); SetMoveState(PEDMOVE_SPRINT); m_nLastPedState = PED_WANDER_PATH; } } } else { eWeaponType weaponType = GetWeapon()->m_eWeaponType; if (weaponType != WEAPONTYPE_UNARMED && weaponType != WEAPONTYPE_BASEBALLBAT) SetCurrentWeapon(WEAPONTYPE_UNARMED); CAnimBlendAssociation *newReloadAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PARTIAL_FUCKU, 8.0f); newReloadAssoc->flags |= ASSOC_DELETEFADEDOUT; newReloadAssoc->flags |= ASSOC_FADEOUTWHENDONE; } } else { SetSeek(m_pedInObjective, 1.0f); CAnimBlendAssociation *walkAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_WALK); if (walkAssoc) walkAssoc->speed = 1.3f; } } else { ClearObjective(); SetWanderPath(CGeneral::GetRandomNumber() & 7); } } else { m_objective = OBJECTIVE_NONE; ClearObjective(); } } // fall through case OBJECTIVE_WANDER: if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer && !bInVehicle) { m_leaveCarTimer = 0; m_objective = OBJECTIVE_NONE; SetWanderPath(m_nPathDir); } break; case OBJECTIVE_LEAVE_CAR_AND_DIE: { if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer) { if (InVehicle()) { if (m_nPedState != PED_EXIT_CAR && m_nPedState != PED_DRAG_FROM_CAR && m_nPedState != PED_EXIT_TRAIN) { if (m_pMyVehicle->IsBoat()) SetExitBoat(m_pMyVehicle); else if (m_pMyVehicle->bIsBus) SetExitCar(m_pMyVehicle, 0); else { eCarNodes doorNode = CAR_DOOR_LF; if (m_pMyVehicle->pDriver != this) { if (m_pMyVehicle->pPassengers[0] == this) { doorNode = CAR_DOOR_RF; } else if (m_pMyVehicle->pPassengers[1] == this) { doorNode = CAR_DOOR_LR; } else if (m_pMyVehicle->pPassengers[2] == this) { doorNode = CAR_DOOR_RR; } } SetBeingDraggedFromCar(m_pMyVehicle, doorNode, false); } } } } break; } case OBJECTIVE_GOTO_AREA_ANY_MEANS: { distWithTarget = m_nextRoutePointPos - GetPosition(); distWithTarget.z = 0.0f; if (InVehicle()) { CCarAI::GetCarToGoToCoors(m_pMyVehicle, &m_nextRoutePointPos); CCarCtrl::RegisterVehicleOfInterest(m_pMyVehicle); if (distWithTarget.MagnitudeSqr() < sq(20.0f)) { m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; ForceStoredObjective(OBJECTIVE_GOTO_AREA_ANY_MEANS); SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); } break; } if (distWithTarget.Magnitude() > 30.0f) { if (m_pMyVehicle) { m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; } else { float closestVehDist = SQR(60.0f); int16 lastVehicle; CEntity* vehicles[8]; // NB: 25.0f in here is prolly a forgotten setting, all other places now use 30.0f (ENTER_CAR_MAX_DIST) CWorld::FindObjectsInRange(GetPosition(), 25.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); CVehicle* foundVeh = nil; for (int i = 0; i < lastVehicle; i++) { CVehicle* nearVeh = (CVehicle*)vehicles[i]; /* Not used. CVector vehSpeed = nearVeh->GetSpeed(); CVector ourSpeed = GetSpeed(); */ CVector vehDistVec = nearVeh->GetPosition() - GetPosition(); if (vehDistVec.MagnitudeSqr() < closestVehDist && m_pedInObjective->m_pMyVehicle != nearVeh) { foundVeh = nearVeh; closestVehDist = vehDistVec.MagnitudeSqr(); } } m_pMyVehicle = foundVeh; if (m_pMyVehicle) { m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle); } } break; } // Falls to different objectives in III and VC #ifdef FIX_BUGS break; #else // fall through #endif } case OBJECTIVE_GOTO_SEAT_ON_FOOT: case OBJECTIVE_GOTO_ATM_ON_FOOT: case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: case OBJECTIVE_GOTO_PIZZA_ON_FOOT: case OBJECTIVE_GOTO_SHELTER_ON_FOOT: case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: if (CTimer::GetTimeInMilliseconds() > m_objectiveTimer) { m_objectiveTimer = 0; if (m_attractor) GetPedAttractorManager()->DeRegisterPed(this, m_attractor); } else { CVector distance = m_nextRoutePointPos - GetPosition(); distance.z = 0.0f; if (m_objective == OBJECTIVE_GOTO_SHELTER_ON_FOOT) { if (m_nMoveState == PEDMOVE_RUN && distance.MagnitudeSqr() < SQR(2.0f)) { SetMoveState(PEDMOVE_WALK); bIsRunning = false; } if (CWeather::Rain < 0.2f && m_attractor) { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); return; } } else if (m_objective == OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT) { if (m_nMoveState == PEDMOVE_RUN && distance.MagnitudeSqr() < SQR(4.0f)) { SetMoveState(PEDMOVE_WALK); bIsRunning = false; } CVehicle* pIceCreamVan = GetPedAttractorManager()->GetIceCreamVanForEffect(m_attractor->GetEffect()); if (0.01f * CTimer::GetTimeStep() * 5.0f < pIceCreamVan->m_fDistanceTravelled) { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); return; } if (!pIceCreamVan->pDriver || !pIceCreamVan->pDriver->IsPlayer() || pIceCreamVan->pDriver->GetPedState() == PED_ARRESTED || pIceCreamVan->pDriver->GetPedState() == PED_DEAD) { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); return; } if (!pIceCreamVan->m_bSirenOrAlarm) { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); return; } if (pIceCreamVan->GetStatus() == STATUS_WRECKED) { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); return; } } if (sq(m_distanceToCountSeekDone) < distance.MagnitudeSqr()) { if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer || GetPedState() != PED_SEEK_POS) { m_vecSeekPos = m_nextRoutePointPos; SetSeek(m_vecSeekPos, m_distanceToCountSeekDone); } } else { if (!bReachedAttractorHeadingTarget) { float fHeadingDistance = m_fRotationCur - m_attractorHeading; float fSinHeading = Sin(fHeadingDistance); float fCosHeading = Cos(fHeadingDistance); if (fSinHeading > 0.0f) { if (fCosHeading > 0.0f) m_attractorHeading = m_fRotationCur - Asin(fSinHeading); else m_attractorHeading = m_fRotationCur - Acos(fCosHeading); } else { if (fCosHeading > 0.0f) m_attractorHeading = m_fRotationCur - Asin(fSinHeading); else m_attractorHeading = m_fRotationCur + Acos(fCosHeading); } m_fRotationDest = m_attractorHeading; m_headingRate = 3.5f; bReachedAttractorHeadingTarget = true; bTurnedAroundOnAttractor = false; } if (Abs(m_fRotationCur - m_attractorHeading) >= m_acceptableHeadingOffset && Abs(m_fRotationCur - m_attractorHeading + TWOPI) >= m_acceptableHeadingOffset && Abs(m_fRotationCur - m_attractorHeading - TWOPI) >= m_acceptableHeadingOffset) SetMoveState(PEDMOVE_STILL); else { m_fRotationDest = m_fRotationCur; bReachedAttractorHeadingTarget = false; bObjectiveCompleted = true; bScriptObjectiveCompleted = true; RestoreHeadingRate(); GetPedAttractorManager()->BroadcastArrival(this, m_attractor); if (GetPedAttractorManager()->IsAtHeadOfQueue(this, m_attractor)) { switch (m_objective) { case OBJECTIVE_GOTO_SEAT_ON_FOOT: if (!bTurnedAroundOnAttractor) { ClearObjective(); SetWaitState(WAITSTATE_SIT_DOWN, 0); } else { ClearObjective(); SetWaitState(WAITSTATE_SIT_DOWN_RVRS, 0); } break; case OBJECTIVE_GOTO_ATM_ON_FOOT: ClearObjective(); SetWaitState(WAITSTATE_USE_ATM, 0); break; case OBJECTIVE_GOTO_BUS_STOP_ON_FOOT: ClearObjective(); SetObjective(OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP); break; case OBJECTIVE_GOTO_PIZZA_ON_FOOT: ClearObjective(); m_prevObjective = OBJECTIVE_NONE; SetObjective(OBJECTIVE_WAIT_ON_FOOT); m_objectiveTimer = CTimer::GetTimeInMilliseconds() + m_attractor->GetHeadOfQueueWaitTime(); break; case OBJECTIVE_GOTO_SHELTER_ON_FOOT: m_prevObjective = OBJECTIVE_NONE; SetObjective(OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER); break; case OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT: m_prevObjective = OBJECTIVE_NONE; SetObjective(OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN); break; } } else { m_prevObjective = OBJECTIVE_NONE; SetObjective(OBJECTIVE_WAIT_ON_FOOT); m_objectiveTimer = 0; } } } } return; case OBJECTIVE_FLEE_CAR: if (!bInVehicle && m_nPedState != PED_FLEE_ENTITY && m_pMyVehicle) { RestorePreviousObjective(); SetFlee(m_pMyVehicle, 6000); break; } // fall through case OBJECTIVE_LEAVE_CAR: if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer) { if (InVehicle() && (FindPlayerPed() != this || !CPad::GetPad(0)->GetAccelerate() || bBusJacked)) { if (m_nPedState != PED_EXIT_CAR && m_nPedState != PED_DRAG_FROM_CAR && m_nPedState != PED_EXIT_TRAIN && (m_nPedType != PEDTYPE_COP || m_pMyVehicle->IsBoat() || m_pMyVehicle->m_vecMoveSpeed.MagnitudeSqr2D() < sq(0.005f))) { #ifdef GTA_TRAIN if (m_pMyVehicle->IsTrain()) SetExitTrain(m_pMyVehicle); else #endif if (m_pMyVehicle->IsBoat()) SetExitBoat(m_pMyVehicle); else SetExitCar(m_pMyVehicle, 0); } } else { RestorePreviousObjective(); } } if (bHeldHostageInCar) { if (CTheScripts::IsPlayerOnAMission()) { CVehicle *playerVeh = FindPlayerVehicle(); if (playerVeh && playerVeh->IsPassenger(this)) { if (m_leaveCarTimer != 0) m_leaveCarTimer = 0; } } } break; case OBJECTIVE_AIM_GUN_AT: if (m_pedInObjective) { if (!bObstacleShowedUpDuringKillObjective) SetPointGunAt(m_pedInObjective); if (m_nMoveState == PEDMOVE_STILL && IsPedInControl()) { SetLookFlag(m_pedInObjective, false); TurnBody(); } } else { ClearObjective(); } break; case OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER: SetIdle(); if (m_attractor && CWeather::Rain < 0.2f) GetPedAttractorManager()->DeRegisterPed(this, m_attractor); break; case OBJECTIVE_KILL_CHAR_ON_BOAT: SetPedStats(PEDSTAT_TOUGH_GUY); if (bInVehicle) { if (m_pedInObjective && m_pedInObjective->m_pCurrentPhysSurface != m_pMyVehicle) { bObjectiveCompleted = true; ClearObjective(); CCarCtrl::SwitchVehicleToRealPhysics(m_pMyVehicle); m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; m_pMyVehicle->AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * m_pMyVehicle->pHandling->Transmission.fMaxCruiseVelocity; m_pMyVehicle->SetStatus(STATUS_PHYSICS); } else { SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle); } } else if (m_pedInObjective && !m_pedInObjective->DyingOrDead() && (!m_pCurrentPhysSurface || !m_pedInObjective->m_pCurrentPhysSurface || m_pedInObjective->m_pCurrentPhysSurface == m_pCurrentPhysSurface)) { CBoat *boatWeAreOn = nil; if (m_pCurrentPhysSurface && m_pCurrentPhysSurface->IsVehicle() && ((CVehicle*)m_pCurrentPhysSurface)->IsBoat()) boatWeAreOn = (CBoat*)m_pCurrentPhysSurface; if (boatWeAreOn) { SetObjective(OBJECTIVE_RUN_TO_AREA, boatWeAreOn->GetPosition() - (boatWeAreOn->GetForward() * 10.f)); } else if (m_pedInObjective) { SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_ALWAYS, m_pedInObjective); } SetMoveAnim(); } else { ClearLookFlag(); SetMoveAnim(); if (m_pCurrentPhysSurface && m_pCurrentPhysSurface->IsVehicle()) SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pCurrentPhysSurface); } break; case OBJECTIVE_SOLICIT_FOOT: if (m_pedInObjective) { if (m_objectiveTimer < CTimer::GetTimeInMilliseconds()) bScriptObjectiveCompleted = true; } else { bScriptObjectiveCompleted = true; } break; case OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP: SetIdle(); if (m_attractor) { float left = GetPosition().x - 10.0f; float right = GetPosition().x + 10.0f; float top = GetPosition().y - 10.0f; float bottom = GetPosition().y + 10.0f; int xstart = Max(0, CWorld::GetSectorIndexX(left)); int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(right)); int ystart = Max(0, CWorld::GetSectorIndexY(top)); int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(bottom)); assert(xstart <= xend); assert(ystart <= yend); float minDistance = SQR(10.0f); CVehicle* pBus = nil; for (int y = ystart; y <= yend; y++) { for (int x = xstart; x <= xend; x++) { CSector* s = CWorld::GetSector(x, y); for (CPtrNode* pNode = s->m_lists[ENTITYLIST_VEHICLES].first; pNode != nil; pNode = pNode->next) { CEntity* pEntity = (CEntity*)pNode->item; if (!pEntity->IsVehicle()) continue; CVehicle* pVehicle = (CVehicle*)pEntity; if (!pVehicle->bIsBus) continue; if (pVehicle->GetMoveSpeed().MagnitudeSqr() >= SQR(0.005f)) continue; float distanceSq = (GetPosition() - pVehicle->GetPosition()).MagnitudeSqr(); if (distanceSq < minDistance) { minDistance = distanceSq; pBus = pVehicle; } } } } if (pBus) { if (pBus->m_nNumPassengers >= pBus->m_nNumMaxPassengers - 1) SetObjective(OBJECTIVE_WAIT_ON_FOOT); else { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, pBus); bDontDragMeOutCar = true; bRemoveMeWhenIGotIntoCar = true; CPlayerPed *player = FindPlayerPed(); if (pBus->IsPassenger(player) || pBus->IsDriver(player)) { bCollectBusFare = true; } } } } break; case OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN: { SetIdle(); CVehicle* pIceCreamVan = GetPedAttractorManager()->GetIceCreamVanForEffect(m_attractor->GetEffect()); if (m_attractor && m_nWaitState != WAITSTATE_PLAYANIM_CHAT && pIceCreamVan && pIceCreamVan->pDriver && pIceCreamVan->pDriver->IsPlayer()) { int time = 5000; SetWaitState(WAITSTATE_PLAYANIM_CHAT, &time); break; } if (!m_attractor) break; CVehicle* pVan = GetPedAttractorManager()->GetIceCreamVanForEffect(m_attractor->GetEffect()); if (!pVan) break; if (0.01f * CTimer::GetTimeStep() * 5.0f < pVan->m_fDistanceTravelled) { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); break; } if (!pVan->pDriver || !pVan->pDriver->IsPlayer() || pVan->pDriver->GetPedState() == PED_ARRESTED || pVan->pDriver->GetPedState() == PED_DEAD) { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); break; } if (!pVan->m_bSirenOrAlarm) { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); return; // Why? } if (pVan->GetStatus() == STATUS_WRECKED) { GetPedAttractorManager()->DeRegisterPed(this, m_attractor); return; // Why? } break; } } if (bObjectiveCompleted || m_objectiveTimer > 0 && CTimer::GetTimeInMilliseconds() > m_objectiveTimer) { RestorePreviousObjective(); if (m_objectiveTimer > CTimer::GetTimeInMilliseconds() || !m_objectiveTimer) m_objectiveTimer = CTimer::GetTimeInMilliseconds() - 1; if (CharCreatedBy != RANDOM_CHAR || bInVehicle) { if (IsPedInControl()) RestorePreviousState(); } else { SetWanderPath(CGeneral::GetRandomNumber() & 7); } ClearAimFlag(); ClearLookFlag(); } } } void CPed::SetFollowRoute(int16 currentPoint, int16 routeType) { m_routeLastPoint = currentPoint; m_routeType = routeType; m_routePointsBeingPassed = 1; m_routePointsPassed = 0; m_objective = OBJECTIVE_FOLLOW_ROUTE; m_routeStartPoint = CRouteNode::GetRouteStart(currentPoint); m_nextRoutePointPos = CRouteNode::GetPointPosition(GetNextPointOnRoute()); } int CPed::GetNextPointOnRoute(void) { int16 nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint; // Route is complete if (nextPoint < 0 || nextPoint > NUMPEDROUTES || m_routeLastPoint != CRouteNode::GetRouteThisPointIsOn(nextPoint)) { switch (m_routeType) { case PEDROUTE_STOP_WHEN_DONE: nextPoint = -1; break; case PEDROUTE_GO_BACKWARD_WHEN_DONE: m_routePointsBeingPassed = -m_routePointsBeingPassed; nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint; break; case PEDROUTE_GO_TO_START_WHEN_DONE: m_routePointsPassed = -1; nextPoint = m_routePointsBeingPassed + m_routePointsPassed + m_routeStartPoint; break; default: break; } } return nextPoint; } bool CPed::HaveReachedNextPointOnRoute(float distToCountReached) { if ((m_nextRoutePointPos - GetPosition()).Magnitude2D() < distToCountReached) { m_routePointsPassed += m_routePointsBeingPassed; return true; } return false; } bool CPed::CanSeeEntity(CEntity *entity, float threshold) { float neededAngle = CGeneral::GetRadianAngleBetweenPoints( entity->GetPosition().x, entity->GetPosition().y, GetPosition().x, GetPosition().y); if (neededAngle < 0.0f) neededAngle += TWOPI; else if (neededAngle > TWOPI) neededAngle -= TWOPI; float ourAngle = m_fRotationCur; if (ourAngle < 0.0f) ourAngle += TWOPI; else if (ourAngle > TWOPI) ourAngle -= TWOPI; float neededTurn = Abs(neededAngle - ourAngle); return neededTurn < threshold || TWOPI - threshold < neededTurn; } // Only used while deciding which gun ped should switch to, if no ammo left. bool CPed::SelectGunIfArmed(void) { for (int i = 0; i < TOTAL_WEAPON_SLOTS; i++) { if (GetWeapon(i).m_nAmmoTotal > 0) { eWeaponType weaponType = GetWeapon(i).m_eWeaponType; if (weaponType == WEAPONTYPE_COLT45 || weaponType == WEAPONTYPE_UZI || weaponType == WEAPONTYPE_MP5 || weaponType == WEAPONTYPE_M4 || weaponType == WEAPONTYPE_COLT45 || weaponType == WEAPONTYPE_PYTHON || weaponType == WEAPONTYPE_SHOTGUN || weaponType == WEAPONTYPE_SPAS12_SHOTGUN || weaponType == WEAPONTYPE_STUBBY_SHOTGUN || weaponType == WEAPONTYPE_ROCKETLAUNCHER || weaponType == WEAPONTYPE_SNIPERRIFLE || weaponType == WEAPONTYPE_FLAMETHROWER) { SetCurrentWeapon(i); return true; } } } SetCurrentWeapon(WEAPONTYPE_UNARMED); return false; } void CPed::ReactToPointGun(CEntity *entWithGun) { CPed *pedWithGun = (CPed*)entWithGun; int waitTime; if (IsPlayer() || !IsPedInControl() || (CharCreatedBy == MISSION_CHAR && !bCrouchWhenScared)) return; if (m_leader == pedWithGun) return; if (m_nWaitState == WAITSTATE_PLAYANIM_HANDSUP || m_nWaitState == WAITSTATE_PLAYANIM_HANDSCOWER || (GetPosition() - pedWithGun->GetPosition()).MagnitudeSqr2D() > 225.0f || (m_nPedType == PEDTYPE_GANG7 && pedWithGun == FindPlayerPed())) return; if (m_leader) { if (FindPlayerPed() == m_leader) return; ClearLeader(); } if (m_pedStats->m_flags & STAT_GUN_PANIC && (m_nPedState != PED_ATTACK || GetWeapon()->IsTypeMelee()) && m_nPedState != PED_FLEE_ENTITY && m_nPedState != PED_AIM_GUN) { waitTime = CGeneral::GetRandomNumberInRange(3000, 6000); SetWaitState(WAITSTATE_PLAYANIM_HANDSCOWER, &waitTime); Say(SOUND_PED_HANDS_COWER); m_pLookTarget = pedWithGun; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); SetMoveState(PEDMOVE_NONE); } else if (m_nPedType != pedWithGun->m_nPedType) { if (IsGangMember() || m_nPedType == PEDTYPE_EMERGENCY || m_nPedType == PEDTYPE_FIREMAN) { RegisterThreatWithGangPeds(pedWithGun); } if (m_nPedType == PEDTYPE_COP) { if (pedWithGun->IsPlayer()) { ((CPlayerPed*)pedWithGun)->m_pWanted->SetWantedLevelNoDrop(2); if (bCrouchWhenShooting || bKindaStayInSamePlace) { SetDuck(CGeneral::GetRandomNumberInRange(1000, 3000)); return; } } } if (m_nPedType != PEDTYPE_COP && (m_nPedState != PED_ATTACK || GetWeapon()->IsTypeMelee()) && (m_nPedState != PED_FLEE_ENTITY || pedWithGun->IsPlayer() && m_fleeFrom != pedWithGun) && m_nPedState != PED_AIM_GUN && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) { waitTime = CGeneral::GetRandomNumberInRange(3000, 6000); SetWaitState(WAITSTATE_PLAYANIM_HANDSUP, &waitTime); Say(SOUND_PED_HANDS_UP); m_pLookTarget = pedWithGun; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); SetMoveState(PEDMOVE_NONE); if (m_nPedState == PED_FLEE_ENTITY) { m_fleeFrom = pedWithGun; m_fleeFrom->RegisterReference((CEntity **) &m_fleeFrom); } if (FindPlayerPed() == pedWithGun && bRichFromMugging) { int money = CGeneral::GetRandomNumberInRange(100, 300); int pickupCount = money / 40 + 1; int moneyPerPickup = money / pickupCount; for (int i = 0; i < pickupCount; i++) { float pickupX = 1.5f * Sin((CGeneral::GetRandomNumber() % 256)/256.0f * TWOPI) + GetPosition().x; float pickupY = 1.5f * Cos((CGeneral::GetRandomNumber() % 256)/256.0f * TWOPI) + GetPosition().y; bool found = false; float groundZ = CWorld::FindGroundZFor3DCoord(pickupX, pickupY, GetPosition().z, &found) + 0.5f; if (found) { CPickups::GenerateNewOne(CVector(pickupX, pickupY, groundZ), MI_MONEY, PICKUP_MONEY, moneyPerPickup + (CGeneral::GetRandomNumber() & 7)); } } bRichFromMugging = false; } } } } void CPed::ReactToAttack(CEntity *attacker) { if (IsPlayer() && attacker->IsPed()) { InformMyGangOfAttack(attacker); SetLookFlag(attacker, true); SetLookTimer(700); return; } if (m_nPedType == PEDTYPE_GANG7 && attacker->IsPed() && ((CPed*)attacker)->IsPlayer()) { if (m_nPedState != PED_FALL) { SetFall(15000, (AnimationId)(ANIM_STD_KO_FRONT + CGeneral::GetRandomNumberInRange(0, 5)), 0); } } else if (m_nPedState == PED_DRIVING && InVehicle() && (m_pMyVehicle->pDriver == this || m_pMyVehicle->pDriver && m_pMyVehicle->pDriver->m_nPedState == PED_DRIVING && m_pMyVehicle->pDriver->m_objective != OBJECTIVE_LEAVE_CAR_AND_DIE)) { if (m_pMyVehicle->VehicleCreatedBy == RANDOM_VEHICLE && (m_pMyVehicle->GetStatus() == STATUS_SIMPLE || m_pMyVehicle->GetStatus() == STATUS_PHYSICS) && m_pMyVehicle->AutoPilot.m_nCarMission == MISSION_CRUISE) { CCarCtrl::SwitchVehicleToRealPhysics(m_pMyVehicle); m_pMyVehicle->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; m_pMyVehicle->AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * m_pMyVehicle->pHandling->Transmission.fMaxCruiseVelocity; m_pMyVehicle->SetStatus(STATUS_PHYSICS); } } else if ((IsPedInControl() || m_nPedState == PED_DRIVING) && (CharCreatedBy != MISSION_CHAR || bRespondsToThreats)) { if (m_leader != attacker && (!m_leader || FindPlayerPed() != m_leader) && attacker->IsPed()) { CPed *attackerPed = (CPed*)attacker; if (bNotAllowedToDuck) { if (!attackerPed->GetWeapon()->IsTypeMelee()) { m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds(); return; } } else if (bCrouchWhenShooting || bKindaStayInSamePlace) { SetDuck(CGeneral::GetRandomNumberInRange(1000,3000)); return; } if (m_nWaitState == WAITSTATE_STRIPPER) { ClearWaitState(); SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, attacker); SetObjectiveTimer(20000); } else { if (m_pedStats->m_fear <= 100 - attackerPed->m_pedStats->m_temper) { if (m_pedStats != attackerPed->m_pedStats) { if (IsGangMember() || m_nPedType == PEDTYPE_EMERGENCY || m_nPedType == PEDTYPE_FIREMAN) { RegisterThreatWithGangPeds(attackerPed); } if (!attackerPed->GetWeapon()->IsTypeMelee() && GetWeapon()->IsTypeMelee()) { SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, attacker); SetMoveState(PEDMOVE_RUN); } else { SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, attacker); SetObjectiveTimer(20000); } } } else { SetObjective(OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE, attackerPed); SetMoveState(PEDMOVE_RUN); if (attackerPed->GetWeapon()->IsTypeMelee()) Say(SOUND_PED_FLEE_RUN); } } } } } void CPed::PedAnimAlignCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; CVehicle *veh = ped->m_pMyVehicle; if (animAssoc) animAssoc->blendDelta = -1000.0f; if (!ped->IsNotInWreckedVehicle()) return; if (!ped->EnteringCar()) { if (ped->m_nPedState != PED_DRIVING) ped->QuitEnteringCar(); return; } if (!ped->m_vehDoor) { ped->QuitEnteringCar(); return; } if (ped->m_fHealth == 0.0f) { ped->QuitEnteringCar(); return; } bool itsVan = !!veh->bIsVan; bool itsBus = !!veh->bIsBus; bool itsLow = !!veh->bLowVehicle; eDoors enterDoor; switch (ped->m_vehDoor) { case CAR_DOOR_RF: itsVan = false; enterDoor = DOOR_FRONT_RIGHT; break; case CAR_DOOR_RR: enterDoor = DOOR_REAR_RIGHT; break; case CAR_DOOR_LF: itsVan = false; enterDoor = DOOR_FRONT_LEFT; break; case CAR_DOOR_LR: enterDoor = DOOR_REAR_LEFT; break; default: break; } if (veh->IsBike()) { CPed *pedToDragOut = nil; if (veh->GetStatus() == STATUS_ABANDONED) { if (ped->m_vehDoor == CAR_WINDSCREEN) { ped->m_pVehicleAnim = CAnimManager::BlendAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_KICK, 6.0f); ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); ((CBike*)veh)->bIsBeingPickedUp = true; } else if (veh->GetRight().z >= 0.5f || veh->GetRight().z <= -0.5f || veh->GetUp().z <= 0.0f) { if (enterDoor == DOOR_FRONT_LEFT || enterDoor == DOOR_REAR_LEFT) { if (veh->GetRight().z > 0.0f) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_PICKUP_LHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_PULLUP_LHS); } else { if (veh->GetRight().z < 0.0f) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_PICKUP_RHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_BIKE_PULLUP_RHS); } ped->m_pVehicleAnim->SetFinishCallback(PedAnimDoorOpenCB, ped); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, enterDoor == DOOR_FRONT_LEFT || enterDoor == DOOR_REAR_LEFT ? ANIM_BIKE_JUMPON_LHS : ANIM_BIKE_JUMPON_RHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); ((CBike*)veh)->bIsBeingPickedUp = true; } } else if (ped->m_vehDoor == CAR_WINDSCREEN) { if (veh->pDriver->m_nPedState != PED_DRIVING || veh->pDriver->bDontDragMeOutCar) { ped->QuitEnteringCar(); } else { ped->m_pVehicleAnim = CAnimManager::BlendAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_KICK, 6.0f); ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); pedToDragOut = veh->pDriver; } ((CBike*)veh)->bIsBeingPickedUp = true; } else { if (enterDoor == DOOR_FRONT_LEFT || enterDoor == DOOR_FRONT_RIGHT) { if (veh->pDriver) { if (veh->m_vecMoveSpeed.Magnitude() > 0.2f) { ped->QuitEnteringCar(); ped->SetFall(1000, ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_LR ? ANIM_STD_HIGHIMPACT_RIGHT : ANIM_STD_HIGHIMPACT_LEFT, false); return; } if (veh->pDriver->m_nPedState != PED_DRIVING || veh->pDriver->bDontDragMeOutCar) { ped->QuitEnteringCar(); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, enterDoor == DOOR_FRONT_LEFT ? ANIM_STD_BIKE_ELBOW_LHS : ANIM_STD_BIKE_ELBOW_RHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimPullPedOutCB, ped); pedToDragOut = veh->pDriver; } ((CBike*)veh)->bIsBeingPickedUp = true; } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, enterDoor == DOOR_FRONT_LEFT ? ANIM_BIKE_JUMPON_LHS : ANIM_BIKE_JUMPON_RHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); ((CBike*)veh)->bIsBeingPickedUp = true; } } else { if (veh->pPassengers[0]) { if (veh->m_vecMoveSpeed.Magnitude() > 0.2f) { ped->QuitEnteringCar(); ped->SetFall(1000, ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_LR ? ANIM_STD_HIGHIMPACT_RIGHT : ANIM_STD_HIGHIMPACT_LEFT, false); return; } if (veh->pPassengers[0]->m_nPedState != PED_DRIVING || veh->pPassengers[0]->bDontDragMeOutCar) { ped->QuitEnteringCar(); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, enterDoor == DOOR_REAR_LEFT ? ANIM_STD_BIKE_ELBOW_LHS : ANIM_STD_BIKE_ELBOW_RHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimPullPedOutCB, ped); pedToDragOut = veh->pPassengers[0]; } ((CBike*)veh)->bIsBeingPickedUp = true; } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, enterDoor == DOOR_REAR_LEFT ? ANIM_BIKE_JUMPON_LHS : ANIM_BIKE_JUMPON_RHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); ((CBike*)veh)->bIsBeingPickedUp = true; } } } if (pedToDragOut) { pedToDragOut->SetBeingDraggedFromCar(veh, ped->m_vehDoor, false); if (pedToDragOut->IsGangMember()) pedToDragOut->RegisterThreatWithGangPeds(ped); if (ped->m_nPedType == PEDTYPE_COP && pedToDragOut == FindPlayerPed() && veh->IsBike()) ((CCopPed*)ped)->m_bDragsPlayerFromCar = 1; if (pedToDragOut == veh->pDriver) { if (veh->pPassengers[0]) veh->pPassengers[0]->SetBeingDraggedFromCar(veh, CAR_DOOR_LR, false); } } return; } if (veh->IsDoorMissing(enterDoor) || veh->IsDoorFullyOpen(enterDoor)) { veh->AutoPilot.m_nCruiseSpeed = 0; if (ped->m_nPedState == PED_CARJACK || veh->m_nNumMaxPassengers == 0 && veh->pDriver && enterDoor == DOOR_FRONT_RIGHT) { ped->PedAnimDoorOpenCB(nil, ped); return; } if (enterDoor != DOOR_FRONT_LEFT && enterDoor != DOOR_REAR_LEFT) { if (itsVan) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_RHS); } else if (itsBus) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_IN_RHS); } else if (itsLow) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_RHS); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_RHS); } } else if (itsVan) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_LHS); } else if (itsBus) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_IN_LHS); } else if (itsLow) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS); } ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); } else if (veh->CanPedOpenLocks(ped)) { veh->AutoPilot.m_nCruiseSpeed = 0; if (enterDoor != DOOR_FRONT_LEFT && enterDoor != DOOR_REAR_LEFT) { if (itsVan) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_OPEN_DOOR_REAR_RHS); } else if (itsBus) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_OPEN_RHS); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_OPEN_DOOR_RHS); } } else if (itsVan) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_OPEN_DOOR_REAR_LHS); } else if (itsBus) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_OPEN_LHS); } else { if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && veh->pDriver) { if (!veh->bLowVehicle && veh->pDriver->CharCreatedBy != MISSION_CHAR && veh->pDriver->m_nPedState == PED_DRIVING) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_QUICKJACK); ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); CPlayerPed *player = nil; CCopPed *cop = nil; veh->MakeNonDraggedPedsLeaveVehicle(veh->pDriver, ped, player, cop); if (player && cop) { cop->QuitEnteringCar(); cop->SetArrestPlayer(player); } if (player != veh->pDriver) { veh->pDriver->SetBeingDraggedFromCar(veh, ped->m_vehDoor, true); if (veh->pDriver->IsGangMember()) veh->pDriver->RegisterThreatWithGangPeds(ped); } return; } } if (veh->IsOpenTopCar() && !veh->pDriver && ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_JUMP_IN_LO_LHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); return; } ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_OPEN_DOOR_LHS); } ped->m_pVehicleAnim->SetFinishCallback(PedAnimDoorOpenCB, ped); } else { if (enterDoor != DOOR_FRONT_LEFT && enterDoor != DOOR_REAR_LEFT) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CARDOOR_LOCKED_RHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CARDOOR_LOCKED_LHS); ped->bCancelEnteringCar = true; ped->m_pVehicleAnim->SetFinishCallback(PedAnimDoorOpenCB, ped); } } void CPed::PedAnimDoorOpenCB(CAnimBlendAssociation* animAssoc, void* arg) { CPed* ped = (CPed*)arg; CVehicle* veh = ped->m_pMyVehicle; if (animAssoc) animAssoc->blendDelta = -1000.0f; if (!ped->IsNotInWreckedVehicle()) return; if (!ped->EnteringCar()) { if(ped->m_nPedState != PED_DRIVING) ped->QuitEnteringCar(); return; } eDoors door; CPed *pedInSeat = nil; switch (ped->m_vehDoor) { case CAR_DOOR_RF: door = DOOR_FRONT_RIGHT; pedInSeat = veh->pPassengers[0]; if (!veh->pPassengers[0] && ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) pedInSeat = veh->pDriver; break; case CAR_DOOR_RR: door = DOOR_REAR_RIGHT; pedInSeat = veh->pPassengers[2]; break; case CAR_DOOR_LF: door = DOOR_FRONT_LEFT; pedInSeat = veh->pDriver; if (veh->bIsBus && ped->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) pedInSeat = nil; break; case CAR_DOOR_LR: door = DOOR_REAR_LEFT; pedInSeat = veh->pPassengers[1]; break; default: assert(0); } if (ped->m_fHealth == 0.0f || CPad::GetPad(0)->ArePlayerControlsDisabled() && pedInSeat && pedInSeat->IsPlayer()) { ped->QuitEnteringCar(); return; } bool isVan = veh->bIsVan; bool isBus = veh->bIsBus; bool isLow = veh->bLowVehicle; bool vehUpsideDown = veh->IsUpsideDown(); if (ped->bCancelEnteringCar) { if (ped->IsPlayer()) { if (veh->pDriver) { if (veh->pDriver->m_nPedType == PEDTYPE_COP) { FindPlayerPed()->SetWantedLevelNoDrop(1); } } } #ifdef CANCELLABLE_CAR_ENTER if (!veh->IsDoorMissing(door) && veh->CanPedOpenLocks(ped) && veh->IsCar()) { ((CAutomobile*)veh)->Damage.SetDoorStatus(door, DOOR_STATUS_SWINGING); } #endif ped->QuitEnteringCar(); ped->RestorePreviousObjective(); ped->bCancelEnteringCar = false; return; } if (!veh->IsDoorMissing(door) && veh->IsCar()) { ((CAutomobile*)veh)->Damage.SetDoorStatus(door, DOOR_STATUS_SWINGING); } if (veh->m_vecMoveSpeed.Magnitude() > 0.2f || veh->IsCar() && veh->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI && ((CAutomobile*)veh)->m_nWheelsOnGround == 0) { ped->QuitEnteringCar(); if (ped->m_vehDoor != CAR_DOOR_LF && ped->m_vehDoor != CAR_DOOR_LR) ped->SetFall(1000, ANIM_STD_HIGHIMPACT_LEFT, false); else ped->SetFall(1000, ANIM_STD_HIGHIMPACT_RIGHT, false); return; } veh->ProcessOpenDoor(ped->m_vehDoor, ANIM_STD_CAR_OPEN_DOOR_LHS, 1.0f); if (ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_RF) isVan = false; if (ped->m_nPedState != PED_CARJACK || isBus) { if (ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_LR) { if (veh->IsBike()) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_JUMPON_LHS); } else if (isVan) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_LHS); } else if (isBus) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_IN_LHS); } else if (isLow) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS); } } else { if (veh->IsBike()) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_JUMPON_RHS); } else if (isVan) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_RHS); } else if (isBus) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_IN_RHS); } else if (isLow) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_RHS); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_RHS); } if (ped->m_vehDoor == CAR_DOOR_RF && pedInSeat && veh->IsCar()) pedInSeat->SetObjective(OBJECTIVE_LEAVE_CAR, veh); } ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); } else { CPed *pedToDragOut = nil; switch (ped->m_vehDoor) { case CAR_DOOR_RF: pedToDragOut = veh->pPassengers[0]; break; case CAR_DOOR_RR: pedToDragOut = veh->pPassengers[2]; break; case CAR_DOOR_LF: pedToDragOut = veh->pDriver; break; case CAR_DOOR_LR: pedToDragOut = veh->pPassengers[1]; break; default: assert(0); } if (vehUpsideDown) { ped->QuitEnteringCar(); if (ped->m_nPedType == PEDTYPE_COP) ((CCopPed*)ped)->SetArrestPlayer(ped->m_pedInObjective); } if (ped->m_vehDoor != CAR_DOOR_LF && ped->m_vehDoor != CAR_DOOR_LR) { if (pedToDragOut && !pedToDragOut->bDontDragMeOutCar) { if (pedToDragOut->m_nPedState != PED_DRIVING) { ped->QuitEnteringCar(); pedToDragOut = nil; } else { if (isLow) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_PULL_OUT_PED_LO_RHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_PULL_OUT_PED_RHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimPullPedOutCB, ped); } } else if (ped->m_nPedType == PEDTYPE_COP) { ped->QuitEnteringCar(); if (ped->m_pedInObjective && ped->m_pedInObjective->m_nPedState == PED_DRIVING) { veh->SetStatus(STATUS_PLAYER_DISABLED); if (ped->m_pedInObjective->IsPlayer()) { ((CCopPed*)ped)->SetArrestPlayer(ped->m_pedInObjective); } else { ped->ClearObjective(); ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.f, 8.f)); } } else if (!veh->IsDoorMissing(DOOR_FRONT_RIGHT)) { ((CAutomobile*)veh)->Damage.SetDoorStatus(DOOR_FRONT_RIGHT, DOOR_STATUS_SWINGING); } } else { if (isLow) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_RHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_RHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); } } else if (pedToDragOut) { if (pedToDragOut->m_nPedState != PED_DRIVING || pedToDragOut->bDontDragMeOutCar) { ped->QuitEnteringCar(); pedToDragOut = nil; } else { if (isLow) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_PULL_OUT_PED_LO_LHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_PULL_OUT_PED_LHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimPullPedOutCB, ped); } } else { if (isLow) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS); ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); } if (pedToDragOut) { CPlayerPed* player = nil; CCopPed* cop = nil; veh->MakeNonDraggedPedsLeaveVehicle(pedToDragOut, ped, player, cop); if (player && cop) { cop->QuitEnteringCar(); veh->SetStatus(STATUS_PLAYER_DISABLED); cop->SetArrestPlayer(player); } if (player != pedToDragOut) { pedToDragOut->SetBeingDraggedFromCar(veh, ped->m_vehDoor, false); if (pedToDragOut->IsGangMember()) pedToDragOut->RegisterThreatWithGangPeds(ped); } } } return; } void CPed::PedAnimPullPedOutCB(CAnimBlendAssociation* animAssoc, void* arg) { CPed* ped = (CPed*)arg; CVehicle* veh = ped->m_pMyVehicle; if (animAssoc) animAssoc->blendDelta = -1000.0f; if (ped->EnteringCar()) { if (!ped->IsNotInWreckedVehicle()) return; #ifdef CANCELLABLE_CAR_ENTER if (ped->bCancelEnteringCar) { ped->QuitEnteringCar(); ped->RestorePreviousObjective(); ped->bCancelEnteringCar = false; return; } #endif bool isLow = !!veh->bLowVehicle; int padNo; if (ped->IsPlayer()) { // BUG? This will cause crash if m_nPedType is bigger then 1, there are only 2 pads switch (ped->m_nPedType) { case PEDTYPE_PLAYER1: padNo = 0; break; case PEDTYPE_PLAYER2: padNo = 1; break; case PEDTYPE_PLAYER3: padNo = 2; break; case PEDTYPE_PLAYER4: padNo = 3; break; } CPad *pad = CPad::GetPad(padNo); if (!pad->ArePlayerControlsDisabled()) { if (pad->GetTarget() || pad->NewState.LeftStickX || pad->NewState.LeftStickY || pad->NewState.DPadUp || pad->NewState.DPadDown || pad->NewState.DPadLeft || pad->NewState.DPadRight) { ped->QuitEnteringCar(); ped->RestorePreviousObjective(); return; } } } if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { if (ped->m_vehDoor == CAR_DOOR_LF || ped->m_vehDoor == CAR_DOOR_LR) { if (veh->IsBike()) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_JUMPON_LHS); else if (isLow) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS); } else { if (veh->IsBike()) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ((CBike*)veh)->m_bikeAnimType, ANIM_BIKE_JUMPON_RHS); else if (isLow) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_RHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_RHS); } ped->m_pVehicleAnim->SetFinishCallback(PedAnimGetInCB, ped); } else { ped->QuitEnteringCar(); } } else if(ped->m_nPedState != PED_DRIVING) { ped->QuitEnteringCar(); } } void CPed::PedAnimGetInCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*) arg; CVehicle *veh = ped->m_pMyVehicle; if (animAssoc) animAssoc->blendDelta = -1000.0f; if (!ped->IsNotInWreckedVehicle() || ped->DyingOrDead()) return; if (!ped->EnteringCar()) { if(ped->m_nPedState != PED_DRIVING) ped->QuitEnteringCar(); return; } ped->RemoveWeaponWhenEnteringVehicle(); if (ped->IsPlayer() && ped->bGonnaKillTheCarJacker && ((CPlayerPed*)ped)->m_pArrestingCop) { PedSetInCarCB(nil, ped); ped->m_nLastPedState = ped->m_nPedState; ped->SetPedState(PED_ARRESTED); ped->bGonnaKillTheCarJacker = false; if (veh) { veh->m_nNumGettingIn = 0; veh->m_nGettingInFlags = 0; veh->bIsHandbrakeOn = true; veh->SetStatus(STATUS_PLAYER_DISABLED); } return; } if (ped->IsPlayer() && ped->m_vehDoor == CAR_DOOR_LF && (Pads[0].GetAccelerate() >= 255.0f || Pads[0].GetBrake() >= 255.0f) && veh->IsCar() && !veh->pDriver) { if (!animAssoc || animAssoc->animId != ANIM_STD_CAR_JUMP_IN_LO_LHS) if (((CAutomobile*)veh)->Damage.GetDoorStatus(DOOR_FRONT_LEFT) != DOOR_STATUS_MISSING) ((CAutomobile*)veh)->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_SWINGING); PedSetInCarCB(nil, ped); return; } if (veh->IsBike()) { ped->PedSetInCarCB(nil, ped); return; } bool isVan = !!veh->bIsVan; bool isBus = !!veh->bIsBus; bool isLow = !!veh->bLowVehicle; eDoors enterDoor; switch (ped->m_vehDoor) { case CAR_DOOR_RF: isVan = false; enterDoor = DOOR_FRONT_RIGHT; break; case CAR_DOOR_RR: enterDoor = DOOR_REAR_RIGHT; break; case CAR_DOOR_LF: isVan = false; enterDoor = DOOR_FRONT_LEFT; break; case CAR_DOOR_LR: enterDoor = DOOR_REAR_LEFT; break; default: break; } bool doorClosed = true; if (veh->IsOpenTopCar() && enterDoor == DOOR_FRONT_LEFT && veh->IsDoorClosed(DOOR_FRONT_LEFT)) { doorClosed = false; } else if (!veh->IsDoorMissing(enterDoor)) { if (veh->IsCar()) ((CAutomobile*)veh)->Damage.SetDoorStatus(enterDoor, DOOR_STATUS_SWINGING); } CPed *driver = veh->pDriver; if (driver && (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK)) { if (veh->bIsBus) { driver->SetObjective(OBJECTIVE_LEAVE_CAR, veh); if (driver->IsPlayer()) { veh->bIsHandbrakeOn = true; veh->SetStatus(STATUS_PLAYER_DISABLED); } driver->bBusJacked = true; veh->bIsBeingCarJacked = false; PedSetInCarCB(nil, ped); if (ped->m_nPedType == PEDTYPE_COP || ped->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || ped->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) { ped->SetObjective(OBJECTIVE_LEAVE_CAR, veh); } ped->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 400; return; } if (driver != ped && ped->m_vehDoor != CAR_DOOR_LF) { if (!driver->IsPlayer()) { driver->bUsePedNodeSeek = true; driver->m_pLastPathNode = nil; if (driver->m_pedStats->m_temper <= driver->m_pedStats->m_fear || driver->CharCreatedBy == MISSION_CHAR || veh->VehicleCreatedBy == MISSION_VEHICLE) { driver->bFleeAfterExitingCar = true; } else { driver->bGonnaKillTheCarJacker = true; veh->pDriver->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, ped); if (veh->pDriver->m_nPedType == PEDTYPE_COP && ped->IsPlayer()) { FindPlayerPed()->SetWantedLevelNoDrop(1); } } } if ((ped->m_nPedType != PEDTYPE_EMERGENCY || veh->pDriver->m_nPedType != PEDTYPE_EMERGENCY) && (ped->m_nPedType != PEDTYPE_COP || veh->pDriver->m_nPedType != PEDTYPE_COP)) { veh->pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, veh); veh->pDriver->Say(SOUND_PED_CAR_JACKED); veh->pDriver->SetRadioStation(); if (veh->m_nDoorLock == CARLOCK_UNLOCKED) ped->Say(SOUND_PED_CAR_JACKING); } else { ped->m_objective = OBJECTIVE_ENTER_CAR_AS_PASSENGER; } } } if (veh->IsDoorMissing(enterDoor) || !doorClosed || isBus) { PedAnimDoorCloseCB(nil, ped); } else { if (enterDoor != DOOR_FRONT_LEFT && enterDoor != DOOR_REAR_LEFT) { if (isVan) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS); } else if (isLow) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_LO_RHS); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_RHS); } } else if (isVan) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS); } else if (isLow) { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_LO_LHS); } else { ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_LHS); } ped->m_pVehicleAnim->SetFinishCallback(PedAnimDoorCloseCB, ped); } } void CPed::PedShuffle(void) { if (m_pMyVehicle->pPassengers[0] == this) { CPed *driver = m_pMyVehicle->pDriver; if (!driver || driver->m_objective == OBJECTIVE_LEAVE_CAR) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, m_pMyVehicle->bLowVehicle ? ANIM_STD_CAR_SHUFFLE_LO_RHS : ANIM_STD_CAR_SHUFFLE_RHS); m_objective = OBJECTIVE_ENTER_CAR_AS_DRIVER; m_pMyVehicle->RemovePassenger(this); bInVehicle = false; m_pVehicleAnim->SetFinishCallback(PedSetInCarCB, this); } } } void CPed::PedAnimDoorCloseCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; CAutomobile *veh = (CAutomobile*)(ped->m_pMyVehicle); if (!ped->IsNotInWreckedVehicle() || ped->DyingOrDead()) return; if (ped->EnteringCar()) { bool isLow = !!veh->bLowVehicle; if (!veh->bIsBus) veh->ProcessOpenDoor(ped->m_vehDoor, ANIM_STD_CAR_CLOSE_DOOR_LHS, 1.0f); eDoors door; switch (ped->m_vehDoor) { 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 (veh->Damage.GetDoorStatus(door) == DOOR_STATUS_SWINGING) veh->Damage.SetDoorStatus(door, DOOR_STATUS_OK); if (door == DOOR_FRONT_LEFT || ped->m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || veh->bIsBus || veh->m_nNumMaxPassengers == 0) { PedSetInCarCB(nil, ped); } else if (ped->m_vehDoor == CAR_DOOR_RF && (veh->m_nGettingInFlags & CAR_DOOR_FLAG_LF || (veh->pDriver != nil && (veh->pDriver->m_objective != OBJECTIVE_LEAVE_CAR && veh->pDriver->m_objective != OBJECTIVE_LEAVE_CAR_AND_DIE || !veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, nil))))) { if (ped->m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || ped->m_nPedState == PED_CARJACK) veh->bIsBeingCarJacked = false; ped->m_objective = OBJECTIVE_ENTER_CAR_AS_PASSENGER; PedSetInCarCB(nil, ped); ped->SetObjective(OBJECTIVE_LEAVE_CAR, veh); if (!ped->IsPlayer()) ped->bFleeAfterExitingCar = true; ped->bUsePedNodeSeek = true; ped->m_pNextPathNode = nil; } else { if (animAssoc) animAssoc->blendDelta = -1000.0f; if (isLow) ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SHUFFLE_LO_RHS); else ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SHUFFLE_RHS); ped->m_pVehicleAnim->SetFinishCallback(PedSetInCarCB, ped); } } else if (ped->m_nPedState != PED_DRIVING) { ped->QuitEnteringCar(); } } void CPed::PedAnimShuffleCB(CAnimBlendAssociation* assoc, void* arg) { CPed *ped = (CPed*)arg; if (ped->EnteringCar()) { PedSetInCarCB(nil, ped); } else if (ped->m_nPedState != PED_DRIVING) { ped->QuitEnteringCar(); } } void CPed::SetFormation(eFormation type) { // FIX: Formations in GetFormationPosition were in range 1-8, whereas in here it's 0-7. // To not change the behaviour, range in here tweaked by 1 with the use of enum. switch (m_pedFormation) { case FORMATION_REAR: case FORMATION_REAR_LEFT: case FORMATION_REAR_RIGHT: case FORMATION_FRONT_LEFT: case FORMATION_FRONT_RIGHT: case FORMATION_LEFT: case FORMATION_RIGHT: case FORMATION_FRONT: break; default: Error("Unknown formation type, PedAI.cpp"); break; } m_pedFormation = type; } CVector CPed::GetFormationPosition(void) { if (!m_pedInObjective) return GetPosition(); if (m_pedInObjective->m_nPedState == PED_DEAD) { if (!m_pedInObjective->m_pedInObjective) { m_pedInObjective = nil; return GetPosition(); } m_pedInObjective = m_pedInObjective->m_pedInObjective; } CVector formationOffset; float offset = CGeneral::GetRandomNumberInRange(1.f, 1.25f) * 1.75f; switch (m_pedFormation) { case FORMATION_REAR: formationOffset = CVector(0.0f, -offset, 0.0f); break; case FORMATION_REAR_LEFT: formationOffset = CVector(-offset, -offset, 0.0f); break; case FORMATION_REAR_RIGHT: formationOffset = CVector(offset, -offset, 0.0f); break; case FORMATION_FRONT_LEFT: formationOffset = CVector(-offset, offset, 0.0f); break; case FORMATION_FRONT_RIGHT: formationOffset = CVector(offset, offset, 0.0f); break; case FORMATION_LEFT: formationOffset = CVector(-offset, 0.0f, 0.0f); break; case FORMATION_RIGHT: formationOffset = CVector(offset, 0.0f, 0.0f); break; case FORMATION_FRONT: formationOffset = CVector(0.0f, offset, 0.0f); break; default: formationOffset = CVector(0.0f, 0.0f, 0.0f); break; } return m_pedInObjective->GetMatrix() * formationOffset; } void CPed::PedAnimStepOutCarCB(CAnimBlendAssociation* animAssoc, void* arg) { CPed* ped = (CPed*)arg; CVehicle* veh = ped->m_pMyVehicle; if (animAssoc) { if ((animAssoc->animId == ANIM_STD_ROLLOUT_LHS || animAssoc->animId == ANIM_STD_ROLLOUT_RHS) && ped && ped->m_nPedState == PED_FALL) { ped->RestoreHeadingRate(); return; } animAssoc->blendDelta = -1000.0f; if (animAssoc->animId == ANIM_BIKE_GETOFF_BACK) ped->RestoreHeadingRate(); } if (!veh) { PedSetOutCarCB(nil, ped); return; } CVector posForZ = ped->GetPosition(); CPedPlacement::FindZCoorForPed(&posForZ); if (ped->GetPosition().z - 0.5f > posForZ.z) { PedSetOutCarCB(nil, ped); return; } veh->m_nStaticFrames = 0; veh->m_vecMoveSpeed += CVector(0.001f, 0.001f, 0.001f); veh->m_vecTurnSpeed += CVector(0.001f, 0.001f, 0.001f); if (!veh->bIsBus) veh->ProcessOpenDoor(ped->m_vehDoor, ANIM_STD_GETOUT_LHS, 1.0f); /* // Duplicate and only in PC for some reason if (!veh) { PedSetOutCarCB(nil, ped); return; } */ eDoors door; switch (ped->m_vehDoor) { 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: break; } bool closeDoor = !veh->IsDoorMissing(door); int padNo; if (ped->IsPlayer()) { // BUG? This will cause crash if m_nPedType is bigger then 1, there are only 2 pads switch (ped->m_nPedType) { case PEDTYPE_PLAYER1: padNo = 0; break; case PEDTYPE_PLAYER2: padNo = 1; break; case PEDTYPE_PLAYER3: padNo = 2; break; case PEDTYPE_PLAYER4: padNo = 3; break; } CPad* pad = CPad::GetPad(padNo); bool engineIsIntact = veh->IsCar() && ((CAutomobile*)veh)->Damage.GetEngineStatus() >= 225; if (!pad->ArePlayerControlsDisabled() && veh->m_nDoorLock != CARLOCK_FORCE_SHUT_DOORS && (pad->GetTarget() || pad->NewState.LeftStickX || pad->NewState.LeftStickY || pad->NewState.DPadUp || pad->NewState.DPadDown || pad->NewState.DPadLeft || pad->NewState.DPadRight) || veh->bIsBus || veh->m_pCarFire || engineIsIntact) { closeDoor = false; } } if (ped->m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) closeDoor = false; if (!closeDoor) { if (!veh->IsDoorMissing(door) && !veh->bIsBus) { ((CAutomobile*)veh)->Damage.SetDoorStatus(door, DOOR_STATUS_SWINGING); } PedSetOutCarCB(nil, ped); return; } if (ped->bFleeAfterExitingCar || ped->bGonnaKillTheCarJacker) { #ifdef FIX_BUGS if (!veh->IsDoorMissing(door)) ((CAutomobile*)veh)->Damage.SetDoorStatus(door, DOOR_STATUS_SWINGING); PedSetOutCarCB(nil, ped); return; #else if (!veh->IsDoorMissing(door)) ((CAutomobile*)veh)->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_SWINGING); #endif } else { switch (door) { case DOOR_FRONT_LEFT: ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_LHS); break; case DOOR_FRONT_RIGHT: ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_RHS); break; case DOOR_REAR_LEFT: ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_LHS); break; case DOOR_REAR_RIGHT: ped->m_pVehicleAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_RHS); break; default: break; } } if (ped->m_pVehicleAnim) ped->m_pVehicleAnim->SetFinishCallback(PedSetOutCarCB, ped); return; } void CPed::LineUpPedWithCar(PedLineUpPhase phase) { bool vehIsUpsideDown = false; bool stillGettingInOut = false; int vehAnim; float seatPosMult = 0.0f; float currentZ; float adjustedTimeStep; CVector autoZPos; if (CReplay::IsPlayingBack()) return; if (!bChangedSeat && phase != LINE_UP_TO_CAR_2) { if (m_pMyVehicle->IsBike()) { if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_RIDE) || RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_RIDE_P)) { SetPedPositionInCar(); return; } } else { if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT)) { SetPedPositionInCar(); return; } if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT_LO)) { SetPedPositionInCar(); return; } if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT_P)) { SetPedPositionInCar(); return; } if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT_P_LO)) { SetPedPositionInCar(); return; } } bChangedSeat = true; } if (phase == LINE_UP_TO_CAR_FALL) { SetPedPositionInCar(); autoZPos = GetPosition(); CPedPlacement::FindZCoorForPed(&autoZPos); if (m_pVehicleAnim && (m_pVehicleAnim->animId == ANIM_STD_ROLLOUT_LHS || m_pVehicleAnim->animId == ANIM_STD_ROLLOUT_RHS) && autoZPos.z > GetPosition().z) { m_matrix.GetPosition().z = autoZPos.z; } if (m_pVehicleAnim && m_pVehicleAnim->animId == ANIM_BIKE_HIT) { if (autoZPos.z > GetPosition().z) m_matrix.GetPosition().z += m_pVehicleAnim->GetProgress() * (autoZPos.z - GetPosition().z); } else if (m_pVehicleAnim) { if (m_pVehicleAnim->animId == ANIM_BIKE_GETOFF_BACK) { if (autoZPos.z > GetPosition().z) { m_matrix.GetPosition().z += (m_pVehicleAnim->currentTime * (20.f / 7.f)) * (autoZPos.z - GetPosition().z); } } } return; } if (phase == LINE_UP_TO_CAR_START) { m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); } CVehicle *veh = m_pMyVehicle; // Not quite right, IsUpsideDown func. checks for <= -0.9f. if (veh->GetUp().z <= -0.8f) vehIsUpsideDown = true; if (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR) { if (vehIsUpsideDown) { m_fRotationDest = -PI + veh->GetForward().Heading(); } else if (veh->bIsBus) { m_fRotationDest = 0.5f * PI + veh->GetForward().Heading(); } else { m_fRotationDest = veh->GetForward().Heading(); } } else if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) { if (vehIsUpsideDown) { m_fRotationDest = veh->GetForward().Heading(); } else if (veh->bIsBus) { m_fRotationDest = -0.5f * PI + veh->GetForward().Heading(); } else { m_fRotationDest = veh->GetForward().Heading(); } } else { // I don't know will this part ever run(maybe boats?), but the game also handles that. I don't know is it intentional. if (vehIsUpsideDown) { m_fRotationDest = veh->GetForward().Heading(); } else if (veh->bIsBus) { m_fRotationDest = 0.5f * PI + veh->GetForward().Heading(); } else if (m_vehDoor == CAR_WINDSCREEN) { m_fRotationDest = veh->GetForward().Heading() + PI; } else { m_fRotationDest = veh->GetForward().Heading(); } } bool multExtractedFromAnim = false; bool multExtractedFromAnimBus = false; float zBlend; if (m_pVehicleAnim) { vehAnim = m_pVehicleAnim->animId; switch (vehAnim) { case ANIM_STD_JACKEDCAR_RHS: case ANIM_STD_JACKEDCAR_LO_RHS: case ANIM_STD_JACKEDCAR_LHS: case ANIM_STD_JACKEDCAR_LO_LHS: case ANIM_STD_VAN_GET_IN_REAR_LHS: case ANIM_STD_VAN_GET_IN_REAR_RHS: multExtractedFromAnim = true; zBlend = Max(m_pVehicleAnim->GetProgress() - 0.3f, 0.0f) / (1.0f - 0.3f); // fall through case ANIM_STD_QUICKJACKED: case ANIM_STD_GETOUT_LHS: case ANIM_STD_GETOUT_LO_LHS: case ANIM_STD_GETOUT_RHS: case ANIM_STD_GETOUT_LO_RHS: if (!multExtractedFromAnim) { multExtractedFromAnim = true; zBlend = Max(m_pVehicleAnim->GetProgress() - 0.5f, 0.0f) / (1.0f - 0.5f); } // fall through case ANIM_STD_CRAWLOUT_LHS: case ANIM_STD_CRAWLOUT_RHS: case ANIM_STD_VAN_GET_OUT_REAR_LHS: case ANIM_STD_VAN_GET_OUT_REAR_RHS: case ANIM_BIKE_GETOFF_LHS: case ANIM_BIKE_GETOFF_RHS: seatPosMult = m_pVehicleAnim->GetProgress(); break; case ANIM_STD_CAR_GET_IN_RHS: case ANIM_STD_CAR_GET_IN_LHS: if (veh && veh->IsCar() && veh->bIsBus) { multExtractedFromAnimBus = true; zBlend = Min(m_pVehicleAnim->GetProgress(), 0.5f) / 0.5f; } // fall through case ANIM_STD_QUICKJACK: case ANIM_STD_CAR_GET_IN_LO_LHS: case ANIM_STD_CAR_GET_IN_LO_RHS: case ANIM_STD_BOAT_DRIVE: seatPosMult = m_pVehicleAnim->GetTimeLeft() / m_pVehicleAnim->hierarchy->totalLength; break; case ANIM_STD_CAR_CLOSE_DOOR_LHS: case ANIM_STD_CAR_CLOSE_DOOR_LO_LHS: case ANIM_STD_CAR_CLOSE_DOOR_RHS: case ANIM_STD_CAR_CLOSE_DOOR_LO_RHS: case ANIM_STD_CAR_SHUFFLE_RHS: case ANIM_STD_CAR_SHUFFLE_LO_RHS: seatPosMult = 0.0f; break; case ANIM_STD_CAR_JUMP_IN_LO_LHS: { float animLength = m_pVehicleAnim->hierarchy->totalLength; seatPosMult = Max(0.0f, 0.5f * animLength - m_pVehicleAnim->currentTime) / animLength; break; } case ANIM_STD_CAR_CLOSE_LHS: case ANIM_STD_CAR_CLOSE_RHS: case ANIM_STD_COACH_OPEN_LHS: case ANIM_STD_COACH_OPEN_RHS: case ANIM_STD_COACH_GET_IN_LHS: case ANIM_STD_COACH_GET_IN_RHS: case ANIM_STD_COACH_GET_OUT_LHS: seatPosMult = 1.0f; break; default: if (veh->IsBike()) { seatPosMult = 0.0f; } else { if (bInVehicle) seatPosMult = 0.0f; else seatPosMult = 1.0f; } break; } } else { if (veh->IsBike()) { seatPosMult = 0.0f; } else { if (bInVehicle) seatPosMult = 0.0f; else seatPosMult = 1.0f; } } CVector neededPos; if (phase == LINE_UP_TO_CAR_2) { neededPos = GetPosition(); } else { neededPos = GetPositionToOpenCarDoor(veh, m_vehDoor, seatPosMult); } autoZPos = neededPos; if (veh->bIsInWater) { if (veh->m_vehType == VEHICLE_TYPE_BOAT && veh->IsUpsideDown()) autoZPos.z += 1.0f; } else { CPedPlacement::FindZCoorForPed(&autoZPos); } if (phase == LINE_UP_TO_CAR_END || phase == LINE_UP_TO_CAR_2) { neededPos.z = GetPosition().z; // Getting out if (!veh->bIsBus || (veh->bIsBus && vehIsUpsideDown)) { float nextZSpeed = m_vecMoveSpeed.z - GRAVITY * CTimer::GetTimeStep(); // If we're not in ground at next step, apply animation if (neededPos.z + nextZSpeed >= autoZPos.z) { m_vecMoveSpeed.z = nextZSpeed; ApplyMoveSpeed(); // Removing below line breaks the animation neededPos.z = GetPosition().z; } else { neededPos.z = autoZPos.z; m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); } } } if (autoZPos.z > neededPos.z) { vehAnim = m_pVehicleAnim->animId; if (veh->IsBike() && (m_pVehicleAnim && vehAnim != ANIM_BIKE_KICK)) { float zBlend; if (vehAnim != ANIM_BIKE_GETOFF_LHS && vehAnim != ANIM_BIKE_GETOFF_RHS) { if (vehAnim != ANIM_BIKE_JUMPON_LHS && vehAnim != ANIM_BIKE_JUMPON_RHS) { zBlend = 0.0f; } else { float animLength = m_pVehicleAnim->hierarchy->totalLength; zBlend = Min(1.0f, 2.0f * m_pVehicleAnim->currentTime / animLength); } } else { zBlend = 1.0f - seatPosMult; } float curZ = veh->GetPosition().z + FEET_OFFSET; neededPos.z = ((curZ - autoZPos.z) - veh->GetHeightAboveRoad()) * zBlend + autoZPos.z; } else if (multExtractedFromAnim) { neededPos.z += (autoZPos.z - neededPos.z) * zBlend; } else { currentZ = GetPosition().z; if (m_pVehicleAnim && vehAnim != ANIM_STD_VAN_GET_IN_REAR_LHS && vehAnim != ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS && vehAnim != ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS && vehAnim != ANIM_STD_VAN_GET_IN_REAR_RHS) { neededPos.z = autoZPos.z; m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); } else if (neededPos.z < currentZ && m_pVehicleAnim && vehAnim != ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS && vehAnim != ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS) { adjustedTimeStep = Max(m_pVehicleAnim->timeStep, 0.1f); // Smoothly change ped position neededPos.z = currentZ - (currentZ - neededPos.z) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep); } } } else { // We may need to raise up the ped if (phase == LINE_UP_TO_CAR_START) { currentZ = GetPosition().z; if (neededPos.z > currentZ) { if (multExtractedFromAnimBus) { neededPos.z = (neededPos.z - currentZ) * zBlend + currentZ; } else { if (m_pVehicleAnim && (vehAnim == ANIM_STD_CAR_GET_IN_RHS || vehAnim == ANIM_STD_CAR_GET_IN_LO_RHS || vehAnim == ANIM_STD_CAR_GET_IN_LHS || vehAnim == ANIM_STD_CAR_GET_IN_LO_LHS || vehAnim == ANIM_STD_QUICKJACK || vehAnim == ANIM_STD_VAN_GET_IN_REAR_LHS || vehAnim == ANIM_STD_VAN_GET_IN_REAR_RHS)) { adjustedTimeStep = Max(m_pVehicleAnim->timeStep, 0.1f); // Smoothly change ped position neededPos.z = (neededPos.z - currentZ) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep) + currentZ; } else if (EnteringCar() || m_nPedState == PED_DRIVING && veh->IsBike()) { neededPos.z = Max(currentZ, autoZPos.z); } } } } } if (CTimer::GetTimeInMilliseconds() < m_nPedStateTimer) stillGettingInOut = veh->m_vehType != VEHICLE_TYPE_BOAT || bOnBoat; if (!stillGettingInOut) { m_fRotationCur = m_fRotationDest; } else { float limitedDest = CGeneral::LimitRadianAngle(m_fRotationDest); float timeUntilStateChange = (m_nPedStateTimer - CTimer::GetTimeInMilliseconds())/600.0f; if (timeUntilStateChange <= 0.0f) { m_vecOffsetSeek.x = 0.0f; m_vecOffsetSeek.y = 0.0f; } m_vecOffsetSeek.z = 0.0f; neededPos -= timeUntilStateChange * m_vecOffsetSeek; if (PI + m_fRotationCur < limitedDest) { limitedDest -= 2 * PI; } else if (m_fRotationCur - PI > limitedDest) { limitedDest += 2 * PI; } m_fRotationCur -= (m_fRotationCur - limitedDest) * (1.0f - timeUntilStateChange); } if (seatPosMult > 0.2f || vehIsUpsideDown || veh->IsBike()) { SetPosition(neededPos); SetHeading(m_fRotationCur); } else { CMatrix vehDoorMat(veh->GetMatrix()); vehDoorMat.GetPosition() += Multiply3x3(vehDoorMat, GetLocalPositionToOpenCarDoor(veh, m_vehDoor, 0.0f)); if (m_vehDoor == CAR_WINDSCREEN || veh->bIsBus) { CMatrix correctionMat; if (veh->bIsBus && (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR)) correctionMat.SetRotateZ(-HALFPI); else if (veh->bIsBus && (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR)) correctionMat.SetRotateZ(HALFPI); else correctionMat.SetRotateZ(PI); vehDoorMat = vehDoorMat * correctionMat; } GetMatrix() = vehDoorMat; } } void CPed::SetCarJack(CVehicle* car) { uint8 doorFlag; eDoors door; CPed *pedInSeat = nil; if (car->IsBoat()) return; if (car->IsBike()) { switch (m_vehDoor) { case CAR_DOOR_RF: doorFlag = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_RF; door = DOOR_FRONT_RIGHT; pedInSeat = car->pDriver; break; case CAR_DOOR_RR: doorFlag = CAR_DOOR_FLAG_LR | CAR_DOOR_FLAG_RR; door = DOOR_REAR_RIGHT; pedInSeat = car->pPassengers[0]; break; case CAR_DOOR_LF: case CAR_WINDSCREEN: doorFlag = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_RF; door = DOOR_FRONT_LEFT; pedInSeat = car->pDriver; break; case CAR_DOOR_LR: doorFlag = CAR_DOOR_FLAG_LR | CAR_DOOR_FLAG_RR; door = DOOR_REAR_LEFT; pedInSeat = car->pPassengers[0]; break; default: doorFlag = CAR_DOOR_FLAG_UNKNOWN; break; } } else { switch (m_vehDoor) { case CAR_DOOR_RF: doorFlag = CAR_DOOR_FLAG_RF; door = DOOR_FRONT_RIGHT; if (car->pPassengers[0]) { pedInSeat = car->pPassengers[0]; } else if (m_nPedType == PEDTYPE_COP) { pedInSeat = car->pDriver; } break; case CAR_DOOR_RR: doorFlag = CAR_DOOR_FLAG_RR; door = DOOR_REAR_RIGHT; pedInSeat = car->pPassengers[2]; break; case CAR_DOOR_LF: doorFlag = CAR_DOOR_FLAG_LF; door = DOOR_FRONT_LEFT; pedInSeat = car->pDriver; break; case CAR_DOOR_LR: doorFlag = CAR_DOOR_FLAG_LR; door = DOOR_REAR_LEFT; pedInSeat = car->pPassengers[1]; break; default: doorFlag = CAR_DOOR_FLAG_UNKNOWN; break; } } if(car->bIsBus) pedInSeat = car->pDriver; if (m_fHealth > 0.0f && (IsPlayer() || m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS || CharCreatedBy == MISSION_CHAR || (car->VehicleCreatedBy != MISSION_VEHICLE && car->GetModelIndex() != MI_DODO))) if (pedInSeat && !pedInSeat->IsPedDoingDriveByShooting() && pedInSeat->m_nPedState == PED_DRIVING) if (m_nPedState != PED_CARJACK && !m_pVehicleAnim) if ((car->IsDoorReady(door) || car->IsDoorFullyOpen(door))) if (!car->bIsBeingCarJacked && !(doorFlag & car->m_nGettingInFlags) && !(doorFlag & car->m_nGettingOutFlags)) SetCarJack_AllClear(car, m_vehDoor, doorFlag); } void CPed::SetCarJack_AllClear(CVehicle* car, uint32 doorNode, uint32 doorFlag) { if (m_nPedState != PED_SEEK_CAR) SetStoredState(); m_pSeekTarget = car; m_pSeekTarget->RegisterReference((CEntity**)&m_pSeekTarget); SetPedState(PED_CARJACK); car->bIsBeingCarJacked = true; m_pMyVehicle = (CVehicle*)m_pSeekTarget; m_pMyVehicle->RegisterReference((CEntity**)&m_pMyVehicle); ((CVehicle*)m_pSeekTarget)->m_nNumGettingIn++; if (m_nPedType == PEDTYPE_COP) Say(SOUND_PED_ARREST_COP); else if (car->m_nDoorLock == CARLOCK_UNLOCKED) Say(SOUND_PED_CAR_JACKING, 1000); CVector carEnterPos; carEnterPos = GetPositionToOpenCarDoor(car, m_vehDoor); car->m_nGettingInFlags |= doorFlag; m_vecOffsetSeek = carEnterPos - GetPosition(); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 600; if(car->IsBike()){ bUsesCollision = false; PedAnimAlignCB(nil, this); } else { float zDiff = Max(0.0f, carEnterPos.z - GetPosition().z); bUsesCollision = false; if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, zDiff > 4.4f ? ANIM_STD_CAR_ALIGNHI_DOOR_LHS : ANIM_STD_CAR_ALIGN_DOOR_LHS, 4.0f); else m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, zDiff > 4.4f ? ANIM_STD_CAR_ALIGNHI_DOOR_RHS : ANIM_STD_CAR_ALIGN_DOOR_RHS, 4.0f); m_pVehicleAnim->SetFinishCallback(PedAnimAlignCB, this); } } void CPed::SetBeingDraggedFromCar(CVehicle *veh, uint32 vehEnterType, bool quickJack) { if (m_nPedState == PED_DRAG_FROM_CAR) return; bUsesCollision = false; m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); m_nLastPedState = PED_IDLE; SetMoveState(PEDMOVE_STILL); m_pSeekTarget = veh; m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); if (veh->IsBike()) { ((CBike*)veh)->bIsBeingPickedUp = true; if (veh->pPassengers[0] != this && (vehEnterType != CAR_WINDSCREEN || veh->pPassengers[0])) m_vehDoor = CAR_DOOR_LF; else m_vehDoor = CAR_DOOR_LR; } else { m_vehDoor = vehEnterType; } if (m_vehDoor == CAR_DOOR_LF) { if (veh->pDriver && veh->pDriver->IsPlayer()) veh->SetStatus(STATUS_PLAYER_DISABLED); else veh->SetStatus(STATUS_ABANDONED); } RemoveInCarAnims(); SetMoveState(PEDMOVE_NONE); LineUpPedWithCar(veh->IsBike() ? LINE_UP_TO_CAR_FALL : LINE_UP_TO_CAR_START); m_pVehicleAnim = nil; SetPedState(PED_DRAG_FROM_CAR); bChangedSeat = false; bWillBeQuickJacked = quickJack; SetHeading(m_fRotationCur); Say(SOUND_PED_CAR_JACKED); SetRadioStation(); if(veh->IsBike()) veh->m_nGettingOutFlags |= GetBikeDoorFlagInclJumpInFromFront(m_vehDoor); else veh->m_nGettingOutFlags |= GetCarDoorFlag(m_vehDoor); } void CPed::BeingDraggedFromCar(void) { AnimationId enterAnim; bool dontRunAnim = false; if (!m_pVehicleAnim) { CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 1000.0f); AssocGroupId assocGroup; if (m_pMyVehicle && m_pMyVehicle->IsBike()) { enterAnim = ANIM_BIKE_HIT; assocGroup = ((CBike*)m_pMyVehicle)->m_bikeAnimType; } else { if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) { if (bWillBeQuickJacked && m_vehDoor == CAR_DOOR_LF) { enterAnim = ANIM_STD_QUICKJACKED; } else if (m_pMyVehicle->bLowVehicle) { enterAnim = ANIM_STD_JACKEDCAR_LO_LHS; } else { enterAnim = ANIM_STD_JACKEDCAR_LHS; } } else if (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR) { if (m_pMyVehicle->bLowVehicle) enterAnim = ANIM_STD_JACKEDCAR_LO_RHS; else enterAnim = ANIM_STD_JACKEDCAR_RHS; } else dontRunAnim = true; assocGroup = ASSOCGRP_STD; } if (!dontRunAnim) m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), assocGroup, enterAnim); m_pVehicleAnim->SetFinishCallback(PedSetDraggedOutCarCB, this); if (m_pMyVehicle && m_pMyVehicle->IsBike()) { LineUpPedWithCar(LINE_UP_TO_CAR_FALL); } else { LineUpPedWithCar(LINE_UP_TO_CAR_START); } return; } else if (m_pVehicleAnim->animId == ANIM_BIKE_HIT) { LineUpPedWithCar(LINE_UP_TO_CAR_FALL); } else if (m_pVehicleAnim->currentTime <= 1.4f) { m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); LineUpPedWithCar(LINE_UP_TO_CAR_START); } else { LineUpPedWithCar(LINE_UP_TO_CAR_2); } static float mult = 5.f; if (m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) { if (m_pMyVehicle) { m_pMyVehicle->ProcessOpenDoor(m_vehDoor, ANIM_STD_NUM, m_pVehicleAnim->currentTime * mult); } } } void CPed::SetEnterCar(CVehicle *car, uint32 unused) { if (CCranes::IsThisCarBeingCarriedByAnyCrane(car)) { RestorePreviousState(); RestorePreviousObjective(); } else { uint8 doorFlag; eDoors door; if (car->IsBike()) { switch (m_vehDoor) { case CAR_DOOR_RF: doorFlag = CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; door = DOOR_FRONT_RIGHT; break; case CAR_DOOR_RR: doorFlag = CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; door = DOOR_REAR_RIGHT; break; case CAR_WING_LF: case CAR_WING_LR: case CAR_BONNET: case CAR_BOOT: doorFlag = CAR_DOOR_FLAG_UNKNOWN; break; case CAR_DOOR_LF: case CAR_WINDSCREEN: doorFlag = CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; door = DOOR_FRONT_LEFT; break; case CAR_DOOR_LR: doorFlag = CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; door = DOOR_REAR_LEFT; break; } } else { switch (m_vehDoor) { case CAR_DOOR_RF: doorFlag = CAR_DOOR_FLAG_RF; door = DOOR_FRONT_RIGHT; break; case CAR_DOOR_RR: doorFlag = CAR_DOOR_FLAG_RR; door = DOOR_REAR_RIGHT; break; case CAR_DOOR_LF: if(car->m_nNumMaxPassengers != 0) doorFlag = CAR_DOOR_FLAG_LF; else doorFlag = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; door = DOOR_FRONT_LEFT; break; case CAR_DOOR_LR: if (car->m_nNumMaxPassengers != 0) doorFlag = CAR_DOOR_FLAG_LR; else doorFlag = CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; door = DOOR_REAR_LEFT; break; default: doorFlag = CAR_DOOR_FLAG_UNKNOWN; break; } } if (!IsPedInControl() || m_fHealth <= 0.0f || doorFlag & car->m_nGettingInFlags || doorFlag & car->m_nGettingOutFlags || car->bIsBeingCarJacked || m_pVehicleAnim || doorFlag && !car->IsDoorReady(door) && !car->IsDoorFullyOpen(door)) SetMoveState(PEDMOVE_STILL); else SetEnterCar_AllClear(car, m_vehDoor, doorFlag); } } void CPed::SetEnterCar_AllClear(CVehicle *car, uint32 doorNode, uint32 doorFlag) { float zDiff = 0.0f; car->m_nGettingInFlags |= doorFlag; bVehEnterDoorIsBlocked = false; if (m_nPedState != PED_SEEK_CAR && m_nPedState != PED_SEEK_IN_BOAT) SetStoredState(); m_pSeekTarget = car; m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); m_vehDoor = doorNode; SetPedState(PED_ENTER_CAR); if (m_vehDoor == CAR_DOOR_RF && m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER && !car->IsBike()) { car->bIsBeingCarJacked = true; } m_pMyVehicle = (CVehicle*)m_pSeekTarget; m_pMyVehicle->RegisterReference((CEntity**) &m_pMyVehicle); ((CVehicle*)m_pSeekTarget)->m_nNumGettingIn++; bUsesCollision = false; CVector doorOpenPos = GetPositionToOpenCarDoor(car, m_vehDoor); // Because buses have stairs if (!m_pMyVehicle->bIsBus) zDiff = Max(0.0f, doorOpenPos.z - GetPosition().z); m_vecOffsetSeek = doorOpenPos - GetPosition(); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 600; if (car->IsBoat()) { if (car->pHandling->Flags & HANDLING_SIT_IN_BOAT) m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT, 100.0f); else m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_DRIVE, 100.0f); PedSetInCarCB(nil, this); bVehExitWillBeInstant = true; } else if (car->IsBike()) { PedAnimAlignCB(0, this); car->AutoPilot.m_nCruiseSpeed = 0; } else { if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, zDiff > 4.4f ? ANIM_STD_CAR_ALIGNHI_DOOR_RHS : ANIM_STD_CAR_ALIGN_DOOR_RHS, 4.0f); else m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, zDiff > 4.4f ? ANIM_STD_CAR_ALIGNHI_DOOR_LHS : ANIM_STD_CAR_ALIGN_DOOR_LHS, 4.0f); m_pVehicleAnim->SetFinishCallback(PedAnimAlignCB, this); } } void CPed::EnterCar(void) { if (IsNotInWreckedVehicle() && m_fHealth > 0.0f) { CVehicle *veh = m_pMyVehicle; // Not used. // CVector posForDoor = GetPositionToOpenCarDoor(veh, m_vehDoor); if (veh->CanPedOpenLocks(this)) { if (m_vehDoor && m_pVehicleAnim) { veh->ProcessOpenDoor(m_vehDoor, m_pVehicleAnim->animId, m_pVehicleAnim->currentTime); } } bIsInTheAir = false; LineUpPedWithCar(LINE_UP_TO_CAR_START); if (veh->IsBike()) { CBike *bike = (CBike*)veh; if (bike->GetStatus() == STATUS_ABANDONED && !bike->bIsBeingPickedUp && m_pVehicleAnim) { int anim = m_pVehicleAnim->animId; // One is pickup and other one is pullup, not same :p if ((anim == ANIM_STD_BIKE_PICKUP_LHS || anim == ANIM_STD_BIKE_PICKUP_RHS) && m_pVehicleAnim->currentTime > 0.4667f) bike->bIsBeingPickedUp = true; else if ((anim == ANIM_STD_BIKE_PULLUP_LHS || anim == ANIM_STD_BIKE_PULLUP_RHS) && m_pVehicleAnim->currentTime > 0.4667f) bike->bIsBeingPickedUp = true; } else if (m_nPedState == PED_CARJACK && m_pVehicleAnim) { if (m_pVehicleAnim->currentTime > 0.4f && m_pVehicleAnim->currentTime - m_pVehicleAnim->timeStep <= 0.4f) { int anim = m_pVehicleAnim->animId; if (anim == ANIM_BIKE_KICK) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_187, 3.0f); } else if (anim == ANIM_STD_BIKE_ELBOW_LHS || anim == ANIM_STD_BIKE_ELBOW_RHS) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_186, 3.0f); } } } } } else { QuitEnteringCar(); SetDie(); } } void CPed::QuitEnteringCar(void) { CVehicle *veh = m_pMyVehicle; if (m_pVehicleAnim) m_pVehicleAnim->blendDelta = -1000.0f; RestartNonPartialAnims(); if (!RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE)) CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 100.0f); if (veh) { if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_nPedState == PED_CARJACK) veh->bIsBeingCarJacked = false; if (veh->m_nNumGettingIn != 0) veh->m_nNumGettingIn--; if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) RestorePreviousObjective(); if (veh->IsBike()) { veh->m_nGettingInFlags &= ~GetBikeDoorFlag(m_vehDoor); ((CBike*)veh)->bIsBeingPickedUp = false; } else veh->m_nGettingInFlags &= ~GetEnterCarDoorFlag(m_vehDoor, veh->m_nNumMaxPassengers); } bUsesCollision = true; if (DyingOrDead()) { if (m_pVehicleAnim) { m_pVehicleAnim->blendDelta = -4.0f; m_pVehicleAnim->flags |= ASSOC_DELETEFADEDOUT; m_pVehicleAnim->flags &= ~ASSOC_RUNNING; } } else SetIdle(); m_pVehicleAnim = nil; if (veh) { if (veh->AutoPilot.m_nCruiseSpeed == 0 && veh->VehicleCreatedBy == RANDOM_VEHICLE) veh->AutoPilot.m_nCruiseSpeed = 17; } } void CPed::SetExitBoat(CVehicle *boat) { SetPedState(PED_IDLE); CVector newPos = GetPosition(); RemoveInCarAnims(); CColModel* boatCol = boat->GetColModel(); if (boat->IsUpsideDown()) { newPos = CVector(0.0f, 0.0f, boatCol->boundingBox.min.z); newPos = boat->GetMatrix() * newPos; newPos.z += 1.0f; m_vehDoor = CAR_DOOR_RF; PedSetOutCarCB(nil, this); bIsStanding = true; m_pCurSurface = boat; m_pCurSurface->RegisterReference((CEntity**)&m_pCurSurface); m_pCurrentPhysSurface = boat; } else { if (boat->m_modelIndex == MI_SKIMMER) { if (!boat->bIsInWater) { m_vehDoor = CAR_DOOR_RF; PedSetOutCarCB(nil, this); bIsStanding = true; SetMoveState(PEDMOVE_STILL); bTryingToReachDryLand = true; float upMult = 1.04f + boatCol->boundingBox.min.z; float rightMult = 0.6f * boatCol->boundingBox.max.x; newPos = upMult * boat->GetUp() + rightMult * boat->GetRight() + boat->GetPosition(); SetPosition(newPos); if (m_pMyVehicle) { PositionPedOutOfCollision(); } else { m_pMyVehicle = boat; PositionPedOutOfCollision(); m_pMyVehicle = nil; } return; } newPos.z += 2.0f; } m_vehDoor = CAR_DOOR_RF; PedSetOutCarCB(nil, this); bIsStanding = true; m_pCurSurface = boat; m_pCurSurface->RegisterReference((CEntity**)&m_pCurSurface); m_pCurrentPhysSurface = boat; CColPoint foundCol; CEntity *foundEnt = nil; if (CWorld::ProcessVerticalLine(newPos, newPos.z - 1.4f, foundCol, foundEnt, false, true, false, false, false, false, nil)) newPos.z = FEET_OFFSET + foundCol.point.z; } SetPosition(newPos); SetMoveState(PEDMOVE_STILL); m_vecMoveSpeed = boat->m_vecMoveSpeed; } // wantedDoorNode = 0 means that func. will determine it void CPed::SetExitCar(CVehicle *veh, uint32 wantedDoorNode) { uint32 optedDoorNode = wantedDoorNode; bool teleportNeeded = false; bool isLow = !!veh->bLowVehicle; bool canJumpOut = false; if (!veh->CanPedExitCar(false) && !bBusJacked) { if (IsPlayer()) { canJumpOut = veh->IsBike() ? veh->CanPedJumpOffBike() : veh->CanPedJumpOutCar(); } if (!canJumpOut) { if (veh->pDriver) { if (veh->pDriver->IsPlayer()) { if (veh->pDriver != this) { m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 5000; bHeldHostageInCar = true; } } else { veh->AutoPilot.m_nCruiseSpeed = 0; veh->AutoPilot.m_nCarMission = MISSION_NONE; } } return; } } if (m_nPedState == PED_EXIT_CAR || m_nPedState == PED_DRAG_FROM_CAR) return; m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); if (wantedDoorNode == 0) { optedDoorNode = CAR_DOOR_LF; if (veh->IsBike()) { if (canJumpOut) { optedDoorNode = veh->pPassengers[0] == this ? CAR_BUMP_REAR : CAR_BOOT; } else if (veh->pPassengers[0] == this) { optedDoorNode = CAR_DOOR_LR; } else { optedDoorNode = CAR_DOOR_LF; } } else if (veh->bIsBus) { optedDoorNode = CAR_DOOR_LF; } else if (veh->pDriver == this) { optedDoorNode = CAR_DOOR_LF; } else if (veh->pPassengers[0] == this) { optedDoorNode = CAR_DOOR_RF; } else if (veh->pPassengers[1] == this) { optedDoorNode = CAR_DOOR_LR; } else if (veh->pPassengers[2] == this) { optedDoorNode = CAR_DOOR_RR; } else { for (int i = 3; i < veh->m_nNumMaxPassengers; ++i) { if (veh->pPassengers[i] == this) { if (i & 1) optedDoorNode = CAR_DOOR_RR; else optedDoorNode = CAR_DOOR_LR; break; } } } } bool someoneExitsFromOurExitDoor = false; bool someoneEntersFromOurExitDoor = false; if (veh->IsBike()) { switch (optedDoorNode) { case CAR_BUMP_REAR: case CAR_DOOR_RR: case CAR_DOOR_LR: if (veh->m_nGettingInFlags & (CAR_DOOR_FLAG_LR | CAR_DOOR_FLAG_RR)) someoneEntersFromOurExitDoor = true; break; case CAR_DOOR_RF: case CAR_DOOR_LF: case CAR_BOOT: if (veh->m_nGettingInFlags & (CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_RF)) someoneEntersFromOurExitDoor = true; break; default: break; } } else { switch (optedDoorNode) { case CAR_DOOR_RF: if (veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF) someoneEntersFromOurExitDoor = true; if (veh->m_nGettingOutFlags & CAR_DOOR_FLAG_RF) someoneExitsFromOurExitDoor = true; break; case CAR_DOOR_RR: if (veh->m_nGettingInFlags & CAR_DOOR_FLAG_RR) someoneEntersFromOurExitDoor = true; if (veh->m_nGettingOutFlags & CAR_DOOR_FLAG_RR) someoneExitsFromOurExitDoor = true; break; case CAR_DOOR_LF: if (veh->m_nGettingInFlags & CAR_DOOR_FLAG_LF) someoneEntersFromOurExitDoor = true; if (veh->m_nGettingOutFlags & CAR_DOOR_FLAG_LF) someoneExitsFromOurExitDoor = true; break; case CAR_DOOR_LR: if (veh->m_nGettingInFlags & CAR_DOOR_FLAG_LR) someoneEntersFromOurExitDoor = true; if (veh->m_nGettingOutFlags & CAR_DOOR_FLAG_LR) someoneExitsFromOurExitDoor = true; break; default: break; } } if (someoneEntersFromOurExitDoor && m_objective == OBJECTIVE_LEAVE_CAR) { RestorePreviousObjective(); return; } if (!someoneExitsFromOurExitDoor || m_nPedType == PEDTYPE_COP && veh->bIsBus) { #if defined GTAVC_JP_PATCH || defined FIX_BUGS if (veh->pDriver == this && !IsPlayer() && veh == CGameLogic::pShortCutTaxi) { m_objective = OBJECTIVE_NONE; return; } #endif bool thereIsRoom; if (canJumpOut) { thereIsRoom = 1; } else { // Again, unused... // CVector exitPos = GetPositionToOpenCarDoor(veh, optedDoorNode); thereIsRoom = veh->IsRoomForPedToLeaveCar(optedDoorNode, nil); } if (!thereIsRoom) { bool trySideSeat = false; CPed *pedOnSideSeat; int firstOptedDoor = optedDoorNode; if (veh->IsBike()) { switch (optedDoorNode) { case CAR_DOOR_RF: optedDoorNode = CAR_DOOR_LF; break; case CAR_DOOR_RR: optedDoorNode = CAR_DOOR_LR; break; case CAR_DOOR_LF: optedDoorNode = CAR_DOOR_RF; break; case CAR_DOOR_LR: optedDoorNode = CAR_DOOR_RR; break; default: break; } } else { switch (optedDoorNode) { case CAR_DOOR_RF: if (veh->pDriver || veh->m_nGettingInFlags & CAR_DOOR_FLAG_LF) { pedOnSideSeat = veh->pDriver; trySideSeat = true; } else optedDoorNode = CAR_DOOR_LF; break; case CAR_DOOR_RR: if (veh->pPassengers[1] || veh->m_nGettingInFlags & CAR_DOOR_FLAG_LR) { pedOnSideSeat = veh->pPassengers[1]; trySideSeat = true; } else optedDoorNode = CAR_DOOR_LR; break; case CAR_DOOR_LF: if (veh->pPassengers[0] || veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF) { pedOnSideSeat = veh->pPassengers[0]; trySideSeat = true; } else optedDoorNode = CAR_DOOR_RF; break; case CAR_DOOR_LR: if (veh->pPassengers[2] || veh->m_nGettingInFlags & CAR_DOOR_FLAG_RR) { pedOnSideSeat = (CPed*)veh->pPassengers[2]; trySideSeat = true; } else optedDoorNode = CAR_DOOR_RR; break; default: break; } } if (trySideSeat) { if (!pedOnSideSeat || !IsPlayer() && CharCreatedBy != MISSION_CHAR) return; switch (optedDoorNode) { case CAR_DOOR_RF: optedDoorNode = CAR_DOOR_LF; break; case CAR_DOOR_RR: optedDoorNode = CAR_DOOR_LR; break; case CAR_DOOR_LF: optedDoorNode = CAR_DOOR_RF; break; case CAR_DOOR_LR: optedDoorNode = CAR_DOOR_RR; break; default: break; } } // ... // CVector exitPos = GetPositionToOpenCarDoor(veh, optedDoorNode); if (!veh->IsRoomForPedToLeaveCar(optedDoorNode, nil)) { if (!IsPlayer() && CharCreatedBy != MISSION_CHAR) return; // needed for PositionPedOutOfCollision() optedDoorNode = firstOptedDoor; m_vehDoor = firstOptedDoor; PositionPedOutOfCollision(); teleportNeeded = true; } } if (!teleportNeeded && veh->IsOnItsSide()) { m_vehDoor = optedDoorNode; PositionPedOutOfCollision(); teleportNeeded = true; } if (m_nPedState == PED_FLEE_POS) { m_nLastPedState = PED_FLEE_POS; m_nPrevMoveState = PEDMOVE_RUN; SetMoveState(PEDMOVE_SPRINT); } else { m_nLastPedState = PED_IDLE; m_nPrevMoveState = PEDMOVE_STILL; SetMoveState(PEDMOVE_STILL); } bUsesCollision = false; m_pSeekTarget = veh; m_pSeekTarget->RegisterReference((CEntity**) &m_pSeekTarget); m_vehDoor = optedDoorNode; SetPedState(PED_EXIT_CAR); if (m_pVehicleAnim && m_pVehicleAnim->flags & ASSOC_PARTIAL) m_pVehicleAnim->blendDelta = -1000.0f; RemoveInCarAnims(); SetMoveState(PEDMOVE_NONE); CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 100.0f); veh->AutoPilot.m_nCruiseSpeed = 0; if (teleportNeeded) { PedSetOutCarCB(nil, this); } else { if (veh->GetUp().z <= -0.8f && !veh->IsBike()) { if (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CRAWLOUT_RHS); } else if (m_vehDoor == CAR_DOOR_LF || m_vehDoor == CAR_DOOR_LR) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CRAWLOUT_LHS); } m_pVehicleAnim->SetFinishCallback(PedSetOutCarCB, this); } else if (veh->IsBike()) { CBike* bike = (CBike*)veh; switch (m_vehDoor) { case CAR_BUMP_REAR: case CAR_BOOT: m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_GETOFF_BACK); break; case CAR_DOOR_RF: case CAR_DOOR_RR: m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_GETOFF_RHS); break; case CAR_DOOR_LF: case CAR_DOOR_LR: m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_GETOFF_LHS); break; default: break; } int8 exitFlags = 0; // Bike door flags incl. passenger jump off switch (m_vehDoor) { case CAR_BUMP_REAR: case CAR_DOOR_RR: case CAR_DOOR_LR: exitFlags = CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; break; case CAR_DOOR_RF: case CAR_DOOR_LF: case CAR_BOOT: exitFlags = CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; break; } // Passenger get off if (m_vehDoor == CAR_BUMP_REAR || m_vehDoor == CAR_BOOT) { m_pVehicleAnim->SetFinishCallback(RestoreHeadingRateCB, this); m_headingRate = 0.0f; } else { veh->m_nGettingOutFlags |= exitFlags; m_pVehicleAnim->SetFinishCallback(PedAnimStepOutCarCB, this); } } else { switch (m_vehDoor) { case CAR_DOOR_RF: if (canJumpOut) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROLLOUT_RHS); } else if (veh->bIsBus) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_OUT_LHS); } else { if (isLow) m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LO_RHS); else m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_RHS); } break; case CAR_DOOR_RR: if (canJumpOut) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROLLOUT_RHS); } else if (veh->bIsVan) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_OUT_REAR_RHS); } else if (isLow) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LO_RHS); } else { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_RHS); } break; case CAR_DOOR_LF: if (canJumpOut) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROLLOUT_LHS); } else if (veh->bIsBus) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_COACH, ANIM_STD_COACH_GET_OUT_LHS); } else { if (isLow) m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LO_LHS); else m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LHS); } break; case CAR_DOOR_LR: if (canJumpOut) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROLLOUT_LHS); } else if (veh->bIsVan) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_VAN, ANIM_STD_VAN_GET_OUT_REAR_LHS); } else if (isLow) { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LO_LHS); } else { m_pVehicleAnim = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_GETOUT_LHS); } break; default: break; } if (!bBusJacked && !canJumpOut) { veh->m_nGettingOutFlags |= GetCarDoorFlag(m_vehDoor); } m_pVehicleAnim->SetFinishCallback(canJumpOut ? RestoreHeadingRateCB : PedAnimStepOutCarCB, this); } } bChangedSeat = false; if (veh->bIsBus) bRenderPedInCar = true; SetRadioStation(); if (veh->pDriver == this) { if (IsPlayer()) veh->SetStatus(STATUS_PLAYER_DISABLED); else veh->SetStatus(STATUS_ABANDONED); } } } void CPed::ExitCar(void) { if (!m_pVehicleAnim) { if (InVehicle()) { if (m_pMyVehicle->IsBike()) { if (m_vehDoor == CAR_BOOT || m_vehDoor == CAR_BUMP_REAR) { ((CBike*)m_pMyVehicle)->KnockOffRider(WEAPONTYPE_UNARMED, 0, this, false); } } else if (m_pMyVehicle->IsCar()) { if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_LHS)) { ((CAutomobile*)m_pMyVehicle)->KnockPedOutCar(WEAPONTYPE_UNIDENTIFIED, CAR_DOOR_LF, this); } else if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_RHS)) { ((CAutomobile*)m_pMyVehicle)->KnockPedOutCar(WEAPONTYPE_UNIDENTIFIED, CAR_DOOR_RF, this); } } } return; } AnimationId exitAnim = (AnimationId) m_pVehicleAnim->animId; float animTime = m_pVehicleAnim->currentTime; if (exitAnim == ANIM_BIKE_GETOFF_BACK) { if (animTime > 0.35f && m_pMyVehicle && m_pMyVehicle->IsBike()) ((CBike*)m_pMyVehicle)->KnockOffRider(WEAPONTYPE_UNARMED, 0, this, false); else LineUpPedWithCar(LINE_UP_TO_CAR_FALL); } else if (exitAnim == ANIM_STD_ROLLOUT_LHS || exitAnim == ANIM_STD_ROLLOUT_RHS) { if (animTime > 0.07f && m_pMyVehicle && m_pMyVehicle->IsCar()) { if (exitAnim == ANIM_STD_ROLLOUT_LHS) { ((CAutomobile*)m_pMyVehicle)->KnockPedOutCar(WEAPONTYPE_UNIDENTIFIED, CAR_DOOR_LF, this); } else { ((CAutomobile*)m_pMyVehicle)->KnockPedOutCar(WEAPONTYPE_UNIDENTIFIED, CAR_DOOR_RF, this); } } else { LineUpPedWithCar(LINE_UP_TO_CAR_FALL); } } else { m_pMyVehicle->ProcessOpenDoor(m_vehDoor, exitAnim, animTime); if (m_pSeekTarget) { // Car is upside down if (m_pMyVehicle->GetUp().z > -0.8f) { if (exitAnim == ANIM_STD_CAR_CLOSE_RHS || exitAnim == ANIM_STD_CAR_CLOSE_LHS || animTime > 0.3f) LineUpPedWithCar(LINE_UP_TO_CAR_END); else LineUpPedWithCar((m_pMyVehicle->GetModelIndex() == MI_DODO ? LINE_UP_TO_CAR_END : LINE_UP_TO_CAR_START)); } else { LineUpPedWithCar(LINE_UP_TO_CAR_END); } } // If there is someone in front of the door, make him fall while we exit. if (m_nPedState == PED_EXIT_CAR) { CPed* foundPed = nil; for (int i = 0; i < m_numNearPeds; i++) { if ((m_nearPeds[i]->GetPosition() - GetPosition()).MagnitudeSqr2D() < SQR(0.2f)) { foundPed = m_nearPeds[i]; break; } } if(foundPed && (!foundPed->IsPlayer() || m_nPedType == PEDTYPE_COP || m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS)) if (animTime > 0.4f && foundPed->IsPedInControl()) foundPed->SetFall(1000, ANIM_STD_HIGHIMPACT_FRONT, 1); } } } CVector CPed::GetPositionToOpenCarDoor(CVehicle *veh, uint32 component) { CVector vehDoorPos = GetPositionToOpenCarDoor(veh, component, 1.0f); /* // Unused vehDoorPosWithoutOffset = veh->GetMatrix() * localVehDoorPos; */ return vehDoorPos; } void CPed::GetNearestDoor(CVehicle *veh, CVector &posToOpen) { CVector *enterOffset = nil; if (veh->IsBike()) { if (m_objective != OBJECTIVE_ENTER_CAR_AS_PASSENGER) { // If bike didn't fall to ground if (Abs(veh->GetRight().z) < 0.1f) { float angleDiff = (GetPosition() - veh->GetPosition()).Heading() - veh->GetForward().Heading(); if (angleDiff > PI) angleDiff -= TWOPI; else if (angleDiff < -PI) angleDiff += TWOPI; if (Abs(angleDiff) < DEGTORAD(30.f) && (IsPlayer() && ((CPlayerPed*)this)->m_fMoveSpeed > 1.5f && !m_vehDoor || !IsPlayer() && m_nPedType != PEDTYPE_COP && m_nMoveState == PEDMOVE_RUN && m_pedStats->m_temper > 65 && !m_vehDoor || m_vehDoor == CAR_WINDSCREEN)) { m_vehDoor = CAR_WINDSCREEN; posToOpen = GetPositionToOpenCarDoor(veh, CAR_WINDSCREEN); return; } } } } else if (m_vehDoor == CAR_DOOR_LF && veh->pDriver && !veh->bLowVehicle && !veh->bIsBus) { enterOffset = &vecPedQuickDraggedOutCarAnimOffset; } CVector lfPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_LF); CVector rfPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RF); // Left front door is closer if ((lfPos - GetPosition()).MagnitudeSqr2D() < (rfPos - GetPosition()).MagnitudeSqr2D()) { if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset)) { m_vehDoor = CAR_DOOR_LF; posToOpen = lfPos; } else if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_RF, enterOffset)) { m_vehDoor = CAR_DOOR_RF; posToOpen = rfPos; } } else { if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_RF, enterOffset)) { CPed *rfPassenger = veh->pPassengers[0]; if (rfPassenger && !veh->IsBike() && (rfPassenger->m_leader == this || rfPassenger->bDontDragMeOutCar || veh->VehicleCreatedBy == MISSION_VEHICLE && m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) && veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset) || (veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF) && veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset)) { m_vehDoor = CAR_DOOR_LF; posToOpen = lfPos; } else { m_vehDoor = CAR_DOOR_RF; posToOpen = rfPos; } } else if (veh->IsRoomForPedToLeaveCar(CAR_DOOR_LF, enterOffset)) { m_vehDoor = CAR_DOOR_LF; posToOpen = lfPos; } } } bool CPed::GetNearestPassengerDoor(CVehicle *veh, CVector &posToOpen) { CVector rfPos, lrPos, rrPos; bool canEnter = false; CVehicleModelInfo *vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(veh->GetModelIndex()); switch (veh->GetModelIndex()) { case MI_BUS: m_vehDoor = CAR_DOOR_RF; posToOpen = GetPositionToOpenCarDoor(veh, CAR_DOOR_RF); return true; case MI_RHINO: default: break; } CVector2D rfPosDist(999.0f, 999.0f); CVector2D lrPosDist(999.0f, 999.0f); CVector2D rrPosDist(999.0f, 999.0f); if (veh->IsBike()) { if (!veh->pPassengers[0] && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_LR) && veh->IsRoomForPedToLeaveCar(CAR_DOOR_LR, nil)) { lrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_LR); canEnter = true; lrPosDist = lrPos - GetPosition(); } if (!veh->pPassengers[0] && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_RR) && veh->IsRoomForPedToLeaveCar(CAR_DOOR_RR, nil)) { rrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RR); canEnter = true; rrPosDist = rrPos - GetPosition(); } } else if (!veh->pPassengers[0] && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_RF) && veh->IsRoomForPedToLeaveCar(CAR_DOOR_RF, nil)) { rfPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RF); canEnter = true; rfPosDist = rfPos - GetPosition(); } if (vehModel->m_numDoors == 4) { if (!veh->pPassengers[1] && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_LR) && veh->IsRoomForPedToLeaveCar(CAR_DOOR_LR, nil)) { lrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_LR); canEnter = true; lrPosDist = lrPos - GetPosition(); } if (!veh->pPassengers[2] && !(veh->m_nGettingInFlags & CAR_DOOR_FLAG_RR) && veh->IsRoomForPedToLeaveCar(CAR_DOOR_RR, nil)) { rrPos = GetPositionToOpenCarDoor(veh, CAR_DOOR_RR); canEnter = true; rrPosDist = rrPos - GetPosition(); } // When the door we should enter is blocked by some object. if (!canEnter) veh->ShufflePassengersToMakeSpace(); } CVector2D nextToCompare = rfPosDist; posToOpen = rfPos; m_vehDoor = CAR_DOOR_RF; if (lrPosDist.MagnitudeSqr() < nextToCompare.MagnitudeSqr()) { m_vehDoor = CAR_DOOR_LR; posToOpen = lrPos; nextToCompare = lrPosDist; } if (rrPosDist.MagnitudeSqr() < nextToCompare.MagnitudeSqr()) { m_vehDoor = CAR_DOOR_RR; posToOpen = rrPos; } return canEnter; } void CPed::GoToNearestDoor(CVehicle *veh) { CVector posToOpen; GetNearestDoor(veh, posToOpen); SetSeek(posToOpen, 0.5f); SetMoveState(PEDMOVE_RUN); } // Unused void CPed::PedSetGetInCarPositionCB(CAnimBlendAssociation* assoc, void* arg) { CPed* pPed = (CPed*)arg; CMatrix mat(pPed->GetMatrix()); CVehicle* pVehicle = pPed->m_pMyVehicle; const CVector& offset = (pVehicle->bIsVan && (pPed->m_vehDoor == CAR_DOOR_RR || pPed->m_vehDoor == CAR_DOOR_LR)) ? vecPedVanRearDoorAnimOffset : vecPedCarDoorAnimOffset; CVector position = Multiply3x3(mat, offset) + pPed->GetPosition(); CPedPlacement::FindZCoorForPed(&position); pPed->SetMoveSpeed(0.0f, 0.0f, 0.0f); pPed->SetPosition(position); } void CPed::SetAnimOffsetForEnterOrExitVehicle(void) { // FIX_BUGS: If there were no translations on enter anims, there were overflows all over this function. int vanBlock = CAnimManager::GetAnimationBlockIndex("van"); int bikesBlock = CAnimManager::GetAnimationBlockIndex("bikes"); int bikevBlock = CAnimManager::GetAnimationBlockIndex("bikev"); int bikehBlock = CAnimManager::GetAnimationBlockIndex("bikeh"); int bikedBlock = CAnimManager::GetAnimationBlockIndex("biked"); CStreaming::RequestAnim(vanBlock, STREAMFLAGS_DEPENDENCY); CStreaming::RequestAnim(bikesBlock, STREAMFLAGS_DEPENDENCY); CStreaming::RequestAnim(bikevBlock, STREAMFLAGS_DEPENDENCY); CStreaming::RequestAnim(bikehBlock, STREAMFLAGS_DEPENDENCY); CStreaming::RequestAnim(bikedBlock, STREAMFLAGS_DEPENDENCY); CStreaming::LoadAllRequestedModels(false); CAnimManager::AddAnimBlockRef(vanBlock); CAnimManager::AddAnimBlockRef(bikesBlock); CAnimManager::AddAnimBlockRef(bikevBlock); CAnimManager::AddAnimBlockRef(bikehBlock); CAnimManager::AddAnimBlockRef(bikedBlock); CAnimBlendHierarchy *enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_JACKEDCAR_LHS)->hierarchy; CAnimBlendSequence *seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedDraggedOutCarAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans *lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedDraggedOutCarAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LHS)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedCarDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans *lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedCarDoorAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_CAR_GET_IN_LO_LHS)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedCarDoorLoAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans *lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedCarDoorLoAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_QUICKJACKED)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedQuickDraggedOutCarAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans *lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedQuickDraggedOutCarAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_VAN, ANIM_STD_VAN_GET_IN_REAR_LHS)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedVanRearDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans *lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedVanRearDoorAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, ANIM_STD_TRAIN_GETOUT)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedTrainDoorAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans *lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedTrainDoorAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_STANDARD, ANIM_BIKE_JUMPON_LHS)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedStdBikeJumpRhsAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedStdBikeJumpRhsAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_VESPA, ANIM_BIKE_JUMPON_LHS)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedVespaBikeJumpRhsAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedVespaBikeJumpRhsAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_HARLEY, ANIM_BIKE_JUMPON_LHS)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedHarleyBikeJumpRhsAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedHarleyBikeJumpRhsAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_DIRT, ANIM_BIKE_JUMPON_LHS)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedDirtBikeJumpRhsAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedDirtBikeJumpRhsAnimOffset = lastFrame->translation; } } enterAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_BIKE_HARLEY, ANIM_BIKE_KICK)->hierarchy; seq = enterAssoc->sequences; CAnimManager::UncompressAnimation(enterAssoc); if (seq->numFrames > 0) { if (!seq->HasTranslation()) vecPedBikeKickAnimOffset = CVector(0.0f, 0.0f, 0.0f); else { KeyFrameTrans* lastFrame = (KeyFrameTrans*)seq->GetKeyFrame(seq->numFrames - 1); vecPedBikeKickAnimOffset = lastFrame->translation; } } CAnimManager::RemoveAnimBlockRef(vanBlock); CAnimManager::RemoveAnimBlockRef(bikesBlock); CAnimManager::RemoveAnimBlockRef(bikevBlock); CAnimManager::RemoveAnimBlockRef(bikehBlock); CAnimManager::RemoveAnimBlockRef(bikedBlock); } void CPed::PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; CVehicle *veh = ped->m_pMyVehicle; CVector finalPos; CVector draggedOutOffset; CMatrix pedMat(ped->GetMatrix()); ped->bUsesCollision = true; ped->RestartNonPartialAnims(); draggedOutOffset = vecPedQuickDraggedOutCarAnimOffset; if (ped->m_vehDoor == CAR_DOOR_RF || ped->m_vehDoor == CAR_DOOR_RR) draggedOutOffset.x = -draggedOutOffset.x; finalPos = Multiply3x3(pedMat, draggedOutOffset) + ped->GetPosition(); CPedPlacement::FindZCoorForPed(&finalPos); ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); ped->SetPosition(finalPos); if (veh) { ped->m_fRotationDest = veh->GetForward().Heading() - HALFPI; ped->m_fRotationCur = ped->m_fRotationDest; ped->CalculateNewOrientation(); if (!veh->IsRoomForPedToLeaveCar(ped->m_vehDoor, &vecPedQuickDraggedOutCarAnimOffset)) ped->PositionPedOutOfCollision(); } if (!ped->CanSetPedState()) return; ped->SetIdle(); if (veh) { if (ped->bFleeAfterExitingCar) { ped->bFleeAfterExitingCar = false; ped->SetFlee(veh->GetPosition(), 14000); } else if (ped->bWanderPathAfterExitingCar) { ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); ped->bWanderPathAfterExitingCar = false; } else if (ped->bGonnaKillTheCarJacker) { ped->bGonnaKillTheCarJacker = false; if (ped->m_pedInObjective && CGeneral::GetRandomNumber() & 1) { if (ped->m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) ped->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, ped->m_pedInObjective); } else { CPed *driver = veh->pDriver; if (!driver || driver == ped || driver->IsPlayer() && CTheScripts::IsPlayerOnAMission()) { ped->SetFlee(veh->GetPosition(), 14000); } else { ped->ClearObjective(); ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, veh); } ped->bUsePedNodeSeek = true; ped->m_pNextPathNode = nil; ped->Say(SOUND_PED_FLEE_RUN); } } else if (ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && ped->CharCreatedBy != MISSION_CHAR && veh->VehicleCreatedBy != MISSION_VEHICLE && veh->pDriver && veh->pDriver->IsPlayer() && !CTheScripts::IsPlayerOnAMission()) { ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, veh); } else if (ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && ped->CharCreatedBy != MISSION_CHAR && veh->VehicleCreatedBy != MISSION_VEHICLE && !veh->pDriver && FindPlayerPed()->m_carInObjective == veh && !CTheScripts::IsPlayerOnAMission()) { ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, veh); } else { ped->SetFindPathAndFlee(veh->GetPosition(), 10000); if (CGeneral::GetRandomNumber() & 1 || ped->m_pedStats->m_fear > 70) { ped->SetMoveState(PEDMOVE_SPRINT); ped->Say(SOUND_PED_FLEE_SPRINT); } else { ped->Say(SOUND_PED_FLEE_RUN); } } } if (ped->m_nLastPedState == PED_IDLE) ped->m_nLastPedState = PED_WANDER_PATH; } void CPed::PedSetDraggedOutCarPositionCB(CAnimBlendAssociation* animAssoc, void* arg) { CPed *ped = (CPed*)arg; ped->bUsesCollision = true; ped->RestartNonPartialAnims(); CMatrix pedMat(ped->GetMatrix()); CVector draggedOutOffset; if (ped->m_pMyVehicle && ped->m_pMyVehicle->IsBike()) { AssocGroupId animGroup = ((CBike*)ped->m_pMyVehicle)->m_bikeAnimType; switch (animGroup) { case ASSOCGRP_BIKE_VESPA: draggedOutOffset = vecPedVespaBikeJumpRhsAnimOffset; break; case ASSOCGRP_BIKE_HARLEY: draggedOutOffset = vecPedHarleyBikeJumpRhsAnimOffset; break; case ASSOCGRP_BIKE_DIRT: draggedOutOffset = vecPedDirtBikeJumpRhsAnimOffset; break; default: draggedOutOffset = vecPedStdBikeJumpRhsAnimOffset; break; } } else { draggedOutOffset = vecPedDraggedOutCarAnimOffset; } if (ped->m_vehDoor == CAR_DOOR_RF || ped->m_vehDoor == CAR_DOOR_RR) draggedOutOffset.x = -draggedOutOffset.x; CVector posAfterBeingDragged = Multiply3x3(pedMat, draggedOutOffset); posAfterBeingDragged += ped->GetPosition(); CPedPlacement::FindZCoorForPed(&posAfterBeingDragged); ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); ped->SetPosition(posAfterBeingDragged); if (ped->m_pMyVehicle && !ped->m_pMyVehicle->IsBike() && !ped->m_pMyVehicle->IsRoomForPedToLeaveCar(ped->m_vehDoor, &vecPedDraggedOutCarAnimOffset)) { ped->PositionPedOutOfCollision(); } if (!ped->CanSetPedState()) return; if (!ped->m_pMyVehicle) { ped->SetIdle(); ped->SetGetUp(); return; } CPed *driver = ped->m_pMyVehicle->pDriver; if (ped->IsPlayer()) { ped->SetIdle(); } else if (ped->bFleeAfterExitingCar) { ped->bFleeAfterExitingCar = false; ped->SetFlee(ped->m_pMyVehicle->GetPosition(), 4000); } else if (ped->bWanderPathAfterExitingCar) { ped->SetWanderPath(CGeneral::GetRandomNumberInRange(0.0f, 8.0f)); ped->bWanderPathAfterExitingCar = false; } else if (ped->bGonnaKillTheCarJacker) { // Kill objective is already set at this point. ped->bGonnaKillTheCarJacker = false; if (!ped->m_pedInObjective || !(CGeneral::GetRandomNumber() & 1)) { if (!driver || driver == ped || driver->IsPlayer() && CTheScripts::IsPlayerOnAMission()) { ped->SetPedState(PED_NONE); ped->m_nLastPedState = PED_NONE; ped->SetFlee(ped->m_pMyVehicle->GetPosition(), 4000); } else { ped->ClearObjective(); ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); } } } else if (ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && driver && driver->IsPlayer() && !CTheScripts::IsPlayerOnAMission()) { ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); } else if (ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && !driver && FindPlayerPed()->m_carInObjective == ped->m_pMyVehicle && !CTheScripts::IsPlayerOnAMission()) ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); else { ped->SetPedState(PED_NONE); ped->m_nLastPedState = PED_NONE; ped->SetFindPathAndFlee(ped->m_pMyVehicle->GetPosition(), 10000); } ped->SetGetUp(); } uint8 CPed::GetNearestTrainDoor(CVehicle *train, CVector &doorPos) { GetNearestTrainPedPosition(train, doorPos); /* // Not used. CVehicleModelInfo* trainModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(train->m_modelIndex); CMatrix trainMat = CMatrix(train->GetMatrix()); doorPos = trainModel->m_positions[m_vehDoor]; doorPos.x -= 1.5f; doorPos = Multiply3x3(trainMat, doorPos); doorPos += train->GetPosition(); */ return 1; } uint8 CPed::GetNearestTrainPedPosition(CVehicle *train, CVector &enterPos) { CVector enterStepOffset; CVehicleModelInfo *trainModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(train->GetModelIndex()); CMatrix trainMat = CMatrix(train->GetMatrix()); CVector leftEntryPos, rightEntryPos, midEntryPos; float distLeftEntry, distRightEntry, distMidEntry; // enterStepOffset = vecPedCarDoorAnimOffset; enterStepOffset = CVector(1.5f, 0.0f, 0.0f); if (train->pPassengers[TRAIN_POS_LEFT_ENTRY]) { distLeftEntry = 999.0f; } else { leftEntryPos = trainModel->m_positions[TRAIN_POS_LEFT_ENTRY] - enterStepOffset; leftEntryPos = Multiply3x3(trainMat, leftEntryPos); leftEntryPos += train->GetPosition(); distLeftEntry = (leftEntryPos - GetPosition()).Magnitude(); } if (train->pPassengers[TRAIN_POS_MID_ENTRY]) { distMidEntry = 999.0f; } else { midEntryPos = trainModel->m_positions[TRAIN_POS_MID_ENTRY] - enterStepOffset; midEntryPos = Multiply3x3(trainMat, midEntryPos); midEntryPos += train->GetPosition(); distMidEntry = (midEntryPos - GetPosition()).Magnitude(); } if (train->pPassengers[TRAIN_POS_RIGHT_ENTRY]) { distRightEntry = 999.0f; } else { rightEntryPos = trainModel->m_positions[TRAIN_POS_RIGHT_ENTRY] - enterStepOffset; rightEntryPos = Multiply3x3(trainMat, rightEntryPos); rightEntryPos += train->GetPosition(); distRightEntry = (rightEntryPos - GetPosition()).Magnitude(); } if (distMidEntry < distLeftEntry) { if (distMidEntry < distRightEntry) { enterPos = midEntryPos; m_vehDoor = TRAIN_POS_MID_ENTRY; } else { enterPos = rightEntryPos; m_vehDoor = TRAIN_POS_RIGHT_ENTRY; } } else if (distRightEntry < distLeftEntry) { enterPos = rightEntryPos; m_vehDoor = TRAIN_POS_RIGHT_ENTRY; } else { enterPos = leftEntryPos; m_vehDoor = TRAIN_POS_LEFT_ENTRY; } return 1; } void CPed::PedSetInTrainCB(CAnimBlendAssociation* animAssoc, void* arg) { CPed *ped = (CPed*)arg; CTrain *veh = (CTrain*)ped->m_pMyVehicle; if (!veh) return; ped->bInVehicle = true; ped->SetPedState(PED_DRIVING); ped->RestorePreviousObjective(); ped->SetMoveState(PEDMOVE_STILL); veh->AddPassenger(ped); } #ifdef GTA_TRAIN void CPed::SetEnterTrain(CVehicle *train, uint32 unused) { if (m_nPedState == PED_ENTER_TRAIN || !((CTrain*)train)->Doors[0].IsFullyOpen()) return; /* // Not used CVector enterPos; GetNearestTrainPedPosition(train, enterPos); */ m_fRotationCur = train->GetForward().Heading() - HALFPI; m_pMyVehicle = train; m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); SetPedState(PED_ENTER_TRAIN); m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_TRAIN_GETIN, 4.0f); m_pVehicleAnim->SetFinishCallback(PedSetInTrainCB, this); bUsesCollision = false; LineUpPedWithTrain(); if (IsPlayer()) { if (((CPlayerPed*)this)->m_bAdrenalineActive) ((CPlayerPed*)this)->ClearAdrenaline(); } } void CPed::EnterTrain(void) { LineUpPedWithTrain(); } void CPed::SetPedPositionInTrain(void) { LineUpPedWithTrain(); } void CPed::LineUpPedWithTrain(void) { CVector lineUpPos; CVehicleModelInfo *trainModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(m_pMyVehicle->GetModelIndex()); CVector enterOffset(1.5f, 0.0f, -0.2f); m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); m_fRotationCur = m_pMyVehicle->GetForward().Heading() - HALFPI; m_fRotationDest = m_fRotationCur; if (!bInVehicle) { GetNearestTrainDoor(m_pMyVehicle, lineUpPos); lineUpPos.z += 0.2f; } else { if (m_pMyVehicle->pPassengers[TRAIN_POS_LEFT_ENTRY] == this) { lineUpPos = trainModel->m_positions[TRAIN_POS_LEFT_ENTRY] - enterOffset; } else if (m_pMyVehicle->pPassengers[TRAIN_POS_MID_ENTRY] == this) { lineUpPos = trainModel->m_positions[TRAIN_POS_MID_ENTRY] - enterOffset; } else if (m_pMyVehicle->pPassengers[TRAIN_POS_RIGHT_ENTRY] == this) { lineUpPos = trainModel->m_positions[TRAIN_POS_RIGHT_ENTRY] - enterOffset; } lineUpPos = Multiply3x3(m_pMyVehicle->GetMatrix(), lineUpPos); lineUpPos += m_pMyVehicle->GetPosition(); } if (m_pVehicleAnim) { float percentageLeft = m_pVehicleAnim->GetTimeLeft() / m_pVehicleAnim->hierarchy->totalLength; lineUpPos += (GetPosition() - lineUpPos) * percentageLeft; } SetPosition(lineUpPos); SetHeading(m_fRotationCur); } void CPed::SetExitTrain(CVehicle* train) { if (m_nPedState == PED_EXIT_TRAIN || train->GetStatus() != STATUS_TRAIN_NOT_MOVING || !((CTrain*)train)->Doors[0].IsFullyOpen()) return; /* // Not used CVector exitPos; GetNearestTrainPedPosition(train, exitPos); */ SetPedState(PED_EXIT_TRAIN); m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_TRAIN_GETOUT, 4.0f); m_pVehicleAnim->SetFinishCallback(PedSetOutTrainCB, this); bUsesCollision = false; LineUpPedWithTrain(); } void CPed::ExitTrain(void) { LineUpPedWithTrain(); } void CPed::PedSetOutTrainCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; CVehicle *veh = ped->m_pMyVehicle; if (ped->m_pVehicleAnim) ped->m_pVehicleAnim->blendDelta = -1000.0f; ped->bUsesCollision = true; ped->m_pVehicleAnim = nil; ped->bInVehicle = false; ped->SetPedState(PED_IDLE); ped->RestorePreviousObjective(); ped->SetMoveState(PEDMOVE_STILL); CMatrix pedMat(ped->GetMatrix()); ped->m_fRotationCur = HALFPI + veh->GetForward().Heading(); ped->m_fRotationDest = ped->m_fRotationCur; CVector posAfterExit = Multiply3x3(pedMat, vecPedTrainDoorAnimOffset); posAfterExit += ped->GetPosition(); CPedPlacement::FindZCoorForPed(&posAfterExit); ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); ped->SetPosition(posAfterExit); ped->SetHeading(ped->m_fRotationCur); veh->RemovePassenger(ped); } #endif void CPed::RegisterThreatWithGangPeds(CEntity *attacker) { CPed *attackerPed = nil; if ((CharCreatedBy == MISSION_CHAR && bIsPlayerFriend && (attacker == FindPlayerPed() || attacker == FindPlayerVehicle())) || (attacker && m_leader == attacker) || (m_nPedType == PEDTYPE_GANG7 && attacker == FindPlayerPed())) return; if (attacker) { if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS) { if (attacker->IsPed()) { attackerPed = (CPed*)attacker; } else if (attacker->IsVehicle()) { attackerPed = ((CVehicle*)attacker)->pDriver; if (!attackerPed) return; } else return; if (attackerPed && (attackerPed->IsPlayer() || attackerPed->IsGangMember())) { for (int i = 0; i < m_numNearPeds; ++i) { CPed *nearPed = m_nearPeds[i]; if (nearPed->IsPointerValid()) { if (nearPed->CharCreatedBy == RANDOM_CHAR && nearPed != this && nearPed->m_nPedType == m_nPedType) nearPed->m_fearFlags |= CPedType::GetFlag(attackerPed->m_nPedType); } } } } } if (attackerPed && attackerPed->IsPlayer() && (attackerPed->m_nPedState == PED_CARJACK || attackerPed->bInVehicle)) { if (!attackerPed->m_pMyVehicle || attackerPed->m_pMyVehicle->GetModelIndex() != MI_TOPFUN) { int16 lastVehicle; CEntity *vehicles[8]; CWorld::FindObjectsInRange(GetPosition(), 30.0f, true, &lastVehicle, 6, vehicles, false, true, false, false, false); if (lastVehicle > 8) lastVehicle = 8; for (int j = 0; j < lastVehicle; ++j) { CVehicle *nearVeh = (CVehicle*) vehicles[j]; if (nearVeh->VehicleCreatedBy != MISSION_VEHICLE) { CPed *nearVehDriver = nearVeh->pDriver; if (nearVehDriver && nearVehDriver != this && nearVehDriver->m_nPedType == m_nPedType && nearVehDriver->CharCreatedBy == RANDOM_CHAR) { if (nearVeh->IsVehicleNormal() && nearVeh->IsCar()) { nearVeh->AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * nearVeh->pHandling->Transmission.fMaxCruiseVelocity * 0.8f; nearVeh->AutoPilot.m_nCarMission = MISSION_RAMPLAYER_FARAWAY; nearVeh->SetStatus(STATUS_PHYSICS); nearVeh->AutoPilot.m_nTempAction = TEMPACT_NONE; nearVeh->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; } } } } } } } // Some helper function which doesn't exist in og game. inline void SelectClosestNodeForSeek(CPed *ped, CPathNode *node, CVector2D closeDist, CVector2D farDist, CPathNode *closeNode, CPathNode *closeNode2, int runCount = 3) { for (int i = 0; i < node->numLinks; i++) { CPathNode *testNode = &ThePaths.m_pathNodes[ThePaths.ConnectedNode(i + node->firstLink)]; if (testNode && testNode != closeNode && testNode != closeNode2) { CVector2D posDiff(ped->m_vecSeekPos - testNode->GetPosition()); float dist = posDiff.MagnitudeSqr(); if (farDist.MagnitudeSqr() > dist) { if (closeDist.MagnitudeSqr() > dist) { ped->m_pNextPathNode = (closeNode2 ? closeNode2 : testNode); farDist = posDiff; } else { ped->m_pNextPathNode = closeNode; closeDist = posDiff; } } if (--runCount > 0) SelectClosestNodeForSeek(ped, testNode, closeDist, farDist, closeNode, (closeNode2 ? closeNode2 : testNode), runCount); } } } bool CPed::FindBestCoordsFromNodes(CVector unused, CVector *bestCoords) { if (m_pNextPathNode || !bUsePedNodeSeek) return false; const CVector &ourPos = GetPosition(); int closestNodeId = ThePaths.FindNodeClosestToCoors(GetPosition(), 1, 999999.9f); CVector seekObjPos = m_vecSeekPos; seekObjPos.z += 1.0f; if (CWorld::GetIsLineOfSightClear(ourPos, seekObjPos, true, false, false, true, false, false, false)) return false; m_pNextPathNode = nil; CVector2D seekPosDist (m_vecSeekPos - ourPos); CPathNode *closestNode = &ThePaths.m_pathNodes[closestNodeId]; CVector2D closeDist(m_vecSeekPos - closestNode->GetPosition()); SelectClosestNodeForSeek(this, closestNode, closeDist, seekPosDist, closestNode, nil); if (m_pNextPathNode) { // Function above decided that directly going to next node makes more sense then seeking the object. CVector correctedCoords = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); if ((correctedCoords - ourPos).MagnitudeSqr2D() < seekPosDist.MagnitudeSqr()) { *bestCoords = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); return true; } m_pNextPathNode = nil; } return false; } bool CPed::DuckAndCover(void) { if (!m_pedInObjective || CTimer::GetTimeInMilliseconds() <= m_duckAndCoverTimer) return false; if (bKindaStayInSamePlace){ if (CTimer::GetTimeInMilliseconds() <= m_leaveCarTimer) { if (!m_pLookTarget || m_pLookTarget != m_pedInObjective) { m_pLookTarget = m_pedInObjective; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); } if (!bIsAimingGun) SetAimFlag(m_pedInObjective); } else { bKindaStayInSamePlace = false; if (bIsDucking) ClearDuck(true); bCrouchWhenShooting = false; bDuckAndCover = false; m_headingRate = 10.0f; m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(20000,30000); if (m_pSeekTarget && m_pSeekTarget->IsVehicle()) ((CVehicle*)m_pSeekTarget)->m_numPedsUseItAsCover--; } return false; } int16 lastVehicle = 0; CEntity* vehicles[8]; bool justDucked = false; CVehicle *foundVeh = nil; float maxDist = 225.0f; if (bIsDucking) ClearDuck(true); bCrouchWhenShooting = false; bool duckingWithoutVeh = false; if (CTimer::GetTimeInMilliseconds() > m_leaveCarTimer) { for(int i = 0; i < 6; i++) { CPlayerPed *player = (CPlayerPed*)m_pedInObjective; if (player->m_pPedAtSafePos[i] == this) { duckingWithoutVeh = true; CVector &safePos = player->m_vecSafePos[i]; bool notRunningToSafePos = false; if (m_vecSeekPos.x != safePos.x && m_vecSeekPos.y != safePos.y && m_vecSeekPos.z != safePos.z) notRunningToSafePos = true; if (!notRunningToSafePos) { CVector target = player->m_vecSafePos[i]; SetSeek(target, 1.0f); duckingWithoutVeh = true; m_attackTimer = CTimer::GetTimeInMilliseconds() + 6000; bDuckAndCover = true; } break; } } if (!duckingWithoutVeh) { for (int i = 0; i < 6; i++) { CPlayerPed* player = (CPlayerPed*)m_pedInObjective; if (!player->m_pPedAtSafePos[i] && player->m_vecSafePos[i].x != 0.0f) { player->m_pPedAtSafePos[i] = this; CVector target = player->m_vecSafePos[i]; SetSeek(target, 1.0f); m_headingRate = 15.0f; ClearPointGunAt(); duckingWithoutVeh = 1; bIsRunning = true; bDuckAndCover = true; justDucked = true; m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 500; break; } } } if (!duckingWithoutVeh) { CVector pos = GetPosition(); CWorld::FindObjectsInRange(pos, CHECK_NEARBY_THINGS_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); } for (int i = 0; i < lastVehicle; i++) { CVehicle *veh = (CVehicle*) vehicles[i]; if (veh->IsCar() && veh->m_vecMoveSpeed.Magnitude() <= 0.02f && !veh->bIsBus && !veh->bIsVan && !veh->bIsBig && veh->m_numPedsUseItAsCover < 3) { float dist = (GetPosition() - veh->GetPosition()).MagnitudeSqr(); if (dist < maxDist) { maxDist = dist; foundVeh = veh; } } } if (foundVeh) { // Unused. // CVector lfWheelPos, rfWheelPos; // foundVeh->GetComponentWorldPosition(CAR_WHEEL_RF, rfWheelPos); // foundVeh->GetComponentWorldPosition(CAR_WHEEL_LF, lfWheelPos); CVector rightSide, leftSide; // 3 persons can use the car as cover. Found the correct position for us. if (foundVeh->m_numPedsUseItAsCover == 2) { rightSide = CVector(1.5f, -0.5f, 0.0f); leftSide = CVector(-1.5f, -0.5f, 0.0f); } else if (foundVeh->m_numPedsUseItAsCover == 1) { rightSide = CVector(1.5f, 0.5f, 0.0f); leftSide = CVector(-1.5f, 0.5f, 0.0f); } else if (foundVeh->m_numPedsUseItAsCover == 0) { rightSide = CVector(1.5f, 0.0f, 0.0f); leftSide = CVector(-1.5f, 0.0f, 0.0f); } CMatrix vehMatrix(foundVeh->GetMatrix()); CVector duckAtRightSide = Multiply3x3(vehMatrix, rightSide) + foundVeh->GetPosition(); CVector duckAtLeftSide = Multiply3x3(vehMatrix, leftSide) + foundVeh->GetPosition(); CVector distWithPedRightSide = m_pedInObjective->GetPosition() - duckAtRightSide; CVector distWithPedLeftSide = m_pedInObjective->GetPosition() - duckAtLeftSide; CVector duckPos; if (distWithPedRightSide.MagnitudeSqr() <= distWithPedLeftSide.MagnitudeSqr()) duckPos = duckAtLeftSide; else duckPos = duckAtRightSide; if (CWorld::TestSphereAgainstWorld(duckPos, 0.5f, nil, true, true, true, false, false, false)) { SetSeek(duckPos, 1.0f); m_headingRate = 15.0f; bIsRunning = true; bDuckAndCover = true; justDucked = true; m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 500; if (foundVeh->bIsLawEnforcer) { m_carInObjective = foundVeh; m_carInObjective->RegisterReference((CEntity**)&m_carInObjective); } m_pSeekTarget = foundVeh; m_pSeekTarget->RegisterReference((CEntity**)&m_pSeekTarget); ClearPointGunAt(); } else { m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(10000, 15000); bDuckAndCover = false; } } else if (!duckingWithoutVeh) { bDuckAndCover = false; } } if (!justDucked && !bDuckAndCover) return false; if (!Seek()) { if (m_nMoveState == PEDMOVE_STILL) { bDuckAndCover = false; return false; } else return true; } bKindaStayInSamePlace = true; bDuckAndCover = false; m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); if (m_pSeekTarget && m_pSeekTarget->IsVehicle()) ((CVehicle*)m_pSeekTarget)->m_numPedsUseItAsCover++; SetIdle(); SetMoveState(PEDMOVE_STILL); SetMoveAnim(); if (!m_pLookTarget || m_pLookTarget != m_pedInObjective) { m_pLookTarget = m_pedInObjective; m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); } m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(3000, 6000); bCrouchWhenShooting = true; SetDuck(CGeneral::GetRandomNumberInRange(2000, 5000), true); return false; } CVector CPed::GetPositionToOpenCarDoor(CVehicle *veh, uint32 component, float offset) { CVector doorPos; CMatrix vehMat(veh->GetMatrix()); if (veh->IsBike()) { CVehicleModelInfo* vehModel = (CVehicleModelInfo*)CModelInfo::GetModelInfo(veh->GetModelIndex()); CVector vehDoorOffset; CBike* bike = (CBike*)veh; doorPos = vehModel->GetFrontSeatPosn(); if (component == CAR_WINDSCREEN) { return bike->GetMatrix() * (doorPos + vecPedBikeKickAnimOffset); } else { switch (bike->m_bikeAnimType) { case ASSOCGRP_BIKE_VESPA: vehDoorOffset = vecPedVespaBikeJumpRhsAnimOffset; break; case ASSOCGRP_BIKE_HARLEY: vehDoorOffset = vecPedHarleyBikeJumpRhsAnimOffset; break; case ASSOCGRP_BIKE_DIRT: vehDoorOffset = vecPedDirtBikeJumpRhsAnimOffset; break; default: vehDoorOffset = vecPedStdBikeJumpRhsAnimOffset; break; } vehDoorOffset.x += offset * bike->pHandling->fSeatOffsetDistance; if (component == CAR_DOOR_LR || component == CAR_DOOR_RR) { doorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; } if (component == CAR_DOOR_LR || component == CAR_DOOR_LF) vehDoorOffset.x *= -1.f; CVector correctedPos; bike->GetCorrectedWorldDoorPosition(correctedPos, vehDoorOffset, doorPos); return correctedPos; } } doorPos = Multiply3x3(vehMat, GetLocalPositionToOpenCarDoor(veh, component, offset)); return veh->GetPosition() + doorPos; } CVector CPed::GetLocalPositionToOpenCarDoor(CVehicle *veh, uint32 component, float seatPosMult) { CVehicleModelInfo *vehModel; CVector vehDoorPos; CVector vehDoorOffset; float seatOffset; vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(veh->GetModelIndex()); if (veh->IsBike()) { CBike *bike = (CBike*)veh; vehDoorPos = vehModel->GetFrontSeatPosn(); if (component == CAR_WINDSCREEN) { return bike->GetMatrix() * (vehDoorPos + vecPedBikeKickAnimOffset); } else { switch (bike->m_bikeAnimType) { case ASSOCGRP_BIKE_VESPA: vehDoorOffset = vecPedVespaBikeJumpRhsAnimOffset; break; case ASSOCGRP_BIKE_HARLEY: vehDoorOffset = vecPedHarleyBikeJumpRhsAnimOffset; break; case ASSOCGRP_BIKE_DIRT: vehDoorOffset = vecPedDirtBikeJumpRhsAnimOffset; break; default: vehDoorOffset = vecPedStdBikeJumpRhsAnimOffset; break; } float xOffsetFromAnim = vehDoorOffset.x + seatPosMult * bike->pHandling->fSeatOffsetDistance; if (component == CAR_DOOR_LR || component == CAR_DOOR_RR) { vehDoorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; } if (component == CAR_DOOR_LR || component == CAR_DOOR_LF) xOffsetFromAnim *= -1.f; return bike->GetMatrix() * (vehDoorPos + CVector(xOffsetFromAnim, vehDoorOffset.y, vehDoorOffset.z)); } } else { if (veh->bIsVan && (component == CAR_DOOR_LR || component == CAR_DOOR_RR)) { seatOffset = 0.0f; vehDoorOffset = vecPedVanRearDoorAnimOffset; } else { seatOffset = veh->pHandling->fSeatOffsetDistance * seatPosMult; if (veh->bLowVehicle) { vehDoorOffset = vecPedCarDoorLoAnimOffset; } else { vehDoorOffset = vecPedCarDoorAnimOffset; } } switch (component) { case CAR_DOOR_RF: vehDoorPos = vehModel->GetFrontSeatPosn(); vehDoorPos.x += seatOffset; vehDoorOffset.x = -vehDoorOffset.x; break; case CAR_DOOR_RR: vehDoorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; vehDoorPos.x += seatOffset; vehDoorOffset.x = -vehDoorOffset.x; break; case CAR_DOOR_LF: vehDoorPos = vehModel->GetFrontSeatPosn(); vehDoorPos.x = -(vehDoorPos.x + seatOffset); break; case CAR_DOOR_LR: vehDoorPos = vehModel->m_positions[CAR_POS_BACKSEAT]; vehDoorPos.x = -(vehDoorPos.x + seatOffset); break; default: vehDoorPos = vehModel->GetFrontSeatPosn(); vehDoorOffset = CVector(0.0f, 0.0f, 0.0f); } return vehDoorPos - vehDoorOffset; } } // TODO: what is this parameter for? void CPed::SetDuck(uint32 time, bool sth) { if (bIsDucking || CTimer::GetTimeInMilliseconds() <= m_duckTimer && !sth) { if (sth && CTimer::GetTimeInMilliseconds() + time > m_duckTimer) m_duckTimer = CTimer::GetTimeInMilliseconds() + time; return; } CAnimBlendAssociation *duckAssoc; if (bCrouchWhenShooting) { duckAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_WEAPON, 4.0f); duckAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; bIsDucking = true; m_duckTimer = CTimer::GetTimeInMilliseconds() + time; } else { CAnimBlendAssociation *duckAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_DOWN); if (!duckAssoc || duckAssoc->blendDelta < 0.0f) { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_DOWN, 4.0f); bIsDucking = true; m_duckTimer = CTimer::GetTimeInMilliseconds() + time; } } } void CPed::Duck(void) { if (CTimer::GetTimeInMilliseconds() > m_duckTimer) ClearDuck(); else if (bIsDucking && bCrouchWhenShooting) { CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); CAnimBlendAssociation *crouchAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_WEAPON); if (!crouchAnim) { if(GetCrouchFireAnim(weapon)) crouchAnim = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weapon)); } if (!crouchAnim) { if(GetCrouchReloadAnim(weapon)) crouchAnim = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchReloadAnim(weapon)); } if (!crouchAnim) { bIsDucking = false; #if defined FIX_BUGS || defined FREE_CAM if (IsPlayer()) bCrouchWhenShooting = false; #endif } } } void CPed::ClearDuck(bool clearTimer) { CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_DOWN); if (!animAssoc) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_LOW); } if (!animAssoc) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_WEAPON); } if (animAssoc) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc->blendDelta = -4.0f; } bIsDucking = false; if (clearTimer) { m_duckTimer = 0; } } void CPed::InformMyGangOfAttack(CEntity *attacker) { CPed *attackerPed; if (m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS) return; if (attacker->IsPed()) { attackerPed = (CPed*)attacker; } else { if (!attacker->IsVehicle()) return; attackerPed = ((CVehicle*)attacker)->pDriver; if (!attackerPed) return; } if (attackerPed->m_nPedType == PEDTYPE_COP) return; for (int i = 0; i < m_numNearPeds; i++) { CPed *nearPed = m_nearPeds[i]; if (nearPed && nearPed != this) { CPed *leader = nearPed->m_leader; if (leader && leader == this && nearPed->m_pedStats->m_fear < nearPed->m_pedStats->m_temper) { nearPed->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, attackerPed); nearPed->SetObjectiveTimer(30000); } } } } void CPed::PedAnimDoorCloseRollingCB(CAnimBlendAssociation* animAssoc, void* arg) { CPed* ped = (CPed*)arg; CAutomobile* veh = (CAutomobile*)(ped->m_pMyVehicle); if (animAssoc) animAssoc->blendDelta = -1000.0f; if (veh->bLowVehicle) { veh->ProcessOpenDoor(CAR_DOOR_LF, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS, 1.0f); } else { veh->ProcessOpenDoor(CAR_DOOR_LF, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, 1.0f); } veh->m_nGettingOutFlags &= ~CAR_DOOR_FLAG_LF; if (veh->Damage.GetDoorStatus(DOOR_FRONT_LEFT) == DOOR_STATUS_SWINGING) veh->Damage.SetDoorStatus(DOOR_FRONT_LEFT, DOOR_STATUS_OK); } void CPed::SetSeekBoatPosition(CVehicle *boat) { if (m_nPedState == PED_SEEK_IN_BOAT || boat->pDriver || !IsPedInControl()) return; SetStoredState(); m_carInObjective = boat; m_carInObjective->RegisterReference((CEntity **) &m_carInObjective); m_pMyVehicle = boat; m_pMyVehicle->RegisterReference((CEntity **) &m_pMyVehicle); m_distanceToCountSeekDone = 0.5f; SetPedState(PED_SEEK_IN_BOAT); } void CPed::SeekBoatPosition(void) { if (m_carInObjective && !m_carInObjective->pDriver) { CVehicleModelInfo *boatModel = m_carInObjective->GetModelInfo(); CVector enterOffset; enterOffset = boatModel->GetFrontSeatPosn(); enterOffset.x = 0.0f; CMatrix boatMat(m_carInObjective->GetMatrix()); SetMoveState(PEDMOVE_WALK); m_vecSeekPos = boatMat * enterOffset; if (Seek()) { // We arrived to the boat m_vehDoor = 0; SetEnterCar(m_carInObjective, 0); } } else RestorePreviousState(); } bool CPed::IsRoomToBeCarJacked(void) { if (!m_pMyVehicle) return false; CVector offset; if (m_pMyVehicle->IsBike()) { offset = vecPedStdBikeJumpRhsAnimOffset; } else if (m_pMyVehicle->bLowVehicle || m_nPedType == PEDTYPE_COP) { offset = vecPedDraggedOutCarAnimOffset; } else { offset = vecPedQuickDraggedOutCarAnimOffset; } offset.z = 0.0f; if (m_pMyVehicle->IsRoomForPedToLeaveCar(CAR_DOOR_LF, &offset)) { return true; } return false; } void CPed::AddInCarAnims(CVehicle* car, bool isDriver) { if (car->IsBoat()) { if (car->pHandling->Flags & HANDLING_SIT_IN_BOAT) { m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT, 100.0f); } else { m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_DRIVE, 100.0f); } } else if (car->IsBike()) { if (isDriver) { m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ((CBike*)car)->m_bikeAnimType, ANIM_BIKE_RIDE, 100.0f); } else { m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ((CBike*)car)->m_bikeAnimType, ANIM_BIKE_RIDE_P, 100.0f); } } else { if (isDriver) { if (car->bLowVehicle) { m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT_LO, 100.0f); } else { m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT, 100.0f); } } else { if (car->bLowVehicle) { m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT_P_LO, 100.0f); } else { m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT_P, 100.0f); } } } StopNonPartialAnims(); } void CPed::RemoveDrivebyAnims() { CAnimBlendAssociation *animAssoc; AnimationId LeftAnim = ANIM_STD_CAR_DRIVEBY_LEFT; AnimationId RightAnim = ANIM_STD_CAR_DRIVEBY_RIGHT; if (m_pMyVehicle->pHandling->Flags & HANDLING_IS_BIKE) { LeftAnim = ANIM_BIKE_DRIVEBY_RHS; RightAnim = ANIM_BIKE_DRIVEBY_LHS; } else if (m_pMyVehicle->bLowVehicle) { LeftAnim = ANIM_STD_CAR_DRIVEBY_LEFT_LO; RightAnim = ANIM_STD_CAR_DRIVEBY_RIGHT_LO; } animAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_BIKE_DRIVEBY_RHS); if (animAssoc) animAssoc->blendDelta = -1000.0f; animAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_BIKE_DRIVEBY_LHS); if (animAssoc) animAssoc->blendDelta = -1000.0f; animAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_BIKE_DRIVEBY_FORWARD); if (animAssoc) animAssoc->blendDelta = -1000.0f; } void CPed::RemoveInCarAnims(void) { CAnimBlendAssociation* assoc; for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_DRIVING); assoc; assoc = RpAnimBlendGetNextAssociation(assoc, ASSOC_DRIVING)) { assoc->flags |= ASSOC_DELETEFADEDOUT; assoc->blendDelta = -1000.0f; } } bool CPed::PositionPedOutOfCollision(void) { CVehicle *veh = m_pMyVehicle; if (!veh) return false; if (bDonePositionOutOfCollision) return true; bool foundAPos = false; CColModel *vehCol = veh->GetColModel(); CVector vehPos = veh->GetPosition(); CVector ourPos = GetPosition(); CVector newPos = ourPos; CWorld::pIgnoreEntity = veh; bUsesCollision = false; bJustCheckCollision = true; m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); if (veh->IsOnItsSide()) { // Top of the veh. newPos = vehPos; newPos.z = FEET_OFFSET + vehCol->boundingBox.max.x + vehPos.z; GetMatrix().SetTranslate(newPos); if (!CheckCollision()) { if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) foundAPos = true; } } else if (m_vehDoor != 0) { // Try the normal way CVector pos = GetPositionToOpenCarDoor(m_pMyVehicle, m_vehDoor); newPos = pos; GetMatrix().SetTranslate(newPos); if (!CheckCollision()) { if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) foundAPos = true; } } float vehRelativeExitX = vehCol->boundingBox.min.x - 0.355f; if (m_vehDoor == CAR_DOOR_RF || m_vehDoor == CAR_DOOR_RR) vehRelativeExitX = 0.355f + vehCol->boundingBox.max.x; if (!foundAPos) { // Check sides of veh., respective to seat column-veh. center difference(why?) float exitOffset = vehRelativeExitX - DotProduct(ourPos - vehPos, veh->GetRight()); newPos = exitOffset * veh->GetRight() + ourPos; GetMatrix().SetTranslate(newPos); if (!CheckCollision()) { if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) foundAPos = true; } } if (!foundAPos) { // Iterate through sections of veh. length + static offset on X float minY = vehCol->boundingBox.min.y; float ySection = (vehCol->boundingBox.max.y - minY) / 3.f; for (int i = 0; i < 4; i++) { float fwdMult = i * ySection + minY; newPos = vehRelativeExitX * veh->GetRight() + fwdMult * veh->GetForward() + vehPos; GetMatrix().SetTranslate(newPos); if (!CheckCollision()) { if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) { foundAPos = true; break; } } } } if (!foundAPos) { // Back of veh. newPos = (vehCol->boundingBox.min.y - 0.355f) * veh->GetForward() + vehPos; GetMatrix().SetTranslate(newPos); if (!CheckCollision()) { if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) foundAPos = true; } } if (!foundAPos) { // Front of veh. newPos = (0.355f + vehCol->boundingBox.max.y) * veh->GetForward() + vehPos; GetMatrix().SetTranslate(newPos); if (!CheckCollision()) { if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) foundAPos = true; } } if (!foundAPos) { // Opposite X side + back newPos = vehCol->boundingBox.min.y * veh->GetForward() + vehPos - vehRelativeExitX * veh->GetRight(); GetMatrix().SetTranslate(newPos); if (!CheckCollision()) { if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) foundAPos = true; } } if (!foundAPos) { // Opposite X side + front newPos = vehCol->boundingBox.max.y * veh->GetForward() + vehPos - vehRelativeExitX * veh->GetRight(); GetMatrix().SetTranslate(newPos); if (!CheckCollision()) { if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) foundAPos = true; } } if (!foundAPos) { // Top of veh. if (veh->m_vehType == 0) { newPos = vehCol->boundingBox.max.z * veh->GetUp() + vehPos; newPos.z += FEET_OFFSET; GetMatrix().SetTranslate(newPos); if (!CheckCollision()) { if (CWorld::GetIsLineOfSightClear(vehPos, newPos, true, false, false, true, false, false, false)) foundAPos = true; } } } m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); m_vecTurnSpeed = CVector(0.f, 0.f, 0.f); veh->m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); veh->m_vecTurnSpeed = CVector(0.f, 0.f, 0.f); CWorld::pIgnoreEntity = nil; bUsesCollision = true; bJustCheckCollision = false; bDonePositionOutOfCollision = true; if (foundAPos) return true; int foundNode = ThePaths.FindNodeClosestToCoors(vehPos, PATH_PED, 999999.9f, true, false, false, false); if (foundNode < 0) return false; newPos = ThePaths.m_pathNodes[foundNode].GetPosition(); CPedPlacement::FindZCoorForPed(&newPos); GetMatrix().SetTranslate(newPos); SetHeading(m_pMyVehicle->GetForward().Heading()); return true; } // "Any" means he shouldn't have to be in vehicle. bool CPed::PositionAnyPedOutOfCollision(void) { CVehicle *veh; CVector posNearVeh; CVector posSomewhereClose; bool putNearVeh = false; bool putSomewhereClose = false; int smallestDistNearVeh = 999; int smallestDistSomewhereClose = 999; CVector potentialPos; potentialPos.y = GetPosition().y - 3.5f; potentialPos.z = GetPosition().z; for (int yTry = 0; yTry < 15; yTry++) { potentialPos.x = GetPosition().x - 3.5f; for (int xTry = 0; xTry < 15; xTry++) { CPedPlacement::FindZCoorForPed(&potentialPos); if (!CWorld::TestSphereAgainstWorld(potentialPos, 0.6f, this, true, false, false, true, false, false)) { float potentialChangeSqr = (potentialPos - GetPosition()).MagnitudeSqr(); veh = (CVehicle*)CWorld::TestSphereAgainstWorld(potentialPos, 0.6f, this, false, true, false, false, false, false); if (veh) { if (potentialChangeSqr < smallestDistNearVeh) { posNearVeh = potentialPos; putNearVeh = true; smallestDistNearVeh = potentialChangeSqr; } } else if (potentialChangeSqr < smallestDistSomewhereClose) { smallestDistSomewhereClose = potentialChangeSqr; posSomewhereClose = potentialPos; putSomewhereClose = true; } } potentialPos.x += 0.5f; } potentialPos.y += 0.5f; } if (!putSomewhereClose && !putNearVeh) return false; // We refrain from using posNearVeh, probably because of it may be top of the vehicle. if (putSomewhereClose) { SetPosition(posSomewhereClose); } else { CVector vehSize = veh->GetModelInfo()->GetColModel()->boundingBox.max; posNearVeh.z += vehSize.z; SetPosition(posNearVeh); } return true; } bool CPed::WarpPedToNearLeaderOffScreen(void) { bool teleported = false; if (GetIsOnScreen() || m_leaveCarTimer > CTimer::GetTimeInMilliseconds()) return false; CVector warpToPos = m_leader->GetPosition(); CVector distVec = warpToPos - GetPosition(); float halfOfDist = distVec.Magnitude() * 0.5f; CVector halfNormalizedDist = distVec / halfOfDist; CVector appropriatePos = GetPosition(); int tryCount = Min(10, (int)halfOfDist); for (int i = 0; i < tryCount; ++i) { appropriatePos += halfNormalizedDist; CVector zCorrectedPos = appropriatePos; CPedPlacement::FindZCoorForPed(&zCorrectedPos); if (Abs(zCorrectedPos.z - warpToPos.z) < 3.0f || Abs(zCorrectedPos.z - appropriatePos.z) < 3.0f) { appropriatePos.z = zCorrectedPos.z; if (!TheCamera.IsSphereVisible(appropriatePos, 0.6f) && CWorld::GetIsLineOfSightClear(appropriatePos, warpToPos, true, true, false, true, false, false, false) && !CWorld::TestSphereAgainstWorld(appropriatePos, 0.6f, this, true, true, false, true, false, false)) { teleported = true; Teleport(appropriatePos); } } } m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 3000; return teleported; } bool CPed::WarpPedToNearEntityOffScreen(CEntity *warpTo) { bool teleported = false; if (GetIsOnScreen() || m_leaveCarTimer > CTimer::GetTimeInMilliseconds()) return false; CVector warpToPos = warpTo->GetPosition(); CVector distVec = warpToPos - GetPosition(); float halfOfDist = distVec.Magnitude() * 0.5f; CVector halfNormalizedDist = distVec / halfOfDist; CVector appropriatePos = GetPosition(); int tryCount = Min(10, (int)halfOfDist); for (int i = 0; i < tryCount; ++i) { appropriatePos += halfNormalizedDist; CVector zCorrectedPos = appropriatePos; CPedPlacement::FindZCoorForPed(&zCorrectedPos); if (Abs(zCorrectedPos.z - warpToPos.z) < 3.0f || Abs(zCorrectedPos.z - appropriatePos.z) < 3.0f) { appropriatePos.z = zCorrectedPos.z; if (!TheCamera.IsSphereVisible(appropriatePos, 0.6f, &TheCamera.GetCameraMatrix()) && CWorld::GetIsLineOfSightClear(appropriatePos, warpToPos, true, true, false, true, false, false, false) && !CWorld::TestSphereAgainstWorld(appropriatePos, 0.6f, this, true, true, false, true, false, false)) { teleported = true; Teleport(appropriatePos); } } } m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 3000; return teleported; } int32 CPed::KillCharOnFootArmed(CVector &ourPos, CVector &targetPos, CVector &distWithTarget) { bool killPlayerInNoPoliceZone = false; if (m_pedInObjective->IsPlayer() && CCullZones::NoPolice()) killPlayerInNoPoliceZone = true; if (!bNotAllowedToDuck || killPlayerInNoPoliceZone) { if (m_nPedType == PEDTYPE_COP && !m_pedInObjective->GetWeapon()->IsTypeMelee()) bNotAllowedToDuck = true; } else { if (!m_pedInObjective->bInVehicle) { if (m_pedInObjective->GetWeapon()->IsTypeMelee()) { bNotAllowedToDuck = false; bCrouchWhenShooting = false; } else if (DuckAndCover()) { return CANT_ATTACK; } } else { bNotAllowedToDuck = false; bCrouchWhenShooting = false; } } if (m_leaveCarTimer > CTimer::GetTimeInMilliseconds() && !bKindaStayInSamePlace) { SetMoveState(PEDMOVE_STILL); return CANT_ATTACK; } if (m_pedInObjective->IsPlayer()) { CPlayerPed *player = FindPlayerPed(); if (m_nPedType == PEDTYPE_COP && player->m_pWanted->m_bIgnoredByCops || player->m_pWanted->m_bIgnoredByEveryone || m_pedInObjective->bIsInWater || m_pedInObjective->m_nPedState == PED_ARRESTED) { if (m_nPedState != PED_ARREST_PLAYER) SetIdle(); return CANT_ATTACK; } } if (m_pedInObjective->IsPlayer() && m_nPedType != PEDTYPE_COP && CharCreatedBy != MISSION_CHAR && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) { SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); return CANT_ATTACK; } if (m_pedInObjective->m_fHealth <= 0.0f) { bObjectiveCompleted = true; bScriptObjectiveCompleted = true; return CANT_ATTACK; } CWeaponInfo *wepInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); float wepRange = wepInfo->m_fRange; float wepRangeAdjusted = wepRange / 3.f; float distWithTargetSc = distWithTarget.Magnitude(); if (m_pedInObjective->bInVehicle && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { CVehicle *vehOfTarget = m_pedInObjective->m_pMyVehicle; if (vehOfTarget->bIsInWater || vehOfTarget->GetStatus() == STATUS_PLAYER_DISABLED || m_pedInObjective->IsPlayer() && CPad::GetPad(0)->ArePlayerControlsDisabled()) { SetIdle(); return WATCH_UNTIL_HE_DISAPPEARS; } SetLookFlag(vehOfTarget, false); if (m_nMoveState == PEDMOVE_STILL && IsPedInControl()) TurnBody(); if (m_nPedState != PED_CARJACK) { if (m_pedInObjective->m_nPedState != PED_ARRESTED) { if (m_attackTimer < CTimer::GetTimeInMilliseconds() && distWithTargetSc < wepRange && distWithTargetSc > 3.0f) { SetAttack(vehOfTarget); SetWeaponLockOnTarget(vehOfTarget); SetShootTimer(CGeneral::GetRandomNumberInRange(500, 2000)); CVector2D dirVehGoing = vehOfTarget->m_vecMoveSpeed; if (dirVehGoing.Magnitude() > 0.2f) { CVector2D vehDist = GetPosition() - vehOfTarget->GetPosition(); vehDist.Normalise(); dirVehGoing.Normalise(); if (DotProduct2D(vehDist, dirVehGoing) > 0.8f) { SetAttackTimer(CGeneral::GetRandomNumberInRange(200, 500)); SetMoveState(PEDMOVE_STILL); } return ATTACK_IN_PROGRESS; } if (distWithTargetSc <= m_distanceToCountSeekDone) { SetAttackTimer(CGeneral::GetRandomNumberInRange(200, 500)); SetMoveState(PEDMOVE_STILL); } else { SetAttackTimer(CGeneral::GetRandomNumberInRange(2000, 5000)); } return ATTACK_IN_PROGRESS; } else if (m_nPedState != PED_ATTACK && !bKindaStayInSamePlace && !killPlayerInNoPoliceZone) { if (vehOfTarget) { if (m_nPedType == PEDTYPE_COP || vehOfTarget->bIsBus) { GoToNearestDoor(vehOfTarget); } else { m_vehDoor = 0; if (m_pedInObjective == vehOfTarget->pDriver || vehOfTarget->bIsBus) { m_vehDoor = CAR_DOOR_LF; } else if (m_pedInObjective == vehOfTarget->pPassengers[0]) { m_vehDoor = CAR_DOOR_RF; } else if (m_pedInObjective == vehOfTarget->pPassengers[1]) { m_vehDoor = CAR_DOOR_LR; } else if (m_pedInObjective == vehOfTarget->pPassengers[2]) { m_vehDoor = CAR_DOOR_RR; } // Unused // GetPositionToOpenCarDoor(vehOfTarget, m_vehDoor); SetSeekCar(vehOfTarget, m_vehDoor); SetMoveState(PEDMOVE_RUN); } } } } } return ATTACK_IN_PROGRESS; } if (m_nMoveState == PEDMOVE_STILL && IsPedInControl()) { SetLookFlag(m_pedInObjective, false); TurnBody(); } if (m_nPedType == PEDTYPE_COP && m_pedInObjective->IsPlayer()) { float maxArrestDist = 1.5f; if (((CCopPed*)this)->m_bDragsPlayerFromCar) { if (m_nPedState == PED_FALL) { maxArrestDist = 3.5f; } else if (m_nPedState != PED_DRAG_FROM_CAR) { ((CCopPed*)this)->m_bDragsPlayerFromCar = 0; } } if (distWithTargetSc < maxArrestDist) { if (m_pedInObjective->m_getUpTimer > CTimer::GetTimeInMilliseconds() || m_pedInObjective->m_nPedState == PED_DRAG_FROM_CAR) { ((CCopPed*)this)->SetArrestPlayer(m_pedInObjective); return WATCH_UNTIL_HE_DISAPPEARS; } } } /* if (distWithTargetSc > 0.1f) { junk code } */ if (m_shotTime != 0 && m_ceaseAttackTimer != 0) { if (CTimer::GetTimeInMilliseconds() > m_ceaseAttackTimer + m_shotTime) { ClearLookFlag(); bObjectiveCompleted = true; m_shotTime = 0; return CANT_ATTACK; } } if (!bKindaStayInSamePlace && !bStopAndShoot && m_nPedState != PED_ATTACK && !bDuckAndCover && !killPlayerInNoPoliceZone) { if (distWithTargetSc > wepRange || m_pedInObjective->m_getUpTimer > CTimer::GetTimeInMilliseconds() || m_pedInObjective->m_nPedState == PED_ARRESTED || m_pedInObjective->EnteringCar() && distWithTargetSc < 3.0f) { if (m_pedInObjective->EnteringCar()) wepRangeAdjusted = 2.0f; if (bUsePedNodeSeek) { CVector bestCoords(0.0f, 0.0f, 0.0f); m_vecSeekPos = m_pedInObjective->GetPosition(); if (!m_pNextPathNode) FindBestCoordsFromNodes(m_vecSeekPos, &bestCoords); if (m_pNextPathNode) m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); SetSeek(m_vecSeekPos, m_distanceToCountSeekDone); } else { SetSeek(m_pedInObjective, wepRangeAdjusted); } if (m_pedInObjective->m_pCurrentPhysSurface && distWithTargetSc < 5.0f) { bStopAndShoot = true; b158_8 = true; SetMoveState(PEDMOVE_STILL); } else if (b158_8) { bStopAndShoot = false; b158_8 = false; } return ATTACK_IN_PROGRESS; } } if (m_attackTimer < CTimer::GetTimeInMilliseconds() && distWithTargetSc < wepRange && m_pedInObjective->m_nPedState != PED_GETUP && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { if (bIsDucking && !bCrouchWhenShooting) { CAnimBlendAssociation* duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_DOWN); if (!duckAnim) duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_LOW); if (!duckAnim) duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_WEAPON); if (duckAnim) { duckAnim->flags |= ASSOC_DELETEFADEDOUT; duckAnim->blendDelta = -4.0f; } bIsDucking = false; return CANT_ATTACK; } bObstacleShowedUpDuringKillObjective = false; SetAttack(m_pedInObjective); SetWeaponLockOnTarget(m_pedInObjective); SetShootTimer(CGeneral::GetRandomNumberInRange(600.0f, 1500.0f)); int time; if (distWithTargetSc <= wepRangeAdjusted) time = CGeneral::GetRandomNumberInRange(100.0f, 500.0f); else time = CGeneral::GetRandomNumberInRange(1500.0f, 3000.0f); SetAttackTimer(time); } else { if (!m_pedInObjective->m_pCurrentPhysSurface && b158_8) { b158_8 = false; bStopAndShoot = false; } if (m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT) { if (bNotAllowedToDuck && bKindaStayInSamePlace && !bIsDucking && bCrouchWhenShooting) { SetDuck(CGeneral::GetRandomNumberInRange(1000, 5000), false); return CANT_ATTACK; } if (bObstacleShowedUpDuringKillObjective) { if (m_nPedType == PEDTYPE_COP) { if (GetWeapon()->m_eWeaponType > WEAPONTYPE_COLT45 || m_fleeFrom && m_fleeFrom->IsObject()) { wepRangeAdjusted = 6.0f; } else if (m_fleeFrom && m_fleeFrom->IsVehicle()) { wepRangeAdjusted = 4.0f; } else { wepRangeAdjusted = 2.0f; } } else { wepRangeAdjusted = 2.0f; } } if (distWithTargetSc <= wepRangeAdjusted) { SetMoveState(PEDMOVE_STILL); bIsPointingGunAt = true; if (m_nPedState != PED_AIM_GUN && !bDuckAndCover) { m_attackTimer = CTimer::GetTimeInMilliseconds(); SetIdle(); } } else { if (m_nPedState != PED_SEEK_ENTITY && m_nPedState != PED_SEEK_POS && !bStopAndShoot && !killPlayerInNoPoliceZone && !bKindaStayInSamePlace) { Say(SOUND_PED_ATTACK); SetSeek(m_pedInObjective, wepRangeAdjusted); bIsRunning = true; if (m_nPedType == PEDTYPE_COP && FindPlayerPed()->m_pWanted->m_CurrentCops > 1) { if (CGeneral::GetRandomNumber() & 1) Say(SOUND_PED_GUNAIMEDAT3); else Say(SOUND_PED_GUNAIMEDAT2); } } } } } if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(m_pedInObjective->GetPosition().x, m_pedInObjective->GetPosition().y, GetPosition().x, GetPosition().y); return ATTACK_IN_PROGRESS; } int32 CPed::KillCharOnFootMelee(CVector &ourPos, CVector &targetPos, CVector &distWithTarget) { bool killPlayerInNoPoliceZone = false; float distWithTargetSc = distWithTarget.Magnitude(); CPlayerPed *victimPlayer = nil; if (m_pedInObjective->IsPlayer()) victimPlayer = (CPlayerPed*)m_pedInObjective; if (victimPlayer) { if (CCullZones::NoPolice() || m_pedInObjective->m_pCurrentPhysSurface && m_pedInObjective->m_pCurrentPhysSurface != m_pCurrentPhysSurface && distWithTargetSc < 5.f) { if (victimPlayer && victimPlayer->m_bSpeedTimerFlag && (IsGangMember() || m_nPedType == PEDTYPE_COP) && CharCreatedBy != MISSION_CHAR) { GiveWeapon(WEAPONTYPE_COLT45, 1000, 1); SetCurrentWeapon(WEAPONTYPE_COLT45); SetMoveState(PEDMOVE_STILL); bStopAndShoot = true; b158_8 = true; return CANT_ATTACK; } killPlayerInNoPoliceZone = true; } } bNotAllowedToDuck = false; bStopAndShoot = false; b158_8 = false; if (m_leaveCarTimer > CTimer::GetTimeInMilliseconds() && !bKindaStayInSamePlace) { SetMoveState(PEDMOVE_STILL); return CANT_ATTACK; } if (victimPlayer) { CPlayerPed *player = FindPlayerPed(); if (m_nPedType == PEDTYPE_COP && player->m_pWanted->m_bIgnoredByCops || player->m_pWanted->m_bIgnoredByEveryone || m_pedInObjective->bIsInWater || m_pedInObjective->m_nPedState == PED_ARRESTED) { if (m_nPedState != PED_ARREST_PLAYER) SetIdle(); return CANT_ATTACK; } } if (victimPlayer && m_nPedType != PEDTYPE_COP && CharCreatedBy != MISSION_CHAR && FindPlayerPed()->m_pWanted->m_CurrentCops != 0) { SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); return CANT_ATTACK; } if (m_pedInObjective->m_fHealth <= 0.0f) { bObjectiveCompleted = true; bScriptObjectiveCompleted = true; return ATTACK_IN_PROGRESS; } bool canReachVictim = false; uint32 endOfAttack = 0; CWeaponInfo *wepInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); // Already calculated at the start // float distWithTargetSc = distWithTarget.Magnitude(); float maxDistToKeep = 0.3f; float wepRange = wepInfo->m_fRange / 2.f; if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED && !IsPlayer() && !(m_pedStats->m_flags & STAT_CAN_KICK)) wepRange -= 0.3f; if (distWithTargetSc <= 5.f && victimPlayer && !victimPlayer->m_bNoPosForMeleeAttack) { if (m_pedInObjective->EnteringCar() && wepRange > 2.f) { m_vecSeekPos = m_pedInObjective->GetPosition(); wepRange = 1.0f; maxDistToKeep = 0.5f; } else { int8 attackDir = victimPlayer->FindMeleeAttackPoint(this, distWithTarget, endOfAttack); if (attackDir == -1) { m_vecSeekPos = victimPlayer->GetPosition(); maxDistToKeep = 4.0f; } else { victimPlayer->GetMeleeAttackCoords(m_vecSeekPos, attackDir, wepRange); distWithTargetSc = (m_vecSeekPos - GetPosition()).Magnitude(); canReachVictim = true; } } } else { m_vecSeekPos = m_pedInObjective->GetPosition(); maxDistToKeep = Max(0.8f, 0.9f * wepRange); wepRange *= 1.1f; if (victimPlayer && victimPlayer->m_bNoPosForMeleeAttack) victimPlayer = nil; } if (m_pedInObjective->bInVehicle && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { CVehicle *vehOfTarget = m_pedInObjective->m_pMyVehicle; if (vehOfTarget){ if (vehOfTarget->bIsInWater || vehOfTarget->GetStatus() == STATUS_PLAYER_DISABLED || m_pedInObjective->IsPlayer() && CPad::GetPad(0)->ArePlayerControlsDisabled()) { SetIdle(); return WATCH_UNTIL_HE_DISAPPEARS; } SetLookFlag(vehOfTarget, false); if (m_nPedState != PED_CARJACK) { if (m_pedInObjective->m_nPedState != PED_ARRESTED) { if (m_nPedState != PED_ATTACK && !bKindaStayInSamePlace && !killPlayerInNoPoliceZone) { if (m_nPedType == PEDTYPE_COP || vehOfTarget->bIsBus) { GoToNearestDoor(vehOfTarget); } else { m_vehDoor = 0; if (m_pedInObjective == vehOfTarget->pDriver || vehOfTarget->bIsBus) { m_vehDoor = CAR_DOOR_LF; } else if (m_pedInObjective == vehOfTarget->pPassengers[0]) { m_vehDoor = CAR_DOOR_RF; } else if (m_pedInObjective == vehOfTarget->pPassengers[1]) { m_vehDoor = CAR_DOOR_LR; } else if (m_pedInObjective == vehOfTarget->pPassengers[2]) { m_vehDoor = CAR_DOOR_RR; } // Unused // GetPositionToOpenCarDoor(vehOfTarget, m_vehDoor); SetSeekCar(vehOfTarget, m_vehDoor); SetMoveState(PEDMOVE_RUN); } } } } } return ATTACK_IN_PROGRESS; } if (m_nMoveState == PEDMOVE_STILL && IsPedInControl()) { SetLookFlag(m_pedInObjective, false); if(m_nPedState == PED_IDLE || m_nPedState == PED_ATTACK || m_nPedState == PED_FIGHT) TurnBody(); } if (m_nPedType == PEDTYPE_COP && m_pedInObjective->IsPlayer()) { float maxArrestDist = 1.5f; if (((CCopPed*)this)->m_bDragsPlayerFromCar) { if (m_nPedState == PED_FALL) { maxArrestDist = 3.5f; } else if (m_nPedState != PED_DRAG_FROM_CAR) { ((CCopPed*)this)->m_bDragsPlayerFromCar = 0; } } if (distWithTargetSc < maxArrestDist) { if (m_pedInObjective->m_getUpTimer > CTimer::GetTimeInMilliseconds() || m_pedInObjective->m_nPedState == PED_DRAG_FROM_CAR) { ((CCopPed*)this)->SetArrestPlayer(m_pedInObjective); return WATCH_UNTIL_HE_DISAPPEARS; } } } if (distWithTargetSc > maxDistToKeep && !bKindaStayInSamePlace && m_nPedState != PED_ATTACK && (m_nPedState != PED_FIGHT || m_curFightMove == FIGHTMOVE_IDLE) && !killPlayerInNoPoliceZone) { bool goForward = false; if (m_nPedState == PED_FIGHT) { if (canReachVictim) { CVector attackAndVictimDist = m_vecSeekPos - m_pedInObjective->GetPosition(); CVector victimFarness = attackAndVictimDist / wepRange; CVector distVec = GetPosition() - m_pedInObjective->GetPosition(); float distSqr = distVec.MagnitudeSqr(); if (sq(wepRange) > distSqr && distSqr > 0.05f) { distVec.Normalise(); if (DotProduct2D(victimFarness, distVec) > Cos(DEGTORAD(30.f))) goForward = true; } } } if (goForward) { m_curFightMove = FIGHTMOVE_SHUFFLE_F; CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_SHUFFLE_B, 16.f)->SetFinishCallback(FinishFightMoveCB,this); m_fightState = FIGHTSTATE_NO_MOVE; m_fightButtonPressure = 0; m_takeAStepAfterAttack = false; } else if (bUsePedNodeSeek && !canReachVictim) { CVector bestCoords(0.0f, 0.0f, 0.0f); if (!m_pNextPathNode) FindBestCoordsFromNodes(m_vecSeekPos, &bestCoords); if (m_pNextPathNode) m_vecSeekPos = CPathFind::TakeWidthIntoAccountForWandering(m_pNextPathNode, m_randomSeed); SetSeek(m_vecSeekPos, m_distanceToCountSeekDone); } else { if (canReachVictim) SetSeek(m_vecSeekPos, maxDistToKeep); else SetSeek(m_pedInObjective, maxDistToKeep); } return ATTACK_IN_PROGRESS; } if (m_attackTimer < CTimer::GetTimeInMilliseconds() && distWithTargetSc < wepRange && m_pedInObjective->m_nPedState != PED_GETUP && m_pedInObjective->m_nPedState != PED_DRAG_FROM_CAR) { if (bIsDucking) { CAnimBlendAssociation* duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_DOWN); if (!duckAnim) duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_LOW); if (!duckAnim) duckAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_DUCK_WEAPON); if (duckAnim) { duckAnim->flags |= ASSOC_DELETEFADEDOUT; duckAnim->blendDelta = -4.0f; } bIsDucking = false; return CANT_ATTACK; } if (canReachVictim || !victimPlayer) { SetMoveState(PEDMOVE_STILL); SetAttack(m_pedInObjective); m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( m_pedInObjective->GetPosition().x, m_pedInObjective->GetPosition().y, GetPosition().x, GetPosition().y); SetShootTimer(CGeneral::GetRandomNumberInRange(0.f, 500.f)); int time; if (endOfAttack <= CTimer::GetTimeInMilliseconds()) time = CGeneral::GetRandomNumberInRange(100.0f, 1500.0f); else time = endOfAttack - CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(400.0f, 1500.0f); SetAttackTimer(time); bObstacleShowedUpDuringKillObjective = false; } return ATTACK_IN_PROGRESS; } else { if (!m_pedInObjective->m_pCurrentPhysSurface && m_pCurrentPhysSurface && b158_8) { b158_8 = false; bStopAndShoot = false; } if (m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT) { SetMoveState(PEDMOVE_STILL); if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED) StartFightAttack(0); } return ATTACK_IN_PROGRESS; } } bool CPed::CanBeDamagedByThisGangMember(CPed* who) { return m_gangFlags & (1 << (who->m_nPedType - PEDTYPE_GANG1)); } ================================================ FILE: src/peds/PedAttractor.cpp ================================================ #include "common.h" #include "PedAttractor.h" #include "General.h" #include "Vehicle.h" #include "World.h" #include "MemoryHeap.h" const int gcMaxSizeOfAtmQueue = 1; const int gcMaxSizeOfSeatQueue = 1; const int gcMaxSizeOfStopQueue = 5; const int gcMaxSizeOfPizzaQueue = 5; const int gcMaxSizeOfShelterQueue = 5; const int gcMaxSizeOfIceCreamQueue = 1; std::vector CPedShelterAttractor::ms_displacements; CPedAttractorManager* GetPedAttractorManager() { // mobile just has a static here: /* static CPedAttractorManager pedAttrMgr; return &pedAttrMgr; */ static CPedAttractorManager *pedAttrMgr; if(pedAttrMgr == nil){ PUSH_MEMID(MEMID_PED_ATTR); pedAttrMgr = new CPedAttractorManager; POP_MEMID(); } return pedAttrMgr; } CVehicleToEffect::CVehicleToEffect(CVehicle* pVehicle) : m_pVehicle(pVehicle) { m_effects[1].col = CRGBA(0, 0, 0, 0); m_effects[1].type = EFFECT_PED_ATTRACTOR; m_effects[1].pos = CVector(2.0f, 1.0f, 0.0f); m_effects[1].pedattr.useDir = CVector(-1.0f, 0.0f, 0.0f); m_effects[1].pedattr.queueDir = CVector(-1.0f, 0.0f, 0.0f); m_effects[1].pedattr.type = ATTRACTOR_ICECREAM; m_effects[3].col = CRGBA(0, 0, 0, 0); m_effects[3].type = EFFECT_PED_ATTRACTOR; m_effects[3].pos = CVector(2.0f, -0.5f, 0.0f); m_effects[3].pedattr.useDir = CVector(-1.0f, 0.0f, 0.0f); m_effects[3].pedattr.queueDir = CVector(-1.0f, 0.0f, 0.0f); m_effects[3].pedattr.type = ATTRACTOR_ICECREAM; m_effects[0].col = CRGBA(0, 0, 0, 0); m_effects[0].type = EFFECT_PED_ATTRACTOR; m_effects[0].pos = CVector(-2.0f, 1.0f, 0.0f); m_effects[0].pedattr.useDir = CVector(1.0f, 0.0f, 0.0f); m_effects[0].pedattr.queueDir = CVector(1.0f, 0.0f, 0.0f); m_effects[0].pedattr.type = ATTRACTOR_ICECREAM; m_effects[2].col = CRGBA(0, 0, 0, 0); m_effects[2].type = EFFECT_PED_ATTRACTOR; m_effects[2].pos = CVector(-2.0f, -0.5f, 0.0f); m_effects[2].pedattr.useDir = CVector(1.0f, 0.0f, 0.0f); m_effects[2].pedattr.queueDir = CVector(1.0f, 0.0f, 0.0f); m_effects[2].pedattr.type = ATTRACTOR_ICECREAM; } CVehicleToEffect& CVehicleToEffect::From(const CVehicleToEffect& other) { m_pVehicle = other.m_pVehicle; for (int i = 0; i < NUM_ATTRACTORS_FOR_ICECREAM_VAN; i++) { m_effects[i].col = other.m_effects[i].col; m_effects[i].type = other.m_effects[i].type; m_effects[i].pos = other.m_effects[i].pos; m_effects[i].pedattr = other.m_effects[i].pedattr; } return *this; } const C2dEffect* CVehicleToEffect::ChooseEffect(const CVector& pos) const { if (!m_pVehicle) return nil; if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetRight()) > 0.0f) { if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetForward()) > 0.0f) return &m_effects[1]; else return &m_effects[3]; } else { if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetForward()) > 0.0f) return &m_effects[0]; else return &m_effects[2]; } } bool CVehicleToEffect::HasThisEffect(C2dEffect* pEffect) const { for (int i = 0; i < NUM_ATTRACTORS_FOR_ICECREAM_VAN; i++) { if (pEffect == &m_effects[i]) return true; } return false; } const C2dEffect* CPedAttractorManager::GetEffectForIceCreamVan(CVehicle* pVehicle, const CVector& pos) { if (!vVehicleToEffect.empty()) { for (std::vector::const_iterator assoc = vVehicleToEffect.begin(); assoc != vVehicleToEffect.end(); ++assoc) { if (assoc->GetVehicle() == pVehicle) return assoc->ChooseEffect(pos); } } PUSH_MEMID(MEMID_PED_ATTR); CVehicleToEffect effect(pVehicle); vVehicleToEffect.push_back(effect); POP_MEMID(); #ifdef FIX_BUGS return vVehicleToEffect.back().ChooseEffect(pos); #else return effect.ChooseEffect(pos); #endif } CVehicle* CPedAttractorManager::GetIceCreamVanForEffect(C2dEffect* pEffect) { if (vVehicleToEffect.empty()) return nil; for (std::vector::const_iterator assoc = vVehicleToEffect.begin(); assoc != vVehicleToEffect.end(); ++assoc) { if (assoc->HasThisEffect(pEffect)) return assoc->GetVehicle(); } return nil; } const CPedAttractor* CPedAttractorManager::FindAssociatedAttractor(const C2dEffect* pEffect, std::vector& vecAttractors) { if (vecAttractors.empty()) return nil; for (std::vector::const_iterator attractor = vecAttractors.begin(); attractor != vecAttractors.end(); ++attractor) { if ((*attractor)->GetEffect() == pEffect) return *attractor; } return nil; } void CPedAttractorManager::RemoveIceCreamVanEffects(C2dEffect* pEffect) { CVehicle* pVehicle = GetIceCreamVanForEffect(pEffect); if (!pVehicle) return; if (vVehicleToEffect.empty()) return; for (std::vector::iterator assoc = vVehicleToEffect.begin(); assoc != vVehicleToEffect.end();) { if (assoc->GetVehicle() != pVehicle) { ++assoc; continue; } uint32 total = 0; for (uint32 j = 0; j < NUM_ATTRACTORS_FOR_ICECREAM_VAN; j++) { if (FindAssociatedAttractor(assoc->GetEffect(j), vIceCreamAttractors)) total++; } if (total > 0) ++assoc; else assoc = vVehicleToEffect.erase(assoc); } } CPedAttractor::CPedAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : p2dEffect(pEffect), m_nMaxPedsInAttractor(maxpeds), m_fQueueDistance(qdist), m_fTimeInWaitQueue(waitTime), m_fTimeInApproachingQueue(approachTime), m_fDistanceToUseAttractor(distance), m_fAcceptableHeading(headingdiff), m_fMaxPositionDisplacement(posdisp), m_fMaxHeadingDisplacement(headdisp) { CPedAttractorManager::ComputeEffectPos(pEffect, matrix, vecEffectPos); CPedAttractorManager::ComputeEffectQueueDir(pEffect, matrix, vecQueueDir); CPedAttractorManager::ComputeEffectUseDir(pEffect, matrix, vecUseDir); } void CPedPizzaAttractor::UpdatePedStateOnDeparture(CPed* pPed) const { if (pPed->m_nPedMoney > 10) pPed->m_nPedMoney -= 10; else pPed->m_nPedMoney = 0; } void CPedAtmAttractor::UpdatePedStateOnDeparture(CPed* pPed) const { pPed->m_nPedMoney += 20 * CGeneral::GetRandomNumberInRange(1, 51); }; float CPedAttractor::ComputeDeltaHeading() const { return CGeneral::GetRandomNumberInRange(-m_fMaxHeadingDisplacement, m_fMaxHeadingDisplacement); } float CPedAttractor::ComputeDeltaPos() const { return CGeneral::GetRandomNumberInRange(-m_fMaxPositionDisplacement, m_fMaxPositionDisplacement); } void CPedAttractor::ComputeAttractTime(int32 id, bool approacher, float& time) const { if (approacher) time = m_fTimeInApproachingQueue; else time = m_fTimeInWaitQueue; } void CPedAttractor::ComputeAttractPos(int32 qid, CVector& pos) const { if (!p2dEffect) return; pos = vecEffectPos - qid * vecQueueDir * m_fQueueDistance; if (qid != 0) { pos.x += ComputeDeltaPos(); pos.y += ComputeDeltaPos(); } } CVector CPedShelterAttractor::GetDisplacement(int32 qid) const { if (ms_displacements.empty()) { int i = 0; while (i < gcMaxSizeOfShelterQueue) { float fRandomAngle = CGeneral::GetRandomNumberInRange(0.0f, TWOPI); float fRandomOffset = CGeneral::GetRandomNumberInRange(0.0f, 2.0f); CVector vecDisplacement(fRandomOffset * Sin(fRandomAngle), fRandomOffset * Cos(fRandomAngle), 0.0f); bool close = false; for (std::vector::const_iterator v = ms_displacements.begin(); v != ms_displacements.end(); ++v) { if ((*v - vecDisplacement).Magnitude() < 1.0f) { close = true; break; } } if (!close) { ms_displacements.push_back(vecDisplacement); i++; } } } return ms_displacements[qid]; } void CPedShelterAttractor::ComputeAttractPos(int32 qid, CVector& pos) const { if (!p2dEffect) return; pos = vecEffectPos + GetDisplacement(qid); } void CPedAttractor::ComputeAttractHeading(int32 qid, float& heading) const { heading = CGeneral::GetRadianAngleBetweenPoints(qid != 0 ? vecQueueDir.x : vecUseDir.x, qid != 0 ? vecQueueDir.y : vecUseDir.y, 0.0f, 0.0f); if (qid != 0) heading += ComputeDeltaHeading(); } void CPedShelterAttractor::ComputeAttractHeading(int32 qid, float& heading) const { heading = CGeneral::GetRandomNumberInRange(0.0f, TWOPI); } bool CPedAttractor::RegisterPed(CPed* pPed) { for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { if (*pPedIt == pPed) { vApproachingQueue.erase(pPedIt); return false; } } if (GetNoOfRegisteredPeds() >= m_nMaxPedsInAttractor) return 0; vApproachingQueue.push_back(pPed); CVector pos; float heading; float time; int32 slot = ComputeFreeSlot(); ComputeAttractPos(slot, pos); ComputeAttractHeading(slot, heading); ComputeAttractTime(slot, false, time); pPed->SetNewAttraction(this, pos, heading, time, slot); return true; } static bool IsPedUsingAttractorOfThisType(int8 type, CPed* pPed) { switch (type) { case ATTRACTOR_ATM: if (pPed->m_objective == OBJECTIVE_GOTO_ATM_ON_FOOT) return true; break; case ATTRACTOR_SEAT: if (pPed->m_objective == OBJECTIVE_GOTO_SEAT_ON_FOOT) return true; break; case ATTRACTOR_STOP: if (pPed->m_objective == OBJECTIVE_GOTO_BUS_STOP_ON_FOOT || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT_AT_BUS_STOP || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT) return true; break; case ATTRACTOR_PIZZA: if (pPed->m_objective == OBJECTIVE_GOTO_PIZZA_ON_FOOT || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT) return true; break; case ATTRACTOR_SHELTER: if (pPed->m_objective == OBJECTIVE_GOTO_SHELTER_ON_FOOT || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT_AT_SHELTER) return true; break; case ATTRACTOR_ICECREAM: if (pPed->m_objective == OBJECTIVE_GOTO_ICE_CREAM_VAN_ON_FOOT || pPed->m_objective == OBJECTIVE_WAIT_ON_FOOT_AT_ICE_CREAM_VAN) return true; break; } return false; } bool CPedAttractor::DeRegisterPed(CPed* pPed) { for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { if (*pPedIt != pPed) continue; pPed->m_attractor = nil; pPed->m_positionInQueue = -1; pPed->bHasAlreadyUsedAttractor = true; if (IsPedUsingAttractorOfThisType(p2dEffect->pedattr.type, pPed)) pPed->SetObjective(OBJECTIVE_NONE); else if (pPed->GetPedState() != PED_IDLE && pPed->GetPedState() != PED_NONE) { vApproachingQueue.erase(pPedIt); return true; } pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); vApproachingQueue.erase(pPedIt); return true; } return BroadcastDeparture(pPed); } bool CPedAttractor::BroadcastArrival(CPed* pPed) { for (std::vector::const_iterator pPedIt = vWaitingQueue.begin(); pPedIt != vWaitingQueue.end(); ++pPedIt) { if (*pPedIt == pPed) return false; } vWaitingQueue.push_back(pPed); for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { if (*pPedIt == pPed) { vApproachingQueue.erase(pPedIt); break; } } for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { CPed* pPed = *pPedIt; CVector pos; float heading; float time; int32 slot = ComputeFreeSlot(); ComputeAttractPos(slot, pos); ComputeAttractHeading(slot, heading); ComputeAttractTime(slot, false, time); pPed->SetNewAttraction(this, pos, heading, time, slot); } return true; } bool CPedAttractor::BroadcastDeparture(CPed* pPed) { int qid = -1; for (uint32 i = 0; i < vWaitingQueue.size(); i++){ if (vWaitingQueue[i] == pPed) qid = i; } if (qid < 0) return false; for (uint32 i = qid + 1; i < vWaitingQueue.size(); i++) { CVector pos; float heading; float time; ComputeAttractPos(i - 1, pos); ComputeAttractHeading(i - 1, heading); ComputeAttractTime(i - 1, true, time); pPed->SetNewAttraction(this, pos, heading, time, i - 1); } pPed->m_attractor = nil; pPed->m_positionInQueue = -1; pPed->bHasAlreadyUsedAttractor = true; if (!IsPedUsingAttractorOfThisType(p2dEffect->pedattr.type, pPed)) { if (pPed->GetPedState() == PED_IDLE || pPed->GetPedState() == PED_NONE) pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); } else { pPed->SetObjective(OBJECTIVE_NONE); if (qid == 0) pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(vecQueueDir.x, vecQueueDir.y)); else if (qid == vWaitingQueue.size() - 1) pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); else pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.y, -vecQueueDir.x)); UpdatePedStateOnDeparture(pPed); } vWaitingQueue.erase(vWaitingQueue.begin() + qid); for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { CPed* pPed = *pPedIt; CVector pos; float heading; float time; int32 slot = ComputeFreeSlot(); ComputeAttractPos(slot, pos); ComputeAttractHeading(slot, heading); ComputeAttractTime(slot, false, time); pPed->SetNewAttraction(this, pos, heading, time, slot); } return true; } bool CPedShelterAttractor::BroadcastDeparture(CPed* pPed) { int qid = -1; for (uint32 i = 0; i < vWaitingQueue.size(); i++) { if (vWaitingQueue[i] == pPed) qid = i; } if (qid < 0) return false; pPed->m_attractor = nil; pPed->m_positionInQueue = -1; pPed->bHasAlreadyUsedAttractor = true; if (!IsPedUsingAttractorOfThisType(p2dEffect->pedattr.type, pPed)) { if (pPed->GetPedState() == PED_IDLE || pPed->GetPedState() == PED_NONE) pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); } else { pPed->SetObjective(OBJECTIVE_NONE); if (qid == 0) pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(vecQueueDir.x, vecQueueDir.y)); else if (qid == vWaitingQueue.size() - 1) pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.x, -vecQueueDir.y)); else pPed->SetWanderPath(CGeneral::GetNodeHeadingFromVector(-vecQueueDir.y, -vecQueueDir.x)); UpdatePedStateOnDeparture(pPed); } vWaitingQueue.erase(vWaitingQueue.begin() + qid); for (std::vector::iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { CPed* pPed = *pPedIt; CVector pos; float heading; float time; int32 slot = ComputeFreeSlot(); ComputeAttractPos(slot, pos); ComputeAttractHeading(slot, heading); ComputeAttractTime(slot, false, time); pPed->SetNewAttraction(this, pos, heading, time, slot); } return true; } bool CPedAttractor::IsRegisteredWithPed(CPed* pPed) const { for (std::vector::const_iterator pPedIt = vWaitingQueue.begin(); pPedIt != vWaitingQueue.end(); ++pPedIt) { if (*pPedIt == pPed) return true; } for (std::vector::const_iterator pPedIt = vApproachingQueue.begin(); pPedIt != vApproachingQueue.end(); ++pPedIt) { if (*pPedIt == pPed) { return true; } } return false; } bool CPedAttractor::IsInQueue(CPed* pPed) const { for (std::vector::const_iterator pPedIt = vWaitingQueue.begin(); pPedIt != vWaitingQueue.end(); ++pPedIt) { if (*pPedIt == pPed) return true; } return false; } CPedAttractor* CPedAttractorManager::RegisterPedWithAttractor(CPed* pPed, C2dEffect* pEffect, const CMatrix& matrix) { if (pEffect->type != EFFECT_PED_ATTRACTOR) return nil; if (IsPedRegisteredWithEffect(pPed)) return nil; switch (pEffect->pedattr.type) { case ATTRACTOR_ATM: return RegisterPed(pPed, pEffect, matrix, vAtmAttractors); case ATTRACTOR_SEAT: return RegisterPed(pPed, pEffect, matrix, vSeatAttractors); case ATTRACTOR_STOP: return RegisterPed(pPed, pEffect, matrix, vStopAttractors); case ATTRACTOR_PIZZA: return RegisterPed(pPed, pEffect, matrix, vPizzaAttractors); case ATTRACTOR_SHELTER: return RegisterPed(pPed, pEffect, matrix, vShelterAttractors); case ATTRACTOR_ICECREAM: return RegisterPed(pPed, pEffect, matrix, vIceCreamAttractors); } return nil; } bool CPedAttractorManager::DeRegisterPed(CPed* pPed, CPedAttractor* pAttractor) { if (!pAttractor) return false; if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) return nil; if (!IsPedRegisteredWithEffect(pPed)) return nil; switch (pAttractor->GetEffect()->pedattr.type) { case ATTRACTOR_ATM: return DeRegisterPed(pPed, pAttractor, vAtmAttractors); case ATTRACTOR_SEAT: return DeRegisterPed(pPed, pAttractor, vSeatAttractors); case ATTRACTOR_STOP: return DeRegisterPed(pPed, pAttractor, vStopAttractors); case ATTRACTOR_PIZZA: return DeRegisterPed(pPed, pAttractor, vPizzaAttractors); case ATTRACTOR_SHELTER: return DeRegisterPed(pPed, pAttractor, vShelterAttractors); case ATTRACTOR_ICECREAM: return DeRegisterPed(pPed, pAttractor, vIceCreamAttractors); } return nil; } bool CPedAttractorManager::BroadcastArrival(CPed* pPed, CPedAttractor* pAttractor) { if (!pAttractor) return false; if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) return nil; if (!IsPedRegisteredWithEffect(pPed)) return nil; switch (pAttractor->GetEffect()->pedattr.type) { case ATTRACTOR_ATM: return BroadcastArrival(pPed, pAttractor, vAtmAttractors); case ATTRACTOR_SEAT: return BroadcastArrival(pPed, pAttractor, vSeatAttractors); case ATTRACTOR_STOP: return BroadcastArrival(pPed, pAttractor, vStopAttractors); case ATTRACTOR_PIZZA: return BroadcastArrival(pPed, pAttractor, vPizzaAttractors); case ATTRACTOR_SHELTER: return BroadcastArrival(pPed, pAttractor, vShelterAttractors); case ATTRACTOR_ICECREAM: return BroadcastArrival(pPed, pAttractor, vIceCreamAttractors); } return nil; } bool CPedAttractorManager::BroadcastDeparture(CPed* pPed, CPedAttractor* pAttractor) { if (!pAttractor) return false; if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) return nil; if (!IsPedRegisteredWithEffect(pPed)) return nil; switch (pAttractor->GetEffect()->pedattr.type) { case ATTRACTOR_ATM: return BroadcastDeparture(pPed, pAttractor, vAtmAttractors); case ATTRACTOR_SEAT: return BroadcastDeparture(pPed, pAttractor, vSeatAttractors); case ATTRACTOR_STOP: return BroadcastDeparture(pPed, pAttractor, vStopAttractors); case ATTRACTOR_PIZZA: return BroadcastDeparture(pPed, pAttractor, vPizzaAttractors); case ATTRACTOR_SHELTER: return BroadcastDeparture(pPed, pAttractor, vShelterAttractors); case ATTRACTOR_ICECREAM: return BroadcastDeparture(pPed, pAttractor, vIceCreamAttractors); } return nil; } bool CPedAttractorManager::IsAtHeadOfQueue(CPed* pPed, CPedAttractor* pAttractor) { if (!pAttractor) return false; if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) return nil; if (!IsPedRegisteredWithEffect(pPed)) return nil; switch (pAttractor->GetEffect()->pedattr.type) { case ATTRACTOR_ATM: return IsAtHeadOfQueue(pPed, pAttractor, vAtmAttractors); case ATTRACTOR_SEAT: return IsAtHeadOfQueue(pPed, pAttractor, vSeatAttractors); case ATTRACTOR_STOP: return IsAtHeadOfQueue(pPed, pAttractor, vStopAttractors); case ATTRACTOR_PIZZA: return IsAtHeadOfQueue(pPed, pAttractor, vPizzaAttractors); case ATTRACTOR_SHELTER: return IsAtHeadOfQueue(pPed, pAttractor, vShelterAttractors); case ATTRACTOR_ICECREAM: return IsAtHeadOfQueue(pPed, pAttractor, vIceCreamAttractors); } return nil; } bool CPedAttractorManager::IsInQueue(CPed* pPed, CPedAttractor* pAttractor) { if (!pAttractor) return false; if (pAttractor->GetEffect()->type != EFFECT_PED_ATTRACTOR) return nil; if (!IsPedRegisteredWithEffect(pPed)) return nil; switch (pAttractor->GetEffect()->pedattr.type) { case ATTRACTOR_ATM: return IsInQueue(pPed, pAttractor, vAtmAttractors); case ATTRACTOR_SEAT: return IsInQueue(pPed, pAttractor, vSeatAttractors); case ATTRACTOR_STOP: return IsInQueue(pPed, pAttractor, vStopAttractors); case ATTRACTOR_PIZZA: return IsInQueue(pPed, pAttractor, vPizzaAttractors); case ATTRACTOR_SHELTER: return IsInQueue(pPed, pAttractor, vShelterAttractors); case ATTRACTOR_ICECREAM: return IsInQueue(pPed, pAttractor, vIceCreamAttractors); } return nil; } bool CPedAttractorManager::HasEmptySlot(const C2dEffect* pEffect) { if (!pEffect) return false; if (pEffect->type != EFFECT_PED_ATTRACTOR) return nil; const CPedAttractor* pAttractor; switch (pEffect->pedattr.type) { case ATTRACTOR_ATM: pAttractor = FindAssociatedAttractor(pEffect, vAtmAttractors); break; case ATTRACTOR_SEAT: pAttractor = FindAssociatedAttractor(pEffect, vSeatAttractors); break; case ATTRACTOR_STOP: pAttractor = FindAssociatedAttractor(pEffect, vStopAttractors); break; case ATTRACTOR_PIZZA: pAttractor = FindAssociatedAttractor(pEffect, vPizzaAttractors); break; case ATTRACTOR_SHELTER: pAttractor = FindAssociatedAttractor(pEffect, vShelterAttractors); break; case ATTRACTOR_ICECREAM: pAttractor = FindAssociatedAttractor(pEffect, vIceCreamAttractors); break; default: return true; } if (!pAttractor) return true; return pAttractor->GetNoOfRegisteredPeds() < pAttractor->GetMaxPedsInAttractor(); } bool CPedAttractorManager::IsPedRegisteredWithEffect(CPed* pPed) { return IsPedRegistered(pPed, vAtmAttractors) || IsPedRegistered(pPed, vSeatAttractors) || IsPedRegistered(pPed, vStopAttractors) || IsPedRegistered(pPed, vPizzaAttractors) || IsPedRegistered(pPed, vShelterAttractors) || IsPedRegistered(pPed, vIceCreamAttractors); } void CPedAttractorManager::ComputeEffectPos(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos) { pos = matrix.GetPosition() + Multiply3x3(matrix, pEffect->pos); } void CPedAttractorManager::ComputeEffectQueueDir(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos) { pos = Multiply3x3(matrix, pEffect->pedattr.queueDir); } void CPedAttractorManager::ComputeEffectUseDir(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos) { pos = Multiply3x3(matrix, pEffect->pedattr.useDir); } CPedAttractor* CPedAttractorManager::RegisterPed(CPed* pPed, C2dEffect* pEffect, const CMatrix& matrix, std::vector& vecAttractors) { CPedAttractor* pRegisteredAttractor = nil; for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { CPedAttractor* pAttractor = *pAttractorIt; CVector vEffectPos; ComputeEffectPos(pAttractor->GetEffect(), matrix, vEffectPos); if (pAttractor->GetEffect() == pEffect && vEffectPos == pAttractor->GetEffectPos()) { if (!IsApproachable(pEffect, matrix, pAttractor->ComputeFreeSlot(), pPed)) return nil; pRegisteredAttractor = pAttractor; break; } } if (pRegisteredAttractor || !IsApproachable(pEffect, matrix, 0, pPed)) { if (pRegisteredAttractor) pRegisteredAttractor->RegisterPed(pPed); return pRegisteredAttractor; } PUSH_MEMID(MEMID_PED_ATTR); switch (pEffect->pedattr.type) { case ATTRACTOR_ATM: pRegisteredAttractor = new CPedAtmAttractor(pEffect, matrix, gcMaxSizeOfAtmQueue, 1.0f, 30000.0f, 3000.0f, 0.2f, 0.15f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; case ATTRACTOR_SEAT: pRegisteredAttractor = new CPedSeatAttractor(pEffect, matrix, gcMaxSizeOfSeatQueue, 1.0f, 30000.0f, 3000.0f, 0.125f, 0.1f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; case ATTRACTOR_STOP: pRegisteredAttractor = new CPedStopAttractor(pEffect, matrix, gcMaxSizeOfStopQueue, 1.0f, 30000.0f, 3000.0f, 0.2f, 0.1f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; case ATTRACTOR_PIZZA: pRegisteredAttractor = new CPedPizzaAttractor(pEffect, matrix, gcMaxSizeOfPizzaQueue, 1.0f, 30000.0f, 3000.0f, 0.2f, 0.1f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; case ATTRACTOR_SHELTER: pRegisteredAttractor = new CPedShelterAttractor(pEffect, matrix, gcMaxSizeOfShelterQueue, 1.0f, 30000.0f, 3000.0f, 0.5f, 6.28f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; case ATTRACTOR_ICECREAM: pRegisteredAttractor = new CPedIceCreamAttractor(pEffect, matrix, gcMaxSizeOfIceCreamQueue, 1.0f, 30000.0f, 3000.0f, 0.2f, 0.3f, 0.1f, 0.1f); vecAttractors.push_back(pRegisteredAttractor); break; } POP_MEMID(); if (pRegisteredAttractor) pRegisteredAttractor->RegisterPed(pPed); return pRegisteredAttractor; } bool CPedAttractorManager::DeRegisterPed(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) { if (!pAttractor) return false; CPedAttractor* pFound = nil; for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { if (*pAttractorIt == pAttractor) { pFound = *pAttractorIt; break; } } if (!pFound) return false; pFound->DeRegisterPed(pPed); if (pFound->GetNoOfRegisteredPeds() != 0) return true; for (std::vector::iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { if (*pAttractorIt == pAttractor) { vecAttractors.erase(pAttractorIt); break; } } delete pAttractor; return true; } bool CPedAttractorManager::BroadcastArrival(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) { if (!pAttractor) return false; CPedAttractor* pFound = nil; for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { if (*pAttractorIt == pAttractor) { pFound = *pAttractorIt; break; } } if (!pFound) return false; pFound->BroadcastArrival(pPed); return true; } bool CPedAttractorManager::BroadcastDeparture(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) { if (!pAttractor) return false; CPedAttractor* pFound = nil; for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { if (*pAttractorIt == pAttractor) { pFound = *pAttractorIt; break; } } if (!pFound) return false; pFound->DeRegisterPed(pPed); if (pFound->GetNoOfRegisteredPeds() != 0) return true; for (std::vector::iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { if (*pAttractorIt == pAttractor) { vecAttractors.erase(pAttractorIt); break; } } delete pAttractor; return true; } bool CPedAttractorManager::IsInQueue(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) { if (!pAttractor) return false; for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { if (*pAttractorIt == pAttractor) { return (*pAttractorIt)->IsInQueue(pPed); } } return false; } bool CPedAttractorManager::IsAtHeadOfQueue(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors) { if (!pAttractor) return false; for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { if (*pAttractorIt == pAttractor) { return (*pAttractorIt)->IsAtHeadOfQueue(pPed); } } return false; } bool CPedAttractorManager::IsPedRegistered(CPed* pPed, std::vector& vecAttractors) { for (std::vector::const_iterator pAttractorIt = vecAttractors.begin(); pAttractorIt != vecAttractors.end(); ++pAttractorIt) { if ((*pAttractorIt)->IsRegisteredWithPed(pPed)) return true; } return false; } bool CPedAttractorManager::IsApproachable(C2dEffect* pEffect, const CMatrix& matrix, int32, CPed* pPed) { if (pEffect->pedattr.type == ATTRACTOR_SHELTER) { CVector pos; ComputeEffectPos(pEffect, matrix, pos); return CWorld::GetIsLineOfSightClear(pPed->GetPosition(), pos, true, false, false, false, false, false); } CVector vecUseDir, vecEffectPos; ComputeEffectUseDir(pEffect, matrix, vecUseDir); ComputeEffectPos(pEffect, matrix, vecEffectPos); float dp = -DotProduct(vecUseDir, vecEffectPos); if (pEffect->pedattr.type == ATTRACTOR_ATM || pEffect->pedattr.type == ATTRACTOR_PIZZA || pEffect->pedattr.type == ATTRACTOR_ICECREAM) { vecUseDir = -vecUseDir; dp = -dp; } if (dp + DotProduct(vecEffectPos, pPed->GetPosition()) > 0.0f) { CVector vecPedToAttractor = pPed->GetPosition() - vecEffectPos; vecPedToAttractor.Normalise(); if (DotProduct(vecPedToAttractor, vecUseDir) > 0.25f && CWorld::IsWanderPathClear(pPed->GetPosition(), vecEffectPos, 2.0f, 0)) return true; } return false; } ================================================ FILE: src/peds/PedAttractor.h ================================================ #pragma once #include "common.h" #include #include "2dEffect.h" #include "Ped.h" #define NUM_ATTRACTORS_FOR_ICECREAM_VAN 4 class CPedAttractor; class CVehicleToEffect { CVehicle* m_pVehicle; C2dEffect m_effects[NUM_ATTRACTORS_FOR_ICECREAM_VAN]; public: CVehicleToEffect(CVehicle* pVehicle); const C2dEffect* ChooseEffect(const CVector& pos) const; CVehicleToEffect& From(const CVehicleToEffect& other); CVehicleToEffect& operator=(const CVehicleToEffect& other) { return From(other); } ~CVehicleToEffect() { m_pVehicle = nil; } CVehicle* GetVehicle() const { return m_pVehicle; } bool HasThisEffect(C2dEffect* pEffect) const; const C2dEffect* GetEffect(int32 i) const { return &m_effects[i]; } }; class CPedAttractorManager { std::vector vAtmAttractors; std::vector vSeatAttractors; std::vector vStopAttractors; std::vector vPizzaAttractors; std::vector vShelterAttractors; std::vector vIceCreamAttractors; std::vector vVehicleToEffect; public: CPedAttractor* RegisterPedWithAttractor(CPed* pPed, C2dEffect* pEffect, const CMatrix& matrix); CPedAttractor* RegisterPed(CPed* pPed, C2dEffect* pEffect, const CMatrix& matrix, std::vector& vecAttractors); bool BroadcastArrival(CPed* pPed, CPedAttractor* pAttractor); bool BroadcastArrival(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); const C2dEffect* GetEffectForIceCreamVan(CVehicle* pVehicle, const CVector& pos); bool IsApproachable(C2dEffect* pEffect, const CMatrix& matrix, int32, CPed* pPed); void RemoveIceCreamVanEffects(C2dEffect* pEffect); bool HasEmptySlot(const C2dEffect* pEffect); const CPedAttractor* FindAssociatedAttractor(const C2dEffect* pEffect, std::vector& vecAttractors); bool IsInQueue(CPed* pPed, CPedAttractor* pAttractor); bool IsInQueue(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); bool IsAtHeadOfQueue(CPed* pPed, CPedAttractor* pAttractor); bool IsAtHeadOfQueue(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); bool BroadcastDeparture(CPed* pPed, CPedAttractor* pAttractor); bool BroadcastDeparture(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); bool DeRegisterPed(CPed* pPed, CPedAttractor* pAttractor); bool DeRegisterPed(CPed* pPed, CPedAttractor* pAttractor, std::vector& vecAttractors); bool IsPedRegisteredWithEffect(CPed* pPed); bool IsPedRegistered(CPed* pPed, std::vector& vecAttractors); CVehicle* GetIceCreamVanForEffect(C2dEffect* pEffect); static void ComputeEffectPos(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos); static void ComputeEffectQueueDir(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos); static void ComputeEffectUseDir(const C2dEffect* pEffect, const CMatrix& matrix, CVector& pos); }; CPedAttractorManager* GetPedAttractorManager(); enum ePedAttractorType { ATTRACTOR_ATM = 0, ATTRACTOR_SEAT, ATTRACTOR_STOP, ATTRACTOR_PIZZA, ATTRACTOR_SHELTER, ATTRACTOR_ICECREAM }; class CPedAttractor { protected: C2dEffect* p2dEffect; std::vector vApproachingQueue; std::vector vWaitingQueue; int32 m_nMaxPedsInAttractor; float m_fQueueDistance; float m_fTimeInWaitQueue; float m_fTimeInApproachingQueue; float m_fDistanceToUseAttractor; float m_fAcceptableHeading; float m_fMaxPositionDisplacement; float m_fMaxHeadingDisplacement; CVector vecEffectPos; CVector vecQueueDir; CVector vecUseDir; public: virtual float GetHeadOfQueueWaitTime() { return 0.0f; } virtual ~CPedAttractor() {}; virtual ePedAttractorType GetType() const = 0; virtual void UpdatePedStateOnDeparture(CPed* pPed) const = 0; virtual bool IsAtHeadOfQueue(CPed* pPed) const { return vWaitingQueue.front() == pPed; } virtual void ComputeAttractPos(int32 id, CVector& pos) const; virtual void ComputeAttractHeading(int32 id, float& pHeading) const; virtual bool BroadcastDeparture(CPed* pPed); bool IsRegisteredWithPed(CPed* pPed) const; bool DeRegisterPed(CPed* pPed); float ComputeDeltaHeading() const; float ComputeDeltaPos() const; void ComputeAttractTime(int32 id, bool, float& time) const; int32 GetNoOfRegisteredPeds() const { return (int32)(vWaitingQueue.size() + vApproachingQueue.size()); } int32 ComputeFreeSlot() const { return (int32)vWaitingQueue.size(); } bool IsInQueue(CPed*) const; bool RegisterPed(CPed*); bool BroadcastArrival(CPed*); CPedAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp); C2dEffect* GetEffect() const { return p2dEffect; } const CVector& GetEffectPos() const { return vecEffectPos; } int32 GetMaxPedsInAttractor() const { return m_nMaxPedsInAttractor; } float GetDistanceToCountSeekDone() const { return m_fDistanceToUseAttractor; } float GetAcceptableHeading() const { return m_fAcceptableHeading; } }; class CPedAtmAttractor : public CPedAttractor { public: virtual ePedAttractorType GetType() const { return ATTRACTOR_ATM; }; virtual void UpdatePedStateOnDeparture(CPed* pPed) const; CPedAtmAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) {}; }; class CPedIceCreamAttractor : public CPedAttractor { public: virtual ~CPedIceCreamAttractor() { GetPedAttractorManager()->RemoveIceCreamVanEffects(p2dEffect); } virtual ePedAttractorType GetType() const { return ATTRACTOR_ICECREAM; } virtual void UpdatePedStateOnDeparture(CPed* pPed) const {}; CPedIceCreamAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) {}; }; class CPedPizzaAttractor : public CPedAttractor { public: virtual float GetHeadOfQueueWaitTime() { return 2000.0f; } virtual ePedAttractorType GetType() const { return ATTRACTOR_PIZZA; } virtual void UpdatePedStateOnDeparture(CPed* pPed) const; CPedPizzaAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) {}; }; class CPedSeatAttractor : public CPedAttractor { public: virtual ePedAttractorType GetType() const { return ATTRACTOR_SEAT; } virtual void UpdatePedStateOnDeparture(CPed* pPed) const {}; CPedSeatAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) {}; }; class CPedShelterAttractor : public CPedAttractor { static std::vector ms_displacements; public: virtual ePedAttractorType GetType() const { return ATTRACTOR_SHELTER; } virtual bool BroadcastDeparture(CPed*); virtual void UpdatePedStateOnDeparture(CPed* pPed) const {}; virtual bool IsAtHeadOfQueue(CPed* pPed) const { return true; } virtual void ComputeAttractPos(int qid, CVector& pos) const; virtual void ComputeAttractHeading(int qid, float& heading) const; CPedShelterAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) {}; CVector GetDisplacement(int32 qid) const; }; class CPedStopAttractor : public CPedAttractor { public: virtual ePedAttractorType GetType() const { return ATTRACTOR_STOP; } virtual void UpdatePedStateOnDeparture(CPed* pPed) const {}; CPedStopAttractor(C2dEffect* pEffect, const CMatrix& matrix, int32 maxpeds, float qdist, float waitTime, float approachTime, float distance, float headingdiff, float posdisp, float headdisp) : CPedAttractor(pEffect, matrix, maxpeds, qdist, waitTime, approachTime, distance, headingdiff, posdisp, headdisp) {}; }; ================================================ FILE: src/peds/PedChat.cpp ================================================ #include "common.h" #include "Camera.h" #include "DMAudio.h" #include "General.h" #include "Ped.h" // Corresponds to ped sounds (from SOUND_PED_DEATH to SOUND_PED_TAXI_CALL) PedAudioData CommentWaitTime[56] = { { 500, 800, 500, 2 }, { 500, 800, 500, 2 }, { 500, 800, 500, 2 }, { 500, 800, 500, 2 }, { 100, 2, 100, 2 }, { 500, 500, 2000, 1000 }, { 2000, 50, 2050, 1000 }, { 5000, 2000, 7000, 3000 }, { 5000, 2000, 7000, 3000 }, { 300, 200, 500, 200 }, { 3000, 1000, 4000, 1000 }, { 6000, 6000, 6000, 6000 }, { 4000, 1000, 5000, 1000 }, { 3000, 1000, 4000, 1000 }, { 1000, 1000, 2000, 2000 }, { 1000, 500, 2000, 1500 }, { 1700, 1000, 3000, 1000 }, { 800, 200, 1000, 500 }, { 800, 200, 1000, 500 }, { 800, 400, 2000, 1000 }, { 800, 400, 2000, 1000 }, { 2000, 2000, 4000, 4000 }, { 2000, 2000, 4000, 1000 }, { 4000, 1000, 5000, 1000 }, { 800, 400, 1200, 500 }, { 5000, 1000, 6000, 2000 }, { 5000, 1000, 6000, 2000 }, { 5000, 1000, 6000, 2000 }, { 5000, 1000, 6000, 2000 }, { 5000, 1000, 6000, 2000 }, { 5000, 1000, 6000, 2000 }, { 5000, 1000, 6000, 2000 }, { 4000, 2000, 7000, 2000 }, { 1000, 300, 2000, 1000 }, { 1500, 1000, 2500, 1000 }, { 200, 200, 200, 200 }, { 2000, 1000, 4000, 1000 }, { 2000, 1000, 4000, 1000 }, { 1000, 500, 3000, 1000 }, { 1000, 500, 1000, 1000 }, { 3000, 2000, 5000, 1000 }, { 3000, 2000, 5000, 1000 }, { 3000, 2000, 3000, 2000 }, { 2000, 1000, 3000, 1000 }, { 2500, 1000, 5000, 5000 }, { 2000, 1000, 3000, 2000 }, { 4000, 1000, 5000, 1000 }, { 1000, 500, 2000, 4000 }, { 1000, 500, 2000, 5000 }, { 2000, 500, 2500, 500 }, { 1000, 500, 3000, 2000 }, { 1600, 1000, 2000, 2000 }, { 2000, 1000, 4000, 2000 }, { 1500, 1000, 2500, 1000 }, { 1000, 1000, 5000, 5000 }, { 0, 0, 0, 0 } }; bool CPed::ServiceTalkingWhenDead(void) { return m_queuedSound == SOUND_PED_DEATH; } void CPed::ServiceTalking(void) { if (bBodyPartJustCameOff && m_bodyPartBleeding == PED_HEAD) return; if (!CGame::germanGame && m_pFire) m_queuedSound = SOUND_PED_BURNING; if (m_queuedSound != SOUND_NO_SOUND) { if (m_queuedSound == SOUND_PED_DEATH) m_soundStart = CTimer::GetTimeInMilliseconds() - 1; if (CTimer::GetTimeInMilliseconds() > m_soundStart) { DMAudio.PlayOneShot(m_audioEntityId, m_queuedSound, 1.0f); m_lastSoundStart = CTimer::GetTimeInMilliseconds(); m_soundStart = CommentWaitTime[m_queuedSound - SOUND_PED_DEATH].m_nFixedDelayTime + CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(0, CommentWaitTime[m_queuedSound - SOUND_PED_DEATH].m_nOverrideFixedDelayTime); if (m_queuedSound == SOUND_PED_PLAYER_BEFORESEX && IsPlayer()) m_soundStart += 2000; m_lastQueuedSound = m_queuedSound; m_queuedSound = SOUND_NO_SOUND; } } } void CPed::Say(uint16 audio) { if (3.0f + TheCamera.GetPosition().z < GetPosition().z) return; if (TheCamera.m_CameraAverageSpeed > 1.65f) { if (audio != SOUND_PED_DAMAGE && audio != SOUND_PED_HIT && audio != SOUND_PED_LAND) return; } else if (TheCamera.m_CameraAverageSpeed > 1.25f) { if (audio != SOUND_PED_DEATH && audio != SOUND_PED_DAMAGE && audio != SOUND_PED_HIT && audio != SOUND_PED_LAND && audio != SOUND_PED_TAXI_WAIT && audio != SOUND_PED_EVADE) return; } else if (TheCamera.m_CameraAverageSpeed > 0.9f) { switch (audio) { case SOUND_PED_DEATH: case SOUND_PED_DAMAGE: case SOUND_PED_HIT: case SOUND_PED_LAND: case SOUND_PED_BURNING: case SOUND_PED_FLEE_SPRINT: case SOUND_PED_TAXI_WAIT: case SOUND_PED_EVADE: case SOUND_PED_CRASH_VEHICLE: case SOUND_PED_CRASH_CAR: case SOUND_PED_ANNOYED_DRIVER: break; default: return; } } if (audio < m_queuedSound) { if (audio != m_lastQueuedSound || audio == SOUND_PED_DEATH // See VC Ped Speech patch #ifdef FIX_BUGS || CommentWaitTime[audio - SOUND_PED_DEATH].m_nOverrideMaxRandomDelayTime + (uint32)CGeneral::GetRandomNumberInRange(0, CommentWaitTime[audio - SOUND_PED_DEATH].m_nMaxRandomDelayTime) #else || CommentWaitTime[m_queuedSound - SOUND_PED_DEATH].m_nOverrideMaxRandomDelayTime + (uint32)CGeneral::GetRandomNumberInRange(0, CommentWaitTime[m_queuedSound - SOUND_PED_DEATH].m_nMaxRandomDelayTime) #endif + m_lastSoundStart <= CTimer::GetTimeInMilliseconds()) { m_queuedSound = audio; } } } ================================================ FILE: src/peds/PedDebug.cpp ================================================ #include "common.h" #ifndef MASTER #include "main.h" #include "Camera.h" #include "Font.h" #include "Ped.h" #include "Sprite.h" #include "Text.h" static char ObjectiveText[][28] = { "No Obj", "Wait on Foot", "Wait on Foot for cop", "Flee on Foot Till Safe", "Guard Spot", "Guard Area", "Wait in Car", "Wait in Car then Getout", "Kill Char on Foot", "Kill Char Any Means", "Flee Char on Foot Till Safe", "Flee Char on Foot Always", "GoTo Char on Foot", "GoTo Char on Foot walking", "Hassle char", "Follow Char in Formation", "Leave Car", "Enter Car as Passenger", "Enter Car as Driver", "Follow Car in Car", "Fire at Obj from Vehicle", "Destroy Obj", "Destroy Car", "GoTo Area Any Means", "GoTo Area on Foot", "Run to Area", "GoTo Area in Car", "Follow Car on Foot Woffset", "Guard Attack", "Set Leader", "Follow Route", "Solicit vehicle", "Take Taxi", "Catch Train", "Buy IceCream", "Steal Any Car", "Steal any mission car", "Mug Char", "Lv car die", "Goto seat", "Goto atm", "Flee car", "Sunbathe", "Goto bus stop", "Goto pizza", "Goto shelter", "Aim gun at", "Wander", "Wait on foot at shltr", "Sprint to area", "Kill char on boat", "Solicit ped", "Wait at bus stop", "Goto ice cream van foot", "Wait foot icecream van" }; static char StateText[][18] = { "None", "Idle", "Look Entity", "Look Heading", "Wander Range", "Wander Path", "Seek Pos", "Seek Entity", "Flee Pos", "Flee Entity", "Pursue", "Follow Path", "Sniper Mode", "Rocket Mode", "Dummy", "Pause", "Attack", "Fight", "Face Phone", "Make Call", "Chat", "Mug", "AimGun", "AI Control", "Seek Car", "Seek InBoat", "Follow Route", "C.P.R.", "Solicit", "Buy IceCream", "Investigate", "Step away", "On Fire", "Bathe", "Flash", "Jog", "Answer mobile", "Hang out", "STATES_NO_AI", "Abseil", "Sit", "Jump", "Fall", "GetUp", "Stagger", "Dive away", "STATES_NO_ST", "Enter Train", "Exit Train", "Arrest Plyr", "Driving", "Passenger", "Taxi Passngr", "Open Door", "Die", "Dead", "CarJack", "Drag fm Car", "Enter Car", "Steal Car", "Exit Car", "Hands Up", "Arrested", "Deply stgr" }; static char PersonalityTypeText[][18] = { "Player", "Cop", "Medic", "Fireman", "Gang 1", "Gang 2", "Gang 3", "Gang 4", "Gang 5", "Gang 6", "Gang 7", "Street Guy", "Suit Guy", "Sensible Guy", "Geek Guy", "Old Guy", "Tough Guy", "Street Girl", "Suit Girl", "Sensible Girl", "Geek Girl", "Old Girl", "Tough Girl", "Tramp", "Tourist", "Prostitute", "Criminal", "Busker", "Taxi Driver", "Psycho", "Steward", "Sports Fan", }; static char WaitStateText[][16] = { "No Wait", "Traffic Lights", "Pause CrossRoad", "Look CrossRoad", "Look Ped", "Look Shop", "Look Accident", "FaceOff Gang", "Double Back", "Hit Wall", "Turn 180deg", "Surprised", "Ped Stuck", "Look About", "Play Duck", "Play Cower", "Play Taxi", "Play HandsUp", "Play HandsCower", "Play Chat", "Finish Flee", "Sit down", "Sit down rvrs", "Sit up", "Sit idle", "Use atm", "Sunbth pre", "Sunbth down", "Sunbth idle", "Riot", "Fast fall", "Bomber", "Stripper", "Ground attack", "Lance sitting", "Handsup simple" }; void CPed::DebugDrawPedDestination(CPed *, int, int) { #ifndef FINAL // TODO: something was here #endif // !FINAL } void CPed::DebugDrawPedDesiredHeading(CPed *, int, int) { #ifndef FINAL // TODO: something was here #endif // !FINAL } void CPed::DebugDrawCollisionRadius(float, float, float, float, int) { #ifndef FINAL // TODO: something was here #endif // !FINAL } void CPed::DebugDrawVisionRange(CVector a1, float) { for (int i = a1.x - 90; i < a1.x + 89; i += 30) { #ifndef FINAL // TODO: something was here #endif // !FINAL } } void CPed::DebugDrawVisionSimple(CVector, float) { #ifndef FINAL // TODO: something was here #endif // !FINAL } void CPed::DebugDrawLook() { #ifndef FINAL // TODO: something was here #endif // !FINAL } void CPed::DebugDrawPedPsyche() { #ifndef FINAL // TODO: something was here #endif // !FINAL } void CPed::DebugDrawDebugLines() { #ifndef FINAL // TODO: something was here #endif // !FINAL } int nDisplayDebugInfo = 0; void CPed::SwitchDebugDisplay(void) { if (++nDisplayDebugInfo > 2) nDisplayDebugInfo = 0; } int CPed::GetDebugDisplay(void) { return nDisplayDebugInfo; } void CPed::DebugDrawLookAtPoints() { // TODO: mobile code } void CPed::DebugRenderOnePedText(void) { if ((GetPosition() - TheCamera.GetPosition()).MagnitudeSqr() < sq(30.0f)) { float width, height; RwV3d screenCoords; CVector bitAbove = GetPosition(); bitAbove.z += 2.0f; if (CSprite::CalcScreenCoors(bitAbove, &screenCoords, &width, &height, true)) { float lineHeight = SCREEN_SCALE_Y(Min(height / 100.0f, 0.7f) * 22.0f); DefinedState(); CFont::SetPropOn(); CFont::SetBackgroundOn(); // Originally both of them were being divided by 60.0f. float xScale = Min(width / 240.0f, 0.7f); float yScale = Min(height / 80.0f, 0.7f); CFont::SetScale(SCREEN_SCALE_X(xScale), SCREEN_SCALE_Y(yScale)); CFont::SetCentreOn(); CFont::SetCentreSize(SCREEN_WIDTH); CFont::SetJustifyOff(); CFont::SetColor(CRGBA(255, 255, 0, 255)); CFont::SetBackGroundOnlyTextOn(); CFont::SetFontStyle(1); AsciiToUnicode(StateText[m_nPedState], gUString); CFont::PrintString(screenCoords.x, screenCoords.y, gUString); AsciiToUnicode(ObjectiveText[m_objective], gUString); CFont::PrintString(screenCoords.x, screenCoords.y + lineHeight, gUString); AsciiToUnicode(PersonalityTypeText[m_pedStats->m_type], gUString); CFont::PrintString(screenCoords.x, screenCoords.y + 2 * lineHeight, gUString); AsciiToUnicode(WaitStateText[m_nWaitState], gUString); CFont::PrintString(screenCoords.x, screenCoords.y + 3 * lineHeight, gUString); if (m_nPedState == PED_SEEK_POS || m_nPedState == PED_SEEK_ENTITY) { sprintf(gString, "Safe distance to target: %.2f", m_distanceToCountSeekDone); AsciiToUnicode(gString, gUString); CFont::PrintString(screenCoords.x, screenCoords.y + 4 * lineHeight, gUString); } DefinedState(); } } } void CPed::DebugRenderClosePedText() { // TODO: mobile code } #endif ================================================ FILE: src/peds/PedFight.cpp ================================================ #include "common.h" #include "main.h" #include "RpAnimBlend.h" #include "AnimBlendClumpData.h" #include "AnimBlendAssociation.h" #include "Camera.h" #include "CarCtrl.h" #include "Darkel.h" #include "DMAudio.h" #include "FileMgr.h" #include "General.h" #include "Object.h" #include "Pad.h" #include "Particle.h" #include "Ped.h" #include "PlayerPed.h" #include "Stats.h" #include "TempColModels.h" #include "VisibilityPlugins.h" #include "Vehicle.h" #include "Automobile.h" #include "WaterLevel.h" #include "World.h" #include "Bike.h" #include "Glass.h" #include "SpecialFX.h" uint16 nPlayerInComboMove; RpClump* flyingClumpTemp; FightMove tFightMoves[NUM_FIGHTMOVES] = { { ANIM_STD_NUM, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_PUNCH, 0.2f, 8.f/30.f, 0.0f, 0.3f, 1.0f, HITLEVEL_HIGH, 1, 0 }, { ANIM_STD_FIGHT_IDLE, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_FIGHT_SHUFFLE_F, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_FIGHT_KNEE, 4.f/30.f, 0.2f, 0.0f, 0.6f, 1.0f, HITLEVEL_LOW, 2, 0 }, { ANIM_STD_FIGHT_LHOOK, 8.f/30.f, 10.f/30.f, 0.0f, 0.4f, 1.0f, HITLEVEL_HIGH, 3, 0 }, { ANIM_STD_FIGHT_JAB, 4.f/30.f, 0.2f, 0.0f, 0.7f, 1.0f, HITLEVEL_HIGH, 3, 0 }, { ANIM_STD_FIGHT_PUNCH, 4.f/30.f, 7.f/30.f, 10.f/30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 }, { ANIM_STD_FIGHT_LONGKICK, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_MEDIUM, 4, 0 }, { ANIM_STD_FIGHT_ROUNDHOUSE, 8.f/30.f, 10.f/30.f, 0.0f, 0.6f, 1.0f, HITLEVEL_MEDIUM, 4, 0 }, { ANIM_STD_FIGHT_KICK, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_HIGH, 2, 0 }, { ANIM_STD_FIGHT_HEAD, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_MEDIUM, 2, 0 }, { ANIM_STD_FIGHT_BKICK_L, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_LOW, 2, 0 }, { ANIM_STD_FIGHT_BKICK_L, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_LOW, 2, 0 }, { ANIM_STD_FIGHT_ELBOW_L, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_MEDIUM, 2, 0 }, { ANIM_STD_FIGHT_BKICK_R, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_MEDIUM, 2, 0 }, { ANIM_STD_FIGHT_ELBOW_R, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_HIGH, 2, 0 }, { ANIM_STD_KICKGROUND, 10.f/30.f, 14.f/30.f, 0.0f, 0.4f, 1.0f, HITLEVEL_GROUND, 1, 0 }, { ANIM_STD_HIT_FRONT, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_HIT_BACK, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_HIT_RIGHT, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_HIT_LEFT, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_HIT_BODYBLOW, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_HIT_CHEST, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_HIT_HEAD, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_HIT_WALK, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_HIT_FLOOR, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_STD_HIT_BEHIND, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 }, { ANIM_ATTACK_1, 4.f/30.f, 7.f/30.f, 10.f/30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 }, { ANIM_ATTACK_2, 4.f/30.f, 7.f/30.f, 10.f/30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 }, { ANIM_ATTACK_3, 4.f / 30.f, 7.f / 30.f, 10.f / 30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 }, { ANIM_STD_FIGHT_2IDLE, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, HITLEVEL_NULL, 0, 0 } }; static PedOnGroundState CheckForPedsOnGroundToAttack(CPed *attacker, CPed **pedOnGround) { PedOnGroundState stateToReturn; float angleToFace; CPed *currentPed = nil; PedState currentPedState; CPed *pedOnTheFloor = nil; CPed *deadPed = nil; CPed *pedBelow = nil; bool foundDead = false; bool foundOnTheFloor = false; bool foundBelow = false; float angleDiff; float distance; if (!CGame::nastyGame) return NO_PED; for (int currentPedId = 0; currentPedId < attacker->m_numNearPeds; currentPedId++) { currentPed = attacker->m_nearPeds[currentPedId]; CVector posDifference = currentPed->GetPosition() - attacker->GetPosition(); distance = posDifference.Magnitude(); if (distance < 2.0f) { angleToFace = CGeneral::GetRadianAngleBetweenPoints( currentPed->GetPosition().x, currentPed->GetPosition().y, attacker->GetPosition().x, attacker->GetPosition().y); angleToFace = CGeneral::LimitRadianAngle(angleToFace); attacker->m_fRotationCur = CGeneral::LimitRadianAngle(attacker->m_fRotationCur); angleDiff = Abs(angleToFace - attacker->m_fRotationCur); if (angleDiff > PI) angleDiff = 2 * PI - angleDiff; currentPedState = currentPed->m_nPedState; if (currentPed->OnGroundOrGettingUp()) { if (distance < 2.0f && angleDiff < DEGTORAD(65.0f)) { if (currentPedState == PED_DEAD) { foundDead = 1; if (!deadPed) deadPed = currentPed; } else if (!currentPed->IsPedHeadAbovePos(-0.6f)) { foundOnTheFloor = 1; if (!pedOnTheFloor) pedOnTheFloor = currentPed; } } } else if ((distance < 0.8f && angleDiff < DEGTORAD(75.0f)) || (distance < 1.3f && angleDiff < DEGTORAD(55.0f)) || (distance < 1.7f && angleDiff < DEGTORAD(35.0f)) || (distance < 2.0f && angleDiff < DEGTORAD(30.0f))) { // Either this condition or below one was probably returning 4 early in development. See Fight(). foundBelow = 1; pedBelow = currentPed; break; } else { if (angleDiff < DEGTORAD(75.0f)) { foundBelow = 1; if (!pedBelow) pedBelow = currentPed; } } } } if (foundOnTheFloor) { currentPed = pedOnTheFloor; stateToReturn = PED_ON_THE_FLOOR; } else if (foundDead) { currentPed = deadPed; stateToReturn = PED_DEAD_ON_THE_FLOOR; } else if (foundBelow) { currentPed = pedBelow; stateToReturn = PED_IN_FRONT_OF_ATTACKER; } else { currentPed = nil; stateToReturn = NO_PED; } if (pedOnGround) *pedOnGround = currentPed; return stateToReturn; } void CPed::SetPointGunAt(CEntity *to) { if (to) { SetLookFlag(to, true, true); SetAimFlag(to); SetLookTimer(INT32_MAX); } CWeaponInfo* curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); if (m_nPedState == PED_AIM_GUN || (bIsDucking && !IsPlayer()) || m_nWaitState == WAITSTATE_PLAYANIM_DUCK || curWeapon->m_AnimToPlay == ASSOCGRP_STD) return; if (m_nPedState != PED_ATTACK) SetStoredState(); SetPedState(PED_AIM_GUN); bIsPointingGunAt = true; SetMoveState(PEDMOVE_STILL); CAnimBlendAssociation *aimAssoc; if (bCrouchWhenShooting && bIsDucking && GetCrouchFireAnim(curWeapon)) { aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(curWeapon)); } else { aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE); } if (!aimAssoc || aimAssoc->blendDelta < 0.0f) { if (bCrouchWhenShooting && bIsDucking && GetCrouchFireAnim(curWeapon)) { aimAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, GetCrouchFireAnim(curWeapon), 4.0f); } else { aimAssoc = CAnimManager::AddAnimation(GetClump(), curWeapon->m_AnimToPlay, ANIM_WEAPON_FIRE); } aimAssoc->blendAmount = 0.0f; aimAssoc->blendDelta = 8.0f; } if (to && !IsPlayer()) Say(SOUND_PED_ATTACK); } void CPed::PointGunAt(void) { CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); float animLoopStart = weaponInfo->m_fAnimLoopStart; CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE); if (!weaponAssoc || weaponAssoc->blendDelta < 0.0f) { if (weaponInfo->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) { weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weaponInfo)); animLoopStart = weaponInfo->m_fAnim2LoopStart; } } if (weaponAssoc && weaponAssoc->currentTime > animLoopStart * 0.4f) { weaponAssoc->SetCurrentTime(animLoopStart); weaponAssoc->flags &= ~ASSOC_RUNNING; if (bIsDucking) m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; if (weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) m_pedIK.m_flags |= CPedIK::AIMS_WITH_ARM; else m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; } } void CPed::ClearPointGunAt(void) { CAnimBlendAssociation *animAssoc; CWeaponInfo *weaponInfo; ClearLookFlag(); ClearAimFlag(); bIsPointingGunAt = false; if (m_nPedState == PED_AIM_GUN || m_nPedState == PED_ATTACK) { SetPedState(PED_IDLE); RestorePreviousState(); } weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE); if (!animAssoc || animAssoc->blendDelta < 0.0f) { if (weaponInfo->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weaponInfo)); } } if (animAssoc) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc->blendDelta = -4.0f; } } void CPed::SetAttack(CEntity *victim) { CPed *victimPed = nil; CWeaponInfo *curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); CAnimBlendAssociation *animAssoc; if (victim && victim->IsPed()) victimPed = (CPed*)victim; if (m_attackTimer > CTimer::GetTimeInMilliseconds() || m_nWaitState == WAITSTATE_SURPRISE || (bIsDucking && !bCrouchWhenShooting)) return; if (curWeapon->IsFlagSet(WEAPONFLAG_RELOAD) && (RpAnimBlendClumpGetAssociation(GetClump(), GetReloadAnim(curWeapon)) || RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchReloadAnim(curWeapon)))) { if (!IsPlayer() || m_nPedState != PED_ATTACK || ((CPlayerPed*)this)->m_bHaveTargetSelected) bIsAttacking = false; else bIsAttacking = true; return; } if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED || curWeapon->IsFlagSet(WEAPONFLAG_FIGHTMODE) || GetWeapon()->m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) { if (IsPlayer() || (m_nPedState != PED_FIGHT && m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL && !(m_pedStats->m_flags & STAT_SHOPPING_BAGS) && curWeapon->IsFlagSet(WEAPONFLAG_PARTIALATTACK))) { if (m_nPedState != PED_ATTACK) { SetPedState(PED_ATTACK); bIsAttacking = false; CAnimBlendAssociation *animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, ANIM_MELEE_ATTACK_START, 8.0f); animAssoc->SetRun(); if (animAssoc->currentTime == animAssoc->hierarchy->totalLength) animAssoc->SetCurrentTime(0.0f); animAssoc->SetFinishCallback(FinishedAttackCB, this); } } else { StartFightAttack(CGeneral::GetRandomNumber()); } return; } if (curWeapon->IsFlagSet(WEAPONFLAG_PARTIALATTACK) && (IsPlayer() && ((CPlayerPed*)this)->m_fMoveSpeed >= 1.0f || m_nMoveState == PEDMOVE_WALK || m_nMoveState == PEDMOVE_RUN)) { if (m_nPedState != PED_ATTACK) { SetPedState(PED_ATTACK); bIsAttacking = false; CAnimBlendAssociation* animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, ANIM_MELEE_ATTACK_START, 8.0f); animAssoc->SetRun(); if (animAssoc->currentTime == animAssoc->hierarchy->totalLength) animAssoc->SetCurrentTime(0.0f); animAssoc->SetFinishCallback(FinishedAttackCB, this); } return; } if (m_pSeekTarget) m_pSeekTarget->CleanUpOldReference(&m_pSeekTarget); m_pSeekTarget = victim; if (m_pSeekTarget) m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); if (curWeapon->IsFlagSet(WEAPONFLAG_CANAIM)) { CVector aimPos = GetRight() * 0.1f + GetForward() * 0.2f + GetPosition(); aimPos += GetUp() * 0.35f; CEntity *obstacle = CWorld::TestSphereAgainstWorld(aimPos, 0.2f, nil, true, false, false, true, false, false); if (obstacle) { if(gaTempSphereColPoints[0].surfaceB != SURFACE_TRANSPARENT_CLOTH && gaTempSphereColPoints[0].surfaceB != SURFACE_METAL_CHAIN_FENCE && gaTempSphereColPoints[0].surfaceB != SURFACE_WOOD_BENCH && gaTempSphereColPoints[0].surfaceB != SURFACE_SCAFFOLD_POLE) { if (!IsPlayer()) { bObstacleShowedUpDuringKillObjective = true; m_shootTimer = 0; SetAttackTimer(1500); m_shotTime = CTimer::GetTimeInMilliseconds(); } return; } } m_pLookTarget = victim; if (victim) { m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); } if (m_pLookTarget) { SetAimFlag(m_pLookTarget); } else if (this == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam()) { SetAimFlag(m_fRotationCur); ((CPlayerPed*)this)->m_fFPSMoveHeading = TheCamera.Find3rdPersonQuickAimPitch(); } else if (curWeapon->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) { SetAimFlag(m_fRotationCur); } } if (m_nPedState == PED_ATTACK) { bIsAttacking = true; return; } if (IsPlayer() || (!victimPed || victimPed->IsPedInControl())) { if (IsPlayer()) CPad::GetPad(0)->ResetAverageWeapon(); uint8 pointBlankStatus; if ((curWeapon->m_eWeaponFire == WEAPON_FIRE_INSTANT_HIT || GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER) && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON_RUNABOUT && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER_RUNABOUT && (pointBlankStatus = CheckForPointBlankPeds(victimPed)) != NO_POINT_BLANK_PED) { ClearAimFlag(); // This condition is pointless if (pointBlankStatus == POINT_BLANK_FOR_WANTED_PED || !victimPed && (IsPlayer() || !m_carInObjective)) StartFightAttack(200); } else { if (!curWeapon->IsFlagSet(WEAPONFLAG_CANAIM)) m_pSeekTarget = nil; if (m_nPedState != PED_AIM_GUN) SetStoredState(); SetPedState(PED_ATTACK); SetMoveState(PEDMOVE_NONE); if (bCrouchWhenShooting && bIsDucking && curWeapon->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) { CAnimBlendAssociation* curMoveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(curWeapon)); if (curMoveAssoc) { if (strcmp(CAnimManager::GetAnimAssociation(curWeapon->m_AnimToPlay, GetCrouchFireAnim(curWeapon))->hierarchy->name, curMoveAssoc->hierarchy->name) != 0) { delete curMoveAssoc; } } animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, GetCrouchFireAnim(curWeapon), 8.0f); } else { float animDelta = 8.0f; if (curWeapon->m_eWeaponFire == WEAPON_FIRE_MELEE) animDelta = 1000.0f; AnimationId fireAnim; if (curWeapon->IsFlagSet(WEAPONFLAG_THROW)) fireAnim = ANIM_THROWABLE_START_THROW; else if (CGame::nastyGame && (curWeapon->IsFlagSet(WEAPONFLAG_GROUND_2ND) || curWeapon->IsFlagSet(WEAPONFLAG_GROUND_3RD))) { PedOnGroundState pedOnGround = CheckForPedsOnGroundToAttack(this, nil); if (pedOnGround > PED_IN_FRONT_OF_ATTACKER || pedOnGround == NO_PED && bIsStanding && m_pCurSurface && m_pCurSurface->IsVehicle()) { fireAnim = GetFireAnimGround(curWeapon, false); } else { fireAnim = GetFireAnimNotDucking(curWeapon); } } else { fireAnim = GetFireAnimNotDucking(curWeapon); } CAnimBlendAssociation* curFireAssoc = RpAnimBlendClumpGetAssociation(GetClump(), fireAnim); if (curFireAssoc) { if (strcmp(CAnimManager::GetAnimAssociation(curWeapon->m_AnimToPlay, fireAnim)->hierarchy->name, curFireAssoc->hierarchy->name) != 0) { delete curFireAssoc; } } animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, fireAnim, animDelta); } animAssoc->SetRun(); if (animAssoc->currentTime == animAssoc->hierarchy->totalLength) animAssoc->SetCurrentTime(0.0f); animAssoc->SetFinishCallback(FinishedAttackCB, this); } return; } if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT && victimPed->m_nPedState == PED_GETUP) SetWaitState(WAITSTATE_SURPRISE, nil); SetLookFlag(victim, true, true); SetLookTimer(100); } void CPed::ClearAttack(void) { if (m_nPedState != PED_ATTACK || (bIsDucking && !IsPlayer()) || m_nWaitState == WAITSTATE_PLAYANIM_DUCK) return; if (FindPlayerPed() == this && TheCamera.Using1stPersonWeaponMode()) { SetPointGunAt(nil); } else if (bIsPointingGunAt) { if (m_pLookTarget) SetPointGunAt(m_pLookTarget); else ClearPointGunAt(); } else if (m_objective != OBJECTIVE_NONE) { SetIdle(); } else { RestorePreviousState(); } } void CPed::ClearAttackByRemovingAnim(void) { if (m_nPedState != PED_ATTACK) return; CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(weapon)); if (!weaponAssoc) { if (GetCrouchFireAnim(weapon)) weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weapon)); } if (!weaponAssoc) { if(GetFinishingAttackAnim(weapon)) weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetFinishingAttackAnim(weapon)); } if (!weaponAssoc) { if(GetSecondFireAnim(weapon)) weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetSecondFireAnim(weapon)); } if (!weaponAssoc) { if(Get3rdFireAnim(weapon)) weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), Get3rdFireAnim(weapon)); } if (weaponAssoc) { weaponAssoc->blendDelta = -8.0f; weaponAssoc->flags &= ~ASSOC_RUNNING; weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; weaponAssoc->SetDeleteCallback(FinishedAttackCB, this); } else { ClearAttack(); } } void CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg) { CAnimBlendAssociation *newAnim, *reloadAnimAssoc = nil; CPed *ped = (CPed*)arg; CWeaponInfo *currentWeapon = CWeaponInfo::GetWeaponInfo(ped->GetWeapon()->m_eWeaponType); if (ped->m_nPedState != PED_ATTACK) { if (ped->bIsDucking && ped->IsPedInControl()) { if (GetCrouchReloadAnim(currentWeapon)) { reloadAnimAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), GetCrouchReloadAnim(currentWeapon)); } if (GetCrouchFireAnim(currentWeapon) && attackAssoc) { if (attackAssoc->animId == GetCrouchFireAnim(currentWeapon) && !reloadAnimAssoc) { newAnim = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_WEAPON, 8.0f); newAnim->SetCurrentTime(newAnim->hierarchy->totalLength); newAnim->flags &= ~ASSOC_RUNNING; } } } } else if (attackAssoc && attackAssoc->animId == ANIM_THROWABLE_START_THROW && currentWeapon->m_AnimToPlay == ASSOCGRP_THROW) { if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->m_bHaveTargetSelected) && ped->IsPlayer()) { attackAssoc->blendDelta = -1000.0f; newAnim = CAnimManager::AddAnimation(ped->GetClump(), currentWeapon->m_AnimToPlay, ANIM_THROWABLE_THROWU); } else { attackAssoc->blendDelta = -1000.0; newAnim = CAnimManager::AddAnimation(ped->GetClump(), currentWeapon->m_AnimToPlay, ANIM_THROWABLE_THROW); } newAnim->SetFinishCallback(FinishedAttackCB, ped); } else if (ped->bIsDucking && ped->bCrouchWhenShooting) { if (GetCrouchReloadAnim(currentWeapon)) { reloadAnimAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), GetCrouchReloadAnim(currentWeapon)); } if (GetCrouchFireAnim(currentWeapon) && attackAssoc) { if (attackAssoc->animId == GetCrouchFireAnim(currentWeapon) && !reloadAnimAssoc) { newAnim = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_WEAPON, 8.0f); newAnim->SetCurrentTime(newAnim->hierarchy->totalLength); newAnim->flags &= ~ASSOC_RUNNING; } } if (!ped->bIsAttacking) ped->ClearAttack(); } else if (GetSecondFireAnim(currentWeapon) && ped->bIsAttacking && currentWeapon->m_AnimToPlay != ASSOCGRP_THROW) { AnimationId groundAnim = GetFireAnimGround(currentWeapon); CAnimBlendAssociation *groundAnimAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), groundAnim); if (!(groundAnimAssoc && (groundAnimAssoc->blendAmount > 0.95f || groundAnimAssoc->blendDelta > 0.0f))) { if (attackAssoc && attackAssoc->animId == ANIM_MELEE_ATTACK) { newAnim = CAnimManager::BlendAnimation( ped->GetClump(), currentWeapon->m_AnimToPlay, GetSecondFireAnim(currentWeapon), 8.0f); } else { newAnim = CAnimManager::BlendAnimation( ped->GetClump(), currentWeapon->m_AnimToPlay, ANIM_MELEE_ATTACK, 8.0f); } newAnim->SetFinishCallback(FinishedAttackCB, ped); } } else { if (attackAssoc && attackAssoc->animId == ANIM_MELEE_ATTACK && currentWeapon->m_AnimToPlay == ASSOCGRP_UNARMED) { attackAssoc->blendDelta = -8.0f; attackAssoc->flags |= ASSOC_DELETEFADEDOUT; ped->ClearAttack(); return; } if (attackAssoc) { if (currentWeapon->m_AnimToPlay == ASSOCGRP_THROW) { if ((attackAssoc->animId == ANIM_THROWABLE_THROW || attackAssoc->animId == ANIM_THROWABLE_THROWU) && ped->GetWeapon()->m_nAmmoTotal > 0) { ped->RemoveWeaponModel(currentWeapon->m_nModelId); ped->AddWeaponModel(currentWeapon->m_nModelId); } } } if (!ped->bIsAttacking) ped->ClearAttack(); } } void CPed::FinishedReloadCB(CAnimBlendAssociation *reloadAssoc, void *arg) { CPed *ped = (CPed*)arg; CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(ped->GetWeapon()->m_eWeaponType); if (ped->DyingOrDead()) return; if (ped->bIsDucking && ped->bCrouchWhenShooting) { CAnimBlendAssociation *crouchFireAssoc = nil; if (weapon->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) { crouchFireAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), GetCrouchFireAnim(weapon)); } if (weapon->IsFlagSet(WEAPONFLAG_RELOAD) && reloadAssoc) { if (reloadAssoc->animId == GetCrouchReloadAnim(weapon) && !crouchFireAssoc) { CAnimBlendAssociation *crouchAssoc = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_DUCK_WEAPON, 8.0f); crouchAssoc->SetCurrentTime(crouchAssoc->hierarchy->totalLength); crouchAssoc->flags &= ~ASSOC_RUNNING; } } } else if (weapon->IsFlagSet(WEAPONFLAG_RELOAD_LOOP2START) && ped->bIsAttacking) { CAnimBlendAssociation *fireAssoc = CAnimManager::BlendAnimation(ped->GetClump(), weapon->m_AnimToPlay, GetPrimaryFireAnim(weapon), 8.0f); fireAssoc->SetFinishCallback(FinishedAttackCB, ped); fireAssoc->SetRun(); if (fireAssoc->currentTime == reloadAssoc->hierarchy->totalLength) fireAssoc->SetCurrentTime(Max(weapon->m_fAnimLoopStart - 0.04f, 0.0f)); else if (fireAssoc->currentTime < weapon->m_fAnimLoopStart) fireAssoc->SetCurrentTime(Max(weapon->m_fAnimLoopStart - 0.04f, 0.0f)); } } uint8 CPed::CheckForPointBlankPeds(CPed *pedToVerify) { float pbDistance = 1.1f; if (GetWeapon()->IsType2Handed()) pbDistance = 1.6f; for (int i = 0; i < m_numNearPeds; i++) { CPed *nearPed = m_nearPeds[i]; if (!pedToVerify || pedToVerify == nearPed) { CVector diff = nearPed->GetPosition() - GetPosition(); if (diff.MagnitudeSqr() < SQR(pbDistance)) { float neededAngle = CGeneral::GetRadianAngleBetweenPoints( nearPed->GetPosition().x, nearPed->GetPosition().y, GetPosition().x, GetPosition().y); neededAngle = CGeneral::LimitRadianAngle(neededAngle); m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); float neededTurn = Abs(neededAngle - m_fRotationCur); if (neededTurn > PI) neededTurn = 2*PI - neededTurn; PedState nearPedState = nearPed->m_nPedState; if (nearPedState == PED_FALL || nearPedState == PED_GETUP || nearPedState == PED_DIE || nearPedState == PED_DEAD || nearPedState == PED_DIVE_AWAY) return NO_POINT_BLANK_PED; if (neededTurn < CAN_SEE_ENTITY_ANGLE_THRESHOLD) { if (pedToVerify == nearPed) return POINT_BLANK_FOR_WANTED_PED; else return POINT_BLANK_FOR_SOMEONE_ELSE; } } } } return NO_POINT_BLANK_PED; } void CPed::Attack(void) { CAnimBlendAssociation *weaponAnimAssoc; int32 weaponAnim; float weaponAnimTime; float animLoopEnd; CWeaponInfo *ourWeapon; bool attackShouldContinue; CAnimBlendAssociation *reloadAnimAssoc; CAnimBlendAssociation *throwAssoc; float delayBetweenAnimAndFire; float animLoopStart; CVector firePos; ourWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); weaponAnimAssoc = nil; attackShouldContinue = !!bIsAttacking; reloadAnimAssoc = nil; throwAssoc = nil; animLoopStart = ourWeapon->m_fAnimLoopStart; animLoopEnd = ourWeapon->m_fAnimLoopEnd; delayBetweenAnimAndFire = ourWeapon->m_fAnimFrameFire; weaponAnim = ourWeapon->m_AnimToPlay; if (bIsDucking) { if(GetCrouchFireAnim(ourWeapon) && bCrouchWhenShooting) { weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(ourWeapon)); if (weaponAnimAssoc) { animLoopStart = ourWeapon->m_fAnim2LoopStart; animLoopEnd = ourWeapon->m_fAnim2LoopEnd; delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; } } } else if (m_nPedType == PEDTYPE_COP && Get3rdFireAnim(ourWeapon)){ weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), Get3rdFireAnim(ourWeapon)); if (weaponAnimAssoc) { animLoopStart = 11.f/30.f; animLoopEnd = 19.f/30.f; delayBetweenAnimAndFire = 14.f/30.f; } } else { weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(ourWeapon)); } if (GetReloadAnim(ourWeapon)) { reloadAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetReloadAnim(ourWeapon)); } if (GetCrouchReloadAnim(ourWeapon) && !reloadAnimAssoc) { reloadAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchReloadAnim(ourWeapon)); } if ( reloadAnimAssoc && reloadAnimAssoc->IsRunning() ) { if (!IsPlayer() || ((CPlayerPed*)this)->m_bHaveTargetSelected) ClearAttack(); return; } if ( reloadAnimAssoc ) { reloadAnimAssoc->flags |= ASSOC_DELETEFADEDOUT; if ( reloadAnimAssoc->blendDelta >= 0.0f ) reloadAnimAssoc->blendDelta = -8.0f; } if (GetThrowAnim(ourWeapon)) { throwAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetThrowAnim(ourWeapon)); } if ( CTimer::GetTimeInMilliseconds() < m_shootTimer ) attackShouldContinue = true; bool meleeAttackStarted = false; if ( !weaponAnimAssoc ) { if (GetMeleeStartAnim(ourWeapon)) { weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetMeleeStartAnim(ourWeapon)); if ( weaponAnimAssoc ) { if ( IsPlayer() ) meleeAttackStarted = true; switch ( ourWeapon->m_AnimToPlay ) { case ASSOCGRP_UNARMED: case ASSOCGRP_SCREWDRIVER: case ASSOCGRP_KNIFE: case ASSOCGRP_BASEBALLBAT: case ASSOCGRP_GOLFCLUB: case ASSOCGRP_CHAINSAW: delayBetweenAnimAndFire = 0.2f; animLoopStart = 0.1f; break; default: break; } animLoopEnd = 99.9f; } } } if (!weaponAnimAssoc) { if (GetSecondFireAnim(ourWeapon)) { weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetSecondFireAnim(ourWeapon)); if (weaponAnimAssoc) { animLoopStart = ourWeapon->m_fAnim2LoopStart; animLoopEnd = ourWeapon->m_fAnim2LoopEnd; delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; } } } if (!weaponAnimAssoc) { weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetFireAnimGround(ourWeapon)); if (weaponAnimAssoc) { animLoopStart = ourWeapon->m_fAnim2LoopStart; animLoopEnd = ourWeapon->m_fAnim2LoopEnd; delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; } } if (!weaponAnimAssoc) { if (!throwAssoc) { if (attackShouldContinue) { if (ourWeapon->m_eWeaponFire != WEAPON_FIRE_PROJECTILE || !IsPlayer() || ((CPlayerPed*)this)->m_bHaveTargetSelected) { if (bCrouchWhenShooting && bIsDucking && GetCrouchFireAnim(ourWeapon)) { weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetCrouchFireAnim(ourWeapon), 8.0f); } else if(GetSecondFireAnim(ourWeapon) && CGeneral::GetRandomNumber() & 1){ weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetSecondFireAnim(ourWeapon), 8.0f); } else if(!CGame::nastyGame || ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE || !GetFireAnimGround(ourWeapon, false) || CheckForPedsOnGroundToAttack(this, nil) < PED_ON_THE_FLOOR) { weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetFireAnimNotDucking(ourWeapon), 8.0f); } else { weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetFireAnimGround(ourWeapon, false), 8.0f); } weaponAnimAssoc->SetFinishCallback(FinishedAttackCB, this); weaponAnimAssoc->SetRun(); if (weaponAnimAssoc->currentTime == weaponAnimAssoc->hierarchy->totalLength) weaponAnimAssoc->SetCurrentTime(0.0f); if (IsPlayer()) { ((CPlayerPed*)this)->m_fAttackButtonCounter = 0.0f; ((CPlayerPed*)this)->m_bHaveTargetSelected = false; } } } else FinishedAttackCB(nil, this); } return; } if (meleeAttackStarted && IsPlayer()) { if (((CPlayerPed*)this)->m_bHaveTargetSelected || ((CPlayerPed*)this)->m_fMoveSpeed < 0.5f) { weaponAnimAssoc->SetRun(); } else { if (weaponAnimAssoc->currentTime > animLoopStart && weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep <= animLoopStart) weaponAnimAssoc->flags &= ~ASSOC_RUNNING; } } float animStart = animLoopStart * 0.4f; weaponAnimTime = weaponAnimAssoc->currentTime; if (weaponAnimTime > animStart && weaponAnimTime - weaponAnimAssoc->timeStep <= animStart) { if (!bIsDucking && !GetFireAnimNotDucking(ourWeapon) && ourWeapon->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) m_pedIK.m_flags |= CPedIK::AIMS_WITH_ARM; else m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; } if (GetWeapon()->m_eWeaponType != WEAPONTYPE_CHAINSAW || !meleeAttackStarted && delayBetweenAnimAndFire - 0.5f >= weaponAnimAssoc->currentTime || weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep > delayBetweenAnimAndFire) { if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_ATTACK, 0.0f); } else if (weaponAnimTime <= delayBetweenAnimAndFire || weaponAnimTime - weaponAnimAssoc->timeStep > delayBetweenAnimAndFire || !weaponAnimAssoc->IsRunning()) { if (weaponAnimAssoc->speed < 1.0f) weaponAnimAssoc->speed = 1.0f; } else { firePos = ourWeapon->m_vecFireOffset; if(ourWeapon->m_AnimToPlay != ASSOCGRP_BASEBALLBAT && ourWeapon->m_AnimToPlay != ASSOCGRP_GOLFCLUB) { if (ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE) { TransformToNode(firePos, (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_2ND && ourWeapon->m_AnimToPlay == ASSOCGRP_UNARMED) ? PED_FOOTR : PED_HANDR); } else { firePos = GetMatrix() * firePos; } } else { if (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_2ND) firePos.z = 0.7f * ourWeapon->m_fRadius - 1.0f; firePos = GetMatrix() * firePos; } GetWeapon()->Fire(this, &firePos); if (GetWeapon()->m_eWeaponType == WEAPONTYPE_MOLOTOV || GetWeapon()->m_eWeaponType == WEAPONTYPE_GRENADE || GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE || GetWeapon()->m_eWeaponType == WEAPONTYPE_TEARGAS) { RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nModelId); } if (GetWeapon()->m_nAmmoTotal == 0 && ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE && FindPlayerPed() != this) { SelectGunIfArmed(); } if (GetWeapon()->m_eWeaponState == WEAPONSTATE_MELEE_MADECONTACT) { int damagerType = ENTITY_TYPE_NOTHING; if (m_pDamageEntity && (m_fDamageImpulse == 0.0f || !m_pDamageEntity->IsBuilding())) { damagerType = m_pDamageEntity->GetType(); } switch (ourWeapon->m_AnimToPlay) { case ASSOCGRP_UNARMED: if (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK || weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_START) DMAudio.PlayOneShot(m_audioEntityId, SOUND_FIGHT_46, (damagerType | (GetWeapon()->m_eWeaponType << 8))); break; case ASSOCGRP_KNIFE: case ASSOCGRP_BASEBALLBAT: case ASSOCGRP_GOLFCLUB: case ASSOCGRP_CHAINSAW: DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_BAT_ATTACK, (damagerType | (GetWeapon()->m_eWeaponType << 8))); break; default: break; } if (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_shootTimer) { weaponAnimAssoc->callbackType = 0; } } attackShouldContinue = false; } } else { CVector firePos = ourWeapon->m_vecFireOffset; if (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_2ND) firePos.z = 0.7f * ourWeapon->m_fRadius - 1.0f; firePos = GetMatrix() * firePos; GetWeapon()->Fire(this, &firePos); if (GetWeapon()->m_eWeaponState == WEAPONSTATE_MELEE_MADECONTACT) { int damagerType = ENTITY_TYPE_PED; if (m_pDamageEntity) damagerType = m_pDamageEntity->GetType(); DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_MADECONTACT, (float)damagerType); if (IsPlayer()) { CPad::GetPad(0)->StartShake(240, 180); } } else { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_IDLE, 0.0f); if (IsPlayer()) { CPad::GetPad(0)->StartShake(240, 90); } } attackShouldContinue = false; } if (ourWeapon->m_eWeaponFire == WEAPON_FIRE_INSTANT_HIT && ourWeapon->m_AnimToPlay == ASSOCGRP_SHOTGUN) { weaponAnimTime = weaponAnimAssoc->currentTime; if (weaponAnimTime > 1.0f && weaponAnimTime - weaponAnimAssoc->timeStep <= 1.0f && weaponAnimAssoc->IsRunning()) { firePos = ourWeapon->m_vecFireOffset; TransformToNode(firePos, PED_HANDR); CVector gunshellPos( firePos.x - 0.6f * GetForward().x, firePos.y - 0.6f * GetForward().y, firePos.z - 0.15f * GetUp().z ); CVector2D gunshellRot( GetRight().x, GetRight().y ); gunshellRot.Normalise(); GetWeapon()->AddGunshell(this, gunshellPos, gunshellRot, 0.025f); } } if (IsPlayer()) { if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT || GetWeapon()->m_eWeaponType == WEAPONTYPE_GOLFCLUB || GetWeapon()->m_eWeaponType == WEAPONTYPE_KATANA) { float loopEndWithDelay = animLoopEnd; if (loopEndWithDelay >= 98.0f) loopEndWithDelay = (14.0f / 30.0f) + delayBetweenAnimAndFire; if (weaponAnimAssoc->flags & ASSOC_RUNNING) { if (weaponAnimAssoc->currentTime >= animLoopStart && weaponAnimAssoc->currentTime <= loopEndWithDelay) CSpecialFX::AddWeaponStreak(GetWeapon()->m_eWeaponType); } } } // Anim breakout on running if (IsPlayer()) { if (CPad::GetPad(0)->GetSprint()) { if (!attackShouldContinue && weaponAnimAssoc->currentTime > ourWeapon->m_fAnimBreakout) { weaponAnimAssoc->blendDelta = -4.0f; FinishedAttackCB(nil, this); return; } } } weaponAnimTime = weaponAnimAssoc->currentTime; // Anim loop end, either start the loop again or finish the attack if (weaponAnimTime > animLoopEnd || !weaponAnimAssoc->IsRunning() && ourWeapon->m_eWeaponFire != WEAPON_FIRE_PROJECTILE) { if (GetWeapon()->m_eWeaponState == WEAPONSTATE_RELOADING) { if (GetReloadAnim(ourWeapon) && !reloadAnimAssoc) { if (!CWorld::Players[CWorld::PlayerInFocus].m_bFastReload) { CAnimBlendAssociation *newReloadAssoc = CAnimManager::BlendAnimation( GetClump(), ourWeapon->m_AnimToPlay, bIsDucking && GetCrouchReloadAnim(ourWeapon) ? GetCrouchReloadAnim(ourWeapon) : GetReloadAnim(ourWeapon), 8.0f); newReloadAssoc->SetFinishCallback(FinishedReloadCB, this); } ClearLookFlag(); ClearAimFlag(); bIsAttacking = false; bIsPointingGunAt = false; m_shootTimer = CTimer::GetTimeInMilliseconds(); DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType); return; } } if (weaponAnimTime - 2.0f * weaponAnimAssoc->timeStep <= animLoopEnd && (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_shootTimer) && (GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING || GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN)) { PedOnGroundState pedOnGroundState; if (ourWeapon->m_eWeaponFire == WEAPON_FIRE_MELEE && (CGame::nastyGame && ((pedOnGroundState = CheckForPedsOnGroundToAttack(this, nil)) > PED_IN_FRONT_OF_ATTACKER) || GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT && pedOnGroundState == NO_PED && bIsStanding && m_pCurSurface && m_pCurSurface->IsVehicle())) { AnimationId fireAnim = GetFireAnimGround(ourWeapon, false); if (weaponAnimAssoc->animId == fireAnim) weaponAnimAssoc->SetCurrentTime(0.1f); else { if (GetFireAnimGround(ourWeapon, false)) { weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, fireAnim, 8.0f); } else { weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_KICKGROUND, 8.0f); } } weaponAnimAssoc->SetFinishCallback(FinishedAttackCB, this); } else if (GetSecondFireAnim(ourWeapon)) { if (weaponAnimAssoc->animId == GetSecondFireAnim(ourWeapon)) { weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, ANIM_WEAPON_FIRE, 8.0f); } else { weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetSecondFireAnim(ourWeapon), 8.0f); } weaponAnimAssoc->SetFinishCallback(FinishedAttackCB, this); } else { weaponAnimAssoc->SetCurrentTime(animLoopStart); weaponAnimAssoc->SetRun(); } } else if (IsPlayer() && m_pPointGunAt && bIsAimingGun && GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING) { weaponAnimAssoc->SetCurrentTime(animLoopEnd); weaponAnimAssoc->flags &= ~ASSOC_RUNNING; SetPointGunAt(m_pPointGunAt); } else { ClearAimFlag(); // Echoes of bullets, at the end of the attack. (Bug: doesn't play while reloading) if (weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep < animLoopEnd) DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType); // Fun fact: removing this part leds to reloading flamethrower if (GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER && weaponAnimAssoc->IsRunning()) { weaponAnimAssoc->flags |= ASSOC_DELETEFADEDOUT; weaponAnimAssoc->flags &= ~ASSOC_RUNNING; weaponAnimAssoc->blendDelta = -4.0f; } } } if (weaponAnimAssoc->currentTime > delayBetweenAnimAndFire) attackShouldContinue = false; bIsAttacking = attackShouldContinue; } void CPed::StartFightAttack(uint8 buttonPressure) { if (!IsPedInControl() || (m_attackTimer > CTimer::GetTimeInMilliseconds() && buttonPressure != 0)) return; if (m_nPedState == PED_FIGHT) { m_fightButtonPressure = buttonPressure; return; } if (m_nPedState != PED_AIM_GUN) SetStoredState(); if (m_nWaitState != WAITSTATE_FALSE) { ClearWaitState(); RestoreHeadingRate(); } CAnimBlendAssociation* animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP1); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP2); if (animAssoc) { RestoreHeadingRate(); } SetMoveState(PEDMOVE_NONE); m_nStoredMoveState = PEDMOVE_NONE; bool fightWithWeapon = false; CAnimBlendAssociation *fightIdleAssoc; CWeaponInfo* weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { if (GetFightIdleWithMeleeAnim(weaponInfo)) { fightIdleAssoc = CAnimManager::BlendAnimation(GetClump(), weaponInfo->m_AnimToPlay, GetFightIdleWithMeleeAnim(weaponInfo), 1000.0f); fightWithWeapon = true; } else { fightIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE, 1000.0f); } } else { fightIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE, 1000.0f); } m_lastFightMove = FIGHTMOVE_IDLE; m_curFightMove = IsPlayer() ? ChooseAttackPlayer(buttonPressure, fightWithWeapon) : ChooseAttackAI(buttonPressure, fightWithWeapon); SetPedState(PED_FIGHT); m_fightButtonPressure = 0; if (m_curFightMove > FIGHTMOVE_NULL && m_curFightMove != FIGHTMOVE_IDLE) { animAssoc = CAnimManager::BlendAnimation(GetClump(), m_curFightMove < FIGHTMOVE_MELEE1 ? ASSOCGRP_STD : weaponInfo->m_AnimToPlay, tFightMoves[m_curFightMove].animId, 8.0f); if (weaponInfo->m_AnimToPlay == ASSOCGRP_KNIFE && m_curFightMove >= FIGHTMOVE_MELEE1) { switch (GetWeapon()->m_eWeaponType) { case WEAPONTYPE_SCREWDRIVER: case WEAPONTYPE_KNIFE: animAssoc->speed = 1.05f; break; case WEAPONTYPE_GOLFCLUB: case WEAPONTYPE_NIGHTSTICK: case WEAPONTYPE_BASEBALLBAT: case WEAPONTYPE_HAMMER: case WEAPONTYPE_KATANA: animAssoc->speed = 0.8f; break; case WEAPONTYPE_CLEAVER: case WEAPONTYPE_MACHETE: animAssoc->speed = 0.9f; break; } } else { if (m_curFightMove == FIGHTMOVE_BACKKICK) animAssoc->speed = 1.15f; else animAssoc->speed = 0.8f; } if (IsPlayer()) animAssoc->SetCurrentTime(0.08f); animAssoc->SetFinishCallback(FinishFightMoveCB, this); } else { m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; } m_fightState = FIGHTSTATE_NO_MOVE; m_takeAStepAfterAttack = false; bIsAttacking = true; if (IsPlayer()) nPlayerInComboMove = 0; } void CPed::StartFightDefend(uint8 direction, uint8 hitLevel, uint8 unk) { if (m_nPedState == PED_DEAD) { if (CGame::nastyGame) { if (hitLevel == HITLEVEL_GROUND) { CAnimBlendAssociation *floorHitAssoc; if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) { floorHitAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); } else { floorHitAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[FIGHTMOVE_HITONFLOOR].animId, 8.0f); } if (floorHitAssoc) { floorHitAssoc->SetCurrentTime(0.0f); floorHitAssoc->SetRun(); floorHitAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; } } if (CGame::nastyGame) { CVector headPos = GetNodePosition(PED_HEAD); for(int i = 0; i < 4; ++i) { CVector bloodDir(0.0f, 0.0f, 0.1f); CVector bloodPos = headPos - 0.2f * GetForward(); CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, bloodDir, nil, 0.0f, 0, 0, 0, 0); } } } } else if (m_nPedState == PED_FALL) { if (hitLevel == HITLEVEL_GROUND && !IsPedHeadAbovePos(-0.3f)) { CAnimBlendAssociation *floorHitAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL) ? CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f) : CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); if (floorHitAssoc) { floorHitAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; floorHitAssoc->flags |= ASSOC_DELETEFADEDOUT; } } } else if (IsPedInControl()) { if ((IsPlayer() && m_nPedState != PED_FIGHT && ((CPlayerPed*)this)->m_fMoveSpeed > 1.0f) || (!IsPlayer() && m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE)) { if (hitLevel != HITLEVEL_HIGH && hitLevel != HITLEVEL_LOW || (IsPlayer() || CGeneral::GetRandomNumber() & 1) && CGeneral::GetRandomNumber() & 7) { if (IsPlayer() || CGeneral::GetRandomNumber() & 1) { AnimationId shotAnim; switch (direction) { case 1: shotAnim = ANIM_STD_HITBYGUN_LEFT; break; case 2: shotAnim = ANIM_STD_HITBYGUN_BACK; break; case 3: shotAnim = ANIM_STD_HITBYGUN_RIGHT; break; default: shotAnim = ANIM_STD_HITBYGUN_FRONT; break; } CAnimBlendAssociation *shotAssoc = RpAnimBlendClumpGetAssociation(GetClump(), shotAnim); if (!shotAssoc || shotAssoc->blendDelta < 0.0f) shotAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, shotAnim, 8.0f); shotAssoc->SetCurrentTime(0.0f); shotAssoc->SetRun(); shotAssoc->flags |= ASSOC_FADEOUTWHENDONE; } else { int time = CGeneral::GetRandomNumberInRange(1000, 3000); SetWaitState(WAITSTATE_PLAYANIM_DUCK, &time); } } else { bool fall = true; AnimationId hitAnim; switch (direction) { case 1: hitAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: if (CGeneral::GetRandomNumber() & 1) { fall = false; hitAnim = ANIM_STD_HIT_BACK; } else { hitAnim = ANIM_STD_HIGHIMPACT_BACK; } break; case 3: hitAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: if (hitLevel == HITLEVEL_LOW) { hitAnim = ANIM_STD_KO_SHOT_STOMACH; } else if (CGeneral::GetRandomNumber() & 1) { fall = false; hitAnim = ANIM_STD_HIT_WALK; } else if (CGeneral::GetRandomNumber() & 1) { fall = false; hitAnim = ANIM_STD_HIT_HEAD; } else { hitAnim = ANIM_STD_KO_SHOT_FACE; } break; } if (fall) { SetFall(500, hitAnim, false); } else { CAnimBlendAssociation *hitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), hitAnim); if (!hitAssoc || hitAssoc->blendDelta < 0.0f) hitAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, hitAnim, 8.0f); hitAssoc->SetCurrentTime(0.0f); hitAssoc->SetRun(); hitAssoc->flags |= ASSOC_FADEOUTWHENDONE; } } Say(SOUND_PED_DEFEND); } else { Say(SOUND_PED_DEFEND); switch (hitLevel) { case HITLEVEL_GROUND: m_curFightMove = FIGHTMOVE_HITONFLOOR; break; case HITLEVEL_LOW: if (direction == 2 && (!IsPlayer() || ((CGeneral::GetRandomNumber() & 1) && m_fHealth < 30.0f))) { SetFall(1000, ANIM_STD_HIGHIMPACT_BACK, false); Say(SOUND_PED_DEFEND); return; } else if (direction != 2 && !IsPlayer() && (CGeneral::GetRandomNumber() & 1) && m_fHealth < 30.0f) { SetFall(1000, ANIM_STD_KO_SHOT_STOMACH, false); Say(SOUND_PED_DEFEND); return; } m_curFightMove = FIGHTMOVE_HITBODY; break; case HITLEVEL_HIGH: switch (direction) { case 1: m_curFightMove = FIGHTMOVE_HITLEFT; break; case 2: m_curFightMove = FIGHTMOVE_HITBACK; break; case 3: m_curFightMove = FIGHTMOVE_HITRIGHT; break; default: if (unk <= 5) m_curFightMove = FIGHTMOVE_HITHEAD; else m_curFightMove = FIGHTMOVE_HITBIGSTEP; break; } break; default: switch (direction) { case 1: m_curFightMove = FIGHTMOVE_HITLEFT; break; case 2: m_curFightMove = FIGHTMOVE_HITBACK; break; case 3: m_curFightMove = FIGHTMOVE_HITRIGHT; break; default: if (unk <= 5) m_curFightMove = FIGHTMOVE_HITCHEST; else m_curFightMove = FIGHTMOVE_HITBIGSTEP; break; } break; } if (m_nPedState == PED_GETUP && !IsPedHeadAbovePos(0.0f)) m_curFightMove = FIGHTMOVE_HITONFLOOR; if (m_nPedState == PED_FIGHT) { CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 8.0f); moveAssoc->SetCurrentTime(0.0f); moveAssoc->SetFinishCallback(FinishFightMoveCB, this); if (IsPlayer()) moveAssoc->speed = 1.2f; m_takeAStepAfterAttack = 0; m_fightButtonPressure = 0; } else if (IsPlayer() && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE && !CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_FIGHTMODE)) { CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 4.0f); moveAssoc->SetCurrentTime(0.0f); moveAssoc->speed = 1.2f; } else { if (m_nPedState != PED_AIM_GUN && m_nPedState != PED_ATTACK) SetStoredState(); if (m_nWaitState != WAITSTATE_FALSE) { ClearWaitState(); RestoreHeadingRate(); } SetPedState(PED_FIGHT); m_fightButtonPressure = 0; m_lastFightMove = FIGHTMOVE_IDLE; RpAnimBlendClumpRemoveAssociations(GetClump(), ASSOC_REPEAT); CAnimBlendAssociation *walkStartAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_STARTWALK); if (walkStartAssoc) { walkStartAssoc->flags |= ASSOC_DELETEFADEDOUT; walkStartAssoc->blendDelta = -1000.0f; } CAnimBlendAssociation *walkStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP1); if (!walkStopAssoc) walkStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP2); if (walkStopAssoc) { walkStopAssoc->flags |= ASSOC_DELETEFADEDOUT; walkStopAssoc->blendDelta = -1000.0f; RestoreHeadingRate(); } SetMoveState(PEDMOVE_NONE); m_nStoredMoveState = PEDMOVE_NONE; CAnimBlendAssociation *fightIdleAssoc; if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); if (GetFightIdleWithMeleeAnim(weaponInfo)) { fightIdleAssoc = CAnimManager::AddAnimation(GetClump(), weaponInfo->m_AnimToPlay, GetFightIdleWithMeleeAnim(weaponInfo)); } else { fightIdleAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE); } } else { fightIdleAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE); } fightIdleAssoc->blendAmount = 1.0f; CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 8.0f); moveAssoc->SetFinishCallback(FinishFightMoveCB, this); m_fightState = FIGHTSTATE_NO_MOVE; m_takeAStepAfterAttack = false; bIsAttacking = true; } if (m_pedInObjective && m_pedInObjective->IsPlayer() && !IsPlayer()) ((CPlayerPed*)m_pedInObjective)->RemovePedFromMeleeList(this); } } } void CPed::Fight(void) { CAnimBlendAssociation *currentAssoc, *animAssoc; bool fightWithWeapon = false; eWeaponType weapon = GetWeapon()->m_eWeaponType; CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon); if (weaponInfo->IsFlagSet(WEAPONFLAG_FIGHTMODE) && weapon != WEAPONTYPE_UNARMED) { fightWithWeapon = true; tFightMoves[FIGHTMOVE_MELEE1].startFireTime = weaponInfo->m_fAnimFrameFire; tFightMoves[FIGHTMOVE_MELEE1].endFireTime = weaponInfo->m_fAnimLoopEnd; tFightMoves[FIGHTMOVE_MELEE2].startFireTime = weaponInfo->m_fAnim2FrameFire; tFightMoves[FIGHTMOVE_MELEE2].endFireTime = weaponInfo->m_fAnim2LoopEnd; tFightMoves[FIGHTMOVE_MELEE3].startFireTime = weaponInfo->m_fAnim2FrameFire; tFightMoves[FIGHTMOVE_MELEE3].endFireTime = weaponInfo->m_fAnim2LoopEnd; } switch (m_curFightMove) { case FIGHTMOVE_NULL: return; case FIGHTMOVE_IDLE2NORM: m_curFightMove = FIGHTMOVE_NULL; RestorePreviousState(); // FIX: Uninitialized currentAssoc = nil; break; case FIGHTMOVE_IDLE: currentAssoc = nil; break; default: currentAssoc = RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId); break; } if (m_curFightMove == FIGHTMOVE_SHUFFLE_F && !currentAssoc) currentAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_SHUFFLE_B); if (IsPlayer() && currentAssoc && weapon == WEAPONTYPE_KATANA) { if (m_curFightMove == FIGHTMOVE_MELEE1 || m_curFightMove == FIGHTMOVE_MELEE2) { static float streakDelay = 0.2f; if (tFightMoves[m_curFightMove].startFireTime - streakDelay < currentAssoc->currentTime && streakDelay + tFightMoves[m_curFightMove].endFireTime > currentAssoc->currentTime) { CSpecialFX::AddWeaponStreak(GetWeapon()->m_eWeaponType); } } } if (!bIsAttacking && IsPlayer()) { if (currentAssoc) { currentAssoc->blendDelta = -1000.0f; currentAssoc->flags |= ASSOC_DELETEFADEDOUT; currentAssoc->flags &= ~ASSOC_RUNNING; } if (m_takeAStepAfterAttack) EndFight(ENDFIGHT_WITH_A_STEP); else EndFight(ENDFIGHT_FAST); } else if (currentAssoc && m_fightState > FIGHTSTATE_MOVE_FINISHED) { float animTime = currentAssoc->currentTime; FightMove &curMove = tFightMoves[m_curFightMove]; if (curMove.hitLevel != HITLEVEL_NULL && animTime > curMove.startFireTime && animTime <= curMove.endFireTime && m_fightState >= FIGHTSTATE_NO_MOVE) { if (animTime > curMove.startFireTime && animTime - currentAssoc->timeStep < curMove.startFireTime && (IsPlayer() || weapon != WEAPONTYPE_UNARMED)) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_MELEE_ATTACK_START, weapon << 8); } CVector touchingNodePos(0.0f, 0.0f, 0.0f); switch (m_curFightMove) { case FIGHTMOVE_KNEE: TransformToNode(touchingNodePos, PED_LOWERLEGR); break; case FIGHTMOVE_PUNCHHOOK: case FIGHTMOVE_PUNCHJAB: TransformToNode(touchingNodePos, PED_HANDL); break; case FIGHTMOVE_LONGKICK: case FIGHTMOVE_ROUNDHOUSE: case FIGHTMOVE_FWDLEFT: case FIGHTMOVE_BACKRIGHT: case FIGHTMOVE_GROUNDKICK: TransformToNode(touchingNodePos, PED_FOOTR); break; case FIGHTMOVE_FWDRIGHT: TransformToNode(touchingNodePos, PED_HEAD); break; case FIGHTMOVE_BACKKICK: case FIGHTMOVE_BACKFLIP: TransformToNode(touchingNodePos, PED_FOOTL); break; case FIGHTMOVE_BACKLEFT: TransformToNode(touchingNodePos, PED_UPPERARML); break; default: TransformToNode(touchingNodePos, PED_HANDR); break; } FightStrike(touchingNodePos, fightWithWeapon); m_fightButtonPressure = 0; return; } if (curMove.hitLevel != HITLEVEL_NULL) { if (animTime > curMove.endFireTime && weaponInfo->m_AnimToPlay != ASSOCGRP_KNIFE) { if (IsPlayer()) currentAssoc->speed = 1.0f; else currentAssoc->speed = 0.8f; } if (IsPlayer() && !nPlayerInComboMove && !fightWithWeapon) { if (curMove.comboFollowOnTime > 0.0f && m_fightButtonPressure != 0 && animTime > curMove.comboFollowOnTime) { m_lastFightMove = m_curFightMove; // Notice that it increases fight move index, because we're in combo! animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[++m_curFightMove].animId, 8.0f); animAssoc->SetFinishCallback(FinishFightMoveCB, this); animAssoc->SetCurrentTime(0.1f * animAssoc->hierarchy->totalLength); animAssoc->speed = 0.8f; m_fightButtonPressure = 0; nPlayerInComboMove = 1; } } } } else if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE && !fightWithWeapon) { EndFight(ENDFIGHT_FAST); } else if (m_fightButtonPressure != 0) { if (!IsPlayer()) Say(SOUND_PED_ATTACK); if (m_curFightMove != FIGHTMOVE_IDLE) m_lastFightMove = m_curFightMove; m_curFightMove = IsPlayer() ? ChooseAttackPlayer(m_fightButtonPressure, fightWithWeapon) : ChooseAttackAI(m_fightButtonPressure, fightWithWeapon); if (m_curFightMove != FIGHTMOVE_IDLE) { animAssoc = CAnimManager::BlendAnimation(GetClump(), m_curFightMove < FIGHTMOVE_MELEE1 ? ASSOCGRP_STD : weaponInfo->m_AnimToPlay, tFightMoves[m_curFightMove].animId, 8.0f); if (weaponInfo->m_AnimToPlay != ASSOCGRP_KNIFE || m_curFightMove < FIGHTMOVE_MELEE1) { if (m_curFightMove == FIGHTMOVE_BACKKICK) animAssoc->speed = 1.15f; else animAssoc->speed = 0.8f; } else { switch (GetWeapon()->m_eWeaponType) { case WEAPONTYPE_SCREWDRIVER: case WEAPONTYPE_KNIFE: animAssoc->speed = 1.05f; break; case WEAPONTYPE_GOLFCLUB: case WEAPONTYPE_NIGHTSTICK: case WEAPONTYPE_BASEBALLBAT: case WEAPONTYPE_HAMMER: case WEAPONTYPE_KATANA: animAssoc->speed = 0.8f; break; case WEAPONTYPE_CLEAVER: case WEAPONTYPE_MACHETE: animAssoc->speed = 0.9f; break; } } if (m_fightState == FIGHTSTATE_MOVE_FINISHED && animAssoc->currentTime != 0.0f) { animAssoc->SetRun(); if (!IsPlayer()) animAssoc->SetCurrentTime(0.0f); } if (IsPlayer()) animAssoc->SetCurrentTime(0.08f); animAssoc->SetFinishCallback(FinishFightMoveCB, this); m_fightButtonPressure = 0; } m_fightState = FIGHTSTATE_NO_MOVE; } else if (m_takeAStepAfterAttack && m_curFightMove != FIGHTMOVE_SHUFFLE_F #ifndef FIX_BUGS && CheckForPedsOnGroundToAttack(this, nil) == 4) { #else && CheckForPedsOnGroundToAttack(this, nil) == PED_IN_FRONT_OF_ATTACKER) { #endif m_lastFightMove = m_curFightMove; m_curFightMove = FIGHTMOVE_SHUFFLE_F; animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId); if (animAssoc) { animAssoc->SetCurrentTime(0.0f); animAssoc->blendDelta = 4.0f; animAssoc->SetRun(); } else { animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 32.0f); } animAssoc->SetFinishCallback(FinishFightMoveCB, this); m_fightState = FIGHTSTATE_NO_MOVE; m_fightButtonPressure = 0; m_takeAStepAfterAttack = false; } else if (m_takeAStepAfterAttack) { EndFight(ENDFIGHT_FAST); } else if (m_curFightMove == FIGHTMOVE_IDLE) { if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { EndFight(ENDFIGHT_NORMAL); } } else { m_lastFightMove = m_curFightMove; m_curFightMove = FIGHTMOVE_IDLE; if (IsPlayer()) m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 500; else m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; } } int32 CPed::ChooseAttackAI(uint8 buttonPressure, bool fightWithWeapon) { eWeaponType weapon = GetWeapon()->m_eWeaponType; CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon); if (!fightWithWeapon && weapon != WEAPONTYPE_UNARMED && weapon != WEAPONTYPE_BRASSKNUCKLE) { return FIGHTMOVE_PUNCH; } if (!m_pedInObjective) return FIGHTMOVE_IDLE; if (buttonPressure == 0) return FIGHTMOVE_IDLE; uint16 pedFeatures = m_pedStats->m_flags; bool punchOnly = !!(pedFeatures & STAT_PUNCH_ONLY); bool canRoundhouse = !!(pedFeatures & STAT_CAN_ROUNDHOUSE); bool canKneeHead = !!(pedFeatures & STAT_CAN_KNEE_HEAD); bool canKick = !!(pedFeatures & STAT_CAN_KICK); bool hasShoppingBags = !!(pedFeatures & STAT_SHOPPING_BAGS); CVector distVec(m_pedInObjective->GetPosition() - GetPosition()); float dist = distVec.Magnitude(); m_fRotationDest = CGeneral::LimitRadianAngle(distVec.Heading()); m_fRotationCur = m_fRotationDest; if (fightWithWeapon) { if (m_pedInObjective->OnGroundOrGettingUp()) { if (CGame::nastyGame && dist < 1.2f && !m_pedInObjective->IsPlayer() && (m_pedInObjective->m_nPedState == PED_DEAD || !m_pedInObjective->IsPedHeadAbovePos(-0.3f))) { if (weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_2ND)) return FIGHTMOVE_MELEE2; if (weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_3RD)) return FIGHTMOVE_MELEE3; return FIGHTMOVE_GROUNDKICK; } else { return FIGHTMOVE_IDLE; } } if (dist < 2.f) { if (m_curFightMove == FIGHTMOVE_MELEE1) { if (GetSecondFireAnim(weaponInfo)) return FIGHTMOVE_MELEE2; } if (m_curFightMove == FIGHTMOVE_MELEE2) { if (GetFinishingAttackAnim(weaponInfo)) return FIGHTMOVE_MELEE3; } return FIGHTMOVE_MELEE1; } return FIGHTMOVE_SHUFFLE_F; } if (!hasShoppingBags) { if (punchOnly) { if (dist < 1.4f) return FIGHTMOVE_PUNCH; } else { if (m_pedInObjective->OnGroundOrGettingUp()) { if (CGame::nastyGame && dist < 1.2f && !m_pedInObjective->IsPlayer() && (m_pedInObjective->m_nPedState == PED_DEAD || !m_pedInObjective->IsPedHeadAbovePos(-0.3f))) { return FIGHTMOVE_GROUNDKICK; } else { return FIGHTMOVE_IDLE; } } if (dist < 0.95f && canKneeHead) return FIGHTMOVE_KNEE; if (dist < 1.4f) return FIGHTMOVE_PUNCH; if (dist < 2.f && canKick) { int nextMove = FIGHTMOVE_LONGKICK; if (canRoundhouse && CGeneral::GetRandomNumber() & 1) nextMove = FIGHTMOVE_ROUNDHOUSE; return nextMove; } } return FIGHTMOVE_SHUFFLE_F; } if (dist < 2.f) return FIGHTMOVE_ROUNDHOUSE; else return FIGHTMOVE_SHUFFLE_F; } int32 CPed::ChooseAttackPlayer(uint8 buttonPressure, bool fightWithWeapon) { CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); const float maxAttackDist = 2.7f; float weaponAttackDist = 2.0f; CPed *victimPed = nil; CPed *walkUpTo = nil; CPed *groundAttackDeadPed = nil; CPed *groundAttackAlivePed = nil; if (fightWithWeapon) weaponAttackDist = weaponInfo->m_fRange; bool willWalkUp = false; PedFightMoves choosenMove = FIGHTMOVE_IDLE; int numPedsWeCanReach = 0; if (m_takeAStepAfterAttack) willWalkUp = true; float groundAttackDeadAngle, groundAttackAliveAngle, walkAngle, victimAngle, distToVictim; for (int i = 0; i < m_numNearPeds; ++i) { CPed *nearPed = m_nearPeds[i]; CVector distVec(nearPed->GetPosition() - GetPosition()); float dist = distVec.Magnitude(); if (dist < maxAttackDist) { float nearPedAngle = CGeneral::LimitRadianAngle(distVec.Heading()); m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); float neededTurn = Abs(nearPedAngle - m_fRotationCur); if (neededTurn > PI) neededTurn = TWOPI - neededTurn; if (!nearPed->OnGroundOrGettingUp() && nearPed->m_nWaitState != WAITSTATE_SUN_BATHE_IDLE) { if (!willWalkUp || neededTurn <= DEGTORAD(45.0f)) { if (neededTurn <= DEGTORAD(30.0f) || nearPed->m_pedInObjective == this && (nearPed->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || nearPed->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS)) { if (dist < weaponAttackDist) { if (!victimPed || nearPed->m_attackTimer < victimPed->m_attackTimer && nearPed->m_attackTimer > CTimer::GetTimeInMilliseconds() - 100) { victimPed = nearPed; victimAngle = nearPedAngle; distToVictim = dist; } ++numPedsWeCanReach; } else { if (neededTurn < DEGTORAD(30.0f)) { walkUpTo = nearPed; walkAngle = nearPedAngle; } } } } } else if (CGame::nastyGame && dist < 1.2f && neededTurn < DEGTORAD(55.0f)) { if (!nearPed->DyingOrDead() || groundAttackDeadPed) { if (!nearPed->IsPedHeadAbovePos(-0.3f)) { groundAttackAlivePed = nearPed; groundAttackAliveAngle = nearPedAngle; } } else { groundAttackDeadPed = nearPed; groundAttackDeadAngle = nearPedAngle; } ++numPedsWeCanReach; } else if (dist > 1.4f && dist < maxAttackDist && neededTurn < DEGTORAD(30.0f)) { if (!walkUpTo) { walkUpTo = nearPed; walkAngle = nearPedAngle; } #ifdef FIX_BUGS if(dist < 2.1f) #endif ++numPedsWeCanReach; } } } if (victimPed) { float adjustedAngleDiff = victimAngle - m_fRotationCur + DEGTORAD(30.0f); if (adjustedAngleDiff < 0.0f) adjustedAngleDiff += TWOPI; int16 dir = Floor(adjustedAngleDiff / DEGTORAD(60.0f)); // Just focus on who we're fighting with, don't care peds on ground if (numPedsWeCanReach < 2 || fightWithWeapon) { float angleDiff = Abs(victimAngle - m_fRotationCur); if (angleDiff > PI) angleDiff = TWOPI - angleDiff; if (angleDiff < DEGTORAD(60.0f)) dir = 0; // forward } int16 randVal = CGeneral::GetRandomNumber() & 3; switch (dir) { case 0: // forward if (fightWithWeapon) { if (distToVictim < 0.95f - 0.2f && m_nPedState == PED_FIGHT) { choosenMove = FIGHTMOVE_KNEE; } else { if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CLEAVER) { if (distToVictim < 0.85f * weaponInfo->m_fRange) choosenMove = FIGHTMOVE_MELEE1; else choosenMove = FIGHTMOVE_SHUFFLE_F; } else { float weaponRange = weaponInfo->m_fRange; if (distToVictim < 0.75f * weaponRange && GetWeapon()->m_eWeaponType != WEAPONTYPE_SCREWDRIVER) { if (m_lastFightMove == FIGHTMOVE_MELEE1 && GetFinishingAttackAnim(weaponInfo)) { choosenMove = FIGHTMOVE_MELEE2; } else if (m_lastFightMove == FIGHTMOVE_MELEE2 && GetFinishingAttackAnim(weaponInfo)) { choosenMove = FIGHTMOVE_MELEE3; } else { choosenMove = FIGHTMOVE_MELEE1; } } else if (distToVictim < weaponRange && GetFinishingAttackAnim(weaponInfo)) { choosenMove = FIGHTMOVE_MELEE3; } else { choosenMove = FIGHTMOVE_SHUFFLE_F; } } } } else if (distToVictim < 0.95f && m_nPedState == PED_FIGHT) { choosenMove = FIGHTMOVE_KNEE; } else if (distToVictim < 1.4f) { if (m_curFightMove == FIGHTMOVE_PUNCHJAB) { choosenMove = FIGHTMOVE_PUNCH; } else if (m_curFightMove != FIGHTMOVE_PUNCH || randVal != 1) { if (randVal == 2) choosenMove = FIGHTMOVE_PUNCH; else choosenMove = FIGHTMOVE_PUNCHJAB; } else { choosenMove = FIGHTMOVE_LONGKICK; } } else { choosenMove = FIGHTMOVE_LONGKICK; } break; case 1: choosenMove = FIGHTMOVE_FWDLEFT; break; case 2: choosenMove = FIGHTMOVE_BACKLEFT; break; case 3: choosenMove = FIGHTMOVE_BACKKICK; break; case 4: choosenMove = FIGHTMOVE_BACKRIGHT; break; default: choosenMove = FIGHTMOVE_FWDRIGHT; break; } // forward if (dir == 0) { m_fRotationDest = CGeneral::LimitRadianAngle(victimAngle); } else { m_fRotationDest = victimAngle - dir * DEGTORAD(60.0f); m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); } m_fRotationCur = m_fRotationDest; Say(SOUND_PED_ATTACK); } else if (groundAttackAlivePed || groundAttackDeadPed) { if (fightWithWeapon && weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_2ND)) { choosenMove = FIGHTMOVE_MELEE2; } else if (fightWithWeapon && weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_3RD)) { choosenMove = FIGHTMOVE_MELEE3; } else { choosenMove = FIGHTMOVE_GROUNDKICK; } if (groundAttackAlivePed) m_fRotationDest = groundAttackAliveAngle; else m_fRotationDest = groundAttackDeadAngle; m_fRotationCur = m_fRotationDest; m_lookTimer = 0; if (groundAttackAlivePed) SetLookFlag(groundAttackAlivePed, 1, 0); else SetLookFlag(groundAttackDeadPed, 1, 0); SetLookTimer(1500u); } else if (walkUpTo) { choosenMove = FIGHTMOVE_SHUFFLE_F; m_fRotationCur = m_fRotationDest = walkAngle; m_lookTimer = 0; SetLookFlag(walkUpTo, true); SetLookTimer(1500); } else if (fightWithWeapon) { // No enemy, fight with space if (GetWeapon()->m_eWeaponType == WEAPONTYPE_SCREWDRIVER) { choosenMove = FIGHTMOVE_MELEE3; } else { if (m_lastFightMove == FIGHTMOVE_MELEE1 && GetFinishingAttackAnim(weaponInfo)) { choosenMove = FIGHTMOVE_MELEE2; } else if (m_lastFightMove == FIGHTMOVE_MELEE2 && GetFinishingAttackAnim(weaponInfo)) { choosenMove = FIGHTMOVE_MELEE3; } else { choosenMove = FIGHTMOVE_MELEE1; } } } else { // Max number GetRandomNumberInRange returns is max-1 #ifdef FIX_BUGS switch (CGeneral::GetRandomNumberInRange(0,4)) { #else switch (CGeneral::GetRandomNumberInRange(0,3)) { #endif case 0: choosenMove = FIGHTMOVE_PUNCHJAB; break; case 1: choosenMove = FIGHTMOVE_PUNCH; break; case 2: choosenMove = FIGHTMOVE_LONGKICK; break; case 3: choosenMove = FIGHTMOVE_KNEE; break; default: break; } } return choosenMove; } void CPed::EndFight(uint8 endType) { if (m_nPedState != PED_FIGHT) return; m_curFightMove = FIGHTMOVE_NULL; RestorePreviousState(); CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); if (animAssoc) animAssoc->flags |= ASSOC_DELETEFADEDOUT; switch (endType) { case ENDFIGHT_NORMAL: CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_2IDLE, 8.0f); break; case ENDFIGHT_WITH_A_STEP: CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 1.0f); CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_STARTWALK, 8.0f); break; case ENDFIGHT_FAST: CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_2IDLE, 8.0f)->speed = 2.0f; break; default: break; } m_nWaitTimer = 0; } void CPed::PlayHitSound(CPed *hitTo) { // That was very complicated to reverse for me... // First index is our fight move ID (from 1 to 17, total 17), second is the one of we fight with (from 18 to 27, total 10). enum { S37 = SOUND_FIGHT_37, S38 = SOUND_FIGHT_38, S39 = SOUND_FIGHT_39, S40 = SOUND_FIGHT_40, S41 = SOUND_FIGHT_41, S42 = SOUND_FIGHT_42, S43 = SOUND_FIGHT_43, S44 = SOUND_FIGHT_44, S45 = SOUND_FIGHT_45, S46 = SOUND_FIGHT_46, S47 = SOUND_FIGHT_47, S48 = SOUND_FIGHT_48, NO_SND = SOUND_NO_SOUND }; const uint16 hitSoundsByFightMoves[17][10] = { { S37, S46, S41, S41, S46, S46, S40, S41, S43, S40 }, { NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND }, { NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND, NO_SND }, { S46, S46, S46, S46, S37, S47, S37, S38, S43, S38 }, { S46, S46, S46, S46, S46, S46, S40, S41, S43, S46 }, { S46, S46, S46, S46, S46, S46, S40, S41, S43, S40 }, { S46, S46, S46, S46, S46, S46, S40, S41, S43, S40 }, { S46, S46, S37, S46, S37, S47, S40, S47, S43, S37 }, { S46, S46, S46, S46, S46, S46, S43, S44, S43, S43 }, { S37, S46, S46, S46, S38, S47, S40, S38, S43, S46 }, { S46, S37, S46, S37, S39, S46, S40, S39, S43, S37 }, { S46, S37, S46, S46, S38, S47, S40, S38, S43, S46 }, { S37, S37, S46, S46, S38, S47, S48, S38, S43, S37 }, { S46, S46, S46, S46, S37, S46, S40, S38, S43, S46 }, { S46, S46, S46, S37, S39, S46, S40, S39, S43, S46 }, { S37, S46, S46, S46, S37, S46, S40, S37, S43, S46 }, { S43, S43, S43, S43, S43, S43, S43, S43, S43, S43 } }; eWeaponType weapon = GetWeapon()->m_eWeaponType; CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon); if (weaponInfo->m_AnimToPlay == ASSOCGRP_KNIFE) { if (m_curFightMove >= FIGHTMOVE_MELEE1) { if (m_curFightMove == FIGHTMOVE_MELEE3) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_BAT_ATTACK, (weapon << 8) | ENTITY_TYPE_PED); } else { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_KNIFE_ATTACK, (weapon << 8) | ENTITY_TYPE_PED); } return; } } // This is why first dimension is between FightMove 1 and 17. if (m_curFightMove <= FIGHTMOVE_NULL || m_curFightMove >= FIGHTMOVE_HITFRONT) return; uint16 soundId; // And this is why second dimension is between 18 and 27. if (hitTo->m_curFightMove > FIGHTMOVE_GROUNDKICK && hitTo->m_curFightMove < FIGHTMOVE_IDLE2NORM) { soundId = hitSoundsByFightMoves[m_curFightMove - FIGHTMOVE_STDPUNCH][hitTo->m_curFightMove - FIGHTMOVE_HITFRONT]; } else { if (hitTo->m_nPedState == PED_DEAD || hitTo->UseGroundColModel()) { soundId = hitSoundsByFightMoves[m_curFightMove - FIGHTMOVE_STDPUNCH][FIGHTMOVE_HITONFLOOR - FIGHTMOVE_HITFRONT]; } else { soundId = hitSoundsByFightMoves[m_curFightMove - FIGHTMOVE_STDPUNCH][FIGHTMOVE_HITFRONT - FIGHTMOVE_HITFRONT]; } } if (soundId != NO_SND) DMAudio.PlayOneShot(m_audioEntityId, soundId, (weapon << 8) | ENTITY_TYPE_PED); } bool CPed::FightStrike(CVector &touchedNodePos, bool fightWithWeapon) { CColModel *hisCol; CVector attackDistance; float maxDistanceToBeat; CPed *nearPed; CVector extendedTouchPoint; CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); float radius = tFightMoves[m_curFightMove].strikeRadius; if (fightWithWeapon) radius = weaponInfo->m_fRadius; if (m_fightState == FIGHTSTATE_JUST_ATTACKED) return false; if (this == FindPlayerPed() && fightWithWeapon && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) CGlass::BreakGlassPhysically(touchedNodePos, radius); for (int i = 0; i < m_numNearPeds; i++) { int8 pedFound = 0; nearPed = m_nearPeds[i]; if (!fightWithWeapon && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE) maxDistanceToBeat = nearPed->GetBoundRadius() + radius + 0.1f; else maxDistanceToBeat = nearPed->GetBoundRadius() + radius; if ((nearPed->bUsesCollision || nearPed->m_nPedState == PED_DEAD) && (m_pedInObjective != FindPlayerPed() || nearPed == FindPlayerPed())) { CVector nearPedCentre; // Have to animate a skinned clump because the initial col model is useless hisCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(nearPed->GetModelIndex()))->AnimatePedColModelSkinnedWorld(nearPed->GetClump()); nearPed->GetBoundCentre(nearPedCentre); CVector potentialAttackDistance = nearPedCentre - touchedNodePos; // He can beat us if (sq(maxDistanceToBeat) > potentialAttackDistance.MagnitudeSqr()) { for (int j = 0; j < hisCol->numSpheres; j++) { attackDistance = hisCol->spheres[j].center; attackDistance -= touchedNodePos; CColSphere *hisPieces = hisCol->spheres; maxDistanceToBeat = hisPieces[j].radius + radius; // We can beat him too if (sq(maxDistanceToBeat) > attackDistance.MagnitudeSqr()) { FightHitPed(nearPed, touchedNodePos, attackDistance, hisPieces[j].piece); pedFound = 1; break; } } } if (!pedFound && !fightWithWeapon) { extendedTouchPoint = touchedNodePos - GetPosition(); if (DotProduct(touchedNodePos - GetPosition(), nearPed->GetPosition() - GetPosition()) > 0.f) { if (m_curFightMove == FIGHTMOVE_GROUNDKICK) { extendedTouchPoint += tFightMoves[FIGHTMOVE_GROUNDKICK].extendReachMultiplier * GetForward(); } else { extendedTouchPoint.x *= tFightMoves[m_curFightMove].extendReachMultiplier; extendedTouchPoint.y *= tFightMoves[m_curFightMove].extendReachMultiplier; } pedFound = -1; extendedTouchPoint += GetPosition(); } } if (pedFound == -1) { CVector nearPedCentre = nearPed->GetBoundCentre(); if (sq(maxDistanceToBeat) > (nearPedCentre - extendedTouchPoint).MagnitudeSqr()) { for (int j = 0; j < hisCol->numSpheres; j++) { attackDistance = hisCol->spheres[j].center; attackDistance -= extendedTouchPoint; CColSphere* hisPieces = hisCol->spheres; float maxDistanceToBeat2 = hisPieces[j].radius + tFightMoves[m_curFightMove].strikeRadius; // We can beat him too if (sq(maxDistanceToBeat2) > attackDistance.MagnitudeSqr()) { FightHitPed(nearPed, extendedTouchPoint, attackDistance, hisPieces[j].piece); break; } } } } } } if (m_fightState == FIGHTSTATE_NO_MOVE) m_fightState = FIGHTSTATE_1; m_vecHitLastPos = touchedNodePos; return false; } void CPed::FightHitPed(CPed *victim, CVector &touchPoint, CVector &dir, int16 piece) { if (victim->IsPlayer() && victim->m_nPedState == PED_GETUP) return; CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); bool fightingWithWeapon = false; int damageMult = tFightMoves[m_curFightMove].damage * ((CGeneral::GetRandomNumber() & 1) + 2) + 1; if (weaponInfo->IsFlagSet(WEAPONFLAG_FIGHTMODE)) { fightingWithWeapon = true; if (m_curFightMove >= FIGHTMOVE_MELEE1) { damageMult = weaponInfo->m_nDamage; if (m_curFightMove == FIGHTMOVE_MELEE3 && GetWeapon()->m_eWeaponType != WEAPONTYPE_SCREWDRIVER) damageMult *= 5; } } if (IsPlayer()) { if (((CPlayerPed*)this)->m_bAdrenalineActive) damageMult = 20; } else if (!fightingWithWeapon) { damageMult *= m_pedStats->m_attackStrength; } float oldVictimHealth = victim->m_fHealth; CVector bloodPos = 0.5f * dir + touchPoint; CVector2D diff(GetPosition() - victim->GetPosition()); int direction = victim->GetLocalDirection(diff); bool brassKnucklePunch = false; if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) { if (m_curFightMove == FIGHTMOVE_PUNCHHOOK || m_curFightMove == FIGHTMOVE_PUNCHJAB || m_curFightMove == FIGHTMOVE_BACKLEFT || m_curFightMove == FIGHTMOVE_STDPUNCH || m_curFightMove == FIGHTMOVE_PUNCH) { brassKnucklePunch = true; damageMult *= 1.5f; } } victim->ReactToAttack(this); // Mostly unused. if > 5, ANIM_HIT_BIGSTEP will be run, that's it. int unk2; if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE && !victim->IsPlayer() && !fightingWithWeapon) unk2 = 101; else unk2 = damageMult; victim->StartFightDefend(direction, tFightMoves[m_curFightMove].hitLevel, unk2); PlayHitSound(victim); m_fightState = FIGHTSTATE_JUST_ATTACKED; RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId)->speed = 0.6f; if (!victim->DyingOrDead()) { if(fightingWithWeapon) victim->InflictDamage(this, GetWeapon()->m_eWeaponType, damageMult, (ePedPieceTypes)piece, direction); else victim->InflictDamage(this, WEAPONTYPE_UNARMED, damageMult * 3.0f, (ePedPieceTypes)piece, direction); } if (CGame::nastyGame && weaponInfo->m_AnimToPlay == ASSOCGRP_KNIFE && m_curFightMove >= FIGHTMOVE_MELEE1 && victim->GetIsOnScreen()) { static float particleRightLen = 0.05f; static float particleUpLen = 0.05f; // Just for particles. We will restore it below. dir /= (20.0f * dir.Magnitude()); if (m_curFightMove == FIGHTMOVE_MELEE1) { float rightMult = -particleRightLen; dir += particleUpLen * GetUp() + rightMult * GetRight(); } else if (m_curFightMove == FIGHTMOVE_MELEE2) { float upMult = 2.0f * particleUpLen; dir += particleRightLen * GetRight() + upMult * GetUp(); } CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); if (IsPlayer()) { CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); } if (!(CGeneral::GetRandomNumber() & 3)) { CParticle::AddParticle(PARTICLE_TEST, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); } } else if (CGame::nastyGame && (tFightMoves[m_curFightMove].hitLevel > HITLEVEL_MEDIUM || fightingWithWeapon) && victim->GetIsOnScreen()) { // Just for particles. We will restore it below. dir /= (10.0f * dir.Magnitude()); for (int i = 0; i < 4; i++) { CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0); } } eWeaponType weaponType = GetWeapon()->m_eWeaponType; if (!fightingWithWeapon) { if (!victim->OnGround()) { float curVictimHealth = victim->m_fHealth; if (curVictimHealth > 0.0f && (curVictimHealth < 30.0f && oldVictimHealth > 30.0f || weaponType != WEAPONTYPE_UNARMED && weaponType != WEAPONTYPE_BRASSKNUCKLE && IsPlayer() || victim->m_pedStats->m_flags & STAT_ONE_HIT_KNOCKDOWN || brassKnucklePunch)) { victim->SetFall(0, (AnimationId)(direction + ANIM_STD_HIGHIMPACT_FRONT), 0); if (victim->m_nPedState == PED_FALL) victim->bIsStanding = false; } } } if (victim->m_nPedState == PED_DIE || !victim->bIsStanding) { dir = victim->GetPosition() - GetPosition(); dir.Normalise(); dir.z = 1.0f; victim->bIsStanding = false; float moveMult; if (fightingWithWeapon) { moveMult = Min(damageMult * 0.02f, 1.0f); } else if (m_curFightMove == FIGHTMOVE_GROUNDKICK) { moveMult = Min(damageMult * 0.6f, 4.0f); } else { if (victim->m_nPedState != PED_DIE || damageMult >= 20) { moveMult = damageMult; } else { moveMult = Min(damageMult * 2.0f, 14.0f); } } victim->ApplyMoveForce(moveMult * 0.6 * dir); } if (weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_MACHETE && weaponType != WEAPONTYPE_KATANA && weaponType != WEAPONTYPE_CHAINSAW) { if (victim->m_nPedType == PEDTYPE_COP) CEventList::RegisterEvent(EVENT_ASSAULT_POLICE, EVENT_ENTITY_PED, victim, this, 2000); else CEventList::RegisterEvent(EVENT_ASSAULT, EVENT_ENTITY_PED, victim, this, 2000); } else { if (victim->m_nPedType == PEDTYPE_COP) CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON_POLICE, EVENT_ENTITY_PED, victim, this, 2000); else CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON, EVENT_ENTITY_PED, victim, this, 2000); } } void CPed::FinishFightMoveCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; if (tFightMoves[ped->m_curFightMove].animId == animAssoc->animId) { ped->m_fightState = FIGHTSTATE_MOVE_FINISHED; animAssoc->blendDelta = -1000.0f; } } void CPed::LoadFightData(void) { float startFireTime, endFireTime, comboFollowOnTime, strikeRadius, extendReachMultiplier; int damage, flags; char line[256], moveName[32], animName[32], hitLevel; int moveId = 0; CAnimBlendAssociation *animAssoc; size_t bp, buflen; int lp, linelen; buflen = CFileMgr::LoadFile("DATA\\fistfite.dat", work_buff, sizeof(work_buff), "r"); for (bp = 0; bp < buflen; ) { // read file line by line for (linelen = 0; work_buff[bp] != '\n' && bp < buflen; bp++) { line[linelen++] = work_buff[bp]; } bp++; line[linelen] = '\0'; // skip white space for (lp = 0; line[lp] <= ' ' && line[lp] != '\0'; lp++); if (line[lp] == '\0' || line[lp] == '#') continue; sscanf( &line[lp], "%s %f %f %f %f %f %c %s %d %d", moveName, &startFireTime, &endFireTime, &comboFollowOnTime, &strikeRadius, &extendReachMultiplier, &hitLevel, animName, &damage, &flags); if (strncmp(moveName, "ENDWEAPONDATA", 13) == 0) return; tFightMoves[moveId].startFireTime = startFireTime / 30.0f; tFightMoves[moveId].endFireTime = endFireTime / 30.0f; tFightMoves[moveId].comboFollowOnTime = comboFollowOnTime / 30.0f; tFightMoves[moveId].strikeRadius = strikeRadius; tFightMoves[moveId].extendReachMultiplier = extendReachMultiplier; tFightMoves[moveId].damage = damage; tFightMoves[moveId].flags = flags; switch (hitLevel) { case 'G': tFightMoves[moveId].hitLevel = HITLEVEL_GROUND; break; case 'H': tFightMoves[moveId].hitLevel = HITLEVEL_HIGH; break; case 'L': tFightMoves[moveId].hitLevel = HITLEVEL_LOW; break; case 'M': tFightMoves[moveId].hitLevel = HITLEVEL_MEDIUM; break; case 'N': tFightMoves[moveId].hitLevel = HITLEVEL_NULL; break; default: break; } if (strcmp(animName, "default") != 0) { if (strcmp(animName, "null") != 0) { animAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, animName); tFightMoves[moveId].animId = (AnimationId)animAssoc->animId; } else { tFightMoves[moveId].animId = ANIM_STD_WALK; } } moveId++; } } void CPed::SetInvestigateEvent(eEventType event, CVector2D pos, float distanceToCountDone, uint16 time, float angle) { if (!IsPedInControl() || CharCreatedBy == MISSION_CHAR) return; SetStoredState(); bFindNewNodeAfterStateRestore = false; SetPedState(PED_INVESTIGATE); m_chatTimer = CTimer::GetTimeInMilliseconds() + time; m_eventType = event; m_eventOrThreat = pos; m_distanceToCountSeekDone = distanceToCountDone; m_fAngleToEvent = angle; if (m_eventType >= EVENT_ICECREAM) m_lookTimer = 0; else CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSCOWER, 4.0f); } void CPed::InvestigateEvent(void) { CAnimBlendAssociation *animAssoc; AnimationId animToPlay; AssocGroupId animGroup; if (m_nWaitState == WAITSTATE_TURN180) return; if (CTimer::GetTimeInMilliseconds() > m_chatTimer) { if (m_chatTimer) { if (m_eventType < EVENT_UNK) SetWaitState(WAITSTATE_TURN180, nil); m_chatTimer = 0; } else { ClearInvestigateEvent(); } return; } CVector2D vecDist = m_eventOrThreat - GetPosition(); float distSqr = vecDist.MagnitudeSqr(); if (sq(m_distanceToCountSeekDone) >= distSqr) { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(vecDist.x, vecDist.y, 0.0f, 0.0f); SetMoveState(PEDMOVE_STILL); switch (m_eventType) { case EVENT_DEAD_PED: case EVENT_HIT_AND_RUN: case EVENT_HIT_AND_RUN_COP: if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; if (m_pEventEntity) SetLookFlag(m_pEventEntity, true); SetLookTimer(CGeneral::GetRandomNumberInRange(1500, 4000)); } else if (CGeneral::GetRandomNumber() & 3) { ClearLookFlag(); CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROADCROSS, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); Say(SOUND_PED_CHAT_EVENT); } else { ClearInvestigateEvent(); } } break; case EVENT_FIRE: case EVENT_EXPLOSION: if (bHasACamera && CTimer::GetTimeInMilliseconds() > m_lookTimer) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_CAM); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); if (animAssoc && animAssoc->animId == ANIM_STD_IDLE_CAM) { CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); } else if (CGeneral::GetRandomNumber() & 3) { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_CAM, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(2500, 5000)); if (!CGame::germanGame) Say(SOUND_PED_CHAT_EVENT); } else { m_chatTimer = 0; } } else if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); if (animAssoc && animAssoc->animId == ANIM_STD_IDLE) { if (CGeneral::GetRandomNumber() & 1) animToPlay = ANIM_STD_IDLE_HBHB; else animToPlay = ANIM_STD_XPRESS_SCRATCH; CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1500, 4000)); } else if (animAssoc && animAssoc->animId == ANIM_STD_IDLE_HBHB) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; if (CGeneral::GetRandomNumber() & 1) { animToPlay = ANIM_STD_IDLE; animGroup = m_animGroup; } else { animToPlay = ANIM_STD_XPRESS_SCRATCH; animGroup = ASSOCGRP_STD; } CAnimManager::BlendAnimation(GetClump(), animGroup, animToPlay, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); } else { if (CGeneral::GetRandomNumber() & 1) { animToPlay = ANIM_STD_IDLE; animGroup = m_animGroup; } else { animToPlay = ANIM_STD_IDLE_HBHB; animGroup = ASSOCGRP_STD; } CAnimManager::BlendAnimation(GetClump(), animGroup, animToPlay, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); } if (!CGame::germanGame) Say(SOUND_PED_CHAT_EVENT); } break; case EVENT_ICECREAM: case EVENT_SHOPSTALL: m_fRotationDest = m_fAngleToEvent; if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { if (m_lookTimer) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; if (m_eventType == EVENT_ICECREAM) animToPlay = ANIM_STD_CHAT; else animToPlay = ANIM_STD_XPRESS_SCRATCH; CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay,4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(2000, 5000)); } else { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; ClearInvestigateEvent(); } else { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } ClearInvestigateEvent(); } } } else { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROADCROSS, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); } } break; default: return; } } else { m_vecSeekPos.x = m_eventOrThreat.x; m_vecSeekPos.y = m_eventOrThreat.y; m_vecSeekPos.z = GetPosition().z; Seek(); if (m_eventType < EVENT_ICECREAM) { if (sq(5.0f + m_distanceToCountSeekDone) < distSqr) { SetMoveState(PEDMOVE_RUN); return; } } if (m_eventType <= EVENT_EXPLOSION || m_eventType >= EVENT_SHOPSTALL) { SetMoveState(PEDMOVE_WALK); return; } if (distSqr > sq(1.2f)) { SetMoveState(PEDMOVE_WALK); return; } bool willStandStill = false; for (int i = 0; i < m_numNearPeds; i++) { if ((m_eventOrThreat - m_nearPeds[i]->GetPosition()).MagnitudeSqr() < sq(0.4f)) { SetMoveState(PEDMOVE_STILL); willStandStill = true; break; } } if (!willStandStill) SetMoveState(PEDMOVE_WALK); } } void CPed::ClearInvestigateEvent(void) { CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } if (m_eventType > EVENT_EXPLOSION) m_chatTimer = CTimer::GetTimeInMilliseconds() + 15000; bGonnaInvestigateEvent = false; m_pEventEntity = nil; ClearLookFlag(); RestorePreviousState(); if(m_nMoveState == PEDMOVE_NONE || m_nMoveState == PEDMOVE_STILL) SetMoveState(PEDMOVE_WALK); } bool CPed::InflictDamage(CEntity *damagedBy, eWeaponType method, float damage, ePedPieceTypes pedPiece, uint8 direction) { CPlayerPed *player = FindPlayerPed(); float dieDelta = 4.0f; float dieSpeed = 0.0f; AnimationId dieAnim = ANIM_STD_KO_FRONT; bool headShot = false; bool willLinger = false; int random; if (damagedBy == FindPlayerPed() && damagedBy != this && damage > 3.0f) ++CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel; if (player == this) { if (!player->m_bCanBeDamaged) return false; if (damagedBy && damagedBy->IsPed() && ((CPed*)damagedBy)->m_nPedType == PEDTYPE_GANG7) return false; if ((method == WEAPONTYPE_FLAMETHROWER || method == WEAPONTYPE_MOLOTOV) && CWorld::Players[CWorld::PlayerInFocus].m_bFireproof) return false; player->AnnoyPlayerPed(false); } if (DyingOrDead()) return false; if (method == WEAPONTYPE_DROWNING && !bDrownsInWater) return false; if (!bUsesCollision && (!bInVehicle || m_nPedState != PED_DRIVING) && method != WEAPONTYPE_DROWNING) return false; if (bOnlyDamagedByPlayer && damagedBy != player && damagedBy != FindPlayerVehicle() && method != WEAPONTYPE_DROWNING && method != WEAPONTYPE_EXPLOSION) return false; float healthImpact; if (IsPlayer()) healthImpact = damage * 0.33f; else healthImpact = damage * m_pedStats->m_defendWeakness; if (!IsPlayer() && (method == WEAPONTYPE_SCREWDRIVER || method == WEAPONTYPE_KNIFE || (method >= WEAPONTYPE_CLEAVER && method <= WEAPONTYPE_CHAINSAW))) m_bleedCounter = 200; bool detectDieAnim = true; if (m_nPedState == PED_GETUP) { if (!IsPedHeadAbovePos(-0.3f)) { if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) dieAnim = ANIM_STD_HIT_FLOOR_FRONT; else dieAnim = ANIM_STD_HIT_FLOOR; dieDelta *= 2.0f; dieSpeed = 0.5f; detectDieAnim = false; } } else if (m_nPedState == PED_FALL) { CAnimBlendAssociation *fallAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_PARTIAL); if (!fallAssoc || fallAssoc->IsRunning()) { if (fallAssoc && fallAssoc->blendDelta >= 0.0f) dieAnim = ANIM_STD_NUM; else dieAnim = ANIM_STD_KO_FRONT; } else { if (fallAssoc->flags & ASSOC_FRONTAL) dieAnim = ANIM_STD_HIT_FLOOR_FRONT; else dieAnim = ANIM_STD_HIT_FLOOR; dieDelta *= 2.0f; dieSpeed = 0.5f; } detectDieAnim = false; } if (detectDieAnim) { switch (method) { case WEAPONTYPE_UNARMED: case WEAPONTYPE_BRASSKNUCKLE: if (bMeleeProof) return false; if (m_nPedState == PED_FALL) { if (IsPedHeadAbovePos(-0.3f)) { dieAnim = ANIM_STD_NUM; } else { if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) dieAnim = ANIM_STD_HIT_FLOOR_FRONT; else dieAnim = ANIM_STD_HIT_FLOOR; dieDelta = dieDelta * 2.0f; dieSpeed = 0.5f; } } else { switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } } break; case WEAPONTYPE_SCREWDRIVER: case WEAPONTYPE_GOLFCLUB: case WEAPONTYPE_NIGHTSTICK: case WEAPONTYPE_KNIFE: case WEAPONTYPE_BASEBALLBAT: case WEAPONTYPE_HAMMER: case WEAPONTYPE_CLEAVER: case WEAPONTYPE_MACHETE: case WEAPONTYPE_KATANA: case WEAPONTYPE_CHAINSAW: if (bMeleeProof) return false; if (method != WEAPONTYPE_KATANA || damagedBy != FindPlayerPed() || FindPlayerPed()->m_nPedState != PED_FIGHT || FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE1 && FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE2 || CGeneral::GetRandomNumber() & 3) { if (m_nPedState == PED_FALL) { if (IsPedHeadAbovePos(-0.3f)) { dieAnim = ANIM_STD_NUM; } else { if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) dieAnim = ANIM_STD_HIT_FLOOR_FRONT; else dieAnim = ANIM_STD_HIT_FLOOR; dieDelta = dieDelta * 2.0f; dieSpeed = 0.5f; } } else if (damagedBy != FindPlayerPed() || FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE2) { if (damagedBy != FindPlayerPed() || FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE3) { switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } } else { dieAnim = ANIM_STD_KO_SHOT_STOMACH; } } else { dieAnim = ANIM_STD_KO_SHOT_FACE; } } else { dieAnim = ANIM_STD_KO_SHOT_FACE; RemoveBodyPart(PED_HEAD, direction); headShot = true; willLinger = true; } break; case WEAPONTYPE_COLT45: case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: case WEAPONTYPE_SPAS12_SHOTGUN: case WEAPONTYPE_TEC9: case WEAPONTYPE_UZI: case WEAPONTYPE_SILENCED_INGRAM: case WEAPONTYPE_MP5: case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: case WEAPONTYPE_M60: case WEAPONTYPE_MINIGUN: case WEAPONTYPE_UZI_DRIVEBY: if (bBulletProof) return false; bool dontRemoveLimb; if (IsPlayer() || bNoCriticalHits) dontRemoveLimb = true; else if (method != WEAPONTYPE_M4 && method != WEAPONTYPE_RUGER && method != WEAPONTYPE_SNIPERRIFLE && method != WEAPONTYPE_LASERSCOPE) { if (method == WEAPONTYPE_SHOTGUN) dontRemoveLimb = CGeneral::GetRandomNumber() & 7; else dontRemoveLimb = CGeneral::GetRandomNumber() & 15; } else dontRemoveLimb = false; if (dontRemoveLimb) { if (method == WEAPONTYPE_SHOTGUN) { switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } } else dieAnim = ANIM_STD_KO_FRONT; willLinger = false; } else { switch (pedPiece) { case PEDPIECE_TORSO: willLinger = false; dieAnim = ANIM_STD_KO_FRONT; break; case PEDPIECE_MID: willLinger = false; dieAnim = ANIM_STD_KO_SHOT_STOMACH; break; case PEDPIECE_LEFTARM: dieAnim = ANIM_STD_KO_SHOT_ARM_L; RemoveBodyPart(PED_UPPERARML, direction); willLinger = true; break; case PEDPIECE_RIGHTARM: dieAnim = ANIM_STD_KO_SHOT_ARM_R; RemoveBodyPart(PED_UPPERARMR, direction); willLinger = true; break; case PEDPIECE_LEFTLEG: dieAnim = ANIM_STD_KO_SHOT_LEG_L; RemoveBodyPart(PED_UPPERLEGL, direction); willLinger = true; break; case PEDPIECE_RIGHTLEG: dieAnim = ANIM_STD_KO_SHOT_LEG_R; RemoveBodyPart(PED_UPPERLEGR, direction); willLinger = true; break; case PEDPIECE_HEAD: dieAnim = ANIM_STD_KO_SHOT_FACE; RemoveBodyPart(PED_HEAD, direction); headShot = true; willLinger = true; break; default: break; } } break; case WEAPONTYPE_GRENADE: case WEAPONTYPE_ROCKETLAUNCHER: case WEAPONTYPE_EXPLOSION: if (bExplosionProof) return false; if (CGame::nastyGame && !IsPlayer() && !bInVehicle && 1.0f + healthImpact > m_fArmour + m_fHealth) { random = CGeneral::GetRandomNumber(); if (random & 1) RemoveBodyPart(PED_UPPERARML, direction); if (random & 2) RemoveBodyPart(PED_UPPERLEGR, direction); if (random & 4) RemoveBodyPart(PED_HEAD, direction); if (random & 8) RemoveBodyPart(PED_UPPERARMR, direction); if (random & 0x10) RemoveBodyPart(PED_UPPERLEGL, direction); if (bBodyPartJustCameOff) willLinger = true; } // fall through case WEAPONTYPE_MOLOTOV: if (bExplosionProof) return false; switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } break; case WEAPONTYPE_FLAMETHROWER: if (bFireProof) return false; dieAnim = ANIM_STD_KO_FRONT; break; case WEAPONTYPE_RAMMEDBYCAR: case WEAPONTYPE_RUNOVERBYCAR: if (bCollisionProof) return false; random = CGeneral::GetRandomNumber() & 3; switch (random) { case 0: if ((pedPiece != PEDPIECE_LEFTARM || random <= 1) && (pedPiece != PEDPIECE_MID || random != 1)) { if (pedPiece == PEDPIECE_RIGHTARM && random > 1 || pedPiece == PEDPIECE_MID && random == 2) dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; else dieAnim = ANIM_STD_HIGHIMPACT_FRONT; } else dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 1: if (m_nPedState == PED_DIVE_AWAY) dieAnim = ANIM_STD_SPINFORWARD_LEFT; else dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: if ((pedPiece != PEDPIECE_LEFTARM || random <= 1) && (pedPiece != PEDPIECE_MID || random != 1)) { if ((pedPiece != PEDPIECE_RIGHTARM || random <= 1) && (pedPiece != PEDPIECE_MID || random != 2)) { dieAnim = ANIM_STD_HIGHIMPACT_BACK; } else { dieAnim = ANIM_STD_SPINFORWARD_RIGHT; } } else dieAnim = ANIM_STD_SPINFORWARD_LEFT; break; case 3: if (m_nPedState == PED_DIVE_AWAY) dieAnim = ANIM_STD_SPINFORWARD_RIGHT; else dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } if (damagedBy && pedPiece != PEDPIECE_TORSO) { CVehicle *vehicle = (CVehicle*)damagedBy; if (method == WEAPONTYPE_RAMMEDBYCAR) { float vehSpeed = vehicle->m_vecMoveSpeed.Magnitude(); dieDelta = 8.0f * vehSpeed + 4.0f; } else { float vehSpeed = vehicle->m_vecMoveSpeed.Magnitude(); dieDelta = 12.0f * vehSpeed + 4.0f; dieSpeed = 16.0f * vehSpeed + 1.0f; } } break; case WEAPONTYPE_DROWNING: dieAnim = ANIM_STD_DROWN; break; case WEAPONTYPE_FALL: if (bCollisionProof) return false; switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } break; default: break; } } if (m_fArmour != 0.0f && method != WEAPONTYPE_DROWNING) { if (player == this) CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss = CTimer::GetTimeInMilliseconds(); if (healthImpact < m_fArmour) { m_fArmour = m_fArmour - healthImpact; healthImpact = 0.0f; } else { healthImpact = healthImpact - m_fArmour; m_fArmour = 0.0f; } } if (healthImpact != 0.0f) { if (player == this) CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss = CTimer::GetTimeInMilliseconds(); m_lastWepDam = method; m_lastDamEntity = damagedBy; } if (method == WEAPONTYPE_FALL) { if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROLLOUT_LHS)) { if (m_fHealth >= 1.0 && m_fHealth - healthImpact < 5.0f) { m_fHealth = Min(m_fHealth, 5.0f); return false; } } } if (m_fHealth - healthImpact >= 1.0f && !willLinger) { m_fHealth -= healthImpact; return false; } if (bInVehicle) { if (method != WEAPONTYPE_DROWNING) { if (m_pMyVehicle) { CVehicle* pVehicle = m_pMyVehicle; bool bDone = false; if (m_pMyVehicle->IsBike()) { m_fHealth = 0.0f; ((CBike*)m_pMyVehicle)->KnockOffRider(method, direction, this, false); bDone = true; } else { if (m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) { if (m_pMyVehicle->pDriver == this) { if (m_pMyVehicle->GetStatus() == STATUS_SIMPLE) { m_pMyVehicle->SetStatus(STATUS_PHYSICS); CCarCtrl::SwitchVehicleToRealPhysics(m_pMyVehicle); } m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE; m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; m_pMyVehicle->AutoPilot.m_nTempAction = TEMPACT_HANDBRAKESTRAIGHT; m_pMyVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; } } if (m_pMyVehicle->CanPedExitCar(true)) { SetObjective(OBJECTIVE_LEAVE_CAR_AND_DIE, m_pMyVehicle); } else { m_fHealth = 0.0f; if (m_pMyVehicle && m_pMyVehicle->pDriver == this) { SetRadioStation(); m_pMyVehicle->SetStatus(STATUS_ABANDONED); } SetDie(dieAnim, dieDelta, dieSpeed); if (damagedBy == FindPlayerPed() && damagedBy != this) { CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 10; CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 5.f; } } } for (int i = 0; i < ARRAY_SIZE(pVehicle->pPassengers); i++) { CPed* passenger = pVehicle->pPassengers[i]; if (passenger && passenger != this && damagedBy) passenger->ReactToAttack(damagedBy); } CPed *driverOfVeh = pVehicle->pDriver; if (driverOfVeh && driverOfVeh != this && damagedBy) driverOfVeh->ReactToAttack(damagedBy); if (damagedBy == FindPlayerPed() || damagedBy && damagedBy == FindPlayerVehicle()) { CDarkel::RegisterKillByPlayer(this, method, headShot); m_threatEntity = FindPlayerPed(); } else { CDarkel::RegisterKillNotByPlayer(this, method); } if (bDone) return true; } m_fHealth = 1.0f; return false; } m_fHealth = 0.0f; if (player == this) m_pMyVehicle->SetStatus(STATUS_PLAYER_DISABLED); SetDie(ANIM_STD_NUM, 4.0f, 0.0f); return true; } else { m_fHealth = 0.0f; SetDie(dieAnim, dieDelta, dieSpeed); if (damagedBy == player || damagedBy && damagedBy == FindPlayerVehicle()) { CDarkel::RegisterKillByPlayer(this, method, headShot); CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 10; CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 5.f; m_threatEntity = player; } else { CDarkel::RegisterKillNotByPlayer(this, method); } if (method == WEAPONTYPE_DROWNING) { bIsInTheAir = false; if (FindPlayerPed() == this) CStats::TimesDrowned++; } return true; } } static RwObject* SetPedAtomicVisibilityCB(RwObject* object, void* data) { if (data == nil) RpAtomicSetFlags((RpAtomic*)object, 0); return object; } static RwFrame* RecurseFrameChildrenVisibilityCB(RwFrame* frame, void* data) { RwFrameForAllObjects(frame, SetPedAtomicVisibilityCB, data); RwFrameForAllChildren(frame, RecurseFrameChildrenVisibilityCB, nil); return frame; } static RwObject* CloneAtomicToFrameCB(RwObject *frame, void *data) { RpAtomic *newAtomic = RpAtomicClone((RpAtomic*)frame); RpAtomicSetFrame(newAtomic, (RwFrame*)data); RpClumpAddAtomic(flyingClumpTemp, newAtomic); CVisibilityPlugins::SetAtomicRenderCallback(newAtomic, nil); return frame; } static RwFrame* RecurseFrameChildrenToCloneCB(RwFrame *frame, void *data) { RwFrame *newFrame = RwFrameCreate(); RwFrameAddChild((RwFrame*)data, newFrame); RwFrameTransform(newFrame, RwFrameGetMatrix(frame), rwCOMBINEREPLACE); RwFrameForAllObjects(frame, CloneAtomicToFrameCB, newFrame); RwFrameForAllChildren(frame, RecurseFrameChildrenToCloneCB, newFrame); return newFrame; } void CPed::RemoveBodyPart(PedNode nodeId, int8 direction) { RwFrame *frame; CVector pos; frame = m_pFrames[nodeId]->frame; if (frame) { if (CGame::nastyGame) { if (CEntity::GetIsOnScreen()) { m_pedIK.GetComponentPosition(pos, nodeId); CParticle::AddParticle(PARTICLE_TEST, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.1f, 0, 0, 0, 0); for (int i = 0; i < 16; i++) { CParticle::AddParticle(PARTICLE_BLOOD_SMALL, pos, CVector(0.0f, 0.0f, 0.03f), nil, 0.0f, 0, 0, 0, 0); } } bBodyPartJustCameOff = true; m_bodyPartBleeding = nodeId; } } else { printf("Trying to remove ped component"); } } CObject* CPed::SpawnFlyingComponent(int pedNode, int8 direction) { // VC doesn't have detachable limbs :shrug: return nil; } // III leftover and unused void CPed::ApplyHeadShot(eWeaponType weaponType, CVector pos, bool evenOnPlayer) { CVector pos2 = CVector( pos.x, pos.y, pos.z + 0.1f ); if (!IsPlayer() || evenOnPlayer) { ++CStats::HeadsPopped; // BUG: This condition will always return true. Even fixing it won't work, because these states are unused. // if (m_nPedState != PED_PASSENGER || m_nPedState != PED_TAXI_PASSENGER) { SetDie(); // } bBodyPartJustCameOff = true; m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 150; RemoveBodyPart(PED_HEAD, 0); CParticle::AddParticle(PARTICLE_TEST, pos2, CVector(0.0f, 0.0f, 0.0f), nil, 0.2f, 0, 0, 0, 0); if (CEntity::GetIsOnScreen()) { for(int i=0; i < 32; i++) { CParticle::AddParticle(PARTICLE_BLOOD_SMALL, pos2, CVector(0.0f, 0.0f, 0.03f), nil, 0.0f, 0, 0, 0, 0); } for (int i = 0; i < 16; i++) { CParticle::AddParticle(PARTICLE_DEBRIS2, pos2, CVector(0.0f, 0.0f, 0.01f), nil, 0.0f, 0, 0, 0, 0); } } } } bool CPed::IsPedHeadAbovePos(float zOffset) { return zOffset + GetPosition().z < GetNodePosition(PED_HEAD).z; } bool CPed::PlacePedOnDryLand(void) { float waterLevel = 0.0f; CEntity *foundEnt = nil; CColPoint foundCol; float foundColZ; CWaterLevel::GetWaterLevelNoWaves(GetPosition().x, GetPosition().y, GetPosition().z, &waterLevel); CVector potentialGround = GetPosition(); potentialGround.z = waterLevel; if (!CWorld::TestSphereAgainstWorld(potentialGround, 5.0f, nil, true, false, false, false, false, false)) return false; CVector potentialGroundDist = gaTempSphereColPoints[0].point - GetPosition(); potentialGroundDist.z = 0.0f; potentialGroundDist.Normalise(); CVector posToCheck = 0.5f * potentialGroundDist + gaTempSphereColPoints[0].point; posToCheck.z = 3.0f + waterLevel; if (CWorld::ProcessVerticalLine(posToCheck, waterLevel - 1.0f, foundCol, foundEnt, true, true, false, true, false, false, nil)) { foundColZ = foundCol.point.z; if (foundColZ >= waterLevel) { posToCheck.z = 0.8f + foundColZ; SetPosition(posToCheck); bIsStanding = true; bWasStanding = true; return true; } } posToCheck = 5.0f * potentialGroundDist + GetPosition(); posToCheck.z = 3.0f + waterLevel; if (!CWorld::ProcessVerticalLine(posToCheck, waterLevel - 1.0f, foundCol, foundEnt, true, true, false, true, false, false, nil)) return false; foundColZ = foundCol.point.z; if (foundColZ < waterLevel) return false; posToCheck.z = 0.8f + foundColZ; SetPosition(posToCheck); bIsStanding = true; bWasStanding = true; return true; } void CPed::CollideWithPed(CPed *collideWith) { CAnimBlendAssociation *animAssoc; AnimationId animToPlay; bool weAreMissionChar = CharCreatedBy == MISSION_CHAR; bool heIsMissionChar = collideWith->CharCreatedBy == MISSION_CHAR; CVector posDiff = collideWith->GetPosition() - GetPosition(); int waitTime = 0; if (weAreMissionChar || !collideWith->IsPlayer() || collideWith->m_nPedState != PED_MAKE_CALL) { if (m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) { SetGetUp(); return; } if (collideWith->m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) { collideWith->SetGetUp(); return; } bool weDontLookToHim = DotProduct(posDiff, GetForward()) > 0.0f; bool heLooksToUs = DotProduct(posDiff, collideWith->GetForward()) < 0.0f; if (m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL) { if ((!IsPlayer() || ((CPlayerPed*)this)->m_fMoveSpeed <= 1.8f) && (IsPlayer() || heIsMissionChar && weAreMissionChar || m_nMoveState != PEDMOVE_RUN && m_nMoveState != PEDMOVE_SPRINT || m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && m_pedInObjective == collideWith || collideWith->m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && collideWith->m_pedInObjective == this )) { if (m_objective != OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && m_objective != OBJECTIVE_GOTO_CHAR_ON_FOOT) { if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { if (heIsMissionChar || !weAreMissionChar && collideWith->m_nMoveState != PEDMOVE_STILL) { if (weAreMissionChar && (m_nPedState == PED_SEEK_POS || m_nPedState == PED_SEEK_ENTITY)) { if (collideWith->m_nMoveState != PEDMOVE_STILL && (!collideWith->IsPlayer() || collideWith->IsPlayer() && CPad::GetPad(0)->ArePlayerControlsDisabled())) { float seekPosDist = (GetPosition() - m_vecSeekPos).MagnitudeSqr2D(); float heAndSeekPosDist = (collideWith->GetPosition() - m_vecSeekPos).MagnitudeSqr2D(); if (seekPosDist <= heAndSeekPosDist) { waitTime = 1000; collideWith->SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &waitTime); collideWith->m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + waitTime; } else { waitTime = 500; SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &waitTime); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + waitTime; } } else if (collideWith->m_nMoveState == PEDMOVE_STILL) { SetDirectionToWalkAroundObject(collideWith); } } else { if (FindPlayerPed() != m_pedInObjective || m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT || collideWith == m_pedInObjective) { if (weAreMissionChar || m_pedStats->m_fear <= 100 - collideWith->m_pedStats->m_temper || (collideWith->IsPlayer() || collideWith->m_nMoveState == PEDMOVE_NONE || collideWith->m_nMoveState == PEDMOVE_STILL) && (!collideWith->IsPlayer() || ((CPlayerPed*)collideWith)->m_fMoveSpeed <= 1.0f)) { SetDirectionToWalkAroundObject(collideWith); if (!weAreMissionChar) Say(SOUND_PED_CHAT); } else { SetEvasiveStep(collideWith, 2); } } else if (collideWith->m_nMoveState != PEDMOVE_STILL && GetWeapon()->IsTypeMelee() && collideWith->m_pedInObjective == m_pedInObjective) { int colliderIndexAtPlayersKillList = -1; int ourIndexAtPlayersKillList = -1; for (int i = 0; i < ARRAY_SIZE(((CPlayerPed*)m_pedInObjective)->m_pMeleeList); i++) { CPed *pedInKillList = ((CPlayerPed*)m_pedInObjective)->m_pMeleeList[i]; if (pedInKillList == this) { ourIndexAtPlayersKillList = i; } else if (pedInKillList == collideWith) { colliderIndexAtPlayersKillList = i; } } bool weAreCloserToTargetThenCollider = false; if ((GetPosition() - m_vecSeekPos).MagnitudeSqr2D() < (collideWith->GetPosition() - m_vecSeekPos).MagnitudeSqr2D()) weAreCloserToTargetThenCollider = true; if (ourIndexAtPlayersKillList > 0 && !weAreCloserToTargetThenCollider) { if (colliderIndexAtPlayersKillList > 0) { int time = 300; SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &time); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + time; } else if (collideWith->m_pedInObjective == FindPlayerPed()) { ((CPlayerPed*)m_pedInObjective)->RemovePedFromMeleeList(this); int time = 500; SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &time); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + time; } } else if (!weAreCloserToTargetThenCollider) { int time = 300; SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &time); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + time; } } else { SetDirectionToWalkAroundObject(collideWith); } } } else { if (m_pedStats->m_temper <= m_pedStats->m_fear || GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED || weAreMissionChar || collideWith->m_nPedType == PEDTYPE_CIVFEMALE || collideWith->m_nPedType == m_nPedType || collideWith->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { SetDirectionToWalkAroundObject(collideWith); Say(SOUND_PED_CHAT); } else { TurnBody(); SetAttack(collideWith); m_fRotationCur = 0.3f + m_fRotationCur; m_fRotationDest = m_fRotationCur; } m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(250, 450); } } } else { if (m_pedInObjective && (collideWith == m_pedInObjective || collideWith->m_pedInObjective == m_pedInObjective) && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { if (heLooksToUs) { SetEvasiveStep(collideWith, 1); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; } } else if (weDontLookToHim && IsPedInControl()) { if (m_pedStats != collideWith->m_pedStats) { if (collideWith->m_pedStats->m_fear <= 100 - m_pedStats->m_temper || collideWith->IsPlayer() || CTimer::GetTimeInMilliseconds() < m_nPedStateTimer) { if (collideWith->IsPlayer()) { // He's on our right side if (DotProduct(posDiff,GetRight()) <= 0.0f) m_fRotationCur -= m_headingRate; else m_fRotationCur += m_headingRate; } else { // He's on our right side if (DotProduct(posDiff, collideWith->GetRight()) <= 0.0f) collideWith->m_fRotationCur -= collideWith->m_headingRate; else collideWith->m_fRotationCur += collideWith->m_headingRate; } } else { SetLookFlag(collideWith, false); TurnBody(); animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PARTIAL_PUNCH, 8.0f); animAssoc->flags |= ASSOC_FADEOUTWHENDONE; m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 2000; if (!heIsMissionChar) { CVector2D posDiff2D(posDiff); int direction = collideWith->GetLocalDirection(posDiff2D); collideWith->StartFightDefend(direction, HITLEVEL_HIGH, 5); } } } } } } else if (collideWith->m_pedStats->m_defendWeakness <= 1.5f || heIsMissionChar || m_pedStats->m_defendWeakness <= collideWith->m_pedStats->m_defendWeakness) { // He looks us and we're not at his right side if (heLooksToUs && DotProduct(posDiff,collideWith->GetRight()) > 0.0f) { CVector moveForce = GetRight(); moveForce.z += 0.1f; ApplyMoveForce(moveForce); if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) animToPlay = ANIM_STD_HIT_LEFT; else animToPlay = ANIM_STD_HITBYGUN_LEFT; } else if (heLooksToUs) { CVector moveForce = GetRight() * -1.0f; moveForce.z += 0.1f; ApplyMoveForce(moveForce); if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) animToPlay = ANIM_STD_HIT_RIGHT; else animToPlay = ANIM_STD_HITBYGUN_RIGHT; } else { if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) animToPlay = ANIM_STD_HIT_BACK; else animToPlay = ANIM_STD_HITBYGUN_BACK; } if (collideWith->IsPedInControl() && CTimer::GetTimeInMilliseconds() > collideWith->m_nPedStateTimer) { animAssoc = CAnimManager::BlendAnimation(collideWith->GetClump(), ASSOCGRP_STD, animToPlay, 8.0f); animAssoc->flags |= ASSOC_FADEOUTWHENDONE; collideWith->m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 1000; if (m_nPedState == PED_ATTACK) DMAudio.PlayOneShot(m_audioEntityId, SOUND_49, 0.0f); } } else { // We're at his right side if (DotProduct(posDiff, collideWith->GetRight()) <= 0.0f) { CVector moveForce = GetRight() * -1.0f; moveForce.z += 0.1f; ApplyMoveForce(moveForce); if (heLooksToUs) animToPlay = ANIM_STD_HIGHIMPACT_RIGHT; else animToPlay = ANIM_STD_SPINFORWARD_RIGHT; } else { CVector moveForce = GetRight(); moveForce.z += 0.1f; ApplyMoveForce(moveForce); if (heLooksToUs) animToPlay = ANIM_STD_HIGHIMPACT_LEFT; else animToPlay = ANIM_STD_SPINFORWARD_LEFT; } if (m_nPedState == PED_ATTACK && collideWith->IsPedInControl()) DMAudio.PlayOneShot(m_audioEntityId, SOUND_49, 0.0f); collideWith->SetFall(3000, animToPlay, 0); } } else { if (!IsPedInControl()) return; if (collideWith->m_nMoveState == PEDMOVE_NONE || collideWith->m_nMoveState == PEDMOVE_STILL) return; if (m_nPedType != collideWith->m_nPedType || m_nPedType == PEDTYPE_CIVMALE || m_nPedType == PEDTYPE_CIVFEMALE) { if (!weAreMissionChar && heLooksToUs && m_pedStats->m_fear > 100 - collideWith->m_pedStats->m_temper) { if (CGeneral::GetRandomNumber() & 1 && CTimer::GetTimeInMilliseconds() < m_nPedStateTimer){ SetEvasiveStep(collideWith, 2); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; } else if (collideWith->m_nMoveState > PEDMOVE_WALK) { waitTime = 2000; SetWaitState(WAITSTATE_PLAYANIM_DUCK, &waitTime); } } } else if (heLooksToUs && collideWith->m_nPedState != PED_STEP_AWAY && m_nPedState != PED_STEP_AWAY && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { SetEvasiveStep(collideWith, 1); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; } } if (IsPlayer()) { SetLookFlag(collideWith, true); SetLookTimer(800); } } else { bool isRunning = m_nMoveState == PEDMOVE_RUN || m_nMoveState == PEDMOVE_SPRINT; SetFindPathAndFlee(collideWith, 5000, !isRunning); } } void CPed::KillPedWithCar(CVehicle *car, float impulse) { CVehicleModelInfo *vehModel; CColModel *vehColModel; uint8 damageDir; PedNode nodeToDamage; eWeaponType killMethod; if (m_nPedState == PED_FALL || m_nPedState == PED_DIE) { if (!m_pCollidingEntity || car->GetStatus() == STATUS_PLAYER) m_pCollidingEntity = car; return; } if (m_nPedState == PED_DEAD) return; if (m_pCurSurface) { if (m_pCurSurface->IsVehicle() && (((CVehicle*)m_pCurSurface)->IsBoat()|| IsPlayer())) return; } CVector distVec = GetPosition() - car->GetPosition(); if ((impulse > 12.0f || car->GetModelIndex() == MI_TRAIN) && !IsPlayer()) { nodeToDamage = PED_TORSO; killMethod = WEAPONTYPE_RAMMEDBYCAR; uint8 randVal = CGeneral::GetRandomNumber() & 3; if (car == FindPlayerVehicle()) { float carSpeed = car->m_vecMoveSpeed.Magnitude(); uint8 shakeFreq; if (100.0f * carSpeed * 2000.0f / car->m_fMass + 80.0f <= 250.0f) { shakeFreq = 100.0f * carSpeed * 2000.0f / car->m_fMass + 80.0f; } else { shakeFreq = 250.0f; } CPad::GetPad(0)->StartShake(40000 / shakeFreq, shakeFreq); } bIsStanding = false; damageDir = GetLocalDirection(-m_vecMoveSpeed); vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(car->GetModelIndex()); vehColModel = vehModel->GetColModel(); float carRightAndDistDotProd = DotProduct(distVec, car->GetRight()); if (car->GetModelIndex() == MI_TRAIN) { killMethod = WEAPONTYPE_RUNOVERBYCAR; nodeToDamage = PED_HEAD; m_vecMoveSpeed = 0.9f * car->m_vecMoveSpeed; m_vecMoveSpeed.z = 0.0f; if (damageDir == 1 || damageDir == 3) damageDir = 2; if (CGame::nastyGame) DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLATTER, 0.0f); // Car doesn't look to us } else if (DotProduct(car->m_vecMoveSpeed, car->GetForward()) >= 0.0f){ if (0.99f * vehColModel->boundingBox.max.x < Abs(carRightAndDistDotProd)) { // We're at the right of the car if (carRightAndDistDotProd <= 0.0f) nodeToDamage = PED_UPPERARML; else nodeToDamage = PED_UPPERARMR; if (Abs(DotProduct(distVec, car->GetForward())) < 0.85f * vehColModel->boundingBox.max.y) { killMethod = WEAPONTYPE_RUNOVERBYCAR; m_vecMoveSpeed = 0.9f * car->m_vecMoveSpeed; m_vecMoveSpeed.z = 0.0f; if (damageDir == 1 || damageDir == 3) damageDir = 2; if (CGame::nastyGame) DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLATTER, 0.0f); } } else { float carFrontAndDistDotProd = DotProduct(distVec, car->GetForward()); // carFrontAndDistDotProd <= 0.0 car looks to us if ((carFrontAndDistDotProd <= 0.1 || randVal <= 1) && randVal != 0) { killMethod = WEAPONTYPE_RUNOVERBYCAR; nodeToDamage = PED_HEAD; m_vecMoveSpeed = 0.9f * car->m_vecMoveSpeed; m_vecMoveSpeed.z = 0.0f; if (damageDir == 1 || damageDir == 3) damageDir = 2; if (CGame::nastyGame) DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLATTER, 0.0f); } else { nodeToDamage = PED_MID; float vehColMaxY = vehColModel->boundingBox.max.y; float vehColMinY = vehColModel->boundingBox.min.y; float vehColMaxZ = vehColModel->boundingBox.max.z; float carFrontZ = car->GetForward().z; float carHighestZ, carLength; if (carFrontZ < -0.2f) { // Highest point of car's back carHighestZ = (car->GetMatrix() * CVector(0.0f, vehColMinY, vehColMaxZ)).z; carLength = vehColMaxY - vehColMinY; } else if (carFrontZ > 0.1f) { // Highest point of car's front carHighestZ = (car->GetMatrix() * CVector(0.0f, vehColMaxY, vehColMaxZ)).z; float highestZDist = carHighestZ - GetPosition().z; if (highestZDist > 0.0f) { GetMatrix().GetPosition().z += 0.5f * highestZDist; carHighestZ += highestZDist * 0.25f; } carLength = vehColMaxY; } else { // Highest point of car's front carHighestZ = (car->GetMatrix() * CVector(0.0f, vehColMaxY, vehColMaxZ)).z; carLength = vehColMaxY; } float pedJumpSpeedToReachHighestZ = (carHighestZ - GetPosition().z) / (carLength / car->m_vecMoveSpeed.Magnitude()); // TODO: What are we doing down here? float unknown = ((CGeneral::GetRandomNumber() % 256) * 0.002 + 1.5) * pedJumpSpeedToReachHighestZ; // After this point distVec isn't really distVec. distVec = car->m_vecMoveSpeed; distVec.Normalise(); distVec *= 0.2 * unknown; if (damageDir != 1 && damageDir != 3) distVec.z += unknown; else distVec.z += 1.5f * unknown; m_vecMoveSpeed = distVec; damageDir += 2; if (damageDir > 3) damageDir = damageDir - 4; if (car->IsCar()) { CObject *bonnet = ((CAutomobile*)car)->RemoveBonnetInPedCollision(); if (bonnet) { if (CGeneral::GetRandomNumber() & 1) { bonnet->m_vecMoveSpeed += Multiply3x3(car->GetMatrix(), CVector(0.1f, 0.0f, 0.5f)); } else { bonnet->m_vecMoveSpeed += Multiply3x3(car->GetMatrix(), CVector(-0.1f, 0.0f, 0.5f)); } CVector forceDir = car->GetUp() * 10.0f; bonnet->ApplyTurnForce(forceDir, car->GetForward()); } } } } } if (car->pDriver) { CEventList::RegisterEvent((m_nPedType == PEDTYPE_COP ? EVENT_HIT_AND_RUN_COP : EVENT_HIT_AND_RUN), EVENT_ENTITY_PED, this, car->pDriver, 1000); } ePedPieceTypes pieceToDamage; switch (nodeToDamage) { case PED_HEAD: pieceToDamage = PEDPIECE_HEAD; break; case PED_UPPERARML: pieceToDamage = PEDPIECE_LEFTARM; break; case PED_UPPERARMR: pieceToDamage = PEDPIECE_RIGHTARM; break; default: pieceToDamage = PEDPIECE_MID; break; } InflictDamage(car, killMethod, 1000.0f, pieceToDamage, damageDir); if (DyingOrDead() && bIsPedDieAnimPlaying && !m_pCollidingEntity) { m_pCollidingEntity = car; } if (nodeToDamage == PED_MID) bKnockedUpIntoAir = true; else bKnockedUpIntoAir = false; distVec.Normalise(); distVec *= Min(car->m_fMass / 1400.0f, 1.0f); car->ApplyMoveForce(distVec * -100.0f); Say(SOUND_PED_DEFEND); } else if (m_vecDamageNormal.z < -0.8f && impulse > 3.0f || impulse > 6.0f && (!IsPlayer() || impulse > 10.0f)) { bIsStanding = false; uint8 fallDirection = GetLocalDirection(-car->m_vecMoveSpeed); float damage; if (IsPlayer() && car->GetModelIndex() == MI_TRAIN) damage = 150.0f; else damage = 30.0f; InflictDamage(car, WEAPONTYPE_RAMMEDBYCAR, damage, PEDPIECE_TORSO, fallDirection); SetFall(1000, (AnimationId)(fallDirection + ANIM_STD_HIGHIMPACT_FRONT), true); if (OnGround() && !m_pCollidingEntity && (!IsPlayer() || bHasHitWall || car->GetModelIndex() == MI_TRAIN || m_vecDamageNormal.z < -0.8f)) { m_pCollidingEntity = car; } bKnockedUpIntoAir = false; if (car->GetModelIndex() != MI_TRAIN && !bHasHitWall) { m_vecMoveSpeed = car->m_vecMoveSpeed * 0.75f; } m_vecMoveSpeed.z = 0.0f; distVec.Normalise(); distVec *= Min(car->m_fMass / 1400.0f, 1.0f); car->ApplyMoveForce(distVec * -60.0f); Say(SOUND_PED_DEFEND); } if (IsGangMember()) { CPed *driver = car->pDriver; if (driver && driver->IsPlayer() #ifdef FIX_BUGS && (CharCreatedBy != MISSION_CHAR || bRespondsToThreats) && (!m_leader || m_leader != driver) #endif ) { RegisterThreatWithGangPeds(driver); } } } void CPed::DriveVehicle(void) { if (bOffscreen) return; CVehicle *veh = m_pMyVehicle; if (veh->IsBike()) { CBike *bike = (CBike*)veh; float blendDelta = 1.0f; float targetUDLean = 0.0f; CAnimBlendAssociation *leftAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_LEFT); CAnimBlendAssociation *rightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_RIGHT); CAnimBlendAssociation *stillAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_READY); CAnimBlendAssociation *fwdAssoc, *backAssoc; if (IsPlayer()) { fwdAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_LEANF); backAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_LEANB); } CAnimBlendAssociation *walkbackAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_WALKBACK); CAnimBlendAssociation *drivebyAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_DRIVEBY_LHS); if (!drivebyAssoc) drivebyAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_DRIVEBY_RHS); if (!drivebyAssoc) drivebyAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); float velocityFwdDotProd = DotProduct(bike->m_vecMoveSpeed, bike->GetForward()); if (m_vecTurnSpeed.MagnitudeSqr() > 0.09f) { bike->KnockOffRider(WEAPONTYPE_FALL, 2, this, false); if (bike->pPassengers[0]) bike->KnockOffRider(WEAPONTYPE_FALL, 2, bike->pPassengers[0], false); return; } if (!drivebyAssoc && Abs(velocityFwdDotProd) < 0.02f) { if (!stillAssoc || stillAssoc->blendAmount < 1.0 && stillAssoc->blendDelta <= 0.0) { stillAssoc = CAnimManager::BlendAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_READY, 2.0f); } } else { if (velocityFwdDotProd >= 0.0f) { if (stillAssoc && stillAssoc->blendDelta >= 0.0f) stillAssoc->blendDelta = -4.0f; if (walkbackAssoc && walkbackAssoc->blendDelta >= 0.0f) walkbackAssoc->blendDelta = -4.0f; } else { float maxReverseSpeed = bike->pHandling->Transmission.fMaxReverseVelocity; if (3.5f * maxReverseSpeed > velocityFwdDotProd && (bike->m_nWheelsOnGround || bike->GetUp().z < -0.5f)) { bike->KnockOffRider(WEAPONTYPE_FALL, 2, this, false); if (bike->pPassengers[0]) bike->KnockOffRider(WEAPONTYPE_FALL, 2, bike->pPassengers[0], false); return; } if (bike->m_fGasPedal >= 0.0 || velocityFwdDotProd <= maxReverseSpeed * 1.5) { if (IsPlayer() && velocityFwdDotProd < maxReverseSpeed * 1.5) targetUDLean = -1.0f; if (stillAssoc && stillAssoc->blendDelta >= 0.0f) stillAssoc->blendDelta = -4.0f; if (walkbackAssoc && walkbackAssoc->blendDelta >= 0.0f) { walkbackAssoc->blendDelta = -4.0f; } } else if (!walkbackAssoc || walkbackAssoc->blendAmount < 1.0f && walkbackAssoc->blendDelta <= 0.0f) { walkbackAssoc = CAnimManager::BlendAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_WALKBACK, 4.0f); } } } if (stillAssoc) blendDelta -= Min(1.0f, CTimer::GetTimeStepNonClipped() * 0.02f * stillAssoc->blendDelta + stillAssoc->blendAmount); if (drivebyAssoc) blendDelta -= Min(blendDelta, CTimer::GetTimeStepNonClipped() * 0.02f * drivebyAssoc->blendDelta + drivebyAssoc->blendAmount); if (walkbackAssoc) blendDelta -= Min(blendDelta, CTimer::GetTimeStepNonClipped() * 0.02f * walkbackAssoc->blendDelta + walkbackAssoc->blendAmount); float targetLRLean, timeBlend, neededAngForWheelie, stoppieAng; // Smooth the lean amount if (targetUDLean == -1.0f) { targetLRLean = 0.0f; timeBlend = Pow(0.86f, CTimer::GetTimeStep()); } else { targetLRLean = clamp(bike->m_fLeanLRAngle / bike->pBikeHandling->fFullAnimLean, -1.0f, 1.0f); timeBlend = Pow(0.86f, CTimer::GetTimeStep()); } bike->m_fPedLeanAmountLR = bike->m_fPedLeanAmountLR * timeBlend + (1.0 - timeBlend) * targetLRLean; if (!IsPlayer()) { targetUDLean = 0.0f; } else if (targetUDLean > -1.0f) { targetUDLean = bike->m_fLeanInput; bike->bWheelieCam = false; neededAngForWheelie = 1.0f; if (bike->m_aWheelTimer[0] != 0.0f || bike->m_aWheelTimer[1] != 0.0f || bike->GetForward().z <= 0.0f || (0.0f == bike->m_aWheelTimer[2] && 0.0f == bike->m_aWheelTimer[3])) { if (0.0f == bike->m_aWheelTimer[2] && 0.0f == bike->m_aWheelTimer[3] && (bike->GetForward().z < 0.0f && (bike->m_aWheelTimer[0] != 0.0f || bike->m_aWheelTimer[1] != 0.0f))) { stoppieAng = bike->pBikeHandling->fStoppieAng; if (stoppieAng - bike->GetForward().z > 0.6f * stoppieAng) bike->bWheelieCam = true; } } else { float wheelieAng = bike->pBikeHandling->fWheelieAng; neededAngForWheelie = wheelieAng - bike->GetForward().z; if (neededAngForWheelie < wheelieAng / 2.f) bike->bWheelieCam = true; } if (neededAngForWheelie >= 0.15f) { if (bike->m_fBrakePedal <= 0.5f || velocityFwdDotProd <= 0.01f) { if (bike->m_fGasPedal > 0.5f && targetUDLean <= 0.0f && 0.3f * bike->pHandling->Transmission.fMaxCruiseVelocity > velocityFwdDotProd) { targetUDLean = Min(0.1f, targetUDLean); } } else { targetUDLean = Max(0.1f, targetUDLean); } } else { targetUDLean = Max(0.25f, targetUDLean); } float targetLRLeanABS = Abs(targetLRLean); if (targetLRLeanABS > 0.3f) { // Yes, UD targetUDLean *= Max(0.0f, 1.0f - (targetLRLeanABS - 0.3f) * 50.f / 13.f); } } if (IsPlayer()) { float timeBlend = Pow(0.89f, CTimer::GetTimeStep()); bike->m_fPedLeanAmountUD = (timeBlend * bike->m_fPedLeanAmountUD) + ((1.0f - timeBlend) * targetUDLean); } else { bike->m_fPedLeanAmountUD = 0.0f; } float fwdBackLeanAmount, leftRightLeanAmount; if (Abs(bike->m_fPedLeanAmountLR) <= 0.56f && IsPlayer()) { if (Abs(bike->m_fPedLeanAmountUD) <= 0.56f) { CVector2D smoothedLean(bike->m_fPedLeanAmountLR, bike->m_fPedLeanAmountUD); float smoothLeanMag = smoothedLean.Magnitude(); if (smoothLeanMag <= 0.01f) { fwdBackLeanAmount = Abs(smoothedLean.y); leftRightLeanAmount = Abs(smoothedLean.x); } else { fwdBackLeanAmount = Abs(smoothedLean.y / smoothLeanMag); leftRightLeanAmount = Abs(smoothedLean.x / smoothLeanMag); } } else { fwdBackLeanAmount = 1.0f; leftRightLeanAmount = 0.0f; } } else { fwdBackLeanAmount = 0.0f; leftRightLeanAmount = 1.0f; } float fwdBackBlend = fwdBackLeanAmount * blendDelta; float leftRightBlend = leftRightLeanAmount * blendDelta; if (IsPlayer()) { if (!fwdAssoc) fwdAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_LEANF); if (!backAssoc) backAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_LEANB); if (bike->m_fPedLeanAmountUD < 0.0f) { backAssoc->blendAmount = fwdBackBlend; backAssoc->SetCurrentTime(-(bike->m_fPedLeanAmountUD * backAssoc->hierarchy->totalLength)); backAssoc->flags &= ~ASSOC_RUNNING; fwdAssoc->blendAmount = 0.0f; } else { fwdAssoc->blendAmount = fwdBackBlend; fwdAssoc->SetCurrentTime(bike->m_fPedLeanAmountUD* fwdAssoc->hierarchy->totalLength); fwdAssoc->flags &= ~ASSOC_RUNNING; backAssoc->blendAmount = 0.0f; } } if (!leftAssoc) leftAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_LEFT); if (!rightAssoc) rightAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_RIGHT); if (bike->m_fPedLeanAmountLR < 0.0f) { leftAssoc->blendAmount = leftRightBlend; leftAssoc->SetCurrentTime(-(bike->m_fPedLeanAmountLR * leftAssoc->hierarchy->totalLength)); leftAssoc->flags &= ~ASSOC_RUNNING; rightAssoc->blendAmount = 0.0f; } else { rightAssoc->blendAmount = leftRightBlend; rightAssoc->SetCurrentTime(bike->m_fPedLeanAmountLR* rightAssoc->hierarchy->totalLength); rightAssoc->flags &= ~ASSOC_RUNNING; leftAssoc->blendAmount = 0.0f; } if (velocityFwdDotProd > 0.3f) { RwV3d Xaxis = { 1.0f, 0.0f, 0.0f }; RwV3d Yaxis = { 0.0f, 1.0f, 0.0f }; RtQuatRotate(&m_pFrames[PED_HEAD]->hanimFrame->q, &Xaxis, CGeneral::GetRandomNumberInRange(-6.0f * velocityFwdDotProd, 6.0f * velocityFwdDotProd), rwCOMBINEPOSTCONCAT); RtQuatRotate(&m_pFrames[PED_HEAD]->hanimFrame->q, &Yaxis, CGeneral::GetRandomNumberInRange(-6.0f * velocityFwdDotProd, 6.0f * velocityFwdDotProd), rwCOMBINEPOSTCONCAT); bDontAcceptIKLookAts = true; } return; } if (!IsPlayer()) return; float steerAngle = m_pMyVehicle->m_fSteerAngle; CAnimBlendAssociation* lDriveAssoc; CAnimBlendAssociation* rDriveAssoc; CAnimBlendAssociation* lbAssoc; CAnimBlendAssociation* sitAssoc; if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT)) { sitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_BOAT_DRIVE); if (!sitAssoc || sitAssoc->blendAmount < 1.0f) { return; } lDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_BOAT_DRIVE_LEFT); rDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_BOAT_DRIVE_RIGHT); lbAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_BOAT_LOOKBEHIND); } else if (m_pMyVehicle->bLowVehicle) { sitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT_LO); if (!sitAssoc || sitAssoc->blendAmount < 1.0f) { return; } lDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVE_LEFT_LO); lbAssoc = nil; rDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVE_RIGHT_LO); } else { sitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_SIT); if (!sitAssoc || sitAssoc->blendAmount < 1.0f) { return; } lDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVE_LEFT); rDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVE_RIGHT); lbAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_LOOKBEHIND); } if (lbAssoc && TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON && TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking == LOOKING_LEFT) { lbAssoc->blendDelta = -1000.0f; } CAnimBlendAssociation* driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT); if (!driveByAssoc) driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVEBY_RIGHT); if (!driveByAssoc) driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT_LO); if (!driveByAssoc) driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_DRIVEBY_RIGHT_LO); if (m_pMyVehicle->bLowVehicle || m_pMyVehicle->m_fGasPedal >= 0.0f || driveByAssoc || m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) { if (steerAngle == 0.0f || driveByAssoc) { if (lDriveAssoc) lDriveAssoc->blendAmount = 0.0f; if (rDriveAssoc) rDriveAssoc->blendAmount = 0.0f; } else if (steerAngle <= 0.0f) { if (lDriveAssoc) lDriveAssoc->blendAmount = 0.0f; if (rDriveAssoc) rDriveAssoc->blendAmount = clamp(steerAngle * -100.0f / 61.0f, 0.0f, 1.0f); else if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT)) CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_DRIVE_RIGHT); else if (m_pMyVehicle->bLowVehicle) CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVE_RIGHT_LO); else CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVE_RIGHT); } else { if (rDriveAssoc) rDriveAssoc->blendAmount = 0.0f; if (lDriveAssoc) lDriveAssoc->blendAmount = clamp(steerAngle * 100.0f / 61.0f, 0.0f, 1.0f); else if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT)) CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_DRIVE_LEFT); else if (m_pMyVehicle->bLowVehicle) CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVE_LEFT_LO); else CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVE_LEFT); } if (lbAssoc) lbAssoc->blendDelta = -4.0f; } else { if ((TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON || TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking != LOOKING_LEFT) && (!lbAssoc || lbAssoc->blendAmount < 1.0f && lbAssoc->blendDelta <= 0.0f)) { if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT)) CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_BOAT_LOOKBEHIND, 4.0f); else CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_LOOKBEHIND, 4.0f); } } } void CPed::RemoveWeaponAnims(int unused, float animDelta) { CAnimBlendAssociation *weaponAssoc; //CWeaponInfo::GetWeaponInfo(unused); weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE); if (weaponAssoc) { weaponAssoc->blendDelta = animDelta; weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; } weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE_2ND); if (weaponAssoc) { weaponAssoc->blendDelta = animDelta; weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; } weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE_3RD); if (weaponAssoc) { weaponAssoc->blendDelta = animDelta; weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; } weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_RELOAD); if (weaponAssoc) { weaponAssoc->blendDelta = animDelta; weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; } weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); if (weaponAssoc) { weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; if (weaponAssoc->flags & ASSOC_PARTIAL) weaponAssoc->blendDelta = animDelta; else CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, -animDelta); } } ================================================ FILE: src/peds/PedIK.cpp ================================================ #include "common.h" #include "Bones.h" #include "Camera.h" #include "PedIK.h" #include "Ped.h" #include "General.h" #include "RwHelper.h" LimbMovementInfo CPedIK::ms_torsoInfo = { DEGTORAD(50.0f), DEGTORAD(-50.0f), DEGTORAD(8.0f), DEGTORAD(45.0f), DEGTORAD(-45.0f), DEGTORAD(5.0f) }; LimbMovementInfo CPedIK::ms_headInfo = { DEGTORAD(90.0f), DEGTORAD(-90.0f), DEGTORAD(15.0f), DEGTORAD(45.0f), DEGTORAD(-45.0f), DEGTORAD(8.0f) }; LimbMovementInfo CPedIK::ms_headRestoreInfo = { DEGTORAD(90.0f), DEGTORAD(-90.0f), DEGTORAD(10.0f), DEGTORAD(45.0f), DEGTORAD(-45.0f), DEGTORAD(5.0f) }; LimbMovementInfo CPedIK::ms_upperArmInfo = { DEGTORAD(5.0f), DEGTORAD(-120.0f), DEGTORAD(20.0f), DEGTORAD(70.0f), DEGTORAD(-70.0f), DEGTORAD(20.0f) }; LimbMovementInfo CPedIK::ms_lowerArmInfo = { DEGTORAD(60.0f), DEGTORAD(0.0f), DEGTORAD(15.0f), DEGTORAD(90.0f), DEGTORAD(-90.0f), DEGTORAD(10.0f) }; const RwV3d XaxisIK = { 1.0f, 0.0f, 0.0f}; const RwV3d YaxisIK = { 0.0f, 1.0f, 0.0f}; const RwV3d ZaxisIK = { 0.0f, 0.0f, 1.0f}; CPedIK::CPedIK(CPed *ped) { m_ped = ped; m_flags = 0; m_headOrient.yaw = 0.0f; m_headOrient.pitch = 0.0f; m_torsoOrient.yaw = 0.0f; m_torsoOrient.pitch = 0.0f; m_upperArmOrient.yaw = 0.0f; m_upperArmOrient.pitch = 0.0f; m_lowerArmOrient.yaw = 0.0f; m_lowerArmOrient.pitch = 0.0f; } inline RwMatrix* GetBoneMatrix(CPed *ped, int32 bone) { RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(ped->GetClump()); int idx = RpHAnimIDGetIndex(hier, bone); RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); return &mats[idx]; } inline RwMatrix* GetComponentMatrix(CPed *ped, int32 node) { return GetBoneMatrix(ped, ped->m_pFrames[node]->nodeID); } void CPedIK::RotateTorso(AnimBlendFrameData *node, LimbOrientation *limb, bool changeRoll) { RtQuat *q = &node->hanimFrame->q; RtQuatRotate(q, &XaxisIK, RADTODEG(limb->yaw), rwCOMBINEREPLACE); RtQuatRotate(q, &ZaxisIK, RADTODEG(limb->pitch), rwCOMBINEPRECONCAT); m_ped->bDontAcceptIKLookAts = true; } void CPedIK::GetComponentPosition(RwV3d &pos, uint32 node) { pos = GetComponentMatrix(m_ped, node)->pos; } LimbMoveStatus CPedIK::MoveLimb(LimbOrientation &limb, float targetYaw, float targetPitch, LimbMovementInfo &moveInfo) { LimbMoveStatus result = ONE_ANGLE_COULDNT_BE_SET_EXACTLY; // yaw if(Abs(limb.yaw-targetYaw) < moveInfo.yawD){ limb.yaw = targetYaw; result = ANGLES_SET_EXACTLY; }else{ if (limb.yaw > targetYaw) { limb.yaw -= moveInfo.yawD; } else if (limb.yaw < targetYaw) { limb.yaw += moveInfo.yawD; } } if (limb.yaw > moveInfo.maxYaw || limb.yaw < moveInfo.minYaw) { limb.yaw = clamp(limb.yaw, moveInfo.minYaw, moveInfo.maxYaw); result = ANGLES_SET_TO_MAX; } // pitch if (Abs(limb.pitch - targetPitch) < moveInfo.pitchD){ limb.pitch = targetPitch; }else{ if (limb.pitch > targetPitch) { limb.pitch -= moveInfo.pitchD; } else if (limb.pitch < targetPitch) { limb.pitch += moveInfo.pitchD; } result = ONE_ANGLE_COULDNT_BE_SET_EXACTLY; } if (limb.pitch > moveInfo.maxPitch || limb.pitch < moveInfo.minPitch) { limb.pitch = clamp(limb.pitch, moveInfo.minPitch, moveInfo.maxPitch); result = ANGLES_SET_TO_MAX; } return result; } bool CPedIK::RestoreGunPosn(void) { LimbMoveStatus limbStatus = MoveLimb(m_torsoOrient, 0.0f, 0.0f, ms_torsoInfo); RotateTorso(m_ped->m_pFrames[PED_MID], &m_torsoOrient, false); return limbStatus == ANGLES_SET_EXACTLY; } bool CPedIK::LookInDirection(float targetYaw, float targetPitch) { bool success = true; float yaw, pitch; if (!(m_ped->m_pFrames[PED_HEAD]->flag & AnimBlendFrameData::IGNORE_ROTATION)) { m_ped->m_pFrames[PED_HEAD]->flag |= AnimBlendFrameData::IGNORE_ROTATION; RwMatrix *m = GetComponentMatrix(m_ped, PED_NECK); m_headOrient.yaw = Atan2(-m->at.y, -m->at.x); m_headOrient.yaw -= m_ped->m_fRotationCur; m_headOrient.yaw = CGeneral::LimitRadianAngle(m_headOrient.yaw); float up = clamp(m->up.z, -1.0f, 1.0f); m_headOrient.pitch = Atan2(-up, Sqrt(1.0f - SQR(-up))); } // parent of head is neck RwMatrix *m = GetComponentMatrix(m_ped, PED_NECK); yaw = CGeneral::LimitRadianAngle(Atan2(-m->at.y, -m->at.x)); float up = clamp(m->up.z, -1.0f, 1.0f); pitch = Atan2(-up, Sqrt(1.0f - SQR(-up))); float headYaw = CGeneral::LimitRadianAngle(targetYaw - (yaw + m_torsoOrient.yaw)); float headPitch = CGeneral::LimitRadianAngle(targetPitch - pitch) * Cos(Min(Abs(headYaw), HALFPI)); LimbMoveStatus headStatus = MoveLimb(m_headOrient, headYaw, headPitch, ms_headInfo); if (headStatus == ANGLES_SET_TO_MAX) success = false; if (headStatus != ANGLES_SET_EXACTLY && !(m_flags & LOOKAROUND_HEAD_ONLY)) if (MoveLimb(m_torsoOrient, CGeneral::LimitRadianAngle(targetYaw-m_ped->m_fRotationCur), targetPitch, ms_torsoInfo)) success = true; // This was RotateHead RtQuat *q = &m_ped->m_pFrames[PED_HEAD]->hanimFrame->q; RtQuatRotate(q, &ZaxisIK, RADTODEG(m_headOrient.pitch), rwCOMBINEREPLACE); RtQuatRotate(q, &XaxisIK, RADTODEG(m_headOrient.yaw), rwCOMBINEPRECONCAT); m_ped->bDontAcceptIKLookAts = true; if (!(m_flags & LOOKAROUND_HEAD_ONLY)) RotateTorso(m_ped->m_pFrames[PED_MID], &m_torsoOrient, false); return success; } bool CPedIK::LookAtPosition(CVector const &pos) { RwV3d *pedpos = &GetComponentMatrix(m_ped, PED_MID)->pos; float yawToFace = CGeneral::GetRadianAngleBetweenPoints( pos.x, pos.y, pedpos->x, pedpos->y); float pitchToFace = CGeneral::GetRadianAngleBetweenPoints( // BUG? not using pedpos here pos.z, (m_ped->GetPosition() - pos).Magnitude2D(), pedpos->z, 0.0f); return LookInDirection(yawToFace, pitchToFace); } bool CPedIK::PointGunInDirection(float targetYaw, float targetPitch) { bool result = true; bool armPointedToGun = false; targetYaw = CGeneral::LimitRadianAngle(targetYaw - m_ped->GetForward().Heading()); m_flags &= ~GUN_POINTED_SUCCESSFULLY; m_flags |= LOOKAROUND_HEAD_ONLY; if (m_flags & AIMS_WITH_ARM) { armPointedToGun = PointGunInDirectionUsingArm(targetYaw, targetPitch); targetYaw = CGeneral::LimitRadianAngle(targetYaw - (m_upperArmOrient.yaw + m_lowerArmOrient.yaw)); } if (armPointedToGun) { if (m_flags & AIMS_WITH_ARM && m_torsoOrient.yaw * m_upperArmOrient.yaw < 0.0f) MoveLimb(m_torsoOrient, 0.0f, m_torsoOrient.pitch, ms_torsoInfo); } else { // Unused code RwMatrix *matrix; float yaw, pitch; matrix = RwMatrixCreate(); *matrix = *GetComponentMatrix(m_ped, PED_CLAVICLER); ExtractYawAndPitchWorld(matrix, &yaw, &pitch); RwMatrixDestroy(matrix); if(m_flags & AIMS_WITH_ARM){ if(targetPitch > 0.0f) targetPitch = Max(targetPitch - Abs(targetYaw), 0.0f); else targetPitch = Min(targetPitch + Abs(targetYaw), 0.0f); } LimbMoveStatus status = MoveLimb(m_torsoOrient, targetYaw, targetPitch, ms_torsoInfo); if (status == ANGLES_SET_TO_MAX) result = false; else if (status == ANGLES_SET_EXACTLY) m_flags |= GUN_POINTED_SUCCESSFULLY; } RwMatrix *m = GetBoneMatrix(m_ped, BONE_spine); // BUG: game uses index 2 directly, which happens to be identical to BONE_spine RwV3d axis = { 0.0f, 0.0f, 0.0f }; float axisangle = -CGeneral::LimitRadianAngle(Atan2(-m->at.y, -m->at.x) - m_ped->m_fRotationCur); axis.y = -Sin(axisangle); axis.z = Cos(axisangle); // this was RotateTorso RtQuat *q = &m_ped->m_pFrames[PED_MID]->hanimFrame->q; RtQuatRotate(q, &axis, RADTODEG(m_torsoOrient.pitch), rwCOMBINEPOSTCONCAT); RtQuatRotate(q, &XaxisIK, RADTODEG(m_torsoOrient.yaw), rwCOMBINEPOSTCONCAT); m_ped->bDontAcceptIKLookAts = true; return result; } bool CPedIK::PointGunInDirectionUsingArm(float targetYaw, float targetPitch) { bool result = false; RwMatrix *matrix; float yaw, pitch; float uaRoll = 45.0f; float handRoll = 30.0f; matrix = GetComponentMatrix(m_ped, PED_CLAVICLER); yaw = CGeneral::LimitRadianAngle(Atan2(matrix->right.y, matrix->right.x) - m_ped->m_fRotationCur); pitch = Atan2(matrix->up.z, Sqrt(1.0f - SQR(matrix->up.z))); float uaYaw, uaPitch; uaYaw = CGeneral::LimitRadianAngle(targetYaw - yaw - DEGTORAD(15.0f)); uaPitch = CGeneral::LimitRadianAngle(targetPitch - pitch + DEGTORAD(10.0f)); LimbMoveStatus uaStatus = MoveLimb(m_upperArmOrient, uaYaw, uaPitch, ms_upperArmInfo); if (uaStatus == ANGLES_SET_EXACTLY) { m_flags |= GUN_POINTED_SUCCESSFULLY; result = true; } if (uaStatus == ANGLES_SET_TO_MAX) { float laYaw = uaYaw - m_upperArmOrient.yaw; LimbMoveStatus laStatus; if (laYaw > 0.0f){ float rollReduce = laYaw/DEGTORAD(30.0f); uaRoll *= 1.0f - Min(rollReduce, 1.0f); handRoll *= 1.0f - Min(rollReduce, 1.0f); laYaw *= 1.9f; laStatus = MoveLimb(m_lowerArmOrient, laYaw, 0.0f, ms_lowerArmInfo); // some unused statics here float uaPitchAmount = 1.0f - (m_lowerArmOrient.yaw + m_upperArmOrient.yaw) * 0.34f; float f1 = ms_upperArmInfo.maxPitch * Max(uaPitchAmount, 0.0f); float f2 = 0.2f*m_lowerArmOrient.yaw + m_upperArmOrient.pitch; m_upperArmOrient.pitch = Min(f1, f2); }else laStatus = MoveLimb(m_lowerArmOrient, laYaw, 0.0f, ms_lowerArmInfo); if (laStatus == ANGLES_SET_EXACTLY) { m_flags |= GUN_POINTED_SUCCESSFULLY; result = true; } // game does this stupidly by going through the clump extension... RtQuat *q = &m_ped->m_pFrames[PED_FOREARMR]->hanimFrame->q; RtQuatRotate(q, &ZaxisIK, -RADTODEG(m_lowerArmOrient.yaw), rwCOMBINEREPLACE); RtQuatRotate(q, &XaxisIK, -RADTODEG(m_lowerArmOrient.pitch), rwCOMBINEPOSTCONCAT); m_ped->bDontAcceptIKLookAts = true; } RtQuat *q = &m_ped->m_pFrames[PED_UPPERARMR]->hanimFrame->q; RtQuatRotate(q, &XaxisIK, uaRoll, rwCOMBINEREPLACE); RtQuatRotate(q, &YaxisIK, -RADTODEG(m_upperArmOrient.pitch), rwCOMBINEPOSTCONCAT); RtQuatRotate(q, &ZaxisIK, -RADTODEG(m_upperArmOrient.yaw+HALFPI), rwCOMBINEPOSTCONCAT); m_ped->bDontAcceptIKLookAts = true; q = &m_ped->m_pFrames[PED_HANDR]->hanimFrame->q; RtQuatRotate(q, &XaxisIK, handRoll, rwCOMBINEPRECONCAT); return result; } bool CPedIK::PointGunAtPosition(CVector const& position) { CVector startPoint; if (m_ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_SPAS12_SHOTGUN || m_ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_STUBBY_SHOTGUN) startPoint = m_ped->GetPosition(); else { RwV3d armPos; GetComponentPosition(armPos, PED_UPPERARMR); startPoint.x = m_ped->GetPosition().x; startPoint.y = m_ped->GetPosition().y; startPoint.z = armPos.z; } return PointGunInDirection( CGeneral::GetRadianAngleBetweenPoints(position.x, position.y, startPoint.x, startPoint.y), CGeneral::GetRadianAngleBetweenPoints(position.z, Distance2D(m_ped->GetPosition(), position.x, position.y), startPoint.z, 0.0f)); } bool CPedIK::RestoreLookAt(void) { bool result = false; float yaw, pitch; if (m_ped->m_pFrames[PED_HEAD]->flag & AnimBlendFrameData::IGNORE_ROTATION) { m_ped->m_pFrames[PED_HEAD]->flag &= (~AnimBlendFrameData::IGNORE_ROTATION); } else { ExtractYawAndPitchLocalSkinned(m_ped->m_pFrames[PED_HEAD], &yaw, &pitch); if (MoveLimb(m_headOrient, yaw, pitch, ms_headRestoreInfo) == ANGLES_SET_EXACTLY) result = true; } // This was RotateHead RtQuat *q = &m_ped->m_pFrames[PED_HEAD]->hanimFrame->q; RtQuatRotate(q, &XaxisIK, RADTODEG(m_headOrient.yaw), rwCOMBINEREPLACE); RtQuatRotate(q, &ZaxisIK, RADTODEG(m_headOrient.pitch), rwCOMBINEPRECONCAT); m_ped->bDontAcceptIKLookAts = true; if (!(m_flags & LOOKAROUND_HEAD_ONLY)) MoveLimb(m_torsoOrient, 0.0f, 0.0f, ms_torsoInfo); if (!(m_flags & LOOKAROUND_HEAD_ONLY)) RotateTorso(m_ped->m_pFrames[PED_MID], &m_torsoOrient, false); return result; } void CPedIK::ExtractYawAndPitchWorld(RwMatrix *mat, float *yaw, float *pitch) { float f = clamp(DotProduct(mat->up, CVector(0.0f, 1.0f, 0.0f)), -1.0f, 1.0f); *yaw = Acos(f); if (mat->up.x > 0.0f) *yaw = -*yaw; f = clamp(DotProduct(mat->right, CVector(0.0f, 0.0f, 1.0f)), -1.0f, 1.0f); *pitch = Acos(f); if (mat->up.z > 0.0f) *pitch = -*pitch; } void CPedIK::ExtractYawAndPitchLocal(RwMatrix *mat, float *yaw, float *pitch) { float f = clamp(DotProduct(mat->at, CVector(0.0f, 0.0f, 1.0f)), -1.0f, 1.0f); *yaw = Acos(f); if (mat->at.y > 0.0f) *yaw = -*yaw; f = clamp(DotProduct(mat->right, CVector(1.0f, 0.0f, 0.0f)), -1.0f, 1.0f); *pitch = Acos(f); if (mat->up.x > 0.0f) *pitch = -*pitch; } void CPedIK::ExtractYawAndPitchLocalSkinned(AnimBlendFrameData *node, float *yaw, float *pitch) { RwMatrix *mat = RwMatrixCreate(); RtQuatConvertToMatrix(&node->hanimFrame->q, mat); ExtractYawAndPitchLocal(mat, yaw, pitch); RwMatrixDestroy(mat); } ================================================ FILE: src/peds/PedIK.h ================================================ #pragma once #include "common.h" #include "AnimBlendClumpData.h" struct LimbOrientation { float yaw; float pitch; }; struct LimbMovementInfo { float maxYaw; float minYaw; float yawD; float maxPitch; float minPitch; float pitchD; }; enum LimbMoveStatus { ANGLES_SET_TO_MAX, // because given angles were unreachable ONE_ANGLE_COULDNT_BE_SET_EXACTLY, // because it can't be reached in a jiffy ANGLES_SET_EXACTLY }; class CPed; class CPedIK { public: enum { GUN_POINTED_SUCCESSFULLY = 1, // set but unused LOOKAROUND_HEAD_ONLY = 2, AIMS_WITH_ARM = 4, }; CPed *m_ped; LimbOrientation m_headOrient; LimbOrientation m_torsoOrient; LimbOrientation m_upperArmOrient; LimbOrientation m_lowerArmOrient; int32 m_flags; static LimbMovementInfo ms_torsoInfo; static LimbMovementInfo ms_headInfo; static LimbMovementInfo ms_headRestoreInfo; static LimbMovementInfo ms_upperArmInfo; static LimbMovementInfo ms_lowerArmInfo; CPedIK(CPed *ped); bool PointGunInDirection(float targetYaw, float targetPitch); bool PointGunInDirectionUsingArm(float targetYaw, float targetPitch); bool PointGunAtPosition(CVector const& position); void GetComponentPosition(RwV3d &pos, uint32 node); void RotateTorso(AnimBlendFrameData* animBlend, LimbOrientation* limb, bool changeRoll); void ExtractYawAndPitchLocal(RwMatrix *mat, float *yaw, float *pitch); void ExtractYawAndPitchLocalSkinned(AnimBlendFrameData *node, float *yaw, float *pitch); void ExtractYawAndPitchWorld(RwMatrix *mat, float *yaw, float *pitch); LimbMoveStatus MoveLimb(LimbOrientation &limb, float targetYaw, float targetPitch, LimbMovementInfo &moveInfo); bool RestoreGunPosn(void); bool LookInDirection(float targetYaw, float targetPitch); bool LookAtPosition(CVector const& pos); bool RestoreLookAt(void); }; VALIDATE_SIZE(CPedIK, 0x28); ================================================ FILE: src/peds/PedPlacement.cpp ================================================ #include "common.h" #include "Ped.h" #include "PedPlacement.h" #include "World.h" bool CPedPlacement::FindZCoorForPed(CVector* pos) { float zForPed; float startZ = pos->z - 100.0f; float foundColZ = -100.0f; float foundColZ2 = -100.0f; CColPoint foundCol; CEntity* foundEnt; CVector vec( pos->x, pos->y, pos->z + 1.0f ); if (CWorld::ProcessVerticalLine(vec, startZ, foundCol, foundEnt, true, false, false, false, true, false, nil)) foundColZ = foundCol.point.z; // Adjust coords and do a second test vec.x += 0.1f; vec.y += 0.1f; if (CWorld::ProcessVerticalLine(vec, startZ, foundCol, foundEnt, true, false, false, false, true, false, nil)) foundColZ2 = foundCol.point.z; zForPed = Max(foundColZ, foundColZ2); if (zForPed > -99.0f) { pos->z = FEET_OFFSET + zForPed; return true; } return false; } CEntity* CPedPlacement::IsPositionClearOfCars(Const CVector *pos) { return CWorld::TestSphereAgainstWorld(*pos, 0.25f, nil, true, true, false, false, false, false); } bool CPedPlacement::IsPositionClearForPed(const CVector& pos, float radius, int total, CEntity** entities) { int16 count; if (radius == -1.0f) radius = 0.75f; if (total == -1) total = 2; CWorld::FindObjectsKindaColliding(pos, radius, true, &count, total, entities, false, true, true, false, false); return count == 0; } ================================================ FILE: src/peds/PedPlacement.h ================================================ #pragma once class CPedPlacement { public: static bool FindZCoorForPed(CVector* pos); static CEntity* IsPositionClearOfCars(Const CVector*); static bool IsPositionClearForPed(const CVector& pos, float radius = -1.0f, int total = -1, CEntity** entities = nil); }; ================================================ FILE: src/peds/PedRoutes.cpp ================================================ #include "common.h" #include "main.h" #include "PedRoutes.h" CRouteNode gaRoutes[NUMPEDROUTES]; void CRouteNode::Initialise() { for (int i = 0; i < NUMPEDROUTES; i++) { gaRoutes[i].m_route = -1; gaRoutes[i].m_pos = CVector(0.0f, 0.0f, 0.0f); } } int16 CRouteNode::GetRouteThisPointIsOn(int16 point) { return gaRoutes[point].m_route; } // Actually GetFirstPointOfRoute int16 CRouteNode::GetRouteStart(int16 route) { for (int i = 0; i < NUMPEDROUTES; i++) { if (route == gaRoutes[i].m_route) return i; } return -1; } CVector CRouteNode::GetPointPosition(int16 point) { return gaRoutes[point].m_pos; } void CRouteNode::AddRoutePoint(int16 route, CVector pos) { uint16 point; for (point = 0; point < NUMPEDROUTES; point++) { if (gaRoutes[point].m_route == -1) break; } #ifdef FIX_BUGS if (point == NUMPEDROUTES) return; #endif gaRoutes[point].m_route = route; gaRoutes[point].m_pos = pos; } void CRouteNode::RemoveRoute(int16 route) { uint16 first_point, last_point, i; for (first_point = 0; first_point < NUMPEDROUTES; first_point++) { if (gaRoutes[first_point].m_route == route) break; } if (first_point == NUMPEDROUTES) return; for (last_point = first_point; last_point < NUMPEDROUTES; last_point++) if (gaRoutes[last_point].m_route != route) break; uint16 diff = last_point - first_point; #ifdef FIX_BUGS for (i = first_point; i < NUMPEDROUTES - diff; i++) gaRoutes[i] = gaRoutes[i + diff]; #else for (i = 0; i < diff; i++) gaRoutes[first_point + i] = gaRoutes[last_point + i]; #endif for (i = NUMPEDROUTES - diff; i < NUMPEDROUTES; i++) gaRoutes[i].m_route = -1; } ================================================ FILE: src/peds/PedRoutes.h ================================================ #pragma once class CRouteNode { public: int16 m_route; CVector m_pos; static int16 GetRouteThisPointIsOn(int16); static CVector GetPointPosition(int16); static int16 GetRouteStart(int16); static void AddRoutePoint(int16, CVector); static void RemoveRoute(int16); static void Initialise(void); }; ================================================ FILE: src/peds/PedType.cpp ================================================ #include "common.h" #include "General.h" #include "FileMgr.h" #include "PedType.h" CPedType *CPedType::ms_apPedType[NUM_PEDTYPES]; CPedStats *CPedStats::ms_apPedStats[NUM_PEDSTATS]; void CPedType::Initialise(void) { int i; debug("Initialising CPedType...\n"); for(i = 0; i < NUM_PEDTYPES; i++){ ms_apPedType[i] = new CPedType; ms_apPedType[i]->m_flag = PED_FLAG_PLAYER1; ms_apPedType[i]->unknown1 = 0.0f; ms_apPedType[i]->unknown2 = 0.0f; // unknown3 not initialized ms_apPedType[i]->unknown4 = 0.0f; ms_apPedType[i]->unknown5 = 0.0f; ms_apPedType[i]->m_threats = 0; ms_apPedType[i]->m_avoid = 0; } debug("Loading ped data...\n"); LoadPedData(); debug("CPedType ready\n"); } void CPedType::Shutdown(void) { int i; debug("Shutting down CPedType...\n"); for(i = 0; i < NUM_PEDTYPES; i++) delete ms_apPedType[i]; debug("CPedType shut down\n"); } void CPedType::LoadPedData(void) { char *buf; char line[256]; char word[32]; ssize_t bp, buflen; int lp, linelen; int type; uint32 flags; float f1, f2, f3, f4, f5; type = NUM_PEDTYPES; buf = new char[16 * 1024]; CFileMgr::SetDir("DATA"); buflen = CFileMgr::LoadFile("PED.DAT", (uint8*)buf, 16 * 1024, "r"); CFileMgr::SetDir(""); for(bp = 0; bp < buflen; ){ // read file line by line for(linelen = 0; buf[bp] != '\n' && bp < buflen; bp++){ if(buf[bp] == '\r' || buf[bp] == ',' || buf[bp] == '\t') line[linelen++] = ' '; else line[linelen++] = buf[bp]; } bp++; line[linelen] = '\0'; // skip white space for(lp = 0; line[lp] <= ' '; lp++); if(lp >= linelen || // FIX: game uses == here, but this is safer if we have empty lines line[lp] == '#') continue; // Game uses just "line" here since sscanf already trims whitespace, but this is safer sscanf(&line[lp], "%s", word); if(strcmp(word, "Threat") == 0){ flags = 0; lp += 7; while(sscanf(&line[lp], "%s", word) == 1 && lp <= linelen){ flags |= FindPedFlag(word); // skip word while(line[lp] != ' ' && line[lp] != '\n' && line[lp] != '\0') lp++; // skip white space while(line[lp] == ' ') lp++; } ms_apPedType[type]->m_threats = flags; }else if(strcmp(word, "Avoid") == 0){ flags = 0; lp += 6; while(sscanf(&line[lp], "%s", word) == 1 && lp <= linelen){ flags |= FindPedFlag(word); // skip word while(line[lp] != ' ' && line[lp] != '\n' && line[lp] != '\0') lp++; // skip white space while(line[lp] == ' ') lp++; } ms_apPedType[type]->m_avoid = flags; }else{ sscanf(line, "%s %f %f %f %f %f", word, &f1, &f2, &f3, &f4, &f5); type = FindPedType(word); ms_apPedType[type]->m_flag = FindPedFlag(word); // unknown values ms_apPedType[type]->unknown1 = f1 / 50.0f; ms_apPedType[type]->unknown2 = f2 / 50.0f; ms_apPedType[type]->unknown3 = f3 / 50.0f; ms_apPedType[type]->unknown4 = f4; ms_apPedType[type]->unknown5 = f5; } } delete[] buf; } ePedType CPedType::FindPedType(char *type) { if(strcmp(type, "PLAYER1") == 0) return PEDTYPE_PLAYER1; if(strcmp(type, "PLAYER2") == 0) return PEDTYPE_PLAYER2; if(strcmp(type, "PLAYER3") == 0) return PEDTYPE_PLAYER3; if(strcmp(type, "PLAYER4") == 0) return PEDTYPE_PLAYER4; if(strcmp(type, "CIVMALE") == 0) return PEDTYPE_CIVMALE; if(strcmp(type, "CIVFEMALE") == 0) return PEDTYPE_CIVFEMALE; if(strcmp(type, "COP") == 0) return PEDTYPE_COP; if(strcmp(type, "GANG1") == 0) return PEDTYPE_GANG1; if(strcmp(type, "GANG2") == 0) return PEDTYPE_GANG2; if(strcmp(type, "GANG3") == 0) return PEDTYPE_GANG3; if(strcmp(type, "GANG4") == 0) return PEDTYPE_GANG4; if(strcmp(type, "GANG5") == 0) return PEDTYPE_GANG5; if(strcmp(type, "GANG6") == 0) return PEDTYPE_GANG6; if(strcmp(type, "GANG7") == 0) return PEDTYPE_GANG7; if(strcmp(type, "GANG8") == 0) return PEDTYPE_GANG8; if(strcmp(type, "GANG9") == 0) return PEDTYPE_GANG9; if(strcmp(type, "EMERGENCY") == 0) return PEDTYPE_EMERGENCY; if(strcmp(type, "FIREMAN") == 0) return PEDTYPE_FIREMAN; if(strcmp(type, "CRIMINAL") == 0) return PEDTYPE_CRIMINAL; if(strcmp(type, "SPECIAL") == 0) return PEDTYPE_SPECIAL; if(strcmp(type, "PROSTITUTE") == 0) return PEDTYPE_PROSTITUTE; Error("Unknown ped type, Pedtype.cpp"); return NUM_PEDTYPES; } uint32 CPedType::FindPedFlag(char *type) { if(strcmp(type, "PLAYER1") == 0) return PED_FLAG_PLAYER1; if(strcmp(type, "PLAYER2") == 0) return PED_FLAG_PLAYER2; if(strcmp(type, "PLAYER3") == 0) return PED_FLAG_PLAYER3; if(strcmp(type, "PLAYER4") == 0) return PED_FLAG_PLAYER4; if(strcmp(type, "CIVMALE") == 0) return PED_FLAG_CIVMALE; if(strcmp(type, "CIVFEMALE") == 0) return PED_FLAG_CIVFEMALE; if(strcmp(type, "COP") == 0) return PED_FLAG_COP; if(strcmp(type, "GANG1") == 0) return PED_FLAG_GANG1; if(strcmp(type, "GANG2") == 0) return PED_FLAG_GANG2; if(strcmp(type, "GANG3") == 0) return PED_FLAG_GANG3; if(strcmp(type, "GANG4") == 0) return PED_FLAG_GANG4; if(strcmp(type, "GANG5") == 0) return PED_FLAG_GANG5; if(strcmp(type, "GANG6") == 0) return PED_FLAG_GANG6; if(strcmp(type, "GANG7") == 0) return PED_FLAG_GANG7; if(strcmp(type, "GANG8") == 0) return PED_FLAG_GANG8; if(strcmp(type, "GANG9") == 0) return PED_FLAG_GANG9; if(strcmp(type, "EMERGENCY") == 0) return PED_FLAG_EMERGENCY; if(strcmp(type, "FIREMAN") == 0) return PED_FLAG_FIREMAN; if(strcmp(type, "CRIMINAL") == 0) return PED_FLAG_CRIMINAL; if(strcmp(type, "SPECIAL") == 0) return PED_FLAG_SPECIAL; if(strcmp(type, "GUN") == 0) return PED_FLAG_GUN; if(strcmp(type, "COP_CAR") == 0) return PED_FLAG_COP_CAR; if(strcmp(type, "FAST_CAR") == 0) return PED_FLAG_FAST_CAR; if(strcmp(type, "EXPLOSION") == 0) return PED_FLAG_EXPLOSION; if(strcmp(type, "PROSTITUTE") == 0) return PED_FLAG_PROSTITUTE; if(strcmp(type, "DEADPEDS") == 0) return PED_FLAG_DEADPEDS; return 0; } void CPedType::Save(uint8 *buf, uint32 *size) { *size = sizeof(CPedType) * NUM_PEDTYPES + SAVE_HEADER_SIZE; INITSAVEBUF WriteSaveHeader(buf, 'P','T','P','\0', *size - SAVE_HEADER_SIZE); for(int i = 0; i < NUM_PEDTYPES; i++) WriteSaveBuf(buf, *ms_apPedType[i]); VALIDATESAVEBUF(*size) } void CPedType::Load(uint8 *buf, uint32 size) { INITSAVEBUF // original: SkipSaveBuf(buf, SAVE_HEADER_SIZE); CheckSaveHeader(buf, 'P', 'T', 'P', '\0', size - SAVE_HEADER_SIZE); for(int i = 0; i < NUM_PEDTYPES; i++) *ms_apPedType[i] = ReadSaveBuf(buf); VALIDATESAVEBUF(size) } void CPedStats::Initialise(void) { int i; debug("Initialising CPedStats...\n"); for(i = 0; i < NUM_PEDSTATS; i++){ ms_apPedStats[i] = new CPedStats; ms_apPedStats[i]->m_type = PEDSTAT_PLAYER; ms_apPedStats[i]->m_name[8] = 'R'; // WHAT? ms_apPedStats[i]->m_fleeDistance = 20.0f; ms_apPedStats[i]->m_headingChangeRate = 15.0f; ms_apPedStats[i]->m_fear = 50; ms_apPedStats[i]->m_temper = 50; ms_apPedStats[i]->m_lawfulness = 50; ms_apPedStats[i]->m_sexiness = 50; ms_apPedStats[i]->m_attackStrength = 1.0f; ms_apPedStats[i]->m_defendWeakness = 1.0f; ms_apPedStats[i]->m_flags = 0; } debug("Loading pedstats data...\n"); CPedStats::LoadPedStats(); debug("CPedStats ready\n"); } void CPedStats::Shutdown(void) { int i; debug("Shutting down CPedStats...\n"); for(i = 0; i < NUM_PEDSTATS; i++) delete ms_apPedStats[i]; debug("CPedStats shut down\n"); } void CPedStats::LoadPedStats(void) { char *buf; char line[256]; char name[32]; ssize_t bp, buflen; int lp, linelen; int type; float fleeDist, headingChangeRate, attackStrength, defendWeakness; int fear, temper, lawfullness, sexiness, flags; type = 0; buf = new char[16 * 1024]; CFileMgr::SetDir("DATA"); buflen = CFileMgr::LoadFile("PEDSTATS.DAT", (uint8*)buf, 16 * 1024, "r"); CFileMgr::SetDir(""); for(bp = 0; bp < buflen; ){ // read file line by line for(linelen = 0; buf[bp] != '\n' && bp < buflen; bp++){ if(buf[bp] == '\r' || buf[bp] == ',' || buf[bp] == '\t') line[linelen++] = ' '; else line[linelen++] = buf[bp]; } bp++; line[linelen] = '\0'; // skip white space for(lp = 0; line[lp] <= ' '; lp++); if(lp >= linelen || // FIX: game uses == here, but this is safer if we have empty lines line[lp] == '#') continue; sscanf(&line[lp], "%s %f %f %d %d %d %d %f %f %d", name, &fleeDist, &headingChangeRate, &fear, &temper, &lawfullness, &sexiness, &attackStrength, &defendWeakness, &flags); ms_apPedStats[type]->m_type = (ePedStats)type; strncpy(ms_apPedStats[type]->m_name, name, 24); // FIX: game uses strcpy ms_apPedStats[type]->m_fleeDistance = fleeDist; ms_apPedStats[type]->m_headingChangeRate = headingChangeRate; ms_apPedStats[type]->m_fear = fear; ms_apPedStats[type]->m_temper = temper; ms_apPedStats[type]->m_lawfulness = lawfullness; ms_apPedStats[type]->m_sexiness = sexiness; ms_apPedStats[type]->m_attackStrength = attackStrength; ms_apPedStats[type]->m_defendWeakness = defendWeakness; ms_apPedStats[type]->m_flags = flags; type++; } delete[] buf; } ePedStats CPedStats::GetPedStatType(char *name) { for(uint16 type = 0; type < NUM_PEDSTATS; type++) if(!CGeneral::faststrcmp(ms_apPedStats[type]->m_name, name)) return (ePedStats) type; return NUM_PEDSTATS; } ================================================ FILE: src/peds/PedType.h ================================================ #pragma once // Index into the PedType array enum ePedType { PEDTYPE_PLAYER1, PEDTYPE_PLAYER2, PEDTYPE_PLAYER3, PEDTYPE_PLAYER4, PEDTYPE_CIVMALE, PEDTYPE_CIVFEMALE, PEDTYPE_COP, PEDTYPE_GANG1, PEDTYPE_GANG2, PEDTYPE_GANG3, PEDTYPE_GANG4, PEDTYPE_GANG5, // Security - hardcoded PEDTYPE_GANG6, PEDTYPE_GANG7, // Vercetti gang - hardcoded PEDTYPE_GANG8, PEDTYPE_GANG9, PEDTYPE_EMERGENCY, PEDTYPE_FIREMAN, PEDTYPE_CRIMINAL, PEDTYPE_UNUSED1, PEDTYPE_PROSTITUTE, PEDTYPE_SPECIAL, PEDTYPE_UNUSED2, NUM_PEDTYPES }; enum { PED_FLAG_PLAYER1 = 1 << 0, PED_FLAG_PLAYER2 = 1 << 1, PED_FLAG_PLAYER3 = 1 << 2, PED_FLAG_PLAYER4 = 1 << 3, PED_FLAG_CIVMALE = 1 << 4, PED_FLAG_CIVFEMALE = 1 << 5, PED_FLAG_COP = 1 << 6, PED_FLAG_GANG1 = 1 << 7, PED_FLAG_GANG2 = 1 << 8, PED_FLAG_GANG3 = 1 << 9, PED_FLAG_GANG4 = 1 << 10, PED_FLAG_GANG5 = 1 << 11, PED_FLAG_GANG6 = 1 << 12, PED_FLAG_GANG7 = 1 << 13, PED_FLAG_GANG8 = 1 << 14, PED_FLAG_GANG9 = 1 << 15, PED_FLAG_EMERGENCY = 1 << 16, PED_FLAG_PROSTITUTE = 1 << 17, PED_FLAG_CRIMINAL = 1 << 18, PED_FLAG_SPECIAL = 1 << 19, PED_FLAG_GUN = 1 << 20, PED_FLAG_COP_CAR = 1 << 21, PED_FLAG_FAST_CAR = 1 << 22, PED_FLAG_EXPLOSION = 1 << 23, PED_FLAG_FIREMAN = 1 << 24, PED_FLAG_DEADPEDS = 1 << 25, }; class CPedType { uint32 m_flag; float unknown1; float unknown2; float unknown3; float unknown4; float unknown5; uint32 m_threats; uint32 m_avoid; static CPedType *ms_apPedType[NUM_PEDTYPES]; public: static void Initialise(void); static void Shutdown(void); static void LoadPedData(void); static ePedType FindPedType(char *type); static uint32 FindPedFlag(char *type); static void Save(uint8 *buf, uint32 *size); static void Load(uint8 *buf, uint32 size); static uint32 GetFlag(int type) { return ms_apPedType[type]->m_flag; } static uint32 GetAvoid(int type) { return ms_apPedType[type]->m_avoid; } static uint32 GetThreats(int type) { return ms_apPedType[type]->m_threats; } static void SetThreats(int type, uint32 threat) { ms_apPedType[type]->m_threats = threat; } static void AddThreat(int type, int threat) { ms_apPedType[type]->m_threats |= threat; } static void RemoveThreat(int type, int threat) { ms_apPedType[type]->m_threats &= ~threat; } static bool IsThreat(int type, int threat) { return ms_apPedType[type]->m_threats & threat; } }; VALIDATE_SIZE(CPedType, 0x20); enum ePedStats { PEDSTAT_PLAYER, PEDSTAT_COP, PEDSTAT_MEDIC, PEDSTAT_FIREMAN, PEDSTAT_GANG1, PEDSTAT_GANG2, PEDSTAT_GANG3, PEDSTAT_GANG4, PEDSTAT_GANG5, PEDSTAT_GANG6, PEDSTAT_GANG7, PEDSTAT_STREET_GUY, PEDSTAT_SUIT_GUY, PEDSTAT_SENSIBLE_GUY, PEDSTAT_GEEK_GUY, PEDSTAT_OLD_GUY, PEDSTAT_TOUGH_GUY, PEDSTAT_STREET_GIRL, PEDSTAT_SUIT_GIRL, PEDSTAT_SENSIBLE_GIRL, PEDSTAT_GEEK_GIRL, PEDSTAT_OLD_GIRL, PEDSTAT_TOUGH_GIRL, PEDSTAT_TRAMP_MALE, PEDSTAT_TRAMP_FEMALE, PEDSTAT_TOURIST, PEDSTAT_PROSTITUTE, PEDSTAT_CRIMINAL, PEDSTAT_BUSKER, PEDSTAT_TAXIDRIVER, PEDSTAT_PSYCHO, PEDSTAT_STEWARD, PEDSTAT_SPORTSFAN, PEDSTAT_SHOPPER, PEDSTAT_OLDSHOPPER, PEDSTAT_BEACH_GUY, PEDSTAT_BEACH_GIRL, PEDSTAT_SKATER, PEDSTAT_STD_MISSION, PEDSTAT_COWARD, NUM_PEDSTATS }; // flags enum { STAT_PUNCH_ONLY = 1, STAT_CAN_KNEE_HEAD = 2, STAT_CAN_KICK = 4, STAT_CAN_ROUNDHOUSE = 8, STAT_NO_DIVE = 0x10, STAT_ONE_HIT_KNOCKDOWN = 0x20, STAT_SHOPPING_BAGS = 0x40, STAT_GUN_PANIC = 0x80 }; class CPedStats { public: ePedStats m_type; char m_name[24]; float m_fleeDistance; float m_headingChangeRate; int8 m_fear; int8 m_temper; int8 m_lawfulness; int8 m_sexiness; float m_attackStrength; float m_defendWeakness; int16 m_flags; static CPedStats *ms_apPedStats[NUM_PEDSTATS]; static void Initialise(void); static void Shutdown(void); static void LoadPedStats(void); static ePedStats GetPedStatType(char *name); }; VALIDATE_SIZE(CPedStats, 0x34); ================================================ FILE: src/peds/PlayerPed.cpp ================================================ #include "common.h" #include "RwHelper.h" #include "PlayerPed.h" #include "Wanted.h" #include "Fire.h" #include "DMAudio.h" #include "Pad.h" #include "Camera.h" #include "WeaponEffects.h" #include "ModelIndices.h" #include "World.h" #include "RpAnimBlend.h" #include "AnimBlendAssociation.h" #include "General.h" #include "Pools.h" #include "Darkel.h" #include "CarCtrl.h" #include "MBlur.h" #include "Streaming.h" #include "Population.h" #include "Script.h" #include "Replay.h" #include "PedPlacement.h" #include "VarConsole.h" #define PAD_MOVE_TO_GAME_WORLD_MOVE 60.0f bool CPlayerPed::bDontAllowWeaponChange; #ifndef MASTER bool CPlayerPed::bDebugPlayerInfo; #endif const uint32 CPlayerPed::nSaveStructSize = #ifdef COMPATIBLE_SAVES 1752; #else sizeof(CPlayerPed); #endif int32 idleAnimBlockIndex; CPad* GetPadFromPlayer(CPlayerPed*) { return CPad::GetPad(0); } CPlayerPed::~CPlayerPed() { delete m_pWanted; } CPlayerPed::CPlayerPed(void) : CPed(PEDTYPE_PLAYER1) { m_fMoveSpeed = 0.0f; SetModelIndex(MI_PLAYER); #ifdef FIX_BUGS m_fCurrentStamina = m_fMaxStamina = 150.0f; #endif SetInitialState(); m_pWanted = new CWanted(); m_pWanted->Initialise(); m_pArrestingCop = nil; m_currentWeapon = WEAPONTYPE_UNARMED; m_nSelectedWepSlot = WEAPONSLOT_UNARMED; m_nSpeedTimer = 0; m_bSpeedTimerFlag = false; SetWeaponLockOnTarget(nil); SetPedState(PED_IDLE); #ifndef FIX_BUGS m_fCurrentStamina = m_fMaxStamina = 150.0f; #endif m_fStaminaProgress = 0.0f; m_nEvadeAmount = 0; m_pEvadingFrom = nil; m_nHitAnimDelayTimer = 0; m_fAttackButtonCounter = 0.0f; m_bHaveTargetSelected = false; m_bHasLockOnTarget = false; m_bCanBeDamaged = true; m_bNoPosForMeleeAttack = false; m_fWalkAngle = 0.0f; m_fFPSMoveHeading = 0.0f; m_pMinigunTopAtomic = nil; m_fGunSpinSpeed = 0.0; m_fGunSpinAngle = 0.0; m_nPadDownPressedInMilliseconds = 0; m_nTargettableObjects[0] = m_nTargettableObjects[1] = m_nTargettableObjects[2] = m_nTargettableObjects[3] = -1; unk1 = false; for (int i = 0; i < 6; i++) { m_vecSafePos[i] = CVector(0.0f, 0.0f, 0.0f); m_pPedAtSafePos[i] = nil; m_pMeleeList[i] = nil; } m_nAttackDirToCheck = 0; m_nLastBusFareCollected = 0; idleAnimBlockIndex = CAnimManager::GetAnimationBlockIndex("playidles"); } void CPlayerPed::ClearWeaponTarget() { if (m_nPedType == PEDTYPE_PLAYER1) { SetWeaponLockOnTarget(nil); TheCamera.ClearPlayerWeaponMode(); CWeaponEffects::ClearCrossHair(); } ClearPointGunAt(); } void CPlayerPed::SetWantedLevel(int32 level) { m_pWanted->SetWantedLevel(level); } void CPlayerPed::SetWantedLevelNoDrop(int32 level) { m_pWanted->SetWantedLevelNoDrop(level); } void CPlayerPed::MakeObjectTargettable(int32 handle) { for (int i = 0; i < ARRAY_SIZE(m_nTargettableObjects); i++) { if (CPools::GetObjectPool()->GetAt(m_nTargettableObjects[i]) == nil) { m_nTargettableObjects[i] = handle; return; } } } // 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 && m_pedStats->m_temper < 55) { m_pedStats->m_temper++; } else if (annoyedByPassingEntity) { m_pedStats->m_temper = 46; } } void CPlayerPed::ClearAdrenaline(void) { if (m_bAdrenalineActive && m_nAdrenalineTime != 0) { m_nAdrenalineTime = 0; CTimer::SetTimeScale(1.0f); } } CPlayerInfo * CPlayerPed::GetPlayerInfoForThisPlayerPed() { if (CWorld::Players[0].m_pPed == this) return &CWorld::Players[0]; return nil; } void CPlayerPed::SetupPlayerPed(int32 index) { CPlayerPed *player = new CPlayerPed(); CWorld::Players[index].m_pPed = player; #ifdef FIX_BUGS player->RegisterReference((CEntity**)&CWorld::Players[index].m_pPed); #endif player->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(player); player->m_wepAccuracy = 100; #ifndef MASTER VarConsole.Add("Debug PlayerPed", &CPlayerPed::bDebugPlayerInfo, true); VarConsole.Add("Tweak Vehicle Handling", &CVehicle::m_bDisplayHandlingInfo, true); #endif } void CPlayerPed::DeactivatePlayerPed(int32 index) { CWorld::Remove(CWorld::Players[index].m_pPed); } void CPlayerPed::ReactivatePlayerPed(int32 index) { CWorld::Add(CWorld::Players[index].m_pPed); } void CPlayerPed::UseSprintEnergy(void) { if (m_fCurrentStamina > -150.0f && !CWorld::Players[CWorld::PlayerInFocus].m_bInfiniteSprint && !m_bAdrenalineActive) { m_fCurrentStamina = m_fCurrentStamina - CTimer::GetTimeStep(); m_fStaminaProgress = m_fStaminaProgress + CTimer::GetTimeStep(); } if (m_fStaminaProgress >= 500.0f) { m_fStaminaProgress = 0; if (m_fMaxStamina < 1000.0f) m_fMaxStamina += 10.0f; } } void CPlayerPed::MakeChangesForNewWeapon(eWeaponType weapon) { if (m_nPedState == PED_SNIPER_MODE) { RestorePreviousState(); TheCamera.ClearPlayerWeaponMode(); } SetCurrentWeapon(weapon); m_nSelectedWepSlot = m_currentWeapon; GetWeapon()->m_nAmmoInClip = Min(GetWeapon()->m_nAmmoTotal, CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nAmountofAmmunition); if (CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM)) ClearWeaponTarget(); // WEAPONTYPE_SNIPERRIFLE? Wut? CAnimBlendAssociation* weaponAnim = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_SNIPERRIFLE))); if (weaponAnim) { weaponAnim->SetRun(); weaponAnim->flags |= ASSOC_FADEOUTWHENDONE; } TheCamera.ClearPlayerWeaponMode(); } void CPlayerPed::MakeChangesForNewWeapon(int32 slot) { if(slot != -1) MakeChangesForNewWeapon(m_weapons[slot].m_eWeaponType); } void CPlayerPed::ReApplyMoveAnims(void) { static AnimationId moveAnims[] = { ANIM_STD_WALK, ANIM_STD_RUN, ANIM_STD_RUNFAST, ANIM_STD_IDLE, ANIM_STD_STARTWALK }; for(int i = 0; i < ARRAY_SIZE(moveAnims); i++) { CAnimBlendAssociation *curMoveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), moveAnims[i]); if (curMoveAssoc) { if (CGeneral::faststrcmp(CAnimManager::GetAnimAssociation(m_animGroup, moveAnims[i])->hierarchy->name, curMoveAssoc->hierarchy->name)) { CAnimBlendAssociation *newMoveAssoc = CAnimManager::AddAnimation(GetClump(), m_animGroup, moveAnims[i]); newMoveAssoc->blendDelta = curMoveAssoc->blendDelta; newMoveAssoc->blendAmount = curMoveAssoc->blendAmount; curMoveAssoc->blendDelta = -1000.0f; curMoveAssoc->flags |= ASSOC_DELETEFADEDOUT; } } } } void CPlayerPed::SetInitialState(void) { m_nDrunkenness = 0; m_nFadeDrunkenness = 0; CMBlur::ClearDrunkBlur(); m_nDrunkCountdown = 0; m_bAdrenalineActive = false; m_nAdrenalineTime = 0; CTimer::SetTimeScale(1.0f); m_pSeekTarget = nil; m_vecSeekPos = CVector(0.0f, 0.0f, 0.0f); m_fleeFromPos = CVector2D(0.0f, 0.0f); m_fleeFrom = nil; m_fleeTimer = 0; m_objective = OBJECTIVE_NONE; m_prevObjective = OBJECTIVE_NONE; bUsesCollision = true; ClearAimFlag(); ClearLookFlag(); bIsPointingGunAt = false; bRenderPedInCar = true; if (m_pFire) m_pFire->Extinguish(); RpAnimBlendClumpRemoveAllAssociations(GetClump()); SetPedState(PED_IDLE); SetMoveState(PEDMOVE_STILL); m_nLastPedState = PED_NONE; m_animGroup = ASSOCGRP_PLAYER; m_fMoveSpeed = 0.0f; m_nSelectedWepSlot = WEAPONSLOT_UNARMED; m_nEvadeAmount = 0; m_pEvadingFrom = nil; bIsPedDieAnimPlaying = false; SetRealMoveAnim(); m_bCanBeDamaged = true; m_pedStats->m_temper = 50; m_fWalkAngle = 0.0f; if (m_attachedTo && !bUsesCollision) bUsesCollision = true; m_attachedTo = nil; m_attachWepAmmo = 0; } void CPlayerPed::SetRealMoveAnim(void) { CAnimBlendAssociation *curWalkAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_WALK); CAnimBlendAssociation *curRunAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUN); CAnimBlendAssociation *curSprintAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNFAST); CAnimBlendAssociation *curWalkStartAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_STARTWALK); CAnimBlendAssociation *curIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); CAnimBlendAssociation *curRunStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP1); CAnimBlendAssociation *curRunStopRAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP2); if (bResetWalkAnims) { if (curWalkAssoc) curWalkAssoc->SetCurrentTime(0.0f); if (curRunAssoc) curRunAssoc->SetCurrentTime(0.0f); if (curSprintAssoc) curSprintAssoc->SetCurrentTime(0.0f); bResetWalkAnims = false; } if (!curIdleAssoc) curIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); if (!curIdleAssoc) curIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); if (!curIdleAssoc) curIdleAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); if (!((curRunStopAssoc && curRunStopAssoc->IsRunning()) || (curRunStopRAssoc && curRunStopRAssoc->IsRunning()))) { if (curRunStopAssoc && curRunStopAssoc->blendDelta >= 0.0f || curRunStopRAssoc && curRunStopRAssoc->blendDelta >= 0.0f) { if (curRunStopAssoc) { curRunStopAssoc->flags |= ASSOC_DELETEFADEDOUT; curRunStopAssoc->blendAmount = 1.0f; curRunStopAssoc->blendDelta = -8.0f; } else if (curRunStopRAssoc) { curRunStopRAssoc->flags |= ASSOC_DELETEFADEDOUT; curRunStopRAssoc->blendAmount = 1.0f; curRunStopRAssoc->blendDelta = -8.0f; } RestoreHeadingRate(); if (!curIdleAssoc) { if (m_fCurrentStamina < 0.0f && !bIsAimingGun && !CWorld::TestSphereAgainstWorld(GetPosition(), 0.5f, nil, true, false, false, false, false, false)) { curIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 8.0f); } else { curIdleAssoc = CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); } m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2500, 4000); } curIdleAssoc->blendAmount = 0.0f; curIdleAssoc->blendDelta = 8.0f; } else if (m_fMoveSpeed == 0.0f && !curSprintAssoc) { if (!curIdleAssoc) { if (m_fCurrentStamina < 0.0f && !bIsAimingGun && !CWorld::TestSphereAgainstWorld(GetPosition(), 0.5f, nil, true, false, false, false, false, false)) { curIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 4.0f); } else { curIdleAssoc = CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); } m_nWaitTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(2500, 4000); } if ((m_fCurrentStamina > 0.0f || bIsAimingGun) && curIdleAssoc->animId == ANIM_STD_IDLE_TIRED) { CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); } else if (m_nPedState != PED_FIGHT) { if (m_fCurrentStamina < 0.0f && !bIsAimingGun && curIdleAssoc->animId != ANIM_STD_IDLE_TIRED && !CWorld::TestSphereAgainstWorld(GetPosition(), 0.5f, nil, true, false, false, false, false, false)) { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_TIRED, 4.0f); } else if (curIdleAssoc->animId != ANIM_STD_IDLE) { CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); } } m_nMoveState = PEDMOVE_STILL; } else { if (curIdleAssoc) { if (curWalkStartAssoc) { curWalkStartAssoc->blendAmount = 1.0f; curWalkStartAssoc->blendDelta = 0.0f; } else { curWalkStartAssoc = CAnimManager::AddAnimation(GetClump(), m_animGroup, ANIM_STD_STARTWALK); } if (curWalkAssoc) curWalkAssoc->SetCurrentTime(0.0f); if (curRunAssoc) curRunAssoc->SetCurrentTime(0.0f); delete curIdleAssoc; delete RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_TIRED); CAnimBlendAssociation *fightIdleAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); if (!fightIdleAnim) fightIdleAnim = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE); delete fightIdleAnim; delete curSprintAssoc; curSprintAssoc = nil; m_nMoveState = PEDMOVE_WALK; } if (curRunStopAssoc) { delete curRunStopAssoc; RestoreHeadingRate(); } if (curRunStopRAssoc) { delete curRunStopRAssoc; RestoreHeadingRate(); } if (!curWalkAssoc) { curWalkAssoc = CAnimManager::AddAnimation(GetClump(), m_animGroup, ANIM_STD_WALK); curWalkAssoc->blendAmount = 0.0f; } if (!curRunAssoc) { curRunAssoc = CAnimManager::AddAnimation(GetClump(), m_animGroup, ANIM_STD_RUN); curRunAssoc->blendAmount = 0.0f; } if (curWalkStartAssoc && !(curWalkStartAssoc->IsRunning())) { delete curWalkStartAssoc; curWalkStartAssoc = nil; curWalkAssoc->SetRun(); curRunAssoc->SetRun(); } if (m_nMoveState == PEDMOVE_SPRINT) { if (m_fCurrentStamina < 0.0f && (m_fCurrentStamina <= -150.0f || !curSprintAssoc || curSprintAssoc->blendDelta < 0.0f)) m_nMoveState = PEDMOVE_STILL; if (curWalkStartAssoc) m_nMoveState = PEDMOVE_STILL; } if (curSprintAssoc && (m_nMoveState != PEDMOVE_SPRINT || m_fMoveSpeed < 0.4f)) { // Stop sprinting in various conditions if (curSprintAssoc->blendAmount == 0.0f) { curSprintAssoc->blendDelta = -1000.0f; curSprintAssoc->flags |= ASSOC_DELETEFADEDOUT; } else if (curSprintAssoc->blendDelta >= 0.0f || curSprintAssoc->blendAmount >= 0.8f) { if (m_fMoveSpeed < 0.4f) { AnimationId runStopAnim; if (curSprintAssoc->GetProgress() < 0.5) // double runStopAnim = ANIM_STD_RUNSTOP1; else runStopAnim = ANIM_STD_RUNSTOP2; CAnimBlendAssociation* newRunStopAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, runStopAnim); newRunStopAssoc->blendAmount = 1.0f; newRunStopAssoc->SetDeleteCallback(RestoreHeadingRateCB, this); m_headingRate = 0.0f; curSprintAssoc->flags |= ASSOC_DELETEFADEDOUT; curSprintAssoc->blendDelta = -1000.0f; curWalkAssoc->flags &= ~ASSOC_RUNNING; curWalkAssoc->blendAmount = 0.0f; curWalkAssoc->blendDelta = 0.0f; curRunAssoc->flags &= ~ASSOC_RUNNING; curRunAssoc->blendAmount = 0.0f; curRunAssoc->blendDelta = 0.0f; } else if (curSprintAssoc->blendDelta >= 0.0f) { // this condition is absent on mobile // Stop sprinting when tired curSprintAssoc->flags |= ASSOC_DELETEFADEDOUT; curSprintAssoc->blendDelta = -1.0f; curRunAssoc->blendDelta = 1.0f; } } else if (m_fMoveSpeed < 1.0f) { curSprintAssoc->blendDelta = -8.0f; curRunAssoc->blendDelta = 8.0f; } } else if (curWalkStartAssoc) { // Walk start and walk/run shouldn't run at the same time curWalkAssoc->flags &= ~ASSOC_RUNNING; curRunAssoc->flags &= ~ASSOC_RUNNING; curWalkAssoc->blendAmount = 0.0f; curRunAssoc->blendAmount = 0.0f; } else if (m_nMoveState == PEDMOVE_SPRINT) { if (curSprintAssoc) { // We have anim, do it if (curSprintAssoc->blendDelta < 0.0f) { curSprintAssoc->blendDelta = 2.0f; curRunAssoc->blendDelta = -2.0f; } } else { // Transition between run-sprint curWalkAssoc->blendAmount = 0.0f; curRunAssoc->blendAmount = 1.0f; curSprintAssoc = CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_RUNFAST, 2.0f); } UseSprintEnergy(); } else { if (m_fMoveSpeed < 1.0f) { curWalkAssoc->blendAmount = 1.0f; curRunAssoc->blendAmount = 0.0f; m_nMoveState = PEDMOVE_WALK; } else if (m_fMoveSpeed < 2.0f) { curWalkAssoc->blendAmount = 2.0f - m_fMoveSpeed; curRunAssoc->blendAmount = m_fMoveSpeed - 1.0f; m_nMoveState = PEDMOVE_RUN; } else { curWalkAssoc->blendAmount = 0.0f; curRunAssoc->blendAmount = 1.0f; m_nMoveState = PEDMOVE_RUN; } curWalkAssoc->blendDelta = 0.0f; curRunAssoc->blendDelta = 0.0f; } } } if (m_bAdrenalineActive) { if (CTimer::GetTimeInMilliseconds() > m_nAdrenalineTime) { m_bAdrenalineActive = false; CTimer::SetTimeScale(1.0f); if (curWalkStartAssoc) curWalkStartAssoc->speed = 1.0f; if (curWalkAssoc) curWalkAssoc->speed = 1.0f; if (curRunAssoc) curRunAssoc->speed = 1.0f; if (curSprintAssoc) curSprintAssoc->speed = 1.0f; } else { CTimer::SetTimeScale(1.0f / 3); if (curWalkStartAssoc) curWalkStartAssoc->speed = 2.0f; if (curWalkAssoc) curWalkAssoc->speed = 2.0f; if (curRunAssoc) curRunAssoc->speed = 2.0f; if (curSprintAssoc) curSprintAssoc->speed = 2.0f; } } else if (curSprintAssoc) { if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIXED) { curSprintAssoc->speed = 0.7f; } else curSprintAssoc->speed = 1.0f; } } void CPlayerPed::RestoreSprintEnergy(float restoreSpeed) { if (m_fCurrentStamina < m_fMaxStamina) m_fCurrentStamina += restoreSpeed * CTimer::GetTimeStep() * 0.5f; } float CPlayerPed::DoWeaponSmoothSpray(void) { if (m_nPedState == PED_ATTACK && !m_pPointGunAt) { CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); switch (GetWeapon()->m_eWeaponType) { case WEAPONTYPE_GOLFCLUB: case WEAPONTYPE_NIGHTSTICK: case WEAPONTYPE_BASEBALLBAT: if (GetFireAnimGround(weaponInfo, false) && RpAnimBlendClumpGetAssociation(GetClump(), GetFireAnimGround(weaponInfo, false))) return PI / 176.f; else return -1.0f; case WEAPONTYPE_CHAINSAW: if (GetMeleeStartAnim(weaponInfo) && RpAnimBlendClumpGetAssociation(GetClump(), GetMeleeStartAnim(weaponInfo))) return PI / 128.0f; else if (GetFireAnimGround(weaponInfo, false) && RpAnimBlendClumpGetAssociation(GetClump(), GetFireAnimGround(weaponInfo, false))) return PI / 176.f; else return PI / 80.f; case WEAPONTYPE_PYTHON: return PI / 112.f; case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_SPAS12_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: return PI / 112.f; case WEAPONTYPE_UZI: case WEAPONTYPE_MP5: return PI / 112.f; case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: return PI / 112.f; case WEAPONTYPE_FLAMETHROWER: return PI / 80.f; case WEAPONTYPE_M60: case WEAPONTYPE_MINIGUN: case WEAPONTYPE_HELICANNON: return PI / 176.f; default: return -1.0f; } } else if (bIsDucking) return PI / 112.f; else return -1.0f; } void CPlayerPed::DoStuffToGoOnFire(void) { if (m_nPedState == PED_SNIPER_MODE) TheCamera.ClearPlayerWeaponMode(); } bool CPlayerPed::DoesTargetHaveToBeBroken(CVector target, CWeapon *weaponUsed) { CVector distVec = target - GetPosition(); if (distVec.Magnitude() > CWeaponInfo::GetWeaponInfo(weaponUsed->m_eWeaponType)->m_fRange) return true; return false; } // Cancels landing anim while running & jumping? I think void CPlayerPed::RunningLand(CPad *padUsed) { CAnimBlendAssociation *landAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FALL_LAND); if (landAssoc && landAssoc->currentTime == 0.0f && m_fMoveSpeed > 1.5f && padUsed && (padUsed->GetPedWalkLeftRight() != 0.0f || padUsed->GetPedWalkUpDown() != 0.0f)) { landAssoc->blendDelta = -1000.0f; landAssoc->flags |= ASSOC_DELETEFADEDOUT; CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_JUMP_LAND)->SetFinishCallback(FinishJumpCB, this); if (m_nPedState == PED_JUMP) RestorePreviousState(); } } bool CPlayerPed::IsThisPedAnAimingPriority(CPed *suspect) { if (!suspect->bIsPlayerFriend) return true; if (suspect->m_pPointGunAt == this) return true; switch (suspect->m_objective) { case OBJECTIVE_KILL_CHAR_ON_FOOT: case OBJECTIVE_KILL_CHAR_ANY_MEANS: if (suspect->m_pedInObjective == this) return true; break; default: break; } return suspect->m_nPedState == PED_ABSEIL; } void CPlayerPed::PlayerControlSniper(CPad *padUsed) { ProcessWeaponSwitch(padUsed); TheCamera.PlayerExhaustion = (1.0f - (m_fCurrentStamina - -150.0f) / 300.0f) * 0.9f + 0.1f; if (padUsed->DuckJustDown() && !bIsDucking && m_nMoveState != PEDMOVE_SPRINT) { bCrouchWhenShooting = true; SetDuck(60000, true); } else if (bIsDucking && (padUsed->DuckJustDown() || m_nMoveState == PEDMOVE_SPRINT)) { ClearDuck(true); bCrouchWhenShooting = false; } if (!padUsed->GetTarget() && !m_attachedTo) { RestorePreviousState(); TheCamera.ClearPlayerWeaponMode(); return; } int firingRate = GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE ? 333 : 266; if (padUsed->WeaponJustDown() && CTimer::GetTimeInMilliseconds() > GetWeapon()->m_nTimer) { CVector firePos(0.0f, 0.0f, 0.6f); firePos = GetMatrix() * firePos; GetWeapon()->Fire(this, &firePos); m_nPadDownPressedInMilliseconds = CTimer::GetTimeInMilliseconds(); } else if (CTimer::GetTimeInMilliseconds() > m_nPadDownPressedInMilliseconds + firingRate && CTimer::GetTimeInMilliseconds() - CTimer::GetTimeStepInMilliseconds() < m_nPadDownPressedInMilliseconds + firingRate && padUsed->GetWeapon()) { if (GetWeapon()->m_nAmmoTotal > 0) { DMAudio.PlayFrontEndSound(SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType); } } GetWeapon()->Update(m_audioEntityId, nil); } // I think R* also used goto in here. void CPlayerPed::ProcessWeaponSwitch(CPad *padUsed) { if (CDarkel::FrenzyOnGoing() || m_attachedTo) goto switchDetectDone; if (!m_pPointGunAt && !bDontAllowWeaponChange && GetWeapon()->m_eWeaponType != WEAPONTYPE_DETONATOR) { if (padUsed->CycleWeaponRightJustDown()) { if (TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON_RUNABOUT && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER_RUNABOUT && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_ROCKETLAUNCHER && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_ROCKETLAUNCHER_RUNABOUT && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_CAMERA) { for (m_nSelectedWepSlot = m_currentWeapon + 1; m_nSelectedWepSlot < TOTAL_WEAPON_SLOTS; ++m_nSelectedWepSlot) { if (HasWeaponSlot(m_nSelectedWepSlot) && GetWeapon(m_nSelectedWepSlot).HasWeaponAmmoToBeUsed()) { goto spentAmmoCheck; } } m_nSelectedWepSlot = 0; } } else if (padUsed->CycleWeaponLeftJustDown()) { if (TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_ROCKETLAUNCHER && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_CAMERA) { // I don't know what kind of loop that was m_nSelectedWepSlot = m_currentWeapon - 1; do { if (m_nSelectedWepSlot < 0) m_nSelectedWepSlot = TOTAL_WEAPON_SLOTS - 1; if (m_nSelectedWepSlot == WEAPONSLOT_UNARMED) break; if (HasWeaponSlot(m_nSelectedWepSlot) && GetWeapon(m_nSelectedWepSlot).HasWeaponAmmoToBeUsed()) break; --m_nSelectedWepSlot; } while (m_nSelectedWepSlot != WEAPONSLOT_UNARMED); } } } spentAmmoCheck: if (CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_eWeaponFire != WEAPON_FIRE_MELEE && (!padUsed->GetWeapon() || GetWeapon()->m_eWeaponType != WEAPONTYPE_MINIGUN)) { if (GetWeapon()->m_nAmmoTotal <= 0) { if (TheCamera.PlayerWeaponMode.Mode == CCam::MODE_M16_1STPERSON || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_SNIPER || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_ROCKETLAUNCHER) return; if (GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR && GetWeapon(WEAPONSLOT_PROJECTILE).m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) m_nSelectedWepSlot = WEAPONSLOT_PROJECTILE; else m_nSelectedWepSlot = m_currentWeapon - 1; for (; m_nSelectedWepSlot >= WEAPONSLOT_UNARMED; --m_nSelectedWepSlot) { // BUG: m_nSelectedWepSlot and GetWeapon(..) takes slot in VC but they compared them against weapon types in whole condition! jeez #ifdef FIX_BUGS if (m_nSelectedWepSlot == WEAPONSLOT_MELEE || GetWeapon(m_nSelectedWepSlot).m_nAmmoTotal > 0 && m_nSelectedWepSlot != WEAPONSLOT_PROJECTILE) { #else if (m_nSelectedWepSlot == WEAPONTYPE_BASEBALLBAT && GetWeapon(WEAPONTYPE_BASEBALLBAT).m_eWeaponType == WEAPONTYPE_BASEBALLBAT || GetWeapon(m_nSelectedWepSlot).m_nAmmoTotal > 0 && m_nSelectedWepSlot != WEAPONTYPE_MOLOTOV && m_nSelectedWepSlot != WEAPONTYPE_GRENADE && m_nSelectedWepSlot != WEAPONTYPE_TEARGAS) { #endif goto switchDetectDone; } } m_nSelectedWepSlot = WEAPONSLOT_UNARMED; } } switchDetectDone: if (m_nSelectedWepSlot != m_currentWeapon) { if (m_nPedState != PED_ATTACK && m_nPedState != PED_AIM_GUN && m_nPedState != PED_FIGHT) { RemoveWeaponAnims(m_currentWeapon, -1000.0f); MakeChangesForNewWeapon(m_nSelectedWepSlot); } } } void CPlayerPed::PlayerControlM16(CPad *padUsed) { ProcessWeaponSwitch(padUsed); TheCamera.PlayerExhaustion = (1.0f - (m_fCurrentStamina - -150.0f) / 300.0f) * 0.9f + 0.1f; if (padUsed->DuckJustDown() && !bIsDucking && m_nMoveState != PEDMOVE_SPRINT) { bCrouchWhenShooting = true; SetDuck(60000, true); } else if (bIsDucking && (padUsed->DuckJustDown() || m_nMoveState == PEDMOVE_SPRINT)) { ClearDuck(true); bCrouchWhenShooting = false; } if (!padUsed->GetTarget() && !m_attachedTo) { RestorePreviousState(); TheCamera.ClearPlayerWeaponMode(); } if (padUsed->GetWeapon() && CTimer::GetTimeInMilliseconds() > GetWeapon()->m_nTimer) { if (GetWeapon()->m_eWeaponState == WEAPONSTATE_OUT_OF_AMMO) { DMAudio.PlayFrontEndSound(SOUND_WEAPON_SNIPER_SHOT_NO_ZOOM, 0.f); GetWeapon()->m_nTimer = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nFiringRate + CTimer::GetTimeInMilliseconds(); } else { CVector firePos(0.0f, 0.0f, 0.6f); firePos = GetMatrix() * firePos; GetWeapon()->Fire(this, &firePos); m_nPadDownPressedInMilliseconds = CTimer::GetTimeInMilliseconds(); } } else if (CTimer::GetTimeInMilliseconds() > GetWeapon()->m_nTimer && CTimer::GetTimeInMilliseconds() - CTimer::GetTimeStepInMilliseconds() < GetWeapon()->m_nTimer && GetWeapon()->m_eWeaponState != WEAPONSTATE_OUT_OF_AMMO) { DMAudio.PlayFrontEndSound(SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType); } GetWeapon()->Update(m_audioEntityId, nil); } void CPlayerPed::PlayerControlFighter(CPad *padUsed) { float leftRight = padUsed->GetPedWalkLeftRight(); float upDown = padUsed->GetPedWalkUpDown(); float padMove = CVector2D(leftRight, upDown).Magnitude(); if (padMove > 0.0f) { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown) - TheCamera.Orientation; m_takeAStepAfterAttack = padMove > 2 * PAD_MOVE_TO_GAME_WORLD_MOVE; if (padUsed->GetSprint() && padMove > 1 * PAD_MOVE_TO_GAME_WORLD_MOVE) bIsAttacking = false; } if (!CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY) && padUsed->JumpJustDown()) { if (m_nEvadeAmount != 0 && m_pEvadingFrom) { SetEvasiveDive((CPhysical*)m_pEvadingFrom, 1); m_nEvadeAmount = 0; m_pEvadingFrom = nil; } else { SetJump(); } } } void CPlayerPed::PlayerControl1stPersonRunAround(CPad *padUsed) { float leftRight = padUsed->GetPedWalkLeftRight(); float upDown = padUsed->GetPedWalkUpDown(); float padMove = CVector2D(leftRight, upDown).Magnitude(); float padMoveInGameUnit = padMove / PAD_MOVE_TO_GAME_WORLD_MOVE; if (padMoveInGameUnit > 0.0f) { m_fRotationDest = CGeneral::LimitRadianAngle(TheCamera.Orientation); m_fMoveSpeed = Min(padMoveInGameUnit, 0.07f * CTimer::GetTimeStep() + m_fMoveSpeed); } else { m_fMoveSpeed = 0.0f; } if (m_nPedState == PED_JUMP) { if (bIsInTheAir) { if (bUsesCollision && !bHitSteepSlope && (!bHitSomethingLastFrame || m_vecDamageNormal.z > 0.6f) && m_fDistanceTravelled < CTimer::GetTimeStep() * 0.02 && m_vecMoveSpeed.MagnitudeSqr() < 0.01f) { float angleSin = Sin(m_fRotationCur); // originally sin(DEGTORAD(RADTODEG(m_fRotationCur))) o_O float angleCos = Cos(m_fRotationCur); ApplyMoveForce(-angleSin * 3.0f, 3.0f * angleCos, 0.05f); } } else if (bIsLanding) { m_fMoveSpeed = 0.0f; } } if (m_nPedState == PED_ANSWER_MOBILE) { SetRealMoveAnim(); return; } if (!CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY) && padUsed->GetSprint()) { m_nMoveState = PEDMOVE_SPRINT; } if (m_nPedState != PED_FIGHT) SetRealMoveAnim(); if (!bIsInTheAir && !(CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY)) && padUsed->JumpJustDown() && m_nPedState != PED_JUMP) { ClearAttack(); ClearWeaponTarget(); if (m_nEvadeAmount != 0 && m_pEvadingFrom) { SetEvasiveDive((CPhysical*)m_pEvadingFrom, 1); m_nEvadeAmount = 0; m_pEvadingFrom = nil; } else { SetJump(); } } // FIX: Fact that PlayIdleAnimations only called through PlayerControlZelda was making it visible to only Classic control players. This isn't fair! #ifdef FIX_BUGS if (m_nPedState != PED_FIGHT) PlayIdleAnimations(padUsed); #endif } void CPlayerPed::KeepAreaAroundPlayerClear(void) { BuildPedLists(); for (int i = 0; i < m_numNearPeds; ++i) { CPed *nearPed = m_nearPeds[i]; if (nearPed->CharCreatedBy == RANDOM_CHAR && nearPed->m_nPedState != PED_DRIVING && !nearPed->DyingOrDead()) { if (nearPed->GetIsOnScreen()) { if (nearPed->m_objective == OBJECTIVE_NONE) { nearPed->SetFindPathAndFlee(this, 5000, true); } else { if (nearPed->EnteringCar()) nearPed->QuitEnteringCar(); nearPed->ClearObjective(); } } else { nearPed->FlagToDestroyWhenNextProcessed(); } } } CVector playerPos = (InVehicle() ? m_pMyVehicle->GetPosition() : GetPosition()); CVector pos = GetPosition(); int16 lastVehicle; CEntity *vehicles[8]; CWorld::FindObjectsInRange(pos, CHECK_NEARBY_THINGS_MAX_DIST, true, &lastVehicle, 6, vehicles, false, true, false, false, false); for (int i = 0; i < lastVehicle; i++) { CVehicle *veh = (CVehicle*)vehicles[i]; if (veh->VehicleCreatedBy != MISSION_VEHICLE) { if (veh->GetStatus() != STATUS_PLAYER && veh->GetStatus() != STATUS_PLAYER_DISABLED) { if ((veh->GetPosition() - playerPos).MagnitudeSqr() > 25.0f) { veh->AutoPilot.m_nTempAction = TEMPACT_WAIT; veh->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 5000; } else { if (DotProduct2D(playerPos - veh->GetPosition(), veh->GetForward()) > 0.0f) veh->AutoPilot.m_nTempAction = TEMPACT_REVERSE; else veh->AutoPilot.m_nTempAction = TEMPACT_GOFORWARD; veh->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; } CCarCtrl::PossiblyRemoveVehicle(veh); } } } } void CPlayerPed::EvaluateNeighbouringTarget(CEntity *candidate, CEntity **targetPtr, float *lastCloseness, float distLimit, float angleOffset, bool lookToLeft, bool priority) { // priority param is unused CVector distVec = candidate->GetPosition() - GetPosition(); if (distVec.Magnitude2D() <= distLimit) { if (!DoesTargetHaveToBeBroken(candidate->GetPosition(), GetWeapon())) { float angleBetweenUs = CGeneral::GetATanOfXY(candidate->GetPosition().x - TheCamera.GetPosition().x, candidate->GetPosition().y - TheCamera.GetPosition().y); angleBetweenUs = CGeneral::LimitAngle(angleBetweenUs - angleOffset); float closeness; if (lookToLeft) { closeness = angleBetweenUs > 0.0f ? -Abs(angleBetweenUs) : -100000.0f; } else { closeness = angleBetweenUs > 0.0f ? -100000.0f : -Abs(angleBetweenUs); } if (closeness > *lastCloseness) { *targetPtr = candidate; *lastCloseness = closeness; } } } } void CPlayerPed::EvaluateTarget(CEntity *candidate, CEntity **targetPtr, float *lastCloseness, float distLimit, float angleOffset, bool priority) { CVector distVec = candidate->GetPosition() - GetPosition(); float dist = distVec.Magnitude2D(); if (dist <= distLimit) { if (!DoesTargetHaveToBeBroken(candidate->GetPosition(), GetWeapon())) { float angleBetweenUs = CGeneral::GetATanOfXY(distVec.x, distVec.y); angleBetweenUs = CGeneral::LimitAngle(angleBetweenUs - angleOffset); float closeness = -dist - 5.0f * Abs(angleBetweenUs); if (priority) { closeness += 30.0f; } if (closeness > *lastCloseness) { *targetPtr = candidate; *lastCloseness = closeness; } } } } bool CPlayerPed::CanIKReachThisTarget(CVector target, CWeapon* weapon, bool zRotImportant) { float angleToFace = CGeneral::GetRadianAngleBetweenPoints(target.x, target.y, GetPosition().x, GetPosition().y); float angleDiff = CGeneral::LimitRadianAngle(angleToFace - m_fRotationCur); return (!zRotImportant || CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM) || Abs(angleDiff) <= HALFPI) && (CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM) || Abs(target.z - GetPosition().z) <= (target - GetPosition()).Magnitude2D()); } void CPlayerPed::RotatePlayerToTrackTarget(void) { if (CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) return; float angleToFace = CGeneral::GetRadianAngleBetweenPoints( m_pPointGunAt->GetPosition().x, m_pPointGunAt->GetPosition().y, GetPosition().x, GetPosition().y); float angleDiff = CGeneral::LimitRadianAngle(m_fRotationCur - angleToFace); if (angleDiff < -DEGTORAD(25.0f)) { m_fRotationCur -= angleDiff + DEGTORAD(25.0f); m_fRotationDest -= angleDiff + DEGTORAD(25.0f); } else if (angleDiff > DEGTORAD(25.0f)) { m_fRotationCur -= angleDiff - DEGTORAD(25.0f); m_fRotationDest -= angleDiff - DEGTORAD(25.0f); } } bool CPlayerPed::FindNextWeaponLockOnTarget(CEntity *previousTarget, bool lookToLeft) { CEntity *nextTarget = nil; float weaponRange = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_fRange; // nextTarget = nil; // duplicate float lastCloseness = -10000.0f; // CGeneral::GetATanOfXY(GetForward().x, GetForward().y); // unused CVector distVec = previousTarget->GetPosition() - GetPosition(); float referenceBeta = CGeneral::GetATanOfXY(distVec.x, distVec.y); for (int h = CPools::GetPedPool()->GetSize() - 1; h >= 0; h--) { CPed *pedToCheck = CPools::GetPedPool()->GetSlot(h); if (pedToCheck) { if (pedToCheck != this && pedToCheck != previousTarget) { if (!pedToCheck->DyingOrDead() #ifndef AIMING_VEHICLE_OCCUPANTS // Mobile thing && (!pedToCheck->bInVehicle || (pedToCheck->m_pMyVehicle && pedToCheck->m_pMyVehicle->IsBike())) #endif && pedToCheck->m_leader != this && !pedToCheck->bNeverEverTargetThisPed && OurPedCanSeeThisOne(pedToCheck) && CanIKReachThisTarget(pedToCheck->GetPosition(), GetWeapon(), true)) { EvaluateNeighbouringTarget(pedToCheck, &nextTarget, &lastCloseness, weaponRange, referenceBeta, lookToLeft, IsThisPedAnAimingPriority(pedToCheck)); } } } } for (int i = 0; i < ARRAY_SIZE(m_nTargettableObjects); i++) { CObject *obj = CPools::GetObjectPool()->GetAt(m_nTargettableObjects[i]); if (obj && !obj->bHasBeenDamaged && CanIKReachThisTarget(obj->GetPosition(), GetWeapon(), true)) EvaluateNeighbouringTarget(obj, &nextTarget, &lastCloseness, weaponRange, referenceBeta, lookToLeft, true); } if (!nextTarget) return false; SetWeaponLockOnTarget(nextTarget); bDontAllowWeaponChange = true; SetPointGunAt(nextTarget); return true; } bool CPlayerPed::FindWeaponLockOnTarget(void) { CEntity *nextTarget = nil; float weaponRange = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_fRange; if (m_pPointGunAt) { CVector distVec = m_pPointGunAt->GetPosition() - GetPosition(); if (distVec.Magnitude2D() > weaponRange) { SetWeaponLockOnTarget(nil); return false; } else { return true; } } // nextTarget = nil; // duplicate float lastCloseness = -10000.0f; float referenceBeta = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); for (int h = CPools::GetPedPool()->GetSize() - 1; h >= 0; h--) { CPed *pedToCheck = CPools::GetPedPool()->GetSlot(h); if (pedToCheck) { if (pedToCheck != this) { if (!pedToCheck->DyingOrDead() #ifndef AIMING_VEHICLE_OCCUPANTS // Mobile thing && (!pedToCheck->bInVehicle || (pedToCheck->m_pMyVehicle && pedToCheck->m_pMyVehicle->IsBike())) #endif && pedToCheck->m_leader != this && !pedToCheck->bNeverEverTargetThisPed && OurPedCanSeeThisOne(pedToCheck) && CanIKReachThisTarget(pedToCheck->GetPosition(), GetWeapon(), true)) { EvaluateTarget(pedToCheck, &nextTarget, &lastCloseness, weaponRange, referenceBeta, IsThisPedAnAimingPriority(pedToCheck)); } } } } for (int i = 0; i < ARRAY_SIZE(m_nTargettableObjects); i++) { CObject *obj = CPools::GetObjectPool()->GetAt(m_nTargettableObjects[i]); if (obj && !obj->bHasBeenDamaged && CanIKReachThisTarget(obj->GetPosition(), GetWeapon(), true)) EvaluateTarget(obj, &nextTarget, &lastCloseness, weaponRange, referenceBeta, true); } if (!nextTarget) return false; SetWeaponLockOnTarget(nextTarget); bDontAllowWeaponChange = true; SetPointGunAt(nextTarget); Say(SOUND_PED_AIMING); return true; } void CPlayerPed::ProcessAnimGroups(void) { AssocGroupId groupToSet; #ifdef PC_PLAYER_CONTROLS if ((m_fWalkAngle <= -DEGTORAD(50.0f) || m_fWalkAngle >= DEGTORAD(50.0f)) && TheCamera.Cams[TheCamera.ActiveCam].Using3rdPersonMouseCam() && CanStrafeOrMouseControl()) { if (m_fWalkAngle >= -DEGTORAD(130.0f) && m_fWalkAngle <= DEGTORAD(130.0f)) { if (m_fWalkAngle > 0.0f) { if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER) groupToSet = ASSOCGRP_ROCKETLEFT; else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW || GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER || GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) groupToSet = ASSOCGRP_CHAINSAWLEFT; else groupToSet = ASSOCGRP_PLAYERLEFT; } else { if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER) groupToSet = ASSOCGRP_ROCKETRIGHT; else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW || GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER || GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) groupToSet = ASSOCGRP_CHAINSAWRIGHT; else groupToSet = ASSOCGRP_PLAYERRIGHT; } } else { if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER) groupToSet = ASSOCGRP_ROCKETBACK; else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW || GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER || GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) groupToSet = ASSOCGRP_CHAINSAWBACK; else groupToSet = ASSOCGRP_PLAYERBACK; } } else #endif { if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER) { groupToSet = ASSOCGRP_PLAYERROCKET; } else { if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT || GetWeapon()->m_eWeaponType == WEAPONTYPE_MACHETE) groupToSet = ASSOCGRP_PLAYERBBBAT; else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW || GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER || GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) groupToSet = ASSOCGRP_PLAYERCHAINSAW; else if (GetWeapon()->m_eWeaponType != WEAPONTYPE_COLT45 && GetWeapon()->m_eWeaponType != WEAPONTYPE_UZI // I hope this is a inlined function... && GetWeapon()->m_eWeaponType != WEAPONTYPE_PYTHON && GetWeapon()->m_eWeaponType != WEAPONTYPE_TEC9 && GetWeapon()->m_eWeaponType != WEAPONTYPE_SILENCED_INGRAM && GetWeapon()->m_eWeaponType != WEAPONTYPE_MP5 && GetWeapon()->m_eWeaponType != WEAPONTYPE_GOLFCLUB && GetWeapon()->m_eWeaponType != WEAPONTYPE_KATANA && GetWeapon()->m_eWeaponType != WEAPONTYPE_CAMERA) { if (!GetWeapon()->IsType2Handed()) { groupToSet = ASSOCGRP_PLAYER; } else { groupToSet = ASSOCGRP_PLAYER2ARMED; } } else { groupToSet = ASSOCGRP_PLAYER1ARMED; } } } if (m_animGroup != groupToSet) { m_animGroup = groupToSet; ReApplyMoveAnims(); } } void CPlayerPed::ProcessPlayerWeapon(CPad *padUsed) { CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); if (m_bHasLockOnTarget && !m_pPointGunAt) { TheCamera.ClearPlayerWeaponMode(); CWeaponEffects::ClearCrossHair(); ClearPointGunAt(); } if (padUsed->DuckJustDown() && !bIsDucking && m_nMoveState != PEDMOVE_SPRINT) { bCrouchWhenShooting = true; SetDuck(60000, true); } else if (bIsDucking && (padUsed->DuckJustDown() || m_nMoveState == PEDMOVE_SPRINT || padUsed->GetSprint() || padUsed->JumpJustDown() || padUsed->ExitVehicleJustDown())) { ClearDuck(true); bCrouchWhenShooting = false; } if(weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM)) m_wepAccuracy = 95; else m_wepAccuracy = 100; if (!m_pFire) { eWeaponType weapon = GetWeapon()->m_eWeaponType; if (weapon == WEAPONTYPE_ROCKETLAUNCHER || weapon == WEAPONTYPE_SNIPERRIFLE || weapon == WEAPONTYPE_LASERSCOPE || weapon == WEAPONTYPE_M4 || weapon == WEAPONTYPE_RUGER || weapon == WEAPONTYPE_M60 || weapon == WEAPONTYPE_CAMERA) { if (padUsed->TargetJustDown() || TheCamera.m_bJustJumpedOutOf1stPersonBecauseOfTarget) { #ifdef FREE_CAM if (CCamera::bFreeCam && TheCamera.Cams[0].Using3rdPersonMouseCam()) { m_fRotationCur = CGeneral::LimitRadianAngle(-TheCamera.Orientation); SetHeading(m_fRotationCur); } #endif if (weapon == WEAPONTYPE_ROCKETLAUNCHER) TheCamera.SetNewPlayerWeaponMode(CCam::MODE_ROCKETLAUNCHER, 0, 0); else if (weapon == WEAPONTYPE_SNIPERRIFLE || weapon == WEAPONTYPE_LASERSCOPE) TheCamera.SetNewPlayerWeaponMode(CCam::MODE_SNIPER, 0, 0); else if (weapon == WEAPONTYPE_CAMERA) TheCamera.SetNewPlayerWeaponMode(CCam::MODE_CAMERA, 0, 0); else TheCamera.SetNewPlayerWeaponMode(CCam::MODE_M16_1STPERSON, 0, 0); m_fMoveSpeed = 0.0f; CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE, 1000.0f); SetPedState(PED_SNIPER_MODE); return; } if (!TheCamera.Using1stPersonWeaponMode()) if (weapon == WEAPONTYPE_ROCKETLAUNCHER || weapon == WEAPONTYPE_SNIPERRIFLE || weapon == WEAPONTYPE_LASERSCOPE || weapon == WEAPONTYPE_CAMERA) return; } } if (padUsed->GetWeapon() && m_nMoveState != PEDMOVE_SPRINT) { if (m_nSelectedWepSlot == m_currentWeapon) { if (m_pPointGunAt) { if (m_nPedState == PED_ATTACK) { m_fAttackButtonCounter *= Pow(0.94f, CTimer::GetTimeStep()); } else { m_fAttackButtonCounter = 0.0f; } SetAttack(m_pPointGunAt); } else { if (m_nPedState == PED_ATTACK) { if (padUsed->WeaponJustDown()) { m_bHaveTargetSelected = true; } else if (!m_bHaveTargetSelected) { m_fAttackButtonCounter += CTimer::GetTimeStepNonClipped(); } } else { m_fAttackButtonCounter = 0.0f; m_bHaveTargetSelected = false; } if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE && !weaponInfo->IsFlagSet(WEAPONFLAG_FIGHTMODE)) { if (GetWeapon()->m_eWeaponType != WEAPONTYPE_DETONATOR && GetWeapon()->m_eWeaponType != WEAPONTYPE_DETONATOR_GRENADE || padUsed->WeaponJustDown()) SetAttack(nil); } else if (padUsed->WeaponJustDown()) { if (m_fMoveSpeed < 1.0f || m_nPedState == PED_FIGHT) StartFightAttack(padUsed->GetWeapon()); else SetAttack(nil); } } } } else { m_pedIK.m_flags &= ~CPedIK::LOOKAROUND_HEAD_ONLY; if (m_nPedState == PED_ATTACK) { m_bHaveTargetSelected = true; bIsAttacking = false; } } #ifdef FREE_CAM static int8 changedHeadingRate = 0; static int8 pointedGun = 0; if (changedHeadingRate == 2) changedHeadingRate = 1; if (pointedGun == 2) pointedGun = 1; // Rotate player/arm when shooting. We don't have auto-rotation anymore if (CCamera::m_bUseMouse3rdPerson && CCamera::bFreeCam && m_nSelectedWepSlot == m_currentWeapon && m_nMoveState != PEDMOVE_SPRINT) { #define CAN_AIM_WITH_ARM (weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM) && !bIsDucking && !bCrouchWhenShooting) // Weapons except throwable and melee ones if (weaponInfo->m_nWeaponSlot > 2) { if ((padUsed->GetTarget() && CAN_AIM_WITH_ARM) || padUsed->GetWeapon()) { float limitedCam = CGeneral::LimitRadianAngle(-TheCamera.Orientation); // On this one we can rotate arm. if (CAN_AIM_WITH_ARM) { if (!padUsed->GetWeapon()) { // making this State != ATTACK still stops it after attack. Re-start it immediately! SetPointGunAt(nil); bIsPointingGunAt = false; // to not stop after attack } pointedGun = 2; SetLookFlag(limitedCam, true); SetAimFlag(limitedCam); SetLookTimer(INT32_MAX); // removing this makes head move for real, but I experinced some bugs. } else { m_fRotationDest = limitedCam; changedHeadingRate = 2; m_headingRate = 12.5f; // Anim. fix for shotgun, ak47 and m16 (we must finish rot. it quickly) if (weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM) && padUsed->WeaponJustDown()) { m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); float limitedRotDest = m_fRotationDest; if (m_fRotationCur - PI > m_fRotationDest) { limitedRotDest += 2 * PI; } else if (PI + m_fRotationCur < m_fRotationDest) { limitedRotDest -= 2 * PI; } m_fRotationCur += (limitedRotDest - m_fRotationCur) / 2; } } } } #undef CAN_AIM_WITH_ARM } if (changedHeadingRate == 1) { changedHeadingRate = 0; RestoreHeadingRate(); } if (pointedGun == 1 && m_nPedState != PED_ATTACK) { pointedGun = 0; ClearPointGunAt(); } #endif if (padUsed->GetTarget() && m_nSelectedWepSlot == m_currentWeapon && m_nMoveState != PEDMOVE_SPRINT && !TheCamera.Using1stPersonWeaponMode() && weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM)) { if (m_pPointGunAt) { // what?? if (!m_pPointGunAt #ifdef FREE_CAM || (!CCamera::bFreeCam && CCamera::m_bUseMouse3rdPerson) #else || CCamera::m_bUseMouse3rdPerson #endif ) { ClearWeaponTarget(); return; } if (m_pPointGunAt->IsPed() && ( #ifndef AIMING_VEHICLE_OCCUPANTS (((CPed*)m_pPointGunAt)->bInVehicle && (!((CPed*)m_pPointGunAt)->m_pMyVehicle || !((CPed*)m_pPointGunAt)->m_pMyVehicle->IsBike())) || #endif !CGame::nastyGame && ((CPed*)m_pPointGunAt)->DyingOrDead())) { ClearWeaponTarget(); return; } if (CPlayerPed::DoesTargetHaveToBeBroken(m_pPointGunAt->GetPosition(), GetWeapon()) || (!bCanPointGunAtTarget && !weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM))) { // this line isn't on Mobile, idk why ClearWeaponTarget(); return; } if (m_pPointGunAt) { RotatePlayerToTrackTarget(); } if (m_pPointGunAt) { if (padUsed->ShiftTargetLeftJustDown()) FindNextWeaponLockOnTarget(m_pPointGunAt, true); if (padUsed->ShiftTargetRightJustDown()) FindNextWeaponLockOnTarget(m_pPointGunAt, false); } TheCamera.SetNewPlayerWeaponMode(CCam::MODE_SYPHON, 0, 0); TheCamera.UpdateAimingCoors(m_pPointGunAt->GetPosition()); } else if (!CCamera::m_bUseMouse3rdPerson) { if (padUsed->TargetJustDown() || TheCamera.m_bJustJumpedOutOf1stPersonBecauseOfTarget) FindWeaponLockOnTarget(); } } else if (m_pPointGunAt) { ClearWeaponTarget(); } if (m_pPointGunAt) { CVector markPos; if (m_pPointGunAt->IsPed()) { ((CPed*)m_pPointGunAt)->m_pedIK.GetComponentPosition(markPos, PED_MID); } else { markPos = m_pPointGunAt->GetPosition(); } if (bCanPointGunAtTarget) { CWeaponEffects::MarkTarget(markPos, 64, 0, 0, 255, 0.8f); } else { CWeaponEffects::MarkTarget(markPos, 64, 32, 0, 255, 0.8f); } } m_bHasLockOnTarget = m_pPointGunAt != nil; } bool CPlayerPed::MovementDisabledBecauseOfTargeting(void) { return m_pPointGunAt && !CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM); } void CPlayerPed::PlayerControlZelda(CPad *padUsed) { float smoothSprayRate = DoWeaponSmoothSpray(); float camOrientation = TheCamera.Orientation; float leftRight = padUsed->GetPedWalkLeftRight(); float upDown = padUsed->GetPedWalkUpDown(); float padMoveInGameUnit; bool smoothSprayWithoutMove = false; if (MovementDisabledBecauseOfTargeting()) { upDown = 0.0f; leftRight = 0.0f; } if (smoothSprayRate > 0.0f && upDown > 0.0f) { padMoveInGameUnit = 0.0f; smoothSprayWithoutMove = true; } else { padMoveInGameUnit = CVector2D(leftRight, upDown).Magnitude() / PAD_MOVE_TO_GAME_WORLD_MOVE; } if (padMoveInGameUnit > 0.0f || smoothSprayWithoutMove) { float padHeading = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown); float neededTurn = CGeneral::LimitRadianAngle(padHeading - camOrientation); if (smoothSprayRate > 0.0f) { m_fRotationDest = m_fRotationCur - leftRight / 128.0f * smoothSprayRate * CTimer::GetTimeStep(); } else { m_fRotationDest = neededTurn; } float maxAcc = 0.07f * CTimer::GetTimeStep(); m_fMoveSpeed = Min(padMoveInGameUnit, m_fMoveSpeed + maxAcc); } else { m_fMoveSpeed = 0.0f; } if (m_nPedState == PED_JUMP) { if (bIsInTheAir) { if (bUsesCollision && !bHitSteepSlope && (!bHitSomethingLastFrame || m_vecDamageNormal.z > 0.6f) && m_fDistanceTravelled < CTimer::GetTimeStep() * 0.02 && m_vecMoveSpeed.MagnitudeSqr() < 0.01f) { float angleSin = Sin(m_fRotationCur); // originally sin(DEGTORAD(RADTODEG(m_fRotationCur))) o_O float angleCos = Cos(m_fRotationCur); ApplyMoveForce(-angleSin * 3.0f, 3.0f * angleCos, 0.05f); } } else if (bIsLanding) { m_fMoveSpeed = 0.0f; } } if (m_nPedState == PED_ANSWER_MOBILE) { SetRealMoveAnim(); return; } if (!CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY) && padUsed->GetSprint()) { if (!m_pCurrentPhysSurface || (!m_pCurrentPhysSurface->bInfiniteMass || m_pCurrentPhysSurface->m_phy_flagA08)) m_nMoveState = PEDMOVE_SPRINT; } if (m_nPedState != PED_FIGHT) SetRealMoveAnim(); if (!bIsInTheAir && !CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_HEAVY) && padUsed->JumpJustDown() && m_nPedState != PED_JUMP) { ClearAttack(); ClearWeaponTarget(); if (m_nEvadeAmount != 0 && m_pEvadingFrom) { SetEvasiveDive((CPhysical*)m_pEvadingFrom, 1); m_nEvadeAmount = 0; m_pEvadingFrom = nil; } else { SetJump(); } } PlayIdleAnimations(padUsed); } // Finds nice positions for peds to duck and shoot player. And it's inside PlayerPed, this is treachery! void CPlayerPed::FindNewAttackPoints(void) { for (int i=0; im_nPedState == PED_DEAD || safeNeighbour->m_pedInObjective != this) { m_vecSafePos[i].x = 0.0f; m_vecSafePos[i].y = 0.0f; m_vecSafePos[i].z = 0.0f; m_pPedAtSafePos[i] = nil; } } else { m_vecSafePos[i].x = 0.0f; m_vecSafePos[i].y = 0.0f; m_vecSafePos[i].z = 0.0f; } } CEntity *entities[6]; int16 numEnts; float rightMult, fwdMult; CWorld::FindObjectsInRange(GetPosition(), 18.0f, true, &numEnts, 6, entities, true, false, false, true, false); for (int i = 0; i < numEnts; ++i) { CEntity *ent = entities[i]; int16 mi = ent->GetModelIndex(); if (!ent->IsObject() || ((CObject*)ent)->m_nSpecialCollisionResponseCases == COLLRESPONSE_FENCEPART) if (!IsTreeModel(mi)) continue; if (mi == MI_TRAFFICLIGHTS) { rightMult = 2.957f; fwdMult = 0.147f; } else if (mi == MI_SINGLESTREETLIGHTS1) { rightMult = 0.744f; fwdMult = 0.0f; } else if (mi == MI_SINGLESTREETLIGHTS2) { rightMult = 0.043f; fwdMult = 0.0f; } else if (mi == MI_SINGLESTREETLIGHTS3) { rightMult = 1.143f; fwdMult = 0.145f; } else if (mi == MI_DOUBLESTREETLIGHTS) { rightMult = 0.744f; fwdMult = 0.0f; } else if (mi == MI_LAMPPOST1) { rightMult = 0.744f; fwdMult = 0.0f; } else if (mi == MI_TRAFFICLIGHT01) { rightMult = 2.957f; fwdMult = 0.147f; } else if (mi == MI_LITTLEHA_POLICE) { rightMult = 0.0f; fwdMult = 0.0f; } else if (mi == MI_PARKBENCH) { rightMult = 0.0f; fwdMult = 0.0f; } else if (IsTreeModel(mi)) { rightMult = 0.0f; fwdMult = 0.0f; } else continue; CVector entAttackPoint(rightMult * ent->GetRight().x + fwdMult * ent->GetForward().x + ent->GetPosition().x, rightMult * ent->GetRight().y + fwdMult * ent->GetForward().y + ent->GetPosition().y, ent->GetPosition().z); CVector attackerPos = GetPosition() - entAttackPoint; // for now it's dist, not attackerPos CVector dirTowardsUs = attackerPos; dirTowardsUs.Normalise(); dirTowardsUs *= 2.0f; attackerPos = entAttackPoint - dirTowardsUs; // to make cop farther from us CPedPlacement::FindZCoorForPed(&attackerPos); if (CPedPlacement::IsPositionClearForPed(attackerPos)) m_vecSafePos[i] = attackerPos; } } void CPlayerPed::ProcessControl(void) { // Mobile has some debug/abandoned cheat thing in here: "gbFrankenTommy" if (m_nEvadeAmount != 0) --m_nEvadeAmount; if (m_nEvadeAmount == 0) m_pEvadingFrom = nil; if (m_pWanted->GetWantedLevel() > 0) FindNewAttackPoints(); UpdateMeleeAttackers(); if (m_pCurrentPhysSurface && m_pCurrentPhysSurface->IsVehicle() && ((CVehicle*)m_pCurrentPhysSurface)->IsBoat()) { bTryingToReachDryLand = true; } else if (!(((uint8)CTimer::GetFrameCounter() + m_randomSeed) & 0xF)) { CVehicle *nearVeh = (CVehicle*)CWorld::TestSphereAgainstWorld(GetPosition(), 7.0f, nil, false, true, false, false, false, false); if (nearVeh && nearVeh->IsBoat()) bTryingToReachDryLand = true; else bTryingToReachDryLand = false; } if (m_nFadeDrunkenness) { if (m_nDrunkenness - 1 > 0) { --m_nDrunkenness; } else { m_nDrunkenness = 0; CMBlur::ClearDrunkBlur(); m_nFadeDrunkenness = 0; } } if (m_nDrunkenness != 0) { CMBlur::SetDrunkBlur(m_nDrunkenness / 255.f); } CPed::ProcessControl(); SetNearbyPedsToInteractWithPlayer(); if (bWasPostponed) return; CPad *padUsed = GetPadFromPlayer(this); m_pWanted->Update(); PruneReferences(); if (GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN) { CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); CAnimBlendAssociation *fireAnim = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(weaponInfo)); if (fireAnim && fireAnim->currentTime - fireAnim->timeStep < weaponInfo->m_fAnimLoopEnd && m_nPedState == PED_ATTACK) { if (m_fGunSpinSpeed < 0.45f) { m_fGunSpinSpeed = Min(0.45f, m_fGunSpinSpeed + CTimer::GetTimeStep() * 0.013f); } if (padUsed->GetWeapon() && GetWeapon()->m_nAmmoTotal > 0 && fireAnim->currentTime >= weaponInfo->m_fAnimLoopStart) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_MINIGUN_ATTACK, 0.0f); } else { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_MINIGUN_2, m_fGunSpinSpeed * (20.f / 9)); } } else { if (m_fGunSpinSpeed > 0.0f) { if (m_fGunSpinSpeed >= 0.45f) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_MINIGUN_3, 0.0f); } m_fGunSpinSpeed = Max(0.0f, m_fGunSpinSpeed - CTimer::GetTimeStep() * 0.003f); } } } if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW && m_nPedState != PED_ATTACK && !bInVehicle) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_ATTACK, 0.0f); } if (m_nMoveState != PEDMOVE_RUN && m_nMoveState != PEDMOVE_SPRINT) RestoreSprintEnergy(1.0f); else if (m_nMoveState == PEDMOVE_RUN) RestoreSprintEnergy(0.3f); if (m_nPedState == PED_DEAD) { ClearWeaponTarget(); return; } if (m_nPedState == PED_DIE) { ClearWeaponTarget(); if (CTimer::GetTimeInMilliseconds() > m_bloodyFootprintCountOrDeathTime + 4000) SetDead(); return; } if (m_nPedState == PED_DRIVING && m_objective != OBJECTIVE_LEAVE_CAR) { if (!CReplay::IsPlayingBack() || m_pMyVehicle) { if (m_pMyVehicle->IsCar() && ((CAutomobile*)m_pMyVehicle)->Damage.GetDoorStatus(DOOR_FRONT_LEFT) == DOOR_STATUS_SWINGING) { CAnimBlendAssociation *rollDoorAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS); if (m_pMyVehicle->m_nGettingOutFlags & CAR_DOOR_FLAG_LF || rollDoorAssoc || (rollDoorAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS))) { if (rollDoorAssoc) m_pMyVehicle->ProcessOpenDoor(CAR_DOOR_LF, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, rollDoorAssoc->currentTime); } else { // These comparisons are wrong, they return uint16 if (padUsed && (padUsed->GetAccelerate() != 0.0f || padUsed->GetSteeringLeftRight() != 0.0f || padUsed->GetBrake() != 0.0f)) { if (rollDoorAssoc) m_pMyVehicle->ProcessOpenDoor(CAR_DOOR_LF, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS, rollDoorAssoc->currentTime); } else { m_pMyVehicle->m_nGettingOutFlags |= CAR_DOOR_FLAG_LF; if (m_pMyVehicle->bLowVehicle) rollDoorAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS); else rollDoorAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS); rollDoorAssoc->SetFinishCallback(PedAnimDoorCloseRollingCB, this); } } } } return; } if (m_objective == OBJECTIVE_NONE) m_nMoveState = PEDMOVE_STILL; if (bIsLanding) RunningLand(padUsed); if (padUsed && padUsed->WeaponJustDown() && !TheCamera.Using1stPersonWeaponMode()) { // ...Really? eWeaponType playerWeapon = FindPlayerPed()->GetWeapon()->m_eWeaponType; if (playerWeapon == WEAPONTYPE_SNIPERRIFLE || playerWeapon == WEAPONTYPE_LASERSCOPE) { DMAudio.PlayFrontEndSound(SOUND_WEAPON_SNIPER_SHOT_NO_ZOOM, 0); } else if (playerWeapon == WEAPONTYPE_ROCKETLAUNCHER) { DMAudio.PlayFrontEndSound(SOUND_WEAPON_ROCKET_SHOT_NO_ZOOM, 0); } } switch (m_nPedState) { case PED_NONE: case PED_IDLE: case PED_FLEE_POS: case PED_FLEE_ENTITY: case PED_ATTACK: case PED_FIGHT: case PED_AIM_GUN: case PED_ANSWER_MOBILE: if (!RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_BLOCK) && !m_attachedTo) { if (TheCamera.Using1stPersonWeaponMode()) { if (padUsed) PlayerControlSniper(padUsed); } else if (TheCamera.Cams[0].Using3rdPersonMouseCam() #ifdef FREE_CAM && !CCamera::bFreeCam #endif ) { if (padUsed) PlayerControl1stPersonRunAround(padUsed); } else if (m_nPedState == PED_FIGHT) { if (padUsed) PlayerControlFighter(padUsed); } else if (padUsed) { PlayerControlZelda(padUsed); } } if (IsPedInControl() && m_nPedState != PED_ANSWER_MOBILE && padUsed) ProcessPlayerWeapon(padUsed); break; case PED_SEEK_ENTITY: m_vecSeekPos = m_pSeekTarget->GetPosition(); // fall through case PED_SEEK_POS: switch (m_nMoveState) { case PEDMOVE_WALK: m_fMoveSpeed = 1.0f; break; case PEDMOVE_RUN: m_fMoveSpeed = 1.8f; break; case PEDMOVE_SPRINT: m_fMoveSpeed = 2.5f; break; default: m_fMoveSpeed = 0.0f; break; } SetRealMoveAnim(); if (Seek()) { RestorePreviousState(); SetMoveState(PEDMOVE_STILL); } break; case PED_SNIPER_MODE: if (GetWeapon()->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE) { if (padUsed) PlayerControlSniper(padUsed); } else if (padUsed) { PlayerControlM16(padUsed); } break; case PED_SEEK_CAR: case PED_SEEK_IN_BOAT: if (bVehEnterDoorIsBlocked || bKindaStayInSamePlace) { m_fMoveSpeed = 0.0f; } else { m_fMoveSpeed = Min(2.0f, 2.0f * (m_vecSeekPos - GetPosition()).Magnitude2D()); } if (padUsed && !padUsed->ArePlayerControlsDisabled()) { if (padUsed->GetTarget() || padUsed->GetLeftStickXJustDown() || padUsed->GetLeftStickYJustDown() || padUsed->GetDPadUpJustDown() || padUsed->GetDPadDownJustDown() || padUsed->GetDPadLeftJustDown() || padUsed->GetDPadRightJustDown()) { RestorePreviousState(); if (m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER || m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER) { RestorePreviousObjective(); } } } if (padUsed && padUsed->GetSprint()) m_nMoveState = PEDMOVE_SPRINT; SetRealMoveAnim(); break; case PED_JUMP: if (padUsed) PlayerControlZelda(padUsed); if (bIsLanding) break; // This has been added later it seems return; case PED_FALL: case PED_GETUP: case PED_ENTER_TRAIN: case PED_EXIT_TRAIN: case PED_CARJACK: case PED_DRAG_FROM_CAR: case PED_ENTER_CAR: case PED_STEAL_CAR: case PED_EXIT_CAR: ClearWeaponTarget(); break; case PED_ARRESTED: if (m_nLastPedState == PED_DRAG_FROM_CAR && m_pVehicleAnim) BeingDraggedFromCar(); break; default: break; } if (padUsed && IsPedShootable() && m_nPedState != PED_ANSWER_MOBILE && m_nLastPedState != PED_ANSWER_MOBILE) { ProcessWeaponSwitch(padUsed); GetWeapon()->Update(m_audioEntityId, this); } ProcessAnimGroups(); if (padUsed) { if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FOLLOWPED && TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking == LOOKING_BEHIND) { m_lookTimer = 0; float camAngle = CGeneral::LimitRadianAngle(TheCamera.Cams[TheCamera.ActiveCam].Front.Heading()); float angleBetweenPlayerAndCam = Abs(camAngle - m_fRotationCur); if (m_nPedState != PED_ATTACK && angleBetweenPlayerAndCam > DEGTORAD(30.0f) && angleBetweenPlayerAndCam < DEGTORAD(330.0f)) { if (angleBetweenPlayerAndCam > DEGTORAD(150.0f) && angleBetweenPlayerAndCam < DEGTORAD(210.0f)) { float rightTurnAngle = CGeneral::LimitRadianAngle(m_fRotationCur - DEGTORAD(150.0f)); float leftTurnAngle = CGeneral::LimitRadianAngle(DEGTORAD(150.0f) + m_fRotationCur); if (m_fLookDirection == 999999.0f || bIsDucking) camAngle = rightTurnAngle; else if (Abs(rightTurnAngle - m_fLookDirection) < Abs(leftTurnAngle - m_fLookDirection)) camAngle = rightTurnAngle; else camAngle = leftTurnAngle; } SetLookFlag(camAngle, true); SetLookTimer(CTimer::GetTimeStepInMilliseconds() * 5.0f); } else { ClearLookFlag(); } } } if (m_nMoveState == PEDMOVE_SPRINT && bIsLooking) { ClearLookFlag(); SetLookTimer(250); } if (m_vecMoveSpeed.Magnitude2D() < 0.1f) { if (m_nSpeedTimer) { if (CTimer::GetTimeInMilliseconds() > m_nSpeedTimer) m_bSpeedTimerFlag = true; } else { m_nSpeedTimer = CTimer::GetTimeInMilliseconds() + 500; } } else { m_nSpeedTimer = 0; m_bSpeedTimerFlag = false; } if (bDontAllowWeaponChange && FindPlayerPed() == this) { if (!CPad::GetPad(0)->GetTarget()) bDontAllowWeaponChange = false; } if (m_nPedState != PED_SNIPER_MODE && (GetWeapon()->m_eWeaponState == WEAPONSTATE_FIRING || m_nPedState == PED_ATTACK)) m_nPadDownPressedInMilliseconds = CTimer::GetTimeInMilliseconds(); if (!bIsVisible) UpdateRpHAnim(); } bool CPlayerPed::DoesPlayerWantNewWeapon(eWeaponType weapon, bool onlyIfSlotIsEmpty) { // GetPadFromPlayer(); // unused uint32 slot = CWeaponInfo::GetWeaponInfo(weapon)->m_nWeaponSlot; if (!HasWeaponSlot(slot) || GetWeapon(slot).m_eWeaponType == weapon) return true; if (onlyIfSlotIsEmpty) return false; // Check if he's using that slot right now. return m_nPedState != PED_ATTACK && m_nPedState != PED_AIM_GUN || slot != m_currentWeapon; } void CPlayerPed::PlayIdleAnimations(CPad *padUsed) { CAnimBlendAssociation* assoc; if (TheCamera.m_WideScreenOn || bIsDucking) return; struct animAndGroup { AnimationId animId; AssocGroupId groupId; }; const animAndGroup idleAnims[] = { {ANIM_PLAYER_IDLE1, ASSOCGRP_PLAYER_IDLE}, {ANIM_PLAYER_IDLE2, ASSOCGRP_PLAYER_IDLE}, {ANIM_PLAYER_IDLE3, ASSOCGRP_PLAYER_IDLE}, {ANIM_PLAYER_IDLE4, ASSOCGRP_PLAYER_IDLE}, {ANIM_STD_XPRESS_SCRATCH, ASSOCGRP_STD}, }; static int32 lastTime = 0; static int32 lastAnim = -1; bool hasIdleAnim = false; CAnimBlock *idleAnimBlock = CAnimManager::GetAnimationBlock(idleAnimBlockIndex); uint32 sinceLastInput = padUsed->InputHowLongAgo(); if (sinceLastInput <= 30000) { if (idleAnimBlock->isLoaded) { for (assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { if (assoc->flags & ASSOC_IDLE) { hasIdleAnim = true; assoc->blendDelta = -8.0f; } } if (!hasIdleAnim) CStreaming::RemoveAnim(idleAnimBlockIndex); } else { lastTime = 0; } } else { CStreaming::RequestAnim(idleAnimBlockIndex, STREAMFLAGS_DONT_REMOVE); if (idleAnimBlock->isLoaded) { for(CAnimBlendAssociation *assoc = RpAnimBlendClumpGetFirstAssociation(GetClump()); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { int firstIdle = idleAnimBlock->firstIndex; int index = assoc->hierarchy - CAnimManager::GetAnimation(0); if (index >= firstIdle && index < firstIdle + idleAnimBlock->numAnims) { hasIdleAnim = true; break; } } if (!hasIdleAnim && !bIsLooking && !bIsRestoringLook && sinceLastInput - lastTime > 25000) { int anim; do anim = CGeneral::GetRandomNumberInRange(0, ARRAY_SIZE(idleAnims)); while (lastAnim == anim); assoc = CAnimManager::BlendAnimation(GetClump(), idleAnims[anim].groupId, idleAnims[anim].animId, 8.0f); assoc->flags |= ASSOC_IDLE; lastAnim = anim; lastTime = sinceLastInput; } } } } void CPlayerPed::SetNearbyPedsToInteractWithPlayer(void) { if (CGame::noProstitutes) return; for (int i = 0; i < m_numNearPeds; ++i) { CPed *nearPed = m_nearPeds[i]; if (nearPed && nearPed->m_objectiveTimer < CTimer::GetTimeInMilliseconds() && !CTheScripts::IsPlayerOnAMission()) { int mi = nearPed->GetModelIndex(); if (CPopulation::CanSolicitPlayerOnFoot(mi)) { CVector distToMe = nearPed->GetPosition() - GetPosition(); CVector dirToMe = GetPosition() - nearPed->GetPosition(); dirToMe.Normalise(); if (DotProduct(dirToMe, nearPed->GetForward()) > 0.707 && DotProduct(GetForward(), nearPed->GetForward()) < -0.707 // those are double && distToMe.MagnitudeSqr() < 9.0f && nearPed->m_objective == OBJECTIVE_NONE) { nearPed->SetObjective(OBJECTIVE_SOLICIT_FOOT, this); nearPed->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000; nearPed->Say(SOUND_PED_SOLICIT); } } else if (CPopulation::CanSolicitPlayerInCar(mi)) { if (InVehicle() && m_pMyVehicle->IsVehicleNormal()) { if (m_pMyVehicle->IsCar()) { CVector distToVeh = nearPed->GetPosition() - m_pMyVehicle->GetPosition(); if (distToVeh.MagnitudeSqr() < 25.0f && m_pMyVehicle->IsRoomForPedToLeaveCar(CAR_DOOR_LF, nil) && nearPed->m_objective == OBJECTIVE_NONE) { nearPed->SetObjective(OBJECTIVE_SOLICIT_VEHICLE, m_pMyVehicle); } } } } } } } void CPlayerPed::UpdateMeleeAttackers(void) { CVector attackCoord; if (((CTimer::GetFrameCounter() + m_randomSeed + 7) & 3) == 0) { GetMeleeAttackCoords(attackCoord, m_nAttackDirToCheck, 2.0f); // Check if there is any vehicle/building inbetween us and m_nAttackDirToCheck. Peds will be able to attack us from those available directions. if (CWorld::GetIsLineOfSightClear(GetPosition(), attackCoord, true, true, false, true, false, false, false) && !CWorld::TestSphereAgainstWorld(attackCoord, 0.4f, m_pMeleeList[m_nAttackDirToCheck], true, true, false, true, false, false)) { if (m_pMeleeList[m_nAttackDirToCheck] == this) m_pMeleeList[m_nAttackDirToCheck] = nil; // mark it as available } else { m_pMeleeList[m_nAttackDirToCheck] = this; // slot not available. useful for m_bNoPosForMeleeAttack } if (++m_nAttackDirToCheck >= ARRAY_SIZE(m_pMeleeList)) m_nAttackDirToCheck = 0; } // 6 directions for (int i = 0; i < ARRAY_SIZE(m_pMeleeList); ++i) { CPed *victim = m_pMeleeList[i]; if (victim && victim != this) { if (victim->m_nPedState != PED_DEAD && victim->m_pedInObjective == this) { if (victim->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || victim->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS || victim->m_objective == OBJECTIVE_KILL_CHAR_ON_BOAT) { GetMeleeAttackCoords(attackCoord, i, 2.0f); if ((attackCoord - GetPosition()).MagnitudeSqr() > 12.25f) m_pMeleeList[i] = nil; } else { m_pMeleeList[i] = nil; } } else { m_pMeleeList[i] = nil; } } } m_bNoPosForMeleeAttack = m_pMeleeList[0] == this && m_pMeleeList[1] == this && m_pMeleeList[2] == this #ifdef FIX_BUGS && m_pMeleeList[3] == this #endif && m_pMeleeList[4] == this && m_pMeleeList[5] == this; } void CPlayerPed::RemovePedFromMeleeList(CPed *ped) { int i = 0; for (; m_pMeleeList[i] != ped; i++) { if (i >= ARRAY_SIZE(m_pMeleeList)) return; } m_pMeleeList[i] = nil; ped->m_attackTimer = 0; } void CPlayerPed::GetMeleeAttackCoords(CVector& coords, int8 dir, float dist) { coords = GetPosition(); switch (dir) { case 0: coords.y += dist; break; case 1: coords.x += Sqrt(3.f / 4.f) * dist; coords.y += 0.5f * dist; break; case 2: coords.x += Sqrt(3.f / 4.f) * dist; coords.y -= 0.5f * dist; break; case 3: coords.y -= dist; break; case 4: coords.x -= Sqrt(3.f / 4.f) * dist; coords.y -= 0.5f * dist; break; case 5: coords.x -= Sqrt(3.f / 4.f) * dist; coords.y += 0.5f * dist; break; default: break; } } int32 CPlayerPed::FindMeleeAttackPoint(CPed *victim, CVector &dist, uint32 &endOfAttackOut) { endOfAttackOut = 0; bool thereIsAnEmptySlot = false; int dirToAttack = -1; for (int i = 0; i < ARRAY_SIZE(m_pMeleeList); i++) { CPed* pedAtThisDir = m_pMeleeList[i]; if (pedAtThisDir) { if (pedAtThisDir == victim) { dirToAttack = i; } else { if (pedAtThisDir->m_attackTimer > endOfAttackOut) endOfAttackOut = pedAtThisDir->m_attackTimer; } } else { thereIsAnEmptySlot = true; } } // We don't have victim ped in our melee list if (dirToAttack == -1 && thereIsAnEmptySlot) { float angle = Atan2(-dist.x, -dist.y); float adjustedAngle = angle + DEGTORAD(30.0f); if (adjustedAngle < 0.f) adjustedAngle += TWOPI; int wantedDir = Floor(adjustedAngle / DEGTORAD(60.0f)); // And we have another ped at the direction of victim ped, so store victim to next empty direction to it's real direction. (Bollocks) if (m_pMeleeList[wantedDir]) { int closestDirToPreferred = -99; int preferredDir = wantedDir; for (int i = 0; i < ARRAY_SIZE(m_pMeleeList); i++) { if (!m_pMeleeList[i]) { if (Abs(i - preferredDir) < Abs(closestDirToPreferred - preferredDir)) closestDirToPreferred = i; } } if (closestDirToPreferred > 0) dirToAttack = closestDirToPreferred; } else { // Luckily the direction of victim ped is already empty, good dirToAttack = wantedDir; } if (dirToAttack != -1) { m_pMeleeList[dirToAttack] = victim; victim->RegisterReference((CEntity**) &m_pMeleeList[dirToAttack]); if (endOfAttackOut > CTimer::GetTimeInMilliseconds()) victim->m_attackTimer = endOfAttackOut + CGeneral::GetRandomNumberInRange(1000, 2000); else victim->m_attackTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(500, 1000); } } return dirToAttack; } #ifdef COMPATIBLE_SAVES #define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); #define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); void CPlayerPed::Save(uint8*& buf) { CPed::Save(buf); SkipSaveBuf(buf, 16); CopyToBuf(buf, m_fMaxStamina); SkipSaveBuf(buf, 28); CopyToBuf(buf, m_nTargettableObjects[0]); CopyToBuf(buf, m_nTargettableObjects[1]); CopyToBuf(buf, m_nTargettableObjects[2]); CopyToBuf(buf, m_nTargettableObjects[3]); SkipSaveBuf(buf, 164); } void CPlayerPed::Load(uint8*& buf) { CPed::Load(buf); SkipSaveBuf(buf, 16); CopyFromBuf(buf, m_fMaxStamina); SkipSaveBuf(buf, 28); CopyFromBuf(buf, m_nTargettableObjects[0]); CopyFromBuf(buf, m_nTargettableObjects[1]); CopyFromBuf(buf, m_nTargettableObjects[2]); CopyFromBuf(buf, m_nTargettableObjects[3]); SkipSaveBuf(buf, 164); } #undef CopyFromBuf #undef CopyToBuf #endif ================================================ FILE: src/peds/PlayerPed.h ================================================ #pragma once #include "Ped.h" class CPad; class CCopPed; class CWanted; class CPlayerPed : public CPed { public: CWanted *m_pWanted; CCopPed *m_pArrestingCop; float m_fMoveSpeed; float m_fCurrentStamina; float m_fMaxStamina; float m_fStaminaProgress; int8 m_nSelectedWepSlot; bool m_bSpeedTimerFlag; uint8 m_nEvadeAmount; uint32 m_nSpeedTimer; // m_nStandStillTimer? uint32 m_nHitAnimDelayTimer; // m_nShotDelay? float m_fAttackButtonCounter; bool m_bHaveTargetSelected; // may have better name CEntity *m_pEvadingFrom; // is this CPhysical? int32 m_nTargettableObjects[4]; uint32 m_nAdrenalineTime; uint8 m_nDrunkenness; // Needed to work out whether we lost target this frame uint8 m_nFadeDrunkenness; uint8 m_nDrunkCountdown; //countdown in frames when the drunk effect ends bool m_bAdrenalineActive; bool m_bHasLockOnTarget; bool m_bCanBeDamaged; bool m_bNoPosForMeleeAttack; bool unk1; CVector m_vecSafePos[6]; // safe places from the player, for example behind a tree CPed *m_pPedAtSafePos[6]; CPed *m_pMeleeList[6]; // reachable peds at each direction(6) int16 m_nAttackDirToCheck; float m_fWalkAngle; //angle between heading and walking direction float m_fFPSMoveHeading; RpAtomic* m_pMinigunTopAtomic; //atomic for the spinning part of the minigun model float m_fGunSpinSpeed; // for minigun float m_fGunSpinAngle; unsigned int m_nPadDownPressedInMilliseconds; unsigned int m_nLastBusFareCollected; static bool bDontAllowWeaponChange; #ifndef MASTER static bool bDebugPlayerInfo; #endif CPlayerPed(); ~CPlayerPed(); void SetMoveAnim() { }; void ReApplyMoveAnims(void); void ClearWeaponTarget(void); void SetWantedLevel(int32 level); void SetWantedLevelNoDrop(int32 level); void KeepAreaAroundPlayerClear(void); void AnnoyPlayerPed(bool); void MakeChangesForNewWeapon(int32); void MakeChangesForNewWeapon(eWeaponType); void SetInitialState(void); void ProcessControl(void); void ClearAdrenaline(void); void UseSprintEnergy(void); class CPlayerInfo *GetPlayerInfoForThisPlayerPed(); void SetRealMoveAnim(void); void RestoreSprintEnergy(float); float DoWeaponSmoothSpray(void); void DoStuffToGoOnFire(void); bool DoesTargetHaveToBeBroken(CVector, CWeapon*); void RunningLand(CPad*); bool IsThisPedAnAimingPriority(CPed*); void PlayerControlSniper(CPad*); void PlayerControlM16(CPad*); void PlayerControlFighter(CPad*); void ProcessWeaponSwitch(CPad*); void MakeObjectTargettable(int32); void PlayerControl1stPersonRunAround(CPad *padUsed); void EvaluateNeighbouringTarget(CEntity*, CEntity**, float*, float, float, bool, bool); void EvaluateTarget(CEntity*, CEntity**, float*, float, float, bool); bool FindNextWeaponLockOnTarget(CEntity*, bool); bool FindWeaponLockOnTarget(void); void ProcessAnimGroups(void); void ProcessPlayerWeapon(CPad*); void PlayerControlZelda(CPad*); bool DoesPlayerWantNewWeapon(eWeaponType, bool); void PlayIdleAnimations(CPad*); void RemovePedFromMeleeList(CPed*); void GetMeleeAttackCoords(CVector&, int8, float); int32 FindMeleeAttackPoint(CPed*, CVector&, uint32&); bool CanIKReachThisTarget(CVector, CWeapon*, bool); void RotatePlayerToTrackTarget(void); bool MovementDisabledBecauseOfTargeting(void); void FindNewAttackPoints(void); void SetNearbyPedsToInteractWithPlayer(void); void UpdateMeleeAttackers(void); static void SetupPlayerPed(int32); static void DeactivatePlayerPed(int32); static void ReactivatePlayerPed(int32); #ifdef COMPATIBLE_SAVES virtual void Save(uint8*& buf); virtual void Load(uint8*& buf); #endif static const uint32 nSaveStructSize; }; //VALIDATE_SIZE(CPlayerPed, 0x5F0); ================================================ FILE: src/peds/Population.cpp ================================================ #include "common.h" #include "Game.h" #include "General.h" #include "World.h" #include "Population.h" #include "CopPed.h" #include "Wanted.h" #include "FileMgr.h" #include "Gangs.h" #include "ModelIndices.h" #include "Zones.h" #include "CivilianPed.h" #include "EmergencyPed.h" #include "Replay.h" #include "Camera.h" #include "CutsceneMgr.h" #include "CarCtrl.h" #include "IniFile.h" #include "VisibilityPlugins.h" #include "PedPlacement.h" #include "DummyObject.h" #include "Script.h" #include "Shadows.h" #include "SurfaceTable.h" #include "Weather.h" #include "Darkel.h" #include "Streaming.h" #include "Clock.h" #include "WaterLevel.h" #define MIN_CREATION_DIST 40.0f // not for start of the game (look at the GeneratePedsAtStartOfGame) #define CREATION_RANGE 10.0f // added over the MIN_CREATION_DIST. #define OFFSCREEN_CREATION_MULT 0.5f #define PED_REMOVE_DIST (MIN_CREATION_DIST + CREATION_RANGE + 1.0f) #define PED_REMOVE_DIST_SPECIAL (MIN_CREATION_DIST + CREATION_RANGE + 15.0f) // for peds with bCullExtraFarAway flag PedGroup CPopulation::ms_pPedGroups[NUMPEDGROUPS]; bool CPopulation::ms_bGivePedsWeapons; int32 CPopulation::m_AllRandomPedsThisType = -1; float CPopulation::PedDensityMultiplier = 1.0f; uint32 CPopulation::ms_nTotalMissionPeds; int32 CPopulation::MaxNumberOfPedsInUse = 25; int32 CPopulation::MaxNumberOfPedsInUseInterior = 40; uint32 CPopulation::ms_nNumCivMale; uint32 CPopulation::ms_nNumCivFemale; uint32 CPopulation::ms_nNumCop; bool CPopulation::bZoneChangeHasHappened; uint32 CPopulation::ms_nNumEmergency; int8 CPopulation::m_CountDownToPedsAtStart; uint32 CPopulation::ms_nNumGang1; uint32 CPopulation::ms_nNumGang2; uint32 CPopulation::ms_nTotalPeds; uint32 CPopulation::ms_nNumGang3; uint32 CPopulation::ms_nTotalGangPeds; uint32 CPopulation::ms_nNumGang4; uint32 CPopulation::ms_nTotalCivPeds; uint32 CPopulation::ms_nNumGang5; uint32 CPopulation::ms_nNumDummy; uint32 CPopulation::ms_nNumGang6; uint32 CPopulation::ms_nNumGang9; uint32 CPopulation::ms_nNumGang7; uint32 CPopulation::ms_nNumGang8; uint32 CPopulation::ms_nTotalCarPassengerPeds; uint32 CPopulation::NumMiamiViceCops; uint32 gLastSelectedCivilianIndex; CEntity *gSunbatheObstacles[2]; CEntity *gCoupleObstacles[3]; void CPopulation::Initialise() { debug("Initialising CPopulation...\n"); ms_nNumCivMale = 0; ms_nNumCivFemale = 0; ms_nNumCop = 0; ms_nNumEmergency = 0; ms_nNumGang1 = 0; ms_nNumGang2 = 0; ms_nNumGang3 = 0; ms_nNumGang4 = 0; ms_nNumGang5 = 0; ms_nNumGang6 = 0; ms_nNumGang7 = 0; ms_nNumGang8 = 0; ms_nNumGang9 = 0; ms_nNumDummy = 0; ms_nTotalCarPassengerPeds = 0; ms_nTotalCivPeds = 0; ms_nTotalGangPeds = 0; ms_nTotalPeds = 0; ms_nTotalMissionPeds = 0; m_CountDownToPedsAtStart = 2; bZoneChangeHasHappened = false; // III leftover m_AllRandomPedsThisType = -1; PedDensityMultiplier = 1.0f; LoadPedGroups(); debug("CPopulation ready\n"); } void CPopulation::RemovePed(CPed *ent) { CWorld::Remove((CEntity*)ent); delete ent; } int32 CPopulation::ChooseCivilianOccupation(int32 group) { if (CWeather::Rain > 0.1f) { int32 lastModel; for (int i = 0; i < 8; i++) { gLastSelectedCivilianIndex = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); lastModel = ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; if (!CPopulation::IsSunbather(lastModel)) break; } return lastModel; } else { gLastSelectedCivilianIndex = CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP); return ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; } } int32 CPopulation::ChooseNextCivilianOccupation(int32 group) { if (CWeather::Rain > 0.1f) { int32 lastModel; for (int i = 0; i < NUMMODELSPERPEDGROUP; i++) { ++gLastSelectedCivilianIndex; if (gLastSelectedCivilianIndex >= NUMMODELSPERPEDGROUP) gLastSelectedCivilianIndex = 0; lastModel = ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; if (!CPopulation::IsSunbather(ms_pPedGroups[group].models[gLastSelectedCivilianIndex])) break; } return lastModel; } else { ++gLastSelectedCivilianIndex; if (gLastSelectedCivilianIndex >= NUMMODELSPERPEDGROUP) gLastSelectedCivilianIndex = 0; return ms_pPedGroups[group].models[gLastSelectedCivilianIndex]; } } // returns eCopType int32 CPopulation::ChoosePolicePedOccupation() { CGeneral::GetRandomNumber(); return COP_STREET; } void CPopulation::LoadPedGroups() { int fd; char line[1024]; int nextPedGroup = 0; // char unused[16]; // non-existence of that in mobile kinda verifies that char modelName[256]; CFileMgr::ChangeDir("\\DATA\\"); fd = CFileMgr::OpenFile("PEDGRP.DAT", "r"); CFileMgr::ChangeDir("\\"); while (CFileMgr::ReadLine(fd, line, sizeof(line))) { int end; // find end of line for (end = 0; ; end++) { if (line[end] == '\n') break; if (line[end] == ',' || line[end] == '\r') line[end] = ' '; } line[end] = '\0'; int cursor = 0; int i; for (i = 0; i < NUMMODELSPERPEDGROUP; i++) { while (line[cursor] <= ' ' && line[cursor] != '\0') ++cursor; if (line[cursor] == '#') break; // find next whitespace int nextWhitespace; for (nextWhitespace = cursor; line[nextWhitespace] > ' '; ++nextWhitespace) ; if (cursor == nextWhitespace) break; // read until next whitespace strncpy(modelName, &line[cursor], nextWhitespace - cursor); modelName[nextWhitespace - cursor] = '\0'; CModelInfo::GetModelInfo(modelName, &ms_pPedGroups[nextPedGroup].models[i]); cursor = nextWhitespace; } if (i == NUMMODELSPERPEDGROUP) nextPedGroup++; } CFileMgr::CloseFile(fd); } void CPopulation::UpdatePedCount(ePedType pedType, bool decrease) { if (decrease) { switch (pedType) { case PEDTYPE_PLAYER1: case PEDTYPE_PLAYER2: case PEDTYPE_PLAYER3: case PEDTYPE_PLAYER4: case PEDTYPE_UNUSED1: case PEDTYPE_SPECIAL: return; case PEDTYPE_CIVMALE: --ms_nNumCivMale; break; case PEDTYPE_CIVFEMALE: --ms_nNumCivFemale; break; case PEDTYPE_COP: --ms_nNumCop; break; case PEDTYPE_GANG1: --ms_nNumGang1; break; case PEDTYPE_GANG2: --ms_nNumGang2; break; case PEDTYPE_GANG3: --ms_nNumGang3; break; case PEDTYPE_GANG4: --ms_nNumGang4; break; case PEDTYPE_GANG5: --ms_nNumGang5; break; case PEDTYPE_GANG6: --ms_nNumGang6; break; case PEDTYPE_GANG7: --ms_nNumGang7; break; case PEDTYPE_GANG8: --ms_nNumGang8; break; case PEDTYPE_GANG9: --ms_nNumGang9; break; case PEDTYPE_EMERGENCY: case PEDTYPE_FIREMAN: --ms_nNumEmergency; break; case PEDTYPE_CRIMINAL: --ms_nNumCivMale; break; case PEDTYPE_PROSTITUTE: --ms_nNumCivFemale; break; case PEDTYPE_UNUSED2: --ms_nNumDummy; break; default: Error("Unknown ped type, UpdatePedCount, Population.cpp"); break; } } else { switch (pedType) { case PEDTYPE_PLAYER1: case PEDTYPE_PLAYER2: case PEDTYPE_PLAYER3: case PEDTYPE_PLAYER4: case PEDTYPE_UNUSED1: case PEDTYPE_SPECIAL: return; case PEDTYPE_CIVMALE: ++ms_nNumCivMale; break; case PEDTYPE_CIVFEMALE: ++ms_nNumCivFemale; break; case PEDTYPE_COP: ++ms_nNumCop; break; case PEDTYPE_GANG1: ++ms_nNumGang1; break; case PEDTYPE_GANG2: ++ms_nNumGang2; break; case PEDTYPE_GANG3: ++ms_nNumGang3; break; case PEDTYPE_GANG4: ++ms_nNumGang4; break; case PEDTYPE_GANG5: ++ms_nNumGang5; break; case PEDTYPE_GANG6: ++ms_nNumGang6; break; case PEDTYPE_GANG7: ++ms_nNumGang7; break; case PEDTYPE_GANG8: ++ms_nNumGang8; break; case PEDTYPE_GANG9: ++ms_nNumGang9; break; case PEDTYPE_EMERGENCY: case PEDTYPE_FIREMAN: ++ms_nNumEmergency; break; case PEDTYPE_CRIMINAL: ++ms_nNumCivMale; break; case PEDTYPE_PROSTITUTE: ++ms_nNumCivFemale; break; case PEDTYPE_UNUSED2: ++ms_nNumDummy; break; default: Error("Unknown ped type, UpdatePedCount, Population.cpp"); break; } } } int CPopulation::ChooseGangOccupation(int gangId) { return CGangs::ChooseGangPedModel(gangId); } void CPopulation::DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool forceIndustrialZone) { } void CPopulation::Update(bool addPeds) { if (!CReplay::IsPlayingBack()) { ManagePopulation(); RemovePedsIfThePoolGetsFull(); MoveCarsAndPedsOutOfAbandonedZones(); if (m_CountDownToPedsAtStart != 0) { if (--m_CountDownToPedsAtStart == 0) GeneratePedsAtStartOfGame(); } else { ms_nTotalCivPeds = ms_nNumCivFemale + ms_nNumCivMale; ms_nTotalGangPeds = ms_nNumGang9 + ms_nNumGang8 + ms_nNumGang7 + ms_nNumGang6 + ms_nNumGang5 + ms_nNumGang4 + ms_nNumGang3 + ms_nNumGang2 + ms_nNumGang1; ms_nTotalPeds = ms_nNumDummy + ms_nNumEmergency + ms_nNumCop + ms_nTotalGangPeds + ms_nNumCivFemale + ms_nNumCivMale; ms_nTotalPeds -= ms_nTotalCarPassengerPeds; if (!CCutsceneMgr::IsRunning() && addPeds) { float pcdm = PedCreationDistMultiplier(); AddToPopulation(pcdm * (MIN_CREATION_DIST * TheCamera.GenerationDistMultiplier), pcdm * ((MIN_CREATION_DIST + CREATION_RANGE) * TheCamera.GenerationDistMultiplier), pcdm * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT - CREATION_RANGE, pcdm * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT); } } } } void CPopulation::GeneratePedsAtStartOfGame() { for (int i = 0; i < 100; i++) { ms_nTotalCivPeds = ms_nNumCivFemale + ms_nNumCivMale; ms_nTotalGangPeds = ms_nNumGang9 + ms_nNumGang8 + ms_nNumGang7 + ms_nNumGang6 + ms_nNumGang5 + ms_nNumGang4 + ms_nNumGang3 + ms_nNumGang2 + ms_nNumGang1; ms_nTotalPeds = ms_nNumDummy + ms_nNumEmergency + ms_nNumCop + ms_nTotalGangPeds + ms_nNumCivFemale + ms_nNumCivMale; ms_nTotalPeds -= ms_nTotalCarPassengerPeds; // Min dist is 10.0f only for start of the game (naturally) AddToPopulation(10.0f, PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE), 10.0f, PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE)); } } // More speed = wider area to spawn peds float CPopulation::PedCreationDistMultiplier() { CVehicle *veh = FindPlayerVehicle(); if (!veh) return 1.0f; float vehSpeed = veh->m_vecMoveSpeed.Magnitude2D(); return clamp(vehSpeed - 0.1f + 1.0f, 1.0f, 1.5f); } CPed* CPopulation::AddPed(ePedType pedType, uint32 miOrCopType, CVector const &coors, int32 modifier) { switch (pedType) { case PEDTYPE_CIVMALE: case PEDTYPE_CIVFEMALE: { CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType); ped->SetPosition(coors); ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); if (ms_bGivePedsWeapons) { eWeaponType weapon; switch (CGeneral::GetRandomNumber() & 3) { case 0: weapon = WEAPONTYPE_COLT45; break; case 1: weapon = WEAPONTYPE_NIGHTSTICK; break; case 2: weapon = WEAPONTYPE_GOLFCLUB; break; case 3: weapon = WEAPONTYPE_TEC9; break; default: break; } if (weapon != WEAPONTYPE_UNARMED) { ped->GiveDelayedWeapon(weapon, 25001); ped->SetCurrentWeapon(CWeaponInfo::GetWeaponInfo(weapon)->m_nWeaponSlot); } } return ped; } case PEDTYPE_COP: { CCopPed *ped = new CCopPed((eCopType)miOrCopType, modifier); ped->SetPosition(coors); ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); return ped; } case PEDTYPE_GANG1: case PEDTYPE_GANG2: case PEDTYPE_GANG3: case PEDTYPE_GANG4: case PEDTYPE_GANG5: case PEDTYPE_GANG6: case PEDTYPE_GANG7: case PEDTYPE_GANG8: case PEDTYPE_GANG9: { CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType); ped->SetPosition(coors); ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); eWeaponType weapon; if (CGeneral::GetRandomNumberInRange(0, 100) >= 50) weapon = (eWeaponType)CGangs::GetGangInfo(pedType - PEDTYPE_GANG1)->m_Weapon2; else weapon = (eWeaponType)CGangs::GetGangInfo(pedType - PEDTYPE_GANG1)->m_Weapon1; ped->GiveDelayedWeapon(weapon, 25001); ped->SetCurrentWeapon(CWeaponInfo::GetWeaponInfo(weapon)->m_nWeaponSlot); return ped; } case PEDTYPE_EMERGENCY: { CEmergencyPed *ped = new CEmergencyPed(PEDTYPE_EMERGENCY); ped->SetPosition(coors); ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); return ped; } case PEDTYPE_FIREMAN: { CEmergencyPed *ped = new CEmergencyPed(PEDTYPE_FIREMAN); ped->SetPosition(coors); ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); return ped; } case PEDTYPE_CRIMINAL: case PEDTYPE_PROSTITUTE: { CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType); ped->SetPosition(coors); ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); return ped; } default: Error("Unknown ped type, AddPed, Population.cpp"); return nil; } } void CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen) { uint32 pedTypeToAdd; int32 modelToAdd; int pedAmount; CZoneInfo zoneInfo; int32 man = -1, woman = -1; CPed *gangLeader = nil; bool addCop = false; bool isSecurityGuard = false; bool forceAddingCop = false; CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus]; CVector playerCentreOfWorld = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); CTheZones::GetZoneInfoForTimeOfDay(&playerCentreOfWorld, &zoneInfo); CWanted *wantedInfo = playerInfo->m_pPed->m_pWanted; if (wantedInfo->GetWantedLevel() > 2) { if (!CGame::IsInInterior() && (CGeneral::GetRandomNumber() % 32 == 0) && FindPlayerVehicle()) forceAddingCop = true; uint32 maxCops = CGame::IsInInterior() ? wantedInfo->m_MaxCops * 1.6f : wantedInfo->m_MaxCops; if ((ms_nNumCop < maxCops || forceAddingCop) && (!playerInfo->m_pPed->bInVehicle && (CCarCtrl::NumLawEnforcerCars >= wantedInfo->m_MaximumLawEnforcerVehicles || CCarCtrl::NumRandomCars >= playerInfo->m_nTrafficMultiplier * CCarCtrl::CarDensityMultiplier || CCarCtrl::NumFiretrucksOnDuty + CCarCtrl::NumAmbulancesOnDuty + CCarCtrl::NumParkedCars + CCarCtrl::NumMissionCars + CCarCtrl::NumLawEnforcerCars + CCarCtrl::NumRandomCars >= CCarCtrl::MaxNumberOfCarsInUse) || forceAddingCop)) { addCop = true; minDist = PedCreationDistMultiplier() * MIN_CREATION_DIST; maxDist = PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE); } } float missionAndWeatherMult = -0.8f * Sqrt(CWeather::Rain) + 1.0f; // Taxi side mission if (CTheScripts::IsPlayerOnAMission()) { CPed *player = FindPlayerPed(); if (player && player->InVehicle() && player->m_pMyVehicle->IsTaxi()) missionAndWeatherMult = 1.0f; } if (CDarkel::FrenzyOnGoing()) missionAndWeatherMult = 1.0f; int selectedMaxPeds = CGame::IsInInterior() ? CPopulation::MaxNumberOfPedsInUseInterior : CPopulation::MaxNumberOfPedsInUse; // Yeah, float float maxPossiblePedsForArea = (zoneInfo.pedDensity + zoneInfo.carDensity) * playerInfo->m_fRoadDensity * PedDensityMultiplier * (CDarkel::FrenzyOnGoing() ? 1.f : CIniFile::PedNumberMultiplier) * missionAndWeatherMult; maxPossiblePedsForArea = Min(maxPossiblePedsForArea, selectedMaxPeds); if (ms_nTotalPeds < maxPossiblePedsForArea || addCop) { int decisionThreshold = CGeneral::GetRandomNumberInRange(0, 1000); if (decisionThreshold < zoneInfo.copPedThreshold || addCop) { pedTypeToAdd = PEDTYPE_COP; modelToAdd = ChoosePolicePedOccupation(); } else { int i = 0; for (i = 0; i < NUM_GANGS; i++) { if (decisionThreshold < zoneInfo.gangPedThreshold[i]) { break; } } if (i == NUM_GANGS) { if (CGeneral::GetRandomNumberInRange(0.0f, 1.0f) <= 0.95f) { modelToAdd = ChooseCivilianOccupation(zoneInfo.pedGroup); if (modelToAdd == -1) return; pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedType; } else { ChooseCivilianCoupleOccupations(zoneInfo.pedGroup, man, woman); if (man == -1 || woman == -1) return; pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(woman))->m_pedType; } } else { pedTypeToAdd = PEDTYPE_GANG1 + i; if (IsSecurityGuard((ePedType)pedTypeToAdd)) { isSecurityGuard = true; modelToAdd = ChooseGangOccupation(pedTypeToAdd - PEDTYPE_GANG1); if (modelToAdd == -1) return; pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedType; } } } if (!addCop && m_AllRandomPedsThisType > PEDTYPE_PLAYER1) pedTypeToAdd = m_AllRandomPedsThisType; if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9 && !isSecurityGuard) { minDist += 30.0f; maxDist += 30.0f; pedAmount = ComputeRandomisedGangSize(); } else pedAmount = 1; CVector generatedCoors; int node1, node2; float randomPos; bool foundCoors = !!ThePaths.GeneratePedCreationCoors(playerCentreOfWorld.x, playerCentreOfWorld.y, minDist, maxDist, minDistOffScreen, maxDistOffScreen, &generatedCoors, &node1, &node2, &randomPos, nil); if (!foundCoors) return; uint8 nodeSpawnRate = Min(ThePaths.m_pathNodes[node1].spawnRate, ThePaths.m_pathNodes[node2].spawnRate); int randomRate = CGeneral::GetRandomNumber() & 0xF; if (randomRate > nodeSpawnRate) return; CPathFind::TakeWidthIntoAccountForCoors(&ThePaths.m_pathNodes[node1], &ThePaths.m_pathNodes[node2], CGeneral::GetRandomNumber(), &generatedCoors.x, &generatedCoors.y); if (CGame::currArea == AREA_MALL && (pedTypeToAdd == PEDTYPE_CIVMALE || pedTypeToAdd == PEDTYPE_CIVFEMALE || pedTypeToAdd == PEDTYPE_CRIMINAL) && CGeneral::GetRandomNumberInRange(0.f, 1.f) > 0.5f) { PlaceMallPedsAsStationaryGroup(generatedCoors, zoneInfo.pedGroup); return; } if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9 && !isSecurityGuard) { PlaceGangMembers((ePedType)pedTypeToAdd, pedAmount, generatedCoors); return; } if (man > -1 && woman > -1) { PlaceCouple(PEDTYPE_CIVMALE, man, PEDTYPE_CIVFEMALE, woman, generatedCoors); return; } for (int i = 0; i < pedAmount; ++i) { if (pedTypeToAdd == PEDTYPE_COP) { // Unused code, ChoosePolicePedOccupation returns COP_STREET. Spawning FBI/SWAT/Army done in somewhere else. if (modelToAdd == COP_STREET) { if (!CStreaming::HasModelLoaded(MI_COP)) return; } else if (modelToAdd == COP_FBI) { if (!CStreaming::HasModelLoaded(MI_COP) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_MP5)->m_nModelId)) return; } else if (modelToAdd == COP_SWAT) { if (!CStreaming::HasModelLoaded(MI_SWAT) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_UZI)->m_nModelId)) return; } else if (modelToAdd == COP_ARMY) { if (!CStreaming::HasModelLoaded(MI_ARMY) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_MP5)->m_nModelId) || !CStreaming::HasModelLoaded(CWeaponInfo::GetWeaponInfo(WEAPONTYPE_GRENADE)->m_nModelId)) return; } } else if (!CStreaming::HasModelLoaded(modelToAdd)) { return; } generatedCoors.z += 0.7f; // What? How can this not be met? if (i < pedAmount) { // rand() // III leftover, unused if (gangLeader) { // Align gang members in formation. (btw i can't be 0 in here) float offsetMin = i * 0.75f; float offsetMax = (i + 1.0f) * 0.75f - offsetMin; float xOffset = CGeneral::GetRandomNumberInRange(offsetMin, offsetMin + offsetMax); float yOffset = CGeneral::GetRandomNumberInRange(offsetMin, offsetMin + offsetMax); if (CGeneral::GetRandomNumber() & 1) xOffset = -xOffset; if (CGeneral::GetRandomNumber() & 1) yOffset = -yOffset; generatedCoors.x = xOffset + gangLeader->GetPosition().x; generatedCoors.y = yOffset + gangLeader->GetPosition().y; } } if (!CPedPlacement::IsPositionClearForPed(generatedCoors)) break; // Why no love for last gang member?! if (i + 1 < pedAmount) { bool foundGround; float groundZ = CWorld::FindGroundZFor3DCoord(generatedCoors.x, generatedCoors.y, 2.0f + generatedCoors.z, &foundGround) + 0.7f; if (!foundGround) return; generatedCoors.z = Max(generatedCoors.z, groundZ); } bool surfaceAndDistIsOk = true; if (TheCamera.IsSphereVisible(generatedCoors, 2.0f)) { if (PedCreationDistMultiplier() * MIN_CREATION_DIST > (generatedCoors - playerCentreOfWorld).Magnitude2D()) surfaceAndDistIsOk = false; } // Place skaters if only they're on tarmac. if (((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedStatType == PEDSTAT_SKATER) { CEntity* foundEnt = nil; CColPoint foundCol; CWorld::ProcessVerticalLine(generatedCoors + CVector(0.f, 0.f, 2.f), generatedCoors.z - 2.0f, foundCol, foundEnt, true, false, false, false, false, false, nil); if (foundEnt) { if (foundCol.surfaceB == SURFACE_TARMAC || foundCol.surfaceB == SURFACE_PAVEMENT) surfaceAndDistIsOk = true; else surfaceAndDistIsOk = false; } else { surfaceAndDistIsOk = false; } } if (!surfaceAndDistIsOk) break; CPed *newPed = AddPed((ePedType)pedTypeToAdd, modelToAdd, generatedCoors); if (forceAddingCop && newPed->m_nPedType == PEDTYPE_COP) ((CCopPed*)newPed)->m_bThrowsSpikeTrap = true; bool gonnaSunbathe = false; if (CPopulation::IsSunbather(modelToAdd)) { CEntity* foundEnt = nil; CColPoint foundCol; CWorld::ProcessVerticalLine(generatedCoors + CVector(0.f, 0.f, 2.f), generatedCoors.z - 2.0f, foundCol, foundEnt, true, false, false, false, false, false, nil); if (foundEnt) { if ((foundCol.surfaceB == SURFACE_CONCRETE_BEACH || foundCol.surfaceB == SURFACE_SAND) && CClock::GetHours() >= 10 && CClock::GetHours() <= 18 && 0.0f == CWeather::Rain) { gonnaSunbathe = true; if (CPedPlacement::IsPositionClearForPed(generatedCoors, 3.0f, ARRAY_SIZE(gSunbatheObstacles), gSunbatheObstacles)) { for (int j = 0; j < ARRAY_SIZE(gSunbatheObstacles); j++) { if (gSunbatheObstacles[j] && gSunbatheObstacles[j] != newPed) gonnaSunbathe = false; } } } } } if (gonnaSunbathe) { float heading = CGeneral::GetRandomNumberInRange(0.f, 1.f) * TWOPI; newPed->m_fRotationDest = heading; newPed->m_fRotationCur = heading; // unused // v61 = CGeneral::GetRandomTrueFalse(); newPed->SetWaitState(WAITSTATE_SUN_BATHE_IDLE, nil); CVector toyPos(newPed->GetPosition()); float waterLevel; if (CWaterLevel::GetGroundLevel(toyPos, &waterLevel, nil, 30.0f)) { toyPos.z = 0.04f + waterLevel; CEntity *toy = CWaterLevel::CreateBeachToy(toyPos, BEACHTOY_ANY_TOWEL); if (toy) toy->SetHeading(heading); if (!(CGeneral::GetRandomNumber() & 3)) { CWaterLevel::CreateBeachToy(toyPos + CVector(CGeneral::GetRandomNumberInRange(-2.f, 2.f), CGeneral::GetRandomNumberInRange(-2.f, 2.f), 0.f), BEACHTOY_LOTION); } } } else { newPed->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); } if (i != 0) { // Gang member newPed->SetLeader(gangLeader); newPed->SetPedState(PED_UNKNOWN); gangLeader->SetPedState(PED_UNKNOWN); } else { gangLeader = newPed; } CVisibilityPlugins::SetClumpAlpha(newPed->GetClump(), 0); /* // Pointless, this is already a for loop if (i + 1 > pedAmount) break; if (pedAmount <= 1) break; */ } } } CPed* CPopulation::AddPedInCar(CVehicle* car, bool isDriver) { const int defaultModel = MI_MALE01; int miamiViceIndex = 0; bool imSureThatModelIsLoaded = true; CVector coors = FindPlayerCoors(); CZoneInfo zoneInfo; int pedType; // May be eCopType, model index or non-sense(for medic), AddPed knows that by looking to ped type. int preferredModel; CTheZones::GetZoneInfoForTimeOfDay(&coors, &zoneInfo); switch (car->GetModelIndex()) { case MI_FIRETRUCK: preferredModel = 0; pedType = PEDTYPE_FIREMAN; break; case MI_AMBULAN: preferredModel = 0; pedType = PEDTYPE_EMERGENCY; break; case MI_POLICE: case MI_PREDATOR: preferredModel = COP_STREET; pedType = PEDTYPE_COP; break; case MI_ENFORCER: preferredModel = COP_SWAT; pedType = PEDTYPE_COP; break; case MI_RHINO: case MI_BARRACKS: preferredModel = COP_ARMY; pedType = PEDTYPE_COP; break; case MI_FBIRANCH: preferredModel = COP_FBI; pedType = PEDTYPE_COP; break; default: if (car->IsTaxi()) { if (isDriver) { pedType = PEDTYPE_CIVMALE; preferredModel = MI_TAXI_D; break; } // fall through if not } else if (car->GetModelIndex() == MI_VICECHEE) { if (car->bIsLawEnforcer) { preferredModel = COP_MIAMIVICE; pedType = PEDTYPE_COP; miamiViceIndex = (isDriver ? 2 * CCarCtrl::MiamiViceCycle : 2 * CCarCtrl::MiamiViceCycle + 1); break; } // fall through if not } int gangOfPed = 0; imSureThatModelIsLoaded = false; while (gangOfPed < NUM_GANGS && CGangs::GetGangInfo(gangOfPed)->m_nVehicleMI != car->GetModelIndex()) gangOfPed++; if (gangOfPed < NUM_GANGS) { pedType = gangOfPed + PEDTYPE_GANG1; preferredModel = ChooseGangOccupation(gangOfPed); } else if (gangOfPed == NUM_GANGS) { CVehicleModelInfo *carModel = ((CVehicleModelInfo *)CModelInfo::GetModelInfo(car->GetModelIndex())); preferredModel = ChooseCivilianOccupation(zoneInfo.pedGroup); int i = 15; for(; i >= 0; i--) { CPedModelInfo* pedModel = (CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel); if (pedModel->GetRwObject()) { if (!car->IsPassenger(preferredModel) && !car->IsDriver(preferredModel)) { if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_carsCanDrive & (1 << carModel->m_vehicleClass)) break; } } preferredModel = ChooseNextCivilianOccupation(zoneInfo.pedGroup); } if (i == -1) preferredModel = defaultModel; pedType = ((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_pedType; } break; } if (!imSureThatModelIsLoaded && !((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->GetRwObject()) { preferredModel = defaultModel; pedType = ((CPedModelInfo*)CModelInfo::GetModelInfo(defaultModel))->m_pedType; } CPed *newPed = CPopulation::AddPed((ePedType)pedType, preferredModel, car->GetPosition(), miamiViceIndex); newPed->bUsesCollision = false; if (newPed->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { newPed->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(newPed->GetWeapon()->m_eWeaponType)->m_nModelId); } newPed->AddInCarAnims(car, isDriver); return newPed; } void CPopulation::MoveCarsAndPedsOutOfAbandonedZones() { } void CPopulation::ConvertAllObjectsToDummyObjects() { int poolSize = CPools::GetObjectPool()->GetSize(); for (int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { CObject *obj = CPools::GetObjectPool()->GetSlot(poolIndex); if (obj) { if (obj->CanBeDeleted()) ConvertToDummyObject(obj); } } } void CPopulation::ConvertToRealObject(CDummyObject *dummy) { if (!TestSafeForRealObject(dummy)) return; CObject *obj = new CObject(dummy); if (!obj) return; CWorld::Remove(dummy); delete dummy; CWorld::Add(obj); CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(obj->GetModelIndex()); if (IsGlass(obj->GetModelIndex()) && !mi->m_isArtistGlass) { obj->bIsVisible = false; } else if (obj->GetModelIndex() == MI_BUOY) { obj->SetIsStatic(false); obj->m_vecMoveSpeed = CVector(0.0f, 0.0f, -0.001f); obj->bTouchingWater = true; obj->AddToMovingList(); } } void CPopulation::ConvertToDummyObject(CObject *obj) { CDummyObject *dummy = new CDummyObject(obj); dummy->GetMatrix() = obj->m_objectMatrix; dummy->GetMatrix().UpdateRW(); dummy->UpdateRwFrame(); CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(obj->GetModelIndex()); if (IsGlass(obj->GetModelIndex()) && !mi->m_isArtistGlass) dummy->bIsVisible = false; CWorld::Remove(obj); delete obj; CWorld::Add(dummy); } bool CPopulation::TestRoomForDummyObject(CObject *obj) { int16 collidingObjs; CWorld::FindObjectsKindaColliding(obj->m_objectMatrix.GetPosition(), obj->GetBoundRadius(), false, &collidingObjs, 2, nil, false, true, true, false, false); return collidingObjs == 0; } bool CPopulation::TestSafeForRealObject(CDummyObject *dummy) { CPtrNode *ptrNode; CColModel *dummyCol = dummy->GetColModel(); float colRadius = dummy->GetBoundRadius(); CVector colCentre = dummy->GetBoundCentre(); int minX = CWorld::GetSectorIndexX(dummy->GetPosition().x - colRadius); if (minX < 0) minX = 0; int minY = CWorld::GetSectorIndexY(dummy->GetPosition().y - colRadius); if (minY < 0) minY = 0; int maxX = CWorld::GetSectorIndexX(dummy->GetPosition().x + colRadius); #ifdef FIX_BUGS if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; #else if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; #endif int maxY = CWorld::GetSectorIndexY(dummy->GetPosition().y + colRadius); #ifdef FIX_BUGS if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; #else if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; #endif static CColPoint aTempColPoints[MAX_COLLISION_POINTS]; for (int curY = minY; curY <= maxY; curY++) { for (int curX = minX; curX <= maxX; curX++) { CSector *sector = CWorld::GetSector(curX, curY); for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES].first; ptrNode; ptrNode = ptrNode->next) { CVehicle *veh = (CVehicle*)ptrNode->item; if (veh->m_scanCode != CWorld::GetCurrentScanCode()) { if (veh->GetIsTouching(colCentre, colRadius)) { veh->m_scanCode = CWorld::GetCurrentScanCode(); if (CCollision::ProcessColModels(dummy->GetMatrix(), *dummyCol, veh->GetMatrix(), *veh->GetColModel(), aTempColPoints, nil, nil) > 0) return false; } } } for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP].first; ptrNode; ptrNode = ptrNode->next) { CVehicle *veh = (CVehicle*)ptrNode->item; if (veh->m_scanCode != CWorld::GetCurrentScanCode()) { if (veh->GetIsTouching(colCentre, colRadius)) { veh->m_scanCode = CWorld::GetCurrentScanCode(); if (CCollision::ProcessColModels(dummy->GetMatrix(), *dummyCol, veh->GetMatrix(), *veh->GetColModel(), aTempColPoints, nil, nil) > 0) return false; } } } } } return true; } void CPopulation::ManagePopulation(void) { int frameMod32 = CTimer::GetFrameCounter() & 31; CVector playerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus); // Why this code is here?! Delete temporary objects when they got too far, and convert others to "dummy" objects. (like lamp posts) int objectPoolSize = CPools::GetObjectPool()->GetSize(); for (int i = objectPoolSize * frameMod32 / 32; i < objectPoolSize * (frameMod32 + 1) / 32; i++) { CObject *obj = CPools::GetObjectPool()->GetSlot(i); if (obj && obj->CanBeDeleted()) { float objPlayerDist = (obj->GetPosition() - playerPos).Magnitude(); if (obj->ObjectCreatedBy == TEMP_OBJECT) { if (obj->GetModelIndex() != MI_ROADWORKBARRIER1 && obj->GetModelIndex() != MI_BEACHBALL) { if (objPlayerDist > 51.0f || objPlayerDist > 25.0f && !obj->GetIsOnScreen() || CTimer::GetTimeInMilliseconds() > obj->m_nEndOfLifeTime) { CWorld::Remove(obj); delete obj; } } else if (objPlayerDist > 120.0f) { CWorld::Remove(obj); delete obj; } } else if (objPlayerDist > 80.0f && (obj->m_objectMatrix.GetPosition() - playerPos).Magnitude() > 80.0f) { if (obj->ObjectCreatedBy != CUTSCENE_OBJECT && TestRoomForDummyObject(obj)) { ConvertToDummyObject(obj); } } } } // Convert them back to real objects. Dummy objects don't have collisions, so they need to be converted. int dummyPoolSize = CPools::GetDummyPool()->GetSize(); for (int i = dummyPoolSize * frameMod32 / 32; i < dummyPoolSize * (frameMod32 + 1) / 32; i++) { CDummy *dummy = CPools::GetDummyPool()->GetSlot(i); if (dummy && (dummy->m_area == CGame::currArea || dummy->m_area == AREA_EVERYWHERE)) { if ((dummy->GetPosition() - playerPos).Magnitude() < 80.0f) ConvertToRealObject((CDummyObject*)dummy); } } int pedPoolSize = CPools::GetPedPool()->GetSize(); #ifndef SQUEEZE_PERFORMANCE for (int poolIndex = pedPoolSize-1; poolIndex >= 0; poolIndex--) { #else for (int poolIndex = (pedPoolSize * (frameMod32 + 1) / 32) - 1; poolIndex >= pedPoolSize * frameMod32 / 32; poolIndex--) { #endif CPed *ped = CPools::GetPedPool()->GetSlot(poolIndex); if (ped && !ped->IsPlayer() && ped->CanBeDeleted() && !ped->bInVehicle) { uint32 timeSinceDeath = CTimer::GetTimeInMilliseconds() - ped->m_bloodyFootprintCountOrDeathTime; if (ped->m_nPedState == PED_DEAD && (timeSinceDeath > 30000 || CDarkel::FrenzyOnGoing() && timeSinceDeath > 15000)) ped->bFadeOut = true; if (ped->bFadeOut && CVisibilityPlugins::GetClumpAlpha(ped->GetClump()) == 0) { RemovePed(ped); continue; } float dist = (ped->GetPosition() - playerPos).Magnitude2D(); bool pedIsFarAway = false; if (ped->IsGangMember()) dist -= 30.0f; else if (ped->bDeadPedInFrontOfCar && ped->m_vehicleInAccident) dist = 0.0f; if (PedCreationDistMultiplier() * (PED_REMOVE_DIST_SPECIAL * TheCamera.GenerationDistMultiplier) < dist || (!ped->bCullExtraFarAway && PedCreationDistMultiplier() * PED_REMOVE_DIST * TheCamera.GenerationDistMultiplier < dist)) { pedIsFarAway = true; } else if (PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT < dist) { if (CTimer::GetTimeInMilliseconds() > ped->m_nExtendedRangeTimer && !ped->GetIsOnScreen()) { if (TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER_RUNABOUT && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_CAMERA && !TheCamera.Cams[TheCamera.ActiveCam].LookingLeft && !TheCamera.Cams[TheCamera.ActiveCam].LookingRight && !TheCamera.Cams[TheCamera.ActiveCam].LookingBehind) { pedIsFarAway = true; } } } else { ped->m_nExtendedRangeTimer = ped->m_nPedType == PEDTYPE_COP ? CTimer::GetTimeInMilliseconds() + 10000 : CTimer::GetTimeInMilliseconds() + 4000; } if (!pedIsFarAway) continue; if (ped->m_nPedState == PED_DEAD && !ped->bFadeOut) { CVector pedPos = ped->GetPosition(); float randAngle = (uint8) CGeneral::GetRandomNumber() * (3.14f / 128.0f); // Not PI, 3.14 switch (CGeneral::GetRandomNumber() % 3) { case 0: CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline1Tex, &pedPos, 0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle), 255, 255, 255, 255, 4.0f, 40000, 1.0f); break; case 1: CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline2Tex, &pedPos, 0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle), 255, 255, 255, 255, 4.0f, 40000, 1.0f); break; case 2: CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline3Tex, &pedPos, 0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle), 255, 255, 255, 255, 4.0f, 40000, 1.0f); break; default: break; } } if (ped->GetIsOnScreen()) ped->bFadeOut = true; else RemovePed(ped); } } } CPed* CPopulation::AddDeadPedInFrontOfCar(const CVector& pos, CVehicle* pCulprit) { if (TheCamera.IsSphereVisible(pos, 2.0f) && MIN_CREATION_DIST * PedCreationDistMultiplier() > (pos - FindPlayerPed()->GetPosition()).Magnitude2D()) { return nil; } bool found; float z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &found) + 1.0f; if (!found) return nil; z = Max(z, pos.z); if (!CModelInfo::GetModelInfo(MI_MALE01)->GetRwObject()) return nil; CPed* pPed = CPopulation::AddPed(PEDTYPE_CIVMALE, MI_MALE01, pos); pPed->SetDie(); pPed->m_nPedMoney = 0; pPed->bDeadPedInFrontOfCar = true; pPed->m_vehicleInAccident = pCulprit; pCulprit->RegisterReference((CEntity**)&pPed->m_vehicleInAccident); CEntity* pEntities[3] = { 0 }; if (!CPedPlacement::IsPositionClearForPed(pos, 2.0f, 3, pEntities)) { for (int i = 0; i < 3; i++) { if (pEntities[i] && pEntities[i] != pCulprit && pEntities[i] != pPed) { RemovePed(pPed); return nil; } } } CColPoint colpts[MAX_COLLISION_POINTS]; if (CCollision::ProcessColModels(pCulprit->GetMatrix(), *pCulprit->GetColModel(), pPed->GetMatrix(), *pPed->GetColModel(), colpts, nil, nil)) { RemovePed(pPed); return nil; } CVisibilityPlugins::SetClumpAlpha(pPed->GetClump(), 0); return pPed; } bool CPopulation::IsSkateable(CVector const& pos) { CColPoint foundCol; CEntity* foundEnt = nil; CWorld::ProcessVerticalLine(pos + CVector(0.f, 0.f, 2.f), pos.z - 2.0f, foundCol, foundEnt, true, false, false, false, false, false, nil); if (!foundEnt) return false; return foundCol.surfaceB == SURFACE_TARMAC || foundCol.surfaceB == SURFACE_PAVEMENT; } bool CPopulation::CanJeerAtStripper(int32 model) { return model == MI_WMOBE || model == MI_WMYBE || model == MI_WMOST || model == MI_BMYBB; } void CPopulation::RemovePedsIfThePoolGetsFull(void) { if ((CTimer::GetFrameCounter() & 7) == 5) { if (CPools::GetPedPool()->GetNoOfFreeSpaces() < 8) { CPed *closestPed = nil; float closestDist = 10000000.0; int poolSize = CPools::GetPedPool()->GetSize(); for (int i = poolSize - 1; i >= 0; i--) { CPed* ped = CPools::GetPedPool()->GetSlot(i); if (ped && ped->CanBeDeleted()) { float dist = (TheCamera.GetPosition() - ped->GetPosition()).Magnitude(); if (dist < closestDist) { closestDist = dist; closestPed = ped; } } } if (closestPed) { RemovePed(closestPed); } } } } bool CPopulation::IsMale(int32 model) { switch (model) { case MI_HMYST: case MI_HMOST: case MI_HMYRI: case MI_HMORI: case MI_HMYBE: case MI_HMOBE: case MI_HMOTR: case MI_HMYAP: case MI_HMOCA: case MI_BMODK: case MI_BMYKR: case MI_BMYST: case MI_BMOST: case MI_BMYRI: case MI_BMYBE: case MI_BMOBE: case MI_BMYBU: case MI_BMOTR: case MI_BMYPI: case MI_BMYBB: case MI_WMYCR: case MI_WMYST: case MI_WMOST: case MI_WMYRI: case MI_WMORI: case MI_WMYBE: case MI_WMOBE: case MI_WMYCW: case MI_WMYGO: case MI_WMOGO: case MI_WMYLG: case MI_WMYBU: case MI_WMOBU: case MI_WMOTR: case MI_WMYPI: case MI_WMOCA: case MI_WMYJG: case MI_WMYSK: // BUG? Why no JMOTO? return true; default: return false; } } bool CPopulation::IsFemale(int32 model) { switch (model) { case MI_HFYST: case MI_HFOST: case MI_HFYRI: case MI_HFORI: case MI_HFYBE: case MI_HFOBE: case MI_HFYBU: case MI_HFYMD: case MI_HFYCG: case MI_HFYPR: case MI_HFOTR: case MI_BFYST: case MI_BFOST: case MI_BFYRI: case MI_BFORI: case MI_BFYBE: case MI_BFOBE: case MI_BFYPR: case MI_BFOTR: case MI_WFYST: case MI_WFOST: case MI_WFYRI: case MI_WFORI: case MI_WFYBE: case MI_WFOBE: case MI_WFOGO: case MI_WFYLG: case MI_WFYBU: case MI_WFYPR: case MI_WFOTR: case MI_WFYJG: case MI_WFYSK: case MI_WFYSH: case MI_WFOSH: case MI_JFOTO: return true; default: return false; } } bool CPopulation::IsSunbather(int32 model) { switch (model) { case MI_HFYBE: case MI_HFOBE: case MI_HMYBE: case MI_HMOBE: case MI_BFYBE: case MI_BMYBE: case MI_BFOBE: case MI_BMOBE: case MI_WFYBE: case MI_WMYBE: case MI_WFOBE: case MI_WMOBE: return true; default: return false; } } int32 CPopulation::ComputeRandomisedGangSize(void) { return CGeneral::GetRandomNumberInRange(3, 6); } bool CPopulation::CanSolicitPlayerInCar(int32 model) { return model == MI_HFYPR || model == MI_BFYPR || model == MI_WFYPR; } bool CPopulation::CanSolicitPlayerOnFoot(int32 model) { return model == MI_HFYMD || model == MI_HFYCG || model == MI_BFOTR || model == MI_BMOTR || model == MI_WFOTR || model == MI_WMOTR; } bool CPopulation::IsSecurityGuard(ePedType pedType) { return pedType == PEDTYPE_GANG5; } void CPopulation::ChooseCivilianCoupleOccupations(int32 group, int32& man, int32& woman) { man = -1; woman = -1; for (int i = 0; i < 8; i++) { if (man > -1) break; int32 model = ms_pPedGroups[group].models[CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP)]; if (man == -1 && IsMale(model) && ((CPedModelInfo*)CModelInfo::GetModelInfo(model))->m_pedType == PEDTYPE_CIVMALE) { man = model; } } if (man != -1) { int32 model; for (int i = 0; i < NUMMODELSPERPEDGROUP; i++) { model = ms_pPedGroups[group].models[i]; if (IsFemale(model)) { CPedModelInfo* womanModelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(model); if (womanModelInfo->m_pedType == PEDTYPE_CIVFEMALE) { CPedModelInfo* manModelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(man); // If both are skater or not, finalize the decision if (manModelInfo && womanModelInfo) { if (manModelInfo->m_animGroup == womanModelInfo->m_animGroup) { if (manModelInfo->m_pedStatType != PEDSTAT_SKATER && womanModelInfo->m_pedStatType != PEDSTAT_SKATER) break; if (manModelInfo->m_pedStatType == PEDSTAT_SKATER && womanModelInfo->m_pedStatType == PEDSTAT_SKATER) break; } } } } } woman = model; } } void CPopulation::PlaceGangMembers(ePedType pedType, int pedAmount, CVector const& coors) { if (CGeneral::GetRandomNumberInRange(0.f, 1.f) < 0.333f) { PlaceGangMembersInFormation(pedType, pedAmount, coors); } else { PlaceGangMembersInCircle(pedType, pedAmount, coors); } } void CPopulation::PlaceGangMembersInFormation(ePedType pedType, int pedAmount, CVector const& coors) { CPed *createdPeds[5]; if (!TheCamera.IsSphereVisible(coors, 3.0f) || MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { if (CPedPlacement::IsPositionClearForPed(coors, 3.0f, -1, 0)) { bool leaderFoundGround; float leaderGroundZ = CWorld::FindGroundZFor3DCoord(coors.x, coors.y, coors.z, &leaderFoundGround) + 1.0f; if (leaderFoundGround) { float finalZ = coors.z > leaderGroundZ ? coors.z : leaderGroundZ; int leaderModel = ChooseGangOccupation(pedType - PEDTYPE_GANG1); if (((CPedModelInfo*)CModelInfo::GetModelInfo(leaderModel))->GetRwObject()) { CPed *leader = AddPed(pedType, leaderModel, CVector(coors.x, coors.y, finalZ)); if (leader) { leader->SetObjective(OBJECTIVE_NONE); leader->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); leader->bIsLeader = true; if (CGangs::GetWillAttackPlayerWithCops(pedType)) leader->bCanAttackPlayerWithCops = true; int pedIdx = 1; createdPeds[0] = leader; for (int i = 1; i < pedAmount; ++i) { int memberModel = ChooseGangOccupation(pedType - PEDTYPE_GANG1); if (!((CPedModelInfo*)CModelInfo::GetModelInfo(memberModel))->GetRwObject()) continue; CPed* memberPed = AddPed(pedType, memberModel, CVector(coors.x, coors.y, finalZ)); if (!memberPed) continue; memberPed->SetObjective(OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, leader); memberPed->SetFormation((eFormation)i); CVector formationPos = memberPed->GetFormationPosition(); CVector finalFormationPos = formationPos; bool formationFoundGround; float formationGroundZ = CWorld::FindGroundZFor3DCoord(formationPos.x, formationPos.y, 1.0f + formationPos.z, &formationFoundGround) + 1.0f; finalFormationPos.z = Max(finalFormationPos.z, formationGroundZ); if (formationFoundGround) { if (Abs(finalFormationPos.z - leader->GetPosition().z) <= 1.0f) { if (CWorld::GetIsLineOfSightClear(finalFormationPos, leader->GetPosition(), true, false, false, false, false, false, false)) { memberPed->SetPosition(finalFormationPos); createdPeds[pedIdx++] = memberPed; if (CGangs::GetWillAttackPlayerWithCops(pedType)) leader->bCanAttackPlayerWithCops = true; CVisibilityPlugins::SetClumpAlpha(memberPed->GetClump(), 0); continue; } } } RemovePed(memberPed); } if (pedIdx >= 3) { for (int j = 1; j < pedIdx; ++j) createdPeds[j]->SetLeader(createdPeds[0]); } else { for (int k = 0; k < pedIdx; ++k) { RemovePed(createdPeds[k]); } } } } } } } } void CPopulation::PlaceGangMembersInCircle(ePedType pedType, int pedAmount, CVector const& coors) { CPed *createdPeds[5]; if (pedAmount < 2) return; float circleSector = TWOPI / pedAmount; float circleR = Sqrt(0.5f / (1.0f - Cos(circleSector))); if (!TheCamera.IsSphereVisible(coors, circleR) || MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { if (CPedPlacement::IsPositionClearForPed(coors, circleR, -1, 0)) { int pedIdx = 0; CVector leaderPos; for (int i = 0; i < pedAmount; i++) { float angleMult = i + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); float randomR = circleR + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * circleR; float xOffset = randomR * Cos(angleMult * circleSector); float yOffset = randomR * Sin(angleMult * circleSector); bool foundGround; float groundZ = CWorld::FindGroundZFor3DCoord(xOffset + coors.x, yOffset + coors.y, coors.z + 1.0, &foundGround) + 1.0f; if (foundGround) { CVector finalPos(coors.x + xOffset, coors.y + yOffset, coors.z > groundZ ? coors.z : groundZ); if (i == 0) leaderPos = finalPos; int gangModel = ChooseGangOccupation(pedType - PEDTYPE_GANG1); if (((CPedModelInfo*)CModelInfo::GetModelInfo(gangModel))->GetRwObject()) { CEntity* obstacles[6] = { nil, nil, nil, nil, nil, nil }; CPedPlacement::IsPositionClearForPed(finalPos, CModelInfo::GetModelInfo(gangModel)->GetColModel()->boundingSphere.radius, ARRAY_SIZE(obstacles), obstacles); bool foundObstacle = false; for (int m = 0; m < ARRAY_SIZE(obstacles); m++) { CEntity* obstacle = obstacles[m]; if (obstacle) { int n = 0; bool obstacleIsHarmless = false; for (int n = 0; n < pedIdx; n++) { if (obstacle == createdPeds[n]) obstacleIsHarmless = true; } if (!obstacleIsHarmless) { foundObstacle = true; break; } } } bool memberCanSeeLeader = i == 0 ? true : CWorld::GetIsLineOfSightClear(finalPos, leaderPos, true, false, false, false, false, false, false); bool notTooCloseToLeader = i == 0 ? true : !(Abs(finalPos.z - leaderPos.z) < 1.0f); if (!foundObstacle && memberCanSeeLeader && notTooCloseToLeader) { CPed* newPed = AddPed(pedType, gangModel, finalPos); if (newPed) { createdPeds[pedIdx++] = newPed; float angle = CGeneral::GetRadianAngleBetweenPoints( coors.x, coors.y, finalPos.x, finalPos.y); newPed->m_fRotationDest = angle; newPed->m_fRotationCur = angle; if (CGangs::GetWillAttackPlayerWithCops(pedType)) newPed->bCanAttackPlayerWithCops = true; CVisibilityPlugins::SetClumpAlpha(newPed->GetClump(), 0); } // No. #ifndef FIX_BUGS else CWorld::Remove(nil); #endif } } } } if (pedIdx >= 3) { for (int j = 0; j < pedIdx / 2; ++j) { createdPeds[j]->SetChat(createdPeds[pedIdx - 1 - j], 100000); createdPeds[pedIdx - 1 - j]->SetChat(createdPeds[j], 100000); } // Make that extra guy in the middle stand there(PED_UNKNOWN locks him) and do nothing :lmao: if (pedIdx % 2 != 0) { CPed *tmim = createdPeds[(pedIdx - 1) / 2]; float angle = CGeneral::GetRadianAngleBetweenPoints( tmim->GetPosition().x, tmim->GetPosition().y, createdPeds[0]->GetPosition().x, createdPeds[0]->GetPosition().y); tmim->SetHeading(angle); tmim->SetPedState(PED_UNKNOWN); } createdPeds[0]->bIsLeader = true; for (int l = 1; l < pedIdx; ++l) createdPeds[l]->SetLeader(createdPeds[0]); } else { for (int k = 0; k < pedIdx; ++k) { RemovePed(createdPeds[k]); } } } } } void CPopulation::PlaceCouple(ePedType manType, int32 manModel, ePedType womanType, int32 womanModel, CVector coors) { // Homosexuality filter!!!! Homophobic R* >>>:( if (manType != PEDTYPE_CIVMALE || womanType != PEDTYPE_CIVFEMALE) return; if (!TheCamera.IsSphereVisible(coors, 1.5f) || MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { if (CPedPlacement::IsPositionClearForPed(coors, CModelInfo::GetModelInfo(manModel)->GetColModel()->boundingSphere.radius, -1, 0)) { bool manFoundGround; float manGroundZ = CWorld::FindGroundZFor3DCoord(coors.x, coors.y, coors.z, &manFoundGround) + 1.0f; if (manFoundGround) { CVector correctedManPos = coors; correctedManPos.z = Max(coors.z, manGroundZ); if (((CPedModelInfo*)CModelInfo::GetModelInfo(manModel))->GetRwObject()) { CPed *man = AddPed(PEDTYPE_CIVMALE, manModel, correctedManPos); if (man) { man->SetObjective(OBJECTIVE_NONE); man->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8)); man->bIsLeader = true; CVisibilityPlugins::SetClumpAlpha(man->GetClump(), 0); if (((CPedModelInfo*)CModelInfo::GetModelInfo(womanModel))->GetRwObject()) { CPed* woman = AddPed(PEDTYPE_CIVFEMALE, womanModel, correctedManPos); // will set the correct position later if (woman) { woman->SetObjective(OBJECTIVE_FOLLOW_CHAR_IN_FORMATION, man); woman->SetFormation(FORMATION_RIGHT); CVector formationPos = woman->GetFormationPosition(); CVector womanPos = formationPos; bool womanFoundGround; float formationGroundZ = CWorld::FindGroundZFor3DCoord(formationPos.x, formationPos.y, 1.0f + formationPos.z, &womanFoundGround) + 1.0f; if (womanFoundGround) { CVector correctedWomanPos = womanPos; correctedWomanPos.z = Max(womanPos.z, formationGroundZ); woman->SetPosition(correctedWomanPos); // What's the point of this?? CEntity* obstacles[3]; memcpy(obstacles, gCoupleObstacles, sizeof(gCoupleObstacles)); CPedPlacement::IsPositionClearForPed(womanPos, CModelInfo::GetModelInfo(womanModel)->GetColModel()->boundingSphere.radius, ARRAY_SIZE(obstacles), obstacles); for (int i = 0; i < ARRAY_SIZE(obstacles); i++) { CEntity *obstacle = obstacles[i]; if (obstacle) { // We found a real obstacle, so let's break and we can delete them... if (obstacle != man && obstacle != woman) break; } if (i == ARRAY_SIZE(obstacles) - 1) { CVisibilityPlugins::SetClumpAlpha(woman->GetClump(), 0); return; } } } RemovePed(woman); RemovePed(man); } } } } } } } } // Mostly copy paste of PlaceGangMembersInFormation. void CPopulation::PlaceMallPedsAsStationaryGroup(CVector const& coors, int32 group) { #ifdef FIX_BUGS CPed *createdPeds[6]; #else CPed *createdPeds[5]; #endif if (CGame::currArea != AREA_MALL) return; int pedAmount = CGeneral::GetRandomNumberInRange(0, 4) + 3; float circleSector = TWOPI / pedAmount; float circleR = Sqrt(0.5f / (1.0f - Cos(circleSector))); if (!TheCamera.IsSphereVisible(coors, circleR) || MIN_CREATION_DIST * PedCreationDistMultiplier() <= (coors - FindPlayerPed()->GetPosition()).Magnitude2D()) { if (CPedPlacement::IsPositionClearForPed(coors, circleR, -1, 0)) { int pedIdx = 0; CVector leaderPos; for (int i = 0; i < pedAmount; i++) { float angleMult = i + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); float randomR = circleR + CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * circleR; float xOffset = randomR * Cos(angleMult * circleSector); float yOffset = randomR * Sin(angleMult * circleSector); bool foundGround; float groundZ = CWorld::FindGroundZFor3DCoord(xOffset + coors.x, yOffset + coors.y, coors.z + 1.0, &foundGround) + 1.0f; if (foundGround) { CVector finalPos(coors.x + xOffset, coors.y + yOffset, coors.z > groundZ ? coors.z : groundZ); if (i == 0) leaderPos = finalPos; int pedModel = ChooseCivilianOccupation(group); CPedModelInfo *pedModelInfo = (CPedModelInfo*)CModelInfo::GetModelInfo(pedModel); if (pedModelInfo->GetRwObject()) { CEntity* obstacles[6] = { nil, nil, nil, nil, nil, nil }; CPedPlacement::IsPositionClearForPed(finalPos, CModelInfo::GetModelInfo(pedModel)->GetColModel()->boundingSphere.radius, ARRAY_SIZE(obstacles), obstacles); bool foundObstacle = false; for (int m = 0; m < ARRAY_SIZE(obstacles); m++) { CEntity* obstacle = obstacles[m]; if (obstacle) { int n = 0; bool obstacleIsHarmless = false; for (int n = 0; n < pedIdx; n++) { if (obstacle == createdPeds[n]) obstacleIsHarmless = true; } if (!obstacleIsHarmless) { foundObstacle = true; break; } } } bool memberCanSeeLeader = i == 0 ? true : CWorld::GetIsLineOfSightClear(finalPos, leaderPos, true, false, false, false, false, false, false); bool notTooCloseToLeader = i == 0 ? true : !(Abs(finalPos.z - leaderPos.z) < 1.0f); if (!foundObstacle && memberCanSeeLeader && notTooCloseToLeader) { CPed *newPed = AddPed(pedModelInfo->m_pedType, pedModel, finalPos); if (newPed) { createdPeds[pedIdx++] = newPed; float angle = CGeneral::GetRadianAngleBetweenPoints( coors.x, coors.y, finalPos.x, finalPos.y); newPed->m_fRotationDest = angle; newPed->m_fRotationCur = angle; newPed->m_fearFlags = 0; CVisibilityPlugins::SetClumpAlpha(newPed->GetClump(), 0); } // No. #ifndef FIX_BUGS else CWorld::Remove(nil); #endif } } } } if (pedIdx >= 3) { for (int j = 0; j < pedIdx / 2; ++j) { createdPeds[j]->SetChat(createdPeds[pedIdx - 1 - j], 100000); createdPeds[pedIdx - 1 - j]->SetChat(createdPeds[j], 100000); } // Make that extra guy in the middle stand there(PED_UNKNOWN locks him) and do nothing :lmao: if (pedIdx % 2 != 0) { CPed *tmim = createdPeds[(pedIdx - 1) / 2]; float angle = CGeneral::GetRadianAngleBetweenPoints( tmim->GetPosition().x, tmim->GetPosition().y, createdPeds[0]->GetPosition().x, createdPeds[0]->GetPosition().y); tmim->SetHeading(angle); tmim->SetPedState(PED_UNKNOWN); } createdPeds[0]->bIsLeader = true; for (int l = 1; l < pedIdx; ++l) createdPeds[l]->SetLeader(createdPeds[0]); } else { for (int k = 0; k < pedIdx; ++k) { RemovePed(createdPeds[k]); } } } } } ================================================ FILE: src/peds/Population.h ================================================ #pragma once #include "Game.h" #include "PedType.h" class CPed; class CVehicle; class CDummyObject; class CObject; struct PedGroup { int32 models[NUMMODELSPERPEDGROUP]; }; class CPopulation { public: static PedGroup ms_pPedGroups[NUMPEDGROUPS]; static bool ms_bGivePedsWeapons; static int32 m_AllRandomPedsThisType; static float PedDensityMultiplier; static uint32 ms_nTotalMissionPeds; static int32 MaxNumberOfPedsInUse; static int32 MaxNumberOfPedsInUseInterior; static uint32 ms_nNumCivMale; static uint32 ms_nNumCivFemale; static uint32 ms_nNumCop; static bool bZoneChangeHasHappened; static uint32 ms_nNumEmergency; static int8 m_CountDownToPedsAtStart; static uint32 ms_nNumGang1; static uint32 ms_nNumGang2; static uint32 ms_nTotalPeds; static uint32 ms_nNumGang3; static uint32 ms_nTotalGangPeds; static uint32 ms_nNumGang4; static uint32 ms_nTotalCivPeds; static uint32 ms_nNumGang5; static uint32 ms_nNumDummy; static uint32 ms_nNumGang6; static uint32 ms_nNumGang9; static uint32 ms_nNumGang7; static uint32 ms_nNumGang8; static uint32 ms_nTotalCarPassengerPeds; static uint32 NumMiamiViceCops; static void Initialise(); static void Update(bool); static void LoadPedGroups(); static void UpdatePedCount(ePedType, bool); static void DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool); static CPed *AddPedInCar(CVehicle *car, bool isDriver); static void RemovePed(CPed *ent); static int32 ChooseCivilianOccupation(int32); static int32 ChooseNextCivilianOccupation(int32); static void ChooseCivilianCoupleOccupations(int32, int32&, int32&); static int32 ChoosePolicePedOccupation(); static int32 ChooseGangOccupation(int); static void GeneratePedsAtStartOfGame(); static float PedCreationDistMultiplier(); static CPed *AddPed(ePedType pedType, uint32 mi, CVector const &coors, int32 modifier = 0); static void AddToPopulation(float, float, float, float); static void ManagePopulation(void); static void MoveCarsAndPedsOutOfAbandonedZones(void); static void ConvertToRealObject(CDummyObject*); static void ConvertToDummyObject(CObject*); static void ConvertAllObjectsToDummyObjects(void); static bool TestRoomForDummyObject(CObject*); static bool TestSafeForRealObject(CDummyObject*); static bool IsSkateable(CVector const&); static bool CanJeerAtStripper(int32 model); static void RemovePedsIfThePoolGetsFull(void); static bool IsMale(int32); static bool IsFemale(int32); static bool IsSunbather(int32); static int32 ComputeRandomisedGangSize(void); static bool CanSolicitPlayerInCar(int32); static bool CanSolicitPlayerOnFoot(int32); static bool IsSecurityGuard(ePedType); static void PlaceGangMembers(ePedType, int32, CVector const&); static void PlaceGangMembersInFormation(ePedType, int32, CVector const&); static void PlaceGangMembersInCircle(ePedType, int32, CVector const&); static void PlaceCouple(ePedType, int32, ePedType, int32, CVector); static void PlaceMallPedsAsStationaryGroup(CVector const&, int32); static CPed* AddDeadPedInFrontOfCar(const CVector& pos, CVehicle* pCulprit); }; ================================================ FILE: src/render/2dEffect.h ================================================ #pragma once enum { EFFECT_LIGHT, EFFECT_PARTICLE, EFFECT_ATTRACTOR, EFFECT_PED_ATTRACTOR, EFFECT_SUNGLARE }; enum { LIGHT_ON, LIGHT_ON_NIGHT, LIGHT_FLICKER, LIGHT_FLICKER_NIGHT, LIGHT_FLASH1, LIGHT_FLASH1_NIGHT, LIGHT_FLASH2, LIGHT_FLASH2_NIGHT, LIGHT_FLASH3, LIGHT_FLASH3_NIGHT, LIGHT_RANDOM_FLICKER, LIGHT_RANDOM_FLICKER_NIGHT, LIGHT_SPECIAL, LIGHT_BRIDGE_FLASH1, LIGHT_BRIDGE_FLASH2, }; enum { ATTRACTORTYPE_ICECREAM, ATTRACTORTYPE_STARE }; enum { LIGHTFLAG_LOSCHECK = 1, // same order as CPointLights flags, must start at 2 LIGHTFLAG_FOG_NORMAL = 2, // can have light and fog LIGHTFLAG_FOG_ALWAYS = 4, // fog only LIGHTFLAG_HIDE_OBJECT = 8, // hide the object instead of rendering light (???) LIGHTFLAG_LONG_DIST = 16, LIGHTFLAG_FOG = (LIGHTFLAG_FOG_NORMAL|LIGHTFLAG_FOG_ALWAYS) }; class C2dEffect { public: struct Light { float dist; float range; // of pointlight float size; float shadowSize; uint8 lightType; // LIGHT_ uint8 roadReflection; uint8 flareType; uint8 shadowIntensity; uint8 flags; // LIGHTFLAG_ RwTexture *corona; RwTexture *shadow; }; struct Particle { int particleType; CVector dir; float scale; }; struct Attractor { CVector dir; int8 type; uint8 probability; }; struct PedAttractor { CVector queueDir; CVector useDir; int8 type; }; CVector pos; CRGBA col; uint8 type; union { Light light; Particle particle; Attractor attractor; PedAttractor pedattr; }; C2dEffect(void) {} void Shutdown(void){ if(type == EFFECT_LIGHT){ if(light.corona) RwTextureDestroy(light.corona); light.corona = nil; if(light.shadow) RwTextureDestroy(light.shadow); light.shadow = nil; } } }; VALIDATE_SIZE(C2dEffect, 0x34); ================================================ FILE: src/render/Antennas.cpp ================================================ #include "common.h" #include "main.h" #include "Antennas.h" CAntenna CAntennas::aAntennas[NUMANTENNAS]; void CAntennas::Init(void) { int i; for(i = 0; i < NUMANTENNAS; i++){ aAntennas[i].active = false; aAntennas[i].updatedLastFrame = false; } } // Free antennas that aren't used anymore void CAntennas::Update(void) { int i; for(i = 0; i < NUMANTENNAS; i++){ if(aAntennas[i].active && !aAntennas[i].updatedLastFrame) aAntennas[i].active = false; aAntennas[i].updatedLastFrame = false; } } // Add a new one or update an old one void CAntennas::RegisterOne(uint32 id, CVector dir, CVector position, float length) { int i, j; for(i = 0; i < NUMANTENNAS; i++) if(aAntennas[i].active && aAntennas[i].id == id) break; if(i >= NUMANTENNAS){ // not found, register new one // find empty slot for(i = 0; i < NUMANTENNAS; i++) if(!aAntennas[i].active) break; // there is space if(i < NUMANTENNAS){ aAntennas[i].active = true; aAntennas[i].updatedLastFrame = true; aAntennas[i].id = id; aAntennas[i].segmentLength = length/6.0f; for(j = 0; j < 6; j++){ aAntennas[i].pos[j] = position + dir*j*aAntennas[i].segmentLength; aAntennas[i].speed[j] = CVector(0.0f, 0.0f, 0.0f); } } }else{ // found, update aAntennas[i].Update(dir, position); aAntennas[i].updatedLastFrame = true; } } static RwIm3DVertex vertexbufferA[2]; void CAntennas::Render(void) { int i, j; PUSH_RENDERGROUP("CAntennas::Render"); for(i = 0; i < NUMANTENNAS; i++){ if(!aAntennas[i].active) continue; RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); for(j = 0; j < 5; j++){ RwIm3DVertexSetRGBA(&vertexbufferA[0], 200, 200, 200, 100); RwIm3DVertexSetPos(&vertexbufferA[0], aAntennas[i].pos[j].x, aAntennas[i].pos[j].y, aAntennas[i].pos[j].z); RwIm3DVertexSetRGBA(&vertexbufferA[1], 200, 200, 200, 100); RwIm3DVertexSetPos(&vertexbufferA[1], aAntennas[i].pos[j+1].x, aAntennas[i].pos[j+1].y, aAntennas[i].pos[j+1].z); // LittleTest(); if(RwIm3DTransform(vertexbufferA, 2, nil, 0)){ RwIm3DRenderLine(0, 1); RwIm3DEnd(); } } } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); POP_RENDERGROUP(); } void CAntenna::Update(CVector dir, CVector basepos) { int i; pos[0] = basepos; pos[1] = basepos + dir*segmentLength; for(i = 2; i < 6; i++){ CVector basedir = pos[i-1] - pos[i-2]; CVector newdir = pos[i] - pos[i-1] + // drag along dir*0.1f + // also drag up a bit for stiffness speed[i]; // and keep moving newdir.Normalise(); newdir *= segmentLength; CVector newpos = pos[i-1] + (basedir + newdir)/2.0f; speed[i] = (newpos - pos[i])*0.9f; pos[i] = newpos; } } ================================================ FILE: src/render/Antennas.h ================================================ #pragma once class CAntenna { public: bool active; bool updatedLastFrame; uint32 id; float segmentLength; CVector pos[6]; CVector speed[6]; void Update(CVector dir, CVector pos); }; class CAntennas { // no need to use game's array static CAntenna aAntennas[NUMANTENNAS]; public: static void Init(void); static void Update(void); static void RegisterOne(uint32 id, CVector dir, CVector position, float length); static void Render(void); }; ================================================ FILE: src/render/Clouds.cpp ================================================ #include "common.h" #include "main.h" #include "Sprite.h" #include "Sprite2d.h" #include "General.h" #include "Game.h" #include "Coronas.h" #include "Camera.h" #include "TxdStore.h" #include "Weather.h" #include "Clock.h" #include "Timer.h" #include "Timecycle.h" #include "Renderer.h" #include "Clouds.h" #define SMALLSTRIPHEIGHT 4.0f #define HORIZSTRIPHEIGHT 48.0f RwTexture *gpCloudTex[5]; float CClouds::CloudRotation; uint32 CClouds::IndividualRotation; float CClouds::ms_cameraRoll; float CClouds::ms_horizonZ; float CClouds::ms_HorizonTilt; CRGBA CClouds::ms_colourTop; CRGBA CClouds::ms_colourBottom; CRGBA CClouds::ms_colourBkGrd; void CClouds::Init(void) { CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle")); gpCloudTex[0] = RwTextureRead("cloud1", nil); gpCloudTex[1] = RwTextureRead("cloud2", nil); gpCloudTex[2] = RwTextureRead("cloud3", nil); gpCloudTex[3] = RwTextureRead("cloudhilit", nil); gpCloudTex[4] = RwTextureRead("cloudmasked", nil); CTxdStore::PopCurrentTxd(); CloudRotation = 0.0f; } void CClouds::Shutdown(void) { RwTextureDestroy(gpCloudTex[0]); gpCloudTex[0] = nil; RwTextureDestroy(gpCloudTex[1]); gpCloudTex[1] = nil; RwTextureDestroy(gpCloudTex[2]); gpCloudTex[2] = nil; RwTextureDestroy(gpCloudTex[3]); gpCloudTex[3] = nil; RwTextureDestroy(gpCloudTex[4]); gpCloudTex[4] = nil; } void CClouds::Update(void) { float s = Sin(TheCamera.Orientation - 0.85f); #ifdef FIX_BUGS CloudRotation += CWeather::Wind*s*0.001f*CTimer::GetTimeStepFix(); IndividualRotation += (CWeather::Wind*CTimer::GetTimeStep()*0.5f + 0.3f*CTimer::GetTimeStepFix()) * 60.0f; #else CloudRotation += CWeather::Wind*s*0.001f; IndividualRotation += (CWeather::Wind*CTimer::GetTimeStep()*0.5f + 0.3f) * 60.0f; #endif } float StarCoorsX[9] = { 0.0f, 0.05f, 0.13f, 0.4f, 0.7f, 0.6f, 0.27f, 0.55f, 0.75f }; float StarCoorsY[9] = { 0.0f, 0.45f, 0.9f, 1.0f, 0.85f, 0.52f, 0.48f, 0.35f, 0.2f }; float StarSizes[9] = { 1.0f, 1.4f, 0.9f, 1.0f, 0.6f, 1.5f, 1.3f, 1.0f, 0.8f }; float LowCloudsX[12] = { 1.0f, 0.7f, 0.0f, -0.7f, -1.0f, -0.7f, 0.0f, 0.7f, 0.8f, -0.8f, 0.4f, -0.4f }; float LowCloudsY[12] = { 0.0f, -0.7f, -1.0f, -0.7f, 0.0f, 0.7f, 1.0f, 0.7f, 0.4f, 0.4f, -0.8f, -0.8f }; float LowCloudsZ[12] = { 0.0f, 1.0f, 0.5f, 0.0f, 1.0f, 0.3f, 0.9f, 0.4f, 1.3f, 1.4f, 1.2f, 1.7f }; float CoorsOffsetX[37] = { 0.0f, 60.0f, 72.0f, 48.0f, 21.0f, 12.0f, 9.0f, -3.0f, -8.4f, -18.0f, -15.0f, -36.0f, -40.0f, -48.0f, -60.0f, -24.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, -30.0f, -20.0f, 10.0f, 30.0f, 0.0f, -100.0f, -100.0f, -100.0f, -100.0f, -100.0f, -100.0f }; float CoorsOffsetY[37] = { 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, -30.0f, 10.0f, -25.0f, -5.0f, 28.0f, -10.0f, 10.0f, 0.0f, 15.0f, 40.0f, -100.0f, -100.0f, -100.0f, -100.0f, -100.0f, -40.0f, -20.0f, 0.0f, 10.0f, 30.0f, 35.0f }; float CoorsOffsetZ[37] = { 2.0f, 1.0f, 0.0f, 0.3f, 0.7f, 1.4f, 1.7f, 0.24f, 0.7f, 1.3f, 1.6f, 1.0f, 1.2f, 0.3f, 0.7f, 1.4f, 0.0f, 0.1f, 0.5f, 0.4f, 0.55f, 0.75f, 1.0f, 1.4f, 1.7f, 2.0f, 2.0f, 2.3f, 1.9f, 2.4f, 2.0f, 2.0f, 1.5f, 1.2f, 1.7f, 1.5f, 2.1f }; uint8 BowRed[6] = { 30, 30, 30, 10, 0, 15 }; uint8 BowGreen[6] = { 0, 15, 30, 30, 0, 0 }; uint8 BowBlue[6] = { 0, 0, 0, 10, 30, 30 }; void CClouds::Render(void) { int i; float szx, szy; RwV3d screenpos; RwV3d worldpos; if(!CGame::CanSeeOutSideFromCurrArea()) return; PUSH_RENDERGROUP("CClouds::Render"); CCoronas::SunBlockedByClouds = false; RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); CSprite::InitSpriteBuffer(); float minute = CClock::GetHours()*60 + CClock::GetMinutes() + CClock::GetSeconds()/60.0f; RwV3d campos = TheCamera.GetPosition(); // Moon float moonfadeout = Abs(minute - 180.0f); // fully visible at 3AM if((int)moonfadeout < 180){ // fade in/out 3 hours float coverage = Max(CWeather::Foggyness, CWeather::CloudCoverage); int brightness = (1.0f - coverage) * (180 - (int)moonfadeout); RwV3d pos = { 0.0f, -100.0f, 15.0f }; RwV3dAdd(&worldpos, &campos, &pos); if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[2])); szx *= CCoronas::MoonSize*2.0f + 4.0f; szy *= CCoronas::MoonSize*2.0f + 4.0f; CSprite::RenderOneXLUSprite(screenpos.x, screenpos.y, screenpos.z, szx, szy, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255); } } // The R* logo int starintens = 0; if(CClock::GetHours() < 22 && CClock::GetHours() > 5) starintens = 0; else if(CClock::GetHours() > 22 || CClock::GetHours() < 5) starintens = 255; else if(CClock::GetHours() == 22) starintens = 255 * CClock::GetMinutes()/60.0f; else if(CClock::GetHours() == 5) starintens = 255 * (60 - CClock::GetMinutes())/60.0f; if(starintens != 0){ float coverage = Max(CWeather::Foggyness, CWeather::CloudCoverage); int brightness = (1.0f - coverage) * starintens; // R RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0])); for(i = 0; i < 11; i++){ RwV3d pos = { 100.0f, 0.0f, 10.0f }; if(i >= 9) pos.x = -pos.x; RwV3dAdd(&worldpos, &campos, &pos); worldpos.y -= 90.0f*StarCoorsX[i%9]; worldpos.z += 80.0f*StarCoorsY[i%9]; if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ float sz = 0.8f*StarSizes[i%9]; CSprite::RenderBufferedOneXLUSprite(screenpos.x, screenpos.y, screenpos.z, szx*sz, szy*sz, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255); } } CSprite::FlushSpriteBuffer(); // * RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0])); RwV3d pos = { 100.0f, 0.0f, 10.0f }; RwV3dAdd(&worldpos, &campos, &pos); worldpos.y -= 90.0f; if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ brightness *= (CGeneral::GetRandomNumber()&127) / 640.0f + 0.5f; CSprite::RenderOneXLUSprite(screenpos.x, screenpos.y, screenpos.z, szx*5.0f, szy*5.0f, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255); } } // Low clouds float lowcloudintensity = 1.0f - Max(Max(CWeather::Foggyness, CWeather::CloudCoverage), CWeather::ExtraSunnyness); int r = CTimeCycle::GetLowCloudsRed() * lowcloudintensity; int g = CTimeCycle::GetLowCloudsGreen() * lowcloudintensity; int b = CTimeCycle::GetLowCloudsBlue() * lowcloudintensity; for(int cloudtype = 0; cloudtype < 3; cloudtype++){ for(i = cloudtype; i < 12; i += 3){ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCloudTex[cloudtype])); RwV3d pos = { 800.0f*LowCloudsX[i], 800.0f*LowCloudsY[i], 60.0f*LowCloudsZ[i] }; worldpos.x = campos.x + pos.x; worldpos.y = campos.y + pos.y; worldpos.z = 40.0f + pos.z; if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)) CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(screenpos.x, screenpos.y, screenpos.z, szx*320.0f, szy*40.0f, r, g, b, 255, 1.0f/screenpos.z, ms_cameraRoll, 255); } CSprite::FlushSpriteBuffer(); } // Fluffy clouds float rot_sin = Sin(CloudRotation); float rot_cos = Cos(CloudRotation); int fluffyalpha = 160 * (1.0f - Max(CWeather::Foggyness, CWeather::ExtraSunnyness)); if(fluffyalpha != 0){ static bool bCloudOnScreen[37]; float sundist, hilight; RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCloudTex[4])); for(i = 0; i < 37; i++){ RwV3d pos = { 2.0f*CoorsOffsetX[i], 2.0f*CoorsOffsetY[i], 40.0f*CoorsOffsetZ[i] + 40.0f }; worldpos.x = pos.x*rot_cos + pos.y*rot_sin + campos.x; worldpos.y = pos.x*rot_sin - pos.y*rot_cos + campos.y; worldpos.z = pos.z; if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ sundist = Sqrt(sq(screenpos.x-CCoronas::SunScreenX) + sq(screenpos.y-CCoronas::SunScreenY)); int tr = CTimeCycle::GetFluffyCloudsTopRed(); int tg = CTimeCycle::GetFluffyCloudsTopGreen(); int tb = CTimeCycle::GetFluffyCloudsTopBlue(); int br = CTimeCycle::GetFluffyCloudsBottomRed(); int bg = CTimeCycle::GetFluffyCloudsBottomGreen(); int bb = CTimeCycle::GetFluffyCloudsBottomBlue(); int distLimit = (3*SCREEN_WIDTH)/4; if(sundist < distLimit){ hilight = (1.0f - Max(CWeather::Foggyness, CWeather::CloudCoverage)) * (1.0f - sundist/(float)distLimit); tr = tr*(1.0f-hilight) + 255*hilight; tg = tg*(1.0f-hilight) + 190*hilight; tb = tb*(1.0f-hilight) + 190*hilight; br = br*(1.0f-hilight) + 255*hilight; bg = bg*(1.0f-hilight) + 190*hilight; bb = bb*(1.0f-hilight) + 190*hilight; if(sundist < SCREEN_WIDTH/10) CCoronas::SunBlockedByClouds = true; }else hilight = 0.0f; CSprite::RenderBufferedOneXLUSprite_Rotate_2Colours(screenpos.x, screenpos.y, screenpos.z, szx*55.0f, szy*55.0f, tr, tg, tb, br, bg, bb, 0.0f, -1.0f, 1.0f/screenpos.z, (uint16)IndividualRotation/65336.0f * 6.28f + ms_cameraRoll, fluffyalpha); bCloudOnScreen[i] = true; }else bCloudOnScreen[i] = false; } CSprite::FlushSpriteBuffer(); // Highlights RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCloudTex[3])); for(i = 0; i < 37; i++){ RwV3d pos = { 2.0f*CoorsOffsetX[i], 2.0f*CoorsOffsetY[i], 40.0f*CoorsOffsetZ[i] + 40.0f }; worldpos.x = pos.x*rot_cos + pos.y*rot_sin + campos.x; worldpos.y = pos.x*rot_sin + pos.y*rot_cos + campos.y; worldpos.z = pos.z; if(bCloudOnScreen[i] && CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){ if(sundist < SCREEN_WIDTH/3){ CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(screenpos.x, screenpos.y, screenpos.z, szx*30.0f, szy*30.0f, 200*hilight, 0, 0, 255, 1.0f/screenpos.z, 1.7f - CGeneral::GetATanOfXY(screenpos.x-CCoronas::SunScreenX, screenpos.y-CCoronas::SunScreenY) + CClouds::ms_cameraRoll, 255); } } } CSprite::FlushSpriteBuffer(); } // Rainbow if(CWeather::Rainbow != 0.0f){ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0])); for(i = 0; i < 6; i++){ RwV3d pos = { i*1.5f, 100.0f, 5.0f }; RwV3dAdd(&worldpos, &campos, &pos); if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)) CSprite::RenderBufferedOneXLUSprite(screenpos.x, screenpos.y, screenpos.z, 2.0f*szx, 50.0*szy, BowRed[i]*CWeather::Rainbow, BowGreen[i]*CWeather::Rainbow, BowBlue[i]*CWeather::Rainbow, 255, 1.0f/screenpos.z, 255); } CSprite::FlushSpriteBuffer(); } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); POP_RENDERGROUP(); } bool UseDarkBackground(void) { return TheCamera.GetForward().z < -0.9f || gbShowCollisionPolys; } void CClouds::RenderBackground(int16 topred, int16 topgreen, int16 topblue, int16 botred, int16 botgreen, int16 botblue, int16 alpha) { PUSH_RENDERGROUP("CClouds::RenderBackground"); CVector right = CrossProduct(TheCamera.GetUp(), TheCamera.GetForward()); right.Normalise(); float c = right.Magnitude2D(); if(c > 1.0f) c = 1.0f; ms_cameraRoll = Acos(c); if(right.z < 0.0f) ms_cameraRoll = -ms_cameraRoll; ms_HorizonTilt = SCREEN_WIDTH/2.0f * Tan(ms_cameraRoll); if(UseDarkBackground()){ ms_colourTop.r = 50; ms_colourTop.g = 50; ms_colourTop.b = 50; ms_colourTop.a = 255; if(gbShowCollisionPolys){ if(CTimer::GetFrameCounter() & 1){ ms_colourTop.r = 0; ms_colourTop.g = 0; ms_colourTop.b = 0; }else{ ms_colourTop.r = 255; ms_colourTop.g = 255; ms_colourTop.b = 255; } } ms_colourBottom = ms_colourTop; CRect r(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); CSprite2d::DrawRect(r, ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop); }else{ ms_horizonZ = CSprite::CalcHorizonCoors(); int fogr = (topred + 2 * botred) / 3; int fogg = (topgreen + 2 * botgreen) / 3; int fogb = (topblue + 2 * botblue) / 3; // Draw top/bottom gradient float gradheight = SCREEN_HEIGHT/2.0f; ms_colourTop.r = topred; ms_colourTop.g = topgreen; ms_colourTop.b = topblue; ms_colourTop.a = alpha; ms_colourBottom.r = botred; ms_colourBottom.g = botgreen; ms_colourBottom.b = botblue; ms_colourBottom.a = alpha; float botright = ms_horizonZ - ms_HorizonTilt; float botleft = ms_horizonZ + ms_HorizonTilt; float topright = botright - gradheight; float topleft = botleft - gradheight; CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, ms_colourTop, ms_colourTop, ms_colourBottom, ms_colourBottom); // draw the small stripe (whatever it's supposed to be) ms_colourTop.r = fogr; ms_colourTop.g = fogg; ms_colourTop.b = fogb; ms_colourTop.a = alpha; topright = ms_horizonZ - ms_HorizonTilt; topleft = ms_horizonZ + ms_HorizonTilt; botright = topright + SMALLSTRIPHEIGHT; botleft = topleft + SMALLSTRIPHEIGHT; CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, ms_colourTop, ms_colourTop, ms_colourTop, ms_colourTop); // Only top if(ms_horizonZ + ms_HorizonTilt - gradheight > 0.0f || ms_horizonZ - ms_HorizonTilt - gradheight > 0.0f){ ms_colourTop.r = topred; ms_colourTop.g = topgreen; ms_colourTop.b = topblue; ms_colourTop.a = alpha; if(ms_horizonZ - Abs(ms_HorizonTilt) - gradheight > SCREEN_HEIGHT){ // only top is visible topleft = 0.0f; topright = 0.0f; botleft = SCREEN_HEIGHT; botright = SCREEN_HEIGHT; }else{ botright = ms_horizonZ - ms_HorizonTilt - gradheight; botleft = ms_horizonZ + ms_HorizonTilt - gradheight; topright = Min(ms_horizonZ - ms_HorizonTilt - 2*SCREEN_HEIGHT, 0.0f); topleft = Min(ms_horizonZ + ms_HorizonTilt - 2*SCREEN_HEIGHT, 0.0f); } CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, ms_colourTop, ms_colourTop, ms_colourTop, ms_colourTop); } // Set both to fog colour for RenderHorizon ms_colourTop.r = fogr; ms_colourTop.g = fogg; ms_colourTop.b = fogb; ms_colourBottom.r = fogr; ms_colourBottom.g = fogg; ms_colourBottom.b = fogb; } POP_RENDERGROUP(); } void CClouds::RenderHorizon(void) { if(UseDarkBackground()) return; PUSH_RENDERGROUP("CClouds::RenderHorizon"); ms_colourBottom.a = 230; ms_colourTop.a = 80; float topright = ms_horizonZ - ms_HorizonTilt; float topleft = ms_horizonZ + ms_HorizonTilt; float botright = topright + SMALLSTRIPHEIGHT; float botleft = topleft + SMALLSTRIPHEIGHT; CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, ms_colourTop, ms_colourTop, ms_colourBottom, ms_colourBottom); ms_colourBkGrd.r = 128.0f*CTimeCycle::GetAmbientRed(); ms_colourBkGrd.g = 128.0f*CTimeCycle::GetAmbientGreen(); ms_colourBkGrd.b = 128.0f*CTimeCycle::GetAmbientBlue(); ms_colourBkGrd.a = 255; float horzstrip = SCREEN_STRETCH_Y(HORIZSTRIPHEIGHT); topright = botright; topleft = botleft; botright = topright + horzstrip; botleft = topleft + horzstrip; CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, ms_colourBottom, ms_colourBottom, ms_colourBkGrd, ms_colourBkGrd); topright = botright; topleft = botleft; botright = Max(topright, SCREEN_HEIGHT); botleft = Max(topleft, SCREEN_HEIGHT); CSprite2d::DrawAnyRect(0.0f, topleft, SCREEN_WIDTH, topright, 0.0f, botleft, SCREEN_WIDTH, botright, ms_colourBkGrd, ms_colourBkGrd, ms_colourBkGrd, ms_colourBkGrd); POP_RENDERGROUP(); } ================================================ FILE: src/render/Clouds.h ================================================ #pragma once class CClouds { public: static float CloudRotation; static uint32 IndividualRotation; static float ms_cameraRoll; static float ms_horizonZ; static float ms_HorizonTilt; static CRGBA ms_colourTop; static CRGBA ms_colourBottom; static CRGBA ms_colourBkGrd; static void Init(void); static void Shutdown(void); static void Update(void); static void Render(void); static void RenderBackground(int16 topred, int16 topgreen, int16 topblue, int16 botred, int16 botgreen, int16 botblue, int16 alpha); static void RenderHorizon(void); }; ================================================ FILE: src/render/Console.cpp ================================================ #include "common.h" #include #include "Console.h" #include "Font.h" #include "Timer.h" #define CONSOLE_X_POS (30.0f) #define CONSOLE_Y_POS (10.0f) #define CONSOLE_LINE_HEIGHT (12.0f) CConsole TheConsole; void CConsole::AddLine(char *s, uint8 r, uint8 g, uint8 b) { char tempstr[MAX_STR_LEN+1]; while (strlen(s) > MAX_STR_LEN) { strncpy(tempstr, s, MAX_STR_LEN); tempstr[MAX_STR_LEN-1] = '\0'; s += MAX_STR_LEN - 1; AddOneLine(tempstr, r, g, b); } AddOneLine(s, r, g, b); } void CConsole::AddOneLine(char *s, uint8 r, uint8 g, uint8 b) { int32 StrIndex = (m_nLineCount + m_nCurrentLine) % MAX_LINES; for (int32 i = 0; i < MAX_STR_LEN; i++) { Buffers[StrIndex][i] = s[i]; if (s[i] == '\0') break; } uint8 _strNum1 = m_nLineCount; if (_strNum1 < MAX_LINES) _strNum1++; m_aTimer[StrIndex] = CTimer::GetTimeInMilliseconds(); Buffers[StrIndex][MAX_STR_LEN-1] = '\0'; m_aRed[StrIndex] = r; m_aGreen[StrIndex] = g; m_aBlue[StrIndex] = b; if (_strNum1 >= MAX_LINES) m_nCurrentLine = (m_nCurrentLine + 1) % MAX_LINES; else m_nLineCount = _strNum1; } void CConsole::Display() { CFont::SetPropOn(); CFont::SetBackgroundOff(); CFont::SetScale(0.6f, 0.6f); CFont::SetCentreOff(); CFont::SetRightJustifyOff(); CFont::SetJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_STANDARD); #ifndef FIX_BUGS CFont::SetPropOff(); // not sure why this is here anyway #endif CFont::SetWrapx(RsGlobal.width); while (m_nLineCount != 0 && CTimer::GetTimeInMilliseconds() - m_aTimer[m_nCurrentLine] > 20000) { m_nLineCount--; m_nCurrentLine = (m_nCurrentLine + 1) % MAX_LINES; } for (int16 i = 0; i < m_nLineCount; i++) { int16 line = (i + m_nCurrentLine) % MAX_LINES; CFont::SetColor(CRGBA(0, 0, 0, 200)); CFont::PrintString(CONSOLE_X_POS + 1.0f, CONSOLE_Y_POS + 1.0f + i * CONSOLE_LINE_HEIGHT, Buffers[line]); CFont::SetColor(CRGBA(m_aRed[line], m_aGreen[line], m_aBlue[line], 200)); CFont::PrintString(CONSOLE_X_POS, CONSOLE_Y_POS + i * CONSOLE_LINE_HEIGHT, Buffers[line]); } } void cprintf(char* format, ...) { char s[256]; va_list vl1, vl2; va_start(vl1, format); va_copy(vl2, vl1); vsprintf(s, format, vl1); TheConsole.AddLine(s, 255, 255, 128); } ================================================ FILE: src/render/Console.h ================================================ #pragma once class CConsole { enum { MAX_LINES = 8, // BUG? only shows 7 MAX_STR_LEN = 40, }; uint8 m_nLineCount; uint8 m_nCurrentLine; wchar Buffers[MAX_LINES][MAX_STR_LEN]; uint32 m_aTimer[MAX_LINES]; uint8 m_aRed[MAX_LINES]; uint8 m_aGreen[MAX_LINES]; uint8 m_aBlue[MAX_LINES]; public: void AddLine(char *s, uint8 r, uint8 g, uint8 b); void AddOneLine(char *s, uint8 r, uint8 g, uint8 b); void Display(); void Init() { m_nCurrentLine = 0; m_nLineCount = 0; } }; extern CConsole TheConsole; ================================================ FILE: src/render/Coronas.cpp ================================================ #include "common.h" #include "main.h" #include "General.h" #include "Entity.h" #include "RenderBuffer.h" #include "TxdStore.h" #include "Camera.h" #include "Sprite.h" #include "Timer.h" #include "World.h" #include "Weather.h" #include "Collision.h" #include "Timecycle.h" #include "Coronas.h" #include "PointLights.h" #include "Shadows.h" #include "Clock.h" #include "Bridge.h" struct FlareDef { float position; float size; int16 red; int16 green; int16 blue; int16 alpha; int16 texture; }; FlareDef SunFlareDef[] = { { -0.5f, 15.0f, 50, 50, 0, 200, 1 }, { -1.0f, 10.0f, 50, 20, 0, 200, 2 }, { -1.5f, 15.0f, 50, 0, 0, 200, 3 }, { -2.5f, 25.0f, 50, 0, 0, 200, 1 }, { 0.5f, 12.5f, 40, 40, 25, 200, 1 }, { 0.05f, 20.0f, 30, 22, 9, 200, 2 }, { 1.3f, 7.5f, 50, 30, 9, 200, 3 }, { 0.0f, 0.0f, 255, 255, 255, 255, 0 } }; FlareDef HeadLightsFlareDef[] = { { -0.5f, 15.5, 70, 70, 70, 200, 1 }, { -1.0f, 10.0, 70, 70, 70, 200, 2 }, { -1.5f, 5.5f, 50, 50, 50, 200, 3 }, { 0.5f, 12.0f, 50, 50, 50, 200, 1 }, { 0.05f, 20.0f, 40, 40, 40, 200, 2 }, { 1.3f, 8.0f, 60, 60, 60, 200, 3 }, { -2.0f, 12.0f, 50, 50, 50, 200, 1 }, { -2.3f, 15.0f, 40, 40, 40, 200, 2 }, { -3.0f, 16.0f, 40, 40, 40, 200, 3 }, { 0.0f, 0.0f, 255, 255, 255, 255, 0 } }; RwTexture *gpCoronaTexture[9] = { nil, nil, nil, nil, nil, nil, nil, nil, nil }; float CCoronas::LightsMult = 1.0f; float CCoronas::SunScreenX; float CCoronas::SunScreenY; int CCoronas::MoonSize; bool CCoronas::SunBlockedByClouds; int CCoronas::bChangeBrightnessImmediately; CRegisteredCorona CCoronas::aCoronas[NUMCORONAS]; const char aCoronaSpriteNames[][32] = { "coronastar", "corona", "coronamoon", "coronareflect", "coronaheadlightline", "coronahex", "coronacircle", "coronaringa", "streek" }; void CCoronas::Init(void) { int i; CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle")); for(i = 0; i < 9; i++) if(gpCoronaTexture[i] == nil) gpCoronaTexture[i] = RwTextureRead(aCoronaSpriteNames[i], nil); CTxdStore::PopCurrentTxd(); for(i = 0; i < NUMCORONAS; i++) aCoronas[i].id = 0; } void CCoronas::Shutdown(void) { int i; for(i = 0; i < 9; i++) if(gpCoronaTexture[i]){ RwTextureDestroy(gpCoronaTexture[i]); gpCoronaTexture[i] = nil; } } void CCoronas::Update(void) { int i; static int LastCamLook = 0; LightsMult = Min(LightsMult + 0.03f * CTimer::GetTimeStep(), 1.0f); int CamLook = 0; if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) CamLook |= 1; if(TheCamera.Cams[TheCamera.ActiveCam].LookingRight) CamLook |= 2; if(TheCamera.Cams[TheCamera.ActiveCam].LookingBehind) CamLook |= 4; // BUG? if(TheCamera.GetLookDirection() == LOOKING_BEHIND) CamLook |= 8; if(LastCamLook != CamLook) bChangeBrightnessImmediately = 3; else bChangeBrightnessImmediately = Max(bChangeBrightnessImmediately-1, 0); LastCamLook = CamLook; for(i = 0; i < NUMCORONAS; i++) if(aCoronas[i].id != 0) aCoronas[i].Update(); } void CCoronas::RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha, const CVector &coors, float size, float drawDist, RwTexture *tex, int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle, bool useNearDist, float nearDist) { int i; if(sq(drawDist) < (TheCamera.GetPosition() - coors).MagnitudeSqr2D()) return; if(useNearDist){ float dist = (TheCamera.GetPosition() - coors).Magnitude(); if(dist < 35.0f) return; if(dist < 50.0f) alpha *= (dist - 35.0f)/(50.0f - 35.0f); } for(i = 0; i < NUMCORONAS; i++) if(aCoronas[i].id == id) break; if(i == NUMCORONAS){ // add a new one // find empty slot for(i = 0; i < NUMCORONAS; i++) if(aCoronas[i].id == 0) break; if(i == NUMCORONAS) return; // no space aCoronas[i].fadeAlpha = 0; aCoronas[i].offScreen = true; aCoronas[i].firstUpdate = true; aCoronas[i].renderReflection = false; aCoronas[i].lastLOScheck = 0; aCoronas[i].sightClear = false; aCoronas[i].hasValue[0] = false; aCoronas[i].hasValue[1] = false; aCoronas[i].hasValue[2] = false; aCoronas[i].hasValue[3] = false; aCoronas[i].hasValue[4] = false; aCoronas[i].hasValue[5] = false; }else{ // use existing one if(aCoronas[i].fadeAlpha == 0 && alpha == 0){ // unregister aCoronas[i].id = 0; return; } } aCoronas[i].id = id; aCoronas[i].red = red; aCoronas[i].green = green; aCoronas[i].blue = blue; aCoronas[i].alpha = alpha; aCoronas[i].coors = coors; aCoronas[i].size = size; aCoronas[i].someAngle = someAngle; aCoronas[i].registeredThisFrame = true; aCoronas[i].drawDist = drawDist; aCoronas[i].texture = tex; aCoronas[i].flareType = flareType; aCoronas[i].reflection = reflection; aCoronas[i].LOScheck = LOScheck; aCoronas[i].drawStreak = drawStreak; aCoronas[i].useNearDist = useNearDist; aCoronas[i].nearDist = nearDist; } void CCoronas::RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha, const CVector &coors, float size, float drawDist, uint8 type, int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle, bool useNearDist, float nearDist) { RegisterCorona(id, red, green, blue, alpha, coors, size, drawDist, gpCoronaTexture[type], flareType, reflection, LOScheck, drawStreak, someAngle, useNearDist, nearDist); } void CCoronas::UpdateCoronaCoors(uint32 id, const CVector &coors, float drawDist, float someAngle) { int i; if(sq(drawDist) < (TheCamera.GetPosition() - coors).MagnitudeSqr2D()) return; for(i = 0; i < NUMCORONAS; i++) if(aCoronas[i].id == id) break; if(i == NUMCORONAS) return; if(aCoronas[i].fadeAlpha == 0) aCoronas[i].id = 0; // faded out, remove else{ aCoronas[i].coors = coors; aCoronas[i].someAngle = someAngle; } } static RwIm2DVertex vertexbufferX[2]; void CCoronas::Render(void) { int i, j; int screenw, screenh; PUSH_RENDERGROUP("CCoronas::Render"); screenw = RwRasterGetWidth(RwCameraGetRaster(Scene.camera)); screenh = RwRasterGetHeight(RwCameraGetRaster(Scene.camera)); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); for(i = 0; i < NUMCORONAS; i++){ for(j = 5; j > 0; j--){ aCoronas[i].prevX[j] = aCoronas[i].prevX[j-1]; aCoronas[i].prevY[j] = aCoronas[i].prevY[j-1]; aCoronas[i].prevRed[j] = aCoronas[i].prevRed[j-1]; aCoronas[i].prevGreen[j] = aCoronas[i].prevGreen[j-1]; aCoronas[i].prevBlue[j] = aCoronas[i].prevBlue[j-1]; aCoronas[i].hasValue[j] = aCoronas[i].hasValue[j-1]; } aCoronas[i].hasValue[0] = false; if(aCoronas[i].id == 0 || aCoronas[i].fadeAlpha == 0 && aCoronas[i].alpha == 0) continue; CVector spriteCoors; float spritew, spriteh; if(!CSprite::CalcScreenCoors(aCoronas[i].coors, &spriteCoors, &spritew, &spriteh, true)){ aCoronas[i].offScreen = true; aCoronas[i].sightClear = false; }else{ aCoronas[i].offScreen = false; if(spriteCoors.x < 0.0f || spriteCoors.y < 0.0f || spriteCoors.x > screenw || spriteCoors.y > screenh){ aCoronas[i].offScreen = true; aCoronas[i].sightClear = false; }else{ if(CTimer::GetTimeInMilliseconds() > aCoronas[i].lastLOScheck + 2000){ aCoronas[i].lastLOScheck = CTimer::GetTimeInMilliseconds(); aCoronas[i].sightClear = CWorld::GetIsLineOfSightClear( aCoronas[i].coors, TheCamera.Cams[TheCamera.ActiveCam].Source, true, true, false, false, false, true, false); } // add new streak point if(aCoronas[i].sightClear){ aCoronas[i].prevX[0] = spriteCoors.x; aCoronas[i].prevY[0] = spriteCoors.y; aCoronas[i].prevRed[0] = aCoronas[i].red; aCoronas[i].prevGreen[0] = aCoronas[i].green; aCoronas[i].prevBlue[0] = aCoronas[i].blue; aCoronas[i].hasValue[0] = true; } // if distance too big, break streak if(aCoronas[i].hasValue[1]){ if(Abs(aCoronas[i].prevX[0] - aCoronas[i].prevX[1]) > 50.0f || Abs(aCoronas[i].prevY[0] - aCoronas[i].prevY[1]) > 50.0f) aCoronas[i].hasValue[0] = false; } } if(aCoronas[i].fadeAlpha && spriteCoors.z < aCoronas[i].drawDist){ float recipz = 1.0f/spriteCoors.z; float fadeDistance = aCoronas[i].drawDist / 2.0f; float distanceFade = spriteCoors.z < fadeDistance ? 1.0f : 1.0f - (spriteCoors.z - fadeDistance)/fadeDistance; int totalFade = aCoronas[i].fadeAlpha * distanceFade; if(aCoronas[i].LOScheck) RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); else RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); // render corona itself if(aCoronas[i].texture){ 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)); spriteCoors.z -= aCoronas[i].nearDist; if(aCoronas[i].texture == gpCoronaTexture[8]){ // what's this? 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); CSprite::RenderOneXLUSprite(spriteCoors.x, spriteCoors.y, spriteCoors.z, spritew * aCoronas[i].size * wscale, spriteh * aCoronas[i].size * fogscale * hscale, CCoronas::aCoronas[i].red / fogscale, CCoronas::aCoronas[i].green / fogscale, CCoronas::aCoronas[i].blue / fogscale, totalFade, recipz, 255); }else{ CSprite::RenderOneXLUSprite_Rotate_Aspect( spriteCoors.x, spriteCoors.y, spriteCoors.z, spritew * aCoronas[i].size * fogscale, spriteh * aCoronas[i].size * fogscale, CCoronas::aCoronas[i].red / fogscale, CCoronas::aCoronas[i].green / fogscale, CCoronas::aCoronas[i].blue / fogscale, totalFade, recipz, 20.0f * recipz, 255); } } // render flares if(aCoronas[i].flareType != FLARE_NONE){ FlareDef *flare; switch(aCoronas[i].flareType){ case FLARE_SUN: flare = SunFlareDef; break; case FLARE_HEADLIGHTS: flare = HeadLightsFlareDef; break; default: assert(0); } for(; flare->texture; flare++){ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[flare->texture + 4])); CSprite::RenderOneXLUSprite( (spriteCoors.x - (screenw/2)) * flare->position + (screenw/2), (spriteCoors.y - (screenh/2)) * flare->position + (screenh/2), spriteCoors.z, 4.0f*flare->size * spritew/spriteh, 4.0f*flare->size, (flare->red * aCoronas[i].red)>>8, (flare->green * aCoronas[i].green)>>8, (flare->blue * aCoronas[i].blue)>>8, (totalFade * flare->alpha)>>8, recipz, 255); } } } } } RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); // streaks for(i = 0; i < NUMCORONAS; i++){ if(aCoronas[i].id == 0 || !aCoronas[i].drawStreak) continue; for(j = 0; j < 5; j++){ if(!aCoronas[i].hasValue[j] || !aCoronas[i].hasValue[j+1]) continue; int alpha1 = (float)(6 - j) / 6 * 128; int alpha2 = (float)(6 - (j+1)) / 6 * 128; RwIm2DVertexSetScreenX(&vertexbufferX[0], aCoronas[i].prevX[j]); RwIm2DVertexSetScreenY(&vertexbufferX[0], aCoronas[i].prevY[j]); RwIm2DVertexSetIntRGBA(&vertexbufferX[0], aCoronas[i].prevRed[j] * alpha1 / 256, aCoronas[i].prevGreen[j] * alpha1 / 256, aCoronas[i].prevBlue[j] * alpha1 / 256, 255); RwIm2DVertexSetScreenX(&vertexbufferX[1], aCoronas[i].prevX[j+1]); RwIm2DVertexSetScreenY(&vertexbufferX[1], aCoronas[i].prevY[j+1]); RwIm2DVertexSetIntRGBA(&vertexbufferX[1], aCoronas[i].prevRed[j+1] * alpha2 / 256, aCoronas[i].prevGreen[j+1] * alpha2 / 256, aCoronas[i].prevBlue[j+1] * alpha2 / 256, 255); #ifdef FIX_BUGS RwIm2DVertexSetScreenZ(&vertexbufferX[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&vertexbufferX[0], RwCameraGetNearClipPlane(Scene.camera)); RwIm2DVertexSetRecipCameraZ(&vertexbufferX[0], 1.0f/RwCameraGetNearClipPlane(Scene.camera)); RwIm2DVertexSetScreenZ(&vertexbufferX[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&vertexbufferX[1], RwCameraGetNearClipPlane(Scene.camera)); RwIm2DVertexSetRecipCameraZ(&vertexbufferX[1], 1.0f/RwCameraGetNearClipPlane(Scene.camera)); #endif RwIm2DRenderLine(vertexbufferX, 2, 0, 1); } } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); POP_RENDERGROUP(); } void CCoronas::RenderReflections(void) { int i; CColPoint point; CEntity *entity; if(CWeather::WetRoads > 0.0f){ PUSH_RENDERGROUP("CCoronas::RenderReflections"); CSprite::InitSpriteBuffer(); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[3])); for(i = 0; i < NUMCORONAS; i++){ if(aCoronas[i].id == 0 || aCoronas[i].fadeAlpha == 0 && aCoronas[i].alpha == 0 || aCoronas[i].reflection == 0) continue; // check if we want a reflection on this corona if(aCoronas[i].renderReflection){ if(((CTimer::GetFrameCounter() + i) & 0xF) == 0 && CWorld::ProcessVerticalLine(aCoronas[i].coors, -1000.0f, point, entity, true, false, false, false, true, false, nil)) aCoronas[i].heightAboveRoad = aCoronas[i].coors.z - point.point.z; }else{ if(CWorld::ProcessVerticalLine(aCoronas[i].coors, -1000.0f, point, entity, true, false, false, false, true, false, nil)){ aCoronas[i].heightAboveRoad = aCoronas[i].coors.z - point.point.z; aCoronas[i].renderReflection = true; } } // Don't draw if reflection is too high if(aCoronas[i].renderReflection && aCoronas[i].heightAboveRoad < 20.0f){ // don't draw if camera is below road if(CCoronas::aCoronas[i].coors.z - aCoronas[i].heightAboveRoad > TheCamera.GetPosition().z) continue; CVector coors = aCoronas[i].coors; coors.z -= 2.0f*aCoronas[i].heightAboveRoad; CVector spriteCoors; float spritew, spriteh; if(CSprite::CalcScreenCoors(coors, &spriteCoors, &spritew, &spriteh, true)) { float drawDist = 0.75f * aCoronas[i].drawDist; drawDist = Min(drawDist, 55.0f); if(spriteCoors.z < drawDist){ float fadeDistance = drawDist / 2.0f; float distanceFade = spriteCoors.z < fadeDistance ? 1.0f : 1.0f - (spriteCoors.z - fadeDistance)/fadeDistance; distanceFade = clamp(distanceFade, 0.0f, 1.0f); float recipz = 1.0f/RwCameraGetNearClipPlane(Scene.camera); float heightFade = (20.0f - aCoronas[i].heightAboveRoad)/20.0f; int intensity = distanceFade*heightFade * 230.0 * CWeather::WetRoads; CSprite::RenderBufferedOneXLUSprite( #ifdef FIX_BUGS spriteCoors.x, spriteCoors.y, spriteCoors.z, #else spriteCoors.x, spriteCoors.y, RwIm2DGetNearScreenZ(), #endif spritew * aCoronas[i].size * 0.75f, spriteh * aCoronas[i].size * 2.0f, (intensity * CCoronas::aCoronas[i].red)>>8, (intensity * CCoronas::aCoronas[i].green)>>8, (intensity * CCoronas::aCoronas[i].blue)>>8, 255, recipz, 255); } } } } CSprite::FlushSpriteBuffer(); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); POP_RENDERGROUP(); }else{ for(i = 0; i < NUMCORONAS; i++) aCoronas[i].renderReflection = false; } } void CCoronas::RenderSunReflection(void) { float sunZDir = CTimeCycle::GetSunDirection().z; if(sunZDir > -0.05f){ float intensity = (0.3f - Abs(sunZDir - 0.25f))/0.3f * (1.0f - CWeather::CloudCoverage) * (1.0f - CWeather::Foggyness) * (1.0f - CWeather::Wind); if(intensity > 0.0f){ int r = (CTimeCycle::GetSunCoreRed() + CTimeCycle::GetSunCoronaRed())*intensity*0.25f; int g = (CTimeCycle::GetSunCoreGreen() + CTimeCycle::GetSunCoronaGreen())*intensity*0.25f; int b = (CTimeCycle::GetSunCoreBlue() + CTimeCycle::GetSunCoronaBlue())*intensity*0.25f; CVector sunPos = 40.0f*CTimeCycle::GetSunDirection() + TheCamera.GetPosition(); sunPos.z = 0.5f*CWeather::Wind + 6.1f; CVector sunDir = CTimeCycle::GetSunDirection(); sunDir.z = 0.0; sunDir.Normalise(); TempBufferIndicesStored = 6; TempBufferRenderIndexList[0] = 2; TempBufferRenderIndexList[1] = 1; TempBufferRenderIndexList[2] = 0; TempBufferRenderIndexList[3] = 2; TempBufferRenderIndexList[4] = 3; TempBufferRenderIndexList[5] = 1; // 60 unit square in sun direction TempBufferVerticesStored = 4; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[0], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[0], sunPos.x + 30.0f*sunDir.y, sunPos.y - 30.0f*sunDir.x, sunPos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[1], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[1], sunPos.x - 30.0f*sunDir.y, sunPos.y + 30.0f*sunDir.x, sunPos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[2], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[2], sunPos.x + 60.0f*sunDir.x + 30.0f*sunDir.y, sunPos.y + 60.0f*sunDir.y - 30.0f*sunDir.x, sunPos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[3], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[3], sunPos.x + 60.0f*sunDir.x - 30.0f*sunDir.y, sunPos.y + 60.0f*sunDir.y + 30.0f*sunDir.x, sunPos.z); RwIm3DVertexSetU(&TempBufferRenderVertices[0], 0.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[0], 1.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[1], 1.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[1], 1.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[2], 0.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[2], 0.5f); RwIm3DVertexSetU(&TempBufferRenderVertices[3], 1.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[3], 0.5f); int timeInc = 0; int sideInc = 0; int fwdInc = 0; for(int i = 0; i < 20; i++){ TempBufferRenderIndexList[TempBufferIndicesStored + 0] = TempBufferVerticesStored; TempBufferRenderIndexList[TempBufferIndicesStored + 1] = TempBufferVerticesStored-1; TempBufferRenderIndexList[TempBufferIndicesStored + 2] = TempBufferVerticesStored-2; TempBufferRenderIndexList[TempBufferIndicesStored + 3] = TempBufferVerticesStored; TempBufferRenderIndexList[TempBufferIndicesStored + 4] = TempBufferVerticesStored+1; TempBufferRenderIndexList[TempBufferIndicesStored + 5] = TempBufferVerticesStored-1; TempBufferIndicesStored += 6; // What a weird way to do it... float fwdLen = fwdInc/20 + 60; float sideLen = sideInc/20 + 30; sideLen += 10.0f*Sin((float)(CTimer::GetTimeInMilliseconds()+timeInc & 0x7FF)/0x800*TWOPI); timeInc += 900; sideInc += 970; fwdInc += 1440; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+0], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+0], sunPos.x + fwdLen*sunDir.x + sideLen*sunDir.y, sunPos.y + fwdLen*sunDir.y - sideLen*sunDir.x, sunPos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+1], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+1], sunPos.x + fwdLen*sunDir.x - sideLen*sunDir.y, sunPos.y + fwdLen*sunDir.y + sideLen*sunDir.x, sunPos.z); RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored+0], 0.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored+0], 0.5f); RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored+1], 1.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored+1], 0.5f); TempBufferVerticesStored += 2; } RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEFOGTYPE, (void*)rwFOGTYPELINEAR); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[4])); if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; } } } void CCoronas::DoSunAndMoon(void) { // yeah, moon is done somewhere else.... CVector sunCoors = CTimeCycle::GetSunDirection(); sunCoors *= 150.0f; sunCoors += TheCamera.GetPosition(); if(CTimeCycle::GetSunDirection().z > -0.2f){ float size = ((CGeneral::GetRandomNumber()&0xFF) * 0.005f + 10.0f) * CTimeCycle::GetSunSize(); RegisterCorona(SUN_CORE, CTimeCycle::GetSunCoreRed(), CTimeCycle::GetSunCoreGreen(), CTimeCycle::GetSunCoreBlue(), 255, sunCoors, size, 999999.88f, TYPE_STAR, FLARE_NONE, REFLECTION_OFF, LOSCHECK_OFF, STREAK_OFF, 0.0f); if(CTimeCycle::GetSunDirection().z > 0.0f && !CGame::IsInInterior()) RegisterCorona(SUN_CORONA, CTimeCycle::GetSunCoronaRed(), CTimeCycle::GetSunCoronaGreen(), CTimeCycle::GetSunCoronaBlue(), 255, sunCoors, 25.0f * CTimeCycle::GetSunSize(), 999999.88f, TYPE_STAR, FLARE_SUN, REFLECTION_OFF, LOSCHECK_ON, STREAK_OFF, 0.0f); } CVector spriteCoors; float spritew, spriteh; if(CSprite::CalcScreenCoors(sunCoors, &spriteCoors, &spritew, &spriteh, true)) { SunScreenX = spriteCoors.x; SunScreenY = spriteCoors.y; }else{ SunScreenX = 1000000.0f; SunScreenY = 1000000.0f; } } void CRegisteredCorona::Update(void) { if(!registeredThisFrame) alpha = 0; if(LOScheck && (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); }else if(offScreen){ // Same when off screen fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f); }else{ // Visible if(alpha > fadeAlpha){ // fade in 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); } // darken scene when the sun is visible if(id == CCoronas::SUN_CORONA) CCoronas::LightsMult = Max(CCoronas::LightsMult - CTimer::GetTimeStep()*0.06f, 0.6f); } // remove if invisible if(fadeAlpha == 0 && !firstUpdate) id = 0; firstUpdate = false; registeredThisFrame = false; } void CEntity::ProcessLightsForEntity(void) { int i, n; C2dEffect *effect; CVector pos; bool lightOn, lightFlickering; uint32 flashTimer1, flashTimer2, flashTimer3; if(bRenderDamaged || !bIsVisible || GetUp().z < 0.96f) return; flashTimer1 = 0; flashTimer2 = 0; flashTimer3 = 0; n = CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects(); for(i = 0; i < n; i++, flashTimer1 += 0x80, flashTimer2 += 0x100, flashTimer3 += 0x200){ effect = CModelInfo::GetModelInfo(GetModelIndex())->Get2dEffect(i); switch(effect->type){ case EFFECT_LIGHT: pos = GetMatrix() * effect->pos; lightOn = false; lightFlickering = false; switch(effect->light.lightType){ case LIGHT_ON: lightOn = true; break; case LIGHT_ON_NIGHT: if(CClock::GetHours() > 18 || CClock::GetHours() < 7) lightOn = true; break; case LIGHT_FLICKER: if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed) & 0x60) lightOn = true; else lightFlickering = true; if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed) & 3) lightOn = true; break; case LIGHT_FLICKER_NIGHT: if(CClock::GetHours() > 18 || CClock::GetHours() < 7 || CWeather::WetRoads > 0.5f){ if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed) & 0x60) lightOn = true; else lightFlickering = true; if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed) & 3) lightOn = true; } break; case LIGHT_FLASH1: if((CTimer::GetTimeInMilliseconds() + flashTimer1) & 0x200) lightOn = true; break; case LIGHT_FLASH1_NIGHT: if(CClock::GetHours() > 18 || CClock::GetHours() < 7) if((CTimer::GetTimeInMilliseconds() + flashTimer1) & 0x200) lightOn = true; break; case LIGHT_FLASH2: if((CTimer::GetTimeInMilliseconds() + flashTimer2) & 0x400) lightOn = true; break; case LIGHT_FLASH2_NIGHT: if(CClock::GetHours() > 18 || CClock::GetHours() < 7) if((CTimer::GetTimeInMilliseconds() + flashTimer2) & 0x400) lightOn = true; break; case LIGHT_FLASH3: if((CTimer::GetTimeInMilliseconds() + flashTimer3) & 0x800) lightOn = true; break; case LIGHT_FLASH3_NIGHT: if(CClock::GetHours() > 18 || CClock::GetHours() < 7) if((CTimer::GetTimeInMilliseconds() + flashTimer3) & 0x800) lightOn = true; break; case LIGHT_RANDOM_FLICKER: if(m_randomSeed > 16) lightOn = true; else{ if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed*8) & 0x60) lightOn = true; else lightFlickering = true; if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed*8) & 3) lightOn = true; } break; case LIGHT_RANDOM_FLICKER_NIGHT: if(CClock::GetHours() > 18 || CClock::GetHours() < 7){ if(m_randomSeed > 16) lightOn = true; else{ if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed*8) & 0x60) lightOn = true; else lightFlickering = true; if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed*8) & 3) lightOn = true; } } break; case LIGHT_BRIDGE_FLASH1: if(CBridge::ShouldLightsBeFlashing() && CTimer::GetTimeInMilliseconds() & 0x200) lightOn = true; break; case LIGHT_BRIDGE_FLASH2: if(CBridge::ShouldLightsBeFlashing() && (CTimer::GetTimeInMilliseconds() & 0x1FF) < 60) lightOn = true; break; } if(effect->light.flags & LIGHTFLAG_HIDE_OBJECT){ if(lightOn) bDoNotRender = false; else bDoNotRender = true; return; } // Corona if(lightOn) CCoronas::RegisterCorona((uintptr)this + i, effect->col.r, effect->col.g, effect->col.b, 255, pos, effect->light.size, effect->light.dist, effect->light.corona, effect->light.flareType, effect->light.roadReflection, effect->light.flags&LIGHTFLAG_LOSCHECK, CCoronas::STREAK_OFF, 0.0f, !!(effect->light.flags&LIGHTFLAG_LONG_DIST)); else if(lightFlickering) CCoronas::RegisterCorona((uintptr)this + i, 0, 0, 0, 255, pos, effect->light.size, effect->light.dist, effect->light.corona, effect->light.flareType, effect->light.roadReflection, effect->light.flags&LIGHTFLAG_LOSCHECK, CCoronas::STREAK_OFF, 0.0f, !!(effect->light.flags&LIGHTFLAG_LONG_DIST)); // Pointlight bool alreadyProcessedFog; alreadyProcessedFog = false; if(effect->light.range != 0.0f && lightOn){ if(effect->col.r == 0 && effect->col.g == 0 && effect->col.b == 0){ CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), effect->light.range, 0.0f, 0.0f, 0.0f, CPointLights::FOG_NONE, true); }else{ CPointLights::AddLight(CPointLights::LIGHT_POINT, pos, CVector(0.0f, 0.0f, 0.0f), effect->light.range, effect->col.r*CTimeCycle::GetSpriteBrightness()/255.0f, effect->col.g*CTimeCycle::GetSpriteBrightness()/255.0f, effect->col.b*CTimeCycle::GetSpriteBrightness()/255.0f, (effect->light.flags & LIGHTFLAG_FOG) >> 1, true); alreadyProcessedFog = true; } } if(!alreadyProcessedFog){ if(effect->light.flags & LIGHTFLAG_FOG_ALWAYS){ CPointLights::AddLight(CPointLights::LIGHT_FOGONLY_ALWAYS, pos, CVector(0.0f, 0.0f, 0.0f), 0.0f, effect->col.r/255.0f, effect->col.g/255.0f, effect->col.b/255.0f, CPointLights::FOG_ALWAYS, true); }else if(effect->light.flags & LIGHTFLAG_FOG_NORMAL && lightOn && effect->light.range == 0.0f){ CPointLights::AddLight(CPointLights::LIGHT_FOGONLY, pos, CVector(0.0f, 0.0f, 0.0f), 0.0f, effect->col.r/255.0f, effect->col.g/255.0f, effect->col.b/255.0f, CPointLights::FOG_NORMAL, true); } } // Light shadow if(effect->light.shadowSize != 0.0f){ if(lightOn){ CShadows::StoreStaticShadow((uintptr)this + i, SHADOWTYPE_ADDITIVE, effect->light.shadow, &pos, effect->light.shadowSize, 0.0f, 0.0f, -effect->light.shadowSize, 128, effect->col.r*CTimeCycle::GetSpriteBrightness()*effect->light.shadowIntensity/255.0f, effect->col.g*CTimeCycle::GetSpriteBrightness()*effect->light.shadowIntensity/255.0f, effect->col.b*CTimeCycle::GetSpriteBrightness()*effect->light.shadowIntensity/255.0f, 15.0f, 1.0f, 40.0f, false, 0.0f); }else if(lightFlickering){ CShadows::StoreStaticShadow((uintptr)this + i, SHADOWTYPE_ADDITIVE, effect->light.shadow, &pos, effect->light.shadowSize, 0.0f, 0.0f, -effect->light.shadowSize, 0, 0.0f, 0.0f, 0.0f, 15.0f, 1.0f, 40.0f, false, 0.0f); } } break; case EFFECT_SUNGLARE: if(CWeather::SunGlare >= 0.0f){ CVector pos = GetMatrix() * effect->pos; CVector glareDir = pos - GetPosition(); glareDir.Normalise(); CVector camDir = TheCamera.GetPosition() - pos; float dist = camDir.Magnitude(); camDir *= 2.0f/dist; glareDir += camDir; glareDir.Normalise(); float camAngle = -DotProduct(glareDir, CTimeCycle::GetSunDirection()); if(camAngle > 0.0f){ float intens = Sqrt(camAngle) * CWeather::SunGlare; pos += camDir; CCoronas::RegisterCorona((uintptr)this + 33 + i, intens * (CTimeCycle::GetSunCoreRed() + 2*255)/3.0f, intens * (CTimeCycle::GetSunCoreGreen() + 2*255)/3.0f, intens * (CTimeCycle::GetSunCoreBlue() + 2*255)/3.0f, 255, pos, 0.5f*CWeather::SunGlare*Sqrt(dist), 120.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } } break; } } } ================================================ FILE: src/render/Coronas.h ================================================ #pragma once extern RwTexture *gpCoronaTexture[9]; struct CRegisteredCorona { CVector coors; uint32 id; uint32 lastLOScheck; RwTexture *texture; float size; float someAngle; float drawDist; float nearDist; float heightAboveRoad; uint8 red; uint8 green; uint8 blue; uint8 alpha; // alpha when fully visible uint8 fadeAlpha; // actual value used for rendering, faded bool registeredThisFrame; int8 flareType; int8 reflection; uint8 LOScheck : 1; uint8 offScreen : 1; uint8 firstUpdate : 1; uint8 drawStreak : 1; uint8 sightClear : 1; uint8 useNearDist : 1; uint8 renderReflection : 1; int16 prevX[6]; int16 prevY[6]; uint8 prevRed[6]; uint8 prevGreen[6]; uint8 prevBlue[6]; bool hasValue[6]; void Update(void); }; VALIDATE_SIZE(CRegisteredCorona, 0x68); class CCoronas { static CRegisteredCorona aCoronas[NUMCORONAS]; public: enum { SUN_CORE = 1, SUN_CORONA }; enum { TYPE_STAR, TYPE_NORMAL, TYPE_MOON, TYPE_REFLECT, TYPE_HEADLIGHT, TYPE_HEX, TYPE_CIRCLE, TYPE_RING, TYPE_STREAK, }; enum { FLARE_NONE, FLARE_SUN, FLARE_HEADLIGHTS }; enum { REFLECTION_OFF, REFLECTION_ON, }; enum { LOSCHECK_OFF, LOSCHECK_ON, }; enum { STREAK_OFF, STREAK_ON, }; static float LightsMult; static float SunScreenY; static float SunScreenX; static int MoonSize; static bool SunBlockedByClouds; static int bChangeBrightnessImmediately; static void Init(void); static void Shutdown(void); static void Update(void); static void RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha, const CVector &coors, float size, float drawDist, RwTexture *tex, int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle, bool useNearDist = false, float nearDist = 1.5f); static void RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha, const CVector &coors, float size, float drawDist, uint8 type, int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle, bool useNearDist = false, float nearDist = 1.5f); static void UpdateCoronaCoors(uint32 id, const CVector &coors, float drawDist, float someAngle); static void Render(void); static void RenderReflections(void); static void RenderSunReflection(void); static void DoSunAndMoon(void); }; ================================================ FILE: src/render/Credits.cpp ================================================ #include "common.h" #include "Timer.h" #include "Font.h" #include "Frontend.h" #include "RwHelper.h" #include "Camera.h" #include "Text.h" #include "Credits.h" #include "Pad.h" bool CCredits::bCreditsGoing; uint32 CCredits::CreditsStartTime; void CCredits::Init(void) { Stop(); } void CCredits::Start(void) { bCreditsGoing = true; CreditsStartTime = CTimer::GetTimeInMilliseconds(); } void CCredits::Stop(void) { bCreditsGoing = false; } void CCredits::PrintCreditSpace(float space, uint32 &line) { line += space * 25.0f; } void CCredits::PrintCreditText(float scaleX, float scaleY, wchar *text, uint32 &lineoffset, float scrolloffset) { CPad::UpdatePads(); if (CPad::GetPad(0)->GetCrossJustDown()) bCreditsGoing = false; else { float start = DEFAULT_SCREEN_HEIGHT + 20.0f; float y = lineoffset + start - scrolloffset; if (y > 20.0f && DEFAULT_SCREEN_HEIGHT - 20.0f > y) { CFont::SetScale(SCREEN_SCALE_X(scaleX), SCREEN_SCALE_Y(scaleY)); CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString(SCREEN_WIDTH / 2.0f, SCREEN_SCALE_Y(y), (uint16*)text); CFont::SetColor(CRGBA(220, 220, 220, 220)); CFont::PrintString(SCREEN_WIDTH / 2.0f - SCREEN_SCALE_X(1.0f), SCREEN_SCALE_Y(y - 1.0f), (uint16*)text); } lineoffset += scaleY*25.0f; } } void CCredits::Render(void) { uint32 lineoffset; float scrolloffset; if(!bCreditsGoing || FrontEndMenuManager.m_bMenuActive) return; DefinedState(); lineoffset = 0; scrolloffset = (CTimer::GetTimeInMilliseconds() - CreditsStartTime) / 24.0f; CFont::SetJustifyOff(); CFont::SetBackgroundOff(); CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.75f)); CFont::SetCentreOn(); CFont::SetPropOn(); CFont::SetFontStyle(FONT_STANDARD); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED001"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED002"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED003"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED004"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED005"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED006"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED007"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED008"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED025"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED026"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED027"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED028"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED029"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED030"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED031"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD031A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD031B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD031C"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD031D"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD031E"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD024A"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED024"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED023"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD023A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD023B"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED018"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED019"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD018A"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD019A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD019B"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED020"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED021"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD022A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED022"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD022B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD022C"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED032"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED033"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD032A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED034"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED035"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED036"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED037"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD037A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD037B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD037C"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD041B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED042"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED039"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED044"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED040"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD042A"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED142"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD142A"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED009"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED010"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED011"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED012"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED013"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD013A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD013B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD013C"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED089"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED090"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED347"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED047"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED048"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED049"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED348"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED050"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED051"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED052"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED053"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED054"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED055"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED056"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD056A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD056B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD056C"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD056D"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED349"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED350"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED351"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED352"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED353"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED354"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED355"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED356"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED357"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED359"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED360"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED361"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED362"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED363"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED364"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED365"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED366"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED367"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED368"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED369"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED370"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED371"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED372"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED373"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED256"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED257"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED258"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED057"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED058"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD057A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED059"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD060A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD060B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD060C"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD002A"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED003"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD001A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD001B"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED060"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED061"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED062"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED063"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED064"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED069"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED070"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED065"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED066"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED067"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED068"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD071A"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD072A"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED091"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED094"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED095"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED097"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED098"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD098A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD098B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD098C"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED099"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED096"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED273"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD092A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED092"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD092B"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED073"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED074"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED076"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED075"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED077"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED078"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED081"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED082"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED079"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED080"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED083"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED084"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD084A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD084B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD084C"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD084D"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD084E"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED085"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED086"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD086A"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED087"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED088"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088A"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088B"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088C"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088D"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088E"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088F"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD088G"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED107"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED108"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED109"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED110"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD110A"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED111"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED112"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED113"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED114"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED115"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED116"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED117"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED118"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED119"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED120"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED121"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED122"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED123"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED124"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED125"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED126"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED127"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED128"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED129"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.8f)); PrintCreditText(1.1f, 0.8f, TheText.Get("CRD111A"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED130"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED131"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED132"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED133"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED134"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134A"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134B"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134C"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134D"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134E"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134F"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134G"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134H"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD134I"), lineoffset, scrolloffset); CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.7f)); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED135"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD136A"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD137A"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED138"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED066"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD138B"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED139"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(1.1f, 0.8f, TheText.Get("CRED140"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_FRENCH) PrintCreditSpace(0.5f, lineoffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140A"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140B"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140C"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140D"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140E"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140F"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140G"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140H"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140I"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140J"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140K"), lineoffset, scrolloffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD140L"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.85f)); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED259"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED260"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED261"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED262"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED263"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED264"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED265"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED266"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED141"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD141A"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD141B"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED143"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED144"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED145"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED146"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED147"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED148"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED149"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED150"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED151"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED152"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED153"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED154"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED155"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED156"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED157"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED158"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED159"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED160"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED161"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED162"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED163"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED164"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED165"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED166"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED167"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED168"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED169"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED170"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED171"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED172"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.75f)); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED217"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED218"), lineoffset, scrolloffset); PrintCreditSpace(1.0f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRD218A"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED219"), lineoffset, scrolloffset); PrintCreditSpace(1.0f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED220"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED221"), lineoffset, scrolloffset); PrintCreditSpace(1.0f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED222"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED223"), lineoffset, scrolloffset); PrintCreditSpace(1.0f, lineoffset); PrintCreditText(1.1f, 1.1f, TheText.Get("CRED224"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED227"), lineoffset, scrolloffset); PrintCreditSpace(1.0f, lineoffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED228"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED229"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD229A"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD229B"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED274"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED275"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED276"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED277"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED278"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED279"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED280"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED281"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED282"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED283"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED284"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED285"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED286"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED287"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED288"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED289"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED290"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED291"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED292"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED293"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED294"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED295"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED296"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED297"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED298"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED299"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED300"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED301"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED302"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED303"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED304"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED305"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED306"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED307"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED308"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED309"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED310"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED314"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED315"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED316"), lineoffset, scrolloffset); PrintCreditSpace(1.0f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED317"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED318"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED319"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED320"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED321"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED322"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED323"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED324"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED325"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED326"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED327"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED328"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED329"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED330"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED331"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED332"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.8f)); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED333"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED334"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED335"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED336"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED337"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED338"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED339"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED340"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED341"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED342"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditSpace(1.0f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD344A"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED344"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED345"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRD345A"), lineoffset, scrolloffset); PrintCreditSpace(1.0f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED346"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(1.0f, lineoffset); PrintCreditSpace(1.5f, lineoffset); CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH * 0.75f)); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED267"), lineoffset, scrolloffset); PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED268"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED269"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED270"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED271"), lineoffset, scrolloffset); PrintCreditText(0.65f, 0.65f, TheText.Get("CRED272"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditText(0.95f, 0.7f, TheText.Get("CRED230"), lineoffset, scrolloffset); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_ITALIAN) PrintCreditSpace(0.5f, lineoffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED231"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED232"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED233"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED234"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED235"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED236"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED237"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED238"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED239"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED240"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED241"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED242"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED243"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED244"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED245"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED246"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED247"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED248"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED249"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED358"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED250"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED251"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED252"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRD251A"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRD252A"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED253"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED254"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED374"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED375"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED376"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED377"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED378"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED379"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED380"), lineoffset, scrolloffset); PrintCreditText(0.95f, 0.95f, TheText.Get("CRED381"), lineoffset, scrolloffset); PrintCreditSpace(1.5f, lineoffset); PrintCreditSpace(1.5f, lineoffset); CFont::DrawFonts(); #ifdef CUTSCENE_BORDERS_SWITCH if (CMenuManager::m_PrefsCutsceneBorders) #endif if(TheCamera.m_WideScreenOn) TheCamera.DrawBordersForWideScreen(); #ifdef FIX_BUGS if(lineoffset + DEFAULT_SCREEN_HEIGHT - scrolloffset < -10.0f) #else if(lineoffset + SCREEN_HEIGHT - scrolloffset < -10.0f) #endif { bCreditsGoing = false; } } bool CCredits::AreCreditsDone(void) { return !bCreditsGoing; } ================================================ FILE: src/render/Credits.h ================================================ #pragma once class CCredits { static bool bCreditsGoing; static uint32 CreditsStartTime; public: static void Init(void); static void Start(void); static void Stop(void); static bool AreCreditsDone(void); static void Render(void); static void PrintCreditSpace(float space, uint32 &line); static void PrintCreditText(float scaleX, float scaleY, wchar *text, uint32 &lineoffset, float scrolloffset); }; ================================================ FILE: src/render/CutsceneShadow.cpp ================================================ #include "common.h" #include "main.h" #include "rwcore.h" #include "rwplcore.h" #include "CutsceneShadow.h" #include "RwHelper.h" #define DLIGHT_VALUE 0.8f /* Directional light intensity */ CCutsceneShadow::CCutsceneShadow() { m_pAtomic = nil; m_nRwObjectType = -1; m_pLight = nil; m_nBlurPasses = 0; m_bResample = false; m_bGradient = false; } CCutsceneShadow::~CCutsceneShadow() { Destroy(); } bool CCutsceneShadow::Create(RwObject *object, int32 rasterSize, bool resample, int32 blurPasses, bool gradient) { ASSERT(object != nil); RwRGBAReal color; RwFrame *frame; if (!object) return false; m_pLight = RpLightCreate(rpLIGHTDIRECTIONAL); ASSERT(m_pLight != nil); if (!m_pLight) return false; color.red = color.green = color.blue = DLIGHT_VALUE; color.alpha = 0.0f; RpLightSetColor(m_pLight, &color); frame = RwFrameCreate(); ASSERT(frame != nil); RpLightSetFrame(m_pLight, frame); SetLightProperties(180.0f, 90.0f, false); m_pObject = object; m_nRwObjectType = RwObjectGetType(m_pObject); switch ( m_nRwObjectType ) { case rpCLUMP: { RpClumpGetBoundingSphere(m_pClump, &m_BoundingSphere, 1); m_BaseSphere.radius = m_BoundingSphere.radius; RwV3dTransformPoints(&m_BaseSphere.center, &m_BoundingSphere.center, 1, RwFrameGetMatrix(RpClumpGetFrame(m_pClump))); break; } case rpATOMIC: { m_BoundingSphere = *RpAtomicGetBoundingSphere(m_pAtomic); m_BaseSphere.radius = m_BoundingSphere.radius; RwV3dTransformPoints(&m_BaseSphere.center, &m_BoundingSphere.center, 1, RwFrameGetMatrix(RpAtomicGetFrame(m_pAtomic))); break; } default: { Destroy(); return false; break; } } if ( !m_Camera.Create(rasterSize) ) { Destroy(); return false; } m_nBlurPasses = blurPasses; m_bResample = resample; m_bGradient = gradient; if ( m_bResample && !m_ResampleCamera.Create(rasterSize - 1) ) { Destroy(); return false; } if ( m_nBlurPasses != 0 ) { if ( !m_BlurCamera.Create(resample ? rasterSize - 1 : rasterSize) ) { Destroy(); return false; } } if ( m_bGradient ) { if ( !m_GradientCamera.Create(resample ? rasterSize - 1 : rasterSize) ) { Destroy(); return false; } m_GradientCamera.MakeGradientRaster(); } m_Camera.SetLight(m_pLight); switch ( m_nRwObjectType ) { case rpATOMIC: m_Camera.SetFrustum(1.1f * m_BoundingSphere.radius); break; case rpCLUMP: m_Camera.SetFrustum(1.1f * m_BoundingSphere.radius); break; } m_Camera.SetCenter(&m_BaseSphere.center); return true; } RwFrame * CCutsceneShadow::SetLightProperties(float angleY, float angleX, bool setLight) { ASSERT(m_pLight != nil); RwFrame *frame; static RwV3d Xaxis = { 1.0f, 0.0f, 0.0f }; static RwV3d Yaxis = { 0.0f, 1.0f, 0.0f }; frame = RpLightGetFrame(m_pLight); ASSERT(frame != nil); if ( !frame ) return nil; RwFrameRotate(frame, &Yaxis, angleY, rwCOMBINEREPLACE); RwFrameRotate(frame, &Xaxis, angleX, rwCOMBINEPOSTCONCAT); if ( setLight ) m_Camera.SetLight(m_pLight); return frame; } bool CCutsceneShadow::IsInitialized() { return m_pObject != nil; } void CCutsceneShadow::Destroy() { m_Camera.Destroy(); m_ResampleCamera.Destroy(); m_BlurCamera.Destroy(); m_GradientCamera.Destroy(); m_pAtomic = nil; m_nRwObjectType = -1; if (m_pLight) { RwFrame *frame = RpLightGetFrame(m_pLight); RpLightSetFrame(m_pLight, nil); RwFrameDestroy(frame); RpLightDestroy(m_pLight); m_pLight = nil; } } RwRaster * CCutsceneShadow::Update() { switch ( m_nRwObjectType ) { case rpCLUMP: ASSERT(m_pClump != nil); RwV3dTransformPoints(&m_BaseSphere.center, &m_BoundingSphere.center, 1, RwFrameGetMatrix(RpClumpGetFrame(m_pClump))); break; case rpATOMIC: ASSERT(m_pAtomic != nil); RwV3dTransformPoints(&m_BaseSphere.center, &m_BoundingSphere.center, 1, RwFrameGetMatrix(RpAtomicGetFrame(m_pAtomic))); break; } m_Camera.SetCenter(&m_BaseSphere.center); switch ( m_nRwObjectType ) { case rpCLUMP: m_Camera.Update(m_pClump); break; case rpATOMIC: m_Camera.Update(m_pAtomic); break; } RwRaster *raster = m_Camera.GetRwRenderRaster(); ASSERT(raster != nil); if ( m_bResample ) return m_ResampleCamera.RasterResample(raster); if ( m_nBlurPasses ) return m_BlurCamera.RasterBlur(raster, m_nBlurPasses); if ( m_bGradient ) return m_GradientCamera.RasterGradient(raster); return raster; } RwTexture * CCutsceneShadow::UpdateForCutscene() { Update(); return GetShadowRwTexture(); } CShadowCamera * CCutsceneShadow::GetShadowCamera(int32 camType) { switch ( camType ) { case RESAMPLE: return &m_ResampleCamera; case BLUR: return &m_BlurCamera; case GRADIENT: return &m_GradientCamera; } return &m_Camera; } RwTexture * CCutsceneShadow::GetShadowRwTexture() { if ( m_bResample ) return m_ResampleCamera.GetRwRenderTexture(); else return m_Camera.GetRwRenderTexture(); } void CCutsceneShadow::DrawBorderAroundTexture(RwRGBA const& color) { if ( m_bResample ) m_ResampleCamera.DrawOutlineBorder(color); else m_Camera.DrawOutlineBorder(color); } ================================================ FILE: src/render/CutsceneShadow.h ================================================ #pragma once #include "ShadowCamera.h" class CCutsceneShadow { public: enum { RASTER = 0, RESAMPLE, BLUR, GRADIENT, }; CShadowCamera m_Camera; bool m_bResample; CShadowCamera m_ResampleCamera; int32 m_nBlurPasses; CShadowCamera m_BlurCamera; bool m_bGradient; CShadowCamera m_GradientCamera; union { RwObject *m_pObject; RpAtomic *m_pAtomic; RpClump *m_pClump; }; int32 m_nRwObjectType; RpLight *m_pLight; RwSphere m_BoundingSphere; RwSphere m_BaseSphere; CCutsceneShadow(); ~CCutsceneShadow(); RwSphere GetBaseSphere() { return m_BaseSphere; } bool Create(RwObject *object, int32 rasterSize, bool resample, int32 blurPasses, bool gradient); RwFrame *SetLightProperties(float angleY, float angleX, bool setLight); bool IsInitialized(); void Destroy(); RwRaster *Update(); RwTexture *UpdateForCutscene(); CShadowCamera *GetShadowCamera(int32 camType = RASTER); RwTexture *GetShadowRwTexture(); void DrawBorderAroundTexture(RwRGBA const& color); }; ================================================ FILE: src/render/Draw.cpp ================================================ #include "common.h" #include "Draw.h" #include "Frontend.h" #include "Camera.h" #include "CutsceneMgr.h" float CDraw::ms_fAspectRatio = DEFAULT_ASPECT_RATIO; #ifdef ASPECT_RATIO_SCALE float CDraw::ms_fScaledFOV = 45.0f; #endif float CDraw::ms_fNearClipZ; float CDraw::ms_fFarClipZ; float CDraw::ms_fFOV = 45.0f; float CDraw::ms_fLODDistance; uint8 CDraw::FadeValue; uint8 CDraw::FadeRed; uint8 CDraw::FadeGreen; uint8 CDraw::FadeBlue; #ifdef PROPER_SCALING bool CDraw::ms_bProperScaling = true; #endif #ifdef FIX_RADAR bool CDraw::ms_bFixRadar = true; #endif #ifdef FIX_SPRITES bool CDraw::ms_bFixSprites = true; #endif #ifdef ASPECT_RATIO_SCALE float FindAspectRatio(void) { switch (FrontEndMenuManager.m_PrefsUseWideScreen) { case AR_AUTO: return SCREEN_WIDTH / SCREEN_HEIGHT; default: case AR_4_3: return 4.0f / 3.0f; case AR_5_4: return 5.0f / 4.0f; case AR_16_10: return 16.0f / 10.0f; case AR_16_9: return 16.0f / 9.0f; case AR_21_9: return 21.0f / 9.0f; }; } #endif float CDraw::CalculateAspectRatio(void) { #ifdef ASPECT_RATIO_SCALE if (TheCamera.m_WideScreenOn) CDraw::ms_fAspectRatio = (5.f / 3.f) * FindAspectRatio() / (16.f / 9.f); // It's used on theatrical showings according to Wiki else CDraw::ms_fAspectRatio = FindAspectRatio(); #else if(FrontEndMenuManager.m_PrefsUseWideScreen) { if (TheCamera.m_WideScreenOn) CDraw::ms_fAspectRatio = 5.f / 3.f; // It's used on theatrical showings according to Wiki else CDraw::ms_fAspectRatio = 16.f / 9.f; } else if (TheCamera.m_WideScreenOn) { CDraw::ms_fAspectRatio = 5.f/4.f; } else { CDraw::ms_fAspectRatio = 4.f/3.f; } #endif return CDraw::ms_fAspectRatio; } #ifdef ASPECT_RATIO_SCALE // convert a 4:3 hFOV to vFOV, // then convert that vFOV to hFOV for our aspect ratio, // i.e. HOR+ float CDraw::ConvertFOV(float hfov) { // => tan(hFOV/2) = tan(vFOV/2)*aspectRatio // => tan(vFOV/2) = tan(hFOV/2)/aspectRatio float ar1 = DEFAULT_ASPECT_RATIO; float ar2 = GetAspectRatio(); hfov = DEGTORAD(hfov); float vfov = Atan(tan(hfov/2) / ar1) *2; hfov = Atan(tan(vfov/2) * ar2) *2; return RADTODEG(hfov); } #endif void CDraw::SetFOV(float fov) { #ifdef ASPECT_RATIO_SCALE if (!CCutsceneMgr::IsRunning()) ms_fScaledFOV = ConvertFOV(fov); else ms_fScaledFOV = fov; #endif ms_fFOV = fov; } #ifdef PROPER_SCALING float CDraw::ScaleY(float y) { return ms_bProperScaling ? y : y * ((float)DEFAULT_SCREEN_HEIGHT/SCREEN_HEIGHT_NTSC); } #endif ================================================ FILE: src/render/Draw.h ================================================ #pragma once enum eAspectRatio { // Make sure these work the same as FrontEndMenuManager.m_PrefsUseWideScreen // without widescreen support AR_AUTO, AR_4_3, AR_5_4, AR_16_10, AR_16_9, AR_21_9, AR_MAX, }; class CDraw { private: static float ms_fNearClipZ; static float ms_fFarClipZ; static float ms_fFOV; // we use this variable to scale a lot of 2D elements // so better cache it static float ms_fAspectRatio; #ifdef ASPECT_RATIO_SCALE // similar thing for 3D rendering static float ms_fScaledFOV; #endif public: static float ms_fLODDistance; // set but unused? static uint8 FadeValue; static uint8 FadeRed; static uint8 FadeGreen; static uint8 FadeBlue; #ifdef PROPER_SCALING static bool ms_bProperScaling; #endif #ifdef FIX_RADAR static bool ms_bFixRadar; #endif #ifdef FIX_SPRITES static bool ms_bFixSprites; #endif static void SetNearClipZ(float nearclip) { ms_fNearClipZ = nearclip; } static float GetNearClipZ(void) { return ms_fNearClipZ; } static void SetFarClipZ(float farclip) { ms_fFarClipZ = farclip; } static float GetFarClipZ(void) { return ms_fFarClipZ; } static void SetFOV(float fov); static float GetFOV(void) { return ms_fFOV; } #ifdef ASPECT_RATIO_SCALE static float GetScaledFOV(void) { return ms_fScaledFOV; } #else static float GetScaledFOV(void) { return ms_fFOV; } #endif static float CalculateAspectRatio(void); #ifdef ASPECT_RATIO_SCALE static float ConvertFOV(float fov); #endif static float GetAspectRatio(void) { return ms_fAspectRatio; } static void SetAspectRatio(float ratio) { ms_fAspectRatio = ratio; } #ifdef PROPER_SCALING static float ScaleY(float y); #endif }; ================================================ FILE: src/render/Fluff.cpp ================================================ #include "common.h" #include "main.h" #include "RenderBuffer.h" #include "Entity.h" #include "Fluff.h" #include "Camera.h" #include "Sprite.h" #include "Coronas.h" #include "PointLights.h" #include "Rubbish.h" #include "Timecycle.h" #include "General.h" #include "Timer.h" #include "Clock.h" #include "Weather.h" #include "Stats.h" #include "maths.h" #include "Frontend.h" #include "CutsceneMgr.h" #include "PlayerPed.h" #include "Bones.h" #include "World.h" #include "Replay.h" #include "Coronas.h" CPlaneTrail CPlaneTrails::aArray[6]; RwImVertexIndex TrailIndices[32] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16 }; void CPlaneTrail::Init(void) { int i; for(i = 0; i < ARRAY_SIZE(m_time); i++) m_time[i] = 0; } void CPlaneTrail::Render(float visibility) { int i; int numVerts = 0; if(!TheCamera.IsSphereVisible(m_pos[0], 1000.0f)) return; int alpha = visibility*110.0f; if(alpha == 0) return; for(i = 0; i < ARRAY_SIZE(m_pos); i++){ int32 time = CTimer::GetTimeInMilliseconds() - m_time[i]; if(time > 30000) m_time[i] = 0; if(m_time[i] != 0){ float fade = (30000.0f - time) / 10000.0f; fade = Min(fade, 1.0f); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[numVerts], 255, 255, 255, (int)(alpha*fade)); RwIm3DVertexSetPos(&TempBufferRenderVertices[numVerts], m_pos[i].x, m_pos[i].y, m_pos[i].z); numVerts++; } } if(numVerts > 1){ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); if(RwIm3DTransform(TempBufferRenderVertices, numVerts, nil, rwIM3D_VERTEXXYZ|rwIM3D_VERTEXRGBA)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPELINELIST, TrailIndices, (numVerts-1)*2); RwIm3DEnd(); } } } void CPlaneTrail::RegisterPoint(CVector pos) { int i; bool bNewPoint = false; if(m_time[0] != 0 && CTimer::GetTimeInMilliseconds() - m_time[0] > 2000){ bNewPoint = true; for(i = ARRAY_SIZE(m_pos)-1; i > 0; i--){ m_pos[i] = m_pos[i-1]; m_time[i] = m_time[i-1]; } } m_pos[0] = pos; if(bNewPoint || m_time[0] == 0) m_time[0] = CTimer::GetTimeInMilliseconds(); } void CPlaneTrails::Init(void) { int i; for(i = 0; i < ARRAY_SIZE(aArray); i++) aArray[i].Init(); } void CPlaneTrails::Update(void) { CVector planePos; planePos.x = 1590.0f * Sin((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); planePos.y = 1200.0f * Cos((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); planePos.z = 550.0f; RegisterPoint(planePos, 3); if(CClock::GetHours() > 22 || CClock::GetHours() < 7){ if(CTimer::GetTimeInMilliseconds() & 0x200) CCoronas::RegisterCorona(101, 255, 0, 0, 255, planePos, 5.0f, 2000.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); else CCoronas::UpdateCoronaCoors(101, planePos, 2000.0f, 0.0f); } planePos.x = 1000.0f * Sin((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); planePos.y = -1600.0f * Cos((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); planePos.z = 500.0f; RegisterPoint(planePos, 4); if(CClock::GetHours() > 22 || CClock::GetHours() < 7){ if(CTimer::GetTimeInMilliseconds() & 0x200) CCoronas::RegisterCorona(102, 255, 0, 0, 255, planePos, 5.0f, 2000.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); else CCoronas::UpdateCoronaCoors(102, planePos, 2000.0f, 0.0f); } planePos.x = 1100.0f * Cos((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); planePos.y = 700.0f * Sin((float)(CTimer::GetTimeInMilliseconds() & 0x1FFFF)/0x20000 * TWOPI); planePos.z = 600.0f; RegisterPoint(planePos, 5); if(CClock::GetHours() > 22 || CClock::GetHours() < 7){ if(CTimer::GetTimeInMilliseconds() & 0x200) CCoronas::RegisterCorona(103, 255, 0, 0, 255, planePos, 5.0f, 2000.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); else CCoronas::UpdateCoronaCoors(103, planePos, 2000.0f, 0.0f); } } void CPlaneTrails::Render(void) { int i; float visibility = Min(1.0f-CWeather::Foggyness, 1.0f-CWeather::CloudCoverage); visibility = Min(visibility, 1.0f-CWeather::Rain); visibility = Min(Max(Max(CTimeCycle::GetSkyTopRed(), CTimeCycle::GetSkyTopGreen()), CTimeCycle::GetSkyTopBlue())/256.0f, visibility); if(visibility > 0.0001f) for(i = 0; i < ARRAY_SIZE(aArray); i++) aArray[i].Render(visibility); } void CPlaneTrails::RegisterPoint(CVector pos, uint32 id) { aArray[id].RegisterPoint(pos); } CPlaneBanner CPlaneBanners::aArray[5]; void CPlaneBanner::Init(void) { int i; for(i = 0; i < ARRAY_SIZE(m_pos); i++){ m_pos[i].x = i; m_pos[i].y = 0.0f; m_pos[i].z = -60.0f; } } void CPlaneBanner::Update(void) { int i; if(m_pos[0].z > -50.0f){ m_pos[0].z -= 0.05f*CTimer::GetTimeStep(); m_pos[0].z = Max(m_pos[0].z, -100.0f); for(i = 1; i < ARRAY_SIZE(m_pos); i++){ CVector dist = m_pos[i] - m_pos[i-1]; float len = dist.Magnitude(); if(len > 8.0f) m_pos[i] = m_pos[i-1] + dist/len*8.0f; } } } void CPlaneBanner::Render(void) { int i; if(m_pos[0].z > -50.0f){ float camDist = (TheCamera.GetPosition() - m_pos[0]).Magnitude(); if(TheCamera.IsSphereVisible(m_pos[4], 32.0f) && camDist < 300.0f){ TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; int alpha = camDist < 250.0f ? 160 : (300.0f-camDist)/(300.0f-250.0f)*160; TempBufferVerticesStored += 2; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[0], 255, 255, 255, alpha); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[1], 255, 255, 255, alpha); RwIm3DVertexSetPos(&TempBufferRenderVertices[0], m_pos[2].x, m_pos[2].y, m_pos[2].z); RwIm3DVertexSetPos(&TempBufferRenderVertices[1], m_pos[2].x, m_pos[2].y, m_pos[2].z - 4.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[0], 0.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[0], 0.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[1], 0.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[1], 1.0f); for(i = 2; i < 8; i++){ RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+0], 255, 255, 255, alpha); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+1], 255, 255, 255, alpha); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+0], m_pos[i].x, m_pos[i].y, m_pos[i].z); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+1], m_pos[i].x, m_pos[i].y, m_pos[i].z - 4.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored+0], (i-2)/5.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored+0], 0.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored+1], (i-2)/5.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored+1], 1.0f); TempBufferRenderIndexList[TempBufferIndicesStored+0] = TempBufferVerticesStored-2; TempBufferRenderIndexList[TempBufferIndicesStored+1] = TempBufferVerticesStored-1; TempBufferRenderIndexList[TempBufferIndicesStored+2] = TempBufferVerticesStored+1; TempBufferRenderIndexList[TempBufferIndicesStored+3] = TempBufferVerticesStored-2; TempBufferRenderIndexList[TempBufferIndicesStored+4] = TempBufferVerticesStored+1; TempBufferRenderIndexList[TempBufferIndicesStored+5] = TempBufferVerticesStored; TempBufferVerticesStored += 2; TempBufferIndicesStored += 6; } RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpRubbishTexture[2])); #ifdef FIX_BUGS if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXXYZ|rwIM3D_VERTEXUV|rwIM3D_VERTEXRGBA)){ #else if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, 0)){ #endif RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; } } } void CPlaneBanner::RegisterPoint(CVector pos) { m_pos[0] = pos; } void CPlaneBanners::Init(void) { int i; for(i = 0; i < ARRAY_SIZE(aArray); i++) aArray[i].Init(); } void CPlaneBanners::Update(void) { int i; for(i = 0; i < ARRAY_SIZE(aArray); i++) aArray[i].Update(); } void CPlaneBanners::Render(void) { int i; for(i = 0; i < ARRAY_SIZE(aArray); i++) aArray[i].Render(); } void CPlaneBanners::RegisterPoint(CVector pos, uint32 id) { aArray[id].RegisterPoint(pos); } bool CSmokeTrails::CigOn = false; CSmokeTrail CSmokeTrails::aSmoke[3]; RwImVertexIndex SmokeTrailIndices[32] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16 }; float RandomSmoke[16] = { 10.0f, 5.0f, -1.0f, -9.0f, -7.0f, -1.0f, 0.0f, 3.0f, 6.0f, 7.0f, 4.0f, 2.0f, 5.0f, 7.0f }; uint8 ScrollCharSet[59][5] = { { 0x00, 0x00, 0x00, 0x00, 0x00 }, // ' ' { 0x00, 0x00, 0x1D, 0x00, 0x00 }, // '!' { 0x00, 0x00, 0x00, 0x00, 0x00 }, // '"' { 0x0A, 0x1F, 0x0A, 0x1F, 0x0A }, // '#' { 0x00, 0x09, 0x1F, 0x12, 0x00 }, // '$' { 0x18, 0x18, 0x00, 0x03, 0x03 }, // '%' { 0x00, 0x00, 0x00, 0x00, 0x00 }, // '&' { 0x00, 0x00, 0x00, 0x00, 0x00 }, // ''' { 0x01, 0x02, 0x04, 0x08, 0x10 }, // '(' { 0x00, 0x00, 0x18, 0x00, 0x00 }, // ')' { 0x15, 0x04, 0x1F, 0x04, 0x15 }, // '*' { 0x00, 0x04, 0x0E, 0x04, 0x00 }, // '+' { 0x00, 0x00, 0x03, 0x00, 0x00 }, // ',' { 0x00, 0x04, 0x04, 0x04, 0x00 }, // '-' { 0x00, 0x00, 0x01, 0x00, 0x00 }, // '.' { 0x00, 0x00, 0x00, 0x00, 0x00 }, // '/' { 0x0E, 0x11, 0x11, 0x11, 0x0E }, // '0' { 0x01, 0x09, 0x1F, 0x01, 0x01 }, // '1' { 0x03, 0x15, 0x15, 0x15, 0x09 }, // '2' { 0x11, 0x11, 0x15, 0x15, 0x0A }, // '3' { 0x02, 0x06, 0x0A, 0x1F, 0x02 }, // '4' { 0x1D, 0x15, 0x15, 0x15, 0x12 }, // '5' { 0x0E, 0x15, 0x15, 0x15, 0x12 }, // '6' { 0x18, 0x10, 0x13, 0x14, 0x18 }, // '7' { 0x0A, 0x15, 0x15, 0x15, 0x0A }, // '8' { 0x08, 0x15, 0x15, 0x15, 0x0E }, // '9' { 0x00, 0x00, 0x0A, 0x00, 0x00 }, // ':' { 0x18, 0x18, 0x00, 0x03, 0x03 }, // ';' { 0x04, 0x08, 0x1F, 0x08, 0x04 }, // '<' { 0x00, 0x0A, 0x0A, 0x0A, 0x00 }, // '=' { 0x04, 0x02, 0x1F, 0x02, 0x04 }, // '>' { 0x10, 0x10, 0x15, 0x14, 0x1D }, // '?' { 0x00, 0x1C, 0x14, 0x1C, 0x00 }, // '@' { 0x0F, 0x12, 0x12, 0x12, 0x0F }, // 'A' { 0x1F, 0x15, 0x15, 0x15, 0x0A }, // 'B' { 0x0E, 0x11, 0x11, 0x11, 0x0A }, // 'C' { 0x1F, 0x11, 0x11, 0x11, 0x0E }, // 'D' { 0x1F, 0x15, 0x15, 0x11, 0x11 }, // 'E' { 0x1F, 0x14, 0x14, 0x10, 0x10 }, // 'F' { 0x0E, 0x11, 0x15, 0x15, 0x06 }, // 'G' { 0x1F, 0x04, 0x04, 0x04, 0x1F }, // 'H' { 0x11, 0x11, 0x1F, 0x11, 0x11 }, // 'I' { 0x02, 0x01, 0x01, 0x01, 0x1E }, // 'J' { 0x1F, 0x04, 0x0C, 0x12, 0x01 }, // 'K' { 0x1F, 0x01, 0x01, 0x01, 0x01 }, // 'L' { 0x1F, 0x08, 0x06, 0x08, 0x1F }, // 'M' { 0x1F, 0x08, 0x04, 0x02, 0x1F }, // 'N' { 0x0E, 0x11, 0x11, 0x11, 0x0E }, // 'O' { 0x1F, 0x12, 0x12, 0x12, 0x0C }, // 'P' { 0x0C, 0x12, 0x12, 0x13, 0x0D }, // 'Q' { 0x1F, 0x14, 0x14, 0x16, 0x09 }, // 'R' { 0x09, 0x15, 0x15, 0x15, 0x02 }, // 'S' { 0x10, 0x10, 0x1F, 0x10, 0x10 }, // 'T' { 0x1E, 0x01, 0x01, 0x01, 0x1E }, // 'U' { 0x1C, 0x02, 0x01, 0x02, 0x1C }, // 'V' { 0x1E, 0x01, 0x06, 0x01, 0x1E }, // 'W' { 0x11, 0x0A, 0x04, 0x0A, 0x11 }, // 'X' { 0x18, 0x04, 0x03, 0x04, 0x18 }, // 'Y' { 0x11, 0x13, 0x15, 0x19, 0x11 } // 'Z' }; // ---------- CMovingThings ---------- enum eScrollBarTypes { SCROLL_ARENA_STRING }; CScrollBar aScrollBars[1]; CMovingThing CMovingThings::StartCloseList; CMovingThing CMovingThings::EndCloseList; int16 CMovingThings::Num; CMovingThing CMovingThings::aMovingThings[NUMMOVINGTHINGS]; int32 CScrollBar::TonightsEvent; void CMovingThings::Init() { StartCloseList.m_pNext = &CMovingThings::EndCloseList; StartCloseList.m_pPrev = nil; EndCloseList.m_pNext = nil; EndCloseList.m_pPrev = &CMovingThings::StartCloseList; CPlaneTrails::Init(); CSmokeTrails::Init(); CPlaneBanners::Init(); CPointLights::Init(); Num = 0; for (int32 i = 0; i < NUMMOVINGTHINGS; i++) { aMovingThings[i].m_nType = 0; aMovingThings[i].m_farAway = 0; } for (int i = 0; i < NUMSECTORS_X; i++) { for (int j = 0; j < NUMSECTORS_Y; j++) { for (CPtrNode *pNode = CWorld::GetSector(i, j)->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; PossiblyAddThisEntity(pEntity); } } } for (int32 i = 0; i < NUM_LEVELS; i++) { for (CPtrNode *pNode = CWorld::GetBigBuildingList((eLevelName)i).first; pNode; pNode = pNode->next) { CEntity *pEntity = (CEntity *)pNode->item; PossiblyAddThisEntity(pEntity); } } CEscalators::Init(); aScrollBars[0].Init(CVector(-1069.209f, 1320.126f, 18.848f), CVector(-1069.209f, 1342.299f, 22.612f), SCROLL_ARENA_STRING, 128, 255, 0, 0.3f); } void CMovingThings::Shutdown() { aScrollBars[0].SetVisibility(false); CEscalators::Shutdown(); } void CMovingThings::Update() { CPlaneBanners::Update(); CPlaneTrails::Update(); CEscalators::Update(); const int TIME_SPAN = 8; // frames to process all aMovingThings int16 i; int block = CTimer::GetFrameCounter() % TIME_SPAN; for (i = (block * NUMMOVINGTHINGS) / TIME_SPAN; i < ((block + 1) * NUMMOVINGTHINGS) / TIME_SPAN; i++) { if (aMovingThings[i].m_farAway == 1) aMovingThings[i].Update(); } for (i = 0; i < CMovingThings::Num; i++) { if (aMovingThings[i].m_farAway == 0) aMovingThings[i].Update(); } for (i = 0; i < ARRAY_SIZE(aScrollBars); ++i) { if (aScrollBars[i].IsVisible() || (CTimer::GetFrameCounter() + i) % 8 == 0) aScrollBars[i].Update(); } } void CMovingThings::Render() { PUSH_RENDERGROUP("CMovingThings::Render"); CSmokeTrails::Update(); int i; for (i = 0; i < ARRAY_SIZE(aScrollBars); ++i) { if (aScrollBars[i].IsVisible()) aScrollBars[i].Render(); } CPlaneTrails::Render(); CSmokeTrails::Render(); CPlaneBanners::Render(); POP_RENDERGROUP(); } void CMovingThings::RegisterOne(CEntity *pEnt, uint16 nType) { if (Num >= NUMMOVINGTHINGS) return; aMovingThings[Num].m_pEntity = pEnt; aMovingThings[Num].m_nType = nType; aMovingThings[Num].m_farAway = 0; aMovingThings[Num].m_vecPosn = pEnt->GetPosition(); aMovingThings[Num].AddToList(&CMovingThings::StartCloseList); Num++; } void CMovingThings::PossiblyAddThisEntity(CEntity *pEnt) { if (pEnt->GetModelIndex() == MI_LIGHTBEAM) { RegisterOne(pEnt, 1); } else if (pEnt->GetModelIndex() == MI_AIRPORTRADAR) { RegisterOne(pEnt, 2); } else if (pEnt->GetModelIndex() == MI_MALLFAN || pEnt->GetModelIndex() == MI_HOTELFAN_NIGHT || pEnt->GetModelIndex() == MI_HOTELFAN_DAY || pEnt->GetModelIndex() == MI_HOTROOMFAN) { RegisterOne(pEnt, 3); } else if (pEnt->GetModelIndex() == MI_BLIMP_NIGHT || pEnt->GetModelIndex() == MI_BLIMP_DAY) { RegisterOne(pEnt, 4); } } // ---------- CMovingThing ---------- static float maxUpdateDists[5] = { 100.0f, 1500.0f, 400.0f, 100.0f, 2000.0f }; void CMovingThing::Update() { switch (m_nType) { case 1: { float angle = (CTimer::GetTimeInMilliseconds() % 0x3FFF) * TWOPI / 0x3FFF; float s = Sin(angle); float c = Cos(angle); m_pEntity->GetRight() = CVector(-s, c, 0.0f); m_pEntity->GetForward() = CVector(0.0f, 0.0f, 1.0f); m_pEntity->GetUp() = CVector(c, s, 0.0f); if (CClock::GetHours() >= 20 || CClock::GetHours() < 5) { if (Abs(TheCamera.GetPosition().x - m_pEntity->GetPosition().x) < 600.0f && Abs(TheCamera.GetPosition().y - m_pEntity->GetPosition().y) < 600.0f) { CVector delta = m_pEntity->GetPosition() - TheCamera.GetPosition(); delta /= delta.Magnitude(); if (DotProduct(delta, CVector(c, s, 0.0f)) < -0.92f) { CVector coors = m_pEntity->GetPosition() - 10.0f * delta; CCoronas::RegisterCorona(43, 128, 128, 100, 255, coors, 70.0f, 600.0f, 0.0f, CCoronas::TYPE_STAR, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } } } } break; case 2: { float angle = (CTimer::GetTimeInMilliseconds() % 0x7FF) * TWOPI / 0x7FF; float s = Sin(angle); float c = Cos(angle); m_pEntity->GetRight() = CVector(c, s, 0.0f); m_pEntity->GetForward() = CVector(-s, c, 0.0f); m_pEntity->GetUp() = CVector(0.0f, 0.0f, 1.0f); } break; case 3: { float angle = (CTimer::GetTimeInMilliseconds() % 0x3FF) * TWOPI / 0x3FF; float s = Sin(angle); float c = Cos(angle); m_pEntity->GetRight() = CVector(c, s, 0.0f); m_pEntity->GetForward() = CVector(-s, c, 0.0f); m_pEntity->GetUp() = CVector(0.0f, 0.0f, 1.0f); } break; case 4: { float angle = (CTimer::GetTimeInMilliseconds() % 0x3FFFF) * TWOPI / 0x3FFFF; float s = Sin(angle); float c = Cos(angle); m_pEntity->GetRight() = CVector(-c, -s, 0.0f); m_pEntity->GetForward() = CVector(s, -c, 0.0f); m_pEntity->GetUp() = CVector(0.0f, 0.0f, 1.0f); m_pEntity->SetPosition(CVector(350.0f * c - 465.0f, 350.0f * s + 1163.0f, 260.0f)); } break; default: break; } m_pEntity->GetMatrix().UpdateRW(); m_pEntity->UpdateRwFrame(); if (SQR(m_pEntity->GetPosition().x - TheCamera.GetPosition().x) + SQR(m_pEntity->GetPosition().y - TheCamera.GetPosition().y) < SQR(maxUpdateDists[m_nType])) { if (m_farAway == 1) { AddToList(&CMovingThings::StartCloseList); m_farAway = 0; } } else { if (m_farAway == 0) { RemoveFromList(); m_farAway = 1; } } } void CMovingThing::AddToList(CMovingThing *pThing) { m_pNext = pThing->m_pNext; m_pPrev = pThing; pThing->m_pNext = this; m_pNext->m_pPrev = this; } void CMovingThing::RemoveFromList() { m_pNext->m_pPrev = m_pPrev; m_pPrev->m_pNext = m_pNext; } int16 CMovingThing::SizeList() { CMovingThing *next = m_pNext; int16 count = 0; while (next != nil) { next = next->m_pNext; count++; } return count; } char String_Time[] = "THE TIME IS 12:34 "; const char* FindTimeMessage() { String_Time[12] = '0' + CClock::GetHours() / 10; String_Time[13] = '0' + CClock::GetHours() % 10; String_Time[15] = '0' + CClock::GetMinutes() / 10; String_Time[16] = '0' + CClock::GetMinutes() % 10; return String_Time; } // ---------- CScrollBar ---------- void CScrollBar::Init(CVector pos1, CVector pos2, uint8 type, uint8 red, uint8 green, uint8 blue, float scale) { for (int i = 0; i < ARRAY_SIZE(m_MessageBar); ++i) m_MessageBar[i] = 0; m_pMessage = ". "; m_MessageCurrentChar = 0; m_MessageLength = strlen(m_pMessage); m_Counter = 0; m_bVisible = false; m_Position = pos1; m_Type = type; m_Size.x = (pos2.x - pos1.x) * 0.025f; m_Size.y = (pos2.y - pos1.y) * 0.025f; m_Size.z = (pos2.z - pos1.z) * 0.2f; m_uRed = red; m_uGreen = green; m_uBlue = blue; m_fScale = scale; } void CScrollBar::Update() { float distanceFromCamera = (TheCamera.GetPosition() - m_Position).Magnitude(); if (distanceFromCamera > 100.0f) { m_bVisible = false; return; } m_bVisible = true; if (distanceFromCamera < 75.0f) m_fIntensity = 1.0f; else m_fIntensity = 1.0f - 4.0f * (distanceFromCamera - 75.0f) / 100.0f; m_Counter = (m_Counter + 1) % 8; // if message is fully printed, load up the next one if (m_Counter == 0 && ++m_MessageCurrentChar >= m_MessageLength) { const char* previousMessage = m_pMessage; if (m_Type == SCROLL_ARENA_STRING) { while (previousMessage == m_pMessage) { switch (CGeneral::GetRandomNumber() % 4) { case 0: switch (TonightsEvent) { case 0: m_pMessage = "MAIN EVENT TONIGHT: CAR RACING . . . "; break; case 1: m_pMessage = "MAIN EVENT TONIGHT: DESTRUCTION DERBY . . . "; break; case 2: m_pMessage = "MAIN EVENT TONIGHT: BIKE RACING . . . "; break; } break; case 1: switch (TonightsEvent) { case 0: m_pMessage = "FOR TICKETS TO THE HOT RING EVENT CALL 555-3764 . . . "; break; case 1: m_pMessage = "FOR TICKETS TO THE BLOOD RING EVENT CALL 555-3765 . . . "; break; case 2: m_pMessage = "FOR TICKETS TO THE DIRT RING EVENT CALL 555-3766 . . . "; break; } break; case 2: m_pMessage = "HYMAN MEMORIAL STADIUM. HOME TO SOME OF THE BIGGEST EVENTS OF" " THE WESTERN HEMISPHERE. ALSO AVAILABLE FOR CHILDREN PARTIES. . . "; break; case 3: m_pMessage = FindTimeMessage(); break; default: break; } } } m_MessageLength = (uint32)strlen(m_pMessage); m_MessageCurrentChar = 0; } // Scroll for (int i = 0; i < ARRAY_SIZE(m_MessageBar)-1; i++) m_MessageBar[i] = m_MessageBar[i + 1]; m_MessageBar[ARRAY_SIZE(m_MessageBar)-1] = m_Counter < 5 ? ScrollCharSet[m_pMessage[m_MessageCurrentChar] - ' '][m_Counter] : 0; // Introduce some random displaying glitches; signs aren't supposed to be perfect :P switch (CGeneral::GetRandomNumber() & 0xFF) { case 0x0D: m_MessageBar[ARRAY_SIZE(m_MessageBar)-1] = 0; break; case 0xE3: m_MessageBar[ARRAY_SIZE(m_MessageBar)-1] = 0xE3; break; case 0x64: m_MessageBar[ARRAY_SIZE(m_MessageBar)-1] = ~m_MessageBar[ARRAY_SIZE(m_MessageBar)-1]; break; } } void CScrollBar::Render() { if (!TheCamera.IsSphereVisible(m_Position, 2.0f * 20.0f * (ABS(m_Size.x) + ABS(m_Size.y)))) return; CSprite::InitSpriteBuffer(); // Calculate intensity of colours uint8 r = m_fIntensity * m_uRed; uint8 g = m_fIntensity * m_uGreen; uint8 b = m_fIntensity * m_uBlue; // Set render states RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0])); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); CVector coronaCoord, screenCoord; float screenW, screenH; for (int i = 1; i < ARRAY_SIZE(m_MessageBar); ++i) { for (int j = 0; j < 5; ++j) { coronaCoord.x = m_Position.x + m_Size.x * i; coronaCoord.y = m_Position.y + m_Size.y * i; coronaCoord.z = m_Position.z + m_Size.z * j; // Render main coronas if (m_MessageBar[i] & (1 << j)) { if (CSprite::CalcScreenCoors(coronaCoord, &screenCoord, &screenW, &screenH, true)) { CSprite::RenderBufferedOneXLUSprite( screenCoord.x, screenCoord.y, screenCoord.z, screenW * m_fScale, screenH * m_fScale, r, g, b, 255, 1.0f / screenCoord.z, 255); } } // Render smaller and faded coronas for a trailing effect else if (m_MessageBar[i - 1] & (1 << j)) { if (CSprite::CalcScreenCoors(coronaCoord, &screenCoord, &screenW, &screenH, true)) { CSprite::RenderBufferedOneXLUSprite( screenCoord.x, screenCoord.y, screenCoord.z, screenW * m_fScale * 0.8f, screenH * m_fScale * 0.8f, r / 2, g / 2, b / 2, 255, 1.0f / screenCoord.z, 255); } } } } CSprite::FlushSpriteBuffer(); } void CSmokeTrail::RegisterPoint(CVector regPosition, float opacity) { bool bAddedNewPoint = false; if (m_time[0] && CTimer::GetTimeInMilliseconds() - m_time[0] > 150) { bAddedNewPoint = true; for (int32 i = 15; i > 0; i--) { m_pos[i] = m_pos[i - 1]; m_time[i] = m_time[i - 1]; m_opacity[i] = m_opacity[i - 1]; } ++m_seed; } m_pos[0] = regPosition; if (bAddedNewPoint || !m_time[0]) { m_time[0] = CTimer::GetTimeInMilliseconds(); float density = 0.1f / (m_pos[1] - m_pos[2]).Magnitude(); m_opacity[1] = opacity * Min(density, 1.0f); } m_opacity[0] = 0.0f; } void CSmokeTrail::Init(int num) { for (int32 i = 0; i < 16; i++) m_time[i] = 0; m_seed = num * 2; } void CSmokeTrails::Init(void) { for (int32 i = 0; i < 3; i++) aSmoke[i].Init(i); } void CSmokeTrails::Render(void) { for (int32 i = 0; i < 3; i++) aSmoke[i].Render(); } void CSmokeTrail::Render(void) { int numVerts = 0; if (TheCamera.IsSphereVisible(m_pos[0], 10.0f)) { for (int32 i = 0; i < 16; i++) { int timeSinceSpawned = CTimer::GetTimeInMilliseconds() - m_time[i]; if (timeSinceSpawned > 2250) m_time[i] = 0; if (m_time[i]) { int alpha = (1.0f - timeSinceSpawned / 2250.0f) * 110.0f * m_opacity[i]; float offset = timeSinceSpawned * CWeather::Wind * 0.0001f; float posX = (m_pos[i].x + timeSinceSpawned * RandomSmoke[(i - m_seed) & 0xF] * 0.00001f) - offset; float posY = (m_pos[i].y + timeSinceSpawned * RandomSmoke[(i - m_seed + 5) & 0xF] * 0.00001f) - offset; float posZ = m_pos[i].z + timeSinceSpawned * 0.0004f; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[i], 200, 200, 200, alpha); RwIm3DVertexSetPos(&TempBufferRenderVertices[i], posX, posY, posZ); numVerts++; } } } if (numVerts > 1) { RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); if (RwIm3DTransform(TempBufferRenderVertices, numVerts, nil, rwIM3D_VERTEXXYZ | rwIM3D_VERTEXRGBA)) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, SmokeTrailIndices, 2 * (numVerts - 1)); RwIm3DEnd(); } } } void CSmokeTrails::Update(void) { if (!CSmokeTrails::CigOn || TheCamera.Using1stPersonWeaponMode() || !FindPlayerPed() || FindPlayerVehicle() || CCutsceneMgr::IsRunning() || !FindPlayerPed()->GetClump()) return; RwV3d startPos = { 0.026f, 0.15f, 0.02f }; RwV3d endPos = { 0.026f, 0.05f, 0.02f }; RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(FindPlayerPed()->GetClump()); int32 idx = RpHAnimIDGetIndex(hier, ConvertPedNode2BoneTag(PED_HEAD)); RwMatrix *head = &RpHAnimHierarchyGetMatrixArray(hier)[idx]; RwV3dTransformPoints(&startPos, &startPos, 1, head); RwV3dTransformPoints(&endPos, &endPos, 1, head); aSmoke[0].RegisterPoint(startPos, 1.0f); aSmoke[1].RegisterPoint(startPos, 0.75f); aSmoke[2].RegisterPoint(startPos, 0.5f); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[0], 255, 255, 255, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[0], startPos.x, startPos.y, startPos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[1], 255, 255, 255, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[1], endPos.x, endPos.y, endPos.z); if (RwIm3DTransform(TempBufferRenderVertices, 2, nil, rwIM3D_VERTEXXYZ | rwIM3D_VERTEXRGBA)) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, SmokeTrailIndices, 2); RwIm3DEnd(); } } CEscalator CEscalators::aEscalators[NUM_ESCALATORS]; int32 CEscalators::NumEscalators; CEscalator::CEscalator() { m_bIsActive = false; for (int i = 0; i < 24; i++) { m_pSteps[i] = nil; } } void CEscalator::AddThisOne(CVector pos0, CVector pos1, CVector pos2, CVector pos3, bool b_isMovingDown) { m_pos0 = pos0; m_pos1 = pos1; m_pos2 = pos2; m_pos3 = pos3; float escalatorStepHeight = CModelInfo::GetModelInfo(MI_ESCALATORSTEP)->GetColModel()->boundingBox.max.z; m_pos0.z -= escalatorStepHeight; m_pos1.z -= escalatorStepHeight; m_pos2.z -= escalatorStepHeight; m_pos3.z -= escalatorStepHeight; float magnitudes[3]; magnitudes[0] = (m_pos0 - m_pos1).Magnitude(); magnitudes[1] = (m_pos1 - m_pos2).Magnitude(); magnitudes[2] = (m_pos2 - m_pos3).Magnitude(); float length = magnitudes[0] + magnitudes[1] + magnitudes[2]; m_lowerEnd = magnitudes[0] / length; m_upperEnd = (magnitudes[0] + magnitudes[1]) / length; m_stepsCount = Min(24.0f, length / 0.6f); CVector direction(m_pos0.x - m_pos1.x, m_pos0.y - m_pos1.y, 0.0f); direction.Normalise(); m_matrix.GetUp() = CVector(0.0f, 0.0f, 1.0f); m_matrix.GetForward() = CVector(direction.x, direction.y, 0.0f); m_matrix.GetRight() = CVector(direction.y, -direction.x, 0.0f); m_matrix.GetPosition() = CVector(0.0f, 0.0f, 0.0f); m_bIsMovingDown = b_isMovingDown; m_midPoint = (m_pos0 + m_pos3) / 2.0f; m_radius = (m_pos0 - m_midPoint).Magnitude(); } void CEscalator::Update(void) { if (!m_bIsActive) { if ((TheCamera.GetPosition() - m_midPoint).Magnitude() < 25.0f) { if (TheCamera.IsSphereVisible(m_midPoint, m_radius) && (m_stepsCount + 10 < CPools::GetObjectPool()->GetNoOfFreeSpaces())) { m_bIsActive = true; for (int i = 0; i < m_stepsCount; i++) { m_pSteps[i] = new CObject(MI_ESCALATORSTEP, TRUE); if (m_pSteps[i]) { m_pSteps[i]->SetPosition(m_pos1); CWorld::Add(m_pSteps[i]); m_pSteps[i]->ObjectCreatedBy = CONTROLLED_SUB_OBJECT; } } } } } if (m_bIsActive) { float time = (CTimer::GetTimeInMilliseconds() % 16384) / 16384.0f; for (int i = 0; i < m_stepsCount; i++) { if (m_pSteps[i]) { float t = i / (float)m_stepsCount + time; if (t > 1.0f) t -= 1.0f; if (m_bIsMovingDown) t = 1.0f - t; CVector oldPosition = m_pSteps[i]->GetPosition(); m_pSteps[i]->GetMatrix() = m_matrix; CVector newPosition; if (t < m_lowerEnd) { float ratio = t / m_lowerEnd; newPosition = (ratio * m_pos1) + ((1.0f - ratio) * m_pos0); } else if (t < m_upperEnd) { float ratio = (t - m_lowerEnd) / (m_upperEnd - m_lowerEnd); newPosition = (ratio * m_pos2) + ((1.0f - ratio) * m_pos1); } else { float ratio = (t - m_upperEnd) / (1.0f - m_upperEnd); newPosition = (ratio * m_pos3) + ((1.0f - ratio) * m_pos2); } m_pSteps[i]->SetPosition(newPosition); m_pSteps[i]->m_vecMoveSpeed = (newPosition - oldPosition) / Max(CTimer::GetTimeStep(), 1.0f); m_pSteps[i]->GetMatrix().UpdateRW(); m_pSteps[i]->UpdateRwFrame(); } if ((TheCamera.GetPosition() - m_midPoint).Magnitude() > 28.0f || !TheCamera.IsSphereVisible(m_midPoint, m_radius)) SwitchOff(); } } } bool deletingEscalator; void CEscalator::SwitchOff(void) { if (m_bIsActive) { for (int i = 0; i < m_stepsCount; i++) { if (m_pSteps[i]) { CWorld::Remove(m_pSteps[i]); deletingEscalator = true; delete m_pSteps[i]; m_pSteps[i] = nil; deletingEscalator = false; } } m_bIsActive = false; } } void CEscalators::AddOne(CVector pos0, CVector pos1, CVector pos2, CVector pos3, bool b_isMovingDown) { aEscalators[NumEscalators++].AddThisOne(pos0, pos1, pos2, pos3, b_isMovingDown); } void CEscalators::Init(void) { Shutdown(); NumEscalators = 0; AddOne(CVector(-9.82999f, -938.04498f, 9.4219f), CVector(-8.573f, -938.04498f, 9.4219f), CVector(-0.747f, -938.045f, 15.065f), CVector(0.88f, -938.045f, 15.065f), TRUE); AddOne(CVector(-9.83f, -939.966f, 9.422f), CVector(-8.573f, -939.966f, 9.422f), CVector(-0.747f, -939.966f, 15.065f), CVector(0.880f, -939.966f, 15.065f), FALSE); AddOne(CVector(408.116f, 1058.36f, 18.261f), CVector(408.094f, 1057.04f, 18.261f), CVector(408.116f, 1048.0f, 24.765f), CVector(408.094f, 1046.57f, 24.799f), TRUE); AddOne(CVector(406.195f, 1058.36f, 18.261f), CVector(406.173f, 1057.04f, 18.261f), CVector(406.195f, 1048.0f, 24.729f), CVector(406.173f, 1046.57f, 24.79f), FALSE); AddOne(CVector(421.729f, 1058.3789f, 18.075f), CVector(421.707f, 1057.052f, 18.099f), CVector(421.729f, 1048.016f, 24.604f), CVector(421.707f, 1046.589f, 24.637f), TRUE); AddOne(CVector(419.808f, 1058.378f, 18.099f), CVector(419.786f, 1057.052f, 18.099f), CVector(419.808f, 1048.016f, 24.568f), CVector(419.786f, 1046.589f, 24.637f), FALSE); AddOne(CVector(412.69901f, 1102.729f, 17.569f), CVector(412.72198f, 1104.057f, 17.57f), CVector(412.69901f, 1113.092f, 24.073f), CVector(412.72198f, 1114.3201f, 24.108f), TRUE); AddOne(CVector(414.62f, 1102.729f, 17.569f), CVector(414.64301f, 1104.057f, 17.57f), CVector(414.62f, 1113.092f, 24.037001f), CVector(414.64301f, 1114.3201f, 24.099001f), FALSE); AddOne(CVector(414.64301f, 1145.589f, 17.57f), CVector(414.62f, 1144.261f, 17.569f), CVector(414.64301f, 1135.226f, 24.073999f), CVector(414.62f, 1133.798f, 24.107f), TRUE); AddOne(CVector(412.72198f, 1145.589f, 17.57f), CVector(412.69901f, 1144.261f, 17.569f), CVector(412.72198f, 1135.226f, 24.038f), CVector(412.69901f, 1133.798f, 24.098f), FALSE); AddOne(CVector(406.05099f, 1193.4771f, 18.016001f), CVector(406.07401f, 1194.8051f, 18.017f), CVector(406.05099f, 1203.84f, 24.52f), CVector(406.07401f, 1205.2679f, 24.555f), TRUE); AddOne(CVector(407.97198f, 1193.4771f, 18.016001f), CVector(407.995f, 1194.8051f, 18.017f), CVector(407.97198f, 1203.84f, 24.483999f), CVector(407.995f, 1205.2679f, 24.546f), FALSE); AddOne(CVector(419.659f, 1193.479f, 17.979f), CVector(419.68201f, 1194.807f, 17.98f), CVector(419.659f, 1203.842f, 24.483f), CVector(419.68201f, 1205.27f, 24.518f), TRUE); AddOne(CVector(421.57999f, 1193.479f, 17.979f), CVector(421.603f, 1194.807f, 17.98f), CVector(421.57999f, 1203.842f, 24.447001f), CVector(421.603f, 1205.27f, 24.509001f), FALSE); AddOne(CVector(406.23199f, 1022.857f, 17.917f), CVector(406.23199f, 1024.1851f, 17.917f), CVector(406.23199f, 1033.22f, 24.521f), CVector(406.23199f, 1034.647f, 24.555f), TRUE); AddOne(CVector(408.15302f, 1022.857f, 17.917f), CVector(408.15302f, 1024.1851f, 17.916f), CVector(408.15302f, 1033.22f, 24.486f), CVector(408.15302f, 1034.647f, 24.52f), FALSE); AddOne(CVector(-1506.39f, -813.13f, 13.834f), CVector(-1506.177f, -814.51703f, 13.834f), CVector(-1504.566f, -823.20898f, 19.836f), CVector(-1504.329f, -824.48499f, 19.837f), FALSE); AddOne(CVector(-1481.951f, -859.05402f, 13.834f), CVector(-1482.7791f, -858.22498f, 13.834f), CVector(-1489.03f, -851.974f, 19.836f), CVector(-1489.948f, -851.05701f, 19.837f), TRUE); AddOne(CVector(-1461.743f, -871.35901f, 13.834f), CVector(-1460.62f, -871.69202f, 13.834f), CVector(-1452.144f, -874.20203f, 19.836f), CVector(-1450.9f, -874.57098f, 19.837f), FALSE); AddOne(CVector(-1409.889f, -871.41498f, 13.834f), CVector(-1411.0129f, -871.74701f, 13.834f), CVector(-1419.489f, -874.258f, 19.836f), CVector(-1420.733f, -874.62701f, 19.837f), TRUE); AddOne(CVector(-1389.577f, -858.89301f, 13.834f), CVector(-1388.7271f, -858.08698f, 13.834f), CVector(-1382.314f, -852.00201f, 19.836f), CVector(-1381.373f, -851.10797f, 19.837f), FALSE); AddOne(CVector(-1364.981f, -813.13f, 13.834f), CVector(-1365.204f, -814.28003f, 13.834f), CVector(-1366.891f, -822.95801f, 19.83f), CVector(-1367.139f, -824.23199f, 19.837f), TRUE); for (int i = 0; i < NUM_ESCALATORS; i++) { aEscalators[i].SwitchOff(); } } void CEscalators::Update(void) { if (CReplay::IsPlayingBack()) return; for (int i = 0; i < NUM_ESCALATORS; i++) { aEscalators[i].Update(); } } void CEscalators::Shutdown(void) { for (int i = 0; i < NUM_ESCALATORS; i++) { aEscalators[i].SwitchOff(); } NumEscalators = 0; } CScriptPath CScriptPaths::aArray[3]; void CScriptPath::FindCoorsFromDistanceOnPath(float t, float *pX, float *pY, float *pZ) { int32 i; for (i = 0; m_pNode[i + 1].t < t; i++) if (i == m_numNodes - 1) { // don't go beyond last node *pX = m_pNode[m_numNodes - 1].p.x; *pY = m_pNode[m_numNodes - 1].p.y; *pZ = m_pNode[m_numNodes - 1].p.z; return; } float f = (t - m_pNode[i].t) / (m_pNode[i + 1].t - m_pNode[i].t); *pX = (1.0f - f)*m_pNode[i].p.x + f*m_pNode[i + 1].p.x; *pY = (1.0f - f)*m_pNode[i].p.y + f*m_pNode[i + 1].p.y; *pZ = (1.0f - f)*m_pNode[i].p.z + f*m_pNode[i + 1].p.z; } void CScriptPath::Update(void) { if (m_state != SCRIPT_PATH_ACTIVE) return; m_fPosition += m_fSpeed * CTimer::GetTimeStepInSeconds(); m_fPosition = clamp(m_fPosition, 0.0f, m_fTotalLength); if (m_pObjects[0] || m_pObjects[1] || m_pObjects[2] || m_pObjects[3] || m_pObjects[4] || m_pObjects[5]) { float t1, t2; CVector pos1, pos2; t1 = Max(m_fPosition - m_fObjectLength / 2.0f, 0.0f); FindCoorsFromDistanceOnPath(t1, &pos1.x, &pos1.y, &pos1.z); t2 = Min(m_fPosition + m_fObjectLength / 2.0f, m_fTotalLength); FindCoorsFromDistanceOnPath(t2, &pos2.x, &pos2.y, &pos2.z); CVector newForward, newUp(0.0f, 0.0f, 1.0f), newRight; newForward = pos2 - pos1; newForward.Normalise(); newRight = CrossProduct(newForward, newUp); newRight.Normalise(); newUp = CrossProduct(newRight, newForward); for (int i = 0; i < 6; i++) { if (m_pObjects[i]) { CMatrix prevMat(m_pObjects[i]->GetMatrix()); CVector prevPosition = m_pObjects[i]->GetPosition(); m_pObjects[i]->SetPosition((pos1 + pos2) / 2.0f); m_pObjects[i]->GetRight() = newRight; m_pObjects[i]->GetUp() = newUp; m_pObjects[i]->GetForward() = newForward; m_pObjects[i]->GetMatrix().UpdateRW(); m_pObjects[i]->UpdateRwFrame(); if (!m_pObjects[i]->bIsBIGBuilding && prevPosition != m_pObjects[i]->GetPosition()) m_pObjects[i]->RemoveAndAdd(); m_pObjects[i]->GetMatrix().UpdateRW(); m_pObjects[i]->UpdateRwFrame(); m_pObjects[i]->m_vecMoveSpeed = (m_pObjects[i]->GetPosition() - prevMat.GetPosition()) / CTimer::GetTimeStep(); float deltaAngle = m_pObjects[i]->GetForward().Heading() - prevMat.GetForward().Heading(); while (deltaAngle < (float)PI) deltaAngle += (float)TWOPI; while (deltaAngle > (float)PI) deltaAngle -= (float)TWOPI; float zTurnSpeed = deltaAngle / CTimer::GetTimeStep(); m_pObjects[i]->m_vecTurnSpeed = CVector(0.0f, 0.0f, zTurnSpeed); m_pObjects[i]->m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f); m_pObjects[i]->m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f); } } } } void CScriptPath::Clear(void) { if (m_pNode) delete[] m_pNode; m_pNode = nil; m_numNodes = 0; for (int i = 0; i < 6; i++) m_pObjects[i] = nil; m_state = SCRIPT_PATH_DISABLED; } void CScriptPath::InitialiseOne(int32 numNodes, float length) { char Dest[32]; sprintf(Dest, "data\\paths\\spath%d.dat", numNodes); m_pNode = CPlane::LoadPath(Dest, m_numNodes, m_fTotalLength, false); m_fSpeed = 1.0f; m_fPosition = 0.0f; m_fObjectLength = length; m_state = SCRIPT_PATH_INITIALIZED; } void CScriptPath::SetObjectToControl(CObject *pObj) { int32 i = 0; while (i < 6 && m_pObjects[i]) i++; m_pObjects[i] = pObj; pObj->RegisterReference((CEntity**)&m_pObjects[i]); pObj->m_phy_flagA08 = false; m_state = SCRIPT_PATH_ACTIVE; } void CScriptPaths::Init(void) { for (int i = 0; i < 3; i++) aArray[i].Clear(); } void CScriptPaths::Shutdown(void) { for (int i = 0; i < 3; i++) aArray[i].Clear(); } void CScriptPaths::Update(void) { for (int i = 0; i < 3; i++) aArray[i].Update(); } bool CScriptPaths::IsOneActive(void) { for (int i = 0; i < 3; i++) if (aArray[i].m_state == SCRIPT_PATH_ACTIVE && aArray[i].m_fSpeed != 0.0f) return true; return false; } void CScriptPaths::Load(uint8 *buf, uint32 size) { INITSAVEBUF for (int32 i = 0; i < 3; i++) aArray[i].Clear(); for (int32 i = 0; i < 3; i++) { aArray[i] = ReadSaveBuf(buf); for (int32 j = 0; j < 6; j++) { CScriptPath *pPath = &aArray[i]; if (pPath->m_pObjects[j] != nil) { pPath->m_pObjects[j] = CPools::GetObjectPool()->GetSlot((uintptr)pPath->m_pObjects[j] - 1); pPath->m_pObjects[j]->m_phy_flagA08 = false; } } aArray[i].m_pNode = new CPlaneNode[aArray[i].m_numNodes]; for (int32 j = 0; j < aArray[i].m_numNodes; j++) { aArray[i].m_pNode[j] = ReadSaveBuf(buf); } } VALIDATESAVEBUF(size) } void CScriptPaths::Save(uint8 *buf, uint32 *size) { *size = sizeof(aArray); INITSAVEBUF for (int32 i = 0; i < 3; i++) { CScriptPath *pPath = WriteSaveBuf(buf, aArray[i]); for (int32 j = 0; j < 6; j++) { if (pPath->m_pObjects[j] != nil) pPath->m_pObjects[j] = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pPath->m_pObjects[j]) + 1); } for (int32 j = 0; j < aArray[i].m_numNodes; j++) { WriteSaveBuf(buf, aArray[i].m_pNode[j]); *size += sizeof(aArray[i].m_pNode[j]); } } VALIDATESAVEBUF(*size); } CObject *g_pScriptPathObjects[18]; void CScriptPaths::Load_ForReplay(void) { for (int i = 0; i < 3; i++) { for (int32 j = 0; j < 6; j++) { aArray[i].m_pObjects[j] = g_pScriptPathObjects[6 * i + j]; } } } void CScriptPaths::Save_ForReplay(void) { for (int i = 0; i < 3; i++) { for (int32 j = 0; j < 6; j++) { g_pScriptPathObjects[6 * i + j] = aArray[i].m_pObjects[j]; } } } ================================================ FILE: src/render/Fluff.h ================================================ #pragma once #include "common.h" #include "Vector.h" #include "Object.h" #include "Plane.h" enum { SCRIPT_PATH_DISABLED = 0, SCRIPT_PATH_INITIALIZED, SCRIPT_PATH_ACTIVE }; class CScriptPath { public: int32 m_numNodes; CPlaneNode *m_pNode; float m_fTotalLength; float m_fSpeed; float m_fPosition; float m_fObjectLength; int32 m_state; CObject *m_pObjects[6]; void Clear(void); void Update(void); void InitialiseOne(int32 numNodes, float length); void FindCoorsFromDistanceOnPath(float t, float *pX, float *pY, float *pZ); void SetObjectToControl(CObject *pObj); }; class CScriptPaths { public: static CScriptPath aArray[3]; static void Init(void); static void Shutdown(void); static void Update(void); static bool IsOneActive(void); static void Save(uint8 *buf, uint32 *size); static void Load(uint8 *buf, uint32 size); static void Save_ForReplay(); static void Load_ForReplay(); }; class CPlaneTrail { CVector m_pos[16]; int32 m_time[16]; public: void Init(void); void Render(float visibility); void RegisterPoint(CVector pos); }; class CPlaneTrails { static CPlaneTrail aArray[6]; // NB: 3 CPlanes and 3 hardcoded far away ones public: static void Init(void); static void Update(void); static void Render(void); static void RegisterPoint(CVector pos, uint32 id); }; class CPlaneBanner { CVector m_pos[8]; public: void Init(void); void Update(void); void Render(void); void RegisterPoint(CVector pos); }; class CPlaneBanners { static CPlaneBanner aArray[5]; public: static void Init(void); static void Update(void); static void Render(void); static void RegisterPoint(CVector pos, uint32 id); }; class CEscalator { CVector m_pos0; CVector m_pos1; CVector m_pos2; CVector m_pos3; CMatrix m_matrix; bool m_bIsActive; bool m_bIsMovingDown; int32 m_stepsCount; float m_lowerEnd; float m_upperEnd; CVector m_midPoint; float m_radius; CObject *m_pSteps[24]; public: CEscalator(); void Update(void); void SwitchOff(void); void AddThisOne(CVector pos0, CVector pos1, CVector pos2, CVector pos3, bool b_isMovingDown); bool IsActive() const { return m_bIsActive; }; const CVector& GetPosition() const { return m_midPoint; }; }; class CEscalators { static CEscalator aEscalators[NUM_ESCALATORS]; public: static int32 NumEscalators; static void Init(void); static void Update(void); static void AddOne(CVector pos0, CVector pos1, CVector pos2, CVector pos3, bool b_isMovingDown); static void Shutdown(void); static const CEscalator& GetEscalator(int ind) { return aEscalators[ind]; }; }; class CMovingThing { public: CMovingThing *m_pNext; CMovingThing *m_pPrev; int16 m_nType; int16 m_farAway; CVector m_vecPosn; CEntity* m_pEntity; void Update(); void AddToList(CMovingThing *pThing); void RemoveFromList(); int16 SizeList(); }; #define NUMMOVINGTHINGS 48 class CMovingThings { public: static CMovingThing StartCloseList; static CMovingThing EndCloseList; static int16 Num; static CMovingThing aMovingThings[NUMMOVINGTHINGS]; static void Init(); static void Shutdown(); static void Update(); static void Render(); static void PossiblyAddThisEntity(CEntity *pEnt); static void RegisterOne(CEntity *pEnt, uint16 nType); }; class CScrollBar { private: uint8 m_Counter; const char* m_pMessage; CVector m_Position; uint32 m_MessageCurrentChar; uint32 m_MessageLength; CVector m_Size; float m_fIntensity; uint8 m_MessageBar[40]; uint8 m_Type; bool m_bVisible; uint8 m_uRed; uint8 m_uGreen; uint8 m_uBlue; float m_fScale; public: static int TonightsEvent; public: void SetVisibility(bool visible) { m_bVisible = visible; } bool IsVisible() { return m_bVisible; } void Init(CVector pos1, CVector pos2, uint8 type, uint8 red, uint8 green, uint8 blue, float scale); void Update(); void Render(); }; class CSmokeTrail { CVector m_pos[16]; float m_opacity[16]; int m_time[16]; char m_unused[536]; int m_seed; public: void Render(void); void RegisterPoint(CVector position, float a); void Init(int num); }; class CSmokeTrails { static CSmokeTrail aSmoke[3]; public: static bool CigOn; static void Update(void); static void Render(void); static void Init(void); }; ================================================ FILE: src/render/Font.cpp ================================================ #include "common.h" #include "Sprite2d.h" #include "TxdStore.h" #include "Font.h" #ifdef BUTTON_ICONS #include "FileMgr.h" #endif #include "Timer.h" void AsciiToUnicode(const char *src, wchar *dst) { while((*dst++ = (unsigned char)*src++) != '\0'); } void UnicodeStrcat(wchar *dst, wchar *append) { UnicodeStrcpy(&dst[UnicodeStrlen(dst)], append); } void UnicodeStrcpy(wchar *dst, const wchar *src) { while((*dst++ = *src++) != '\0'); } int UnicodeStrlen(const wchar *str) { int len; for(len = 0; *str != '\0'; len++, str++); return len; } void UnicodeMakeUpperCase(wchar *dst, const wchar *src) //idk what to do with it, seems to be incorrect implementation by R* { while (*src != '\0') { if (*src < 'a' || *src > 'z') *dst = *src; else *dst = *src - 32; dst++; src++; } *dst = '\0'; } CFontDetails CFont::Details; int16 CFont::NewLine; CSprite2d CFont::Sprite[MAX_FONTS]; CFontRenderState CFont::RenderState; #ifdef MORE_LANGUAGES uint8 CFont::LanguageSet = FONT_LANGSET_EFIGS; int32 CFont::Slot = -1; #define JAP_TERMINATION (0x8000 | '~') int16 CFont::Size[LANGSET_MAX][MAX_FONTS][210] = { { #else int16 CFont::Size[MAX_FONTS][210] = { #endif { //FONT2 EFIGS //SPC,!, $, %, &, ', [, ], +, , -, ., 12, 9, 22, 17, 19, 19, 25, 4, 33, 33, 25, 35, 11, 10, 6, 33, //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ??, 18, 10, 17, 17, 17, 17, 17, 15, 12, 16, 5, 30, 30, 30, 30, 30, // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, 12, 16, 19, 16, 19, 18, 18, 17, 22, 11, 17, 18, 18, 30, 22, 19, //P, Q, R, S, T, U, V, W, X, Y, Z, ??, ??, ??, , \, #ifdef FIX_BUGS 22, 19, 19, 20, 18, 19, 19, 29, 19, 18, 19, 19, 33, 33, 10, 19, #else 22, 19, 19, 20, 18, 19, 19, 29, 19, 18, 19, 19, 33, 33, 19, 19, #endif //??,a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, 12, 14, 11, 11, 16, 11, 12, 14, 14, 10, 13, 12, 10, 19, 18, 12, //p, q, r, s, t, u, v, w, x, y, z, ??, ??, ??, ??, ??, 16, 13, 13, 11, 12, 15, 12, 15, 13, 12, 12, 37, 33, 37, 35, 37, //, , , , , , , , , , , , , , , , 16, 16, 16, 16, 33, 17, 18, 18, 18, 18, 11, 11, 11, 11, 19, 19, //, , , , , , , , , , , , , , , , 19, 19, 19, 19, 19, 19, 15, 14, 14, 14, 14, 20, 14, 11, 11, 11, //, , , , , , , , , , , , , , , , #ifdef FIX_BUGS 11, 10, 10, 10, 10, 12, 12, 12, 12, 15, 15, 15, 15, 22, 18, 21, #else 11, 10, 10, 10, 10, 12, 12, 12, 12, 15, 15, 15, 15, 24, 18, 21, #endif //i,BLANKS 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, //space, unprop 19, 16 }, { //FONT1 EFIGS //Characters with a '2' refer to the Pricedown font. //Characters that are referred as '*I' are characters that contain icons for PS2/XBOX, but contain regular characters on PC //in order to display them properly in the Keyboard controls menu. //!2,!, *I,(R), $, %, &, ', [, ], *I, +, , -, ., *I, 15, 7, 31, 25, 20, 23, 21, 7, 11, 10, 26, 14, 6, 12, 6, 26, //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, *I, *I, *I, *I, ?, 20, 7, 20, 20, 21, 20, 20, 19, 21, 20, 8, 30, 24, 30, 24, 19, //TM,A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, 20, 22, 22, 21, 22, 18, 18, 22, 22, 9, 14, 21, 18, 27, 21, 24, //P, Q, R, S, T, U, V, W, X, Y, Z, *I, \, *I, , , #ifdef FIX_BUGS 22, 22, 23, 20, 19, 23, 22, 31, 23, 23, 21, 25, 13, 30, 7, 19, #else 22, 22, 23, 20, 19, 23, 22, 31, 23, 23, 21, 25, 13, 30, 10, 19, #endif //(C),a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, 10, 17, 17, 16, 17, 17, 11, 17, 17, 7, 7, 18, 7, 25, 17, 17, //p, q, r, s, t, u, v, w, x, y, z, *I, *I, $2, (2, )2, 17, 17, 11, 17, 11, 17, 18, 25, 19, 18, 17, 28, 26, 20, 15, 15, //, , , , , , , , , , , , , , , , 20, 20, 20, 20, 29, 22, 19, 19, 19, 19, 9, 9, 9, 9, 23, 23, //, , , , , , , , , , , , , , , , 23, 23, 24, 24, 24, 24, 20, 19, 17, 17, 17, 30, 16, 17, 17, 17, //, , , , , , , , , , , , , , , , #ifdef FIX_BUGS 17, 11, 11, 15, 12, 17, 17, 17, 17, 17, 17, 17, 17, 21, 17, 19, #else 17, 11, 11, 15, 12, 17, 17, 17, 17, 17, 17, 17, 17, 19, 20, 20, #endif //02,12,22, 32, 42, 52, 62, 72, 82, 92, :2, A2, B2, C2, D2, E2, 20, 18, 19, 19, 21, 19, 19, 19, 19, 19, 16, 19, 19, 19, 20, 19, //F2,G2,H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, 16, 19, 19, 9, 19, 20, 14, 29, 19, 19, 19, 19, 19, 19, 21, 19, //V2,W2,X2, Y2, Z2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 32, 20, 19, 19, 19, 19, 19, 19, 29, 19, 19, 19, 19, 19, 9, //2,2,2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, '2, .2, #ifdef FIX_BUGS 9, 9, 9, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 9, #else 9, 9, 9, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 10, 9, #endif //space, unprop 10, 20 } #ifdef MORE_LANGUAGES }, { { 5, 9, 9, 0, 17, 17, 23, 3, 21, 18, 0, 8, 3, 8, 3, 0, 16, 9, 16, 16, 15, 19, 15, 14, 17, 17, 4, 4, 0, 0, 0, 17, 19, 17, 19, 15, 21, 18, 19, 16, 21, 13, 15, 21, 20, 28, 21, 18, 22, 17, 21, 20, 18, 18, 20, 26, 22, 18, 18, 0, 8, 0, 9, 8, 0, 14, 11, 12, 16, 11, 13, 13, 15, 10, 14, 15, 11, 21, 17, 10, 20, 15, 12, 12, 16, 17, 13, 16, 13, 21, 11, 0, 0, 0, 0, 0, 20, 19, 19, 22, 27, 15, 18, 18, 20, 26, 21, 23, 17, 22, 21, 17, 26, 25, 26, 17, 20, 26, 17, 16, 11, 12, 13, 21, 11, 17, 17, 12, 21, 17, 17, 15, 24, 16, 10, 20, 23, 16, 7, 9, 16, 23, 12, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 16 }, { 11, 5, 10, 15, 19, 22, 20, 5, 9, 8, 11, 12, 5, 12, 6, 12, 19, 5, 18, 19, 20, 18, 19, 18, 20, 19, 5, 6, 26, 12, 30, 19, 23, 21, 20, 20, 20, 16, 16, 21, 19, 5, 13, 19, 16, 24, 20, 21, 20, 21, 20, 19, 17, 20, 21, 30, 22, 21, 20, 25, 13, 30, 5, 9, 10, 15, 15, 14, 15, 16, 10, 15, 15, 5, 5, 15, 5, 23, 15, 16, 15, 15, 9, 16, 10, 15, 17, 24, 18, 15, 15, 27, 5, 19, 2, 2, 20, 20, 16, 23, 30, 19, 20, 20, 21, 24, 19, 19, 20, 23, 22, 19, 27, 29, 25, 20, 20, 28, 24, 16, 16, 14, 19, 25, 16, 16, 16, 17, 19, 16, 16, 17, 25, 19, 15, 23, 26, 21, 16, 14, 22, 20, 16, 19, 15, 14, 15, 16, 17, 15, 15, 15, 15, 15, 7, 15, 15, 15, 15, 15, 13, 15, 15, 7, 15, 16, 13, 23, 15, 15, 15, 15, 15, 15, 17, 15, 16, 24, 17, 17, 17, 15, 15, 13, 20, 23, 15, 17, 17, 16, 24, 15, 15, 15, 23, 18, 15, 23, 26, 23, 16, 15, 23, 15, 15, 19, 2, 2, 10, 20 }, }, { { //FONT2 EFIGS //SPC,!, $, %, &, ', [, ], +, , -, ., 12, 9, 22, 17, 19, 19, 25, 4, 33, 33, 25, 35, 11, 10, 6, 33, //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ??, 18, 10, 17, 17, 17, 17, 17, 15, 12, 16, 5, 30, 30, 30, 30, 30, // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, 12, 16, 19, 16, 19, 18, 18, 17, 22, 11, 17, 18, 18, 30, 22, 19, //P, Q, R, S, T, U, V, W, X, Y, Z, ??, ??, ??, , \, 22, 19, 19, 20, 18, 19, 19, 29, 19, 18, 19, 19, 33, 33, 10, 19, //??,a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, 12, 14, 11, 11, 16, 11, 12, 14, 14, 10, 13, 12, 10, 19, 18, 12, //p, q, r, s, t, u, v, w, x, y, z, ??, ??, ??, ??, ??, 16, 13, 13, 11, 12, 15, 12, 15, 13, 12, 12, 37, 33, 37, 35, 37, //, , , , , , , , , , , , , , , , 16, 16, 16, 16, 33, 17, 18, 18, 18, 18, 11, 11, 11, 11, 19, 19, //, , , , , , , , , , , , , , , , 19, 19, 19, 19, 19, 19, 15, 14, 14, 14, 14, 20, 14, 11, 11, 11, //, , , , , , , , , , , , , , , , 11, 10, 10, 10, 10, 12, 12, 12, 12, 15, 15, 15, 15, 22, 18, 21, //i,BLANKS 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, //space, unprop 19, 16 }, { //FONT1 EFIGS //Characters with a '2' refer to the Pricedown font. //Characters that are referred as '*I' are characters that contain icons for PS2/XBOX, but contain regular characters on PC //in order to display them properly in the Keyboard controls menu. //!2,!, *I,(R), $, %, &, ', [, ], *I, +, , -, ., *I, 15, 7, 31, 25, 20, 23, 21, 7, 11, 10, 26, 14, 6, 12, 6, 26, //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, *I, *I, *I, *I, ?, 20, 7, 20, 20, 21, 20, 20, 19, 21, 20, 8, 30, 24, 30, 24, 19, //TM,A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, 20, 22, 22, 21, 22, 18, 18, 22, 22, 9, 14, 21, 18, 27, 21, 24, //P, Q, R, S, T, U, V, W, X, Y, Z, *I, \, *I, , , 22, 22, 23, 20, 19, 23, 22, 31, 23, 23, 21, 25, 13, 30, 7, 19, //(C),a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, 10, 17, 17, 16, 17, 17, 11, 17, 17, 7, 7, 18, 7, 25, 17, 17, //p, q, r, s, t, u, v, w, x, y, z, *I, *I, $2, (2, )2, 17, 17, 11, 17, 11, 17, 18, 25, 19, 18, 17, 28, 26, 20, 15, 15, //, , , , , , , , , , , , , , , , 20, 20, 20, 20, 29, 22, 19, 19, 19, 19, 9, 9, 9, 9, 23, 23, //, , , , , , , , , , , , , , , , 23, 23, 24, 24, 24, 24, 20, 19, 17, 17, 17, 30, 16, 17, 17, 17, //, , , , , , , , , , , , , , , , 17, 11, 11, 15, 12, 17, 17, 17, 17, 17, 17, 17, 17, 21, 17, 19, //02,12,22, 32, 42, 52, 62, 72, 82, 92, :2, A2, B2, C2, D2, E2, 20, 18, 19, 19, 21, 19, 19, 19, 19, 19, 16, 19, 19, 19, 20, 19, //F2,G2,H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, 16, 19, 19, 9, 19, 20, 14, 29, 19, 19, 19, 19, 19, 19, 21, 19, //V2,W2,X2, Y2, Z2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 32, 20, 19, 19, 19, 19, 19, 19, 29, 19, 19, 19, 19, 19, 9, //2,2,2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, '2, .2, 9, 9, 9, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 10, 9, //space, unprop 10, 20 } } #endif }; #ifdef MORE_LANGUAGES int16 Size_jp[] = { 15, 14, 16, 20, 19, 26, 22, 11, 18, 18, 27, 26, 13, //; 0 19, 20, 27, 19, 15, 19, 19, 21, 19, 20, 18, 19, 15, //; 13 13, 28, 15, 32, 15, 35, 15, 19, 19, 19, 19, 17, 16, //; 26 19, 20, 15, 19, 20, 14, 17, 19, 19, 19, 19, 19, 19, //; 39 19, 19, 20, 25, 20, 19, 19, 33, 31, 39, 37, 39, 37, //; 52 21, 21, 21, 19, 17, 15, 23, 21, 15, 19, 20, 16, 19, //; 65 19, 19, 20, 20, 17, 22, 19, 22, 22, 19, 22, 22, 23, //; 78 35, 35, 35, 35, 37, 19, 19, 19, 19, 29, 19, 19, 19, //; 91 19, 19, 9, 9, 9, 9, 19, 19, 19, 19, 19, 19, 19, 19, //; 104 19, 19, 19, 19, 19, 30, 19, 19, 19, 19, 19, 10, 10, //; 118 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 23, 35, //; 131 12, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, //; 144 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, //; 157 19, 19, 19, 11, 19, 19, 19, 19, 19, 19, 19, 19, 19, //; 170 19, 19, 19, 19, 19, 19, 19, 19, 19, 21 }; #endif wchar foreign_table[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 175, 128, 129, 130, 0, 131, 0, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 0, 173, 142, 143, 144, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 150, 151, 152, 153, 0, 154, 0, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 0, 174, 165, 166, 167, 0, 168, 0, 0, 169, 170, 171, 172, 0, 0, 0, }; union tFontRenderStatePointer { CFontRenderState *pRenderState; wchar *pStr; void Align() { if ((uintptr)pStr % 4) pStr++; } }; tFontRenderStatePointer FontRenderStatePointer; uint8 FontRenderStateBuf[1024]; #ifdef BUTTON_ICONS CSprite2d CFont::ButtonSprite[MAX_BUTTON_ICONS]; int CFont::PS2Symbol = BUTTON_NONE; int CFont::ButtonsSlot = -1; #endif // BUTTON_ICONS void CFont::Initialise(void) { int slot; slot = CTxdStore::AddTxdSlot("fonts"); #ifdef MORE_LANGUAGES Slot = slot; switch (LanguageSet) { case FONT_LANGSET_EFIGS: default: CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD"); break; case FONT_LANGSET_POLISH: CTxdStore::LoadTxd(slot, "MODELS/FONTS_P.TXD"); break; case FONT_LANGSET_RUSSIAN: CTxdStore::LoadTxd(slot, "MODELS/FONTS_R.TXD"); break; case FONT_LANGSET_JAPANESE: CTxdStore::LoadTxd(slot, "MODELS/FONTS_J.TXD"); break; } #else CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD"); #endif CTxdStore::AddRef(slot); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(slot); Sprite[0].SetTexture("font2", "font2m"); #ifdef MORE_LANGUAGES if (IsJapanese()) { Sprite[1].SetTexture("FONTJAP", "FONTJAP_mask"); Sprite[3].SetTexture("FONTJAP", "FONTJAP_mask"); } #endif // MORE_LANGUAGES Sprite[1].SetTexture("font1", "font1m"); SetScale(1.0f, 1.0f); SetSlantRefPoint(SCREEN_WIDTH, 0.0f); SetSlant(0.0f); SetColor(CRGBA(255, 255, 255, 0)); SetJustifyOff(); SetCentreOff(); SetWrapx(SCREEN_WIDTH); SetCentreSize(SCREEN_WIDTH); SetBackgroundOff(); SetBackgroundColor(CRGBA(128, 128, 128, 128)); SetBackGroundOnlyTextOff(); SetPropOn(); SetFontStyle(FONT_BANK); SetRightJustifyWrap(0.0f); SetAlphaFade(255.0f); SetDropShadowPosition(0); CTxdStore::PopCurrentTxd(); #if !defined(GAMEPAD_MENU) && defined(BUTTON_ICONS) // loaded in CMenuManager with GAMEPAD_MENU defined LoadButtons("MODELS/X360BTNS.TXD"); #endif } #ifdef BUTTON_ICONS void CFont::LoadButtons(const char *txdPath) { if (int file = CFileMgr::OpenFile(txdPath)) { CFileMgr::CloseFile(file); if (ButtonsSlot == -1) ButtonsSlot = CTxdStore::AddTxdSlot("buttons"); else { for (int i = 0; i < MAX_BUTTON_ICONS; i++) ButtonSprite[i].Delete(); CTxdStore::RemoveTxd(ButtonsSlot); } CTxdStore::LoadTxd(ButtonsSlot, txdPath); CTxdStore::AddRef(ButtonsSlot); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(ButtonsSlot); ButtonSprite[BUTTON_UP].SetTexture("thumblyu"); ButtonSprite[BUTTON_DOWN].SetTexture("thumblyd"); ButtonSprite[BUTTON_LEFT].SetTexture("thumblxl"); ButtonSprite[BUTTON_RIGHT].SetTexture("thumblxr"); ButtonSprite[BUTTON_CROSS].SetTexture("cross"); ButtonSprite[BUTTON_CIRCLE].SetTexture("circle"); ButtonSprite[BUTTON_SQUARE].SetTexture("square"); ButtonSprite[BUTTON_TRIANGLE].SetTexture("triangle"); ButtonSprite[BUTTON_L1].SetTexture("l1"); ButtonSprite[BUTTON_L2].SetTexture("l2"); ButtonSprite[BUTTON_L3].SetTexture("l3"); ButtonSprite[BUTTON_R1].SetTexture("r1"); ButtonSprite[BUTTON_R2].SetTexture("r2"); ButtonSprite[BUTTON_R3].SetTexture("r3"); ButtonSprite[BUTTON_RSTICK_UP].SetTexture("thumbryu"); ButtonSprite[BUTTON_RSTICK_DOWN].SetTexture("thumbryd"); ButtonSprite[BUTTON_RSTICK_LEFT].SetTexture("thumbrxl"); ButtonSprite[BUTTON_RSTICK_RIGHT].SetTexture("thumbrxr"); CTxdStore::PopCurrentTxd(); } else { if (ButtonsSlot != -1) { for (int i = 0; i < MAX_BUTTON_ICONS; i++) ButtonSprite[i].Delete(); CTxdStore::RemoveTxdSlot(ButtonsSlot); ButtonsSlot = -1; } } } #endif // BUTTON_ICONS #ifdef MORE_LANGUAGES void CFont::ReloadFonts(uint8 set) { if (Slot != -1 && LanguageSet != set) { Sprite[0].Delete(); Sprite[1].Delete(); if (IsJapanese()) Sprite[2].Delete(); CTxdStore::PushCurrentTxd(); CTxdStore::RemoveTxd(Slot); switch (set) { case FONT_LANGSET_EFIGS: default: CTxdStore::LoadTxd(Slot, "MODELS/FONTS.TXD"); break; case FONT_LANGSET_POLISH: CTxdStore::LoadTxd(Slot, "MODELS/FONTS_P.TXD"); break; case FONT_LANGSET_RUSSIAN: CTxdStore::LoadTxd(Slot, "MODELS/FONTS_R.TXD"); break; case FONT_LANGSET_JAPANESE: CTxdStore::LoadTxd(Slot, "MODELS/FONTS_J.TXD"); break; } CTxdStore::SetCurrentTxd(Slot); Sprite[0].SetTexture("font2", "font2_mask"); if (set == FONT_LANGSET_JAPANESE) { Sprite[2].SetTexture("FONTJAP", "FONTJAP_mask"); } Sprite[1].SetTexture("font1", "font1_mask"); CTxdStore::PopCurrentTxd(); } LanguageSet = set; } #endif void CFont::Shutdown(void) { #ifdef BUTTON_ICONS if (ButtonsSlot != -1) { for (int i = 0; i < MAX_BUTTON_ICONS; i++) ButtonSprite[i].Delete(); CTxdStore::RemoveTxdSlot(ButtonsSlot); ButtonsSlot = -1; } #endif Sprite[0].Delete(); Sprite[1].Delete(); #ifdef MORE_LANGUAGES if (IsJapanese()) Sprite[3].Delete(); CTxdStore::RemoveTxdSlot(Slot); Slot = -1; #else CTxdStore::RemoveTxdSlot(CTxdStore::FindTxdSlot("fonts")); #endif } void CFont::InitPerFrame(void) { RenderState.style = -1; Details.anonymous_25 = 0; FontRenderStatePointer.pRenderState = (CFontRenderState*)FontRenderStateBuf; SetDropShadowPosition(0); NewLine = false; #ifdef BUTTON_ICONS PS2Symbol = BUTTON_NONE; #endif } #ifdef BUTTON_ICONS void CFont::DrawButton(float x, float y) { if (x <= 0.0f || x > SCREEN_WIDTH || y <= 0.0f || y > SCREEN_HEIGHT) return; if (PS2Symbol != BUTTON_NONE) { CRect rect; rect.left = x; rect.top = RenderState.scaleY + RenderState.scaleY + y; rect.right = RenderState.scaleY * 17.0f + x; rect.bottom = RenderState.scaleY * 19.0f + y; int vertexAlphaState; void *raster; RwRenderStateGet(rwRENDERSTATEVERTEXALPHAENABLE, &vertexAlphaState); RwRenderStateGet(rwRENDERSTATETEXTURERASTER, &raster); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); if (RenderState.bIsShadow) ButtonSprite[PS2Symbol].Draw(rect, RenderState.color); else ButtonSprite[PS2Symbol].Draw(rect, CRGBA(255, 255, 255, RenderState.color.a)); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, raster); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)vertexAlphaState); } } #endif void CFont::PrintChar(float x, float y, wchar c) { bool bDontPrint = false; if(x <= 0.0f || x > SCREEN_WIDTH || y <= 0.0f || y > SCREEN_HEIGHT) // BUG: game uses SCREENW again return; bDontPrint = c == '\0'; float w = GetCharacterWidth(c) / 32.0f; if (Details.bFontHalfTexture && c == 208) c = '\0'; float xoff = c % 16; float yoff = c / 16; #ifdef MORE_LANGUAGES if (IsJapaneseFont()) { w = 21.0f; xoff = (float)(c % 48); yoff = c / 48; } #endif if(RenderState.style == FONT_BANK || RenderState.style == FONT_STANDARD){ if (bDontPrint) return; if (RenderState.slant == 0.0f) { #ifdef FIX_BUGS if (c < 192) { #else if (c < 193) { #endif CSprite2d::AddToBuffer( CRect(x, y, x + 32.0f * RenderState.scaleX * 1.0f, y + 40.0f * RenderState.scaleY * 0.5f), RenderState.color, xoff / 16.0f, yoff / 12.8f + 0.0021f, (xoff + 1.0f) / 16.0f - 0.001f, yoff / 12.8f + 0.0021f, xoff / 16.0f, (yoff + 1.0f) / 12.8f - 0.0021f, (xoff + 1.0f) / 16.0f - 0.001f, (yoff + 1.0f) / 12.8f - 0.0021f); } else { CSprite2d::AddToBuffer( CRect(x, y, x + 32.0f * RenderState.scaleX * 1.0f, y + 33.0f * RenderState.scaleY * 0.5f), RenderState.color, xoff / 16.0f, yoff / 12.8f + 0.0021f, (xoff + 1.0f) / 16.0f - 0.001f, yoff / 12.8f + 0.0021f, xoff / 16.0f, (yoff + 1.0f) / 12.8f - 0.017f, (xoff + 1.0f) / 16.0f - 0.001f, (yoff + 1.0f) / 12.8f - 0.017f); } } else CSprite2d::AddToBuffer( CRect(x, y, x + 32.0f * RenderState.scaleX * 1.0f, y + 40.0f * RenderState.scaleY * 0.5f), RenderState.color, xoff / 16.0f, yoff / 12.8f + 0.00055f, (xoff + 1.0f) / 16.0f - 0.001f, yoff / 12.8f + 0.0021f + 0.01f, xoff / 16.0f, (yoff + 1.0f) / 12.8f - 0.009f, (xoff + 1.0f) / 16.0f - 0.001f, (yoff + 1.0f) / 12.8f - 0.0021f + 0.01f); #ifdef MORE_LANGUAGES /*}else if (IsJapaneseFont()) { if (Details.dropShadowPosition != 0) { CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank CRect(x + SCREEN_SCALE_X(Details.dropShadowPosition), y + SCREEN_SCALE_Y(Details.dropShadowPosition), x + SCREEN_SCALE_X(Details.dropShadowPosition) + 32.0f * Details.scaleX * 1.0f, y + SCREEN_SCALE_Y(Details.dropShadowPosition) + 40.0f * Details.scaleY / 2.75f), Details.dropColor, xoff * w / 1024.0f, yoff / 25.6f, xoff * w / 1024.0f + (1.0f / 48.0f) - 0.001f, yoff / 25.6f, xoff * w / 1024.0f, (yoff + 1.0f) / 25.6f, xoff * w / 1024.0f + (1.0f / 48.0f) - 0.001f, (yoff + 1.0f) / 25.6f - 0.0001f); } CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank CRect(x, y, x + 32.0f * Details.scaleX * 1.0f, y + 40.0f * Details.scaleY / 2.75f), Details.color, xoff * w / 1024.0f, yoff / 25.6f, xoff * w / 1024.0f + (1.0f / 48.0f) - 0.001f, yoff / 25.6f, xoff * w / 1024.0f, (yoff + 1.0f) / 25.6f - 0.002f, xoff * w / 1024.0f + (1.0f / 48.0f) - 0.001f, (yoff + 1.0f) / 25.6f - 0.0001f);*/ #endif } else { if (bDontPrint) return; CSprite2d::AddToBuffer( CRect(x, y, x + 32.0f * RenderState.scaleX * w, y + 32.0f * RenderState.scaleY * 0.5f), RenderState.color, xoff / 16.0f, yoff / 16.0f, (xoff + w) / 16.0f, yoff / 16.0f, xoff / 16.0f, (yoff + 1.0f) / 16.0f, (xoff + w) / 16.0f - 0.0001f, (yoff + 1.0f) / 16.0f - 0.0001f); } } #ifdef MORE_LANGUAGES bool CFont::IsJapanesePunctuation(wchar *str) { return (*str == 0xE7 || *str == 0x124 || *str == 0x126 || *str == 0x128 || *str == 0x104 || *str == ',' || *str == '>' || *str == '!' || *str == 0x99 || *str == '?' || *str == ':'); } bool CFont::IsAnsiCharacter(wchar *s) { if (*s >= 'A' && *s <= 'Z') return true; if (*s >= 'a' && *s <= 'z') return true; if (*s >= '0' && *s <= ':') return true; if (*s == '(' || *s == ')') return true; if (*s == 'D' || *s == '$') return true; return false; } #endif void CFont::RenderFontBuffer() { if (FontRenderStatePointer.pRenderState == (CFontRenderState*)FontRenderStateBuf) return; float textPosX; float textPosY; CRGBA color; bool bBold = false; bool bFlash = false; Sprite[RenderState.style].SetRenderState(); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RenderState = *(CFontRenderState*)&FontRenderStateBuf[0]; textPosX = RenderState.fTextPosX; textPosY = RenderState.fTextPosY; color = RenderState.color; tFontRenderStatePointer pRenderStateBufPointer; pRenderStateBufPointer.pRenderState = (CFontRenderState*)&FontRenderStateBuf[0]; for (++pRenderStateBufPointer.pRenderState; pRenderStateBufPointer.pStr < FontRenderStatePointer.pStr; pRenderStateBufPointer.pStr++) { if (*pRenderStateBufPointer.pStr == '\0') { tFontRenderStatePointer tmpPointer = pRenderStateBufPointer; tmpPointer.pStr++; tmpPointer.Align(); if (tmpPointer.pStr >= FontRenderStatePointer.pStr) break; RenderState = *(tmpPointer.pRenderState++); pRenderStateBufPointer = tmpPointer; textPosX = RenderState.fTextPosX; textPosY = RenderState.fTextPosY; color = RenderState.color; } if (*pRenderStateBufPointer.pStr == '~') { #ifdef BUTTON_ICONS PS2Symbol = BUTTON_NONE; #endif pRenderStateBufPointer.pStr = ParseToken(pRenderStateBufPointer.pStr, color, bFlash, bBold); #ifdef BUTTON_ICONS if(PS2Symbol != BUTTON_NONE) { DrawButton(textPosX, textPosY); textPosX += RenderState.scaleY * 17.0f; PS2Symbol = BUTTON_NONE; } #endif if (bFlash) { if (CTimer::GetTimeInMilliseconds() - Details.nFlashTimer > 300) { Details.bFlashState = !Details.bFlashState; Details.nFlashTimer = CTimer::GetTimeInMilliseconds(); } Details.color.alpha = Details.bFlashState ? 0 : 255; } if (!RenderState.bIsShadow) RenderState.color = color; } wchar c = *pRenderStateBufPointer.pStr; c -= ' '; if (RenderState.bFontHalfTexture) c = FindNewCharacter(c); else if (c > 155) c = '\0'; if (RenderState.slant != 0.0f) textPosY = (RenderState.slantRefX - textPosX) * RenderState.slant + RenderState.slantRefY; PrintChar(textPosX, textPosY, c); if (bBold) { PrintChar(textPosX + 1.0f, textPosY, c); PrintChar(textPosX + 2.0f, textPosY, c); textPosX += 2.0f; } #ifdef FIX_BUGS // PS2 uses different chars for some symbols if (!RenderState.bFontHalfTexture && c == 30) c = 61; // wanted star #endif textPosX += RenderState.scaleX * GetCharacterWidth(c); if (c == '\0') textPosX += RenderState.fExtraSpace; } CSprite2d::RenderVertexBuffer(); FontRenderStatePointer.pRenderState = (CFontRenderState*)FontRenderStateBuf; } #if 0 //def MORE_LANGUAGES bool CFont::PrintString(float x, float y, wchar *start, wchar *&end, float spwidth, float japX) { wchar *s, c, unused; if (IsJapanese()) { float jx = 0.0f; for (s = start; s < end; s++) { if (*s == JAP_TERMINATION || *s == '~') s = ParseToken(s, &unused, true); if (NewLine) { NewLine = false; break; } jx += GetCharacterSize(*s - ' '); } s = start; if (Details.centre) x = japX - jx / 2.0f; else if (Details.rightJustify) x = japX - jx; } for (s = start; s < end; s++) { if (*s == '~' || (IsJapanese() && *s == JAP_TERMINATION)) s = ParseToken(s, &unused); if (NewLine && IsJapanese()) { NewLine = false; end = s; return true; } c = *s - ' '; if (Details.slant != 0.0f && !IsJapanese()) y = (Details.slantRefX - x) * Details.slant + Details.slantRefY; PrintChar(x, y, c); x += GetCharacterSize(c); if (c == 0 && (!NewLine || !IsJapanese())) // space x += spwidth; } return false; } #else void CFont::PrintString(float x, float y, uint32, wchar *start, wchar *end, float spwidth) { wchar *s; if (RenderState.style != Details.style) { RenderFontBuffer(); RenderState.style = Details.style; } float dropShadowPosition = Details.dropShadowPosition; if (dropShadowPosition != 0.0f && (Details.style == FONT_BANK || Details.style == FONT_STANDARD)) { CRGBA color = Details.color; Details.color = Details.dropColor; Details.dropShadowPosition = 0; Details.bIsShadow = true; if (Details.slant != 0.0f) { Details.slantRefX += SCREEN_SCALE_X(dropShadowPosition); Details.slantRefY += SCREEN_SCALE_Y(dropShadowPosition); PrintString(SCREEN_SCALE_X(dropShadowPosition) + x, SCREEN_SCALE_Y(dropShadowPosition) + y, Details.anonymous_25, start, end, spwidth); Details.slantRefX -= SCREEN_SCALE_X(dropShadowPosition); Details.slantRefY -= SCREEN_SCALE_Y(dropShadowPosition); } else { PrintString(SCREEN_SCALE_X(dropShadowPosition) + x, SCREEN_SCALE_Y(dropShadowPosition) + y, Details.anonymous_25, start, end, spwidth); } Details.color = color; Details.dropShadowPosition = dropShadowPosition; Details.bIsShadow = false; } if (FontRenderStatePointer.pStr >= (wchar*)&FontRenderStateBuf[ARRAY_SIZE(FontRenderStateBuf)] - (end - start + 26)) // why 26? RenderFontBuffer(); CFontRenderState *pRenderState = FontRenderStatePointer.pRenderState; pRenderState->fTextPosX = x; pRenderState->fTextPosY = y; pRenderState->scaleX = Details.scaleX; pRenderState->scaleY = Details.scaleY; pRenderState->color = Details.color; pRenderState->fExtraSpace = spwidth; pRenderState->slant = Details.slant; pRenderState->slantRefX = Details.slantRefX; pRenderState->slantRefY = Details.slantRefY; pRenderState->bFontHalfTexture = Details.bFontHalfTexture; pRenderState->proportional = Details.proportional; pRenderState->style = Details.style; pRenderState->bIsShadow = Details.bIsShadow; FontRenderStatePointer.pRenderState++; for(s = start; s < end;){ if (*s == '~') { for (wchar *i = ParseToken(s); s != i; FontRenderStatePointer.pStr++) { *FontRenderStatePointer.pStr = *(s++); } if (Details.bFlash) { if (CTimer::GetTimeInMilliseconds() - Details.nFlashTimer > 300) { Details.bFlashState = !Details.bFlashState; Details.nFlashTimer = CTimer::GetTimeInMilliseconds(); } Details.color.a = Details.bFlashState ? 0 : 255; } } else *(FontRenderStatePointer.pStr++) = *(s++); } *(FontRenderStatePointer.pStr++) = '\0'; FontRenderStatePointer.Align(); } #endif void CFont::PrintStringFromBottom(float x, float y, wchar *str) { y -= (32.0f * Details.scaleY / 2.0f + 2.0f * Details.scaleY) * GetNumberLines(x, y, str); if (Details.slant != 0.0f) y -= ((Details.slantRefX - x) * Details.slant + Details.slantRefY); PrintString(x, y, str); } void CFont::PrintString(float xstart, float ystart, wchar *s) { CRect rect; int numSpaces; float lineLength; float x, y; bool first; wchar *start, *t; Details.bFlash = false; if(*s == '*') return; Details.anonymous_25++; if(Details.background){ RenderState.color = Details.color; GetNumberLines(xstart, ystart, s); // BUG: result not used GetTextRect(&rect, xstart, ystart, s); CSprite2d::DrawRect(rect, Details.backgroundColor); } lineLength = 0.0f; numSpaces = 0; first = true; if(Details.centre || Details.rightJustify) x = 0.0f; else x = xstart; y = ystart; start = s; // This is super ugly, I blame R* for(;;){ for(;;){ for(;;){ if(*s == '\0') return; float xend = Details.centre ? Details.centreSize : Details.rightJustify ? xstart - Details.rightJustifyWrap : Details.wrapX; #ifdef MORE_LANGUAGES if (IsJapaneseFont()) xend -= SCREEN_SCALE_X(21.0f * 2.0f); #endif if(x + GetStringWidth(s) > xend && !first){ #ifdef MORE_LANGUAGES if (IsJapanese() && IsJapanesePunctuation(s)) s--; #endif // flush line float spaceWidth = !Details.justify || Details.centre ? 0.0f : (Details.wrapX - lineLength) / numSpaces; float xleft = Details.centre ? xstart - x/2 : Details.rightJustify ? xstart - x : xstart; #if 0//def MORE_LANGUAGES PrintString(xleft, y, start, s, spaceWidth, xstart); #else PrintString(xleft, y, Details.anonymous_25, start, s, spaceWidth); #endif // reset things lineLength = 0.0f; numSpaces = 0; first = true; if(Details.centre || Details.rightJustify) x = 0.0f; else x = xstart; #ifdef MORE_LANGUAGES if (IsJapaneseFont()) y += 32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY; else #endif y += 32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY; start = s; }else break; } // advance by one word t = GetNextSpace(s); if(t[0] == '\0' || t[0] == ' ' && t[1] == '\0') break; if(!first) numSpaces++; first = false; x += GetStringWidth(s) + GetCharacterSize(*t - ' '); #ifdef MORE_LANGUAGES if (IsJapaneseFont() && IsAnsiCharacter(s)) x += 21.0f; #endif lineLength = x; s = t+1; #if 0 //def MORE_LANGUAGES if (IsJapaneseFont() && !*s) { x += GetStringWidth(s); if (IsAnsiCharacter(s)) x += 21.0f; float xleft = Details.centre ? xstart - x / 2 : Details.rightJustify ? xstart - x : xstart; if (PrintString(xleft, y, start, s, 0.0f, xstart)) { start = s; if (!Details.centre && !Details.rightJustify) x = xstart; else x = 0.0f; y += 32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY; numSpaces = 0; first = true; lineLength = 0.0f; } } #endif } // print rest if(t[0] == ' ' && t[1] == '\0') t[0] = '\0'; x += GetStringWidth(s); s = t; float xleft = Details.centre ? xstart - x/2 : Details.rightJustify ? xstart - x : xstart; #if 0 //def MORE_LANGUAGES if (PrintString(xleft, y, start, s, 0.0f, xstart) && IsJapaneseFont()) { start = s; if (!Details.centre && !Details.rightJustify) x = xstart; else x = 0.0f; y += 32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY; numSpaces = 0; first = true; lineLength = 0.0f; } #else PrintString(xleft, y, Details.anonymous_25, start, s, 0.0f); #endif } } int CFont::GetNumberLines(float xstart, float ystart, wchar *s) { int n; float x, y; wchar *t; n = 0; #if 0//def MORE_LANGUAGES bool bSomeJapBool = false; if (IsJapanese()) { t = s; wchar unused; while (*t) { if (*t == JAP_TERMINATION || *t == '~') t = ParseToken(t, &unused, true); if (NewLine) { n++; NewLine = false; bSomeJapBool = true; } t++; } } if (bSomeJapBool) n--; #endif if(Details.centre || Details.rightJustify) x = 0.0f; else x = xstart; y = ystart; while(*s){ #ifdef FIX_BUGS float f = Details.centre ? Details.centreSize : Details.rightJustify ? xstart - Details.rightJustifyWrap : Details.wrapX; #else float f = (Details.centre ? Details.centreSize : Details.wrapX); #endif #ifdef MORE_LANGUAGES if (IsJapaneseFont()) f -= SCREEN_SCALE_X(21.0f * 2.0f); #endif if(x + GetStringWidth(s) > f){ #ifdef MORE_LANGUAGES if (IsJapanese()) { if (IsJapanesePunctuation(s)) s--; } #endif // reached end of line if(Details.centre || Details.rightJustify) x = 0.0f; else x = xstart; n++; // Why even? #ifdef MORE_LANGUAGES if (IsJapanese()) y += 32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY; else #endif y += 32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY; }else{ // still space in current line t = GetNextSpace(s); if(*t == '\0'){ // end of string x += GetStringWidth(s); #ifdef MORE_LANGUAGES if (IsJapanese() && IsAnsiCharacter(s)) x += 21.0f; #endif n++; s = t; }else{ x += GetStringWidth(s); #ifdef MORE_LANGUAGES if (IsJapanese() && IsAnsiCharacter(s)) x += 21.0f; #endif s = t+1; x += GetCharacterSize(*t - ' '); #ifdef MORE_LANGUAGES if (IsJapanese() && !*s) n++; #endif } } } return n; } void CFont::GetTextRect(CRect *rect, float xstart, float ystart, wchar *s) { int numLines; float x, y; int16 maxlength; wchar *t; maxlength = 0; numLines = 0; #ifdef MORE_LANGUAGES if (IsJapanese()) { numLines = GetNumberLines(xstart, ystart, s); }else{ #endif if(Details.centre || Details.rightJustify) x = 0.0f; else x = xstart; y = ystart; #ifdef FIX_BUGS float xEnd = Details.centre ? Details.centreSize : Details.rightJustify ? xstart - Details.rightJustifyWrap : Details.wrapX; #else float xEnd = (Details.centre ? Details.centreSize : Details.wrapX); #endif while(*s){ if(x + GetStringWidth(s) > xEnd){ // reached end of line if(x > maxlength) maxlength = x; if(Details.centre || Details.rightJustify) x = 0.0f; else x = xstart; numLines++; y += 32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY; }else{ // still space in current line t = GetNextSpace(s); if(*t == '\0'){ // end of string x += GetStringWidth(s); if(x > maxlength) maxlength = x; numLines++; s = t; }else{ x += GetStringWidth(s); x += GetCharacterSize(*t - ' '); s = t+1; } } } #ifdef MORE_LANGUAGES } #endif if(Details.centre){ if(Details.backgroundOnlyText){ rect->left = xstart - maxlength/2 - 4.0f; rect->right = xstart + maxlength/2 + 4.0f; #ifdef MORE_LANGUAGES if (IsJapaneseFont()) { rect->bottom = (32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY) * numLines + ystart + (4.0f / 2.75f); rect->top = ystart - (4.0f / 2.75f); } else { #endif rect->bottom = (32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY) * numLines + ystart + 2.0f; rect->top = ystart - 2.0f; #ifdef MORE_LANGUAGES } #endif }else{ rect->left = xstart - Details.centreSize*0.5f - 4.0f; rect->right = xstart + Details.centreSize*0.5f + 4.0f; #ifdef MORE_LANGUAGES if (IsJapaneseFont()) { rect->bottom = (32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY) * numLines + ystart + (4.0f / 2.75f); rect->top = ystart - (4.0f / 2.75f); } else { #endif rect->bottom = (32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY) * numLines + ystart + 2.0f; rect->top = ystart - 2.0f; #ifdef MORE_LANGUAGES } #endif } }else{ rect->left = xstart - 4.0f; rect->right = Details.wrapX; // WTF? rect->bottom = ystart - 4.0f + 4.0f; #ifdef MORE_LANGUAGES if (IsJapaneseFont()) rect->top = (32.0f * Details.scaleY / 2.75f + 2.0f * Details.scaleY) * numLines + ystart + 2.0f + (4.0f / 2.75f); else #endif rect->top = (32.0f * Details.scaleY * 0.5f + 2.0f * Details.scaleY) * numLines + ystart + 2.0f + 2.0f; } } float CFont::GetCharacterWidth(wchar c) { #ifdef MORE_LANGUAGES if (IsJapanese()) { if (!RenderState.proportional) return Size[0][Details.style][192]; if (c <= 94 || Details.style == FONT_HEADING || RenderState.style == FONT_BANK) { switch (RenderState.style) { case FONT_JAPANESE: return Size_jp[c]; default: return Size[0][RenderState.style][c]; } } switch (RenderState.style) { case FONT_JAPANESE: return 29.4f; case FONT_BANK: return 10.0f; default: return Size[0][RenderState.style][c]; } } else if (RenderState.proportional) return Size[LanguageSet][RenderState.style][c]; else return Size[LanguageSet][RenderState.style][209]; #else if (RenderState.proportional) return Size[RenderState.style][c]; else return Size[RenderState.style][209]; #endif // MORE_LANGUAGES } float CFont::GetCharacterSize(wchar c) { #ifdef MORE_LANGUAGES if (IsJapanese()) { if (!Details.proportional) return Size[0][Details.style][209] * Details.scaleX; if (c <= 94 || Details.style == FONT_HEADING || Details.style == FONT_BANK) { switch (Details.style) { case FONT_JAPANESE: return Size_jp[c] * Details.scaleX; default: return Size[0][Details.style][c] * Details.scaleX; } } switch (Details.style) { case FONT_JAPANESE: return 29.4f * Details.scaleX; case FONT_BANK: return 10.0f * Details.scaleX; default: return Size[0][Details.style][c] * Details.scaleX; } } else { if (!Details.bFontHalfTexture && c == 30) c = 61; // wanted star if (Details.bFontHalfTexture) c = FindNewCharacter(c); if (Details.proportional) return Size[LanguageSet][Details.style][c] * Details.scaleX; else return Size[LanguageSet][Details.style][209] * Details.scaleX; } #else #ifdef FIX_BUGS // PS2 don't call FindNewCharacter in here at all, and also uses different chars for some symbols if (!Details.bFontHalfTexture && c == 30) c = 61; // wanted star #endif if (Details.bFontHalfTexture) c = FindNewCharacter(c); if (Details.proportional) return Size[Details.style][c] * Details.scaleX; else return Size[Details.style][209] * Details.scaleX; #endif // MORE_LANGUAGES } float CFont::GetStringWidth(wchar *s, bool spaces) { float w; w = 0.0f; #ifdef MORE_LANGUAGES if (IsJapanese()) { do { if ((*s != ' ' || spaces) && *s != '\0') { do { while (*s == '~' || *s == JAP_TERMINATION) { s++; #ifdef BUTTON_ICONS switch (*s) { case 'U': case 'D': case '<': case '>': case 'X': case 'O': case 'Q': case 'T': case 'K': case 'M': case 'A': case 'J': case 'V': case 'C': case '(': case ')': w += 17.0f * Details.scaleY; break; default: break; } #endif while (!(*s == '~' || *s == JAP_TERMINATION)) s++; s++; } w += GetCharacterSize(*s - ' '); ++s; } while (*s == '~' || *s == JAP_TERMINATION); } } while (IsAnsiCharacter(s)); } else #endif { for (wchar c = *s; (c != ' ' || spaces) && c != '\0'; c = *(++s)) { if (c == '~') { s++; #ifdef BUTTON_ICONS switch (*s) { case 'U': case 'D': case '<': case '>': case 'X': case 'O': case 'Q': case 'T': case 'K': case 'M': case 'A': case 'J': case 'V': case 'C': case '(': case ')': w += 17.0f * Details.scaleY; break; default: break; } #endif while (*s != '~') { s++; } } else { w += GetCharacterSize(c - ' '); } } } return w; } #ifdef MORE_LANGUAGES float CFont::GetStringWidth_Jap(wchar* s) { float w; w = 0.0f; for (; *s != '\0';) { do { while (*s == '~' || *s == JAP_TERMINATION) { s++; while (!(*s == '~' || *s == JAP_TERMINATION)) s++; s++; } w += GetCharacterSize(*s - ' '); ++s; } while (*s == '~' || *s == JAP_TERMINATION); } return w; } #endif wchar* CFont::GetNextSpace(wchar *s) { #ifdef MORE_LANGUAGES if (IsJapanese()) { do { if (*s != ' ' && *s != '\0') { do { while (*s == '~' || *s == JAP_TERMINATION) { s++; while (!(*s == '~' || *s == JAP_TERMINATION)) s++; s++; } ++s; } while (*s == '~' || *s == JAP_TERMINATION); } } while (IsAnsiCharacter(s)); } else #endif { for(; *s != ' ' && *s != '\0'; s++) if(*s == '~'){ s++; while(*s != '~') s++; } } return s; } wchar* CFont::ParseToken(wchar* str, CRGBA &color, bool &flash, bool &bold) { Details.anonymous_23 = false; wchar *s = str + 1; if (Details.color.r || Details.color.g || Details.color.b) { switch (*s) { case 'B': bold = !bold; break; case 'b': color.r = 27; color.g = 89; color.b = 130; break; case 'f': flash = !flash; break; case 'g': color.r = 255; color.g = 150; color.b = 225; break; case 'h': color.r = 225; color.g = 225; color.b = 225; break; case 'l': color.r = 0; color.g = 0; color.b = 0; break; case 'o': color.r = 229; color.g = 125; color.b = 126; break; case 'p': color.r = 168; color.g = 110; color.b = 252; break; case 'q': color.r = 199; color.g = 144; color.b = 203; break; case 'r': color.r = 255; color.g = 150; color.b = 225; break; case 't': color.r = 86; color.g = 212; color.b = 146; break; case 'w': color.r = 175; color.g = 175; color.b = 175; break; #ifdef FIX_BUGS case 'x': color.r = 0; color.g = 255; color.b = 255; break; #else case 'x': color.r = 132; color.g = 146; color.b = 197; break; #endif case 'y': color.r = 255; color.g = 227; color.b = 79; break; #ifdef BUTTON_ICONS case 'U': PS2Symbol = BUTTON_UP; break; case 'D': PS2Symbol = BUTTON_DOWN; break; case '<': PS2Symbol = BUTTON_LEFT; break; case '>': PS2Symbol = BUTTON_RIGHT; break; case 'X': PS2Symbol = BUTTON_CROSS; break; case 'O': PS2Symbol = BUTTON_CIRCLE; break; case 'Q': PS2Symbol = BUTTON_SQUARE; break; case 'T': PS2Symbol = BUTTON_TRIANGLE; break; case 'K': PS2Symbol = BUTTON_L1; break; case 'M': PS2Symbol = BUTTON_L2; break; case 'A': PS2Symbol = BUTTON_L3; break; case 'J': PS2Symbol = BUTTON_R1; break; case 'V': PS2Symbol = BUTTON_R2; break; case 'C': PS2Symbol = BUTTON_R3; break; case 'H': PS2Symbol = BUTTON_RSTICK_UP; break; case 'L': PS2Symbol = BUTTON_RSTICK_DOWN; break; case '(': PS2Symbol = BUTTON_RSTICK_LEFT; break; case ')': PS2Symbol = BUTTON_RSTICK_RIGHT; break; #endif default: break; } } while (*s != '~') ++s; if (*(++s) == '~') s = ParseToken(s, color, flash, bold); return s; } #if 0//def MORE_LANGUAGES wchar* CFont::ParseToken(wchar *s, bool japShit) { s++; if ((Details.color.r || Details.color.g || Details.color.b) && !japShit) { wchar c = *s; if (IsJapanese()) c &= 0x7FFF; switch (c) { case 'N': case 'n': NewLine = true; break; case 'b': SetColor(CRGBA(128, 167, 243, 255)); break; case 'g': SetColor(CRGBA(95, 160, 106, 255)); break; case 'h': SetColor(CRGBA(225, 225, 225, 255)); break; case 'l': SetColor(CRGBA(0, 0, 0, 255)); break; case 'p': SetColor(CRGBA(168, 110, 252, 255)); break; case 'r': SetColor(CRGBA(113, 43, 73, 255)); break; case 'w': SetColor(CRGBA(175, 175, 175, 255)); break; case 'y': SetColor(CRGBA(210, 196, 106, 255)); break; #ifdef BUTTON_ICONS case 'U': PS2Symbol = BUTTON_UP; break; case 'D': PS2Symbol = BUTTON_DOWN; break; case '<': PS2Symbol = BUTTON_LEFT; break; case '>': PS2Symbol = BUTTON_RIGHT; break; case 'X': PS2Symbol = BUTTON_CROSS; break; case 'O': PS2Symbol = BUTTON_CIRCLE; break; case 'Q': PS2Symbol = BUTTON_SQUARE; break; case 'T': PS2Symbol = BUTTON_TRIANGLE; break; case 'K': PS2Symbol = BUTTON_L1; break; case 'M': PS2Symbol = BUTTON_L2; break; case 'A': PS2Symbol = BUTTON_L3; break; case 'J': PS2Symbol = BUTTON_R1; break; case 'V': PS2Symbol = BUTTON_R2; break; case 'C': PS2Symbol = BUTTON_R3; break; case 'H': PS2Symbol = BUTTON_RSTICK_UP; break; case 'L': PS2Symbol = BUTTON_RSTICK_DOWN; break; case '(': PS2Symbol = BUTTON_RSTICK_LEFT; break; case ')': PS2Symbol = BUTTON_RSTICK_RIGHT; break; #endif } } else if (IsJapanese()) { if ((*s & 0x7FFF) == 'N' || (*s & 0x7FFF) == 'n') NewLine = true; } while ((!IsJapanese() || (*s != JAP_TERMINATION)) && *s != '~') s++; return s + 1; } #else wchar* CFont::ParseToken(wchar *s) { Details.anonymous_23 = false; s++; if(Details.color.r || Details.color.g || Details.color.b) switch(*s){ case 'B': Details.bBold = !Details.bBold; break; case 'N': case 'n': NewLine = true; break; case 'b': Details.color.r = 27; Details.color.g = 89; Details.color.b = 130; Details.anonymous_23 = true; break; case 'f': Details.bFlash = !Details.bFlash; if (!Details.bFlash) Details.color.a = 255; break; case 'g': Details.color.r = 255; Details.color.g = 150; Details.color.b = 225; Details.anonymous_23 = true; break; case 'h': Details.color.r = 225; Details.color.g = 225; Details.color.b = 225; Details.anonymous_23 = true; break; case 'l': Details.color.r = 0; Details.color.g = 0; Details.color.b = 0; Details.anonymous_23 = true; break; case 'o': Details.color.r = 229; Details.color.g = 125; Details.color.b = 126; Details.anonymous_23 = true; break; case 'p': Details.color.r = 168; Details.color.g = 110; Details.color.b = 252; Details.anonymous_23 = true; break; case 'q': Details.color.r = 199; Details.color.g = 144; Details.color.b = 203; Details.anonymous_23 = true; break; case 'r': Details.color.r = 255; Details.color.g = 150; Details.color.b = 225; Details.anonymous_23 = true; break; case 't': Details.color.r = 86; Details.color.g = 212; Details.color.b = 146; Details.anonymous_23 = true; break; case 'w': Details.color.r = 175; Details.color.g = 175; Details.color.b = 175; Details.anonymous_23 = true; break; case 'x': #ifdef FIX_BUGS Details.color.r = 0; Details.color.g = 255; Details.color.b = 255; #else Details.color.r = 132; Details.color.g = 146; Details.color.b = 197; #endif Details.anonymous_23 = true; break; case 'y': Details.color.r = 255; Details.color.g = 227; Details.color.b = 79; Details.anonymous_23 = true; break; #ifdef BUTTON_ICONS case 'U': PS2Symbol = BUTTON_UP; break; case 'D': PS2Symbol = BUTTON_DOWN; break; case '<': PS2Symbol = BUTTON_LEFT; break; case '>': PS2Symbol = BUTTON_RIGHT; break; case 'X': PS2Symbol = BUTTON_CROSS; break; case 'O': PS2Symbol = BUTTON_CIRCLE; break; case 'Q': PS2Symbol = BUTTON_SQUARE; break; case 'T': PS2Symbol = BUTTON_TRIANGLE; break; case 'K': PS2Symbol = BUTTON_L1; break; case 'M': PS2Symbol = BUTTON_L2; break; case 'A': PS2Symbol = BUTTON_L3; break; case 'J': PS2Symbol = BUTTON_R1; break; case 'V': PS2Symbol = BUTTON_R2; break; case 'C': PS2Symbol = BUTTON_R3; break; case 'H': PS2Symbol = BUTTON_RSTICK_UP; break; case 'L': PS2Symbol = BUTTON_RSTICK_DOWN; break; case '(': PS2Symbol = BUTTON_RSTICK_LEFT; break; case ')': PS2Symbol = BUTTON_RSTICK_RIGHT; break; #endif } while(*s != '~') s++; if (*(++s) == '~') s = ParseToken(s); return s; } #endif void CFont::FilterOutTokensFromString(wchar *str) { int newIdx = 0; wchar copy[256], *c; UnicodeStrcpy(copy, str); for (c = copy; *c != '\0'; c++) { if (*c == '~') { c++; while (*c != '~') c++; } else { str[newIdx++] = *c; } } str[newIdx] = '\0'; } void CFont::DrawFonts(void) { RenderFontBuffer(); } void CFont::SetScale(float x, float y) { #ifdef MORE_LANGUAGES /*if (IsJapanese()) { x *= 1.35f; y *= 1.25f; }*/ #endif Details.scaleX = x; Details.scaleY = y; } void CFont::SetSlantRefPoint(float x, float y) { Details.slantRefX = x; Details.slantRefY = y; } void CFont::SetSlant(float s) { Details.slant = s; } void CFont::SetColor(CRGBA col) { Details.color = col; if (Details.alphaFade < 255.0f) Details.color.a *= Details.alphaFade / 255.0f; } void CFont::SetJustifyOn(void) { Details.justify = true; Details.centre = false; Details.rightJustify = false; } void CFont::SetJustifyOff(void) { Details.justify = false; Details.rightJustify = false; } void CFont::SetCentreOn(void) { Details.centre = true; Details.justify = false; Details.rightJustify = false; } void CFont::SetCentreOff(void) { Details.centre = false; } void CFont::SetWrapx(float x) { Details.wrapX = x; } void CFont::SetCentreSize(float s) { Details.centreSize = s; } void CFont::SetBackgroundOn(void) { Details.background = true; } void CFont::SetBackgroundOff(void) { Details.background = false; } void CFont::SetBackgroundColor(CRGBA col) { Details.backgroundColor = col; } void CFont::SetBackGroundOnlyTextOn(void) { Details.backgroundOnlyText = true; } void CFont::SetBackGroundOnlyTextOff(void) { Details.backgroundOnlyText = false; } void CFont::SetRightJustifyOn(void) { Details.rightJustify = true; Details.justify = false; Details.centre = false; } void CFont::SetRightJustifyOff(void) { Details.rightJustify = false; Details.justify = false; Details.centre = false; } void CFont::SetPropOff(void) { Details.proportional = false; } void CFont::SetPropOn(void) { Details.proportional = true; } void CFont::SetFontStyle(int16 style) { if (style == FONT_HEADING) { Details.style = FONT_STANDARD; Details.bFontHalfTexture = true; } else { Details.style = style; Details.bFontHalfTexture = false; } } void CFont::SetRightJustifyWrap(float wrap) { Details.rightJustifyWrap = wrap; } void CFont::SetAlphaFade(float fade) { Details.alphaFade = fade; } void CFont::SetDropColor(CRGBA col) { Details.dropColor = col; if (Details.alphaFade < 255.0f) Details.dropColor.a *= Details.alphaFade / 255.0f; } void CFont::SetDropShadowPosition(int16 pos) { Details.dropShadowPosition = pos; } wchar CFont::FindNewCharacter(wchar c) { if (c >= 16 && c <= 26) return c + 128; if (c >= 8 && c <= 9) return c + 86; if (c == 4) return c + 89; if (c == 7) return 206; if (c == 14) return 207; if (c >= 33 && c <= 58) return c + 122; if (c >= 65 && c <= 90) return c + 90; if (c >= 96 && c <= 118) return c + 85; if (c >= 119 && c <= 140) return c + 62; if (c >= 141 && c <= 142) return 204; if (c == 143) return 205; if (c == 1) return 208; return c; } wchar CFont::character_code(uint8 c) { if(c < 128) return c; return foreign_table[c-128]; } ================================================ FILE: src/render/Font.h ================================================ #pragma once #include "Sprite2d.h" void AsciiToUnicode(const char *src, wchar *dst); void UnicodeStrcpy(wchar *dst, const wchar *src); void UnicodeStrcat(wchar *dst, wchar *append); int UnicodeStrlen(const wchar *str); void UnicodeMakeUpperCase(wchar *dst, const wchar *src); struct CFontDetails { CRGBA color; float scaleX; float scaleY; float slant; float slantRefX; float slantRefY; bool8 justify; bool8 centre; bool8 rightJustify; bool8 background; bool8 backgroundOnlyText; bool8 proportional; bool8 bIsShadow; bool8 bFlash; bool8 bBold; float alphaFade; CRGBA backgroundColor; float wrapX; float centreSize; float rightJustifyWrap; int16 style; bool8 bFontHalfTexture; uint32 bank; int16 dropShadowPosition; CRGBA dropColor; bool8 bFlashState; int nFlashTimer; bool8 anonymous_23; uint32 anonymous_25; }; struct CFontRenderState { uint32 anonymous_0; float fTextPosX; float fTextPosY; float scaleX; float scaleY; CRGBA color; float fExtraSpace; float slant; float slantRefX; float slantRefY; bool8 bIsShadow; bool8 bFontHalfTexture; bool8 proportional; bool8 anonymous_14; int16 style; }; class CSprite2d; enum { FONT_BANK, FONT_STANDARD, FONT_HEADING, #ifdef MORE_LANGUAGES FONT_JAPANESE, #endif MAX_FONTS = FONT_HEADING }; enum { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, }; #ifdef MORE_LANGUAGES enum { FONT_LANGSET_EFIGS, FONT_LANGSET_RUSSIAN, FONT_LANGSET_POLISH, FONT_LANGSET_JAPANESE, LANGSET_MAX }; #define FONT_LOCALE(style) (CFont::IsJapanese() ? FONT_JAPANESE : style) #else #define FONT_LOCALE(style) (style) #endif #ifdef BUTTON_ICONS enum { BUTTON_NONE = -1, BUTTON_UP, BUTTON_DOWN, BUTTON_LEFT, BUTTON_RIGHT, BUTTON_CROSS, BUTTON_CIRCLE, BUTTON_SQUARE, BUTTON_TRIANGLE, BUTTON_L1, BUTTON_L2, BUTTON_L3, BUTTON_R1, BUTTON_R2, BUTTON_R3, BUTTON_RSTICK_UP, BUTTON_RSTICK_DOWN, BUTTON_RSTICK_LEFT, BUTTON_RSTICK_RIGHT, MAX_BUTTON_ICONS }; #endif // BUTTON_ICONS class CFont { #ifdef MORE_LANGUAGES static int16 Size[LANGSET_MAX][MAX_FONTS][210]; static uint8 LanguageSet; static int32 Slot; #else static int16 Size[MAX_FONTS][210]; #endif static int16 NewLine; public: static CSprite2d Sprite[MAX_FONTS]; static CFontDetails Details; static CFontRenderState RenderState; #ifdef BUTTON_ICONS static int32 ButtonsSlot; static CSprite2d ButtonSprite[MAX_BUTTON_ICONS]; static int PS2Symbol; static void LoadButtons(const char *txdPath); static void DrawButton(float x, float y); #endif // BUTTON_ICONS static void Initialise(void); static void Shutdown(void); static void InitPerFrame(void); static void PrintChar(float x, float y, wchar c); static void PrintString(float x, float y, wchar *s); #ifdef XBOX_SUBTITLES static void PrintStringFromBottom(float x, float y, wchar *str); static void PrintOutlinedString(float x, float y, wchar *str, float outlineStrength, bool fromBottom, CRGBA outlineColor); #endif static int GetNumberLines(float xstart, float ystart, wchar *s); static void GetTextRect(CRect *rect, float xstart, float ystart, wchar *s); //#ifdef MORE_LANGUAGES // static bool PrintString(float x, float y, wchar *start, wchar* &end, float spwidth, float japX); //#else static void PrintString(float x, float y, uint32, wchar *start, wchar *end, float spwidth); //#endif static void PrintStringFromBottom(float x, float y, wchar *str); static float GetCharacterWidth(wchar c); static float GetCharacterSize(wchar c); static float GetStringWidth(wchar *s, bool spaces = false); #ifdef MORE_LANGUAGES static float GetStringWidth_Jap(wchar* s); #endif static uint16 *GetNextSpace(wchar *s); //#ifdef MORE_LANGUAGES // static uint16 *ParseToken(wchar *s, bool japShit = false); //#else static uint16 *ParseToken(wchar *s); static uint16 *ParseToken(wchar *s, CRGBA &color, bool &flash, bool &bold); //#endif static void DrawFonts(void); static void RenderFontBuffer(void); static uint16 character_code(uint8 c); static void SetScale(float x, float y); static void SetSlantRefPoint(float x, float y); static void SetSlant(float s); static void SetJustifyOn(void); static void SetJustifyOff(void); static void SetRightJustifyOn(void); static void SetRightJustifyOff(void); static void SetCentreOn(void); static void SetCentreOff(void); static void SetWrapx(float x); static void SetCentreSize(float s); static void SetBackgroundOn(void); static void SetBackgroundOff(void); static void SetBackGroundOnlyTextOn(void); static void SetBackGroundOnlyTextOff(void); static void SetPropOn(void); static void SetPropOff(void); static void SetFontStyle(int16 style); static void SetRightJustifyWrap(float wrap); static void SetAlphaFade(float fade); static void SetDropShadowPosition(int16 pos); static void SetBackgroundColor(CRGBA col); static void SetColor(CRGBA col); static void SetDropColor(CRGBA col); static wchar FindNewCharacter(wchar c); static void FilterOutTokensFromString(wchar*); #ifdef MORE_LANGUAGES static void ReloadFonts(uint8 set); // japanese stuff static bool IsAnsiCharacter(wchar* s); static bool IsJapanesePunctuation(wchar* str); static bool IsJapanese() { return LanguageSet == FONT_LANGSET_JAPANESE; } static bool IsJapaneseFont() { return IsJapanese() && (Details.style == FONT_JAPANESE); } #endif }; ================================================ FILE: src/render/Glass.cpp ================================================ #include "common.h" #include "Glass.h" #include "Timer.h" #include "Object.h" #include "Vehicle.h" #include "Pools.h" #include "General.h" #include "AudioScriptObject.h" #include "World.h" #include "Timecycle.h" #include "Particle.h" #include "Camera.h" #include "RenderBuffer.h" #include "Shadows.h" #include "ModelIndices.h" #include "main.h" #include "soundlist.h" #include "SurfaceTable.h" uint32 CGlass::NumGlassEntities; CEntity *CGlass::apEntitiesToBeRendered[NUM_GLASSENTITIES]; CFallingGlassPane CGlass::aGlassPanes[NUM_GLASSPANES]; CVector2D CentersWithTriangle[NUM_GLASSTRIANGLES]; const CVector2D CoorsWithTriangle[NUM_GLASSTRIANGLES][3] = { { CVector2D(0.0f, 0.0f), CVector2D(0.0f, 1.0f), CVector2D(0.4f, 0.5f) }, { CVector2D(0.0f, 1.0f), CVector2D(1.0f, 1.0f), CVector2D(0.4f, 0.5f) }, { CVector2D(0.0f, 0.0f), CVector2D(0.4f, 0.5f), CVector2D(0.7f, 0.0f) }, { CVector2D(0.7f, 0.0f), CVector2D(0.4f, 0.5f), CVector2D(1.0f, 1.0f) }, { CVector2D(0.7f, 0.0f), CVector2D(1.0f, 1.0f), CVector2D(1.0f, 0.0f) } }; #define TEMPBUFFERVERTHILIGHTOFFSET 0 #define TEMPBUFFERINDEXHILIGHTOFFSET 0 #define TEMPBUFFERVERTHILIGHTSIZE 256 #define TEMPBUFFERINDEXHILIGHTSIZE 512 #define TEMPBUFFERVERTSHATTEREDOFFSET TEMPBUFFERVERTHILIGHTSIZE #define TEMPBUFFERINDEXSHATTEREDOFFSET TEMPBUFFERINDEXHILIGHTSIZE #define TEMPBUFFERVERTSHATTEREDSIZE 384 #define TEMPBUFFERINDEXSHATTEREDSIZE 768 #define TEMPBUFFERVERTREFLECTIONOFFSET TEMPBUFFERVERTSHATTEREDSIZE #define TEMPBUFFERINDEXREFLECTIONOFFSET TEMPBUFFERINDEXSHATTEREDSIZE #define TEMPBUFFERVERTREFLECTIONSIZE 512 #define TEMPBUFFERINDEXREFLECTIONSIZE 1024 int32 TempBufferIndicesStoredHiLight = 0; int32 TempBufferVerticesStoredHiLight = 0; int32 TempBufferIndicesStoredShattered = 0; int32 TempBufferVerticesStoredShattered = 0; int32 TempBufferIndicesStoredReflection = 0; int32 TempBufferVerticesStoredReflection = 0; void CFallingGlassPane::Update(void) { if ( CTimer::GetTimeInMilliseconds() >= m_nTimer ) { // Apply MoveSpeed if ( m_bCarGlass ) GetPosition() += m_vecMoveSpeed * CTimer::GetTimeStep() * 0.35f; else GetPosition() += m_vecMoveSpeed * CTimer::GetTimeStep(); // Apply Gravity if ( m_bCarGlass ) m_vecMoveSpeed.z -= 0.01f * CTimer::GetTimeStep(); else m_vecMoveSpeed.z -= 0.02f * CTimer::GetTimeStep(); // Apply TurnSpeed GetRight() += CrossProduct(m_vecTurn, GetRight()); GetForward() += CrossProduct(m_vecTurn, GetForward()); GetUp() += CrossProduct(m_vecTurn, GetUp()); if ( GetPosition().z < m_fGroundZ ) { CVector pos; CVector dir; m_bActive = false; pos = CVector(GetPosition().x, GetPosition().y, m_fGroundZ); PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_LIGHT_BREAK, pos); RwRGBA color = { 255, 255, 255, 255 }; if ( !m_bCarGlass ) { static int32 nFrameGen = 0; for ( int32 i = 0; i < 4; i++ ) { dir.x = CGeneral::GetRandomNumberInRange(-0.35f, 0.35f); dir.y = CGeneral::GetRandomNumberInRange(-0.35f, 0.35f); dir.z = CGeneral::GetRandomNumberInRange(0.05f, 0.20f); CParticle::AddParticle(PARTICLE_CAR_DEBRIS, pos, dir, nil, CGeneral::GetRandomNumberInRange(0.02f, 0.2f), color, CGeneral::GetRandomNumberInRange(-40, 40), 0, ++nFrameGen & 3, 500); } } } } } void CFallingGlassPane::Render(void) { float distToCamera = (TheCamera.GetPosition() - GetPosition()).Magnitude(); CVector fwdNorm = GetForward(); fwdNorm.Normalise(); uint8 alpha = CGlass::CalcAlphaWithNormal(&fwdNorm); #ifdef FIX_BUGS uint16 time = clamp(CTimer::GetTimeInMilliseconds() > m_nTimer ? CTimer::GetTimeInMilliseconds() - m_nTimer : 0u, 0u, 500u); #else uint16 time = clamp(CTimer::GetTimeInMilliseconds() - m_nTimer, 0, 500); #endif uint8 color = int32( float(alpha) * (float(time) / 500) ); if ( TempBufferIndicesStoredHiLight >= TEMPBUFFERINDEXHILIGHTSIZE-7 || TempBufferVerticesStoredHiLight >= TEMPBUFFERVERTHILIGHTSIZE-4 ) CGlass::RenderHiLightPolys(); // HiLight Polys if ( m_bCarGlass && color < 64 ) color = 64; RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 0], color, color, color, color); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 1], color, color, color, color); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 2], color, color, color, color); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 0], 0.5f); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 0], 0.5f); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 1], 0.5f); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 1], 0.6f); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 2], 0.6f); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 2], 0.6f); ASSERT(m_nTriIndex < NUM_GLASSTRIANGLES); CVector2D p0 = CoorsWithTriangle[m_nTriIndex][0] - CentersWithTriangle[m_nTriIndex]; CVector2D p1 = CoorsWithTriangle[m_nTriIndex][1] - CentersWithTriangle[m_nTriIndex]; CVector2D p2 = CoorsWithTriangle[m_nTriIndex][2] - CentersWithTriangle[m_nTriIndex]; CVector v0 = *this * CVector(p0.x, 0.0f, p0.y); CVector v1 = *this * CVector(p1.x, 0.0f, p1.y); CVector v2 = *this * CVector(p2.x, 0.0f, p2.y); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 0], v0.x, v0.y, v0.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 1], v1.x, v1.y, v1.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredHiLight + 2], v2.x, v2.y, v2.z); TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 0] = TempBufferVerticesStoredHiLight + 0; TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 1] = TempBufferVerticesStoredHiLight + 1; TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 2] = TempBufferVerticesStoredHiLight + 2; TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 3] = TempBufferVerticesStoredHiLight + 0; TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 4] = TempBufferVerticesStoredHiLight + 2; TempBufferRenderIndexList[TempBufferIndicesStoredHiLight + 5] = TempBufferVerticesStoredHiLight + 1; TempBufferVerticesStoredHiLight += 3; TempBufferIndicesStoredHiLight += 6; if ( m_bShattered ) { if ( TempBufferIndicesStoredShattered >= TEMPBUFFERINDEXSHATTEREDSIZE-7 || TempBufferVerticesStoredShattered >= TEMPBUFFERVERTSHATTEREDSIZE-4 ) CGlass::RenderShatteredPolys(); uint8 shatteredColor = 140; if ( distToCamera > 30.0f ) shatteredColor = int32((1.0f - (distToCamera - 30.0f) * 4.0f / 40.0f) * 140); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], shatteredColor, shatteredColor, shatteredColor, shatteredColor); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], shatteredColor, shatteredColor, shatteredColor, shatteredColor); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], shatteredColor, shatteredColor, shatteredColor, shatteredColor); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], 4.0f * CoorsWithTriangle[m_nTriIndex][0].x * m_fStep); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], 4.0f * CoorsWithTriangle[m_nTriIndex][0].y * m_fStep); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], 4.0f * CoorsWithTriangle[m_nTriIndex][1].x * m_fStep); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], 4.0f * CoorsWithTriangle[m_nTriIndex][1].y * m_fStep); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], 4.0f * CoorsWithTriangle[m_nTriIndex][2].x * m_fStep); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], 4.0f * CoorsWithTriangle[m_nTriIndex][2].y * m_fStep); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], v0.x, v0.y, v0.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], v1.x, v1.y, v1.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], v2.x, v2.y, v2.z); TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 0] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 0; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 1] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 1; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 2] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 2; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 3] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 0; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 4] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 2; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 5] = TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET + 1; TempBufferIndicesStoredShattered += 6; TempBufferVerticesStoredShattered += 3; } } void CGlass::Init(void) { for ( int32 i = 0; i < NUM_GLASSPANES; i++ ) aGlassPanes[i].m_bActive = false; for ( int32 i = 0; i < NUM_GLASSTRIANGLES; i++ ) CentersWithTriangle[i] = (CoorsWithTriangle[i][0] + CoorsWithTriangle[i][1] + CoorsWithTriangle[i][2]) / 3; } void CGlass::Update(void) { for ( int32 i = 0; i < NUM_GLASSPANES; i++ ) { if ( aGlassPanes[i].m_bActive ) aGlassPanes[i].Update(); } } void CGlass::Render(void) { TempBufferVerticesStoredHiLight = 0; TempBufferIndicesStoredHiLight = 0; TempBufferVerticesStoredShattered = TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferIndicesStoredShattered = TEMPBUFFERINDEXSHATTEREDOFFSET; TempBufferVerticesStoredReflection = TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferIndicesStoredReflection = TEMPBUFFERINDEXREFLECTIONOFFSET; RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEFOGCOLOR, (void *)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255)); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); PUSH_RENDERGROUP("CGlass::Render"); for ( int32 i = 0; i < NUM_GLASSPANES; i++ ) { if ( aGlassPanes[i].m_bActive ) aGlassPanes[i].Render(); } for ( uint32 i = 0; i < NumGlassEntities; i++ ) RenderEntityInGlass(apEntitiesToBeRendered[i]); POP_RENDERGROUP(); NumGlassEntities = 0; RenderHiLightPolys(); RenderShatteredPolys(); RenderReflectionPolys(); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); } CFallingGlassPane * CGlass::FindFreePane(void) { for ( int32 i = 0; i < NUM_GLASSPANES; i++ ) { if ( !aGlassPanes[i].m_bActive ) return &aGlassPanes[i]; } return nil; } void CGlass::GeneratePanesForWindow(uint32 type, CVector pos, CVector up, CVector right, CVector speed, CVector center, float moveSpeed, bool cracked, bool explosion, int32 stepmul, bool carGlass) { float upLen = up.Magnitude(); float rightLen = right.Magnitude(); float upSteps = upLen + 0.75f; if ( upSteps < 1.0f ) upSteps = 1.0f; float rightSteps = rightLen + 0.75f; if ( rightSteps < 1.0f ) rightSteps = 1.0f; uint32 ysteps = stepmul * (uint32)upSteps; if ( ysteps > 3 ) ysteps = 3; uint32 xsteps = stepmul * (uint32)rightSteps; if ( xsteps > 3 ) xsteps = 3; if ( explosion ) { if ( ysteps > 1 ) ysteps = 1; if ( xsteps > 1 ) xsteps = 1; } float upScl = upLen / float(ysteps); float rightScl = rightLen / float(xsteps); bool bZFound; float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &bZFound); if ( !bZFound ) groundZ = pos.z - 2.0f; for ( uint32 y = 0; y < ysteps; y++ ) { for ( uint32 x = 0; x < xsteps; x++ ) { float stepy = float(y) * upLen / float(ysteps); float stepx = float(x) * rightLen / float(xsteps); for ( int32 i = 0; i < NUM_GLASSTRIANGLES; i++ ) { CFallingGlassPane *pane = FindFreePane(); if ( pane ) { pane->m_nTriIndex = i; pane->GetRight() = (right * rightScl) / rightLen; pane->GetUp() = (up * upScl) / upLen; CVector fwd = CrossProduct(pane->GetRight(), pane->GetUp()); fwd.Normalise(); pane->GetForward() = fwd; pane->GetPosition() = right / rightLen * (rightScl * CentersWithTriangle[i].x + stepx) + up / upLen * (upScl * CentersWithTriangle[i].y + stepy) + pos; pane->m_vecMoveSpeed.x = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.0015f + speed.x; pane->m_vecMoveSpeed.y = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.0015f + speed.y; pane->m_vecMoveSpeed.z = 0.0f + speed.z; if ( moveSpeed != 0.0f ) { CVector dist = pane->GetPosition() - center; dist.Normalise(); pane->m_vecMoveSpeed += moveSpeed * dist; } pane->m_vecTurn.x = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.002f; pane->m_vecTurn.y = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.002f; pane->m_vecTurn.z = float((CGeneral::GetRandomNumber() & 127) - 64) * 0.002f; switch ( type ) { case 0: case 2: pane->m_nTimer = CTimer::GetTimeInMilliseconds(); break; case 1: float dist = (pane->GetPosition() - center).Magnitude(); pane->m_nTimer = uint32(dist*100 + CTimer::GetTimeInMilliseconds()); break; } pane->m_fGroundZ = groundZ; pane->m_bShattered = cracked; pane->m_fStep = upLen / float(ysteps); pane->m_bCarGlass = carGlass; pane->m_bActive = true; } } } } } void CGlass::AskForObjectToBeRenderedInGlass(CEntity *entity) { #ifdef FIX_BUGS if ( NumGlassEntities < NUM_GLASSENTITIES ) #else if ( NumGlassEntities < NUM_GLASSENTITIES-1 ) #endif { apEntitiesToBeRendered[NumGlassEntities++] = entity; } } void CGlass::RenderEntityInGlass(CEntity *entity) { ASSERT(entity!=nil); CObject *object = (CObject *)entity; if ( object->bGlassBroken ) return; float distToCamera = (TheCamera.GetPosition() - object->GetPosition()).Magnitude(); if ( distToCamera > 40.0f ) return; CVector fwdNorm = object->GetForward(); fwdNorm.Normalise(); uint8 alpha = CalcAlphaWithNormal(&fwdNorm); CColModel *col = object->GetColModel(); ASSERT(col!=nil); if ( col->numTriangles >= 2 ) { CVector a = object->GetMatrix() * col->vertices[0].Get(); CVector b = object->GetMatrix() * col->vertices[1].Get(); CVector c = object->GetMatrix() * col->vertices[2].Get(); CVector d = object->GetMatrix() * col->vertices[3].Get(); if ( object->bGlassCracked ) { uint8 color = 255; if ( distToCamera > 30.0f ) color = int32((1.0f - (distToCamera - 30.0f) * 4.0f / 40.0f) * 255); if ( TempBufferIndicesStoredShattered >= TEMPBUFFERINDEXSHATTEREDSIZE-13 || TempBufferVerticesStoredShattered >= TEMPBUFFERVERTSHATTEREDSIZE-5 ) RenderShatteredPolys(); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], color, color, color, color); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], color, color, color, color); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], color, color, color, color); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 3], color, color, color, color); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], 0.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], 0.0f); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], 16.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], 0.0f); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], 0.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], 16.0f); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 3], 16.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 3], 16.0f); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 0], a.x, a.y, a.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 1], b.x, b.y, b.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 2], c.x, c.y, c.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredShattered + 3], d.x, d.y, d.z); TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 0] = col->triangles[0].a + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 1] = col->triangles[0].b + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 2] = col->triangles[0].c + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 3] = col->triangles[1].a + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 4] = col->triangles[1].b + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 5] = col->triangles[1].c + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 6] = col->triangles[0].a + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 7] = col->triangles[0].c + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 8] = col->triangles[0].b + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 9] = col->triangles[1].a + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 10] = col->triangles[1].c + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredShattered + 11] = col->triangles[1].b + TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET; TempBufferIndicesStoredShattered += 12; TempBufferVerticesStoredShattered += 4; } if ( TempBufferIndicesStoredReflection >= TEMPBUFFERINDEXREFLECTIONSIZE-13 || TempBufferVerticesStoredReflection >= TEMPBUFFERVERTREFLECTIONSIZE-5 ) RenderReflectionPolys(); uint8 color = 100; if ( distToCamera > 30.0f ) color = int32((1.0f - (distToCamera - 30.0f) * 4.0f / 40.0f) * 100); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 0], color, color, color, color); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 1], color, color, color, color); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 2], color, color, color, color); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 3], color, color, color, color); float FwdAngle = CGeneral::GetATanOfXY(TheCamera.GetForward().x, TheCamera.GetForward().y); float v = 2.0f * TheCamera.GetForward().z * 0.2f; float u = float(object->m_randomSeed & 15) * 0.02f + (FwdAngle / TWOPI); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 0], u); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 0], v); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 1], u+0.2f); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 1], v); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 2], u); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 2], v+0.2f); RwIm3DVertexSetU (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 3], u+0.2f); RwIm3DVertexSetV (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 3], v+0.2f); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 0], a.x, a.y, a.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 1], b.x, b.y, b.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 2], c.x, c.y, c.z); RwIm3DVertexSetPos (&TempBufferRenderVertices[TempBufferVerticesStoredReflection + 3], d.x, d.y, d.z); TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 0] = col->triangles[0].a + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 1] = col->triangles[0].b + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 2] = col->triangles[0].c + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 3] = col->triangles[1].a + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 4] = col->triangles[1].b + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 5] = col->triangles[1].c + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 6] = col->triangles[0].a + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 7] = col->triangles[0].c + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 8] = col->triangles[0].b + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 9] = col->triangles[1].a + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 10] = col->triangles[1].c + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferRenderIndexList[TempBufferIndicesStoredReflection + 11] = col->triangles[1].b + TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET; TempBufferIndicesStoredReflection += 12; TempBufferVerticesStoredReflection += 4; } } int32 CGlass::CalcAlphaWithNormal(CVector *normal) { ASSERT(normal!=nil); float fwdDir = 2.0f * DotProduct(*normal, TheCamera.GetForward()); float fwdDot = DotProduct(TheCamera.GetForward()-fwdDir*(*normal), CVector(0.57f, 0.57f, -0.57f)); return int32(lerp(fwdDot*fwdDot*fwdDot*fwdDot*fwdDot*fwdDot, 20.0f, 255.0f)); } void CGlass::RenderHiLightPolys(void) { if ( TempBufferVerticesStoredHiLight != TEMPBUFFERVERTHILIGHTOFFSET ) { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)RwTextureGetRaster(gpShadowExplosionTex)); LittleTest(); if ( RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStoredHiLight, nil, rwIM3D_VERTEXUV) ) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStoredHiLight); RwIm3DEnd(); } TempBufferVerticesStoredHiLight = TEMPBUFFERVERTHILIGHTOFFSET; TempBufferIndicesStoredHiLight = TEMPBUFFERINDEXHILIGHTOFFSET; } } void CGlass::RenderShatteredPolys(void) { if ( TempBufferVerticesStoredShattered != TEMPBUFFERVERTSHATTEREDOFFSET ) { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)RwTextureGetRaster(gpCrackedGlassTex)); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); LittleTest(); if ( RwIm3DTransform(&TempBufferRenderVertices[TEMPBUFFERVERTSHATTEREDOFFSET], TempBufferVerticesStoredShattered - TEMPBUFFERVERTSHATTEREDOFFSET, nil, rwIM3D_VERTEXUV) ) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, &TempBufferRenderIndexList[TEMPBUFFERINDEXSHATTEREDOFFSET], TempBufferIndicesStoredShattered - TEMPBUFFERINDEXSHATTEREDOFFSET); RwIm3DEnd(); } TempBufferIndicesStoredShattered = TEMPBUFFERINDEXSHATTEREDOFFSET; TempBufferVerticesStoredShattered = TEMPBUFFERVERTSHATTEREDOFFSET; } } void CGlass::RenderReflectionPolys(void) { if ( TempBufferVerticesStoredReflection != TEMPBUFFERVERTREFLECTIONOFFSET ) { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)RwTextureGetRaster(gpShadowHeadLightsTex)); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); LittleTest(); if ( RwIm3DTransform(&TempBufferRenderVertices[TEMPBUFFERVERTREFLECTIONOFFSET], TempBufferVerticesStoredReflection - TEMPBUFFERVERTREFLECTIONOFFSET, nil, rwIM3D_VERTEXUV) ) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, &TempBufferRenderIndexList[TEMPBUFFERINDEXREFLECTIONOFFSET], TempBufferIndicesStoredReflection - TEMPBUFFERINDEXREFLECTIONOFFSET); RwIm3DEnd(); } TempBufferIndicesStoredReflection = TEMPBUFFERINDEXREFLECTIONOFFSET; TempBufferVerticesStoredReflection = TEMPBUFFERVERTREFLECTIONOFFSET; } } void CGlass::WindowRespondsToCollision(CEntity *entity, float amount, CVector speed, CVector point, bool explosion) { ASSERT(entity!=nil); CObject *object = (CObject *)entity; if ( object->bGlassBroken ) return; object->bGlassCracked = true; CColModel *col = object->GetColModel(); ASSERT(col!=nil); if ( col->numTriangles == 2 ) { CVector a = col->vertices[0].Get(); CVector b = col->vertices[1].Get(); CVector c = col->vertices[2].Get(); CVector d = col->vertices[3].Get(); float minx = Min(Min(a.x, b.x), Min(c.x, d.x)); float maxx = Max(Max(a.x, b.x), Max(c.x, d.x)); float miny = Min(Min(a.y, b.y), Min(c.y, d.y)); float maxy = Max(Max(a.y, b.y), Max(c.y, d.y)); float minz = Min(Min(a.z, b.z), Min(c.z, d.z)); float maxz = Max(Max(a.z, b.z), Max(c.z, d.z)); CVector pa = object->GetMatrix() * CVector(minx, miny, minz); CVector pb = object->GetMatrix() * CVector(maxx, maxy, minz); if ( amount > 300.0f ) { PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_BREAK_L, object->GetPosition()); GeneratePanesForWindow(0, pa, CVector(0.0f, 0.0f, maxz-minz), pb - pa, speed, point, 0.1f, !!object->bGlassCracked, explosion, 1, false); } else { PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_BREAK_S, object->GetPosition()); GeneratePanesForWindow(1, pa, CVector(0.0f, 0.0f, maxz-minz), pb - pa, speed, point, 0.1f, !!object->bGlassCracked, explosion, 1, false); } } object->bGlassBroken = true; object->bIsVisible = false; object->bUsesCollision = false; } void CGlass::WindowRespondsToSoftCollision(CEntity *entity, float amount) { ASSERT(entity!=nil); CObject *object = (CObject *)entity; if ( entity->bUsesCollision && amount > 50.0f && !object->bGlassCracked ) { PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_CRACK, object->GetPosition()); object->bGlassCracked = true; } } void CGlass::WasGlassHitByBullet(CEntity *entity, CVector point) { ASSERT(entity!=nil); CObject *object = (CObject *)entity; if ( IsGlass(object->GetModelIndex()) ) { if ( object->bUsesCollision ) { if ( !object->bGlassCracked ) { PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_CRACK, object->GetPosition()); object->bGlassCracked = true; } else { if ( (CGeneral::GetRandomNumber() & 3) == 2 ) WindowRespondsToCollision(object, 0.0f, CVector(0.0f, 0.0f, 0.0f), point, false); } } } } void CGlass::WindowRespondsToExplosion(CEntity *entity, CVector point) { ASSERT(entity!=nil); CObject *object = (CObject *)entity; if ( object->bUsesCollision ) { CVector distToGlass = object->GetPosition() - point; float fDistToGlass = distToGlass.Magnitude(); if ( fDistToGlass < 10.0f ) { distToGlass *= (0.3f / fDistToGlass); // normalise WindowRespondsToCollision(object, 10000.0f, distToGlass, object->GetPosition(), true); } else { if ( fDistToGlass < 30.0f ) object->bGlassCracked = true; } } } void CGlass::CarWindscreenShatters(CVehicle *vehicle, bool unk) { ASSERT(vehicle!=nil); CColModel *col = vehicle->GetColModel(); ASSERT(col!=nil); if ( col->numTriangles < 2 ) return; CColTriangle *tria = nil; int32 triIndex = -1; CColTriangle *trib = nil; for ( int32 i = 0; i < col->numTriangles; i++ ) { CColTriangle *tri = &col->triangles[i]; if ( tri->surface == SURFACE_GLASS ) { if ( tria ) { trib = tri; break; } triIndex = i; tria = tri; } } if ( trib == nil ) return; CCollision::CalculateTrianglePlanes(col); CColTrianglePlane *triPlanes = col->trianglePlanes; if ( triPlanes == nil ) return; CVector planeNormal; triPlanes[triIndex].GetNormal(planeNormal); planeNormal = Multiply3x3(vehicle->GetMatrix(), planeNormal); CVector vec1 = CrossProduct(vehicle->GetRight(), planeNormal); vec1.Normalise(); CVector vec2 = CrossProduct(planeNormal, vehicle->GetUp()); vec2.Normalise(); CVector v[6]; float proj1[6]; float proj2[6]; v[0] = col->vertices[tria->a].Get(); v[1] = col->vertices[tria->b].Get(); v[2] = col->vertices[tria->c].Get(); v[3] = col->vertices[trib->a].Get(); v[4] = col->vertices[trib->b].Get(); v[5] = col->vertices[trib->c].Get(); v[0] = vehicle->GetMatrix() * v[0]; v[1] = vehicle->GetMatrix() * v[1]; v[2] = vehicle->GetMatrix() * v[2]; v[3] = vehicle->GetMatrix() * v[3]; v[4] = vehicle->GetMatrix() * v[4]; v[5] = vehicle->GetMatrix() * v[5]; proj1[0] = DotProduct(v[0], vec1); proj2[0] = DotProduct(v[0], vec2); proj1[1] = DotProduct(v[1], vec1); proj2[1] = DotProduct(v[1], vec2); proj1[2] = DotProduct(v[2], vec1); proj2[2] = DotProduct(v[2], vec2); proj1[3] = DotProduct(v[3], vec1); proj2[3] = DotProduct(v[3], vec2); proj1[4] = DotProduct(v[4], vec1); proj2[4] = DotProduct(v[4], vec2); proj1[5] = DotProduct(v[5], vec1); proj2[5] = DotProduct(v[5], vec2); int32 originIndex = 0; float max1 = proj1[0]; float max2 = proj2[0]; float origin = proj1[0] + proj2[0]; for ( int32 i = 1; i < 6; i++ ) { float o = proj1[i] + proj2[i]; if ( o < origin ) { origin = o; originIndex = i; } if ( proj1[i] > max1 ) max1 = proj1[i]; if ( proj2[i] > max2 ) max2 = proj2[i]; } float bound1 = max1 - proj1[originIndex]; float bound2 = max2 - proj2[originIndex]; PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_BREAK_L, vehicle->GetPosition()); CVector center = v[originIndex] + ((0.5f*bound1) * vec1) + ((0.5f*bound2) * vec2); CVector speed = vehicle->m_vecMoveSpeed; CVector right = bound2 * vec2; CVector up = bound1 * vec1; CVector pos = v[originIndex]; GeneratePanesForWindow(2, pos, up, right, speed, center, 0.1f, false, false, 2, true); } bool CGlass::HasGlassBeenShatteredAtCoors(float x, float y, float z) { CEntity *entity = nil; float dist = 20.0f; int32 nStartX = Max(CWorld::GetSectorIndexX(x - 30.0f), 0); int32 nStartY = Max(CWorld::GetSectorIndexY(y - 30.0f), 0); int32 nEndX = Min(CWorld::GetSectorIndexX(x + 30.0f), NUMSECTORS_X-1); int32 nEndY = Min(CWorld::GetSectorIndexY(y + 30.0f), NUMSECTORS_Y-1); CWorld::AdvanceCurrentScanCode(); for ( int32 ys = nStartY; ys <= nEndY; ys++ ) { for ( int32 xs = nStartX; xs <= nEndX; xs++ ) { CSector *sector = CWorld::GetSector(xs, ys); ASSERT(sector != nil); FindWindowSectorList(sector->m_lists[ENTITYLIST_OBJECTS], &dist, &entity, x, y, z); FindWindowSectorList(sector->m_lists[ENTITYLIST_DUMMIES], &dist, &entity, x, y, z); } } if ( entity ) { if ( entity->GetType() == ENTITY_TYPE_DUMMY ) return false; return !!((CObject*)entity)->bGlassBroken; } return false; } void CGlass::FindWindowSectorList(CPtrList &list, float *dist, CEntity **entity, float x, float y, float z) { ASSERT(dist!=nil); ASSERT(entity!=nil); CPtrNode *node = list.first; while ( node != nil ) { CEntity *ent = (CEntity *)node->item; uint16 scanCode = ent->m_scanCode; node = node->next; ASSERT(ent!=nil); if ( IsGlass(ent->GetModelIndex()) ) { if ( scanCode != CWorld::GetCurrentScanCode() ) { ent->m_scanCode = CWorld::GetCurrentScanCode(); float dst = (CVector(x,y,z) - ent->GetPosition()).Magnitude(); if ( dst < *dist ) { *dist = dst; *entity = ent; } } } } } void CGlass::BreakGlassPhysically(CVector pos, float radius) { static uint32 breakTime = 0; if ( CTimer::GetTimeInMilliseconds() < breakTime + 1000 && CTimer::GetTimeInMilliseconds() >= breakTime ) return; CColSphere sphere; sphere.piece = 0; sphere.radius = radius; sphere.surface = 0; for ( int32 i = CPools::GetObjectPool()->GetSize() - 1; i >= 0; i-- ) { CObject *object = CPools::GetObjectPool()->GetSlot(i); if (object) { if ( IsGlass(object->GetModelIndex()) ) { if ( object->bUsesCollision ) { CColModel *col = object->GetColModel(); ASSERT(col!=nil); if ( col->numTriangles < 2 ) continue; bool hit = false; CVector dist = pos - object->GetPosition(); sphere.center.x = DotProduct(dist, object->GetRight()); sphere.center.y = DotProduct(dist, object->GetForward()); sphere.center.z = DotProduct(dist, object->GetUp()); CCollision::CalculateTrianglePlanes(col); for ( int32 j = 0; j < col->numTriangles; j++ ) { if ( CCollision::TestSphereTriangle(sphere, col->vertices, col->triangles[j], col->trianglePlanes[j]) ) { hit = true; } } if ( hit ) { breakTime = CTimer::GetTimeInMilliseconds(); if ( object->bGlassCracked ) { CVector a = col->vertices[0].Get(); CVector b = col->vertices[1].Get(); CVector c = col->vertices[2].Get(); CVector d = col->vertices[3].Get(); float minx = Min(Min(a.x, b.x), Min(c.x, d.x)); float maxx = Max(Max(a.x, b.x), Max(c.x, d.x)); float miny = Min(Min(a.y, b.y), Min(c.y, d.y)); float maxy = Max(Max(a.y, b.y), Max(c.y, d.y)); float minz = Min(Min(a.z, b.z), Min(c.z, d.z)); float maxz = Max(Max(a.z, b.z), Max(c.z, d.z)); CVector pa = object->GetMatrix() * CVector(minx, miny, minz); CVector pb = object->GetMatrix() * CVector(maxx, maxy, minz); PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_BREAK_S, object->GetPosition()); GeneratePanesForWindow(1, pa, CVector(0.0f, 0.0f, maxz-minz), pb - pa, CVector(0.0f, 0.0f, 0.0f), pos, 0.1f, !!object->bGlassCracked, false, 1, false); object->bGlassBroken = true; object->bIsVisible = false; object->bUsesCollision = false; } else { PlayOneShotScriptObject(SCRIPT_SOUND_GLASS_CRACK, object->GetPosition()); object->bGlassCracked = true; } } } } } } } ================================================ FILE: src/render/Glass.h ================================================ #pragma once class CEntity; class CVehicle; class CPtrList; class CFallingGlassPane : public CMatrix { public: CVector m_vecMoveSpeed; CVector m_vecTurn; uint32 m_nTimer; float m_fGroundZ; float m_fStep; uint8 m_nTriIndex; bool m_bActive; bool m_bShattered; bool m_bCarGlass; CFallingGlassPane() { } ~CFallingGlassPane() { } void Update(void); void Render(void); }; VALIDATE_SIZE(CFallingGlassPane, 0x70); enum { NUM_GLASSTRIANGLES = 5, }; class CGlass { static uint32 NumGlassEntities; static CEntity *apEntitiesToBeRendered[NUM_GLASSENTITIES]; static CFallingGlassPane aGlassPanes[NUM_GLASSPANES]; public: static void Init(void); static void Update(void); static void Render(void); static CFallingGlassPane *FindFreePane(void); static void GeneratePanesForWindow(uint32 type, CVector pos, CVector up, CVector right, CVector speed, CVector center, float moveSpeed, bool cracked, bool explosion, int32 stepmul, bool carGlass); static void AskForObjectToBeRenderedInGlass(CEntity *entity); static void RenderEntityInGlass(CEntity *entity); static int32 CalcAlphaWithNormal(CVector *normal); static void RenderHiLightPolys(void); static void RenderShatteredPolys(void); static void RenderReflectionPolys(void); static void WindowRespondsToCollision(CEntity *entity, float amount, CVector speed, CVector point, bool explosion); static void WindowRespondsToSoftCollision(CEntity *entity, float amount); static void WasGlassHitByBullet(CEntity *entity, CVector point); static void WindowRespondsToExplosion(CEntity *entity, CVector point); static void CarWindscreenShatters(CVehicle *vehicle, bool unk); static bool HasGlassBeenShatteredAtCoors(float x, float y, float z); static void FindWindowSectorList(CPtrList &list, float *dist, CEntity **entity, float x, float y, float z); static void BreakGlassPhysically(CVector pos, float radius); }; ================================================ FILE: src/render/Hud.cpp ================================================ #include "common.h" #include "Camera.h" #include "DMAudio.h" #include "Clock.h" #include "Darkel.h" #include "Hud.h" #include "Messages.h" #include "Frontend.h" #include "Font.h" #include "Pad.h" #include "Radar.h" #include "Replay.h" #include "Wanted.h" #include "Sprite.h" #include "Sprite2d.h" #include "Text.h" #include "Timer.h" #include "Script.h" #include "TxdStore.h" #include "User.h" #include "World.h" #include "CutsceneMgr.h" #include "Stats.h" #include "main.h" #include "General.h" #include "VarConsole.h" #if defined(FIX_BUGS) #define SCREEN_SCALE_X_FIX(a) SCREEN_SCALE_X(a) #define SCREEN_SCALE_Y_FIX(a) SCREEN_SCALE_Y(a) #define SCALE_AND_CENTER_X_FIX(a) SCALE_AND_CENTER_X(a) #else #define SCREEN_SCALE_X_FIX(a) (a) #define SCREEN_SCALE_Y_FIX(a) (a) #define SCALE_AND_CENTER_X_FIX(a) (a) #endif // Game has colors inlined in code. // For easier modification we collect them here: CRGBA MONEY_COLOR(0, 207, 133, 255); CRGBA AMMO_COLOR(255, 150, 225, 255); CRGBA HEALTH_COLOR(255, 150, 225, 255); CRGBA ARMOUR_COLOR(185, 185, 185, 255); CRGBA NOTWANTED_COLOR(27, 89, 130, 255); CRGBA WANTED_COLOR_FLASH(62, 141, 181, 255); CRGBA WANTED_COLOR(97, 194, 247, 255); CRGBA ZONE_COLOR(45, 155, 90, 255); CRGBA VEHICLE_COLOR(97, 194, 247, 255); CRGBA CLOCK_COLOR(97, 194, 247, 255); CRGBA TIMER_COLOR(97, 194, 247, 255); CRGBA COUNTER_COLOR(97, 194, 247, 255); CRGBA PAGER_COLOR(32, 162, 66, 205); CRGBA RADARDISC_COLOR(255, 255, 255, 255); CRGBA BIGMESSAGE_COLOR(255, 150, 225, 255); CRGBA WASTEDBUSTED_COLOR(0, 207, 133, 255); CRGBA ODDJOB_COLOR(0, 207, 133, 255); CRGBA ODDJOB2_COLOR(97, 194, 247, 255); CRGBA MISSIONTITLE_COLOR(220, 172, 2, 255); wchar CHud::m_HelpMessage[HELP_MSG_LENGTH]; wchar CHud::m_LastHelpMessage[HELP_MSG_LENGTH]; uint32 CHud::m_HelpMessageState; uint32 CHud::m_HelpMessageTimer; int32 CHud::m_HelpMessageFadeTimer; wchar CHud::m_HelpMessageToPrint[HELP_MSG_LENGTH]; float CHud::m_HelpMessageDisplayTime; bool CHud::m_HelpMessageDisplayForever; bool CHud::m_HelpMessageQuick; uint32 CHud::m_ZoneState; int32 CHud::m_ZoneFadeTimer; uint32 CHud::m_ZoneNameTimer; wchar *CHud::m_pZoneName; wchar *CHud::m_pLastZoneName; wchar *CHud::m_ZoneToPrint; uint32 CHud::m_VehicleState; int32 CHud::m_VehicleFadeTimer; uint32 CHud::m_VehicleNameTimer; wchar *CHud::m_VehicleName; wchar *CHud::m_pLastVehicleName; wchar *CHud::m_pVehicleNameToPrint; wchar CHud::m_Message[256]; wchar CHud::m_PagerMessage[256]; bool CHud::m_Wants_To_Draw_Hud; bool CHud::m_Wants_To_Draw_3dMarkers; wchar CHud::m_BigMessage[6][128]; int16 CHud::m_ItemToFlash; bool CHud::m_HideRadar; int32 CHud::m_ClockState; // These aren't really in CHud float CHud::BigMessageInUse[6]; float CHud::BigMessageAlpha[6]; float CHud::BigMessageX[6]; float CHud::OddJob2OffTimer; bool CHud::CounterOnLastFrame[NUMONSCREENCOUNTERS]; float CHud::OddJob2XOffset; uint16 CHud::CounterFlashTimer[NUMONSCREENCOUNTERS]; uint16 CHud::OddJob2Timer; bool CHud::TimerOnLastFrame; int16 CHud::OddJob2On; uint16 CHud::TimerFlashTimer; int16 CHud::PagerSoundPlayed; int32 CHud::SpriteBrightness; float CHud::PagerXOffset; int16 CHud::PagerTimer; int16 CHud::PagerOn; wchar *prevChaseString; uint32 CHud::m_WantedFadeTimer; uint32 CHud::m_WantedState; uint32 CHud::m_WantedTimer; uint32 CHud::m_EnergyLostFadeTimer; uint32 CHud::m_EnergyLostState; uint32 CHud::m_EnergyLostTimer; uint32 CHud::m_DisplayScoreFadeTimer; uint32 CHud::m_DisplayScoreState; uint32 CHud::m_DisplayScoreTimer; uint32 CHud::m_WeaponFadeTimer; uint32 CHud::m_WeaponState; uint32 CHud::m_WeaponTimer; uint32 CHud::m_LastDisplayScore; uint32 CHud::m_LastWanted; uint32 CHud::m_LastWeapon; uint32 CHud::m_LastTimeEnergyLost; CSprite2d CHud::Sprites[NUM_HUD_SPRITES]; struct { const char *name; const char *mask; } WeaponFilenames[] = { { "fist", "fistm" }, { "brassk", "brasskA" }, { "screw", "screwA" }, { "golf", "golfA" }, { "nightstick", "nightstickA" }, { "knife", "knifeA" }, { "bat", "batm" }, { "hammer", "hammerA" }, { "cleaver", "cleaverA" }, { "machete", "macheteA" }, { "sword", "swordA" }, { "chainsaw", "chainsawA" }, { "grenade", "grenadeA" }, { "grenade", "grenadeA" }, { "teargas", "teargasA" }, { "molotov", "molotovA" }, { "rocket", "rocketA" }, { "handGun1", "handGun1A" }, { "", "" }, { "python", "pythonA" }, { "chromegun", "chromegunA" }, { "spasshotGun", "spasshotGunA" }, { "stubshotGun", "stubshotGunA" }, { "tec9", "tec9A" }, { "uzi1", "uzi1A" }, { "uzi2", "uzi2A" }, { "mp5", "mp5A" }, { "", "" }, { "m4", "m4A" }, { "ruger", "rugerA" }, { "sniper", "sniperA" }, { "laserscope", "laserscopeA" }, { "", "" }, { "rocket", "rocketA" }, { "flamer", "flamerA" }, { "m60", "m60A" }, { "minigun", "minigunA" }, { "bomb", "bombA" }, { "", "" }, { "camera", "cameraA" }, { "", "" }, { "siterocket", "siterocket" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "radardisc", "radardisc" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "", "" }, { "sitesniper", "sitesniperm" }, { "siteM16", "siteM16m" }, { "sitelaser", "sitelaserm" }, { "laserdot", "laserdotm" }, { "viewfinder_128", "viewfinder_128m" }, { "bleeder", "" } }; RwTexture *gpSniperSightTex; RwTexture *gpRocketSightTex; RwTexture *gpLaserSightTex; RwTexture *gpLaserDotTex; RwTexture *gpViewFinderTex; void CHud::Draw() { RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); // disable hud via second controller if (CPad::GetPad(1)->GetStartJustDown()) m_Wants_To_Draw_Hud = !m_Wants_To_Draw_Hud; if (CReplay::IsPlayingBack()) return; if (m_Wants_To_Draw_Hud && !TheCamera.m_WideScreenOn) { // unused statics in here bool DrawCrossHair = false; bool CrossHairHidesHud = false; bool DrawCrossHairPC = false; CPlayerPed *playerPed = FindPlayerPed(); eWeaponType WeaponType = playerPed->GetWeapon()->m_eWeaponType; int32 Mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if ((Mode == CCam::MODE_SNIPER || Mode == CCam::MODE_ROCKETLAUNCHER || Mode == CCam::MODE_M16_1STPERSON || Mode == CCam::MODE_HELICANNON_1STPERSON || Mode == CCam::MODE_CAMERA) && playerPed && !playerPed->GetWeapon()->IsTypeMelee()) DrawCrossHair = true; if (Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || Mode == CCam::MODE_SNIPER_RUNABOUT) DrawCrossHairPC = true; if (TheCamera.Cams[TheCamera.ActiveCam].Using3rdPersonMouseCam() && (!CPad::GetPad(0)->GetLookBehindForPed() || TheCamera.m_bPlayerIsInGarage) || Mode == CCam::MODE_1STPERSON_RUNABOUT) { if (playerPed) { if (playerPed->m_nPedState != PED_ENTER_CAR && playerPed->m_nPedState != PED_CARJACK) { if (WeaponType >= WEAPONTYPE_COLT45 && WeaponType <= WEAPONTYPE_RUGER || WeaponType == WEAPONTYPE_M60 || WeaponType == WEAPONTYPE_MINIGUN || WeaponType == WEAPONTYPE_FLAMETHROWER) { DrawCrossHairPC = 1; } } } } if (DrawCrossHair || DrawCrossHairPC) { RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); SpriteBrightness = Min(SpriteBrightness+1, 30); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); float fStep = Sin((CTimer::GetTimeInMilliseconds() & 1023)/1024.0f * 6.28f); float fMultBright = SpriteBrightness * 0.03f * (0.25f * fStep + 0.75f); CRect rect; if (DrawCrossHairPC && TheCamera.Cams[TheCamera.ActiveCam].Using3rdPersonMouseCam()) { float f3rdX = SCREEN_WIDTH * TheCamera.m_f3rdPersonCHairMultX; float f3rdY = SCREEN_HEIGHT * TheCamera.m_f3rdPersonCHairMultY; #ifdef ASPECT_RATIO_SCALE f3rdY -= SCREEN_SCALE_Y(2.0f); #endif if (playerPed && (WeaponType == WEAPONTYPE_M4 || WeaponType == WEAPONTYPE_RUGER || WeaponType == WEAPONTYPE_M60)) { rect.left = f3rdX - SCREEN_SCALE_X(32.0f * 0.6f); rect.top = f3rdY - SCREEN_SCALE_Y(32.0f * 0.6f); rect.right = f3rdX + SCREEN_SCALE_X(32.0f * 0.6f); rect.bottom = f3rdY + SCREEN_SCALE_Y(32.0f * 0.6f); Sprites[HUD_SITEM16].Draw(CRect(rect), CRGBA(255, 255, 255, 255), 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); } else { rect.left = f3rdX - SCREEN_SCALE_X(32.0f * 0.4f); rect.top = f3rdY - SCREEN_SCALE_Y(32.0f * 0.4f); rect.right = f3rdX + SCREEN_SCALE_X(32.0f * 0.4f); rect.bottom = f3rdY + SCREEN_SCALE_Y(32.0f * 0.4f); Sprites[HUD_SITEM16].Draw(CRect(rect), CRGBA(255, 255, 255, 255), 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); } } else { if (Mode == CCam::MODE_M16_1STPERSON || Mode == CCam::MODE_M16_1STPERSON_RUNABOUT || Mode == CCam::MODE_HELICANNON_1STPERSON) { rect.left = (SCREEN_WIDTH / 2) - SCREEN_SCALE_X(32.0f); rect.top = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(32.0f); rect.right = (SCREEN_WIDTH / 2) + SCREEN_SCALE_X(32.0f); rect.bottom = (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(32.0f); Sprites[HUD_SITEM16].Draw(CRect(rect), CRGBA(255, 255, 255, 255), 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); } else if (Mode == CCam::MODE_1STPERSON_RUNABOUT) { rect.left = (SCREEN_WIDTH / 2) - SCREEN_SCALE_X(32.0f * 0.7f); rect.top = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(32.0f * 0.7f); rect.right = (SCREEN_WIDTH / 2) + SCREEN_SCALE_X(32.0f * 0.7f); rect.bottom = (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(32.0f * 0.7f); Sprites[HUD_SITEM16].Draw(CRect(rect), CRGBA(255, 255, 255, 255), 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); } else if (Mode == CCam::MODE_ROCKETLAUNCHER || Mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT) { RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpRocketSightTex)); CSprite::RenderOneXLUSprite(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 1.0f, SCREEN_SCALE_X(40.0f), SCREEN_SCALE_Y(40.0f), (100.0f * fMultBright), (200.0f * fMultBright), (100.0f * fMultBright), 255, 1.0f, 255); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); } else { int sprite = HUD_SITESNIPER; float xOffset = SCREEN_SCALE_X(210.0f); float yOffset = SCREEN_SCALE_Y(210.0f); if (FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE) sprite = HUD_SITELASER; if (FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_CAMERA) { sprite = HUD_VIEWFINDER; CrossHairHidesHud = true; xOffset = SCREEN_SCALE_X(256.0f); yOffset = SCREEN_SCALE_Y(192.0f); } rect.left = SCREEN_WIDTH/2 - xOffset; rect.top = SCREEN_HEIGHT/2 - yOffset; rect.right = SCREEN_WIDTH/2; rect.bottom = SCREEN_HEIGHT/2; Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255), 0.01f, 0.01f, 1.0f, 0.0f, 0.01f, 1.0f, 1.0f, 1.0f); rect.left = SCREEN_WIDTH/2; rect.top = SCREEN_HEIGHT/2 - yOffset; rect.right = SCREEN_WIDTH/2 + xOffset; rect.bottom = SCREEN_HEIGHT/2; Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255), 0.99f, 0.0f, 0.01f, 0.01f, 0.99f, 1.0f, 0.01f, 1.0f); rect.left = SCREEN_WIDTH/2 - xOffset; rect.top = SCREEN_HEIGHT/2; rect.right = SCREEN_WIDTH/2; rect.bottom = SCREEN_HEIGHT/2 + yOffset; Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255), 0.01f, 0.99f, 1.0f, 0.99f, 0.01f, 0.01f, 1.0f, 0.01f); rect.left = SCREEN_WIDTH/2; rect.top = SCREEN_HEIGHT/2; rect.right = SCREEN_WIDTH/2 + xOffset; rect.bottom = SCREEN_HEIGHT/2 + yOffset; Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255), 0.99f, 0.99f, 0.01f, 0.99f, 0.99f, 0.01f, 0.01f, 0.01f); CVector dotPos; float size = 25.0f; if (FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE && FindPlayerPed()->GetWeapon()->LaserScopeDot(&dotPos, &size)) { RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); #ifdef FIX_BUGS RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); #else RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVDESTALPHA); #endif RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpLaserDotTex)); #ifdef FIX_BUGS int intensity = CGeneral::GetRandomNumberInRange(0, 37); #else int intensity = CGeneral::GetRandomNumberInRange(0, 35); #endif CSprite::RenderOneXLUSprite(dotPos.x, dotPos.y, dotPos.z, SCREEN_SCALE_X(size), SCREEN_SCALE_Y(size), intensity - 36, 0, 0, intensity - 36, 1.0f, 127); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); } } } RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); } else { SpriteBrightness = 0; } if (CrossHairHidesHud) return; /* DrawMoneyCounter */ wchar sPrint[16]; wchar sPrintIcon[16]; char sTemp[16]; float alpha; if (m_LastDisplayScore == CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney) { alpha = DrawFadeState(HUD_SCORE_FADING, 0); } else { alpha = DrawFadeState(HUD_SCORE_FADING, 1); m_LastDisplayScore = CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney; } if (m_DisplayScoreState != FADED_OUT) { sprintf(sTemp, "$%08d", CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney); AsciiToUnicode(sTemp, sPrint); CFont::SetPropOff(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); CFont::SetCentreOff(); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_HEADING); CFont::SetPropOff(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, alpha)); MONEY_COLOR.a = alpha; CFont::SetColor(MONEY_COLOR); if (FrontEndMenuManager.m_PrefsShowHud) { CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(43.0f), sPrint); } } /* DrawAmmo */ if (m_LastWeapon == playerPed->GetWeapon()->m_eWeaponType) { alpha = CHud::DrawFadeState(HUD_WEAPON_FADING, 0); } else { alpha = CHud::DrawFadeState(HUD_WEAPON_FADING, 1); m_LastWeapon = playerPed->GetWeapon()->m_eWeaponType; } if (m_WeaponState != FADED_OUT) { CWeapon *weapon = playerPed->GetWeapon(); int32 AmmoAmount = CWeaponInfo::GetWeaponInfo((eWeaponType)WeaponType)->m_nAmountofAmmunition; int32 AmmoInClip = weapon->m_nAmmoInClip; int32 TotalAmmo = weapon->m_nAmmoTotal; int32 Ammo, Clip; if (AmmoAmount <= 1 || AmmoAmount >= 1000) sprintf(sTemp, "%d", TotalAmmo); else { if (WeaponType == WEAPONTYPE_FLAMETHROWER) { Clip = AmmoInClip / 10; Ammo = Min((TotalAmmo - AmmoInClip) / 10, 9999); } else { Clip = AmmoInClip; Ammo = Min(TotalAmmo - AmmoInClip, 9999); } sprintf(sTemp, "%d-%d", Ammo, Clip); } AsciiToUnicode(sTemp, sPrint); CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo((eWeaponType)WeaponType); /* DrawWeaponIcon */ if (FrontEndMenuManager.m_PrefsShowHud) { if (weaponInfo->m_nModelId <= 0) { RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); if (FrontEndMenuManager.m_PrefsShowHud) Sprites[WeaponType].Draw( CRect(SCREEN_SCALE_FROM_RIGHT(99.0f), SCREEN_SCALE_Y(27.0f), SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(91.0f)), CRGBA(255, 255, 255, alpha), 0.015f, 0.015f, 1.0f, 0.0f, 0.015f, 1.0f, 1.0f, 1.0f); } else { CBaseModelInfo *weaponModel = CModelInfo::GetModelInfo(weaponInfo->m_nModelId); RwTexDictionary *weaponTxd = CTxdStore::GetSlot(weaponModel->GetTxdSlot())->texDict; if (weaponTxd) { RwTexture *weaponIcon = RwTexDictionaryFindNamedTexture(weaponTxd, weaponModel->GetModelName()); if (weaponIcon) { RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); #ifndef FIX_BUGS const float xSize = SCREEN_SCALE_X(64.0f / 2.0f); const float ySize = SCREEN_SCALE_Y(64.0f / 2.0f); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(weaponIcon)); CSprite::RenderOneXLUSprite(SCREEN_SCALE_FROM_RIGHT(99.0f) + xSize, SCREEN_SCALE_Y(25.0f) + ySize, 1.0f, xSize, ySize, 255, 255, 255, 255, 1.0f, 255); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); #else static CSprite2d sprite; sprite.m_pTexture = weaponIcon; sprite.Draw( CRect(SCREEN_SCALE_FROM_RIGHT(99.0f), SCREEN_SCALE_Y(27.0f), SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(91.0f)), CRGBA(255, 255, 255, alpha), 0.015f, 0.015f, 1.0f, 0.0f, 0.015f, 1.0f, 1.0f, 1.0f); sprite.m_pTexture = nil; #endif } } } CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.8f)); CFont::SetJustifyOff(); CFont::SetCentreOn(); CFont::SetCentreSize(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); CFont::SetPropOn(); CFont::SetDropShadowPosition(0); CFont::SetFontStyle(FONT_STANDARD); if (Min(9999, TotalAmmo - AmmoInClip) != 9999 && !CDarkel::FrenzyOnGoing() && weaponInfo->m_nWeaponSlot > 1 && weapon->m_eWeaponType != WEAPONTYPE_DETONATOR) { CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, alpha)); AMMO_COLOR.a = alpha; CFont::SetColor(AMMO_COLOR); if (FrontEndMenuManager.m_PrefsShowHud) CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(66.0f), SCREEN_SCALE_Y(90.0f), sPrint); CFont::SetDropShadowPosition(0); } } } /* DrawHealth */ if ( m_LastTimeEnergyLost == CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss ) { CHud::DrawFadeState(HUD_ENERGY_FADING, 0); } else { CHud::DrawFadeState(HUD_ENERGY_FADING, 1); m_LastTimeEnergyLost = CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss; } if (m_EnergyLostState != FADED_OUT) { CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); CFont::SetJustifyOff(); CFont::SetCentreOff(); CFont::SetRightJustifyWrap(0.0f); CFont::SetRightJustifyOn(); CFont::SetPropOff(); CFont::SetFontStyle(FONT_HEADING); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); if (m_ItemToFlash == ITEM_HEALTH && CTimer::GetFrameCounter() & 8 || m_ItemToFlash != ITEM_HEALTH || playerPed->m_fHealth < 10 && CTimer::GetFrameCounter() & 8) { if (playerPed->m_fHealth >= 10 || playerPed->m_fHealth < 10 && CTimer::GetFrameCounter() & 8) { AsciiToUnicode("{", sPrintIcon); #ifdef FIX_BUGS sprintf(sTemp, "%03d", int32(playerPed->m_fHealth + 0.5f)); #else sprintf(sTemp, "%03d", (int32)playerPed->m_fHealth); #endif AsciiToUnicode(sTemp, sPrint); CFont::SetColor(HEALTH_COLOR); if (FrontEndMenuManager.m_PrefsShowHud) { CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(65.0f), sPrint); if (!CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss || CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss + 2000 || CTimer::GetFrameCounter() & 4) { // CFont::SetColor(HEALTH_COLOR); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 54.0f), SCREEN_SCALE_Y(65.0f), sPrintIcon); } } } } /* DrawArmour */ if (m_ItemToFlash == ITEM_ARMOUR && CTimer::GetFrameCounter() & 8 || m_ItemToFlash != ITEM_ARMOUR) { CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); if (playerPed->m_fArmour > 1.0f) { AsciiToUnicode("<", sPrintIcon); #ifdef FIX_BUGS sprintf(sTemp, "%03d", int32(playerPed->m_fArmour + 0.5f)); #else sprintf(sTemp, "%03d", (int32)playerPed->m_fArmour); #endif AsciiToUnicode(sTemp, sPrint); CFont::SetColor(ARMOUR_COLOR); if (FrontEndMenuManager.m_PrefsShowHud) { CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(182.0f), SCREEN_SCALE_Y(65.0f), sPrint); if (!CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss || CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss + 2000 || CTimer::GetFrameCounter() & 4) { // CFont::SetColor(ARMOUR_COLOR); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(182.0f + 52.0f), SCREEN_SCALE_Y(65.0f), sPrintIcon); } } } } } /* DrawWantedLevel */ if (m_LastWanted == playerPed->m_pWanted->GetWantedLevel()) { alpha = DrawFadeState(HUD_WANTED_FADING, 0); } else { alpha = DrawFadeState(HUD_WANTED_FADING, 1); m_LastWanted = playerPed->m_pWanted->GetWantedLevel(); } if (m_WantedState != FADED_OUT) { CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); CFont::SetJustifyOff(); CFont::SetCentreOff(); CFont::SetRightJustifyOn(); CFont::SetPropOn(); CFont::SetFontStyle(FONT_STANDARD); AsciiToUnicode(">", sPrintIcon); for (int i = 0; i < 6; i++) { if (FrontEndMenuManager.m_PrefsShowHud) { if (playerPed->m_pWanted->GetWantedLevel() > i && (CTimer::GetTimeInMilliseconds() > playerPed->m_pWanted->m_nLastWantedLevelChange + 2000 || CTimer::GetFrameCounter() & 4)) { WANTED_COLOR.a = alpha; CFont::SetColor(WANTED_COLOR); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 23.0f * i), SCREEN_SCALE_Y(87.0f), sPrintIcon); } else if (playerPed->m_pWanted->m_nMinWantedLevel > i && CTimer::GetFrameCounter() & 4) { WANTED_COLOR_FLASH.a = alpha; CFont::SetColor(WANTED_COLOR_FLASH); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 23.0f * i), SCREEN_SCALE_Y(87.0f), sPrintIcon); } else if (playerPed->m_pWanted->GetWantedLevel() <= i) { NOTWANTED_COLOR.a = alpha; CFont::SetColor(NOTWANTED_COLOR); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 23.0f * i), SCREEN_SCALE_Y(87.0f), sPrintIcon); } } } } static int32 nMediaLevelCounter = 0; if (CStats::ShowChaseStatOnScreen != 0) { float fCurAttentionLevel = CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention; if (0.7f * CStats::HighestChaseValue > fCurAttentionLevel || fCurAttentionLevel <= 40.0f || CTheScripts::IsPlayerOnAMission()) { nMediaLevelCounter = 0; } else { if (fCurAttentionLevel == CStats::HighestChaseValue) { sprintf(gString, "%s %d", UnicodeToAscii(TheText.Get("CHSE")), (int32)fCurAttentionLevel); } else { sprintf(gString, "%s %d" "-%d-", UnicodeToAscii(TheText.Get("CHSE")), (int32)fCurAttentionLevel, (int32)CStats::HighestChaseValue); } AsciiToUnicode(gString, gUString); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); CFont::SetCentreOff(); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_HEADING); CFont::SetPropOff(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CRGBA colour; if (CTimer::GetTimeInMilliseconds() & 0x200) colour = CRGBA(204, 0, 185, 180); else colour = CRGBA(178, 0, 162, 180); CFont::SetColor(colour); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(113.0f), gUString); if (CStats::FindChaseString(fCurAttentionLevel) != prevChaseString) { prevChaseString = CStats::FindChaseString(fCurAttentionLevel); nMediaLevelCounter = 100; } if (nMediaLevelCounter != 0) { nMediaLevelCounter--; UnicodeMakeUpperCase(gUString, CStats::FindChaseString(fCurAttentionLevel)); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(138.0f), gUString); } } } /* DrawZoneName */ if (m_pZoneName) { if (m_pZoneName != m_pLastZoneName) { switch (m_ZoneState) { case 0: m_ZoneState = 2; m_ZoneToPrint = m_pZoneName; m_ZoneNameTimer = 0; m_ZoneFadeTimer = 0; if (m_VehicleState == 1 || m_VehicleState == 2) m_VehicleState = 3; break; case 1: case 2: case 3: case 4: m_ZoneNameTimer = 5; m_ZoneState = 4; break; default: break; } m_pLastZoneName = m_pZoneName; } float fZoneAlpha = 255.0f; if (m_ZoneState) { switch (m_ZoneState) { case 1: fZoneAlpha = 255.0f; m_ZoneFadeTimer = 1000; if (m_ZoneNameTimer > 10000.0f) { m_ZoneFadeTimer = 1000; m_ZoneState = 3; } break; case 2: m_ZoneFadeTimer += CTimer::GetTimeStepInMilliseconds(); if (m_ZoneFadeTimer > 1000.0f) { m_ZoneState = 1; m_ZoneFadeTimer = 1000; } fZoneAlpha = m_ZoneFadeTimer * 0.001f * 255.0f; break; case 3: m_ZoneFadeTimer -= CTimer::GetTimeStepInMilliseconds(); if (m_ZoneFadeTimer < 0.0f) { m_ZoneState = 0; m_ZoneFadeTimer = 0; } fZoneAlpha = m_ZoneFadeTimer * 0.001f * 255.0f; break; case 4: m_ZoneFadeTimer -= CTimer::GetTimeStepInMilliseconds(); if (m_ZoneFadeTimer < 0.0f) { m_ZoneFadeTimer = 0; m_ZoneToPrint = m_pLastZoneName; m_ZoneState = 2; } fZoneAlpha = m_ZoneFadeTimer * 0.001f * 255.0f; break; default: break; } if (!m_Message[0] && BigMessageInUse[1] == 0.0f && BigMessageInUse[2] == 0.0f) { m_ZoneNameTimer += CTimer::GetTimeStepInMilliseconds(); CFont::SetJustifyOff(); CFont::SetPropOn(); CFont::SetBackgroundOff(); if (FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) CFont::SetScale(SCREEN_SCALE_X(1.7f * 0.8f), SCREEN_SCALE_Y(1.8f)); else CFont::SetScale(SCREEN_SCALE_X(1.7f), SCREEN_SCALE_Y(1.8f)); CFont::SetSlantRefPoint(SCREEN_SCALE_FROM_RIGHT(32.0f), SCREEN_SCALE_FROM_BOTTOM(128.0f)); CFont::SetSlant(0.15f); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetBackGroundOnlyTextOff(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, fZoneAlpha)); CFont::SetFontStyle(FONT_BANK); CFont::SetColor(CRGBA(ZONE_COLOR.r, ZONE_COLOR.g, ZONE_COLOR.b, fZoneAlpha)); if (!CTheScripts::bPlayerIsInTheStatium) CFont::PrintStringFromBottom(SCREEN_SCALE_FROM_RIGHT(32.0f), SCREEN_SCALE_FROM_BOTTOM(128.0f), m_ZoneToPrint); CFont::SetSlant(0.f); } else { m_ZoneState = 3; } } } /* DrawVehicleName */ if (m_VehicleName) { float fVehicleAlpha = 0.0f; if (m_VehicleName != m_pLastVehicleName) { switch (m_VehicleState) { case 0: m_VehicleState = 2; m_pVehicleNameToPrint = m_VehicleName; m_VehicleNameTimer = 0; m_VehicleFadeTimer = 0; if (m_ZoneState == 1 || m_ZoneState == 2) m_ZoneState = 3; break; case 1: case 2: case 3: case 4: m_VehicleNameTimer = 0; m_VehicleState = 4; break; default: break; } m_pLastVehicleName = m_VehicleName; } if (m_VehicleState) { switch (m_VehicleState) { case 1: if (m_VehicleNameTimer > 10000) { m_VehicleFadeTimer = 1000; m_VehicleState = 3; } fVehicleAlpha = 255.0f; break; case 2: m_VehicleFadeTimer += CTimer::GetTimeStepInMilliseconds(); if (m_VehicleFadeTimer > 1000) { m_VehicleState = 1; m_VehicleFadeTimer = 1000; } fVehicleAlpha = m_VehicleFadeTimer * 0.001f * 255.0f; break; case 3: m_VehicleFadeTimer -= CTimer::GetTimeStepInMilliseconds(); if (m_VehicleFadeTimer < 0) { m_VehicleState = 0; m_VehicleFadeTimer = 0; } fVehicleAlpha = m_VehicleFadeTimer * 0.001f * 255.0f; break; case 4: m_VehicleFadeTimer -= CTimer::GetTimeStepInMilliseconds(); if (m_VehicleFadeTimer < 0) { m_VehicleFadeTimer = 0; m_pVehicleNameToPrint = m_pLastVehicleName; m_VehicleNameTimer = 0; m_VehicleState = 2; } fVehicleAlpha = m_VehicleFadeTimer * 0.001f * 255.0f; break; default: break; } if (!m_Message[0]) { m_VehicleNameTimer += CTimer::GetTimeStepInMilliseconds(); CFont::SetJustifyOff(); CFont::SetPropOn(); CFont::SetBackgroundOff(); if (FrontEndMenuManager.m_PrefsLanguage != CMenuManager::LANGUAGE_ITALIAN && FrontEndMenuManager.m_PrefsLanguage != CMenuManager::LANGUAGE_SPANISH) CFont::SetScale(SCREEN_SCALE_X(1.7f), SCREEN_SCALE_Y(1.8f)); else CFont::SetScale(SCREEN_SCALE_X(1.7f * 0.85f), SCREEN_SCALE_Y(1.8f)); CFont::SetSlantRefPoint(SCREEN_SCALE_FROM_RIGHT(32.0f), SCREEN_SCALE_FROM_BOTTOM(105.0f)); CFont::SetSlant(0.15f); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_BANK); CFont::SetDropShadowPosition(2); CFont::SetColor(CRGBA(VEHICLE_COLOR.r, VEHICLE_COLOR.g, VEHICLE_COLOR.b, fVehicleAlpha)); CFont::SetDropColor(CRGBA(0, 0, 0, fVehicleAlpha)); CFont::PrintStringFromBottom(SCREEN_SCALE_FROM_RIGHT(32.0f), SCREEN_SCALE_FROM_BOTTOM(105.0f), m_pVehicleNameToPrint); CFont::SetSlant(0.f); } } } else { m_pLastVehicleName = nil; m_VehicleState = 0; m_VehicleFadeTimer = 0; m_VehicleNameTimer = 0; } /* DrawClock */ if (m_ClockState) { CFont::SetJustifyOff(); CFont::SetCentreOff(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); CFont::SetBackGroundOnlyTextOff(); CFont::SetPropOff(); CFont::SetFontStyle(FONT_HEADING); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); sprintf(sTemp, "%02d:%02d", CClock::GetHours(), CClock::GetMinutes()); AsciiToUnicode(sTemp, sPrint); CFont::SetColor(CLOCK_COLOR); if (FrontEndMenuManager.m_PrefsShowHud) CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(111.0f), SCREEN_SCALE_Y(22.0f), sPrint); } /* DrawOnScreenTimer */ wchar sTimer[16]; if (!CUserDisplay::OnscnTimer.m_sClocks[0].m_bClockProcessed) TimerOnLastFrame = false; for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { if (!CUserDisplay::OnscnTimer.m_sCounters[0].m_bCounterProcessed) CounterOnLastFrame[i] = false; } if (CUserDisplay::OnscnTimer.m_bProcessed) { if (CUserDisplay::OnscnTimer.m_sClocks[0].m_bClockProcessed) { if (!TimerOnLastFrame) TimerFlashTimer = 1; TimerOnLastFrame = true; if (TimerFlashTimer != 0) { if (++TimerFlashTimer > 50) TimerFlashTimer = 0; } if (CTimer::GetFrameCounter() & 4 || TimerFlashTimer == 0) { AsciiToUnicode(CUserDisplay::OnscnTimer.m_sClocks[0].m_aClockBuffer, sTimer); CFont::SetPropOn(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetPropOff(); CFont::SetBackGroundOnlyTextOn(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); CFont::SetColor(TIMER_COLOR); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f), SCREEN_SCALE_Y(110.0f), sTimer); CFont::SetPropOn(); if (CUserDisplay::OnscnTimer.m_sClocks[0].m_aClockText[0]) { CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetColor(TIMER_COLOR); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f) - SCREEN_SCALE_X(80.0f), SCREEN_SCALE_Y(110.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sClocks[0].m_aClockText)); } } } for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) { if (CUserDisplay::OnscnTimer.m_sCounters[i].m_bCounterProcessed) { if (!CounterOnLastFrame[i]) CounterFlashTimer[i] = 1; CounterOnLastFrame[i] = true; if (CounterFlashTimer[i] != 0) { if (++CounterFlashTimer[i] > 50) CounterFlashTimer[i] = 0; } if (CTimer::GetFrameCounter() & 4 || CounterFlashTimer[i] == 0) { if (CUserDisplay::OnscnTimer.m_sCounters[i].m_nType == COUNTER_DISPLAY_NUMBER) { AsciiToUnicode(CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterBuffer, sTimer); CFont::SetPropOn(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); CFont::SetCentreOff(); CFont::SetRightJustifyOn(); CFont::SetRightJustifyWrap(0.0f); CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH)); CFont::SetPropOn(); CFont::SetBackGroundOnlyTextOn(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetColor(COUNTER_COLOR); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y * 20.f * i) + SCREEN_SCALE_Y(132.0f), sTimer); } else { int counter = atoi(CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterBuffer); const float barWidth = SCREEN_SCALE_X(100.f / 2.f); const float right = SCREEN_SCALE_FROM_RIGHT(37.0f); const float left = right - barWidth; const float barHeight = SCREEN_SCALE_Y(11.0f); const float top = SCREEN_SCALE_Y(132.0f) + SCREEN_SCALE_Y(8.0f) + SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y * 20.f * i); const float bottom = top + barHeight; // shadow CSprite2d::DrawRect(CRect(left + SCREEN_SCALE_X(6.0f), top + SCREEN_SCALE_Y(2.0f), right + SCREEN_SCALE_X(6.0f), bottom + SCREEN_SCALE_Y(2.0f)), CRGBA(0, 0, 0, 255)); CSprite2d::DrawRect(CRect(left + SCREEN_SCALE_X(4.0f), top, right + SCREEN_SCALE_X(4.0f), bottom), CRGBA(27, 89, 130, 255)); CSprite2d::DrawRect(CRect(left + SCREEN_SCALE_X(4.0f), top, left + SCREEN_SCALE_X(counter) / 2.0f + SCREEN_SCALE_X(4.0f), bottom), CRGBA(97, 194, 247, 255)); } if (CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterText[0]) { CFont::SetPropOn(); CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING)); CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y)); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetColor(COUNTER_COLOR); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f) - SCREEN_SCALE_X(61.0f), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y * 20.f * i) + SCREEN_SCALE_Y(132.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterText)); } // unused/leftover color. I wonder what was it for CFont::SetColor(CRGBA(244, 225, 91, 255)); } } } } /* DrawRadar */ if (FrontEndMenuManager.m_PrefsRadarMode != 2 && !m_HideRadar && (m_ItemToFlash == ITEM_RADAR && CTimer::GetFrameCounter() & 8 || m_ItemToFlash != ITEM_RADAR)) { RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); CRadar::DrawMap(); if (FrontEndMenuManager.m_PrefsRadarMode != 1) { CRect rect(0.0f, 0.0f, SCREEN_SCALE_X(RADAR_WIDTH), SCREEN_SCALE_Y(RADAR_HEIGHT)); rect.Translate(SCREEN_SCALE_X_FIX(RADAR_LEFT), SCREEN_SCALE_FROM_BOTTOM(RADAR_BOTTOM + RADAR_HEIGHT)); #ifdef FIX_BUGS rect.Grow(SCREEN_SCALE_X(6.0f), SCREEN_SCALE_X(6.0f), SCREEN_SCALE_Y(6.0f), SCREEN_SCALE_Y(6.0f)); #else rect.Grow(6.0f); #endif rect.Translate(SCREEN_SCALE_X_FIX(0.0f), SCREEN_SCALE_Y_FIX(2.0f)); Sprites[HUD_RADARDISC].Draw(rect, CRGBA(0, 0, 0, 255)); rect.Translate(SCREEN_SCALE_X_FIX(0.0f), SCREEN_SCALE_Y_FIX(-2.0f)); Sprites[HUD_RADARDISC].Draw(rect, RADARDISC_COLOR); } CRadar::DrawBlips(); } } /* Draw3dMarkers */ if (m_Wants_To_Draw_3dMarkers && !TheCamera.m_WideScreenOn && !m_BigMessage[0][0] && !m_BigMessage[2][0]) { CRadar::Draw3dMarkers(); } /* DrawScriptText */ if (!CTimer::GetIsUserPaused()) { for (int i = 0; i < ARRAY_SIZE(CTheScripts::IntroTextLines); i++) { if (CTheScripts::IntroTextLines[i].m_Text[0] && CTheScripts::IntroTextLines[i].m_bTextBeforeFade) { CFont::SetScale(SCREEN_SCALE_X(CTheScripts::IntroTextLines[i].m_fScaleX), SCREEN_SCALE_Y(CTheScripts::IntroTextLines[i].m_fScaleY * 0.5f)); CFont::SetColor(CTheScripts::IntroTextLines[i].m_sColor); if (CTheScripts::IntroTextLines[i].m_bJustify) CFont::SetJustifyOn(); else CFont::SetJustifyOff(); if (CTheScripts::IntroTextLines[i].m_bRightJustify) CFont::SetRightJustifyOn(); else CFont::SetRightJustifyOff(); if (CTheScripts::IntroTextLines[i].m_bCentered) CFont::SetCentreOn(); else CFont::SetCentreOff(); CFont::SetWrapx(SCALE_AND_CENTER_X(CTheScripts::IntroTextLines[i].m_fWrapX)); CFont::SetCentreSize(SCREEN_SCALE_X(CTheScripts::IntroTextLines[i].m_fCenterSize)); if (CTheScripts::IntroTextLines[i].m_bBackground) CFont::SetBackgroundOn(); else CFont::SetBackgroundOff(); CFont::SetBackgroundColor(CTheScripts::IntroTextLines[i].m_sBackgroundColor); if (CTheScripts::IntroTextLines[i].m_bBackgroundOnly) CFont::SetBackGroundOnlyTextOn(); else CFont::SetBackGroundOnlyTextOff(); if (CTheScripts::IntroTextLines[i].m_bTextProportional) CFont::SetPropOn(); else CFont::SetPropOff(); CFont::SetFontStyle(FONT_LOCALE(CTheScripts::IntroTextLines[i].m_nFont)); CFont::PrintString(SCREEN_WIDTH - SCALE_AND_CENTER_X(DEFAULT_SCREEN_WIDTH - CTheScripts::IntroTextLines[i].m_fAtX), SCREEN_HEIGHT - SCREEN_SCALE_Y(DEFAULT_SCREEN_HEIGHT - CTheScripts::IntroTextLines[i].m_fAtY), CTheScripts::IntroTextLines[i].m_Text); } } for (int i = 0; i < ARRAY_SIZE(CTheScripts::IntroRectangles); i++) { intro_script_rectangle &IntroRect = CTheScripts::IntroRectangles[i]; // Yeah, top and bottom changed place. R* vision if (IntroRect.m_bIsUsed && IntroRect.m_bBeforeFade) { if (IntroRect.m_nTextureId >= 0) { CRect rect ( IntroRect.m_sRect.left, IntroRect.m_sRect.bottom, IntroRect.m_sRect.right, IntroRect.m_sRect.top ); CTheScripts::ScriptSprites[IntroRect.m_nTextureId].Draw(rect, IntroRect.m_sColor); } else { CRect rect ( IntroRect.m_sRect.left, IntroRect.m_sRect.bottom, IntroRect.m_sRect.right, IntroRect.m_sRect.top ); CSprite2d::DrawRect(rect, IntroRect.m_sColor); } } } /* DrawSubtitles */ if (m_Message[0] && !m_BigMessage[2][0]) { if (m_VehicleState != 0) m_VehicleState = 3; if (m_ZoneState != 0) m_ZoneState = 3; CFont::SetJustifyOff(); CFont::SetBackgroundOff(); CFont::SetBackgroundColor(CRGBA(0, 0, 0, 128)); CFont::SetCentreOn(); CFont::SetPropOn(); #ifdef CUTSCENE_BORDERS_SWITCH if (!FrontEndMenuManager.m_PrefsCutsceneBorders) { CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetDropShadowPosition(2); } else #endif CFont::SetDropShadowPosition(0); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetColor(CRGBA(225, 225, 225, 255)); static bool onceItWasWidescreen = false; if (TheCamera.m_WideScreenOn) { onceItWasWidescreen = true; if (FrontEndMenuManager.m_PrefsShowSubtitles || !CCutsceneMgr::IsRunning()) { CFont::SetCentreSize(SCREEN_WIDTH - SCREEN_SCALE_X(60.0f)); CFont::SetScale(SCREEN_SCALE_X(0.58f), SCREEN_SCALE_Y(1.2f)); CFont::PrintString(SCREEN_WIDTH / 2.f, SCREEN_SCALE_FROM_BOTTOM(80.0f), m_Message); } } else { if (onceItWasWidescreen) m_Message[0] = '\0'; onceItWasWidescreen = false; CFont::DrawFonts(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetScale(SCREEN_SCALE_X(0.58f), SCREEN_SCALE_Y(1.22f)); float radarBulge = SCREEN_SCALE_X(140.0f) + SCREEN_SCALE_X(8.0f); float rectWidth = SCREEN_WIDTH - SCREEN_SCALE_X(20.0f) - SCREEN_SCALE_X(8.0f) - radarBulge; CFont::SetCentreSize(rectWidth); CFont::PrintString(rectWidth / 2.0f + radarBulge, SCREEN_SCALE_FROM_BOTTOM(105.f + 2.0f), m_Message); } CFont::SetDropShadowPosition(0); } /* HelpMessage */ if (m_HelpMessage[0]) { if (!CMessages::WideStringCompare(m_HelpMessage, m_LastHelpMessage, HELP_MSG_LENGTH)) { switch (m_HelpMessageState) { case 0: m_HelpMessageFadeTimer = 0; m_HelpMessageState = 2; m_HelpMessageTimer = 0; CMessages::WideStringCopy(m_HelpMessageToPrint, m_HelpMessage, HELP_MSG_LENGTH); m_HelpMessageDisplayTime = CMessages::GetWideStringLength(m_HelpMessage) * 0.05f + 3.0f; if (TheCamera.m_ScreenReductionPercentage == 0.0f) DMAudio.PlayFrontEndSound(SOUND_HUD, 0); break; case 1: case 2: case 3: case 4: m_HelpMessageTimer = 5; m_HelpMessageState = 4; break; default: break; } CMessages::WideStringCopy(m_LastHelpMessage, m_HelpMessage, HELP_MSG_LENGTH); } float fAlpha = 225.0f; if (m_HelpMessageState != 0) { switch (m_HelpMessageState) { case 1: fAlpha = 225.0f; m_HelpMessageFadeTimer = 600; if (!m_HelpMessageDisplayForever && m_HelpMessageTimer > m_HelpMessageDisplayTime * 1000.0f || m_HelpMessageQuick && m_HelpMessageTimer > 1500.0f) { m_HelpMessageFadeTimer = 600; m_HelpMessageState = 3; } break; case 2: if (TheCamera.m_WideScreenOn) break; m_HelpMessageFadeTimer += 2 * CTimer::GetTimeStepInMilliseconds(); if (m_HelpMessageFadeTimer > 0) { m_HelpMessageState = 1; m_HelpMessageFadeTimer = 0; } fAlpha = m_HelpMessageFadeTimer * 0.001f * 225.0f; break; case 3: m_HelpMessageFadeTimer -= 2 * CTimer::GetTimeStepInMilliseconds(); if (m_HelpMessageFadeTimer < 0 || TheCamera.m_WideScreenOn) { m_HelpMessageState = 0; m_HelpMessageFadeTimer = 0; } fAlpha = m_HelpMessageFadeTimer * 0.001f * 225.0f; break; case 4: m_HelpMessageFadeTimer -= 2 * CTimer::GetTimeStepInMilliseconds(); if (m_HelpMessageFadeTimer < 0) { m_HelpMessageState = 2; m_HelpMessageFadeTimer = 0; CMessages::WideStringCopy(m_HelpMessageToPrint, m_LastHelpMessage, HELP_MSG_LENGTH); } fAlpha = m_HelpMessageFadeTimer * 0.001f * 225.0f; break; default: break; } if (!TheCamera.m_WideScreenOn) { m_HelpMessageTimer += CTimer::GetTimeStepInMilliseconds(); CFont::SetAlphaFade(fAlpha); CFont::SetCentreOff(); CFont::SetPropOn(); if (CGame::germanGame) CFont::SetScale(SCREEN_SCALE_X(0.52f * 0.85f), SCREEN_SCALE_Y(1.1f * 0.85f)); #ifdef MORE_LANGUAGES else if (CFont::IsJapanese()) CFont::SetScale(SCREEN_SCALE_X(0.52f) * 1.35f, SCREEN_SCALE_Y(1.1f) * 1.25f); #endif else CFont::SetScale(SCREEN_SCALE_X(0.52f), SCREEN_SCALE_Y(1.1f)); CFont::DrawFonts(); CFont::SetColor(CRGBA(175, 175, 175, 255)); CFont::SetJustifyOff(); #ifdef MORE_LANGUAGES if (CFont::IsJapanese()) CFont::SetWrapx(SCREEN_SCALE_X(229.0f + 34.0f - 4.0f)); else #endif CFont::SetWrapx(SCREEN_SCALE_X(200.0f + 34.0f - 4.0f)); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetBackgroundOn(); CFont::SetBackGroundOnlyTextOff(); CFont::SetDropShadowPosition(0); CFont::SetBackgroundColor(CRGBA(0, 0, 0, fAlpha * 0.9f)); CFont::SetColor(CRGBA(175, 175, 175, 255)); CFont::PrintString(SCREEN_SCALE_X(34.0f), SCREEN_SCALE_Y(28.0f + (150.0f - PagerXOffset) * 0.6f), m_HelpMessageToPrint); CFont::SetAlphaFade(255.0f); CFont::SetWrapx(SCREEN_WIDTH); } } } else m_HelpMessageState = 0; /* DrawBigMessage */ // MissionCompleteFailedText if (m_BigMessage[0][0]) { if (BigMessageInUse[0] != 0.0f) { CFont::SetJustifyOff(); CFont::SetBackgroundOff(); CFont::SetBackGroundOnlyTextOff(); if (CGame::frenchGame || CGame::germanGame) { CFont::SetScale(SCREEN_SCALE_X(1.6f), SCREEN_SCALE_Y(1.8f)); } else { CFont::SetScale(SCREEN_SCALE_X(1.8f), SCREEN_SCALE_Y(1.8f)); } CFont::SetPropOn(); CFont::SetCentreOn(); CFont::SetCentreSize(SCREEN_SCALE_X(590.0f)); CFont::SetColor(CRGBA(255, 255, 0, BigMessageAlpha[0])); // unused color CFont::SetFontStyle(FONT_HEADING); // Appearently sliding text in here was abandoned very early, since this text is centered now. if (BigMessageX[0] >= SCALE_AND_CENTER_X(620.0f)) { BigMessageInUse[0] += CTimer::GetTimeStep(); if (BigMessageInUse[0] >= 120.0f) { BigMessageInUse[0] = 120.0f; BigMessageAlpha[0] -= (CTimer::GetTimeStepInMilliseconds() * 0.3f); } if (BigMessageAlpha[0] <= 0.0f) { m_BigMessage[0][0] = 0; BigMessageAlpha[0] = 0.0f; } } else { BigMessageX[0] += SCREEN_SCALE_X((CTimer::GetTimeStepInMilliseconds() * 0.3f)); BigMessageAlpha[0] += (CTimer::GetTimeStepInMilliseconds() * 0.3f); if (BigMessageAlpha[0] > 255.0f) BigMessageAlpha[0] = 255.0f; } CFont::DrawFonts(); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, BigMessageAlpha[0])); CFont::SetColor(CRGBA(BIGMESSAGE_COLOR.r, BIGMESSAGE_COLOR.g, BIGMESSAGE_COLOR.b, BigMessageAlpha[0])); CFont::PrintString(SCREEN_WIDTH / 2, (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(18.0f), m_BigMessage[0]); } else { BigMessageAlpha[0] = 0.0f; BigMessageX[0] = SCALE_AND_CENTER_X(-60.0f); BigMessageInUse[0] = 1.0f; } } else { BigMessageInUse[0] = 0.0f; } // WastedBustedText if (m_BigMessage[2][0]) { if (BigMessageInUse[2] != 0.0f) { BigMessageAlpha[2] += (CTimer::GetTimeStepInMilliseconds() * 0.4f); if (BigMessageAlpha[2] > 255.0f) BigMessageAlpha[2] = 255.0f; CFont::SetBackgroundOff(); if (CGame::frenchGame || CGame::germanGame) CFont::SetScale(SCREEN_SCALE_X(1.4f), SCREEN_SCALE_Y(1.4f)); else CFont::SetScale(SCREEN_SCALE_X(2.0f), SCREEN_SCALE_Y(2.0f)); CFont::SetPropOn(); CFont::SetRightJustifyOn(); CFont::SetFontStyle(FONT_HEADING); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, BigMessageAlpha[2])); CFont::SetColor(CRGBA(WASTEDBUSTED_COLOR.r, WASTEDBUSTED_COLOR.g, WASTEDBUSTED_COLOR.b, BigMessageAlpha[2])); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(20.0f), SCREEN_SCALE_FROM_BOTTOM(90.0f), m_BigMessage[2]); } else { BigMessageInUse[2] = 1.0f; BigMessageAlpha[2] = 0.0f; if (m_VehicleState != 0) // Hide vehicle name if wasted/busted text is displaying m_VehicleState = 0; if (m_ZoneState != 0) m_ZoneState = 0; } } else { BigMessageInUse[2] = 0.0f; } } } void CHud::DrawAfterFade() { RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); if (CTimer::GetIsUserPaused() || CReplay::IsPlayingBack()) return; for (int i = 0; i < ARRAY_SIZE(CTheScripts::IntroTextLines); i++) { intro_text_line &line = CTheScripts::IntroTextLines[i]; if (line.m_Text[0] != '\0' && !line.m_bTextBeforeFade) { CFont::SetScale(SCREEN_SCALE_X(line.m_fScaleX), SCREEN_SCALE_Y(line.m_fScaleY) / 2); CFont::SetColor(line.m_sColor); if (line.m_bJustify) CFont::SetJustifyOn(); else CFont::SetJustifyOff(); if (line.m_bRightJustify) CFont::SetRightJustifyOn(); else CFont::SetRightJustifyOff(); if (line.m_bCentered) CFont::SetCentreOn(); else CFont::SetCentreOff(); CFont::SetWrapx(SCALE_AND_CENTER_X(line.m_fWrapX)); CFont::SetCentreSize(SCREEN_SCALE_X(line.m_fCenterSize)); if (line.m_bBackground) CFont::SetBackgroundOn(); else CFont::SetBackgroundOff(); CFont::SetBackgroundColor(line.m_sBackgroundColor); if (line.m_bBackgroundOnly) CFont::SetBackGroundOnlyTextOn(); else CFont::SetBackGroundOnlyTextOff(); if (line.m_bTextProportional) CFont::SetPropOn(); else CFont::SetPropOff(); CFont::SetFontStyle(line.m_nFont); CFont::PrintString(SCREEN_WIDTH - SCALE_AND_CENTER_X(DEFAULT_SCREEN_WIDTH - line.m_fAtX), SCREEN_HEIGHT - SCREEN_SCALE_Y(DEFAULT_SCREEN_HEIGHT - line.m_fAtY), line.m_Text); } } for (int i = 0; i < ARRAY_SIZE(CTheScripts::IntroRectangles); i++) { intro_script_rectangle &rectangle = CTheScripts::IntroRectangles[i]; if (rectangle.m_bIsUsed && !rectangle.m_bBeforeFade) { // Yeah, top and bottom changed place. R* vision if (rectangle.m_nTextureId >= 0) { CTheScripts::ScriptSprites[rectangle.m_nTextureId].Draw(CRect(rectangle.m_sRect.left, rectangle.m_sRect.bottom, rectangle.m_sRect.right, rectangle.m_sRect.top), rectangle.m_sColor); } else { CSprite2d::DrawRect(CRect(rectangle.m_sRect.left, rectangle.m_sRect.bottom, rectangle.m_sRect.right, rectangle.m_sRect.top), rectangle.m_sColor); } } } /* DrawBigMessage2 */ // Oddjob if (m_BigMessage[3][0]) { CFont::SetJustifyOff(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(1.2f), SCREEN_SCALE_Y(1.5f)); CFont::SetCentreOn(); CFont::SetPropOn(); CFont::SetCentreSize(SCREEN_SCALE_X(600.0f)); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetColor(ODDJOB_COLOR); CFont::PrintString((SCREEN_WIDTH / 2), SCREEN_SCALE_Y(140.0f) - SCREEN_SCALE_Y(16.0f), m_BigMessage[3]); } if (!m_BigMessage[1][0] && m_BigMessage[4][0]) { CFont::SetJustifyOff(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(1.2f), SCREEN_SCALE_Y(1.5f)); CFont::SetCentreOn(); CFont::SetPropOn(); CFont::SetCentreSize(SCREEN_SCALE_X(580.0f)); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetColor(ODDJOB_COLOR); CFont::PrintString((SCREEN_WIDTH / 2), SCREEN_SCALE_Y(140.0f), m_BigMessage[4]); } // Oddjob result if (OddJob2OffTimer > 0) OddJob2OffTimer -= CTimer::GetTimeStepInMilliseconds(); float fStep; if (m_BigMessage[5][0] && OddJob2OffTimer <= 0.0f) { switch (OddJob2On) { case 0: OddJob2On = 1; OddJob2XOffset = 380.0f; break; case 1: if (OddJob2XOffset <= 2.0f) { OddJob2Timer = 0; OddJob2On = 2; } else { fStep = Min(40.0f, OddJob2XOffset / 6.0f); OddJob2XOffset = OddJob2XOffset - fStep; } break; case 2: OddJob2Timer += CTimer::GetTimeStepInMilliseconds(); if (OddJob2Timer > 1500) { OddJob2On = 3; } break; case 3: fStep = Max(30.0f, OddJob2XOffset / 5.0f); OddJob2XOffset = OddJob2XOffset - fStep; if (OddJob2XOffset < -380.0f) { OddJob2OffTimer = 5000.0f; OddJob2On = 0; } break; default: break; } if (!m_BigMessage[1][0]) { CFont::SetJustifyOff(); CFont::SetBackgroundOff(); CFont::SetScale(SCREEN_SCALE_X(1.0f), SCREEN_SCALE_Y(1.2f)); CFont::SetCentreOn(); CFont::SetPropOn(); CFont::SetCentreSize(SCREEN_SCALE_X(560.0f)); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetColor(ODDJOB2_COLOR); CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_SCALE_Y(217.0f), m_BigMessage[5]); } } /* DrawMissionTitle */ if (m_BigMessage[1][0]) { if (BigMessageInUse[1] != 0.0f) { CFont::SetJustifyOff(); CFont::SetBackgroundOff(); // will be overwritten below if (CGame::frenchGame || FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_SPANISH) CFont::SetScale(SCREEN_SCALE_X(0.884f), SCREEN_SCALE_Y(1.36f)); else CFont::SetScale(SCREEN_SCALE_X(1.04f), SCREEN_SCALE_Y(1.6f)); CFont::SetPropOn(); CFont::SetRightJustifyWrap(SCALE_AND_CENTER_X(0.0f)); CFont::SetRightJustifyOn(); CFont::SetFontStyle(FONT_BANK); CFont::SetScale(FrontEndMenuManager.m_PrefsLanguage == CMenuManager::LANGUAGE_AMERICAN ? SCREEN_SCALE_X(1.7f) : SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.8f)); if (BigMessageX[1] >= SCREEN_SCALE_FROM_RIGHT(20.0f)) { BigMessageInUse[1] += CTimer::GetTimeStep(); if (BigMessageInUse[1] >= 120.0f) { BigMessageInUse[1] = 120.0f; BigMessageAlpha[1] -= CTimer::GetTimeStepInMilliseconds(); } if (BigMessageAlpha[1] <= 0.0f) { m_BigMessage[1][0] = 0; BigMessageInUse[1] = 0.0f; BigMessageAlpha[1] = 0.0f; } } else { BigMessageX[1] += SCREEN_SCALE_X((CTimer::GetTimeStepInMilliseconds() * 0.3f)); BigMessageAlpha[1] += CTimer::GetTimeStepInMilliseconds(); if (BigMessageAlpha[1] > 255.0f) BigMessageAlpha[1] = 255.0f; } CFont::SetColor(CRGBA(40, 40, 40, BigMessageAlpha[1])); // what was that for? CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, BigMessageAlpha[1])); CFont::SetColor(CRGBA(MISSIONTITLE_COLOR.r, MISSIONTITLE_COLOR.g, MISSIONTITLE_COLOR.b, BigMessageAlpha[1])); CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(20.0f), SCREEN_SCALE_FROM_BOTTOM(140.0f), m_BigMessage[1]); } else { m_ZoneFadeTimer = 0; BigMessageX[1] = SCREEN_SCALE_FROM_RIGHT(DEFAULT_SCREEN_WIDTH + 60.0f); BigMessageInUse[1] = 1.0f; m_ZoneState = 0; } } else { BigMessageInUse[1] = 0.0f; } } void CHud::GetRidOfAllHudMessages() { m_ZoneNameTimer = 0; m_pZoneName = nil; m_ZoneState = 0; for (int i = 0; i < HELP_MSG_LENGTH; i++) { m_HelpMessage[i] = 0; m_LastHelpMessage[i] = 0; m_HelpMessageToPrint[i] = 0; } m_HelpMessageTimer = 0; m_HelpMessageFadeTimer = 0; m_HelpMessageState = 0; m_HelpMessageQuick = 0; m_HelpMessageDisplayForever = false; m_HelpMessageDisplayTime = 1.0f; m_VehicleName = nil; m_pVehicleNameToPrint = nil; m_VehicleNameTimer = 0; m_VehicleFadeTimer = 0; m_VehicleState = 0; for (int i = 0; i < ARRAY_SIZE(m_Message); i++) m_Message[i] = 0; for (int i = 0; i < 6; i++) { BigMessageInUse[i] = 0.0f; for (int j = 0; j < 128; j++) m_BigMessage[i][j] = 0; } } #ifdef RELOADABLES void CHud::ReloadTXD() { for (int i = 0; i < NUM_HUD_SPRITES; ++i) { Sprites[i].Delete(); } int HudTXD = CTxdStore::FindTxdSlot("hud"); CTxdStore::RemoveTxdSlot(HudTXD); debug("Reloading HUD.TXD...\n"); HudTXD = CTxdStore::AddTxdSlot("hud"); CTxdStore::LoadTxd(HudTXD, "MODELS/HUD.TXD"); CTxdStore::AddRef(HudTXD); CTxdStore::PopCurrentTxd(); CTxdStore::SetCurrentTxd(HudTXD); for (int i = 0; i < NUM_HUD_SPRITES; i++) { Sprites[i].SetTexture(WeaponFilenames[i].name, WeaponFilenames[i].mask); } } #endif void CHud::Initialise() { m_Wants_To_Draw_Hud = true; m_Wants_To_Draw_3dMarkers = true; int HudTXD = CTxdStore::AddTxdSlot("hud"); CTxdStore::LoadTxd(HudTXD, "MODELS/HUD.TXD"); CTxdStore::AddRef(HudTXD); CTxdStore::PopCurrentTxd(); CTxdStore::SetCurrentTxd(HudTXD); for (int i = 0; i < NUM_HUD_SPRITES; i++) { Sprites[i].SetTexture(WeaponFilenames[i].name, WeaponFilenames[i].mask); } m_pLastZoneName = nil; GetRidOfAllHudMessages(); m_pLastVehicleName = nil; if (gpSniperSightTex == nil) gpSniperSightTex = RwTextureRead("sitesniper", nil); // unused if (gpRocketSightTex == nil) gpRocketSightTex = RwTextureRead("siterocket", nil); if (gpLaserSightTex == nil) gpLaserSightTex = RwTextureRead("sitelaser", nil); // unused if (gpLaserDotTex == nil) gpLaserDotTex = RwTextureRead("laserdot", "laserdotm"); if (gpViewFinderTex == nil) gpViewFinderTex = RwTextureRead("viewfinder_128", "viewfinder_128m"); // unused m_ClockState = 1; CounterOnLastFrame[0] = false; CounterOnLastFrame[1] = false; CounterOnLastFrame[2] = false; m_ItemToFlash = ITEM_NONE; OddJob2Timer = 0; OddJob2OffTimer = 0.0f; OddJob2On = 0; OddJob2XOffset = 0.0f; CounterFlashTimer[0] = 0; CounterFlashTimer[1] = 0; CounterFlashTimer[2] = 0; TimerOnLastFrame = false; TimerFlashTimer = 0; SpriteBrightness = 0; PagerOn = 0; PagerTimer = 0; PagerSoundPlayed = 0; PagerXOffset = 150.0f; #ifdef HUD_AUTO_FADE m_EnergyLostState = START_FADE_OUT; m_WantedState = START_FADE_OUT; m_DisplayScoreState = START_FADE_OUT; m_WeaponState = START_FADE_OUT; #else m_EnergyLostState = FADE_DISABLED; m_WantedState = FADE_DISABLED; m_DisplayScoreState = FADE_DISABLED; m_WeaponState = FADE_DISABLED; #endif m_WantedFadeTimer = 0; m_WantedTimer = 0; m_EnergyLostFadeTimer = 0; m_EnergyLostTimer = 0; m_DisplayScoreFadeTimer = 0; m_DisplayScoreTimer = 0; m_WeaponFadeTimer = 0; m_WeaponTimer = 0; m_HideRadar = false; m_LastDisplayScore = CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney; m_LastTimeEnergyLost = CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss; m_LastWanted = 0; m_LastWeapon = 0; #ifndef MASTER VarConsole.Add("Draw HUD", &m_Wants_To_Draw_Hud, false); #endif CTxdStore::PopCurrentTxd(); } void CHud::ReInitialise() { m_Wants_To_Draw_Hud = true; m_Wants_To_Draw_3dMarkers = true; m_pLastZoneName = nil; GetRidOfAllHudMessages(); m_pLastVehicleName = nil; CounterOnLastFrame[0] = false; CounterOnLastFrame[1] = false; CounterOnLastFrame[2] = false; m_ItemToFlash = ITEM_NONE; m_ClockState = 1; OddJob2Timer = 0; OddJob2OffTimer = 0.0f; OddJob2On = 0; OddJob2XOffset = 0.0f; CounterFlashTimer[0] = 0; CounterFlashTimer[1] = 0; CounterFlashTimer[2] = 0; TimerOnLastFrame = false; TimerFlashTimer = 0; SpriteBrightness = 0; PagerOn = 0; PagerTimer = 0; PagerSoundPlayed = 0; PagerXOffset = 150.0f; #ifdef HUD_AUTO_FADE m_EnergyLostState = START_FADE_OUT; m_WantedState = START_FADE_OUT; m_DisplayScoreState = START_FADE_OUT; m_WeaponState = START_FADE_OUT; #else m_EnergyLostState = FADE_DISABLED; m_WantedState = FADE_DISABLED; m_DisplayScoreState = FADE_DISABLED; m_WeaponState = FADE_DISABLED; #endif m_WantedFadeTimer = 0; m_WantedTimer = 0; m_EnergyLostFadeTimer = 0; m_EnergyLostTimer = 0; m_DisplayScoreFadeTimer = 0; m_DisplayScoreTimer = 0; m_WeaponFadeTimer = 0; m_WeaponTimer = 0; m_HideRadar = false; m_LastDisplayScore = CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney; m_LastTimeEnergyLost = CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss; m_LastWanted = 0; m_LastWeapon = 0; } wchar LastBigMessage[6][128]; void CHud::SetBigMessage(wchar *message, uint16 style) { int i = 0; if (BigMessageInUse[style] != 0.0f) return; if (style == 5) { for (i = 0; i < 128; i++) { if (message[i] == 0) break; if (message[i] != LastBigMessage[5][i]) { OddJob2On = 0; OddJob2OffTimer = 0.0f; } m_BigMessage[5][i] = message[i]; LastBigMessage[5][i] = message[i]; } } else { for (i = 0; i < 128; i++) { if (message[i] == 0) break; m_BigMessage[style][i] = message[i]; } } LastBigMessage[style][i] = 0; m_BigMessage[style][i] = 0; } void CHud::SetHelpMessage(wchar *message, bool quick, bool displayForever) { if (!CReplay::IsPlayingBack()) { for (int i = 0; i < HELP_MSG_LENGTH; i++) { m_HelpMessage[i] = 0; } for (int i = 0; i < HELP_MSG_LENGTH; i++) { m_LastHelpMessage[i] = 0; } for (int i = 0; i < HELP_MSG_LENGTH; i++) { m_HelpMessageToPrint[i] = 0; } CMessages::WideStringCopy(m_HelpMessage, message, HELP_MSG_LENGTH); CMessages::InsertPlayerControlKeysInString(m_HelpMessage); if (m_HelpMessageState == 0 || !CMessages::WideStringCompare(m_HelpMessage, m_HelpMessageToPrint, HELP_MSG_LENGTH)) { for (int i = 0; i < HELP_MSG_LENGTH; i++) { m_LastHelpMessage[i] = 0; } if (!message) { m_HelpMessage[0] = 0; m_HelpMessageToPrint[0] = 0; } if (!displayForever) { m_HelpMessageState = displayForever; } else { m_HelpMessageState = 1; CMessages::WideStringCopy(m_HelpMessageToPrint, m_HelpMessage, HELP_MSG_LENGTH); CMessages::WideStringCopy(m_LastHelpMessage, m_HelpMessage, HELP_MSG_LENGTH); } m_HelpMessageQuick = quick; m_HelpMessageDisplayForever = displayForever; } } } bool CHud::IsHelpMessageBeingDisplayed(void) { return m_HelpMessageState != 0; } void CHud::SetMessage(wchar *message) { int i = 0; for (i = 0; i < ARRAY_SIZE(m_Message); i++) { if (message[i] == 0) break; m_Message[i] = message[i]; } m_Message[i] = 0; } void CHud::SetPagerMessage(wchar *message) { int i = 0; for (i = 0; i < ARRAY_SIZE(m_PagerMessage); i++) { if (message[i] == 0) break; m_PagerMessage[i] = message[i]; } m_PagerMessage[i] = 0; } void CHud::SetVehicleName(wchar *name) { m_VehicleName = name; } void CHud::SetZoneName(wchar *name) { m_pZoneName = name; } void CHud::Shutdown() { for (int i = 0; i < NUM_HUD_SPRITES; ++i) { Sprites[i].Delete(); } RwTextureDestroy(gpSniperSightTex); gpSniperSightTex = nil; RwTextureDestroy(gpRocketSightTex); gpRocketSightTex = nil; RwTextureDestroy(gpLaserSightTex); gpLaserSightTex = nil; RwTextureDestroy(gpLaserDotTex); gpLaserDotTex = nil; RwTextureDestroy(gpViewFinderTex); gpViewFinderTex = nil; int HudTXD = CTxdStore::FindTxdSlot("hud"); CTxdStore::RemoveTxdSlot(HudTXD); } float CHud::DrawFadeState(DRAW_FADE_STATE fadingElement, int forceFadingIn) { float alpha = 255.0f; uint32 operation, timer; int32 fadeTimer; switch (fadingElement) { case HUD_WANTED_FADING: fadeTimer = m_WantedFadeTimer; operation = m_WantedState; timer = m_WantedTimer; break; case HUD_ENERGY_FADING: fadeTimer = m_EnergyLostFadeTimer; operation = m_EnergyLostState; timer = m_EnergyLostTimer; break; case HUD_SCORE_FADING: fadeTimer = m_DisplayScoreFadeTimer; operation = m_DisplayScoreState; timer = m_DisplayScoreTimer; break; case HUD_WEAPON_FADING: fadeTimer = m_WeaponFadeTimer; operation = m_WeaponState; timer = m_WeaponTimer; break; default: break; } if (forceFadingIn) { switch (operation) { case FADED_OUT: fadeTimer = 0; case START_FADE_OUT: case FADING_OUT: timer = 5; operation = FADING_IN; break; default: break; } } if (operation != FADED_OUT && operation != FADE_DISABLED) { switch (operation) { case START_FADE_OUT: fadeTimer = 1000; alpha = 255.0f; if (timer > 10000) { fadeTimer = 3000; operation = FADING_OUT; } break; case FADING_IN: fadeTimer += CTimer::GetTimeStepInMilliseconds(); if (fadeTimer > 1000.0f) { operation = START_FADE_OUT; fadeTimer = 1000; } alpha = fadeTimer / 1000.0f * 255.0f; break; case FADING_OUT: fadeTimer -= CTimer::GetTimeStepInMilliseconds(); if (fadeTimer < 0.0f) { fadeTimer = 0; operation = FADED_OUT; } alpha = fadeTimer / 1000.0f * 255.0f; break; default: break; } timer += CTimer::GetTimeStepInMilliseconds(); } switch (fadingElement) { case HUD_WANTED_FADING: m_WantedFadeTimer = fadeTimer; m_WantedState = operation; m_WantedTimer = timer; break; case HUD_ENERGY_FADING: m_EnergyLostFadeTimer = fadeTimer; m_EnergyLostState = operation; m_EnergyLostTimer = timer; break; case HUD_SCORE_FADING: m_DisplayScoreFadeTimer = fadeTimer; m_DisplayScoreState = operation; m_DisplayScoreTimer = timer; break; case HUD_WEAPON_FADING: m_WeaponFadeTimer = fadeTimer; m_WeaponState = operation; m_WeaponTimer = timer; break; default: break; } return clamp(alpha, 0.0f, 255.0f); } void CHud::ResetWastedText(void) { BigMessageInUse[2] = 0.0f; BigMessageInUse[0] = 0.0f; m_BigMessage[2][0] = 0; m_BigMessage[0][0] = 0; } ================================================ FILE: src/render/Hud.h ================================================ #pragma once #include "Sprite2d.h" #define HELP_MSG_LENGTH 256 #define HUD_TEXT_SCALE_X 0.7f #define HUD_TEXT_SCALE_Y 1.25f enum eItems { ITEM_NONE = -1, ITEM_ARMOUR = 3, ITEM_HEALTH = 4, ITEM_RADAR = 8 }; // Thanks for vague name, R* enum DRAW_FADE_STATE { HUD_WANTED_FADING = 0, HUD_ENERGY_FADING, HUD_SCORE_FADING, HUD_WEAPON_FADING, }; // My name enum eFadeOperation { FADED_OUT = 0, START_FADE_OUT, FADING_IN, FADING_OUT, FADE_DISABLED = 5, }; enum eSprites { HUD_FIST, HUD_SITEROCKET = 41, HUD_RADARDISC = 50, HUD_SITESNIPER = 63, HUD_SITEM16, HUD_SITELASER, HUD_LASERDOT, HUD_VIEWFINDER, HUD_BLEEDER, NUM_HUD_SPRITES = 69, }; class CHud { public: static CSprite2d Sprites[NUM_HUD_SPRITES]; static wchar m_HelpMessage[HELP_MSG_LENGTH]; static wchar m_LastHelpMessage[HELP_MSG_LENGTH]; static uint32 m_HelpMessageState; static uint32 m_HelpMessageTimer; static int32 m_HelpMessageFadeTimer; static wchar m_HelpMessageToPrint[HELP_MSG_LENGTH]; static float m_HelpMessageDisplayTime; static bool m_HelpMessageDisplayForever; static bool m_HelpMessageQuick; static uint32 m_ZoneState; static int32 m_ZoneFadeTimer; static uint32 m_ZoneNameTimer; static wchar *m_pZoneName; static wchar *m_pLastZoneName; static wchar *m_ZoneToPrint; static wchar *m_VehicleName; static wchar *m_pLastVehicleName; static wchar *m_pVehicleNameToPrint; static uint32 m_VehicleState; static int32 m_VehicleFadeTimer; static uint32 m_VehicleNameTimer; static wchar m_Message[256]; static wchar m_PagerMessage[256]; static bool m_Wants_To_Draw_Hud; static bool m_Wants_To_Draw_3dMarkers; static wchar m_BigMessage[6][128]; static int16 m_ItemToFlash; static bool m_HideRadar; static int32 m_ClockState; // These aren't really in CHud static float BigMessageInUse[6]; static float BigMessageAlpha[6]; static float BigMessageX[6]; static float OddJob2OffTimer; static bool CounterOnLastFrame[NUMONSCREENCOUNTERS]; static float OddJob2XOffset; static uint16 CounterFlashTimer[NUMONSCREENCOUNTERS]; static uint16 OddJob2Timer; static bool TimerOnLastFrame; static int16 OddJob2On; static uint16 TimerFlashTimer; static int16 PagerSoundPlayed; static int32 SpriteBrightness; static float PagerXOffset; static int16 PagerTimer; static int16 PagerOn; static uint32 m_WantedFadeTimer; static uint32 m_WantedState; static uint32 m_WantedTimer; static uint32 m_EnergyLostFadeTimer; static uint32 m_EnergyLostState; static uint32 m_EnergyLostTimer; static uint32 m_DisplayScoreFadeTimer; static uint32 m_DisplayScoreState; static uint32 m_DisplayScoreTimer; static uint32 m_WeaponFadeTimer; static uint32 m_WeaponState; static uint32 m_WeaponTimer; static uint32 m_LastDisplayScore; static uint32 m_LastWanted; static uint32 m_LastWeapon; static uint32 m_LastTimeEnergyLost; public: static void Draw(); static void DrawAfterFade(); static void GetRidOfAllHudMessages(); #ifdef RELOADABLES static void ReloadTXD(); #endif static void Initialise(); static void ReInitialise(); static void SetBigMessage(wchar *message, uint16 style); static void SetHelpMessage(wchar *message, bool quick, bool displayForever = false); static bool IsHelpMessageBeingDisplayed(void); static void SetMessage(wchar *message); static void SetPagerMessage(wchar *message); static void SetVehicleName(wchar *name); static void SetZoneName(wchar *name); static void Shutdown(); static float DrawFadeState(DRAW_FADE_STATE, int); static void ResetWastedText(void); }; ================================================ FILE: src/render/Instance.cpp ================================================ #include "common.h" #include "Instance.h" void CInstance::Shutdown() { GetMatrix().Detach(); } ================================================ FILE: src/render/Instance.h ================================================ #pragma once #include "Placeable.h" // unused class CInstance : public CPlaceable { public: int m_modelIndex; public: ~CInstance() { } void Shutdown(); }; ================================================ FILE: src/render/Lines.cpp ================================================ #include "common.h" #include "main.h" #include "Lines.h" // This is super inefficient, why split the line into segments at all? void CLines::RenderLineWithClipping(float x1, float y1, float z1, float x2, float y2, float z2, uint32 c1, uint32 c2) { static RwIm3DVertex v[2]; #ifdef THIS_IS_STUPID int i; float f1, f2; float len = sqrt(sq(x1-x2) + sq(y1-y2) + sq(z1-z2)); int numsegs = len/1.5f + 1.0f; RwRGBA col1; col1.red = c1>>24; col1.green = c1>>16; col1.blue = c1>>8; col1.alpha = c1; RwRGBA col2; col2.red = c2>>24; col2.green = c2>>16; col2.blue = c2>>8; col2.alpha = c2; float dx = x2 - x1; float dy = y2 - y1; float dz = z2 - z1; for(i = 0; i < numsegs; i++){ f1 = (float)i/numsegs; f2 = (float)(i+1)/numsegs; RwIm3DVertexSetRGBA(&v[0], (int)(col1.red + (col2.red-col1.red)*f1), (int)(col1.green + (col2.green-col1.green)*f1), (int)(col1.blue + (col2.blue-col1.blue)*f1), (int)(col1.alpha + (col2.alpha-col1.alpha)*f1)); RwIm3DVertexSetRGBA(&v[1], (int)(col1.red + (col2.red-col1.red)*f2), (int)(col1.green + (col2.green-col1.green)*f2), (int)(col1.blue + (col2.blue-col1.blue)*f2), (int)(col1.alpha + (col2.alpha-col1.alpha)*f2)); RwIm3DVertexSetPos(&v[0], x1 + dx*f1, y1 + dy*f1, z1 + dz*f1); RwIm3DVertexSetPos(&v[1], x1 + dx*f2, y1 + dy*f2, z1 + dz*f2); LittleTest(); if(RwIm3DTransform(v, 2, nil, 0)){ RwIm3DRenderLine(0, 1); RwIm3DEnd(); } } #else RwRGBA col1; col1.red = c1>>24; col1.green = c1>>16; col1.blue = c1>>8; col1.alpha = c1; RwRGBA col2; col2.red = c2>>24; col2.green = c2>>16; col2.blue = c2>>8; col2.alpha = c2; RwIm3DVertexSetRGBA(&v[0], col1.red, col1.green, col1.blue, col1.alpha); RwIm3DVertexSetRGBA(&v[1], col2.red, col2.green, col2.blue, col2.alpha); RwIm3DVertexSetPos(&v[0], x1, y1, z1); RwIm3DVertexSetPos(&v[1], x2, y2, z2); LittleTest(); if(RwIm3DTransform(v, 2, nil, 0)){ RwIm3DRenderLine(0, 1); RwIm3DEnd(); } #endif } ================================================ FILE: src/render/Lines.h ================================================ #pragma once class CLines { public: static void RenderLineWithClipping(float x1, float y1, float z1, float x2, float y2, float z2, uint32 c1, uint32 c2); }; ================================================ FILE: src/render/MBlur.cpp ================================================ #ifndef LIBRW #define WITHD3D #endif #include "common.h" #ifndef LIBRW #include #endif #include "main.h" #include "General.h" #include "RwHelper.h" #include "Camera.h" #include "Timecycle.h" #include "Particle.h" #include "Timer.h" #include "Hud.h" #include "Frontend.h" #include "MBlur.h" #include "postfx.h" // Originally taken from RW example 'mblur' RwRaster *CMBlur::pFrontBuffer; bool CMBlur::ms_bJustInitialised; bool CMBlur::ms_bScaledBlur; bool CMBlur::BlurOn; float CMBlur::Drunkness; int32 CMBlur::pBufVertCount; static RwIm2DVertex Vertex[4]; static RwIm2DVertex Vertex2[4]; static RwImVertexIndex Index[6] = { 0, 1, 2, 0, 2, 3 }; #ifndef LIBRW extern "C" D3DCAPS8 _RwD3D8DeviceCaps; #endif RwBool CMBlur::MotionBlurOpen(RwCamera *cam) { #ifdef EXTENDED_COLOURFILTER CPostFX::Open(cam); return TRUE; #else #ifdef GTA_PS2 RwRect rect = {0, 0, 0, 0}; if (pFrontBuffer) return TRUE; BlurOn = true; rect.w = RwRasterGetWidth(RwCameraGetRaster(cam)); rect.h = RwRasterGetHeight(RwCameraGetRaster(cam)); pFrontBuffer = RwRasterCreate(0, 0, 0, rwRASTERDONTALLOCATE|rwRASTERTYPECAMERATEXTURE); if (!pFrontBuffer) { printf("Error creating raster\n"); return FALSE; } RwRaster *raster = RwRasterSubRaster(pFrontBuffer, RwCameraGetRaster(cam), &rect); if (!raster) { RwRasterDestroy(pFrontBuffer); pFrontBuffer = NULL; printf("Error subrastering\n"); return FALSE; } CreateImmediateModeData(cam, &rect); #else RwRect rect = { 0, 0, 0, 0 }; if(pFrontBuffer) MotionBlurClose(); #ifndef LIBRW extern void _GetVideoMemInfo(LPDWORD total, LPDWORD avaible); DWORD total, avaible; _GetVideoMemInfo(&total, &avaible); debug("Available video memory %d\n", avaible); #endif if(BlurOn) { uint32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1); uint32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1); uint32 depth = RwRasterGetDepth(RwCameraGetRaster(cam)); #ifndef LIBRW extern DWORD _dwMemTotalVideo; if ( _RwD3D8DeviceCaps.MaxTextureWidth >= width && _RwD3D8DeviceCaps.MaxTextureHeight >= height ) { total = _dwMemTotalVideo - 3 * ( RwRasterGetDepth(RwCameraGetRaster(cam)) * RwRasterGetHeight(RwCameraGetRaster(cam)) * RwRasterGetWidth(RwCameraGetRaster(cam)) / 8 ); BlurOn = total >= height*width*(depth/8) + (12*1024*1024) /*12 MB*/; } else BlurOn = false; #endif if ( BlurOn ) { ms_bScaledBlur = false; rect.w = width; rect.h = height; pFrontBuffer = RwRasterCreate(rect.w, rect.h, depth, rwRASTERTYPECAMERATEXTURE); if ( !pFrontBuffer ) { debug("MBlurOpen can't create raster."); BlurOn = false; rect.w = RwRasterGetWidth(RwCameraGetRaster(cam)); rect.h = RwRasterGetHeight(RwCameraGetRaster(cam)); } else ms_bJustInitialised = true; } else { rect.w = RwRasterGetWidth(RwCameraGetRaster(cam)); rect.h = RwRasterGetHeight(RwCameraGetRaster(cam)); } #ifndef LIBRW _GetVideoMemInfo(&total, &avaible); debug("Available video memory %d\n", avaible); #endif CreateImmediateModeData(cam, &rect); } else { rect.w = RwRasterGetWidth(RwCameraGetRaster(cam)); rect.h = RwRasterGetHeight(RwCameraGetRaster(cam)); CreateImmediateModeData(cam, &rect); } return TRUE; #endif #endif } RwBool CMBlur::MotionBlurClose(void) { #ifdef EXTENDED_COLOURFILTER CPostFX::Close(); #else if(pFrontBuffer){ RwRasterDestroy(pFrontBuffer); pFrontBuffer = nil; return TRUE; } #endif return FALSE; } void CMBlur::CreateImmediateModeData(RwCamera *cam, RwRect *rect) { float zero, xmax, ymax; if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){ zero = HALFPX; xmax = rect->w + HALFPX; ymax = rect->h + HALFPX; }else{ zero = -HALFPX; xmax = rect->w - HALFPX; ymax = rect->h - HALFPX; } RwIm2DVertexSetScreenX(&Vertex[0], zero); RwIm2DVertexSetScreenY(&Vertex[0], zero); RwIm2DVertexSetScreenZ(&Vertex[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex[0], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex[0], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex[1], zero); RwIm2DVertexSetScreenY(&Vertex[1], ymax); RwIm2DVertexSetScreenZ(&Vertex[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex[1], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex[1], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex[2], xmax); RwIm2DVertexSetScreenY(&Vertex[2], ymax); RwIm2DVertexSetScreenZ(&Vertex[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex[2], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex[2], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex[3], xmax); RwIm2DVertexSetScreenY(&Vertex[3], zero); RwIm2DVertexSetScreenZ(&Vertex[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex[3], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex[3], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex2[0], zero + 2.0f); RwIm2DVertexSetScreenY(&Vertex2[0], zero + 2.0f); RwIm2DVertexSetScreenZ(&Vertex2[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex2[0], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex2[0], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex2[0], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex2[0], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex2[1], 2.0f); RwIm2DVertexSetScreenY(&Vertex2[1], ymax + 2.0f); RwIm2DVertexSetScreenZ(&Vertex2[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex2[1], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex2[1], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex2[1], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex2[1], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex2[1], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex2[2], xmax + 2.0f); RwIm2DVertexSetScreenY(&Vertex2[2], ymax + 2.0f); RwIm2DVertexSetScreenZ(&Vertex2[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex2[2], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex2[2], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex2[2], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex2[2], 255, 255, 255, 255); RwIm2DVertexSetScreenX(&Vertex2[3], xmax + 2.0f); RwIm2DVertexSetScreenY(&Vertex2[3], zero + 2.0f); RwIm2DVertexSetScreenZ(&Vertex2[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&Vertex2[3], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&Vertex2[3], 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetU(&Vertex2[3], 1.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetV(&Vertex2[3], 0.0f, 1.0f/RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetIntRGBA(&Vertex2[3], 255, 255, 255, 255); } void CMBlur::CreateImmediateModeData(RwCamera *cam, RwRect *rect, RwIm2DVertex *verts, RwRGBA color, float u1Off, float v1Off, float u2Off, float v2Off, float z, int fullTexture) { float x1 = rect->x; float y1 = rect->y; float x2 = rect->w; float y2 = rect->h; float u1, v1, u2, v2; if(fullTexture){ u1 = 0.0f; v1 = 0.0f; u2 = 1.0f; v2 = 1.0f; }else{ if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){ x1 += HALFPX; y1 += HALFPX; x2 += HALFPX; y2 += HALFPX; }else{ x1 -= HALFPX; y1 -= HALFPX; x2 -= HALFPX; y2 -= HALFPX; } int32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1); int32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1); u1 = x1/width + u1Off; v1 = y1/height + v1Off; u2 = x2/width + u2Off; v2 = y2/height + v2Off; u1 = clamp(u1, 0.0f, 1.0f); v1 = clamp(v1, 0.0f, 1.0f); u2 = clamp(u2, 0.0f, 1.0f); v2 = clamp(v2, 0.0f, 1.0f); } float recipz = 1.0f/z; // TODO: CameraZ is wrong, what should we do? RwIm2DVertexSetScreenX(&verts[0], x1); RwIm2DVertexSetScreenY(&verts[0], y1); RwIm2DVertexSetScreenZ(&verts[0], z); RwIm2DVertexSetCameraZ(&verts[0], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&verts[0], recipz); RwIm2DVertexSetU(&verts[0], u1, recipz); RwIm2DVertexSetV(&verts[0], v1, recipz); RwIm2DVertexSetIntRGBA(&verts[0], color.red, color.green, color.blue, color.alpha); RwIm2DVertexSetScreenX(&verts[1], x1); RwIm2DVertexSetScreenY(&verts[1], y2); RwIm2DVertexSetScreenZ(&verts[1], z); RwIm2DVertexSetCameraZ(&verts[1], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&verts[1], recipz); RwIm2DVertexSetU(&verts[1], u1, recipz); RwIm2DVertexSetV(&verts[1], v2, recipz); RwIm2DVertexSetIntRGBA(&verts[1], color.red, color.green, color.blue, color.alpha); RwIm2DVertexSetScreenX(&verts[2], x2); RwIm2DVertexSetScreenY(&verts[2], y2); RwIm2DVertexSetScreenZ(&verts[2], z); RwIm2DVertexSetCameraZ(&verts[2], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&verts[2], recipz); RwIm2DVertexSetU(&verts[2], u2, recipz); RwIm2DVertexSetV(&verts[2], v2, recipz); RwIm2DVertexSetIntRGBA(&verts[2], color.red, color.green, color.blue, color.alpha); RwIm2DVertexSetScreenX(&verts[3], x2); RwIm2DVertexSetScreenY(&verts[3], y1); RwIm2DVertexSetScreenZ(&verts[3], z); RwIm2DVertexSetCameraZ(&verts[3], RwCameraGetNearClipPlane(cam)); RwIm2DVertexSetRecipCameraZ(&verts[3], recipz); RwIm2DVertexSetU(&verts[3], u2, recipz); RwIm2DVertexSetV(&verts[3], v1, recipz); RwIm2DVertexSetIntRGBA(&verts[3], color.red, color.green, color.blue, color.alpha); } void CMBlur::MotionBlurRender(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha) { #ifdef EXTENDED_COLOURFILTER CPostFX::Render(cam, red, green, blue, blur, type, bluralpha); #else PUSH_RENDERGROUP("CMBlur::MotionBlurRender"); RwRGBA color = { (RwUInt8)red, (RwUInt8)green, (RwUInt8)blue, (RwUInt8)blur }; #ifdef GTA_PS2 if( pFrontBuffer ) OverlayRender(cam, pFrontBuffer, color, type, bluralpha); #else if(ms_bJustInitialised) ms_bJustInitialised = false; else OverlayRender(cam, pFrontBuffer, color, type, bluralpha); if(BlurOn){ RwRasterPushContext(pFrontBuffer); RwRasterRenderFast(RwCameraGetRaster(cam), 0, 0); RwRasterPopContext(); } #endif POP_RENDERGROUP(); #endif } static uint8 DrunkBlurRed = 128; static uint8 DrunkBlurGreen = 128; static uint8 DrunkBlurBlue = 128; static int32 DrunkBlurIncrement = 1; void CMBlur::OverlayRender(RwCamera *cam, RwRaster *raster, RwRGBA color, int32 type, int32 bluralpha) { int r, g, b, a; r = color.red; g = color.green; b = color.blue; a = color.alpha; DefinedState(); switch(type) { case MOTION_BLUR_SECURITY_CAM: r = 0; g = 255; b = 0; a = 128; break; case MOTION_BLUR_INTRO: r = 100; g = 220; b = 230; a = 158; break; case MOTION_BLUR_INTRO2: r = 80; g = 255; b = 230; a = 138; break; case MOTION_BLUR_INTRO3: r = 255; g = 60; b = 60; a = 200; break; case MOTION_BLUR_INTRO4: r = 255; g = 180; b = 180; a = 128; break; } if(!BlurOn){ // gta clamps these to 255 (probably a macro or inlined function) int ovR = r * 0.6f; int ovG = g * 0.6f; int ovB = b * 0.6f; int ovA = type == MOTION_BLUR_SNIPER ? a : a*0.6f; RwIm2DVertexSetIntRGBA(&Vertex[0], ovR, ovG, ovB, ovA); RwIm2DVertexSetIntRGBA(&Vertex[1], ovR, ovG, ovB, ovA); RwIm2DVertexSetIntRGBA(&Vertex[2], ovR, ovG, ovB, ovA); RwIm2DVertexSetIntRGBA(&Vertex[3], ovR, ovG, ovB, ovA); }else{ RwIm2DVertexSetIntRGBA(&Vertex2[0], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex2[1], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex2[2], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex2[3], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a); } RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, raster); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); if(BlurOn){ if(type == MOTION_BLUR_SNIPER){ RwIm2DVertexSetIntRGBA(&Vertex2[0], r, g, b, 80); RwIm2DVertexSetIntRGBA(&Vertex2[1], r, g, b, 80); RwIm2DVertexSetIntRGBA(&Vertex2[2], r, g, b, 80); RwIm2DVertexSetIntRGBA(&Vertex2[3], r, g, b, 80); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); pBufVertCount = 0; }else{ RwIm2DVertexSetIntRGBA(&Vertex2[0], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex2[1], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex2[2], r*2, g*2, b*2, 30); RwIm2DVertexSetIntRGBA(&Vertex2[3], r*2, g*2, b*2, 30); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex2, 4, Index, 6); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwIm2DVertexSetIntRGBA(&Vertex2[0], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[0], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex2[1], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[1], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex2[2], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[2], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex2[3], r, g, b, a); RwIm2DVertexSetIntRGBA(&Vertex[3], r, g, b, a); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex2, 4, Index, 6); } } int DrunkBlurAlpha = 175.0f * Drunkness; if(DrunkBlurAlpha != 0){ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); if(BlurOn){ RwIm2DVertexSetIntRGBA(&Vertex[0], 255, 255, 255, DrunkBlurAlpha); RwIm2DVertexSetIntRGBA(&Vertex[1], 255, 255, 255, DrunkBlurAlpha); RwIm2DVertexSetIntRGBA(&Vertex[2], 255, 255, 255, DrunkBlurAlpha); RwIm2DVertexSetIntRGBA(&Vertex[3], 255, 255, 255, DrunkBlurAlpha); }else{ RwIm2DVertexSetIntRGBA(&Vertex[0], DrunkBlurRed, DrunkBlurGreen, DrunkBlurBlue, DrunkBlurAlpha); RwIm2DVertexSetIntRGBA(&Vertex[1], DrunkBlurRed, DrunkBlurGreen, DrunkBlurBlue, DrunkBlurAlpha); RwIm2DVertexSetIntRGBA(&Vertex[2], DrunkBlurRed, DrunkBlurGreen, DrunkBlurBlue, DrunkBlurAlpha); RwIm2DVertexSetIntRGBA(&Vertex[3], DrunkBlurRed, DrunkBlurGreen, DrunkBlurBlue, DrunkBlurAlpha); if(DrunkBlurIncrement){ if(DrunkBlurRed < 255) DrunkBlurRed++; if(DrunkBlurGreen < 255) DrunkBlurGreen++; if(DrunkBlurBlue < 255) DrunkBlurBlue++; if(DrunkBlurRed == 255) DrunkBlurIncrement = 0; }else{ if(DrunkBlurRed > 128) DrunkBlurRed--; if(DrunkBlurGreen > 128) DrunkBlurGreen--; if(DrunkBlurBlue > 128) DrunkBlurBlue--; if(DrunkBlurRed == 128) DrunkBlurIncrement = 1; } } RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, Vertex, 4, Index, 6); } if(type != MOTION_BLUR_SNIPER) OverlayRenderFx(cam, pFrontBuffer); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); } void CMBlur::SetDrunkBlur(float drunkness) { Drunkness = clamp(drunkness, 0.0f, 1.0f); } void CMBlur::ClearDrunkBlur() { Drunkness = 0.0f; CTimer::SetTimeScale(1.0f); } #define NUM_RENDER_FX 64 static RwRect fxRect[NUM_RENDER_FX]; static FxType fxType[NUM_RENDER_FX]; static float fxZ[NUM_RENDER_FX]; bool CMBlur::PosInside(RwRect *rect, float x1, float y1, float x2, float y2) { if((rect->x < x1 - 10.0f || rect->x > x2 + 10.0f || rect->y < y1 - 10.0f || rect->y > y2 + 10.0f) && (rect->w < x1 - 10.0f || rect->w > x2 + 10.0f || rect->h < y1 - 10.0f || rect->h > y2 + 10.0f) && (rect->x < x1 - 10.0f || rect->x > x2 + 10.0f || rect->h < y1 - 10.0f || rect->h > y2 + 10.0f) && (rect->w < x1 - 10.0f || rect->w > x2 + 10.0f || rect->y < y1 - 10.0f || rect->y > y2 + 10.0f)) return false; return true; } bool CMBlur::AddRenderFx(RwCamera *cam, RwRect *rect, float z, FxType type) { if(pBufVertCount >= NUM_RENDER_FX) return false; rect->x = Max(rect->x, 0); rect->y = Max(rect->y, 0); rect->w = Min(rect->w, SCREEN_WIDTH); rect->h = Min(rect->h, SCREEN_HEIGHT); if(rect->x >= rect->w || rect->y >= rect->h) return false; switch(type){ case FXTYPE_WATER1: case FXTYPE_WATER2: case FXTYPE_BLOOD1: case FXTYPE_BLOOD2: case FXTYPE_HEATHAZE: // code seems to be duplicated for this case for(int i = 0; i < pBufVertCount; i++) if(fxType[i] == type && PosInside(rect, fxRect[i].x-10.0f, fxRect[i].y-10.0f, fxRect[i].w+10.0f, fxRect[i].h+10.0f)) return false; // TODO: fix aspect ratio scaling // radar if(PosInside(rect, 40.0f, SCREEN_SCALE_FROM_BOTTOM(116.0f), 40.0f + SCREEN_SCALE_X(94.0f), SCREEN_SCALE_FROM_BOTTOM(116.0f - 76.0f))) return false; // HUD if(PosInside(rect, 400.0f, 0.0f, SCREEN_WIDTH, 90.0f)) return false; // vehicle name if(CHud::m_VehicleState != 0 && PosInside(rect, SCREEN_WIDTH/2, 350.0f, SCREEN_WIDTH, SCREEN_HEIGHT)) return false; // zone name if(CHud::m_ZoneState != 0 && PosInside(rect, SCREEN_WIDTH/2, 350.0f, SCREEN_WIDTH, SCREEN_HEIGHT)) return false; break; } fxRect[pBufVertCount] = *rect; fxZ[pBufVertCount] = z; fxType[pBufVertCount] = type; pBufVertCount++; return true; } void CMBlur::OverlayRenderFx(RwCamera *cam, RwRaster *frontBuf) { bool drawWaterDrops = false; RwIm2DVertex verts[4]; int red = (0.75f*CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed())*0.55f * 255; int green = (0.75f*CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen())*0.55f * 255; int blue = (0.75f*CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue())*0.55f * 255; red = clamp(red, 0, 255); green = clamp(green, 0, 255); blue = clamp(blue, 0, 255); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); #ifdef LIBRW rw::SetRenderState(rw::STENCILENABLE, TRUE); #else RwD3D8SetRenderState(D3DRS_STENCILENABLE, TRUE); #endif for(int i = 0; i < pBufVertCount; i++) switch(fxType[i]){ case FXTYPE_WATER1: case FXTYPE_WATER2: case FXTYPE_BLOOD1: case FXTYPE_BLOOD2: { drawWaterDrops = true; int32 width = Pow(2.0f, int32(log2(RwRasterGetWidth (RwCameraGetRaster(cam))))+1); int32 height = Pow(2.0f, int32(log2(RwRasterGetHeight(RwCameraGetRaster(cam))))+1); float u1Off = (fxRect[i].w - fxRect[i].x)/width; float u2Off = u1Off - (fxRect[i].w - fxRect[i].x + 0.5f)*0.66f/width; float halfHeight = (fxRect[i].h - fxRect[i].y + 0.5f)*0.25f/height; if(RwRasterGetDepth(RwCameraGetRaster(cam)) == 16){ if(fxType[i] == FXTYPE_BLOOD1 || fxType[i] == FXTYPE_BLOOD2) CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(255, 0, 0, 128), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); else CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(32, 32, 32, 225), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); }else{ CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(32, 32, 32, 225), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); } RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpDotRaster); #ifdef LIBRW rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILALWAYS); rw::SetRenderState(rw::STENCILFUNCTIONREF, 1); rw::SetRenderState(rw::STENCILFUNCTIONMASK, 0xFFFFFFFF); rw::SetRenderState(rw::STENCILFUNCTIONWRITEMASK, 0xFFFFFFFF); rw::SetRenderState(rw::STENCILZFAIL, rw::STENCILKEEP); rw::SetRenderState(rw::STENCILFAIL, rw::STENCILKEEP); rw::SetRenderState(rw::STENCILPASS, rw::STENCILREPLACE); #else RwD3D8SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); RwD3D8SetRenderState(D3DRS_STENCILREF, 1); RwD3D8SetRenderState(D3DRS_STENCILMASK, 0xFFFFFFFF); RwD3D8SetRenderState(D3DRS_STENCILWRITEMASK, 0xFFFFFFFF); RwD3D8SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); RwD3D8SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); RwD3D8SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); #endif RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); if(RwRasterGetDepth(RwCameraGetRaster(cam)) != 16){ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, frontBuf); #ifdef LIBRW rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILEQUAL); rw::SetRenderState(rw::STENCILPASS, rw::STENCILKEEP); #else RwD3D8SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL); RwD3D8SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); #endif if(BlurOn){ if(fxType[i] == FXTYPE_BLOOD1 || fxType[i] == FXTYPE_BLOOD2) CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(255, 0, 0, 255), u1Off, 0.0f+halfHeight, u2Off, 0.0f-halfHeight, fxZ[i], false); else CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(225, 225, 225, 160), u1Off, 0.0f+halfHeight, u2Off, 0.0f-halfHeight, fxZ[i], false); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDDESTALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVDESTALPHA); }else{ if(fxType[i] == FXTYPE_BLOOD1 || fxType[i] == FXTYPE_BLOOD2) CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(255, 0, 0, 128), u1Off, 0.0f+halfHeight, u2Off, 0.0f-halfHeight, fxZ[i], false); else CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(128, 128, 128, 32), u1Off, 0.0f+halfHeight, u2Off, 0.0f-halfHeight, fxZ[i], false); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); } RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); } break; } case FXTYPE_SPLASH1: case FXTYPE_SPLASH2: case FXTYPE_SPLASH3: drawWaterDrops = true; break; case FXTYPE_HEATHAZE: if(TheCamera.GetScreenFadeStatus() == FADE_0 && frontBuf){ int alpha = FrontEndMenuManager.m_PrefsBrightness > 255 ? FrontEndMenuManager.m_PrefsBrightness - 90 : FrontEndMenuManager.m_PrefsBrightness - 130; alpha = clamp(alpha, 16, 200)/2; CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(0, 0, 0, alpha), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpHeatHazeRaster); #ifdef LIBRW rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILALWAYS); rw::SetRenderState(rw::STENCILFUNCTIONREF, 1); rw::SetRenderState(rw::STENCILFUNCTIONMASK, 0xFFFFFFFF); rw::SetRenderState(rw::STENCILFUNCTIONWRITEMASK, 0xFFFFFFFF); rw::SetRenderState(rw::STENCILZFAIL, rw::STENCILKEEP); rw::SetRenderState(rw::STENCILFAIL, rw::STENCILKEEP); rw::SetRenderState(rw::STENCILPASS, rw::STENCILREPLACE); #else RwD3D8SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); RwD3D8SetRenderState(D3DRS_STENCILREF, 1); RwD3D8SetRenderState(D3DRS_STENCILMASK, 0xFFFFFFFF); RwD3D8SetRenderState(D3DRS_STENCILWRITEMASK, 0xFFFFFFFF); RwD3D8SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); RwD3D8SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); RwD3D8SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); #endif RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(255, 255, 255, alpha), CGeneral::GetRandomNumberInRange(-0.002f, 0.002f), CGeneral::GetRandomNumberInRange(-0.002f, 0.002f), CGeneral::GetRandomNumberInRange(-0.002f, 0.002f), CGeneral::GetRandomNumberInRange(-0.002f, 0.002f), fxZ[i], false); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, frontBuf); #ifdef LIBRW rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILEQUAL); rw::SetRenderState(rw::STENCILPASS, rw::STENCILKEEP); #else RwD3D8SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL); RwD3D8SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); #endif RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); } break; } #ifdef LIBRW rw::SetRenderState(rw::STENCILENABLE, FALSE); #else RwD3D8SetRenderState(D3DRS_STENCILENABLE, FALSE); #endif if(drawWaterDrops){ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); // Draw drops RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpRainDripRaster[0]); for(int i = 0; i < pBufVertCount; i++) if(fxType[i] == FXTYPE_WATER1 || fxType[i] == FXTYPE_BLOOD1){ CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(red, green, blue, fxType[i] == FXTYPE_BLOOD1 ? 255 : 192), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); } RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpRainDripRaster[1]); for(int i = 0; i < pBufVertCount; i++) if(fxType[i] == FXTYPE_WATER2 || fxType[i] == FXTYPE_BLOOD2){ CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(red, green, blue, fxType[i] == FXTYPE_BLOOD2 ? 255 : 192), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); } RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCarSplashRaster[0]); for(int i = 0; i < pBufVertCount; i++) if(fxType[i] == FXTYPE_SPLASH1 || fxType[i] == FXTYPE_SPLASH2 || fxType[i] == FXTYPE_SPLASH3){ CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(200, 200, 200, 255), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); } // Darken the water drops int alpha = 192*0.5f; RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpRainDripDarkRaster[0]); for(int i = 0; i < pBufVertCount; i++) if(fxType[i] == FXTYPE_WATER1){ CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(red, green, blue, alpha), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); } RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpRainDripDarkRaster[1]); for(int i = 0; i < pBufVertCount; i++) if(fxType[i] == FXTYPE_WATER2){ CreateImmediateModeData(cam, &fxRect[i], verts, CRGBA(red, green, blue, alpha), 0.0f, 0.0f, 0.0f, 0.0f, fxZ[i], true); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, verts, 4, Index, 6); } } RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); pBufVertCount = 0; } ================================================ FILE: src/render/MBlur.h ================================================ #pragma once enum FxType { FXTYPE_WATER1, FXTYPE_WATER2, FXTYPE_BLOOD1, FXTYPE_BLOOD2, FXTYPE_HEATHAZE, FXTYPE_SPLASH1, FXTYPE_SPLASH2, FXTYPE_SPLASH3 }; class CMBlur { public: static RwRaster *pFrontBuffer; static bool ms_bJustInitialised; static bool ms_bScaledBlur; static bool BlurOn; static float Drunkness; static int32 pBufVertCount; public: static RwBool MotionBlurOpen(RwCamera *cam); static RwBool MotionBlurClose(void); static void CreateImmediateModeData(RwCamera *cam, RwRect *rect); static void CreateImmediateModeData(RwCamera *cam, RwRect *rect, RwIm2DVertex *verts, RwRGBA color, float u1Off, float v1Off, float u2Off, float v2Off, float z, int fullTexture); static void MotionBlurRender(RwCamera *cam, uint32 red, uint32 green, uint32 blue, uint32 blur, int32 type, uint32 bluralpha); static void OverlayRender(RwCamera *cam, RwRaster *raster, RwRGBA color, int32 type, int32 bluralpha); static void SetDrunkBlur(float drunkness); static void ClearDrunkBlur(); static bool PosInside(RwRect *rect, float x1, float y1, float x2, float y2); static bool AddRenderFx(RwCamera *cam, RwRect *rect, float z, FxType type); static void OverlayRenderFx(RwCamera *cam, RwRaster *frontBuf); }; ================================================ FILE: src/render/Occlusion.cpp ================================================ #include "common.h" #include "main.h" #include "Entity.h" #include "Occlusion.h" #include "Game.h" #include "Camera.h" #include "Vector.h" #include "Draw.h" #include "Timer.h" #include "RwHelper.h" #include "VarConsole.h" int32 COcclusion::NumOccludersOnMap; int16 COcclusion::FarAwayList; int16 COcclusion::NearbyList; int16 COcclusion::ListWalkThroughFA; int16 COcclusion::PreviousListWalkThroughFA; int16 COcclusion::NumActiveOccluders; COccluder COcclusion::aOccluders[NUMOCCLUSIONVOLUMES]; CActiveOccluder COcclusion::aActiveOccluders[NUMACTIVEOCCLUDERS]; CVector gCenterOnScreen; float gMinYInOccluder; float gMinXInOccluder; float gMaxYInOccluder; float gMaxXInOccluder; bool gOccluderCoorsValid[8]; CVector gOccluderCoorsOnScreen[8]; CVector gOccluderCoors[8]; #ifndef MASTER bool bDispayOccDebugStuff; // disPAY, yeah #endif void COcclusion::Init(void) { NumOccludersOnMap = 0; #ifndef MASTER VarConsole.Add("Occlusion debug", &bDispayOccDebugStuff, true); #endif FarAwayList = -1; NearbyList = -1; ListWalkThroughFA = -1; PreviousListWalkThroughFA = -1; } void COcclusion::AddOne(float x, float y, float z, float width, float length, float height, float angle) { if(NumOccludersOnMap >= NUMOCCLUSIONVOLUMES) return; aOccluders[NumOccludersOnMap].x = x; aOccluders[NumOccludersOnMap].y = y; aOccluders[NumOccludersOnMap].z = z; aOccluders[NumOccludersOnMap].width = width; aOccluders[NumOccludersOnMap].length = length; aOccluders[NumOccludersOnMap].height = height; while(angle < 0.0f) angle += 360.0f; while(angle > 360.0f) angle -= 360.0f; aOccluders[NumOccludersOnMap].angle = angle/360.0f * UINT16_MAX; aOccluders[NumOccludersOnMap].listIndex = FarAwayList; FarAwayList = NumOccludersOnMap++; } bool COccluder::NearCamera() { return (TheCamera.GetPosition() - CVector(x, y, z)).Magnitude() - (Max(width, length) / 2.0f) < 250.0f; } bool DoesInfiniteLineCrossFiniteLine(float p1X, float p1Y, float p2X, float p2Y, float lineX, float lineY, float lineDX, float lineDY) { float side1 = (p1X - lineX) * lineDY - (p1Y - lineY) * lineDX; float side2 = (p2X - lineX) * lineDY - (p2Y - lineY) * lineDX; return side1 * side2 < 0.0f; // if points lie on opposite sides of the infinte line, the line between them crosses it } bool DoesInfiniteLineTouchScreen(float lineX, float lineY, float lineDX, float lineDY) { if (lineX > 0.0f && lineY > 0.0f && SCREEN_WIDTH > lineX && SCREEN_HEIGHT > lineY) return true; return (DoesInfiniteLineCrossFiniteLine(0.0f, 0.0f, SCREEN_WIDTH, 0.0f, lineX, lineY, lineDX, lineDY) || DoesInfiniteLineCrossFiniteLine(0.0f, 0.0f, 0.0f, SCREEN_HEIGHT, lineX, lineY, lineDX, lineDY) || DoesInfiniteLineCrossFiniteLine(SCREEN_WIDTH, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, lineX, lineY, lineDX, lineDY) || DoesInfiniteLineCrossFiniteLine(0.0f, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, lineX, lineY, lineDX, lineDY)); } bool IsPointInsideLine(float lineX, float lineY, float lineDX, float lineDY, float pX, float pY, float area = 0.0f) { return (pX - lineX) * lineDY - (pY - lineY) * lineDX >= area; } bool CalcScreenCoors(CVector const &in, CVector *out, float *outw, float *outh) { *out = TheCamera.m_viewMatrix * in; if (out->z <= 1.0f) return false; float recip = 1.0f / out->z; out->x *= SCREEN_WIDTH * recip; out->y *= SCREEN_HEIGHT * recip; float fovScale = DefaultFOV / CDraw::GetFOV(); *outw = fovScale * recip * SCREEN_WIDTH; *outh = fovScale * recip * SCREEN_HEIGHT; return true; } bool CalcScreenCoors(CVector const &in, CVector *out) { *out = TheCamera.m_viewMatrix * in; if (out->z <= 1.0f) return false; float recip = 1.0f / out->z; out->x *= SCREEN_WIDTH * recip; out->y *= SCREEN_HEIGHT * recip; return true; } bool COccluder::ProcessLineSegment(int corner1, int corner2, CActiveOccluder *occl) { if (!gOccluderCoorsValid[corner1] && !gOccluderCoorsValid[corner2]) return false; float x1, y1, x2, y2; CVector p1, p2; if (!gOccluderCoorsValid[corner1]) { float clipDist1 = Abs((TheCamera.m_viewMatrix * gOccluderCoors[corner1]).z - 1.1f); float clipDist2 = Abs((TheCamera.m_viewMatrix * gOccluderCoors[corner2]).z - 1.1f); float ratio = clipDist2 / (clipDist1 + clipDist2); CVector clippedCoors = (1.0f - ratio) * gOccluderCoors[corner2] + ratio * gOccluderCoors[corner1]; if (!CalcScreenCoors(clippedCoors, &p1, &x1, &y1)) return true; } else { p1 = gOccluderCoorsOnScreen[corner1]; } if (!gOccluderCoorsValid[corner2]) { float clipDist1 = Abs((TheCamera.m_viewMatrix * gOccluderCoors[corner1]).z - 1.1f); float clipDist2 = Abs((TheCamera.m_viewMatrix * gOccluderCoors[corner2]).z - 1.1f); float ratio = clipDist1 / (clipDist1 + clipDist2); CVector clippedCoors = (1.0f - ratio) * gOccluderCoors[corner2] + ratio * gOccluderCoors[corner1]; if (!CalcScreenCoors(clippedCoors, &p2, &x2, &y2)) return true; } else { p2 = gOccluderCoorsOnScreen[corner2]; } gMinXInOccluder = Min(Min(gMinXInOccluder, p1.x), p2.x); gMaxXInOccluder = Max(Max(gMaxXInOccluder, p1.x), p2.x); gMinYInOccluder = Min(Min(gMinYInOccluder, p1.y), p2.y); gMaxYInOccluder = Max(Max(gMaxYInOccluder, p1.y), p2.y); CVector2D origin = p1; CVector2D direction = p2 - p1; // Make sure lines are counter-clockwise around center if (!IsPointInsideLine(origin.x, origin.y, direction.x, direction.y, gCenterOnScreen.x, gCenterOnScreen.y, 0.0f)) { origin += direction; direction *= -1.0f; } float magnitude = direction.Magnitude(); occl->lines[occl->linesCount].origin = origin; occl->lines[occl->linesCount].direction = direction / magnitude; occl->lines[occl->linesCount].length = magnitude; if (!DoesInfiniteLineTouchScreen(origin.x, origin.y, direction.x, direction.y)) return !IsPointInsideLine(origin.x, origin.y, direction.x, direction.y, SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f, 0.0f); occl->linesCount++; return false; } bool COccluder::ProcessOneOccluder(CActiveOccluder *occl) { float outX, outY; occl->linesCount = 0; CVector pos(x, y, z); if (!CalcScreenCoors(pos, &gCenterOnScreen, &outX, &outY) || gCenterOnScreen.z < -150.0f || gCenterOnScreen.z > 300.0f) { return false; } occl->radius = Max(width, length) * 0.35f + gCenterOnScreen.z; CVector vec[3]; vec[0].x = length / 2.0f * Sin(GetAngle()); vec[0].y = -length / 2.0f * Cos(GetAngle()); vec[0].z = 0.0f; vec[1].x = width / 2.0f * Cos(GetAngle()); vec[1].y = width / 2.0f * Sin(GetAngle()); vec[1].z = 0.0f; vec[2].x = 0.0f; vec[2].y = 0.0f; vec[2].z = height / 2.0f; // Figure out if we see the front or back of a face bool bFrontFace[6]; for (int i = 0; i < 3; i++) { bFrontFace[i*2+0] = DotProduct((pos + vec[i] - TheCamera.GetPosition()), vec[i]) < 0.0f; bFrontFace[i*2+1] = DotProduct((pos - vec[i] - TheCamera.GetPosition()), -vec[i]) < 0.0f; } //calculating vertices of a box gOccluderCoors[0] = pos + vec[0] + vec[1] + vec[2]; gOccluderCoors[1] = pos - vec[0] + vec[1] + vec[2]; gOccluderCoors[2] = pos + vec[0] - vec[1] + vec[2]; gOccluderCoors[3] = pos - vec[0] - vec[1] + vec[2]; gOccluderCoors[4] = pos + vec[0] + vec[1] - vec[2]; gOccluderCoors[5] = pos - vec[0] + vec[1] - vec[2]; gOccluderCoors[6] = pos + vec[0] - vec[1] - vec[2]; gOccluderCoors[7] = pos - vec[0] - vec[1] - vec[2]; for(int i = 0; i < 8; i++) gOccluderCoorsValid[i] = CalcScreenCoors(gOccluderCoors[i], &gOccluderCoorsOnScreen[i], &outX, &outY); gMinYInOccluder = 999999.875f; gMinXInOccluder = 999999.875f; gMaxYInOccluder = -999999.875f; gMaxXInOccluder = -999999.875f; // Between two differently facing sides we see an edge, so process those if (bFrontFace[2] != bFrontFace[0] && ProcessLineSegment(0, 4, occl)) return false; if (bFrontFace[3] != bFrontFace[0] && ProcessLineSegment(2, 6, occl)) return false; if (bFrontFace[4] != bFrontFace[0] && ProcessLineSegment(0, 2, occl)) return false; if (bFrontFace[5] != bFrontFace[0] && ProcessLineSegment(4, 6, occl)) return false; if (bFrontFace[2] != bFrontFace[1] && ProcessLineSegment(1, 5, occl)) return false; if (bFrontFace[3] != bFrontFace[1] && ProcessLineSegment(3, 7, occl)) return false; if (bFrontFace[4] != bFrontFace[1] && ProcessLineSegment(1, 3, occl)) return false; if (bFrontFace[5] != bFrontFace[1] && ProcessLineSegment(5, 7, occl)) return false; if (bFrontFace[4] != bFrontFace[2] && ProcessLineSegment(0, 1, occl)) return false; if (bFrontFace[3] != bFrontFace[4] && ProcessLineSegment(2, 3, occl)) return false; if (bFrontFace[5] != bFrontFace[3] && ProcessLineSegment(6, 7, occl)) return false; if (bFrontFace[2] != bFrontFace[5] && ProcessLineSegment(4, 5, occl)) return false; if (gMaxXInOccluder - gMinXInOccluder < SCREEN_WIDTH * 0.1f || gMaxYInOccluder - gMinYInOccluder < SCREEN_HEIGHT * 0.07f) return false; return true; } bool COcclusion::OccluderHidesBehind(CActiveOccluder *occl1, CActiveOccluder *occl2) { for (int i = 0; i < occl1->linesCount; i++) { for (int j = 0; j < occl2->linesCount; j++) { if (!IsPointInsideLine(occl2->lines[j].origin.x, occl2->lines[j].origin.y, occl2->lines[j].direction.x, occl2->lines[j].direction.y, occl1->lines[i].origin.x, occl1->lines[i].origin.y, 0.0f)) return false; if (!IsPointInsideLine(occl2->lines[j].origin.x, occl2->lines[j].origin.y, occl2->lines[j].direction.x, occl2->lines[j].direction.y, (occl1->lines[i].origin.x + occl1->lines[i].direction.x * occl1->lines[i].length), (occl1->lines[i].origin.y + occl1->lines[i].direction.y * occl1->lines[i].length), 0.0f)) return false; } } return true; } void COcclusion::ProcessBeforeRendering(void) { NumActiveOccluders = 0; #ifndef MASTER if (gbModelViewer) return; #endif if (CGame::currArea != AREA_MAIN_MAP) return; if (ListWalkThroughFA == -1) { PreviousListWalkThroughFA = -1; ListWalkThroughFA = FarAwayList; } int i; for (i = 0; i < 16 && ListWalkThroughFA != -1; i++) { if (aOccluders[ListWalkThroughFA].NearCamera()) { int prevListWalkThroughFA = ListWalkThroughFA; if (PreviousListWalkThroughFA == -1) { FarAwayList = aOccluders[ListWalkThroughFA].listIndex; } else { aOccluders[PreviousListWalkThroughFA].listIndex = aOccluders[ListWalkThroughFA].listIndex; } int prevNearbyList = NearbyList; ListWalkThroughFA = aOccluders[ListWalkThroughFA].listIndex; NearbyList = prevListWalkThroughFA; aOccluders[prevListWalkThroughFA].listIndex = prevNearbyList; } else { PreviousListWalkThroughFA = ListWalkThroughFA; ListWalkThroughFA = aOccluders[ListWalkThroughFA].listIndex; } } int prevNearbyList = -1; int tmpNearbyList = NearbyList; int indexTmpNearbyList, storeTmpNearbyList, prevFarAwayList; while (tmpNearbyList != -1) { if (NumActiveOccluders < NUMACTIVEOCCLUDERS && aOccluders[tmpNearbyList].ProcessOneOccluder(&aActiveOccluders[NumActiveOccluders])) ++NumActiveOccluders; indexTmpNearbyList = tmpNearbyList; if (aOccluders[indexTmpNearbyList].NearCamera()) { prevNearbyList = tmpNearbyList; tmpNearbyList = aOccluders[indexTmpNearbyList].listIndex; } else { storeTmpNearbyList = tmpNearbyList; if (prevNearbyList == -1) { NearbyList = aOccluders[indexTmpNearbyList].listIndex; } else { aOccluders[prevNearbyList].listIndex = aOccluders[indexTmpNearbyList].listIndex; } tmpNearbyList = aOccluders[indexTmpNearbyList].listIndex; prevFarAwayList = FarAwayList; FarAwayList = storeTmpNearbyList; aOccluders[storeTmpNearbyList].listIndex = prevFarAwayList; } } for (i = 0; i < NumActiveOccluders; i++) { for (int j = 0; j < NumActiveOccluders; j++) { if (i != j && aActiveOccluders[j].radius < aActiveOccluders[i].radius) { if (OccluderHidesBehind(&aActiveOccluders[i], &aActiveOccluders[j])) { for (int k = i; k < NumActiveOccluders - 1; k++) { for (int l = 0; l < aActiveOccluders[k + 1].linesCount; l++) aActiveOccluders[k].lines[l] = aActiveOccluders[k + 1].lines[l]; aActiveOccluders[k].linesCount = aActiveOccluders[k + 1].linesCount; aActiveOccluders[k].radius = aActiveOccluders[k + 1].radius; } NumActiveOccluders--; i--; // Taken from Mobile! #ifdef FIX_BUGS if (i == -1) { i = 0; } #endif } } } } } bool CActiveOccluder::IsPointWithinOcclusionArea(float pX, float pY, float area) { for (int i = 0; i < linesCount; i++) { if (!IsPointInsideLine(lines[i].origin.x, lines[i].origin.y, lines[i].direction.x, lines[i].direction.y, pX, pY, area)) return false; } return true; } bool COcclusion::IsAABoxOccluded(CVector pos, float width, float length, float height) { CVector coors; float outW, outH; if (!NumActiveOccluders || !CalcScreenCoors(pos, &coors, &outW, &outH)) return false; float side = CVector(width, length, height).Magnitude() / 4.0f; float area = Max(outW, outH) * side; CVector minCorner, maxCorner; minCorner.x = pos.x - width / 2.0f; minCorner.y = pos.y - length / 2.0f; minCorner.z = pos.z - height / 2.0f; maxCorner.x = pos.x + width / 2.0f; maxCorner.y = pos.y + length / 2.0f; maxCorner.z = pos.z + height / 2.0f; for (int i = 0; i < NumActiveOccluders; i++) { if (coors.z - (side * 0.85f) > aActiveOccluders[i].radius) { if (aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, area)) return true; if (aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) { if (CalcScreenCoors(minCorner, &coors) && !aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; if (CalcScreenCoors(CVector(maxCorner.x, maxCorner.y, minCorner.z), &coors) && !aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; if (CalcScreenCoors(CVector(maxCorner.x, minCorner.y, maxCorner.z), &coors) && !aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; if (CalcScreenCoors(CVector(minCorner.x, maxCorner.y, maxCorner.z), &coors, &outW, &outH) && !aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; return true; } } } return false; } bool COcclusion::IsPositionOccluded(CVector pos, float side) { CVector coors; float width, height; if (!NumActiveOccluders || !CalcScreenCoors(pos, &coors, &width, &height)) return false; float area = Max(width, height) * side; for (int i = 0; i < NumActiveOccluders; i++) { if (coors.z - (side * 0.85f) > aActiveOccluders[i].radius) if (aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, area)) return true; } return false; } #ifndef MASTER #include "Lines.h" RwIm2DVertex vertexbufferT[2]; void COcclusion::Render() { if (!bDispayOccDebugStuff || !(CTimer::GetTimeInMilliseconds() & 0x200)) return; RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, FALSE); float recipz = 1.0f/RwCameraGetNearClipPlane(Scene.camera); for (int i = 0; i < NumActiveOccluders; i++) { for (int j = 0; j < aActiveOccluders[i].linesCount; j++) { RwIm2DVertexSetScreenX(&vertexbufferT[0], aActiveOccluders[i].lines[j].origin.x); RwIm2DVertexSetScreenY(&vertexbufferT[0], aActiveOccluders[i].lines[j].origin.y); RwIm2DVertexSetScreenZ(&vertexbufferT[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&vertexbufferT[0], RwCameraGetNearClipPlane(Scene.camera)); RwIm2DVertexSetRecipCameraZ(&vertexbufferT[0], recipz); RwIm2DVertexSetScreenX(&vertexbufferT[1], aActiveOccluders[i].lines[j].origin.x + aActiveOccluders[i].lines[j].direction.x * aActiveOccluders[i].lines[j].length); RwIm2DVertexSetScreenY(&vertexbufferT[1], aActiveOccluders[i].lines[j].origin.y + aActiveOccluders[i].lines[j].direction.y * aActiveOccluders[i].lines[j].length); RwIm2DVertexSetScreenZ(&vertexbufferT[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetCameraZ(&vertexbufferT[1], RwCameraGetNearClipPlane(Scene.camera)); RwIm2DVertexSetRecipCameraZ(&vertexbufferT[1], recipz); RwIm2DVertexSetIntRGBA(&vertexbufferT[0], 255, 255, 0, 255); RwIm2DVertexSetIntRGBA(&vertexbufferT[1], 255, 255, 0, 255); RwIm2DRenderLine(vertexbufferT, 2, 0, 1); } } DefinedState(); } #endif bool CEntity::IsEntityOccluded(void) { CVector coors; float width, height; if (COcclusion::NumActiveOccluders == 0 || !CalcScreenCoors(GetBoundCentre(), &coors, &width, &height)) return false; float area = Max(width, height) * GetBoundRadius() * 0.9f; for (int i = 0; i < COcclusion::NumActiveOccluders; i++) { if (coors.z - (GetBoundRadius() * 0.85f) > COcclusion::aActiveOccluders[i].radius) { if (COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, area)) { return true; } if (COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) { CVector min = m_matrix * CModelInfo::GetModelInfo(GetModelIndex())->GetColModel()->boundingBox.min; CVector max = m_matrix * CModelInfo::GetModelInfo(GetModelIndex())->GetColModel()->boundingBox.max; if (CalcScreenCoors(min, &coors) && !COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; if (CalcScreenCoors(CVector(max.x, max.y, min.z), &coors) && !COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; if (CalcScreenCoors(CVector(max.x, min.y, max.z), &coors) && !COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; if (CalcScreenCoors(CVector(min.x, max.y, max.z), &coors) && !COcclusion::aActiveOccluders[i].IsPointWithinOcclusionArea(coors.x, coors.y, 0.0f)) continue; return true; } } } return false; } ================================================ FILE: src/render/Occlusion.h ================================================ #pragma once struct ActiveOccluderLine { CVector2D origin; CVector2D direction; float length; }; class CActiveOccluder { public: ActiveOccluderLine lines[6]; int32 linesCount; float radius; bool IsPointWithinOcclusionArea(float x, float y, float area); }; class COccluder { public: int16 length, width, height; int16 x, y, z; uint16 angle; int16 listIndex; bool NearCamera(); bool ProcessOneOccluder(CActiveOccluder *occl); bool ProcessLineSegment(int corner1, int corner2, CActiveOccluder* occl); float GetAngle(void) { return angle*TWOPI/UINT16_MAX; } }; class COcclusion { public: static int32 NumOccludersOnMap; static int16 FarAwayList; static int16 NearbyList; static int16 ListWalkThroughFA; static int16 PreviousListWalkThroughFA; static int16 NumActiveOccluders; static COccluder aOccluders[NUMOCCLUSIONVOLUMES]; static CActiveOccluder aActiveOccluders[NUMACTIVEOCCLUDERS]; static void Init(void); static void AddOne(float x, float y, float z, float width, float length, float height, float angle); static void ProcessBeforeRendering(void); static bool OccluderHidesBehind(CActiveOccluder *occl1, CActiveOccluder *occl2); static bool IsAABoxOccluded(CVector pos, float width, float length, float height); static bool IsPositionOccluded(CVector pos, float side); #ifndef MASTER static void Render(); #endif }; bool CalcScreenCoors(CVector const &in, CVector *out, float *outw, float *outh); bool CalcScreenCoors(CVector const &in, CVector *out); #ifndef MASTER extern bool bDispayOccDebugStuff; #endif ================================================ FILE: src/render/Particle.cpp ================================================ #include "common.h" #include "main.h" #include "General.h" #include "Timer.h" #include "TxdStore.h" #include "Sprite.h" #include "Camera.h" #include "Clock.h" #include "Collision.h" #include "World.h" #include "Shadows.h" #include "Replay.h" #include "Stats.h" #include "Weather.h" #include "MBlur.h" #include "main.h" #include "AudioScriptObject.h" #include "ParticleObject.h" #include "Particle.h" #include "soundlist.h" #define MAX_PARTICLES_ON_SCREEN (750) //(5) #define MAX_SMOKE_FILES ARRAY_SIZE(SmokeFiles) //(5) #define MAX_RUBBER_FILES ARRAY_SIZE(RubberFiles) //(5) #define MAX_RAINSPLASH_FILES ARRAY_SIZE(RainSplashFiles) //(3) #define MAX_WATERSPRAY_FILES ARRAY_SIZE(WatersprayFiles) //(6) #define MAX_EXPLOSIONMEDIUM_FILES ARRAY_SIZE(ExplosionMediumFiles) //(4) #define MAX_GUNFLASH_FILES ARRAY_SIZE(GunFlashFiles) //(2) #define MAX_RAINSPLASHUP_FILES ARRAY_SIZE(RainSplashupFiles) //(4) #define MAX_BIRDFRONT_FILES ARRAY_SIZE(BirdfrontFiles) //(8) #define MAX_BOAT_FILES ARRAY_SIZE(BoatFiles) //(4) #define MAX_CARDEBRIS_FILES ARRAY_SIZE(CardebrisFiles) //(4) #define MAX_CARSPLASH_FILES ARRAY_SIZE(CarsplashFiles) #define MAX_RAINDRIP_FILES (2) #define MAX_LEAF_FILES (2) const char SmokeFiles[][6+1] = { "smoke1", "smoke2", "smoke3", "smoke4", "smoke5" }; const char RubberFiles[][7+1] = { "rubber1", "rubber2", "rubber3", "rubber4", "rubber5" }; const char RainSplashFiles[][7+1] = { "splash1", "splash2", "splash3", "splash4", "splash5" }; const char WatersprayFiles[][11+1] = { "waterspray1", "waterspray2", "waterspray3" }; const char ExplosionMediumFiles[][7+1] = { "explo01", "explo02", "explo03", "explo04", "explo05", "explo06" }; const char GunFlashFiles[][9+1] = { "gunflash1", "gunflash2", "gunflash3", "gunflash4" }; const char RainSplashupFiles[][10+1] = { "splash_up1", "splash_up2" }; const char BirdfrontFiles[][8+1] = { "birdf_01", "birdf_02", "birdf_03", "birdf_04" }; const char BoatFiles[][8+1] = { "boats_01", "boats_02", "boats_03", "boats_04", "boats_05", "boats_06", "boats_07", "boats_08" }; const char CardebrisFiles[][12+1] = { "cardebris_01", "cardebris_02", "cardebris_03", "cardebris_04" }; const char CarsplashFiles[][12+1] = { "carsplash_01", "carsplash_02", "carsplash_03", "carsplash_04" }; CParticle gParticleArray[MAX_PARTICLES_ON_SCREEN]; RwTexture *gpSmokeTex[MAX_SMOKE_FILES]; RwTexture *gpSmoke2Tex; RwTexture *gpRubberTex[MAX_RUBBER_FILES]; RwTexture *gpRainSplashTex[MAX_RAINSPLASH_FILES]; RwTexture *gpWatersprayTex[MAX_WATERSPRAY_FILES]; RwTexture *gpExplosionMediumTex[MAX_EXPLOSIONMEDIUM_FILES]; RwTexture *gpGunFlashTex[MAX_GUNFLASH_FILES]; RwTexture *gpRainSplashupTex[MAX_RAINSPLASHUP_FILES]; RwTexture *gpBirdfrontTex[MAX_BIRDFRONT_FILES]; RwTexture *gpBoatTex[MAX_BOAT_FILES]; RwTexture *gpCarDebrisTex[MAX_CARDEBRIS_FILES]; RwTexture *gpCarSplashTex[MAX_CARSPLASH_FILES]; RwTexture *gpBoatWakeTex; RwTexture *gpFlame1Tex; RwTexture *gpFlame5Tex; RwTexture *gpRainDropSmallTex; RwTexture *gpBloodTex; RwTexture *gpLeafTex[MAX_LEAF_FILES]; RwTexture *gpCloudTex1; RwTexture *gpCloudTex4; RwTexture *gpBloodSmallTex; RwTexture *gpGungeTex; RwTexture *gpCollisionSmokeTex; RwTexture *gpBulletHitTex; RwTexture *gpGunShellTex; RwTexture *gpPointlightTex; RwRaster *gpSmokeRaster[MAX_SMOKE_FILES]; RwRaster *gpSmoke2Raster; RwRaster *gpRubberRaster[MAX_RUBBER_FILES]; RwRaster *gpRainSplashRaster[MAX_RAINSPLASH_FILES]; RwRaster *gpWatersprayRaster[MAX_WATERSPRAY_FILES]; RwRaster *gpExplosionMediumRaster[MAX_EXPLOSIONMEDIUM_FILES]; RwRaster *gpGunFlashRaster[MAX_GUNFLASH_FILES]; RwRaster *gpRainSplashupRaster[MAX_RAINSPLASHUP_FILES]; RwRaster *gpBirdfrontRaster[MAX_BIRDFRONT_FILES]; RwRaster *gpBoatRaster[MAX_BOAT_FILES]; RwRaster *gpCarDebrisRaster[MAX_CARDEBRIS_FILES]; RwRaster *gpCarSplashRaster[MAX_CARSPLASH_FILES]; RwRaster *gpBoatWakeRaster; RwRaster *gpFlame1Raster; RwRaster *gpFlame5Raster; RwRaster *gpRainDropSmallRaster; RwRaster *gpBloodRaster; RwRaster *gpLeafRaster[MAX_LEAF_FILES]; RwRaster *gpCloudRaster1; RwRaster *gpCloudRaster4; RwRaster *gpBloodSmallRaster; RwRaster *gpGungeRaster; RwRaster *gpCollisionSmokeRaster; RwRaster *gpBulletHitRaster; RwRaster *gpGunShellRaster; RwRaster *gpPointlightRaster; RwTexture *gpRainDropTex; RwRaster *gpRainDropRaster; RwTexture *gpSparkTex; RwTexture *gpNewspaperTex; RwTexture *gpGunSmokeTex; RwTexture *gpDotTex; RwTexture *gpHeatHazeTex; RwTexture *gpBeastieTex; RwTexture *gpRainDripTex[MAX_RAINDRIP_FILES]; RwTexture *gpRainDripDarkTex[MAX_RAINDRIP_FILES]; RwRaster *gpSparkRaster; RwRaster *gpNewspaperRaster; RwRaster *gpGunSmokeRaster; RwRaster *gpDotRaster; RwRaster *gpHeatHazeRaster; RwRaster *gpBeastieRaster; RwRaster *gpRainDripRaster[MAX_RAINDRIP_FILES]; RwRaster *gpRainDripDarkRaster[MAX_RAINDRIP_FILES]; float CParticle::ms_afRandTable[CParticle::RAND_TABLE_SIZE]; CParticle *CParticle::m_pUnusedListHead; float CParticle::m_SinTable[CParticle::SIN_COS_TABLE_SIZE]; float CParticle::m_CosTable[CParticle::SIN_COS_TABLE_SIZE]; int32 Randomizer; int32 nParticleCreationInterval = 1; float PARTICLE_WIND_TEST_SCALE = 0.002f; float fParticleScaleLimit = 0.5f; bool clearWaterDrop; int32 numWaterDropOnScreen; #ifdef DEBUGMENU SETTWEAKPATH("Particle"); TWEAKINT32(nParticleCreationInterval, 0, 5, 1); TWEAKFLOAT(fParticleScaleLimit, 0.0f, 1.0f, 0.1f); TWEAKFUNC(CParticle::ReloadConfig); #endif void CParticle::ReloadConfig() { debug("Initialising CParticleMgr..."); mod_ParticleSystemManager.Initialise(); debug("Initialising CParticle..."); m_pUnusedListHead = gParticleArray; for ( int32 i = 0; i < MAX_PARTICLES_ON_SCREEN; i++ ) { if ( i == MAX_PARTICLES_ON_SCREEN - 1 ) gParticleArray[i].m_pNext = nil; else gParticleArray[i].m_pNext = &gParticleArray[i + 1]; gParticleArray[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f); gParticleArray[i].m_vecVelocity = CVector(0.0f, 0.0f, 0.0f); gParticleArray[i].m_nTimeWhenWillBeDestroyed = 0; gParticleArray[i].m_nTimeWhenColorWillBeChanged = 0; gParticleArray[i].m_fSize = 0.2f; gParticleArray[i].m_fExpansionRate = 0.0f; gParticleArray[i].m_nColorIntensity = 255; gParticleArray[i].m_nFadeToBlackTimer = 0; gParticleArray[i].m_nAlpha = 255; gParticleArray[i].m_nFadeAlphaTimer = 0; gParticleArray[i].m_nCurrentZRotation = 0; gParticleArray[i].m_nZRotationTimer = 0; gParticleArray[i].m_fCurrentZRadius = 0.0f; gParticleArray[i].m_nZRadiusTimer = 0; gParticleArray[i].m_nCurrentFrame = 0; gParticleArray[i].m_nAnimationSpeedTimer = 0; gParticleArray[i].m_nRotation = 0; gParticleArray[i].m_nRotationStep = 0; } } void CParticle::Initialise() { ReloadConfig(); CParticleObject::Initialise(); float randVal = -1.0f; for ( int32 i = 0; i < RAND_TABLE_SIZE; i++ ) { ms_afRandTable[i] = randVal; randVal += 0.1f; } for ( int32 i = 0; i < SIN_COS_TABLE_SIZE; i++ ) { float angle = DEGTORAD(float(i) * float(360.0f / SIN_COS_TABLE_SIZE)); m_SinTable[i] = ::Sin(angle); m_CosTable[i] = ::Cos(angle); } int32 slot = CTxdStore::FindTxdSlot("particle"); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(slot); for ( int32 i = 0; i < MAX_SMOKE_FILES; i++ ) { gpSmokeTex[i] = RwTextureRead(SmokeFiles[i], nil); gpSmokeRaster[i] = RwTextureGetRaster(gpSmokeTex[i]); } gpSmoke2Tex = RwTextureRead("smokeII_3", nil); gpSmoke2Raster = RwTextureGetRaster(gpSmoke2Tex); for ( int32 i = 0; i < MAX_RUBBER_FILES; i++ ) { gpRubberTex[i] = RwTextureRead(RubberFiles[i], nil); gpRubberRaster[i] = RwTextureGetRaster(gpRubberTex[i]); } for ( int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ ) { gpRainSplashTex[i] = RwTextureRead(RainSplashFiles[i], nil); gpRainSplashRaster[i] = RwTextureGetRaster(gpRainSplashTex[i]); } for ( int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ ) { gpWatersprayTex[i] = RwTextureRead(WatersprayFiles[i], nil); gpWatersprayRaster[i] = RwTextureGetRaster(gpWatersprayTex[i]); } for ( int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ ) { gpExplosionMediumTex[i] = RwTextureRead(ExplosionMediumFiles[i], nil); gpExplosionMediumRaster[i] = RwTextureGetRaster(gpExplosionMediumTex[i]); } for ( int32 i = 0; i < MAX_GUNFLASH_FILES; i++ ) { gpGunFlashTex[i] = RwTextureRead(GunFlashFiles[i], nil); gpGunFlashRaster[i] = RwTextureGetRaster(gpGunFlashTex[i]); } gpRainDropTex = RwTextureRead("raindrop4", nil); gpRainDropRaster = RwTextureGetRaster(gpRainDropTex); for ( int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ ) { gpRainSplashupTex[i] = RwTextureRead(RainSplashupFiles[i], nil); gpRainSplashupRaster[i] = RwTextureGetRaster(gpRainSplashupTex[i]); } for ( int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ ) { gpBirdfrontTex[i] = RwTextureRead(BirdfrontFiles[i], nil); gpBirdfrontRaster[i] = RwTextureGetRaster(gpBirdfrontTex[i]); } for ( int32 i = 0; i < MAX_BOAT_FILES; i++ ) { gpBoatTex[i] = RwTextureRead(BoatFiles[i], nil); gpBoatRaster[i] = RwTextureGetRaster(gpBoatTex[i]); } for ( int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ ) { gpCarDebrisTex[i] = RwTextureRead(CardebrisFiles[i], nil); gpCarDebrisRaster[i] = RwTextureGetRaster(gpCarDebrisTex[i]); } for ( int32 i = 0; i < MAX_CARSPLASH_FILES; i++ ) { gpCarSplashTex[i] = RwTextureRead(CarsplashFiles[i], nil); gpCarSplashRaster[i] = RwTextureGetRaster(gpCarSplashTex[i]); } gpBoatWakeTex = RwTextureRead("boatwake2", nil); gpBoatWakeRaster = RwTextureGetRaster(gpBoatWakeTex); gpFlame1Tex = RwTextureRead("flame1", nil); gpFlame1Raster = RwTextureGetRaster(gpFlame1Tex); gpFlame5Tex = RwTextureRead("flame5", nil); //#ifdef FIX_BUGS #if 0 gpFlame5Raster = RwTextureGetRaster(gpFlame5Tex); #else // this seems to have become more of a design choice gpFlame5Raster = RwTextureGetRaster(gpFlame1Tex); // copy-paste bug ? #endif gpRainDropSmallTex = RwTextureRead("rainsmall", nil); gpRainDropSmallRaster = RwTextureGetRaster(gpRainDropSmallTex); gpBloodTex = RwTextureRead("blood", nil); gpBloodRaster = RwTextureGetRaster(gpBloodTex); gpLeafTex[0] = RwTextureRead("gameleaf01_64", nil); gpLeafRaster[0] = RwTextureGetRaster(gpLeafTex[0]); gpLeafTex[1] = RwTextureRead("letter", nil); gpLeafRaster[1] = RwTextureGetRaster(gpLeafTex[1]); gpCloudTex1 = RwTextureRead("cloud3", nil); gpCloudRaster1 = RwTextureGetRaster(gpCloudTex1); gpCloudTex4 = RwTextureRead("cloudmasked", nil); gpCloudRaster4 = RwTextureGetRaster(gpCloudTex4); gpBloodSmallTex = RwTextureRead("bloodsplat2", nil); gpBloodSmallRaster = RwTextureGetRaster(gpBloodSmallTex); gpGungeTex = RwTextureRead("gunge", nil); gpGungeRaster = RwTextureGetRaster(gpGungeTex); gpCollisionSmokeTex = RwTextureRead("collisionsmoke", nil); gpCollisionSmokeRaster = RwTextureGetRaster(gpCollisionSmokeTex); gpBulletHitTex = RwTextureRead("bullethitsmoke", nil); gpBulletHitRaster = RwTextureGetRaster(gpBulletHitTex); gpGunShellTex = RwTextureRead("gunshell", nil); gpGunShellRaster = RwTextureGetRaster(gpGunShellTex); gpPointlightTex = RwTextureRead("pointlight", nil); gpPointlightRaster = RwTextureGetRaster(gpPointlightTex); gpSparkTex = RwTextureRead("spark", nil); gpSparkRaster = RwTextureGetRaster(gpSparkTex); gpNewspaperTex = RwTextureRead("newspaper02_64", nil); gpNewspaperRaster = RwTextureGetRaster(gpNewspaperTex); gpGunSmokeTex = RwTextureRead("gunsmoke3", nil); gpGunSmokeRaster = RwTextureGetRaster(gpGunSmokeTex); gpDotTex = RwTextureRead("dot", nil); gpDotRaster = RwTextureGetRaster(gpDotTex); gpHeatHazeTex = RwTextureRead("heathaze", nil); gpHeatHazeRaster = RwTextureGetRaster(gpHeatHazeTex); gpBeastieTex = RwTextureRead("beastie", nil); gpBeastieRaster = RwTextureGetRaster(gpBeastieTex); gpRainDripTex[0] = RwTextureRead("raindrip64", nil); gpRainDripRaster[0] = RwTextureGetRaster(gpRainDripTex[0]); gpRainDripTex[1] = RwTextureRead("raindripb64", nil); gpRainDripRaster[1] = RwTextureGetRaster(gpRainDripTex[1]); gpRainDripDarkTex[0] = RwTextureRead("raindrip64_d", nil); gpRainDripDarkRaster[0] = RwTextureGetRaster(gpRainDripDarkTex[0]); gpRainDripDarkTex[1] = RwTextureRead("raindripb64_d", nil); gpRainDripDarkRaster[1] = RwTextureGetRaster(gpRainDripDarkTex[1]); CTxdStore::PopCurrentTxd(); for ( int32 i = 0; i < MAX_PARTICLES; i++ ) { tParticleSystemData *entry = &mod_ParticleSystemManager.m_aParticles[i]; switch ( i ) { case PARTICLE_SPARK: case PARTICLE_SPARK_SMALL: case PARTICLE_RAINDROP_SMALL: case PARTICLE_HELI_ATTACK: entry->m_ppRaster = &gpRainDropSmallRaster; break; case PARTICLE_WATER_SPARK: entry->m_ppRaster = &gpSparkRaster; break; case PARTICLE_WHEEL_DIRT: case PARTICLE_SAND: case PARTICLE_STEAM2: case PARTICLE_STEAM_NY: case PARTICLE_STEAM_NY_SLOWMOTION: case PARTICLE_GROUND_STEAM: case PARTICLE_ENGINE_STEAM: case PARTICLE_PEDFOOT_DUST: case PARTICLE_CAR_DUST: case PARTICLE_EXHAUST_FUMES: entry->m_ppRaster = &gpSmoke2Raster; break; case PARTICLE_WHEEL_WATER: case PARTICLE_WATER: case PARTICLE_SMOKE: case PARTICLE_SMOKE_SLOWMOTION: case PARTICLE_DRY_ICE: case PARTICLE_GARAGEPAINT_SPRAY: case PARTICLE_STEAM: case PARTICLE_WATER_CANNON: case PARTICLE_EXTINGUISH_STEAM: case PARTICLE_HELI_DUST: case PARTICLE_PAINT_SMOKE: case PARTICLE_BULLETHIT_SMOKE: entry->m_ppRaster = gpSmokeRaster; break; case PARTICLE_BLOOD: entry->m_ppRaster = &gpBloodRaster; break; case PARTICLE_BLOOD_SMALL: case PARTICLE_BLOOD_SPURT: entry->m_ppRaster = &gpBloodSmallRaster; break; case PARTICLE_DEBRIS: case PARTICLE_TREE_LEAVES: entry->m_ppRaster = gpLeafRaster; break; case PARTICLE_DEBRIS2: entry->m_ppRaster = &gpGungeRaster; break; case PARTICLE_FLYERS: entry->m_ppRaster = &gpNewspaperRaster; break; case PARTICLE_FLAME: case PARTICLE_CARFLAME: entry->m_ppRaster = &gpFlame1Raster; break; case PARTICLE_FIREBALL: entry->m_ppRaster = &gpFlame5Raster; break; case PARTICLE_GUNFLASH: case PARTICLE_GUNFLASH_NOANIM: entry->m_ppRaster = gpGunFlashRaster; break; case PARTICLE_GUNSMOKE: case PARTICLE_WATERDROP: case PARTICLE_BLOODDROP: case PARTICLE_HEATHAZE: case PARTICLE_HEATHAZE_IN_DIST: entry->m_ppRaster = nil; break; case PARTICLE_GUNSMOKE2: case PARTICLE_BOAT_THRUSTJET: case PARTICLE_RUBBER_SMOKE: entry->m_ppRaster = gpRubberRaster; break; case PARTICLE_CIGARETTE_SMOKE: entry->m_ppRaster = &gpGunSmokeRaster; break; case PARTICLE_TEARGAS: entry->m_ppRaster = &gpHeatHazeRaster; break; case PARTICLE_SHARD: case PARTICLE_RAINDROP: case PARTICLE_RAINDROP_2D: entry->m_ppRaster = &gpRainDropRaster; break; case PARTICLE_SPLASH: case PARTICLE_PED_SPLASH: case PARTICLE_CAR_SPLASH: case PARTICLE_WATER_HYDRANT: entry->m_ppRaster = gpCarSplashRaster; break; case PARTICLE_RAIN_SPLASH: case PARTICLE_RAIN_SPLASH_BIGGROW: entry->m_ppRaster = gpRainSplashRaster; break; case PARTICLE_RAIN_SPLASHUP: entry->m_ppRaster = gpRainSplashupRaster; break; case PARTICLE_WATERSPRAY: entry->m_ppRaster = gpWatersprayRaster; break; case PARTICLE_EXPLOSION_MEDIUM: case PARTICLE_EXPLOSION_LARGE: case PARTICLE_EXPLOSION_MFAST: case PARTICLE_EXPLOSION_LFAST: entry->m_ppRaster = gpExplosionMediumRaster; break; case PARTICLE_BOAT_SPLASH: entry->m_ppRaster = &gpBoatWakeRaster; break; case PARTICLE_ENGINE_SMOKE: case PARTICLE_ENGINE_SMOKE2: case PARTICLE_CARFLAME_SMOKE: case PARTICLE_FIREBALL_SMOKE: case PARTICLE_ROCKET_SMOKE: case PARTICLE_TEST: entry->m_ppRaster = &gpCloudRaster4; break; case PARTICLE_CARCOLLISION_DUST: case PARTICLE_BURNINGRUBBER_SMOKE: entry->m_ppRaster = &gpCollisionSmokeRaster; break; case PARTICLE_CAR_DEBRIS: case PARTICLE_HELI_DEBRIS: case PARTICLE_BIRD_DEBRIS: entry->m_ppRaster = gpCarDebrisRaster; break; case PARTICLE_GUNSHELL_FIRST: case PARTICLE_GUNSHELL: case PARTICLE_GUNSHELL_BUMP1: case PARTICLE_GUNSHELL_BUMP2: entry->m_ppRaster = &gpGunShellRaster; break; case PARTICLE_BIRD_FRONT: entry->m_ppRaster = gpBirdfrontRaster; break; case PARTICLE_SHIP_SIDE: entry->m_ppRaster = gpBoatRaster; break; case PARTICLE_BEASTIE: entry->m_ppRaster = &gpBeastieRaster; break; } } debug("CParticle ready"); } void CParticle::Shutdown() { debug("Shutting down CParticle..."); for ( int32 i = 0; i < MAX_SMOKE_FILES; i++ ) { RwTextureDestroy(gpSmokeTex[i]); gpSmokeTex[i] = nil; } RwTextureDestroy(gpSmoke2Tex); gpSmoke2Tex = nil; for ( int32 i = 0; i < MAX_RUBBER_FILES; i++ ) { RwTextureDestroy(gpRubberTex[i]); gpRubberTex[i] = nil; } for ( int32 i = 0; i < MAX_RAINSPLASH_FILES; i++ ) { RwTextureDestroy(gpRainSplashTex[i]); gpRainSplashTex[i] = nil; } for ( int32 i = 0; i < MAX_WATERSPRAY_FILES; i++ ) { RwTextureDestroy(gpWatersprayTex[i]); gpWatersprayTex[i] = nil; } for ( int32 i = 0; i < MAX_EXPLOSIONMEDIUM_FILES; i++ ) { RwTextureDestroy(gpExplosionMediumTex[i]); gpExplosionMediumTex[i] = nil; } for ( int32 i = 0; i < MAX_GUNFLASH_FILES; i++ ) { RwTextureDestroy(gpGunFlashTex[i]); gpGunFlashTex[i] = nil; } RwTextureDestroy(gpRainDropTex); gpRainDropTex = nil; for ( int32 i = 0; i < MAX_RAINSPLASHUP_FILES; i++ ) { RwTextureDestroy(gpRainSplashupTex[i]); gpRainSplashupTex[i] = nil; } for ( int32 i = 0; i < MAX_BIRDFRONT_FILES; i++ ) { RwTextureDestroy(gpBirdfrontTex[i]); gpBirdfrontTex[i] = nil; } for ( int32 i = 0; i < MAX_BOAT_FILES; i++ ) { RwTextureDestroy(gpBoatTex[i]); gpBoatTex[i] = nil; } for ( int32 i = 0; i < MAX_CARDEBRIS_FILES; i++ ) { RwTextureDestroy(gpCarDebrisTex[i]); gpCarDebrisTex[i] = nil; } for ( int32 i = 0; i < MAX_CARSPLASH_FILES; i++ ) { RwTextureDestroy(gpCarSplashTex[i]); gpCarSplashTex[i] = nil; } for ( int32 i = 0; i < MAX_RAINDRIP_FILES; i++ ) { RwTextureDestroy(gpRainDripTex[i]); gpRainDripTex[i] = nil; RwTextureDestroy(gpRainDripDarkTex[i]); gpRainDripDarkTex[i] = nil; } RwTextureDestroy(gpBoatWakeTex); gpBoatWakeTex = nil; RwTextureDestroy(gpFlame1Tex); gpFlame1Tex = nil; RwTextureDestroy(gpFlame5Tex); gpFlame5Tex = nil; RwTextureDestroy(gpRainDropSmallTex); gpRainDropSmallTex = nil; RwTextureDestroy(gpBloodTex); gpBloodTex = nil; RwTextureDestroy(gpLeafTex[0]); gpLeafTex[0] = nil; RwTextureDestroy(gpLeafTex[1]); gpLeafTex[1] = nil; RwTextureDestroy(gpCloudTex1); gpCloudTex1 = nil; RwTextureDestroy(gpCloudTex4); gpCloudTex4 = nil; RwTextureDestroy(gpBloodSmallTex); gpBloodSmallTex = nil; RwTextureDestroy(gpGungeTex); gpGungeTex = nil; RwTextureDestroy(gpCollisionSmokeTex); gpCollisionSmokeTex = nil; RwTextureDestroy(gpBulletHitTex); gpBulletHitTex = nil; RwTextureDestroy(gpGunShellTex); gpGunShellTex = nil; RwTextureDestroy(gpPointlightTex); gpPointlightTex = nil; RwTextureDestroy(gpSparkTex); gpSparkTex = nil; RwTextureDestroy(gpNewspaperTex); gpNewspaperTex = nil; RwTextureDestroy(gpGunSmokeTex); gpGunSmokeTex = nil; RwTextureDestroy(gpDotTex); gpDotTex = nil; RwTextureDestroy(gpHeatHazeTex); gpHeatHazeTex = nil; RwTextureDestroy(gpBeastieTex); gpBeastieTex = nil; int32 slot; slot = CTxdStore::FindTxdSlot("particle"); CTxdStore::RemoveTxdSlot(slot); debug("CParticle shut down"); } void CParticle::AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity, float fSize, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan) { CVector vecDist = vecEnd - vecStart; float fDist = vecDist.Magnitude(); float fSteps = Max(fDist / fPower, 1.0f); int32 nSteps = (int32)fSteps; CVector vecStep = vecDist * (1.0f / (float)nSteps); for ( int32 i = 0; i < nSteps; i++ ) { CVector vecPos = float(i) * vecStep + vecStart; AddParticle(type, vecPos, vecDir, pEntity, fSize, nRotationSpeed, nRotation, nCurFrame, nLifeSpan); } } void CParticle::AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity, float fSize, RwRGBA const &color, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan) { CVector vecDist = vecEnd - vecStart; float fDist = vecDist.Magnitude(); float fSteps = Max(fDist / fPower, 1.0f); int32 nSteps = (int32)fSteps; CVector vecStep = vecDist * (1.0f / (float)nSteps); for ( int32 i = 0; i < nSteps; i++ ) { CVector vecPos = float(i) * vecStep + vecStart; AddParticle(type, vecPos, vecDir, pEntity, fSize, color, nRotationSpeed, nRotation, nCurFrame, nLifeSpan); } } CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, float fSize, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan) { CRGBA color(0, 0, 0, 0); return AddParticle(type, vecPos, vecDir, pEntity, fSize, color, nRotationSpeed, nRotation, nCurFrame, nLifeSpan); } CParticle *CParticle::AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, float fSize, RwRGBA const &color, int32 nRotationSpeed, int32 nRotation, int32 nCurFrame, int32 nLifeSpan) { if ( CTimer::GetIsPaused() ) return nil; if ( ( type == PARTICLE_ENGINE_SMOKE || type == PARTICLE_ENGINE_SMOKE2 || type == PARTICLE_ENGINE_STEAM || type == PARTICLE_CARFLAME_SMOKE || type == PARTICLE_RUBBER_SMOKE || type == PARTICLE_BURNINGRUBBER_SMOKE || type == PARTICLE_EXHAUST_FUMES || type == PARTICLE_CARCOLLISION_DUST ) && nParticleCreationInterval & CTimer::GetFrameCounter() ) { return nil; } if ( !CReplay::IsPlayingBack() ) CReplay::RecordParticle(type, vecPos, vecDir, fSize, color); CParticle *pParticle = m_pUnusedListHead; if ( pParticle == nil ) return nil; tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[type]; if ( psystem->m_fCreateRange != 0.0f && psystem->m_fCreateRange < ( TheCamera.GetPosition() - vecPos ).MagnitudeSqr() ) return nil; pParticle->m_fSize = psystem->m_fDefaultInitialRadius; pParticle->m_fExpansionRate = psystem->m_fExpansionRate; if ( nLifeSpan != 0 ) pParticle->m_nTimeWhenWillBeDestroyed = CTimer::GetTimeInMilliseconds() + nLifeSpan; else pParticle->m_nTimeWhenWillBeDestroyed = CTimer::GetTimeInMilliseconds() + psystem->m_nLifeSpan; pParticle->m_nColorIntensity = psystem->m_nFadeToBlackInitialIntensity; pParticle->m_nFadeToBlackTimer = psystem->m_nFadeToBlackAmount; if ( psystem->m_nFadeToBlackTime ) pParticle->m_nFadeToBlackTimer /= psystem->m_nFadeToBlackTime; pParticle->m_nAlpha = psystem->m_nFadeAlphaInitialIntensity; pParticle->m_nFadeAlphaTimer = psystem->m_nFadeAlphaAmount; if ( psystem->m_nFadeAlphaTime ) pParticle->m_nFadeAlphaTimer /= psystem->m_nFadeAlphaTime; pParticle->m_nCurrentZRotation = psystem->m_nZRotationInitialAngle; pParticle->m_fCurrentZRadius = psystem->m_fInitialZRadius; if ( nCurFrame != 0 ) pParticle->m_nCurrentFrame = nCurFrame; else pParticle->m_nCurrentFrame = psystem->m_nStartAnimationFrame; pParticle->m_nZRotationTimer = 0; pParticle->m_nZRadiusTimer = 0; pParticle->m_nAnimationSpeedTimer = 0; pParticle->m_fZGround = 0.0f; if ( type != PARTICLE_HEATHAZE ) pParticle->m_vecPosition = vecPos; else { CVector screen; float w, h; if ( !CSprite::CalcScreenCoors(vecPos, &screen, &w, &h, true) ) return nil; pParticle->m_vecPosition = screen; psystem->m_vecTextureStretch.x = w; psystem->m_vecTextureStretch.y = h; } pParticle->m_vecVelocity = vecDir; pParticle->m_vecParticleMovementOffset = CVector(0.0f, 0.0f, 0.0f); pParticle->m_nTimeWhenColorWillBeChanged = 0; if ( color.alpha != 0 ) RwRGBAAssign(&pParticle->m_Color, &color); else { RwRGBAAssign(&pParticle->m_Color, psystem->m_RenderColouring); if ( psystem->m_ColorFadeTime != 0 ) pParticle->m_nTimeWhenColorWillBeChanged = CTimer::GetTimeInMilliseconds() + psystem->m_ColorFadeTime; if ( psystem->m_InitialColorVariation != 0 ) { int32 ColorVariation = CGeneral::GetRandomNumberInRange(-psystem->m_InitialColorVariation, psystem->m_InitialColorVariation); //float ColorVariation = CGeneral::GetRandomNumberInRange((float)-psystem->m_InitialColorVariation, (float)psystem->m_InitialColorVariation); pParticle->m_Color.red = clamp(pParticle->m_Color.red + PERCENT(pParticle->m_Color.red, ColorVariation), 0, 255); pParticle->m_Color.green = clamp(pParticle->m_Color.green + PERCENT(pParticle->m_Color.green, ColorVariation), 0, 255); pParticle->m_Color.blue = clamp(pParticle->m_Color.blue + PERCENT(pParticle->m_Color.blue, ColorVariation), 0, 255); } } pParticle->m_nRotation = nRotation; if ( nRotationSpeed != 0 ) pParticle->m_nRotationStep = nRotationSpeed; else pParticle->m_nRotationStep = psystem->m_nRotationSpeed; if ( CGeneral::GetRandomNumber() & 1 ) pParticle->m_nRotationStep = -pParticle->m_nRotationStep; if ( psystem->m_fPositionRandomError != 0.0f ) { pParticle->m_vecPosition.x += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; pParticle->m_vecPosition.y += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; if ( psystem->Flags & RAND_VERT_V ) pParticle->m_vecPosition.z += psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; } if ( psystem->m_fVelocityRandomError != 0.0f ) { pParticle->m_vecVelocity.x += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; pParticle->m_vecVelocity.y += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; if ( psystem->Flags & RAND_VERT_V ) pParticle->m_vecVelocity.z += psystem->m_fVelocityRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; } if ( psystem->m_fExpansionRateError != 0.0f && !(psystem->Flags & SCREEN_TRAIL) ) pParticle->m_fExpansionRate += psystem->m_fExpansionRateError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE] + psystem->m_fExpansionRateError; if ( psystem->m_nRotationRateError != 0 ) pParticle->m_nRotationStep += CGeneral::GetRandomNumberInRange(-psystem->m_nRotationRateError, psystem->m_nRotationRateError); if ( psystem->m_nLifeSpanErrorShape != 0 ) { float randVal = ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; if ( randVal > 0.0f ) pParticle->m_nTimeWhenWillBeDestroyed += int32(float(psystem->m_nLifeSpan) * randVal * float(psystem->m_nLifeSpanErrorShape)); else pParticle->m_nTimeWhenWillBeDestroyed += int32(float(psystem->m_nLifeSpan) * randVal / float(psystem->m_nLifeSpanErrorShape)); } if ( psystem->Flags & ZCHECK_FIRST ) { static bool bValidGroundFound = false; static CVector LastTestCoors; static float LastTestGroundZ; if ( bValidGroundFound && vecPos.x == LastTestCoors.x && vecPos.y == LastTestCoors.y && vecPos.z == LastTestCoors.z ) { pParticle->m_fZGround = LastTestGroundZ; } else { bValidGroundFound = false; CColPoint point; CEntity *entity; if ( !CWorld::ProcessVerticalLine( pParticle->m_vecPosition + CVector(0.0f, 0.0f, 0.5f), -100.0f, point, entity, true, true, false, false, true, false, nil) ) { return nil; } if ( point.point.z >= pParticle->m_vecPosition.z ) return nil; pParticle->m_fZGround = point.point.z; bValidGroundFound = true; LastTestCoors = vecPos; LastTestGroundZ = point.point.z; } } if ( psystem->Flags & ZCHECK_BUMP ) { static float Z_Ground = 0.0f; if ( psystem->Flags & ZCHECK_BUMP_FIRST ) { bool bZFound = false; Z_Ground = CWorld::FindGroundZFor3DCoord(vecPos.x, vecPos.y, vecPos.z, (bool *)&bZFound); if ( bZFound == false ) return nil; pParticle->m_fZGround = Z_Ground; } pParticle->m_fZGround = Z_Ground; } switch ( type ) { case PARTICLE_DEBRIS: pParticle->m_vecVelocity.z *= CGeneral::GetRandomNumberInRange(0.5f, 3.0f); break; case PARTICLE_EXPLOSION_MEDIUM: pParticle->m_nColorIntensity -= 30 * (CGeneral::GetRandomNumber() & 1); // mb "+= -30 * rand" here ? pParticle->m_nAnimationSpeedTimer = CGeneral::GetRandomNumber() & 7; pParticle->m_fSize = CGeneral::GetRandomNumberInRange(0.3f, 0.8f); pParticle->m_vecPosition.z -= CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); break; case PARTICLE_EXPLOSION_LARGE: pParticle->m_nColorIntensity -= 30 * (CGeneral::GetRandomNumber() & 1); // mb "+= -30 * rand" here ? pParticle->m_nAnimationSpeedTimer = CGeneral::GetRandomNumber() & 7; pParticle->m_fSize = CGeneral::GetRandomNumberInRange(0.8f, 1.4f); pParticle->m_vecPosition.z -= CGeneral::GetRandomNumberInRange(-0.3f, 0.3f); break; case PARTICLE_WATER_HYDRANT: pParticle->m_vecPosition.z += 20.0f * psystem->m_fPositionRandomError * ms_afRandTable[CGeneral::GetRandomNumber() % RAND_TABLE_SIZE]; break; default: break; } if ( fSize != 0.0f ) pParticle->m_fSize = fSize; m_pUnusedListHead = pParticle->m_pNext; pParticle->m_pNext = psystem->m_pParticles; psystem->m_pParticles = pParticle; return pParticle; } void CParticle::Update() { if ( CTimer::GetIsPaused() ) return; CRGBA color(0, 0, 0, 0); float fFricDeccel50 = pow(0.50f, CTimer::GetTimeStep()); float fFricDeccel80 = pow(0.80f, CTimer::GetTimeStep()); float fFricDeccel90 = pow(0.90f, CTimer::GetTimeStep()); float fFricDeccel95 = pow(0.95f, CTimer::GetTimeStep()); float fFricDeccel96 = pow(0.96f, CTimer::GetTimeStep()); float fFricDeccel99 = pow(0.99f, CTimer::GetTimeStep()); CParticleObject::UpdateAll(); // ejaculation at 23:00, 23:15, 23:30, 23:45 if ( CClock::ms_nGameClockHours == 23 && ( CClock::ms_nGameClockMinutes == 0 || CClock::ms_nGameClockMinutes == 15 || CClock::ms_nGameClockMinutes == 30 || CClock::ms_nGameClockMinutes == 45 ) ) { AddParticle(PARTICLE_CAR_SPLASH, CVector(557.03f, -4.0f, 151.46f), CVector(0.0f, 0.0f, 2.5f), NULL, 2.0f, CRGBA(255, 255, 255, 255), 0, 0, 1, 1000); } for ( int32 i = 0; i < MAX_PARTICLES; i++ ) { tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[i]; CParticle *particle = psystem->m_pParticles; CParticle *prevParticle = nil; bool bRemoveParticle; if ( particle == nil ) continue; for ( ; particle != nil; _Next(particle, prevParticle, psystem, bRemoveParticle) ) { CVector vecWind(0.0f, 0.0f, 0.0f); bRemoveParticle = false; CVector vecMoveStep = particle->m_vecVelocity * CTimer::GetTimeStep(); CVector vecPos = particle->m_vecPosition; if ( numWaterDropOnScreen == 0 ) clearWaterDrop = false; if ( psystem->m_Type == PARTICLE_WATERDROP ) { if ( CGame::IsInInterior() || clearWaterDrop == true ) { bRemoveParticle = true; continue; } static uint8 nWaterDropCount; if ( nWaterDropCount == 5 ) { vecMoveStep = CVector(0.0f, 0.0f, 0.0f); particle->m_nTimeWhenWillBeDestroyed += 1250; nWaterDropCount = 0; } else { if ( TheCamera.m_CameraAverageSpeed > 0.35f ) { if ( vecMoveStep.Magnitude() > 0.5f ) { if ( vecMoveStep.Magnitude() > 0.4f && vecMoveStep.Magnitude() < 0.8f ) { vecMoveStep.x += TheCamera.m_CameraAverageSpeed * 1.5f; vecMoveStep.y += TheCamera.m_CameraAverageSpeed * 1.5f; } else if ( vecMoveStep.Magnitude() != 0.0f ) { vecMoveStep.x += CGeneral::GetRandomNumberInRange(0.01f, 0.05f); vecMoveStep.y += CGeneral::GetRandomNumberInRange(0.01f, 0.05f); } } } nWaterDropCount++; } if ( vecPos.z <= 1.5f ) vecMoveStep.z = 0.0f; } if ( psystem->m_Type == PARTICLE_HEATHAZE || psystem->m_Type == PARTICLE_HEATHAZE_IN_DIST ) { #ifdef FIX_BUGS int32 nSinCosIndex = (int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) + SIN_COS_TABLE_SIZE) % SIN_COS_TABLE_SIZE; #else int32 nSinCosIndex = int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) % SIN_COS_TABLE_SIZE; #endif vecMoveStep.x = Sin(nSinCosIndex); vecMoveStep.y = Sin(nSinCosIndex); if ( psystem->m_Type == PARTICLE_HEATHAZE_IN_DIST ) particle->m_nRotation = int16((float)particle->m_nRotation + 0.75f); else particle->m_nRotation = int16((float)particle->m_nRotation + 1.0f); } if ( psystem->m_Type == PARTICLE_BEASTIE ) { #ifdef FIX_BUGS int32 nSinCosIndex = (int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) + SIN_COS_TABLE_SIZE) % SIN_COS_TABLE_SIZE; #else int32 nSinCosIndex = int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) % SIN_COS_TABLE_SIZE; #endif particle->m_vecVelocity.x = 0.50f * Cos(nSinCosIndex); particle->m_vecVelocity.y = Cos(nSinCosIndex); particle->m_vecVelocity.z = 0.25f * Sin(nSinCosIndex); if ( particle->m_vecVelocity.Magnitude() > 2.0f || vecPos.z > 40.0f || (TheCamera.GetPosition() - vecPos).Magnitude() < 60.0f ) { bRemoveParticle = true; continue; } } vecPos += vecMoveStep; if ( psystem->m_Type == PARTICLE_FIREBALL ) { AddParticle(PARTICLE_HEATHAZE, particle->m_vecPosition, CVector(0.0f, 0.0f, 0.0f), nil, particle->m_fSize * 5.0f); } if ( psystem->m_Type == PARTICLE_GUNSMOKE2 ) { if ( CTimer::GetFrameCounter() & 10 ) { #ifdef FIX_BUGS if ( FindPlayerPed() && FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN ) #else if ( FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN ) #endif { AddParticle(PARTICLE_HEATHAZE, particle->m_vecPosition, CVector(0.0f, 0.0f, 0.0f)); } } } if ( CWeather::Wind > 0.0f ) { if ( vecMoveStep.Magnitude() != 0.0f ) { vecWind.x = CGeneral::GetRandomNumberInRange(0.75f, 1.25f) * -CWeather::Wind; vecWind.y = CGeneral::GetRandomNumberInRange(0.75f, 1.25f) * -CWeather::Wind; vecWind *= PARTICLE_WIND_TEST_SCALE * psystem->m_fWindFactor * CTimer::GetTimeStep(); particle->m_vecVelocity += vecWind; } } if ( psystem->m_Type == PARTICLE_RAINDROP || psystem->m_Type == PARTICLE_RAINDROP_SMALL || psystem->m_Type == PARTICLE_RAIN_SPLASH || psystem->m_Type == PARTICLE_RAIN_SPLASH_BIGGROW || psystem->m_Type == PARTICLE_CAR_SPLASH || psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_RAINDROP_2D ) { int32 nMaxDrops = int32(6.0f * TheCamera.m_CameraAverageSpeed + 1.0f); float fDistToCam = 0.0f; if ( psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_CAR_SPLASH ) { if ( vecPos.z + particle->m_fSize < 5.0f ) { bRemoveParticle = true; continue; } switch ( TheCamera.GetLookDirection() ) { case LOOKING_LEFT: case LOOKING_RIGHT: case LOOKING_FORWARD: nMaxDrops /= 2; break; default: nMaxDrops = 0; break; } fDistToCam = (TheCamera.GetPosition() - vecPos).Magnitude(); } if ( numWaterDropOnScreen < nMaxDrops && numWaterDropOnScreen < 63 && fDistToCam < 10.0f && clearWaterDrop == false && !CGame::IsInInterior() ) { CVector vecWaterdropTarget ( CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), CGeneral::GetRandomNumberInRange(0.1f, 0.75f), -0.01f ); CVector vecWaterdropPos; if ( TheCamera.m_CameraAverageSpeed < 0.35f ) vecWaterdropPos.x = (float)CGeneral::GetRandomNumberInRange(50, int32(SCREEN_WIDTH) - 50); else vecWaterdropPos.x = (float)CGeneral::GetRandomNumberInRange(200, int32(SCREEN_WIDTH) - 200); if ( psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_CAR_SPLASH ) vecWaterdropPos.y = (float)CGeneral::GetRandomNumberInRange(SCREEN_HEIGHT / 2, SCREEN_HEIGHT); else { if ( TheCamera.m_CameraAverageSpeed < 0.35f ) vecWaterdropPos.y = (float)CGeneral::GetRandomNumberInRange(0, int32(SCREEN_HEIGHT)); else vecWaterdropPos.y = (float)CGeneral::GetRandomNumberInRange(150, int32(SCREEN_HEIGHT) - 200); } vecWaterdropPos.z = 2.0f; if ( AddParticle(PARTICLE_WATERDROP, vecWaterdropPos, vecWaterdropTarget, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.15f), 0, 0, CGeneral::GetRandomNumber() & 1, 0) != nil ) { numWaterDropOnScreen++; } } } if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed || particle->m_nAlpha == 0 ) { bRemoveParticle = true; continue; } if ( particle->m_nTimeWhenColorWillBeChanged != 0 ) { if ( particle->m_nTimeWhenColorWillBeChanged > CTimer::GetTimeInMilliseconds() ) { float colorMul = 1.0f - float(particle->m_nTimeWhenColorWillBeChanged - CTimer::GetTimeInMilliseconds()) / float(psystem->m_ColorFadeTime); particle->m_Color.red = clamp( psystem->m_RenderColouring.red + int32(float(psystem->m_FadeDestinationColor.red - psystem->m_RenderColouring.red) * colorMul), 0, 255); particle->m_Color.green = clamp( psystem->m_RenderColouring.green + int32(float(psystem->m_FadeDestinationColor.green - psystem->m_RenderColouring.green) * colorMul), 0, 255); particle->m_Color.blue = clamp( psystem->m_RenderColouring.blue + int32(float(psystem->m_FadeDestinationColor.blue - psystem->m_RenderColouring.blue) * colorMul), 0, 255); } else RwRGBAAssign(&particle->m_Color, psystem->m_FadeDestinationColor); } if ( psystem->Flags & CLIPOUT2D ) { if ( particle->m_vecPosition.x < -10.0f || particle->m_vecPosition.x > SCREEN_WIDTH + 10.0f || particle->m_vecPosition.y < -10.0f || particle->m_vecPosition.y > SCREEN_HEIGHT + 10.0f ) { bRemoveParticle = true; continue; } } if ( !(psystem->Flags & SCREEN_TRAIL) ) { float size; if ( particle->m_fExpansionRate > 0.0f ) { float speed = Max(vecWind.Magnitude(), vecMoveStep.Magnitude()); if ( psystem->m_Type == PARTICLE_EXHAUST_FUMES || psystem->m_Type == PARTICLE_ENGINE_STEAM ) speed *= 2.0f; if ( ( psystem->m_Type == PARTICLE_BOAT_SPLASH || psystem->m_Type == PARTICLE_CAR_SPLASH ) && particle->m_fSize > 1.2f ) { size = particle->m_fSize - (1.0f + speed) * particle->m_fExpansionRate; particle->m_vecVelocity.z -= 0.15f; } else size = particle->m_fSize + (1.0f + speed) * particle->m_fExpansionRate; } else size = particle->m_fSize + particle->m_fExpansionRate; if ( psystem->m_Type == PARTICLE_WATERDROP ) size = (size - Abs(vecMoveStep.x) * 0.000150000007f) + (Abs(vecMoveStep.z) * 0.0500000007f); //TODO: if ( size < 0.0f ) { bRemoveParticle = true; continue; } particle->m_fSize = size; } switch ( psystem->m_nFrictionDecceleration ) { case 50: particle->m_vecVelocity *= fFricDeccel50; break; case 80: particle->m_vecVelocity *= fFricDeccel80; break; case 90: particle->m_vecVelocity *= fFricDeccel90; break; case 95: particle->m_vecVelocity *= fFricDeccel95; break; case 96: particle->m_vecVelocity *= fFricDeccel96; break; case 99: particle->m_vecVelocity *= fFricDeccel99; break; } if ( psystem->m_fGravitationalAcceleration > 0.0f ) { if ( -50.0f * psystem->m_fGravitationalAcceleration < particle->m_vecVelocity.z ) particle->m_vecVelocity.z -= psystem->m_fGravitationalAcceleration * CTimer::GetTimeStep(); if ( psystem->Flags & ZCHECK_FIRST ) { if ( particle->m_vecPosition.z < particle->m_fZGround ) { switch ( psystem->m_Type ) { case PARTICLE_RAINDROP: case PARTICLE_RAINDROP_SMALL: { bRemoveParticle = true; if ( CGeneral::GetRandomNumber() & 1 ) { AddParticle(PARTICLE_RAIN_SPLASH, CVector ( particle->m_vecPosition.x, particle->m_vecPosition.y, 0.05f + particle->m_fZGround ), CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0); } else { AddParticle(PARTICLE_RAIN_SPLASHUP, CVector ( particle->m_vecPosition.x, particle->m_vecPosition.y, 0.05f + particle->m_fZGround ), CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0); } continue; } break; case PARTICLE_WHEEL_WATER: { bRemoveParticle = true; int32 randVal = CGeneral::GetRandomNumber(); if ( randVal & 1 ) { if ( (randVal % 5) == 0 ) { AddParticle(PARTICLE_RAIN_SPLASH, CVector ( particle->m_vecPosition.x, particle->m_vecPosition.y, 0.05f + particle->m_fZGround ), CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0); } else { AddParticle(PARTICLE_RAIN_SPLASHUP, CVector ( particle->m_vecPosition.x, particle->m_vecPosition.y, 0.05f + particle->m_fZGround ), CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, 0, 0); } } continue; } break; case PARTICLE_BLOOD: case PARTICLE_BLOOD_SMALL: { bRemoveParticle = true; CVector vecPosn = particle->m_vecPosition; vecPosn.z += 1.0f; Randomizer++; int32 randVal = int32(Randomizer & 7); if ( randVal == 5 ) { CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &vecPosn, 0.1f, 0.0f, 0.0f, -0.1f, 255, 255, 0, 0, 4.0f, (CGeneral::GetRandomNumber() & 4095) + 2000, 1.0f); } else if ( randVal == 2 ) { CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpBloodPoolTex, &vecPosn, 0.2f, 0.0f, 0.0f, -0.2f, 255, 255, 0, 0, 4.0f, (CGeneral::GetRandomNumber() & 4095) + 8000, 1.0f); } continue; } break; default: break; } } } else if ( psystem->Flags & ZCHECK_STEP ) { CColPoint point; CEntity *entity; if ( CWorld::ProcessVerticalLine(particle->m_vecPosition, vecPos.z, point, entity, true, true, false, false, true, false, nil) ) { if ( vecPos.z <= point.point.z ) { vecPos.z = point.point.z; if ( psystem->m_Type == PARTICLE_DEBRIS2 ) { particle->m_vecVelocity.x *= 0.8f; particle->m_vecVelocity.y *= 0.8f; particle->m_vecVelocity.z *= -0.4f; if ( particle->m_vecVelocity.z < 0.005f ) particle->m_vecVelocity.z = 0.0f; } } } } else if ( psystem->Flags & ZCHECK_BUMP ) { if ( particle->m_vecPosition.z < particle->m_fZGround ) { switch ( psystem->m_Type ) { case PARTICLE_GUNSHELL_FIRST: case PARTICLE_GUNSHELL: { bRemoveParticle = true; AddParticle(PARTICLE_GUNSHELL_BUMP1, CVector ( particle->m_vecPosition.x, particle->m_vecPosition.y, 0.05f + particle->m_fZGround ), CVector ( CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), CGeneral::GetRandomNumberInRange(0.05f, 0.1f) ), nil, particle->m_fSize, color, particle->m_nRotationStep, 0, 0, 0); PlayOneShotScriptObject(SCRIPT_SOUND_GUNSHELL_DROP, particle->m_vecPosition); } break; case PARTICLE_GUNSHELL_BUMP1: { bRemoveParticle = true; AddParticle(PARTICLE_GUNSHELL_BUMP2, CVector ( particle->m_vecPosition.x, particle->m_vecPosition.y, 0.05f + particle->m_fZGround ), CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.03f, 0.06f)), nil, particle->m_fSize, color, 0, 0, 0, 0); PlayOneShotScriptObject(SCRIPT_SOUND_GUNSHELL_DROP_SOFT, particle->m_vecPosition); } break; case PARTICLE_GUNSHELL_BUMP2: { bRemoveParticle = true; continue; } break; default: break; } } } } else { if ( psystem->m_fGravitationalAcceleration < 0.0f ) { if ( -5.0f * psystem->m_fGravitationalAcceleration > particle->m_vecVelocity.z ) particle->m_vecVelocity.z -= psystem->m_fGravitationalAcceleration * CTimer::GetTimeStep(); } else { if ( psystem->Flags & ZCHECK_STEP ) { CColPoint point; CEntity *entity; if ( CWorld::ProcessVerticalLine(particle->m_vecPosition, vecPos.z, point, entity, true, false, false, false, true, false, nil) ) { if ( vecPos.z <= point.point.z ) { vecPos.z = point.point.z; if ( psystem->m_Type == PARTICLE_HELI_ATTACK ) { bRemoveParticle = true; AddParticle(PARTICLE_STEAM, vecPos, CVector(0.0f, 0.0f, 0.05f), nil, 0.2f, 0, 0, 0, 0); continue; } } } } } } if ( particle->m_nFadeToBlackTimer != 0 ) { particle->m_nColorIntensity = clamp(particle->m_nColorIntensity - particle->m_nFadeToBlackTimer, 0, 255); } if ( particle->m_nFadeAlphaTimer != 0 ) { particle->m_nAlpha = clamp(particle->m_nAlpha - particle->m_nFadeAlphaTimer, 0, 255); if ( particle->m_nAlpha == 0 ) { bRemoveParticle = true; continue; } } if ( psystem->m_nZRotationAngleChangeAmount != 0 ) { if ( particle->m_nZRotationTimer >= psystem->m_nZRotationChangeTime ) { particle->m_nZRotationTimer = 0; particle->m_nCurrentZRotation += psystem->m_nZRotationAngleChangeAmount; } else ++particle->m_nZRotationTimer; } if ( psystem->m_fZRadiusChangeAmount != 0.0f ) { if ( particle->m_nZRadiusTimer >= psystem->m_nZRadiusChangeTime ) { particle->m_nZRadiusTimer = 0; particle->m_fCurrentZRadius += psystem->m_fZRadiusChangeAmount; } else ++particle->m_nZRadiusTimer; } if ( psystem->m_nAnimationSpeed != 0 ) { if ( particle->m_nAnimationSpeedTimer > psystem->m_nAnimationSpeed ) { particle->m_nAnimationSpeedTimer = 0; if ( ++particle->m_nCurrentFrame > psystem->m_nFinalAnimationFrame ) { if ( psystem->Flags & CYCLE_ANIM ) particle->m_nCurrentFrame = psystem->m_nStartAnimationFrame; else --particle->m_nCurrentFrame; } } else ++particle->m_nAnimationSpeedTimer; } if ( particle->m_nRotationStep != 0 ) #ifdef FIX_BUGS particle->m_nRotation = CGeneral::LimitAngle(particle->m_nRotation + particle->m_nRotationStep); #else particle->m_nRotation += particle->m_nRotationStep; #endif if ( particle->m_fCurrentZRadius != 0.0f ) { int32 nSinCosIndex = particle->m_nCurrentZRotation % SIN_COS_TABLE_SIZE; float fX = (Cos(nSinCosIndex) - Sin(nSinCosIndex)) * particle->m_fCurrentZRadius; float fY = (Sin(nSinCosIndex) + Cos(nSinCosIndex)) * particle->m_fCurrentZRadius; vecPos -= particle->m_vecParticleMovementOffset; vecPos += CVector(fX, fY, 0.0f); particle->m_vecParticleMovementOffset = CVector(fX, fY, 0.0f); } particle->m_vecPosition = vecPos; } } } void CParticle::Render() { PUSH_RENDERGROUP("CParticle::Render"); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSWRAP); RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); CSprite::InitSpriteBuffer2D(); uint32 flags = DRAW_OPAQUE; RwRaster *prevFrame = nil; for ( int32 i = 0; i < MAX_PARTICLES; i++ ) { tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[i]; bool particleBanned = false; CParticle *particle = psystem->m_pParticles; RwRaster **frames = psystem->m_ppRaster; tParticleType type = psystem->m_Type; if ( type == PARTICLE_ENGINE_SMOKE || type == PARTICLE_ENGINE_SMOKE2 || type == PARTICLE_ENGINE_STEAM || type == PARTICLE_CARFLAME_SMOKE || type == PARTICLE_RUBBER_SMOKE || type == PARTICLE_BURNINGRUBBER_SMOKE || type == PARTICLE_EXHAUST_FUMES || type == PARTICLE_CARCOLLISION_DUST ) { particleBanned = true; } if ( particle ) { if ( (flags & DRAW_OPAQUE) != (psystem->Flags & DRAW_OPAQUE) || (flags & DRAW_DARK) != (psystem->Flags & DRAW_DARK) ) { CSprite::FlushSpriteBuffer(); if ( psystem->Flags & DRAW_OPAQUE ) { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); } else { if ( psystem->Flags & DRAW_DARK ) RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); else RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); } flags = psystem->Flags; } if ( frames != nil ) { RwRaster *curFrame = *frames; if ( curFrame != prevFrame ) { CSprite::FlushSpriteBuffer(); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)curFrame); prevFrame = curFrame; } } } while ( particle != nil ) { bool canDraw = true; if ( particle->m_nAlpha == 0 ) canDraw = false; if ( canDraw && psystem->m_nFinalAnimationFrame != 0 && frames != nil ) { RwRaster *curFrame = frames[particle->m_nCurrentFrame]; if ( prevFrame != curFrame ) { CSprite::FlushSpriteBuffer(); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)curFrame); prevFrame = curFrame; } } if ( canDraw && psystem->Flags & DRAWTOP2D ) { float screenZ = (particle->m_vecPosition.z - CDraw::GetNearClipZ()) * (CSprite::GetFarScreenZ() - CSprite::GetNearScreenZ()) * CDraw::GetFarClipZ() / ( (CDraw::GetFarClipZ() - CDraw::GetNearClipZ()) * particle->m_vecPosition.z ) + CSprite::GetNearScreenZ(); float stretchTexW; float stretchTexH; if ( i == PARTICLE_RAINDROP || i == PARTICLE_RAINDROP_SMALL || i == PARTICLE_RAINDROP_2D ) { stretchTexW = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x * (float)particle->m_nCurrentFrame + 63.0f; stretchTexH = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y * (float)particle->m_nCurrentFrame + 63.0f; } else { stretchTexW = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x + 63.0f; stretchTexH = CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y + 63.0f; } if ( i == PARTICLE_WATERDROP ) { int32 timeLeft = (particle->m_nTimeWhenWillBeDestroyed - CTimer::GetTimeInMilliseconds()) / particle->m_nTimeWhenWillBeDestroyed; stretchTexH += (1.0f - (float)timeLeft ) * psystem->m_vecTextureStretch.y; RwRect rect; rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); FxType fxtype; if ( particle->m_nCurrentFrame != 0 ) fxtype = FXTYPE_WATER2; else fxtype = FXTYPE_WATER1; CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, fxtype); canDraw = false; } if ( i == PARTICLE_BLOODDROP ) { int32 timeLeft = (particle->m_nTimeWhenWillBeDestroyed - CTimer::GetTimeInMilliseconds()) / particle->m_nTimeWhenWillBeDestroyed; stretchTexH += (1.0f + (float)timeLeft) * psystem->m_vecTextureStretch.y; stretchTexW += (1.0f - (float)timeLeft) * psystem->m_vecTextureStretch.x; RwRect rect; rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH)); FxType fxtype; if ( particle->m_nCurrentFrame ) fxtype = FXTYPE_BLOOD2; else fxtype = FXTYPE_BLOOD1; CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, fxtype); canDraw = false; } if ( i == PARTICLE_HEATHAZE_IN_DIST ) { RwRect rect; rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH * 0.15f)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * stretchTexW)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * stretchTexH * 0.15f)); CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, FXTYPE_HEATHAZE); canDraw = false; } if ( i == PARTICLE_HEATHAZE ) { RwRect rect; switch ( TheCamera.GetLookDirection() ) { case LOOKING_LEFT: rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x * 2.0f)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); rect.w = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); break; case LOOKING_RIGHT: rect.x = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x * 4.0f)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); break; default: rect.x = int32(particle->m_vecPosition.x - SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); rect.y = int32(particle->m_vecPosition.y - SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); rect.w = int32(particle->m_vecPosition.x + SCREEN_STRETCH_X(particle->m_fSize * psystem->m_vecTextureStretch.x)); rect.h = int32(particle->m_vecPosition.y + SCREEN_STRETCH_Y(particle->m_fSize * psystem->m_vecTextureStretch.y)); break; } CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, FXTYPE_HEATHAZE); canDraw = false; } if ( canDraw ) { if ( particle->m_nRotation != 0 ) { CSprite::RenderBufferedOneXLUSprite2D_Rotate_Dimension( particle->m_vecPosition.x, particle->m_vecPosition.y, particle->m_fSize * stretchTexW, particle->m_fSize * stretchTexH, particle->m_Color, particle->m_nColorIntensity, DEGTORAD((float)particle->m_nRotation), particle->m_nAlpha); } else { CSprite::RenderBufferedOneXLUSprite2D( particle->m_vecPosition.x, particle->m_vecPosition.y, particle->m_fSize * stretchTexW, particle->m_fSize * stretchTexH, particle->m_Color, particle->m_nColorIntensity, particle->m_nAlpha); } } canDraw = false; } if ( canDraw ) { CVector coors; float w; float h; if ( CSprite::CalcScreenCoors(particle->m_vecPosition, &coors, &w, &h, true) ) { if ( i == PARTICLE_ENGINE_STEAM || i == PARTICLE_ENGINE_SMOKE || i == PARTICLE_ENGINE_SMOKE2 || i == PARTICLE_CARFLAME_SMOKE || i == PARTICLE_CARCOLLISION_DUST || i == PARTICLE_EXHAUST_FUMES || i == PARTICLE_RUBBER_SMOKE || i == PARTICLE_BURNINGRUBBER_SMOKE ) { switch ( TheCamera.GetLookDirection() ) { case LOOKING_LEFT: case LOOKING_RIGHT: w += CGeneral::GetRandomNumberInRange(1.0f, 7.5f) * psystem->m_vecTextureStretch.x; h += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y; break; default: w += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x; h += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y; break; } } else if ( i == PARTICLE_WATER_HYDRANT ) { int32 timeLeft = (particle->m_nTimeWhenWillBeDestroyed - CTimer::GetTimeInMilliseconds()) / particle->m_nTimeWhenWillBeDestroyed; w += (1.0f - (float)timeLeft) * psystem->m_vecTextureStretch.x; h += (1.0f - (float)timeLeft) * psystem->m_vecTextureStretch.y; } else if ( i == PARTICLE_FLYERS ) { w += psystem->m_vecTextureStretch.x; h += psystem->m_vecTextureStretch.y; w = Max(w, 12.0f); h = Max(h, 12.0f); } else { w += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.x; h += CGeneral::GetRandomNumberInRange(0.1f, 1.0f) * psystem->m_vecTextureStretch.y; } if ( i == PARTICLE_WATER_HYDRANT || (!particleBanned || SCREEN_WIDTH * fParticleScaleLimit >= w) && SCREEN_HEIGHT * fParticleScaleLimit >= h ) { if ( i == PARTICLE_WATER_HYDRANT ) { RwRect rect; if ( w > 0.0f ) { rect.x = int32(coors.x - SCREEN_STRETCH_X(particle->m_fSize * w)); rect.w = int32(coors.x + SCREEN_STRETCH_X(particle->m_fSize * w)); } else { rect.w = int32(coors.x - SCREEN_STRETCH_X(particle->m_fSize * w)); rect.x = int32(coors.x + SCREEN_STRETCH_X(particle->m_fSize * w)); } if ( h > 0.0f ) { rect.y = int32(coors.y - SCREEN_STRETCH_Y(particle->m_fSize * h)); rect.h = int32(coors.y + SCREEN_STRETCH_Y(particle->m_fSize * h)); } else { rect.h = int32(coors.y - SCREEN_STRETCH_Y(particle->m_fSize * h)); rect.y = int32(coors.y + SCREEN_STRETCH_Y(particle->m_fSize * h)); } float screenZ = (coors.z - CDraw::GetNearClipZ()) * (CSprite::GetFarScreenZ() - CSprite::GetNearScreenZ()) * CDraw::GetFarClipZ() / ( (CDraw::GetFarClipZ() - CDraw::GetNearClipZ()) * coors.z ) + CSprite::GetNearScreenZ(); CMBlur::AddRenderFx(Scene.camera, &rect, screenZ, FXTYPE_SPLASH1); } else { if ( particle->m_nRotation != 0 && i != PARTICLE_BEASTIE ) { CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z, particle->m_fSize * w, particle->m_fSize * h, particle->m_Color.red, particle->m_Color.green, particle->m_Color.blue, particle->m_nColorIntensity, 1.0f / coors.z, DEGTORAD((float)particle->m_nRotation), particle->m_nAlpha); } else if ( psystem->Flags & SCREEN_TRAIL ) { float fRotation; float fTrailLength; if ( particle->m_fZGround == 0.0f ) { fTrailLength = 0.0f; fRotation = 0.0f; } else { CVector2D vecDist ( coors.x - particle->m_fZGround, coors.y - particle->m_fExpansionRate ); float fDist = vecDist.Magnitude(); fTrailLength = fDist; float fRot = Asin(vecDist.x / fDist); fRotation = fRot; if ( vecDist.y < 0.0f ) fRotation = -1.0f * fRot + DEGTORAD(180.0f); float fSpeed = particle->m_vecVelocity.Magnitude(); float fNewTrailLength = fSpeed * CTimer::GetTimeStep() * w * 2.0f; if ( fDist > fNewTrailLength ) fTrailLength = fNewTrailLength; } CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z, particle->m_fSize * w, particle->m_fSize * h + fTrailLength * psystem->m_fTrailLengthMultiplier, particle->m_Color.red, particle->m_Color.green, particle->m_Color.blue, particle->m_nColorIntensity, 1.0f / coors.z, fRotation, particle->m_nAlpha); particle->m_fZGround = coors.x; // WTF ? particle->m_fExpansionRate = coors.y; // WTF ? } else if ( psystem->Flags & SPEED_TRAIL ) { CVector vecPrevPos = particle->m_vecPosition - particle->m_vecVelocity; float fRotation; float fTrailLength; CVector vecScreenPosition; if ( CSprite::CalcScreenCoors(vecPrevPos, &vecScreenPosition, &fTrailLength, &fRotation, true) ) { CVector2D vecDist ( coors.x - vecScreenPosition.x, coors.y - vecScreenPosition.y ); float fDist = vecDist.Magnitude(); fTrailLength = fDist; float fRot = Asin(vecDist.x / fDist); fRotation = fRot; if ( vecDist.y < 0.0f ) fRotation = -1.0f * fRot + DEGTORAD(180.0f); } else { fRotation = 0.0f; fTrailLength = 0.0f; } CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(coors.x, coors.y, coors.z, particle->m_fSize * w, particle->m_fSize * h + fTrailLength * psystem->m_fTrailLengthMultiplier, particle->m_Color.red, particle->m_Color.green, particle->m_Color.blue, particle->m_nColorIntensity, 1.0f / coors.z, fRotation, particle->m_nAlpha); } else if ( psystem->Flags & VERT_TRAIL ) { float fTrailLength = fabsf(particle->m_vecVelocity.z * 10.0f); CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z, particle->m_fSize * w, (particle->m_fSize + fTrailLength * psystem->m_fTrailLengthMultiplier) * h, particle->m_Color.red, particle->m_Color.green, particle->m_Color.blue, particle->m_nColorIntensity, 1.0f / coors.z, particle->m_nAlpha); } else if ( i == PARTICLE_RAINDROP_SMALL ) { CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z, particle->m_fSize * w * 0.05f, particle->m_fSize * h, particle->m_Color.red, particle->m_Color.green, particle->m_Color.blue, particle->m_nColorIntensity, 1.0f / coors.z, particle->m_nAlpha); } /*else if ( i == PARTICLE_BOAT_WAKE )*/ else { CSprite::RenderBufferedOneXLUSprite(coors.x, coors.y, coors.z, particle->m_fSize * w, particle->m_fSize * h, particle->m_Color.red, particle->m_Color.green, particle->m_Color.blue, particle->m_nColorIntensity, 1.0f / coors.z, particle->m_nAlpha); } } } } } particle = particle->m_pNext; } CSprite::FlushSpriteBuffer(); } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); POP_RENDERGROUP(); } void CParticle::RemovePSystem(tParticleType type) { tParticleSystemData *psystemdata = &mod_ParticleSystemManager.m_aParticles[type]; for ( CParticle *particle = psystemdata->m_pParticles; particle; particle = psystemdata->m_pParticles ) RemoveParticle(particle, nil, psystemdata); } void CParticle::RemoveParticle(CParticle *pParticle, CParticle *pPrevParticle, tParticleSystemData *pPSystemData) { if ( pPSystemData->m_Type == PARTICLE_WATERDROP ) --numWaterDropOnScreen; if ( pPrevParticle ) pPrevParticle->m_pNext = pParticle->m_pNext; else pPSystemData->m_pParticles = pParticle->m_pNext; pParticle->m_pNext = m_pUnusedListHead; m_pUnusedListHead = pParticle; } void CParticle::AddJetExplosion(CVector const &vecPos, float fPower, float fSize) { CRGBA color(240, 240, 240, 255); if ( fPower < 1.0f ) fPower = 1.0f; CVector vecRandOffset ( CGeneral::GetRandomNumberInRange(-0.4f, 0.4f), CGeneral::GetRandomNumberInRange(-0.4f, 0.4f), CGeneral::GetRandomNumberInRange(0.1f, 0.3f) ); vecRandOffset *= 2.0f; CVector vecStepPos = vecPos; for ( int32 i = 0; i < int32(fPower * 4.0f); i++ ) { AddParticle(PARTICLE_EXPLOSION_MFAST, vecStepPos, CVector ( CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), CGeneral::GetRandomNumberInRange(-0.02f, 0.0f) ), nil, fSize, color, 0, 0, 0, 0); AddParticle(PARTICLE_EXPLOSION_MFAST, vecStepPos, CVector ( CGeneral::GetRandomNumberInRange(-0.04f, 0.04f), CGeneral::GetRandomNumberInRange(-0.04f, 0.04f), CGeneral::GetRandomNumberInRange(0.0f, 0.07f) ), nil, fSize, color, 0, 0, 0, 0); AddParticle(PARTICLE_EXPLOSION_MFAST, vecStepPos, CVector ( CGeneral::GetRandomNumberInRange(-0.04f, 0.04f), CGeneral::GetRandomNumberInRange(-0.04f, 0.04f), CGeneral::GetRandomNumberInRange(0.0f, 0.07f) ), nil, fSize, color, 0, 0, 0, 0); vecStepPos += vecRandOffset; } } void CParticle::AddYardieDoorSmoke(CVector const &vecPos, CMatrix const &matMatrix) { CRGBA color(0, 0, 0, 0); CMatrix invMat(Invert(matMatrix)); CVector vecBasePos = matMatrix * (invMat * vecPos + CVector(0.0f, -1.0f, 0.5f)); for ( int32 i = 0; i < 5; i++ ) { CVector pos = vecBasePos; pos.x += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f); pos.y += CGeneral::GetRandomNumberInRange(-0.5f, 0.5f); AddParticle(PARTICLE_CARCOLLISION_DUST, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.3f, color, 0, 0, 0, 0); } } void CParticle::CalWindDir(CVector *vecDirIn, CVector *vecDirOut) { vecDirOut->x = (Cos(128) * vecDirIn->x) + (Sin(128) * vecDirIn->y); vecDirOut->x = (Cos(128) * vecDirIn->x) + (Sin(128) * vecDirIn->y) * CWeather::Wind; vecDirOut->y = (Sin(128) * vecDirIn->x) - (Cos(128) * vecDirIn->y) * CWeather::Wind; } void CParticle::HandleShipsAtHorizonStuff() { tParticleSystemData *psystemdata = &mod_ParticleSystemManager.m_aParticles[PARTICLE_SHIP_SIDE]; for ( CParticle *particle = psystemdata->m_pParticles; particle; particle = particle->m_pNext ) { if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed - 32000 && CTimer::GetTimeInMilliseconds() < particle->m_nTimeWhenWillBeDestroyed - 22000 ) { particle->m_nAlpha = Min(particle->m_nAlpha + 1, 96); } if ( CTimer::GetTimeInMilliseconds() > particle->m_nTimeWhenWillBeDestroyed - 10000 ) particle->m_nFadeAlphaTimer = 1; } } void CParticle::HandleShootableBirdsStuff(CEntity *entity, CVector const&camPos) { float fHeadingRad = entity->GetForward().Heading(); float fHeading = RADTODEG(fHeadingRad); float fBirdAngle = ::Cos(DEGTORAD(1.5f)); tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[PARTICLE_BIRD_FRONT]; CParticle *particle = psystem->m_pParticles; CParticle *prevParticle = nil; bool bRemoveParticle; for ( ; particle != nil; _Next(particle, prevParticle, psystem, bRemoveParticle) ) { bRemoveParticle = false; CVector2D vecPos(particle->m_vecPosition.x, particle->m_vecPosition.y); CVector2D vecCamPos(camPos.x, camPos.y); CVector2D vecDist = vecPos - vecCamPos; vecDist.Normalise(); float fHead = DEGTORAD(fHeading); CVector2D vecDir(-::Sin(fHead), ::Cos(fHead)); vecDir.Normalise(); float fDot = DotProduct2D(vecDir, vecDist); if ( fDot > 0.0f && fDot > fBirdAngle ) { if ( (camPos - particle->m_vecPosition).MagnitudeSqr() < 40000.0f ) { CStats::SeagullsKilled++; bRemoveParticle = true; for ( int32 i = 0; i < 8; i++ ) { CParticle *pBirdDerbis = AddParticle(PARTICLE_BIRD_DEBRIS, particle->m_vecPosition, CVector ( CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), CGeneral::GetRandomNumberInRange(-3.0f, 3.0f) ), nil, 0.3f, particle->m_Color, CGeneral::GetRandomNumberInRange(20, 40), 0, CGeneral::GetRandomNumber() & 3, 200); if ( pBirdDerbis ) pBirdDerbis->m_nAlpha = particle->m_nAlpha; } } } } } void CEntity::AddSteamsFromGround(CVector *unused) { int i, n; C2dEffect *effect; CVector pos; n = CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects(); for(i = 0; i < n; i++){ effect = CModelInfo::GetModelInfo(GetModelIndex())->Get2dEffect(i); if(effect->type != EFFECT_PARTICLE) continue; pos = GetMatrix() * effect->pos; switch(effect->particle.particleType){ case 0: CParticleObject::AddObject(POBJECT_PAVEMENT_STEAM, pos, effect->particle.dir, effect->particle.scale, false); break; case 1: CParticleObject::AddObject(POBJECT_WALL_STEAM, pos, effect->particle.dir, effect->particle.scale, false); break; case 2: CParticleObject::AddObject(POBJECT_DRY_ICE, pos, effect->particle.scale, false); break; case 3: CParticleObject::AddObject(POBJECT_SMALL_FIRE, pos, effect->particle.dir, effect->particle.scale, false); break; case 4: CParticleObject::AddObject(POBJECT_DARK_SMOKE, pos, effect->particle.dir, effect->particle.scale, false); break; case 5: CParticleObject::AddObject(POBJECT_WATER_FOUNTAIN_VERT, pos, effect->particle.dir, effect->particle.scale, false); break; case 6: CParticleObject::AddObject(POBJECT_WATER_FOUNTAIN_HORIZ, pos, effect->particle.dir, effect->particle.scale, false); break; } } } ================================================ FILE: src/render/Particle.h ================================================ #pragma once #include "ParticleMgr.h" class CEntity; class CParticle { public: enum { RAND_TABLE_SIZE = 20, SIN_COS_TABLE_SIZE = 1024 }; CVector m_vecPosition; CVector m_vecVelocity; uint32 m_nTimeWhenWillBeDestroyed; uint32 m_nTimeWhenColorWillBeChanged; float m_fZGround; CVector m_vecParticleMovementOffset; int16 m_nCurrentZRotation; uint16 m_nZRotationTimer; float m_fCurrentZRadius; uint16 m_nZRadiusTimer; uint8 m_nColorIntensity; uint8 m_nAlpha; float m_fSize; float m_fExpansionRate; int16 m_nFadeToBlackTimer; int16 m_nFadeAlphaTimer; int16 m_nAnimationSpeedTimer; int16 m_nRotationStep; int16 m_nRotation; uint8 m_nCurrentFrame; RwRGBA m_Color; CParticle *m_pNext; CParticle() { ; } ~CParticle() { ; } static float ms_afRandTable[RAND_TABLE_SIZE]; static CParticle *m_pUnusedListHead; static float m_SinTable[SIN_COS_TABLE_SIZE]; static float m_CosTable[SIN_COS_TABLE_SIZE]; static float Sin(int32 value) { return m_SinTable[value]; } static float Cos(int32 value) { return m_CosTable[value]; } static void ReloadConfig(); static void Initialise(); static void Shutdown(); static void AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity = nil, float fSize = 0.0f, int32 nRotationSpeed = 0, int32 nRotation = 0, int32 nCurFrame = 0, int32 nLifeSpan = 0); static void AddParticlesAlongLine(tParticleType type, CVector const &vecStart, CVector const &vecEnd, CVector const &vecDir, float fPower, CEntity *pEntity, float fSize, RwRGBA const&color, int32 nRotationSpeed = 0, int32 nRotation = 0, int32 nCurFrame = 0, int32 nLifeSpan = 0); static CParticle *AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity = nil, float fSize = 0.0f, int32 nRotationSpeed = 0, int32 nRotation = 0, int32 nCurFrame = 0, int32 nLifeSpan = 0); static CParticle *AddParticle(tParticleType type, CVector const &vecPos, CVector const &vecDir, CEntity *pEntity, float fSize, RwRGBA const &color, int32 nRotationSpeed = 0, int32 nRotation = 0, int32 nCurFrame = 0, int32 nLifeSpan = 0); static void Update(); static void Render(); static void RemovePSystem(tParticleType type); static void RemoveParticle(CParticle *pParticle, CParticle *pPrevParticle, tParticleSystemData *pPSystemData); static void _Next(CParticle *&pParticle, CParticle *&pPrevParticle, tParticleSystemData *pPSystemData, bool bRemoveParticle) { if ( bRemoveParticle ) { RemoveParticle(pParticle, pPrevParticle, pPSystemData); if ( pPrevParticle ) pParticle = pPrevParticle->m_pNext; else pParticle = pPSystemData->m_pParticles; } else { pPrevParticle = pParticle; pParticle = pParticle->m_pNext; } } static void AddJetExplosion(CVector const &vecPos, float fPower, float fSize); static void AddYardieDoorSmoke(CVector const &vecPos, CMatrix const &matMatrix); static void CalWindDir(CVector *vecDirIn, CVector *vecDirOut); static void HandleShipsAtHorizonStuff(); static void HandleShootableBirdsStuff(CEntity *entity, CVector const&camPos); }; extern bool clearWaterDrop; extern int32 numWaterDropOnScreen; extern RwRaster *gpCarSplashRaster[]; extern RwRaster *gpHeatHazeRaster; extern RwRaster *gpDotRaster; extern RwRaster *gpRainDripRaster[]; extern RwRaster *gpRainDripDarkRaster[]; VALIDATE_SIZE(CParticle, 0x58); ================================================ FILE: src/render/ParticleMgr.cpp ================================================ #include "common.h" #include "main.h" #include "FileMgr.h" #include "ParticleMgr.h" cParticleSystemMgr mod_ParticleSystemManager; const char *ParticleFilename = "PARTICLE.CFG"; cParticleSystemMgr::cParticleSystemMgr() { memset(this, 0, sizeof(*this)); } void cParticleSystemMgr::Initialise() { LoadParticleData(); for ( int32 i = 0; i < MAX_PARTICLES; i++ ) m_aParticles[i].m_pParticles = nil; } void cParticleSystemMgr::LoadParticleData() { CFileMgr::SetDir("DATA"); CFileMgr::LoadFile(ParticleFilename, work_buff, ARRAY_SIZE(work_buff), "r"); CFileMgr::SetDir(""); tParticleSystemData *entry = nil; int32 type = PARTICLE_FIRST; char *lineStart = (char *)work_buff; char *lineEnd = lineStart + 1; char line[500]; char delims[4]; while ( true ) { ASSERT(lineStart != nil); ASSERT(lineEnd != nil); while ( *lineEnd != '\n' ) ++lineEnd; int32 lineLength = lineEnd - lineStart; ASSERT(lineLength < 500); strncpy(line, lineStart, lineLength); line[lineLength] = '\0'; if ( !strcmp(line, ";the end") ) break; if ( *line != ';' ) { int32 param = CFG_PARAM_FIRST; strcpy(delims, " \t"); char *value = strtok(line, delims); ASSERT(value != nil); do { switch ( param ) { case CFG_PARAM_PARTICLE_TYPE_NAME: ASSERT(type < MAX_PARTICLES); entry = &m_aParticles[type]; ASSERT(entry != nil); entry->m_Type = (tParticleType)type++; strcpy(entry->m_aName, value); break; case CFG_PARAM_RENDER_COLOURING_R: entry->m_RenderColouring.red = atoi(value); break; case CFG_PARAM_RENDER_COLOURING_G: entry->m_RenderColouring.green = atoi(value); break; case CFG_PARAM_RENDER_COLOURING_B: entry->m_RenderColouring.blue = atoi(value); break; case CFG_PARAM_INITIAL_COLOR_VARIATION: entry->m_InitialColorVariation = Min(atoi(value), 100); break; case CFG_PARAM_FADE_DESTINATION_COLOR_R: entry->m_FadeDestinationColor.red = atoi(value); break; case CFG_PARAM_FADE_DESTINATION_COLOR_G: entry->m_FadeDestinationColor.green = atoi(value); break; case CFG_PARAM_FADE_DESTINATION_COLOR_B: entry->m_FadeDestinationColor.blue = atoi(value); break; case CFG_PARAM_COLOR_FADE_TIME: entry->m_ColorFadeTime = atoi(value); break; case CFG_PARAM_DEFAULT_INITIAL_RADIUS: entry->m_fDefaultInitialRadius = atof(value); break; case CFG_PARAM_EXPANSION_RATE: entry->m_fExpansionRate = atof(value); break; case CFG_PARAM_INITIAL_INTENSITY: entry->m_nFadeToBlackInitialIntensity = atoi(value); break; case CFG_PARAM_FADE_TIME: entry->m_nFadeToBlackTime = atoi(value); break; case CFG_PARAM_FADE_AMOUNT: entry->m_nFadeToBlackAmount = atoi(value); break; case CFG_PARAM_INITIAL_ALPHA_INTENSITY: entry->m_nFadeAlphaInitialIntensity = atoi(value); break; case CFG_PARAM_FADE_ALPHA_TIME: entry->m_nFadeAlphaTime = atoi(value); break; case CFG_PARAM_FADE_ALPHA_AMOUNT: entry->m_nFadeAlphaAmount = atoi(value); break; case CFG_PARAM_INITIAL_ANGLE: entry->m_nZRotationInitialAngle = atoi(value); break; case CFG_PARAM_CHANGE_TIME: entry->m_nZRotationChangeTime = atoi(value); break; case CFG_PARAM_ANGLE_CHANGE_AMOUNT: entry->m_nZRotationAngleChangeAmount = atoi(value); break; case CFG_PARAM_INITIAL_Z_RADIUS: entry->m_fInitialZRadius = atof(value); break; case CFG_PARAM_Z_RADIUS_CHANGE_TIME: entry->m_nZRadiusChangeTime = atoi(value); break; case CFG_PARAM_Z_RADIUS_CHANGE_AMOUNT: entry->m_fZRadiusChangeAmount = atof(value); break; case CFG_PARAM_ANIMATION_SPEED: entry->m_nAnimationSpeed = atoi(value); break; case CFG_PARAM_START_ANIMATION_FRAME: entry->m_nStartAnimationFrame = atoi(value); break; case CFG_PARAM_FINAL_ANIMATION_FRAME: entry->m_nFinalAnimationFrame = atoi(value); break; case CFG_PARAM_ROTATION_SPEED: entry->m_nRotationSpeed = atoi(value); break; case CFG_PARAM_GRAVITATIONAL_ACCELERATION: entry->m_fGravitationalAcceleration = atof(value); break; case CFG_PARAM_FRICTION_DECCELERATION: entry->m_nFrictionDecceleration = atoi(value); break; case CFG_PARAM_LIFE_SPAN: entry->m_nLifeSpan = atoi(value); break; case CFG_PARAM_POSITION_RANDOM_ERROR: entry->m_fPositionRandomError = atof(value); break; case CFG_PARAM_VELOCITY_RANDOM_ERROR: entry->m_fVelocityRandomError = atof(value); break; case CFG_PARAM_EXPANSION_RATE_ERROR: entry->m_fExpansionRateError = atof(value); break; case CFG_PARAM_ROTATION_RATE_ERROR: entry->m_nRotationRateError = atoi(value); break; case CFG_PARAM_LIFE_SPAN_ERROR_SHAPE: entry->m_nLifeSpanErrorShape = atoi(value); break; case CFG_PARAM_TRAIL_LENGTH_MULTIPLIER: entry->m_fTrailLengthMultiplier = atof(value); break; case CFG_PARAM_STRETCH_VALUE_X: entry->m_vecTextureStretch.x = atof(value); break; case CFG_PARAM_STRETCH_VALUE_Y: entry->m_vecTextureStretch.y = atof(value); break; case CFG_PARAM_WIND_FACTOR: entry->m_fWindFactor = atof(value); break; case CFG_PARAM_PARTICLE_CREATE_RANGE: entry->m_fCreateRange = SQR(atof(value)); break; case CFG_PARAM_FLAGS: entry->Flags = atoi(value); break; } value = strtok(nil, delims); param++; if ( param > CFG_PARAM_LAST ) param = CFG_PARAM_FIRST; } while ( value != nil ); } lineEnd++; lineStart = lineEnd; lineEnd++; } } ================================================ FILE: src/render/ParticleMgr.h ================================================ #pragma once #include "ParticleType.h" class CParticle; enum { ZCHECK_FIRST = BIT(0), ZCHECK_STEP = BIT(1), DRAW_OPAQUE = BIT(2), SCREEN_TRAIL = BIT(3), SPEED_TRAIL = BIT(4), RAND_VERT_V = BIT(5), CYCLE_ANIM = BIT(6), DRAW_DARK = BIT(7), VERT_TRAIL = BIT(8), _FLAG9 = BIT(9), // unused DRAWTOP2D = BIT(10), CLIPOUT2D = BIT(11), ZCHECK_BUMP = BIT(12), ZCHECK_BUMP_FIRST = BIT(13) }; struct tParticleSystemData { tParticleType m_Type; char m_aName[20]; float m_fCreateRange; float m_fDefaultInitialRadius; float m_fExpansionRate; uint16 m_nZRotationInitialAngle; int16 m_nZRotationAngleChangeAmount; uint16 m_nZRotationChangeTime; uint16 m_nZRadiusChangeTime; float m_fInitialZRadius; float m_fZRadiusChangeAmount; int16 m_nFadeToBlackTime; uint8 m_nFadeToBlackInitialIntensity; int16 m_nFadeToBlackAmount; uint8 m_nFadeAlphaInitialIntensity; int16 m_nFadeAlphaTime; int16 m_nFadeAlphaAmount; uint8 m_nStartAnimationFrame; uint8 m_nFinalAnimationFrame; uint16 m_nAnimationSpeed; uint16 m_nRotationSpeed; float m_fGravitationalAcceleration; int32 m_nFrictionDecceleration; int32 m_nLifeSpan; float m_fPositionRandomError; float m_fVelocityRandomError; float m_fExpansionRateError; int32 m_nRotationRateError; uint32 m_nLifeSpanErrorShape; float m_fTrailLengthMultiplier; uint32 Flags; CRGBA m_RenderColouring; uint8 m_InitialColorVariation; CRGBA m_FadeDestinationColor; uint32 m_ColorFadeTime; CVector2D m_vecTextureStretch; float m_fWindFactor; RwRaster **m_ppRaster; CParticle *m_pParticles; }; VALIDATE_SIZE(tParticleSystemData, 0x94); class cParticleSystemMgr { enum { CFG_PARAM_PARTICLE_TYPE_NAME = 0, CFG_PARAM_RENDER_COLOURING_R, CFG_PARAM_RENDER_COLOURING_G, CFG_PARAM_RENDER_COLOURING_B, CFG_PARAM_INITIAL_COLOR_VARIATION, CFG_PARAM_FADE_DESTINATION_COLOR_R, CFG_PARAM_FADE_DESTINATION_COLOR_G, CFG_PARAM_FADE_DESTINATION_COLOR_B, CFG_PARAM_COLOR_FADE_TIME, CFG_PARAM_DEFAULT_INITIAL_RADIUS, CFG_PARAM_EXPANSION_RATE, CFG_PARAM_INITIAL_INTENSITY, CFG_PARAM_FADE_TIME, CFG_PARAM_FADE_AMOUNT, CFG_PARAM_INITIAL_ALPHA_INTENSITY, CFG_PARAM_FADE_ALPHA_TIME, CFG_PARAM_FADE_ALPHA_AMOUNT, CFG_PARAM_INITIAL_ANGLE, CFG_PARAM_CHANGE_TIME, CFG_PARAM_ANGLE_CHANGE_AMOUNT, CFG_PARAM_INITIAL_Z_RADIUS, CFG_PARAM_Z_RADIUS_CHANGE_TIME, CFG_PARAM_Z_RADIUS_CHANGE_AMOUNT, CFG_PARAM_ANIMATION_SPEED, CFG_PARAM_START_ANIMATION_FRAME, CFG_PARAM_FINAL_ANIMATION_FRAME, CFG_PARAM_ROTATION_SPEED, CFG_PARAM_GRAVITATIONAL_ACCELERATION, CFG_PARAM_FRICTION_DECCELERATION, CFG_PARAM_LIFE_SPAN, CFG_PARAM_POSITION_RANDOM_ERROR, CFG_PARAM_VELOCITY_RANDOM_ERROR, CFG_PARAM_EXPANSION_RATE_ERROR, CFG_PARAM_ROTATION_RATE_ERROR, CFG_PARAM_LIFE_SPAN_ERROR_SHAPE, CFG_PARAM_TRAIL_LENGTH_MULTIPLIER, CFG_PARAM_STRETCH_VALUE_X, CFG_PARAM_STRETCH_VALUE_Y, CFG_PARAM_WIND_FACTOR, CFG_PARAM_PARTICLE_CREATE_RANGE, CFG_PARAM_FLAGS, MAX_CFG_PARAMS, CFG_PARAM_FIRST = CFG_PARAM_PARTICLE_TYPE_NAME, CFG_PARAM_LAST = CFG_PARAM_FLAGS }; public: tParticleSystemData m_aParticles[MAX_PARTICLES]; cParticleSystemMgr(); void Initialise(); void LoadParticleData(); void RangeCheck(tParticleSystemData *pData) { } }; VALIDATE_SIZE(cParticleSystemMgr, 0x2FFC); extern cParticleSystemMgr mod_ParticleSystemManager; ================================================ FILE: src/render/ParticleType.h ================================================ #pragma once enum tParticleType { PARTICLE_SPARK = 0, PARTICLE_SPARK_SMALL, PARTICLE_WATER_SPARK, PARTICLE_WHEEL_DIRT, PARTICLE_SAND, PARTICLE_WHEEL_WATER, PARTICLE_BLOOD, PARTICLE_BLOOD_SMALL, PARTICLE_BLOOD_SPURT, PARTICLE_DEBRIS, PARTICLE_DEBRIS2, PARTICLE_FLYERS, PARTICLE_WATER, PARTICLE_FLAME, PARTICLE_FIREBALL, PARTICLE_GUNFLASH, PARTICLE_GUNFLASH_NOANIM, PARTICLE_GUNSMOKE, PARTICLE_GUNSMOKE2, PARTICLE_CIGARETTE_SMOKE, PARTICLE_SMOKE, PARTICLE_SMOKE_SLOWMOTION, PARTICLE_DRY_ICE, PARTICLE_TEARGAS, PARTICLE_GARAGEPAINT_SPRAY, PARTICLE_SHARD, PARTICLE_SPLASH, PARTICLE_CARFLAME, PARTICLE_STEAM, PARTICLE_STEAM2, PARTICLE_STEAM_NY, PARTICLE_STEAM_NY_SLOWMOTION, PARTICLE_GROUND_STEAM, PARTICLE_ENGINE_STEAM, PARTICLE_RAINDROP, PARTICLE_RAINDROP_SMALL, PARTICLE_RAIN_SPLASH, PARTICLE_RAIN_SPLASH_BIGGROW, PARTICLE_RAIN_SPLASHUP, PARTICLE_WATERSPRAY, PARTICLE_WATERDROP, PARTICLE_BLOODDROP, PARTICLE_EXPLOSION_MEDIUM, PARTICLE_EXPLOSION_LARGE, PARTICLE_EXPLOSION_MFAST, PARTICLE_EXPLOSION_LFAST, PARTICLE_CAR_SPLASH, PARTICLE_BOAT_SPLASH, PARTICLE_BOAT_THRUSTJET, PARTICLE_WATER_HYDRANT, PARTICLE_WATER_CANNON, PARTICLE_EXTINGUISH_STEAM, PARTICLE_PED_SPLASH, PARTICLE_PEDFOOT_DUST, PARTICLE_CAR_DUST, PARTICLE_HELI_DUST, PARTICLE_HELI_ATTACK, PARTICLE_ENGINE_SMOKE, PARTICLE_ENGINE_SMOKE2, PARTICLE_CARFLAME_SMOKE, PARTICLE_FIREBALL_SMOKE, PARTICLE_PAINT_SMOKE, PARTICLE_TREE_LEAVES, PARTICLE_CARCOLLISION_DUST, PARTICLE_CAR_DEBRIS, PARTICLE_BIRD_DEBRIS, PARTICLE_HELI_DEBRIS, PARTICLE_EXHAUST_FUMES, PARTICLE_RUBBER_SMOKE, PARTICLE_BURNINGRUBBER_SMOKE, PARTICLE_BULLETHIT_SMOKE, PARTICLE_GUNSHELL_FIRST, PARTICLE_GUNSHELL, PARTICLE_GUNSHELL_BUMP1, PARTICLE_GUNSHELL_BUMP2, PARTICLE_ROCKET_SMOKE, PARTICLE_TEST, PARTICLE_BIRD_FRONT, PARTICLE_SHIP_SIDE, PARTICLE_BEASTIE, PARTICLE_RAINDROP_2D, PARTICLE_HEATHAZE, PARTICLE_HEATHAZE_IN_DIST, MAX_PARTICLES, PARTICLE_FIRST = PARTICLE_SPARK, PARTICLE_LAST = PARTICLE_HEATHAZE_IN_DIST }; ================================================ FILE: src/render/PlayerSkin.cpp ================================================ #include "common.h" #include "main.h" #include "PlayerSkin.h" #include "TxdStore.h" #include "rtbmp.h" #include "ClumpModelInfo.h" #include "VisibilityPlugins.h" #include "World.h" #include "PlayerInfo.h" #include "CdStream.h" #include "FileMgr.h" #include "Directory.h" #include "RwHelper.h" #include "Timer.h" #include "Lights.h" #include "MemoryMgr.h" RpClump *gpPlayerClump; float gOldFov; int CPlayerSkin::m_txdSlot; void FindPlayerDff(uint32 &offset, uint32 &size) { int file; CDirectory::DirectoryInfo info; file = CFileMgr::OpenFile("models\\gta3.dir", "rb"); do { if (!CFileMgr::Read(file, (char*)&info, sizeof(CDirectory::DirectoryInfo))) return; } while (strcasecmp("player.dff", info.name) != 0); offset = info.offset; size = info.size; } void LoadPlayerDff(void) { RwStream *stream; RwMemory mem; uint32 offset, size; uint8 *buffer; bool streamWasAdded = false; if (CdStreamGetNumImages() == 0) { CdStreamAddImage("models\\gta3.img"); streamWasAdded = true; } FindPlayerDff(offset, size); buffer = (uint8*)RwMallocAlign(size << 11, 2048); CdStreamRead(0, buffer, offset, size); CdStreamSync(0); mem.start = buffer; mem.length = size << 11; stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); if (RwStreamFindChunk(stream, rwID_CLUMP, nil, nil)) gpPlayerClump = RpClumpStreamRead(stream); RwStreamClose(stream, &mem); RwFreeAlign(buffer); if (streamWasAdded) CdStreamRemoveImages(); } void CPlayerSkin::Initialise(void) { // empty on PS2 m_txdSlot = CTxdStore::AddTxdSlot("skin"); CTxdStore::Create(m_txdSlot); CTxdStore::AddRef(m_txdSlot); } void CPlayerSkin::Shutdown(void) { // empty on PS2 CTxdStore::RemoveTxdSlot(m_txdSlot); } RwTexture * CPlayerSkin::GetSkinTexture(const char *texName) { RwTexture *tex; RwRaster *raster; int32 width, height, depth, format; CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(m_txdSlot); tex = RwTextureRead(texName, NULL); CTxdStore::PopCurrentTxd(); if (tex != nil) return tex; if (strcmp(DEFAULT_SKIN_NAME, texName) == 0 || texName[0] == '\0') sprintf(gString, "models\\generic\\player.bmp"); else sprintf(gString, "skins\\%s.bmp", texName); if (RwImage *image = RtBMPImageRead(gString)) { RwImageFindRasterFormat(image, rwRASTERTYPETEXTURE, &width, &height, &depth, &format); raster = RwRasterCreate(width, height, depth, format); RwRasterSetFromImage(raster, image); tex = RwTextureCreate(raster); RwTextureSetName(tex, texName); RwTextureSetFilterMode(tex, rwFILTERLINEAR); RwTexDictionaryAddTexture(CTxdStore::GetSlot(m_txdSlot)->texDict, tex); RwImageDestroy(image); } return tex; } void CPlayerSkin::BeginFrontendSkinEdit(void) { LoadPlayerDff(); RpClumpForAllAtomics(gpPlayerClump, CClumpModelInfo::SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB); CWorld::Players[0].LoadPlayerSkin(); gOldFov = CDraw::GetFOV(); CDraw::SetFOV(30.0f); } void CPlayerSkin::EndFrontendSkinEdit(void) { RpClumpDestroy(gpPlayerClump); gpPlayerClump = NULL; CDraw::SetFOV(gOldFov); } void CPlayerSkin::RenderFrontendSkinEdit(void) { static float rotation = 0.0f; RwRGBAReal AmbientColor = { 0.65f, 0.65f, 0.65f, 1.0f }; const RwV3d pos = { 1.35f, 0.35f, 7.725f }; const RwV3d axis = { 0.0f, 1.0f, 0.0f }; static uint32 LastFlash = 0; RwFrame *frame = RpClumpGetFrame(gpPlayerClump); if (CTimer::GetTimeInMillisecondsPauseMode() - LastFlash > 7) { rotation += 2.0f; if (rotation > 360.0f) rotation -= 360.0f; LastFlash = CTimer::GetTimeInMillisecondsPauseMode(); } RwFrameTransform(frame, RwFrameGetMatrix(RwCameraGetFrame(Scene.camera)), rwCOMBINEREPLACE); RwFrameTranslate(frame, &pos, rwCOMBINEPRECONCAT); RwFrameRotate(frame, &axis, rotation, rwCOMBINEPRECONCAT); RwFrameUpdateObjects(frame); SetAmbientColours(&AmbientColor); RpClumpRender(gpPlayerClump); } ================================================ FILE: src/render/PlayerSkin.h ================================================ #pragma once #define DEFAULT_SKIN_NAME "$$\"\"" class CPlayerSkin { static int m_txdSlot; public: static void Initialise(); static void Shutdown(); static RwTexture *GetSkinTexture(const char *texName); static void BeginFrontendSkinEdit(); static void EndFrontendSkinEdit(); static void RenderFrontendSkinEdit(); }; ================================================ FILE: src/render/PointLights.cpp ================================================ #include "common.h" #include "main.h" #include "CutsceneMgr.h" #include "Lights.h" #include "Camera.h" #include "Weather.h" #include "World.h" #include "Collision.h" #include "Sprite.h" #include "Timer.h" #include "PointLights.h" int16 CPointLights::NumLights; CRegisteredPointLight CPointLights::aLights[NUMPOINTLIGHTS]; CVector CPointLights::aCachedMapReads[32]; float CPointLights::aCachedMapReadResults[32]; int32 CPointLights::NextCachedValue; void CPointLights::Init(void) { for(int i = 0; i < ARRAY_SIZE(aCachedMapReads); i++){ aCachedMapReads[i] = CVector(0.0f, 0.0f, 0.0f); aCachedMapReadResults[i] = 0.0f; } NextCachedValue = 0; } void CPointLights::InitPerFrame(void) { NumLights = 0; } #define MAX_DIST 22.0f void CPointLights::AddLight(uint8 type, CVector coors, CVector dir, float radius, float red, float green, float blue, uint8 fogType, bool castExtraShadows) { CVector dist; float distance; // The check is done in some weird way in the game // we're doing it a bit better here if(NumLights >= NUMPOINTLIGHTS) return; dist = coors - TheCamera.GetPosition(); if(Abs(dist.x) < MAX_DIST && Abs(dist.y) < MAX_DIST){ distance = dist.Magnitude(); if(distance < MAX_DIST){ aLights[NumLights].type = type; aLights[NumLights].fogType = fogType; aLights[NumLights].coors = coors; aLights[NumLights].dir = dir; aLights[NumLights].radius = radius; aLights[NumLights].castExtraShadows = castExtraShadows; if(distance < MAX_DIST*0.75f){ aLights[NumLights].red = red; aLights[NumLights].green = green; aLights[NumLights].blue = blue; }else{ float fade = 1.0f - (distance/MAX_DIST - 0.75f)*4.0f; aLights[NumLights].red = red * fade; aLights[NumLights].green = green * fade; aLights[NumLights].blue = blue * fade; } NumLights++; } } } float CPointLights::GenerateLightsAffectingObject(Const CVector *objCoors) { int i; float ret; CVector dist; float radius, distance; ret = 1.0f; for(i = 0; i < NumLights; i++){ if(aLights[i].type == LIGHT_FOGONLY || aLights[i].type == LIGHT_FOGONLY_ALWAYS) continue; // same weird distance calculation. simplified here dist = aLights[i].coors - *objCoors; radius = aLights[i].radius; if(Abs(dist.x) < radius && Abs(dist.y) < radius && Abs(dist.z) < radius){ distance = dist.Magnitude(); if(distance < radius){ float distNorm = distance/radius; if(aLights[i].type == LIGHT_DARKEN){ // darken the object the closer it is ret *= distNorm; }else{ float intensity; // distance fade if(distNorm < 0.5f) intensity = 1.0f; else intensity = 1.0f - (distNorm - 0.5f)/(1.0f - 0.5f); if(distance != 0.0f){ CVector dir = dist / distance; if(aLights[i].type == LIGHT_DIRECTIONAL){ float dot = -DotProduct(dir, aLights[i].dir); intensity *= Max((dot-0.5f)*2.0f, 0.0f); } if(intensity > 0.0f) AddAnExtraDirectionalLight(Scene.world, dir.x, dir.y, dir.z, aLights[i].red*intensity, aLights[i].green*intensity, aLights[i].blue*intensity); } } } } } return ret; } extern RwRaster *gpPointlightRaster; void CPointLights::RemoveLightsAffectingObject(void) { RemoveExtraDirectionalLights(Scene.world); } // for directional fog #define FOG_AREA_LENGTH 12.0f #define FOG_AREA_WIDTH 5.0f // for pointlight fog #define FOG_AREA_RADIUS 9.0f float FogSizes[8] = { 1.3f, 2.0f, 1.7f, 2.0f, 1.4f, 2.1f, 1.5f, 2.3f }; void CPointLights::RenderFogEffect(void) { int i; float fogginess; CColPoint point; CEntity *entity; float xmin, ymin; float xmax, ymax; int16 xi, yi; CVector spriteCoors; float spritew, spriteh; if(CCutsceneMgr::IsRunning()) return; PUSH_RENDERGROUP("CPointLights::RenderFogEffect"); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpPointlightRaster); CSprite::InitSpriteBuffer(); for(i = 0; i < NumLights; i++){ if(aLights[i].fogType != FOG_NORMAL && aLights[i].fogType != FOG_ALWAYS) continue; fogginess = aLights[i].fogType == FOG_NORMAL ? CWeather::Foggyness : 1.0f; if(fogginess == 0.0f) continue; if(aLights[i].type == LIGHT_DIRECTIONAL){ // TODO: test this. haven't found directional fog so far float coors2X = aLights[i].coors.x + FOG_AREA_LENGTH*aLights[i].dir.x; float coors2Y = aLights[i].coors.y + FOG_AREA_LENGTH*aLights[i].dir.y; if(coors2X < aLights[i].coors.x){ xmin = coors2X; xmax = aLights[i].coors.x; }else{ xmax = coors2X; xmin = aLights[i].coors.x; } if(coors2Y < aLights[i].coors.y){ ymin = coors2Y; ymax = aLights[i].coors.y; }else{ ymax = coors2Y; ymin = aLights[i].coors.y; } xmin -= 5.0f; ymin -= 5.0f; xmax += 5.0f; ymax += 5.0f; for(xi = (int16)xmin - (int16)xmin % 4; xi <= (int16)xmax + 4; xi += 4){ for(yi = (int16)ymin - (int16)ymin % 4; yi <= (int16)ymax + 4; yi += 4){ // Some kind of pseudo random number? int r = (xi ^ yi)>>2 & 0xF; if((r & 1) == 0) continue; // Check if fog effect is close enough to directional line in x and y float dx = xi - aLights[i].coors.x; float dy = yi - aLights[i].coors.y; float dot = dx*aLights[i].dir.x + dy*aLights[i].dir.y; float distsq = sq(dx) + sq(dy); float linedistsq = distsq - sq(dot); if(dot > 0.0f && dot < FOG_AREA_LENGTH && linedistsq < sq(FOG_AREA_WIDTH)){ CVector fogcoors(xi, yi, aLights[i].coors.z + 10.0f); if(CWorld::ProcessVerticalLine(fogcoors, fogcoors.z - 20.0f, point, entity, true, false, false, false, true, false, nil)){ // Now same check again in xyz fogcoors.z = point.point.z + 1.3f; // actually we don't have to recalculate x and y, but the game does it that way dx = xi - aLights[i].coors.x; dy = yi - aLights[i].coors.y; float dz = fogcoors.z - aLights[i].coors.z; dot = dx*aLights[i].dir.x + dy*aLights[i].dir.y + dz*aLights[i].dir.z; distsq = sq(dx) + sq(dy) + sq(dz); linedistsq = distsq - sq(dot); if(dot > 0.0f && dot < FOG_AREA_LENGTH && linedistsq < sq(FOG_AREA_WIDTH)){ float intensity = 158.0f * fogginess; // more intensity the smaller the angle intensity *= dot/Sqrt(distsq); // more intensity the closer to light source intensity *= 1.0f - sq(dot/FOG_AREA_LENGTH); // more intensity the closer to line intensity *= 1.0f - sq(Sqrt(linedistsq) / FOG_AREA_WIDTH); if(CSprite::CalcScreenCoors(fogcoors, &spriteCoors, &spritew, &spriteh, true)) { float rotation = (CTimer::GetTimeInMilliseconds()&0x1FFF) * 2*3.14f / 0x2000; float size = FogSizes[r>>1]; CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(spriteCoors.x, spriteCoors.y, spriteCoors.z, spritew * size, spriteh * size, aLights[i].red * intensity, aLights[i].green * intensity, aLights[i].blue * intensity, intensity, 1/spriteCoors.z, rotation, 255); } } } } } } }else if(aLights[i].type == LIGHT_POINT || aLights[i].type == LIGHT_FOGONLY || aLights[i].type == LIGHT_FOGONLY_ALWAYS){ float groundZ; if(ProcessVerticalLineUsingCache(aLights[i].coors, &groundZ)){ xmin = aLights[i].coors.x - FOG_AREA_RADIUS; ymin = aLights[i].coors.y - FOG_AREA_RADIUS; xmax = aLights[i].coors.x + FOG_AREA_RADIUS; ymax = aLights[i].coors.y + FOG_AREA_RADIUS; for(xi = (int16)xmin - (int16)xmin % 2; xi <= (int16)xmax + 2; xi += 2){ for(yi = (int16)ymin - (int16)ymin % 2; yi <= (int16)ymax + 2; yi += 2){ // Some kind of pseudo random number? int r = (xi ^ yi)>>1 & 0xF; if((r & 1) == 0) continue; float dx = xi - aLights[i].coors.x; float dy = yi - aLights[i].coors.y; float lightdist = Sqrt(sq(dx) + sq(dy)); if(lightdist < FOG_AREA_RADIUS){ dx = xi - TheCamera.GetPosition().x; dy = yi - TheCamera.GetPosition().y; float camdist = Sqrt(sq(dx) + sq(dy)); if(camdist < MAX_DIST){ float intensity; // distance fade if(camdist < MAX_DIST/2) intensity = 1.0f; else intensity = 1.0f - (camdist - MAX_DIST/2) / (MAX_DIST/2); intensity *= 132.0f * fogginess; // more intensity the closer to light source intensity *= 1.0f - sq(lightdist / FOG_AREA_RADIUS); CVector fogcoors(xi, yi, groundZ + 1.6f); if(CSprite::CalcScreenCoors(fogcoors, &spriteCoors, &spritew, &spriteh, true)) { float rotation = (CTimer::GetTimeInMilliseconds()&0x3FFF) * 2*3.14f / 0x4000; float size = FogSizes[r>>1]; CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(spriteCoors.x, spriteCoors.y, spriteCoors.z, spritew * size, spriteh * size, aLights[i].red * intensity, aLights[i].green * intensity, aLights[i].blue * intensity, intensity, 1/spriteCoors.z, rotation, 255); } } } } } } } } CSprite::FlushSpriteBuffer(); POP_RENDERGROUP(); } bool CPointLights::ProcessVerticalLineUsingCache(CVector coors, float *groundZ) { for(int i = 0; i < ARRAY_SIZE(aCachedMapReads); i++) if(aCachedMapReads[i] == coors){ *groundZ = aCachedMapReadResults[i]; return true; } CColPoint point; CEntity *entity; if(CWorld::ProcessVerticalLine(coors, coors.z - 20.0f, point, entity, true, false, false, false, true, false, nil)){ aCachedMapReads[NextCachedValue] = coors; aCachedMapReadResults[NextCachedValue] = point.point.z; NextCachedValue = (NextCachedValue+1) % ARRAY_SIZE(aCachedMapReads); *groundZ = point.point.z; return true; } return false; } ================================================ FILE: src/render/PointLights.h ================================================ #pragma once class CRegisteredPointLight { public: CVector coors; CVector dir; float radius; float red; float green; float blue; int8 type; int8 fogType; bool castExtraShadows; }; VALIDATE_SIZE(CRegisteredPointLight, 0x2C); class CPointLights { public: static int16 NumLights; static CRegisteredPointLight aLights[NUMPOINTLIGHTS]; static CVector aCachedMapReads[32]; static float aCachedMapReadResults[32]; static int32 NextCachedValue; enum { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_DARKEN, // no effects at all // these have only fog, otherwise no difference? // only used by CEntity::ProcessLightsForEntity it seems // and there used together with fog type LIGHT_FOGONLY_ALWAYS, LIGHT_FOGONLY, }; enum { FOG_NONE, FOG_NORMAL, // taken from Foggyness FOG_ALWAYS }; static void Init(void); static void InitPerFrame(void); static void AddLight(uint8 type, CVector coors, CVector dir, float radius, float red, float green, float blue, uint8 fogType, bool castExtraShadows); static float GenerateLightsAffectingObject(Const CVector *objCoors); static void RemoveLightsAffectingObject(void); static void RenderFogEffect(void); static bool ProcessVerticalLineUsingCache(CVector coors, float *groundZ); }; ================================================ FILE: src/render/RenderBuffer.cpp ================================================ #include "common.h" #include "RenderBuffer.h" int32 TempBufferVerticesStored; int32 TempBufferIndicesStored; VertexBufferUnion TempVertexBuffer; RwImVertexIndex TempBufferRenderIndexList[TEMPBUFFERINDEXSIZE]; int RenderBuffer::VerticesToBeStored; int RenderBuffer::IndicesToBeStored; void RenderBuffer::ClearRenderBuffer(void) { TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; } void RenderBuffer::StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, RwIm3DVertex **vertexStart) { if(TempBufferIndicesStored + numIndices >= TEMPBUFFERINDEXSIZE) RenderStuffInBuffer(); if(TempBufferVerticesStored + numVertices >= TEMPBUFFERVERTSIZE) RenderStuffInBuffer(); *indexStart = &TempBufferRenderIndexList[TempBufferIndicesStored]; *vertexStart = &TempBufferRenderVertices[TempBufferVerticesStored]; IndicesToBeStored = numIndices; VerticesToBeStored = numVertices; } void RenderBuffer::StopStoring(void) { int i; for(i = TempBufferIndicesStored; i < TempBufferIndicesStored+IndicesToBeStored; i++) TempBufferRenderIndexList[i] += TempBufferVerticesStored; TempBufferIndicesStored += IndicesToBeStored; TempBufferVerticesStored += VerticesToBeStored; } void RenderBuffer::RenderStuffInBuffer(void) { if(TempBufferVerticesStored && RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } ClearRenderBuffer(); } ================================================ FILE: src/render/RenderBuffer.h ================================================ class RenderBuffer { public: static int VerticesToBeStored; static int IndicesToBeStored; static void ClearRenderBuffer(void); static void StartStoring(int numIndices, int numVertices, RwImVertexIndex **indexStart, RwIm3DVertex **vertexStart); static void StopStoring(void); static void RenderStuffInBuffer(void); }; #define TEMPBUFFERVERTSIZE 512 #define TEMPBUFFERINDEXSIZE 1024 struct VertexBufferUnion { RwIm2DVertex im2d[TEMPBUFFERVERTSIZE]; RwIm3DVertex im3d[TEMPBUFFERVERTSIZE]; }; extern int32 TempBufferVerticesStored; extern int32 TempBufferIndicesStored; extern VertexBufferUnion TempVertexBuffer; #define TempBufferRenderVertices (TempVertexBuffer.im3d) extern RwImVertexIndex TempBufferRenderIndexList[TEMPBUFFERINDEXSIZE]; ================================================ FILE: src/render/Renderer.cpp ================================================ #define WITHD3D #include "common.h" #include "main.h" #include "Lights.h" #include "ModelInfo.h" #include "Treadable.h" #include "Ped.h" #include "Vehicle.h" #include "Boat.h" #include "Heli.h" #include "Bike.h" #include "Object.h" #include "PathFind.h" #include "Collision.h" #include "VisibilityPlugins.h" #include "Clock.h" #include "World.h" #include "Camera.h" #include "ModelIndices.h" #include "Streaming.h" #include "Shadows.h" #include "PointLights.h" #include "Occlusion.h" #include "Renderer.h" #include "custompipes.h" #include "Frontend.h" bool gbShowPedRoadGroups; bool gbShowCarRoadGroups; bool gbShowCollisionPolys; bool gbShowCollisionLines; bool gbBigWhiteDebugLightSwitchedOn; bool gbDontRenderBuildings; bool gbDontRenderBigBuildings; bool gbDontRenderPeds; bool gbDontRenderObjects; bool gbDontRenderVehicles; // unused int16 TestCloseThings; int16 TestBigThings; struct EntityInfo { CEntity *ent; float sort; }; CLinkList gSortedVehiclesAndPeds; int32 CRenderer::ms_nNoOfVisibleEntities; CEntity *CRenderer::ms_aVisibleEntityPtrs[NUMVISIBLEENTITIES]; CEntity *CRenderer::ms_aInVisibleEntityPtrs[NUMINVISIBLEENTITIES]; int32 CRenderer::ms_nNoOfInVisibleEntities; #ifdef NEW_RENDERER int32 CRenderer::ms_nNoOfVisibleVehicles; CEntity *CRenderer::ms_aVisibleVehiclePtrs[NUMVISIBLEENTITIES]; int32 CRenderer::ms_nNoOfVisibleBuildings; CEntity *CRenderer::ms_aVisibleBuildingPtrs[NUMVISIBLEENTITIES]; #endif CVector CRenderer::ms_vecCameraPosition; CVehicle *CRenderer::m_pFirstPersonVehicle; bool CRenderer::m_loadingPriority; float CRenderer::ms_lodDistScale = 1.2f; // unused BlockedRange CRenderer::aBlockedRanges[16]; BlockedRange* CRenderer::pFullBlockedRanges; BlockedRange* CRenderer::pEmptyBlockedRanges; void CRenderer::Init(void) { gSortedVehiclesAndPeds.Init(40); SortBIGBuildings(); } void CRenderer::Shutdown(void) { gSortedVehiclesAndPeds.Shutdown(); } void CRenderer::PreRender(void) { int i; CLink *node; for(i = 0; i < ms_nNoOfVisibleEntities; i++) ms_aVisibleEntityPtrs[i]->PreRender(); #ifdef NEW_RENDERER if(gbNewRenderer){ for(i = 0; i < ms_nNoOfVisibleVehicles; i++) ms_aVisibleVehiclePtrs[i]->PreRender(); // How is this done with cWorldStream? for(i = 0; i < ms_nNoOfVisibleBuildings; i++) ms_aVisibleBuildingPtrs[i]->PreRender(); for(node = CVisibilityPlugins::m_alphaBuildingList.head.next; node != &CVisibilityPlugins::m_alphaBuildingList.tail; node = node->next) ((CEntity*)node->item.entity)->PreRender(); } #endif for (i = 0; i < ms_nNoOfInVisibleEntities; i++) { #ifdef SQUEEZE_PERFORMANCE if (ms_aInVisibleEntityPtrs[i]->IsVehicle() && ((CVehicle*)ms_aInVisibleEntityPtrs[i])->IsHeli()) #endif ms_aInVisibleEntityPtrs[i]->PreRender(); } for(node = CVisibilityPlugins::m_alphaEntityList.head.next; node != &CVisibilityPlugins::m_alphaEntityList.tail; node = node->next) ((CEntity*)node->item.entity)->PreRender(); CHeli::SpecialHeliPreRender(); CShadows::RenderExtraPlayerShadows(); } void CRenderer::RenderOneRoad(CEntity *e) { if(gbDontRenderBuildings) return; if(gbShowCollisionPolys) CCollision::DrawColModel_Coloured(e->GetMatrix(), *CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel(), e->GetModelIndex()); else{ PUSH_RENDERGROUP(CModelInfo::GetModelInfo(e->GetModelIndex())->GetModelName()); e->Render(); POP_RENDERGROUP(); } } void CRenderer::RenderOneNonRoad(CEntity *e) { CPed *ped; CVehicle *veh; int i; bool resetLights; #ifndef MASTER if(gbShowCollisionPolys){ if(!e->IsVehicle()){ CCollision::DrawColModel_Coloured(e->GetMatrix(), *CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel(), e->GetModelIndex()); return; } }else if(e->IsBuilding()){ if(e->bIsBIGBuilding){ if(gbDontRenderBigBuildings) return; }else{ if(gbDontRenderBuildings) return; } }else #endif if(e->IsPed()){ #ifndef MASTER if(gbDontRenderPeds) return; #endif ped = (CPed*)e; if(ped->m_nPedState == PED_DRIVING) return; } #ifndef MASTER else if(e->IsObject() || e->IsDummy()){ if(gbDontRenderObjects) return; }else if(e->IsVehicle()){ // re3 addition if(gbDontRenderVehicles) return; } #endif PUSH_RENDERGROUP(CModelInfo::GetModelInfo(e->GetModelIndex())->GetModelName()); resetLights = e->SetupLighting(); if(e->IsVehicle()){ // unfortunately can't use GetClump here CVisibilityPlugins::SetupVehicleVariables((RpClump*)e->m_rwObject); CVisibilityPlugins::InitAlphaAtomicList(); } // Render Peds in vehicle before vehicle itself if(e->IsVehicle()){ veh = (CVehicle*)e; if(veh->pDriver && veh->pDriver->m_nPedState == PED_DRIVING) veh->pDriver->Render(); for(i = 0; i < 8; i++) if(veh->pPassengers[i] && veh->pPassengers[i]->m_nPedState == PED_DRIVING) veh->pPassengers[i]->Render(); SetCullMode(rwCULLMODECULLNONE); } e->Render(); if(e->IsVehicle()){ e->bImBeingRendered = true; CVisibilityPlugins::RenderAlphaAtomics(); e->bImBeingRendered = false; SetCullMode(rwCULLMODECULLBACK); } e->RemoveLighting(resetLights); POP_RENDERGROUP(); } void CRenderer::RenderFirstPersonVehicle(void) { if(m_pFirstPersonVehicle == nil) return; RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RenderOneNonRoad(m_pFirstPersonVehicle); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); } inline bool IsRoad(CEntity *e) { return e->IsBuilding() && ((CSimpleModelInfo*)CModelInfo::GetModelInfo(e->GetModelIndex()))->m_wetRoadReflection; } void CRenderer::RenderRoads(void) { int i; CEntity *e; PUSH_RENDERGROUP("CRenderer::RenderRoads"); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); DeActivateDirectional(); SetAmbientColours(); for(i = 0; i < ms_nNoOfVisibleEntities; i++){ e = ms_aVisibleEntityPtrs[i]; if(IsRoad(e)) RenderOneRoad(e); } POP_RENDERGROUP(); } inline bool PutIntoSortedVehicleList(CVehicle *veh) { if(veh->IsBoat()){ int mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if(mode == CCam::MODE_WHEELCAM || mode == CCam::MODE_1STPERSON && TheCamera.GetLookDirection() != LOOKING_FORWARD && TheCamera.GetLookDirection() != LOOKING_BEHIND || CVisibilityPlugins::GetClumpAlpha(veh->GetClump()) != 255) return false; return true; }else return veh->bTouchingWater; } void CRenderer::RenderEverythingBarRoads(void) { int i; CEntity *e; EntityInfo ei; PUSH_RENDERGROUP("CRenderer::RenderEverythingBarRoads"); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); gSortedVehiclesAndPeds.Clear(); for(i = 0; i < ms_nNoOfVisibleEntities; i++){ e = ms_aVisibleEntityPtrs[i]; if(IsRoad(e)) continue; #ifdef EXTENDED_PIPELINES if(CustomPipes::bRenderingEnvMap && (e->IsPed() || e->IsVehicle())) continue; #endif if(e->IsVehicle() || e->IsPed() && CVisibilityPlugins::GetClumpAlpha((RpClump*)e->m_rwObject) != 255){ if(e->IsVehicle() && PutIntoSortedVehicleList((CVehicle*)e)){ ei.ent = e; ei.sort = (ms_vecCameraPosition - e->GetPosition()).MagnitudeSqr(); gSortedVehiclesAndPeds.InsertSorted(ei); }else{ if(!CVisibilityPlugins::InsertEntityIntoSortedList(e, (ms_vecCameraPosition - e->GetPosition()).Magnitude())){ printf("Ran out of space in alpha entity list"); RenderOneNonRoad(e); } } }else RenderOneNonRoad(e); } POP_RENDERGROUP(); } void CRenderer::RenderBoats(void) { CLink *node; PUSH_RENDERGROUP("CRenderer::RenderBoats"); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); #ifdef NEW_RENDERER int i; CEntity *e; EntityInfo ei; if(gbNewRenderer){ gSortedVehiclesAndPeds.Clear(); // not the real thing for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ e = ms_aVisibleVehiclePtrs[i]; if(e->IsVehicle() && PutIntoSortedVehicleList((CVehicle*)e)){ ei.ent = e; ei.sort = (ms_vecCameraPosition - e->GetPosition()).MagnitudeSqr(); gSortedVehiclesAndPeds.InsertSorted(ei); } } } #endif for(node = gSortedVehiclesAndPeds.tail.prev; node != &gSortedVehiclesAndPeds.head; node = node->prev){ CVehicle *v = (CVehicle*)node->item.ent; RenderOneNonRoad(v); } POP_RENDERGROUP(); } #ifdef NEW_RENDERER #ifndef LIBRW #error "Need librw for EXTENDED_PIPELINES" #endif #include "WaterLevel.h" enum { // blend passes PASS_NOZ, // no z-write PASS_ADD, // additive PASS_BLEND // normal blend }; static void SetStencilState(int state) { switch(state){ // disable stencil case 0: rw::SetRenderState(rw::STENCILENABLE, FALSE); break; // test against stencil case 1: rw::SetRenderState(rw::STENCILENABLE, TRUE); rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILNOTEQUAL); rw::SetRenderState(rw::STENCILPASS, rw::STENCILKEEP); rw::SetRenderState(rw::STENCILFAIL, rw::STENCILKEEP); rw::SetRenderState(rw::STENCILZFAIL, rw::STENCILKEEP); rw::SetRenderState(rw::STENCILFUNCTIONMASK, 0xFF); rw::SetRenderState(rw::STENCILFUNCTIONREF, 0xFF); break; // write to stencil case 2: rw::SetRenderState(rw::STENCILENABLE, TRUE); rw::SetRenderState(rw::STENCILFUNCTION, rw::STENCILALWAYS); rw::SetRenderState(rw::STENCILPASS, rw::STENCILREPLACE); rw::SetRenderState(rw::STENCILFUNCTIONREF, 0xFF); break; } } void CRenderer::RenderOneBuilding(CEntity *ent, float camdist) { if(ent->m_rwObject == nil) return; ent->bImBeingRendered = true; // TODO: this seems wrong, but do we even need it? assert(RwObjectGetType(ent->m_rwObject) == rpATOMIC); RpAtomic *atomic = (RpAtomic*)ent->m_rwObject; CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->GetModelIndex()); int pass = PASS_BLEND; if(mi->m_additive) // very questionable pass = PASS_ADD; if(mi->m_noZwrite) pass = PASS_NOZ; if(ent->bDistanceFade){ RpAtomic *lodatm; float fadefactor; uint32 alpha; lodatm = mi->GetAtomicFromDistance(camdist - FADE_DISTANCE); fadefactor = (mi->GetLargestLodDistance() - (camdist - FADE_DISTANCE))/FADE_DISTANCE; if(fadefactor > 1.0f) fadefactor = 1.0f; alpha = mi->m_alpha * fadefactor; if(alpha == 255) WorldRender::AtomicFirstPass(atomic, pass); else{ // not quite sure what this is about, do we have to do that? RpGeometry *geo = RpAtomicGetGeometry(lodatm); if(geo != RpAtomicGetGeometry(atomic)) RpAtomicSetGeometry(atomic, geo, rpATOMICSAMEBOUNDINGSPHERE); WorldRender::AtomicFullyTransparent(atomic, pass, alpha); } }else WorldRender::AtomicFirstPass(atomic, pass); ent->bImBeingRendered = false; // TODO: this seems wrong, but do we even need it? } void CRenderer::RenderWorld(int pass) { int i; CEntity *e; CLink *node; RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); DeActivateDirectional(); SetAmbientColours(); // Temporary...have to figure out sorting better switch(pass){ case 0: // Roads PUSH_RENDERGROUP("CRenderer::RenderWorld - Roads"); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); for(i = 0; i < ms_nNoOfVisibleBuildings; i++){ e = ms_aVisibleBuildingPtrs[i]; if(e->bIsBIGBuilding || IsRoad(e)) RenderOneBuilding(e); } for(node = CVisibilityPlugins::m_alphaBuildingList.tail.prev; node != &CVisibilityPlugins::m_alphaBuildingList.head; node = node->prev){ e = node->item.entity; if(e->bIsBIGBuilding || IsRoad(e)) RenderOneBuilding(e, node->item.sort); } POP_RENDERGROUP(); break; case 1: // Opaque PUSH_RENDERGROUP("CRenderer::RenderWorld - Opaque"); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); for(i = 0; i < ms_nNoOfVisibleBuildings; i++){ e = ms_aVisibleBuildingPtrs[i]; if(!(e->bIsBIGBuilding || IsRoad(e))) RenderOneBuilding(e); } for(node = CVisibilityPlugins::m_alphaBuildingList.tail.prev; node != &CVisibilityPlugins::m_alphaBuildingList.head; node = node->prev){ e = node->item.entity; if(!(e->bIsBIGBuilding || IsRoad(e))) RenderOneBuilding(e, node->item.sort); } // Now we have iterated through all visible buildings (unsorted and sorted) // and the transparency list is done. RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE); WorldRender::RenderBlendPass(PASS_NOZ); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); POP_RENDERGROUP(); break; case 2: // Transparent PUSH_RENDERGROUP("CRenderer::RenderWorld - Transparent"); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); WorldRender::RenderBlendPass(PASS_ADD); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); WorldRender::RenderBlendPass(PASS_BLEND); POP_RENDERGROUP(); break; } } void CRenderer::RenderPeds(void) { int i; CEntity *e; PUSH_RENDERGROUP("CRenderer::RenderPeds"); for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ e = ms_aVisibleVehiclePtrs[i]; if(e->IsPed()) RenderOneNonRoad(e); } POP_RENDERGROUP(); } void CRenderer::RenderVehicles(void) { int i; CEntity *e; EntityInfo ei; CLink *node; PUSH_RENDERGROUP("CRenderer::RenderVehicles"); // not the real thing for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ e = ms_aVisibleVehiclePtrs[i]; if(!e->IsVehicle()) continue; if(PutIntoSortedVehicleList((CVehicle*)e)) continue; // boats handled elsewhere ei.ent = e; ei.sort = (ms_vecCameraPosition - e->GetPosition()).MagnitudeSqr(); gSortedVehiclesAndPeds.InsertSorted(ei); } for(node = gSortedVehiclesAndPeds.tail.prev; node != &gSortedVehiclesAndPeds.head; node = node->prev) RenderOneNonRoad(node->item.ent); POP_RENDERGROUP(); } void CRenderer::RenderTransparentWater(void) { int i; CEntity *e; PUSH_RENDERGROUP("CRenderer::RenderTransparentWater"); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); SetStencilState(2); for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ e = ms_aVisibleVehiclePtrs[i]; if(e->IsVehicle() && ((CVehicle*)e)->IsBoat()) ((CBoat*)e)->RenderWaterOutPolys(); } RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); SetStencilState(1); CWaterLevel::RenderTransparentWater(); SetStencilState(0); POP_RENDERGROUP(); } void CRenderer::ClearForFrame(void) { ms_nNoOfVisibleEntities = 0; ms_nNoOfVisibleVehicles = 0; ms_nNoOfVisibleBuildings = 0; ms_nNoOfInVisibleEntities = 0; gSortedVehiclesAndPeds.Clear(); WorldRender::numBlendInsts[PASS_NOZ] = 0; WorldRender::numBlendInsts[PASS_ADD] = 0; WorldRender::numBlendInsts[PASS_BLEND] = 0; } #endif void CRenderer::RenderFadingInEntities(void) { PUSH_RENDERGROUP("CRenderer::RenderFadingInEntities"); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); DeActivateDirectional(); SetAmbientColours(); CVisibilityPlugins::RenderFadingEntities(); POP_RENDERGROUP(); } void CRenderer::RenderFadingInUnderwaterEntities(void) { PUSH_RENDERGROUP("CRenderer::RenderFadingInUnderwaterEntities"); DeActivateDirectional(); SetAmbientColours(); CVisibilityPlugins::RenderFadingUnderwaterEntities(); POP_RENDERGROUP(); } void CRenderer::RenderCollisionLines(void) { int i; // game doesn't draw fading in entities // this should probably be fixed for(i = 0; i < ms_nNoOfVisibleEntities; i++){ CEntity *e = ms_aVisibleEntityPtrs[i]; if(Abs(e->GetPosition().x - ms_vecCameraPosition.x) < 100.0f && Abs(e->GetPosition().y - ms_vecCameraPosition.y) < 100.0f) CCollision::DrawColModel(e->GetMatrix(), *e->GetColModel()); } } enum Visbility { VIS_INVISIBLE, VIS_VISIBLE, VIS_OFFSCREEN, VIS_STREAMME }; // Time Objects can be time culled if // other == -1 || CModelInfo::GetModelInfo(other)->GetRwObject() // i.e. we have to draw even at the wrong time if // other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject() == nil #define OTHERUNAVAILABLE (other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject() == nil) #define CANTIMECULL (!OTHERUNAVAILABLE) int32 CRenderer::SetupEntityVisibility(CEntity *ent) { CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->m_modelIndex); CTimeModelInfo *ti; int32 other; float dist; bool request = true; if(mi->GetModelType() == MITYPE_TIME){ ti = (CTimeModelInfo*)mi; other = ti->GetOtherTimeModel(); if(CClock::GetIsTimeInRange(ti->GetTimeOn(), ti->GetTimeOff())){ // don't fade in, or between time objects if(CANTIMECULL) ti->m_alpha = 255; }else{ // Hide if possible if(CANTIMECULL) return VIS_INVISIBLE; // can't cull, so we'll try to draw this one, but don't request // it since what we really want is the other one. request = false; } }else{ if(mi->GetModelType() != MITYPE_SIMPLE && mi->GetModelType() != MITYPE_WEAPON){ if(FindPlayerVehicle() == ent && TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON && !(FindPlayerVehicle()->IsBike() && ((CBike*)FindPlayerVehicle())->bWheelieCam)){ // Player's vehicle in first person mode CVehicle *veh = (CVehicle*)ent; int model = veh->GetModelIndex(); int direction = TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking; if(direction == LOOKING_FORWARD || ent->GetModelIndex() == MI_RHINO || ent->GetModelIndex() == MI_COACH || TheCamera.m_bInATunnelAndABigVehicle || direction == LOOKING_BEHIND && veh->pHandling->Flags & HANDLING_UNKNOWN){ ent->bNoBrightHeadLights = true; return VIS_OFFSCREEN; } if(direction != LOOKING_BEHIND || !veh->IsBoat() || model == MI_REEFER || model == MI_TROPIC || model == MI_PREDATOR || model == MI_SKIMMER){ m_pFirstPersonVehicle = (CVehicle*)ent; ent->bNoBrightHeadLights = false; return VIS_OFFSCREEN; } } // All sorts of Clumps if(ent->m_rwObject == nil || !ent->bIsVisible) return VIS_INVISIBLE; if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()) return VIS_OFFSCREEN; if(ent->bDrawLast){ dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude(); CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = false; return VIS_INVISIBLE; } return VIS_VISIBLE; } if(ent->bDontStream){ if(ent->m_rwObject == nil || !ent->bIsVisible) return VIS_INVISIBLE; if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()) return VIS_OFFSCREEN; if(ent->bDrawLast){ dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude(); CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = false; return VIS_INVISIBLE; } return VIS_VISIBLE; } } // Simple ModelInfo if(!IsAreaVisible(ent->m_area)) return VIS_INVISIBLE; dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude(); #ifndef FIX_BUGS // Whatever this is supposed to do, it breaks fading for objects // whose draw dist is > LOD_DISTANCE-FADE_DISTANCE, i.e. 280 // because decreasing dist here makes the object visible above LOD_DISTANCE // before fading normally once below LOD_DISTANCE. // aha! this must be a workaround for the fact that we're not taking // the LOD multiplier into account here anywhere if(LOD_DISTANCE < dist && dist < mi->GetLargestLodDistance() + FADE_DISTANCE) dist += mi->GetLargestLodDistance() - LOD_DISTANCE; #endif if(ent->IsObject() && ent->bRenderDamaged) mi->m_isDamaged = true; RpAtomic *a = mi->GetAtomicFromDistance(dist); if(a){ mi->m_isDamaged = false; if(ent->m_rwObject == nil) ent->CreateRwObject(); assert(ent->m_rwObject); RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; // Make sure our atomic uses the right geometry and not // that of an atomic for another draw distance. if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) mi->IncreaseAlpha(); if(ent->m_rwObject == nil || !ent->bIsVisible) return VIS_INVISIBLE; if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()){ mi->m_alpha = 255; return VIS_OFFSCREEN; } if(mi->m_alpha != 255){ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = true; return VIS_INVISIBLE; } if(mi->m_drawLast || ent->bDrawLast){ if(CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist)){ ent->bDistanceFade = false; return VIS_INVISIBLE; } } return VIS_VISIBLE; } // Object is not loaded, figure out what to do if(mi->m_noFade){ mi->m_isDamaged = false; // request model if(dist - STREAM_DISTANCE < mi->GetLargestLodDistance() && request) return VIS_STREAMME; return VIS_INVISIBLE; } // We might be fading a = mi->GetAtomicFromDistance(dist - FADE_DISTANCE); mi->m_isDamaged = false; if(a == nil){ // request model if(dist - FADE_DISTANCE - STREAM_DISTANCE < mi->GetLargestLodDistance() && request) return VIS_STREAMME; return VIS_INVISIBLE; } if(ent->m_rwObject == nil) ent->CreateRwObject(); assert(ent->m_rwObject); RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) mi->IncreaseAlpha(); if(ent->m_rwObject == nil || !ent->bIsVisible) return VIS_INVISIBLE; if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()){ mi->m_alpha = 255; return VIS_OFFSCREEN; }else{ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = true; return VIS_OFFSCREEN; // Why this? } } int32 CRenderer::SetupBigBuildingVisibility(CEntity *ent) { CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->m_modelIndex); CTimeModelInfo *ti; int32 other; if(!IsAreaVisible(ent->m_area)) return VIS_INVISIBLE; bool request = true; if(mi->GetModelType() == MITYPE_TIME){ ti = (CTimeModelInfo*)mi; other = ti->GetOtherTimeModel(); if(CClock::GetIsTimeInRange(ti->GetTimeOn(), ti->GetTimeOff())){ // don't fade in, or between time objects if(CANTIMECULL) ti->m_alpha = 255; }else{ // Hide if possible if(CANTIMECULL){ ent->DeleteRwObject(); return VIS_INVISIBLE; } // can't cull, so we'll try to draw this one, but don't request // it since what we really want is the other one. request = false; } }else if(mi->GetModelType() == MITYPE_VEHICLE) return ent->IsVisible() ? VIS_VISIBLE : VIS_INVISIBLE; float dist = (ms_vecCameraPosition-ent->GetPosition()).Magnitude(); CSimpleModelInfo *nonLOD = mi->GetRelatedModel(); // Find out whether to draw below near distance. // This is only the case if there is a non-LOD which is either not // loaded or not completely faded in yet. if(dist < mi->GetNearDistance() && dist < LOD_DISTANCE){ // No non-LOD or non-LOD is completely visible. if(nonLOD == nil || nonLOD->GetRwObject() && nonLOD->m_alpha == 255) return VIS_INVISIBLE; // But if it is a time object, we'd rather draw the wrong // non-LOD than the right LOD. if(nonLOD->GetModelType() == MITYPE_TIME){ ti = (CTimeModelInfo*)nonLOD; other = ti->GetOtherTimeModel(); if(other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject()) return VIS_INVISIBLE; } } RpAtomic *a = mi->GetFirstAtomicFromDistance(dist); if(a){ if(ent->m_rwObject == nil) ent->CreateRwObject(); assert(ent->m_rwObject); RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; // Make sure our atomic uses the right geometry and not // that of an atomic for another draw distance. if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) mi->IncreaseAlpha(); if(!ent->IsVisible() || !ent->GetIsOnScreenComplex() || ent->IsEntityOccluded()){ mi->m_alpha = 255; return VIS_INVISIBLE; } if(mi->m_alpha != 255){ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = true; return VIS_INVISIBLE; } if(mi->m_drawLast){ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = false; return VIS_INVISIBLE; } return VIS_VISIBLE; } if(mi->m_noFade){ ent->DeleteRwObject(); return VIS_INVISIBLE; } // get faded atomic a = mi->GetFirstAtomicFromDistance(dist - FADE_DISTANCE); if(a == nil){ if(ent->bStreamBIGBuilding && dist-STREAM_DISTANCE < mi->GetLodDistance(0) && request){ return ent->GetIsOnScreen() ? VIS_STREAMME : VIS_INVISIBLE; }else{ ent->DeleteRwObject(); return VIS_INVISIBLE; } } // Fade... if(ent->m_rwObject == nil) ent->CreateRwObject(); assert(ent->m_rwObject); RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) mi->IncreaseAlpha(); if(!ent->IsVisible() || !ent->GetIsOnScreenComplex() || ent->IsEntityOccluded()){ mi->m_alpha = 255; return VIS_INVISIBLE; } CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = true; return VIS_INVISIBLE; } void CRenderer::ConstructRenderList(void) { COcclusion::ProcessBeforeRendering(); #ifdef NEW_RENDERER if(!gbNewRenderer) #endif { ms_nNoOfVisibleEntities = 0; ms_nNoOfInVisibleEntities = 0; } ms_vecCameraPosition = TheCamera.GetPosition(); // unused pFullBlockedRanges = nil; pEmptyBlockedRanges = aBlockedRanges; for(int i = 0; i < 16; i++){ aBlockedRanges[i].prev = &aBlockedRanges[i-1]; aBlockedRanges[i].next = &aBlockedRanges[i+1]; } aBlockedRanges[0].prev = nil; aBlockedRanges[15].next = nil; // unused TestCloseThings = 0; TestBigThings = 0; ScanWorld(); } void LimitFrustumVector(CVector &vec1, const CVector &vec2, float l) { float f; f = (l - vec2.z) / (vec1.z - vec2.z); vec1.x = f*(vec1.x - vec2.x) + vec2.x; vec1.y = f*(vec1.y - vec2.y) + vec2.y; vec1.z = f*(vec1.z - vec2.z) + vec2.z; } enum Corners { CORNER_CAM = 0, CORNER_FAR_TOPLEFT, CORNER_FAR_TOPRIGHT, CORNER_FAR_BOTRIGHT, CORNER_FAR_BOTLEFT, CORNER_LOD_LEFT, CORNER_LOD_RIGHT, CORNER_PRIO_LEFT, CORNER_PRIO_RIGHT, }; void CRenderer::ScanWorld(void) { float f = RwCameraGetFarClipPlane(TheCamera.m_pRwCamera); RwV2d vw = *RwCameraGetViewWindow(TheCamera.m_pRwCamera); CVector vectors[9]; RwMatrix *cammatrix; RwV2d poly[3]; memset(vectors, 0, sizeof(vectors)); vectors[CORNER_FAR_TOPLEFT].x = -vw.x * f; vectors[CORNER_FAR_TOPLEFT].y = vw.y * f; vectors[CORNER_FAR_TOPLEFT].z = f; vectors[CORNER_FAR_TOPRIGHT].x = vw.x * f; vectors[CORNER_FAR_TOPRIGHT].y = vw.y * f; vectors[CORNER_FAR_TOPRIGHT].z = f; vectors[CORNER_FAR_BOTRIGHT].x = vw.x * f; vectors[CORNER_FAR_BOTRIGHT].y = -vw.y * f; vectors[CORNER_FAR_BOTRIGHT].z = f; vectors[CORNER_FAR_BOTLEFT].x = -vw.x * f; vectors[CORNER_FAR_BOTLEFT].y = -vw.y * f; vectors[CORNER_FAR_BOTLEFT].z = f; cammatrix = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); m_pFirstPersonVehicle = nil; CVisibilityPlugins::InitAlphaEntityList(); CWorld::AdvanceCurrentScanCode(); // unused static CVector prevPos; static CVector prevFwd; static bool smallMovement; smallMovement = (TheCamera.GetPosition() - prevPos).MagnitudeSqr() < SQR(4.0f) && DotProduct(TheCamera.GetForward(), prevFwd) > 0.98f; prevPos = TheCamera.GetPosition(); prevFwd = TheCamera.GetForward(); if(cammatrix->at.z > 0.0f){ // looking up, bottom corners are further away vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_BOTLEFT] * LOD_DISTANCE/f; vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_BOTRIGHT] * LOD_DISTANCE/f; }else{ // looking down, top corners are further away vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_TOPLEFT] * LOD_DISTANCE/f; vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_TOPRIGHT] * LOD_DISTANCE/f; } vectors[CORNER_PRIO_LEFT].x = vectors[CORNER_LOD_LEFT].x * 0.2f; vectors[CORNER_PRIO_LEFT].y = vectors[CORNER_LOD_LEFT].y * 0.2f; vectors[CORNER_PRIO_LEFT].z = vectors[CORNER_LOD_LEFT].z; vectors[CORNER_PRIO_RIGHT].x = vectors[CORNER_LOD_RIGHT].x * 0.2f; vectors[CORNER_PRIO_RIGHT].y = vectors[CORNER_LOD_RIGHT].y * 0.2f; vectors[CORNER_PRIO_RIGHT].z = vectors[CORNER_LOD_RIGHT].z; RwV3dTransformPoints(vectors, vectors, 9, cammatrix); m_loadingPriority = false; if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || #ifdef FIX_BUGS TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_GTACLASSIC || #endif TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ CRect rect; int x1, x2, y1, y2; LimitFrustumVector(vectors[CORNER_FAR_TOPLEFT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_TOPLEFT]); LimitFrustumVector(vectors[CORNER_FAR_TOPRIGHT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_TOPRIGHT]); LimitFrustumVector(vectors[CORNER_FAR_BOTRIGHT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_BOTRIGHT]); LimitFrustumVector(vectors[CORNER_FAR_BOTLEFT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_BOTLEFT]); x1 = CWorld::GetSectorIndexX(rect.left); if(x1 < 0) x1 = 0; x2 = CWorld::GetSectorIndexX(rect.right); if(x2 >= NUMSECTORS_X-1) x2 = NUMSECTORS_X-1; y1 = CWorld::GetSectorIndexY(rect.top); if(y1 < 0) y1 = 0; y2 = CWorld::GetSectorIndexY(rect.bottom); if(y2 >= NUMSECTORS_Y-1) y2 = NUMSECTORS_Y-1; for(; x1 <= x2; x1++) for(int y = y1; y <= y2; y++) ScanSectorList(CWorld::GetSector(x1, y)->m_lists); }else{ #ifdef GTA_TRAIN CVehicle *train = FindPlayerTrain(); if(train && train->GetPosition().z < 0.0f){ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList_Subway); }else #endif { if(f > LOD_DISTANCE){ // priority poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_PRIO_LEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_PRIO_LEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_PRIO_RIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_PRIO_RIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList_Priority); // below LOD poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList); }else{ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_FAR_TOPLEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_FAR_TOPLEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_FAR_TOPRIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_FAR_TOPRIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList); } #ifdef NO_ISLAND_LOADING if (FrontEndMenuManager.m_PrefsIslandLoading == CMenuManager::ISLAND_LOADING_HIGH) { ScanBigBuildingList(CWorld::GetBigBuildingList(LEVEL_BEACH)); ScanBigBuildingList(CWorld::GetBigBuildingList(LEVEL_MAINLAND)); } else #endif { #ifdef FIX_BUGS if(CCollision::ms_collisionInMemory != LEVEL_GENERIC) #endif ScanBigBuildingList(CWorld::GetBigBuildingList(CGame::currLevel)); } ScanBigBuildingList(CWorld::GetBigBuildingList(LEVEL_GENERIC)); } } } void CRenderer::RequestObjectsInFrustum(void) { float f = RwCameraGetFarClipPlane(TheCamera.m_pRwCamera); RwV2d vw = *RwCameraGetViewWindow(TheCamera.m_pRwCamera); CVector vectors[9]; RwMatrix *cammatrix; RwV2d poly[3]; memset(vectors, 0, sizeof(vectors)); vectors[CORNER_FAR_TOPLEFT].x = -vw.x * f; vectors[CORNER_FAR_TOPLEFT].y = vw.y * f; vectors[CORNER_FAR_TOPLEFT].z = f; vectors[CORNER_FAR_TOPRIGHT].x = vw.x * f; vectors[CORNER_FAR_TOPRIGHT].y = vw.y * f; vectors[CORNER_FAR_TOPRIGHT].z = f; vectors[CORNER_FAR_BOTRIGHT].x = vw.x * f; vectors[CORNER_FAR_BOTRIGHT].y = -vw.y * f; vectors[CORNER_FAR_BOTRIGHT].z = f; vectors[CORNER_FAR_BOTLEFT].x = -vw.x * f; vectors[CORNER_FAR_BOTLEFT].y = -vw.y * f; vectors[CORNER_FAR_BOTLEFT].z = f; cammatrix = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); CWorld::AdvanceCurrentScanCode(); ms_vecCameraPosition = TheCamera.GetPosition(); if(cammatrix->at.z > 0.0f){ // looking up, bottom corners are further away vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_BOTLEFT] * LOD_DISTANCE/f; vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_BOTRIGHT] * LOD_DISTANCE/f; }else{ // looking down, top corners are further away vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_TOPLEFT] * LOD_DISTANCE/f; vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_TOPRIGHT] * LOD_DISTANCE/f; } vectors[CORNER_PRIO_LEFT].x = vectors[CORNER_LOD_LEFT].x * 0.2f; vectors[CORNER_PRIO_LEFT].y = vectors[CORNER_LOD_LEFT].y * 0.2f; vectors[CORNER_PRIO_LEFT].z = vectors[CORNER_LOD_LEFT].z; vectors[CORNER_PRIO_RIGHT].x = vectors[CORNER_LOD_RIGHT].x * 0.2f; vectors[CORNER_PRIO_RIGHT].y = vectors[CORNER_LOD_RIGHT].y * 0.2f; vectors[CORNER_PRIO_RIGHT].z = vectors[CORNER_LOD_RIGHT].z; RwV3dTransformPoints(vectors, vectors, 9, cammatrix); if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || #ifdef FIX_BUGS TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_GTACLASSIC || #endif TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ CRect rect; int x1, x2, y1, y2; LimitFrustumVector(vectors[CORNER_FAR_TOPLEFT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_TOPLEFT]); LimitFrustumVector(vectors[CORNER_FAR_TOPRIGHT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_TOPRIGHT]); LimitFrustumVector(vectors[CORNER_FAR_BOTRIGHT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_BOTRIGHT]); LimitFrustumVector(vectors[CORNER_FAR_BOTLEFT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_BOTLEFT]); x1 = CWorld::GetSectorIndexX(rect.left); if(x1 < 0) x1 = 0; x2 = CWorld::GetSectorIndexX(rect.right); if(x2 >= NUMSECTORS_X-1) x2 = NUMSECTORS_X-1; y1 = CWorld::GetSectorIndexY(rect.top); if(y1 < 0) y1 = 0; y2 = CWorld::GetSectorIndexY(rect.bottom); if(y2 >= NUMSECTORS_Y-1) y2 = NUMSECTORS_Y-1; for(; x1 <= x2; x1++) for(int y = y1; y <= y2; y++) ScanSectorList_RequestModels(CWorld::GetSector(x1, y)->m_lists); }else{ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList_RequestModels); } } bool CEntity::SetupLighting(void) { return false; } void CEntity::RemoveLighting(bool) { } bool CPed::SetupLighting(void) { ActivateDirectional(); SetAmbientColoursForPedsCarsAndObjects(); #ifndef MASTER // Originally this was being called through iteration of Sectors, but putting it here is better. if (GetDebugDisplay() != 0 && !IsPlayer()) DebugRenderOnePedText(); #endif if (bRenderScorched) { WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); } else { // Note that this lightMult is only affected by LIGHT_DARKEN. If there's no LIGHT_DARKEN, it will be 1.0. float lightMult = CPointLights::GenerateLightsAffectingObject(&GetPosition()); if (lightMult != 1.0f) { SetAmbientAndDirectionalColours(lightMult); return true; } } return false; } void CPed::RemoveLighting(bool reset) { if (!bRenderScorched) { CRenderer::RemoveVehiclePedLights(this, reset); if (reset) ReSetAmbientAndDirectionalColours(); } SetAmbientColours(); DeActivateDirectional(); } float CalcNewDelta(RwV2d *a, RwV2d *b) { return (b->x - a->x) / (b->y - a->y); } #ifdef FIX_BUGS #define TOINT(x) ((int)Floor(x)) #else #define TOINT(x) ((int)(x)) #endif void CRenderer::ScanSectorPoly(RwV2d *poly, int32 numVertices, void (*scanfunc)(CPtrList *)) { float miny, maxy; int y, yend; int x, xstart, xend; int i; int a1, a2, b1, b2; float deltaA, deltaB; float xA, xB; miny = poly[0].y; maxy = poly[0].y; a2 = 0; xstart = 9999; xend = -9999; for(i = 1; i < numVertices; i++){ if(poly[i].y > maxy) maxy = poly[i].y; if(poly[i].y < miny){ miny = poly[i].y; a2 = i; } } y = TOINT(miny); yend = TOINT(maxy); // Go left in poly to find first edge b b2 = a2; for(i = 0; i < numVertices; i++){ b1 = b2--; if(b2 < 0) b2 = numVertices-1; if(poly[b1].x < xstart) xstart = TOINT(poly[b1].x); if(TOINT(poly[b1].y) != TOINT(poly[b2].y)) break; } // Go right to find first edge a for(i = 0; i < numVertices; i++){ a1 = a2++; if(a2 == numVertices) a2 = 0; if(poly[a1].x > xend) xend = TOINT(poly[a1].x); if(TOINT(poly[a1].y) != TOINT(poly[a2].y)) break; } // prestep x1 and x2 to next integer y deltaA = CalcNewDelta(&poly[a1], &poly[a2]); xA = deltaA * (Ceil(poly[a1].y) - poly[a1].y) + poly[a1].x; deltaB = CalcNewDelta(&poly[b1], &poly[b2]); xB = deltaB * (Ceil(poly[b1].y) - poly[b1].y) + poly[b1].x; if(y != yend){ if(deltaB < 0.0f && TOINT(xB) < xstart) xstart = TOINT(xB); if(deltaA >= 0.0f && TOINT(xA) > xend) xend = TOINT(xA); } while(y <= yend && y < NUMSECTORS_Y){ // scan one x-line if(y >= 0 && xstart < NUMSECTORS_X) for(x = xstart; x <= xend && x != NUMSECTORS_X; x++) if(x >= 0) scanfunc(CWorld::GetSector(x, y)->m_lists); // advance one scan line y++; xA += deltaA; xB += deltaB; // update left side if(y == TOINT(poly[b2].y)){ // reached end of edge if(y == yend){ if(deltaB < 0.0f){ do{ xstart = TOINT(poly[b2--].x); if(b2 < 0) b2 = numVertices-1; }while(xstart > TOINT(poly[b2].x)); }else xstart = TOINT(xB - deltaB); }else{ // switch edges if(deltaB < 0.0f) xstart = TOINT(poly[b2].x); else xstart = TOINT(xB - deltaB); do{ b1 = b2--; if(b2 < 0) b2 = numVertices-1; if(TOINT(poly[b1].x) < xstart) xstart = TOINT(poly[b1].x); }while(y == TOINT(poly[b2].y)); deltaB = CalcNewDelta(&poly[b1], &poly[b2]); xB = deltaB * (Ceil(poly[b1].y) - poly[b1].y) + poly[b1].x; if(deltaB < 0.0f && TOINT(xB) < xstart) xstart = TOINT(xB); } }else{ if(deltaB < 0.0f) xstart = TOINT(xB); else xstart = TOINT(xB - deltaB); } // update right side if(y == TOINT(poly[a2].y)){ // reached end of edge if(y == yend){ if(deltaA < 0.0f) xend = TOINT(xA - deltaA); else{ do{ xend = TOINT(poly[a2++].x); if(a2 == numVertices) a2 = 0; }while(xend < TOINT(poly[a2].x)); } }else{ // switch edges if(deltaA < 0.0f) xend = TOINT(xA - deltaA); else xend = TOINT(poly[a2].x); do{ a1 = a2++; if(a2 == numVertices) a2 = 0; if(TOINT(poly[a1].x) > xend) xend = TOINT(poly[a1].x); }while(y == TOINT(poly[a2].y)); deltaA = CalcNewDelta(&poly[a1], &poly[a2]); xA = deltaA * (Ceil(poly[a1].y) - poly[a1].y) + poly[a1].x; if(deltaA >= 0.0f && TOINT(xA) > xend) xend = TOINT(xA); } }else{ if(deltaA < 0.0f) xend = TOINT(xA - deltaA); else xend = TOINT(xA); } } } void CRenderer::InsertEntityIntoList(CEntity *ent) { #ifdef FIX_BUGS if (!ent->m_rwObject) return; #endif #ifdef NEW_RENDERER // TODO: there are more flags being checked here if(gbNewRenderer && (ent->IsVehicle() || ent->IsPed())) ms_aVisibleVehiclePtrs[ms_nNoOfVisibleVehicles++] = ent; else if(gbNewRenderer && ent->IsBuilding()) ms_aVisibleBuildingPtrs[ms_nNoOfVisibleBuildings++] = ent; else #endif ms_aVisibleEntityPtrs[ms_nNoOfVisibleEntities++] = ent; } void CRenderer::ScanBigBuildingList(CPtrList &list) { CPtrNode *node; CEntity *ent; int vis; int f = CTimer::GetFrameCounter() & 3; for(node = list.first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->bOffscreen || (ent->m_randomSeed&3) != f){ ent->bOffscreen = true; vis = SetupBigBuildingVisibility(ent); }else vis = VIS_VISIBLE; switch(vis){ case VIS_VISIBLE: InsertEntityIntoList(ent); ent->bOffscreen = false; break; case VIS_STREAMME: if(!CStreaming::ms_disableStreaming) CStreaming::RequestModel(ent->GetModelIndex(), 0); break; } } } void CRenderer::ScanSectorList(CPtrList *lists) { CPtrNode *node; CPtrList *list; CEntity *ent; int i; float dx, dy; for(i = 0; i < NUMSECTORENTITYLISTS; i++){ list = &lists[i]; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->m_scanCode == CWorld::GetCurrentScanCode()) continue; // already seen ent->m_scanCode = CWorld::GetCurrentScanCode(); ent->bOffscreen = false; switch(SetupEntityVisibility(ent)){ case VIS_VISIBLE: InsertEntityIntoList(ent); break; case VIS_INVISIBLE: if(!IsGlass(ent->GetModelIndex())) break; // fall through case VIS_OFFSCREEN: ent->bOffscreen = true; dx = ms_vecCameraPosition.x - ent->GetPosition().x; dy = ms_vecCameraPosition.y - ent->GetPosition().y; if(dx > -30.0f && dx < 30.0f && dy > -30.0f && dy < 30.0f && ms_nNoOfInVisibleEntities < NUMINVISIBLEENTITIES - 1) ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent; break; case VIS_STREAMME: if(!CStreaming::ms_disableStreaming) if(!m_loadingPriority || CStreaming::ms_numModelsRequested < 10) CStreaming::RequestModel(ent->GetModelIndex(), 0); break; } } } } void CRenderer::ScanSectorList_Priority(CPtrList *lists) { CPtrNode *node; CPtrList *list; CEntity *ent; int i; float dx, dy; for(i = 0; i < NUMSECTORENTITYLISTS; i++){ list = &lists[i]; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->m_scanCode == CWorld::GetCurrentScanCode()) continue; // already seen ent->m_scanCode = CWorld::GetCurrentScanCode(); ent->bOffscreen = false; switch(SetupEntityVisibility(ent)){ case VIS_VISIBLE: InsertEntityIntoList(ent); break; case VIS_INVISIBLE: if(!IsGlass(ent->GetModelIndex())) break; // fall through case VIS_OFFSCREEN: ent->bOffscreen = true; dx = ms_vecCameraPosition.x - ent->GetPosition().x; dy = ms_vecCameraPosition.y - ent->GetPosition().y; if(dx > -30.0f && dx < 30.0f && dy > -30.0f && dy < 30.0f && ms_nNoOfInVisibleEntities < NUMINVISIBLEENTITIES - 1) ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent; break; case VIS_STREAMME: if(!CStreaming::ms_disableStreaming){ CStreaming::RequestModel(ent->GetModelIndex(), 0); if(CStreaming::ms_aInfoForModel[ent->GetModelIndex()].m_loadState != STREAMSTATE_LOADED) m_loadingPriority = true; } break; } } } } #ifdef GTA_TRAIN void CRenderer::ScanSectorList_Subway(CPtrList *lists) { CPtrNode *node; CPtrList *list; CEntity *ent; int i; float dx, dy; for(i = 0; i < NUMSECTORENTITYLISTS; i++){ list = &lists[i]; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->m_scanCode == CWorld::GetCurrentScanCode()) continue; // already seen ent->m_scanCode = CWorld::GetCurrentScanCode(); ent->bOffscreen = false; switch(SetupEntityVisibility(ent)){ case VIS_VISIBLE: InsertEntityIntoList(ent); break; case VIS_OFFSCREEN: ent->bOffscreen = true; dx = ms_vecCameraPosition.x - ent->GetPosition().x; dy = ms_vecCameraPosition.y - ent->GetPosition().y; if(dx > -30.0f && dx < 30.0f && dy > -30.0f && dy < 30.0f && ms_nNoOfInVisibleEntities < NUMINVISIBLEENTITIES - 1) ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent; break; } } } } #endif void CRenderer::ScanSectorList_RequestModels(CPtrList *lists) { CPtrNode *node; CPtrList *list; CEntity *ent; int i; for(i = 0; i < NUMSECTORENTITYLISTS; i++){ list = &lists[i]; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->m_scanCode == CWorld::GetCurrentScanCode()) continue; // already seen ent->m_scanCode = CWorld::GetCurrentScanCode(); if(ShouldModelBeStreamed(ent, ms_vecCameraPosition)) CStreaming::RequestModel(ent->GetModelIndex(), 0); } } } // Put big buildings in front // This seems pointless because the sector lists shouldn't have big buildings in the first place void CRenderer::SortBIGBuildings(void) { int x, y; for(y = 0; y < NUMSECTORS_Y; y++) for(x = 0; x < NUMSECTORS_X; x++){ SortBIGBuildingsForSectorList(&CWorld::GetSector(x, y)->m_lists[ENTITYLIST_BUILDINGS]); SortBIGBuildingsForSectorList(&CWorld::GetSector(x, y)->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); } } void CRenderer::SortBIGBuildingsForSectorList(CPtrList *list) { CPtrNode *node; CEntity *ent; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->bIsBIGBuilding){ list->RemoveNode(node); list->InsertNode(node); } } } bool CRenderer::ShouldModelBeStreamed(CEntity *ent, const CVector &campos) { if(!IsAreaVisible(ent->m_area)) return false; CTimeModelInfo *mi = (CTimeModelInfo *)CModelInfo::GetModelInfo(ent->GetModelIndex()); if(mi->GetModelType() == MITYPE_TIME) if(!CClock::GetIsTimeInRange(mi->GetTimeOn(), mi->GetTimeOff())) return false; float dist = (ent->GetPosition() - campos).Magnitude(); if(mi->m_noFade) return dist - STREAM_DISTANCE < mi->GetLargestLodDistance(); else return dist - FADE_DISTANCE - STREAM_DISTANCE < mi->GetLargestLodDistance(); } void CRenderer::RemoveVehiclePedLights(CEntity *ent, bool reset) { if(!ent->bRenderScorched){ CPointLights::RemoveLightsAffectingObject(); if(reset) ReSetAmbientAndDirectionalColours(); } SetAmbientColours(); DeActivateDirectional(); } ================================================ FILE: src/render/Renderer.h ================================================ #pragma once class CEntity; #ifdef FIX_BUGS #define LOD_DISTANCE (300.0f*TheCamera.LODDistMultiplier) #else #define LOD_DISTANCE 300.0f #endif #define FADE_DISTANCE 20.0f #define STREAM_DISTANCE 30.0f extern bool gbShowPedRoadGroups; extern bool gbShowCarRoadGroups; extern bool gbShowCollisionPolys; extern bool gbShowCollisionLines; extern bool gbBigWhiteDebugLightSwitchedOn; extern bool gbDontRenderBuildings; extern bool gbDontRenderBigBuildings; extern bool gbDontRenderPeds; extern bool gbDontRenderObjects; extern bool gbDontRenderVehicles; class CVehicle; class CPtrList; // unused struct BlockedRange { float a, b; // unknown BlockedRange *prev, *next; }; class CRenderer { static int32 ms_nNoOfVisibleEntities; static CEntity *ms_aVisibleEntityPtrs[NUMVISIBLEENTITIES]; static int32 ms_nNoOfInVisibleEntities; static CEntity *ms_aInVisibleEntityPtrs[NUMINVISIBLEENTITIES]; #ifdef NEW_RENDERER static int32 ms_nNoOfVisibleVehicles; static CEntity *ms_aVisibleVehiclePtrs[NUMVISIBLEENTITIES]; // for cWorldStream emulation static int32 ms_nNoOfVisibleBuildings; static CEntity *ms_aVisibleBuildingPtrs[NUMVISIBLEENTITIES]; #endif static CVector ms_vecCameraPosition; static CVehicle *m_pFirstPersonVehicle; // unused static BlockedRange aBlockedRanges[16]; static BlockedRange *pFullBlockedRanges; static BlockedRange *pEmptyBlockedRanges; public: static float ms_lodDistScale; static bool m_loadingPriority; static void Init(void); static void Shutdown(void); static void PreRender(void); static void RenderRoads(void); static void RenderFadingInEntities(void); static void RenderFadingInUnderwaterEntities(void); static void RenderEverythingBarRoads(void); static void RenderBoats(void); static void RenderOneRoad(CEntity *); static void RenderOneNonRoad(CEntity *); static void RenderFirstPersonVehicle(void); static void RenderCollisionLines(void); static int32 SetupEntityVisibility(CEntity *ent); static int32 SetupBigBuildingVisibility(CEntity *ent); static void ConstructRenderList(void); static void ScanWorld(void); static void RequestObjectsInFrustum(void); static void ScanSectorPoly(RwV2d *poly, int32 numVertices, void (*scanfunc)(CPtrList *)); static void ScanBigBuildingList(CPtrList &list); static void ScanSectorList(CPtrList *lists); static void ScanSectorList_Priority(CPtrList *lists); static void ScanSectorList_Subway(CPtrList *lists); static void ScanSectorList_RequestModels(CPtrList *lists); static void SortBIGBuildings(void); static void SortBIGBuildingsForSectorList(CPtrList *list); static bool ShouldModelBeStreamed(CEntity *ent, const CVector &campos); static void RemoveVehiclePedLights(CEntity *ent, bool reset); #ifdef NEW_RENDERER static void ClearForFrame(void); static void RenderPeds(void); static void RenderVehicles(void); // also renders peds in LCS static void RenderOneBuilding(CEntity *ent, float camdist = 0.0f); static void RenderWorld(int pass); // like cWorldStream::Render(int) static void RenderTransparentWater(void); // keep-out polys and transparent water #endif static void InsertEntityIntoList(CEntity *ent); }; ================================================ FILE: src/render/Rubbish.cpp ================================================ #include "common.h" #include "main.h" #include "General.h" #include "Timer.h" #include "Weather.h" #include "Camera.h" #include "World.h" #include "Vehicle.h" #include "ZoneCull.h" #include "Stats.h" #include "TxdStore.h" #include "RenderBuffer.h" #include "Rubbish.h" #define RUBBISH_MAX_DIST (23.0f) #define RUBBISH_FADE_DIST (20.0f) RwTexture *gpRubbishTexture[4]; RwImVertexIndex RubbishIndexList[6]; bool CRubbish::bRubbishInvisible; int CRubbish::RubbishVisibility; COneSheet CRubbish::aSheets[NUM_RUBBISH_SHEETS]; COneSheet CRubbish::StartEmptyList; COneSheet CRubbish::EndEmptyList; COneSheet CRubbish::StartStaticsList; COneSheet CRubbish::EndStaticsList; COneSheet CRubbish::StartMoversList; COneSheet CRubbish::EndMoversList; void COneSheet::AddToList(COneSheet *list) { this->m_next = list->m_next; this->m_prev = list; list->m_next = this; this->m_next->m_prev = this; } void COneSheet::RemoveFromList(void) { m_next->m_prev = m_prev; m_prev->m_next = m_next; } void CRubbish::Render(void) { int type; if(RubbishVisibility == 0) return; PUSH_RENDERGROUP("CRubbish::Render"); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); for(type = 0; type < 4; type++){ if(type < 3 || CStats::PamphletMissionPassed) RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpRubbishTexture[type])); TempBufferIndicesStored = 0; TempBufferVerticesStored = 0; COneSheet *sheet; for(sheet = &aSheets[type*NUM_RUBBISH_SHEETS / 4]; sheet < &aSheets[(type+1)*NUM_RUBBISH_SHEETS / 4]; sheet++){ if(sheet->m_state == 0) continue; uint32 alpha = 100; CVector pos; if(sheet->m_state == 1){ pos = sheet->m_basePos; if(!sheet->m_isVisible) alpha = 0; }else{ pos = sheet->m_animatedPos; // Not fully visible during animation, calculate current alpha if(!sheet->m_isVisible || !sheet->m_targetIsVisible){ float t = (float)(CTimer::GetTimeInMilliseconds() - sheet->m_moveStart)/sheet->m_moveDuration; float f1 = sheet->m_isVisible ? 1.0f-t : 0.0f; float f2 = sheet->m_targetIsVisible ? t : 0.0f; alpha = 100 * (f1+f2); } } float camDist = (pos - TheCamera.GetPosition()).Magnitude2D(); if(camDist < RUBBISH_MAX_DIST){ if(camDist >= RUBBISH_FADE_DIST) alpha -= alpha*(camDist-RUBBISH_FADE_DIST)/(RUBBISH_MAX_DIST-RUBBISH_FADE_DIST); alpha = (RubbishVisibility*alpha)/256; float vx1, vy1, vx2, vy2; if(type == 0 || type == 1){ vx1 = 0.9f*Sin(sheet->m_angle); vy1 = 0.9f*Cos(sheet->m_angle); vx2 = 0.3f*Cos(sheet->m_angle); vy2 = -0.3f*Sin(sheet->m_angle); }else{ vx1 = 0.3f*Sin(sheet->m_angle); vy1 = 0.3f*Cos(sheet->m_angle); vx2 = 0.3f*Cos(sheet->m_angle); vy2 = -0.3f*Sin(sheet->m_angle); } int v = TempBufferVerticesStored; RwIm3DVertexSetPos(&TempBufferRenderVertices[v+0], pos.x + vx1 + vx2, pos.y + vy1 + vy2, pos.z); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+1], pos.x + vx1 - vx2, pos.y + vy1 - vy2, pos.z); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+2], pos.x - vx1 + vx2, pos.y - vy1 + vy2, pos.z); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+3], pos.x - vx1 - vx2, pos.y - vy1 - vy2, pos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+0], 255, 255, 255, alpha); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+1], 255, 255, 255, alpha); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+2], 255, 255, 255, alpha); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+3], 255, 255, 255, alpha); RwIm3DVertexSetU(&TempBufferRenderVertices[v+0], 0.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[v+0], 0.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[v+1], 1.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[v+1], 0.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[v+2], 0.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[v+2], 1.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[v+3], 1.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[v+3], 1.0f); int i = TempBufferIndicesStored; TempBufferRenderIndexList[i+0] = RubbishIndexList[0] + TempBufferVerticesStored; TempBufferRenderIndexList[i+1] = RubbishIndexList[1] + TempBufferVerticesStored; TempBufferRenderIndexList[i+2] = RubbishIndexList[2] + TempBufferVerticesStored; TempBufferRenderIndexList[i+3] = RubbishIndexList[3] + TempBufferVerticesStored; TempBufferRenderIndexList[i+4] = RubbishIndexList[4] + TempBufferVerticesStored; TempBufferRenderIndexList[i+5] = RubbishIndexList[5] + TempBufferVerticesStored; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; } } if(TempBufferIndicesStored != 0){ LittleTest(); if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } } } RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); POP_RENDERGROUP(); } void CRubbish::StirUp(CVehicle *veh) { if((CTimer::GetFrameCounter() ^ (veh->m_randomSeed&3)) == 0) return; if(Abs(veh->GetPosition().x - TheCamera.GetPosition().x) < 20.0f && Abs(veh->GetPosition().y - TheCamera.GetPosition().y) < 20.0f) if(Abs(veh->GetMoveSpeed().x) > 0.05f || Abs(veh->GetMoveSpeed().y) > 0.05f){ float speed = veh->GetMoveSpeed().Magnitude2D(); if(speed > 0.05f){ bool movingForward = DotProduct2D(veh->GetMoveSpeed(), veh->GetForward()) > 0.0f; COneSheet *sheet = StartStaticsList.m_next; CVector2D size = veh->GetColModel()->boundingBox.max; // Check all static sheets while(sheet != &EndStaticsList){ COneSheet *next = sheet->m_next; CVector2D carToSheet = sheet->m_basePos - veh->GetPosition(); float distFwd = DotProduct2D(carToSheet, veh->GetForward()); // sheet has to be a bit behind car if(movingForward && distFwd < -0.5f*size.y && distFwd > -1.5f*size.y || !movingForward && distFwd > 0.5f*size.y && distFwd < 1.5f*size.y){ float distSide = Abs(DotProduct2D(carToSheet, veh->GetRight())); if(distSide < 1.5*size.x){ // Check with higher speed for sheet directly behind car float speedToCheck = distSide < size.x ? speed : speed*0.5f; if(speedToCheck > 0.05f){ sheet->m_state = 2; if(speedToCheck > 0.15f) sheet->m_animationType = 2; else sheet->m_animationType = 1; sheet->m_moveDuration = 2000; sheet->m_xDist = veh->GetMoveSpeed().x; sheet->m_yDist = veh->GetMoveSpeed().y; float dist = Sqrt(SQR(sheet->m_xDist)+SQR(sheet->m_yDist)); sheet->m_xDist *= 25.0f*speed/dist; sheet->m_yDist *= 25.0f*speed/dist; sheet->m_animHeight = 3.0f*speed; sheet->m_moveStart = CTimer::GetTimeInMilliseconds(); float tx = sheet->m_basePos.x + sheet->m_xDist; float ty = sheet->m_basePos.y + sheet->m_yDist; float tz = sheet->m_basePos.z + 3.0f; sheet->m_targetZ = CWorld::FindGroundZFor3DCoord(tx, ty, tz, nil) + 0.1f; sheet->RemoveFromList(); sheet->AddToList(&StartMoversList); } } } sheet = next; } } } } static float aAnimations[3][34] = { { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, // Normal move { 0.0f, 0.05f, 0.12f, 0.25f, 0.42f, 0.57f, 0.68f, 0.8f, 0.86f, 0.9f, 0.93f, 0.95f, 0.96f, 0.97f, 0.98f, 0.99f, 1.0f, // XY movemnt 0.15f, 0.35f, 0.6f, 0.9f, 1.2f, 1.25f, 1.3f, 1.2f, 1.1f, 0.95f, 0.8f, 0.6f, 0.45f, 0.3f, 0.2f, 0.1f, 0 }, // Z movement // Stirred up by fast vehicle { 0.0f, 0.05f, 0.12f, 0.25f, 0.42f, 0.57f, 0.68f, 0.8f, 0.95f, 1.1f, 1.15f, 1.18f, 1.15f, 1.1f, 1.05f, 1.03f, 1.0f, 0.15f, 0.35f, 0.6f, 0.9f, 1.2f, 1.25f, 1.3f, 1.2f, 1.1f, 0.95f, 0.8f, 0.6f, 0.45f, 0.3f, 0.2f, 0.1f, 0 } }; void CRubbish::Update(void) { bool foundGround; // FRAMETIME if(bRubbishInvisible) RubbishVisibility = Max(RubbishVisibility-5, 0); else RubbishVisibility = Min(RubbishVisibility+5, 255); // Spawn a new sheet COneSheet *sheet = StartEmptyList.m_next; if(sheet != &EndEmptyList){ float spawnDist; float spawnAngle; spawnDist = (CGeneral::GetRandomNumber()&0xFF)/256.0f + RUBBISH_MAX_DIST; uint8 r = CGeneral::GetRandomNumber(); if(r&1) spawnAngle = (CGeneral::GetRandomNumber()&0xFF)/256.0f * 6.28f; else spawnAngle = (r-128)/160.0f + TheCamera.Orientation; sheet->m_basePos.x = TheCamera.GetPosition().x + spawnDist*Sin(spawnAngle); sheet->m_basePos.y = TheCamera.GetPosition().y + spawnDist*Cos(spawnAngle); sheet->m_basePos.z = CWorld::FindGroundZFor3DCoord(sheet->m_basePos.x, sheet->m_basePos.y, TheCamera.GetPosition().z, &foundGround) + 0.1f; if(foundGround){ // Found ground, so add to statics list sheet->m_angle = (CGeneral::GetRandomNumber()&0xFF)/256.0f * 6.28f; sheet->m_state = 1; if(CCullZones::FindAttributesForCoors(sheet->m_basePos, nil) & ATTRZONE_NORAIN) sheet->m_isVisible = false; else sheet->m_isVisible = true; sheet->RemoveFromList(); sheet->AddToList(&StartStaticsList); } } // Process animation sheet = StartMoversList.m_next; while(sheet != &EndMoversList){ uint32 currentTime = CTimer::GetTimeInMilliseconds() - sheet->m_moveStart; if(currentTime < sheet->m_moveDuration){ // Animation int step = 16 * currentTime / sheet->m_moveDuration; // 16 steps in animation int stepTime = sheet->m_moveDuration/16; // time in each step float s = (float)(currentTime - stepTime*step) / stepTime; // position on step float t = (float)currentTime / sheet->m_moveDuration; // position on total animation // factors for xy and z-movment float fxy = aAnimations[sheet->m_animationType][step]*(1.0f-s) + aAnimations[sheet->m_animationType][step+1]*s; float fz = aAnimations[sheet->m_animationType][step+17]*(1.0f-s) + aAnimations[sheet->m_animationType][step+1+17]*s; sheet->m_animatedPos.x = sheet->m_basePos.x + fxy*sheet->m_xDist; sheet->m_animatedPos.y = sheet->m_basePos.y + fxy*sheet->m_yDist; sheet->m_animatedPos.z = (1.0f-t)*sheet->m_basePos.z + t*sheet->m_targetZ + fz*sheet->m_animHeight; sheet->m_angle += CTimer::GetTimeStep()*0.04f; if(sheet->m_angle > 6.28f) sheet->m_angle -= 6.28f; sheet = sheet->m_next; }else{ // End of animation, back into statics list sheet->m_basePos.x += sheet->m_xDist; sheet->m_basePos.y += sheet->m_yDist; sheet->m_basePos.z = sheet->m_targetZ; sheet->m_state = 1; sheet->m_isVisible = sheet->m_targetIsVisible; COneSheet *next = sheet->m_next; sheet->RemoveFromList(); sheet->AddToList(&StartStaticsList); sheet = next; } } // Stir up a sheet by wind // FRAMETIME int freq; if(CWeather::Wind < 0.1f) freq = 31; else if(CWeather::Wind < 0.4f) freq = 7; else if(CWeather::Wind < 0.7f) freq = 1; else freq = 0; if((CTimer::GetFrameCounter() & freq) == 0){ // Pick a random sheet and set animation state if static int i = CGeneral::GetRandomNumber() % NUM_RUBBISH_SHEETS; if(aSheets[i].m_state == 1){ aSheets[i].m_moveStart = CTimer::GetTimeInMilliseconds(); aSheets[i].m_moveDuration = CWeather::Wind*1500.0f + 1000.0f; aSheets[i].m_animHeight = 0.2f; aSheets[i].m_xDist = 3.0f*CWeather::Wind; aSheets[i].m_yDist = 3.0f*CWeather::Wind; // Check if target position is ok float tx = aSheets[i].m_basePos.x + aSheets[i].m_xDist; float ty = aSheets[i].m_basePos.y + aSheets[i].m_yDist; float tz = aSheets[i].m_basePos.z + 3.0f; aSheets[i].m_targetZ = CWorld::FindGroundZFor3DCoord(tx, ty, tz, &foundGround) + 0.1f; if(CCullZones::FindAttributesForCoors(CVector(tx, ty, aSheets[i].m_targetZ), nil) & ATTRZONE_NORAIN) aSheets[i].m_targetIsVisible = false; else aSheets[i].m_targetIsVisible = true; if(foundGround){ // start animation aSheets[i].m_state = 2; aSheets[i].m_animationType = 1; aSheets[i].RemoveFromList(); aSheets[i].AddToList(&StartMoversList); } } } // Remove sheets that are too far away int i = (CTimer::GetFrameCounter()%(NUM_RUBBISH_SHEETS/4))*4; int last = ((CTimer::GetFrameCounter()%(NUM_RUBBISH_SHEETS/4)) + 1)*4; for(; i < last; i++){ if(aSheets[i].m_state == 1 && (aSheets[i].m_basePos - TheCamera.GetPosition()).MagnitudeSqr2D() > SQR(RUBBISH_MAX_DIST+1.0f)){ aSheets[i].m_state = 0; aSheets[i].RemoveFromList(); aSheets[i].AddToList(&StartEmptyList); } } } void CRubbish::SetVisibility(bool visible) { bRubbishInvisible = !visible; } void CRubbish::Init(void) { int i; for(i = 0; i < NUM_RUBBISH_SHEETS; i++){ aSheets[i].m_state = 0; if(i < NUM_RUBBISH_SHEETS-1) aSheets[i].m_next = &aSheets[i+1]; else aSheets[i].m_next = &EndEmptyList; if(i > 0) aSheets[i].m_prev = &aSheets[i-1]; else aSheets[i].m_prev = &StartEmptyList; } StartEmptyList.m_next = &aSheets[0]; StartEmptyList.m_prev = nil; EndEmptyList.m_next = nil; EndEmptyList.m_prev = &aSheets[NUM_RUBBISH_SHEETS-1]; StartStaticsList.m_next = &EndStaticsList; StartStaticsList.m_prev = nil; EndStaticsList.m_next = nil; EndStaticsList.m_prev = &StartStaticsList; StartMoversList.m_next = &EndMoversList; StartMoversList.m_prev = nil; EndMoversList.m_next = nil; EndMoversList.m_prev = &StartMoversList; RubbishIndexList[0] = 0; RubbishIndexList[1] = 1; RubbishIndexList[2] = 2; RubbishIndexList[3] = 1; RubbishIndexList[4] = 3; RubbishIndexList[5] = 2; CTxdStore::PushCurrentTxd(); int slot = CTxdStore::FindTxdSlot("particle"); CTxdStore::SetCurrentTxd(slot); gpRubbishTexture[0] = RwTextureRead("gameleaf01_64", nil); gpRubbishTexture[1] = RwTextureRead("gameleaf02_64", nil); gpRubbishTexture[2] = RwTextureRead("newspaper01_64", nil); gpRubbishTexture[3] = RwTextureRead("newspaper02_64", nil); CTxdStore::PopCurrentTxd(); RubbishVisibility = 255; bRubbishInvisible = false; } void CRubbish::Shutdown(void) { RwTextureDestroy(gpRubbishTexture[0]); gpRubbishTexture[0] = nil; RwTextureDestroy(gpRubbishTexture[1]); gpRubbishTexture[1] = nil; RwTextureDestroy(gpRubbishTexture[2]); gpRubbishTexture[2] = nil; RwTextureDestroy(gpRubbishTexture[3]); gpRubbishTexture[3] = nil; } ================================================ FILE: src/render/Rubbish.h ================================================ #pragma once class CVehicle; enum { // NB: not all values are allowed, check the code #ifdef SQUEEZE_PERFORMANCE NUM_RUBBISH_SHEETS = 32 #else NUM_RUBBISH_SHEETS = 64 #endif }; class COneSheet { public: CVector m_basePos; CVector m_animatedPos; float m_targetZ; int8 m_state; int8 m_animationType; uint32 m_moveStart; uint32 m_moveDuration; float m_animHeight; float m_xDist; float m_yDist; float m_angle; bool m_isVisible; bool m_targetIsVisible; COneSheet *m_next; COneSheet *m_prev; void AddToList(COneSheet *list); void RemoveFromList(void); }; class CRubbish { static bool bRubbishInvisible; static int RubbishVisibility; static COneSheet aSheets[NUM_RUBBISH_SHEETS]; static COneSheet StartEmptyList; static COneSheet EndEmptyList; static COneSheet StartStaticsList; static COneSheet EndStaticsList; static COneSheet StartMoversList; static COneSheet EndMoversList; public: static void Render(void); static void StirUp(CVehicle *veh); // CAutomobile on PS2 static void Update(void); static void SetVisibility(bool visible); static void Init(void); static void Shutdown(void); }; extern RwTexture *gpRubbishTexture[4]; ================================================ FILE: src/render/ShadowCamera.cpp ================================================ #include "common.h" #include "rwcore.h" #include "ShadowCamera.h" #include "RwHelper.h" #define TEXELOFFSET 0.5f RpAtomic *ShadowRenderCallBack(RpAtomic *atomic, void *data) { RpAtomicCallBackRender savedCB = RpAtomicGetRenderCallBack(atomic); RpAtomicSetRenderCallBack(atomic, AtomicDefaultRenderCallBack); RpAtomicRender(atomic); RpAtomicSetRenderCallBack(atomic, savedCB); return atomic; } CShadowCamera::CShadowCamera() { m_pCamera = nil; m_pTexture = nil; } CShadowCamera::~CShadowCamera() { Destroy(); } void CShadowCamera::Destroy() { if ( m_pCamera ) { RwRaster *raster; RwFrame *frame; frame = RwCameraGetFrame(m_pCamera); if ( frame ) { RwCameraSetFrame(m_pCamera, nil); RwFrameDestroy(frame); } raster = RwCameraGetZRaster(m_pCamera); if ( raster ) { RwCameraSetZRaster(m_pCamera, nil); RwRasterDestroy(raster); } raster = RwCameraGetRaster(m_pCamera); if ( raster ) { RwCameraSetRaster(m_pCamera, nil); RwRasterDestroy(raster); } if ( m_pTexture ) { RwTextureSetRaster(m_pTexture, nil); RwTextureDestroy(m_pTexture); m_pTexture = nil; } RwCameraDestroy(m_pCamera); m_pCamera = nil; } return; } RwCamera * CShadowCamera::Create(int32 rasterSize) { int32 size = 1 << rasterSize; m_pCamera = RwCameraCreate(); ASSERT(m_pCamera != nil); if ( m_pCamera ) { RwCameraSetFrame(m_pCamera, RwFrameCreate()); if( RwCameraGetFrame(m_pCamera) ) { RwRaster *zRaster = RwRasterCreate(size, size, 0, rwRASTERTYPEZBUFFER); ASSERT(zRaster != nil); if ( zRaster ) { RwCameraSetZRaster(m_pCamera, zRaster); RwRaster *raster = RwRasterCreate(size, size, 0, rwRASTERTYPECAMERATEXTURE); ASSERT(raster != nil); if ( raster ) { RwCameraSetRaster(m_pCamera, raster); m_pTexture = RwTextureCreate(raster); ASSERT(m_pTexture != nil); if ( m_pTexture ) { RwTextureSetAddressing(m_pTexture, rwTEXTUREADDRESSCLAMP); RwTextureSetFilterMode(m_pTexture, rwFILTERLINEAR); RwCameraSetProjection(m_pCamera, rwPARALLEL); return (m_pCamera); } } } } } Destroy(); return (nil); } RwCamera * CShadowCamera::SetFrustum(float objectRadius) { ASSERT(m_pCamera != nil); RwV2d vw; RwCameraSetFarClipPlane (m_pCamera, 2.0f * objectRadius); RwCameraSetNearClipPlane(m_pCamera, 0.001f * objectRadius); vw.x = objectRadius; vw.y = objectRadius; RwCameraSetViewWindow(m_pCamera, &vw); return m_pCamera; } RwCamera * CShadowCamera::SetLight(RpLight *light) { ASSERT(light != nil); ASSERT(m_pCamera != nil); RwFrame *camFrame = RwCameraGetFrame(m_pCamera); RwMatrix *camMatrix = RwFrameGetMatrix(camFrame); RwFrame *lightFrame = RpLightGetFrame(light); RwMatrix *lightMatrix = RwFrameGetMatrix(lightFrame); *RwMatrixGetRight(camMatrix) = *RwMatrixGetRight(lightMatrix); *RwMatrixGetUp(camMatrix) = *RwMatrixGetUp(lightMatrix); *RwMatrixGetAt(camMatrix) = *RwMatrixGetAt(lightMatrix); RwMatrixUpdate(camMatrix); RwFrameUpdateObjects(camFrame); return m_pCamera; } RwCamera * CShadowCamera::SetCenter(RwV3d *center) { ASSERT(center != nil); ASSERT(m_pCamera != nil); RwFrame *camFrame = RwCameraGetFrame(m_pCamera); RwMatrix *camMatrix = RwFrameGetMatrix(camFrame); *RwMatrixGetPos(camMatrix) = *center; RwV3dIncrementScaled(RwMatrixGetPos(camMatrix), RwMatrixGetAt(camMatrix), -0.5f * RwCameraGetFarClipPlane(m_pCamera)); RwMatrixUpdate(camMatrix); RwFrameUpdateObjects(camFrame); RwFrameOrthoNormalize(camFrame); return m_pCamera; } RwCamera * CShadowCamera::Update(RpClump *clump) { ASSERT(clump != nil); ASSERT(m_pCamera != nil); RwUInt32 flags; RpGeometry *geometry; RwRGBA bgColor = { 255, 255, 255, 0 }; RwCameraClear(m_pCamera, &bgColor, rwCAMERACLEARZ | rwCAMERACLEARIMAGE); if ( RwCameraBeginUpdate(m_pCamera) ) { geometry = RpAtomicGetGeometry(GetFirstAtomic(clump)); ASSERT(geometry != nil); flags = RpGeometryGetFlags(geometry); RpGeometrySetFlags(geometry, flags & ~(rpGEOMETRYPRELIT|rpGEOMETRYLIGHT |rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2|rpGEOMETRYMODULATEMATERIALCOLOR)); RpClumpForAllAtomics(clump, ShadowRenderCallBack, nil); RpGeometrySetFlags(geometry, flags); InvertRaster(); RwCameraEndUpdate(m_pCamera); } return m_pCamera; } RwCamera * CShadowCamera::Update(RpAtomic *atomic) { ASSERT(atomic != nil); ASSERT(m_pCamera != nil); RwUInt32 flags; RpGeometry *geometry; RwRGBA bgColor = { 255, 255, 255, 0 }; RwCameraClear(m_pCamera, &bgColor, rwCAMERACLEARZ | rwCAMERACLEARIMAGE); if ( RwCameraBeginUpdate(m_pCamera) ) { geometry = RpAtomicGetGeometry(atomic); ASSERT(geometry != nil); flags = RpGeometryGetFlags(geometry); RpGeometrySetFlags(geometry, flags & ~(rpGEOMETRYPRELIT|rpGEOMETRYLIGHT |rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2|rpGEOMETRYMODULATEMATERIALCOLOR|rpGEOMETRYNORMALS)); ShadowRenderCallBack(atomic, nil); RpGeometrySetFlags(geometry, flags); InvertRaster(); RwCameraEndUpdate(m_pCamera); } return m_pCamera; } void CShadowCamera::InvertRaster() { ASSERT(m_pCamera != nil); RwIm2DVertex vx[4]; float crw, crh; RwRaster *raster; float recipZ; raster = RwCameraGetRaster(m_pCamera); ASSERT(raster != nil); crw = (float)RwRasterGetWidth(raster); crh = (float)RwRasterGetHeight(raster); recipZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); RwIm2DVertexSetScreenX (&vx[0], 0.0f); RwIm2DVertexSetScreenY (&vx[0], 0.0f); RwIm2DVertexSetScreenZ (&vx[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetRecipCameraZ(&vx[0], recipZ); RwIm2DVertexSetIntRGBA (&vx[0], 255, 255, 255, 255); RwIm2DVertexSetScreenX (&vx[1], 0.0f); RwIm2DVertexSetScreenY (&vx[1], crh); RwIm2DVertexSetScreenZ (&vx[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetRecipCameraZ(&vx[1], recipZ); RwIm2DVertexSetIntRGBA (&vx[1], 255, 255, 255, 255); RwIm2DVertexSetScreenX (&vx[2], crw); RwIm2DVertexSetScreenY (&vx[2], 0.0f); RwIm2DVertexSetScreenZ (&vx[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetRecipCameraZ(&vx[2], recipZ); RwIm2DVertexSetIntRGBA (&vx[2], 255, 255, 255, 255); RwIm2DVertexSetScreenX (&vx[3], crw); RwIm2DVertexSetScreenY (&vx[3], crh); RwIm2DVertexSetScreenZ (&vx[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetRecipCameraZ(&vx[3], recipZ); RwIm2DVertexSetIntRGBA (&vx[3], 255, 255, 255, 255); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)nil); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDINVDESTCOLOR); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); RwIm2DRenderPrimitive(rwPRIMTYPETRISTRIP, vx, 4); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); } RwRaster * CShadowCamera::MakeGradientRaster() { ASSERT(m_pCamera != nil); RwIm2DVertex vx[2]; if ( !m_pCamera ) return nil; float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); RwRaster *raster = RwCameraGetRaster(m_pCamera); ASSERT(raster != nil); float width = (float)RwRasterGetWidth(raster); float height = (float)RwRasterGetHeight(raster); if ( height < 1 ) return nil; if ( RwCameraBeginUpdate(m_pCamera) ) { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)rwFILTERNAFILTERMODE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDINVDESTCOLOR); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void *)rwSHADEMODEFLAT); float color = 255.0f; float step = (-191.0f / height); for ( int32 i = 0; i < height; i++ ) { RwIm2DVertexSetScreenX (&vx[0], 0.0f); RwIm2DVertexSetScreenY (&vx[0], i); RwIm2DVertexSetScreenZ (&vx[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetRecipCameraZ(&vx[0], recipCamZ); RwIm2DVertexSetIntRGBA (&vx[0], (uint32)color, (uint32)color, (uint32)color, (uint32)color); RwIm2DVertexSetScreenX (&vx[1], width - 1); RwIm2DVertexSetScreenY (&vx[1], i); RwIm2DVertexSetScreenZ (&vx[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetRecipCameraZ(&vx[1], recipCamZ); RwIm2DVertexSetIntRGBA (&vx[1], (uint32)color, (uint32)color, (uint32)color, (uint32)color); RwIm2DRenderLine(vx, 2, 0, 1); color += step; } RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void *)rwSHADEMODEGOURAUD); RwCameraEndUpdate(m_pCamera); } return raster; } RwRaster * CShadowCamera::RasterResample(RwRaster *dstRaster) { ASSERT(dstRaster != nil); ASSERT(m_pCamera != nil); if ( !m_pCamera ) return nil; RwRaster *raster = RwCameraGetRaster(m_pCamera); ASSERT(raster != nil); float size = (float) RwRasterGetWidth(raster); float uvOffset = TEXELOFFSET / size; float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); if ( RwCameraBeginUpdate(m_pCamera) ) { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)dstRaster); Im2DRenderQuad(0.0f, 0.0f, size, size, RwIm2DGetNearScreenZ(), recipCamZ, uvOffset); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); RwCameraEndUpdate(m_pCamera); } return raster; } RwRaster * CShadowCamera::RasterBlur(RwRaster *dstRaster, int32 numPasses) { ASSERT(dstRaster != nil); ASSERT(m_pCamera != nil); if ( !m_pCamera ) return nil; RwRaster *raster = RwCameraGetRaster(m_pCamera); ASSERT(raster != nil); float size = (float) RwRasterGetWidth(dstRaster); float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); for (int i = 0; i < numPasses; i++ ) { RwCameraSetRaster(m_pCamera, raster); if ( RwCameraBeginUpdate(m_pCamera) ) { if ( i == 0 ) { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); } RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)dstRaster); Im2DRenderQuad(0.0f, 0.0f, size, size, RwIm2DGetNearScreenZ(), recipCamZ, 1.0f / size); RwCameraEndUpdate(m_pCamera); } RwCameraSetRaster(m_pCamera, dstRaster); if ( RwCameraBeginUpdate(m_pCamera) ) { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)raster); Im2DRenderQuad(0.0f, 0.0f, size, size, RwIm2DGetNearScreenZ(), recipCamZ, 0); if ( i == numPasses - 1 ) { RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); } RwCameraEndUpdate(m_pCamera); } } RwCameraSetRaster(m_pCamera, raster); return dstRaster; } RwRaster * CShadowCamera::RasterGradient(RwRaster *dstRaster) { ASSERT(dstRaster != nil); ASSERT(m_pCamera != nil); RwRaster *raster = RwCameraGetRaster(m_pCamera); ASSERT(raster != nil); float size = (float)RwRasterGetWidth(dstRaster); float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); RwCameraSetRaster(m_pCamera, dstRaster); if ( RwCameraBeginUpdate(m_pCamera) ) { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDSRCCOLOR); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void *)rwFILTERLINEAR); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)raster); Im2DRenderQuad(0, 0, size, size, RwIm2DGetNearScreenZ(), recipCamZ, 0); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); RwCameraEndUpdate(m_pCamera); } RwCameraSetRaster(m_pCamera, raster); return dstRaster; } RwRaster *CShadowCamera::DrawOutlineBorder(RwRGBA const& color) { ASSERT(m_pCamera != nil); RwIm2DVertex vx[4]; RwImVertexIndex ix[5]; RwRaster *raster = RwCameraGetRaster(m_pCamera); ASSERT(raster != nil); float size = (float)RwRasterGetWidth(raster) - 1.0f; float recipCamZ = 1.0f / RwCameraGetNearClipPlane(m_pCamera); RwIm2DVertexSetScreenX (&vx[0], 0.0f); RwIm2DVertexSetScreenY (&vx[0], 0.0f); RwIm2DVertexSetScreenZ (&vx[0], RwIm2DGetNearScreenZ()); RwIm2DVertexSetIntRGBA (&vx[0], color.red, color.green, color.blue, color.alpha); RwIm2DVertexSetRecipCameraZ(&vx[0], recipCamZ); RwIm2DVertexSetScreenX (&vx[1], size); RwIm2DVertexSetScreenY (&vx[1], 0.0f); RwIm2DVertexSetScreenZ (&vx[1], RwIm2DGetNearScreenZ()); RwIm2DVertexSetIntRGBA (&vx[1], color.red, color.green, color.blue, color.alpha); RwIm2DVertexSetRecipCameraZ(&vx[1], recipCamZ); RwIm2DVertexSetScreenX (&vx[2], size); RwIm2DVertexSetScreenY (&vx[2], size); RwIm2DVertexSetScreenZ (&vx[2], RwIm2DGetNearScreenZ()); RwIm2DVertexSetIntRGBA (&vx[2], color.red, color.green, color.blue, color.alpha); RwIm2DVertexSetRecipCameraZ(&vx[2], recipCamZ); RwIm2DVertexSetScreenX (&vx[3], 0.0f); RwIm2DVertexSetScreenY (&vx[3], size); RwIm2DVertexSetScreenZ (&vx[3], RwIm2DGetNearScreenZ()); RwIm2DVertexSetIntRGBA (&vx[3], color.red, color.green, color.blue, color.alpha); RwIm2DVertexSetRecipCameraZ(&vx[3], recipCamZ); ix[0] = 0; ix[4] = 0; ix[1] = 1; ix[2] = 2; ix[3] = 3; if ( RwCameraBeginUpdate(m_pCamera) ) { RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)nil); RwIm2DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, vx, 4, ix, 5); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwCameraEndUpdate(m_pCamera); } return raster; } ================================================ FILE: src/render/ShadowCamera.h ================================================ #pragma once class CShadowCamera { public: RwCamera *m_pCamera; RwTexture *m_pTexture; CShadowCamera(); ~CShadowCamera(); RwCamera *Create(int32 rasterSize); void Destroy(); RwCamera *SetFrustum(float objectRadius); RwCamera *SetLight(RpLight *light); RwCamera *SetCenter(RwV3d *center); RwCamera *Update(RpClump *clump); RwCamera *Update(RpAtomic *atomic); void InvertRaster(); RwRaster* GetRwRenderRaster() { return RwCameraGetRaster(m_pCamera); } // ShadowRasterRender(RwV2d *) // ApplyAlphaMapToRaster(void) RwRaster *MakeGradientRaster(); RwTexture *GetRwRenderTexture() { return m_pTexture; } RwRaster* GetRwZRaster() { return RwCameraGetZRaster(m_pCamera); } RwRaster *RasterResample(RwRaster *dstRaster); RwRaster *RasterBlur(RwRaster *dstRaster, int32 numPasses); RwRaster *RasterGradient(RwRaster *dstRaster); RwRaster *DrawOutlineBorder(RwRGBA const& color); RwCamera *GetRwCamera() { return m_pCamera; } }; ================================================ FILE: src/render/Shadows.cpp ================================================ #include "common.h" #include "main.h" #include "TxdStore.h" #include "Timer.h" #include "Camera.h" #include "Timecycle.h" #include "CutsceneMgr.h" #include "Automobile.h" #include "Bike.h" #include "Ped.h" #include "PlayerPed.h" #include "World.h" #include "Weather.h" #include "ModelIndices.h" #include "RenderBuffer.h" #ifdef FIX_BUGS #include "Replay.h" #endif #include "PointLights.h" #include "SpecialFX.h" #include "Script.h" #include "TimeStep.h" #include "Shadows.h" #include "CutsceneObject.h" #include "CutsceneShadow.h" #include "Clock.h" #include "VarConsole.h" #ifdef DEBUGMENU //SETTWEAKPATH("Shadows"); //TWEAKBOOL(gbPrintShite); #endif RwImVertexIndex ShadowIndexList[24]; RwTexture *gpShadowCarTex; RwTexture *gpShadowPedTex; RwTexture *gpShadowHeliTex; RwTexture *gpShadowBikeTex; RwTexture *gpShadowBaronTex; RwTexture *gpShadowExplosionTex; RwTexture *gpShadowHeadLightsTex; RwTexture *gpOutline1Tex; RwTexture *gpOutline2Tex; RwTexture *gpOutline3Tex; RwTexture *gpBloodPoolTex; RwTexture *gpReflectionTex; RwTexture *gpWalkDontTex; RwTexture *gpCrackedGlassTex; RwTexture *gpPostShadowTex; RwTexture *gpGoalTex; int16 CShadows::ShadowsStoredToBeRendered; CStoredShadow CShadows::asShadowsStored [MAX_STOREDSHADOWS]; CPolyBunch CShadows::aPolyBunches [MAX_POLYBUNCHES]; CStaticShadow CShadows::aStaticShadows [MAX_STATICSHADOWS]; CPolyBunch *CShadows::pEmptyBunchList; CPermanentShadow CShadows::aPermanentShadows[MAX_PERMAMENTSHADOWS]; #ifndef MASTER bool gbCountPolysInShadow; #endif void CShadows::Init(void) { CTxdStore::PushCurrentTxd(); int32 slut = CTxdStore::FindTxdSlot("particle"); CTxdStore::SetCurrentTxd(slut); gpShadowCarTex = RwTextureRead("shad_car", nil); gpShadowPedTex = RwTextureRead("shad_ped", nil); gpShadowHeliTex = RwTextureRead("shad_heli", nil); gpShadowBikeTex = RwTextureRead("shad_bike", nil); gpShadowBaronTex = RwTextureRead("shad_rcbaron", nil); gpShadowExplosionTex = RwTextureRead("shad_exp", nil); gpShadowHeadLightsTex = RwTextureRead("headlight", nil); gpOutline1Tex = RwTextureRead("outline_64", nil); gpOutline2Tex = RwTextureRead("outline2_64", nil); gpOutline3Tex = RwTextureRead("outline3_64", nil); gpBloodPoolTex = RwTextureRead("bloodpool_64", nil); gpReflectionTex = RwTextureRead("reflection01", nil); gpWalkDontTex = RwTextureRead("walk_dont", nil); gpCrackedGlassTex = RwTextureRead("wincrack_32", nil); gpPostShadowTex = RwTextureRead("lamp_shad_64", nil); CTxdStore::PopCurrentTxd(); ASSERT(gpShadowCarTex != nil); ASSERT(gpShadowPedTex != nil); ASSERT(gpShadowHeliTex != nil); ASSERT(gpShadowBikeTex != nil); ASSERT(gpShadowBaronTex != nil); ASSERT(gpShadowExplosionTex != nil); ASSERT(gpShadowHeadLightsTex != nil); ASSERT(gpOutline1Tex != nil); ASSERT(gpOutline2Tex != nil); ASSERT(gpOutline3Tex != nil); ASSERT(gpBloodPoolTex != nil); ASSERT(gpReflectionTex != nil); ASSERT(gpWalkDontTex != nil); ASSERT(gpCrackedGlassTex != nil); ASSERT(gpPostShadowTex != nil); ShadowIndexList[0] = 0; ShadowIndexList[1] = 2; ShadowIndexList[2] = 1; ShadowIndexList[3] = 0; ShadowIndexList[4] = 3; ShadowIndexList[5] = 2; ShadowIndexList[6] = 0; ShadowIndexList[7] = 4; ShadowIndexList[8] = 3; ShadowIndexList[9] = 0; ShadowIndexList[10] = 5; ShadowIndexList[11] = 4; ShadowIndexList[12] = 0; ShadowIndexList[13] = 6; ShadowIndexList[14] = 5; ShadowIndexList[15] = 0; ShadowIndexList[16] = 7; ShadowIndexList[17] = 6; ShadowIndexList[18] = 0; ShadowIndexList[19] = 8; ShadowIndexList[20] = 7; ShadowIndexList[21] = 0; ShadowIndexList[22] = 9; ShadowIndexList[23] = 8; for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) { aStaticShadows[i].m_nId = 0; aStaticShadows[i].m_pPolyBunch = nil; } pEmptyBunchList = &aPolyBunches[0]; for ( int32 i = 0; i < MAX_POLYBUNCHES; i++ ) { if ( i == MAX_POLYBUNCHES - 1 ) aPolyBunches[i].m_pNext = nil; else aPolyBunches[i].m_pNext = &aPolyBunches[i + 1]; } for ( int32 i = 0; i < MAX_PERMAMENTSHADOWS; i++ ) { aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; } #ifndef MASTER VarConsole.Add("Count polys in shadow", &gbCountPolysInShadow, true); #endif } void CShadows::Shutdown(void) { ASSERT(gpShadowCarTex != nil); ASSERT(gpShadowPedTex != nil); ASSERT(gpShadowHeliTex != nil); ASSERT(gpShadowBikeTex != nil); ASSERT(gpShadowBaronTex != nil); ASSERT(gpShadowExplosionTex != nil); ASSERT(gpShadowHeadLightsTex != nil); ASSERT(gpOutline1Tex != nil); ASSERT(gpOutline2Tex != nil); ASSERT(gpOutline3Tex != nil); ASSERT(gpBloodPoolTex != nil); ASSERT(gpReflectionTex != nil); ASSERT(gpWalkDontTex != nil); ASSERT(gpCrackedGlassTex != nil); ASSERT(gpPostShadowTex != nil); RwTextureDestroy(gpShadowCarTex); RwTextureDestroy(gpShadowPedTex); RwTextureDestroy(gpShadowHeliTex); RwTextureDestroy(gpShadowBikeTex); RwTextureDestroy(gpShadowBaronTex); RwTextureDestroy(gpShadowExplosionTex); RwTextureDestroy(gpShadowHeadLightsTex); RwTextureDestroy(gpOutline1Tex); RwTextureDestroy(gpOutline2Tex); RwTextureDestroy(gpOutline3Tex); RwTextureDestroy(gpBloodPoolTex); RwTextureDestroy(gpReflectionTex); RwTextureDestroy(gpWalkDontTex); RwTextureDestroy(gpCrackedGlassTex); RwTextureDestroy(gpPostShadowTex); } void CShadows::AddPermanentShadow(uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, uint32 nTime, float fScale) { ASSERT(pTexture != nil); ASSERT(pPosn != nil); // find free slot int32 nSlot = 0; while ( nSlot < MAX_PERMAMENTSHADOWS && aPermanentShadows[nSlot].m_nType != SHADOWTYPE_NONE ) nSlot++; if ( nSlot < MAX_PERMAMENTSHADOWS ) { aPermanentShadows[nSlot].m_nType = ShadowType; aPermanentShadows[nSlot].m_pTexture = pTexture; aPermanentShadows[nSlot].m_vecPos = *pPosn; aPermanentShadows[nSlot].m_vecFront.x = fFrontX; aPermanentShadows[nSlot].m_vecFront.y = fFrontY; aPermanentShadows[nSlot].m_vecSide.x = fSideX; aPermanentShadows[nSlot].m_vecSide.y = fSideY; aPermanentShadows[nSlot].m_nIntensity = nIntensity; aPermanentShadows[nSlot].m_nRed = nRed; aPermanentShadows[nSlot].m_nGreen = nGreen; aPermanentShadows[nSlot].m_nBlue = nBlue; aPermanentShadows[nSlot].m_fZDistance = fZDistance; aPermanentShadows[nSlot].m_nLifeTime = nTime; aPermanentShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); } } bool CShadows::StoreStaticShadow(uint32 nID, uint8 ShadowType, RwTexture *pTexture, Const CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, float fDrawDistance, bool bTempShadow, float fUpDistance) { ASSERT(pPosn != nil); float fDistToCamSqr = (*pPosn - TheCamera.GetPosition()).MagnitudeSqr2D(); if ( SQR(fDrawDistance) > fDistToCamSqr || fDrawDistance == 0.0f ) { if ( fDrawDistance != 0.0f ) { float fDistToCam = Sqrt(fDistToCamSqr); if ( fDistToCam >= (fDrawDistance*(1.0f-(1.0f/4.0f))) ) { //fDistToCam == 0 -> 4 //fDistToCam == fDrawDistance -> 0 float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f-(1.0f/4.0f)))); nIntensity = (int32)(nIntensity * fMult); nRed = (int32)(nRed * fMult); nGreen = (int32)(nGreen * fMult); nBlue = (int32)(nBlue * fMult); } } int32 nSlot; nSlot = 0; while ( nSlot < MAX_STATICSHADOWS && !(nID == aStaticShadows[nSlot].m_nId && aStaticShadows[nSlot].m_pPolyBunch != nil) ) nSlot++; if ( nSlot < MAX_STATICSHADOWS ) { if ( Abs(pPosn->x - aStaticShadows[nSlot].m_vecPosn.x) < fUpDistance && Abs(pPosn->y - aStaticShadows[nSlot].m_vecPosn.y) < fUpDistance ) { aStaticShadows[nSlot].m_bJustCreated = true; aStaticShadows[nSlot].m_nType = ShadowType; aStaticShadows[nSlot].m_pTexture = pTexture; aStaticShadows[nSlot].m_nIntensity = nIntensity; aStaticShadows[nSlot].m_nRed = nRed; aStaticShadows[nSlot].m_nGreen = nGreen; aStaticShadows[nSlot].m_nBlue = nBlue; aStaticShadows[nSlot].m_fZDistance = fZDistance; aStaticShadows[nSlot].m_fScale = fScale; aStaticShadows[nSlot].m_bTemp = bTempShadow; aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); return true; } else if ( Abs(pPosn->x - aStaticShadows[nSlot].m_vecPosn.x) < 0.05f && Abs(pPosn->y - aStaticShadows[nSlot].m_vecPosn.y) < 0.05f && Abs(pPosn->z - aStaticShadows[nSlot].m_vecPosn.z) < 2.0f && fFrontX == aStaticShadows[nSlot].m_vecFront.x && fFrontY == aStaticShadows[nSlot].m_vecFront.y && fSideX == aStaticShadows[nSlot].m_vecSide.x && fSideY == aStaticShadows[nSlot].m_vecSide.y ) { aStaticShadows[nSlot].m_bJustCreated = true; aStaticShadows[nSlot].m_nType = ShadowType; aStaticShadows[nSlot].m_pTexture = pTexture; aStaticShadows[nSlot].m_nIntensity = nIntensity; aStaticShadows[nSlot].m_nRed = nRed; aStaticShadows[nSlot].m_nGreen = nGreen; aStaticShadows[nSlot].m_nBlue = nBlue; aStaticShadows[nSlot].m_fZDistance = fZDistance; aStaticShadows[nSlot].m_fScale = fScale; aStaticShadows[nSlot].m_bTemp = bTempShadow; aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); return true; } else { aStaticShadows[nSlot].Free(); aStaticShadows[nSlot].m_nId = nID; aStaticShadows[nSlot].m_nType = ShadowType; aStaticShadows[nSlot].m_pTexture = pTexture; aStaticShadows[nSlot].m_nIntensity = nIntensity; aStaticShadows[nSlot].m_nRed = nRed; aStaticShadows[nSlot].m_nGreen = nGreen; aStaticShadows[nSlot].m_nBlue = nBlue; aStaticShadows[nSlot].m_fZDistance = fZDistance; aStaticShadows[nSlot].m_fScale = fScale; aStaticShadows[nSlot].m_vecPosn = *pPosn; aStaticShadows[nSlot].m_vecFront.x = fFrontX; aStaticShadows[nSlot].m_vecFront.y = fFrontY; aStaticShadows[nSlot].m_vecSide.x = fSideX; aStaticShadows[nSlot].m_vecSide.y = fSideY; aStaticShadows[nSlot].m_bJustCreated = true; aStaticShadows[nSlot].m_bTemp = bTempShadow; aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); GeneratePolysForStaticShadow(nSlot); return aStaticShadows[nSlot].m_pPolyBunch != nil; } } else { nSlot = 0; while ( nSlot < MAX_STATICSHADOWS && aStaticShadows[nSlot].m_pPolyBunch != nil ) nSlot++; if ( nSlot != MAX_STATICSHADOWS ) { aStaticShadows[nSlot].m_nId = nID; aStaticShadows[nSlot].m_nType = ShadowType; aStaticShadows[nSlot].m_pTexture = pTexture; aStaticShadows[nSlot].m_nIntensity = nIntensity; aStaticShadows[nSlot].m_nRed = nRed; aStaticShadows[nSlot].m_nGreen = nGreen; aStaticShadows[nSlot].m_nBlue = nBlue; aStaticShadows[nSlot].m_fZDistance = fZDistance; aStaticShadows[nSlot].m_fScale = fScale; aStaticShadows[nSlot].m_vecPosn = *pPosn; aStaticShadows[nSlot].m_vecFront.x = fFrontX; aStaticShadows[nSlot].m_vecFront.y = fFrontY; aStaticShadows[nSlot].m_vecSide.x = fSideX; aStaticShadows[nSlot].m_vecSide.y = fSideY; aStaticShadows[nSlot].m_bJustCreated = true; aStaticShadows[nSlot].m_bTemp = bTempShadow; aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); GeneratePolysForStaticShadow(nSlot); return aStaticShadows[nSlot].m_pPolyBunch != nil; } } } return true; } void CShadows::StoreShadowToBeRendered(uint8 ShadowTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue) { ASSERT(pPosn != nil); switch ( ShadowTexture ) { case SHADOWTEX_NONE: { break; } case SHADOWTEX_CAR: { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowCarTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_PED: { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowPedTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_EXPLOSION: { StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_HELI: { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowHeliTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_HEADLIGHTS: { StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowHeadLightsTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_BLOOD: { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpBloodPoolTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, 150, 0, 15.0f, false, 1.0f, nil, false); break; } } //ASSERT(false); } void CShadows::StoreShadowToBeRendered(uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, bool bDrawOnWater, float fScale, CCutsceneShadow *pShadow, bool bDrawOnBuildings) { ASSERT(pTexture != nil); ASSERT(pPosn != nil); if ( ShadowsStoredToBeRendered < MAX_STOREDSHADOWS ) { asShadowsStored[ShadowsStoredToBeRendered].m_ShadowType = ShadowType; asShadowsStored[ShadowsStoredToBeRendered].m_pTexture = pTexture; asShadowsStored[ShadowsStoredToBeRendered].m_vecPos = *pPosn; asShadowsStored[ShadowsStoredToBeRendered].m_vecFront.x = fFrontX; asShadowsStored[ShadowsStoredToBeRendered].m_vecFront.y = fFrontY; asShadowsStored[ShadowsStoredToBeRendered].m_vecSide.x = fSideX; asShadowsStored[ShadowsStoredToBeRendered].m_vecSide.y = fSideY; asShadowsStored[ShadowsStoredToBeRendered].m_nIntensity = nIntensity; asShadowsStored[ShadowsStoredToBeRendered].m_nRed = nRed; asShadowsStored[ShadowsStoredToBeRendered].m_nGreen = nGreen; asShadowsStored[ShadowsStoredToBeRendered].m_nBlue = nBlue; asShadowsStored[ShadowsStoredToBeRendered].m_fZDistance = fZDistance; asShadowsStored[ShadowsStoredToBeRendered].m_nFlags.bDrawOnWater = bDrawOnWater; asShadowsStored[ShadowsStoredToBeRendered].m_nFlags.bDrawOnBuildings = bDrawOnBuildings; asShadowsStored[ShadowsStoredToBeRendered].m_fScale = fScale; asShadowsStored[ShadowsStoredToBeRendered].m_pCutsceneShadow = pShadow; ShadowsStoredToBeRendered++; } } void CShadows::StoreShadowForVehicle(CVehicle *pCar, VEH_SHD_TYPE type) { ASSERT(pCar != nil); if ( CTimeCycle::GetShadowStrength() != 0 ) { CVector CarPos = pCar->GetPosition(); float fDistToCamSqr = (CarPos - TheCamera.GetPosition()).MagnitudeSqr2D(); if ( CCutsceneMgr::IsRunning() ) fDistToCamSqr /= SQR(TheCamera.LODDistMultiplier) * 4.0f; float fDrawDistance; switch ( type ) { case VEH_SHD_TYPE_SEAPLANE: case VEH_SHD_TYPE_RCPLANE: fDrawDistance = 144.0f; break; case VEH_SHD_TYPE_HELI: fDrawDistance = 144.0f; break; default: fDrawDistance = 18.0f; break; } if ( fDistToCamSqr < SQR(fDrawDistance) ) { float fDistToCam = Sqrt(fDistToCamSqr); //fDistToCam == 0 -> 4 //fDistToCam == fDrawDistance -> 0 float fMult = 1.0f - (fDistToCam - (fDrawDistance*(1.0f-(1.0f/4.0f)))) / (fDrawDistance*(1.0f/4.0f)); int32 nColorStrength; if ( fDistToCam >= (fDrawDistance*(1.0f-(1.0f/4.0f))) ) nColorStrength = (int32)(fMult * CTimeCycle::GetShadowStrength()); else nColorStrength = CTimeCycle::GetShadowStrength(); float fVehicleHeight = pCar->GetColModel()->boundingBox.GetSize().y; float fVehicleWidth = pCar->GetColModel()->boundingBox.GetSize().x; float size = 1.0f; if ( pCar->GetModelIndex() == MI_HUNTER ) { fVehicleWidth *= 3.0f; fVehicleHeight *= 1.4f; size *= 0.5f; } else if ( pCar->GetModelIndex() == MI_ANGEL ) { fVehicleHeight = fVehicleHeight * 1.5f; size = 0.03f; } else if ( pCar->GetModelIndex() == MI_SEASPAR ) { fVehicleWidth *= 3.0f; fVehicleHeight *= 1.4f; size *= 0.5f; } else if ( pCar->GetModelIndex() == MI_PIZZABOY || pCar->GetModelIndex() == MI_PCJ600 || pCar->GetModelIndex() == MI_FAGGIO ) { fVehicleHeight *= 1.2f; size = 0.05f; } else if ( pCar->GetModelIndex() == MI_FREEWAY ) { fVehicleHeight *= 1.5f; size = 0.03f; } else if ( pCar->GetModelIndex() == MI_RCRAIDER ) { fVehicleHeight *= 1.5f; fVehicleWidth *= 2.0f; size = 0.2f; } else if ( pCar->GetModelIndex() == MI_SANCHEZ ) { fVehicleHeight *= 1.5f; size = 0.03f; } else if ( pCar->GetModelIndex() == MI_SPARROW || pCar->GetModelIndex() == MI_MAVERICK || pCar->GetModelIndex() == MI_VCNMAV || pCar->GetModelIndex() == MI_POLMAV ) { fVehicleWidth *= 3.0f; fVehicleHeight *= 1.4f; size *= 0.5f; } else if ( pCar->GetModelIndex() == MI_RCGOBLIN ) { fVehicleHeight *= 1.5f; fVehicleWidth *= 2.0f; size = 0.2f; } else if ( pCar->GetModelIndex() == MI_DODO ) { fVehicleHeight *= 0.9f; fVehicleWidth *= 0.4f; } CarPos.x -= pCar->GetForward().x * (((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y)*size); CarPos.y -= pCar->GetForward().y * (((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y)*size); RwTexture *tex = gpShadowCarTex; switch ( type ) { case VEH_SHD_TYPE_BIKE: { float wheelZ = Abs(((CBike*)pCar)->m_fLeanLRAngle); float mul = 5.092958f * wheelZ + 1.0f; if (pCar->GetStatus() == STATUS_PHYSICS) { float z = pCar->GetRight().z; if (z > 0.6f) mul += 4.0f * z; } fVehicleWidth *= mul; tex = gpShadowBikeTex; break; } case VEH_SHD_TYPE_HELI: tex = gpShadowHeliTex; break; case VEH_SHD_TYPE_SEAPLANE: nColorStrength = CTimeCycle::GetShadowStrength(); tex = gpShadowBaronTex; break; case VEH_SHD_TYPE_RCPLANE: tex = gpShadowBaronTex; fVehicleHeight *= 1.5f; fVehicleWidth *= 2.2f; break; case VEH_SHD_TYPE_CAR: tex = gpShadowCarTex; break; } float frontx = pCar->GetForward().x; float fronty = pCar->GetForward().y; float sidex = pCar->GetRight().x; float sidey = pCar->GetRight().y; switch ( type ) { case VEH_SHD_TYPE_BIKE: if ( Abs(pCar->GetRight().z) > 0.6f ) { sidex = pCar->GetUp().x; sidey = pCar->GetUp().y; } break; case VEH_SHD_TYPE_HELI: if ( Abs(pCar->GetRight().z) > 0.57f ) { sidex = pCar->GetUp().x; sidey = pCar->GetUp().y; } if ( Abs(pCar->GetForward().z) > 0.57f ) { frontx = pCar->GetUp().x; fronty = pCar->GetUp().y; } break; } bool bDrawOnBuildings = false; if ( pCar->GetModelIndex() == MI_RCBANDIT || pCar->GetModelIndex() == MI_RCBARON || pCar->GetModelIndex() == MI_RCRAIDER || pCar->GetModelIndex() == MI_RCGOBLIN || pCar == FindPlayerVehicle() ) { bDrawOnBuildings = true; } if ( pCar->m_vecMoveSpeed.Magnitude() * CTimeStep::ms_fTimeStep > 0.1f || bDrawOnBuildings ) { if ( pCar->GetUp().z > 0.0f ) { StoreShadowToBeRendered(SHADOWTYPE_DARK, tex, &CarPos, frontx * (fVehicleHeight / 2), fronty * (fVehicleHeight / 2), sidex * (fVehicleWidth / 2), sidey * (fVehicleWidth / 2), nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.5f, false, 1.0f, nil, bDrawOnBuildings); } else { StoreShadowToBeRendered(SHADOWTYPE_DARK, tex, &CarPos, frontx * (fVehicleHeight / 2), fronty * (fVehicleHeight / 2), -sidex * (fVehicleWidth / 2), -sidey * (fVehicleWidth / 2), nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.5f, false, 1.0f, nil, bDrawOnBuildings); } } else { if ( pCar->GetUp().z > 0.0f ) { StoreStaticShadow((uintptr)pCar + 1, SHADOWTYPE_DARK, tex, &CarPos, frontx * (fVehicleHeight / 2), fronty * (fVehicleHeight / 2), sidex * (fVehicleWidth / 2), sidey * (fVehicleWidth / 2), nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.5f, 1.0f, 0.0f, false, 0.1f); } else { StoreStaticShadow((uintptr)pCar + 1, SHADOWTYPE_DARK, tex, &CarPos, frontx * (fVehicleHeight / 2), fronty * (fVehicleHeight / 2), -sidex * (fVehicleWidth / 2), -sidey * (fVehicleWidth / 2), nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.5f, 1.0f, 0.0f, false, 0.1f); } } } } } void CShadows::StoreCarLightShadow(CVehicle *pCar, int32 nID, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, uint8 nRed, uint8 nGreen, uint8 nBlue, float fMaxViewAngle) { ASSERT(pCar != nil); ASSERT(pPosn != nil); float fDistToCamSqr = (*pPosn - TheCamera.GetPosition()).MagnitudeSqr2D(); bool bSpecialCam = TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || CCutsceneMgr::IsRunning(); float fDrawDistance = 27.0f; if ( fDistToCamSqr < SQR(fDrawDistance) || bSpecialCam ) { if ( bSpecialCam || DotProduct2D(CVector2D(TheCamera.CamFrontXNorm, TheCamera.CamFrontYNorm), *pPosn - TheCamera.GetPosition() ) > -fMaxViewAngle ) { float fDistToCam = Sqrt(fDistToCamSqr); if ( fDistToCam >= (fDrawDistance*(1.0f-(1.0f/4.0f))) && !bSpecialCam ) // BUG? must be 3.0? { //fDistToCam == 0 -> 3 //fDistToCam == fDrawDistance -> 0 float fMult = 1.0f - (3.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f-(1.0f/3.0f))) ); nRed = (int32)(nRed * fMult); nGreen = (int32)(nGreen * fMult); nBlue = (int32)(nBlue * fMult); } if ( pCar->m_vecMoveSpeed.Magnitude() * CTimeStep::ms_fTimeStep > 0.4f || pCar == FindPlayerVehicle() ) { StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, pTexture, pPosn, fFrontX, fFrontY, fSideX, fSideY, 128, nRed, nGreen, nBlue, 6.0f, false, 1.0f, nil, pCar == FindPlayerVehicle()); } else { StoreStaticShadow((uintptr)pCar + nID, SHADOWTYPE_ADDITIVE, pTexture, pPosn, fFrontX, fFrontY, fSideX, fSideY, 128, nRed, nGreen, nBlue, 6.0f, 1.0f, 27.0f, false, 0.4f); } } } } #ifdef USE_CUTSCENE_SHADOW_FOR_PED void StoreShadowForCutscenePedObject(CPed *pObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY) { ASSERT(pObject != nil); CCutsceneShadow *shadow = pObject->m_pRTShadow; if ( shadow == nil ) return; if ( !shadow->IsInitialized() ) return; CVector pos = pObject->GetPosition(); float fDistToCamSqr = (pos - TheCamera.GetPosition()).MagnitudeSqr2D(); float fDrawDistance = 100.0f; if ( fDistToCamSqr < SQR(fDrawDistance*0.5f) ) { if ( (CEntity*)pObject == FindPlayerPed() || TheCamera.IsSphereVisible(pos, 2.0f) ) { float fDistToCam = Sqrt(fDistToCamSqr); float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f/4.0f))); int32 nColorStrength; if ( fDistToCam >= (fDrawDistance*(1.0f/4.0f)) ) nColorStrength = (int32)(CTimeCycle::GetShadowStrength() * fMult); else nColorStrength = CTimeCycle::GetShadowStrength(); int32 color = int32(nColorStrength * 0.8f); pos.x += fDisplacementX; pos.y += fDisplacementY; RwTexture *texture = shadow->GetShadowRwTexture(); ASSERT(texture); RwRGBA bordercolor = {0, 0, 0, 0}; shadow->DrawBorderAroundTexture(bordercolor); pos.x -= fDisplacementX; pos.y -= fDisplacementY; float angleY = 360.0f - RADTODEG((CClock::ms_nGameClockMinutes +60*CClock::ms_nGameClockHours+CClock::ms_nGameClockSeconds/60)*(HALFPI/360.0f)); RwFrame *frame = shadow->SetLightProperties(angleY, -85.0f, true); ASSERT(frame); CVector at(RwFrameGetMatrix(frame)->at); at.Normalise(); CShadows::CalcPedShadowValues(at, &fFrontX, &fFrontY, &fSideX, &fSideY, &fDisplacementX, &fDisplacementY); pos.x -= 2.5f * fDisplacementX; pos.y -= 2.5f * fDisplacementY; CShadows::StoreShadowToBeRendered(SHADOWTYPE_INVCOLOR, texture, &pos, fFrontX * 1.5f, fFrontY * 1.5f, fSideX * 1.5f, fSideY * 1.5f, color, color, color, color, 4.0f, false, 1.0f, shadow, false); } } } #endif void CShadows::StoreShadowForPed(CPed *pPed, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY) { ASSERT(pPed != nil); if ( pPed->bIsVisible ) { if ( !(pPed->bInVehicle && pPed->m_nPedState != PED_DRAG_FROM_CAR && pPed->m_nPedState != PED_EXIT_CAR) ) { if ( CTimeCycle::GetShadowStrength() != 0 ) { #ifdef USE_CUTSCENE_SHADOW_FOR_PED CCutsceneShadow *pShadow = pPed->m_pRTShadow; if (pShadow) { if (pShadow->IsInitialized()) pShadow->UpdateForCutscene(); ::StoreShadowForCutscenePedObject(pPed, fDisplacementX, fDisplacementY, fFrontX, fFrontY, fSideX, fSideY); } return; #endif StoreShadowForPedObject(pPed, fDisplacementX, fDisplacementY, fFrontX, fFrontY, fSideX, fSideY); } } } } void CShadows::StoreShadowForPedObject(CEntity *pPedObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY) { ASSERT(pPedObject != nil); CVector PedPos = pPedObject->GetPosition(); float fDistToCamSqr = (PedPos - TheCamera.GetPosition()).MagnitudeSqr2D(); float fDrawDistance = 26.0f; if ( fDistToCamSqr < SQR(fDrawDistance*0.5f) ) { if ( pPedObject == FindPlayerPed() || TheCamera.IsSphereVisible(PedPos, 2.0f) != false ) { float fDistToCam = Sqrt(fDistToCamSqr); //fDistToCam == 0 -> 2 //fDistToCam == fDrawDistance -> -2 float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f/4.0f))); // BUG ? negative int32 nColorStrength; if ( fDistToCam >= (fDrawDistance*(1.0f/4.0f)) ) // BUG ? negative nColorStrength = (int32)(CTimeCycle::GetShadowStrength() * fMult); else nColorStrength = CTimeCycle::GetShadowStrength(); PedPos.x += fDisplacementX; PedPos.y += fDisplacementY; StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowPedTex, &PedPos, fFrontX, fFrontY, fSideX, fSideY, nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.0f, false, 1.0f, nil, pPedObject == FindPlayerPed()); } } } void CShadows::StoreShadowForCutscenePedObject(CCutsceneObject *pObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY) { #ifdef DISABLE_CUTSCENE_SHADOWS return; #endif ASSERT(pObject != nil); CCutsceneShadow *shadow = pObject->m_pShadow; if ( shadow == nil ) return; if ( !shadow->IsInitialized() ) return; CVector pos = pObject->GetPosition(); float fDistToCamSqr = (pos - TheCamera.GetPosition()).MagnitudeSqr2D(); float fDrawDistance = 100.0f; if ( fDistToCamSqr < SQR(fDrawDistance*0.5f) ) { if ( (CEntity*)pObject == FindPlayerPed() || TheCamera.IsSphereVisible(pos, 2.0f) ) { float fDistToCam = Sqrt(fDistToCamSqr); float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f/4.0f))); int32 nColorStrength; if ( fDistToCam >= (fDrawDistance*(1.0f/4.0f)) ) nColorStrength = (int32)(CTimeCycle::GetShadowStrength() * fMult); else nColorStrength = CTimeCycle::GetShadowStrength(); int32 color = int32(nColorStrength * 0.8f); pos.x += fDisplacementX; pos.y += fDisplacementY; RwTexture *texture = shadow->GetShadowRwTexture(); ASSERT(texture); RwRGBA bordercolor = {0, 0, 0, 0}; shadow->DrawBorderAroundTexture(bordercolor); pos.x -= fDisplacementX; pos.y -= fDisplacementY; float angleY = 360.0f - RADTODEG((CClock::ms_nGameClockMinutes+60* CClock::ms_nGameClockHours+CClock::ms_nGameClockSeconds/60)*(HALFPI/360.0f)); RwFrame *frame = shadow->SetLightProperties(angleY, -85.0f, true); ASSERT(frame); CVector at(RwFrameGetMatrix(frame)->at); at.Normalise(); CalcPedShadowValues(at, &fFrontX, &fFrontY, &fSideX, &fSideY, &fDisplacementX, &fDisplacementY); pos.x -= 2.5f * fDisplacementX; pos.y -= 2.5f * fDisplacementY; StoreShadowToBeRendered(SHADOWTYPE_INVCOLOR, texture, &pos, fFrontX * 1.5f, fFrontY * 1.5f, fSideX * 1.5f, fSideY * 1.5f, color, color, color, color, 4.0f, false, 1.0f, shadow, false); } } } void CShadows::StoreShadowForTree(CEntity *pTree) { ASSERT(pTree != nil); } void CShadows::StoreShadowForPole(CEntity *pPole, float fOffsetX, float fOffsetY, float fOffsetZ, float fPoleHeight, float fPoleWidth, uint32 nID) { ASSERT(pPole != nil); if ( CTimeCycle::GetShadowStrength() != 0 ) { if ( pPole->GetUp().z < 0.5f ) return; CVector PolePos = pPole->GetPosition(); PolePos.x += fOffsetX * pPole->GetRight().x + fOffsetY * pPole->GetForward().x; PolePos.y += fOffsetX * pPole->GetRight().y + fOffsetY * pPole->GetForward().y; PolePos.z += fOffsetZ; PolePos.x += -CTimeCycle::GetSunDirection().x * (fPoleHeight / 2); PolePos.y += -CTimeCycle::GetSunDirection().y * (fPoleHeight / 2); StoreStaticShadow((uintptr)pPole + nID + _TODOCONST(51), SHADOWTYPE_DARK, gpPostShadowTex, &PolePos, -CTimeCycle::GetSunDirection().x * (fPoleHeight / 2), -CTimeCycle::GetSunDirection().y * (fPoleHeight / 2), CTimeCycle::GetShadowSideX() * fPoleWidth, CTimeCycle::GetShadowSideY() * fPoleWidth, 2 * (int32)((pPole->GetUp().z - 0.5f) * CTimeCycle::GetShadowStrength() * 2.0f) / 3, 0, 0, 0, 15.0f, 1.0f, 40.0f, false, 0.0f); } } void CShadows::SetRenderModeForShadowType(uint8 ShadowType) { switch ( ShadowType ) { case SHADOWTYPE_DARK: { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); break; } case SHADOWTYPE_ADDITIVE: { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); break; } case SHADOWTYPE_INVCOLOR: { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCCOLOR); break; } } } void CShadows::RenderStoredShadows(void) { PUSH_RENDERGROUP("CShadows::RenderStoredShadows"); RenderBuffer::ClearRenderBuffer(); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSCLAMP); for ( int32 i = 0; i < ShadowsStoredToBeRendered; i++ ) asShadowsStored[i].m_nFlags.bRendered = false; for ( int32 i = 0; i < ShadowsStoredToBeRendered; i++ ) { if ( !asShadowsStored[i].m_nFlags.bRendered ) { SetRenderModeForShadowType(asShadowsStored[i].m_ShadowType); ASSERT(asShadowsStored[i].m_pTexture != nil); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(asShadowsStored[i].m_pTexture)); for ( int32 j = i; j < ShadowsStoredToBeRendered; j++ ) { if ( asShadowsStored[i].m_ShadowType == asShadowsStored[j].m_ShadowType && asShadowsStored[i].m_pTexture == asShadowsStored[j].m_pTexture ) { float fWidth = Abs(asShadowsStored[j].m_vecFront.x) + Abs(asShadowsStored[j].m_vecSide.x); float fHeight = Abs(asShadowsStored[j].m_vecFront.y) + Abs(asShadowsStored[j].m_vecSide.y); CVector shadowPos = asShadowsStored[j].m_vecPos; float fStartX = shadowPos.x - fWidth; float fEndX = shadowPos.x + fWidth; 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); CWorld::AdvanceCurrentScanCode(); for ( int32 y = nStartY; y <= nEndY; y++ ) { for ( int32 x = nStartX; x <= nEndX; x++ ) { CSector *pCurSector = CWorld::GetSector(x, y); ASSERT(pCurSector != nil); if ( asShadowsStored[j].m_pCutsceneShadow ) { CastCutsceneShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil, asShadowsStored[j].m_pCutsceneShadow); CastCutsceneShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil, asShadowsStored[j].m_pCutsceneShadow); } else if ( asShadowsStored[j].m_nFlags.bDrawOnBuildings ) { CastPlayerShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil); CastPlayerShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil); } else { CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil); CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil); } } } asShadowsStored[j].m_nFlags.bRendered = true; } } RenderBuffer::RenderStuffInBuffer(); } } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSWRAP); ShadowsStoredToBeRendered = 0; POP_RENDERGROUP(); } void CShadows::RenderStaticShadows(void) { PUSH_RENDERGROUP("CShadows::RenderStaticShadows"); RenderBuffer::ClearRenderBuffer(); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); SetAlphaTest(0); for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) aStaticShadows[i].m_bRendered = false; for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) { if ( aStaticShadows[i].m_pPolyBunch && !aStaticShadows[i].m_bRendered ) { SetRenderModeForShadowType(aStaticShadows[i].m_nType); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(aStaticShadows[i].m_pTexture)); // optimization trick, render all shadows with same renderstate and texture for ( int32 j = i; j < MAX_STATICSHADOWS; j++ ) { if ( aStaticShadows[j].m_pPolyBunch != nil && aStaticShadows[i].m_nType == aStaticShadows[j].m_nType && aStaticShadows[i].m_pTexture == aStaticShadows[j].m_pTexture ) { for ( CPolyBunch *bunch = aStaticShadows[j].m_pPolyBunch; bunch != nil; bunch = bunch->m_pNext ) { RwImVertexIndex *pIndexes; RwIm3DVertex *pVerts; RenderBuffer::StartStoring(3 * (bunch->m_nNumVerts - 2), bunch->m_nNumVerts, &pIndexes, &pVerts); ASSERT(pIndexes != nil); ASSERT(pVerts != nil); for ( int32 k = 0; k < bunch->m_nNumVerts; k++ ) { RwIm3DVertexSetRGBA(&pVerts[k], aStaticShadows[j].m_nRed, aStaticShadows[j].m_nGreen, aStaticShadows[j].m_nBlue, (int32)(aStaticShadows[j].m_nIntensity * (1.0f - CWeather::Foggyness * 0.5f))); RwIm3DVertexSetU (&pVerts[k], bunch->m_aU[k] / 200.0f); RwIm3DVertexSetV (&pVerts[k], bunch->m_aV[k] / 200.0f); RwIm3DVertexSetPos(&pVerts[k], bunch->m_aVerts[k].x, bunch->m_aVerts[k].y, bunch->m_aVerts[k].z + 0.03f); } for ( int32 k = 0; k < 3 * (bunch->m_nNumVerts - 2); k++ ) pIndexes[k] = ShadowIndexList[k]; RenderBuffer::StopStoring(); } aStaticShadows[j].m_bRendered = true; } } RenderBuffer::RenderStuffInBuffer(); } } RestoreAlphaTest(); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); POP_RENDERGROUP(); } void CShadows::GeneratePolysForStaticShadow(int16 nStaticShadowID) { float fWidth = Abs(aStaticShadows[nStaticShadowID].m_vecFront.x) + Abs(aStaticShadows[nStaticShadowID].m_vecSide.x); float fHeight = Abs(aStaticShadows[nStaticShadowID].m_vecFront.y) + Abs(aStaticShadows[nStaticShadowID].m_vecSide.y); CVector shadowPos = aStaticShadows[nStaticShadowID].m_vecPosn; float fStartX = shadowPos.x - fWidth; float fEndX = shadowPos.x + fWidth; 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); CWorld::AdvanceCurrentScanCode(); for ( int32 y = nStartY; y <= nEndY; y++ ) { for ( int32 x = nStartX; x <= nEndX; x++ ) { CSector *pCurSector = CWorld::GetSector(x, y); ASSERT(pCurSector != nil); CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], fStartX, fStartY, fEndX, fEndY, &shadowPos, aStaticShadows[nStaticShadowID].m_vecFront.x, aStaticShadows[nStaticShadowID].m_vecFront.y, aStaticShadows[nStaticShadowID].m_vecSide.x, aStaticShadows[nStaticShadowID].m_vecSide.y, 0, 0, 0, 0, aStaticShadows[nStaticShadowID].m_fZDistance, aStaticShadows[nStaticShadowID].m_fScale, &aStaticShadows[nStaticShadowID].m_pPolyBunch); CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], fStartX, fStartY, fEndX, fEndY, &shadowPos, aStaticShadows[nStaticShadowID].m_vecFront.x, aStaticShadows[nStaticShadowID].m_vecFront.y, aStaticShadows[nStaticShadowID].m_vecSide.x, aStaticShadows[nStaticShadowID].m_vecSide.y, 0, 0, 0, 0, aStaticShadows[nStaticShadowID].m_fZDistance, aStaticShadows[nStaticShadowID].m_fScale, &aStaticShadows[nStaticShadowID].m_pPolyBunch); } } } void CShadows::CastShadowSectorList(CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch) { ASSERT(pPosn != nil); CPtrNode *pNode = PtrList.first; CRect Bound; while ( pNode != nil ) { CEntity *pEntity = (CEntity *)pNode->item; uint16 nScanCode = pEntity->m_scanCode; pNode = pNode->next; ASSERT( pEntity != nil ); if ( nScanCode != CWorld::GetCurrentScanCode() ) { pEntity->m_scanCode = CWorld::GetCurrentScanCode(); if ( pEntity->bUsesCollision && !pEntity->m_flagE2 ) { if ( IsAreaVisible(pEntity->m_area) ) { Bound = pEntity->GetBoundRect(); if ( fStartX < Bound.right && fEndX > Bound.left && fStartY < Bound.bottom && fEndY > Bound.top ) { if ( pPosn->z - fZDistance < pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.max.z && pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.min.z < pPosn->z ) { CastShadowEntityXY(pEntity, fStartX, fStartY, fEndX, fEndY, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, fZDistance, fScale, ppPolyBunch); } } } } } } } void CShadows::CastPlayerShadowSectorList(CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch) { ASSERT(pPosn != nil); CPtrNode *pNode = PtrList.first; CRect Bound; while ( pNode != nil ) { CEntity *pEntity = (CEntity *)pNode->item; uint16 nScanCode = pEntity->m_scanCode; pNode = pNode->next; ASSERT( pEntity != nil ); if ( nScanCode != CWorld::GetCurrentScanCode() ) { pEntity->m_scanCode = CWorld::GetCurrentScanCode(); if ( pEntity->bUsesCollision ) { if ( IsAreaVisible(pEntity->m_area) ) { Bound = pEntity->GetBoundRect(); if ( fStartX < Bound.right && fEndX > Bound.left && fStartY < Bound.bottom && fEndY > Bound.top ) { if ( pPosn->z - fZDistance < pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.max.z && pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.min.z < pPosn->z ) { CastShadowEntityXY(pEntity, fStartX, fStartY, fEndX, fEndY, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, fZDistance, fScale, ppPolyBunch); } } } } } } } void CShadows::CastCutsceneShadowSectorList(CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow) { ASSERT(pPosn != nil); ASSERT(pShadow != nil); CPtrNode *pNode = PtrList.first; CRect Bound; while ( pNode != nil ) { CEntity *pEntity = (CEntity *)pNode->item; uint16 nScanCode = pEntity->m_scanCode; pNode = pNode->next; ASSERT( pEntity != nil ); if ( nScanCode != CWorld::GetCurrentScanCode() ) { pEntity->m_scanCode = CWorld::GetCurrentScanCode(); if ( pEntity->bUsesCollision ) { if ( IsAreaVisible(pEntity->m_area) ) { Bound = pEntity->GetBoundRect(); if ( fStartX < Bound.right && fEndX > Bound.left && fStartY < Bound.bottom && fEndY > Bound.top ) { if ( pPosn->z - fZDistance < pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.max.z && pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.min.z < pPosn->z ) { CastShadowEntityXYZ(pEntity, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, fZDistance, fScale, ppPolyBunch, pShadow); } } } } } } } void CShadows::CastShadowEntityXY(CEntity *pEntity, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch) { ASSERT(pEntity != nil); ASSERT(pPosn != nil); static CVector List [20]; static CVector Texture[20]; static CVector Points [4]; CColModel *pCol = pEntity->GetColModel(); ASSERT(pCol != nil); #ifndef MASTER if ( gbPrintShite ) printf("MI:%d Triangles:%d Coors:%f %f BBoxXY:%f %f\n", pEntity->GetModelIndex(), pCol->numTriangles, pEntity->GetPosition().x, pEntity->GetPosition().y, pCol->boundingBox.GetSize().x, pCol->boundingBox.GetSize().y); #endif CCollision::CalculateTrianglePlanes(pCol); float fFrontRight = DotProduct2D(CVector2D(fFrontX, fFrontY), pEntity->GetRight()); float fFrontForward = DotProduct2D(CVector2D(fFrontX, fFrontY), pEntity->GetForward()); float fSideRight = DotProduct2D(CVector2D(fSideX, fSideY), pEntity->GetRight()); float fSideForward = DotProduct2D(CVector2D(fSideX, fSideY), pEntity->GetForward()); float fLengthRight = DotProduct2D(*pPosn - pEntity->GetPosition(), pEntity->GetRight()); float fLengthForward = DotProduct2D(*pPosn - pEntity->GetPosition(), pEntity->GetForward()); Points[0].x = (fLengthRight + fFrontRight ) - fSideRight; Points[0].y = (fLengthForward + fFrontForward) - fSideForward; Points[1].x = fSideRight + (fLengthRight + fFrontRight); Points[1].y = fSideForward + (fLengthForward + fFrontForward); Points[2].x = fSideRight + (fLengthRight - fFrontRight); Points[2].y = fSideForward + (fLengthForward - fFrontForward); 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 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; for ( int32 i = 0; i < pCol->numTriangles; i++ ) { CColTrianglePlane *pColTriPlanes = pCol->trianglePlanes; ASSERT(pColTriPlanes != nil); CVector normal; pColTriPlanes[i].GetNormal(normal); if ( Abs(normal.z) > 0.1f ) { CColTriangle *pColTri = pCol->triangles; ASSERT(pColTri != nil); CVector PointA, PointB, PointC; pCol->GetTrianglePoint(PointA, pColTri[i].a); pCol->GetTrianglePoint(PointB, pColTri[i].b); pCol->GetTrianglePoint(PointC, pColTri[i].c); if ( (PointA.x > MinX || PointB.x > MinX || PointC.x > MinX) && (PointA.x < MaxX || PointB.x < MaxX || PointC.x < MaxX) && (PointA.y > MinY || PointB.y > MinY || PointC.y > MinY) && (PointA.y < MaxY || PointB.y < MaxY || PointC.y < MaxY) && (PointA.z < MaxZ || PointB.z < MaxZ || PointC.z < MaxZ) && (PointA.z > MinZ || PointB.z > MinZ || PointC.z > MinZ) ) { List[0].x = Points[0].x; List[0].y = Points[0].y; List[1].x = Points[1].x; List[1].y = Points[1].y; List[2].x = Points[2].x; List[2].y = Points[2].y; List[3].x = Points[3].x; List[3].y = Points[3].y; Texture[0].x = 0.0f; Texture[0].y = 0.0f; Texture[1].x = 1.0f; Texture[1].y = 0.0f; Texture[2].x = 1.0f; Texture[2].y = 1.0f; Texture[3].x = 0.0f; Texture[3].y = 1.0f; CVector2D start; CVector2D dist; int32 numVerts1 = 0; int16 vertType1 = 0; { for ( int32 j = 0; j < 4; j++ ) { start = PointA; dist = PointB - PointA; int32 in = j; float cp = CrossProduct2D(CVector2D(List[in]) - start, dist); if ( cp > 0.0f ) { switch ( vertType1 ) { case 0: { int32 out = numVerts1++ + 10; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 1: { int32 out = numVerts1++ + 10; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 2: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out1 = numVerts1++ + 10; int32 out2 = numVerts1++ + 10; Texture[out1].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out1].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out1].x = Compl*List[in-1].x + Scale*List[in].x; List[out1].y = Compl*List[in-1].y + Scale*List[in].y; Texture[out2].x = Texture[in].x; Texture[out2].y = Texture[in].y; List[out2].x = List[in].x; List[out2].y = List[in].y; break; } } vertType1 = 1; } else { switch ( vertType1 ) { case 1: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out = numVerts1++ + 10; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out].x = Compl*List[in-1].x + Scale*List[in].x; List[out].y = Compl*List[in-1].y + Scale*List[in].y; break; } } vertType1 = 2; } } float cp1 = CrossProduct2D(CVector2D(List[0]) - start, dist); if ( cp1 > 0.0f && vertType1 == 2 || cp1 <= 0.0f && vertType1 == 1 ) { float cp2 = CrossProduct2D(CVector2D(List[3]) - start, dist); float Scale = Abs(cp2) / (Abs(cp2) + Abs(cp1)); float Compl = 1.0f - Scale; int32 out = numVerts1++ + 10; Texture[out].x = Compl*Texture[3].x + Scale*Texture[0].x; Texture[out].y = Compl*Texture[3].y + Scale*Texture[0].y; List[out].x = Compl*List[3].x + Scale*List[0].x; List[out].y = Compl*List[3].y + Scale*List[0].y; } } int32 numVerts2 = 0; int16 vertType2 = 0; { for ( int32 j = 0; j < numVerts1; j++ ) { start = PointB; dist = PointC - PointB; int32 in = j + 10; float cp = CrossProduct2D(CVector2D(List[in]) - start, dist); if ( cp > 0.0f ) { switch ( vertType2 ) { case 0: { int32 out = numVerts2++; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 1: { int32 out = numVerts2++; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 2: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out1 = numVerts2++; int32 out2 = numVerts2++; Texture[out1].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out1].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out1].x = Compl*List[in-1].x + Scale*List[in].x; List[out1].y = Compl*List[in-1].y + Scale*List[in].y; Texture[out2].x = Texture[in].x; Texture[out2].y = Texture[in].y; List[out2].x = List[in].x; List[out2].y = List[in].y; break; } } vertType2 = 1; } else { switch ( vertType2 ) { case 1: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out = numVerts2++; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out].x = Compl*List[in-1].x + Scale*List[in].x; List[out].y = Compl*List[in-1].y + Scale*List[in].y; break; } } vertType2 = 2; } } float cp1 = CrossProduct2D(CVector2D(List[10]) - start, dist); if ( cp1 > 0.0f && vertType2 == 2 || cp1 <= 0.0f && vertType2 == 1 ) { int32 in = numVerts1 + 10; float cp2 = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(cp2) / (Abs(cp2) + Abs(cp1)); float Compl = 1.0f - Scale; int32 out = numVerts2++; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[10].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[10].y; List[out].x = Compl*List[in-1].x + Scale*List[10].x; List[out].y = Compl*List[in-1].y + Scale*List[10].y; } } int32 numVerts3 = 0; int16 vertType3 = 0; { for ( int32 j = 0; j < numVerts2; j++ ) { start = PointC; dist = PointA - PointC; int32 in = j; float cp = CrossProduct2D(CVector2D(List[in]) - start, dist); if ( cp > 0.0f ) { switch ( vertType3 ) { case 0: { int32 out = numVerts3++ + 10; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 1: { int32 out = numVerts3++ + 10; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 2: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out1 = numVerts3++ + 10; int32 out2 = numVerts3++ + 10; Texture[out1].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out1].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out1].x = Compl*List[in-1].x + Scale*List[in].x; List[out1].y = Compl*List[in-1].y + Scale*List[in].y; Texture[out2].x = Texture[in].x; Texture[out2].y = Texture[in].y; List[out2].x = List[in].x; List[out2].y = List[in].y; break; } } vertType3 = 1; } else { switch ( vertType3 ) { case 1: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out = numVerts3++ + 10; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out].x = Compl*List[in-1].x + Scale*List[in].x; List[out].y = Compl*List[in-1].y + Scale*List[in].y; break; } } vertType3 = 2; } } float cp1 = CrossProduct2D(CVector2D(List[0]) - start, dist); if ( cp1 > 0.0f && vertType3 == 2 || cp1 <= 0.0f && vertType3 == 1 ) { int32 in = numVerts2; float cp2 = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(cp2) / (Abs(cp2) + Abs(cp1)); float Compl = 1.0f - Scale; int32 out = numVerts3++ + 10; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[0].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[0].y; List[out].x = Compl*List[in-1].x + Scale*List[0].x; List[out].y = Compl*List[in-1].y + Scale*List[0].y; } } if ( numVerts3 >= 3 ) { CVector norm; pColTriPlanes[i].GetNormal(norm); float dot = DotProduct(norm, PointA); for ( int32 j = 0; j < numVerts3; j++ ) { int32 idx = j + 10; List[idx].z = -(DotProduct2D(norm, List[idx]) - dot) / norm.z; } for ( int32 j = 0; j < numVerts3; j++ ) { int32 idx = j + 10; CVector p = List[idx]; List[idx].x = p.y * pEntity->GetForward().x + p.x * pEntity->GetRight().x + pEntity->GetPosition().x; List[idx].y = p.y * pEntity->GetForward().y + p.x * pEntity->GetRight().y + pEntity->GetPosition().y; List[idx].z = p.z + pEntity->GetPosition().z; } if ( ppPolyBunch != nil ) { if ( pEmptyBunchList != nil ) { CPolyBunch *pBunch = pEmptyBunchList; ASSERT(pBunch != nil); pEmptyBunchList = pEmptyBunchList->m_pNext; pBunch->m_pNext = *ppPolyBunch; *ppPolyBunch = pBunch; pBunch->m_nNumVerts = numVerts3; for ( int32 j = 0; j < numVerts3; j++ ) { int32 in = j + 10; pBunch->m_aVerts[j] = List[in]; pBunch->m_aU[j] = (int32)(Texture[in].x * 200.0f); pBunch->m_aV[j] = (int32)(Texture[in].y * 200.0f); } } } else { RwImVertexIndex *pIndexes; RwIm3DVertex *pVerts; RenderBuffer::StartStoring(3 * (numVerts3 - 2), numVerts3, &pIndexes, &pVerts); ASSERT(pIndexes != nil); ASSERT(pVerts != nil); for ( int32 j = 0; j < numVerts3; j++ ) { int32 in = j + 10; RwIm3DVertexSetRGBA(&pVerts[j], nRed, nGreen, nBlue, nIntensity); RwIm3DVertexSetU (&pVerts[j], Texture[in].x*fScale); RwIm3DVertexSetV (&pVerts[j], Texture[in].y*fScale); RwIm3DVertexSetPos (&pVerts[j], List[in].x, List[in].y, List[in].z + 0.03f); } for ( int32 j = 0; j < 3*(numVerts3 - 2); j++ ) pIndexes[j] = ShadowIndexList[j]; RenderBuffer::StopStoring(); } } } } } } typedef struct _ProjectionParam { RwV3d at; /* Camera at vector */ RwMatrix invMatrix; /* Transforms to shadow camera space */ RwUInt8 shadowValue; /* Shadow opacity value */ RwBool fade; /* Shadow fades with distance */ RwUInt32 numIm3DBatch; /* Number of buffer flushes */ RwMatrix entityMatrix; } ProjectionParam; RwV3d *ShadowRenderTriangleCB(RwV3d *points, RwV3d *normal, ProjectionParam *param) { RwV3d vIn[3]; RwV3d vShad[3]; RwV3dTransformPoints(&vIn[0], points, 3, ¶m->entityMatrix); /* * Reject backfacing triangles * This reject the triangles parallel to the light as well */ if (RwV3dDotProduct(normal, ¶m->at) > 0.0f) { return points; } RwV3dTransformPoints(&vShad[0], &vIn[0], 3, ¶m->invMatrix); /* * Reject triangles behind the camera (z test). Note that any world * triangles lying in front of the camera but before the object may * have a shadow applied. To minimize such artefacts, this test could * be modified to use a specific value rather than 0.0f, perhaps * to reject triangles behind the center plane of the object. * * Reject triangles that lie entirely outside the shadow texture range * (x,y test). */ if (((vShad[0].z < 0.0f) && (vShad[1].z < 0.0f) && (vShad[2].z < 0.0f)) || ((vShad[0].x < 0.0f) && (vShad[1].x < 0.0f) && (vShad[2].x < 0.0f)) || ((vShad[0].x > 1.0f) && (vShad[1].x > 1.0f) && (vShad[2].x > 1.0f)) || ((vShad[0].y < 0.0f) && (vShad[1].y < 0.0f) && (vShad[2].y < 0.0f)) || ((vShad[0].y > 1.0f) && (vShad[1].y > 1.0f) && (vShad[2].y > 1.0f))) { return points; } RwIm3DVertex *imv = nil; RwImVertexIndex *imi = nil; RenderBuffer::StartStoring(3, 3, &imi, &imv); /* * Set the immediate mode vertices for this triangle */ RwIm3DVertexSetPos(imv, vIn[0].x, vIn[0].y, vIn[0].z); RwIm3DVertexSetPos(imv + 1, vIn[1].x, vIn[1].y, vIn[1].z); RwIm3DVertexSetPos(imv + 2, vIn[2].x, vIn[2].y, vIn[2].z); RwIm3DVertexSetU(imv, vShad[0].x); RwIm3DVertexSetU(imv + 1, vShad[1].x); RwIm3DVertexSetU(imv + 2, vShad[2].x); RwIm3DVertexSetV(imv, vShad[0].y); RwIm3DVertexSetV(imv + 1, vShad[1].y); RwIm3DVertexSetV(imv + 2, vShad[2].y); /* * Do we fade out the shadow with distance? */ if (param->fade) { RwReal fadeVal; RwUInt8 val; fadeVal = 1.0f - vShad[0].z * vShad[0].z; val = (fadeVal < 0.0f) ? 0 : (RwUInt8) (fadeVal * param->shadowValue); RwIm3DVertexSetRGBA(imv, val, val, val, val); fadeVal = 1.0f - vShad[1].z * vShad[1].z; val = (fadeVal < 0.0f) ? 0 : (RwUInt8) (fadeVal * param->shadowValue); RwIm3DVertexSetRGBA(imv + 1, val, val, val, val); fadeVal = 1.0f - vShad[2].z * vShad[2].z; val = (fadeVal < 0.0f) ? 0 : (RwUInt8) (fadeVal * param->shadowValue); RwIm3DVertexSetRGBA(imv + 2, val, val, val, val); } else { RwUInt8 val = param->shadowValue; RwIm3DVertexSetRGBA(imv, val, val, val, val); RwIm3DVertexSetRGBA(imv + 1, val, val, val, val); RwIm3DVertexSetRGBA(imv + 2, val, val, val, val); } /* * Update buffer position */ imi[0] = 0; imi[1] = 1; imi[2] = 2; RenderBuffer::StopStoring(); return points; } void CShadows::CastShadowEntityXYZ(CEntity *pEntity, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow) { ASSERT(pEntity != nil); ASSERT(pPosn != nil); if ( pShadow ) { ProjectionParam proj; RwV3d scl; RwV3d tr; CShadowCamera *shadow = pShadow->GetShadowCamera(); CColModel *collision = pEntity->GetColModel(); CCollision::CalculateTrianglePlanes(collision); RwMatrix mat; mat = *RwFrameGetMatrix(RwCameraGetFrame(shadow->GetRwCamera())); RwV3d Xaxis = { 1.0f, 0.0f, 0.0f }; RwMatrixRotate(&mat, &Xaxis, -45.0f, rwCOMBINEPRECONCAT); proj.at = mat.at; pEntity->GetMatrix().CopyToRwMatrix(&proj.entityMatrix); RwMatrixInvert(&proj.invMatrix, &mat); RwReal radius = RwCameraGetViewWindow(shadow->GetRwCamera())->x; scl.x = scl.y = -0.5f / (radius*0.9f); scl.z = 1.0f / (radius*0.8f); RwMatrixScale(&proj.invMatrix, &scl, rwCOMBINEPOSTCONCAT); tr.x = 0.5f; tr.y = tr.z = 0.0f; RwMatrixTranslate(&proj.invMatrix, &tr, rwCOMBINEPOSTCONCAT); proj.shadowValue = nIntensity; proj.fade = 0; RwMatrix matrix; pEntity->GetMatrix().CopyToRwMatrix(&matrix); RwMatrix invMatrix; RwMatrixInvert(&invMatrix, &matrix); CVector center(pShadow->GetBaseSphere().center); center += CVector(-fFrontX * 1.1f, -fFrontY * 1.1f, -0.5f); CSphere sphere; sphere.Set(2.0f, center); RwV3d point; RwV3dTransformPoints(&point, ¢er, 1, &invMatrix); CColSphere colSphere; colSphere.Set(2.0f, CVector(point), 0, 0); int i = 0; while ( i < collision->numTriangles ) { CVector p[3]; collision->GetTrianglePoint(p[0], collision->triangles[i].a); collision->GetTrianglePoint(p[1], collision->triangles[i].b); collision->GetTrianglePoint(p[2], collision->triangles[i].c); if ( CCollision::TestSphereTriangle(colSphere, collision->vertices, collision->triangles[i], collision->trianglePlanes[i]) ) { CVector n(collision->trianglePlanes[i].GetNormalX(), collision->trianglePlanes[i].GetNormalY(), collision->trianglePlanes[i].GetNormalZ()); CVector offset = n * 0.028f; p[0] += offset; p[1] += offset; p[2] += offset; if ( !ShadowRenderTriangleCB(p, &n, &proj) ) break; } i++; } } } void CShadows::UpdateStaticShadows(void) { for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) { if ( aStaticShadows[i].m_pPolyBunch != nil && !aStaticShadows[i].m_bJustCreated && (!aStaticShadows[i].m_bTemp || CTimer::GetTimeInMilliseconds() > aStaticShadows[i].m_nTimeCreated + 5000) ) { aStaticShadows[i].Free(); } aStaticShadows[i].m_bJustCreated = false; } } void CShadows::UpdatePermanentShadows(void) { for ( int32 i = 0; i < MAX_PERMAMENTSHADOWS; i++ ) { if ( aPermanentShadows[i].m_nType != SHADOWTYPE_NONE ) { uint32 timePassed = CTimer::GetTimeInMilliseconds() - aPermanentShadows[i].m_nTimeCreated; if ( timePassed >= aPermanentShadows[i].m_nLifeTime ) aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; else { bool bOk; if ( timePassed >= (aPermanentShadows[i].m_nLifeTime * 3 / 4) ) { // timePassed == 0 -> 4 // timePassed == aPermanentShadows[i].m_nLifeTime -> 0 float fMult = 1.0f - float(timePassed - (aPermanentShadows[i].m_nLifeTime * 3 / 4)) / (aPermanentShadows[i].m_nLifeTime / 4); bOk = StoreStaticShadow((uintptr)&aPermanentShadows[i], aPermanentShadows[i].m_nType, aPermanentShadows[i].m_pTexture, &aPermanentShadows[i].m_vecPos, aPermanentShadows[i].m_vecFront.x, aPermanentShadows[i].m_vecFront.y, aPermanentShadows[i].m_vecSide.x, aPermanentShadows[i].m_vecSide.y, (int32)(aPermanentShadows[i].m_nIntensity * fMult), (int32)(aPermanentShadows[i].m_nRed * fMult), (int32)(aPermanentShadows[i].m_nGreen * fMult), (int32)(aPermanentShadows[i].m_nBlue * fMult), aPermanentShadows[i].m_fZDistance, 1.0f, 40.0f, false, 0.0f); } else { bOk = StoreStaticShadow((uintptr)&aPermanentShadows[i], aPermanentShadows[i].m_nType, aPermanentShadows[i].m_pTexture, &aPermanentShadows[i].m_vecPos, aPermanentShadows[i].m_vecFront.x, aPermanentShadows[i].m_vecFront.y, aPermanentShadows[i].m_vecSide.x, aPermanentShadows[i].m_vecSide.y, aPermanentShadows[i].m_nIntensity, aPermanentShadows[i].m_nRed, aPermanentShadows[i].m_nGreen, aPermanentShadows[i].m_nBlue, aPermanentShadows[i].m_fZDistance, 1.0f, 40.0f, false, 0.0f); } if ( !bOk ) aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; } } } } void CStaticShadow::Free(void) { if ( m_pPolyBunch != nil ) { CPolyBunch *pFree = CShadows::pEmptyBunchList; CShadows::pEmptyBunchList = m_pPolyBunch; CPolyBunch *pUsed = m_pPolyBunch; while (pUsed->m_pNext != nil) pUsed = pUsed->m_pNext; pUsed->m_pNext = pFree; } m_pPolyBunch = nil; m_nId = 0; } void CShadows::CalcPedShadowValues(CVector vecLightDir, float *pfFrontX, float *pfFrontY, float *pfSideX, float *pfSideY, float *pfDisplacementX, float *pfDisplacementY) { ASSERT(pfFrontX != nil); ASSERT(pfFrontY != nil); ASSERT(pfSideX != nil); ASSERT(pfSideY != nil); ASSERT(pfDisplacementX != nil); ASSERT(pfDisplacementY != nil); *pfFrontX = -vecLightDir.x; *pfFrontY = -vecLightDir.y; float fDist = Sqrt(*pfFrontY * *pfFrontY + *pfFrontX * *pfFrontX); float fMult = (fDist + 1.0f) / fDist; *pfFrontX *= fMult; *pfFrontY *= fMult; *pfSideX = -vecLightDir.y / fDist; *pfSideY = vecLightDir.x / fDist; *pfDisplacementX = -vecLightDir.x; *pfDisplacementY = -vecLightDir.y; *pfFrontX /= 2; *pfFrontY /= 2; *pfSideX /= 2; *pfSideY /= 2; *pfDisplacementX /= 2; *pfDisplacementY /= 2; } void CShadows::RenderExtraPlayerShadows(void) { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return; #endif if ( CTimeCycle::GetLightShadowStrength() != 0 ) { CVehicle *pCar = FindPlayerVehicle(); if ( pCar == nil ) ; // R* cut it out for playerped else { if ( pCar->GetModelIndex() != MI_RCBANDIT && pCar->GetVehicleAppearance() != VEHICLE_APPEARANCE_BIKE && !pCar->IsBike() && !pCar->IsPlane() && !pCar->IsBoat() ) { for ( int32 i = 0; i < CPointLights::NumLights; i++ ) { if ( CPointLights::aLights[i].type == CPointLights::LIGHT_POINT && CPointLights::aLights[i].castExtraShadows &&(0.0f != CPointLights::aLights[i].red || 0.0f != CPointLights::aLights[i].green || 0.0f != CPointLights::aLights[i].blue) ) { CVector vecLight = CPointLights::aLights[i].coors - FindPlayerCoors(); float fLightDist = vecLight.Magnitude(); float fRadius = CPointLights::aLights[i].radius; if ( fLightDist < fRadius ) { // fLightDist == 0 -> 2.0f // fLightDist == fRadius -> 0.0f float fMult = (1.0f - (2.0f * fLightDist - fRadius) / fRadius); int32 nColorStrength; if ( fLightDist < fRadius*0.5f ) nColorStrength = (5*CTimeCycle::GetLightShadowStrength()/8); else nColorStrength = int32((5*CTimeCycle::GetLightShadowStrength()/8) * fMult); float fInv = 1.0f / fLightDist; vecLight.x *= fInv; vecLight.y *= fInv; vecLight.z *= fInv; CVector shadowPos = pCar->GetPosition(); shadowPos.x -= vecLight.x * 1.2f; shadowPos.y -= vecLight.y * 1.2f; float fVehicleWidth = pCar->GetColModel()->boundingBox.GetSize().x; float fVehicleHeight = pCar->GetColModel()->boundingBox.GetSize().y; shadowPos.x -= ((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y) * pCar->GetForward().x; shadowPos.y -= ((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y) * pCar->GetForward().y; if ( pCar->GetUp().z > 0.0f ) { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowCarTex, &shadowPos, pCar->GetForward().x * (fVehicleHeight/2), pCar->GetForward().y * (fVehicleHeight/2), pCar->GetRight().x * (fVehicleWidth/3), pCar->GetRight().y * (fVehicleWidth/3), nColorStrength, 0, 0, 0, 4.5f, false, 1.0f, nil, false); } else { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowCarTex, &shadowPos, pCar->GetForward().x * (fVehicleHeight/2), pCar->GetForward().y * (fVehicleHeight/2), -pCar->GetRight().x * (fVehicleWidth/2), -pCar->GetRight().y * (fVehicleWidth/2), nColorStrength, 0, 0, 0, 4.5f, false, 1.0f, nil, false); } } } } } } } } void CShadows::TidyUpShadows(void) { for ( int32 i = 0; i < MAX_PERMAMENTSHADOWS; i++ ) aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; } void CShadows::RenderIndicatorShadow(uint32 nID, uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity) { ASSERT(pPosn != nil); C3dMarkers::PlaceMarkerSet(nID, MARKERTYPE_CYLINDER, *pPosn, Max(fFrontX, -fSideY), SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, SPHERE_MARKER_A, SPHERE_MARKER_PULSE_PERIOD, 0.2f, 0); } ================================================ FILE: src/render/Shadows.h ================================================ #pragma once #define MAX_STOREDSHADOWS 48 #define MAX_POLYBUNCHES 380 #define MAX_STATICSHADOWS 48 #define MAX_PERMAMENTSHADOWS 48 class CEntity; class CPtrList; class CAutomobile; class CVehicle; class CPed; class CCutsceneShadow; class CCutsceneObject; enum eShadowType { SHADOWTYPE_NONE = 0, SHADOWTYPE_DARK, SHADOWTYPE_ADDITIVE, SHADOWTYPE_INVCOLOR }; enum eShadowTextureType { SHADOWTEX_NONE = 0, SHADOWTEX_CAR, SHADOWTEX_PED, SHADOWTEX_EXPLOSION, SHADOWTEX_HELI, SHADOWTEX_HEADLIGHTS, SHADOWTEX_BLOOD }; enum VEH_SHD_TYPE { VEH_SHD_TYPE_CAR = 0, VEH_SHD_TYPE_BIKE, VEH_SHD_TYPE_HELI, VEH_SHD_TYPE_SEAPLANE, VEH_SHD_TYPE_RCPLANE, }; class CStoredShadow { public: CVector m_vecPos; CVector2D m_vecFront; CVector2D m_vecSide; float m_fZDistance; float m_fScale; RwTexture *m_pTexture; CCutsceneShadow *m_pCutsceneShadow; int16 m_nIntensity; uint8 m_ShadowType; uint8 m_nRed; uint8 m_nGreen; uint8 m_nBlue; struct { uint8 bDrawOnWater : 1; uint8 bRendered : 1; uint8 bDrawOnBuildings : 1; } m_nFlags; CStoredShadow() { } }; VALIDATE_SIZE(CStoredShadow, 0x30); class CPolyBunch { public: CVector m_aVerts[7]; CPolyBunch *m_pNext; int16 m_nNumVerts; uint8 m_aU[7]; uint8 m_aV[7]; CPolyBunch() { } }; VALIDATE_SIZE(CPolyBunch, 0x6C); class CStaticShadow { public: uint32 m_nId; CPolyBunch *m_pPolyBunch; uint32 m_nTimeCreated; CVector m_vecPosn; CVector2D m_vecFront; CVector2D m_vecSide; float m_fZDistance; float m_fScale; RwTexture *m_pTexture; int16 m_nIntensity; // unsigned ? uint8 m_nType; uint8 m_nRed; uint8 m_nGreen; uint8 m_nBlue; bool m_bJustCreated; bool m_bRendered; bool m_bTemp; CStaticShadow() { } void Free(); }; VALIDATE_SIZE(CStaticShadow, 0x40); class CPermanentShadow { public: CVector m_vecPos; CVector2D m_vecFront; CVector2D m_vecSide; float m_fZDistance; float m_fScale; uint32 m_nTimeCreated; uint32 m_nLifeTime; RwTexture *m_pTexture; int16 m_nIntensity; uint8 m_nType; // eShadowType uint8 m_nRed; uint8 m_nGreen; uint8 m_nBlue; CPermanentShadow() { } }; VALIDATE_SIZE(CPermanentShadow, 0x38); class CShadows { public: static int16 ShadowsStoredToBeRendered; static CStoredShadow asShadowsStored [MAX_STOREDSHADOWS]; static CPolyBunch aPolyBunches [MAX_POLYBUNCHES]; static CStaticShadow aStaticShadows [MAX_STATICSHADOWS]; static CPolyBunch *pEmptyBunchList; static CPermanentShadow aPermanentShadows[MAX_PERMAMENTSHADOWS]; static void Init (void); static void Shutdown (void); static void AddPermanentShadow ( uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, uint32 nTime, float fScale); static bool StoreStaticShadow (uint32 nID, uint8 ShadowType, RwTexture *pTexture, Const CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, float fDrawDistance, bool bTempShadow, float fUpDistance); static void StoreShadowToBeRendered ( uint8 ShadowType, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue); static void StoreShadowToBeRendered ( uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, bool bDrawOnWater, float fScale, CCutsceneShadow *pShadow, bool bDrawOnBuildings); static void StoreShadowForVehicle (CVehicle *pCar, VEH_SHD_TYPE type); static void StoreCarLightShadow (CVehicle *pCar, int32 nID, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, uint8 nRed, uint8 nGreen, uint8 nBlue, float fMaxViewAngle); static void StoreShadowForPed (CPed *pPed, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY); static void StoreShadowForPedObject (CEntity *pPedObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY); static void StoreShadowForCutscenePedObject(CCutsceneObject *pObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY); static void StoreShadowForTree (CEntity *pTree); static void StoreShadowForPole (CEntity *pPole, float fOffsetX, float fOffsetY, float fOffsetZ, float fPoleHeight, float fPoleWidth, uint32 nID); static void SetRenderModeForShadowType (uint8 ShadowType); static void RenderStoredShadows (void); static void RenderStaticShadows (void); static void GeneratePolysForStaticShadow (int16 nStaticShadowID); static void CastShadowSectorList (CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch); static void CastPlayerShadowSectorList (CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch); static void CastCutsceneShadowSectorList (CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow); static void CastShadowEntityXY (CEntity *pEntity, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch); static void CastShadowEntityXYZ (CEntity *pEntity, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow); static void UpdateStaticShadows (void); static void UpdatePermanentShadows (void); static void CalcPedShadowValues (CVector vecLightDir, float *pfFrontX, float *pfFrontY, float *pfSideX, float *pfSideY, float *pfDisplacementX, float *pfDisplacementY); static void RenderExtraPlayerShadows (void); static void TidyUpShadows (void); static void RenderIndicatorShadow (uint32 nID, uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity); }; extern RwTexture *gpShadowCarTex; extern RwTexture *gpShadowPedTex; extern RwTexture *gpShadowHeliTex; extern RwTexture *gpShadowBikeTex; extern RwTexture *gpShadowBaronTex; extern RwTexture *gpShadowExplosionTex; extern RwTexture *gpShadowHeadLightsTex; extern RwTexture *gpOutline1Tex; extern RwTexture *gpOutline2Tex; extern RwTexture *gpOutline3Tex; extern RwTexture *gpBloodPoolTex; extern RwTexture *gpReflectionTex; extern RwTexture *gpWalkDontTex; extern RwTexture *gpCrackedGlassTex; extern RwTexture *gpPostShadowTex; extern RwTexture *gpGoalTex; ================================================ FILE: src/render/Skidmarks.cpp ================================================ #include "common.h" #include "main.h" #include "TxdStore.h" #include "Timer.h" #include "Replay.h" #include "Skidmarks.h" CSkidmark CSkidmarks::aSkidmarks[NUMSKIDMARKS]; RwImVertexIndex SkidmarkIndexList[SKIDMARK_LENGTH * 6]; RwIm3DVertex SkidmarkVertices[SKIDMARK_LENGTH * 2]; RwTexture *gpSkidTex; void CSkidmarks::Init(void) { int i, ix, slot; CTxdStore::PushCurrentTxd(); slot = CTxdStore::FindTxdSlot("particle"); CTxdStore::SetCurrentTxd(slot); gpSkidTex = RwTextureRead("particleskid", nil); CTxdStore::PopCurrentTxd(); for(i = 0; i < NUMSKIDMARKS; i++){ aSkidmarks[i].m_state = 0; aSkidmarks[i].m_wasUpdated = false; } ix = 0; for(i = 0; i < SKIDMARK_LENGTH; i++){ SkidmarkIndexList[i*6+0] = ix+0; SkidmarkIndexList[i*6+1] = ix+2; SkidmarkIndexList[i*6+2] = ix+1; SkidmarkIndexList[i*6+3] = ix+1; SkidmarkIndexList[i*6+4] = ix+2; SkidmarkIndexList[i*6+5] = ix+3; ix += 2; } } void CSkidmarks::Shutdown(void) { RwTextureDestroy(gpSkidTex); gpSkidTex = nil; } void CSkidmarks::Clear(void) { int i; for(i = 0; i < NUMSKIDMARKS; i++){ aSkidmarks[i].m_state = 0; aSkidmarks[i].m_wasUpdated = false; } } void CSkidmarks::Update(void) { int i; uint32 t1 = CTimer::GetTimeInMilliseconds() + 2500; uint32 t2 = CTimer::GetTimeInMilliseconds() + 5000; uint32 t3 = CTimer::GetTimeInMilliseconds() + 10000; uint32 t4 = CTimer::GetTimeInMilliseconds() + 20000; for(i = 0; i < NUMSKIDMARKS; i++){ switch(aSkidmarks[i].m_state){ case 1: if(!aSkidmarks[i].m_wasUpdated){ // Didn't continue this one last time, so finish it and set fade times aSkidmarks[i].m_state = 2; if(aSkidmarks[i].m_last < 4){ aSkidmarks[i].m_fadeStart = t1; aSkidmarks[i].m_fadeEnd = t2; }else if(aSkidmarks[i].m_last < 9){ aSkidmarks[i].m_fadeStart = t2; aSkidmarks[i].m_fadeEnd = t3; }else{ aSkidmarks[i].m_fadeStart = t3; aSkidmarks[i].m_fadeEnd = t4; } } break; case 2: if(CTimer::GetTimeInMilliseconds() > aSkidmarks[i].m_fadeEnd) aSkidmarks[i].m_state = 0; break; } aSkidmarks[i].m_wasUpdated = false; } } void CSkidmarks::Render(void) { int i, j; PUSH_RENDERGROUP("CSkidmarks::Render"); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpSkidTex)); for(i = 0; i < NUMSKIDMARKS; i++){ if(aSkidmarks[i].m_state == 0 || aSkidmarks[i].m_last < 1) continue; CRGBA color(0, 0, 0, 255); switch(aSkidmarks[i].m_type){ case SKIDMARK_NORMAL: color = CRGBA(0, 0, 0, 255); break; case SKIDMARK_MUDDY: color = CRGBA(90, 62, 9, 255); break; case SKIDMARK_SANDY: color = CRGBA(108, 108, 96, 255); break; case SKIDMARK_BLOODY: color = CRGBA(132, 34, 11, 255); break; } uint32 fade, alpha; if(aSkidmarks[i].m_state == 1 || CTimer::GetTimeInMilliseconds() < aSkidmarks[i].m_fadeStart) fade = 255; else fade = 255*(aSkidmarks[i].m_fadeEnd - CTimer::GetTimeInMilliseconds()) / (aSkidmarks[i].m_fadeEnd - aSkidmarks[i].m_fadeStart); for(j = 0; j <= aSkidmarks[i].m_last; j++){ alpha = 128; if(j == 0 || j == aSkidmarks[i].m_last && aSkidmarks[i].m_state == 2) alpha = 0; alpha = alpha*fade/256; CVector p1 = aSkidmarks[i].m_pos[j]; p1.x += aSkidmarks[i].m_sideX[j]; p1.y += aSkidmarks[i].m_sideY[j]; CVector p2 = aSkidmarks[i].m_pos[j]; p2.x -= aSkidmarks[i].m_sideX[j]; p2.y -= aSkidmarks[i].m_sideY[j]; RwIm3DVertexSetRGBA(&SkidmarkVertices[j*2+0], color.red, color.green, color.blue, alpha); RwIm3DVertexSetPos(&SkidmarkVertices[j*2+0], p1.x, p1.y, p1.z+0.1f); RwIm3DVertexSetU(&SkidmarkVertices[j*2+0], 0.0f); RwIm3DVertexSetV(&SkidmarkVertices[j*2+0], j*5.01f); RwIm3DVertexSetRGBA(&SkidmarkVertices[j*2+1], color.red, color.green, color.blue, alpha); RwIm3DVertexSetPos(&SkidmarkVertices[j*2+1], p2.x, p2.y, p2.z+0.1f); RwIm3DVertexSetU(&SkidmarkVertices[j*2+1], 1.0f); RwIm3DVertexSetV(&SkidmarkVertices[j*2+1], j*5.01f); } LittleTest(); if(RwIm3DTransform(SkidmarkVertices, 2*(aSkidmarks[i].m_last+1), nil, rwIM3D_VERTEXUV)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, SkidmarkIndexList, 6*aSkidmarks[i].m_last); RwIm3DEnd(); } } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); POP_RENDERGROUP(); } void CSkidmarks::RegisterOne(uintptr id, const CVector &pos, float fwdX, float fwdY, bool *isMuddy, bool *isBloody) { eSkidmarkType type; if(*isBloody) type = SKIDMARK_BLOODY; else if(*isMuddy) type = SKIDMARK_MUDDY; else type = SKIDMARK_NORMAL; RegisterOne(id, pos, fwdX, fwdY, type, isBloody); } void CSkidmarks::RegisterOne(uintptr id, const CVector &pos, float fwdX, float fwdY, eSkidmarkType type, bool *isBloody) { int i; CVector2D fwd(fwdX, fwdY); if(CReplay::IsPlayingBack()) return; // Find a skidmark to continue for(i = 0; i < NUMSKIDMARKS; i++) if(aSkidmarks[i].m_state == 1 && aSkidmarks[i].m_id == id) break; if(i < NUMSKIDMARKS){ // Continue this one if((aSkidmarks[i].m_type==SKIDMARK_BLOODY) != *isBloody){ // Blood-status changed, end this one aSkidmarks[i].m_state = 2; aSkidmarks[i].m_fadeStart = CTimer::GetTimeInMilliseconds() + 10000; aSkidmarks[i].m_fadeEnd = CTimer::GetTimeInMilliseconds() + 20000; return; } aSkidmarks[i].m_wasUpdated = true; if(CTimer::GetTimeInMilliseconds() - aSkidmarks[i].m_lastUpdate <= 100){ // Last update was recently, just change last coords aSkidmarks[i].m_pos[aSkidmarks[i].m_last] = pos; return; } aSkidmarks[i].m_lastUpdate = CTimer::GetTimeInMilliseconds(); if(aSkidmarks[i].m_last >= SKIDMARK_LENGTH-1){ // No space to continue, end it aSkidmarks[i].m_state = 2; aSkidmarks[i].m_fadeStart = CTimer::GetTimeInMilliseconds() + 10000; aSkidmarks[i].m_fadeEnd = CTimer::GetTimeInMilliseconds() + 20000; *isBloody = false; // stpo blood marks at end return; } aSkidmarks[i].m_last++; aSkidmarks[i].m_pos[aSkidmarks[i].m_last] = pos; CVector2D right(aSkidmarks[i].m_pos[aSkidmarks[i].m_last].y - aSkidmarks[i].m_pos[aSkidmarks[i].m_last - 1].y, aSkidmarks[i].m_pos[aSkidmarks[i].m_last - 1].x - aSkidmarks[i].m_pos[aSkidmarks[i].m_last].x); right.Normalise(); fwd.Normalise(); float turn = DotProduct2D(fwd, right); turn = Abs(turn) + 1.0f; aSkidmarks[i].m_sideX[aSkidmarks[i].m_last] = right.x * turn * 0.125f; aSkidmarks[i].m_sideY[aSkidmarks[i].m_last] = right.y * turn * 0.125f; if(aSkidmarks[i].m_last == 1){ aSkidmarks[i].m_sideX[0] = aSkidmarks[i].m_sideX[1]; aSkidmarks[i].m_sideY[0] = aSkidmarks[i].m_sideY[1]; } if(aSkidmarks[i].m_last > 8) *isBloody = false; // stop blood marks after 8 return; } // Start a new one for(i = 0; i < NUMSKIDMARKS; i++) if(aSkidmarks[i].m_state == 0) break; if(i < NUMSKIDMARKS){ // Found a free slot aSkidmarks[i].m_state = 1; aSkidmarks[i].m_id = id; aSkidmarks[i].m_pos[0] = pos; aSkidmarks[i].m_sideX[0] = 0.0f; aSkidmarks[i].m_sideY[0] = 0.0f; aSkidmarks[i].m_wasUpdated = true; aSkidmarks[i].m_last = 0; aSkidmarks[i].m_lastUpdate = CTimer::GetTimeInMilliseconds() - 1000; if(*isBloody) aSkidmarks[i].m_type = SKIDMARK_BLOODY; else aSkidmarks[i].m_type = type; }else *isBloody = false; // stop blood marks if no space } ================================================ FILE: src/render/Skidmarks.h ================================================ #pragma once enum { SKIDMARK_LENGTH = 16 }; enum eSkidmarkType { SKIDMARK_NORMAL, SKIDMARK_MUDDY, SKIDMARK_SANDY, SKIDMARK_BLOODY }; class CSkidmark { public: CVector m_pos[SKIDMARK_LENGTH]; float m_sideX[SKIDMARK_LENGTH]; float m_sideY[SKIDMARK_LENGTH]; uintptr m_id; uint32 m_lastUpdate; uint32 m_fadeStart; uint32 m_fadeEnd; uint32 m_type; int16 m_last; uint8 m_state; bool m_wasUpdated; }; class CSkidmarks { static CSkidmark aSkidmarks[NUMSKIDMARKS]; public: static void Init(void); static void Shutdown(void); static void Clear(void); static void Update(void); static void Render(void); static void RegisterOne(uintptr id, const CVector &pos, float fwdX, float fwdY, eSkidmarkType type, bool *isBloody); static void RegisterOne(uintptr id, const CVector &pos, float fwdX, float fwdY, bool *isMuddy, bool *isBloody); }; ================================================ FILE: src/render/SpecialFX.cpp ================================================ #include "common.h" #include "SpecialFX.h" #include "RenderBuffer.h" #include "Timer.h" #include "Sprite.h" #include "Font.h" #include "Text.h" #include "TxdStore.h" #include "FileMgr.h" #include "FileLoader.h" #include "Timecycle.h" #include "Lights.h" #include "ModelIndices.h" #include "VisibilityPlugins.h" #include "World.h" #include "PlayerPed.h" #include "Particle.h" #include "Shadows.h" #include "General.h" #include "Camera.h" #include "Shadows.h" #include "main.h" #include "ColStore.h" #include "Coronas.h" #include "Script.h" #include "DMAudio.h" RwIm3DVertex StreakVertices[4]; RwImVertexIndex StreakIndexList[12]; RwIm3DVertex TraceVertices[10]; static RwImVertexIndex TraceIndexList[48] = {0, 5, 7, 0, 7, 2, 0, 7, 5, 0, 2, 7, 0, 4, 9, 0, 9, 5, 0, 9, 4, 0, 5, 9, 0, 1, 6, 0, 6, 5, 0, 6, 1, 0, 5, 6, 0, 3, 8, 0, 8, 5, 0, 8, 3, 0, 5, 8 }; bool CSpecialFX::bVideoCam; bool CSpecialFX::bLiftCam; bool CSpecialFX::bSnapShotActive; int32 CSpecialFX::SnapShotFrames; static RwTexture* gpSmokeTrailTexture; void CSpecialFX::Init(void) { CBulletTraces::Init(); RwIm3DVertexSetU(&TraceVertices[0], 0.0); RwIm3DVertexSetV(&TraceVertices[0], 0.0); RwIm3DVertexSetU(&TraceVertices[1], 1.0); RwIm3DVertexSetV(&TraceVertices[1], 0.0); RwIm3DVertexSetU(&TraceVertices[2], 1.0); RwIm3DVertexSetV(&TraceVertices[2], 0.0); RwIm3DVertexSetU(&TraceVertices[3], 1.0); RwIm3DVertexSetV(&TraceVertices[3], 0.0); RwIm3DVertexSetU(&TraceVertices[4], 1.0); RwIm3DVertexSetV(&TraceVertices[4], 0.0); RwIm3DVertexSetU(&TraceVertices[5], 0.0); RwIm3DVertexSetU(&TraceVertices[6], 1.0); RwIm3DVertexSetU(&TraceVertices[7], 1.0); RwIm3DVertexSetU(&TraceVertices[8], 1.0); RwIm3DVertexSetU(&TraceVertices[9], 1.0); RwIm3DVertexSetU(&StreakVertices[0], 0.0f); RwIm3DVertexSetV(&StreakVertices[0], 0.0f); RwIm3DVertexSetU(&StreakVertices[1], 1.0f); RwIm3DVertexSetV(&StreakVertices[1], 0.0f); RwIm3DVertexSetU(&StreakVertices[2], 0.0f); RwIm3DVertexSetV(&StreakVertices[2], 0.0f); RwIm3DVertexSetU(&StreakVertices[3], 1.0f); RwIm3DVertexSetV(&StreakVertices[3], 0.0f); StreakIndexList[0] = 0; StreakIndexList[1] = 1; StreakIndexList[2] = 2; StreakIndexList[3] = 1; StreakIndexList[4] = 3; StreakIndexList[5] = 2; StreakIndexList[6] = 0; StreakIndexList[7] = 2; StreakIndexList[8] = 1; StreakIndexList[9] = 1; StreakIndexList[10] = 2; StreakIndexList[11] = 3; CMotionBlurStreaks::Init(); CBrightLights::Init(); CShinyTexts::Init(); CMoneyMessages::Init(); C3dMarkers::Init(); CSpecialFX::bSnapShotActive = false; CSpecialFX::bVideoCam = false; CSpecialFX::SnapShotFrames = 0; CSpecialFX::bLiftCam = false; CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle")); if(gpSmokeTrailTexture == nil) gpSmokeTrailTexture = RwTextureRead("smoketrail", 0); CTxdStore::PopCurrentTxd(); } void CSpecialFX::AddWeaponStreak(int type) { static CMatrix matrix; CVector start; CVector end; if (FindPlayerPed() != nil && FindPlayerPed()->m_pWeaponModel != nil) { switch (type) { case WEAPONTYPE_BASEBALLBAT: matrix = RwFrameGetLTM(RpAtomicGetFrame(FindPlayerPed()->m_pWeaponModel)); start = matrix * CVector(0.02f, 0.05f, 0.07f); end = matrix * CVector(0.246f, 0.0325f, 0.796f); break; case WEAPONTYPE_GOLFCLUB: matrix = RwFrameGetLTM(RpAtomicGetFrame(FindPlayerPed()->m_pWeaponModel)); start = matrix * CVector(0.02f, 0.05f, 0.07f); end = matrix * CVector(-0.054f, 0.0325f, 0.796f); break; case WEAPONTYPE_KATANA: matrix = RwFrameGetLTM(RpAtomicGetFrame(FindPlayerPed()->m_pWeaponModel)); start = matrix * CVector(0.02f, 0.05f, 0.07f); end = matrix * CVector(0.096f, -0.0175f, 1.096f); break; default: return; } CMotionBlurStreaks::RegisterStreak((uintptr)FindPlayerPed()->m_pWeaponModel, 100, 100, 100, start, end); } } RwObject* LookForBatCB(RwObject *object, void *data) { static CMatrix MatLTM; if(CVisibilityPlugins::GetAtomicModelInfo((RpAtomic*)object) == (CSimpleModelInfo*)data){ MatLTM = CMatrix(RwFrameGetLTM(RpAtomicGetFrame((RpAtomic*)object))); CVector p1 = MatLTM * CVector(0.02f, 0.05f, 0.07f); CVector p2 = MatLTM * CVector(0.246f, 0.0325f, 0.796f); CMotionBlurStreaks::RegisterStreak((uintptr)object, 100, 100, 100, p1, p2); } return nil; } void CSpecialFX::Update(void) { CMotionBlurStreaks::Update(); CBulletTraces::Update(); } void CSpecialFX::Shutdown(void) { C3dMarkers::Shutdown(); if (gpSmokeTrailTexture) { RwTextureDestroy(gpSmokeTrailTexture); gpSmokeTrailTexture = nil; } } void CSpecialFX::Render(void) { PUSH_RENDERGROUP("CSpecialFX::Render"); CMotionBlurStreaks::Render(); CBulletTraces::Render(); CBrightLights::Render(); CShinyTexts::Render(); CMoneyMessages::Render(); #ifdef NEW_RENDERER if(!(gbNewRenderer && FredIsInFirstPersonCam())) #endif C3dMarkers::Render(); POP_RENDERGROUP(); } void CSpecialFX::Render2DFXs(void) { if (CSpecialFX::bVideoCam) { CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f)); CFont::SetJustifyOff(); CFont::SetBackgroundOff(); CFont::SetCentreSize(SCREEN_SCALE_X(620.0f)); // unused CFont::SetCentreOff(); CFont::SetPropOn(); CFont::SetColor(CRGBA(0, 255, 0, 200)); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); sprintf(gString, "%d", CTimer::GetFrameCounter() & 0x3F); // mb % 63 AsciiToUnicode(gString, gUString); CFont::PrintString(SCREEN_WIDTH * 8 / 10, SCREEN_HEIGHT * 8 / 10, gUString); for (int32 i = 0; i < SCREEN_HEIGHT; i += 4) { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); CSprite2d::Draw2DPolygon(0.0f, i, SCREEN_WIDTH, i, 0.0f, i+1, SCREEN_WIDTH, i+1, CRGBA(0, 100, 0, 100)); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); CSprite2d::Draw2DPolygon(0.0f, i+2, SCREEN_WIDTH, i+2, 0.0f, i+3, SCREEN_WIDTH, i+3, CRGBA(0, 0, 0, 150)); } int32 tmp = (CTimer::GetTimeInMilliseconds() & 0x7ff) * (SCREEN_HEIGHT + 70.0f) / 2048 - 70.0f; //mb % 2048 RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); CSprite2d::Draw2DPolygon(0.0, tmp, SCREEN_WIDTH, tmp, 0.0, tmp + 70.0f, SCREEN_WIDTH, tmp + 70.0f , CRGBA(0, 100, 0, 60)); } if (CSpecialFX::bLiftCam) { CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f)); CFont::SetJustifyOff(); CFont::SetBackgroundOff(); CFont::SetCentreSize(SCREEN_SCALE_X(620.0f)); // unused CFont::SetCentreOff(); CFont::SetPropOn(); CFont::SetColor(CRGBA(100, 100, 100, 200)); CFont::SetFontStyle(FONT_LOCALE(FONT_STANDARD)); CFont::PrintString(SCREEN_WIDTH * 8 / 10, SCREEN_HEIGHT * 8 / 10, gUString); for (int32 i = 0; i < SCREEN_HEIGHT; i += 4) { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); CSprite2d::Draw2DPolygon(0.0f, i, SCREEN_WIDTH, i, 0.0f, i + 1, SCREEN_WIDTH, i + 1, CRGBA(100, 100, 100, 100)); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); CSprite2d::Draw2DPolygon(0.0f, i + 2, SCREEN_WIDTH, i + 2, 0.0f, i + 3, SCREEN_WIDTH, i + 3, CRGBA(0, 0, 0, 150)); } int32 tmp = (CTimer::GetTimeInMilliseconds() & 0x7ff) * (SCREEN_HEIGHT + 70.0f) / 2048 - 70.0f; //mb % 2048 RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); CSprite2d::Draw2DPolygon(0.0, tmp, SCREEN_WIDTH, tmp, 0.0, tmp + 70.0f, SCREEN_WIDTH, tmp + 70.0f, CRGBA(100, 100, 100, 60)); for (int32 i = 0; i < 200; i++) { int32 posX = CGeneral::GetRandomNumber() % (int32)SCREEN_WIDTH; int32 posY = CGeneral::GetRandomNumber() % (int32)SCREEN_HEIGHT; CSprite2d::DrawRect(CRect(posX, posY + 2, posX+20, posY), CRGBA(255, 255, 255, 64)); } } if (CSpecialFX::bSnapShotActive) { if (++CSpecialFX::SnapShotFrames > 20) { CSpecialFX::bSnapShotActive = false; CTimer::SetTimeScale(1.0f); } else { CTimer::SetTimeScale(0.0f); //in andro it's 0.00001 if (CSpecialFX::SnapShotFrames < 10) { int32 tmp = (255 - 255 * CSpecialFX::SnapShotFrames / 10) * 0.65f; RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); CSprite2d::Draw2DPolygon(0.0f, 0.0f, SCREEN_WIDTH, 0.0f, 0.0f, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT, CRGBA(tmp, tmp, tmp, tmp)); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); } } } } CRegisteredMotionBlurStreak CMotionBlurStreaks::aStreaks[NUMMBLURSTREAKS]; void CRegisteredMotionBlurStreak::Update(void) { int i; bool wasUpdated; bool lastWasUpdated = false; for(i = 2; i > 0; i--){ m_pos1[i] = m_pos1[i-1]; m_pos2[i] = m_pos2[i-1]; m_isValid[i] = m_isValid[i-1]; wasUpdated = true; if(!lastWasUpdated && !m_isValid[i]) wasUpdated = false; lastWasUpdated = wasUpdated; } m_isValid[0] = false; if(!wasUpdated) m_id = 0; } void CRegisteredMotionBlurStreak::Render(void) { int i; int a1, a2; for(i = 0; i < 2; i++) if(m_isValid[i] && m_isValid[i+1]){ a1 = (255/3)*(3-i)/3; RwIm3DVertexSetRGBA(&StreakVertices[0], m_red, m_green, m_blue, a1); RwIm3DVertexSetRGBA(&StreakVertices[1], m_red, m_green, m_blue, a1); a2 = (255/3)*(3-(i+1))/3; RwIm3DVertexSetRGBA(&StreakVertices[2], m_red, m_green, m_blue, a2); RwIm3DVertexSetRGBA(&StreakVertices[3], m_red, m_green, m_blue, a2); RwIm3DVertexSetPos(&StreakVertices[0], m_pos1[i].x, m_pos1[i].y, m_pos1[i].z); RwIm3DVertexSetPos(&StreakVertices[1], m_pos2[i].x, m_pos2[i].y, m_pos2[i].z); RwIm3DVertexSetPos(&StreakVertices[2], m_pos1[i+1].x, m_pos1[i+1].y, m_pos1[i+1].z); RwIm3DVertexSetPos(&StreakVertices[3], m_pos2[i+1].x, m_pos2[i+1].y, m_pos2[i+1].z); LittleTest(); if(RwIm3DTransform(StreakVertices, 4, nil, rwIM3D_VERTEXUV)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, StreakIndexList, 12); RwIm3DEnd(); } } } void CMotionBlurStreaks::Init(void) { int i; for(i = 0; i < NUMMBLURSTREAKS; i++) aStreaks[i].m_id = 0; } void CMotionBlurStreaks::Update(void) { int i; for(i = 0; i < NUMMBLURSTREAKS; i++) if(aStreaks[i].m_id != 0) aStreaks[i].Update(); } void CMotionBlurStreaks::RegisterStreak(uintptr id, uint8 r, uint8 g, uint8 b, CVector p1, CVector p2) { int i; for(i = 0; i < NUMMBLURSTREAKS; i++){ if(aStreaks[i].m_id == id){ // Found a streak from last frame, update aStreaks[i].m_red = r; aStreaks[i].m_green = g; aStreaks[i].m_blue = b; aStreaks[i].m_pos1[0] = p1; aStreaks[i].m_pos2[0] = p2; aStreaks[i].m_isValid[0] = true; return; } } // Find free slot for(i = 0; aStreaks[i].m_id != 0 ; i++) if(i == NUMMBLURSTREAKS-1) return; // Create a new streak aStreaks[i].m_id = id; aStreaks[i].m_red = r; aStreaks[i].m_green = g; aStreaks[i].m_blue = b; aStreaks[i].m_pos1[0] = p1; aStreaks[i].m_pos2[0] = p2; aStreaks[i].m_isValid[0] = true; aStreaks[i].m_isValid[1] = false; aStreaks[i].m_isValid[2] = false; } void CMotionBlurStreaks::Render(void) { bool setRenderStates = false; int i; for(i = 0; i < NUMMBLURSTREAKS; i++) if(aStreaks[i].m_id != 0){ if(!setRenderStates){ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEFOGCOLOR, (void*)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255)); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void*)FALSE); setRenderStates = true; } aStreaks[i].Render(); } if(setRenderStates){ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); } } CBulletTrace CBulletTraces::aTraces[NUMBULLETTRACES]; void CBulletTraces::Init(void) { for (int i = 0; i < NUMBULLETTRACES; i++) aTraces[i].m_bInUse = false; } void CBulletTraces::AddTrace(CVector* start, CVector* end, float thickness, uint32 lifeTime, uint8 visibility) { int32 enabledCount; uint32 modifiedLifeTime; int32 nextSlot; enabledCount = 0; for (int i = 0; i < NUMBULLETTRACES; i++) if (aTraces[i].m_bInUse) enabledCount++; if (enabledCount >= 10) modifiedLifeTime = lifeTime / 4; else if (enabledCount >= 5) modifiedLifeTime = lifeTime / 2; else modifiedLifeTime = lifeTime; nextSlot = 0; for (int i = 0; nextSlot < NUMBULLETTRACES && aTraces[i].m_bInUse; i++) nextSlot++; if (nextSlot < 16) { aTraces[nextSlot].m_vecStartPos = *start; aTraces[nextSlot].m_vecEndPos = *end; aTraces[nextSlot].m_bInUse = true; aTraces[nextSlot].m_nCreationTime = CTimer::GetTimeInMilliseconds(); aTraces[nextSlot].m_fVisibility = visibility; aTraces[nextSlot].m_fThickness = thickness; aTraces[nextSlot].m_nLifeTime = modifiedLifeTime; } float startProjFwd = DotProduct(TheCamera.GetForward(), *start - TheCamera.GetPosition()); float endProjFwd = DotProduct(TheCamera.GetForward(), *end - TheCamera.GetPosition()); if (startProjFwd * endProjFwd < 0.0f) { //if one of point behind us and second before us float fStartDistFwd = Abs(startProjFwd) / (Abs(startProjFwd) + Abs(endProjFwd)); float startProjUp = DotProduct(TheCamera.GetUp(), *start - TheCamera.GetPosition()); float endProjUp = DotProduct(TheCamera.GetUp(), *end - TheCamera.GetPosition()); float distUp = (endProjUp - startProjUp) * fStartDistFwd + startProjUp; float startProjRight = DotProduct(TheCamera.GetRight(), *start - TheCamera.GetPosition()); float endProjRight = DotProduct(TheCamera.GetRight(), *end - TheCamera.GetPosition()); float distRight = (endProjRight - startProjRight) * fStartDistFwd + startProjRight; float dist = Sqrt(SQR(distUp) + SQR(distRight)); if (dist < 2.0f) { if(distRight < 0.0f) DMAudio.PlayFrontEndSound(SOUND_BULLETTRACE_2, 127 * (1.0f - dist * 0.5f)); else DMAudio.PlayFrontEndSound(SOUND_BULLETTRACE_1, 127 * (1.0f - dist * 0.5f)); } } } void CBulletTraces::AddTrace(CVector* start, CVector* end, int32 weaponType, class CEntity* shooter) { CPhysical* player; float speed; int16 camMode; if (shooter == (CEntity*)FindPlayerPed() || (FindPlayerVehicle() != nil && FindPlayerVehicle() == (CVehicle*)shooter)) { camMode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if (camMode == CCam::MODE_M16_1STPERSON || camMode == CCam::MODE_CAMERA || camMode == CCam::MODE_SNIPER || camMode == CCam::MODE_M16_1STPERSON_RUNABOUT || camMode == CCam::MODE_ROCKETLAUNCHER || camMode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || camMode == CCam::MODE_SNIPER_RUNABOUT || camMode == CCam::MODE_HELICANNON_1STPERSON) { player = FindPlayerVehicle() ? (CPhysical*)FindPlayerVehicle() : (CPhysical*)FindPlayerPed(); speed = player->m_vecMoveSpeed.Magnitude(); if (speed < 0.05f) return; } } switch (weaponType) { case WEAPONTYPE_PYTHON: case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_SPAS12_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: CBulletTraces::AddTrace(start, end, 0.7f, 1000, 200); break; case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: case WEAPONTYPE_M60: case WEAPONTYPE_MINIGUN: case WEAPONTYPE_HELICANNON: CBulletTraces::AddTrace(start, end, 1.0f, 2000, 220); break; default: CBulletTraces::AddTrace(start, end, 0.4f, 750, 150); break; } } void CBulletTraces::Render(void) { for (int i = 0; i < NUMBULLETTRACES; i++) { if (!aTraces[i].m_bInUse) continue; RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpSmokeTrailTexture)); float timeAlive = CTimer::GetTimeInMilliseconds() - aTraces[i].m_nCreationTime; float traceThickness = aTraces[i].m_fThickness * timeAlive / aTraces[i].m_nLifeTime; CVector horizontalOffset = aTraces[i].m_vecEndPos - aTraces[i].m_vecStartPos; horizontalOffset.Normalise(); horizontalOffset *= traceThickness; //then closer trace to die then it more transparent uint8 nAlphaValue = aTraces[i].m_fVisibility * (aTraces[i].m_nLifeTime - timeAlive) / aTraces[i].m_nLifeTime; CVector start = aTraces[i].m_vecStartPos; CVector end = aTraces[i].m_vecEndPos; float startProj = DotProduct(start - TheCamera.GetPosition(), TheCamera.GetForward()) - 0.7f; float endProj = DotProduct(end - TheCamera.GetPosition(), TheCamera.GetForward()) - 0.7f; if (startProj < 0.0f && endProj < 0.0f) //we dont need render trace behind us continue; if (startProj < 0.0f) { //if strat behind us move it closer float absStartProj = Abs(startProj); float absEndProj = Abs(endProj); start = (absEndProj * start + absStartProj * end) / (absStartProj + absEndProj); } else if (endProj < 0.0f) { float absStartProj = Abs(startProj); float absEndProj = Abs(endProj); end = (absEndProj * start + absStartProj * end) / (absStartProj + absEndProj); } //we divide trace at three parts CVector start2 = (7.0f * start + end) / 8; CVector end2 = (7.0f * end + start) / 8; RwIm3DVertexSetV(&TraceVertices[5], 10.0f); RwIm3DVertexSetV(&TraceVertices[6], 10.0f); RwIm3DVertexSetV(&TraceVertices[7], 10.0f); RwIm3DVertexSetV(&TraceVertices[8], 10.0f); RwIm3DVertexSetV(&TraceVertices[9], 10.0f); RwIm3DVertexSetRGBA(&TraceVertices[0], 255, 255, 255, nAlphaValue); RwIm3DVertexSetRGBA(&TraceVertices[1], 255, 255, 255, nAlphaValue); RwIm3DVertexSetRGBA(&TraceVertices[2], 255, 255, 255, nAlphaValue); RwIm3DVertexSetRGBA(&TraceVertices[3], 255, 255, 255, nAlphaValue); RwIm3DVertexSetRGBA(&TraceVertices[4], 255, 255, 255, nAlphaValue); RwIm3DVertexSetRGBA(&TraceVertices[5], 255, 255, 255, nAlphaValue); RwIm3DVertexSetRGBA(&TraceVertices[6], 255, 255, 255, nAlphaValue); RwIm3DVertexSetRGBA(&TraceVertices[7], 255, 255, 255, nAlphaValue); RwIm3DVertexSetRGBA(&TraceVertices[8], 255, 255, 255, nAlphaValue); RwIm3DVertexSetRGBA(&TraceVertices[9], 255, 255, 255, nAlphaValue); //two points in center RwIm3DVertexSetPos(&TraceVertices[0], start2.x, start2.y, start2.z); RwIm3DVertexSetPos(&TraceVertices[5], end2.x, end2.y, end2.z); //vertical planes RwIm3DVertexSetPos(&TraceVertices[1], start2.x, start2.y, start2.z + traceThickness); RwIm3DVertexSetPos(&TraceVertices[3], start2.x, start2.y, start2.z - traceThickness); RwIm3DVertexSetPos(&TraceVertices[6], end2.x, end2.y, end2.z + traceThickness); RwIm3DVertexSetPos(&TraceVertices[8], end2.x, end2.y, end2.z - traceThickness); //horizontal planes RwIm3DVertexSetPos(&TraceVertices[2], start2.x + horizontalOffset.y, start2.y - horizontalOffset.x, start2.z); RwIm3DVertexSetPos(&TraceVertices[7], end2.x + horizontalOffset.y, end2.y - horizontalOffset.x, end2.z); #ifdef FIX_BUGS //this point calculated wrong for some reason RwIm3DVertexSetPos(&TraceVertices[4], start2.x - horizontalOffset.y, start2.y + horizontalOffset.x, start2.z); RwIm3DVertexSetPos(&TraceVertices[9], end2.x - horizontalOffset.y, end2.y + horizontalOffset.x, end2.z); #else RwIm3DVertexSetPos(&TraceVertices[4], start2.x - horizontalOffset.y, start2.y - horizontalOffset.y, start2.z); RwIm3DVertexSetPos(&TraceVertices[9], end2.x - horizontalOffset.y, end2.y - horizontalOffset.y, end2.z); #endif if (RwIm3DTransform(TraceVertices, ARRAY_SIZE(TraceVertices), nil, 1)) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TraceIndexList, ARRAY_SIZE(TraceIndexList)); RwIm3DEnd(); } RwIm3DVertexSetV(&TraceVertices[5], 2.0f); RwIm3DVertexSetV(&TraceVertices[6], 2.0f); RwIm3DVertexSetV(&TraceVertices[7], 2.0f); RwIm3DVertexSetV(&TraceVertices[8], 2.0f); RwIm3DVertexSetV(&TraceVertices[9], 2.0f); RwIm3DVertexSetRGBA(&TraceVertices[0], 255, 255, 255, 0); RwIm3DVertexSetRGBA(&TraceVertices[1], 255, 255, 255, 0); RwIm3DVertexSetRGBA(&TraceVertices[2], 255, 255, 255, 0); RwIm3DVertexSetRGBA(&TraceVertices[3], 255, 255, 255, 0); RwIm3DVertexSetRGBA(&TraceVertices[4], 255, 255, 255, 0); RwIm3DVertexSetPos(&TraceVertices[0], start.x, start.y, start.z); RwIm3DVertexSetPos(&TraceVertices[1], start.x, start.y, start.z + traceThickness); RwIm3DVertexSetPos(&TraceVertices[3], start.x, start.y, start.z - traceThickness); RwIm3DVertexSetPos(&TraceVertices[2], start.x + horizontalOffset.y, start.y - horizontalOffset.x, start.z); RwIm3DVertexSetPos(&TraceVertices[5], start2.x, start2.y, start2.z); RwIm3DVertexSetPos(&TraceVertices[6], start2.x, start2.y, start2.z + traceThickness); RwIm3DVertexSetPos(&TraceVertices[8], start2.x, start2.y, start2.z - traceThickness); RwIm3DVertexSetPos(&TraceVertices[7], start2.x + horizontalOffset.y, start2.y - horizontalOffset.x, start2.z); #ifdef FIX_BUGS RwIm3DVertexSetPos(&TraceVertices[4], start.x - horizontalOffset.y, start.y + horizontalOffset.x, start.z); RwIm3DVertexSetPos(&TraceVertices[9], start2.x - horizontalOffset.y, start2.y + horizontalOffset.x, start2.z); #else RwIm3DVertexSetPos(&TraceVertices[4], start.x - horizontalOffset.y, start.y - horizontalOffset.y, start.z); RwIm3DVertexSetPos(&TraceVertices[9], start2.x - horizontalOffset.y, start2.y - horizontalOffset.y, start2.z); #endif if (RwIm3DTransform(TraceVertices, ARRAY_SIZE(TraceVertices), nil, rwIM3D_VERTEXUV)) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TraceIndexList, ARRAY_SIZE(TraceIndexList)); RwIm3DEnd(); } RwIm3DVertexSetPos(&TraceVertices[1], end.x, end.y, end.z); RwIm3DVertexSetPos(&TraceVertices[2], end.x, end.y, end.z + traceThickness); RwIm3DVertexSetPos(&TraceVertices[4], end.x, end.y, end.z - traceThickness); RwIm3DVertexSetPos(&TraceVertices[3], end.x + horizontalOffset.y, end.y - horizontalOffset.x, end.z); RwIm3DVertexSetPos(&TraceVertices[5], end2.x, end2.y, end2.z); RwIm3DVertexSetPos(&TraceVertices[6], end2.x, end2.y, end2.z + traceThickness); RwIm3DVertexSetPos(&TraceVertices[8], end2.x, end2.y, end2.z - traceThickness); RwIm3DVertexSetPos(&TraceVertices[7], end2.x + horizontalOffset.y, end2.y - horizontalOffset.x, end2.z); #ifdef FIX_BUGS RwIm3DVertexSetPos(&TraceVertices[5], end.x - horizontalOffset.y, end.y + horizontalOffset.x, end.z); RwIm3DVertexSetPos(&TraceVertices[9], end2.x - horizontalOffset.y, end2.y + horizontalOffset.x, end2.z); #else RwIm3DVertexSetPos(&TraceVertices[5], end.x - horizontalOffset.y, end.y - horizontalOffset.y, end.z); RwIm3DVertexSetPos(&TraceVertices[9], end2.x - horizontalOffset.y, end2.y - horizontalOffset.y, end2.z); #endif if (RwIm3DTransform(TraceVertices, ARRAY_SIZE(TraceVertices), nil, rwIM3D_VERTEXUV)) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TraceIndexList, ARRAY_SIZE(TraceIndexList)); RwIm3DEnd(); } } RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); } void CBulletTraces::Update(void) { for (int i = 0; i < NUMBULLETTRACES; i++) { if (aTraces[i].m_bInUse) aTraces[i].Update(); } } void CBulletTrace::Update(void) { if (CTimer::GetTimeInMilliseconds() - m_nCreationTime >= m_nLifeTime) m_bInUse = false; } RpAtomic * MarkerAtomicCB(RpAtomic *atomic, void *data) { *(RpAtomic**)data = atomic; return atomic; } bool C3dMarker::AddMarker(uint32 identifier, uint16 type, float fSize, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) { m_nIdentifier = identifier; m_Matrix.SetUnity(); RpAtomic *origAtomic; origAtomic = nil; RpClumpForAllAtomics(C3dMarkers::m_pRpClumpArray[type], MarkerAtomicCB, &origAtomic); RpAtomic *atomic = RpAtomicClone(origAtomic); RwFrame *frame = RwFrameCreate(); RpAtomicSetFrame(atomic, frame); CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); RpGeometry *geometry = RpAtomicGetGeometry(atomic); RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR); m_pAtomic = atomic; m_Matrix.Attach(RwFrameGetMatrix(RpAtomicGetFrame(m_pAtomic))); m_pMaterial = RpGeometryGetMaterial(geometry, 0); m_fSize = fSize; m_fStdSize = m_fSize; m_Color.red = r; m_Color.green = g; m_Color.blue = b; m_Color.alpha = a; m_nPulsePeriod = pulsePeriod; m_fPulseFraction = pulseFraction; m_nRotateRate = rotateRate; m_nStartTime = CTimer::GetTimeInMilliseconds(); m_nType = type; return m_pAtomic != nil; } void C3dMarker::DeleteMarkerObject() { RwFrame *frame; m_nIdentifier = 0; m_nStartTime = 0; m_bIsUsed = false; m_bFindZOnNextPlacement = false; m_nType = MARKERTYPE_INVALID; frame = RpAtomicGetFrame(m_pAtomic); RpAtomicDestroy(m_pAtomic); RwFrameDestroy(frame); m_pAtomic = nil; } void C3dMarker::Render() { if (m_pAtomic == nil) return; RpMaterialSetColor(m_pMaterial, &m_Color); m_Matrix.UpdateRW(); CMatrix matrix; matrix.Attach(m_Matrix.m_attachment); matrix.Scale(m_fSize); matrix.UpdateRW(); RwFrameUpdateObjects(RpAtomicGetFrame(m_pAtomic)); SetBrightMarkerColours(m_fBrightness); if (m_nType != MARKERTYPE_ARROW) RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RpAtomicRender(m_pAtomic); if (m_nType != MARKERTYPE_ARROW) RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); ReSetAmbientAndDirectionalColours(); } C3dMarker C3dMarkers::m_aMarkerArray[NUM3DMARKERS]; int32 C3dMarkers::NumActiveMarkers; RpClump* C3dMarkers::m_pRpClumpArray[NUMMARKERTYPES]; void C3dMarkers::Init() { for (int i = 0; i < NUM3DMARKERS; i++) { m_aMarkerArray[i].m_pAtomic = nil; m_aMarkerArray[i].m_nType = MARKERTYPE_INVALID; m_aMarkerArray[i].m_bIsUsed = false; m_aMarkerArray[i].m_bFindZOnNextPlacement = false; m_aMarkerArray[i].m_nIdentifier = 0; m_aMarkerArray[i].m_Color.red = 255; m_aMarkerArray[i].m_Color.green = 255; m_aMarkerArray[i].m_Color.blue = 255; m_aMarkerArray[i].m_Color.alpha = 255; m_aMarkerArray[i].m_nPulsePeriod = 1024; m_aMarkerArray[i].m_nRotateRate = 5; m_aMarkerArray[i].m_nStartTime = 0; m_aMarkerArray[i].m_fPulseFraction = 0.25f; m_aMarkerArray[i].m_fStdSize = 1.0f; m_aMarkerArray[i].m_fSize = 1.0f; m_aMarkerArray[i].m_fBrightness = 1.0f; m_aMarkerArray[i].m_fCameraRange = 0.0f; } NumActiveMarkers = 0; int txdSlot = CTxdStore::FindTxdSlot("particle"); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(txdSlot); CFileMgr::ChangeDir("\\"); m_pRpClumpArray[MARKERTYPE_ARROW] = CFileLoader::LoadAtomicFile2Return("models/generic/arrow.dff"); m_pRpClumpArray[MARKERTYPE_CYLINDER] = CFileLoader::LoadAtomicFile2Return("models/generic/zonecylb.dff"); CTxdStore::PopCurrentTxd(); } void C3dMarkers::Shutdown() { for (int i = 0; i < NUM3DMARKERS; i++) { if (m_aMarkerArray[i].m_pAtomic != nil) m_aMarkerArray[i].DeleteMarkerObject(); } for (int i = 0; i < NUMMARKERTYPES; i++) { if (m_pRpClumpArray[i] != nil) RpClumpDestroy(m_pRpClumpArray[i]); } } void C3dMarkers::Render() { NumActiveMarkers = 0; ActivateDirectional(); for (int i = 0; i < NUM3DMARKERS; i++) { if (m_aMarkerArray[i].m_bIsUsed) { if (m_aMarkerArray[i].m_fCameraRange < 150.0f) { m_aMarkerArray[i].Render(); if (m_aMarkerArray[i].m_nType == MARKERTYPE_ARROW) { CCoronas::RegisterCorona((uintptr)&m_aMarkerArray[i], SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, 192, m_aMarkerArray[i].m_Matrix.GetPosition(), 1.2f * m_aMarkerArray[i].m_fSize, 50.0f * TheCamera.LODDistMultiplier, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f, false); } } NumActiveMarkers++; m_aMarkerArray[i].m_bIsUsed = false; } else if (m_aMarkerArray[i].m_pAtomic != nil) { m_aMarkerArray[i].DeleteMarkerObject(); } } } C3dMarker * C3dMarkers::PlaceMarker(uint32 identifier, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) { C3dMarker *pMarker; CVector2D playerPos = FindPlayerCentreOfWorld(0); pMarker = nil; float dist = ((CVector2D)pos - playerPos).Magnitude(); if (type != MARKERTYPE_ARROW && type != MARKERTYPE_CYLINDER) return nil; for (int i = 0; i < NUM3DMARKERS; i++) { if (!m_aMarkerArray[i].m_bIsUsed && m_aMarkerArray[i].m_nIdentifier == identifier) { pMarker = &m_aMarkerArray[i]; break; } } if (pMarker == nil) { for (int i = 0; i < NUM3DMARKERS; i++) { if (m_aMarkerArray[i].m_nType == MARKERTYPE_INVALID) { pMarker = &m_aMarkerArray[i]; break; } } } if (pMarker == nil && type == MARKERTYPE_ARROW) { for (int i = 0; i < NUM3DMARKERS; i++) { if (dist < m_aMarkerArray[i].m_fCameraRange && m_aMarkerArray[i].m_nType == MARKERTYPE_ARROW && (pMarker == nil || m_aMarkerArray[i].m_fCameraRange > pMarker->m_fCameraRange)) { pMarker = &m_aMarkerArray[i]; break; } } if (pMarker != nil) pMarker->m_nType = MARKERTYPE_INVALID; } if (pMarker == nil) return pMarker; pMarker->m_fCameraRange = dist; if (pMarker->m_nIdentifier == identifier && pMarker->m_nType == type) { if (type == MARKERTYPE_ARROW) { if (dist < 25.0f) { if (dist > 5.0f) pMarker->m_fStdSize = size - (25.0f - dist) * (0.3f * size) / 20.0f; else pMarker->m_fStdSize = size - 0.3f * size; } else { pMarker->m_fStdSize = size; } } else if (type == MARKERTYPE_CYLINDER) { if (dist < size + 12.0f) { if (dist > size + 1.0f) pMarker->m_Color.alpha = (1.0f - (size + 12.0f - dist) * 0.7f / 11.0f) * (float)a; else pMarker->m_Color.alpha = (float)a * 0.3f; } else { pMarker->m_Color.alpha = a; } } float someSin = Sin(TWOPI * (float)((pMarker->m_nPulsePeriod - 1) & (CTimer::GetTimeInMilliseconds() - pMarker->m_nStartTime)) / (float)pMarker->m_nPulsePeriod); pMarker->m_fSize = pMarker->m_fStdSize - pulseFraction * pMarker->m_fStdSize * someSin; if (type == MARKERTYPE_ARROW) { pos.z += 0.25f * pMarker->m_fStdSize * someSin; } else if (type == MARKERTYPE_0) { if (someSin > 0.0f) pMarker->m_Color.alpha = (float)a * 0.7f * someSin + a; else pMarker->m_Color.alpha = (float)a * 0.4f * someSin + a; } if (pMarker->m_nRotateRate != 0) { CVector pos = pMarker->m_Matrix.GetPosition(); pMarker->m_Matrix.RotateZ(DEGTORAD(pMarker->m_nRotateRate * CTimer::GetTimeStep())); pMarker->m_Matrix.GetPosition() = pos; } if (type == MARKERTYPE_ARROW) pMarker->m_Matrix.GetPosition() = pos; if (pMarker->m_bFindZOnNextPlacement) { if ((playerPos - pos).MagnitudeSqr() < sq(100.f) && CColStore::HasCollisionLoaded(CVector2D(pos))) { float z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 1.0f, nil); if (z != 0.0f) pMarker->m_Matrix.GetPosition().z = z - 0.05f * size; pMarker->m_bFindZOnNextPlacement = false; } } pMarker->m_bIsUsed = true; return pMarker; } if (pMarker->m_nIdentifier != 0) pMarker->DeleteMarkerObject(); pMarker->AddMarker(identifier, type, size, r, g, b, a, pulsePeriod, pulseFraction, rotateRate); if (type == MARKERTYPE_CYLINDER || type == MARKERTYPE_0 || type == MARKERTYPE_2) { if ((playerPos - pos).MagnitudeSqr() < sq(100.f) && CColStore::HasCollisionLoaded(CVector2D(pos))) { float z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 1.0f, nil); if (z != 0.0f) pos.z = z - 0.05f * size; pMarker->m_bFindZOnNextPlacement = false; } else { pMarker->m_bFindZOnNextPlacement = true; } } pMarker->m_Matrix.SetTranslate(pos.x, pos.y, pos.z); if (type == MARKERTYPE_2) { pMarker->m_Matrix.RotateX(PI); pMarker->m_Matrix.GetPosition() = pos; } pMarker->m_Matrix.UpdateRW(); if (type == MARKERTYPE_ARROW) { if (dist < 25.0f) { if (dist > 5.0f) pMarker->m_fStdSize = size - (25.0f - dist) * (0.3f * size) / 20.0f; else pMarker->m_fStdSize = size - 0.3f * size; } else { pMarker->m_fStdSize = size; } } else if (type == MARKERTYPE_CYLINDER) { if (dist < size + 12.0f) { if (dist > size + 1.0f) pMarker->m_Color.alpha = (1.0f - (size + 12.0f - dist) * 0.7f / 11.0f) * (float)a; else pMarker->m_Color.alpha = (float)a * 0.3f; } else { pMarker->m_Color.alpha = a; } } pMarker->m_bIsUsed = true; return pMarker; } void C3dMarkers::PlaceMarkerSet(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate) { PlaceMarker(id, type, pos, size, r, g, b, a, pulsePeriod, pulseFraction, 1); PlaceMarker(id, type, pos, size * 0.93f, r, g, b, a, pulsePeriod, pulseFraction, 2); PlaceMarker(id, type, pos, size * 0.86f, r, g, b, a, pulsePeriod, pulseFraction, -1); } void C3dMarkers::Update() { } #define BRIGHTLIGHTS_MAX_DIST (60.0f) // invisible beyond this #define BRIGHTLIGHTS_FADE_DIST (45.0f) // strongest between these two #define CARLIGHTS_MAX_DIST (30.0f) #define CARLIGHTS_FADE_DIST (15.0f) // 31 for close lights int CBrightLights::NumBrightLights; CBrightLight CBrightLights::aBrightLights[NUMBRIGHTLIGHTS]; void CBrightLights::Init(void) { NumBrightLights = 0; } void CBrightLights::RegisterOne(CVector pos, CVector up, CVector side, CVector front, uint8 type, uint8 red, uint8 green, uint8 blue) { if(NumBrightLights >= NUMBRIGHTLIGHTS) return; aBrightLights[NumBrightLights].m_camDist = (pos - TheCamera.GetPosition()).Magnitude(); if(aBrightLights[NumBrightLights].m_camDist > BRIGHTLIGHTS_MAX_DIST) return; aBrightLights[NumBrightLights].m_pos = pos; aBrightLights[NumBrightLights].m_up = up; aBrightLights[NumBrightLights].m_side = side; aBrightLights[NumBrightLights].m_front = front; aBrightLights[NumBrightLights].m_type = type; aBrightLights[NumBrightLights].m_red = red; aBrightLights[NumBrightLights].m_green = green; aBrightLights[NumBrightLights].m_blue = blue; NumBrightLights++; } static float TrafficLightsSide[6] = { -0.09f, 0.09f, 0.162f, 0.09f, -0.09f, -0.162f }; static float TrafficLightsUp[6] = { 0.162f, 0.162f, 0.0f, -0.162f, -0.162f, 0.0f }; static float LongCarHeadLightsSide[8] = { -0.2f, 0.2f, -0.2f, 0.2f, -0.2f, 0.2f, -0.2f, 0.2f }; static float LongCarHeadLightsFront[8] = { 0.1f, 0.1f, -0.1f, -0.1f, 0.1f, 0.1f, -0.1f, -0.1f }; static float LongCarHeadLightsUp[8] = { 0.1f, 0.1f, 0.1f, 0.1f, -0.1f, -0.1f, -0.1f, -0.1f }; static float SmallCarHeadLightsSide[8] = { -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f }; static float SmallCarHeadLightsFront[8] = { 0.08f, 0.08f, -0.08f, -0.08f, 0.08f, 0.08f, -0.08f, -0.08f }; static float SmallCarHeadLightsUp[8] = { 0.08f, 0.08f, 0.08f, 0.08f, -0.08f, -0.08f, -0.08f, -0.08f }; static float BigCarHeadLightsSide[8] = { -0.15f, 0.15f, -0.15f, 0.15f, -0.15f, 0.15f, -0.15f, 0.15f }; static float BigCarHeadLightsFront[8] = { 0.15f, 0.15f, -0.15f, -0.15f, 0.15f, 0.15f, -0.15f, -0.15f }; static float BigCarHeadLightsUp[8] = { 0.15f, 0.15f, 0.15f, 0.15f, -0.15f, -0.15f, -0.15f, -0.15f }; static float TallCarHeadLightsSide[8] = { -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f, -0.08f, 0.08f }; static float TallCarHeadLightsFront[8] = { 0.08f, 0.08f, -0.08f, -0.08f, 0.08f, 0.08f, -0.08f, -0.08f }; static float TallCarHeadLightsUp[8] = { 0.2f, 0.2f, 0.2f, 0.2f, -0.2f, -0.2f, -0.2f, -0.2f }; static float SirenLightsSide[6] = { -0.04f, 0.04f, 0.06f, 0.04f, -0.04f, -0.06f }; static float SirenLightsUp[6] = { 0.06f, 0.06f, 0.0f, -0.06f, -0.06f, 0.0f }; static RwImVertexIndex TrafficLightIndices[4*3] = { 0, 1, 5, 1, 2, 3, 1, 3, 4, 1, 4, 5 }; static RwImVertexIndex CubeIndices[12*3] = { 0, 2, 1, 1, 2, 3, 3, 5, 1, 3, 7, 5, 2, 7, 3, 2, 6, 7, 4, 0, 1, 4, 1, 5, 6, 0, 4, 6, 2, 0, 6, 5, 7, 6, 4, 5 }; void CBrightLights::Render(void) { int i, j; CVector pos; if(NumBrightLights == 0) return; RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; for(i = 0; i < NumBrightLights; i++){ if(TempBufferIndicesStored > TEMPBUFFERINDEXSIZE-40 || TempBufferVerticesStored > TEMPBUFFERVERTSIZE-40) RenderOutGeometryBuffer(); int r, g, b, a; float flicker = (CGeneral::GetRandomNumber()&0xFF) * 0.2f; switch(aBrightLights[i].m_type){ case BRIGHTLIGHT_TRAFFIC_GREEN: r = flicker; g = 255; b = flicker; break; case BRIGHTLIGHT_TRAFFIC_YELLOW: r = 255; g = 128; b = flicker; break; case BRIGHTLIGHT_TRAFFIC_RED: r = 255; g = flicker; b = flicker; break; case BRIGHTLIGHT_FRONT_LONG: case BRIGHTLIGHT_FRONT_SMALL: case BRIGHTLIGHT_FRONT_BIG: case BRIGHTLIGHT_FRONT_TALL: r = 255; g = 255; b = 255; break; case BRIGHTLIGHT_REAR_LONG: case BRIGHTLIGHT_REAR_SMALL: case BRIGHTLIGHT_REAR_BIG: case BRIGHTLIGHT_REAR_TALL: r = 255; g = flicker; b = flicker; break; case BRIGHTLIGHT_SIREN: r = aBrightLights[i].m_red; g = aBrightLights[i].m_green; b = aBrightLights[i].m_blue; break; #ifdef FIX_BUGS //just to make sure that color never will be undefined default: return; #endif } if(aBrightLights[i].m_camDist < BRIGHTLIGHTS_FADE_DIST) a = 255; else a = 255*(1.0f - (aBrightLights[i].m_camDist-BRIGHTLIGHTS_FADE_DIST)/(BRIGHTLIGHTS_MAX_DIST-BRIGHTLIGHTS_FADE_DIST)); // fade car lights down to 31 as they come near if(aBrightLights[i].m_type >= BRIGHTLIGHT_FRONT_LONG && aBrightLights[i].m_type <= BRIGHTLIGHT_REAR_TALL){ if(aBrightLights[i].m_camDist < CARLIGHTS_FADE_DIST) a = 31; else if(aBrightLights[i].m_camDist < CARLIGHTS_MAX_DIST) a = 31 + (255-31)*((aBrightLights[i].m_camDist-CARLIGHTS_FADE_DIST)/(CARLIGHTS_MAX_DIST-CARLIGHTS_FADE_DIST)); } switch(aBrightLights[i].m_type){ case BRIGHTLIGHT_TRAFFIC_GREEN: case BRIGHTLIGHT_TRAFFIC_YELLOW: case BRIGHTLIGHT_TRAFFIC_RED: for(j = 0; j < 6; j++){ pos = TrafficLightsSide[j]*aBrightLights[i].m_side + TrafficLightsUp[j]*aBrightLights[i].m_up + aBrightLights[i].m_pos; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); } for(j = 0; j < 4*3; j++) TempBufferRenderIndexList[TempBufferIndicesStored+j] = TrafficLightIndices[j] + TempBufferVerticesStored; TempBufferVerticesStored += 6; TempBufferIndicesStored += 4*3; break; case BRIGHTLIGHT_FRONT_LONG: case BRIGHTLIGHT_REAR_LONG: for(j = 0; j < 8; j++){ pos = LongCarHeadLightsSide[j]*aBrightLights[i].m_side + LongCarHeadLightsUp[j]*aBrightLights[i].m_up + LongCarHeadLightsFront[j]*aBrightLights[i].m_front + aBrightLights[i].m_pos; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); } for(j = 0; j < 12*3; j++) TempBufferRenderIndexList[TempBufferIndicesStored+j] = CubeIndices[j] + TempBufferVerticesStored; TempBufferVerticesStored += 8; TempBufferIndicesStored += 12*3; break; case BRIGHTLIGHT_FRONT_SMALL: case BRIGHTLIGHT_REAR_SMALL: for(j = 0; j < 8; j++){ pos = SmallCarHeadLightsSide[j]*aBrightLights[i].m_side + SmallCarHeadLightsUp[j]*aBrightLights[i].m_up + SmallCarHeadLightsFront[j]*aBrightLights[i].m_front + aBrightLights[i].m_pos; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); } for(j = 0; j < 12*3; j++) TempBufferRenderIndexList[TempBufferIndicesStored+j] = CubeIndices[j] + TempBufferVerticesStored; TempBufferVerticesStored += 8; TempBufferIndicesStored += 12*3; break; case BRIGHTLIGHT_FRONT_BIG: case BRIGHTLIGHT_REAR_BIG: for (j = 0; j < 8; j++) { pos = BigCarHeadLightsSide[j] * aBrightLights[i].m_side + BigCarHeadLightsUp[j] * aBrightLights[i].m_up + BigCarHeadLightsFront[j] * aBrightLights[i].m_front + aBrightLights[i].m_pos; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + j], r, g, b, a); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + j], pos.x, pos.y, pos.z); } for (j = 0; j < 12 * 3; j++) TempBufferRenderIndexList[TempBufferIndicesStored + j] = CubeIndices[j] + TempBufferVerticesStored; TempBufferVerticesStored += 8; TempBufferIndicesStored += 12 * 3; break; case BRIGHTLIGHT_FRONT_TALL: case BRIGHTLIGHT_REAR_TALL: for(j = 0; j < 8; j++){ pos = TallCarHeadLightsSide[j]*aBrightLights[i].m_side + TallCarHeadLightsUp[j]*aBrightLights[i].m_up + TallCarHeadLightsFront[j]*aBrightLights[i].m_front + aBrightLights[i].m_pos; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); } for(j = 0; j < 12*3; j++) TempBufferRenderIndexList[TempBufferIndicesStored+j] = CubeIndices[j] + TempBufferVerticesStored; TempBufferVerticesStored += 8; TempBufferIndicesStored += 12*3; break; case BRIGHTLIGHT_SIREN: for(j = 0; j < 6; j++){ pos = SirenLightsSide[j] * TheCamera.GetRight() + SirenLightsUp[j] * TheCamera.GetUp() + aBrightLights[i].m_pos; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored+j], r, g, b, a); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored+j], pos.x, pos.y, pos.z); } for(j = 0; j < 4*3; j++) TempBufferRenderIndexList[TempBufferIndicesStored+j] = TrafficLightIndices[j] + TempBufferVerticesStored; TempBufferVerticesStored += 6; TempBufferIndicesStored += 4*3; break; } } RenderOutGeometryBuffer(); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); NumBrightLights = 0; } void CBrightLights::RenderOutGeometryBuffer(void) { if(TempBufferIndicesStored != 0){ LittleTest(); if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; } } int CShinyTexts::NumShinyTexts; CShinyText CShinyTexts::aShinyTexts[NUMSHINYTEXTS]; void CShinyTexts::Init(void) { NumShinyTexts = 0; } void CShinyTexts::RegisterOne(CVector p0, CVector p1, CVector p2, CVector p3, float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3, uint8 type, uint8 red, uint8 green, uint8 blue, float maxDist) { if(NumShinyTexts >= NUMSHINYTEXTS) return; aShinyTexts[NumShinyTexts].m_camDist = (p0 - TheCamera.GetPosition()).Magnitude(); if(aShinyTexts[NumShinyTexts].m_camDist > maxDist) return; aShinyTexts[NumShinyTexts].m_verts[0] = p0; aShinyTexts[NumShinyTexts].m_verts[1] = p1; aShinyTexts[NumShinyTexts].m_verts[2] = p2; aShinyTexts[NumShinyTexts].m_verts[3] = p3; aShinyTexts[NumShinyTexts].m_texCoords[0].x = u0; aShinyTexts[NumShinyTexts].m_texCoords[0].y = v0; aShinyTexts[NumShinyTexts].m_texCoords[1].x = u1; aShinyTexts[NumShinyTexts].m_texCoords[1].y = v1; aShinyTexts[NumShinyTexts].m_texCoords[2].x = u2; aShinyTexts[NumShinyTexts].m_texCoords[2].y = v2; aShinyTexts[NumShinyTexts].m_texCoords[3].x = u3; aShinyTexts[NumShinyTexts].m_texCoords[3].y = v3; aShinyTexts[NumShinyTexts].m_type = type; aShinyTexts[NumShinyTexts].m_red = red; aShinyTexts[NumShinyTexts].m_green = green; aShinyTexts[NumShinyTexts].m_blue = blue; // Fade out at half the max dist float halfDist = maxDist*0.5f; if(aShinyTexts[NumShinyTexts].m_camDist > halfDist){ float f = 1.0f - (aShinyTexts[NumShinyTexts].m_camDist - halfDist)/halfDist; aShinyTexts[NumShinyTexts].m_red *= f; aShinyTexts[NumShinyTexts].m_green *= f; aShinyTexts[NumShinyTexts].m_blue *= f; } NumShinyTexts++; } void CShinyTexts::Render(void) { int i, ix, v; RwTexture *lastTex = nil; if(NumShinyTexts == 0) return; RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; for(i = 0; i < NumShinyTexts; i++){ if(TempBufferIndicesStored > TEMPBUFFERINDEXSIZE-64 || TempBufferVerticesStored > TEMPBUFFERVERTSIZE-62) RenderOutGeometryBuffer(); uint8 r = aShinyTexts[i].m_red; uint8 g = aShinyTexts[i].m_green; uint8 b = aShinyTexts[i].m_blue; switch(aShinyTexts[i].m_type){ case SHINYTEXT_WALK: if(lastTex != gpWalkDontTex){ RenderOutGeometryBuffer(); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpWalkDontTex)); lastTex = gpWalkDontTex; } quad: v = TempBufferVerticesStored; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+0], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_verts[0].x, aShinyTexts[i].m_verts[0].y, aShinyTexts[i].m_verts[0].z); RwIm3DVertexSetU(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_texCoords[0].x); RwIm3DVertexSetV(&TempBufferRenderVertices[v+0], aShinyTexts[i].m_texCoords[0].y); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+1], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_verts[1].x, aShinyTexts[i].m_verts[1].y, aShinyTexts[i].m_verts[1].z); RwIm3DVertexSetU(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_texCoords[1].x); RwIm3DVertexSetV(&TempBufferRenderVertices[v+1], aShinyTexts[i].m_texCoords[1].y); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+2], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_verts[2].x, aShinyTexts[i].m_verts[2].y, aShinyTexts[i].m_verts[2].z); RwIm3DVertexSetU(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_texCoords[2].x); RwIm3DVertexSetV(&TempBufferRenderVertices[v+2], aShinyTexts[i].m_texCoords[2].y); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+3], r, g, b, 255); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_verts[3].x, aShinyTexts[i].m_verts[3].y, aShinyTexts[i].m_verts[3].z); RwIm3DVertexSetU(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_texCoords[3].x); RwIm3DVertexSetV(&TempBufferRenderVertices[v+3], aShinyTexts[i].m_texCoords[3].y); ix = TempBufferIndicesStored; TempBufferRenderIndexList[ix+0] = 0 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+1] = 1 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+2] = 2 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+3] = 2 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+4] = 1 + TempBufferVerticesStored; TempBufferRenderIndexList[ix+5] = 3 + TempBufferVerticesStored; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; break; case SHINYTEXT_FLAT: if(lastTex != nil){ RenderOutGeometryBuffer(); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); lastTex = nil; } goto quad; } } RenderOutGeometryBuffer(); NumShinyTexts = 0; RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); } void CShinyTexts::RenderOutGeometryBuffer(void) { if(TempBufferIndicesStored != 0){ LittleTest(); if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; } } #define MONEY_MESSAGE_LIFETIME_MS 2000 CMoneyMessage CMoneyMessages::aMoneyMessages[NUMMONEYMESSAGES]; void CMoneyMessage::Render() { const float MAX_SCALE = 4.0f; uint32 nLifeTime = CTimer::GetTimeInMilliseconds() - m_nTimeRegistered; if (nLifeTime >= MONEY_MESSAGE_LIFETIME_MS) { m_nTimeRegistered = 0; } else { float fLifeTime = (float)nLifeTime / MONEY_MESSAGE_LIFETIME_MS; RwV3d vecOut; float fDistX, fDistY; if (CSprite::CalcScreenCoors(m_vecPosition + CVector(0.0f, 0.0f, fLifeTime), &vecOut, &fDistX, &fDistY, true)) { fDistX *= (0.7f * fLifeTime + 2.0f) * m_fSize; fDistY *= (0.7f * fLifeTime + 2.0f) * m_fSize; CFont::SetPropOn(); CFont::SetBackgroundOff(); float fScaleY = Min(fDistY / 100.0f, MAX_SCALE); float fScaleX = Min(fDistX / 100.0f, MAX_SCALE); #ifdef FIX_BUGS CFont::SetScale(SCREEN_SCALE_X(fScaleX), SCREEN_SCALE_Y(fScaleY)); #else CFont::SetScale(fScaleX, fScaleY); #endif CFont::SetCentreOn(); CFont::SetCentreSize(SCREEN_WIDTH); CFont::SetJustifyOff(); CFont::SetColor(CRGBA(m_Colour.r, m_Colour.g, m_Colour.b, (255.0f - 255.0f * fLifeTime) * m_fOpacity)); CFont::SetBackGroundOnlyTextOff(); FONT_LOCALE(FONT_STANDARD); CFont::PrintString(vecOut.x, vecOut.y, m_aText); } } } void CMoneyMessages::Init() { for (int32 i = 0; i < NUMMONEYMESSAGES; i++) aMoneyMessages[i].m_nTimeRegistered = 0; } void CMoneyMessages::Render() { for (int32 i = 0; i < NUMMONEYMESSAGES; i++) { if (aMoneyMessages[i].m_nTimeRegistered != 0) aMoneyMessages[i].Render(); } } void CMoneyMessages::RegisterOne(CVector vecPos, const char *pText, uint8 bRed, uint8 bGreen, uint8 bBlue, float fSize, float fOpacity) { uint32 i; #ifdef FIX_BUGS for(i = 0; i < NUMMONEYMESSAGES && aMoneyMessages[i].m_nTimeRegistered != 0; i++); #else for(i = 0; aMoneyMessages[i].m_nTimeRegistered != 0 && i < NUMMONEYMESSAGES; i++); #endif if(i < NUMMONEYMESSAGES) { // Add data of this money message to the array AsciiToUnicode(pText, aMoneyMessages[i].m_aText); aMoneyMessages[i].m_nTimeRegistered = CTimer::GetTimeInMilliseconds(); aMoneyMessages[i].m_vecPosition = vecPos; aMoneyMessages[i].m_Colour.red = bRed; aMoneyMessages[i].m_Colour.green = bGreen; aMoneyMessages[i].m_Colour.blue = bBlue; aMoneyMessages[i].m_fSize = fSize; aMoneyMessages[i].m_fOpacity = fOpacity; } } CRGBA FoamColour(255, 255, 255, 255); uint32 CSpecialParticleStuff::BoatFromStart; void CSpecialParticleStuff::CreateFoamAroundObject(CMatrix* pMatrix, float innerFw, float innerRg, float innerUp, int32 particles) { float outerFw = innerFw + 5.0f; float outerRg = innerRg + 5.0f; float outerUp = innerUp + 5.0f; for (int attempts = 0; particles > 0 && attempts < 1000; attempts++) { CVector pos; int rnd = CGeneral::GetRandomNumber(); pos.x = (int8)(rnd - 128) * innerFw / 110.0f; pos.y = (int8)((rnd >> 8) - 128) * innerFw / 110.0f; pos.z = 0.0f; if (DotProduct2D(pos, TheCamera.GetForward()) >= 0) continue; // was there any point in adding it here? pos += pMatrix->GetPosition(); pos.z = 2.0f; float fw = Abs(DotProduct(pMatrix->GetForward(), pos - pMatrix->GetPosition())); if (fw >= outerFw) continue; float rg = Abs(DotProduct(pMatrix->GetRight(), pos - pMatrix->GetPosition())); if (rg >= outerRg) continue; float up = Abs(DotProduct(pMatrix->GetUp(), pos - pMatrix->GetPosition())); if (up >= outerUp) continue; if (fw > innerFw || rg > innerRg || up > innerUp) { CParticle::AddParticle(PARTICLE_STEAM2, pos, CVector(0.0f, 0.0f, 0.0f), nil, 4.0f, FoamColour, 1, 0, 0, 0); particles--; } } } void CSpecialParticleStuff::StartBoatFoamAnimation() { BoatFromStart = CTimer::GetTimeInMilliseconds(); } void CSpecialParticleStuff::UpdateBoatFoamAnimation(CMatrix* pMatrix) { static int32 FrameInAnimation = 0; static float X, Y, Z, dX, dY, dZ; CreateFoamAroundObject(pMatrix, 107.0f, 24.1f, 30.5f, 2); uint32 prev = CTimer::GetPreviousTimeInMilliseconds(); uint32 cur = CTimer::GetTimeInMilliseconds(); if (FrameInAnimation != 0) { X += dX; Y += dY; Z += dZ; CVector pos = *pMatrix * CVector(X, Y, Z); CParticle::AddParticle(PARTICLE_STEAM_NY, pos, CVector(0.0f, 0.0f, 0.0f), nil, FrameInAnimation * 0.5f + 2.0f, FoamColour, 1, 0, 0, 0); if (++FrameInAnimation > 15) FrameInAnimation = 0; } if ((cur & 0x3FF) < (prev & 0x3FF)) { FrameInAnimation = 1; int rnd = CGeneral::GetRandomNumber(); X = (int8)(rnd - 128) * 0.2f; Y = (int8)((rnd >> 8) - 128) * 0.2f; Z = 10.0f; rnd = CGeneral::GetRandomNumber(); dX = (int8)(rnd - 128) * 0.02f; dY = (int8)((rnd >> 8) - 128) * 0.02f; dZ = 2.0f; } } ================================================ FILE: src/render/SpecialFX.h ================================================ #pragma once //file done class CSpecialFX { public: static bool bVideoCam; static bool bLiftCam; static bool bSnapShotActive; static int32 SnapShotFrames; static void Render(void); static void Update(void); static void Init(void); static void Shutdown(void); static void AddWeaponStreak(int type); static void Render2DFXs(); }; class CRegisteredMotionBlurStreak { public: uintptr m_id; uint8 m_red; uint8 m_green; uint8 m_blue; CVector m_pos1[3]; CVector m_pos2[3]; bool m_isValid[3]; void Update(void); void Render(void); }; class CMotionBlurStreaks { static CRegisteredMotionBlurStreak aStreaks[NUMMBLURSTREAKS]; public: static void Init(void); static void Update(void); static void RegisterStreak(uintptr id, uint8 r, uint8 g, uint8 b, CVector p1, CVector p2); static void Render(void); }; struct CBulletTrace { CVector m_vecStartPos; CVector m_vecEndPos; bool m_bInUse; uint32 m_nCreationTime; uint32 m_nLifeTime; float m_fThickness; uint8 m_fVisibility; void Update(void); }; class CBulletTraces { public: static CBulletTrace aTraces[NUMBULLETTRACES]; static void Init(void); static void Render(void); static void Update(void); static void AddTrace(CVector* start, CVector* end, float thickness, uint32 lifeTime, uint8 visibility); static void AddTrace(CVector* start, CVector* end, int32 weaponType, class CEntity* shooter); }; enum { MARKERTYPE_0 = 0, MARKERTYPE_ARROW, MARKERTYPE_2, MARKERTYPE_3, MARKERTYPE_CYLINDER, NUMMARKERTYPES, MARKERTYPE_INVALID = 0x101 }; class C3dMarker { public: CMatrix m_Matrix; RpAtomic *m_pAtomic; RpMaterial *m_pMaterial; uint16 m_nType; bool m_bIsUsed; bool m_bFindZOnNextPlacement; uint32 m_nIdentifier; RwRGBA m_Color; uint16 m_nPulsePeriod; int16 m_nRotateRate; uint32 m_nStartTime; float m_fPulseFraction; float m_fStdSize; float m_fSize; float m_fBrightness; float m_fCameraRange; bool AddMarker(uint32 identifier, uint16 type, float fSize, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate); void DeleteMarkerObject(); void Render(); }; class C3dMarkers { public: static void Init(); static void Shutdown(); static C3dMarker *PlaceMarker(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate); static void PlaceMarkerSet(uint32 id, uint16 type, CVector &pos, float size, uint8 r, uint8 g, uint8 b, uint8 a, uint16 pulsePeriod, float pulseFraction, int16 rotateRate); static void Render(); static void Update(); static C3dMarker m_aMarkerArray[NUM3DMARKERS]; static int32 NumActiveMarkers; static RpClump* m_pRpClumpArray[NUMMARKERTYPES]; }; enum { BRIGHTLIGHT_INVALID, BRIGHTLIGHT_TRAFFIC_GREEN, BRIGHTLIGHT_TRAFFIC_YELLOW, BRIGHTLIGHT_TRAFFIC_RED, // white BRIGHTLIGHT_FRONT_LONG, BRIGHTLIGHT_FRONT_SMALL, BRIGHTLIGHT_FRONT_BIG, BRIGHTLIGHT_FRONT_TALL, // red BRIGHTLIGHT_REAR_LONG, BRIGHTLIGHT_REAR_SMALL, BRIGHTLIGHT_REAR_BIG, BRIGHTLIGHT_REAR_TALL, BRIGHTLIGHT_SIREN, // unused BRIGHTLIGHT_FRONT = BRIGHTLIGHT_FRONT_LONG, BRIGHTLIGHT_REAR = BRIGHTLIGHT_REAR_LONG, }; class CBrightLight { public: CVector m_pos; CVector m_up; CVector m_side; CVector m_front; float m_camDist; uint8 m_type; uint8 m_red; uint8 m_green; uint8 m_blue; }; class CBrightLights { static int NumBrightLights; static CBrightLight aBrightLights[NUMBRIGHTLIGHTS]; public: static void Init(void); static void RegisterOne(CVector pos, CVector up, CVector side, CVector front, uint8 type, uint8 red = 0, uint8 green = 0, uint8 blue = 0); static void Render(void); static void RenderOutGeometryBuffer(void); }; enum { SHINYTEXT_WALK = 1, SHINYTEXT_FLAT }; class CShinyText { public: CVector m_verts[4]; CVector2D m_texCoords[4]; float m_camDist; uint8 m_type; uint8 m_red; uint8 m_green; uint8 m_blue; }; class CShinyTexts { static int NumShinyTexts; static CShinyText aShinyTexts[NUMSHINYTEXTS]; public: static void Init(void); static void RegisterOne(CVector p0, CVector p1, CVector p2, CVector p3, float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3, uint8 type, uint8 red, uint8 green, uint8 blue, float maxDist); //not used static void Render(void); static void RenderOutGeometryBuffer(void); }; class CMoneyMessage { friend class CMoneyMessages; uint32 m_nTimeRegistered; CVector m_vecPosition; wchar m_aText[16]; CRGBA m_Colour; float m_fSize; float m_fOpacity; public: void Render(); }; class CMoneyMessages { static CMoneyMessage aMoneyMessages[NUMMONEYMESSAGES]; public: static void Init(); static void Render(); static void RegisterOne(CVector vecPos, const char *pText, uint8 bRed, uint8 bGreen, uint8 bBlue, float fSize, float fOpacity); }; class CSpecialParticleStuff { static uint32 BoatFromStart; public: static void CreateFoamAroundObject(CMatrix*, float, float, float, int32); //not used static void StartBoatFoamAnimation(); //not used static void UpdateBoatFoamAnimation(CMatrix*); //not used }; ================================================ FILE: src/render/Sprite.cpp ================================================ #include "common.h" #include "main.h" #include "Draw.h" #include "Camera.h" #include "Sprite.h" float CSprite::m_f2DNearScreenZ; float CSprite::m_f2DFarScreenZ; float CSprite::m_fRecipNearClipPlane; int32 CSprite::m_bFlushSpriteBufferSwitchZTest; float CSprite::CalcHorizonCoors(void) { CVector p = TheCamera.GetPosition() + CVector(TheCamera.CamFrontXNorm, TheCamera.CamFrontYNorm, 0.0f)*3000.0f; p.z = 0.0f; p = TheCamera.m_viewMatrix * p; return p.y * SCREEN_HEIGHT / p.z; } bool CSprite::CalcScreenCoors(const RwV3d &in, RwV3d *out, float *outw, float *outh, bool farclip) { CVector viewvec = TheCamera.m_viewMatrix * in; *out = viewvec; if(out->z <= CDraw::GetNearClipZ() + 1.0f) return false; if(out->z >= CDraw::GetFarClipZ() && farclip) return false; float recip = 1.0f/out->z; out->x *= SCREEN_WIDTH * recip; out->y *= SCREEN_HEIGHT * recip; const float fov = DefaultFOV; // this is used to scale correctly if you zoom in with sniper rifle float fovScale = fov / CDraw::GetFOV(); #ifdef FIX_SPRITES *outw = CDraw::ms_bFixSprites ? (fovScale * recip * SCREEN_HEIGHT) : (fovScale * SCREEN_SCALE_AR(recip) * SCREEN_WIDTH); #else *outw = fovScale * SCREEN_SCALE_AR(recip) * SCREEN_WIDTH; #endif *outh = fovScale * recip * SCREEN_HEIGHT; return true; } #define SPRITEBUFFERSIZE 64 static int32 nSpriteBufferIndex; static RwIm2DVertex SpriteBufferVerts[SPRITEBUFFERSIZE*6]; static RwIm2DVertex verts[4]; void CSprite::InitSpriteBuffer(void) { m_f2DNearScreenZ = RwIm2DGetNearScreenZ(); m_f2DFarScreenZ = RwIm2DGetFarScreenZ(); } void CSprite::InitSpriteBuffer2D(void) { m_fRecipNearClipPlane = 1.0f / RwCameraGetNearClipPlane(Scene.camera); InitSpriteBuffer(); } void CSprite::FlushSpriteBuffer(void) { if(nSpriteBufferIndex > 0){ if(m_bFlushSpriteBufferSwitchZTest){ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwIm2DRenderPrimitive(rwPRIMTYPETRILIST, SpriteBufferVerts, nSpriteBufferIndex*6); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); }else RwIm2DRenderPrimitive(rwPRIMTYPETRILIST, SpriteBufferVerts, nSpriteBufferIndex*6); nSpriteBufferIndex = 0; } } void CSprite::RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a) { static short indices[] = { 0, 1, 2, 3 }; // 0---3 // | | // 1---2 float xs[4]; float ys[4]; float us[4]; float vs[4]; int i; xs[0] = x-w; us[0] = 0.0f; xs[1] = x-w; us[1] = 0.0f; xs[2] = x+w; us[2] = 1.0f; xs[3] = x+w; us[3] = 1.0f; ys[0] = y-h; vs[0] = 0.0f; ys[1] = y+h; vs[1] = 1.0f; ys[2] = y+h; vs[2] = 1.0f; ys[3] = y-h; vs[3] = 0.0f; // clip for(i = 0; i < 4; i++){ if(xs[i] < 0.0f){ us[i] = -xs[i] / (2.0f*w); xs[i] = 0.0f; } if(xs[i] > SCREEN_WIDTH){ us[i] = 1.0f - (xs[i]-SCREEN_WIDTH) / (2.0f*w); xs[i] = SCREEN_WIDTH; } if(ys[i] < 0.0f){ vs[i] = -ys[i] / (2.0f*h); ys[i] = 0.0f; } if(ys[i] > SCREEN_HEIGHT){ vs[i] = 1.0f - (ys[i]-SCREEN_HEIGHT) / (2.0f*h); ys[i] = SCREEN_HEIGHT; } } // (DrawZ - DrawNear)/(DrawFar - DrawNear) = (SpriteZ-SpriteNear)/(SpriteFar-SpriteNear) // So to calculate SpriteZ: float screenz = m_f2DNearScreenZ + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); for(i = 0; i < 4; i++){ RwIm2DVertexSetScreenX(&verts[i], xs[i]); RwIm2DVertexSetScreenY(&verts[i], ys[i]); RwIm2DVertexSetScreenZ(&verts[i], screenz); RwIm2DVertexSetCameraZ(&verts[i], z); RwIm2DVertexSetRecipCameraZ(&verts[i], recipz); RwIm2DVertexSetIntRGBA(&verts[i], r*intens>>8, g*intens>>8, b*intens>>8, a); RwIm2DVertexSetU(&verts[i], us[i], recipz); RwIm2DVertexSetV(&verts[i], vs[i], recipz); } RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, verts, 4); } void CSprite::RenderOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a) { float c = Cos(rotation); float s = Sin(rotation); float xs[4]; float ys[4]; float us[4]; float vs[4]; int i; // Fade out when too near // why not in buffered version? if(z < 2.3f){ if(z < 1.3f) return; int f = (z - 1.3f)/(2.3f-1.3f) * 255; r = f*r >> 8; g = f*g >> 8; b = f*b >> 8; intens = f*intens >> 8; } xs[0] = x + w*(-c-s); us[0] = 0.0f; xs[1] = x + w*(-c+s); us[1] = 0.0f; xs[2] = x + w*(+c+s); us[2] = 1.0f; xs[3] = x + w*(+c-s); us[3] = 1.0f; ys[0] = y + h*(-c+s); vs[0] = 0.0f; ys[1] = y + h*(+c+s); vs[1] = 1.0f; ys[2] = y + h*(+c-s); vs[2] = 1.0f; ys[3] = y + h*(-c-s); vs[3] = 0.0f; // No clipping, just culling if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return; if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return; if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH && xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return; if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT && ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return; float screenz = m_f2DNearScreenZ + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); for(i = 0; i < 4; i++){ RwIm2DVertexSetScreenX(&verts[i], xs[i]); RwIm2DVertexSetScreenY(&verts[i], ys[i]); RwIm2DVertexSetScreenZ(&verts[i], screenz); RwIm2DVertexSetCameraZ(&verts[i], z); RwIm2DVertexSetRecipCameraZ(&verts[i], recipz); RwIm2DVertexSetIntRGBA(&verts[i], r*intens>>8, g*intens>>8, b*intens>>8, a); RwIm2DVertexSetU(&verts[i], us[i], recipz); RwIm2DVertexSetV(&verts[i], vs[i], recipz); } RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, verts, 4); } void CSprite::RenderBufferedOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a) { m_bFlushSpriteBufferSwitchZTest = 0; // 0---3 // | | // 1---2 float xs[4]; float ys[4]; float us[4]; float vs[4]; int i; xs[0] = x-w; us[0] = 0.0f; xs[1] = x-w; us[1] = 0.0f; xs[2] = x+w; us[2] = 1.0f; xs[3] = x+w; us[3] = 1.0f; ys[0] = y-h; vs[0] = 0.0f; ys[1] = y+h; vs[1] = 1.0f; ys[2] = y+h; vs[2] = 1.0f; ys[3] = y-h; vs[3] = 0.0f; // clip for(i = 0; i < 4; i++){ if(xs[i] < 0.0f){ us[i] = -xs[i] / (2.0f*w); xs[i] = 0.0f; } if(xs[i] > SCREEN_WIDTH){ us[i] = 1.0f - (xs[i]-SCREEN_WIDTH) / (2.0f*w); xs[i] = SCREEN_WIDTH; } if(ys[i] < 0.0f){ vs[i] = -ys[i] / (2.0f*h); ys[i] = 0.0f; } if(ys[i] > SCREEN_HEIGHT){ vs[i] = 1.0f - (ys[i]-SCREEN_HEIGHT) / (2.0f*h); ys[i] = SCREEN_HEIGHT; } } float screenz = m_f2DNearScreenZ + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6]; static int indices[6] = { 0, 1, 2, 3, 0, 2 }; for(i = 0; i < 6; i++){ RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]); RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]); RwIm2DVertexSetScreenZ(&vert[i], screenz); RwIm2DVertexSetCameraZ(&vert[i], z); RwIm2DVertexSetRecipCameraZ(&vert[i], recipz); RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a); RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz); RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz); } nSpriteBufferIndex++; if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) FlushSpriteBuffer(); } void CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a) { m_bFlushSpriteBufferSwitchZTest = 0; // TODO: replace with lookup float c = Cos(rotation); float s = Sin(rotation); float xs[4]; float ys[4]; float us[4]; float vs[4]; int i; xs[0] = x - c*w - s*h; us[0] = 0.0f; xs[1] = x - c*w + s*h; us[1] = 0.0f; xs[2] = x + c*w + s*h; us[2] = 1.0f; xs[3] = x + c*w - s*h; us[3] = 1.0f; ys[0] = y - c*h + s*w; vs[0] = 0.0f; ys[1] = y + c*h + s*w; vs[1] = 1.0f; ys[2] = y + c*h - s*w; vs[2] = 1.0f; ys[3] = y - c*h - s*w; vs[3] = 0.0f; // No clipping, just culling if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return; if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return; if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH && xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return; if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT && ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return; float screenz = m_f2DNearScreenZ + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6]; static int indices[6] = { 0, 1, 2, 3, 0, 2 }; for(i = 0; i < 6; i++){ RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]); RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]); RwIm2DVertexSetScreenZ(&vert[i], screenz); RwIm2DVertexSetCameraZ(&vert[i], z); RwIm2DVertexSetRecipCameraZ(&vert[i], recipz); RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a); RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz); RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz); } nSpriteBufferIndex++; if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) FlushSpriteBuffer(); } void CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float rotation, uint8 a) { m_bFlushSpriteBufferSwitchZTest = 0; float c = Cos(rotation); float s = Sin(rotation); float xs[4]; float ys[4]; float us[4]; float vs[4]; int i; xs[0] = x + w*(-c-s); us[0] = 0.0f; xs[1] = x + w*(-c+s); us[1] = 0.0f; xs[2] = x + w*(+c+s); us[2] = 1.0f; xs[3] = x + w*(+c-s); us[3] = 1.0f; ys[0] = y + h*(-c+s); vs[0] = 0.0f; ys[1] = y + h*(+c+s); vs[1] = 1.0f; ys[2] = y + h*(+c-s); vs[2] = 1.0f; ys[3] = y + h*(-c-s); vs[3] = 0.0f; // No clipping, just culling if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return; if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return; if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH && xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return; if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT && ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return; float screenz = m_f2DNearScreenZ + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6]; static int indices[6] = { 0, 1, 2, 3, 0, 2 }; for(i = 0; i < 6; i++){ RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]); RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]); RwIm2DVertexSetScreenZ(&vert[i], screenz); RwIm2DVertexSetCameraZ(&vert[i], z); RwIm2DVertexSetRecipCameraZ(&vert[i], recipz); RwIm2DVertexSetIntRGBA(&vert[i], r*intens>>8, g*intens>>8, b*intens>>8, a); RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz); RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz); } nSpriteBufferIndex++; if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) FlushSpriteBuffer(); } void CSprite::RenderBufferedOneXLUSprite_Rotate_2Colours(float x, float y, float z, float w, float h, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2, float cx, float cy, float recipz, float rotation, uint8 a) { m_bFlushSpriteBufferSwitchZTest = 0; float c = Cos(rotation); float s = Sin(rotation); float xs[4]; float ys[4]; float us[4]; float vs[4]; float cf[4]; int i; xs[0] = x + w*(-c-s); us[0] = 0.0f; xs[1] = x + w*(-c+s); us[1] = 0.0f; xs[2] = x + w*(+c+s); us[2] = 1.0f; xs[3] = x + w*(+c-s); us[3] = 1.0f; ys[0] = y + h*(-c+s); vs[0] = 0.0f; ys[1] = y + h*(+c+s); vs[1] = 1.0f; ys[2] = y + h*(+c-s); vs[2] = 1.0f; ys[3] = y + h*(-c-s); vs[3] = 0.0f; // No clipping, just culling if(xs[0] < 0.0f && xs[1] < 0.0f && xs[2] < 0.0f && xs[3] < 0.0f) return; if(ys[0] < 0.0f && ys[1] < 0.0f && ys[2] < 0.0f && ys[3] < 0.0f) return; if(xs[0] > SCREEN_WIDTH && xs[1] > SCREEN_WIDTH && xs[2] > SCREEN_WIDTH && xs[3] > SCREEN_WIDTH) return; if(ys[0] > SCREEN_HEIGHT && ys[1] > SCREEN_HEIGHT && ys[2] > SCREEN_HEIGHT && ys[3] > SCREEN_HEIGHT) return; // Colour factors, cx/y is the direction in which colours change from rgb1 to rgb2 cf[0] = (cx*(-c-s) + cy*(-c+s))*0.5f + 0.5f; cf[0] = clamp(cf[0], 0.0f, 1.0f); cf[1] = (cx*(-c+s) + cy*( c+s))*0.5f + 0.5f; cf[1] = clamp(cf[1], 0.0f, 1.0f); cf[2] = (cx*( c+s) + cy*( c-s))*0.5f + 0.5f; cf[2] = clamp(cf[2], 0.0f, 1.0f); cf[3] = (cx*( c-s) + cy*(-c-s))*0.5f + 0.5f; cf[3] = clamp(cf[3], 0.0f, 1.0f); float screenz = m_f2DNearScreenZ + (z-CDraw::GetNearClipZ())*(m_f2DFarScreenZ-m_f2DNearScreenZ)*CDraw::GetFarClipZ() / ((CDraw::GetFarClipZ()-CDraw::GetNearClipZ())*z); RwIm2DVertex *vert = &SpriteBufferVerts[nSpriteBufferIndex*6]; static int indices[6] = { 0, 1, 2, 3, 0, 2 }; for(i = 0; i < 6; i++){ RwIm2DVertexSetScreenX(&vert[i], xs[indices[i]]); RwIm2DVertexSetScreenY(&vert[i], ys[indices[i]]); RwIm2DVertexSetScreenZ(&vert[i], screenz); RwIm2DVertexSetCameraZ(&vert[i], z); RwIm2DVertexSetRecipCameraZ(&vert[i], recipz); RwIm2DVertexSetIntRGBA(&vert[i], r1*cf[indices[i]] + r2*(1.0f - cf[indices[i]]), g1*cf[indices[i]] + g2*(1.0f - cf[indices[i]]), b1*cf[indices[i]] + b2*(1.0f - cf[indices[i]]), a); RwIm2DVertexSetU(&vert[i], us[indices[i]], recipz); RwIm2DVertexSetV(&vert[i], vs[indices[i]], recipz); } nSpriteBufferIndex++; if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) FlushSpriteBuffer(); } void CSprite::Set6Vertices2D(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) { float screenz, recipz; float z = RwCameraGetNearClipPlane(Scene.camera); // not done by game screenz = m_f2DNearScreenZ; recipz = m_fRecipNearClipPlane; RwIm2DVertexSetScreenX(&verts[0], r.left); RwIm2DVertexSetScreenY(&verts[0], r.top); RwIm2DVertexSetScreenZ(&verts[0], screenz); RwIm2DVertexSetCameraZ(&verts[0], z); RwIm2DVertexSetRecipCameraZ(&verts[0], recipz); RwIm2DVertexSetIntRGBA(&verts[0], c2.r, c2.g, c2.b, c2.a); RwIm2DVertexSetU(&verts[0], 0.0f, recipz); RwIm2DVertexSetV(&verts[0], 0.0f, recipz); RwIm2DVertexSetScreenX(&verts[1], r.right); RwIm2DVertexSetScreenY(&verts[1], r.top); RwIm2DVertexSetScreenZ(&verts[1], screenz); RwIm2DVertexSetCameraZ(&verts[1], z); RwIm2DVertexSetRecipCameraZ(&verts[1], recipz); RwIm2DVertexSetIntRGBA(&verts[1], c3.r, c3.g, c3.b, c3.a); RwIm2DVertexSetU(&verts[1], 1.0f, recipz); RwIm2DVertexSetV(&verts[1], 0.0f, recipz); RwIm2DVertexSetScreenX(&verts[2], r.right); RwIm2DVertexSetScreenY(&verts[2], r.bottom); RwIm2DVertexSetScreenZ(&verts[2], screenz); RwIm2DVertexSetCameraZ(&verts[2], z); RwIm2DVertexSetRecipCameraZ(&verts[2], recipz); RwIm2DVertexSetIntRGBA(&verts[2], c1.r, c1.g, c1.b, c1.a); RwIm2DVertexSetU(&verts[2], 1.0f, recipz); RwIm2DVertexSetV(&verts[2], 1.0f, recipz); RwIm2DVertexSetScreenX(&verts[3], r.left); RwIm2DVertexSetScreenY(&verts[3], r.bottom); RwIm2DVertexSetScreenZ(&verts[3], screenz); RwIm2DVertexSetCameraZ(&verts[3], z); RwIm2DVertexSetRecipCameraZ(&verts[3], recipz); RwIm2DVertexSetIntRGBA(&verts[3], c0.r, c0.g, c0.b, c0.a); RwIm2DVertexSetU(&verts[3], 0.0f, recipz); RwIm2DVertexSetV(&verts[3], 1.0f, recipz); RwIm2DVertexSetScreenX(&verts[4], r.left); RwIm2DVertexSetScreenY(&verts[4], r.top); RwIm2DVertexSetScreenZ(&verts[4], screenz); RwIm2DVertexSetCameraZ(&verts[4], z); RwIm2DVertexSetRecipCameraZ(&verts[4], recipz); RwIm2DVertexSetIntRGBA(&verts[4], c2.r, c2.g, c2.b, c2.a); RwIm2DVertexSetU(&verts[4], 0.0f, recipz); RwIm2DVertexSetV(&verts[4], 0.0f, recipz); RwIm2DVertexSetScreenX(&verts[5], r.right); RwIm2DVertexSetScreenY(&verts[5], r.bottom); RwIm2DVertexSetScreenZ(&verts[5], screenz); RwIm2DVertexSetCameraZ(&verts[5], z); RwIm2DVertexSetRecipCameraZ(&verts[5], recipz); RwIm2DVertexSetIntRGBA(&verts[5], c1.r, c1.g, c1.b, c1.a); RwIm2DVertexSetU(&verts[5], 1.0f, recipz); RwIm2DVertexSetV(&verts[5], 1.0f, recipz); } void CSprite::Set6Vertices2D(RwIm2DVertex *verts, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) { float screenz, recipz; float z = RwCameraGetNearClipPlane(Scene.camera); // not done by game screenz = m_f2DNearScreenZ; recipz = m_fRecipNearClipPlane; RwIm2DVertexSetScreenX(&verts[0], x3); RwIm2DVertexSetScreenY(&verts[0], y3); RwIm2DVertexSetScreenZ(&verts[0], screenz); RwIm2DVertexSetCameraZ(&verts[0], z); RwIm2DVertexSetRecipCameraZ(&verts[0], recipz); RwIm2DVertexSetIntRGBA(&verts[0], c2.r, c2.g, c2.b, c2.a); RwIm2DVertexSetU(&verts[0], 0.0f, recipz); RwIm2DVertexSetV(&verts[0], 0.0f, recipz); RwIm2DVertexSetScreenX(&verts[1], x4); RwIm2DVertexSetScreenY(&verts[1], y4); RwIm2DVertexSetScreenZ(&verts[1], screenz); RwIm2DVertexSetCameraZ(&verts[1], z); RwIm2DVertexSetRecipCameraZ(&verts[1], recipz); RwIm2DVertexSetIntRGBA(&verts[1], c3.r, c3.g, c3.b, c3.a); RwIm2DVertexSetU(&verts[1], 1.0f, recipz); RwIm2DVertexSetV(&verts[1], 0.0f, recipz); RwIm2DVertexSetScreenX(&verts[2], x2); RwIm2DVertexSetScreenY(&verts[2], y2); RwIm2DVertexSetScreenZ(&verts[2], screenz); RwIm2DVertexSetCameraZ(&verts[2], z); RwIm2DVertexSetRecipCameraZ(&verts[2], recipz); RwIm2DVertexSetIntRGBA(&verts[2], c1.r, c1.g, c1.b, c1.a); RwIm2DVertexSetU(&verts[2], 1.0f, recipz); RwIm2DVertexSetV(&verts[2], 1.0f, recipz); RwIm2DVertexSetScreenX(&verts[3], x1); RwIm2DVertexSetScreenY(&verts[3], y1); RwIm2DVertexSetScreenZ(&verts[3], screenz); RwIm2DVertexSetCameraZ(&verts[3], z); RwIm2DVertexSetRecipCameraZ(&verts[3], recipz); RwIm2DVertexSetIntRGBA(&verts[3], c0.r, c0.g, c0.b, c0.a); RwIm2DVertexSetU(&verts[3], 0.0f, recipz); RwIm2DVertexSetV(&verts[3], 1.0f, recipz); RwIm2DVertexSetScreenX(&verts[4], x3); RwIm2DVertexSetScreenY(&verts[4], y3); RwIm2DVertexSetScreenZ(&verts[4], screenz); RwIm2DVertexSetCameraZ(&verts[4], z); RwIm2DVertexSetRecipCameraZ(&verts[4], recipz); RwIm2DVertexSetIntRGBA(&verts[4], c2.r, c2.g, c2.b, c2.a); RwIm2DVertexSetU(&verts[4], 0.0f, recipz); RwIm2DVertexSetV(&verts[4], 0.0f, recipz); RwIm2DVertexSetScreenX(&verts[5], x2); RwIm2DVertexSetScreenY(&verts[5], y2); RwIm2DVertexSetScreenZ(&verts[5], screenz); RwIm2DVertexSetCameraZ(&verts[5], z); RwIm2DVertexSetRecipCameraZ(&verts[5], recipz); RwIm2DVertexSetIntRGBA(&verts[5], c1.r, c1.g, c1.b, c1.a); RwIm2DVertexSetU(&verts[5], 1.0f, recipz); RwIm2DVertexSetV(&verts[5], 1.0f, recipz); } void CSprite::RenderBufferedOneXLUSprite2D(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, uint8 alpha) { m_bFlushSpriteBufferSwitchZTest = 1; CRGBA col(intens * colour.red >> 8, intens * colour.green >> 8, intens * colour.blue >> 8, alpha); CRect rect(x - w, y - h, x + h, y + h); Set6Vertices2D(&SpriteBufferVerts[6 * nSpriteBufferIndex], rect, col, col, col, col); nSpriteBufferIndex++; if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) FlushSpriteBuffer(); } void CSprite::RenderBufferedOneXLUSprite2D_Rotate_Dimension(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, float rotation, uint8 alpha) { m_bFlushSpriteBufferSwitchZTest = 1; CRGBA col(intens * colour.red >> 8, intens * colour.green >> 8, intens * colour.blue >> 8, alpha); float c = Cos(rotation); float s = Sin(rotation); Set6Vertices2D(&SpriteBufferVerts[6 * nSpriteBufferIndex], x + c*w - s*h, y - c*h - s*w, x + c*w + s*h, y + c*h - s*w, x - c*w - s*h, y - c*h + s*w, x - c*w + s*h, y + c*h + s*w, col, col, col, col); nSpriteBufferIndex++; if(nSpriteBufferIndex >= SPRITEBUFFERSIZE) FlushSpriteBuffer(); } ================================================ FILE: src/render/Sprite.h ================================================ #pragma once class CSprite { static float m_f2DNearScreenZ; static float m_f2DFarScreenZ; static float m_fRecipNearClipPlane; static int32 m_bFlushSpriteBufferSwitchZTest; public: static float GetNearScreenZ(void) { return m_f2DNearScreenZ; } static float GetFarScreenZ(void) { return m_f2DFarScreenZ; } static float CalcHorizonCoors(void); static bool CalcScreenCoors(const RwV3d &in, RwV3d *out, float *outw, float *outh, bool farclip); static void InitSpriteBuffer(void); static void InitSpriteBuffer2D(void); static void FlushSpriteBuffer(void); static void RenderOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a); static void RenderOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a); static void RenderBufferedOneXLUSprite(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, uint8 a); static void RenderBufferedOneXLUSprite_Rotate_Dimension(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a); static void RenderBufferedOneXLUSprite_Rotate_Aspect(float x, float y, float z, float w, float h, uint8 r, uint8 g, uint8 b, int16 intens, float recipz, float roll, uint8 a); // cx/y is the direction in which the colour changes static void RenderBufferedOneXLUSprite_Rotate_2Colours(float x, float y, float z, float w, float h, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2, float cx, float cy, float recipz, float rotation, uint8 a); static void Set6Vertices2D(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); static void Set6Vertices2D(RwIm2DVertex *verts, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); static void RenderBufferedOneXLUSprite2D(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, uint8 alpha); static void RenderBufferedOneXLUSprite2D_Rotate_Dimension(float x, float y, float w, float h, const RwRGBA &colour, int16 intens, float rotation, uint8 alpha); }; ================================================ FILE: src/render/Sprite2d.cpp ================================================ #include "common.h" #include "main.h" #include "Draw.h" #include "Camera.h" #include "Sprite2d.h" #include "Font.h" #include "RenderBuffer.h" float CSprite2d::RecipNearClip; float CSprite2d::NearScreenZ; float CSprite2d::NearCamZ; int CSprite2d::nextBufferVertex; int CSprite2d::nextBufferIndex; RwIm2DVertex CSprite2d::maVertices[8]; void CSprite2d::SetRecipNearClip(void) { // Used but empty in VC, instead they set in InitPerFrame. Isn't that great? } void CSprite2d::InitPerFrame(void) { nextBufferVertex = 0; nextBufferIndex = 0; RecipNearClip = 1.0f / RwCameraGetNearClipPlane(Scene.camera); NearScreenZ = RwIm2DGetNearScreenZ(); // not original but you're supposed to set camera z too // wrapping all this in FIX_BUGS is too ugly NearCamZ = RwCameraGetNearClipPlane(Scene.camera); } void CSprite2d::Delete(void) { if(m_pTexture){ RwTextureDestroy(m_pTexture); m_pTexture = nil; } } void CSprite2d::SetTexture(const char *name) { Delete(); if(name) m_pTexture = RwTextureRead(name, nil); } void CSprite2d::SetTexture(const char *name, const char *mask) { Delete(); if(name) m_pTexture = RwTextureRead(name, mask); } void CSprite2d::SetAddressing(RwTextureAddressMode addr) { if(m_pTexture) RwTextureSetAddressing(m_pTexture, addr); } void CSprite2d::SetRenderState(void) { if(m_pTexture) RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(m_pTexture)); else RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); } void CSprite2d::Draw(float x, float y, float w, float h, const CRGBA &col) { SetVertices(CRect(x, y, x + w, y + h), col, col, col, col); SetRenderState(); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); } void CSprite2d::Draw(const CRect &rect, const CRGBA &col) { SetVertices(rect, col, col, col, col); SetRenderState(); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); } void CSprite2d::Draw(const CRect &rect, const CRGBA &col, float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2) { SetVertices(rect, col, col, col, col, u0, v0, u1, v1, u3, v3, u2, v2); SetRenderState(); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); } void CSprite2d::Draw(const CRect &rect, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) { SetVertices(rect, c0, c1, c2, c3); SetRenderState(); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); } void CSprite2d::Draw(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &col) { SetVertices(x1, y1, x2, y2, x3, y3, x4, y4, col, col, col, col); SetRenderState(); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); } // Arguments: // 2---3 // | | // 0---1 void CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) { float offset = 1.0f/1024.0f; // This is what we draw: // 0---1 // | / | // 3---2 RwIm2DVertexSetScreenX(&maVertices[0], r.left); RwIm2DVertexSetScreenY(&maVertices[0], r.top); RwIm2DVertexSetScreenZ(&maVertices[0], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[0], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[0], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a); RwIm2DVertexSetU(&maVertices[0], 0.0f+offset, RecipNearClip); RwIm2DVertexSetV(&maVertices[0], 0.0f+offset, RecipNearClip); RwIm2DVertexSetScreenX(&maVertices[1], r.right); RwIm2DVertexSetScreenY(&maVertices[1], r.top); RwIm2DVertexSetScreenZ(&maVertices[1], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[1], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[1], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a); RwIm2DVertexSetU(&maVertices[1], 1.0f+offset, RecipNearClip); RwIm2DVertexSetV(&maVertices[1], 0.0f+offset, RecipNearClip); RwIm2DVertexSetScreenX(&maVertices[2], r.right); RwIm2DVertexSetScreenY(&maVertices[2], r.bottom); RwIm2DVertexSetScreenZ(&maVertices[2], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[2], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[2], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a); RwIm2DVertexSetU(&maVertices[2], 1.0f+offset, RecipNearClip); RwIm2DVertexSetV(&maVertices[2], 1.0f+offset, RecipNearClip); RwIm2DVertexSetScreenX(&maVertices[3], r.left); RwIm2DVertexSetScreenY(&maVertices[3], r.bottom); RwIm2DVertexSetScreenZ(&maVertices[3], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[3], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[3], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a); RwIm2DVertexSetU(&maVertices[3], 0.0f+offset, RecipNearClip); RwIm2DVertexSetV(&maVertices[3], 1.0f+offset, RecipNearClip); } void CSprite2d::SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2) { // This is what we draw: // 0---1 // | / | // 3---2 RwIm2DVertexSetScreenX(&maVertices[0], r.left); RwIm2DVertexSetScreenY(&maVertices[0], r.top); RwIm2DVertexSetScreenZ(&maVertices[0], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[0], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[0], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a); RwIm2DVertexSetU(&maVertices[0], u0, RecipNearClip); RwIm2DVertexSetV(&maVertices[0], v0, RecipNearClip); RwIm2DVertexSetScreenX(&maVertices[1], r.right); RwIm2DVertexSetScreenY(&maVertices[1], r.top); RwIm2DVertexSetScreenZ(&maVertices[1], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[1], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[1], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a); RwIm2DVertexSetU(&maVertices[1], u1, RecipNearClip); RwIm2DVertexSetV(&maVertices[1], v1, RecipNearClip); RwIm2DVertexSetScreenX(&maVertices[2], r.right); RwIm2DVertexSetScreenY(&maVertices[2], r.bottom); RwIm2DVertexSetScreenZ(&maVertices[2], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[2], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[2], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a); RwIm2DVertexSetU(&maVertices[2], u2, RecipNearClip); RwIm2DVertexSetV(&maVertices[2], v2, RecipNearClip); RwIm2DVertexSetScreenX(&maVertices[3], r.left); RwIm2DVertexSetScreenY(&maVertices[3], r.bottom); RwIm2DVertexSetScreenZ(&maVertices[3], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[3], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[3], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a); RwIm2DVertexSetU(&maVertices[3], u3, RecipNearClip); RwIm2DVertexSetV(&maVertices[3], v3, RecipNearClip); } void CSprite2d::SetVertices(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) { RwIm2DVertexSetScreenX(&maVertices[0], x3); RwIm2DVertexSetScreenY(&maVertices[0], y3); RwIm2DVertexSetScreenZ(&maVertices[0], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[0], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[0], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[0], c2.r, c2.g, c2.b, c2.a); RwIm2DVertexSetU(&maVertices[0], 0.0f, RecipNearClip); RwIm2DVertexSetV(&maVertices[0], 0.0f, RecipNearClip); RwIm2DVertexSetScreenX(&maVertices[1], x4); RwIm2DVertexSetScreenY(&maVertices[1], y4); RwIm2DVertexSetScreenZ(&maVertices[1], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[1], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[1], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[1], c3.r, c3.g, c3.b, c3.a); RwIm2DVertexSetU(&maVertices[1], 1.0f, RecipNearClip); RwIm2DVertexSetV(&maVertices[1], 0.0f, RecipNearClip); RwIm2DVertexSetScreenX(&maVertices[2], x2); RwIm2DVertexSetScreenY(&maVertices[2], y2); RwIm2DVertexSetScreenZ(&maVertices[2], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[2], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[2], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[2], c1.r, c1.g, c1.b, c1.a); RwIm2DVertexSetU(&maVertices[2], 1.0f, RecipNearClip); RwIm2DVertexSetV(&maVertices[2], 1.0f, RecipNearClip); RwIm2DVertexSetScreenX(&maVertices[3], x1); RwIm2DVertexSetScreenY(&maVertices[3], y1); RwIm2DVertexSetScreenZ(&maVertices[3], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[3], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[3], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[3], c0.r, c0.g, c0.b, c0.a); RwIm2DVertexSetU(&maVertices[3], 0.0f, RecipNearClip); RwIm2DVertexSetV(&maVertices[3], 1.0f, RecipNearClip); } void CSprite2d::SetVertices(int n, float *positions, float *uvs, const CRGBA &col) { int i; for(i = 0; i < n; i++){ RwIm2DVertexSetScreenX(&maVertices[i], positions[i*2 + 0]); RwIm2DVertexSetScreenY(&maVertices[i], positions[i*2 + 1]); RwIm2DVertexSetScreenZ(&maVertices[i], NearScreenZ + 0.0001f); RwIm2DVertexSetCameraZ(&maVertices[i], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[i], RecipNearClip); RwIm2DVertexSetIntRGBA(&maVertices[i], col.r, col.g, col.b, col.a); RwIm2DVertexSetU(&maVertices[i], uvs[i*2 + 0], RecipNearClip); RwIm2DVertexSetV(&maVertices[i], uvs[i*2 + 1], RecipNearClip); } } void CSprite2d::SetMaskVertices(int n, float *positions) { int i; for(i = 0; i < n; i++){ RwIm2DVertexSetScreenX(&maVertices[i], positions[i*2 + 0]); RwIm2DVertexSetScreenY(&maVertices[i], positions[i*2 + 1]); RwIm2DVertexSetScreenZ(&maVertices[i], NearScreenZ); RwIm2DVertexSetCameraZ(&maVertices[i], NearCamZ); RwIm2DVertexSetRecipCameraZ(&maVertices[i], RecipNearClip); #if !defined(GTA_PS2_STUFF) && defined(RWLIBS) RwIm2DVertexSetIntRGBA(&maVertices[i], 0, 0, 0, 0); #else RwIm2DVertexSetIntRGBA(&maVertices[i], 255, 255, 255, 255); #endif } } void CSprite2d::SetVertices(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2) { RwIm2DVertexSetScreenX(&verts[0], r.left); RwIm2DVertexSetScreenY(&verts[0], r.top); RwIm2DVertexSetScreenZ(&verts[0], NearScreenZ); RwIm2DVertexSetCameraZ(&verts[0], NearCamZ); RwIm2DVertexSetRecipCameraZ(&verts[0], RecipNearClip); RwIm2DVertexSetIntRGBA(&verts[0], c2.r, c2.g, c2.b, c2.a); RwIm2DVertexSetU(&verts[0], u0, RecipNearClip); RwIm2DVertexSetV(&verts[0], v0, RecipNearClip); RwIm2DVertexSetScreenX(&verts[1], r.right); RwIm2DVertexSetScreenY(&verts[1], r.top); RwIm2DVertexSetScreenZ(&verts[1], NearScreenZ); RwIm2DVertexSetCameraZ(&verts[1], NearCamZ); RwIm2DVertexSetRecipCameraZ(&verts[1], RecipNearClip); RwIm2DVertexSetIntRGBA(&verts[1], c3.r, c3.g, c3.b, c3.a); RwIm2DVertexSetU(&verts[1], u1, RecipNearClip); RwIm2DVertexSetV(&verts[1], v1, RecipNearClip); RwIm2DVertexSetScreenX(&verts[2], r.right); RwIm2DVertexSetScreenY(&verts[2], r.bottom); RwIm2DVertexSetScreenZ(&verts[2], NearScreenZ); RwIm2DVertexSetCameraZ(&verts[2], NearCamZ); RwIm2DVertexSetRecipCameraZ(&verts[2], RecipNearClip); RwIm2DVertexSetIntRGBA(&verts[2], c1.r, c1.g, c1.b, c1.a); RwIm2DVertexSetU(&verts[2], u2, RecipNearClip); RwIm2DVertexSetV(&verts[2], v2, RecipNearClip); RwIm2DVertexSetScreenX(&verts[3], r.left); RwIm2DVertexSetScreenY(&verts[3], r.bottom); RwIm2DVertexSetScreenZ(&verts[3], NearScreenZ); RwIm2DVertexSetCameraZ(&verts[3], NearCamZ); RwIm2DVertexSetRecipCameraZ(&verts[3], RecipNearClip); RwIm2DVertexSetIntRGBA(&verts[3], c0.r, c0.g, c0.b, c0.a); RwIm2DVertexSetU(&verts[3], u3, RecipNearClip); RwIm2DVertexSetV(&verts[3], v3, RecipNearClip); } void CSprite2d::DrawRect(const CRect &r, const CRGBA &col) { SetVertices(r, col, col, col, col); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(col.a != 255)); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, maVertices, 4); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); } void CSprite2d::DrawRect(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) { SetVertices(r, c0, c1, c2, c3); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, maVertices, 4); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); } void CSprite2d::DrawRectXLU(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) { SetVertices(r, c0, c1, c2, c3); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); } void CSprite2d::DrawAnyRect(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3) { SetVertices(x1, y1, x2, y2, x3, y3, x4, y4, c0, c1, c2, c3); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(c0.alpha != 255 || c1.alpha != 255 || c2.alpha != 255 || c3.alpha != 255)); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); } void CSprite2d::Draw2DPolygon(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &color) { SetVertices(x1, y1, x2, y2, x3, y3, x4, y4, color, color, color, color); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, 0); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(color.a != 255)); RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::maVertices, 4); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); } void CSprite2d::AddToBuffer(const CRect &r, const CRGBA &c, float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2) { SetVertices(&TempVertexBuffer.im2d[nextBufferVertex], r, c, c, c, c, u0, v0, u1, v1, u3, v3, u2, v2); RwImVertexIndex *pIndexList = &TempBufferRenderIndexList[nextBufferIndex]; pIndexList[0] = nextBufferVertex; pIndexList[1] = nextBufferVertex + 1; pIndexList[2] = nextBufferVertex + 2; pIndexList[3] = nextBufferVertex + 3; pIndexList[4] = nextBufferVertex; pIndexList[5] = nextBufferVertex + 2; nextBufferIndex += 6; nextBufferVertex += 4; if (IsVertexBufferFull()) RenderVertexBuffer(); } bool CSprite2d::IsVertexBufferFull() { return (nextBufferVertex > TEMPBUFFERVERTSIZE-128-4 || nextBufferIndex > ARRAY_SIZE(TempBufferRenderIndexList)-6); } void CSprite2d::RenderVertexBuffer() { if (nextBufferVertex > 0) { RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempVertexBuffer.im2d, nextBufferVertex, TempBufferRenderIndexList, nextBufferIndex); nextBufferVertex = 0; nextBufferIndex = 0; } } ================================================ FILE: src/render/Sprite2d.h ================================================ #pragma once class CSprite2d { static float RecipNearClip; static float NearScreenZ; static float NearCamZ; // not original static int nextBufferVertex; static int nextBufferIndex; static RwIm2DVertex maVertices[8]; public: RwTexture *m_pTexture; static void SetRecipNearClip(void); static void InitPerFrame(void); CSprite2d(void) : m_pTexture(nil) {}; ~CSprite2d(void) { Delete(); }; void Delete(void); void SetRenderState(void); void SetTexture(const char *name); void SetTexture(const char *name, const char *mask); void SetAddressing(RwTextureAddressMode addr); void Draw(float x, float y, float w, float h, const CRGBA &col); void Draw(const CRect &rect, const CRGBA &col); void Draw(const CRect &rect, const CRGBA &col, float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2); void Draw(const CRect &rect, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); void Draw(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &col); static void SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); static void SetVertices(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2); static void SetVertices(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); static void SetVertices(int n, float *positions, float *uvs, const CRGBA &col); static void SetMaskVertices(int n, float *positions); static void SetVertices(RwIm2DVertex *verts, const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3, float u0, float v0, float u1, float v1, float u3, float v3, float u2, float v2); static void DrawRect(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); static void DrawRect(const CRect &r, const CRGBA &col); static void DrawRectXLU(const CRect &r, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); static void DrawAnyRect(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &c0, const CRGBA &c1, const CRGBA &c2, const CRGBA &c3); static void Draw2DPolygon(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA &color); static RwIm2DVertex* GetVertices() { return maVertices; }; static bool IsVertexBufferFull(); static void AddToBuffer(const CRect &a1, const CRGBA &a2, float a3, float a4, float a5, float a6, float a7, float a8, float a9, float a10); static void RenderVertexBuffer(); }; ================================================ FILE: src/render/TexList.cpp ================================================ #include "common.h" #include "TexList.h" #include "rtbmp.h" #include "FileMgr.h" bool CTexList::ms_nTexUsed[MAX_TEXUSED]; void CTexList::Initialise() {} void CTexList::Shutdown() {} RwTexture * CTexList::SetTexture(int32 slot, char *name) { return nil; } int32 CTexList::GetFirstFreeTexture() { for (int32 i = 0; i < MAX_TEXUSED; i++) if (!ms_nTexUsed[i]) return i; return -1; } RwTexture * CTexList::LoadFileNameTexture(char *name) { return SetTexture(GetFirstFreeTexture(), name); } void CTexList::LoadGlobalTextureList() { CFileMgr::SetDir("TEXTURES"); } ================================================ FILE: src/render/TexList.h ================================================ #pragma once class CTexList { enum { MAX_TEXUSED = 400, }; static bool ms_nTexUsed[MAX_TEXUSED]; public: static void Initialise(); static void Shutdown(); static RwTexture *SetTexture(int32 slot, char *name); static int32 GetFirstFreeTexture(); static RwTexture *LoadFileNameTexture(char *name); static void LoadGlobalTextureList(); }; ================================================ FILE: src/render/Timecycle.cpp ================================================ #include "common.h" #include "main.h" #include "Clock.h" #include "Weather.h" #include "Camera.h" #include "Shadows.h" #include "ZoneCull.h" #include "CutsceneMgr.h" #include "FileMgr.h" #include "Timecycle.h" uint8 CTimeCycle::m_nAmbientRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientRed_Obj[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientGreen_Obj[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientBlue_Obj[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientRed_Bl[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientGreen_Bl[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientBlue_Bl[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientRed_Obj_Bl[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientGreen_Obj_Bl[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nAmbientBlue_Obj_Bl[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nDirectionalRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nDirectionalGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nDirectionalBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSkyTopRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSkyTopGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSkyTopBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSkyBottomRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSkyBottomGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSkyBottomBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSunCoreRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSunCoreGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSunCoreBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSunCoronaRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSunCoronaGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nSunCoronaBlue[NUMHOURS][NUMWEATHERS]; int8 CTimeCycle::m_fSunSize[NUMHOURS][NUMWEATHERS]; int8 CTimeCycle::m_fSpriteSize[NUMHOURS][NUMWEATHERS]; int8 CTimeCycle::m_fSpriteBrightness[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nShadowStrength[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nLightShadowStrength[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nPoleShadowStrength[NUMHOURS][NUMWEATHERS]; int16 CTimeCycle::m_fFogStart[NUMHOURS][NUMWEATHERS]; int16 CTimeCycle::m_fFarClip[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_fLightsOnGroundBrightness[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nLowCloudsRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nLowCloudsGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nLowCloudsBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nFluffyCloudsTopRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nFluffyCloudsTopGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nFluffyCloudsTopBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nFluffyCloudsBottomRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nFluffyCloudsBottomGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_nFluffyCloudsBottomBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_fBlurRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_fBlurGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_fBlurBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_fWaterRed[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_fWaterGreen[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_fWaterBlue[NUMHOURS][NUMWEATHERS]; uint8 CTimeCycle::m_fWaterAlpha[NUMHOURS][NUMWEATHERS]; float CTimeCycle::m_fCurrentAmbientRed; float CTimeCycle::m_fCurrentAmbientGreen; float CTimeCycle::m_fCurrentAmbientBlue; float CTimeCycle::m_fCurrentAmbientRed_Obj; float CTimeCycle::m_fCurrentAmbientGreen_Obj; float CTimeCycle::m_fCurrentAmbientBlue_Obj; float CTimeCycle::m_fCurrentAmbientRed_Bl; float CTimeCycle::m_fCurrentAmbientGreen_Bl; float CTimeCycle::m_fCurrentAmbientBlue_Bl; float CTimeCycle::m_fCurrentAmbientRed_Obj_Bl; float CTimeCycle::m_fCurrentAmbientGreen_Obj_Bl; float CTimeCycle::m_fCurrentAmbientBlue_Obj_Bl; float CTimeCycle::m_fCurrentDirectionalRed; float CTimeCycle::m_fCurrentDirectionalGreen; float CTimeCycle::m_fCurrentDirectionalBlue; int32 CTimeCycle::m_nCurrentSkyTopRed; int32 CTimeCycle::m_nCurrentSkyTopGreen; int32 CTimeCycle::m_nCurrentSkyTopBlue; int32 CTimeCycle::m_nCurrentSkyBottomRed; int32 CTimeCycle::m_nCurrentSkyBottomGreen; int32 CTimeCycle::m_nCurrentSkyBottomBlue; int32 CTimeCycle::m_nCurrentSunCoreRed; int32 CTimeCycle::m_nCurrentSunCoreGreen; int32 CTimeCycle::m_nCurrentSunCoreBlue; int32 CTimeCycle::m_nCurrentSunCoronaRed; int32 CTimeCycle::m_nCurrentSunCoronaGreen; int32 CTimeCycle::m_nCurrentSunCoronaBlue; float CTimeCycle::m_fCurrentSunSize; float CTimeCycle::m_fCurrentSpriteSize; float CTimeCycle::m_fCurrentSpriteBrightness; int32 CTimeCycle::m_nCurrentShadowStrength; int32 CTimeCycle::m_nCurrentLightShadowStrength; int32 CTimeCycle::m_nCurrentPoleShadowStrength; float CTimeCycle::m_fCurrentFogStart; float CTimeCycle::m_fCurrentFarClip; float CTimeCycle::m_fCurrentLightsOnGroundBrightness; int32 CTimeCycle::m_nCurrentLowCloudsRed; int32 CTimeCycle::m_nCurrentLowCloudsGreen; int32 CTimeCycle::m_nCurrentLowCloudsBlue; int32 CTimeCycle::m_nCurrentFluffyCloudsTopRed; int32 CTimeCycle::m_nCurrentFluffyCloudsTopGreen; int32 CTimeCycle::m_nCurrentFluffyCloudsTopBlue; int32 CTimeCycle::m_nCurrentFluffyCloudsBottomRed; int32 CTimeCycle::m_nCurrentFluffyCloudsBottomGreen; int32 CTimeCycle::m_nCurrentFluffyCloudsBottomBlue; float CTimeCycle::m_fCurrentBlurRed; float CTimeCycle::m_fCurrentBlurGreen; float CTimeCycle::m_fCurrentBlurBlue; float CTimeCycle::m_fCurrentWaterRed; float CTimeCycle::m_fCurrentWaterGreen; float CTimeCycle::m_fCurrentWaterBlue; float CTimeCycle::m_fCurrentWaterAlpha; int32 CTimeCycle::m_nCurrentFogColourRed; int32 CTimeCycle::m_nCurrentFogColourGreen; int32 CTimeCycle::m_nCurrentFogColourBlue; int32 CTimeCycle::m_FogReduction; int32 CTimeCycle::m_bExtraColourOn; int32 CTimeCycle::m_ExtraColour; float CTimeCycle::m_ExtraColourInter; int32 CTimeCycle::m_CurrentStoredValue; CVector CTimeCycle::m_VectorToSun[16]; float CTimeCycle::m_fShadowFrontX[16]; float CTimeCycle::m_fShadowFrontY[16]; float CTimeCycle::m_fShadowSideX[16]; float CTimeCycle::m_fShadowSideY[16]; float CTimeCycle::m_fShadowDisplacementX[16]; float CTimeCycle::m_fShadowDisplacementY[16]; void CTimeCycle::Initialise(void) { int w, h; int li, bi; char line[1040]; int ambR, ambG, ambB; int ambobjR, ambobjG, ambobjB; int ambblR, ambblG, ambblB; int ambobjblR, ambobjblG, ambobjblB; int dirR, dirG, dirB; int skyTopR, skyTopG, skyTopB; int skyBotR, skyBotG, skyBotB; int sunCoreR, sunCoreG, sunCoreB; int sunCoronaR, sunCoronaG, sunCoronaB; float sunSz, sprSz, sprBght; int shad, lightShad, poleShad; float farClp, fogSt, lightGnd; int cloudR, cloudG, cloudB; int fluffyTopR, fluffyTopG, fluffyTopB; int fluffyBotR, fluffyBotG, fluffyBotB; float blurR, blurG, blurB; float waterR, waterG, waterB, waterA; debug("Intialising CTimeCycle...\n"); CFileMgr::SetDir("DATA"); CFileMgr::LoadFile("TIMECYC.DAT", work_buff, sizeof(work_buff), "rb"); CFileMgr::SetDir(""); line[0] = '\0'; bi = 0; for(w = 0; w < NUMWEATHERS; w++) for(h = 0; h < NUMHOURS; h++){ li = 0; while(work_buff[bi] == '/' || work_buff[bi] == '\n' || work_buff[bi] == '\0' || work_buff[bi] == ' ' || work_buff[bi] == '\r'){ while(work_buff[bi] != '\n' && work_buff[bi] != '\0' && work_buff[bi] != '\r') bi++; bi++; } while(work_buff[bi] != '\n' #ifdef FIX_BUGS && work_buff[bi] != '\0' #endif ) line[li++] = work_buff[bi++]; line[li] = '\0'; bi++; sscanf(line, "%d %d %d %d %d %d %d %d %d %d %d %d " "%d %d %d %d %d %d %d %d %d " "%d %d %d %d %d %d %f %f %f %d %d %d %f %f %f " "%d %d %d %d %d %d %d %d %d %f %f %f %f %f %f %f", &ambR, &ambG, &ambB, &ambobjR, &ambobjG, &ambobjB, &ambblR, &ambblG, &ambblB, &ambobjblR, &ambobjblG, &ambobjblB, &dirR, &dirG, &dirB, &skyTopR, &skyTopG, &skyTopB, &skyBotR, &skyBotG, &skyBotB, &sunCoreR, &sunCoreG, &sunCoreB, &sunCoronaR, &sunCoronaG, &sunCoronaB, &sunSz, &sprSz, &sprBght, &shad, &lightShad, &poleShad, &farClp, &fogSt, &lightGnd, &cloudR, &cloudG, &cloudB, &fluffyTopR, &fluffyTopG, &fluffyTopB, &fluffyBotR, &fluffyBotG, &fluffyBotB, &blurR, &blurG, &blurB, &waterR, &waterG, &waterB, &waterA); m_nAmbientRed[h][w] = ambR; m_nAmbientGreen[h][w] = ambG; m_nAmbientBlue[h][w] = ambB; m_nAmbientRed_Obj[h][w] = ambobjR; m_nAmbientGreen_Obj[h][w] = ambobjG; m_nAmbientBlue_Obj[h][w] = ambobjB; m_nAmbientRed_Bl[h][w] = ambblR; m_nAmbientGreen_Bl[h][w] = ambblG; m_nAmbientBlue_Bl[h][w] = ambblB; m_nAmbientRed_Obj_Bl[h][w] = ambobjblR; m_nAmbientGreen_Obj_Bl[h][w] = ambobjblG; m_nAmbientBlue_Obj_Bl[h][w] = ambobjblB; m_nDirectionalRed[h][w] = dirR; m_nDirectionalGreen[h][w] = dirG; m_nDirectionalBlue[h][w] = dirB; m_nSkyTopRed[h][w] = skyTopR; m_nSkyTopGreen[h][w] = skyTopG; m_nSkyTopBlue[h][w] = skyTopB; m_nSkyBottomRed[h][w] = skyBotR; m_nSkyBottomGreen[h][w] = skyBotG; m_nSkyBottomBlue[h][w] = skyBotB; m_nSunCoreRed[h][w] = sunCoreR; m_nSunCoreGreen[h][w] = sunCoreG; m_nSunCoreBlue[h][w] = sunCoreB; m_nSunCoronaRed[h][w] = sunCoronaR; m_nSunCoronaGreen[h][w] = sunCoronaG; m_nSunCoronaBlue[h][w] = sunCoronaB; m_fSunSize[h][w] = sunSz * 10.0f; m_fSpriteSize[h][w] = sprSz * 10.0f; m_fSpriteBrightness[h][w] = sprBght * 10.0f; m_nShadowStrength[h][w] = shad; m_nLightShadowStrength[h][w] = lightShad; m_nPoleShadowStrength[h][w] = poleShad; m_fFarClip[h][w] = farClp; m_fFogStart[h][w] = fogSt; m_fLightsOnGroundBrightness[h][w] = lightGnd * 10.0f; m_nLowCloudsRed[h][w] = cloudR; m_nLowCloudsGreen[h][w] = cloudG; m_nLowCloudsBlue[h][w] = cloudB; m_nFluffyCloudsTopRed[h][w] = fluffyTopR; m_nFluffyCloudsTopGreen[h][w] = fluffyTopG; m_nFluffyCloudsTopBlue[h][w] = fluffyTopB; m_nFluffyCloudsBottomRed[h][w] = fluffyBotR; m_nFluffyCloudsBottomGreen[h][w] = fluffyBotG; m_nFluffyCloudsBottomBlue[h][w] = fluffyBotB; m_fBlurRed[h][w] = blurR; m_fBlurGreen[h][w] = blurG; m_fBlurBlue[h][w] = blurB; m_fWaterRed[h][w] = waterR; m_fWaterGreen[h][w] = waterG; m_fWaterBlue[h][w] = waterB; m_fWaterAlpha[h][w] = waterA; } m_FogReduction = 0; debug("CTimeCycle ready\n"); } static float interp_c0, interp_c1, interp_c2, interp_c3; float CTimeCycle::Interpolate(int8 *a, int8 *b) { return a[CWeather::OldWeatherType] * interp_c0 + b[CWeather::OldWeatherType] * interp_c1 + a[CWeather::NewWeatherType] * interp_c2 + b[CWeather::NewWeatherType] * interp_c3; } float CTimeCycle::Interpolate(uint8 *a, uint8 *b) { return a[CWeather::OldWeatherType] * interp_c0 + b[CWeather::OldWeatherType] * interp_c1 + a[CWeather::NewWeatherType] * interp_c2 + b[CWeather::NewWeatherType] * interp_c3; } float CTimeCycle::Interpolate(int16 *a, int16 *b) { return a[CWeather::OldWeatherType] * interp_c0 + b[CWeather::OldWeatherType] * interp_c1 + a[CWeather::NewWeatherType] * interp_c2 + b[CWeather::NewWeatherType] * interp_c3; } void CTimeCycle::StartExtraColour(int32 c, bool fade) { m_bExtraColourOn = true; m_ExtraColour = c; if(fade) m_ExtraColourInter = 0.0f; else m_ExtraColourInter = 1.0f; } void CTimeCycle::StopExtraColour(bool fade) { m_bExtraColourOn = false; if(!fade) m_ExtraColourInter = 0.0f; } void CTimeCycle::Update(void) { int h1 = CClock::GetHours(); int h2 = (h1+1)%24; int w1 = CWeather::OldWeatherType; int w2 = CWeather::NewWeatherType; float timeInterp = (CClock::GetMinutes() + CClock::GetSeconds()/60.0f)/60.0f; // coefficients for a bilinear interpolation interp_c0 = (1.0f-timeInterp) * (1.0f-CWeather::InterpolationValue); interp_c1 = timeInterp * (1.0f-CWeather::InterpolationValue); interp_c2 = (1.0f-timeInterp) * CWeather::InterpolationValue; interp_c3 = timeInterp * CWeather::InterpolationValue; #define INTERP(v) Interpolate(v[h1], v[h2]) m_nCurrentSkyTopRed = INTERP(m_nSkyTopRed); m_nCurrentSkyTopGreen = INTERP(m_nSkyTopGreen); m_nCurrentSkyTopBlue = INTERP(m_nSkyTopBlue); m_nCurrentSkyBottomRed = INTERP(m_nSkyBottomRed); m_nCurrentSkyBottomGreen = INTERP(m_nSkyBottomGreen); m_nCurrentSkyBottomBlue = INTERP(m_nSkyBottomBlue); m_fCurrentAmbientRed = INTERP(m_nAmbientRed); m_fCurrentAmbientGreen = INTERP(m_nAmbientGreen); m_fCurrentAmbientBlue = INTERP(m_nAmbientBlue); m_fCurrentAmbientRed_Obj = INTERP(m_nAmbientRed_Obj); m_fCurrentAmbientGreen_Obj = INTERP(m_nAmbientGreen_Obj); m_fCurrentAmbientBlue_Obj = INTERP(m_nAmbientBlue_Obj); m_fCurrentAmbientRed_Bl = INTERP(m_nAmbientRed_Bl); m_fCurrentAmbientGreen_Bl = INTERP(m_nAmbientGreen_Bl); m_fCurrentAmbientBlue_Bl = INTERP(m_nAmbientBlue_Bl); m_fCurrentAmbientRed_Obj_Bl = INTERP(m_nAmbientRed_Obj_Bl); m_fCurrentAmbientGreen_Obj_Bl = INTERP(m_nAmbientGreen_Obj_Bl); m_fCurrentAmbientBlue_Obj_Bl = INTERP(m_nAmbientBlue_Obj_Bl); m_fCurrentDirectionalRed = INTERP(m_nDirectionalRed); m_fCurrentDirectionalGreen = INTERP(m_nDirectionalGreen); m_fCurrentDirectionalBlue = INTERP(m_nDirectionalBlue); m_nCurrentSunCoreRed = INTERP(m_nSunCoreRed); m_nCurrentSunCoreGreen = INTERP(m_nSunCoreGreen); m_nCurrentSunCoreBlue = INTERP(m_nSunCoreBlue); m_nCurrentSunCoronaRed = INTERP(m_nSunCoronaRed); m_nCurrentSunCoronaGreen = INTERP(m_nSunCoronaGreen); m_nCurrentSunCoronaBlue = INTERP(m_nSunCoronaBlue); m_fCurrentSunSize = INTERP(m_fSunSize)/10.0f; m_fCurrentSpriteSize = INTERP(m_fSpriteSize)/10.0f; m_fCurrentSpriteBrightness = INTERP(m_fSpriteBrightness)/10.0f; m_nCurrentShadowStrength = INTERP(m_nShadowStrength); m_nCurrentLightShadowStrength = INTERP(m_nLightShadowStrength); m_nCurrentPoleShadowStrength = INTERP(m_nPoleShadowStrength); m_fCurrentFarClip = INTERP(m_fFarClip); m_fCurrentFogStart = INTERP(m_fFogStart); m_fCurrentLightsOnGroundBrightness = INTERP(m_fLightsOnGroundBrightness)/10.0f; m_nCurrentLowCloudsRed = INTERP(m_nLowCloudsRed); m_nCurrentLowCloudsGreen = INTERP(m_nLowCloudsGreen); m_nCurrentLowCloudsBlue = INTERP(m_nLowCloudsBlue); m_nCurrentFluffyCloudsTopRed = INTERP(m_nFluffyCloudsTopRed); m_nCurrentFluffyCloudsTopGreen = INTERP(m_nFluffyCloudsTopGreen); m_nCurrentFluffyCloudsTopBlue = INTERP(m_nFluffyCloudsTopBlue); m_nCurrentFluffyCloudsBottomRed = INTERP(m_nFluffyCloudsBottomRed); m_nCurrentFluffyCloudsBottomGreen = INTERP(m_nFluffyCloudsBottomGreen); m_nCurrentFluffyCloudsBottomBlue = INTERP(m_nFluffyCloudsBottomBlue); m_fCurrentBlurRed = INTERP(m_fBlurRed); m_fCurrentBlurGreen = INTERP(m_fBlurGreen); m_fCurrentBlurBlue = INTERP(m_fBlurBlue); m_fCurrentWaterRed = INTERP(m_fWaterRed); m_fCurrentWaterGreen = INTERP(m_fWaterGreen); m_fCurrentWaterBlue = INTERP(m_fWaterBlue); m_fCurrentWaterAlpha = INTERP(m_fWaterAlpha); #undef INTERP if(m_FogReduction != 0) m_fCurrentFarClip = Max(m_fCurrentFarClip, m_FogReduction/64.0f * 650.0f); m_CurrentStoredValue = (m_CurrentStoredValue+1)&0xF; float sunAngle = 2*PI*(CClock::GetSeconds()/60.0f + CClock::GetMinutes() + CClock::GetHours()*60)/(24*60); CVector &sunPos = GetSunDirection(); sunPos.x = Sin(sunAngle); sunPos.y = 1.0f; sunPos.z = 0.2f - Cos(sunAngle); sunPos.Normalise(); if(m_bExtraColourOn) m_ExtraColourInter = Min(1.0f, m_ExtraColourInter + CTimer::GetTimeStep()/120.0f); else m_ExtraColourInter = Max(-.0f, m_ExtraColourInter - CTimer::GetTimeStep()/120.0f); if(m_ExtraColourInter > 0.0f){ #define INTERP(extra,cur) (m_ExtraColourInter*extra[m_ExtraColour][WEATHER_EXTRACOLOURS] + (1.0f-m_ExtraColourInter)*cur) #define INTERPscl(extra,scl,cur) (m_ExtraColourInter*extra[m_ExtraColour][WEATHER_EXTRACOLOURS]/scl + (1.0f-m_ExtraColourInter)*cur) if(m_nSkyTopRed[m_ExtraColour][WEATHER_EXTRACOLOURS] != 0 || m_nSkyTopGreen[m_ExtraColour][WEATHER_EXTRACOLOURS] != 0 || m_nSkyTopBlue[m_ExtraColour][WEATHER_EXTRACOLOURS] != 0){ m_nCurrentSkyTopRed = INTERP(m_nSkyTopRed,m_nCurrentSkyTopRed); m_nCurrentSkyTopGreen = INTERP(m_nSkyTopGreen,m_nCurrentSkyTopGreen); m_nCurrentSkyTopBlue = INTERP(m_nSkyTopBlue,m_nCurrentSkyTopBlue); m_nCurrentSkyBottomRed = INTERP(m_nSkyBottomRed,m_nCurrentSkyBottomRed); m_nCurrentSkyBottomGreen = INTERP(m_nSkyBottomGreen,m_nCurrentSkyBottomGreen); m_nCurrentSkyBottomBlue = INTERP(m_nSkyBottomBlue,m_nCurrentSkyBottomBlue); m_nCurrentSunCoreRed = INTERP(m_nSunCoreRed,m_nCurrentSunCoreRed); m_nCurrentSunCoreGreen = INTERP(m_nSunCoreGreen,m_nCurrentSunCoreGreen); m_nCurrentSunCoreBlue = INTERP(m_nSunCoreBlue,m_nCurrentSunCoreBlue); m_nCurrentSunCoronaRed = INTERP(m_nSunCoronaRed,m_nCurrentSunCoronaRed); m_nCurrentSunCoronaGreen = INTERP(m_nSunCoronaGreen,m_nCurrentSunCoronaGreen); m_nCurrentSunCoronaBlue = INTERP(m_nSunCoronaBlue,m_nCurrentSunCoronaBlue); m_fCurrentSunSize = INTERPscl(m_fSunSize,10.0f,m_fCurrentSunSize); m_nCurrentLowCloudsRed = INTERP(m_nLowCloudsRed,m_nCurrentLowCloudsRed); m_nCurrentLowCloudsGreen = INTERP(m_nLowCloudsGreen,m_nCurrentLowCloudsGreen); m_nCurrentLowCloudsBlue = INTERP(m_nLowCloudsBlue,m_nCurrentLowCloudsBlue); m_nCurrentFluffyCloudsTopRed = INTERP(m_nFluffyCloudsTopRed,m_nCurrentFluffyCloudsTopRed); m_nCurrentFluffyCloudsTopGreen = INTERP(m_nFluffyCloudsTopGreen,m_nCurrentFluffyCloudsTopGreen); m_nCurrentFluffyCloudsTopBlue = INTERP(m_nFluffyCloudsTopBlue,m_nCurrentFluffyCloudsTopBlue); m_nCurrentFluffyCloudsBottomRed = INTERP(m_nFluffyCloudsBottomRed,m_nCurrentFluffyCloudsBottomRed); m_nCurrentFluffyCloudsBottomGreen = INTERP(m_nFluffyCloudsBottomGreen,m_nCurrentFluffyCloudsBottomGreen); m_nCurrentFluffyCloudsBottomBlue = INTERP(m_nFluffyCloudsBottomBlue,m_nCurrentFluffyCloudsBottomBlue); m_fCurrentWaterRed = INTERP(m_fWaterRed,m_fCurrentWaterRed); m_fCurrentWaterGreen = INTERP(m_fWaterGreen,m_fCurrentWaterGreen); m_fCurrentWaterBlue = INTERP(m_fWaterBlue,m_fCurrentWaterBlue); m_fCurrentWaterAlpha = INTERP(m_fWaterAlpha,m_fCurrentWaterAlpha); } m_fCurrentAmbientRed = INTERP(m_nAmbientRed,m_fCurrentAmbientRed); m_fCurrentAmbientGreen = INTERP(m_nAmbientGreen,m_fCurrentAmbientGreen); m_fCurrentAmbientBlue = INTERP(m_nAmbientBlue,m_fCurrentAmbientBlue); m_fCurrentAmbientRed_Obj = INTERP(m_nAmbientRed_Obj,m_fCurrentAmbientRed_Obj); m_fCurrentAmbientGreen_Obj = INTERP(m_nAmbientGreen_Obj,m_fCurrentAmbientGreen_Obj); m_fCurrentAmbientBlue_Obj = INTERP(m_nAmbientBlue_Obj,m_fCurrentAmbientBlue_Obj); m_fCurrentAmbientRed_Bl = INTERP(m_nAmbientRed_Bl,m_fCurrentAmbientRed_Bl); m_fCurrentAmbientGreen_Bl = INTERP(m_nAmbientGreen_Bl,m_fCurrentAmbientGreen_Bl); m_fCurrentAmbientBlue_Bl = INTERP(m_nAmbientBlue_Bl,m_fCurrentAmbientBlue_Bl); m_fCurrentAmbientRed_Obj_Bl = INTERP(m_nAmbientRed_Obj_Bl,m_fCurrentAmbientRed_Obj_Bl); m_fCurrentAmbientGreen_Obj_Bl = INTERP(m_nAmbientGreen_Obj_Bl,m_fCurrentAmbientGreen_Obj_Bl); m_fCurrentAmbientBlue_Obj_Bl = INTERP(m_nAmbientBlue_Obj_Bl,m_fCurrentAmbientBlue_Obj_Bl); m_fCurrentDirectionalRed = INTERP(m_nDirectionalRed,m_fCurrentDirectionalRed); m_fCurrentDirectionalGreen = INTERP(m_nDirectionalGreen,m_fCurrentDirectionalGreen); m_fCurrentDirectionalBlue = INTERP(m_nDirectionalBlue,m_fCurrentDirectionalBlue); m_fCurrentSpriteSize = INTERPscl(m_fSpriteSize,10.0f,m_fCurrentSpriteSize); m_fCurrentSpriteBrightness = INTERPscl(m_fSpriteBrightness,10.0f,m_fCurrentSpriteBrightness); m_nCurrentShadowStrength = INTERP(m_nShadowStrength,m_nCurrentShadowStrength); m_nCurrentLightShadowStrength = INTERP(m_nLightShadowStrength,m_nCurrentLightShadowStrength); m_nCurrentPoleShadowStrength = INTERP(m_nPoleShadowStrength,m_nCurrentPoleShadowStrength); m_fCurrentFarClip = INTERP(m_fFarClip,m_fCurrentFarClip); m_fCurrentFogStart = INTERP(m_fFogStart,m_fCurrentFogStart); m_fCurrentLightsOnGroundBrightness = INTERPscl(m_fLightsOnGroundBrightness,10.0f,m_fCurrentLightsOnGroundBrightness); m_fCurrentBlurRed = INTERP(m_fBlurRed,m_fCurrentBlurRed); m_fCurrentBlurGreen = INTERP(m_fBlurGreen,m_fCurrentBlurGreen); m_fCurrentBlurBlue = INTERP(m_fBlurBlue,m_fCurrentBlurBlue); #undef INTERP #undef INTERPscl } if(TheCamera.m_BlurType == MOTION_BLUR_NONE || TheCamera.m_BlurType == MOTION_BLUR_LIGHT_SCENE) TheCamera.SetMotionBlur(m_fCurrentBlurRed, m_fCurrentBlurGreen, m_fCurrentBlurBlue, 5, MOTION_BLUR_LIGHT_SCENE); m_nCurrentFogColourRed = (m_nCurrentSkyTopRed + 2*m_nCurrentSkyBottomRed) / 3; m_nCurrentFogColourGreen = (m_nCurrentSkyTopGreen + 2*m_nCurrentSkyBottomGreen) / 3; m_nCurrentFogColourBlue = (m_nCurrentSkyTopBlue + 2*m_nCurrentSkyBottomBlue) / 3; m_fCurrentAmbientRed /= 255.0f; m_fCurrentAmbientGreen /= 255.0f; m_fCurrentAmbientBlue /= 255.0f; m_fCurrentAmbientRed_Obj /= 255.0f; m_fCurrentAmbientGreen_Obj /= 255.0f; m_fCurrentAmbientBlue_Obj /= 255.0f; m_fCurrentAmbientRed_Bl /= 255.0f; m_fCurrentAmbientGreen_Bl /= 255.0f; m_fCurrentAmbientBlue_Bl /= 255.0f; m_fCurrentAmbientRed_Obj_Bl /= 255.0f; m_fCurrentAmbientGreen_Obj_Bl /= 255.0f; m_fCurrentAmbientBlue_Obj_Bl /= 255.0f; m_fCurrentDirectionalRed /= 255.0f; m_fCurrentDirectionalGreen /= 255.0f; m_fCurrentDirectionalBlue /= 255.0f; CShadows::CalcPedShadowValues(sunPos, &m_fShadowFrontX[m_CurrentStoredValue], &m_fShadowFrontY[m_CurrentStoredValue], &m_fShadowSideX[m_CurrentStoredValue], &m_fShadowSideY[m_CurrentStoredValue], &m_fShadowDisplacementX[m_CurrentStoredValue], &m_fShadowDisplacementY[m_CurrentStoredValue]); if(TheCamera.GetForward().z < -0.9f || !CWeather::bScriptsForceRain && (CCullZones::PlayerNoRain() || CCullZones::CamNoRain() || CCutsceneMgr::IsRunning())) m_FogReduction = Min(m_FogReduction+1, 64); else m_FogReduction = Max(m_FogReduction-1, 0); } ================================================ FILE: src/render/Timecycle.h ================================================ #pragma once class CTimeCycle { static uint8 m_nAmbientRed[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientRed_Obj[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientGreen_Obj[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientBlue_Obj[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientRed_Bl[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientGreen_Bl[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientBlue_Bl[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientRed_Obj_Bl[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientGreen_Obj_Bl[NUMHOURS][NUMWEATHERS]; static uint8 m_nAmbientBlue_Obj_Bl[NUMHOURS][NUMWEATHERS]; static uint8 m_nDirectionalRed[NUMHOURS][NUMWEATHERS]; static uint8 m_nDirectionalGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_nDirectionalBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_nSkyTopRed[NUMHOURS][NUMWEATHERS]; static uint8 m_nSkyTopGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_nSkyTopBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_nSkyBottomRed[NUMHOURS][NUMWEATHERS]; static uint8 m_nSkyBottomGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_nSkyBottomBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_nSunCoreRed[NUMHOURS][NUMWEATHERS]; static uint8 m_nSunCoreGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_nSunCoreBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_nSunCoronaRed[NUMHOURS][NUMWEATHERS]; static uint8 m_nSunCoronaGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_nSunCoronaBlue[NUMHOURS][NUMWEATHERS]; static int8 m_fSunSize[NUMHOURS][NUMWEATHERS]; static int8 m_fSpriteSize[NUMHOURS][NUMWEATHERS]; static int8 m_fSpriteBrightness[NUMHOURS][NUMWEATHERS]; static uint8 m_nShadowStrength[NUMHOURS][NUMWEATHERS]; static uint8 m_nLightShadowStrength[NUMHOURS][NUMWEATHERS]; static uint8 m_nPoleShadowStrength[NUMHOURS][NUMWEATHERS]; static int16 m_fFogStart[NUMHOURS][NUMWEATHERS]; static int16 m_fFarClip[NUMHOURS][NUMWEATHERS]; static uint8 m_fLightsOnGroundBrightness[NUMHOURS][NUMWEATHERS]; static uint8 m_nLowCloudsRed[NUMHOURS][NUMWEATHERS]; static uint8 m_nLowCloudsGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_nLowCloudsBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_nFluffyCloudsTopRed[NUMHOURS][NUMWEATHERS]; static uint8 m_nFluffyCloudsTopGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_nFluffyCloudsTopBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_nFluffyCloudsBottomRed[NUMHOURS][NUMWEATHERS]; static uint8 m_nFluffyCloudsBottomGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_nFluffyCloudsBottomBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_fBlurRed[NUMHOURS][NUMWEATHERS]; static uint8 m_fBlurGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_fBlurBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_fWaterRed[NUMHOURS][NUMWEATHERS]; static uint8 m_fWaterGreen[NUMHOURS][NUMWEATHERS]; static uint8 m_fWaterBlue[NUMHOURS][NUMWEATHERS]; static uint8 m_fWaterAlpha[NUMHOURS][NUMWEATHERS]; static float m_fCurrentAmbientRed; static float m_fCurrentAmbientGreen; static float m_fCurrentAmbientBlue; static float m_fCurrentAmbientRed_Obj; static float m_fCurrentAmbientGreen_Obj; static float m_fCurrentAmbientBlue_Obj; static float m_fCurrentAmbientRed_Bl; static float m_fCurrentAmbientGreen_Bl; static float m_fCurrentAmbientBlue_Bl; static float m_fCurrentAmbientRed_Obj_Bl; static float m_fCurrentAmbientGreen_Obj_Bl; static float m_fCurrentAmbientBlue_Obj_Bl; static float m_fCurrentDirectionalRed; static float m_fCurrentDirectionalGreen; static float m_fCurrentDirectionalBlue; static int32 m_nCurrentSkyTopRed; static int32 m_nCurrentSkyTopGreen; static int32 m_nCurrentSkyTopBlue; static int32 m_nCurrentSkyBottomRed; static int32 m_nCurrentSkyBottomGreen; static int32 m_nCurrentSkyBottomBlue; static int32 m_nCurrentSunCoreRed; static int32 m_nCurrentSunCoreGreen; static int32 m_nCurrentSunCoreBlue; static int32 m_nCurrentSunCoronaRed; static int32 m_nCurrentSunCoronaGreen; static int32 m_nCurrentSunCoronaBlue; static float m_fCurrentSunSize; static float m_fCurrentSpriteSize; static float m_fCurrentSpriteBrightness; static int32 m_nCurrentShadowStrength; static int32 m_nCurrentLightShadowStrength; static int32 m_nCurrentPoleShadowStrength; static float m_fCurrentFogStart; static float m_fCurrentFarClip; static float m_fCurrentLightsOnGroundBrightness; static int32 m_nCurrentLowCloudsRed; static int32 m_nCurrentLowCloudsGreen; static int32 m_nCurrentLowCloudsBlue; static int32 m_nCurrentFluffyCloudsTopRed; static int32 m_nCurrentFluffyCloudsTopGreen; static int32 m_nCurrentFluffyCloudsTopBlue; static int32 m_nCurrentFluffyCloudsBottomRed; static int32 m_nCurrentFluffyCloudsBottomGreen; static int32 m_nCurrentFluffyCloudsBottomBlue; static float m_fCurrentBlurRed; static float m_fCurrentBlurGreen; static float m_fCurrentBlurBlue; static float m_fCurrentWaterRed; static float m_fCurrentWaterGreen; static float m_fCurrentWaterBlue; static float m_fCurrentWaterAlpha; static int32 m_nCurrentFogColourRed; static int32 m_nCurrentFogColourGreen; static int32 m_nCurrentFogColourBlue; static int32 m_FogReduction; public: static int32 m_bExtraColourOn; static int32 m_ExtraColour; static float m_ExtraColourInter; static int32 m_CurrentStoredValue; static CVector m_VectorToSun[16]; static float m_fShadowFrontX[16]; static float m_fShadowFrontY[16]; static float m_fShadowSideX[16]; static float m_fShadowSideY[16]; static float m_fShadowDisplacementX[16]; static float m_fShadowDisplacementY[16]; static float GetAmbientRed(void) { return m_fCurrentAmbientRed; } static float GetAmbientGreen(void) { return m_fCurrentAmbientGreen; } static float GetAmbientBlue(void) { return m_fCurrentAmbientBlue; } static float GetAmbientRed_Obj(void) { return m_fCurrentAmbientRed_Obj; } static float GetAmbientGreen_Obj(void) { return m_fCurrentAmbientGreen_Obj; } static float GetAmbientBlue_Obj(void) { return m_fCurrentAmbientBlue_Obj; } static float GetAmbientRed_Bl(void) { return m_fCurrentAmbientRed_Bl; } static float GetAmbientGreen_Bl(void) { return m_fCurrentAmbientGreen_Bl; } static float GetAmbientBlue_Bl(void) { return m_fCurrentAmbientBlue_Bl; } static float GetAmbientRed_Obj_Bl(void) { return m_fCurrentAmbientRed_Obj_Bl; } static float GetAmbientGreen_Obj_Bl(void) { return m_fCurrentAmbientGreen_Obj_Bl; } static float GetAmbientBlue_Obj_Bl(void) { return m_fCurrentAmbientBlue_Obj_Bl; } static float GetDirectionalRed(void) { return m_fCurrentDirectionalRed; } static float GetDirectionalGreen(void) { return m_fCurrentDirectionalGreen; } static float GetDirectionalBlue(void) { return m_fCurrentDirectionalBlue; } static int32 GetSkyTopRed(void) { return m_nCurrentSkyTopRed; } static int32 GetSkyTopGreen(void) { return m_nCurrentSkyTopGreen; } static int32 GetSkyTopBlue(void) { return m_nCurrentSkyTopBlue; } static int32 GetSkyBottomRed(void) { return m_nCurrentSkyBottomRed; } static int32 GetSkyBottomGreen(void) { return m_nCurrentSkyBottomGreen; } static int32 GetSkyBottomBlue(void) { return m_nCurrentSkyBottomBlue; } static int32 GetSunCoreRed(void) { return m_nCurrentSunCoreRed; } static int32 GetSunCoreGreen(void) { return m_nCurrentSunCoreGreen; } static int32 GetSunCoreBlue(void) { return m_nCurrentSunCoreBlue; } static int32 GetSunCoronaRed(void) { return m_nCurrentSunCoronaRed; } static int32 GetSunCoronaGreen(void) { return m_nCurrentSunCoronaGreen; } static int32 GetSunCoronaBlue(void) { return m_nCurrentSunCoronaBlue; } static float GetSunSize(void) { return m_fCurrentSunSize; } static float GetSpriteBrightness(void) { return m_fCurrentSpriteBrightness; } static float GetSpriteSize(void) { return m_fCurrentSpriteSize; } static int32 GetShadowStrength(void) { return m_nCurrentShadowStrength; } static int32 GetLightShadowStrength(void) { return m_nCurrentLightShadowStrength; } static float GetLightOnGroundBrightness(void) { return m_fCurrentLightsOnGroundBrightness; } static float GetFarClip(void) { return m_fCurrentFarClip; } static float GetFogStart(void) { return m_fCurrentFogStart; } static int32 GetLowCloudsRed(void) { return m_nCurrentLowCloudsRed; } static int32 GetLowCloudsGreen(void) { return m_nCurrentLowCloudsGreen; } static int32 GetLowCloudsBlue(void) { return m_nCurrentLowCloudsBlue; } static int32 GetFluffyCloudsTopRed(void) { return m_nCurrentFluffyCloudsTopRed; } static int32 GetFluffyCloudsTopGreen(void) { return m_nCurrentFluffyCloudsTopGreen; } static int32 GetFluffyCloudsTopBlue(void) { return m_nCurrentFluffyCloudsTopBlue; } static int32 GetFluffyCloudsBottomRed(void) { return m_nCurrentFluffyCloudsBottomRed; } static int32 GetFluffyCloudsBottomGreen(void) { return m_nCurrentFluffyCloudsBottomGreen; } static int32 GetFluffyCloudsBottomBlue(void) { return m_nCurrentFluffyCloudsBottomBlue; } static int32 GetFogRed(void) { return m_nCurrentFogColourRed; } static int32 GetFogGreen(void) { return m_nCurrentFogColourGreen; } static int32 GetFogBlue(void) { return m_nCurrentFogColourBlue; } static int32 GetFogReduction(void) { return m_FogReduction; } static int32 GetBlurRed(void) { return m_fCurrentBlurRed; } static int32 GetBlurGreen(void) { return m_fCurrentBlurGreen; } static int32 GetBlurBlue(void) { return m_fCurrentBlurBlue; } static int32 GetWaterRed(void) { return m_fCurrentWaterRed; } static int32 GetWaterGreen(void) { return m_fCurrentWaterGreen; } static int32 GetWaterBlue(void) { return m_fCurrentWaterBlue; } static int32 GetWaterAlpha(void) { return m_fCurrentWaterAlpha; } static void Initialise(void); static void Update(void); static float Interpolate(int8 *a, int8 *b); static float Interpolate(uint8 *a, uint8 *b); static float Interpolate(int16 *a, int16 *b); static void StartExtraColour(int32 c, bool fade); static void StopExtraColour(bool fade); static CVector &GetSunDirection(void) { return m_VectorToSun[m_CurrentStoredValue]; } static float GetShadowFrontX(void) { return m_fShadowFrontX[m_CurrentStoredValue]; } static float GetShadowFrontY(void) { return m_fShadowFrontY[m_CurrentStoredValue]; } static float GetShadowSideX(void) { return m_fShadowSideX[m_CurrentStoredValue]; } static float GetShadowSideY(void) { return m_fShadowSideY[m_CurrentStoredValue]; } static float GetShadowDisplacementX(void) { return m_fShadowDisplacementX[m_CurrentStoredValue]; } static float GetShadowDisplacementY(void) { return m_fShadowDisplacementY[m_CurrentStoredValue]; } }; ================================================ FILE: src/render/VarConsole.cpp ================================================ #include "common.h" #include "VarConsole.h" #include "Font.h" #include "Pad.h" #define VAR_CONSOLE_PAD 1 CVarConsole VarConsole; void CVarConsole::Initialise() { m_nCountEntries = 0; m_nCurPage = 1; m_bIsOpen = false; m_nCurEntry = 0; m_nFirstEntryOnPage = 0; } void CVarConsole::Add(char *text, int8 *pVal, uint8 step, int8 min, int8 max, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pInt8Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_INT8; m_aEntries[i].I8_step = step; m_aEntries[i].I8_min = min; m_aEntries[i].I8_max = max; m_nCountEntries++; } void CVarConsole::Add(char *text, int16 *pVal, uint16 step, int16 min, int16 max, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pInt16Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_INT16; m_aEntries[i].I16_step = step; m_aEntries[i].I16_min = min; m_aEntries[i].I16_max = max; m_nCountEntries++; } void CVarConsole::Add(char *text, int32 *pVal, uint32 step, int32 min, int32 max, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pInt32Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_INT32; m_aEntries[i].I32_step = step; m_aEntries[i].I32_min = min; m_aEntries[i].I32_max = max; m_nCountEntries++; } void CVarConsole::Add(char *text, int64 *pVal, uint64 step, int64 min, int64 max, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pInt64Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_INT64; m_aEntries[i].I64_step = step; m_aEntries[i].I64_min = min; m_aEntries[i].I64_max = max; m_nCountEntries++; } void CVarConsole::Add(char *text, uint8 *pVal, uint8 step, int8 min, int8 max, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pUint8Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_UINT8; m_aEntries[i].I8_step = step; m_aEntries[i].I8_min = min; m_aEntries[i].I8_max = max; m_nCountEntries++; } void CVarConsole::Add(char *text, uint16 *pVal, uint16 step, int16 min, int16 max, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pUint16Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_UINT16; m_aEntries[i].I16_step = step; m_aEntries[i].I16_min = min; m_aEntries[i].I16_max = max; m_nCountEntries++; } void CVarConsole::Add(char *text, uint32 *pVal, uint32 step, int32 min, int32 max, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pUint32Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_UINT32; m_aEntries[i].I32_step = step; m_aEntries[i].I32_min = min; m_aEntries[i].I32_max = max; m_nCountEntries++; } void CVarConsole::Add(char *text, uint64 *pVal, uint64 step, int64 min, int64 max, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pUint64Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_UINT64; m_aEntries[i].I64_step = step; m_aEntries[i].I64_min = min; m_aEntries[i].I64_max = max; m_nCountEntries++; } void CVarConsole::Add(char *text, float *pVal, float step, float min, float max, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pFloatValue = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_FLOAT; m_aEntries[i].F_step = step; m_aEntries[i].F_min = min; m_aEntries[i].F_max = max; m_nCountEntries++; } void CVarConsole::Add(char *text, bool *pVal, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pBoolValue = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_BOOL; m_nCountEntries++; } void CVarConsole::Add(char *text, bool8 *pVal, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pUint8Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_BOOL8; m_nCountEntries++; } void CVarConsole::Add(char *text, bool16 *pVal, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pUint16Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_BOOL16; m_nCountEntries++; } void CVarConsole::Add(char *text, bool32 *pVal, bool8 isVar) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pUint32Value = pVal; m_aEntries[i].bAllowExceedBounds = isVar; m_aEntries[i].VarType = VCE_TYPE_BOOL32; m_nCountEntries++; } void CVarConsole::Add(char *text, void (*pCallback)(void)) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) return; } m_aEntries[i].text = text; m_aEntries[i].pCallback = pCallback; m_aEntries[i].VarType = VCE_TYPE_FUNCTION; m_nCountEntries++; } void CVarConsole::Remove(char *text) { int i; for (i = 0; i < m_nCountEntries; i++) { if (m_aEntries[i].text == text) { for (int j = i; j < m_nCountEntries-1; j++) m_aEntries[j] = m_aEntries[j+1]; m_nCountEntries--; return; } } } void CVarConsole::SortPages() { m_nNumPages = m_nCountEntries / 30 + 1; } void CVarConsole::Display() { char s[256]; wchar ws[256]; CFont::SetColor(CRGBA(200, 200, 200, 255)); CFont::SetFontStyle(FONT_STANDARD); CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.6f)); CFont::SetDropShadowPosition(2); CFont::SetDropColor(CRGBA(0, 0, 0, 255)); CFont::SetPropOn(); CFont::SetWrapx(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH)); CFont::SetRightJustifyWrap(0.0f); sprintf(s, "PAGE %d", m_nCurPage); AsciiToUnicode(s, ws); CFont::SetRightJustifyOn(); CFont::PrintString(SCREEN_SCALE_X(310.0f), SCREEN_SCALE_Y(30.0f), ws); CFont::SetRightJustifyOff(); int y = 45; for (int i = m_nFirstEntryOnPage; i < m_nCountEntries && i < m_nFirstEntryOnPage + 30; i++) { switch (m_aEntries[i].VarType) { case VCE_TYPE_INT8: sprintf(s, "(%d) %s:I8:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pInt8Value); break; case VCE_TYPE_INT16: sprintf(s, "(%d) %s:I16:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pInt16Value); break; case VCE_TYPE_INT32: sprintf(s, "(%d) %s:I32:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pInt32Value); break; case VCE_TYPE_INT64: #ifdef FIX_BUGS sprintf(s, "(%d) %s:I64:%lld", i + 1, m_aEntries[i].text, *m_aEntries[i].pInt64Value); #else sprintf(s, "(%d) %s:I64:%d", i + 1, m_aEntries[i].text, (int32)*m_aEntries[i].pInt64Value); #endif break; case VCE_TYPE_UINT8: sprintf(s, "(%d) %s:U8:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pUint8Value); break; case VCE_TYPE_UINT16: sprintf(s, "(%d) %s:U6:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pUint16Value); break; case VCE_TYPE_UINT32: sprintf(s, "(%d) %s:U32:%d", i + 1, m_aEntries[i].text, *m_aEntries[i].pUint32Value); break; case VCE_TYPE_UINT64: #ifdef FIX_BUGS sprintf(s, "(%d) %s:U64:%llu", i + 1, m_aEntries[i].text, *m_aEntries[i].pUint64Value); #else sprintf(s, "(%d) %s:U64:%d", i + 1, m_aEntries[i].text, (uint32)*m_aEntries[i].pUint64Value); #endif break; case VCE_TYPE_FLOAT: sprintf(s, "(%d) %s:F:%f", i + 1, m_aEntries[i].text, *m_aEntries[i].pFloatValue); break; case VCE_TYPE_BOOL: if (*m_aEntries[i].pBoolValue) sprintf(s, "(%d) %s:B:TRUE", i + 1, m_aEntries[i].text); else sprintf(s, "(%d) %s:B : FALSE", i + 1, m_aEntries[i].text); break; case VCE_TYPE_BOOL8: if (*m_aEntries[i].pUint8Value == FALSE) sprintf(s, "(%d) %s:B8:FALSE", i + 1, m_aEntries[i].text); else sprintf(s, "(%d) %s:B8:TRUE", i + 1, m_aEntries[i].text); break; case VCE_TYPE_BOOL16: if (*m_aEntries[i].pUint16Value == FALSE) sprintf(s, "(%d) %s:B16:FALSE", i + 1, m_aEntries[i].text); else sprintf(s, "(%d) %s:B16:TRUE", i + 1, m_aEntries[i].text); break; case VCE_TYPE_BOOL32: if (*m_aEntries[i].pUint32Value == FALSE) sprintf(s, "(%d) %s:B32:FALSE", i + 1, m_aEntries[i].text); else sprintf(s, "(%d) %s:B32:TRUE", i + 1, m_aEntries[i].text); break; case VCE_TYPE_FUNCTION: sprintf(s, "(%d) %s:FUNCTION:call this function?", i + 1, m_aEntries[i].text); break; } AsciiToUnicode(s, ws); if (m_nCurEntry == i) { CFont::SetBackgroundOn(); #ifdef FIX_BUGS CFont::SetBackgroundColor(CRGBA(128, 128, 128, 128)); #endif } #ifdef FIX_BUGS else CFont::SetBackgroundColor(CRGBA(128, 128, 128, 0)); #endif CFont::SetColor(CRGBA(200, 200, 200, 255)); CFont::PrintString(SCREEN_SCALE_X(30.0f), SCREEN_SCALE_Y(y), ws); if (m_nCurEntry == i) CFont::SetBackgroundOff(); y += 12; } } void CVarConsole::ModifyLeft() { CVarConsoleEntry &entry = m_aEntries[m_nCurEntry]; switch (entry.VarType) { case VCE_TYPE_INT8: *entry.pInt8Value -= entry.I8_step; if (entry.bAllowExceedBounds) { if (*entry.pInt8Value < entry.I8_min) *entry.pInt8Value = entry.I8_max; } else { if (*entry.pInt8Value < entry.I8_min) *entry.pInt8Value = entry.I8_min; } break; case VCE_TYPE_INT16: *entry.pInt16Value -= entry.I16_step; if (entry.bAllowExceedBounds) { if (*entry.pInt16Value < entry.I16_min) *entry.pInt16Value = entry.I16_max; } else { if (*entry.pInt16Value < entry.I16_min) *entry.pInt16Value = entry.I16_min; } break; case VCE_TYPE_INT32: *entry.pInt32Value -= entry.I32_step; if (entry.bAllowExceedBounds) { if (*entry.pInt32Value < entry.I32_min) *entry.pInt32Value = entry.I32_max; } else { if (*entry.pInt32Value < entry.I32_min) *entry.pInt32Value = entry.I32_min; } break; case VCE_TYPE_INT64: *entry.pInt64Value -= entry.I64_step; if (entry.bAllowExceedBounds) { if (*entry.pInt64Value < entry.I64_min) *entry.pInt64Value = entry.I64_max; } else { if (*entry.pInt64Value < entry.I64_min) *entry.pInt64Value = entry.I64_min; } break; case VCE_TYPE_UINT8: *entry.pUint8Value -= entry.I8_step; if (entry.bAllowExceedBounds) { if (*(int8*)entry.pUint8Value < entry.I8_min) *entry.pUint8Value = entry.I8_max; } else { if (*(int8*)entry.pUint8Value < entry.I8_min) *entry.pUint8Value = entry.I8_min; } break; case VCE_TYPE_UINT16: *entry.pUint16Value -= entry.I16_step; if (entry.bAllowExceedBounds) { if (*(int16*)entry.pUint16Value < entry.I16_min) *entry.pUint16Value = entry.I16_max; } else { if (*(int16*)entry.pUint16Value < entry.I16_min) *entry.pUint16Value = entry.I16_min; } break; case VCE_TYPE_UINT32: *entry.pUint32Value -= entry.I32_step; if (entry.bAllowExceedBounds) { if (*(int32*)entry.pUint32Value < entry.I32_min) *entry.pUint32Value = entry.I32_max; } else { if (*(int32*)entry.pUint32Value < entry.I32_min) *entry.pUint32Value = entry.I32_min; } break; case VCE_TYPE_UINT64: *entry.pUint64Value -= entry.I64_step; if (entry.bAllowExceedBounds) { if (*(int64*)entry.pUint64Value < entry.I64_min) *entry.pUint64Value = entry.I64_max; } else { if (*(int64*)entry.pUint64Value < entry.I64_min) *entry.pUint64Value = entry.I64_min; } break; case VCE_TYPE_FLOAT: *entry.pFloatValue -= entry.F_step; if (entry.bAllowExceedBounds) { if (*entry.pFloatValue < entry.F_min) *entry.pFloatValue = entry.F_max; } else { if (*entry.pFloatValue < entry.F_min) *entry.pFloatValue = entry.F_min; } break; case VCE_TYPE_BOOL: if (entry.bAllowExceedBounds) *entry.pBoolValue ^= true; else *entry.pBoolValue = false; break; case VCE_TYPE_BOOL8: if (entry.bAllowExceedBounds) *entry.pUint8Value = *entry.pUint8Value == false; else *entry.pUint8Value = false; break; case VCE_TYPE_BOOL16: if (entry.bAllowExceedBounds) *entry.pUint16Value = *entry.pUint16Value == false; else *entry.pUint16Value = false; break; case VCE_TYPE_BOOL32: if (entry.bAllowExceedBounds) *entry.pUint32Value = *entry.pUint32Value == false; else *entry.pUint32Value = false; break; case VCE_TYPE_FUNCTION: entry.pCallback(); break; default: return; } } void CVarConsole::ModifyRight() { CVarConsoleEntry &entry = m_aEntries[m_nCurEntry]; switch (entry.VarType) { case VCE_TYPE_INT8: *entry.pInt8Value += entry.I8_step; if (entry.bAllowExceedBounds) { if (*entry.pInt8Value > entry.I8_max) *entry.pInt8Value = entry.I8_min; } else { if (*entry.pInt8Value > entry.I8_max) *entry.pInt8Value = entry.I8_max; } break; case VCE_TYPE_INT16: *entry.pInt16Value += entry.I16_step; if (entry.bAllowExceedBounds) { if (*entry.pInt16Value > entry.I16_max) *entry.pInt16Value = entry.I16_min; } else { if (*entry.pInt16Value > entry.I16_max) *entry.pInt16Value = entry.I16_max; } break; case VCE_TYPE_INT32: *entry.pInt32Value += entry.I32_step; if (entry.bAllowExceedBounds) { if (*entry.pInt32Value > entry.I32_max) *entry.pInt32Value = entry.I32_min; } else { if (*entry.pInt32Value > entry.I32_max) *entry.pInt32Value = entry.I32_max; } break; case VCE_TYPE_INT64: *entry.pInt64Value += entry.I64_step; if (entry.bAllowExceedBounds) { if (*entry.pInt64Value > entry.I64_max) *entry.pInt64Value = entry.I64_min; } else { if (*entry.pInt64Value > entry.I64_max) *entry.pInt64Value = entry.I64_max; } break; case VCE_TYPE_UINT8: *entry.pUint8Value += entry.I8_step; if (entry.bAllowExceedBounds) { if (*entry.pUint8Value > (uint8)entry.I8_max) *entry.pUint8Value = entry.I8_min; } else { if (*entry.pUint8Value > (uint8)entry.I8_max) *entry.pUint8Value = entry.I8_max; } break; case VCE_TYPE_UINT16: *entry.pUint16Value += entry.I16_step; if (entry.bAllowExceedBounds) { if (*entry.pUint16Value > (uint16)entry.I16_max) *entry.pUint16Value = entry.I16_min; } else { if (*entry.pUint16Value > (uint16)entry.I16_max) *entry.pUint16Value = entry.I16_max; } break; case VCE_TYPE_UINT32: *entry.pUint32Value += entry.I32_step; if (entry.bAllowExceedBounds) { if (*entry.pUint32Value > (uint32)entry.I32_max) *entry.pUint32Value = entry.I32_min; } else { if (*entry.pUint32Value > (uint32)entry.I32_max) *entry.pUint32Value = entry.I32_max; } break; case VCE_TYPE_UINT64: *entry.pUint64Value += entry.I64_step; if (entry.bAllowExceedBounds) { if (*entry.pUint64Value > (uint64)entry.I64_max) *entry.pUint64Value = entry.I64_min; } else { if (*entry.pUint64Value > (uint64)entry.I64_max) *entry.pUint64Value = entry.I64_max; } break; case VCE_TYPE_FLOAT: *entry.pFloatValue += entry.F_step; if (entry.bAllowExceedBounds) { if (*entry.pFloatValue > entry.F_max) *entry.pFloatValue = entry.F_min; } else { if (*entry.pFloatValue > entry.F_max) *entry.pFloatValue = entry.F_max; } break; case VCE_TYPE_BOOL: if (entry.bAllowExceedBounds) *entry.pBoolValue ^= true; else *entry.pBoolValue = true; break; case VCE_TYPE_BOOL8: if (entry.bAllowExceedBounds) *entry.pUint8Value = *entry.pUint8Value == false; else *entry.pUint8Value = true; break; case VCE_TYPE_BOOL16: if (entry.bAllowExceedBounds) *entry.pUint16Value = *entry.pUint16Value == false; else *entry.pUint16Value = true; break; case VCE_TYPE_BOOL32: if (entry.bAllowExceedBounds) *entry.pUint32Value = *entry.pUint32Value == false; else *entry.pUint32Value = true; break; case VCE_TYPE_FUNCTION: entry.pCallback(); break; default: return; } } void CVarConsole::Enter() { m_bIsOpen = true; } void CVarConsole::Exit() { m_bIsOpen = false; } void CVarConsole::Input() { if (CPad::GetPad(VAR_CONSOLE_PAD)->GetDPadDownJustDown() || CPad::GetPad(VAR_CONSOLE_PAD)->GetAnaloguePadDown()) { m_nCurEntry++; if (m_nCurEntry < m_nCountEntries) { if (m_nCurEntry > m_nFirstEntryOnPage + 29) { m_nFirstEntryOnPage = m_nCurEntry; ++m_nCurPage; } } else { m_nCurEntry = m_nCountEntries - 1; } } if (CPad::GetPad(VAR_CONSOLE_PAD)->GetDPadUpJustDown() || CPad::GetPad(VAR_CONSOLE_PAD)->GetAnaloguePadUp()) { m_nCurEntry--; if (m_nCurEntry < m_nFirstEntryOnPage) { m_nFirstEntryOnPage = m_nCurEntry - 29; --m_nCurPage; } if (m_nFirstEntryOnPage < 0) { m_nCurEntry = 0; m_nFirstEntryOnPage = 0; m_nCurPage = 1; } } if (CPad::GetPad(VAR_CONSOLE_PAD)->GetSquare()) ModifyLeft(); if (CPad::GetPad(VAR_CONSOLE_PAD)->GetTriangle()) ModifyRight(); if (CPad::GetPad(VAR_CONSOLE_PAD)->GetDPadLeftJustDown() || CPad::GetPad(VAR_CONSOLE_PAD)->GetAnaloguePadLeft()) ModifyLeft(); if (CPad::GetPad(VAR_CONSOLE_PAD)->GetDPadRightJustDown() || CPad::GetPad(VAR_CONSOLE_PAD)->GetAnaloguePadRight()) ModifyRight(); if (CPad::GetPad(VAR_CONSOLE_PAD)->GetLeftShoulder2JustDown()) { if (m_nCurPage > 1) { m_nCurPage--; m_nFirstEntryOnPage -= 30; m_nCurEntry = m_nFirstEntryOnPage; if (m_nFirstEntryOnPage < 0) { m_nFirstEntryOnPage = 0; m_nCurEntry = m_nFirstEntryOnPage; m_nCurPage = 1; } } } if (CPad::GetPad(VAR_CONSOLE_PAD)->GetRightShoulder2JustDown()) { if (m_nCurPage < m_nNumPages) { m_nCurPage++; m_nFirstEntryOnPage += 30; m_nCurEntry = m_nFirstEntryOnPage; if (m_nFirstEntryOnPage >= m_nCountEntries) { m_nFirstEntryOnPage -= 30; m_nCurEntry = m_nFirstEntryOnPage; m_nCurPage--; } } } if (CPad::GetPad(VAR_CONSOLE_PAD)->GetRightShoulder1JustDown() && CPad::GetPad(VAR_CONSOLE_PAD)->GetLeftShoulder1JustDown()) Exit(); } void CVarConsole::Process() { Input(); SortPages(); Display(); } bool8 CVarConsole::Open() { return m_bIsOpen; } void CVarConsole::Check() { if (Open()) Process(); else if (CPad::GetPad(VAR_CONSOLE_PAD)->GetRightShoulder1JustDown() && CPad::GetPad(VAR_CONSOLE_PAD)->GetLeftShoulder1JustDown()) Enter(); } ================================================ FILE: src/render/VarConsole.h ================================================ #pragma once enum eVarConsoleEntryType { VCE_TYPE_INT8, VCE_TYPE_INT16, VCE_TYPE_INT32, VCE_TYPE_INT64, VCE_TYPE_UINT8, VCE_TYPE_UINT16, VCE_TYPE_UINT32, VCE_TYPE_UINT64, VCE_TYPE_FLOAT, VCE_TYPE_BOOL, VCE_TYPE_BOOL8, VCE_TYPE_BOOL16, VCE_TYPE_BOOL32, VCE_TYPE_FUNCTION, }; struct CVarConsoleEntry { char *text; int8 *pInt8Value; int16 *pInt16Value; int32 *pInt32Value; int64 *pInt64Value; uint8 *pUint8Value; uint16 *pUint16Value; uint32 *pUint32Value; uint64 *pUint64Value; float *pFloatValue; bool *pBoolValue; void (*pCallback)(void); int8 I8_step, I8_max, I8_min; int16 I16_step, I16_max, I16_min; int32 I32_step, I32_max, I32_min; int64 I64_step, I64_max, I64_min; float F_step, F_max, F_min; bool8 bAllowExceedBounds; uint8 VarType; }; class CVarConsole { int32 m_nCountEntries; bool8 m_bIsOpen; int32 m_nCurEntry; int32 m_nFirstEntryOnPage; int32 m_nCurPage; int32 m_nNumPages; CVarConsoleEntry m_aEntries[91]; public: #ifdef FIX_BUGS CVarConsole() { Initialise(); } #endif void Initialise(); void Add(char *text, int8 *pVal, uint8 step, int8 min, int8 max, bool8 isVar); void Add(char *text, int16 *pVal, uint16 step, int16 min, int16 max, bool8 isVar); void Add(char *text, int32 *pVal, uint32 step, int32 min, int32 max, bool8 isVar); void Add(char *text, int64 *pVal, uint64 step, int64 min, int64 max, bool8 isVar); void Add(char *text, uint8 *pVal, uint8 step, int8 min, int8 max, bool8 isVar); void Add(char *text, uint16 *pVal, uint16 step, int16 min, int16 max, bool8 isVar); void Add(char *text, uint32 *pVal, uint32 step, int32 min, int32 max, bool8 isVar); void Add(char *text, uint64 *pVal, uint64 step, int64 min, int64 max, bool8 isVar); void Add(char *text, float *pVal, float step, float min, float max, bool8 isVar); void Add(char *text, bool *pVal, bool8 isVar); void Add(char *text, bool8 *pVal, bool8 isVar); void Add(char *text, bool16 *pVal, bool8 isVar); void Add(char *text, bool32 *pVal, bool8 isVar); void Add(char *text, void (*pVar)(void)); void Remove(char *text); void SortPages(); void Display(); void ModifyLeft(); void ModifyRight(); void Enter(); void Exit(); void Input(); void Process(); bool8 Open(); void Check(); }; extern CVarConsole VarConsole; ================================================ FILE: src/render/WaterCannon.cpp ================================================ #include "common.h" #include "WaterCannon.h" #include "Vector.h" #include "General.h" #include "main.h" #include "Timer.h" #include "Pools.h" #include "Ped.h" #include "AnimManager.h" #include "Fire.h" #include "WaterLevel.h" #include "Camera.h" #include "Particle.h" #define WATERCANNONVERTS 4 #define WATERCANNONINDEXES 12 RwIm3DVertex WaterCannonVertices[WATERCANNONVERTS]; RwImVertexIndex WaterCannonIndexList[WATERCANNONINDEXES]; CWaterCannon CWaterCannons::aCannons[NUM_WATERCANNONS]; void CWaterCannon::Init(void) { m_nId = 0; m_nCur = 0; m_nTimeCreated = CTimer::GetTimeInMilliseconds(); for ( int32 i = 0; i < NUM_SEGMENTPOINTS; i++ ) m_abUsed[i] = false; RwIm3DVertexSetU(&WaterCannonVertices[0], 0.0f); RwIm3DVertexSetV(&WaterCannonVertices[0], 0.0f); RwIm3DVertexSetU(&WaterCannonVertices[1], 1.0f); RwIm3DVertexSetV(&WaterCannonVertices[1], 0.0f); RwIm3DVertexSetU(&WaterCannonVertices[2], 0.0f); RwIm3DVertexSetV(&WaterCannonVertices[2], 0.0f); RwIm3DVertexSetU(&WaterCannonVertices[3], 1.0f); RwIm3DVertexSetV(&WaterCannonVertices[3], 0.0f); WaterCannonIndexList[0] = 0; WaterCannonIndexList[1] = 1; WaterCannonIndexList[2] = 2; WaterCannonIndexList[3] = 1; WaterCannonIndexList[4] = 3; WaterCannonIndexList[5] = 2; WaterCannonIndexList[6] = 0; WaterCannonIndexList[7] = 2; WaterCannonIndexList[8] = 1; WaterCannonIndexList[9] = 1; WaterCannonIndexList[10] = 2; WaterCannonIndexList[11] = 3; } void CWaterCannon::Update_OncePerFrame(int16 index) { ASSERT(index < NUM_WATERCANNONS); if (CTimer::GetTimeInMilliseconds() > m_nTimeCreated + WATERCANNON_LIFETIME ) { m_nCur = (m_nCur + 1) % NUM_SEGMENTPOINTS; m_abUsed[m_nCur] = false; } for ( int32 i = 0; i < NUM_SEGMENTPOINTS; i++ ) { if ( m_abUsed[i] ) { m_avecVelocity[i].z += -WATERCANNON_GRAVITY * CTimer::GetTimeStep(); m_avecPos[i] += m_avecVelocity[i] * CTimer::GetTimeStep(); } } for ( int32 i = 0; i < NUM_SEGMENTPOINTS; i++ ) { if ( m_abUsed[i] && gFireManager.ExtinguishPointWithWater(m_avecPos[i], 4.0f) ) { break; } } if ( ((index + CTimer::GetFrameCounter()) & 3) == 0 ) PushPeds(); // free if unused int32 i = 0; while ( 1 ) { if ( m_abUsed[i] ) break; if ( ++i >= NUM_SEGMENTPOINTS ) { m_nId = 0; return; } } } void CWaterCannon::Update_NewInput(CVector *pos, CVector *dir) { ASSERT(pos != NULL); ASSERT(dir != NULL); m_avecPos[m_nCur] = *pos; m_avecVelocity[m_nCur] = *dir; m_abUsed[m_nCur] = true; } void CWaterCannon::Render(void) { RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)gpWaterRaster); float v = float(CGeneral::GetRandomNumber() & 255) / 256; RwIm3DVertexSetV(&WaterCannonVertices[0], v); RwIm3DVertexSetV(&WaterCannonVertices[1], v); RwIm3DVertexSetV(&WaterCannonVertices[2], v); RwIm3DVertexSetV(&WaterCannonVertices[3], v); int16 pointA = m_nCur % NUM_SEGMENTPOINTS; int16 pointB = pointA - 1; if ( pointB < 0 ) pointB += NUM_SEGMENTPOINTS; bool bInit = false; CVector norm; for ( int32 i = 0; i < NUM_SEGMENTPOINTS - 1; i++ ) { if ( m_abUsed[pointA] && m_abUsed[pointB] ) { if ( !bInit ) { CVector cp = CrossProduct(m_avecPos[pointB] - m_avecPos[pointA], TheCamera.GetForward()); norm = cp * (0.05f / cp.Magnitude()); bInit = true; } float dist = float(i*i*i) / 300.0f + 1.0f; float brightness = float(i) / NUM_SEGMENTPOINTS; int32 color = (int32)((1.0f - brightness*brightness) * 255.0f); CVector offset = dist * norm; RwIm3DVertexSetRGBA(&WaterCannonVertices[0], color, color, color, color); RwIm3DVertexSetPos (&WaterCannonVertices[0], m_avecPos[pointA].x - offset.x, m_avecPos[pointA].y - offset.y, m_avecPos[pointA].z - offset.z); RwIm3DVertexSetRGBA(&WaterCannonVertices[1], color, color, color, color); RwIm3DVertexSetPos (&WaterCannonVertices[1], m_avecPos[pointA].x + offset.x, m_avecPos[pointA].y + offset.y, m_avecPos[pointA].z + offset.z); RwIm3DVertexSetRGBA(&WaterCannonVertices[2], color, color, color, color); RwIm3DVertexSetPos (&WaterCannonVertices[2], m_avecPos[pointB].x - offset.x, m_avecPos[pointB].y - offset.y, m_avecPos[pointB].z - offset.z); RwIm3DVertexSetRGBA(&WaterCannonVertices[3], color, color, color, color); RwIm3DVertexSetPos (&WaterCannonVertices[3], m_avecPos[pointB].x + offset.x, m_avecPos[pointB].y + offset.y, m_avecPos[pointB].z + offset.z); LittleTest(); if ( RwIm3DTransform(WaterCannonVertices, WATERCANNONVERTS, NULL, rwIM3D_VERTEXUV) ) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, WaterCannonIndexList, WATERCANNONINDEXES); RwIm3DEnd(); } } pointA = pointB--; if ( pointB < 0 ) pointB += NUM_SEGMENTPOINTS; } RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); } void CWaterCannon::PushPeds(void) { float minx = 10000.0f; float maxx = -10000.0f; float miny = 10000.0f; float maxy = -10000.0f; float minz = 10000.0f; float maxz = -10000.0f; for ( int32 i = 0; i < NUM_SEGMENTPOINTS; i++ ) { if ( m_abUsed[i] ) { minx = Min(minx, m_avecPos[i].x); maxx = Max(maxx, m_avecPos[i].x); miny = Min(miny, m_avecPos[i].y); maxy = Max(maxy, m_avecPos[i].y); minz = Min(minz, m_avecPos[i].z); maxz = Max(maxz, m_avecPos[i].z); } } for ( int32 i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--) { CPed *ped = CPools::GetPedPool()->GetSlot(i); if ( ped ) { if ( ped->GetPosition().x > minx && ped->GetPosition().x < maxx && ped->GetPosition().y > miny && ped->GetPosition().y < maxy && ped->GetPosition().z > minz && ped->GetPosition().z < maxz ) { for ( int32 j = 0; j < NUM_SEGMENTPOINTS; j++ ) { if ( m_abUsed[j] ) { CVector dist = m_avecPos[j] - ped->GetPosition(); if ( dist.MagnitudeSqr() < 5.0f ) { int32 localDir = ped->GetLocalDirection(CVector2D(1.0f, 0.0f)); ped->bIsStanding = false; ped->ApplyMoveForce(0.0f, 0.0f, 2.0f * CTimer::GetTimeStep()); ped->m_vecMoveSpeed.x = (0.6f * m_avecVelocity[j].x + ped->m_vecMoveSpeed.x) * 0.5f; ped->m_vecMoveSpeed.y = (0.6f * m_avecVelocity[j].y + ped->m_vecMoveSpeed.y) * 0.5f; float pedSpeed2D = ped->m_vecMoveSpeed.Magnitude2D(); if ( pedSpeed2D > 0.2f ) { ped->m_vecMoveSpeed.x *= (0.2f / pedSpeed2D); ped->m_vecMoveSpeed.y *= (0.2f / pedSpeed2D); } ped->SetFall(2000, (AnimationId)(localDir + ANIM_STD_HIGHIMPACT_FRONT), 0); CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, ped->GetPosition(), ped->m_vecMoveSpeed * 0.3f, 0, 0.5f); CParticle::AddParticle(PARTICLE_CAR_SPLASH, ped->GetPosition(), ped->m_vecMoveSpeed * -0.3f + CVector(0.f, 0.f, 0.5f), 0, 0.5f, CGeneral::GetRandomNumberInRange(0.f, 10.f), CGeneral::GetRandomNumberInRange(0.f, 90.f), 1); j = NUM_SEGMENTPOINTS; } } } } } } } void CWaterCannons::Init(void) { for ( int32 i = 0; i < NUM_WATERCANNONS; i++ ) aCannons[i].Init(); } void CWaterCannons::UpdateOne(uint32 id, CVector *pos, CVector *dir) { ASSERT(pos != NULL); ASSERT(dir != NULL); // find the one by id { int32 n = 0; while ( n < NUM_WATERCANNONS && id != aCannons[n].m_nId ) n++; if ( n < NUM_WATERCANNONS ) { aCannons[n].Update_NewInput(pos, dir); return; } } // if no luck then find a free one { int32 n = 0; while ( n < NUM_WATERCANNONS && 0 != aCannons[n].m_nId ) n++; if ( n < NUM_WATERCANNONS ) { aCannons[n].Init(); aCannons[n].m_nId = id; aCannons[n].Update_NewInput(pos, dir); return; } } } void CWaterCannons::Update(void) { for ( int32 i = 0; i < NUM_WATERCANNONS; i++ ) { if ( aCannons[i].m_nId != 0 ) aCannons[i].Update_OncePerFrame(i); } } void CWaterCannons::Render(void) { PUSH_RENDERGROUP("CWaterCannons::Render"); for ( int32 i = 0; i < NUM_WATERCANNONS; i++ ) { if ( aCannons[i].m_nId != 0 ) aCannons[i].Render(); } POP_RENDERGROUP(); } ================================================ FILE: src/render/WaterCannon.h ================================================ #pragma once #define WATERCANNON_GRAVITY (0.009f) #define WATERCANNON_LIFETIME (150) class CWaterCannon { public: enum { NUM_SEGMENTPOINTS = 16, }; int32 m_nId; int16 m_nCur; uint32 m_nTimeCreated; CVector m_avecPos[NUM_SEGMENTPOINTS]; CVector m_avecVelocity[NUM_SEGMENTPOINTS]; bool m_abUsed[NUM_SEGMENTPOINTS]; void Init(void); void Update_OncePerFrame(int16 index); void Update_NewInput(CVector *pos, CVector *dir); void Render(void); void PushPeds(void); }; VALIDATE_SIZE(CWaterCannon, 412); class CWaterCannons { public: static CWaterCannon aCannons[NUM_WATERCANNONS]; static void Init(void); static void UpdateOne(uint32 id, CVector *pos, CVector *dir); static void Update(); static void Render(void); }; ================================================ FILE: src/render/WaterCreatures.cpp ================================================ #include "common.h" #include "WaterCreatures.h" #include "ModelIndices.h" #include "World.h" #include "WaterLevel.h" #include "Camera.h" #include "PlayerPed.h" #include "General.h" #include "Object.h" int CWaterCreatures::nNumActiveSeaLifeForms; CWaterCreature CWaterCreatures::aWaterCreatures[NUM_WATER_CREATURES]; struct WaterCreatureProperties aProperties[65] = { { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_JELLYFISH, 0.01f, 2.2f, 0.0005f, 3.5f }, { &MI_JELLYFISH01, 0.01f, 2.2f, 0.0005f, 3.5f }, { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_TURTLE, 0.01f, 2.0f, 0.0005f, 4.0f }, { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_DOLPHIN, 0.03f, 1.5f, 0.0005f, 4.0f }, { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_SHARK, 0.03f, 0.4f, 0.0005f, 4.0f }, { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH1SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH2SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH2S, 0.04f, 1.5f, 0.0008f, 3.0f }, { &MI_FISH3SINGLE, 0.04f, 1.0f, 0.0008f, 3.0f }, { &MI_FISH3S, 0.04f, 1.5f, 0.0008f, 3.0f }, }; CWaterCreature::CWaterCreature() { Free(); } void CWaterCreature::Initialise(CObject *pObj, float fFwdSpeed, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state) { this->m_pObj = pObj; this->m_fFwdSpeed = fFwdSpeed; this->m_fZTurnSpeed = fZTurnSpeed; this->m_fWaterDepth = fWaterDepth; this->m_alpha = alpha; this->m_state = state; } void CWaterCreature::Allocate(CObject *pObj, float fFwdSpeed, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state) { CWaterCreature::Initialise(pObj, fFwdSpeed, fZTurnSpeed, fWaterDepth, alpha, state); } void CWaterCreature::Free() { CWaterCreature::Initialise(nil, 0.0f, 0.0f, 0.0f, 0, WATER_CREATURE_DISABLED); } CWaterCreature *CWaterCreatures::GetFishStructSlot() { for (int i = 0; i < NUM_WATER_CREATURES; i++) if (aWaterCreatures[i].m_state == WATER_CREATURE_DISABLED) return &aWaterCreatures[i]; return nil; } CObject *CWaterCreatures::CreateSeaLifeForm(CVector const& pos, int16 modelID, int32 zRotAngle) { if (CObject::nNoTempObjects >= NUMTEMPOBJECTS) return nil; CObject *pObj = new CObject(modelID, true); if (!pObj) return nil; pObj->SetPosition(pos); pObj->GetMatrix().UpdateRW(); pObj->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); pObj->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); pObj->GetMatrix().SetRotateZOnly(DEGTORAD(zRotAngle)); pObj->GetMatrix().UpdateRW(); pObj->ObjectCreatedBy = CONTROLLED_SUB_OBJECT; pObj->bIsStatic = false; if (pObj->ObjectCreatedBy == TEMP_OBJECT) { CObject::nNoTempObjects++; pObj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 60000; } pObj->bTouchingWater = true; pObj->bUnderwater = true; CWorld::Add(pObj); return pObj; } bool CWaterCreatures::IsSpaceForMoreWaterCreatures() { return nNumActiveSeaLifeForms < NUM_WATER_CREATURES; } float CWaterCreatures::CalculateFishHeading(CVector const& pos1, CVector const& pos2) { CVector delta = pos1 - pos2; delta.Normalise(); return CGeneral::GetRandomNumberInRange(-90, 90) + RADTODEG(delta.Heading() + HALFPI + PI); } void CWaterCreatures::CreateOne(CVector const& pos, int32 modelID) { if (!IsSpaceForMoreWaterCreatures()) return; CVector playerPos = FindPlayerPed()->GetPosition(); CVector fishPos = pos; float fDepth, fLevelNoWaves; if (!TheCamera.IsSphereVisible(fishPos, 3.0f) && CWaterLevel::GetWaterDepth(fishPos, &fDepth, &fLevelNoWaves, nil) && fDepth > 4.5f) { if (modelID == -1 || modelID < 0 || modelID > 64) modelID = CGeneral::GetRandomNumberInRange(0, 64); WaterCreatureProperties *creature = &aProperties[modelID]; fishPos.z = fLevelNoWaves - creature->fLevel; float fFwdSpeed = CGeneral::GetRandomNumberInRange(0.0f, creature->fFwdSpeed) + 0.01f; float angle = CWaterCreatures::CalculateFishHeading(playerPos, fishPos); CObject *fish = CreateSeaLifeForm(fishPos, *creature->modelID, angle); if (!fish) return; fish->SetRwObjectAlpha(255); CWaterCreature *wc = GetFishStructSlot(); wc->Allocate(fish, fFwdSpeed, 0.0f, creature->fWaterDepth, 255, WATER_CREATURE_INIT); nNumActiveSeaLifeForms++; } } void CWaterCreatures::FreeFishStructSlot(CWaterCreature *wc) { wc->Free(); } void CWaterCreatures::UpdateAll() { if (nNumActiveSeaLifeForms == 0) return; CVector playerPos = FindPlayerPed()->GetPosition(); for (int i = 0; i < NUM_WATER_CREATURES; i++) { switch (aWaterCreatures[i].m_state) { case WATER_CREATURE_ACTIVE: // is this even reachable? aWaterCreatures[i].m_pObj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 40000; if (!aWaterCreatures[i].m_pObj->GetIsOnScreen()) { aWaterCreatures[i].m_pObj->SetRwObjectAlpha(0); aWaterCreatures[i].m_state = WATER_CREATURE_REMOVE; break; } // fall through case WATER_CREATURE_INIT: { if ((playerPos - aWaterCreatures[i].m_pObj->GetPosition()).MagnitudeSqr() < SQR(75.0f)) { if (aWaterCreatures[i].m_alpha < 255) aWaterCreatures[i].m_alpha = Min(aWaterCreatures[i].m_alpha + 4, 255); aWaterCreatures[i].m_pObj->SetRwObjectAlpha(aWaterCreatures[i].m_alpha); CVector fwd = aWaterCreatures[i].m_pObj->GetRight(); // for some reason they used x for forward fwd.Normalise(); aWaterCreatures[i].m_pObj->m_vecMoveSpeed = fwd * aWaterCreatures[i].m_fFwdSpeed; aWaterCreatures[i].m_pObj->m_vecTurnSpeed = CVector(0.0f, 0.0f, aWaterCreatures[i].m_fZTurnSpeed); aWaterCreatures[i].m_pObj->bIsStatic = false; float fDepth = 0.0; CWaterLevel::GetWaterDepth(aWaterCreatures[i].m_pObj->GetPosition(), &fDepth, nil, nil); if (aWaterCreatures[i].m_fWaterDepth < fDepth) { // it looks like this can never be true initially, looks like a BUG if (aWaterCreatures[i].m_pObj->m_nEndOfLifeTime - 40000 <= CTimer::GetTimeInMilliseconds()) aWaterCreatures[i].m_state = WATER_CREATURE_ACTIVE; } else { // creature is deeper than water aWaterCreatures[i].m_state = WATER_CREATURE_FADE_OUT; } } else { aWaterCreatures[i].m_state = WATER_CREATURE_REMOVE; } break; } case WATER_CREATURE_FADE_OUT: { aWaterCreatures[i].m_pObj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 40000; if (aWaterCreatures[i].m_alpha <= 0) { aWaterCreatures[i].m_state = WATER_CREATURE_REMOVE; } else { aWaterCreatures[i].m_alpha = Max(aWaterCreatures[i].m_alpha - 6, 0); aWaterCreatures[i].m_pObj->SetRwObjectAlpha(aWaterCreatures[i].m_alpha); CVector speed = aWaterCreatures[i].m_pObj->GetRight(); speed.Normalise(); speed.x *= aWaterCreatures[i].m_fFwdSpeed; speed.y *= aWaterCreatures[i].m_fFwdSpeed; speed.z = -0.015f; aWaterCreatures[i].m_pObj->m_vecMoveSpeed = speed; if (!aWaterCreatures[i].m_pObj->GetIsOnScreen()) aWaterCreatures[i].m_state = WATER_CREATURE_REMOVE; } break; } case WATER_CREATURE_REMOVE: if (aWaterCreatures[i].m_pObj){ CWorld::Remove(aWaterCreatures[i].m_pObj); delete aWaterCreatures[i].m_pObj; } FreeFishStructSlot(&aWaterCreatures[i]); nNumActiveSeaLifeForms--; aWaterCreatures[i].m_state = WATER_CREATURE_DISABLED; break; default: break; } } } void CWaterCreatures::RemoveAll() { for (int i = 0; i < NUM_WATER_CREATURES; i++) { if (aWaterCreatures[i].m_state != WATER_CREATURE_DISABLED) { if (aWaterCreatures[i].m_pObj){ CWorld::Remove(aWaterCreatures[i].m_pObj); delete aWaterCreatures[i].m_pObj; } FreeFishStructSlot(&aWaterCreatures[i]); aWaterCreatures[i].m_state = WATER_CREATURE_DISABLED; nNumActiveSeaLifeForms--; } } } ================================================ FILE: src/render/WaterCreatures.h ================================================ #pragma once class CObject; enum eFishSlotState { WATER_CREATURE_INIT = 0, WATER_CREATURE_ACTIVE, WATER_CREATURE_FADE_OUT, WATER_CREATURE_REMOVE, WATER_CREATURE_DISABLED }; class CWaterCreature { public: CObject *m_pObj; float m_fFwdSpeed; float m_fZTurnSpeed; int32 m_alpha; float m_fWaterDepth; int32 m_state; CWaterCreature(); void Allocate(CObject *pObj, float fFwdSpeed, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state); void Free(); void Initialise(CObject *pObj, float fFwdSpeed, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state); }; class CWaterCreatures { public: static CWaterCreature aWaterCreatures[NUM_WATER_CREATURES]; static int32 nNumActiveSeaLifeForms; static CObject *CreateSeaLifeForm(CVector const& pos, int16 modelID, int32 zRotAngle); static void CreateOne(CVector const& pos, int32 modelID); static void UpdateAll(); static void FreeFishStructSlot(CWaterCreature *wc); static bool IsSpaceForMoreWaterCreatures(); static float CalculateFishHeading(CVector const& pos1, CVector const& pos2); static void RemoveAll(); static CWaterCreature* GetFishStructSlot(); }; struct WaterCreatureProperties { int16 *modelID; float fFwdSpeed; float fLevel; float fUnknown; //unused float fWaterDepth; }; ================================================ FILE: src/render/WaterLevel.cpp ================================================ #include "common.h" #include "main.h" #include "FileMgr.h" #include "FileLoader.h" #include "TxdStore.h" #include "Timer.h" #include "Weather.h" #include "Camera.h" #include "Vehicle.h" #include "PlayerPed.h" #include "Boat.h" #include "World.h" #include "General.h" #include "Timecycle.h" #include "ZoneCull.h" #include "Clock.h" #include "Particle.h" #include "ParticleMgr.h" #include "RwHelper.h" #include "Streaming.h" #include "ColStore.h" #include "CdStream.h" #include "Pad.h" #include "RenderBuffer.h" #include #include #include #include "Occlusion.h" #include "Replay.h" #include "WaterLevel.h" #include "SurfaceTable.h" #include "WaterCreatures.h" #define RwIm3DVertexSet_RGBA(vert, rgba) RwIm3DVertexSetRGBA(vert, rgba.red, rgba.green, rgba.blue, rgba.alpha) // (RwRGBAAssign(&(_dst)->color, &_src)) float TEXTURE_ADDU; float TEXTURE_ADDV; float _TEXTURE_MASK_ADDU; float _TEXTURE_MASK_ADDV; float _TEXTURE_WAKE_ADDU; float _TEXTURE_WAKE_ADDV; int32 CWaterLevel::ms_nNoOfWaterLevels; float CWaterLevel::ms_aWaterZs[48]; CRect CWaterLevel::ms_aWaterRects[48]; int8 CWaterLevel::aWaterBlockList[MAX_LARGE_SECTORS][MAX_LARGE_SECTORS]; int8 CWaterLevel::aWaterFineBlockList[MAX_SMALL_SECTORS][MAX_SMALL_SECTORS]; bool CWaterLevel::WavesCalculatedThisFrame; bool CWaterLevel::RequireWavySector; bool CWaterLevel::MaskCalculatedThisFrame; CVector CWaterLevel::PreCalculatedMaskPosn; bool CWaterLevel::m_bRenderSeaBed; int32 CWaterLevel::m_nRenderWaterLayers; RpAtomic *CWaterLevel::ms_pWavyAtomic; RpAtomic *CWaterLevel::ms_pMaskAtomic; //"Custom" Don't Render Water Toggle bool gbDontRenderWater; RwTexture *gpWaterTex; RwTexture *gpWaterEnvTex; RwTexture *gpWaterEnvBaseTex; RwTexture *gpWaterWakeTex; RwRaster *gpWaterRaster; RwRaster *gpWaterEnvRaster; RwRaster *gpWaterEnvBaseRaster; RwRaster *gpWaterWakeRaster; bool _bSeaLife; float _fWaterZOffset = WATER_Z_OFFSET; #ifdef PC_WATER float fEnvScale = 0.25f; #else float fEnvScale = 0.5f; #endif float fWave2InvLength = 0.03f; float fWave2NormScale = 0.5f; float fWave2Ampl = 0.1f; uint8 nWaterAlpha = 192; uint8 nWakeAlpha = 192; float fUnder1 = 4.0; float fUnder2 = 2.5; float fUnder3 = 1.5; int nMaskAlpha = 230; float fAdd1 = 180.0f; float fAdd2 = 80.0; float fRedMult = 0.6f; float fGreenMult = 1.0f; float fBlueMult = 1.4f; float fAlphaMult = 500.0f; float fAlphaBase = 30.0f; float fRandomMoveDiv = 8.0f; float fRandomDamp = 0.99f; float fNormMult = 2.0f; float fNormMultB = 1.0f; float fBumpScale = 1.5; float fBumpTexRepeat = 2.0; float fNormalDirectionScalar1 = 2.0f; float fNormalDirectionScalar2 = 1.0f; bool bTestDoNormals = true; float fSeaBedZ = 25.0f; float aAlphaFade[5] = { 0.4f, 1.0f, 0.2f, 1.0f, 0.4f}; //CWaterLevel::RenderWakeSegment float fFlatWaterBlendRange = 0.05f; float fStartBlendDistanceAdd = 64.0f; float fMinWaterAlphaMult = -30.0f; void CWaterLevel::Initialise(Const char *pWaterDat) { ms_nNoOfWaterLevels = 0; #ifdef MASTER int32 hFile = -1; do { hFile = CFileMgr::OpenFile("DATA\\waterpro.dat", "rb"); } while ( hFile < 0 ); #else int32 hFile = CFileMgr::OpenFile("DATA\\waterpro.dat", "rb"); #endif if (hFile > 0) { CFileMgr::Read(hFile, (char *)&ms_nNoOfWaterLevels, sizeof(ms_nNoOfWaterLevels)); CFileMgr::Read(hFile, (char *)ms_aWaterZs, sizeof(ms_aWaterZs)); CFileMgr::Read(hFile, (char *)ms_aWaterRects, sizeof(ms_aWaterRects)); CFileMgr::Read(hFile, (char *)aWaterBlockList, sizeof(aWaterBlockList)); CFileMgr::Read(hFile, (char *)aWaterFineBlockList, sizeof(aWaterFineBlockList)); CFileMgr::CloseFile(hFile); } #ifndef MASTER else { printf("Init waterlevels\n"); // collision is streamed in VC CColStore::LoadAllCollision(); CFileMgr::SetDir(""); hFile = CFileMgr::OpenFile(pWaterDat, "r"); char *line; while ((line = CFileLoader::LoadLine(hFile))) { if (*line && *line != ';' && !strstr(line, "* ;end of file")) { float z, l, b, r, t; sscanf(line, "%f %f %f %f %f", &z, &l, &b, &r, &t); AddWaterLevel(l, b, r, t, z); } } CFileMgr::CloseFile(hFile); for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) { for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) { aWaterFineBlockList[x][y] = NO_WATER; } } // rasterize water rects read from file for (int32 i = 0; i < ms_nNoOfWaterLevels; i++) { int32 l = WATER_HUGE_X(ms_aWaterRects[i].left + WATER_X_OFFSET); int32 r = WATER_HUGE_X(ms_aWaterRects[i].right + WATER_X_OFFSET) + 1.0f; int32 t = WATER_HUGE_Y(ms_aWaterRects[i].top); int32 b = WATER_HUGE_Y(ms_aWaterRects[i].bottom) + 1.0f; l = clamp(l, 0, MAX_SMALL_SECTORS - 1); r = clamp(r, 0, MAX_SMALL_SECTORS - 1); t = clamp(t, 0, MAX_SMALL_SECTORS - 1); b = clamp(b, 0, MAX_SMALL_SECTORS - 1); for (int32 x = l; x <= r; x++) { for (int32 y = t; y <= b; y++) { aWaterFineBlockList[x][y] = i; } } } // remove tiles that are obscured by land for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) { float worldX = WATER_START_X + x * SMALL_SECTOR_SIZE - WATER_X_OFFSET; for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) { if (CWaterLevel::aWaterFineBlockList[x][y] >= 0) { float worldY = WATER_START_Y + y * SMALL_SECTOR_SIZE; int32 i; for (i = 0; i <= 8; i++) { for (int32 j = 0; j <= 8; j++) { CVector worldPos = CVector(worldX + i * (SMALL_SECTOR_SIZE / 8), worldY + j * (SMALL_SECTOR_SIZE / 8), ms_aWaterZs[aWaterFineBlockList[x][y]]); if ((worldPos.x > WORLD_MIN_X && worldPos.x < WORLD_MAX_X) && (worldPos.y > WORLD_MIN_Y && worldPos.y < WORLD_MAX_Y) && (!WaterLevelAccordingToRectangles(worldPos.x, worldPos.y) || TestVisibilityForFineWaterBlocks(worldPos))) continue; // at least one point in the tile wasn't blocked, so don't remove water i = 1000; break; } } if (i < 1000) aWaterFineBlockList[x][y] = NO_WATER; } } } RemoveIsolatedWater(); // calculate coarse tiles from fine tiles for (int32 x = 0; x < MAX_LARGE_SECTORS; x++) { for (int32 y = 0; y < MAX_LARGE_SECTORS; y++) { if (aWaterFineBlockList[x * 2][y * 2] >= 0) { aWaterBlockList[x][y] = aWaterFineBlockList[x * 2][y * 2]; } else if (aWaterFineBlockList[x * 2 + 1][y * 2] >= 0) { aWaterBlockList[x][y] = aWaterFineBlockList[x * 2 + 1][y * 2]; } else if (aWaterFineBlockList[x * 2][y * 2 + 1] >= 0) { aWaterBlockList[x][y] = aWaterFineBlockList[x * 2][y * 2 + 1]; } else if (aWaterFineBlockList[x * 2 + 1][y * 2 + 1] >= 0) { aWaterBlockList[x][y] = aWaterFineBlockList[x * 2 + 1][y * 2 + 1]; } else { aWaterBlockList[x][y] = NO_WATER; } } } hFile = CFileMgr::OpenFileForWriting("data\\waterpro.dat"); if (hFile > 0) { CFileMgr::Write(hFile, (char *)&ms_nNoOfWaterLevels, sizeof(ms_nNoOfWaterLevels)); CFileMgr::Write(hFile, (char *)ms_aWaterZs, sizeof(ms_aWaterZs)); CFileMgr::Write(hFile, (char *)ms_aWaterRects, sizeof(ms_aWaterRects)); CFileMgr::Write(hFile, (char *)aWaterBlockList, sizeof(aWaterBlockList)); CFileMgr::Write(hFile, (char *)aWaterFineBlockList, sizeof(aWaterFineBlockList)); CFileMgr::CloseFile(hFile); } // collision is streamed in VC CColStore::RemoveAllCollision(); } #endif CTxdStore::PushCurrentTxd(); int32 slot = CTxdStore::FindTxdSlot("particle"); CTxdStore::SetCurrentTxd(slot); if ( gpWaterTex == nil ) gpWaterTex = RwTextureRead("waterclear256", nil); gpWaterRaster = RwTextureGetRaster(gpWaterTex); if ( gpWaterEnvTex == nil ) gpWaterEnvTex = RwTextureRead("waterreflection2", nil); gpWaterEnvRaster = RwTextureGetRaster(gpWaterEnvTex); #ifdef PC_WATER if ( gpWaterEnvBaseTex == nil ) gpWaterEnvBaseTex = RwTextureRead("sandywater", nil); gpWaterEnvBaseRaster = RwTextureGetRaster(gpWaterEnvBaseTex); #endif if ( gpWaterWakeTex == nil ) gpWaterWakeTex = RwTextureRead("waterwake", nil); gpWaterWakeRaster = RwTextureGetRaster(gpWaterWakeTex); CTxdStore::PopCurrentTxd(); CreateWavyAtomic(); printf("Done Initing waterlevels\n"); } void CWaterLevel::Shutdown() { DestroyWavyAtomic(); #define _DELETE_TEXTURE(t) if ( t ) \ { \ RwTextureDestroy(t); \ t = nil; \ } _DELETE_TEXTURE(gpWaterTex); _DELETE_TEXTURE(gpWaterEnvTex); _DELETE_TEXTURE(gpWaterWakeTex); _DELETE_TEXTURE(gpWaterEnvBaseTex); #undef _DELETE_TEXTURE } void CWaterLevel::CreateWavyAtomic() { RpGeometry *wavyGeometry; RpGeometry *maskGeometry; RpMaterial *wavyMaterial; RpMaterial *maskMaterial; RpTriangle *wavytlist; RpTriangle *masktlist; RpMorphTarget *wavyMorphTarget; RpMorphTarget *maskMorphTarget; RwSphere boundingSphere; RwV3d *wavyVert; RwV3d *wavyNormal; RwV3d *maskVert; RwV3d *maskNormal; RwFrame *wavyFrame; RwFrame *maskFrame; { wavyGeometry = RpGeometryCreate(17*17, 512, rpGEOMETRYTRISTRIP |rpGEOMETRYTEXTURED |rpGEOMETRYPRELIT |rpGEOMETRYNORMALS |rpGEOMETRYMODULATEMATERIALCOLOR); #ifdef PC_WATER RpGeometryAddMorphTarget(wavyGeometry); #endif } { maskGeometry = RpGeometryCreate(33*33, 2048, rpGEOMETRYTRISTRIP |rpGEOMETRYTEXTURED |rpGEOMETRYPRELIT |rpGEOMETRYNORMALS |rpGEOMETRYMODULATEMATERIALCOLOR); #ifdef PC_WATER RpGeometryAddMorphTarget(maskGeometry); #endif } { wavyMaterial = RpMaterialCreate(); RpMaterialSetTexture(wavyMaterial, gpWaterTex); RwRGBA watercolor = { 255, 255, 255, 192 }; RpMaterialSetColor(wavyMaterial, &watercolor); } { maskMaterial = RpMaterialCreate(); #ifdef PC_WATER RpMaterialSetTexture(maskMaterial, gpWaterEnvBaseTex); #else RpMaterialSetTexture(maskMaterial, gpWaterTex); #endif RwRGBA watercolor = { 255, 255, 255, 192 }; RpMaterialSetColor(maskMaterial, &watercolor); } { wavytlist = RpGeometryGetTriangles(wavyGeometry); for ( int32 i = 0; i < 16; i++ ) { for ( int32 j = 0; j < 16; j++ ) { const RwUInt16 base = (RwUInt16)((16 + 1)*i+j); RpGeometryTriangleSetVertexIndices(wavyGeometry, wavytlist, (RwInt16)base, (RwInt16)(base+1), (RwInt16)(base+16+2)); RpGeometryTriangleSetVertexIndices(wavyGeometry, (wavytlist+1), (RwInt16)base, (RwInt16)(base+16+2), (RwInt16)(base+16+1)); RpGeometryTriangleSetMaterial(wavyGeometry, wavytlist, wavyMaterial); RpGeometryTriangleSetMaterial(wavyGeometry, (wavytlist+1), wavyMaterial); wavytlist+=2; } } } { masktlist = RpGeometryGetTriangles(maskGeometry); for ( int32 i = 0; i < 32; i++ ) { for ( int32 j = 0; j < 32; j++ ) { const RwUInt16 base = (RwUInt16)((32 + 1)*i+j); RpGeometryTriangleSetVertexIndices(maskGeometry, masktlist, (RwInt16)base, (RwInt16)(base+1), (RwInt16)(base+32+2)); RpGeometryTriangleSetVertexIndices(maskGeometry, (masktlist+1), (RwInt16)base, (RwInt16)(base+32+2), (RwInt16)(base+32+1)); RpGeometryTriangleSetMaterial(maskGeometry, masktlist, maskMaterial); RpGeometryTriangleSetMaterial(maskGeometry, (masktlist+1), maskMaterial); masktlist+=2; } } } { wavyMorphTarget = RpGeometryGetMorphTarget(wavyGeometry, 0); wavyVert = RpMorphTargetGetVertices(wavyMorphTarget); wavyNormal = RpMorphTargetGetVertexNormals(wavyMorphTarget); for ( int32 i = 0; i < 17; i++ ) { for ( int32 j = 0; j < 17; j++ ) { (*wavyVert).x = (float)i * 2.0f; (*wavyVert).y = (float)j * 2.0f; (*wavyVert).z = 0.0f; (*wavyNormal).x = 0.0f; (*wavyNormal).y = 0.0f; (*wavyNormal).z = 1.0f; wavyVert++; wavyNormal++; } } RpMorphTargetCalcBoundingSphere(wavyMorphTarget, &boundingSphere); RpMorphTargetSetBoundingSphere(wavyMorphTarget, &boundingSphere); RpGeometryUnlock(wavyGeometry); } { maskMorphTarget = RpGeometryGetMorphTarget(maskGeometry, 0); maskVert = RpMorphTargetGetVertices(maskMorphTarget); maskNormal = RpMorphTargetGetVertexNormals(maskMorphTarget); for ( int32 i = 0; i < 33; i++ ) { for ( int32 j = 0; j < 33; j++ ) { (*maskVert).x = (float)i * 2.0f; (*maskVert).y = (float)j * 2.0f; (*maskVert).z = 0.0f; (*maskNormal).x = 0.0f; (*maskNormal).y = 0.0f; (*maskNormal).z = 1.0f; maskVert++; maskNormal++; } } RpMorphTargetCalcBoundingSphere(maskMorphTarget, &boundingSphere); RpMorphTargetSetBoundingSphere(maskMorphTarget, &boundingSphere); RpGeometryUnlock(maskGeometry); } { wavyFrame = RwFrameCreate(); ms_pWavyAtomic = RpAtomicCreate(); RpAtomicSetGeometry(ms_pWavyAtomic, wavyGeometry, 0); RpAtomicSetFrame(ms_pWavyAtomic, wavyFrame); RpMaterialDestroy(wavyMaterial); RpGeometryDestroy(wavyGeometry); } { maskFrame = RwFrameCreate(); ms_pMaskAtomic = RpAtomicCreate(); RpAtomicSetGeometry(ms_pMaskAtomic, maskGeometry, 0); RpAtomicSetFrame(ms_pMaskAtomic, maskFrame); RpMaterialDestroy(maskMaterial); RpGeometryDestroy(maskGeometry); } static RwFrame *wakeEnvFrame; if ( wakeEnvFrame == nil ) { wakeEnvFrame = RwFrameCreate(); RwMatrixSetIdentity(RwFrameGetMatrix(wakeEnvFrame)); RwFrameUpdateObjects(wakeEnvFrame); } RpMatFXMaterialSetEffects(maskMaterial, rpMATFXEFFECTENVMAP); RpMatFXMaterialSetupEnvMap(maskMaterial, gpWaterEnvTex, wakeEnvFrame, TRUE, fEnvScale); RpMatFXAtomicEnableEffects(ms_pMaskAtomic); } void CWaterLevel::DestroyWavyAtomic() { #define _DELETE_ATOMIC(a) \ { \ RwFrame *frame; \ frame = RpAtomicGetFrame(a); \ RpAtomicDestroy(a); \ RwFrameDestroy(frame); \ } _DELETE_ATOMIC(ms_pWavyAtomic); _DELETE_ATOMIC(ms_pMaskAtomic); #undef _DELETE_ATOMIC } #ifndef MASTER void CWaterLevel::AddWaterLevel(float fXLeft, float fYBottom, float fXRight, float fYTop, float fLevel) { ms_aWaterRects[ms_nNoOfWaterLevels] = CRect(fXLeft, fYBottom, fXRight, fYTop); ms_aWaterZs[ms_nNoOfWaterLevels] = fLevel; ms_nNoOfWaterLevels++; } bool CWaterLevel::WaterLevelAccordingToRectangles(float fX, float fY, float *pfOutLevel) { if (ms_nNoOfWaterLevels <= 0) return false; for (int32 i = 0; i < ms_nNoOfWaterLevels; i++) { if (fX >= ms_aWaterRects[i].left && fX <= ms_aWaterRects[i].right && fY >= ms_aWaterRects[i].top && fY <= ms_aWaterRects[i].bottom) { if (pfOutLevel) *pfOutLevel = ms_aWaterZs[i]; return true; } } return false; } bool CWaterLevel::TestVisibilityForFineWaterBlocks(const CVector &worldPos) { static CVector2D tab[] = { { 50.0f, 50.0f }, { -50.0f, 50.0f }, { -50.0f, -50.0f }, { 50.0f, -50.0f }, { 50.0f, 0.0f }, { -50.0f, 0.0f }, { 0.0f, -50.0f }, { 0.0f, 50.0f }, }; CEntity *entity; CColPoint col; CVector lineStart, lineEnd; lineStart = worldPos; if (!CWorld::ProcessVerticalLine(lineStart, lineStart.z + 100.0f, col, entity, true, false, false, false, true, false, nil)) { lineStart.x += 0.4f; lineStart.y += 0.4f; if (!CWorld::ProcessVerticalLine(lineStart, lineStart.z + 100.0f, col, entity, true, false, false, false, true, false, nil)) { return false; } } for (int32 i = 0; i < ARRAY_SIZE(tab); i++) { lineStart = worldPos; lineEnd = worldPos; lineEnd.x += tab[i].x; lineEnd.y += tab[i].y; lineEnd.z += 100.0f; if ((lineEnd.x > WORLD_MIN_X && lineEnd.x < WORLD_MAX_X) && (lineEnd.y > WORLD_MIN_Y && lineEnd.y < WORLD_MAX_Y)) { if (!CWorld::ProcessLineOfSight(lineStart, lineEnd, col, entity, true, false, false, false, true, false)) { lineStart.x += 0.4f; lineStart.y += 0.4f; lineEnd.x += 0.4f; lineEnd.y += 0.4f; if (!CWorld::ProcessLineOfSight(lineStart, lineEnd, col, entity, true, false, false, false, true, false)) { return false; } } } } return true; } void CWaterLevel::RemoveIsolatedWater() { bool (*isConnected)[MAX_SMALL_SECTORS] = new bool[MAX_SMALL_SECTORS][MAX_SMALL_SECTORS]; for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) { for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) { isConnected[x][y] = false; } } isConnected[0][0] = true; bool keepGoing; do { keepGoing = false; for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) { for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) { if (aWaterFineBlockList[x][y] < 0 || isConnected[x][y]) continue; if (x > 0 && isConnected[x - 1][y]) { isConnected[x][y] = true; keepGoing = true; } if (y > 0 && isConnected[x][y - 1]) { isConnected[x][y] = true; keepGoing = true; } if (x + 1 < MAX_SMALL_SECTORS && isConnected[x + 1][y]) { isConnected[x][y] = true; keepGoing = true; } if (y + 1 < MAX_SMALL_SECTORS && isConnected[x][y + 1]) { isConnected[x][y] = true; keepGoing = true; } } } } while (keepGoing); int32 numRemoved = 0; for (int32 x = 0; x < MAX_SMALL_SECTORS; x++) { for (int32 y = 0; y < MAX_SMALL_SECTORS; y++) { if (aWaterFineBlockList[x][y] >= 0 && !isConnected[x][y] && ms_aWaterZs[aWaterFineBlockList[x][y]] == 6.0f) { numRemoved++; aWaterFineBlockList[x][y] = NO_WATER; } } } printf("Removed %d isolated patches of water\n", numRemoved); delete[] isConnected; } #endif bool CWaterLevel::GetWaterLevel(float fX, float fY, float fZ, float *pfOutLevel, bool bDontCheckZ) { int32 x = WATER_TO_SMALL_SECTOR_X(fX + WATER_X_OFFSET); int32 y = WATER_TO_SMALL_SECTOR_Y(fY); #ifdef FIX_BUGS if ( x < 0 || x >= MAX_SMALL_SECTORS ) return false; if ( y < 0 || y >= MAX_SMALL_SECTORS ) return false; #endif int8 nBlock = aWaterFineBlockList[x][y]; if ( nBlock == NO_WATER ) return false; ASSERT( pfOutLevel != nil ); *pfOutLevel = ms_aWaterZs[nBlock]; float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); float fWave = Sin ( ( WATER_UNSIGN_Y(fY) - y*SMALL_SECTOR_SIZE + WATER_UNSIGN_X(fX + WATER_X_OFFSET) - x*SMALL_SECTOR_SIZE ) * (TWOPI / SMALL_SECTOR_SIZE ) + fAngle ); float fWindFactor = CWeather::WindClipped * 0.4f + 0.2f; *pfOutLevel += fWave * fWindFactor; if ( bDontCheckZ == false && (*pfOutLevel - fZ) > 3.0f ) { *pfOutLevel = 0.0f; return false; } return true; } bool CWaterLevel::GetWaterLevelNoWaves(float fX, float fY, float fZ, float *pfOutLevel) { int32 x = WATER_TO_SMALL_SECTOR_X(fX + WATER_X_OFFSET); int32 y = WATER_TO_SMALL_SECTOR_Y(fY); #ifdef FIX_BUGS if ( x < 0 || x >= MAX_SMALL_SECTORS ) return false; if ( y < 0 || y >= MAX_SMALL_SECTORS ) return false; #endif int8 nBlock = aWaterFineBlockList[x][y]; if ( nBlock == NO_WATER ) return false; ASSERT( pfOutLevel != nil ); *pfOutLevel = ms_aWaterZs[nBlock]; return true; } float CWaterLevel::GetWaterWavesOnly(short x, short y) { float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); float fWindFactor = CWeather::WindClipped * 0.7f + 0.3f; float fWave = Sin( float(float(4 * y + 4 * x) * (TWOPI / SMALL_SECTOR_SIZE )) + fAngle ); return fWave * fWindFactor; } CVector CWaterLevel::GetWaterNormal(float fX, float fY) { //TODO: BUG ? no x offset int32 x = WATER_TO_SMALL_SECTOR_X(fX); int32 y = WATER_TO_SMALL_SECTOR_Y(fY); float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); float fWindFactor = CWeather::WindClipped * 0.4f + 0.2f; float _fWave = (WATER_UNSIGN_Y(fY) - y*SMALL_SECTOR_SIZE + WATER_UNSIGN_X(fX) - x*SMALL_SECTOR_SIZE) * (TWOPI / SMALL_SECTOR_SIZE ) + fAngle; CVector vA(1.0f, 0.0f, fWindFactor * (TWOPI / SMALL_SECTOR_SIZE ) * Cos(_fWave)); CVector vB(0.0f, 1.0f, fWindFactor * (TWOPI / SMALL_SECTOR_SIZE ) * Cos(_fWave)); CVector norm = CrossProduct(vA, vB); norm.Normalise(); return norm; } inline float _GetWaterDrawDist() { if ( TheCamera.GetPosition().z < 15.0f ) return 1200.0f; if ( TheCamera.GetPosition().z > 60.0f ) return 2000.0f; return ( TheCamera.GetPosition().z + -15.0f ) * 800.0f / 45.0f + 1200.0f; } inline float _GetWavyDrawDist() { if ( FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() ) return 120.0f; else return 70.0f; } inline void _GetCamBounds(bool *bUseCamStartY, bool *bUseCamEndY, bool *bUseCamStartX, bool *bUseCamEndX) { if ( TheCamera.GetForward().z > -0.8f ) { if ( Abs(TheCamera.GetForward().x) > Abs(TheCamera.GetForward().y) ) { if ( TheCamera.GetForward().x > 0.0f ) *bUseCamStartX = true; else *bUseCamEndX = true; } else { if ( TheCamera.GetForward().y > 0.0f ) *bUseCamStartY = true; else *bUseCamEndY = true; } } } inline bool _IsColideWithBlock(int32 x, int32 y, int32 &block) { block = CWaterLevel::aWaterFineBlockList[x + 0][y + 0]; if (block >= 0) return true; block = CWaterLevel::aWaterFineBlockList[x + 0][y + 1]; if (block >= 0) { block = CWaterLevel::aWaterFineBlockList[x + 0][y + 2]; if (block >= 0) return true; } block = CWaterLevel::aWaterFineBlockList[x + 1][y + 0]; if (block >= 0) return true; block = CWaterLevel::aWaterFineBlockList[x + 1][y + 1]; if (block >= 0) { block = CWaterLevel::aWaterFineBlockList[x + 1][y + 2]; if (block >= 0) return true; } block = CWaterLevel::aWaterFineBlockList[x + 2][y + 0]; if (block >= 0) return true; block = CWaterLevel::aWaterFineBlockList[x + 2][y + 1]; if (block >= 0) { block = CWaterLevel::aWaterFineBlockList[x + 2][y + 2]; if (block >= 0) return true; } return false; } inline float SectorRadius(float fSize) { return Sqrt(Pow(fSize, 2) + Pow(fSize, 2)); } void CWaterLevel::RenderWater() { //"Custom" Don't Render Water Toggle #ifndef MASTER if (gbDontRenderWater) return; #endif bool bUseCamEndX = false; bool bUseCamStartY = false; bool bUseCamStartX = false; bool bUseCamEndY = false; if ( !CGame::CanSeeWaterFromCurrArea() ) return; _GetCamBounds(&bUseCamStartY, &bUseCamEndY, &bUseCamStartX, &bUseCamEndX); float fHugeSectorMaxRenderDist = _GetWaterDrawDist(); float fHugeSectorMaxRenderDistSqr = SQR(fHugeSectorMaxRenderDist); float windAddUV = CWeather::WindClipped * 0.0005f + 0.0006f; float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); if ( !CTimer::GetIsPaused() ) { TEXTURE_ADDU += windAddUV; TEXTURE_ADDV += windAddUV; _TEXTURE_MASK_ADDU += Sin(fAngle) * 0.0005f + 1.1f * windAddUV; _TEXTURE_MASK_ADDV -= Cos(fAngle * 1.3f) * 0.0005f + 1.2f * windAddUV; _TEXTURE_WAKE_ADDU -= Sin(fAngle) * 0.0003f + windAddUV; _TEXTURE_WAKE_ADDV += Cos(fAngle * 0.7f) * 0.0003f + windAddUV; } if ( _TEXTURE_MASK_ADDU >= 1.0f ) _TEXTURE_MASK_ADDU = 0.0f; if ( _TEXTURE_MASK_ADDV >= 1.0f ) _TEXTURE_MASK_ADDV = 0.0f; if ( _TEXTURE_WAKE_ADDU >= 1.0f ) _TEXTURE_WAKE_ADDU = 0.0f; if ( _TEXTURE_WAKE_ADDV >= 1.0f ) _TEXTURE_WAKE_ADDV = 0.0f; if ( TEXTURE_ADDU >= 1.0f ) TEXTURE_ADDU = 0.0f; if ( TEXTURE_ADDV >= 1.0f ) TEXTURE_ADDV = 0.0f; #ifdef PC_WATER _fWaterZOffset = CWeather::WindClipped * 0.5f + 0.25f; #endif RwRGBA color = { 0, 0, 0, 255 }; color.red = CTimeCycle::GetWaterRed(); color.green = CTimeCycle::GetWaterGreen(); color.blue = CTimeCycle::GetWaterBlue(); #ifndef PC_WATER RwRGBA colorUnderwater = { 0, 0, 0, 255 }; colorUnderwater.red = (uint32)(0.8f * (float)colorUnderwater.red); colorUnderwater.green = (uint32)(0.8f * (float)colorUnderwater.green); colorUnderwater.blue = (uint32)(0.8f * (float)colorUnderwater.blue); #endif TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; #ifndef PC_WATER WavesCalculatedThisFrame = false; #endif RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)gpWaterRaster); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDZERO); CVector2D camPos(TheCamera.GetPosition().x, TheCamera.GetPosition().y); int32 nStartX = WATER_TO_HUGE_SECTOR_X(camPos.x - fHugeSectorMaxRenderDist + WATER_X_OFFSET); int32 nEndX = WATER_TO_HUGE_SECTOR_X(camPos.x + fHugeSectorMaxRenderDist + WATER_X_OFFSET) + 1; int32 nStartY = WATER_TO_HUGE_SECTOR_Y(camPos.y - fHugeSectorMaxRenderDist); int32 nEndY = WATER_TO_HUGE_SECTOR_Y(camPos.y + fHugeSectorMaxRenderDist) + 1; if ( bUseCamStartX ) nStartX = WATER_TO_HUGE_SECTOR_X(camPos.x + WATER_X_OFFSET); if ( bUseCamEndX ) nEndX = WATER_TO_HUGE_SECTOR_X(camPos.x + WATER_X_OFFSET); if ( bUseCamStartY ) nStartY = WATER_TO_HUGE_SECTOR_Y(camPos.y); if ( bUseCamEndY ) nEndY = WATER_TO_HUGE_SECTOR_Y(camPos.y); nStartX = clamp(nStartX, 0, MAX_HUGE_SECTORS - 1); nEndX = clamp(nEndX, 0, MAX_HUGE_SECTORS - 1); nStartY = clamp(nStartY, 0, MAX_HUGE_SECTORS - 1); nEndY = clamp(nEndY, 0, MAX_HUGE_SECTORS - 1); for ( int32 x = nStartX; x <= nEndX; x++ ) { for ( int32 y = nStartY; y <= nEndY; y++ ) { if ( aWaterBlockList[2*x+0][2*y+0] >= 0 || aWaterBlockList[2*x+1][2*y+0] >= 0 || aWaterBlockList[2*x+0][2*y+1] >= 0 || aWaterBlockList[2*x+1][2*y+1] >= 0 ) { float fX = WATER_FROM_HUGE_SECTOR_X(x) - WATER_X_OFFSET; float fY = WATER_FROM_HUGE_SECTOR_Y(y); CVector2D vecHugeSectorCentre(fX + HUGE_SECTOR_SIZE/2,fY + HUGE_SECTOR_SIZE/2); float fHugeSectorDistToCamSqr = (camPos - vecHugeSectorCentre).MagnitudeSqr(); if ( fHugeSectorMaxRenderDistSqr > fHugeSectorDistToCamSqr ) { if ( TheCamera.IsSphereVisible(CVector(vecHugeSectorCentre.x, vecHugeSectorCentre.y, 0.0f), SectorRadius(HUGE_SECTOR_SIZE), &TheCamera.GetCameraMatrix()) ) { #ifndef PC_WATER WavesCalculatedThisFrame = true; #endif float fZ; if ( aWaterBlockList[2*x+0][2*y+0] >= 0 ) fZ = ms_aWaterZs[ aWaterBlockList[2*x+0][2*y+0] ]; if ( aWaterBlockList[2*x+1][2*y+0] >= 0 ) fZ = ms_aWaterZs[ aWaterBlockList[2*x+1][2*y+0] ]; if ( aWaterBlockList[2*x+0][2*y+1] >= 0 ) fZ = ms_aWaterZs[ aWaterBlockList[2*x+0][2*y+1] ]; if ( aWaterBlockList[2*x+1][2*y+1] >= 0 ) fZ = ms_aWaterZs[ aWaterBlockList[2*x+1][2*y+1] ]; if ( fHugeSectorDistToCamSqr >= SQR(500.0f) ) { RenderOneFlatHugeWaterPoly(fX, fY, fZ, color); } else { #ifndef PC_WATER if (m_bRenderSeaBed) RenderOneSlopedUnderWaterPoly(fX, fY, fZ, colorUnderwater); #endif // see RenderTransparentWater() ; } } } } } } /* ----------- ---------------------- ---------------------- | [N] | | [ EndY ] | | [ top ] | | | | | | | |[W] [0] [E]| |[StartX] [] [ EndX ]| |[ left ] [] [ right]| | | | | | | | [S] | | [StartY] | | [bottom] | ----------- ---------------------- ---------------------- [S] [StartY] [bottom] [N] [EndY] [top] [W] [StartX] [left] [E] [EndX] [right] [S] -> [N] && [W] -> [E] bottom -> top && left -> right */ for ( int32 x = 0; x < 26; x++ ) { for ( int32 y = 0; y < 5; y++ ) { float fX = WATER_SIGN_X(float(x) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f - WATER_X_OFFSET; float fY = WATER_SIGN_Y(float(y) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f; if ( !bUseCamStartY ) { CVector2D vecExtraHugeSectorCentre(fX + EXTRAHUGE_SECTOR_SIZE/2, fY + EXTRAHUGE_SECTOR_SIZE/2); float fCamDistToSector = (vecExtraHugeSectorCentre - camPos).Magnitude(); if ( fCamDistToSector < fHugeSectorMaxRenderDistSqr ) { if ( TheCamera.IsSphereVisible(CVector(vecExtraHugeSectorCentre.x, vecExtraHugeSectorCentre.y, 0.0f), SectorRadius(EXTRAHUGE_SECTOR_SIZE), &TheCamera.GetCameraMatrix()) ) { RenderOneFlatExtraHugeWaterPoly( vecExtraHugeSectorCentre.x - EXTRAHUGE_SECTOR_SIZE/2, vecExtraHugeSectorCentre.y - EXTRAHUGE_SECTOR_SIZE/2, 0.0f, color); } } } if ( !bUseCamEndY ) { CVector2D vecExtraHugeSectorCentre(fX + EXTRAHUGE_SECTOR_SIZE/2, -(fY + EXTRAHUGE_SECTOR_SIZE/2)); float fCamDistToSector = (vecExtraHugeSectorCentre - camPos).Magnitude(); if ( fCamDistToSector < fHugeSectorMaxRenderDistSqr ) { if ( TheCamera.IsSphereVisible(CVector(vecExtraHugeSectorCentre.x, vecExtraHugeSectorCentre.y, 0.0f), SectorRadius(EXTRAHUGE_SECTOR_SIZE), &TheCamera.GetCameraMatrix()) ) { RenderOneFlatExtraHugeWaterPoly( vecExtraHugeSectorCentre.x - EXTRAHUGE_SECTOR_SIZE/2, vecExtraHugeSectorCentre.y - EXTRAHUGE_SECTOR_SIZE/2, 0.0f, color); } } } } } for ( int32 y = 5; y < 21; y++ ) { for ( int32 x = 0; x < 5; x++ ) { float fX = WATER_SIGN_X(float(x) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f - WATER_X_OFFSET; float fX2 = WATER_SIGN_X(float(x) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f + WATER_X_OFFSET; float fY = WATER_SIGN_Y(float(y) * EXTRAHUGE_SECTOR_SIZE) - 1280.0f; if ( !bUseCamStartX ) { CVector2D vecExtraHugeSectorCentre(fX + EXTRAHUGE_SECTOR_SIZE/2, fY + EXTRAHUGE_SECTOR_SIZE/2); float fCamDistToSector = (vecExtraHugeSectorCentre - camPos).Magnitude(); if ( fCamDistToSector < fHugeSectorMaxRenderDistSqr ) { if ( TheCamera.IsSphereVisible(CVector(vecExtraHugeSectorCentre.x, vecExtraHugeSectorCentre.y, 0.0f), SectorRadius(EXTRAHUGE_SECTOR_SIZE), &TheCamera.GetCameraMatrix()) ) { RenderOneFlatExtraHugeWaterPoly( vecExtraHugeSectorCentre.x - EXTRAHUGE_SECTOR_SIZE/2, vecExtraHugeSectorCentre.y - EXTRAHUGE_SECTOR_SIZE/2, 0.0f, color); } } } if ( !bUseCamEndX ) { CVector2D vecExtraHugeSectorCentre(-(fX2 + EXTRAHUGE_SECTOR_SIZE/2), fY + EXTRAHUGE_SECTOR_SIZE/2); float fCamDistToSector = (vecExtraHugeSectorCentre - camPos).Magnitude(); if ( fCamDistToSector < fHugeSectorMaxRenderDistSqr ) { if ( TheCamera.IsSphereVisible(CVector(vecExtraHugeSectorCentre.x, vecExtraHugeSectorCentre.x, 0.0f), SectorRadius(EXTRAHUGE_SECTOR_SIZE), &TheCamera.GetCameraMatrix()) ) { RenderOneFlatExtraHugeWaterPoly( vecExtraHugeSectorCentre.x - EXTRAHUGE_SECTOR_SIZE/2, vecExtraHugeSectorCentre.y - EXTRAHUGE_SECTOR_SIZE/2, 0.0f, color); } } } } } RenderAndEmptyRenderBuffer(); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); if ( WavesCalculatedThisFrame ) { RenderSeaBirds(); RenderShipsOnHorizon(); CParticle::HandleShipsAtHorizonStuff(); HandleBeachToysStuff(); } if ( _bSeaLife ) HandleSeaLifeForms(); DefinedState(); } void CWaterLevel::RenderTransparentWater(void) { bool bUseCamEndX = false; bool bUseCamStartY = false; bool bUseCamStartX = false; bool bUseCamEndY = false; _bSeaLife = false; if ( !CGame::CanSeeWaterFromCurrArea() ) return; PUSH_RENDERGROUP("CWaterLevel::RenderTransparentWater"); float fWaterDrawDist = _GetWavyDrawDist(); float fWaterDrawDistLarge = fWaterDrawDist + 90.0f; float fWavySectorMaxRenderDistSqr = SQR(fWaterDrawDist); _GetCamBounds(&bUseCamStartY, &bUseCamEndY, &bUseCamStartX, &bUseCamEndX); float fHugeSectorMaxRenderDist = _GetWaterDrawDist(); float fHugeSectorMaxRenderDistSqr = SQR(fHugeSectorMaxRenderDist); RenderBoatWakes(); RwRGBA color; color.red = CTimeCycle::GetWaterRed(); color.green = CTimeCycle::GetWaterGreen(); color.blue = CTimeCycle::GetWaterBlue(); color.alpha = 255; RwRGBA colorTrans; colorTrans.red = CTimeCycle::GetWaterRed(); colorTrans.green = CTimeCycle::GetWaterGreen(); colorTrans.blue = CTimeCycle::GetWaterBlue(); colorTrans.alpha = CTimeCycle::GetWaterAlpha(); TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; #ifndef PC_WATER WavesCalculatedThisFrame = false; #endif RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)gpWaterRaster); #ifndef PC_WATER RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); #endif CVector2D camPos(TheCamera.GetPosition().x, TheCamera.GetPosition().y); int32 nStartX = WATER_TO_HUGE_SECTOR_X(camPos.x - fHugeSectorMaxRenderDist + WATER_X_OFFSET); int32 nEndX = WATER_TO_HUGE_SECTOR_X(camPos.x + fHugeSectorMaxRenderDist + WATER_X_OFFSET) + 1; int32 nStartY = WATER_TO_HUGE_SECTOR_Y(camPos.y - fHugeSectorMaxRenderDist ); int32 nEndY = WATER_TO_HUGE_SECTOR_Y(camPos.y + fHugeSectorMaxRenderDist ) + 1; if ( bUseCamStartX ) nStartX = WATER_TO_HUGE_SECTOR_X(camPos.x + WATER_X_OFFSET); if ( bUseCamEndX ) nEndX = WATER_TO_HUGE_SECTOR_X(camPos.x + WATER_X_OFFSET); if ( bUseCamStartY ) nStartY = WATER_TO_HUGE_SECTOR_Y(camPos.y ); if ( bUseCamEndY ) nEndY = WATER_TO_HUGE_SECTOR_Y(camPos.y ); nStartX = clamp(nStartX, 0, MAX_HUGE_SECTORS - 1); nEndX = clamp(nEndX, 0, MAX_HUGE_SECTORS - 1); nStartY = clamp(nStartY, 0, MAX_HUGE_SECTORS - 1); nEndY = clamp(nEndY, 0, MAX_HUGE_SECTORS - 1); for ( int32 x = nStartX; x <= nEndX; x++ ) { for ( int32 y = nStartY; y <= nEndY; y++ ) { if ( aWaterBlockList[2*x+0][2*y+0] >= 0 || aWaterBlockList[2*x+1][2*y+0] >= 0 || aWaterBlockList[2*x+0][2*y+1] >= 0 || aWaterBlockList[2*x+1][2*y+1] >= 0 ) { float fX = WATER_FROM_HUGE_SECTOR_X(x) - WATER_X_OFFSET; float fY = WATER_FROM_HUGE_SECTOR_Y(y); CVector2D vecHugeSectorCentre ( fX + HUGE_SECTOR_SIZE/2, fY + HUGE_SECTOR_SIZE/2 ); float fHugeSectorDistToCamSqr = (camPos - vecHugeSectorCentre).MagnitudeSqr(); if ( fHugeSectorMaxRenderDistSqr > fHugeSectorDistToCamSqr ) { if ( TheCamera.IsSphereVisible(CVector(vecHugeSectorCentre.x, vecHugeSectorCentre.y, 0.0f), SectorRadius(HUGE_SECTOR_SIZE), &TheCamera.GetCameraMatrix()) ) { if ( fHugeSectorDistToCamSqr >= SQR(500.0f) ) { // see RenderWater() ; } else { for ( int32 x2 = 2*x; x2 <= 2*x+1; x2++ ) { for ( int32 y2 = 2*y; y2 <= 2*y+1; y2++ ) { if ( aWaterBlockList[x2][y2] >= 0 ) { float fLargeX = WATER_FROM_LARGE_SECTOR_X(x2) - WATER_X_OFFSET; float fLargeY = WATER_FROM_LARGE_SECTOR_Y(y2); CVector2D vecLargeSectorCentre(fLargeX + LARGE_SECTOR_SIZE/2, fLargeY + LARGE_SECTOR_SIZE/2); float fLargeSectorDistToCamSqr = (camPos - vecLargeSectorCentre).MagnitudeSqr(); if ( fLargeSectorDistToCamSqr < fHugeSectorMaxRenderDistSqr ) { if ( TheCamera.IsSphereVisible(CVector(vecLargeSectorCentre.x, vecLargeSectorCentre.y, 0.0f), SectorRadius(LARGE_SECTOR_SIZE), &TheCamera.GetCameraMatrix()) ) { // Render four small(32x32) sectors, or one large(64x64). // // [N] // --------- // |0x1|1x1| // [W] --------- [E] // |0x0|1x0| // --------- // [S] // float fLargeSectorDrawDistSqr = SQR((fWaterDrawDistLarge + 16.0f)); if ( fLargeSectorDistToCamSqr < fLargeSectorDrawDistSqr ) { _bSeaLife = true; float fZ; // WS if ( aWaterFineBlockList[2*x2+0][2*y2+0] >= 0 ) { float fSmallX = fLargeX; float fSmallY = fLargeY; CVector2D vecSmallSectorCentre(fSmallX + SMALL_SECTOR_SIZE/2, fSmallY + SMALL_SECTOR_SIZE/2); float fSmallSectorDistToCamSqr = (camPos - vecSmallSectorCentre).MagnitudeSqr(); fZ = ms_aWaterZs[ aWaterFineBlockList[2*x2+0][2*y2+0] ]; if ( fSmallSectorDistToCamSqr < fWavySectorMaxRenderDistSqr ) RenderOneWavySector(fSmallX, fSmallY, fZ, colorTrans); else RenderOneFlatSmallWaterPolyBlended(fSmallX, fSmallY, fZ, camPos.x, camPos.y, color, colorTrans, fWaterDrawDist); } // SE if ( aWaterFineBlockList[2*x2+1][2*y2+0] >= 0 ) { float fSmallX = fLargeX + (LARGE_SECTOR_SIZE/2); float fSmallY = fLargeY; CVector2D vecSmallSectorCentre(fSmallX + SMALL_SECTOR_SIZE/2, fSmallY + SMALL_SECTOR_SIZE/2); float fSmallSectorDistToCamSqr = (camPos - vecSmallSectorCentre).MagnitudeSqr(); fZ = ms_aWaterZs[ aWaterFineBlockList[2*x2+1][2*y2+0] ]; if ( fSmallSectorDistToCamSqr < fWavySectorMaxRenderDistSqr ) RenderOneWavySector(fSmallX, fSmallY, fZ, colorTrans); else RenderOneFlatSmallWaterPolyBlended(fSmallX, fSmallY, fZ, camPos.x, camPos.y, color, colorTrans, fWaterDrawDist); } // WN if ( aWaterFineBlockList[2*x2+0][2*y2+1] >= 0 ) { float fSmallX = fLargeX; float fSmallY = fLargeY + (LARGE_SECTOR_SIZE/2); CVector2D vecSmallSectorCentre(fSmallX + SMALL_SECTOR_SIZE/2,fSmallY + SMALL_SECTOR_SIZE/2); float fSmallSectorDistToCamSqr = (camPos - vecSmallSectorCentre).MagnitudeSqr(); fZ = ms_aWaterZs[ aWaterFineBlockList[2*x2+0][2*y2+1] ]; if ( fSmallSectorDistToCamSqr < fWavySectorMaxRenderDistSqr ) RenderOneWavySector(fSmallX, fSmallY, fZ, colorTrans); else RenderOneFlatSmallWaterPolyBlended(fSmallX, fSmallY, fZ, camPos.x, camPos.y, color, colorTrans, fWaterDrawDist); } //NE if ( aWaterFineBlockList[2*x2+1][2*y2+1] >= 0 ) { float fSmallX = fLargeX + (LARGE_SECTOR_SIZE/2); float fSmallY = fLargeY + (LARGE_SECTOR_SIZE/2); CVector2D vecSmallSectorCentre(fSmallX + SMALL_SECTOR_SIZE/2, fSmallY + SMALL_SECTOR_SIZE/2); float fSmallSectorDistToCamSqr = (camPos - vecSmallSectorCentre).MagnitudeSqr(); fZ = ms_aWaterZs[ aWaterFineBlockList[2*x2+1][2*y2+1] ]; if ( fSmallSectorDistToCamSqr < fWavySectorMaxRenderDistSqr ) RenderOneWavySector(fSmallX, fSmallY, fZ, colorTrans); else RenderOneFlatSmallWaterPolyBlended(fSmallX, fSmallY, fZ, camPos.x, camPos.y, color, colorTrans, fWaterDrawDist); } } else { float fZ; fZ = ms_aWaterZs[ aWaterBlockList[x2][y2] ]; RenderOneFlatLargeWaterPoly(fLargeX, fLargeY, fZ, color); } } } } } } } } } } } } RenderAndEmptyRenderBuffer(); #ifdef PC_WATER if ( MaskCalculatedThisFrame && (m_nRenderWaterLayers == 0 || m_nRenderWaterLayers == 2 || m_nRenderWaterLayers == 3) ) { RwV3d pos = { 0.0f, 0.0f, 0.0f }; pos.x = PreCalculatedMaskPosn.x; pos.y = PreCalculatedMaskPosn.y; pos.z = PreCalculatedMaskPosn.z; RpMatFXMaterialSetEnvMapFrame(RpGeometryGetMaterial(RpAtomicGetGeometry(ms_pMaskAtomic), 0), RwCameraGetFrame(RwCameraGetCurrentCamera())); RwFrameTranslate(RpAtomicGetFrame(ms_pMaskAtomic), &pos, rwCOMBINEREPLACE); RpAtomicRender(ms_pMaskAtomic); } #else if (!CCullZones::WaterFudge()) { int32 signX = 0; int32 signY = 0; float fCamX = camPos.x - SMALL_SECTOR_SIZE; float fCamY = camPos.y - SMALL_SECTOR_SIZE; if (TheCamera.GetForward().x > 0.3f) signX = 1; else if (TheCamera.GetForward().x < -0.3f) signX = -1; fCamX += 0.3f * (float)signX * float(SMALL_SECTOR_SIZE * 2.0f); // 19.2f if (TheCamera.GetForward().y > 0.3f) signY = 1; else if (TheCamera.GetForward().y < -0.3f) signY = -1; fCamY += 0.3f * (float)signY * float(SMALL_SECTOR_SIZE * 2.0f); // 19.2f int32 nBlock; int32 BlockX = WATER_TO_SMALL_SECTOR_X(fCamX + WATER_X_OFFSET) + 1; int32 BlockY = WATER_TO_SMALL_SECTOR_Y(fCamY) + 1; if (_IsColideWithBlock(BlockX, BlockY, nBlock)) { if (m_nRenderWaterLayers != 1 && m_nRenderWaterLayers != 6) { float fMaskX = Floor(fCamX / 2.0f) * 2.0f; float fMaskY = Floor(fCamY / 2.0f) * 2.0f; float fWaterZ = CWaterLevel::ms_aWaterZs[nBlock]; float fSectorX = WATER_FROM_SMALL_SECTOR_X(BlockX) - WATER_X_OFFSET; float fSectorY = WATER_FROM_SMALL_SECTOR_Y(BlockY); RenderWavyMask(fMaskX, fMaskY, fWaterZ, fSectorX, fSectorY, signX, signY, colorTrans); } } } DefinedState(); #endif POP_RENDERGROUP(); } void CWaterLevel::RenderOneFlatSmallWaterPoly(float fX, float fY, float fZ, RwRGBA const &color) { if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) RenderAndEmptyRenderBuffer(); int32 vidx = TempBufferVerticesStored; RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 0], color); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + SMALL_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 1.0f); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 1], color); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + SMALL_SECTOR_SIZE, fY + SMALL_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 1.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 1.0f); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 2], color); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + SMALL_SECTOR_SIZE, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 1.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 3], color); int32 iidx = TempBufferIndicesStored; TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; } void CWaterLevel::RenderOneFlatLargeWaterPoly(float fX, float fY, float fZ, RwRGBA const &color) { if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) RenderAndEmptyRenderBuffer(); int32 vidx = TempBufferVerticesStored; RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 0], color); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + LARGE_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 2.0f); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 1], color); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + LARGE_SECTOR_SIZE, fY + LARGE_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 2.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 2.0f); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 2], color); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + LARGE_SECTOR_SIZE, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 2.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 3], color); int32 iidx = TempBufferIndicesStored; TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; } void CWaterLevel::RenderOneFlatHugeWaterPoly(float fX, float fY, float fZ, RwRGBA const &color) { if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) RenderAndEmptyRenderBuffer(); int32 vidx = TempBufferVerticesStored; RwRGBA c; c.red = color.red; c.green = color.green; c.blue = color.blue; c.alpha = 255; RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 0], c); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + HUGE_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 4.0f); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 1], c); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + HUGE_SECTOR_SIZE, fY + HUGE_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 4.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 4.0f); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 2], c); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + HUGE_SECTOR_SIZE, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 4.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 3], c); int32 iidx = TempBufferIndicesStored; TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; } void CWaterLevel::RenderOneFlatExtraHugeWaterPoly(float fX, float fY, float fZ, RwRGBA const &color) { if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) RenderAndEmptyRenderBuffer(); int32 vidx = TempBufferVerticesStored; RwRGBA c; c.red = color.red; c.green = color.green; c.blue = color.blue; c.alpha = 255; RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 0], c); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + EXTRAHUGE_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 8.0f); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 1], c); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + EXTRAHUGE_SECTOR_SIZE, fY + EXTRAHUGE_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 8.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 8.0f); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 2], c); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + EXTRAHUGE_SECTOR_SIZE, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 8.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); RwIm3DVertexSet_RGBA(&TempBufferRenderVertices[vidx + 3], c); int32 iidx = TempBufferIndicesStored; TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; } void CWaterLevel::RenderOneWavySector(float fX, float fY, float fZ, RwRGBA const &color, bool bDontRender) { CVector vecSectorPos(fX + (SMALL_SECTOR_SIZE/2), fY + (SMALL_SECTOR_SIZE/2), fZ + 2.0f); if ( COcclusion::IsAABoxOccluded(vecSectorPos, SMALL_SECTOR_SIZE, SMALL_SECTOR_SIZE, 4.0f) ) return; #ifdef PC_WATER RequireWavySector = true; #else if (!WavesCalculatedThisFrame) { WavesCalculatedThisFrame = true; float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); RpGeometry *wavyGeometry = RpAtomicGetGeometry(ms_pWavyAtomic); RwTexCoords *wavyTexCoords = RpGeometryGetVertexTexCoords(wavyGeometry, rwTEXTURECOORDINATEINDEX0); RpMorphTarget *wavyMorph = RpGeometryGetMorphTarget(wavyGeometry, 0); RwRGBA *wavyPreLight = RpGeometryGetPreLightColors(wavyGeometry); RwV3d *wavyMorphVerts = RpMorphTargetGetVertices(wavyMorph); RwV3d *wavyMorphNormals = RpMorphTargetGetVertexNormals(wavyMorph); RpGeometryLock(wavyGeometry, rpGEOMETRYLOCKVERTICES | rpGEOMETRYLOCKNORMALS | rpGEOMETRYLOCKPRELIGHT | rpGEOMETRYLOCKTEXCOORDS); RwMatrix *camMat = RwFrameGetLTM(RwCameraGetFrame(RwCameraGetCurrentCamera())); //or curWorld float randomDampInv2 = (1.0f - fRandomDamp) * 2.0f; float move = 1.0f / 16.0f; float randomMove = 1.0f / (16.0f * fRandomMoveDiv); float vertMul = 0.5f; float wind = CWeather::WindClipped * 0.4f + 0.2f; float waveWind = CWeather::WindClipped * fWave2Ampl + 0.05f; float waveA = (TWOPI / 16.0f) * ((fNormalDirectionScalar1 * Abs(camMat->at.x + camMat->at.y) + fNormMult) * (CWeather::WindClipped * 0.4f + 0.2f)); float waveB = TWOPI / (16.0f * fWave2NormScale) * ((fNormalDirectionScalar2 * Abs(camMat->at.y - camMat->at.x) + fNormMultB) * (CWeather::WindClipped * 0.2f + 0.1f)); CVector vA(1.0f, 0.0f, 0.0f); CVector vB(0.0f, 1.0f, 0.0f); for ( int32 i = 0; i < 17; i++ ) { for ( int32 j = 0; j < 17; j++ ) { wavyTexCoords->u = float(i) * move + TEXTURE_ADDV; wavyTexCoords->v = float(j) * move + TEXTURE_ADDU; RwRGBAAssign(wavyPreLight, &color); if (i > 0 && i < 16 && j > 0 && j < 16) { wavyMorphVerts->x += CGeneral::GetRandomNumberInRange(-1.0f, 1.0f) * randomMove; wavyMorphVerts->x *= fRandomDamp; wavyMorphVerts->x += float(i) * randomDampInv2; wavyMorphVerts->y += CGeneral::GetRandomNumberInRange(-1.0f, 1.0f) * randomMove; wavyMorphVerts->y *= fRandomDamp; wavyMorphVerts->y += float(j) * randomDampInv2; } float morphVertXHalf = (i == 16) ? 0.0f : vertMul * wavyMorphVerts->x; float morphVertYHalf = (j == 16) ? 0.0f : vertMul * wavyMorphVerts->y; float waveMulA = (morphVertYHalf + morphVertXHalf) * (TWOPI / 16.0f) + fAngle; float waveMulB = (morphVertYHalf - morphVertXHalf) * (TWOPI / (16.0f * fWave2InvLength)) + fAngle; wavyMorphVerts->z = wind * Sin(waveMulA) + waveWind * Sin(waveMulB); vA.z = (waveA * Cos(waveMulA)) - (waveB * Cos(waveMulB)); vB.z = (waveA * Cos(waveMulA)) + (waveB * Cos(waveMulB)); CVector norm = CrossProduct(vA, vB); norm.Normalise(); wavyMorphNormals->x = norm.x; wavyMorphNormals->y = norm.y; wavyMorphNormals->z = norm.z; ++wavyPreLight; ++wavyTexCoords; ++wavyMorphVerts; ++wavyMorphNormals; } } RpGeometryUnlock(wavyGeometry); } float fCentreX = fX + (SMALL_SECTOR_SIZE / 2); float fCentreY = fY + (SMALL_SECTOR_SIZE / 2); #endif #ifdef PC_WATER if ( WavesCalculatedThisFrame ) #endif { if (bDontRender == false && m_nRenderWaterLayers != 2 && m_nRenderWaterLayers != 4 && m_nRenderWaterLayers != 6 ) { RwV3d pos = { 0.0f, 0.0f, 0.0f }; pos.x = fX; pos.y = fY; pos.z = fZ; RwFrameTranslate(RpAtomicGetFrame(ms_pWavyAtomic), &pos, rwCOMBINEREPLACE); RpAtomicRender(ms_pWavyAtomic); } } } int16 _RoundValue(int32 v) { int16 result = v; while ( result < 0 ) result += 16; while ( result > 16 ) result -= 16; return result; } void CWaterLevel::RenderWavyMask(float fX, float fY, float fZ, float fSectorX, float fSectorY, #ifdef PC_WATER float fCamPosX, float fCamPosY, float fCamDirX, float fCamDirY, RwRGBA const&color) #else int32 nCamDirX, int32 nCamDirY, RwRGBA const&color) #endif { #ifndef PC_WATER bool bRender = true; if (m_nRenderWaterLayers != 0 && m_nRenderWaterLayers != 2 && m_nRenderWaterLayers != 3) bRender = false; #endif CVector vecSectorPos(fX + (LARGE_SECTOR_SIZE/2), fY + (LARGE_SECTOR_SIZE/2), fZ + 2.0f); if ( COcclusion::IsAABoxOccluded(vecSectorPos, LARGE_SECTOR_SIZE, LARGE_SECTOR_SIZE, 4.0f) ) return; #ifndef PC_WATER float fUOffset = fX - (MAX_LARGE_SECTORS * (int32)Floor(fX / MAX_LARGE_SECTORS)); float fVOffset = fY - (MAX_LARGE_SECTORS * (int32)Floor(fY / MAX_LARGE_SECTORS)); int32 nSecsX = (int32)((fX - fSectorX) / 2.0f); int32 nSecsY = (int32)((fY - fSectorY) / 2.0f); #endif RpGeometry *wavyGeometry = RpAtomicGetGeometry(ms_pWavyAtomic); RpMorphTarget *wavyMorph = RpGeometryGetMorphTarget(wavyGeometry, 0); RwV3d *wavyMorphVerts = RpMorphTargetGetVertices(wavyMorph); RwV3d *wavyMorphNormals = RpMorphTargetGetVertexNormals(wavyMorph); RpGeometry *maskGeometry = RpAtomicGetGeometry(ms_pMaskAtomic); RwTexCoords *maskTexCoords = RpGeometryGetVertexTexCoords(maskGeometry, rwTEXTURECOORDINATEINDEX0); RwRGBA *maskPreLight = RpGeometryGetPreLightColors(maskGeometry); RpMorphTarget *maskMorph = RpGeometryGetMorphTarget(maskGeometry, 0); RwV3d *maskMorphVerts = RpMorphTargetGetVertices(maskMorph); RwV3d *maskMorphNormals = RpMorphTargetGetVertexNormals(maskMorph); RpGeometryLock(maskGeometry, rpGEOMETRYLOCKVERTICES|rpGEOMETRYLOCKNORMALS|rpGEOMETRYLOCKPRELIGHT|rpGEOMETRYLOCKTEXCOORDS); #ifndef PC_WATER RpMaterial *maskMat = RpGeometryGetMaterial(maskGeometry, 0); RpMatFXMaterialSetEnvMapFrame(maskMat, RwCameraGetFrame(RwCameraGetCurrentCamera())); RpMatFXMaterialSetEnvMapCoefficient(maskMat, fEnvScale); RpMatFXMaterialSetEnvMapFrameBufferAlpha(maskMat, TRUE); #endif #ifndef PC_WATER float fMinSparkZ = (CWeather::WindClipped * fWave2Ampl + 0.05f + CWeather::WindClipped * 0.4f + 0.2) * (1.0f - 0.04f * CWeather::SunGlare); int32 randval = CGeneral::GetRandomNumber(); float fUVStep = 0.125f; float f27 = 2.0f; float fMinU = (fUOffset / 16.0f) + _TEXTURE_MASK_ADDU; float fMinV = (fVOffset / 16.0f) + _TEXTURE_MASK_ADDV; float fAlphaMul = ((float)color.alpha * 0.4f) / 16.0f; float fXOffset = 16.0f; if (nCamDirX > 0) fXOffset = 6.4f; else if (nCamDirX < 0) fXOffset = 25.6f; float fYOffset = 16.0f; if (nCamDirY > 0) fYOffset = 6.4f; else if (nCamDirY < 0) fYOffset = 25.6f; int16 nX = _RoundValue(nSecsX - 1); int16 nY = _RoundValue(nSecsY - 1); #else float fMinSparkZ = (fWave2Ampl * CWeather::WindClipped + 0.05f + 0.4f * CWeather::WindClipped + 0.2) * (1.0f - 0.02f * CWeather::SunGlare); int32 randval = CGeneral::GetRandomNumber() & 255; int16 nX = _RoundValue((int32)((fX - fSectorX) * 0.5f) - 1); int16 nY = _RoundValue((int32)((fY - fSectorY) * 0.5f) - 1); #endif int16 idxX = nX; for ( int32 i = 0; i < 17; i++ ) { int16 idxY = nY; if ( ++idxX > 16 ) idxX -= 16; for ( int32 j = 0; j < 17; j++ ) { if ( ++idxY > 16 ) idxY -= 16; const int32 a = (0*16); const int32 b = (1*16); const int32 c = (33*16); const int32 d = (34*16); int32 base = (i*33+j); #ifndef PC_WATER maskTexCoords[base + a].u = fMinU + ((float)i * fUVStep); maskTexCoords[base + a].v = fMinV + ((float)j * fUVStep); maskTexCoords[base + b].u = maskTexCoords[base + a].u; maskTexCoords[base + b].v = maskTexCoords[base + a].v + (16.0f * fUVStep); maskTexCoords[base + c].u = maskTexCoords[base + a].u + (16.0f * fUVStep); maskTexCoords[base + c].v = maskTexCoords[base + a].v; maskTexCoords[base + d].u = maskTexCoords[base + a].u + (16.0f * fUVStep); maskTexCoords[base + d].v = maskTexCoords[base + a].v + (16.0f * fUVStep); #else maskTexCoords[base+a].v = float(j) / SMALL_SECTOR_SIZE + ((fCamPosY - fY) / 64); maskTexCoords[base+c].v = maskTexCoords[base+a].v; maskTexCoords[base+d].v = maskTexCoords[base+a].v + 0.5f; maskTexCoords[base+b].v = maskTexCoords[base+d].v; maskTexCoords[base+a].u = float(i) / SMALL_SECTOR_SIZE + ((fCamPosX - fX) / 64); maskTexCoords[base+b].u = maskTexCoords[base+a].u; maskTexCoords[base+d].u = maskTexCoords[base+a].u + 0.5f; maskTexCoords[base+c].u = maskTexCoords[base+d].u; #endif maskMorphVerts[base+a].x = (wavyMorphVerts[idxY + (17 * idxX)].x - (float)idxX * 2.0f) + (float(i) * 2.0f); maskMorphVerts[base+b].x = maskMorphVerts[base+a].x; maskMorphVerts[base+c].x = maskMorphVerts[base+a].x + SMALL_SECTOR_SIZE; maskMorphVerts[base+d].x = maskMorphVerts[base+c].x; maskMorphVerts[base+a].y = (wavyMorphVerts[idxY + (17 * idxX)].y - (float)idxY * 2.0f) + (float(j) * 2.0f); maskMorphVerts[base+c].y = maskMorphVerts[base+a].y; maskMorphVerts[base+b].y = maskMorphVerts[base+a].y + SMALL_SECTOR_SIZE; maskMorphVerts[base+d].y = maskMorphVerts[base+b].y; maskMorphVerts[base+a].z = wavyMorphVerts[idxY + (17 * idxX)].z; maskMorphVerts[base+d].z = maskMorphVerts[base+a].z; maskMorphVerts[base+c].z = maskMorphVerts[base+d].z; maskMorphVerts[base+b].z = maskMorphVerts[base+c].z; #ifndef PC_WATER if (maskMorphVerts[base].z >= fMinSparkZ) #else if ( maskMorphVerts[base].z > fMinSparkZ ) #endif { switch ( (i + j + randval) & 3 ) { case 0: { CVector vecPos ( fX + maskMorphVerts[base+a].x, fY + maskMorphVerts[base+a].y, fZ + maskMorphVerts[base+a].z + 0.12f ); vecPos -= 0.05f * TheCamera.GetForward(); CParticle::AddParticle(PARTICLE_WATER_SPARK, vecPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 15, CGeneral::GetRandomNumberInRange(-90, 90), 0, 0); } break; case 1: { CVector vecPos ( fX + maskMorphVerts[base+c].x, fY + maskMorphVerts[base+c].y, fZ + maskMorphVerts[base+c].z + 0.12f ); vecPos -= 0.05f * TheCamera.GetForward(); CParticle::AddParticle(PARTICLE_WATER_SPARK, vecPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 15, CGeneral::GetRandomNumberInRange(-90, 90), 0, 0); } break; case 2: { CVector vecPos ( fX + maskMorphVerts[base+b].x, fY + maskMorphVerts[base+b].y, fZ + maskMorphVerts[base+b].z + 0.12f ); vecPos -= 0.05f * TheCamera.GetForward(); CParticle::AddParticle(PARTICLE_WATER_SPARK, vecPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 15, CGeneral::GetRandomNumberInRange(-90, 90), 0, 0); } break; case 3: { CVector vecPos ( fX + maskMorphVerts[base+d].x, fY + maskMorphVerts[base+d].y, fZ + maskMorphVerts[base+d].z + 0.12f ); vecPos -= 0.05f * TheCamera.GetForward(); CParticle::AddParticle(PARTICLE_WATER_SPARK, vecPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 15, CGeneral::GetRandomNumberInRange(-90, 90), 0, 0); } break; } } maskMorphNormals[base+a].x = wavyMorphNormals[idxY + (17 * idxX)].x; maskMorphNormals[base+a].y = wavyMorphNormals[idxY + (17 * idxX)].y; maskMorphNormals[base+a].z = wavyMorphNormals[idxY + (17 * idxX)].z; maskMorphNormals[base+d].x = maskMorphNormals[base+a].x; maskMorphNormals[base+d].y = maskMorphNormals[base+a].y; maskMorphNormals[base+d].z = maskMorphNormals[base+a].z; maskMorphNormals[base+c].x = maskMorphNormals[base+d].x; maskMorphNormals[base+c].y = maskMorphNormals[base+d].y; maskMorphNormals[base+c].z = maskMorphNormals[base+d].z; maskMorphNormals[base+b].x = maskMorphNormals[base+c].x; maskMorphNormals[base+b].y = maskMorphNormals[base+c].y; maskMorphNormals[base+b].z = maskMorphNormals[base+c].z; maskPreLight[base+a].red = color.red; maskPreLight[base+a].green = color.green; maskPreLight[base+a].blue = color.blue; maskPreLight[base+a].alpha = color.alpha; maskPreLight[base+d].red = maskPreLight[base+a].red; maskPreLight[base+d].green = maskPreLight[base+a].green; maskPreLight[base+d].blue = maskPreLight[base+a].blue; maskPreLight[base+d].alpha = maskPreLight[base+a].alpha; maskPreLight[base+c].red = maskPreLight[base+d].red; maskPreLight[base+c].green = maskPreLight[base+d].green; maskPreLight[base+c].blue = maskPreLight[base+d].blue; maskPreLight[base+c].alpha = maskPreLight[base+d].alpha; maskPreLight[base+b].red = maskPreLight[base+c].red; maskPreLight[base+b].green = maskPreLight[base+c].green; maskPreLight[base+b].blue = maskPreLight[base+c].blue; maskPreLight[base+b].alpha = maskPreLight[base+c].alpha; #ifndef PC_WATER maskPreLight[base + a].alpha = Max(0, (int32)((float)color.alpha - (fAlphaMul * (Abs((float)i - fXOffset) + Abs((float)j - fYOffset))))); maskPreLight[base + b].alpha = Max(0, (int32)((float)color.alpha - (fAlphaMul * (Abs((float)i - fXOffset) + Abs(16.0f + (float)j - fYOffset))))); maskPreLight[base + c].alpha = Max(0, (int32)((float)color.alpha - (fAlphaMul * (Abs(16.0f + (float)i - fXOffset) + Abs((float)j - fYOffset))))); maskPreLight[base + d].alpha = Max(0, (int32)((float)color.alpha - (fAlphaMul * (Abs(16.0f + (float)i - fXOffset) + Abs(16.0f + (float)j - fYOffset))))); #endif } } RpGeometryUnlock(maskGeometry); #ifndef PC_WATER { RwV3d pos = { 0.0f, 0.0f, 0.0f }; pos.x = fX; pos.y = fY; pos.z = fZ + 0.05f; RwFrameTranslate(RpAtomicGetFrame(ms_pMaskAtomic), &pos, rwCOMBINEREPLACE); if (bRender) { #ifdef PS2 RpSkyTexCacheFlush(); #endif RpAtomicRender(ms_pMaskAtomic); } } #endif } #ifdef PC_WATER void CWaterLevel::PreCalcWaterGeometry(void) { if ( !RequireWavySector ) { WavesCalculatedThisFrame = false; MaskCalculatedThisFrame = false; return; } RequireWavySector = false; WavesCalculatedThisFrame = true; RwRGBA color; color.red = CTimeCycle::GetWaterRed(); color.green = CTimeCycle::GetWaterGreen(); color.blue = CTimeCycle::GetWaterBlue(); color.alpha = CTimeCycle::GetWaterAlpha(); PreCalcWavySector(color); if ( CCullZones::WaterFudge() ) { MaskCalculatedThisFrame = false; return; } CVector CamFwdDir = TheCamera.GetForward(); CamFwdDir.z = 0.0f; CamFwdDir.Normalise(); float fCamX = TheCamera.GetPosition().x - SMALL_SECTOR_SIZE; float fCamY = TheCamera.GetPosition().y - SMALL_SECTOR_SIZE; //1.4144272f; 1.4144f; float signX = CamFwdDir.x * 1.4144272f; float signY = CamFwdDir.y * 1.4144272f; signX = clamp(signX, -1.0f, 1.0f); fCamX += 0.4f * signX * float(SMALL_SECTOR_SIZE * 2.0f); signY = clamp(signY, -1.0f, 1.0f); fCamY += 0.4f * signY * float(SMALL_SECTOR_SIZE * 2.0f); int32 nBlock; int32 BlockX = WATER_TO_SMALL_SECTOR_X(fCamX + WATER_X_OFFSET) + 1; int32 BlockY = WATER_TO_SMALL_SECTOR_Y(fCamY ) + 1; ASSERT( BlockX >= 0 && BlockX < MAX_SMALL_SECTORS ); ASSERT( BlockY >= 0 && BlockY < MAX_SMALL_SECTORS ); if ( _IsColideWithBlock(BlockX, BlockY, nBlock) ) { float fMaskX = Floor(fCamX / 2.0f) * 2.0f; float fMaskY = Floor(fCamY / 2.0f) * 2.0f; float fSectorX = WATER_FROM_SMALL_SECTOR_X(BlockX) - WATER_X_OFFSET; float fSectorY = WATER_FROM_SMALL_SECTOR_Y(BlockY); if ( PreCalcWavyMask( fMaskX, fMaskY, ms_aWaterZs[nBlock], fSectorX, fSectorY, fCamX, fCamY, CamFwdDir.x, CamFwdDir.y, color ) ) { PreCalculatedMaskPosn.x = fMaskX; PreCalculatedMaskPosn.y = fMaskY; PreCalculatedMaskPosn.z = ms_aWaterZs[nBlock] + 0.05f; MaskCalculatedThisFrame = true; } else MaskCalculatedThisFrame = false; } else MaskCalculatedThisFrame = false; } bool CWaterLevel::PreCalcWavySector(RwRGBA const &color) { float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); RpGeometry *wavyGeometry = RpAtomicGetGeometry(ms_pWavyAtomic); RwTexCoords *wavyTexCoords = RpGeometryGetVertexTexCoords(wavyGeometry, rwTEXTURECOORDINATEINDEX0); RpMorphTarget *wavyMorph = RpGeometryGetMorphTarget(wavyGeometry, 0); RwRGBA *wavyPreLight = RpGeometryGetPreLightColors(wavyGeometry); RwV3d *wavyMorphVerts = RpMorphTargetGetVertices(wavyMorph); RwV3d *wavyMorphNormals = RpMorphTargetGetVertexNormals(wavyMorph); if ( !m_bRenderSeaBed ) RpGeometryLock(wavyGeometry, rpGEOMETRYLOCKVERTICES |rpGEOMETRYLOCKNORMALS |rpGEOMETRYLOCKPRELIGHT |rpGEOMETRYLOCKTEXCOORDS); CVector camPosUp = TheCamera.GetForward(); float randomDampInv2 = (1.0f - fRandomDamp) * 2.0f; float randomMove = 1.0f / (16.0f * fRandomMoveDiv); float wind = CWeather::WindClipped * 0.4f + 0.2f; float waveWind = CWeather::WindClipped * fWave2Ampl + 0.05f; float waveA = (TWOPI / 16.0f) * ((CWeather::WindClipped * 0.4f + 0.2f) * (fNormalDirectionScalar1 * Abs(camPosUp.x + camPosUp.y) + fNormMult)); float waveB = TWOPI / (16.0f * fWave2NormScale) * ((CWeather::WindClipped * 0.2f + 0.1f) * (fNormalDirectionScalar2 * Abs(camPosUp.y - camPosUp.x) + fNormMultB)); CVector vA(1.0f, 0.0f, 0.0f); CVector vB(0.0f, 1.0f, 0.0f); for ( int32 i = 0; i < 17; i++ ) { for ( int32 j = 0; j < 17; j++ ) { wavyTexCoords->u = (float(i) / 16.0f) + TEXTURE_ADDV; wavyTexCoords->v = (float(j) / 16.0f) + TEXTURE_ADDU; RwRGBAAssign(wavyPreLight, &color); if ( i > 0 && i < 16 && j > 0 && j < 16 ) { wavyMorphVerts->x += CGeneral::GetRandomNumberInRange(-1.0f, 1.0f) * randomMove; wavyMorphVerts->x *= fRandomDamp; wavyMorphVerts->x += float(i) * randomDampInv2; wavyMorphVerts->y += CGeneral::GetRandomNumberInRange(-1.0f, 1.0f) * randomMove; wavyMorphVerts->y *= fRandomDamp; wavyMorphVerts->y += float(j) * randomDampInv2; } float morphVertXHalf = ( i == 16 ) ? 0.0f : 0.5f * wavyMorphVerts->x; float morphVertYHalf = ( j == 16 ) ? 0.0f : 0.5f * wavyMorphVerts->y; float waveMulA = (morphVertYHalf + morphVertXHalf) * (TWOPI / 16.0f) + fAngle; float waveMulB = (morphVertYHalf - morphVertXHalf) * (TWOPI / (16.0f * fWave2InvLength)) + fAngle; wavyMorphVerts->z = wind * Sin(waveMulA) + waveWind * Sin(waveMulB); vA.z = (waveA * Cos(waveMulA)) - (waveB * Cos(waveMulB)); vB.z = (waveA * Cos(waveMulA)) + (waveB * Cos(waveMulB)); CVector norm = CrossProduct(vA, vB); norm.Normalise(); wavyMorphNormals->x = norm.x; wavyMorphNormals->y = norm.y; wavyMorphNormals->z = norm.z; ++wavyPreLight; ++wavyTexCoords; ++wavyMorphVerts; ++wavyMorphNormals; } } RpGeometryUnlock(wavyGeometry); return true; } bool CWaterLevel::PreCalcWavyMask(float fX, float fY, float fZ, float fSectorX, float fSectorY, float fCamPosX, float fCamPosY, float fCamDirX, float fCamDirY, RwRGBA const&color) { CVector vecSectorPos(fX + (MAX_LARGE_SECTORS/2), fY + (MAX_LARGE_SECTORS/2), fZ + 2.0f); if ( COcclusion::IsAABoxOccluded(vecSectorPos, MAX_LARGE_SECTORS, MAX_LARGE_SECTORS, 4.0f) ) return false; Floor(fX / MAX_LARGE_SECTORS); Floor(fY / MAX_LARGE_SECTORS); RpGeometry *wavyGeometry = RpAtomicGetGeometry(ms_pWavyAtomic); RpMorphTarget *wavyMorph = RpGeometryGetMorphTarget(wavyGeometry, 0); RwV3d *wavyMorphVerts = RpMorphTargetGetVertices(wavyMorph); RwV3d *wavyMorphNormals = RpMorphTargetGetVertexNormals(wavyMorph); RpGeometry *maskGeometry = RpAtomicGetGeometry(ms_pMaskAtomic); RwTexCoords *maskTexCoords = RpGeometryGetVertexTexCoords(maskGeometry, rwTEXTURECOORDINATEINDEX0); RwRGBA *maskPreLight = RpGeometryGetPreLightColors(maskGeometry); RpMorphTarget *maskMorph = RpGeometryGetMorphTarget(maskGeometry, 0); RwV3d *maskMorphVerts = RpMorphTargetGetVertices(maskMorph); RwV3d *maskMorphNormals = RpMorphTargetGetVertexNormals(maskMorph); if ( !m_bRenderSeaBed ) RpGeometryLock(maskGeometry, rpGEOMETRYLOCKVERTICES | rpGEOMETRYLOCKNORMALS | rpGEOMETRYLOCKPRELIGHT | rpGEOMETRYLOCKTEXCOORDS); float fMinSparkZ = (fWave2Ampl * CWeather::WindClipped + 0.05f + 0.4f * CWeather::WindClipped + 0.2) * (1.0f - 0.02f * CWeather::SunGlare); int32 randval = CGeneral::GetRandomNumber() & 255; int16 nX = _RoundValue((int32)((fX - fSectorX) * 0.5f) - 1); int16 nY = _RoundValue((int32)((fY - fSectorY) * 0.5f) - 1); int16 idxX = nX; for ( int32 i = 0; i < 17; i++ ) { int16 idxY = nY; if ( ++idxX > 16 ) idxX -= 16; for ( int32 j = 0; j < 17; j++ ) { if ( ++idxY > 16 ) idxY -= 16; const int32 a = (0*16); const int32 b = (1*16); const int32 c = (33*16); const int32 d = (34*16); int32 base = (i*33+j); maskTexCoords[base+a].v = float(j) / 32 + ((fCamPosY - fY) / 64); maskTexCoords[base+c].v = maskTexCoords[base+a].v; maskTexCoords[base+d].v = maskTexCoords[base+a].v + 0.5f; maskTexCoords[base+b].v = maskTexCoords[base+d].v; maskTexCoords[base+a].u = float(i) / 32 + ((fCamPosX - fX) / 64); maskTexCoords[base+b].u = maskTexCoords[base+a].u; maskTexCoords[base+d].u = maskTexCoords[base+a].u + 0.5f; maskTexCoords[base+c].u = maskTexCoords[base+d].u; maskMorphVerts[base+a].x = (wavyMorphVerts[idxY + (17 * idxX)].x - (float)idxX * 2.0f) + (float(i) * 2.0f); maskMorphVerts[base+b].x = maskMorphVerts[base+a].x; maskMorphVerts[base+c].x = maskMorphVerts[base+a].x + SMALL_SECTOR_SIZE; maskMorphVerts[base+d].x = maskMorphVerts[base+c].x; maskMorphVerts[base+a].y = (wavyMorphVerts[idxY + (17 * idxX)].y - (float)idxY * 2.0f) + (float(j) * 2.0f); maskMorphVerts[base+c].y = maskMorphVerts[base+a].y; maskMorphVerts[base+b].y = maskMorphVerts[base+a].y + SMALL_SECTOR_SIZE; maskMorphVerts[base+d].y = maskMorphVerts[base+b].y; maskMorphVerts[base+a].z = wavyMorphVerts[idxY + (17 * idxX)].z; maskMorphVerts[base+d].z = maskMorphVerts[base+a].z; maskMorphVerts[base+c].z = maskMorphVerts[base+d].z; maskMorphVerts[base+b].z = maskMorphVerts[base+c].z; if ( maskMorphVerts[base].z > fMinSparkZ ) { switch ( (i + j + randval) & 3 ) { case 0: { CVector vecPos ( fX + maskMorphVerts[base+a].x, fY + maskMorphVerts[base+a].y, fZ + maskMorphVerts[base+a].z + 0.12f ); vecPos -= 0.05f * TheCamera.GetForward(); CParticle::AddParticle(PARTICLE_WATER_SPARK, vecPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 15, CGeneral::GetRandomNumberInRange(-90, 90), 0, 0); } break; case 1: { CVector vecPos ( fX + maskMorphVerts[base+c].x, fY + maskMorphVerts[base+c].y, fZ + maskMorphVerts[base+c].z + 0.12f ); vecPos -= 0.05f * TheCamera.GetForward(); CParticle::AddParticle(PARTICLE_WATER_SPARK, vecPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 15, CGeneral::GetRandomNumberInRange(-90, 90), 0, 0); } break; case 2: { CVector vecPos ( fX + maskMorphVerts[base+b].x, fY + maskMorphVerts[base+b].y, fZ + maskMorphVerts[base+b].z + 0.12f ); vecPos -= 0.05f * TheCamera.GetForward(); CParticle::AddParticle(PARTICLE_WATER_SPARK, vecPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 15, CGeneral::GetRandomNumberInRange(-90, 90), 0, 0); } break; case 3: { CVector vecPos ( fX + maskMorphVerts[base+d].x, fY + maskMorphVerts[base+d].y, fZ + maskMorphVerts[base+d].z + 0.12f ); vecPos -= 0.05f * TheCamera.GetForward(); CParticle::AddParticle(PARTICLE_WATER_SPARK, vecPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 15, CGeneral::GetRandomNumberInRange(-90, 90), 0, 0); } break; } } maskMorphNormals[base+a].x = wavyMorphNormals[idxY + (17 * idxX)].x; maskMorphNormals[base+a].y = wavyMorphNormals[idxY + (17 * idxX)].y; maskMorphNormals[base+a].z = wavyMorphNormals[idxY + (17 * idxX)].z; maskMorphNormals[base+d].x = maskMorphNormals[base+a].x; maskMorphNormals[base+d].y = maskMorphNormals[base+a].y; maskMorphNormals[base+d].z = maskMorphNormals[base+a].z; maskMorphNormals[base+c].x = maskMorphNormals[base+d].x; maskMorphNormals[base+c].y = maskMorphNormals[base+d].y; maskMorphNormals[base+c].z = maskMorphNormals[base+d].z; maskMorphNormals[base+b].x = maskMorphNormals[base+c].x; maskMorphNormals[base+b].y = maskMorphNormals[base+c].y; maskMorphNormals[base+b].z = maskMorphNormals[base+c].z; maskPreLight[base+a].red = color.red; maskPreLight[base+a].green = color.green; maskPreLight[base+a].blue = color.blue; maskPreLight[base+a].alpha = color.alpha; maskPreLight[base+d].red = maskPreLight[base+a].red; maskPreLight[base+d].green = maskPreLight[base+a].green; maskPreLight[base+d].blue = maskPreLight[base+a].blue; maskPreLight[base+d].alpha = maskPreLight[base+a].alpha; maskPreLight[base+c].red = maskPreLight[base+d].red; maskPreLight[base+c].green = maskPreLight[base+d].green; maskPreLight[base+c].blue = maskPreLight[base+d].blue; maskPreLight[base+c].alpha = maskPreLight[base+d].alpha; maskPreLight[base+b].red = maskPreLight[base+c].red; maskPreLight[base+b].green = maskPreLight[base+c].green; maskPreLight[base+b].blue = maskPreLight[base+c].blue; maskPreLight[base+b].alpha = maskPreLight[base+c].alpha; } } RpGeometryUnlock(maskGeometry); return true; } #endif void CWaterLevel::RenderBoatWakes(void) { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)gpWaterWakeRaster); #ifndef PC_WATER RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); #endif #ifdef _XBOX // TODO save and restore rwRENDERSTATESRCBLEND rwRENDERSTATEDESTBLEND #endif CBoat::FillBoatList(); float fWakeZ = 5.97f; float fWakeLifeTimeMult = 0.01f / CBoat::WAKE_LIFETIME; for ( int32 idx = 0; idx < ARRAY_SIZE(CBoat::apFrameWakeGeneratingBoats); idx++ ) { CBoat *pBoat = CBoat::apFrameWakeGeneratingBoats[idx]; if ( pBoat == nil ) break; CVector2D vecDistA(pBoat->GetForward().x, pBoat->GetForward().y); float fSize = pBoat->GetColModel()->boundingBox.max.z * 0.65f; if ( pBoat->GetModelIndex() == MI_SKIMMER) fSize *= 0.4f; float fAplhaA = 255.0f; float fSizeA = fSize; float fAplhaB; float fSizeB; for ( int32 wake = 1; wake < pBoat->m_nNumWakePoints; wake++ ) { bool bRender = true; float fTimeleft = CBoat::WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[wake]; float fWakeSizeB = ((float)wake * 0.19f) + fSize - fWakeLifeTimeMult * Max(fTimeleft, 0.0f); fSizeB = fWakeSizeB / CBoat::MIN_WAKE_INTERVAL; if ( fSizeB < 0.0f ) fSizeB = 1.0f; if ( wake == pBoat->m_nNumWakePoints - 1 ) { // set alpha to 0 if it's last point fAplhaB = 0.0f; } else { // clip (-100, 500), less lifetime - less val float val = 500.0f - (CBoat::WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[wake]) * 600.0f / CBoat::WAKE_LIFETIME; fAplhaB = clamp(val, 0.0f, 255.0f); } CVector2D vecDistB = pBoat->m_avec2dWakePoints[wake - 1] - pBoat->m_avec2dWakePoints[wake]; float fScal = vecDistB.MagnitudeSqr(); // normalize if distance between points is greater than 3 if ( fScal > SQR(3.0f) ) { float fNorm = 1.0f / sqrt(fScal); vecDistB.x *= fNorm; vecDistB.y *= fNorm; // disable render if distance between points too big if ( sqrt(fScal) > 13.0f ) bRender = false; } CVector2D vecAA ( pBoat->m_avec2dWakePoints[wake - 1].x - (fSizeA * vecDistA.y), pBoat->m_avec2dWakePoints[wake - 1].y + (fSizeA * vecDistA.x) ); CVector2D vecAB ( pBoat->m_avec2dWakePoints[wake - 1].x + (fSizeA * vecDistA.y), pBoat->m_avec2dWakePoints[wake - 1].y - (fSizeA * vecDistA.x) ); CVector2D vecBA ( pBoat->m_avec2dWakePoints[wake].x + (fSizeB * vecDistB.y), pBoat->m_avec2dWakePoints[wake].y - (fSizeB * vecDistB.x) ); CVector2D vecBB ( pBoat->m_avec2dWakePoints[wake].x - (fSizeB * vecDistB.y), pBoat->m_avec2dWakePoints[wake].y + (fSizeB * vecDistB.x) ); if ( bRender ) RenderWakeSegment(vecAA, vecAB, vecBA, vecBB, fSizeA, fSizeB, fAplhaA, fAplhaB, fWakeZ); vecDistA = vecDistB; fSizeA = fSizeB; fAplhaB = fAplhaA; } } RenderAndEmptyRenderBuffer(); } inline float _GetWindedWave(float fX, float fY) { float fAngle = (CTimer::GetTimeInMilliseconds() & 4095) * (TWOPI / 4096.0f); float x = WATER_HUGE_X(fX + WATER_X_OFFSET); float y = WATER_HUGE_Y(fY); float fWindFactor (CWeather::WindClipped * 0.4f + 0.2f); float fWave = Sin(( (x - Floor(x)) + (y - Floor(y)) ) * TWOPI + fAngle); return fWindFactor * fWave; } void CWaterLevel::RenderWakeSegment(CVector2D &vecA, CVector2D &vecB, CVector2D &vecC, CVector2D &vecD, float &fSizeA, float &fSizeB, float &fAlphaA, float &fAlphaB, float &fWakeZ) { for ( int32 i = 0; i < 4; i++ ) { if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) RenderAndEmptyRenderBuffer(); float fCurStep = (float)i / 4; float fNxtStep = (float)(i + 1) / 4; float fLeftCurStep = 1.0f - fCurStep; float fLeftNxtStep = 1.0f - fNxtStep; uint8 AlphaA = (uint32)(fAlphaA * aAlphaFade[i] ); uint8 AlphaB = (uint32)(fAlphaA * aAlphaFade[i + 1]); uint8 AlphaC = (uint32)(fAlphaB * aAlphaFade[i + 1]); uint8 AlphaD = (uint32)(fAlphaB * aAlphaFade[i] ); CVector2D PosA = vecB*fCurStep + vecA*fLeftCurStep; CVector2D PosB = vecB*fNxtStep + vecA*fLeftNxtStep; CVector2D PosC = vecC*fNxtStep + vecD*fLeftNxtStep; CVector2D PosD = vecC*fCurStep + vecD*fLeftCurStep; float fUA = (PosA.x / 4) + _TEXTURE_WAKE_ADDU; float fVA = (PosA.y / 4) + _TEXTURE_WAKE_ADDV; float fUB = (PosB.x / 4) + _TEXTURE_WAKE_ADDU; float fVB = (PosB.y / 4) + _TEXTURE_WAKE_ADDV; float fUC = (PosC.x / 4) + _TEXTURE_WAKE_ADDU; float fVC = (PosC.y / 4) + _TEXTURE_WAKE_ADDV; float fUD = (PosD.x / 4) + _TEXTURE_WAKE_ADDU; float fVD = (PosD.y / 4) + _TEXTURE_WAKE_ADDV; #define MIN4(a, b, c, d) (Min((a), Min((b), Min((c), (d))))) float fMinU = Floor(MIN4(fUA, fUB, fUC, fUD)); float fMinV = Floor(MIN4(fVA, fVB, fVC, fVD)); #undef MIN4 float fZA = _GetWindedWave(PosA.x, PosA.y) + fWakeZ; float fZB = _GetWindedWave(PosB.x, PosB.y) + fWakeZ; float fZC = _GetWindedWave(PosC.x, PosC.y) + fWakeZ; float fZD = _GetWindedWave(PosD.x, PosD.y) + fWakeZ; int32 vidx = TempBufferVerticesStored; RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], PosA.x, PosA.y, fZA); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], fUA - fMinU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], fVA - fMinV); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[vidx + 0], 255, 255, 255, AlphaA); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], PosB.x, PosB.y, fZB); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], fUB - fMinU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], fVB - fMinV); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[vidx + 1], 255, 255, 255, AlphaB); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], PosC.x, PosC.y, fZC); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], fUC - fMinU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], fVC - fMinV); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[vidx + 2], 255, 255, 255, AlphaC); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], PosD.x, PosD.y, fZD); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], fUD - fMinU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], fVD - fMinV); RwIm3DVertexSetRGBA (&TempBufferRenderVertices[vidx + 3], 255, 255, 255, AlphaD); int32 iidx = TempBufferIndicesStored; TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; } } void CWaterLevel::RenderOneSlopedUnderWaterPoly(float fX, float fY, float fZ, RwRGBA const&color) { CVector2D camPos(TheCamera.GetPosition().x, TheCamera.GetPosition().y); float fDistA = (CVector2D(fX, fY) - camPos).Magnitude() + -140.0f; float fDistB = (CVector2D(fX, fY + HUGE_SECTOR_SIZE) - camPos).Magnitude() + -140.0f; float fDistC = (CVector2D(fX + HUGE_SECTOR_SIZE, fY + HUGE_SECTOR_SIZE) - camPos).Magnitude() + -140.0f; float fDistD = (CVector2D(fX + HUGE_SECTOR_SIZE, fY) - camPos).Magnitude() + -140.0f; #ifndef PC_WATER #define CALCSEABED(v, d) \ { \ if ( d < 0.0f ) \ v = 0.1f + fSeaBedZ; \ else if ( d > 240.0f ) \ v = 0.1f; \ else \ v = 0.1f + ((fSeaBedZ * (240.0f - d)) / 240.0f); \ } #else #define CALCSEABED(v, d) \ { \ v = 0.1f; \ if ( d < 0.0f ) \ v += fSeaBedZ; \ else if ( d <= 240.0f ) \ v += (fSeaBedZ / 240.0f) * (240.0f - d); \ } #endif float fSeaBedA, fSeaBedB, fSeaBedC, fSeaBedD; CALCSEABED(fSeaBedA, fDistA); CALCSEABED(fSeaBedB, fDistB); CALCSEABED(fSeaBedC, fDistC); CALCSEABED(fSeaBedD, fDistD); #undef CALCSEABED if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) RenderAndEmptyRenderBuffer(); int32 vidx = TempBufferVerticesStored; RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset - fSeaBedA); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], 0.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], 0.0f); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 0], color.red, color.green, color.blue, 255); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + HUGE_SECTOR_SIZE, fZ - _fWaterZOffset - fSeaBedB); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], 0.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], 4.0f); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 1], color.red, color.green, color.blue, 255); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + HUGE_SECTOR_SIZE, fY + HUGE_SECTOR_SIZE, fZ - _fWaterZOffset - fSeaBedC); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], 4.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], 4.0f); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 2], color.red, color.green, color.blue, 255); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + HUGE_SECTOR_SIZE, fY, fZ - _fWaterZOffset - fSeaBedD); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], 4.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], 0.0f); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 3], color.red, color.green, color.blue, 255); int32 iidx = TempBufferIndicesStored; TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; } void CWaterLevel::RenderOneFlatSmallWaterPolyBlended(float fX, float fY, float fZ, float fCamX, float fCamY, RwRGBA const &color, RwRGBA const &colorTrans, float fDrawDist) { if ( TempBufferIndicesStored >= TEMPBUFFERINDEXSIZE-6 || TempBufferVerticesStored >= TEMPBUFFERVERTSIZE-4 ) RenderAndEmptyRenderBuffer(); int32 vidx = TempBufferVerticesStored; float fBlendDrawDist = fDrawDist + fStartBlendDistanceAdd; float fDistStartX = SQR(fX - fCamX); float fDistStartY = SQR(fY - fCamY); float fDistEndX = SQR((fX + SMALL_SECTOR_SIZE) - fCamX); float fDistEndY = SQR((fY + SMALL_SECTOR_SIZE) - fCamY); float fAlphaBlendMulA = Min(fFlatWaterBlendRange * Max(sqrt(fDistStartX + fDistStartY) - fBlendDrawDist, fMinWaterAlphaMult), 1.0f); float fAlphaBlendMulB = Min(fFlatWaterBlendRange * Max(sqrt(fDistStartX + fDistEndY ) - fBlendDrawDist, fMinWaterAlphaMult), 1.0f); float fAlphaBlendMulC = Min(fFlatWaterBlendRange * Max(sqrt(fDistEndX + fDistEndY ) - fBlendDrawDist, fMinWaterAlphaMult), 1.0f); float fAlphaBlendMulD = Min(fFlatWaterBlendRange * Max(sqrt(fDistEndX + fDistStartY) - fBlendDrawDist, fMinWaterAlphaMult), 1.0f); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 0], fX, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 0], TEXTURE_ADDV); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 0], color.red, color.green, color.blue, (colorTrans.alpha + (color.alpha - colorTrans.alpha) * (uint8)(int32)fAlphaBlendMulA)); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 1], fX, fY + SMALL_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDU); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 1], TEXTURE_ADDV + 1.0f); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 1], color.red, color.green, color.blue, (colorTrans.alpha + (color.alpha - colorTrans.alpha) * (uint8)(int32)fAlphaBlendMulB)); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 2], fX + SMALL_SECTOR_SIZE, fY + SMALL_SECTOR_SIZE, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDU + 1.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 2], TEXTURE_ADDV + 1.0f); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 2], color.red, color.green, color.blue, (colorTrans.alpha + (color.alpha - colorTrans.alpha) * (uint8)(int32)fAlphaBlendMulC)); RwIm3DVertexSetPos (&TempBufferRenderVertices[vidx + 3], fX + SMALL_SECTOR_SIZE, fY, fZ - _fWaterZOffset); RwIm3DVertexSetU (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDU + 1.0f); RwIm3DVertexSetV (&TempBufferRenderVertices[vidx + 3], TEXTURE_ADDV); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[vidx + 3], color.red, color.green, color.blue, (colorTrans.alpha + (color.alpha - colorTrans.alpha) * (uint8)(int32)fAlphaBlendMulD)); int32 iidx = TempBufferIndicesStored; TempBufferRenderIndexList[iidx + 0] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 1] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[iidx + 2] = TempBufferVerticesStored + 1; TempBufferRenderIndexList[iidx + 3] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[iidx + 4] = TempBufferVerticesStored + 3; TempBufferRenderIndexList[iidx + 5] = TempBufferVerticesStored + 2; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; } float CWaterLevel::CalcDistanceToWater(float fX, float fY) { const float fSectorMaxRenderDist = 250.0f; int32 nStartX = WATER_TO_SMALL_SECTOR_X(fX - fSectorMaxRenderDist + WATER_X_OFFSET) - 1; int32 nEndX = WATER_TO_SMALL_SECTOR_X(fX + fSectorMaxRenderDist + WATER_X_OFFSET) + 1; int32 nStartY = WATER_TO_SMALL_SECTOR_Y(fY - fSectorMaxRenderDist) - 1; int32 nEndY = WATER_TO_SMALL_SECTOR_Y(fY + fSectorMaxRenderDist) + 1; nStartX = clamp(nStartX, 0, MAX_SMALL_SECTORS - 1); nEndX = clamp(nEndX, 0, MAX_SMALL_SECTORS - 1); nStartY = clamp(nStartY, 0, MAX_SMALL_SECTORS - 1); nEndY = clamp(nEndY, 0, MAX_SMALL_SECTORS - 1); float fDistSqr = 1.0e10f; for ( int32 x = nStartX; x <= nEndX; x++ ) { for ( int32 y = nStartY; y <= nEndY; y++ ) { if ( aWaterFineBlockList[x][y] >= 0 ) { float fSectorX = WATER_FROM_SMALL_SECTOR_X(x) - WATER_X_OFFSET; float fSectorY = WATER_FROM_SMALL_SECTOR_Y(y); CVector2D vecDist ( fSectorX + SMALL_SECTOR_SIZE - fX, fSectorY + SMALL_SECTOR_SIZE - fY ); fDistSqr = Min(vecDist.MagnitudeSqr(), fDistSqr); } } } return clamp(Sqrt(fDistSqr) - 23.0f, 0.0f, fSectorMaxRenderDist); } void CWaterLevel::RenderAndEmptyRenderBuffer() { if ( TempBufferVerticesStored ) { LittleTest(); if ( RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV) ) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } } TempBufferIndicesStored = 0; TempBufferVerticesStored = 0; } bool CWaterLevel::GetGroundLevel(CVector const &vecPosn, float *pfOutLevel, ColData *pData, float fDistance) { CColPoint point; CEntity *entity; if ( !CWorld::ProcessVerticalLine(vecPosn + CVector(0.0f, 0.0f, fDistance), -fDistance, point, entity, true, false, false, false, true, false, nil) ) return false; *pfOutLevel = point.point.z; if ( pData != nil ) { pData->SurfaceType = point.surfaceB; pData->PieceType = point.pieceB; } return true; } bool CWaterLevel::IsLocationOutOfWorldBounds_WS(CVector const &vecPosn, int nOffset) { int32 x = int32((vecPosn.x / 50.0f) + 48.0f); int32 y = int32((vecPosn.y / 50.0f) + 40.0f); return x < nOffset || x >= 80 - nOffset || y < nOffset || y >= 80 - nOffset; } bool CWaterLevel::GetGroundLevel_WS(CVector const &vecPosn, float *pfOutLevel, ColData *pData, float fDistance) { if ( IsLocationOutOfWorldBounds_WS(vecPosn, 0) ) return false; else return GetGroundLevel(vecPosn, pfOutLevel, pData, fDistance); } bool CWaterLevel::GetWaterDepth(CVector const &vecPosn, float *pfDepth, float *pfLevelNoWaves, float *pfGroundLevel) { float fLevelNoWaves; float fGroundLevel; if ( !GetWaterLevelNoWaves(vecPosn.x, vecPosn.y, vecPosn.z, &fLevelNoWaves) ) return false; if ( !GetGroundLevel(vecPosn, &fGroundLevel, nil, 30.0f) ) fGroundLevel = -100.0; if ( pfDepth != nil ) *pfDepth = fLevelNoWaves - fGroundLevel; if ( pfLevelNoWaves != nil ) *pfLevelNoWaves = fLevelNoWaves; if ( pfGroundLevel != nil ) *pfGroundLevel = fGroundLevel; return true; } void CWaterLevel::RenderSeaBirds() { CVector cur_pos = TheCamera.GetPosition(); if ( !CCullZones::CamNoRain() && !CCullZones::PlayerNoRain() && (CWeather::NewWeatherType == WEATHER_SUNNY || CWeather::NewWeatherType == WEATHER_EXTRA_SUNNY) && CClock::ms_nGameClockHours > 6 && CClock::ms_nGameClockHours < 20 ) { static CVector prev_pos(0.0f, 0.0f, 0.0f); static CVector prev_front(0.0f, 0.0f, 0.0f); static int32 timecounter; if ( Abs(prev_pos.x - cur_pos.x) + Abs(prev_pos.y - cur_pos.y) + Abs(prev_pos.z - cur_pos.z) > 1.5f ) { prev_pos = cur_pos; timecounter = CTimer::GetTimeInMilliseconds(); } else if ( (CTimer::GetTimeInMilliseconds() - timecounter) > 5000 ) { static int32 birdgenTime = 0; if ( (CTimer::GetTimeInMilliseconds() - birdgenTime) > 1000 ) { birdgenTime = CTimer::GetTimeInMilliseconds(); CVector vecPos = cur_pos; float fAngle = CGeneral::GetRandomNumberInRange(90.0f, 150.0f); uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); float fCos = CParticle::Cos(nSinCosIdx); float fSin = CParticle::Sin(nSinCosIdx); vecPos.x += (fCos - fSin) * fAngle; vecPos.y += (fSin + fCos) * fAngle; vecPos.z += CGeneral::GetRandomNumberInRange(10.0f, 30.0f); CVector vecDir(CGeneral::GetRandomNumberInRange(-1.0f, 1.0f), CGeneral::GetRandomNumberInRange(-1.0f, 1.0f), 0.0f); CParticle::AddParticle(PARTICLE_BIRD_FRONT, vecPos, vecDir, nil, 0.0f, 0, 0, 0, 0); } } } } void CWaterLevel::RenderShipsOnHorizon() { #ifdef FIX_BUGS CVector cur_pos = FindPlayerCoors(); #else CVector cur_pos = FindPlayerPed()->GetPosition(); #endif static CVector prev_pos(0.0f, 0.0f, 0.0f); static CVector prev_front(0.0f, 0.0f, 0.0f); static int32 timecounter; if ( Abs(prev_pos.x - cur_pos.x) + Abs(prev_pos.y - cur_pos.y) + Abs(prev_pos.z - cur_pos.z) > 1.5f ) { prev_pos = cur_pos; timecounter = CTimer::GetTimeInMilliseconds(); } else if ( (CTimer::GetTimeInMilliseconds() - timecounter) > 5000 ) { static int32 shipgenTime = 0; if ( (CTimer::GetTimeInMilliseconds() - shipgenTime) > 4000 ) { shipgenTime = CTimer::GetTimeInMilliseconds(); CVector vecPos = cur_pos; float fAngle = CGeneral::GetRandomNumberInRange(450.0f, 750.0f); uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); float fCos = CParticle::Cos(nSinCosIdx); float fSin = CParticle::Sin(nSinCosIdx); vecPos.x += (fCos - fSin) * fAngle; vecPos.y += (fSin + fCos) * fAngle; float fLevelNoWaves; if ( GetWaterLevelNoWaves(vecPos.x, vecPos.y, vecPos.z, &fLevelNoWaves) ) { if ( IsLocationOutOfWorldBounds_WS(vecPos, 1) ) { vecPos.z = fLevelNoWaves + 9.5f; CVector vecDir ( CGeneral::GetRandomNumberInRange(-0.1f, 0.1f), 0.0f, 0.0f ); CParticle::AddParticle(PARTICLE_SHIP_SIDE, vecPos, vecDir, nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 7, 0); } } } } } void CWaterLevel::HandleSeaLifeForms() { if ( CReplay::IsPlayingBack() ) return; CVector cur_pos = FindPlayerPed()->GetPosition(); static CVector prev_pos(0.0f, 0.0f, 0.0f); static int32 timecounter; if ( Abs(prev_pos.x - cur_pos.x) + Abs(prev_pos.y - cur_pos.y) + Abs(prev_pos.z - cur_pos.z) > 1.5f ) { prev_pos = cur_pos; timecounter = CTimer::GetTimeInMilliseconds(); } else if ( (CTimer::GetTimeInMilliseconds() - timecounter) > 5000 ) { if ( CWaterCreatures::IsSpaceForMoreWaterCreatures() ) { for ( int32 i = 0; i < 3; i++ ) { CVector vecPos = cur_pos; float fAngle = CGeneral::GetRandomNumberInRange(15.0f, 30.0f); uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); float fCos = CParticle::Cos(nSinCosIdx); float fSin = CParticle::Sin(nSinCosIdx); vecPos.x += (fCos - fSin) * fAngle; vecPos.y += (fSin + fCos) * fAngle; CWaterCreatures::CreateOne(vecPos, -1); } } } CWaterCreatures::UpdateAll(); } void CWaterLevel::HandleBeachToysStuff(void) { #ifdef FIX_BUGS CVector cur_pos = FindPlayerCoors(); #else CVector cur_pos = FindPlayerPed()->GetPosition(); #endif static bool bBeachBallInit = true; static CVector FirstBeachBallPos = cur_pos; static bool bLoungeInit = true; static CVector FirstLoungePos = cur_pos; static CVector prev_pos(0.0f, 0.0f, 0.0f); static int32 timecounter; if ( Abs(prev_pos.x - cur_pos.x) + Abs(prev_pos.y - cur_pos.y) + Abs(prev_pos.z - cur_pos.z) > 1.5f ) { prev_pos = cur_pos; timecounter = CTimer::GetTimeInMilliseconds(); } else if ( (CTimer::GetTimeInMilliseconds() - timecounter) > 5000 ) { static int32 toygenTime = CTimer::GetTimeInMilliseconds(); if ( (CTimer::GetTimeInMilliseconds() - toygenTime) > 20000 ) { toygenTime = CTimer::GetTimeInMilliseconds(); if ( bBeachBallInit || (cur_pos - FirstBeachBallPos).MagnitudeSqr() > 6400.0f ) { for ( int32 i = 0; i < 3; i++ ) { CVector vecPos = cur_pos; float fAngle = CGeneral::GetRandomNumberInRange(20.0f, 35.0f); uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); float fCos = CParticle::Cos(nSinCosIdx); float fSin = CParticle::Sin(nSinCosIdx); vecPos.x += (fCos - fSin) * fAngle; vecPos.y += (fSin + fCos) * fAngle; if ( TheCamera.IsSphereVisible(vecPos, 1.0f, &TheCamera.GetCameraMatrix()) ) { float fWaterLevel; if ( !GetWaterLevel(vecPos.x, vecPos.y, vecPos.z, &fWaterLevel, false) ) { float fGroundLevel; ColData coldata; if ( GetGroundLevel(vecPos, &fGroundLevel, &coldata, 30.0f) ) { if ( coldata.SurfaceType == SURFACE_SAND ) { CEntity *toy = CreateBeachToy(vecPos, BEACHTOY_BALL); if ( toy ) { FirstBeachBallPos = cur_pos; bBeachBallInit = false; i = 10; } } } } } } } if ( bLoungeInit || (cur_pos - FirstLoungePos).MagnitudeSqr() > 6400.0f ) { for ( int32 i = 0; i < 5; i++ ) { CVector vecPos = cur_pos; float fAngle = CGeneral::GetRandomNumberInRange(20.0f, 35.0f); uint16 nSinCosIdx = CGeneral::GetRandomNumber() % (CParticle::SIN_COS_TABLE_SIZE-1); float fCos = CParticle::Cos(nSinCosIdx); float fSin = CParticle::Sin(nSinCosIdx); vecPos.x += (fCos - fSin) * fAngle; vecPos.y += (fSin + fCos) * fAngle; if ( TheCamera.IsSphereVisible(vecPos, 2.0f, &TheCamera.GetCameraMatrix()) ) { float fWaterLevel; if ( !GetWaterLevel(vecPos.x, vecPos.y, vecPos.z, &fWaterLevel, false) ) { float fGroundLevel; ColData coldata; if ( GetGroundLevel(vecPos, &fGroundLevel, &coldata, 30.0f) ) { if ( coldata.SurfaceType == SURFACE_SAND ) { CEntity *toy = CreateBeachToy(vecPos, BEACHTOY_ANY_LOUNGE); if ( toy ) { toy->SetHeading(DEGTORAD(CGeneral::GetRandomNumberInRange(0.0f, 359.0f))); FirstLoungePos = cur_pos; bLoungeInit = false; } } } } } } } } } } CEntity * CWaterLevel::CreateBeachToy(CVector const &vec, eBeachToy beachtoy) { if (CObject::nNoTempObjects >= NUMTEMPOBJECTS) return nil; int finalToy = beachtoy; bool isStatic = false; int model = MI_BEACHBALL; switch (beachtoy) { case BEACHTOY_ANY_LOUNGE: switch ( CGeneral::GetRandomNumber() & 7 ) { case 1: case 7: finalToy = BEACHTOY_LOUNGE_WOOD_UP; break; case 3: case 5: finalToy = BEACHTOY_LOUNGE_TOWEL_UP; break; default: finalToy = BEACHTOY_LOUNGE_WOOD_ON; break; } break; case BEACHTOY_ANY_TOWEL: switch ( CGeneral::GetRandomNumber() & 7 ) { case 1: case 7: finalToy = BEACHTOY_TOWEL2; break; case 2: case 6: finalToy = BEACHTOY_TOWEL3; break; case 3: case 5: finalToy = BEACHTOY_TOWEL4; break; default: finalToy = BEACHTOY_TOWEL1; break; } if (CObject::nNoTempObjects >= 35) { return nil; } default: break; } switch (finalToy) { case BEACHTOY_BALL: isStatic = false; model = MI_BEACHBALL; break; case BEACHTOY_LOUNGE_WOOD_UP: isStatic = false; model = MI_LOUNGE_WOOD_UP; break; case BEACHTOY_LOUNGE_TOWEL_UP: isStatic = false; model = MI_LOUNGE_TOWEL_UP; break; case BEACHTOY_LOUNGE_WOOD_ON: isStatic = false; model = MI_LOUNGE_WOOD_DN; break; case BEACHTOY_LOTION: model = MI_LOTION; isStatic = true; break; case BEACHTOY_TOWEL1: model = MI_BEACHTOWEL01; isStatic = true; break; case BEACHTOY_TOWEL2: model = MI_BEACHTOWEL02; isStatic = true; break; case BEACHTOY_TOWEL3: model = MI_BEACHTOWEL03; isStatic = true; break; case BEACHTOY_TOWEL4: model = MI_BEACHTOWEL04; isStatic = true; break; default: break; } CObject *toy = new CObject(model, true); if (toy) { toy->SetPosition(vec); toy->GetMatrix().UpdateRW(); toy->m_vecMoveSpeed = CVector(0.f, 0.f, 0.f); toy->m_vecTurnSpeed = CVector(0.f, 0.f, 0.f); toy->ObjectCreatedBy = TEMP_OBJECT; toy->bIsStatic = isStatic; CObject::nNoTempObjects++; toy->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 43200000; CWorld::Add(toy); return toy; } else return nil; } ================================================ FILE: src/render/WaterLevel.h ================================================ #pragma once #define WATER_X_OFFSET (400.0f) #define WATER_Z_OFFSET (0.5f) #define NO_WATER -128 #define MAX_SMALL_SECTORS 128 #define MAX_LARGE_SECTORS 64 #define MAX_HUGE_SECTORS 32 #define MAX_EXTRAHUGE_SECTORS 16 #define SMALL_SECTOR_SIZE 32 #define LARGE_SECTOR_SIZE 64 #define HUGE_SECTOR_SIZE 128 #define EXTRAHUGE_SECTOR_SIZE 256 #define WATER_START_X -2048.0f #define WATER_END_X 2048.0f #define WATER_START_Y -2048.0f #define WATER_END_Y 2048.0f #define WATER_WIDTH ((WATER_END_X - WATER_START_X)) #define WATER_HEIGHT ((WATER_END_Y - WATER_START_Y)) #define WATER_UNSIGN_X(x) ( (x) + (WATER_WIDTH /2) ) #define WATER_UNSIGN_Y(y) ( (y) + (WATER_HEIGHT/2) ) #define WATER_SIGN_X(x) ( (x) - (WATER_WIDTH /2) ) #define WATER_SIGN_Y(y) ( (y) - (WATER_HEIGHT/2) ) // 64x64 Large blocks 64x64 each #define WATER_TO_BLOCK_X(x) ( WATER_UNSIGN_X(x) / WATER_BLOCK_SECTORS ) #define WATER_TO_BLOCK_Y(x) ( WATER_UNSIGN_Y(x) / WATER_BLOCK_SECTORS ) // 128x128 Small blocks 32x32 each #define WATER_TO_FINEBLOCK_X(x) ( WATER_UNSIGN_X(x) / WATER_FINEBLOCK_SECTORS ) #define WATER_TO_FINEBLOCK_Y(x) ( WATER_UNSIGN_Y(x) / WATER_FINEBLOCK_SECTORS ) // 32 #define WATER_SMALL_X(x) ( WATER_UNSIGN_X(x) / MAX_SMALL_SECTORS ) #define WATER_SMALL_Y(y) ( WATER_UNSIGN_Y(y) / MAX_SMALL_SECTORS ) #define WATER_FROM_SMALL_SECTOR_X(x) ( ((x) - (MAX_SMALL_SECTORS/2) ) * SMALL_SECTOR_SIZE ) #define WATER_FROM_SMALL_SECTOR_Y(y) ( ((y) - (MAX_SMALL_SECTORS/2) ) * SMALL_SECTOR_SIZE ) #define WATER_TO_SMALL_SECTOR_X(x) ( WATER_UNSIGN_X(x) / SMALL_SECTOR_SIZE ) #define WATER_TO_SMALL_SECTOR_Y(y) ( WATER_UNSIGN_Y(y) / SMALL_SECTOR_SIZE ) // 64 #define WATER_LARGE_X(x) ( WATER_UNSIGN_X(x) / MAX_LARGE_SECTORS ) #define WATER_LARGE_Y(y) ( WATER_UNSIGN_Y(y) / MAX_LARGE_SECTORS ) #define WATER_FROM_LARGE_SECTOR_X(x) ( ((x) - (MAX_LARGE_SECTORS/2) ) * LARGE_SECTOR_SIZE ) #define WATER_FROM_LARGE_SECTOR_Y(y) ( ((y) - (MAX_LARGE_SECTORS/2) ) * LARGE_SECTOR_SIZE ) #define WATER_TO_LARGE_SECTOR_X(x) ( WATER_UNSIGN_X(x) / LARGE_SECTOR_SIZE ) #define WATER_TO_LARGE_SECTOR_Y(y) ( WATER_UNSIGN_Y(y) / LARGE_SECTOR_SIZE ) // 128 #define WATER_HUGE_X(x) ( WATER_UNSIGN_X(x) / MAX_HUGE_SECTORS ) #define WATER_HUGE_Y(y) ( WATER_UNSIGN_Y(y) / MAX_HUGE_SECTORS ) #define WATER_FROM_HUGE_SECTOR_X(x) ( ((x) - (MAX_HUGE_SECTORS/2) ) * HUGE_SECTOR_SIZE ) #define WATER_FROM_HUGE_SECTOR_Y(y) ( ((y) - (MAX_HUGE_SECTORS/2) ) * HUGE_SECTOR_SIZE ) #define WATER_TO_HUGE_SECTOR_X(x) ( WATER_UNSIGN_X(x) / HUGE_SECTOR_SIZE ) #define WATER_TO_HUGE_SECTOR_Y(y) ( WATER_UNSIGN_Y(y) / HUGE_SECTOR_SIZE ) // 256 #define WATER_EXTRAHUGE_X(x) ( WATER_UNSIGN_X(x) / MAX_EXTRAHUGE_SECTORS ) #define WATER_EXTRAHUGE_Y(y) ( WATER_UNSIGN_Y(y) / MAX_EXTRAHUGE_SECTORS ) #define WATER_FROM_EXTRAHUGE_SECTOR_X(x) ( ((x) - (MAX_EXTRAHUGE_SECTORS/2)) * EXTRAHUGE_SECTOR_SIZE ) #define WATER_FROM_EXTRAHUGE_SECTOR_Y(y) ( ((y) - (MAX_EXTRAHUGE_SECTORS/2)) * EXTRAHUGE_SECTOR_SIZE ) #define WATER_TO_EXTRAHUGE_SECTOR_X(x) ( WATER_UNSIGN_X(x) / EXTRAHUGE_SECTOR_SIZE ) #define WATER_TO_EXTRAHUGE_SECTOR_Y(y) ( WATER_UNSIGN_Y(y) / EXTRAHUGE_SECTOR_SIZE ) struct ColData { uint8 SurfaceType; uint8 PieceType; }; enum eBeachToy { BEACHTOY_0 = 0, BEACHTOY_BALL, BEACHTOY_LOUNGE_WOOD_UP, BEACHTOY_LOUNGE_TOWEL_UP, BEACHTOY_LOUNGE_WOOD_ON, BEACHTOY_ANY_LOUNGE, BEACHTOY_LOTION, BEACHTOY_TOWEL1, BEACHTOY_TOWEL2, BEACHTOY_TOWEL3, BEACHTOY_TOWEL4, BEACHTOY_ANY_TOWEL, }; extern RwRaster* gpWaterRaster; extern bool gbDontRenderWater; class CEntity; class CWaterLevel { public: static int32 ms_nNoOfWaterLevels; static float ms_aWaterZs[48]; static CRect ms_aWaterRects[48]; static int8 aWaterBlockList[MAX_LARGE_SECTORS][MAX_LARGE_SECTORS]; // 64x64 Large blocks 64x64 each static int8 aWaterFineBlockList[MAX_SMALL_SECTORS][MAX_SMALL_SECTORS]; // 128x128 Small blocks 32x32 each static bool WavesCalculatedThisFrame; static bool RequireWavySector; static bool MaskCalculatedThisFrame; static CVector PreCalculatedMaskPosn; static bool m_bRenderSeaBed; static int32 m_nRenderWaterLayers; static RpAtomic *ms_pWavyAtomic; static RpAtomic *ms_pMaskAtomic; static void Initialise(Const char *pWaterDat); // out of class in III PC and later because of SecuROM static void Shutdown(); static void CreateWavyAtomic(); static void DestroyWavyAtomic(); static void AddWaterLevel(float fXLeft, float fYBottom, float fXRight, float fYTop, float fLevel); static bool WaterLevelAccordingToRectangles(float fX, float fY, float *pfOutLevel = nil); static bool TestVisibilityForFineWaterBlocks(const CVector &worldPos); static void RemoveIsolatedWater(); static bool GetWaterLevel(float fX, float fY, float fZ, float *pfOutLevel, bool bDontCheckZ); static bool GetWaterLevel(CVector coors, float *pfOutLevel, bool bDontCheckZ) { return GetWaterLevel(coors.x, coors.y, coors.z, pfOutLevel, bDontCheckZ); } static bool GetWaterLevelNoWaves(float fX, float fY, float fZ, float *pfOutLevel); static float GetWaterWavesOnly(short x, short y); // unused static CVector GetWaterNormal(float fX, float fY); static void RenderWater(); static void RenderTransparentWater(void); // unused static void RenderOneFlatSmallWaterPoly (float fX, float fY, float fZ, RwRGBA const &color); // inlined static void RenderOneFlatLargeWaterPoly (float fX, float fY, float fZ, RwRGBA const &color); static void RenderOneFlatHugeWaterPoly (float fX, float fY, float fZ, RwRGBA const &color); static void RenderOneFlatExtraHugeWaterPoly(float fX, float fY, float fZ, RwRGBA const &color); // inlined static void RenderOneWavySector (float fX, float fY, float fZ, RwRGBA const &color, bool bDontRender = false); // unused #ifdef PC_WATER static void RenderWavyMask(float fX, float fY, float fZ, float fSectorX, float fSectorY, float fCamPosX, float fCamPosY, float fCamDirX, float fCamDirY, RwRGBA const&color); #else static void RenderWavyMask(float fX, float fY, float fZ, float fSectorX, float fSectorY, int32 nCamDirX, int32 nCamDirY, RwRGBA const&color); #endif #ifdef PC_WATER static void PreCalcWaterGeometry(void); static bool PreCalcWavySector(RwRGBA const &color); //fucked up static bool PreCalcWavyMask(float fX, float fY, float fZ, float fSectorX, float fSectorY, float fCamPosX, float fCamPosY, float fCamDirX, float fCamDirY, RwRGBA const&color); #endif static void RenderBoatWakes(void); static void RenderWakeSegment(CVector2D &vecA, CVector2D &vecB, CVector2D &vecC, CVector2D &vecD, float &fSizeA, float &fSizeB, float &fAlphaA, float &fAlphaB, float &fWakeZ); // unused static void RenderOneSlopedUnderWaterPoly(float fX, float fY, float fZ, RwRGBA const&color); // UNUSED static void RenderOneFlatSmallWaterPolyBlended(float fX, float fY, float fZ, float fCamX, float fCamY, RwRGBA const &color, RwRGBA const &colorTrans, float fDrawDist); static float CalcDistanceToWater(float fX, float fY); static void RenderAndEmptyRenderBuffer(); static bool GetGroundLevel(CVector const &vecPosn, float *pfOutLevel, ColData *pData, float fDistance); // unused static bool IsLocationOutOfWorldBounds_WS(CVector const &vecPosn, int nOffset); // unused static bool GetGroundLevel_WS(CVector const & vecPosn, float *pfOutLevel, ColData *pData, float fDistance); static bool GetWaterDepth(CVector const &vecPosn, float *pfDepth, float *pfLevelNoWaves, float *pfGroundLevel); static void RenderSeaBirds(); static void RenderShipsOnHorizon(); static void HandleSeaLifeForms(); static void HandleBeachToysStuff(void); static CEntity *CreateBeachToy(CVector const &vec, eBeachToy beachtoy); }; ================================================ FILE: src/render/Weather.cpp ================================================ #include "common.h" #include "Weather.h" #include "Camera.h" #include "Clock.h" #include "CutsceneMgr.h" #include "DMAudio.h" #include "General.h" #include "Pad.h" #include "PlayerPed.h" #include "Particle.h" #include "RenderBuffer.h" #include "Stats.h" #include "Shadows.h" #include "Timecycle.h" #include "Timer.h" #include "Vehicle.h" #include "World.h" #include "ZoneCull.h" #include "SpecialFX.h" #include "Replay.h" int32 CWeather::SoundHandle = -1; int32 CWeather::WeatherTypeInList; int16 CWeather::OldWeatherType; int16 CWeather::NewWeatherType; int16 CWeather::ForcedWeatherType; bool CWeather::LightningFlash; bool CWeather::LightningBurst; uint32 CWeather::LightningStart; uint32 CWeather::LightningFlashLastChange; uint32 CWeather::WhenToPlayLightningSound; uint32 CWeather::LightningDuration; int32 CWeather::StreamAfterRainTimer; float CWeather::ExtraSunnyness; float CWeather::Foggyness; float CWeather::CloudCoverage; float CWeather::Wind; float CWeather::Rain; float CWeather::InterpolationValue; float CWeather::WetRoads; float CWeather::Rainbow; float CWeather::SunGlare; float CWeather::WindClipped; float CWeather::TrafficLightBrightness; bool CWeather::bScriptsForceRain; tRainStreak Streaks[NUM_RAIN_STREAKS]; int16 WeatherTypesList[] = { WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_CLOUDY, WEATHER_RAINY, WEATHER_RAINY, WEATHER_RAINY, WEATHER_RAINY, WEATHER_CLOUDY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY }; int16 WeatherTypesList_WithHurricanes[] = { WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_CLOUDY, WEATHER_HURRICANE, WEATHER_HURRICANE, WEATHER_CLOUDY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_CLOUDY, WEATHER_HURRICANE, WEATHER_HURRICANE, WEATHER_HURRICANE, WEATHER_CLOUDY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY, WEATHER_EXTRA_SUNNY }; const float Windyness[] = { 0.25f,// WEATHER_SUNNY 0.7f, // WEATHER_CLOUDY 1.0f, // WEATHER_RAINY 0.0f, // WEATHER_FOGGY 0.0f, // WEATHER_EXTRA_SUNNY 2.0f, // WEATHER_HURRICANE 0.0f }; #define MIN_TIME_BETWEEN_LIGHTNING_FLASH_CHANGES (50) #define RAIN_CHANGE_SPEED (0.003f) #define DROPLETS_LEFT_OFFSET (10.0f) #define DROPLETS_RIGHT_OFFSET (10.0f) #define DROPLETS_TOP_OFFSET (10.0f) #define DROPLETS_BOTTOM_OFFSET (10.0f) #define STREAK_U (10.0f) #define STREAK_V (18.0f) #define LARGE_STREAK_COEFFICIENT (1.23f) #define STREAK_MIN_DISTANCE (8.0f) #define STREAK_MAX_DISTANCE (16.0f) #define SPLASH_CHECK_RADIUS (7.0f) #define SPLASH_OFFSET_RADIUS (2.0f) #define STREAK_LIFETIME (4.0f) #define STREAK_INTEROLATION_TIME (0.3f) #define RAIN_COLOUR_R (200) #define RAIN_COLOUR_G (200) #define RAIN_COLOUR_B (256) #define RAIN_ALPHA (255) void CWeather::Init(void) { NewWeatherType = WEATHER_EXTRA_SUNNY; bScriptsForceRain = false; OldWeatherType = WEATHER_EXTRA_SUNNY; InterpolationValue = 0.0f; WhenToPlayLightningSound = 0; WeatherTypeInList = 0; ForcedWeatherType = WEATHER_RANDOM; SoundHandle = DMAudio.CreateEntity(AUDIOTYPE_WEATHER, (void*)1); if (SoundHandle >= 0) DMAudio.SetEntityStatus(SoundHandle, true); } void CWeather::Update(void) { if(!CReplay::IsPlayingBack()){ float fNewInterpolation = (CClock::GetMinutes() + CClock::GetSeconds()/60.0f)/60.0f; if (fNewInterpolation < InterpolationValue) { // new hour OldWeatherType = NewWeatherType; if (ForcedWeatherType >= 0) NewWeatherType = ForcedWeatherType; else { WeatherTypeInList = (WeatherTypeInList + 1) % ARRAY_SIZE(WeatherTypesList); NewWeatherType = CStats::NoMoreHurricanes ? WeatherTypesList[WeatherTypeInList] : WeatherTypesList_WithHurricanes[WeatherTypeInList]; } } InterpolationValue = fNewInterpolation; } #ifndef FINAL if (CPad::GetPad(1)->GetRightShockJustDown()) { NewWeatherType = (NewWeatherType + 1) % WEATHER_TOTAL; OldWeatherType = NewWeatherType; } #endif // Lightning if (NewWeatherType != WEATHER_RAINY || OldWeatherType != WEATHER_RAINY) { LightningFlash = false; LightningBurst = false; } else{ if (LightningBurst) { if ((CGeneral::GetRandomNumber() & 255) >= 32) { // 0.875 probability if (CTimer::GetTimeInMilliseconds() - LightningFlashLastChange > MIN_TIME_BETWEEN_LIGHTNING_FLASH_CHANGES) { bool bOldLightningFlash = LightningFlash; LightningFlash = CGeneral::GetRandomTrueFalse(); if (LightningFlash != bOldLightningFlash) LightningFlashLastChange = CTimer::GetTimeInMilliseconds(); } } else { // 0.125 probability LightningBurst = false; LightningDuration = Min(CTimer::GetFrameCounter() - LightningStart, 20); LightningFlash = false; WhenToPlayLightningSound = CTimer::GetTimeInMilliseconds() + 150 * (20 - LightningDuration); } } else { if (CGeneral::GetRandomNumber() >= 200) { // lower probability on PC due to randomness bug LightningFlash = false; } else { LightningBurst = true; LightningStart = CTimer::GetFrameCounter(); LightningFlashLastChange = CTimer::GetTimeInMilliseconds(); LightningFlash = true; } } } if (WhenToPlayLightningSound && CTimer::GetTimeInMilliseconds() > WhenToPlayLightningSound) { DMAudio.PlayOneShot(SoundHandle, SOUND_LIGHTNING, LightningDuration); CPad::GetPad(0)->StartShake(40 * LightningDuration + 100, 2 * LightningDuration + 80); WhenToPlayLightningSound = 0; } // Wet roads if (OldWeatherType == WEATHER_RAINY || OldWeatherType == WEATHER_HURRICANE) { if (NewWeatherType == WEATHER_RAINY || NewWeatherType == WEATHER_HURRICANE) WetRoads = 1.0f; else WetRoads = 1.0f - InterpolationValue; } else { if (NewWeatherType == WEATHER_RAINY || NewWeatherType == WEATHER_HURRICANE) WetRoads = InterpolationValue; else WetRoads = 0.0f; } // Rain float fNewRain; if (NewWeatherType == WEATHER_RAINY || NewWeatherType == WEATHER_HURRICANE) { // if raining for >1 hour, values: 0, 0.33, switching every ~16.5s fNewRain = (((uint16)CTimer::GetTimeInMilliseconds() >> 14) & 1) * 0.33f; if (OldWeatherType != WEATHER_RAINY && OldWeatherType != WEATHER_HURRICANE) { if (InterpolationValue < 0.4f) // if rain has just started (<24 minutes), always 0.5 fNewRain = 0.5f; else // if rain is ongoing for >24 minutes, values: 0.25, 0.5, switching every ~16.5s fNewRain = 0.25f + (((uint16)CTimer::GetTimeInMilliseconds() >> 14) & 1) * 0.25f; } fNewRain = Max(fNewRain, 0.5f); } else fNewRain = 0.0f; Rain = fNewRain; // Clouds if (OldWeatherType != WEATHER_SUNNY && OldWeatherType != WEATHER_EXTRA_SUNNY) CloudCoverage = 1.0f - InterpolationValue; else CloudCoverage = 0.0f; if (NewWeatherType != WEATHER_SUNNY && OldWeatherType != WEATHER_EXTRA_SUNNY) CloudCoverage += InterpolationValue; // Fog if (OldWeatherType == WEATHER_FOGGY) Foggyness = 1.0f - InterpolationValue; else Foggyness = 0.0f; if (NewWeatherType == WEATHER_FOGGY) Foggyness += InterpolationValue; // Extra Sunnyness if (OldWeatherType == WEATHER_EXTRA_SUNNY) ExtraSunnyness = 1.0f - InterpolationValue; else ExtraSunnyness = 0.0f; if (NewWeatherType == WEATHER_EXTRA_SUNNY) ExtraSunnyness += InterpolationValue; // Rainbow if (OldWeatherType == WEATHER_CLOUDY && (NewWeatherType == WEATHER_SUNNY || NewWeatherType == WEATHER_EXTRA_SUNNY) && InterpolationValue < 0.5f && CClock::GetHours() > 6 && CClock::GetHours() < 21) Rainbow = 1.0f - 4.0f * Abs(InterpolationValue - 0.25f) / 4.0f; else Rainbow = 0.0f; // Sun Glare if (OldWeatherType == WEATHER_EXTRA_SUNNY) SunGlare = 1.0f - InterpolationValue; else SunGlare = 0.0f; if (NewWeatherType == WEATHER_EXTRA_SUNNY) SunGlare += InterpolationValue; if (SunGlare > 0.0f) { SunGlare *= Min(1.0f, 7.0 * CTimeCycle::GetSunDirection().z); SunGlare = clamp(SunGlare, 0.0f, 1.0f); if (!CSpecialFX::bSnapShotActive) SunGlare *= (1.0f - (CGeneral::GetRandomNumber()&0x1F)*0.007f); } Wind = InterpolationValue * Windyness[NewWeatherType] + (1.0f - InterpolationValue) * Windyness[OldWeatherType]; WindClipped = Min(1.0f, Wind); if (CClock::GetHours() > 20) TrafficLightBrightness = 1.0f; else if (CClock::GetHours() > 19) TrafficLightBrightness = CClock::GetMinutes() / 60.0f; else if (CClock::GetHours() > 6) TrafficLightBrightness = 0.0f; else if (CClock::GetHours() > 5) TrafficLightBrightness = 1.0f - CClock::GetMinutes() / 60.0f; else TrafficLightBrightness = 1.0f; TrafficLightBrightness = Max(WetRoads, TrafficLightBrightness); TrafficLightBrightness = Max(Foggyness, TrafficLightBrightness); TrafficLightBrightness = Max(Rain, TrafficLightBrightness); AddRain(); if ((NewWeatherType == WEATHER_SUNNY || NewWeatherType == WEATHER_EXTRA_SUNNY) && !CGame::IsInInterior() && !CCutsceneMgr::IsRunning() && (CTimer::GetFrameCounter() & 7) == 0) { #ifdef FIX_BUGS if (FindPlayerPed() && (!FindPlayerPed()->CheckIfInTheAir() || FindPlayerPed()->CheckIfInTheAir() && FindPlayerPed()->GetPosition().z < 7.5f && CClock::GetHours() > 6 && CClock::GetHours() < 18)) #else if (!FindPlayerPed()->CheckIfInTheAir() || FindPlayerPed()->CheckIfInTheAir() && FindPlayerPed()->GetPosition().z < 7.5f && CClock::GetHours() > 6 && CClock::GetHours() < 18) #endif AddHeatHaze(); } if ((NewWeatherType == WEATHER_SUNNY || NewWeatherType == WEATHER_EXTRA_SUNNY) && !CGame::IsInInterior() && !CCutsceneMgr::IsRunning()) AddBeastie(); } void CWeather::AddHeatHaze() { if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED) return; CVector pos; pos.x = SCREEN_WIDTH*0.5f; if(TheCamera.GetLookingForwardFirstPerson()) pos.y = CGeneral::GetRandomNumberInRange(SCREEN_HEIGHT*0.25f, SCREEN_HEIGHT*0.9f); else pos.y = CGeneral::GetRandomNumberInRange(SCREEN_HEIGHT*0.4f, SCREEN_HEIGHT*0.9f); pos.z = 100.0f; CParticle::AddParticle(PARTICLE_HEATHAZE_IN_DIST, pos, CVector(0.0f, 0.0f, 0.0f)); } void CWeather::AddBeastie() { if(FindPlayerVehicle() || CTimer::GetFrameCounter()%10 || (CGeneral::GetRandomNumber()&5) == 0) return; CVector pos = TheCamera.GetPosition(); float dist = CGeneral::GetRandomNumberInRange(90.0f, 60.0f); int angle = CGeneral::GetRandomNumber() % CParticle::SIN_COS_TABLE_SIZE; float c = CParticle::m_CosTable[angle]; float s = CParticle::m_SinTable[angle]; pos.x += dist*(c - s); pos.y += dist*(c + s); pos.z += CGeneral::GetRandomNumberInRange(7.5f, 30.0f); CParticle::AddParticle(PARTICLE_BEASTIE, pos, CVector(0.0f, 0.0f, 0.0f)); } void CWeather::ForceWeather(int16 weather) { ForcedWeatherType = weather; } void CWeather::ForceWeatherNow(int16 weather) { OldWeatherType = weather; NewWeatherType = weather; ForcedWeatherType = weather; } void CWeather::ReleaseWeather() { ForcedWeatherType = -1; } void CWeather::AddSplashesDuringHurricane() { RwRGBA colour = { 255, 255, 255, 32 }; CVector pos = TheCamera.pTargetEntity ? TheCamera.pTargetEntity->GetPosition() : TheCamera.GetPosition(); bool foundGround; float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &foundGround) + 0.1f; if(!foundGround) groundZ = pos.z + 0.5f; for(int i = 0; i < 20; i++){ float dist = (CGeneral::GetRandomNumber()&0xFF)/255.0f + CGeneral::GetRandomNumberInRange(-10.0f, 30.0f); float angle; uint8 rnd = CGeneral::GetRandomNumber(); if(rnd&1) angle = (CGeneral::GetRandomNumber()&0x7F)/128.0f * TWOPI; else angle = TheCamera.Orientation + (rnd-128)/160.0f; pos.x = TheCamera.GetPosition().x + dist*Sin(angle); pos.y = TheCamera.GetPosition().y + dist*Cos(angle); pos.z = groundZ; if(foundGround) CParticle::AddParticle(PARTICLE_GROUND_STEAM, pos, CVector(-0.002f, -0.002f, 0.015f), nil, 0.0f, colour); } } static int startStreamAfterRain; void CWeather::AddStreamAfterRain() { if(CClock::GetHours() > 6 && CClock::GetHours() < 18){ RwRGBA colour = { 255, 255, 255, 24 }; CVector pos = TheCamera.pTargetEntity ? TheCamera.pTargetEntity->GetPosition() : TheCamera.GetPosition(); bool foundGround; float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &foundGround) + 0.2f; if(!foundGround) groundZ = pos.z + 0.75f; for(int i = 0; i < 20; i++){ float dist = (CGeneral::GetRandomNumber()&0xFF)/255.0f + CGeneral::GetRandomNumberInRange(-10.0f, 30.0f); float angle; uint8 rnd = CGeneral::GetRandomNumber(); if(rnd&1) angle = (CGeneral::GetRandomNumber()&0x7F)/128.0f * TWOPI; else angle = TheCamera.Orientation + (rnd-128)/160.0f; pos.x = TheCamera.GetPosition().x + dist*Sin(angle); pos.y = TheCamera.GetPosition().y + dist*Cos(angle); pos.z = groundZ; CParticle::AddParticle(PARTICLE_GROUND_STEAM, pos, CVector(0.0f, 0.0f, 0.015f), nil, 0.0f, colour); } }else{ startStreamAfterRain = 0; StreamAfterRainTimer = 800; } } void CWeather::AddRain() { if (CCullZones::CamNoRain() || CCullZones::PlayerNoRain()) return; if (TheCamera.GetLookingLRBFirstPerson()) { CVehicle* pVehicle = FindPlayerVehicle(); if (pVehicle && pVehicle->CarHasRoof()) { CParticle::RemovePSystem(PARTICLE_RAINDROP_2D); return; } } if(Rain > 0.0){ startStreamAfterRain = 1; StreamAfterRainTimer = 800; }else if(startStreamAfterRain){ if(StreamAfterRainTimer > 0){ AddStreamAfterRain(); StreamAfterRainTimer--; }else{ startStreamAfterRain = 0; StreamAfterRainTimer = 800; } } if (Wind > 1.1f) AddSplashesDuringHurricane(); if (Rain <= 0.1f) return; static RwRGBA colour; int numDrops = 5.0f * Rain; int numSplashes = 2.0f * Rain; CVector pos, dir; for(int i = 0; i < numDrops; i++){ pos.x = CGeneral::GetRandomNumberInRange(0, (int)SCREEN_WIDTH); pos.y = CGeneral::GetRandomNumberInRange(0, (int)SCREEN_HEIGHT/5); pos.z = 0.0f; dir.x = 0.0f; dir.y = CGeneral::GetRandomNumberInRange(30.0f, 40.0f); dir.z = 0.0f; CParticle::AddParticle(PARTICLE_RAINDROP_2D, pos, dir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.75f), 0, 0, (int)Rain&3, 0); pos.x = CGeneral::GetRandomNumberInRange(0, (int)SCREEN_WIDTH); pos.y = CGeneral::GetRandomNumberInRange((int)SCREEN_HEIGHT/5, (int)SCREEN_HEIGHT/2); pos.z = 0.0f; dir.x = 0.0f; dir.y = CGeneral::GetRandomNumberInRange(30.0f, 40.0f); dir.z = 0.0f; CParticle::AddParticle(PARTICLE_RAINDROP_2D, pos, dir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.75f), 0, 0, (int)Rain&3, 0); pos.x = CGeneral::GetRandomNumberInRange(0, (int)SCREEN_WIDTH); pos.y = 0.0f; pos.z = 0.0f; dir.x = 0.0f; dir.y = CGeneral::GetRandomNumberInRange(30.0f, 40.0f); dir.z = 0.0f; CParticle::AddParticle(PARTICLE_RAINDROP_2D, pos, dir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.75f), 0, 0, (int)Rain&3, 0); float dist = CGeneral::GetRandomNumberInRange(0.0f, Max(10.0f*Rain, 40.0f)/2.0f); float angle; uint8 rnd = CGeneral::GetRandomNumber(); if(rnd&1) angle = (CGeneral::GetRandomNumber()&0x7F)/128.0f * TWOPI; else angle = TheCamera.Orientation + (rnd-128)/160.0f; pos.x = TheCamera.GetPosition().x + dist*Sin(angle); pos.y = TheCamera.GetPosition().y + dist*Cos(angle); pos.z = 0.0f; CColPoint point; CEntity *ent; if(CWorld::ProcessVerticalLine(pos+CVector(0.0f, 0.0f, 40.0f), -40.0f, point, ent, true, false, false, false, true, false, nil)){ pos.z = point.point.z; for(int j = 0; j < numSplashes+15; j++){ CVector pos2 = pos; pos2.x += CGeneral::GetRandomNumberInRange(-15.0f, 15.0f); pos2.y += CGeneral::GetRandomNumberInRange(-15.0f, 15.0f); if(CGeneral::GetRandomNumber() & 1) CParticle::AddParticle(PARTICLE_RAIN_SPLASH, pos2, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, colour); else CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, pos2, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, colour); } } } } void RenderOneRainStreak(CVector pos, CVector unused, int intensity, bool scale, float distance) { static float RandomTex; static float RandomTexX; static float RandomTexY; TempBufferRenderIndexList[TempBufferIndicesStored + 0] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[TempBufferIndicesStored + 1] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[TempBufferIndicesStored + 2] = TempBufferVerticesStored + 1; TempBufferRenderIndexList[TempBufferIndicesStored + 3] = TempBufferVerticesStored + 0; TempBufferRenderIndexList[TempBufferIndicesStored + 4] = TempBufferVerticesStored + 3; TempBufferRenderIndexList[TempBufferIndicesStored + 5] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[TempBufferIndicesStored + 6] = TempBufferVerticesStored + 1; TempBufferRenderIndexList[TempBufferIndicesStored + 7] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[TempBufferIndicesStored + 8] = TempBufferVerticesStored + 4; TempBufferRenderIndexList[TempBufferIndicesStored + 9] = TempBufferVerticesStored + 2; TempBufferRenderIndexList[TempBufferIndicesStored + 10] = TempBufferVerticesStored + 3; TempBufferRenderIndexList[TempBufferIndicesStored + 11] = TempBufferVerticesStored + 4; RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 0], 0, 0, 0, 0); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 0], pos.x + 11.0f * TheCamera.GetUp().x, pos.y + 11.0f * TheCamera.GetUp().y, pos.z + 11.0f * TheCamera.GetUp().z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 1], 0, 0, 0, 0); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 1], pos.x - 9.0f * TheCamera.GetRight().x, pos.y - 9.0f * TheCamera.GetRight().y, pos.z - 9.0f * TheCamera.GetRight().z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 2], RAIN_COLOUR_R * intensity / 256, RAIN_COLOUR_G * intensity / 256, RAIN_COLOUR_B * intensity / 256, RAIN_ALPHA); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 2], pos.x, pos.y, pos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 3], 0, 0, 0, 0); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 3], pos.x + 9.0f * TheCamera.GetRight().x, pos.y + 9.0f * TheCamera.GetRight().y, pos.z + 9.0f * TheCamera.GetRight().z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[TempBufferVerticesStored + 4], 0, 0, 0, 0); RwIm3DVertexSetPos(&TempBufferRenderVertices[TempBufferVerticesStored + 4], pos.x - 11.0f * TheCamera.GetUp().x, pos.y - 11.0f * TheCamera.GetUp().y, pos.z - 11.0f * TheCamera.GetUp().z); float u = STREAK_U; float v = STREAK_V; if (scale) { u *= LARGE_STREAK_COEFFICIENT; v *= LARGE_STREAK_COEFFICIENT; } float distance_coefficient; if (distance < STREAK_MIN_DISTANCE) distance_coefficient = 1.0f; else if (distance > STREAK_MAX_DISTANCE) distance_coefficient = 0.5f; else distance_coefficient = 1.0f - 0.5f * (distance - STREAK_MIN_DISTANCE) / (STREAK_MAX_DISTANCE - STREAK_MIN_DISTANCE); u *= distance_coefficient; v *= distance_coefficient; if (!CTimer::GetIsPaused()) { RandomTex = 0.0f; RandomTexX = 0.0f; RandomTexY = 0.0f; } RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 0], 0.5f * u - RandomTex + RandomTexX); RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 0], -v * 0.5f + RandomTexY); RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 1], RandomTexX); RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 1], RandomTexY); RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 2], 0.5f * u + RandomTexX); RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 2], RandomTexY); RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 3], u + RandomTexX); RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 3], RandomTexY); RwIm3DVertexSetU(&TempBufferRenderVertices[TempBufferVerticesStored + 4], 0.5f * u + RandomTex + RandomTexX); RwIm3DVertexSetV(&TempBufferRenderVertices[TempBufferVerticesStored + 5], 0.5f * v + RandomTexY); TempBufferIndicesStored += 12; TempBufferVerticesStored += 5; } void CWeather::RenderRainStreaks(void) { if (CTimer::GetIsCodePaused()) return; int base_intensity = (64.0f - CTimeCycle::GetFogReduction()) / 64.0f * int(255 * Rain); if (base_intensity == 0) return; if (TheCamera.m_CameraAverageSpeed > 1.75f) return; TempBufferIndicesStored = 0; TempBufferVerticesStored = 0; for (int i = 0; i < NUM_RAIN_STREAKS; i++) { if (Streaks[i].timer) { float secondsElapsed = (CTimer::GetTimeInMilliseconds() - Streaks[i].timer) / 1024.0f; if (secondsElapsed > STREAK_LIFETIME) Streaks[i].timer = 0; else{ int intensity; if (secondsElapsed < STREAK_INTEROLATION_TIME) intensity = base_intensity * 0.25f * secondsElapsed / STREAK_INTEROLATION_TIME; else if (secondsElapsed > (STREAK_LIFETIME - STREAK_INTEROLATION_TIME)) intensity = (STREAK_LIFETIME - secondsElapsed) * 0.25f * base_intensity / STREAK_INTEROLATION_TIME; else intensity = base_intensity * 0.25f; CVector dir = Streaks[i].direction; dir.Normalise(); CVector pos = Streaks[i].position + secondsElapsed * Streaks[i].direction; RenderOneRainStreak(pos, dir, intensity, false, (pos - TheCamera.GetPosition()).Magnitude()); #ifndef FIX_BUGS // remove useless code if (secondsElapsed > 1.0f && secondsElapsed < STREAK_LIFETIME - 1.0f) { CGeneral::GetRandomNumber(), CGeneral::GetRandomNumber(); } #endif } } else if ((CGeneral::GetRandomNumber() & 0xF00) == 0){ // 1/16 probability Streaks[i].direction = CVector(0.0f, 0.0f, -12.0f); Streaks[i].position = 6.0f * TheCamera.GetForward() + TheCamera.GetPosition() + CVector(-1.8f * Streaks[i].direction.x, -1.8f * Streaks[i].direction.y, 8.0f); if (!CCutsceneMgr::IsRunning()) { Streaks[i].position.x += 2.0f * FindPlayerSpeed().x * 60.0f; Streaks[i].position.y += 2.0f * FindPlayerSpeed().y * 60.0f; } else Streaks[i].position += (TheCamera.GetPosition() - TheCamera.m_RealPreviousCameraPosition) * 20.0f; Streaks[i].position.x += ((CGeneral::GetRandomNumber() & 255) - 128) * 0.04f; Streaks[i].position.y += ((CGeneral::GetRandomNumber() & 255) - 128) * 0.04f; Streaks[i].timer = CTimer::GetTimeInMilliseconds(); } } if (TempBufferIndicesStored){ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEFOGTYPE, (void*)rwFOGTYPELINEAR); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpRainDropTex)); if (RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, 1)) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); } TempBufferVerticesStored = 0; TempBufferIndicesStored = 0; } #ifdef SECUROM void CWeather::ForceHurricaneWeather() { for (int i = 0; i < ARRAY_SIZE(WeatherTypesList_WithHurricanes); i++) { WeatherTypesList[i] = WEATHER_HURRICANE; WeatherTypesList_WithHurricanes[i] = WEATHER_HURRICANE; } CWeather::OldWeatherType = WEATHER_HURRICANE; CWeather::NewWeatherType = WEATHER_HURRICANE; CWeather::ForcedWeatherType = WEATHER_HURRICANE; } #endif ================================================ FILE: src/render/Weather.h ================================================ enum { WEATHER_RANDOM = -1, WEATHER_SUNNY = 0, WEATHER_CLOUDY, WEATHER_RAINY, WEATHER_FOGGY, WEATHER_EXTRA_SUNNY, WEATHER_HURRICANE, WEATHER_TOTAL, WEATHER_EXTRACOLOURS = 6 }; class CWeather { public: static int32 SoundHandle; static int32 WeatherTypeInList; static int16 OldWeatherType; static int16 NewWeatherType; static int16 ForcedWeatherType; static bool LightningFlash; static bool LightningBurst; static uint32 LightningStart; static uint32 LightningFlashLastChange; static uint32 WhenToPlayLightningSound; static uint32 LightningDuration; static int32 StreamAfterRainTimer; static float ExtraSunnyness; static float Foggyness; static float CloudCoverage; static float Wind; static float Rain; static float InterpolationValue; static float WetRoads; static float Rainbow; static float SunGlare; static float WindClipped; static float TrafficLightBrightness; static bool bScriptsForceRain; static void RenderRainStreaks(void); static void Update(void); static void Init(void); static void ReleaseWeather(); static void ForceWeather(int16); static void ForceWeatherNow(int16); static void AddSplashesDuringHurricane(); static void AddStreamAfterRain(); static void AddRain(); static void AddHeatHaze(); static void AddBeastie(); static void ForceHurricaneWeather(); }; enum { NUM_RAIN_STREAKS = 35 }; struct tRainStreak { CVector position; CVector direction; uint32 timer; }; extern RwTexture* gpRainDropTex; ================================================ FILE: src/render/WindModifiers.cpp ================================================ #include "common.h" #include "WindModifiers.h" #include "Camera.h" #include "General.h" #define MAX_HEIGHT_DIST 40.0f #define MIN_FADE_DIST 20.0f #define MAX_FADE_DIST 50.0f CWindModifiers Array[16]; int32 CWindModifiers::Number; void CWindModifiers::RegisterOne(CVector pos, int32 type = 1) { if (CWindModifiers::Number < 16 && (pos - TheCamera.GetPosition()).Magnitude() < 100.0f) { Array[Number].m_pos = pos; Array[Number].m_type = type; Number++; } } bool CWindModifiers::FindWindModifier(CVector pos, float *x, float *y) { bool bWasWindModifierFound = false; CVector2D dir; for (int i = 0; i < Number; i++) { if (Array[i].m_type == 1) { float zDist = Abs(15.0f + pos.z - Array[i].m_pos.z); if (zDist < MAX_HEIGHT_DIST) { float dist = (pos - Array[i].m_pos).Magnitude(); if (dist < MAX_FADE_DIST) { float distFade = dist < MIN_FADE_DIST ? 1.0f : 1.0f - (dist - MIN_FADE_DIST) / (MAX_FADE_DIST - MIN_FADE_DIST); float heightFade = 1.0f - zDist / MAX_HEIGHT_DIST; float fade = distFade * heightFade * 0.5f; dir = (pos - Array[i].m_pos) * fade / dist; bWasWindModifierFound = true; } } } } if (bWasWindModifierFound) { float directionMult = ((CGeneral::GetRandomNumber() & 0x1F) - 16) * 0.0035f + 1.0f; *x += dir.x * directionMult; *y += dir.y * directionMult; } return bWasWindModifierFound; } ================================================ FILE: src/render/WindModifiers.h ================================================ #pragma once class CWindModifiers { CVector m_pos; int32 m_type; public: static int32 Number; static void RegisterOne(CVector pos, int32 windSourceType); static bool FindWindModifier(CVector pos, float *x, float *y); }; ================================================ FILE: src/rw/ClumpRead.cpp ================================================ #include "common.h" struct rpGeometryList { RpGeometry **geometries; int32 numGeoms; }; struct rpAtomicBinary { RwInt32 frameIndex; RwInt32 geomIndex; RwInt32 flags; RwInt32 unused; }; static int32 numberGeometrys; static int32 streamPosition; static rpGeometryList gGeomList; static rwFrameList gFrameList; static RpClumpChunkInfo gClumpInfo; rpGeometryList* GeometryListStreamRead1(RwStream *stream, rpGeometryList *geomlist) { int i; RwUInt32 size, version; RwInt32 numGeoms; numberGeometrys = 0; if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) return nil; assert(size == 4); if(RwStreamRead(stream, &numGeoms, 4) != 4) return nil; numberGeometrys = numGeoms/2; geomlist->numGeoms = numGeoms; if(geomlist->numGeoms > 0){ geomlist->geometries = (RpGeometry**)RwMalloc(geomlist->numGeoms * sizeof(RpGeometry*)); if(geomlist->geometries == nil) return nil; memset(geomlist->geometries, 0, geomlist->numGeoms * sizeof(RpGeometry*)); }else geomlist->geometries = nil; for(i = 0; i < numberGeometrys; i++){ if(!RwStreamFindChunk(stream, rwID_GEOMETRY, nil, &version)) return nil; geomlist->geometries[i] = RpGeometryStreamRead(stream); if(geomlist->geometries[i] == nil) return nil; } return geomlist; } rpGeometryList* GeometryListStreamRead2(RwStream *stream, rpGeometryList *geomlist) { int i; RwUInt32 version; for(i = numberGeometrys; i < geomlist->numGeoms; i++){ if(!RwStreamFindChunk(stream, rwID_GEOMETRY, nil, &version)) return nil; geomlist->geometries[i] = RpGeometryStreamRead(stream); if(geomlist->geometries[i] == nil) return nil; } return geomlist; } void GeometryListDeinitialize(rpGeometryList *geomlist) { int i; for(i = 0; i < geomlist->numGeoms; i++) if(geomlist->geometries[i]) RpGeometryDestroy(geomlist->geometries[i]); if(geomlist->numGeoms){ RwFree(geomlist->geometries); geomlist->numGeoms = 0; } } RpAtomic* ClumpAtomicStreamRead(RwStream *stream, rwFrameList *frmList, rpGeometryList *geomList) { RwUInt32 size, version; rpAtomicBinary a; RpAtomic *atomic; numberGeometrys = 0; if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) return nil; assert(size <= sizeof(rpAtomicBinary)); if(RwStreamRead(stream, &a, size) != size) return nil; atomic = RpAtomicCreate(); if(atomic == nil) return nil; RpAtomicSetFlags(atomic, a.flags); if(frmList->numFrames){ assert(a.frameIndex < frmList->numFrames); RpAtomicSetFrame(atomic, frmList->frames[a.frameIndex]); } if(geomList->numGeoms){ assert(a.geomIndex < geomList->numGeoms); RpAtomicSetGeometry(atomic, geomList->geometries[a.geomIndex], 0); }else{ RpGeometry *geom; if(!RwStreamFindChunk(stream, rwID_GEOMETRY, nil, &version)){ RpAtomicDestroy(atomic); return nil; } geom = RpGeometryStreamRead(stream); if(geom == nil){ RpAtomicDestroy(atomic); return nil; } RpAtomicSetGeometry(atomic, geom, 0); RpGeometryDestroy(geom); } return atomic; } bool RpClumpGtaStreamRead1(RwStream *stream) { RwUInt32 size, version; if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) return false; if(version >= 0x33000){ assert(size == 12); if(RwStreamRead(stream, &gClumpInfo, 12) != 12) return false; }else{ assert(size == 4); if(RwStreamRead(stream, &gClumpInfo, 4) != 4) return false; } if(!RwStreamFindChunk(stream, rwID_FRAMELIST, nil, &version)) return false; if(rwFrameListStreamRead(stream, &gFrameList) == nil) return false; if(!RwStreamFindChunk(stream, rwID_GEOMETRYLIST, nil, &version)){ rwFrameListDeinitialize(&gFrameList); return false; } if(GeometryListStreamRead1(stream, &gGeomList) == nil){ rwFrameListDeinitialize(&gFrameList); return false; } streamPosition = STREAMPOS(stream); return true; } RpClump* RpClumpGtaStreamRead2(RwStream *stream) { int i; RwUInt32 version; RpAtomic *atomic; RpClump *clump; clump = RpClumpCreate(); if(clump == nil) return nil; RwStreamSkip(stream, streamPosition - STREAMPOS(stream)); if(GeometryListStreamRead2(stream, &gGeomList) == nil){ GeometryListDeinitialize(&gGeomList); rwFrameListDeinitialize(&gFrameList); RpClumpDestroy(clump); return nil; } RpClumpSetFrame(clump, gFrameList.frames[0]); for(i = 0; i < gClumpInfo.numAtomics; i++){ if(!RwStreamFindChunk(stream, rwID_ATOMIC, nil, &version)){ GeometryListDeinitialize(&gGeomList); rwFrameListDeinitialize(&gFrameList); RpClumpDestroy(clump); return nil; } atomic = ClumpAtomicStreamRead(stream, &gFrameList, &gGeomList); if(atomic == nil){ GeometryListDeinitialize(&gGeomList); rwFrameListDeinitialize(&gFrameList); RpClumpDestroy(clump); return nil; } RpClumpAddAtomic(clump, atomic); } GeometryListDeinitialize(&gGeomList); rwFrameListDeinitialize(&gFrameList); return clump; } void RpClumpGtaCancelStream(void) { GeometryListDeinitialize(&gGeomList); rwFrameListDeinitialize(&gFrameList); gFrameList.numFrames = 0; } ================================================ FILE: src/rw/Lights.cpp ================================================ #include "common.h" #include #include #include "Lights.h" #include "Timer.h" #include "Timecycle.h" #include "Coronas.h" #include "Weather.h" #include "ZoneCull.h" #include "Frontend.h" #include "MBlur.h" RpLight *pAmbient; RpLight *pDirect; RpLight *pExtraDirectionals[4] = { nil }; int LightStrengths[4]; int NumExtraDirLightsInWorld; RwRGBAReal AmbientLightColourForFrame; RwRGBAReal AmbientLightColourForFrame_PedsCarsAndObjects; RwRGBAReal DirectionalLightColourForFrame; RwRGBAReal AmbientLightColour; RwRGBAReal DirectionalLightColour; #ifdef EXTENDED_COLOURFILTER #include "postfx.h" #define USEBLURCOLORS CPostFX::UseBlurColours() #else #define USEBLURCOLORS CMBlur::BlurOn #endif void SetLightsWithTimeOfDayColour(RpWorld *) { CVector vec1, vec2, vecsun; RwMatrix mat; if(pAmbient){ if(USEBLURCOLORS){ AmbientLightColourForFrame.red = CTimeCycle::GetAmbientRed_Bl() * CCoronas::LightsMult; AmbientLightColourForFrame.green = CTimeCycle::GetAmbientGreen_Bl() * CCoronas::LightsMult; AmbientLightColourForFrame.blue = CTimeCycle::GetAmbientBlue_Bl() * CCoronas::LightsMult; }else{ AmbientLightColourForFrame.red = CTimeCycle::GetAmbientRed() * CCoronas::LightsMult; AmbientLightColourForFrame.green = CTimeCycle::GetAmbientGreen() * CCoronas::LightsMult; AmbientLightColourForFrame.blue = CTimeCycle::GetAmbientBlue() * CCoronas::LightsMult; } if(USEBLURCOLORS){ AmbientLightColourForFrame_PedsCarsAndObjects.red = CTimeCycle::GetAmbientRed_Obj_Bl() * CCoronas::LightsMult; AmbientLightColourForFrame_PedsCarsAndObjects.green = CTimeCycle::GetAmbientGreen_Obj_Bl() * CCoronas::LightsMult; AmbientLightColourForFrame_PedsCarsAndObjects.blue = CTimeCycle::GetAmbientBlue_Obj_Bl() * CCoronas::LightsMult; }else{ AmbientLightColourForFrame_PedsCarsAndObjects.red = CTimeCycle::GetAmbientRed_Obj() * CCoronas::LightsMult; AmbientLightColourForFrame_PedsCarsAndObjects.green = CTimeCycle::GetAmbientGreen_Obj() * CCoronas::LightsMult; AmbientLightColourForFrame_PedsCarsAndObjects.blue = CTimeCycle::GetAmbientBlue_Obj() * CCoronas::LightsMult; } if(CWeather::LightningFlash && !CCullZones::CamNoRain()){ AmbientLightColourForFrame.red = 1.0f; AmbientLightColourForFrame.green = 1.0f; AmbientLightColourForFrame.blue = 1.0f; AmbientLightColourForFrame_PedsCarsAndObjects.red = 1.0f; AmbientLightColourForFrame_PedsCarsAndObjects.green = 1.0f; AmbientLightColourForFrame_PedsCarsAndObjects.blue = 1.0f; } RpLightSetColor(pAmbient, &AmbientLightColourForFrame); } if(pDirect){ DirectionalLightColourForFrame.red = CTimeCycle::GetDirectionalRed() * CCoronas::LightsMult; DirectionalLightColourForFrame.green = CTimeCycle::GetDirectionalGreen() * CCoronas::LightsMult; DirectionalLightColourForFrame.blue = CTimeCycle::GetDirectionalBlue() * CCoronas::LightsMult; RpLightSetColor(pDirect, &DirectionalLightColourForFrame); vecsun = CTimeCycle::m_VectorToSun[CTimeCycle::m_CurrentStoredValue]; vec1 = CVector(0.0f, 0.0f, 1.0f); vec2 = CrossProduct(vec1, vecsun); vec2.Normalise(); vec1 = CrossProduct(vec2, vecsun); mat.at.x = -vecsun.x; mat.at.y = -vecsun.y; mat.at.z = -vecsun.z; mat.right.x = vec1.x; mat.right.y = vec1.y; mat.right.z = vec1.z; mat.up.x = vec2.x; mat.up.y = vec2.y; mat.up.z = vec2.z; RwFrameTransform(RpLightGetFrame(pDirect), &mat, rwCOMBINEREPLACE); } if(FrontEndMenuManager.m_PrefsBrightness > 256){ float f1 = 2.0f * (FrontEndMenuManager.m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f; float f2 = 3.0f * (FrontEndMenuManager.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); #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); #else DirectionalLightColourForFrame.red = Min(1.0f, AmbientLightColourForFrame.red * f1); DirectionalLightColourForFrame.green = Min(1.0f, AmbientLightColourForFrame.green * f1); DirectionalLightColourForFrame.blue = Min(1.0f, AmbientLightColourForFrame.blue * f1); #endif } } RpWorld* LightsCreate(RpWorld *world) { int i; RwRGBAReal color; RwFrame *frame; if(world == nil) return nil; pAmbient = RpLightCreate(rpLIGHTAMBIENT); RpLightSetFlags(pAmbient, rpLIGHTLIGHTATOMICS); color.red = 0.25f; color.green = 0.25f; color.blue = 0.2f; RpLightSetColor(pAmbient, &color); pDirect = RpLightCreate(rpLIGHTDIRECTIONAL); RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS); color.red = 1.0f; color.green = 0.85f; color.blue = 0.45f; RpLightSetColor(pDirect, &color); RpLightSetRadius(pDirect, 2.0f); frame = RwFrameCreate(); RpLightSetFrame(pDirect, frame); RwV3d axis = { 1.0f, 1.0f, 0.0f }; RwFrameRotate(frame, &axis, 160.0f, rwCOMBINEPRECONCAT); RpWorldAddLight(world, pAmbient); RpWorldAddLight(world, pDirect); for(i = 0; i < NUMEXTRADIRECTIONALS; i++){ pExtraDirectionals[i] = RpLightCreate(rpLIGHTDIRECTIONAL); RpLightSetFlags(pExtraDirectionals[i], 0); color.red = 1.0f; color.green = 0.5f; color.blue = 0.0f; RpLightSetColor(pExtraDirectionals[i], &color); RpLightSetRadius(pExtraDirectionals[i], 2.0f); frame = RwFrameCreate(); RpLightSetFrame(pExtraDirectionals[i], frame); RpWorldAddLight(world, pExtraDirectionals[i]); } return world; } void LightsDestroy(RpWorld *world) { int i; if(world == nil) return; if(pAmbient){ RpWorldRemoveLight(world, pAmbient); RpLightDestroy(pAmbient); pAmbient = nil; } if(pDirect){ RpWorldRemoveLight(world, pDirect); RwFrameDestroy(RpLightGetFrame(pDirect)); RpLightDestroy(pDirect); pDirect = nil; } for(i = 0; i < NUMEXTRADIRECTIONALS; i++) if(pExtraDirectionals[i]){ RpWorldRemoveLight(world, pExtraDirectionals[i]); RwFrameDestroy(RpLightGetFrame(pExtraDirectionals[i])); RpLightDestroy(pExtraDirectionals[i]); pExtraDirectionals[i] = nil; } } void WorldReplaceNormalLightsWithScorched(RpWorld *world, float l) { RwRGBAReal color; color.red = l; color.green = l; color.blue = l; RpLightSetColor(pAmbient, &color); RpLightSetFlags(pDirect, 0); } void WorldReplaceScorchedLightsWithNormal(RpWorld *world) { RpLightSetColor(pAmbient, &AmbientLightColourForFrame); RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS); } void AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue) { float strength; int weakest; int i, n; RwRGBAReal color; RwV3d *dir; strength = Max(Max(red, green), blue); n = -1; if(NumExtraDirLightsInWorld < NUMEXTRADIRECTIONALS) n = NumExtraDirLightsInWorld; else{ weakest = strength; for(i = 0; i < NUMEXTRADIRECTIONALS; i++) if(LightStrengths[i] < weakest){ weakest = LightStrengths[i]; n = i; } } if(n < 0) return; color.red = red; color.green = green; color.blue = blue; RpLightSetColor(pExtraDirectionals[n], &color); dir = RwMatrixGetAt(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n]))); dir->x = -dirx; dir->y = -diry; dir->z = -dirz; RwMatrixUpdate(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n]))); RwFrameUpdateObjects(RpLightGetFrame(pExtraDirectionals[n])); RpLightSetFlags(pExtraDirectionals[n], rpLIGHTLIGHTATOMICS); LightStrengths[n] = strength; NumExtraDirLightsInWorld = Min(NumExtraDirLightsInWorld+1, NUMEXTRADIRECTIONALS); } void RemoveExtraDirectionalLights(RpWorld *world) { int i; for(i = 0; i < NumExtraDirLightsInWorld; i++) RpLightSetFlags(pExtraDirectionals[i], 0); NumExtraDirLightsInWorld = 0; } void SetAmbientAndDirectionalColours(float f) { AmbientLightColour.red = AmbientLightColourForFrame.red * f; AmbientLightColour.green = AmbientLightColourForFrame.green * f; AmbientLightColour.blue = AmbientLightColourForFrame.blue * f; DirectionalLightColour.red = DirectionalLightColourForFrame.red * f; DirectionalLightColour.green = DirectionalLightColourForFrame.green * f; DirectionalLightColour.blue = DirectionalLightColourForFrame.blue * f; RpLightSetColor(pAmbient, &AmbientLightColour); RpLightSetColor(pDirect, &DirectionalLightColour); } // unused void SetFlashyColours(float f) { if(CTimer::GetTimeInMilliseconds() & 0x100){ AmbientLightColour.red = 1.0f; AmbientLightColour.green = 1.0f; AmbientLightColour.blue = 1.0f; DirectionalLightColour.red = DirectionalLightColourForFrame.red; DirectionalLightColour.green = DirectionalLightColourForFrame.green; DirectionalLightColour.blue = DirectionalLightColourForFrame.blue; RpLightSetColor(pAmbient, &AmbientLightColour); RpLightSetColor(pDirect, &DirectionalLightColour); }else{ SetAmbientAndDirectionalColours(f * 0.75f); } } // unused void SetFlashyColours_Mild(float f) { if(CTimer::GetTimeInMilliseconds() & 0x100){ AmbientLightColour.red = 0.65f; AmbientLightColour.green = 0.65f; AmbientLightColour.blue = 0.65f; DirectionalLightColour.red = DirectionalLightColourForFrame.red; DirectionalLightColour.green = DirectionalLightColourForFrame.green; DirectionalLightColour.blue = DirectionalLightColourForFrame.blue; RpLightSetColor(pAmbient, &AmbientLightColour); RpLightSetColor(pDirect, &DirectionalLightColour); }else{ SetAmbientAndDirectionalColours(f * 0.9f); } } void SetBrightMarkerColours(float f) { AmbientLightColour.red = 0.6f; AmbientLightColour.green = 0.6f; AmbientLightColour.blue = 0.6f; DirectionalLightColour.red = (1.0f - DirectionalLightColourForFrame.red) * 0.4f + DirectionalLightColourForFrame.red; DirectionalLightColour.green = (1.0f - DirectionalLightColourForFrame.green) * 0.4f + DirectionalLightColourForFrame.green; DirectionalLightColour.blue = (1.0f - DirectionalLightColourForFrame.blue) * 0.4f + DirectionalLightColourForFrame.blue; RpLightSetColor(pAmbient, &AmbientLightColour); RpLightSetColor(pDirect, &DirectionalLightColour); } void ReSetAmbientAndDirectionalColours(void) { RpLightSetColor(pAmbient, &AmbientLightColourForFrame); RpLightSetColor(pDirect, &DirectionalLightColourForFrame); } void DeActivateDirectional(void) { RpLightSetFlags(pDirect, 0); } void ActivateDirectional(void) { RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS); } RwRGBAReal FullLight = { 1.0f, 1.0f, 1.0f, 1.0f }; void SetFullAmbient(void) { RpLightSetColor(pAmbient, &FullLight); } void SetAmbientColours(void) { RpLightSetColor(pAmbient, &AmbientLightColourForFrame); } void SetAmbientColoursForPedsCarsAndObjects(void) { RpLightSetColor(pAmbient, &AmbientLightColourForFrame_PedsCarsAndObjects); } uint8 IndicateR[] = { 0, 255, 0, 0, 255, 255, 0 }; uint8 IndicateG[] = { 0, 0, 255, 0, 255, 0, 255 }; uint8 IndicateB[] = { 0, 0, 0, 255, 0, 255, 255 }; void SetAmbientColoursToIndicateRoadGroup(int i) { AmbientLightColour.red = IndicateR[i%7]/255.0f; AmbientLightColour.green = IndicateG[i%7]/255.0f; AmbientLightColour.blue = IndicateB[i%7]/255.0f; RpLightSetColor(pAmbient, &AmbientLightColour); } void SetAmbientColours(RwRGBAReal *color) { RpLightSetColor(pAmbient, color); } ================================================ FILE: src/rw/Lights.h ================================================ #pragma once extern RpLight *pAmbient; extern RpLight *pDirect; extern RpLight *pExtraDirectionals[4]; extern int LightStrengths[4]; extern int NumExtraDirLightsInWorld; void SetLightsWithTimeOfDayColour(RpWorld *); RpWorld *LightsCreate(RpWorld *world); void LightsDestroy(RpWorld *world); void WorldReplaceNormalLightsWithScorched(RpWorld *world, float l); void WorldReplaceScorchedLightsWithNormal(RpWorld *world); void AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue); void RemoveExtraDirectionalLights(RpWorld *world); void SetAmbientAndDirectionalColours(float f); void SetFlashyColours(float f); void SetFlashyColours_Mild(float f); void SetBrightMarkerColours(float f); void ReSetAmbientAndDirectionalColours(void); void DeActivateDirectional(void); void ActivateDirectional(void); void SetAmbientColours(void); void SetAmbientColoursForPedsCarsAndObjects(void); void SetAmbientColoursToIndicateRoadGroup(int i); void SetAmbientColours(RwRGBAReal *color); void SetFullAmbient(void); ================================================ FILE: src/rw/MemoryHeap.cpp ================================================ #include "common.h" #include "main.h" #include "FileMgr.h" #include "Timer.h" #include "ModelInfo.h" #include "Streaming.h" #include "FileLoader.h" #include "MemoryHeap.h" // TODO(MIAMI) #ifdef USE_CUSTOM_ALLOCATOR //#define MEMORYHEAP_ASSERT(cond) { if (!(cond)) { printf("ASSERT File:%s Line:%d\n", __FILE__, __LINE__); exit(1); } } //#define MEMORYHEAP_ASSERT_MESSAGE(cond, message) { if (!(cond)) { printf("ASSERT File:%s Line:%d:\n\t%s\n", __FILE__, __LINE__, message); exit(1); } } #define MEMORYHEAP_ASSERT(cond) assert(cond) #define MEMORYHEAP_ASSERT_MESSAGE(cond, message) assert(cond) // registered pointers that we keep track of void **gPtrList[4000]; int32 numPtrs; int32 gPosnInList; // indices into the ptr list in here are free CStack m_ptrListIndexStack; // how much memory we've moved uint32 memMoved; CMemoryHeap gMainHeap; void CMemoryHeap::Init(uint32 total) { MEMORYHEAP_ASSERT((total != 0xF) != 0); m_totalMemUsed = 0; m_memUsed = nil; m_currentMemID = MEMID_FREE; m_blocksUsed = nil; m_totalBlocksUsed = 0; m_unkMemId = -1; uint8 *mem = (uint8*)malloc(total); assert(((uintptr)mem & 0xF) == 0); m_start = (HeapBlockDesc*)mem; m_end = (HeapBlockDesc*)(mem + total - sizeof(HeapBlockDesc)); m_start->m_memId = MEMID_FREE; m_start->m_size = total - 2*sizeof(HeapBlockDesc); m_end->m_memId = MEMID_GAME; m_end->m_size = 0; m_freeList.m_last.m_size = INT_MAX; m_freeList.Init(); m_freeList.Insert(m_start); // TODO: figure out what these are and use sizeof m_fixedSize[0].Init(0x10); m_fixedSize[1].Init(0x20); m_fixedSize[2].Init(0xE0); m_fixedSize[3].Init(0x60); m_fixedSize[4].Init(0x1C0); m_fixedSize[5].Init(0x50); m_currentMemID = MEMID_FREE; // disable registration m_memUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32)); m_blocksUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32)); RegisterMalloc(GetDescFromHeapPointer(m_memUsed)); RegisterMalloc(GetDescFromHeapPointer(m_blocksUsed)); m_currentMemID = MEMID_GAME; for(int i = 0; i < NUM_MEMIDS; i++){ m_memUsed[i] = 0; m_blocksUsed[i] = 0; } } void CMemoryHeap::RegisterMalloc(HeapBlockDesc *block) { block->m_memId = m_currentMemID; if(m_currentMemID == MEMID_FREE) return; m_totalMemUsed += block->m_size + sizeof(HeapBlockDesc); m_memUsed[m_currentMemID] += block->m_size + sizeof(HeapBlockDesc); m_blocksUsed[m_currentMemID]++; m_totalBlocksUsed++; } void CMemoryHeap::RegisterFree(HeapBlockDesc *block) { if(block->m_memId == MEMID_FREE) return; m_totalMemUsed -= block->m_size + sizeof(HeapBlockDesc); m_memUsed[block->m_memId] -= block->m_size + sizeof(HeapBlockDesc); m_blocksUsed[block->m_memId]--; m_totalBlocksUsed--; } void* CMemoryHeap::Malloc(uint32 size) { static int recursion = 0; // weird way to round up if((size & 0xF) != 0) size = (size&~0xF) + 0x10; recursion++; // See if we can allocate from one of the fixed-size lists for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){ CommonSize *list = &m_fixedSize[i]; if(m_fixedSize[i].m_size == size){ HeapBlockDesc *block = list->Malloc(); if(block){ RegisterMalloc(block); recursion--; return block->GetDataPointer(); } break; } } // now try the normal free list HeapBlockDesc *next; for(HeapBlockDesc *block = m_freeList.m_first.m_next; block != &m_freeList.m_last; block = next){ MEMORYHEAP_ASSERT(block->m_memId == MEMID_FREE); MEMORYHEAP_ASSERT_MESSAGE(block >= m_start && block <= m_end, "Block outside of memory"); // make sure block has maximum size uint32 initialsize = block->m_size; uint32 blocksize = CombineFreeBlocks(block); #ifdef FIX_BUGS // has to be done here because block can be moved next = block->m_next; #endif if(initialsize != blocksize){ block->RemoveHeapFreeBlock(); HeapBlockDesc *pos = block->m_prev->FindSmallestFreeBlock(block->m_size); block->InsertHeapFreeBlock(pos->m_prev); } if(block->m_size >= size){ // got space to allocate from! block->RemoveHeapFreeBlock(); FillInBlockData(block, block->GetNextConsecutive(), size); recursion--; return block->GetDataPointer(); } #ifndef FIX_BUGS next = block->m_next; #endif } // oh no, we're losing, try to free some stuff static bool removeCollision = false; static bool removeIslands = false; static bool removeBigBuildings = false; size_t initialMemoryUsed = CStreaming::ms_memoryUsed; CStreaming::MakeSpaceFor(0xCFE800 - CStreaming::ms_memoryUsed); if (recursion > 10) CGame::TidyUpMemory(true, false); else if (recursion > 6) CGame::TidyUpMemory(false, true); if (initialMemoryUsed == CStreaming::ms_memoryUsed && recursion > 11) { if (!removeCollision && !CGame::playingIntro) { CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_GENERIC); removeCollision = true; } else if (!removeIslands && !CGame::playingIntro) { CStreaming::RemoveIslandsNotUsed(LEVEL_INDUSTRIAL); CStreaming::RemoveIslandsNotUsed(LEVEL_COMMERCIAL); CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN); removeIslands = true; } else if (!removeBigBuildings) { CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL); CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL); CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN); } else { LoadingScreen("NO MORE MEMORY", nil, nil); LoadingScreen("NO MORE MEMORY", nil, nil); } CGame::TidyUpMemory(true, false); } void *mem = Malloc(size); if (removeCollision) { CTimer::Stop(); // TODO: different on PS2 CFileLoader::LoadCollisionFromDatFile(CCollision::ms_collisionInMemory); removeCollision = false; CTimer::Update(); } if (removeBigBuildings || removeIslands) { CTimer::Stop(); if (!CGame::playingIntro) CStreaming::RequestBigBuildings(CGame::currLevel); CStreaming::LoadAllRequestedModels(true); removeBigBuildings = false; removeIslands = false; CTimer::Update(); } recursion--; return mem; } void* CMemoryHeap::Realloc(void *ptr, uint32 size) { if(ptr == nil) return Malloc(size); // weird way to round up if((size & 0xF) != 0) size = (size&~0xF) + 0x10; HeapBlockDesc *block = GetDescFromHeapPointer(ptr); #ifdef FIX_BUGS // better handling of size < block->m_size if(size == 0){ Free(ptr); return nil; } if(block->m_size >= size){ // shrink allocated block RegisterFree(block); PushMemId(block->m_memId); FillInBlockData(block, block->GetNextConsecutive(), size); PopMemId(); return ptr; } #else // not growing. just returning here is a bit cheap though if(block->m_size >= size) return ptr; #endif // have to grow allocated block HeapBlockDesc *next = block->GetNextConsecutive(); MEMORYHEAP_ASSERT_MESSAGE(next >= m_start && next <= m_end, "Block outside of memory"); if(next->m_memId == MEMID_FREE){ // try to grow the current block // make sure the next free block has maximum size uint32 freespace = CombineFreeBlocks(next); HeapBlockDesc *end = next->GetNextConsecutive(); MEMORYHEAP_ASSERT_MESSAGE(end >= m_start && end <= m_end, "Block outside of memory"); // why the sizeof here? if(block->m_size + next->m_size + sizeof(HeapBlockDesc) >= size){ // enough space to grow next->RemoveHeapFreeBlock(); RegisterFree(block); PushMemId(block->m_memId); FillInBlockData(block, next->GetNextConsecutive(), size); PopMemId(); return ptr; } } // can't grow the existing block, have to get a new one and copy PushMemId(block->m_memId); void *dst = Malloc(size); PopMemId(); memcpy(dst, ptr, block->m_size); Free(ptr); return dst; } void CMemoryHeap::Free(void *ptr) { HeapBlockDesc *block = GetDescFromHeapPointer(ptr); MEMORYHEAP_ASSERT_MESSAGE(block->m_memId != MEMID_FREE, "MemoryHeap corrupt"); MEMORYHEAP_ASSERT(m_unkMemId == -1 || m_unkMemId == block->m_memId); RegisterFree(block); block->m_memId = MEMID_FREE; CombineFreeBlocks(block); FreeBlock(block); if(block->m_ptrListIndex != -1){ int32 idx = block->m_ptrListIndex; gPtrList[idx] = nil; m_ptrListIndexStack.push(idx); } block->m_ptrListIndex = -1; } // allocate 'size' bytes from 'block' void CMemoryHeap::FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size) { block->m_size = size; block->m_ptrListIndex = -1; HeapBlockDesc *remainder = block->GetNextConsecutive(); MEMORYHEAP_ASSERT(remainder <= end); if(remainder < end-1){ RegisterMalloc(block); // can fit another block in the remaining space remainder->m_size = GetSizeBetweenBlocks(remainder, end); remainder->m_memId = MEMID_FREE; MEMORYHEAP_ASSERT(remainder->m_size != 0); FreeBlock(remainder); }else{ // fully allocate this one if(remainder < end) // no gaps allowed block->m_size = GetSizeBetweenBlocks(block, end); RegisterMalloc(block); } } // Make sure free block has no other free blocks after it uint32 CMemoryHeap::CombineFreeBlocks(HeapBlockDesc *block) { HeapBlockDesc *next = block->GetNextConsecutive(); if(next->m_memId != MEMID_FREE) return block->m_size; // get rid of free blocks after this one and adjust size for(; next->m_memId == MEMID_FREE; next = next->GetNextConsecutive()) next->RemoveHeapFreeBlock(); block->m_size = GetSizeBetweenBlocks(block, next); return block->m_size; } // Try to move all registered memory blocks into more optimal location void CMemoryHeap::TidyHeap(void) { for(int i = 0; i < numPtrs; i++){ if(gPtrList[i] == nil || *gPtrList[i] == nil) continue; HeapBlockDesc *newblock = WhereShouldMemoryMove(*gPtrList[i]); if(newblock) *gPtrList[i] = MoveHeapBlock(newblock, GetDescFromHeapPointer(*gPtrList[i])); } } // MIAMI: this is empty void CMemoryHeap::RegisterMemPointer(void *ptr) { HeapBlockDesc *block = GetDescFromHeapPointer(*(void**)ptr); if(block->m_ptrListIndex != -1) return; // already registered int index; if(m_ptrListIndexStack.sp > 0){ // re-use a previously free'd index index = m_ptrListIndexStack.pop(); }else{ // have to find a new index index = gPosnInList; void **pp = gPtrList[index]; // we're replacing an old pointer here?? if(pp && *pp && *pp != (void*)0xDDDDDDDD) GetDescFromHeapPointer(*pp)->m_ptrListIndex = -1; gPosnInList++; if(gPosnInList == 4000) gPosnInList = 0; if(numPtrs < 4000) numPtrs++; } gPtrList[index] = (void**)ptr; block->m_ptrListIndex = index; } void* CMemoryHeap::MoveMemory(void *ptr) { HeapBlockDesc *newblock = WhereShouldMemoryMove(ptr); if(newblock) return MoveHeapBlock(newblock, GetDescFromHeapPointer(ptr)); else return ptr; } HeapBlockDesc* CMemoryHeap::WhereShouldMemoryMove(void *ptr) { HeapBlockDesc *block = GetDescFromHeapPointer(ptr); MEMORYHEAP_ASSERT(block->m_memId != MEMID_FREE); HeapBlockDesc *next = block->GetNextConsecutive(); if(next->m_memId != MEMID_FREE) return nil; // we want to move the block into another block // such that the free space between this and the next block can be minimized HeapBlockDesc *newblock = m_freeList.m_first.FindSmallestFreeBlock(block->m_size); // size of free space wouldn't decrease, so return if(newblock->m_size >= block->m_size + next->m_size) return nil; // size of free space wouldn't decrease enough if(newblock->m_size >= 16 + 1.125f*block->m_size) // what are 16 and 1.125 here? sizeof(HeapBlockDesc)? return nil; return newblock; } void* CMemoryHeap::MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src) { PushMemId(src->m_memId); dst->RemoveHeapFreeBlock(); FillInBlockData(dst, dst->GetNextConsecutive(), src->m_size); PopMemId(); memcpy(dst->GetDataPointer(), src->GetDataPointer(), src->m_size); memMoved += src->m_size; dst->m_ptrListIndex = src->m_ptrListIndex; src->m_ptrListIndex = -1; Free(src->GetDataPointer()); return dst->GetDataPointer(); } uint32 CMemoryHeap::GetMemoryUsed(int32 id) { return m_memUsed[id]; } uint32 CMemoryHeap::GetBlocksUsed(int32 id) { return m_blocksUsed[id]; } void CMemoryHeap::PopMemId(void) { assert(m_idStack.sp > 0); m_currentMemID = m_idStack.pop(); assert(m_currentMemID != MEMID_FREE); } void CMemoryHeap::PushMemId(int32 id) { MEMORYHEAP_ASSERT(id != MEMID_FREE); assert(m_idStack.sp < 16); m_idStack.push(m_currentMemID); m_currentMemID = id; } void CMemoryHeap::ParseHeap(void) { char tmp[16]; int fd = CFileMgr::OpenFileForWriting("heap.txt"); CTimer::Stop(); // CMemoryHeap::IntegrityCheck(); uint32 addrQW = 0; for(HeapBlockDesc *block = m_start; block < m_end; block = block->GetNextConsecutive()){ char chr = '*'; // free if(block->m_memId != MEMID_FREE) chr = block->m_memId-1 + 'A'; int numQW = block->m_size>>4; if((addrQW & 0x3F) == 0){ sprintf(tmp, "\n%5dK:", addrQW>>6); CFileMgr::Write(fd, tmp, 8); } CFileMgr::Write(fd, "#", 1); // the descriptor, has to be 16 bytes!!!! addrQW++; while(numQW--){ if((addrQW & 0x3F) == 0){ sprintf(tmp, "\n%5dK:", addrQW>>6); CFileMgr::Write(fd, tmp, 8); } CFileMgr::Write(fd, &chr, 1); addrQW++; } } CTimer::Update(); CFileMgr::CloseFile(fd); } void CommonSize::Init(uint32 size) { m_freeList.Init(); m_size = size; m_failed = 0; m_remaining = 0; } #endif ================================================ FILE: src/rw/MemoryHeap.h ================================================ #pragma once // some windows shit #ifdef MoveMemory #undef MoveMemory #endif #ifdef USE_CUSTOM_ALLOCATOR #define PUSH_MEMID(id) gMainHeap.PushMemId(id) #define POP_MEMID() gMainHeap.PopMemId() #define REGISTER_MEMPTR(ptr) gMainHeap.RegisterMemPointer(ptr) #else #define PUSH_MEMID(id) #define POP_MEMID() #define REGISTER_MEMPTR(ptr) #endif enum { MEMID_FREE, MEMID_GAME = 1, // "Game" MEMID_WORLD = 2, // "World" MEMID_ANIMATION = 3, // "Animation" MEMID_POOLS = 4, // "Pools" MEMID_DEF_MODELS = 5, // "Default Models" MEMID_STREAM = 6, // "Streaming" MEMID_STREAM_MODELS = 7, // "Streamed Models" MEMID_STREAM_LODS = 8, // "Streamed LODs" MEMID_STREAM_TEXUTRES = 9, // "Streamed Textures" MEMID_STREAM_COLLISION = 10, // "Streamed Collision" MEMID_STREAM_ANIMATION = 11, // "Streamed Animation" MEMID_TEXTURES = 12, // "Textures" MEMID_COLLISION = 13, // "Collision" MEMID_PRE_ALLOC = 14, // "PreAlloc" MEMID_GAME_PROCESS = 15, // "Game Process" MEMID_SCRIPT = 16, // "Script" MEMID_CARS = 17, // "Cars" MEMID_RENDER = 18, // "Render" MEMID_PED_ATTR = 19, // "Ped Attr" NUM_MEMIDS, NUM_FIXED_MEMBLOCKS = 6 }; template class CStack { public: T values[N]; uint32 sp; CStack() : sp(0) {} void push(const T& val) { values[sp++] = val; } T& pop() { return values[--sp]; } }; struct HeapBlockDesc { uint32 m_size; int16 m_memId; int16 m_ptrListIndex; HeapBlockDesc *m_next; HeapBlockDesc *m_prev; HeapBlockDesc *GetNextConsecutive(void) { return (HeapBlockDesc*)((uintptr)this + sizeof(HeapBlockDesc) + m_size); } void *GetDataPointer(void) { return (void*)((uintptr)this + sizeof(HeapBlockDesc)); } void RemoveHeapFreeBlock(void) { m_next->m_prev = m_prev; m_prev->m_next = m_next; } // after node void InsertHeapFreeBlock(HeapBlockDesc *node) { m_next = node->m_next; node->m_next->m_prev = this; m_prev = node; node->m_next = this; } HeapBlockDesc *FindSmallestFreeBlock(uint32 size) { HeapBlockDesc *b; for(b = m_next; b->m_size < size; b = b->m_next); return b; } }; #ifdef USE_CUSTOM_ALLOCATOR // TODO: figure something out for 64 bit pointers static_assert(sizeof(HeapBlockDesc) == 0x10, "HeapBlockDesc must have 0x10 size otherwise most of assumptions don't make sense"); #endif struct HeapBlockList { HeapBlockDesc m_first; HeapBlockDesc m_last; void Init(void) { m_first.m_next = &m_last; m_last.m_prev = &m_first; } void Insert(HeapBlockDesc *node) { node->InsertHeapFreeBlock(&m_first); } }; struct CommonSize { HeapBlockList m_freeList; uint32 m_size; uint32 m_failed; uint32 m_remaining; void Init(uint32 size); void Free(HeapBlockDesc *node) { m_freeList.Insert(node); m_remaining++; } HeapBlockDesc *Malloc(void) { if(m_freeList.m_first.m_next == &m_freeList.m_last){ m_failed++; return nil; } HeapBlockDesc *block = m_freeList.m_first.m_next; m_remaining--; block->RemoveHeapFreeBlock(); block->m_ptrListIndex = -1; return block; } }; class CMemoryHeap { public: HeapBlockDesc *m_start; HeapBlockDesc *m_end; HeapBlockList m_freeList; CommonSize m_fixedSize[NUM_FIXED_MEMBLOCKS]; uint32 m_totalMemUsed; CStack m_idStack; uint32 m_currentMemID; uint32 *m_memUsed; uint32 m_totalBlocksUsed; uint32 *m_blocksUsed; uint32 m_unkMemId; CMemoryHeap(void) : m_start(nil) {} void Init(uint32 total); void RegisterMalloc(HeapBlockDesc *block); void RegisterFree(HeapBlockDesc *block); void *Malloc(uint32 size); void *Realloc(void *ptr, uint32 size); void Free(void *ptr); void FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size); uint32 CombineFreeBlocks(HeapBlockDesc *block); void *MoveMemory(void *ptr); HeapBlockDesc *WhereShouldMemoryMove(void *ptr); void *MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src); void PopMemId(void); void PushMemId(int32 id); void RegisterMemPointer(void *ptr); void TidyHeap(void); uint32 GetMemoryUsed(int32 id); uint32 GetBlocksUsed(int32 id); int32 GetLargestFreeBlock(void) { return m_freeList.m_last.m_prev->m_size; } void ParseHeap(void); HeapBlockDesc *GetDescFromHeapPointer(void *block) { return (HeapBlockDesc*)((uintptr)block - sizeof(HeapBlockDesc)); } uint32 GetSizeBetweenBlocks(HeapBlockDesc *first, HeapBlockDesc *second) { return (uintptr)second - (uintptr)first - sizeof(HeapBlockDesc); } void FreeBlock(HeapBlockDesc *block){ for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){ if(m_fixedSize[i].m_size == block->m_size){ m_fixedSize[i].Free(block); return; } } HeapBlockDesc *b = m_freeList.m_first.FindSmallestFreeBlock(block->m_size); block->InsertHeapFreeBlock(b->m_prev); } }; extern CMemoryHeap gMainHeap; ================================================ FILE: src/rw/MemoryMgr.cpp ================================================ #include "common.h" #include "MemoryHeap.h" #include "MemoryMgr.h" uint8 *pMemoryTop; void InitMemoryMgr(void) { #ifdef USE_CUSTOM_ALLOCATOR #ifdef GTA_PS2 #error "finish this" #else // randomly allocate 128mb gMainHeap.Init(128*1024*1024); #endif #endif } RwMemoryFunctions memFuncs = { MemoryMgrMalloc, MemoryMgrFree, MemoryMgrRealloc, MemoryMgrCalloc }; #ifdef USE_CUSTOM_ALLOCATOR // game seems to be using heap directly here, but this is nicer void *operator new(size_t sz) { return MemoryMgrMalloc(sz); } void *operator new[](size_t sz) { return MemoryMgrMalloc(sz); } void operator delete(void *ptr) noexcept { MemoryMgrFree(ptr); } void operator delete[](void *ptr) noexcept { MemoryMgrFree(ptr); } #endif void* MemoryMgrMalloc(size_t size) { #ifdef USE_CUSTOM_ALLOCATOR void *mem = gMainHeap.Malloc(size); #else void *mem = malloc(size); #endif if((uint8*)mem + size > pMemoryTop) pMemoryTop = (uint8*)mem + size ; return mem; } void* MemoryMgrRealloc(void *ptr, size_t size) { #ifdef USE_CUSTOM_ALLOCATOR void *mem = gMainHeap.Realloc(ptr, size); #else void *mem = realloc(ptr, size); #endif if((uint8*)mem + size > pMemoryTop) pMemoryTop = (uint8*)mem + size ; return mem; } void* MemoryMgrCalloc(size_t num, size_t size) { #ifdef USE_CUSTOM_ALLOCATOR void *mem = gMainHeap.Malloc(num*size); #else void *mem = calloc(num, size); #endif if((uint8*)mem + size > pMemoryTop) pMemoryTop = (uint8*)mem + size ; #ifdef FIX_BUGS memset(mem, 0, num*size); #endif return mem; } void MemoryMgrFree(void *ptr) { #ifdef USE_CUSTOM_ALLOCATOR #ifdef FIX_BUGS // i don't suppose this is handled by RW? if(ptr == nil) return; #endif gMainHeap.Free(ptr); #else free(ptr); #endif } void * RwMallocAlign(RwUInt32 size, RwUInt32 align) { #if defined (FIX_BUGS) || defined(FIX_BUGS_64) uintptr ptralign = align-1; void *mem = (void *)MemoryMgrMalloc(size + sizeof(uintptr) + ptralign); ASSERT(mem != nil); void *addr = (void *)((((uintptr)mem) + sizeof(uintptr) + ptralign) & ~ptralign); ASSERT(addr != nil); #else void *mem = (void *)MemoryMgrMalloc(size + align); ASSERT(mem != nil); void *addr = (void *)((((uintptr)mem) + align) & ~(align - 1)); ASSERT(addr != nil); #endif *(((void **)addr) - 1) = mem; return addr; } void RwFreeAlign(void *mem) { ASSERT(mem != nil); void *addr = *(((void **)mem) - 1); ASSERT(addr != nil); MemoryMgrFree(addr); } ================================================ FILE: src/rw/MemoryMgr.h ================================================ #pragma once extern RwMemoryFunctions memFuncs; void InitMemoryMgr(void); void *MemoryMgrMalloc(size_t size); void *MemoryMgrRealloc(void *ptr, size_t size); void *MemoryMgrCalloc(size_t num, size_t size); void MemoryMgrFree(void *ptr); void *RwMallocAlign(RwUInt32 size, RwUInt32 align); void RwFreeAlign(void *mem); ================================================ FILE: src/rw/NodeName.cpp ================================================ #include "common.h" #include "NodeName.h" static int32 gPluginOffset; enum { ID_NODENAME = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0xFE), }; #define NODENAMEEXT(o) (RWPLUGINOFFSET(char, o, gPluginOffset)) void* NodeNameConstructor(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) { if(gPluginOffset > 0) NODENAMEEXT(object)[0] = '\0'; return object; } void* NodeNameDestructor(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) { return object; } void* NodeNameCopy(void *dstObject, const void *srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject) { strncpy(NODENAMEEXT(dstObject), NODENAMEEXT(srcObject), 23); return nil; } RwStream* NodeNameStreamRead(RwStream *stream, RwInt32 binaryLength, void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) { RwStreamRead(stream, NODENAMEEXT(object), binaryLength); NODENAMEEXT(object)[binaryLength] = '\0'; return stream; } RwStream* NodeNameStreamWrite(RwStream *stream, RwInt32 binaryLength, const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) { RwStreamWrite(stream, NODENAMEEXT(object), binaryLength); return stream; } RwInt32 NodeNameStreamGetSize(const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject) { char *name = NODENAMEEXT(object); // can't be nil return name ? (RwInt32)rwstrlen(name) : 0; } bool NodeNamePluginAttach(void) { gPluginOffset = RwFrameRegisterPlugin(24, ID_NODENAME, NodeNameConstructor, NodeNameDestructor, NodeNameCopy); RwFrameRegisterPluginStream(ID_NODENAME, NodeNameStreamRead, NodeNameStreamWrite, NodeNameStreamGetSize); return gPluginOffset != -1; } char* GetFrameNodeName(RwFrame *frame) { if(gPluginOffset < 0) return nil; return NODENAMEEXT(frame); } ================================================ FILE: src/rw/NodeName.h ================================================ #pragma once bool NodeNamePluginAttach(void); char *GetFrameNodeName(RwFrame *frame); ================================================ FILE: src/rw/RwHelper.cpp ================================================ #define WITHD3D #include "common.h" #include "Timecycle.h" #include "skeleton.h" #include "Debug.h" #if !defined(FINAL) || defined(DEBUGMENU) #include "rtcharse.h" #endif #ifndef FINAL RtCharset *debugCharset; bool bDebugRenderGroups; #endif #ifdef PS2_ALPHA_TEST bool gPS2alphaTest = true; #else bool gPS2alphaTest = false; #endif bool gBackfaceCulling = true; #if !defined(FINAL) || defined(DEBUGMENU) static bool charsetOpen; void OpenCharsetSafe() { if(!charsetOpen) RtCharsetOpen(); charsetOpen = true; } #endif void CreateDebugFont() { #ifndef FINAL RwRGBA color = { 255, 255, 128, 255 }; RwRGBA colorbg = { 0, 0, 0, 0 }; OpenCharsetSafe(); debugCharset = RtCharsetCreate(&color, &colorbg); #endif } void DestroyDebugFont() { #ifndef FINAL RtCharsetDestroy(debugCharset); RtCharsetClose(); charsetOpen = false; #endif } void ObrsPrintfString(const char *str, short x, short y) { #ifndef FINAL RtCharsetPrintBuffered(debugCharset, str, x*8, y*16, true); #endif } void FlushObrsPrintfs() { #ifndef FINAL RtCharsetBufferFlush(); #endif } void DefinedState(void) { RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSWRAP); RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD); RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); //RwRenderStateSet(rwRENDERSTATEALPHAPRIMITIVEBUFFER, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEBORDERCOLOR, (void*)RWRGBALONG(0, 0, 0, 255)); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEFOGCOLOR, (void*)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255)); RwRenderStateSet(rwRENDERSTATEFOGTYPE, (void*)rwFOGTYPELINEAR); RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); #ifdef LIBRW rw::SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL); rw::SetRenderState(rw::ALPHATESTREF, 3); rw::SetRenderState(rw::GSALPHATEST, gPS2alphaTest); #else // D3D stuff RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); RwD3D8SetRenderState(D3DRS_ALPHAREF, 2); #endif } void SetCullMode(uint32 mode) { if(gBackfaceCulling) RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)mode); else RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE); } #ifndef FINAL void PushRendergroup(const char *name) { if(!bDebugRenderGroups) return; #if defined(RW_OPENGL) if(GLAD_GL_KHR_debug) glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, name); #elif defined(RW_D3D9) static WCHAR tmp[256]; MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, tmp, sizeof(tmp)); D3DPERF_BeginEvent(0xFFFFFFFF, tmp); #endif } void PopRendergroup(void) { if(!bDebugRenderGroups) return; #if defined(RW_OPENGL) if(GLAD_GL_KHR_debug) glPopDebugGroup(); #elif defined(RW_D3D9) D3DPERF_EndEvent(); #endif } #endif RwFrame* GetFirstFrameCallback(RwFrame *child, void *data) { *(RwFrame**)data = child; return nil; } RwFrame* GetFirstChild(RwFrame *frame) { RwFrame *child; child = nil; RwFrameForAllChildren(frame, GetFirstFrameCallback, &child); return child; } RwObject* GetFirstObjectCallback(RwObject *object, void *data) { *(RwObject**)data = object; return nil; } RwObject* GetFirstObject(RwFrame *frame) { RwObject *obj; obj = nil; RwFrameForAllObjects(frame, GetFirstObjectCallback, &obj); return obj; } RpAtomic* GetFirstAtomicCallback(RpAtomic *atm, void *data) { *(RpAtomic**)data = atm; return nil; } RpAtomic* GetFirstAtomic(RpClump *clump) { RpAtomic *atm; atm = nil; RpClumpForAllAtomics(clump, GetFirstAtomicCallback, &atm); return atm; } RwTexture* GetFirstTextureCallback(RwTexture *tex, void *data) { *(RwTexture**)data = tex; return nil; } RwTexture* GetFirstTexture(RwTexDictionary *txd) { RwTexture *tex; tex = nil; RwTexDictionaryForAllTextures(txd, GetFirstTextureCallback, &tex); return tex; } bool IsClumpSkinned(RpClump *clump) { RpAtomic *atomic = GetFirstAtomic(clump); return atomic ? RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)) : nil; } static RpAtomic* GetAnimHierarchyCallback(RpAtomic *atomic, void *data) { *(RpHAnimHierarchy**)data = RpSkinAtomicGetHAnimHierarchy(atomic); return nil; } RpHAnimHierarchy* GetAnimHierarchyFromSkinClump(RpClump *clump) { RpHAnimHierarchy *hier = nil; RpClumpForAllAtomics(clump, GetAnimHierarchyCallback, &hier); return hier; } static RwFrame* GetAnimHierarchyFromClumpCB(RwFrame *frame, void *data) { RpHAnimHierarchy *hier = RpHAnimFrameGetHierarchy(frame); if(hier){ *(RpHAnimHierarchy**)data = hier; return nil; } RwFrameForAllChildren(frame, GetAnimHierarchyFromClumpCB, data); return frame; } RpHAnimHierarchy* GetAnimHierarchyFromClump(RpClump *clump) { RpHAnimHierarchy *hier = nil; RwFrameForAllChildren(RpClumpGetFrame(clump), GetAnimHierarchyFromClumpCB, &hier); return hier; } void SkinGetBonePositionsToTable(RpClump *clump, RwV3d *boneTable) { int i, parent; RpAtomic *atomic; RpSkin *skin; RpHAnimHierarchy *hier; int numBones; RwMatrix m, invmat; int stack[32]; int sp; if(boneTable == nil) return; atomic = GetFirstAtomic(clump); // mobile, also VC assert(atomic); skin = RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)); assert(skin); hier = GetAnimHierarchyFromSkinClump(clump); assert(hier); boneTable[0].x = 0.0f; boneTable[0].y = 0.0f; boneTable[0].z = 0.0f; numBones = RpSkinGetNumBones(skin); parent = 0; sp = 0; #ifdef FIX_BUGS stack[0] = 0; // i think this is ok #endif for(i = 1; i < numBones; i++){ RwMatrixCopy(&m, &RpSkinGetSkinToBoneMatrices(skin)[i]); RwMatrixInvert(&invmat, &m); const RwMatrix *x = RpSkinGetSkinToBoneMatrices(skin); RwV3dTransformPoints(&boneTable[i], &invmat.pos, 1, &x[parent]); if(HIERNODEINFO(hier)[i].flags & rpHANIMPUSHPARENTMATRIX) stack[++sp] = parent; if(HIERNODEINFO(hier)[i].flags & rpHANIMPOPPARENTMATRIX) parent = stack[sp--]; else parent = i; //assert(parent >= 0 && parent < numBones); } } RpHAnimAnimation* HAnimAnimationCreateForHierarchy(RpHAnimHierarchy *hier) { int i; #if defined FIX_BUGS || defined LIBRW int numNodes = hier->numNodes*2; // you're supposed to have at least two KFs per node #else int numNodes = hier->numNodes; #endif RpHAnimAnimation *anim = RpHAnimAnimationCreate(rpHANIMSTDKEYFRAMETYPEID, numNodes, 0, 0.0f); if(anim == nil) return nil; RpHAnimStdKeyFrame *frame; for(i = 0; i < numNodes; i++){ frame = (RpHAnimStdKeyFrame*)HANIMFRAME(anim, i); // games uses struct size here, not safe frame->q.real = 1.0f; frame->q.imag.x = frame->q.imag.y = frame->q.imag.z = 0.0f; frame->t.x = frame->t.y = frame->t.z = 0.0f; #if defined FIX_BUGS || defined LIBRW // times are subtracted and divided giving NaNs // so they can't both be 0 frame->time = i/hier->numNodes; #else frame->time = 0.0f; #endif frame->prevFrame = nil; } return anim; } void RenderSkeleton(RpHAnimHierarchy *hier) { int i; int sp; int stack[32]; int par; CVector p1, p2; int numNodes = hier->numNodes; RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier); p1 = mats[0].pos; par = 0; sp = 0; stack[sp++] = par; for(i = 1; i < numNodes; i++){ p1 = mats[par].pos; p2 = mats[i].pos; CDebug::AddLine(p1, p2, 0xFFFFFFFF, 0xFFFFFFFF); if(HIERNODEINFO(hier)[i].flags & rpHANIMPUSHPARENTMATRIX) stack[sp++] = par; par = i; if(HIERNODEINFO(hier)[i].flags & rpHANIMPOPPARENTMATRIX) par = stack[--sp]; } } RwBool Im2DRenderQuad(RwReal x1, RwReal y1, RwReal x2, RwReal y2, RwReal z, RwReal recipCamZ, RwReal uvOffset) { RwIm2DVertex vx[4]; /* * Render an opaque white 2D quad at the given coordinates and * spanning a whole texture. */ RwIm2DVertexSetScreenX(&vx[0], x1); RwIm2DVertexSetScreenY(&vx[0], y1); RwIm2DVertexSetScreenZ(&vx[0], z); RwIm2DVertexSetIntRGBA(&vx[0], 255, 255, 255, 255); RwIm2DVertexSetRecipCameraZ(&vx[0], recipCamZ); RwIm2DVertexSetU(&vx[0], uvOffset, recipCamZ); RwIm2DVertexSetV(&vx[0], uvOffset, recipCamZ); RwIm2DVertexSetScreenX(&vx[1], x1); RwIm2DVertexSetScreenY(&vx[1], y2); RwIm2DVertexSetScreenZ(&vx[1], z); RwIm2DVertexSetIntRGBA(&vx[1], 255, 255, 255, 255); RwIm2DVertexSetRecipCameraZ(&vx[1], recipCamZ); RwIm2DVertexSetU(&vx[1], uvOffset, recipCamZ); RwIm2DVertexSetV(&vx[1], 1.0f + uvOffset, recipCamZ); RwIm2DVertexSetScreenX(&vx[2], x2); RwIm2DVertexSetScreenY(&vx[2], y1); RwIm2DVertexSetScreenZ(&vx[2], z); RwIm2DVertexSetIntRGBA(&vx[2], 255, 255, 255, 255); RwIm2DVertexSetRecipCameraZ(&vx[2], recipCamZ); RwIm2DVertexSetU(&vx[2], 1.0f + uvOffset, recipCamZ); RwIm2DVertexSetV(&vx[2], uvOffset, recipCamZ); RwIm2DVertexSetScreenX(&vx[3], x2); RwIm2DVertexSetScreenY(&vx[3], y2); RwIm2DVertexSetScreenZ(&vx[3], z); RwIm2DVertexSetIntRGBA(&vx[3], 255, 255, 255, 255); RwIm2DVertexSetRecipCameraZ(&vx[3], recipCamZ); RwIm2DVertexSetU(&vx[3], 1.0f + uvOffset, recipCamZ); RwIm2DVertexSetV(&vx[3], 1.0f + uvOffset, recipCamZ); RwIm2DRenderPrimitive(rwPRIMTYPETRISTRIP, vx, 4); return TRUE; } bool b_cbsUseLTM = true; RpAtomic *cbsCalcMeanBSphereRadiusCB(RpAtomic *atomic, void *data) { RwV3d atomicPos; if ( b_cbsUseLTM ) RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetLTM(RpClumpGetFrame(atomic->clump))); else RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetMatrix(RpClumpGetFrame(atomic->clump))); RwV3d temp; RwV3dSub(&temp, &atomicPos, &((RwSphere *)data)->center); RwReal radius = RwV3dLength(&temp) + RpAtomicGetBoundingSphere(atomic)->radius; if ( ((RwSphere *)data)->radius < radius ) ((RwSphere *)data)->radius = radius; return atomic; } RpAtomic *cbsCalcMeanBSphereCenterCB(RpAtomic *atomic, void *data) { RwV3d atomicPos; if ( b_cbsUseLTM ) RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetLTM(RpClumpGetFrame(atomic->clump))); else RwV3dTransformPoints(&atomicPos, &RpAtomicGetBoundingSphere(atomic)->center, 1, RwFrameGetMatrix(RpClumpGetFrame(atomic->clump))); RwV3dAdd(&((RwSphere *)data)->center, &((RwSphere *)data)->center, &atomicPos); return atomic; } RpClump *RpClumpGetBoundingSphere(RpClump *clump, RwSphere *sphere, bool useLTM) { RwMatrix matrix; RwSphere result = { 0.0f, 0.0f, 0.0f, 0.0f }; b_cbsUseLTM = useLTM; if ( clump == nil || sphere == nil ) return nil; sphere->radius = 0.0f; sphere->center.x = 0.0f; sphere->center.y = 0.0f; sphere->center.z = 0.0f; RwInt32 numAtomics = RpClumpGetNumAtomics(clump); if ( numAtomics < 1.0f ) return nil; RpClumpForAllAtomics(clump, cbsCalcMeanBSphereCenterCB, &result); RwV3dScale(&result.center, &result.center, 1.0f/numAtomics); RpClumpForAllAtomics(clump, cbsCalcMeanBSphereRadiusCB, &result); if ( b_cbsUseLTM ) RwMatrixInvert(&matrix, RwFrameGetLTM(RpClumpGetFrame(clump))); else RwMatrixInvert(&matrix, RwFrameGetMatrix(RpClumpGetFrame(clump))); RwV3dTransformPoints(&result.center, &result.center, 1, &matrix); *sphere = result; return clump; } void CameraSize(RwCamera * camera, RwRect * rect, RwReal viewWindow, RwReal aspectRatio) { if (camera) { RwVideoMode videoMode; RwRect r; RwRect origSize = { 0, 0, 0, 0 }; // FIX just to make the compier happy RwV2d vw; RwEngineGetVideoModeInfo(&videoMode, RwEngineGetCurrentVideoMode()); origSize.w = RwRasterGetWidth(RwCameraGetRaster(camera)); origSize.h = RwRasterGetHeight(RwCameraGetRaster(camera)); if (!rect) { if (videoMode.flags & rwVIDEOMODEEXCLUSIVE) { /* For full screen applications, resizing the camera just doesn't * make sense, use the video mode size. */ r.x = r.y = 0; r.w = videoMode.width; r.h = videoMode.height; rect = &r; } else { /* rect not specified - reuse current values */ r.w = RwRasterGetWidth(RwCameraGetRaster(camera)); r.h = RwRasterGetHeight(RwCameraGetRaster(camera)); r.x = r.y = 0; rect = &r; } } if (( origSize.w != rect->w ) || ( origSize.h != rect->h )) { RwRaster *raster; RwRaster *zRaster; // BUG: game just changes camera raster's sizes, but this is a hack #if defined FIX_BUGS || defined LIBRW /* * Destroy rasters... */ raster = RwCameraGetRaster(camera); if( raster ) { RwRasterDestroy(raster); camera->frameBuffer = nil; } zRaster = RwCameraGetZRaster(camera); if( zRaster ) { RwRasterDestroy(zRaster); camera->zBuffer = nil; } /* * Create new rasters... */ raster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPECAMERA); zRaster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPEZBUFFER); if( raster && zRaster ) { RwCameraSetRaster(camera, raster); RwCameraSetZRaster(camera, zRaster); } else { if( raster ) { RwRasterDestroy(raster); } if( zRaster ) { RwRasterDestroy(zRaster); } rect->x = origSize.x; rect->y = origSize.y; rect->w = origSize.w; rect->h = origSize.h; /* * Use default values... */ raster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPECAMERA); zRaster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPEZBUFFER); RwCameraSetRaster(camera, raster); RwCameraSetZRaster(camera, zRaster); } #else raster = RwCameraGetRaster(camera); zRaster = RwCameraGetZRaster(camera); raster->width = zRaster->width = rect->w; raster->height = zRaster->height = rect->h; #endif } /* Figure out the view window */ if (videoMode.flags & rwVIDEOMODEEXCLUSIVE) { /* derive ratio from aspect ratio */ vw.x = viewWindow; vw.y = viewWindow / aspectRatio; } else { /* derive from pixel ratios */ if (rect->w > rect->h) { vw.x = viewWindow; vw.y = (rect->h * viewWindow) / rect->w; } else { vw.x = (rect->w * viewWindow) / rect->h; vw.y = viewWindow; } } RwCameraSetViewWindow(camera, &vw); RsGlobal.width = rect->w; RsGlobal.height = rect->h; } return; } void CameraDestroy(RwCamera *camera) { RwRaster *raster, *tmpRaster; RwFrame *frame; if (camera) { frame = RwCameraGetFrame(camera); if (frame) { RwFrameDestroy(frame); } raster = RwCameraGetRaster(camera); if (raster) { tmpRaster = RwRasterGetParent(raster); RwRasterDestroy(raster); if ((tmpRaster != nil) && (tmpRaster != raster)) { RwRasterDestroy(tmpRaster); } } raster = RwCameraGetZRaster(camera); if (raster) { tmpRaster = RwRasterGetParent(raster); RwRasterDestroy(raster); if ((tmpRaster != nil) && (tmpRaster != raster)) { RwRasterDestroy(tmpRaster); } } RwCameraDestroy(camera); } return; } RwCamera * CameraCreate(RwInt32 width, RwInt32 height, RwBool zBuffer) { RwCamera *camera; camera = RwCameraCreate(); if (camera) { RwCameraSetFrame(camera, RwFrameCreate()); RwCameraSetRaster(camera, RwRasterCreate(0, 0, 0, rwRASTERTYPECAMERA)); if (zBuffer) { RwCameraSetZRaster(camera, RwRasterCreate(0, 0, 0, rwRASTERTYPEZBUFFER)); } /* now check that everything is valid */ if (RwCameraGetFrame(camera) && RwCameraGetRaster(camera) && RwRasterGetParent(RwCameraGetRaster(camera)) && (!zBuffer || (RwCameraGetZRaster(camera) && RwRasterGetParent(RwCameraGetZRaster (camera))))) { /* everything OK */ return (camera); } } /* if we're here then an error must have occurred so clean up */ CameraDestroy(camera); return (nil); } #ifdef LIBRW #include #include "VehicleModelInfo.h" int32 findPlatform(rw::Atomic *a) { rw::Geometry *g = a->geometry; if(g->instData) return g->instData->platform; return 0; } // Game doesn't read atomic extensions so we never get any other than the default pipe, // but we need it for uninstancing void attachPipe(rw::Atomic *atomic) { if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic))) atomic->pipeline = rw::skinGlobals.pipelines[rw::platform]; else{ int fx = rpMATFXEFFECTNULL; RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), CVehicleModelInfo::GetMatFXEffectMaterialCB, &fx); if(fx != rpMATFXEFFECTNULL) RpMatFXAtomicEnableEffects(atomic); } } // Attach pipes for the platform we have native data for so we can uninstance void switchPipes(rw::Atomic *a, int32 platform) { if(a->pipeline && a->pipeline->platform != platform){ uint32 plgid = a->pipeline->pluginID; switch(plgid){ // assume default pipe won't be attached explicitly case rw::ID_SKIN: a->pipeline = rw::skinGlobals.pipelines[platform]; break; case rw::ID_MATFX: a->pipeline = rw::matFXGlobals.pipelines[platform]; break; } } } RpAtomic* ConvertPlatformAtomic(RpAtomic *atomic, void *data) { int32 driver = rw::platform; int32 platform = findPlatform(atomic); if(platform != 0 && platform != driver){ attachPipe(atomic); // kludge rw::ObjPipeline *origPipe = atomic->pipeline; rw::platform = platform; switchPipes(atomic, rw::platform); if(atomic->geometry->flags & rw::Geometry::NATIVE) atomic->uninstance(); // no ADC in this game //rw::ps2::unconvertADC(atomic->geometry); rw::platform = driver; atomic->pipeline = origPipe; } return atomic; } #endif #if defined(FIX_BUGS) && defined(GTA_PC) RwUInt32 saved_alphafunc, saved_alpharef; void SetAlphaTest(RwUInt32 alpharef) { #ifdef LIBRW saved_alphafunc = rw::GetRenderState(rw::ALPHATESTFUNC); saved_alpharef = rw::GetRenderState(rw::ALPHATESTREF); rw::SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL); rw::SetRenderState(rw::ALPHATESTREF, 0); #else RwD3D8GetRenderState(D3DRS_ALPHAFUNC, &saved_alphafunc); RwD3D8GetRenderState(D3DRS_ALPHAREF, &saved_alpharef); RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); RwD3D8SetRenderState(D3DRS_ALPHAREF, alpharef); #endif } void RestoreAlphaTest() { #ifdef LIBRW rw::SetRenderState(rw::ALPHATESTFUNC, saved_alphafunc); rw::SetRenderState(rw::ALPHATESTREF, saved_alpharef); #else RwD3D8SetRenderState(D3DRS_ALPHAFUNC, saved_alphafunc); RwD3D8SetRenderState(D3DRS_ALPHAREF, saved_alpharef); #endif } #endif ================================================ FILE: src/rw/RwHelper.h ================================================ #pragma once extern bool bDebugRenderGroups; extern bool gPS2alphaTest; extern bool gBackfaceCulling; void OpenCharsetSafe(); void CreateDebugFont(); void DestroyDebugFont(); void ObrsPrintfString(const char *str, short x, short y); void FlushObrsPrintfs(); void DefinedState(void); void SetCullMode(uint32 mode); RwFrame *GetFirstChild(RwFrame *frame); RwObject *GetFirstObject(RwFrame *frame); RpAtomic *GetFirstAtomic(RpClump *clump); RwTexture *GetFirstTexture(RwTexDictionary *txd); bool IsClumpSkinned(RpClump *clump); RpHAnimHierarchy *GetAnimHierarchyFromSkinClump(RpClump *clump); // get from atomic RpHAnimHierarchy *GetAnimHierarchyFromClump(RpClump *clump); // get from frame void SkinGetBonePositionsToTable(RpClump *clump, RwV3d *boneTable); RpHAnimAnimation *HAnimAnimationCreateForHierarchy(RpHAnimHierarchy *hier); RpAtomic *AtomicRemoveAnimFromSkinCB(RpAtomic *atomic, void *data); void RenderSkeleton(RpHAnimHierarchy *hier); RwBool Im2DRenderQuad(RwReal x1, RwReal y1, RwReal x2, RwReal y2, RwReal z, RwReal recipCamZ, RwReal uvOffset); RpClump *RpClumpGetBoundingSphere(RpClump *clump, RwSphere *sphere, bool useLTM); RwTexDictionary *RwTexDictionaryGtaStreamRead(RwStream *stream); RwTexDictionary *RwTexDictionaryGtaStreamRead1(RwStream *stream); RwTexDictionary *RwTexDictionaryGtaStreamRead2(RwStream *stream, RwTexDictionary *texDict); void ReadVideoCardCapsFile(uint32&, uint32&, uint32&, uint32&); bool CheckVideoCardCaps(void); void WriteVideoCardCapsFile(void); bool CanVideoCardDoDXT(void); void ConvertingTexturesScreen(uint32, uint32, const char*); void DealWithTxdWriteError(uint32, uint32, const char*); bool CreateTxdImageForVideoCard(); bool RpClumpGtaStreamRead1(RwStream *stream); RpClump *RpClumpGtaStreamRead2(RwStream *stream); void RpClumpGtaCancelStream(void); void CameraSize(RwCamera *camera, RwRect *rect, RwReal viewWindow, RwReal aspectRatio); void CameraDestroy(RwCamera *camera); RwCamera *CameraCreate(RwInt32 width, RwInt32 height, RwBool zBuffer); RpAtomic *ConvertPlatformAtomic(RpAtomic *atomic, void *data); #if defined(FIX_BUGS) && defined (GTA_PC) void SetAlphaTest(RwUInt32 alpharef); void RestoreAlphaTest(); #else #define SetAlphaTest(a) (0) #define RestoreAlphaTest() (0) #endif ================================================ FILE: src/rw/RwMatFX.cpp ================================================ #ifndef LIBRW #define WITHD3D #include "common.h" #include "rpmatfx.h" struct MatFXNothing { int pad[5]; int effect; }; struct MatFXBump { RwFrame *bumpFrame; RwTexture *bumpedTex; RwTexture *bumpTex; float negBumpCoefficient; int pad; int effect; }; struct MatFXEnv { RwFrame *envFrame; RwTexture *envTex; float envCoeff; int envFBalpha; int pad; int effect; }; struct MatFXDual { RwTexture *dualTex; RwInt32 srcBlend; RwInt32 dstBlend; }; struct MatFX { union { MatFXNothing n; MatFXBump b; MatFXEnv e; MatFXDual d; } fx[2]; int effects; }; extern "C" { extern int MatFXMaterialDataOffset; extern int MatFXAtomicDataOffset; void _rpMatFXD3D8AtomicMatFXEnvRender(RxD3D8InstanceData* inst, int flags, int sel, RwTexture* texture, RwTexture* envMap); void _rpMatFXD3D8AtomicMatFXRenderBlack(RxD3D8InstanceData *inst); void _rpMatFXD3D8AtomicMatFXBumpMapRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture, RwTexture *bumpMap, RwTexture *envMap); void _rpMatFXD3D8AtomicMatFXDualPassRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture, RwTexture *dualTexture); } #ifdef PS2_MATFX void _rpMatFXD3D8AtomicMatFXDefaultRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture) { if(flags & (rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2) && texture) RwD3D8SetTexture(texture, 0); else RwD3D8SetTexture(nil, 0); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(inst->vertexAlpha || inst->material->color.alpha != 0xFF)); RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, inst->vertexAlpha != 0); RwD3D8SetPixelShader(0); RwD3D8SetVertexShader(inst->vertexShader); RwD3D8SetStreamSource(0, inst->vertexBuffer, inst->stride); if(inst->indexBuffer){ RwD3D8SetIndices(inst->indexBuffer, inst->baseIndex); RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices); }else RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices); } // map [-1; -1] -> [0; 1], flip V static RwMatrix scalenormal = { { 0.5f, 0.0f, 0.0f }, 0, { 0.0f, -0.5f, 0.0f }, 0, { 0.0f, 0.0f, 1.0f }, 0, { 0.5f, 0.5f, 0.0f }, 0, }; // flipped U for PS2 static RwMatrix scalenormal_flipU = { { -0.5f, 0.0f, 0.0f }, 0, { 0.0f, -0.5f, 0.0f }, 0, { 0.0f, 0.0f, 1.0f }, 0, { 0.5f, 0.5f, 0.0f }, 0, }; void ApplyEnvMapTextureMatrix(RwTexture *tex, int n, RwFrame *frame) { RwD3D8SetTexture(tex, n); RwD3D8SetTextureStageState(n, D3DRS_ALPHAREF, 2); RwD3D8SetTextureStageState(n, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL); if(frame){ RwMatrix *envframemat = RwMatrixCreate(); RwMatrix *tmpmat = RwMatrixCreate(); RwMatrix *envmat = RwMatrixCreate(); RwMatrixInvert(envframemat, RwFrameGetLTM(frame)); // PS2 // can this be simplified? *tmpmat = *RwFrameGetLTM(RwCameraGetFrame((RwCamera*)RWSRCGLOBAL(curCamera))); RwV3dNegate(&tmpmat->right, &tmpmat->right); tmpmat->flags = 0; tmpmat->pos.x = 0.0f; tmpmat->pos.y = 0.0f; tmpmat->pos.z = 0.0f; RwMatrixMultiply(envmat, tmpmat, envframemat); *tmpmat = *envmat; // important because envframemat can have a translation that we don't like tmpmat->pos.x = 0.0f; tmpmat->pos.y = 0.0f; tmpmat->pos.z = 0.0f; // for some reason we flip in U as well RwMatrixMultiply(envmat, tmpmat, &scalenormal_flipU); RwD3D8SetTransform(D3DTS_TEXTURE0+n, envmat); RwMatrixDestroy(envmat); RwMatrixDestroy(tmpmat); RwMatrixDestroy(envframemat); }else RwD3D8SetTransform(D3DTS_TEXTURE0+n, &scalenormal); } void _rpMatFXD3D8AtomicMatFXEnvRender_ps2(RxD3D8InstanceData *inst, int flags, int sel, RwTexture *texture, RwTexture *envMap) { MatFX *matfx = *RWPLUGINOFFSET(MatFX*, inst->material, MatFXMaterialDataOffset); MatFXEnv *env = &matfx->fx[sel].e; uint8 intens = (uint8)(env->envCoeff*255.0f); if(intens == 0 || envMap == nil){ if(sel == 0) _rpMatFXD3D8AtomicMatFXDefaultRender(inst, flags, texture); return; } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(inst->vertexAlpha || inst->material->color.alpha != 0xFF)); if(flags & (rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2) && texture) RwD3D8SetTexture(texture, 0); else RwD3D8SetTexture(nil, 0); RwD3D8SetPixelShader(0); RwD3D8SetVertexShader(inst->vertexShader); RwD3D8SetStreamSource(0, inst->vertexBuffer, inst->stride); RwD3D8SetIndices(inst->indexBuffer, inst->baseIndex); if(inst->indexBuffer) RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices); else RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices); // Effect pass ApplyEnvMapTextureMatrix(envMap, 0, env->envFrame); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwUInt32 src, dst, lighting, zwrite, fog, fogcol; RwRenderStateGet(rwRENDERSTATESRCBLEND, &src); RwRenderStateGet(rwRENDERSTATEDESTBLEND, &dst); // This is of course not using framebuffer alpha, // but if the diffuse texture had no alpha, the result should actually be rather the same if(env->envFBalpha) RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); else RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting); RwD3D8GetRenderState(D3DRS_ZWRITEENABLE, &zwrite); RwD3D8GetRenderState(D3DRS_FOGENABLE, &fog); RwD3D8SetRenderState(D3DRS_ZWRITEENABLE, FALSE); if(fog){ RwD3D8GetRenderState(D3DRS_FOGCOLOR, &fogcol); RwD3D8SetRenderState(D3DRS_FOGCOLOR, 0); } D3DCOLOR texfactor = D3DCOLOR_RGBA(intens, intens, intens, intens); RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, texfactor); RwD3D8SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); RwD3D8SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT); RwD3D8SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TFACTOR); // alpha unused //RwD3D8SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); //RwD3D8SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT); //RwD3D8SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_TFACTOR); if(inst->indexBuffer) RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices); else RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices); // Reset states RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)src); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)dst); RwD3D8SetRenderState(D3DRS_LIGHTING, lighting); RwD3D8SetRenderState(D3DRS_ZWRITEENABLE, zwrite); if(fog) RwD3D8SetRenderState(D3DRS_FOGCOLOR, fogcol); RwD3D8SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); RwD3D8SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); RwD3D8SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, 0); RwD3D8SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); } void _rwD3D8EnableClippingIfNeeded(void *object, RwUInt8 type) { int clip; if (type == rpATOMIC) clip = !RwD3D8CameraIsSphereFullyInsideFrustum(RwCameraGetCurrentCameraMacro(), RpAtomicGetWorldBoundingSphere((RpAtomic *)object)); else clip = !RwD3D8CameraIsBBoxFullyInsideFrustum(RwCameraGetCurrentCameraMacro(), &((RpWorldSector *)object)->tightBoundingBox); RwD3D8SetRenderState(D3DRS_CLIPPING, clip); } void _rwD3D8AtomicMatFXRenderCallback(RwResEntry *repEntry, void *object, RwUInt8 type, RwUInt32 flags) { RwBool lighting; RwBool forceBlack; RxD3D8ResEntryHeader *header; RxD3D8InstanceData *inst; RwInt32 i; if (flags & rpGEOMETRYPRELIT) { RwD3D8SetRenderState(D3DRS_COLORVERTEX, 1); RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1); } else { RwD3D8SetRenderState(D3DRS_COLORVERTEX, 0); RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL); } _rwD3D8EnableClippingIfNeeded(object, type); RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting); if (lighting || flags & rpGEOMETRYPRELIT) { forceBlack = FALSE; } else { forceBlack = TRUE; RwD3D8SetTexture(nil, 0); RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(0, 0, 0, 255)); RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); } header = (RxD3D8ResEntryHeader *)(repEntry + 1); inst = (RxD3D8InstanceData *)(header + 1); for (i = 0; i < header->numMeshes; i++) { if (forceBlack) _rpMatFXD3D8AtomicMatFXRenderBlack(inst); else { if (lighting) RwD3D8SetSurfaceProperties(&inst->material->color, &inst->material->surfaceProps, flags & rpGEOMETRYMODULATEMATERIALCOLOR); MatFX *matfx = *RWPLUGINOFFSET(MatFX *, inst->material, MatFXMaterialDataOffset); int effect = matfx ? matfx->effects : rpMATFXEFFECTNULL; switch (effect) { case rpMATFXEFFECTNULL: default: _rpMatFXD3D8AtomicMatFXDefaultRender(inst, flags, inst->material->texture); break; case rpMATFXEFFECTBUMPMAP: _rpMatFXD3D8AtomicMatFXBumpMapRender(inst, flags, inst->material->texture, matfx->fx[0].b.bumpedTex, nil); break; case rpMATFXEFFECTENVMAP: { // TODO: matfx switch in the settings //_rpMatFXD3D8AtomicMatFXEnvRender(inst, flags, 0, inst->material->texture, matfx->fx[0].e.envTex); _rpMatFXD3D8AtomicMatFXEnvRender_ps2(inst, flags, 0, inst->material->texture, matfx->fx[0].e.envTex); break; } case rpMATFXEFFECTBUMPENVMAP: _rpMatFXD3D8AtomicMatFXBumpMapRender(inst, flags, inst->material->texture, matfx->fx[0].b.bumpedTex, matfx->fx[1].e.envTex); break; case rpMATFXEFFECTDUAL: _rpMatFXD3D8AtomicMatFXDualPassRender(inst, flags, inst->material->texture, matfx->fx[0].d.dualTex); break; } } inst++; } if (forceBlack) { RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); } } void ReplaceMatFxCallback() { RxD3D8AllInOneSetRenderCallBack( RxPipelineFindNodeByName(RpMatFXGetD3D8Pipeline(rpMATFXD3D8ATOMICPIPELINE), RxNodeDefinitionGetD3D8AtomicAllInOne()->name, nil, nil), _rwD3D8AtomicMatFXRenderCallback); } #endif // PS2_MATFX #endif // !LIBRW ================================================ FILE: src/rw/RwPS2AlphaTest.cpp ================================================ #ifndef LIBRW #define WITHD3D #include "common.h" #ifdef PS2_ALPHA_TEST #include "rwcore.h" extern "C" { RwBool _rwD3D8RenderStateIsVertexAlphaEnable(void); RwBool _rwD3D8RenderStateVertexAlphaEnable(RwBool enable); RwRaster *_rwD3D8RWGetRasterStage(RwUInt32 stage); } extern bool gPS2alphaTest; void _rxD3D8DualPassRenderCallback(RwResEntry *repEntry, void *object, RwUInt8 type, RwUInt32 flags) { RxD3D8ResEntryHeader *resEntryHeader; RxD3D8InstanceData *instancedData; RwInt32 numMeshes; RwBool lighting; RwBool vertexAlphaBlend; RwBool forceBlack; RwUInt32 ditherEnable; RwUInt32 shadeMode; void *lastVertexBuffer; /* Get lighting state */ RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting); forceBlack = FALSE; if (lighting) { if (flags & rxGEOMETRY_PRELIT) { /* Emmisive color from the vertex colors */ RwD3D8SetRenderState(D3DRS_COLORVERTEX, TRUE); RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1); } else { /* Emmisive color from material, set to black in the submit node */ RwD3D8SetRenderState(D3DRS_COLORVERTEX, FALSE); RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL); } } else { if ((flags & rxGEOMETRY_PRELIT) == 0) { forceBlack = TRUE; RwD3D8GetRenderState(D3DRS_DITHERENABLE, &ditherEnable); RwD3D8GetRenderState(D3DRS_SHADEMODE, &shadeMode); RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, 0xff000000); RwD3D8SetRenderState(D3DRS_DITHERENABLE, FALSE); RwD3D8SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); } } /* Enable clipping */ if (type == rpATOMIC) { RpAtomic *atomic; RwCamera *cam; atomic = (RpAtomic *)object; cam = RwCameraGetCurrentCamera(); // RWASSERT(cam); if (RwD3D8CameraIsSphereFullyInsideFrustum(cam, RpAtomicGetWorldBoundingSphere(atomic))) { RwD3D8SetRenderState(D3DRS_CLIPPING, FALSE); } else { RwD3D8SetRenderState(D3DRS_CLIPPING, TRUE); } } else { RpWorldSector *worldSector; RwCamera *cam; worldSector = (RpWorldSector *)object; cam = RwCameraGetCurrentCamera(); // RWASSERT(cam); if (RwD3D8CameraIsBBoxFullyInsideFrustum(cam, RpWorldSectorGetTightBBox(worldSector))) { RwD3D8SetRenderState(D3DRS_CLIPPING, FALSE); } else { RwD3D8SetRenderState(D3DRS_CLIPPING, TRUE); } } /* Set texture to NULL if hasn't any texture flags */ if ((flags & (rxGEOMETRY_TEXTURED | rpGEOMETRYTEXTURED2)) == 0) { RwD3D8SetTexture(NULL, 0); if (forceBlack) { RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); RwD3D8SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); } } /* Get vertex alpha Blend state */ vertexAlphaBlend = _rwD3D8RenderStateIsVertexAlphaEnable(); /* Set Last vertex buffer to force the call */ lastVertexBuffer = (void *)0xffffffff; /* Get the instanced data */ resEntryHeader = (RxD3D8ResEntryHeader *)(repEntry + 1); instancedData = (RxD3D8InstanceData *)(resEntryHeader + 1); /* * Data shared between meshes */ /* * Set the Default Pixel shader */ RwD3D8SetPixelShader(0); /* * Vertex shader */ RwD3D8SetVertexShader(instancedData->vertexShader); /* Get the number of meshes */ numMeshes = resEntryHeader->numMeshes; while (numMeshes--) { // RWASSERT(instancedData->material != NULL); if ((flags & (rxGEOMETRY_TEXTURED | rpGEOMETRYTEXTURED2))) { RwD3D8SetTexture(instancedData->material->texture, 0); if (forceBlack) { /* Only change the colorop, we need to use the texture alpha channel */ RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); } } if (instancedData->vertexAlpha || (0xFF != instancedData->material->color.alpha)) { if (!vertexAlphaBlend) { vertexAlphaBlend = TRUE; _rwD3D8RenderStateVertexAlphaEnable(TRUE); } } else { if (vertexAlphaBlend) { vertexAlphaBlend = FALSE; _rwD3D8RenderStateVertexAlphaEnable(FALSE); } } if (lighting) { if (instancedData->vertexAlpha) { RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1); } else { RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL); } RwD3D8SetSurfaceProperties(&instancedData->material->color, &instancedData->material->surfaceProps, (flags & rxGEOMETRY_MODULATE)); } /* * Render */ /* Set the stream source */ if (lastVertexBuffer != instancedData->vertexBuffer) { RwD3D8SetStreamSource(0, instancedData->vertexBuffer, instancedData->stride); lastVertexBuffer = instancedData->vertexBuffer; } if (!gPS2alphaTest) { /* Set the Index buffer */ if (instancedData->indexBuffer != NULL) { RwD3D8SetIndices(instancedData->indexBuffer, instancedData->baseIndex); /* Draw the indexed primitive */ RwD3D8DrawIndexedPrimitive((D3DPRIMITIVETYPE)instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices); } else { RwD3D8DrawPrimitive((D3DPRIMITIVETYPE)instancedData->primType, instancedData->baseIndex, instancedData->numVertices); } } else { RwD3D8SetIndices(instancedData->indexBuffer, instancedData->baseIndex); int hasAlpha, alphafunc, alpharef, zwrite; RwD3D8GetRenderState(D3DRS_ALPHABLENDENABLE, &hasAlpha); RwD3D8GetRenderState(D3DRS_ZWRITEENABLE, &zwrite); if (hasAlpha && zwrite) { RwD3D8GetRenderState(D3DRS_ALPHAFUNC, &alphafunc); RwD3D8GetRenderState(D3DRS_ALPHAREF, &alpharef); RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); RwD3D8SetRenderState(D3DRS_ALPHAREF, 128); if (instancedData->indexBuffer) RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices); else RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices); RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_LESS); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE); if (instancedData->indexBuffer) RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices); else RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices); RwD3D8SetRenderState(D3DRS_ALPHAFUNC, alphafunc); RwD3D8SetRenderState(D3DRS_ALPHAREF, alpharef); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); } else { if (instancedData->indexBuffer) RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices); else RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices); } } /* Move onto the next instancedData */ instancedData++; } if (forceBlack) { RwD3D8SetRenderState(D3DRS_DITHERENABLE, ditherEnable); RwD3D8SetRenderState(D3DRS_SHADEMODE, shadeMode); if (_rwD3D8RWGetRasterStage(0)) { RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); } else { RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); } } } void ReplaceAtomicPipeCallback() { RxD3D8AllInOneSetRenderCallBack(RxPipelineFindNodeByName(RXPIPELINEGLOBAL(platformAtomicPipeline), RxNodeDefinitionGetD3D8AtomicAllInOne()->name, nil, nil), _rxD3D8DualPassRenderCallback); } #endif // PS2_ALPHA_TEST #endif // !LIBRW ================================================ FILE: src/rw/TexRead.cpp ================================================ #pragma warning( push ) #pragma warning( disable : 4005) #pragma warning( pop ) #define FORCE_PC_SCALING #ifndef LIBRW #define WITHD3D #endif #include "common.h" #ifdef ANISOTROPIC_FILTERING #include "rpanisot.h" #endif #include "crossplatform.h" #include "platform.h" #include "Timer.h" #ifdef GTA_PC #include "FileMgr.h" #include "Pad.h" #include "main.h" #include "Directory.h" #include "Streaming.h" #include "TxdStore.h" #include "CdStream.h" #include "Font.h" #include "Sprite2d.h" #include "Text.h" #include "RwHelper.h" #include "Frontend.h" #endif //GTA_PC float texLoadTime; int32 texNumLoaded; #ifdef LIBRW #define READNATIVE(stream, tex, size) rwNativeTextureHackRead(stream, tex, size) #else #define READNATIVE(stream, tex, size) RWSRCGLOBAL(stdFunc[rwSTANDARDNATIVETEXTUREREAD](stream, tex, size)) #endif RwTexture* RwTextureGtaStreamRead(RwStream *stream) { RwUInt32 size, version; RwTexture *tex; if(!RwStreamFindChunk(stream, rwID_TEXTURENATIVE, &size, &version)) return nil; float preloadTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); if(!READNATIVE(stream, &tex, size)) return nil; if (gGameState == GS_INIT_PLAYING_GAME) { texLoadTime = (texNumLoaded * texLoadTime + (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond() - preloadTime) / (float)(texNumLoaded+1); texNumLoaded++; } #ifdef ANISOTROPIC_FILTERING if(tex && RpAnisotGetMaxSupportedMaxAnisotropy() > 1) // BUG? this was RpAnisotTextureGetMaxAnisotropy, but that doesn't make much sense RpAnisotTextureSetMaxAnisotropy(tex, RpAnisotGetMaxSupportedMaxAnisotropy()); #endif return tex; } RwTexture* destroyTexture(RwTexture *texture, void *data) { RwTextureDestroy(texture); return texture; } RwTexDictionary* RwTexDictionaryGtaStreamRead(RwStream *stream) { RwUInt32 size, version; RwInt32 numTextures; RwTexDictionary *texDict; RwTexture *tex; if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) return nil; if(RwStreamRead(stream, &numTextures, size) != size) return nil; texDict = RwTexDictionaryCreate(); if(texDict == nil) return nil; while(numTextures--){ tex = RwTextureGtaStreamRead(stream); if(tex == nil){ RwTexDictionaryForAllTextures(texDict, destroyTexture, nil); RwTexDictionaryDestroy(texDict); return nil; } RwTexDictionaryAddTexture(texDict, tex); } return texDict; } static int32 numberTextures = -1; static int32 streamPosition; RwTexDictionary* RwTexDictionaryGtaStreamRead1(RwStream *stream) { RwUInt32 size, version; RwInt32 numTextures; RwTexDictionary *texDict; RwTexture *tex; numberTextures = 0; if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version)) return nil; assert(size == 4); if(RwStreamRead(stream, &numTextures, size) != size) return nil; texDict = RwTexDictionaryCreate(); if(texDict == nil) return nil; numberTextures = numTextures/2; while(numTextures > numberTextures){ numTextures--; tex = RwTextureGtaStreamRead(stream); if(tex == nil){ RwTexDictionaryForAllTextures(texDict, destroyTexture, nil); RwTexDictionaryDestroy(texDict); return nil; } RwTexDictionaryAddTexture(texDict, tex); } numberTextures = numTextures; streamPosition = STREAMPOS(stream); return texDict; } RwTexDictionary* RwTexDictionaryGtaStreamRead2(RwStream *stream, RwTexDictionary *texDict) { RwTexture *tex; RwStreamSkip(stream, streamPosition - STREAMPOS(stream)); while(numberTextures--){ tex = RwTextureGtaStreamRead(stream); if(tex == nil){ RwTexDictionaryForAllTextures(texDict, destroyTexture, nil); RwTexDictionaryDestroy(texDict); return nil; } RwTexDictionaryAddTexture(texDict, tex); } return texDict; } #ifdef GTA_PC #ifdef LIBRW #define CAPSVERSION 0 struct GPUcaps { uint32 version; // so we can force regeneration easily uint32 platform; uint32 subplatform; uint32 dxtSupport; }; static void GetGPUcaps(GPUcaps *caps) { caps->version = CAPSVERSION; caps->platform = rw::platform; caps->subplatform = 0; caps->dxtSupport = 0; // TODO: more later #ifdef RW_GL3 caps->subplatform = rw::gl3::gl3Caps.gles; caps->dxtSupport = rw::gl3::gl3Caps.dxtSupported; #endif #ifdef RW_D3D9 caps->dxtSupport = 1; // TODO, probably #endif } void ReadVideoCardCapsFile(GPUcaps *caps) { memset(caps, 0, sizeof(GPUcaps)); int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "rb"); if (file != 0) { CFileMgr::Read(file, (char*)&caps->version, 4); CFileMgr::Read(file, (char*)&caps->platform, 4); CFileMgr::Read(file, (char*)&caps->subplatform, 4); CFileMgr::Read(file, (char*)&caps->dxtSupport, 4); CFileMgr::CloseFile(file); } } bool CheckVideoCardCaps(void) { GPUcaps caps, fcaps; GetGPUcaps(&caps); ReadVideoCardCapsFile(&fcaps); return caps.version != fcaps.version || caps.platform != fcaps.platform || caps.subplatform != fcaps.subplatform || caps.dxtSupport != fcaps.dxtSupport; } void WriteVideoCardCapsFile(void) { GPUcaps caps; GetGPUcaps(&caps); int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "wb"); if (file != 0) { CFileMgr::Write(file, (char*)&caps.version, 4); CFileMgr::Write(file, (char*)&caps.platform, 4); CFileMgr::Write(file, (char*)&caps.subplatform, 4); CFileMgr::Write(file, (char*)&caps.dxtSupport, 4); CFileMgr::CloseFile(file); } } #else extern "C" RwInt32 _rwD3D8FindCorrectRasterFormat(RwRasterType type, RwInt32 flags); extern "C" RwBool _rwD3D8CheckValidTextureFormat(RwInt32 format); void ReadVideoCardCapsFile(uint32 &cap32, uint32 &cap24, uint32 &cap16, uint32 &cap8) { cap32 = UINT32_MAX; cap24 = UINT32_MAX; cap16 = UINT32_MAX; cap8 = UINT32_MAX; int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "rb"); if (file != 0) { CFileMgr::Read(file, (char*)&cap32, 4); CFileMgr::Read(file, (char*)&cap24, 4); CFileMgr::Read(file, (char*)&cap16, 4); CFileMgr::Read(file, (char*)&cap8, 4); CFileMgr::CloseFile(file); } } bool CheckVideoCardCaps(void) { uint32 cap32 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT8888); uint32 cap24 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT888); uint32 cap16 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT1555); uint32 cap8 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMATPAL8 | rwRASTERFORMAT8888); uint32 fcap32, fcap24, fcap16, fcap8; ReadVideoCardCapsFile(fcap32, fcap24, fcap16, fcap8); return cap32 != fcap32 || cap24 != fcap24 || cap16 != fcap16 || cap8 != fcap8; } void WriteVideoCardCapsFile(void) { uint32 cap32 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT8888); uint32 cap24 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT888); uint32 cap16 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT1555); uint32 cap8 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMATPAL8 | rwRASTERFORMAT8888); int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "wb"); if (file != 0) { CFileMgr::Write(file, (char*)&cap32, 4); CFileMgr::Write(file, (char*)&cap24, 4); CFileMgr::Write(file, (char*)&cap16, 4); CFileMgr::Write(file, (char*)&cap8, 4); CFileMgr::CloseFile(file); } } #endif bool CanVideoCardDoDXT(void) { #ifdef LIBRW // TODO #ifdef RW_OPENGL return false; #else return true; #endif #else return _rwD3D8CheckValidTextureFormat(D3DFMT_DXT1) && _rwD3D8CheckValidTextureFormat(D3DFMT_DXT3); #endif } void ConvertingTexturesScreen(uint32 num, uint32 count, const char *text) { HandleExit(); CSprite2d *splash = LoadSplash(nil); if (!DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255)) return; CSprite2d::SetRecipNearClip(); CSprite2d::InitPerFrame(); CFont::InitPerFrame(); DefinedState(); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, 255)); CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), SCREEN_SCALE_FROM_RIGHT(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(64, 64, 64, 255)); #ifdef FIX_BUGS CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), (SCREEN_SCALE_FROM_RIGHT(200.0f) - SCREEN_SCALE_X(200.0f)) * ((float)num / (float)count) + SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(255, 150, 225, 255)); #else CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), (SCREEN_SCALE_FROM_RIGHT(200.0f) - SCREEN_SCALE_X(200.0f)) * ((float)num / (float)count) + SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(255, 217, 106, 255)); #endif CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(120.0f), SCREEN_SCALE_Y(150.0f), SCREEN_SCALE_FROM_RIGHT(120.0f), SCREEN_HEIGHT - SCREEN_SCALE_Y(220.0f)), CRGBA(50, 50, 50, 210)); CFont::SetBackgroundOff(); CFont::SetPropOn(); CFont::SetScale(SCREEN_SCALE_X(0.45f), SCREEN_SCALE_Y(0.7f)); CFont::SetCentreOff(); CFont::SetWrapx(SCREEN_SCALE_FROM_RIGHT(170.0f)); CFont::SetJustifyOff(); #ifdef FIX_BUGS CFont::SetColor(CRGBA(255, 150, 225, 255)); #else CFont::SetColor(CRGBA(255, 217, 106, 255)); #endif CFont::SetBackGroundOnlyTextOff(); CFont::SetFontStyle(FONT_STANDARD); CFont::PrintString(SCREEN_SCALE_X(170.0f), SCREEN_SCALE_Y(160.0f), TheText.Get(text)); CFont::DrawFonts(); DoRWStuffEndOfFrame(); } void DealWithTxdWriteError(uint32 num, uint32 count, const char *text) { while (!RsGlobal.quit) { ConvertingTexturesScreen(num, count, text); CPad::UpdatePads(); if (CPad::GetPad(0)->GetEscapeJustDown()) break; } RsGlobal.quit = false; LoadingScreen(nil, nil, nil); RsGlobal.quit = true; } #ifdef LIBRW #define STREAMTELL(str) str->tell() #else #define STREAMTELL(str) filesys->rwftell((str)->Type.file.fpFile) #endif bool CreateTxdImageForVideoCard() { uint8 *buf = new uint8[CDSTREAM_SECTOR_SIZE]; CDirectory *pDir = new CDirectory(TXDSTORESIZE); CDirectory::DirectoryInfo dirInfo; CStreaming::FlushRequestList(); #ifndef LIBRW RwFileFunctions *filesys = RwOsGetFileInterface(); #endif RwStream *img = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMWRITE, "models\\txd.img"); if (img == nil) { // original code does otherwise and it leaks delete []buf; delete pDir; if (_dwOperatingSystemVersion == OS_WINNT || _dwOperatingSystemVersion == OS_WIN2000 || _dwOperatingSystemVersion == OS_WINXP) DealWithTxdWriteError(0, TXDSTORESIZE, "CVT_CRT"); return false; } #ifdef RW_GL3 // so we can read back DXT with GLES // only works for textures that are not yet loaded // so let's hope that is the case for all rw::gl3::needToReadBackTextures = true; #endif #ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION // let's disable vsync and frame limiter to speed up texture conversion // (actually we probably don't need to disable frame limiter in here, but let's do it just in case =P) int8 vsyncState = FrontEndMenuManager.m_PrefsVsync; int8 frameLimiterState = FrontEndMenuManager.m_PrefsFrameLimiter; FrontEndMenuManager.m_PrefsVsync = 0; FrontEndMenuManager.m_PrefsFrameLimiter = 0; #endif int32 i; for (i = 0; i < TXDSTORESIZE; i++) { ConvertingTexturesScreen(i, TXDSTORESIZE, "CVT_MSG"); if (CTxdStore::GetSlot(i) != nil && CStreaming::IsObjectInCdImage(i + STREAM_OFFSET_TXD)) { #ifdef FIX_BUGS if(strcmp(CTxdStore::GetTxdName(i), "generic") == 0) continue; #endif CStreaming::RequestTxd(i, STREAMFLAGS_KEEP_IN_MEMORY); CStreaming::RequestModelStream(0); CStreaming::FlushChannels(); char filename[64]; sprintf(filename, "%s.txd", CTxdStore::GetTxdName(i)); if (CTxdStore::GetSlot(i)->texDict) { int32 pos = STREAMTELL(img); if (RwTexDictionaryStreamWrite(CTxdStore::GetSlot(i)->texDict, img) == nil) { DealWithTxdWriteError(i, TXDSTORESIZE, "CVT_ERR"); RwStreamClose(img, nil); delete []buf; delete pDir; CStreaming::RemoveTxd(i); #ifdef RW_GL3 rw::gl3::needToReadBackTextures = false; #endif return false; } int32 size = STREAMTELL(img) - pos; int32 num = size % CDSTREAM_SECTOR_SIZE; size /= CDSTREAM_SECTOR_SIZE; if (num != 0) { size++; num = CDSTREAM_SECTOR_SIZE - num; RwStreamWrite(img, buf, num); } dirInfo.offset = pos / CDSTREAM_SECTOR_SIZE; dirInfo.size = size; strncpy(dirInfo.name, filename, sizeof(dirInfo.name)); pDir->AddItem(dirInfo); CStreaming::RemoveTxd(i); } CStreaming::FlushRequestList(); } } #ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION // restore vsync and frame limiter states FrontEndMenuManager.m_PrefsVsync = vsyncState; FrontEndMenuManager.m_PrefsFrameLimiter = frameLimiterState; #endif RwStreamClose(img, nil); delete []buf; #ifdef RW_GL3 rw::gl3::needToReadBackTextures = false; #endif if (!pDir->WriteDirFile("models\\txd.dir")) { DealWithTxdWriteError(i, TXDSTORESIZE, "CVT_ERR"); delete pDir; return false; } delete pDir; WriteVideoCardCapsFile(); return true; } #endif // GTA_PC ================================================ FILE: src/rw/TexturePools.cpp ================================================ #ifndef LIBRW #include #define WITHD3D #include "common.h" #include "TexturePools.h" // TODO: this needs to be integrated into RW extern "C" LPDIRECT3DDEVICE8 _RwD3DDevice; CTexturePool aTexturePools[12]; CPaletteList PaletteList; int numTexturePools; int MaxPaletteIndex; bool bUsePaletteIndex = true; void CTexturePool::Create(D3DFORMAT _Format, int _size, uint32 mipmapLevels, int32 numTextures) { Format = _Format; size = _size; levels = mipmapLevels; pTextures = new IDirect3DTexture8 *[numTextures]; texturesMax = numTextures; texturesNum = 0; texturesUsed = 0; } void CTexturePool::Release() { int i = 0; while (i < texturesNum) { pTextures[i]->Release(); i++; } delete[] pTextures; pTextures = nil; texturesNum = 0; texturesUsed = 0; } IDirect3DTexture8 * CTexturePool::FindTexture() { if (texturesNum == 0) return nil; texturesUsed--; return pTextures[--texturesNum]; } bool CTexturePool::AddTexture(IDirect3DTexture8 *texture) { ++texturesUsed; if (texturesNum >= texturesMax) return false; pTextures[texturesNum] = texture; ++texturesNum; return true; } void CTexturePool::Resize(int numTextures) { if (numTextures == texturesMax) return; IDirect3DTexture8 **newTextures = new IDirect3DTexture8 *[numTextures]; for (int i = 0; i < texturesNum && i < numTextures; i++) newTextures[i] = pTextures[i]; if (numTextures < texturesNum) { for (int i = numTextures; i < texturesNum; i++) pTextures[i]->Release(); } delete[] pTextures; pTextures = newTextures; texturesMax = numTextures; } void CPaletteList::Alloc(int max) { Data = new int[max]; Max = max; Num = 0; } void CPaletteList::Free() { delete[] Data; Data = nil; Num = 0; } int CPaletteList::Find() { if (Num == 0) return -1; return Data[--Num]; } void CPaletteList::Add(int item) { if (Num < Max) Data[Num++] = item; else { Resize(2 * Max); Add(item); } } void CPaletteList::Resize(int max) { if (max == Max) return; int *newData = new int[4 * max]; for (int i = 0; i < Num && i < max; i++) newData[i] = Data[i]; delete[] Data; Data = newData; Max = max; } HRESULT CreateTexture(int width, int height, int levels, D3DFORMAT Format, IDirect3DTexture8 **texture) { if (width == height) { for (int i = 0; i < numTexturePools; i++) { if (width != aTexturePools[i].GetSize() && levels == aTexturePools[i].levels && Format == aTexturePools[i].Format) *texture = aTexturePools[i].FindTexture(); } } if (*texture) return D3D_OK; else return _RwD3DDevice->CreateTexture(width, height, levels, 0, Format, D3DPOOL_MANAGED, texture); } void ReleaseTexture(IDirect3DTexture8 *texture) { int levels = 1; if (texture->GetLevelCount() > 1) levels = 0; D3DSURFACE_DESC SURFACE_DESC; texture->GetLevelDesc(0, &SURFACE_DESC); if (SURFACE_DESC.Width == SURFACE_DESC.Height) { for (int i = 0; i < numTexturePools; i++) { if (SURFACE_DESC.Width == aTexturePools[i].GetSize() && SURFACE_DESC.Format == aTexturePools[i].Format && levels == aTexturePools[i].levels) { if (!aTexturePools[i].AddTexture(texture)) { if (aTexturePools[i].texturesUsed > 3 * aTexturePools[i].texturesMax / 2) { aTexturePools[i].Resize(2 * aTexturePools[i].texturesMax); aTexturePools[i].texturesUsed--; aTexturePools[i].AddTexture(texture); } else { texture->Release(); } } return; } } } if (numTexturePools < 12 && bUsePaletteIndex && levels != 0 && SURFACE_DESC.Width == SURFACE_DESC.Height && (SURFACE_DESC.Width == 64 || SURFACE_DESC.Width == 128 || SURFACE_DESC.Width == 256)) { aTexturePools[numTexturePools].Create(SURFACE_DESC.Format, SURFACE_DESC.Width, 1, 16); aTexturePools[numTexturePools].AddTexture(texture); numTexturePools++; } else texture->Release(); } int FindAvailablePaletteIndex() { int index = PaletteList.Find(); if (index == -1) index = MaxPaletteIndex++; return index; } void AddAvailablePaletteIndex(int index) { if (bUsePaletteIndex) PaletteList.Add(index); } void _TexturePoolsInitialise() { PaletteList.Alloc(100); MaxPaletteIndex = 0; } void _TexturePoolsShutdown() { for (int i = 0; i < numTexturePools; i++) aTexturePools[i].Release(); numTexturePools = 0; bUsePaletteIndex = false; PaletteList.Free(); } #endif // !LIBRW ================================================ FILE: src/rw/TexturePools.h ================================================ #pragma once class CTexturePool { public: D3DFORMAT Format; int size; uint32 levels; int32 texturesMax; int32 texturesUsed; int32 texturesNum; IDirect3DTexture8 **pTextures; public: CTexturePool() {} void Create(D3DFORMAT _Format, int size, uint32 mipmapLevels, int32 numTextures); void Release(); IDirect3DTexture8 *FindTexture(); bool AddTexture(IDirect3DTexture8 *texture); void Resize(int numTextures); #ifdef FIX_BUGS int GetSize() { return size; } #else float GetSize() { return size; } #endif }; class CPaletteList { int Max; int Num; int *Data; public: void Alloc(int max); void Free(); int Find(); void Add(int item); void Resize(int max); }; void _TexturePoolsInitialise(); void _TexturePoolsShutdown(); ================================================ FILE: src/rw/TxdStore.cpp ================================================ #include "common.h" #include "templates.h" #include "General.h" #include "Streaming.h" #include "RwHelper.h" #include "TxdStore.h" CPool *CTxdStore::ms_pTxdPool; RwTexDictionary *CTxdStore::ms_pStoredTxd; void CTxdStore::Initialise(void) { if(ms_pTxdPool == nil) ms_pTxdPool = new CPool(TXDSTORESIZE, "TexDictionary"); } void CTxdStore::Shutdown(void) { if(ms_pTxdPool) delete ms_pTxdPool; } void CTxdStore::GameShutdown(void) { int i; for(i = 0; i < TXDSTORESIZE; i++){ TxdDef *def = GetSlot(i); if(def && GetNumRefs(i) == 0) RemoveTxdSlot(i); } } int CTxdStore::AddTxdSlot(const char *name) { TxdDef *def = ms_pTxdPool->New(); assert(def); def->texDict = nil; def->refCount = 0; strcpy(def->name, name); return ms_pTxdPool->GetJustIndex(def); } void CTxdStore::RemoveTxdSlot(int slot) { TxdDef *def = GetSlot(slot); if(def->texDict) RwTexDictionaryDestroy(def->texDict); ms_pTxdPool->Delete(def); } int CTxdStore::FindTxdSlot(const char *name) { int size = ms_pTxdPool->GetSize(); for(int i = 0; i < size; i++){ TxdDef *def = GetSlot(i); if(def && !CGeneral::faststricmp(def->name, name)) return i; } return -1; } char* CTxdStore::GetTxdName(int slot) { return GetSlot(slot)->name; } void CTxdStore::PushCurrentTxd(void) { ms_pStoredTxd = RwTexDictionaryGetCurrent(); } void CTxdStore::PopCurrentTxd(void) { RwTexDictionarySetCurrent(ms_pStoredTxd); ms_pStoredTxd = nil; } void CTxdStore::SetCurrentTxd(int slot) { RwTexDictionarySetCurrent(GetSlot(slot)->texDict); } void CTxdStore::Create(int slot) { GetSlot(slot)->texDict = RwTexDictionaryCreate(); } int CTxdStore::GetNumRefs(int slot) { return GetSlot(slot)->refCount; } void CTxdStore::AddRef(int slot) { GetSlot(slot)->refCount++; } void CTxdStore::RemoveRef(int slot) { if(--GetSlot(slot)->refCount <= 0) CStreaming::RemoveTxd(slot); } void CTxdStore::RemoveRefWithoutDelete(int slot) { GetSlot(slot)->refCount--; } bool CTxdStore::LoadTxd(int slot, RwStream *stream) { TxdDef *def = GetSlot(slot); if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)){ def->texDict = RwTexDictionaryGtaStreamRead(stream); return def->texDict != nil; } printf("Failed to load TXD\n"); return false; } bool CTxdStore::LoadTxd(int slot, const char *filename) { RwStream *stream; bool ret; ret = false; _rwD3D8TexDictionaryEnableRasterFormatConversion(true); do stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename); while(stream == nil); ret = LoadTxd(slot, stream); RwStreamClose(stream, nil); return ret; } bool CTxdStore::StartLoadTxd(int slot, RwStream *stream) { TxdDef *def = GetSlot(slot); if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)){ def->texDict = RwTexDictionaryGtaStreamRead1(stream); return def->texDict != nil; }else{ printf("Failed to load TXD\n"); return false; } } bool CTxdStore::FinishLoadTxd(int slot, RwStream *stream) { TxdDef *def = GetSlot(slot); def->texDict = RwTexDictionaryGtaStreamRead2(stream, def->texDict); return def->texDict != nil; } void CTxdStore::RemoveTxd(int slot) { TxdDef *def = GetSlot(slot); if(def->texDict) RwTexDictionaryDestroy(def->texDict); def->texDict = nil; } ================================================ FILE: src/rw/TxdStore.h ================================================ #pragma once #include "templates.h" struct TxdDef { RwTexDictionary *texDict; int refCount; char name[20]; }; class CTxdStore { static CPool *ms_pTxdPool; static RwTexDictionary *ms_pStoredTxd; public: static void Initialise(void); static void Shutdown(void); static void GameShutdown(void); static int AddTxdSlot(const char *name); static void RemoveTxdSlot(int slot); static int FindTxdSlot(const char *name); static char *GetTxdName(int slot); static void PushCurrentTxd(void); static void PopCurrentTxd(void); static void SetCurrentTxd(int slot); static void Create(int slot); static int GetNumRefs(int slot); static void AddRef(int slot); static void RemoveRef(int slot); static void RemoveRefWithoutDelete(int slot); static bool LoadTxd(int slot, RwStream *stream); static bool LoadTxd(int slot, const char *filename); static bool StartLoadTxd(int slot, RwStream *stream); static bool FinishLoadTxd(int slot, RwStream *stream); static void RemoveTxd(int slot); static TxdDef *GetSlot(int slot) { assert(slot >= 0); assert(ms_pTxdPool); assert(slot < ms_pTxdPool->GetSize()); return ms_pTxdPool->GetSlot(slot); } static bool isTxdLoaded(int slot); }; ================================================ FILE: src/rw/VisibilityPlugins.cpp ================================================ #include "common.h" #include "RwHelper.h" #include "templates.h" #include "main.h" #include "Entity.h" #include "ModelInfo.h" #include "Lights.h" #include "RwHelper.h" #include "Renderer.h" #include "Camera.h" #include "VisibilityPlugins.h" #include "World.h" #include "custompipes.h" #include "MemoryHeap.h" CLinkList CVisibilityPlugins::m_alphaList; CLinkList CVisibilityPlugins::m_alphaBoatAtomicList; CLinkList CVisibilityPlugins::m_alphaEntityList; CLinkList CVisibilityPlugins::m_alphaUnderwaterEntityList; #ifdef NEW_RENDERER CLinkList CVisibilityPlugins::m_alphaBuildingList; #endif int32 CVisibilityPlugins::ms_atomicPluginOffset = -1; int32 CVisibilityPlugins::ms_framePluginOffset = -1; int32 CVisibilityPlugins::ms_clumpPluginOffset = -1; RwCamera *CVisibilityPlugins::ms_pCamera; RwV3d *CVisibilityPlugins::ms_pCameraPosn; float CVisibilityPlugins::ms_cullCompsDist; float CVisibilityPlugins::ms_vehicleLod0Dist; float CVisibilityPlugins::ms_vehicleLod1Dist; float CVisibilityPlugins::ms_vehicleFadeDist; float CVisibilityPlugins::ms_bigVehicleLod0Dist; float CVisibilityPlugins::ms_bigVehicleLod1Dist; float CVisibilityPlugins::ms_pedLod1Dist; float CVisibilityPlugins::ms_pedFadeDist; #define RENDERCALLBACK AtomicDefaultRenderCallBack void CVisibilityPlugins::Initialise(void) { m_alphaList.Init(NUMALPHALIST); m_alphaList.head.item.sort = 0.0f; m_alphaList.tail.item.sort = 100000000.0f; m_alphaBoatAtomicList.Init(NUMBOATALPHALIST); m_alphaBoatAtomicList.head.item.sort = 0.0f; m_alphaBoatAtomicList.tail.item.sort = 100000000.0f; #ifdef ASPECT_RATIO_SCALE // default 150 if not enough for bigger FOVs m_alphaEntityList.Init(NUMALPHAENTITYLIST * 3); #else m_alphaEntityList.Init(NUMALPHAENTITYLIST); #endif // ASPECT_RATIO_SCALE m_alphaEntityList.head.item.sort = 0.0f; m_alphaEntityList.tail.item.sort = 100000000.0f; m_alphaUnderwaterEntityList.Init(NUMALPHAUNTERWATERENTITYLIST); m_alphaUnderwaterEntityList.head.item.sort = 0.0f; m_alphaUnderwaterEntityList.tail.item.sort = 100000000.0f; #ifdef NEW_RENDERER m_alphaBuildingList.Init(NUMALPHAENTITYLIST); m_alphaBuildingList.head.item.sort = 0.0f; m_alphaBuildingList.tail.item.sort = 100000000.0f; #endif } void CVisibilityPlugins::Shutdown(void) { m_alphaList.Shutdown(); m_alphaBoatAtomicList.Shutdown(); m_alphaEntityList.Shutdown(); m_alphaUnderwaterEntityList.Shutdown(); #ifdef NEW_RENDERER m_alphaBuildingList.Shutdown(); #endif } void CVisibilityPlugins::InitAlphaEntityList(void) { m_alphaEntityList.Clear(); m_alphaBoatAtomicList.Clear(); m_alphaUnderwaterEntityList.Clear(); #ifdef NEW_RENDERER m_alphaBuildingList.Clear(); #endif } bool CVisibilityPlugins::InsertEntityIntoSortedList(CEntity *e, float dist) { #ifdef FIX_BUGS if (!e->m_rwObject) return true; #endif AlphaObjectInfo item; item.entity = e; item.sort = dist; #ifdef NEW_RENDERER if(gbNewRenderer && e->IsBuilding()) return !!m_alphaBuildingList.InsertSorted(item); #endif if(e->bUnderwater && m_alphaUnderwaterEntityList.InsertSorted(item)) return true; return !!m_alphaEntityList.InsertSorted(item); } void CVisibilityPlugins::InitAlphaAtomicList(void) { m_alphaList.Clear(); } bool CVisibilityPlugins::InsertAtomicIntoSortedList(RpAtomic *a, float dist) { AlphaObjectInfo item; item.atomic = a; item.sort = dist; return !!m_alphaList.InsertSorted(item); } bool CVisibilityPlugins::InsertAtomicIntoBoatSortedList(RpAtomic *a, float dist) { AlphaObjectInfo item; item.atomic = a; item.sort = dist; return !!m_alphaBoatAtomicList.InsertSorted(item); } // can't increase this yet unfortunately... // probably have to fix fading for this so material alpha isn't overwritten #define VEHICLE_LODDIST_MULTIPLIER (TheCamera.GenerationDistMultiplier) void CVisibilityPlugins::SetRenderWareCamera(RwCamera *camera) { ms_pCamera = camera; ms_pCameraPosn = RwMatrixGetPos(RwFrameGetMatrix(RwCameraGetFrame(camera))); if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED) ms_cullCompsDist = 1000000.0f; else ms_cullCompsDist = sq(TheCamera.LODDistMultiplier * 20.0f); ms_vehicleLod0Dist = sq(70.0f * VEHICLE_LODDIST_MULTIPLIER); ms_vehicleLod1Dist = sq(90.0f * VEHICLE_LODDIST_MULTIPLIER); ms_vehicleFadeDist = sq(100.0f * VEHICLE_LODDIST_MULTIPLIER); ms_bigVehicleLod0Dist = sq(60.0f * VEHICLE_LODDIST_MULTIPLIER); ms_bigVehicleLod1Dist = sq(150.0f * VEHICLE_LODDIST_MULTIPLIER); ms_pedLod1Dist = sq(60.0f * TheCamera.LODDistMultiplier); ms_pedFadeDist = sq(70.0f * TheCamera.LODDistMultiplier); } static float DistToCameraSq; static float PitchToCamera; void CVisibilityPlugins::SetupVehicleVariables(RpClump *vehicle) { if (RwObjectGetType((RwObject*)vehicle) != rpCLUMP) return; DistToCameraSq = GetDistanceSquaredFromCamera(RpClumpGetFrame(vehicle)); RwV3d distToCam; RwV3dSub(&distToCam, ms_pCameraPosn, &RwFrameGetMatrix(RpClumpGetFrame(vehicle))->pos); float dist2d = Sqrt(SQR(distToCam.x) + SQR(distToCam.y)); PitchToCamera = Atan2(distToCam.z, dist2d); } RpMaterial* SetAlphaCB(RpMaterial *material, void *data) { ((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data; return material; } RpMaterial* SetTextureCB(RpMaterial *material, void *data) { RpMaterialSetTexture(material, (RwTexture*)data); return material; } void CVisibilityPlugins::RenderAtomicList(CLinkList &list) { CLink *node; for(node = list.tail.prev; node != &list.head; node = node->prev) RENDERCALLBACK(node->item.atomic); } void CVisibilityPlugins::RenderAlphaAtomics(void) { RenderAtomicList(m_alphaList); } void CVisibilityPlugins::RenderBoatAlphaAtomics(void) { SetCullMode(rwCULLMODECULLNONE); RenderAtomicList(m_alphaBoatAtomicList); SetCullMode(rwCULLMODECULLBACK); } void CVisibilityPlugins::RenderFadingEntities(CLinkList &list) { CLink *node; CSimpleModelInfo *mi; for(node = list.tail.prev; node != &list.head; node = node->prev){ CEntity *e = node->item.entity; if(e->m_rwObject == nil) continue; #ifdef EXTENDED_PIPELINES if(CustomPipes::bRenderingEnvMap && (e->IsPed() || e->IsVehicle())) continue; #endif mi = (CSimpleModelInfo *)CModelInfo::GetModelInfo(e->GetModelIndex()); if(mi->GetModelType() == MITYPE_SIMPLE && mi->m_noZwrite) RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE); if(e->bDistanceFade){ DeActivateDirectional(); SetAmbientColours(); e->bImBeingRendered = true; PUSH_RENDERGROUP(mi->GetModelName()); RenderFadingAtomic((RpAtomic*)e->m_rwObject, node->item.sort); POP_RENDERGROUP(); e->bImBeingRendered = false; }else CRenderer::RenderOneNonRoad(e); if(mi->GetModelType() == MITYPE_SIMPLE && mi->m_noZwrite) RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); } } void CVisibilityPlugins::RenderFadingEntities(void) { RenderFadingEntities(m_alphaEntityList); RenderBoatAlphaAtomics(); } void CVisibilityPlugins::RenderFadingUnderwaterEntities(void) { RenderFadingEntities(m_alphaUnderwaterEntityList); } RpAtomic* CVisibilityPlugins::RenderWheelAtomicCB(RpAtomic *atomic) { RpAtomic *lodatm; float len; CSimpleModelInfo *mi; mi = GetAtomicModelInfo(atomic); len = Sqrt(DistToCameraSq); lodatm = mi->GetAtomicFromDistance(len * TheCamera.LODDistMultiplier / VEHICLE_LODDIST_MULTIPLIER); if(lodatm){ if(RpAtomicGetGeometry(lodatm) != RpAtomicGetGeometry(atomic)) RpAtomicSetGeometry(atomic, RpAtomicGetGeometry(lodatm), rpATOMICSAMEBOUNDINGSPHERE); RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderObjNormalAtomic(RpAtomic *atomic) { RwMatrix *m; RwV3d view; float len; m = RwFrameGetLTM(RpAtomicGetFrame(atomic)); RwV3dSub(&view, RwMatrixGetPos(m), ms_pCameraPosn); len = RwV3dLength(&view); if(RwV3dDotProduct(&view, RwMatrixGetUp(m)) < -0.3f*len && len > 8.0f) return atomic; RENDERCALLBACK(atomic); return atomic; } RpAtomic* CVisibilityPlugins::RenderAlphaAtomic(RpAtomic *atomic, int alpha) { RpGeometry *geo; uint32 flags; geo = RpAtomicGetGeometry(atomic); flags = RpGeometryGetFlags(geo); RpGeometrySetFlags(geo, flags | rpGEOMETRYMODULATEMATERIALCOLOR); RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)alpha); RENDERCALLBACK(atomic); RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255); RpGeometrySetFlags(geo, flags); return atomic; } RpAtomic* CVisibilityPlugins::RenderWeaponCB(RpAtomic *atomic) { RwMatrix *m; RwV3d view; float maxdist, distsq; CSimpleModelInfo *mi; mi = GetAtomicModelInfo(atomic); m = RwFrameGetLTM(RpAtomicGetFrame(atomic)); RwV3dSub(&view, RwMatrixGetPos(m), ms_pCameraPosn); maxdist = mi->GetLodDistance(0); distsq = RwV3dDotProduct(&view, &view); if(distsq < maxdist*maxdist) RENDERCALLBACK(atomic); return atomic; } RpAtomic* CVisibilityPlugins::RenderFadingAtomic(RpAtomic *atomic, float camdist) { RpAtomic *lodatm; float fadefactor; uint32 alpha; CSimpleModelInfo *mi; mi = GetAtomicModelInfo(atomic); lodatm = mi->GetAtomicFromDistance(camdist - FADE_DISTANCE); if(mi->m_additive) RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); fadefactor = (mi->GetLargestLodDistance() - (camdist - FADE_DISTANCE))/FADE_DISTANCE; if(fadefactor > 1.0f) fadefactor = 1.0f; alpha = mi->m_alpha * fadefactor; if(alpha == 255) RENDERCALLBACK(atomic); else{ RpGeometry *geo = RpAtomicGetGeometry(lodatm); uint32 flags = RpGeometryGetFlags(geo); RpGeometrySetFlags(geo, flags | rpGEOMETRYMODULATEMATERIALCOLOR); RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)alpha); if(geo != RpAtomicGetGeometry(atomic)) RpAtomicSetGeometry(atomic, geo, rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) RENDERCALLBACK(atomic); RpGeometryForAllMaterials(geo, SetAlphaCB, (void*)255); RpGeometrySetFlags(geo, flags); } if(mi->m_additive) RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleHiDetailCB(RpAtomic *atomic) { RwFrame *clumpframe; float dot; uint32 flags; clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); if(DistToCameraSq < ms_vehicleLod0Dist){ flags = GetAtomicId(atomic); if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), RwFrameGetLTM(clumpframe), flags); if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) return atomic; } RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleHiDetailAlphaCB(RpAtomic *atomic) { RwFrame *clumpframe; float dot; uint32 flags; clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); if(DistToCameraSq < ms_vehicleLod0Dist){ flags = GetAtomicId(atomic); dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), RwFrameGetLTM(clumpframe), flags); if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f) if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) return atomic; if(flags & ATOMIC_FLAG_DRAWLAST){ // sort before clump if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq - 0.0001f)) RENDERCALLBACK(atomic); }else{ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot)) RENDERCALLBACK(atomic); } } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleHiDetailCB_BigVehicle(RpAtomic *atomic) { RwFrame *clumpframe; float dot; uint32 flags; clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); if(DistToCameraSq < ms_bigVehicleLod0Dist){ flags = GetAtomicId(atomic); if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), RwFrameGetLTM(clumpframe), flags); if(dot > 0.0f) return atomic; } RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_BigVehicle(RpAtomic *atomic) { RwFrame *clumpframe; float dot; uint32 flags; clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); if(DistToCameraSq < ms_bigVehicleLod0Dist){ flags = GetAtomicId(atomic); dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), RwFrameGetLTM(clumpframe), flags); if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f) if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) return atomic; if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot)) RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleHiDetailCB_Boat(RpAtomic *atomic) { if(DistToCameraSq < ms_vehicleLod0Dist) RENDERCALLBACK(atomic); return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_Boat(RpAtomic *atomic) { if(DistToCameraSq < ms_vehicleLod0Dist){ if(GetAtomicId(atomic) & ATOMIC_FLAG_DRAWLAST){ if(!InsertAtomicIntoBoatSortedList(atomic, DistToCameraSq)) RENDERCALLBACK(atomic); }else RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleLoDetailCB_Boat(RpAtomic *atomic) { RpClump *clump; int32 alpha; clump = RpAtomicGetClump(atomic); if(DistToCameraSq >= ms_vehicleLod0Dist){ alpha = GetClumpAlpha(clump); if(alpha == 255) RENDERCALLBACK(atomic); else RenderAlphaAtomic(atomic, alpha); } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleLowDetailCB_BigVehicle(RpAtomic *atomic) { RwFrame *clumpframe; float dot; uint32 flags; clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); if(DistToCameraSq >= ms_bigVehicleLod0Dist && DistToCameraSq < ms_bigVehicleLod1Dist){ flags = GetAtomicId(atomic); if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), RwFrameGetLTM(clumpframe), flags); if(dot > 0.0f) return atomic; } RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleLowDetailAlphaCB_BigVehicle(RpAtomic *atomic) { RwFrame *clumpframe; float dot; uint32 flags; clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); if(DistToCameraSq >= ms_bigVehicleLod0Dist && DistToCameraSq < ms_bigVehicleLod1Dist){ flags = GetAtomicId(atomic); dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), RwFrameGetLTM(clumpframe), flags); if(dot > 0.0f) if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f) return atomic; if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot)) RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleReallyLowDetailCB(RpAtomic *atomic) { RpClump *clump; int32 alpha; clump = RpAtomicGetClump(atomic); if(DistToCameraSq >= ms_vehicleLod0Dist){ alpha = GetClumpAlpha(clump); if(alpha == 255) RENDERCALLBACK(atomic); else RenderAlphaAtomic(atomic, alpha); } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle(RpAtomic *atomic) { if(DistToCameraSq >= ms_bigVehicleLod1Dist) RENDERCALLBACK(atomic); return atomic; } RpAtomic* CVisibilityPlugins::RenderTrainHiDetailCB(RpAtomic *atomic) { RwFrame *clumpframe; float dot; uint32 flags; clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); if(DistToCameraSq < ms_bigVehicleLod1Dist){ flags = GetAtomicId(atomic); if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f){ dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), RwFrameGetLTM(clumpframe), flags); if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) return atomic; } RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderTrainHiDetailAlphaCB(RpAtomic *atomic) { RwFrame *clumpframe; float dot; uint32 flags; clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); if(DistToCameraSq < ms_bigVehicleLod1Dist){ flags = GetAtomicId(atomic); dot = GetDotProductWithCameraVector(RwFrameGetLTM(RpAtomicGetFrame(atomic)), RwFrameGetLTM(clumpframe), flags); if(DistToCameraSq > ms_cullCompsDist && (flags & ATOMIC_FLAG_NOCULL) == 0 && PitchToCamera < 0.2f) if(dot > 0.0f && ((flags & ATOMIC_FLAG_ANGLECULL) || 0.1f*DistToCameraSq < dot*dot)) return atomic; if(flags & ATOMIC_FLAG_DRAWLAST){ // sort before clump if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq - 0.0001f)) RENDERCALLBACK(atomic); }else{ if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot)) RENDERCALLBACK(atomic); } } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleRotorAlphaCB(RpAtomic *atomic) { RwFrame *clumpframe; float dot; RwV3d cam2atm; clumpframe = RpClumpGetFrame(RpAtomicGetClump(atomic)); if(DistToCameraSq < ms_bigVehicleLod1Dist){ RwV3dSub(&cam2atm, &RwFrameGetLTM(RpAtomicGetFrame(atomic))->pos, ms_pCameraPosn); dot = RwV3dDotProduct(&cam2atm, &RwFrameGetLTM(clumpframe)->at); if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq + dot*20.0f)) RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderVehicleTailRotorAlphaCB(RpAtomic *atomic) { RwMatrix *clumpMat, *atmMat; float dot; RwV3d cam2atm; if(DistToCameraSq < ms_bigVehicleLod0Dist){ atmMat = RwFrameGetLTM(RpAtomicGetFrame(atomic)); clumpMat = RwFrameGetLTM(RpClumpGetFrame(RpAtomicGetClump(atomic))); RwV3dSub(&cam2atm, &atmMat->pos, ms_pCameraPosn); dot = RwV3dDotProduct(&cam2atm, &clumpMat->up) + RwV3dDotProduct(&cam2atm, &clumpMat->right); if(!InsertAtomicIntoSortedList(atomic, DistToCameraSq - dot)) RENDERCALLBACK(atomic); } return atomic; } RpAtomic* CVisibilityPlugins::RenderPlayerCB(RpAtomic *atomic) { if(CWorld::Players[0].m_pSkinTexture) RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), SetTextureCB, CWorld::Players[0].m_pSkinTexture); RENDERCALLBACK(atomic); return atomic; } RpAtomic* CVisibilityPlugins::RenderPedCB(RpAtomic *atomic) { int32 alpha; RwV3d cam2atm; RwV3dSub(&cam2atm, &RwFrameGetLTM(RpAtomicGetFrame(atomic))->pos, ms_pCameraPosn); if(RwV3dDotProduct(&cam2atm, &cam2atm) < ms_pedLod1Dist){ alpha = GetClumpAlpha(RpAtomicGetClump(atomic)); if(alpha == 255) RENDERCALLBACK(atomic); else RenderAlphaAtomic(atomic, alpha); } return atomic; } float CVisibilityPlugins::GetDistanceSquaredFromCamera(RwV3d *pos) { RwV3d dist; RwV3dSub(&dist, pos, ms_pCameraPosn); return RwV3dDotProduct(&dist, &dist); } float CVisibilityPlugins::GetDistanceSquaredFromCamera(RwFrame *frame) { RwMatrix *m; RwV3d dist; m = RwFrameGetLTM(frame); RwV3dSub(&dist, RwMatrixGetPos(m), ms_pCameraPosn); return RwV3dDotProduct(&dist, &dist); } float CVisibilityPlugins::GetDotProductWithCameraVector(RwMatrix *atomicMat, RwMatrix *clumpMat, uint32 flags) { RwV3d dist; float dot, dotdoor; // Vehicle forward is the y axis (RwMatrix.up) // Vehicle right is the x axis (RwMatrix.right) RwV3dSub(&dist, RwMatrixGetPos(atomicMat), ms_pCameraPosn); // forward/backward facing if(flags & (ATOMIC_FLAG_FRONT | ATOMIC_FLAG_REAR)) dot = RwV3dDotProduct(&dist, RwMatrixGetUp(clumpMat)); // left/right facing else if(flags & (ATOMIC_FLAG_LEFT | ATOMIC_FLAG_RIGHT)) dot = RwV3dDotProduct(&dist, RwMatrixGetRight(clumpMat)); else dot = 0.0f; if(flags & (ATOMIC_FLAG_LEFT | ATOMIC_FLAG_REAR)) dot = -dot; if(flags & (ATOMIC_FLAG_REARDOOR | ATOMIC_FLAG_FRONTDOOR)){ if(flags & ATOMIC_FLAG_REARDOOR) dotdoor = -RwV3dDotProduct(&dist, RwMatrixGetUp(clumpMat)); else if(flags & ATOMIC_FLAG_FRONTDOOR) dotdoor = RwV3dDotProduct(&dist, RwMatrixGetUp(clumpMat)); else dotdoor = 0.0f; if(dot < 0.0f && dotdoor < 0.0f) dot += dotdoor; if(dot > 0.0f && dotdoor > 0.0f) dot += dotdoor; } return dot; } /* These are all unused */ bool CVisibilityPlugins::DefaultVisibilityCB(RpClump *clump) { return true; } bool CVisibilityPlugins::FrustumSphereCB(RpClump *clump) { RwSphere sphere; RwFrame *frame = RpClumpGetFrame(clump); CClumpModelInfo *modelInfo = (CClumpModelInfo*)GetFrameHierarchyId(frame); sphere.radius = modelInfo->GetColModel()->boundingSphere.radius; sphere.center.x = modelInfo->GetColModel()->boundingSphere.center.x; sphere.center.y = modelInfo->GetColModel()->boundingSphere.center.y; sphere.center.z = modelInfo->GetColModel()->boundingSphere.center.z; RwV3dTransformPoints(&sphere.center, &sphere.center, 1, RwFrameGetLTM(frame)); return RwCameraFrustumTestSphere(ms_pCamera, &sphere) != rwSPHEREOUTSIDE; } bool CVisibilityPlugins::VehicleVisibilityCB(RpClump *clump) { if (GetDistanceSquaredFromCamera(RpClumpGetFrame(clump)) <= ms_vehicleLod1Dist) return FrustumSphereCB(clump); return false; } bool CVisibilityPlugins::VehicleVisibilityCB_BigVehicle(RpClump *clump) { return FrustumSphereCB(clump); } // // RW Plugins // enum { ID_VISIBILITYATOMIC = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x00), ID_VISIBILITYCLUMP = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x01), ID_VISIBILITYFRAME = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0x02), }; bool CVisibilityPlugins::PluginAttach(void) { ms_atomicPluginOffset = RpAtomicRegisterPlugin(sizeof(AtomicExt), ID_VISIBILITYATOMIC, AtomicConstructor, AtomicDestructor, AtomicCopyConstructor); ms_framePluginOffset = RwFrameRegisterPlugin(sizeof(FrameExt), ID_VISIBILITYFRAME, FrameConstructor, FrameDestructor, FrameCopyConstructor); ms_clumpPluginOffset = RpClumpRegisterPlugin(sizeof(ClumpExt), ID_VISIBILITYCLUMP, ClumpConstructor, ClumpDestructor, ClumpCopyConstructor); return ms_atomicPluginOffset != -1 && ms_clumpPluginOffset != -1; } #define ATOMICEXT(o) (RWPLUGINOFFSET(AtomicExt, o, ms_atomicPluginOffset)) #define FRAMEEXT(o) (RWPLUGINOFFSET(FrameExt, o, ms_framePluginOffset)) #define CLUMPEXT(o) (RWPLUGINOFFSET(ClumpExt, o, ms_clumpPluginOffset)) // // Atomic // void* CVisibilityPlugins::AtomicConstructor(void *object, int32, int32) { ATOMICEXT(object)->modelInfo = nil; return object; } void* CVisibilityPlugins::AtomicDestructor(void *object, int32, int32) { return object; } void* CVisibilityPlugins::AtomicCopyConstructor(void *dst, const void *src, int32, int32) { *ATOMICEXT(dst) = *ATOMICEXT(src); return dst; } void CVisibilityPlugins::SetAtomicModelInfo(RpAtomic *atomic, CSimpleModelInfo *modelInfo) { AtomicExt *ext = ATOMICEXT(atomic); ext->modelInfo = modelInfo; } CSimpleModelInfo* CVisibilityPlugins::GetAtomicModelInfo(RpAtomic *atomic) { return ATOMICEXT(atomic)->modelInfo; } void CVisibilityPlugins::SetAtomicFlag(RpAtomic *atomic, int f) { ATOMICEXT(atomic)->flags |= f; } void CVisibilityPlugins::ClearAtomicFlag(RpAtomic *atomic, int f) { ATOMICEXT(atomic)->flags &= ~f; } int CVisibilityPlugins::GetAtomicId(RpAtomic *atomic) { return ATOMICEXT(atomic)->flags; } void CVisibilityPlugins::SetAtomicRenderCallback(RpAtomic *atomic, RpAtomicCallBackRender cb) { if(cb == nil) cb = RENDERCALLBACK; // not necessary RpAtomicSetRenderCallBack(atomic, cb); } // // Frame // void* CVisibilityPlugins::FrameConstructor(void *object, int32, int32) { FRAMEEXT(object)->id = 0; return object; } void* CVisibilityPlugins::FrameDestructor(void *object, int32, int32) { return object; } void* CVisibilityPlugins::FrameCopyConstructor(void *dst, const void *src, int32, int32) { *FRAMEEXT(dst) = *FRAMEEXT(src); return dst; } void CVisibilityPlugins::SetFrameHierarchyId(RwFrame *frame, intptr id) { FRAMEEXT(frame)->id = id; } intptr CVisibilityPlugins::GetFrameHierarchyId(RwFrame *frame) { return FRAMEEXT(frame)->id; } // // Clump // void* CVisibilityPlugins::ClumpConstructor(void *object, int32, int32) { ClumpExt *ext = CLUMPEXT(object); ext->visibilityCB = DefaultVisibilityCB; ext->alpha = 0xFF; return object; } void* CVisibilityPlugins::ClumpDestructor(void *object, int32, int32) { return object; } void* CVisibilityPlugins::ClumpCopyConstructor(void *dst, const void *src, int32, int32) { CLUMPEXT(dst)->visibilityCB = CLUMPEXT(src)->visibilityCB; return dst; } void CVisibilityPlugins::SetClumpModelInfo(RpClump *clump, CClumpModelInfo *modelInfo) { CVehicleModelInfo *vmi; SetFrameHierarchyId(RpClumpGetFrame(clump), (intptr)modelInfo); // Unused switch (modelInfo->GetModelType()) { // ignore MLO case MITYPE_VEHICLE: vmi = (CVehicleModelInfo*)modelInfo; if(vmi->m_vehicleType == VEHICLE_TYPE_TRAIN || vmi->m_vehicleType == VEHICLE_TYPE_HELI || vmi->m_vehicleType == VEHICLE_TYPE_PLANE) CLUMPEXT(clump)->visibilityCB = VehicleVisibilityCB_BigVehicle; else CLUMPEXT(clump)->visibilityCB = VehicleVisibilityCB; break; default: break; } } void CVisibilityPlugins::SetClumpAlpha(RpClump *clump, int alpha) { CLUMPEXT(clump)->alpha = alpha; } int CVisibilityPlugins::GetClumpAlpha(RpClump *clump) { return CLUMPEXT(clump)->alpha; } ================================================ FILE: src/rw/VisibilityPlugins.h ================================================ #pragma once #include "templates.h" class CEntity; class CSimpleModelInfo; class CClumpModelInfo; typedef bool (*ClumpVisibilityCB)(RpClump*); class CVisibilityPlugins { public: struct AlphaObjectInfo { union { CEntity *entity; RpAtomic *atomic; }; float sort; }; static CLinkList m_alphaList; static CLinkList m_alphaBoatAtomicList; static CLinkList m_alphaEntityList; static CLinkList m_alphaUnderwaterEntityList; #ifdef NEW_RENDERER static CLinkList m_alphaBuildingList; #endif static RwCamera *ms_pCamera; static RwV3d *ms_pCameraPosn; static float ms_cullCompsDist; static float ms_vehicleLod0Dist; static float ms_vehicleLod1Dist; static float ms_vehicleFadeDist; static float ms_bigVehicleLod0Dist; static float ms_bigVehicleLod1Dist; static float ms_pedLod1Dist; static float ms_pedFadeDist; static void Initialise(void); static void Shutdown(void); static void InitAlphaEntityList(void); static bool InsertEntityIntoSortedList(CEntity *e, float dist); static void InitAlphaAtomicList(void); static bool InsertAtomicIntoSortedList(RpAtomic *a, float dist); static bool InsertAtomicIntoBoatSortedList(RpAtomic *a, float dist); static void SetRenderWareCamera(RwCamera *camera); static void SetupVehicleVariables(RpClump *vehicle); static RpAtomic *RenderWheelAtomicCB(RpAtomic *atomic); static RpAtomic *RenderObjNormalAtomic(RpAtomic *atomic); static RpAtomic *RenderAlphaAtomic(RpAtomic *atomic, int alpha); static RpAtomic *RenderWeaponCB(RpAtomic *atomic); static RpAtomic *RenderFadingAtomic(RpAtomic *atm, float dist); static RpAtomic *RenderVehicleHiDetailCB(RpAtomic *atomic); static RpAtomic *RenderVehicleHiDetailAlphaCB(RpAtomic *atomic); static RpAtomic *RenderVehicleHiDetailCB_BigVehicle(RpAtomic *atomic); static RpAtomic *RenderVehicleHiDetailAlphaCB_BigVehicle(RpAtomic *atomic); static RpAtomic *RenderVehicleHiDetailCB_Boat(RpAtomic *atomic); static RpAtomic *RenderVehicleHiDetailAlphaCB_Boat(RpAtomic *atomic); static RpAtomic *RenderVehicleLoDetailCB_Boat(RpAtomic *atomic); static RpAtomic *RenderVehicleLowDetailCB_BigVehicle(RpAtomic *atomic); static RpAtomic *RenderVehicleLowDetailAlphaCB_BigVehicle(RpAtomic *atomic); static RpAtomic *RenderVehicleReallyLowDetailCB(RpAtomic *atomic); static RpAtomic *RenderVehicleReallyLowDetailCB_BigVehicle(RpAtomic *atomic); static RpAtomic *RenderTrainHiDetailCB(RpAtomic *atomic); static RpAtomic *RenderTrainHiDetailAlphaCB(RpAtomic *atomic); static RpAtomic *RenderVehicleRotorAlphaCB(RpAtomic *atomic); static RpAtomic *RenderVehicleTailRotorAlphaCB(RpAtomic *atomic); static RpAtomic *RenderPlayerCB(RpAtomic *atomic); static RpAtomic *RenderPedCB(RpAtomic *atomic); // for skinned models with only one clump static void RenderAtomicList(CLinkList &list); static void RenderAlphaAtomics(void); static void RenderBoatAlphaAtomics(void); static void RenderFadingEntities(CLinkList &list); static void RenderFadingEntities(void); static void RenderFadingUnderwaterEntities(void); // All actually unused static bool DefaultVisibilityCB(RpClump *clump); static bool FrustumSphereCB(RpClump *clump); static bool VehicleVisibilityCB(RpClump *clump); static bool VehicleVisibilityCB_BigVehicle(RpClump *clump); static float GetDistanceSquaredFromCamera(RwV3d *pos); static float GetDistanceSquaredFromCamera(RwFrame *frame); static float GetDotProductWithCameraVector(RwMatrix *atomicMat, RwMatrix *clumpMat, uint32 flags); // // RW Plugins // union AtomicExt { CSimpleModelInfo *modelInfo; // used by SimpleModelInfo int flags; // used by ClumpModelInfo }; static void SetAtomicModelInfo(RpAtomic*, CSimpleModelInfo*); static CSimpleModelInfo *GetAtomicModelInfo(RpAtomic *atomic); static void SetAtomicFlag(RpAtomic*, int); static void ClearAtomicFlag(RpAtomic*, int); static int GetAtomicId(RpAtomic *atomic); static void SetAtomicRenderCallback(RpAtomic*, RpAtomicCallBackRender); static void *AtomicConstructor(void *object, int32 offset, int32 len); static void *AtomicDestructor(void *object, int32 offset, int32 len); static void *AtomicCopyConstructor(void *dst, const void *src, int32 offset, int32 len); static int32 ms_atomicPluginOffset; struct FrameExt { // BUG: this is abused to hold a pointer by SetClumpModelInfo intptr id; }; static void SetFrameHierarchyId(RwFrame *frame, intptr id); static intptr GetFrameHierarchyId(RwFrame *frame); static void *FrameConstructor(void *object, int32 offset, int32 len); static void *FrameDestructor(void *object, int32 offset, int32 len); static void *FrameCopyConstructor(void *dst, const void *src, int32 offset, int32 len); static int32 ms_framePluginOffset; // Not actually used struct ClumpExt { ClumpVisibilityCB visibilityCB; int alpha; }; static void SetClumpModelInfo(RpClump*, CClumpModelInfo*); static void SetClumpAlpha(RpClump*, int); static int GetClumpAlpha(RpClump*); static void *ClumpConstructor(void *object, int32 offset, int32 len); static void *ClumpDestructor(void *object, int32 offset, int32 len); static void *ClumpCopyConstructor(void *dst, const void *src, int32 offset, int32 len); static int32 ms_clumpPluginOffset; static bool PluginAttach(void); }; RpMaterial *SetAlphaCB(RpMaterial *material, void *data); ================================================ FILE: src/save/Date.cpp ================================================ #include "common.h" #include "Date.h" CDate::CDate() { m_nYear = 0; m_nSecond = 0; m_nMinute = 0; m_nHour = 0; m_nDay = 0; m_nMonth = 0; } bool CDate::operator>(const CDate &right) { if (m_nYear > right.m_nYear) return true; if (m_nYear != right.m_nYear) return false; if (m_nMonth > right.m_nMonth) return true; if (m_nMonth != right.m_nMonth) return false; if (m_nDay > right.m_nDay) return true; if (m_nDay != right.m_nDay) return false; if (m_nHour > right.m_nHour) return true; if (m_nHour != right.m_nHour) return false; if (m_nMinute > right.m_nMinute) return true; if (m_nMinute != right.m_nMinute) return false; return m_nSecond > right.m_nSecond; } bool CDate::operator<(const CDate &right) { if (m_nYear < right.m_nYear) return true; if (m_nYear != right.m_nYear) return false; if (m_nMonth < right.m_nMonth) return true; if (m_nMonth != right.m_nMonth) return false; if (m_nDay < right.m_nDay) return true; if (m_nDay != right.m_nDay) return false; if (m_nHour < right.m_nHour) return true; if (m_nHour != right.m_nHour) return false; if (m_nMinute < right.m_nMinute) return true; if (m_nMinute != right.m_nMinute) return false; return m_nSecond < right.m_nSecond; } bool CDate::operator==(const CDate &right) { if (m_nYear != right.m_nYear || m_nMonth != right.m_nMonth || m_nDay != right.m_nDay || m_nHour != right.m_nHour || m_nMinute != right.m_nMinute) return false; return m_nSecond == right.m_nSecond; } void CDate::PopulateDateFields(int8 &second, int8 &minute, int8 &hour, int8 &day, int8 &month, int16 year) { m_nSecond = second; m_nMinute = minute; m_nHour = hour; m_nDay = day; m_nMonth = month; m_nYear = year; } ================================================ FILE: src/save/Date.h ================================================ #pragma once class CDate { public: int m_nSecond; int m_nMinute; int m_nHour; int m_nDay; int m_nMonth; int m_nYear; CDate(); bool operator>(const CDate &right); bool operator<(const CDate &right); bool operator==(const CDate &right); void PopulateDateFields(int8 &second, int8 &minute, int8 &hour, int8 &day, int8 &month, int16 year); }; ================================================ FILE: src/save/GenericGameStorage.cpp ================================================ #define WITHWINDOWS #include "common.h" #include "crossplatform.h" #include "main.h" #include "DMAudio.h" #include "AudioScriptObject.h" #include "Camera.h" #include "CarGen.h" #include "Cranes.h" #include "Clock.h" #include "Date.h" #include "FileMgr.h" #include "Font.h" #include "Frontend.h" #include "GameLogic.h" #include "Gangs.h" #include "Garages.h" #include "GenericGameStorage.h" #include "Pad.h" #include "Particle.h" #include "ParticleObject.h" #include "PathFind.h" #include "PCSave.h" #include "Phones.h" #include "Pickups.h" #include "PlayerPed.h" #include "ProjectileInfo.h" #include "Pools.h" #include "Radar.h" #include "Restart.h" #include "Script.h" #include "SetPieces.h" #include "Stats.h" #include "Streaming.h" #include "Timer.h" #include "TimeStep.h" #include "Weather.h" #include "World.h" #include "Zones.h" #include "Timecycle.h" #include "Fluff.h" #define BLOCK_COUNT 22 #define SIZE_OF_SIMPLEVARS 0xE4 const uint32 SIZE_OF_ONE_GAME_IN_BYTES = 201729; #ifdef MISSION_REPLAY int8 IsQuickSave; const int PAUSE_SAVE_SLOT = SLOT_COUNT; #endif char DefaultPCSaveFileName[260]; char ValidSaveName[260]; char LoadFileName[256]; wchar SlotFileName[SLOT_COUNT][260]; wchar SlotSaveDate[SLOT_COUNT][70]; int CheckSum; eLevelName m_LevelToLoad; char SaveFileNameJustSaved[260]; int Slots[SLOT_COUNT]; bool b_FoundRecentSavedGameWantToLoad; bool JustLoadedDontFadeInYet; bool StillToFadeOut; uint32 TimeStartedCountingForFade; uint32 TimeToStayFadedBeforeFadeOut = 1750; int32 RadioStationPosition[NUM_RADIOS]; void InitRadioStationPositionList() { for (int i = 0; i < NUM_RADIOS; i++) RadioStationPosition[i] = -1; } int32 GetSavedRadioStationPosition(int32 station) { return RadioStationPosition[station]; } void PopulateRadioStationPositionList() { for (int i = 0; i < NUM_RADIOS; i++) RadioStationPosition[i] = DMAudio.GetRadioPosition(i); } #define ReadDataFromBufferPointer(buf, to) memcpy(&to, buf, sizeof(to)); buf += align4bytes(sizeof(to)); #define WriteDataToBufferPointer(buf, from) memcpy(buf, &from, sizeof(from)); buf += align4bytes(sizeof(from)); #define LoadSaveDataBlock()\ do {\ if (!ReadDataFromFile(file, (uint8 *) &size, 4))\ return false;\ size = align4bytes(size);\ if (!ReadDataFromFile(file, work_buff, size))\ return false;\ buf = work_buff;\ } while (0) #define ReadDataFromBlock(msg,load_func)\ do {\ debug(msg);\ ReadDataFromBufferPointer(buf, size);\ load_func(buf, size);\ size = align4bytes(size);\ buf += size;\ } while (0) #define WriteSaveDataBlock(save_func, msg)\ do {\ size = 0;\ buf = work_buff;\ reserved = 0;\ MakeSpaceForSizeInBufferPointer(presize, buf, postsize);\ save_func(buf, &size);\ debug(msg"== %i \n", size);\ CopySizeAndPreparePointer(presize, buf, postsize, reserved, size);\ if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, buf - work_buff))\ return false;\ totalSize += buf - work_buff;\ } while (0) bool GenericSave(int file) { uint8 *buf, *presize, *postsize; uint32 size; uint32 reserved; uint32 totalSize; wchar *lastMissionPassed; wchar suffix[6]; wchar saveName[24]; SYSTEMTIME saveTime; CPad *currPad; CheckSum = 0; buf = work_buff; reserved = 0; // Save simple vars lastMissionPassed = TheText.Get(CStats::LastMissionPassedName[0] ? CStats::LastMissionPassedName : "ITBEG"); AsciiToUnicode("...'", suffix); suffix[3] = L'\0'; #ifdef FIX_BUGS // fix buffer overflow int len = UnicodeStrlen(lastMissionPassed); if (len > ARRAY_SIZE(saveName)-1) len = ARRAY_SIZE(saveName)-1; memcpy(saveName, lastMissionPassed, sizeof(wchar) * len); #else TextCopy(saveName, lastMissionPassed); int len = UnicodeStrlen(saveName); #endif saveName[len] = '\0'; if (len > ARRAY_SIZE(saveName)-2) TextCopy(&saveName[ARRAY_SIZE(saveName)-ARRAY_SIZE(suffix)], suffix); saveName[ARRAY_SIZE(saveName)-1] = '\0'; WriteDataToBufferPointer(buf, saveName); GetLocalTime(&saveTime); WriteDataToBufferPointer(buf, saveTime); #ifdef MISSION_REPLAY int32 data = IsQuickSave << 24 | SIZE_OF_ONE_GAME_IN_BYTES; WriteDataToBufferPointer(buf, data); #else WriteDataToBufferPointer(buf, SIZE_OF_ONE_GAME_IN_BYTES); #endif WriteDataToBufferPointer(buf, CGame::currLevel); WriteDataToBufferPointer(buf, TheCamera.GetPosition().x); WriteDataToBufferPointer(buf, TheCamera.GetPosition().y); WriteDataToBufferPointer(buf, TheCamera.GetPosition().z); WriteDataToBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute); WriteDataToBufferPointer(buf, CClock::ms_nLastClockTick); WriteDataToBufferPointer(buf, CClock::ms_nGameClockHours); WriteDataToBufferPointer(buf, CClock::ms_nGameClockMinutes); currPad = CPad::GetPad(0); WriteDataToBufferPointer(buf, currPad->Mode); WriteDataToBufferPointer(buf, CTimer::m_snTimeInMilliseconds); WriteDataToBufferPointer(buf, CTimer::ms_fTimeScale); WriteDataToBufferPointer(buf, CTimer::ms_fTimeStep); WriteDataToBufferPointer(buf, CTimer::ms_fTimeStepNonClipped); WriteDataToBufferPointer(buf, CTimer::m_FrameCounter); WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeStep); WriteDataToBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate); WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeScale); WriteDataToBufferPointer(buf, CWeather::OldWeatherType); WriteDataToBufferPointer(buf, CWeather::NewWeatherType); WriteDataToBufferPointer(buf, CWeather::ForcedWeatherType); WriteDataToBufferPointer(buf, CWeather::InterpolationValue); WriteDataToBufferPointer(buf, CWeather::WeatherTypeInList); #ifdef COMPATIBLE_SAVES // converted to float for compatibility with original format // TODO: maybe remove this? not really gonna break anything vital float f = TheCamera.CarZoomIndicator; WriteDataToBufferPointer(buf, f); f = TheCamera.PedZoomIndicator; WriteDataToBufferPointer(buf, f); #else WriteDataToBufferPointer(buf, TheCamera.CarZoomIndicator); WriteDataToBufferPointer(buf, TheCamera.PedZoomIndicator); #endif WriteDataToBufferPointer(buf, CGame::currArea); WriteDataToBufferPointer(buf, CVehicle::bAllTaxisHaveNitro); WriteDataToBufferPointer(buf, CPad::bInvertLook4Pad); WriteDataToBufferPointer(buf, CTimeCycle::m_ExtraColour); WriteDataToBufferPointer(buf, CTimeCycle::m_bExtraColourOn); WriteDataToBufferPointer(buf, CTimeCycle::m_ExtraColourInter); PopulateRadioStationPositionList(); WriteDataToBufferPointer(buf, RadioStationPosition); assert(buf - work_buff == SIZE_OF_SIMPLEVARS); // Save scripts, block is nested within the same block as simple vars for some reason presize = buf; buf += 4; postsize = buf; CTheScripts::SaveAllScripts(buf, &size); debug("ScriptSize== %i \n", size); CopySizeAndPreparePointer(presize, buf, postsize, reserved, size); if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, buf - work_buff)) return false; totalSize = buf - work_buff; // Save the rest WriteSaveDataBlock(CPools::SavePedPool, "PedPoolSize"); WriteSaveDataBlock(CGarages::Save, "GaragesSize"); WriteSaveDataBlock(CGameLogic::Save, "GameLogicSize"); WriteSaveDataBlock(CPools::SaveVehiclePool, "VehPoolSize"); WriteSaveDataBlock(CPools::SaveObjectPool, "ObjectPoolSize"); WriteSaveDataBlock(ThePaths.Save, "ThePathsSize"); WriteSaveDataBlock(CCranes::Save, "CranesSize"); WriteSaveDataBlock(CPickups::Save, "PickUpsSize"); WriteSaveDataBlock(gPhoneInfo.Save, "PhoneInfoSize"); WriteSaveDataBlock(CRestart::SaveAllRestartPoints, "RestartPointsBufferSize"); WriteSaveDataBlock(CRadar::SaveAllRadarBlips, "RadarBlipsBufferSize"); WriteSaveDataBlock(CTheZones::SaveAllZones, "AllZonesBufferSize"); WriteSaveDataBlock(CGangs::SaveAllGangData, "AllGangDataSize"); WriteSaveDataBlock(CTheCarGenerators::SaveAllCarGenerators, "AllCarGeneratorsSize"); WriteSaveDataBlock(CParticleObject::SaveParticle, "ParticlesSize"); WriteSaveDataBlock(cAudioScriptObject::SaveAllAudioScriptObjects, "AllAudioScriptObjectsSize"); WriteSaveDataBlock(CScriptPaths::Save, "ScriptPathsSize"); WriteSaveDataBlock(CWorld::Players[CWorld::PlayerInFocus].SavePlayerInfo, "PlayerInfoSize"); WriteSaveDataBlock(CStats::SaveStats, "StatsSize"); WriteSaveDataBlock(CSetPieces::Save, "SetPiecesSize"); WriteSaveDataBlock(CStreaming::MemoryCardSave, "StreamingSize"); WriteSaveDataBlock(CPedType::Save, "PedTypeSize"); // Write padding for (int i = 0; i < 4; i++) { size = align4bytes(SIZE_OF_ONE_GAME_IN_BYTES - totalSize - 4); if (size > sizeof(work_buff)) size = sizeof(work_buff); if (size > 4) { if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff, size)) return false; totalSize += size; } } // Write checksum and close CFileMgr::Write(file, (const char *) &CheckSum, sizeof(CheckSum)); if (CFileMgr::GetErrorReadWrite(file)) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; if (!CloseFile(file)) PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; return false; } CPad::FixPadsAfterSave(); return true; } bool GenericLoad() { uint8 *buf; int32 file; uint32 size; #ifdef MISSION_REPLAY int8 qs; #endif int32 saveSize; CPad *currPad; // Load SimpleVars and Scripts CheckSum = 0; CDate dummy; // unused CPad::ResetCheats(); if (!ReadInSizeofSaveFileBuffer(file, size)) return false; size = align4bytes(size); ReadDataFromFile(file, work_buff, size); buf = (work_buff + 0x40); ReadDataFromBufferPointer(buf, saveSize); #ifdef MISSION_REPLAY // a hack to keep compatibility but get new data from save qs = saveSize >> 24; #endif ReadDataFromBufferPointer(buf, CGame::currLevel); ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().x); ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().y); ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().z); ReadDataFromBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute); ReadDataFromBufferPointer(buf, CClock::ms_nLastClockTick); ReadDataFromBufferPointer(buf, CClock::ms_nGameClockHours); ReadDataFromBufferPointer(buf, CClock::ms_nGameClockMinutes); currPad = CPad::GetPad(0); ReadDataFromBufferPointer(buf, currPad->Mode); ReadDataFromBufferPointer(buf, CTimer::m_snTimeInMilliseconds); ReadDataFromBufferPointer(buf, CTimer::ms_fTimeScale); ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStep); ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStepNonClipped); ReadDataFromBufferPointer(buf, CTimer::m_FrameCounter); ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeStep); ReadDataFromBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate); ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeScale); ReadDataFromBufferPointer(buf, CWeather::OldWeatherType); ReadDataFromBufferPointer(buf, CWeather::NewWeatherType); ReadDataFromBufferPointer(buf, CWeather::ForcedWeatherType); #ifdef SECUROM if (CTimer::m_FrameCounter > 72000){ buf += align4bytes(4); } #endif ReadDataFromBufferPointer(buf, CWeather::InterpolationValue); ReadDataFromBufferPointer(buf, CWeather::WeatherTypeInList); #ifdef COMPATIBLE_SAVES // converted to float for compatibility with original format // TODO: maybe remove this? not really gonna break anything vital float f; ReadDataFromBufferPointer(buf, f); TheCamera.CarZoomIndicator = f; ReadDataFromBufferPointer(buf, f); TheCamera.PedZoomIndicator = f; #else ReadDataFromBufferPointer(buf, TheCamera.CarZoomIndicator); ReadDataFromBufferPointer(buf, TheCamera.PedZoomIndicator); #endif ReadDataFromBufferPointer(buf, CGame::currArea); ReadDataFromBufferPointer(buf, CVehicle::bAllTaxisHaveNitro); #ifdef LOAD_INI_SETTINGS buf += align4bytes(sizeof(CPad::bInvertLook4Pad)); #else ReadDataFromBufferPointer(buf, CPad::bInvertLook4Pad); #endif ReadDataFromBufferPointer(buf, CTimeCycle::m_ExtraColour); ReadDataFromBufferPointer(buf, CTimeCycle::m_bExtraColourOn); ReadDataFromBufferPointer(buf, CTimeCycle::m_ExtraColourInter); ReadDataFromBufferPointer(buf, RadioStationPosition); assert(buf - work_buff == SIZE_OF_SIMPLEVARS); #ifdef MISSION_REPLAY WaitForSave = 0; if (FrontEndMenuManager.m_nCurrSaveSlot == PAUSE_SAVE_SLOT && qs == 3) WaitForMissionActivate = CTimer::GetTimeInMilliseconds() + 2000; #endif ReadDataFromBlock("Loading Scripts \n", CTheScripts::LoadAllScripts); // Load the rest LoadSaveDataBlock(); ReadDataFromBlock("Loading PedPool \n", CPools::LoadPedPool); LoadSaveDataBlock(); ReadDataFromBlock("Loading Garages \n", CGarages::Load); LoadSaveDataBlock(); ReadDataFromBlock("Loading GameLogic \n", CGameLogic::Load); LoadSaveDataBlock(); ReadDataFromBlock("Loading Vehicles \n", CPools::LoadVehiclePool); LoadSaveDataBlock(); CProjectileInfo::RemoveAllProjectiles(); CObject::DeleteAllTempObjects(); ReadDataFromBlock("Loading Objects \n", CPools::LoadObjectPool); LoadSaveDataBlock(); ReadDataFromBlock("Loading Paths \n", ThePaths.Load); LoadSaveDataBlock(); ReadDataFromBlock("Loading Cranes \n", CCranes::Load); LoadSaveDataBlock(); ReadDataFromBlock("Loading Pickups \n", CPickups::Load); LoadSaveDataBlock(); ReadDataFromBlock("Loading Phoneinfo \n", gPhoneInfo.Load); LoadSaveDataBlock(); ReadDataFromBlock("Loading Restart \n", CRestart::LoadAllRestartPoints); LoadSaveDataBlock(); ReadDataFromBlock("Loading Radar Blips \n", CRadar::LoadAllRadarBlips); LoadSaveDataBlock(); ReadDataFromBlock("Loading Zones \n", CTheZones::LoadAllZones); LoadSaveDataBlock(); ReadDataFromBlock("Loading Gang Data \n", CGangs::LoadAllGangData); LoadSaveDataBlock(); ReadDataFromBlock("Loading Car Generators \n", CTheCarGenerators::LoadAllCarGenerators); CParticle::ReloadConfig(); LoadSaveDataBlock(); ReadDataFromBlock("Loading Particles \n", CParticleObject::LoadParticle); LoadSaveDataBlock(); ReadDataFromBlock("Loading AudioScript Objects \n", cAudioScriptObject::LoadAllAudioScriptObjects); LoadSaveDataBlock(); ReadDataFromBlock("Loading ScriptPaths \n", CScriptPaths::Load); LoadSaveDataBlock(); ReadDataFromBlock("Loading Player Info \n", CWorld::Players[CWorld::PlayerInFocus].LoadPlayerInfo); LoadSaveDataBlock(); ReadDataFromBlock("Loading Stats \n", CStats::LoadStats); LoadSaveDataBlock(); ReadDataFromBlock("Loading Set Pieces \n", CSetPieces::Load); LoadSaveDataBlock(); ReadDataFromBlock("Loading Streaming Stuff \n", CStreaming::MemoryCardLoad); LoadSaveDataBlock(); ReadDataFromBlock("Loading PedType Stuff \n", CPedType::Load); DMAudio.SetMusicMasterVolume(FrontEndMenuManager.m_PrefsMusicVolume); DMAudio.SetEffectsMasterVolume(FrontEndMenuManager.m_PrefsSfxVolume); if (!CloseFile(file)) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; return false; } DoGameSpecificStuffAfterSucessLoad(); debug("Game successfully loaded \n"); return true; } bool ReadInSizeofSaveFileBuffer(int32 &file, uint32 &size) { file = CFileMgr::OpenFile(LoadFileName, "rb"); if (file == 0) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_OPEN; return false; } CFileMgr::Read(file, (const char*)&size, sizeof(size)); if (CFileMgr::GetErrorReadWrite(file)) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; if (!CloseFile(file)) PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; return false; } return true; } bool ReadDataFromFile(int32 file, uint8 *buf, uint32 size) { if (file == 0) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_OPEN; return false; } size_t read_size = CFileMgr::Read(file, (const char*)buf, size); if (CFileMgr::GetErrorReadWrite(file) || read_size != size) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; if (!CloseFile(file)) PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; return false; } return true; } bool CloseFile(int32 file) { return CFileMgr::CloseFile(file) == 0; } void DoGameSpecificStuffAfterSucessLoad() { CCollision::SortOutCollisionAfterLoad(); CStreaming::LoadSceneCollision(TheCamera.GetPosition()); CStreaming::LoadScene(TheCamera.GetPosition()); CGame::TidyUpMemory(true, false); StillToFadeOut = true; JustLoadedDontFadeInYet = true; TheCamera.Fade(0.0f, 0); CTheScripts::Process(); } bool CheckSlotDataValid(int32 slot) { PcSaveHelper.nErrorCode = SAVESTATUS_SUCCESSFUL; if (CheckDataNotCorrupt(slot, LoadFileName)) { CStreaming::DeleteAllRwObjects(); return true; } PcSaveHelper.nErrorCode = SAVESTATUS_ERR_DATA_INVALID; return false; } void MakeSpaceForSizeInBufferPointer(uint8 *&presize, uint8 *&buf, uint8 *&postsize) { presize = buf; buf += sizeof(uint32); postsize = buf; } void CopySizeAndPreparePointer(uint8 *&buf, uint8 *&postbuf, uint8 *&postbuf2, uint32 &unused, uint32 &size) { memcpy(buf, &size, sizeof(size)); size = align4bytes(size); postbuf2 += size; postbuf = postbuf2; } void DoGameSpecificStuffBeforeSave() { CGameLogic::PassTime(360); CPlayerPed *ped = FindPlayerPed(); ped->m_fCurrentStamina = ped->m_fMaxStamina; CGame::TidyUpMemory(true, false); } void MakeValidSaveName(int32 slot) { ValidSaveName[0] = '\0'; sprintf(ValidSaveName, "%s%i", DefaultPCSaveFileName, slot + 1); strncat(ValidSaveName, ".b", 5); } wchar * GetSavedGameDateAndTime(int32 slot) { return SlotSaveDate[slot]; } wchar * GetNameOfSavedGame(int32 slot) { return SlotFileName[slot]; } bool CheckDataNotCorrupt(int32 slot, char *name) { char filename[100]; int32 blocknum = 0; eLevelName level = LEVEL_GENERIC; CheckSum = 0; uint32 bytes_processed = 0; sprintf(filename, "%s%i%s", DefaultPCSaveFileName, slot + 1, ".b"); int file = CFileMgr::OpenFile(filename, "rb"); if (file == 0) return false; strcpy(name, filename); while (SIZE_OF_ONE_GAME_IN_BYTES - sizeof(uint32) > bytes_processed && blocknum < 40) { int32 blocksize; if (!ReadDataFromFile(file, (uint8*)&blocksize, sizeof(blocksize))) { CloseFile(file); return false; } if (blocksize > align4bytes(sizeof(work_buff))) blocksize = sizeof(work_buff) - sizeof(uint32); if (!ReadDataFromFile(file, work_buff, align4bytes(blocksize))) { CloseFile(file); return false; } CheckSum += ((uint8*)&blocksize)[0]; CheckSum += ((uint8*)&blocksize)[1]; CheckSum += ((uint8*)&blocksize)[2]; CheckSum += ((uint8*)&blocksize)[3]; uint8 *_work_buf = work_buff; for (int i = 0; i < align4bytes(blocksize); i++) { CheckSum += *_work_buf++; bytes_processed++; } if (blocknum == 0) memcpy(&level, work_buff+4, sizeof(level)); blocknum++; } int32 _checkSum; if (ReadDataFromFile(file, (uint8*)&_checkSum, sizeof(_checkSum))) { if (CloseFile(file)) { if (CheckSum == _checkSum) { m_LevelToLoad = level; return true; } return false; } return false; } CloseFile(file); return false; } bool RestoreForStartLoad() { uint8 buf[999]; int file = CFileMgr::OpenFile(LoadFileName, "rb"); if (file == 0) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_OPEN; return false; } ReadDataFromFile(file, buf, sizeof(buf)); if (CFileMgr::GetErrorReadWrite(file)) { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_READ; if (!CloseFile(file)) PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; return false; } else { uint8 *_buf = buf + sizeof(int32) + sizeof(wchar[24]) + sizeof(SYSTEMTIME) + sizeof(SIZE_OF_ONE_GAME_IN_BYTES); ReadDataFromBufferPointer(_buf, CGame::currLevel); ReadDataFromBufferPointer(_buf, TheCamera.GetMatrix().GetPosition().x); ReadDataFromBufferPointer(_buf, TheCamera.GetMatrix().GetPosition().y); ReadDataFromBufferPointer(_buf, TheCamera.GetMatrix().GetPosition().z); CStreaming::RemoveUnusedBigBuildings(CGame::currLevel); CStreaming::RemoveUnusedBuildings(CGame::currLevel); if (CloseFile(file)) { return true; } else { PcSaveHelper.nErrorCode = SAVESTATUS_ERR_LOAD_CLOSE; return false; } } } int align4bytes(int32 size) { return (size + 3) & 0xFFFFFFFC; } #ifdef MISSION_REPLAY void DisplaySaveResult(int unk, char* name) {} bool SaveGameForPause(int type) { if (AllowMissionReplay != 0 || type != 3 && WaitForSave > CTimer::GetTimeInMilliseconds()) return false; WaitForSave = 0; if (gGameState != GS_PLAYING_GAME || CTheScripts::IsPlayerOnAMission() || CStats::LastMissionPassedName[0] == '\0') { DisplaySaveResult(3, CStats::LastMissionPassedName); return false; } IsQuickSave = type; MissionStartTime = 0; int res = PcSaveHelper.SaveSlot(PAUSE_SAVE_SLOT); PcSaveHelper.PopulateSlotInfo(); IsQuickSave = 0; DisplaySaveResult(res, CStats::LastMissionPassedName); return true; } #endif ================================================ FILE: src/save/GenericGameStorage.h ================================================ #pragma once #include "Game.h" #include "PCSave.h" #define SLOT_COUNT (8) void InitRadioStationPositionList(); int32 GetSavedRadioStationPosition(int32 station); void PopulateRadioStationPositionList(); bool GenericSave(int file); bool GenericLoad(); bool ReadInSizeofSaveFileBuffer(int32 &file, uint32 &size); bool ReadDataFromFile(int32 file, uint8 *buf, uint32 size); bool CloseFile(int32 file); void DoGameSpecificStuffAfterSucessLoad(); bool CheckSlotDataValid(int32 slot); void MakeSpaceForSizeInBufferPointer(uint8 *&presize, uint8 *&buf, uint8 *&postsize); void CopySizeAndPreparePointer(uint8 *&buf, uint8 *&postbuf, uint8 *&postbuf2, uint32 &unused, uint32 &size); void DoGameSpecificStuffBeforeSave(); void MakeValidSaveName(int32 slot); wchar *GetSavedGameDateAndTime(int32 slot); wchar *GetNameOfSavedGame(int32 slot); bool CheckDataNotCorrupt(int32 slot, char *name); bool RestoreForStartLoad(); int align4bytes(int32 size); extern char DefaultPCSaveFileName[260]; extern char ValidSaveName[260]; extern char LoadFileName[256]; extern wchar SlotFileName[SLOT_COUNT][260]; extern wchar SlotSaveDate[SLOT_COUNT][70]; extern int CheckSum; extern enum eLevelName m_LevelToLoad; extern int Slots[SLOT_COUNT]; extern bool b_FoundRecentSavedGameWantToLoad; extern bool JustLoadedDontFadeInYet; extern bool StillToFadeOut; extern uint32 TimeStartedCountingForFade; extern uint32 TimeToStayFadedBeforeFadeOut; extern char SaveFileNameJustSaved[260]; // 8F2570 const char TopLineEmptyFile[] = "THIS FILE IS NOT VALID YET"; #ifdef MISSION_REPLAY extern int8 IsQuickSave; // originally int bool SaveGameForPause(int); #endif ================================================ FILE: src/save/MemoryCard.cpp ================================================ #define WITHWINDOWS #include "common.h" #ifdef PS2_MENU #include "crossplatform.h" #include "MemoryCard.h" #include "main.h" #include "DMAudio.h" #include "AudioScriptObject.h" #include "Camera.h" #include "CarGen.h" #include "Cranes.h" #include "Clock.h" #include "MBlur.h" #include "Date.h" #include "Font.h" #include "FileMgr.h" #include "Game.h" #include "GameLogic.h" #include "Gangs.h" #include "Garages.h" #include "GenericGameStorage.h" #include "Pad.h" #include "Particle.h" #include "ParticleObject.h" #include "PathFind.h" #include "PCSave.h" #include "Phones.h" #include "Pickups.h" #include "PlayerPed.h" #include "ProjectileInfo.h" #include "Pools.h" #include "Radar.h" #include "Restart.h" #include "Script.h" #include "Stats.h" #include "Streaming.h" #include "Sprite2d.h" #include "Timer.h" #include "TimeStep.h" #include "Weather.h" #include "World.h" #include "Zones.h" #include "Frontend_PS2.h" CMemoryCard TheMemoryCard; char icon_one[16] = "slime1.ico"; char icon_two[16] = "slime2.ico"; char icon_three[16] = "slime3.ico"; char HostFileLocationOfIcons[64] = "icons\\"; char TheGameRootDirectory[64] = "/BESLES-50330GTA30000"; #define ReadDataFromBufferPointer(buf, to) memcpy(&to, buf, sizeof(to)); buf += align4bytes(sizeof(to)); #define WriteDataToBufferPointer(buf, from) memcpy(buf, &from, sizeof(from)); buf += align4bytes(sizeof(from)); static int align4bytes(int32 size) { return (size + 3) & 0xFFFFFFFC; } unsigned short ascii_table[3][2] = { { 0x824F, 0x30 }, /* 0-9 */ { 0x8260, 0x41 }, /* A-Z */ { 0x8281, 0x61 }, /* a-z */ }; unsigned short ascii_special[33][2] = { {0x8140, 0x20}, /* " " */ {0x8149, 0x21}, /* "!" */ {0x8168, 0x22}, /* """ */ {0x8194, 0x23}, /* "#" */ {0x8190, 0x24}, /* "$" */ {0x8193, 0x25}, /* "%" */ {0x8195, 0x26}, /* "&" */ {0x8166, 0x27}, /* "'" */ {0x8169, 0x28}, /* "(" */ {0x816A, 0x29}, /* ")" */ {0x8196, 0x2A}, /* "*" */ {0x817B, 0x2B}, /* "+" */ {0x8143, 0x2C}, /* "," */ {0x817C, 0x2D}, /* "-" */ {0x8144, 0x2E}, /* "." */ {0x815E, 0x2F}, /* "/" */ {0x8146, 0x3A}, /* ":" */ {0x8147, 0x3B}, /* ";" */ {0x8171, 0x3C}, /* "<" */ {0x8181, 0x3D}, /* "=" */ {0x8172, 0x3E}, /* ">" */ {0x8148, 0x3F}, /* "?" */ {0x8197, 0x40}, /* "@" */ {0x816D, 0x5B}, /* "[" */ {0x818F, 0x5C}, /* "\" */ {0x816E, 0x5D}, /* "]" */ {0x814F, 0x5E}, /* "^" */ {0x8151, 0x5F}, /* "_" */ {0x8165, 0x60}, /* "`" */ {0x816F, 0x7B}, /* "{" */ {0x8162, 0x7C}, /* "|" */ {0x8170, 0x7D}, /* "}" */ {0x8150, 0x7E}, /* "~" */ }; unsigned short Ascii2Sjis(unsigned char ascii_code) { unsigned short sjis_code = 0; unsigned char stmp; unsigned char stmp2 = 0; if ((ascii_code >= 0x20) && (ascii_code <= 0x2f)) stmp2 = 1; else if ((ascii_code >= 0x30) && (ascii_code <= 0x39)) stmp = 0; else if ((ascii_code >= 0x3a) && (ascii_code <= 0x40)) stmp2 = 11; else if ((ascii_code >= 0x41) && (ascii_code <= 0x5a)) stmp = 1; else if ((ascii_code >= 0x5b) && (ascii_code <= 0x60)) stmp2 = 37; else if ((ascii_code >= 0x61) && (ascii_code <= 0x7a)) stmp = 2; else if ((ascii_code >= 0x7b) && (ascii_code <= 0x7e)) stmp2 = 63; else { printf("bad ASCII code 0x%x\n", ascii_code); return(0); } if (stmp2) sjis_code = ascii_special[ascii_code - 0x20 - (stmp2 - 1)][0]; else sjis_code = ascii_table[stmp][0] + ascii_code - ascii_table[stmp][1]; return(sjis_code); } #if defined(GTA_PC) extern "C" { extern void HandleExit(); } char CardCurDir[MAX_CARDS][260] = { "", "" }; char PCCardsPath[260]; char PCCardDir[MAX_CARDS][12] = { "memcard1", "memcard2" }; const char* _psGetUserFilesFolder(); void _psCreateFolder(LPCSTR path); void PCMCInit() { sprintf(PCCardsPath, "%s", _psGetUserFilesFolder()); char path[512]; sprintf(path, "%s\\%s", PCCardsPath, PCCardDir[CARD_ONE]); _psCreateFolder(path); sprintf(path, "%s\\%s", PCCardsPath, PCCardDir[CARD_TWO]); _psCreateFolder(path); } #endif CMemoryCardInfo::CMemoryCardInfo(void) { type = 0; free = 0; format = 0; for ( int32 i = 0; i < sizeof(dir); i++ ) dir[i] = '\0'; strncpy(dir, TheGameRootDirectory, sizeof(dir) - 1); } int32 CMemoryCard::Init(void) { #if defined(PS2) if ( sceMcInit() == sceMcIniSucceed ) { printf("Memory card initialsed\n"); return RES_SUCCESS; } printf("Memory Card not being initialised\n"); return RES_FAILED; #else PCMCInit(); printf("Memory card initialsed\n"); return RES_SUCCESS; #endif } CMemoryCard::CMemoryCard(void) { _unk0 = 0; CurrentCard = CARD_ONE; Cards[CARD_ONE].port = 0; Cards[CARD_TWO].port = 1; for ( int32 i = 0; i < sizeof(_unkName3); i++ ) _unkName3[i] = '\0'; m_bWantToLoad = false; _bunk2 = false; _bunk7 = false; JustLoadedDontFadeInYet = false; StillToFadeOut = false; TimeStartedCountingForFade = 0; TimeToStayFadedBeforeFadeOut = 1750; b_FoundRecentSavedGameWantToLoad = false; char date[64]; char time[64]; char day[8]; char month[8]; char year[8]; char hour[8]; char minute[8]; char second[8]; strncpy(date, "Oct 7 2001", 62); strncpy(time, "15:48:32", 62); strncpy(month, date, 3); month[3] = '\0'; strncpy(day, &date[4], 2); day[2] = '\0'; strncpy(year, &date[7], 4); year[4] = '\0'; strncpy(hour, time, 2); hour[2] = '\0'; strncpy(minute, &time[3], 2); minute[2] = '\0'; strncpy(second, &time[6], 2); second[2] = '\0'; #define _CMP(m) strncmp(month, m, sizeof(m)-1) if ( !_CMP("Jan") ) CompileDateAndTime.m_nMonth = 1; else if ( !_CMP("Feb") ) CompileDateAndTime.m_nMonth = 2; else if ( !_CMP("Mar") ) CompileDateAndTime.m_nMonth = 3; else if ( !_CMP("Apr") ) CompileDateAndTime.m_nMonth = 4; else if ( !_CMP("May") ) CompileDateAndTime.m_nMonth = 5; else if ( !_CMP("Jun") ) CompileDateAndTime.m_nMonth = 6; else if ( !_CMP("Jul") ) CompileDateAndTime.m_nMonth = 7; else if ( !_CMP("Aug") ) CompileDateAndTime.m_nMonth = 8; else if ( !_CMP("Oct") ) CompileDateAndTime.m_nMonth = 9; // BUG: oct and sep is swapped here else if ( !_CMP("Sep") ) CompileDateAndTime.m_nMonth = 10; else if ( !_CMP("Nov") ) CompileDateAndTime.m_nMonth = 11; else if ( !_CMP("Dec") ) CompileDateAndTime.m_nMonth = 12; #undef _CMP CompileDateAndTime.m_nDay = atoi(day); CompileDateAndTime.m_nYear = atoi(year); CompileDateAndTime.m_nHour = atoi(hour); CompileDateAndTime.m_nMinute = atoi(minute); CompileDateAndTime.m_nSecond = atoi(second); } int32 CMemoryCard::RestoreForStartLoad(void) { uint8 buf[30]; int32 file = OpenMemCardFileForReading(CurrentCard, LoadFileName); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; ReadFromMemCard(file, buf, sizeof(buf) - 1); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; uint8 *pBuf = buf + sizeof(uint32) + sizeof(uint32); ReadDataFromBufferPointer(pBuf, CGame::currLevel); ReadDataFromBufferPointer(pBuf, TheCamera.GetMatrix().GetPosition().x); ReadDataFromBufferPointer(pBuf, TheCamera.GetMatrix().GetPosition().y); ReadDataFromBufferPointer(pBuf, TheCamera.GetMatrix().GetPosition().z); if ( CGame::currLevel != LEVEL_INDUSTRIAL ) CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL); if ( CGame::currLevel != LEVEL_COMMERCIAL ) CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL); if ( CGame::currLevel != LEVEL_SUBURBAN ) CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN); CStreaming::RemoveIslandsNotUsed(CGame::currLevel); CCollision::SortOutCollisionAfterLoad(); CStreaming::RequestBigBuildings(CGame::currLevel); CStreaming::LoadAllRequestedModels(false); CStreaming::HaveAllBigBuildingsLoaded(CGame::currLevel); CGame::TidyUpMemory(true, false); CloseMemCardFile(file); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; return RES_SUCCESS; } int32 CMemoryCard::LoadSavedGame(void) { CheckSum = 0; CDate date; int32 saveSize = 0; uint32 size = 0; int32 oldLang = CMenuManager::m_PrefsLanguage; CPad::ResetCheats(); ChangeDirectory(CurrentCard, Cards[CurrentCard].dir); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; int32 file = OpenMemCardFileForReading(CurrentCard, LoadFileName); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; #define LoadSaveDataBlock()\ do {\ ReadFromMemCard(file, &size, sizeof(size)); \ if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; \ size = align4bytes(size); \ ReadFromMemCard(file, work_buff, size); \ if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; \ buf = work_buff; \ } while (0) uint8 *buf; LoadSaveDataBlock(); ReadDataFromBufferPointer(buf, saveSize); ReadDataFromBufferPointer(buf, CGame::currLevel); ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().x); ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().y); ReadDataFromBufferPointer(buf, TheCamera.GetMatrix().GetPosition().z); ReadDataFromBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute); ReadDataFromBufferPointer(buf, CClock::ms_nLastClockTick); ReadDataFromBufferPointer(buf, CClock::ms_nGameClockHours); ReadDataFromBufferPointer(buf, CClock::ms_nGameClockMinutes); ReadDataFromBufferPointer(buf, CPad::GetPad(0)->Mode); ReadDataFromBufferPointer(buf, CTimer::m_snTimeInMilliseconds); ReadDataFromBufferPointer(buf, CTimer::ms_fTimeScale); ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStep); ReadDataFromBufferPointer(buf, CTimer::ms_fTimeStepNonClipped); ReadDataFromBufferPointer(buf, CTimer::m_FrameCounter); ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeStep); ReadDataFromBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate); ReadDataFromBufferPointer(buf, CTimeStep::ms_fTimeScale); ReadDataFromBufferPointer(buf, CWeather::OldWeatherType); ReadDataFromBufferPointer(buf, CWeather::NewWeatherType); ReadDataFromBufferPointer(buf, CWeather::ForcedWeatherType); ReadDataFromBufferPointer(buf, CWeather::InterpolationValue); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsMusicVolume); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsSfxVolume); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsControllerConfig); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsUseVibration); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsStereoMono); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsRadioStation); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsBrightness); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsShowTrails); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsShowSubtitles); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsLanguage); ReadDataFromBufferPointer(buf, CMenuManager::m_PrefsUseWideScreen); ReadDataFromBufferPointer(buf, CPad::GetPad(0)->Mode); #ifdef PS2 ReadDataFromBufferPointer(buf, BlurOn); #else ReadDataFromBufferPointer(buf, CMBlur::BlurOn); #endif ReadDataFromBufferPointer(buf, date.m_nSecond); ReadDataFromBufferPointer(buf, date.m_nMinute); ReadDataFromBufferPointer(buf, date.m_nHour); ReadDataFromBufferPointer(buf, date.m_nDay); ReadDataFromBufferPointer(buf, date.m_nMonth); ReadDataFromBufferPointer(buf, date.m_nYear); ReadDataFromBufferPointer(buf, CWeather::WeatherTypeInList); ReadDataFromBufferPointer(buf, TheCamera.CarZoomIndicator); ReadDataFromBufferPointer(buf, TheCamera.PedZoomIndicator); if ( date > CompileDateAndTime ) ; else if ( date < CompileDateAndTime ) ; #define ReadDataFromBlock(load_func)\ do {\ ReadDataFromBufferPointer(buf, size);\ load_func(buf, size);\ size = align4bytes(size);\ buf += size;\ } while (0) printf("Loading Scripts \n"); ReadDataFromBlock(CTheScripts::LoadAllScripts); printf("Loading PedPool \n"); ReadDataFromBlock(CPools::LoadPedPool); printf("Loading Garages \n"); ReadDataFromBlock(CGarages::Load); printf("Loading Vehicles \n"); ReadDataFromBlock(CPools::LoadVehiclePool); LoadSaveDataBlock(); CProjectileInfo::RemoveAllProjectiles(); CObject::DeleteAllTempObjects(); printf("Loading Objects \n"); ReadDataFromBlock(CPools::LoadObjectPool); printf("Loading Paths \n"); ReadDataFromBlock(ThePaths.Load); printf("Loading Cranes \n"); ReadDataFromBlock(CCranes::Load); LoadSaveDataBlock(); printf("Loading Pickups \n"); ReadDataFromBlock(CPickups::Load); printf("Loading Phoneinfo \n"); ReadDataFromBlock(gPhoneInfo.Load); printf("Loading Restart \n"); ReadDataFromBlock(CRestart::LoadAllRestartPoints); printf("Loading Radar Blips \n"); ReadDataFromBlock(CRadar::LoadAllRadarBlips); printf("Loading Zones \n"); ReadDataFromBlock(CTheZones::LoadAllZones); printf("Loading Gang Data \n"); ReadDataFromBlock(CGangs::LoadAllGangData); printf("Loading Car Generators \n"); ReadDataFromBlock(CTheCarGenerators::LoadAllCarGenerators); printf("Loading Particles \n"); ReadDataFromBlock(CParticleObject::LoadParticle); printf("Loading AudioScript Objects \n"); ReadDataFromBlock(cAudioScriptObject::LoadAllAudioScriptObjects); printf("Loading Player Info \n"); ReadDataFromBlock(CWorld::Players[CWorld::PlayerInFocus].LoadPlayerInfo); printf("Loading Stats \n"); ReadDataFromBlock(CStats::LoadStats); printf("Loading Streaming Stuff \n"); ReadDataFromBlock(CStreaming::MemoryCardLoad); printf("Loading PedType Stuff \n"); ReadDataFromBlock(CPedType::Load); #undef LoadSaveDataBlock #undef ReadDataFromBlock FrontEndMenuManager.SetSoundLevelsForMusicMenu(); FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame(); CloseMemCardFile(file); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; if ( oldLang != CMenuManager::m_PrefsLanguage ) { TheText.Unload(); TheText.Load(); } JustLoadedDontFadeInYet = true; StillToFadeOut = true; CTheScripts::Process(); printf("Game sucessfully loaded \n"); return RES_SUCCESS; } int32 CMemoryCard::CheckCardInserted(int32 cardID) { #if defined(PS2) int cmd = sceMcFuncNoCardInfo; int type = sceMcTypeNoCard; CTimer::Stop(); while ( sceMcGetInfo(Cards[cardID].port, 0, &type, 0, 0) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( type == sceMcTypePS2 ) { if ( result == sceMcResChangedCard || result == sceMcResSucceed ) { nError = NO_ERR_SUCCESS; return nError; } else if ( result == sceMcResNoFormat ) { nError = ERR_NOFORMAT; return nError; } } printf("Memory card %i not present\n", cardID); nError = ERR_NONE; return nError; #else nError = NO_ERR_SUCCESS; return nError; #endif } int32 CMemoryCard::PopulateCardFlags(int32 cardID, bool bSlotFlag, bool bTypeFlag, bool bFreeFlag, bool bFormatFlag) { #if defined(PS2) int cmd = sceMcFuncNoCardInfo; int type = sceMcTypeNoCard; int free = 0; int format = 0; CTimer::Stop(); Cards[cardID].type = 0; Cards[cardID].free = 0; Cards[cardID].format = 0; while ( sceMcGetInfo(Cards[cardID].port, 0, &type, &free, &format) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( type == sceMcTypePS2 ) { if ( result == sceMcResChangedCard || result == sceMcResSucceed ) { if ( bSlotFlag ) Cards[cardID].slot = 0; //if ( bTypeFlag ) Cards[cardID].type = type; if ( bFreeFlag ) Cards[cardID].free = free; if ( bFormatFlag ) Cards[cardID].format = format; printf("Memory card %i present\n", cardID); nError = NO_ERR_SUCCESS; return nError; } else if ( result == sceMcResNoFormat ) { nError = ERR_NOFORMAT; return nError; } } printf("Memory card %i not present\n", cardID); nError = ERR_NONE; return nError; #else CTimer::Stop(); Cards[cardID].type = 0; Cards[cardID].free = 0; Cards[cardID].format = 0; if ( bSlotFlag ) Cards[cardID].slot = 0; //if ( bTypeFlag ) Cards[cardID].type = 0; if ( bFreeFlag ) Cards[cardID].free = 1024 * 1024 * 4; if ( bFormatFlag ) Cards[cardID].format = 0; printf("Memory card %i present\n", cardID); nError = NO_ERR_SUCCESS; return nError; #endif } int32 CMemoryCard::FormatCard(int32 cardID) { CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoFormat; int32 r = CheckCardInserted(cardID); if ( r == NO_ERR_SUCCESS ) { while ( sceMcFormat(Cards[cardID].port, 0) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result < sceMcResSucceed ) { printf("Memory card %i could not be formatted\n", cardID); nError = ERR_FORMATFAILED; return nError; } printf("Memory card %i present and formatted\n", cardID); nError = NO_ERR_SUCCESS; return nError; } return r; #else printf("Memory card %i present and formatted\n", cardID); nError = NO_ERR_SUCCESS; return nError; #endif } int32 CMemoryCard::PopulateFileTable(int32 cardID) { CTimer::Stop(); #if defined (PS2) int cmd = sceMcFuncNoGetDir; ClearFileTableBuffer(cardID); while ( sceMcGetDir(Cards[cardID].port, 0, "*", 0, ARRAY_SIZE(Cards[cardID].table), Cards[cardID].table) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result >= sceMcResSucceed ) { printf("Memory card %i present PopulateFileTables function successfull \n", cardID); nError = NO_ERR_SUCCESS; return nError; } if ( result == sceMcResNoFormat ) { printf("Memory card %i PopulateFileTables function successfull. MemoryCard not Formatted \n", cardID); nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResNoEntry ) { printf("Memory card %i PopulateFileTables function unsuccessfull. Path does not exist \n", cardID); nError = ERR_FILETABLENOENTRY; return nError; } printf("Memory card %i not Present\n", cardID); nError = ERR_NONE; return nError; #else ClearFileTableBuffer(cardID); char path[512]; sprintf(path, "%s\\%s\\%s\\*", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID]); memset(Cards[cardID].table, 0, sizeof(Cards[cardID].table)); WIN32_FIND_DATA fd; HANDLE hFind; int32 num = 0; if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE ) { printf("Memory card %i not Present\n", cardID); nError = ERR_NONE; return nError; } do { SYSTEMTIME st; FileTimeToSystemTime(&fd.ftCreationTime, &st); Cards[cardID].table[num]._Create.Sec = st.wSecond;Cards[cardID].table[num]._Create.Min = st.wMinute;Cards[cardID].table[num]._Create.Hour = st.wHour;Cards[cardID].table[num]._Create.Day = st.wDay;Cards[cardID].table[num]._Create.Month = st.wMonth;Cards[cardID].table[num]._Create.Year = st.wYear; FileTimeToSystemTime(&fd.ftLastWriteTime, &st); Cards[cardID].table[num]._Modify.Sec = st.wSecond;Cards[cardID].table[num]._Modify.Min = st.wMinute;Cards[cardID].table[num]._Modify.Hour = st.wHour;Cards[cardID].table[num]._Modify.Day = st.wDay;Cards[cardID].table[num]._Modify.Month = st.wMonth;Cards[cardID].table[num]._Modify.Year = st.wYear; Cards[cardID].table[num].FileSizeByte = fd.nFileSizeLow; strncpy((char *)Cards[cardID].table[num].EntryName, fd.cFileName, sizeof(Cards[cardID].table[num].EntryName) - 1); num++; } while( FindNextFile(hFind, &fd) && num < ARRAY_SIZE(Cards[cardID].table) ); FindClose(hFind); //todo errors printf("Memory card %i present PopulateFileTables function successfull \n", cardID); nError = NO_ERR_SUCCESS; return nError; #endif } int32 CMemoryCard::CreateRootDirectory(int32 cardID) { CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoMkdir; while ( sceMcMkdir(Cards[cardID].port, 0, Cards[cardID].dir) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result == sceMcResSucceed ) { printf("Memory card %i present. RootDirectory Created\n", cardID); nError = NO_ERR_SUCCESS; return nError; } if ( result == sceMcResNoFormat ) { printf("Memory card %i RootDirectory not created card unformatted\n", cardID); nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResFullDevice ) { printf("Memory card %i RootDirectory not created due to insufficient memory card capacity\n", cardID); nError = ERR_DIRFULLDEVICE; return nError; } if ( result == sceMcResNoEntry ) { printf("Memory card %i RootDirectory not created due to problem with pathname\n", cardID); nError = ERR_DIRBADENTRY; return nError; } printf("Memory card %i not present so RootDirectory not created \n", cardID); nError = ERR_NONE; return nError; #else char path[512]; sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], Cards[cardID].dir); _psCreateFolder(path); printf("Memory card %i present. RootDirectory Created\n", cardID); nError = NO_ERR_SUCCESS; return nError; #endif } int32 CMemoryCard::ChangeDirectory(int32 cardID, char *dir) { CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoChDir; while ( sceMcChdir(Cards[cardID].port, 0, dir, 0) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result == sceMcResSucceed ) { printf("Memory Card %i. Changed to the directory %s \n", cardID, dir); nError = NO_ERR_SUCCESS; return nError; } if ( result == sceMcResNoFormat ) { printf("Memory card %i. Couldn't change to the directory %s. MemoryCard not Formatted \n", cardID, dir); nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResNoEntry ) { printf("Memory card %i Couldn't change to the directory %s. Path does not exist \n", cardID, dir); nError = ERR_DIRNOENTRY; return nError; } printf("Memory card %i not Present. So could not change to directory %s.\n", cardID, dir); nError = ERR_NONE; return nError; #else if ( !strcmp(dir, "/" ) ) { strncpy(CardCurDir[cardID], dir, sizeof(CardCurDir[cardID]) - 1); printf("Memory Card %i. Changed to the directory %s \n", cardID, dir); nError = NO_ERR_SUCCESS; return nError; } char path[512]; sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], dir); WIN32_FIND_DATA fd; HANDLE hFind; if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE ) { printf("Memory card %i Couldn't change to the directory %s. Path does not exist \n", cardID, dir); nError = ERR_DIRNOENTRY; return nError; } FindClose(hFind); strncpy(CardCurDir[cardID], dir, sizeof(CardCurDir[cardID]) - 1); printf("Memory Card %i. Changed to the directory %s \n", cardID, dir); nError = NO_ERR_SUCCESS; return nError; #endif } int32 CMemoryCard::CreateIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three) { #if defined(PS2) sceMcIconSys icon; static sceVu0IVECTOR bgcolor[4] = { { 0x80, 0, 0, 0 }, { 0, 0x80, 0, 0 }, { 0, 0, 0x80, 0 }, { 0x80, 0x80, 0x80, 0 }, }; static sceVu0FVECTOR lightdir[3] = { { 0.5, 0.5, 0.5, 0.0 }, { 0.0,-0.4,-0.1, 0.0 }, {-0.5,-0.5, 0.5, 0.0 }, }; static sceVu0FVECTOR lightcol[3] = { { 0.48, 0.48, 0.03, 0.00 }, { 0.50, 0.33, 0.20, 0.00 }, { 0.14, 0.14, 0.38, 0.00 }, }; static sceVu0FVECTOR ambient = { 0.50, 0.50, 0.50, 0.00 }; char head[8] = "PS2D"; char title[8] = "GTA3"; memset(&icon, 0, sizeof(icon)); memcpy(icon.BgColor, bgcolor, sizeof(bgcolor)); memcpy(icon.LightDir, lightdir, sizeof(lightdir)); memcpy(icon.LightColor, lightcol, sizeof(lightcol)); memcpy(icon.Ambient, ambient, sizeof(ambient)); icon.OffsLF = 24; icon.TransRate = 0x60; unsigned short *titleName = (unsigned short *)icon.TitleName; uint32 titlec = 0; while ( titlec < strlen(title) ) { unsigned short sjis = Ascii2Sjis(title[titlec]); titleName[titlec] = (sjis << 8) | (sjis >> 8); titlec++; } titleName[titlec] = L'\0'; char icon1[80]; char icon2[80]; char icon3[80]; strncpy(icon1, icon_one, sizeof(icon1) - 1); strncpy(icon2, icon_two, sizeof(icon2) - 1); strncpy(icon3, icon_three, sizeof(icon3) - 1); strncpy((char *)icon.FnameView, icon1, sizeof(icon.FnameView) - 1); strncpy((char *)icon.FnameCopy, icon2, sizeof(icon.FnameCopy) - 1); strncpy((char *)icon.FnameDel, icon3, sizeof(icon.FnameDel) - 1); strncpy((char *)icon.Head, head, sizeof(icon.Head)); int32 iconFile = CreateMemCardFileReadWrite(Cards[cardID].port, "icon.sys"); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; WritetoMemCard(iconFile, &icon, sizeof(icon)); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; CloseMemCardFile(iconFile); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; if ( LoadIconFiles(Cards[cardID].port, icon_one, icon_two, icon_three) == RES_SUCCESS ) { printf("All Icon files Created and loaded. \n"); return RES_SUCCESS; } printf("Could not load all the icon files \n"); return RES_FAILED; #else return RES_SUCCESS; #endif } int32 CMemoryCard::LoadIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three) { #if defined(PS2) const uint32 size = 50968; uint8 *data = new uint8[size]; char icon1_path[80]; char icon2_path[80]; char icon3_path[80]; char icon1[32]; char icon2[32]; char icon3[32]; strncpy(icon1, icon_one, sizeof(icon1) - 1); strncpy(icon2, icon_two, sizeof(icon2) - 1); strncpy(icon3, icon_three, sizeof(icon3) - 1); int hostlen = strlen(HostFileLocationOfIcons); strncpy(icon1_path, HostFileLocationOfIcons, sizeof(icon1_path) - 1); strncpy(icon2_path, HostFileLocationOfIcons, sizeof(icon2_path) - 1); strncpy(icon3_path, HostFileLocationOfIcons, sizeof(icon3_path) - 1); strncpy(icon1_path+hostlen, icon_one, sizeof(icon1_path) - 1 - hostlen); strncpy(icon2_path+hostlen, icon_two, sizeof(icon2_path) - 1 - hostlen); strncpy(icon3_path+hostlen, icon_three, sizeof(icon3_path) - 1 - hostlen); // ico1 copy int32 ico1file = CFileMgr::OpenFile(icon1_path); CFileMgr::Read(ico1file, (char *)data, size); CFileMgr::CloseFile(ico1file); int32 ico1mc = CreateMemCardFileReadWrite(Cards[cardID].port, icon1); if ( nError != NO_ERR_SUCCESS ) { delete [] data; return RES_FAILED; } WritetoMemCard(ico1mc, data, size); if ( nError != NO_ERR_SUCCESS ) { delete [] data; return RES_FAILED; } CloseMemCardFile(ico1mc); if ( nError != NO_ERR_SUCCESS ) { delete [] data; return RES_FAILED; } // ico2 copy int32 ico2file = CFileMgr::OpenFile(icon2_path); CFileMgr::Read(ico2file, (char *)data, size); CFileMgr::CloseFile(ico2file); int32 ico2mc = CreateMemCardFileReadWrite(Cards[cardID].port, icon2); if ( nError != NO_ERR_SUCCESS ) { delete [] data; return RES_FAILED; } WritetoMemCard(ico2mc, data, size); if ( nError != NO_ERR_SUCCESS ) { delete [] data; return RES_FAILED; } CloseMemCardFile(ico2mc); if ( nError != NO_ERR_SUCCESS ) { delete [] data; return RES_FAILED; } // ico3 copy int32 ico3file = CFileMgr::OpenFile(icon3_path); CFileMgr::Read(ico3file, (char *)data, size); CFileMgr::CloseFile(ico3file); int32 ico3mc = CreateMemCardFileReadWrite(Cards[cardID].port, icon3); if ( nError != NO_ERR_SUCCESS ) { delete [] data; return RES_FAILED; } WritetoMemCard(ico3mc, data, size); if ( nError != NO_ERR_SUCCESS ) { delete [] data; return RES_FAILED; } CloseMemCardFile(ico3mc); if ( nError != NO_ERR_SUCCESS ) { delete [] data; return RES_FAILED; } delete [] data; return RES_SUCCESS; #else return RES_SUCCESS; #endif } int32 CMemoryCard::CloseMemCardFile(int32 file) { CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoClose; while ( sceMcClose(file) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result == sceMcResSucceed ) { printf("File %i closed\n", file); nError = NO_ERR_SUCCESS; return nError; } if ( result == sceMcResNoFormat ) { printf("Memory Card is Unformatted"); nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResNoEntry ) { printf("Memory Card File Handle %i has not been opened", file); nError = ERR_OPENNOENTRY; return nError; } nError = ERR_NONE; return nError; #else CFileMgr::CloseFile(file); printf("File %i closed\n", file); nError = NO_ERR_SUCCESS; return nError; #endif } int32 CMemoryCard::CreateMemCardFileReadWrite(int32 cardID, char *filename) { CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoOpen; char buff[255]; strncpy(buff, filename, sizeof(buff)); while ( sceMcOpen(Cards[cardID].port, 0, buff, SCE_RDWR|SCE_CREAT) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result >= sceMcResSucceed ) { printf("%s File Created for MemoryCard. Its File handle is %i. \n", buff, result); nError = NO_ERR_SUCCESS; return result; } if ( result == sceMcResNoFormat ) { nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResFullDevice ) { nError = ERR_FILEFULLDEVICE; return nError; } if ( result == sceMcResNoEntry ) { nError = ERR_FILENOPATHENTRY; return nError; } if ( result == sceMcResDeniedPermit ) { nError = ERR_FILEDENIED; return nError; } if ( result == sceMcResUpLimitHandle ) { nError = ERR_FILEUPLIMIT; return nError; } printf("File %s not created on memory card.\n", buff); return ERR_NONE; #else char path[512]; sprintf(path, "%s\\%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID], filename); int32 file = CFileMgr::OpenFile(path, "wb+"); if (file == 0) { sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], filename); file = CFileMgr::OpenFile(path, "wb+"); } if ( file ) { printf("%s File Created for MemoryCard. Its File handle is %i. \n", filename, file); nError = NO_ERR_SUCCESS; return file; } printf("File %s not created on memory card.\n", path); nError = ERR_NONE; return 0; #endif } int32 CMemoryCard::OpenMemCardFileForReading(int32 cardID, char *filename) { CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoOpen; char buff[255]; strncpy(buff, filename, sizeof(buff)); while ( sceMcOpen(Cards[cardID].port, 0, buff, SCE_RDONLY) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result >= sceMcResSucceed ) { printf("%s File Created for MemoryCard. Its File handle is %i. \n", buff, result); nError = NO_ERR_SUCCESS; return result; } if ( result == sceMcResNoFormat ) { nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResFullDevice ) { nError = ERR_FILEFULLDEVICE; return nError; } if ( result == sceMcResNoEntry ) { nError = ERR_FILENOPATHENTRY; return nError; } if ( result == sceMcResDeniedPermit ) { nError = ERR_FILEDENIED; return nError; } if ( result == sceMcResUpLimitHandle ) { nError = ERR_FILEUPLIMIT; return nError; } printf("File %s not created on memory card.\n", buff); nError = ERR_NONE; return nError; #else char path[512]; sprintf(path, "%s\\%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID], filename); int32 file = CFileMgr::OpenFile(path, "rb"); if (file == 0) { sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], filename); file = CFileMgr::OpenFile(path, "rb"); } if ( file ) { printf("%s File Created for MemoryCard. Its File handle is %i. \n", filename, file); nError = NO_ERR_SUCCESS; return file; } printf("File %s not created on memory card.\n", path); nError = ERR_NONE; return 0; #endif } int32 CMemoryCard::ReadFromMemCard(int32 file, void *buff, int32 size) { CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoRead; while ( sceMcRead(file, buff, size) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result >= sceMcResSucceed ) { if ( size >= result ) { printf("%i Bytes Read for Filehandle %i \n", result, file); nError = NO_ERR_SUCCESS; return result; } } if ( result == sceMcResNoFormat ) { nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResNoEntry ) { nError = ERR_READNOENTRY; return nError; } if ( result == sceMcResDeniedPermit ) { nError = ERR_READDENIED; return nError; } printf("No Bytes Read for Filehandle %i \n", file); nError = ERR_NONE; return result; #else int32 s = CFileMgr::Read(file, (const char *)buff, size); if ( s == size ) { printf("%i Bytes Read for Filehandle %i \n", s, file); nError = NO_ERR_SUCCESS; return s; } printf("No Bytes Read for Filehandle %i \n", file); nError = ERR_NONE; return s; #endif } int32 CMemoryCard::DeleteMemoryCardFile(int32 cardID, char *filename) { CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoDelete; while ( sceMcDelete(Cards[cardID].port, 0, filename) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result == sceMcResSucceed ) { printf("Memory Card %i, %s NO_ERR_SUCCESSfully deleted", cardID, filename); nError = NO_ERR_SUCCESS; return nError; } if ( result == sceMcResNoFormat ) { printf("Memory Card %i, %s not deleted as memory Card is unformatted", cardID, filename); nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResNoEntry ) { printf("Memory Card %i, %s attempt made to delete non existing file", cardID, filename); nError = ERR_DELETENOENTRY; return nError; } if ( result == sceMcResDeniedPermit ) { printf("Memory Card %i, %s not deleted as file is in use or write protected", cardID, filename); nError = ERR_DELETEDENIED; return nError; } if ( result == sceMcResNotEmpty ) { printf("Memory Card %i, %s not deleted. Entries remain in subdirectory", cardID, filename); nError = ERR_DELETEFAILED; return nError; } nError = ERR_NONE; return nError; #else char path[512]; sprintf(path, "%s\\%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID], filename); int32 file = CFileMgr::OpenFile(path, "rb"); if (file == 0) { sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], filename); file = CFileMgr::OpenFile(path, "rb"); } if ( file ) { CFileMgr::CloseFile(file); DeleteFile(path); printf("Memory Card %i, %s NO_ERR_SUCCESSfully deleted", cardID, filename); nError = NO_ERR_SUCCESS; return nError; } printf("Memory Card %i, %s attempt made to delete non existing file", cardID, filename); nError = ERR_DELETENOENTRY; //nError = ERR_NONE; return nError; #endif } void CMemoryCard::PopulateErrorMessage() { switch ( nError ) { case ERR_WRITEFULLDEVICE: case ERR_DIRFULLDEVICE: pErrorMsg = TheText.Get("SLONDR"); break; // Insufficient space to save. Please insert a Memory Card (PS2) with at least 500KB of free space available into MEMORY CARD slot 1. case ERR_FORMATFAILED: pErrorMsg = TheText.Get("SLONFM"); break; // Error formatting Memory Card (PS2) in MEMORY CARD slot 1. case ERR_SAVEFAILED: pErrorMsg = TheText.Get("SLNSP"); break; // Insufficient space to save. Please insert a Memory Card (PS2) with at least 200KB of free space available into MEMORY CARD slot 1. case ERR_DELETEDENIED: case ERR_NOFORMAT: pErrorMsg = TheText.Get("SLONNF"); break; // Memory Card (PS2) in MEMORY CARD slot 1 is unformatted. case ERR_NONE: pErrorMsg = TheText.Get("SLONNO"); break; // No Memory Card (PS2) in MEMORY CARD slot 1. } } int32 CMemoryCard::WritetoMemCard(int32 file, void *buff, int32 size) { #if defined(PS2) int cmd = sceMcFuncNoWrite; int result = sceMcResSucceed; int result1 = sceMcResSucceed; CTimer::Stop(); while ( sceMcWrite(file, buff, size) != sceMcResSucceed ) ; sceMcSync(0, &cmd, &result); if ( result == sceMcResNoFormat ) { printf("No Bytes written for Filehandle %i \n", file); nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResFullDevice ) { printf("No Bytes written for Filehandle %i \n", file); nError = ERR_WRITEFULLDEVICE; return nError; } if ( result == sceMcResNoEntry ) { printf("No Bytes written for Filehandle %i \n", file); nError = ERR_WRITENOENTRY; return nError; } if ( result == sceMcResDeniedPermit ) { printf("No Bytes written for Filehandle %i \n", file); nError = ERR_WRITEDENIED; return nError; } if ( result == sceMcResFailReplace ) { printf("No Bytes written for Filehandle %i \n", file); nError = ERR_WRITEFAILED; return nError; } if ( result <= -10 ) { printf("No Bytes written for Filehandle %i \n", file); nError = ERR_NONE; return nError; } cmd = sceMcFuncNoFlush; while ( sceMcFlush(file) != sceMcResSucceed ) ; sceMcSync(0, &cmd, &result1); if ( result1 == sceMcResNoFormat ) { printf("No Bytes written for Filehandle %i \n", file); nError = ERR_NOFORMAT; return nError; } if ( result1 == sceMcResNoEntry ) { printf("No Bytes written for Filehandle %i \n", file); nError = ERR_FLUSHNOENTRY; return nError; } if ( result1 <= -10 ) { printf("No Bytes written for Filehandle %i \n", file); nError = ERR_NONE; return nError; } if ( result > 0 && result1 == sceMcResSucceed ) { printf("%i Bytes written for Filehandle %i \n", result, file); } else if ( result == sceMcResSucceed && result1 == sceMcResSucceed ) { printf("Filehandle %i was flushed\n", file); } nError = NO_ERR_SUCCESS; return nError; #else CTimer::Stop(); int32 s = CFileMgr::Write(file, (const char *)buff, size); if ( s == size ) { printf("%i Bytes written for Filehandle %i \n", s, file); nError = NO_ERR_SUCCESS; return s; } nError = ERR_NONE; return s; #endif } static inline void MakeSpaceForSizeInBufferPointer(uint8 *&presize, uint8 *&buf, uint8 *&postsize) { presize = buf; buf += sizeof(uint32); postsize = buf; } static inline void CopySizeAndPreparePointer(uint8 *&buf, uint8 *&postbuf, uint8 *&postbuf2, uint32 &unused, uint32 &size) { memcpy(buf, &size, sizeof(size)); size = align4bytes(size); postbuf2 += size; postbuf = postbuf2; } bool CMemoryCard::SaveGame(void) { uint32 saveSize = 0; uint32 totalSize = 0; CurrentCard = CARD_ONE; CheckSum = 0; CGameLogic::PassTime(360); CPlayerPed *ped = FindPlayerPed(); ped->m_fCurrentStamina = ped->m_fMaxStamina; CGame::TidyUpMemory(true, false); saveSize = SAVE_FILE_SIZE; int32 minfree = 198; PopulateCardFlags(CurrentCard, false, false, true, false); if ( nError != NO_ERR_SUCCESS ) return false; minfree += GetClusterAmountForFileCreation(CurrentCard); if ( nError != NO_ERR_SUCCESS ) return false; if ( Cards[CurrentCard].free < 200 ) { CTimer::Update(); uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode(); while ( CTimer::GetTimeInMillisecondsPauseMode()-startTime < 1250 ) { for ( int32 i = 0; i < 1000; i++ ) powf(3.33f, 3.444f); CTimer::Update(); } nError = ERR_SAVEFAILED; return false; } if ( Cards[CurrentCard].free < minfree ) { CTimer::Update(); uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode(); while ( CTimer::GetTimeInMillisecondsPauseMode()-startTime < 1250 ) { for ( int32 i = 0; i < 1000; i++ ) powf(3.33f, 3.444f); CTimer::Update(); } nError = ERR_SAVEFAILED; return false; } uint32 size; uint8 *buf = work_buff; uint32 reserved = 0; int32 file = CreateMemCardFileReadWrite(CurrentCard, ValidSaveName); if ( nError != NO_ERR_SUCCESS ) { strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1); return false; } WriteDataToBufferPointer(buf, saveSize); WriteDataToBufferPointer(buf, CGame::currLevel); WriteDataToBufferPointer(buf, TheCamera.GetPosition().x); WriteDataToBufferPointer(buf, TheCamera.GetPosition().y); WriteDataToBufferPointer(buf, TheCamera.GetPosition().z); WriteDataToBufferPointer(buf, CClock::ms_nMillisecondsPerGameMinute); WriteDataToBufferPointer(buf, CClock::ms_nLastClockTick); WriteDataToBufferPointer(buf, CClock::ms_nGameClockHours); WriteDataToBufferPointer(buf, CClock::ms_nGameClockMinutes); WriteDataToBufferPointer(buf, CPad::GetPad(0)->Mode); WriteDataToBufferPointer(buf, CTimer::m_snTimeInMilliseconds); WriteDataToBufferPointer(buf, CTimer::ms_fTimeScale); WriteDataToBufferPointer(buf, CTimer::ms_fTimeStep); WriteDataToBufferPointer(buf, CTimer::ms_fTimeStepNonClipped); WriteDataToBufferPointer(buf, CTimer::m_FrameCounter); WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeStep); WriteDataToBufferPointer(buf, CTimeStep::ms_fFramesPerUpdate); WriteDataToBufferPointer(buf, CTimeStep::ms_fTimeScale); WriteDataToBufferPointer(buf, CWeather::OldWeatherType); WriteDataToBufferPointer(buf, CWeather::NewWeatherType); WriteDataToBufferPointer(buf, CWeather::ForcedWeatherType); WriteDataToBufferPointer(buf, CWeather::InterpolationValue); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsMusicVolume); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsSfxVolume); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsControllerConfig); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsUseVibration); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsStereoMono); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsRadioStation); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsBrightness); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsShowTrails); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsShowSubtitles); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsLanguage); WriteDataToBufferPointer(buf, CMenuManager::m_PrefsUseWideScreen); WriteDataToBufferPointer(buf, CPad::GetPad(0)->Mode); #ifdef PS2 WriteDataToBufferPointer(buf, BlurOn); #else WriteDataToBufferPointer(buf, CMBlur::BlurOn); #endif WriteDataToBufferPointer(buf, CompileDateAndTime.m_nSecond); WriteDataToBufferPointer(buf, CompileDateAndTime.m_nMinute); WriteDataToBufferPointer(buf, CompileDateAndTime.m_nHour); WriteDataToBufferPointer(buf, CompileDateAndTime.m_nDay); WriteDataToBufferPointer(buf, CompileDateAndTime.m_nMonth); WriteDataToBufferPointer(buf, CompileDateAndTime.m_nYear); WriteDataToBufferPointer(buf, CWeather::WeatherTypeInList); WriteDataToBufferPointer(buf, TheCamera.CarZoomIndicator); WriteDataToBufferPointer(buf, TheCamera.PedZoomIndicator); if ( nError != NO_ERR_SUCCESS ) { strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1); return false; } uint8 *presize; uint8 *postsize; #define WriteSaveDataBlock(save_func)\ do {\ MakeSpaceForSizeInBufferPointer(presize, buf, postsize);\ save_func(buf, &size);\ CopySizeAndPreparePointer(presize, buf, postsize, reserved, size);\ } while (0) WriteSaveDataBlock(CTheScripts::SaveAllScripts); printf("Script Save Size %d, \n", size); WriteSaveDataBlock(CPools::SavePedPool); printf("PedPool Save Size %d, \n", size); WriteSaveDataBlock(CGarages::Save); printf("Garage Save Size %d, \n", size); WriteSaveDataBlock(CPools::SaveVehiclePool); printf("Vehicle Save Size %d, \n", size); DoClassSaveRoutine(file, work_buff, buf - work_buff); totalSize += buf - work_buff; if ( nError != NO_ERR_SUCCESS ) return false; buf = work_buff; reserved = 0; WriteSaveDataBlock(CPools::SaveObjectPool); printf("Object Save Size %d, \n", size); WriteSaveDataBlock(ThePaths.Save); printf("The Paths Save Size %d, \n", size); WriteSaveDataBlock(CCranes::Save); printf("Cranes Save Size %d, \n", size); DoClassSaveRoutine(file, work_buff, buf - work_buff); totalSize += buf - work_buff; if ( nError != NO_ERR_SUCCESS ) return false; buf = work_buff; reserved = 0; WriteSaveDataBlock(CPickups::Save); printf("Pick Ups Save Size %d, \n", size); WriteSaveDataBlock(gPhoneInfo.Save); printf("Phones Save Size %d, \n", size); WriteSaveDataBlock(CRestart::SaveAllRestartPoints); printf("RestartPoints Save Size %d, \n", size); WriteSaveDataBlock(CRadar::SaveAllRadarBlips); printf("Radar Save Size %d, \n", size); WriteSaveDataBlock(CTheZones::SaveAllZones); printf("Save Size %d, \n", size); WriteSaveDataBlock(CGangs::SaveAllGangData); printf("Gangs Save Size %d, \n", size); WriteSaveDataBlock(CTheCarGenerators::SaveAllCarGenerators); printf("Car Gens Save Size %d, \n", size); WriteSaveDataBlock(CParticleObject::SaveParticle); printf("Particles Save Size %d, \n", size); WriteSaveDataBlock(cAudioScriptObject::SaveAllAudioScriptObjects); printf("Audio Script Save Size %d, \n", size); WriteSaveDataBlock(CWorld::Players[CWorld::PlayerInFocus].SavePlayerInfo); printf("Player Info Save Size %d, \n", size); WriteSaveDataBlock(CStats::SaveStats); printf("Stats Save Size %d, \n", size); WriteSaveDataBlock(CStreaming::MemoryCardSave); printf("Streaming Save Size %d, \n", size); WriteSaveDataBlock(CPedType::Save); printf("PedType Save Size %d, \n", size); DoClassSaveRoutine(file, work_buff, buf - work_buff); totalSize += buf - work_buff; if ( nError != NO_ERR_SUCCESS ) return false; buf = work_buff; reserved = 0; for (int32 i = 0; i < 3; i++) { size = align4bytes(saveSize - totalSize - 4); if (size > sizeof(work_buff)) size = sizeof(work_buff); if (size > 4) { DoClassSaveRoutine(file, work_buff, size); totalSize += size; } } WritetoMemCard(file, &CheckSum, sizeof(CheckSum)); CloseMemCardFile(file); #undef WriteSaveDataBlock if ( nError != NO_ERR_SUCCESS ) { strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1); DoHackRoundSTUPIDSonyDateTimeStuff(CARD_ONE, ValidSaveName); return false; } DoHackRoundSTUPIDSonyDateTimeStuff(CARD_ONE, ValidSaveName); strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(SaveFileNameJustSaved) - 1); return true; } bool CMemoryCard::DoHackRoundSTUPIDSonyDateTimeStuff(int32 port, char *filename) { #if defined(PS2) int cmd = sceMcFuncNoFileInfo; int result = sceMcResSucceed; sceCdCLOCK rtc; sceCdReadClock(&rtc); sceScfGetLocalTimefromRTC(&rtc); #define ROUNDHACK(a) ( ((a) & 15) + ( ( ( ((a) >> 4) << 2 ) + ((a) >> 4) ) << 1 ) ) sceMcTblGetDir info; info._Create.Sec = ROUNDHACK(rtc.second); info._Create.Min = ROUNDHACK(rtc.minute); info._Create.Hour = ROUNDHACK(rtc.hour); info._Create.Day = ROUNDHACK(rtc.day); info._Create.Month = ROUNDHACK(rtc.month); info._Create.Year = ROUNDHACK(rtc.year) + 2000; #undef ROUNDHACK while ( sceMcSetFileInfo(port, 0, filename, (char *)&info, sceMcFileInfoCreate) != sceMcResSucceed ) ; sceMcSync(0, &cmd, &result); return sceMcResSucceed >= result; #else return true; #endif } int32 CMemoryCard::LookForRootDirectory(int32 cardID) { CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoGetDir; while ( sceMcGetDir(Cards[cardID].port, 0, Cards[cardID].dir, 0, ARRAY_SIZE(Cards[cardID].table), Cards[cardID].table) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result == 0 ) { nError = NO_ERR_SUCCESS; return ERR_NOROOTDIR; } if ( result > sceMcResSucceed ) { printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID); nError = NO_ERR_SUCCESS; return nError; } if ( result == sceMcResNoFormat ) { printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. MemoryCard not Formatted \n", cardID); nError = ERR_NOFORMAT; return nError; } if ( result == sceMcResNoEntry ) { printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. Path does not exist \n", cardID); nError = ERR_FILETABLENOENTRY; return nError; } printf("Memory card %i not Present\n", cardID); nError = ERR_NONE; return nError; #else char path[512]; sprintf(path, "%s\\%s\\%s", PCCardsPath, PCCardDir[cardID], Cards[cardID].dir); memset(Cards[cardID].table, 0, sizeof(Cards[cardID].table)); WIN32_FIND_DATA fd; HANDLE hFind; int32 num = 0; if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE ) { nError = NO_ERR_SUCCESS; return ERR_NOROOTDIR; } do { SYSTEMTIME st; FileTimeToSystemTime(&fd.ftCreationTime, &st); Cards[cardID].table[num]._Create.Sec = st.wSecond;Cards[cardID].table[num]._Create.Min = st.wMinute;Cards[cardID].table[num]._Create.Hour = st.wHour;Cards[cardID].table[num]._Create.Day = st.wDay;Cards[cardID].table[num]._Create.Month = st.wMonth;Cards[cardID].table[num]._Create.Year = st.wYear; FileTimeToSystemTime(&fd.ftLastWriteTime, &st); Cards[cardID].table[num]._Modify.Sec = st.wSecond;Cards[cardID].table[num]._Modify.Min = st.wMinute;Cards[cardID].table[num]._Modify.Hour = st.wHour;Cards[cardID].table[num]._Modify.Day = st.wDay;Cards[cardID].table[num]._Modify.Month = st.wMonth;Cards[cardID].table[num]._Modify.Year = st.wYear; Cards[cardID].table[num].FileSizeByte = fd.nFileSizeLow; strncpy((char *)Cards[cardID].table[num].EntryName, fd.cFileName, sizeof(Cards[cardID].table[num].EntryName) - 1); num++; } while( FindNextFile(hFind, &fd) && num < ARRAY_SIZE(Cards[cardID].table) ); FindClose(hFind); if ( num == 0 ) { nError = NO_ERR_SUCCESS; return ERR_NOROOTDIR; } //todo errors printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID); nError = NO_ERR_SUCCESS; return nError; #endif } int32 CMemoryCard::FillFirstFileWithGuff(int32 cardID) { CTimer::Stop(); char buff[80]; strncpy(buff, Cards[cardID].dir+1, sizeof(buff) - 1); int32 file = CreateMemCardFileReadWrite(Cards[cardID].port, buff); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; const int32 kBlockSize = GUFF_FILE_SIZE / 3; work_buff[kBlockSize-1] = 5; WritetoMemCard(file, work_buff, kBlockSize); WritetoMemCard(file, work_buff, kBlockSize); WritetoMemCard(file, work_buff, kBlockSize); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; CloseMemCardFile(file); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; return RES_SUCCESS; } bool CMemoryCard::FindMostRecentFileName(int32 cardID, char *filename) { CDate date1, date2; CTimer::Stop(); #if defined(PS2) int cmd = sceMcFuncNoGetDir; ClearFileTableBuffer(cardID); while ( sceMcGetDir(Cards[cardID].port, 0, "*", 0, ARRAY_SIZE(Cards[cardID].table), Cards[cardID].table) != sceMcResSucceed ) ; int result; sceMcSync(0, &cmd, &result); if ( result >= sceMcResSucceed ) { printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID); nError = NO_ERR_SUCCESS; for ( int32 entry = 7; entry < ARRAY_SIZE(Cards[CARD_ONE].table); entry++ ) { bool found = false; if ( Cards[CARD_ONE].table[entry]._Modify.Sec != 0 || Cards[CARD_ONE].table[entry]._Modify.Min != 0 || Cards[CARD_ONE].table[entry]._Modify.Hour != 0 || Cards[CARD_ONE].table[entry]._Modify.Day != 0 || Cards[CARD_ONE].table[entry]._Modify.Month != 0 || Cards[CARD_ONE].table[entry]._Modify.Year != 0 ) { date1.m_nSecond = Cards[CARD_ONE].table[entry]._Modify.Sec; date1.m_nMinute = Cards[CARD_ONE].table[entry]._Modify.Min; date1.m_nHour = Cards[CARD_ONE].table[entry]._Modify.Hour; date1.m_nDay = Cards[CARD_ONE].table[entry]._Modify.Day; date1.m_nMonth = Cards[CARD_ONE].table[entry]._Modify.Month; date1.m_nYear = Cards[CARD_ONE].table[entry]._Modify.Year; if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0 && Cards[CARD_ONE].table[entry].AttrFile & sceMcFileAttrClosed && Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) { found = true; } } else if ( Cards[CARD_ONE].table[entry]._Create.Sec != 0 || Cards[CARD_ONE].table[entry]._Create.Min != 0 || Cards[CARD_ONE].table[entry]._Create.Hour != 0 || Cards[CARD_ONE].table[entry]._Create.Day != 0 || Cards[CARD_ONE].table[entry]._Create.Month != 0 || Cards[CARD_ONE].table[entry]._Create.Year != 0 ) { date1.m_nSecond = Cards[CARD_ONE].table[entry]._Create.Sec; date1.m_nMinute = Cards[CARD_ONE].table[entry]._Create.Min; date1.m_nHour = Cards[CARD_ONE].table[entry]._Create.Hour; date1.m_nDay = Cards[CARD_ONE].table[entry]._Create.Day; date1.m_nMonth = Cards[CARD_ONE].table[entry]._Create.Month; date1.m_nYear = Cards[CARD_ONE].table[entry]._Create.Year; if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0 && Cards[CARD_ONE].table[entry].AttrFile & sceMcFileAttrClosed && Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) { found = true; } } if ( found ) { int32 d; if ( date1 > date2 ) d = 1; else if ( date1 < date2 ) d = 2; else d = 0; if ( d == 1 ) { char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName; date2 = date1; strncpy(filename, entryname, 28); } else { int32 d; if ( date1 > date2 ) d = 1; else if ( date1 < date2 ) d = 2; else d = 0; if ( d == 0 ) { char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName; date2 = date1; strncpy(filename, entryname, 28); } } } } if ( date2.m_nSecond != 0 || date2.m_nMinute != 0 || date2.m_nHour != 0 || date2.m_nDay != 0 || date2.m_nMonth != 0 || date2.m_nYear != 0 ) { return true; } return false; } if ( result == sceMcResNoFormat ) { printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. MemoryCard not Formatted \n", cardID); nError = ERR_NOFORMAT; return false; } if ( result == sceMcResNoEntry ) { printf("Memory card %i PopulateFileTables function unNO_ERR_SUCCESSfull. Path does not exist \n", cardID); nError = ERR_FILETABLENOENTRY; return false; } printf("Memory card %i not Present\n", cardID); nError = ERR_NONE; return false; #else ClearFileTableBuffer(cardID); char path[512]; sprintf(path, "%s\\%s\\%s\\*", PCCardsPath, PCCardDir[cardID], CardCurDir[cardID]); memset(Cards[cardID].table, 0, sizeof(Cards[cardID].table)); WIN32_FIND_DATA fd; HANDLE hFind; int32 num = 0; if ( (hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE ) { printf("Memory card %i not Present\n", cardID); nError = ERR_NONE; return nError; } do { SYSTEMTIME st; FileTimeToSystemTime(&fd.ftCreationTime, &st); Cards[cardID].table[num]._Create.Sec = st.wSecond;Cards[cardID].table[num]._Create.Min = st.wMinute;Cards[cardID].table[num]._Create.Hour = st.wHour;Cards[cardID].table[num]._Create.Day = st.wDay;Cards[cardID].table[num]._Create.Month = st.wMonth;Cards[cardID].table[num]._Create.Year = st.wYear; FileTimeToSystemTime(&fd.ftLastWriteTime, &st); Cards[cardID].table[num]._Modify.Sec = st.wSecond;Cards[cardID].table[num]._Modify.Min = st.wMinute;Cards[cardID].table[num]._Modify.Hour = st.wHour;Cards[cardID].table[num]._Modify.Day = st.wDay;Cards[cardID].table[num]._Modify.Month = st.wMonth;Cards[cardID].table[num]._Modify.Year = st.wYear; Cards[cardID].table[num].FileSizeByte = fd.nFileSizeLow; strncpy((char *)Cards[cardID].table[num].EntryName, fd.cFileName, sizeof(Cards[cardID].table[num].EntryName) - 1); num++; } while( FindNextFile(hFind, &fd) && num < ARRAY_SIZE(Cards[cardID].table) ); FindClose(hFind); if ( num > 0 ) { printf("Memory card %i present PopulateFileTables function NO_ERR_SUCCESSfull \n", cardID); nError = NO_ERR_SUCCESS; for ( int32 entry = 0; entry < ARRAY_SIZE(Cards[CARD_ONE].table); entry++ ) { bool found = false; if ( Cards[CARD_ONE].table[entry]._Modify.Sec != 0 || Cards[CARD_ONE].table[entry]._Modify.Min != 0 || Cards[CARD_ONE].table[entry]._Modify.Hour != 0 || Cards[CARD_ONE].table[entry]._Modify.Day != 0 || Cards[CARD_ONE].table[entry]._Modify.Month != 0 || Cards[CARD_ONE].table[entry]._Modify.Year != 0 ) { date1.m_nSecond = Cards[CARD_ONE].table[entry]._Modify.Sec; date1.m_nMinute = Cards[CARD_ONE].table[entry]._Modify.Min; date1.m_nHour = Cards[CARD_ONE].table[entry]._Modify.Hour; date1.m_nDay = Cards[CARD_ONE].table[entry]._Modify.Day; date1.m_nMonth = Cards[CARD_ONE].table[entry]._Modify.Month; date1.m_nYear = Cards[CARD_ONE].table[entry]._Modify.Year; if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0 && Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) { found = true; } } else if ( Cards[CARD_ONE].table[entry]._Create.Sec != 0 || Cards[CARD_ONE].table[entry]._Create.Min != 0 || Cards[CARD_ONE].table[entry]._Create.Hour != 0 || Cards[CARD_ONE].table[entry]._Create.Day != 0 || Cards[CARD_ONE].table[entry]._Create.Month != 0 || Cards[CARD_ONE].table[entry]._Create.Year != 0 ) { date1.m_nSecond = Cards[CARD_ONE].table[entry]._Create.Sec; date1.m_nMinute = Cards[CARD_ONE].table[entry]._Create.Min; date1.m_nHour = Cards[CARD_ONE].table[entry]._Create.Hour; date1.m_nDay = Cards[CARD_ONE].table[entry]._Create.Day; date1.m_nMonth = Cards[CARD_ONE].table[entry]._Create.Month; date1.m_nYear = Cards[CARD_ONE].table[entry]._Create.Year; if ( Cards[CARD_ONE].table[entry].FileSizeByte != 0 && Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) { found = true; } } if ( found ) { int32 d; if ( date1 > date2 ) d = 1; else if ( date1 < date2 ) d = 2; else d = 0; if ( d == 1 ) { char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName; date2 = date1; strncpy(filename, entryname, 28); } else { int32 d; if ( date1 > date2 ) d = 1; else if ( date1 < date2 ) d = 2; else d = 0; if ( d == 0 ) { char *entryname = (char *)Cards[CARD_ONE].table[entry].EntryName; date2 = date1; strncpy(filename, entryname, 28); } } } } if ( date2.m_nSecond != 0 || date2.m_nMinute != 0 || date2.m_nHour != 0 || date2.m_nDay != 0 || date2.m_nMonth != 0 || date2.m_nYear != 0 ) { return true; } return false; } //todo errors nError = ERR_NONE; return false; #endif } void CMemoryCard::ClearFileTableBuffer(int32 cardID) { for ( int32 i = 0; i < ARRAY_SIZE(Cards[cardID].table); i++ ) { Cards[cardID].table[i].FileSizeByte = 0; strncpy((char *)Cards[cardID].table[i].EntryName, " ", sizeof(Cards[cardID].table[i].EntryName) - 1); } } int32 CMemoryCard::GetClusterAmountForFileCreation(int32 port) { #if defined(PS2) int cmd = sceMcFuncNoEntSpace; int result = 0; CTimer::Stop(); while ( sceMcGetEntSpace(port, 0, TheGameRootDirectory) != sceMcResSucceed ) ; sceMcSync(0, &cmd, &result); if ( result >= sceMcResSucceed ) { nError = NO_ERR_SUCCESS; return result; } if ( result == sceMcResNoFormat ) { nError = ERR_NOFORMAT; return nError; } nError = ERR_NONE; return nError; #else CTimer::Stop(); nError = NO_ERR_SUCCESS; return 0; #endif } bool CMemoryCard::DeleteEverythingInGameRoot(int32 cardID) { CTimer::Stop(); ChangeDirectory(CurrentCard, Cards[CurrentCard].dir); if ( nError != NO_ERR_SUCCESS ) return false; PopulateFileTable(cardID); for ( int32 i = ARRAY_SIZE(Cards[cardID].table) - 1; i >= 0; i--) DeleteMemoryCardFile(cardID, (char *)Cards[cardID].table[i].EntryName); ChangeDirectory(CurrentCard, "/"); DeleteMemoryCardFile(cardID, Cards[CurrentCard].dir); if ( nError != NO_ERR_SUCCESS ) return false; return true; } int32 CMemoryCard::CheckDataNotCorrupt(char *filename) { CheckSum = 0; int32 lang = 0; int32 level = 0; LastBlockSize = 0; char buf[100*4]; for ( int32 i = 0; i < sizeof(buf); i++ ) buf[i] = '\0'; strncpy(buf, Cards[CurrentCard].dir, sizeof(buf) - 1); strncat(buf, "/", sizeof(buf) - 1); strcat (buf, filename); ChangeDirectory(CurrentCard, Cards[CurrentCard].dir); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; int32 file = OpenMemCardFileForReading(CurrentCard, buf); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; int32 bytes_processed = 0; int32 blocknum = 0; int32 lastblocksize; while ( SAVE_FILE_SIZE - sizeof(int32) > bytes_processed && blocknum < 8 ) { int32 size; ReadFromMemCard(file, &size, sizeof(size)); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; lastblocksize = ReadFromMemCard(file, work_buff, align4bytes(size)); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; uint8 sizebuff[4]; memcpy(sizebuff, &size, sizeof(size)); for ( int32 i = 0; i < ARRAY_SIZE(sizebuff); i++ ) CheckSum += sizebuff[i]; uint8 *pWork_buf = work_buff; for ( int32 i = 0; i < lastblocksize; i++ ) { CheckSum += *pWork_buf++; bytes_processed++; } if ( blocknum == 0 ) { uint8 *pBuf = work_buff + sizeof(uint32); ReadDataFromBufferPointer(pBuf, level); pBuf += sizeof(uint32) * 29; ReadDataFromBufferPointer(pBuf, lang); } blocknum++; } int32 checkSum; ReadFromMemCard(file, &checkSum, sizeof(checkSum)); CloseMemCardFile(file); if ( nError != NO_ERR_SUCCESS ) return RES_FAILED; if ( CheckSum == checkSum ) { m_LevelToLoad = level; m_LanguageToLoad = lang; LastBlockSize = lastblocksize; return RES_SUCCESS; } nError = ERR_DATACORRUPTED; return RES_FAILED; } int32 CMemoryCard::GetLanguageToLoad(void) { return m_LanguageToLoad; } int32 CMemoryCard::GetLevelToLoad(void) { return m_LevelToLoad; } bool CMemoryCard::CreateGameDirectoryFromScratch(int32 cardID) { TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); int32 err = RES_SUCCESS; if ( nError != NO_ERR_SUCCESS ) return false; if ( Cards[CurrentCard].free < 500 ) { CTimer::Update(); uint32 startTime = CTimer::GetTimeInMillisecondsPauseMode(); while ( CTimer::GetTimeInMillisecondsPauseMode()-startTime < 1250 ) { for ( int32 i = 0; i < 1000; i++ ) powf(3.33f, 3.444f); CTimer::Update(); } nError = ERR_DIRFULLDEVICE; return false; } TheMemoryCard.ChangeDirectory(CARD_ONE, "/"); if ( nError != NO_ERR_SUCCESS ) return false; int32 r = LookForRootDirectory(CARD_ONE); if ( nError != NO_ERR_SUCCESS ) return false; if ( r == ERR_NOROOTDIR ) { CreateRootDirectory(CARD_ONE); if ( nError != NO_ERR_SUCCESS ) DeleteEverythingInGameRoot(CARD_ONE); } ChangeDirectory(CARD_ONE, Cards[CARD_ONE].dir); if ( nError != NO_ERR_SUCCESS ) return false; PopulateFileTable(CARD_ONE); if ( nError != NO_ERR_SUCCESS ) return false; #if defined(PS2) bool entryExist; entryExist = false; if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS ) { for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ ) { if ( !strcmp("icon.sys", (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) ) { entryExist = true; break; } } } if ( !entryExist ) err = RES_FAILED; if ( nError != NO_ERR_SUCCESS ) return false; entryExist = false; if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS ) { for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ ) { if ( !strcmp(icon_one, (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) ) { entryExist = true; break; } } } if ( !entryExist ) err = RES_FAILED; if ( nError != NO_ERR_SUCCESS ) return false; entryExist = false; if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS ) { for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ ) { if ( !strcmp(icon_two, (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) ) { entryExist = true; break; } } } if ( !entryExist ) err = RES_FAILED; if ( nError != NO_ERR_SUCCESS ) return false; entryExist = false; if ( TheMemoryCard.PopulateFileTable(CARD_ONE) == NO_ERR_SUCCESS ) { for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[CARD_ONE].table); i++ ) { if ( !strcmp(icon_three, (char *)TheMemoryCard.Cards[CARD_ONE].table[i].EntryName) ) { entryExist = true; break; } } } if ( !entryExist ) err = RES_FAILED; if ( nError != NO_ERR_SUCCESS ) return false; if ( err != RES_SUCCESS ) { int32 icon = CreateIconFiles(CARD_ONE, icon_one, icon_two, icon_three); if ( nError != NO_ERR_SUCCESS ) return false; if ( icon != RES_SUCCESS ) DeleteEverythingInGameRoot(CARD_ONE); } #endif int32 guff = FillFirstFileWithGuff(CARD_ONE); if ( nError != NO_ERR_SUCCESS ) return false; if ( guff == RES_SUCCESS ) { printf("Game Default directory present"); return true; } DeleteEverythingInGameRoot(CARD_ONE); return false; } bool CMemoryCard::CheckGameDirectoryThere(int32 cardID) { TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); if ( TheMemoryCard.nError != NO_ERR_SUCCESS ) return false; TheMemoryCard.ChangeDirectory(cardID, Cards[CARD_ONE].dir); if ( TheMemoryCard.nError != NO_ERR_SUCCESS ) return false; PopulateFileTable(cardID); if ( TheMemoryCard.nError != NO_ERR_SUCCESS ) return false; bool entryExist; #if defined(PS2) entryExist = false; if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) { for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) { if ( !strcmp("icon.sys", (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) { entryExist = true; break; } } } if ( !entryExist ) return false; entryExist = false; if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) { for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) { if ( !strcmp(icon_one, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) { entryExist = true; break; } } } if ( !entryExist ) return false; entryExist = false; if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) { for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) { if ( !strcmp(icon_two, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) { entryExist = true; break; } } } if ( !entryExist ) return false; entryExist = false; if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) { for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) { if ( !strcmp(icon_three, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) { entryExist = true; break; } } } if ( !entryExist ) return false; #endif char buff[80]; strncpy(buff, Cards[cardID].dir+1, sizeof(buff) - 1); entryExist = false; if ( TheMemoryCard.PopulateFileTable(cardID) == NO_ERR_SUCCESS ) { for ( int32 i = 0; i < ARRAY_SIZE(TheMemoryCard.Cards[cardID].table); i++ ) { if ( !strcmp(buff, (char *)TheMemoryCard.Cards[cardID].table[i].EntryName) ) { entryExist = true; break; } } } if ( !entryExist ) return false; printf("Game directory present"); return true; } void CMemoryCard::PopulateSlotInfo(int32 cardID) { CTimer::Stop(); for ( int32 i = 0; i < MAX_SLOTS; i++ ) { Slots[i] = SLOT_NOTPRESENT; for ( int32 j = 0; j < ARRAY_SIZE(SlotFileName[i]); j++ ) SlotFileName[i][j] = L'\0'; for ( int32 j = 0; j < ARRAY_SIZE(SlotSaveDate[i]); j++ ) SlotSaveDate[i][j] = L'\0'; UnicodeStrcpy(SlotSaveDate[i], TheText.Get("DEFDT")); } TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); if ( nError != NO_ERR_SUCCESS ) return; TheMemoryCard.ChangeDirectory(cardID, TheMemoryCard.Cards[CARD_ONE].dir); if ( nError != NO_ERR_SUCCESS && nError != ERR_DIRNOENTRY ) return; PopulateFileTable(cardID); if ( nError != NO_ERR_SUCCESS && nError != ERR_FILETABLENOENTRY ) return; for ( int32 slot = 0; slot < MAX_SLOTS; slot++ ) { #if defined(PS2) for ( int32 entry = 7; entry < ARRAY_SIZE(Cards[cardID].table); entry++ ) #else for ( int32 entry = 0; entry < ARRAY_SIZE(Cards[cardID].table); entry++ ) #endif { if ( TheMemoryCard.Cards[CARD_ONE].table[entry].FileSizeByte != 0 ) { char slotnum[30]; char slotname[30]; char slotdate[30]; if ( #if defined(PS2) TheMemoryCard.Cards[CARD_ONE].table[entry].AttrFile & sceMcFileAttrClosed && #endif TheMemoryCard.Cards[CARD_ONE].table[entry].FileSizeByte >= SAVE_FILE_SIZE ) { char *entryname = (char *)Cards[cardID].table[entry].EntryName; bool bFound = false; #if defined(PS2) for ( int32 i = 7; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) #else for ( int32 i = 0; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) #endif { sprintf(slotnum, "%i ", slot+1); for ( int32 j = 0; j < sizeof(slotname); j++ ) slotname[j] = '\0'; strncat(slotname, slotnum, sizeof(slotnum)-1); if ( !strncmp(slotname, entryname, 1) ) { bFound = true; Slots[slot] = SLOT_PRESENT; AsciiToUnicode(entryname, SlotFileName[slot]); int32 sec = Cards[CARD_ONE].table[entry]._Create.Sec; int32 month = Cards[CARD_ONE].table[entry]._Create.Month; int32 year = Cards[CARD_ONE].table[entry]._Create.Year; int32 min = Cards[CARD_ONE].table[entry]._Create.Min; int32 hour = Cards[CARD_ONE].table[entry]._Create.Hour; int32 day = Cards[CARD_ONE].table[entry]._Create.Day; for ( int32 j = 0; j < ARRAY_SIZE(SlotSaveDate[slot]); j++ ) SlotSaveDate[slot][j] = L'\0'; for ( int32 j = 0; j < ARRAY_SIZE(slotdate); j++ ) slotdate[j] = '\0'; char *monthstr; switch ( month ) { case 1: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("JAN")); break; case 2: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("FEB")); break; case 3: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("MAR")); break; case 4: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("APR")); break; case 5: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("MAY")); break; case 6: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("JUN")); break; case 7: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("JUL")); break; case 8: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("AUG")); break; case 9: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("SEP")); break; case 10: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("OCT")); break; case 11: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("NOV")); break; case 12: monthstr = UnicodeToAsciiForMemoryCard(TheText.Get("DEC")); break; } sprintf(slotdate, "%02d %s %04d %02d:%02d:%02d", day, monthstr, year, hour, min, sec); AsciiToUnicode(slotdate, SlotSaveDate[slot]); } } } else { char *entryname = (char *)Cards[cardID].table[entry].EntryName; bool bFound = false; #if defined(PS2) for ( int32 i = 7; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) // again ... #else for ( int32 i = 0; i < ARRAY_SIZE(Cards[cardID].table) && !bFound; i++ ) // again ... #endif { sprintf(slotnum, "%i ", slot+1); for ( int32 j = 0; j < sizeof(slotname); j++ ) slotname[j] = '\0'; strncat(slotname, slotnum, sizeof(slotnum)-1); if ( !strncmp(slotname, entryname, 1) ) { bFound = true; Slots[slot] = SLOT_CORRUPTED; AsciiToUnicode(entryname, SlotFileName[slot]); } } } } } } nError = NO_ERR_SUCCESS; return; } int32 CMemoryCard::GetInfoOnSpecificSlot(int32 slotID) { return Slots[slotID]; } wchar * CMemoryCard::GetDateAndTimeOfSavedGame(int32 slotID) { return SlotSaveDate[slotID]; } int32 CMemoryCard::CheckCardStateAtGameStartUp(int32 cardID) { CheckCardInserted(cardID); if ( nError == ERR_NOFORMAT ) return MCSTATE_OK; if ( nError == ERR_NONE ) return MCSTATE_NOCARD; if ( !CheckGameDirectoryThere(cardID) ) { if ( nError == ERR_NONE ) return MCSTATE_NOCARD; DeleteEverythingInGameRoot(cardID); if ( nError == ERR_NONE ) return MCSTATE_NOCARD; TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); if ( nError == ERR_NONE ) return MCSTATE_NOCARD; if ( Cards[CurrentCard].free < 500 ) return MCSTATE_NEED_500KB; return MCSTATE_OK; } TheMemoryCard.CheckCardInserted(CARD_ONE); if ( nError == NO_ERR_SUCCESS ) { if ( TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CARD_ONE].dir) != ERR_NONE ) { if ( TheMemoryCard.FindMostRecentFileName(CARD_ONE, MostRecentFile) == true ) { if ( TheMemoryCard.CheckDataNotCorrupt(MostRecentFile) == RES_FAILED ) { TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); if ( Cards[CurrentCard].free < 200 ) return MCSTATE_NEED_200KB; } } else { TheMemoryCard.PopulateCardFlags(cardID, false, false, true, true); if ( Cards[CurrentCard].free < 200 ) return MCSTATE_NEED_200KB; } } } if ( TheMemoryCard.CheckCardInserted(CARD_ONE) != NO_ERR_SUCCESS ) return MCSTATE_NOCARD; return MCSTATE_OK; } void CMemoryCard::SaveSlot(int32 slotID) { bool bSave = true; for ( int32 j = 0; j < sizeof(ValidSaveName); j++ ) ValidSaveName[j] = '\0'; char buff[100]; sprintf(buff, "%i ", slotID+1); strncat(ValidSaveName, buff, sizeof(ValidSaveName) - 1); if ( CStats::LastMissionPassedName[0] != '\0' ) { char mission[100]; strcpy(mission, UnicodeToAsciiForMemoryCard(TheText.Get(CStats::LastMissionPassedName))); #ifdef FIX_BUGS strncat(ValidSaveName, mission, sizeof(ValidSaveName)-1); #else strncat(ValidSaveName, mission, 21); strncat(ValidSaveName, "...", strlen("...")); #endif } if ( !CheckGameDirectoryThere(CARD_ONE) ) { DeleteEverythingInGameRoot(CARD_ONE); bSave = CreateGameDirectoryFromScratch(CARD_ONE); } if ( bSave ) { if ( Slots[slotID] == SLOT_PRESENT ) { TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CurrentCard].dir); if ( nError == NO_ERR_SUCCESS ) TheMemoryCard.DeleteMemoryCardFile(CARD_ONE, UnicodeToAsciiForMemoryCard(SlotFileName[slotID])); } SaveGame(); } CTimer::Stop(); CStreaming::FlushRequestList(); CStreaming::DeleteRwObjectsAfterDeath(FindPlayerPed()->GetPosition()); CStreaming::RemoveUnusedModelsInLoadedList(); CGame::DrasticTidyUpMemory(false); CTimer::Update(); } void CMemoryCard::DeleteSlot(int32 slotID) { TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CurrentCard].dir); if ( nError == NO_ERR_SUCCESS ) TheMemoryCard.DeleteMemoryCardFile(CARD_ONE, UnicodeToAsciiForMemoryCard(SlotFileName[slotID])); } void CMemoryCard::LoadSlotToBuffer(int32 slotID) { CStreaming::DeleteAllRwObjects(); strcpy(LoadFileName, UnicodeToAsciiForMemoryCard(SlotFileName[slotID])); TheMemoryCard.ChangeDirectory(CARD_ONE, Cards[CurrentCard].dir); if ( nError == NO_ERR_SUCCESS ) TheMemoryCard.CheckDataNotCorrupt(LoadFileName); } wchar * CMemoryCard::GetNameOfSavedGame(int32 slotID) { return SlotFileName[slotID]; } int32 CMemoryCard::DoClassSaveRoutine(int32 file, uint8 *data, uint32 size) { WritetoMemCard(file, &size, sizeof(size)); if ( nError != NO_ERR_SUCCESS ) { strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); return ERR_NONE; } WritetoMemCard(file, data, align4bytes(size)); uint8 sizebuff[4]; memcpy(sizebuff, &size, sizeof(size)); for ( int32 i = 0; i < ARRAY_SIZE(sizebuff); i++ ) CheckSum += sizebuff[i]; for ( int32 i = 0; i < align4bytes(size); i++ ) CheckSum += *data++; if ( nError != NO_ERR_SUCCESS ) { strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); return ERR_NONE; } return nError; } #endif ================================================ FILE: src/save/MemoryCard.h ================================================ #pragma once #include "common.h" #ifdef PS2_MENU #include "Date.h" #if defined(PS2) #include #include #include #endif enum { CARD_ONE = 0, CARD_TWO, MAX_CARDS, }; class CMemoryCardInfo { public: int port; int slot; int type; int free; int format; char dir[40]; #if defined(PS2) sceMcTblGetDir table[15]; #else struct { typedef struct {unsigned char Sec,Min,Hour; unsigned char Day,Month; unsigned short Year;} _time; _time _Create; _time _Modify; unsigned int FileSizeByte; unsigned short AttrFile; unsigned char EntryName[32]; }table[15]; #endif CMemoryCardInfo(void); }; #define GUFF_FILE_SIZE 147096 #define SAVE_FILE_SIZE 201729 class CMemoryCard { public: enum { MAX_SLOTS = 8, }; enum MCSTATE { MCSTATE_OK = 0, MCSTATE_NEED_500KB, MCSTATE_NEED_200KB, MCSTATE_NOCARD, }; enum SLOTINFO { SLOT_PRESENT = 0, SLOT_NOTPRESENT, SLOT_CORRUPTED, }; int _unk0; int _unk1; bool m_bWantToLoad; bool JustLoadedDontFadeInYet; bool StillToFadeOut; bool b_FoundRecentSavedGameWantToLoad; uint32 TimeStartedCountingForFade; uint32 TimeToStayFadedBeforeFadeOut; uint32 LastBlockSize; bool _bunk2; char ValidSaveName [30]; char MostRecentFile [30]; char _unkName3 [30]; char SaveFileNameJustSaved[30]; char _pad0[3]; wchar *pErrorMsg; char _unk4[32]; bool _bunk5; bool _bunk6; bool _bunk7; bool _bunk8; int nError; wchar _unk9[30]; char LoadFileName[30]; char _pad1[2]; CDate CompileDateAndTime; int m_LanguageToLoad; int m_LevelToLoad; int CurrentCard; CMemoryCardInfo Cards [MAX_CARDS]; int Slots [MAX_SLOTS]; wchar SlotFileName[MAX_SLOTS][30]; wchar SlotSaveDate[MAX_SLOTS][30]; char _unk10[32]; enum { ERR_NONE = 0, ERR_NOFORMAT = 1, ERR_DIRNOENTRY = 2, ERR_OPENNOENTRY = 3, ERR_DELETENOENTRY = 4, ERR_DELETEDENIED = 5, ERR_DELETEFAILED = 6, ERR_WRITEFULLDEVICE = 7, ERR_WRITENOENTRY = 8, ERR_WRITEDENIED = 9, ERR_FLUSHNOENTRY, ERR_WRITEFAILED, ERR_FORMATFAILED = 12, ERR_FILETABLENOENTRY = 13, ERR_DIRFULLDEVICE = 14, ERR_DIRBADENTRY = 15, ERR_FILEFULLDEVICE = 16, ERR_FILENOPATHENTRY = 17, ERR_FILEDENIED = 18, ERR_FILEUPLIMIT = 19, ERR_READNOENTRY = 20, ERR_READDENIED = 21, ERR_LOADFAILED = 22, // unused ERR_SAVEFAILED = 23, ERR_DATACORRUPTED = 24, ERR_NOROOTDIR = 25, NO_ERR_SUCCESS = 26, }; enum { RES_SUCCESS = 1, RES_FAILED = -1, }; int32 GetError() { return nError; } wchar *GetErrorMessage() { return pErrorMsg; } int32 Init(void); CMemoryCard(void); int32 RestoreForStartLoad(void); int32 LoadSavedGame(void); int32 CheckCardInserted(int32 cardID); int32 PopulateCardFlags(int32 cardID, bool bSlotFlag, bool bTypeFlag, bool bFreeFlag, bool bFormatFlag); int32 FormatCard(int32 cardID); int32 PopulateFileTable(int32 cardID); int32 CreateRootDirectory(int32 cardID); int32 ChangeDirectory(int32 cardID, char *dir); int32 CreateIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three); int32 LoadIconFiles(int32 cardID, char *icon_one, char *icon_two, char *icon_three); int32 CloseMemCardFile(int32 file); int32 CreateMemCardFileReadWrite(int32 cardID, char *filename); int32 OpenMemCardFileForReading(int32 cardID, char *filename); int32 ReadFromMemCard(int32 file, void *buff, int32 size); int32 DeleteMemoryCardFile(int32 cardID, char *filename); void PopulateErrorMessage(); int32 WritetoMemCard(int32 file, void *buff, int32 size); bool SaveGame(void); bool DoHackRoundSTUPIDSonyDateTimeStuff(int32 port, char *filename); int32 LookForRootDirectory(int32 cardID); int32 FillFirstFileWithGuff(int32 cardID); bool FindMostRecentFileName(int32 cardID, char *filename); void ClearFileTableBuffer(int32 cardID); int32 GetClusterAmountForFileCreation(int32 port); bool DeleteEverythingInGameRoot(int32 cardID); int32 CheckDataNotCorrupt(char *filename); int32 GetLanguageToLoad(void); int32 GetLevelToLoad(void); bool CreateGameDirectoryFromScratch(int32 cardID); bool CheckGameDirectoryThere(int32 cardID); void PopulateSlotInfo(int32 cardID); int32 GetInfoOnSpecificSlot(int32 slotID); wchar *GetDateAndTimeOfSavedGame(int32 slotID); int32 CheckCardStateAtGameStartUp(int32 cardID); void SaveSlot(int32 slotID); void DeleteSlot(int32 slotID); void LoadSlotToBuffer(int32 slotID); wchar *GetNameOfSavedGame(int32 slotID); int32 DoClassSaveRoutine(int32 file, uint8 *data, uint32 size); }; extern CMemoryCard TheMemoryCard; #endif ================================================ FILE: src/save/PCSave.cpp ================================================ #define WITHWINDOWS #include "common.h" #include "crossplatform.h" #include "FileMgr.h" #include "Font.h" #ifdef MORE_LANGUAGES #include "Game.h" #endif #include "GenericGameStorage.h" #include "Messages.h" #include "PCSave.h" #include "Text.h" const char* _psGetUserFilesFolder(); C_PcSave PcSaveHelper; void C_PcSave::SetSaveDirectory(const char *path) { sprintf(DefaultPCSaveFileName, "%s\\%s", path, "GTAVCsf"); } bool C_PcSave::DeleteSlot(int32 slot) { char FileName[200]; PcSaveHelper.nErrorCode = SAVESTATUS_SUCCESSFUL; sprintf(FileName, "%s%i.b", DefaultPCSaveFileName, slot + 1); DeleteFile(FileName); SlotSaveDate[slot][0] = '\0'; return true; } int8 C_PcSave::SaveSlot(int32 slot) { MakeValidSaveName(slot); PcSaveHelper.nErrorCode = SAVESTATUS_SUCCESSFUL; _psGetUserFilesFolder(); int file = CFileMgr::OpenFile(ValidSaveName, "wb"); if (file != 0) { #ifdef MISSION_REPLAY if (!IsQuickSave) #endif DoGameSpecificStuffBeforeSave(); if (GenericSave(file)) { if (!!CFileMgr::CloseFile(file)) nErrorCode = SAVESTATUS_ERR_SAVE_CLOSE; return 0; } return 2; } PcSaveHelper.nErrorCode = SAVESTATUS_ERR_SAVE_CREATE; return false; } bool C_PcSave::PcClassSaveRoutine(int32 file, uint8 *data, uint32 size) { CFileMgr::Write(file, (const char*)&size, sizeof(size)); if (CFileMgr::GetErrorReadWrite(file)) { nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); return false; } CFileMgr::Write(file, (const char*)data, align4bytes(size)); CheckSum += (uint8) size; CheckSum += (uint8) (size >> 8); CheckSum += (uint8) (size >> 16); CheckSum += (uint8) (size >> 24); for (int i = 0; i < align4bytes(size); i++) { CheckSum += *data++; } if (CFileMgr::GetErrorReadWrite(file)) { nErrorCode = SAVESTATUS_ERR_SAVE_WRITE; strncpy(SaveFileNameJustSaved, ValidSaveName, sizeof(ValidSaveName) - 1); return false; } return true; } void C_PcSave::PopulateSlotInfo() { for (int i = 0; i < SLOT_COUNT; i++) { Slots[i] = SLOT_EMPTY; SlotFileName[i][0] = '\0'; SlotSaveDate[i][0] = '\0'; } for (int i = 0; i < SLOT_COUNT; i++) { #ifdef FIX_BUGS char savename[MAX_PATH]; #else char savename[52]; #endif struct { int size; wchar FileName[24]; SYSTEMTIME SaveDateTime; } header; sprintf(savename, "%s%i%s", DefaultPCSaveFileName, i + 1, ".b"); int file = CFileMgr::OpenFile(savename, "rb"); if (file != 0) { CFileMgr::Read(file, (char*)&header, sizeof(header)); if (strncmp((char*)&header, TopLineEmptyFile, sizeof(TopLineEmptyFile)-1) != 0) { Slots[i] = SLOT_OK; memcpy(SlotFileName[i], &header.FileName, sizeof(header.FileName)); SlotFileName[i][24] = '\0'; } CFileMgr::CloseFile(file); } if (Slots[i] == SLOT_OK) { if (CheckDataNotCorrupt(i, savename)) { SYSTEMTIME st; memcpy(&st, &header.SaveDateTime, sizeof(SYSTEMTIME)); const char *month; switch (st.wMonth) { case 1: month = "JAN"; break; case 2: month = "FEB"; break; case 3: month = "MAR"; break; case 4: month = "APR"; break; case 5: month = "MAY"; break; case 6: month = "JUN"; break; case 7: month = "JUL"; break; case 8: month = "AUG"; break; case 9: month = "SEP"; break; case 10: month = "OCT"; break; case 11: month = "NOV"; break; case 12: month = "DEC"; break; default: assert(0); } char date[70]; #ifdef MORE_LANGUAGES if (CGame::japaneseGame) sprintf(date, "%02d %02d %04d %02d:%02d:%02d", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond); else #endif // MORE_LANGUAGES sprintf(date, "%02d %s %04d %02d:%02d:%02d", st.wDay, UnicodeToAsciiForSaveLoad(TheText.Get(month)), st.wYear, st.wHour, st.wMinute, st.wSecond); AsciiToUnicode(date, SlotSaveDate[i]); } else { CMessages::InsertNumberInString(TheText.Get("FEC_SLC"), i + 1, -1, -1, -1, -1, -1, SlotFileName[i]); Slots[i] = SLOT_CORRUPTED; } } } } ================================================ FILE: src/save/PCSave.h ================================================ #pragma once enum eSaveStatus { SAVESTATUS_SUCCESSFUL = 0, SAVESTATUS_ERR_SAVE_CREATE, SAVESTATUS_ERR_SAVE_WRITE, SAVESTATUS_ERR_SAVE_CLOSE, SAVESTATUS_ERR_LOAD_OPEN, SAVESTATUS_ERR_LOAD_READ, SAVESTATUS_ERR_LOAD_CLOSE, SAVESTATUS_ERR_DATA_INVALID, // unused SAVESTATUS_DELETEFAILED8, SAVESTATUS_DELETEFAILED9, SAVESTATUS_DELETEFAILED10, }; enum { SLOT_OK = 0, SLOT_EMPTY, SLOT_CORRUPTED }; class C_PcSave { public: eSaveStatus nErrorCode; C_PcSave() : nErrorCode(SAVESTATUS_SUCCESSFUL) {} void PopulateSlotInfo(); bool DeleteSlot(int32 slot); int8 SaveSlot(int32 slot); bool PcClassSaveRoutine(int32 a2, uint8 *data, uint32 size); static void SetSaveDirectory(const char *path); }; extern C_PcSave PcSaveHelper; ================================================ FILE: src/skel/crossplatform.cpp ================================================ #include "common.h" #include "crossplatform.h" // Codes compatible with Windows and Linux #ifndef _WIN32 // For internal use // wMilliseconds is not needed void tmToSystemTime(const tm *tm, SYSTEMTIME *out) { out->wYear = tm->tm_year + 1900; out->wMonth = tm->tm_mon + 1; out->wDayOfWeek = tm->tm_wday; out->wDay = tm->tm_mday; out->wHour = tm->tm_hour; out->wMinute = tm->tm_min; out->wSecond = tm->tm_sec; } void GetLocalTime_CP(SYSTEMTIME *out) { time_t timestamp = time(nil); tm *localTm = localtime(×tamp); tmToSystemTime(localTm, out); } #endif // Compatible with Linux/POSIX and MinGW on Windows #ifndef _WIN32 HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) { char pathCopy[MAX_PATH]; strcpy(pathCopy, pathname); char *folder = strtok(pathCopy, "*"); char *extension = strtok(NULL, "*"); // because I remember like strtok might not return NULL for last delimiter if (extension && extension - folder == strlen(pathname)) extension = nil; // Case-sensitivity and backslashes... // Will be freed at the bottom char *realFolder = casepath(folder); if (realFolder) { folder = realFolder; } strncpy(firstfile->folder, folder, sizeof(firstfile->folder)); if (extension) strncpy(firstfile->extension, extension, sizeof(firstfile->extension)); else firstfile->extension[0] = '\0'; if (realFolder) free(realFolder); HANDLE d; if ((d = (HANDLE)opendir(firstfile->folder)) == NULL || !FindNextFile(d, firstfile)) return NULL; return d; } bool FindNextFile(HANDLE d, WIN32_FIND_DATA* finddata) { dirent *file; static struct stat fileStats; static char path[PATH_MAX], relativepath[NAME_MAX + sizeof(finddata->folder) + 1]; int extensionLen = strlen(finddata->extension); while ((file = readdir((DIR*)d)) != NULL) { // We only want "DT_REG"ular Files, but reportedly some FS and OSes gives DT_UNKNOWN as type. if ((file->d_type == DT_UNKNOWN || file->d_type == DT_REG || file->d_type == DT_LNK) && (extensionLen == 0 || strncasecmp(&file->d_name[strlen(file->d_name) - extensionLen], finddata->extension, extensionLen) == 0)) { sprintf(relativepath, "%s/%s", finddata->folder, file->d_name); realpath(relativepath, path); stat(path, &fileStats); strncpy(finddata->cFileName, file->d_name, sizeof(finddata->cFileName)); finddata->ftLastWriteTime = fileStats.st_mtime; return true; } } return false; } void GetDateFormat(int unused1, int unused2, SYSTEMTIME* in, int unused3, char* out, int size) { tm linuxTime; linuxTime.tm_year = in->wYear - 1900; linuxTime.tm_mon = in->wMonth - 1; linuxTime.tm_wday = in->wDayOfWeek; linuxTime.tm_mday = in->wDay; linuxTime.tm_hour = in->wHour; linuxTime.tm_min = in->wMinute; linuxTime.tm_sec = in->wSecond; strftime(out, size, nl_langinfo(D_FMT), &linuxTime); } void FileTimeToSystemTime(time_t* writeTime, SYSTEMTIME* out) { tm *ptm = gmtime(writeTime); tmToSystemTime(ptm, out); } #endif // Because wchar length differs between platforms. wchar* AllocUnicode(const char* src) { wchar *dst = (wchar*)malloc(strlen(src)*2 + 2); wchar *i = dst; while((*i++ = (unsigned char)*src++) != '\0'); return dst; } // Funcs/features from Windows that we need on other platforms #ifndef _WIN32 char *strupr(char *s) { char* tmp = s; for (;*tmp;++tmp) { *tmp = toupper((unsigned char) *tmp); } return s; } char *strlwr(char *s) { char* tmp = s; for (;*tmp;++tmp) { *tmp = tolower((unsigned char) *tmp); } return s; } char *trim(char *s) { char *ptr; if (!s) return NULL; // handle NULL string if (!*s) return s; // handle empty string for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr); ptr[1] = '\0'; return s; } FILE* _fcaseopen(char const* filename, char const* mode) { FILE* result; char* real = casepath(filename); if (!real) result = fopen(filename, mode); else { result = fopen(real, mode); free(real); } return result; } // Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen) // Returned string should freed manually (if exists) char* casepath(char const* path, bool checkPathFirst) { if (checkPathFirst && access(path, F_OK) != -1) { // File path is correct return nil; } size_t l = strlen(path); char* p = (char*)alloca(l + 1); char* out = (char*)malloc(l + 3); // for extra ./ strcpy(p, path); // my addon: linux doesn't handle filenames with spaces at the end nicely p = trim(p); size_t rl = 0; DIR* d; if (p[0] == '/' || p[0] == '\\') { d = opendir("/"); } else { d = opendir("."); out[0] = '.'; out[1] = 0; rl = 1; } bool cantProceed = false; // just convert slashes in what's left in string, don't correct case of letters(because we can't) bool mayBeTrailingSlash = false; char* c; while (c = strsep(&p, "/\\")) { // May be trailing slash(allow), slash at the start(avoid), or multiple slashes(avoid) if (*c == '\0') { mayBeTrailingSlash = true; continue; } else { mayBeTrailingSlash = false; } out[rl] = '/'; rl += 1; out[rl] = 0; if (cantProceed) { strcpy(out + rl, c); rl += strlen(c); continue; } struct dirent* e; while (e = readdir(d)) { if (strcasecmp(c, e->d_name) == 0) { strcpy(out + rl, e->d_name); int reportedLen = (int)strlen(e->d_name); rl += reportedLen; assert(reportedLen == strlen(c) && "casepath: This is not good at all"); closedir(d); d = opendir(out); // Either it wasn't a folder, or permission error, I/O error etc. if (!d) { cantProceed = true; } break; } } if (!e) { printf("casepath couldn't find dir/file \"%s\", full path was %s\n", c, path); // No match, add original name and continue converting further slashes. strcpy(out + rl, c); rl += strlen(c); cantProceed = true; } } if (d) closedir(d); if (mayBeTrailingSlash) { out[rl] = '/'; rl += 1; out[rl] = '\0'; } if (rl > l + 2) { printf("\n\ncasepath: Corrected path length is longer then original+2:\n\tOriginal: %s (%zu chars)\n\tCorrected: %s (%zu chars)\n\n", path, l, out, rl); } return out; } #endif #if !defined(_MSC_VER) && !defined(__CWCC__) char *strdate(char *buf) { time_t timestamp; time(×tamp); tm *localTm = localtime(×tamp); strftime(buf, 10, "%m/%d/%y", localTm); return buf; } char *_strdate(char *buf) { return strdate(buf); } #endif ================================================ FILE: src/skel/crossplatform.h ================================================ #include // This is the common include for platform/renderer specific skeletons(glfw.cpp, win.cpp etc.) and using cross platform things (like Windows directories wrapper, platform specific global arrays etc.) // Functions that's different on glfw and win but have same signature, should be located on platform.h. enum eWinVersion { OS_WIN95 = 0, OS_WIN98, OS_WINNT, OS_WIN2000, OS_WINXP, }; #if !defined(_MSC_VER) && !defined(__CWCC__) char *_strdate(char *buf); #endif #ifdef _WIN32 // As long as WITHWINDOWS isn't defined / isn't included, we only need type definitions so let's include . // NOTE: It's perfectly fine to include here, but it can increase build size and time in *some* conditions, and maybe substantially in future if we'll use crossplatform.h more. #ifndef _INC_WINDOWS #ifndef __MWERKS__ #include #else #include #endif #endif #if defined RW_D3D9 || defined RWLIBS #include "win.h" #endif extern DWORD _dwOperatingSystemVersion; #define fcaseopen fopen #else char *strupr(char *str); char *strlwr(char *str); enum { LANG_OTHER, LANG_GERMAN, LANG_FRENCH, LANG_ENGLISH, LANG_ITALIAN, LANG_SPANISH, }; enum { SUBLANG_OTHER, SUBLANG_ENGLISH_AUS }; extern long _dwOperatingSystemVersion; char *casepath(char const *path, bool checkPathFirst = true); FILE *_fcaseopen(char const *filename, char const *mode); #define fcaseopen _fcaseopen #endif #ifdef RW_GL3 typedef struct { GLFWwindow* window; RwBool fullScreen; RwV2d lastMousePos; double mouseWheel; // glfw doesn't cache it bool cursorIsInWindow; RwInt8 joy1id; RwInt8 joy2id; } psGlobalType; #define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) void CapturePad(RwInt32 padID); void joysChangeCB(int jid, int event); #endif #ifdef DETECT_JOYSTICK_MENU extern char gSelectedJoystickName[128]; #endif enum eGameState { GS_START_UP = 0, GS_INIT_LOGO_MPEG, GS_LOGO_MPEG, GS_INIT_INTRO_MPEG, GS_INTRO_MPEG, GS_INIT_ONCE, GS_INIT_FRONTEND, GS_FRONTEND, GS_INIT_PLAYING_GAME, GS_PLAYING_GAME, }; extern RwUInt32 gGameState; RwBool IsForegroundApp(); #ifndef MAX_PATH #if !defined _WIN32 || defined __MINGW32__ #define MAX_PATH PATH_MAX #else #define MAX_PATH 260 #endif #endif // Codes compatible with Windows and Linux #ifndef _WIN32 #define DeleteFile unlink // Needed for save games struct SYSTEMTIME { RwUInt16 wYear; RwUInt16 wMonth; RwUInt16 wDayOfWeek; RwUInt16 wDay; RwUInt16 wHour; RwUInt16 wMinute; RwUInt16 wSecond; RwUInt16 wMilliseconds; }; void GetLocalTime_CP(SYSTEMTIME* out); #define GetLocalTime GetLocalTime_CP #define OutputDebugString(s) re3_debug("[DBG-2]: %s\n",s) #endif // Compatible with Linux/POSIX and MinGW on Windows #ifndef _WIN32 #include #include #include #include #include #include typedef void* HANDLE; #define INVALID_HANDLE_VALUE NULL #define FindClose(h) \ do { \ if (h != nil) \ closedir((DIR*)h); \ } while(0) #define LOCALE_USER_DEFAULT 0 #define DATE_SHORTDATE 0 struct WIN32_FIND_DATA { char extension[32]; // for searching char folder[32]; // for searching char cFileName[256]; // because tSkinInfo has it 256 time_t ftLastWriteTime; }; HANDLE FindFirstFile(const char*, WIN32_FIND_DATA*); bool FindNextFile(HANDLE, WIN32_FIND_DATA*); void FileTimeToSystemTime(time_t*, SYSTEMTIME*); void GetDateFormat(int, int, SYSTEMTIME*, int, char*, int); #endif ================================================ FILE: src/skel/events.cpp ================================================ #include "common.h" #include "Pad.h" #include "ControllerConfig.h" #include "Frontend.h" #include "Camera.h" #include "rwcore.h" #include "skeleton.h" #include "events.h" /* ***************************************************************************** */ static RsEventStatus HandleKeyDown(RsKeyStatus *keyStatus) { CPad *pad0 = CPad::GetPad(0); CPad *pad1 = CPad::GetPad(1); RwInt32 c = keyStatus->keyCharCode; if ( c != rsNULL ) { switch (c) { case rsESC: { CPad::TempKeyState.ESC = 255; break; } case rsINS: { CPad::TempKeyState.INS = 255; break; } case rsDEL: { CPad::TempKeyState.DEL = 255; break; } case rsHOME: { CPad::TempKeyState.HOME = 255; break; } case rsEND: { CPad::TempKeyState.END = 255; break; } case rsPGUP: { CPad::TempKeyState.PGUP = 255; break; } case rsPGDN: { CPad::TempKeyState.PGDN = 255; break; } case rsUP: { CPad::TempKeyState.UP = 255; break; } case rsDOWN: { CPad::TempKeyState.DOWN = 255; break; } case rsLEFT: { CPad::TempKeyState.LEFT = 255; break; } case rsRIGHT: { CPad::TempKeyState.RIGHT = 255; break; } case rsNUMLOCK: { CPad::TempKeyState.NUMLOCK = 255; break; } case rsPADDEL: { CPad::TempKeyState.DECIMAL = 255; break; } case rsPADEND: { CPad::TempKeyState.NUM1 = 255; break; } case rsPADDOWN: { CPad::TempKeyState.NUM2 = 255; break; } case rsPADPGDN: { CPad::TempKeyState.NUM3 = 255; break; } case rsPADLEFT: { CPad::TempKeyState.NUM4 = 255; break; } case rsPAD5: { CPad::TempKeyState.NUM5 = 255; break; } case rsPADRIGHT: { CPad::TempKeyState.NUM6 = 255; break; } case rsPADHOME: { CPad::TempKeyState.NUM7 = 255; break; } case rsPADUP: { CPad::TempKeyState.NUM8 = 255; break; } case rsPADPGUP: { CPad::TempKeyState.NUM9 = 255; break; } case rsPADINS: { CPad::TempKeyState.NUM0 = 255; break; } case rsDIVIDE: { CPad::TempKeyState.DIV = 255; break; } case rsTIMES: { CPad::TempKeyState.MUL = 255; break; } case rsMINUS: { CPad::TempKeyState.SUB = 255; break; } case rsPADENTER: { CPad::TempKeyState.ENTER = 255; break; } case rsPLUS: { CPad::TempKeyState.ADD = 255; break; } case rsENTER: { CPad::TempKeyState.EXTENTER = 255; break; } case rsSCROLL: { CPad::TempKeyState.SCROLLLOCK = 255; break; } case rsPAUSE: { CPad::TempKeyState.PAUSE = 255; break; } case rsBACKSP: { CPad::TempKeyState.BACKSP = 255; break; } case rsTAB: { CPad::TempKeyState.TAB = 255; break; } case rsCAPSLK: { CPad::TempKeyState.CAPSLOCK = 255; break; } case rsLSHIFT: { CPad::TempKeyState.LSHIFT = 255; break; } case rsSHIFT: { CPad::TempKeyState.SHIFT = 255; break; } case rsRSHIFT: { CPad::TempKeyState.RSHIFT = 255; break; } case rsLCTRL: { CPad::TempKeyState.LCTRL = 255; break; } case rsRCTRL: { CPad::TempKeyState.RCTRL = 255; break; } case rsLALT: { CPad::TempKeyState.LALT = 255; break; } case rsRALT: { CPad::TempKeyState.RALT = 255; break; } case rsLWIN: { CPad::TempKeyState.LWIN = 255; break; } case rsRWIN: { CPad::TempKeyState.RWIN = 255; break; } case rsAPPS: { CPad::TempKeyState.APPS = 255; break; } case rsF1: case rsF2: case rsF3: case rsF4: case rsF5: case rsF6: case rsF7: case rsF8: case rsF9: case rsF10: case rsF11: case rsF12: { CPad::TempKeyState.F[c - rsF1] = 255; break; } default: { if ( c < 255 ) { CPad::TempKeyState.VK_KEYS[c] = 255; pad0->AddToPCCheatString(c); } break; } } if ( CPad::m_bMapPadOneToPadTwo ) { if ( c == 'D' ) pad1->PCTempKeyState.LeftStickX = 128; if ( c == 'A' ) pad1->PCTempKeyState.LeftStickX = -128; if ( c == 'W' ) pad1->PCTempKeyState.LeftStickY = 128; if ( c == 'S' ) pad1->PCTempKeyState.LeftStickY = -128; if ( c == 'J' ) pad1->PCTempKeyState.RightStickX = 128; if ( c == 'G' ) pad1->PCTempKeyState.RightStickX = -128; if ( c == 'Y' ) pad1->PCTempKeyState.RightStickY = 128; if ( c == 'H' ) pad1->PCTempKeyState.RightStickY = -128; if ( c == 'Z' ) pad1->PCTempKeyState.LeftShoulder1 = 255; if ( c == 'X' ) pad1->PCTempKeyState.LeftShoulder2 = 255; if ( c == 'C' ) pad1->PCTempKeyState.RightShoulder1 = 255; if ( c == 'V' ) pad1->PCTempKeyState.RightShoulder2 = 255; if ( c == 'O' ) pad1->PCTempKeyState.DPadUp = 255; if ( c == 'L' ) pad1->PCTempKeyState.DPadDown = 255; if ( c == 'K' ) pad1->PCTempKeyState.DPadLeft = 255; if ( c == ';' ) pad1->PCTempKeyState.DPadRight = 255; if ( c == 'B' ) pad1->PCTempKeyState.Start = 255; if ( c == 'N' ) pad1->PCTempKeyState.Select = 255; if ( c == 'M' ) pad1->PCTempKeyState.Square = 255; if ( c == ',' ) pad1->PCTempKeyState.Triangle = 255; if ( c == '.' ) pad1->PCTempKeyState.Cross = 255; if ( c == '/' ) pad1->PCTempKeyState.Circle = 255; if ( c == rsRSHIFT ) pad1->PCTempKeyState.LeftShock = 255; if ( c == rsRCTRL ) pad1->PCTempKeyState.RightShock = 255; } } return rsEVENTPROCESSED; } static RsEventStatus HandleKeyUp(RsKeyStatus *keyStatus) { CPad *pad0 = CPad::GetPad(0); CPad *pad1 = CPad::GetPad(1); RwInt32 c = keyStatus->keyCharCode; if ( c != rsNULL ) { switch (c) { case rsESC: { CPad::TempKeyState.ESC = 0; break; } case rsINS: { CPad::TempKeyState.INS = 0; break; } case rsDEL: { CPad::TempKeyState.DEL = 0; break; } case rsHOME: { CPad::TempKeyState.HOME = 0; break; } case rsEND: { CPad::TempKeyState.END = 0; break; } case rsPGUP: { CPad::TempKeyState.PGUP = 0; break; } case rsPGDN: { CPad::TempKeyState.PGDN = 0; break; } case rsUP: { CPad::TempKeyState.UP = 0; break; } case rsDOWN: { CPad::TempKeyState.DOWN = 0; break; } case rsLEFT: { CPad::TempKeyState.LEFT = 0; break; } case rsRIGHT: { CPad::TempKeyState.RIGHT = 0; break; } case rsNUMLOCK: { CPad::TempKeyState.NUMLOCK = 0; break; } case rsPADDEL: { CPad::TempKeyState.DECIMAL = 0; break; } case rsPADEND: { CPad::TempKeyState.NUM1 = 0; break; } case rsPADDOWN: { CPad::TempKeyState.NUM2 = 0; break; } case rsPADPGDN: { CPad::TempKeyState.NUM3 = 0; break; } case rsPADLEFT: { CPad::TempKeyState.NUM4 = 0; break; } case rsPAD5: { CPad::TempKeyState.NUM5 = 0; break; } case rsPADRIGHT: { CPad::TempKeyState.NUM6 = 0; break; } case rsPADHOME: { CPad::TempKeyState.NUM7 = 0; break; } case rsPADUP: { CPad::TempKeyState.NUM8 = 0; break; } case rsPADPGUP: { CPad::TempKeyState.NUM9 = 0; break; } case rsPADINS: { CPad::TempKeyState.NUM0 = 0; break; } case rsDIVIDE: { CPad::TempKeyState.DIV = 0; break; } case rsTIMES: { CPad::TempKeyState.MUL = 0; break; } case rsMINUS: { CPad::TempKeyState.SUB = 0; break; } case rsPADENTER: { CPad::TempKeyState.ENTER = 0; break; } case rsPLUS: { CPad::TempKeyState.ADD = 0; break; } case rsENTER: { CPad::TempKeyState.EXTENTER = 0; break; } case rsSCROLL: { CPad::TempKeyState.SCROLLLOCK = 0; break; } case rsPAUSE: { CPad::TempKeyState.PAUSE = 0; break; } case rsBACKSP: { CPad::TempKeyState.BACKSP = 0; break; } case rsTAB: { CPad::TempKeyState.TAB = 0; break; } case rsCAPSLK: { CPad::TempKeyState.CAPSLOCK = 0; break; } case rsLSHIFT: { CPad::TempKeyState.LSHIFT = 0; break; } case rsSHIFT: { CPad::TempKeyState.SHIFT = 0; break; } case rsRSHIFT: { CPad::TempKeyState.RSHIFT = 0; break; } case rsLCTRL: { CPad::TempKeyState.LCTRL = 0; break; } case rsRCTRL: { CPad::TempKeyState.RCTRL = 0; break; } case rsLALT: { CPad::TempKeyState.LALT = 0; break; } case rsRALT: { CPad::TempKeyState.RALT = 0; break; } case rsLWIN: { CPad::TempKeyState.LWIN = 0; break; } case rsRWIN: { CPad::TempKeyState.RWIN = 0; break; } case rsAPPS: { CPad::TempKeyState.APPS = 0; break; } case rsF1: case rsF2: case rsF3: case rsF4: case rsF5: case rsF6: case rsF7: case rsF8: case rsF9: case rsF10: case rsF11: case rsF12: { CPad::TempKeyState.F[c - rsF1] = 0; break; } default: { if ( c < 255 ) { CPad::TempKeyState.VK_KEYS[c] = 0; } break; } } if ( CPad::m_bMapPadOneToPadTwo ) { if ( c == 'D' ) pad1->PCTempKeyState.LeftStickX = 0; if ( c == 'A' ) pad1->PCTempKeyState.LeftStickX = 0; if ( c == 'W' ) pad1->PCTempKeyState.LeftStickY = 0; if ( c == 'S' ) pad1->PCTempKeyState.LeftStickY = 0; if ( c == 'J' ) pad1->PCTempKeyState.RightStickX = 0; if ( c == 'G' ) pad1->PCTempKeyState.RightStickX = 0; if ( c == 'Y' ) pad1->PCTempKeyState.RightStickY = 0; if ( c == 'H' ) pad1->PCTempKeyState.RightStickY = 0; if ( c == 'Z' ) pad1->PCTempKeyState.LeftShoulder1 = 0; if ( c == 'X' ) pad1->PCTempKeyState.LeftShoulder2 = 0; if ( c == 'C' ) pad1->PCTempKeyState.RightShoulder1 = 0; if ( c == 'V' ) pad1->PCTempKeyState.RightShoulder2 = 0; if ( c == 'O' ) pad1->PCTempKeyState.DPadUp = 0; if ( c == 'L' ) pad1->PCTempKeyState.DPadDown = 0; if ( c == 'K' ) pad1->PCTempKeyState.DPadLeft = 0; if ( c == ';' ) pad1->PCTempKeyState.DPadRight = 0; if ( c == 'B' ) pad1->PCTempKeyState.Start = 0; if ( c == 'N' ) pad1->PCTempKeyState.Select = 0; if ( c == 'M' ) pad1->PCTempKeyState.Square = 0; if ( c == ',' ) pad1->PCTempKeyState.Triangle = 0; if ( c == '.' ) pad1->PCTempKeyState.Cross = 0; if ( c == '/' ) pad1->PCTempKeyState.Circle = 0; if ( c == rsRSHIFT ) pad1->PCTempKeyState.LeftShock = 0; if ( c == rsRCTRL ) pad1->PCTempKeyState.RightShock = 0; } } return rsEVENTPROCESSED; } /* ***************************************************************************** */ static RsEventStatus KeyboardHandler(RsEvent event, void *param) { /* * ...then the application events, if necessary... */ switch( event ) { case rsKEYDOWN: { return HandleKeyDown((RsKeyStatus *)param); } case rsKEYUP: { return HandleKeyUp((RsKeyStatus *)param); } default: { return rsEVENTNOTPROCESSED; } } } /* ***************************************************************************** */ static RsEventStatus HandlePadButtonDown(RsPadButtonStatus *padButtonStatus) { bool bPadTwo = false; int32 padNumber = padButtonStatus->padID; CPad *pad = CPad::GetPad(padNumber); if ( CPad::m_bMapPadOneToPadTwo ) padNumber = 1; if ( padNumber == 1 ) bPadTwo = true; ControlsManager.UpdateJoyButtonState(padNumber); for ( int32 i = 0; i < _TODOCONST(16); i++ ) { RsPadButtons btn = rsPADNULL; if ( ControlsManager.m_aButtonStates[i] == TRUE ) btn = (RsPadButtons)(i + 1); if ( FrontEndMenuManager.m_bMenuActive || bPadTwo ) ControlsManager.UpdateJoyInConfigMenus_ButtonDown(btn, padNumber); else ControlsManager.AffectControllerStateOn_ButtonDown(btn, JOYSTICK); } return rsEVENTPROCESSED; } /* ***************************************************************************** */ static RsEventStatus HandlePadButtonUp(RsPadButtonStatus *padButtonStatus) { bool bPadTwo = false; int32 padNumber = padButtonStatus->padID; CPad *pad = CPad::GetPad(padNumber); if ( CPad::m_bMapPadOneToPadTwo ) padNumber = 1; if ( padNumber == 1 ) bPadTwo = true; bool bCam = false; int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if ( mode == CCam::MODE_FLYBY || mode == CCam::MODE_FIXED ) bCam = true; ControlsManager.UpdateJoyButtonState(padNumber); for ( int32 i = 1; i < _TODOCONST(16); i++ ) { RsPadButtons btn = rsPADNULL; if ( ControlsManager.m_aButtonStates[i] == FALSE ) btn = (RsPadButtons)(i + 1); // bug ?, cycle begins from 1(not zero), 1+1==2==rsPADBUTTON2, so we skip rsPADBUTTON1, right ? if ( FrontEndMenuManager.m_bMenuActive || bPadTwo || bCam ) ControlsManager.UpdateJoyInConfigMenus_ButtonUp(btn, padNumber); else ControlsManager.AffectControllerStateOn_ButtonUp(btn, JOYSTICK); } return rsEVENTPROCESSED; } /* ***************************************************************************** */ static RsEventStatus PadHandler(RsEvent event, void *param) { switch( event ) { case rsPADBUTTONDOWN: { return HandlePadButtonDown((RsPadButtonStatus *)param); } case rsPADBUTTONUP: { return HandlePadButtonUp((RsPadButtonStatus *)param); } default: { return rsEVENTNOTPROCESSED; } } } /* ***************************************************************************** */ RwBool AttachInputDevices(void) { RsInputDeviceAttach(rsKEYBOARD, KeyboardHandler); RsInputDeviceAttach(rsPAD, PadHandler); return TRUE; } ================================================ FILE: src/skel/events.h ================================================ #ifndef EVENTS_H #define EVENTS_H #include #include "skeleton.h" #endif /* EVENTS_H */ ================================================ FILE: src/skel/glfw/glfw.cpp ================================================ #if defined RW_GL3 && !defined LIBRW_SDL2 #ifdef _WIN32 #include #include #include #include #include #include DWORD _dwOperatingSystemVersion; #include "resource.h" #else long _dwOperatingSystemVersion; #ifndef __APPLE__ #include #else #include #include #endif #include #include #include #include #endif #include "common.h" #if (defined(_MSC_VER)) #include #endif /* (defined(_MSC_VER)) */ #include #include "rwcore.h" #include "skeleton.h" #include "platform.h" #include "crossplatform.h" #include "main.h" #include "FileMgr.h" #include "Text.h" #include "Pad.h" #include "Timer.h" #include "DMAudio.h" #include "ControllerConfig.h" #include "Frontend.h" #include "Game.h" #include "PCSave.h" #include "MemoryCard.h" #include "Sprite2d.h" #include "AnimViewer.h" #include "Font.h" #include "MemoryMgr.h" // We found out that GLFW's keyboard input handling is still pretty delayed/not stable, so now we fetch input from X11 directly on Linux. #if !defined _WIN32 && !defined __APPLE__ && !defined __SWITCH__ // && !defined WAYLAND #define GET_KEYBOARD_INPUT_FROM_X11 #endif #ifdef GET_KEYBOARD_INPUT_FROM_X11 #include #include #define GLFW_EXPOSE_NATIVE_X11 #include #endif #ifdef _WIN32 #define GLFW_EXPOSE_NATIVE_WIN32 #include #endif #define MAX_SUBSYSTEMS (16) rw::EngineOpenParams openParams; static RwBool ForegroundApp = TRUE; static RwBool WindowIconified = FALSE; static RwBool WindowFocused = TRUE; static RwBool RwInitialised = FALSE; static RwSubSystemInfo GsubSysInfo[MAX_SUBSYSTEMS]; static RwInt32 GnumSubSystems = 0; static RwInt32 GcurSel = 0, GcurSelVM = 0; static RwBool useDefault; // What is that for anyway? #ifndef IMPROVED_VIDEOMODE static RwBool defaultFullscreenRes = TRUE; #else static RwBool defaultFullscreenRes = FALSE; static RwInt32 bestWndMode = -1; #endif static psGlobalType PsGlobal; #define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) size_t _dwMemAvailPhys; RwUInt32 gGameState; #ifdef DETECT_JOYSTICK_MENU char gSelectedJoystickName[128] = ""; #endif /* ***************************************************************************** */ void _psCreateFolder(const char *path) { #ifdef _WIN32 HANDLE hfle = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, nil); if ( hfle == INVALID_HANDLE_VALUE ) CreateDirectory(path, nil); else CloseHandle(hfle); #else struct stat info; char fullpath[PATH_MAX]; realpath(path, fullpath); if (lstat(fullpath, &info) != 0) { if (errno == ENOENT || (errno != EACCES && !S_ISDIR(info.st_mode))) { mkdir(fullpath, 0755); } } #endif } /* ***************************************************************************** */ const char *_psGetUserFilesFolder() { #if defined USE_MY_DOCUMENTS && defined _WIN32 HKEY hKey = NULL; static CHAR szUserFiles[256]; if ( RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_SPECIAL_FOLDERS, REG_OPTION_RESERVED, KEY_READ, &hKey) == ERROR_SUCCESS ) { DWORD KeyType; DWORD KeycbData = sizeof(szUserFiles); if ( RegQueryValueEx(hKey, "Personal", NULL, &KeyType, (LPBYTE)szUserFiles, &KeycbData) == ERROR_SUCCESS ) { RegCloseKey(hKey); strcat(szUserFiles, "\\GTA Vice City User Files"); _psCreateFolder(szUserFiles); return szUserFiles; } RegCloseKey(hKey); } strcpy(szUserFiles, "data"); return szUserFiles; #else static char szUserFiles[256]; strcpy(szUserFiles, "userfiles"); _psCreateFolder(szUserFiles); return szUserFiles; #endif } /* ***************************************************************************** */ RwBool psCameraBeginUpdate(RwCamera *camera) { if ( !RwCameraBeginUpdate(Scene.camera) ) { ForegroundApp = FALSE; RsEventHandler(rsACTIVATE, (void *)FALSE); return FALSE; } return TRUE; } /* ***************************************************************************** */ void psCameraShowRaster(RwCamera *camera) { #ifdef LEGACY_MENU_OPTIONS if (FrontEndMenuManager.m_PrefsVsync || FrontEndMenuManager.m_bMenuActive) #else if (FrontEndMenuManager.m_PrefsFrameLimiter || FrontEndMenuManager.m_bMenuActive) #endif RwCameraShowRaster(camera, PSGLOBAL(window), rwRASTERFLIPWAITVSYNC); else RwCameraShowRaster(camera, PSGLOBAL(window), rwRASTERFLIPDONTWAIT); return; } /* ***************************************************************************** */ RwImage * psGrabScreen(RwCamera *pCamera) { #ifndef LIBRW RwRaster *pRaster = RwCameraGetRaster(pCamera); if (RwImage *pImage = RwImageCreate(pRaster->width, pRaster->height, 32)) { RwImageAllocatePixels(pImage); RwImageSetFromRaster(pImage, pRaster); return pImage; } #else rw::Image *image = RwCameraGetRaster(pCamera)->toImage(); image->removeMask(); if(image) return image; #endif return nil; } /* ***************************************************************************** */ #ifdef _WIN32 #pragma comment( lib, "Winmm.lib" ) // Needed for time RwUInt32 psTimer(void) { RwUInt32 time; TIMECAPS TimeCaps; timeGetDevCaps(&TimeCaps, sizeof(TIMECAPS)); timeBeginPeriod(TimeCaps.wPeriodMin); time = (RwUInt32) timeGetTime(); timeEndPeriod(TimeCaps.wPeriodMin); return time; } #else double psTimer(void) { struct timespec start; #if defined(CLOCK_MONOTONIC_RAW) clock_gettime(CLOCK_MONOTONIC_RAW, &start); #elif defined(CLOCK_MONOTONIC_FAST) clock_gettime(CLOCK_MONOTONIC_FAST, &start); #else clock_gettime(CLOCK_MONOTONIC, &start); #endif return start.tv_sec * 1000.0 + start.tv_nsec/1000000.0; } #endif /* ***************************************************************************** */ void psMouseSetPos(RwV2d *pos) { glfwSetCursorPos(PSGLOBAL(window), pos->x, pos->y); PSGLOBAL(lastMousePos.x) = (RwInt32)pos->x; PSGLOBAL(lastMousePos.y) = (RwInt32)pos->y; return; } /* ***************************************************************************** */ RwMemoryFunctions* psGetMemoryFunctions(void) { #ifdef USE_CUSTOM_ALLOCATOR return &memFuncs; #else return nil; #endif } /* ***************************************************************************** */ RwBool psInstallFileSystem(void) { return (TRUE); } /* ***************************************************************************** */ RwBool psNativeTextureSupport(void) { return true; } /* ***************************************************************************** */ #ifdef UNDER_CE #define CMDSTR LPWSTR #else #define CMDSTR LPSTR #endif /* ***************************************************************************** */ RwBool psInitialize(void) { PsGlobal.lastMousePos.x = PsGlobal.lastMousePos.y = 0.0f; RsGlobal.ps = &PsGlobal; PsGlobal.fullScreen = FALSE; PsGlobal.cursorIsInWindow = FALSE; WindowFocused = TRUE; WindowIconified = FALSE; PsGlobal.joy1id = -1; PsGlobal.joy2id = -1; CFileMgr::Initialise(); #ifdef PS2_MENU CPad::Initialise(); CPad::GetPad(0)->Mode = 0; CGame::frenchGame = false; CGame::germanGame = false; CGame::nastyGame = true; CMenuManager::m_PrefsAllowNastyGame = true; #ifndef _WIN32 // Mandatory for Linux(Unix? Posix?) to set lang. to environment lang. setlocale(LC_ALL, ""); char *systemLang, *keyboardLang; systemLang = setlocale (LC_ALL, NULL); keyboardLang = setlocale (LC_CTYPE, NULL); short lang; lang = !strncmp(systemLang, "fr_",3) ? LANG_FRENCH : !strncmp(systemLang, "de_",3) ? LANG_GERMAN : !strncmp(systemLang, "en_",3) ? LANG_ENGLISH : !strncmp(systemLang, "it_",3) ? LANG_ITALIAN : !strncmp(systemLang, "es_",3) ? LANG_SPANISH : LANG_OTHER; #else WORD lang = PRIMARYLANGID(GetSystemDefaultLCID()); #endif if ( lang == LANG_ITALIAN ) CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; else if ( lang == LANG_SPANISH ) CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; else if ( lang == LANG_GERMAN ) { CGame::germanGame = true; CGame::nastyGame = false; CMenuManager::m_PrefsAllowNastyGame = false; CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; } else if ( lang == LANG_FRENCH ) { CGame::frenchGame = true; CGame::nastyGame = false; CMenuManager::m_PrefsAllowNastyGame = false; CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; } else CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame(); TheMemoryCard.Init(); #else C_PcSave::SetSaveDirectory(_psGetUserFilesFolder()); InitialiseLanguage(); #endif gGameState = GS_START_UP; TRACE("gGameState = GS_START_UP"); #ifdef _WIN32 OSVERSIONINFO verInfo; verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&verInfo); _dwOperatingSystemVersion = OS_WIN95; if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) { if ( verInfo.dwMajorVersion == 4 ) { debug("Operating System is WinNT\n"); _dwOperatingSystemVersion = OS_WINNT; } else if ( verInfo.dwMajorVersion == 5 ) { debug("Operating System is Win2000\n"); _dwOperatingSystemVersion = OS_WIN2000; } else if ( verInfo.dwMajorVersion > 5 ) { debug("Operating System is WinXP or greater\n"); _dwOperatingSystemVersion = OS_WINXP; } } else if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) { if ( verInfo.dwMajorVersion > 4 || verInfo.dwMajorVersion == 4 && verInfo.dwMinorVersion != 0 ) { debug("Operating System is Win98\n"); _dwOperatingSystemVersion = OS_WIN98; } else { debug("Operating System is Win95\n"); _dwOperatingSystemVersion = OS_WIN95; } } #else _dwOperatingSystemVersion = OS_WINXP; // To fool other classes #endif #ifndef PS2_MENU FrontEndMenuManager.LoadSettings(); #endif #ifdef _WIN32 MEMORYSTATUS memstats; GlobalMemoryStatus(&memstats); _dwMemAvailPhys = memstats.dwAvailPhys; debug("Physical memory size %u\n", memstats.dwTotalPhys); debug("Available physical memory %u\n", memstats.dwAvailPhys); #elif defined (__APPLE__) uint64_t size = 0; uint64_t page_size = 0; size_t uint64_len = sizeof(uint64_t); size_t ull_len = sizeof(unsigned long long); sysctl((int[]){CTL_HW, HW_PAGESIZE}, 2, &page_size, &ull_len, NULL, 0); sysctl((int[]){CTL_HW, HW_MEMSIZE}, 2, &size, &uint64_len, NULL, 0); vm_statistics_data_t vm_stat; mach_msg_type_number_t count = HOST_VM_INFO_COUNT; host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stat, &count); _dwMemAvailPhys = (uint64_t)(vm_stat.free_count * page_size); debug("Physical memory size %llu\n", _dwMemAvailPhys); debug("Available physical memory %llu\n", size); #else #ifndef __APPLE__ struct sysinfo systemInfo; sysinfo(&systemInfo); _dwMemAvailPhys = systemInfo.freeram; debug("Physical memory size %u\n", systemInfo.totalram); debug("Available physical memory %u\n", systemInfo.freeram); #else uint64_t size = 0; uint64_t page_size = 0; size_t uint64_len = sizeof(uint64_t); size_t ull_len = sizeof(unsigned long long); sysctl((int[]){CTL_HW, HW_PAGESIZE}, 2, &page_size, &ull_len, NULL, 0); sysctl((int[]){CTL_HW, HW_MEMSIZE}, 2, &size, &uint64_len, NULL, 0); vm_statistics_data_t vm_stat; mach_msg_type_number_t count = HOST_VM_INFO_COUNT; host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stat, &count); _dwMemAvailPhys = (uint64_t)(vm_stat.free_count * page_size); debug("Physical memory size %llu\n", _dwMemAvailPhys); debug("Available physical memory %llu\n", size); #endif _dwOperatingSystemVersion = OS_WINXP; // To fool other classes #endif TheText.Unload(); return TRUE; } /* ***************************************************************************** */ void psTerminate(void) { return; } /* ***************************************************************************** */ static RwChar **_VMList; RwInt32 _psGetNumVideModes() { return RwEngineGetNumVideoModes(); } /* ***************************************************************************** */ RwBool _psFreeVideoModeList() { RwInt32 numModes; RwInt32 i; numModes = _psGetNumVideModes(); if ( _VMList == nil ) return TRUE; for ( i = 0; i < numModes; i++ ) { RwFree(_VMList[i]); } RwFree(_VMList); _VMList = nil; return TRUE; } /* ***************************************************************************** */ RwChar **_psGetVideoModeList() { RwInt32 numModes; RwInt32 i; if ( _VMList != nil ) { return _VMList; } numModes = RwEngineGetNumVideoModes(); _VMList = (RwChar **)RwCalloc(numModes, sizeof(RwChar*)); for ( i = 0; i < numModes; i++ ) { RwVideoMode vm; RwEngineGetVideoModeInfo(&vm, i); if ( vm.flags & rwVIDEOMODEEXCLUSIVE ) { _VMList[i] = (RwChar*)RwCalloc(100, sizeof(RwChar)); rwsprintf(_VMList[i],"%d X %d X %d", vm.width, vm.height, vm.depth); } else _VMList[i] = nil; } return _VMList; } /* ***************************************************************************** */ void _psSelectScreenVM(RwInt32 videoMode) { RwTexDictionarySetCurrent( nil ); FrontEndMenuManager.UnloadTextures(); if (!_psSetVideoMode(RwEngineGetCurrentSubSystem(), videoMode)) { RsGlobal.quit = TRUE; printf("ERROR: Failed to select new screen resolution\n"); } else FrontEndMenuManager.LoadAllTextures(); } /* ***************************************************************************** */ RwBool IsForegroundApp() { return !!ForegroundApp; } /* UINT GetBestRefreshRate(UINT width, UINT height, UINT depth) { LPDIRECT3D8 d3d = Direct3DCreate8(D3D_SDK_VERSION); ASSERT(d3d != nil); UINT refreshRate = INT_MAX; D3DFORMAT format; if ( depth == 32 ) format = D3DFMT_X8R8G8B8; else if ( depth == 24 ) format = D3DFMT_R8G8B8; else format = D3DFMT_R5G6B5; UINT modeCount = d3d->GetAdapterModeCount(GcurSel); for ( UINT i = 0; i < modeCount; i++ ) { D3DDISPLAYMODE mode; d3d->EnumAdapterModes(GcurSel, i, &mode); if ( mode.Width == width && mode.Height == height && mode.Format == format ) { if ( mode.RefreshRate == 0 ) return 0; if ( mode.RefreshRate < refreshRate && mode.RefreshRate >= 60 ) refreshRate = mode.RefreshRate; } } #ifdef FIX_BUGS d3d->Release(); #endif if ( refreshRate == -1 ) return -1; return refreshRate; } */ /* ***************************************************************************** */ RwBool psSelectDevice() { RwVideoMode vm; RwInt32 subSysNum; RwInt32 AutoRenderer = 0; RwBool modeFound = FALSE; if ( !useDefault ) { GnumSubSystems = RwEngineGetNumSubSystems(); if ( !GnumSubSystems ) { return FALSE; } /* Just to be sure ... */ GnumSubSystems = (GnumSubSystems > MAX_SUBSYSTEMS) ? MAX_SUBSYSTEMS : GnumSubSystems; /* Get the names of all the sub systems */ for (subSysNum = 0; subSysNum < GnumSubSystems; subSysNum++) { RwEngineGetSubSystemInfo(&GsubSysInfo[subSysNum], subSysNum); } /* Get the default selection */ GcurSel = RwEngineGetCurrentSubSystem(); #ifdef IMPROVED_VIDEOMODE if(FrontEndMenuManager.m_nPrefsSubsystem < GnumSubSystems) GcurSel = FrontEndMenuManager.m_nPrefsSubsystem; #endif } /* Set the driver to use the correct sub system */ if (!RwEngineSetSubSystem(GcurSel)) { return FALSE; } #ifdef IMPROVED_VIDEOMODE FrontEndMenuManager.m_nPrefsSubsystem = GcurSel; #endif #ifndef IMPROVED_VIDEOMODE if ( !useDefault ) { if ( _psGetVideoModeList()[FrontEndMenuManager.m_nDisplayVideoMode] && FrontEndMenuManager.m_nDisplayVideoMode ) { FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; GcurSelVM = FrontEndMenuManager.m_nDisplayVideoMode; } else { #ifdef DEFAULT_NATIVE_RESOLUTION // get the native video mode HDC hDevice = GetDC(NULL); int w = GetDeviceCaps(hDevice, HORZRES); int h = GetDeviceCaps(hDevice, VERTRES); int d = GetDeviceCaps(hDevice, BITSPIXEL); #else const int w = 640; const int h = 480; const int d = 16; #endif while ( !modeFound && GcurSelVM < RwEngineGetNumVideoModes() ) { RwEngineGetVideoModeInfo(&vm, GcurSelVM); if ( defaultFullscreenRes && vm.width != w || vm.height != h || vm.depth != d || !(vm.flags & rwVIDEOMODEEXCLUSIVE) ) ++GcurSelVM; else modeFound = TRUE; } if ( !modeFound ) { #ifdef DEFAULT_NATIVE_RESOLUTION GcurSelVM = 1; #else printf("WARNING: Cannot find 640x480 video mode, selecting device cancelled\n"); return FALSE; #endif } } } #else if ( !useDefault ) { if(FrontEndMenuManager.m_nPrefsWidth == 0 || FrontEndMenuManager.m_nPrefsHeight == 0 || FrontEndMenuManager.m_nPrefsDepth == 0){ // Defaults if nothing specified const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); FrontEndMenuManager.m_nPrefsWidth = mode->width; FrontEndMenuManager.m_nPrefsHeight = mode->height; FrontEndMenuManager.m_nPrefsDepth = 32; FrontEndMenuManager.m_nPrefsWindowed = 0; } // Find the videomode that best fits what we got from the settings file RwInt32 bestFsMode = -1; RwInt32 bestWidth = -1; RwInt32 bestHeight = -1; RwInt32 bestDepth = -1; for(GcurSelVM = 0; GcurSelVM < RwEngineGetNumVideoModes(); GcurSelVM++){ RwEngineGetVideoModeInfo(&vm, GcurSelVM); if (!(vm.flags & rwVIDEOMODEEXCLUSIVE)){ bestWndMode = GcurSelVM; } else { // try the largest one that isn't larger than what we wanted if(vm.width >= bestWidth && vm.width <= FrontEndMenuManager.m_nPrefsWidth && vm.height >= bestHeight && vm.height <= FrontEndMenuManager.m_nPrefsHeight && vm.depth >= bestDepth && vm.depth <= FrontEndMenuManager.m_nPrefsDepth){ bestWidth = vm.width; bestHeight = vm.height; bestDepth = vm.depth; bestFsMode = GcurSelVM; } } } if(bestFsMode < 0){ printf("WARNING: Cannot find desired video mode, selecting device cancelled\n"); return FALSE; } GcurSelVM = bestFsMode; FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; FrontEndMenuManager.m_nSelectedScreenMode = FrontEndMenuManager.m_nPrefsWindowed; } #endif RwEngineGetVideoModeInfo(&vm, GcurSelVM); #ifdef IMPROVED_VIDEOMODE if (FrontEndMenuManager.m_nPrefsWindowed) GcurSelVM = bestWndMode; // Now GcurSelVM is 0 but vm has sizes(and fullscreen flag) of the video mode we want, that's why we changed the rwVIDEOMODEEXCLUSIVE conditions below FrontEndMenuManager.m_nPrefsWidth = vm.width; FrontEndMenuManager.m_nPrefsHeight = vm.height; FrontEndMenuManager.m_nPrefsDepth = vm.depth; #endif #ifndef PS2_MENU FrontEndMenuManager.m_nCurrOption = 0; #endif /* Set up the video mode and set the apps window * dimensions to match */ if (!RwEngineSetVideoMode(GcurSelVM)) { return FALSE; } /* TODO if (vm.flags & rwVIDEOMODEEXCLUSIVE) { debug("%dx%dx%d", vm.width, vm.height, vm.depth); UINT refresh = GetBestRefreshRate(vm.width, vm.height, vm.depth); if ( refresh != (UINT)-1 ) { debug("refresh %d", refresh); RwD3D8EngineSetRefreshRate((RwUInt32)refresh); } } */ #ifndef IMPROVED_VIDEOMODE if (vm.flags & rwVIDEOMODEEXCLUSIVE) { RsGlobal.maximumWidth = vm.width; RsGlobal.maximumHeight = vm.height; RsGlobal.width = vm.width; RsGlobal.height = vm.height; PSGLOBAL(fullScreen) = TRUE; } #else RsGlobal.maximumWidth = FrontEndMenuManager.m_nPrefsWidth; RsGlobal.maximumHeight = FrontEndMenuManager.m_nPrefsHeight; RsGlobal.width = FrontEndMenuManager.m_nPrefsWidth; RsGlobal.height = FrontEndMenuManager.m_nPrefsHeight; PSGLOBAL(fullScreen) = !FrontEndMenuManager.m_nPrefsWindowed; #endif #ifdef MULTISAMPLING RwD3D8EngineSetMultiSamplingLevels(1 << FrontEndMenuManager.m_nPrefsMSAALevel); #endif return TRUE; } #ifndef GET_KEYBOARD_INPUT_FROM_X11 void keypressCB(GLFWwindow* window, int key, int scancode, int action, int mods); #endif void resizeCB(GLFWwindow* window, int width, int height); void scrollCB(GLFWwindow* window, double xoffset, double yoffset); void cursorCB(GLFWwindow* window, double xpos, double ypos); void cursorEnterCB(GLFWwindow* window, int entered); void windowFocusCB(GLFWwindow* window, int focused); void windowIconifyCB(GLFWwindow* window, int iconified); void joysChangeCB(int jid, int event); bool IsThisJoystickBlacklisted(int i) { #ifndef DETECT_JOYSTICK_MENU return false; #else if (glfwJoystickIsGamepad(i)) return false; const char* joyname = glfwGetJoystickName(i); if (gSelectedJoystickName[0] != '\0' && strncmp(joyname, gSelectedJoystickName, strlen(gSelectedJoystickName)) == 0) return false; return true; #endif } void _InputInitialiseJoys() { PSGLOBAL(joy1id) = -1; PSGLOBAL(joy2id) = -1; // Load our gamepad mappings. #define SDL_GAMEPAD_DB_PATH "gamecontrollerdb.txt" FILE *f = fopen(SDL_GAMEPAD_DB_PATH, "rb"); if (f) { fseek(f, 0, SEEK_END); size_t fsize = ftell(f); fseek(f, 0, SEEK_SET); char *db = (char*)malloc(fsize + 1); if (fread(db, 1, fsize, f) == fsize) { db[fsize] = '\0'; if (glfwUpdateGamepadMappings(db) == GLFW_FALSE) Error("glfwUpdateGamepadMappings didn't succeed, check " SDL_GAMEPAD_DB_PATH ".\n"); } else Error("fread on " SDL_GAMEPAD_DB_PATH " wasn't successful.\n"); free(db); fclose(f); } else printf("You don't seem to have copied " SDL_GAMEPAD_DB_PATH " file from re3/gamefiles to GTA3 directory. Some gamepads may not be recognized.\n"); #undef SDL_GAMEPAD_DB_PATH // But always overwrite it with the one in SDL_GAMECONTROLLERCONFIG. char const* EnvControlConfig = getenv("SDL_GAMECONTROLLERCONFIG"); if (EnvControlConfig != nil) { glfwUpdateGamepadMappings(EnvControlConfig); } for (int i = 0; i <= GLFW_JOYSTICK_LAST; i++) { if (glfwJoystickPresent(i) && !IsThisJoystickBlacklisted(i)) { if (PSGLOBAL(joy1id) == -1) PSGLOBAL(joy1id) = i; else if (PSGLOBAL(joy2id) == -1) PSGLOBAL(joy2id) = i; else break; } } if (PSGLOBAL(joy1id) != -1) { int count; glfwGetJoystickButtons(PSGLOBAL(joy1id), &count); #ifdef DETECT_JOYSTICK_MENU strcpy(gSelectedJoystickName, glfwGetJoystickName(PSGLOBAL(joy1id))); #endif ControlsManager.InitDefaultControlConfigJoyPad(count); } } int lastCursorMode = GLFW_CURSOR_HIDDEN; long _InputInitialiseMouse(bool exclusive) { // Disabled = keep cursor centered and hide lastCursorMode = exclusive ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_HIDDEN; glfwSetInputMode(PSGLOBAL(window), GLFW_CURSOR, lastCursorMode); return 0; } void _InputShutdownMouse() { // Not needed } // Not "needs exclusive" on GLFW, but more like "needs to change mode" bool _InputMouseNeedsExclusive() { // That was the cause of infamous mouse bug on Win. RwVideoMode vm; RwEngineGetVideoModeInfo(&vm, GcurSelVM); // If windowed, free the cursor on menu(where this func. is called and DISABLED-HIDDEN transition is done accordingly) // If it's fullscreen, be sure that it didn't stuck on HIDDEN. return !(vm.flags & rwVIDEOMODEEXCLUSIVE) || lastCursorMode == GLFW_CURSOR_HIDDEN; } void psPostRWinit(void) { RwVideoMode vm; RwEngineGetVideoModeInfo(&vm, GcurSelVM); #ifndef GET_KEYBOARD_INPUT_FROM_X11 glfwSetKeyCallback(PSGLOBAL(window), keypressCB); #endif glfwSetFramebufferSizeCallback(PSGLOBAL(window), resizeCB); glfwSetScrollCallback(PSGLOBAL(window), scrollCB); glfwSetCursorPosCallback(PSGLOBAL(window), cursorCB); glfwSetCursorEnterCallback(PSGLOBAL(window), cursorEnterCB); glfwSetWindowIconifyCallback(PSGLOBAL(window), windowIconifyCB); glfwSetWindowFocusCallback(PSGLOBAL(window), windowFocusCB); glfwSetJoystickCallback(joysChangeCB); _InputInitialiseJoys(); _InputInitialiseMouse(false); if(!(vm.flags & rwVIDEOMODEEXCLUSIVE)) glfwSetWindowSize(PSGLOBAL(window), RsGlobal.maximumWidth, RsGlobal.maximumHeight); // Make sure all keys are released CPad::GetPad(0)->Clear(true); CPad::GetPad(1)->Clear(true); } /* ***************************************************************************** */ RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode) { RwInitialised = FALSE; RsEventHandler(rsRWTERMINATE, nil); GcurSel = subSystem; GcurSelVM = videoMode; useDefault = TRUE; if ( RsEventHandler(rsRWINITIALIZE, &openParams) == rsEVENTERROR ) return FALSE; RwInitialised = TRUE; useDefault = FALSE; RwRect r; r.x = 0; r.y = 0; r.w = RsGlobal.maximumWidth; r.h = RsGlobal.maximumHeight; RsEventHandler(rsCAMERASIZE, &r); psPostRWinit(); return TRUE; } /* ***************************************************************************** */ static RwChar ** CommandLineToArgv(RwChar *cmdLine, RwInt32 *argCount) { RwInt32 numArgs = 0; RwBool inArg, inString; RwInt32 i, len; RwChar *res, *str, **aptr; len = strlen(cmdLine); /* * Count the number of arguments... */ inString = FALSE; inArg = FALSE; for(i=0; i<=len; i++) { if( cmdLine[i] == '"' ) { inString = !inString; } if( (cmdLine[i] <= ' ' && !inString) || i == len ) { if( inArg ) { inArg = FALSE; numArgs++; } } else if( !inArg ) { inArg = TRUE; } } /* * Allocate memory for result... */ res = (RwChar *)malloc(sizeof(RwChar *) * numArgs + len + 1); str = res + sizeof(RwChar *) * numArgs; aptr = (RwChar **)res; strcpy(str, cmdLine); /* * Walk through cmdLine again this time setting pointer to each arg... */ inArg = FALSE; inString = FALSE; for(i=0; i<=len; i++) { if( cmdLine[i] == '"' ) { inString = !inString; } if( (cmdLine[i] <= ' ' && !inString) || i == len ) { if( inArg ) { if( str[i-1] == '"' ) { str[i-1] = '\0'; } else { str[i] = '\0'; } inArg = FALSE; } } else if( !inArg && cmdLine[i] != '"' ) { inArg = TRUE; *aptr++ = &str[i]; } } *argCount = numArgs; return (RwChar **)res; } /* ***************************************************************************** */ void InitialiseLanguage() { #ifndef _WIN32 // Mandatory for Linux(Unix? Posix?) to set lang. to environment lang. setlocale(LC_ALL, ""); char *systemLang, *keyboardLang; systemLang = setlocale (LC_ALL, NULL); keyboardLang = setlocale (LC_CTYPE, NULL); short primUserLCID, primSystemLCID; primUserLCID = primSystemLCID = !strncmp(systemLang, "fr_",3) ? LANG_FRENCH : !strncmp(systemLang, "de_",3) ? LANG_GERMAN : !strncmp(systemLang, "en_",3) ? LANG_ENGLISH : !strncmp(systemLang, "it_",3) ? LANG_ITALIAN : !strncmp(systemLang, "es_",3) ? LANG_SPANISH : LANG_OTHER; short primLayout = !strncmp(keyboardLang, "fr_",3) ? LANG_FRENCH : (!strncmp(keyboardLang, "de_",3) ? LANG_GERMAN : LANG_ENGLISH); short subUserLCID, subSystemLCID; subUserLCID = subSystemLCID = !strncmp(systemLang, "en_AU",5) ? SUBLANG_ENGLISH_AUS : SUBLANG_OTHER; short subLayout = !strncmp(keyboardLang, "en_AU",5) ? SUBLANG_ENGLISH_AUS : SUBLANG_OTHER; #else WORD primUserLCID = PRIMARYLANGID(GetSystemDefaultLCID()); WORD primSystemLCID = PRIMARYLANGID(GetUserDefaultLCID()); WORD primLayout = PRIMARYLANGID((DWORD)GetKeyboardLayout(0)); WORD subUserLCID = SUBLANGID(GetSystemDefaultLCID()); WORD subSystemLCID = SUBLANGID(GetUserDefaultLCID()); WORD subLayout = SUBLANGID((DWORD)GetKeyboardLayout(0)); #endif if ( primUserLCID == LANG_GERMAN || primSystemLCID == LANG_GERMAN || primLayout == LANG_GERMAN ) { CGame::nastyGame = false; FrontEndMenuManager.m_PrefsAllowNastyGame = false; CGame::germanGame = true; } if ( primUserLCID == LANG_FRENCH || primSystemLCID == LANG_FRENCH || primLayout == LANG_FRENCH ) { CGame::nastyGame = false; FrontEndMenuManager.m_PrefsAllowNastyGame = false; CGame::frenchGame = true; } if ( subUserLCID == SUBLANG_ENGLISH_AUS || subSystemLCID == SUBLANG_ENGLISH_AUS || subLayout == SUBLANG_ENGLISH_AUS ) CGame::noProstitutes = true; #ifdef NASTY_GAME CGame::nastyGame = true; FrontEndMenuManager.m_PrefsAllowNastyGame = true; CGame::noProstitutes = false; #endif int32 lang; switch ( primSystemLCID ) { case LANG_GERMAN: { lang = LANG_GERMAN; break; } case LANG_FRENCH: { lang = LANG_FRENCH; break; } case LANG_SPANISH: { lang = LANG_SPANISH; break; } case LANG_ITALIAN: { lang = LANG_ITALIAN; break; } default: { lang = ( subSystemLCID == SUBLANG_ENGLISH_AUS ) ? -99 : LANG_ENGLISH; break; } } FrontEndMenuManager.OS_Language = primUserLCID; switch ( lang ) { case LANG_GERMAN: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; break; } case LANG_SPANISH: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; break; } case LANG_FRENCH: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; break; } case LANG_ITALIAN: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; break; } default: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; break; } } #ifndef _WIN32 // TODO this is needed for strcasecmp to work correctly across all languages, but can these cause other problems?? setlocale(LC_CTYPE, "C"); setlocale(LC_COLLATE, "C"); setlocale(LC_NUMERIC, "C"); #endif TheText.Unload(); TheText.Load(); } /* ***************************************************************************** */ void HandleExit() { #ifdef _WIN32 MSG message; while ( PeekMessage(&message, nil, 0U, 0U, PM_REMOVE|PM_NOYIELD) ) { if( message.message == WM_QUIT ) { RsGlobal.quit = TRUE; } else { TranslateMessage(&message); DispatchMessage(&message); } } #else // We now handle terminate message always, why handle on some cases? return; #endif } #ifndef _WIN32 void terminateHandler(int sig, siginfo_t *info, void *ucontext) { RsGlobal.quit = TRUE; } #ifdef FLUSHABLE_STREAMING void dummyHandler(int sig){ // Don't kill the app pls } #endif #endif void resizeCB(GLFWwindow* window, int width, int height) { /* * Handle event to ensure window contents are displayed during re-size * as this can be disabled by the user, then if there is not enough * memory things don't work. */ /* redraw window */ if (RwInitialised && gGameState == GS_PLAYING_GAME) { RsEventHandler(rsIDLE, (void *)TRUE); } if (RwInitialised && height > 0 && width > 0) { RwRect r; // TODO fix artifacts of resizing with mouse RsGlobal.maximumHeight = height; RsGlobal.maximumWidth = width; r.x = 0; r.y = 0; r.w = width; r.h = height; RsEventHandler(rsCAMERASIZE, &r); } // glfwSetWindowPos(window, 0, 0); } void scrollCB(GLFWwindow* window, double xoffset, double yoffset) { PSGLOBAL(mouseWheel) = yoffset; } bool lshiftStatus = false; bool rshiftStatus = false; #ifndef GET_KEYBOARD_INPUT_FROM_X11 int keymap[GLFW_KEY_LAST + 1]; static void initkeymap(void) { int i; for (i = 0; i < GLFW_KEY_LAST + 1; i++) keymap[i] = rsNULL; keymap[GLFW_KEY_SPACE] = ' '; keymap[GLFW_KEY_APOSTROPHE] = '\''; keymap[GLFW_KEY_COMMA] = ','; keymap[GLFW_KEY_MINUS] = '-'; keymap[GLFW_KEY_PERIOD] = '.'; keymap[GLFW_KEY_SLASH] = '/'; keymap[GLFW_KEY_0] = '0'; keymap[GLFW_KEY_1] = '1'; keymap[GLFW_KEY_2] = '2'; keymap[GLFW_KEY_3] = '3'; keymap[GLFW_KEY_4] = '4'; keymap[GLFW_KEY_5] = '5'; keymap[GLFW_KEY_6] = '6'; keymap[GLFW_KEY_7] = '7'; keymap[GLFW_KEY_8] = '8'; keymap[GLFW_KEY_9] = '9'; keymap[GLFW_KEY_SEMICOLON] = ';'; keymap[GLFW_KEY_EQUAL] = '='; keymap[GLFW_KEY_A] = 'A'; keymap[GLFW_KEY_B] = 'B'; keymap[GLFW_KEY_C] = 'C'; keymap[GLFW_KEY_D] = 'D'; keymap[GLFW_KEY_E] = 'E'; keymap[GLFW_KEY_F] = 'F'; keymap[GLFW_KEY_G] = 'G'; keymap[GLFW_KEY_H] = 'H'; keymap[GLFW_KEY_I] = 'I'; keymap[GLFW_KEY_J] = 'J'; keymap[GLFW_KEY_K] = 'K'; keymap[GLFW_KEY_L] = 'L'; keymap[GLFW_KEY_M] = 'M'; keymap[GLFW_KEY_N] = 'N'; keymap[GLFW_KEY_O] = 'O'; keymap[GLFW_KEY_P] = 'P'; keymap[GLFW_KEY_Q] = 'Q'; keymap[GLFW_KEY_R] = 'R'; keymap[GLFW_KEY_S] = 'S'; keymap[GLFW_KEY_T] = 'T'; keymap[GLFW_KEY_U] = 'U'; keymap[GLFW_KEY_V] = 'V'; keymap[GLFW_KEY_W] = 'W'; keymap[GLFW_KEY_X] = 'X'; keymap[GLFW_KEY_Y] = 'Y'; keymap[GLFW_KEY_Z] = 'Z'; keymap[GLFW_KEY_LEFT_BRACKET] = '['; keymap[GLFW_KEY_BACKSLASH] = '\\'; keymap[GLFW_KEY_RIGHT_BRACKET] = ']'; keymap[GLFW_KEY_GRAVE_ACCENT] = '`'; keymap[GLFW_KEY_ESCAPE] = rsESC; keymap[GLFW_KEY_ENTER] = rsENTER; keymap[GLFW_KEY_TAB] = rsTAB; keymap[GLFW_KEY_BACKSPACE] = rsBACKSP; keymap[GLFW_KEY_INSERT] = rsINS; keymap[GLFW_KEY_DELETE] = rsDEL; keymap[GLFW_KEY_RIGHT] = rsRIGHT; keymap[GLFW_KEY_LEFT] = rsLEFT; keymap[GLFW_KEY_DOWN] = rsDOWN; keymap[GLFW_KEY_UP] = rsUP; keymap[GLFW_KEY_PAGE_UP] = rsPGUP; keymap[GLFW_KEY_PAGE_DOWN] = rsPGDN; keymap[GLFW_KEY_HOME] = rsHOME; keymap[GLFW_KEY_END] = rsEND; keymap[GLFW_KEY_CAPS_LOCK] = rsCAPSLK; keymap[GLFW_KEY_SCROLL_LOCK] = rsSCROLL; keymap[GLFW_KEY_NUM_LOCK] = rsNUMLOCK; keymap[GLFW_KEY_PRINT_SCREEN] = rsNULL; keymap[GLFW_KEY_PAUSE] = rsPAUSE; keymap[GLFW_KEY_F1] = rsF1; keymap[GLFW_KEY_F2] = rsF2; keymap[GLFW_KEY_F3] = rsF3; keymap[GLFW_KEY_F4] = rsF4; keymap[GLFW_KEY_F5] = rsF5; keymap[GLFW_KEY_F6] = rsF6; keymap[GLFW_KEY_F7] = rsF7; keymap[GLFW_KEY_F8] = rsF8; keymap[GLFW_KEY_F9] = rsF9; keymap[GLFW_KEY_F10] = rsF10; keymap[GLFW_KEY_F11] = rsF11; keymap[GLFW_KEY_F12] = rsF12; keymap[GLFW_KEY_F13] = rsNULL; keymap[GLFW_KEY_F14] = rsNULL; keymap[GLFW_KEY_F15] = rsNULL; keymap[GLFW_KEY_F16] = rsNULL; keymap[GLFW_KEY_F17] = rsNULL; keymap[GLFW_KEY_F18] = rsNULL; keymap[GLFW_KEY_F19] = rsNULL; keymap[GLFW_KEY_F20] = rsNULL; keymap[GLFW_KEY_F21] = rsNULL; keymap[GLFW_KEY_F22] = rsNULL; keymap[GLFW_KEY_F23] = rsNULL; keymap[GLFW_KEY_F24] = rsNULL; keymap[GLFW_KEY_F25] = rsNULL; keymap[GLFW_KEY_KP_0] = rsPADINS; keymap[GLFW_KEY_KP_1] = rsPADEND; keymap[GLFW_KEY_KP_2] = rsPADDOWN; keymap[GLFW_KEY_KP_3] = rsPADPGDN; keymap[GLFW_KEY_KP_4] = rsPADLEFT; keymap[GLFW_KEY_KP_5] = rsPAD5; keymap[GLFW_KEY_KP_6] = rsPADRIGHT; keymap[GLFW_KEY_KP_7] = rsPADHOME; keymap[GLFW_KEY_KP_8] = rsPADUP; keymap[GLFW_KEY_KP_9] = rsPADPGUP; keymap[GLFW_KEY_KP_DECIMAL] = rsPADDEL; keymap[GLFW_KEY_KP_DIVIDE] = rsDIVIDE; keymap[GLFW_KEY_KP_MULTIPLY] = rsTIMES; keymap[GLFW_KEY_KP_SUBTRACT] = rsMINUS; keymap[GLFW_KEY_KP_ADD] = rsPLUS; keymap[GLFW_KEY_KP_ENTER] = rsPADENTER; keymap[GLFW_KEY_KP_EQUAL] = rsNULL; keymap[GLFW_KEY_LEFT_SHIFT] = rsLSHIFT; keymap[GLFW_KEY_LEFT_CONTROL] = rsLCTRL; keymap[GLFW_KEY_LEFT_ALT] = rsLALT; keymap[GLFW_KEY_LEFT_SUPER] = rsLWIN; keymap[GLFW_KEY_RIGHT_SHIFT] = rsRSHIFT; keymap[GLFW_KEY_RIGHT_CONTROL] = rsRCTRL; keymap[GLFW_KEY_RIGHT_ALT] = rsRALT; keymap[GLFW_KEY_RIGHT_SUPER] = rsRWIN; keymap[GLFW_KEY_MENU] = rsNULL; } void keypressCB(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key >= 0 && key <= GLFW_KEY_LAST && action != GLFW_REPEAT) { RsKeyCodes ks = (RsKeyCodes)keymap[key]; if (key == GLFW_KEY_LEFT_SHIFT) lshiftStatus = action != GLFW_RELEASE; if (key == GLFW_KEY_RIGHT_SHIFT) rshiftStatus = action != GLFW_RELEASE; if (action == GLFW_RELEASE) RsKeyboardEventHandler(rsKEYUP, &ks); else if (action == GLFW_PRESS) RsKeyboardEventHandler(rsKEYDOWN, &ks); } } #else uint32 keymap[512]; // 256 ascii + 256 KeySyms between 0xff00 - 0xffff bool keyStates[512]; uint32 keyCodeToKeymapIndex[256]; // cache for physical keys #define KEY_MAP_OFFSET (0xff00 - 256) static void initkeymap(void) { Display *display = glfwGetX11Display(); int i; for (i = 0; i < ARRAY_SIZE(keymap); i++) keymap[i] = rsNULL; // You can add new ASCII mappings to here freely (but beware that if right hand side of assignment isn't supported on CFont, it'll be blank/won't work on binding screen) // Right hand side of assigments should always be uppercase counterpart of character keymap[XK_space] = ' '; keymap[XK_apostrophe] = '\''; keymap[XK_ampersand] = '&'; keymap[XK_percent] = '%'; keymap[XK_dollar] = '$'; keymap[XK_comma] = ','; keymap[XK_minus] = '-'; keymap[XK_period] = '.'; keymap[XK_slash] = '/'; keymap[XK_question] = '?'; keymap[XK_exclam] = '!'; keymap[XK_quotedbl] = '"'; keymap[XK_colon] = ':'; keymap[XK_semicolon] = ';'; keymap[XK_equal] = '='; keymap[XK_bracketleft] = '['; keymap[XK_backslash] = '\\'; keymap[XK_bracketright] = ']'; keymap[XK_grave] = '`'; keymap[XK_0] = '0'; keymap[XK_1] = '1'; keymap[XK_2] = '2'; keymap[XK_3] = '3'; keymap[XK_4] = '4'; keymap[XK_5] = '5'; keymap[XK_6] = '6'; keymap[XK_7] = '7'; keymap[XK_8] = '8'; keymap[XK_9] = '9'; keymap[XK_a] = 'A'; keymap[XK_b] = 'B'; keymap[XK_c] = 'C'; keymap[XK_d] = 'D'; keymap[XK_e] = 'E'; keymap[XK_f] = 'F'; keymap[XK_g] = 'G'; keymap[XK_h] = 'H'; keymap[XK_i] = 'I'; keymap[XK_I] = 'I'; // Turkish I problem keymap[XK_j] = 'J'; keymap[XK_k] = 'K'; keymap[XK_l] = 'L'; keymap[XK_m] = 'M'; keymap[XK_n] = 'N'; keymap[XK_o] = 'O'; keymap[XK_p] = 'P'; keymap[XK_q] = 'Q'; keymap[XK_r] = 'R'; keymap[XK_s] = 'S'; keymap[XK_t] = 'T'; keymap[XK_u] = 'U'; keymap[XK_v] = 'V'; keymap[XK_w] = 'W'; keymap[XK_x] = 'X'; keymap[XK_y] = 'Y'; keymap[XK_z] = 'Z'; // Some of regional but ASCII characters that GTA supports keymap[XK_agrave] = 0x00c0; keymap[XK_aacute] = 0x00c1; keymap[XK_acircumflex] = 0x00c2; keymap[XK_adiaeresis] = 0x00c4; keymap[XK_ae] = 0x00c6; keymap[XK_egrave] = 0x00c8; keymap[XK_eacute] = 0x00c9; keymap[XK_ecircumflex] = 0x00ca; keymap[XK_ediaeresis] = 0x00cb; keymap[XK_igrave] = 0x00cc; keymap[XK_iacute] = 0x00cd; keymap[XK_icircumflex] = 0x00ce; keymap[XK_idiaeresis] = 0x00cf; keymap[XK_ccedilla] = 0x00c7; keymap[XK_odiaeresis] = 0x00d6; keymap[XK_udiaeresis] = 0x00dc; // These are 0xff00 - 0xffff range of KeySym's, and subtracting KEY_MAP_OFFSET is needed keymap[XK_Escape - KEY_MAP_OFFSET] = rsESC; keymap[XK_Return - KEY_MAP_OFFSET] = rsENTER; keymap[XK_Tab - KEY_MAP_OFFSET] = rsTAB; keymap[XK_BackSpace - KEY_MAP_OFFSET] = rsBACKSP; keymap[XK_Insert - KEY_MAP_OFFSET] = rsINS; keymap[XK_Delete - KEY_MAP_OFFSET] = rsDEL; keymap[XK_Right - KEY_MAP_OFFSET] = rsRIGHT; keymap[XK_Left - KEY_MAP_OFFSET] = rsLEFT; keymap[XK_Down - KEY_MAP_OFFSET] = rsDOWN; keymap[XK_Up - KEY_MAP_OFFSET] = rsUP; keymap[XK_Page_Up - KEY_MAP_OFFSET] = rsPGUP; keymap[XK_Page_Down - KEY_MAP_OFFSET] = rsPGDN; keymap[XK_Home - KEY_MAP_OFFSET] = rsHOME; keymap[XK_End - KEY_MAP_OFFSET] = rsEND; keymap[XK_Caps_Lock - KEY_MAP_OFFSET] = rsCAPSLK; keymap[XK_Scroll_Lock - KEY_MAP_OFFSET] = rsSCROLL; keymap[XK_Num_Lock - KEY_MAP_OFFSET] = rsNUMLOCK; keymap[XK_Pause - KEY_MAP_OFFSET] = rsPAUSE; keymap[XK_F1 - KEY_MAP_OFFSET] = rsF1; keymap[XK_F2 - KEY_MAP_OFFSET] = rsF2; keymap[XK_F3 - KEY_MAP_OFFSET] = rsF3; keymap[XK_F4 - KEY_MAP_OFFSET] = rsF4; keymap[XK_F5 - KEY_MAP_OFFSET] = rsF5; keymap[XK_F6 - KEY_MAP_OFFSET] = rsF6; keymap[XK_F7 - KEY_MAP_OFFSET] = rsF7; keymap[XK_F8 - KEY_MAP_OFFSET] = rsF8; keymap[XK_F9 - KEY_MAP_OFFSET] = rsF9; keymap[XK_F10 - KEY_MAP_OFFSET] = rsF10; keymap[XK_F11 - KEY_MAP_OFFSET] = rsF11; keymap[XK_F12 - KEY_MAP_OFFSET] = rsF12; keymap[XK_F13 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F14 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F15 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F16 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F17 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F18 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F19 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F20 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F21 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F22 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F23 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F24 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_F25 - KEY_MAP_OFFSET] = rsNULL; keymap[XK_KP_0 - KEY_MAP_OFFSET] = rsPADINS; keymap[XK_KP_1 - KEY_MAP_OFFSET] = rsPADEND; keymap[XK_KP_2 - KEY_MAP_OFFSET] = rsPADDOWN; keymap[XK_KP_3 - KEY_MAP_OFFSET] = rsPADPGDN; keymap[XK_KP_4 - KEY_MAP_OFFSET] = rsPADLEFT; keymap[XK_KP_5 - KEY_MAP_OFFSET] = rsPAD5; keymap[XK_KP_6 - KEY_MAP_OFFSET] = rsPADRIGHT; keymap[XK_KP_7 - KEY_MAP_OFFSET] = rsPADHOME; keymap[XK_KP_8 - KEY_MAP_OFFSET] = rsPADUP; keymap[XK_KP_9 - KEY_MAP_OFFSET] = rsPADPGUP; keymap[XK_KP_Insert - KEY_MAP_OFFSET] = rsPADINS; keymap[XK_KP_End - KEY_MAP_OFFSET] = rsPADEND; keymap[XK_KP_Down - KEY_MAP_OFFSET] = rsPADDOWN; keymap[XK_KP_Page_Down - KEY_MAP_OFFSET] = rsPADPGDN; keymap[XK_KP_Left - KEY_MAP_OFFSET] = rsPADLEFT; keymap[XK_KP_Begin - KEY_MAP_OFFSET] = rsPAD5; keymap[XK_KP_Right - KEY_MAP_OFFSET] = rsPADRIGHT; keymap[XK_KP_Home - KEY_MAP_OFFSET] = rsPADHOME; keymap[XK_KP_Up - KEY_MAP_OFFSET] = rsPADUP; keymap[XK_KP_Page_Up - KEY_MAP_OFFSET] = rsPADPGUP; keymap[XK_KP_Decimal - KEY_MAP_OFFSET] = rsPADDEL; keymap[XK_KP_Divide - KEY_MAP_OFFSET] = rsDIVIDE; keymap[XK_KP_Multiply - KEY_MAP_OFFSET] = rsTIMES; keymap[XK_KP_Subtract - KEY_MAP_OFFSET] = rsMINUS; keymap[XK_KP_Add - KEY_MAP_OFFSET] = rsPLUS; keymap[XK_KP_Enter - KEY_MAP_OFFSET] = rsPADENTER; keymap[XK_KP_Equal - KEY_MAP_OFFSET] = rsNULL; keymap[XK_Shift_L - KEY_MAP_OFFSET] = rsLSHIFT; keymap[XK_Control_L - KEY_MAP_OFFSET] = rsLCTRL; keymap[XK_Alt_L - KEY_MAP_OFFSET] = rsLALT; keymap[XK_Super_L - KEY_MAP_OFFSET] = rsLWIN; keymap[XK_Shift_R - KEY_MAP_OFFSET] = rsRSHIFT; keymap[XK_Control_R - KEY_MAP_OFFSET] = rsRCTRL; keymap[XK_Alt_R - KEY_MAP_OFFSET] = rsRALT; keymap[XK_Super_R - KEY_MAP_OFFSET] = rsRWIN; keymap[XK_Menu - KEY_MAP_OFFSET] = rsNULL; // Cache the key codes' key symbol equivelants, otherwise we will have to do it on each frame // KeyCode is always in [0,255], and represents a physical key int min_keycode, max_keycode, keysyms_per_keycode; KeySym *keymap, *origkeymap; char *keyboardLang = setlocale (LC_CTYPE, NULL); setlocale(LC_CTYPE, ""); XDisplayKeycodes(display, &min_keycode, &max_keycode); origkeymap = XGetKeyboardMapping(display, min_keycode, (max_keycode - min_keycode + 1), &keysyms_per_keycode); keymap = origkeymap; for (int i = min_keycode; i <= max_keycode; i++) { int j, lastKeysym; lastKeysym = keysyms_per_keycode - 1; while ((lastKeysym >= 0) && (keymap[lastKeysym] == NoSymbol)) lastKeysym--; for (j = 0; j <= lastKeysym; j++) { KeySym ks = keymap[j]; if (ks == NoSymbol) continue; if (ks < 256) { keyCodeToKeymapIndex[i] = ks; break; } else if (ks >= 0xff00 && ks < 0xffff) { keyCodeToKeymapIndex[i] = ks - KEY_MAP_OFFSET; break; } } keymap += keysyms_per_keycode; } XFree(origkeymap); setlocale(LC_CTYPE, keyboardLang); } #undef KEY_MAP_OFFSET void checkKeyPresses() { Display *display = glfwGetX11Display(); char keys[32]; XQueryKeymap(display, keys); for (int i = 0; i < sizeof(keys); i++) { for (int j = 0; j < 8; j++) { KeyCode keycode = 8 * i + j; uint32 keymapIndex = keyCodeToKeymapIndex[keycode]; if (keymapIndex != 0) { int rsCode = keymap[keymapIndex]; if (rsCode == rsNULL) continue; bool pressed = WindowFocused && !!(keys[i] & (1 << j)); // idk why R* does that if (rsCode == rsLSHIFT) lshiftStatus = pressed; else if (rsCode == rsRSHIFT) rshiftStatus = pressed; if (keyStates[keymapIndex] != pressed) { if (pressed) { RsKeyboardEventHandler(rsKEYDOWN, &rsCode); } else { RsKeyboardEventHandler(rsKEYUP, &rsCode); } } keyStates[keymapIndex] = pressed; } } } } #endif // R* calls that in ControllerConfig, idk why void _InputTranslateShiftKeyUpDown(RsKeyCodes *rs) { RsKeyboardEventHandler(lshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsLSHIFT)); RsKeyboardEventHandler(rshiftStatus ? rsKEYDOWN : rsKEYUP, &(*rs = rsRSHIFT)); } // TODO this only works in frontend(and luckily only frontend use this). Fun fact: if I get pos manually in game, glfw reports that it's > 32000 void cursorCB(GLFWwindow* window, double xpos, double ypos) { if (!FrontEndMenuManager.m_bMenuActive) return; int winw, winh; glfwGetWindowSize(PSGLOBAL(window), &winw, &winh); FrontEndMenuManager.m_nMouseTempPosX = xpos * (RsGlobal.maximumWidth / winw); FrontEndMenuManager.m_nMouseTempPosY = ypos * (RsGlobal.maximumHeight / winh); } void cursorEnterCB(GLFWwindow* window, int entered) { PSGLOBAL(cursorIsInWindow) = !!entered; } void windowFocusCB(GLFWwindow* window, int focused) { WindowFocused = !!focused; } void windowIconifyCB(GLFWwindow* window, int iconified) { WindowIconified = !!iconified; } /* ***************************************************************************** */ #ifdef _WIN32 int PASCAL WinMain(HINSTANCE instance, HINSTANCE prevInstance __RWUNUSED__, CMDSTR cmdLine, int cmdShow) { RwInt32 argc; RwChar** argv; SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, nil, SPIF_SENDCHANGE); #ifndef MASTER if (strstr(cmdLine, "-console")) { AllocConsole(); freopen("CONIN$", "r", stdin); freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); } #endif #else int main(int argc, char *argv[]) { #endif RwV2d pos; RwInt32 i; #ifdef USE_CUSTOM_ALLOCATOR InitMemoryMgr(); #endif #ifndef _WIN32 struct sigaction act; act.sa_sigaction = terminateHandler; act.sa_flags = SA_SIGINFO; sigaction(SIGTERM, &act, NULL); #ifdef FLUSHABLE_STREAMING struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_handler = dummyHandler; sa.sa_flags = 0; sigaction(SIGUSR1, &sa, NULL); #endif #endif /* * Initialize the platform independent data. * This will in turn initialize the platform specific data... */ if( RsEventHandler(rsINITIALIZE, nil) == rsEVENTERROR ) { return FALSE; } #ifdef _WIN32 /* * Get proper command line params, cmdLine passed to us does not * work properly under all circumstances... */ cmdLine = GetCommandLine(); /* * Parse command line into standard (argv, argc) parameters... */ argv = CommandLineToArgv(cmdLine, &argc); /* * Parse command line parameters (except program name) one at * a time BEFORE RenderWare initialization... */ #endif for(i=1; iGetLeftMouseJustDown()) // ++gGameState; // else if (CPad::GetPad(0)->GetEnterJustDown()) // ++gGameState; // else if (CPad::GetPad(0)->GetCharJustDown(' ')) // ++gGameState; // else if (CPad::GetPad(0)->GetAltJustDown()) // ++gGameState; // else if (CPad::GetPad(0)->GetTabJustDown()) // ++gGameState; break; } case GS_INIT_INTRO_MPEG: { //#ifndef NO_MOVIES // CloseClip(); // CoUninitialize(); //#endif // // if (CMenuManager::OS_Language == LANG_FRENCH || CMenuManager::OS_Language == LANG_GERMAN) // PlayMovieInWindow(cmdShow, "movies\\GTAtitlesGER.mpg"); // else // PlayMovieInWindow(cmdShow, "movies\\GTAtitles.mpg"); gGameState = GS_INTRO_MPEG; TRACE("gGameState = GS_INTRO_MPEG;"); break; } case GS_INTRO_MPEG: { // CPad::UpdatePads(); // // if (startupDeactivate || ControlsManager.GetJoyButtonJustDown() != 0) ++gGameState; // else if (CPad::GetPad(0)->GetLeftMouseJustDown()) // ++gGameState; // else if (CPad::GetPad(0)->GetEnterJustDown()) // ++gGameState; // else if (CPad::GetPad(0)->GetCharJustDown(' ')) // ++gGameState; // else if (CPad::GetPad(0)->GetAltJustDown()) // ++gGameState; // else if (CPad::GetPad(0)->GetTabJustDown()) // ++gGameState; break; } case GS_INIT_ONCE: { //CoUninitialize(); #ifdef PS2_MENU extern char version_name[64]; if ( CGame::frenchGame || CGame::germanGame ) LoadingScreen(NULL, version_name, "loadsc24"); else LoadingScreen(NULL, version_name, "loadsc0"); printf("Into TheGame!!!\n"); #else LoadingScreen(nil, nil, "loadsc0"); // LoadingScreen(nil, nil, "loadsc0"); // duplicate #endif if ( !CGame::InitialiseOnceAfterRW() ) RsGlobal.quit = TRUE; #ifdef PS2_MENU gGameState = GS_INIT_PLAYING_GAME; #else gGameState = GS_INIT_FRONTEND; TRACE("gGameState = GS_INIT_FRONTEND;"); #endif break; } #ifndef PS2_MENU case GS_INIT_FRONTEND: { LoadingScreen(nil, nil, "loadsc0"); // LoadingScreen(nil, nil, "loadsc0"); // duplicate FrontEndMenuManager.m_bGameNotLoaded = true; FrontEndMenuManager.m_bStartUpFrontEndRequested = true; if ( defaultFullscreenRes ) { defaultFullscreenRes = FALSE; FrontEndMenuManager.m_nPrefsVideoMode = GcurSelVM; FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; } gGameState = GS_FRONTEND; TRACE("gGameState = GS_FRONTEND;"); break; } case GS_FRONTEND: { if(!WindowIconified) RsEventHandler(rsFRONTENDIDLE, nil); #ifdef PS2_MENU if ( !FrontEndMenuManager.m_bMenuActive || TheMemoryCard.m_bWantToLoad ) #else if ( !FrontEndMenuManager.m_bMenuActive || FrontEndMenuManager.m_bWantToLoad ) #endif { gGameState = GS_INIT_PLAYING_GAME; TRACE("gGameState = GS_INIT_PLAYING_GAME;"); } #ifdef PS2_MENU if (TheMemoryCard.m_bWantToLoad ) #else if ( FrontEndMenuManager.m_bWantToLoad ) #endif { InitialiseGame(); FrontEndMenuManager.m_bGameNotLoaded = false; gGameState = GS_PLAYING_GAME; TRACE("gGameState = GS_PLAYING_GAME;"); } break; } #endif case GS_INIT_PLAYING_GAME: { #ifdef PS2_MENU CGame::Initialise("DATA\\GTA3.DAT"); //LoadingScreen("Starting Game", NULL, GetRandomSplashScreen()); if ( TheMemoryCard.CheckCardInserted(CARD_ONE) == CMemoryCard::NO_ERR_SUCCESS && TheMemoryCard.ChangeDirectory(CARD_ONE, TheMemoryCard.Cards[CARD_ONE].dir) && TheMemoryCard.FindMostRecentFileName(CARD_ONE, TheMemoryCard.MostRecentFile) == true && TheMemoryCard.CheckDataNotCorrupt(TheMemoryCard.MostRecentFile)) { strcpy(TheMemoryCard.LoadFileName, TheMemoryCard.MostRecentFile); TheMemoryCard.b_FoundRecentSavedGameWantToLoad = true; if (CMenuManager::m_PrefsLanguage != TheMemoryCard.GetLanguageToLoad()) { CMenuManager::m_PrefsLanguage = TheMemoryCard.GetLanguageToLoad(); TheText.Unload(); TheText.Load(); } CGame::currLevel = (eLevelName)TheMemoryCard.GetLevelToLoad(); } #else InitialiseGame(); FrontEndMenuManager.m_bGameNotLoaded = false; #endif gGameState = GS_PLAYING_GAME; TRACE("gGameState = GS_PLAYING_GAME;"); break; } case GS_PLAYING_GAME: { float ms = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); if ( RwInitialised ) { if (!FrontEndMenuManager.m_PrefsFrameLimiter || (1000.0f / (float)RsGlobal.maxFPS) < ms) RsEventHandler(rsIDLE, (void *)TRUE); } break; } } } else { if ( RwCameraBeginUpdate(Scene.camera) ) { RwCameraEndUpdate(Scene.camera); ForegroundApp = TRUE; RsEventHandler(rsACTIVATE, (void *)TRUE); } } } /* * About to shut down - block resize events again... */ RwInitialised = FALSE; FrontEndMenuManager.UnloadTextures(); #ifdef PS2_MENU if ( !(FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad)) break; #else if ( !FrontEndMenuManager.m_bWantToRestart ) break; #endif CPad::ResetCheats(); CPad::StopPadsShaking(); DMAudio.ChangeMusicMode(MUSICMODE_DISABLE); #ifdef PS2_MENU CGame::ShutDownForRestart(); #endif CTimer::Stop(); #ifdef PS2_MENU if (FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad) { if (TheMemoryCard.b_FoundRecentSavedGameWantToLoad) { FrontEndMenuManager.m_bWantToRestart = true; TheMemoryCard.m_bWantToLoad = true; } CGame::InitialiseWhenRestarting(); DMAudio.ChangeMusicMode(MUSICMODE_GAME); FrontEndMenuManager.m_bWantToRestart = false; continue; } CGame::ShutDown(); CTimer::Stop(); break; #else if ( FrontEndMenuManager.m_bWantToLoad ) { CGame::ShutDownForRestart(); CGame::InitialiseWhenRestarting(); DMAudio.ChangeMusicMode(MUSICMODE_GAME); LoadSplash(GetLevelSplashScreen(CGame::currLevel)); FrontEndMenuManager.m_bWantToLoad = false; } else { #ifndef MASTER if ( gbModelViewer ) CAnimViewer::Shutdown(); else #endif if ( gGameState == GS_PLAYING_GAME ) CGame::ShutDown(); CTimer::Stop(); if ( FrontEndMenuManager.m_bFirstTime == true ) { gGameState = GS_INIT_FRONTEND; TRACE("gGameState = GS_INIT_FRONTEND;"); } else { gGameState = GS_INIT_PLAYING_GAME; TRACE("gGameState = GS_INIT_PLAYING_GAME;"); } } FrontEndMenuManager.m_bFirstTime = false; FrontEndMenuManager.m_bWantToRestart = false; #endif } #ifndef MASTER if ( gbModelViewer ) CAnimViewer::Shutdown(); else #endif if ( gGameState == GS_PLAYING_GAME ) CGame::ShutDown(); DMAudio.Terminate(); _psFreeVideoModeList(); /* * Tidy up the 3D (RenderWare) components of the application... */ RsEventHandler(rsRWTERMINATE, nil); /* * Free the platform dependent data... */ RsEventHandler(rsTERMINATE, nil); #ifdef _WIN32 /* * Free the argv strings... */ free(argv); SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &SavedStickyKeys, SPIF_SENDCHANGE); SystemParametersInfo(SPI_SETPOWEROFFACTIVE, TRUE, nil, SPIF_SENDCHANGE); SystemParametersInfo(SPI_SETLOWPOWERACTIVE, TRUE, nil, SPIF_SENDCHANGE); SetErrorMode(0); #endif return 0; } /* ***************************************************************************** */ RwV2d leftStickPos; RwV2d rightStickPos; void CapturePad(RwInt32 padID) { int8 glfwPad = -1; if( padID == 0 ) glfwPad = PSGLOBAL(joy1id); else if( padID == 1) glfwPad = PSGLOBAL(joy2id); else assert("invalid padID"); if ( glfwPad == -1 ) return; int numButtons, numAxes; const uint8 *buttons = glfwGetJoystickButtons(glfwPad, &numButtons); const float *axes = glfwGetJoystickAxes(glfwPad, &numAxes); GLFWgamepadstate gamepadState; if (ControlsManager.m_bFirstCapture == false) { memcpy(&ControlsManager.m_OldState, &ControlsManager.m_NewState, sizeof(ControlsManager.m_NewState)); } else { // In case connected gamepad doesn't have L-R trigger axes. ControlsManager.m_NewState.mappedButtons[15] = ControlsManager.m_NewState.mappedButtons[16] = 0; } ControlsManager.m_NewState.buttons = (uint8*)buttons; ControlsManager.m_NewState.numButtons = numButtons; ControlsManager.m_NewState.id = glfwPad; ControlsManager.m_NewState.isGamepad = glfwGetGamepadState(glfwPad, &gamepadState); if (ControlsManager.m_NewState.isGamepad) { memcpy(&ControlsManager.m_NewState.mappedButtons, gamepadState.buttons, sizeof(gamepadState.buttons)); float lt = gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER], rt = gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER]; // glfw returns 0.0 for non-existent axises(which is bullocks) so we treat it as deadzone, and keep value of previous frame. // otherwise if this axis is present, -1 = released, 1 = pressed if (lt != 0.0f) ControlsManager.m_NewState.mappedButtons[15] = lt > -0.8f; if (rt != 0.0f) ControlsManager.m_NewState.mappedButtons[16] = rt > -0.8f; } // TODO? L2-R2 axes(not buttons-that's fine) on joysticks that don't have SDL gamepad mapping AREN'T handled, and I think it's impossible to do without mapping. if (ControlsManager.m_bFirstCapture == true) { memcpy(&ControlsManager.m_OldState, &ControlsManager.m_NewState, sizeof(ControlsManager.m_NewState)); ControlsManager.m_bFirstCapture = false; } RsPadButtonStatus bs; bs.padID = padID; RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); // Gamepad axes are guaranteed to return 0.0f if that particular gamepad doesn't have that axis. // And that's really good for sticks, because gamepads return 0.0 for them when sticks are in released state. if ( glfwPad != -1 ) { leftStickPos.x = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_X] : numAxes >= 1 ? axes[0] : 0.0f; leftStickPos.y = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y] : numAxes >= 2 ? axes[1] : 0.0f; rightStickPos.x = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X] : numAxes >= 3 ? axes[2] : 0.0f; rightStickPos.y = ControlsManager.m_NewState.isGamepad ? gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y] : numAxes >= 4 ? axes[3] : 0.0f; } { if (CPad::m_bMapPadOneToPadTwo) bs.padID = 1; RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); RsPadEventHandler(rsPADBUTTONDOWN, (void *)&bs); } { if (CPad::m_bMapPadOneToPadTwo) bs.padID = 1; CPad *pad = CPad::GetPad(bs.padID); if ( Abs(leftStickPos.x) > 0.3f ) pad->PCTempJoyState.LeftStickX = (int32)(leftStickPos.x * 128.0f); if ( Abs(leftStickPos.y) > 0.3f ) pad->PCTempJoyState.LeftStickY = (int32)(leftStickPos.y * 128.0f); if ( Abs(rightStickPos.x) > 0.3f ) pad->PCTempJoyState.RightStickX = (int32)(rightStickPos.x * 128.0f); if ( Abs(rightStickPos.y) > 0.3f ) pad->PCTempJoyState.RightStickY = (int32)(rightStickPos.y * 128.0f); } return; } void joysChangeCB(int jid, int event) { if (event == GLFW_CONNECTED && !IsThisJoystickBlacklisted(jid)) { if (PSGLOBAL(joy1id) == -1) { PSGLOBAL(joy1id) = jid; #ifdef DETECT_JOYSTICK_MENU strcpy(gSelectedJoystickName, glfwGetJoystickName(jid)); #endif // This is behind LOAD_INI_SETTINGS, because otherwise the Init call below will destroy/overwrite your bindings. #ifdef LOAD_INI_SETTINGS int count; glfwGetJoystickButtons(PSGLOBAL(joy1id), &count); ControlsManager.InitDefaultControlConfigJoyPad(count); #endif } else if (PSGLOBAL(joy2id) == -1) PSGLOBAL(joy2id) = jid; } else if (event == GLFW_DISCONNECTED) { if (PSGLOBAL(joy1id) == jid) { PSGLOBAL(joy1id) = -1; } else if (PSGLOBAL(joy2id) == jid) PSGLOBAL(joy2id) = -1; } } #if (defined(_MSC_VER)) int strcasecmp(const char* str1, const char* str2) { return _strcmpi(str1, str2); } int strncasecmp(const char *str1, const char *str2, size_t len) { return _strnicmp(str1, str2, len); } #endif #endif ================================================ FILE: src/skel/platform.h ================================================ #ifndef PLATFORM_H #define PLATFORM_H // Functions that's different on glfw/win etc. but have same signature (but if a function only used in win.cpp you can keep in win.h) #include "rwcore.h" #include "skeleton.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifdef _WIN32 extern RwUInt32 psTimer(void); #else extern double psTimer(void); #endif extern RwBool psInitialize(void); extern void psTerminate(void); extern void psCameraShowRaster(RwCamera *camera); extern RwBool psCameraBeginUpdate(RwCamera *camera); extern RwImage *psGrabScreen(RwCamera *camera); extern void psMouseSetPos(RwV2d *pos); extern RwBool psSelectDevice(); extern RwMemoryFunctions *psGetMemoryFunctions(void); /* install the platform specific file system */ extern RwBool psInstallFileSystem(void); /* Handle native texture support */ extern RwBool psNativeTextureSupport(void); extern void _InputTranslateShiftKeyUpDown(RsKeyCodes* rs); extern long _InputInitialiseMouse(bool exclusive); // returns HRESULT on Windows actually extern void _InputShutdownMouse(); extern bool _InputMouseNeedsExclusive(); extern void _InputInitialiseJoys(); extern void HandleExit(); extern void _psSelectScreenVM(RwInt32 videoMode); extern void InitialiseLanguage(); extern RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode); extern RwChar** _psGetVideoModeList(); extern RwInt32 _psGetNumVideModes(); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* PLATFORM_H */ ================================================ FILE: src/skel/skeleton.cpp ================================================ #include "common.h" #include #include #include #include #include "rwcore.h" #include "skeleton.h" #include "platform.h" #include "main.h" #include "MemoryHeap.h" static RwBool DefaultVideoMode = TRUE; RsGlobalType RsGlobal; #ifdef _WIN32 RwUInt32 #else double #endif RsTimer(void) { return psTimer(); } /* ***************************************************************************** */ void RsCameraShowRaster(RwCamera * camera) { psCameraShowRaster(camera); return; } /* ***************************************************************************** */ RwBool RsCameraBeginUpdate(RwCamera * camera) { return psCameraBeginUpdate(camera); } /* ***************************************************************************** */ RwImage* RsGrabScreen(RwCamera *camera) { return psGrabScreen(camera); } /* ***************************************************************************** */ RwBool RsRegisterImageLoader(void) { return TRUE; } /* ***************************************************************************** */ static RwBool RsSetDebug(void) { return TRUE; } /* ***************************************************************************** */ void RsMouseSetPos(RwV2d * pos) { psMouseSetPos(pos); return; } /* ***************************************************************************** */ RwBool RsSelectDevice(void) { return psSelectDevice(); } /* ***************************************************************************** */ RwBool RsInputDeviceAttach(RsInputDeviceType inputDevice, RsInputEventHandler inputEventHandler) { switch (inputDevice) { case rsKEYBOARD: { RsGlobal.keyboard.inputEventHandler = inputEventHandler; RsGlobal.keyboard.used = TRUE; break; } case rsMOUSE: { RsGlobal.mouse.inputEventHandler = inputEventHandler; RsGlobal.mouse.used = TRUE; break; } case rsPAD: { RsGlobal.pad.inputEventHandler = inputEventHandler; RsGlobal.pad.used = TRUE; break; } default: { return FALSE; } } return TRUE; } /* ***************************************************************************** */ static RwBool rsCommandLine(RwChar *arg) { RsEventHandler(rsFILELOAD, arg); return TRUE; } /* ***************************************************************************** */ static RwBool rsPreInitCommandLine(RwChar *arg) { if( !strcmp(arg, RWSTRING("-vms")) ) { DefaultVideoMode = FALSE; return TRUE; } #ifndef MASTER if (!strcmp(arg, RWSTRING("-animviewer"))) { gbModelViewer = TRUE; return TRUE; } #endif return FALSE; } /* ***************************************************************************** */ RsEventStatus RsKeyboardEventHandler(RsEvent event, void *param) { if (RsGlobal.keyboard.used) { return RsGlobal.keyboard.inputEventHandler(event, param); } return rsEVENTNOTPROCESSED; } /* ***************************************************************************** */ RsEventStatus RsPadEventHandler(RsEvent event, void *param) { if (RsGlobal.pad.used) { return RsGlobal.pad.inputEventHandler(event, param); } return rsEVENTNOTPROCESSED; } /* ***************************************************************************** */ RsEventStatus RsEventHandler(RsEvent event, void *param) { RsEventStatus result; RsEventStatus es; /* * Give the application an opportunity to override any events... */ es = AppEventHandler(event, param); /* * We never allow the app to replace the quit behaviour, * only to intercept... */ if (event == rsQUITAPP) { /* * Set the flag which causes the event loop to exit... */ RsGlobal.quit = TRUE; } if (es == rsEVENTNOTPROCESSED) { switch (event) { case rsSELECTDEVICE: result = (RsSelectDevice()? rsEVENTPROCESSED : rsEVENTERROR); break; case rsCOMMANDLINE: result = (rsCommandLine((RwChar *) param) ? rsEVENTPROCESSED : rsEVENTERROR); break; case rsPREINITCOMMANDLINE: result = (rsPreInitCommandLine((RwChar *) param) ? rsEVENTPROCESSED : rsEVENTERROR); break; case rsINITDEBUG: result = (RsSetDebug()? rsEVENTPROCESSED : rsEVENTERROR); break; case rsREGISTERIMAGELOADER: result = (RsRegisterImageLoader()? rsEVENTPROCESSED : rsEVENTERROR); break; case rsRWTERMINATE: RsRwTerminate(); result = (rsEVENTPROCESSED); break; case rsRWINITIALIZE: result = (RsRwInitialize(param) ? rsEVENTPROCESSED : rsEVENTERROR); break; case rsTERMINATE: RsTerminate(); result = (rsEVENTPROCESSED); break; case rsINITIALIZE: result = (RsInitialize()? rsEVENTPROCESSED : rsEVENTERROR); break; default: result = (es); break; } } else { result = (es); } return result; } /* ***************************************************************************** */ void RsRwTerminate(void) { /* Close RenderWare */ RwEngineStop(); RwEngineClose(); RwEngineTerm(); return; } /* ***************************************************************************** */ RwBool RsRwInitialize(void *displayID) { RwEngineOpenParams openParams; /* * Start RenderWare... */ if (!RwEngineInit(psGetMemoryFunctions(), 0, rsRESOURCESDEFAULTARENASIZE)) { return (FALSE); } /* * Install any platform specific file systems... */ psInstallFileSystem(); /* * Initialize debug message handling... */ RsEventHandler(rsINITDEBUG, nil); /* * Attach all plugins... */ if (RsEventHandler(rsPLUGINATTACH, nil) == rsEVENTERROR) { return (FALSE); } /* * Attach input devices... */ if (RsEventHandler(rsINPUTDEVICEATTACH, nil) == rsEVENTERROR) { return (FALSE); } openParams.displayID = displayID; if (!RwEngineOpen(&openParams)) { RwEngineTerm(); return (FALSE); } if (RsEventHandler(rsSELECTDEVICE, displayID) == rsEVENTERROR) { RwEngineClose(); RwEngineTerm(); return (FALSE); } if (!RwEngineStart()) { RwEngineClose(); RwEngineTerm(); return (FALSE); } /* * Register loaders for an image with a particular file extension... */ RsEventHandler(rsREGISTERIMAGELOADER, nil); psNativeTextureSupport(); RwTextureSetAutoMipmapping(TRUE); RwTextureSetMipmapping(FALSE); return TRUE; } /* ***************************************************************************** */ void RsTerminate(void) { psTerminate(); return; } /* ***************************************************************************** */ RwBool RsInitialize(void) { /* * Initialize Platform independent data... */ RwBool result; RsGlobal.appName = RWSTRING("GTA: Vice City"); RsGlobal.maximumWidth = DEFAULT_SCREEN_WIDTH; RsGlobal.maximumHeight = DEFAULT_SCREEN_HEIGHT; RsGlobal.width = DEFAULT_SCREEN_WIDTH; RsGlobal.height = DEFAULT_SCREEN_HEIGHT; RsGlobal.maxFPS = 30; RsGlobal.quit = FALSE; /* setup the keyboard */ RsGlobal.keyboard.inputDeviceType = rsKEYBOARD; RsGlobal.keyboard.inputEventHandler = nil; RsGlobal.keyboard.used = FALSE; /* setup the mouse */ RsGlobal.mouse.inputDeviceType = rsMOUSE; RsGlobal.mouse.inputEventHandler = nil; RsGlobal.mouse.used = FALSE; /* setup the pad */ RsGlobal.pad.inputDeviceType = rsPAD; RsGlobal.pad.inputEventHandler = nil; RsGlobal.pad.used = FALSE; result = psInitialize(); return result; } ================================================ FILE: src/skel/skeleton.h ================================================ #ifndef SKELETON_H #define SKELETON_H #include "rwcore.h" /* Default arena size depending on platform. */ #define rsRESOURCESDEFAULTARENASIZE (1 << 20) #if (!defined(RsSprintf)) #define RsSprintf rwsprintf #endif /* (!defined(RsSprintf)) */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #if (!defined(RSASSERT)) #define RSASSERT(_condition) /* No-op */ #endif /* (!defined(RSASSERT)) */ #define RSASSERTISTYPE(_f, _t) \ RSASSERT( (!(_f)) || ((((const RwObject *)(_f))->type)==(_t)) ) enum RsInputDeviceType { rsKEYBOARD, rsMOUSE, rsPAD }; typedef enum RsInputDeviceType RsInputDeviceType; enum RsEventStatus { rsEVENTERROR, rsEVENTPROCESSED, rsEVENTNOTPROCESSED }; typedef enum RsEventStatus RsEventStatus; enum RsEvent { rsCAMERASIZE, rsCOMMANDLINE, rsFILELOAD, rsINITDEBUG, rsINPUTDEVICEATTACH, rsLEFTBUTTONDOWN, rsLEFTBUTTONUP, rsMOUSEMOVE, rsMOUSEWHEELMOVE, rsPLUGINATTACH, rsREGISTERIMAGELOADER, rsRIGHTBUTTONDOWN, rsRIGHTBUTTONUP, _rs_13, _rs_14, _rs_15, _rs_16, _rs_17, _rs_18, _rs_19, _rs_20, rsRWINITIALIZE, rsRWTERMINATE, rsSELECTDEVICE, rsINITIALIZE, rsTERMINATE, rsIDLE, rsFRONTENDIDLE, rsKEYDOWN, rsKEYUP, rsQUITAPP, rsPADBUTTONDOWN, rsPADBUTTONUP, rsPADANALOGUELEFT, rsPADANALOGUELEFTRESET, rsPADANALOGUERIGHT, rsPADANALOGUERIGHTRESET, rsPREINITCOMMANDLINE, rsACTIVATE, }; typedef enum RsEvent RsEvent; typedef RsEventStatus (*RsInputEventHandler)(RsEvent event, void *param); typedef struct RsInputDevice RsInputDevice; struct RsInputDevice { RsInputDeviceType inputDeviceType; RwBool used; RsInputEventHandler inputEventHandler; }; typedef struct RsGlobalType RsGlobalType; struct RsGlobalType { const RwChar *appName; RwInt32 width; RwInt32 height; RwInt32 maximumWidth; RwInt32 maximumHeight; RwInt32 maxFPS; RwBool quit; void *ps; /* platform specific data */ RsInputDevice keyboard; RsInputDevice mouse; RsInputDevice pad; }; enum RsKeyCodes { rsESC = 1000, rsF1 = 1001, rsF2 = 1002, rsF3 = 1003, rsF4 = 1004, rsF5 = 1005, rsF6 = 1006, rsF7 = 1007, rsF8 = 1008, rsF9 = 1009, rsF10 = 1010, rsF11 = 1011, rsF12 = 1012, rsINS = 1013, rsDEL = 1014, rsHOME = 1015, rsEND = 1016, rsPGUP = 1017, rsPGDN = 1018, rsUP = 1019, rsDOWN = 1020, rsLEFT = 1021, rsRIGHT = 1022, rsDIVIDE = 1023, rsTIMES = 1024, rsPLUS = 1025, rsMINUS = 1026, rsPADDEL = 1027, rsPADEND = 1028, rsPADDOWN = 1029, rsPADPGDN = 1030, rsPADLEFT = 1031, rsPAD5 = 1032, rsNUMLOCK = 1033, rsPADRIGHT = 1034, rsPADHOME = 1035, rsPADUP = 1036, rsPADPGUP = 1037, rsPADINS = 1038, rsPADENTER = 1039, rsSCROLL = 1040, rsPAUSE = 1041, rsBACKSP = 1042, rsTAB = 1043, rsCAPSLK = 1044, rsENTER = 1045, rsLSHIFT = 1046, rsRSHIFT = 1047, rsSHIFT = 1048, rsLCTRL = 1049, rsRCTRL = 1050, rsLALT = 1051, rsRALT = 1052, rsLWIN = 1053, rsRWIN = 1054, rsAPPS = 1055, rsNULL = 1056, rsMOUSELEFTBUTTON = 1, rsMOUSMIDDLEBUTTON = 2, rsMOUSERIGHTBUTTON = 3, rsMOUSEWHEELUPBUTTON = 4, rsMOUSEWHEELDOWNBUTTON = 5, rsMOUSEX1BUTTON = 6, rsMOUSEX2BUTTON = 7, }; typedef enum RsKeyCodes RsKeyCodes; typedef struct RsKeyStatus RsKeyStatus; struct RsKeyStatus { RwInt32 keyCharCode; }; typedef struct RsPadButtonStatus RsPadButtonStatus; struct RsPadButtonStatus { RwInt32 padID; }; enum RsPadButtons { rsPADNULL = 0, rsPADBUTTON1 = 1, rsPADBUTTON2 = 2, rsPADBUTTON3 = 3, rsPADBUTTON4 = 4, rsPADBUTTON5 = 5, rsPADBUTTON6 = 6, rsPADBUTTON7 = 7, rsPADBUTTON8 = 8, rsPADSELECT = 9, rsPADBUTTONA1 = 10, rsPADBUTTONA2 = 11, rsPADSTART = 12, rsPADDPADUP = 13, rsPADDPADRIGHT = 14, rsPADDPADDOWN = 15, rsPADDPADLEFT = 16, }; typedef enum RsPadButtons RsPadButtons; extern RsGlobalType RsGlobal; extern RsEventStatus AppEventHandler(RsEvent event, void *param); extern RwBool AttachInputDevices(void); extern RsEventStatus RsEventHandler(RsEvent event, void *param); extern RsEventStatus RsKeyboardEventHandler(RsEvent event, void *param); extern RsEventStatus RsPadEventHandler(RsEvent event, void *param); extern RwBool RsInitialize(void); extern RwBool RsRegisterImageLoader(void); extern RwBool RsRwInitialize(void *param); extern RwBool RsSelectDevice(void); extern RwBool RsInputDeviceAttach(RsInputDeviceType inputDevice, RsInputEventHandler inputEventHandler); #ifdef _WIN32 extern RwUInt32 #else extern double #endif RsTimer(void); extern void RsCameraShowRaster(RwCamera *camera); extern RwBool RsCameraBeginUpdate(RwCamera *camera); //TODO //extern void //RsMouseSetVisibility(RwBool visible); extern RwImage* RsGrabScreen(RwCamera *camera); extern void RsMouseSetPos(RwV2d *pos); extern void RsRwTerminate(void); extern void RsTerminate(void); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* SKELETON_H */ ================================================ FILE: src/skel/win/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by dungeon.rc // #define IDD_DIALOG1 104 #define IDC_DEVICESEL 1000 #define IDC_VIDMODE 1001 #define IDEXIT 1002 #define IDC_SELECTDEVICE 1005 #define IDI_MAIN_ICON 100 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 104 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: src/skel/win/win.cpp ================================================ #if defined RW_D3D9 || defined RWLIBS || defined __MWERKS__ #define _WIN32_WINDOWS 0x0500 #define WINVER 0x0500 #include #include #include #include #include #include #include #include #include #pragma warning( push ) #pragma warning( disable : 4005) #ifdef __MWERKS__ #define MAPVK_VK_TO_CHAR (2) // this is missing from codewarrior win32 headers - but it gets used ... how? #endif #include #include #pragma warning( pop ) #define WM_GRAPHNOTIFY WM_USER+13 #ifndef USE_D3D9 #pragma comment( lib, "d3d8.lib" ) #endif #pragma comment( lib, "ddraw.lib" ) #pragma comment( lib, "Winmm.lib" ) #pragma comment( lib, "dxguid.lib" ) #pragma comment( lib, "strmiids.lib" ) #pragma comment( lib, "dinput8.lib" ) #define WITHD3D #define WITHDINPUT #include "common.h" #if (defined(_MSC_VER)) #include #endif /* (defined(_MSC_VER)) */ #include #include "rwcore.h" #include "resource.h" #include "skeleton.h" #include "platform.h" #include "crossplatform.h" #define MAX_SUBSYSTEMS (16) static RwBool ForegroundApp = TRUE; static RwBool RwInitialised = FALSE; static RwSubSystemInfo GsubSysInfo[MAX_SUBSYSTEMS]; static RwInt32 GnumSubSystems = 0; static RwInt32 GcurSel = 0, GcurSelVM = 0; static RwBool startupDeactivate; static RwBool useDefault; /* Class name for the MS Window's window class. */ static const RwChar *AppClassName = RWSTRING("Grand theft auto 3"); static psGlobalType PsGlobal; #define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) #undef MAKEPOINTS #define MAKEPOINTS(l) (*((POINTS /*FAR*/ *)&(l))) #define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; } #define JIF(x) if (FAILED(hr=(x))) \ {debug(TEXT("FAILED(hr=0x%x) in ") TEXT(#x) TEXT("\n"), hr); return;} #include "main.h" #include "FileMgr.h" #include "Text.h" #include "Pad.h" #include "Timer.h" #include "DMAudio.h" #include "ControllerConfig.h" #include "Frontend.h" #include "Game.h" #include "PCSave.h" #include "AnimViewer.h" #include "MemoryMgr.h" #ifdef PS2_MENU #include "MemoryCard.h" #include "Font.h" #endif VALIDATE_SIZE(psGlobalType, 0x28); // DirectShow interfaces IGraphBuilder *pGB = nil; IMediaControl *pMC = nil; IMediaEventEx *pME = nil; IVideoWindow *pVW = nil; IMediaSeeking *pMS = nil; DWORD dwDXVersion; SIZE_T _dwMemTotalPhys; size_t _dwMemAvailPhys; SIZE_T _dwMemTotalVirtual; SIZE_T _dwMemAvailVirtual; DWORD _dwMemTotalVideo; DWORD _dwMemAvailVideo; DWORD _dwOperatingSystemVersion; RwUInt32 gGameState; CJoySticks AllValidWinJoys; #ifdef DETECT_JOYSTICK_MENU char gSelectedJoystickName[128] = ""; #endif // What is that for anyway? #ifndef IMPROVED_VIDEOMODE static RwBool defaultFullscreenRes = TRUE; #else static RwBool defaultFullscreenRes = FALSE; static RwInt32 bestWndMode = -1; #endif CJoySticks::CJoySticks() { for (int i = 0; i < MAX_JOYSTICKS; i++) { ClearJoyInfo(i); } } void CJoySticks::ClearJoyInfo(int joyID) { m_aJoys[joyID].m_State = JOYPAD_UNUSED; m_aJoys[joyID].m_bInitialised = false; m_aJoys[joyID].m_bHasAxisZ = false; m_aJoys[joyID].m_bHasAxisR = false; } /* ***************************************************************************** */ void _psCreateFolder(LPCSTR path) { HANDLE hfle = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, nil); if ( hfle == INVALID_HANDLE_VALUE ) CreateDirectory(path, nil); else CloseHandle(hfle); } /* ***************************************************************************** */ const char *_psGetUserFilesFolder() { #ifdef USE_MY_DOCUMENTS HKEY hKey = NULL; static CHAR szUserFiles[256]; if ( RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_SPECIAL_FOLDERS, REG_OPTION_RESERVED, KEY_READ, &hKey) == ERROR_SUCCESS ) { DWORD KeyType; DWORD KeycbData = sizeof(szUserFiles); if ( RegQueryValueEx(hKey, "Personal", NULL, &KeyType, (LPBYTE)szUserFiles, &KeycbData) == ERROR_SUCCESS ) { RegCloseKey(hKey); strcat(szUserFiles, "\\GTA Vice City User Files"); _psCreateFolder(szUserFiles); return szUserFiles; } RegCloseKey(hKey); } strcpy(szUserFiles, "data"); return szUserFiles; #else static CHAR szUserFiles[256]; strcpy(szUserFiles, "userfiles"); _psCreateFolder(szUserFiles); return szUserFiles; #endif } /* ***************************************************************************** */ RwBool psCameraBeginUpdate(RwCamera *camera) { if ( !RwCameraBeginUpdate(Scene.camera) ) { ForegroundApp = FALSE; RsEventHandler(rsACTIVATE, (void *)FALSE); return FALSE; } return TRUE; } /* ***************************************************************************** */ void psCameraShowRaster(RwCamera *camera) { #ifdef LEGACY_MENU_OPTIONS if (FrontEndMenuManager.m_PrefsVsync || FrontEndMenuManager.m_bMenuActive) #else if (FrontEndMenuManager.m_PrefsFrameLimiter || FrontEndMenuManager.m_bMenuActive) #endif RwCameraShowRaster(camera, PSGLOBAL(window), rwRASTERFLIPWAITVSYNC); else RwCameraShowRaster(camera, PSGLOBAL(window), rwRASTERFLIPDONTWAIT); return; } /* ***************************************************************************** */ RwImage * psGrabScreen(RwCamera *pCamera) { #ifndef LIBRW RwRaster *pRaster = RwCameraGetRaster(pCamera); if (RwImage *pImage = RwImageCreate(pRaster->width, pRaster->height, 32)) { RwImageAllocatePixels(pImage); RwImageSetFromRaster(pImage, pRaster); return pImage; } #else rw::Image *image = RwCameraGetRaster(pCamera)->toImage(); image->removeMask(); if(image) return image; #endif return nil; } /* ***************************************************************************** */ RwUInt32 psTimer(void) { RwUInt32 time; TIMECAPS TimeCaps; timeGetDevCaps(&TimeCaps, sizeof(TIMECAPS)); timeBeginPeriod(TimeCaps.wPeriodMin); time = (RwUInt32) timeGetTime(); timeEndPeriod(TimeCaps.wPeriodMin); return time; } /* ***************************************************************************** */ void psMouseSetPos(RwV2d *pos) { POINT point; point.x = (RwInt32) pos->x; point.y = (RwInt32) pos->y; ClientToScreen(PSGLOBAL(window), &point); SetCursorPos(point.x, point.y); PSGLOBAL(lastMousePos.x) = (RwInt32)pos->x; PSGLOBAL(lastMousePos.y) = (RwInt32)pos->y; return; } /* ***************************************************************************** */ RwMemoryFunctions* psGetMemoryFunctions(void) { #ifdef USE_CUSTOM_ALLOCATOR return &memFuncs; #else return nil; #endif } /* ***************************************************************************** */ RwBool psInstallFileSystem(void) { return (TRUE); } /* ***************************************************************************** */ RwBool psNativeTextureSupport(void) { return RwD3D8DeviceSupportsDXTTexture(); } /* ***************************************************************************** */ static HWND InitInstance(HANDLE instance) { /* * Perform any necessary initialization for this instance of the * application. * * Create the MS Window's window instance for this application. The * initial window size is given by the defined camera size. The window * is not given a title as we set it during Init3D() with information * about the version of RenderWare being used. */ RECT rect; rect.left = rect.top = 0; rect.right = RsGlobal.maximumWidth; rect.bottom = RsGlobal.maximumHeight; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); return CreateWindow(AppClassName, RsGlobal.appName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, (HWND)nil, (HMENU)nil, (HINSTANCE)instance, nil); } void _GetVideoMemInfo(LPDWORD total, LPDWORD avaible) { HRESULT hr; LPDIRECTDRAW7 pDD7; hr = DirectDrawCreateEx(nil, (VOID**)&pDD7, IID_IDirectDraw7, nil); if ( FAILED(hr) ) return; DDSCAPS2 caps; ZeroMemory(&caps, sizeof(DDSCAPS2)); caps.dwCaps = DDSCAPS_VIDEOMEMORY; pDD7->GetAvailableVidMem(&caps, total, avaible); pDD7->Release(); } /* ***************************************************************************** */ typedef HRESULT(WINAPI * DIRECTDRAWCREATEEX)( GUID*, VOID**, REFIID, IUnknown* ); //----------------------------------------------------------------------------- // Name: GetDXVersion() // Desc: This function returns the DirectX version number as follows: // 0x0000 = No DirectX installed // 0x0700 = At least DirectX 7 installed. // 0x0800 = At least DirectX 8 installed. // // Please note that this code is intended as a general guideline. Your // app will probably be able to simply query for functionality (via // QueryInterface) for one or two components. // // Please also note: // "if( dwDXVersion != 0x500 ) return FALSE;" is VERY BAD. // "if( dwDXVersion < 0x500 ) return FALSE;" is MUCH BETTER. // to ensure your app will run on future releases of DirectX. //----------------------------------------------------------------------------- DWORD GetDXVersion() { DIRECTDRAWCREATEEX DirectDrawCreateEx = NULL; HINSTANCE hDDrawDLL = nil; HINSTANCE hD3D8DLL = nil; HINSTANCE hDPNHPASTDLL = NULL; DWORD dwDXVersion = 0; //HRESULT hr; // First see if DDRAW.DLL even exists. hDDrawDLL = LoadLibrary( "DDRAW.DLL" ); if( hDDrawDLL == nil ) { dwDXVersion = 0; OutputDebugString( "Couldn't LoadLibrary DDraw\r\n" ); return dwDXVersion; } //------------------------------------------------------------------------- // DirectX 7.0 Checks //------------------------------------------------------------------------- // Check for DirectX 7 by creating a DDraw7 object LPDIRECTDRAW7 pDD7; DirectDrawCreateEx = (DIRECTDRAWCREATEEX)GetProcAddress( hDDrawDLL, "DirectDrawCreateEx" ); if( nil == DirectDrawCreateEx ) { FreeLibrary( hDDrawDLL ); OutputDebugString( "Couldn't GetProcAddress DirectDrawCreateEx\r\n" ); return dwDXVersion; } if( FAILED( DirectDrawCreateEx( nil, (VOID**)&pDD7, IID_IDirectDraw7, nil ) ) ) { FreeLibrary( hDDrawDLL ); OutputDebugString( "Couldn't DirectDrawCreateEx\r\n" ); return dwDXVersion; } // DDraw7 was created successfully. We must be at least DX7.0 dwDXVersion = 0x700; pDD7->Release(); #ifdef USE_D3D9 HINSTANCE hD3D9DLL = LoadLibrary("D3D9.DLL"); if (hD3D9DLL != nil) { FreeLibrary(hDDrawDLL); FreeLibrary(hD3D9DLL); dwDXVersion = 0x900; return dwDXVersion; } #endif //------------------------------------------------------------------------- // DirectX 8.0 Checks //------------------------------------------------------------------------- // Simply see if D3D8.dll exists. hD3D8DLL = LoadLibrary( "D3D8.DLL" ); if( hD3D8DLL == nil ) { FreeLibrary( hDDrawDLL ); OutputDebugString( "Couldn't LoadLibrary D3D8.DLL\r\n" ); return dwDXVersion; } // D3D8.dll exists. We must be at least DX8.0 dwDXVersion = 0x800; //------------------------------------------------------------------------- // DirectX 8.1 Checks //------------------------------------------------------------------------- // Simply see if dpnhpast.dll exists. hDPNHPASTDLL = LoadLibrary( "dpnhpast.dll" ); if( hDPNHPASTDLL == nil ) { FreeLibrary( hDPNHPASTDLL ); OutputDebugString( "Couldn't LoadLibrary dpnhpast.dll\r\n" ); return dwDXVersion; } // dpnhpast.dll exists. We must be at least DX8.1 dwDXVersion = 0x801; //------------------------------------------------------------------------- // End of checking for versions of DirectX //------------------------------------------------------------------------- // Close open libraries and return FreeLibrary( hDDrawDLL ); FreeLibrary( hD3D8DLL ); return dwDXVersion; } /* ***************************************************************************** */ #ifndef _WIN64 static char cpuvendor[16] = "UnknownVendr"; __declspec(naked) const char * _psGetCpuVendr() { __asm { push ebx xor eax, eax cpuid mov dword ptr [cpuvendor+0], ebx mov dword ptr [cpuvendor+4], edx mov dword ptr [cpuvendor+8], ecx mov eax, offset cpuvendor pop ebx retn } } /* ***************************************************************************** */ __declspec(naked) RwUInt32 _psGetCpuFeatures() { __asm { mov eax, 1 cpuid mov eax, edx retn } } /* ***************************************************************************** */ __declspec(naked) RwUInt32 _psGetCpuFeaturesEx() { __asm { mov eax, 80000000h cpuid cmp eax, 80000000h jbe short _NOEX mov eax, 80000001h cpuid mov eax, edx jmp short _RETEX _NOEX: xor eax, eax mov eax, eax _RETEX: retn } } #ifdef __MWERKS__ #pragma dont_inline on #endif void _psPrintCpuInfo() { RwUInt32 features = _psGetCpuFeatures(); RwUInt32 FeaturesEx = _psGetCpuFeaturesEx(); debug("Running on a %s", _psGetCpuVendr()); if ( features & 0x800000 ) debug("with MMX"); if ( features & 0x2000000 ) debug("with SSE"); if ( FeaturesEx & 0x80000000 ) debug("with 3DNow"); } #ifdef __MWERKS__ #pragma dont_inline off #endif #endif /* ***************************************************************************** */ #ifdef UNDER_CE #define CMDSTR LPWSTR #else #define CMDSTR LPSTR #endif /* ***************************************************************************** */ RwBool psInitialize(void) { PsGlobal.lastMousePos.x = PsGlobal.lastMousePos.y = 0.0f; RsGlobal.ps = &PsGlobal; PsGlobal.fullScreen = FALSE; PsGlobal.dinterface = nil; PsGlobal.mouse = nil; PsGlobal.joy1 = nil; PsGlobal.joy2 = nil; CFileMgr::Initialise(); #ifdef PS2_MENU CPad::Initialise(); CPad::GetPad(0)->Mode = 0; CGame::frenchGame = false; CGame::germanGame = false; CGame::nastyGame = true; CMenuManager::m_PrefsAllowNastyGame = true; WORD lang = PRIMARYLANGID(GetSystemDefaultLCID()); if ( lang == LANG_ITALIAN ) CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; else if ( lang == LANG_SPANISH ) CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; else if ( lang == LANG_GERMAN ) { CGame::germanGame = true; CGame::nastyGame = false; CMenuManager::m_PrefsAllowNastyGame = false; CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; } else if ( lang == LANG_FRENCH ) { CGame::frenchGame = true; CGame::nastyGame = false; CMenuManager::m_PrefsAllowNastyGame = false; CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; } else CMenuManager::m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; FrontEndMenuManager.InitialiseMenuContentsAfterLoadingGame(); TheMemoryCard.Init(); #else C_PcSave::SetSaveDirectory(_psGetUserFilesFolder()); InitialiseLanguage(); #endif gGameState = GS_START_UP; TRACE("gGameState = GS_START_UP"); #ifndef _WIN64 _psPrintCpuInfo(); #endif OSVERSIONINFO verInfo; verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&verInfo); _dwOperatingSystemVersion = OS_WIN95; if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) { if ( verInfo.dwMajorVersion == 4 ) { debug("Operating System is WinNT\n"); _dwOperatingSystemVersion = OS_WINNT; } else if ( verInfo.dwMajorVersion == 5 ) { debug("Operating System is Win2000\n"); _dwOperatingSystemVersion = OS_WIN2000; } else if ( verInfo.dwMajorVersion > 5 ) { debug("Operating System is WinXP or greater\n"); _dwOperatingSystemVersion = OS_WINXP; } } else if ( verInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) { if ( verInfo.dwMajorVersion > 4 || verInfo.dwMajorVersion == 4 && verInfo.dwMinorVersion != 0 ) { debug("Operating System is Win98\n"); _dwOperatingSystemVersion = OS_WIN98; } else { debug("Operating System is Win95\n"); _dwOperatingSystemVersion = OS_WIN95; } } #ifndef PS2_MENU FrontEndMenuManager.LoadSettings(); #endif dwDXVersion = GetDXVersion(); debug("DirectX version 0x%x\n", dwDXVersion); if ( _dwOperatingSystemVersion == OS_WIN95 ) { MessageBoxW(nil, (LPCWSTR)TheText.Get("WIN_95"), // Grand Theft Auto III cannot run on Windows 95 (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto III MB_OK); return FALSE; } if ( dwDXVersion < 0x801 ) { MessageBoxW(nil, (LPCWSTR)TheText.Get("WIN_DX"), // Grand Theft Auto III requires at least DirectX version 8.1 (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto III MB_OK); return FALSE; } MEMORYSTATUS memstats; GlobalMemoryStatus(&memstats); _dwMemTotalPhys = memstats.dwTotalPhys; _dwMemAvailPhys = memstats.dwAvailPhys; _dwMemTotalVirtual = memstats.dwTotalVirtual; _dwMemAvailVirtual = memstats.dwAvailVirtual; _GetVideoMemInfo(&_dwMemTotalVideo, &_dwMemAvailVideo); #ifdef FIX_BUGS debug("Physical memory size %lu\n", _dwMemTotalPhys); debug("Available physical memory %lu\n", _dwMemAvailPhys); debug("Video memory size %lu\n", _dwMemTotalVideo); debug("Available video memory %lu\n", _dwMemAvailVideo); #else debug("Physical memory size %d\n", _dwMemTotalPhys); debug("Available physical memory %d\n", _dwMemAvailPhys); debug("Video memory size %d\n", _dwMemTotalVideo); debug("Available video memory %d\n", _dwMemAvailVideo); #endif if ( _dwMemAvailVideo < (12 * 1024 * 1024) /*12 MB*/ ) { MessageBoxW(nil, (LPCWSTR)TheText.Get("WIN_VDM"), // Grand Theft Auto III requires at least 12MB of available video memory (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto III MB_OK); return FALSE; } TheText.Unload(); return TRUE; } /* ***************************************************************************** */ void psTerminate(void) { return; } /* ***************************************************************************** */ static RwChar **_VMList; RwInt32 _psGetNumVideModes() { return RwEngineGetNumVideoModes(); } /* ***************************************************************************** */ RwBool _psFreeVideoModeList() { RwInt32 numModes; RwInt32 i; numModes = _psGetNumVideModes(); if ( _VMList == nil ) return TRUE; for ( i = 0; i < numModes; i++ ) { RwFree(_VMList[i]); } RwFree(_VMList); _VMList = nil; return TRUE; } /* ***************************************************************************** */ RwChar **_psGetVideoModeList() { RwInt32 numModes; RwInt32 i; if ( _VMList != nil ) { return _VMList; } numModes = RwEngineGetNumVideoModes(); _VMList = (RwChar **)RwCalloc(numModes, sizeof(RwChar*)); for ( i = 0; i < numModes; i++ ) { RwVideoMode vm; RwEngineGetVideoModeInfo(&vm, i); if ( vm.flags & rwVIDEOMODEEXCLUSIVE ) { if ( vm.width >= 640 && vm.height >= 480 && (vm.width == 640 && vm.height == 480) || !(vm.flags & rwVIDEOMODEEXCLUSIVE) || (_dwMemTotalVideo - vm.depth * vm.height * vm.width / 8) > (12 * 1024 * 1024)/*12 MB*/ ) { _VMList[i] = (RwChar*)RwCalloc(100, sizeof(RwChar)); rwsprintf(_VMList[i],"%lu X %lu X %lu", vm.width, vm.height, vm.depth); } else _VMList[i] = nil; } else _VMList[i] = nil; } return _VMList; } /* ***************************************************************************** */ void _psSelectScreenVM(RwInt32 videoMode) { RwTexDictionarySetCurrent( nil ); FrontEndMenuManager.UnloadTextures(); if ( !_psSetVideoMode(RwEngineGetCurrentSubSystem(), videoMode) ) { RsGlobal.quit = TRUE; ShowWindow(PSGLOBAL(window), SW_HIDE); MessageBoxW(nil, (LPCWSTR)TheText.Get("WIN_RSZ"), // Failed to select new screen resolution (LPCWSTR)TheText.Get("WIN_TTL"), // Grand Theft Auto III MB_OK); } else FrontEndMenuManager.LoadAllTextures(); } /* ***************************************************************************** */ void WaitForState(FILTER_STATE State) { HRESULT hr; ASSERT(pMC != nil); // Make sure we have switched to the required state LONG lfs; do { hr = pMC->GetState(10, &lfs); } while (State != lfs); } /* ***************************************************************************** */ void HandleGraphEvent(void) { LONG evCode, evParam1, evParam2; HRESULT hr=S_OK; ASSERT(pME != nil); // Process all queued events while (SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *)&evParam1, (LONG_PTR *)&evParam2, 0))) { // Free memory associated with callback, since we're not using it hr = pME->FreeEventParams(evCode, evParam1, evParam2); // If this is the end of the clip, reset to beginning if (EC_COMPLETE == evCode) { switch (gGameState) { case GS_LOGO_MPEG: { gGameState = GS_INIT_INTRO_MPEG; TRACE("gGameState = GS_INIT_INTRO_MPEG"); break; } case GS_INTRO_MPEG: { gGameState = GS_INIT_ONCE; TRACE("gGameState = GS_INIT_ONCE"); break; } default: { break; } } pME->SetNotifyWindow((OAHWND)NULL, 0, 0); } } } /* ***************************************************************************** */ LRESULT CALLBACK MainWndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { POINTS points; static BOOL noMemory = FALSE; switch( message ) { case WM_SETCURSOR: { ShowCursor(FALSE); SetCursor(nil); break; // is this correct ? } case WM_SIZE: { RwRect r; r.x = 0; r.y = 0; r.w = LOWORD(lParam); r.h = HIWORD(lParam); if (RwInitialised && r.h > 0 && r.w > 0) { RsEventHandler(rsCAMERASIZE, &r); if (r.w != LOWORD(lParam) && r.h != HIWORD(lParam)) { WINDOWPLACEMENT wp; /* failed to create window of required size */ noMemory = TRUE; /* stop re-sizing */ ReleaseCapture(); /* handle maximised window */ GetWindowPlacement(window, &wp); if (wp.showCmd == SW_SHOWMAXIMIZED) { SendMessage(window, WM_WINDOWPOSCHANGED, 0, 0); } } else { noMemory = FALSE; } } return 0L; } case WM_SIZING: { /* * Handle event to ensure window contents are displayed during re-size * as this can be disabled by the user, then if there is not enough * memory things don't work. */ RECT *newPos = (LPRECT) lParam; RECT rect; /* redraw window */ if (RwInitialised && gGameState == GS_PLAYING_GAME) { RsEventHandler(rsIDLE, (void *)TRUE); } /* Manually resize window */ rect.left = rect.top = 0; rect.bottom = newPos->bottom - newPos->top; rect.right = newPos->right - newPos->left; SetWindowPos(window, HWND_TOP, rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top), SWP_NOMOVE); return 0L; } case WM_LBUTTONDOWN: { SetCapture(window); return 0L; } case WM_RBUTTONDOWN: { SetCapture(window); return 0L; } case WM_MBUTTONDOWN: { SetCapture(window); return 0L; } case WM_MOUSEWHEEL: { return 0L; } case WM_MOUSEMOVE: { points = MAKEPOINTS(lParam); FrontEndMenuManager.m_nMouseTempPosX = points.x; FrontEndMenuManager.m_nMouseTempPosY = points.y; return 0L; } case WM_LBUTTONUP: { ReleaseCapture(); return 0L; } case WM_RBUTTONUP: { ReleaseCapture(); return 0L; } case WM_MBUTTONUP: { ReleaseCapture(); return 0L; } case WM_KEYDOWN: { RsKeyCodes ks; if ( _InputTranslateKey(&ks, lParam, wParam) ) RsKeyboardEventHandler(rsKEYDOWN, &ks); if ( wParam == VK_SHIFT ) _InputTranslateShiftKeyUpDown(&ks); #ifdef FIX_BUGS break; #else return 0L; #endif } case WM_KEYUP: { RsKeyCodes ks; if ( _InputTranslateKey(&ks, lParam, wParam) ) RsKeyboardEventHandler(rsKEYUP, &ks); if ( wParam == VK_SHIFT ) _InputTranslateShiftKeyUpDown(&ks); #ifdef FIX_BUGS break; #else return 0L; #endif } case WM_SYSKEYDOWN: { RsKeyCodes ks; if ( _InputTranslateKey(&ks, lParam, wParam) ) RsKeyboardEventHandler(rsKEYDOWN, &ks); if ( wParam == VK_SHIFT ) _InputTranslateShiftKeyUpDown(&ks); #ifdef FIX_BUGS break; #else return 0L; #endif } case WM_SYSKEYUP: { RsKeyCodes ks; if ( _InputTranslateKey(&ks, lParam, wParam) ) RsKeyboardEventHandler(rsKEYUP, &ks); if ( wParam == VK_SHIFT ) _InputTranslateShiftKeyUpDown(&ks); #ifdef FIX_BUGS break; #else return 0L; #endif } case WM_ACTIVATEAPP: { switch ( gGameState ) { case GS_LOGO_MPEG: case GS_INTRO_MPEG: { ASSERT(pMC != nil); LONG state; pMC->GetState(10, &state); if ( !(BOOL)wParam ) // losing activation { if ( state == State_Running && pMC != nil ) { HRESULT hr = pMC->Pause(); if (hr == S_FALSE) OutputDebugString("Failed to pause the MPEG"); else WaitForState(State_Paused); } } else { CenterVideo(); if ( state != State_Running && pMC != nil ) { HRESULT hr = pMC->Run(); if ( hr == S_FALSE ) OutputDebugString("Failed to run the MPEG"); else { WaitForState(State_Running); SetFocus(PSGLOBAL(window)); } } } break; } case GS_START_UP: { if ( !(BOOL)wParam && PSGLOBAL(fullScreen) ) // losing activation startupDeactivate = TRUE; break; } } CPad::GetPad(0)->Clear(false); CPad::GetPad(1)->Clear(false); return 0L; } case WM_TIMER: { return 0L; } case WM_GRAPHNOTIFY: { if (gGameState == GS_INTRO_MPEG || gGameState == GS_LOGO_MPEG) HandleGraphEvent(); break; } case WM_CLOSE: case WM_DESTROY: { /* * Quit message handling. */ ClipCursor(nil); _InputShutdown(); PostQuitMessage(0); return 0L; } case WM_DEVICECHANGE: { if( wParam == DBT_DEVICEREMOVECOMPLETE ) { PDEV_BROADCAST_HDR pDev = (PDEV_BROADCAST_HDR)lParam; if (pDev->dbch_devicetype != DBT_DEVTYP_VOLUME) break; if ( DMAudio.IsAudioInitialised() ) { PDEV_BROADCAST_VOLUME pVol = (PDEV_BROADCAST_VOLUME)pDev; if ( pVol->dbcv_flags & DBTF_MEDIA ) { char c = DMAudio.GetCDAudioDriveLetter(); if ( c >= 'A' && pVol->dbcv_unitmask & (1 << (c - 'A')) ) { OutputDebugString("About to check CD drive..."); while ( true ) { FrontEndMenuManager.WaitForUserCD(); if ( !FrontEndMenuManager.m_bQuitGameNoCD ) { if ( DMAudio.CheckForAnAudioFileOnCD() ) { OutputDebugString("GTA3 Audio CD has been inserted"); break; } } else { OutputDebugString("Exiting game as Audio CD was not inserted"); break; } } } } } } break; } #ifdef FIX_BUGS // game turns on menu when focus is re-gained rather than lost case WM_KILLFOCUS: #else case WM_SETFOCUS: #endif { CGame::InitAfterFocusLoss(); break; } } /* * Let Windows handle all other messages. */ return DefWindowProc(window, message, wParam, lParam); } /* ***************************************************************************** */ static BOOL InitApplication(HANDLE instance) { /* * Perform any necessary MS Windows application initialization. Basically, * this means registering the window class for this application. */ WNDCLASS windowClass; windowClass.style = CS_BYTEALIGNWINDOW; windowClass.lpfnWndProc = (WNDPROC)MainWndProc; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hInstance = (HINSTANCE)instance; windowClass.hIcon = LoadIcon((HINSTANCE)instance, (LPCSTR)IDI_MAIN_ICON); windowClass.hCursor = LoadCursor(nil, IDC_ARROW); windowClass.hbrBackground = nil; windowClass.lpszMenuName = NULL; windowClass.lpszClassName = AppClassName; return RegisterClass(&windowClass); } /* ***************************************************************************** */ RwBool IsForegroundApp() { return !!ForegroundApp; } UINT GetBestRefreshRate(UINT width, UINT height, UINT depth) { #ifdef USE_D3D9 LPDIRECT3D9 d3d = Direct3DCreate9(D3D_SDK_VERSION); #else LPDIRECT3D8 d3d = Direct3DCreate8(D3D_SDK_VERSION); #endif ASSERT(d3d != nil); UINT refreshRate = INT_MAX; D3DFORMAT format; if ( depth == 32 ) format = D3DFMT_X8R8G8B8; else if ( depth == 24 ) format = D3DFMT_R8G8B8; else format = D3DFMT_R5G6B5; #ifdef USE_D3D9 UINT modeCount = d3d->GetAdapterModeCount(GcurSel, format); #else UINT modeCount = d3d->GetAdapterModeCount(GcurSel); #endif for ( UINT i = 0; i < modeCount; i++ ) { D3DDISPLAYMODE mode; #ifdef USE_D3D9 d3d->EnumAdapterModes(GcurSel, format, i, &mode); #else d3d->EnumAdapterModes(GcurSel, i, &mode); #endif if ( mode.Width == width && mode.Height == height && mode.Format == format ) { if ( mode.RefreshRate == 0 ) { d3d->Release(); return 0; } if ( mode.RefreshRate < refreshRate && mode.RefreshRate >= 60 ) refreshRate = mode.RefreshRate; } } d3d->Release(); if ( refreshRate == -1 ) return -1; return refreshRate; } /* ***************************************************************************** */ RwBool psSelectDevice() { RwVideoMode vm; RwInt32 subSysNum; RwInt32 AutoRenderer = 0; RwBool modeFound = FALSE; if ( !useDefault ) { GnumSubSystems = RwEngineGetNumSubSystems(); if ( !GnumSubSystems ) { return FALSE; } /* Just to be sure ... */ GnumSubSystems = (GnumSubSystems > MAX_SUBSYSTEMS) ? MAX_SUBSYSTEMS : GnumSubSystems; /* Get the names of all the sub systems */ for (subSysNum = 0; subSysNum < GnumSubSystems; subSysNum++) { RwEngineGetSubSystemInfo(&GsubSysInfo[subSysNum], subSysNum); } /* Get the default selection */ GcurSel = RwEngineGetCurrentSubSystem(); #ifdef IMPROVED_VIDEOMODE if(FrontEndMenuManager.m_nPrefsSubsystem < GnumSubSystems) GcurSel = FrontEndMenuManager.m_nPrefsSubsystem; #endif } /* Set the driver to use the correct sub system */ if (!RwEngineSetSubSystem(GcurSel)) { return FALSE; } #ifdef IMPROVED_VIDEOMODE FrontEndMenuManager.m_nPrefsSubsystem = GcurSel; #endif #ifndef IMPROVED_VIDEOMODE if ( !useDefault ) { if ( _psGetVideoModeList()[FrontEndMenuManager.m_nDisplayVideoMode] && FrontEndMenuManager.m_nDisplayVideoMode ) { FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; GcurSelVM = FrontEndMenuManager.m_nDisplayVideoMode; } else { #ifdef DEFAULT_NATIVE_RESOLUTION // get the native video mode HDC hDevice = GetDC(NULL); int w = GetDeviceCaps(hDevice, HORZRES); int h = GetDeviceCaps(hDevice, VERTRES); int d = GetDeviceCaps(hDevice, BITSPIXEL); #else const int w = 640; const int h = 480; const int d = 16; #endif while ( !modeFound && GcurSelVM < RwEngineGetNumVideoModes() ) { RwEngineGetVideoModeInfo(&vm, GcurSelVM); if ( defaultFullscreenRes && vm.width != w || vm.height != h || vm.depth != d || !(vm.flags & rwVIDEOMODEEXCLUSIVE) ) ++GcurSelVM; else modeFound = TRUE; } if ( !modeFound ) { #ifdef DEFAULT_NATIVE_RESOLUTION GcurSelVM = 1; #else MessageBox(nil, "Cannot find 640x480 video mode", "GTA3", MB_OK); return FALSE; #endif } } } #else if ( !useDefault ) { if(FrontEndMenuManager.m_nPrefsWidth == 0 || FrontEndMenuManager.m_nPrefsHeight == 0 || FrontEndMenuManager.m_nPrefsDepth == 0){ // Defaults if nothing specified FrontEndMenuManager.m_nPrefsWidth = GetSystemMetrics(SM_CXSCREEN); FrontEndMenuManager.m_nPrefsHeight = GetSystemMetrics(SM_CYSCREEN); FrontEndMenuManager.m_nPrefsDepth = 32; FrontEndMenuManager.m_nPrefsWindowed = 0; } // Find the videomode that best fits what we got from the settings file RwInt32 bestFsMode = -1; RwInt32 bestWidth = -1; RwInt32 bestHeight = -1; RwInt32 bestDepth = -1; for (GcurSelVM = 0; GcurSelVM < RwEngineGetNumVideoModes(); GcurSelVM++) { RwEngineGetVideoModeInfo(&vm, GcurSelVM); if (!(vm.flags & rwVIDEOMODEEXCLUSIVE)) { bestWndMode = GcurSelVM; } else { // try the largest one that isn't larger than what we wanted if (vm.width >= bestWidth && vm.width <= FrontEndMenuManager.m_nPrefsWidth && vm.height >= bestHeight && vm.height <= FrontEndMenuManager.m_nPrefsHeight && vm.depth >= bestDepth && vm.depth <= FrontEndMenuManager.m_nPrefsDepth){ bestWidth = vm.width; bestHeight = vm.height; bestDepth = vm.depth; bestFsMode = GcurSelVM; } } } if(bestFsMode < 0){ MessageBox(nil, "Cannot find desired video mode", "GTA3", MB_OK); return FALSE; } GcurSelVM = bestFsMode; FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode; FrontEndMenuManager.m_nSelectedScreenMode = FrontEndMenuManager.m_nPrefsWindowed; } #endif RwEngineGetVideoModeInfo(&vm, GcurSelVM); #ifdef IMPROVED_VIDEOMODE if (FrontEndMenuManager.m_nPrefsWindowed) GcurSelVM = bestWndMode; // Now GcurSelVM is 0 but vm has sizes(and fullscreen flag) of the video mode we want, that's why we changed the rwVIDEOMODEEXCLUSIVE conditions below FrontEndMenuManager.m_nPrefsWidth = vm.width; FrontEndMenuManager.m_nPrefsHeight = vm.height; FrontEndMenuManager.m_nPrefsDepth = vm.depth; #endif #ifndef PS2_MENU FrontEndMenuManager.m_nCurrOption = 0; #endif /* Set up the video mode and set the apps window * dimensions to match */ if (!RwEngineSetVideoMode(GcurSelVM)) { return FALSE; } #ifdef IMPROVED_VIDEOMODE if (!FrontEndMenuManager.m_nPrefsWindowed) #else if (vm.flags & rwVIDEOMODEEXCLUSIVE) #endif { debug("%dx%dx%d", vm.width, vm.height, vm.depth); UINT refresh = GetBestRefreshRate(vm.width, vm.height, vm.depth); if ( refresh != (UINT)-1 ) { debug("refresh %d", refresh); RwD3D8EngineSetRefreshRate((RwUInt32)refresh); } } #ifdef IMPROVED_VIDEOMODE if (!FrontEndMenuManager.m_nPrefsWindowed) #else if (vm.flags & rwVIDEOMODEEXCLUSIVE) #endif { RsGlobal.maximumWidth = vm.width; RsGlobal.maximumHeight = vm.height; RsGlobal.width = vm.width; RsGlobal.height = vm.height; PSGLOBAL(fullScreen) = TRUE; #ifdef IMPROVED_VIDEOMODE SetWindowLong(PSGLOBAL(window), GWL_STYLE, WS_POPUP); SetWindowPos(PSGLOBAL(window), nil, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER| SWP_FRAMECHANGED); }else{ RECT rect; rect.left = rect.top = 0; rect.right = FrontEndMenuManager.m_nPrefsWidth; rect.bottom = FrontEndMenuManager.m_nPrefsHeight; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); // center it int spaceX = GetSystemMetrics(SM_CXSCREEN) - (rect.right-rect.left); int spaceY = GetSystemMetrics(SM_CYSCREEN) - (rect.bottom-rect.top); SetWindowLong(PSGLOBAL(window), GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW); SetWindowPos(PSGLOBAL(window), HWND_NOTOPMOST, spaceX/2, spaceY/2, (rect.right - rect.left), (rect.bottom - rect.top), 0); // Have to get actual size because the window perhaps didn't fit GetClientRect(PSGLOBAL(window), &rect); RsGlobal.maximumWidth = rect.right; RsGlobal.maximumHeight = rect.bottom; RsGlobal.width = rect.right; RsGlobal.height = rect.bottom; PSGLOBAL(fullScreen) = FALSE; #endif } #ifdef MULTISAMPLING RwD3D8EngineSetMultiSamplingLevels(1 << FrontEndMenuManager.m_nPrefsMSAALevel); #endif return TRUE; } /* ***************************************************************************** */ RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode) { RwInitialised = FALSE; RsEventHandler(rsRWTERMINATE, nil); GcurSel = subSystem; GcurSelVM = videoMode; useDefault = TRUE; if ( RsEventHandler(rsRWINITIALIZE, PSGLOBAL(window)) == rsEVENTERROR ) return FALSE; RwInitialised = TRUE; useDefault = FALSE; RwRect r; r.x = 0; r.y = 0; r.w = RsGlobal.maximumWidth; r.h = RsGlobal.maximumHeight; RsEventHandler(rsCAMERASIZE, &r); return TRUE; } /* ***************************************************************************** */ static RwChar ** CommandLineToArgv(RwChar *cmdLine, RwInt32 *argCount) { RwInt32 numArgs = 0; RwBool inArg, inString; RwInt32 i, len; RwChar *res, *str, **aptr; len = (int)strlen(cmdLine); /* * Count the number of arguments... */ inString = FALSE; inArg = FALSE; for(i=0; i<=len; i++) { if( cmdLine[i] == '"' ) { inString = !inString; } if( (cmdLine[i] <= ' ' && !inString) || i == len ) { if( inArg ) { inArg = FALSE; numArgs++; } } else if( !inArg ) { inArg = TRUE; } } /* * Allocate memory for result... */ res = (RwChar *)malloc(sizeof(RwChar *) * numArgs + len + 1); str = res + sizeof(RwChar *) * numArgs; aptr = (RwChar **)res; strcpy(str, cmdLine); /* * Walk through cmdLine again this time setting pointer to each arg... */ inArg = FALSE; inString = FALSE; for(i=0; i<=len; i++) { if( cmdLine[i] == '"' ) { inString = !inString; } if( (cmdLine[i] <= ' ' && !inString) || i == len ) { if( inArg ) { if( str[i-1] == '"' ) { str[i-1] = '\0'; } else { str[i] = '\0'; } inArg = FALSE; } } else if( !inArg && cmdLine[i] != '"' ) { inArg = TRUE; *aptr++ = &str[i]; } } *argCount = numArgs; return (RwChar **)res; } /* ***************************************************************************** */ void InitialiseLanguage() { WORD primUserLCID = PRIMARYLANGID(GetSystemDefaultLCID()); WORD primSystemLCID = PRIMARYLANGID(GetUserDefaultLCID()); WORD primLayout = PRIMARYLANGID((DWORD_PTR)GetKeyboardLayout(0)); WORD subUserLCID = SUBLANGID(GetSystemDefaultLCID()); WORD subSystemLCID = SUBLANGID(GetUserDefaultLCID()); WORD subLayout = SUBLANGID((DWORD_PTR)GetKeyboardLayout(0)); if ( primUserLCID == LANG_GERMAN || primSystemLCID == LANG_GERMAN || primLayout == LANG_GERMAN ) { CGame::nastyGame = false; FrontEndMenuManager.m_PrefsAllowNastyGame = false; CGame::germanGame = true; } if ( primUserLCID == LANG_FRENCH || primSystemLCID == LANG_FRENCH || primLayout == LANG_FRENCH ) { CGame::nastyGame = false; FrontEndMenuManager.m_PrefsAllowNastyGame = false; CGame::frenchGame = true; } if ( subUserLCID == SUBLANG_ENGLISH_AUS || subSystemLCID == SUBLANG_ENGLISH_AUS || subLayout == SUBLANG_ENGLISH_AUS ) CGame::noProstitutes = true; #ifdef NASTY_GAME CGame::nastyGame = true; FrontEndMenuManager.m_PrefsAllowNastyGame = true; CGame::noProstitutes = false; #endif int32 lang; switch ( primSystemLCID ) { case LANG_GERMAN: { lang = LANG_GERMAN; break; } case LANG_FRENCH: { lang = LANG_FRENCH; break; } case LANG_SPANISH: { lang = LANG_SPANISH; break; } case LANG_ITALIAN: { lang = LANG_ITALIAN; break; } default: { lang = ( subSystemLCID == SUBLANG_ENGLISH_AUS ) ? -99 : LANG_ENGLISH; break; } } FrontEndMenuManager.OS_Language = primUserLCID; switch ( lang ) { case LANG_GERMAN: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_GERMAN; break; } case LANG_SPANISH: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_SPANISH; break; } case LANG_FRENCH: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_FRENCH; break; } case LANG_ITALIAN: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_ITALIAN; break; } default: { FrontEndMenuManager.m_PrefsLanguage = CMenuManager::LANGUAGE_AMERICAN; break; } } TheText.Unload(); TheText.Load(); } /* ***************************************************************************** */ void CenterVideo(void) { HRESULT hr = S_OK; RECT rect; ASSERT(pVW != nil); GetClientRect(PSGLOBAL(window), &rect); JIF(pVW->SetWindowPosition(rect.left, rect.top, rect.right, rect.bottom)); JIF(pVW->put_MessageDrain((OAHWND) PSGLOBAL(window))); SetFocus(PSGLOBAL(window)); } /* ***************************************************************************** */ void PlayMovieInWindow(int cmdShow, const char* szFile) { WCHAR wFileName[256]; HRESULT hr; // Clear open dialog remnants before calling RenderFile() UpdateWindow(PSGLOBAL(window)); // Convert filename to wide character string MultiByteToWideChar(CP_ACP, 0, szFile, -1, wFileName, sizeof(wFileName) - 1); // Initialize COM #ifdef FIX_BUGS // will also return S_FALSE if it has already been inited in the same thread CoInitialize(nil); #else JIF(CoInitialize(nil)); #endif // Get the interface for DirectShow's GraphBuilder JIF(CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGB)); if(SUCCEEDED(hr)) { // Have the graph builder construct its the appropriate graph automatically JIF(pGB->RenderFile(&wFileName[0], nil)); // QueryInterface for DirectShow interfaces JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC)); JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME)); JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS)); // Query for video interfaces, which may not be relevant for audio files JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW)); JIF(pVW->put_Owner((OAHWND) PSGLOBAL(window))); JIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN)); // Have the graph signal event via window callbacks for performance JIF(pME->SetNotifyWindow((OAHWND)PSGLOBAL(window), WM_GRAPHNOTIFY, 0)); CenterVideo(); // Run the graph to play the media file JIF(pMC->Run()); SetFocus(PSGLOBAL(window)); } ASSERT(pGB != nil); ASSERT(pVW != nil); ASSERT(pME != nil); ASSERT(pMC != nil); if(FAILED(hr)) CloseClip(); } /* ***************************************************************************** */ void CloseInterfaces(void) { // Release and zero DirectShow interfaces SAFE_RELEASE(pME); SAFE_RELEASE(pMS); SAFE_RELEASE(pMC); SAFE_RELEASE(pVW); SAFE_RELEASE(pGB); } /* ***************************************************************************** */ void CloseClip(void) { HRESULT hr; // Stop playback if(pMC) hr = pMC->Stop(); // Free DirectShow interfaces CloseInterfaces(); } /* ***************************************************************************** */ void HandleExit() { MSG message; while ( PeekMessage(&message, nil, 0U, 0U, PM_REMOVE|PM_NOYIELD) ) { if( message.message == WM_QUIT ) { RsGlobal.quit = TRUE; } else { TranslateMessage(&message); DispatchMessage(&message); } } } /* ***************************************************************************** */ int PASCAL WinMain(HINSTANCE instance, HINSTANCE prevInstance __RWUNUSED__, CMDSTR cmdLine, int cmdShow) { MSG message; RwV2d pos; RwInt32 argc, i; RwChar **argv; SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, nil, SPIF_SENDCHANGE); #ifndef MASTER if (strstr(cmdLine, "-console")) { AllocConsole(); freopen("CONIN$", "r", stdin); freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); } #endif #ifdef USE_CUSTOM_ALLOCATOR InitMemoryMgr(); #endif /* * Initialize the platform independent data. * This will in turn initialize the platform specific data... */ if( RsEventHandler(rsINITIALIZE, nil) == rsEVENTERROR ) { return FALSE; } /* * Register the window class... */ if( !InitApplication(instance) ) { return FALSE; } /* * Get proper command line params, cmdLine passed to us does not * work properly under all circumstances... */ cmdLine = GetCommandLine(); /* * Parse command line into standard (argv, argc) parameters... */ argv = CommandLineToArgv(cmdLine, &argc); /* * Parse command line parameters (except program name) one at * a time BEFORE RenderWare initialization... */ for(i=1; iNewState.CheckForInput() ) ++gGameState; else if ( CPad::GetPad(0)->GetLeftMouseJustDown() ) ++gGameState; else if ( CPad::GetPad(0)->GetEnterJustDown() ) ++gGameState; else if ( CPad::GetPad(0)->GetCharJustDown(' ') ) ++gGameState; else if ( CPad::GetPad(0)->GetAltJustDown() ) ++gGameState; else if ( CPad::GetPad(0)->GetTabJustDown() ) ++gGameState; break; } case GS_INIT_INTRO_MPEG: { #ifdef NO_MOVIES if (!gbNoMovies) #endif CloseClip(); #ifndef FIX_BUGS CoUninitialize(); #endif if ( FrontEndMenuManager.OS_Language == LANG_FRENCH || FrontEndMenuManager.OS_Language == LANG_GERMAN ) PlayMovieInWindow(cmdShow, "movies\\GTAtitlesGER.mpg"); else PlayMovieInWindow(cmdShow, "movies\\GTAtitles.mpg"); gGameState = GS_INTRO_MPEG; TRACE("gGameState = GS_INTRO_MPEG;"); break; } case GS_INTRO_MPEG: { CPad::UpdatePads(); if ( startupDeactivate || ControlsManager.GetJoyButtonJustDown() != 0 ) ++gGameState; else if ( CPad::GetPad(0)->NewState.CheckForInput() ) ++gGameState; else if ( CPad::GetPad(0)->GetLeftMouseJustDown() ) ++gGameState; else if ( CPad::GetPad(0)->GetEnterJustDown() ) ++gGameState; else if ( CPad::GetPad(0)->GetCharJustDown(' ') ) ++gGameState; else if ( CPad::GetPad(0)->GetAltJustDown() ) ++gGameState; else if ( CPad::GetPad(0)->GetTabJustDown() ) ++gGameState; break; } case GS_INIT_ONCE: { #ifdef NO_MOVIES if (!gbNoMovies) #endif CloseClip(); #ifndef FIX_BUGS CoUninitialize(); #endif #ifdef FIX_BUGS // draw one frame because otherwise we'll end up looking at black screen for a while if vsync is on RsCameraShowRaster(Scene.camera); #endif #ifdef PS2_MENU extern char version_name[64]; if ( CGame::frenchGame || CGame::germanGame ) LoadingScreen(NULL, version_name, "loadsc24"); else LoadingScreen(NULL, version_name, "loadsc0"); printf("Into TheGame!!!\n"); #else LoadingScreen(nil, nil, "loadsc0"); // LoadingScreen(nil, nil, "loadsc0"); // duplicate #endif if ( !CGame::InitialiseOnceAfterRW() ) RsGlobal.quit = TRUE; #ifdef PS2_MENU gGameState = GS_INIT_PLAYING_GAME; #else gGameState = GS_INIT_FRONTEND; TRACE("gGameState = GS_INIT_FRONTEND;"); #endif break; } #ifndef PS2_MENU case GS_INIT_FRONTEND: { LoadingScreen(nil, nil, "loadsc0"); // LoadingScreen(nil, nil, "loadsc0"); // duplicate FrontEndMenuManager.m_bGameNotLoaded = true; FrontEndMenuManager.m_bStartUpFrontEndRequested = true; if ( defaultFullscreenRes ) { defaultFullscreenRes = FALSE; FrontEndMenuManager.m_nPrefsVideoMode = GcurSelVM; FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM; } gGameState = GS_FRONTEND; TRACE("gGameState = GS_FRONTEND;"); break; } case GS_FRONTEND: { GetWindowPlacement(PSGLOBAL(window), &wp); if (wp.showCmd != SW_SHOWMINIMIZED) RsEventHandler(rsFRONTENDIDLE, nil); #ifdef PS2_MENU if ( !FrontEndMenuManager.m_bMenuActive || TheMemoryCard.m_bWantToLoad ) #else if ( !FrontEndMenuManager.m_bMenuActive || FrontEndMenuManager.m_bWantToLoad ) #endif { gGameState = GS_INIT_PLAYING_GAME; TRACE("gGameState = GS_INIT_PLAYING_GAME;"); } #ifdef PS2_MENU if (TheMemoryCard.m_bWantToLoad ) #else if ( FrontEndMenuManager.m_bWantToLoad ) #endif { InitialiseGame(); FrontEndMenuManager.m_bGameNotLoaded = false; gGameState = GS_PLAYING_GAME; TRACE("gGameState = GS_PLAYING_GAME;"); } break; } #endif case GS_INIT_PLAYING_GAME: { #ifdef PS2_MENU CGame::Initialise("DATA\\GTA3.DAT"); //LoadingScreen("Starting Game", NULL, GetRandomSplashScreen()); if ( TheMemoryCard.CheckCardInserted(CARD_ONE) == CMemoryCard::NO_ERR_SUCCESS && TheMemoryCard.ChangeDirectory(CARD_ONE, TheMemoryCard.Cards[CARD_ONE].dir) && TheMemoryCard.FindMostRecentFileName(CARD_ONE, TheMemoryCard.MostRecentFile) == true && TheMemoryCard.CheckDataNotCorrupt(TheMemoryCard.MostRecentFile)) { strcpy(TheMemoryCard.LoadFileName, TheMemoryCard.MostRecentFile); TheMemoryCard.b_FoundRecentSavedGameWantToLoad = true; if (CMenuManager::m_PrefsLanguage != TheMemoryCard.GetLanguageToLoad()) { CMenuManager::m_PrefsLanguage = TheMemoryCard.GetLanguageToLoad(); TheText.Unload(); TheText.Load(); } CGame::currLevel = (eLevelName)TheMemoryCard.GetLevelToLoad(); } #else InitialiseGame(); FrontEndMenuManager.m_bGameNotLoaded = false; #endif gGameState = GS_PLAYING_GAME; TRACE("gGameState = GS_PLAYING_GAME;"); break; } case GS_PLAYING_GAME: { float ms = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond(); if ( RwInitialised ) { if (!FrontEndMenuManager.m_PrefsFrameLimiter || (1000.0f / (float)RsGlobal.maxFPS) < ms) RsEventHandler(rsIDLE, (void *)TRUE); } break; } } } else { if ( RwCameraBeginUpdate(Scene.camera) ) { RwCameraEndUpdate(Scene.camera); ForegroundApp = TRUE; RsEventHandler(rsACTIVATE, (void *)TRUE); } WaitMessage(); } } /* * About to shut down - block resize events again... */ RwInitialised = FALSE; FrontEndMenuManager.UnloadTextures(); #ifdef PS2_MENU if ( !(FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad)) break; #else if ( !FrontEndMenuManager.m_bWantToRestart ) break; #endif CPad::ResetCheats(); CPad::StopPadsShaking(); DMAudio.ChangeMusicMode(MUSICMODE_DISABLE); #ifdef PS2_MENU CGame::ShutDownForRestart(); #endif CTimer::Stop(); #ifdef PS2_MENU if (FrontEndMenuManager.m_bWantToRestart || TheMemoryCard.b_FoundRecentSavedGameWantToLoad) { if (TheMemoryCard.b_FoundRecentSavedGameWantToLoad) { FrontEndMenuManager.m_bWantToRestart = true; TheMemoryCard.m_bWantToLoad = true; } CGame::InitialiseWhenRestarting(); DMAudio.ChangeMusicMode(MUSICMODE_GAME); FrontEndMenuManager.m_bWantToRestart = false; continue; } CGame::ShutDown(); CTimer::Stop(); break; #else if ( FrontEndMenuManager.m_bWantToLoad ) { CGame::ShutDownForRestart(); CGame::InitialiseWhenRestarting(); DMAudio.ChangeMusicMode(MUSICMODE_GAME); LoadSplash(GetLevelSplashScreen(CGame::currLevel)); FrontEndMenuManager.m_bWantToLoad = false; } else { #ifndef MASTER if ( gbModelViewer ) CAnimViewer::Shutdown(); else #endif if ( gGameState == GS_PLAYING_GAME ) CGame::ShutDown(); CTimer::Stop(); if ( FrontEndMenuManager.m_bFirstTime == true ) { gGameState = GS_INIT_FRONTEND; TRACE("gGameState = GS_INIT_FRONTEND;"); } else { gGameState = GS_INIT_PLAYING_GAME; TRACE("gGameState = GS_INIT_PLAYING_GAME;"); } } FrontEndMenuManager.m_bFirstTime = false; FrontEndMenuManager.m_bWantToRestart = false; #endif } #ifndef MASTER if ( gbModelViewer ) CAnimViewer::Shutdown(); else #endif if ( gGameState == GS_PLAYING_GAME ) CGame::ShutDown(); DMAudio.Terminate(); _psFreeVideoModeList(); /* * Tidy up the 3D (RenderWare) components of the application... */ RsEventHandler(rsRWTERMINATE, nil); /* * Kill the window... */ DestroyWindow(PSGLOBAL(window)); /* * Free the platform dependent data... */ RsEventHandler(rsTERMINATE, nil); /* * Free the argv strings... */ free(argv); ShowCursor(TRUE); SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &SavedStickyKeys, SPIF_SENDCHANGE); SystemParametersInfo(SPI_SETPOWEROFFACTIVE, TRUE, nil, SPIF_SENDCHANGE); SystemParametersInfo(SPI_SETLOWPOWERACTIVE, TRUE, nil, SPIF_SENDCHANGE); SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, nil, SPIF_SENDCHANGE); SetErrorMode(0); return message.wParam; } /* ***************************************************************************** */ #define DEVICE_AXIS_MIN -2000 #define DEVICE_AXIS_MAX 2000 HRESULT _InputInitialise() { HRESULT hr; // Create a DInput object if( FAILED( hr = DirectInput8Create( GetModuleHandle(nil), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&PSGLOBAL(dinterface), nil ) ) ) return hr; return S_OK; } HRESULT _InputInitialiseMouse(bool exclusive) { HRESULT hr; // Obtain an interface to the system mouse device. if( FAILED( hr = PSGLOBAL(dinterface)->CreateDevice( GUID_SysMouse, &PSGLOBAL(mouse), nil ) ) ) return hr; // Set the data format to "mouse format" - a predefined data format // // A data format specifies which controls on a device we // are interested in, and how they should be reported. // // This tells DirectInput that we will be passing a // DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState. if( FAILED( hr = PSGLOBAL(mouse)->SetDataFormat( &c_dfDIMouse2 ) ) ) return hr; if( FAILED( hr = PSGLOBAL(mouse)->SetCooperativeLevel( PSGLOBAL(window), (exclusive ? DISCL_EXCLUSIVE : DISCL_NONEXCLUSIVE) | DISCL_FOREGROUND ) ) ) return hr; // Acquire the newly created device PSGLOBAL(mouse)->Acquire(); return S_OK; } RwV2d leftStickPos; RwV2d rightStickPos; HRESULT CapturePad(RwInt32 padID) { HRESULT hr; DIJOYSTATE2 js; LPDIRECTINPUTDEVICE8 *pPad = nil; if( padID == 0 ) pPad = &PSGLOBAL(joy1); else if( padID == 1) pPad = &PSGLOBAL(joy2); else assert("invalid padID"); if ( nil == (*pPad) ) return S_OK; // Poll the device to read the current state hr = (*pPad)->Poll(); if( FAILED(hr) ) { // DInput is telling us that the input stream has been // interrupted. We aren't tracking any state between polls, so // we don't have any special reset that needs to be done. We // just re-acquire and try again. hr = (*pPad)->Acquire(); while( hr == DIERR_INPUTLOST ) hr = (*pPad)->Acquire(); // hr may be DIERR_OTHERAPPHASPRIO or other errors. This // may occur when the app is minimized or in the process of // switching, so just try again later if( FAILED(hr) ) return hr; hr = (*pPad)->Poll(); if( FAILED(hr) ) return hr; } // Get the input's device state if( FAILED( hr = (*pPad)->GetDeviceState( sizeof(DIJOYSTATE2), &js ) ) ) return hr; // The device should have been acquired during the Poll() if ( ControlsManager.m_bFirstCapture == true ) { memcpy(&ControlsManager.m_OldState, &js, sizeof(DIJOYSTATE2)); memcpy(&ControlsManager.m_NewState, &js, sizeof(DIJOYSTATE2)); ControlsManager.m_bFirstCapture = false; } else { memcpy(&ControlsManager.m_OldState, &ControlsManager.m_NewState, sizeof(DIJOYSTATE2)); memcpy(&ControlsManager.m_NewState, &js, sizeof(DIJOYSTATE2)); } RsPadButtonStatus bs; bs.padID = padID; RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); bool deviceAvailable = (*pPad) != nil; if ( deviceAvailable ) { leftStickPos.x = (float)js.lX / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); leftStickPos.y = (float)js.lY / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); if (LOWORD(js.rgdwPOV[0]) != 0xFFFF) { float angle = DEGTORAD((float)js.rgdwPOV[0] / 100.0f); leftStickPos.x = Sin(angle); leftStickPos.y = -Cos(angle); } if ( AllValidWinJoys.m_aJoys[bs.padID].m_bHasAxisR && AllValidWinJoys.m_aJoys[bs.padID].m_bHasAxisZ ) { rightStickPos.x = (float)js.lZ / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); rightStickPos.y = (float)js.lRz / (float)((DEVICE_AXIS_MAX - DEVICE_AXIS_MIN) / 2); } } { if (CPad::m_bMapPadOneToPadTwo) bs.padID = 1; RsPadEventHandler(rsPADBUTTONUP, (void *)&bs); RsPadEventHandler(rsPADBUTTONDOWN, (void *)&bs); } { if (CPad::m_bMapPadOneToPadTwo) bs.padID = 1; CPad *pad = CPad::GetPad(bs.padID); if ( Abs(leftStickPos.x) > 0.3f ) pad->PCTempJoyState.LeftStickX = (int32)(leftStickPos.x * 128.0f); if ( Abs(leftStickPos.y) > 0.3f ) pad->PCTempJoyState.LeftStickY = (int32)(leftStickPos.y * 128.0f); if ( Abs(rightStickPos.x) > 0.3f ) pad->PCTempJoyState.RightStickX = (int32)(rightStickPos.x * 128.0f); if ( Abs(rightStickPos.y) > 0.3f ) pad->PCTempJoyState.RightStickY = (int32)(rightStickPos.y * 128.0f); } return S_OK; } void _InputInitialiseJoys() { DIPROPDWORD prop; DIDEVCAPS devCaps; for ( int32 i = 0; i < _TODOCONST(2); i++ ) AllValidWinJoys.ClearJoyInfo(i); _InputAddJoys(); if ( PSGLOBAL(joy1) != nil ) { devCaps.dwSize = sizeof(DIDEVCAPS); PSGLOBAL(joy1)->GetCapabilities(&devCaps); prop.diph.dwSize = sizeof(DIPROPDWORD); prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); prop.diph.dwObj = 0; prop.diph.dwHow = 0; PSGLOBAL(joy1)->GetProperty(DIPROP_VIDPID, (LPDIPROPHEADER)&prop); AllValidWinJoys.m_aJoys[0].m_nVendorID = LOWORD(prop.dwData); AllValidWinJoys.m_aJoys[0].m_nProductID = HIWORD(prop.dwData); AllValidWinJoys.m_aJoys[0].m_bInitialised = true; ControlsManager.InitDefaultControlConfigJoyPad(devCaps.dwButtons); } if ( PSGLOBAL(joy2) != nil ) { PSGLOBAL(joy2)->GetProperty(DIPROP_VIDPID, (LPDIPROPHEADER)&prop); AllValidWinJoys.m_aJoys[1].m_nVendorID = LOWORD(prop.dwData); AllValidWinJoys.m_aJoys[1].m_nProductID = HIWORD(prop.dwData); AllValidWinJoys.m_aJoys[1].m_bInitialised = true; } } void _InputAddJoyStick(LPDIRECTINPUTDEVICE8 lpDevice, INT num) { DIDEVICEOBJECTINSTANCE objInst; objInst.dwSize = sizeof( DIDEVICEOBJECTINSTANCE ); DIPROPRANGE range; range.diph.dwSize = sizeof(DIPROPRANGE); range.diph.dwHeaderSize = sizeof(DIPROPHEADER); range.lMin = DEVICE_AXIS_MIN; range.lMax = DEVICE_AXIS_MAX; range.diph.dwHow = DIPH_BYOFFSET; // get the info about the object from the device range.diph.dwObj = DIJOFS_X; if ( lpDevice != nil ) { if ( SUCCEEDED( lpDevice->GetObjectInfo( &objInst, DIJOFS_X, DIPH_BYOFFSET ) ) ) { if( FAILED( lpDevice->SetProperty( DIPROP_RANGE, (LPCDIPROPHEADER)&range ) ) ) return; else ; } } range.diph.dwObj = DIJOFS_Y; if ( lpDevice != nil ) { if ( SUCCEEDED( lpDevice->GetObjectInfo( &objInst, DIJOFS_Y, DIPH_BYOFFSET ) ) ) { if( FAILED( lpDevice->SetProperty( DIPROP_RANGE, (LPCDIPROPHEADER)&range ) ) ) return; else ; } } range.diph.dwObj = DIJOFS_Z; if ( lpDevice != nil ) { if ( SUCCEEDED( lpDevice->GetObjectInfo( &objInst, DIJOFS_Z, DIPH_BYOFFSET ) ) ) { if( FAILED( lpDevice->SetProperty( DIPROP_RANGE, (LPCDIPROPHEADER)&range ) ) ) return; else AllValidWinJoys.m_aJoys[num].m_bHasAxisZ = true; // z rightStickPos.x } } range.diph.dwObj = DIJOFS_RZ; if ( lpDevice != nil ) { if ( SUCCEEDED( lpDevice->GetObjectInfo( &objInst, DIJOFS_RZ, DIPH_BYOFFSET ) ) ) { if( FAILED( lpDevice->SetProperty( DIPROP_RANGE, (LPCDIPROPHEADER)&range ) ) ) return; else AllValidWinJoys.m_aJoys[num].m_bHasAxisR = true; // r rightStickPos.y } } } HRESULT _InputAddJoys() { HRESULT hr; hr = PSGLOBAL(dinterface)->EnumDevices(DI8DEVCLASS_GAMECTRL, _InputEnumDevicesCallback, nil, DIEDFL_ATTACHEDONLY ); if( FAILED(hr) ) return hr; if ( PSGLOBAL(joy1) == nil ) return S_FALSE; _InputAddJoyStick(PSGLOBAL(joy1), 0); if ( PSGLOBAL(joy2) == nil ) return S_OK; // we have one device already so return OK and ignore second _InputAddJoyStick(PSGLOBAL(joy2), 1); return S_OK; } HRESULT _InputGetMouseState(DIMOUSESTATE2 *state) { HRESULT hr; if ( PSGLOBAL(mouse) == nil ) return S_FALSE; // Get the input's device state, and put the state in dims ZeroMemory( state, sizeof(DIMOUSESTATE2) ); hr = PSGLOBAL(mouse)->GetDeviceState( sizeof(DIMOUSESTATE2), state ); if( FAILED(hr) ) { // DirectInput may be telling us that the input stream has been // interrupted. We aren't tracking any state between polls, so // we don't have any special reset that needs to be done. // We just re-acquire and try again. // If input is lost then acquire and keep trying hr = PSGLOBAL(mouse)->Acquire(); while( hr == DIERR_INPUTLOST ) hr = PSGLOBAL(mouse)->Acquire(); ZeroMemory( state, sizeof(DIMOUSESTATE2) ); hr = PSGLOBAL(mouse)->GetDeviceState( sizeof(DIMOUSESTATE2), state ); return hr; } return S_OK; } void _InputShutdown() { SAFE_RELEASE(PSGLOBAL(dinterface)); } void _InputShutdownMouse() { if (PSGLOBAL(mouse) == nil) return; PSGLOBAL(mouse)->Unacquire(); SAFE_RELEASE(PSGLOBAL(mouse)); } bool _InputMouseNeedsExclusive(void) { // FIX: I don't know why R* needed that, but it causes infamous mouse bug on modern systems. // Probably DirectInput bug, since Acquire() and GetDeviceState() reports everything A-OK. #ifdef FIX_BUGS return false; #endif RwVideoMode vm; RwEngineGetVideoModeInfo(&vm, GcurSelVM); return vm.flags & rwVIDEOMODEEXCLUSIVE; } BOOL CALLBACK _InputEnumDevicesCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext ) { HRESULT hr; static INT Count = 0; LPDIRECTINPUTDEVICE8 *pJoystick = nil; if ( Count == 0 ) pJoystick = &PSGLOBAL(joy1); else if ( Count == 1 ) pJoystick = &PSGLOBAL(joy2); else assert("too many pads"); // Obtain an interface to the enumerated joystick. hr = PSGLOBAL(dinterface)->CreateDevice( pdidInstance->guidInstance, pJoystick, nil ); // If it failed, then we can't use this joystick. (Maybe the user unplugged // it while we were in the middle of enumerating it.) if( hr != S_OK ) return DIENUM_CONTINUE; hr = (*pJoystick)->SetDataFormat( &c_dfDIJoystick2 ); if( hr != S_OK ) { (*pJoystick)->Release(); return DIENUM_CONTINUE; } ++Count; hr = (*pJoystick)->SetCooperativeLevel( PSGLOBAL(window), DISCL_NONEXCLUSIVE|DISCL_FOREGROUND ); if( hr != S_OK ) { (*pJoystick)->Release(); #ifdef FIX_BUGS // BUG: enum will be called with Count == 2, which will write to a null pointer // So decrement count again since we're not using this pad --Count; #endif return DIENUM_CONTINUE; } // Stop enumeration. Note: we're just taking the first two joysticks we get. You // could store all the enumerated joysticks and let the user pick. if ( Count == 2 ) return DIENUM_STOP; return DIENUM_CONTINUE; } BOOL _InputTranslateKey(RsKeyCodes *rs, UINT flag, UINT key) { *rs = rsNULL; switch ( key ) { case VK_SHIFT: { if ( _dwOperatingSystemVersion == OS_WIN98 ) *rs = rsSHIFT; break; } case VK_RETURN: { if ( _InputIsExtended(flag) ) *rs = rsPADENTER; else *rs = rsENTER; break; } case VK_CONTROL: { if ( _InputIsExtended(flag) ) *rs = rsRCTRL; else *rs = rsLCTRL; break; } case VK_MENU: { if ( _InputIsExtended(flag) ) *rs = rsRALT; else *rs = rsLALT; break; } case VK_APPS: { *rs = rsAPPS; break; } case VK_PAUSE: { *rs = rsPAUSE; break; } case VK_CAPITAL: { *rs = rsCAPSLK; break; } case VK_ESCAPE: { *rs = rsESC; break; } case VK_PRIOR: { if ( _InputIsExtended(flag) ) *rs = rsPGUP; else *rs = rsPADPGUP; break; } case VK_NEXT: { if ( _InputIsExtended(flag) ) *rs = rsPGDN; else *rs = rsPADPGDN; break; } case VK_END: { if ( _InputIsExtended(flag) ) *rs = rsEND; else *rs = rsPADEND; break; } case VK_HOME: { if ( _InputIsExtended(flag) ) *rs = rsHOME; else *rs = rsPADHOME; break; } case VK_LEFT: { if ( _InputIsExtended(flag) ) *rs = rsLEFT; else *rs = rsPADLEFT; break; } case VK_UP: { if ( _InputIsExtended(flag) ) *rs = rsUP; else *rs = rsPADUP; break; } case VK_RIGHT: { if ( _InputIsExtended(flag) ) *rs = rsRIGHT; else *rs = rsPADRIGHT; break; } case VK_DOWN: { if ( _InputIsExtended(flag) ) *rs = rsDOWN; else *rs = rsPADDOWN; break; } case VK_INSERT: { if ( _InputIsExtended(flag) ) *rs = rsINS; else *rs = rsPADINS; break; } case VK_DELETE: { if ( _InputIsExtended(flag) ) *rs = rsDEL; else *rs = rsPADDEL; break; } case VK_LWIN: { *rs = rsLWIN; break; } case VK_RWIN: { *rs = rsRWIN; break; } case VK_NUMPAD0: { *rs = rsPADINS; break; } case VK_NUMPAD1: { *rs = rsPADEND; break; } case VK_NUMPAD2: { *rs = rsPADDOWN; break; } case VK_NUMPAD3: { *rs = rsPADPGDN; break; } case VK_NUMPAD4: { *rs = rsPADLEFT; break; } case VK_NUMPAD5: { *rs = rsPAD5; break; } case VK_NUMPAD6: { *rs = rsPADRIGHT; break; } case VK_NUMPAD7: { *rs = rsPADHOME; break; } case VK_NUMPAD8: { *rs = rsPADUP; break; } case VK_NUMPAD9: { *rs = rsPADPGUP; break; } case VK_MULTIPLY: { *rs = rsTIMES; break; } case VK_DIVIDE: { *rs = rsDIVIDE; break; } case VK_ADD: { *rs = rsPLUS; break; } case VK_SUBTRACT: { *rs = rsMINUS; break; } case VK_DECIMAL: { *rs = rsPADDEL; break; } case VK_F1: { *rs = rsF1; break; } case VK_F2: { *rs = rsF2; break; } case VK_F3: { *rs = rsF3; break; } case VK_F4: { *rs = rsF4; break; } case VK_F5: { *rs = rsF5; break; } case VK_F6: { *rs = rsF6; break; } case VK_F7: { *rs = rsF7; break; } case VK_F8: { *rs = rsF8; break; } case VK_F9: { *rs = rsF9; break; } case VK_F10: { *rs = rsF10; break; } case VK_F11: { *rs = rsF11; break; } case VK_F12: { *rs = rsF12; break; } case VK_NUMLOCK: { *rs = rsNUMLOCK; break; } case VK_SCROLL: { *rs = rsSCROLL; break; } case VK_BACK: { *rs = rsBACKSP; break; } case VK_TAB: { *rs = rsTAB; break; } default: { UINT vkey = MapVirtualKey(key, MAPVK_VK_TO_CHAR) & 0xFFFF; if ( vkey < 255 ) *rs = (RsKeyCodes)vkey; break; } } return *rs != rsNULL; } void _InputTranslateShiftKeyUpDown(RsKeyCodes *rs) { if ( _dwOperatingSystemVersion != OS_WIN98 ) { if ( _InputTranslateShiftKey(rs, VK_LSHIFT, TRUE) ) RsKeyboardEventHandler(rsKEYDOWN, rs); if ( _InputTranslateShiftKey(rs, VK_RSHIFT, TRUE) ) RsKeyboardEventHandler(rsKEYDOWN, rs); if ( _InputTranslateShiftKey(rs, VK_LSHIFT, FALSE) ) RsKeyboardEventHandler(rsKEYUP, rs); if ( _InputTranslateShiftKey(rs, VK_RSHIFT, FALSE) ) RsKeyboardEventHandler(rsKEYUP, rs); } } BOOL _InputTranslateShiftKey(RsKeyCodes *rs, UINT key, BOOLEAN bDown) { *rs = rsNULL; switch ( key ) { case VK_LSHIFT: { if ( bDown == (GetKeyState(VK_LSHIFT) & 0x8000) >> 15 ) *rs = rsLSHIFT; break; } case VK_RSHIFT: { if ( bDown == (GetKeyState(VK_RSHIFT) & 0x8000) >> 15 ) *rs = rsRSHIFT; break; } default: { return *rs != rsNULL; } } return TRUE; } BOOL _InputIsExtended(INT flag) { return (flag & 0x1000000) != 0; } #if (defined(_MSC_VER)) int strcasecmp(const char *str1, const char *str2) { return _strcmpi(str1, str2); } int strncasecmp(const char *str1, const char *str2, size_t len) { return _strnicmp(str1, str2, len); } #endif #endif ================================================ FILE: src/skel/win/win.h ================================================ // DON'T include directly. crossplatform.h includes this if you're using D3D9 backend(win.cpp). #if (!defined(_PLATFORM_WIN_H)) #define _PLATFORM_WIN_H #if (!defined(RSREGSETBREAKALLOC)) #define RSREGSETBREAKALLOC(_name) /* No op */ #endif /* (!defined(RSREGSETBREAKALLOC)) */ #ifdef __DINPUT_INCLUDED__ /* platform specfic global data */ typedef struct { HWND window; HINSTANCE instance; RwBool fullScreen; RwV2d lastMousePos; DWORD field_14; LPDIRECTINPUT8 dinterface; LPDIRECTINPUTDEVICE8 mouse; LPDIRECTINPUTDEVICE8 joy1; LPDIRECTINPUTDEVICE8 joy2; } psGlobalType; #define PSGLOBAL(var) (((psGlobalType *)(RsGlobal.ps))->var) enum eJoypads { JOYSTICK1 = 0, JOYSTICK2, MAX_JOYSTICKS }; enum eJoypadState { JOYPAD_UNUSED, JOYPAD_ATTACHED, }; struct tJoy { eJoypadState m_State; bool m_bInitialised; bool m_bHasAxisZ; bool m_bHasAxisR; int m_nVendorID; int m_nProductID; }; class CJoySticks { public: tJoy m_aJoys[MAX_JOYSTICKS]; CJoySticks(); void ClearJoyInfo(int joyID); }; extern CJoySticks AllValidWinJoys; #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifdef __DINPUT_INCLUDED__ HRESULT _InputInitialise(); HRESULT CapturePad(RwInt32 padID); void _InputAddJoyStick(LPDIRECTINPUTDEVICE8 lpDevice, INT num); HRESULT _InputAddJoys(); HRESULT _InputGetMouseState(DIMOUSESTATE2 *state); void _InputShutdown(); BOOL CALLBACK _InputEnumDevicesCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext ); BOOL _InputTranslateKey(RsKeyCodes *rs, UINT flag, UINT key); BOOL _InputTranslateShiftKey(RsKeyCodes *rs, UINT key, BOOLEAN bDown); BOOL _InputIsExtended(INT flag); #endif void CenterVideo(void); void CloseClip(void); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* (!defined(_PLATFORM_WIN_H)) */ ================================================ FILE: src/skel/win/win.rc ================================================ #include "resource.h" ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // //#if !defined(__GNU_C__) //#include "afxres.h" //#else #include "winresrc.h" //#endif /* !defined(__GNU_C__) */ ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 186, 90 STYLE DS_MODALFRAME | DS_CENTER | DS_CENTERMOUSE | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Device Selection" FONT 8, "MS Sans Serif" BEGIN COMBOBOX IDC_DEVICESEL,7,25,172,33,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_VIDMODE,7,46,172,74,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP DEFPUSHBUTTON "EXIT",IDEXIT,103,69,52,14 DEFPUSHBUTTON "OK",IDOK,28,69,50,14 LTEXT "Please select the Device To Use:",IDC_SELECTDEVICE,7,7, 137,8 END ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_MAIN_ICON ICON DISCARDABLE "gtavc.ico" ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: src/text/Messages.cpp ================================================ #include "common.h" #include "Messages.h" #include "RwHelper.h" #include "Hud.h" #include "User.h" #include "Timer.h" #include "Text.h" #include "ControllerConfig.h" #include "Font.h" tMessage CMessages::BriefMessages[NUMBRIEFMESSAGES]; tPreviousBrief CMessages::PreviousBriefs[NUMPREVIOUSBRIEFS]; tBigMessage CMessages::BIGMessages[NUMBIGMESSAGES]; char CMessages::PreviousMissionTitle[16]; // unused void CMessages::Init() { ClearMessages(); for (int32 i = 0; i < NUMPREVIOUSBRIEFS; i++) { PreviousBriefs[i].m_pText = nil; PreviousBriefs[i].m_pString = nil; } } uint16 CMessages::GetWideStringLength(wchar *src) { uint16 length = 0; while (*(src++)) length++; return length; } void CMessages::WideStringCopy(wchar *dst, wchar *src, uint16 size) { int32 i = 0; if (src) { while (i < size - 1) { if (!src[i]) break; dst[i] = src[i]; i++; } } else { while (i < size - 1) dst[i++] = '\0'; } dst[i] = '\0'; } wchar FixupChar(wchar c) { #ifdef MORE_LANGUAGES if (CFont::IsJapanese()) return c & 0x7fff; #endif return c; } bool CMessages::WideStringCompare(wchar *str1, wchar *str2, uint16 size) { uint16 len1 = GetWideStringLength(str1); uint16 len2 = GetWideStringLength(str2); if (len1 != len2 && (len1 < size || len2 < size)) return false; for (int32 i = 0; i < size && FixupChar(str1[i]) != '\0'; i++) { if (FixupChar(str1[i]) != FixupChar(str2[i])) return false; } return true; } void CMessages::Process() { for (int32 style = 0; style < 6; style++) { if (BIGMessages[style].m_Stack[0].m_pText != nil && CTimer::GetTimeInMilliseconds() > BIGMessages[style].m_Stack[0].m_nTime + BIGMessages[style].m_Stack[0].m_nStartTime) { BIGMessages[style].m_Stack[0].m_pText = nil; int32 i = 0; while (i < 3) { if (BIGMessages[style].m_Stack[i + 1].m_pText == nil) break; BIGMessages[style].m_Stack[i] = BIGMessages[style].m_Stack[i + 1]; i++; } BIGMessages[style].m_Stack[i].m_pText = nil; BIGMessages[style].m_Stack[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); } } if (BriefMessages[0].m_pText != nil && CTimer::GetTimeInMilliseconds() > BriefMessages[0].m_nTime + BriefMessages[0].m_nStartTime) { BriefMessages[0].m_pText = nil; int32 i; for (i = 0; i < NUMBRIEFMESSAGES-1 && BriefMessages[i + 1].m_pText != nil; i++) { BriefMessages[i] = BriefMessages[i + 1]; } CMessages::BriefMessages[i].m_pText = nil; CMessages::BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); if (BriefMessages[0].m_pText != nil) AddToPreviousBriefArray( BriefMessages[0].m_pText, BriefMessages[0].m_nNumber[0], BriefMessages[0].m_nNumber[1], BriefMessages[0].m_nNumber[2], BriefMessages[0].m_nNumber[3], BriefMessages[0].m_nNumber[4], BriefMessages[0].m_nNumber[5], BriefMessages[0].m_pString); } } void CMessages::Display() { wchar outstr[256]; DefinedState(); for (int32 i = 0; i < NUMBIGMESSAGES; i++) { InsertNumberInString( BIGMessages[i].m_Stack[0].m_pText, BIGMessages[i].m_Stack[0].m_nNumber[0], BIGMessages[i].m_Stack[0].m_nNumber[1], BIGMessages[i].m_Stack[0].m_nNumber[2], BIGMessages[i].m_Stack[0].m_nNumber[3], BIGMessages[i].m_Stack[0].m_nNumber[4], BIGMessages[i].m_Stack[0].m_nNumber[5], outstr); InsertStringInString(outstr, BIGMessages[i].m_Stack[0].m_pString); InsertPlayerControlKeysInString(outstr); CHud::SetBigMessage(outstr, i); } InsertNumberInString( BriefMessages[0].m_pText, BriefMessages[0].m_nNumber[0], BriefMessages[0].m_nNumber[1], BriefMessages[0].m_nNumber[2], BriefMessages[0].m_nNumber[3], BriefMessages[0].m_nNumber[4], BriefMessages[0].m_nNumber[5], outstr); InsertStringInString(outstr, BriefMessages[0].m_pString); InsertPlayerControlKeysInString(outstr); CHud::SetMessage(outstr); } void CMessages::AddMessage(wchar *msg, uint32 time, uint16 flag) { wchar outstr[512]; // unused WideStringCopy(outstr, msg, 256); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); int32 i = 0; while (i < NUMBRIEFMESSAGES && BriefMessages[i].m_pText != nil) i++; if (i >= NUMBRIEFMESSAGES) return; BriefMessages[i].m_pText = msg; BriefMessages[i].m_nFlag = flag; BriefMessages[i].m_nTime = time; BriefMessages[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[i].m_nNumber[0] = -1; BriefMessages[i].m_nNumber[1] = -1; BriefMessages[i].m_nNumber[2] = -1; BriefMessages[i].m_nNumber[3] = -1; BriefMessages[i].m_nNumber[4] = -1; BriefMessages[i].m_nNumber[5] = -1; BriefMessages[i].m_pString = nil; if (i == 0) AddToPreviousBriefArray( BriefMessages[0].m_pText, BriefMessages[0].m_nNumber[0], BriefMessages[0].m_nNumber[1], BriefMessages[0].m_nNumber[2], BriefMessages[0].m_nNumber[3], BriefMessages[0].m_nNumber[4], BriefMessages[0].m_nNumber[5], BriefMessages[0].m_pString); } void CMessages::AddMessageJumpQ(wchar *msg, uint32 time, uint16 flag) { wchar outstr[512]; // unused WideStringCopy(outstr, msg, 256); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); BriefMessages[0].m_pText = msg; BriefMessages[0].m_nFlag = flag; BriefMessages[0].m_nTime = time; BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[0].m_nNumber[0] = -1; BriefMessages[0].m_nNumber[1] = -1; BriefMessages[0].m_nNumber[2] = -1; BriefMessages[0].m_nNumber[3] = -1; BriefMessages[0].m_nNumber[4] = -1; BriefMessages[0].m_nNumber[5] = -1; BriefMessages[0].m_pString = nil; AddToPreviousBriefArray(msg, -1, -1, -1, -1, -1, -1, 0); } void CMessages::AddMessageSoon(wchar *msg, uint32 time, uint16 flag) { wchar outstr[512]; // unused WideStringCopy(outstr, msg, 256); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); if (BriefMessages[0].m_pText != nil) { for (int i = NUMBRIEFMESSAGES-1; i > 1; i--) BriefMessages[i] = BriefMessages[i-1]; BriefMessages[1].m_pText = msg; BriefMessages[1].m_nFlag = flag; BriefMessages[1].m_nTime = time; BriefMessages[1].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[1].m_nNumber[0] = -1; BriefMessages[1].m_nNumber[1] = -1; BriefMessages[1].m_nNumber[2] = -1; BriefMessages[1].m_nNumber[3] = -1; BriefMessages[1].m_nNumber[4] = -1; BriefMessages[1].m_nNumber[5] = -1; BriefMessages[1].m_pString = nil; }else{ BriefMessages[0].m_pText = msg; BriefMessages[0].m_nFlag = flag; BriefMessages[0].m_nTime = time; BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[0].m_nNumber[0] = -1; BriefMessages[0].m_nNumber[1] = -1; BriefMessages[0].m_nNumber[2] = -1; BriefMessages[0].m_nNumber[3] = -1; BriefMessages[0].m_nNumber[4] = -1; BriefMessages[0].m_nNumber[5] = -1; BriefMessages[0].m_pString = nil; AddToPreviousBriefArray(msg, -1, -1, -1, -1, -1, -1, nil); } } void CMessages::ClearMessages() { for (int32 i = 0; i < NUMBIGMESSAGES; i++) { for (int32 j = 0; j < 4; j++) { BIGMessages[i].m_Stack[j].m_pText = nil; BIGMessages[i].m_Stack[j].m_pString = nil; } } ClearSmallMessagesOnly(); } void CMessages::ClearSmallMessagesOnly() { for (int32 i = 0; i < NUMBRIEFMESSAGES; i++) { BriefMessages[i].m_pText = nil; BriefMessages[i].m_pString = nil; } } void CMessages::AddBigMessage(wchar *msg, uint32 time, uint16 style) { wchar outstr[512]; // unused WideStringCopy(outstr, msg, 256); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); BIGMessages[style].m_Stack[0].m_pText = msg; BIGMessages[style].m_Stack[0].m_nFlag = 0; BIGMessages[style].m_Stack[0].m_nTime = time; BIGMessages[style].m_Stack[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); BIGMessages[style].m_Stack[0].m_nNumber[0] = -1; BIGMessages[style].m_Stack[0].m_nNumber[1] = -1; BIGMessages[style].m_Stack[0].m_nNumber[2] = -1; BIGMessages[style].m_Stack[0].m_nNumber[3] = -1; BIGMessages[style].m_Stack[0].m_nNumber[4] = -1; BIGMessages[style].m_Stack[0].m_nNumber[5] = -1; BIGMessages[style].m_Stack[0].m_pString = nil; } void CMessages::AddBigMessageQ(wchar *msg, uint32 time, uint16 style) { wchar outstr[512]; // unused WideStringCopy(outstr, msg, 256); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); int32 i = 0; while (i < 4 && BIGMessages[style].m_Stack[i].m_pText != nil) i++; if (i >= 4) return; BIGMessages[style].m_Stack[i].m_pText = msg; BIGMessages[style].m_Stack[i].m_nFlag = 0; BIGMessages[style].m_Stack[i].m_nTime = time; BIGMessages[style].m_Stack[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); BIGMessages[style].m_Stack[i].m_nNumber[0] = -1; BIGMessages[style].m_Stack[i].m_nNumber[1] = -1; BIGMessages[style].m_Stack[i].m_nNumber[2] = -1; BIGMessages[style].m_Stack[i].m_nNumber[3] = -1; BIGMessages[style].m_Stack[i].m_nNumber[4] = -1; BIGMessages[style].m_Stack[i].m_nNumber[5] = -1; BIGMessages[style].m_Stack[i].m_pString = nil; } void CMessages::AddToPreviousBriefArray(wchar *text, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, wchar *string) { int32 i; for (i = 0; i < NUMPREVIOUSBRIEFS && PreviousBriefs[i].m_pText != nil; i++) { if (PreviousBriefs[i].m_nNumber[0] == n1 && PreviousBriefs[i].m_nNumber[1] == n2 && PreviousBriefs[i].m_nNumber[2] == n3 && PreviousBriefs[i].m_nNumber[3] == n4 && PreviousBriefs[i].m_nNumber[4] == n5 && PreviousBriefs[i].m_nNumber[5] == n6 && PreviousBriefs[i].m_pText == text && PreviousBriefs[i].m_pString == string) return; } if (i != 0) { if (i == NUMPREVIOUSBRIEFS) i -= 2; else i--; while (i >= 0) { PreviousBriefs[i + 1] = PreviousBriefs[i]; i--; } } PreviousBriefs[0].m_pText = text; PreviousBriefs[0].m_nNumber[0] = n1; PreviousBriefs[0].m_nNumber[1] = n2; PreviousBriefs[0].m_nNumber[2] = n3; PreviousBriefs[0].m_nNumber[3] = n4; PreviousBriefs[0].m_nNumber[4] = n5; PreviousBriefs[0].m_nNumber[5] = n6; PreviousBriefs[0].m_pString = string; } void CMessages::InsertNumberInString(wchar *str, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, wchar *outstr) { char numStr[10]; wchar wNumStr[10]; if (str == nil) { *outstr = '\0'; return; } sprintf(numStr, "%d", n1); size_t outLen = strlen(numStr); AsciiToUnicode(numStr, wNumStr); if (str[0] == 0) { *outstr = '\0'; return; } int32 size = GetWideStringLength(str); int32 i = 0; for (int32 c = 0; c < size;) { #ifdef MORE_LANGUAGES if ((CFont::IsJapanese() && str[c] == (0x8000 | '~') && str[c + 1] == (0x8000 | '1') && str[c + 2] == (0x8000 | '~')) || (!CFont::IsJapanese() && str[c] == '~' && str[c + 1] == '1' && str[c + 2] == '~')) { #else if (str[c] == '~' && str[c + 1] == '1' && str[c + 2] == '~') { #endif c += 3; for (int j = 0; j < outLen; ) *(outstr++) = wNumStr[j++]; i++; switch (i) { case 1: sprintf(numStr, "%d", n2); break; case 2: sprintf(numStr, "%d", n3); break; case 3: sprintf(numStr, "%d", n4); break; case 4: sprintf(numStr, "%d", n5); break; case 5: sprintf(numStr, "%d", n6); break; } outLen = strlen(numStr); AsciiToUnicode(numStr, wNumStr); } else { *(outstr++) = str[c++]; } } *outstr = '\0'; } void CMessages::InsertStringInString(wchar *str1, wchar *str2) { wchar tempstr[256]; if (!str1 || !str2) return; int32 str1_size = GetWideStringLength(str1); int32 str2_size = GetWideStringLength(str2); int32 total_size = str1_size + str2_size; wchar *_str1 = str1; uint16 i; for (i = 0; i < total_size; ) { #ifdef MORE_LANGUAGES if ((CFont::IsJapanese() && *_str1 == (0x8000 | '~') && *(_str1 + 1) == (0x8000 | 'a') && *(_str1 + 2) == (0x8000 | '~')) || (*_str1 == '~' && *(_str1 + 1) == 'a' && *(_str1 + 2) == '~')) { #else if (*_str1 == '~' && *(_str1 + 1) == 'a' && *(_str1 + 2) == '~') { #endif _str1 += 3; for (int j = 0; j < str2_size; j++) { tempstr[i++] = str2[j]; } } else { tempstr[i++] = *(_str1++); } } tempstr[i] = '\0'; for (i = 0; i < total_size; i++) str1[i] = tempstr[i]; while (i < 256) str1[i++] = '\0'; } void CMessages::InsertPlayerControlKeysInString(wchar *str) { uint16 i; wchar outstr[256]; wchar keybuf[256]; if (!str) return; uint16 strSize = GetWideStringLength(str); memset(keybuf, 0, 256*sizeof(wchar)); wchar *_outstr = outstr; for (i = 0; i < strSize;) { #ifdef MORE_LANGUAGES if ((CFont::IsJapanese() && str[i] == (0x8000 | '~') && str[i + 1] == (0x8000 | 'k') && str[i + 2] == (0x8000 | '~')) || (!CFont::IsJapanese() && str[i] == '~' && str[i + 1] == 'k' && str[i + 2] == '~')) { #else if (str[i] == '~' && str[i + 1] == 'k' && str[i + 2] == '~') { #endif i += 4; bool done = false; for (int32 cont = 0; cont < MAX_CONTROLLERACTIONS && !done; cont++) { uint16 contSize = GetWideStringLength(ControlsManager.m_aActionNames[cont]); if (contSize != 0) { if (WideStringCompare(&str[i], ControlsManager.m_aActionNames[cont], contSize)) { done = true; ControlsManager.GetWideStringOfCommandKeys(cont, keybuf, 256); uint16 keybuf_size = GetWideStringLength(keybuf); for (uint16 j = 0; j < keybuf_size; j++) { *(_outstr++) = keybuf[j]; keybuf[j] = '\0'; } i += contSize + 1; } } } } else { *(_outstr++) = str[i++]; } } *_outstr = '\0'; for (i = 0; i < GetWideStringLength(outstr); i++) str[i] = outstr[i]; while (i < 256) str[i++] = '\0'; } void CMessages::AddMessageWithNumber(wchar *str, uint32 time, uint16 flag, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) { wchar outstr[512]; // unused InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); uint16 i = 0; while (i < NUMBRIEFMESSAGES && BriefMessages[i].m_pText != nil) i++; if (i >= NUMBRIEFMESSAGES) return; BriefMessages[i].m_pText = str; BriefMessages[i].m_nFlag = flag; BriefMessages[i].m_nTime = time; BriefMessages[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[i].m_nNumber[0] = n1; BriefMessages[i].m_nNumber[1] = n2; BriefMessages[i].m_nNumber[2] = n3; BriefMessages[i].m_nNumber[3] = n4; BriefMessages[i].m_nNumber[4] = n5; BriefMessages[i].m_nNumber[5] = n6; BriefMessages[i].m_pString = nil; if (i == 0) AddToPreviousBriefArray( BriefMessages[0].m_pText, BriefMessages[0].m_nNumber[0], BriefMessages[0].m_nNumber[1], BriefMessages[0].m_nNumber[2], BriefMessages[0].m_nNumber[3], BriefMessages[0].m_nNumber[4], BriefMessages[0].m_nNumber[5], BriefMessages[0].m_pString); } void CMessages::AddMessageJumpQWithNumber(wchar *str, uint32 time, uint16 flag, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) { wchar outstr[512]; // unused InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); BriefMessages[0].m_pText = str; BriefMessages[0].m_nFlag = flag; BriefMessages[0].m_nTime = time; BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[0].m_nNumber[0] = n1; BriefMessages[0].m_nNumber[1] = n2; BriefMessages[0].m_nNumber[2] = n3; BriefMessages[0].m_nNumber[3] = n4; BriefMessages[0].m_nNumber[4] = n5; BriefMessages[0].m_nNumber[5] = n6; BriefMessages[0].m_pString = nil; AddToPreviousBriefArray(str, n1, n2, n3, n4, n5, n6, nil); } void CMessages::AddMessageSoonWithNumber(wchar *str, uint32 time, uint16 flag, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) { wchar outstr[512]; // unused InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); if (BriefMessages[0].m_pText != nil) { for (int32 i = NUMBRIEFMESSAGES-1; i > 1; i--) BriefMessages[i] = BriefMessages[i-1]; BriefMessages[1].m_pText = str; BriefMessages[1].m_nFlag = flag; BriefMessages[1].m_nTime = time; BriefMessages[1].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[1].m_nNumber[0] = n1; BriefMessages[1].m_nNumber[1] = n2; BriefMessages[1].m_nNumber[2] = n3; BriefMessages[1].m_nNumber[3] = n4; BriefMessages[1].m_nNumber[4] = n5; BriefMessages[1].m_nNumber[5] = n6; BriefMessages[1].m_pString = nil; } else { BriefMessages[0].m_pText = str; BriefMessages[0].m_nFlag = flag; BriefMessages[0].m_nTime = time; BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[0].m_nNumber[0] = n1; BriefMessages[0].m_nNumber[1] = n2; BriefMessages[0].m_nNumber[2] = n3; BriefMessages[0].m_nNumber[3] = n4; BriefMessages[0].m_nNumber[4] = n5; BriefMessages[0].m_nNumber[5] = n6; BriefMessages[0].m_pString = nil; AddToPreviousBriefArray(str, n1, n2, n3, n4, n5, n6, nil); } } void CMessages::AddBigMessageWithNumber(wchar *str, uint32 time, uint16 style, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) { wchar outstr[512]; // unused InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); BIGMessages[style].m_Stack[0].m_pText = str; BIGMessages[style].m_Stack[0].m_nFlag = 0; BIGMessages[style].m_Stack[0].m_nTime = time; BIGMessages[style].m_Stack[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); BIGMessages[style].m_Stack[0].m_nNumber[0] = n1; BIGMessages[style].m_Stack[0].m_nNumber[1] = n2; BIGMessages[style].m_Stack[0].m_nNumber[2] = n3; BIGMessages[style].m_Stack[0].m_nNumber[3] = n4; BIGMessages[style].m_Stack[0].m_nNumber[4] = n5; BIGMessages[style].m_Stack[0].m_nNumber[5] = n6; BIGMessages[style].m_Stack[0].m_pString = nil; } void CMessages::AddBigMessageWithNumberQ(wchar *str, uint32 time, uint16 style, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6) { wchar outstr[512]; // unused InsertNumberInString(str, n1, n2, n3, n4, n5, n6, outstr); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); int32 i = 0; while (i < 4 && BIGMessages[style].m_Stack[i].m_pText != nil) i++; if (i >= 4) return; BIGMessages[style].m_Stack[i].m_pText = str; BIGMessages[style].m_Stack[i].m_nFlag = 0; BIGMessages[style].m_Stack[i].m_nTime = time; BIGMessages[style].m_Stack[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); BIGMessages[style].m_Stack[i].m_nNumber[0] = n1; BIGMessages[style].m_Stack[i].m_nNumber[1] = n2; BIGMessages[style].m_Stack[i].m_nNumber[2] = n3; BIGMessages[style].m_Stack[i].m_nNumber[3] = n4; BIGMessages[style].m_Stack[i].m_nNumber[4] = n5; BIGMessages[style].m_Stack[i].m_nNumber[5] = n6; BIGMessages[style].m_Stack[i].m_pString = nil; } void CMessages::AddMessageWithString(wchar *text, uint32 time, uint16 flag, wchar *str) { wchar outstr[512]; // unused WideStringCopy(outstr, text, 256); InsertStringInString(outstr, str); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); int32 i = 0; while (i < NUMBRIEFMESSAGES && BriefMessages[i].m_pText != nil) i++; if (i >= NUMBRIEFMESSAGES) return; BriefMessages[i].m_pText = text; BriefMessages[i].m_nFlag = flag; BriefMessages[i].m_nTime = time; BriefMessages[i].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[i].m_nNumber[0] = -1; BriefMessages[i].m_nNumber[1] = -1; BriefMessages[i].m_nNumber[2] = -1; BriefMessages[i].m_nNumber[3] = -1; BriefMessages[i].m_nNumber[4] = -1; BriefMessages[i].m_nNumber[5] = -1; BriefMessages[i].m_pString = str; if (i == 0) AddToPreviousBriefArray( BriefMessages[0].m_pText, BriefMessages[0].m_nNumber[0], BriefMessages[0].m_nNumber[1], BriefMessages[0].m_nNumber[2], BriefMessages[0].m_nNumber[3], BriefMessages[0].m_nNumber[4], BriefMessages[0].m_nNumber[5], BriefMessages[0].m_pString); } void CMessages::AddMessageJumpQWithString(wchar *text, uint32 time, uint16 flag, wchar *str) { wchar outstr[512]; // unused WideStringCopy(outstr, text, 256); InsertStringInString(outstr, str); InsertPlayerControlKeysInString(outstr); GetWideStringLength(outstr); BriefMessages[0].m_pText = text; BriefMessages[0].m_nFlag = flag; BriefMessages[0].m_nTime = time; BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); BriefMessages[0].m_nNumber[0] = -1; BriefMessages[0].m_nNumber[1] = -1; BriefMessages[0].m_nNumber[2] = -1; BriefMessages[0].m_nNumber[3] = -1; BriefMessages[0].m_nNumber[4] = -1; BriefMessages[0].m_nNumber[5] = -1; BriefMessages[0].m_pString = str; AddToPreviousBriefArray(text, -1, -1, -1, -1, -1, -1, str); } inline bool FastWideStringComparison(wchar *str1, wchar *str2) { while (*str1 == *str2) { ++str1; ++str2; if (!*str1 && !*str2) return true; } return false; } void CMessages::ClearThisPrint(wchar *str) { bool equal; do { equal = false; uint16 i; for (i = 0; i < NUMBRIEFMESSAGES && BriefMessages[i].m_pText != nil; i++) { equal = FastWideStringComparison(str, BriefMessages[i].m_pText); if (equal) break; } if (equal) { if (i != 0) { BriefMessages[i].m_pText = nil; for (; i < NUMBRIEFMESSAGES-1 && BriefMessages[i+1].m_pText != nil; i++) { BriefMessages[i] = BriefMessages[i + 1]; } BriefMessages[i].m_pText = nil; } else { BriefMessages[0].m_pText = nil; for (; i < NUMBRIEFMESSAGES-1 && BriefMessages[i+1].m_pText != nil; i++) { BriefMessages[i] = BriefMessages[i + 1]; } BriefMessages[i].m_pText = nil; BriefMessages[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); if (BriefMessages[0].m_pText != nil) AddToPreviousBriefArray( BriefMessages[0].m_pText, BriefMessages[0].m_nNumber[0], BriefMessages[0].m_nNumber[1], BriefMessages[0].m_nNumber[2], BriefMessages[0].m_nNumber[3], BriefMessages[0].m_nNumber[4], BriefMessages[0].m_nNumber[5], BriefMessages[0].m_pString); } } } while (equal); } void CMessages::ClearThisBigPrint(wchar *str) { bool equal; do { uint16 i = 0; equal = false; uint16 style = 0; while (style < NUMBIGMESSAGES) { if (i >= 4) break; if (CMessages::BIGMessages[style].m_Stack[i].m_pText == nil || equal) break; equal = FastWideStringComparison(str, BIGMessages[style].m_Stack[i].m_pText); if (!equal && ++i == 4) { i = 0; style++; } } if (equal) { if (i != 0) { BIGMessages[style].m_Stack[i].m_pText = nil; while (i < 3) { if (BIGMessages[style].m_Stack[i + 1].m_pText == nil) break; BIGMessages[style].m_Stack[i] = BIGMessages[style].m_Stack[i + 1]; i++; } BIGMessages[style].m_Stack[i].m_pText = nil; } else { BIGMessages[style].m_Stack[0].m_pText = nil; i = 0; while (i < 3) { if (BIGMessages[style].m_Stack[i + 1].m_pText == nil) break; BIGMessages[style].m_Stack[i] = BIGMessages[style].m_Stack[i + 1]; i++; } BIGMessages[style].m_Stack[i].m_pText = nil; BIGMessages[style].m_Stack[0].m_nStartTime = CTimer::GetTimeInMilliseconds(); } } } while (equal); } void CMessages::ClearAllMessagesDisplayedByGame() { ClearMessages(); for (int32 i = 0; i < NUMPREVIOUSBRIEFS; i++) { PreviousBriefs[i].m_pText = nil; PreviousBriefs[i].m_pString = nil; } CHud::GetRidOfAllHudMessages(); CUserDisplay::Pager.ClearMessages(); } ================================================ FILE: src/text/Messages.h ================================================ #pragma once struct tMessage { wchar *m_pText; uint16 m_nFlag; uint32 m_nTime; uint32 m_nStartTime; int32 m_nNumber[6]; wchar *m_pString; }; struct tBigMessage { tMessage m_Stack[4]; }; struct tPreviousBrief { wchar *m_pText; int32 m_nNumber[6]; wchar *m_pString; }; #define NUMBRIEFMESSAGES 8 #define NUMBIGMESSAGES 6 #define NUMPREVIOUSBRIEFS 5 class CMessages { public: static tMessage BriefMessages[NUMBRIEFMESSAGES]; static tBigMessage BIGMessages[NUMBIGMESSAGES]; static tPreviousBrief PreviousBriefs[NUMPREVIOUSBRIEFS]; static char PreviousMissionTitle[16]; // unused public: static void Init(void); static uint16 GetWideStringLength(wchar *src); static void WideStringCopy(wchar *dst, wchar *src, uint16 size); static bool WideStringCompare(wchar *str1, wchar *str2, uint16 size); static void Process(void); static void Display(void); static void AddMessage(wchar *key, uint32 time, uint16 pos); static void AddMessageJumpQ(wchar *key, uint32 time, uint16 pos); static void AddMessageSoon(wchar *key, uint32 time, uint16 pos); static void ClearMessages(void); static void ClearSmallMessagesOnly(void); static void AddBigMessage(wchar *key, uint32 time, uint16 pos); static void AddBigMessageQ(wchar *key, uint32 time, uint16 pos); static void AddToPreviousBriefArray(wchar *text, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, wchar *string); static void InsertNumberInString(wchar *src, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, wchar *dst); static void InsertStringInString(wchar *str1, wchar *str2); static void InsertPlayerControlKeysInString(wchar *src); static void AddMessageWithNumber(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); static void AddMessageJumpQWithNumber(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); static void AddMessageSoonWithNumber(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); static void AddBigMessageWithNumber(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); static void AddBigMessageWithNumberQ(wchar *key, uint32 time, uint16 pos, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6); static void AddMessageWithString(wchar *text, uint32 time, uint16 flag, wchar *str); static void AddMessageJumpQWithString(wchar *text, uint32 time, uint16 flag, wchar *str); static void ClearThisPrint(wchar *str); static void ClearThisBigPrint(wchar *str); static void ClearAllMessagesDisplayedByGame(void); // unused or cut //static void AddMessageSoonWithString(wchar*, uint32, uint16, wchar*); //static void CutString(int16, char*, char**); //static void PrintString(char*, int16, int16, int16); }; ================================================ FILE: src/text/Pager.cpp ================================================ #include "common.h" #include "Pager.h" #include "Timer.h" #include "Messages.h" #include "Hud.h" #include "Camera.h" void CPager::Init() { ClearMessages(); m_nNumDisplayLetters = 8; } void CPager::Process() { if (m_messages[0].m_pText != nil && m_messages[0].m_nCurrentPosition >= (int32)m_messages[0].m_nStringLength) { m_messages[0].m_pText = nil; uint16 i = 0; while (i < NUMPAGERMESSAGES-1) { if (m_messages[i + 1].m_pText == nil) break; m_messages[i] = m_messages[i + 1]; i++; } m_messages[i].m_pText = nil; if (m_messages[0].m_pText != nil) CMessages::AddToPreviousBriefArray( m_messages[0].m_pText, m_messages[0].m_nNumber[0], m_messages[0].m_nNumber[1], m_messages[0].m_nNumber[2], m_messages[0].m_nNumber[3], m_messages[0].m_nNumber[4], m_messages[0].m_nNumber[5], 0); } Display(); if (m_messages[0].m_pText != nil) { if (TheCamera.m_WideScreenOn || !CHud::m_Wants_To_Draw_Hud || CHud::m_BigMessage[0][0] || CHud::m_BigMessage[2][0]) { RestartCurrentMessage(); } else { if (CTimer::GetTimeInMilliseconds() > m_messages[0].m_nTimeToChangePosition) { m_messages[0].m_nCurrentPosition++; m_messages[0].m_nTimeToChangePosition = CTimer::GetTimeInMilliseconds() + m_messages[0].m_nSpeedMs; } } } } void CPager::Display() { wchar outstr1[256]; wchar outstr2[260]; wchar *pText = m_messages[0].m_pText; uint16 i = 0; if (pText != nil) { CMessages::InsertNumberInString( pText, m_messages[0].m_nNumber[0], m_messages[0].m_nNumber[1], m_messages[0].m_nNumber[2], m_messages[0].m_nNumber[3], m_messages[0].m_nNumber[4], m_messages[0].m_nNumber[5], outstr1); for (; i < m_nNumDisplayLetters; i++) { int pos = m_messages[0].m_nCurrentPosition + i; if (pos >= 0) { if (!outstr1[pos]) break; outstr2[i] = outstr1[pos]; } else { outstr2[i] = ' '; } } } outstr2[i] = '\0'; CHud::SetPagerMessage(outstr2); } void CPager::AddMessage(wchar *str, uint16 speed, uint16 priority, uint16 a5) { uint16 size = CMessages::GetWideStringLength(str); for (int32 i = 0; i < NUMPAGERMESSAGES; i++) { if (m_messages[i].m_pText) { if (m_messages[i].m_nPriority >= priority) continue; for (int j = NUMPAGERMESSAGES-1; j > i; j--) m_messages[j] = m_messages[j-1]; } m_messages[i].m_pText = str; m_messages[i].m_nSpeedMs = speed; m_messages[i].m_nPriority = priority; m_messages[i].unused = a5; m_messages[i].m_nCurrentPosition = -(m_nNumDisplayLetters + 10); m_messages[i].m_nTimeToChangePosition = CTimer::GetTimeInMilliseconds() + speed; m_messages[i].m_nStringLength = size; m_messages[i].m_nNumber[0] = -1; m_messages[i].m_nNumber[1] = -1; m_messages[i].m_nNumber[2] = -1; m_messages[i].m_nNumber[3] = -1; m_messages[i].m_nNumber[4] = -1; m_messages[i].m_nNumber[5] = -1; if (i == 0) CMessages::AddToPreviousBriefArray( m_messages[0].m_pText, m_messages[0].m_nNumber[0], m_messages[0].m_nNumber[1], m_messages[0].m_nNumber[2], m_messages[0].m_nNumber[3], m_messages[0].m_nNumber[4], m_messages[0].m_nNumber[5], nil); return; } } void CPager::AddMessageWithNumber(wchar *str, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, uint16 speed, uint16 priority, uint16 a11) { wchar nstr[520]; CMessages::InsertNumberInString(str, n1, n2, n3, n4, n5, n6, nstr); uint16 size = CMessages::GetWideStringLength(nstr); for (int32 i = 0; i < NUMPAGERMESSAGES; i++) { if (m_messages[i].m_pText) { if (m_messages[i].m_nPriority >= priority) continue; for (int j = NUMPAGERMESSAGES-1; j > i; j--) m_messages[j] = m_messages[j - 1]; } m_messages[i].m_pText = str; m_messages[i].m_nSpeedMs = speed; m_messages[i].m_nPriority = priority; m_messages[i].unused = a11; m_messages[i].m_nCurrentPosition = -(m_nNumDisplayLetters + 10); m_messages[i].m_nTimeToChangePosition = CTimer::GetTimeInMilliseconds() + speed; m_messages[i].m_nStringLength = size; m_messages[i].m_nNumber[0] = n1; m_messages[i].m_nNumber[1] = n2; m_messages[i].m_nNumber[2] = n3; m_messages[i].m_nNumber[3] = n4; m_messages[i].m_nNumber[4] = n5; m_messages[i].m_nNumber[5] = n6; if (i == 0) CMessages::AddToPreviousBriefArray( m_messages[0].m_pText, m_messages[0].m_nNumber[0], m_messages[0].m_nNumber[1], m_messages[0].m_nNumber[2], m_messages[0].m_nNumber[3], m_messages[0].m_nNumber[4], m_messages[0].m_nNumber[5], nil); return; } } void CPager::ClearMessages() { for (int32 i = 0; i < NUMPAGERMESSAGES; i++) m_messages[i].m_pText = nil; } void CPager::RestartCurrentMessage() { if (m_messages[0].m_pText != nil) { m_messages[0].m_nCurrentPosition = -(m_nNumDisplayLetters + 10); m_messages[0].m_nTimeToChangePosition = CTimer::GetTimeInMilliseconds() + m_messages[0].m_nSpeedMs; } } ================================================ FILE: src/text/Pager.h ================================================ #pragma once struct PagerMessage { wchar *m_pText; uint16 m_nSpeedMs; int16 m_nCurrentPosition; uint16 m_nStringLength; uint16 m_nPriority; uint32 m_nTimeToChangePosition; int16 unused; // but still set in SCM. importance? ringtone? int32 m_nNumber[6]; }; #define NUMPAGERMESSAGES 8 class CPager { int16 m_nNumDisplayLetters; PagerMessage m_messages[NUMPAGERMESSAGES]; public: void Init(); void Process(); void Display(); void AddMessage(wchar*, uint16, uint16, uint16); void AddMessageWithNumber(wchar *str, int32 n1, int32 n2, int32 n3, int32 n4, int32 n5, int32 n6, uint16 speed, uint16 priority, uint16 a11); void ClearMessages(); void RestartCurrentMessage(); }; ================================================ FILE: src/text/Text.cpp ================================================ #include "common.h" #include "FileMgr.h" #ifdef MORE_LANGUAGES #include "Game.h" #endif #include "Frontend.h" #include "Messages.h" #include "Text.h" #include "Timer.h" wchar WideErrorString[25]; CText TheText; CText::CText(void) { encoding = 'e'; bHasMissionTextOffsets = false; bIsMissionTextLoaded = false; memset(szMissionTableName, 0, sizeof(szMissionTableName)); memset(WideErrorString, 0, sizeof(WideErrorString)); } void CText::Load(void) { char filename[32]; size_t offset; int file; bool tkey_loaded = false, tdat_loaded = false; ChunkHeader m_ChunkHeader; bIsMissionTextLoaded = false; bHasMissionTextOffsets = false; Unload(); CFileMgr::SetDir("TEXT"); switch(FrontEndMenuManager.m_PrefsLanguage){ case CMenuManager::LANGUAGE_AMERICAN: sprintf(filename, "AMERICAN.GXT"); break; case CMenuManager::LANGUAGE_FRENCH: sprintf(filename, "FRENCH.GXT"); break; case CMenuManager::LANGUAGE_GERMAN: sprintf(filename, "GERMAN.GXT"); break; case CMenuManager::LANGUAGE_ITALIAN: sprintf(filename, "ITALIAN.GXT"); break; case CMenuManager::LANGUAGE_SPANISH: sprintf(filename, "SPANISH.GXT"); break; #ifdef MORE_LANGUAGES case CMenuManager::LANGUAGE_POLISH: sprintf(filename, "POLISH.GXT"); break; case CMenuManager::LANGUAGE_RUSSIAN: sprintf(filename, "RUSSIAN.GXT"); break; case CMenuManager::LANGUAGE_JAPANESE: sprintf(filename, "JAPANESE.GXT"); break; #endif } file = CFileMgr::OpenFile(filename, "rb"); offset = 0; while (!tkey_loaded || !tdat_loaded) { ReadChunkHeader(&m_ChunkHeader, file, &offset); if (m_ChunkHeader.size != 0) { if (strncmp(m_ChunkHeader.magic, "TABL", 4) == 0) { MissionTextOffsets.Load(m_ChunkHeader.size, file, &offset, 0x58000); bHasMissionTextOffsets = true; } else if (strncmp(m_ChunkHeader.magic, "TKEY", 4) == 0) { this->keyArray.Load(m_ChunkHeader.size, file, &offset); tkey_loaded = true; } else if (strncmp(m_ChunkHeader.magic, "TDAT", 4) == 0) { this->data.Load(m_ChunkHeader.size, file, &offset); tdat_loaded = true; } else { CFileMgr::Seek(file, m_ChunkHeader.size, SEEK_CUR); offset += m_ChunkHeader.size; } } } keyArray.Update(data.chars); CFileMgr::CloseFile(file); CFileMgr::SetDir(""); } void CText::Unload(void) { CMessages::ClearAllMessagesDisplayedByGame(); keyArray.Unload(); data.Unload(); mission_keyArray.Unload(); mission_data.Unload(); bIsMissionTextLoaded = false; memset(szMissionTableName, 0, sizeof(szMissionTableName)); } wchar* CText::Get(const char *key) { uint8 result = false; #if defined (FIX_BUGS) || defined(FIX_BUGS_64) wchar *outstr = keyArray.Search(key, data.chars, &result); #else wchar *outstr = keyArray.Search(key, &result); #endif if (!result && bHasMissionTextOffsets && bIsMissionTextLoaded) #if defined (FIX_BUGS) || defined(FIX_BUGS_64) outstr = mission_keyArray.Search(key, mission_data.chars, &result); #else outstr = mission_keyArray.Search(key, &result); #endif return outstr; } wchar UpperCaseTable[128] = { 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 173, 173, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }; wchar FrenchUpperCaseTable[128] = { 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 65, 65, 65, 65, 132, 133, 69, 69, 69, 69, 73, 73, 73, 73, 79, 79, 79, 79, 85, 85, 85, 85, 173, 173, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }; wchar CText::GetUpperCase(wchar c) { switch (encoding) { case 'e': if (c >= 'a' && c <= 'z') return c - 32; break; case 'f': if (c >= 'a' && c <= 'z') return c - 32; if (c >= 128 && c <= 255) return FrenchUpperCaseTable[c-128]; break; case 'g': case 'i': case 's': if (c >= 'a' && c <= 'z') return c - 32; if (c >= 128 && c <= 255) return UpperCaseTable[c-128]; break; default: break; } return c; } void CText::UpperCase(wchar *s) { while(*s){ *s = GetUpperCase(*s); s++; } } void CText::GetNameOfLoadedMissionText(char *outName) { strcpy(outName, szMissionTableName); } void CText::ReadChunkHeader(ChunkHeader *buf, int32 file, size_t *offset) { #ifdef THIS_IS_STUPID char *_buf = (char*)buf; for (int i = 0; i < sizeof(ChunkHeader); i++) { CFileMgr::Read(file, &_buf[i], 1); (*offset)++; } #else // original code loops 8 times to read 1 byte with CFileMgr::Read, that's retarded CFileMgr::Read(file, (char*)buf, sizeof(ChunkHeader)); *offset += sizeof(ChunkHeader); #endif } void CText::LoadMissionText(char *MissionTableName) { char filename[32]; CMessages::ClearAllMessagesDisplayedByGame(); mission_keyArray.Unload(); mission_data.Unload(); bool search_result = false; int missionTableId = 0; for (missionTableId = 0; missionTableId < MissionTextOffsets.size; missionTableId++) { if (strncmp(MissionTextOffsets.data[missionTableId].szMissionName, MissionTableName, strlen(MissionTextOffsets.data[missionTableId].szMissionName)) == 0) { search_result = true; break; } } if (!search_result) { printf("CText::LoadMissionText - couldn't find %s", MissionTableName); return; } CFileMgr::SetDir("TEXT"); switch (FrontEndMenuManager.m_PrefsLanguage) { case CMenuManager::LANGUAGE_AMERICAN: sprintf(filename, "AMERICAN.GXT"); break; case CMenuManager::LANGUAGE_FRENCH: sprintf(filename, "FRENCH.GXT"); break; case CMenuManager::LANGUAGE_GERMAN: sprintf(filename, "GERMAN.GXT"); break; case CMenuManager::LANGUAGE_ITALIAN: sprintf(filename, "ITALIAN.GXT"); break; case CMenuManager::LANGUAGE_SPANISH: sprintf(filename, "SPANISH.GXT"); break; #ifdef MORE_LANGUAGES case CMenuManager::LANGUAGE_POLISH: sprintf(filename, "POLISH.GXT"); break; case CMenuManager::LANGUAGE_RUSSIAN: sprintf(filename, "RUSSIAN.GXT"); break; case CMenuManager::LANGUAGE_JAPANESE: sprintf(filename, "JAPANESE.GXT"); break; #endif } CTimer::Suspend(); int file = CFileMgr::OpenFile(filename, "rb"); CFileMgr::Seek(file, MissionTextOffsets.data[missionTableId].offset, SEEK_SET); char TableCheck[8]; CFileMgr::Read(file, TableCheck, 8); if (strncmp(TableCheck, MissionTableName, 8) != 0) printf("CText::LoadMissionText - expected to find %s in the text file", MissionTableName); bool tkey_loaded = false, tdat_loaded = false; ChunkHeader m_ChunkHeader; while (!tkey_loaded || !tdat_loaded) { size_t bytes_read = 0; ReadChunkHeader(&m_ChunkHeader, file, &bytes_read); if (m_ChunkHeader.size != 0) { if (strncmp(m_ChunkHeader.magic, "TKEY", 4) == 0) { size_t bytes_read = 0; mission_keyArray.Load(m_ChunkHeader.size, file, &bytes_read); tkey_loaded = true; } else if (strncmp(m_ChunkHeader.magic, "TDAT", 4) == 0) { size_t bytes_read = 0; mission_data.Load(m_ChunkHeader.size, file, &bytes_read); tdat_loaded = true; } else CFileMgr::Seek(file, m_ChunkHeader.size, SEEK_CUR); } } mission_keyArray.Update(mission_data.chars); CFileMgr::CloseFile(file); CTimer::Resume(); CFileMgr::SetDir(""); strcpy(szMissionTableName, MissionTableName); bIsMissionTextLoaded = true; } void CKeyArray::Load(size_t length, int file, size_t* offset) { char *rawbytes; // You can make numEntries size_t if you want to exceed 32-bit boundaries, everything else should be ready. numEntries = (int)(length / sizeof(CKeyEntry)); entries = new CKeyEntry[numEntries]; rawbytes = (char*)entries; #ifdef THIS_IS_STUPID for (uint32 i = 0; i < length; i++) { CFileMgr::Read(file, &rawbytes[i], 1); (*offset)++; } #else CFileMgr::Read(file, rawbytes, length); *offset += length; #endif } void CKeyArray::Unload(void) { delete[] entries; entries = nil; numEntries = 0; } void CKeyArray::Update(wchar *chars) { #if !defined(FIX_BUGS) && !defined(FIX_BUGS_64) int i; for(i = 0; i < numEntries; i++) entries[i].value = (wchar*)((uint8*)chars + (uintptr)entries[i].value); #endif } CKeyEntry* CKeyArray::BinarySearch(const char *key, CKeyEntry *entries, int16 low, int16 high) { int mid; int diff; if(low > high) return nil; mid = (low + high)/2; diff = strcmp(key, entries[mid].key); if(diff == 0) return &entries[mid]; if(diff < 0) return BinarySearch(key, entries, low, mid-1); if(diff > 0) return BinarySearch(key, entries, mid+1, high); return nil; } wchar* #if defined (FIX_BUGS) || defined(FIX_BUGS_64) CKeyArray::Search(const char *key, wchar *data, uint8 *result) #else CKeyArray::Search(const char *key, uint8 *result) #endif { CKeyEntry *found; char errstr[25]; int i; #if defined (FIX_BUGS) || defined(FIX_BUGS_64) found = BinarySearch(key, entries, 0, numEntries-1); if (found) { *result = true; return (wchar*)((uint8*)data + found->valueOffset); } #else found = BinarySearch(key, entries, 0, numEntries-1); if (found) { *result = true; return found->value; } #endif *result = false; #ifdef MASTER sprintf(errstr, ""); #else sprintf(errstr, "%s missing", key); #endif // MASTER for(i = 0; i < 25; i++) WideErrorString[i] = errstr[i]; return WideErrorString; } void CData::Load(size_t length, int file, size_t * offset) { char *rawbytes; // You can make numChars size_t if you want to exceed 32-bit boundaries, everything else should be ready. numChars = (int)(length / sizeof(wchar)); chars = new wchar[numChars]; rawbytes = (char*)chars; #ifdef THIS_IS_STUPID for(uint32 i = 0; i < length; i++){ CFileMgr::Read(file, &rawbytes[i], 1); (*offset)++; } #else CFileMgr::Read(file, rawbytes, length); *offset += length; #endif } void CData::Unload(void) { delete[] chars; chars = nil; numChars = 0; } void CMissionTextOffsets::Load(size_t table_size, int file, size_t *offset, int) { #ifdef THIS_IS_STUPID size_t num_of_entries = table_size / sizeof(CMissionTextOffsets::Entry); for (size_t mi = 0; mi < num_of_entries; mi++) { for (uint32 i = 0; i < sizeof(data[mi].szMissionName); i++) { CFileMgr::Read(file, &data[i].szMissionName[i], 1); (*offset)++; } char* _buf = (char*)&data[mi].offset; for (uint32 i = 0; i < sizeof(data[mi].offset); i++) { CFileMgr::Read(file, &_buf[i], 1); (*offset)++; } } size = (uint16)num_of_entries; #else // not exact VC code but smaller and better :P // You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready. size = (uint16) (table_size / sizeof(CMissionTextOffsets::Entry)); CFileMgr::Read(file, (char*)data, sizeof(CMissionTextOffsets::Entry) * size); *offset += sizeof(CMissionTextOffsets::Entry) * size; #endif } char* UnicodeToAscii(wchar *src) { static char aStr[256]; int len; for(len = 0; *src != '\0' && len < 256-1; len++, src++) #ifdef MORE_LANGUAGES if(*src < 128 || ((CGame::russianGame || CGame::japaneseGame) && *src < 256)) #else if(*src < 128) #endif aStr[len] = *src; // convert to CP1252 else if(*src <= 131) aStr[len] = *src + 64; else if (*src <= 141) aStr[len] = *src + 66; else if (*src <= 145) aStr[len] = *src + 68; else if (*src <= 149) aStr[len] = *src + 71; else if (*src <= 154) aStr[len] = *src + 73; else if (*src <= 164) aStr[len] = *src + 75; else if (*src <= 168) aStr[len] = *src + 77; else if (*src <= 204) aStr[len] = *src + 80; else switch (*src) { case 205: aStr[len] = 209; break; case 206: aStr[len] = 241; break; case 207: aStr[len] = 191; break; default: aStr[len] = '#'; break; } aStr[len] = '\0'; return aStr; } char* UnicodeToAsciiForSaveLoad(wchar *src) { static char aStr[256]; int len; for(len = 0; *src != '\0' && len < 256; len++, src++) if(*src < 256) aStr[len] = *src; else aStr[len] = '#'; aStr[len] = '\0'; return aStr; } char* UnicodeToAsciiForMemoryCard(wchar *src) { static char aStr[256]; int len; for(len = 0; *src != '\0' && len < 256; len++, src++) if(*src < 256) aStr[len] = *src; else aStr[len] = '#'; aStr[len] = '\0'; return aStr; } void TextCopy(wchar *dst, const wchar *src) { while((*dst++ = *src++) != '\0'); } ================================================ FILE: src/text/Text.h ================================================ #pragma once char *UnicodeToAscii(wchar *src); char *UnicodeToAsciiForSaveLoad(wchar *src); char *UnicodeToAsciiForMemoryCard(wchar *src); void TextCopy(wchar *dst, const wchar *src); struct CKeyEntry { #if defined(FIX_BUGS) || defined(FIX_BUGS_64) uint32 valueOffset; #else wchar *value; #endif char key[8]; }; // If this fails, CKeyArray::Load will have to be fixed VALIDATE_SIZE(CKeyEntry, 12); class CKeyArray { public: CKeyEntry *entries; int numEntries; // You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready. CKeyArray(void) : entries(nil), numEntries(0) {} ~CKeyArray(void) { Unload(); } void Load(size_t length, int file, size_t *offset); void Unload(void); void Update(wchar *chars); CKeyEntry *BinarySearch(const char *key, CKeyEntry *entries, int16 low, int16 high); #if defined (FIX_BUGS) || defined(FIX_BUGS_64) wchar *Search(const char *key, wchar *data, uint8 *result); #else wchar *Search(const char *key, uint8* result); #endif }; class CData { public: wchar *chars; int numChars; // You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready. CData(void) : chars(nil), numChars(0) {} ~CData(void) { Unload(); } void Load(size_t length, int file, size_t* offset); void Unload(void); }; class CMissionTextOffsets { public: struct Entry { char szMissionName[8]; uint32 offset; }; enum {MAX_MISSION_TEXTS = 90}; // beware that LCS has more Entry data[MAX_MISSION_TEXTS]; uint16 size; // You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready. CMissionTextOffsets(void) : size(0) {} void Load(size_t table_size, int file, size_t* bytes_read, int); }; struct ChunkHeader { char magic[4]; int size; }; class CText { CKeyArray keyArray; CData data; CKeyArray mission_keyArray; CData mission_data; char encoding; bool bHasMissionTextOffsets; bool bIsMissionTextLoaded; char szMissionTableName[8]; CMissionTextOffsets MissionTextOffsets; public: CText(void); void Load(void); void Unload(void); wchar *Get(const char *key); wchar GetUpperCase(wchar c); void UpperCase(wchar *s); void GetNameOfLoadedMissionText(char *outName); void ReadChunkHeader(ChunkHeader *buf, int32 file, size_t *bytes_read); void LoadMissionText(char *MissionTableName); }; extern CText TheText; ================================================ FILE: src/vehicles/Automobile.cpp ================================================ #include "common.h" #include "main.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 "Glass.h" #include "Antennas.h" #include "Skidmarks.h" #include "WindModifiers.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 "Stats.h" #include "Garages.h" #include "PathFind.h" #include "Replay.h" #include "AnimManager.h" #include "RpAnimBlend.h" #include "AnimBlendAssociation.h" #include "Ped.h" #include "PlayerPed.h" #include "Object.h" #include "Automobile.h" #include "Bike.h" #include "Wanted.h" bool bAllCarCheat; RwObject *GetCurrentAtomicObjectCB(RwObject *object, void *data); bool CAutomobile::m_sAllTaxiLights; const uint32 CAutomobile::nSaveStructSize = #ifdef COMPATIBLE_SAVES 1500; #else sizeof(CAutomobile); #endif 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; m_doingBurnout = 0; bTaxiLight = m_sAllTaxiLights; bFixedColour = false; bBigWheels = false; bWaterTight = false; SetModelIndex(id); // Already done in CVehicle... switch(GetModelIndex()){ case MI_HUNTER: case MI_ANGEL: case MI_FREEWAY: m_nRadioStation = V_ROCK; break; case MI_RCBARON: case MI_RCBANDIT: case MI_RCRAIDER: case MI_RCGOBLIN: case MI_TOPFUN: case MI_CADDY: case MI_BAGGAGE: m_nRadioStation = RADIO_OFF; break; } pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)mi->m_handlingId); pFlyingHandling = mod_HandlingManager.GetFlyingPointer((tVehicleType)mi->m_handlingId); m_auto_unused1 = 20.0f; m_auto_unused2 = 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_fOrientation = m_fPlaneSteer = 0.0f; m_nBusDoorTimerEnd = 0; m_nBusDoorTimerStart = 0; m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; m_fBrakePedal = 0.0f; m_pSetOnFireEntity = nil; m_fGasPedalAudio = 0.0f; bNotDamagedUpsideDown = false; bMoreResistantToDamage = false; bTankDetonateCars = true; bStuckInSand = false; bHeliDestroyed = 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_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i] = 1.0f; m_aWheelTimer[i] = 0.0f; m_aWheelRotation[i] = 0.0f; m_aWheelSpeed[i] = 0.0f; m_aWheelState[i] = WHEEL_STATE_NORMAL; m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; m_aWheelSkidmarkBloody[i] = false; } m_nWheelsOnGround = 0; m_nDriveWheelsOnGround = 0; m_nDriveWheelsOnGroundPrev = 0; m_fHeightAboveRoad = 0.0f; m_fTraction = 1.0f; m_fTireTemperature = 1.0f; CColModel *colModel = mi->GetColModel(); if(colModel->lines == nil){ colModel->lines = (CColLine*)RwMalloc(4*sizeof(CColLine)); colModel->numLines = 4; } SetupSuspensionLines(); SetStatus(STATUS_SIMPLE); bUseCollisionRecords = true; m_nNumPassengers = 0; 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_fHeliOrientation = -1.0f; m_weaponDoorTimerLeft = 0.0f; m_weaponDoorTimerRight = m_weaponDoorTimerLeft; if(GetModelIndex() == MI_DODO){ RpAtomicSetFlags((RpAtomic*)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_HUNTER){ RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); }else if(IsRealHeli()){ RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); RpAtomicSetFlags((RpAtomic*)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(); } #define SAND_SLOWDOWN (0.01f) float CAR_BALANCE_MULT = 0.3f; CVector vecSeaSparrowGunPos(-0.5f, 2.4f, -0.785f); CVector vecHunterGunPos(0.0f, 4.8f, -1.3f); CVector vecHunterRocketPos(2.5f, 1.0f, -0.5f); CVector vecDAMAGE_ENGINE_POS_SMALL(-0.1f, -0.1f, 0.0f); CVector vecDAMAGE_ENGINE_POS_BIG(-0.5f, -0.3f, 0.0f); #pragma optimize("", off) // a workaround for another compiler bug void CAutomobile::ProcessControl(void) { int i; float wheelRot; CColModel *colModel; float brake = 0.0f; if(bUsingSpecialColModel) colModel = &CWorld::Players[CWorld::PlayerInFocus].m_ColModel; else colModel = GetColModel(); bool drivingInSand = false; bWarnedPeds = false; m_doingBurnout = 0; bStuckInSand = false; bRestingOnPhysical = false; bool carHasNitro = bAllTaxisHaveNitro && GetStatus() == STATUS_PLAYER && IsTaxi(); if(CReplay::IsPlayingBack()) return; // Heli wind if(IsRealHeli()) if((GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PHYSICS) && m_aWheelSpeed[1] > 0.075f || GetStatus() == STATUS_SIMPLE) CWindModifiers::RegisterOne(GetPosition(), 1); UpdatePassengerList(); // Improve grip of vehicles in certain cases bool strongGrip1 = false; bool strongGrip2 = false; if(FindPlayerVehicle() && this != FindPlayerVehicle() && FindPlayerPed()->m_pWanted->GetWantedLevel() > 3 && (AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE || AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_FARAWAY || AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE) && FindPlayerSpeed().Magnitude() > 0.3f){ strongGrip1 = true; if(FindPlayerSpeed().Magnitude() > 0.4f && m_vecMoveSpeed.Magnitude() < 0.3f) strongGrip2 = true; else if((GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f) strongGrip2 = true; }else if(GetModelIndex() == MI_RCBANDIT && GetStatus() != STATUS_PLAYER_REMOTE) strongGrip1 = true; if(bIsBus) ProcessAutoBusDoors(); ProcessCarAlarm(); // Scan if this car sees the player committing any crimes if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED && GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && GetStatus() != STATUS_PLAYER_DISABLED){ switch(GetModelIndex()) case MI_FBIRANCH: case MI_POLICE: case MI_ENFORCER: case MI_SECURICA: case MI_RHINO: case MI_BARRACKS: ScanForCrimes(); } // Process driver if(pDriver) if(IsUpsideDown() && CanPedEnterCar()){ if(!pDriver->IsPlayer() && !(pDriver->m_leader && pDriver->m_leader->bInVehicle) && pDriver->CharCreatedBy != MISSION_CHAR) pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, this); } ActivateBombWhenEntered(); // 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_CAR, this); } CRubbish::StirUp(this); UpdateClumpAlpha(); 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.0f*m_fHeightAboveRoad; else if(pHandling->Flags & HANDLING_NONPLAYER_STABILISER && GetStatus() == STATUS_PHYSICS) m_vecCentreOfMass.z = pHandling->CentreOfMass.z - 0.2f*pHandling->Dimension.z; else m_vecCentreOfMass = pHandling->CentreOfMass; // Park car if(bCanPark && !bParking && VehicleCreatedBy != MISSION_VEHICLE && AutoPilot.m_nCarMission == MISSION_CRUISE && ((CTimer::GetFrameCounter() + m_randomSeed)&0xF) == 0 && !IsTaxi()){ CVector parkPosition = GetPosition() + 3.0f*GetRight() + 10.0f*GetForward(); CEntity *ent = nil; CColPoint colpoint; if(!CWorld::ProcessLineOfSight(GetPosition(), parkPosition, colpoint, ent, true, true, true, false, false, false) || ent == this) CCarAI::GetCarToParkAtCoors(this, &parkPosition); } // Process depending on status bool playerRemote = false; switch(GetStatus()){ case STATUS_PLAYER_REMOTE: #ifdef FIX_BUGS if(CPad::GetPad(0)->CarGunJustDown() && !bDisableRemoteDetonation){ #else if(CPad::GetPad(0)->WeaponJustDown() && !bDisableRemoteDetonation){ #endif BlowUpCar(FindPlayerPed()); CRemote::TakeRemoteControlledCarFromPlayer(); } if(GetModelIndex() == MI_RCBANDIT && !bDisableRemoteDetonationOnContact){ //CVector pos = GetPosition(); // FindPlayerCoors unused if(RcbanditCheckHitWheels() || bIsInWater){ 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 && pDriver->GetPedState() != PED_ARRESTED){ // process control input if controlled by player if(playerRemote || pDriver->m_nPedType == PEDTYPE_PLAYER1) ProcessControlInputs(0); PruneReferences(); if(GetStatus() == STATUS_PLAYER && !CRecordDataForChase::IsRecording()) DoDriveByShootings(); // Tweak center on mass when driving on two wheels int twoWheelTime = CWorld::Players[CWorld::PlayerInFocus].m_nTimeNotFullyOnGround; if(twoWheelTime > 500 && !IsRealHeli() && !IsRealPlane()){ float tweak = Min(twoWheelTime-500, 1000)/500.0f; if(GetUp().z > 0.0f){ // positive when on left wheels, negative on right wheels if(GetRight().z <= 0.0f) tweak *= -1.0f; m_vecCentreOfMass.z = pHandling->CentreOfMass.z + CPad::GetPad(0)->GetSteeringLeftRight()/128.0f * CAR_BALANCE_MULT * tweak * colModel->boundingBox.max.z; } }else m_vecCentreOfMass.z = pHandling->CentreOfMass.z; if(bHoverCheat) DoHoverSuspensionRatios(); if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_RHINO){ float slowdown; CVector parallelSpeed = m_vecMoveSpeed - DotProduct(m_vecMoveSpeed, GetUp())*GetUp(); float fSpeed = parallelSpeed.MagnitudeSqr(); if(fSpeed > SQR(0.3f)){ fSpeed = Sqrt(fSpeed); parallelSpeed *= 0.3f / fSpeed; slowdown = SAND_SLOWDOWN * Max(1.0f - 2.0f*fSpeed, 0.2f); }else{ bStuckInSand = true; slowdown = SAND_SLOWDOWN; } if(pHandling->Flags & HANDLING_GOOD_INSAND) slowdown *= 0.5f; if(CWeather::WetRoads > 0.2f) slowdown *= (1.2f - CWeather::WetRoads); ApplyMoveForce(parallelSpeed * -CTimer::GetTimeStep()*slowdown*m_fMass); drivingInSand = true; } } }else if(pDriver && pDriver->IsPlayer() && (pDriver->GetPedState() == PED_ARRESTED || pDriver->GetPedState() == PED_DRAG_FROM_CAR || (pDriver->GetPedState() == PED_EXIT_CAR || pDriver->m_objective == OBJECTIVE_LEAVE_CAR) && !CanPedJumpOutCar())){ bIsHandbrakeOn = true; m_fBrakePedal = 1.0f; m_fGasPedal = 0.0f; } if(CPad::GetPad(0)->CarGunJustDown()) ActivateBomb(); 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); wheelRot = ProcessWheelRotation(WHEEL_STATE_NORMAL, GetForward(), m_vecMoveSpeed, 0.35f); for(i = 0; i < 4; i++) m_aWheelRotation[i] += wheelRot; PlayHornIfNecessary(); ReduceHornCounter(); bVehicleColProcessed = false; bAudioChangingGear = false; // that's all we do for simple vehicles return; case STATUS_PHYSICS: CCarAI::UpdateCarAI(this); CCarCtrl::SteerAICarWithPhysics(this); PlayHornIfNecessary(); if(bIsBeingCarJacked){ m_fGasPedal = 0.0f; m_fBrakePedal = 1.0f; bIsHandbrakeOn = true; } if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_SANDKING && GetModelIndex() != MI_BFINJECT){ bStuckInSand = true; if(CWeather::WetRoads > 0.0f) ApplyMoveForce(m_vecMoveSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass * (1.0f-CWeather::WetRoads)); else ApplyMoveForce(m_vecMoveSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass); } } break; case STATUS_ABANDONED: if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f)) m_fBrakePedal = 0.2f; else m_fBrakePedal = 0.0f; bIsHandbrakeOn = false; m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; if(!IsAlarmOn()) m_nCarHornTimer = 0; if(bIsBeingCarJacked){ m_fGasPedal = 0.0f; m_fBrakePedal = 1.0f; bIsHandbrakeOn = true; } break; case STATUS_WRECKED: m_fBrakePedal = 0.05f; bIsHandbrakeOn = true; m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; if(!IsAlarmOn()) m_nCarHornTimer = 0; break; case STATUS_PLAYER_DISABLED: if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f) || (pDriver && pDriver->IsPlayer() && (pDriver->GetPedState() == PED_ARRESTED || pDriver->GetPedState() == PED_DRAG_FROM_CAR || (pDriver->GetPedState() == PED_EXIT_CAR || pDriver->m_objective == OBJECTIVE_LEAVE_CAR) && !CanPedJumpOutCar()))){ bIsHandbrakeOn = true; m_fBrakePedal = 1.0f; m_fGasPedal = 0.0f; }else{ m_fBrakePedal = 0.0f; bIsHandbrakeOn = false; } m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; if(!IsAlarmOn()) m_nCarHornTimer = 0; break; default: break; } // Skip physics if object is found to have been static recently bool skipPhysics = false; if(!bIsStuck && (GetStatus() == STATUS_ABANDONED || GetStatus() == 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(GetStatus() == 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 && !(m_fDamageImpulse > 0.0f && m_pDamageEntity && m_pDamageEntity->IsPed()) || 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; if(IsRealHeli() && m_aWheelSpeed[1] > 0.0f){ skipPhysics = false; m_nStaticFrames = 0; } } // Postpone for(i = 0; i < 4; i++) if(m_aGroundPhysical[i]){ bRestingOnPhysical = true; if(!CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ bWasPostponed = true; return; } } if(bRestingOnPhysical){ skipPhysics = false; m_nStaticFrames = 0; } VehicleDamage(0.0f, 0); // special control switch(GetModelIndex()){ case MI_FIRETRUCK: FireTruckControl(); break; case MI_RHINO: TankControl(); BlowUpCarsInPath(); break; case MI_VOODOO: HydraulicControl(); break; default: if(CVehicle::bCheat3 || carHasNitro){ // Make vehicle jump when horn is sounded if(GetStatus() == 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_fTurnMass*0.01f, GetForward()*1.0f); } } break; } if(GetStatus() == STATUS_PHYSICS || GetStatus() == STATUS_SIMPLE) if(AutoPilot.m_nCarMission == MISSION_HELI_FLYTOCOORS || AutoPilot.m_nCarMission == MISSION_PLANE_FLYTOCOORS) skipPhysics = true; if(skipPhysics){ bHasContacted = false; bIsInSafePosition = false; bWasPostponed = false; bHasHitWall = false; m_nCollisionRecords = 0; bHasCollided = false; bVehicleColProcessed = false; bAudioChangingGear = 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); m_fTireTemperature = 1.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; m_bIsVehicleBeingShifted = false; bSkipLineCol = 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 = Abs(DotProduct(m_vecMoveSpeed, GetForward())); CVector contactPoints[4]; // relative to model CVector contactSpeeds[4]; // speed at contact points CVector springDirections[4]; // normalized, in world 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; } }else if(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[i].surfaceB) == ADHESIVE_SAND && GetModelIndex() != MI_RHINO){ fwdSpeed *= 0.7f; float f = 1.0f - fwdSpeed/0.3f - 0.7f*CWeather::WetRoads; f = Max(f, 0.4f); m_aSuspensionSpringRatio[i] += 0.35f*f*(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 == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT) bias = 1.0f - bias; ApplySpringCollisionAlt(pHandling->fSuspensionForceLevel, springDirections[i], contactPoints[i], m_aSuspensionSpringRatio[i], bias, m_aWheelColPoints[i].normal); m_aWheelSkidmarkUnk[i] = false; if(m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || m_aWheelColPoints[i].surfaceB == SURFACE_MUD_DRY) m_aWheelSkidmarkType[i] = SKIDMARK_MUDDY; else if(m_aWheelColPoints[i].surfaceB == SURFACE_SAND || m_aWheelColPoints[i].surfaceB == SURFACE_SAND_BEACH){ m_aWheelSkidmarkType[i] = SKIDMARK_SANDY; m_aWheelSkidmarkUnk[i] = true; }else m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; }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 } if(m_aSuspensionSpringRatio[i] < 1.0f && m_aWheelColPoints[i].normal.z > 0.35f) springDirections[i] = -m_aWheelColPoints[i].normal; } // dampen springs for(i = 0; i < 4; i++) if(m_aSuspensionSpringRatio[i] < 0.99999f) 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 /= m_fForceMultiplier; if(IsRealHeli() || IsRealPlane()) acceleration = 0.0f; if(bAudioChangingGear && m_fGasPedal > 0.4f && m_fBrakePedal < 0.1f && fwdSpeed > 0.15f && this == FindPlayerVehicle() && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON){ if(GetStatus() == STATUS_PLAYER && !(pHandling->Flags & HANDLING_IS_BUS)){ if(m_nBusDoorTimerEnd == 0) m_nBusDoorTimerEnd = 1000; else { uint32 timeStepInMs = CTimer::GetTimeStepInMilliseconds(); if(m_nBusDoorTimerEnd > timeStepInMs) m_nBusDoorTimerEnd -= timeStepInMs; else m_nBusDoorTimerEnd = 0; } } if((m_aSuspensionSpringRatio[0] < 1.0f || m_aSuspensionSpringRatio[2] < 1.0f) && (m_aSuspensionSpringRatio[1] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f)) ApplyTurnForce(-GRAVITY*Min(m_fTurnMass, 2500.0f)*GetUp(), -1.0f*GetForward()); } brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (pHandling->Flags & HANDLING_NEUTRALHANDLING); float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias; float brakeBiasRear = neutralHandling ? 1.0f : 2.0f-pHandling->fBrakeBias; // looks like a bug, but it was correct in III... float tractionBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fTractionBias; float tractionBiasRear = neutralHandling ? 1.0f : 2.0f-tractionBiasFront; // 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(GetStatus() == STATUS_PHYSICS) traction = 0.004f * m_fTraction; else traction = 0.004f; traction *= pHandling->fTractionMultiplier / 4.0f; traction /= m_fForceMultiplier; if(CVehicle::bCheat3) traction *= 4.0f; if(FindPlayerVehicle() != this && (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]; bool rearWheelsFirst = !!(pHandling->Flags & HANDLING_REARWHEEL_1ST); // Process front wheels on ground - first try if(!rearWheelsFirst){ 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, wheelRight, tmp; if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) fThrust = acceleration; else fThrust = 0.0f; wheelFwd = GetForward(); wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal; wheelFwd.Normalise(); wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal); wheelRight.Normalise(); tmp = c*wheelFwd - s*wheelRight; wheelRight = s*wheelFwd + c*wheelRight; wheelFwd = tmp; m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_LEFT])*traction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB); WheelState[CARWHEEL_FRONT_LEFT] = m_aWheelState[CARWHEEL_FRONT_LEFT]; if(Damage.GetWheelStatus(CARWHEEL_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.HasFrontWheelDrive(pHandling->nIdentifier)) fThrust = acceleration; else fThrust = 0.0f; wheelFwd = GetForward(); wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal; wheelFwd.Normalise(); wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal); wheelRight.Normalise(); tmp = c*wheelFwd - s*wheelRight; wheelRight = s*wheelFwd + c*wheelRight; wheelFwd = tmp; m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT])*traction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB); WheelState[CARWHEEL_FRONT_RIGHT] = m_aWheelState[CARWHEEL_FRONT_RIGHT]; if(Damage.GetWheelStatus(CARWHEEL_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(!IsRealHeli()){ if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ 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; } }else{ m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; } m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; } if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ 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; } }else{ m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; } 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(); // overwritten for resp. wheel float rearBrake = brake; float rearTraction = traction; if(bIsHandbrakeOn){ #ifdef FIX_BUGS // Not sure if this is needed, but brake usually has timestep as a factor rearBrake = 20000.0f * CTimer::GetTimeStepFix(); #else rearBrake = 20000.0f; #endif if(fwdSpeed > 0.1f && pHandling->Flags & HANDLING_HANDBRAKE_TYRE){ m_fTireTemperature += 0.005*CTimer::GetTimeStep(); if(m_fTireTemperature > 2.0f) m_fTireTemperature = 2.0f; } }else if(m_doingBurnout && mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)){ rearBrake = 0.0f; rearTraction = 0.0f; // BUG: missing timestep ApplyTurnForce(contactPoints[CARWHEEL_REAR_LEFT], -0.001f*m_fTurnMass*m_fSteerAngle*GetRight()); }else if(m_fTireTemperature > 1.0f){ rearTraction *= m_fTireTemperature; } if(m_aWheelTimer[CARWHEEL_REAR_LEFT] > 0.0f){ if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) fThrust = acceleration; else fThrust = 0.0f; wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal)*m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal; wheelFwd.Normalise(); wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_LEFT].normal); wheelRight.Normalise(); m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_LEFT])*rearTraction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB); WheelState[CARWHEEL_REAR_LEFT] = m_aWheelState[CARWHEEL_REAR_LEFT]; if(Damage.GetWheelStatus(CARWHEEL_REAR_LEFT) == WHEEL_STATUS_BURST) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_LEFT], contactPoints[CARWHEEL_REAR_LEFT], m_nWheelsOnGround, fThrust, rearBrake*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, rearBrake*brakeBiasRear, adhesion*tractionBiasRear, CARWHEEL_REAR_LEFT, &m_aWheelSpeed[CARWHEEL_REAR_LEFT], &WheelState[CARWHEEL_REAR_LEFT], WHEEL_STATUS_OK); } #ifdef FIX_BUGS // Shouldn't we reset these after the left wheel? wheelFwd = GetForward(); wheelRight = GetRight(); // actually useless #endif if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] > 0.0f){ if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier)) fThrust = acceleration; else fThrust = 0.0f; wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal; wheelFwd.Normalise(); wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].normal); wheelRight.Normalise(); m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_REAR_RIGHT])*rearTraction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB); WheelState[CARWHEEL_REAR_RIGHT] = m_aWheelState[CARWHEEL_REAR_RIGHT]; if(Damage.GetWheelStatus(CARWHEEL_REAR_RIGHT) == WHEEL_STATUS_BURST) ProcessWheel(wheelFwd, wheelRight, contactSpeeds[CARWHEEL_REAR_RIGHT], contactPoints[CARWHEEL_REAR_RIGHT], m_nWheelsOnGround, fThrust, rearBrake*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, rearBrake*brakeBiasRear, adhesion*tractionBiasRear, CARWHEEL_REAR_RIGHT, &m_aWheelSpeed[CARWHEEL_REAR_RIGHT], &WheelState[CARWHEEL_REAR_RIGHT], WHEEL_STATUS_OK); } } if(m_doingBurnout && mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) && (m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING || m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING)){ m_fTireTemperature += 0.001f*CTimer::GetTimeStep(); if(m_fTireTemperature > 3.0f) m_fTireTemperature = 3.0f; }else if(m_fTireTemperature > 1.0f){ m_fTireTemperature = (m_fTireTemperature - 1.0f)*Pow(0.995f, CTimer::GetTimeStep()) + 1.0f; } // Process rear wheels off ground if(!IsRealHeli()){ if(m_aWheelTimer[CARWHEEL_REAR_LEFT] <= 0.0f){ if(bIsHandbrakeOn) m_aWheelSpeed[CARWHEEL_REAR_LEFT] = 0.0f; else if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ 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; } }else{ m_aWheelSpeed[CARWHEEL_REAR_LEFT] *= 0.95f; } m_aWheelRotation[CARWHEEL_REAR_LEFT] += m_aWheelSpeed[CARWHEEL_REAR_LEFT]; } if(m_aWheelTimer[CARWHEEL_REAR_RIGHT] <= 0.0f){ if(bIsHandbrakeOn) m_aWheelSpeed[CARWHEEL_REAR_RIGHT] = 0.0f; else if(mod_HandlingManager.HasRearWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ 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; } }else{ m_aWheelSpeed[CARWHEEL_REAR_RIGHT] *= 0.95f; } m_aWheelRotation[CARWHEEL_REAR_RIGHT] += m_aWheelSpeed[CARWHEEL_REAR_RIGHT]; } } // Process front wheels on ground - second try if(rearWheelsFirst){ 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, wheelRight, tmp; if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] > 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier)) fThrust = acceleration; else fThrust = 0.0f; wheelFwd = GetForward(); wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal; wheelFwd.Normalise(); wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].normal); wheelRight.Normalise(); tmp = c*wheelFwd - s*wheelRight; wheelRight = s*wheelFwd + c*wheelRight; wheelFwd = tmp; m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_LEFT])*traction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB); WheelState[CARWHEEL_FRONT_LEFT] = m_aWheelState[CARWHEEL_FRONT_LEFT]; if(Damage.GetWheelStatus(CARWHEEL_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.HasFrontWheelDrive(pHandling->nIdentifier)) fThrust = acceleration; else fThrust = 0.0f; wheelFwd = GetForward(); wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal)*m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal; wheelFwd.Normalise(); wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].normal); wheelRight.Normalise(); tmp = c*wheelFwd - s*wheelRight; wheelRight = s*wheelFwd + c*wheelRight; wheelFwd = tmp; m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT])*traction; if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB); WheelState[CARWHEEL_FRONT_RIGHT] = m_aWheelState[CARWHEEL_FRONT_RIGHT]; if(Damage.GetWheelStatus(CARWHEEL_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 (!IsRealHeli()) { if(m_aWheelTimer[CARWHEEL_FRONT_LEFT] <= 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ 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; } }else{ m_aWheelSpeed[CARWHEEL_FRONT_LEFT] *= 0.95f; } m_aWheelRotation[CARWHEEL_FRONT_LEFT] += m_aWheelSpeed[CARWHEEL_FRONT_LEFT]; } if(m_aWheelTimer[CARWHEEL_FRONT_RIGHT] <= 0.0f){ if(mod_HandlingManager.HasFrontWheelDrive(pHandling->nIdentifier) && acceleration != 0.0f){ 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; } }else{ m_aWheelSpeed[CARWHEEL_FRONT_RIGHT] *= 0.95f; } m_aWheelRotation[CARWHEEL_FRONT_RIGHT] += m_aWheelSpeed[CARWHEEL_FRONT_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]; if(GetModelIndex() == MI_VOODOO && bUsingSpecialColModel) m_aWheelPosition[i] = wheelPos; else m_aWheelPosition[i] += (wheelPos - m_aWheelPosition[i])*0.75f; } for(i = 0; i < 4; i++) m_aWheelState[i] = WheelState[i]; if(m_fGasPedal < 0.0f){ if(m_aWheelState[CARWHEEL_REAR_LEFT] == WHEEL_STATE_SPINNING) m_aWheelState[CARWHEEL_REAR_LEFT] = WHEEL_STATE_NORMAL; if(m_aWheelState[CARWHEEL_REAR_RIGHT] == WHEEL_STATE_SPINNING) m_aWheelState[CARWHEEL_REAR_RIGHT] = WHEEL_STATE_NORMAL; } // Process horn if(GetStatus() != STATUS_PLAYER){ if(!IsAlarmOn()) ReduceHornCounter(); }else{ if(UsesSiren()){ 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_VOODOO && !CVehicle::bCheat3 && !carHasNitro){ if(!IsAlarmOn()){ if(Pads[0].GetHorn()) m_nCarHornTimer = 1; else m_nCarHornTimer = 0; } } } // Flying bool playRotorSound = false; bool isPlane = GetModelIndex() == MI_DODO || bAllDodosCheat; #ifdef FIX_BUGS isPlane = isPlane && !IsRealHeli(); #endif if(GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && GetStatus() != STATUS_PHYSICS){ if(IsRealHeli()){ bEngineOn = false; m_aWheelSpeed[1] = Max(m_aWheelSpeed[1]-0.0005f, 0.0f); if(GetModelIndex() != MI_RCRAIDER && GetModelIndex() != MI_RCGOBLIN) if(m_aWheelSpeed[1] < 0.154f && m_aWheelSpeed[1] > 0.0044f) playRotorSound = true; } }else if(isPlane && m_vecMoveSpeed.Magnitude() > 0.0f && CTimer::GetTimeStep() > 0.0f){ if(GetModelIndex() == MI_DODO) FlyingControl(FLIGHT_MODEL_DODO); else FlyingControl(FLIGHT_MODEL_PLANE); }else if(GetModelIndex() == MI_RCBARON){ FlyingControl(FLIGHT_MODEL_RCPLANE); }else if(IsRealHeli() || bAllCarCheat){ #ifdef RESTORE_ALLCARSHELI_CHEAT if (bAllCarCheat) FlyingControl(FLIGHT_MODEL_HELI); else #endif { // Speed up rotor if (m_aWheelSpeed[1] < 0.22f && !bIsInWater) { if (GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) m_aWheelSpeed[1] += 0.003f; else m_aWheelSpeed[1] += 0.001f; } // Fly if (m_aWheelSpeed[1] > 0.15f) { if (GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) FlyingControl(FLIGHT_MODEL_RCHELI); else if (m_nWheelsOnGround < 4 && !(GetModelIndex() == MI_SEASPAR && bTouchingWater) || CPad::GetPad(0)->GetAccelerate() != 0 || #ifndef FREE_CAM CPad::GetPad(0)->GetCarGunUpDown() > 1.0f || #else ((!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) && CPad::GetPad(0)->GetCarGunUpDown() > 1.0f) || #endif Abs(m_vecMoveSpeed.x) > 0.02f || Abs(m_vecMoveSpeed.y) > 0.02f || Abs(m_vecMoveSpeed.z) > 0.02f) FlyingControl(FLIGHT_MODEL_HELI); } } // Blade collision if(m_aWheelSpeed[1] > 0.015f && m_aCarNodes[CAR_BONNET]){ CMatrix mat; mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); if(GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN) DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 0.72f, 0.9f); else if(GetModelIndex() == MI_SPARROW || GetModelIndex() == MI_SEASPAR) DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 5.15f, 0.8f); else if(GetModelIndex() == MI_HUNTER) DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 6.15f, 0.5f); else DoBladeCollision(mat.GetPosition(), GetMatrix(), ROTOR_TOP, 6.15f, 1.0f); } // Heli weapons if(GetModelIndex() == MI_HUNTER && GetStatus() == STATUS_PLAYER){ // Hunter rockets if(CPad::GetPad(0)->CarGunJustDown() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+350){ CWeapon gun(WEAPONTYPE_ROCKETLAUNCHER, 100); CVector source = vecHunterRocketPos; source = GetMatrix()*source + Max(DotProduct(m_vecMoveSpeed, GetForward()), 0.0f)*GetForward()*CTimer::GetTimeStep(); gun.FireProjectile(this, &source, 0.0f); source = vecHunterRocketPos; source.x = -source.x; source = GetMatrix()*source + Max(DotProduct(m_vecMoveSpeed, GetForward()), 0.0f)*GetForward()*CTimer::GetTimeStep(); gun.FireProjectile(this, &source, 0.0f); CStats::RoundsFiredByPlayer++; DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); // Hunter gun }else if(CPad::GetPad(0)->GetHandBrake() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+60){ CWeapon gun(WEAPONTYPE_HELICANNON, 5000); CVector source = vecHunterGunPos; source = GetMatrix()*source + m_vecMoveSpeed*CTimer::GetTimeStep(); gun.FireInstantHit(this, &source); gun.AddGunshell(this, source, CVector2D(0.0f, 0.1f), 0.025f); CStats::RoundsFiredByPlayer++; DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); } }else if(GetModelIndex() == MI_SEASPAR && GetStatus() == STATUS_PLAYER){ // Sea sparrow gun if(CPad::GetPad(0)->GetHandBrake() && CTimer::GetTimeInMilliseconds() > m_nGunFiringTime+40){ CWeapon gun(WEAPONTYPE_M4, 5000); CVector source = vecSeaSparrowGunPos; source = GetMatrix()*source + m_vecMoveSpeed*CTimer::GetTimeStep(); gun.FireInstantHit(this, &source); gun.AddGunshell(this, source, CVector2D(0.0f, 0.1f), 0.025f); CStats::RoundsFiredByPlayer++; DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); } } if(GetModelIndex() != MI_RCRAIDER && GetModelIndex() != MI_RCGOBLIN) if(m_aWheelSpeed[1] < 0.154f && m_aWheelSpeed[1] > 0.0044f) playRotorSound = true; } // Play rotor sound if(playRotorSound && m_aCarNodes[CAR_BONNET]){ CVector camDist = TheCamera.GetPosition() - GetPosition(); float distSq = camDist.MagnitudeSqr(); if(distSq < SQR(20.0f) && Abs(m_fPropellerRotation - m_aWheelRotation[1]) > DEGTORAD(30.0f)){ CMatrix mat; mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); CVector blade = mat.GetRight(); blade = Multiply3x3(blade, GetMatrix()); camDist /= Max(Sqrt(distSq), 0.01f); if(Abs(DotProduct(camDist, blade)) > 0.95f){ DMAudio.PlayOneShot(m_audioEntityId, SOUND_HELI_BLADE, 0.0f); m_fPropellerRotation = m_aWheelRotation[1]; } } } } // 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 && GetStatus() != 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 && GetStatus() != STATUS_WRECKED){ // Car is on fire CParticle::AddParticle(PARTICLE_CARFLAME, damagePos, CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.01125f, 0.09f)), nil, 0.63f); 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) 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() != MI_MRWHOOP) 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; float speedsq = m_vecMoveSpeed.MagnitudeSqr(); for(i = 0; i < 4; i++){ float suspChange = m_aSuspensionSpringRatioPrev[i] - m_aSuspensionSpringRatio[i]; if(suspChange > 0.3f && !drivingInSand && speedsq > 0.04f){ if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST) DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP_2, suspChange); else DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, suspChange); if(suspChange > suspShake) suspShake = suspChange; } uint8 surf = m_aWheelColPoints[i].surfaceB; if(surf == SURFACE_GRAVEL || surf == SURFACE_WATER || surf == SURFACE_HEDGE){ if(surfShake < 0.2f) surfShake = 0.3f; }else if(surf == SURFACE_MUD_DRY){ if(surfShake < 0.1f) surfShake = 0.2f; }else if(surf == SURFACE_GRASS){ if(surfShake < 0.05f) surfShake = 0.1f; } if(this == FindPlayerVehicle()) // BUG: this only observes one of the wheels TheCamera.m_bVehicleSuspenHigh = Abs(suspChange) > 0.05f; m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i]; m_aSuspensionSpringRatio[i] = 1.0f; } // Shake pad if(!drivingInSand && (suspShake > 0.0f || surfShake > 0.0f) && GetStatus() == 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, 150.0f); CPad::GetPad(0)->StartShake(5000.0f*CTimer::GetTimeStep()/freq, freq); } } } bVehicleColProcessed = false; bAudioChangingGear = false; if(!bWarnedPeds && GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI && GetVehicleAppearance() != VEHICLE_APPEARANCE_PLANE) CCarCtrl::ScanForPedDanger(this); // Turn around at the edge of the world // TODO: make the numbers defines float heading; if(GetPosition().x > 1950.0f-400.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 < -1950.0f-400.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 > 1950.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 < -1950.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 || GetStatus() == STATUS_WRECKED)){ if(Abs(m_vecMoveSpeed.x) < 0.005f && Abs(m_vecMoveSpeed.y) < 0.005f && Abs(m_vecMoveSpeed.z) < 0.005f && !(m_fDamageImpulse > 0.0f && m_pDamageEntity == FindPlayerPed()) && (m_aSuspensionSpringRatioPrev[0] < 1.0f || m_aSuspensionSpringRatioPrev[1] < 1.0f || m_aSuspensionSpringRatioPrev[2] < 1.0f || m_aSuspensionSpringRatioPrev[3] < 1.0f)){ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); m_vecTurnSpeed.z = 0.0f; } } if(IsRealHeli() && bHeliDestroyed && !bRenderScorched){ ApplyMoveForce(0.0f, 0.0f, -2.0f*CTimer::GetTimeStep()); m_vecTurnSpeed.z += -0.002f*CTimer::GetTimeStep(); m_vecTurnSpeed.x += -0.0002f*CTimer::GetTimeStep(); RwRGBA col = { 84, 84, 84, 255 }; CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, GetMatrix()*CVector(0.0f, 0.0f, -10.0f), CVector(0.0f, 0.0f, 0.0f), nil, 0.7f, col, 0, 0, 0, 3000); if(CWorld::TestSphereAgainstWorld(GetPosition(), 10.0f, this, true, false, false, false, false, false) || GetPosition().z < 6.0f) if(!bRenderScorched){ // we already know this is true... CExplosion::AddExplosion(this, nil, EXPLOSION_CAR, GetPosition(), 0); bRenderScorched = true; } } } #pragma optimize("", on) void CAutomobile::Teleport(CVector pos) { CWorld::Remove(this); SetPosition(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_RHINO && m_aCarNodes[CAR_WINDSCREEN]){ // Rotate Rhino turret CMatrix m; CVector p; m.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); p = m.GetPosition(); m.SetRotateZ(m_fCarGunLR); m.Translate(p); m.UpdateRW(); } 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 || GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR){ ; // 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; default: break; } } }else{ if(GetStatus() == 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(GetStatus() == STATUS_SIMPLE || GetStatus() == STATUS_PHYSICS || GetStatus() == STATUS_PLAYER || GetStatus() == 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++){ if(m_aSuspensionSpringRatioPrev[i] < 1.0f && m_aWheelColPoints[i].surfaceB != SURFACE_WATER) 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_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); break; case WHEEL_STATE_SKIDDING: if(i == CARWHEEL_REAR_LEFT || i == CARWHEEL_REAR_RIGHT || rearSkidding){ // same as below if(Abs(fwdSpeed) > 5.0f){ 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_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); } break; case WHEEL_STATE_FIXED: if(Abs(fwdSpeed) > 5.0f){ 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_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); break; default: if(Abs(fwdSpeed) > 0.5f) AddWheelDirtAndWater(&m_aWheelColPoints[i], drawParticles); if((m_aWheelSkidmarkBloody[i] || m_aWheelSkidmarkUnk[i]) && m_aWheelTimer[i] > 0.0f) CSkidmarks::RegisterOne((uintptr)this + i, m_aWheelColPoints[i].point, GetForward().x, GetForward().y, m_aWheelSkidmarkType[i], &m_aWheelSkidmarkBloody[i]); } // Sparks for friction of burst wheels if(Damage.GetWheelStatus(i) == WHEEL_STATUS_BURST && m_aSuspensionSpringRatioPrev[i] < 1.0f){ static float speedSq; speedSq = m_vecMoveSpeed.MagnitudeSqr(); if(speedSq > SQR(0.1f) && m_aWheelColPoints[i].surfaceB != SURFACE_GRASS && m_aWheelColPoints[i].surfaceB != SURFACE_MUD_DRY && m_aWheelColPoints[i].surfaceB != SURFACE_SAND && m_aWheelColPoints[i].surfaceB != SURFACE_SAND_BEACH && m_aWheelColPoints[i].surfaceB != SURFACE_WATER){ CVector normalSpeed = m_aWheelColPoints[i].normal * DotProduct(m_aWheelColPoints[i].normal, m_vecMoveSpeed); CVector frictionSpeed = m_vecMoveSpeed - normalSpeed; if(i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_REAR_LEFT) frictionSpeed -= 0.05f*GetRight(); else frictionSpeed += 0.05f*GetRight(); CVector unusedRight = 0.15f*GetRight(); CVector sparkDir = 0.25f*frictionSpeed; CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); if(speedSq > 0.04f) CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); if(speedSq > 0.16f){ CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[i].point, sparkDir); } } } } } } 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_aWheelSkidmarkType[CARWHEEL_REAR_LEFT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_LEFT]); break; default: 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_aWheelSkidmarkType[CARWHEEL_REAR_RIGHT], &m_aWheelSkidmarkBloody[CARWHEEL_REAR_RIGHT]); break; default: 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(-0.4f, 0.4f), CGeneral::GetRandomNumberInRange(-0.4f, 0.4f), 0.0f), CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 1); } } AddDamagedVehicleParticles(); // Exhaust smoke if(bEngineOn && !(pHandling->Flags & HANDLING_NO_EXHAUST) && fwdSpeed < 130.0f){ CVector exhaustPos = mi->m_positions[CAR_POS_EXHAUST]; CVector pos1, pos2, dir1, dir2; if(exhaustPos != CVector(0.0f, 0.0f, 0.0f)){ dir1.z = 0.0f; dir2.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); dir1.x = steerFwd.x * r; dir1.y = steerFwd.y * r; }else{ dir1.x = m_vecMoveSpeed.x; dir1.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; dir2 = dir1; } static float fumesLimit = 2.0f; if(CGeneral::GetRandomNumberInRange(1.0f, 3.0f)*(m_fGasPedal+1.1f) > fumesLimit) for(i = 0; i < 4;){ CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir1); if(pHandling->Flags & HANDLING_DBL_EXHAUST) CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir2); static float extraFumesLimit = 0.5f; if(m_fGasPedal > extraFumesLimit && m_nCurrentGear < 3){ if(CGeneral::GetRandomNumber() & 1) CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir1); else if(pHandling->Flags & HANDLING_DBL_EXHAUST) CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir2); } // Fire on Cuban hermes if(GetModelIndex() == MI_CUBAN && i == 1 && m_fGasPedal > 0.9f){ if(m_nCurrentGear == 1 || m_nCurrentGear == 3 && (CTimer::GetTimeInMilliseconds()%1500) > 750){ if(CGeneral::GetRandomNumber() & 1){ CParticle::AddParticle(PARTICLE_FIREBALL, pos1, dir1, nil, 0.05f, 0, 0, 2, 200); CParticle::AddParticle(PARTICLE_FIREBALL, pos1, dir1, nil, 0.05f, 0, 0, 2, 200); }else{ CParticle::AddParticle(PARTICLE_FIREBALL, pos2, dir2, nil, 0.05f, 0, 0, 2, 200); CParticle::AddParticle(PARTICLE_FIREBALL, pos2, dir2, nil, 0.05f, 0, 0, 2, 200); } } } if(GetStatus() == STATUS_PLAYER && (CTimer::GetFrameCounter()&3) == 0 && CWeather::Rain == 0.0f && i == 0){ CVector camDist = GetPosition() - TheCamera.GetPosition(); if(DotProduct(GetForward(), camDist) > 0.0f || TheCamera.GetLookDirection() == LOOKING_LEFT || TheCamera.GetLookDirection() == LOOKING_RIGHT){ CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); if(pHandling->Flags & HANDLING_DBL_EXHAUST) CParticle::AddParticle(PARTICLE_HEATHAZE, pos2, CVector(0.0f, 0.0f, 0.0f)); CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); if(pHandling->Flags & HANDLING_DBL_EXHAUST) CParticle::AddParticle(PARTICLE_HEATHAZE, pos2, CVector(0.0f, 0.0f, 0.0f)); } } if(GetModelIndex() == MI_CUBAN && i < 1){ i = 1; pos1 = GetMatrix() * CVector(1.134f, -1.276f, -0.56f); pos2 = GetMatrix() * CVector(-1.134f, -1.276f, -0.56f); dir1 += 0.05f*GetRight(); dir2 -= 0.05f*GetRight(); }else i = 99; } } } // 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_FBIRANCH: case MI_VICECHEE: if(m_bSirenOrAlarm){ CVector pos = GetMatrix() * CVector(0.4f, 0.6f, 0.3f); if(CTimer::GetTimeInMilliseconds() & 0x100 && DotProduct(GetForward(), GetPosition() - TheCamera.GetPosition()) < 0.0f) if(GetModelIndex() == MI_VICECHEE) CCoronas::RegisterCorona((uintptr)this + 21, 255, 70, 70, 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::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_ZEBRA: case MI_KAUFMAN: 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 && GetModelIndex() != MI_RCBARON && GetVehicleAppearance() != VEHICLE_APPEARANCE_HELI) { // Process lights // 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 && GetStatus() != STATUS_WRECKED){ if(GetStatus() == 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 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(DotProduct(lightR-TheCamera.GetPosition(), GetForward()) < 0.0f && (TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON || this != FindPlayerVehicle())){ // 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 + BRIGHTLIGHT_FRONT); if(Damage.GetLightStatus(VEHLIGHT_FRONT_RIGHT) == LIGHT_STATUS_OK && !bNoBrightHeadLights) CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + BRIGHTLIGHT_FRONT); // Taillights CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; lightR = GetMatrix() * tailLightPos; lightL = lightR; lightL -= GetRight()*2.0f*tailLightPos.x; // Taillight coronas if(DotProduct(lightR-TheCamera.GetPosition(), GetForward()) > 0.0f){ // Behind car float intensity = 0.4f*behindness + 0.4f; 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.0f){ 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 + BRIGHTLIGHT_REAR); if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); // 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, 5.5f*fwd.y, -5.5f*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 if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED){ // Lights off 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 + BRIGHTLIGHT_FRONT); if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_FRONT); }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 + BRIGHTLIGHT_REAR); if(Damage.GetLightStatus(VEHLIGHT_REAR_RIGHT) == LIGHT_STATUS_OK) CBrightLights::RegisterOne(lightR, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); } }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); } } // end of lights } if (IsRealHeli()) CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_HELI); else if ( GetModelIndex() == MI_RCBARON) CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_RCPLANE); else CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_CAR); DoSunGlare(); // Heli dust if(IsRealHeli() && m_aWheelSpeed[1] > 0.1125f && GetPosition().z < 30.0f){ bool foundGround = false; float waterZ = -1000.0f; float groundZ = CWorld::FindGroundZFor3DCoord(GetPosition().x, GetPosition().y, GetPosition().z, &foundGround); if(!CWaterLevel::GetWaterLevel(GetPosition(), &waterZ, false)) waterZ = 0.0f; groundZ = Max(groundZ, waterZ); float rnd = (m_aWheelSpeed[1]-0.1125f)*((int)Max(16.0f-4.0f*CTimer::GetTimeStep(),2.0f))*400.0f/43.0f; float radius = 10.0f; if(GetModelIndex() == MI_RCGOBLIN || GetModelIndex() == MI_RCRAIDER) radius = 3.0f; if(GetPosition().z - groundZ < radius) HeliDustGenerate(this, radius-(GetPosition().z - groundZ), groundZ, Ceil(rnd)); } CMatrix mat; CVector pos; bool onlyFrontWheels = false; if(IsRealHeli()){ // top rotor m_aWheelRotation[1] += m_aWheelSpeed[1]*CTimer::GetTimeStep(); if(m_aWheelRotation[1] > TWOPI) m_aWheelRotation[1] -= TWOPI; // rear rotor m_aWheelRotation[3] += m_aWheelSpeed[1]*CTimer::GetTimeStep(); if(m_aWheelRotation[3] > TWOPI) m_aWheelRotation[3] -= TWOPI; onlyFrontWheels = true; } 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++){ if (m_aWheelTimer[i] > 0.0f && (!onlyFrontWheels || i == CARWHEEL_FRONT_LEFT || i == CARWHEEL_FRONT_RIGHT)) { 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]; } } RwRGBA hoverParticleCol = { 255, 255, 255, 32 }; // Rear right wheel mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); pos = mat.GetPosition(); 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]); if(GetStatus() == STATUS_PLAYER){ if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_RIGHT] < 1.0f && m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB == SURFACE_WATER){ // hovering on water mat.RotateY(-HALFPI); if((CTimer::GetFrameCounter()+CARWHEEL_REAR_RIGHT) & 1){ CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point, 0.5f*m_vecMoveSpeed+0.1f*GetRight(), nil, 0.4f, hoverParticleCol); }else{ CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_REAR_RIGHT].point, 0.3f*m_vecMoveSpeed+0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, CGeneral::GetRandomNumberInRange(0.0f, 90.0f), CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); } #ifdef BETTER_ALLCARSAREDODO_CHEAT } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { mat.RotateY(-HALFPI); #endif }else{ // tilt wheel depending oh how much it presses on ground float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; if(GetModelIndex() == MI_VOODOO) groundOffset *= 0.6f; mat.RotateY(Asin(clamp(-groundOffset, -1.0f, 1.0f))); } } if(pHandling->Flags & HANDLING_FAT_REARW) mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); else mat.Scale(mi->m_wheelScale); mat.Translate(pos); mat.UpdateRW(); // Rear left wheel mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); pos = mat.GetPosition(); 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); if(GetStatus() == STATUS_PLAYER){ if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_LEFT] < 1.0f && m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB == SURFACE_WATER){ // hovering on water mat.RotateY(HALFPI); if((CTimer::GetFrameCounter()+CARWHEEL_REAR_LEFT) & 1){ CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_REAR_LEFT].point, 0.5f*m_vecMoveSpeed-0.1f*GetRight(), nil, 0.4f, hoverParticleCol); }else{ CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_REAR_LEFT].point, 0.3f*m_vecMoveSpeed-0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, CGeneral::GetRandomNumberInRange(0.0f, 90.0f), CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); } #ifdef BETTER_ALLCARSAREDODO_CHEAT } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { mat.RotateY(HALFPI); #endif }else{ // tilt wheel depending oh how much it presses on ground float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; if(GetModelIndex() == MI_VOODOO) groundOffset *= 0.6f; mat.RotateY(Asin(clamp(groundOffset, -1.0f, 1.0f))); } } if(pHandling->Flags & HANDLING_FAT_REARW) mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); else mat.Scale(mi->m_wheelScale); mat.Translate(pos); mat.UpdateRW(); // Mid right wheel if(m_aCarNodes[CAR_WHEEL_RM]){ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RM])); pos = mat.GetPosition(); 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]); if(GetStatus() == STATUS_PLAYER){ if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_RIGHT] < 1.0f && m_aWheelColPoints[CARWHEEL_REAR_RIGHT].surfaceB == SURFACE_WATER){ // hovering on water mat.RotateY(-HALFPI); #ifdef BETTER_ALLCARSAREDODO_CHEAT } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { mat.RotateY(-HALFPI); #endif }else{ // tilt wheel depending oh how much it presses on ground float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; if(GetModelIndex() == MI_VOODOO) groundOffset *= 0.6f; mat.RotateY(Asin(clamp(-groundOffset, -1.0f, 1.0f))); } } if(pHandling->Flags & HANDLING_FAT_REARW) mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); else mat.Scale(mi->m_wheelScale); mat.Translate(pos); mat.UpdateRW(); } // Mid left wheel if(m_aCarNodes[CAR_WHEEL_LM]){ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LM])); pos = mat.GetPosition(); 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); if(GetStatus() == STATUS_PLAYER){ if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_REAR_LEFT] < 1.0f && m_aWheelColPoints[CARWHEEL_REAR_LEFT].surfaceB == SURFACE_WATER){ // hovering on water mat.RotateY(HALFPI); #ifdef BETTER_ALLCARSAREDODO_CHEAT } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { mat.RotateY(HALFPI); #endif }else{ // tilt wheel depending oh how much it presses on ground float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; if(GetModelIndex() == MI_VOODOO) groundOffset *= 0.6f; mat.RotateY(Asin(clamp(groundOffset, -1.0f, 1.0f))); } } if(pHandling->Flags & HANDLING_FAT_REARW) mat.Scale(1.15f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); else mat.Scale(mi->m_wheelScale); mat.Translate(pos); mat.UpdateRW(); } if(GetModelIndex() == MI_DODO){ // Front wheel mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); pos = mat.GetPosition(); 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(); // 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 = mat.GetPosition(); 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(); // Front left wheel mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); pos = mat.GetPosition(); 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(); }else if(IsRealHeli()){ // Top rotor if(m_aCarNodes[CAR_BONNET]){ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BONNET])); pos = mat.GetPosition(); mat.SetRotateZ(m_aWheelRotation[1]); mat.Translate(pos); mat.UpdateRW(); } // Blurred top rotor if(m_aCarNodes[CAR_WINDSCREEN]){ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WINDSCREEN])); pos = mat.GetPosition(); mat.SetRotateZ(-m_aWheelRotation[1]); mat.Translate(pos); mat.UpdateRW(); } // Rear rotor if(m_aCarNodes[CAR_BOOT]){ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BOOT])); pos = mat.GetPosition(); mat.SetRotateX(m_aWheelRotation[3]); mat.Translate(pos); mat.UpdateRW(); } // Blurred rear rotor if(m_aCarNodes[CAR_BUMP_REAR]){ mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_BUMP_REAR])); pos = mat.GetPosition(); mat.SetRotateX(-m_aWheelRotation[3]); mat.Translate(pos); mat.UpdateRW(); } }else{ // Front right wheel mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); pos = mat.GetPosition(); 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); if(GetStatus() == STATUS_PLAYER){ if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_FRONT_RIGHT] < 1.0f && m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].surfaceB == SURFACE_WATER){ // hovering on water mat.RotateY(-HALFPI); if((CTimer::GetFrameCounter()+CARWHEEL_FRONT_RIGHT) & 1){ CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point, 0.5f*m_vecMoveSpeed+0.1f*GetRight(), nil, 0.4f, hoverParticleCol); }else{ CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_FRONT_RIGHT].point, 0.3f*m_vecMoveSpeed+0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, CGeneral::GetRandomNumberInRange(0.0f, 90.0f), CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); } #ifdef BETTER_ALLCARSAREDODO_CHEAT } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { mat.RotateY(-HALFPI); #endif }else{ // tilt wheel depending oh how much it presses on ground float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; if(GetModelIndex() == MI_VOODOO) groundOffset *= 0.6f; mat.RotateY(Asin(clamp(-groundOffset, -1.0f, 1.0f))); } } if(pHandling->Flags & HANDLING_NARROW_FRONTW) mat.Scale(0.7f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); else mat.Scale(mi->m_wheelScale); mat.Translate(pos); mat.UpdateRW(); // Front left wheel mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LF])); pos = mat.GetPosition(); 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); if(GetStatus() == STATUS_PLAYER){ if(bHoverCheat && m_aSuspensionSpringRatioPrev[CARWHEEL_FRONT_LEFT] < 1.0f && m_aWheelColPoints[CARWHEEL_FRONT_LEFT].surfaceB == SURFACE_WATER){ // hovering on water mat.RotateY(HALFPI); if((CTimer::GetFrameCounter()+CARWHEEL_FRONT_LEFT) & 1){ CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point, 0.5f*m_vecMoveSpeed-0.1f*GetRight(), nil, 0.4f, hoverParticleCol); }else{ CParticle::AddParticle(PARTICLE_CAR_SPLASH, m_aWheelColPoints[CARWHEEL_FRONT_LEFT].point, 0.3f*m_vecMoveSpeed-0.15f*GetRight()+CVector(0.0f, 0.0f, 0.1f), nil, 0.15f, hoverParticleCol, CGeneral::GetRandomNumberInRange(0.0f, 90.0f), CGeneral::GetRandomNumberInRange(0.0f, 10.0f), 1); } #ifdef BETTER_ALLCARSAREDODO_CHEAT } else if (bAllDodosCheat && m_nDriveWheelsOnGround == 0 && m_nDriveWheelsOnGroundPrev == 0) { mat.RotateY(HALFPI); #endif }else{ // tilt wheel depending oh how much it presses on ground float groundOffset = pos.z + m_fHeightAboveRoad - 0.5f*mi->m_wheelScale; if(GetModelIndex() == MI_VOODOO) groundOffset *= 0.6f; mat.RotateY(Asin(clamp(groundOffset, -1.0f, 1.0f))); } } if(pHandling->Flags & HANDLING_NARROW_FRONTW) mat.Scale(0.7f*mi->m_wheelScale, mi->m_wheelScale, mi->m_wheelScale); else mat.Scale(mi->m_wheelScale); mat.Translate(pos); mat.UpdateRW(); 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); } if((GetModelIndex() == MI_PHEONIX || GetModelIndex() == MI_BFINJECT) && GetStatus() == STATUS_PLAYER && m_aCarNodes[CAR_WING_LR]){ float rotation = 0.0f; if(GetModelIndex() == MI_BFINJECT) if(m_fPropellerRotation > TWOPI) m_fPropellerRotation -= TWOPI; if(Abs(m_fGasPedal) > 0.0f){ if(GetModelIndex() == MI_BFINJECT){ m_fPropellerRotation += 0.2f*CTimer::GetTimeStep(); rotation = m_fPropellerRotation; }else{ if(m_fPropellerRotation < 1.3f){ m_fPropellerRotation = Min(m_fPropellerRotation+0.1f*CTimer::GetTimeStep(), 1.3f); rotation = m_fPropellerRotation; }else{ float wave = Sin((CTimer::GetTimeInMilliseconds()%10000)/70.0f); rotation = m_fPropellerRotation + 0.13*wave; } } }else{ if(GetModelIndex() == MI_BFINJECT){ m_fPropellerRotation += 0.1f*CTimer::GetTimeStep(); rotation = m_fPropellerRotation; }else{ if(m_fPropellerRotation > 0.0f){ m_fPropellerRotation = Max(m_fPropellerRotation-0.05f*CTimer::GetTimeStep(), 0.0f); rotation = m_fPropellerRotation; } } } mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WING_LR])); pos = mat.GetPosition(); if(GetModelIndex() == MI_BFINJECT) mat.SetRotateY(rotation); else mat.SetRotateX(rotation); mat.Translate(pos); mat.UpdateRW(); } } void CAutomobile::Render(void) { CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); mi->SetVehicleColour(m_currentColour1, m_currentColour2); if(IsRealHeli()){ RpAtomic *atomic = nil; int rotorAlpha = (1.5f - Min(1.7f*Max(m_aWheelSpeed[1],0.0f)/0.22f, 1.5f))*255.0f; rotorAlpha = Min(rotorAlpha, 255); int blurAlpha = Max(1.5f*m_aWheelSpeed[1]/0.22f - 0.4f, 0.0f)*150.0f; blurAlpha = Min(blurAlpha, 150); // Top rotor if(m_aCarNodes[CAR_BONNET]){ RwFrameForAllObjects(m_aCarNodes[CAR_BONNET], GetCurrentAtomicObjectCB, &atomic); if(atomic) SetComponentAtomicAlpha(atomic, rotorAlpha); } atomic = nil; // Rear rotor if(m_aCarNodes[CAR_BOOT]){ RwFrameForAllObjects(m_aCarNodes[CAR_BOOT], GetCurrentAtomicObjectCB, &atomic); if(atomic) SetComponentAtomicAlpha(atomic, rotorAlpha); } atomic = nil; // Blurred top rotor if(m_aCarNodes[CAR_WINDSCREEN]){ RwFrameForAllObjects(m_aCarNodes[CAR_WINDSCREEN], GetCurrentAtomicObjectCB, &atomic); if(atomic) SetComponentAtomicAlpha(atomic, blurAlpha); } atomic = nil; // Blurred rear rotor if(m_aCarNodes[CAR_BUMP_REAR]){ RwFrameForAllObjects(m_aCarNodes[CAR_BUMP_REAR], GetCurrentAtomicObjectCB, &atomic); if(atomic) SetComponentAtomicAlpha(atomic, blurAlpha); } } if(CVehicle::bWheelsOnlyCheat){ RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB])); RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB])); RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF])); RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF])); if(m_aCarNodes[CAR_WHEEL_RM]) RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RM])); if(m_aCarNodes[CAR_WHEEL_LM]) RpAtomicRender((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LM])); }else CEntity::Render(); } int32 CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) { int i; CColModel *colModel; if(GetStatus() != 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]; if(m_bIsVehicleBeingShifted || bSkipLineCol || ent->IsPed() || GetModelIndex() == MI_DODO && ent->IsVehicle()) colModel->numLines = 0; 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(colModel->numLines){ 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(); } m_nSurfaceTouched = m_aWheelColPoints[i].surfaceB; if(ent->IsBuilding()) m_pCurGroundEntity = ent; } }else colModel->numLines = 4; 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() || pDriver && pDriver->m_pVehicleAnim && (pDriver->m_pVehicleAnim->animId == ANIM_STD_ROLLOUT_LHS || pDriver->m_pVehicleAnim->animId == ANIM_STD_ROLLOUT_RHS)) bIsHandbrakeOn = !!CPad::GetPad(pad)->GetHandBrake(); else bIsHandbrakeOn = true; // Steer left/right if(CCamera::m_bUseMouse3rdPerson && !CVehicle::m_bDisableMouseSteering){ if(CPad::GetPad(pad)->GetMouseX() != 0.0f){ m_fSteerInput += fMouseSteerSens*CPad::GetPad(pad)->GetMouseX(); nLastControlInput = 2; if(Abs(m_fSteerInput) < fMouseCentreRange) m_fSteerInput *= Pow(fMouseCentreMult, CTimer::GetTimeStep()); }else if(CPad::GetPad(pad)->GetSteeringLeftRight() || nLastControlInput != 2){ // mouse hasn't move, steer with pad like below m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* 0.2f*CTimer::GetTimeStep(); nLastControlInput = 0; } }else{ m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* 0.2f*CTimer::GetTimeStep(); nLastControlInput = 0; } m_fSteerInput = clamp(m_fSteerInput, -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 if(CPad::GetPad(pad)->GetAccelerate() > 150.0f && CPad::GetPad(pad)->GetBrake() > 150.0f){ m_fGasPedal = CPad::GetPad(pad)->GetAccelerate()/255.0f; m_fBrakePedal = CPad::GetPad(pad)->GetBrake()/255.0f; m_doingBurnout = 1; }else{ 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_fSteerInput < 0.0f) fValue = -sq(m_fSteerInput); else fValue = sq(m_fSteerInput); 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 #ifdef FIX_BUGS if(CPad::GetPad(pad)->ArePlayerControlsDisabled()){ #else if(CPad::GetPad(0)->ArePlayerControlsDisabled()){ #endif 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)->GetCarGunFired()) return; #ifdef FREE_CAM if (!CCamera::bFreeCam) #endif { 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(GetStatus() == 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; #ifdef FREE_CAM if(!CCamera::bFreeCam) #endif 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.5f)*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 += 5.0f*m_vecMoveSpeed.x; flashPos.y += 5.0f*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); } } #define HYDRAULIC_UPPER_EXT (-0.16f) #define HYDRAULIC_LOWER_EXT (0.16f) 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(GetStatus() != 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 += HYDRAULIC_UPPER_EXT; normalSpringLength = normalUpperLimit - (normalLowerLimit+HYDRAULIC_LOWER_EXT); 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; return; } 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; 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 += HYDRAULIC_UPPER_EXT; normalLowerLimit += HYDRAULIC_LOWER_EXT; 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; } 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 += HYDRAULIC_UPPER_EXT; normalLowerLimit += HYDRAULIC_LOWER_EXT; 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]*(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; } } }else{ if(m_hydraulicState < 104) m_hydraulicState++; if(m_fVelocityChangeForAudio < 0.1f){ normalUpperLimit += HYDRAULIC_UPPER_EXT; normalLowerLimit += HYDRAULIC_LOWER_EXT; 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)){ 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; bool heliHitWaterHard = false; if(IsRealHeli() && m_aWheelSpeed[1] > 0.15f){ if(GetModelIndex() == MI_SEASPAR){ if(impulseRatio > 3.0f){ m_aWheelSpeed[1] = 0.0f; heliHitWaterHard = true; } }else{ float strength = 1.0f/Max(8.0f*impulseRatio, 1.0f); ApplyMoveForce(-2.0f*impulse/strength); ApplyTurnForce(-impulse/strength, point); if(impulseRatio > 0.9f){ m_aWheelSpeed[1] = 0.0f; heliHitWaterHard = true; }else return; } } bTouchingWater = true; ApplyMoveForce(impulse); ApplyTurnForce(impulse, point); CVector initialSpeed = m_vecMoveSpeed; if(m_modelIndex == MI_SEASPAR && impulseRatio < 3.0f && (GetUp().z > -0.5f || impulseRatio < 0.6f) || CVehicle::bHoverCheat && GetStatus() == STATUS_PLAYER && GetUp().z > 0.1f){ bIsInWater = false; bIsDrowning = false; }else if(heliHitWaterHard || impulseRatio > 1.0f || impulseRatio > 0.6f && (m_aSuspensionSpringRatio[0] == 1.0f || m_aSuspensionSpringRatio[1] == 1.0f || m_aSuspensionSpringRatio[2] == 1.0f || m_aSuspensionSpringRatio[3] == 1.0f)){ bIsInWater = true; bIsDrowning = 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_DROWNING, 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_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); } }else{ bIsInWater = false; bIsDrowning = false; } static uint32 nGenerateRaindrops = 0; static uint32 nGenerateWaterCircles = 0; if(initialSpeed.z < -0.1f && impulse.z > 0.3f || heliHitWaterHard){ RwRGBA color; color.red = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj())*0.45f*255; color.green = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj())*0.45f*255; color.blue = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj())*0.45f*255; color.alpha = CGeneral::GetRandomNumberInRange(0, 32) + 128; CVector target = CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.15f, 0.45f)); CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition(), target, 0.0f, 75, color, true); nGenerateRaindrops = CTimer::GetTimeInMilliseconds() + 300; nGenerateWaterCircles = CTimer::GetTimeInMilliseconds() + 60; if(heliHitWaterHard){ CVector right = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f)); CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition() + right, target, 0.0f, 75, color, true); CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, GetPosition() - right, target, 0.0f, 75, color, true); } 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.5f; CParticleObject::AddObject(POBJECT_SPLASHES_AROUND, pos, CVector(0.0f, 0.0f, 0.0f), 6.5f, 2500, black, true); } } }else{ bIsInWater = false; bIsDrowning = false; bTouchingWater = 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_WATER){ 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 = nil; CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); if (playerInfo && !playerInfo->m_bDriveByAllowed) return; CWeapon *weapon = pDriver->GetWeapon(); if(CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->m_nWeaponSlot != WEAPONSLOT_SUBMACHINEGUN) return; weapon->Update(pDriver->m_audioEntityId, nil); bool lookingLeft = false; bool lookingRight = false; if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || TheCamera.m_bObbeCinematicCarCamOn){ 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; } AnimationId rightAnim = ANIM_STD_CAR_DRIVEBY_RIGHT; AnimationId leftAnim = ANIM_STD_CAR_DRIVEBY_LEFT; if (pDriver->m_pMyVehicle->bLowVehicle) { rightAnim = ANIM_STD_CAR_DRIVEBY_RIGHT_LO; leftAnim = ANIM_STD_CAR_DRIVEBY_LEFT_LO; } if(lookingLeft || lookingRight){ if(lookingLeft){ anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); if(anim == nil || anim->blendDelta < 0.0f) anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, leftAnim); }else if(pDriver->m_pMyVehicle->pPassengers[0] == nil || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON){ anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); if(anim == nil || anim->blendDelta < 0.0f) anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, rightAnim); } if (!anim || !anim->IsRunning()) { if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { weapon->FireFromCar(this, lookingLeft, true); weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; } } }else{ weapon->Reload(); anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), leftAnim); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), rightAnim); 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, ANIM_STD_NUM, m_weaponDoorTimerLeft); } if(!lookingRight && m_weaponDoorTimerRight > 0.0f){ m_weaponDoorTimerRight = Max(m_weaponDoorTimerRight - CTimer::GetTimeStep()*0.1f, 0.0f); ProcessOpenDoor(CAR_DOOR_RF, ANIM_STD_NUM, m_weaponDoorTimerRight); } } void CAutomobile::DoHoverSuspensionRatios(void) { int i; if(GetUp().z < 0.1f) return; CColModel *colmodel = GetColModel(); for(i = 0; i < 4; i++){ float z, waterZ; CVector upper = GetMatrix() * colmodel->lines[i].p0; CVector lower = GetMatrix() * colmodel->lines[i].p1; if(m_aSuspensionSpringRatio[i] < 1.0f) z = m_aWheelColPoints[i].point.z; else z = -100.0f; // see if touching water if(CWaterLevel::GetWaterLevel(lower, &waterZ, false) && waterZ > z && lower.z-1.0f < waterZ){ // compress spring if(lower.z < waterZ){ if(upper.z < waterZ) m_aSuspensionSpringRatio[i] = 0.0f; else m_aSuspensionSpringRatio[i] = (upper.z - waterZ)/(upper.z - lower.z); }else m_aSuspensionSpringRatio[i] = 0.99999f; m_aWheelColPoints[i].point.x = (lower.x - upper.x)*m_aSuspensionSpringRatio[i] + upper.x; m_aWheelColPoints[i].point.y = (lower.y - upper.y)*m_aSuspensionSpringRatio[i] + upper.y; m_aWheelColPoints[i].point.z = waterZ; m_aWheelColPoints[i].normal = CVector(0.01f, 0.0f, 1.0f); m_aWheelColPoints[i].surfaceB = SURFACE_WATER; } } } 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::GetSectorIndexY(GetPosition().y - 2.0f); if(ymin < 0) ymin = 0; ymax = CWorld::GetSectorIndexY(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->GetModelIndex() != MI_RCBANDIT && 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->GetStatus() == 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 = m_fMapObjectHeightAhead; } 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 = m_fMapObjectHeightBehind; } float len = lenFwd + lenBack; float angle = Atan((frontZ - rearZ)/len); float c = Cos(angle); float s = Sin(angle); GetMatrix().GetRight() = CVector((front.y - rear.y) / len, -(front.x - rear.x) / len, 0.0f); GetMatrix().GetForward() = CVector(-c * GetRight().y, c * GetRight().x, s); GetMatrix().GetUp() = CrossProduct(GetRight(), GetForward()); GetMatrix().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.333f; if(impulse == 0.0f){ impulse = m_fDamageImpulse; damagedPiece = m_nDamagePieceType; damageMultiplier = 1.0f; } if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) impulse *= 0.5f; CVector pos(0.0f, 0.0f, 0.0f); if(!bCanBeDamaged) return; if(m_pDamageEntity && m_pDamageEntity->IsPed() && ((CPed*)m_pDamageEntity)->bIsStanding){ float speed = ((CPed*)m_pDamageEntity)->m_vecAnimMoveDelta.y * DotProduct(GetForward(), m_vecDamageNormal); if(speed < 0.0f) impulse = Max(impulse + ((CPed*)m_pDamageEntity)->m_fMass * speed, 0.0f); } // damage flipped over car if(GetUp().z < 0.0f && this != FindPlayerVehicle()){ if(bNotDamagedUpsideDown || GetStatus() == STATUS_PLAYER_REMOTE || bIsInWater) return; if(GetStatus() != STATUS_WRECKED) m_fHealth = Max(m_fHealth - 4.0f*CTimer::GetTimeStep(), 0.0f); } float minImpulse = GetModelIndex() == MI_RCRAIDER || GetModelIndex() == MI_RCGOBLIN ? 1.0f : 25.0f; if(impulse > minImpulse && GetStatus() != STATUS_WRECKED){ if(bIsLawEnforcer && FindPlayerVehicle() && FindPlayerVehicle() == m_pDamageEntity && GetStatus() != STATUS_ABANDONED && FindPlayerVehicle()->m_vecMoveSpeed.Magnitude() >= m_vecMoveSpeed.Magnitude() && FindPlayerVehicle()->m_vecMoveSpeed.Magnitude() > 0.1f) FindPlayerPed()->SetWantedLevelNoDrop(1); if(GetStatus() == 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(GetStatus() != STATUS_PLAYER && bOnlyDamagedByPlayer){ if(m_pDamageEntity != FindPlayerPed() && m_pDamageEntity != FindPlayerVehicle()) return; } if(m_pDamageEntity && m_pDamageEntity->IsVehicle()){ m_nLastWeaponDamage = WEAPONTYPE_RAMMEDBYCAR; m_pLastDamageEntity = m_pDamageEntity; } 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*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_BUMPER_FRONT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetBumperDamage(CAR_BUMP_FRONT, VEHBUMPER_FRONT); if(m_aCarNodes[CAR_BONNET] && Damage.GetPanelStatus(VEHBUMPER_FRONT) == PANEL_STATUS_MISSING){ case CAR_PIECE_BONNET: GetComponentWorldPosition(CAR_BONNET, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(GetModelIndex() != MI_DODO) if(Damage.ApplyDamage(COMPONENT_DOOR_BONNET, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_BONNET, DOOR_BONNET); } break; case CAR_PIECE_BUMP_REAR: GetComponentWorldPosition(CAR_BUMP_REAR, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_BUMPER_REAR, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetBumperDamage(CAR_BUMP_REAR, VEHBUMPER_REAR); if(m_aCarNodes[CAR_BOOT] && Damage.GetPanelStatus(VEHBUMPER_REAR) == PANEL_STATUS_MISSING){ case CAR_PIECE_BOOT: GetComponentWorldPosition(CAR_BOOT, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_DOOR_BOOT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_BOOT, DOOR_BOOT); } break; case CAR_PIECE_DOOR_LF: GetComponentWorldPosition(CAR_DOOR_LF, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_DOOR_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_DOOR_LF, DOOR_FRONT_LEFT); break; case CAR_PIECE_DOOR_RF: GetComponentWorldPosition(CAR_DOOR_RF, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_DOOR_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_DOOR_RF, DOOR_FRONT_RIGHT); break; case CAR_PIECE_DOOR_LR: GetComponentWorldPosition(CAR_DOOR_LR, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_DOOR_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_DOOR_LR, DOOR_REAR_LEFT); break; case CAR_PIECE_DOOR_RR: GetComponentWorldPosition(CAR_DOOR_RR, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_DOOR_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetDoorDamage(CAR_DOOR_RR, DOOR_REAR_RIGHT); break; case CAR_PIECE_WING_LF: GetComponentWorldPosition(CAR_WING_LF, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetPanelDamage(CAR_WING_LF, VEHPANEL_FRONT_LEFT); break; case CAR_PIECE_WING_RF: GetComponentWorldPosition(CAR_WING_RF, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_PANEL_FRONT_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetPanelDamage(CAR_WING_RF, VEHPANEL_FRONT_RIGHT); break; case CAR_PIECE_WING_LR: GetComponentWorldPosition(CAR_WING_LR, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_LEFT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetPanelDamage(CAR_WING_LR, VEHPANEL_REAR_LEFT); break; case CAR_PIECE_WING_RR: GetComponentWorldPosition(CAR_WING_RR, pos); dmgDrawCarCollidingParticles(pos, impulse*damageMultiplier); if(Damage.ApplyDamage(COMPONENT_PANEL_REAR_RIGHT, impulse*impulseMult, pHandling->fCollisionDamageMultiplier)) SetPanelDamage(CAR_WING_RR, VEHPANEL_REAR_RIGHT); 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); } } break; } } float damage = (impulse-minImpulse)*pHandling->fCollisionDamageMultiplier*0.6f*damageMultiplier; if(GetModelIndex() == MI_SECURICA && m_pDamageEntity && m_pDamageEntity->GetStatus() == STATUS_PLAYER) damage *= 7.0f; if(GetModelIndex() == MI_RCGOBLIN || GetModelIndex() == MI_RCRAIDER) damage *= 30.0f; if(damage > 0.0f){ if(damage > 5.0f && pDriver && m_pDamageEntity && m_pDamageEntity->IsVehicle() && (this != FindPlayerVehicle() || ((CVehicle*)m_pDamageEntity)->VehicleCreatedBy == MISSION_VEHICLE) && ((CVehicle*)m_pDamageEntity)->pDriver){ if(GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) pDriver->Say(SOUND_PED_CRASH_CAR); else pDriver->Say(SOUND_PED_CRASH_VEHICLE); } int oldHealth = m_fHealth; if(this == FindPlayerVehicle()) m_fHealth -= bTakeLessDamage ? damage/6.0f : damage/2.0f; else if(bTakeLessDamage) m_fHealth -= damage/12.0f; else if(m_pDamageEntity && m_pDamageEntity == FindPlayerVehicle()) m_fHealth -= damage/1.5f; else m_fHealth -= 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) { int i, n; if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) return; if(this != FindPlayerVehicle() && (CTimer::GetFrameCounter() + m_randomSeed) & 1) return; if(m_fHealth >= 650.0f) return; CVector direction = 0.85f*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); else if(GetModelIndex() == MI_CADDY) damagePos = CVector(0.6f, -1.0f, -0.25f); else if(IsRealHeli()){ damagePos.x = 0.4f*GetColModel()->boundingBox.max.x; damagePos.y = 0.2f*GetColModel()->boundingBox.min.y; damagePos.z = 0.3f*GetColModel()->boundingBox.max.z; }else damagePos.z += 0.4f*(GetColModel()->boundingBox.max.z-damagePos.z) * DotProduct(GetForward(), m_vecMoveSpeed); damagePos = GetMatrix()*damagePos; damagePos.z += 0.15f; bool electric = pHandling->Transmission.nEngineType == 'E'; if(electric && m_fHealth < 320.0f && m_fHealth > 1.0f){ direction = 0.85f*m_vecMoveSpeed; direction += GetRight() * CGeneral::GetRandomNumberInRange(0.0f, 0.04f) * (1.0f - 2.0f*m_vecMoveSpeed.Magnitude()); direction.z += 0.001f; n = (CGeneral::GetRandomNumber() & 7) + 2; for(i = 0; i < n; i++) CParticle::AddParticle(PARTICLE_SPARK_SMALL, damagePos, direction); if(((CTimer::GetFrameCounter() + m_randomSeed) & 7) == 0) CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, 0.8f*m_vecMoveSpeed, nil, 0.1f, 0, 0, 0, 1000); }else if(electric && m_fHealth < 460.0f){ direction = 0.85f*m_vecMoveSpeed; direction += GetRight() * CGeneral::GetRandomNumberInRange(0.0f, 0.04f) * (1.0f - 2.0f*m_vecMoveSpeed.Magnitude()); direction.z += 0.001f; n = (CGeneral::GetRandomNumber() & 3) + 2; for(i = 0; i < n; i++) CParticle::AddParticle(PARTICLE_SPARK_SMALL, damagePos, direction); if(((CTimer::GetFrameCounter() + m_randomSeed) & 0xF) == 0) CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.8f*m_vecMoveSpeed, nil, 0.1f, 0, 0, 0, 1000); }else if(m_fHealth < 250.0f){ // nothing }else if(m_fHealth < 320.0f){ CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, 0.8f*direction); }else if(m_fHealth < 390.0f){ CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, 0.75f*direction); CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.85f*direction); }else if(m_fHealth < 460.0f){ int rnd = CTimer::GetFrameCounter() + m_randomSeed; if(rnd < 10 || rnd < 70 && rnd > 25 || rnd < 160 && rnd > 100 || rnd < 200 && rnd > 175 || rnd > 235) return; direction.z += 0.05f*Max(1.0f - 1.6f*m_vecMoveSpeed.Magnitude(), 0.0f); if(electric){ // BUG. we had that case already direction = 0.85f*m_vecMoveSpeed; direction += GetRight() * CGeneral::GetRandomNumberInRange(0.0f, 0.04f) * (1.0f - 2.0f*m_vecMoveSpeed.Magnitude()); direction.z += 0.001f; n = (CGeneral::GetRandomNumber() & 2) + 2; for(i = 0; i < n; i++) CParticle::AddParticle(PARTICLE_SPARK_SMALL, damagePos, direction); if(((CTimer::GetFrameCounter() + m_randomSeed) & 0xF) == 0) CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, 0.8f*m_vecMoveSpeed, nil, 0.1f, 0, 0, 0, 1000); }else{ if(TheCamera.GetLookDirection() != LOOKING_FORWARD) CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, 0.75f*direction); else if(((CTimer::GetFrameCounter() + m_randomSeed) & 1) == 0) CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, 0.85f*m_vecMoveSpeed); } }else if(((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 0 || ((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 2){ CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, 0.9f*direction); } } int32 CAutomobile::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) { int i; CVector dir; static RwRGBA grassCol = { 8, 24, 8, 255 }; static RwRGBA gravelCol = { 64, 64, 64, 255 }; static RwRGBA mudCol = { 64, 32, 16, 255 }; static RwRGBA sandCol = { 170, 165, 140, 255 }; static RwRGBA waterCol = { 48, 48, 64, 0 }; if(!belowEffectSpeed && colpoint->surfaceB != SURFACE_SAND && colpoint->surfaceB != SURFACE_SAND_BEACH) 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_GRAVEL: 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.05f, 0.09f), gravelCol); } return 1; case SURFACE_MUD_DRY: 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), mudCol); } return 0; case SURFACE_SAND: case SURFACE_SAND_BEACH: if(CTimer::GetFrameCounter() & 2 || CGeneral::GetRandomNumberInRange(CWeather::WetRoads, 1.01f) > 0.5f) return 0; dir.x = 0.5f*m_vecMoveSpeed.x; dir.y = 0.5f*m_vecMoveSpeed.y; for(i = 0; i < 1; i++){ dir.z = CGeneral::GetRandomNumberInRange(0.02f, 0.055f); CParticle::AddParticle(PARTICLE_SAND, colpoint->point, dir, nil, 2.0f*m_vecMoveSpeed.Magnitude(), sandCol); } return 0; default: if(CWeather::WetRoads > 0.01f){ if(CTimer::GetFrameCounter() & 1) CParticle::AddParticle( PARTICLE_WATERSPRAY, colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.005f, 0.04f)), 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_STD_QUICKJACK: case ANIM_STD_CAR_OPEN_DOOR_LHS: case ANIM_STD_CAR_OPEN_DOOR_RHS: ProcessDoorOpenAnimation(this, component, door, time, 0.41f, 0.89f); break; case ANIM_STD_CAR_CLOSE_DOOR_LHS: case ANIM_STD_CAR_CLOSE_DOOR_LO_LHS: case ANIM_STD_CAR_CLOSE_DOOR_RHS: case ANIM_STD_CAR_CLOSE_DOOR_LO_RHS: ProcessDoorCloseAnimation(this, component, door, time, 0.2f, 0.45f); break; case ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LHS: case ANIM_STD_CAR_CLOSE_DOOR_ROLLING_LO_LHS: ProcessDoorOpenCloseAnimation(this, component, door, time, 0.1f, 0.6f, 0.95f); break; case ANIM_STD_GETOUT_LHS: case ANIM_STD_GETOUT_LO_LHS: case ANIM_STD_GETOUT_RHS: case ANIM_STD_GETOUT_LO_RHS: ProcessDoorOpenAnimation(this, component, door, time, 0.06f, 0.43f); break; case ANIM_STD_CAR_CLOSE_LHS: case ANIM_STD_CAR_CLOSE_RHS: ProcessDoorCloseAnimation(this, component, door, time, 0.1f, 0.23f); break; case ANIM_STD_CAR_PULL_OUT_PED_RHS: case ANIM_STD_CAR_PULL_OUT_PED_LO_RHS: OpenDoor(component, door, 1.0f); break; case ANIM_STD_COACH_OPEN_LHS: case ANIM_STD_COACH_OPEN_RHS: ProcessDoorOpenAnimation(this, component, door, time, 0.66f, 0.8f); break; case ANIM_STD_COACH_GET_OUT_LHS: ProcessDoorOpenAnimation(this, component, door, time, 0.0f, 0.3f); break; case ANIM_STD_VAN_OPEN_DOOR_REAR_LHS: case ANIM_STD_VAN_OPEN_DOOR_REAR_RHS: ProcessDoorOpenAnimation(this, component, door, time, 0.37f, 0.55f); break; case ANIM_STD_VAN_CLOSE_DOOR_REAR_LHS: case ANIM_STD_VAN_CLOSE_DOOR_REAR_RHS: ProcessDoorCloseAnimation(this, component, door, time, 0.5f, 0.8f); break; case ANIM_STD_VAN_GET_OUT_REAR_LHS: case ANIM_STD_VAN_GET_OUT_REAR_RHS: ProcessDoorOpenAnimation(this, component, door, time, 0.5f, 0.6f); break; case ANIM_STD_NUM: OpenDoor(component, door, time); break; } } bool CAutomobile::IsDoorReady(eDoors door) { if(Doors[door].IsClosed() || IsDoorMissing(door)) return true; int doorflag = 0; switch(door){ case DOOR_FRONT_LEFT: doorflag = CAR_DOOR_FLAG_LF; break; case DOOR_FRONT_RIGHT: doorflag = CAR_DOOR_FLAG_RF; break; case DOOR_REAR_LEFT: doorflag = CAR_DOOR_FLAG_LR; break; case DOOR_REAR_RIGHT: doorflag = CAR_DOOR_FLAG_RR; break; default: 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; } bool CAutomobile::IsDoorReady(uint32 door) { switch(door){ case CAR_DOOR_RF: return IsDoorReady(DOOR_FRONT_RIGHT); case CAR_DOOR_RR: return IsDoorReady(DOOR_REAR_RIGHT); case CAR_DOOR_LF: return IsDoorReady(DOOR_FRONT_LEFT); case CAR_DOOR_LR: return IsDoorReady(DOOR_REAR_LEFT); default: return false; } } bool CAutomobile::IsDoorMissing(uint32 door) { switch(door){ case CAR_DOOR_RF: return IsDoorMissing(DOOR_FRONT_RIGHT); case CAR_DOOR_RR: return IsDoorMissing(DOOR_REAR_RIGHT); case CAR_DOOR_LF: return IsDoorMissing(DOOR_FRONT_LEFT); case CAR_DOOR_LR: return IsDoorMissing(DOOR_REAR_LEFT); default: return false; } } bool CAutomobile::IsOpenTopCar(void) { return GetModelIndex() == MI_STINGER || // component 0 is assumed to be a roof GetModelIndex() == MI_COMET && m_aExtras[0] != 0 && m_aExtras[1] != 0 || GetModelIndex() == MI_STALLION && m_aExtras[0] != 0 && m_aExtras[1] != 0; } 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) { RpAtomic *atomic; if(!bCanBeDamaged) return; if(culprit == FindPlayerPed() || culprit == FindPlayerVehicle()){ CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 20; CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 10.0f; CStats::PropertyDestroyed += CGeneral::GetRandomNumber()%6000 + 4000; } // explosion pushes vehicle up m_vecMoveSpeed.z += 0.13f; SetStatus(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); KillPedsInVehicle(); 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, true); 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(); if(GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) return false; 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 / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_LF); mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_LB])); colModel->spheres[1].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_LR); mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RF])); colModel->spheres[2].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_RF); mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RB])); colModel->spheres[3].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, 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 / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_LR); mat.Attach(RwFrameGetMatrix(m_aCarNodes[CAR_WHEEL_RM])); colModel->spheres[5].Set(mi->m_wheelScale / 2, mat.GetPosition(), SURFACE_RUBBER, CAR_PIECE_WHEEL_RR); colModel->numSpheres = 6; }else colModel->numSpheres = 4; return true; } void CAutomobile::BurstTyre(uint8 wheel, bool applyForces) { if(GetModelIndex() == MI_RHINO || bTyresDontBurst) return; switch(wheel){ case CAR_PIECE_WHEEL_LF: wheel = CARWHEEL_FRONT_LEFT; break; case CAR_PIECE_WHEEL_RF: wheel = CARWHEEL_FRONT_RIGHT; break; case CAR_PIECE_WHEEL_LR: wheel = CARWHEEL_REAR_LEFT; break; case CAR_PIECE_WHEEL_RR: wheel = CARWHEEL_REAR_RIGHT; break; } int status = Damage.GetWheelStatus(wheel); if(status == WHEEL_STATUS_OK){ Damage.SetWheelStatus(wheel, WHEEL_STATUS_BURST); CStats::TyresPopped++; DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TYRE_POP, 0.0f); if(GetStatus() == STATUS_SIMPLE){ SetStatus(STATUS_PHYSICS); CCarCtrl::SwitchVehicleToRealPhysics(this); } if(applyForces){ ApplyMoveForce(GetRight() * m_fMass * CGeneral::GetRandomNumberInRange(-0.03f, 0.03f)); ApplyTurnForce(GetRight() * m_fTurnMass * CGeneral::GetRandomNumberInRange(-0.03f, 0.03f), 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->GetFrontSeatPosn(); break; case CAR_DOOR_LF: seatPos = mi->GetFrontSeatPosn(); 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; // Removing that makes thiProcessEntityCollisions func. return false for van doors. doorPos.z += 0.5f; 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 (IsAlarmOn() || m_nCarHornTimer != 0) return; if (m_nCarHornDelay) { m_nCarHornDelay--; return; } m_nCarHornDelay = (CGeneral::GetRandomNumber() & 0x7F) + 150; r = m_nCarHornDelay & 7; if(r < 2){ m_nCarHornTimer = 45; }else if(r < 4){ if(pDriver) pDriver->Say(SOUND_PED_ANNOYED_DRIVER); m_nCarHornTimer = 45; }else{ if(pDriver) pDriver->Say(SOUND_PED_ANNOYED_DRIVER); } } 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 = m_aSuspensionSpringLength[0]*(1.0f - 1.0f/(4.0f*pHandling->fSuspensionForceLevel)) - colModel->lines[0].p0.z + mi->m_wheelScale*0.5f; 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 && bTankDetonateCars) 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){ if(this == FindPlayerVehicle()) CEventList::RegisterEvent(EVENT_EXPLOSION, EVENT_ENTITY_VEHICLE, m_aCollisionRecords[i], FindPlayerPed(), 2000); ((CVehicle*)m_aCollisionRecords[i])->BlowUpCar(this); } } bool CAutomobile::HasCarStoppedBecauseOfLight(void) { int i; if(GetStatus() != STATUS_SIMPLE && GetStatus() != 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.ConnectedNode(curnode->firstLink + i) == AutoPilot.m_nNextRouteNode) break; if(i < curnode->numLinks && ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) 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.ConnectedNode(curnode->firstLink + i) == AutoPilot.m_nPrevRouteNode) break; if(i < curnode->numLinks && ThePaths.m_carPathLinks[ThePaths.m_carPathConnections[curnode->firstLink + i]].trafficLightType & 3) return true; } return false; } void CPed::DeadPedMakesTyresBloody(void) { int minX = CWorld::GetSectorIndexX(GetPosition().x - 2.0f); if (minX < 0) minX = 0; int minY = CWorld::GetSectorIndexY(GetPosition().y - 2.0f); if (minY < 0) minY = 0; int maxX = CWorld::GetSectorIndexX(GetPosition().x + 2.0f); if (maxX > NUMSECTORS_X-1) maxX = NUMSECTORS_X-1; int maxY = CWorld::GetSectorIndexY(GetPosition().y + 2.0f); if (maxY > NUMSECTORS_Y-1) maxY = NUMSECTORS_Y-1; CWorld::AdvanceCurrentScanCode(); for (int curY = minY; curY <= maxY; curY++) { for (int curX = minX; curX <= maxX; curX++) { CSector *sector = CWorld::GetSector(curX, curY); MakeTyresMuddySectorList(sector->m_lists[ENTITYLIST_VEHICLES]); MakeTyresMuddySectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); } } } void CPed::MakeTyresMuddySectorList(CPtrList &list) { CAutomobile *car = nil; CBike *bike = nil; for (CPtrNode *node = list.first; node; node = node->next) { CVehicle *veh = (CVehicle*)node->item; if (veh->m_scanCode != CWorld::GetCurrentScanCode()) { veh->m_scanCode = CWorld::GetCurrentScanCode(); if (Abs(GetPosition().x - veh->GetPosition().x) < 10.0f && Abs(GetPosition().y - veh->GetPosition().y) < 10.0f) { if (veh->IsCar()) { bike = nil; car = (CAutomobile*)veh; } else if (veh->IsBike()) { bike = (CBike*)veh; car = nil; } if (veh->m_vecMoveSpeed.MagnitudeSqr2D() > 0.05f) { if (car) { for (int wheel = 0; wheel < 4; wheel++) { if (!car->m_aWheelSkidmarkBloody[wheel] && car->m_aSuspensionSpringRatio[wheel] < 1.0f) { CColModel* vehCol = car->GetModelInfo()->GetColModel(); CVector approxWheelOffset; switch (wheel) { case 0: approxWheelOffset = CVector(-vehCol->boundingBox.max.x, vehCol->boundingBox.max.y, 0.0f); break; case 1: approxWheelOffset = CVector(-vehCol->boundingBox.max.x, vehCol->boundingBox.min.y, 0.0f); break; case 2: approxWheelOffset = CVector(vehCol->boundingBox.max.x, vehCol->boundingBox.max.y, 0.0f); break; case 3: approxWheelOffset = CVector(vehCol->boundingBox.max.x, vehCol->boundingBox.min.y, 0.0f); break; default: break; } // I hope so CVector wheelPos = car->GetMatrix() * approxWheelOffset; if (Abs(wheelPos.z - GetPosition().z) < 2.0f) { if ((wheelPos - GetPosition()).MagnitudeSqr2D() < 1.0f) { if (CGame::nastyGame) { car->m_aWheelSkidmarkBloody[wheel] = true; DMAudio.PlayOneShot(car->m_audioEntityId, SOUND_SPLATTER, 0.0f); } if (car->m_fMass > 500.f) { car->ApplyMoveForce(CVector(0.0f, 0.0f, 50.0f * Min(1.0f, m_fMass * 0.001f))); CVector vehAndWheelDist = wheelPos - car->GetPosition(); car->ApplyTurnForce(CVector(0.0f, 0.0f, 50.0f * Min(1.0f, m_fTurnMass * 0.0005f)), vehAndWheelDist); if (car == FindPlayerVehicle()) { CPad::GetPad(0)->StartShake(300, 70); } } } } } } } else if (bike) { for (int wheel = 0; wheel < 2; wheel++) { if (!bike->m_aWheelSkidmarkBloody[wheel] && bike->m_aSuspensionSpringRatio[wheel] < 1.0f) { CColModel* vehCol = bike->GetModelInfo()->GetColModel(); CVector approxWheelOffset; switch (wheel) { case 0: approxWheelOffset = CVector(0.0f, 0.8f * vehCol->boundingBox.max.y, 0.0f); break; case 1: approxWheelOffset = CVector(0.0f, 0.8f * vehCol->boundingBox.min.y, 0.0f); default: break; } // I hope so CVector wheelPos = bike->GetMatrix() * approxWheelOffset; if (Abs(wheelPos.z - GetPosition().z) < 2.0f) { if ((wheelPos - GetPosition()).MagnitudeSqr2D() < 1.0f) { if (CGame::nastyGame) { bike->m_aWheelSkidmarkBloody[wheel] = true; DMAudio.PlayOneShot(bike->m_audioEntityId, SOUND_SPLATTER, 0.0f); } if (bike->m_fMass > 100.0f) { bike->ApplyMoveForce(CVector(0.0f, 0.0f, 10.0f)); CVector vehAndWheelDist = wheelPos - bike->GetPosition(); bike->ApplyTurnForce(CVector(0.0f, 0.0f, 10.0f), vehAndWheelDist); if (bike == FindPlayerVehicle()) { CPad::GetPad(0)->StartShake(300, 70); } } } } } } } } } } } } 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 & CAR_DOOR_FLAG_LF) == 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 & CAR_DOOR_FLAG_RF) == 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 & CAR_DOOR_FLAG_LF) == 0) OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, 0.0f); if(!IsDoorMissing(DOOR_FRONT_RIGHT) && (m_nGettingInFlags & CAR_DOOR_FLAG_RF) == 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; if (m_aCarNodes[component] == nil) 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(); // make wind rip off bonnet if(door == DOOR_BONNET && Doors[door].m_nDoorState == DOORST_OPEN && DotProduct(m_vecMoveSpeed, GetForward()) > 0.4f){ #ifdef FIX_BUGS CObject *comp = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_BONNET); #else CObject *comp = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_DOOR); #endif // make both doors invisible on car SetComponentVisibility(m_aCarNodes[CAR_BONNET], ATOMIC_FLAG_NONE); Damage.SetDoorStatus(DOOR_BONNET, DOOR_STATUS_MISSING); if(comp){ if(CGeneral::GetRandomNumber() & 1) comp->m_vecMoveSpeed = 0.4f*m_vecMoveSpeed + 0.1f*GetRight() + 0.5f*GetUp(); else comp->m_vecMoveSpeed = 0.4f*m_vecMoveSpeed - 0.1f*GetRight() + 0.5f*GetUp(); comp->ApplyTurnForce(10.0f*GetUp(), GetForward()); } } } 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(); } } for(component = 0; component < 4; component++) Damage.SetWheelStatus(component, WHEEL_STATUS_OK); if(GetModelIndex() == MI_HUNTER){ RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); }else if(IsRealHeli()){ RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RF]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_LB]), 0); RpAtomicSetFlags((RpAtomic*)GetFirstObject(m_aCarNodes[CAR_WHEEL_RB]), 0); } } 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; } 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); obj->bDrawLast = true; 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); obj->bDontStream = true; // 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->SetIsStatic(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).Magnitude(); obj->GetMatrix().Translate(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(GetStatus() == STATUS_WRECKED && IsVisible() && DotProduct(dist, TheCamera.GetPosition() - GetPosition()) > -0.5f){ dist = TheCamera.GetPosition() - GetPosition(); dist.Normalise(); dist.z += 0.3f; ApplyMoveForce(5.0f*dist); } if(CCollision::ProcessColModels(obj->GetMatrix(), *obj->GetColModel(), this->GetMatrix(), *this->GetColModel(), CWorld::m_aTempColPts, 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){ #ifdef FIX_BUGS obj = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_BONNET); #else obj = SpawnFlyingComponent(CAR_BONNET, COMPGROUP_DOOR); #endif // 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){ DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.0f); // show damaged part SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_DAM); }else if(status == PANEL_STATUS_MISSING){ if(!noFlyingComponents) SpawnFlyingComponent(component, COMPGROUP_PANEL); else CGlass::CarWindscreenShatters(this, false); // 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())->GetModelName()); 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())->GetModelName()); return; } if(!CanDoorsBeDamaged() && status > DOOR_STATUS_SMASHED && door != DOOR_BONNET && door != DOOR_BOOT){ Damage.SetDoorStatus(door, DOOR_STATUS_SMASHED); status = DOOR_STATUS_SMASHED; } if(door == DOOR_BOOT && status == DOOR_STATUS_SWINGING && pHandling->Flags & HANDLING_NOSWING_BOOT){ Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_MISSING); status = DOOR_STATUS_MISSING; } switch(status){ case DOOR_STATUS_SMASHED: // show damaged part SetComponentVisibility(m_aCarNodes[component], ATOMIC_FLAG_DAM); break; case DOOR_STATUS_SWINGING: // turn off angle cull for swinging doors RwFrameForAllObjects(m_aCarNodes[component], CVehicleModelInfo::SetAtomicFlagCB, (void*)ATOMIC_FLAG_NOCULL); break; case 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); break; } } 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(GetClump(), 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; } void CAutomobile::TellHeliToGoToCoors(float x, float y, float z, uint8 speed) { AutoPilot.m_nCarMission = MISSION_HELI_FLYTOCOORS; AutoPilot.m_vecDestinationCoors.x = x; AutoPilot.m_vecDestinationCoors.y = y; AutoPilot.m_vecDestinationCoors.z = z; AutoPilot.m_nCruiseSpeed = speed; SetStatus(STATUS_PHYSICS); if(m_fOrientation == 0.0f){ m_fOrientation = CGeneral::GetATanOfXY(GetForward().x, GetForward().y) + PI; while(m_fOrientation > TWOPI) m_fOrientation -= TWOPI; } } void CAutomobile::TellPlaneToGoToCoors(float x, float y, float z, uint8 speed) { AutoPilot.m_nCarMission = MISSION_PLANE_FLYTOCOORS; AutoPilot.m_vecDestinationCoors.x = x; AutoPilot.m_vecDestinationCoors.y = y; AutoPilot.m_vecDestinationCoors.z = z; AutoPilot.m_nCruiseSpeed = speed; SetStatus(STATUS_PHYSICS); if(m_fOrientation == 0.0f) m_fOrientation = CGeneral::GetATanOfXY(GetForward().x, GetForward().y); } void CAutomobile::PopBoot(void) { switch(Damage.GetDoorStatus(DOOR_BOOT)){ case DOOR_STATUS_OK: case DOOR_STATUS_SMASHED: Doors[DOOR_BOOT].m_fAngle = Doors[DOOR_BOOT].m_fMinAngle; CMatrix mat(RwFrameGetMatrix(m_aCarNodes[CAR_BOOT])); CVector pos = mat.GetPosition(); float axes[3] = { 0.0f, 0.0f, 0.0f }; axes[Doors[DOOR_BOOT].m_nAxis] = Doors[DOOR_BOOT].m_fAngle; mat.SetRotate(axes[0], axes[1], axes[2]); mat.Translate(pos); mat.UpdateRW(); } } void CAutomobile::PopBootUsingPhysics(void) { switch(Damage.GetDoorStatus(DOOR_BOOT)) case DOOR_STATUS_OK: case DOOR_STATUS_SMASHED: Damage.SetDoorStatus(DOOR_BOOT, DOOR_STATUS_SWINGING); Doors[DOOR_BOOT].m_fAngVel = -2.0f; } void CAutomobile::CloseAllDoors(void) { CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); if(!IsDoorMissing(DOOR_FRONT_LEFT)) OpenDoor(CAR_DOOR_LF, DOOR_FRONT_LEFT, 0.0f); if(mi->m_numDoors > 1){ if(!IsDoorMissing(DOOR_FRONT_RIGHT)) OpenDoor(CAR_DOOR_RF, DOOR_FRONT_RIGHT, 0.0f); if(mi->m_numDoors > 2){ if(!IsDoorMissing(DOOR_REAR_LEFT)) OpenDoor(CAR_DOOR_LR, DOOR_REAR_LEFT, 0.0f); if(!IsDoorMissing(DOOR_REAR_RIGHT)) OpenDoor(CAR_DOOR_RR, DOOR_REAR_RIGHT, 0.0f); } } } void CAutomobile::KnockPedOutCar(eWeaponType weapon, uint16 door, CPed *ped) { AnimationId anim = ANIM_STD_KO_FRONT; if(ped == nil) return; ped->m_vehDoor = door; ped->SetPedState(PED_IDLE); CAnimManager::BlendAnimation(ped->GetClump(), ped->m_animGroup, ANIM_STD_IDLE, 100.0f); CPed::PedSetOutCarCB(nil, ped); ped->SetMoveState(PEDMOVE_STILL); if(GetUp().z < 0.0f) ped->SetHeading(CGeneral::LimitRadianAngle(GetForward().Heading() + PI)); else ped->SetHeading(GetForward().Heading()); switch(weapon){ case WEAPONTYPE_UNARMED: case WEAPONTYPE_UNIDENTIFIED: ped->m_vecMoveSpeed = m_vecMoveSpeed; ped->m_pCollidingEntity = this; anim = ANIM_STD_NUM; break; case WEAPONTYPE_BASEBALLBAT: case WEAPONTYPE_RAMMEDBYCAR: case WEAPONTYPE_FALL: ped->m_vecMoveSpeed = m_vecMoveSpeed; anim = ANIM_STD_SPINFORWARD_LEFT; ApplyMoveForce(4.0f*GetUp() + 8.0f*GetRight()); break; } if(weapon != WEAPONTYPE_UNARMED){ ped->SetFall(1000, anim, 0); ped->bIsStanding = false; ped->m_headingRate = 0.0f; } ped->m_pMyVehicle = nil; } #ifdef COMPATIBLE_SAVES void CAutomobile::Save(uint8*& buf) { CVehicle::Save(buf); WriteSaveBuf(buf, Damage); SkipSaveBuf(buf, 1500 - 672 - sizeof(CDamageManager)); } void CAutomobile::Load(uint8*& buf) { CVehicle::Load(buf); Damage = ReadSaveBuf(buf); SkipSaveBuf(buf, 1500 - 672 - sizeof(CDamageManager)); SetupDamageAfterLoad(); } #endif ================================================ FILE: src/vehicles/Automobile.h ================================================ #pragma once #include "Vehicle.h" #include "DamageManager.h" #include "Door.h" #include "Skidmarks.h" class CObject; enum { CARWHEEL_FRONT_LEFT, CARWHEEL_REAR_LEFT, CARWHEEL_FRONT_RIGHT, CARWHEEL_REAR_RIGHT }; class CAutomobile : public CVehicle { public: CDamageManager Damage; CDoor Doors[6]; RwFrame *m_aCarNodes[NUM_CAR_NODES]; CColPoint m_aWheelColPoints[4]; float m_aSuspensionSpringRatio[4]; float m_aSuspensionSpringRatioPrev[4]; float m_aWheelTimer[4]; // set to 4.0 when wheel is touching ground, then decremented float m_auto_unused1; eSkidmarkType m_aWheelSkidmarkType[4]; bool m_aWheelSkidmarkBloody[4]; bool m_aWheelSkidmarkUnk[4]; float m_aWheelRotation[4]; float m_aWheelPosition[4]; float m_aWheelSpeed[4]; uint8 m_auto_unused2; #if (defined GTA_PS2 && !defined FIX_BUGS) uint8 m_bombType : 3; #endif uint8 bTaxiLight : 1; uint8 bFixedColour : 1; uint8 bBigWheels : 1; uint8 bWaterTight : 1; // no damage for non-player peds uint8 bNotDamagedUpsideDown : 1; uint8 bMoreResistantToDamage : 1; uint8 bTankDetonateCars : 1; uint8 bStuckInSand : 1; uint8 bHeliDestroyed : 1; #if (defined GTA_PS2 && !defined FIX_BUGS) CEntity* m_pBombRigger; #endif int16 m_doingBurnout; uint16 m_hydraulicState; uint32 m_nBusDoorTimerEnd; uint32 m_nBusDoorTimerStart; float m_aSuspensionSpringLength[4]; float m_aSuspensionLineLength[4]; float m_fHeightAboveRoad; float m_fTraction; float m_fTireTemperature; float m_fOrientation; // for heli and plane go-to float m_fPlaneSteer; // related to the above float m_fVelocityChangeForAudio; float m_randomValues[6]; // used for what? float m_fFireBlowUpTimer; CPhysical *m_aGroundPhysical[4]; // physicals touching wheels CVector m_aGroundOffset[4]; // from ground object to colpoint CEntity *m_pSetOnFireEntity; float m_weaponDoorTimerLeft; // still don't know what exactly this is float m_weaponDoorTimerRight; float m_fCarGunLR; float m_fCarGunUD; float m_fHeliOrientation; float m_fPropellerRotation; uint8 stuff4[4]; uint8 m_nWheelsOnGround; uint8 m_nDriveWheelsOnGround; uint8 m_nDriveWheelsOnGroundPrev; float m_fGasPedalAudio; tWheelState m_aWheelState[4]; static bool m_sAllTaxiLights; CAutomobile(int32 id, uint8 CreatedBy); // from CEntity void SetModelIndex(uint32 id); void ProcessControl(void); void Teleport(CVector v); void PreRender(void); void Render(void); // from CPhysical int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints); // from CVehicle void ProcessControlInputs(uint8); void GetComponentWorldPosition(int32 component, CVector &pos); bool IsComponentPresent(int32 component); void SetComponentRotation(int32 component, CVector rotation); void OpenDoor(int32 component, eDoors door, float openRatio); void ProcessOpenDoor(uint32, uint32, float); bool IsDoorReady(eDoors door); bool IsDoorFullyOpen(eDoors door); bool IsDoorClosed(eDoors door); bool IsDoorMissing(eDoors door); bool IsDoorReady(uint32 door); bool IsDoorMissing(uint32 door); bool IsOpenTopCar(void); void RemoveRefsToVehicle(CEntity *ent); void BlowUpCar(CEntity *ent); bool SetUpWheelColModel(CColModel *colModel); void BurstTyre(uint8 tyre, bool applyForces); bool IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset); float GetHeightAboveRoad(void); void PlayCarHorn(void); void FireTruckControl(void); void TankControl(void); void HydraulicControl(void); void VehicleDamage(float impulse, uint16 damagedPiece); void ProcessBuoyancy(void); void DoDriveByShootings(void); void DoHoverSuspensionRatios(void); int32 RcbanditCheckHitWheels(void); int32 RcbanditCheck1CarWheels(CPtrList &list); void PlaceOnRoadProperly(void); void dmgDrawCarCollidingParticles(const CVector &pos, float amount); void AddDamagedVehicleParticles(void); int32 AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed); void PlayHornIfNecessary(void); void ResetSuspension(void); void SetupSuspensionLines(void); void ScanForCrimes(void); void BlowUpCarsInPath(void); bool HasCarStoppedBecauseOfLight(void); void SetBusDoorTimer(uint32 timer, uint8 type); void ProcessAutoBusDoors(void); void ProcessSwingingDoor(int32 component, eDoors door); void SetupDamageAfterLoad(void); CObject *SpawnFlyingComponent(int32 component, uint32 type); CObject *RemoveBonnetInPedCollision(void); void SetPanelDamage(int32 component, ePanels panel, bool noFlyingComponents = false); void SetBumperDamage(int32 component, ePanels panel, bool noFlyingComponents = false); void SetDoorDamage(int32 component, eDoors door, bool noFlyingComponents = false); void TellHeliToGoToCoors(float x, float y, float z, uint8 speed); void TellPlaneToGoToCoors(float x, float y, float z, uint8 speed); void SetHeliOrientation(float orient) { m_fHeliOrientation = orient; } void ClearHeliOrientation(void) { m_fHeliOrientation = -1.0f; } void Fix(void); void SetComponentVisibility(RwFrame *frame, uint32 flags); void SetupModelNodes(void); void SetTaxiLight(bool light); bool GetAllWheelsOffGround(void); void HideAllComps(void); void ShowAllComps(void); void ReduceHornCounter(void); void PopBoot(void); void PopBootUsingPhysics(void); void CloseAllDoors(void); void KnockPedOutCar(eWeaponType weapon, uint16 door, CPed *ped); #ifdef COMPATIBLE_SAVES virtual void Save(uint8*& buf); virtual void Load(uint8*& buf); #endif static const uint32 nSaveStructSize; static void SetAllTaxiLights(bool set); }; extern CVector vecHunterGunPos; extern bool bAllCarCheat; ================================================ FILE: src/vehicles/Bike.cpp ================================================ #include "common.h" #include "General.h" #include "Pad.h" #include "DMAudio.h" #include "Clock.h" #include "Timecycle.h" #include "ZoneCull.h" #include "Camera.h" #include "Darkel.h" #include "Rubbish.h" #include "Explosion.h" #include "Particle.h" #include "ParticleObject.h" #include "Shadows.h" #include "PointLights.h" #include "Coronas.h" #include "SpecialFX.h" #include "WaterLevel.h" #include "Floater.h" #include "World.h" #include "SurfaceTable.h" #include "Weather.h" #include "Record.h" #include "CarCtrl.h" #include "CarAI.h" #include "Script.h" #include "Stats.h" #include "Replay.h" #include "AnimManager.h" #include "RpAnimBlend.h" #include "AnimBlendAssociation.h" #include "Ped.h" #include "PlayerPed.h" #include "DamageManager.h" #include "Vehicle.h" #include "Automobile.h" #include "Bike.h" #include "Debug.h" const uint32 CBike::nSaveStructSize = #ifdef COMPATIBLE_SAVES 1260; #else sizeof(CBoat); #endif // TODO: maybe put this somewhere else inline void GetRelativeMatrix(RwMatrix *mat, RwFrame *frm, RwFrame *end) { *mat = *RwFrameGetMatrix(frm); frm = RwFrameGetParent(frm); while(frm){ RwMatrixTransform(mat, RwFrameGetMatrix(frm), rwCOMBINEPOSTCONCAT); frm = RwFrameGetParent(frm); if(frm == end) frm = nil; } } #define FAKESUSPENSION (99999.992f) CBike::CBike(int32 id, uint8 CreatedBy) : CVehicle(CreatedBy) { int i; CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); switch(id){ case MI_ANGEL: case MI_FREEWAY: m_bikeAnimType = ASSOCGRP_BIKE_HARLEY; break; case MI_PIZZABOY: case MI_FAGGIO: m_bikeAnimType = ASSOCGRP_BIKE_VESPA; break; case MI_PCJ600: m_bikeAnimType = ASSOCGRP_BIKE_STANDARD; break; case MI_SANCHEZ: m_bikeAnimType = ASSOCGRP_BIKE_DIRT; break; default: assert(0 && "invalid bike model ID"); } m_vehType = VEHICLE_TYPE_BIKE; m_fFireBlowUpTimer = 0.0f; m_doingBurnout = 0; m_bike_flag01 = false; SetModelIndex(id); pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)mi->m_handlingId); pBikeHandling = mod_HandlingManager.GetBikePointer((tVehicleType)mi->m_handlingId); pFlyingHandling = mod_HandlingManager.GetFlyingPointer((tVehicleType)mi->m_handlingId); m_bike_unused1 = 20.0f; m_bike_unused2 = 0; mi->ChooseVehicleColour(m_currentColour1, m_currentColour2); m_fRearForkLength = 0.0f; m_fFrontForkY = 0.0; m_fFrontForkZ = 0.0; m_fFrontForkSlope = Tan(DEGTORAD(mi->m_bikeSteerAngle)); m_fMass = pHandling->fMass; m_fTurnMass = pHandling->fTurnMass; m_vecCentreOfMass = pHandling->CentreOfMass; m_vecCentreOfMass.z = 0.1f; m_fAirResistance = pHandling->Dimension.x*pHandling->Dimension.z/m_fMass; m_fElasticity = 0.05f; m_fBuoyancy = pHandling->fBuoyancy; m_fSteerAngle = 0.0f; m_fWheelAngle = 0.0f; m_fLeanLRAngle = 0.0f; m_fLeanLRAngle2 = 0.0f; m_fGasPedal = 0.0f; m_fBrakePedal = 0.0f; m_fLeanInput = 0.0f; m_fPedLeanAmountLR = 0.0f; m_fPedLeanAmountUD = 0.0f; m_pSetOnFireEntity = nil; m_pBombRigger = nil; m_fGasPedalAudio = 0.0f; m_bike_flag02 = false; bWaterTight = false; bIsBeingPickedUp = false; bIsStanding = false; bExtraSpeed = false; bIsOnFire = false; bWheelieCam = false; m_fTireTemperature = 1.0f; m_fBrakeDestabilization = 0.0f; m_fVelocityChangeForAudio = 0; for(i = 0; i < 2; i++){ m_aWheelRotation[i] = 0.0f; m_aWheelSpeed[i] = 0.0f; m_aWheelState[i] = WHEEL_STATE_NORMAL; m_aWheelSkidmarkType[i] = SKIDMARK_NORMAL; m_aWheelSkidmarkBloody[i] = false; m_aWheelSkidmarkUnk[0] = false; m_wheelStatus[i] = WHEEL_STATUS_OK; } for(i = 0; i < 4; i++){ m_aGroundPhysical[i] = nil; m_aGroundOffset[i] = CVector(0.0f, 0.0f, 0.0f); m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i] = 1.0f; m_aWheelTimer[i] = 0.0f; } 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; } // BUG? this would make more sense in the if above colModel->lines[0].p0.z = FAKESUSPENSION; SetupSuspensionLines(); AutoPilot.m_nCarMission = MISSION_NONE; AutoPilot.m_nTempAction = TEMPACT_NONE; AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); AutoPilot.m_bStayInCurrentLevel = false; SetStatus(STATUS_SIMPLE); bUseCollisionRecords = true; m_nNumPassengers = 0; bIsVan = false; bIsBus = false; bIsBig = false; bLowVehicle = false; bPedPhysics = false; bLeanMatrixClean = false; m_leanMatrix = GetMatrix(); } void CBike::SetModelIndex(uint32 id) { CVehicle::SetModelIndex(id); SetupModelNodes(); } #define SAND_SLOWDOWN (0.02f) CVector vecTestResistance(0.9995f, 0.9f, 0.95f); float fDAxisX = 1.0f; float fDAxisXExtra = 100.0f; float fDAxisY = 1000.0f; float fInAirXRes = 0.98f; float fFlySpeedMult = -0.6f; #pragma optimize("", off) // a workaround for another compiler bug =P, original had optimize off for this function too though void CBike::ProcessControl(void) { int i; float wheelRot; float acceleration = 0.0f; bool bBalancedByRider = false; bool bStuckInSand = false; float brake = 0.0f; CColModel *colModel = GetColModel(); float wheelScale = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_wheelScale; bWarnedPeds = false; bLeanMatrixClean = false; m_doingBurnout = 0; bExtraSpeed = false; bRestingOnPhysical = false; if(CReplay::IsPlayingBack()) return; ProcessCarAlarm(); ActivateBombWhenEntered(); CRubbish::StirUp(this); UpdateClumpAlpha(); AutoPilot.m_bSlowedDownBecauseOfCars = false; AutoPilot.m_bSlowedDownBecauseOfPeds = false; switch(GetStatus()){ case STATUS_PLAYER: bBalancedByRider = true; bIsBeingPickedUp = false; if(FindPlayerPed()->GetPedState() != PED_EXIT_CAR && FindPlayerPed()->GetPedState() != PED_DRAG_FROM_CAR){ ProcessControlInputs(0); if(m_fLeanInput < 0.0f){ m_vecCentreOfMass.y = pHandling->CentreOfMass.y + pBikeHandling->fLeanBakCOM*m_fLeanInput; CVector com = m_vecCentreOfMass; #ifdef FIX_BUGS // center of mass has to have world space orientation. unfortunately we can't do wheelies // at high speed then, flipping y here is like riding south without this fix where wheelies work com.y = -com.y; com = Multiply3x3(GetMatrix(), com); #endif if(m_fBrakePedal == 0.0f && !bIsHandbrakeOn || m_nWheelsOnGround == 0){ if(GetModelIndex() == MI_SANCHEZ){ float force = m_fLeanInput*m_fTurnMass*pBikeHandling->fLeanBackForce*Min(m_vecMoveSpeed.Magnitude(), 0.1f); force *= 0.7f*m_fGasPedal + 0.3f; ApplyTurnForce(-force*CTimer::GetTimeStep()*GetUp(), com+GetForward()); }else{ float force = m_fLeanInput*m_fTurnMass*pBikeHandling->fLeanBackForce*Min(m_vecMoveSpeed.Magnitude(), 0.1f); force *= 0.5f*m_fGasPedal + 0.5f; ApplyTurnForce(-force*CTimer::GetTimeStep()*GetUp(), com+GetForward()); } } }else{ m_vecCentreOfMass.y = pHandling->CentreOfMass.y + pBikeHandling->fLeanFwdCOM*m_fLeanInput; CVector com = m_vecCentreOfMass; #ifdef FIX_BUGS // see above com.y = -com.y; com = Multiply3x3(GetMatrix(), com); #endif if(m_fBrakePedal < 0.0f || m_nWheelsOnGround == 0){ float force = m_fLeanInput*m_fTurnMass*pBikeHandling->fLeanFwdForce*Min(m_vecMoveSpeed.Magnitude(), 0.1f); ApplyTurnForce(-force*CTimer::GetTimeStep()*GetUp(), com+GetForward()); } } PruneReferences(); if(GetStatus() == STATUS_PLAYER && !CRecordDataForChase::IsRecording()) DoDriveByShootings(); if(m_aSuspensionSpringRatio[0] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[0].surfaceB) == ADHESIVE_SAND || m_aSuspensionSpringRatio[1] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[1].surfaceB) == ADHESIVE_SAND || m_aSuspensionSpringRatio[2] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[2].surfaceB) == ADHESIVE_SAND || m_aSuspensionSpringRatio[3] < 1.0f && CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[3].surfaceB) == ADHESIVE_SAND){ CVector parallelSpeed = m_vecMoveSpeed - DotProduct(m_vecMoveSpeed, GetUp())*GetUp(); if(m_fGasPedal > 0.3f){ if(parallelSpeed.MagnitudeSqr() < SQR(0.3f)) bStuckInSand = true; parallelSpeed -= DotProduct(parallelSpeed, GetForward())*GetForward(); } ApplyMoveForce(parallelSpeed * -CTimer::GetTimeStep()*SAND_SLOWDOWN*m_fMass); } } if(CPad::GetPad(0)->WeaponJustDown()) ActivateBomb(); break; case STATUS_PLAYER_PLAYBACKFROMBUFFER: bBalancedByRider = true; break; case STATUS_SIMPLE: CCarAI::UpdateCarAI(this); CPhysical::ProcessControl(); CCarCtrl::UpdateCarOnRails(this); m_nWheelsOnGround = 2; m_nDriveWheelsOnGroundPrev = m_nDriveWheelsOnGround; m_nDriveWheelsOnGround = 2; pHandling->Transmission.CalculateGearForSimpleCar(AutoPilot.m_fMaxTrafficSpeed/50.0f, m_nCurrentGear); wheelRot = ProcessWheelRotation(WHEEL_STATE_NORMAL, GetForward(), m_vecMoveSpeed, 0.5f*wheelScale); for(i = 0; i < 2; i++) m_aWheelRotation[i] += wheelRot; PlayHornIfNecessary(); ReduceHornCounter(); bVehicleColProcessed = false; bAudioChangingGear = false; bWheelieCam = false; // that's all we do for simple vehicles return; case STATUS_PHYSICS: CCarAI::UpdateCarAI(this); CCarCtrl::SteerAICarWithPhysics(this); PlayHornIfNecessary(); bBalancedByRider = true; bWheelieCam = false; if(bIsBeingCarJacked){ m_fGasPedal = 0.0f; m_fBrakePedal = 1.0f; bIsHandbrakeOn = true; }else bIsBeingPickedUp = false; break; case STATUS_ABANDONED: m_fBrakePedal = 0.0f; if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f) || bIsStanding) bIsHandbrakeOn = true; else bIsHandbrakeOn = false; m_fGasPedal = 0.0f; #ifdef FIX_BUGS if(!IsAlarmOn()) #endif m_nCarHornTimer = 0; bBalancedByRider = (pDriver || pPassengers[0] || bIsBeingCarJacked) && !bIsStanding; m_fPedLeanAmountLR = 0.0f; m_fPedLeanAmountUD = 0.0f; bWheelieCam = false; if(bIsBeingCarJacked){ m_fGasPedal = 0.0f; m_fBrakePedal = 1.0f; bIsHandbrakeOn = true; } break; case STATUS_WRECKED: m_fBrakePedal = 0.05f; bIsHandbrakeOn = true; m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; #ifdef FIX_BUGS if(!IsAlarmOn()) #endif m_nCarHornTimer = 0; bBalancedByRider = false; bWheelieCam = false; m_fPedLeanAmountLR = 0.0f; m_fPedLeanAmountUD = 0.0f; break; case STATUS_PLAYER_DISABLED: if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.1f)){ m_fBrakePedal = 1.0f; bIsHandbrakeOn = true; }else{ m_fBrakePedal = 0.0f; bIsHandbrakeOn = false; } m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; #ifdef FIX_BUGS if(!IsAlarmOn()) #endif m_nCarHornTimer = 0; bBalancedByRider = true; bWheelieCam = false; break; } if(bIsStanding) if(Abs(GetRight().z) > 0.35f || Abs(GetForward().z) > 0.5f) bIsStanding = false; if(bBalancedByRider || bIsBeingPickedUp || bIsStanding){ float fDx = fDAxisX; CVector res = vecTestResistance; CVector localTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); if(GetStatus() == STATUS_PLAYER){ if(m_aWheelTimer[BIKESUSP_F1] == 0.0f && m_aWheelTimer[BIKESUSP_F2] == 0.0f){ fDx = fDAxisXExtra; if(!(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f) && GetForward().z > 0.0f) res.x -= Min(0.25f*Abs(pBikeHandling->fWheelieAng-GetForward().z), 0.07f); else res.x = fInAirXRes; }else if(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f){ fDx = fDAxisXExtra; if(GetForward().z < 0.0f) res.x *= Min(0.3f*Abs(pBikeHandling->fStoppieAng-GetForward().z), 0.1f) + 0.9f; } } res.x *= 1.0f/(fDx*SQR(localTurnSpeed.x) + 1.0f); res.y *= 1.0f/(fDAxisY*SQR(localTurnSpeed.y) + 1.0f); res.x = Pow(res.x, CTimer::GetTimeStep()); res.y = Pow(res.y, CTimer::GetTimeStep()); float turnX = localTurnSpeed.x*(res.x - 1.0f); float turnY = localTurnSpeed.y*(res.y - 1.0f); res = -GetUp() * turnY * m_fTurnMass; ApplyTurnForce(res, GetRight() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); res = GetUp() * turnX * m_fTurnMass; ApplyTurnForce(res, GetForward() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); if(GetStatus() != STATUS_PLAYER) m_vecCentreOfMass = pHandling->CentreOfMass; }else{ m_vecCentreOfMass = pHandling->CentreOfMass; m_vecCentreOfMass.z = pBikeHandling->fNoPlayerCOMz; } // Skip physics if object is found to have been static recently bool skipPhysics = false; if(!bIsStuck && (GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED) && !bIsBeingPickedUp){ 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(GetStatus() == 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]){ bRestingOnPhysical = true; if(!CWorld::bForceProcessControl && m_aGroundPhysical[i]->bIsInSafePosition){ bWasPostponed = true; return; } } if(bRestingOnPhysical){ skipPhysics = false; m_nStaticFrames = 0; } VehicleDamage(); if(skipPhysics){ bHasContacted = false; bIsInSafePosition = false; bWasPostponed = false; bHasHitWall = false; m_nCollisionRecords = 0; bHasCollided = false; bVehicleColProcessed = false; bAudioChangingGear = 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); // missing. BUG? // m_fTireTemperature = 1.0f; if(bIsStanding && m_fWheelAngle < DEGTORAD(20.0f)) m_fWheelAngle += DEGTORAD(1.0f)*CTimer::GetTimeStep(); if(bIsStanding){ float f = Pow(0.97f, CTimer::GetTimeStep()); m_fLeanLRAngle2 = m_fLeanLRAngle2*f - (Asin(clamp(GetRight().z,-1.0f,1.0f))+DEGTORAD(15.0f))*(1.0f-f); m_fLeanLRAngle = m_fLeanLRAngle2; } }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; m_bIsVehicleBeingShifted = false; bSkipLineCol = false; ApplyMoveSpeed(); ApplyTurnSpeed(); for(i = 0; CheckCollision() && i < 5; i++){ GetMatrix() = mat; ApplyMoveSpeed(); ApplyTurnSpeed(); } bIsInSafePosition = true; bIsStuck = false; } if(!(bBalancedByRider || bIsBeingPickedUp || bIsStanding)){ if(GetRight().z < 0.0f){ if(m_fSteerAngle > -DEGTORAD(25.0f)) m_fSteerAngle -= DEGTORAD(0.5f)*CTimer::GetTimeStep(); }else{ if(m_fSteerAngle < DEGTORAD(25.0f)) m_fSteerAngle += DEGTORAD(0.5f)*CTimer::GetTimeStep(); } } // Lean forward speed up float savedAirResistance = m_fAirResistance; if(GetStatus() == STATUS_PLAYER && pDriver){ CAnimBlendAssociation *assoc = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_LEANF); if(assoc && assoc->blendAmount > 0.5f && assoc->currentTime > 0.06f && assoc->currentTime < 0.14f){ m_fAirResistance *= 0.6f; if(m_fGasPedal > 0.5f && DotProduct(m_vecMoveSpeed, GetForward()) > 0.25f){ ApplyMoveForce(0.2f*m_fMass*GRAVITY*CTimer::GetTimeStep()*GetForward()); bExtraSpeed = true; } } } CPhysical::ProcessControl(); m_fAirResistance = savedAirResistance; 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); } int rnd = 0; float fwdSpeed = Abs(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(m_wheelStatus[i/2] == WHEEL_STATUS_MISSING) m_aSuspensionSpringRatio[i] = 1.0f; else if(m_wheelStatus[i/2] == WHEEL_STATUS_BURST){ // wheel more bumpy the faster we are if(i == BIKESUSP_F1 || i == BIKESUSP_R1) rnd = CGeneral::GetRandomNumberInRange(0, (uint16)(40*fwdSpeed) + 98) < 100; if(rnd){ 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(); } } m_aWheelSkidmarkType[0] = m_aWheelSkidmarkType[1] = SKIDMARK_NORMAL; m_aWheelSkidmarkUnk[0] = m_aWheelSkidmarkUnk[1] = false; // Make springs push up vehicle for(i = 0; i < 4; i++){ if(m_aSuspensionSpringRatio[i] < 1.0f){ float bias = pHandling->fSuspensionBias; if(i == BIKESUSP_R1 || i == BIKESUSP_R2) bias = 1.0f - bias; if(m_aWheelColPoints[i].normal.z > 0.35f) ApplySpringCollisionAlt(pHandling->fSuspensionForceLevel, springDirections[i], contactPoints[i], m_aSuspensionSpringRatio[i], bias, m_aWheelColPoints[i].normal); else ApplySpringCollision(pHandling->fSuspensionForceLevel, springDirections[i], contactPoints[i], m_aSuspensionSpringRatio[i], bias); if(m_aWheelColPoints[i].surfaceB == SURFACE_GRASS || m_aWheelColPoints[i].surfaceB == SURFACE_MUD_DRY){ if(i < 2) m_aWheelSkidmarkType[0] = SKIDMARK_MUDDY; else m_aWheelSkidmarkType[1] = SKIDMARK_MUDDY; }else if(m_aWheelColPoints[i].surfaceB == SURFACE_SAND || m_aWheelColPoints[i].surfaceB == SURFACE_SAND_BEACH){ if(i < 2){ m_aWheelSkidmarkType[0] = SKIDMARK_SANDY; m_aWheelSkidmarkUnk[0] = true; }else{ m_aWheelSkidmarkType[1] = SKIDMARK_SANDY; m_aWheelSkidmarkUnk[1] = true; } } }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 } } CVector normal; if(m_aSuspensionSpringRatio[0] < 1.0f || m_aSuspensionSpringRatio[1] < 1.0f){ normal = m_aSuspensionSpringRatio[0] < 1.0f ? m_aWheelColPoints[0].normal : m_aWheelColPoints[1].normal; if(normal.z > 0.35f) springDirections[0] = -normal; normal = m_aSuspensionSpringRatio[1] < 1.0f ? m_aWheelColPoints[1].normal : m_aWheelColPoints[0].normal; if(normal.z > 0.35f) springDirections[1] = -normal; } if(m_aSuspensionSpringRatio[2] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f){ normal = m_aSuspensionSpringRatio[2] < 1.0f ? m_aWheelColPoints[2].normal : m_aWheelColPoints[3].normal; if(normal.z > 0.35f) springDirections[2] = -normal; normal = m_aSuspensionSpringRatio[3] < 1.0f ? m_aWheelColPoints[3].normal : m_aWheelColPoints[2].normal; if(normal.z > 0.35f) springDirections[3] = -normal; } // game has dead code here if m_vecMoveSpeed.Magnitude() < 0.01f // 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(!CVehicle::bCheat3) gripCheat = false; acceleration = pHandling->Transmission.CalculateDriveAcceleration(m_fGasPedal, m_nCurrentGear, m_fChangeGearTime, fwdSpeed, gripCheat); acceleration /= m_fForceMultiplier; brake = m_fBrakePedal * pHandling->fBrakeDeceleration * CTimer::GetTimeStep(); bool neutralHandling = GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE && (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-tractionBiasFront; // 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++; if(i == BIKESUSP_R1 || i == BIKESUSP_R2) m_nDriveWheelsOnGround = 1; if(m_nWheelsOnGround == 1) m_vecAvgSurfaceNormal = m_aWheelColPoints[i].normal; else m_vecAvgSurfaceNormal += m_aWheelColPoints[i].normal; } } if(m_nWheelsOnGround == 0) m_vecAvgSurfaceNormal = CVector(0.0f, 0.0f, 1.0f); else{ m_vecAvgSurfaceNormal /= m_nWheelsOnGround; if(DotProduct(m_vecAvgSurfaceNormal, GetUp()) < -0.5f) m_vecAvgSurfaceNormal *= -1.0f; } // Find contact points for wheel processing int frontLine = m_aSuspensionSpringRatio[BIKESUSP_F1] < m_aSuspensionSpringRatio[BIKESUSP_F2] ? BIKESUSP_F1 : BIKESUSP_F2; CVector frontContact(0.0f, colModel->lines[BIKESUSP_F1].p0.y, colModel->lines[BIKESUSP_F1].p0.z - m_aSuspensionSpringRatio[frontLine]*m_aSuspensionSpringLength[BIKESUSP_F1] - 0.5f*wheelScale); frontContact = Multiply3x3(GetMatrix(), frontContact); int rearLine = m_aSuspensionSpringRatio[BIKESUSP_R1] < m_aSuspensionSpringRatio[BIKESUSP_R2] ? BIKESUSP_R1 : BIKESUSP_R2; CVector rearContact(0.0f, colModel->lines[BIKESUSP_R1].p0.y, colModel->lines[BIKESUSP_R1].p0.z - m_aSuspensionSpringRatio[rearLine]*m_aSuspensionSpringLength[BIKESUSP_R1] - 0.5f*wheelScale); rearContact = Multiply3x3(GetMatrix(), rearContact); float traction = 0.004f * m_fTraction; traction *= pHandling->fTractionMultiplier / 4.0f; // Turn wheel if(GetStatus() == STATUS_PLAYER || !bIsStanding || bIsBeingPickedUp){ if(Abs(m_vecMoveSpeed.x) < 0.01f && Abs(m_vecMoveSpeed.y) < 0.01f && m_fSteerAngle == 0.0f){ m_fWheelAngle *= Pow(0.96f, CTimer::GetTimeStep()); }else{ float f; if(fwdSpeed > 0.01f && m_aWheelTimer[BIKESUSP_F1] > 0.0f && m_aWheelTimer[BIKESUSP_F2] > 0.0f && GetStatus() == STATUS_PLAYER){ CColPoint point; point.surfaceA = SURFACE_WHEELBASE; point.surfaceB = SURFACE_TARMAC; float steer = CSurfaceTable::GetAdhesiveLimit(point)*4.0f*pBikeHandling->fSpeedSteer*traction; if(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[rearLine].surfaceB) == ADHESIVE_LOOSE || CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[rearLine].surfaceB) == ADHESIVE_SAND) steer *= pBikeHandling->fSlipSteer; f = Asin(Min(steer/SQR(fwdSpeed), 1.0))/DEGTORAD(pHandling->fSteeringLock); if(m_fSteerAngle < 0.0f && m_fLeanLRAngle < 0.0f || m_fSteerAngle > 0.0f && m_fLeanLRAngle > 0.0f) f *= 2.0f; f = Min(f, 1.0f); }else{ f = 1.0f; } if(GetStatus() != STATUS_PLAYER) f = 1.0f; m_fWheelAngle = m_fSteerAngle*f; } }else if(m_fWheelAngle < DEGTORAD(20.0f)) m_fWheelAngle += DEGTORAD(1.5f)*CTimer::GetTimeStep(); static float fThrust; static tWheelState WheelState[2]; CVector initialMoveSpeed = m_vecMoveSpeed; bool rearWheelsFirst = !!(pHandling->Flags & HANDLING_REARWHEEL_1ST); // Process front wheel - first try if(!rearWheelsFirst){ if(m_aWheelTimer[BIKESUSP_F1] > 0.0f || m_aWheelTimer[BIKESUSP_F2] > 0.0f){ // Wheel on ground eBikeWheelSpecial spec; if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f) spec = BIKE_WHEELSPEC_0; else spec = BIKE_WHEELSPEC_2; CVector wheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fWheelAngle), Cos(m_fWheelAngle), 0.0f)); wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[frontLine].normal)*m_aWheelColPoints[frontLine].normal; wheelFwd.Normalise(); CVector wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[frontLine].normal); wheelRight.Normalise(); fThrust = 0.0f; m_aWheelColPoints[frontLine].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[frontLine])*traction; float adhesionDestab = 1.0f; if(m_fBrakeDestabilization > 0.0f) switch(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[frontLine].surfaceB)){ case ADHESIVE_HARD: case ADHESIVE_LOOSE: adhesionDestab = 0.9f; break; case ADHESIVE_ROAD: adhesionDestab = 0.7f; break; } if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[frontLine].surfaceB); if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) adhesion *= 0.4f; WheelState[BIKEWHEEL_FRONT] = m_aWheelState[BIKEWHEEL_FRONT]; CVector contactSpeed = GetSpeed(frontContact); ProcessBikeWheel(wheelFwd, wheelRight, contactSpeed, frontContact, 2, fThrust, brake*brakeBiasFront, adhesion*tractionBiasFront, adhesionDestab, BIKEWHEEL_FRONT, &m_aWheelSpeed[BIKEWHEEL_FRONT], &WheelState[BIKEWHEEL_FRONT], spec, m_wheelStatus[BIKEWHEEL_FRONT]); if(bStuckInSand && (WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SPINNING || WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SKIDDING)) WheelState[BIKEWHEEL_FRONT] = WHEEL_STATE_NORMAL; }else{ // Wheel in the air m_aWheelSpeed[BIKEWHEEL_FRONT] *= 0.95f; m_aWheelRotation[BIKEWHEEL_FRONT] += m_aWheelSpeed[BIKEWHEEL_FRONT]; } } // Process rear wheel if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f){ // Wheel on ground float rearBrake = brake; float rearTraction = traction; CVector wheelFwd = GetForward(); CVector wheelRight = GetRight(); wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[rearLine].normal)*m_aWheelColPoints[rearLine].normal; wheelFwd.Normalise(); wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[rearLine].normal); wheelRight.Normalise(); if(bIsHandbrakeOn){ #ifdef FIX_BUGS // Not sure if this is needed, but brake usually has timestep as a factor rearBrake = 20000.0f * CTimer::GetTimeStepFix(); #else rearBrake = 20000.0f; #endif m_fTireTemperature = 1.0f; }else if(m_doingBurnout){ rearBrake = 0.0f; rearTraction = 0.0f; ApplyTurnForce(contactPoints[BIKESUSP_R1], -0.0007f*m_fTurnMass*m_fSteerAngle*GetRight()*CTimer::GetTimeStep()); }else if(m_fTireTemperature < 1.0f && m_fGasPedal > 0.75f){ rearTraction *= m_fTireTemperature; ApplyTurnForce(contactPoints[BIKESUSP_R1], (1.0f-m_fTireTemperature)*-0.0007f*m_fTurnMass*m_fSteerAngle*GetRight()*CTimer::GetTimeStep()); } if(fThrust > 0.0f && brake > 0.0f) brake = 0.0f; // only affects next front wheel. is this intended? fThrust = acceleration; m_aWheelColPoints[rearLine].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[rearLine])*rearTraction; float adhesionDestab = 1.0f; if(m_fBrakeDestabilization > 0.0f) switch(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[rearLine].surfaceB)){ case ADHESIVE_HARD: case ADHESIVE_LOOSE: adhesionDestab = 0.9f; break; case ADHESIVE_ROAD: adhesionDestab = 0.7f; break; } if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[rearLine].surfaceB); if(m_wheelStatus[BIKEWHEEL_REAR] == WHEEL_STATUS_BURST) adhesion *= 0.4f; WheelState[BIKEWHEEL_REAR] = m_aWheelState[BIKEWHEEL_REAR]; CVector contactSpeed = GetSpeed(rearContact); ProcessBikeWheel(wheelFwd, wheelRight, contactSpeed, rearContact, 2, fThrust, rearBrake*brakeBiasRear, adhesion*tractionBiasRear, adhesionDestab, BIKEWHEEL_REAR, &m_aWheelSpeed[BIKEWHEEL_REAR], &WheelState[BIKEWHEEL_REAR], BIKE_WHEELSPEC_1, m_wheelStatus[BIKEWHEEL_REAR]); if(bStuckInSand && (WheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING || WheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SKIDDING)) WheelState[BIKEWHEEL_REAR] = WHEEL_STATE_NORMAL; }else{ // Wheel in the air if(bIsHandbrakeOn) m_aWheelSpeed[BIKEWHEEL_REAR] = 0.0f; else{ if(acceleration > 0.0f){ if(m_aWheelSpeed[BIKEWHEEL_REAR] < 2.0f) m_aWheelSpeed[BIKEWHEEL_REAR] -= 0.2f; }else{ if(m_aWheelSpeed[BIKEWHEEL_REAR] > -2.0f) m_aWheelSpeed[BIKEWHEEL_REAR] += 0.1f; } } m_aWheelRotation[BIKEWHEEL_REAR] += m_aWheelSpeed[BIKEWHEEL_REAR]; } if(m_doingBurnout && m_aWheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING){ m_fTireTemperature += 0.001f*CTimer::GetTimeStep(); if(m_fTireTemperature > 3.0f) m_fTireTemperature = 3.0f; }else if(m_fTireTemperature > 1.0f){ m_fTireTemperature = (m_fTireTemperature - 1.0f)*Pow(0.995f, CTimer::GetTimeStep()) + 1.0f; } // Process front wheel - second try if(rearWheelsFirst){ if(m_aWheelTimer[BIKESUSP_F1] > 0.0f || m_aWheelTimer[BIKESUSP_F2] > 0.0f){ // Wheel on ground eBikeWheelSpecial spec; if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f) spec = BIKE_WHEELSPEC_0; else spec = BIKE_WHEELSPEC_2; CVector wheelFwd = GetMatrix() * CVector(-Sin(m_fWheelAngle), Cos(m_fWheelAngle), 0.0f); wheelFwd -= DotProduct(wheelFwd, m_aWheelColPoints[frontLine].normal)*m_aWheelColPoints[frontLine].normal; wheelFwd.Normalise(); CVector wheelRight = CrossProduct(wheelFwd, m_aWheelColPoints[frontLine].normal); wheelRight.Normalise(); fThrust = 0.0f; m_aWheelColPoints[frontLine].surfaceA = SURFACE_WHEELBASE; float adhesion = CSurfaceTable::GetAdhesiveLimit(m_aWheelColPoints[frontLine])*traction; float adhesionDestab = 1.0f; if(m_fBrakeDestabilization > 0.0f) switch(CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[frontLine].surfaceB)){ case ADHESIVE_HARD: case ADHESIVE_LOOSE: adhesionDestab = 0.9f; break; case ADHESIVE_ROAD: adhesionDestab = 0.7f; break; } if(GetStatus() == STATUS_PLAYER) adhesion *= CSurfaceTable::GetWetMultiplier(m_aWheelColPoints[frontLine].surfaceB); if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) adhesion *= 0.4f; WheelState[BIKEWHEEL_FRONT] = m_aWheelState[BIKEWHEEL_FRONT]; CVector contactSpeed = GetSpeed(frontContact); ProcessBikeWheel(wheelFwd, wheelRight, contactSpeed, frontContact, 2, fThrust, brake*brakeBiasFront, adhesion*tractionBiasFront, adhesionDestab, BIKEWHEEL_FRONT, &m_aWheelSpeed[BIKEWHEEL_FRONT], &WheelState[BIKEWHEEL_FRONT], spec, m_wheelStatus[BIKEWHEEL_FRONT]); if(bStuckInSand && (WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SPINNING || WheelState[BIKEWHEEL_FRONT] == WHEEL_STATE_SKIDDING)) WheelState[BIKEWHEEL_FRONT] = WHEEL_STATE_NORMAL; }else{ // Wheel in the air m_aWheelSpeed[BIKEWHEEL_FRONT] *= 0.95f; m_aWheelRotation[BIKEWHEEL_FRONT] += m_aWheelSpeed[BIKEWHEEL_FRONT]; } } // Process leaning float idleAngle = 0.0f; if(pDriver){ CAnimBlendAssociation *assoc = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_READY); if(assoc) idleAngle = DEGTORAD(10.0f) * assoc->blendAmount; } if(bBalancedByRider || bIsBeingPickedUp){ m_vecAvgSurfaceRight = CrossProduct(GetForward(), m_vecAvgSurfaceNormal); m_vecAvgSurfaceRight.Normalise(); float lean; if(m_nWheelsOnGround == 0) lean = -(m_fSteerAngle/DEGTORAD(pHandling->fSteeringLock))*0.5f*GRAVITY*CTimer::GetTimeStep(); else lean = DotProduct(m_vecMoveSpeed-initialMoveSpeed, m_vecAvgSurfaceRight); lean /= GRAVITY*Max(CTimer::GetTimeStep(), 0.01f); if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) lean = clamp(lean, -0.4f*pBikeHandling->fMaxLean, 0.4f*pBikeHandling->fMaxLean); else lean = clamp(lean, -pBikeHandling->fMaxLean, pBikeHandling->fMaxLean); float f = Pow(pBikeHandling->fDesLean, CTimer::GetTimeStep()); m_fLeanLRAngle2 = (Asin(lean) - idleAngle)*(1.0f-f) + m_fLeanLRAngle2*f; }else{ if(bIsStanding){ float f = Pow(0.97f, CTimer::GetTimeStep()); m_fLeanLRAngle2 = m_fLeanLRAngle2*f - (Asin(GetRight().z) + DEGTORAD(15.0f) + idleAngle)*(1.0f-f); }else{ float f = Pow(0.95f, CTimer::GetTimeStep()); m_fLeanLRAngle2 = m_fLeanLRAngle2*f; } } m_fLeanLRAngle = m_fLeanLRAngle2; // Destabilize steering when braking if((m_aSuspensionSpringRatio[BIKESUSP_F1] < 1.0f || m_aSuspensionSpringRatio[BIKESUSP_F2] < 1.0f) && m_fBrakePedal - m_fGasPedal > 0.9f && fwdSpeed > 0.02f && !bIsHandbrakeOn){ m_fBrakeDestabilization += CGeneral::GetRandomNumberInRange(0.5f, 1.0f)*0.2f*CTimer::GetTimeStep(); if(m_aSuspensionSpringRatio[BIKESUSP_R1] < 1.0f || m_aSuspensionSpringRatio[BIKESUSP_R2] < 1.0f){ // BUG: this clamp makes no sense and the arguments seem swapped too ApplyTurnForce(contactPoints[BIKESUSP_R1], m_fTurnMass*Sin(m_fBrakeDestabilization)*clamp(fwdSpeed, 0.5f, 0.2f)*0.013f*GetRight()*CTimer::GetTimeStep()); }else{ // BUG: this clamp makes no sense and the arguments seem swapped too ApplyTurnForce(contactPoints[BIKESUSP_R1], m_fTurnMass*Sin(m_fBrakeDestabilization)*clamp(fwdSpeed, 0.5f, 0.2f)*0.003f*GetRight()*CTimer::GetTimeStep()); } }else m_fBrakeDestabilization = 0.0f; // Update wheel positions from suspension float frontWheelPos = colModel->lines[frontLine].p0.z; if(m_aSuspensionSpringRatio[frontLine] > 0.0f) frontWheelPos -= m_aSuspensionSpringRatio[frontLine]*m_aSuspensionSpringLength[frontLine]; m_aWheelPosition[BIKEWHEEL_FRONT] += (frontWheelPos - m_aWheelPosition[BIKEWHEEL_FRONT])*0.75f; float rearWheelPos = colModel->lines[rearLine].p0.z; if(m_aSuspensionSpringRatio[rearLine] > 0.0f) rearWheelPos -= m_aSuspensionSpringRatio[rearLine]*m_aSuspensionSpringLength[rearLine]; m_aWheelPosition[BIKEWHEEL_REAR] += (rearWheelPos - m_aWheelPosition[BIKEWHEEL_REAR])*0.75f; for(i = 0; i < 2; i++) m_aWheelState[i] = WheelState[i]; // never spin when moving backwards if(m_fGasPedal < 0.0f && m_aWheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING) m_aWheelState[BIKEWHEEL_REAR] = WHEEL_STATE_NORMAL; // Process horn if(GetStatus() != STATUS_PLAYER){ #ifdef FIX_BUGS if(!IsAlarmOn()) #endif ReduceHornCounter(); }else{ #ifdef FIX_BUGS if(!IsAlarmOn()) #endif { if(Pads[0].GetHorn()) m_nCarHornTimer = 1; else m_nCarHornTimer = 0; } } } if(m_fHealth < 250.0f && GetStatus() != STATUS_WRECKED){ // Car is on fire CVector damagePos, fireDir; // move fire forward if in first person if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()){ damagePos = CVector(0.0f, 1.2f, -0.4f); fireDir = CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.01125f, 0.09f)); }else{ damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->m_positions[CAR_POS_BACKSEAT]; damagePos.z -= 0.3f; fireDir = CGeneral::GetRandomNumberInRange(0.02025f, 0.09f) * GetRight(); fireDir -= CGeneral::GetRandomNumberInRange(0.02025f, 0.18f) * GetForward(); fireDir.z = CGeneral::GetRandomNumberInRange(0.00225f, 0.018f); } damagePos = GetMatrix()*damagePos; CParticle::AddParticle(PARTICLE_CARFLAME, damagePos, fireDir, nil, 0.9f); CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, CVector(0.0f, 0.0f, 0.0f), nil, 0.5f); damagePos.x += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), damagePos.y += CGeneral::GetRandomNumberInRange(-0.5625f, 0.5625f), damagePos.z += CGeneral::GetRandomNumberInRange(0.5625f, 2.25f); CParticle::AddParticle(PARTICLE_CARFLAME_SMOKE, damagePos, CVector(0.0f, 0.0f, 0.0f)); // Blow up car after 5 seconds m_fFireBlowUpTimer += CTimer::GetTimeStepInMilliseconds(); if(m_fFireBlowUpTimer > 5000.0f) BlowUpCar(m_pSetOnFireEntity); }else m_fFireBlowUpTimer = 0.0f; ProcessDelayedExplosion(); // Find out how much to shake the pad depending on suspension and ground surface float suspShake = 0.0f; float surfShake = 0.0f; float speedsq = m_vecMoveSpeed.MagnitudeSqr(); for(i = 0; i < 4; i++){ float suspChange = m_aSuspensionSpringRatioPrev[i] - m_aSuspensionSpringRatio[i]; if(suspChange > 0.3f && (i == BIKESUSP_F1 || i == BIKESUSP_R1) && speedsq > 0.04f){ if(GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PHYSICS){ #ifdef FIX_BUGS // only two wheels but 4 suspensions if(m_wheelStatus[i/2] == WHEEL_STATUS_BURST) #else if(m_wheelStatus[i] == WHEEL_STATUS_BURST) #endif DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP_2, suspChange); else DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_JUMP, suspChange); if(suspChange > suspShake) suspShake = suspChange; } } if(this == FindPlayerVehicle()){ uint8 surf = m_aWheelColPoints[i].surfaceB; if(surf == SURFACE_GRAVEL || surf == SURFACE_WATER || surf == SURFACE_HEDGE){ if(surfShake < 0.2f) surfShake = 0.3f; }else if(surf == SURFACE_MUD_DRY || surf == SURFACE_SAND || surf == SURFACE_SAND_BEACH){ if(surfShake < 0.1f) surfShake = 0.2f; }else if(surf == SURFACE_GRASS){ if(surfShake < 0.05f) surfShake = 0.1f; } // BUG: this only observes one of the wheels TheCamera.m_bVehicleSuspenHigh = Abs(suspChange) > 0.05f; } m_aSuspensionSpringRatioPrev[i] = m_aSuspensionSpringRatio[i]; m_aSuspensionSpringRatio[i] = 1.0f; } // Shake pad if((suspShake > 0.0f || surfShake > 0.0f) && GetStatus() == 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, 150.0f); CPad::GetPad(0)->StartShake(5000.0f*CTimer::GetTimeStep()/freq, freq); } } } bVehicleColProcessed = false; bAudioChangingGear = false; if(!bWarnedPeds) CCarCtrl::ScanForPedDanger(this); 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 && (acceleration == 0.0f && brake == 0.0f || GetStatus() == 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; } } // Balance bike if(bBalancedByRider || bIsBeingPickedUp || bIsStanding){ float onSideness = DotProduct(GetRight(), m_vecAvgSurfaceNormal); onSideness = clamp(onSideness, -1.0f, 1.0f); CVector worldCOM = Multiply3x3(GetMatrix(), m_vecCentreOfMass); // Keep bike upright if(bBalancedByRider){ ApplyTurnForce(-0.07f*onSideness*m_fTurnMass*GetUp()*CTimer::GetTimeStep(), worldCOM+GetRight()); bIsStanding = false; }else ApplyTurnForce(-0.1f*onSideness*m_fTurnMass*GetUp()*CTimer::GetTimeStep(), worldCOM+GetRight()); // Wheelie/Stoppie stabilization if(GetStatus() == STATUS_PLAYER){ if(m_aWheelTimer[BIKESUSP_F1] == 0.0f && m_aWheelTimer[BIKESUSP_F2] == 0.0f && GetForward().z > 0.0 && !(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f)){ // Wheelie float wheelie = pBikeHandling->fWheelieAng - GetForward().z; if(wheelie > 0.15f) // below wheelie angle wheelie = Max(0.3f - wheelie, 0.0f); else if(wheelie < -0.08f) // above wheelie angle wheelie = Min(-0.15f - wheelie, 0.0f); float wheelieStab = pBikeHandling->fWheelieStabMult * Min(m_vecMoveSpeed.Magnitude(), 0.1f) * wheelie; ApplyTurnForce(0.5f*CTimer::GetTimeStep()*wheelieStab*m_fTurnMass*GetUp(), worldCOM+GetForward()); ApplyTurnForce(0.5f*CTimer::GetTimeStep()*m_fWheelAngle*pBikeHandling->fWheelieSteer*m_fTurnMass*GetRight(), worldCOM+GetForward()); }else if(m_aWheelTimer[BIKESUSP_R1] == 0.0f && m_aWheelTimer[BIKESUSP_R2] == 0.0f && GetForward().z < 0.0 && !(m_aWheelTimer[BIKESUSP_F1] == 0.0f && m_aWheelTimer[BIKESUSP_F2] == 0.0f)){ // Stoppie float stoppie = pBikeHandling->fStoppieAng - GetForward().z; if(stoppie > 0.15f) // below stoppie angle stoppie = Max(0.3f - stoppie, 0.0f); else if(stoppie < -0.15f) // above stoppie angle stoppie = Min(-0.3f - stoppie, 0.0f); float speed = m_vecMoveSpeed.Magnitude(); float stoppieStab = pBikeHandling->fStoppieStabMult * Min(speed, 0.1f) * stoppie; ApplyTurnForce(0.5f*CTimer::GetTimeStep()*stoppieStab*m_fTurnMass*GetUp(), worldCOM+GetForward()); ApplyTurnForce(0.5f*Min(5.0f*speed,1.0f)*CTimer::GetTimeStep()*m_fWheelAngle*pBikeHandling->fWheelieSteer*m_fTurnMass*GetRight(), worldCOM+GetForward()); } } } } #pragma optimize("", on) void CBike::Teleport(CVector pos) { CWorld::Remove(this); SetPosition(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 CBike::PreRender(void) { int i; CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); // Wheel particles if(m_aWheelState[BIKEWHEEL_REAR] != WHEEL_STATE_NORMAL && m_aWheelColPoints[BIKESUSP_R2].surfaceB != SURFACE_WATER && m_aWheelTimer[BIKESUSP_R2] > 0.0f){ static float smokeSize = 0.2f; CVector groundPos = m_aWheelColPoints[BIKESUSP_R2].point; if(m_aSuspensionSpringRatioPrev[BIKESUSP_R1] < 1.0f) groundPos = (groundPos + m_aWheelColPoints[BIKESUSP_R1].point)/2.0f; groundPos += Sin(m_fLeanLRAngle) * 0.8f*GetColModel()->boundingBox.min.z * GetRight(); CParticle::AddParticle(PARTICLE_RUBBER_SMOKE, groundPos + CVector(0.0f, 0.0f, 0.25f), CVector(0.0f, 0.0f, 0.0f), nil, smokeSize); CSkidmarks::RegisterOne((uintptr)this, groundPos, GetForward().x, GetForward().y, m_aWheelSkidmarkType[BIKEWHEEL_REAR], &m_aWheelSkidmarkBloody[BIKEWHEEL_REAR]); if(m_aWheelState[BIKEWHEEL_REAR] == WHEEL_STATE_SPINNING && (CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[BIKESUSP_R2].surfaceB) == ADHESIVE_HARD || CSurfaceTable::GetAdhesionGroup(m_aWheelColPoints[BIKESUSP_R2].surfaceB) == ADHESIVE_ROAD)){ CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, groundPos + CVector(0.0f, 0.0f, 0.25f), CVector(0.0f, 0.0f, 0.0f)); CParticle::AddParticle(PARTICLE_BURNINGRUBBER_SMOKE, groundPos + CVector(0.0f, 0.0f, 0.25f), CVector(0.0f, 0.0f, 0.05f)); } }else if(m_aWheelSkidmarkBloody[BIKEWHEEL_REAR] || m_aWheelSkidmarkUnk[BIKEWHEEL_REAR]){ CVector groundPos = m_aWheelColPoints[BIKESUSP_R2].point; groundPos += Sin(m_fLeanLRAngle) * 0.8f*GetColModel()->boundingBox.min.z * GetRight(); CSkidmarks::RegisterOne((uintptr)this, groundPos, GetForward().x, GetForward().y, m_aWheelSkidmarkType[BIKEWHEEL_REAR], &m_aWheelSkidmarkBloody[BIKEWHEEL_REAR]); } // Process lights // 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 && GetStatus() != STATUS_WRECKED){ if(GetStatus() == 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){ CalculateLeanMatrix(); 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 float behindness = DotProduct(lookVector, GetForward()); // 0.0 if behind car, PI if in front float angle = Abs(Acos(Abs(behindness))); // Headlight CMatrix mat; CVector headLightPos = mi->m_positions[CAR_POS_HEADLIGHTS]; if(GetModelIndex() == 152){ // this is the bobcat in VC, but we don't want that effect anyway mat.SetUnity(); mat.RotateZ(m_fWheelAngle); mat = m_leanMatrix * mat; }else mat = m_leanMatrix; CVector light = mat * headLightPos; if(behindness < 0.0f){ // In front of bike 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){ CCoronas::RegisterCorona((uintptr)this + 6, 150, 150, 195, 255, light, 1.2f, 45.0f*TheCamera.LODDistMultiplier, CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); }else{ CCoronas::RegisterCorona((uintptr)this + 6, 160, 160, 140, 255, light, 1.2f, 45.0f*TheCamera.LODDistMultiplier, CCoronas::TYPE_HEADLIGHT, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, angle); } } if(alarmOff){ CCoronas::RegisterCorona((uintptr)this, 0, 0, 0, 0, light, 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){ CCoronas::RegisterCorona((uintptr)this + 1, 190*intensity, 190*intensity, 255*intensity, 255, light, size, 50.0f*TheCamera.LODDistMultiplier, CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); }else{ CCoronas::RegisterCorona((uintptr)this + 1, 210*intensity, 210*intensity, 195*intensity, 255, light, size, 50.0f*TheCamera.LODDistMultiplier, CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); } } }else{ CCoronas::UpdateCoronaCoors((uintptr)this, light, 50.0f*TheCamera.LODDistMultiplier, angle); } // bright light CBrightLights::RegisterOne(light, GetUp(), GetRight(), GetForward(), pHandling->FrontLights + BRIGHTLIGHT_FRONT); // Taillight CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; light = m_leanMatrix * tailLightPos; // Taillight corona if(behindness > 0.0f){ // Behind car float intensity = 0.4f*behindness + 0.4f; float size = (behindness + 1.0f)/2.0f; if(m_fGasPedal < 0.0f){ // reversing // no lights in this case }else{ if(m_fBrakePedal > 0.0f){ intensity += 0.4f; size += 0.3f; } if(alarmOff){ CCoronas::RegisterCorona((uintptr)this + 14, 0, 0, 0, 0, light, size, 0.0f, CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); }else{ CCoronas::RegisterCorona((uintptr)this + 14, 128*intensity, 0, 0, 255, light, size, 50.0f*TheCamera.LODDistMultiplier, CCoronas::TYPE_STREAK, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, angle); } } }else{ CCoronas::UpdateCoronaCoors((uintptr)this + 14, light, 50.0f*TheCamera.LODDistMultiplier, angle); } // bright light CBrightLights::RegisterOne(light, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); // 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); CShadows::StoreCarLightShadow(this, (uintptr)this + 22, gpShadowExplosionTex, &pos, 7.0f*fwd.x, 7.0f*fwd.y, 3.5f*fwd.y, -3.5f*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); 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){ 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(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 if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED){ // Lights off CalculateLeanMatrix(); CVector tailLightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; CVector light = m_leanMatrix * tailLightPos; 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 // no lights in this case }else{ // braking CCoronas::RegisterCorona((uintptr)this + 14, 120, 0, 0, 255, light, 1.2f, 50.0f*TheCamera.LODDistMultiplier, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); CBrightLights::RegisterOne(light, GetUp(), GetRight(), GetForward(), pHandling->RearLights + BRIGHTLIGHT_REAR); } }else{ CCoronas::UpdateCoronaCoors((uintptr)this + 14, light, 50.0f*TheCamera.LODDistMultiplier, 0.0f); } }else{ CCoronas::UpdateCoronaCoors((uintptr)this + 14, light, 50.0f*TheCamera.LODDistMultiplier, 0.0f); } } // Wheel particles float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward())*180.0f; int drawParticles = Abs(fwdSpeed) < 90.0f; int susp = BIKESUSP_F1; for(i = 0; i < 2; i++){ if(i == BIKEWHEEL_REAR) susp = BIKESUSP_R1; static float speedSq; // Sparks for friction of burst wheels if(m_wheelStatus[i] == WHEEL_STATUS_BURST && m_aSuspensionSpringRatioPrev[susp] < 1.0f && (speedSq = m_vecMoveSpeed.MagnitudeSqr(), speedSq > SQR(0.1f)) && m_aWheelColPoints[susp].surfaceB != SURFACE_GRASS && m_aWheelColPoints[susp].surfaceB != SURFACE_MUD_DRY && m_aWheelColPoints[susp].surfaceB != SURFACE_SAND && m_aWheelColPoints[susp].surfaceB != SURFACE_SAND_BEACH){ CVector normalSpeed = m_aWheelColPoints[susp].normal * DotProduct(m_aWheelColPoints[susp].normal, m_vecMoveSpeed); CVector frictionSpeed = m_vecMoveSpeed - normalSpeed; CVector sparkDir = 0.25f*frictionSpeed; CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); if(speedSq > 0.04f) CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); if(speedSq > 0.16f){ CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); CParticle::AddParticle(PARTICLE_SPARK_SMALL, m_aWheelColPoints[susp].point, sparkDir); } }else if(m_aSuspensionSpringRatioPrev[i] < 1.0f && (fwdSpeed > 0.2f || m_aWheelState[i] == WHEEL_STATE_SPINNING)){ if(m_aWheelColPoints[susp].surfaceB == SURFACE_GRASS || m_aWheelColPoints[susp].surfaceB == SURFACE_MUD_DRY || m_aWheelColPoints[susp].surfaceB == SURFACE_SAND || m_aWheelColPoints[susp].surfaceB == SURFACE_SAND_BEACH) AddWheelDirtAndWater(&m_aWheelColPoints[susp], drawParticles); } } AddDamagedVehicleParticles(); CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_BIKE); CMatrix mat; CVector pos; CColModel *colModel = mi->GetColModel(); // Wheel rotation CVector frontWheelFwd = Multiply3x3(GetMatrix(), CVector(-Sin(m_fSteerAngle), Cos(m_fSteerAngle), 0.0f)); CVector rearWheelFwd = GetForward(); if(m_aWheelTimer[BIKESUSP_F1] > 0.0f || m_aWheelTimer[BIKESUSP_F2] > 0.0f){ float springRatio = Min(m_aSuspensionSpringRatioPrev[BIKESUSP_F1], m_aSuspensionSpringRatioPrev[BIKESUSP_F2]); CVector contactPoint(0.0f, (colModel->lines[BIKESUSP_F1].p0.y - colModel->lines[BIKESUSP_F2].p0.y)/2.0f, colModel->lines[BIKESUSP_F1].p0.z - m_aSuspensionSpringLength[BIKESUSP_F1]*springRatio - 0.5f*mi->m_wheelScale); CVector contactSpeed = GetSpeed(contactPoint); // Why is wheel state always normal? m_aWheelSpeed[BIKEWHEEL_FRONT] = ProcessWheelRotation(WHEEL_STATE_NORMAL, frontWheelFwd, contactSpeed, 0.5f*mi->m_wheelScale); m_aWheelRotation[BIKEWHEEL_FRONT] += m_aWheelSpeed[BIKEWHEEL_FRONT]; } if(m_aWheelTimer[BIKESUSP_R1] > 0.0f || m_aWheelTimer[BIKESUSP_R2] > 0.0f){ float springRatio = Min(m_aSuspensionSpringRatioPrev[BIKESUSP_R1], m_aSuspensionSpringRatioPrev[BIKESUSP_R2]); CVector contactPoint(0.0f, (colModel->lines[BIKESUSP_R1].p0.y - colModel->lines[BIKESUSP_R2].p0.y)/2.0f, colModel->lines[BIKESUSP_R1].p0.z - m_aSuspensionSpringLength[BIKESUSP_R1]*springRatio - 0.5f*mi->m_wheelScale); CVector contactSpeed = GetSpeed(contactPoint); m_aWheelSpeed[BIKEWHEEL_REAR] = ProcessWheelRotation(m_aWheelState[BIKEWHEEL_REAR], rearWheelFwd, contactSpeed, 0.5f*mi->m_wheelScale); m_aWheelRotation[BIKEWHEEL_REAR] += m_aWheelSpeed[BIKEWHEEL_REAR]; } // Front fork if(m_aBikeNodes[BIKE_FORKS_FRONT]){ mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_FORKS_FRONT])); pos = mat.GetPosition(); RwMatrix rwrot; // TODO: this looks like some weird ctor we don't have CMatrix rot; rot.m_attachment = &rwrot; rot.SetUnity(); rot.UpdateRW(); // Make rotation matrix with front fork as axis CVector forkAxis(0.0f, Sin(DEGTORAD(mi->m_bikeSteerAngle)), -Cos(DEGTORAD(mi->m_bikeSteerAngle))); forkAxis.Normalise(); // as if that's not already the case CQuaternion quat; quat.Set(&forkAxis, -m_fWheelAngle); quat.Get(rot.m_attachment); rot.Update(); // Transform fork mat.SetUnity(); mat = mat * rot; mat.Translate(pos); mat.UpdateRW(); if(m_aBikeNodes[BIKE_HANDLEBARS]){ // Transform handle mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_HANDLEBARS])); pos = mat.GetPosition(); if(GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED){ mat.SetUnity(); mat = mat * rot; mat.Translate(pos); }else mat.SetTranslate(mat.GetPosition()); mat.UpdateRW(); } } // Rear fork if(m_aBikeNodes[BIKE_FORKS_REAR]){ float sine = (m_aWheelPosition[BIKEWHEEL_REAR] - m_aWheelBasePosition[BIKEWHEEL_REAR])/m_fRearForkLength; mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_FORKS_REAR])); pos = mat.GetPosition(); mat.SetRotate(-Asin(sine), 0.0f, 0.0f); mat.Translate(pos); mat.UpdateRW(); } // Front wheel mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_WHEEL_FRONT])); pos.x = mat.GetPosition().x; pos.z = m_aWheelPosition[BIKEWHEEL_FRONT] - m_fFrontForkZ; float y = (colModel->lines[BIKESUSP_F1].p0.y+colModel->lines[BIKESUSP_F2].p0.y)/2.0f - m_fFrontForkY; pos.y = y - (m_aWheelPosition[BIKEWHEEL_FRONT] - m_aWheelBasePosition[BIKEWHEEL_FRONT])*m_fFrontForkSlope; if(m_wheelStatus[BIKEWHEEL_FRONT] == WHEEL_STATUS_BURST) mat.SetRotate(m_aWheelRotation[BIKEWHEEL_FRONT], 0.0f, 0.05f*Sin(m_aWheelRotation[BIKEWHEEL_FRONT])); else mat.SetRotateX(m_aWheelRotation[BIKEWHEEL_FRONT]); mat.Translate(pos); mat.UpdateRW(); // and mudguard mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_MUDGUARD])); mat.SetTranslateOnly(pos); mat.UpdateRW(); // Rear wheel mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_WHEEL_REAR])); pos = mat.GetPosition(); if(m_wheelStatus[BIKEWHEEL_REAR] == WHEEL_STATUS_BURST) mat.SetRotate(m_aWheelRotation[BIKEWHEEL_REAR], 0.0f, 0.07f*Sin(m_aWheelRotation[BIKEWHEEL_REAR])); else mat.SetRotateX(m_aWheelRotation[BIKEWHEEL_REAR]); mat.Translate(pos); mat.UpdateRW(); // Chassis if(m_aBikeNodes[BIKE_CHASSIS]){ mat.Attach(RwFrameGetMatrix(m_aBikeNodes[BIKE_CHASSIS])); pos = mat.GetPosition(); pos.z = (1.0f - Cos(m_fLeanLRAngle)) * (0.9*colModel->boundingBox.min.z); mat.SetRotateX(-0.05f*Abs(m_fLeanLRAngle)); mat.RotateY(m_fLeanLRAngle); mat.Translate(pos); mat.UpdateRW(); } // Exhaust smoke if(bEngineOn && !(pHandling->Flags & HANDLING_NO_EXHAUST) && fwdSpeed < 130.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; } static float fumesLimit = 2.0f; if(CGeneral::GetRandomNumberInRange(1.0f, 3.0f)*(m_fGasPedal+1.1f) > fumesLimit){ CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos1, dir); if(dblExhaust) CParticle::AddParticle(PARTICLE_EXHAUST_FUMES, pos2, dir); if(GetStatus() == STATUS_PLAYER && (CTimer::GetFrameCounter()&3) == 0 && CWeather::Rain == 0.0f){ CVector camDist = GetPosition() - TheCamera.GetPosition(); if(DotProduct(GetForward(), camDist) > 0.0f || TheCamera.GetLookDirection() == LOOKING_LEFT || TheCamera.GetLookDirection() == LOOKING_RIGHT){ if(dblExhaust) pos1 = 0.5f*pos1 + 0.5f*pos2; if(TheCamera.GetLookDirection() == LOOKING_LEFT || TheCamera.GetLookDirection() == LOOKING_RIGHT) pos1 -= 0.2f*GetForward(); CParticle::AddParticle(PARTICLE_HEATHAZE, pos1, CVector(0.0f, 0.0f, 0.0f)); } } } } } } void CBike::Render(void) { CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 3000; mi->SetVehicleColour(m_currentColour1, m_currentColour2); CEntity::Render(); } int32 CBike::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints) { int i; CColModel *colModel; if(GetStatus() != STATUS_SIMPLE) bVehicleColProcessed = true; 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]; if(m_bIsVehicleBeingShifted || bSkipLineCol || ent->IsPed() || GetModelIndex() == MI_DODO && ent->IsVehicle()) colModel->numLines = 0; 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(colModel->numLines){ 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(); } m_nSurfaceTouched = m_aWheelColPoints[i].surfaceB; if(ent->IsBuilding()) m_pCurGroundEntity = ent; } } }else colModel->numLines = 4; 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 CBike::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 #ifdef FIX_BUGS if(CCamera::m_bUseMouse3rdPerson && !CVehicle::m_bDisableMouseSteering){ if(CPad::GetPad(pad)->GetMouseX() != 0.0f){ m_fSteerInput += fMouseSteerSens*CPad::GetPad(pad)->GetMouseX(); nLastControlInput = 2; if(Abs(m_fSteerInput) < fMouseCentreRange) m_fSteerInput *= Pow(fMouseCentreMult, CTimer::GetTimeStep()); }else if(CPad::GetPad(pad)->GetSteeringLeftRight() || nLastControlInput != 2){ // mouse hasn't move, steer with pad like below m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* 0.2f*CTimer::GetTimeStep(); nLastControlInput = 0; } }else #endif { m_fSteerInput += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteerInput)* 0.2f*CTimer::GetTimeStep(); nLastControlInput = 0; } m_fSteerInput = clamp(m_fSteerInput, -1.0f, 1.0f); // Lean forward/backward float updown; #ifdef FREE_CAM if (CCamera::bFreeCam) updown = CPad::IsAffectedByController ? -CPad::GetPad(pad)->GetSteeringUpDown()/128.0f : CPad::GetPad(pad)->GetCarGunUpDown()/128.0f; else #endif updown = -CPad::GetPad(pad)->GetSteeringUpDown()/128.0f + CPad::GetPad(pad)->GetCarGunUpDown()/128.0f; m_fLeanInput += (updown - m_fLeanInput)*0.2f*CTimer::GetTimeStep(); m_fLeanInput = clamp(m_fLeanInput, -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 if(CPad::GetPad(pad)->GetAccelerate() > 150.0f && CPad::GetPad(pad)->GetBrake() > 150.0f){ m_fGasPedal = CPad::GetPad(pad)->GetAccelerate()/255.0f; m_fBrakePedal = CPad::GetPad(pad)->GetBrake()/255.0f; m_doingBurnout = 1; }else{ 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_fSteerInput < 0.0f) fValue = -sq(m_fSteerInput); else fValue = sq(m_fSteerInput); m_fSteerAngle = DEGTORAD(pHandling->fSteeringLock) * fValue; if(bComedyControls){ if(((CTimer::GetTimeInMilliseconds() >> 10) & 0xF) < 12) m_fGasPedal = 1.0f; if((((CTimer::GetTimeInMilliseconds() >> 10)+6) & 0xF) < 12) m_fBrakePedal = 0.0f; bIsHandbrakeOn = false; if(CTimer::GetTimeInMilliseconds() & 0x800) m_fSteerAngle += 0.08f; else m_fSteerAngle -= 0.03f; } // Brake if player isn't in control // BUG: game always uses pad 0 here if(CPad::GetPad(pad)->ArePlayerControlsDisabled()){ 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 CBike::ProcessBuoyancy(void) { int i; CVector impulse, point; if(mod_Buoyancy.ProcessBuoyancy(this, m_fBuoyancy, &point, &impulse)){ bTouchingWater = true; ApplyMoveForce(impulse); ApplyTurnForce(impulse, point); 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.8f || impulseRatio > 0.4f && (m_aSuspensionSpringRatio[0] == 1.0f || m_aSuspensionSpringRatio[1] == 1.0f || m_aSuspensionSpringRatio[2] == 1.0f || m_aSuspensionSpringRatio[3] == 1.0f)){ bIsInWater = true; bIsDrowning = true; if(m_vecMoveSpeed.z < -0.1f) m_vecMoveSpeed.z = -0.1f; if(pDriver){ pDriver->bIsInWater = true; if(pDriver->IsPlayer() || !bWaterTight){ if(m_aSuspensionSpringRatio[0] < 1.0f || m_aSuspensionSpringRatio[1] < 1.0f || m_aSuspensionSpringRatio[2] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f) pDriver->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); else KnockOffRider(WEAPONTYPE_DROWNING, 0, pDriver, false); } } for(i = 0; i < m_nNumMaxPassengers; i++) if(pPassengers[i]){ pPassengers[i]->bIsInWater = true; if(pPassengers[i]->IsPlayer() || !bWaterTight){ if(m_aSuspensionSpringRatio[0] < 1.0f || m_aSuspensionSpringRatio[1] < 1.0f || m_aSuspensionSpringRatio[2] < 1.0f || m_aSuspensionSpringRatio[3] < 1.0f) pPassengers[i]->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); else KnockOffRider(WEAPONTYPE_DROWNING, 0, pPassengers[i], false); } } }else{ bIsInWater = false; bIsDrowning = false; } }else{ bIsInWater = false; bIsDrowning = false; bTouchingWater = false; } } void CBike::DoDriveByShootings(void) { CAnimBlendAssociation *anim; CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); if (playerInfo && !playerInfo->m_bDriveByAllowed) return; CWeapon *weapon = pDriver->GetWeapon(); if(CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->m_nWeaponSlot != 5) return; weapon->Update(pDriver->m_audioEntityId, nil); bool lookingLeft = false; bool lookingRight = false; if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || TheCamera.m_bObbeCinematicCarCamOn){ 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 || CPad::GetPad(0)->GetCarGunFired()){ if(lookingLeft){ anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); if(anim == nil || anim->blendDelta < 0.0f) anim = CAnimManager::AddAnimation(pDriver->GetClump(), m_bikeAnimType, ANIM_BIKE_DRIVEBY_LHS); }else if(lookingRight){ anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); if(anim == nil || anim->blendDelta < 0.0f) anim = CAnimManager::AddAnimation(pDriver->GetClump(), m_bikeAnimType, ANIM_BIKE_DRIVEBY_RHS); }else{ anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); if(anim == nil || anim->blendDelta < 0.0f) anim = CAnimManager::AddAnimation(pDriver->GetClump(), m_bikeAnimType, ANIM_BIKE_DRIVEBY_FORWARD); } if (!anim || !anim->IsRunning()) { if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { weapon->FireFromCar(this, lookingLeft, lookingRight); weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; } } }else{ weapon->Reload(); anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_LHS); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_RHS); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_BIKE_DRIVEBY_FORWARD); if(anim) anim->blendDelta = -1000.0f; } } void CBike::VehicleDamage(void) { float impulse = m_fDamageImpulse; float colSpeed = 800.0f*impulse/m_fMass; if(GetStatus() == STATUS_PLAYER) colSpeed *= 0.65f; else if(VehicleCreatedBy == MISSION_VEHICLE) colSpeed *= 0.4f; if(!bCanBeDamaged) return; if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) impulse *= 0.5f; if(bIsStanding && impulse > 20.0f) bIsStanding = false; // Inflict damage on the driver and passenger if(pDriver && pDriver->GetPedState() == PED_DRIVING && colSpeed > 10.0f){ float fwd = 0.6f; if(Abs(DotProduct(m_vecDamageNormal, GetForward())) > 0.85f){ float u = Max(DotProduct(m_vecDamageNormal, CVector(0.0f, 0.0f, 1.0f)), 0.0f); if(u < 0.85f) u = 0.0f; fwd += 7.0f * SQR(u); } float up = 0.05f; if(GetModelIndex() == MI_SANCHEZ){ fwd *= 0.65f; up *= 0.75f; } float total = fwd*Abs(DotProduct(m_vecDamageNormal, GetForward())) + 0.45f*Abs(DotProduct(m_vecDamageNormal, GetRight())) + up*Max(DotProduct(m_vecDamageNormal, GetUp()), 0.0f); float damage = (total - 1.5f*Min(DotProduct(m_vecDamageNormal, GetUp()), 0.0f))*colSpeed; if(pDriver->IsPlayer() && CCullZones::CamStairsForPlayer() && CCullZones::FindZoneWithStairsAttributeForPlayer()) damage = 0.0f; if(damage > 75.0f){ int dir = -10; if(pDriver){ dir = pDriver->GetLocalDirection(-m_vecDamageNormal); if(pDriver->m_fHealth > 0.0f) pDriver->InflictDamage(m_pDamageEntity, WEAPONTYPE_RAMMEDBYCAR, 0.05f*damage, PEDPIECE_TORSO, dir); if(pDriver && pDriver->GetPedState() == PED_DRIVING) KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, dir, pDriver, false); } if(pPassengers[0]){ dir = pPassengers[0]->GetLocalDirection(-m_vecDamageNormal); if(pPassengers[0]->m_fHealth > 0.0f) pPassengers[0]->InflictDamage(m_pDamageEntity, WEAPONTYPE_RAMMEDBYCAR, 0.05f*damage, PEDPIECE_TORSO, dir); if(pPassengers[0] && pPassengers[0]->GetPedState() == PED_DRIVING) KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, dir, pPassengers[0], false); } } } if(impulse > 25.0f && GetStatus() != STATUS_WRECKED){ float damage = (impulse-25.0f)*pHandling->fCollisionDamageMultiplier; if(damage > 0.0f){ if(damage > 5.0f && pDriver && m_pDamageEntity && m_pDamageEntity->IsVehicle() && (this != FindPlayerVehicle() || ((CVehicle*)m_pDamageEntity)->VehicleCreatedBy == MISSION_VEHICLE) && ((CVehicle*)m_pDamageEntity)->pDriver) pDriver->Say(SOUND_PED_CRASH_VEHICLE); int oldHealth = m_fHealth; if(this == FindPlayerVehicle()) m_fHealth -= bTakeLessDamage ? damage/6.0f : damage/2.0f; else if(bTakeLessDamage) m_fHealth -= damage/12.0f; else if(m_pDamageEntity && m_pDamageEntity == FindPlayerVehicle()) m_fHealth -= damage/1.5f; else m_fHealth -= damage/4.0f; if(m_fHealth <= 0.0f && oldHealth > 0) m_fHealth = 1.0f; } } if(m_fHealth < 250.0f){ // Car is on fire if(!bIsOnFire){ // Set engine on fire and remember who did this bIsOnFire = true; m_fFireBlowUpTimer = 0.0f; m_pSetOnFireEntity = m_pDamageEntity; if(m_pSetOnFireEntity) m_pSetOnFireEntity->RegisterReference(&m_pSetOnFireEntity); } } } void CBike::AddDamagedVehicleParticles(void) { if(this == FindPlayerVehicle() && TheCamera.GetLookingForwardFirstPerson()) return; if(this != FindPlayerVehicle() && (CTimer::GetFrameCounter() + m_randomSeed) & 1) return; if(m_fHealth >= 650.0f) return; CVector direction = 0.5f*m_vecMoveSpeed; CVector damagePos = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->GetFrontSeatPosn(); damagePos.z -= 0.4f; damagePos = GetMatrix()*damagePos; CalculateLeanMatrix(); if(m_fHealth < 250.0f){ // fire, done in processControl }else if(m_fHealth < 320.0f){ direction *= 0.2f; CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, damagePos, direction + 0.02f*m_leanMatrix.GetRight()); }else if(m_fHealth < 390.0f){ if(((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 0 || ((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 2) CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction + 0.05f*m_leanMatrix.GetRight()); direction *= 0.3f; CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, damagePos, direction + 0.04f*m_leanMatrix.GetRight()); }else if(m_fHealth < 460.0f){ int rnd = CTimer::GetFrameCounter() + m_randomSeed; if(rnd < 10 || rnd < 70 && rnd > 25 || rnd < 160 && rnd > 100 || rnd < 200 && rnd > 175 || rnd > 235) return; direction.z += 0.05f; if(TheCamera.GetLookDirection() != LOOKING_FORWARD){ CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction + 0.08f*m_leanMatrix.GetRight(), nil, 0.1f, 0, 0, 0, 1000); }else if(((CTimer::GetFrameCounter() + m_randomSeed) & 1) == 0){ direction = 0.8f*m_vecMoveSpeed; CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos, direction + 0.07f*m_leanMatrix.GetRight(), nil, 0.1f, 0, 0, 0, 1000); } }else if(((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 0 || ((CTimer::GetFrameCounter() + m_randomSeed) & 3) == 2){ CParticle::AddParticle(PARTICLE_ENGINE_STEAM, damagePos + 0.06f*m_leanMatrix.GetRight(), direction); } } int32 CBike::AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed) { int i; CVector dir; static float minSize = 0.02f; static float maxSize = 0.04f; static RwRGBA grassCol = { 8, 24, 8, 255 }; static RwRGBA gravelCol = { 64, 64, 64, 255 }; static RwRGBA mudCol = { 64, 32, 16, 255 }; static RwRGBA sandCol = { 170, 165, 140, 255 }; static RwRGBA waterCol = { 48, 48, 64, 0 }; if(!belowEffectSpeed && colpoint->surfaceB != SURFACE_SAND && colpoint->surfaceB != SURFACE_SAND_BEACH) 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.04f); CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, CGeneral::GetRandomNumberInRange(minSize, maxSize), grassCol); } return 0; case SURFACE_GRAVEL: 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.04f); CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, CGeneral::GetRandomNumberInRange(minSize, maxSize), gravelCol); } return 1; case SURFACE_MUD_DRY: 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.04f); CParticle::AddParticle(PARTICLE_WHEEL_DIRT, colpoint->point, dir, nil, CGeneral::GetRandomNumberInRange(minSize, maxSize), mudCol); } return 0; case SURFACE_SAND: case SURFACE_SAND_BEACH: if(CTimer::GetFrameCounter() & 2) return 0; dir.x = 0.75f*m_vecMoveSpeed.x; dir.y = 0.75f*m_vecMoveSpeed.y; for(i = 0; i < 1; i++){ dir.z = CGeneral::GetRandomNumberInRange(0.02f, 0.055f); CParticle::AddParticle(PARTICLE_SAND, colpoint->point, dir, nil, 0.8f*m_vecMoveSpeed.Magnitude(), sandCol); } return 0; default: if(CWeather::WetRoads > 0.01f){ CParticle::AddParticle( PARTICLE_WATERSPRAY, colpoint->point + CVector(0.0f, 0.0f, 0.25f+0.25f), CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(0.005f, 0.04f)), nil, CGeneral::GetRandomNumberInRange(0.1f, 0.5f), waterCol); return 0; } return 1; } return 0; } void CBike::GetComponentWorldPosition(int32 component, CVector &pos) { if(m_aBikeNodes[component] == nil){ printf("BikeNode missing: %d %d\n", GetModelIndex(), component); return; } RwMatrix *ltm = RwFrameGetLTM(m_aBikeNodes[component]); pos = *RwMatrixGetPos(ltm); } bool CBike::IsComponentPresent(int32 component) { return m_aBikeNodes[component] != nil; } void CBike::SetComponentRotation(int32 component, CVector rotation) { CMatrix mat(RwFrameGetMatrix(m_aBikeNodes[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(); } bool CBike::IsDoorReady(eDoors door) { return true; } bool CBike::IsDoorFullyOpen(eDoors door) { return false; } bool CBike::IsDoorClosed(eDoors door) { return false; } bool CBike::IsDoorMissing(eDoors door) { return true; } void CBike::RemoveRefsToVehicle(CEntity *ent) { int i; for(i = 0; i < 4; i++) if(m_aGroundPhysical[i] == ent) m_aGroundPhysical[i] = nil; } void CBike::BlowUpCar(CEntity *culprit) { if(!bCanBeDamaged) return; #ifdef FIX_BUGS // taken from CAutomobile. maybe tweak values? if(culprit == FindPlayerPed() || culprit == FindPlayerVehicle()){ CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 20; CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 10.0f; CStats::PropertyDestroyed += CGeneral::GetRandomNumber()%6000 + 4000; } #endif // explosion pushes vehicle up m_vecMoveSpeed.z += 0.13f; SetStatus(STATUS_WRECKED); bRenderScorched = true; m_fHealth = 0.0f; m_nBombTimer = 0; TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z); KillPedsInVehicle(); bEngineOn = false; bLightsOn = false; ChangeLawEnforcerState(false); CExplosion::AddExplosion(this, culprit, EXPLOSION_CAR, GetPosition(), 0); CDarkel::RegisterCarBlownUpByPlayer(this); } bool CBike::SetUpWheelColModel(CColModel *colModel) { RwMatrix *mat = RwMatrixCreate(); CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); CColModel *vehColModel = mi->GetColModel(); colModel->boundingSphere = vehColModel->boundingSphere; colModel->boundingBox = vehColModel->boundingBox; GetRelativeMatrix(mat, m_aBikeNodes[BIKE_WHEEL_FRONT], m_aBikeNodes[BIKE_CHASSIS]); colModel->spheres[0].Set(0.5f*mi->m_wheelScale, *RwMatrixGetPos(mat), SURFACE_RUBBER, CAR_PIECE_WHEEL_LF); GetRelativeMatrix(mat, m_aBikeNodes[BIKE_WHEEL_REAR], m_aBikeNodes[BIKE_CHASSIS]); colModel->spheres[1].Set(0.5f*mi->m_wheelScale, *RwMatrixGetPos(mat), SURFACE_RUBBER, CAR_PIECE_WHEEL_LR); colModel->numSpheres = 2; #ifdef FIX_BUGS RwMatrixDestroy(mat); #endif return true; } float fBikeBurstForceMult = 0.02f; float fBikeBurstFallSpeed = 0.3f; float fBikeBurstFallSpeedPlayer = 0.55f; void CBike::BurstTyre(uint8 wheel, bool applyForces) { if(bTyresDontBurst) return; switch(wheel){ case CAR_PIECE_WHEEL_LF: wheel = BIKEWHEEL_FRONT; break; case CAR_PIECE_WHEEL_LR: wheel = BIKEWHEEL_REAR; break; } if(m_wheelStatus[wheel] == WHEEL_STATUS_OK){ m_wheelStatus[wheel] = WHEEL_STATUS_BURST; #ifdef FIX_BUGS CStats::TyresPopped++; #endif DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_TYRE_POP, 0.0f); if(GetStatus() == STATUS_SIMPLE){ SetStatus(STATUS_PHYSICS); CCarCtrl::SwitchVehicleToRealPhysics(this); } if(applyForces){ ApplyMoveForce(GetRight() * m_fMass * CGeneral::GetRandomNumberInRange(-0.02f, 0.02f)); ApplyTurnForce(GetRight() * m_fTurnMass * CGeneral::GetRandomNumberInRange(-0.02f, 0.02f), GetForward()); } // This code checks piece types originally so it is never triggered // as we have converted them to wheel indices above already. if(pDriver){ #ifdef FIX_SIGNIFICANT_BUGS if(wheel == BIKEWHEEL_FRONT && (m_aSuspensionSpringRatioPrev[BIKESUSP_F1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_F2] < 1.0f) || wheel == BIKEWHEEL_REAR && (m_aSuspensionSpringRatioPrev[BIKESUSP_R1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_R2] < 1.0f)){ #else if(wheel == CAR_PIECE_WHEEL_LF && (m_aSuspensionSpringRatioPrev[BIKESUSP_F1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_F2] < 1.0f) || wheel == CAR_PIECE_WHEEL_LR && (m_aSuspensionSpringRatioPrev[BIKESUSP_R1] < 1.0f || m_aSuspensionSpringRatioPrev[BIKESUSP_R2] < 1.0f)){ #endif float speedSq = m_vecMoveSpeed.MagnitudeSqr(); if(speedSq > fBikeBurstFallSpeed && (GetStatus() != STATUS_PLAYER || speedSq > fBikeBurstFallSpeedPlayer)){ #ifdef FIX_SIGNIFICANT_BUGS if(wheel == BIKEWHEEL_FRONT){ #else if(wheel == CAR_PIECE_WHEEL_LF){ #endif KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, 0, pDriver, false); if(pPassengers[0]) KnockOffRider(WEAPONTYPE_RAMMEDBYCAR, 0, pPassengers[0], false); }else ApplyTurnForce(2.0f*fBikeBurstForceMult*m_fTurnMass*GetRight(), GetForward()); } } } } } bool CBike::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 = mi->GetFrontSeatPosn(); if(component == CAR_DOOR_RR || component == CAR_DOOR_LR) seatPos = mi->m_positions[CAR_POS_BACKSEAT]; if(component == CAR_DOOR_LF || component == CAR_DOOR_LR) seatPos.x = -seatPos.x; 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; // Removing that makes thiProcessEntityCollisions func. return false for van doors. doorPos.z += 0.5f; 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 CBike::GetHeightAboveRoad(void) { return m_fHeightAboveRoad; } void CBike::PlayCarHorn(void) { int r; if (IsAlarmOn() || m_nCarHornTimer != 0) return; if (m_nCarHornDelay) { m_nCarHornDelay--; return; } m_nCarHornDelay = (CGeneral::GetRandomNumber() & 0x7F) + 150; r = m_nCarHornDelay & 7; if(r < 2){ m_nCarHornTimer = 45; }else if(r < 4){ if(pDriver) pDriver->Say(SOUND_PED_ANNOYED_DRIVER); m_nCarHornTimer = 45; }else{ if(pDriver) pDriver->Say(SOUND_PED_ANNOYED_DRIVER); } } void CBike::KnockOffRider(eWeaponType weapon, uint8 direction, CPed *ped, bool bGetBackOn) { AnimationId anim = ANIM_STD_KO_FRONT; if(ped == nil) return; if(!ped->IsPlayer()){ if(bGetBackOn){ if(ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && FindPlayerPed()->m_carInObjective == ped->m_pMyVehicle && !CTheScripts::IsPlayerOnAMission()) ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); else if(ped->m_pedStats->m_temper > ped->m_pedStats->m_fear && ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && !CTheScripts::IsPlayerOnAMission()) ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, ped->m_pMyVehicle); else if(ped->m_pedStats->m_temper <= ped->m_pedStats->m_fear && ped->CharCreatedBy != MISSION_CHAR && ped->m_pMyVehicle->VehicleCreatedBy != MISSION_VEHICLE && !CTheScripts::IsPlayerOnAMission()){ ped->SetObjective(OBJECTIVE_WANDER, ped->m_pMyVehicle); ped->m_nPathDir = CGeneral::GetRandomNumberInRange(0, 8); } }else if(ped->m_leader == nil){ if(pDriver == ped) ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, this); else ped->SetObjective(OBJECTIVE_ENTER_CAR_AS_PASSENGER, this); } } if(ped->IsPed()){ CAnimBlendAssociation *assoc; for(assoc = RpAnimBlendClumpGetFirstAssociation(ped->GetClump(), ASSOC_DRIVING); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) assoc->flags |= ASSOC_DELETEFADEDOUT; } ped->SetPedState(PED_IDLE); CAnimManager::BlendAnimation(ped->GetClump(), ped->m_animGroup, ANIM_STD_IDLE, 100.0f); ped->m_vehDoor = CAR_DOOR_LF; CPed::PedSetOutCarCB(nil, ped); ped->SetMoveState(PEDMOVE_STILL); if(GetUp().z < 0.0f) ped->SetHeading(CGeneral::LimitRadianAngle(GetForward().Heading() + PI)); else ped->SetHeading(GetForward().Heading()); switch(weapon){ case WEAPONTYPE_UNARMED: case WEAPONTYPE_UNIDENTIFIED: ped->m_vecMoveSpeed = m_vecMoveSpeed; ped->m_pCollidingEntity = this; anim = ANIM_STD_NUM; break; case WEAPONTYPE_BASEBALLBAT: default: switch(direction){ case 0: anim = ANIM_STD_BIKE_FALLBACK; ped->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.1f); if(m_vecMoveSpeed.MagnitudeSqr() < SQR(0.3f)) ped->ApplyMoveForce(5.0f*GetUp() - 6.0f*GetForward()); ped->m_pCollidingEntity = this; break; case 1: case 2: if(m_vecMoveSpeed.MagnitudeSqr() > SQR(0.3f)){ anim = ANIM_STD_HIGHIMPACT_LEFT; ped->m_vecMoveSpeed = 0.3f*m_vecMoveSpeed; ped->ApplyMoveForce(5.0f*GetUp() + 6.0f*GetRight()); }else{ anim = ANIM_STD_SPINFORWARD_LEFT; ped->m_vecMoveSpeed = m_vecMoveSpeed; ped->ApplyMoveForce(4.0f*GetUp() + 8.0f*GetRight()); } // BUG or is it intentionally missing? //ped->m_pCollidingEntity = this; break; case 3: if(m_vecMoveSpeed.MagnitudeSqr() > SQR(0.3f)){ anim = ANIM_STD_HIGHIMPACT_RIGHT; ped->m_vecMoveSpeed = 0.3f*m_vecMoveSpeed; ped->ApplyMoveForce(5.0f*GetUp() - 6.0f*GetRight()); }else{ anim = ANIM_STD_SPINFORWARD_RIGHT; ped->m_vecMoveSpeed = m_vecMoveSpeed; ped->ApplyMoveForce(4.0f*GetUp() - 8.0f*GetRight()); } // BUG or is it intentionally missing? //ped->m_pCollidingEntity = this; break; } break; case WEAPONTYPE_DROWNING:{ RwRGBA color; anim = ANIM_STD_FALL; ped->m_vecMoveSpeed = m_vecMoveSpeed*0.2f; ped->m_vecMoveSpeed.z = 0.0f; ped->m_pCollidingEntity = this; color.red = (0.5f * CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj())*0.45f*255; color.green = (0.5f * CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj())*0.45f*255; color.blue = (0.5f * CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj())*0.45f*255; color.alpha = CGeneral::GetRandomNumberInRange(48, 96); DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLASH, 0.0f); CVector splashPos = ped->GetPosition() + 2.2f*ped->m_vecMoveSpeed; float waterZ = 0.0f; if(CWaterLevel::GetWaterLevel(splashPos, &waterZ, false)) splashPos.z = waterZ; CParticleObject::AddObject(POBJECT_PED_WATER_SPLASH, splashPos, CVector(0.0f, 0.0f, 0.1f), 0.0f, 200, color, true); break; } case WEAPONTYPE_FALL: { ped->m_vecMoveSpeed = ped->m_pMyVehicle->m_vecMoveSpeed; float forceXY = -0.6*m_fDamageImpulse * ped->m_fMass / m_fMass; ped->ApplyMoveForce(m_vecDamageNormal.x*forceXY, m_vecDamageNormal.y*forceXY, CGeneral::GetRandomNumberInRange(3.0f, 7.0f)); ped->m_pCollidingEntity = this; switch(direction){ case 0: anim = ANIM_STD_HIGHIMPACT_BACK; break; case 1: anim = ANIM_STD_SPINFORWARD_RIGHT; break; case 2: anim = ANIM_STD_BIKE_FALLBACK; break; case 3: anim = ANIM_STD_SPINFORWARD_LEFT; break; } if(m_nWheelsOnGround == 0) ped->bKnockedOffBike = true; break; } case WEAPONTYPE_RAMMEDBYCAR: { ped->m_vecMoveSpeed = ped->m_pMyVehicle->m_vecMoveSpeed; static float minForceZ = 8.0f; static float maxForceZ = 15.0f; float forceXY = -0.6*m_fDamageImpulse * ped->m_fMass / m_fMass; ped->ApplyMoveForce(m_vecDamageNormal.x*forceXY, m_vecDamageNormal.y*forceXY, CGeneral::GetRandomNumberInRange(minForceZ, maxForceZ)); ped->m_pCollidingEntity = this; switch(direction){ case 0: anim = ANIM_STD_HIGHIMPACT_BACK; break; case 1: anim = ANIM_STD_SPINFORWARD_RIGHT; break; case 2: anim = ANIM_STD_HIGHIMPACT_FRONT; break; case 3: anim = ANIM_STD_SPINFORWARD_LEFT; break; } ped->bKnockedOffBike = true; if(ped->IsPlayer()) ped->Say(SOUND_PED_DAMAGE); break; } } if(weapon == WEAPONTYPE_DROWNING){ ped->bIsStanding = false; ped->bWasStanding = false; ped->bIsInTheAir = true; ped->bIsInWater = true; ped->bTouchingWater = true; CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_FALL, 4.0f); }else if(weapon != WEAPONTYPE_UNARMED){ if(ped->m_fHealth > 0.0f) ped->SetFall(1000, anim, 0); else ped->SetDie(anim); ped->bIsStanding = false; } CEntity *ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CVector(0.0f, 0.0, 0.5f), 0.4f, nil, true, false, false, false, false, false); if(ent == nil) ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CVector(0.0f, 0.0, 0.8f), 0.4f, nil, true, false, false, false, false, false); if(ent == nil) ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CTimer::GetTimeStep()*ped->m_vecMoveSpeed+CVector(0.0f, 0.0, 0.5f), 0.4f, nil, true, false, false, false, false, false); if(ent == nil) ent = CWorld::TestSphereAgainstWorld(ped->GetPosition()+CTimer::GetTimeStep()*ped->m_vecMoveSpeed+CVector(0.0f, 0.0, 0.8f), 0.4f, nil, true, false, false, false, false, false); if(ent){ CColPoint point; ent = nil; if(CWorld::ProcessVerticalLine(ped->GetPosition(), ped->GetPosition().z-2.0f, point, ent, true, false, false, false, false, false, nil)){ if(ped->m_pMyVehicle == nil){ ped->m_pMyVehicle = this; ped->PositionPedOutOfCollision(); ped->m_pMyVehicle = nil; }else ped->PositionPedOutOfCollision(); }else ped->GetMatrix().Translate(CVector(0.0f, 0.0f, -2.0f)); ped->m_pCollidingEntity = ped->m_pMyVehicle; ped->bKnockedOffBike = true; ped->bHeadStuckInCollision = true; }else if(weapon == WEAPONTYPE_RAMMEDBYCAR){ if(CWorld::TestSphereAgainstWorld(ped->GetPosition()+CVector(0.0f, 0.0, 1.3f), 0.6f, nil, true, false, false, false, false, false) == nil) ped->GetMatrix().Translate(CVector(0.0f, 0.0f, 0.5f)); } ped->m_pMyVehicle = nil; } void CBike::PlayHornIfNecessary(void) { if(AutoPilot.m_bSlowedDownBecauseOfPeds || AutoPilot.m_bSlowedDownBecauseOfCars) PlayCarHorn(); } void CBike::ResetSuspension(void) { int i; for(i = 0; i < 2; i++){ m_aWheelRotation[i] = 0.0f; m_aWheelState[i] = WHEEL_STATE_NORMAL; } for(i = 0; i < 4; i++){ m_aSuspensionSpringRatio[i] = 1.0f; m_aWheelTimer[i] = 0.0f; } } void CBike::SetupSuspensionLines(void) { int i; CVector posn; float suspOffset = 0.0f; RwFrame *node = nil; CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); CColModel *colModel = mi->GetColModel(); RwMatrix *mat = RwMatrixCreate(); bool initialized = colModel->lines[0].p0.z != FAKESUSPENSION; for(i = 0; i < 4; i++){ if(initialized){ posn = colModel->lines[i].p0; if(i < 2) posn.z = m_aWheelBasePosition[0]; else posn.z = m_aWheelBasePosition[1]; }else{ switch(i){ case BIKESUSP_F1: node = m_aBikeNodes[BIKE_WHEEL_FRONT]; suspOffset = 0.25f*mi->m_wheelScale; break; case BIKESUSP_F2: node = m_aBikeNodes[BIKE_WHEEL_FRONT]; suspOffset = -0.25f*mi->m_wheelScale; break; case BIKESUSP_R1: node = m_aBikeNodes[BIKE_WHEEL_REAR]; suspOffset = 0.25f*mi->m_wheelScale; break; case BIKESUSP_R2: node = m_aBikeNodes[BIKE_WHEEL_REAR]; suspOffset = -0.25f*mi->m_wheelScale; break; } GetRelativeMatrix(mat, node, node); posn = *RwMatrixGetPos(mat); if(i == BIKESUSP_F1) m_aWheelBasePosition[BIKEWHEEL_FRONT] = posn.z; else if(i == BIKESUSP_R1){ m_aWheelBasePosition[BIKEWHEEL_REAR] = posn.z; GetRelativeMatrix(mat, m_aBikeNodes[BIKE_FORKS_REAR], m_aBikeNodes[BIKE_FORKS_REAR]); float dz = posn.z - RwMatrixGetPos(mat)->z; float dy = posn.y - RwMatrixGetPos(mat)->y; m_fRearForkLength = Sqrt(SQR(dy) + SQR(dz)); assert(m_fRearForkLength != 0.0f); // we want to divide by this } posn.y += suspOffset; } // 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; } if(!initialized){ GetRelativeMatrix(mat, m_aBikeNodes[BIKE_FORKS_FRONT], m_aBikeNodes[BIKE_FORKS_FRONT]); m_fFrontForkY = RwMatrixGetPos(mat)->y; m_fFrontForkZ = RwMatrixGetPos(mat)->z; } // Compress spring somewhat to get normal height on road m_fHeightAboveRoad = m_aSuspensionSpringLength[0]*(1.0f - 1.0f/(4.0f*pHandling->fSuspensionForceLevel)) - colModel->lines[0].p0.z + mi->m_wheelScale*0.5f; for(i = 0; i < 2; 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; #ifdef FIX_BUGS RwMatrixDestroy(mat); #endif } void CBike::CalculateLeanMatrix(void) { if(bLeanMatrixClean) return; CMatrix mat; mat.SetRotateX(-0.05f*Abs(m_fLeanLRAngle)); mat.RotateY(m_fLeanLRAngle); m_leanMatrix = GetMatrix(); m_leanMatrix = m_leanMatrix * mat; // place wheel back on ground m_leanMatrix.GetPosition() += GetUp()*(1.0f-Cos(m_fLeanLRAngle))*GetColModel()->boundingBox.min.z; bLeanMatrixClean = true; } void CBike::GetCorrectedWorldDoorPosition(CVector &pos, CVector p1, CVector p2) { CVector &fwd = GetForward(); CVector rightWorld = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); CVector upWorld = CrossProduct(rightWorld, fwd); CColModel *colModel = GetColModel(); float onSide = DotProduct(GetUp(), rightWorld); float diff = Max(colModel->boundingBox.max.z-colModel->boundingBox.max.x, 0.0f); pos = CVector(0.0f, 0.0f, 0.0f); float y = p2.y - p1.y; float x = onSide*diff + p2.x + p1.x; float z = p2.z - p1.z; pos = x*rightWorld + y*fwd + z*upWorld + GetPosition(); } void CBike::Fix(void) { bIsDamaged = false; bIsOnFire = false; m_wheelStatus[0] = WHEEL_STATUS_OK; m_wheelStatus[1] = WHEEL_STATUS_OK; } void CBike::SetupModelNodes(void) { int i; for(i = 0; i < BIKE_NUM_NODES; i++) m_aBikeNodes[i] = nil; CClumpModelInfo::FillFrameArray(GetClump(), m_aBikeNodes); } void CBike::ReduceHornCounter(void) { if(m_nCarHornTimer != 0) m_nCarHornTimer--; } #ifdef COMPATIBLE_SAVES void CBike::Save(uint8*& buf) { CVehicle::Save(buf); SkipSaveBuf(buf, 1260 - 672); } void CBike::Load(uint8*& buf) { CVehicle::Load(buf); SkipSaveBuf(buf, 1260 - 672); } #endif ================================================ FILE: src/vehicles/Bike.h ================================================ #pragma once #include "Vehicle.h" #include "Skidmarks.h" #include "AnimManager.h" enum eBikeNodes { BIKE_NODE_NONE, BIKE_CHASSIS, BIKE_FORKS_FRONT, BIKE_FORKS_REAR, BIKE_WHEEL_FRONT, BIKE_WHEEL_REAR, BIKE_MUDGUARD, BIKE_HANDLEBARS, BIKE_NUM_NODES }; enum { BIKEWHEEL_FRONT, BIKEWHEEL_REAR, }; enum { BIKESUSP_F1, BIKESUSP_F2, BIKESUSP_R1, BIKESUSP_R2, }; class CBike : public CVehicle { public: RwFrame *m_aBikeNodes[BIKE_NUM_NODES]; bool bLeanMatrixClean; CMatrix m_leanMatrix; CVector m_vecAvgSurfaceNormal; CVector m_vecAvgSurfaceRight; tBikeHandlingData *pBikeHandling; AssocGroupId m_bikeAnimType; uint8 m_wheelStatus[2]; CColPoint m_aWheelColPoints[4]; float m_aSuspensionSpringRatio[4]; float m_aSuspensionSpringRatioPrev[4]; float m_aWheelTimer[4]; float m_bike_unused1; eSkidmarkType m_aWheelSkidmarkType[2]; bool m_aWheelSkidmarkBloody[2]; bool m_aWheelSkidmarkUnk[2]; float m_aWheelRotation[2]; float m_aWheelSpeed[2]; float m_aWheelPosition[2]; float m_aWheelBasePosition[2]; float m_aSuspensionSpringLength[4]; float m_aSuspensionLineLength[4]; float m_fHeightAboveRoad; float m_fTraction; float m_fRearForkLength; float m_fFrontForkY; float m_fFrontForkZ; float m_fFrontForkSlope; float m_fWheelAngle; float m_fLeanLRAngle; float m_fLeanLRAngle2; float m_fLeanInput; float m_fPedLeanAmountLR; float m_fPedLeanAmountUD; uint8 m_bike_unused2; uint8 unused[3]; // looks like padding..but for what? uint8 m_bike_flag01 : 1; uint8 m_bike_flag02 : 1; uint8 bWaterTight : 1; uint8 bIsBeingPickedUp : 1; uint8 bIsStanding : 1; uint8 bExtraSpeed : 1; // leaning forward uint8 bIsOnFire : 1; uint8 bWheelieCam : 1; int16 m_doingBurnout; float m_fTireTemperature; float m_fBrakeDestabilization; float m_fVelocityChangeForAudio; float m_fFireBlowUpTimer; CPhysical *m_aGroundPhysical[4]; CVector m_aGroundOffset[4]; CEntity *m_pSetOnFireEntity; uint8 m_nWheelsOnGround; uint8 m_nDriveWheelsOnGround; uint8 m_nDriveWheelsOnGroundPrev; float m_fGasPedalAudio; tWheelState m_aWheelState[2]; CBike(int32 id, uint8 CreatedBy); // from CEntity void SetModelIndex(uint32 id); void ProcessControl(void); void Teleport(CVector v); void PreRender(void); void Render(void); // from CPhysical int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints); // from CVehicle void ProcessControlInputs(uint8); void GetComponentWorldPosition(int32 component, CVector &pos); bool IsComponentPresent(int32 component); void SetComponentRotation(int32 component, CVector rotation); bool IsDoorReady(eDoors door); bool IsDoorFullyOpen(eDoors door); bool IsDoorClosed(eDoors door); bool IsDoorMissing(eDoors door); void RemoveRefsToVehicle(CEntity *ent); void BlowUpCar(CEntity *ent); bool SetUpWheelColModel(CColModel *colModel); void BurstTyre(uint8 tyre, bool applyForces); bool IsRoomForPedToLeaveCar(uint32 component, CVector *doorOffset); float GetHeightAboveRoad(void); void PlayCarHorn(void); void KnockOffRider(eWeaponType weapon, uint8 direction, CPed *ped, bool bGetBackOn); void VehicleDamage(void); void ProcessBuoyancy(void); void DoDriveByShootings(void); void AddDamagedVehicleParticles(void); int32 AddWheelDirtAndWater(CColPoint *colpoint, uint32 belowEffectSpeed); void PlayHornIfNecessary(void); void ResetSuspension(void); void SetupSuspensionLines(void); void CalculateLeanMatrix(void); void GetCorrectedWorldDoorPosition(CVector &pos, CVector p1, CVector p2); void Fix(void); void SetupModelNodes(void); void ReduceHornCounter(void); #ifdef COMPATIBLE_SAVES virtual void Save(uint8*& buf); virtual void Load(uint8*& buf); #endif static const uint32 nSaveStructSize; }; // These functions and function names are made up inline int8 GetBikeDoorFlag(int32 carnode) { switch (carnode) { case CAR_DOOR_RR: case CAR_DOOR_LR: return CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; case CAR_DOOR_RF: case CAR_DOOR_LF: return CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; default: return CAR_DOOR_FLAG_UNKNOWN; } } // for m_nGettingOutFlags inline int8 GetBikeDoorFlagInclJumpInFromFront(int32 carnode) { switch (carnode) { case CAR_DOOR_RR: case CAR_DOOR_LR: return CAR_DOOR_FLAG_RR | CAR_DOOR_FLAG_LR; case CAR_DOOR_RF: case CAR_DOOR_LF: case CAR_WINDSCREEN: return CAR_DOOR_FLAG_RF | CAR_DOOR_FLAG_LF; default: return CAR_DOOR_FLAG_UNKNOWN; } } ================================================ FILE: src/vehicles/Boat.cpp ================================================ #include "common.h" #include "main.h" #include "General.h" #include "Timecycle.h" #include "Weather.h" #include "HandlingMgr.h" #include "CarAI.h" #include "CarCtrl.h" #include "RwHelper.h" #include "ModelIndices.h" #include "VisibilityPlugins.h" #include "DMAudio.h" #include "Camera.h" #include "Darkel.h" #include "Explosion.h" #include "Particle.h" #include "ParticleObject.h" #include "WaterLevel.h" #include "Floater.h" #include "World.h" #include "Stats.h" #include "Pools.h" #include "Pad.h" #include "Boat.h" #include "AnimBlendAssociation.h" #include "RpAnimBlend.h" #include "Record.h" #include "Shadows.h" #include "Wanted.h" #define INVALID_ORIENTATION (-9999.99f) float CBoat::MAX_WAKE_LENGTH = 50.0f; float CBoat::MIN_WAKE_INTERVAL = 2.0f; float CBoat::WAKE_LIFETIME = 150.0f; float fShapeLength = 0.4f; float fShapeTime = 0.05f; float fRangeMult = 0.6f; float fTimeMult = 1.2f/CBoat::WAKE_LIFETIME; CBoat *CBoat::apFrameWakeGeneratingBoats[4]; const uint32 CBoat::nSaveStructSize = #ifdef COMPATIBLE_SAVES 1216; #else sizeof(CBoat); #endif CBoat::CBoat(int mi, uint8 owner) : CVehicle(owner) { CVehicleModelInfo *minfo = (CVehicleModelInfo*)CModelInfo::GetModelInfo(mi); m_vehType = VEHICLE_TYPE_BOAT; m_fAccelerate = 0.0f; m_fBrake = 0.0f; m_fSteeringLeftRight = 0.0f; m_nPadID = 0; m_fMovingRotation = 0.0f; m_fMovingSpeed = 0.0f; m_skimmerThingTimer = 0.0f; m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds(); SetModelIndex(mi); pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)minfo->m_handlingId); pFlyingHandling = mod_HandlingManager.GetFlyingPointer((tVehicleType)minfo->m_handlingId); pBoatHandling = mod_HandlingManager.GetBoatPointer((tVehicleType)minfo->m_handlingId); minfo->ChooseVehicleColour(m_currentColour1, m_currentColour2); m_fMass = pHandling->fMass; m_fTurnMass = pHandling->fTurnMass / 2.0f; m_vecCentreOfMass = pHandling->CentreOfMass; m_fAirResistance = pHandling->Dimension.x * pHandling->Dimension.z / m_fMass; m_fElasticity = 0.1f; m_fBuoyancy = pHandling->fBuoyancy; m_fSteerAngle = 0.0f; m_fGasPedal = 0.0f; m_fBrakePedal = 0.0f; m_boat_unused3 = false; m_fVolumeUnderWater = 7.0f; m_fPrevVolumeUnderWater = 7.0f; m_vecBuoyancePoint = CVector(0.0f, 0.0f, 0.0f); m_nDeltaVolumeUnderWater = 0; bBoatInWater = true; bPropellerInWater = true; bIsInWater = true; m_phys_unused1 = 0.0f; m_boat_unused2 = 0; m_bIsAnchored = true; m_fOrientation = INVALID_ORIENTATION; bTouchingWater = true; m_fDamage = 0.0f; m_pSetOnFireEntity = nil; m_nNumWakePoints = 0; for (int16 i = 0; i < ARRAY_SIZE(m_afWakePointLifeTime); i++) m_afWakePointLifeTime[i] = 0.0f; m_nAmmoInClip = 20; if(GetModelIndex() == MI_MARQUIS) m_boom.Init(-PI/10.0f, PI/10.0f, 0, 2); else m_boom.Init(-PI/5.0f, PI/5.0f, 0, 2); } void CBoat::SetModelIndex(uint32 id) { CVehicle::SetModelIndex(id); SetupModelNodes(); } void CBoat::GetComponentWorldPosition(int32 component, CVector &pos) { pos = *RwMatrixGetPos(RwFrameGetLTM(m_aBoatNodes[component])); } void CBoat::ProcessControl(void) { bool onLand = m_fDamageImpulse > 0.0f && m_vecDamageNormal.z > 0.1f; PruneWakeTrail(); if(bRenderScorched) m_fBuoyancy *= 0.99f; #ifdef FIX_BUGS if(FindPlayerPed() && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 && GetModelIndex() == MI_PREDATOR){ #else if(FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 && GetModelIndex() == MI_PREDATOR){ #endif CVehicle *playerVeh = FindPlayerVehicle(); if(playerVeh && playerVeh->GetVehicleAppearance() == VEHICLE_APPEARANCE_BOAT && (AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE || AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_FARAWAY || AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE || AutoPilot.m_nCarMission == MISSION_ATTACKPLAYER) && CTimer::GetTimeInMilliseconds() > m_nPoliceShoutTimer){ DMAudio.PlayOneShot(m_audioEntityId, SOUND_PED_VCPA_PLAYER_FOUND, 0.0f); m_nPoliceShoutTimer = CTimer::GetTimeInMilliseconds() + 4500 + (CGeneral::GetRandomNumber()&0xFFF); } } int r, g, b; RwRGBA dropColor = { 0, 0, 0, 0 }; RwRGBA splashColor, jetColor; r = 127.5f*(CTimeCycle::GetAmbientRed_Obj() + 0.5f*CTimeCycle::GetDirectionalRed()); g = 127.5f*(CTimeCycle::GetAmbientGreen_Obj() + 0.5f*CTimeCycle::GetDirectionalGreen()); b = 127.5f*(CTimeCycle::GetAmbientBlue_Obj() + 0.5f*CTimeCycle::GetDirectionalBlue()); r = clamp(r, 0, 255); g = clamp(g, 0, 255); b = clamp(b, 0, 255); splashColor.red = r; splashColor.green = g; splashColor.blue = b; splashColor.alpha = CGeneral::GetRandomNumberInRange(160, 196); r = 229.5f*(CTimeCycle::GetAmbientRed() + 0.85f*CTimeCycle::GetDirectionalRed()); g = 229.5f*(CTimeCycle::GetAmbientGreen() + 0.85f*CTimeCycle::GetDirectionalGreen()); b = 229.5f*(CTimeCycle::GetAmbientBlue() + 0.85f*CTimeCycle::GetDirectionalBlue()); r = clamp(r, 0, 255); g = clamp(g, 0, 255); b = clamp(b, 0, 255); jetColor.red = r; jetColor.green = g; jetColor.blue = b; jetColor.alpha = CGeneral::GetRandomNumberInRange(196, 228); CGeneral::GetRandomNumber(); // unused UpdateClumpAlpha(); ProcessCarAlarm(); switch(GetStatus()){ case STATUS_PLAYER: m_bIsAnchored = false; m_fOrientation = INVALID_ORIENTATION; ProcessControlInputs(0); if(GetModelIndex() == MI_PREDATOR) DoFixedMachineGuns(); if (!CRecordDataForChase::IsRecording()) DoDriveByShootings(); break; case STATUS_SIMPLE: m_bIsAnchored = false; m_fOrientation = INVALID_ORIENTATION; CCarAI::UpdateCarAI(this); CPhysical::ProcessControl(); bBoatInWater = true; bPropellerInWater = true; bIsInWater = true; return; case STATUS_PHYSICS: m_bIsAnchored = false; m_fOrientation = INVALID_ORIENTATION; CCarAI::UpdateCarAI(this); CCarCtrl::SteerAICarWithPhysics(this); break; case STATUS_ABANDONED: case STATUS_WRECKED: bBoatInWater = true; bPropellerInWater = true; bIsInWater = true; m_fSteerAngle = 0.0; bIsHandbrakeOn = false; m_fBrakePedal = 0.5f; m_fGasPedal = 0.0f; if((GetPosition() - FindPlayerCentreOfWorld_NoSniperShift()).Magnitude() > 150.0f){ m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); return; } break; default: break; } float collisionDamage = pHandling->fCollisionDamageMultiplier * m_fDamageImpulse; #ifdef FIX_BUGS if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) collisionDamage *= 0.5f; if (collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED && !bCollisionProof) { #else if(collisionDamage > 25.0f && GetStatus() != STATUS_WRECKED){ #endif float prevHealth = m_fHealth; if(prevHealth >= 250.0f){ #ifndef FIX_BUGS // if collisionDamage < 50 we actually increase health here... if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) collisionDamage *= 0.5f; #endif if(this == FindPlayerVehicle()){ if(bTakeLessDamage) m_fHealth -= (collisionDamage-25.0f)/6.0f; else m_fHealth -= (collisionDamage-25.0f)/2.0f; }else{ if(collisionDamage > 60.0f && pDriver) pDriver->Say(SOUND_PED_ANNOYED_DRIVER); if(bTakeLessDamage) m_fHealth -= (collisionDamage-25.0f)/12.0f; else m_fHealth -= (collisionDamage-25.0f)/4.0f; } if(m_fHealth <= 0.0f && prevHealth > 0.0f){ m_fHealth = 1.0f; m_pSetOnFireEntity = m_pDamageEntity; } } } // Damage particles if(m_fHealth <= 460.0f && GetStatus() != STATUS_WRECKED && Abs(GetPosition().x - TheCamera.GetPosition().x) < 200.0f && Abs(GetPosition().y - TheCamera.GetPosition().y) < 200.0f){ float speedSq = m_vecMoveSpeed.MagnitudeSqr(); CVector smokeDir = 0.8f*m_vecMoveSpeed; CVector smokePos; switch(GetModelIndex()){ case MI_SPEEDER: smokePos = CVector(0.4f, -2.4f, 0.8f); smokeDir += 0.05f*GetRight(); smokeDir.z += 0.2f*m_vecMoveSpeed.z; break; case MI_REEFER: smokePos = CVector(2.0f, -1.0f, 0.5f); smokeDir += 0.07f*GetRight(); break; case MI_PREDATOR: default: smokePos = CVector(-1.5f, -0.5f, 1.2f); smokeDir += -0.08f*GetRight(); break; } smokePos = GetMatrix() * smokePos; // On fire if(m_fHealth < 250.0f){ CParticle::AddParticle(PARTICLE_CARFLAME, smokePos, CVector(0.0f, 0.0f, CGeneral::GetRandomNumberInRange(2.25f/200.0f, 0.09f)), nil, 0.9f); CVector smokePos2 = smokePos; smokePos2.x += CGeneral::GetRandomNumberInRange(-2.25f/4.0f, 2.25f/4.0f); smokePos2.y += CGeneral::GetRandomNumberInRange(-2.25f/4.0f, 2.25f/4.0f); smokePos2.z += CGeneral::GetRandomNumberInRange(2.25f/4.0f, 2.25f); CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, smokePos2, CVector(0.0f, 0.0f, 0.0f)); m_fDamage += CTimer::GetTimeStepInMilliseconds(); if(m_fDamage > 5000.0f) BlowUpCar(m_pSetOnFireEntity); } if(speedSq < 0.25f && (CTimer::GetFrameCounter() + m_randomSeed) & 1) CParticle::AddParticle(PARTICLE_ENGINE_STEAM, smokePos, smokeDir); if(speedSq < 0.25f && m_fHealth <= 390.0f) CParticle::AddParticle(PARTICLE_ENGINE_SMOKE, smokePos, 1.25f*smokeDir); } bool bSeparateTurnForce = bHasHitWall; CPhysical::ProcessControl(); CVector buoyanceImpulse(0.0f, 0.0f, 0.0f); CVector buoyancePoint(0.0f, 0.0f, 0.0f); if(mod_Buoyancy.ProcessBuoyancyBoat(this, pHandling->fBuoyancy, &buoyancePoint, &buoyanceImpulse, bSeparateTurnForce)){ // Process boat in water if(0.1f * m_fMass * GRAVITY*CTimer::GetTimeStep() < buoyanceImpulse.z){ bBoatInWater = true; bIsInWater = true; if (GetUp().z < -0.6f && Abs(GetMoveSpeed().x) < 0.05 && Abs(GetMoveSpeed().y) < 0.05) { bIsDrowning = true; if (pDriver){ pDriver->bTouchingWater = true; pDriver->InflictDamage(nil, WEAPONTYPE_DROWNING, CTimer::GetTimeStep(), PEDPIECE_TORSO, 0); } } else bIsDrowning = false; }else{ bBoatInWater = false; bIsInWater = false; bIsDrowning = false; } m_fVolumeUnderWater = mod_Buoyancy.m_volumeUnderWater; m_vecBuoyancePoint = buoyancePoint; if(GetModelIndex() == MI_SKIMMER && GetUp().z < -0.5f && Abs(m_vecMoveSpeed.x) < 0.2f && Abs(m_vecMoveSpeed.y) < 0.2f) ApplyMoveForce(0.03f*buoyanceImpulse); else ApplyMoveForce(buoyanceImpulse); if(bSeparateTurnForce) ApplyTurnForce(0.4f*buoyanceImpulse, buoyancePoint); // TODO: what is this? if(GetModelIndex() == MI_SKIMMER) if(m_skimmerThingTimer != 0.0f || GetForward().z < -0.5f && GetUp().z > -0.5f && m_vecMoveSpeed.z < -0.15f && buoyanceImpulse.z > 0.01f*m_fMass * GRAVITY*CTimer::GetTimeStep() && buoyanceImpulse.z < 0.4f*m_fMass * GRAVITY*CTimer::GetTimeStep()){ float turnImpulse = -0.00017f*GetForward().z*buoyanceImpulse.z * m_fMass*CTimer::GetTimeStep(); ApplyTurnForce(turnImpulse*GetForward(), GetUp()); bBoatInWater = false; //BUG? aren't we forgetting the timestep here? float moveImpulse = -0.5f*DotProduct(m_vecMoveSpeed, GetForward()) * m_fMass; ApplyMoveForce(moveImpulse*GetForward()); if(m_skimmerThingTimer == 0.0f) m_skimmerThingTimer = CTimer::GetTimeInMilliseconds() + 300.0f; else if(m_skimmerThingTimer < CTimer::GetTimeInMilliseconds()) m_skimmerThingTimer = 0.0f; } if(!onLand && bBoatInWater && GetUp().z > 0.0f){ float impulse = m_vecMoveSpeed.MagnitudeSqr()*pBoatHandling->fAqPlaneForce*buoyanceImpulse.z*CTimer::GetTimeStep()*0.5f; if(GetModelIndex() == MI_SKIMMER) impulse *= 1.0f + m_fGasPedal; else if(m_fGasPedal > 0.05f) impulse *= m_fGasPedal; else impulse = 0.0f; impulse = Min(impulse, GRAVITY*pBoatHandling->fAqPlaneLimit*m_fMass*CTimer::GetTimeStep()); ApplyMoveForce(impulse*GetUp()); ApplyTurnForce(impulse*GetUp(), buoyancePoint - pBoatHandling->fAqPlaneOffset*GetForward()); } // Handle boat moving forward float fwdSpeed = 1.0f; if(Abs(m_fGasPedal) > 0.05f || (fwdSpeed = m_vecMoveSpeed.Magnitude2D()) > 0.01f){ if(bBoatInWater && fwdSpeed > 0.05f) AddWakePoint(GetPosition()); float steerFactor = 1.0f; if(GetStatus() == STATUS_PLAYER){ float steerLoss = DotProduct(m_vecMoveSpeed, GetForward())*pHandling->fTractionBias; if(CPad::GetPad(0)->GetHandBrake()) steerLoss *= 0.5f; steerFactor -= steerLoss; steerFactor = clamp(steerFactor, 0.0f, 1.0f); } CVector boundMin = GetColModel()->boundingBox.min; CVector propeller(0.0f, boundMin.y*pBoatHandling->fThrustY, boundMin.z*pBoatHandling->fThrustZ); propeller = Multiply3x3(GetMatrix(), propeller); CVector propellerWorld = GetPosition() + propeller; float steerSin = Sin(-m_fSteerAngle * steerFactor); float steerCos = Cos(-m_fSteerAngle * steerFactor); float waterLevel; CWaterLevel::GetWaterLevel(propellerWorld, &waterLevel, true); if(propellerWorld.z-0.5f < waterLevel){ float propellerDepth = waterLevel - (propellerWorld.z - 0.5f); if(propellerDepth > 1.0f) propellerDepth = 1.0f; else propellerDepth = SQR(propellerDepth); bPropellerInWater = true; bool bSlowAhead = false; if(Abs(m_fGasPedal) > 0.01f && GetModelIndex() != MI_SKIMMER){ if(Abs(m_fGasPedal) < 0.05f) bSlowAhead = true; CVector forceDir = Multiply3x3(GetMatrix(), CVector(-steerSin, steerCos, -Abs(m_fSteerAngle))); CVector force = propellerDepth * m_fGasPedal * 40.0f * pHandling->Transmission.fEngineAcceleration * pHandling->fMass * forceDir; if(force.z > 0.2f) force.z = SQR(1.2f - force.z) + 0.2f; if(onLand){ if(m_fGasPedal < 0.0f){ force.x *= 5.0f; force.y *= 5.0f; } if(force.z < 0.0f) force.z = 0.0f; ApplyMoveForce(force * CTimer::GetTimeStep()); }else{ ApplyMoveForce(force * CTimer::GetTimeStep()); ApplyTurnForce(force * CTimer::GetTimeStep(), propeller - pBoatHandling->fThrustAppZ*GetUp()); float rightForce = -DotProduct(GetRight(), force)*pHandling->fTractionMultiplier; ApplyTurnForce(rightForce*GetRight() * CTimer::GetTimeStep(), GetUp()); } // Spray some particles CVector jetDir = -0.04f * force; if(m_fGasPedal > 0.0f){ if(GetStatus() == STATUS_PLAYER){ CVector sternPos = GetColModel()->boundingBox.min; sternPos.x = 0.0f; sternPos.z = 0.0f; sternPos = Multiply3x3(GetMatrix(), sternPos); CVector wakePos = GetPosition() + sternPos; // no actual particles for player... }else if(IsVisible() && ((CTimer::GetFrameCounter() + m_randomSeed) & 1) && CVisibilityPlugins::GetDistanceSquaredFromCamera(&propellerWorld) < SQR(70.0f * TheCamera.GenerationDistMultiplier)){ jetDir.z = 0.015f; jetDir.x *= 3.5f; jetDir.y *= 3.5f; propellerWorld.z += 0.5f; CParticle::AddParticle(PARTICLE_BOAT_SPLASH, propellerWorld, jetDir, nil, 1.25f, jetColor, CGeneral::GetRandomNumberInRange(0, 5), CGeneral::GetRandomNumberInRange(0, 90), 1, 500); CParticle::AddParticle(PARTICLE_CAR_SPLASH, propellerWorld, 0.75f * jetDir, nil, 0.5f, splashColor, CGeneral::GetRandomNumberInRange(0, 30), CGeneral::GetRandomNumberInRange(0, 45), 3, 500); } } }else bSlowAhead = true; if(!onLand && bSlowAhead){ float force = pHandling->fTractionLoss*DotProduct(m_vecMoveSpeed, GetForward()); force = Min(force, 0.01f*m_fTurnMass); if(m_fGasPedal > 0.01f){ if(GetStatus() == STATUS_PLAYER) force *= (0.55f - Abs(m_fGasPedal)) * 1.3f; else force *= (0.55f - Abs(m_fGasPedal)) * 2.5f; } if(m_fGasPedal < 0.0f && force > 0.0f || m_fGasPedal > 0.0f && force < 0.0f) force *= -1.0f; CVector propellerForce = propellerDepth * Multiply3x3(GetMatrix(), force*CVector(-steerSin, 0.0f, 0.0f)); ApplyMoveForce(propellerForce * CTimer::GetTimeStep()); ApplyTurnForce(propellerForce * CTimer::GetTimeStep(), propeller); float rightForce = -steerSin * force * 0.75f/steerFactor * Max(CTimer::GetTimeStep(), 0.01f); ApplyTurnForce(GetRight() * rightForce, GetUp()); } }else bPropellerInWater = false; if(pHandling->fSuspensionBias != 0.0f){ CVector right = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f)); float rightSpeed = DotProduct(m_vecMoveSpeed, right); float impulse = 0.1f*pHandling->fSuspensionBias * m_fMass * m_fVolumeUnderWater * rightSpeed * CTimer::GetTimeStep(); ApplyMoveForce(right - impulse * 0.3f * CVector(-right.y, right.x, 0.0f)); } if(GetStatus() == STATUS_PLAYER && CPad::GetPad(0)->GetHandBrake()){ float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); if(fwdSpeed > 0.0f){ float impulse = -0.1f*pHandling->fSuspensionLowerLimit * m_fMass * m_fVolumeUnderWater * fwdSpeed * CTimer::GetTimeStep(); ApplyMoveForce(impulse * GetForward()); } } } // Slow down or push down boat as it approaches the world limits m_vecMoveSpeed.x = Min(m_vecMoveSpeed.x, -(GetPosition().x - (WORLD_MAX_X-100.0f))*0.01f); // east m_vecMoveSpeed.x = Max(m_vecMoveSpeed.x, -(GetPosition().x - (WORLD_MIN_X+100.0f))*0.01f); // west m_vecMoveSpeed.y = Min(m_vecMoveSpeed.y, -(GetPosition().y - (WORLD_MAX_Y-100.0f))*0.01f); // north m_vecMoveSpeed.y = Max(m_vecMoveSpeed.y, -(GetPosition().y - (WORLD_MIN_Y+100.0f))*0.01f); // south if(!onLand && bBoatInWater && !bSeparateTurnForce) ApplyWaterResistance(); if((GetModelIndex() != MI_SKIMMER || m_skimmerThingTimer == 0.0f) && !bSeparateTurnForce){ // No idea what exactly is going on here besides drag in YZ float fx = Pow(pBoatHandling->vecTurnRes.x, CTimer::GetTimeStep()); float fy = Pow(pBoatHandling->vecTurnRes.y, CTimer::GetTimeStep()); float fz = Pow(pBoatHandling->vecTurnRes.z, CTimer::GetTimeStep()); m_vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); // invert - to local space // TODO: figure this out float magic = 1.0f/(1000.0f * SQR(m_vecTurnSpeed.x) + 1.0f) * fx; m_vecTurnSpeed.y *= fy; m_vecTurnSpeed.z *= fz; float forceUp = (magic - 1.0f) * m_vecTurnSpeed.x * m_fTurnMass; m_vecTurnSpeed = Multiply3x3(GetMatrix(), m_vecTurnSpeed); // back to world CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass); ApplyTurnForce(forceUp*GetUp(), com + GetForward()); } m_nDeltaVolumeUnderWater = (m_fVolumeUnderWater-m_fPrevVolumeUnderWater)*10000; // Falling into water if(!onLand && bBoatInWater && GetUp().z > 0.0f){ float splashVol = m_nDeltaVolumeUnderWater*pBoatHandling->fWaveAudioMult; if(splashVol > 200.0f) DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_SPLASH, splashVol); if(m_nDeltaVolumeUnderWater > 200){ float speedUp = m_vecMoveSpeed.MagnitudeSqr() * m_nDeltaVolumeUnderWater * 0.001f; if(speedUp + m_vecMoveSpeed.z > pHandling->fBrakeDeceleration) speedUp = pHandling->fBrakeDeceleration - m_vecMoveSpeed.z; if(speedUp < 0.0f) speedUp = 0.0f; float speedFwd = DotProduct(m_vecMoveSpeed, GetForward()); speedFwd *= -m_nDeltaVolumeUnderWater * 0.01f * pHandling->fBrakeBias; CVector speed = speedFwd*GetForward() + CVector(0.0f, 0.0f, speedUp); CVector splashImpulse = speed * m_fMass; ApplyMoveForce(splashImpulse); ApplyTurnForce(splashImpulse, buoyancePoint); } } // Splashes float speed = m_vecMoveSpeed.Magnitude(); if(speed > 0.05f && GetUp().x > 0.0f && !TheCamera.GetLookingForwardFirstPerson() && IsVisible() && (AutoPilot.m_nCarMission != MISSION_CRUISE || (CTimer::GetFrameCounter()&2) == 0)){ CVector splashPos, splashDir; float splashSize, front, waterLevel; switch(GetModelIndex()){ case MI_RIO: splashSize = speed; front = 0.9f * GetColModel()->boundingBox.max.y; splashDir = -0.5f * m_vecMoveSpeed; splashDir.z += 0.25f*speed; splashDir += 0.35f*speed*GetRight(); splashPos = GetPosition() + 1.85f*GetRight() + front*GetForward(); splashPos.z += 0.5f; break; case MI_SQUALO: splashSize = speed; front = 0.75f * GetColModel()->boundingBox.max.y; splashDir = -0.125f * m_vecMoveSpeed; splashDir.z += 0.15f*speed; splashDir += 0.25f*speed*GetRight(); splashPos = GetPosition() + m_vecBuoyancePoint + 0.5f*GetRight() + front*GetForward(); splashPos.z += 0.5f; break; case MI_REEFER: splashSize = speed; front = 0.75f * GetColModel()->boundingBox.max.y; splashDir = -0.5f * m_vecMoveSpeed; splashDir.z += 0.15f*speed; splashDir += 0.5f*speed*GetRight(); splashPos = GetPosition() + m_vecBuoyancePoint + 1.3f*GetRight() + front*GetForward(); break; case MI_COASTG: splashSize = 0.25f*speed; front = 0.8f * GetColModel()->boundingBox.max.y; splashDir = 0.165f * m_vecMoveSpeed; splashDir.z += 0.25f*speed; splashDir += 0.15f*speed*GetRight(); splashPos = GetPosition() + 0.65f*GetRight() + front*GetForward(); splashPos.z += 0.5f; break; case MI_DINGHY: splashSize = 0.25f*speed; front = 0.9f * GetColModel()->boundingBox.max.y; splashDir = 0.35f * m_vecMoveSpeed; splashDir.z += 0.25f*speed; splashDir += 0.25f*speed*GetRight(); splashPos = GetPosition() + 0.6f*GetRight() + front*GetForward(); splashPos.z += 0.5f; break; default: splashSize = speed; front = 0.9f * GetColModel()->boundingBox.max.y; splashDir = -0.5f * m_vecMoveSpeed; splashDir.z += 0.25f*speed; splashDir += 0.35f*speed*GetRight(); splashPos = GetPosition() + m_vecBuoyancePoint + 0.5f*GetRight() + front*GetForward(); break; } if(splashSize > 0.75f) splashSize = 0.75f; if(AutoPilot.m_nCarMission == MISSION_CRUISE) splashDir *= 1.5f; static float lifeMult = 1000.0f; static float lifeBase = 300.0f; splashDir.z += 0.0003f*m_nDeltaVolumeUnderWater; CWaterLevel::GetWaterLevel(splashPos, &waterLevel, true); if(splashPos.z-waterLevel < 3.0f && CVisibilityPlugins::GetDistanceSquaredFromCamera(&splashPos) < SQR(70.0f * TheCamera.GenerationDistMultiplier)){ splashPos.z = waterLevel + 0.1f; CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashPos, 0.75f*splashDir, nil, splashSize+0.1f, splashColor, CGeneral::GetRandomNumberInRange(0.0f, 10.0f), CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1, lifeBase + splashDir.z*lifeMult); CParticle::AddParticle(PARTICLE_BOAT_SPLASH, splashPos, splashDir, nil, splashSize, jetColor, CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, lifeBase + splashDir.z*lifeMult); } switch(GetModelIndex()){ case MI_RIO: splashDir = -0.5f * m_vecMoveSpeed; splashDir.z += 0.25f*speed; splashDir -= 0.35f*speed*GetRight(); splashPos = GetPosition() - 1.85f*GetRight() + front*GetForward(); splashPos.z += 0.5f; break; case MI_SQUALO: splashDir = -0.125f * m_vecMoveSpeed; splashDir.z += 0.15f*speed; splashDir -= 0.25f*speed*GetRight(); splashPos = GetPosition() + m_vecBuoyancePoint - 0.5f*GetRight() + front*GetForward(); splashPos.z += 0.5f; break; case MI_REEFER: splashDir = -0.5f * m_vecMoveSpeed; splashDir.z += 0.15f*speed; splashDir -= 0.5f*speed*GetRight(); splashPos = GetPosition() + m_vecBuoyancePoint - 1.3f*GetRight() + front*GetForward(); break; case MI_COASTG: splashDir = 0.165f * m_vecMoveSpeed; splashDir.z += 0.25f*speed; splashDir -= 0.15f*speed*GetRight(); splashPos = GetPosition() - 0.65f*GetRight() + front*GetForward(); splashPos.z += 0.5f; break; case MI_DINGHY: splashDir = 0.35f * m_vecMoveSpeed; splashDir.z += 0.25f*speed; splashDir -= 0.25f*speed*GetRight(); splashPos = GetPosition() - 0.6f*GetRight() + front*GetForward(); splashPos.z += 0.5f; break; default: splashDir = -0.5f * m_vecMoveSpeed; splashDir.z += 0.25f*speed; splashDir -= 0.35f*speed*GetRight(); splashPos = GetPosition() + m_vecBuoyancePoint - 0.5f*GetRight() + front*GetForward(); break; } if(AutoPilot.m_nCarMission == MISSION_CRUISE) splashDir *= 1.5f; splashDir.z += 0.0003f*m_nDeltaVolumeUnderWater; CWaterLevel::GetWaterLevel(splashPos, &waterLevel, true); if(splashPos.z-waterLevel < 3.0f && CVisibilityPlugins::GetDistanceSquaredFromCamera(&splashPos) < SQR(70.0f * TheCamera.GenerationDistMultiplier)){ splashPos.z = waterLevel + 0.1f; CParticle::AddParticle(PARTICLE_CAR_SPLASH, splashPos, 0.75f*splashDir, nil, splashSize+0.1f, splashColor, CGeneral::GetRandomNumberInRange(0.0f, 10.0f), CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1, lifeBase + splashDir.z*lifeMult); CParticle::AddParticle(PARTICLE_BOAT_SPLASH, splashPos, splashDir, nil, splashSize, jetColor, CGeneral::GetRandomNumberInRange(0.0f, 0.4f), CGeneral::GetRandomNumberInRange(0.0f, 45.0f), 0, lifeBase + splashDir.z*lifeMult); } } // Spray waterdrops on screen if(TheCamera.GetLookingForwardFirstPerson() && FindPlayerVehicle() && FindPlayerVehicle()->IsBoat() && m_nDeltaVolumeUnderWater > 0 && numWaterDropOnScreen < 20){ CVector dropPos; CVector dropDir(CGeneral::GetRandomNumberInRange(-0.25f, 0.25f), CGeneral::GetRandomNumberInRange(1.0f, 0.75f), 0.0f); int frm = CGeneral::GetRandomNumber() & 1; if(TheCamera.m_CameraAverageSpeed < 0.35f){ dropPos.x = CGeneral::GetRandomNumberInRange(50, (int)SCREEN_WIDTH-50); dropPos.y = CGeneral::GetRandomNumberInRange(50, (int)SCREEN_HEIGHT-50); }else{ dropPos.x = CGeneral::GetRandomNumberInRange(200, (int)SCREEN_WIDTH-200); dropPos.y = CGeneral::GetRandomNumberInRange(150, (int)SCREEN_HEIGHT-150); } dropPos.z = 1.0f; if(TheCamera.m_CameraAverageSpeed > 0.35f){ if((int)SCREEN_WIDTH / 2 < dropPos.x) dropPos.x += CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; else dropPos.x -= CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; if((int)SCREEN_HEIGHT / 2 < dropPos.y) dropPos.y += CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; else dropPos.y -= CGeneral::GetRandomNumberInRange(0.35f, TheCamera.m_CameraAverageSpeed)*7.5f; } if(CParticle::AddParticle(PARTICLE_WATERDROP, dropPos, dropDir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.15f), dropColor, 0, 0, frm)) numWaterDropOnScreen++; } if(m_fPrevVolumeUnderWater == 0.0f && m_fVolumeUnderWater > 0.0f && GetModelIndex() == MI_SKIMMER){ CVector splashDir(0.0f, 0.0f, 0.25f*speed); CVector splashPos = GetPosition(); float level; CWaterLevel::GetWaterLevel(splashPos, &level, true); splashPos.z = level; CParticleObject::AddObject(POBJECT_CAR_WATER_SPLASH, splashPos, splashDir, 0.0f, 65, splashColor, true); } m_fPrevVolumeUnderWater = m_fVolumeUnderWater; }else{ bBoatInWater = false; bIsInWater = false; #ifdef FIX_BUGS bIsDrowning = false; #endif } if(m_modelIndex == MI_SKIMMER && CTimer::GetTimeStep() > 0.0f){ if(GetStatus() == STATUS_PLAYER){ if(m_fMovingSpeed < 0.22f) m_fMovingSpeed += 0.001f*CTimer::GetTimeStep(); FlyingControl(FLIGHT_MODEL_SEAPLANE); }else{ if(m_fMovingSpeed > 0.0005f*CTimer::GetTimeStep()) m_fMovingSpeed -= 0.0005f*CTimer::GetTimeStep(); else m_fMovingSpeed = 0.0f; } }else if(bCheat8) FlyingControl(FLIGHT_MODEL_PLANE); if(m_bIsAnchored){ m_vecMoveSpeed.x = 0.0f; m_vecMoveSpeed.y = 0.0f; if(m_fOrientation == INVALID_ORIENTATION){ m_fOrientation = GetForward().Heading(); }else{ // is this some inlined CPlaceable method? CVector pos = GetPosition(); GetMatrix().RotateZ(m_fOrientation - GetForward().Heading()); GetMatrix().SetTranslateOnly(pos); } } ProcessDelayedExplosion(); } void CBoat::ProcessControlInputs(uint8 pad) { m_nPadID = pad; if(m_nPadID > 3) m_nPadID = 3; m_fBrake += (CPad::GetPad(pad)->GetBrake()/255.0f - m_fBrake)*0.1f; m_fBrake = clamp(m_fBrake, 0.0f, 1.0f); if(m_fBrake < 0.05f){ m_fBrake = 0.0f; m_fAccelerate += (CPad::GetPad(pad)->GetAccelerate()/255.0f - m_fAccelerate)*0.1f; m_fAccelerate = clamp(m_fAccelerate, 0.0f, 1.0f); }else m_fAccelerate = -m_fBrake*0.3f; m_fSteeringLeftRight += (-CPad::GetPad(pad)->GetSteeringLeftRight()/128.0f - m_fSteeringLeftRight)*0.2f; m_fSteeringLeftRight = clamp(m_fSteeringLeftRight, -1.0f, 1.0f); float steeringSq = m_fSteeringLeftRight < 0.0f ? -SQR(m_fSteeringLeftRight) : SQR(m_fSteeringLeftRight); m_fSteerAngle = pHandling->fSteeringLock * DEGTORAD(steeringSq); m_fGasPedal = m_fAccelerate; } float fSeaPlaneWaterResistance = 30.0f; void CBoat::ApplyWaterResistance(void) { // TODO: figure out how this works float resistance = 0.001f * pHandling->fSuspensionForceLevel * SQR(m_fVolumeUnderWater) * m_fMass; if(GetModelIndex() == MI_SKIMMER) resistance *= fSeaPlaneWaterResistance; float fwdSpeed = DotProduct(GetMoveSpeed(), GetForward()); float magic = (SQR(fwdSpeed) + 0.05f) * resistance + 1.0f; magic = Abs(magic); float fx = Pow(pBoatHandling->vecMoveRes.x/magic, 0.5f*CTimer::GetTimeStep()); float fy = Pow(pBoatHandling->vecMoveRes.y/magic, 0.5f*CTimer::GetTimeStep()); float fz = Pow(pBoatHandling->vecMoveRes.z/magic, 0.5f*CTimer::GetTimeStep()); m_vecMoveSpeed = Multiply3x3(m_vecMoveSpeed, GetMatrix()); // invert - to local space m_vecMoveSpeed.x *= fx; m_vecMoveSpeed.y *= fy; m_vecMoveSpeed.z *= fz; float force = (fy - 1.0f) * m_vecMoveSpeed.y * m_fMass; m_vecMoveSpeed = Multiply3x3(GetMatrix(), m_vecMoveSpeed); // back to world ApplyTurnForce(force*GetForward(), -GetUp()); if(m_vecMoveSpeed.z > 0.0f) m_vecMoveSpeed.z *= fz; else m_vecMoveSpeed.z *= (1.0f - fz)*0.5f + fz; } RwObject* GetBoatAtomicObjectCB(RwObject *object, void *data) { RpAtomic *atomic = (RpAtomic*)object; assert(RwObjectGetType(object) == rpATOMIC); if(RpAtomicGetFlags(atomic) & rpATOMICRENDER) *(RpAtomic**)data = atomic; return object; } void CBoat::BlowUpCar(CEntity *culprit) { RpAtomic *atomic; RwFrame *frame; RwMatrix *matrix; CObject *obj; if(!bCanBeDamaged) return; // explosion pushes vehicle up m_vecMoveSpeed.z += 0.13f; SetStatus(STATUS_WRECKED); bRenderScorched = true; m_fHealth = 0.0; m_nBombTimer = 0; TheCamera.CamShake(0.7f, GetPosition().x, GetPosition().y, GetPosition().z); KillPedsInVehicle(); bEngineOn = false; bLightsOn = false; ChangeLawEnforcerState(false); CExplosion::AddExplosion(this, culprit, EXPLOSION_BOAT, GetPosition(), 0); CDarkel::RegisterCarBlownUpByPlayer(this); if(m_aBoatNodes[BOAT_MOVING] == nil) return; // much like CAutomobile::SpawnFlyingComponent from here on atomic = nil; RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic); if(atomic == nil) return; obj = new CObject(); if(obj == nil) return; obj->SetModelIndexNoCreate(MI_CAR_WHEEL); // object needs base model obj->RefModelInfo(GetModelIndex()); // create new atomic matrix = RwFrameGetLTM(m_aBoatNodes[BOAT_MOVING]); frame = RwFrameCreate(); atomic = RpAtomicClone(atomic); *RwFrameGetMatrix(frame) = *matrix; RpAtomicSetFrame(atomic, frame); CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil); obj->AttachToRwObject((RwObject*)atomic); obj->bDontStream = true; // 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->SetIsStatic(false); obj->bIsPickup = false; // life time CObject::nNoTempObjects++; obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 20000; obj->m_vecMoveSpeed = m_vecMoveSpeed; if(GetUp().z > 0.0f) obj->m_vecMoveSpeed.z = 0.3f; else obj->m_vecMoveSpeed.z = 0.0f; obj->m_vecTurnSpeed = m_vecTurnSpeed*2.0f; obj->m_vecTurnSpeed.x = 0.5f; // push component away from boat CVector dist = obj->GetPosition() - GetPosition(); dist.Normalise(); if(GetUp().z > 0.0f) dist += GetUp(); obj->GetMatrix().GetPosition() += dist; CWorld::Add(obj); atomic = nil; RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic); if(atomic) RpAtomicSetFlags(atomic, 0); } void CBoat::PreRender(void) { CMatrix matrix; CVector pos; RpAtomic *atomic; if(GetModelIndex() == MI_SKIMMER){ m_fMovingRotation += m_fMovingSpeed*CTimer::GetTimeStep(); if(m_fMovingRotation > TWOPI) m_fMovingRotation -= TWOPI; int alpha = (1.0f - Min(2.0f*m_fMovingSpeed*8.0f/PI, 1.0f))*255.0f; if(GetStatus() == STATUS_PLAYER || GetStatus() == STATUS_PLAYER_REMOTE || GetStatus() == STATUS_PLAYER_PLAYBACKFROMBUFFER){ if(m_aBoatNodes[BOAT_RUDDER]){ float sine = Sin(m_fSteerAngle); matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_RUDDER])); pos = matrix.GetPosition(); matrix.SetRotate(0.0f, 0.0f, -m_fSteerAngle); matrix.Rotate(0.0f, DEGTORAD(22.0f)*sine, 0.0f); matrix.Translate(pos); matrix.UpdateRW(); } if(m_aBoatNodes[BOAT_FLAP_LEFT]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_LEFT])); pos = matrix.GetPosition(); matrix.SetRotateX(-m_fSteerAngle); matrix.Translate(pos); matrix.UpdateRW(); } if(m_aBoatNodes[BOAT_FLAP_RIGHT]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_RIGHT])); pos = matrix.GetPosition(); matrix.SetRotateX(m_fSteerAngle); matrix.Translate(pos); matrix.UpdateRW(); } // FIX: Planes can also be controlled with GetCarGunUpDown #ifdef FIX_BUGS static float steeringUpDown = 0.0f; #ifdef FREE_CAM if(!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) #endif steeringUpDown += ((Abs(CPad::GetPad(0)->GetCarGunUpDown()) > 1.0f ? (-CPad::GetPad(0)->GetCarGunUpDown()/128.0f) : (-CPad::GetPad(0)->GetSteeringUpDown()/128.0f)) - steeringUpDown) * Min(1.f, CTimer::GetTimeStep()/5.f); #ifdef FREE_CAM else steeringUpDown = -CPad::GetPad(0)->GetSteeringUpDown()/128.0f; #endif #else float steeringUpDown = -CPad::GetPad(0)->GetSteeringUpDown()/128.0f; #endif if(m_aBoatNodes[BOAT_REARFLAP_LEFT]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_LEFT])); pos = matrix.GetPosition(); matrix.SetRotateX(steeringUpDown); matrix.Translate(pos); matrix.UpdateRW(); } if(m_aBoatNodes[BOAT_REARFLAP_RIGHT]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_RIGHT])); pos = matrix.GetPosition(); matrix.SetRotateX(steeringUpDown); matrix.Translate(pos); matrix.UpdateRW(); } } if(m_aBoatNodes[BOAT_MOVING]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); pos = matrix.GetPosition(); matrix.SetRotateY(m_fMovingRotation); matrix.Translate(pos); matrix.UpdateRW(); atomic = nil; RwFrameForAllObjects(m_aBoatNodes[BOAT_MOVING], GetBoatAtomicObjectCB, &atomic); if(atomic) SetComponentAtomicAlpha(atomic, alpha); } if(m_aBoatNodes[BOAT_WINDSCREEN]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_WINDSCREEN])); pos = matrix.GetPosition(); matrix.SetRotateY(-m_fMovingRotation); matrix.Translate(pos); matrix.UpdateRW(); atomic = nil; RwFrameForAllObjects(m_aBoatNodes[BOAT_WINDSCREEN], GetBoatAtomicObjectCB, &atomic); if(atomic) SetComponentAtomicAlpha(atomic, Max(150-alpha, 0)); } CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_SEAPLANE); }else if(GetModelIndex() == MI_COASTG || GetModelIndex() == MI_DINGHY || GetModelIndex() == MI_RIO || GetModelIndex() == MI_SQUALO || GetModelIndex() == MI_MARQUIS){ if(m_aBoatNodes[BOAT_RUDDER]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_RUDDER])); pos = matrix.GetPosition(); matrix.SetRotateZ(-m_fSteerAngle); matrix.Translate(pos); matrix.UpdateRW(); } if(m_aBoatNodes[BOAT_REARFLAP_LEFT]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_LEFT])); pos = matrix.GetPosition(); matrix.SetRotateZ(-m_fSteerAngle); matrix.Translate(pos); matrix.UpdateRW(); } if(m_aBoatNodes[BOAT_REARFLAP_RIGHT]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_REARFLAP_RIGHT])); pos = matrix.GetPosition(); matrix.SetRotateZ(-m_fSteerAngle); matrix.Translate(pos); matrix.UpdateRW(); } } if(GetModelIndex() == MI_RIO || GetModelIndex() == MI_MARQUIS){ float axes[3] = { 0.0f, 0.0f, 0.0f }; m_boom.Process(this); axes[m_boom.m_nAxis] = m_boom.m_fAngle; matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_LEFT])); pos = matrix.GetPosition(); matrix.SetRotate(axes[0], axes[1], axes[2]); matrix.Translate(pos); matrix.UpdateRW(); } if(GetModelIndex() == MI_RIO){ // That little wind propeller if(m_aBoatNodes[BOAT_FLAP_RIGHT]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_FLAP_RIGHT])); pos = matrix.GetPosition(); float flapHeading = matrix.GetForward().Heading(); float boatHeading = GetForward().Heading(); float rot = -DEGTORAD(45.0f) - (flapHeading + boatHeading); // eh what? rot = CGeneral::LimitRadianAngle(rot); if(rot > HALFPI) rot = PI; else if(rot < -HALFPI) rot = -PI; rot = clamp(rot, -DEGTORAD(63.0f), DEGTORAD(63.0f)); m_fMovingSpeed += (0.008f * CWeather::Wind + 0.002f) * rot; m_fMovingSpeed *= Pow(0.9985f, CTimer::GetTimeStep())/(500.0f*SQR(m_fMovingSpeed) + 1.0f); matrix.SetRotateZ(flapHeading + m_fMovingSpeed*CTimer::GetTimeStep()); matrix.Translate(pos); matrix.UpdateRW(); } if(m_aBoatNodes[BOAT_MOVING]){ matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); pos = matrix.GetPosition(); matrix.SetRotateY(m_fMovingRotation); matrix.Translate(pos); matrix.UpdateRW(); CVector wind = CVector(0.707f, 0.707f, 0.0f) * (CWeather::Wind + 0.15f)*0.4f; m_fMovingRotation += (m_vecMoveSpeed + wind).Magnitude()*CTimer::GetTimeStep(); } }else if(GetModelIndex() == MI_PREDATOR || GetModelIndex() == MI_REEFER){ if (m_aBoatNodes[BOAT_MOVING] != nil) { matrix.Attach(RwFrameGetMatrix(m_aBoatNodes[BOAT_MOVING])); CVector pos = matrix.GetPosition(); matrix.SetRotateZ(m_fMovingRotation); matrix.Translate(pos); matrix.UpdateRW(); if (CVehicle::bWheelsOnlyCheat) { RpAtomicRender((RpAtomic*)GetFirstObject(m_aBoatNodes[BOAT_MOVING])); } } m_fMovingRotation += 0.02f * CTimer::GetTimeStep(); } } RwIm3DVertex KeepWaterOutVertices[4]; RwImVertexIndex KeepWaterOutIndices[6]; void CBoat::Render() { ((CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()))->SetVehicleColour(m_currentColour1, m_currentColour2); m_nSetPieceExtendedRangeTime = CTimer::GetTimeInMilliseconds() + 3000; if (!CVehicle::bWheelsOnlyCheat) CEntity::Render(); #ifdef NEW_RENDERER if(!gbNewRenderer) #endif RenderWaterOutPolys(); // not separate function in VC } void CBoat::RenderWaterOutPolys(void) { if(GetModelIndex() == MI_SKIMMER) return; KeepWaterOutIndices[0] = 0; KeepWaterOutIndices[1] = 2; KeepWaterOutIndices[2] = 1; KeepWaterOutIndices[3] = 1; KeepWaterOutIndices[4] = 2; KeepWaterOutIndices[5] = 3; RwIm3DVertexSetRGBA(&KeepWaterOutVertices[0], 255, 255, 255, 255); RwIm3DVertexSetRGBA(&KeepWaterOutVertices[1], 255, 255, 255, 255); RwIm3DVertexSetRGBA(&KeepWaterOutVertices[2], 255, 255, 255, 255); RwIm3DVertexSetRGBA(&KeepWaterOutVertices[3], 255, 255, 255, 255); switch (GetModelIndex()) { case MI_RIO: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.3f, -1.016f, 0.51f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.3f, -1.016f, 0.51f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.3f, -2.832f, 0.51f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.3f, -2.832f, 0.51f); break; case MI_SQUALO: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.222f, 2.004f, 0.846f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.222f, 2.004f, 0.846f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.24f, -1.367f, 0.846f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.24f, -1.367f, 0.846f); break; case MI_SPEEDER: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.15f, 3.61f, 1.03f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.15f, 3.61f, 1.03f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.15f, 0.06f, 1.03f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.15f, 0.06f, 1.03f); break; case MI_REEFER: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.9f, 2.83f, 1.0f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.9f, 2.83f, 1.0f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.66f, -4.48f, 0.83f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.66f, -4.48f, 0.83f); break; case MI_PREDATOR: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.45f, 1.9f, 0.96f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.45f, 1.9f, 0.96f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.45f, -3.75f, 0.96f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.45f, -3.75f, 0.96f); break; case MI_TROPIC: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.886f, -2.347f, 0.787f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.886f, -2.347f, 0.787f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.886f, -4.67f, 0.842f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.886f, -4.67f, 0.842f); break; case MI_COASTG: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -0.663f, 3.565f, 0.382f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 0.663f, 3.565f, 0.382f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.087f, 0.83f, 0.381f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.087f, 0.83f, 0.381f); break; case MI_DINGHY: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -0.797f, 1.641f, 0.573f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 0.797f, 1.641f, 0.573f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -0.865f, -1.444f, 0.509f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 0.865f, -1.444f, 0.509f); break; case MI_MARQUIS: RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.246f, -1.373f, 0.787f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.246f, -1.373f, 0.787f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.023f, -5.322f, 0.787f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.023f, -5.322f, 0.787f); break; default: return; } KeepWaterOutVertices[0].u = 0.0f; KeepWaterOutVertices[0].v = 0.0f; KeepWaterOutVertices[1].u = 1.0f; KeepWaterOutVertices[1].v = 0.0f; KeepWaterOutVertices[2].u = 0.0f; KeepWaterOutVertices[2].v = 1.0f; KeepWaterOutVertices[3].u = 1.0f; KeepWaterOutVertices[3].v = 1.0f; #ifdef NEW_RENDERER if(!gbNewRenderer) #endif { RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpWaterRaster); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); } if (!CVehicle::bWheelsOnlyCheat && RwIm3DTransform(KeepWaterOutVertices, 4, GetMatrix().m_attachment, rwIM3D_VERTEXUV)) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, KeepWaterOutIndices, 6); RwIm3DEnd(); } bool drawAnotherRect = false; if(GetModelIndex() == MI_COASTG){ drawAnotherRect = true; RwIm3DVertexSetPos(&KeepWaterOutVertices[0], -1.087f, 0.831f, 0.381f); RwIm3DVertexSetPos(&KeepWaterOutVertices[1], 1.087f, 0.831f, 0.381f); RwIm3DVertexSetPos(&KeepWaterOutVertices[2], -1.097f, -2.977f, 0.381f); RwIm3DVertexSetPos(&KeepWaterOutVertices[3], 1.097f, -2.977f, 0.381f); } if(drawAnotherRect){ KeepWaterOutVertices[0].u = 0.0f; KeepWaterOutVertices[0].v = 0.0f; KeepWaterOutVertices[1].u = 1.0f; KeepWaterOutVertices[1].v = 0.0f; KeepWaterOutVertices[2].u = 0.0f; KeepWaterOutVertices[2].v = 1.0f; KeepWaterOutVertices[3].u = 1.0f; KeepWaterOutVertices[3].v = 1.0f; if (!CVehicle::bWheelsOnlyCheat && RwIm3DTransform(KeepWaterOutVertices, 4, GetMatrix().m_attachment, rwIM3D_VERTEXUV)) { RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, KeepWaterOutIndices, 6); RwIm3DEnd(); } } #ifdef NEW_RENDERER if(!gbNewRenderer) #endif { RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); } } void CBoat::Teleport(CVector v) { CWorld::Remove(this); SetPosition(v); SetOrientation(0.0f, 0.0f, 0.0f); SetMoveSpeed(0.0f, 0.0f, 0.0f); SetTurnSpeed(0.0f, 0.0f, 0.0f); CWorld::Add(this); } // unused bool CBoat::IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats) { uint8 numVerts = 0; if ( apFrameWakeGeneratingBoats[0] == NULL ) return false; for ( int32 i = 0; i < 4; i++ ) { CBoat *pBoat = apFrameWakeGeneratingBoats[i]; if ( !pBoat ) break; for ( int j = 0; j < pBoat->m_nNumWakePoints; j++ ) { float fDist = (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[j]) * fShapeTime + float(j) * fShapeLength + fSize; if ( Abs(pBoat->m_avec2dWakePoints[j].x - sector.x) < fDist && Abs(pBoat->m_avec2dWakePoints[i].y - sector.y) < fDist ) { apBoats[numVerts] = pBoat; numVerts = 1; // += ? break; } } } return numVerts != 0; } // unused float CBoat::IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat) { for ( int i = 0; i < pBoat->m_nNumWakePoints; i++ ) { float fMaxDist = (WAKE_LIFETIME - pBoat->m_afWakePointLifeTime[i]) * fShapeTime + float(i) * fShapeLength; CVector2D vecDist = pBoat->m_avec2dWakePoints[i] - CVector2D(vecVertex); 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 0.0f; } void CBoat::SetupModelNodes() { int i; for(i = 0; i < ARRAY_SIZE(m_aBoatNodes); i++) m_aBoatNodes[i] = nil; CClumpModelInfo::FillFrameArray(GetClump(), m_aBoatNodes); } void CBoat::FillBoatList() { int16 frameId = 0; apFrameWakeGeneratingBoats[0] = nil; apFrameWakeGeneratingBoats[1] = nil; apFrameWakeGeneratingBoats[2] = nil; apFrameWakeGeneratingBoats[3] = nil; CVector2D camPos = TheCamera.GetPosition(); CVector2D camFwd = TheCamera.GetForward(); float camDist = camFwd.Magnitude(); if(camDist > 0.0f) camFwd /= camDist; for (int i = CPools::GetVehiclePool()->GetSize() - 1; i >= 0; i--) { CBoat *boat = (CBoat *)(CPools::GetVehiclePool()->GetSlot(i)); if (boat && boat->m_vehType == VEHICLE_TYPE_BOAT) { if (boat->m_nNumWakePoints != 0) { CVector2D camToBoat = CVector2D(boat->GetPosition()) - camPos; float distToCam = DotProduct2D(camFwd, camToBoat); if(distToCam > 100.0f || distToCam < -15.0f) continue; float distSq = camToBoat.MagnitudeSqr(); if(distSq > SQR(70.0f)) continue; if (frameId >= ARRAY_SIZE(apFrameWakeGeneratingBoats)) { float nearest = 999999.88f; int16 frameId2 = -1; for (int16 j = 0; j < ARRAY_SIZE(apFrameWakeGeneratingBoats); j++) { float tmpDistSq = (CVector2D(apFrameWakeGeneratingBoats[j]->GetPosition()) - camPos).MagnitudeSqr(); if (tmpDistSq < nearest) { nearest = tmpDistSq; frameId2 = j; } } if (frameId2 != -1 && (distSq < nearest || boat->GetStatus() == STATUS_PLAYER)) apFrameWakeGeneratingBoats[frameId2] = boat; } else { apFrameWakeGeneratingBoats[frameId++] = boat; } } } } } void CBoat::PruneWakeTrail(void) { int i; for(i = 0; i < ARRAY_SIZE(m_afWakePointLifeTime); i++){ if(m_afWakePointLifeTime[i] <= 0.0f) break; if(m_afWakePointLifeTime[i] <= CTimer::GetTimeStep()){ m_afWakePointLifeTime[i] = 0.0f; break; } m_afWakePointLifeTime[i] -= CTimer::GetTimeStep(); } m_nNumWakePoints = i; } void CBoat::AddWakePoint(CVector point) { int i; if(m_afWakePointLifeTime[0] > 0.0f){ if((CVector2D(GetPosition()) - m_avec2dWakePoints[0]).MagnitudeSqr() < SQR(2.0f)) { if(GetStatus() == STATUS_PLAYER){ if(m_nNumWakePoints >= 31) m_nNumWakePoints = 31; }else if(VehicleCreatedBy == MISSION_VEHICLE){ if(m_nNumWakePoints >= 20) m_nNumWakePoints = 20; }else{ if(m_nNumWakePoints >= 15) m_nNumWakePoints = 15; } for(i = m_nNumWakePoints; i != 0; i--){ m_avec2dWakePoints[i] = m_avec2dWakePoints[i-1]; m_afWakePointLifeTime[i] = m_afWakePointLifeTime[i-1]; } m_avec2dWakePoints[0] = point; m_afWakePointLifeTime[0] = 150.0f; if(m_nNumWakePoints < ARRAY_SIZE(m_afWakePointLifeTime)) m_nNumWakePoints++; } }else{ m_avec2dWakePoints[0] = point; m_afWakePointLifeTime[0] = 150.0f; m_nNumWakePoints = 1; } } void CBoat::DoDriveByShootings(void) { CAnimBlendAssociation *anim = nil; CPlayerInfo* playerInfo = ((CPlayerPed*)pDriver)->GetPlayerInfoForThisPlayerPed(); if (playerInfo && !playerInfo->m_bDriveByAllowed) return; CWeapon *weapon = pDriver->GetWeapon(); if(CWeaponInfo::GetWeaponInfo(weapon->m_eWeaponType)->m_nWeaponSlot != 5) return; weapon->Update(pDriver->m_audioEntityId, nil); bool lookingLeft = false; bool lookingRight = false; if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || TheCamera.m_bObbeCinematicCarCamOn){ 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_STD_CAR_DRIVEBY_RIGHT); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT); if(anim == nil || anim->blendDelta < 0.0f) anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVEBY_LEFT); }else if(pDriver->m_pMyVehicle->pPassengers[0] == nil || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON){ anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_RIGHT); if(anim == nil || anim->blendDelta < 0.0f) anim = CAnimManager::AddAnimation(pDriver->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_DRIVEBY_RIGHT); } if (!anim || !anim->IsRunning()) { if (CPad::GetPad(0)->GetCarGunFired() && CTimer::GetTimeInMilliseconds() > weapon->m_nTimer) { weapon->FireFromCar(this, lookingLeft, true); weapon->m_nTimer = CTimer::GetTimeInMilliseconds() + 70; } } }else{ weapon->Reload(); anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_LEFT); if(anim) anim->blendDelta = -1000.0f; anim = RpAnimBlendClumpGetAssociation(pDriver->GetClump(), ANIM_STD_CAR_DRIVEBY_RIGHT); if(anim) anim->blendDelta = -1000.0f; } } #ifdef COMPATIBLE_SAVES void CBoat::Save(uint8*& buf) { CVehicle::Save(buf); SkipSaveBuf(buf, 1216 - 672); } void CBoat::Load(uint8*& buf) { CVehicle::Load(buf); SkipSaveBuf(buf, 1216 - 672); } #endif ================================================ FILE: src/vehicles/Boat.h ================================================ #pragma once #include "Vehicle.h" #include "Door.h" enum eBoatNodes { BOAT_MOVING = 1, BOAT_WINDSCREEN, BOAT_RUDDER, BOAT_FLAP_LEFT, BOAT_FLAP_RIGHT, BOAT_REARFLAP_LEFT, BOAT_REARFLAP_RIGHT, NUM_BOAT_NODES }; class CBoat : public CVehicle { public: float m_fMovingRotation; float m_fMovingSpeed; int32 m_boat_unused1; RwFrame *m_aBoatNodes[NUM_BOAT_NODES]; CDoor m_boom; tBoatHandlingData *pBoatHandling; uint8 bBoatInWater : 1; uint8 bPropellerInWater : 1; bool m_bIsAnchored; float m_fOrientation; uint32 m_nPoliceShoutTimer; int32 m_boat_unused2; float m_fDamage; CEntity *m_pSetOnFireEntity; float m_skimmerThingTimer; bool m_boat_unused3; float m_fAccelerate; float m_fBrake; float m_fSteeringLeftRight; uint8 m_nPadID; int32 m_boat_unused4; float m_fVolumeUnderWater; CVector m_vecBuoyancePoint; float m_fPrevVolumeUnderWater; int16 m_nDeltaVolumeUnderWater; uint16 m_nNumWakePoints; CVector2D m_avec2dWakePoints[32]; float m_afWakePointLifeTime[32]; static float MAX_WAKE_LENGTH; static float MIN_WAKE_INTERVAL; static float WAKE_LIFETIME; CBoat(int, uint8); virtual void SetModelIndex(uint32 id); virtual void ProcessControl(); virtual void Teleport(CVector v); virtual void PreRender(void); virtual void Render(void); virtual void ProcessControlInputs(uint8); virtual void GetComponentWorldPosition(int32 component, CVector &pos); virtual bool IsComponentPresent(int32 component) { return true; } virtual void BlowUpCar(CEntity *ent); void RenderWaterOutPolys(void); void ApplyWaterResistance(void); void SetupModelNodes(); void PruneWakeTrail(void); void AddWakePoint(CVector point); void DoDriveByShootings(void); static CBoat *apFrameWakeGeneratingBoats[4]; static bool IsSectorAffectedByWake(CVector2D sector, float fSize, CBoat **apBoats); static float IsVertexAffectedByWake(CVector vecVertex, CBoat *pBoat); static void FillBoatList(void); #ifdef COMPATIBLE_SAVES virtual void Save(uint8*& buf); virtual void Load(uint8*& buf); #endif static const uint32 nSaveStructSize; }; ================================================ FILE: src/vehicles/CarGen.cpp ================================================ #include "common.h" #include "CarGen.h" #include "Automobile.h" #include "Bike.h" #include "Boat.h" #include "Camera.h" #include "CarCtrl.h" #include "CutsceneMgr.h" #include "General.h" #include "Pools.h" #include "Streaming.h" #include "Timer.h" #include "Vehicle.h" #include "VisibilityPlugins.h" #include "World.h" #include "Zones.h" #include "Occlusion.h" uint8 CTheCarGenerators::ProcessCounter; uint32 CTheCarGenerators::NumOfCarGenerators; CCarGenerator CTheCarGenerators::CarGeneratorArray[NUM_CARGENS]; uint8 CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter; uint32 CTheCarGenerators::CurrentActiveCount; void CCarGenerator::SwitchOff() { #ifdef FIX_BUGS if (m_nUsesRemaining != 0) #endif { m_nUsesRemaining = 0; --CTheCarGenerators::CurrentActiveCount; } } void CCarGenerator::SwitchOn() { m_nUsesRemaining = UINT16_MAX; m_nTimer = CalcNextGen(); ++CTheCarGenerators::CurrentActiveCount; } uint32 CCarGenerator::CalcNextGen() { return CTimer::GetTimeInMilliseconds() + 4; } void CCarGenerator::DoInternalProcessing() { int mi; if (CCarCtrl::NumParkedCars >= 10) return; if (m_nModelIndex >= 0) { if (CheckForBlockage(m_nModelIndex)) { m_nTimer += 4; return; } CStreaming::RequestModel(m_nModelIndex, STREAMFLAGS_DEPENDENCY); mi = m_nModelIndex; } else { mi = -m_nModelIndex; if (m_nModelIndex == -1 || !CStreaming::HasModelLoaded(mi)) { CZoneInfo pZone; CVector pos = FindPlayerCoors(); CTheZones::GetZoneInfoForTimeOfDay(&pos, &pZone); mi = CCarCtrl::ChooseCarModel(CCarCtrl::ChooseCarRating(&pZone)); if (mi < 0) return; m_nModelIndex = -mi; m_nColor1 = -1; m_nColor2 = -1; } if (CheckForBlockage(mi)) { m_nTimer += 4; return; } } if (!CStreaming::HasModelLoaded(mi)) return; CVehicle* pVehicle; CVector pos; if (CModelInfo::IsBoatModel(mi)){ CBoat* pBoat = new CBoat(mi, PARKED_VEHICLE); pos = m_vecPos; pVehicle = pBoat; if (pos.z <= -100.0f) pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); pBoat->bExtendedRange = true; }else{ bool groundFound; pos = m_vecPos; if (pos.z > -100.0f){ pos.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, &groundFound); }else{ groundFound = false; CColPoint cp; CEntity* pEntity; groundFound = CWorld::ProcessVerticalLine(CVector(pos.x, pos.y, 1000.0f), -1000.0f, cp, pEntity, true, false, false, false, false, false, nil); if (groundFound) pos.z = cp.point.z; } if (!groundFound) { debug("CCarGenerator::DoInternalProcessing - can't find ground z for new car x = %f y = %f \n", m_vecPos.x, m_vecPos.y); return; } if (((CVehicleModelInfo*)CModelInfo::GetModelInfo(mi))->m_vehicleType == VEHICLE_TYPE_BIKE) { CBike* pBike = new CBike(mi, PARKED_VEHICLE); pBike->bIsStanding = true; pVehicle = pBike; } else { CAutomobile* pCar = new CAutomobile(mi, PARKED_VEHICLE); pVehicle = pCar; } // pVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); pVehicle->bLightsOn = false; } pVehicle->bIsStatic = false; pVehicle->bEngineOn = false; pos.z += pVehicle->GetDistanceFromCentreOfMassToBaseOfModel(); pVehicle->SetPosition(pos); pVehicle->SetOrientation(0.0f, 0.0f, DEGTORAD(m_fAngle)); pVehicle->SetStatus(STATUS_ABANDONED); pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; CWorld::Add(pVehicle); if (CGeneral::GetRandomNumberInRange(0, 100) < m_nAlarm) pVehicle->m_nAlarmState = -1; if (CGeneral::GetRandomNumberInRange(0, 100) < m_nDoorlock) pVehicle->m_nDoorLock = CARLOCK_LOCKED; if (m_nColor1 != -1 && m_nColor2 != -1) { pVehicle->m_currentColour1 = m_nColor1; pVehicle->m_currentColour2 = m_nColor2; } else if (m_nModelIndex < -1) { m_nColor1 = pVehicle->m_currentColour1; m_nColor2 = pVehicle->m_currentColour2; } CVisibilityPlugins::SetClumpAlpha(pVehicle->GetClump(), 0); m_nVehicleHandle = CPools::GetVehiclePool()->GetIndex(pVehicle); /* I don't think this is a correct comparasion */ #ifdef FIX_BUGS if (m_nUsesRemaining < UINT16_MAX) --m_nUsesRemaining; #else if (m_nUsesRemaining < ~0) --m_nUsesRemaining; #endif m_nTimer = CalcNextGen(); if (m_nUsesRemaining == 0) --CTheCarGenerators::CurrentActiveCount; } void CCarGenerator::Process() { if (m_nVehicleHandle == -1 && (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter || CTimer::GetTimeInMilliseconds() >= m_nTimer) && m_nUsesRemaining != 0 && CheckIfWithinRangeOfAnyPlayers()) DoInternalProcessing(); if (m_nVehicleHandle == -1) return; CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(m_nVehicleHandle); if (!pVehicle){ m_nVehicleHandle = -1; return; } if (pVehicle->GetStatus() != STATUS_PLAYER) return; m_nTimer += 60000; m_nVehicleHandle = -1; m_bIsBlocking = true; pVehicle->bExtendedRange = false; if (m_nModelIndex < 0) m_nModelIndex = -1; } void CCarGenerator::Setup(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay) { CMatrix m1, m2, m3; /* Unused but present on stack, so I'll leave them. */ m_vecPos = CVector(x, y, z); m_fAngle = angle; m_nModelIndex = mi; m_nColor1 = color1; m_nColor2 = color2; m_bForceSpawn = force; m_nAlarm = alarm; m_nDoorlock = lock; m_nMinDelay = min_delay; m_nMaxDelay = max_delay; m_nVehicleHandle = -1; m_nTimer = CTimer::GetTimeInMilliseconds() + 1; m_nUsesRemaining = 0; m_bIsBlocking = false; } bool CCarGenerator::CheckForBlockage(int32 mi) { int16 entities; CEntity* pEntities[8]; CColModel* pColModel = CModelInfo::GetModelInfo(mi)->GetColModel(); CWorld::FindObjectsKindaColliding(CVector(m_vecPos), pColModel->boundingSphere.radius, 1, &entities, 8, pEntities, false, true, true, false, false); for (int i = 0; i < entities; i++) { if (m_vecPos.z + pColModel->boundingBox.min.z < pEntities[i]->GetPosition().z + pEntities[i]->GetColModel()->boundingBox.max.z + 1.0f && m_vecPos.z + pColModel->boundingBox.max.z > pEntities[i]->GetPosition().z + pEntities[i]->GetColModel()->boundingBox.min.z - 1.0f) { m_bIsBlocking = true; return true; } } return false; } bool CCarGenerator::CheckIfWithinRangeOfAnyPlayers() { CVector2D direction = FindPlayerCentreOfWorld(CWorld::PlayerInFocus) - m_vecPos; float distance = direction.Magnitude(); float farclip = 110.0f * TheCamera.GenerationDistMultiplier; float nearclip = farclip - 20.0f; bool canBeRemoved = (m_nModelIndex > 0 && CModelInfo::IsBoatModel(m_nModelIndex) && 165.0f * TheCamera.GenerationDistMultiplier > distance && TheCamera.IsSphereVisible(m_vecPos, 0.0f) && !COcclusion::IsPositionOccluded(m_vecPos, 0.0f)); if (distance >= farclip && !canBeRemoved){ if (m_bIsBlocking) m_bIsBlocking = false; return false; } if (CTheCarGenerators::GenerateEvenIfPlayerIsCloseCounter) return true; if (m_bIsBlocking) return false; if (distance < nearclip && !m_bForceSpawn) return false; return DotProduct2D(direction, FindPlayerSpeed()) <= 0; } void CTheCarGenerators::Process() { if (FindPlayerTrain() || CCutsceneMgr::IsCutsceneProcessing()) return; if (++CTheCarGenerators::ProcessCounter == 4) CTheCarGenerators::ProcessCounter = 0; for (uint32 i = ProcessCounter; i < NumOfCarGenerators; i += 4) CTheCarGenerators::CarGeneratorArray[i].Process(); if (GenerateEvenIfPlayerIsCloseCounter) GenerateEvenIfPlayerIsCloseCounter--; } int32 CTheCarGenerators::CreateCarGenerator(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay) { if (NumOfCarGenerators < NUM_CARGENS) CarGeneratorArray[NumOfCarGenerators++].Setup(x, y, z, angle, mi, color1, color2, force, alarm, lock, min_delay, max_delay); return NumOfCarGenerators - 1; } void CTheCarGenerators::Init() { GenerateEvenIfPlayerIsCloseCounter = 0; NumOfCarGenerators = 0; ProcessCounter = 0; CurrentActiveCount = 0; } void CTheCarGenerators::SaveAllCarGenerators(uint8 *buffer, uint32 *size) { const uint32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16); *size = sizeof(int) + nGeneralDataSize + sizeof(uint32) + sizeof(CarGeneratorArray) + SAVE_HEADER_SIZE; INITSAVEBUF WriteSaveHeader(buffer, 'C','G','N','\0', *size - SAVE_HEADER_SIZE); WriteSaveBuf(buffer, nGeneralDataSize); WriteSaveBuf(buffer, NumOfCarGenerators); WriteSaveBuf(buffer, CurrentActiveCount); WriteSaveBuf(buffer, ProcessCounter); WriteSaveBuf(buffer, GenerateEvenIfPlayerIsCloseCounter); WriteSaveBuf(buffer, (int16)0); // alignment WriteSaveBuf(buffer, (uint32)sizeof(CarGeneratorArray)); for (int i = 0; i < NUM_CARGENS; i++) WriteSaveBuf(buffer, CarGeneratorArray[i]); VALIDATESAVEBUF(*size) } void CTheCarGenerators::LoadAllCarGenerators(uint8* buffer, uint32 size) { NumOfCarGenerators = 0; GenerateEvenIfPlayerIsCloseCounter = 0; CurrentActiveCount = 0; ProcessCounter = 0; const int32 nGeneralDataSize = sizeof(NumOfCarGenerators) + sizeof(CurrentActiveCount) + sizeof(ProcessCounter) + sizeof(GenerateEvenIfPlayerIsCloseCounter) + sizeof(int16); Init(); INITSAVEBUF CheckSaveHeader(buffer, 'C','G','N','\0', size - SAVE_HEADER_SIZE); assert(ReadSaveBuf(buffer) == nGeneralDataSize); NumOfCarGenerators = ReadSaveBuf(buffer); CurrentActiveCount = ReadSaveBuf(buffer); ProcessCounter = ReadSaveBuf(buffer); GenerateEvenIfPlayerIsCloseCounter = ReadSaveBuf(buffer); ReadSaveBuf(buffer); // alignment assert(ReadSaveBuf(buffer) == sizeof(CarGeneratorArray)); for (int i = 0; i < NUM_CARGENS; i++) CarGeneratorArray[i] = ReadSaveBuf(buffer); VALIDATESAVEBUF(size) } ================================================ FILE: src/vehicles/CarGen.h ================================================ #pragma once #include "common.h" #include "config.h" enum { CARGEN_MAXACTUALLIMIT = 100 }; class CCarGenerator { int32 m_nModelIndex; CVector m_vecPos; float m_fAngle; int16 m_nColor1; int16 m_nColor2; uint8 m_bForceSpawn; uint8 m_nAlarm; uint8 m_nDoorlock; int16 m_nMinDelay; int16 m_nMaxDelay; uint32 m_nTimer; int32 m_nVehicleHandle; uint16 m_nUsesRemaining; bool m_bIsBlocking; public: void SwitchOff(); void SwitchOn(); uint32 CalcNextGen(); void DoInternalProcessing(); void Process(); void Setup(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay); bool CheckForBlockage(int32 mi); bool CheckIfWithinRangeOfAnyPlayers(); void SetUsesRemaining(uint16 uses) { m_nUsesRemaining = uses; } }; class CTheCarGenerators { public: static uint8 ProcessCounter; static uint32 NumOfCarGenerators; static CCarGenerator CarGeneratorArray[NUM_CARGENS]; static uint8 GenerateEvenIfPlayerIsCloseCounter; static uint32 CurrentActiveCount; static void Process(); static int32 CreateCarGenerator(float x, float y, float z, float angle, int32 mi, int16 color1, int16 color2, uint8 force, uint8 alarm, uint8 lock, uint16 min_delay, uint16 max_delay); static void Init(); static void SaveAllCarGenerators(uint8 *, uint32 *); static void LoadAllCarGenerators(uint8 *, uint32); }; ================================================ FILE: src/vehicles/Cranes.cpp ================================================ #include "common.h" #include "Cranes.h" #include "Camera.h" #include "DMAudio.h" #include "Garages.h" #include "General.h" #include "Entity.h" #include "ModelIndices.h" #include "Replay.h" #include "Object.h" #include "World.h" #define MAX_DISTANCE_TO_FIND_CRANE (10.0f) #define CRANE_UPDATE_RADIUS (300.0f) #define CRANE_MOVEMENT_PROCESSING_RADIUS (150.0f) #define CRUSHER_Z (-0.951f) #define MILITARY_Z (10.7862f) #define DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE (5.0f) #define DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT (0.5f) #define CAR_REWARD_MILITARY_CRANE (1500) #define CAR_MOVING_SPEED_THRESHOLD (0.01f) #define CRANE_SLOWDOWN_MULTIPLIER (0.3f) #define OSCILLATION_SPEED (0.002f) #define CAR_ROTATION_SPEED (0.0035f) #define CRANE_MOVEMENT_SPEED (0.001f) #define HOOK_ANGLE_MOVEMENT_SPEED (0.004f) #define HOOK_OFFSET_MOVEMENT_SPEED (0.1f) #define HOOK_HEIGHT_MOVEMENT_SPEED (0.06f) #define MESSAGE_SHOW_DURATION (4000) #define MAX_DISTANCE (99999.9f) #define MIN_VALID_POSITION (-10000.0f) #define DEFAULT_OFFSET (20.0f) uint32 TimerForCamInterpolation; uint32 CCranes::CarsCollectedMilitaryCrane; int32 CCranes::NumCranes; CCrane CCranes::aCranes[NUM_CRANES]; void CCranes::InitCranes(void) { CarsCollectedMilitaryCrane = 0; NumCranes = 0; for (int i = 0; i < NUMSECTORS_X; i++) { for (int j = 0; j < NUMSECTORS_Y; j++) { for (CPtrNode* pNode = CWorld::GetSector(i, j)->m_lists[ENTITYLIST_BUILDINGS].first; pNode; pNode = pNode->next) { CEntity* pEntity = (CEntity*)pNode->item; if (MODELID_CRANE_1 == pEntity->GetModelIndex() || MODELID_CRANE_2 == pEntity->GetModelIndex() || MODELID_CRANE_3 == pEntity->GetModelIndex() || MODELID_CRANE_4 == pEntity->GetModelIndex() || MODELID_CRANE_5 == pEntity->GetModelIndex() || MODELID_CRANE_6 == pEntity->GetModelIndex()) AddThisOneCrane(pEntity); } } } for (CPtrNode* pNode = CWorld::GetBigBuildingList(LEVEL_MAINLAND).first; pNode; pNode = pNode->next) { CEntity* pEntity = (CEntity*)pNode->item; if (MODELID_CRANE_1 == pEntity->GetModelIndex() || MODELID_CRANE_2 == pEntity->GetModelIndex() || MODELID_CRANE_3 == pEntity->GetModelIndex() || MODELID_CRANE_4 == pEntity->GetModelIndex() || MODELID_CRANE_5 == pEntity->GetModelIndex() || MODELID_CRANE_6 == pEntity->GetModelIndex()) AddThisOneCrane(pEntity); } } void CCranes::AddThisOneCrane(CEntity* pEntity) { pEntity->GetMatrix().ResetOrientation(); if (NumCranes >= NUM_CRANES) return; CCrane* pCrane = &aCranes[NumCranes]; pCrane->Init(); pCrane->m_pCraneEntity = (CBuilding*)pEntity; pCrane->m_nCraneStatus = CCrane::NONE; pCrane->m_fHookAngle = NumCranes; // lol wtf while (pCrane->m_fHookAngle > TWOPI) pCrane->m_fHookAngle -= TWOPI; pCrane->m_fHookOffset = DEFAULT_OFFSET; pCrane->m_fHookHeight = DEFAULT_OFFSET; pCrane->m_nTimeForNextCheck = 0; pCrane->m_nCraneState = CCrane::IDLE; pCrane->m_bWasMilitaryCrane = false; pCrane->m_bIsTop = (MODELID_CRANE_1 != pEntity->GetModelIndex()); pCrane->m_pHook = nil; NumCranes++; } void CCranes::ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY) { float fMinDistance = MAX_DISTANCE; float X = fPosX, Y = fPosY; if (X <= MIN_VALID_POSITION || Y <= MIN_VALID_POSITION) { X = fDropOffX; Y = fDropOffY; } int index = 0; for (int i = 0; i < NumCranes; i++) { float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { fMinDistance = distance; index = i; } } #ifdef FIX_BUGS // classic if (fMinDistance == MAX_DISTANCE) return; #endif CCrane* pCrane = &aCranes[index]; pCrane->m_fPickupX1 = fInfX; pCrane->m_fPickupX2 = fSupX; pCrane->m_fPickupY1 = fInfY; pCrane->m_fPickupY2 = fSupY; pCrane->m_vecDropoffTarget.x = fDropOffX; pCrane->m_vecDropoffTarget.y = fDropOffY; pCrane->m_vecDropoffTarget.z = fDropOffZ; pCrane->m_nCraneStatus = CCrane::ACTIVATED; pCrane->m_pVehiclePickedUp = nil; pCrane->m_nVehiclesCollected = 0; pCrane->m_fDropoffHeading = fHeading; pCrane->m_bIsCrusher = bIsCrusher; pCrane->m_bIsMilitaryCrane = bIsMilitary; bool military = true; if (!bIsMilitary && !pCrane->m_bWasMilitaryCrane) military = false; pCrane->m_bWasMilitaryCrane = military; pCrane->m_nTimeForNextCheck = 0; pCrane->m_nCraneState = CCrane::IDLE; float Z; if (bIsCrusher) Z = CRUSHER_Z; else if (bIsMilitary) Z = MILITARY_Z; else Z = CWorld::FindGroundZForCoord((fInfX + fSupX) / 2, (fInfY + fSupY) / 2); pCrane->FindParametersForTarget((fInfX + fSupX) / 2, (fInfY + fSupY) / 2, Z, &pCrane->m_fPickupAngle, &pCrane->m_fPickupDistance, &pCrane->m_fPickupHeight); pCrane->FindParametersForTarget(fDropOffX, fDropOffY, fDropOffZ, &pCrane->m_fDropoffAngle, &pCrane->m_fDropoffDistance, &pCrane->m_fDropoffHeight); } void CCranes::DeActivateCrane(float X, float Y) { float fMinDistance = MAX_DISTANCE; int index = 0; for (int i = 0; i < NumCranes; i++) { float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); if (distance < fMinDistance && distance < MAX_DISTANCE_TO_FIND_CRANE) { fMinDistance = distance; index = i; } } #ifdef FIX_BUGS // classic if (fMinDistance == MAX_DISTANCE) return; #endif aCranes[index].m_nCraneStatus = CCrane::DEACTIVATED; aCranes[index].m_nCraneState = CCrane::IDLE; } bool CCranes::IsThisCarPickedUp(float X, float Y, CVehicle* pVehicle) { int index = 0; bool result = false; for (int i = 0; i < NumCranes; i++) { float distance = (CVector2D(X, Y) - aCranes[i].m_pCraneEntity->GetPosition()).Magnitude(); if (distance < MAX_DISTANCE_TO_FIND_CRANE && aCranes[i].m_pVehiclePickedUp == pVehicle) { if (aCranes[i].m_nCraneState == CCrane::LIFTING_TARGET || aCranes[i].m_nCraneState == CCrane::ROTATING_TARGET) result = true; } } return result; } void CCranes::UpdateCranes(void) { for (int i = 0; i < NumCranes; i++) { if (aCranes[i].m_bIsTop || aCranes[i].m_bIsCrusher || (TheCamera.GetPosition().x + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().x && TheCamera.GetPosition().x - CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().x && TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS > aCranes[i].m_pCraneEntity->GetPosition().y && TheCamera.GetPosition().y + CRANE_UPDATE_RADIUS < aCranes[i].m_pCraneEntity->GetPosition().y)) aCranes[i].Update(); } } void CCrane::Update(void) { if (CReplay::IsPlayingBack()) return; if (((m_nCraneStatus == ACTIVATED || m_nCraneStatus == DEACTIVATED) && Abs(TheCamera.GetGameCamPosition().x - m_pCraneEntity->GetPosition().x) < CRANE_MOVEMENT_PROCESSING_RADIUS && Abs(TheCamera.GetGameCamPosition().y - m_pCraneEntity->GetPosition().y) < CRANE_MOVEMENT_PROCESSING_RADIUS) || m_nCraneState != IDLE) { switch (m_nCraneState) { case IDLE: if (GoTowardsTarget(m_fPickupAngle, m_fPickupDistance, GetHeightToPickup()) && CTimer::GetTimeInMilliseconds() > m_nTimeForNextCheck) { CWorld::AdvanceCurrentScanCode(); #ifdef FIX_BUGS int xstart = Max(0, CWorld::GetSectorIndexX(m_fPickupX1)); int xend = Min(NUMSECTORS_X - 1, CWorld::GetSectorIndexX(m_fPickupX2)); int ystart = Max(0, CWorld::GetSectorIndexY(m_fPickupY1)); int yend = Min(NUMSECTORS_Y - 1, CWorld::GetSectorIndexY(m_fPickupY2)); #else int xstart = CWorld::GetSectorIndexX(m_fPickupX1); int xend = CWorld::GetSectorIndexX(m_fPickupX2); int ystart = CWorld::GetSectorIndexY(m_fPickupY1); int yend = CWorld::GetSectorIndexY(m_fPickupY1); #endif assert(xstart <= xend); assert(ystart <= yend); for (int i = xstart; i <= xend; i++) { for (int j = ystart; j <= yend; j++) { FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES]); FindCarInSectorList(&CWorld::GetSector(i, j)->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); } } } break; case GOING_TOWARDS_TARGET: if (m_pVehiclePickedUp){ if (m_pVehiclePickedUp->GetPosition().x < m_fPickupX1 || m_pVehiclePickedUp->GetPosition().x > m_fPickupX2 || m_pVehiclePickedUp->GetPosition().y < m_fPickupY1 || m_pVehiclePickedUp->GetPosition().y > m_fPickupY2 || m_pVehiclePickedUp->pDriver || Abs(m_pVehiclePickedUp->GetMoveSpeed().x) > CAR_MOVING_SPEED_THRESHOLD || Abs(m_pVehiclePickedUp->GetMoveSpeed().y) > CAR_MOVING_SPEED_THRESHOLD || Abs(m_pVehiclePickedUp->GetMoveSpeed().z) > CAR_MOVING_SPEED_THRESHOLD || (FindPlayerPed()->GetPedState() == PED_ENTER_CAR #ifdef FIX_BUGS || FindPlayerPed()->GetPedState() == PED_CARJACK #endif ) && FindPlayerPed()->m_pSeekTarget == m_pVehiclePickedUp) { m_pVehiclePickedUp = nil; m_nCraneState = IDLE; } else { float fAngle, fOffset, fHeight; FindParametersForTarget( m_pVehiclePickedUp->GetPosition().x, m_pVehiclePickedUp->GetPosition().y, m_pVehiclePickedUp->GetPosition().z + m_pVehiclePickedUp->GetColModel()->boundingBox.max.z, &fAngle, &fOffset, &fHeight); if (GoTowardsTarget(fAngle, fOffset, fHeight)) { CVector distance = m_pVehiclePickedUp->GetPosition() - m_vecHookCurPos; distance.z += m_pVehiclePickedUp->GetColModel()->boundingBox.max.z; if (distance.MagnitudeSqr() < SQR(DISTANCE_FROM_HOOK_TO_VEHICLE_TO_COLLECT)) { m_nCraneState = GOING_TOWARDS_TARGET_ONLY_HEIGHT; m_vecHookVelocity *= 0.4f; m_pVehiclePickedUp->bLightsOn = false; m_pVehiclePickedUp->bUsesCollision = false; if (m_bIsCrusher) m_pVehiclePickedUp->bCollisionProof = true; } } } } else m_nCraneState = IDLE; break; case LIFTING_TARGET: RotateCarriedCarProperly(); if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoff(), CRANE_SLOWDOWN_MULTIPLIER)) m_nCraneState = ROTATING_TARGET; if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { m_pVehiclePickedUp = nil; m_nCraneState = IDLE; } break; case GOING_TOWARDS_TARGET_ONLY_HEIGHT: RotateCarriedCarProperly(); if (GoTowardsHeightTarget(GetHeightToPickupHeight(), CRANE_SLOWDOWN_MULTIPLIER)) m_nCraneState = LIFTING_TARGET; TimerForCamInterpolation = CTimer::GetTimeInMilliseconds(); if (!m_pVehiclePickedUp || m_pVehiclePickedUp->pDriver) { m_pVehiclePickedUp = nil; m_nCraneState = IDLE; } break; case ROTATING_TARGET: { bool bRotateFinished = RotateCarriedCarProperly(); bool bMovementFinished = GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, m_fDropoffHeight, 0.3f); if (bMovementFinished && bRotateFinished) { float fDistanceFromPlayer = m_pVehiclePickedUp ? ((CVector2D)FindPlayerCoors() - (CVector2D)m_pVehiclePickedUp->GetPosition()).Magnitude() : 0.0f; if (fDistanceFromPlayer > DISTANCE_FROM_PLAYER_TO_REMOVE_VEHICLE || !m_bWasMilitaryCrane) { m_nCraneState = DROPPING_TARGET; if (m_pVehiclePickedUp) { m_pVehiclePickedUp->bUsesCollision = true; m_pVehiclePickedUp->m_nStaticFrames = 0; ++m_nVehiclesCollected; if (m_bIsMilitaryCrane) { CCranes::RegisterCarForMilitaryCrane(m_pVehiclePickedUp->GetModelIndex()); if (!CCranes::HaveAllCarsBeenCollectedByMilitaryCrane()) { CWorld::Players[CWorld::PlayerInFocus].m_nMoney += CAR_REWARD_MILITARY_CRANE; CGarages::TriggerMessage("GA_10", CAR_REWARD_MILITARY_CRANE, MESSAGE_SHOW_DURATION, -1); } CWorld::Remove(m_pVehiclePickedUp); delete m_pVehiclePickedUp; } } m_pVehiclePickedUp = nil; } } break; } case DROPPING_TARGET: if (GoTowardsTarget(m_fDropoffAngle, m_fDropoffDistance, GetHeightToDropoffHeight(), CRANE_SLOWDOWN_MULTIPLIER)) { m_nCraneState = IDLE; m_nTimeForNextCheck = CTimer::GetTimeInMilliseconds() + 10000; } break; default: break; } CVector vecHook; CalcHookCoordinates(&vecHook.x, &vecHook.y, &vecHook.z); m_vecHookVelocity += ((CVector2D)vecHook - (CVector2D)m_vecHookCurPos) * CTimer::GetTimeStep() * CRANE_MOVEMENT_SPEED; m_vecHookVelocity *= Pow(0.98f, CTimer::GetTimeStep()); m_vecHookCurPos.x += m_vecHookVelocity.x * CTimer::GetTimeStep(); m_vecHookCurPos.y += m_vecHookVelocity.y * CTimer::GetTimeStep(); m_vecHookCurPos.z = vecHook.z; switch (m_nCraneState) { case LIFTING_TARGET: case GOING_TOWARDS_TARGET_ONLY_HEIGHT: case ROTATING_TARGET: if (m_pVehiclePickedUp) { m_pVehiclePickedUp->SetPosition(m_vecHookCurPos.x, m_vecHookCurPos.y, m_vecHookCurPos.z - m_pVehiclePickedUp->GetColModel()->boundingBox.max.z); m_pVehiclePickedUp->SetMoveSpeed(0.0f, 0.0f, 0.0f); CVector up(vecHook.x - m_vecHookCurPos.x, vecHook.y - m_vecHookCurPos.y, 20.0f); up.Normalise(); m_pVehiclePickedUp->GetRight() = CrossProduct(m_pVehiclePickedUp->GetForward(), up); m_pVehiclePickedUp->GetForward() = CrossProduct(up, m_pVehiclePickedUp->GetRight()); m_pVehiclePickedUp->GetUp() = up; } break; default: break; } } else { int16 rnd = (m_pCraneEntity->m_randomSeed + (CTimer::GetTimeInMilliseconds() >> 11)) & 0xF; // 16 options, lasting 2048 ms each // a bit awkward: why there are 4 periods for -= and 6 for +=? is it a bug? if (rnd < 4) { m_fHookAngle -= OSCILLATION_SPEED * CTimer::GetTimeStep(); if (m_fHookAngle < 0.0f) m_fHookAngle += TWOPI; } else if (rnd > 5 && rnd < 12) { m_fHookAngle += OSCILLATION_SPEED * CTimer::GetTimeStep(); if (m_fHookAngle > TWOPI) m_fHookAngle -= TWOPI; } CalcHookCoordinates(&m_vecHookCurPos.x, &m_vecHookCurPos.y, &m_vecHookCurPos.z); m_vecHookVelocity.x = m_vecHookVelocity.y = 0.0f; } float fCos = Cos(m_fHookAngle); float fSin = Sin(m_fHookAngle); m_pCraneEntity->GetRight().x = fCos; m_pCraneEntity->GetForward().y = fCos; m_pCraneEntity->GetRight().y = fSin; m_pCraneEntity->GetForward().x = -fSin; m_pCraneEntity->GetMatrix().UpdateRW(); m_pCraneEntity->UpdateRwFrame(); SetHookMatrix(); } bool CCrane::RotateCarriedCarProperly() { if (m_fDropoffHeading <= 0.0f) return true; if (!m_pVehiclePickedUp) return true; float fAngleDelta = m_fDropoffHeading - CGeneral::GetATanOfXY(m_pVehiclePickedUp->GetForward().x, m_pVehiclePickedUp->GetForward().y); while (fAngleDelta < -HALFPI) fAngleDelta += PI; while (fAngleDelta > HALFPI) fAngleDelta -= PI; float fDeltaThisFrame = CAR_ROTATION_SPEED * CTimer::GetTimeStep(); if (Abs(fAngleDelta) <= fDeltaThisFrame) // no rotation is actually applied? return true; m_pVehiclePickedUp->GetMatrix().RotateZ(fAngleDelta < 0 ? -fDeltaThisFrame : fDeltaThisFrame); return false; } void CCrane::FindCarInSectorList(CPtrList* pList) { CPtrNode* node; for (node = pList->first; node; node = node->next) { CVehicle* pVehicle = (CVehicle*)node->item; if (pVehicle->m_scanCode == CWorld::GetCurrentScanCode()) continue; pVehicle->m_scanCode = CWorld::GetCurrentScanCode(); if (pVehicle->GetPosition().x < m_fPickupX1 || pVehicle->GetPosition().x > m_fPickupX2 || pVehicle->GetPosition().y < m_fPickupY1 || pVehicle->GetPosition().y > m_fPickupY2) continue; if (pVehicle->pDriver) continue; if (Abs(pVehicle->GetMoveSpeed().x) >= CAR_MOVING_SPEED_THRESHOLD || Abs(pVehicle->GetMoveSpeed().y) >= CAR_MOVING_SPEED_THRESHOLD || Abs(pVehicle->GetMoveSpeed().z) >= CAR_MOVING_SPEED_THRESHOLD) continue; if (!pVehicle->IsCar() || pVehicle->GetStatus() == STATUS_WRECKED || pVehicle->m_fHealth < 250.0f) continue; if (!DoesCranePickUpThisCarType(pVehicle->GetModelIndex()) || m_bIsMilitaryCrane && CCranes::DoesMilitaryCraneHaveThisOneAlready(pVehicle->GetModelIndex())) { if (!pVehicle->bCraneMessageDone) { pVehicle->bCraneMessageDone = true; if (!m_bIsMilitaryCrane) CGarages::TriggerMessage("CR_1", -1, MESSAGE_SHOW_DURATION, -1); // Crane cannot lift this vehicle. else if (DoesCranePickUpThisCarType(pVehicle->GetModelIndex())) CGarages::TriggerMessage("GA_20", -1, MESSAGE_SHOW_DURATION, -1); // We got more of these than we can shift. Sorry man, no deal. else CGarages::TriggerMessage("GA_19", -1, MESSAGE_SHOW_DURATION, -1); // We're not interested in that model. } } else { m_pVehiclePickedUp = pVehicle; pVehicle->RegisterReference((CEntity**)&m_pVehiclePickedUp); m_nCraneState = GOING_TOWARDS_TARGET; } } } bool CCrane::DoesCranePickUpThisCarType(uint32 mi) { if (m_bIsCrusher) { return mi != MI_FIRETRUCK && mi != MI_TRASH && #ifdef FIX_BUGS mi != MI_COACH && #endif mi != MI_SECURICA && mi != MI_BUS && mi != MI_DODO && mi != MI_RHINO; } if (m_bIsMilitaryCrane) { return mi == MI_FIRETRUCK || mi == MI_AMBULAN || mi == MI_ENFORCER || mi == MI_FBIRANCH || mi == MI_RHINO || mi == MI_BARRACKS || mi == MI_POLICE; } return true; } bool CCranes::DoesMilitaryCraneHaveThisOneAlready(uint32 mi) { switch (mi) { case MI_FIRETRUCK: return (CarsCollectedMilitaryCrane & 1); case MI_AMBULAN: return (CarsCollectedMilitaryCrane & 2); case MI_ENFORCER: return (CarsCollectedMilitaryCrane & 4); case MI_FBIRANCH: return (CarsCollectedMilitaryCrane & 8); case MI_RHINO: return (CarsCollectedMilitaryCrane & 0x10); case MI_BARRACKS: return (CarsCollectedMilitaryCrane & 0x20); case MI_POLICE: return (CarsCollectedMilitaryCrane & 0x40); default: break; } return false; } void CCranes::RegisterCarForMilitaryCrane(uint32 mi) { switch (mi) { case MI_FIRETRUCK: CarsCollectedMilitaryCrane |= 1; break; case MI_AMBULAN: CarsCollectedMilitaryCrane |= 2; break; case MI_ENFORCER: CarsCollectedMilitaryCrane |= 4; break; case MI_FBIRANCH: CarsCollectedMilitaryCrane |= 8; break; case MI_RHINO: CarsCollectedMilitaryCrane |= 0x10; break; case MI_BARRACKS: CarsCollectedMilitaryCrane |= 0x20; break; case MI_POLICE: CarsCollectedMilitaryCrane |= 0x40; break; default: break; } } bool CCranes::HaveAllCarsBeenCollectedByMilitaryCrane() { return (CarsCollectedMilitaryCrane & 0x7F) == 0x7F; } bool CCrane::GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier) { bool bAngleMovementFinished, bOffsetMovementFinished, bHeightMovementFinished; float fHookAngleDelta = fAngleToTarget - m_fHookAngle; while (fHookAngleDelta > PI) fHookAngleDelta -= TWOPI; while (fHookAngleDelta < -PI) fHookAngleDelta += TWOPI; float fHookAngleChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_ANGLE_MOVEMENT_SPEED; if (Abs(fHookAngleDelta) < fHookAngleChangeThisFrame) { m_fHookAngle = fAngleToTarget; bAngleMovementFinished = true; } else { if (fHookAngleDelta < 0.0f) { m_fHookAngle -= fHookAngleChangeThisFrame; if (m_fHookAngle < 0.0f) m_fHookAngle += TWOPI; } else { m_fHookAngle += fHookAngleChangeThisFrame; if (m_fHookAngle > TWOPI) m_fHookAngle -= TWOPI; } bAngleMovementFinished = false; } float fHookOffsetDelta = fDistanceToTarget - m_fHookOffset; float fHookOffsetChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_OFFSET_MOVEMENT_SPEED; if (Abs(fHookOffsetDelta) < fHookOffsetChangeThisFrame) { m_fHookOffset = fDistanceToTarget; bOffsetMovementFinished = true; } else { if (fHookOffsetDelta < 0.0f) m_fHookOffset -= fHookOffsetChangeThisFrame; else m_fHookOffset += fHookOffsetChangeThisFrame; bOffsetMovementFinished = false; } float fHookHeightDelta = fTargetHeight - m_fHookHeight; float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { m_fHookHeight = fTargetHeight; bHeightMovementFinished = true; } else { if (fHookHeightDelta < 0.0f) m_fHookHeight -= fHookHeightChangeThisFrame; else m_fHookHeight += fHookHeightChangeThisFrame; bHeightMovementFinished = false; } return bAngleMovementFinished && bOffsetMovementFinished && bHeightMovementFinished; } bool CCrane::GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier) { bool bHeightMovementFinished; float fHookHeightDelta = fTargetHeight - m_fHookHeight; float fHookHeightChangeThisFrame = fSpeedMultiplier * CTimer::GetTimeStep() * HOOK_HEIGHT_MOVEMENT_SPEED; if (Abs(fHookHeightDelta) < fHookHeightChangeThisFrame) { m_fHookHeight = fTargetHeight; bHeightMovementFinished = true; } else { if (fHookHeightDelta < 0.0f) m_fHookHeight -= fHookHeightChangeThisFrame; else m_fHookHeight += fHookHeightChangeThisFrame; bHeightMovementFinished = false; } return bHeightMovementFinished; } void CCrane::FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight) { *pAngle = CGeneral::GetATanOfXY(X - m_pCraneEntity->GetPosition().x, Y - m_pCraneEntity->GetPosition().y); *pDistance = ((CVector2D(X, Y) - (CVector2D)m_pCraneEntity->GetPosition())).Magnitude(); *pHeight = Z; } void CCrane::CalcHookCoordinates(float* pX, float* pY, float* pZ) { *pX = Cos(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().x; *pY = Sin(m_fHookAngle) * m_fHookOffset + m_pCraneEntity->GetPosition().y; *pZ = m_fHookHeight; } void CCrane::SetHookMatrix() { if (m_pHook == nil) return; m_pHook->SetPosition(m_vecHookCurPos); CVector up(m_vecHookInitPos.x - m_vecHookCurPos.x, m_vecHookInitPos.y - m_vecHookCurPos.y, 20.0f); up.Normalise(); m_pHook->GetRight() = CrossProduct(CVector(0.0f, 1.0f, 0.0f), up); m_pHook->GetForward() = CrossProduct(up, m_pHook->GetRight()); m_pHook->GetUp() = up; m_pHook->SetOrientation(0.0f, 0.0f, -HALFPI); m_pHook->GetMatrix().UpdateRW(); m_pHook->UpdateRwFrame(); CWorld::Remove(m_pHook); CWorld::Add(m_pHook); } bool CCranes::IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle) { for (int i = 0; i < NumCranes; i++) { if (pVehicle == aCranes[i].m_pVehiclePickedUp) { switch (aCranes[i].m_nCraneState) { case CCrane::GOING_TOWARDS_TARGET_ONLY_HEIGHT: case CCrane::LIFTING_TARGET: case CCrane::ROTATING_TARGET: return true; default: break; } } } return false; } bool CCranes::IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle) { for (int i = 0; i < NumCranes; i++) { if (pVehicle == aCranes[i].m_pVehiclePickedUp) return true; } return false; } void CCranes::Save(uint8* buf, uint32* size) { INITSAVEBUF *size = 2 * sizeof(uint32) + sizeof(aCranes); WriteSaveBuf(buf, NumCranes); WriteSaveBuf(buf, CarsCollectedMilitaryCrane); for (int i = 0; i < NUM_CRANES; i++) { CCrane *pCrane = WriteSaveBuf(buf, aCranes[i]); if (pCrane->m_pCraneEntity != nil) pCrane->m_pCraneEntity = (CBuilding*)(CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pCrane->m_pCraneEntity) + 1); if (pCrane->m_pHook != nil) pCrane->m_pHook = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pCrane->m_pHook) + 1); if (pCrane->m_pVehiclePickedUp != nil) pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(pCrane->m_pVehiclePickedUp) + 1); } VALIDATESAVEBUF(*size); } void CCranes::Load(uint8* buf, uint32 size) { INITSAVEBUF NumCranes = ReadSaveBuf(buf); CarsCollectedMilitaryCrane = ReadSaveBuf(buf); for (int i = 0; i < NUM_CRANES; i++) aCranes[i] = ReadSaveBuf(buf); for (int i = 0; i < NUM_CRANES; i++) { CCrane *pCrane = &aCranes[i]; if (pCrane->m_pCraneEntity != nil) pCrane->m_pCraneEntity = CPools::GetBuildingPool()->GetSlot((uintptr)pCrane->m_pCraneEntity - 1); if (pCrane->m_pHook != nil) pCrane->m_pHook = CPools::GetObjectPool()->GetSlot((uintptr)pCrane->m_pHook - 1); if (pCrane->m_pVehiclePickedUp != nil) pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uintptr)pCrane->m_pVehiclePickedUp - 1); } VALIDATESAVEBUF(size); } ================================================ FILE: src/vehicles/Cranes.h ================================================ #pragma once #include "common.h" #include "World.h" class CVehicle; class CEntity; class CObject; class CBuilding; class CCrane { public: enum CraneState { IDLE = 0, GOING_TOWARDS_TARGET = 1, LIFTING_TARGET = 2, GOING_TOWARDS_TARGET_ONLY_HEIGHT = 3, ROTATING_TARGET = 4, DROPPING_TARGET = 5 }; enum CraneStatus { NONE = 0, ACTIVATED = 1, DEACTIVATED = 2 }; CBuilding *m_pCraneEntity; CObject *m_pHook; float m_fPickupX1; float m_fPickupX2; float m_fPickupY1; float m_fPickupY2; CVector m_vecDropoffTarget; float m_fDropoffHeading; float m_fPickupAngle; float m_fDropoffAngle; float m_fPickupDistance; float m_fDropoffDistance; float m_fPickupHeight; float m_fDropoffHeight; float m_fHookAngle; float m_fHookOffset; float m_fHookHeight; CVector m_vecHookInitPos; CVector m_vecHookCurPos; CVector2D m_vecHookVelocity; CVehicle *m_pVehiclePickedUp; uint32 m_nTimeForNextCheck; uint8 m_nCraneStatus; uint8 m_nCraneState; uint8 m_nVehiclesCollected; bool m_bIsCrusher; bool m_bIsMilitaryCrane; bool m_bWasMilitaryCrane; bool m_bIsTop; void Init(void) { memset(this, 0, sizeof(*this)); } void Update(void); bool RotateCarriedCarProperly(void); void FindCarInSectorList(CPtrList* pList); bool DoesCranePickUpThisCarType(uint32 mi); bool GoTowardsTarget(float fAngleToTarget, float fDistanceToTarget, float fTargetHeight, float fSpeedMultiplier = 1.0f); bool GoTowardsHeightTarget(float fTargetHeight, float fSpeedMultiplier = 1.0f); void FindParametersForTarget(float X, float Y, float Z, float* pAngle, float* pDistance, float* pHeight); void CalcHookCoordinates(float* pX, float* pY, float* pZ); void SetHookMatrix(void); float GetHeightToPickup() { return 4.0f + m_fPickupHeight + (m_bIsCrusher ? 4.5f : 0.0f); }; float GetHeightToDropoff() { return m_bIsCrusher ? (2.0f + m_fDropoffHeight + 3.0f) : (2.0f + m_fDropoffHeight); } float GetHeightToPickupHeight() { return m_fPickupHeight + (m_bIsCrusher ? 7.0f : 4.0f); } float GetHeightToDropoffHeight() { return m_fDropoffHeight + (m_bIsCrusher ? 7.0f : 2.0f); } }; VALIDATE_SIZE(CCrane, 128); class CCranes { public: static void InitCranes(void); static void AddThisOneCrane(CEntity* pCraneEntity); static void ActivateCrane(float fInfX, float fSupX, float fInfY, float fSupY, float fDropOffX, float fDropOffY, float fDropOffZ, float fHeading, bool bIsCrusher, bool bIsMilitary, float fPosX, float fPosY); static void DeActivateCrane(float fX, float fY); static bool IsThisCarPickedUp(float fX, float fY, CVehicle* pVehicle); static void UpdateCranes(void); static bool DoesMilitaryCraneHaveThisOneAlready(uint32 mi); static void RegisterCarForMilitaryCrane(uint32 mi); static bool HaveAllCarsBeenCollectedByMilitaryCrane(void); static bool IsThisCarBeingCarriedByAnyCrane(CVehicle* pVehicle); static bool IsThisCarBeingTargettedByAnyCrane(CVehicle* pVehicle); static void Save(uint8* buf, uint32* size); static void Load(uint8* buf, uint32 size); // out of class in III PC and later because of SecuROM static uint32 CarsCollectedMilitaryCrane; static int32 NumCranes; static CCrane aCranes[NUM_CRANES]; }; ================================================ FILE: src/vehicles/DamageManager.cpp ================================================ #include "common.h" #include "General.h" #include "Vehicle.h" #include "DamageManager.h" float G_aComponentDamage[] = { 2.5f, 1.25f, 3.2f, 1.4f, 2.5f, 2.8f, 0.5f }; CDamageManager::CDamageManager(void) { ResetDamageStatus(); m_fWheelDamageEffect = 0.5f; field_18 = 1; } void CDamageManager::ResetDamageStatus(void) { int i; m_fWheelDamageEffect = 0.0f; m_engineStatus = 0; for(i = 0; i < ARRAY_SIZE(m_wheelStatus); i++) m_wheelStatus[i] = 0; for(i = 0; i < ARRAY_SIZE(m_doorStatus); i++) m_doorStatus[i] = 0; m_lightStatus = 0; m_panelStatus = 0; } void CDamageManager::FuckCarCompletely(void) { int i; m_wheelStatus[0] = WHEEL_STATUS_MISSING; // wheels 1-3 not reset? for(i = 0; i < ARRAY_SIZE(m_doorStatus); i++) m_doorStatus[i] = DOOR_STATUS_MISSING; for(i = 0; i < 3; i++){ #ifdef FIX_BUGS ProgressPanelDamage(VEHBUMPER_FRONT); ProgressPanelDamage(VEHBUMPER_REAR); #else // this can't be right ProgressPanelDamage(COMPONENT_BUMPER_FRONT); ProgressPanelDamage(COMPONENT_BUMPER_REAR); #endif } // Why set to no damage? #ifndef FIX_BUGS m_lightStatus = 0; m_panelStatus = 0; #endif SetEngineStatus(250); } bool CDamageManager::ApplyDamage(tComponent component, float damage, float unused) { tComponentGroup group; uint8 subComp; GetComponentGroup(component, &group, &subComp); damage *= G_aComponentDamage[group]; if(component == COMPONENT_PANEL_WINDSCREEN) damage *= 0.6f; if(damage > 150.0f){ switch(group){ case COMPGROUP_WHEEL: ProgressWheelDamage(subComp); break; case COMPGROUP_DOOR: case COMPGROUP_BOOT: ProgressDoorDamage(subComp); break; case COMPGROUP_BONNET: if(damage > 220.0f) ProgressEngineDamage(); ProgressDoorDamage(subComp); break; case COMPGROUP_PANEL: // so windscreen is a light? SetLightStatus((eLights)subComp, 1); // fall through case COMPGROUP_BUMPER: if(damage > 220.0f && (component == COMPONENT_PANEL_FRONT_LEFT || component == COMPONENT_PANEL_FRONT_RIGHT || component == COMPONENT_PANEL_WINDSCREEN)) ProgressEngineDamage(); ProgressPanelDamage(subComp); break; default: break; } return true; } return false; } bool CDamageManager::GetComponentGroup(tComponent component, tComponentGroup *componentGroup, uint8 *subComp) { *subComp = -2; // ?? // This is done very strangely in the game, maybe an optimized switch? if(component >= COMPONENT_PANEL_FRONT_LEFT){ if(component >= COMPONENT_BUMPER_FRONT) *componentGroup = COMPGROUP_BUMPER; else *componentGroup = COMPGROUP_PANEL; *subComp = component - COMPONENT_PANEL_FRONT_LEFT; return true; }else if(component >= COMPONENT_DOOR_BONNET){ if(component == COMPONENT_DOOR_BONNET) *componentGroup = COMPGROUP_BONNET; else if(component == COMPONENT_DOOR_BOOT) *componentGroup = COMPGROUP_BOOT; else *componentGroup = COMPGROUP_DOOR; *subComp = component - COMPONENT_DOOR_BONNET; return true; }else if(component >= COMPONENT_WHEEL_FRONT_LEFT){ *componentGroup = COMPGROUP_WHEEL; *subComp = component - COMPONENT_WHEEL_FRONT_LEFT; return true; }else if(component >= COMPONENT_DEFAULT){ *componentGroup = COMPGROUP_DEFAULT; *subComp = COMPONENT_DEFAULT; return true; }else return false; } void CDamageManager::SetDoorStatus(int32 door, uint32 status) { m_doorStatus[door] = status; } int32 CDamageManager::GetDoorStatus(int32 door) { return m_doorStatus[door]; } bool CDamageManager::ProgressDoorDamage(uint8 door) { int status = GetDoorStatus(door); if(status == PANEL_STATUS_MISSING) return false; SetDoorStatus(door, status+1); return true; } void CDamageManager::SetPanelStatus(int32 panel, uint32 status) { m_panelStatus = dpb(status, panel*4, 4, m_panelStatus); } int32 CDamageManager::GetPanelStatus(int32 panel) { return ldb(panel*4, 4, m_panelStatus); } bool CDamageManager::ProgressPanelDamage(uint8 panel) { int status = GetPanelStatus(panel); if(status == DOOR_STATUS_MISSING) return false; SetPanelStatus(panel, status+1); return true; } void CDamageManager::SetLightStatus(eLights light, uint32 status) { m_lightStatus = dpb(status, light*2, 2, m_lightStatus); } int32 CDamageManager::GetLightStatus(eLights light) { return ldb(light*2, 2, m_lightStatus); } void CDamageManager::SetWheelStatus(int32 wheel, uint32 status) { m_wheelStatus[wheel] = status; } int32 CDamageManager::GetWheelStatus(int32 wheel) { return m_wheelStatus[wheel]; } bool CDamageManager::ProgressWheelDamage(uint8 wheel) { int status = GetWheelStatus(wheel); if(status == WHEEL_STATUS_MISSING) return false; SetWheelStatus(wheel, status+1); return true; } void CDamageManager::SetEngineStatus(uint32 status) { if(status > 250) m_engineStatus = 250; else m_engineStatus = status; } int32 CDamageManager::GetEngineStatus(void) { return m_engineStatus; } bool CDamageManager::ProgressEngineDamage(void) { // gone in VC return false; } ================================================ FILE: src/vehicles/DamageManager.h ================================================ #pragma once #include "common.h" // TODO: move some of this into Vehicle.h enum eEngineStatus { ENGINE_STATUS_STEAM1 = 100, ENGINE_STATUS_STEAM2 = 150, ENGINE_STATUS_SMOKE = 200, ENGINE_STATUS_ON_FIRE = 225 }; enum eDoorStatus { DOOR_STATUS_OK, DOOR_STATUS_SMASHED, DOOR_STATUS_SWINGING, DOOR_STATUS_MISSING }; enum ePanelStatus { PANEL_STATUS_OK, PANEL_STATUS_SMASHED1, PANEL_STATUS_SMASHED2, PANEL_STATUS_MISSING, }; enum eWheelStatus { WHEEL_STATUS_OK, WHEEL_STATUS_BURST, WHEEL_STATUS_MISSING }; enum eLightStatus { LIGHT_STATUS_OK, LIGHT_STATUS_BROKEN }; enum tComponent { COMPONENT_DEFAULT, COMPONENT_WHEEL_FRONT_LEFT, COMPONENT_WHEEL_FRONT_RIGHT, COMPONENT_WHEEL_REAR_LEFT, COMPONENT_WHEEL_REAR_RIGHT, COMPONENT_DOOR_BONNET, COMPONENT_DOOR_BOOT, COMPONENT_DOOR_FRONT_LEFT, COMPONENT_DOOR_FRONT_RIGHT, COMPONENT_DOOR_REAR_LEFT, COMPONENT_DOOR_REAR_RIGHT, COMPONENT_PANEL_FRONT_LEFT, COMPONENT_PANEL_FRONT_RIGHT, COMPONENT_PANEL_REAR_LEFT, COMPONENT_PANEL_REAR_RIGHT, COMPONENT_PANEL_WINDSCREEN, COMPONENT_BUMPER_FRONT, COMPONENT_BUMPER_REAR, }; enum tComponentGroup { COMPGROUP_BUMPER, COMPGROUP_WHEEL, COMPGROUP_DOOR, COMPGROUP_BONNET, COMPGROUP_BOOT, COMPGROUP_PANEL, COMPGROUP_DEFAULT, }; enum eLights; class CDamageManager { public: float m_fWheelDamageEffect; uint8 m_engineStatus; uint8 m_wheelStatus[4]; uint8 m_doorStatus[6]; uint32 m_lightStatus; uint32 m_panelStatus; uint8 field_18; CDamageManager(void); void ResetDamageStatus(void); void FuckCarCompletely(void); bool ApplyDamage(tComponent component, float damage, float unused); bool GetComponentGroup(tComponent component, tComponentGroup *componentGroup, uint8 *foo); void SetDoorStatus(int32 door, uint32 status); int32 GetDoorStatus(int32 door); bool ProgressDoorDamage(uint8 door); void SetPanelStatus(int32 panel, uint32 status); int32 GetPanelStatus(int32 panel); bool ProgressPanelDamage(uint8 panel); // needed for CReplay static int32 GetPanelStatus(uint32 panelstatus, int32 panel) { return ldb(panel*4, 4, panelstatus); } void SetLightStatus(eLights light, uint32 status); int32 GetLightStatus(eLights light); void SetWheelStatus(int32 wheel, uint32 status); int32 GetWheelStatus(int32 wheel); bool ProgressWheelDamage(uint8 wheel); void SetEngineStatus(uint32 status); int32 GetEngineStatus(void); bool ProgressEngineDamage(void); }; VALIDATE_SIZE(CDamageManager, 0x1C); ================================================ FILE: src/vehicles/Door.cpp ================================================ #include "common.h" #include "Vehicle.h" #include "Door.h" CDoor::CDoor(void) { memset(this, 0, sizeof(*this)); } void CDoor::Open(float ratio) { float open; m_fPrevAngle = m_fAngle; open = RetAngleWhenOpen(); if(ratio < 1.0f){ m_fAngle = open*ratio; if(m_fAngle == 0.0f) m_fAngVel = 0.0f; }else{ m_nDoorState = DOORST_OPEN; m_fAngle = open; } } void CDoor::Process(CVehicle *vehicle) { static CVector vecOffset(1.0f, 0.0f, 0.0f); CVector speed = vehicle->GetSpeed(vecOffset); CVector vecSpeedDiff = speed - m_vecSpeed; vecSpeedDiff = Multiply3x3(vecSpeedDiff, vehicle->GetMatrix()); // air resistance float fSpeedDiff = 0.0f; // uninitialized in game switch(m_nAxis){ case 0: // x-axis if(m_nDirn) fSpeedDiff = vecSpeedDiff.y + vecSpeedDiff.z; else fSpeedDiff = -(vecSpeedDiff.y + vecSpeedDiff.z); break; // we don't support y axis apparently? case 2: // z-axis if(m_nDirn) fSpeedDiff = -(vecSpeedDiff.y + vecSpeedDiff.x); else fSpeedDiff = vecSpeedDiff.y - vecSpeedDiff.x; break; } fSpeedDiff = clamp(fSpeedDiff, -0.2f, 0.2f); if(Abs(fSpeedDiff) > 0.002f) m_fAngVel += fSpeedDiff; m_fAngVel *= 0.945f; m_fAngVel = clamp(m_fAngVel, -0.3f, 0.3f); m_fAngle += m_fAngVel; m_nDoorState = DOORST_SWINGING; if(m_fAngle > m_fMaxAngle){ m_fAngle = m_fMaxAngle; m_fAngVel *= -0.8f; m_nDoorState = DOORST_OPEN; } if(m_fAngle < m_fMinAngle){ m_fAngle = m_fMinAngle; m_fAngVel *= -0.8f; m_nDoorState = DOORST_CLOSED; } m_vecSpeed = speed; } float CDoor::RetAngleWhenClosed(void) { if(Abs(m_fMaxAngle) < Abs(m_fMinAngle)) return m_fMaxAngle; else return m_fMinAngle; } float CDoor::RetAngleWhenOpen(void) { if(Abs(m_fMaxAngle) < Abs(m_fMinAngle)) return m_fMinAngle; else return m_fMaxAngle; } float CDoor::GetAngleOpenRatio(void) { float open = RetAngleWhenOpen(); if(open == 0.0f) return 0.0f; return m_fAngle/open; } bool CDoor::IsFullyOpen(void) { // why -0.5? that's around 28 deg less than fully open if(Abs(m_fAngle) < Abs(RetAngleWhenOpen()) - 0.5f) return false; return true; } bool CDoor::IsClosed(void) { return m_fAngle == RetAngleWhenClosed(); } CTrainDoor::CTrainDoor(void) { memset(this, 0, sizeof(*this)); } void CTrainDoor::Open(float ratio) { float open; m_fPrevPosn = m_fPosn; open = RetTranslationWhenOpen(); if(ratio < 1.0f){ m_fPosn = open*ratio; }else{ m_nDoorState = DOORST_OPEN; m_fPosn = open; } } float CTrainDoor::RetTranslationWhenClosed(void) { if(Abs(m_fClosedPosn) < Abs(m_fOpenPosn)) return m_fClosedPosn; else return m_fOpenPosn; } float CTrainDoor::RetTranslationWhenOpen(void) { if(Abs(m_fClosedPosn) < Abs(m_fOpenPosn)) return m_fOpenPosn; else return m_fClosedPosn; } bool CTrainDoor::IsFullyOpen(void) { // 0.5f again... if(Abs(m_fPosn) < Abs(RetTranslationWhenOpen()) - 0.5f) return false; return true; } bool CTrainDoor::IsClosed(void) { return m_fPosn == RetTranslationWhenClosed(); } ================================================ FILE: src/vehicles/Door.h ================================================ #pragma once class CVehicle; enum eDoorState { DOORST_SWINGING, // actually wrong though, // OPEN is really MAX_ANGLE and CLOSED is MIN_ANGLE DOORST_OPEN, DOORST_CLOSED }; class CDoor { public: float m_fMaxAngle; float m_fMinAngle; // direction of rotation for air resistance int8 m_nDirn; // axis in which this door rotates int8 m_nAxis; int8 m_nDoorState; float m_fAngle; float m_fPrevAngle; float m_fAngVel; CVector m_vecSpeed; CDoor(void); void Init(float minAngle, float maxAngle, int8 dir, int8 axis) { m_fMinAngle = minAngle; m_fMaxAngle = maxAngle; m_nDirn = dir; m_nAxis = axis; } void Open(float ratio); void Process(CVehicle *veh); float RetAngleWhenClosed(void); // dead float RetAngleWhenOpen(void); float GetAngleOpenRatio(void); bool IsFullyOpen(void); bool IsClosed(void); // dead }; class CTrainDoor { public: float m_fClosedPosn; float m_fOpenPosn; int8 m_nDirn; int8 m_nDoorState; // same enum as above? int8 m_nAxis; float m_fPosn; float m_fPrevPosn; int field_14; // unused? CTrainDoor(void); void Init(float open, float closed, int8 dir, int8 axis) { m_fOpenPosn = open; m_fClosedPosn = closed; m_nDirn = dir; m_nAxis = axis; } bool IsClosed(void); bool IsFullyOpen(void); float RetTranslationWhenClosed(void); float RetTranslationWhenOpen(void); void Open(float ratio); }; ================================================ FILE: src/vehicles/Floater.cpp ================================================ #include "common.h" #include "Timer.h" #include "WaterLevel.h" #include "ModelIndices.h" #include "Physical.h" #include "Vehicle.h" #include "Floater.h" cBuoyancy mod_Buoyancy; float fVolMultiplier = 1.0f; // amount of boat volume in bounding box // 1.0-volume is the empty space in the bbox float fBoatVolumeDistribution[9] = { // rear 0.75f, 0.9f, 0.75f, 0.95f, 1.0f, 0.95f, 0.4f, 0.7f, 0.4f // bow }; float fBoatVolumeDistributionCat[9] = { 0.9f, 0.3f, 0.9f, 1.0f, 0.5f, 1.0f, 0.95f, 0.4f, 0.95f }; float fBoatVolumeDistributionSail[9] = { 0.55f, 0.95f, 0.55f, 0.75f, 1.1f, 0.75f, 0.3f, 0.8f, 0.3f }; float fBoatVolumeDistributionDinghy[9] = { 0.65f, 0.85f, 0.65f, 0.85f, 1.1f, 0.85f, 0.65f, 0.95f, 0.65f }; float fBoatVolumeDistributionSpeed[9] = { 0.7f, 0.9f, 0.7f, 0.95f, 1.0f, 0.95f, 0.6f, 0.7f, 0.6f }; bool cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse) { m_numSteps = 2.0f; if(!CWaterLevel::GetWaterLevel(phys->GetPosition(), &m_waterlevel, phys->bTouchingWater)) return false; m_matrix = phys->GetMatrix(); PreCalcSetup(phys, buoyancy); SimpleCalcBuoyancy(); float f = CalcBuoyancyForce(phys, point, impulse); if(m_isBoat) return true; return f != 0.0f; } bool cBuoyancy::ProcessBuoyancyBoat(CVehicle *veh, float buoyancy, CVector *point, CVector *impulse, bool bNoTurnForce) { m_numSteps = 2.0f; if(!CWaterLevel::GetWaterLevel(veh->GetPosition(), &m_waterlevel, veh->bTouchingWater)) return false; m_matrix = veh->GetMatrix(); PreCalcSetup(veh, buoyancy); float x, y; int ix, i; tWaterLevel waterPosition; CVector waterNormal; // Floater is divided into 3x3 parts. Process and sum each of them float volDiv = 1.0f/((m_dimMax.z - m_dimMin.z)*sq(m_numSteps+1.0f)); ix = 0; for(x = m_dimMin.x; x <= m_dimMax.x; x += m_step.x){ i = ix; for(y = m_dimMin.y; y <= m_dimMax.y; y += m_step.y){ CVector waterLevel(x, y, 0.0f); FindWaterLevelNorm(m_positionZ, &waterLevel, &waterPosition, &waterNormal); switch(veh->GetModelIndex()){ case MI_RIO: fVolMultiplier = fBoatVolumeDistributionCat[i]; break; case MI_SQUALO: case MI_SPEEDER: case MI_JETMAX: fVolMultiplier = fBoatVolumeDistributionSpeed[i]; break; case MI_COASTG: case MI_DINGHY: fVolMultiplier = fBoatVolumeDistributionDinghy[i]; break; case MI_MARQUIS: fVolMultiplier = fBoatVolumeDistributionSail[i]; break; case MI_PREDATOR: case MI_SKIMMER: case MI_REEFER: case MI_TROPIC: default: fVolMultiplier = fBoatVolumeDistribution[i]; break; } if(waterPosition != FLOATER_ABOVE_WATER){ float volume = SimpleSumBuoyancyData(waterLevel, waterPosition); float upImpulse = volume * volDiv * buoyancy * CTimer::GetTimeStep(); CVector speed = veh->GetSpeed(Multiply3x3(veh->GetMatrix(), CVector(x, y, 0.0f))); float damp = 1.0f - DotProduct(speed, waterNormal)*veh->pHandling->fSuspensionDampingLevel; float finalImpulse = upImpulse*Max(damp, 0.0f); impulse->z += finalImpulse; if(!bNoTurnForce) veh->ApplyTurnForce(finalImpulse*waterNormal, Multiply3x3(m_matrix, waterLevel)); } i += 3; } ix++; } m_volumeUnderWater *= volDiv; *point = Multiply3x3(m_matrix, m_impulsePoint); return m_isBoat || m_haveVolume; } void cBuoyancy::PreCalcSetup(CPhysical *phys, float buoyancy) { CColModel *colModel; m_isBoat = phys->IsVehicle() && ((CVehicle*)phys)->IsBoat(); colModel = phys->GetColModel(); m_dimMin = colModel->boundingBox.min; m_dimMax = colModel->boundingBox.max; if(m_isBoat){ switch(phys->GetModelIndex()){ case MI_PREDATOR: default: m_dimMax.y *= 1.05f; m_dimMin.y *= 0.9f; break; case MI_SPEEDER: m_dimMax.y *= 1.25f; m_dimMin.y *= 0.83f; break; case MI_REEFER: m_dimMin.y *= 0.9f; break; case MI_RIO: m_dimMax.y *= 0.9f; m_dimMin.y *= 0.9f; m_dimMax.z += 0.25f; m_dimMin.z -= 0.2f; break; case MI_SQUALO: m_dimMax.y *= 0.9f; m_dimMin.y *= 0.9f; break; case MI_TROPIC: m_dimMax.y *= 1.3f; m_dimMin.y *= 0.82f; m_dimMin.z -= 0.2f; break; case MI_SKIMMER: m_dimMin.y = -m_dimMax.y; m_dimMax.y *= 1.2f; break; case MI_COASTG: m_dimMax.y *= 1.1f; m_dimMin.y *= 0.9f; m_dimMin.z -= 0.3f; break; case MI_DINGHY: m_dimMax.y *= 1.3f; m_dimMin.y *= 0.9f; m_dimMin.z -= 0.2f; break; case MI_MARQUIS: m_dimMax.y *= 1.3f; m_dimMin.y *= 0.9f; break; case MI_JETMAX: m_dimMin.y *= 0.9f; break; } } m_step = (m_dimMax - m_dimMin)/m_numSteps; if(m_step.z > m_step.x && m_step.z > m_step.y){ m_stepRatio.x = m_step.x/m_step.z; m_stepRatio.y = m_step.y/m_step.z; m_stepRatio.z = 1.0f; }else if(m_step.y > m_step.x && m_step.y > m_step.z){ m_stepRatio.x = m_step.x/m_step.y; m_stepRatio.y = 1.0f; m_stepRatio.z = m_step.z/m_step.y; }else{ m_stepRatio.x = 1.0f; m_stepRatio.y = m_step.y/m_step.x; m_stepRatio.z = m_step.z/m_step.x; } m_haveVolume = false; m_numPartialVolumes = 1.0f; m_volumeUnderWater = 0.0f; m_impulsePoint = CVector(0.0f, 0.0f, 0.0f); m_position = phys->GetPosition(); m_positionZ = CVector(0.0f, 0.0f, m_position.z); m_buoyancy = buoyancy; m_waterlevel += m_waterLevelInc; } void cBuoyancy::SimpleCalcBuoyancy(void) { float x, y; tWaterLevel waterPosition; // Floater is divided into 3x3 parts. Process and sum each of them for(x = m_dimMin.x; x <= m_dimMax.x; x += m_step.x){ for(y = m_dimMin.y; y <= m_dimMax.y; y += m_step.y){ CVector waterLevel(x, y, 0.0f); FindWaterLevel(m_positionZ, &waterLevel, &waterPosition); fVolMultiplier = 1.0f; if(waterPosition != FLOATER_ABOVE_WATER) SimpleSumBuoyancyData(waterLevel, waterPosition); } } m_volumeUnderWater /= (m_dimMax.z - m_dimMin.z)*sq(m_numSteps+1.0f); } float cBuoyancy::SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition) { static float fThisVolume; static CVector AverageOfWaterLevel; static float fFraction; static float fRemainingSlice; float submerged = Abs(waterLevel.z - m_dimMin.z); // subtract empty space from submerged volume fThisVolume = submerged - (1.0f - fVolMultiplier); if(fThisVolume < 0.0f) return 0.0f; if(m_isBoat){ fThisVolume *= fVolMultiplier; fThisVolume = sq(fThisVolume); } m_volumeUnderWater += fThisVolume; AverageOfWaterLevel.x = waterLevel.x * m_stepRatio.x; AverageOfWaterLevel.y = waterLevel.y * m_stepRatio.y; AverageOfWaterLevel.z = (waterLevel.z+m_dimMin.z)/2.0f * m_stepRatio.z; if(m_flipAverage) AverageOfWaterLevel = -AverageOfWaterLevel; fFraction = 1.0f/m_numPartialVolumes; fRemainingSlice = 1.0f - fFraction; m_impulsePoint = m_impulsePoint*fRemainingSlice + AverageOfWaterLevel*fThisVolume*fFraction; m_numPartialVolumes += 1.0f; m_haveVolume = true; return fThisVolume; } void cBuoyancy::FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition) { *waterPosition = FLOATER_IN_WATER; // waterLevel is a local x,y point // m_position is the global position of our floater // zpos is the global z coordinate of our floater CVector xWaterLevel = Multiply3x3(m_matrix, *waterLevel); CWaterLevel::GetWaterLevel(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y, m_position.z, &waterLevel->z, true); waterLevel->z -= xWaterLevel.z + zpos.z; // make local if(waterLevel->z > m_dimMax.z){ waterLevel->z = m_dimMax.z; *waterPosition = FLOATER_UNDER_WATER; }else if(waterLevel->z < m_dimMin.z){ waterLevel->z = m_dimMin.z; *waterPosition = FLOATER_ABOVE_WATER; } } // Same as above but also get normal void cBuoyancy::FindWaterLevelNorm(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition, CVector *normal) { *waterPosition = FLOATER_IN_WATER; CVector xWaterLevel = Multiply3x3(m_matrix, *waterLevel); CWaterLevel::GetWaterLevel(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y, m_position.z, &waterLevel->z, true); waterLevel->z -= xWaterLevel.z + zpos.z; // make local if(waterLevel->z >= m_dimMin.z) *normal = CWaterLevel::GetWaterNormal(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y); if(waterLevel->z > m_dimMax.z){ waterLevel->z = m_dimMax.z; *waterPosition = FLOATER_UNDER_WATER; }else if(waterLevel->z < m_dimMin.z){ waterLevel->z = m_dimMin.z; *waterPosition = FLOATER_ABOVE_WATER; } } bool cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *point, CVector *impulse) { if(!m_haveVolume) return false; *point = Multiply3x3(m_matrix, m_impulsePoint); *impulse = CVector(0.0f, 0.0f, m_volumeUnderWater*m_buoyancy*CTimer::GetTimeStep()); return true; } ================================================ FILE: src/vehicles/Floater.h ================================================ #pragma once class CPhysical; enum tWaterLevel { FLOATER_ABOVE_WATER, FLOATER_IN_WATER, FLOATER_UNDER_WATER, }; class cBuoyancy { public: CVector m_position; CMatrix m_matrix; int m_field_54; CVector m_positionZ; float m_waterlevel; float m_waterLevelInc; float m_buoyancy; CVector m_dimMax; CVector m_dimMin; float m_numPartialVolumes; int m_field_8C; int m_field_90; int m_field_94; bool m_haveVolume; CVector m_step; CVector m_stepRatio; float m_numSteps; bool m_flipAverage; char m_field_B9; bool m_isBoat; float m_volumeUnderWater; CVector m_impulsePoint; bool ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse); bool ProcessBuoyancyBoat(CVehicle *phys, float buoyancy, CVector *point, CVector *impulse, bool bNoTurnForce); void PreCalcSetup(CPhysical *phys, float buoyancy); void SimpleCalcBuoyancy(void); float SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition); void FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition); void FindWaterLevelNorm(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition, CVector *normal); bool CalcBuoyancyForce(CPhysical *phys, CVector *impulse, CVector *point); }; extern cBuoyancy mod_Buoyancy; ================================================ FILE: src/vehicles/HandlingMgr.cpp ================================================ #include "common.h" #include "main.h" #include "FileMgr.h" #include "Physical.h" #include "HandlingMgr.h" cHandlingDataMgr mod_HandlingManager; const char *HandlingFilename = "HANDLING.CFG"; const char VehicleNames[NUMHANDLINGS][14] = { "LANDSTAL", "IDAHO", "STINGER", "LINERUN", "PEREN", "SENTINEL", "PATRIOT", "FIRETRUK", "TRASH", "STRETCH", "MANANA", "INFERNUS", "PONY", "MULE", "CHEETAH", "AMBULAN", "FBICAR", "MOONBEAM", "ESPERANT", "TAXI", "KURUMA", "BOBCAT", "MRWHOOP", "BFINJECT", "POLICE", "ENFORCER", "SECURICA", "BANSHEE", "BUS", "RHINO", "BARRACKS", "TRAIN", "HELI", "DODO", "COACH", "CABBIE", "STALLION", "RUMPO", "RCBANDIT", "MAFIA", "AIRTRAIN", "DEADDODO", "FLATBED", "YANKEE", "GOLFCART", "VOODOO", "WASHING", "CUBAN", "ROMERO", "PACKER", "ADMIRAL", "GANGBUR", "ZEBRA", "TOPFUN", "GLENDALE", "OCEANIC", "HERMES", "SABRE1", "SABRETUR", "PHEONIX", "WALTON", "REGINA", "COMET", "DELUXO", "BURRITO", "SPAND", "BAGGAGE", "KAUFMAN", "RANCHER", "FBIRANCH", "VIRGO", "GREENWOO", "HOTRING", "SANDKING", "BLISTAC", "BOXVILLE", "BENSON", "DESPERAD", "LOVEFIST", "BLOODRA", "BLOODRB", "BIKE", "MOPED", "DIRTBIKE", "ANGEL", "FREEWAY", "PREDATOR", "SPEEDER", "REEFER", "RIO", "SQUALO", "TROPIC", "COASTGRD", "DINGHY", "MARQUIS", "CUPBOAT", "SEAPLANE", "SPARROW", "SEASPAR", "MAVERICK", "COASTMAV", "POLMAV", "HUNTER", "RCBARON", "RCGOBLIN", "RCCOPTER" }; cHandlingDataMgr::cHandlingDataMgr(void) { memset(this, 0, sizeof(*this)); } void cHandlingDataMgr::Initialise(void) { LoadHandlingData(); field_0 = 0.1f; fWheelFriction = 0.9f; field_8 = 1.0f; field_C = 0.8f; field_10 = 0.98f; } void cHandlingDataMgr::LoadHandlingData(void) { char *start, *end; char line[201]; // weird value char delim[4]; // not sure char *word; int field, handlingId; int keepGoing; tHandlingData *handling; tFlyingHandlingData *flyingHandling; tBoatHandlingData *boatHandling; tBikeHandlingData *bikeHandling; CFileMgr::SetDir("DATA"); CFileMgr::LoadFile(HandlingFilename, work_buff, sizeof(work_buff), "r"); CFileMgr::SetDir(""); start = (char*)work_buff; end = start+1; handling = nil; flyingHandling = nil; boatHandling = nil; bikeHandling = nil; keepGoing = 1; while(keepGoing){ // find end of line while(*end != '\n') end++; // get line strncpy(line, start, end - start); line[end - start] = '\0'; start = end+1; end = start+1; // yeah, this is kinda crappy if(strcmp(line, ";the end") == 0) keepGoing = 0; else if(line[0] != ';'){ if(line[0] == '!'){ // Bike data field = 0; strcpy(delim, " \t"); // FIX: game seems to use a do-while loop here for(word = strtok(line, delim); word; word = strtok(nil, delim)){ switch(field){ case 0: break; case 1: handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); assert(handlingId >= 0 && handlingId < NUMHANDLINGS); bikeHandling = GetBikePointer(handlingId); bikeHandling->nIdentifier = (tVehicleType)handlingId; break; case 2: bikeHandling->fLeanFwdCOM = atof(word); break; case 3: bikeHandling->fLeanFwdForce = atof(word); break; case 4: bikeHandling->fLeanBakCOM = atof(word); break; case 5: bikeHandling->fLeanBackForce = atof(word); break; case 6: bikeHandling->fMaxLean = atof(word); break; case 7: bikeHandling->fFullAnimLean = atof(word); break; case 8: bikeHandling->fDesLean = atof(word); break; case 9: bikeHandling->fSpeedSteer = atof(word); break; case 10: bikeHandling->fSlipSteer = atof(word); break; case 11: bikeHandling->fNoPlayerCOMz = atof(word); break; case 12: bikeHandling->fWheelieAng = atof(word); break; case 13: bikeHandling->fStoppieAng = atof(word); break; case 14: bikeHandling->fWheelieSteer = atof(word); break; case 15: bikeHandling->fWheelieStabMult = atof(word); break; case 16: bikeHandling->fStoppieStabMult = atof(word); break; } field++; } ConvertBikeDataToGameUnits(bikeHandling); }else if(line[0] == '$'){ // Flying data field = 0; strcpy(delim, " \t"); // FIX: game seems to use a do-while loop here for(word = strtok(line, delim); word; word = strtok(nil, delim)){ switch(field){ case 0: break; case 1: handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); assert(handlingId >= 0 && handlingId < NUMHANDLINGS); flyingHandling = GetFlyingPointer(handlingId); flyingHandling->nIdentifier = (tVehicleType)handlingId; break; case 2: flyingHandling->fThrust = atof(word); break; case 3: flyingHandling->fThrustFallOff = atof(word); break; case 4: flyingHandling->fYaw = atof(word); break; case 5: flyingHandling->fYawStab = atof(word); break; case 6: flyingHandling->fSideSlip = atof(word); break; case 7: flyingHandling->fRoll = atof(word); break; case 8: flyingHandling->fRollStab = atof(word); break; case 9: flyingHandling->fPitch = atof(word); break; case 10: flyingHandling->fPitchStab = atof(word); break; case 11: flyingHandling->fFormLift = atof(word); break; case 12: flyingHandling->fAttackLift = atof(word); break; case 13: flyingHandling->fMoveRes = atof(word); break; case 14: flyingHandling->vecTurnRes.x = atof(word); break; case 15: flyingHandling->vecTurnRes.y = atof(word); break; case 16: flyingHandling->vecTurnRes.z = atof(word); break; case 17: flyingHandling->vecSpeedRes.x = atof(word); break; case 18: flyingHandling->vecSpeedRes.y = atof(word); break; case 19: flyingHandling->vecSpeedRes.z = atof(word); break; } field++; } }else if(line[0] == '%'){ // Boat data field = 0; strcpy(delim, " \t"); // FIX: game seems to use a do-while loop here for(word = strtok(line, delim); word; word = strtok(nil, delim)){ switch(field){ case 0: break; case 1: handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); assert(handlingId >= 0 && handlingId < NUMHANDLINGS); boatHandling = GetBoatPointer(handlingId); boatHandling->nIdentifier = (tVehicleType)handlingId; break; case 2: boatHandling->fThrustY = atof(word); break; case 3: boatHandling->fThrustZ = atof(word); break; case 4: boatHandling->fThrustAppZ = atof(word); break; case 5: boatHandling->fAqPlaneForce = atof(word); break; case 6: boatHandling->fAqPlaneLimit = atof(word); break; case 7: boatHandling->fAqPlaneOffset = atof(word); break; case 8: boatHandling->fWaveAudioMult = atof(word); break; case 9: boatHandling->vecMoveRes.x = atof(word); break; case 10: boatHandling->vecMoveRes.y = atof(word); break; case 11: boatHandling->vecMoveRes.z = atof(word); break; case 12: boatHandling->vecTurnRes.x = atof(word); break; case 13: boatHandling->vecTurnRes.y = atof(word); break; case 14: boatHandling->vecTurnRes.z = atof(word); break; case 15: boatHandling->fLook_L_R_BehindCamHeight = atof(word); break; } field++; } }else{ field = 0; strcpy(delim, " \t"); // FIX: game seems to use a do-while loop here for(word = strtok(line, delim); word; word = strtok(nil, delim)){ switch(field){ case 0: handlingId = FindExactWord(word, (const char*)VehicleNames, 14, NUMHANDLINGS); assert(handlingId >= 0 && handlingId < NUMHANDLINGS); handling = &HandlingData[handlingId]; handling->nIdentifier = (tVehicleType)handlingId; break; case 1: handling->fMass = atof(word); break; case 2: handling->Dimension.x = atof(word); break; case 3: handling->Dimension.y = atof(word); break; case 4: handling->Dimension.z = atof(word); break; case 5: handling->CentreOfMass.x = atof(word); break; case 6: handling->CentreOfMass.y = atof(word); break; case 7: handling->CentreOfMass.z = atof(word); break; case 8: handling->nPercentSubmerged = atoi(word); break; case 9: handling->fTractionMultiplier = atof(word); break; case 10: handling->fTractionLoss = atof(word); break; case 11: handling->fTractionBias = atof(word); break; case 12: handling->Transmission.nNumberOfGears = atoi(word); break; case 13: handling->Transmission.fMaxVelocity = atof(word); break; case 14: handling->Transmission.fEngineAcceleration = atof(word) * 0.4; break; case 15: handling->Transmission.nDriveType = word[0]; break; case 16: handling->Transmission.nEngineType = word[0]; break; case 17: handling->fBrakeDeceleration = atof(word); break; case 18: handling->fBrakeBias = atof(word); break; case 19: handling->bABS = !!atoi(word); break; case 20: handling->fSteeringLock = atof(word); break; case 21: handling->fSuspensionForceLevel = atof(word); break; case 22: handling->fSuspensionDampingLevel = atof(word); break; case 23: handling->fSeatOffsetDistance = atof(word); break; case 24: handling->fCollisionDamageMultiplier = atof(word); break; case 25: handling->nMonetaryValue = atoi(word); break; case 26: handling->fSuspensionUpperLimit = atof(word); break; case 27: handling->fSuspensionLowerLimit = atof(word); break; case 28: handling->fSuspensionBias = atof(word); break; case 29: handling->fSuspensionAntidiveMultiplier = atof(word); break; case 30: sscanf(word, "%x", &handling->Flags); handling->Transmission.Flags = handling->Flags; break; case 31: handling->FrontLights = atoi(word); break; case 32: handling->RearLights = atoi(word); break; } field++; } ConvertDataToGameUnits(handling); } } } } int cHandlingDataMgr::FindExactWord(const char *word, const char *words, int wordLen, int numWords) { int i; for(i = 0; i < numWords; i++){ // BUG: the game does something really stupid here, it's fixed here if(strncmp(word, words, wordLen) == 0) return i; words += wordLen; } return numWords; } void cHandlingDataMgr::ConvertDataToGameUnits(tHandlingData *handling) { // convert distance to m, time to 1/50s float velocity, a, b; handling->Transmission.fEngineAcceleration *= 1.0f/(50.0f*50.0f); handling->Transmission.fMaxVelocity *= 1000.0f/(60.0f*60.0f * 50.0f); handling->fBrakeDeceleration *= 1.0f/(50.0f*50.0f); handling->fTurnMass = (sq(handling->Dimension.x) + sq(handling->Dimension.y)) * handling->fMass / 12.0f; if(handling->fTurnMass < 10.0f) handling->fTurnMass *= 5.0f; handling->fInvMass = 1.0f/handling->fMass; handling->fCollisionDamageMultiplier *= 2000.0f/handling->fMass; handling->fBuoyancy = 100.0f/handling->nPercentSubmerged * GRAVITY*handling->fMass; // Don't quite understand this. What seems to be going on is that // we calculate a drag (air resistance) deceleration for a given velocity and // find the intersection between that and the max engine acceleration. // at that point the car cannot accelerate any further and we've found the max velocity. a = 0.0f; b = 100.0f; velocity = handling->Transmission.fMaxVelocity; while(a < b && velocity > 0.0f){ velocity -= 0.01f; // what's the 1/6? a = handling->Transmission.fEngineAcceleration/6.0f; // no density or drag coefficient here... float a_drag = 0.5f*SQR(velocity) * handling->Dimension.x*handling->Dimension.z / handling->fMass; // can't make sense of this... maybe v - v/(drag + 1) ? but that doesn't make so much sense either b = -velocity * (1.0f/(a_drag + 1.0f) - 1.0f); } if(handling->nIdentifier == HANDLING_RCBANDIT){ handling->Transmission.fMaxCruiseVelocity = handling->Transmission.fMaxVelocity; handling->Transmission.fMaxReverseVelocity = -handling->Transmission.fMaxVelocity; }else if(handling->nIdentifier >= HANDLING_BIKE && handling->nIdentifier <= HANDLING_FREEWAY){ handling->Transmission.fMaxCruiseVelocity = velocity; handling->Transmission.fMaxVelocity = velocity * 1.2f; handling->Transmission.fMaxReverseVelocity = -0.05f; }else{ handling->Transmission.fMaxCruiseVelocity = velocity; handling->Transmission.fMaxVelocity = velocity * 1.2f; handling->Transmission.fMaxReverseVelocity = -0.2f; } if(handling->Transmission.nDriveType == '4') handling->Transmission.fEngineAcceleration /= 4.0f; else handling->Transmission.fEngineAcceleration /= 2.0f; handling->Transmission.InitGearRatios(); } void cHandlingDataMgr::ConvertBikeDataToGameUnits(tBikeHandlingData *handling) { handling->fMaxLean = Sin(DEGTORAD(handling->fMaxLean)); handling->fFullAnimLean = DEGTORAD(handling->fFullAnimLean); handling->fWheelieAng = Sin(DEGTORAD(handling->fWheelieAng)); handling->fStoppieAng = Sin(DEGTORAD(handling->fStoppieAng)); } int32 cHandlingDataMgr::GetHandlingId(const char *name) { int i; for(i = 0; i < NUMHANDLINGS; i++) if(strncmp(VehicleNames[i], name, 14) == 0) break; return i; } tFlyingHandlingData* cHandlingDataMgr::GetFlyingPointer(uint8 id) { if(id >= HANDLING_SEAPLANE && id <= HANDLING_RCCOPTER) return &FlyingHandlingData[id-HANDLING_SEAPLANE]; return &FlyingHandlingData[0]; } tBoatHandlingData* cHandlingDataMgr::GetBoatPointer(uint8 id) { if(id >= HANDLING_PREDATOR && id <= HANDLING_SEAPLANE) return &BoatHandlingData[id-HANDLING_PREDATOR]; return &BoatHandlingData[0]; } ================================================ FILE: src/vehicles/HandlingMgr.h ================================================ #pragma once #include "Transmission.h" enum tVehicleType { HANDLING_LANDSTAL, HANDLING_IDAHO, HANDLING_STINGER, HANDLING_LINERUN, HANDLING_PEREN, HANDLING_SENTINEL, HANDLING_PATRIOT, HANDLING_FIRETRUK, HANDLING_TRASH, HANDLING_STRETCH, HANDLING_MANANA, HANDLING_INFERNUS, HANDLING_PONY, HANDLING_MULE, HANDLING_CHEETAH, HANDLING_AMBULAN, HANDLING_FBICAR, HANDLING_MOONBEAM, HANDLING_ESPERANT, HANDLING_TAXI, HANDLING_KURUMA, HANDLING_BOBCAT, HANDLING_MRWHOOP, HANDLING_BFINJECT, HANDLING_POLICE, HANDLING_ENFORCER, HANDLING_SECURICA, HANDLING_BANSHEE, HANDLING_BUS, HANDLING_RHINO, HANDLING_BARRACKS, HANDLING_TRAIN, HANDLING_HELI, HANDLING_DODO, HANDLING_COACH, HANDLING_CABBIE, HANDLING_STALLION, HANDLING_RUMPO, HANDLING_RCBANDIT, HANDLING_MAFIA, HANDLING_AIRTRAIN, HANDLING_DEADDODO, HANDLING_FLATBED, HANDLING_YANKEE, HANDLING_GOLFCART, HANDLING_VOODOO, HANDLING_WASHING, HANDLING_CUBAN, HANDLING_ROMERO, HANDLING_PACKER, HANDLING_ADMIRAL, HANDLING_GANGBUR, HANDLING_ZEBRA, HANDLING_TOPFUN, HANDLING_GLENDALE, HANDLING_OCEANIC, HANDLING_HERMES, HANDLING_SABRE1, HANDLING_SABRETUR, HANDLING_PHEONIX, HANDLING_WALTON, HANDLING_REGINA, HANDLING_COMET, HANDLING_DELUXO, HANDLING_BURRITO, HANDLING_SPAND, HANDLING_BAGGAGE, HANDLING_KAUFMAN, HANDLING_RANCHER, HANDLING_FBIRANCH, HANDLING_VIRGO, HANDLING_GREENWOO, HANDLING_HOTRING, HANDLING_SANDKING, HANDLING_BLISTAC, HANDLING_BOXVILLE, HANDLING_BENSON, HANDLING_DESPERAD, HANDLING_LOVEFIST, HANDLING_BLOODRA, HANDLING_BLOODRB, HANDLING_BIKE, HANDLING_MOPED, HANDLING_DIRTBIKE, HANDLING_ANGEL, HANDLING_FREEWAY, HANDLING_PREDATOR, HANDLING_SPEEDER, HANDLING_REEFER, HANDLING_RIO, HANDLING_SQUALO, HANDLING_TROPIC, HANDLING_COASTGRD, HANDLING_DINGHY, HANDLING_MARQUIS, HANDLING_CUPBOAT, HANDLING_SEAPLANE, // both boat and plane! HANDLING_SPARROW, HANDLING_SEASPAR, HANDLING_MAVERICK, HANDLING_COASTMAV, HANDLING_POLMAV, HANDLING_HUNTER, HANDLING_RCBARON, HANDLING_RCGOBLIN, HANDLING_RCCOPTER, NUMHANDLINGS, NUMBIKEHANDLINGS = HANDLING_FREEWAY+1 - HANDLING_BIKE, NUMFLYINGHANDLINGS = HANDLING_RCCOPTER+1 - HANDLING_SEAPLANE, NUMBOATHANDLINGS = HANDLING_SEAPLANE+1 - HANDLING_PREDATOR, }; enum tField // most likely a handling field enum, never used so :shrug: { }; enum { HANDLING_1G_BOOST = 1, HANDLING_2G_BOOST = 2, HANDLING_REV_BONNET = 4, HANDLING_HANGING_BOOT = 8, HANDLING_NO_DOORS = 0x10, HANDLING_IS_VAN = 0x20, HANDLING_IS_BUS = 0x40, HANDLING_IS_LOW = 0x80, HANDLING_DBL_EXHAUST = 0x100, HANDLING_TAILGATE_BOOT = 0x200, HANDLING_NOSWING_BOOT = 0x400, HANDLING_NONPLAYER_STABILISER = 0x800, HANDLING_NEUTRALHANDLING = 0x1000, HANDLING_HAS_NO_ROOF = 0x2000, HANDLING_IS_BIG = 0x4000, HANDLING_HALOGEN_LIGHTS = 0x8000, HANDLING_IS_BIKE = 0x10000, HANDLING_IS_HELI = 0x20000, HANDLING_IS_PLANE = 0x40000, HANDLING_IS_BOAT = 0x80000, HANDLING_NO_EXHAUST = 0x100000, HANDLING_REARWHEEL_1ST = 0x200000, HANDLING_HANDBRAKE_TYRE = 0x400000, HANDLING_SIT_IN_BOAT = 0x800000, HANDLING_FAT_REARW = 0x1000000, HANDLING_NARROW_FRONTW = 0x2000000, HANDLING_GOOD_INSAND = 0x4000000, HANDLING_UNKNOWN = 0x8000000, // something for helis and planes }; struct tHandlingData { tVehicleType nIdentifier; float fMass; float fInvMass; float fTurnMass; CVector Dimension; CVector CentreOfMass; int8 nPercentSubmerged; float fBuoyancy; float fTractionMultiplier; cTransmission Transmission; float fBrakeDeceleration; float fBrakeBias; int8 bABS; float fSteeringLock; float fTractionLoss; float fTractionBias; float fUnused; float fSuspensionForceLevel; float fSuspensionDampingLevel; float fSuspensionUpperLimit; float fSuspensionLowerLimit; float fSuspensionBias; float fSuspensionAntidiveMultiplier; float fCollisionDamageMultiplier; uint32 Flags; float fSeatOffsetDistance; int32 nMonetaryValue; int8 FrontLights; int8 RearLights; }; struct tBikeHandlingData { tVehicleType nIdentifier; float fLeanFwdCOM; float fLeanFwdForce; float fLeanBakCOM; float fLeanBackForce; float fMaxLean; float fFullAnimLean; float fDesLean; float fSpeedSteer; float fSlipSteer; float fNoPlayerCOMz; float fWheelieAng; float fStoppieAng; float fWheelieSteer; float fWheelieStabMult; float fStoppieStabMult; }; struct tBoatHandlingData { tVehicleType nIdentifier; float fThrustY; float fThrustZ; float fThrustAppZ; float fAqPlaneForce; float fAqPlaneLimit; float fAqPlaneOffset; float fWaveAudioMult; float fLook_L_R_BehindCamHeight; CVector vecMoveRes; CVector vecTurnRes; }; struct tFlyingHandlingData { tVehicleType nIdentifier; float fThrust; float fThrustFallOff; float fYaw; float fYawStab; float fSideSlip; float fRoll; float fRollStab; float fPitch; float fPitchStab; float fFormLift; float fAttackLift; float fMoveRes; CVector vecTurnRes; CVector vecSpeedRes; }; class CVehicle; class cHandlingDataMgr { float field_0; // unused it seems public: float fWheelFriction; // wheel related private: float field_8; // float field_C; // unused it seems float field_10; // tHandlingData HandlingData[NUMHANDLINGS]; tBikeHandlingData BikeHandlingData[NUMBIKEHANDLINGS]; tFlyingHandlingData FlyingHandlingData[NUMFLYINGHANDLINGS]; tBoatHandlingData BoatHandlingData[NUMBOATHANDLINGS]; public: cHandlingDataMgr(void); void Initialise(void); void LoadHandlingData(void); int FindExactWord(const char *word, const char *words, int wordLen, int numWords); void ConvertDataToWorldUnits(tHandlingData *handling); void ConvertDataToGameUnits(tHandlingData *handling); void ConvertBikeDataToGameUnits(tBikeHandlingData *handling); int32 GetHandlingId(const char *name); tHandlingData *GetHandlingData(tVehicleType id) { return &HandlingData[id]; } tBikeHandlingData *GetBikePointer(uint8 id) { return &BikeHandlingData[id-HANDLING_BIKE]; } tFlyingHandlingData *GetFlyingPointer(uint8 id); tBoatHandlingData *GetBoatPointer(uint8 id); bool HasRearWheelDrive(tVehicleType id) { return HandlingData[id].Transmission.nDriveType != 'F'; } bool HasFrontWheelDrive(tVehicleType id) { return HandlingData[id].Transmission.nDriveType != 'R'; } }; extern cHandlingDataMgr mod_HandlingManager; ================================================ FILE: src/vehicles/Heli.cpp ================================================ #include "common.h" #include "main.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 "WindModifiers.h" #include "Timecycle.h" #include "TempColModels.h" #include "World.h" #include "WaterLevel.h" #include "Population.h" #include "PlayerPed.h" #include "CopPed.h" #include "Wanted.h" #include "DMAudio.h" #include "Object.h" #include "HandlingMgr.h" #include "Ropes.h" #include "Heli.h" #ifdef FIX_BUGS #include "Replay.h" #endif enum { HELI_STATUS_HOVER, HELI_STATUS_CHASE_PLAYER, HELI_STATUS_FLY_AWAY, HELI_STATUS_SHOT_DOWN, HELI_STATUS_HOVER2, }; CHeli *CHeli::pHelis[NUM_HELIS]; int16 CHeli::NumRandomHelis; uint32 CHeli::TestForNewRandomHelisTimer; bool CHeli::CatalinaHeliOn; bool CHeli::CatalinaHasBeenShotDown; bool CHeli::ScriptHeliOn; 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((tVehicleType)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_numSwat = 4; 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(); SetStatus(STATUS_HELI); m_bTestRight = true; m_fTargetOffset = 0.0f; m_fSearchLightX = m_fSearchLightY = 0.0f; m_aSwatState[0] = m_aSwatState[1] = m_aSwatState[2] = m_aSwatState[3] = 0; // BUG: not in game but gets initialized to CDCDCDCD in debug m_nLastShotTime = 0; } 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; CWindModifiers::RegisterOne(GetPosition(), 1); // 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; GetMatrix().GetPosition().z = 31.55f; m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); break; case 7: GetMatrix().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){ GetMatrix().GetPosition().x = GetMatrix().GetPosition().x*0.99f + target.x*0.01f; GetMatrix().GetPosition().y = GetMatrix().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; } if(m_numSwat) SendDownSwat(); break; 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; } break; } // 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; 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; } GetMatrix().GetPosition().x += m_vecMoveSpeed.x*CTimer::GetTimeStep(); GetMatrix().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; GetMatrix().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) / (60.0f-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); } #ifdef FIX_BUGS if (!CReplay::IsPlayingBack()) #endif { // Shoot int shootTimeout; if (m_heliType == HELI_TYPE_RANDOM) { switch (FindPlayerPed()->m_pWanted->GetWantedLevel()) { 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(); } } } } // Process ropes for(i = 0; i < 4; i++){ if(m_aSwatState[i] == 0) continue; m_aSwatState[i]--; CRopes::RegisterRope((uintptr)this + i, GetMatrix()*FindSwatPositionRelativeToHeli(i), false); if(m_aSwatState[i] == 0){ CVector speed = Multiply3x3(GetMatrix(), 0.05f*FindSwatPositionRelativeToHeli(i)); speed.z = 0.0f; CRopes::SetSpeedOfTopNode((uintptr)this + i, speed); } } RemoveAndAdd(); bIsInSafePosition = true; GetMatrix().UpdateRW(); UpdateRwFrame(); } void CHeli::PreRender(void) { float radius = (GetPosition().z - FindPlayerCoors().z - 10.0f - 1.0f) * 0.3f + 10.0f; HeliDustGenerate(this, radius, FindPlayerCoors().z, Max(16.0f - 4.0f*CTimer::GetTimeStep(), 2.0f)); CShadows::StoreShadowForVehicle(this, VEH_SHD_TYPE_HELI); } 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, nil, false); 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); obj->bDontStream = true; // 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->SetIsStatic(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; } CVector CHeli::FindSwatPositionRelativeToHeli(int n) { switch(n){ case 0: return CVector(-1.2f, -1.0f, -0.5f); case 1: return CVector( 1.2f, -1.0f, -0.5f); case 2: return CVector(-1.2f, 1.0f, -0.5f); case 3: return CVector( 1.2f, 1.0f, -0.5f); default: return CVector(0.0f, 0.0f, 0.0f); } } bool CHeli::SendDownSwat(void) { if(m_numSwat == 0 || !CStreaming::HasModelLoaded(MI_SWAT) || CGeneral::GetRandomNumber() & 0x7F || (GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f) return false; CMatrix mat(GetMatrix()); CVector pos = Multiply3x3(mat, FindSwatPositionRelativeToHeli(m_numSwat-1)) + GetPosition(); float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, nil); if(Abs(FindPlayerCoors().z - groundZ) < 2.5f && CRopes::RegisterRope((uintptr)this + m_numSwat-1, pos, false)){ CCopPed *swat = (CCopPed*)CPopulation::AddPed(PEDTYPE_COP, COP_HELI_SWAT, pos); swat->bUsesCollision = false; swat->m_pRopeEntity = this; RegisterReference(&swat->m_pRopeEntity); m_numSwat--; swat->m_nRopeID = (uintptr)this + m_numSwat; m_aSwatState[m_numSwat] = 255; CAnimManager::BlendAnimation(swat->GetClump(), ASSOCGRP_STD, ANIM_STD_ABSEIL, 4.0f); return true; } return false; } void CHeli::InitHelis(void) { int i; NumRandomHelis = 0; TestForNewRandomHelisTimer = 0; CatalinaHeliOn = false; ScriptHeliOn = false; for(i = 0; i < NUM_HELIS; i++) pHelis[i] = nil; ((CVehicleModelInfo*)CModelInfo::GetModelInfo(MI_CHOPPER))->SetColModel(&CTempColModels::ms_colModelPed1); } CHeli* CHeli::GenerateHeli(bool catalina) { CHeli *heli; CVector heliPos; int i; if(catalina) assert(0 && "can't create catalina's heli"); 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)/0x100 * 6.28f; heliPos.x += 250.0f*Sin(angle); heliPos.y += 250.0f*Cos(angle); if(heliPos.x < -2000.0f-400.0f || heliPos.x > 2000.0f-400.0f || heliPos.y < -2000.0f || heliPos.y > 2000.0f){ heliPos = FindPlayerCoors(); 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->SetStatus(STATUS_ABANDONED); heli->bIsLocked = true; int id = -1; bool found = false; while(!found){ id++; found = true; for(i = 0; i < 4; i++) if(pHelis[i] && 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 = #ifdef FIX_BUGS CReplay::IsPlayingBack() ? 0 : #endif FindPlayerPed()->m_pWanted->NumOfHelisRequired(); if(CCullZones::PlayerNoRain() || CGame::IsInInterior()) numHelisRequired = 0; 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; } // 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, 224, 224, 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&7], 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::PeopleKilledByPlayer += 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]->GetForward(); CExplosion::AddExplosion(nil, nil, EXPLOSION_HELI2, 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 or under water 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::GetRandomTrueFalse() ? 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)/lineLength; 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::GetRandomTrueFalse() ? 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::TestSniperCollision(CVector *line0, CVector *line1) { int i; bool hit = false; for(i = 0; i < NUM_HELIS; i++){ if(pHelis[i] && !pHelis[i]->bBulletProof) { CVector pilotPos = pHelis[i]->GetMatrix() * CVector(-0.43f, 1.49f, 1.5f); if(CCollision::DistToLine(line0, line1, &pilotPos) < 0.8f){ pHelis[i]->m_fAngularSpeed = CGeneral::GetRandomTrueFalse() ? 0.05f : -0.05f; pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN; pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 9999999; pHelis[i]->m_numSwat = 0; 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; } ================================================ FILE: src/vehicles/Heli.h ================================================ #pragma once #include "Vehicle.h" class CObject; enum eHeliNodes { HELI_CHASSIS = 1, HELI_TOPROTOR, HELI_BACKROTOR, HELI_TAIL, HELI_TOPKNOT, HELI_SKID_LEFT, HELI_SKID_RIGHT, NUM_HELI_NODES }; enum { HELI_RANDOM0, HELI_RANDOM1, HELI_SCRIPT, HELI_CATALINA, // TODO 2 in VC NUM_HELIS }; enum { HELI_TYPE_RANDOM, HELI_TYPE_SCRIPT, HELI_TYPE_CATALINA, }; class CHeli : public CVehicle { public: RwFrame *m_aHeliNodes[NUM_HELI_NODES]; int8 m_heliStatus; float m_fSearchLightX; float m_fSearchLightY; uint32 m_nExplosionTimer; float m_fRotation; float m_fAngularSpeed; float m_fTargetZ; float m_fSearchLightIntensity; int8 m_nHeliId; int8 m_heliType; int8 m_pathState; int8 m_numSwat; uint8 m_aSwatState[4]; float m_aSearchLightHistoryX[6]; float m_aSearchLightHistoryY[6]; uint32 m_nSearchLightTimer; uint32 m_nShootTimer; uint32 m_nLastShotTime; uint32 m_nBulletDamage; float m_fRotorRotation; float m_fHeliDustZ[8]; uint32 m_nPoliceShoutTimer; float m_fTargetOffset; bool m_bTestRight; static CHeli *pHelis[NUM_HELIS]; static int16 NumRandomHelis; static uint32 TestForNewRandomHelisTimer; static bool CatalinaHeliOn; static bool CatalinaHasBeenShotDown; static bool ScriptHeliOn; CHeli(int32 id, uint8 CreatedBy); // from CEntity void SetModelIndex(uint32 id); void ProcessControl(void); void PreRender(void); void Render(void); void PreRenderAlways(void); CObject *SpawnFlyingComponent(int32 component); CVector FindSwatPositionRelativeToHeli(int n); bool SendDownSwat(void); static void InitHelis(void); static CHeli *GenerateHeli(bool catalina); // out of class in III PC and later because of SecuROM static void UpdateHelis(void); static void SpecialHeliPreRender(void); static bool TestRocketCollision(CVector *coors); static bool TestBulletCollision(CVector *line0, CVector *line1, CVector *bulletPos, int32 damage); static bool TestSniperCollision(CVector *line0, CVector *line1); static void StartCatalinaFlyBy(void); // out of class in III PC and later because of SecuROM static void RemoveCatalinaHeli(void); static CHeli *FindPointerToCatalinasHeli(void); static void CatalinaTakeOff(void); static void MakeCatalinaHeliFlyAway(void); static bool HasCatalinaBeenShotDown(void); static void ActivateHeli(bool activate); }; ================================================ FILE: src/vehicles/Plane.cpp ================================================ #include "common.h" #include "main.h" #include "General.h" #include "CutsceneMgr.h" #include "ModelIndices.h" #include "FileMgr.h" #include "Streaming.h" #include "Replay.h" #include "Camera.h" #include "DMAudio.h" #include "Wanted.h" #include "Coronas.h" #include "Particle.h" #include "Explosion.h" #include "Fluff.h" #include "World.h" #include "HandlingMgr.h" #include "Heli.h" #include "Plane.h" #include "MemoryHeap.h" CPlaneNode *pPathNodes; CPlaneNode *pPath2Nodes; CPlaneNode *pPath3Nodes; CPlaneNode *pPath4Nodes; int32 NumPathNodes; int32 NumPath2Nodes; int32 NumPath3Nodes; int32 NumPath4Nodes; float TotalLengthOfFlightPath; float TotalLengthOfFlightPath2; float TotalLengthOfFlightPath3; float TotalLengthOfFlightPath4; float TotalDurationOfFlightPath; float TotalDurationOfFlightPath2; float TotalDurationOfFlightPath3; float TotalDurationOfFlightPath4; float LandingPoint; float TakeOffPoint; CPlaneInterpolationLine aPlaneLineBits[6]; float PlanePathPosition[3]; float OldPlanePathPosition[3]; float PlanePathSpeed[3]; float PlanePath2Position[5]; float PlanePath3Position[4]; float PlanePath2Speed[5]; float PlanePath3Speed[4]; enum { CESNA_STATUS_NONE, // doesn't even exist CESNA_STATUS_FLYING, CESNA_STATUS_DESTROYED, CESNA_STATUS_LANDED, }; int32 CesnaMissionStatus; int32 CesnaMissionStartTime; CPlane *pDrugRunCesna; int32 DropOffCesnaMissionStatus; int32 DropOffCesnaMissionStartTime; CPlane *pDropOffCesna; CPlane::CPlane(int32 id, uint8 CreatedBy) : CVehicle(CreatedBy) { CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); m_vehType = VEHICLE_TYPE_PLANE; pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)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_bTempPlane = false; SetStatus(STATUS_PLANE); bIsBIGBuilding = true; m_level = LEVEL_GENERIC; m_isFarAway = false; #ifdef CPLANE_ROTORS m_fRotorRotation = 0.0f; #endif } CPlane::~CPlane() { DeleteRwObject(); } void CPlane::SetModelIndex(uint32 id) { CVehicle::SetModelIndex(id); #ifdef CPLANE_ROTORS int i; for(i = 0; i < NUM_PLANE_NODES; i++) m_aPlaneNodes[i] = nil; if(GetModelIndex() == MI_CHOPPER){ // This is surprisingly annoying... RwFrame *heliNodes[NUM_HELI_NODES]; for(i = 0; i < NUM_HELI_NODES; i++) heliNodes[i] = nil; CClumpModelInfo::FillFrameArray(GetClump(), heliNodes); m_aPlaneNodes[PLANE_TOPROTOR] = heliNodes[HELI_TOPROTOR]; m_aPlaneNodes[PLANE_BACKROTOR] = heliNodes[HELI_BACKROTOR]; }else CClumpModelInfo::FillFrameArray(GetClump(), m_aPlaneNodes); #endif } 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; if(CReplay::IsPlayingBack()) return; if(GetModelIndex() == MI_AIRTRAIN){ if(GetPosition().z > 100.0f) CPlaneTrails::RegisterPoint(GetPosition(), m_nPlaneId); }else if(GetModelIndex() == MI_DEADDODO) CPlaneBanners::RegisterPoint(GetPosition(), m_nPlaneId); // 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&7], 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); 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(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), dir, nil, CGeneral::GetRandomNumberInRange(0.1f, 1.0f), colors[nFrameGen&7], 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 == 62){ 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 GetMatrix().SetTranslateOnly((posRear + posFront) / 2.0f); GetMatrix().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); GetMatrix().GetRight() = right; GetMatrix().GetUp() = up; GetMatrix().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()).MagnitudeSqr2D() < sq(300.0f)); }else{ float planePathPosition; float totalLengthOfFlightPath; CPlaneNode *pathNodes; float planePathSpeed; int numPathNodes; if(GetModelIndex() == MI_CHOPPER){ planePathPosition = PlanePath3Position[m_nPlaneId]; totalLengthOfFlightPath = TotalLengthOfFlightPath3; pathNodes = pPath3Nodes; planePathSpeed = PlanePath3Speed[m_nPlaneId]; numPathNodes = NumPath3Nodes; }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 30 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 GetMatrix().SetTranslateOnly((posRear + posFront) / 2.0f); GetMatrix().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); GetMatrix().GetRight() = right; GetMatrix().GetUp() = up; GetMatrix().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()).MagnitudeSqr2D() < 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){ PUSH_MEMID(MEMID_WORLD); m_rwObject = CModelInfo::GetModelInfo(mi->m_planeLodId)->CreateInstance(); POP_MEMID(); if(m_rwObject) m_matrix.AttachRW(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); } #ifdef CPLANE_ROTORS CMatrix mat; CVector pos; m_fRotorRotation += 3.14f/6.5f; if(m_fRotorRotation > 6.28f) m_fRotorRotation -= 6.28f; if(m_aPlaneNodes[PLANE_TOPROTOR]){ mat.Attach(RwFrameGetMatrix(m_aPlaneNodes[PLANE_TOPROTOR])); pos = mat.GetPosition(); mat.SetRotateZ(m_fRotorRotation); mat.Translate(pos); mat.UpdateRW(); } if(m_aPlaneNodes[PLANE_BACKROTOR]){ mat.Attach(RwFrameGetMatrix(m_aPlaneNodes[PLANE_BACKROTOR])); pos = mat.GetPosition(); mat.SetRotateX(m_fRotorRotation); mat.Translate(pos); mat.UpdateRW(); } #endif } void CPlane::Render(void) { if(!CCutsceneMgr::IsRunning()) 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 for(i = 0; i < NumPathNodes; i++){ if(pPathNodes[i].p.z < 14.0f){ pPathNodes[i].p.z = 14.0f; 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-500.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 = 618.75f/500.0f; time += 500.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); position += 500.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 = -618.75f/500.0f; time += 500.0f/((CRUISE_SPEED+TAXI_SPEED)/2.0f); position += 500.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; } // Heli if(pPath3Nodes == nil){ pPath3Nodes = LoadPath("data\\paths\\flight3.dat", NumPath3Nodes, TotalLengthOfFlightPath3, false); TotalDurationOfFlightPath3 = TotalLengthOfFlightPath3/CRUISE_SPEED; } CStreaming::LoadAllRequestedModels(false); CStreaming::RequestModel(MI_AIRTRAIN, 0); CStreaming::LoadAllRequestedModels(false); // NB: 3 hardcoded also in CPlaneTrails for(i = 0; i < 3; i++){ CPlane *plane = new CPlane(MI_AIRTRAIN, PERMANENT_VEHICLE); plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); plane->SetStatus(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++){ for(lp = 0; work_buff[bp] != '\n' && work_buff[bp] != '\0'; 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; } int32 LastTimeInPlane, LastTimeNotInPlane; bool bCesnasActivated; bool bHelisActivated; 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/5) & 0x7FFFF)*t; PlanePath2Position[2] = CRUISE_SPEED * ((time + 0x80000/5*2) & 0x7FFFF)*t; PlanePath2Position[3] = CRUISE_SPEED * ((time + 0x80000/5*3) & 0x7FFFF)*t; PlanePath2Position[4] = CRUISE_SPEED * ((time + 0x80000/5*4) & 0x7FFFF)*t; PlanePath2Speed[0] = CRUISE_SPEED*t; PlanePath2Speed[1] = CRUISE_SPEED*t; PlanePath2Speed[2] = CRUISE_SPEED*t; PlanePath2Speed[3] = CRUISE_SPEED*t; PlanePath2Speed[4] = CRUISE_SPEED*t; t = TotalDurationOfFlightPath3/0x80000; PlanePath3Position[0] = CRUISE_SPEED * (time & 0x7FFFF)*t; PlanePath3Position[1] = CRUISE_SPEED * ((time + 0x80000/4) & 0x7FFFF)*t; PlanePath3Position[2] = CRUISE_SPEED * ((time + 0x80000/4*2) & 0x7FFFF)*t; PlanePath3Position[3] = CRUISE_SPEED * ((time + 0x80000/4*3) & 0x7FFFF)*t; PlanePath3Speed[0] = CRUISE_SPEED*t; PlanePath3Speed[1] = CRUISE_SPEED*t; PlanePath3Speed[2] = CRUISE_SPEED*t; PlanePath3Speed[3] = CRUISE_SPEED*t; if(FindPlayerVehicle() && (FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || FindPlayerVehicle()->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE)) LastTimeInPlane = CTimer::GetTimeInMilliseconds(); else LastTimeNotInPlane = CTimer::GetTimeInMilliseconds(); if(CTimer::GetTimeInMilliseconds() - LastTimeNotInPlane > 10000){ if(!bCesnasActivated){ if(CStreaming::HasModelLoaded(MI_DEADDODO)){ for(i = 0; i < 5; i++){ CPlane *plane = new CPlane(MI_DEADDODO, PERMANENT_VEHICLE); plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); plane->SetStatus(STATUS_ABANDONED); plane->bIsLocked = true; plane->m_nPlaneId = i; plane->m_nCurPathNode = 0; plane->m_bTempPlane = true; CWorld::Add(plane); } bCesnasActivated = true; }else CStreaming::RequestModel(MI_DEADDODO, 0); } if(!bHelisActivated){ if(CStreaming::HasModelLoaded(MI_CHOPPER)){ for(i = 0; i < 4; i++){ CPlane *plane = new CPlane(MI_CHOPPER, PERMANENT_VEHICLE); plane->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); plane->SetStatus(STATUS_ABANDONED); plane->bIsLocked = true; plane->m_nPlaneId = i; plane->m_nCurPathNode = 0; plane->m_bTempPlane = true; CWorld::Add(plane); } bHelisActivated = true; }else CStreaming::RequestModel(MI_CHOPPER, 0); } }else if(CTimer::GetTimeInMilliseconds() - LastTimeInPlane > 10000) RemoveTemporaryPlanes(); } void CPlane::RemoveTemporaryPlanes(void) { int i; if(!bHelisActivated && !bCesnasActivated) return; i = CPools::GetVehiclePool()->GetSize(); while(--i >= 0){ CPlane *plane = (CPlane*)CPools::GetVehiclePool()->GetSlot(i); if(plane && plane->IsPlane() && plane->m_bTempPlane){ CWorld::Remove(plane); delete plane; } } bCesnasActivated = false; bHelisActivated = false; } 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_DEADDODO) && #else plane->GetModelIndex() != MI_AIRTRAIN && plane->GetModelIndex() == MI_DEADDODO && // 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; } // unused // 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->SetStatus(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"); } // unused 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->SetStatus(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"); } // all unused const CVector CPlane::FindDrugPlaneCoordinates(void) { return pDrugRunCesna->GetPosition(); } const CVector CPlane::FindDropOffCesnaCoordinates(void) { return pDropOffCesna->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; } void CPlane::Load(void) { RemoveTemporaryPlanes(); } void CPlane::Save(void) { RemoveTemporaryPlanes(); } ================================================ FILE: src/vehicles/Plane.h ================================================ #pragma once #include "Vehicle.h" enum ePlaneNodes { #ifdef CPLANE_ROTORS // for heli PLANE_TOPROTOR, PLANE_BACKROTOR, #endif PLANE_WHEEL_FRONT = 2, PLANE_WHEEL_READ, NUM_PLANE_NODES }; struct CPlaneNode { CVector p; // position float t; // xy-distance from start on path bool bOnGround; // i.e. not flying }; struct CPlaneInterpolationLine { uint8 type; float time; // when does this keyframe start // initial values at start of frame float position; float speed; float acceleration; }; class CPlane : public CVehicle { public: #ifdef CPLANE_ROTORS RwFrame *m_aPlaneNodes[NUM_PLANE_NODES]; float m_fRotorRotation; #endif int16 m_nPlaneId; int16 m_isFarAway; int16 m_nCurPathNode; float m_fSpeed; uint32 m_nFrameWhenHit; bool m_bHasBeenHit; bool m_bIsDrugRunCesna; bool m_bIsDropOffCesna; bool m_bTempPlane; CPlane(int32 id, uint8 CreatedBy); ~CPlane(void); // from CEntity void SetModelIndex(uint32 id); void DeleteRwObject(void); void ProcessControl(void); void PreRender(void); void Render(void); void FlagToDestroyWhenNextProcessed() { bRemoveFromWorld = true; } static void InitPlanes(void); static void Shutdown(void); static CPlaneNode *LoadPath(char const *filename, int32 &numNodes, float &totalLength, bool loop); static void RemoveTemporaryPlanes(void); static void UpdatePlanes(void); static bool TestRocketCollision(CVector *rocketPos); static void CreateIncomingCesna(void); static void CreateDropOffCesna(void); static const CVector FindDrugPlaneCoordinates(void); static const CVector FindDropOffCesnaCoordinates(void); static bool HasCesnaLanded(void); static bool HasCesnaBeenDestroyed(void); static bool HasDropOffCesnaBeenShotDown(void); static void Load(void); static void Save(void); }; extern float LandingPoint; extern float TakeOffPoint; extern float PlanePathPosition[3]; ================================================ FILE: src/vehicles/Train.cpp ================================================ #include "common.h" #include "main.h" #include "Timer.h" #include "ModelIndices.h" #include "FileMgr.h" #include "Streaming.h" #include "Pad.h" #include "Camera.h" #include "Coronas.h" #include "World.h" #include "Ped.h" #include "DMAudio.h" #include "HandlingMgr.h" #include "Train.h" #include "AudioScriptObject.h" static CTrainNode* pTrackNodes; static int16 NumTrackNodes; static float StationDist[3] = { 873.0f, 1522.0f, 2481.0f }; static float TotalLengthOfTrack; static float TotalDurationOfTrack; static CTrainInterpolationLine aLineBits[17]; static float EngineTrackPosition[2]; static float EngineTrackSpeed[2]; static CTrainNode* pTrackNodes_S; static int16 NumTrackNodes_S; static float StationDist_S[4] = { 55.0f, 1388.0f, 2337.0f, 3989.0f }; static float TotalLengthOfTrack_S; static float TotalDurationOfTrack_S; static CTrainInterpolationLine aLineBits_S[18]; static float EngineTrackPosition_S[4]; static float EngineTrackSpeed_S[4]; CVector CTrain::aStationCoors[3]; CVector CTrain::aStationCoors_S[4]; static bool bTrainArrivalAnnounced[3] = {false, false, false}; CTrain::CTrain(int32 id, uint8 CreatedBy) : CVehicle(CreatedBy) { #ifdef GTA_TRAIN CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); m_vehType = VEHICLE_TYPE_TRAIN; pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)mi->m_handlingId); SetModelIndex(id); Doors[0].Init(0.8f, 0.0f, 1, 0); Doors[1].Init(-0.8f, 0.0f, 0, 0); m_fMass = 100000000.0f; m_fTurnMass = 100000000.0f; m_fAirResistance = 0.9994f; m_fElasticity = 0.05f; m_bProcessDoor = true; m_bTrainStopping = false; m_nTrackId = TRACK_ELTRAIN; m_nNumMaxPassengers = 5; m_nDoorTimer = CTimer::GetTimeInMilliseconds(); m_nDoorState = TRAIN_DOOR_CLOSED; bUsesCollision = true; SetStatus(STATUS_TRAIN_MOVING); #ifdef FIX_BUGS m_isFarAway = true; #endif #else assert(0 && "No trains in this game"); #endif } void CTrain::SetModelIndex(uint32 id) { #ifdef GTA_TRAIN int i; CVehicle::SetModelIndex(id); for(i = 0; i < NUM_TRAIN_NODES; i++) m_aTrainNodes[i] = nil; CClumpModelInfo::FillFrameArray(GetClump(), m_aTrainNodes); #endif } void CTrain::ProcessControl(void) { #ifdef GTA_TRAIN if(gbModelViewer || m_isFarAway && (CTimer::GetFrameCounter() + m_nWagonId) & 0xF) return; CTrainNode *trackNodes; int16 numTrackNodes; float totalLengthOfTrack; float *engineTrackPosition; float *engineTrackSpeed; if(m_nTrackId == TRACK_SUBWAY){ trackNodes = pTrackNodes_S; numTrackNodes = NumTrackNodes_S; totalLengthOfTrack = TotalLengthOfTrack_S; engineTrackPosition = EngineTrackPosition_S; engineTrackSpeed = EngineTrackSpeed_S; }else{ trackNodes = pTrackNodes; numTrackNodes = NumTrackNodes; totalLengthOfTrack = TotalLengthOfTrack; engineTrackPosition = EngineTrackPosition; engineTrackSpeed = EngineTrackSpeed; } float trackPositionRear = engineTrackPosition[m_nWagonGroup] - m_fWagonPosition; if(trackPositionRear < 0.0f) trackPositionRear += totalLengthOfTrack; // Advance current node to appropriate position float pos1, pos2; int nextTrackNode = m_nCurTrackNode + 1; pos1 = trackNodes[m_nCurTrackNode].t; if(nextTrackNode < numTrackNodes) pos2 = trackNodes[nextTrackNode].t; else{ nextTrackNode = 0; pos2 = totalLengthOfTrack; } while(trackPositionRear < pos1 || trackPositionRear > pos2){ m_nCurTrackNode = (m_nCurTrackNode+1) % numTrackNodes; nextTrackNode = m_nCurTrackNode + 1; pos1 = trackNodes[m_nCurTrackNode].t; if(nextTrackNode < numTrackNodes) pos2 = trackNodes[nextTrackNode].t; else{ nextTrackNode = 0; pos2 = totalLengthOfTrack; } } float dist = trackNodes[nextTrackNode].t - trackNodes[m_nCurTrackNode].t; if(dist < 0.0f) dist += totalLengthOfTrack; float f = (trackPositionRear - trackNodes[m_nCurTrackNode].t)/dist; CVector posRear = (1.0f - f)*trackNodes[m_nCurTrackNode].p + f*trackNodes[nextTrackNode].p; // Now same again for the front float trackPositionFront = trackPositionRear + 20.0f; if(trackPositionFront > totalLengthOfTrack) trackPositionFront -= totalLengthOfTrack; int curTrackNodeFront = m_nCurTrackNode; int nextTrackNodeFront = curTrackNodeFront + 1; pos1 = trackNodes[curTrackNodeFront].t; if(nextTrackNodeFront < numTrackNodes) pos2 = trackNodes[nextTrackNodeFront].t; else{ nextTrackNodeFront = 0; pos2 = totalLengthOfTrack; } while(trackPositionFront < pos1 || trackPositionFront > pos2){ curTrackNodeFront = (curTrackNodeFront+1) % numTrackNodes; nextTrackNodeFront = curTrackNodeFront + 1; pos1 = trackNodes[curTrackNodeFront].t; if(nextTrackNodeFront < numTrackNodes) pos2 = trackNodes[nextTrackNodeFront].t; else{ nextTrackNodeFront = 0; pos2 = totalLengthOfTrack; } } dist = trackNodes[nextTrackNodeFront].t - trackNodes[curTrackNodeFront].t; if(dist < 0.0f) dist += totalLengthOfTrack; f = (trackPositionFront - trackNodes[curTrackNodeFront].t)/dist; CVector posFront = (1.0f - f)*trackNodes[curTrackNodeFront].p + f*trackNodes[nextTrackNodeFront].p; // Now set matrix SetPosition((posRear + posFront)/2.0f); CVector fwd = posFront - posRear; fwd.Normalise(); CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); right.Normalise(); CVector up = CrossProduct(right, fwd); GetRight() = right; GetUp() = up; GetForward() = fwd; // Set speed m_vecMoveSpeed = fwd*engineTrackSpeed[m_nWagonGroup]/60.0f; m_fSpeed = engineTrackSpeed[m_nWagonGroup]/60.0f; m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); if(engineTrackSpeed[m_nWagonGroup] > 0.001f){ SetStatus(STATUS_TRAIN_MOVING); m_bTrainStopping = false; m_bProcessDoor = true; }else{ SetStatus(STATUS_TRAIN_NOT_MOVING); m_bTrainStopping = true; } m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(250.0f)); if(m_fWagonPosition == 20.0f && m_fSpeed > 0.0001f) if(Abs(TheCamera.GetPosition().z - GetPosition().z) < 15.0f) CPad::GetPad(0)->StartShake_Train(GetPosition().x, GetPosition().y); if(m_bProcessDoor) switch(m_nDoorState){ case TRAIN_DOOR_CLOSED: if(m_bTrainStopping){ m_nDoorTimer = CTimer::GetTimeInMilliseconds() + 1000; m_nDoorState = TRAIN_DOOR_OPENING; DMAudio.PlayOneShot(m_audioEntityId, SOUND_TRAIN_DOOR_CLOSE, 0.0f); } break; case TRAIN_DOOR_OPENING: if(CTimer::GetTimeInMilliseconds() < m_nDoorTimer){ OpenTrainDoor(1.0f - (m_nDoorTimer - CTimer::GetTimeInMilliseconds())/1000.0f); }else{ OpenTrainDoor(1.0f); m_nDoorState = TRAIN_DOOR_OPEN; } break; case TRAIN_DOOR_OPEN: if(!m_bTrainStopping){ m_nDoorTimer = CTimer::GetTimeInMilliseconds() + 1000; m_nDoorState = TRAIN_DOOR_CLOSING; DMAudio.PlayOneShot(m_audioEntityId, SOUND_TRAIN_DOOR_OPEN, 0.0f); } break; case TRAIN_DOOR_CLOSING: if(CTimer::GetTimeInMilliseconds() < m_nDoorTimer){ OpenTrainDoor((m_nDoorTimer - CTimer::GetTimeInMilliseconds())/1000.0f); }else{ OpenTrainDoor(0.0f); m_nDoorState = TRAIN_DOOR_CLOSED; m_bProcessDoor = false; } break; } GetMatrix().UpdateRW(); UpdateRwFrame(); RemoveAndAdd(); bIsStuck = false; bIsInSafePosition = true; bWasPostponed = false; // request/remove model if(m_isFarAway){ if(m_rwObject) DeleteRwObject(); }else if(CStreaming::HasModelLoaded(MI_TRAIN)){ if(m_rwObject == nil){ m_modelIndex = -1; SetModelIndex(MI_TRAIN); } }else{ if(FindPlayerCoors().z * GetPosition().z >= 0.0f) CStreaming::RequestModel(MI_TRAIN, STREAMFLAGS_DEPENDENCY); } // Hit stuff if(m_bIsFirstWagon && GetStatus()== STATUS_TRAIN_MOVING){ CVector front = GetPosition() + GetForward()*GetColModel()->boundingBox.max.y + m_vecMoveSpeed*CTimer::GetTimeStep(); int x, xmin, xmax; int y, ymin, ymax; xmin = CWorld::GetSectorIndexX(front.x - 3.0f); if(xmin < 0) xmin = 0; xmax = CWorld::GetSectorIndexX(front.x + 3.0f); if(xmax > NUMSECTORS_X-1) xmax = NUMSECTORS_X-1; ymin = CWorld::GetSectorIndexY(front.y - 3.0f); if(ymin < 0) ymin = 0; ymax = CWorld::GetSectorIndexY(front.y + 3.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); TrainHitStuff(s->m_lists[ENTITYLIST_VEHICLES]); TrainHitStuff(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); TrainHitStuff(s->m_lists[ENTITYLIST_PEDS]); TrainHitStuff(s->m_lists[ENTITYLIST_PEDS_OVERLAP]); } } #endif GTA_TRAIN } void CTrain::PreRender(void) { #ifdef GTA_TRAIN CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); if(m_bIsFirstWagon){ 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()); if(behindness < 0.0f){ // In front of train CVector lightPos = mi->m_positions[TRAIN_POS_LIGHT_FRONT]; CVector lightR = GetMatrix() * lightPos; CVector lightL = lightR; lightL -= GetRight()*2.0f*lightPos.x; float intensity = -0.4f*behindness + 0.2f; float size = 1.0f - behindness; if(behindness < -0.9f && camDist < 35.0f){ // directly in front CCoronas::RegisterCorona((uintptr)this + 10, 255*intensity, 255*intensity, 255*intensity, 255, lightL, size, 80.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, 80.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, 80.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, 80.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); } } } if(m_bIsLastWagon){ CVector lightPos = mi->m_positions[TRAIN_POS_LIGHT_REAR]; CVector lightR = GetMatrix() * lightPos; CVector lightL = lightR; lightL -= GetRight()*2.0f*lightPos.x; CCoronas::RegisterCorona((uintptr)this + 12, 255, 0, 0, 255, lightL, 1.0f, 80.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); CCoronas::RegisterCorona((uintptr)this + 13, 255, 0, 0, 255, lightR, 1.0f, 80.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); } #endif } void CTrain::Render(void) { #ifdef GTA_TRAIN CEntity::Render(); #endif } void CTrain::TrainHitStuff(CPtrList &list) { #ifdef GTA_TRAIN CPtrNode *node; CPhysical *phys; for(node = list.first; node; node = node->next){ phys = (CPhysical*)node->item; if(phys != this && Abs(this->GetPosition().z - phys->GetPosition().z) < 1.5f) phys->bHitByTrain = true; } #endif } void CTrain::AddPassenger(CPed *ped) { #ifdef GTA_TRAIN int i = ped->m_vehDoor; if((i == TRAIN_POS_LEFT_ENTRY || i == TRAIN_POS_MID_ENTRY || i == TRAIN_POS_RIGHT_ENTRY) && pPassengers[i] == nil){ pPassengers[i] = ped; m_nNumPassengers++; }else{ for(i = 0; i < 6; i++) if(pPassengers[i] == nil){ pPassengers[i] = ped; m_nNumPassengers++; return; } } #endif } void CTrain::OpenTrainDoor(float ratio) { #ifdef GTA_TRAIN if(m_rwObject == nil) return; CMatrix doorL(RwFrameGetMatrix(m_aTrainNodes[TRAIN_DOOR_LHS])); CMatrix doorR(RwFrameGetMatrix(m_aTrainNodes[TRAIN_DOOR_RHS])); CVector posL = doorL.GetPosition(); CVector posR = doorR.GetPosition(); bool isClosed = Doors[0].IsClosed(); // useless Doors[0].Open(ratio); Doors[1].Open(ratio); if(isClosed) Doors[0].RetTranslationWhenClosed(); // useless posL.y = Doors[0].m_fPosn; posR.y = Doors[1].m_fPosn; doorL.SetTranslate(posL); doorR.SetTranslate(posR); doorL.UpdateRW(); doorR.UpdateRW(); #endif } void CTrain::InitTrains(void) { #ifdef GTA_TRAIN int i, j; CTrain *train; // El train if(pTrackNodes == nil) ReadAndInterpretTrackFile("data\\paths\\tracks.dat", &pTrackNodes, &NumTrackNodes, 3, StationDist, &TotalLengthOfTrack, &TotalDurationOfTrack, aLineBits, false); // Subway if(pTrackNodes_S == nil) ReadAndInterpretTrackFile("data\\paths\\tracks2.dat", &pTrackNodes_S, &NumTrackNodes_S, 4, StationDist_S, &TotalLengthOfTrack_S, &TotalDurationOfTrack_S, aLineBits_S, true); int trainId; CStreaming::LoadAllRequestedModels(false); if(CModelInfo::GetModelInfo("train", &trainId)) CStreaming::RequestModel(trainId, 0); CStreaming::LoadAllRequestedModels(false); // El-Train wagons float wagonPositions[] = { 0.0f, 20.0f, 40.0f, 0.0f, 20.0f }; int8 firstWagon[] = { 1, 0, 0, 1, 0 }; int8 lastWagon[] = { 0, 0, 1, 0, 1 }; int16 wagonGroup[] = { 0, 0, 0, 1, 1 }; for(i = 0; i < 5; i++){ train = new CTrain(MI_TRAIN, PERMANENT_VEHICLE); train->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); train->SetStatus(STATUS_ABANDONED); train->bIsLocked = true; train->m_fWagonPosition = wagonPositions[i]; train->m_bIsFirstWagon = firstWagon[i]; train->m_bIsLastWagon = lastWagon[i]; train->m_nWagonGroup = wagonGroup[i]; train->m_nWagonId = i; train->m_nCurTrackNode = 0; CWorld::Add(train); } // Subway wagons float wagonPositions_S[] = { 0.0f, 20.0f, 0.0f, 20.0f, 0.0f, 20.0f, 0.0f, 20.0f }; int8 firstWagon_S[] = { 1, 0, 1, 0, 1, 0, 1, 0 }; int8 lastWagon_S[] = { 0, 1, 0, 1, 0, 1, 0, 1 }; int16 wagonGroup_S[] = { 0, 0, 1, 1, 2, 2, 3, 3 }; for(i = 0; i < 8; i++){ train = new CTrain(MI_TRAIN, PERMANENT_VEHICLE); train->GetMatrix().SetTranslate(0.0f, 0.0f, 0.0f); train->SetStatus(STATUS_ABANDONED); train->bIsLocked = true; train->m_fWagonPosition = wagonPositions_S[i]; train->m_bIsFirstWagon = firstWagon_S[i]; train->m_bIsLastWagon = lastWagon_S[i]; train->m_nWagonGroup = wagonGroup_S[i]; train->m_nWagonId = i; train->m_nCurTrackNode = 0; train->m_nTrackId = TRACK_SUBWAY; CWorld::Add(train); } // This code is actually useless, it seems it was used for announcements once for(i = 0; i < 3; i++){ for(j = 0; pTrackNodes[j].t < StationDist[i]; j++); aStationCoors[i] = pTrackNodes[j].p; } for(i = 0; i < 4; i++){ for(j = 0; pTrackNodes_S[j].t < StationDist_S[i]; j++); aStationCoors_S[i] = pTrackNodes_S[j].p; } #endif } void CTrain::Shutdown(void) { #ifdef GTA_TRAIN delete[] pTrackNodes; delete[] pTrackNodes_S; pTrackNodes = nil; pTrackNodes_S = nil; #endif } void CTrain::ReadAndInterpretTrackFile(Const char *filename, CTrainNode **nodes, int16 *numNodes, int32 numStations, float *stationDists, float *totalLength, float *totalDuration, CTrainInterpolationLine *interpLines, bool rightRail) { #ifdef GTA_TRAIN bool readingFile = false; int bp, lp; int i, tmp; if(*nodes == nil){ readingFile = true; 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++; // BUG: game doesn't terminate string and uses numNodes in sscanf directly gString[lp] = '\0'; sscanf(gString, "%d", &tmp); *numNodes = tmp; *nodes = new CTrainNode[*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); } // Coordinates are of one of the rails, but we want the center float toCenter = rightRail ? 0.9f : -0.9f; CVector fwd; for(i = 0; i < *numNodes; i++){ if(i == *numNodes-1) fwd = (*nodes)[0].p - (*nodes)[i].p; else fwd = (*nodes)[i+1].p - (*nodes)[i].p; CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); right.Normalise(); (*nodes)[i].p -= right*toCenter; } } // Calculate length of segments and track float t = 0.0f; for(i = 0; i < *numNodes; i++){ (*nodes)[i].t = t; t += ((*nodes)[(i+1) % (*numNodes)].p - (*nodes)[i].p).Magnitude2D(); } *totalLength = t; // Find correct z values if(readingFile){ CColPoint colpoint; CEntity *entity; for(i = 0; i < *numNodes; i++){ CVector p = (*nodes)[i].p; p.z += 1.0f; if(CWorld::ProcessVerticalLine(p, p.z-0.5f, colpoint, entity, true, false, false, false, true, false, nil)) (*nodes)[i].p.z = colpoint.point.z; (*nodes)[i].p.z += 0.2f; } } // Create animation for stopping at stations // TODO: figure out magic numbers? float position = 0.0f; float time = 0.0f; int j = 0; for(i = 0; i < numStations; i++){ // Start at full speed interpLines[j].type = 1; interpLines[j].time = time; interpLines[j].position = position; interpLines[j].speed = 15.0f; interpLines[j].acceleration = 0.0f; j++; // distance to next keyframe float dist = (stationDists[i]-40.0f) - position; time += dist/15.0f; position += dist; // Now slow down 40 units before stop interpLines[j].type = 2; interpLines[j].time = time; interpLines[j].position = position; interpLines[j].speed = 15.0f; interpLines[j].acceleration = -45.0f/32.0f; j++; time += 80.0f/15.0f; position += 40.0f; // at station // stopping interpLines[j].type = 0; interpLines[j].time = time; interpLines[j].position = position; interpLines[j].speed = 0.0f; interpLines[j].acceleration = 0.0f; j++; time += 25.0f; // accelerate again interpLines[j].type = 2; interpLines[j].time = time; interpLines[j].position = position; interpLines[j].speed = 0.0f; interpLines[j].acceleration = 45.0f/32.0f; j++; time += 80.0f/15.0f; position += 40.0f; // after station } // last keyframe interpLines[j].type = 1; interpLines[j].time = time; interpLines[j].position = position; interpLines[j].speed = 15.0f; interpLines[j].acceleration = 0.0f; j++; *totalDuration = time + (*totalLength - position)/15.0f; // end interpLines[j].time = *totalDuration; #endif } void PlayAnnouncement(uint8 sound, uint8 station) { // this was gone in a PC version but inlined on PS2 cAudioScriptObject *obj = new cAudioScriptObject; obj->AudioId = sound; obj->Posn = CTrain::aStationCoors[station]; obj->AudioEntity = AEHANDLE_NONE; DMAudio.CreateOneShotScriptObject(obj); } void ProcessTrainAnnouncements(void) { #ifdef GTA_TRAIN for (int i = 0; i < ARRAY_SIZE(StationDist); i++) { for (int j = 0; j < ARRAY_SIZE(EngineTrackPosition); j++) { if (!bTrainArrivalAnnounced[i]) { float preDist = StationDist[i] - 100.0f; if (preDist < 0.0f) preDist += TotalLengthOfTrack; if (EngineTrackPosition[j] > preDist && EngineTrackPosition[j] < StationDist[i]) { bTrainArrivalAnnounced[i] = true; PlayAnnouncement(SCRIPT_SOUND_TRAIN_ANNOUNCEMENT_1, i); break; } } else { float postDist = StationDist[i] + 10.0f; #ifdef FIX_BUGS if (postDist > TotalLengthOfTrack) postDist -= TotalLengthOfTrack; #else if (postDist < 0.0f) // does this even make sense here? postDist += TotalLengthOfTrack; #endif if (EngineTrackPosition[j] > StationDist[i] && EngineTrackPosition[j] < postDist) { bTrainArrivalAnnounced[i] = false; PlayAnnouncement(SCRIPT_SOUND_TRAIN_ANNOUNCEMENT_2, i); break; } } } } #endif } void CTrain::UpdateTrains(void) { #ifdef GTA_TRAIN int i, j; uint32 time; float t, deltaT; if(TheCamera.GetPosition().x > 200.0f && TheCamera.GetPosition().x < 1600.0f && TheCamera.GetPosition().y > -1000.0f && TheCamera.GetPosition().y < 500.0f){ // Update El-Train time = CTimer::GetTimeInMilliseconds(); for(i = 0; i < 2; i++){ t = TotalDurationOfTrack * (float)(time & 0x1FFFF)/0x20000; // find current frame for(j = 0; t > aLineBits[j+1].time; j++); deltaT = t - aLineBits[j].time; switch(aLineBits[j].type){ case 0: // standing still EngineTrackPosition[i] = aLineBits[j].position; EngineTrackSpeed[i] = 0.0f; break; case 1: // moving with constant speed EngineTrackPosition[i] = aLineBits[j].position + aLineBits[j].speed*deltaT; EngineTrackSpeed[i] = (TotalDurationOfTrack*1000.0f/0x20000) * aLineBits[j].speed; break; case 2: // accelerating/braking EngineTrackPosition[i] = aLineBits[j].position + (aLineBits[j].speed + aLineBits[j].acceleration*deltaT)*deltaT; EngineTrackSpeed[i] = (TotalDurationOfTrack*1000.0f/0x20000)*aLineBits[j].speed + 2.0f*aLineBits[j].acceleration*deltaT; break; } // time offset for each train time += 0x20000/2; } ProcessTrainAnnouncements(); } // Update Subway time = CTimer::GetTimeInMilliseconds(); for(i = 0; i < 4; i++){ t = TotalDurationOfTrack_S * (float)(time & 0x3FFFF)/0x40000; // find current frame for(j = 0; t > aLineBits_S[j+1].time; j++); deltaT = t - aLineBits_S[j].time; switch(aLineBits_S[j].type){ case 0: // standing still EngineTrackPosition_S[i] = aLineBits_S[j].position; EngineTrackSpeed_S[i] = 0.0f; break; case 1: // moving with constant speed EngineTrackPosition_S[i] = aLineBits_S[j].position + aLineBits_S[j].speed*deltaT; EngineTrackSpeed_S[i] = (TotalDurationOfTrack*1000.0f/0x40000) * aLineBits_S[j].speed; break; case 2: // accelerating/braking EngineTrackPosition_S[i] = aLineBits_S[j].position + (aLineBits_S[j].speed + aLineBits_S[j].acceleration*deltaT)*deltaT; EngineTrackSpeed_S[i] = (TotalDurationOfTrack*1000.0f/0x40000)*aLineBits_S[j].speed + 2.0f*aLineBits_S[j].acceleration*deltaT; break; } // time offset for each train time += 0x40000/4; } #endif } ================================================ FILE: src/vehicles/Train.h ================================================ #pragma once #include "Vehicle.h" #include "Door.h" enum { TRACK_ELTRAIN, TRACK_SUBWAY }; enum { TRAIN_DOOR_CLOSED, TRAIN_DOOR_OPENING, TRAIN_DOOR_OPEN, TRAIN_DOOR_CLOSING }; enum eTrainNodes { TRAIN_DOOR_LHS = 1, TRAIN_DOOR_RHS, NUM_TRAIN_NODES }; struct CTrainNode { CVector p; // position float t; // xy-distance from start on track }; struct CTrainInterpolationLine { uint8 type; float time; // when does this keyframe start // initial values at start of frame float position; float speed; float acceleration; }; class CTrain : public CVehicle { public: // 0x288 float m_fWagonPosition; int16 m_nWagonId; int16 m_isFarAway; // don't update so often? int16 m_nCurTrackNode; int16 m_nWagonGroup; float m_fSpeed; bool m_bProcessDoor; bool m_bTrainStopping; bool m_bIsFirstWagon; bool m_bIsLastWagon; uint8 m_nTrackId; // or m_bUsesSubwayTracks? uint32 m_nDoorTimer; int16 m_nDoorState; CTrainDoor Doors[2]; RwFrame *m_aTrainNodes[NUM_TRAIN_NODES]; // unused static CVector aStationCoors[3]; static CVector aStationCoors_S[4]; CTrain(int32 id, uint8 CreatedBy); // from CEntity void SetModelIndex(uint32 id); void ProcessControl(void); void PreRender(void); void Render(void); void AddPassenger(CPed *ped); void OpenTrainDoor(float ratio); void TrainHitStuff(CPtrList &list); static void InitTrains(void); static void Shutdown(void); static void ReadAndInterpretTrackFile(Const char *filename, CTrainNode **nodes, int16 *numNodes, int32 numStations, float *stationDists, float *totalLength, float *totalDuration, CTrainInterpolationLine *interpLines, bool rightRail); static void UpdateTrains(void); }; ================================================ FILE: src/vehicles/Transmission.cpp ================================================ #include "common.h" #include "Timer.h" #include "HandlingMgr.h" #include "Transmission.h" void cTransmission::InitGearRatios(void) { static tGear *pGearRatio0 = nil; static tGear *pGearRatio1 = nil; int i; float velocityDiff; memset(Gears, 0, sizeof(Gears)); for(i = 1; i <= nNumberOfGears; i++){ pGearRatio0 = &Gears[i-1]; pGearRatio1 = &Gears[i]; pGearRatio1->fMaxVelocity = (float)i / nNumberOfGears * fMaxVelocity; velocityDiff = pGearRatio1->fMaxVelocity - pGearRatio0->fMaxVelocity; if(i >= nNumberOfGears){ pGearRatio1->fShiftUpVelocity = fMaxVelocity; }else{ Gears[i+1].fShiftDownVelocity = velocityDiff*0.42f + pGearRatio0->fMaxVelocity; pGearRatio1->fShiftUpVelocity = velocityDiff*0.6667f + pGearRatio0->fMaxVelocity; } } // Reverse gear Gears[0].fMaxVelocity = fMaxReverseVelocity; Gears[0].fShiftUpVelocity = -0.01f; Gears[0].fShiftDownVelocity = fMaxReverseVelocity; Gears[1].fShiftDownVelocity = -0.01f; } void cTransmission::CalculateGearForSimpleCar(float speed, uint8 &gear) { static tGear *pGearRatio; pGearRatio = &Gears[gear]; fCurVelocity = speed; if(speed > pGearRatio->fShiftUpVelocity) gear++; else if(speed < pGearRatio->fShiftDownVelocity){ if(gear - 1 < 0) gear = 0; else gear--; } } float cTransmission::CalculateDriveAcceleration(const float &gasPedal, uint8 &gear, float &time, const float &velocity, bool cheat) { static float fAcceleration = 0.0f; static float fVelocity; static float fCheat; static tGear *pGearRatio; fVelocity = velocity; if(fVelocity < fMaxReverseVelocity){ fVelocity = fMaxReverseVelocity; return 0.0f; } if(fVelocity > fMaxVelocity){ fVelocity = fMaxVelocity; return 0.0f; } fCurVelocity = fVelocity; assert(gear <= nNumberOfGears); pGearRatio = &Gears[gear]; if(fVelocity > pGearRatio->fShiftUpVelocity){ if(gear != 0 || gasPedal > 0.0f){ gear++; return CalculateDriveAcceleration(gasPedal, gear, time, fVelocity, false); } }else if(fVelocity < pGearRatio->fShiftDownVelocity && gear != 0){ if(gear != 1 || gasPedal < 0.0f){ gear--; return CalculateDriveAcceleration(gasPedal, gear, time, fVelocity, false); } } float speedMul, accelMul; if(gear < 1){ // going reverse accelMul = (Flags & HANDLING_2G_BOOST) ? 2.0f : 1.0f; speedMul = -1.0f; }else if(nNumberOfGears == 1){ accelMul = 1.0f; speedMul = 1.0f; }else{ // BUG or not? this is 1.0 normally but 0.0 in the highest gear float f = 1.0f - (gear-1)/(nNumberOfGears-1); speedMul = 3.0f*sq(f) + 1.0f; // This is pretty ugly, could be written more clearly if(Flags & HANDLING_2G_BOOST){ if(gear == 1) accelMul = (Flags & HANDLING_1G_BOOST) ? 2.0f : 1.6f; else if(gear == 2) accelMul = 1.3f; else accelMul = 1.0f; }else if(Flags & HANDLING_1G_BOOST && gear == 1){ accelMul = 2.0f; }else accelMul = 1.0f; } if(cheat) fCheat = 1.2f; else fCheat = 1.0f; float targetVelocity = Gears[gear].fMaxVelocity*speedMul*fCheat; float accel = (targetVelocity - fVelocity) * (fEngineAcceleration*accelMul) / Abs(targetVelocity); if(Abs(fVelocity) < Abs(Gears[gear].fMaxVelocity*fCheat)) fAcceleration = gasPedal * accel * CTimer::GetTimeStep(); else fAcceleration = 0.0f; return fAcceleration; } ================================================ FILE: src/vehicles/Transmission.h ================================================ #pragma once struct tGear { float fMaxVelocity; float fShiftUpVelocity; float fShiftDownVelocity; }; class cTransmission { public: // Gear 0 is reverse, 1-5 are forward tGear Gears[6]; char nDriveType; char nEngineType; int8 nNumberOfGears; uint8 Flags; float fEngineAcceleration; float fMaxVelocity; float fMaxCruiseVelocity; float fMaxReverseVelocity; float fCurVelocity; void InitGearRatios(void); void CalculateGearForSimpleCar(float speed, uint8 &gear); float CalculateDriveAcceleration(const float &gasPedal, uint8 &gear, float &time, const float &velocity, bool cheat); }; ================================================ FILE: src/vehicles/Vehicle.cpp ================================================ #include "common.h" #include "main.h" #include "General.h" #include "Timer.h" #include "Pad.h" #include "Vehicle.h" #include "Bike.h" #include "Automobile.h" #include "Pools.h" #include "HandlingMgr.h" #include "CarCtrl.h" #include "Population.h" #include "ModelIndices.h" #include "World.h" #include "Lights.h" #include "PointLights.h" #include "Renderer.h" #include "VisibilityPlugins.h" #include "DMAudio.h" #include "Radar.h" #include "Fire.h" #include "Darkel.h" #include "Streaming.h" #include "Camera.h" #include "Stats.h" #include "Garages.h" #include "Wanted.h" #include "SurfaceTable.h" #include "Particle.h" #include "WaterLevel.h" #include "Timecycle.h" #include "Weather.h" #include "Coronas.h" bool CVehicle::bWheelsOnlyCheat; bool CVehicle::bAllDodosCheat; bool CVehicle::bCheat3; bool CVehicle::bCheat4; bool CVehicle::bCheat5; bool CVehicle::bCheat8; bool CVehicle::bCheat9; bool CVehicle::bCheat10; bool CVehicle::bHoverCheat; bool CVehicle::bAllTaxisHaveNitro; bool CVehicle::m_bDisableMouseSteering = true; bool CVehicle::bDisableRemoteDetonation; bool CVehicle::bDisableRemoteDetonationOnContact; #ifndef MASTER bool CVehicle::m_bDisplayHandlingInfo; #endif void *CVehicle::operator new(size_t sz) { return CPools::GetVehiclePool()->New(); } void *CVehicle::operator new(size_t sz, int handle) { return CPools::GetVehiclePool()->New(handle); } void CVehicle::operator delete(void *p, size_t sz) { CPools::GetVehiclePool()->Delete((CVehicle*)p); } void CVehicle::operator delete(void *p, int handle) { CPools::GetVehiclePool()->Delete((CVehicle*)p); } #ifdef FIX_BUGS // I think they meant that #define DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE (MYRAND_MAX * 35 / 100) #define DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE (MYRAND_MAX * 70 / 100) #else #define DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE (35000) #define DAMAGE_FLEE_ON_FOOT_PROBABILITY_VALUE (70000) #endif #define DAMAGE_HEALTH_TO_FLEE_ALWAYS (200.0f) #define DAMAGE_HEALTH_TO_CATCH_FIRE (250.0f) CVehicle::CVehicle(uint8 CreatedBy) { int i; m_nCurrentGear = 1; m_fChangeGearTime = 0.0f; m_fSteerInput = 0.0f; m_type = ENTITY_TYPE_VEHICLE; VehicleCreatedBy = CreatedBy; m_nRouteSeed = 0; bIsLocked = false; bIsLawEnforcer = false; bIsAmbulanceOnDuty = false; bIsFireTruckOnDuty = false; #ifdef FIX_BUGS bIsHandbrakeOn = false; #endif CCarCtrl::UpdateCarCount(this, false); m_fHealth = 1000.0f; bEngineOn = true; bFreebies = true; pDriver = nil; m_nNumPassengers = 0; m_nNumGettingIn = 0; m_nGettingInFlags = 0; m_nGettingOutFlags = 0; m_nNumMaxPassengers = ARRAY_SIZE(pPassengers); for(i = 0; i < m_nNumMaxPassengers; i++) pPassengers[i] = nil; m_nBombTimer = 0; m_pBlowUpEntity = nil; m_nPacManPickupsCarried = 0; bComedyControls = false; bCraneMessageDone = false; bExtendedRange = false; bTakeLessDamage = false; bIsDamaged = false; bFadeOut = false; bIsBeingCarJacked = false; m_nTimeOfDeath = 0; m_pCarFire = nil; bHasBeenOwnedByPlayer = false; bCreateRoadBlockPeds = false; bCanBeDamaged = true; bUsingSpecialColModel = false; bOccupantsHaveBeenGenerated = false; bGunSwitchedOff = false; m_nGunFiringTime = 0; m_nTimeBlocked = 0; bLightsOn = false; bVehicleColProcessed = false; m_numPedsUseItAsCover = 0; bIsCarParkVehicle = false; bHasAlreadyBeenRecorded = false; m_bSirenOrAlarm = false; m_nCarHornTimer = 0; m_nCarHornPattern = 0; m_nCarHornDelay = 0; bPartOfConvoy = false; bHeliMinimumTilt = false; bAudioChangingGear = false; bIsDrowning = false; bTyresDontBurst = false; bCreatedAsPoliceVehicle = false; bRestingOnPhysical = false; bParking = false; bCanPark = CGeneral::GetRandomNumberInRange(0.0f, 1.0f) < 0.0f; // never true. probably doesn't work very well bIsVan = false; bIsBus = false; bIsBig = false; bLowVehicle = false; m_bombType = CARBOMB_NONE; bDriverLastFrame = false; m_pBombRigger = nil; m_nSetPieceExtendedRangeTime = 0; m_nAlarmState = 0; m_nDoorLock = CARLOCK_UNLOCKED; m_nLastWeaponDamage = -1; m_pLastDamageEntity = nil; m_fMapObjectHeightAhead = m_fMapObjectHeightBehind = 0.0f; m_audioEntityId = DMAudio.CreateEntity(AUDIOTYPE_PHYSICAL, this); if(m_audioEntityId >= 0) DMAudio.SetEntityStatus(m_audioEntityId, true); //m_nRadioStation = CGeneral::GetRandomNumber() % NUM_RADIOS; switch(GetModelIndex()){ case MI_HUNTER: case MI_ANGEL: case MI_FREEWAY: m_nRadioStation = V_ROCK; break; case MI_RCBARON: case MI_RCBANDIT: case MI_RCRAIDER: case MI_RCGOBLIN: case MI_TOPFUN: case MI_CADDY: case MI_BAGGAGE: m_nRadioStation = RADIO_OFF; break; default: m_nRadioStation = CGeneral::GetRandomNumber() % NUM_RADIOS; break; } m_pCurGroundEntity = nil; m_bRainAudioCounter = 0; m_bRainSamplesCounter = 0; m_comedyControlState = 0; m_aCollPolys[0].valid = false; m_aCollPolys[1].valid = false; AutoPilot.m_nCarMission = MISSION_NONE; AutoPilot.m_nTempAction = TEMPACT_NONE; AutoPilot.m_nTimeToStartMission = CTimer::GetTimeInMilliseconds(); AutoPilot.m_bStayInCurrentLevel = false; AutoPilot.m_bIgnorePathfinding = false; AutoPilot.m_nSwitchDistance = 20; } CVehicle::~CVehicle() { m_nAlarmState = 0; if (m_audioEntityId >= 0){ DMAudio.DestroyEntity(m_audioEntityId); m_audioEntityId = -5; } CRadar::ClearBlipForEntity(BLIP_CAR, CPools::GetVehiclePool()->GetIndex(this)); if (pDriver) pDriver->FlagToDestroyWhenNextProcessed(); for (int i = 0; i < m_nNumMaxPassengers; i++){ if (pPassengers[i]) pPassengers[i]->FlagToDestroyWhenNextProcessed(); } if (m_pCarFire) m_pCarFire->Extinguish(); CCarCtrl::UpdateCarCount(this, true); if (bIsAmbulanceOnDuty){ CCarCtrl::NumAmbulancesOnDuty--; bIsAmbulanceOnDuty = false; } if (bIsFireTruckOnDuty){ CCarCtrl::NumFiretrucksOnDuty--; bIsFireTruckOnDuty = false; } } void CVehicle::SetModelIndex(uint32 id) { CEntity::SetModelIndex(id); m_aExtras[0] = CVehicleModelInfo::ms_compsUsed[0]; m_aExtras[1] = CVehicleModelInfo::ms_compsUsed[1]; m_nNumMaxPassengers = CVehicleModelInfo::GetMaximumNumberOfPassengersFromNumberOfDoors(id); } bool CVehicle::SetupLighting(void) { ActivateDirectional(); SetAmbientColoursForPedsCarsAndObjects(); if(bRenderScorched){ WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f); }else{ CVector coors = GetPosition(); float lighting = CPointLights::GenerateLightsAffectingObject(&coors); if(lighting != 1.0f){ SetAmbientAndDirectionalColours(lighting); return true; } } return false; } void CVehicle::RemoveLighting(bool reset) { CRenderer::RemoveVehiclePedLights(this, reset); } bool CVehicle::IsClearToDriveAway(void) { CColPoint point; float length = GetColModel()->boundingBox.GetSize().y; CEntity *ent = nil; CVector front = GetForward() * (length*0.5f + 3.0f); return !CWorld::ProcessLineOfSight(GetPosition() + front, GetPosition(), point, ent, true, true, false, false, false, true, true) || ent == this; } float CVehicle::GetHeightAboveRoad(void) { return -1.0f * GetColModel()->boundingBox.min.z; } void CVehicle::FlyingControl(eFlightModel flightModel) { if(pFlyingHandling == nil) return; switch(flightModel){ case FLIGHT_MODEL_DODO: { // This seems pretty magic // Move Left/Right float moveSpeed = m_vecMoveSpeed.Magnitude(); float sideSpeed = DotProduct(m_vecMoveSpeed, GetRight()); float sideImpulse = -1.0f * sideSpeed / moveSpeed; float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); float magic = m_vecMoveSpeed.MagnitudeSqr() * sq(fwdSpeed); float turnImpulse = (sideImpulse*0.003f + m_fSteerAngle*0.001f) * magic*m_fTurnMass*CTimer::GetTimeStep(); ApplyTurnForce(turnImpulse*GetRight(), -4.0f*GetForward()); float impulse = sideImpulse*0.2f * magic*m_fMass*CTimer::GetTimeStep(); ApplyMoveForce(impulse*GetRight()); ApplyTurnForce(impulse*GetRight(), 2.0f*GetUp()); // Move Up/Down moveSpeed = m_vecMoveSpeed.Magnitude(); float upSpeed = DotProduct(m_vecMoveSpeed, GetUp()); float upImpulse = -1.0f * upSpeed / moveSpeed; turnImpulse = (upImpulse*0.002f + -CPad::GetPad(0)->GetSteeringUpDown()/128.0f*0.001f) * magic*m_fTurnMass*CTimer::GetTimeStep(); ApplyTurnForce(turnImpulse*GetUp(), -4.0f*GetForward()); impulse = (upImpulse*3.5f + 0.5f)*0.05f * magic*m_fMass*CTimer::GetTimeStep(); if(GRAVITY*m_fMass*CTimer::GetTimeStep() < impulse && GetPosition().z > 100.0f) impulse = 0.9f*GRAVITY*m_fMass*CTimer::GetTimeStep(); CVector com = Multiply3x3(GetMatrix(), m_vecCentreOfMass); ApplyMoveForce(impulse*GetUp()); ApplyTurnForce(impulse*GetUp(), 2.0f*GetUp() + com); m_vecTurnSpeed.y *= Pow(0.9f, CTimer::GetTimeStep()); moveSpeed = m_vecMoveSpeed.MagnitudeSqr(); if(moveSpeed > SQR(1.5f)) m_vecMoveSpeed *= 1.5f/Sqrt(moveSpeed); float turnSpeed = m_vecTurnSpeed.MagnitudeSqr(); if(turnSpeed > SQR(0.2f)) m_vecTurnSpeed *= 0.2f/Sqrt(turnSpeed); break; } case FLIGHT_MODEL_RCPLANE: case FLIGHT_MODEL_SEAPLANE: case FLIGHT_MODEL_PLANE_UNUSED: case FLIGHT_MODEL_PLANE: { float fSteerLR = CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; float fSteerUD = CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; float fGunUD = Abs(CPad::GetPad(0)->GetCarGunUpDown()); #ifdef FREE_CAM if(!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) #endif if(fGunUD > 1.0f) fSteerUD = -CPad::GetPad(0)->GetCarGunUpDown() / 128.0f; float fSteerAngle = Atan2(fSteerUD, fSteerLR); float fSteerMult = 1.0f; if(fSteerAngle > -PI/4.0f && fSteerAngle <= PI/4.0f) fSteerMult = 1.0f/Cos(fSteerAngle); else if(fSteerAngle > PI/4.0f && fSteerAngle <= PI*3.0f/4.0f) fSteerMult = 1.0f/Cos(fSteerAngle - HALFPI); else if(fSteerAngle > PI*3.0f/4.0f) fSteerMult = 1.0f/Cos(fSteerAngle - PI); else if(fSteerAngle <= -PI*3.0f/4.0f) fSteerMult = 1.0f/Cos(fSteerAngle + PI); else if(fSteerAngle > -PI*3.0f/4.0f && fSteerAngle < -PI/4.0f) fSteerMult = 1.0f/Cos(fSteerAngle + HALFPI); fSteerLR *= fSteerMult; fSteerUD *= -fSteerMult; // thrust float fThrust = pFlyingHandling->fThrust; float fThrustFallOff = pFlyingHandling->fThrustFallOff; float fThrustFallOffBack = pFlyingHandling->fThrustFallOff * 8.0f; #ifdef BETTER_ALLCARSAREDODO_CHEAT if (bAllDodosCheat && !IsRealPlane()) { fThrust = pHandling->Transmission.fEngineAcceleration * (pHandling->Transmission.nDriveType == '4' ? 4.0f : 2.0f); fThrust = 5.0f * Max(fThrust, pFlyingHandling->fThrust); //tweak: (cars engines too weak to thrust car on air) fThrustFallOff = Min(0.7f / pHandling->Transmission.fMaxVelocity, fThrustFallOff); //tweak: (use 0.7 instead of 1.0 to make cars 30% faster) fThrustFallOffBack = -1.0f / pHandling->Transmission.fMaxReverseVelocity; } #endif float fForwSpeed = DotProduct(GetMoveSpeed(), GetForward()); CVector vecTail = GetColModel()->boundingBox.min.y * GetForward(); float fPedalState = (CPad::GetPad(0)->GetAccelerate() - CPad::GetPad(0)->GetBrake()) / 255.0f; float fThrustAccel; if(fForwSpeed > 0.0f || fPedalState > 0.0f) fThrustAccel = (fPedalState - fThrustFallOff * fForwSpeed) * fThrust; else fThrustAccel = Min(fPedalState - fThrustFallOffBack * fForwSpeed, 0.0f) * fThrust; if(flightModel == FLIGHT_MODEL_PLANE_UNUSED) fThrustAccel *= 0.3f; else if(flightModel == FLIGHT_MODEL_PLANE) fThrustAccel *= 0.1f; ApplyMoveForce(fThrustAccel * GetForward() * m_fMass * CTimer::GetTimeStep()); // left/right float fSideSpeed = -DotProduct(GetMoveSpeed(), GetRight()); float fSideSlipAccel = pFlyingHandling->fSideSlip * fSideSpeed * Abs(fSideSpeed); ApplyMoveForce(m_fMass * GetRight() * fSideSlipAccel * CTimer::GetTimeStep()); float fYaw = -DotProduct(GetSpeed(vecTail), GetRight()); float fYawAccel = pFlyingHandling->fYawStab * fYaw * Abs(fYaw) + pFlyingHandling->fYaw * fSteerLR * fForwSpeed; ApplyTurnForce(fYawAccel * GetRight() * m_fTurnMass * CTimer::GetTimeStep(), vecTail); float fRollAccel; if (flightModel == FLIGHT_MODEL_RCPLANE) { float fDirectionMultiplier = CPad::GetPad(0)->GetLookRight(); if (CPad::GetPad(0)->GetLookLeft()) fDirectionMultiplier = -1; fRollAccel = (0.5f * fDirectionMultiplier + fSteerLR) * pFlyingHandling->fRoll; } else fRollAccel = fSteerLR * pFlyingHandling->fRoll; ApplyTurnForce(GetRight() * fRollAccel * fForwSpeed * m_fTurnMass * CTimer::GetTimeStep(), GetUp()); CVector vecFRight = CrossProduct(GetForward(), CVector(0.0f, 0.0f, 1.0f)); CVector vecStabilise = (GetUp().z > 0.0f) ? vecFRight : -vecFRight; float fStabiliseDirection = (GetRight().z > 0.0f) ? -1.0f : 1.0f; float fStabiliseSpeed = pFlyingHandling->fRollStab * fStabiliseDirection * (1.0f - DotProduct(GetRight(), vecStabilise)) * (1.0f - Abs(GetForward().z)); ApplyTurnForce(fStabiliseSpeed * m_fTurnMass * GetRight(), GetUp()); // no CTimer::GetTimeStep(), is it right? // up/down float fTail = -DotProduct(GetSpeed(vecTail), GetUp()); float fPitchAccel = pFlyingHandling->fPitchStab * fTail * Abs(fTail) + pFlyingHandling->fPitch * fSteerUD * fForwSpeed; ApplyTurnForce(fPitchAccel * m_fTurnMass * GetUp() * CTimer::GetTimeStep(), vecTail); float fLift = DotProduct(GetMoveSpeed(), GetUp()) / Max(0.01f, GetMoveSpeed().Magnitude()); //accel*angle float fLiftAccel = (pFlyingHandling->fFormLift - pFlyingHandling->fAttackLift * fLift) * SQR(fForwSpeed); float fLiftImpulse = fLiftAccel * m_fMass * CTimer::GetTimeStep(); if (GRAVITY * CTimer::GetTimeStep() * m_fMass < fLiftImpulse) { if (flightModel == FLIGHT_MODEL_RCPLANE && GetPosition().z > 50.0f) fLiftImpulse = CTimer::GetTimeStep() * 0.9f*GRAVITY * m_fMass; else if (flightModel == FLIGHT_MODEL_SEAPLANE && GetPosition().z > 80.0f) fLiftImpulse = CTimer::GetTimeStep() * 0.9f*GRAVITY * m_fMass; #ifdef BETTER_ALLCARSAREDODO_CHEAT else if(bAllDodosCheat && GetPosition().z > 170.0f) fLiftImpulse = CTimer::GetTimeStep() * 0.9f * GRAVITY * m_fMass; #endif } ApplyMoveForce(fLiftImpulse * GetUp()); CVector vecResistance; vecResistance = pFlyingHandling->vecTurnRes; float rX = Pow(vecResistance.x, CTimer::GetTimeStep()); float rY = Pow(vecResistance.y, CTimer::GetTimeStep()); float rZ = Pow(vecResistance.z, CTimer::GetTimeStep()); CVector vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); vecTurnSpeed.x *= rX; float fResistance = vecTurnSpeed.y * (1.0f / (pFlyingHandling->vecSpeedRes.y * SQR(vecTurnSpeed.y) + 1.0f)) * rY - vecTurnSpeed.y; vecTurnSpeed.z *= rZ; m_vecTurnSpeed = Multiply3x3(GetMatrix(), vecTurnSpeed); ApplyTurnForce(-GetUp() * fResistance * m_fTurnMass, GetRight() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); float fMoveSpeed = m_vecMoveSpeed.MagnitudeSqr(); if(fMoveSpeed > SQR(1.5f)) m_vecMoveSpeed *= 1.5f/Sqrt(fMoveSpeed); float fTurnSpeed = m_vecTurnSpeed.MagnitudeSqr(); if(fTurnSpeed > SQR(0.2f)) m_vecTurnSpeed *= 0.2f/Sqrt(fTurnSpeed); break; } case FLIGHT_MODEL_RCHELI: case FLIGHT_MODEL_HELI: { #ifdef RESTORE_ALLCARSHELI_CHEAT tFlyingHandlingData* flyingHandling = bAllCarCheat && !IsRealHeli() ? mod_HandlingManager.GetFlyingPointer(HANDLING_MAVERICK) : pFlyingHandling; #else tFlyingHandlingData* flyingHandling = pFlyingHandling; #endif float rm = Pow(flyingHandling->fMoveRes, CTimer::GetTimeStep()); m_vecMoveSpeed *= rm; if (GetStatus() != STATUS_PLAYER && GetStatus() != STATUS_PLAYER_REMOTE) return; float fUpSpeed = DotProduct(m_vecMoveSpeed, GetUp()); float fThrust = (CPad::GetPad(0)->GetAccelerate() - CPad::GetPad(0)->GetBrake()) / 255.0f; if(fThrust < 0.0f) fThrust *= 2.0f; if(flightModel == FLIGHT_MODEL_RCHELI){ fThrust = flyingHandling->fThrust * fThrust + 0.45f; ApplyMoveForce(GRAVITY * CVector(0.0f, 0.0f, 0.5f) * m_fMass * CTimer::GetTimeStep()); }else fThrust = flyingHandling->fThrust * fThrust + 0.95f; fThrust -= flyingHandling->fThrustFallOff * fUpSpeed; if(flightModel == FLIGHT_MODEL_RCHELI && GetPosition().z > 40.0f) fThrust *= 10.0f/(GetPosition().z - 30.0f); else if(GetPosition().z > 80.0f) fThrust *= 10.0f/(GetPosition().z - 70.0f); ApplyMoveForce(GRAVITY * GetUp() * fThrust * m_fMass * CTimer::GetTimeStep()); if (GetUp().z > 0.0f){ float upRight = clamp(GetRight().z, -flyingHandling->fFormLift, flyingHandling->fFormLift); float upImpulseRight = -upRight * flyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); ApplyTurnForce(upImpulseRight * GetUp(), GetRight()); float upFwd = clamp(GetForward().z, -flyingHandling->fFormLift, flyingHandling->fFormLift); float upImpulseFwd = -upFwd * flyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); ApplyTurnForce(upImpulseFwd * GetUp(), GetForward()); }else{ float upRight = GetRight().z < 0.0f ? -flyingHandling->fFormLift : flyingHandling->fFormLift; float upImpulseRight = -upRight * flyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); ApplyTurnForce(upImpulseRight * GetUp(), GetRight()); float upFwd = GetForward().z < 0.0f ? -flyingHandling->fFormLift : flyingHandling->fFormLift; float upImpulseFwd = -upFwd * flyingHandling->fAttackLift * m_fTurnMass * CTimer::GetTimeStep(); ApplyTurnForce(upImpulseFwd * GetUp(), GetForward()); } float fRoll, fPitch, fYaw; if (bCheat5) { fPitch = CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; fRoll = CPad::GetPad(0)->GetLookLeft(); if (CPad::GetPad(0)->GetLookRight()) fRoll = -1.0f; fYaw = CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; } else { fPitch = CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; fRoll = -CPad::GetPad(0)->GetSteeringLeftRight() / 128.0f; fYaw = CPad::GetPad(0)->GetLookRight(); if (CPad::GetPad(0)->GetLookLeft()) fYaw = -1.0f; #ifdef FREE_CAM if (!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) #endif if(Abs(CPad::GetPad(0)->GetCarGunLeftRight()) > 1.0f) fYaw = CPad::GetPad(0)->GetCarGunLeftRight() / 128.0f; } #ifdef FREE_CAM if(!CCamera::bFreeCam || (CCamera::bFreeCam && !CPad::IsAffectedByController)) #endif if(Abs(CPad::GetPad(0)->GetCarGunUpDown()) > 1.0f) fPitch = -CPad::GetPad(0)->GetCarGunUpDown() / 128.0f; if (CPad::GetPad(0)->GetHorn()) { fYaw = 0.0f; fPitch = clamp(flyingHandling->fPitchStab * DotProduct(m_vecMoveSpeed, GetForward()), -200.0f, 1.3f); fRoll = clamp(flyingHandling->fRollStab * DotProduct(m_vecMoveSpeed, GetRight()), -200.0f, 1.3f); } ApplyTurnForce(fPitch * GetUp() * flyingHandling->fPitch * m_fTurnMass * CTimer::GetTimeStep(), GetForward()); ApplyTurnForce(fRoll * GetUp() * flyingHandling->fRoll * m_fTurnMass * CTimer::GetTimeStep(), GetRight()); float fSideSpeed = -DotProduct(GetMoveSpeed(), GetRight()); float fSideSlipAccel = flyingHandling->fSideSlip * fSideSpeed * Abs(fSideSpeed); ApplyMoveForce(m_fMass * GetRight() * fSideSlipAccel * CTimer::GetTimeStep()); float fYawAccel = flyingHandling->fYawStab * fSideSpeed * Abs(fSideSpeed) + flyingHandling->fYaw * fYaw; ApplyTurnForce(fYawAccel * GetRight() * m_fTurnMass * CTimer::GetTimeStep(), -GetForward()); ApplyTurnForce(fYaw * GetForward() * flyingHandling->fYaw * m_fTurnMass * CTimer::GetTimeStep(), GetRight()); float rX = Pow(flyingHandling->vecTurnRes.x, CTimer::GetTimeStep()); float rY = Pow(flyingHandling->vecTurnRes.y, CTimer::GetTimeStep()); float rZ = Pow(flyingHandling->vecTurnRes.z, CTimer::GetTimeStep()); CVector vecTurnSpeed = Multiply3x3(m_vecTurnSpeed, GetMatrix()); float fResistanceMultiplier = Pow(1.0f / (flyingHandling->vecSpeedRes.z * SQR(vecTurnSpeed.z) + 1.0f) * rZ, CTimer::GetTimeStep()); float fResistance = vecTurnSpeed.z * fResistanceMultiplier - vecTurnSpeed.z; vecTurnSpeed.x *= rX; vecTurnSpeed.y *= rY; vecTurnSpeed.z *= fResistanceMultiplier; m_vecTurnSpeed = Multiply3x3(GetMatrix(), vecTurnSpeed); ApplyTurnForce(-GetRight() * fResistance * m_fTurnMass, GetForward() + Multiply3x3(GetMatrix(), m_vecCentreOfMass)); break; } } } static CColModel rotorColModel; static CColSphere rotorColSphere; float ROTOR_SEMI_THICKNESS = 0.05f; float ROTOR_TURN_SPEED = 0.2f; float ROTOR_DISGUARD_MULT = 0.3f; float ROTOR_COL_ELASTICITY = 1.0f; float ROTOR_COL_TURNMULT = -0.001f; float ROTOR_DEFAULT_DAMAGE = 100.0f; bool CVehicle::DoBladeCollision(CVector pos, CMatrix &matrix, int16 rotorType, float radius, float damageMult) { CVector max(radius, radius, radius); CVector min(-radius, -radius, -radius); switch(rotorType){ case ROTOR_TOP: case ROTOR_BOTTOM: min.z = -ROTOR_SEMI_THICKNESS; max.z = ROTOR_SEMI_THICKNESS; break; case ROTOR_FRONT: case ROTOR_BACK: min.y = -ROTOR_SEMI_THICKNESS; max.y = ROTOR_SEMI_THICKNESS; break; case ROTOR_RIGHT: case ROTOR_LEFT: min.x = -ROTOR_SEMI_THICKNESS; max.x = ROTOR_SEMI_THICKNESS; break; } min += pos; max += pos; rotorColModel.boundingBox.Set(min, max); rotorColModel.boundingSphere.Set(radius, pos); rotorColSphere.Set(radius, pos, 0, 0); rotorColModel.spheres = &rotorColSphere; rotorColModel.numSpheres = 1; pos = matrix * pos; bool hadCollision = false; int minX = CWorld::GetSectorIndexX(pos.x - radius); if(minX <= 0) minX = 0; int minY = CWorld::GetSectorIndexY(pos.y - radius); if(minY <= 0) minY = 0; int maxX = CWorld::GetSectorIndexX(pos.x + radius); #ifdef FIX_BUGS if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1; #else if(maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X; #endif int maxY = CWorld::GetSectorIndexY(pos.y + radius); #ifdef FIX_BUGS if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1; #else if(maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y; #endif CWorld::AdvanceCurrentScanCode(); for(int curY = minY; curY <= maxY; curY++) { for(int curX = minX; curX <= maxX; curX++) { CSector *sector = CWorld::GetSector(curX, curY); if(BladeColSectorList(sector->m_lists[ENTITYLIST_BUILDINGS], rotorColModel, matrix, rotorType, damageMult)) hadCollision = true; if(BladeColSectorList(sector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], rotorColModel, matrix, rotorType, damageMult)) hadCollision = true; if(BladeColSectorList(sector->m_lists[ENTITYLIST_VEHICLES], rotorColModel, matrix, rotorType, damageMult)) hadCollision = true; if(BladeColSectorList(sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP], rotorColModel, matrix, rotorType, damageMult)) hadCollision = true; if(BladeColSectorList(sector->m_lists[ENTITYLIST_PEDS], rotorColModel, matrix, rotorType, 0.0f)) hadCollision = true; if(BladeColSectorList(sector->m_lists[ENTITYLIST_PEDS_OVERLAP], rotorColModel, matrix, rotorType, 0.0f)) hadCollision = true; if(BladeColSectorList(sector->m_lists[ENTITYLIST_OBJECTS], rotorColModel, matrix, rotorType, damageMult)) hadCollision = true; if(BladeColSectorList(sector->m_lists[ENTITYLIST_OBJECTS_OVERLAP], rotorColModel, matrix, rotorType, damageMult)) hadCollision = true; } } rotorColModel.spheres = nil; rotorColModel.numSpheres = 0; return hadCollision; } bool CVehicle::BladeColSectorList(CPtrList &list, CColModel &rotorColModel, CMatrix &matrix, int16 rotorType, float damageMult) { int i; CVector axis; CVector turnSpeed(0.0f, 0.0f, 0.0f); switch(rotorType){ case ROTOR_TOP: turnSpeed.z = -ROTOR_TURN_SPEED; axis = -matrix.GetUp(); break; case ROTOR_BOTTOM: turnSpeed.z = ROTOR_TURN_SPEED; axis = matrix.GetUp(); break; case ROTOR_FRONT: turnSpeed.y = -ROTOR_TURN_SPEED; axis = -matrix.GetForward(); break; case ROTOR_BACK: turnSpeed.y = ROTOR_TURN_SPEED; axis = matrix.GetForward(); break; case ROTOR_RIGHT: turnSpeed.x = -ROTOR_TURN_SPEED; axis = -matrix.GetRight(); break; case ROTOR_LEFT: turnSpeed.x = ROTOR_TURN_SPEED; axis = matrix.GetRight(); break; } turnSpeed = Multiply3x3(matrix, turnSpeed); CVector center = rotorColModel.boundingSphere.center; center = matrix*center; for(CPtrNode *node = list.first; node; node = node->next) { CEntity *entity = (CEntity *)node->item; if(entity == (CEntity*)this || !entity->bUsesCollision || entity->m_scanCode == CWorld::GetCurrentScanCode()) continue; entity->m_scanCode = CWorld::GetCurrentScanCode(); int numCollisions; CColModel *entityCol; if(entity->IsPed()) entityCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(entity->GetModelIndex()))->AnimatePedColModelSkinned(entity->GetClump()); else entityCol = entity->GetColModel(); if(entityCol) numCollisions = CCollision::ProcessColModels(matrix, rotorColModel, entity->GetMatrix(), *entityCol, CWorld::m_aTempColPts, nil, nil); else numCollisions = 0; if(numCollisions > 0 && entity->IsPed()){ CPed *ped = (CPed*)entity; CVector2D dirToRotor = GetPosition() - entity->GetPosition(); dirToRotor.Normalise(); int localDir = ped->GetLocalDirection(dirToRotor); if(ped->m_attachedTo == nil){ ped->bIsStanding = false; ped->ApplyMoveForce(-5.0f*dirToRotor.x, -5.0f*dirToRotor.y, 5.0f); } ped->InflictDamage(this, WEAPONTYPE_RUNOVERBYCAR, 1000.0f, PEDPIECE_TORSO, localDir); if(CGame::nastyGame && ped->GetIsOnScreen()){ for(i = 0; i < 16; i++) CParticle::AddParticle(PARTICLE_BLOOD_SMALL, ped->GetPosition(), CVector(dirToRotor.x, dirToRotor.y, 1.0f) * 0.01f); CParticle::AddParticle(PARTICLE_TEST, ped->GetPosition(), CVector(0.0f, 0.0f, 0.02f), nil, 0.1f); CParticle::AddParticle(PARTICLE_TEST, ped->GetPosition()+CVector(0.0f, 0.0f, 0.2f), CVector(0.0f, 0.0f, -0.01f), nil, 0.1f); } }else if(numCollisions > 0 && entity->GetModelIndex() != MI_MISSILE){ float impulse = 0.0f; bool hadCollision = false; float savedElasticity = m_fElasticity; m_fElasticity = ROTOR_COL_ELASTICITY; for(i = 0; i < numCollisions; i++){ CVector colpos = CWorld::m_aTempColPts[i].point; CVector localColpos = colpos - center; float axisDir = DotProduct(axis, localColpos); float colDir = DotProduct(CWorld::m_aTempColPts[i].normal, localColpos); if(2.0f*ROTOR_SEMI_THICKNESS < Abs(axisDir) && ROTOR_DISGUARD_MULT*Abs(colDir) < Abs(axisDir)) continue; hadCollision = true; colpos -= axisDir*axis; // get rid of axis component CVector tangentSpeed = CrossProduct(turnSpeed, colpos - center); // Particles for(int j = 0; j < 4; j++){ CParticle::AddParticle(PARTICLE_SPARK_SMALL, colpos, (tangentSpeed+m_vecMoveSpeed)/2.0f); CParticle::AddParticle(PARTICLE_SPARK, colpos, 0.1f*CWorld::m_aTempColPts[i].normal); } // Apply Collision if(IsCar()){ CAutomobile *heli = (CAutomobile*)this; if(heli->m_aWheelSpeed[1] > 0.15f){ ApplyCollision(CWorld::m_aTempColPts[i], impulse); ApplyTurnForce(m_fTurnMass*ROTOR_COL_TURNMULT*tangentSpeed, colpos - center); heli->m_aWheelSpeed[1] = 0.15f; }else if(heli->m_aWheelSpeed[1] < 0.075f && heli->m_aWheelSpeed[1] > 0.0f) heli->m_aWheelSpeed[1] *= -1.0f; } float damageImpulse = damageMult * Max(impulse, ROTOR_DEFAULT_DAMAGE*m_fMass/3000.0f); if(damageImpulse > m_fDamageImpulse) SetDamagedPieceRecord(0, damageImpulse, entity, CWorld::m_aTempColPts[i].normal); } if(hadCollision && !entity->IsPed()) DMAudio.ReportCollision(this, entity, SURFACE_CAR_PANEL, SURFACE_TARMAC, 50.0f, 0.09f); m_fElasticity = savedElasticity; } } return false; } float fBurstSpeedMax = 0.3f; float fBurstTyreMod = 0.13f; void CVehicle::ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, int32 wheelsOnGround, float thrust, float brake, float adhesion, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, uint16 wheelStatus) { // BUG: using statics here is probably a bad idea static bool bAlreadySkidding = false; // this is never reset static bool bBraking; static bool bDriving; #ifdef FIX_SIGNIFICANT_BUGS bAlreadySkidding = false; #endif // how much force we want to apply in these axes float fwd = 0.0f; float right = 0.0f; bBraking = brake != 0.0f; if(bBraking) thrust = 0.0f; bDriving = thrust != 0.0f; float contactSpeedFwd = DotProduct(wheelContactSpeed, wheelFwd); float contactSpeedRight = DotProduct(wheelContactSpeed, wheelRight); if(*wheelState != WHEEL_STATE_NORMAL) bAlreadySkidding = true; *wheelState = WHEEL_STATE_NORMAL; adhesion *= CTimer::GetTimeStep(); if(bAlreadySkidding) adhesion *= pHandling->fTractionLoss; // moving sideways if(contactSpeedRight != 0.0f){ // exert opposing force right = -contactSpeedRight/wheelsOnGround; #ifdef FIX_BUGS // contactSpeedRight is independent of framerate but right has timestep as a factor // so we probably have to fix this right *= CTimer::GetTimeStepFix(); #endif if(wheelStatus == WHEEL_STATUS_BURST){ float fwdspeed = Min(contactSpeedFwd, fBurstSpeedMax); right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstTyreMod, fBurstTyreMod); } } if(bDriving){ fwd = thrust; // limit sideways force (why?) if(right > 0.0f){ if(right > adhesion) right = adhesion; }else{ if(right < -adhesion) right = -adhesion; } }else if(contactSpeedFwd != 0.0f){ fwd = -contactSpeedFwd/wheelsOnGround; #ifdef FIX_BUGS // contactSpeedFwd is independent of framerate but fwd has timestep as a factor // so we probably have to fix this fwd *= CTimer::GetTimeStepFix(); #endif if(!bBraking){ if(m_fGasPedal < 0.01f){ if(IsBike()) brake = 0.6f * mod_HandlingManager.fWheelFriction / (pHandling->fMass + 200.0f); else if(pHandling->fMass < 500.0f) brake = 0.2f * mod_HandlingManager.fWheelFriction / pHandling->fMass; else if(GetModelIndex() == MI_RCBANDIT) brake = 0.2f * mod_HandlingManager.fWheelFriction / pHandling->fMass; else brake = mod_HandlingManager.fWheelFriction / pHandling->fMass; #ifdef FIX_BUGS brake *= CTimer::GetTimeStepFix(); #endif } } if(brake > adhesion){ if(Abs(contactSpeedFwd) > 0.005f) *wheelState = WHEEL_STATE_FIXED; }else { if(fwd > 0.0f){ if(fwd > brake) fwd = brake; }else{ if(fwd < -brake) fwd = -brake; } } } float speedSq = sq(right) + sq(fwd); if(sq(adhesion) < speedSq){ if(*wheelState != WHEEL_STATE_FIXED){ if(bDriving && contactSpeedFwd < 0.2f) *wheelState = WHEEL_STATE_SPINNING; else *wheelState = WHEEL_STATE_SKIDDING; } float l = Sqrt(speedSq); float tractionLoss = bAlreadySkidding ? 1.0f : pHandling->fTractionLoss; right *= adhesion * tractionLoss / l; fwd *= adhesion * tractionLoss / l; } if(fwd != 0.0f || right != 0.0f){ CVector totalSpeed = fwd*wheelFwd + right*wheelRight; CVector turnDirection = totalSpeed; bool separateTurnForce = false; // BUG: not initialized on PC if(pHandling->fSuspensionAntidiveMultiplier > 0.0f){ if(bBraking){ separateTurnForce = true; turnDirection = totalSpeed - pHandling->fSuspensionAntidiveMultiplier*fwd*wheelFwd; }else if(bDriving){ separateTurnForce = true; turnDirection = totalSpeed - 0.5f*pHandling->fSuspensionAntidiveMultiplier*fwd*wheelFwd; } } CVector direction = totalSpeed; float speed = totalSpeed.Magnitude(); float turnSpeed; if(separateTurnForce) turnSpeed = turnDirection.Magnitude(); else turnSpeed = speed; direction.Normalise(); if(separateTurnForce) turnDirection.Normalise(); else turnDirection = direction; float impulse = speed*m_fMass; float turnImpulse = turnSpeed*GetMass(wheelContactPoint, turnDirection); ApplyMoveForce(impulse * direction); ApplyTurnForce(turnImpulse * turnDirection, wheelContactPoint); } } float fBurstBikeSpeedMax = 0.12f; float fBurstBikeTyreMod = 0.05f; float fTweakBikeWheelTurnForce = 2.0f; void CVehicle::ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, int32 wheelsOnGround, float thrust, float brake, float adhesion, float destabTraction, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, eBikeWheelSpecial special, uint16 wheelStatus) { // BUG: using statics here is probably a bad idea static bool bAlreadySkidding = false; // this is never reset static bool bBraking; static bool bDriving; static bool bReversing; #ifdef FIX_SIGNIFICANT_BUGS bAlreadySkidding = false; #endif // how much force we want to apply in these axes float fwd = 0.0f; float right = 0.0f; bBraking = brake != 0.0f; if(bBraking) thrust = 0.0f; bDriving = thrust != 0.0f; bReversing = thrust < 0.0f; float contactSpeedFwd = DotProduct(wheelContactSpeed, wheelFwd); float contactSpeedRight; if(*wheelState != WHEEL_STATE_NORMAL) bAlreadySkidding = true; *wheelState = WHEEL_STATE_NORMAL; adhesion *= CTimer::GetTimeStep(); if(bAlreadySkidding) adhesion *= pHandling->fTractionLoss; if(special == BIKE_WHEELSPEC_2 || special == BIKE_WHEELSPEC_3) contactSpeedRight = 0.0f; else contactSpeedRight = DotProduct(wheelContactSpeed, wheelRight); // moving sideways if(contactSpeedRight != 0.0f){ // exert opposing force right = -contactSpeedRight/wheelsOnGround; #ifdef FIX_BUGS // contactSpeedRight is independent of framerate but right has timestep as a factor // so we probably have to fix this right *= CTimer::GetTimeStepFix(); #endif if(wheelStatus == WHEEL_STATUS_BURST){ float fwdspeed = Min(contactSpeedFwd, fBurstBikeSpeedMax); right += fwdspeed * CGeneral::GetRandomNumberInRange(-fBurstBikeTyreMod, fBurstBikeTyreMod); } } if(bDriving){ fwd = thrust; // limit sideways force (why?) if(right > 0.0f){ if(right > adhesion) right = adhesion; }else{ if(right < -adhesion) right = -adhesion; } }else if(contactSpeedFwd != 0.0f){ fwd = -contactSpeedFwd/wheelsOnGround; #ifdef FIX_BUGS // contactSpeedFwd is independent of framerate but fwd has timestep as a factor // so we probably have to fix this fwd *= CTimer::GetTimeStepFix(); #endif if(!bBraking){ if(m_fGasPedal < 0.01f){ if(IsBike()) brake = 0.6f * mod_HandlingManager.fWheelFriction / (pHandling->fMass + 200.0f); else if(pHandling->fMass < 500.0f) brake = mod_HandlingManager.fWheelFriction / m_fMass; else if(GetModelIndex() == MI_RCBANDIT) brake = 0.2f * mod_HandlingManager.fWheelFriction / m_fMass; else brake = mod_HandlingManager.fWheelFriction / m_fMass; #ifdef FIX_BUGS brake *= CTimer::GetTimeStepFix(); #endif } } if(brake > adhesion){ if(Abs(contactSpeedFwd) > 0.005f) *wheelState = WHEEL_STATE_FIXED; }else { if(fwd > 0.0f){ if(fwd > brake) fwd = brake; }else{ if(fwd < -brake) fwd = -brake; } } } float speedSq = sq(right) + sq(fwd); if(sq(adhesion) < speedSq){ if(*wheelState != WHEEL_STATE_FIXED){ if(bDriving && contactSpeedFwd < 0.2f) *wheelState = WHEEL_STATE_SPINNING; else *wheelState = WHEEL_STATE_SKIDDING; } float l = Sqrt(speedSq); float tractionLoss = bAlreadySkidding ? 1.0f : pHandling->fTractionLoss; right *= adhesion * tractionLoss / l; fwd *= adhesion * tractionLoss / l; if(destabTraction < 1.0f) right *= destabTraction; }else if(destabTraction < 1.0f){ if(!bAlreadySkidding) destabTraction *= pHandling->fTractionLoss; if(sq(adhesion*destabTraction) < speedSq){ float l = Sqrt(speedSq); right *= adhesion * destabTraction / l; } } if(fwd != 0.0f || right != 0.0f){ CVector direction = fwd*wheelFwd + right*wheelRight; float speed = direction.Magnitude(); direction.Normalise(); float impulse = speed*m_fMass; float turnImpulse = speed*GetMass(wheelContactPoint, direction); CVector vTurnImpulse = turnImpulse * direction; ApplyMoveForce(impulse * direction); float turnRight = DotProduct(vTurnImpulse, GetRight()); float contactRight = DotProduct(wheelContactPoint, GetRight()); float contactFwd = DotProduct(wheelContactPoint, GetForward()); if(wheelId != BIKEWHEEL_REAR || !bBraking && !bReversing) ApplyTurnForce((vTurnImpulse - turnRight*GetRight()) * fTweakBikeWheelTurnForce, wheelContactPoint - contactRight*GetRight()); ApplyTurnForce(turnRight*GetRight(), contactFwd*GetForward()); } } float CVehicle::ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVector &speed, float radius) { float angularVelocity; switch(state){ case WHEEL_STATE_SPINNING: angularVelocity = -1.1f; // constant speed forward break; case WHEEL_STATE_FIXED: angularVelocity = 0.0f; // not moving break; default: angularVelocity = -DotProduct(fwd, speed) / radius; // forward speed break; } return angularVelocity * CTimer::GetTimeStep(); } int CVehicle::FindTyreNearestPoint(float x, float y) { CVector pos = CVector(x - GetPosition().x, y - GetPosition().y, 0.0f); float fwd = DotProduct(GetForward(), pos); float right = DotProduct(GetRight(), pos); int piece; if(IsBike()){ piece = fwd > 0.0f ? CAR_PIECE_WHEEL_LF : CAR_PIECE_WHEEL_LR; }else{ piece = fwd > 0.0f ? right > 0.0f ? CAR_PIECE_WHEEL_RF : CAR_PIECE_WHEEL_LF : right > 0.0f ? CAR_PIECE_WHEEL_RR : CAR_PIECE_WHEEL_LR; } return piece - CAR_PIECE_WHEEL_LF; } void CVehicle::InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage, CVector pos) { if (!bCanBeDamaged) return; if(GetStatus() == STATUS_PLAYER && CStats::GetPercentageProgress() >= 100.0f) damage *= 0.5f; if (GetStatus() != STATUS_PLAYER && bOnlyDamagedByPlayer && (damagedBy != FindPlayerPed() && damagedBy != FindPlayerVehicle())) return; if(damage > 10.0f && (damagedBy == FindPlayerPed() || damagedBy == FindPlayerVehicle()) && GetStatus() != STATUS_WRECKED){ CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 2; CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 1.0f; CStats::PropertyDestroyed += CGeneral::GetRandomNumberInRange(5, 25); } bool bFrightensDriver = false; switch (weaponType) { case WEAPONTYPE_UNARMED: case WEAPONTYPE_BRASSKNUCKLE: case WEAPONTYPE_SCREWDRIVER: case WEAPONTYPE_GOLFCLUB: case WEAPONTYPE_NIGHTSTICK: case WEAPONTYPE_KNIFE: case WEAPONTYPE_BASEBALLBAT: case WEAPONTYPE_HAMMER: case WEAPONTYPE_CLEAVER: case WEAPONTYPE_MACHETE: case WEAPONTYPE_KATANA: case WEAPONTYPE_CHAINSAW: if (bMeleeProof) return; break; case WEAPONTYPE_COLT45: case WEAPONTYPE_PYTHON: case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_SPAS12_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: case WEAPONTYPE_TEC9: case WEAPONTYPE_UZI: case WEAPONTYPE_SILENCED_INGRAM: case WEAPONTYPE_MP5: case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: case WEAPONTYPE_M60: case WEAPONTYPE_MINIGUN: case WEAPONTYPE_HELICANNON: case WEAPONTYPE_UZI_DRIVEBY: if (bBulletProof) return; bFrightensDriver = true; break; case WEAPONTYPE_GRENADE: case WEAPONTYPE_MOLOTOV: case WEAPONTYPE_ROCKET: case WEAPONTYPE_EXPLOSION: if (bExplosionProof) return; bFrightensDriver = true; break; case WEAPONTYPE_FLAMETHROWER: if (bFireProof) return; break; case WEAPONTYPE_RAMMEDBYCAR: if (bCollisionProof) return; break; default: break; } if(bFrightensDriver && GetStatus() == STATUS_PLAYER && m_fHealth < 250.0f) return; // Pop tires if(damagedBy && damagedBy->IsPed() && (IsCar() || IsBike())){ int accuracy = 0; switch(weaponType){ case WEAPONTYPE_COLT45: accuracy = 10; break; case WEAPONTYPE_PYTHON: if(!((CPed*)damagedBy)->IsPlayer()) accuracy = 64; break; case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: case WEAPONTYPE_M60: case WEAPONTYPE_HELICANNON: accuracy = 25; break; case WEAPONTYPE_TEC9: case WEAPONTYPE_UZI: case WEAPONTYPE_SILENCED_INGRAM: case WEAPONTYPE_MP5: case WEAPONTYPE_UZI_DRIVEBY: accuracy = 15; break; case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: if(!((CPed*)damagedBy)->IsPlayer()) accuracy = 15; break; } if(((CPed*)damagedBy)->IsPlayer() && (CCamera::m_bUseMouse3rdPerson || TheCamera.Using1stPersonWeaponMode())) accuracy = 0; if(accuracy != 0 && !bTyresDontBurst && (CGeneral::GetRandomNumber()&0x7F) < accuracy){ if(IsBike()) BurstTyre(FindTyreNearestPoint(pos.x, pos.y) + CAR_PIECE_WHEEL_LF, false); else if(GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) BurstTyre(FindTyreNearestPoint(pos.x, pos.y) + CAR_PIECE_WHEEL_LF, true); } } if (m_fHealth > 0.0f) { if (VehicleCreatedBy == RANDOM_VEHICLE && pDriver && (GetStatus() == STATUS_SIMPLE || GetStatus() == STATUS_PHYSICS) && AutoPilot.m_nCarMission == MISSION_CRUISE) { if (m_randomSeed < DAMAGE_FLEE_IN_CAR_PROBABILITY_VALUE) { CCarCtrl::SwitchVehicleToRealPhysics(this); AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_AVOID_CARS; AutoPilot.m_nCruiseSpeed = GAME_SPEED_TO_CARAI_SPEED * pHandling->Transmission.fMaxCruiseVelocity; SetStatus(STATUS_PHYSICS); } } m_nLastWeaponDamage = weaponType; m_pLastDamageEntity = damagedBy; float oldHealth = m_fHealth; if (m_fHealth > damage) { m_fHealth -= damage; if (VehicleCreatedBy == RANDOM_VEHICLE && !IsBoat()){ switch (GetStatus()) { case STATUS_SIMPLE: case STATUS_PHYSICS: if(AutoPilot.m_nDrivingStyle == DRIVINGSTYLE_PLOUGH_THROUGH || CGeneral::GetRandomNumberInRange(0.0f, 1.0f) > 0.5f && AutoPilot.m_nCarMission == MISSION_CRUISE){ // Drive away like a maniac if(pDriver && pDriver->m_objective != OBJECTIVE_LEAVE_CAR){ if(AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_PLOUGH_THROUGH) AutoPilot.m_nCruiseSpeed *= 1.5f; AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; } }else{ // Leave vehicle if (pDriver && pDriver->CharCreatedBy != MISSION_CHAR) { SetStatus(STATUS_ABANDONED); pDriver->bFleeAfterExitingCar = true; pDriver->SetObjective(OBJECTIVE_LEAVE_CAR, this); pDriver->Say(SOUND_PED_FLEE_SPRINT); } int time = 200; for (int i = 0; i < m_nNumMaxPassengers; i++) { if (pPassengers[i] && pPassengers[i]->m_objective != OBJECTIVE_LEAVE_CAR && pPassengers[i]->CharCreatedBy != MISSION_CHAR) { pPassengers[i]->bFleeAfterExitingCar = true; pPassengers[i]->SetObjective(OBJECTIVE_LEAVE_CAR, this); pPassengers[i]->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + time; pPassengers[i]->Say(SOUND_PED_FLEE_SPRINT); time += 200; } } } break; default: break; } } if (oldHealth >= DAMAGE_HEALTH_TO_CATCH_FIRE && m_fHealth < DAMAGE_HEALTH_TO_CATCH_FIRE) { if (IsCar()) { CAutomobile* pThisCar = (CAutomobile*)this; pThisCar->Damage.SetEngineStatus(ENGINE_STATUS_ON_FIRE); pThisCar->m_pSetOnFireEntity = damagedBy; if (damagedBy) damagedBy->RegisterReference((CEntity**)&pThisCar->m_pSetOnFireEntity); } } } else { m_fHealth = 0.0f; if (weaponType == WEAPONTYPE_EXPLOSION) { // between 1000 and 3047. Also not very nice: can't be saved by respray or cheat m_nBombTimer = 1000 + CGeneral::GetRandomNumber() & 0x7FF; m_pBlowUpEntity = damagedBy; if (damagedBy) damagedBy->RegisterReference((CEntity**)&m_pBlowUpEntity); } else BlowUpCar(damagedBy); } } #ifdef FIX_BUGS // removing dumb case when shooting police car in player's own garage gives wanted level if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed() && damagedBy != nil && !bHasBeenOwnedByPlayer) #else if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed()) #endif FindPlayerPed()->SetWantedLevelNoDrop(1); } void CVehicle::DoFixedMachineGuns(void) { if(TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking == LOOKING_FORWARD){ if(CPad::GetPad(0)->GetCarGunFired() && !bGunSwitchedOff){ FireFixedMachineGuns(); }else{ if(CTimer::GetTimeInMilliseconds() > m_nGunFiringTime + 1400) m_nAmmoInClip = 20; } } } void CVehicle::FireFixedMachineGuns(void) { if (CTimer::GetTimeInMilliseconds() <= m_nGunFiringTime + 150) return; CVector source, target; float dx, dy, len; dx = GetForward().x; dy = GetForward().y; len = Sqrt(SQR(dx) + SQR(dy)); if (len < 0.1f) len = 0.1f; dx /= len; dy /= len; m_nGunFiringTime = CTimer::GetTimeInMilliseconds(); source = GetMatrix() * CVector(2.0f, 2.5f, 1.0f); target = source + CVector(dx, dy, 0.0f) * 60.0f; target += CVector( ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.02f); CWeapon::DoTankDoomAiming(this, pDriver, &source, &target); FireOneInstantHitRound(&source, &target, 15); source = GetMatrix() * CVector(-2.0f, 2.5f, 1.0f); target = source + CVector(dx, dy, 0.0f) * 60.0f; target += CVector( ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.015f, ((CGeneral::GetRandomNumber() & 0xFF) - 128) * 0.02f); CWeapon::DoTankDoomAiming(this, pDriver, &source, &target); FireOneInstantHitRound(&source, &target, 15); DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); m_nAmmoInClip--; if (m_nAmmoInClip == 0) { m_nAmmoInClip = 20; m_nGunFiringTime = CTimer::GetTimeInMilliseconds() + 1400; } } void CVehicle::ActivateBomb(void) { 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); } } void CVehicle::ActivateBombWhenEntered(void) { if(pDriver){ if(!bDriverLastFrame && 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); } bDriverLastFrame = true; }else bDriverLastFrame = false; } void CVehicle::ExtinguishCarFire(void) { if(GetStatus() != STATUS_WRECKED) m_fHealth = Max(m_fHealth, 300.0f); if(m_pCarFire) m_pCarFire->Extinguish(); if(IsCar()){ CAutomobile *car = (CAutomobile*)this; if(car->Damage.GetEngineStatus() >= ENGINE_STATUS_ON_FIRE) car->Damage.SetEngineStatus(ENGINE_STATUS_ON_FIRE-10); car->m_fFireBlowUpTimer = 0.0f; } } bool CVehicle::ShufflePassengersToMakeSpace(void) { if (m_nNumPassengers >= m_nNumMaxPassengers) return false; if (pPassengers[1] && !(m_nGettingInFlags & CAR_DOOR_FLAG_LR) && IsRoomForPedToLeaveCar(CAR_DOOR_LR, nil)) { if (!pPassengers[2] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RR)) { pPassengers[2] = pPassengers[1]; pPassengers[1] = nil; pPassengers[2]->m_vehDoor = CAR_DOOR_RR; return true; } if (!pPassengers[0] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RF)) { pPassengers[0] = pPassengers[1]; pPassengers[1] = nil; pPassengers[0]->m_vehDoor = CAR_DOOR_RF; return true; } return false; } if (pPassengers[2] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RR) && IsRoomForPedToLeaveCar(CAR_DOOR_RR, nil)) { if (!pPassengers[1] && !(m_nGettingInFlags & CAR_DOOR_FLAG_LR)) { pPassengers[1] = pPassengers[2]; pPassengers[2] = nil; pPassengers[1]->m_vehDoor = CAR_DOOR_LR; return true; } if (!pPassengers[0] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RF)) { pPassengers[0] = pPassengers[2]; pPassengers[2] = nil; pPassengers[0]->m_vehDoor = CAR_DOOR_RF; return true; } return false; } if (pPassengers[0] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RF) && IsRoomForPedToLeaveCar(CAR_DOOR_RF, nil)) { if (!pPassengers[1] && !(m_nGettingInFlags & CAR_DOOR_FLAG_LR)) { pPassengers[1] = pPassengers[0]; pPassengers[0] = nil; pPassengers[1]->m_vehDoor = CAR_DOOR_LR; return true; } if (!pPassengers[2] && !(m_nGettingInFlags & CAR_DOOR_FLAG_RR)) { pPassengers[2] = pPassengers[0]; pPassengers[0] = nil; pPassengers[2]->m_vehDoor = CAR_DOOR_RR; return true; } return false; } return false; } void CVehicle::MakeNonDraggedPedsLeaveVehicle(CPed *ped1, CPed *ped2, CPlayerPed *&player, CCopPed *&cop) { int i; player = nil; cop = nil; if(ped1->IsPlayer() && ped2->m_nPedType == PEDTYPE_COP && ((CPlayerPed*)ped1)->m_pWanted->GetWantedLevel() > 0 && ped2->m_pedInObjective == ped1){ player = (CPlayerPed*)ped1; cop = (CCopPed*)ped2; return; } bool ped1IsDriver = ped1 == pDriver; // Just what the hell is this weird code? CPed *peds[9]; CPed *peds2[9]; int numPeds = 0; int numPeds2 = 0; for(i = 0; i < m_nNumMaxPassengers; i++){ CPed *p = pPassengers[i]; if(p && p != ped1 && !p->bStayInCarOnJack){ peds[numPeds++] = p; // uhh what? if(i < 1 && !ped1IsDriver) continue; peds2[numPeds2++] = p; } } // So we're copying this array for no reason... CPed *peds3[9]; int numPeds3 = 0; for(i = 0; i < numPeds; i++){ if(peds[i]->IsPlayer() && ped2->m_nPedType == PEDTYPE_COP && ((CPlayerPed*)peds[i])->m_pWanted->GetWantedLevel() > 0 && ped2->m_pedInObjective == peds[i]){ player = (CPlayerPed*)peds[i]; cop = (CCopPed*)ped2; return; } peds3[numPeds3++] = peds[i]; } int time = 1800; for(i = 0; i < numPeds3; i++){ peds3[i]->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + time; peds3[i]->SetObjective(OBJECTIVE_LEAVE_CAR, this); time += CGeneral::GetRandomNumberInRange(300.0f, 600.0f); } if(IsCar() && numPeds2 > 0 && CGeneral::GetRandomTrueFalse()) for(i = 0; i < numPeds2; i++) if(peds2[i]->IsFemale() || CGeneral::GetRandomTrueFalse()){ peds2[i]->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 10000; peds2[i]->bHeldHostageInCar = true; peds2[i]->bFleeAfterExitingCar = true; } } void CVehicle::ProcessDelayedExplosion(void) { if(m_nBombTimer == 0) return; int tick = CTimer::GetTimeStep()/60.0f*1000.0f; int16 prev = m_nBombTimer; if(tick > m_nBombTimer) m_nBombTimer = 0; else m_nBombTimer -= tick; if(IsCar() && ((CAutomobile*)this)->m_bombType == CARBOMB_TIMEDACTIVE && (m_nBombTimer & 0xFE00) != (prev & 0xFE00)) DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_BOMB_TICK, 0.0f); if (m_nBombTimer != 0) return; BlowUpCar(m_pBlowUpEntity); } bool CVehicle::IsLawEnforcementVehicle(void) { switch(GetModelIndex()){ case MI_POLICE: case MI_ENFORCER: case MI_PREDATOR: case MI_RHINO: case MI_BARRACKS: case MI_FBIRANCH: case MI_VICECHEE: return true; default: return false; } } bool CVehicle::UsesSiren(void) { switch(GetModelIndex()){ case MI_FIRETRUCK: case MI_AMBULAN: case MI_FBICAR: case MI_MRWHOOP: case MI_POLICE: case MI_ENFORCER: case MI_PREDATOR: case MI_FBIRANCH: case MI_VICECHEE: return true; default: return false; } } bool CVehicle::IsVehicleNormal(void) { if (!pDriver || m_nNumPassengers != 0 || GetStatus() == STATUS_WRECKED) return false; return GetModelInfo()->m_vehicleClass != -1; } bool CVehicle::CarHasRoof(void) { if((pHandling->Flags & HANDLING_HAS_NO_ROOF) == 0) return true; // component 0 is assumed to be a roof return m_aExtras[0] == 0 || m_aExtras[1] == 0; } bool CVehicle::IsUpsideDown(void) { if(GetUp().z > -0.9f) return false; return true; } bool CVehicle::IsOnItsSide(void) { if(GetRight().z < 0.8f && GetRight().z > -0.8f) return false; return true; } bool CVehicle::CanBeDeleted(void) { int i; if(m_nNumGettingIn || m_nGettingOutFlags) return false; if(pDriver){ // This looks like it was inlined if(pDriver->CharCreatedBy == MISSION_CHAR) return false; if(pDriver->GetPedState() != PED_DRIVING && pDriver->GetPedState() != PED_DEAD) return false; } for(i = 0; i < ARRAY_SIZE(pPassengers); i++){ // Same check as above if(pPassengers[i]){ if(pPassengers[i]->CharCreatedBy == MISSION_CHAR) return false; if(pPassengers[i]->GetPedState() != PED_DRIVING && pPassengers[i]->GetPedState() != PED_DEAD) return false; } // and then again... probably because something was inlined if(pPassengers[i]){ if(pPassengers[i]->GetPedState() != PED_DRIVING && pPassengers[i]->GetPedState() != PED_DEAD) return false; } } switch(VehicleCreatedBy){ case RANDOM_VEHICLE: return true; case MISSION_VEHICLE: return false; case PARKED_VEHICLE: return true; case PERMANENT_VEHICLE: return false; } return true; } bool CVehicle::CanPedOpenLocks(CPed *ped) { if(m_nDoorLock == CARLOCK_LOCKED || m_nDoorLock == CARLOCK_LOCKED_INITIALLY || m_nDoorLock == CARLOCK_LOCKED_PLAYER_INSIDE || m_nDoorLock == CARLOCK_SKIP_SHUT_DOORS) return false; if(ped->IsPlayer() && m_nDoorLock == CARLOCK_LOCKOUT_PLAYER_ONLY) return false; return true; } bool CVehicle::CanDoorsBeDamaged(void) { return m_nDoorLock == CARLOCK_NOT_USED || m_nDoorLock == CARLOCK_UNLOCKED || m_nDoorLock == CARLOCK_SKIP_SHUT_DOORS; } bool CVehicle::CanPedEnterCar(void) { // can't enter when car is on side if(IsBike() || GetUp().z > 0.1f || GetUp().z < -0.1f){ // also when car is moving too fast if(m_vecMoveSpeed.MagnitudeSqr() > sq(0.2f)) return false; if(m_vecTurnSpeed.MagnitudeSqr() > sq(0.2f)) return false; return true; } return false; } bool CVehicle::CanPedExitCar(bool jumpExit) { CVector up = GetUp(); if(up.z > 0.1f || up.z < -0.1f){ if (IsBoat()) return true; // can't exit when car is moving too fast if(m_vecMoveSpeed.MagnitudeSqr() > 0.005f && !jumpExit) return false; // if car is slow enough, check turn speed if(Abs(m_vecTurnSpeed.x) > 0.01f || Abs(m_vecTurnSpeed.y) > 0.01f || Abs(m_vecTurnSpeed.z) > 0.01f) return false; return true; }else{ // What is this? just > replaced by >= ?? // can't exit when car is moving too fast if(m_vecMoveSpeed.MagnitudeSqr() >= 0.005f) return false; // if car is slow enough, check turn speed if(Abs(m_vecTurnSpeed.x) >= 0.01f || Abs(m_vecTurnSpeed.y) >= 0.01f || Abs(m_vecTurnSpeed.z) >= 0.01f) return false; return true; } } bool CVehicle::CanPedJumpOutCar(void) { if(GetUp().z < 0.3f) return false; float speed = m_vecMoveSpeed.MagnitudeSqr(); return speed < 0.1f || speed > 0.5f ? false : true; } bool CVehicle::CanPedJumpOffBike(void) { if(pPassengers[0]) return false; return m_vecMoveSpeed.MagnitudeSqr() < 0.07f ? false : true; } void CVehicle::ChangeLawEnforcerState(uint8 enable) { if (enable) { if (!bIsLawEnforcer) { bIsLawEnforcer = true; CCarCtrl::NumLawEnforcerCars++; } } else { if (bIsLawEnforcer) { bIsLawEnforcer = false; CCarCtrl::NumLawEnforcerCars--; } } } CPed* CVehicle::SetUpDriver(void) { if(pDriver) return pDriver; if(VehicleCreatedBy != RANDOM_VEHICLE) return nil; pDriver = CPopulation::AddPedInCar(this, true); pDriver->m_pMyVehicle = this; pDriver->m_pMyVehicle->RegisterReference((CEntity**)&pDriver->m_pMyVehicle); pDriver->bInVehicle = true; pDriver->SetPedState(PED_DRIVING); if(bIsBus) pDriver->bRenderPedInCar = false; return pDriver; } CPed* CVehicle::SetupPassenger(int n) { int i; if(pPassengers[n]) return pPassengers[n]; if((IsTaxi() || IsLimo()) && n == 0) pPassengers[0] = nil; else{ CPed *passenger = CPopulation::AddPedInCar(this, false); pPassengers[n] = passenger; passenger->m_pMyVehicle = this; passenger->m_pMyVehicle->RegisterReference((CEntity**)&pPassengers[n]->m_pMyVehicle); passenger->bInVehicle = true; passenger->SetPedState(PED_DRIVING); if(passenger->m_nPedType == PEDTYPE_CIVMALE || passenger->m_nPedType == PEDTYPE_CIVFEMALE) for(i = 0; i < n; i++) if(pPassengers[i] && pPassengers[n] && (pPassengers[i]->m_nPedType == PEDTYPE_CIVMALE || pPassengers[i]->m_nPedType == PEDTYPE_CIVFEMALE) && passenger->GetModelIndex() == pPassengers[i]->GetModelIndex()){ pPassengers[n] = nil; CPopulation::RemovePed(passenger); } } if(bIsBus && pPassengers[n]) pPassengers[n]->bRenderPedInCar = false; ++m_nNumPassengers; return pPassengers[n]; } void CVehicle::SetDriver(CPed *driver) { pDriver = driver; pDriver->RegisterReference((CEntity**)&pDriver); if(bFreebies && driver == FindPlayerPed()){ bFreebies = false; switch(GetModelIndex()){ case MI_AMBULAN: FindPlayerPed()->m_fHealth = Max(FindPlayerPed()->m_fHealth, Min(FindPlayerPed()->m_fHealth + 20.0f, CWorld::Players[0].m_nMaxHealth)); break; case MI_TAXI: case MI_CABBIE: case MI_ZEBRA: case MI_KAUFMAN: CWorld::Players[CWorld::PlayerInFocus].m_nMoney += 12; break; case MI_POLICE: CStreaming::RequestModel(MI_SHOTGUN, STREAMFLAGS_DONT_REMOVE); bFreebies = true; break; case MI_ENFORCER: driver->m_fArmour = Max(driver->m_fArmour, CWorld::Players[0].m_nMaxArmour); break; case MI_CADDY: if(!(driver->IsPlayer() && ((CPlayerPed*)driver)->DoesPlayerWantNewWeapon(WEAPONTYPE_GOLFCLUB, true))) CStreaming::RequestModel(MI_GOLFCLUB, STREAMFLAGS_DONT_REMOVE); break; } } if(IsBike()) ApplyMoveForce(-0.02f*driver->m_fMass * GetUp()); else ApplyTurnForce(0.0f, 0.0f, -0.02f*driver->m_fMass, driver->GetPosition().x - GetPosition().x, driver->GetPosition().y - GetPosition().y, 0.0f); } bool CVehicle::AddPassenger(CPed *passenger) { int i; if(IsBike()) ApplyTurnForce(-0.02f*passenger->m_fMass * GetUp(), -0.1f*GetForward()); else ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, passenger->GetPosition().x - GetPosition().x, passenger->GetPosition().y - GetPosition().y, 0.0f); for(i = 0; i < m_nNumMaxPassengers; i++) if(pPassengers[i] == nil){ pPassengers[i] = passenger; m_nNumPassengers++; return true; } return false; } bool CVehicle::AddPassenger(CPed *passenger, uint8 n) { if(bIsBus) return AddPassenger(passenger); if(IsBike()) ApplyTurnForce(-0.02f*passenger->m_fMass * GetUp(), -0.1f*GetForward()); else ApplyTurnForce(0.0f, 0.0f, -0.2f*passenger->m_fMass, passenger->GetPosition().x - GetPosition().x, passenger->GetPosition().y - GetPosition().y, 0.0f); if(n < m_nNumMaxPassengers && pPassengers[n] == nil){ pPassengers[n] = passenger; m_nNumPassengers++; return true; } return false; } void CVehicle::RemoveDriver(void) { #ifdef FIX_BUGS if (GetStatus() != STATUS_WRECKED) #endif SetStatus(STATUS_ABANDONED); if(pDriver == FindPlayerPed()){ if(GetModelIndex() == MI_POLICE && CStreaming::HasModelLoaded(MI_SHOTGUN)){ if(bFreebies){ if(((CPlayerPed*)pDriver)->DoesPlayerWantNewWeapon(WEAPONTYPE_SHOTGUN, true)) pDriver->GiveWeapon(WEAPONTYPE_SHOTGUN, 5, true); else pDriver->GrantAmmo(WEAPONTYPE_SHOTGUN, 5); bFreebies = false; } CStreaming::SetModelIsDeletable(MI_SHOTGUN); }else if(GetModelIndex() == MI_CADDY && CStreaming::HasModelLoaded(MI_GOLFCLUB)){ if(((CPlayerPed*)pDriver)->DoesPlayerWantNewWeapon(WEAPONTYPE_GOLFCLUB, true)) pDriver->GiveWeapon(WEAPONTYPE_GOLFCLUB, 1, true); CStreaming::SetModelIsDeletable(MI_GOLFCLUB); } } pDriver = nil; } void CVehicle::RemovePassenger(CPed *p) { if (IsTrain()){ for (int i = 0; i < ARRAY_SIZE(pPassengers); i++){ if (pPassengers[i] == p) { pPassengers[i] = nil; m_nNumPassengers--; return; } } return; } for (int i = 0; i < m_nNumMaxPassengers; i++){ if (pPassengers[i] == p){ pPassengers[i] = nil; m_nNumPassengers--; return; } } } bool CVehicle::IsDriver(CPed *ped) { if(ped == nil) return false; return ped == pDriver; } bool CVehicle::IsDriver(int32 model) { return pDriver && pDriver->GetModelIndex() == model; } bool CVehicle::IsPassenger(CPed *ped) { int i; if(ped == nil) return false; for(i = 0; i < 8; i++) if(pPassengers[i] == ped) return true; return false; } bool CVehicle::IsPassenger(int32 model) { int i; for(i = 0; i < 8; i++) if(pPassengers[i] && pPassengers[i]->GetModelIndex() == model) return true; return false; } void CVehicle::UpdatePassengerList(void) { int i; bool hasPassenger = false; if(m_nNumPassengers) for(i = 0; i < 8; i++) if(pPassengers[i]){ hasPassenger = true; break; } if(!hasPassenger) m_nNumPassengers = 0; } void CVehicle::ProcessCarAlarm(void) { uint32 step; if(!IsAlarmOn()) return; step = CTimer::GetTimeStepInMilliseconds(); if((uint16)m_nAlarmState < step){ m_nAlarmState = 0; m_nCarHornTimer = 0; }else m_nAlarmState -= step; } bool CVehicle::IsSphereTouchingVehicle(float sx, float sy, float sz, float radius) { float x, y, z; // sphere relative to vehicle CVector sph = CVector(sx, sy, sz) - GetPosition(); CColModel *colmodel = GetColModel(); x = DotProduct(sph, GetRight()); if(colmodel->boundingBox.min.x - radius > x || colmodel->boundingBox.max.x + radius < x) return false; y = DotProduct(sph, GetForward()); if(colmodel->boundingBox.min.y - radius > y || colmodel->boundingBox.max.y + radius < y) return false; z = DotProduct(sph, GetUp()); if(colmodel->boundingBox.min.z - radius > z || colmodel->boundingBox.max.z + radius < z) return false; return true; } RpMaterial* SetCompAlphaCB(RpMaterial *material, void *data) { uint32 alpha = (uint32)(uintptr)data; RwRGBA *col = (RwRGBA*)RpMaterialGetColor(material); // get rid of const col->alpha = alpha; return material; } void CVehicle::SetComponentAtomicAlpha(RpAtomic *atomic, int32 alpha) { RpGeometry *geo = RpAtomicGetGeometry(atomic); RpGeometrySetFlags(geo, RpGeometryGetFlags(geo) | rpGEOMETRYMODULATEMATERIALCOLOR); RpGeometryForAllMaterials(geo, SetCompAlphaCB, (void*)alpha); } void CVehicle::UpdateClumpAlpha(void) { 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); } void CVehicle::HeliDustGenerate(CEntity *heli, float radius, float ground, int rnd) { int i; float angle; CColPoint point; CEntity *entity; uint8 r, g, b; if(heli == nil) return; uint8 surface = SURFACE_TARMAC; int frm = CTimer::GetFrameCounter() & 7; float testLowZ = ground - 10.0f; float dustSize = 0.0f; float baseSize = 1.0f; float offset = 1.0f; // when heli is tilted float particleZ = -101.0f; int n = 0; if(heli->GetModelIndex() == MI_RCGOBLIN || heli->GetModelIndex() == MI_RCRAIDER){ radius = 3.0f; dustSize = 0.04f; baseSize = 0.07f; offset = 0.3f; } CVector heliPos = heli->GetPosition(); if(heli->IsVehicle() && ((CVehicle*)heli)->IsCar()){ heliPos.x -= (heliPos.z - ground)*heli->GetUp().x*offset*0.5f; heliPos.y -= (heliPos.z - ground)*heli->GetUp().y*offset*0.5f; } float steamSize = 0.25f * radius * baseSize; float splashSize = 0.3f * radius * baseSize; i = 0; for(i = 0; i < 32+rnd; i++){ angle = i * TWOPI/32.0f; CVector pos(radius*Cos(angle), radius*Sin(angle), 0.0f); CVector dir = CVector(pos.x, pos.y, 1.0f)*0.01f; pos += heliPos; if(i < 32 && i == 4*frm){ if(CWorld::ProcessVerticalLine(pos, testLowZ, point, entity, true, false, false, false, true, false, nil)){ n = rnd; particleZ = point.point.z; surface = point.surfaceB; }else n = 0; float waterLevel = 0.0f; if(CWaterLevel::GetWaterLevel(pos, &waterLevel, false) && waterLevel > particleZ){ surface = SURFACE_WATER; n = rnd; particleZ = waterLevel; } } if(n){ pos.z = particleZ; if(surface == SURFACE_WATER){ float red = (0.3*CTimeCycle::GetDirectionalRed() + CTimeCycle::GetAmbientRed_Obj())*255.0f/4.0f; float green = (0.3*CTimeCycle::GetDirectionalGreen() + CTimeCycle::GetAmbientGreen_Obj())*255.0f/4.0f; float blue = (0.3*CTimeCycle::GetDirectionalBlue() + CTimeCycle::GetAmbientBlue_Obj())*255.0f/4.0f; r = clamp(red, 0.0f, 255.0f); g = clamp(green, 0.0f, 255.0f); b = clamp(blue, 0.0f, 255.0f); RwRGBA col1 = { r, g, b, (RwUInt8)CGeneral::GetRandomNumberInRange(8, 32) }; RwRGBA col2 = { 255, 255, 255, 32 }; if(n&1) CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, pos, dir, nil, steamSize, col2); else CParticle::AddParticle(PARTICLE_CAR_SPLASH, pos, dir, nil, splashSize, col1, CGeneral::GetRandomNumberInRange(0.0f, 10.0f), CGeneral::GetRandomNumberInRange(0.0f, 90.0f), 1); }else{ switch(surface){ default: case SURFACE_TARMAC: r = 10; g = 10; b = 10; break; case SURFACE_GRASS: r = 10; g = 10; b = 3; break; case SURFACE_GRAVEL: r = 10; g = 8; b = 7; break; case SURFACE_MUD_DRY: r = 10; g = 6; b = 3; break; case SURFACE_SAND: case SURFACE_SAND_BEACH: r = 10; g = 10; b = 7; break; } RwRGBA col = { r, g, b, 32 }; if(heliPos.z - pos.z < 20.0f) CParticle::AddParticle(PARTICLE_HELI_DUST, pos, dir, nil, dustSize, col); } n--; } } } #define GLARE_MIN_DIST (13.0f) #define GLARE_FULL_DIST (30.0f) #define GLARE_MIN_ANGLE (0.99f) #define GLARE_FULL_ANGLE (0.995f) void CVehicle::DoSunGlare(void) { if(bRenderScorched || GetPosition().z < 0.0f || GetVehicleAppearance() != VEHICLE_APPEARANCE_CAR || CWeather::SunGlare <= 0.0f) return; CVector camDir = TheCamera.GetPosition() - GetPosition(); float dist = camDir.Magnitude(); camDir *= 2.0f/dist; CVector glareVec = camDir + CTimeCycle::GetSunDirection(); CVector localGlareVec; localGlareVec.x = DotProduct(glareVec, GetRight()); localGlareVec.y = DotProduct(glareVec, GetForward()); localGlareVec.z = 0.0; localGlareVec.Normalise(); CVector2D fwd2D = GetForward(); fwd2D.Normalise(); CVector2D camDir2D = camDir; camDir2D.Normalise(); float fwdness = Abs(DotProduct2D(fwd2D, camDir2D)); // check angle float strength; if(fwdness > GLARE_FULL_ANGLE) strength = 1.0f; else if(fwdness > GLARE_MIN_ANGLE) strength = (fwdness - GLARE_MIN_ANGLE)/(GLARE_FULL_ANGLE-GLARE_MIN_ANGLE); else return; // check distance if(dist > GLARE_FULL_DIST){ // no max distance }else if(dist > GLARE_MIN_DIST) strength *= (dist - GLARE_MIN_DIST)/(GLARE_FULL_DIST - GLARE_MIN_DIST); else return; float intens = 0.8f * strength * CWeather::SunGlare; int r = intens * (CTimeCycle::GetSunCoreRed() + 2*255)/3.0f; int g = intens * (CTimeCycle::GetSunCoreGreen() + 2*255)/3.0f; int b = intens * (CTimeCycle::GetSunCoreBlue() + 2*255)/3.0f; CColModel *colmodel = GetColModel(); CCollision::CalculateTrianglePlanes(colmodel); int i; for(i = 0; i < colmodel->numTriangles-2; i += 2){ int a1 = colmodel->triangles[i].a; int b1 = colmodel->triangles[i].b; int c1 = colmodel->triangles[i].c; int a2 = colmodel->triangles[i+1].a; int b2 = colmodel->triangles[i+1].b; int c2 = colmodel->triangles[i+1].c; CVector vert1 = colmodel->vertices[a1].Get(); CVector vert4; // Need an upward surface if(vert1.z <= 0.0f) continue; // trying to find a quad here int numTri2Verts = 0; if(a2 != a1 && a2 != b1 && a2 != c1){ // a2 is not in tri1 numTri2Verts++; vert4 = colmodel->vertices[a2].Get(); } if(b2 != a1 && b2 != b1 && b2 != c1){ // b2 is not in tri1 numTri2Verts++; vert4 = colmodel->vertices[b2].Get(); } if(c2 != a1 && c2 != b1 && c2 != c1){ // c2 is not in tri1 numTri2Verts++; vert4 = colmodel->vertices[c2].Get(); } // Need exactly one vertex from tri2 for a quad with tri1 if(numTri2Verts != 1) continue; CVector mid = (vert1 + colmodel->vertices[b1].Get() + colmodel->vertices[c1].Get() + vert4)/4.0f; float dy = mid.y - vert1.y; float dx = mid.x - vert1.x; float dist = 1.4f * Min(Abs(dx), Abs(dy)); if(dist > 0.6f){ CVector pos = GetMatrix() * (dist * localGlareVec + mid) + camDir; CCoronas::RegisterCorona((uintptr)this + 27 + i, r, g, b, 255, pos, 0.9f*CWeather::SunGlare, 90.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } } } void CVehicle::KillPedsInVehicle(void) { int i; if(pDriver){ CDarkel::RegisterKillByPlayer(pDriver, WEAPONTYPE_EXPLOSION); if(pDriver->GetPedState() == PED_DRIVING){ pDriver->SetDead(); if(!pDriver->IsPlayer()) pDriver->FlagToDestroyWhenNextProcessed(); }else pDriver->SetDie(); } 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(); } } } void DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle) { if (pVehicle->pDriver) { CDarkel::RegisterKillByPlayer(pVehicle->pDriver, WEAPONTYPE_UNIDENTIFIED); pVehicle->pDriver->FlagToDestroyWhenNextProcessed(); } for (int i = 0; i < pVehicle->m_nNumMaxPassengers; i++) { if (pVehicle->pPassengers[i]) { CDarkel::RegisterKillByPlayer(pVehicle->pPassengers[i], WEAPONTYPE_UNIDENTIFIED); pVehicle->pPassengers[i]->FlagToDestroyWhenNextProcessed(); } } CWorld::Remove(pVehicle); delete pVehicle; } #ifdef COMPATIBLE_SAVES void CVehicle::Save(uint8*& buf) { SkipSaveBuf(buf, 4); WriteSaveBuf(buf, GetRight().x); WriteSaveBuf(buf, GetRight().y); WriteSaveBuf(buf, GetRight().z); SkipSaveBuf(buf, 4); WriteSaveBuf(buf, GetForward().x); WriteSaveBuf(buf, GetForward().y); WriteSaveBuf(buf, GetForward().z); SkipSaveBuf(buf, 4); WriteSaveBuf(buf, GetUp().x); WriteSaveBuf(buf, GetUp().y); WriteSaveBuf(buf, GetUp().z); SkipSaveBuf(buf, 4); WriteSaveBuf(buf, GetPosition().x); WriteSaveBuf(buf, GetPosition().y); WriteSaveBuf(buf, GetPosition().z); SkipSaveBuf(buf, 16); SaveEntityFlags(buf); SkipSaveBuf(buf, 208); AutoPilot.Save(buf); WriteSaveBuf(buf, m_currentColour1); WriteSaveBuf(buf, m_currentColour2); SkipSaveBuf(buf, 2); WriteSaveBuf(buf, m_nAlarmState); SkipSaveBuf(buf, 42); WriteSaveBuf(buf, m_nNumMaxPassengers); SkipSaveBuf(buf, 3); WriteSaveBuf(buf, field_1D0[0]); WriteSaveBuf(buf, field_1D0[1]); WriteSaveBuf(buf, field_1D0[2]); WriteSaveBuf(buf, field_1D0[3]); SkipSaveBuf(buf, 8); WriteSaveBuf(buf, m_fSteerAngle); WriteSaveBuf(buf, m_fGasPedal); WriteSaveBuf(buf, m_fBrakePedal); WriteSaveBuf(buf, VehicleCreatedBy); uint8 flags = 0; if (bIsLawEnforcer) flags |= BIT(0); if (bIsLocked) flags |= BIT(3); if (bEngineOn) flags |= BIT(4); if (bIsHandbrakeOn) flags |= BIT(5); if (bLightsOn) flags |= BIT(6); if (bFreebies) flags |= BIT(7); WriteSaveBuf(buf, flags); SkipSaveBuf(buf, 10); WriteSaveBuf(buf, m_fHealth); WriteSaveBuf(buf, m_nCurrentGear); SkipSaveBuf(buf, 3); WriteSaveBuf(buf, m_fChangeGearTime); SkipSaveBuf(buf, 12); WriteSaveBuf(buf, m_nTimeOfDeath); SkipSaveBuf(buf, 2); WriteSaveBuf(buf, m_nBombTimer); SkipSaveBuf(buf, 12); WriteSaveBuf(buf, m_nDoorLock); SkipSaveBuf(buf, 111); } void CVehicle::Load(uint8*& buf) { CMatrix tmp; SkipSaveBuf(buf, 4); tmp.GetRight().x = ReadSaveBuf(buf); tmp.GetRight().y = ReadSaveBuf(buf); tmp.GetRight().z = ReadSaveBuf(buf); SkipSaveBuf(buf, 4); tmp.GetForward().x = ReadSaveBuf(buf); tmp.GetForward().y = ReadSaveBuf(buf); tmp.GetForward().z = ReadSaveBuf(buf); SkipSaveBuf(buf, 4); tmp.GetUp().x = ReadSaveBuf(buf); tmp.GetUp().y = ReadSaveBuf(buf); tmp.GetUp().z = ReadSaveBuf(buf); SkipSaveBuf(buf, 4); tmp.GetPosition().x = ReadSaveBuf(buf); tmp.GetPosition().y = ReadSaveBuf(buf); tmp.GetPosition().z = ReadSaveBuf(buf); m_matrix = tmp; SkipSaveBuf(buf, 16); LoadEntityFlags(buf); SkipSaveBuf(buf, 208); AutoPilot.Load(buf); m_currentColour1 = ReadSaveBuf(buf); m_currentColour2 = ReadSaveBuf(buf); SkipSaveBuf(buf, 2); m_nAlarmState = ReadSaveBuf(buf); SkipSaveBuf(buf, 42); m_nNumMaxPassengers = ReadSaveBuf(buf); SkipSaveBuf(buf, 3); field_1D0[0] = ReadSaveBuf(buf); field_1D0[1] = ReadSaveBuf(buf); field_1D0[2] = ReadSaveBuf(buf); field_1D0[3] = ReadSaveBuf(buf); SkipSaveBuf(buf, 8); m_fSteerAngle = ReadSaveBuf(buf); m_fGasPedal = ReadSaveBuf(buf); m_fBrakePedal = ReadSaveBuf(buf); VehicleCreatedBy = ReadSaveBuf(buf); uint8 flags = ReadSaveBuf(buf); bIsLawEnforcer = !!(flags & BIT(0)); bIsLocked = !!(flags & BIT(3)); bEngineOn = !!(flags & BIT(4)); bIsHandbrakeOn = !!(flags & BIT(5)); bLightsOn = !!(flags & BIT(6)); bFreebies = !!(flags & BIT(7)); SkipSaveBuf(buf, 10); m_fHealth = ReadSaveBuf(buf); m_nCurrentGear = ReadSaveBuf(buf); SkipSaveBuf(buf, 3); m_fChangeGearTime = ReadSaveBuf(buf); SkipSaveBuf(buf, 12); m_nTimeOfDeath = ReadSaveBuf(buf); SkipSaveBuf(buf, 2); m_nBombTimer = ReadSaveBuf(buf); SkipSaveBuf(buf, 12); m_nDoorLock = (eCarLock)ReadSaveBuf(buf); SkipSaveBuf(buf, 111); } #endif eVehicleAppearance CVehicle::GetVehicleAppearance(void) { uint32 flags = pHandling->Flags & 0xF0000; if (flags == 0) return VEHICLE_APPEARANCE_CAR; if (flags == HANDLING_IS_BIKE) return VEHICLE_APPEARANCE_BIKE; if (flags == HANDLING_IS_HELI) return VEHICLE_APPEARANCE_HELI; if (flags == HANDLING_IS_PLANE) return VEHICLE_APPEARANCE_PLANE; if (flags == HANDLING_IS_BOAT) return VEHICLE_APPEARANCE_BOAT; return VEHICLE_APPEARANCE_NONE; } bool IsVehiclePointerValid(CVehicle* pVehicle) { if (!pVehicle) return false; int index = CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(pVehicle); #ifdef FIX_BUGS if (index < 0 || index >= NUMVEHICLES) #else if (index < 0 || index > NUMVEHICLES) #endif return false; return pVehicle->m_vehType == VEHICLE_TYPE_PLANE || pVehicle->m_entryInfoList.first; } ================================================ FILE: src/vehicles/Vehicle.h ================================================ #pragma once #include "Physical.h" #include "AutoPilot.h" #include "ModelIndices.h" #include "AnimationId.h" #include "WeaponType.h" #include "Collision.h" #include "HandlingMgr.h" class CPed; class CPlayerPed; class CCopPed; class CFire; enum { RANDOM_VEHICLE = 1, MISSION_VEHICLE = 2, PARKED_VEHICLE = 3, PERMANENT_VEHICLE = 4, }; enum eCarNodes { CAR_WHEEL_RF = 1, CAR_WHEEL_RM, CAR_WHEEL_RB, CAR_WHEEL_LF, CAR_WHEEL_LM, CAR_WHEEL_LB, CAR_BUMP_FRONT, CAR_BUMP_REAR, CAR_WING_RF, CAR_WING_RR, CAR_DOOR_RF, CAR_DOOR_RR, CAR_WING_LF, CAR_WING_LR, CAR_DOOR_LF, CAR_DOOR_LR, CAR_BONNET, CAR_BOOT, CAR_WINDSCREEN, NUM_CAR_NODES, }; enum { CAR_DOOR_FLAG_UNKNOWN = 0x0, CAR_DOOR_FLAG_LF = 0x1, CAR_DOOR_FLAG_LR = 0x2, CAR_DOOR_FLAG_RF = 0x4, CAR_DOOR_FLAG_RR = 0x8 }; enum eCarLock { CARLOCK_NOT_USED, CARLOCK_UNLOCKED, CARLOCK_LOCKED, CARLOCK_LOCKOUT_PLAYER_ONLY, CARLOCK_LOCKED_PLAYER_INSIDE, CARLOCK_LOCKED_INITIALLY, CARLOCK_FORCE_SHUT_DOORS, CARLOCK_SKIP_SHUT_DOORS }; enum eBombType { CARBOMB_NONE, CARBOMB_TIMED, CARBOMB_ONIGNITION, CARBOMB_REMOTE, CARBOMB_TIMEDACTIVE, CARBOMB_ONIGNITIONACTIVE, }; enum eDoors { DOOR_BONNET = 0, DOOR_BOOT, DOOR_FRONT_LEFT, DOOR_FRONT_RIGHT, DOOR_REAR_LEFT, DOOR_REAR_RIGHT }; enum ePanels { VEHPANEL_FRONT_LEFT, VEHPANEL_FRONT_RIGHT, VEHPANEL_REAR_LEFT, VEHPANEL_REAR_RIGHT, VEHPANEL_WINDSCREEN, VEHBUMPER_FRONT, VEHBUMPER_REAR, }; enum eLights { VEHLIGHT_FRONT_LEFT, VEHLIGHT_FRONT_RIGHT, VEHLIGHT_REAR_LEFT, VEHLIGHT_REAR_RIGHT, }; enum { CAR_PIECE_BONNET = 1, CAR_PIECE_BOOT, CAR_PIECE_BUMP_FRONT, CAR_PIECE_BUMP_REAR, CAR_PIECE_DOOR_LF, CAR_PIECE_DOOR_RF, CAR_PIECE_DOOR_LR, CAR_PIECE_DOOR_RR, CAR_PIECE_WING_LF, CAR_PIECE_WING_RF, CAR_PIECE_WING_LR, CAR_PIECE_WING_RR, CAR_PIECE_WHEEL_LF, CAR_PIECE_WHEEL_RF, CAR_PIECE_WHEEL_LR, CAR_PIECE_WHEEL_RR, CAR_PIECE_WINDSCREEN, }; enum tWheelState { WHEEL_STATE_NORMAL, // standing still or rolling normally WHEEL_STATE_SPINNING, // rotating but not moving WHEEL_STATE_SKIDDING, WHEEL_STATE_FIXED, // not rotating }; enum eFlightModel { FLIGHT_MODEL_DODO, FLIGHT_MODEL_RCPLANE, FLIGHT_MODEL_RCHELI, FLIGHT_MODEL_SEAPLANE, FLIGHT_MODEL_PLANE_UNUSED, FLIGHT_MODEL_PLANE, FLIGHT_MODEL_HELI }; enum eVehicleAppearance { VEHICLE_APPEARANCE_NONE, VEHICLE_APPEARANCE_CAR, VEHICLE_APPEARANCE_BIKE, VEHICLE_APPEARANCE_HELI, VEHICLE_APPEARANCE_BOAT, VEHICLE_APPEARANCE_PLANE, }; // TODO: what is this even? enum eBikeWheelSpecial { BIKE_WHEELSPEC_0, // both wheels on ground BIKE_WHEELSPEC_1, // rear wheel on ground BIKE_WHEELSPEC_2, // only front wheel on ground BIKE_WHEELSPEC_3, // can't happen }; enum { ROTOR_TOP = 3, ROTOR_FRONT = 4, ROTOR_RIGHT = 5, ROTOR_LEFT = 7, ROTOR_BACK = 8, ROTOR_BOTTOM = 9, }; class CVehicle : public CPhysical { public: tHandlingData *pHandling; tFlyingHandlingData *pFlyingHandling; CAutoPilot AutoPilot; uint8 m_currentColour1; uint8 m_currentColour2; int8 m_aExtras[2]; int16 m_nAlarmState; int16 m_nRouteSeed; CPed *pDriver; CPed *pPassengers[8]; uint8 m_nNumPassengers; int8 m_nNumGettingIn; int8 m_nGettingInFlags; int8 m_nGettingOutFlags; uint8 m_nNumMaxPassengers; float field_1D0[4]; CEntity *m_pCurGroundEntity; CFire *m_pCarFire; float m_fSteerAngle; float m_fGasPedal; float m_fBrakePedal; uint8 VehicleCreatedBy; // cf. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CVehicle.h from R* uint8 bIsLawEnforcer: 1; // Is this guy chasing the player at the moment uint8 bIsAmbulanceOnDuty: 1; // Ambulance trying to get to an accident uint8 bIsFireTruckOnDuty: 1; // Firetruck trying to get to a fire uint8 bIsLocked: 1; // Is this guy locked by the script (cannot be removed) uint8 bEngineOn: 1; // For sound purposes. Parked cars have their engines switched off (so do destroyed cars) uint8 bIsHandbrakeOn: 1; // How's the handbrake doing ? uint8 bLightsOn: 1; // Are the lights switched on ? uint8 bFreebies: 1; // Any freebies left in this vehicle ? uint8 bIsVan: 1; // Is this vehicle a van (doors at back of vehicle) uint8 bIsBus: 1; // Is this vehicle a bus uint8 bIsBig: 1; // Is this vehicle a bus uint8 bLowVehicle: 1; // Need this for sporty type cars to use low getting-in/out anims uint8 bComedyControls : 1; // Will make the car hard to control (hopefully in a funny way) uint8 bWarnedPeds : 1; // Has scan and warn peds of danger been processed? uint8 bCraneMessageDone : 1; // A crane message has been printed for this car allready uint8 bExtendedRange : 1; // This vehicle needs to be a bit further away to get deleted uint8 bTakeLessDamage : 1; // This vehicle is stronger (takes about 1/4 of damage) uint8 bIsDamaged : 1; // This vehicle has been damaged and is displaying all its components uint8 bHasBeenOwnedByPlayer : 1;// To work out whether stealing it is a crime uint8 bFadeOut : 1; // Fade vehicle out uint8 bIsBeingCarJacked : 1; // Fade vehicle out uint8 bCreateRoadBlockPeds : 1; // If this vehicle gets close enough we will create peds (coppers or gang members) round it uint8 bCanBeDamaged : 1; // Set to FALSE during cut scenes to avoid explosions uint8 bUsingSpecialColModel : 1;// Is player vehicle using special collision model, stored in player strucure uint8 bOccupantsHaveBeenGenerated : 1; // Is true if the occupants have already been generated. (Shouldn't happen again) uint8 bGunSwitchedOff : 1; // Level designers can use this to switch off guns on boats uint8 bVehicleColProcessed : 1;// Has ProcessEntityCollision been processed for this car? uint8 bIsCarParkVehicle : 1; // Car has been created using the special CAR_PARK script command uint8 bHasAlreadyBeenRecorded : 1; // Used for replays uint8 bPartOfConvoy : 1; uint8 bHeliMinimumTilt : 1; // This heli should have almost no tilt really uint8 bAudioChangingGear : 1; // sounds like vehicle is changing gear uint8 bIsDrowning : 1; // is vehicle occupants taking damage in water (i.e. vehicle is dead in water) uint8 bTyresDontBurst : 1; // If this is set the tyres are invincible uint8 bCreatedAsPoliceVehicle : 1;// True if this guy was created as a police vehicle (enforcer, policecar, miamivice car etc) uint8 bRestingOnPhysical : 1; // Dont go static cause car is sitting on a physical object that might get removed uint8 bParking : 1; uint8 bCanPark : 1; #if (!defined GTA_PS2 || defined FIX_BUGS) uint8 m_bombType : 3; #endif uint8 bDriverLastFrame : 1; int8 m_numPedsUseItAsCover; uint8 m_nAmmoInClip; // Used to make the guns on boat do a reload (20 by default) int8 m_nPacManPickupsCarried; uint8 m_nRoadblockType; float m_fHealth; // 1000.0f = full health. 250.0f = fire. 0 -> explode uint8 m_nCurrentGear; float m_fChangeGearTime; #if (!defined GTA_PS2 || defined FIX_BUGS) CEntity* m_pBombRigger; #endif uint32 m_nSetPieceExtendedRangeTime; uint32 m_nGunFiringTime; // last time when gun on vehicle was fired (used on boats) uint32 m_nTimeOfDeath; uint16 m_nTimeBlocked; int16 m_nBombTimer; // goes down with each frame CEntity *m_pBlowUpEntity; float m_fMapObjectHeightAhead; // front Z? float m_fMapObjectHeightBehind; // rear Z? eCarLock m_nDoorLock; int8 m_nLastWeaponDamage; // see eWeaponType, -1 if no damage CEntity *m_pLastDamageEntity; uint8 m_nRadioStation; uint8 m_bRainAudioCounter; uint8 m_bRainSamplesCounter; uint32 m_nCarHornTimer; uint8 m_nCarHornPattern; bool m_bSirenOrAlarm; uint8 m_nCarHornDelay; int8 m_comedyControlState; CStoredCollPoly m_aCollPolys[2]; // poly which is under front/rear part of car float m_fSteerInput; uint8 m_vehType; static void *operator new(size_t); static void *operator new(size_t sz, int slot); static void operator delete(void*, size_t); static void operator delete(void*, int); CVehicle(void) {} // FAKE CVehicle(uint8 CreatedBy); ~CVehicle(void); // from CEntity void SetModelIndex(uint32 id); bool SetupLighting(void); void RemoveLighting(bool); void FlagToDestroyWhenNextProcessed(void) {} virtual void ProcessControlInputs(uint8) {} virtual void GetComponentWorldPosition(int32 component, CVector &pos) {} virtual bool IsComponentPresent(int32 component) { return false; } virtual void SetComponentRotation(int32 component, CVector rotation) {} virtual void OpenDoor(int32, eDoors door, float) {} virtual void ProcessOpenDoor(uint32, uint32, float) {} virtual bool IsDoorReady(eDoors door) { return false; } virtual bool IsDoorFullyOpen(eDoors door) { return false; } virtual bool IsDoorClosed(eDoors door) { return false; } virtual bool IsDoorMissing(eDoors door) { return false; } virtual bool IsDoorReady(uint32 door) { return false; } virtual bool IsDoorMissing(uint32 door) { return false; } virtual bool IsOpenTopCar(void) { return false; } virtual void RemoveRefsToVehicle(CEntity *ent) {} virtual void BlowUpCar(CEntity *ent) {} virtual bool SetUpWheelColModel(CColModel *colModel) { return false; } virtual void BurstTyre(uint8 tyre, bool applyForces) {} virtual bool IsRoomForPedToLeaveCar(uint32 component, CVector *forcedDoorPos) { return false; } virtual bool IsClearToDriveAway(void); virtual float GetHeightAboveRoad(void); virtual void PlayCarHorn(void) {} #ifdef COMPATIBLE_SAVES virtual void Save(uint8*& buf); virtual void Load(uint8*& buf); #endif eVehicleAppearance GetVehicleAppearance(void); bool IsCar(void) { return m_vehType == VEHICLE_TYPE_CAR; } bool IsBoat(void) { return m_vehType == VEHICLE_TYPE_BOAT; } bool IsTrain(void) { return m_vehType == VEHICLE_TYPE_TRAIN; } bool IsHeli(void) { return m_vehType == VEHICLE_TYPE_HELI; } bool IsPlane(void) { return m_vehType == VEHICLE_TYPE_PLANE; } bool IsBike(void) { return m_vehType == VEHICLE_TYPE_BIKE; } void FlyingControl(eFlightModel flightModel); bool DoBladeCollision(CVector pos, CMatrix &matrix, int16 rotorType, float radius, float damageMult); bool BladeColSectorList(CPtrList &list, CColModel &rotorColModel, CMatrix &matrix, int16 rotorType, float damageMult); void ProcessWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, int32 wheelsOnGround, float thrust, float brake, float adhesion, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, uint16 wheelStatus); void ProcessBikeWheel(CVector &wheelFwd, CVector &wheelRight, CVector &wheelContactSpeed, CVector &wheelContactPoint, int32 wheelsOnGround, float thrust, float brake, float adhesion, float destabTraction, int8 wheelId, float *wheelSpeed, tWheelState *wheelState, eBikeWheelSpecial special, uint16 wheelStatus); void ExtinguishCarFire(void); void ProcessDelayedExplosion(void); float ProcessWheelRotation(tWheelState state, const CVector &fwd, const CVector &speed, float radius); int FindTyreNearestPoint(float x, float y); bool IsLawEnforcementVehicle(void); void ChangeLawEnforcerState(uint8 enable); bool UsesSiren(void); bool IsVehicleNormal(void); bool CarHasRoof(void); bool IsUpsideDown(void); bool IsOnItsSide(void); bool CanBeDeleted(void); bool CanPedOpenLocks(CPed *ped); bool CanDoorsBeDamaged(void); bool CanPedEnterCar(void); bool CanPedExitCar(bool jumpExit); bool CanPedJumpOutCar(void); bool CanPedJumpOffBike(void); // do these two actually return something? CPed *SetUpDriver(void); CPed *SetupPassenger(int n); void SetDriver(CPed *driver); bool AddPassenger(CPed *passenger); bool AddPassenger(CPed *passenger, uint8 n); void RemovePassenger(CPed *passenger); void RemoveDriver(void); bool IsDriver(CPed *ped); bool IsDriver(int32 model); bool IsPassenger(CPed *ped); bool IsPassenger(int32 model); void UpdatePassengerList(void); void ProcessCarAlarm(void); bool IsSphereTouchingVehicle(float sx, float sy, float sz, float radius); bool ShufflePassengersToMakeSpace(void); void MakeNonDraggedPedsLeaveVehicle(CPed *ped1, CPed *ped2, CPlayerPed *&player, CCopPed *&cop); void InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage, CVector pos = CVector(0.0f, 0.0f, 0.0f)); void DoFixedMachineGuns(void); void FireFixedMachineGuns(void); void ActivateBomb(void); void ActivateBombWhenEntered(void); void KillPedsInVehicle(void); void SetComponentAtomicAlpha(RpAtomic *atomic, int32 alpha); void UpdateClumpAlpha(void); static void HeliDustGenerate(CEntity *heli, float radius, float ground, int rnd); void DoSunGlare(void); bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1 && GetStatus() != STATUS_WRECKED; } CVehicleModelInfo* GetModelInfo() { return (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); } bool IsTaxi(void) { return GetModelIndex() == MI_TAXI || GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_ZEBRA || GetModelIndex() == MI_KAUFMAN; } bool IsLimo(void) { return GetModelIndex() == MI_STRETCH || GetModelIndex() == MI_LOVEFIST; } bool IsRealHeli(void) { return !!(pHandling->Flags & HANDLING_IS_HELI); } bool IsRealPlane(void) { return !!(pHandling->Flags & HANDLING_IS_PLANE); } static bool bWheelsOnlyCheat; static bool bAllDodosCheat; static bool bCheat3; static bool bCheat4; static bool bCheat5; static bool bCheat8; static bool bCheat9; static bool bCheat10; static bool bHoverCheat; static bool bAllTaxisHaveNitro; static bool m_bDisableMouseSteering; static bool bDisableRemoteDetonation; static bool bDisableRemoteDetonationOnContact; #ifndef MASTER static bool m_bDisplayHandlingInfo; #endif }; void DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle); bool IsVehiclePointerValid(CVehicle* pVehicle); // Names of functions below are made up by us. // Used in III and VC. inline int8 GetCarDoorFlag(int32 carnode) { switch (carnode) { case CAR_DOOR_LF: return CAR_DOOR_FLAG_LF; case CAR_DOOR_LR: return CAR_DOOR_FLAG_LR; case CAR_DOOR_RF: return CAR_DOOR_FLAG_RF; case CAR_DOOR_RR: return CAR_DOOR_FLAG_RR; default: return CAR_DOOR_FLAG_UNKNOWN; } } // VC. Accounts the case numMaxPassengers == 0, only for m_nGettingInFlags. inline int8 GetEnterCarDoorFlag(int32 carnode, uint8 numMaxPassengers) { switch (carnode) { case CAR_DOOR_RF: return CAR_DOOR_FLAG_RF; case CAR_DOOR_RR: return CAR_DOOR_FLAG_RR; case CAR_DOOR_LF: if (numMaxPassengers != 0) return CAR_DOOR_FLAG_LF; else return CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; case CAR_DOOR_LR: if (numMaxPassengers != 0) return CAR_DOOR_FLAG_LR; else return CAR_DOOR_FLAG_LF | CAR_DOOR_FLAG_LR; default: return CAR_DOOR_FLAG_UNKNOWN; } } ================================================ FILE: src/weapons/BulletInfo.cpp ================================================ #include "common.h" #include "BulletInfo.h" #include "AnimBlendAssociation.h" #include "DMAudio.h" #include "AudioScriptObject.h" #ifdef FIX_BUGS #include "Collision.h" #endif #include "RpAnimBlend.h" #include "Entity.h" #include "EventList.h" #include "Fire.h" #include "Glass.h" #include "Particle.h" #include "Ped.h" #include "Object.h" #include "Stats.h" #include "Timer.h" #include "Vehicle.h" #include "Weapon.h" #include "WeaponInfo.h" #include "World.h" #include "SurfaceTable.h" #include "Heli.h" #ifdef SQUEEZE_PERFORMANCE uint32 bulletInfoInUse; #endif #define BULLET_LIFETIME (1000) #define NUM_PED_BLOOD_PARTICLES (8) #define BLOOD_PARTICLE_OFFSET (CVector(0.0f, 0.0f, 0.0f)) #define NUM_VEHICLE_SPARKS (16) #define NUM_TYRE_POP_SMOKES (4) #define NUM_OTHER_SPARKS (8) #define BULLET_HIT_FORCE (7.5f) #define BULLET_BOUNDARY_MIN_X -2400.0f #define BULLET_BOUNDARY_MAX_X 1600.0f #define BULLET_BOUNDARY_MIN_Y -2000.0f #define BULLET_BOUNDARY_MAX_Y 2000.0f CBulletInfo gaBulletInfo[CBulletInfo::NUM_BULLETS]; bool bPlayerSniperBullet; CVector PlayerSniperBulletStart; CVector PlayerSniperBulletEnd; void CBulletInfo::Initialise(void) { debug("Initialising CBulletInfo...\n"); for (int i = 0; i < NUM_BULLETS; i++) { gaBulletInfo[i].m_bInUse = false; gaBulletInfo[i].m_eWeaponType = WEAPONTYPE_COLT45; gaBulletInfo[i].m_fTimer = 0.0f; gaBulletInfo[i].m_pSource = nil; } debug("CBulletInfo ready\n"); #ifdef SQUEEZE_PERFORMANCE bulletInfoInUse = 0; #endif } void CBulletInfo::Shutdown(void) { debug("Shutting down CBulletInfo...\n"); debug("CBulletInfo shut down\n"); } bool CBulletInfo::AddBullet(CEntity* pSource, eWeaponType type, CVector vecPosition, CVector vecSpeed) { int i; for (i = 0; i < NUM_BULLETS; i++) { if (!gaBulletInfo[i].m_bInUse) break; } if (i == NUM_BULLETS) return false; gaBulletInfo[i].m_pSource = pSource; gaBulletInfo[i].m_eWeaponType = type; gaBulletInfo[i].m_nDamage = CWeaponInfo::GetWeaponInfo(type)->m_nDamage; gaBulletInfo[i].m_vecPosition = vecPosition; gaBulletInfo[i].m_vecSpeed = vecSpeed; gaBulletInfo[i].m_fTimer = CTimer::GetTimeInMilliseconds() + BULLET_LIFETIME; gaBulletInfo[i].m_bInUse = true; #ifdef SQUEEZE_PERFORMANCE bulletInfoInUse++; #endif return true; } void CBulletInfo::Update(void) { #ifdef SQUEEZE_PERFORMANCE if (bulletInfoInUse == 0) return; #endif bPlayerSniperBullet = false; for (int i = 0; i < NUM_BULLETS; i++) { CBulletInfo* pBullet = &gaBulletInfo[i]; if (pBullet->m_pSource && pBullet->m_pSource->IsPed() && !((CPed*)pBullet->m_pSource)->IsPointerValid()) pBullet->m_pSource = nil; if (!pBullet->m_bInUse) continue; if (CTimer::GetTimeInMilliseconds() > pBullet->m_fTimer) { pBullet->m_bInUse = false; #ifdef SQUEEZE_PERFORMANCE bulletInfoInUse--; #endif } CVector vecOldPos = pBullet->m_vecPosition; CVector vecNewPos = pBullet->m_vecPosition + pBullet->m_vecSpeed * CTimer::GetTimeStep() * 0.5f; if ( vecNewPos.x <= BULLET_BOUNDARY_MIN_X || vecNewPos.x >= BULLET_BOUNDARY_MAX_X || vecNewPos.y <= BULLET_BOUNDARY_MIN_Y || vecNewPos.y >= BULLET_BOUNDARY_MAX_Y ) { pBullet->m_bInUse = false; continue; } CWorld::bIncludeDeadPeds = true; CWorld::bIncludeBikers = true; CWorld::bIncludeCarTyres = true; CWorld::pIgnoreEntity = pBullet->m_pSource; CColPoint point; CEntity* pHitEntity; if (CWorld::ProcessLineOfSight(vecOldPos, vecNewPos, point, pHitEntity, true, true, true, true, true, false, false, true)) { CWeapon::CheckForShootingVehicleOccupant(&pHitEntity, &point, pBullet->m_eWeaponType, vecOldPos, vecNewPos); if (pHitEntity->IsPed()) { CPed* pPed = (CPed*)pHitEntity; if (!pPed->DyingOrDead() && pPed != pBullet->m_pSource) { if (pPed->IsPedInControl() && !pPed->bIsDucking) { pPed->ClearAttackByRemovingAnim(); CAnimBlendAssociation* pAnim = CAnimManager::AddAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HITBYGUN_FRONT); pAnim->SetBlend(0.0f, 8.0f); } pPed->InflictDamage(pBullet->m_pSource, pBullet->m_eWeaponType, pBullet->m_nDamage, (ePedPieceTypes)point.pieceB, pPed->GetLocalDirection(pPed->GetPosition() - point.point)); CEventList::RegisterEvent(pPed->m_nPedType == PEDTYPE_COP ? EVENT_SHOOT_COP : EVENT_SHOOT_PED, EVENT_ENTITY_PED, pPed, (CPed*)pBullet->m_pSource, 1000); pBullet->m_bInUse = false; #ifdef SQUEEZE_PERFORMANCE bulletInfoInUse--; #endif vecNewPos = point.point; } if (CGame::nastyGame) { CVector vecParticleDirection = (point.point - pPed->GetPosition()) * 0.01f; vecParticleDirection.z = 0.01f; if (pPed->GetIsOnScreen()) { for (int j = 0; j < NUM_PED_BLOOD_PARTICLES; j++) CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point + BLOOD_PARTICLE_OFFSET, vecParticleDirection); } if (pPed->GetPedState() == PED_DEAD) { CAnimBlendAssociation* pAnim; if (RpAnimBlendClumpGetFirstAssociation(pPed->GetClump(), ASSOC_FRONTAL)) pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); else pAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); if (pAnim) { pAnim->SetCurrentTime(0.0f); pAnim->flags |= ASSOC_RUNNING; pAnim->flags &= ~ASSOC_FADEOUTWHENDONE; } } pBullet->m_bInUse = false; #ifdef SQUEEZE_PERFORMANCE bulletInfoInUse--; #endif vecNewPos = point.point; } } else if (pHitEntity->IsVehicle()) { CEntity *source = pBullet->m_pSource; if ( !source || !source->IsPed() || ((CPed*)source)->m_attachedTo != pHitEntity) { if ( point.pieceB >= CAR_PIECE_WHEEL_LF && point.pieceB <= CAR_PIECE_WHEEL_RR ) { ((CVehicle*)pHitEntity)->BurstTyre(point.pieceB, true); for (int j=0; jInflictDamage(source, pBullet->m_eWeaponType, pBullet->m_nDamage); if ( pBullet->m_eWeaponType == WEAPONTYPE_FLAMETHROWER ) { gFireManager.StartFire(pHitEntity, pBullet->m_pSource, 0.8f, 1); } else { for (int j=0; jm_bInUse = false; #ifdef SQUEEZE_PERFORMANCE bulletInfoInUse--; #endif vecNewPos = point.point; #endif } else { for (int j = 0; j < NUM_OTHER_SPARKS; j++) CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal / 20); CEntity *source = pBullet->m_pSource; if ( !source || !source->IsPed() || ((CPed*)source)->m_attachedTo != pHitEntity) { if (pHitEntity->IsObject()) { CObject *pHitObject = (CObject*)pHitEntity; if ( !pHitObject->bInfiniteMass && pHitObject->m_fCollisionDamageMultiplier < 99.9f) { bool notStatic = !pHitObject->GetIsStatic(); if (notStatic && pHitObject->m_fUprootLimit <= 0.0f) { pHitObject->bIsStatic = false; pHitObject->AddToMovingList(); } notStatic = !pHitObject->GetIsStatic(); if (!notStatic) { CVector moveForce = point.normal * -BULLET_HIT_FORCE; pHitObject->ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z); } } else if (pHitObject->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY) { pHitObject->ObjectDamage(50.f); } } } #ifdef FIX_BUGS pBullet->m_bInUse = false; #ifdef SQUEEZE_PERFORMANCE bulletInfoInUse--; #endif vecNewPos = point.point; #endif } if (pBullet->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || pBullet->m_eWeaponType == WEAPONTYPE_LASERSCOPE) { cAudioScriptObject* pAudio; switch (pHitEntity->GetType()) { case ENTITY_TYPE_BUILDING: if (!DMAudio.IsAudioInitialised()) break; pAudio = new cAudioScriptObject(); if (pAudio) pAudio->Reset(); pAudio->Posn = pHitEntity->GetPosition(); pAudio->AudioId = SCRIPT_SOUND_BULLET_HIT_GROUND_1; pAudio->AudioEntity = AEHANDLE_NONE; DMAudio.CreateOneShotScriptObject(pAudio); break; case ENTITY_TYPE_OBJECT: if (!DMAudio.IsAudioInitialised()) break; pAudio = new cAudioScriptObject(); if (pAudio) pAudio->Reset(); pAudio->Posn = pHitEntity->GetPosition(); pAudio->AudioId = SCRIPT_SOUND_BULLET_HIT_GROUND_2; pAudio->AudioEntity = AEHANDLE_NONE; DMAudio.CreateOneShotScriptObject(pAudio); break; case ENTITY_TYPE_DUMMY: if (!DMAudio.IsAudioInitialised()) break; pAudio = new cAudioScriptObject(); if (pAudio) pAudio->Reset(); pAudio->Posn = pHitEntity->GetPosition(); pAudio->AudioId = SCRIPT_SOUND_BULLET_HIT_GROUND_3; pAudio->AudioEntity = AEHANDLE_NONE; DMAudio.CreateOneShotScriptObject(pAudio); break; case ENTITY_TYPE_PED: ++CStats::BulletsThatHit; DMAudio.PlayOneShot(((CPed*)pHitEntity)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); ((CPed*)pHitEntity)->Say(SOUND_PED_BULLET_HIT); break; case ENTITY_TYPE_VEHICLE: ++CStats::BulletsThatHit; DMAudio.PlayOneShot(((CVehicle*)pHitEntity)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); break; default: break; } } CGlass::WasGlassHitByBullet(pHitEntity, point.point); CWeapon::BlowUpExplosiveThings(pHitEntity); } CWorld::pIgnoreEntity = nil; CWorld::bIncludeDeadPeds = false; CWorld::bIncludeCarTyres = false; CWorld::bIncludeBikers = false; if (pBullet->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || pBullet->m_eWeaponType == WEAPONTYPE_LASERSCOPE) { bPlayerSniperBullet = true; PlayerSniperBulletStart = pBullet->m_vecPosition; PlayerSniperBulletEnd = vecNewPos; } pBullet->m_vecPosition = vecNewPos; CHeli::TestSniperCollision(&PlayerSniperBulletStart, &PlayerSniperBulletEnd); } } bool CBulletInfo::TestForSniperBullet(float x1, float x2, float y1, float y2, float z1, float z2) { if (!bPlayerSniperBullet) return false; #ifdef FIX_BUGS // original code is not going work anyway... CColLine line(PlayerSniperBulletStart, PlayerSniperBulletEnd); CColBox box; box.Set(CVector(x1, y1, z1), CVector(x2, y2, z2), SURFACE_DEFAULT, 0); return CCollision::TestLineBox(line, box); #else float minP = 0.0f; float maxP = 1.0f; float minX = Min(PlayerSniperBulletStart.x, PlayerSniperBulletEnd.x); float maxX = Max(PlayerSniperBulletStart.x, PlayerSniperBulletEnd.x); if (minX < x2 || maxX > x1) { if (minX < x1) minP = Min(minP, (x1 - minX) / (maxX - minX)); if (maxX > x2) maxP = Max(maxP, (maxX - x2) / (maxX - minX)); } else return false; float minY = Min(PlayerSniperBulletStart.y, PlayerSniperBulletEnd.y); float maxY = Max(PlayerSniperBulletStart.y, PlayerSniperBulletEnd.y); if (minY < y2 || maxY > y1) { if (minY < y1) minP = Min(minP, (y1 - minY) / (maxY - minY)); if (maxY > y2) maxP = Max(maxP, (maxY - y2) / (maxY - minY)); } #ifdef FIX_BUGS else return false; #endif float minZ = Min(PlayerSniperBulletStart.z, PlayerSniperBulletEnd.z); float maxZ = Max(PlayerSniperBulletStart.z, PlayerSniperBulletEnd.z); if (minZ < z2 || maxZ > z1) { if (minZ < z1) minP = Min(minP, (z1 - minZ) / (maxZ - minZ)); if (maxZ > z2) maxP = Max(maxP, (maxZ - z2) / (maxZ - minZ)); } else return false; return minP <= maxP; #endif } ================================================ FILE: src/weapons/BulletInfo.h ================================================ #pragma once #include "WeaponType.h" class CEntity; class CBulletInfo { eWeaponType m_eWeaponType; CEntity* m_pSource; float m_fTimer; // big mistake bool m_bInUse; CVector m_vecPosition; CVector m_vecSpeed; int16 m_nDamage; public: enum { NUM_BULLETS = 100 }; static void Initialise(void); static void Shutdown(void); static bool AddBullet(CEntity* pSource, eWeaponType type, CVector vecPosition, CVector vecSpeed); static void Update(void); static bool TestForSniperBullet(float x1, float x2, float y1, float y2, float z1, float z2); }; ================================================ FILE: src/weapons/Explosion.cpp ================================================ #include "common.h" #include "Automobile.h" #include "Bike.h" #include "Camera.h" #include "Coronas.h" #include "DMAudio.h" #include "Entity.h" #include "EventList.h" #include "Explosion.h" #include "General.h" #include "Fire.h" #include "Pad.h" #include "Particle.h" #include "PointLights.h" #include "Shadows.h" #include "Timer.h" #include "Vehicle.h" #include "WaterLevel.h" #include "World.h" CExplosion gaExplosion[NUM_EXPLOSIONS]; // these two were not initialised in original code, I'm really not sure what were they meant to be RwRGBA colMedExpl = { 0, 0, 0, 0 }; RwRGBA colUpdate = { 0, 0, 0, 0 }; const RwRGBA colAddExplosion = { 160, 160, 160, 255 }; const RwRGBA colGrenade = { 96, 96, 96, 255 }; int AudioHandle = AEHANDLE_NONE; void CExplosion::Initialise() { debug("Initialising CExplosion...\n"); ClearAllExplosions(); AudioHandle = DMAudio.CreateEntity(AUDIOTYPE_EXPLOSION, (void*)1); if (AudioHandle >= 0) DMAudio.SetEntityStatus(AudioHandle, true); debug("CExplosion ready\n"); } void CExplosion::ClearAllExplosions() { for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) { gaExplosion[i].m_ExplosionType = EXPLOSION_GRENADE; gaExplosion[i].m_vecPosition = CVector(0.0f, 0.0f, 0.0f); gaExplosion[i].m_fRadius = 1.0f; gaExplosion[i].m_fPropagationRate = 0.0f; gaExplosion[i].m_fZshift = 0.0f; gaExplosion[i].m_pCreatorEntity = nil; gaExplosion[i].m_pVictimEntity = nil; gaExplosion[i].m_fStopTime = 0.0f; gaExplosion[i].m_nIteration = 0; gaExplosion[i].m_fStartTime = 0.0f; gaExplosion[i].m_bIsBoat = false; gaExplosion[i].m_bMakeSound = true; } } void CExplosion::Shutdown() { debug("Shutting down CExplosion...\n"); if (AudioHandle >= 0) { DMAudio.DestroyEntity(AudioHandle); AudioHandle = AEHANDLE_NONE; } debug("CExplosion shut down\n"); } int8 CExplosion::GetExplosionActiveCounter(uint8 id) { return gaExplosion[id].m_nActiveCounter; } void CExplosion::ResetExplosionActiveCounter(uint8 id) { gaExplosion[id].m_nActiveCounter = 0; } uint8 CExplosion::GetExplosionType(uint8 id) { return gaExplosion[id].m_ExplosionType; } bool CExplosion::DoesExplosionMakeSound(uint8 id) { return gaExplosion[id].m_bMakeSound; }; CVector * CExplosion::GetExplosionPosition(uint8 id) { return &gaExplosion[id].m_vecPosition; } bool CExplosion::AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionType type, const CVector &pos, uint32 lifetime, bool makeSound) { CVector pPosn; CVector posGround; RwRGBA colorMedium = colMedExpl; RwRGBA color = colAddExplosion; RwRGBA colorGrenade = colGrenade; bool bDontExplode = false; pPosn = pos; pPosn.z += 5.0f; #ifdef FIX_BUGS CShadows::AddPermanentShadow(SHADOWTEX_CAR, gpShadowHeliTex, &pPosn, 8.0f, 0.0f, 0.0f, -8.0f, 200, 0, 0, 0, 10.0f, 30000, 1.0f); #else // last two arguments are swapped resulting in no shadow CShadows::AddPermanentShadow(SHADOWTEX_CAR, gpShadowHeliTex, &pPosn, 8.0f, 0.0f, 0.0f, -8.0f, 200, 0, 0, 0, 10.0f, 1, 30000.0f); #endif int n = 0; #ifdef FIX_BUGS while (n < ARRAY_SIZE(gaExplosion) && gaExplosion[n].m_nIteration != 0) #else // array overrun is UB while (gaExplosion[n].m_nIteration != 0 && n < ARRAY_SIZE(gaExplosion)) #endif n++; if (n == ARRAY_SIZE(gaExplosion)) return false; CExplosion &explosion = gaExplosion[n]; explosion.m_ExplosionType = type; explosion.m_vecPosition = pos; explosion.m_fRadius = 1.0f; explosion.m_fZshift = 0.0f; explosion.m_pCreatorEntity = culprit; if (culprit != nil) culprit->RegisterReference(&explosion.m_pCreatorEntity); explosion.m_pVictimEntity = explodingEntity; if (explodingEntity != nil) explodingEntity->RegisterReference(&explosion.m_pVictimEntity); explosion.m_nIteration = 1; explosion.m_nActiveCounter = 1; explosion.m_bIsBoat = false; explosion.m_bMakeSound = makeSound; explosion.m_nParticlesExpireTime = lifetime != 0 ? CTimer::GetTimeInMilliseconds() + lifetime : 0; switch (type) { case EXPLOSION_GRENADE: explosion.m_fRadius = 9.0f; explosion.m_fPower = 300.0f; explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; explosion.m_fPropagationRate = 0.5f; posGround = pos; posGround.z = CWorld::FindGroundZFor3DCoord(posGround.x, posGround.y, posGround.z + 3.0f, nil); CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); if (Distance(explosion.m_vecPosition, TheCamera.GetPosition()) < 40.0f) { uint8 tmp = CGeneral::GetRandomNumberInRange(0, 64) - 64; colorGrenade.green += tmp; colorGrenade.blue += tmp; CParticle::AddParticle(PARTICLE_EXPLOSION_LFAST, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), nil, 4.5f, colorGrenade); } break; case EXPLOSION_MOLOTOV: { explosion.m_fRadius = 6.0f; explosion.m_fPower = 0.0f; explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 3000; explosion.m_fPropagationRate = 0.5f; posGround = pos; bool found; float tmp = CWorld::FindGroundZFor3DCoord(posGround.x, posGround.y, posGround.z + 3.0f, &found); if (found) posGround.z = tmp; float waterLevel; if (CWaterLevel::GetWaterLevelNoWaves(posGround.x, posGround.y, posGround.z, &waterLevel) && posGround.z < waterLevel && waterLevel - 6.0f < posGround.z) { // some subway/tunnels check? bDontExplode = true; } else if (found) { gFireManager.StartFire(posGround, 1.8f, false); } break; } case EXPLOSION_ROCKET: explosion.m_fRadius = 10.0f; explosion.m_fPower = 300.0f; explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; explosion.m_fPropagationRate = 0.5f; CEventList::RegisterEvent(EVENT_EXPLOSION, pos, 250); if (Distance(explosion.m_vecPosition, TheCamera.GetPosition()) < 40.0f) CParticle::AddParticle(PARTICLE_EXPLOSION_LFAST, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), nil, 5.5f, color); break; case EXPLOSION_CAR: case EXPLOSION_CAR_QUICK: case EXPLOSION_BOAT: explosion.m_fRadius = 9.0f; explosion.m_fPower = 300.0f; explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 4250; explosion.m_fPropagationRate = 0.5f; explosion.m_fStartTime = CTimer::GetTimeInMilliseconds(); if (explosion.m_pVictimEntity != nil) { if (explosion.m_pVictimEntity->IsVehicle() && ((CVehicle*)explosion.m_pVictimEntity)->IsBoat()) explosion.m_bIsBoat = true; CEventList::RegisterEvent(EVENT_EXPLOSION, EVENT_ENTITY_VEHICLE, explosion.m_pVictimEntity, nil, 1000); } else { CEventList::RegisterEvent(EVENT_EXPLOSION, pos, 1000); } if (explosion.m_pVictimEntity != nil && !explosion.m_bIsBoat) { CVehicle *veh = (CVehicle*)explosion.m_pVictimEntity; CVector componentPos; if (veh->IsBike()) { veh->GetComponentWorldPosition(BIKE_FORKS_REAR, componentPos); } else if (veh->IsComponentPresent(CAR_BUMP_REAR) && veh->IsComponentPresent(CAR_WHEEL_LB)) { //mb it's another enum CVector tmpVec; veh->GetComponentWorldPosition(CAR_BUMP_REAR, componentPos); veh->GetComponentWorldPosition(CAR_WHEEL_LB, tmpVec); componentPos += tmpVec; componentPos /= 2.0f; } else if (veh->IsComponentPresent(CAR_BOOT)) { veh->GetComponentWorldPosition(CAR_BOOT, componentPos); } if (componentPos.x != 0.0f) { int rn = (CGeneral::GetRandomNumber() & 1) + 1; for (int i = 0; i < rn; i++) CParticle::AddJetExplosion(componentPos, (CGeneral::GetRandomNumber() & 7) / 7.0f + 1.5f, 0.5f); } } break; case EXPLOSION_HELI: case EXPLOSION_HELI2: if (type == EXPLOSION_HELI2) { explosion.m_fRadius = 12.0f; explosion.m_fPower = 500.0f; } else { explosion.m_fRadius = 6.0f; explosion.m_fPower = 300.0f; } explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; explosion.m_fPropagationRate = 0.5f; explosion.m_fStartTime = CTimer::GetTimeInMilliseconds(); for (int i = 0; i < 10; i++) { CVector randpos; randpos.x = CGeneral::GetRandomNumber(); randpos.y = CGeneral::GetRandomNumber(); randpos.z = CGeneral::GetRandomNumber(); randpos -= CVector(128, 128, 128); randpos /= 20.0f; randpos += pos; CParticle::AddParticle(PARTICLE_EXPLOSION_MFAST, randpos, CVector(0.0f, 0.0f, 0.0f), nil, 2.5f, color); randpos.x = CGeneral::GetRandomNumber(); randpos.y = CGeneral::GetRandomNumber(); randpos.z = CGeneral::GetRandomNumber(); randpos -= CVector(128, 128, 128); randpos /= 20.0f; randpos += pos; CParticle::AddParticle(PARTICLE_EXPLOSION_LFAST, randpos, CVector(0.0f, 0.0f, 0.0f), nil, 5.0f, color); randpos.x = CGeneral::GetRandomNumber(); randpos.y = CGeneral::GetRandomNumber(); randpos.z = CGeneral::GetRandomNumber(); randpos -= CVector(128, 128, 128); randpos /= 20.0f; randpos += pos; CParticle::AddJetExplosion(randpos, 1.4f, 3.0f); } CEventList::RegisterEvent(EVENT_EXPLOSION, pos, 1000); break; case EXPLOSION_MINE: explosion.m_fRadius = 10.0f; explosion.m_fPower = 150.0f; explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; explosion.m_fPropagationRate = 0.5f; posGround = pos; //posGround.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); break; case EXPLOSION_BARREL: explosion.m_fRadius = 7.0f; explosion.m_fPower = 150.0f; explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; explosion.m_fPropagationRate = 0.5f; for (int i = 0; i < 6; i++) { CVector randpos; randpos.x = CGeneral::GetRandomNumber(); randpos.y = CGeneral::GetRandomNumber(); randpos.z = CGeneral::GetRandomNumber(); randpos -= CVector(128, 128, 128); randpos.x /= 50.0f; randpos.y /= 50.0f; randpos.z /= 25.0f; randpos += pos; CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, randpos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, colorMedium); } posGround = pos; //posGround.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); break; case EXPLOSION_TANK_GRENADE: explosion.m_fRadius = 10.0f; explosion.m_fPower = 150.0f; explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; explosion.m_fPropagationRate = 0.5f; posGround = pos; //posGround.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); break; case EXPLOSION_HELI_BOMB: explosion.m_fRadius = 8.0f; explosion.m_fPower = 50.0f; explosion.m_fStopTime = lifetime + CTimer::GetTimeInMilliseconds() + 750; explosion.m_fPropagationRate = 0.5f; posGround = pos; //posGround.z = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z + 4.0f, nil); // BUG? result is unused CEventList::RegisterEvent(EVENT_EXPLOSION, posGround, 250); break; default: debug("Undefined explosion type, AddExplosion, Explosion.cpp"); break; } if (bDontExplode) { explosion.m_nIteration = 0; return false; } if (explosion.m_fPower != 0.0f && explosion.m_nParticlesExpireTime == 0) CWorld::TriggerExplosion(pos, explosion.m_fRadius, explosion.m_fPower, culprit, (type == EXPLOSION_ROCKET || type == EXPLOSION_CAR_QUICK || type == EXPLOSION_MINE || type == EXPLOSION_BARREL || type == EXPLOSION_TANK_GRENADE || type == EXPLOSION_HELI_BOMB)); if (type == EXPLOSION_MOLOTOV) { TheCamera.CamShake(0.2f, pos.x, pos.y, pos.z); } else { TheCamera.CamShake(0.6f, pos.x, pos.y, pos.z); CPad::GetPad(0)->StartShake_Distance(300, 128, pos.x, pos.y, pos.z); } return true; } void CExplosion::Update() { RwRGBA color = colUpdate; for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) { CExplosion &explosion = gaExplosion[i]; if (explosion.m_nIteration == 0) continue; if (explosion.m_nParticlesExpireTime != 0) { if (CTimer::GetTimeInMilliseconds() > explosion.m_nParticlesExpireTime) { explosion.m_nParticlesExpireTime = 0; if (explosion.m_fPower != 0.0f) CWorld::TriggerExplosion(explosion.m_vecPosition, explosion.m_fRadius, explosion.m_fPower, explosion.m_pCreatorEntity, (explosion.m_ExplosionType == EXPLOSION_ROCKET || explosion.m_ExplosionType == EXPLOSION_CAR_QUICK || explosion.m_ExplosionType == EXPLOSION_MINE || explosion.m_ExplosionType == EXPLOSION_BARREL || explosion.m_ExplosionType == EXPLOSION_TANK_GRENADE || explosion.m_ExplosionType == EXPLOSION_HELI_BOMB)); } } else { explosion.m_fRadius += explosion.m_fPropagationRate * CTimer::GetTimeStep(); int32 someTime = explosion.m_fStopTime - CTimer::GetTimeInMilliseconds(); switch (explosion.m_ExplosionType) { case EXPLOSION_GRENADE: case EXPLOSION_ROCKET: case EXPLOSION_HELI: case EXPLOSION_HELI2: case EXPLOSION_MINE: case EXPLOSION_BARREL: if (CTimer::GetFrameCounter() & 1) { CPointLights::AddLight(CPointLights::LIGHT_POINT, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), 20.0f, 1.0f, 1.0f, 0.5f, CPointLights::FOG_NONE, true); CCoronas::RegisterCorona((uintptr)&explosion, 255, 255, 200, 255, explosion.m_vecPosition, 8.0f, 120.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } else CCoronas::RegisterCorona((uintptr)&explosion, 128, 128, 100, 255, explosion.m_vecPosition, 8.0f, 120.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); CCoronas::RegisterCorona((uintptr)&explosion + 1, 30, 30, 25, 255, explosion.m_vecPosition, explosion.m_fRadius, 120.0f, gpCoronaTexture[7], CCoronas::TYPE_STAR, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); break; case EXPLOSION_MOLOTOV: CWorld::SetPedsOnFire(explosion.m_vecPosition.x, explosion.m_vecPosition.y, explosion.m_vecPosition.z, 6.0f, explosion.m_pCreatorEntity); CWorld::SetCarsOnFire(explosion.m_vecPosition.x, explosion.m_vecPosition.y, explosion.m_vecPosition.z, 6.0f, explosion.m_pCreatorEntity); if (explosion.m_nIteration < 10) { if (explosion.m_nIteration == 1) { CVector point1 = explosion.m_vecPosition; point1.z += 5.0f; CColPoint colPoint; CEntity *pEntity; if (CWorld::ProcessVerticalLine(point1, -1000.0f, colPoint, pEntity, true, false, false, false, true, false, nil)) explosion.m_fZshift = colPoint.point.z; else explosion.m_fZshift = explosion.m_vecPosition.z; } float ff = ((float)explosion.m_nIteration * 0.55f); for (int i = 0; i < 5 * ff; i++) { float angle = CGeneral::GetRandomNumber() / 256.0f * 6.28f; CVector pos = explosion.m_vecPosition; pos.x += ff * Sin(angle); pos.y += ff * Cos(angle); pos.z = explosion.m_fZshift + 0.5f; CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, color, CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), CGeneral::GetRandomNumberInRange(-180.0f, 180.0f)); } } break; case EXPLOSION_CAR: case EXPLOSION_CAR_QUICK: case EXPLOSION_BOAT: if (someTime >= 3500) { if (explosion.m_pVictimEntity != nil) { if ((CGeneral::GetRandomNumber() & 0xF) == 0 && !explosion.m_bIsBoat) { CVehicle *veh = (CVehicle*)explosion.m_pVictimEntity; uint8 component = CAR_WING_LR; // miami leftover if (veh->IsBike()) component = BIKE_FORKS_REAR; if (veh->IsComponentPresent(component)) { CVector componentPos; veh->GetComponentWorldPosition(component, componentPos); CParticle::AddJetExplosion(componentPos, 0.5f, 0.0f); } } if (CTimer::GetTimeInMilliseconds() > explosion.m_fStartTime) { explosion.m_fStartTime = CTimer::GetTimeInMilliseconds() + 125 + (CGeneral::GetRandomNumber() & 0x7F); CVector pos = explosion.m_pVictimEntity->GetPosition(); for (int i = 0; i < (CGeneral::GetRandomNumber() & 1) + 1; i++) CParticle::AddParticle(PARTICLE_EXPLOSION_MEDIUM, pos, CVector(0.0f, 0.0f, 0.0f), nil, 3.5f, color); } } if (CTimer::GetFrameCounter() & 1) { CPointLights::AddLight(CPointLights::LIGHT_POINT, explosion.m_vecPosition, CVector(0.0f, 0.0f, 0.0f), 15.0f, 1.0f, 0.0f, 0.0f, CPointLights::FOG_NONE, true); CCoronas::RegisterCorona((uintptr)&explosion, 200, 100, 0, 255, explosion.m_vecPosition, 6.0f, 80.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } else CCoronas::RegisterCorona((uintptr)&explosion, 128, 0, 0, 255, explosion.m_vecPosition, 8.0f, 80.0f, gpCoronaTexture[0], CCoronas::TYPE_NORMAL, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); CCoronas::RegisterCorona((uintptr)&explosion + 1, 30, 15, 0, 255, explosion.m_vecPosition, explosion.m_fRadius, 80.0f, gpCoronaTexture[7], CCoronas::TYPE_STAR, CCoronas::REFLECTION_OFF, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f); } else if (explosion.m_nIteration & 1) { if (explosion.m_pVictimEntity != nil) CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, explosion.m_pVictimEntity->GetPosition(), CVector(0.0f, 0.0f, 0.0f), nil, CGeneral::GetRandomNumberInRange(0.5f, 0.8f), color); CVector pos = explosion.m_vecPosition; pos.z += 1.0f; CParticle::AddParticle(PARTICLE_ENGINE_SMOKE2, pos, CVector(0.0f, 0.0f, 0.11f), nil, CGeneral::GetRandomNumberInRange(0.5f, 2.0f), color); } break; case EXPLOSION_TANK_GRENADE: case EXPLOSION_HELI_BOMB: if (explosion.m_nIteration < 5) { float ff = ((float)explosion.m_nIteration * 0.65f); for (int i = 0; i < 10 * ff; i++) { uint8 x = CGeneral::GetRandomNumber(), y = CGeneral::GetRandomNumber(), z = CGeneral::GetRandomNumber(); CVector pos(x - 128, y - 128, (z % 128) + 1); pos.Normalise(); pos *= (explosion.m_nIteration + 1) * ff / 5.0f; pos += explosion.m_vecPosition; pos.z += 0.5f; CParticle::AddParticle(PARTICLE_EXPLOSION_LARGE, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, color, CGeneral::GetRandomNumberInRange(-3.0f, 3.0f), CGeneral::GetRandomNumberInRange(-180.0f, 180.0f)); } } break; default: break; } if (someTime > 0) explosion.m_nIteration++; else explosion.m_nIteration = 0; } } } bool CExplosion::TestForExplosionInArea(eExplosionType type, float x1, float x2, float y1, float y2, float z1, float z2) { for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) { if (gaExplosion[i].m_nIteration != 0) { if (type == gaExplosion[i].m_ExplosionType) { if (gaExplosion[i].m_vecPosition.x >= x1 && gaExplosion[i].m_vecPosition.x <= x2) { if (gaExplosion[i].m_vecPosition.y >= y1 && gaExplosion[i].m_vecPosition.y <= y2) { if (gaExplosion[i].m_vecPosition.z >= z1 && gaExplosion[i].m_vecPosition.z <= z2) return true; } } } } } return false; } void CExplosion::RemoveAllExplosionsInArea(CVector pos, float radius) { for (int i = 0; i < ARRAY_SIZE(gaExplosion); i++) { if (gaExplosion[i].m_nIteration != 0) { if ((pos - gaExplosion[i].m_vecPosition).MagnitudeSqr() < SQR(radius)) gaExplosion[i].m_nIteration = 0; } } } ================================================ FILE: src/weapons/Explosion.h ================================================ #pragma once class CEntity; class CVector; enum eExplosionType { EXPLOSION_GRENADE, EXPLOSION_MOLOTOV, EXPLOSION_ROCKET, EXPLOSION_CAR, EXPLOSION_CAR_QUICK, EXPLOSION_BOAT, EXPLOSION_HELI, EXPLOSION_HELI2, EXPLOSION_MINE, EXPLOSION_BARREL, EXPLOSION_TANK_GRENADE, EXPLOSION_HELI_BOMB }; class CExplosion { eExplosionType m_ExplosionType; CVector m_vecPosition; float m_fRadius; float m_fPropagationRate; CEntity *m_pCreatorEntity; CEntity *m_pVictimEntity; float m_fStopTime; uint8 m_nIteration; uint8 m_nActiveCounter; bool m_bIsBoat; bool m_bMakeSound; float m_fStartTime; uint32 m_nParticlesExpireTime; float m_fPower; float m_fZshift; public: static bool AddExplosion(CEntity *explodingEntity, CEntity *culprit, eExplosionType type, const CVector &pos, uint32 lifetime, bool makeSound = true); //done(new parametr in android ver is fix for one mission) static void ClearAllExplosions(); //done static bool DoesExplosionMakeSound(uint8 id); //done static int8 GetExplosionActiveCounter(uint8 id); //done static CVector *GetExplosionPosition(uint8 id); //done static uint8 GetExplosionType(uint8 id); //done, mb need change type to tExplosionType static void Initialise(); //done static void RemoveAllExplosionsInArea(CVector pos, float radius); //done static void ResetExplosionActiveCounter(uint8 id); //done static void Shutdown(); //done static void Update(); //done static bool TestForExplosionInArea(eExplosionType type, float x1, float x2, float y1, float y2, float z1, float z2); //done, not used }; extern CExplosion gaExplosion[NUM_EXPLOSIONS]; ================================================ FILE: src/weapons/ProjectileInfo.cpp ================================================ #include "common.h" #include "Camera.h" #include "General.h" #include "Heli.h" #include "ModelIndices.h" #include "Particle.h" #include "Ped.h" #include "Plane.h" #include "ProjectileInfo.h" #include "Projectile.h" #include "Explosion.h" #include "Weapon.h" #include "World.h" #ifdef SQUEEZE_PERFORMANCE uint32 projectileInUse; #endif CProjectileInfo gaProjectileInfo[NUM_PROJECTILES]; CProjectile *CProjectileInfo::ms_apProjectile[NUM_PROJECTILES]; #define PROJECTILE_BOUNDARY_MIN_X -2390.0f #define PROJECTILE_BOUNDARY_MAX_X 1590.0f #define PROJECTILE_BOUNDARY_MIN_Y -1990.0f #define PROJECTILE_BOUNDARY_MAX_Y 1990.0f void CProjectileInfo::Initialise() { debug("Initialising CProjectileInfo...\n"); for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) { ms_apProjectile[i] = nil; gaProjectileInfo[i].m_eWeaponType = WEAPONTYPE_GRENADE; gaProjectileInfo[i].m_pSource = nil; gaProjectileInfo[i].m_nExplosionTime = 0; gaProjectileInfo[i].m_bInUse = false; } debug("CProjectileInfo ready\n"); #ifdef SQUEEZE_PERFORMANCE projectileInUse = 0; #endif } void CProjectileInfo::Shutdown() { debug("Shutting down CProjectileInfo...\n"); debug("CProjectileInfo shut down\n"); } CProjectileInfo* CProjectileInfo::GetProjectileInfo(int32 id) { return &gaProjectileInfo[id]; } bool CProjectileInfo::AddProjectile(CEntity *entity, eWeaponType weapon, CVector pos, float speed) { int8 SpecialCollisionResponseCase = COLLRESPONSE_NONE; bool gravity = true; CMatrix matrix; float elasticity = 0.75f; CPed* ped = (CPed*)entity; int time; CVector velocity; switch (weapon) { case WEAPONTYPE_ROCKET: { float vy = 0.35f; time = CTimer::GetTimeInMilliseconds() + 2000; if (entity->GetModelIndex() == MI_SPARROW || entity->GetModelIndex() == MI_HUNTER || entity->GetModelIndex() == MI_SENTINEL) { matrix = ped->GetMatrix(); matrix.GetPosition() = pos; CVector vecSpeed = ((CPhysical*)entity)->m_vecMoveSpeed; vy += Max(0.0f, DotProduct(vecSpeed, entity->GetForward())) + Max(0.0f, DotProduct(vecSpeed, entity->GetUp())); } else { if (ped->IsPlayer()) { matrix.GetForward() = TheCamera.Cams[TheCamera.ActiveCam].Front; matrix.GetUp() = TheCamera.Cams[TheCamera.ActiveCam].Up; matrix.GetRight() = CrossProduct(TheCamera.Cams[TheCamera.ActiveCam].Up, TheCamera.Cams[TheCamera.ActiveCam].Front); matrix.GetPosition() = pos; } else if (ped->m_pSeekTarget != nil) { float ry = CGeneral::GetRadianAngleBetweenPoints(1.0f, ped->m_pSeekTarget->GetPosition().z, 1.0f, pos.z); float rz = Atan2(-ped->GetForward().x, ped->GetForward().y); vy = 0.35f * speed + 0.15f; matrix.SetTranslate(0.0f, 1.0f, 1.0f); matrix.Rotate(0.0f, ry, rz); matrix.GetPosition() += pos; } else { matrix = ped->GetMatrix(); } } velocity = Multiply3x3(matrix, CVector(0.0f, vy, 0.0f)); gravity = false; break; } case WEAPONTYPE_MOLOTOV: { time = CTimer::GetTimeInMilliseconds() + 2000; float scale = 0.22f * speed + 0.15f; if (scale < 0.2f) scale = 0.2f; float angle = Atan2(-ped->GetForward().x, ped->GetForward().y); matrix.SetTranslate(0.0f, 0.0f, 0.0f); matrix.RotateZ(angle); matrix.GetPosition() += pos; velocity.x = -1.0f * scale * Sin(angle); velocity.y = scale * Cos(angle); velocity.z = (0.2f * speed + 0.4f) * scale; break; } case WEAPONTYPE_TEARGAS: { time = CTimer::GetTimeInMilliseconds() + 20000; float scale = 0.0f; if (speed != 0.0f) scale = 0.22f * speed + 0.15f; float angle = Atan2(-ped->GetForward().x, ped->GetForward().y); matrix.SetTranslate(0.0f, 0.0f, 0.0f); matrix.RotateZ(angle); matrix.GetPosition() += pos; SpecialCollisionResponseCase = COLLRESPONSE_UNKNOWN5; velocity.x = -1.0f * scale * Sin(angle); velocity.y = scale * Cos(angle); velocity.z = (0.4f * speed + 0.4f) * scale; elasticity = 0.5f; break; } case WEAPONTYPE_GRENADE: case WEAPONTYPE_DETONATOR_GRENADE: { time = CTimer::GetTimeInMilliseconds() + 2000; float scale = 0.0f; if (speed != 0.0f) scale = 0.22f * speed + 0.15f; float angle = Atan2(-ped->GetForward().x, ped->GetForward().y); matrix.SetTranslate(0.0f, 0.0f, 0.0f); matrix.RotateZ(angle); matrix.GetPosition() += pos; SpecialCollisionResponseCase = COLLRESPONSE_UNKNOWN5; velocity.x = -1.0f * scale * Sin(angle); velocity.y = scale * Cos(angle); velocity.z = (0.4f * speed + 0.4f) * scale; elasticity = 0.5f; break; } default: Error("Undefined projectile type, AddProjectile, ProjectileInfo.cpp"); break; } int i = 0; #ifdef FIX_BUGS while (i < ARRAY_SIZE(gaProjectileInfo) && gaProjectileInfo[i].m_bInUse) i++; #else // array overrun is UB while (gaProjectileInfo[i].m_bInUse && i < ARRAY_SIZE(gaProjectileInfo)) i++; #endif if (i == ARRAY_SIZE(gaProjectileInfo)) return false; switch (weapon) { case WEAPONTYPE_ROCKET: ms_apProjectile[i] = new CProjectile(MI_MISSILE); break; case WEAPONTYPE_TEARGAS: ms_apProjectile[i] = new CProjectile(MI_TEARGAS); break; case WEAPONTYPE_MOLOTOV: ms_apProjectile[i] = new CProjectile(MI_MOLOTOV); break; case WEAPONTYPE_GRENADE: case WEAPONTYPE_DETONATOR_GRENADE: ms_apProjectile[i] = new CProjectile(MI_GRENADE); break; default: break; } if (ms_apProjectile[i] == nil) return false; gaProjectileInfo[i].m_eWeaponType = weapon; gaProjectileInfo[i].m_pSource = ped; ms_apProjectile[i]->GetMatrix() = matrix; ms_apProjectile[i]->SetMoveSpeed(velocity); ms_apProjectile[i]->bAffectedByGravity = gravity; gaProjectileInfo[i].m_nExplosionTime = time; ms_apProjectile[i]->m_fElasticity = elasticity; ms_apProjectile[i]->m_nSpecialCollisionResponseCases = SpecialCollisionResponseCase; #ifdef SQUEEZE_PERFORMANCE projectileInUse++; #endif gaProjectileInfo[i].m_bInUse = true; CWorld::Add(ms_apProjectile[i]); gaProjectileInfo[i].m_vecPos = ms_apProjectile[i]->GetPosition(); if (entity && entity->IsPed() && !ped->m_pCollidingEntity) { ped->m_pCollidingEntity = ms_apProjectile[i]; } return true; } void CProjectileInfo::RemoveProjectile(CProjectileInfo *info, CProjectile *projectile) { // TODO(Miami): New parameter: 1 switch (info->m_eWeaponType) { case WEAPONTYPE_GRENADE: CExplosion::AddExplosion(nil, info->m_pSource, EXPLOSION_GRENADE, projectile->GetPosition(), 0); break; case WEAPONTYPE_MOLOTOV: CExplosion::AddExplosion(nil, info->m_pSource, EXPLOSION_MOLOTOV, projectile->GetPosition(), 0); break; case WEAPONTYPE_ROCKET: CExplosion::AddExplosion(nil, info->m_pSource->IsVehicle() ? ((CVehicle*)info->m_pSource)->pDriver : info->m_pSource, EXPLOSION_ROCKET, projectile->GetPosition(), 0); break; } #ifdef SQUEEZE_PERFORMANCE projectileInUse--; #endif info->m_bInUse = false; CWorld::Remove(projectile); delete projectile; } void CProjectileInfo::RemoveNotAdd(CEntity *entity, eWeaponType weaponType, CVector pos) { // TODO(Miami): New parameter: 1 switch (weaponType) { case WEAPONTYPE_GRENADE: CExplosion::AddExplosion(nil, entity, EXPLOSION_GRENADE, pos, 0); break; case WEAPONTYPE_MOLOTOV: CExplosion::AddExplosion(nil, entity, EXPLOSION_MOLOTOV, pos, 0); break; case WEAPONTYPE_ROCKET: CExplosion::AddExplosion(nil, entity, EXPLOSION_ROCKET, pos, 0); break; } } void CProjectileInfo::Update() { #ifdef SQUEEZE_PERFORMANCE if (projectileInUse == 0) return; #endif int tearGasOffset = -0.0f; // unused for (int i = 0; i < ARRAY_SIZE(gaProjectileInfo); i++) { if (!gaProjectileInfo[i].m_bInUse) continue; CPed *ped = (CPed*)gaProjectileInfo[i].m_pSource; if (ped != nil && ped->IsPed() && !ped->IsPointerValid()) gaProjectileInfo[i].m_pSource = nil; if (ms_apProjectile[i] == nil) { #ifdef SQUEEZE_PERFORMANCE projectileInUse--; #endif gaProjectileInfo[i].m_bInUse = false; continue; } if ( (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE || gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_TEARGAS) && ms_apProjectile[i]->m_fElasticity > 0.1f ) { if ( Abs(ms_apProjectile[i]->m_vecMoveSpeed.x) < 0.05f && Abs(ms_apProjectile[i]->m_vecMoveSpeed.y) < 0.05f && Abs(ms_apProjectile[i]->m_vecMoveSpeed.z) < 0.05f ) { ms_apProjectile[i]->m_fElasticity = 0.03f; } } const CVector &projectilePos = ms_apProjectile[i]->GetPosition(); CVector nextPos = CTimer::GetTimeStep() * ms_apProjectile[i]->m_vecMoveSpeed + projectilePos; if ( nextPos.x <= PROJECTILE_BOUNDARY_MIN_X || nextPos.x >= PROJECTILE_BOUNDARY_MAX_X || nextPos.y <= PROJECTILE_BOUNDARY_MIN_Y || nextPos.y >= PROJECTILE_BOUNDARY_MAX_Y ) { // Not RemoveProjectile, because we don't want no explosion gaProjectileInfo[i].m_bInUse = false; CWorld::Remove(ms_apProjectile[i]); delete ms_apProjectile[i]; continue; } if ( gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_TEARGAS && CTimer::GetTimeInMilliseconds() > gaProjectileInfo[i].m_nExplosionTime - 19500 ) { CParticle::AddParticle(PARTICLE_TEARGAS, projectilePos, CVector(0.2f, tearGasOffset, 0.0f), 0, 0.0f, 0, 0, 0, 0); CParticle::AddParticle(PARTICLE_TEARGAS, projectilePos, CVector(-0.2f, tearGasOffset, 0.0f), 0, 0.0f, 0, 0, 0, 0); CParticle::AddParticle(PARTICLE_TEARGAS, projectilePos, CVector(tearGasOffset, tearGasOffset, 0.0f), 0, 0.0f, 0, 0, 0, 0); if ( CTimer::GetTimeInMilliseconds() & 0x200 ) CWorld::SetPedsChoking(projectilePos.x, projectilePos.y, projectilePos.z, 6.0f, gaProjectileInfo[i].m_pSource); } if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_ROCKET) { CParticle::AddParticlesAlongLine(PARTICLE_ROCKET_SMOKE, gaProjectileInfo[i].m_vecPos, projectilePos, CVector(0.0f, 0.0f, 0.0f), 0.7f, 0, 0, 0, 3000); } if (CTimer::GetTimeInMilliseconds() <= gaProjectileInfo[i].m_nExplosionTime || gaProjectileInfo[i].m_nExplosionTime == 0) { if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_ROCKET) { CVector pos = ms_apProjectile[i]->GetPosition(); CWorld::pIgnoreEntity = ms_apProjectile[i]; if (ms_apProjectile[i]->bHasCollided || !CWorld::GetIsLineOfSightClear(gaProjectileInfo[i].m_vecPos, pos, true, true, true, true, false, false) || CHeli::TestRocketCollision(&pos) || CPlane::TestRocketCollision(&pos)) { RemoveProjectile(&gaProjectileInfo[i], ms_apProjectile[i]); } CWorld::pIgnoreEntity = nil; ms_apProjectile[i]->m_vecMoveSpeed *= 1.07f; } else if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_MOLOTOV) { CVector pos = ms_apProjectile[i]->GetPosition(); CWorld::pIgnoreEntity = ms_apProjectile[i]; if (gaProjectileInfo[i].m_pSource == nil || ((gaProjectileInfo[i].m_vecPos - gaProjectileInfo[i].m_pSource->GetPosition()).MagnitudeSqr() >= 2.0f)) { if (ms_apProjectile[i]->bHasCollided || !CWorld::GetIsLineOfSightClear(gaProjectileInfo[i].m_vecPos, pos, true, true, true, true, false, false)) { RemoveProjectile(&gaProjectileInfo[i], ms_apProjectile[i]); } } CWorld::pIgnoreEntity = nil; } } else { if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) { CEntity *ent = gaProjectileInfo[i].m_pSource; if (ent->IsPed() && ((CPed*)ped)->IsPlayer()) { CPed *ped = (CPed*)ent; if (ped->GetWeapon(ped->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_eWeaponType != WEAPONTYPE_DETONATOR || ped->GetWeapon(ped->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_nAmmoTotal == 0) { gaProjectileInfo[i].m_nExplosionTime = 0; } } } else { RemoveProjectile(&gaProjectileInfo[i], ms_apProjectile[i]); } } gaProjectileInfo[i].m_vecPos = ms_apProjectile[i]->GetPosition(); } } bool CProjectileInfo::IsProjectileInRange(float x1, float x2, float y1, float y2, float z1, float z2, bool remove) { bool result = false; for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) { if (gaProjectileInfo[i].m_bInUse) { if (gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_ROCKET || gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_MOLOTOV || gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_GRENADE) { const CVector &pos = ms_apProjectile[i]->GetPosition(); if (pos.x >= x1 && pos.x <= x2 && pos.y >= y1 && pos.y <= y2 && pos.z >= z1 && pos.z <= z2) { result = true; if (remove) { #ifdef SQUEEZE_PERFORMANCE projectileInUse--; #endif gaProjectileInfo[i].m_bInUse = false; CWorld::Remove(ms_apProjectile[i]); delete ms_apProjectile[i]; } } } } } return result; } void CProjectileInfo::RemoveDetonatorProjectiles() { for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) { if (gaProjectileInfo[i].m_bInUse && gaProjectileInfo[i].m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) { CExplosion::AddExplosion(nil, gaProjectileInfo[i].m_pSource, EXPLOSION_GRENADE, gaProjectileInfo[i].m_vecPos, 0); // TODO(Miami): New parameter: 1 gaProjectileInfo[i].m_bInUse = false; CWorld::Remove(ms_apProjectile[i]); delete ms_apProjectile[i]; } } } void CProjectileInfo::RemoveAllProjectiles() { #ifdef SQUEEZE_PERFORMANCE if (projectileInUse == 0) return; #endif for (int i = 0; i < ARRAY_SIZE(ms_apProjectile); i++) { if (gaProjectileInfo[i].m_bInUse) { #ifdef SQUEEZE_PERFORMANCE projectileInUse--; #endif gaProjectileInfo[i].m_bInUse = false; CWorld::Remove(ms_apProjectile[i]); delete ms_apProjectile[i]; } } } bool CProjectileInfo::RemoveIfThisIsAProjectile(CObject *object) { #ifdef SQUEEZE_PERFORMANCE if (projectileInUse == 0) return false; #endif int i = 0; while (ms_apProjectile[i++] != object) { if (i >= ARRAY_SIZE(ms_apProjectile)) return false; } #ifdef SQUEEZE_PERFORMANCE projectileInUse--; #endif gaProjectileInfo[i].m_bInUse = false; CWorld::Remove(ms_apProjectile[i]); delete ms_apProjectile[i]; ms_apProjectile[i] = nil; return true; } ================================================ FILE: src/weapons/ProjectileInfo.h ================================================ #pragma once #include "WeaponType.h" class CEntity; class CObject; class CProjectile; class CProjectileInfo { public: eWeaponType m_eWeaponType; CEntity *m_pSource; uint32 m_nExplosionTime; bool m_bInUse; CVector m_vecPos; public: static CProjectileInfo *GetProjectileInfo(int32 id); static CProjectile *ms_apProjectile[NUM_PROJECTILES]; static void Initialise(); static void Shutdown(); static bool AddProjectile(CEntity *ped, eWeaponType weapon, CVector pos, float speed); static void RemoveProjectile(CProjectileInfo *info, CProjectile *projectile); static void RemoveNotAdd(CEntity *entity, eWeaponType weaponType, CVector pos); static bool RemoveIfThisIsAProjectile(CObject *pObject); static void RemoveAllProjectiles(); static void RemoveDetonatorProjectiles(); static void Update(); static bool IsProjectileInRange(float x1, float x2, float y1, float y2, float z1, float z2, bool remove); }; extern CProjectileInfo gaProjectileInfo[NUM_PROJECTILES]; ================================================ FILE: src/weapons/ShotInfo.cpp ================================================ #include "common.h" #include "ShotInfo.h" #include "Entity.h" #include "Weapon.h" #include "World.h" #include "WeaponInfo.h" #include "General.h" #include "Timer.h" #include "Ped.h" #include "Fire.h" CShotInfo gaShotInfo[NUMSHOTINFOS]; float CShotInfo::ms_afRandTable[20]; #ifdef SQUEEZE_PERFORMANCE uint32 shotInfoInUse; #endif /* Used for flamethrower. I don't know why it's name is CShotInfo. Has no relation with any visual, just calculates the area fire affects (including spreading and slowing of fire) and make entities burn/flee. */ void CShotInfo::Initialise() { debug("Initialising CShotInfo...\n"); for(int i=0; im_fRadius; if (weaponInfo->m_fSpread != 0.0f) { gaShotInfo[slot].m_areaAffected.x += CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)] * weaponInfo->m_fSpread; gaShotInfo[slot].m_areaAffected.y += CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)] * weaponInfo->m_fSpread; gaShotInfo[slot].m_areaAffected.z += CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)]; } gaShotInfo[slot].m_areaAffected.Normalise(); if (weaponInfo->IsFlagSet(WEAPONFLAG_RAND_SPEED)) gaShotInfo[slot].m_areaAffected *= CShotInfo::ms_afRandTable[CGeneral::GetRandomNumber() % ARRAY_SIZE(ms_afRandTable)] + weaponInfo->m_fSpeed; else gaShotInfo[slot].m_areaAffected *= weaponInfo->m_fSpeed; gaShotInfo[slot].m_sourceEntity = sourceEntity; gaShotInfo[slot].m_timeout = CTimer::GetTimeInMilliseconds() + weaponInfo->m_fLifespan; return true; } void CShotInfo::Shutdown() { debug("Shutting down CShotInfo...\n"); debug("CShotInfo shut down\n"); } void CShotInfo::Update() { #ifdef SQUEEZE_PERFORMANCE if (shotInfoInUse == 0) return; #endif for (int slot = 0; slot < ARRAY_SIZE(gaShotInfo); slot++) { CShotInfo &shot = gaShotInfo[slot]; if (shot.m_sourceEntity && shot.m_sourceEntity->IsPed() && !((CPed*)shot.m_sourceEntity)->IsPointerValid()) shot.m_sourceEntity = nil; if (!shot.m_inUse) continue; CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(shot.m_weapon); if (CTimer::GetTimeInMilliseconds() > shot.m_timeout) { #ifdef SQUEEZE_PERFORMANCE shotInfoInUse--; #endif shot.m_inUse = false; } if (weaponInfo->IsFlagSet(WEAPONFLAG_SLOWS_DOWN)) shot.m_areaAffected *= pow(0.96, CTimer::GetTimeStep()); // FRAMERATE if (weaponInfo->IsFlagSet(WEAPONFLAG_EXPANDS)) shot.m_radius += 0.075f * CTimer::GetTimeStep(); shot.m_startPos += CTimer::GetTimeStep() * shot.m_areaAffected; if (shot.m_sourceEntity) { assert(shot.m_sourceEntity->IsPed()); CPed *ped = (CPed*) shot.m_sourceEntity; float radius = Max(1.0f, shot.m_radius); for (int i = 0; i < ped->m_numNearPeds; ++i) { CPed *nearPed = ped->m_nearPeds[i]; if (nearPed->IsPointerValid()) { if (nearPed->IsPedInControl() && (nearPed->GetPosition() - shot.m_startPos).MagnitudeSqr() < radius && !nearPed->bFireProof) { if (!nearPed->IsPlayer()) { nearPed->SetFindPathAndFlee(shot.m_sourceEntity, 10000); nearPed->SetMoveState(PEDMOVE_SPRINT); } gFireManager.StartFire(nearPed, shot.m_sourceEntity, 0.8f, true); } } } } if (!((CTimer::GetFrameCounter() + slot) & 3)) CWorld::SetCarsOnFire(shot.m_startPos.x, shot.m_startPos.y, shot.m_startPos.z, 4.0f, shot.m_sourceEntity); } } ================================================ FILE: src/weapons/ShotInfo.h ================================================ #pragma once #include "WeaponType.h" class CEntity; class CShotInfo { public: eWeaponType m_weapon; CVector m_startPos; CVector m_areaAffected; float m_radius; CEntity *m_sourceEntity; float m_timeout; bool m_inUse; static float ms_afRandTable[20]; static void Initialise(void); static bool AddShot(CEntity*, eWeaponType, CVector, CVector); static void Shutdown(void); static void Update(void); }; ================================================ FILE: src/weapons/Weapon.cpp ================================================ #include "common.h" #include "Weapon.h" #include "AnimBlendAssociation.h" #include "AudioManager.h" #include "BulletInfo.h" #include "Camera.h" #include "Coronas.h" #include "DMAudio.h" #include "Explosion.h" #include "General.h" #include "Glass.h" #include "Heli.h" #include "ModelIndices.h" #include "Object.h" #include "Pad.h" #include "Particle.h" #include "Ped.h" #include "PointLights.h" #include "Pools.h" #include "ProjectileInfo.h" #include "RpAnimBlend.h" #include "ShotInfo.h" #include "SpecialFX.h" #include "Stats.h" #include "TempColModels.h" #include "Timer.h" #include "Automobile.h" #include "Boat.h" #include "WaterLevel.h" #include "WeaponInfo.h" #include "World.h" #include "SurfaceTable.h" #include "Bike.h" #include "Glass.h" #include "Sprite.h" #include "Pickups.h" float fReloadAnimSampleFraction[5] = { 0.5f, 0.7f, 0.75f, 0.75f, 0.7f }; float fSeaSparrowAimingAngle = 10.0f; float fHunterAimingAngle = 30.0f; float fPlayerAimScaleDist = 5.0f; float fPlayerAimScale = 2.5f; bool CWeapon::bPhotographHasBeenTaken; #ifdef SECUROM int32 sniperPirateCheck = 0x00797743; // 'Cwy\0' ??? #endif CWeaponInfo * CWeapon::GetInfo() { CWeaponInfo *info = CWeaponInfo::GetWeaponInfo(m_eWeaponType); ASSERT(info!=nil); return info; } CWeapon::CWeapon(eWeaponType type, int32 ammo) { m_eWeaponType = type; m_eWeaponState = WEAPONSTATE_READY; m_nAmmoTotal = Min(ammo, 99999); m_nAmmoInClip = 0; Reload(); m_nTimer = 0; m_bAddRotOffset = false; } void CWeapon::InitialiseWeapons(void) { CWeaponInfo::Initialise(); CShotInfo::Initialise(); CExplosion::Initialise(); CProjectileInfo::Initialise(); CBulletInfo::Initialise(); bPhotographHasBeenTaken = false; } void CWeapon::ShutdownWeapons(void) { CWeaponInfo::Shutdown(); CShotInfo::Shutdown(); CExplosion::Shutdown(); CProjectileInfo::Shutdown(); CBulletInfo::Shutdown(); } void CWeapon::UpdateWeapons(void) { CShotInfo::Update(); CExplosion::Update(); CProjectileInfo::Update(); CBulletInfo::Update(); } void CWeapon::Initialise(eWeaponType type, int32 ammo) { m_eWeaponType = type; m_eWeaponState = WEAPONSTATE_READY; m_nAmmoTotal = Min(ammo, 99999); m_nAmmoInClip = 0; Reload(); m_nTimer = 0; int32 modelId = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModelId; int32 model2Id = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModel2Id; if ( modelId != -1 ) CModelInfo::GetModelInfo(modelId)->AddRef(); if ( model2Id != -1 ) CModelInfo::GetModelInfo(model2Id)->AddRef(); } void CWeapon::Shutdown() { int32 modelId = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModelId; if (modelId != -1) CModelInfo::GetModelInfo(modelId)->RemoveRef(); int32 model2Id = CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_nModel2Id; if (model2Id != -1) CModelInfo::GetModelInfo(model2Id)->RemoveRef(); m_eWeaponType = WEAPONTYPE_UNARMED; m_eWeaponState = WEAPONSTATE_READY; m_nAmmoInClip = 0; m_nAmmoTotal = 0; m_nTimer = 0; } bool CWeapon::Fire(CEntity *shooter, CVector *fireSource) { ASSERT(shooter!=nil); CVector fireOffset(0.0f, 0.0f, 0.6f); CVector *source = fireSource; #ifdef FIX_BUGS static CVector shooterSource; #else CVector shooterSource; #endif if ( !fireSource ) { shooterSource = shooter->GetMatrix() * fireOffset; source = &shooterSource; } if ( m_bAddRotOffset ) { float heading = RADTODEG(shooter->GetForward().Heading()); float angle = DEGTORAD(heading); (*source).x += -Sin(angle) * 0.15f; (*source).y += Cos(angle) * 0.15f; } if ( m_eWeaponState != WEAPONSTATE_READY && m_eWeaponState != WEAPONSTATE_FIRING ) return false; bool fired; bool addFireRateAsDelay = true; if ( GetInfo()->m_eWeaponFire != WEAPON_FIRE_MELEE ) { if (m_nAmmoInClip <= 0) { if (m_nAmmoTotal <= 0 || m_eWeaponState == WEAPONSTATE_RELOADING) return false; Reload(); } switch ( m_eWeaponType ) { case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_SPAS12_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: { addFireRateAsDelay = true; fired = FireShotgun(shooter, source); break; } case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: { if (shooter == FindPlayerPed()) fired = FireSniper(shooter); else fired = FireInstantHit(shooter, source); break; } case WEAPONTYPE_COLT45: case WEAPONTYPE_PYTHON: case WEAPONTYPE_UZI: case WEAPONTYPE_TEC9: case WEAPONTYPE_SILENCED_INGRAM: case WEAPONTYPE_MP5: case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: case WEAPONTYPE_M60: case WEAPONTYPE_MINIGUN: case WEAPONTYPE_HELICANNON: { if ((TheCamera.PlayerWeaponMode.Mode == CCam::MODE_HELICANNON_1STPERSON || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_M16_1STPERSON) && shooter == FindPlayerPed()) { addFireRateAsDelay = true; fired = FireM16_1stPerson(shooter); } else { addFireRateAsDelay = false; fired = FireInstantHit(shooter, source); } break; } case WEAPONTYPE_ROCKETLAUNCHER: { if ( shooter->IsPed() && ((CPed*)shooter)->m_pSeekTarget != nil ) { float distToTarget = (shooter->GetPosition() - ((CPed*)shooter)->m_pSeekTarget->GetPosition()).Magnitude(); if ( distToTarget > 8.0f || ((CPed*)shooter)->IsPlayer() ) fired = FireProjectile(shooter, source, 0.0f); else fired = false; } else fired = FireProjectile(shooter, source, 0.0f); break; } case WEAPONTYPE_MOLOTOV: case WEAPONTYPE_GRENADE: case WEAPONTYPE_DETONATOR_GRENADE: case WEAPONTYPE_TEARGAS: { if ( shooter == FindPlayerPed() ) { fired = FireProjectile(shooter, source, ((CPlayerPed*)shooter)->m_fAttackButtonCounter*0.0375f); } else if ( shooter->IsPed() && ((CPed*)shooter)->m_pSeekTarget != nil ) { float distToTarget = (shooter->GetPosition() - ((CPed*)shooter)->m_pSeekTarget->GetPosition()).Magnitude(); float power = clamp((distToTarget-10.0f)*0.02f, 0.2f, 1.0f); fired = FireProjectile(shooter, source, power); } else fired = FireProjectile(shooter, source, 0.3f); if (m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE) { ((CPed*)shooter)->GiveWeapon(WEAPONTYPE_DETONATOR, 1, true); ((CPed*)shooter)->GetWeapon(((CPed*)shooter)->GetWeaponSlot(WEAPONTYPE_DETONATOR)).m_eWeaponState = WEAPONSTATE_READY; ((CPed*)shooter)->SetCurrentWeapon(WEAPONTYPE_DETONATOR); } break; } case WEAPONTYPE_FLAMETHROWER: { fired = FireAreaEffect(shooter, source); break; } case WEAPONTYPE_DETONATOR: { CWorld::UseDetonator(shooter); m_nAmmoTotal = 1; m_nAmmoInClip = m_nAmmoTotal; fired = true; break; } case WEAPONTYPE_CAMERA: { fired = TakePhotograph(shooter); break; } default: { debug("Unknown weapon type, Weapon.cpp"); break; } } if (fired) { bool isPlayer = false; if (shooter->IsPed()) { CPed* shooterPed = (CPed*)shooter; if ( m_eWeaponType != WEAPONTYPE_CAMERA ) { shooterPed->bIsShooting = true; if (shooterPed->IsPlayer()) isPlayer = true; } DMAudio.PlayOneShot(shooterPed->m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); if ( isPlayer ) { CPed *aimPed = (CPed *)shooterPed->m_pSeekTarget; if ( aimPed ) { if ( aimPed->IsPed() ) shooterPed->Say(SOUND_PED_ON_FIRE); } } } switch ( m_eWeaponType ) { case WEAPONTYPE_COLT45: case WEAPONTYPE_PYTHON: case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_SPAS12_SHOTGUN: case WEAPONTYPE_STUBBY_SHOTGUN: case WEAPONTYPE_TEC9: case WEAPONTYPE_UZI: case WEAPONTYPE_SILENCED_INGRAM: case WEAPONTYPE_MP5: case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: case WEAPONTYPE_M60: case WEAPONTYPE_MINIGUN: CStats::RoundsFiredByPlayer++; break; case WEAPONTYPE_GRENADE: case WEAPONTYPE_DETONATOR_GRENADE: case WEAPONTYPE_MOLOTOV: case WEAPONTYPE_ROCKET: case WEAPONTYPE_ROCKETLAUNCHER: case WEAPONTYPE_DETONATOR: case WEAPONTYPE_HELICANNON: CStats::KgsOfExplosivesUsed++; break; } if (m_nAmmoInClip > 0) m_nAmmoInClip--; if (m_nAmmoTotal > 0 && (m_nAmmoTotal < 25000 || isPlayer) && (!isPlayer || CStats::GetPercentageProgress() < 100.0f || m_eWeaponType == WEAPONTYPE_DETONATOR)) m_nAmmoTotal--; if (m_eWeaponState == WEAPONSTATE_READY && m_eWeaponType == WEAPONTYPE_FLAMETHROWER) DMAudio.PlayOneShot(((CPhysical*)shooter)->m_audioEntityId, SOUND_WEAPON_FLAMETHROWER_FIRE, 0.0f); m_eWeaponState = WEAPONSTATE_FIRING; if (m_nAmmoInClip == 0) { if (m_nAmmoTotal == 0) { if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_CAMERA) CPad::GetPad(0)->Clear(false); return true; } m_eWeaponState = WEAPONSTATE_RELOADING; m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload; if (shooter == FindPlayerPed()) { if (CWorld::Players[CWorld::PlayerInFocus].m_bFastReload) m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload / 4; } return true; } if ( addFireRateAsDelay ) m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nFiringRate; else m_nTimer = CTimer::GetTimeInMilliseconds(); } } else { if ( m_eWeaponState != WEAPONSTATE_FIRING ) { m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload; m_eWeaponState = WEAPONSTATE_FIRING; if (shooter->IsPed() && m_eWeaponType != WEAPONTYPE_CHAINSAW) { DMAudio.PlayOneShot(((CPed*)shooter)->m_audioEntityId, SOUND_MELEE_ATTACK_START, m_eWeaponType << 8); } } fired = FireMelee(shooter, *source); } if ( m_eWeaponType == WEAPONTYPE_UNARMED || m_eWeaponType == WEAPONTYPE_BASEBALLBAT ) return true; else return fired; } bool CWeapon::FireFromCar(CVehicle *shooter, bool left, bool right) { ASSERT(shooter!=nil); if ( m_eWeaponState != WEAPONSTATE_READY && m_eWeaponState != WEAPONSTATE_FIRING ) return false; if ( m_nAmmoInClip <= 0 ) return false; if ( FireInstantHitFromCar(shooter, left, right) ) { DMAudio.PlayOneShot(shooter->m_audioEntityId, SOUND_WEAPON_SHOT_FIRED, 0.0f); if ( m_nAmmoInClip > 0 ) m_nAmmoInClip--; if ( m_nAmmoTotal < 25000 && m_nAmmoTotal > 0 && (!shooter || shooter->GetStatus() != STATUS_PLAYER || CStats::GetPercentageProgress() < 100.f)) m_nAmmoTotal--; m_eWeaponState = WEAPONSTATE_FIRING; if ( m_nAmmoInClip == 0 ) { if ( m_nAmmoTotal == 0 ) return true; m_eWeaponState = WEAPONSTATE_RELOADING; m_nTimer = CTimer::GetTimeInMilliseconds() + GetInfo()->m_nReload; return true; } m_nTimer = CTimer::GetTimeInMilliseconds() + 1000; } return true; } bool CWeapon::FireMelee(CEntity *shooter, CVector &fireSource) { ASSERT(shooter!=nil); CWeaponInfo *info = GetInfo(); bool anim2Playing = false; if ( CPed::GetFireAnimGround(info, false) != (AnimationId)0 ) { if ( RpAnimBlendClumpGetAssociation(shooter->GetClump(), CPed::GetFireAnimGround(info, false)) ) anim2Playing = true; } ASSERT(shooter->IsPed()); CPed *shooterPed = (CPed*)shooter; if (shooterPed == FindPlayerPed()) { if (m_eWeaponType == WEAPONTYPE_GOLFCLUB || m_eWeaponType == WEAPONTYPE_NIGHTSTICK || (m_eWeaponType >= WEAPONTYPE_BASEBALLBAT && m_eWeaponType <= WEAPONTYPE_CHAINSAW)) { CGlass::BreakGlassPhysically(fireSource, info->m_fRadius); if (m_eWeaponType == WEAPONTYPE_CHAINSAW) CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, FindPlayerPed(), FindPlayerPed(), 1000); } } int damageEntityRegistered = 0; for ( int32 i = 0; i < shooterPed->m_numNearPeds; i++ ) { CPed *victimPed = shooterPed->m_nearPeds[i]; ASSERT(victimPed!=nil); if ( (victimPed->m_nPedType != shooterPed->m_nPedType || victimPed == shooterPed->m_pSeekTarget) && victimPed != shooterPed->m_leader || !(CGeneral::GetRandomNumber() & 31) && (!shooterPed->IsGangMember() || victimPed->CanBeDamagedByThisGangMember(shooterPed)) ) { bool collided = false; if (victimPed->m_nPedState == PED_DRIVING && (m_eWeaponType == WEAPONTYPE_UNARMED || m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE || info->IsFlagSet(WEAPONFLAG_FIGHTMODE))) continue; float victimPedRadius = victimPed->GetBoundRadius() + info->m_fRadius; if ( victimPed->bUsesCollision || victimPed->Dead() || victimPed->Driving() ) { CVector victimPedPos = victimPed->GetPosition(); if ( SQR(victimPedRadius) > (victimPedPos-fireSource).MagnitudeSqr() ) { CVector collisionDist; CColModel* victimPedCol = &CTempColModels::ms_colModelPed1; bool useLocalPos = false; if (victimPed->m_nPedState == PED_FALL || victimPed->m_nPedState == PED_DIE && victimPed->bIsPedDieAnimPlaying || victimPed->m_nWaitState == WAITSTATE_SIT_IDLE || victimPed->m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) { useLocalPos = true; victimPedCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(victimPed->GetModelIndex()))->AnimatePedColModelSkinnedWorld(victimPed->GetClump()); } else if (victimPed->DyingOrDead()) { victimPedCol = &CTempColModels::ms_colModelPedGroundHit; } int32 s = 0; while ( s < victimPedCol->numSpheres ) { CColSphere *sphere = &victimPedCol->spheres[s]; if (useLocalPos) collisionDist = sphere->center - fireSource; else collisionDist = victimPedPos + sphere->center - fireSource; if ( SQR(sphere->radius + info->m_fRadius) > collisionDist.MagnitudeSqr() ) { collided = true; break; } s++; } if ( !(victimPed->IsPlayer() && victimPed->GetPedState() == PED_GETUP) ) { if ( collided ) { float victimPedHealth = victimPed->m_fHealth; CVector bloodPos = fireSource + (collisionDist*0.7f); CVector2D posOffset(shooterPed->GetPosition().x-victimPedPos.x, shooterPed->GetPosition().y-victimPedPos.y); int32 localDir = victimPed->GetLocalDirection(posOffset); bool isHeavy = m_eWeaponType >= WEAPONTYPE_GOLFCLUB && m_eWeaponType <= WEAPONTYPE_KATANA && m_eWeaponType != WEAPONTYPE_HAMMER; if (shooterPed->m_fDamageImpulse == 0.0f) { shooterPed->m_pDamageEntity = victimPed; victimPed->RegisterReference(&shooterPed->m_pDamageEntity); } damageEntityRegistered = 3; if (victimPed->bInVehicle) { CVehicle *victimVeh = victimPed->m_pMyVehicle; if (victimVeh) { if (victimVeh->IsBike()) { CBike *victimBike = (CBike*)victimVeh; victimBike->KnockOffRider(m_eWeaponType, localDir, victimPed, false); if (victimBike->pDriver) victimBike->pDriver->ReactToAttack(shooterPed); else { if (victimVeh->pPassengers[0]) victimVeh->pPassengers[0]->ReactToAttack(shooterPed); } continue; } } } if ( !victimPed->DyingOrDead() ) victimPed->ReactToAttack(shooterPed); uint8 hitLevel = HITLEVEL_HIGH; if ( isHeavy && (victimPed->OnGround() || victimPed->m_nWaitState == WAITSTATE_SUN_BATHE_IDLE)) hitLevel = HITLEVEL_GROUND; victimPed->StartFightDefend(localDir, hitLevel, 10); if ( !victimPed->DyingOrDead() ) { if ( shooterPed->IsPlayer() && isHeavy && anim2Playing ) victimPed->InflictDamage(shooterPed, m_eWeaponType, 100.0f, PEDPIECE_TORSO, localDir); else if ( shooterPed->IsPlayer() && ((CPlayerPed*)shooterPed)->m_bAdrenalineActive ) victimPed->InflictDamage(shooterPed, m_eWeaponType, 3.5f*info->m_nDamage, PEDPIECE_TORSO, localDir); else { if ( victimPed->IsPlayer() && isHeavy ) // wtf, it's not fair victimPed->InflictDamage(shooterPed, m_eWeaponType, 2.0f*info->m_nDamage, PEDPIECE_TORSO, localDir); else victimPed->InflictDamage(shooterPed, m_eWeaponType, info->m_nDamage, PEDPIECE_TORSO, localDir); } } if ( CGame::nastyGame && victimPed->GetIsOnScreen() ) { CVector dir = collisionDist * RecipSqrt(1.0f, 10.0f*collisionDist.MagnitudeSqr()); CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); if ( isHeavy ) { dir.x += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); dir.y += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); dir.x += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); dir.y += CGeneral::GetRandomNumberInRange(-0.05f, 0.05f); CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir); } if (m_eWeaponType == WEAPONTYPE_CHAINSAW) { if (victimPed->m_nPedState != PED_DEAD && !((CTimer::GetFrameCounter() + 17) & 1) || victimPed->m_nPedState == PED_DEAD && !((CTimer::GetFrameCounter() + 17) & 3)) { CParticle::AddParticle(PARTICLE_TEST, bloodPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.2f); } CVector newDir(dir); newDir.z += 0.2f; CParticle::AddParticle(PARTICLE_BLOOD_SMALL, bloodPos, newDir); CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, newDir); newDir.z = dir.z + 0.1f; CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, newDir); newDir.x = 0.0f; newDir.y = 0.0f; newDir.z = 0.01f; CParticle::AddParticle(PARTICLE_DEBRIS2, bloodPos, newDir); CVector dropDir(CGeneral::GetRandomNumberInRange(-0.15f, 0.15f), CGeneral::GetRandomNumberInRange(0.1f, 0.35f), 0.f); CVector dropPos(CGeneral::GetRandomNumberInRange(SCREEN_STRETCH_X(50.0f), SCREEN_STRETCH_FROM_RIGHT(50.0f)), CGeneral::GetRandomNumberInRange(SCREEN_STRETCH_Y(50.0f), SCREEN_STRETCH_FROM_BOTTOM(50.0f)), 1.f); CParticle::AddParticle(PARTICLE_BLOODDROP, dropPos, dropDir, nil, CGeneral::GetRandomNumberInRange(0.1f, 0.15f), CRGBA(0, 0, 0, 0), 0, 0, CGeneral::GetRandomNumber() & 1, 0); } if (info->m_AnimToPlay == ASSOCGRP_KNIFE) { dir += 0.1f * shooterPed->GetUp() + 0.05f * shooterPed->GetRight(); CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir); CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir); CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir); } } if ( !victimPed->OnGround() ) { if ( victimPed->m_fHealth > 0.0f && (victimPed->m_fHealth < 30.0f && victimPedHealth > 30.0f || (isHeavy || m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) && !victimPed->IsPlayer()) ) { posOffset.Normalise(); victimPed->bIsStanding = false; if(m_eWeaponType == WEAPONTYPE_CHAINSAW) victimPed->ApplyMoveForce(posOffset.x*-2.0f, posOffset.y*-2.0f, 2.0f); else victimPed->ApplyMoveForce(posOffset.x*-5.0f, posOffset.y*-5.0f, 3.0f); if ( isHeavy && victimPed->IsPlayer() ) victimPed->SetFall(3000, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false); else victimPed->SetFall(1500, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false); shooterPed->m_pSeekTarget = victimPed; shooterPed->m_pSeekTarget->RegisterReference(&shooterPed->m_pSeekTarget); } } else if (victimPed->Dying() && !anim2Playing) { posOffset.Normalise(); victimPed->bIsStanding = false; if(m_eWeaponType == WEAPONTYPE_CHAINSAW) victimPed->ApplyMoveForce(posOffset.x*-1.0f, posOffset.y*-1.0f, 1.0f); else victimPed->ApplyMoveForce(posOffset.x*-5.0f, posOffset.y*-5.0f, 3.0f); } m_eWeaponState = WEAPONSTATE_MELEE_MADECONTACT; if (m_eWeaponType != WEAPONTYPE_KNIFE && m_eWeaponType != WEAPONTYPE_MACHETE && m_eWeaponType != WEAPONTYPE_KATANA && m_eWeaponType != WEAPONTYPE_CHAINSAW) { if (victimPed->m_nPedType == PEDTYPE_COP) CEventList::RegisterEvent(EVENT_ASSAULT_POLICE, EVENT_ENTITY_PED, victimPed, shooterPed, 2000); else CEventList::RegisterEvent(EVENT_ASSAULT, EVENT_ENTITY_PED, victimPed, shooterPed, 2000); } else { if (victimPed->m_nPedType == PEDTYPE_COP) CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON_POLICE, EVENT_ENTITY_PED, victimPed, shooterPed, 2000); else CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON, EVENT_ENTITY_PED, victimPed, shooterPed, 2000); } } } } } } } CVehicle *nearVeh = (CVehicle*)CWorld::TestSphereAgainstWorld(fireSource, info->m_fRadius, nil, false, true, false, false, false, false); if (nearVeh && nearVeh->IsCar()) { CAutomobile *nearCar = (CAutomobile*)nearVeh; m_eWeaponState = WEAPONSTATE_MELEE_MADECONTACT; if (shooterPed == FindPlayerPed()) { if (nearCar->IsLawEnforcementVehicle()) { FindPlayerPed()->SetWantedLevelNoDrop(1); } CEventList::RegisterEvent(EVENT_ASSAULT, EVENT_ENTITY_VEHICLE, nearCar, shooterPed, 2000); } float oldHealth = nearCar->m_fHealth; if (m_eWeaponType == WEAPONTYPE_CHAINSAW) { for( int32 i=0; i<4; i++ ) { CParticle::AddParticle(PARTICLE_SPARK_SMALL, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.3f)); CParticle::AddParticle(PARTICLE_SPARK, gaTempSphereColPoints[0].point, gaTempSphereColPoints[0].normal * 0.1f); } } if (m_eWeaponType == WEAPONTYPE_CHAINSAW) { nearCar->VehicleDamage(info->m_nDamage * (0.00075f * nearCar->pHandling->fMass), gaTempSphereColPoints[0].pieceB); CParticle::AddParticle(PARTICLE_HEATHAZE, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.0f), 0, 0.0f, 0, 0, 0, 0); } else { nearCar->VehicleDamage(info->m_nDamage* (0.01f * nearCar->pHandling->fMass), gaTempSphereColPoints[0].pieceB); } if (nearCar->m_fHealth < oldHealth) { nearCar->m_nLastWeaponDamage = m_eWeaponType; nearCar->m_pLastDamageEntity = shooterPed; } if (shooterPed->m_fDamageImpulse == 0.0f) { shooterPed->m_pDamageEntity = nearCar; nearCar->RegisterReference(&shooterPed->m_pDamageEntity); } damageEntityRegistered = 2; if (FindPlayerPed()->GetWeapon() == this && nearCar->VehicleCreatedBy != MISSION_VEHICLE) { if (nearCar->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_PLOUGH_THROUGH && (CGeneral::GetRandomTrueFalse() || nearCar->AutoPilot.m_nCarMission != MISSION_CRUISE)) { int leaveCarDelay = 200; CPed *driver = nearCar->pDriver; if (driver && driver->CharCreatedBy != MISSION_CHAR) { if (driver->m_pedStats->m_temper <= driver->m_pedStats->m_fear) { driver->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); } else { driver->SetObjective(OBJECTIVE_KILL_CHAR_ON_FOOT, FindPlayerPed()); driver->m_objectiveTimer = CTimer::GetTimeInMilliseconds() + 10000; driver->m_prevObjective = OBJECTIVE_KILL_CHAR_ON_FOOT; } driver->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + 200; leaveCarDelay = 400; } for (int j = 0; j < nearCar->m_nNumPassengers; ++j) { CPed *passenger = nearCar->pPassengers[j]; if (passenger && passenger->CharCreatedBy != MISSION_CHAR) { nearCar->pPassengers[j]->SetObjective(OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE); passenger->m_leaveCarTimer = CTimer::GetTimeInMilliseconds() + leaveCarDelay; leaveCarDelay += 200; } } } else { CPed *driver = nearCar->pDriver; if (driver) { if (driver->m_objective != OBJECTIVE_LEAVE_CAR && driver->m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && driver->m_objective != OBJECTIVE_FLEE_ON_FOOT_TILL_SAFE) { if (nearCar->AutoPilot.m_nDrivingStyle != DRIVINGSTYLE_PLOUGH_THROUGH) nearCar->AutoPilot.m_nCruiseSpeed = nearCar->AutoPilot.m_nCruiseSpeed * 1.5f; nearCar->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_PLOUGH_THROUGH; } } } } } if (m_eWeaponType == WEAPONTYPE_CHAINSAW) { CEntity *nearStatic = (CObject*)CWorld::TestSphereAgainstWorld(fireSource, info->m_fRadius, nil, true, false, false, true, false, false); if (nearStatic) { for(int i=0; i < 4; i++) { CParticle::AddParticle(PARTICLE_SPARK_SMALL, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.3f), 0, 0.0f, 0, 0, 0, 0); CParticle::AddParticle(PARTICLE_SPARK, gaTempSphereColPoints[0].point, 0.1f * gaTempSphereColPoints[0].normal, 0, 0.0f, 0, 0, 0, 0); } CParticle::AddParticle(PARTICLE_HEATHAZE, gaTempSphereColPoints[0].point, CVector(0.0f, 0.0f, 0.0f), 0, 0.0f, 0, 0, 0, 0); if (!damageEntityRegistered) { m_eWeaponState = WEAPONSTATE_MELEE_MADECONTACT; if (shooterPed->m_fDamageImpulse == 0.0f) { shooterPed->m_pDamageEntity = nearStatic; nearStatic->RegisterReference(&shooterPed->m_pDamageEntity); } } if (nearStatic->IsObject() && ((CObject*)nearStatic)->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY) ((CObject*)nearStatic)->ObjectDamage(200.0f); } } return true; } bool CWeapon::FireInstantHit(CEntity *shooter, CVector *fireSource) { ASSERT(shooter!=nil); ASSERT(fireSource!=nil); CWeaponInfo *info = GetInfo(); CVector source, target; CColPoint point; CEntity *victim = nil; float heading = RADTODEG(shooter->GetForward().Heading()); float angle = DEGTORAD(heading); CVector2D ahead(-Sin(angle), Cos(angle)); ahead.Normalise(); CVector vel = ((CPed *)shooter)->m_vecMoveSpeed; int32 shooterMoving = false; if ( Abs(vel.x) > 0.0f && Abs(vel.y) > 0.0f ) shooterMoving = true; if ( shooter == FindPlayerPed() ) { static float prev_heading = 0.0f; prev_heading = ((CPed*)shooter)->m_fRotationCur; } if ( shooter->IsPed() && ((CPed *)shooter)->m_pPointGunAt ) { CPed *shooterPed = (CPed *)shooter; if ( shooterPed->m_pedIK.m_flags & CPedIK::GUN_POINTED_SUCCESSFULLY ) { int32 accuracy = shooterPed->m_wepAccuracy; int32 inaccuracy = 100-accuracy; CPed *threatAttack = (CPed*)shooterPed->m_pPointGunAt; if ( threatAttack->IsPed() ) { threatAttack->m_pedIK.GetComponentPosition(target, PED_MID); threatAttack->ReactToPointGun(shooter); } else target = threatAttack->GetPosition(); target -= *fireSource; float distToTarget = Max(target.Magnitude(), 0.01f); target *= info->m_fRange / distToTarget; target += *fireSource; if (shooter == FindPlayerPed() && inaccuracy != 0.f) { float newInaccuracy = fPlayerAimScale * FindPlayerPed()->m_fAttackButtonCounter * (inaccuracy * Min(1.f, fPlayerAimScaleDist / distToTarget)); if (FindPlayerPed()->bIsDucking) newInaccuracy *= 0.4f; target.x += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f) * newInaccuracy; target.y += CGeneral::GetRandomNumberInRange(-0.15f, 0.15f) * newInaccuracy; target.z += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * newInaccuracy; FindPlayerPed()->m_fAttackButtonCounter += info->m_nDamage * 0.04f; } else if (inaccuracy > 0.f) { if (threatAttack == FindPlayerPed()) { float speed = Min(0.33f, FindPlayerPed()->m_vecMoveSpeed.Magnitude()); inaccuracy *= (0.3f * speed * 100.f / 33.f + 0.8f); } target.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracy; target.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracy; target.z += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * inaccuracy; } if (shooter == FindPlayerPed()) CWorld::bIncludeDeadPeds = true; CWorld::bIncludeBikers = true; ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); CWorld::bIncludeDeadPeds = false; CWorld::bIncludeBikers = false; } else { target.x = info->m_fRange; target.y = 0.0f; target.z = 0.0f; shooterPed->TransformToNode(target, PED_HANDR); CWorld::bIncludeBikers = true; ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); CWorld::bIncludeBikers = false; } } else if ( shooter == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam() ) { TheCamera.Find3rdPersonCamTargetVector(info->m_fRange, *fireSource, source, target); CWorld::bIncludeBikers = true; CWorld::bIncludeDeadPeds = true; CWorld::bIncludeCarTyres = true; ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); CWorld::bIncludeBikers = false; CWorld::bIncludeDeadPeds = false; CWorld::bIncludeCarTyres = false; if (victim) CheckForShootingVehicleOccupant(&victim, &point, m_eWeaponType, source, target); int32 rotSpeed = 1; if ( m_eWeaponType == WEAPONTYPE_M4 ) rotSpeed = 4; CVector bulletPos; if ( CHeli::TestBulletCollision(&source, &target, &bulletPos, 4) ) { for ( int32 i = 0; i < 16; i++ ) CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, rotSpeed); } } else { uint32 model = shooter->GetModelIndex(); if (model == MI_HUNTER || model == MI_SEASPAR || model == MI_SPARROW) { float inaccuracyMult = 0.6f; target = shooter->GetForward(); if (shooter->GetStatus() == STATUS_PLAYER) { target *= info->m_fRange; target += *fireSource; CWeapon::DoDriveByAutoAiming(FindPlayerPed(), (CVehicle*)shooter, fireSource, &target); target -= *fireSource; target.Normalise(); if (model == MI_SEASPAR || model == MI_SPARROW) inaccuracyMult = 0.1f; else inaccuracyMult = 0.3f; } target.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracyMult; target.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f) * inaccuracyMult; target.z += CGeneral::GetRandomNumberInRange(-0.1f, 0.1f) * inaccuracyMult; target.Normalise(); target *= info->m_fRange; target += *fireSource; CWorld::pIgnoreEntity = shooter; ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, true, false); CWorld::pIgnoreEntity = nil; int32 rotSpeed = 1; if (m_eWeaponType == WEAPONTYPE_M4) rotSpeed = 4; CVector bulletPos; if (CHeli::TestBulletCollision(fireSource, &target, &bulletPos, 4)) { for (int32 i = 0; i < 16; i++) CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, rotSpeed); } } else { float shooterHeading = RADTODEG(shooter->GetForward().Heading()); float shooterAngle = DEGTORAD(shooterHeading); CVector2D rotOffset(-Sin(shooterAngle), Cos(shooterAngle)); rotOffset.Normalise(); target = *fireSource; target.x += rotOffset.x * info->m_fRange; target.y += rotOffset.y * info->m_fRange; CParticle::HandleShootableBirdsStuff(shooter, *fireSource); if (shooter->IsPed() && ((CPed*)shooter)->bDoomAim && (shooter != FindPlayerPed() || !info->IsFlagSet(WEAPONFLAG_CANAIM))) { CWeapon::DoDoomAiming(shooter, fireSource, &target); } CWorld::bIncludeBikers = true; ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); CWorld::bIncludeBikers = false; int32 rotSpeed = 1; if (m_eWeaponType == WEAPONTYPE_M4) rotSpeed = 4; CVector bulletPos; if (CHeli::TestBulletCollision(fireSource, &target, &bulletPos, 4)) { for (int32 i = 0; i < 16; i++) CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f, rotSpeed); } } } if ( shooter->IsPed() && victim) { if (victim == ((CPed*)shooter)->m_leader) return false; if (victim->IsPed() && ((CPed*)shooter)->IsGangMember() && !((CPed*)victim)->CanBeDamagedByThisGangMember((CPed*)shooter)) return false; } if (shooter->IsPed()) CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed*)shooter, 1000); else if (shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver) CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, ((CVehicle*)shooter)->pDriver, 1000); if ( shooter == FindPlayerPed() ) { if ( !(CTimer::GetFrameCounter() & 3) ) MakePedsJumpAtShot((CPhysical*)shooter, fireSource, &target); } switch ( m_eWeaponType ) { case WEAPONTYPE_M4: case WEAPONTYPE_RUGER: case WEAPONTYPE_M60: case WEAPONTYPE_MINIGUN: case WEAPONTYPE_HELICANNON: { static uint8 counter = 0; if ( info->m_nFiringRate >= 50 || !(++counter & 1) ) { AddGunFlashBigGuns(*fireSource, *fireSource + target); CVector gunshellPos = *fireSource; gunshellPos -= CVector(0.65f*ahead.x, 0.65f*ahead.y, 0.0f); CVector dir = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f)); dir.Normalise2D(); AddGunshell(shooter, gunshellPos, CVector2D(dir.x, dir.y), 0.02f); } break; } case WEAPONTYPE_UZI: case WEAPONTYPE_TEC9: case WEAPONTYPE_SILENCED_INGRAM: case WEAPONTYPE_MP5: { CPointLights::AddLight(CPointLights::LIGHT_POINT, *fireSource, CVector(0.0f, 0.0f, 0.0f), 5.0f, 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); CVector gunflashPos = *fireSource; if ( shooterMoving ) gunflashPos += CVector(1.5f*vel.x, 1.5f*vel.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.07f); gunflashPos += CVector(0.06f*ahead.x, 0.06f*ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.05f); gunflashPos += CVector(0.04f*ahead.x, 0.04f*ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); gunflashPos += CVector(0.04f*ahead.x, 0.04f*ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f); gunflashPos += CVector(0.03f*ahead.x, 0.03f*ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f); gunflashPos += CVector(0.03f*ahead.x, 0.03f*ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); gunflashPos += CVector(0.02f*ahead.x, 0.02f*ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.01f); CVector gunsmokePos = *fireSource; float rnd = CGeneral::GetRandomNumberInRange(0.05f, 0.25f); CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*rnd, ahead.y*rnd, 0.0f)); CVector gunshellPos = *fireSource; gunshellPos -= CVector(0.2f*ahead.x, 0.2f*ahead.y, 0.0f); CVector dir = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f)); dir.Normalise2D(); AddGunshell(shooter, gunshellPos, CVector2D(dir.x, dir.y), 0.015f); break; } case WEAPONTYPE_COLT45: case WEAPONTYPE_PYTHON: case WEAPONTYPE_SNIPERRIFLE: case WEAPONTYPE_LASERSCOPE: { CPointLights::AddLight(CPointLights::LIGHT_POINT, *fireSource, CVector(0.0f, 0.0f, 0.0f), 5.0f, 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); CVector gunflashPos = *fireSource; if ( shooterMoving ) gunflashPos += CVector(1.5f*vel.x, 1.5f*vel.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.06f); gunflashPos += CVector(0.06f*ahead.x, 0.06f*ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); gunflashPos += CVector(0.04f*ahead.x, 0.04f*ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); CVector gunsmokePos = *fireSource; CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*0.10f, ahead.y*0.10f, 0.0f), nil, 0.005f); CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*0.15f, ahead.y*0.15f, 0.0f), nil, 0.015f); CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x*0.20f, ahead.y*0.20f, 0.0f), nil, 0.025f); CVector gunshellPos = *fireSource; gunshellPos -= CVector(0.2f*ahead.x, 0.2f*ahead.y, 0.0f); CVector dir = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f)); dir.Normalise2D(); AddGunshell(shooter, gunshellPos, CVector2D(dir.x, dir.y), 0.015f); break; } default: break; } DoBulletImpact(shooter, victim, fireSource, &target, &point, ahead); return true; } void CWeapon::AddGunFlashBigGuns(CVector start, CVector end) { CPointLights::AddLight(CPointLights::LIGHT_POINT, start, CVector(0.0f, 0.0f, 0.0f), 5.0f, 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); CVector gunflashPos = start; CVector shootVec = end - start; // Wtf did you do there R*? shootVec.Normalise(); CVector2D ahead = shootVec; ahead.Normalise(); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.08f); gunflashPos += CVector(0.06f * ahead.x, 0.06f * ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.06f); gunflashPos += CVector(0.06f * ahead.x, 0.06f * ahead.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.06f); gunflashPos = start; gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f); gunflashPos.z += 0.04f; CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); gunflashPos.z += 0.04f; CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); gunflashPos.z += 0.03f; CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); gunflashPos = start; gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f); gunflashPos.z -= 0.04f; CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); gunflashPos.z -= 0.04f; CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); gunflashPos.z -= 0.03f; CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); CVector offset = CrossProduct(CVector(ahead.x, ahead.y, 0.0f), CVector(0.0f, 0.0f, 5.0f)); offset.Normalise2D(); gunflashPos = start; gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f); gunflashPos += CVector(0.06f * offset.x, 0.06f * offset.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); gunflashPos += CVector(0.04f * offset.x, 0.04f * offset.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f); gunflashPos += CVector(0.03f * offset.x, 0.03f * offset.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); gunflashPos = start; gunflashPos += CVector(-0.1f * ahead.x, -0.1f * ahead.y, 0.0f); gunflashPos -= CVector(0.06f * offset.x, 0.06f * offset.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.04f); gunflashPos -= CVector(0.04f * offset.x, 0.04f * offset.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.03f); gunflashPos -= CVector(0.03f * offset.x, 0.03f * offset.y, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.02f); CVector gunsmokePos = start; float rnd = CGeneral::GetRandomNumberInRange(0.05f, 0.25f); CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(ahead.x * rnd, ahead.y * rnd, 0.0f)); } void CWeapon::AddGunshell(CEntity *shooter, CVector const &source, CVector2D const &direction, float size) { ASSERT(shooter!=nil); if ( shooter == nil) return; CVector dir(direction.x*0.05f, direction.y*0.05f, CGeneral::GetRandomNumberInRange(0.02f, 0.08f)); static CVector prevEntityPosition(0.0f, 0.0f, 0.0f); CVector entityPosition = shooter->GetPosition(); CVector diff = entityPosition - prevEntityPosition; if ( Abs(diff.x)+Abs(diff.y)+Abs(diff.z) > 1.5f ) { prevEntityPosition = entityPosition; CParticle::AddParticle(PARTICLE_GUNSHELL_FIRST, source, dir, nil, size, CGeneral::GetRandomNumberInRange(-20.0f, 20.0f)); } else { CParticle::AddParticle(PARTICLE_GUNSHELL, source, dir, nil, size, CGeneral::GetRandomNumberInRange(-20.0f, 20.0f)); } } void CWeapon::DoBulletImpact(CEntity *shooter, CEntity *victim, CVector *source, CVector *target, CColPoint *point, CVector2D ahead) { ASSERT(shooter!=nil); ASSERT(source!=nil); ASSERT(target!=nil); ASSERT(point!=nil); CWeaponInfo *info = GetInfo(); if ( victim ) { if (shooter) { if (shooter && shooter->IsPed() && ((CPed*)shooter)->m_attachedTo == victim) return; if (shooter->IsPed() && !((CPed*)shooter)->IsPlayer()) { CPed* shooterPed = (CPed*)shooter; CEntity* guyWePointGun = shooterPed->m_pPointGunAt; if (guyWePointGun) { if (victim != guyWePointGun) { float distWithAim = (guyWePointGun->GetPosition() - shooter->GetPosition()).Magnitude(); float distWithBullet = (point->point - shooter->GetPosition()).Magnitude(); if (distWithAim > 0.1f && distWithBullet > 0.1f) { // Normalize CVector aimDir = (guyWePointGun->GetPosition() - shooter->GetPosition()) * (1.0f / distWithAim); CVector bulletDir = (point->point - shooter->GetPosition()) * (1.0f / distWithBullet); float dotProd = DotProduct(aimDir, bulletDir); float aimAndBulletAngle; if (dotProd <= 0.35f) aimAndBulletAngle = PI; else aimAndBulletAngle = Acos(dotProd); if (aimAndBulletAngle <= DEGTORAD(45.0f) && (aimAndBulletAngle <= DEGTORAD(15.0f) || distWithBullet / distWithAim >= 0.75f) && distWithBullet / distWithAim >= 0.99f) { shooterPed->bObstacleShowedUpDuringKillObjective = false; shooterPed->m_shotTime = 0; } else { shooterPed->bObstacleShowedUpDuringKillObjective = true; shooterPed->m_shootTimer = 0; shooterPed->m_shotTime = CTimer::GetTimeInMilliseconds(); if (distWithAim < 10.0f) shooterPed->SetAttackTimer(1500); else shooterPed->SetAttackTimer(3000); } } } } } } CGlass::WasGlassHitByBullet(victim, point->point); CVector traceTarget = point->point; CBulletTraces::AddTrace(source, &traceTarget, m_eWeaponType, shooter); if (victim->IsPed() && shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver) shooter = ((CVehicle*)shooter)->pDriver; if ( victim->IsPed() && shooter->IsPed() && (((CPed*)shooter)->m_nPedType != ((CPed*)victim)->m_nPedType || ((CPed*)shooter)->m_nPedType == PEDTYPE_PLAYER2 || !((CPed*)shooter)->IsGangMember() && ((CPed*)shooter)->m_nPedType != PEDTYPE_COP)) { CPed *victimPed = (CPed *)victim; if ( !victimPed->DyingOrDead() && victim != shooter ) { CVector pos = victimPed->GetPosition(); CVector2D posOffset(source->x-pos.x, source->y-pos.y); int32 localDir = victimPed->GetLocalDirection(posOffset); victimPed->ReactToAttack(shooter); if ( !victimPed->IsPedInControl() || victimPed->bIsDucking ) { victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point->pieceB, localDir); } else { if ( victimPed->bCanBeShotInVehicle && (IsShotgun(m_eWeaponType) || (!victimPed->IsPlayer() && (m_eWeaponType == WEAPONTYPE_HELICANNON || m_eWeaponType == WEAPONTYPE_M60 || m_eWeaponType == WEAPONTYPE_PYTHON)))) { posOffset.Normalise(); victimPed->bIsStanding = false; victimPed->ApplyMoveForce(posOffset.x*-5.0f, posOffset.y*-5.0f, 5.0f); victimPed->SetFall(1500, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false); victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point->pieceB, localDir); } else { if ( victimPed->IsPlayer() ) { CPlayerPed *victimPlayer = (CPlayerPed *)victimPed; if ( victimPlayer->m_nHitAnimDelayTimer < CTimer::GetTimeInMilliseconds() && victimPed->m_nPedState != PED_DRIVING ) { victimPed->ClearAttackByRemovingAnim(); CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir)); ASSERT(asoc!=nil); asoc->blendAmount = 0.0f; asoc->blendDelta = 8.0f; if ( m_eWeaponType == WEAPONTYPE_M4 ) victimPlayer->m_nHitAnimDelayTimer = CTimer::GetTimeInMilliseconds() + 2500; else victimPlayer->m_nHitAnimDelayTimer = CTimer::GetTimeInMilliseconds() + 1000; } } else { victimPed->ClearAttackByRemovingAnim(); CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir)); ASSERT(asoc!=nil); asoc->blendAmount = 0.0f; asoc->blendDelta = 8.0f; } victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point->pieceB, localDir); } } if ( victimPed->m_nPedType == PEDTYPE_COP ) CEventList::RegisterEvent(EVENT_SHOOT_COP, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000); else CEventList::RegisterEvent(EVENT_SHOOT_PED, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000); if ( CGame::nastyGame ) { uint8 bloodAmount = 8; if ( IsShotgun(m_eWeaponType) || m_eWeaponType == WEAPONTYPE_HELICANNON ) bloodAmount = 32; CVector dir = (point->point - victim->GetPosition()) * 0.01f; dir.z = 0.01f; if ( victimPed->GetIsOnScreen() ) { for ( uint8 i = 0; i < bloodAmount; i++ ) CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point->point, dir); } if (m_eWeaponType == WEAPONTYPE_MINIGUN) { CParticle::AddParticle(PARTICLE_TEST, point->point, CVector(0.f, 0.f, 0.f), nil, 0.f, 0, 0, 0, 0); CParticle::AddParticle(PARTICLE_TEST, point->point + CVector(0.2f, -0.2f, 0.f), CVector(0.f, 0.f, 0.f), nil, 0.f, 0, 0, 0, 0); CParticle::AddParticle(PARTICLE_TEST, point->point + CVector(-0.2f, 0.2f, 0.f), CVector(0.f, 0.f, 0.f), nil, 0.f, 0, 0, 0, 0); } } } else { if ( CGame::nastyGame ) { CVector dir = (point->point - victim->GetPosition()) * 0.01f; dir.z = 0.01f; if ( victim->GetIsOnScreen() ) { for ( int32 i = 0; i < 8; i++ ) CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point->point + CVector(0.0f, 0.0f, 0.15f), dir); } if ( victimPed->Dead() ) { CAnimBlendAssociation *asoc; if ( RpAnimBlendClumpGetFirstAssociation(victimPed->GetClump(), ASSOC_FRONTAL) ) asoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); else asoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); if ( asoc ) { asoc->SetCurrentTime(0.0f); asoc->flags |= ASSOC_RUNNING; asoc->flags &= ~ASSOC_FADEOUTWHENDONE; } } } } } else { switch ( victim->GetType() ) { case ENTITY_TYPE_BUILDING: { for ( int32 i = 0; i < 16; i++ ) CParticle::AddParticle(PARTICLE_SPARK, point->point, point->normal*0.05f); #ifndef FIX_BUGS CVector dist = point->point - (*source); float distMagnitude = dist.Magnitude(); CVector smokePos = point->point - Max(distMagnitude / 10.0f, 0.2f) * dist / distMagnitude; #else CVector smokePos = point->point; #endif // !FIX_BUGS smokePos.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); smokePos.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); smokePos.z += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f)); break; } case ENTITY_TYPE_VEHICLE: { if (point->pieceB >= CAR_PIECE_WHEEL_LF && point->pieceB <= CAR_PIECE_WHEEL_RR) { ((CVehicle*)victim)->BurstTyre(point->pieceB, true); for (int32 i = 0; i < 4; i++) CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, point->point, point->normal * 0.05f); } else { ((CVehicle*)victim)->InflictDamage(shooter, m_eWeaponType, info->m_nDamage); for (int32 i = 0; i < 16; i++) CParticle::AddParticle(PARTICLE_SPARK, point->point, point->normal * 0.05f); #ifndef FIX_BUGS CVector dist = point->point - (*source); CVector offset = dist - Max(0.2f * dist.Magnitude(), 0.5f) * CVector(ahead.x, ahead.y, 0.0f); CVector smokePos = *source + offset; #else CVector smokePos = point->point; #endif CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f)); } if ( shooter->IsPed() ) { CPed *shooterPed = (CPed *)shooter; if ( shooterPed->bNotAllowedToDuck ) { if ( shooterPed->bKindaStayInSamePlace && victim != shooterPed->m_pPointGunAt ) { shooterPed->bKindaStayInSamePlace = false; shooterPed->m_duckAndCoverTimer = CTimer::GetTimeInMilliseconds() + 15000; } } } break; } case ENTITY_TYPE_OBJECT: { for ( int32 i = 0; i < 8; i++ ) CParticle::AddParticle(PARTICLE_SPARK, point->point, point->normal*0.05f); CObject *victimObject = (CObject *)victim; if ( !victimObject->bInfiniteMass && victimObject->m_fCollisionDamageMultiplier < 99.9f) { bool notStatic = !victimObject->GetIsStatic(); if (notStatic && victimObject->m_fUprootLimit <= 0.0f) { victimObject->SetIsStatic(false); victimObject->AddToMovingList(); } notStatic = !victimObject->GetIsStatic(); if (!notStatic) { CVector moveForce = point->normal * -4.0f; victimObject->ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z); } } else if (victimObject->m_nCollisionDamageEffect >= DAMAGE_EFFECT_SMASH_COMPLETELY) { victimObject->ObjectDamage(50.f); } break; } default: break; } } switch ( victim->GetType() ) { case ENTITY_TYPE_BUILDING: { PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point->point); break; } case ENTITY_TYPE_VEHICLE: { CStats::BulletsThatHit++; DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); break; } case ENTITY_TYPE_PED: { CStats::BulletsThatHit++; DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT); break; } case ENTITY_TYPE_OBJECT: { CStats::BulletsThatHit++; PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point->point); break; } case ENTITY_TYPE_DUMMY: { PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point->point); break; } default: break; } } else CBulletTraces::AddTrace(source, target, m_eWeaponType, shooter); if ( shooter == FindPlayerPed() ) CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerPed()->GetPosition().x, FindPlayerPed()->GetPosition().y, FindPlayerPed()->GetPosition().z); BlowUpExplosiveThings(victim); } bool CWeapon::FireShotgun(CEntity *shooter, CVector *fireSource) { ASSERT(shooter!=nil); ASSERT(fireSource!=nil); CWeaponInfo *info = GetInfo(); float heading = RADTODEG(shooter->GetForward().Heading()); float angle = DEGTORAD(heading); CVector2D rotOffset(-Sin(angle), Cos(angle)); rotOffset.Normalise(); CVector gunflashPos = *fireSource; gunflashPos += CVector(rotOffset.x*0.1f, rotOffset.y*0.1f, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f); gunflashPos += CVector(rotOffset.x*0.1f, rotOffset.y*0.1f, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.15f); gunflashPos += CVector(rotOffset.x*0.1f, rotOffset.y*0.1f, 0.0f); CParticle::AddParticle(PARTICLE_GUNFLASH, gunflashPos, CVector(0.0f, 0.0f, 0.0f), nil, 0.2f); CParticle::AddParticle(PARTICLE_GUNFLASH, *fireSource, CVector(0.0f, 0.0f, 0.0f), nil, 0.0f); CVector gunsmokePos = *fireSource; CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.10f, rotOffset.y*0.10f, 0.0f), nil, 0.1f); CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.15f, rotOffset.y*0.15f, 0.0f), nil, 0.1f); CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.20f, rotOffset.y*0.20f, 0.0f), nil, 0.1f); CParticle::AddParticle(PARTICLE_GUNSMOKE2, gunsmokePos, CVector(rotOffset.x*0.25f, rotOffset.y*0.25f, 0.0f), nil, 0.1f); CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed*)shooter, 1000); CPointLights::AddLight(CPointLights::LIGHT_POINT, *fireSource, CVector(0.0, 0.0, 0.0), 5.0f, 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); float shooterAngle; if ( shooter->IsPed() && ((CPed*)shooter)->m_pPointGunAt != nil ) { CEntity *threatAttack = ((CPed*)shooter)->m_pPointGunAt; shooterAngle = CGeneral::GetAngleBetweenPoints(threatAttack->GetPosition().x, threatAttack->GetPosition().y, (*fireSource).x, (*fireSource).y); } else shooterAngle = RADTODEG(shooter->GetForward().Heading()); int shootsAtOnce; int checkObstacleOnShootNo; float angleRange; switch (m_eWeaponType) { case WEAPONTYPE_SHOTGUN: angleRange = DEGTORAD(9.0f); checkObstacleOnShootNo = 1; shootsAtOnce = 3; break; case WEAPONTYPE_SPAS12_SHOTGUN: angleRange = DEGTORAD(6.0f); checkObstacleOnShootNo = 1; shootsAtOnce = 3; break; case WEAPONTYPE_STUBBY_SHOTGUN: angleRange = DEGTORAD(18.0f); checkObstacleOnShootNo = 2; shootsAtOnce = 5; break; default: break; } bool statUpdated = false; float halfAngleRange = angleRange / 2.f; float angleBetweenTwoShot = angleRange / (shootsAtOnce - 1.f); for ( int32 i = 0; i < shootsAtOnce; i++ ) { float shootAngle = DEGTORAD(RADTODEG(halfAngleRange - angleBetweenTwoShot * i) + shooterAngle); CVector2D shootRot(-Sin(shootAngle), Cos(shootAngle)); shootRot.Normalise(); CVector source, target; CColPoint point; CEntity *victim; if ( shooter == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam() ) { TheCamera.Find3rdPersonCamTargetVector(1.0f, *fireSource, source, target); CVector Left = CrossProduct(TheCamera.Cams[TheCamera.ActiveCam].Front, TheCamera.Cams[TheCamera.ActiveCam].Up); float f = (i - (shootsAtOnce / 2)) * angleBetweenTwoShot; target = f * Left + target - source; target *= info->m_fRange; target += source; CWorld::bIncludeCarTyres = true; CWorld::bIncludeBikers = true; CWorld::bIncludeDeadPeds = true; ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); CWorld::bIncludeDeadPeds = false; CWorld::bIncludeCarTyres = false; } else { target = *fireSource; target.x += shootRot.x * info->m_fRange; target.y += shootRot.y * info->m_fRange; if ( shooter->IsPed() ) { CPed *shooterPed = (CPed *)shooter; if ( shooterPed->m_pPointGunAt == nil ) DoDoomAiming(shooter, fireSource, &target); else { CVector pos; if (shooterPed->m_pPointGunAt->IsPed()) { ((CPed*)shooterPed->m_pPointGunAt)->m_pedIK.GetComponentPosition(pos, PED_MID); } else { pos = ((CPed*)shooterPed->m_pPointGunAt)->GetPosition(); } float distToTarget = (pos - (*fireSource)).Magnitude2D(); target.z += info->m_fRange / distToTarget * (pos.z - target.z); } } if (shooter == FindPlayerPed()) CWorld::bIncludeDeadPeds = true; CWorld::bIncludeBikers = true; ProcessLineOfSight(*fireSource, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, false, false); CWorld::bIncludeDeadPeds = false; } CWorld::bIncludeBikers = false; if ( victim ) { CGlass::WasGlassHitByBullet(victim, point.point); CWeapon::BlowUpExplosiveThings(victim); if (i == checkObstacleOnShootNo) { if (shooter) { if (shooter->IsPed() && !((CPed*)shooter)->IsPlayer()) { CPed *shooterPed = (CPed*)shooter; CEntity *guyWePointGun = shooterPed->m_pPointGunAt; if (guyWePointGun) { if (victim != guyWePointGun) { float distWithAim = (guyWePointGun->GetPosition() - shooter->GetPosition()).Magnitude(); float distWithBullet = (point.point - shooter->GetPosition()).Magnitude(); if (distWithAim > 0.1f && distWithBullet > 0.1f) { // Normalize CVector aimDir = (guyWePointGun->GetPosition() - shooter->GetPosition()) * (1.0f / distWithAim); CVector bulletDir = (point.point - shooter->GetPosition()) * (1.0f / distWithBullet); float dotProd = DotProduct(aimDir, bulletDir); float aimAndBulletAngle; if (dotProd <= 0.35f) aimAndBulletAngle = PI; else aimAndBulletAngle = Acos(dotProd); if (aimAndBulletAngle <= DEGTORAD(45.0f) && (aimAndBulletAngle <= DEGTORAD(15.0f) || distWithBullet / distWithAim >= 0.75f) && distWithBullet / distWithAim >= 0.99f) { shooterPed->bObstacleShowedUpDuringKillObjective = false; shooterPed->m_shotTime = 0; } else { shooterPed->bObstacleShowedUpDuringKillObjective = true; shooterPed->m_shootTimer = 0; shooterPed->m_shotTime = CTimer::GetTimeInMilliseconds(); if (distWithAim < 10.0f) shooterPed->SetAttackTimer(1500); else shooterPed->SetAttackTimer(3000); } } } } } } } CBulletTraces::AddTrace(fireSource, &point.point, m_eWeaponType, shooter); if ( victim->IsPed() ) { CPed *victimPed = (CPed *)victim; if ( !victimPed->DyingOrDead() && victim != shooter ) { bool cantStandup = true; CVector pos = victimPed->GetPosition(); CVector2D posOffset((*fireSource).x-pos.x, (*fireSource).y-pos.y); int32 localDir = victimPed->GetLocalDirection(posOffset); victimPed->ReactToAttack(FindPlayerPed()); posOffset.Normalise(); if ( victimPed->m_getUpTimer > (CTimer::GetTimeInMilliseconds() - 3000) || !victimPed->bCanBeShotInVehicle) cantStandup = false; if ( victimPed->bIsStanding && cantStandup ) { victimPed->bIsStanding = false; victimPed->ApplyMoveForce(posOffset.x*-6.0f, posOffset.y*-6.0f, 5.0f); } else victimPed->ApplyMoveForce(posOffset.x*-2.0f, posOffset.y*-2.0f, 0.0f); if ( cantStandup ) victimPed->SetFall(1500, AnimationId(ANIM_STD_HIGHIMPACT_FRONT + localDir), false); victimPed->InflictDamage(shooter, m_eWeaponType, info->m_nDamage, (ePedPieceTypes)point.pieceB, localDir); if ( victimPed->m_nPedType == PEDTYPE_COP ) CEventList::RegisterEvent(EVENT_SHOOT_COP, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000); else CEventList::RegisterEvent(EVENT_SHOOT_PED, EVENT_ENTITY_PED, victim, (CPed*)shooter, 10000); if ( CGame::nastyGame ) { uint8 bloodAmount = 8; if ( IsShotgun(m_eWeaponType) ) bloodAmount = 32; CVector dir = (point.point - victim->GetPosition()) * 0.01f; dir.z = 0.01f; if ( victimPed->GetIsOnScreen() ) { for ( uint8 i = 0; i < bloodAmount; i++ ) CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point, dir); } } } else { if (CGame::nastyGame) { CVector dir = (point.point - victim->GetPosition()) * 0.01f; dir.z = 0.01f; if (victimPed->GetIsOnScreen()) { for (uint8 i = 0; i < 8; i++) CParticle::AddParticle(PARTICLE_BLOOD_SMALL, point.point + CVector(0.0f, 0.0f, 0.15f), dir); } if (victimPed->Dead()) { CAnimBlendAssociation *hitAssoc; if (RpAnimBlendClumpGetFirstAssociation(victimPed->GetClump(), ASSOC_FRONTAL)) { hitAssoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); } else { hitAssoc = CAnimManager::BlendAnimation(victimPed->GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); } if (hitAssoc) { hitAssoc->SetCurrentTime(0.0f); hitAssoc->SetRun(); hitAssoc->flags &= ~ASSOC_DELETEFADEDOUT; } } } } } else { switch ( victim->GetType() ) { case ENTITY_TYPE_VEHICLE: { if (point.pieceB >= CAR_PIECE_WHEEL_LF && point.pieceB <= CAR_PIECE_WHEEL_RR) { ((CVehicle*)victim)->BurstTyre(point.pieceB, true); for (int32 i = 0; i < 4; i++) CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, point.point, point.normal * 0.05f); } else { ((CVehicle*)victim)->InflictDamage(shooter, m_eWeaponType, info->m_nDamage); for (int32 i = 0; i < 16; i++) CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal * 0.05f); #ifndef FIX_BUGS CVector dist = point.point - (*fireSource); CVector offset = dist - Max(0.2f * dist.Magnitude(), 2.0f) * CVector(shootRot.x, shootRot.y, 0.0f); CVector smokePos = *fireSource + offset; #else CVector smokePos = point.point; #endif CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f)); } break; } case ENTITY_TYPE_BUILDING: case ENTITY_TYPE_OBJECT: { for ( int32 i = 0; i < 16; i++ ) CParticle::AddParticle(PARTICLE_SPARK, point.point, point.normal*0.05f); #ifndef FIX_BUGS CVector dist = point.point - (*fireSource); CVector offset = dist - Max(0.2f*dist.Magnitude(), 2.0f) * CVector(shootRot.x, shootRot.y, 0.0f); CVector smokePos = *fireSource + offset; #else CVector smokePos = point.point; #endif smokePos.x += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); smokePos.y += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); smokePos.z += CGeneral::GetRandomNumberInRange(-0.2f, 0.2f); CParticle::AddParticle(PARTICLE_BULLETHIT_SMOKE, smokePos, CVector(0.0f, 0.0f, 0.0f)); if ( victim->IsObject() ) { CObject *victimObject = (CObject *)victim; if ( !victimObject->bInfiniteMass ) { bool notStatic = !victimObject->GetIsStatic(); if ( notStatic && victimObject->m_fUprootLimit <= 0.0f ) { victimObject->SetIsStatic(false); victimObject->AddToMovingList(); } notStatic = !victimObject->GetIsStatic(); if ( !notStatic ) { CVector moveForce = point.normal*-5.0f; victimObject->ApplyMoveForce(moveForce.x, moveForce.y, moveForce.z); } } } break; } default: break; } } switch ( victim->GetType() ) { case ENTITY_TYPE_BUILDING: { PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point.point); break; } case ENTITY_TYPE_VEHICLE: { if (!statUpdated) { CStats::BulletsThatHit++; statUpdated = true; } DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); break; } case ENTITY_TYPE_PED: { if (!statUpdated) { CStats::BulletsThatHit++; statUpdated = true; } DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT); break; } case ENTITY_TYPE_OBJECT: { if (!statUpdated) { CStats::BulletsThatHit++; statUpdated = true; } PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point.point); break; } case ENTITY_TYPE_DUMMY: { PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point.point); break; } default: break; } } else { CVector traceTarget = *fireSource; traceTarget += (target - (*fireSource)) * Min(info->m_fRange, 30.0f) / info->m_fRange; CBulletTraces::AddTrace(fireSource, &traceTarget, m_eWeaponType, shooter); } } if ( shooter == FindPlayerPed() ) CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerPed()->GetPosition().x, FindPlayerPed()->GetPosition().y, FindPlayerPed()->GetPosition().z); return true; } bool CWeapon::FireProjectile(CEntity *shooter, CVector *fireSource, float power) { ASSERT(shooter!=nil); ASSERT(fireSource!=nil); CVector source, target; eWeaponType projectileType = m_eWeaponType; if ( m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER ) { source = *fireSource; projectileType = WEAPONTYPE_ROCKET; if ( shooter->IsPed() && ((CPed*)shooter)->IsPlayer() ) { int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if (!( mode == CCam::MODE_M16_1STPERSON || mode == CCam::MODE_SNIPER || mode == CCam::MODE_ROCKETLAUNCHER || mode == CCam::MODE_M16_1STPERSON_RUNABOUT || mode == CCam::MODE_SNIPER_RUNABOUT || mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT) ) { return false; } *fireSource += TheCamera.Cams[TheCamera.ActiveCam].Front; } else *fireSource += shooter->GetForward(); target = *fireSource; } else { float dot = DotProduct(*fireSource-shooter->GetPosition(), shooter->GetForward()); if ( dot < 0.3f ) *fireSource += (0.3f-dot) * shooter->GetForward(); target = *fireSource; if ( target.z - shooter->GetPosition().z > 0.0f ) target += 0.6f*shooter->GetForward(); source = *fireSource - shooter->GetPosition(); source = *fireSource - DotProduct(source, shooter->GetForward()) * shooter->GetForward(); } if ( !CWorld::GetIsLineOfSightClear(source, target, true, true, false, true, false, false, false) ) { if ( m_eWeaponType != WEAPONTYPE_GRENADE ) CProjectileInfo::RemoveNotAdd(shooter, projectileType, *fireSource); else { if ( shooter->IsPed() ) { source = shooter->GetPosition() - shooter->GetForward(); source.z -= 0.4f; if ( !CWorld::TestSphereAgainstWorld(source, 0.5f, nil, false, false, true, false, false, false) ) CProjectileInfo::AddProjectile(shooter, WEAPONTYPE_GRENADE, source, 0.0f); else CProjectileInfo::RemoveNotAdd(shooter, WEAPONTYPE_GRENADE, *fireSource); } } } else CProjectileInfo::AddProjectile(shooter, projectileType, *fireSource, power); CWorld::pIgnoreEntity = nil; if ( shooter->IsPed() ) CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed *)shooter, 1000); else if ( shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver ) CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, ((CVehicle*)shooter)->pDriver, 1000); return true; } void CWeapon::GenerateFlameThrowerParticles(CVector pos, CVector dir) { dir *= 0.7f; CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); dir *= 0.7f; CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); dir *= 0.7f; CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); dir *= 0.7f; CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); dir *= 0.7f; CParticle::AddParticle(PARTICLE_FIREBALL, pos, dir); } bool CWeapon::FireAreaEffect(CEntity *shooter, CVector *fireSource) { ASSERT(shooter!=nil); ASSERT(fireSource!=nil); CWeaponInfo *info = GetInfo(); float heading = RADTODEG(shooter->GetForward().Heading()); CVector source; CVector target; CVector dir; if ( shooter == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam() ) { TheCamera.Find3rdPersonCamTargetVector(info->m_fRange, *fireSource, source, target); float norm = (1.0f / info->m_fRange); dir = (target - source) * norm; } else { float angle = DEGTORAD(heading); dir = CVector(-Sin(angle)*0.5f, Cos(angle)*0.5f, 0.0f); target = *fireSource + dir; } CShotInfo::AddShot(shooter, m_eWeaponType, *fireSource, target); CWeapon::GenerateFlameThrowerParticles(*fireSource, dir); if ( shooter == (CEntity *)FindPlayerPed() ) { for ( int32 i = 0; i < FindPlayerPed()->m_numNearPeds; i++ ) { if ( FindPlayerPed()->m_nearPeds[i]->CharCreatedBy == RANDOM_CHAR ) { if ( FindPlayerPed()->m_nearPeds[i]->IsPedInControl() && FindPlayerPed()->m_nearPeds[i]->m_nPedState != PED_FLEE_ENTITY ) FindPlayerPed()->m_nearPeds[i]->SetFlee(shooter, 10000); } } } return true; } bool CWeapon::LaserScopeDot(CVector *pOutPos, float *pOutSize) { CWeaponInfo *info = GetInfo(); float range = info->m_fRange; CVector source, target; CEntity *foundEnt = nil; CColPoint foundCol; source = 0.5f * TheCamera.Cams[TheCamera.ActiveCam].Front + TheCamera.Cams[TheCamera.ActiveCam].Source; target = TheCamera.Cams[TheCamera.ActiveCam].Front; target.Normalise(); target *= range; target += source; if ( CWorld::ProcessLineOfSight(source, target, foundCol, foundEnt, true, true, true, true, false, false, false) ) { CVector pos = foundCol.point; float w, h; if ( CSprite::CalcScreenCoors(foundCol.point, &pos, &w, &h, true) ) { *pOutPos = pos; *pOutSize = w * 0.05f; CCoronas::RegisterCorona((uintptr)this + 7, 128, 0, 0, 255, pos, 1.2f, 50.0f, CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); return true; } } return false; } bool CWeapon::FireSniper(CEntity *shooter) { ASSERT(shooter!=nil); if ( (CEntity *)FindPlayerPed() == shooter ) { int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if (!( mode == CCam::MODE_M16_1STPERSON || mode == CCam::MODE_SNIPER || mode == CCam::MODE_CAMERA || mode == CCam::MODE_ROCKETLAUNCHER || mode == CCam::MODE_M16_1STPERSON_RUNABOUT || mode == CCam::MODE_SNIPER_RUNABOUT || mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT) ) { return false; } } #ifdef SECUROM if (sniperPirateCheck){ // if not pirated game // sniperPirateCheck = 0; } #endif #ifndef FIX_BUGS CWeaponInfo *info = GetInfo(); //unused #endif CCam *cam = &TheCamera.Cams[TheCamera.ActiveCam]; ASSERT(cam!=nil); CVector source = cam->Source; CVector dir = cam->Front; if ( DotProduct(dir, CVector(0.0f, -0.9894f, 0.145f)) > 0.997f ) CCoronas::MoonSize = (CCoronas::MoonSize+1) & 7; dir.Normalise(); dir *= 16.0f; #ifdef SECUROM if (sniperPirateCheck) return true; #endif CBulletInfo::AddBullet(shooter, m_eWeaponType, source, dir); if ( shooter == FindPlayerPed() ) { CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerPed()->GetPosition().x, FindPlayerPed()->GetPosition().y, FindPlayerPed()->GetPosition().z); CParticle::HandleShootableBirdsStuff(shooter, source); CamShakeNoPos(&TheCamera, 0.2f); } if ( shooter->IsPed() ) CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, shooter, (CPed*)shooter, 1000); else if ( shooter->IsVehicle() && ((CVehicle*)shooter)->pDriver ) CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, ((CVehicle*)shooter)->pDriver, 1000); return true; } bool CWeapon::TakePhotograph(CEntity *shooter) { if ( TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_CAMERA ) { CSpecialFX::bSnapShotActive = true; CSpecialFX::SnapShotFrames = 0; CStats::PhotosTaken++; bPhotographHasBeenTaken = true; for ( int32 i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--) { CPed *ped = CPools::GetPedPool()->GetSlot(i); if ( ped ) { if ( (ped->GetPosition() - TheCamera.GetPosition()).Magnitude() < 125.0f ) { CVector pedPos = ped->GetPosition(); pedPos.z += 0.8f; CVector pos; float w, h; if ( CSprite::CalcScreenCoors(pedPos, &pos, &w, &h, false) ) { if ( SCREEN_WIDTH * 0.1f < pos.x && SCREEN_WIDTH * 0.9f > pos.x && SCREEN_HEIGHT * 0.1f < pos.y && SCREEN_HEIGHT * 0.9f > pos.y ) { CVector source, target; CEntity *foundEnt = nil; CColPoint foundCol; target = pedPos; source = TheCamera.GetForward() * 2.0f + TheCamera.GetPosition(); if ( CWorld::ProcessLineOfSight(source, target, foundCol, foundEnt, true, true, true, true, true, true, false) ) { if ( foundEnt != (CEntity*)ped ) continue; } ped->bHasBeenPhotographed = true; } } } } } return true; } return false; } bool CWeapon::FireM16_1stPerson(CEntity *shooter) { ASSERT(shooter!=nil); int16 mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if (!( mode == CCam::MODE_M16_1STPERSON || mode == CCam::MODE_SNIPER || mode == CCam::MODE_CAMERA || mode == CCam::MODE_ROCKETLAUNCHER || mode == CCam::MODE_M16_1STPERSON_RUNABOUT || mode == CCam::MODE_SNIPER_RUNABOUT || mode == CCam::MODE_ROCKETLAUNCHER_RUNABOUT || mode == CCam::MODE_HELICANNON_1STPERSON) ) { return false; } CWeaponInfo *info = GetInfo(); CWorld::bIncludeCarTyres = true; CWorld::bIncludeBikers = true; CColPoint point; CEntity *victim; CWorld::pIgnoreEntity = shooter; CWorld::bIncludeDeadPeds = true; CCam *cam = &TheCamera.Cams[TheCamera.ActiveCam]; ASSERT(cam!=nil); CVector source = cam->Source; CVector target = cam->Front*info->m_fRange + source; if (ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, true, false)) { CheckForShootingVehicleOccupant(&victim, &point, m_eWeaponType, source, target); } CWorld::pIgnoreEntity = nil; CWorld::bIncludeDeadPeds = false; CWorld::bIncludeBikers = false; CWorld::bIncludeCarTyres = false; CVector2D front(cam->Front.x, cam->Front.y); front.Normalise(); DoBulletImpact(shooter, victim, &source, &target, &point, front); CVector bulletPos; if ( CHeli::TestBulletCollision(&source, &target, &bulletPos, (m_eWeaponType == WEAPONTYPE_M60 || m_eWeaponType == WEAPONTYPE_HELICANNON ? 20 : 4)) ) { for ( int32 i = 0; i < 16; i++ ) CParticle::AddParticle(PARTICLE_SPARK, bulletPos, CVector(0.0f, 0.0f, 0.0f)); } if ( shooter == FindPlayerPed() ) { float mult; switch (m_eWeaponType) { case WEAPONTYPE_M4: mult = 0.0003f; break; case WEAPONTYPE_RUGER: mult = 0.00015f; break; case WEAPONTYPE_HELICANNON: case WEAPONTYPE_M60: mult = 0.0003f; break; default: mult = 0.0002f; break; } if (FindPlayerPed()->bIsDucking || FindPlayerPed()->m_attachedTo) mult *= 0.3f; TheCamera.Cams[TheCamera.ActiveCam].Beta += float((CGeneral::GetRandomNumber() & 127) - 64) * mult; TheCamera.Cams[TheCamera.ActiveCam].Alpha += float((CGeneral::GetRandomNumber() & 127) - 64) * mult; // yes, double double notFiringRate = (20.0 - info->m_nFiringRate) / 80.0; double raisedNotFiringRate = Max(1.0, Max(0.0, notFiringRate)); uint8 shakeFreq = 80.0 * raisedNotFiringRate + 130.0; CPad::GetPad(0)->StartShake(20000.0f * CTimer::GetTimeStep() / shakeFreq, shakeFreq); } return true; } bool CWeapon::FireInstantHitFromCar(CVehicle *shooter, bool left, bool right) { CWeaponInfo *info = GetInfo(); CVehicleModelInfo *modelInfo = shooter->GetModelInfo(); CVector source, target; if ( shooter->IsBike() ) { if ( shooter->pDriver ) { source = info->m_vecFireOffset; shooter->pDriver->TransformToNode(source, PED_HANDR); source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; if ( left ) target = source - info->m_fRange * shooter->GetRight(); else if ( right ) target = source + info->m_fRange * shooter->GetRight(); else target = source + info->m_fRange * shooter->GetForward(); } else if ( left ) { source = shooter->GetMatrix() * CVector(-shooter->GetColModel()->boundingBox.max.x + -0.25f, float(CGeneral::GetRandomNumber() & 255) * 0.001f + modelInfo->GetFrontSeatPosn().y - 0.05f, modelInfo->GetFrontSeatPosn().z + 0.63f); source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; target = shooter->GetMatrix() * CVector(-info->m_fRange, modelInfo->GetFrontSeatPosn().y, modelInfo->GetFrontSeatPosn().z + 0.6f); } else if ( right ) { source = shooter->GetMatrix() * CVector(shooter->GetColModel()->boundingBox.max.x + 0.25f, float(CGeneral::GetRandomNumber() & 255) * 0.001f + modelInfo->GetFrontSeatPosn().y - 0.18f, modelInfo->GetFrontSeatPosn().z + 0.52f); source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; target = shooter->GetMatrix() * CVector(info->m_fRange, modelInfo->GetFrontSeatPosn().y, modelInfo->GetFrontSeatPosn().z + 0.5f); } else { source = shooter->GetMatrix() * CVector(float(CGeneral::GetRandomNumber() & 255) * 0.001f + -0.4f, modelInfo->GetFrontSeatPosn().y + shooter->GetColModel()->boundingBox.max.y + 0.2f, modelInfo->GetFrontSeatPosn().z + 0.55f); source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; target = shooter->GetMatrix() * CVector(0.0f, info->m_fRange, modelInfo->GetFrontSeatPosn().z + 0.5f); } } else { if ( left ) source = info->m_vecFireOffset; else { source = 1.8f * info->m_vecFireOffset; source.z -= 0.1f; } shooter->pDriver->TransformToNode(source, PED_HANDR); source += CTimer::GetTimeStep() * shooter->m_vecMoveSpeed; if ( left ) target = source - info->m_fRange * shooter->GetRight(); else target = source + info->m_fRange * shooter->GetRight(); } target += CVector(float(CGeneral::GetRandomNumber()&255)*0.01f-1.28f, float(CGeneral::GetRandomNumber()&255)*0.01f-1.28f, float(CGeneral::GetRandomNumber()&255)*0.01f-1.28f); DoDriveByAutoAiming(FindPlayerPed(), shooter, &source, &target); CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_PED, FindPlayerPed(), FindPlayerPed(), 1000); if ( !TheCamera.GetLookingLRBFirstPerson() ) { if ( !shooter->IsBike() ) CParticle::AddParticle(PARTICLE_GUNFLASH, source, CVector(0.0f, 0.0f, 0.0f)); else CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, source, 1.4f*shooter->m_vecMoveSpeed); } else CParticle::AddParticle(PARTICLE_GUNFLASH_NOANIM, source, 1.6f*shooter->m_vecMoveSpeed, nil, 0.18f); CEventList::RegisterEvent(EVENT_GUNSHOT, EVENT_ENTITY_VEHICLE, shooter, FindPlayerPed(), 1000); CPointLights::AddLight(CPointLights::LIGHT_POINT, source, CVector(0.0f, 0.0f, 0.0f), 5.0f, 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); CColPoint point; CEntity *victim; CWorld::bIncludeBikers = true; CWorld::pIgnoreEntity = shooter; ProcessLineOfSight(source, target, point, victim, m_eWeaponType, shooter, true, true, true, true, true, true, false); CWorld::pIgnoreEntity = NULL; CWorld::bIncludeBikers = false; if ( !(CTimer::GetFrameCounter() & 3) ) MakePedsJumpAtShot(shooter, &source, &target); if ( victim ) { CVector traceTarget = point.point; CBulletTraces::AddTrace(&source, &traceTarget, m_eWeaponType, shooter); if ( victim->IsPed() ) { CPed *victimPed = (CPed*)victim; if ( !victimPed->DyingOrDead() && victim != (CEntity *)shooter ) { CVector pos = victimPed->GetPosition(); CVector2D posOffset(source.x-pos.x, source.y-pos.y); int32 localDir = victimPed->GetLocalDirection(posOffset); victimPed->ReactToAttack(FindPlayerPed()); victimPed->ClearAttackByRemovingAnim(); CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir)); ASSERT(asoc!=nil); asoc->blendAmount = 0.0f; asoc->blendDelta = 8.0f; victimPed->InflictDamage(shooter, WEAPONTYPE_UZI_DRIVEBY, 3*info->m_nDamage, (ePedPieceTypes)point.pieceB, localDir); pos.z += 0.8f; if ( victimPed->GetIsOnScreen() ) { if ( CGame::nastyGame ) { for ( int32 i = 0; i < 4; i++ ) { CVector dir; dir.x = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); dir.y = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); dir.z = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); CParticle::AddParticle(PARTICLE_BLOOD, pos, dir); } } } if ( victimPed->m_nPedType == PEDTYPE_COP ) CEventList::RegisterEvent(EVENT_SHOOT_COP, EVENT_ENTITY_PED, victimPed, FindPlayerPed(), 10000); else CEventList::RegisterEvent(EVENT_SHOOT_PED, EVENT_ENTITY_PED, victimPed, FindPlayerPed(), 10000); } } else if ( victim->IsVehicle() ) ((CVehicle *)victim)->InflictDamage(FindPlayerPed(), WEAPONTYPE_UZI_DRIVEBY, info->m_nDamage); else CGlass::WasGlassHitByBullet(victim, point.point); switch ( victim->GetType() ) { case ENTITY_TYPE_BUILDING: { PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point.point); break; } case ENTITY_TYPE_VEHICLE: { DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); break; } case ENTITY_TYPE_PED: { DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT); break; } case ENTITY_TYPE_OBJECT: { PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point.point); break; } case ENTITY_TYPE_DUMMY: { PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point.point); break; } default: break; } } else { float norm = 30.0f/info->m_fRange; CVector traceTarget = (target-source)*norm + source; CBulletTraces::AddTrace(&source, &traceTarget, m_eWeaponType, shooter); } if ( shooter == FindPlayerVehicle() ) CPad::GetPad(0)->StartShake_Distance(240, 128, FindPlayerVehicle()->GetPosition().x, FindPlayerVehicle()->GetPosition().y, FindPlayerVehicle()->GetPosition().z); return true; } void CWeapon::DoDoomAiming(CEntity *shooter, CVector *source, CVector *target) { ASSERT(shooter!=nil); ASSERT(source!=nil); ASSERT(target !=nil); #ifndef FIX_BUGS CEntity entity; // unused #endif CPed *shooterPed = (CPed*)shooter; int16 lastEntity; CEntity *entities[16]; CWorld::FindObjectsInRange(*source, (*target-*source).Magnitude(), true, &lastEntity, 15, entities, false, true, true, false, false); float closestEntityDist = 10000.0f; int16 closestEntity; for ( int32 i = 0; i < lastEntity; i++ ) { CEntity *victim = entities[i]; ASSERT(victim!=nil); if ( (CEntity*)shooterPed != victim && shooterPed->CanSeeEntity(victim, DEGTORAD(22.5f)) ) { if ( !(victim->GetStatus() == STATUS_TRAIN_MOVING || victim->GetStatus() == STATUS_TRAIN_NOT_MOVING || victim->GetStatus() == STATUS_HELI || victim->GetStatus() == STATUS_PLANE || victim->GetStatus() == STATUS_WRECKED) ) { float distToVictim = (shooterPed->GetPosition()-victim->GetPosition()).Magnitude2D(); float distToVictimZ = Abs(shooterPed->GetPosition().z-victim->GetPosition().z); if ( 1.5f*distToVictimZ < distToVictim ) { float entityDist = Sqrt(SQR(distToVictim) + SQR(distToVictimZ)); if ( entityDist < closestEntityDist ) { closestEntityDist = entityDist; closestEntity = i; } } } } } CColPoint foundCol; CEntity *foundEnt; if (closestEntityDist < DOOMAUTOAIMING_MAXDIST && !CWorld::ProcessLineOfSight(*source, entities[closestEntity]->GetPosition(), foundCol, foundEnt, true, false, false, false, false, false, false, true)) { CEntity *victim = entities[closestEntity]; ASSERT(victim !=nil); float distToTarget = (*target - *source).Magnitude2D(); float distToSource = (victim->GetPosition() - *source).Magnitude2D(); float victimZ = victim->GetPosition().z + 0.3f; if ( victim->IsPed() ) { if ( ((CPed*)victim)->bIsDucking ) victimZ -= 0.8f; } (*target).z = (distToTarget / distToSource) * (victimZ - (*source).z) + (*source).z; } } void CWeapon::DoTankDoomAiming(CEntity *shooter, CEntity *driver, CVector *source, CVector *target) { ASSERT(shooter!=nil); ASSERT(driver!=nil); ASSERT(source!=nil); ASSERT(target!=nil); #ifndef FIX_BUGS CEntity entity; // unused #endif int16 lastEntity; CEntity *entities[16]; CWorld::FindObjectsInRange(*source, (*target-*source).Magnitude(), true, &lastEntity, 15, entities, false, true, false, false, false); float closestEntityDist = 10000.0f; int16 closestEntity; float normZ = (target->z - source->z) / (*target-*source).Magnitude(); for ( int32 i = 0; i < lastEntity; i++ ) { CEntity *victim = entities[i]; ASSERT(victim!=nil); if ( shooter != victim && driver != victim ) { if ( !(victim->GetStatus() == STATUS_TRAIN_MOVING || victim->GetStatus() == STATUS_TRAIN_NOT_MOVING || victim->GetStatus() == STATUS_HELI || victim->GetStatus() == STATUS_PLANE) ) { if ( !(victim->IsVehicle() && victim->bRenderScorched) ) { float distToVictim = (shooter->GetPosition()-victim->GetPosition()).Magnitude2D(); float distToVictimZ = Abs(shooter->GetPosition().z - (distToVictim*normZ + victim->GetPosition().z)); if ( 3.0f*distToVictimZ < distToVictim ) { CVector tmp = CVector(victim->GetPosition().x, victim->GetPosition().y, 0.0f); if ( CCollision::DistToLine(source, target, &tmp) < victim->GetBoundRadius()*3.0f ) { float vehicleDist = Sqrt(SQR(distToVictim) + SQR(distToVictimZ)); if ( vehicleDist < closestEntityDist ) { closestEntityDist = vehicleDist; closestEntity = i; } } } } } } } if ( closestEntityDist < DOOMAUTOAIMING_MAXDIST ) { CEntity *victim = entities[closestEntity]; ASSERT(victim!=nil); float distToTarget = (*target - *source).Magnitude2D(); float distToSource = (victim->GetPosition() - *source).Magnitude2D(); (*target).z = (distToTarget / distToSource) * (0.3f + victim->GetPosition().z - (*source).z) + (*source).z; } } void CWeapon::DoDriveByAutoAiming(CEntity *driver, CVehicle *vehicle, CVector *source, CVector *target) { ASSERT(driver!=nil); ASSERT(source!=nil); ASSERT(target!=nil); #ifndef FIX_BUGS CEntity entity; // unused #endif CPed *shooterPed = (CPed*)driver; int16 lastEntity; CEntity *peds[16]; CWorld::FindObjectsInRange(*source, (*target-*source).Magnitude(), true, &lastEntity, 15, peds, false, false, true, false, false); float closestEntityDist = 10000.0f; int16 closestEntity; for ( int32 i = 0; i < lastEntity; i++ ) { CPed *victim = (CPed*)peds[i]; ASSERT(victim!=nil); if (driver != victim && !victim->DyingOrDead() && victim->m_attachedTo != vehicle) { float lineDist = CCollision::DistToLine(source, target, &victim->GetPosition()); uint32 model = vehicle->GetModelIndex(); float pedDist; if (model == MI_HUNTER || model == MI_SEASPAR || model == MI_SPARROW) { float distToVictim = (victim->GetPosition() - vehicle->GetPosition()).Magnitude(); pedDist = lineDist / Max(5.f, distToVictim); } else { float distToVictim = (victim->GetPosition() - driver->GetPosition()).Magnitude(); pedDist = 0.15f * distToVictim + lineDist; } if ( DotProduct((*target-*source), victim->GetPosition()-*source) > 0.0f && pedDist < closestEntityDist) { closestEntity = i; closestEntityDist = pedDist; } } } uint32 model = vehicle->GetModelIndex(); float maxAimDistance = CAR_DRIVEBYAUTOAIMING_MAXDIST; if (model == MI_HUNTER) { maxAimDistance = Tan(DEGTORAD(fHunterAimingAngle)); } else if (model == MI_SEASPAR || model == MI_SPARROW) { maxAimDistance = Tan(DEGTORAD(fSeaSparrowAimingAngle)); } if ( closestEntityDist < maxAimDistance ) { CEntity *victim = peds[closestEntity]; ASSERT(victim!=nil); float distToTarget = (*source - *target).Magnitude(); float distToSource = (*source - victim->GetPosition()).Magnitude(); *target = (distToTarget / distToSource) * (victim->GetPosition() - *source) + *source; } } void CWeapon::Reload(void) { if (m_nAmmoTotal == 0) return; CWeaponInfo *info = GetInfo(); if (m_nAmmoTotal >= info->m_nAmountofAmmunition) m_nAmmoInClip = info->m_nAmountofAmmunition; else m_nAmmoInClip = m_nAmmoTotal; } void CWeapon::Update(int32 audioEntity, CPed *pedToAdjustSound) { CWeaponInfo *info = GetInfo(); switch ( m_eWeaponState ) { case WEAPONSTATE_MELEE_MADECONTACT: { m_eWeaponState = WEAPONSTATE_READY; break; } case WEAPONSTATE_FIRING: { if ( IsShotgun(m_eWeaponType) && AEHANDLE_IS_OK(audioEntity) ) { uint32 timePassed = m_nTimer - CWeaponInfo::ms_aReloadSampleTime[m_eWeaponType]; if ( CTimer::GetPreviousTimeInMilliseconds() < timePassed && CTimer::GetTimeInMilliseconds() >= timePassed ) DMAudio.PlayOneShot(audioEntity, SOUND_WEAPON_RELOAD, 0.0f); } if ( CTimer::GetTimeInMilliseconds() > m_nTimer ) { if ( GetInfo()->m_eWeaponFire != WEAPON_FIRE_MELEE && m_nAmmoTotal == 0 ) { m_eWeaponState = WEAPONSTATE_OUT_OF_AMMO; CPickups::RemoveAllPickupsOfACertainWeaponGroupWithNoAmmo(m_eWeaponType); } else m_eWeaponState = WEAPONSTATE_READY; } break; } case WEAPONSTATE_RELOADING: { if ( AEHANDLE_IS_OK(audioEntity) && m_eWeaponType < WEAPONTYPE_TOTALWEAPONS) { CAnimBlendAssociation *reloadAssoc = nil; if (pedToAdjustSound) { if (CPed::GetReloadAnim(info) && (!CWorld::Players[CWorld::PlayerInFocus].m_bFastReload || !pedToAdjustSound->IsPlayer())) { reloadAssoc = RpAnimBlendClumpGetAssociation(pedToAdjustSound->GetClump(), CPed::GetReloadAnim(info)); if (!reloadAssoc) { reloadAssoc = RpAnimBlendClumpGetAssociation(pedToAdjustSound->GetClump(), CPed::GetCrouchReloadAnim(info)); } } } if (reloadAssoc && reloadAssoc->IsRunning() && reloadAssoc->blendAmount > 0.2f) { float soundStart = 0.75f; switch (info->m_AnimToPlay) { case ASSOCGRP_PYTHON: soundStart = fReloadAnimSampleFraction[0]; break; case ASSOCGRP_COLT: case ASSOCGRP_TEC: soundStart = fReloadAnimSampleFraction[1]; break; case ASSOCGRP_UZI: soundStart = fReloadAnimSampleFraction[2]; break; case ASSOCGRP_RIFLE: soundStart = fReloadAnimSampleFraction[3]; break; case ASSOCGRP_M60: soundStart = fReloadAnimSampleFraction[4]; break; default: break; } if (reloadAssoc->GetProgress() >= soundStart && (reloadAssoc->currentTime - reloadAssoc->timeStep) / reloadAssoc->hierarchy->totalLength < soundStart) DMAudio.PlayOneShot(audioEntity, SOUND_WEAPON_RELOAD, m_eWeaponType); if (CTimer::GetTimeInMilliseconds() > m_nTimer && reloadAssoc->GetProgress() < 0.9f) { m_nTimer = CTimer::GetTimeInMilliseconds(); } } else { uint32 timePassed = m_nTimer - CWeaponInfo::ms_aReloadSampleTime[m_eWeaponType]; if (CTimer::GetPreviousTimeInMilliseconds() < timePassed && CTimer::GetTimeInMilliseconds() >= timePassed) DMAudio.PlayOneShot(audioEntity, SOUND_WEAPON_RELOAD, m_eWeaponType); } } if ( CTimer::GetTimeInMilliseconds() > m_nTimer ) { Reload(); m_eWeaponState = WEAPONSTATE_READY; } break; } default: break; } } void FireOneInstantHitRound(CVector *source, CVector *target, int32 damage) { ASSERT(source!=nil); ASSERT(target!=nil); CParticle::AddParticle(PARTICLE_GUNFLASH, *source, CVector(0.0f, 0.0f, 0.0f)); CPointLights::AddLight(CPointLights::LIGHT_POINT, *source, CVector(0.0f, 0.0f, 0.0f), 5.0f, 1.0f, 0.8f, 0.0f, CPointLights::FOG_NONE, false); CColPoint point; CEntity *victim; CWorld::ProcessLineOfSight(*source, *target, point, victim, true, true, true, true, true, true, false); CParticle::AddParticle(PARTICLE_HELI_ATTACK, *source, ((*target) - (*source)) * 0.15f); if ( victim ) { if ( victim->IsPed() ) { CPed *victimPed = (CPed *)victim; if ( !victimPed->DyingOrDead() ) { CVector pos = victimPed->GetPosition(); CVector2D posOffset((*source).x-pos.x, (*source).y-pos.y); int32 localDir = victimPed->GetLocalDirection(posOffset); victimPed->ClearAttackByRemovingAnim(); CAnimBlendAssociation *asoc = CAnimManager::AddAnimation(victimPed->GetClump(), ASSOCGRP_STD, AnimationId(ANIM_STD_HITBYGUN_FRONT + localDir)); ASSERT(asoc!=nil); asoc->blendAmount = 0.0f; asoc->blendDelta = 8.0f; victimPed->InflictDamage(nil, WEAPONTYPE_UZI, damage, (ePedPieceTypes)point.pieceB, localDir); pos.z += 0.8f; if ( victimPed->GetIsOnScreen() ) { if ( CGame::nastyGame ) { for ( int32 i = 0; i < 4; i++ ) { CVector dir; dir.x = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); dir.y = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); dir.z = CGeneral::GetRandomNumberInRange(-0.1f, 0.1f); CParticle::AddParticle(PARTICLE_BLOOD, pos, dir); } } } } } else if ( victim->IsVehicle() ) ((CVehicle *)victim)->InflictDamage(nil, WEAPONTYPE_UZI, damage); //BUG ? no CGlass::WasGlassHitByBullet switch ( victim->GetType() ) { case ENTITY_TYPE_BUILDING: { PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_1, point.point); CParticle::AddParticle(PARTICLE_SMOKE, point.point, CVector(0.0f, 0.0f, 0.01f)); break; } case ENTITY_TYPE_VEHICLE: { CStats::BulletsThatHit++; DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_VEHICLE, 1.0f); break; } case ENTITY_TYPE_PED: { CStats::BulletsThatHit++; DMAudio.PlayOneShot(((CPhysical*)victim)->m_audioEntityId, SOUND_WEAPON_HIT_PED, 1.0f); ((CPed*)victim)->Say(SOUND_PED_BULLET_HIT); break; } case ENTITY_TYPE_OBJECT: { CStats::BulletsThatHit++; PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_2, point.point); break; } case ENTITY_TYPE_DUMMY: { PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_GROUND_3, point.point); break; } default: break; } } else { float waterLevel; if ( CWaterLevel::GetWaterLevel((*target).x, (*target).y, (*target).z + 10.0f, &waterLevel, false) ) { CParticle::AddParticle(PARTICLE_BOAT_SPLASH, CVector((*target).x, (*target).y, waterLevel), CVector(0.0f, 0.0f, 0.01f)); PlayOneShotScriptObject(SCRIPT_SOUND_BULLET_HIT_WATER, point.point); // no sound(empty) } } } bool CWeapon::IsTypeMelee(void) { return CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_eWeaponFire == WEAPON_FIRE_MELEE; } bool CWeapon::IsType2Handed(void) { return m_eWeaponType == WEAPONTYPE_FLAMETHROWER || m_eWeaponType == WEAPONTYPE_HELICANNON || m_eWeaponType == WEAPONTYPE_M60 || m_eWeaponType == WEAPONTYPE_M4 || IsShotgun(m_eWeaponType) || m_eWeaponType == WEAPONTYPE_RUGER || m_eWeaponType == WEAPONTYPE_SNIPERRIFLE || m_eWeaponType == WEAPONTYPE_LASERSCOPE; } void CWeapon::MakePedsJumpAtShot(CPhysical *shooter, CVector *source, CVector *target) { ASSERT(shooter!=nil); ASSERT(source!=nil); ASSERT(target!=nil); float minx = Min(source->x, target->x) - 2.0f; float maxx = Max(source->x, target->x) + 2.0f; float miny = Min(source->y, target->y) - 2.0f; float maxy = Max(source->y, target->y) + 2.0f; float minz = Min(source->z, target->z) - 2.0f; float maxz = Max(source->z, target->z) + 2.0f; for ( int32 i = CPools::GetPedPool()->GetSize() - 1; i >= 0; i--) { CPed *ped = CPools::GetPedPool()->GetSlot(i); if ( ped ) { if ( ped->GetPosition().x > minx && ped->GetPosition().x < maxx && ped->GetPosition().y > miny && ped->GetPosition().y < maxy && ped->GetPosition().z > minz && ped->GetPosition().z < maxz ) { if ( ped != FindPlayerPed() && !((uint8)(ped->m_randomSeed ^ CGeneral::GetRandomNumber()) & 31) ) ped->SetEvasiveDive(shooter, 1); } } } } bool CWeapon::HitsGround(CEntity *holder, CVector *fireSource, CEntity *aimingTo) { ASSERT(holder!=nil); ASSERT(aimingTo!=nil); if (!holder->IsPed() || !((CPed*)holder)->m_pSeekTarget) return false; CWeaponInfo *info = GetInfo(); CVector adjustedOffset = info->m_vecFireOffset; adjustedOffset.z += 0.6f; CVector source, target; CEntity *foundEnt = nil; CColPoint foundCol; if (fireSource) source = *fireSource; else source = holder->GetMatrix() * adjustedOffset; CEntity *aimEntity = aimingTo ? aimingTo : ((CPed*)holder)->m_pSeekTarget; ASSERT(aimEntity!=nil); target = aimEntity->GetPosition(); target.z += 0.6f; CWorld::ProcessLineOfSight(source, target, foundCol, foundEnt, true, false, false, false, false, false, false); if (foundEnt && foundEnt->IsBuilding()) { // That was supposed to be Magnitude, according to leftover code in assembly float diff = (foundCol.point.z - source.z); if (diff < 0.0f && diff > -3.0f) return true; } return false; } void CWeapon::BlowUpExplosiveThings(CEntity *thing) { if ( thing ) { CObject *object = (CObject*)thing; int32 mi = object->GetModelIndex(); if ( IsExplosiveThingModel(mi) && !object->bHasBeenDamaged && object->IsObject() ) { object->bHasBeenDamaged = true; CExplosion::AddExplosion(object, FindPlayerPed(), EXPLOSION_BARREL, object->GetPosition()+CVector(0.0f,0.0f,0.5f), 100); if ( MI_EXPLODINGBARREL == mi ) object->m_vecMoveSpeed.z += 0.55f; else object->m_vecMoveSpeed.z += 0.45f; object->m_vecMoveSpeed.x += float((CGeneral::GetRandomNumber()&255) - 128) * 0.0002f; object->m_vecMoveSpeed.y += float((CGeneral::GetRandomNumber()&255) - 128) * 0.0002f; if ( object->GetIsStatic()) { object->SetIsStatic(false); object->AddToMovingList(); } } } } bool CWeapon::HasWeaponAmmoToBeUsed(void) { // FIX: This is better (not bug tho) //#if 0 if (m_eWeaponType <= WEAPONTYPE_CHAINSAW) //#else // if (CWeaponInfo::GetWeaponInfo(m_eWeaponType)->m_eWeaponFire == WEAPON_FIRE_MELEE) //#endif return true; else return m_nAmmoTotal != 0; } bool CPed::IsPedDoingDriveByShooting(void) { #ifdef FIX_BUGS if (FindPlayerPed() == this && CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nWeaponSlot == 5) { #else if (FindPlayerPed() == this && GetWeapon()->m_eWeaponType == WEAPONTYPE_UZI) { #endif if (TheCamera.Cams[TheCamera.ActiveCam].LookingLeft || TheCamera.Cams[TheCamera.ActiveCam].LookingRight) return true; } return false; } bool CWeapon::ProcessLineOfSight(CVector const &point1, CVector const &point2, CColPoint &point, CEntity *&entity, eWeaponType type, CEntity *shooter, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects) { return CWorld::ProcessLineOfSight(point1, point2, point, entity, checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, false, ignoreSomeObjects, true); } void CWeapon::CheckForShootingVehicleOccupant(CEntity **victim, CColPoint *point, eWeaponType weapon, CVector const& source, CVector const& target) { if (!(*victim)->IsVehicle()) return; CColSphere headSphere; CVehicle *veh = (CVehicle*)*victim; CColPoint origPoint(*point); float radius = 1.0f; bool found = false; CColLine shootLine(source, target); if (veh->pDriver && veh->pDriver->bCanBeShotInVehicle) { CVector pos(0.f, 0.f, 0.f); veh->pDriver->TransformToNode(pos, PED_HEAD); headSphere.Set(0.2f, pos + CVector(0.f, 0.f, 0.1f), 0, PEDPIECE_HEAD); if (CCollision::ProcessLineSphere(shootLine, headSphere, *point, radius)) { *victim = veh->pDriver; found = true; } } for(int i = 0; i < ARRAY_SIZE(veh->pPassengers); i++) { CPed *passenger = veh->pPassengers[i]; if (passenger && passenger->bCanBeShotInVehicle) { CVector pos(0.f, 0.f, 0.f); passenger->TransformToNode(pos, PED_HEAD); headSphere.Set(0.2f, pos + CVector(0.f, 0.f, 0.1f), 0, PEDPIECE_HEAD); if (CCollision::ProcessLineSphere(shootLine, headSphere, *point, radius)) { *victim = passenger; found = true; } } } if (veh->IsCar()) { CVector distVec = target - source; if (DotProduct(distVec, veh->GetForward()) < 0.0f && DotProduct(distVec, veh->GetUp()) <= 0.0f) { CColModel *colModel = veh->GetColModel(); if (colModel->numTriangles > 0) { bool passesGlass = false; CMatrix invVehMat; Invert(veh->GetMatrix(), invVehMat); shootLine.p0 = invVehMat * shootLine.p0; shootLine.p1 = invVehMat * shootLine.p1; CCollision::CalculateTrianglePlanes(colModel); for (int i = 0; i < colModel->numTriangles; i++) { if (colModel->triangles[i].surface == SURFACE_GLASS && CCollision::TestLineTriangle(shootLine, colModel->vertices, colModel->triangles[i], colModel->trianglePlanes[i])) { passesGlass = true; break; } } CAutomobile *car = (CAutomobile*)veh; // No need to damage windscreen if there isn't one. if (passesGlass && car->Damage.ProgressPanelDamage(VEHPANEL_WINDSCREEN)) { if (car->Damage.GetPanelStatus(VEHPANEL_WINDSCREEN) == PANEL_STATUS_SMASHED2) car->Damage.ProgressPanelDamage(VEHPANEL_WINDSCREEN); car->SetPanelDamage(CAR_WINDSCREEN, VEHPANEL_WINDSCREEN, true); DMAudio.PlayOneShot(veh->m_audioEntityId, SOUND_CAR_WINDSHIELD_CRACK, 0.f); } } } } if (!found) { *victim = veh; *point = origPoint; } } #ifdef COMPATIBLE_SAVES #define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); #define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data)); void CWeapon::Save(uint8*& buf) { CopyToBuf(buf, m_eWeaponType); CopyToBuf(buf, m_eWeaponState); CopyToBuf(buf, m_nAmmoInClip); CopyToBuf(buf, m_nAmmoTotal); CopyToBuf(buf, m_nTimer); CopyToBuf(buf, m_bAddRotOffset); SkipSaveBuf(buf, 3); } void CWeapon::Load(uint8*& buf) { CopyFromBuf(buf, m_eWeaponType); CopyFromBuf(buf, m_eWeaponState); CopyFromBuf(buf, m_nAmmoInClip); CopyFromBuf(buf, m_nAmmoTotal); CopyFromBuf(buf, m_nTimer); CopyFromBuf(buf, m_bAddRotOffset); SkipSaveBuf(buf, 3); } #undef CopyFromBuf #undef CopyToBuf #endif ================================================ FILE: src/weapons/Weapon.h ================================================ #pragma once #include "WeaponType.h" #define CAR_DRIVEBYAUTOAIMING_MAXDIST (2.5f) #define DOOMAUTOAIMING_MAXDIST (9000.0f) class CEntity; class CPhysical; class CVehicle; class CPed; struct CColPoint; class CWeaponInfo; class CWeapon { public: eWeaponType m_eWeaponType; eWeaponState m_eWeaponState; int32 m_nAmmoInClip; int32 m_nAmmoTotal; uint32 m_nTimer; bool m_bAddRotOffset; static bool bPhotographHasBeenTaken; CWeapon() { m_bAddRotOffset = false; } CWeapon(eWeaponType type, int32 ammo); CWeaponInfo *GetInfo(); static void InitialiseWeapons(void); static void ShutdownWeapons (void); static void UpdateWeapons (void); void Initialise(eWeaponType type, int32 ammo); void Shutdown(); bool Fire (CEntity *shooter, CVector *fireSource); bool FireFromCar (CVehicle *shooter, bool left, bool right); bool FireMelee (CEntity *shooter, CVector &fireSource); bool FireInstantHit(CEntity *shooter, CVector *fireSource); static void AddGunFlashBigGuns(CVector start, CVector end); void AddGunshell (CEntity *shooter, CVector const &source, CVector2D const &direction, float size); void DoBulletImpact(CEntity *shooter, CEntity *victim, CVector *source, CVector *target, CColPoint *point, CVector2D ahead); bool FireShotgun (CEntity *shooter, CVector *fireSource); bool FireProjectile(CEntity *shooter, CVector *fireSource, float power); static void GenerateFlameThrowerParticles(CVector pos, CVector dir); bool FireAreaEffect (CEntity *shooter, CVector *fireSource); bool LaserScopeDot (CVector *pOutPos, float *pOutSize); bool FireSniper (CEntity *shooter); bool TakePhotograph (CEntity *shooter); bool FireM16_1stPerson (CEntity *shooter); bool FireInstantHitFromCar(CVehicle *shooter, bool left, bool right); static void DoDoomAiming (CEntity *shooter, CVector *source, CVector *target); static void DoTankDoomAiming (CEntity *shooter, CEntity *driver, CVector *source, CVector *target); static void DoDriveByAutoAiming(CEntity *driver, CVehicle *vehicle, CVector *source, CVector *target); void Reload(void); void Update(int32 audioEntity, CPed *pedToAdjustSound); bool IsTypeMelee (void); bool IsType2Handed(void); static void MakePedsJumpAtShot(CPhysical *shooter, CVector *source, CVector *target); bool HitsGround(CEntity *holder, CVector *fireSource, CEntity *aimingTo); static void BlowUpExplosiveThings(CEntity *thing); bool HasWeaponAmmoToBeUsed(void); static bool IsShotgun(int weapon) { return weapon == WEAPONTYPE_SHOTGUN || weapon == WEAPONTYPE_SPAS12_SHOTGUN || weapon == WEAPONTYPE_STUBBY_SHOTGUN; } static bool ProcessLineOfSight(CVector const &point1, CVector const &point2, CColPoint &point, CEntity *&entity, eWeaponType type, CEntity *shooter, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies, bool ignoreSeeThrough, bool ignoreSomeObjects); static void CheckForShootingVehicleOccupant(CEntity **victim, CColPoint *point, eWeaponType weapon, CVector const& source, CVector const& target); #ifdef COMPATIBLE_SAVES void Save(uint8*& buf); void Load(uint8*& buf); #endif }; VALIDATE_SIZE(CWeapon, 0x18); void FireOneInstantHitRound(CVector *source, CVector *target, int32 damage); ================================================ FILE: src/weapons/WeaponEffects.cpp ================================================ #include "common.h" #include "main.h" #include "WeaponEffects.h" #include "TxdStore.h" #include "Sprite.h" #include "PlayerPed.h" #include "World.h" #include "WeaponType.h" RwTexture *gpCrossHairTex; CWeaponEffects gCrossHair; CWeaponEffects::CWeaponEffects() { } CWeaponEffects::~CWeaponEffects() { } void CWeaponEffects::Init(void) { gCrossHair.m_bActive = false; gCrossHair.m_vecPos = CVector(0.0f, 0.0f, 0.0f); gCrossHair.m_nRed = 255; gCrossHair.m_nGreen = 0; gCrossHair.m_nBlue = 0; gCrossHair.m_nAlpha = 127; gCrossHair.m_fSize = 1.0f; gCrossHair.m_fRotation = 0.0f; CTxdStore::PushCurrentTxd(); int32 slot = CTxdStore::FindTxdSlot("particle"); CTxdStore::SetCurrentTxd(slot); gpCrossHairTex = RwTextureRead("target256", "target256m"); CTxdStore::PopCurrentTxd(); } void CWeaponEffects::Shutdown(void) { RwTextureDestroy(gpCrossHairTex); gpCrossHairTex = nil; } void CWeaponEffects::MarkTarget(CVector pos, uint8 red, uint8 green, uint8 blue, uint8 alpha, float size) { gCrossHair.m_bActive = true; gCrossHair.m_vecPos = pos; gCrossHair.m_fSize = size; } void CWeaponEffects::ClearCrossHair(void) { gCrossHair.m_bActive = false; } void CWeaponEffects::Render(void) { static float aCrossHairSize[WEAPONTYPE_TOTALWEAPONS] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.4f, 0.4f, 0.5f, 0.3f, 0.9f, 0.9f, 0.9f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.1f, 0.1f, 1.0f, 0.6f, 0.7f, 0.0f, 0.0f }; if ( gCrossHair.m_bActive ) { float size = aCrossHairSize[FindPlayerPed()->GetWeapon()->m_eWeaponType]; RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); #ifdef FIX_BUGS RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); #else RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVDESTALPHA); #endif RwRenderStateSet(rwRENDERSTATETEXTURERASTER, (void *)RwTextureGetRaster(gpCrossHairTex)); RwV3d pos; float w, h; if ( CSprite::CalcScreenCoors(gCrossHair.m_vecPos, &pos, &w, &h, true) ) { PUSH_RENDERGROUP("CWeaponEffects::Render"); float recipz = 1.0f / pos.z; CSprite::RenderOneXLUSprite_Rotate_Aspect(pos.x, pos.y, pos.z, w, h, 255, 88, 100, 158, recipz, gCrossHair.m_fRotation, gCrossHair.m_nAlpha); float recipz2 = 1.0f / pos.z; CSprite::RenderOneXLUSprite_Rotate_Aspect(pos.x, pos.y, pos.z, size*w, size*h, 107, 134, 247, 158, recipz2, TWOPI - gCrossHair.m_fRotation, gCrossHair.m_nAlpha); gCrossHair.m_fRotation += 0.02f; if ( gCrossHair.m_fRotation > TWOPI ) gCrossHair.m_fRotation = 0.0; POP_RENDERGROUP(); } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); } } ================================================ FILE: src/weapons/WeaponEffects.h ================================================ #pragma once class CWeaponEffects { public: bool m_bActive; CVector m_vecPos; uint8 m_nRed; uint8 m_nGreen; uint8 m_nBlue; uint8 m_nAlpha; float m_fSize; float m_fRotation; public: CWeaponEffects(); ~CWeaponEffects(); static void Init(void); static void Shutdown(void); static void MarkTarget(CVector pos, uint8 red, uint8 green, uint8 blue, uint8 alpha, float size); static void ClearCrossHair(void); static void Render(void); }; VALIDATE_SIZE(CWeaponEffects, 0x1C); ================================================ FILE: src/weapons/WeaponInfo.cpp ================================================ #include "common.h" #include "main.h" #include "FileMgr.h" #include "WeaponInfo.h" #include "AnimManager.h" #include "AnimBlendAssociation.h" #include "Weapon.h" #include "ModelInfo.h" #include "ModelIndices.h" uint16 CWeaponInfo::ms_aReloadSampleTime[WEAPONTYPE_TOTALWEAPONS] = { 0, // UNARMED 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // GRENADE 0, // DETONATEGRENADE 0, // TEARGAS 0, // MOLOTOV 0, // ROCKET 250, // COLT45 250, // PYTHON 650, // SHOTGUN 650, // SPAS12 SHOTGUN 650, // STUBBY SHOTGUN 400, // TEC9 400, // UZIhec 400, // SILENCED_INGRAM 400, // MP5 300, // M16 300, // AK47 423, // SNIPERRIFLE 423, // LASERSCOPE 400, // ROCKETLAUNCHER 0, // FLAMETHROWER 0, // M60 0, // MINIGUN 0, // DETONATOR 0, // HELICANNON 0 // CAMERA }; // Yeah... int32 CWeaponInfo::ms_aMaxAmmoForWeapon[WEAPONTYPE_TOTALWEAPONS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; CWeaponInfo aWeaponInfo[WEAPONTYPE_TOTALWEAPONS]; char CWeaponInfo::ms_aWeaponNames[WEAPONTYPE_TOTALWEAPONS][32] = { "Unarmed", "BrassKnuckle", "ScrewDriver", "GolfClub", "NightStick", "Knife", "BaseballBat", "Hammer", "Cleaver", "Machete", "Katana", "Chainsaw", "Grenade", "DetonateGrenade", "TearGas", "Molotov", "Rocket", "Colt45", "Python", "Shotgun", "Spas12Shotgun", "StubbyShotgun", "Tec9", "Uzi", "SilencedIngram", "Mp5", "m4", "Ruger", "SniperRifle", "LaserScope", "RocketLauncher", "FlameThrower", "M60", "Minigun", "Detonator", "HeliCannon", "Camera", }; CWeaponInfo* CWeaponInfo::GetWeaponInfo(eWeaponType weaponType) { return &aWeaponInfo[weaponType]; } void CWeaponInfo::Initialise(void) { debug("Initialising CWeaponInfo...\n"); for (int i = 0; i < WEAPONTYPE_TOTALWEAPONS; i++) { aWeaponInfo[i].m_eWeaponFire = WEAPON_FIRE_INSTANT_HIT; aWeaponInfo[i].m_fRange = 0.0f; aWeaponInfo[i].m_nFiringRate = 0; aWeaponInfo[i].m_nReload = 0; aWeaponInfo[i].m_nAmountofAmmunition = 0; aWeaponInfo[i].m_nDamage = 0; aWeaponInfo[i].m_fSpeed = 0.0f; aWeaponInfo[i].m_fRadius = 0.0f; aWeaponInfo[i].m_fLifespan = 0.0f; aWeaponInfo[i].m_fSpread = 0.0f; aWeaponInfo[i].m_vecFireOffset = CVector(0.0f, 0.0f, 0.0f); aWeaponInfo[i].m_AnimToPlay = ASSOCGRP_UNARMED; aWeaponInfo[i].m_fAnimLoopStart = 0.0f; aWeaponInfo[i].m_fAnimLoopEnd = 0.0f; aWeaponInfo[i].m_fAnimFrameFire = 0.0f; aWeaponInfo[i].m_fAnim2LoopStart = 0.0f; aWeaponInfo[i].m_fAnim2LoopEnd = 0.0f; aWeaponInfo[i].m_fAnim2FrameFire = 0.0f; aWeaponInfo[i].m_fAnimBreakout = 0.0f; aWeaponInfo[i].m_Flags = WEAPONFLAG_USE_GRAVITY | WEAPONFLAG_SLOWS_DOWN | WEAPONFLAG_RAND_SPEED | WEAPONFLAG_EXPANDS | WEAPONFLAG_EXPLODES; aWeaponInfo[i].m_nWeaponSlot = WEAPONSLOT_UNARMED; } debug("Loading weapon data...\n"); LoadWeaponData(); debug("CWeaponInfo ready\n"); } void CWeaponInfo::LoadWeaponData(void) { float spread, speed, lifeSpan, radius; float range, fireOffsetX, fireOffsetY, fireOffsetZ; float anim2LoopStart, anim2LoopEnd, delayBetweenAnim2AndFire, animBreakout; float delayBetweenAnimAndFire, animLoopStart, animLoopEnd; int flags, ammoAmount, damage, reload, weaponType; int firingRate, modelId, modelId2, weaponSlot; char line[256], weaponName[32], fireType[32]; char animToPlay[32]; size_t bp, buflen; int lp, linelen; CFileMgr::SetDir("DATA"); buflen = CFileMgr::LoadFile("WEAPON.DAT", work_buff, sizeof(work_buff), "r"); for (bp = 0; bp < buflen; ) { // read file line by line for (linelen = 0; work_buff[bp] != '\n' && bp < buflen; bp++) { line[linelen++] = work_buff[bp]; } bp++; line[linelen] = '\0'; // skip white space for (lp = 0; line[lp] <= ' ' && line[lp] != '\0'; lp++); if (line[lp] == '\0' || line[lp] == '#') continue; spread = 0.0f; flags = 0; speed = 0.0f; ammoAmount = 0; lifeSpan = 0.0f; radius = 0.0f; range = 0.0f; damage = 0; reload = 0; firingRate = 0; fireOffsetX = 0.0f; weaponName[0] = '\0'; fireType[0] = '\0'; fireOffsetY = 0.0f; fireOffsetZ = 0.0f; sscanf( &line[lp], "%s %s %f %d %d %d %d %f %f %f %f %f %f %f %s %f %f %f %f %f %f %f %d %d %x %d", weaponName, fireType, &range, &firingRate, &reload, &ammoAmount, &damage, &speed, &radius, &lifeSpan, &spread, &fireOffsetX, &fireOffsetY, &fireOffsetZ, animToPlay, &animLoopStart, &animLoopEnd, &delayBetweenAnimAndFire, &anim2LoopStart, &anim2LoopEnd, &delayBetweenAnim2AndFire, &animBreakout, &modelId, &modelId2, &flags, &weaponSlot); if (strncmp(weaponName, "ENDWEAPONDATA", 13) == 0) return; weaponType = FindWeaponType(weaponName); CVector vecFireOffset(fireOffsetX, fireOffsetY, fireOffsetZ); aWeaponInfo[weaponType].m_eWeaponFire = FindWeaponFireType(fireType); aWeaponInfo[weaponType].m_fRange = range; aWeaponInfo[weaponType].m_nFiringRate = firingRate; aWeaponInfo[weaponType].m_nReload = reload; aWeaponInfo[weaponType].m_nAmountofAmmunition = ammoAmount; aWeaponInfo[weaponType].m_nDamage = damage; aWeaponInfo[weaponType].m_fSpeed = speed; aWeaponInfo[weaponType].m_fRadius = radius; aWeaponInfo[weaponType].m_fLifespan = lifeSpan; aWeaponInfo[weaponType].m_fSpread = spread; aWeaponInfo[weaponType].m_vecFireOffset = vecFireOffset; aWeaponInfo[weaponType].m_fAnimLoopStart = animLoopStart / 30.0f; aWeaponInfo[weaponType].m_fAnimLoopEnd = animLoopEnd / 30.0f; aWeaponInfo[weaponType].m_fAnim2LoopStart = anim2LoopStart / 30.0f; aWeaponInfo[weaponType].m_fAnim2LoopEnd = anim2LoopEnd / 30.0f; aWeaponInfo[weaponType].m_fAnimFrameFire = delayBetweenAnimAndFire / 30.0f; aWeaponInfo[weaponType].m_fAnim2FrameFire = delayBetweenAnim2AndFire / 30.0f; aWeaponInfo[weaponType].m_fAnimBreakout = animBreakout / 30.0f; aWeaponInfo[weaponType].m_nModelId = modelId; aWeaponInfo[weaponType].m_nModel2Id = modelId2; aWeaponInfo[weaponType].m_Flags = flags; aWeaponInfo[weaponType].m_nWeaponSlot = weaponSlot; if (animLoopEnd < 98.0f && weaponType != WEAPONTYPE_FLAMETHROWER && !CWeapon::IsShotgun(weaponType)) aWeaponInfo[weaponType].m_nFiringRate = ((aWeaponInfo[weaponType].m_fAnimLoopEnd - aWeaponInfo[weaponType].m_fAnimLoopStart) * 900.0f); if (weaponType == WEAPONTYPE_DETONATOR || weaponType == WEAPONTYPE_HELICANNON) modelId = -1; else if (weaponType == WEAPONTYPE_DETONATOR_GRENADE) modelId = MI_BOMB; if (modelId != -1) ((CWeaponModelInfo*)CModelInfo::GetModelInfo(modelId))->SetWeaponInfo(weaponType); for (int i = 0; i < NUM_ANIM_ASSOC_GROUPS; i++) { if (!strcmp(animToPlay, CAnimManager::GetAnimGroupName((AssocGroupId)i))) { aWeaponInfo[weaponType].m_AnimToPlay = (AssocGroupId)i; break; } } } } eWeaponType CWeaponInfo::FindWeaponType(char *name) { for (int i = 0; i < WEAPONTYPE_TOTALWEAPONS; i++) { if (strcmp(ms_aWeaponNames[i], name) == 0) { return static_cast(i); } } return WEAPONTYPE_UNARMED; } eWeaponFire CWeaponInfo::FindWeaponFireType(char *name) { if (strcmp(name, "MELEE") == 0) return WEAPON_FIRE_MELEE; if (strcmp(name, "INSTANT_HIT") == 0) return WEAPON_FIRE_INSTANT_HIT; if (strcmp(name, "PROJECTILE") == 0) return WEAPON_FIRE_PROJECTILE; if (strcmp(name, "AREA_EFFECT") == 0) return WEAPON_FIRE_AREA_EFFECT; if (strcmp(name, "CAMERA") == 0) return WEAPON_FIRE_CAMERA; Error("Unknown weapon fire type, WeaponInfo.cpp"); return WEAPON_FIRE_INSTANT_HIT; } void CWeaponInfo::Shutdown(void) { debug("Shutting down CWeaponInfo...\n"); debug("CWeaponInfo shut down\n"); } ================================================ FILE: src/weapons/WeaponInfo.h ================================================ #pragma once #include "AnimManager.h" #include "AnimationId.h" #include "WeaponType.h" enum { WEAPONFLAG_USE_GRAVITY = 1, WEAPONFLAG_SLOWS_DOWN = 1 << 1, WEAPONFLAG_DISSIPATES = 1 << 2, WEAPONFLAG_RAND_SPEED = 1 << 3, WEAPONFLAG_EXPANDS = 1 << 4, WEAPONFLAG_EXPLODES = 1 << 5, WEAPONFLAG_CANAIM = 1 << 6, WEAPONFLAG_CANAIM_WITHARM = 1 << 7, WEAPONFLAG_1ST_PERSON = 1 << 8, WEAPONFLAG_HEAVY = 1 << 9, WEAPONFLAG_THROW = 1 << 10, WEAPONFLAG_RELOAD_LOOP2START = 1 << 11, WEAPONFLAG_USE_2ND = 1 << 12, WEAPONFLAG_GROUND_2ND = 1 << 13, WEAPONFLAG_FINISH_3RD = 1 << 14, WEAPONFLAG_RELOAD = 1 << 15, WEAPONFLAG_FIGHTMODE = 1 << 16, WEAPONFLAG_CROUCHFIRE = 1 << 17, WEAPONFLAG_COP3_RD = 1 << 18, WEAPONFLAG_GROUND_3RD = 1 << 19, WEAPONFLAG_PARTIALATTACK = 1 << 20, WEAPONFLAG_ANIMDETONATE = 1 << 21, }; class CWeaponInfo { static char ms_aWeaponNames[WEAPONTYPE_TOTALWEAPONS][32]; public: static uint16 ms_aReloadSampleTime[WEAPONTYPE_TOTALWEAPONS]; static int32 ms_aMaxAmmoForWeapon[WEAPONTYPE_TOTALWEAPONS]; eWeaponFire m_eWeaponFire; float m_fRange; uint32 m_nFiringRate; uint32 m_nReload; int32 m_nAmountofAmmunition; uint32 m_nDamage; float m_fSpeed; float m_fRadius; float m_fLifespan; float m_fSpread; CVector m_vecFireOffset; AssocGroupId m_AnimToPlay; float m_fAnimLoopStart; float m_fAnimLoopEnd; float m_fAnimFrameFire; float m_fAnim2LoopStart; float m_fAnim2LoopEnd; float m_fAnim2FrameFire; float m_fAnimBreakout; int32 m_nModelId; int32 m_nModel2Id; uint32 m_Flags; uint32 m_nWeaponSlot; static void Initialise(void); static void LoadWeaponData(void); static CWeaponInfo *GetWeaponInfo(eWeaponType weaponType); static eWeaponFire FindWeaponFireType(char *name); static eWeaponType FindWeaponType(char *name); static void Shutdown(void); static bool IsWeaponSlotAmmoMergeable(uint32 slot) { return slot == WEAPONSLOT_SHOTGUN || slot == WEAPONSLOT_SUBMACHINEGUN || slot == WEAPONSLOT_RIFLE; } bool IsFlagSet(uint32 flag) const { return (m_Flags & flag) != 0; } }; VALIDATE_SIZE(CWeaponInfo, 0x64); ================================================ FILE: src/weapons/WeaponType.h ================================================ #pragma once enum eWeaponType { WEAPONTYPE_UNARMED, WEAPONTYPE_BRASSKNUCKLE, WEAPONTYPE_SCREWDRIVER, WEAPONTYPE_GOLFCLUB, WEAPONTYPE_NIGHTSTICK, WEAPONTYPE_KNIFE, WEAPONTYPE_BASEBALLBAT, WEAPONTYPE_HAMMER, WEAPONTYPE_CLEAVER, WEAPONTYPE_MACHETE, WEAPONTYPE_KATANA, WEAPONTYPE_CHAINSAW, WEAPONTYPE_GRENADE, WEAPONTYPE_DETONATOR_GRENADE, WEAPONTYPE_TEARGAS, WEAPONTYPE_MOLOTOV, WEAPONTYPE_ROCKET, WEAPONTYPE_COLT45, WEAPONTYPE_PYTHON, WEAPONTYPE_SHOTGUN, WEAPONTYPE_SPAS12_SHOTGUN, WEAPONTYPE_STUBBY_SHOTGUN, WEAPONTYPE_TEC9, WEAPONTYPE_UZI, WEAPONTYPE_SILENCED_INGRAM, WEAPONTYPE_MP5, WEAPONTYPE_M4, WEAPONTYPE_RUGER, WEAPONTYPE_SNIPERRIFLE, WEAPONTYPE_LASERSCOPE, WEAPONTYPE_ROCKETLAUNCHER, WEAPONTYPE_FLAMETHROWER, WEAPONTYPE_M60, WEAPONTYPE_MINIGUN, WEAPONTYPE_DETONATOR, WEAPONTYPE_HELICANNON, WEAPONTYPE_CAMERA, WEAPONTYPE_TOTALWEAPONS = 37, WEAPONTYPE_HEALTH = 37, WEAPONTYPE_ARMOUR, WEAPONTYPE_RAMMEDBYCAR, WEAPONTYPE_RUNOVERBYCAR, WEAPONTYPE_EXPLOSION, WEAPONTYPE_UZI_DRIVEBY, WEAPONTYPE_DROWNING, WEAPONTYPE_FALL, WEAPONTYPE_UNIDENTIFIED, WEAPONTYPE_ANYMELEE, WEAPONTYPE_ANYWEAPON }; enum { WEAPONSLOT_UNARMED = 0, WEAPONSLOT_MELEE, WEAPONSLOT_PROJECTILE, WEAPONSLOT_HANDGUN, WEAPONSLOT_SHOTGUN, WEAPONSLOT_SUBMACHINEGUN, WEAPONSLOT_RIFLE, WEAPONSLOT_HEAVY, WEAPONSLOT_SNIPER, WEAPONSLOT_OTHER, TOTAL_WEAPON_SLOTS }; enum eWeaponFire { WEAPON_FIRE_MELEE, WEAPON_FIRE_INSTANT_HIT, WEAPON_FIRE_PROJECTILE, WEAPON_FIRE_AREA_EFFECT, WEAPON_FIRE_CAMERA }; // Taken from MTA SA, seems it's unchanged enum eWeaponState { WEAPONSTATE_READY, WEAPONSTATE_FIRING, WEAPONSTATE_RELOADING, WEAPONSTATE_OUT_OF_AMMO, WEAPONSTATE_MELEE_MADECONTACT }; ================================================ FILE: utils/gxt/american.txt ================================================ [IN_VEH] ~g~Hey! Get back in the vehicle! [HEY] ~g~Don't go solo, keep your posse together! [HELP3] You can only sprint for short periods before becoming tired. [HELP4_D] Push the right analog stick up to ~h~accelerate. [HELP5_D] Pull the right analog stick back to brake, or to reverse if the vehicle has stopped. [HELP7_A] Press and hold the~h~ ~k~~PED_LOCK_TARGET~ button ~w~to ~h~target~w~ with the sniper rifle. [HELP7_D] Press and hold the~h~ ~k~~PED_LOCK_TARGET~ button ~w~to ~h~target ~w~with the sniper rifle. [HELP10] This badge indicates you have a police wanted level. [HELP11] The more badges the higher your wanted level. [HELP13] Sometimes you may need to use pathways not shown on the radar. [TIMER] This is a timed mission, you must complete it before the timer counts down to zero. [HORN] ~g~Sound the horn. [NOMONEY] ~g~You need more cash! [REWARD] REWARD $~1~ [M_FAIL] MISSION FAILED! [M_PASS] MISSION PASSED! $~1~ [DEAD] WASTED! [BUSTED] BUSTED! [WEATHE1] FORCE WEATHER SUNNY [WEATHE2] FORCE WEATHER EXTRA SUNNY [WEATHE3] FORCE WEATHER CLOUDY [WEATHE4] FORCE WEATHER RAINY [WEATHE5] FORCE WEATHER FOGGY [WEATHE6] WEATHER NORMAL [NUMBER] ~1~ [LOADCAR] LOADING VEHICLE... (PRESS L1 TO CANCEL) [CARSOFF] Cars turned off. [CARS_ON] Cars turned on. [TEXTXYZ] Writing coordinates to file... [CHEATON] Cheat mode ON [CHEATOF] Cheat mode OFF [IMPORT1] Go outside and wait for your vehicle. [PAGEB11] Flamethrower delivered to hideout. [WANT_A] You will only be arrested if you have a ~h~wanted level. [WANT_B] Your ~h~wanted level~w~ is represented by the row of stars in the top right of the screen. [WANT_C] You now have a ~h~wanted level~w~ of one... [WANT_D] two... [WANT_E] three... [WANT_F] As your ~h~wanted level~w~ increases you will attract more powerful forms of law enforcement. [WANT_G] When you are ~h~'busted'~w~ you are returned to the nearest police station. [WANT_H] The cops will take all your weapons and some of your cash as a bribe. [WANT_I] Any mission you were on will be failed. [WANT_J] You will find ways of reducing your wanted level the more you play. [WANT_K] If you are in a car, ~h~SPRAY SHOPS~w~ will ~h~clear your wanted level. [HEAL_B] When you are ~h~'wasted'~w~ you are returned to the nearest hospital. [HEAL_C] You will lose your weapons and the doctors will take some cash for patching you up. [HEAL_E] You will find ways of healing or protecting yourself the more you play the game. [SAVE1] Walk into the corona to ~h~Save the game~w~. You cannot save during a mission. [SAVE2] Any vehicle left in this garage will be stored when the game is saved. [AMMU] Go inside Ammu-Nation to buy a weapon. [R_TIME] RACE TIME: [PROP_1] You don't have enough cash for this property [PROP_2] You cannot buy property whilst on a mission [IND_ZON] Vice City Beach [COM_ZON] Vice City Mainland [BEACH1] Ocean Beach [BEACH2] Washington Beach [BEACH3] Vice Point [GOLFC] Leaf Links [STARI] Starfish Island [DOCKS] Viceport [HAVANA] Little Havana [HAITI] Little Haiti [PORNI] Prawn Island [DTOWN] Downtown [VICE_C] Vice City [A_PORT] Escobar International [JUNKY] Junk Yard [PISTOL] Pistol [PYTHON] .357 [UZI] Uz-1 [TEC9] Tec 9 [M4] M4 [INGRAM] Mac [MP5] MP [RUGER] Kruger [SNIPE] Sniper rifle [GRENADE] Grenades [SHOTGN1] Shotgun [SHOTGN2] S.P.A.S. 12 [SHOTGN3] Stubby shotgun [ARMOUR] Body Armor [LASER] .308 Sniper [BASEBAT] Baseball bat [HAMMER] Hammer [SCREWD] Screwdriver [CLEVER] Meat Cleaver [MACHETE] Machete [KNIFE] Knife [KATANA] Katana [CHAINSA] Chainsaw [G_COST] Cost: $~1~ [CAR_1] Ambulance [MALIBU] The Malibu Club [MANSION] Diaz's Mansion [TMANS] Vercetti Estate [STRIP] The 'Pole Position Club' [MALL1] North Point Mall [BANKINT] El Banco Corrupto Grande [RANGE] Rifle Range [POL_HQ] VCPD HQ [INT_B] An Old Friend [INTB_1] ~g~Go to the Lawyer's office. [LAW_1] The Party [LAW_2] Back Alley Brawl [LAW_3] Jury Fury [LAW_4] Riot [COL_1] Treacherous Swine [COL_2] Mall Shootout [COL_3] Guardian Angels [COL_4] Sir, Yes Sir! [COL_5] All Hands On Deck! [COK_1] The Chase [COK_2] Phnom Penh '86 [COK_3] The Fastest Boat [COK_4] Supply & Demand [KENT_1] Death Row [ASS_1] Rub Out [BUD_1] Shakedown [BUD_2] Bar Brawl [BUD_3] Cop Land [CAP_1] Cap the Collector [FIN_1] Keep your Friends Close... [BANK_1] No Escape? [BANK_2] The Shootist [BANK_3] The Driver [BANK_4] The Job [CNT_1] Spilling the Beans [CNT_2] Hit the Courier [PORN_1] Recruitment Drive [PORN_2] Dildo Dodo [PORN_3] Martha's Mug Shot [PORN_4] G-spotlight [TAX_1] Kaufman Cabs [TAXI_1] V.I.P. [TAXI_2] Friendly Rivalry [TAXI_3] Cabmaggedon [ICE_1] Distribution [TEX_1] Four Iron [TEX_2] Two Bit Hit [TEX_3] Demolition Man [PHIL_1] Gun Runner [PHIL_2] Boomshine Saigon [BIKE_1] Alloy Wheels of Steel [BIKE_2] Messing with the Man [BIKE_3] Hog Tied [ROCK_1] Love Juice [ROCK_2] Psycho Killer [ROCK_3] Publicity Tour [ROCK_4] Love Fist!! [HAT_1] Juju Scramble [HAT_2] Bombs Away! [HAT_3] Dirty Lickin's [CUB_1] Stunt Boat Challenge [CUB_2] Cannon Fodder [CUB_3] Naval Engagement [CUB_4] Trojan Voodoo [JOB_1] Road Kill [JOB_2] Waste the Wife [JOB_3] Autocide [JOB_4] Check Out at the Check In [JOB_5] Loose Ends [ANSWER] Press the ~h~~k~~PED_ANSWER_PHONE~~w~ to answer your cell phone. [MOB_01A] Awright me ol'china! It's Paul. I might have a little result for you, but I need to speak to you in person. [MOB_01B] I'm enjoying a little R&R at the Club Malibu. [MOB_01C] Reckon you're gonna owe me a favor or two after this, sunshine. I'll see you later. [MOB_02A] Ssssnniiiiffffff Hey! Hello, Tommy? Tommy! [MOB_02B] We got a situation over at the Print Works. You better go and check it out. [MOB_02C] Some kind of mess or other. Things are messed up. I gotta go. [MOB_03A] Mr. Vercetti? I have here a signed piece of crap stating [MOB_03B] that you have taken on all of BJ's Auto's debts. [MOB_03C] With BJ's sudden disappearance I have no choice [MOB_03D] but to hold you responsible for his financial insecurities. [MOB_03E] Until this account is settled in full [MOB_03F] you should consider Vice City's streets to be very unfriendly. [MOB_04A] How you doin' mate? It's Paulo again. [MOB_04B] Look Tommy, I forgot to mention we're going to need some extra muscle for the concert. A bit of security. [MOB_04C] There's a biker gang led by Mitch Baker, it would be great publicity. Very rock and roll, baby. [MOB_04D] Sort this out for me and I'll get you some back stage passes for the gig, awright? [MOB_05A] Hey, it's Mitch. You did good Tommy, it's good to have the old girl back. [MOB_05B] You tell Kent Paul he'll get his security for the gig. [MOB_05C] You have my word on that. [MOB_05D] Now keep yourself out of trouble. [MOB_06A] Tommy, 'nuf dead man been chattin' about you, my dear. [MOB_06B] Thought you might need something to make you feel better. So Auntie Poulet make you some stew, aye? [MOB_06C] Come by me kitchen some time, ok Tommy? [MOB_08A] Hey Tommy, I thought you might need some business advice. [MOB_08B] Once you got an operation up and running, you'll need to drop by and take the week's cash. [MOB_08C] Let the guys think they got the run of the place and they'll try shaving the profits - ok? [MOB_08D] Hey, I know how to handle business, Ken, ok? [MOB_08E] Ok, ok. I know, you know. I know. I was, [MOB_08F] I was just, you know, telling you I know, that you know, that I know. [MOB_08G] Just keeping it sharp baby! [MOB_08H] Whatever, Ken, whatever... [MOB_09A] Hey Leo! I got some work for you! [MOB_09B] This ain't Leo. [MOB_09C] Hey, if Leo knows you got his phone, he gonna kill you! [MOB_09E] You killed Leo? You must have big cojones - wanna work for me?! [MOB_09F] Drop by my father's cafe in Little Havana and we'll talk mano a mano. [MOB_10A] Tommy! Look, I gotta ask you a favor. [MOB_10B] Steve! How's filming going! [MOB_10C] Fine, fine. I, heh, WE need a car chase scene, but our budget can't stretch to it. [MOB_10D] I've left some wheels around town. You'll know what to do. [MOB_10E] Ok Steve, I'll keep an eye out. Catch you later. [MOB_11A] Howdy son, just thought I'd ring you up and give you some advice. [MOB_11B] Hey, Avery. What's eating you? [MOB_11C] There's a lot of opportunity in this town if you own the right real estate, you catch my drift? [MOB_11D] I reckon so... [MOB_11E] All I'm saying is keep your eyes open and you might find the perfect business opportunity. I'll catch y'later. [MOB_11F] Later, Avery. [MOB12_A] Hey Tommy, it's Avery! Now listen, I got me all tied up at the moment [MOB12_B] and I have a representative of mine needs chaperoning out to the Gator Keys. [MOB12_C] I'm after some land out that way, so I'm sending someone out to sweeten the deal. [MOB12_D] Could you do me a favor and make sure he gets there ok? [MOB12_E] Yeah, sure thing Avery. Where'd you want me to pick him up? [MOB12_F] He's just finishing some business at the building site. I said you'd pick him up from there. [MOB12_G] No problem. See you later, Avery. [MOB13_A] Vercetti? VERCETTI!! Damn you man, you've got to help me! [MOB13_B] Mr. Moffat? How's family life? [MOB13_C] Damn you to hell, HELL, do hear me?! [MOB13_D] Well it was nice chatting... [MOB13_E] WAIT! Wait, Vercetti - Tommy, can I call you Tommy? [MOB13_F] We're both businessmen, yeah? You know a good deal when you hear one, ok? [MOB13_G] I don't have time to chat, get to the point. [MOB13_H] MONEY. Money is the goddamned point. [MOB13_I] I've escaped the coop again, but it's never long before they track me down - they think it's a damned game! [MOB13_J] I'm at a pay phone somewhere in this god forsaken shit hole. [MOB13_K] Get me out of here before they take me back and...and..oh go-o-od... [MOB13_L] Well, I'm busy for the next - [MOB13_M] No! Don't shit with me here, have a heart! No man should have to do such, such things. [MOB13_N] I'm on my knees here Tommy, in the dirt begging you please... [MOB13_O] I guess I could swing by that way, see if I can spot you... [MOB13_P] Oh god, they're coming. For the love of Christ hurry, hurry! [MOB_14A] Hey there Tommy, you're gonna love me mate. [MOB_14B] A little birdy told me that Vice City SWAT Division has a deposit box at a certain rather large banking establishment, [MOB_14C] where they keep all the bribes they've taken over the years, [MOB_14D] like some kind of old boys' retirement fund. [MOB_14E] Of course, if this information should ever help you acquire any of that cash, [MOB_14F] I guess you'd feel obliged to push some of it my way? [MOB_14G] I'll bear that in mind, thanks Kent. [MOB_14H] It's Paul. I'm from Kent, near London, you prat. [MOB_14I] My provincial English geography ain't what it was. [MOB15_A] Tommy, mate, it's Paul, from Kent, [MOB15_B] a couple of proper sorts have your name written all over them, down at the Malibu. [MOB15_C] What are you talking about? [MOB15_D] Sorts. Birds. You know. Girls. Tastey ones, don't think they're brasses or nothing. [MOB15_E] You gotta come check them out. [MOB16_A] Tommy, Paulo here, que pasa amigo? [MOB16_B] What do you want Paul. I don't want any fake label clothes. [MOB16_C] Very funny, mate, but you know I don't touch bent gear. [MOB16_D] Nah, I was just calling to see if I get a part in one your movies, [MOB16_E] back in England I did a lot of blue stuff, mate. [MOB16_F] I'm packing more heat than you, my son. [MOB16_G] Paul, thanks for the offer, I'll bear it in mind. [MOB16_H] Seriously, don't forget about me, after all I done for you. [MOB16_I] That's what I'm trying to forget about. [MOB19_A] Tommy V, It's KP here. Kent Paul. Word on the street is people want to rip you off. [MOB19_B] Keep your eye's peeled, my son. And remember, I didn't say nothing to you about this. [MOB_20A] Alright, Tommy, it's Paul. I just heard from a mush that you've been a real naughty boy. [MOB_20B] Somebody has taken offense to you acting like the big guy all of a sudden, giving it the big shot thing. [MOB_20C] Well, don't say I never warned you or nothing. Boasting is a mug's game, son. [MOB_20D] Anyway, I heard there's some price been put on your head and someone's going to have a crack at you, [MOB_20E] so watch yourself, and remember me, mate. [MOB21_A] Tommy, Thomas, it's Cortez. Que pasa? [MOB21_B] Things are interesting. How are you, my friend? [MOB21_G] I wanted to ask you about Mercedes. [MOB21_H] Ok, what about her? [MOB21_I] Oh Tommy, Tommy. I, I hear these stories, all these stories - I don't know what to think. [MOB21_K] Maybe she thinks she can do what she likes, but Tommy, tell me, is it true? [MOB21_M] Is what true? [MOB21_N] These stories I hear. Is she really going to be a lawyer? [MOB21_O] Oh Tommy, the shame, the shame! You know, we Cortez's are a proud family. [MOB21_P] We would never allow a daughter of ours to become a lawyer. Please tell me it isn't so. I don't think I could take it. [MOB21_Q] Oh Colonel, I can assure you Mercedes is never going to become a lawyer. Don't worry about it. [MOB21_R] Oh thank you, Tommy. Tommy, thank you. The shame would be unbearable. She is a lady, not a parasite, you know. [MOB21_S] I know, colonel. [MOB21_T] Anyway, Tommy, you must excuse me, the new minister of the interior has arrived. [MOB21_U] Many years ago, I killed his father in a failed coup so I must be polite. Good day, amigo. [MOB21_C] Tommy, it is always a struggle here. Excuse the poor line, we have just had another failed coup. [MOB21_D] The people are the most demanding mistress of all. [MOB21_E] So far, we have had three revolutions and four coups since I return from Vice City. [MOB21_F] Luckily, I have been promoted each time. [MOB21_J] Maybe everyone is humiliating me. [MOB21_L] but tell me Tommy, is it true? [MOB22_A] Tommy, you are proving very useful, my friend. [MOB22_B] Thanks, Cortez. What about my deal? [MOB22_C] Tommy, I am working tirelessly on your behalf to ensue we get to the bottom of this trench of stinking lies and deceit, [MOB22_D] you have my word on that, but in the meantime, [MOB22_E] please accept the esteemed thanks of my people for your work on our behalf. [MOB_25A] Tommy, Thomas it's Cortez. Look, the French are giving me all kinds of trouble, amigo. [MOB_25B] Damn hypocrites. They spend a hundred years stealing from poor countries and they call me a thief! [MOB_25C] I am going to need your help as soon as possible, amigo. [MOB_25D] So please hurry, Tommy, I need you, all right? I hate the damn French. [MOB_26A] Hello, Tommy? [MOB_26B] Yeah? [MOB_26C] It's Baker. I just wanted to say I really enjoyed the show. [MOB_26D] Me and the boys want to thank you, and remind you, [MOB_26E] you got our respect. Good day. Keep riding hard, son. [MOB_29A] Hello, is this Mr. Tommy Vercetti? [MOB_29B] Yes. [MOB_29C] Well, I hear through the vine of grapes you the man when someone got a vermin infestation. [MOB_29D] Maybe... [MOB_29E] Well, I got a real vermin infestation. Haitians everywhere. [MOB_29F] My name is Umberto Robina and I want you to meet me at the Cafe Robina as soon as you can, [MOB_29G] 'cause I tell you, these damn Haitians gone too far this time. [MOB_29H] Test [MOB_30A] Tommy, is Umberto Robina [MOB_30B] Hey, how's the cafe? [MOB_30C] Oh, wonderful. Incredible. Tommy, incredible. No wimps, Tommy, just real men, and the beautiful women! [MOB_30D] Anyway, I wanted to tell you, me and Papi, to us, you Cuban. [MOB_30E] You have proved yourself, man. You got big cojones. [MOB_30F] Well thank you, Umberto. Nobody's said that to me since I left jail. I'll see you around. [MOB_33A] Tommy, it's Phil, now cut out all the reminiscing crap and listen to me, you hear? [MOB_33B] Good. I got me some extra strength boomshine nearing fermentation time and I was wondering if you'd fancy having a shot. [MOB_33C] Seriously, Tommy, if you like a drink, or if you need to strip paint, this stuff'll make a man out of you. [MOB_33D] Sure did out of me, even though I can't see out of one eye. I'll be waiting for you, y'hear. [MOB_34A] Tommy, I really enjoyed working with you. Ain't had so much fun since the ridge in Nam, pal. [MOB_34B] Anyhows, you need anything, you call on me, you hear? [MOB_34C] I always remember those I served with, [MOB_34D] and I am sure I can help you out, you hear? [MOB_35A] Tommy, the wound is healing well. Funny thing is, [MOB_35B] I have fought in 6 battle zones and always walked away without a scratch, and now this! [MOB_35C] One armed Phil. Still, I got me a healthy selection of one handed fire power so I'll never be unarmed Phil, you hear. [MOB_35D] Any way son, cut out the sentimental crap and go buy yourself a drink, you hear! [MOB_36A] Tommy, it's Phil, I want to thank you for helping me out back there son, [MOB_36B] Damn Charlie, he'll always ambush you somewhere or other, [MOB_36C] Anyway the wound is healing well, and it means I'll no longer be defrauding the government on my disability check. [MOB_40A] Hey Tommy, it's Sonny. How's the sun tan? [MOB_40B] I ain't got no sun tan. [MOB_40C] Well, you ain't got my money, either, so I'm wondering to myself, [MOB_40D] what are you doing? So, tell me, Tommy, what are you doing? [MOB_40E] I'm looking for the money, Sonny. Don't worry. [MOB_40F] I am worrying, Tommy, that's my style, [MOB_40G] because I seem to have this problem in my life with unreliable people. [MOB_40H] Don't be an unreliable person, Tommy, please. [MOB_40I] Do us both a favor. I'm looking forward to hearing from you. [MOB_41A] Tommy, remember me? [MOB_41B] Hello Sonny. [MOB_41C] That's right, Sonny. We're old friends, [MOB_41D] You never write me, you never call. Don't you want to be friends no more? [MOB_41E] I've been busy trying to sort things out. You didn't give me a lot of support down here, Sonny. [MOB_41F] Oh, my fault is it? We'll I've heard you been busy all right. [MOB_41G] Busy killing drugs barons. Busy taking over. [MOB_41H] Don't forget about us, Tommy, 'cause I can assure you, I ain't forgotten about you. [MOB_42A] Tommy. [MOB_42B] Sonny. [MOB_42C] Obviously you are suffering from hearing problems, so I'll try again. [MOB_42D] Where's the goddamned money, where's the goddamned stuff, and where's my cut of your new action? [MOB_42E] You are making an idiot out of me, Tommy, and I'm not laughing yet. [MOB_43A] Tommy, Tommy, Tommy, I had Sonny on the phone, ok, are you with me?. [MOB_43B] I don't know about you, but there's something about a man threatening to murder my family [MOB_43C] which really scares the crap out of me. What are you going to do? [MOB_43D] Ken, take it easy. [MOB_43E] I AM calm, calm as a man can be when he's fearing for his life! [MOB_43F] Stay off the idiot fuel and look after yourself. [MOB_43G] No one's gonna take us out. I'll see you later. [MOB_43H] I am calm. Don't I sound calm? Must be impending death that is doing this to my voice. [MOB45_A] Tommy We gotta talk about stuff. [MOB45_B] What's the problem Lance? [MOB45_C] It's you, my friend, I feel you're not giving me a fair slice. [MOB45_D] And more than that, you been embarrassing me in front of the boys. I can't have that. [MOB45_E] Lance, it ain't like that. You've been making mistakes. [MOB45_F] Tommy, I'm not your message boy. I'm not your running boy. [MOB45_G] Lance, don't screw up, and we won't have any problems. I screw up, you can lay into me any time. [MOB45_H] Tommy, I've done everything for you, you treat me like a fool. Don't do that. [MOB45_I] Lance, I won't rip you off or stab you in the back, okay? [MOB45_J] Just take it easy. This is tough enough without you getting all emotional on me. [MOB45_K] Trust me. Do you hear me, do you hear me? [MOB45_L] I hear you, Tommy, but I can't take this much more. [MOB45_M] Lance, don't be like this. Now I'm warning you. [MOB45_N] Do you hear me? Just relax, take a few days off. Okay? I'll talk to you. [MOB46_A] Yo, Tommy! It's Lance. [MOB46_B] Yeah? [MOB46_C] Oh, nice to hear from you, Lance. Come on, man, be cool, be cool. [MOB46_D] I'm in the middle of something. What do you want? [MOB46_E] Nothing. Just to say, you know. Look Tommy, we can do this thing. [MOB46_F] You and me, no problem. You know what I mean? [MOB46_G] We're going to have to do it, 'cause otherwise, we're going to be dead, Lance. [MOB46_H] We're in too far now. But thanks for the call. I'll speak to you later. [MOB_47A] Tommy, Lance, we got big problems. Come down here. Right away. [MOB52_A] Hey Leo, I think we got a buyer for Diaz's merchandise. [MOB52_B] You gotta give him a ring, man, set up the deal, you know? [MOB52_C] Where are you now? [MOB52_D] You ok Leo? You sound kinda different. [MOB52_E] Just tell me where you are. [MOB52_F] Who the hell is this? Put Leo on, man! [MOB52_G] Leo's gone away for a while, he left me in charge. [MOB52_H] Screw you, man! [MOB54_A] Hiya Tommy! [MOB54_B] Hi Mercedes, howyadoin'? [MOB54_C] I got a new apartment up in Vice Point [MOB54_D] - thought you might want to drop by sometime. [MOB54_E] I'd love to. I'll catch you later. [MOB55_A] Tommy, it's me. [MOB55_B] Hi Mercedes. [MOB55_C] Tommy, I so bored, when we going to have some fun? [MOB55_D] What do you mean? [MOB55_E] Well, I know you're busy fighting and killing and corrupting people, [MOB55_F] but I just want to have some fun. So don't forget about me, you hear? [MOB56_A] Tommy, I hear you kill Ricardo Diaz. [MOB56_B] there was an unfortunate fire at his mansion. [MOB56_C] I think he burnt to death in an acrylic shirt. [MOB56_D] Tommy, I so proud of you. I knew you were a real man. [MOB56_E] He awful trouser stain of a man, you make me so proud to be your friend. [MOB56_F] No, I know you going to be busy trying to take over this town, [MOB56_G] but don't forget about me, you hear? [MOB57_A] It's merceedes. I no longer love you Tommy. [MOB57_B] I no longer do. Honest. 'cause you no longer nice to Mercedes. [MOB57_C] You no longer treat her like a lady. You ignore me and I hate you. [MOB57_D] I insist you come to see me right away! [MOB58_A] Tommy. [MOB58_B] Hey Mercedes. [MOB58_C] Hey indeed Mr. Tough Guy. I real angry with you Tommy. [MOB58_D] Never make me hang out with Jezz Torrent again. [MOB58_E] He is pathetic. Half way through he starts crying about his doggie [MOB58_F] that died when he was 7 years old and that his mommy never loved him. [MOB58_G] And Tommy. He wear a wig and a bra in private. [MOB58_H] I not very happy with you! [MOB59_A] Ooh Tommy, its Mercedes. [MOB59_B] I just want to say, I have so much fun on that film set. [MOB59_C] Anything else you have like that, you let me know. [MOB59_D] I really mean that. I always wanted to be an actress. [MOB59_E] I think I learn a lot about the dramatic process. [MOB59_F] It so enlightening! Thank you. Thank you. I see you real soon. Adios. [MOB_99] Get to the payphone at location. [MOB_98] Get to the payphone at location. [MOB_97] Get to the payphone at location. [MOB_96] Get to the payphone at location. [MOB_95] Get to the payphone at location. [A_TIME] +~1~ seconds [DODO_FT] You flew for ~1~ seconds! [GA_8] Use the detonator to activate the bomb. [GA_10] Nice one. Here's your $~1~ [GA_11] We got these wheels already. It's worthless to us! [GA_12] Bomb armed [GA_13] Delivered like a pro. Complete the list and there'll be a bonus for you. [GA_14] All the cars. NICE! Here's a little something. [GA_15] Hope you like the new color. [GA_16] Respray is complementary. [GA_19] We're not interested in that model. [GA_20] We got more of these than we can shift. Sorry man, no deal. [CHASE] Highest media attention [CHASE1] Ignored [CHASE2] Boring [CHASE3] Vaguely interesting [CHASE4] Local paper Page 7 [CHASE5] Front page of local paper [CHASE6] Vice Courier Page 2 [CHASE7] Vice Courier Front page [CHASE8] Local TV 3am [CHASE9] Local TV news [CHASE10] Local TV Live coverage [CHASE11] UFA Today page 12 [CHASE12] UFA Today page 4 [CHASE13] Picture in UFA Today [CHASE14] National TV 4am [CHASE15] National TV news [CHASE16] National TV live coverage [CHASE17] International news [CHASE18] National crisis [CHASE19] International crisis [CHASE20] World event [CHASE21] Stuff of legends [CR_1] Crane cannot lift this vehicle. [PU_MONY] You don't have enough cash. [CO_ALL] You got all of them. Here's a little something... [FEM_ON] ON [FEM_OFF] OFF [FEM_YES] Yes [FEM_NO] No [FEC_NA] NA [FEC_CWL] Cycle Weapon left [FEC_CWR] Cycle Weapon right [FEC_LOF] Look forward [FEC_TAR] Target [FEC_MOV] Movement [FEC_CAM] Camera modes [FEC_PAU] Pause [FEC_ENV] Enter vehicle [FEC_JUM] Jump [FEC_ATT] Attack or Fire weapon [FEC_RUN] Run [FEC_FPC] First person camera [FEC_LL] Look left [FEC_LB] Look behind [FEC_LR] Look right [FEC_HOR] Horn [FEC_VES] Vehicle control [FEC_BRA] Brake or Reverse [FEC_HAB] Hand brake [FEC_CAW] Car weapon [FEC_ACC] Accelerate [FEC_CCF] Configuration [FEC_CF1] Setup 1 [FEC_CF2] Setup 2 [FEC_CF3] Setup 3 [FEC_CF4] Setup 4 [FEC_CDP] Controller Display [FEC_ONF] On foot [FEC_INC] In car [FEC_VIB] Vibration [FEL_ENG] English [FEL_FRE] French [FEL_GER] German [FEL_ITA] Italian [FEL_SPA] Spanish [FED_DBG] Menu Debug [FED_RID] Reload IDE [FED_RIP] Reload IPL [FED_PAH] Parse Heap [FED_DFL] CTheScripts::DbgFlag [FED_DLS] Big White Debug Light Switched [FED_SPR] Show Ped Road Groups [FED_SCR] Show Car Road Grups [FED_SCZ] Show Cull Zones [FED_DSR] Debug Streaming Requests [FED_SCP] gbShowCollisionPolys [PL_STAT] Player stats [PE_WAST] People you've wasted [PE_WSOT] People wasted by others [TM_BUST] Times busted [GNG_WST] Gang members wasted [DED_CRI] Criminals wasted [PER_COM] Percentage completed [KGS_EXP] Kgs of explosives used [ACCURA] Accuracy [ST_WEAP] Weapon Budget [ST_PROP] Property Budget [ST_AUTO] Auto Repair and Painting Budget [ST_PHOT] Photographs Taken [ST_LOAN] Visits From Loan Sharks [ST_STOR] Stores Knocked Off [ST_MOVI] Movie Stunts [ST_PIZZ] Pizza's Delivered [ST_GARB] Garbage Pickups Made [TOP_SHO] Top Shooting Range Score [SHO_RAN] Shooting Range Rank [SEAGULL] Seagulls Sniped [PROPOWN] Property Owned [ST_TIME] Playing Time [ST_FTIM] Flight hours [ST_PRAN] Pilot Ranking [ST_RAN0] Learner [ST_RAN1] Navigator [ST_RAN2] Co Pilot [ST_RAN3] Junior [ST_RAN4] Competent [ST_RAN5] Senior [ST_RAN6] Ace [ST_RAN7] Red baron [ST_DRWN] Fishes Fed [ST_FASH] Fashion Budget [ST_DAMA] Property Destroyed [TM_DED] Hospital visits [DAYSPS] Days passed in game [NUMSHV] Safehouse visits [MXCARD] Max. INSANE Jump dist. (ft) [MXCARJ] Max. INSANE Jump height (ft) [MXCARDM] Max. INSANE Jump dist. (m) [MXCARJM] Max. INSANE Jump height (m) [MXFLIP] Max. INSANE Jump flips [MXJUMP] Max. INSANE Jump rotation [BUL_FIR] Bullets fired [BUL_HIT] Bullets that hit [SPRAYIN] Sprayings [BSTSTU] Best INSANE stunt so far [INSTUN] Insane stunt [PRINST] Perfect insane stunt [DBINST] Double insane stunt [DBPINS] Perfect double insane stunt [TRINST] Triple insane stunt [PRTRST] Perfect triple insane stunt [QUINST] Quadruple insane stunt [PQUINS] Perfect quadruple insane stunt [NOSTUC] No INSANE stunts completed [NOUNIF] Unique Jumps completed [NMISON] Mission attempts [PASDRO] Passengers dropped off [MONTAX] Cash made in taxi [DAYPLC] Daily police spending [CRIMRA] Criminal rating: [STPR_1] The Malibu [STPR_2] Print Works [STPR_3] Film Studio [STPR_4] Ice Cream Factory [STPR_5] Car Showroom [STPR_6] Taxi Company [STPR_7] Boatyard [SET1EN] SetUp 1. Enabled [GMSAVE] Save Game [FEDS_TB] Back [FEST_OO] out of [FEC_TUC] Turret control [FEC_RS3] Radio station cycle (L3 button) [FEC_HO3] Horn (L3 button) [C_FAIL] Vigilante mission ended! [C_ESCP] ~r~The suspect has escaped! [C_VIGIL] VIGILANTE BONUS!! [HEAL_A] Your ~h~health~w~ is displayed in orange in the top right of the screen. [WRONGCD] Incorrect disc. Please insert correct disc. [NOCD] The disc tray is empty. Please insert disc. [OPENCD] The disc tray is open. Please close the disc tray. [CDERROR] Error reading the Grand Theft Auto: Vice City DVD [RESTART] Starting new game [GA_3] No more freebies. $100 to respray! [GA_1] Whoa! I don't touch nothing THAT hot! [GA_1A] Come back when you're not so busy... [HELP9_C] Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to ~h~fire~w~ the sniper rifle. [TAXI2] ~r~You're out of time! [PAGEB13] Health delivered to hideout [PAGEB14] Adrenaline delivered to hideout [FESZ_CA] Cancel [FES_NGA] New Game [FES_CAN] Cancel [FESZ_QL] All unsaved progress in your current game will be lost. Proceed with loading? [FESZ_QD] Proceed with deleting this save game? [FESZ_QO] Proceed with overwriting this save game? [T4X4_1] 'PCJ Playground' [BMX_1] 'Trial by Dirt' [BMX_2] 'Test Track' [BMXFAIL] ~r~You failed to set a new record! [BMX_REC] ~g~New Record Set:~1~ !! [T4X4_3] 'GRIPPED!' [MM_1] 'CONE CRAZY' [T4X4_F] ~r~You bailed! Too tough for you?! [LANDSTK] Landstalker [IDAHO] Idaho [STINGER] Stinger [LINERUN] Linerunner [PEREN] Perennial [SENTINL] Sentinel [RIO] Rio [PATRIOT] Patriot [FIRETRK] Firetruck [TRASHM] Trashmaster [STRETCH] Stretch [MANANA] Manana [INFERNS] Infernus [VOODOO] Voodoo [PONY] Pony [MULE] Mule [CHEETAH] Cheetah [AMBULAN] Ambulance [FBICAR] FBI Washington [MOONBM] Moonbeam [ESPERAN] Esperanto [TAXI] Taxi [WASHING] Washington [BOBCAT] Bobcat [WHOOPEE] Mr. Whoopee [BFINJC] BF Injection [HUNTER] Hunter [POLICAR] Police [ENFORCR] Enforcer [SECURI] Securicar [BANSHEE] Banshee [PREDATR] Predator [BUS] Bus [RHINO] Rhino [BARRCKS] Barracks OL [CUBAN] Cuban Hermes [HELI] Helicopter [DODO] Dodo [COACH] Coach [CABBIE] Cabbie [STALION] Stallion [RUMPO] Rumpo [RCBANDT] RC Bandit [ROMERO] Romero's Hearse [PACKER] Packer [ADMIRAL] Admiral [SQUALO] Squalo [SEASPAR] Sea Sparrow [PIZZABO] Pizza Boy [GANGBUR] Gang Burrito [TROPIC] Tropic [SPEEDER] Speeder [REEFER] Reefer [FLATBED] Flatbed [YANKEE] Yankee [CADDY] Caddy [ZEBRA] Zebra Cab [TOPFUN] Top Fun [SKIMMER] Skimmer [PCJ600] PCJ 600 [PHOENIX] Phoenix [FAGGIO] Faggio [FREEWAY] Freeway [RCBARON] RC Baron [RCRAIDE] RC Raider [GLENDAL] Glendale [OCEANIC] Oceanic [SANCHEZ] Sanchez [SPARROW] Sparrow [LOVEFIS] Love Fist [COASTG] Coast Guard [DINGHY] Dinghy [HERMES] Hermes [SABRE] Sabre [SABRETU] Sabre Turbo [WALTON] Walton [REGINA] Regina [COMET] Comet [DELUXO] Deluxo [BURRITO] Burrito [SPAND] Spand Express [MARQUIS] Marquis [BAGGAGE] Baggage Handler [KAUFMAN] Kaufman Cab [COASTMA] Coastguard Maverick [MAVERIC] Maverick [RANCHER] Rancher [FBIRANC] FBI Rancher [VIRGO] Virgo [GREENWO] Greenwood [HOTRING] Hotring Racer [BLISTAC] Blista Compact [FEST_DF] Dist. traveled on foot (miles) [FEST_DC] Dist. traveled by car (miles) [FESTDFM] Distance traveled on foot (m) [FESTDCM] Distance traveled by car (m) [TOT_DIS] Total distance traveled (miles) [TOTDISM] Total distance traveled (m) [DISTHEL] Dist. traveled by helicopter (miles) [DISTHEM] Distance traveled by helicopter (m) [DISTBOA] Dist. traveled by boat (miles) [DISTBOM] Distance traveled by boat (m) [FEST_LS] People saved in an Ambulance [FEST_CC] Criminals killed on Vigilante Mission [FEST_FE] Total fires extinguished [FEST_RP] Rampages passed [FEST_MP] Missions passed [FEST_BB] Bling-bling Scramble: [FEST_H0] Most checkpoints [FEST_GC] Gang Cars Totaled: [FEST_H1] Diablo destruction [FEST_H2] Mafia Massacre [FEST_H3] Casino Calamity [FEST_H4] Rumpo Wrecker [USJ] UNIQUE STUNT BONUS! [RATNG1] Upstanding Citizen [RATNG2] Nobody Special [RATNG3] Litterer [RATNG4] Shoplifter [RATNG5] Vandal [RATNG6] Do boy [RATNG7] Pickpocket [RATNG8] Clepto [RATNG9] Snitch [RATNG10] Rat [RATNG11] Leece [RATNG12] Scam Artist [RATNG13] Trickster [RATNG14] Numbers Runner [RATNG15] Hustler [RATNG16] Bully [RATNG17] Riff-Raff [RATNG18] Scalawag [RATNG19] Ruffian [RATNG20] Outlaw [RATNG21] Thug [RATNG22] Drop Man [RATNG23] SA Goon [RATNG24] Goon [RATNG25] Jailbird [RATNG26] Ex-Con [RATNG27] Felon [RATNG28] Bag Man [RATNG29] Wiseguy [RATNG30] Wheelman [RATNG31] Hired Muscle [RATNG32] Hatchetman [RATNG33] Headhunter [RATNG34] Enforcer [RATNG35] Ronin [RATNG36] Fixer [RATNG37] Hitman [RATNG38] Associate [RATNG39] Butcher [RATNG40] Cleaner [RATNG41] Assassin [RATNG42] Consigliere [RATNG43] Made Man [RATNG44] Right-Hand Man [RATNG45] Executioner [RATNG46] Lieutenant [RATNG47] Underboss [RATNG48] Capo [RATNG49] Boss [RATNG50] Kingpin [RATNG51] Don [RATNG52] Godfather [PAGE_00] . [WELCOME] WELCOME TO [TSCORE] EARNINGS: $~1~ [PBOAT_2] { reVC update } Press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to fire the boat cannons. [HJSTAT] Distance: ~1~.~1~m Height: ~1~.~1~m Flips: ~1~ Rotation: ~1~_ [HJSTATW] Distance: ~1~.~1~m Height: ~1~.~1~m Flips: ~1~ Rotation: ~1~_ And what a great landing! [ATUTOR] Press the ~h~~k~~TOGGLE_SUBMISSIONS~ ~w~button to toggle Paramedic missions on or off. [FEST_HA] Highest Paramedic Mission level [C_KILLS] CRIMINALS KILLED: ~1~ [HJSTATF] Distance: ~1~ft Height: ~1~ft Flips: ~1~ Rotation: ~1~_ [HJSTAWF] Distance: ~1~ft Height: ~1~ft Flips: ~1~ Rotation: ~1~_ And what a great landing! [CINCAM] Cinematic Camera [RC4] 'RUMPO RAMPAGE' [LEGAL] ~g~Eliminate the criminal threat! [GA_2] New engine and paint job. The cops won't recognize you! [HELP15] When on foot press the ~h~~k~~PED_LOOKBEHIND~ ~w~button to ~h~look behind~w~. [FEC_LB4] Look behind (R3 button) [PERPIC] Hidden Packages found [CO_ONE] Hidden Package ~1~ of ~1~ [GA_21] You cannot store any more cars in this garage. [CHEAT1] Cheat activated [CHEAT2] Weapon cheat [CHEAT3] Health cheat [CHEAT4] Armor cheat [CHEAT5] Wanted level cheat [CHEAT6] Money cheat [CHEAT7] Weather cheat [USJ_ALL] ALL UNIQUE STUNTS COMPLETED! [JAN] Jan [FEB] Feb [MAR] Mar [APR] Apr [MAY] May [JUN] Jun [JUL] Jul [AUG] Aug [SEP] Sept [OCT] Oct [NOV] Nov [DEC] Dec [DEFDT] --:---:---- --:--:-- [BONUS] ~g~BONUS $~1~ [HORN1] Press the ~h~~k~~VEHICLE_HORN~ ~w~button to activate the ~h~horn. [HORN2] Press the ~h~~k~~VEHICLE_HORN~ ~w~button to activate the ~h~horn [HORN3] Press the ~h~~k~~VEHICLE_HORN~ ~w~button to activate the ~h~horn [FEC_EXV] Enter and exit vehicle [TAXI_M] 'TAXI DRIVER' [COP_M] 'VIGILANTE' [FIRE_M] 'FIREFIGHTER' [AMBUL_M] 'PARAMEDIC' [HJ_IS] INSANE STUNT BONUS: $~1~ [HJ_PIS] PERFECT INSANE STUNT BONUS: $~1~ [HJ_DIS] DOUBLE INSANE STUNT BONUS: $~1~ [HJ_PDIS] PERFECT DOUBLE INSANE STUNT BONUS: $~1~ [HJ_TIS] TRIPLE INSANE STUNT BONUS: $~1~ [HJ_PTIS] PERFECT TRIPLE INSANE STUNT BONUS: $~1~ [HJ_QIS] QUADRUPLE INSANE STUNT BONUS: $~1~ [HJ_PQIS] PERFECT QUADRUPLE INSANE STUNT BONUS: $~1~ [FESZ_LS] Load Successful. [HELI_1A] Test your skills with the Sparrow, see how quickly you can complete the course. [HELI_1B] Course Complete! $ ~1~ [HELIODD] Helicopter odd jobs [LAW] THE LAWYER MISSIONS [LAW1_1] ~g~Go get some new threads from Rafael's clothes shop. [LAW4_6] Burn the management! [LAW4_7] Kill the bosses! [LAW4_8] Fight, Fight, Fight, Fight. [LAW4_9] More Holiday, Less Work! [LAW4_11] Fight! Fight! Fight! Fight! [LAW4_12] Viva la revolution! [GENERAL] THE COLONEL MISSIONS [GEN3_4] Tommy Vercetti. Let's go... [GEN3_13] What's the matter with you man?! Get on the roof across the yard before they turn up! [GEN3_17] Sheeit! You trying to kill me?! [GEN3_21] ~g~He got Diaz's money! Chase him down and get it back! [GEN3_24] ~r~Diaz died! You failed to protect him! [GEN3_26] ~r~You shot Diaz! [GEN3_27] ~r~You shot Diaz's bodyguards! [GEN3_31] ~g~Now go to the drop off and watch over Diaz. [GEN3_32] ~g~Get to your vantage point on the roof of the building opposite Lance. [COKE] THE COKE BARON MISSIONS [COK1_3] Hope you fall and break your neck! [COK1_6] I'm sick of these pricks. [COK2_7] See those marker boys? Try taking out the lights! [COK2_10] You sure is better at shooting than talking. [COK2_11] Thanks. You're a real charmer yourself. [COK2_12] I Know, Tommy. [COK2_18] You cool with Kenny Loggins [COK2_19] Hell, I love this record [COK2_26] ~r~You killed Lance! [COK3_1] Don't shoot, dude! [COK3_2] What's in this stuff? [COK3_3] He's taking the boat. Bummer. [COK3_4] Help! Some square's stealing the boat, man! [COK4_W] Uugghh! That's the last of them. [COK4_X] I'm going to start her up. [COK4_Y] I think we've got some new friends. [COK4_2] Yeah. [COK4_6] Do you know where we're going? [COK4_7] Are we lost? [COK4_8] We got some competition! [COK4_9] Take 'em out! [COK4_9A] It's time for the Lance Vance Dance! [COK4_10] They're matchwood! And fish food. [COK4_11] We made it! Those other boats ain't VIP class. [COK4_17] They're getting desperate! [COK4_18] My damn feet are wet! WE'RE TAKING ON WATER! [COK4_21] Bridge coming up! [COK4_22] Bail out, she's about to blow! [COK4_23] Good shooting. [COK4_29] ~r~You killed Lance! [ASS1_6] Go on Tommy, I'll be ok! [ASS1_7] Eat this, you murdering bastards!! [ASS1_8] I'm pinned down! [ASS1_9] I got you covered Tommy! [ASS1_10] Hey, this is a real nice herbaceous border [ASS1_11] Hey Tommy, can my room have a view of the bay? [ASS1_12] Beautiful high ceilings in here... [ASS1_3] Lance! I need cover! [ASS1_4] Diaz must be inside! [ASS1_5] Lance! [TAXWAR] TAXI WAR MISSIONS [NOTAXI] ~g~You need a Kaufman Cab to activate this mission. [TAXW1_5] ~g~You need to be in a Kaufman cab! [TAX2_4] Go on, Tommy. [TAX2_5] Beat the hell out of him. [TAX2_6] He hasn't even got a license. [TAX2_7] Damn limo services. [TAXW3_1] ~g~Go and pick up Mercedes. [RACE1] ~g~3..2..1.. GO GO GO! [RACE2] ~g~3 [RACE3] ~g~2 [RACE4] ~g~1 [RACE5] ~g~GO! [FIRST] ~b~1st [SECOND] ~b~2nd [THIRD] ~b~3rd [FOURTH] ~b~4th [RACETM] ~b~RACE TIME: ~1~:~1~ [RACETM2] ~b~RACE TIME: ~1~:0~1~ [RACEFA] ~r~You failed to win the race! [TEX1_5] ~r~He got away! [SEG3_2] ~g~Get to the van that contains the RC Raider and remote timed bombs. [SEG3_3] ~g~You must use the RC RAIDER to transport 4 bombs to 4 target zones on the building site. [SERG3_5] ~g~You can only carry one bomb at a time and cannot pick up successfully planted bombs. [SEG3_7] ~g~Once you have dropped the FIRST bomb successfully in a target zone the detonation timer will start; you must then drop all the bombs within this time period. [SEG3_8] ~g~All 4 bombs must be located at the 4 target zones to pass the mission and demolish the building. [SEG3_9] ~g~You hit the target! 3 more to go. [SEG3_10] ~g~You hit the target! 2 more to go. [SEG3_11] ~g~You hit the target! Just 1 more to go! [SEG3_12] ~r~You missed the target! Go get a bomb! [SEG3_13] ~g~Drop the bomb at a target zone. [SEG3_14] ~r~You ran out of time and failed to demolish the building. [SEG3_15] ~r~Your RC Raider has been destroyed! How you gonna transport the bombs now? [AVERY] AVERY MISSIONS [ASM] ASSASSIN MISSIONS [ASM_1] ASSASSIN MISSION 1 [ASM1_1] ~g~Mr. Teal, your help in eradicating those out-of-towners was invaluable to business. I have more work for you with a more 'hands-on' approach. Your next job is taped under the phone. [ASM1_2] ~g~Get to the payphone outside the Mall in Washington. [ASM1_3] ~g~Carl Pearson, Pizza Delivery Man. He must not complete his deliveries. [ASM1_4] ~g~Kill the Pizza Delivery Man before he completes his deliveries. [ASM_2] ASSASSIN MISSION 2 [ASM_3] ASSASSIN MISSION 3 [ASM3_A] TEXT NO LONGER NEEDED [ASM3_B] TEXT NO LONGER NEEDED [ASM3_1] ~g~Go and get the weapon Mr. Black has left for you. [ASM3_2] ~g~Don't get too close to the target or he may spot you! [ASM3_3] ~g~For a quick safe kill find a secluded spot nearby with a clear view of your target. [ASM3_4] ~g~He's seen you! Better waste him any way you can! [ASM3_5] ~g~Marcus Hammond is working on an advertising board in Washington. [ASM3_6] ~g~Franco Carter is working for DBP Security off Ocean Drive. [ASM3_7] ~g~Dick Tanner is situated near the Jewelry shop in Vice Point. [ASM3_8] ~g~Nick Kong is situated near Washington Beach. [ASM3_9] ~g~Stuntman Driver is situated in Washington. [ASM3_10] ~r~You failed to kill them all. [ASM_4] ASSASSIN MISSION 4 [ASM4_1] ~g~Go get the rifle left for you in the foliage outside the airport terminal. [ASM4_2] ~g~Don't miss your target or you may alert his bodyguards, and remember keep your distance so he does not spot you. [ASM4_3] ~g~Watch the woman on the balcony above the check-in desks inside the airport terminal. DO NOT KILL HER. [ASM4_4] ~g~Kill the man she hands the briefcase to but only AFTER HE PICKS IT UP. Then retrieve the briefcase and take it to Ammu-Nation in Downtown. [ASM4_5] ~g~Get the briefcase! [ASM4_6] ~g~Take the briefcase to Ammu-Nation in Downtown. [ASM4_7] ~r~You killed the woman, you fool! [ASM4_8] ~r~The target heard you firing your weapon! The deal is off! [ASM4_9] ~r~The target has boarded his flight! [ASM4_11] ~r~The target has seen you! The deal is off! [ASM4_13] ~g~He's spotted you and is making a run for it, nail him and get the briefcase! [ASM4_14] ~g~The distance bar in the upper right of the screen gives you an indication to how close you are to your target do not let it fill or he will see you. [ASM_5] ASSASSIN MISSION 5 [KICK] KICKSTART [KICK1_3] ~g~Number of times foot put down: ~1~ [KICK1_4] ~g~Time penalty: ~1~ seconds [BANK] BANKJOB MISSIONS [BANK1] BANKJOB MISSION 1 [BANK2] BANKJOB MISSION 2 [BJM2_21] ~g~Hit as many targets as you can while your ammo lasts. [BANK3] BANKJOB MISSION 3 [BJM3_1] ~g~Get a fast car and get to the starting grid. [BNK4_2A] Boys at the car lot did a great job on this baby. [BNK4_3G] Oh crap, now the cops are onto us! [BNK4_3H] - and we're not even there yet. [BNK4_3K] We'll have to lose the cops first... [BNK4_3L] Christ Tommy, you trying to kill us all!? [BNK4_3N] Everything I care about gets trashed! [BNK4_26] Hot damn! Here they come! [BNK4_32] Use explosives to open the deposit boxes! [BNK4_36] Where's Cam? [BNK4_37] History... [BNK4_38] That's the last of them. GO! GO! GO! [BNK_39] Shit! Where's Hilary? [BK4_40A] I'll give him abandonment issues! [BNK4_42] Hey guys! Get in! I got you covered! [BNK4_43] I've got our asses covered, DRIVE! [BNK4_44] We made it! We're rich! RICH! [BNK4_45] Shame Cam didn't make it, he was a good guy! [BNK4_46] Yeah. Still... more for us! [BNK4_47] Damned straight! YEEEEHAAAH! [BNK4_48] Tommy, would you like a massage? [BNK4_49] Well, Hi there, Mercedes! Yeah, I'm a little tense... [BNK450A] What'd I tell you Tommy? What'd I tell you? Bent SWAT better watch out when Kent Paul is in town. [BNK450B] Come on, gimme a bigger slice, mate, c'mon. I gotta get some new threads. [BNK4_51] You look fine to me. [KENT] KENT PAULS MISSIONS [KENT1] KENT PAUL MISSION1 [COUNT] COUNTERFEITING MISSIONS [COUNT1] COUNTERFEITING MISSION 1 [COUNT2] COUNTERFEITING MISSION 2 [BIKE] THE BIKER GANG MISSIONS [BIKE1] BIKER MISSION 1 [BIKE2] BIKER MISSION 2 [BIKE3] BIKER MISSION 3 [GOAWAY1] ~g~Come back when you have finished the Haitian gang missions. [HAIT] THE HAITIAN GANG MISSIONS [HAIT1] HAITIAN MISSION 1 [HAIT2] HAITIAN MISSION 2 [HAIT3] HAITIAN MISSION 3 [HAM3_6] ~g~Use the sniper rifle I have left to accomplish your task. [ROCK] THE ROCKBAND GANG MISSIONS [ROK1_4] ~g~Ok, I think this is what you were after... [ROK1_1E] ~g~It'll cost more than you've got! [ROK1_1F] ~g~Come back when you got the money. [RBM2_6] ~g~Wow! She's a bloke, stop him! [ROCK3] ROCKBAND MISSION 3 [RBM3_5] ~g~Take Love Fist to the venue. [CUBANM] THE CUBAN GANG MISSIONS [CUBAN1] CUBAN MISSION 1 [CUBAN2] CUBAN MISSION 2 [CUB2_10] ~r~You are supposed to be killing Haitians, not Cubans. [CUBAN3] CUBAN MISSION 3 [CUBAN4] CUBAN MISSION 4 [PROT] PROTECTION MISSIONS [PORN] PORN MISSIONS [PORN1] PORN MISSION 1 [POR1_03] ~r~Candy is dead! [PORN2] PORN MISSION 2 [PORN3] PORN MISSION 3 [PORN4] PORN MISSION 4 [PHIL] PHIL MISSIONS [PHIL1] PHIL MISSION 1 [PHIL2] PHIL MISSION 2 [PIZ1_A] PIZZA BOY MISSION [CNTBUY1] Printworks purchased: $~1~ [CARBUY] Car Showroom purchased: $~1~ [PORNBUY] Film Studio purchased: $~1~ [ICEBUY] Ice Cream Factory purchased: $~1~ [TAXIBUY] Taxi Firm purchased: $~1~ [BANKBUY] The Malibu purchased: $~1~ [BOATBUY] Boatyard purchased: $~1~ [PRNT_NO] You cannot buy the Print Works at this time, come back later. [CAR_NO] You cannot buy the Car Showroom at this time, come back later. [PORN_NO] You cannot buy the Film Studio at this time, come back later. [ICE_NO] You cannot buy the Ice Cream Factory at this time, come back later. [TAXI_NO] You cannot buy the Taxi Company at this time, come back later. [BANK_NO] You cannot buy The Malibu at this time, come back later. [BOAT_NO] You cannot buy the Boatyard at this time, come back later. [COL2_6] Freeze, imperialist American pig! [COL2_6B] Zat iz propertay of ze government Francais. [COL2_6C] 'And eet over! [COL3_A] Thomas I appreciate you coming [COL3_B] Forgive me for getting straight to business, [COL3_C] Diaz has asked me to oversee a minor business transaction. [COL3_D] Let's hope it goes better than last time. [COL3_E] Which is why I thought of you, my friend. [COL3_F] I've dropped some protection at the multistory carpark. [COL3_G] Pick it up then go and watch over Diaz's men at the drop off. [COL4_2] Don't know Sir! [COL4_5] Sir, Yes Sir! [COL4_10] Lets go eat some doughnuts. [COL4_16] Sir! Moving vehicle Sir! [COL4_25] Vehicle self destruct initiated! [COL5_6] Mercedes, that girl will be the death of me. [COL5_8] Damn cockroach! [COL5_5] Die French peegs! [CNT2_1] Kill him. [CNT2_2] Get the Plates! [CNT2_3] Protect the courier! [FINKILL] Ok boys, kill him! [FIN_1A] Come here you double-crossing piece of shit! [FIN_1B] You're going down, you back stabbing prick! [FIN_1C] This is the last dance for lance vance! [FIN_2B] Oh you think so! [FIN_2C] I said I had enough of that at school! [FIN_3] No one to cover your ass now, eh Tommy? [FIN_4] You're history, Tommy, history [FIN_5] You picked the wrong side, Lance... [FIN_6] Sonny's up with the safe and MY money... [FIN_10] Sonny? SONNY! I'm coming for ya! [FIN_11A] You took fifteen years from me Sonny... [FIN_11B] And now I'm gonna make you pay! [FIN_12A] You still don't get it do you! [FIN_12B] I OWN you, Tommy. [FIN_12C] Those fifteen years were mine to spend! [FIN_13] Get him boys, he never understood a thing. [RACES_4] 3 [RACES_5] 2 [RACES_6] 1 [RACES_7] GO! [RACES_9] Time: ~1~:~1~ [RACES] TIME: [RACES17] New best time: ~1~:~1~ [RACES18] YOU HAVE WON: $~1~ [RACES20] New best time: ~1~:0~1~ [RACES21] Time: ~1~:0~1~ [RCH1_1] NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED [RCH1_2] ~g~The CHECKPOINTS are scattered throughout the airport. [RCH1_3] NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED NO LONGER NEEDED [RCH1_5] Time: [RCRC1_2] ~g~Get to the starting grid now! [RCRC1_4] ~g~3 [RCRC1_5] ~g~2 [RCRC1_6] ~g~1 [RCRC1_7] ~g~GO! [RCRC1_8] ~g~Race time: ~1~ seconds [RCPL1_1] ~g~Compete in a CHECKPOINT RACE with 3 other RC Baron's [RCPL1_2] ~g~You must go through the ~o~CENTRE CORONA ~g~to successfully pass a checkpoint. [RCPL1_3] ~g~Get to the starting grid now! [ICC1_O] What is wrong with you?? [FEA_2SP] 2 Speakers [FEA_4SP] More than 2 speakers [FEA_EAR] Headphones [FEA_NAH] NO AUDIO HARDWARE [FET_APP] APPLY [FES_SKN] SKIN NAME [FES_DAT] DATE [FES_SET] Use Skin [FET_DEF] Restore Defaults [FESZ_QZ] Are you sure you want to save this game? [FES_SCG] Save the current game? [FES_LCG] Load the game and continue playing? [FEC_FIR] Fire [FEC_NWE] Next weapon [FEC_PWE] Previous weapon [FEC_FOR] Forward [FEC_BAC] Backwards [FEC_LEF] Left [FEC_RIG] Right [FEC_ZIN] Zoom in [FEC_ZOT] Zoom out [FEC_EEX] Enter+exit [FEC_RAD] Radio [FEC_SUB] Sub-mission [FEC_CMR] Change camera [FEC_JMP] Jump [FEC_SPN] Sprint [FEC_HND] Handbrake [FEC_LOL] Look left [FEC_LOR] Look right [FEC_NTR] Next target [FEC_PTT] Previous target [FEC_LBA] Look behind [FEC_CEN] Center camera [FET_CFT] ON FOOT [FET_CCR] IN CAR [FET_CAC] ACTION [FEC_IBT] - [FEC_MXO] MXB1 [FEC_MXT] MXB2 [FEC_UNB] UNBOUND [FEC_TFL] Look left+Turret L [FEC_TFR] Look right+Turret R [FEC_MWF] MS WHEEL UP [FEC_MWB] MS WHEEL DN [FEC_ORR] or [FEC_NUS] NOT USED [FEC_LUD] Look Up [FEC_LDU] Look Down [FEC_CMP] COMBO: LOOK L+R [LAW_1A] law_1a [LAW_1B] law_1b [LAW_2A] law_2a [LAW_2B] law_2b [FEH_STA] STATS [FEH_LOA] LOAD [FEH_CON] CONTROLS [FEH_AUD] AUDIO [FEH_LAN] LANGUAGE [FEH_SGA] START NEW GAME [FEO_CON] Controller Setup [FEO_AUD] Audio Setup [FEO_DIS] Display Setup [FEO_LAN] Language Setup [FEO_PLA] Player Setup [FEA_OUT] Output [FEA_ST] Stereo [FEA_DTS] DTS [FEA_RSS] Radio station [FEA_NON] RADIO OFF [FEA_FM0] WILDSTYLE [FEA_FM1] FLASH FM [FEA_FM2] KCHAT [FEA_FM3] FEVER 105 [FEA_FM4] VROCK [FEA_FM5] VCPR [FEA_FM7] EMOTION 98.3 [FEA_FM8] WAVE 103 [FED_BRI] Brightness [FED_TRA] Trails [FED_SUB] Subtitles [FED_WIS] Wide Screen [FED_POS] Screen Position [FEP_RES] Resume [FEP_STG] Start Game [FEP_STA] Stats [FEP_BRI] Briefs [FEP_OPT] Options [FEP_QUI] Quit Game [FES_LOA] Load game [FES_DEL] Delete game [FEC_CSU] Controller Setup [FEC_RED] Redefine Controls [FEC_MOU] Mouse Settings [DISTGOL] Dist. traveled by golf cart (miles) [DISTGOM] Distance traveled by golf cart (m) [ST_FAVR] Favorite radio station [ST_WSTR] least favorite radio station [ST_FAVV] Favorite vehicle [ST_STAR] Total number of wanted stars attained [ST_HEAD] Number of headshots [ST_GANG] Least favorite gang [ST_STGN] Total number of wanted stars evaded [TYREPOP] Tires popped with gunfire [TYRESLA] Tires slashed with a blade [ST_BRK] Number of bloodring kills [ST_LTBR] Longest time in bloodring (secs) [ST_GNG1] Cubans [ST_GNG2] Haitians [ST_GNG3] Streetwannabe's [ST_GNG4] Diaz's gang [ST_GNG5] Security guards [ST_GNG6] Biker gang [ST_GNG7] Vercetti gang [ST_GNG8] Golfers [FEA_FM6] ESPANTOSO [ST_ASSI] Assassination Contracts Completed [DISTBIK] Dist. traveled by bike (miles) [DISTBIM] Distance traveled by bike (m) [HOTEL] Ocean View [KICK1_9] CHECKPOINTS: [FIN_B6] You do not have enough money to start this mission. [TEX3_9] ~g~Pickup a bomb by maneuvering the RC helicopter next to it. [HELP22] Go to the green house blip on the radar. [FES_FMS] Format Successful. Select OK to continue. [FES_SSC] Save Successful. Select OK to continue. [FES_DSC] Delete Successful. Select OK to continue. [FESZ_QC] Proceed with overwriting this corrupted save game? [FES_CHE] Warning! One or more cheats have been activated. This may affect your save game. It is recommended that you do not save this game. [FET_SG] SAVE GAME [FEH_BRI] BRIEF [FEH_DIS] DISPLAY [FEH_MAP] MAP [FEM_OK] OK [FEC_CRO] Crouch [FEC_CR3] Crouch (L3 button) [FEC_SMT] Sub-mission [FEC_SM3] Sub-mission (R3 button) [FEC_RSC] Radio stations [ST_PR01] Flyboy [ST_PR02] Aircraftman [ST_PR03] Pilot Officer [ST_PR04] Corporal [ST_PR05] Lieutenant [ST_PR06] Sergeant [ST_PR07] Captain [ST_PR08] Biggs [ST_PR09] Wedge [ST_PR10] Red Baron [ST_PR11] Goose [ST_PR12] Viper [ST_PR13] Jester [ST_PR14] Chappy [ST_PR15] Iceman [ST_PR16] Maverick [ST_PR17] Noops [ST_PR18] Air Chief Marshal [ST_PR19] Ace [FET_LG] LOAD GAME [CAR_EXP] Road Vehicles destroyed [BOA_EXP] Boats destroyed [HEL_DST] Planes & Helicopters destroyed [STFT_01] Fastest time on 'Alloy Wheels Of Steel' [STFT_02] Fastest time on 'The Driver' [STFT_03] Fastest time in Dirt Ring [STFT_04] Fastest time on RC Plane Race [STFT_05] Fastest time on RC Car Race [STFT_06] Fastest time on RC helicopter Pickup [STFT_07] Fastest time on 'Terminal Velocity' [STFT_08] Fastest time on 'Ocean Drive' [STFT_09] Fastest time on 'Border Run' [STFT_10] Fastest time on 'Capital Cruise' [STFT_11] Fastest time on 'Tour!' [STFT_12] Fastest time on 'V.C. Endurance' [STHC_01] Highest score for Shooter [STHC_02] Best Percentage of hits for Shooter [STHC_03] Number of drug deals made [HELP24] You can now take jobs from the Colonel. [HELP25] You can now take jobs from Avery Carrington. [HELP29] You can visit the clothes store when you're not on a mission. [HELP30] When you buy new clothes your wanted level will be set to zero. [ASM4_24] distance: [RBM1_6] ~g~Take Mercedes and the 'Love Juice' to the band at the recording studio. [RBM1_3] NO LONGER NEEDED [HAM1_5] NO LONGER NEEDED [RBM1_11] NO LONGER NEEDED [HELP31] To do a drive-by, first look left or right using the ~h~~k~~VEHICLE_LOOKLEFT~~w~ or the ~h~~k~~VEHICLE_LOOKRIGHT~. [HELP34] You must have a sub machine gun to perform a drive-by. [STRIP_1] ~r~Not enough cash, you cheap sleazebag. [EXIT_1] Press the ~h~~k~~PED_SPRINT~ ~w~button to exit. [ASM1_B] Your next job is taped under the phone. [ASM1_C] I have more work for you with a more 'hands-on' approach. [SCARF] Apartment 3c [LAW4_10] Rich management suck! [RCH1_6] ~g~Use the RC Helicopter to collect checkpoints scattered throughout the airport. [RCH1_9] ~b~TOTAL TIME: ~1~:~1~ [RCH1_10] ~b~TOTAL TIME: ~1~:0~1~ [WHEEL01] TWO WHEELS DOUBLE BONUS: $ ~1~ Distance: ~1~.~1~m Time: ~1~ seconds [WHEEL02] TWO WHEELS DOUBLE BONUS: $ ~1~ Distance: ~1~ feet Time: ~1~ seconds [WHEEL03] TWO WHEELS BONUS: $ ~1~ Time: ~1~ seconds [WHEEL04] TWO WHEELS BONUS: $ ~1~ Distance: ~1~.~1~m [WHEEL05] TWO WHEELS BONUS: $ ~1~ Distance: ~1~ feet [WHEEL06] WHEELIE BONUS: $ ~1~ Distance: ~1~.~1~m Time: ~1~ seconds [WHEEL07] WHEELIE BONUS: $ ~1~ Distance: ~1~ feet Time: ~1~ seconds [WHEEL08] WHEELIE BONUS: $ ~1~ Time: ~1~ seconds [WHEEL09] WHEELIE BONUS: $ ~1~ Distance: ~1~.~1~m [WHEEL10] WHEELIE BONUS: $ ~1~ Distance: ~1~ feet [WHEEL11] STOPPIE BONUS: $ ~1~ Distance: ~1~.~1~m Time: ~1~ seconds [WHEEL12] STOPPIE BONUS: $ ~1~ Distance: ~1~ feet Time: ~1~ seconds [WHEEL13] STOPPIE BONUS: $ ~1~ Time: ~1~ seconds [WHEEL14] STOPPIE BONUS: $ ~1~ Distance: ~1~.~1~m [WHEEL15] STOPPIE BONUS: $ ~1~ Distance: ~1~ feet [ROK3_72] Love Fist! [POR1_19] Hey! [DESPERA] Desperado [MOB_99A] Get to the payphone next to the mall in Washington. [MOB_98A] Get to the pay phone in Vice Point. [MOB_96A] Get to the payphone at the airport terminal. [MOB_95A] Get to the payphone in Little Havana. [BNK1_1] Can I help you, sir? [BNK1_2] There's an imposter! [BNK1_3] He's gone insane! [BNK1_4] Who the hell are you? [BNK1_5] Where's your badge? [BNK1_6] There they are! Shoot to kill! [MOB_24A] Hola, is this Mr. Vercetti? [MOB_24B] Yeah. [MOB_24C] This is Cortez. You were at my party. [MOB_24D] Yeah. I remember. [MOB_24E] Mr. Vercetti, it was a most unfortunate incident that happened with your business deal. [MOB_24F] I know. [MOB_24G] I want you to know me and my people are doing their utmost to get to the bottom of it. [MOB_24H] If you'd like to talk to me more privately, you can find me at the boat, eh? Okay? Good day, senor. [BNK2_6] This guy's a lunatic! [ANGEL] Angel [CUBJET] Cuban Jetmax [SANDKIN] Sandking [POLMAV] Police Maverick [BOXVILL] Boxville [BENSON] Benson [HOTRINA] Hotring Racer [HOTRINB] Hotring Racer [BLOODRA] Bloodring Banger [BLOODRB] Bloodring Banger [MAFIACR] Mafia Cruiser [COP_M2] 'VICE SQUAD' [COP_M3] 'BROWN THUNDER' [BNK3_2] I'm not driving for you, no way, I'm sharing this at group. [FEM_SL1] Save File 1 Not Present [FEM_SL2] Save File 2 Not Present [FEM_SL3] Save File 3 Not Present [FEM_SL4] Save File 4 Not Present [FEM_SL5] Save File 5 Not Present [FEM_SL6] Save File 6 Not Present [FEM_SL7] Save File 7 Not Present [FEM_SL8] Save File 8 Not Present [FEA_CHA] Changing audio output to STEREO. Please wait... [FEA_CHD] Warning! You are changing from STEREO output to DTS. Please wait... [FEI_SEL] Select [FEI_BAC] Back [FEI_RES] Resume [FEI_NAV] Navigate [FEI_BTX] / button - [FEI_BTT] " button - [FEI_STA] START button - [FEI_BTD] ; = > < - [FEI_STO] Stop [ST_ICEC] 'Ice Cream' Sold [MOB_68A] Tommy son, have I got a surprise for you [MOB_68B] I'm down at the recording studios with some major artists [MOB_68C] Why don't you pay us a visit? [MOB_68D] You know it makes sense, dontcha? See ya later. [OUTFT1] Street [OUTFT2] Soiree [OUTFT3] Coveralls [OUTFT4] Country Club [OUTFT5] Havana [OUTFT6] Cop [OUTFT7] Bank Job [OUTFT8] Casual [OUTFT9] Mr Vercetti [OUTFT10] Tracksuit [OUTFT13] MC Tommy [CAR_AS1] CAR SHOWROOM ASSET COMPLETED [CAR_AS2] ~g~Sunshine Auto's will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. [BUYSAVE] ~g~You can now save your game here when not on a mission. [BUYGARG] ~g~You can also store vehicles in this garage. [STRPBUY] Pole Position Club purchased: $~1~ [GA_4] Car bombs are $500 each [GA_5] Your car is already fitted with a bomb. [GA_6] { reVC update } Park it, prime it by pressing the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button and LEG IT! [GA_7] { reVC update } Arm with the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. Bomb will go off when engine is started. [GA_6B] { reVC update } Park it, prime it by pressing the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button and LEG IT! [GA_7B] { reVC update } Arm with the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. Bomb will go off when engine is started. [MOB_70A] Tommy, it's me, Colonel Cortez. Look senor, I believe you are a man who can get things done. So please help me. [MOB_70B] You can find me at the boat. [PICK2] .357 delivered to Ocean View Hotel! [PICK3] Chainsaw delivered to Ocean View Hotel! [PICK4] Flame Thrower delivered to Ocean View Hotel! [PICK5] .308 Sniper delivered to Ocean View Hotel! [PICK6] Minigun delivered to Ocean View Hotel! [PICK7] Rocket Launcher delivered to Ocean View Hotel! [PICK8] Sea Sparrow now available from the Mansion on Starfish Island! [PICK9] Tank now available from the Army Barracks! [PICK10] Hunter now available from the Army Barracks! [CLOTH1] Soiree outfit delivered to Rafael's on Ocean Beach. [CLOTH2] Street outfit delivered to Safehouses. [CLOTH3] Coveralls outfit delivered to Tooled Up in The North Point Mall. [CLOTH4] Country Club outfit delivered to The Golf Club in Leaf Links. [CLOTH5] Havana outfit delivered to Little Havana Streetwear in Little Havana. [CLOTH6] Cop outfit delivered to Police Station on Washington Beach. [CLOTH7] Casual outfit delivered to Gash in The North Point Mall. [CLOTH8] Mr Vercetti outfit delivered to Collar & Cuffs on Ocean Beach. [CLOTH9] Tracksuit outfit delivered to Jocksport in Downtown. [CLOTH10] Bank Job outfit delivered to Malibu Club in Vice Point. [MOB_62A] Tommy, is Ricardo Diaz, I want to thank you for looking out for me my man. [MOB_62B] I ask that prick Cortez, he say you the real deal, my friend, why you not come see me. [MOB_62C] I need a guy like you. All I have now is dickheads, [MOB_62D] dickheads everywhere, yo. I make you real rich. [GOAWAY2] ~g~Come back when you have finished the Biker gang missions. [COL2_9] You American idiot! They followed you here! [LOADCOL] Loading... [STFT_17] Fastest time on 'PCJ Playground' [STFT_18] Fastest time on 'Trial By Dirt' [STFT_19] Fastest time on 'Test Track' [NEW_REC] ~g~New Record Set!! ~w~~1~ minutes ~g~and ~w~~1~ seconds. [BMX_HOW] ~g~Do two laps of the dirt track, ~y~passing through ~g~the ~y~CHECKPOINTS ~g~as you go! [BMXREW1] ~g~Each time you beat your previous record for the two laps [BMXREW2] ~g~a larger ~y~REWARD ~g~will be awarded! [BMXRAIN] ~g~Looks like rain... [ITBEG] In the beginning... [NBMNBUY] El Swanko Casa purchased: $~1~ [LNKVBUY] Links View Apartment purchased: $~1~ [HYCOBUY] Hyman Condo purchased: $~1~ [BUYGARS] ~g~You can also store vehicles in these garages. [OCHEBUY] Ocean Heights Apartment purchased: $~1~ [WASHBUY] 1102 Washington Street purchased: $~1~ [VCPTBUY] 3321 Vice Point purchased: $~1~ [HELP6_C] Press the ~h~~k~~VEHICLE_HANDBRAKE~ ~w~button to apply the vehicle's handbrake. [HELP2_A] Press the ~h~~k~~PED_SPRINT~ ~w~button when running to ~h~sprint [HELP4_A] Press the ~h~~k~~VEHICLE_ACCELERATE~ ~w~button to accelerate. [HELP5_A] Press the ~h~~k~~VEHICLE_BRAKE~ ~w~button to brake, or to reverse if the vehicle has stopped. [HELP8_A] Press the~h~ ~k~~PED_SNIPER_ZOOM_IN~ button ~w~to ~h~zoom in ~w~with the rifle and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ button ~w~to ~h~zoom out ~w~again. [PBOAT_1] { reVC update } Press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to fire the boat cannons. [SEG3_4] { reVC update } ~g~You can pick up bombs by simply piloting your RC Raider adjacent to each one, to drop a bomb press the ~h~~k~~VEHICLE_FIREWEAPON~ ~g~button. [RCR1_3] { reVC update } ~g~If you wish to quit this mission press the ~h~~k~~VEHICLE_FIREWEAPON~ ~g~button to detonate your RC car. [HELP32] { reVC update } Then fire using the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. [HELP33] { reVC update } Then fire using the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. [TTUTOR] Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle taxi missions on or off. [TTUTOR2] Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle taxi missions on or off. [FTUTOR] Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle fire truck missions on or off. [FTUTOR2] Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle fire truck missions on or off. [CTUTOR] Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle Vigilante missions on or off. [CTUTOR2] Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle Vigilante missions on or off. [HELP8_B] Press the ~h~~k~~PED_SNIPER_ZOOM_IN~ button ~w~to ~h~zoom in ~w~with the rifle and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ button ~w~to ~h~zoom out ~w~again. [ATUTOR3] Press the ~h~~k~~TOGGLE_SUBMISSIONS~ button~w~ to toggle Paramedic missions on or off. [GUN_H1] ~w~Press the~h~ ~k~~PED_SPRINT~ ~w~button to buy. Press the~h~ ~k~~VEHICLE_ENTER_EXIT~ ~w~button to exit. [PU_CF3] { reVC update } Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to replace current weapon in this slot. [PU_CF4] { reVC update } Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to replace current weapon in this slot. [HELP9_B] Press the~h~ ~k~~PED_FIREWEAPON~ button ~w~to ~h~fire~w~ the sniper rifle. [HELP37] If you do not want to enter the vehicle while car jacking someone, press the ~h~~k~~PED_SPRINT~ ~w~button. [HELP6_A] Press the~h~ ~k~~VEHICLE_HANDBRAKE~ button ~w~to apply the vehicle's ~h~handbrake. [HELP6_D] Press the~h~ ~k~~VEHICLE_HANDBRAKE~ button ~w~to apply the vehicle's ~h~handbrake. [HELP26] Press the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button to enter or exit a vehicle. [HELP27] Press the ~h~~k~~VEHICLE_TURRETUP~~w~ button and the ~h~~k~~VEHICLE_TURRETDOWN~~w~ button, to shift your weight on a bike. [HELP28] Press the ~h~~k~~VEHICLE_TURRETUP~~w~ button and the ~h~~k~~VEHICLE_TURRETDOWN~~w~ button, to shift your weight on a bike. [HELP35] Press the ~h~~k~~GO_LEFT~~w~, and the ~h~~k~~GO_RIGHT~~w~, to steer the vehicle. [HELP36] Press the ~h~~k~~GO_LEFT~~w~, and the ~h~~k~~GO_RIGHT~~w~, to steer the vehicle. [HELP42] Follow the ~q~pink blip~w~ to find the hotel. [HELP19] Walk into the ~q~pink marker ~w~to continue. [HELP1] Stop in the center of the ~q~pink marker. [HELP12] Walk into the center of the ~q~pink marker~w~ to trigger a mission. [SEG3_6] ~g~To successfully hit a target zone, you must drop the bomb in the area represented by the ~q~pink marker~w~. You can drop the bombs in any order. [S_PROMP] When not on a mission you can save your progress by collecting the ~h~cassette tape pickup. [HELP16] Walk through the front door of the ~h~Ocean View~w~ Hotel to enter the building. [HELP43] ~g~Goto the ~h~Ocean View~g~ hotel on Ocean Drive. [HELI_F1] ~r~Heli Checkpoint mission cancelled! [AMMUHLP] If you need any weapons visit ~h~Ammu-Nation~w~. Follow the ~h~Gun blip~w~ on the radar. [HELI_1] Downtown Chopper Checkpoint [HELI_2] Ocean Beach Chopper Checkpoint [HELI_3] Vice Point Chopper Checkpoint [HELI_4] Little Haiti Chopper Checkpoint [FST_MFR] Most Favorite Radio Station [FST_LFR] Least Favorite Radio Station [FEI_HOL] Hold [FEI_ZOO] Zoom [FEI_BTR] > < - [FEI_NA] N\A [MESA] Mesa Grande [STRP_NO] You cannot buy the Stripclub at this time, come back later. [CHSE] CHASE [NBMN_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase El Swanko Casa for $~1~ [NBMN_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase El Swanko Casa for $~1~ [NBMN_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase El Swanko Casa for $~1~ [LNKV_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Links View Apartment for $~1~ [LNKV_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Links View Apartment for $~1~ [LNKV_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Links View Apartment for $~1~ [HYCO_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Hyman Condo for $~1~ [HYCO_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Hyman Condo for $~1~ [HYCO_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Hyman Condo for $~1~ [OCHE_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Ocean Heights Apartment for $~1~ [OCHE_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Ocean Heights Apartment for $~1~ [OCHE_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Ocean Heights Apartment for $~1~ [WASH_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 1102 Washington Street for $~1~ [WASH_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 1102 Washington Street for $~1~ [WASH_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 1102 Washington Street for $~1~ [VCPT_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 3321 Vice Point for $~1~ [VCPT_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 3321 Vice Point for $~1~ [VCPT_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase 3321 Vice Point for $~1~ [PRNT_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Print Works for $~1~ [PRNT_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Print Works for $~1~ [PRNT_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Print Works for $~1~ [CAR_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Car Showroom for $~1~ [CAR_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Car Showroom for $~1~ [CAR_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Car Showroom for $~1~ [PORN_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Film Studios for $~1~ [PORN_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Film Studios for $~1~ [PORN_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Film Studios for $~1~ [ICE_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Ice Cream Factory for $~1~ [ICE_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Ice Cream Factory for $~1~ [ICE_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Ice Cream Factory for $~1~ [TAXI_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Taxi Company for $~1~ [TAXI_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Taxi Company for $~1~ [TAXI_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Taxi Company for $~1~ [BANK_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase The Malibu for $~1~ [BANK_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase The Malibu for $~1~ [BANK_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase The Malibu for $~1~ [BOAT_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Boatyard for $~1~ [BOAT_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Boatyard for $~1~ [BOAT_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Boatyard for $~1~ [STRP_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Pole Position Club for $~1~ [STRP_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Pole Position Club for $~1~ [STRP_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase the Pole Position Club for $~1~ [STOCK] ~r~out of stock [HELP14] To find the Lawyer's office, follow the ~h~L blip~w~ on the radar [RAMPAGE] RAMPAGE!! [RAMP_F] RAMPAGE FAILED!! [RAMP_P] RAMPAGE PASSED!! [RAMP_A] ALL RAMPAGES COMPLETED!! [PAGE_01] Kill ~1~ Gang Members in 2 Minutes! [PAGE_02] Destroy ~1~ Vehicles in 2 Minutes! [PAGE_03] Drive-by and Waste ~1~ Gang Members in 2 Minutes! [PAGE_04] Runover and Kill ~1~ Gang Members in 2 Minutes! [PAGE_05] Gun Down ~1~ Gang Members in 2 Minutes! [SENTXS] Sentinel XS [MAP_LEG] Map Legend [VCNMAV] VCN Maverick [LG_01] Player position [LG_02] Avery Carrington [LG_03] Biker Contact [LG_04] Colonel Cortez [LG_05] Ricardo Diaz [LG_06] Kent Paul [LG_07] Lawyer [LG_08] Phil Cassidy [LG_09] Boatyard [LG_10] Malibu Club [LG_11] Cubans [LG_12] Film Studio [LG_13] Ammu-Nation [LG_14] Haitians [LG_15] Hardware Store [LG_16] Safe House [LG_17] Ice Cream [LG_18] Kaufman Cabs [LG_19] Love Fist [LG_20] Print Works [LG_21] Property [LG_22] Pay 'n' Spray [LG_23] Clothes Shop [LG_24] Tommy's Mansion [LG_25] Telephone [LG_26] Wildstyle Radio Station [LG_27] Flash FM Radio Station [LG_28] KChat Radio Station [LG_29] Fever 105 Radio Station [LG_30] VROck Radio Station [LG_31] VCPR Radio Station [LG_32] Espantoso Radio Station [LG_33] Emotion 98.3 Radio Station [LG_34] Wave 103 Radio Station [LG_36] Sun Yard [LG_37] Strip Club [MAP_YAH] YOU ARE HERE [TAXSHRT] ~g~You can use this Kaufman Cab to take you to destinations instead of driving. It will cost you $9. [MOB_09D] Maybe Leo's already dead. Maybe I killed Leo and took his phone - you think of that prick? [FE_MLG] MAP LEGEND [FED_RDR] RADAR MODE [FED_HUD] HUD MODE [FED_RDB] BLIPS ONLY [FEST_HV] Highest Vigilante Mission level [BRIBE1] You have just picked up a police bribe, this will reduce your wanted level by one star. [CLOHELP] Clean Clothes!! [CRED001] ROCKSTAR NORTH [CRD001A] STUDIO DIRECTOR [CRD001B] ANDREW SEMPLE [CRED002] PRODUCER [CRD002A] DEVELOPMENT DIRECTOR [CRED003] LESLIE BENZIES [CRED004] ART DIRECTOR [CRED005] AARON GARBUT [CRED006] TECHNICAL DIRECTORS [CRED007] OBBE VERMEIJ [CRED008] ADAM FOWLER [CRED010] ANDREW DUTHIE [CRED011] CRAIG FILSHIE [CRED012] WILLIAM MILLS [CRED013] CHRIS ROTHWELL [CRD013A] IMRAN SARWAR [CRD013B] JAMES WORRALL [CRD013C] JOHN HAIME [CRED014] WRITTEN BY [CRED015] JAMES WORRALL [CRED016] PAUL KUROWSKI [CRED017] DAN HOUSER [CRED018] LEAD CHARACTER DESIGNER [CRD018A] CHARACTER DESIGNER [CRED019] IAN MCQUE [CRD019A] TOKS SOLARIN [CRD019B] ALAN DAVIDSON [CRED020] LEAD ANIMATOR [CRED021] ALEX HORTON [CRD022A] ANIMATORS [CRED022] LEE MONTGOMERY [CRD022B] DUNCAN SHIELDS [CRD022C] GUS BRAID [CRED023] VEHICLE DESIGNERS [CRD023A] JOLYON ORME [CRD023B] ALAN DUNCAN [CRD024A] LEAD VEHICLE DESIGNER [CRED024] PAUL KUROWSKI [CRED025] MAP DESIGNERS [CRED026] ADAM COCHRANE [CRED027] NIK TAYLOR [CRED028] GARY MCADAM [CRED029] KEIRAN BAILLIE [CRED030] ALISDAIR WOOD [CRED031] ANDREW SOOSAY [CRD031A] STEVEN MULHOLLAND [CRD031B] WAYLAND STANDING [CRD031C] CAMPBELL J. DICK [CRD031D] GRAPHIC DESIGNER [CRD031E] STUART PETRI [CRED032] LEAD PROGRAMMER [CRD032A] PROGRAMMERS [CRED033] ALEXANDER ROGER [CRED034] GRAEME WILLIAMSON [CRED035] BARANE CHAN [CRED036] DEREK PAYNE [CRED037] GORDON YEOMAN [CRD037A] ALAN CAMPBELL [CRD037B] MARK HANLON [CRD037C] ANDRZEJ MADAJCZYK [CRED038] LEAD MUSIC PRODUCER [CRED039] CRAIG CONNER [CRED040] STUART ROSS [CRED041] LEAD AUDIO ENGINEER [CRED042] ALLAN WALKER [CRD041A] AUDIO ENGINEER [CRD041B] AUDIO [CRD042A] WILL MORTON [CRED043] AUDIO PROGRAMMER [CRED044] RAYMOND USHER [CRED045] TEST MANAGER [CRED046] CRAIG ARBUTHNOTT [CRED047] LEAD QA [CRED048] NEIL CORBETT [CRED049] KEVIN WONG [CRED050] QA [CRED051] DAVID BEDDOES [CRED052] DAVID WATSON [CRED053] BARRY CLARK [CRED054] ROSS SPARROW [CRED055] JAMES ALLAN [CRED056] NEIL MEIKLE [CRD056A] GEORGE WILLIAMSON [CRD056B] MATT JONES [CRD056C] ROB HARBOUR [CRD056D] TOM WHITTAKER [CRED057] LEAD TECHNICAL SUPPORT [CRED058] LORRAINE ROY [CRD057A] TECHNICAL SUPPORT [CRED059] CHRISTINE CHALMERS [CRD060A] OFFICE SUPPORT [CRD060B] KIM GURNEY [CRD060C] CASSIE OLIVER [CRED060] ROCKSTAR NEW YORK [CRED061] EXECUTIVE PRODUCER [CRED062] SAM HOUSER [CRED063] PRODUCER [CRED064] DAN HOUSER [CRED065] VP OF DEVELOPMENT [CRED066] JAMIE KING [CRED067] CHIEF TECHNOLOGY OFFICER [CRED068] GARY J. FOREMAN [CRED069] ASSOCIATE PRODUCER [CRED070] JEREMY POPE [CRD071A] DIRECTOR OF QUALITY ASSURANCE [CRD072A] JEFF ROSA [CRED071] MUSIC SUPERVISOR [CRED072] TERRY DONOVAN [CRED073] PRODUCTION TEAM [CRED074] TERRY DONOVAN [CRED075] JENNIFER KOLBE [CRED076] JENEFER GROSS [CRED077] LAURA PATERSON [CRED078] JEFF CASTANEDA [CRED079] JERONIMO BARRERA [CRED080] CARLY SLATER [CRED081] JUNG KWAK [CRED082] BRIAN WOOD [CRED083] RENAUD SEBANNE [CRED084] RICHARD KRUGER [CRD084A] DANIEL EINZIG [CRD084B] JACEN BURROWS [CRD084C] LINN PR [CRD084D] COVER ART [CRD084E] STEPHEN BLISS [CRED085] KENT PAUL'S 80 NOSTALGIA ZONE [CRED086] WRITTEN BY DAN HOUSER [CRD086A] PRODUCED BY ADAM TEDMAN [CRED087] WWW.KENTPAUL.COM AND WWW.VICECITY.COM [CRED088] CREATED BY [CRD088A] ADAM TEDMAN [CRD088B] DAVID YU [CRD088C] JERRY LUNA [CRD088D] STUART PETRI [CRD088E] MICHAEL CARNEVALE [CRD088F] GREG LAU [CRD088G] FUTABA HAYASHI [CRED089] QA MANAGER [CRED090] CRAIG ARBUTHNOTT [CRED091] LEAD ANALYST [CRED092] ADAM DAVIDSON [CRD092A] JOE HOWELL [CRD092B] MARC FERNANDEZ [CRED093] GAME ANALYST [CRED094] RICHARD HUIE [CRED095] ROCKSTAR TEST TEAM [CRED096] LANCE WILLIAMS [CRED097] JOE GREENE [CRED098] BRIAN PLANER [CRD098A] ELIZABETH SATTERWHITE [CRD098B] JAMEEL VEGA [CRD098C] MIKE HONG [CRED099] LEE CUMMINGS [CRED100] STORY [CRED101] JAMES WORRALL [CRED102] DAN HOUSER [CRED103] ADAM TEDMAN [CRED104] PAUL YEATES [CRED105] JENEFER GROSS [CRED106] LAURA PATERSON [CRED107] CUT SCENES [CRED108] WRITTEN BY DAN HOUSER AND JAMES WORRALL [CRED109] AUDIO DIRECTED BY DAN HOUSER AND NAVID KHONSARI [CRED110] PRODUCED BY JAMIE KING [CRD110A] TALENT PROCUREMENT: JAMIE KING, SEAN MACALUSO [CRED111] CAST [CRD111A] SUPPORTING CHARACTERS [CRED112] TOMMY VERCETTI - RAY LIOTTA [CRED113] KEN ROSENBERG - WILLIAN FICHTNER [CRED114] SONNY FORELLI - TOM SIZEMORE [CRED115] STEVE SCOTT - DENNIS HOPPER [CRED116] AVERY CARRINGTON - BURT REYNOLDS [CRED117] RICARDO DIAZ - LUIS GUZMAN [CRED118] LANCE VANCE - PHILIP MICHAEL THOMAS [CRED119] COLONEL JUAN CORTEZ - ROBERT DAVI [CRED120] UMBERTO ROBINA - DANNY TREJO [CRED121] PHIL CASSIDY - GARY BUSEY [CRED122] MITCH BAKER - LEE MAJORS [CRED123] MERCEDES CORTEZ - FAIRUZA BALK [CRED124] KENT PAUL - DANNY DYER [CRED125] JEZZ TORRENT - KEVIN MCKIDD [CRED126] TAXI CONTROLLER - DEBORAH HARRY [CRED127] CANDY SUXXX - JENNA JAMESON [CRED128] BJ SMITH - LAWRENCE TAYLOR [CRED129] AUNTIE POULET - YOUREE CLEOMILI HARRIS [CRED130] SUPPLIER - ARMANDO RIESCO [CRED131] COUGAR - BLAYNE PERRY [CRED132] HILARY - CHARLES TUCKER [CRED133] CONGRESSMAN ALEX SHRUB - CHRIS LUCAS [CRED134] OLD MAN KELLY - GEORGE DICENZO [CRD134A] CAM JONES - GREG SIMS [CRD134B] PSYCHO - HUNTER PLATIN [CRD134C] MAUDE THE ICE CREAM LADY - JANE GENNARO [CRD134D] JETHRO - JOHN ZURHELLEN [CRD134E] GONZALES - JORGE PUPO [CRD134F] DWAYNE - NAVID KHONSARI [CRD134G] DICK - PETER MCKAY [CRD134H] MIKE THE GOON & PORN GUY - ROBERT CIHRA [CRD134I] PERCY - RUSSELL FOREMAN [CRED135] MOTION CAPTURE [CRED136] ANIMATED BY [CRD136A] TECHNICAL DIRECTION BY ALEX HORTON [CRED137] DIRECTED BY [CRD137A] DIRECTED BY NAVID KHONSARI [CRED138] PRODUCED BY [CRD138A] PRODUCED BY JAMIE KING [CRD138B] RENAUD SEBBANE [CRED139] RECORDED AT PERSPECTIVE STUDIOS, BROOKLYN [CRED140] MOTION CAPTURE ACTORS [CRD140A] BLAYNE PERRY [CRD140B] JONATHON SALE [CRD140C] CHARLES TUCKER [CRD140D] EDDIE MARRERO [CRD140E] WILLIAM MCCALL [CRD140F] JORGE PUPO [CRD140G] ROBERT JACKSON [CRD140H] TARA RADCLIFFE [CRD140I] JENIFER GAMBETESE [CRD140J] KRIS ACHEVARRIA [CRD140K] ALI ORDOUBADI [CRD140L] KAHLEEM POOLE [CRED141] PEDESTRIAN DIALOGUE [CRD141A] WRITTEN BY DAN HOUSER, MARC FERNANDEZ, GILLIAN TELLING AND NAVID KHONSARI [CRD141B] WITH HELP FROM JEREMY POPE, LANCE WILLIAMS, AND JENNY JEMISON [CRED142] WRITTEN BY [CRD142A] DAN HOUSER AND JAMES WORRALL [CRED143] DIRECTED BY DAN HOUSER, CRAIG CONNER, MARC FERNANDEZ, AND ALLAN WALKER [CRED144] PRODUCED BY RENAUD SEBANNE [CRED145] PEDESTRIANS [CRED146] ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCIA, [CRED147] ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, [CRED148] ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, [CRED149] BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, [CRED150] CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LOPEZ, DAN LEE, [CRED151] DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, [CRED152] DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, [CRED153] DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, [CRED154] DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, [CRED155] ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, [CRED156] GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, [CRED157] HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, [CRED158] JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, [CRED159] KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, [CRED160] LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, [CRED161] LORNA JORDAN, LUCIO AMADIO, MARCO FERNANDEZ, MARIKO TANAKA, MARLON MATTHEWS, [CRED162] MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, [CRED163] MELISSA ALVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, [CRED164] NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, [CRED165] OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZALES, [CRED166] RANDY JOHNSON, REY CONCEPCION, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, [CRED167] ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NUNEZ, SALVADORE SUAZO, [CRED168] SAM WHITE, SANTOS GONZALES, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, [CRED169] STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, [CRED170] SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, [CRED171] DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, [CRED172] CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON [CRED175] ADAM DAVIDSON [CRED176] LANCE WILLIAMS [CRED177] NEIL MCCAFFREY [CRED178] LAURA PATERSON [CRED179] REY CONCEPCION [CRED180] CHARLES HEROLD [CRED181] ANDREW GREENWALD [CRED182] JAMES MIELKE [CRED183] PETER SUCIU [CRED184] ALEX ODULIO [CRED185] DON NKRUMAH [CRED186] KENDALL PITTMAN [CRED187] SAL SUAZO [CRED188] EREK MATEO [CRED189] CHRIS DIFATE [CRED190] LEILA MILTON [CRED191] DARREN ZOLTOWSKI [CRED192] VIRGINIA SMITH [CRED193] KEVIN CASSIN [CRED194] JASON SHIGEMORI [CRED195] KELLY KINSELLA [CRED196] MOLLIE STICKNEY [CRED197] STANTON SARJEANT [CRED198] LAURA WALSH [CRED199] MARK GARONE [CRED200] JOANNA SLY [CRED201] ELIZABETH HOWELL [CRED202] ANA HERCULES [CRED203] SHIRLEY IRICK [CRED204] KASHONA FIELDS [CRED205] JOEL M. LILJE [CRED206] JOHN DIBENEDETTO [CRED207] NANCY GILES [CRED208] RYAN CROY [CRED209] JENNIFER KOLBE [CRED210] LIAM BURKE [CRED211] SIGRID PREISSL [CRED212] ANITA FITZSIMONS [CRED213] PHILIPPA RASELLI [CRED214] WIL QUESNEL [CRED215] FALKO BURKERT [CRED216] SARA SEWELL [CRED217] RADIO STATIONS AND MUSIC [CRED218] MUSIC CONSULTANCY [CRD218A] HEINZ HENN [CRD218B] STUART ROSS [CRED219] SOUNDTRACK CO-ORDINATOR [CRED220] TERRY DONOVAN [CRED221] PRODUCER FOR ROCKSTAR GAMES [CRED222] DAN HOUSER AND LAZLOW [CRED223] PRODUCER FOR ROCKSTAR NORTH [CRED224] CRAIG CONNER [CRED225] ALLAN WALKER [CRED226] LAZLOW [CRED227] DJ BANTER AND IMAGING [CRED228] WRITTEN BY DAN HOUSER AND LAZLOW [CRED229] FLASH FM [CRD229A] TONI-MARIA CHAMBERS [CRD229B] IMAGING VOICE AND PRODUCTION-JEFF BERLIN [CRED230] SPECIAL THANKS TO [CRED231] TOMMY MOTTOLA, [CRED232] MICHELLE ANTHONY, [CRED233] STEVE BARNETT, [CRED234] CHUCK FLECKENSTEIN, [CRED235] RITA LIBERATOR [CRED236] MARTIN & CLAIRE LOGAN [CRED237] SANDRA HUTTON [CRED238] CHRISTINE DAVIDSON [CRED239] ALAN, RED & BIGFOOT [CRED240] LE T [CRED241] COLIN DONALD [CRED242] KERRY STALLWOOD [CRED243] ALAN MCGREGOR [CRED244] CHRIS MORTON [CRED245] EMIL BUSSE [CRED246] EMILY BAILLIE [CRED247] KEVIN ARCHIBALD [CRED248] MORAG KERR [CRED249] CATH WALKER [CRED250] ISO BAR [CRED251] WATERLINE [CRED252] NEWS CAFE [CRD251A] THE POND [CRD252A] PIVO [CRED253] BUDGET VIDEO RENTALS [CRED254] LORNA'S SCOOTER [CRED255] GARETH MURFIN [CRED256] ADDITIONAL ART [CRED257] TONY PORTER [CRED258] CRAIG MOORE [CRED259] CUT SCENE LIP-SYNC ANIMATION [CRED260] COSGROVE HALL FILMS [CRED261] PRODUCER - OWEN BALLHATCHET [CRED262] SENIOR ANIMATOR - JON TURNER [CRED263] ANIMATORS - RICHARD DRUMM [CRED264] DAVE BROWN [CRED265] MAIR THOMAS [CRED266] PRASHANT PATEL [CRED267] AUDIO TECHNOLOGY CONSULTANT [CRED268] RIK EDE FOR GAMESOUND LTD. [CRED269] DTS INTEGRATION SUPPORT [CRED270] TED LAVERTY FOR DTS [CRED271] CHRIS GREER FOR DTS [CRED272] JASON PAGE FOR SCEE [CRED273] RESEARCH AND ANALYSIS [CRED274] VROCK [CRED275] DJ: LAZLOW AS HIMSELF [CRED276] IMAGING VOICE-JOE KELLY [CRED277] IMAGING PRODUCTION-JONATHAN HANST [CRED278] WAVE 103 [CRED279] DJ: ADAM FIRST-JAMIE CANFIELD [CRED280] IMAGING VOICE-JEN SWEENEY [CRED281] IMAGING PRODUCTION-JONATHAN HANST [CRED282] FEVER 105 [CRED283] DJ: OLIVER 'LADYKILLER' BISCUIT-JULIUS DYSON [CRED284] IMAGING VOICE MALE-ED MCMANN [CRED285] IMAGING VOICE FEMALE-SHAWNEE SMITH [CRED286] IMAGING PRODUCTION- LISTEN KITCHEN [CRED287] EMOTION 98.3 [CRED288] DJ: FERNANDO- FRANK CHAVEZ [CRED289] IMAGING VOICE-JEN SWEENEY [CRED290] IMAGING PRODUCTION-JONATHAN HANST [CRED291] RADIO ESPANTOSO [CRED292] DJ: PEPE-TONY CHILRODES [CRED293] WILDSTYLE [CRED294] DJ: MISTER MAGIC AS HIMSELF [CRED295] IMAGING VOICE-FRANK SILVESTRO [CRED296] IMAGING PRODUCTION-LAZLOW [CRED297] KCHAT [CRED298] WRITTEN BY DAN HOUSER AND LAZLOW [CRED299] PRODUCED AND EDITED BY LAZLOW [CRED300] DJ AMY SHECKENHAUSEN -LEYNA WEBER [CRED301] JEZ TORRENT-KEVIN MCKIDD [CRED302] MANDY -COLLEEN CORBETT [CRED303] MICHELLE CARAPADIS-MARY BIRDSONG [CRED304] MR.ZOO-CARL DOWLING [CRED305] GETHSEMANEE-LYNN LIPTON [CRED306] CLAUDE MAGINOT-JOHN MAUCERI [CRED307] BJ SMITH-LAWRENCE TAYLOR [CRED308] THOR-FRANK FAVA [CRED309] RADIO CALLERS [CRED310] COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS [CRED311] LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, [CRED312] DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, [CRED313] KEITH BROADAS [CRED314] VCPR [CRED315] WRITTEN BY DAN HOUSER AND LAZLOW [CRED316] PRODUCED BY LAZLOW [CRED317] MAURICE CHAVEZ-PHILLIP ANTHONY RODRIGUEZ [CRED318] JONATHAN FREELOADER- PATRICK OLSEN [CRED319] MICHELLE MONTANIUS-KELLY GUEST [CRED320] REP. ALEX SHRUB- CHRIS LUCAS [CRED321] CALLUM CRAYSHAW- SEAN MODICA [CRED322] JOHN F. HICKORY- LJ GANSEN [CRED323] PASTOR RICHARDS- DAVID GREEN [CRED324] JAN BROWN- MAUREEN SILLIMAN [CRED325] BARRY STARK- RENAUD SEBBANE [CRED326] JENNY LOUISE CRAB- MARY BIRDSONG [CRED327] KONSTANTINOS SMITH- KONSTANTINOS.COM [CRED328] JEREMY ROBARD-PETER SILVESTRO [CRED329] RADIO COMMERCIALS [CRED330] WRITTEN BY DAN HOUSER AND LAZLOW [CRED331] PRODUCED BY LAZLOW [CRED332] ADDITIONAL JINGLES PRODUCED BY CRAIG CONNER [CRED333] COMMERCIAL VOICES: [CRED334] ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, [CRED335] ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS [CRED336] FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS [CRED337] HARRISON, ED MCMANN, FRANK CHAVEZ, FRANK FAVA, GENE HILGREEN, GREG [CRED338] SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, [CRED339] JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE [CRED340] WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, [CRED341] MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF [CRED342] CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD [CRD344A] AUDIO RECORDED AT DIGITAL ARTS STUDIOS, [CRED344] NYC, TRACK 9 STUDIOS, NYC, [CRED345] WEDDINGTON MULTIMEDIA, LOS ANGELES, [CRD345A] SYNC SOUND, NYC AND RADIO LAZLOW, LONG ISLAND. [CRED346] THANKS TO AXEL ERICSON AND WON LEE AT DIGITAL ARTS, PAUL VASQUEZ AT TRACK 9 STUDIOS, JOHN BOWEN AND JOHN HASSLER AT SYNC SOUND [CRED347] MARK LLOYD [CRED348] TIM BATES [CRED349] KIT BROWN [CRED350] ANDY MASON [CRED351] PHIL DEANE [CRED352] PHIL ALEXANDER [CRED353] MATT HEWITT [CRED354] DENBY GRACE [CRED355] ANTOINE CABROL [CRED356] JONOTHAN STONES [CRED357] MIKE BLACKBURN [CRED358] TIM MCGAFF [SUNSHIN] Sunshine Autos [CHERRYP] Cherry Popper Icecreams [KAUFCAB] Kaufman Cabs [BOATYAR] The Boatyard [WANT_L] Your wanted level has been suspended, if you commit a crime whilst the stars are flashing your full wanted level will be reinstated. [PICK1] Body Armor delivered to Ocean View Hotel! [HOTRNG] HOTRING [BLODRNG] BLOODRING [DIRTRNG] DIRTRING [FEC_ABR] Accelerate, Brake or Reverse [FEI_BTU] ; = - [FEI_SCR] Scroll [SKUMBUY] Skumole Shack purchased: $~1~ [SKUM_L] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Skumole Shack for $~1~ [SKUM_T] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Skumole Shack for $~1~ [SKUM_C] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to purchase Skumole Shack for $~1~ [LG_35] Destination [BOAT_AS] ~g~The Boatyard will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. [BOAT_A2] BOATYARD ASSET COMPLETED [BOAT_N] Checkpoint Charlie [BOAT_P] ~g~collect the packages before the time runs out. [FEI_R1B] R1 \ R2 button - [HELP9_A] Press the~h~ ~k~~PED_FIREWEAPON~ button ~w~to ~h~fire~w~ the sniper rifle. [HELP21] Press the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button to enter or exit a vehicle. [CREAM] Distribution [UMBERTO] Cafe Robina [PU_CF1] Press the ~h~~k~~PED_ANSWER_PHONE~ ~w~button to pick up this weapon. It will replace any weapon you have of the same type. [FED_RDM] MAP & BLIPS [FEC_ILU] Invert Look in 1st Person [NITRO] All taxi's now have a boost jump! Just press the horn button. [RATNG53] Untrustworthy [RATNG54] Embarrassment [RATNG55] Hacker [RATNG56] Cheater [RATNG57] Total Liar [STHC_04] Highest score with Keepie-Uppy beach ball [STHC_05] Hotring Best Result [STFT_13] Fastest time on Downtown Chopper Checkpoint [STFT_14] Fastest time on Ocean Beach Chopper Checkpoint [STFT_15] Fastest time on Vice Point Chopper Checkpoint [STFT_16] Fastest time on Little Haiti Chopper Checkpoint [STFT_21] Fastest time in Hotring [STFT_22] Fastest lap time in Hotring [STFT_20] Fastest time for 'Cone Crazy' [HELP44] Stop in the ~q~pink marker. [HELP45] Press the ~h~~k~~PED_DUCK~~w~ button to duck. This will increase the accuracy of guns you are holding. [RCR1_5] RC Bandit Race [RCPL1_7] RC Baron Race [RCH1_11] RC Raider Pickup [FEA_CTD] Warning! This feature requires DTS compatible hardware to be connected. Proceed? [FEM_STE] USE STEREO [GREET] Greetings from... [LANCE_1] Come on man, drive more careful! [LANCE_2] Hey watch what you're doin! [LANCE_3] Hey where are we goin' now? [LANCE_4] What are we doin' now? [LAW4_15] More money! [MERC_5] Nice car, Mr. Vercetti. [MERC_26] FASTER, FASTER, FASTER! [MERC_27] Careful, Tommy, I only have a nose job last month. [MERC_28] Tommy, drive careful. [MERC_29] Tommy, go slower. [MERC_30] Tommy, please kill someone other than me. [MERC_31] Tommy, baby, don't kill me! [MERC_32] Tommy, I'm glad you stole this car! [MERC_40] I had so much fun. [MERC_43] Adios, angel. [MERC_44] You keep working out, now, you hear. [MERC_45] Ciao, beautiful. [COL5_17] Oh my god they've got a helicopter! [COL5_18] Shoot the helicopter! [COL5_19] Tommy, take that chopper out! [COL5_20] He's coming again! Blow that chopper! [COL5_21] Look at the size of that chopper! [COL5_22] Here he comes again! [FEA_DSM] Warning! This savegame is set to use DTS. This requires DTS compatible hardware to be connected. Please select whether you want to proceed using DTS or STEREO output. [STFT_23] Fastest time for Checkpoint Charlie [HELP50] Press the ~h~~k~~PED_ANSWER_PHONE~~w~ button to set the camera behind you. [HELP51] Press the ~h~~k~~PED_ANSWER_PHONE~~w~ button to set the camera behind you. [HELP52] Press the ~h~~k~~PED_ANSWER_PHONE~~w~ button to set the camera behind you. [HELP53] Press ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~ button or ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~ button to cycle through your available weapons. [HELP46] There are eight different types of weapon. [HELP47] You can carry one of each type of weapon at a time - one type of pistol, one type of shotgun. [HELP54] ~w~Cost: $~1~ ~r~Buying this will replace your current weapon. [HELP2A2] Press the ~h~~k~~PED_SPRINT~~w~ button when running to ~h~sprint [HLPSN_A] The sniper rifle allows you to zoom in and fire accurately at targets from a distance. [HLPSN_B] Press and hold the~h~ ~k~~PED_LOCK_TARGET~ ~w~button to ~h~target~w~ with the sniper rifle. [HLPSN_C] Press and hold the~h~ ~k~~PED_LOCK_TARGET~ ~w~button to ~h~target~w~ with the sniper rifle. [HLPSN_D] Press the ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ button ~w~to ~h~zoom in ~w~with the rifle and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ button ~w~to ~h~zoom out ~w~again. [HLPSN_E] Press the ~h~~k~~PED_FIREWEAPON~~w~ button ~w~to ~h~fire~w~ the sniper rifle. [HLPSN_F] Press the ~h~~k~~PED_FIREWEAPON~~w~ button ~w~to ~h~fire~w~ the sniper rifle. [HLPSN_G] Press the ~h~~k~~PED_FIREWEAPON~~w~ button to ~h~fire~w~ the sniper rifle. [PLANE_H] Use the ~h~~k~~VEHICLE_ACCELERATE~~w~ button to accelerate, Left and right to turn. [PLANE_4] { reVC update } { Use the ~h~~k~~VEHICLE_ACCELERATE~~w~ button to accelerate, Left and right to turn. } Use the right analog stick to accelerate, pull back on the left analog stick to climb, push forwards to descend. Left and right to turn. [HELP55] Press the ~h~~k~~PED_FIREWEAPON~~w~ button to attack the chef. [STPR_8] Pole Position Club [STPR_9] 3321 Vice Point [STPR_10] Links View Apartment [STPR_11] El Swanko Casa [STPR_12] 1102 Washington Street [STPR_13] Ocean Heights Apartment [STPR_14] Skumole Shack [STPR_15] Hyman Condo [RCCANX] ~r~RC plane cancelled. [CLT_HL2] When a Clothes Pickup is collected, a one star or two star wanted level will be cleared. [CRED009] MISSION DESIGN [CRED359] LEE JOHNSON [CRED360] HENDRIK LESSER [CRED361] PASQUALE STACCHIOTTI [CRED362] ENRIQUE FERNANDEZ [CRED363] PAUL BYERS [CRED364] MIKE EMENY [CRED365] ROB DUNKIN [CRED366] CHARLIE KINLOCH [CRED367] KEVIN HOBSON [CRED368] JIM CREE [MOB_66A] Tommy, Tommy why you coming back here for? [MOB_66B] I tell you we don't want to see you around here no more. [MOB_67A] Tommy, me thinks you should be staying away, you hear? [MOB_67B] The Haitian boys not too 'appy with you. [MOB_18A] Tommy, it's Paulo, how are you? Right mate, anyway, thought I had to drop you a line. [MOB_18B] Oh my good lord, my son, you will not believe the quality of the brass I just encountered. [MOB_18C] Street walker or something, just down in Little Havana, mate. [MOB_18D] Said her name was Mercedes or something. [MOB_18E] Oh my god, mate. You gotta check this bird out. [MOB_18F] Could strip the lead out of a pencil. Said I was the best she ever had and all. [MOB_18G] Keep you potatoes skinned for her. Be seeing you. [MOB_72A] Tommy, it's me, Lance. Keep your mouth shut there Tommy, 'cause I ain't got time to talk. [MOB_72B] I ain't interested in what you got to say. Why should I be? You don't care about me, do you? [MOB_72C] You gotta look after me a bit better. Give me a fair slice. You know... [MOB_72D] Tommy... oh, look, man, I'm sorry. It's just that... [MOB_72E] People patronize me all my life, treat me like a little kid. [MOB_72F] My brother would do that. Please, man, don't do that. [MOB_72G] I gotta go. [MOB_63A] Tommy, it's Earnest. Earnest Kelly. [MOB_63B] How are you? [MOB_63C] I'm doing okay. I'll need a stick to walk, but I should be back at work soon enough. [MOB_63D] Good. [MOB_63E] I heard about Lance. What a little prick, eh? [MOB_63F] Yes. [MOB_63G] Never trust a man who walks the streets in his pajamas. That's what I say. Glad you killed him. I hope it was painful for the prick. [MOB_63H] I think it was. I just didn't think he was like that... [MOB_63I] Tommy, for a raging lunatic, you're pretty naive. I'll be back at work soon, teach you a thing or two about life, you hear. [MOB_63J] Take your time, Earnest. Look after yourself. [MOB_16A] Tommy, Paulo here, que pasa amigo? [MOB_16B] What do you want Paul? I don't want any fake label clothes. [MOB_16C] Very funny, mate, but you know I don't touch bent gear. Nah, I was just calling to see if I get a part in one your movies. [MOB_16D] Back in England I did a lot of blue stuff, mate. I'm packing more heat than you, my son. [MOB_16E] Paul, thanks for the offer, I'll bear it in mind. [MOB_16F] Seriously, don't forget about me, after all I done for you. [MOB_16G] That's what I'm trying to forget about. [MOB_17A] Tommy Vercetti, how's it going, Mr. big shot? I hear all these things about you, some kind of player in town, now eh... [MOB_17B] Paul, you're drunk. [MOB_17C] Nah, you stupid prat, I ain't drunk. I only had a couple and some treats, ain't been to bed for a couple of days, you know. [MOB_17D] Anyway, don't give me that. I ain't a mug. Who set you up in this town? Who? Me. That's who. [MOB_17F] Really? [MOB_17G] Don't give me that. Don't! I introduced you to people. I showed you the ropes, did a lot of stuff for you, and this is how you repay me. [MOB_17H] You ignore me. You won't give me a way in, after all I did for you! What do you think I am? A div or something? [MOB_17I] Paul, take it easy. I've been busy, don't be an idiot. [MOB_17J] I ain't no idiot, mush. That's what they said in borstal. Are you asking for trouble son, because you're going to get it! [MOB_17K] Tommy, mate. Please. You was my big hope! Please, don't laugh at me! [MOB_17L] Paul, get some sleep, seriously. [MOB_73A] Tommy, it's Steve. [MOB_73B] Hey, Steve. [MOB_73C] Hey indeed, genius. You're a marvel! I'm a marvel! They love us. We are re-writing the record books, pal. [MOB_73D] We are talking major awards here. Finally, I can put my dad in a home an tell him to shut up. [MOB_73E] Eeer, that's cool, Steve. [MOB_73F] Cool? It's hot, man. Hot. H. O. T. He never believed in me. Never thought I was an artist, and now I've made it. [MOB_73G] I'm the best damn skin flick director of all time, my friend. I just wanted to say, it's a pleasure to have met you. [MOB_73H] Thanks steve. [MOB_73I] I love you, baby. Don't go changing on me, you hear. [MOB_73J] I hear you. Good bye, Steve. [BOLLOX] Press the ~h~~k~~VEHICLE_HANDBRAKE~ ~w~button to drop a bomb. Press the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button to cancel. [BRID_OP] Storm warning over, all bridges to the mainland are now open. [BRID_CL] Storm warning: All bridges to the mainland are closed. [LG_38] Target [ASSET_C] POLE POSITION ASSET COMPLETED! [ASSET_D] ~g~The Pole Position Club will now generate revenue up to a maximum of $~1~ per day. Pick up your cash regularly! [ST_WHEE] Longest Wheelie time (secs) [ST_STOP] Longest Stoppie time (secs) [ST_2WHE] Longest 2 wheels time (secs) [ST_WHED] Longest Wheelie distance (m) [ST_STOD] Longest Stoppie distance (m) [ST_2WHD] Longest 2 wheels distance (m) [OUTFT11] Tracksuit [OUTFT12] Frankie [RELOAD] ~g~You have won the fast reload ability! [APACHE] Hunter delivered to helipad in Ocean Beach. [CRED369] JOHN MCCARDLE [CRED370] DAVID MURDOCH [CRED371] CHRIS BROWN [CRED372] PAUL GREEN [CRED373] KYLE MILNE [CRED374] KEVIN YUN [CRED375] ERICK COBBS [CRED376] RANDY BLAKE [CRED377] BRANDON LIM [CRED378] BRANDON FENOL [CRED379] MICHAEL MANOLE [CRED380] ALETHEIA SIMONSON [CRED381] JOHN JANSEN [CUNTY] New clothes delivered to the Vercetti Estate! [GOODBOY] $50 Good Citizen Bonus! [NEWCONT] New ~h~Contact Point ~w~opened at the marina in Ocean Beach!! [FIRELVL] Fire Truck Mission level ~1~ [FEM_RET] RETRY [FESZ_QR] Are you sure you want to start a new game? All progress will be lost. Proceed? [SEG3_1] TIME: [HELP56] Press the ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ button to change camera modes. [HELP57] Press the ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ button to change camera modes. [HELP58] While targeting you can press the ~h~~k~~PED_CYCLE_TARGET_LEFT~ button or ~k~~PED_CYCLE_TARGET_RIGHT~ ~w~button to cycle through targets. [HELP59] While targeting you can press the ~h~~k~~PED_CYCLE_TARGET_LEFT~ button or ~k~~PED_CYCLE_TARGET_RIGHT~ ~w~button to cycle through targets. [HELP60] If you press the ~w~~k~~PED_SPRINT~ ~w~button while car jacking, you will not enter that vehicle. [HELP61] You now have limitless ammo and double health on all vehicles. [FEC_LB1] Look [FEC_LB2] behind [FEC_LB3] Look behind [FEC_R3] (R3 button) [FEC_PED] Controls On Foot [FEC_VEH] Controls In Vehicle [FEC_FPR] Controls For First Person [FEC_CMM] Common Controls [FEC_PWL] Go Left [FEC_PWR] Go Right [FEC_PWF] Walk Forward [FEC_PWT] Walk towards camera [FEC_PLB] Look Behind. [FEC_PFR] Fire Weapon [FEC_CLE] Cycle Weapon Left [FEC_CRI] Cycle Weapon Right [FEC_LKT] Lock Target [FEC_PJP] Ped Jump [FEC_PSP] Ped Sprint [FEC_PSH] Ped Shoot [FEC_TLF] Next Target To Left [FEC_TRG] Next Target to Right [FEC_CCM] Center Camera Behind player. [FEC_SZI] Sniper Rifle Zoom In [FEC_SZO] Sniper Rifle Zoom Out [FEC_LKL] First Person Look Left [FEC_LRT] First Person Look Right [FEC_LUP] 1st Person Look Up [FEC_LDN] 1st Person Look Down [FEC_LBH] Look Behind Vehicle [FEC_LLF] Look Left of Vehicle [FEC_LRG] Look Right of Vehicle [FEC_HRN] Horn [FEC_HBR] Vehicle Handbrake [FEC_ACL] Vehicle Accelerate [FEC_BRK] Vehicle Brake [FEC_TSM] Toggle SubMissions [FEC_CRD] Change Radio Station [FEC_ENT] Enter/Exit Vehicle [FEC_WPN] Fire Weapon [FEC_PAS] Pause [FEC_FPO] 1st Person Weapons [FEC_SMS] Show mouse pointer [FEC_CMS] Change camera mode all situations. [FEC_TSS] Take Screen Shot [FEC_DBG] DEBUG MENU [FEC_TGD] Toggle Pad Game/Debug [FEC_TDO] Turn Debug Camera Off [FEC_IVH] Invert Mouse Horizontally: [FEC_MSL] LMB [FEC_MSM] MMB [FEC_MSR] RMB [FEC_QUE] ??? [FEC_TWO] Only Two Keyboard Keys Allowed [FEC_UMS] Unique Mouse Keys only please. [FEC_OMS] Only One Mouse Keys Allowed [FEC_UJS] Unique Joystick buttons only please. [FEC_OJS] Only One Joystick Buttons per action allowed [FEC_PTL] Use LockTarget with Weapon Switch Left. [FEC_PTR] Use LockTarget with Weapon Switch Right. [FEC_LBC] Use Look Left With Look Right. [FEC_JBO] JOY ~1~ [FEC_WAR] Warning [FEC_OKK] O.K. [FEC_DLF] Delete Failed. [FEC_SVU] Save Unsuccessful. [FEC_LUN] Load Unsuccessful. File Corrupted, Please delete. [FEC_PAD] Gamepad [FEC_JOY] Joystick [FES_CSA] Select a skin from the list below: [FET_HRD] DEFAULT SETTINGS RESTORED [FET_MST] MOUSE CONTROLLED STEERING [FEC_STR] NUM STAR [FET_MIG] LEFT, RIGHT, MOUSEWHEEL TO ADJUST [FET_CIG] BACKSPACE TO CLEAR - LMB, RETURN TO CHANGE [FET_DSN] Default Player Skin.bmp [FET_RSO] ORIGINAL SETTING RESTORED [FET_RSC] HARDWARE NOT AVAILABLE - ORIGINAL SETTING RESTORED [FEA_3DH] AUDIO HARDWARE [FEA_SPK] SPEAKERS CONFIGURATION [FEM_LOD] DRAW DISTANCE [FEM_VSC] FRAME SYNC [FEM_FRM] FRAME LIMITER [FEM_MM] MAIN MENU [FED_RES] SCREEN RESOLUTION [FET_CTL] CONTROLLER SETUP [FET_OPT] OPTIONS [FEC_MSH] MOUSE SENSITIVITY [FEC_IVV] INVERT MOUSE VERTICALLY [FEC_FNC] F~1~ [FEC_IRT] INS [FEC_DLL] DEL [FEC_HME] HOME [FEC_END] END [FEC_PGU] PGUP [FEC_PGD] PGDN [FEC_UPA] UP [FEC_DWA] DOWN [FEC_LFA] LEFT [FEC_RFA] RIGHT [FEC_NUM] NUM [FEC_NMN] NUM~1~ [FEC_FWS] NUM / [FEC_PLS] NUM + [FEC_MIN] NUM - [FEC_DOT] NUM . [FEC_NLK] NUMLOCK [FEC_ETR] ENT [FEC_SLK] SCROLL LOCK [FEC_PSB] BREAK [FEC_BSP] BSPACE [FEC_TAB] TAB [FEC_CLK] CAPSLOCK [FEC_RTN] RET [FEC_LSF] LSHIFT [FEC_RSF] RSHIFT [FEC_LCT] LCTRL [FEC_RCT] RCTRL [FEC_LAL] LALT [FEC_RAL] RALT [FEC_LWD] LWIN [FEC_RWD] RWIN [FEC_WRC] WINCLICK [FEC_SPC] SPC [WIN_TTL] Grand Theft Auto VC [WIN_95] Grand Theft Auto VC cannot run on Windows 95 [WIN_DX] Grand Theft Auto VC requires at least DirectX version 8.1 [FET_EIG] CANNOT SET A CONTROL FOR THIS ACTION [FET_DAM] DYNAMIC ACOUSTIC MODELING [FEQ_SRE] Are you sure you want to quit? All progress since the last save game will be lost. Proceed? [FEQ_SRW] Are you sure you want to quit the game? [FET_QG] QUIT GAME [FEN_STA] START GAME [FET_PAU] PAUSE MENU [REPLAY] REPLAY [FEC_ANS] Action [CVT_MSG] Converting textures to optimal format for your video card [FEC_SFT] SHIFT [FEH_VMP] VIEW MAP [FES_DEE] Delete Failed! Please try again. [FES_CMP] Save failed! Please try again. [FESZ_WR] Saving current game. Please wait... [FELD_WR] Loading game. Please wait... [FEDL_WR] Deleting saved game. Please wait... [PCRESRT] Starting new game. Please wait... [FET_STI] Standard Controls [FET_CTI] Classic Controls [FET_PS] PLAYER SKIN SETUP [FEH_NA] OPTION NOT AVAILABLE [FEH_MPH] MOUSE, CURSORS TO MOVE - PGUP, PGDN, MSWHEEL TO ZOOM, L - LEGEND [FEA_MP3] MP3 PLAYER [NO_PCCD] Please insert your GTA Vice City CD, or press ESC to cancel [FEH_SSA] CURSORS TO MOVE - S TO SAVE TO FILE [FES_CMI] LAST MISSION PASSED [FET_STS] STATS SAVED TO 'STATS.HTML' + 'STATS.TXT' [WIN_VDM] Grand Theft Auto VC cannot find enough available video memory [FEC_ERI] Error! One or more control actions are not bound to a key or button. Please check all control actions are set [FEC_TFU] Turret + Lean Up [FEC_TFD] Turret + Lean Down [FET_RIG] SELECT A NEW CONTROL FOR THIS ACTION [FEA_NM3] NO MP3 FILES FOUND [FEA_MPB] MP3 VOLUME BOOST [FEA_MUS] MUSIC VOLUME [FEA_SFX] SFX VOLUME [CVT_ERR] You have run out of disk space. Please make some space on your harddisk before continuing. Press ESC to cancel. [FEA_ADP] AUTO-DETECT HARDWARE {=================================== MISSION TABLE AMBULAE ===================================} [ATUTOR2:AMBULAE] ~g~Drive the patients to Hospital CAREFULLY. Each bump reduces their chances of survival. [A_FULL:AMBULAE] ~r~Ambulance full!! [A_FAIL2:AMBULAE] ~r~Your lack of urgency has been fatal to the patient! [A_FAIL3:AMBULAE] ~r~The patient is dead!! [A_PASS:AMBULAE] Rescued! [A_COMP2:AMBULAE] You will never get tired! [A_CANC:AMBULAE] ~r~Paramedic mission cancelled! [A_COMP3:AMBULAE] Paramedic missions complete! You will never get tired when running! [ALEVEL:AMBULAE] Paramedic Mission Level ~1~ [A_FAIL1:AMBULAE] Paramedic mission ended. [A_SAVES:AMBULAE] PEOPLE SAVED: ~1~ [A_COMP1:AMBULAE] Paramedic missions complete: $~1~ {=================================== MISSION TABLE ASSIN1 ===================================} [ASM1_5:ASSIN1] ~r~Your target completed his deliveries! [ASM1_6:ASSIN1] Deliveries left: [ASM1_7:ASSIN1] ~g~Carl Pearson, Pizza Delivery Man. Kill him before he completes his deliveries. [ASM1_A:ASSIN1] Mr. Teal, your help in eradicating those out-of-towners was invaluable to business. I have more work for you with a more 'hands-on' approach. [ASM1_D:ASSIN1] Mr. Teal, your help in eradicating those out-of-towners was invaluable to business. {=================================== MISSION TABLE ASSIN2 ===================================} [ASM2_1:ASSIN2] ~g~Mrs. Dawson will be leaving the Jewelry shop in Vice Point soon. Kill her. It must look like a car accident. [ASM2_3:ASSIN2] ~g~She's gonna blow! Get out of there! [ASM2_4:ASSIN2] ~r~You smashed up her car when she wasn't in it! She won't touch it now! [ASM2_5:ASSIN2] ~r~She got away! [ASM2_6:ASSIN2] ~r~You were too near the scene of the 'accident'! [ASM2_7:ASSIN2] ~r~It's supposed to look like an accident! Try ramming her off the road instead! [ASM2_8:ASSIN2] ~g~You must make Mrs. Dawson's death look like an accident. Do not use any weapons. [ASM2_9:ASSIN2] ~g~You need some wheels for this job! [ASM2_10:ASSIN2] ~g~When her car bursts into flames make your self scarce! [ASM2_11:ASSIN2] Help me! [ASM2_12:ASSIN2] Somebody help me! [ASM2_13:ASSIN2] Oh my god! [ASM2_A:ASSIN2] My compliments on a job well done Mr. Teal. My client was very pleased. [ASM2_2:ASSIN2] health: {=================================== MISSION TABLE ASSIN3 ===================================} [ASM3_11:ASSIN3] TIME: [ASM3_C:ASSIN3] A European gang plans to hit a bank in Vice City. My employers would rather this didn't happen. [ASM3_D:ASSIN3] Each member of the gang has a cover while they are here in Vice City. Some have menial jobs, others are on vacation. [ASM3_E:ASSIN3] Each target and their likely whereabouts are taped under the phone. [ASM3_14:ASSIN3] ~g~Dick Tanner is working for DBP Security in Ocean Drive. [ASM3_15:ASSIN3] ~g~Marcus Hammond and Franco Carter are located near the Jewelry shop in Vice Point. [ASM3_16:ASSIN3] ~g~Nick Kong is cruising off Washington Beach. [ASM3_18:ASSIN3] ~g~Don't get too close to the target or he may spot you! [ASM3_19:ASSIN3] ~g~He's seen you! Waste him! [ASM3_20:ASSIN3] ~g~They have seen you! Make sure you waste them both! [ASM3_21:ASSIN3] ~r~You did not kill all the gang members in time! [ASM3_22:ASSIN3] ~g~Don't get too close to the targets or they may spot you! [ASM3_12:ASSIN3] ~g~A selection of weapons have been left for you nearby if you require them. You have ~h~9 MINUTES ~g~to kill all members of the gang. [ASM3_13:ASSIN3] ~g~Mike Griffin is working on an advertising board in Washington. [ASM3_17:ASSIN3] ~g~Charlie Dilson is riding in Washington. {=================================== MISSION TABLE ASSIN4 ===================================} [ASM4_10:ASSIN4] ~g~Looks like you're not the only one after the briefcase! Get it to Ammu-Nation, quick time! [ASM4_12:ASSIN4] Distance: [ASM4_15:ASSIN4] ~g~Get the sniper rifle to your right. [ASM4_16:ASSIN4] ~g~Watch the woman on the balcony, she will walk down the escalators and ask someone the time. [ASM4_17:ASSIN4] ~g~Once the conversation has finished kill the person she spoke to, but do not kill her. [ASM4_18:ASSIN4] ~g~Once the target is dead retrieve his briefcase and take it to Ammu-Nation in Downtown. [ASM4_19:ASSIN4] ~g~Keep your distance from the target! Use the distance bar in the upper right corner of the screen. [ASM4_20:ASSIN4] ~g~If it gets full he will see you. [ASM4_21:ASSIN4] ~g~Get the briefcase! [ASM4_22:ASSIN4] ~g~Take the briefcase to Ammu-Nation in Downtown. [ASM4_23:ASSIN4] ~g~He has spotted you! Nail him and get the briefcase! [ASM4_25:ASSIN4] ~r~You killed the woman you fool! [ASM4_26:ASSIN4] ~r~The target has boarded his flight! [ASM4_27:ASSIN4] ~r~The target has seen you! You should have kept your distance! [ASM4_28:ASSIN4] ~r~The target heard you firing your weapon! [ASM4_29:ASSIN4] ~r~Itchy trigger finger? You killed him too soon! [ASM4_A:ASSIN4] Time to fry bigger fish, Mr. Teal. There's a rifle in the foliage to your right. [ASM4_B:ASSIN4] Watch the woman standing on the balcony above the check-in desks. She will walk through the crowd and ask someone the time. [ASM4_C:ASSIN4] You must kill that person, retrieve his case and take it to the location taped under the phone. {=================================== MISSION TABLE ASSIN5 ===================================} [ASM5_A:ASSIN5] There is a valuable exchange taking place on the roof of the Cherry Popper Ice Cream Company. [ASM5_B:ASSIN5] Kill everyone involved, steal the merchandise and take it to the helipad at the airport. [ASM5_C:ASSIN5] There is a gate to your left that leads to the back of the factory. [ASM5_1:ASSIN5] ~g~Enter the compound behind the Cherry Popper Ice Cream Company The deal is taking place on the roof. [ASM5_2:ASSIN5] ~g~Get the merchandise and take it to the helipad at the airport! [ASM5_3:ASSIN5] ~g~Take the merchandise to the helipad at the airport! {=================================== MISSION TABLE BANKJ1 ===================================} [WANTED1:BANKJ1] ~g~Shake the cops and lose your wanted level! [BJM1_A:BANKJ1] Tommy! Hey, Tommy, look at this, this is great! I've got us this minibar installed! [BJM1_B:BANKJ1] We got a whole bar downstairs, Ken. [BJM1_C:BANKJ1] Yeah, yeah, whatever. Well, I got the chalkboard you asked for. [BJM1_D:BANKJ1] Ah, that's the benefit of a law school education: the ability to follow instructions. [BJM1_E:BANKJ1] Now, I need a safe man. [BJM1_F:BANKJ1] Oh, all right, well, let me think...safe, safe, safe, safe - I got it! This guy will blow you away! [BJM1_G:BANKJ1] Ahh, nah, that schmuck. He's on the inside. [BJM1_H:BANKJ1] Where inside? [BJM1_I:BANKJ1] In a police headquarter cell awaiting transfer. [BJM1_J:BANKJ1] I think he's about to get paroled.... [BJM1_1:BANKJ1] ~g~Break Cam Jones out of police custody! [BJM1_3:BANKJ1] ~g~You will find something useful in the station's locker room. [BJM1_21:BANKJ1] ~g~The key card to the cells can be found upstairs in the station. [BNK1_7:BANKJ1] Cam Jones? [BNK1_8:BANKJ1] I'm busting you out! [BNK1_10:BANKJ1] Yeah, that's me.. [BNK1_11:BANKJ1] Whatever you say! [BNK1_13:BANKJ1] I'm gonna be doing a job and you're my safe cracker. [BNK1_14:BANKJ1] Beats losing my ass in a cell! [BJM1_22:BANKJ1] ~g~Get Cam back to his house! [BJM1_23:BANKJ1] ~g~You need to get the key card first! [BNK1_12:BANKJ1] Lose the heat and get me back to my place! [BJM1_20:BANKJ1] Put the weapon away or face the consequences! [BJM1_5:BANKJ1] Only authorized personnel beyond this point! [BJM1_2:BANKJ1] ~r~You were supposed to bust Cam out, not get him killed! [BJM1_4:BANKJ1] He's armed! Kill him! {=================================== MISSION TABLE BANKJ2 ===================================} [BJM2_A:BANKJ2] We need a stick up man. You know one? [BJM2_B:BANKJ2] Hey, Tommy, Tommy, Tommy, this stuff keeps you sharp, man. [BJM2_C:BANKJ2] WoooOOOooo! [BJM2_D:BANKJ2] I could be your stick up man! Stick 'em up! Stick 'em up! [BJM2_E:BANKJ2] You ain't a stick up man, you're an idiot. [BJM2_F:BANKJ2] Now get yourself a drink and shut up. [BJM2_G:BANKJ2] Hey, get outta my way! Yeh yeh yeh - ow ow ow! [BJM2_H:BANKJ2] Cam, what do you think? [BJM2_I:BANKJ2] Well, the best shooter in this town is a guy named Cassidy. [BJM2_J:BANKJ2] Is that so? [BJM2_K:BANKJ2] Yeah. A military guy, or he thinks he is. [BJM2_L:BANKJ2] I doubt he was ever in the army, but he certainly knows how to get a hold of guns. [BJM2_M:BANKJ2] He'll be down at the shooting range. [BJM2_2A:BANKJ2] You Phil Cassidy? [BJM2_2B:BANKJ2] Why? [BJM2_2C:BANKJ2] I'm looking for a man who can handle a gun. From this setup, I'm not too convinced. [BJM2_2D:BANKJ2] Son, I could shoot a fly off your head at 80 feet. [BJM2_2E:BANKJ2] Oh really? [BJM2_2F:BANKJ2] Yeah. I learnt in the army. [BJM2_2G:BANKJ2] Fly shooting real popular in the army? Glad I don't pay tax. [BJM2_2H:BANKJ2] You tryin' to be funny kid? [BJM2_2I:BANKJ2] Ha ha ha ha ha! [BJM2_2J:BANKJ2] Let's shoot. [BJM2_1:BANKJ2] ~g~Go to Ammu-Nation in Downtown and talk to Phil Cassidy. [BJM2_3:BANKJ2] HIT RATE: ~1~% [BJM2_4:BANKJ2] SCORE ROUND ONE: ~1~ [BJM2_6:BANKJ2] SCORE ROUND TWO: ~1~ [BJM2_7:BANKJ2] TOTAL SCORE FOR SHOOT: ~1~ [BJM2_9:BANKJ2] ~g~Get to round two's starting point. [BJM2_11:BANKJ2] ~r~Phil's dead! [BJM2_12:BANKJ2] ~r~One of the shooters is dead! [BJM2_14:BANKJ2] ~g~Move on to the next area! [BJM2_15:BANKJ2] SCORE: [BJM2_17:BANKJ2] ~g~Go and talk to Phil. [BJM2_18:BANKJ2] SCORE TO BEAT: [BJM2_19:BANKJ2] ~g~Hit as many targets as you can in the time limit! [BJM2_22:BANKJ2] ~r~You have left the shooting range! [BJM2_23:BANKJ2] ~g~If you leave the shooting range during the competition, you will fail the mission. [BJM2_24:BANKJ2] ~g~The closest target is worth one point. [BJM2_25:BANKJ2] ~g~The middle target is worth two points. [BJM2_27:BANKJ2] ~g~All targets this round are worth one point. [BNK2_2:BANKJ2] AIM 3-2-1 FIRE! [BNK2_3:BANKJ2] AREA CLEAR! [BNK2_4:BANKJ2] Hoooeee! [BNK2_5:BANKJ2] Couldn't hit a barn door! [BNK2_7:BANKJ2] So you wanna do me a favor, and help me put together a job? [BNK2_8:BANKJ2] Son, after shooting like that, if you asked me to be your wife, I'd say yes. [BNK2_9A:BANKJ2] Son, you better get your fancy talking and big ideas and shove 'em where there ain't no sun. You can't shoot nothin'! [BNK2_9B:BANKJ2] You can't shoot nothin'. [BJM2_28:BANKJ2] SCORE ROUND THREE: ~1~ [BJM2_20:BANKJ2] ~g~When you run out of ~w~time ~g~or ~w~ammunition ~g~the round is over! [BJM2_26:BANKJ2] ~g~The far target is worth three points. [BNK2_1:BANKJ2] LIVE AMMUNITION [RANGE_1:BANKJ2] SCORE FOR SHOOT: ~1~ [BJM2_2:BANKJ2] ~g~To exit the round press the ~h~~k~~PED_JUMPING~ ~g~button. [BJM2_N:BANKJ2] Relax {=================================== MISSION TABLE BANKJ3 ===================================} [BJM3_A:BANKJ3] Things are starting to come together nicely here. [BJM3_B:BANKJ3] What's the plan, Tommy? Que pasa, amigo? [BJM3_C:BANKJ3] The plan is you keep doing that like a moron. Anyhow, we need a driver. [BJM3_D:BANKJ3] Tommy, I'll do it. I can drive. [BJM3_E:BANKJ3] You want Hilary, mister. Not some smart-talking law school chump. [BJM3_F:BANKJ3] Hilary's the real deal. You ain't never seen anyone drive so fast. I'll give him a call here. [BJM3_G:BANKJ3] Hey Hil, it's Phil. How's it going? No. don't talk. We'll reminisce later. You want to do me a favor? [BJM3_H:BANKJ3] I got me a guy from up north. No, no, I don't think he was in the service, but he wants a driver. [BJM3_I:BANKJ3] For a bit of action. Okay, I understand. [BJM3_J:BANKJ3] What'd he say? [BJM3_K:BANKJ3] Well, he'll do it, no problem. Well, there might be a little problem - see, he has abandonment issues. [BJM3_L:BANKJ3] Seems he won't work for anyone who can't beat him. Something to do with his momma. [BJM3_M:BANKJ3] Anyway, he wants to race you first, said he'd meet you outside.. [BJM3_2A:BANKJ3] You Tommy? Of course you're Tommy, I mean, [BJM3_2B:BANKJ3] Why else would anyone want to speak to me? [BJM3_2C:BANKJ3] OK. Consider it this way - [BJM3_2D:BANKJ3] I'll drive for you IF, and only IF, you can drive properly. [BJM3_2E:BANKJ3] Leave me alone - and I'll never forgive you. [BJM3_2:BANKJ3] ~r~Hilary is dead! [BJM3_4:BANKJ3] ~g~You need a car to take part! [BNK3_1:BANKJ3] Ok. I'll drive for you, but please, treat me bad. [BNK3_3A:BANKJ3] Illegal street race in progress at Vice Point. [BNK3_3B:BANKJ3] Calling all officers. [BNK3_3C:BANKJ3] Street racers, this is illegal and forbidden! {=================================== MISSION TABLE BANKJ4 ===================================} [BNK4_A:BANKJ4] ~w~As you can see, gentlemen, this is going to be the easiest buck we ever made. [BNK4_B:BANKJ4] ~w~Tommy, seriously, you gotta consider going into law. [BNK4_C:BANKJ4] ~w~What the hell are you smoking, man? This ain't no simple plan! [BNK4_D:BANKJ4] ~w~Well, who needs a simple plan anyway? [BNK4_E:BANKJ4] ~w~Take communism, now that was a simple plan. Didn't do Russia any favors, huh? [BNK4_F:BANKJ4] ~w~Calm down, all right? With a team like this it's going to be no problem. [BNK4_G:BANKJ4] ~w~We got Cam on safe. Phil? You and me will handle security, and Hilary'll drive the getaway car. [BNK4_H:BANKJ4] ~w~Uh, heh heh, aren't you forgetting somebody? Somebody who helped you to no end in this town? Somebody who... [BNK4_I:BANKJ4] ~w~Ken... Ken, that's right. Ken here, he washes the money for us and he keeps the drinks on ice. [BNK4_J:BANKJ4] ~w~I don't understand what I am supposed to be doing here. [BNK4_K:BANKJ4] ~w~Look, it's easy. Haven't you ever seen a movie? [BNK4_L:BANKJ4] ~w~We walk into the bank, we wave the gun around, and leave very rich men. [P_DEAD:BANKJ4] ~r~Phil's dead!! [C_DEAD:BANKJ4] ~r~Cam's dead!! [H_DEAD:BANKJ4] ~r~Hilary's dead!! [P_HIND:BANKJ4] ~r~You've lost Phil! [C_HIND:BANKJ4] ~r~Cam's been left behind! [H_HIND:BANKJ4] ~r~Hilary's been abandoned! [GETCAR:BANKJ4] ~g~Get in the getaway car to do the bank job! [TRASHED:BANKJ4] ~r~YOU TRASHED THE GETAWAY CAR!! [BNK4_1:BANKJ4] I'll drive. [BNK4_2:BANKJ4] Great. A passenger. Wait 'til I tell the group about this. [BNK4_3A:BANKJ4] Hey, watch the wheels, Tommy! [BNK4_3B:BANKJ4] Tommy, Hilary's taking up too much room! [BNK4_3C:BANKJ4] I am not! [BNK4_3D:BANKJ4] Are too! [BNK4_3E:BANKJ4] Hey, shut up you two, or you can get out and walk. [BNK4_3F:BANKJ4] Yeah - HILARY. [BNK4_3I:BANKJ4] For god's sake, Phil, stop waving that thing around! [BNK4_3J:BANKJ4] Yeah, you'll have somebody's eye out! [BNK4_3M:BANKJ4] The car's ruined. RUINED! [BNK4_3O:BANKJ4] You cling to an illusion of permanence. [BNK4_3P:BANKJ4] What? [BNK4_3Q:BANKJ4] You think all things will last. [BNK4_3R:BANKJ4] Youth, loved ones, pizza, [BNK4_3S:BANKJ4] All will pass or end and you must accept that. [BNK4_3T:BANKJ4] You're right. Thanks, Cam. [BNK4_3U:BANKJ4] Don't mention it. [BNK4_3V:BANKJ4] Hey Tommy, why have we stopped? [BNK4_4A:BANKJ4] ~w~Keep driving around the block, OK? [BNK4_5:BANKJ4] ~w~Okay, Tommy, Okay. [BNK4_6:BANKJ4] ~w~THIS IS A RAID! [BNK4_7:BANKJ4] ~w~NOBODY MOVE! [BNK4_8:BANKJ4] ~w~EVERYBODY UP AGAINST THAT WALL! [BNK4_9:BANKJ4] Phil, hold down the fort! [BNK4_10:BANKJ4] Wilco roger that! [BNK4_11:BANKJ4] Come on Cam, the vault's upstairs... [BK4_12A:BANKJ4] Damn! It's a Flange 9000! [BK4_12B:BANKJ4] This could take hours to crack, [BK4_12C:BANKJ4] Or five minutes if you could find the manager. [BNK4_13:BANKJ4] I'll go find where he's holed up. [BK4_14A:BANKJ4] Phil, things still sweet? [BNK4_15:BANKJ4] Sure. Everything's reeaal quiet. [BNK4_16:BANKJ4] You - you're coming with me! [BNK4_17:BANKJ4] Ok! Ok! Just don't shoot! [BNK4_18:BANKJ4] I SAID NOBODY MOVE! [BK4_19A:BANKJ4] It's on a time lock, [BK4_19B:BANKJ4] You might as well give up now! [BK4_20A:BANKJ4] Hell, I can bypass the time lock, [BK4_20B:BANKJ4] Then we just need your key code and we're good! [BNK4_21:BANKJ4] Stay here. You try anything and you're dead. [BNK422A:BANKJ4] Cam, how long? [BK4_23A:BANKJ4] Give me 5 minutes! [BK4_24A:BANKJ4] I'm gonna check on Phil, I'll be right back. [BK4_24B:BANKJ4] I told you not to touch that alarm! [BNK4_25:BANKJ4] The SWAT team will be here any minute! [BNK4_27:BANKJ4] I could do with some help here, Tommy! [BNK4_28:BANKJ4] Vice City S.W.A.T! You are completely surrounded! [BNK4_29:BANKJ4] Surrounded? HA HA HA HAAAAAaaa! [BNK4_30:BANKJ4] They're crapping themselves, corrupt bastards! [BK4_31A:BANKJ4] Tommy! The vault's open! [BK4_34A:BANKJ4] Ok, we got the SWAT retirement fund. Let's get out of here! [BK4_34B:BANKJ4] Ok, you asked for it! You've had your last chance! [BK4_35A:BANKJ4] They're storming the place! [BK4_35B:BANKJ4] Take cover! [BNK4_94:BANKJ4] ~w~Ok, guys. Nice an easy just as we planned. [BM_DEAD:BANKJ4] ~r~You needed the bank manager alive!! [ASSET_A:BANKJ4] BANK HEIST ASSET COMPLETED! [ASSET_B:BANKJ4] ~g~The Malibu Club will now generate revenue up to a maximum of $~1~ per day. Pick up your cash regularly! [IDIOT:BANKJ4] ~r~ That's right, just wander about dressed like a lunatic and draw attention to yourself, IDIOT! {=================================== MISSION TABLE BARON1 ===================================} [COK1_A:BARON1] Come on, baby, go! Yeah! Yeah! Arrrrr! [COK1_B:BARON1] Stupid horse! I'll chop your head off! Grrrrr... [COK1_C:BARON1] Who is this dickhead? [COK1_D:BARON1] Tommy Vercetti. You remember me. [COK1_E:BARON1] Excuse me. I'm a little anxious. Never trust a goddamn horse! [COK1_F:BARON1] You do a good job - you work for me now. [COK1_H:BARON1] As I said, amigo, you work for me now. Shut up. Some Judas has betrayed me. [COK1_I:BARON1] He thinks I don't know how much money I should be making, but stealing 3% is as good as stealing 100%. [COK1_J:BARON1] No one does this to me. NO ONE!! [COK1_K:BARON1] You follow him from his apartment and you see where he goes! Later, we will kill him. [COK1_1:BARON1] Ooh shit! [COK1_2:BARON1] Too slow grandad! [COK1_4:BARON1] Loser. [COK1_5:BARON1] You better keep on running, asshole! [COK1_8:BARON1] ~g~Quick! Grab some wheels and follow him! [COK1_9:BARON1] ~r~You're supposed to follow him, not kill him! [COK1_10:BARON1] ~g~Go to the thief's house and find out where he's stashing the money. [COK1_11:BARON1] ~g~Have a look through his window. [COK1_7:BARON1] ~g~He's escaped to the roof, keep on his tail but don't kill him! [COK1_G:BARON1] I work for money. {=================================== MISSION TABLE BARON2 ===================================} [COK2_A:BARON2] What kind of incompetent fool are you? [COK2_B:BARON2] FOOL! FOOL! FOOL! FOOL! [COK2_C:BARON2] Tommy! [COK2_D:BARON2] What, Ricardo? [COK2_E:BARON2] These idiots - they always trying to screw you. [COK2_F:BARON2] That's the problem with this business. [COK2_G:BARON2] What do you think you're doing? [COK2_H:BARON2] These pricks have failed me miserably, [COK2_I:BARON2] Soon any mom and pop will think they can sell gallo in Vice City. [COK2_J:BARON2] What next, huh? The stinking Mafia?! [COK2_K:BARON2] That gang place is a fortress at ground level, [COK2_L:BARON2] so Quentin here - Quentin! QUENTIN! [COK2_M:BARON2] He'll fly you over the area! [COK2_N:BARON2] Eradicate them! [COK2_O:BARON2] What do you think you're doing? [COK2_P:BARON2] What are you doing here? [COK2_Q:BARON2] Hey, I've been asking around and it's obvious [COK2_R:BARON2] that Diaz jumped the deal and iced my brother. [COK2_S:BARON2] And he'll kill you, too! [COK2_T:BARON2] I can take Diaz! [COK2_U:BARON2] No - listen to me! I'll handle Diaz - [COK2_1:BARON2] One thing puzzling me, What's with 'Quentin!? [COK2_2:BARON2] I dunno, I always kinda liked it...Quentin Vance... [COK2_3:BARON2] Vance? Your name's Lance Vance? [COK2_4:BARON2] Hey! I got enough of that at school! [COK2_5:BARON2] You ever fired one of those from a whirly? [COK2_8:BARON2] Where the hell are we headed anyway? [COK2_9:BARON2] Prawn Island. [COK2_13:BARON2] Lance Vance. Poor bastard. [COK2_14:BARON2] Ok, we're almost there. [COK2_15:BARON2] We'll make a couple of passes. [COK2_16:BARON2] So take out as many guns as you can. [COK2_17:BARON2] Then I'll set you down and you're on your way. [COK2_20:BARON2] Damn! This is a war zone! Take out some of those gunmen! [COK2_21:BARON2] We're taking hits here, man! [COK2_22:BARON2] This thing ain't cheap to fix! Take them out! [COK2_23:BARON2] Ok, you're on your own from here! Good luck, brother! [COK2_24:BARON2] Heli Health: [COK2_25:BARON2] ~g~Go and collect the money on the roof. [COK2_27:BARON2] You're on MY turf asshole! [COK2_28:BARON2] You're going down! [COK2_6:BARON2] No. I'll get a bit of practice on the way though. [OPEN_B:BARON2] The road blocks to the mainland have now been removed [COK2_V:BARON2] - he's beginning to trust me. {=================================== MISSION TABLE BARON3 ===================================} [COK3_A:BARON3] Not so pleased with your selves NOW, huh! [COK3_B:BARON3] Ahahahahaa, Ahahahahaa. [COK3_C:BARON3] Whoa! Watch where you're waving that thing! [COK3_D:BARON3] No more pigeon shit on MY car, eh Tommy! [COK3_E:BARON3] Guess not. [COK3_F:BARON3] You're damn right. Now listen, [COK3_G:BARON3] you know who owns the fastest boat on the east coast? [COK3_H:BARON3] Not off hand, no. [COK3_I:BARON3] ME. And I want it to stay that way. [COK3_J:BARON3] Every smuggler from here to Caracas has one dream, a faster boat. [COK3_K:BARON3] Rumor has it the boatyard has just completed such a vessel. [COK3_L:BARON3] for some Costa Rican dickhead. [COK3_M:BARON3] And Tommy...I WANT THAT BOAT!!! [COK3_N:BARON3] I think your pigeons are back. [COK3_O:BARON3] Ah! I thought I got you. Where'd you come from? [COK3_P:BARON3] Pigeons! Boom! Aaaaah! [COK3_5:BARON3] ~g~Find the switch to lower the boat. [COK3_6:BARON3] ~g~Get the boat to the mansion. [COK3_7:BARON3] ~r~You destroyed the boat! [COK3_8:BARON3] ~g~Go to the boatyard at the docks and steal the fastest boat. [COK3_9:BARON3] ~g~Now get into the boat. {=================================== MISSION TABLE BARON4 ===================================} [COK4_A:BARON4] Eject! PLASTIC CRAP! [COK4_B:BARON4] You doing this to me? [COK4_C:BARON4] Who do you think you are, you piece of plastic SHIT? Aaarrgh! [COK4_D:BARON4] SCREW YOU! [COK4_E:BARON4] It eats my favorite El burro movie, it die! [COK4_F:BARON4] What else could I do? [COK4_G:BARON4] It's probably not plugged in. [COK4_H:BARON4] What? [COK4_I:BARON4] Damn - no matter, I can buy a hundred more. [COK4_J:BARON4] Now Tommy, [COK4_K:BARON4] each month a freelancer sails into Vice City and moors his yacht. [COK4_L:BARON4] He sells his cargo to the first boat. [COK4_M:BARON4] I want you to take the speedboat [COK4_N:BARON4] and beat all the other shitheads to it, [COK4_O:BARON4] then you bring the cargo here, ok!? [COK4_P:BARON4] Let me guess, you thought I could use a guardian angel. [COK4_Q:BARON4] I'm just saying you need to let me in there, my man. [COK4_T:BARON4] and you're probably gonna wanna kiss me! [COK4_U:BARON4] Wacko. [COK4_V:BARON4] Hahahahahaha! [COK4_1:BARON4] So Tommy, we know it was Diaz busted our deal.. [COK4_3:BARON4] So why the hell are we running errands for him? [COK4_4:BARON4] The more we learn now, the less we have to learn when we take this town over! [COK4_5:BARON4] I like your style, man. Real fresh. [COK4_12:BARON4] Watch yourself, they're coming from all over! [COK4_13:BARON4] Got 'em. Head for Diaz's as fast as you can! [COK4_14:BARON4] You want some of this?! [COK4_15:BARON4] Sleep with the fish! [COK4_16:BARON4] Eat it! EAT IT! [COK4_19:BARON4] More trouble up ahead! [COK4_20:BARON4] There are gunmen on that jetty! [COK4_24:BARON4] Good shooting, my friend. You're a real, proper, grade A lunatic. [COK4_25:BARON4] Well, thank you. [COK4_26:BARON4] See you around, Tommy. [COK4_27:BARON4] Okay, Mr. Lance Vance Dance. [COK4_28:BARON4] ~g~Get to the yacht before the other boats do! [COK4_31:BARON4] ~g~Go to the fastest boat at the jetty! [COK4_32:BARON4] ~r~Too slow! [COK4_33:BARON4] ~r~You destroyed the boat! [COK4_34:BARON4] ~g~Take those boats out! [COK4_35:BARON4] Boat health: [COK4_R:BARON4] Now you can feed me all this 'lonely tough guy' crap, [COK4_S:BARON4] but I know one day I'm gonna save your ass, {=================================== MISSION TABLE BARON5 ===================================} [PROP_A:BARON5] PROPERTY ACQUIRED! [COK4_30:BARON5] ~r~Lance is dead! [ASS1_A:BARON5] I got us some cannons in the trunk. [ASS1_B:BARON5] Holy shit! Where'd you get all this stuff? [ASS1_C:BARON5] Been saving it for a rainy day. [ASS1_D:BARON5] You like? [ASS1_E:BARON5] Yeah, I like. [ASS1_F:BARON5] You stupid pricks... [ASS1_G:BARON5] my beautiful house [ASS1_H:BARON5] look what you've done to it! [ASS1_I:BARON5] This is for my brother! [ASS1_J:BARON5] I trusted you, Tommy. [ASS1_K:BARON5] I woulda had you made... [ASS1_L:BARON5] Say goodnight, Mr. Diaz. [ASS1_1:BARON5] This place is going to be crawling with assholes...be careful... [ASS1_2:BARON5] Don't worry Tommy, I'll cover you. [ASS1_13:BARON5] DIAZ?! I've come to take over your business! [ASS1_15:BARON5] ~g~Storm the mansion and kill Diaz! [ASS1_16:BARON5] ~g~Kill Diaz! [ASS1_17:BARON5] ~g~There are multiple routes into the mansion. [BUD1:BARON5] Lances Health: [ASS1_18:BARON5] ~g~The door is locked, try another route. [ASS1_19:BARON5] This way! [ASS1_14:BARON5] TOMMY! You betrayed me, you idiot! I'm gonna kill you real soon.. [ASS1_20:BARON5] Tommy, my problem with Quentin, not wit you, man! {=================================== MISSION TABLE BIKE1 ===================================} [BM1_A:BIKE1] Where's Baker? [BM1_B:BIKE1] I'm looking for Big Mitch Baker... [BM1_C:BIKE1] Who's lookin'? [BM1_D:BIKE1] Tommy Vercetti. [BM1_E:BIKE1] Vercetti [BM1_F:BIKE1] You don't look like the law, so that's bought you a minute. [BM1_G:BIKE1] You better talk fast. [BM1_H:BIKE1] Kent Paul said you might be interested in pulling security for a gig he's got set up. [BM1_I:BIKE1] Kent Paul? Sheesh! No wonder he sent ya. [BM1_J:BIKE1] The last time he was here he left through the window in nothing but his limey birthday suit. [BM1_K:BIKE1] Are you interested or not? [BM1_L:BIKE1] We only do favors for our own. [BM1_M:BIKE1] How do I join? [BM1_N:BIKE1] This ain't no country club, boy. Can you handle a bike? [BM1_O:BIKE1] Can you sit on a stool and drink? [BM1_P:BIKE1] Cougar, Zeppelin, go see how this girl handles a bike... [BM1_3:BIKE1] ~r~The racers have been attacked! [BIKE1_1:BIKE1] All right, fancy clothes. Let's see what you can do. [BM1_1:BIKE1] ~g~Get a Freeway or an Angel and get to the starting grid. [BM1_2:BIKE1] ~g~You need a Freeway or an Angel to compete! {=================================== MISSION TABLE BIKE2 ===================================} [BM2_1:BIKE2] CHAOSMETER: [BM2_A:BIKE2] Ah, got ya again. [BM2_B:BIKE2] Hey Vercetti. [BM2_C:BIKE2] Cougar says you can handle a bike pretty good. [BM2_D:BIKE2] Yeah, how many more errands am I gonna to have to run? [BM2_E:BIKE2] I'm a very busy man. [BM2_F:BIKE2] If it's a fight that's gonna settle this then bring it on. [BM2_G:BIKE2] Being one of us ain't just about brawlin'. It's about being part of a family. [BM2_H:BIKE2] Yeah, I've been part of a family before alright. It didn't work out. [BM2_I:BIKE2] Yeah, right, but this family takes care of its own. [BM2_J:BIKE2] We don't ask a man to do the dirty work and then let him do fifteen years hard time. [BM2_K:BIKE2] Yeah, that's right. I've done my homework. [BM2_L:BIKE2] This here's the biggest family of misfits, outcasts and badasses. [BM2_M:BIKE2] Hell, some of us has even been betrayed by our own country. [BM2_N:BIKE2] I was locked up during 'Nam. Ugly business. [BM2_O:BIKE2] Which is why I'm gonna ask you to go mess with the man. [BM2_Q:BIKE2] So get out there, grab a bike and show this city how pissed you are! [BM2_R:BIKE2] Alright, alright. [BM2_3:BIKE2] ~g~This sound indicates that you have filled a part of the meter, continue to do so. [BM2_2:BIKE2] ~g~You must fill the Chaos Meter in the time given to show us how much of a badass you are! [BM2_4:BIKE2] ~r~You failed to fill the Chaos Meter in time! [BM2_P:BIKE2] This whole damn country needs a kick in the ass, and we're the ones to deliver it. {=================================== MISSION TABLE BIKE3 ===================================} [BM3_A:BIKE3] Hey there, Mitch. [BM3_B:BIKE3] Well, if it ain't 'bad ass' Vercetti. [BM3_C:BIKE3] Now I wanna see how good you can fight for your patch. [BM3_D:BIKE3] A local street gang made the mistake of stealing my hog... [BM3_E:BIKE3] probably because of some machismo thing or somethin'. [BM3_F:BIKE3] Me and the boys would go over there and teach them a lesson in respect an'all. [BM3_G:BIKE3] Anyways. [BM3_H:BIKE3] Then I got to thinking - this would make a good initiation for you. [BM3_I:BIKE3] You get my bike back, you can tell Paul he's got his security. [BM3_2:BIKE3] ~r~You were supposed to bring the bike back, not destroy it! [BM3_3:BIKE3] ~g~Get the bike back to the bar! [BM3_4:BIKE3] ~g~Get on the bike! [INTRUDE:BIKE3] ~g~You've been spotted! [BM3_6:BIKE3] ~g~They are holed up behind Ammu-Nation in the Downtown area. [BM3_7:BIKE3] ~g~You will need a fast bike to gain access to the roof. [BM3_8:BIKE3] ~g~Use the bike to jump from these stairs to the roof on the far side of the road. [BM3_1:BIKE3] ~g~A local gang has stolen Mitch Baker's Angel. Get it back! [BM3_9:BIKE3] ~g~Get Mitch's Angel and get out of there! {=================================== MISSION TABLE BMX_1 ===================================} [GETBIK2:BMX_1] ~r~You have ~w~~1~ seconds ~g~to get on a Dirt Bike! {=================================== MISSION TABLE BOATBUY ===================================} [DRUG_2:BOATBUY] Put it out. There's a dude here. [DRUG_3:BOATBUY] Hey suit dude! I guess you're the new owner? [DRUG_4:BOATBUY] Yeah. Which one of the boats is the fastest? [DRUG_5:BOATBUY] It's already in the water, dude, [DRUG_6:BOATBUY] I though you might want to try her out. [DRUG_7:BOATBUY] Dude, she's already running with a 300 horse power engine... [DRUG_8:BOATBUY] ...and the fiberglass hull, she just shoots through the waves.! [DRUG_9:BOATBUY] She can do like zero to sixty in four seconds flat dude... [DRUG_10:BOATBUY] and she can hold like twenty bales of the best Jamaican smoke right in the hull! [DRUG_11:BOATBUY] So go ahead dude, she's ready to fly! [DRUG_12:BOATBUY] Yo yo, uh, suit dude, you gotta light? [DRUG_1:BOATBUY] Hello? Hel-lo?! Hello? [DRUG_13:BOATBUY] Dude? {=================================== MISSION TABLE CAP_1 ===================================} [CAP1_B1:CAP_1] ~g~The Mafia is taxing your businesses. Find them and kill them. [CAP1_B2:CAP_1] ~g~The Mafia has taxed the Boatyard! [CAP1_B3:CAP_1] ~g~The Mafia has taxed the Ice Cream Factory! [CAP1_B4:CAP_1] ~g~The Mafia has taxed the Car Showroom! [CAP1_B5:CAP_1] ~g~The Mafia has taxed the Taxi Firm! [CAP_01:CAP_1] Ok, what's the emergency? [CAP_02:CAP_1] WHO? [CAP_03:CAP_1] Tommy...some mob thugs ...said they'd come to take their cut... [CAP_04:CAP_1] ...said it was a Mr. Forello's money...I feel like crap. [CAP_05:CAP_1] Forelli? SONNY Forelli? [CAP_06:CAP_1] Yeah, that's the guy...I think...they were very insistent... [CAP_07:CAP_1] Don't you worry, pop, I'm not angry with you. [CAP_08:CAP_1] Get him to the hospital. [CAP_09:CAP_1] Tommy...rip that guy a new asshole for me... [CAP_10:CAP_1] I'm gonna rip him two! [CAP1_2:CAP_1] You know the rules, Vercetti! [CAP1_3:CAP_1] Mr. Forelli sends his regards! [CAP1_4:CAP_1] It's the Harwood Butcher! [CAP1_5:CAP_1] You tell Sonny - stay away! [CAP1_6:CAP_1] Vice City is MINE now, NOT his. [CAP1_7:CAP_1] You think you can take me down, Vercetti? [CAP1_8:CAP_1] We'll keep coming after you until you die, Vercetti. [CAP1_9:CAP_1] You don't stand a chance, you psychotic prick. [CAP1_10:CAP_1] I'll murder you Vercetti. [CAP1_11:CAP_1] You always were a jerk. [CAP1_12:CAP_1] You're going to die, Vercetti. [CAP1_B6:CAP_1] ~g~You have found the collector, kill him. [CAP1_B7:CAP_1] ~g~You've lost the collector. [CAP1_B8:CAP_1] ~r~The collector has taxed all of your businesses. [CAP1_B9:CAP_1] ~g~The mafia has taxed The Malibu! [CAP1_B0:CAP_1] ~g~The mafia has taxed the film studio! [CAP1_C2:CAP_1] ~g~The Mafia has arrived at the Boatyard! [CAP1_C3:CAP_1] ~g~The Mafia has arrived at the Ice Cream Factory! [CAP1_C4:CAP_1] ~g~The Mafia has arrived at the Car Showroom! [CAP1_C5:CAP_1] ~g~The Mafia has arrived at the Taxi Firm! [CAP1_C9:CAP_1] ~g~The Mafia has arrived at The Malibu! [CAP1_C0:CAP_1] ~g~The Mafia has arrived at the film studio! [CAP1_D2:CAP_1] ~g~The Mafia is leaving the Boatyard! [CAP1_D3:CAP_1] ~g~The Mafia is leaving the Ice Cream Factory! [CAP1_D4:CAP_1] ~g~The Mafia is leaving the Car Showroom! [CAP1_D5:CAP_1] ~g~The Mafia is leaving the Taxi Firm! [CAP1_D9:CAP_1] ~g~The Mafia is leaving The Malibu! [CAP1_D0:CAP_1] ~g~The Mafia is leaving the film studio! [CAP1B10:CAP_1] You've capped the Collectors. More are on their way. {=================================== MISSION TABLE CARBUY ===================================} [CAR1_1:CARBUY] B.J. Smith. And you must be Mr. Vercetti. [CAR1_2:CARBUY] Would you like the tour? [CAR1_3:CARBUY] Might as well. [CAR1_4:CARBUY] Well, I'm very sad to be selling the dealership to y'all. [CAR1_5:CARBUY] This was my first investment after I turned pro. [CAR1_6:CARBUY] But now it's time for me to move on. [CAR1_7:CARBUY] You're leaving town? [CAR1_8:CARBUY] Not in too much of a hurry, I hope? [CAR1_9:CARBUY] No. I'm just coming out of retirement, and preparing for my future comeback. [CAR1_10:CARBUY] The business wasn't too strong, [CAR1_11:CARBUY] and my staff took it upon themselves to get a bit more [CAR1_12:CARBUY] creative with the generation of wealth. [CAR1_13:CARBUY] Obviously, I could wind down the business before I hand it over. [CAR1_14:CARBUY] Hell, I could burn the place down if I wanted to. [CAR1_15:CARBUY] This is prime development land. [CAR1_16:CARBUY] Oh, I wouldn't worry about any of that. [CAR1_17:CARBUY] This place seems perfect. [CAR1_18:CARBUY] Yeh it does, So I take it we have a deal? {=================================== MISSION TABLE CARPAR1 ===================================} [MM_1_A:CARPAR1] ~g~Collect ~y~5 checkpoints~r~ WITHOUT ~g~smashing any ~r~CONES! ~g~You may collect checkpoints in ~y~ANY ORDER. [CONE_1:CARPAR1] ~r~You smashed a cone!! [MM_1_C:CARPAR1] ~y~PASS THROUGH~g~ a checkpoint to start the timer. ~g~Each checkpoint will credit you with ~y~~1~ SECONDS~g~. {=================================== MISSION TABLE COPCAR ===================================} [KILLS:COPCAR] KILLS: [C_BREIF:COPCAR] ~g~Suspect last seen in the ~a~ area. [COPCART:COPCAR] ~g~You have ~1~ seconds to return to a police vehicle before the mission ends. [C_CANC:COPCAR] ~r~Vigilante mission cancelled! [C_TIME:COPCAR] ~r~Your time as a law enforcer is over! [C_COMP1:COPCAR] Vigilante mission level 12 complete: Your max Body Armor increased to 150 [CLEVEL:COPCAR] Vigilante Mission Level ~1~ [C_PASS:COPCAR] THREAT ELIMINATED: $ ~1~ {=================================== MISSION TABLE COUNT1 ===================================} [CM1_A:COUNT1] Mr. Vercetti? Hey. You bought the old print works? [CM1_B:COUNT1] Yeah, my old man used to work on these. [CM1_C:COUNT1] I was going to follow him in his trade, but...I lived a different life. [CM1_D:COUNT1] You planning on selling the old machinery, breaking it down? [CM1_E:COUNT1] I'm thinking we might print something - a newspaper, a magazine... [CM1_F:COUNT1] Oh, crap, sonny, low grade crap. I've always fancied printing money. It ain't too hard. [CM1_G:COUNT1] You know, I've been doing it on a small scale for years. [CM1_H:COUNT1] Really? [CM1_I:COUNT1] Sure. But we'd need some good quality plates. [CM1_J:COUNT1] Of course! There's a counterfeiting syndicate already operating in Florida. [CM1_K:COUNT1] A syndicate? [CM1_L:COUNT1] Yeah. Just rumors is all I've heard. [CM1_M:COUNT1] I know a man who's good with rumors... [CM1_N:COUNT1] I used to spend the evenings with him, cleaning the rollers... [CM1_2A:COUNT1] Look at the arse on that! [CM1_2B:COUNT1] Awright girl, it's your loss mate init! [CM1_2C:COUNT1] Awright me ol'china, how's it hangin'? [CM1_2D:COUNT1] What do you know about counterfeiting? [CM1_2E:COUNT1] Oh I'm fine Paul, how 'bout you? [CM1_2F:COUNT1] Come 'ere! [CM1_2G:COUNT1] Awright! Awright! Awright!! You're obviously a busy man. [CM1_2H:COUNT1] All I know about dodgy readys is the Triads supply the plates. [CM1_2I:COUNT1] They've got a shipping company down the docks, [CM1_2J:COUNT1] the boss man would know when the plates are coming in next! [CM1_2K:COUNT1] Thanks Paul. [CM1_2L:COUNT1] What's the matter with you, you maniac! [CM1_2M:COUNT1] Give me another drink, lively! [CM1_3:COUNT1] ~g~You've been spotted! [CM1_5:COUNT1] ~g~Go and meet Kent Paul at the Malibu Club! [CNT1_1:COUNT1] Who are you? Oooof! Aaiieee! Not the face! Not the face! [CM1_1:COUNT1] ~g~Go to the Chartered Libertine Lines boat at the docks. [CM1_2:COUNT1] ~g~The Shipping Officer will have the information that is required. [CNT1_2:COUNT1] Ok, I talk! I talk! [CM1_6:COUNT1] ~g~Get the information back to the Print Works! {=================================== MISSION TABLE COUNT2 ===================================} [CNT2_B1:COUNT2] Alright, the courier's moving the plates from the docks today. [CNT2_B2:COUNT2] I'm gonna go intercept them, grab the plates, lose any heat, and make my way back here. [CNT2_B3:COUNT2] Now. Depending how well this goes, [CNT2_B4:COUNT2] we may have five minutes to print the money before the counterfeit syndicate finds us, or we may have all year. [CNT2_B5:COUNT2] Either way, I want green rolling off the presses five minutes after I get back. Got it? [CNT2_B6:COUNT2] Don't you worry Tommy. We'll be ready. [CNT2_B7:COUNT2] Me an'the boys will be around in the neighborhood case you need any heat taken care of. [CNT2_B8:COUNT2] All right, everybody cool? All right. I'll catch you later... [CNT2_01:COUNT2] ~g~The counterfeit plates ~y~courier~g~ is arriving at the ~r~docks~g~ in a helicopter any second now. [CNT2_02:COUNT2] ~r~The plates courier has fled in the helicopter. [CNT2_03:COUNT2] ~r~The courier has arrived at his destination safely, you're too late! [CNT2_04:COUNT2] ~r~You destroyed the plates in the explosion! [CNT2_05:COUNT2] ~g~You have the counterfeit plates. Take them to the print works. [CNT2_06:COUNT2] ~g~The courier has died and dropped the plates, get to them before anyone else. [CNT2_07:COUNT2] ~g~One of the guards has picked up the plates, don't let 'em get away. [CNT2_08:COUNT2] ~g~The ~r~courier~g~ with the plates has arrived at the docks. [CNT2_4:COUNT2] Private business. You're not welcome! [CNT2_09:COUNT2] PRINT WORKS ASSET COMPLETED [CNT2_10:COUNT2] ~g~The print works will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. [CNT2_11:COUNT2] ~r~The plates are at the bottom of the sea! {=================================== MISSION TABLE CUBAN1 ===================================} [CUB1_A:CUBAN1] Si, men? [CUB1_B:CUBAN1] Hey, easy Papi, this man's for me. You, you the boy? [CUB1_C:CUBAN1] Oh yeh. You the boy. I think so, you know? [CUB1_D:CUBAN1] No. I don't think I do. [CUB1_E:CUBAN1] Oh yeah? You come here, tough guy. [CUB1_F:CUBAN1] You think you can take me on? [CUB1_G:CUBAN1] You think you can play stupid with me? [CUB1_H:CUBAN1] No, I think you're playing plenty stupid enough for both of us. [CUB1_I:CUBAN1] Hey, he call you dumb, son. [CUB1_J:CUBAN1] And I call him a little girl, Papi. [CUB1_K:CUBAN1] Look at him, all dressed up like that. [CUB1_L:CUBAN1] What is this, ladies night? [CUB1_M:CUBAN1] You some kind of tough guy, you dress like a woman? [CUB1_N:CUBAN1] You got on panties like a woman too, huh? [CUB1_O:CUBAN1] What you got against women? You prefer men, big boy? [CUB1_P:CUBAN1] I like women! I like all women! I love my mother, chico! [CUB1_Q:CUBAN1] Alright, alright, I'll take your word for it. Relax. [CUB1_R:CUBAN1] Can you drive, amigo? [CUB1_S:CUBAN1] Yeah... like a woman. [CUB1_T:CUBAN1] Very funny. I like you, big boy. Maybe you can help. [CUB1_U:CUBAN1] Maybe you can prove you a man. Huh? [CUB1_V:CUBAN1] Take out the boat. [CUB1_W:CUBAN1] Show me you got some big cojones, [CUB1_X:CUBAN1] and not some little bitty chiquita ones. [CUB1_02:CUBAN1] Ok man, treat her like a woman. [CUB1_03:CUBAN1] Not bad, you're a real man. [CUB1_04:CUBAN1] You got real big cojones, amigo. [CUB1_05:CUBAN1] Amigo, you a man, man. [CUB1_06:CUBAN1] Call yourself a man, man? [CUB1_07:CUBAN1] You a little scaredy kitten, baby boy, go cry to your mommy! [CUB1_09:CUBAN1] Man, you the man, man. I like you, man. I like you a lot. [CUB1_10:CUBAN1] Any time, man. 'cause you got cojones. And all my friends have big cojones. [CUB1_11:CUBAN1] ~r~You Killed Rico! [CUB1_12:CUBAN1] Go through the first checkpoint to begin the test. [CUB1_14:CUBAN1] Get back in the boat! [CUB1_15:CUBAN1] ~r~You are too slow, man. [CUB1_01:CUBAN1] Hey, I'm Rico. You the man with the big cojones? [CUB1_13:CUBAN1] ~g~You have three minutes to get round the course. [CUB1_08:CUBAN1] You a big waste of space. Walk like a man, talk like a man, but you drive like an idiot. {=================================== MISSION TABLE CUBAN2 ===================================} [CUB2_A:CUBAN2] Un cafecito, por favor, Alberto.. [CUB2_N:CUBAN2] No problema, Tommy. [CUB2_B:CUBAN2] Papi! Una grande problema! [CUB2_O:CUBAN2] Umberto my son, what happened? [CUB2_C:CUBAN2] The Haitians! I hate these Haitians! [CUB2_D:CUBAN2] They mess with me for the last time! [CUB2_E:CUBAN2] These Hai - these Haitians! We take 'em out! [CUB2_F:CUBAN2] Only we need some backup. [CUB2_H:CUBAN2] Amigo, you drive good! [CUB2_I:CUBAN2] For a woman. Right? [CUB2_J:CUBAN2] This is no time for joking! [CUB2_K:CUBAN2] Come on, drive for me again! [CUB2_L:CUBAN2] Take my boys over there, and then we'll take these Haitians down! [CUB2_01:CUBAN2] Not enough room, man, you need a bigger car. [CUB2_02:CUBAN2] We need reinforcements from the cafe! [CUB2_03:CUBAN2] ~g~Get a car and pick up the Cubans from outside Robina's Cafe. [CUB2_04:CUBAN2] ~g~Go and drop the Cubans off at the fight. [CUB2_05:CUBAN2] Take out that cowardly sniper! [CUB2_07:CUBAN2] They fight like girls! Take cover! [CUB2_09:CUBAN2] Sniper on the roof! [CUB2_11:CUBAN2] ~r~You fool, we needed that car. [CUB2_12:CUBAN2] Hey amigo! Good to see you could make it! [CUB2_13:CUBAN2] Stinking nest of Haitians, we gonna kill 'em all! [CUB2_14:CUBAN2] CHAAAAAARGE! [CUB2_15:CUBAN2] Now, my brothers, CHAAARRRGGEE!! [CUB2_16:CUBAN2] Tommy, we have proved our manful bravery! [CUB2_17:CUBAN2] Let us steal the van full of drugs and make good our escape! [CUB2_18:CUBAN2] ~g~Get a car and pick up the Cubans [CUB2_19:CUBAN2] We gonna fight like men! [CUB2_21:CUBAN2] Fight like men with huge cojones! [CUB2_22:CUBAN2] ~g~ Finish off the rest of the Haitians so the Cubans can move forward. [CUB2_23:CUBAN2] ~g~ Little Haiti will be swarming with Haitians trying to even the score with the Cubans. Watch your back. [CUB2_24:CUBAN2] ~g~Return to Robina's Cafe with the Van and park it round the back. [CUB2_25:CUBAN2] KILL ALL THE HAITIANS!! [CUB2_G:CUBAN2] I lost a few hermanos already out there. [CUB2_M:CUBAN2] They mess with me, they mess with the biggest boy in town! {=================================== MISSION TABLE CUBAN3 ===================================} [CUB3_B:CUBAN3] Poppa, don't serve this snake in the straw. [CUB3_C:CUBAN3] You're two-faced, Tommy! [CUB3_E:CUBAN3] The Haitians, man. They're laughing at me! [CUB3_G:CUBAN3] They're laughing at me, Tommy. At me! [CUB3_I:CUBAN3] Nobody does whatever they like, Umberto, they do what you let them do. [CUB3_J:CUBAN3] What? [CUB3_K:CUBAN3] You want somebody taken care of? [CUB3_L:CUBAN3] I can handle it, but it's gonna cost you. [CUB3_M:CUBAN3] I know we're brothers and all, but this is business. [CUB3_N:CUBAN3] Tommy. You a real man. Businessman, a gentleman. [CUB3_O:CUBAN3] These Haitians. They have a load of product coming in off shore, really good stuff. [CUB3_P:CUBAN3] We take it, and we finish them. [CUB3_Q:CUBAN3] You take it, and I look after you. Like my brother. Like my son. [CUB3_R:CUBAN3] I think I prefer the cash to being bounced on your knee, amigo. [CUB3_01:CUBAN3] Hey Rico. Nice boat, you ready? [CUB3_03:CUBAN3] ~g~Collect all the briefcases filled with the drugs and cash. [CUB3_04:CUBAN3] ~g~Get the drugs and cash back to Umberto. [CUB3_05:CUBAN3] Si Tommy. Now you be a good shot today, [CUB3_06:CUBAN3] My boat, she no good full of holes, ok? [CUB3_07:CUBAN3] ~g~ Go meet Rico. He'll drive you to the meet location. [CUB3_02:CUBAN3] ~g~KILL ALL THE HAITIANS ON THE BOATS!! [CUB3_08:CUBAN3] Uh oh.. Pack of Cubans. We under attack! [CUB3_A:CUBAN3] Alberto. Una cafe, senor. [CUB3_D:CUBAN3] You're either two-faced, or you're a wimp, baby boy! [CUB3_F:CUBAN3] Easy, easy. What's your problem? [CUB3_H:CUBAN3] Umberto Robina! They're doing whatever they like! {=================================== MISSION TABLE CUBAN4 ===================================} [CUB4_A:CUBAN4] Hey, ladies. You know what I'm gonna do? [CUB4_B:CUBAN4] I'm gonna kill me a Haitian. And then? [CUB4_C:CUBAN4] And then I'm going to make love like a man. [CUB4_D:CUBAN4] You know that, chica? Something like this. [CUB4_E:CUBAN4] Loser! [CUB4_F:CUBAN4] Prick. [CUB4_G:CUBAN4] Hey, baby, I wouldn't touch you with a ten foot pole! [CUB4_H:CUBAN4] Umberto Robina, he likes the ladies! Not some goat in a skirt! [CUB4_I:CUBAN4] Tommy!! Tommy, I love you, I love you! Let's go! [CUB4_J:CUBAN4] Go where? Can't I get a cup of coffee first? [CUB4_K:CUBAN4] No time for coffee! Besides, I just had one. [CUB4_L:CUBAN4] We gonna take out the Haitians. [CUB4_M:CUBAN4] Tommy, how do you take out a snake? [CUB4_N:CUBAN4] You bite him in the ass! Hahaha! [CUB4_O:CUBAN4] Whatever you say, Umberto. [CUB4_P:CUBAN4] Tommy, you go and get us a little Haitian car. [CUB4_Q:CUBAN4] When you get it, come back and pick up my boy. [CUB4_R:CUBAN4] Pepe, and take him out to the Haitians. [CUB4_S:CUBAN4] Then, you go around to the Haitians processing plant, and you use their solvent as an explosive. [CUB4_T:CUBAN4] Boom! Bye bye! [CUB4_U:CUBAN4] Umberto, what about you? [CUB4_V:CUBAN4] Uhh... I'm going to stay behind, and watch over the cafe with Poppa. [CUB4_W:CUBAN4] He not feeling so good. You know? [CUB4_02:CUBAN4] ~g~The bombs will be set with a 45 second timer. [CUB4_04:CUBAN4] ~r~You've alerted the base, there is no way we will get in now! [CUB4_07:CUBAN4] Oy - the solvent is round the back, amigo. [CUB4_08:CUBAN4] Hola, amigos. [CUB4_09:CUBAN4] Bueno. Haitian Putas. Muerte. [CUB4_10:CUBAN4] Vamos. [CUB4_11:CUBAN4] Vamos indeed. [CUB4_12:CUBAN4] Hey, we need a Haitian gang car! [CUB4_13:CUBAN4] Oye, let's go find our muchachos! [CUB4_14:CUBAN4] Follow my compadres. [CUB4_15:CUBAN4] Ok, in you go... [CUB4_16:CUBAN4] I'm going to plant the bomb, cover me! [CUB4_17:CUBAN4] RUN!! [CUB4_18:CUBAN4] Man, this a nice part of town... [CUB4_19:CUBAN4] This place is a dump, man. [CUB4_20:CUBAN4] I had a beautiful woman... lived around this neighborhood. [CUB4_21:CUBAN4] You know, they do nice pizzas here. [CUB4_22:CUBAN4] Whoah, man. You drive like a crazy bitch! [CUB4_23:CUBAN4] You lost, man? [CUB4_24:CUBAN4] You've left Pepe behind, go and get him. [CUB4_03:CUBAN4] ~g~Stay in the car until safely parked inside the compound. [CUB4_26:CUBAN4] ~g~Take Pepe, head North into Little Haiti and steal a Voodoo car. [CUB4_27:CUBAN4] ~g~Go and meet up with Rico and the other Cubans. [CUB4_28:CUBAN4] ~g~Join the other Cubans at the Haitian Drugs Factory. [CUB4_29:CUBAN4] ~g~Walk into each of the markers to plant a bomb at that location. [CUB4_30:CUBAN4] ~g~After all three bombs are planted, get clear of the factory before it blows. [CUB4_31:CUBAN4] ~g~Get clear of the factory!! [CUB4_32:CUBAN4] ~g~Park the car at the blip and get out. [CUB4_06:CUBAN4] ~r~You did not get far enough away from the base and we had to abort the explosion! {=================================== MISSION TABLE FINALE ===================================} [FIN1_01:FINALE] What's going on? [FIN1_02:FINALE] Tommy! Oh good, good. Listen, listen. Uh, listen, [FIN1_03:FINALE] I like fish. I love fish. [FIN1_04:FINALE] I love them as pets in bowls, or as food on a plate, [FIN1_05:FINALE] but as much as I love em, I don't want to sleep with them. [FIN1_06:FINALE] Okay, but right now your Italian brothers are coming from up there to fit me with some cement shoes, and I... [FIN1_07:FINALE] Shut up Ken. Sit down. [FIN1_08:FINALE] Lance, what the hell's going on? [FIN1_09:FINALE] It's your friends up north Tommy. They ain't too happy you capped their man. [FIN1_10:FINALE] They're coming down to see the business today. [FIN1_11:FINALE] They took longer than I thought... [FIN1_12:FINALE] Guys, we gotta make this final we gotta leave no doubt that this is my operation. Mine! [FIN1_13:FINALE] Ken, you get the first run of counterfeit cash and put three mil in briefcases. [FIN1_14:FINALE] Lance, you get the guys together... [FIN2_01:FINALE] Tommy! [FIN2_02:FINALE] What? No big hugs for your old buddy? [FIN2_03:FINALE] I've had fifteen years out of the loop, [FIN2_04:FINALE] I'm a bit rusty on family etiquette. [FIN2_05:FINALE] Always angry, eh Tommy. [FIN2_06:FINALE] Didn't I say your temper would get you into trouble, huh? [FIN2_07:FINALE] There's three mil in the cases... [FIN2_08:FINALE] How many was it? Ten? No, eleven men. [FIN2_09:FINALE] That's how you get to be called the Harwood Butcher! Heh-heh-heh! [FIN2_10:FINALE] You sent me to kill one man, ONE MAN. They knew I was coming Sonny... [FIN2_11:FINALE] Tommy, Tommy, watch your tone. [FIN2_12:FINALE] Anyone would think you blame me for that unfortunate set of circumstances. [FIN2_13:FINALE] Just take the money... [FIN2_14:FINALE] Get the damn cash. [FIN2_15:FINALE] You know, Tommy? I did what I could for you, I pulled strings, called in favors. [FIN2_16:FINALE] I was your friend, Tommy. I hoped you'd see sense, see what's good for business. [FIN2_17:FINALE] I trusted you, Tommy, and you disappointed me. [FIN2_18:FINALE] But at least someone in your chicken shit organization knows how to do business, [FIN2_19:FINALE] Isn't that right, Lance? [FIN2_20:FINALE] I'm sorry Tommy. This is Vice City. This is business. [FIN2_21:FINALE] You sold us out... [FIN2_22:FINALE] No. I sold YOU out, Tommy, I sold YOU out. [FIN2_23:FINALE] The real cash is upstairs in the safe. [FIN2_24:FINALE] Tommy, what was the big plan? [FIN2_25:FINALE] You think I'd just take the fake cash? [FIN2_26:FINALE] Save face and run away with my tail between my legs?! [FIN2_27:FINALE] No. [FIN2_28:FINALE] I just wanted to piss you off before I kill you. [FIN3_01:FINALE] Tommy? [FIN3_02:FINALE] Oh my god, Tommy! What happened? [FIN3_03:FINALE] What does it look like? [FIN3_04:FINALE] It looks like you ruined your suit! [FIN3_05:FINALE] and Tommy, that was a beautiful suit! Tommy, what on earth happened? [FIN3_06:FINALE] I had a disagreement with a business associate, you know how it is. [FIN3_07:FINALE] Tommy, I have a disagreement, I send them an angry letter. [FIN3_08:FINALE] Maybe I pee in their mailbox. I don't start World War III. [FIN3_09:FINALE] You know, maybe you should speak to my shrink... [FIN3_10:FINALE] That stupid prick, Lance... [FIN3_11:FINALE] Tommy. I never liked that guy, okay? [FIN3_12:FINALE] He's neurotic, he's insecure, he's self-centered - the guy's an asshole! [FIN3_13:FINALE] I'm glad you took him out! [FIN3_14:FINALE] I don't think we're gonna be getting any more heat from up north either... [FIN3_15:FINALE] ...'cause there ain't no 'up north', anymore. [FIN3_16:FINALE] It's all down south now. [FIN3_17:FINALE] Wait, does that mean what I think it means..? Tommy, baby! [FIN3_18:FINALE] What do you think it means? [FIN3_19:FINALE] That we're in charge... I mean, that you're in charge. Oh, Tommy! [FIN3_20:FINALE] You know, Ken. I think this could be the beginning of a beautiful business relationship.... [FIN3_21:FINALE] After all, you're a conniving, backstabbing, two-bit thief [FIN3_22:FINALE] and I'm a convicted psychotic killer and drug dealer. [FIN3_23:FINALE] I know. Ain't it just beautiful? [FIN_B1:FINALE] ~g~Go and kill ~y~Lance Vance~g~ the backstabber. [FIN_B2:FINALE] ~g~Kill ~p~Sonny~g~ and finish this once and for all. [FIN_B3:FINALE] ~g~The Mafia are trying to steal your money. Defend the safe. [FIN_B4:FINALE] ~g~You are close to death, get some ~w~health~g~ from downstairs. [FIN_B5:FINALE] ~g~The Mafia is stealing your money, defend the ~c~safe [FIN_B7:FINALE] ~r~The mafia has stolen all your money. [DEFSAFE:FINALE] ~g~Get back to the safe and defend it. {=================================== MISSION TABLE FIRETRK ===================================} [F_PASS1:FIRETRK] Fire extinguished! [F_FAIL2:FIRETRK] ~r~You're too late! [F_CANC:FIRETRK] ~r~Fire Fighter mission cancelled! [F_EXTIN:FIRETRK] FIRES: [F_START:FIRETRK] ~g~Burning vehicle reported in the ~a~ area. Go and extinguish the fire. [SIREN_1:FIRETRK] To turn on this vehicle's sirens tap the ~h~~k~~VEHICLE_HORN~ ~w~button. [SIREN_2:FIRETRK] To turn on this vehicle's sirens tap the ~h~~k~~VEHICLE_HORN~ ~w~button. [FIREPRO:FIRETRK] Fire Truck Mission level 12 complete. You are now completely fireproof!! [F_FAIL1:FIRETRK] Fire Fighter mission ended. [F_STAR1:FIRETRK] ~g~Burning vehicles reported in the ~a~ area. Go and extinguish the fire. [SPRAY_4:FIRETRK] { reVC update } Use the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to fire the water cannon. Aim using ~h~~k~~VEHICLE_TURRETLEFT~~w~ and ~h~~k~~VEHICLE_TURRETRIGHT~~w~. [SPRAY_1:FIRETRK] { reVC update } Use the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to fire the water cannon. Aim using ~h~~k~~VEHICLE_TURRETLEFT~~w~ and ~h~~k~~VEHICLE_TURRETRIGHT~~w~. {=================================== MISSION TABLE GENERA1 ===================================} [GEN1_A:GENERA1] Mr. Vercetti! [GEN1_B:GENERA1] Colonel. [GEN1_D:GENERA1] No - thanks. [GEN1_E:GENERA1] I'm ashamed to admit that one of the causes of our mutual problem appears to have been the loose tongue of a man I used to trust. [GEN1_F:GENERA1] I've been carrying Gonzalez for years, but now his incompetence reaches new heights! [GEN1_G:GENERA1] It is only right that you kill Gonzalez... [GEN1_H:GENERA1] Did he do it? It's the money that's important to me. [GEN1_J:GENERA1] He will be at his Penthouse, half drunk probably. Use this... [GEN1_06:GENERA1] Eh! He's got a blade! [GEN1_07:GENERA1] Go away from me, you cheap bastard! [GEN1_08:GENERA1] Oh sweet Jesus, I've wasted my life and my looks! [GEN1_10:GENERA1] I'm going to shut that big mouth of yours! [GEN1_11:GENERA1] Stop running you fat slimeball! [GEN1_12:GENERA1] Stand still and I'll make it quick! [GEN1_13:GENERA1] Quit your squealing, no one cares, fatso! [GEN1_C:GENERA1] Thank you for coming. Please sit. Lobster? [GEN1_05:GENERA1] ~g~Go and kill Gonzalez! [GEN1_09:GENERA1] I pay you double, Tommy, DOUBLE! [GEN1_18:GENERA1] ~r~Gonzalez has made it safely to the Police Station! [GEN1_19:GENERA1] ~g~The Vice City Police are on to you! [GEN1_20:GENERA1] ~g~Get into a vehicle. [GEN1_21:GENERA1] ~g~Get to the~h~ Pay 'N' Spray~g~ in~h~ Vice Point~g~. [GEN1_22:GENERA1] Drive your vehicle into the spray shop to lose your ~h~wanted level, ~h~repair and ~h~respray your vehicle. Cost - ~h~$100. This time it's free. [GEN1_01:GENERA1] When jogging, press and hold the~h~ ~k~~PED_FIREWEAPON~ ~w~button to prepare a melee attack. [GEN1_02:GENERA1] When jogging, press and hold the~h~ ~k~~PED_FIREWEAPON~ ~w~button to prepare a melee attack. [GEN1_03:GENERA1] When jogging, press and hold the~h~ ~k~~PED_FIREWEAPON~ ~w~button to prepare a melee attack. [GEN1_14:GENERA1] Release the~h~ ~k~~PED_FIREWEAPON~ ~w~button to make the attack. [GEN1_15:GENERA1] Release the~h~ ~k~~PED_FIREWEAPON~ ~w~button to make the attack. [GEN1_16:GENERA1] Release the~h~ ~k~~PED_FIREWEAPON~ ~w~button to make the attack. [GEN1_I:GENERA1] For this kindness I'll reward you, and then we will find your money together. [GEN1_23:GENERA1] ~g~Go back through the doors to return to the ground floor. {=================================== MISSION TABLE GENERA2 ===================================} [COL2_B:GENERA2] This looks delicious, huh? Tapir snout? [COL2_C:GENERA2] Uhhh... no, no. No, thanks. [COL2_D:GENERA2] Tommy, you are like a pampas breeze that has freed me from the stench of corruption, [COL2_E:GENERA2] although, I must appear to mourn his passing and carry on with business as usual. [COL2_F:GENERA2] This isn't getting me any closer to my money... [COL2_G:GENERA2] Tommy, my friend, you are not in Liberty now. Here we do things differently. [COL2_H:GENERA2] I will continue with my enquiries but in the meantime I have a valuable deal to close. [COL2_I:GENERA2] A favor for a friend, Cortez? [COL2_J:GENERA2] You're a good friend, Tommy. I knew you would not let me down. [COL2_K:GENERA2] I need you to meet a courier who has obtained some valuable technology for me... [COL2_1:GENERA2] Ze rain, she is tres wet zis time of the year... [COL2_2:GENERA2] What? [COL2_3:GENERA2] Ah, comment? [COL2_4:GENERA2] Look, Cortez sent me. Just give me the damn chips. [COL2_5:GENERA2] Oh...d'accord. [COL2_B1:GENERA2] ~g~Meet the courier at the mall. [COL2_B2:GENERA2] ~g~The courier is fleeing with the guidance chips! Don't let him get away! [COL2_B3:GENERA2] ~g~Take the guidance chips back to the Colonel. [COL2_F1:GENERA2] ~r~You killed the contact! [COL2_F2:GENERA2] ~g~The courier is dead. Grab the guidance chips. [COL2_6A:GENERA2] Freeze, imperialist American pig! Zat iz propertay of ze government Francais. 'And eet over! [BLIPHLP:GENERA2] The blip on the radar is a triangle pointing up, this shows that the target is higher than the player. [COL2_F3:GENERA2] ~r~The guidance chips are at the bottom of the sea. [COL2_F4:GENERA2] ~r~The courier has escaped! You failed to get the guidance chips. [COL2_A:GENERA2] Tommy! Come, join me. {=================================== MISSION TABLE GENERA3 ===================================} [GEN3_A:GENERA3] Thomas, I appreciate your coming. [GEN3_B:GENERA3] Forgive me for getting straight to business. [GEN3_C:GENERA3] Diaz has asked me to oversee a minor business transaction. [GEN3_D:GENERA3] Let's hope it goes better than last time, huh? [GEN3_E:GENERA3] Which is why I thought of you, my friend. [GEN3_F:GENERA3] I've dropped some protection at the multistory carpark. [GEN3_G:GENERA3] Pick it up - then go and watch over Diaz's men at the drop off. [GEN3_H:GENERA3] Gracias, amigo. [GEN3_1:GENERA3] Hogging all the action, I see... [GEN3_2:GENERA3] Look, you wanna do something other than just shadowing me everywhere? Why don't you come along and show me if you're any use. [GEN3_3:GENERA3] I might just do that. The name's Lance, by the way. [GEN3_5:GENERA3] You must be Cortez's new gun. [GEN3_6:GENERA3] Until more gainful opportunities arise. [GEN3_7:GENERA3] They'll be here any minute - we both better get a good vantage point... [GEN3_8:GENERA3] OK! I'll take the balcony, you get the roof across the yard. [GEN3_9:GENERA3] I live! Dickheads! And it's all down to you! What is your name? [GEN3_10:GENERA3] Tommy. [GEN3_11:GENERA3] I see you soon, amigo, I think! [GEN3_12:GENERA3] Shit. Where's that guy Lance? [GEN3_14:GENERA3] Tommy! I need some help here! [GEN3_15:GENERA3] Don't worry, I got you covered! [GEN3_16:GENERA3] Diaz's men are getting cut down! [GEN3_19:GENERA3] ~g~Haitians! They're busting the deal! Protect Diaz! [GEN3_20:GENERA3] ~g~The Colonel has arranged some firepower for you at the multistory carpark. [GEN3_22:GENERA3] Diaz's Health: [GEN3_23:GENERA3] ~g~You've left Lance behind! Go and get him! [GEN3_25:GENERA3] ~r~Lance died! [GEN3_28:GENERA3] ~g~Take the briefcase back to Diaz. [GEN3_29:GENERA3] ~g~Collect the briefcase and take it back to Diaz. [GEN3_30:GENERA3] ~r~He got away with the money! Diaz will have your balls for this! [GEN3_33:GENERA3] ~r~Check your fire!! You're supposed to be watching over Diaz and his men, not shooting them! [GEN3_34:GENERA3] ~r~There ain't gonna be a deal if you shoot the Cubans! [GEN3_35:GENERA3] ~g~He's stolen Diaz's money! [GEN3_36:GENERA3] ~g~Grab the bike, chase him down and get Diaz's money back! [GEN3_37:GENERA3] ~g~Here come the Cubans. Watch over the deal making sure Diaz and Lance are safe. [GEN3_38:GENERA3] ~r~Diaz died! You failed to protect him! [GEN3_39:GENERA3] ~g~Get to your vantage point up the stairs. [GEN3_44:GENERA3] ~g~Go with Lance to the drop off and watch over Diaz. [GEN3_45:GENERA3] They'll be here any minute, we both better get a good vantage point. [GEN3_40:GENERA3] { reVC update } To ~h~shoot straight ahead ~w~on a ~h~motorbike ~w~press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. [GEN3_41:GENERA3] { reVC update } To ~h~shoot straight ahead ~w~on a ~h~motorbike ~w~press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button. [GEN3_46:GENERA3] Sheeit! [GEN3_47:GENERA3] Tommy! [GEN3_48:GENERA3] Damn! [GEN3_49:GENERA3] lance's health: [GEN3_50:GENERA3] ~r~You lost Diaz's money! Next time try not to reduce the money to ashes! [GEN3_51:GENERA3] More damned Haitians in a shitty van! [GEN3_54:GENERA3] Don't just stand there, you pricks, chase that Haitian dickhead down! [GEN3_55:GENERA3] Tommy! I'll stay here and watch over Diaz! [GEN3_18:GENERA3] ~g~Here come the Cubans, keep close to Diaz. Watch over the deal making Diaz and Lance are safe. [GEN3_56:GENERA3] ~r~Diaz was ambushed and died! Next time keep him in your sights! [GEN3_57:GENERA3] The Kruger is an assault rifle, which allows you to manually aim in 1st person. [GEN3_58:GENERA3] Press and hold the~h~ ~k~~PED_LOCK_TARGET~~w~ button to ~h~aim~w~ with an assault rifle. [GEN3_59:GENERA3] Press and hold the~h~ ~k~~PED_LOCK_TARGET~~w~ button to ~h~aim~w~ with an assault rifle. [GEN3_60:GENERA3] Press the ~h~~k~~PED_FIREWEAPON~~w~ button to ~h~fire~w~ an assault rifle. [GEN3_61:GENERA3] Press the ~h~~k~~PED_FIREWEAPON~~w~ button to ~h~fire~w~ an assault rifle. [GEN3_62:GENERA3] Press the ~h~~k~~PED_FIREWEAPON~~w~ button to ~h~fire~w~ an assault rifle. [GEN3_63:GENERA3] As well as performing drive-by's,~h~ motorbikes ~w~allow you to ~h~shoot forwards~w~. [GEN3_64:GENERA3] { reVC update } To shoot forwards while on a bike press the ~h~~k~~VEHICLE_FIREWEAPON~~w~ button. [GEN3_65:GENERA3] { reVC update } To shoot forwards while on a bike press the ~h~~k~~VEHICLE_FIREWEAPON~~w~ button. [GEN3_66:GENERA3] { reVC update } To shoot forwards while on a bike press the ~h~~k~~VEHICLE_FIREWEAPON~~w~ button. [GEN3_67:GENERA3] You must have a sub machine gun to shoot forwards on a motorbike. [GEN3_53:GENERA3] MY MONEY! [GEN3_52:GENERA3] These Haitians think they can take RICARDO DIAZ!?! {=================================== MISSION TABLE GENERA4 ===================================} [DETON:GENERA4] DETONATION: [COL4_3:GENERA4] CONVOY HALT! [COL4_6:GENERA4] WE'RE TAKING ENEMY FIRE! [COL4_7:GENERA4] Civilian, move away from the tank! [COL4_8:GENERA4] I SAID, move away, IMMEDIATELY! [COL4_9:GENERA4] DEFENSIVE POSITIONS! [COL4_11:GENERA4] Get that civilian out of our way soldier! - Sir, Yes Sir! [COL4_12:GENERA4] Civilian in the TANK! STOP HIM! [COL4_13:GENERA4] This is a military convoy, do not obstruct our route. [COL4_14:GENERA4] Drop him soldier. [COL4_15:GENERA4] Get that civilian vehicle out of our way! - Sir! Moving vehicle Sir! [COL4_17:GENERA4] Ok, PLATOON MOVE IT OUT! [COL4_18:GENERA4] Someone's on the tank Sir! [COL4_19:GENERA4] Go get some doughnuts, soldier! - Sir, Yes Sir! [COL4_20:GENERA4] Target acquired, Sir [COL4_21:GENERA4] SNIPER! [COL4_22:GENERA4] I'm getting out of here. [COL4_23:GENERA4] Objective completed! Platoon dismissed! - Lets go eat some doughnuts. [COL4_24:GENERA4] Security protocol Delta India Echo triggered! Vehicle self destruct initiated! [COL4_26:GENERA4] Prepare to die Communist scum! [COL4_B2:GENERA4] ~r~The tank arrived at its destination safely! [COL4_B5:GENERA4] ~r~The tank has been destroyed! [COL4_01:GENERA4] Diaz was pleased, and would like to meet you again. [COL4_02:GENERA4] Is that a good thing? [COL4_03:GENERA4] Of course! Although I'm starting to think that Diaz was responsible for our unfortunate loss... [COL4_04:GENERA4] What makes you say that? [COL4_05:GENERA4] One does not wave accusations at a man like Diaz - I'm merely thinking out loud... [COL4_06:GENERA4] No matter. I have a proposal that you could profit from... [COL4_07:GENERA4] I don't have time to run more errands, Cortez. [COL4_08:GENERA4] I would have thought a man with such dangerous debts would be hungry for opportunities. Please, Tommy, at least hear me out. [COL4_09:GENERA4] Go on... [COL410:GENERA4] I have a buyer for a piece of military hardware that is being taken through town. Pick it up for me... [COL411:GENERA4] and once you get it, I want you to call me immediately, then... [COL4_B4:GENERA4] ~g~The tank is locked. Find a way to lure out the occupants. [COL4_1:GENERA4] What's up with the Gunner? - Don't know Sir! [COL4_4:GENERA4] Get topside soldier. - Sir, Yes Sir! [COL4_B1:GENERA4] ~g~Go and acquire the piece of military hardware that is being taken through town. [COL4_B3:GENERA4] ~g~Drop the tank off in the Colonels lockup before it self destructs. [COL4_B6:GENERA4] ~g~Find a way to steal the tank! [COL4_B7:GENERA4] ~g~Drive the tank into the garage. [COL4_B8:GENERA4] ~g~Get out of the tank and walk out of the garage. {=================================== MISSION TABLE GENERA5 ===================================} [COL5A_1:GENERA5] Circumstances force a hasty departure, amigo. [COL5A_2:GENERA5] What's the problem? [COL5A_3:GENERA5] Ehh, the French want their missile technology back and after that last incident, [COL5A_4:GENERA5] I feel it is time to find safer harbors. [COL5A_5:GENERA5] Wouldn't it be safer to fly? [COL5A_6:GENERA5] I'd be dead before I reached check-in. Besides, I need to get my merchandise out of the country. [COL5A_7:GENERA5] Need another gun? [COL5A_8:GENERA5] You, my friend, are worth ten guns... [COL5B_1:GENERA5] Thomas, you have protected and served me well. [COL5B_2:GENERA5] But now you must leave us before we reach the open seas. [COL5B_4:GENERA5] Thank you, Colonel. [COL5B_5:GENERA5] One more request. While I'm away, could you keep an eye on Mercedes for me? [COL5B_6:GENERA5] I think she could look after herself, but sure, I'll keep an eye out. [COL5B_7:GENERA5] Gracias, amigo. Hasta luego. [COL5B_8:GENERA5] Adios, amigo. [COL5_7:GENERA5] Stop shooting at me! [COL5_9:GENERA5] Tommy, stop them shooting at me! [COL5_10:GENERA5] I have diplomatic immunity! [COL5_11:GENERA5] Don't shoot, I am a Colonel! [COL5_12:GENERA5] Thomas, kill them, my country will love you. [COL5_13:GENERA5] Tommy, we are being overrun by the French! [COL5_14:GENERA5] Tommy, everywhere I look, there are French men, I hate it! [COL5_15:GENERA5] Tommy, how are you? [COL5_16:GENERA5] This is for Piaf and Gainesbourg and your stupid french bread! [COL5_1:GENERA5] Port side! Port side! [COL5_2:GENERA5] They're attacking from starboard! [COL5_3:GENERA5] The bridge up ahead! [COL5_4:GENERA5] They've got a helicopter! [COL5_B1:GENERA5] ~g~Defend the Colonel and his yacht at all costs. [COL5_B2:GENERA5] ~g~Get up front and clear the route for the Colonel's yacht. [COL5_B3:GENERA5] ~r~The Colonel is dead! [COL5_B4:GENERA5] ~g~Shoot the attacking helicopter out of the sky. [COL5B_3:GENERA5] I will lower my personal launch. Keep it, my friend, a token of my gratitude. [COL5_B5:GENERA5] ~g~Shoot down the helicopters, do not endanger the yacht. [COL5_B6:GENERA5] ~g~You have run out of ammo, get more from the stairs on the top deck. [COL5_B7:GENERA5] ~g~You are running low on health, get more from the stairs on the top deck. {=================================== MISSION TABLE HAIT1 ===================================} [HAM1_A:HAIT1] Hello? Hello? [HAM1_C:HAIT1] You must be the big bad man me grandaddy been chattin' 'bout. [HAM1_D:HAIT1] Tells me tings about you, you know, when he visits, [HAM1_E:HAIT1] and about the others who wait for you. [HAM1_F:HAIT1] Now, we all dead for long time, but you, [HAM1_G:HAIT1] I wouldn't want to be in your shoes, ha ha ha ha ha! [HAM1_H:HAIT1] I got a message to come here. [HAM1_I:HAIT1] Can you hear dem? [HAM1_J:HAIT1] Dem callin' your name, boy, must want you pretty bad, don't ya tink? [HAM1_K:HAIT1] Now you do old Auntie Poulet a turn, huh, maybe she help you. [HAM1_L:HAIT1] Maybe she can give you a little juju after all of dis. [HAM1_M:HAIT1] Give you some magic to give the law man the stink eye, hmmmmm? [HAM1_N:HAIT1] Look, this is all very, um... give me what? [HAM1_O:HAIT1] I,I, I think I've got the wrong address... [HAM1_P:HAIT1] Do me these tings, Tommy...... [HAM1_Q:HAIT1] The Cubans, nasty proud foofoos, mmm, [HAM1_R:HAIT1] been making my lovely Haitian boys shake de heads. [HAM1_S:HAIT1] Now they told the policeman where me been stashing my powders. [HAM1_T:HAIT1] Dey tink it drugs, them stupid. [HAM1_U:HAIT1] Now be a good boy Tommy and go and get the powders for Auntie Poulet. [HAM1_V:HAIT1] Yeah, yeah, sure, sure. [HAM1_1:HAIT1] ~g~The cops are closing in on our stashes. BE quick, and beat dem to it! [HAM1_2:HAIT1] ~r~The cops got to the stash first! [HAM1_3:HAIT1] ~g~Get this stuff back to the hideout! [HAM1_4:HAIT1] ~g~Good! Now get the next one! [HAM1_6:HAIT1] ~r~The Stash was destroyed, you idiot! [HAM1_7:HAIT1] ~g~The cops have got our stash! Retrieve it before they get away! [HAM1_8:HAIT1] ~g~The cops are on the way to pick up the stash, get a move on! [HAT_1A:HAIT1] ~g~Don't move a muscle, chump! [HAM1_B:HAIT1] Come in, my dear, and rest your soul. {=================================== MISSION TABLE HAIT2 ===================================} [HAT2_B1:HAIT2] ~g~Get to the van that contains the flying bombs. [HAT2_B2:HAIT2] Kill the Cubans... [HAT2_B4:HAIT2] ...and destroy their boats! [HAT2_B5:HAIT2] ~g~The Cubans are making a run for it. Don't let them get away! [HAT2_B6:HAIT2] ~r~The RC plane is getting too far out of range! [HAT2_B7:HAIT2] ~g~One of the Cubans in escaping in a car. Don't leave any witnesses! [HAT2_B8:HAIT2] ~r~You have no RC planes left! [HAT2_B9:HAIT2] RC Planes: [HAT2_1:HAIT2] Oh, sorry, I - I must have the wrong address... [HAT2_2:HAIT2] Well, you might as well come in and rest your soles and have some tea. [HAT2_3:HAIT2] Do you have something there for me, Tommy? [HAT2_4:HAIT2] Yeah... [HAT2_5:HAIT2] This place feels familiar to me, uh - it's - a smell from childhood - a deja vu... [HAT2_6:HAIT2] Now Tommy, I'm going to whisper a lickle errand for you. Hear me well, aye? [HAT2_7:HAIT2] You look like someone I, I... [HAT2_8:HAIT2] The Cubans have fast boats they use to cross the seas with drugs. [HAT2_9:HAIT2] It is their livelihood. [HAT2_10:HAIT2] Me nephew bin making lickle flying bombs to take dem out. [HAT2_11:HAIT2] Blow de boats to coffin wood. [HAT2_12:HAIT2] Thanks for the tea. [HAT2_B3:HAIT2] { reVC update } Press the ~h~~k~~VEHICLE_FIREWEAPON~ ~w~button to drop a bomb. Press the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button to cancel. {=================================== MISSION TABLE HAIT3 ===================================} [HAM3_A:HAIT3] Hello? Hello - uh..I'm looking for somebody around here... [HAM3_B:HAIT3] You looking hungry, Tommy. [HAM3_C:HAIT3] Do I know you? [HAM3_D:HAIT3] Hush now. [HAM3_E:HAIT3] One more ting an I can let you go, Tommy. [HAM3_F:HAIT3] My boys gone war wit dem Cuban boys. [HAM3_G:HAIT3] But no guns. [HAM3_H:HAIT3] Hmm, but de Cubans have a surprise comin'. [HAM3_I:HAIT3] While they fight in de streets, you take this rifle and kill dem in de hubbub. [HAM3_J:HAIT3] No one sees you, no one hear you. [HAM3_K:HAIT3] Now, Tommy, you do this for me, and you no longer tied to my apron strings. [HAM3_1:HAIT3] ~g~We must win this battle. If all the Haitians die we lose. [HAM3_3:HAIT3] ~g~I expect the Cubans to cheat so be on your guard. [HAM3_4:HAIT3] ~r~You have been spotted! The mission is a failure! [HAM3_5:HAIT3] ~g~You must kill the Cubans from a distance. You must not be seen. [HAM3_8:HAIT3] ~g~Haitians are dying! Improve your aim. [HAM3_7:HAIT3] ~g~Look Out! The Cubans have brought reinforcements. Kill them all!! [HAM3_2:HAIT3] ~r~The Haitians have died! [HAM3_L:HAIT3] Kay auntie.. {=================================== MISSION TABLE HOTEL ===================================} [INTB_A:HOTEL] Tommy! Tommy, it's been too long. [INTB_B:HOTEL] Hello Sonny. [INTB_C:HOTEL] I know, I know. You're just overwhelmed with emotion. [INTB_D:HOTEL] Fifteen years - seems like only yesterday. [INTB_E:HOTEL] I guess that's a perspective thing. [INTB_F:HOTEL] Hey, doing time for the family is no piece of cake, [INTB_G:HOTEL] but the family looks after its own, ok? [INTB_H:HOTEL] So, how'd the deal go down - you sitting on some white gold? [INTB_I:HOTEL] Look Sonny, we were set up. The deal was an ambush. Harry and Lee are dead. [INTB_J:HOTEL] You better be kidding me Tommy. Tell me you still got the money. [INTB_K:HOTEL] ...no Sonny...I don't have the money. [INTB_L:HOTEL] That was my money, Tommy, MY MONEY! [INTB_M:HOTEL] You better not be screwing me Tommy because you know I'm not a man to be screwed with! [INTB_N:HOTEL] Wait Sonny. [INTB_O:HOTEL] You have my personal assurance that I'm going to get your money back and the drugs. [INTB_P:HOTEL] And I'm gonna mail you the dicks of those responsible. [INTB_Q:HOTEL] Hey, I already know that. You're not a fool Tommy, but I warn you, neither am I. [INTB_R:HOTEL] If it was anybody else you'd be DEAD already. [INTB_S:HOTEL] But because it's you, because we got history, I'm gonna let you handle this. [INTB_T:HOTEL] Look, Sonny, you got my word. [INTB_U:HOTEL] I'll be in touch. {=================================== MISSION TABLE ICECRE1 ===================================} [ICC1_1:ICECRE1] ~g~Use your Ice Cream van distribute drugs around Vice City. [ICC1_3:ICECRE1] ~g~You receive money for each transaction you make, but the more transactions you make the more police attention you get. [ICC1_4:ICECRE1] ~g~There aren't any customers in this area try another one. [ICC1_5:ICECRE1] Deals done: [ICC1_6:ICECRE1] ~g~Use the Mr. Whoopee van to distribute Cherry Poppers product around Vice City. [ICC1_7:ICECRE1] ~g~You receive money for each transaction you make, but the more transactions you make the more police attention you get. [ICC1_9:ICECRE1] ~g~Local gangs will not appreciate you doing business on their turf so expect hostility if you do so. [ICC1_10:ICECRE1] ~g~You made ~1~ deals! [ICC1_11:ICECRE1] ~g~You made ~1~ deal! [ICC1_12:ICECRE1] PROPERTY ACQUIRED! [ICC1_13:ICECRE1] ~r~You didn't make any deals! [ICC1_14:ICECRE1] ICECREAM ASSET COMPLETED [ICC1_15:ICECRE1] ~g~The icecream factory will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. [ICC1_2:ICECRE1] ~g~Park your van and press the ~h~~k~~VEHICLE_HORN~~w~ to play your ice cream jingle to notify customers that your ready for business. [ICC1_16:ICECRE1] ~g~Use your Mr. Whoopee van to distribute Cherry Poppers product around Vice City. [ICC1_8:ICECRE1] ~g~To make a transaction, ~h~park your van ~g~and press the ~h~~k~~VEHICLE_HORN~ ~g~button to play the ice cream jingle to attract customers. [ICE_AT1:ICECRE1] ICECREAM FACTORY ASSET COMPLETED [ICE_AT2:ICECRE1] ~g~The Cherry Popper factory will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. [ICC1_17:ICECRE1] Distribution mission over [ICC1_18:ICECRE1] Total ice cream sales: $~1~ [ICC1_19:ICECRE1] Total deals done: ~1~ {=================================== MISSION TABLE ICECUT ===================================} [ICC1_A:ICECUT] Who are you? [ICC1_B:ICECUT] Your new owner. [ICC1_C:ICECUT] Were you now, or at any time, a child? [ICC1_D:ICECUT] What are you talking about? [ICC1_E:ICECUT] Were you a child? [ICC1_F:ICECUT] Yes! Calm down! What's wrong with you? [ICC1_G:ICECUT] I knew it. A child. [ICC1_H:ICECUT] A dirty, stinking, sniveling, snotting, vile, puking, crying little baby! [ICC1_K:ICECUT] Ow! Calm down. [ICC1_L:ICECUT] I HATE babies, and I hate children. [ICC1_N:ICECUT] Enough already! [ICC1_P:ICECUT] You make soft ice cream, okay? It's purely for kids. [ICC1_Q:ICECUT] What kind of psycho are you? [ICC1_R:ICECUT] Just so I understand this, why make children happy if you hate them? [ICC1_S:ICECUT] Oh, you stupid, sniveling, snotty- [ICC1_T:ICECUT] Shut up! [ICC1_U:ICECUT] - Brat! [ICC1_V:ICECUT] The ice cream is a front. [ICC1_W:ICECUT] We distribute other, non-dairy products. [ICC1_X:ICECUT] And if I see a kid, I put him to good use. [ICC1_Y:ICECUT] Don't I, kiddies? Yes - yes, I do. Mummy doesn't love you. [ICC1_Z:ICECUT] She HATES you! [ICC1_ZA:ICECUT] PROPERTY ACQUIRED! [ICC1_M:ICECUT] They're dirty, sniveling, snotting, vile, puking little.. [ICC1_I:ICECUT] A baby.. an awful, horrible, disgusting little boo hoo. [ICC1_J:ICECUT] Mommy doesn't love you. You little shit! {=================================== MISSION TABLE INTRO ===================================} [INT1_A:INTRO] Tommy Vercetti...Huh! shit. [INT1_B:INTRO] Didn't think they'd ever let him out. [INT1_C:INTRO] He kept his head down, helps people forget. [INT1_D:INTRO] People will remember soon enough. [INT1_E:INTRO] When they see him walking down the streets of their neighborhoods. [INT1_F:INTRO] It will be bad for business. [INT1_G:INTRO] Well, what are we gonna do, Sonny? [INT1_H:INTRO] We treat him like an old friend and keep him busy out of town. OK? [INT1_I:INTRO] We been talking about expanding down South, right? [INT1_J:INTRO] Vice City is twenty-four carat gold these days. [INT1_K:INTRO] The Colombians, the Mexicans, hell, [INT1_L:INTRO] even those Cuban refugees are cutting themselves a piece of some nice action. [INT1_M:INTRO] But it's all drugs, Sonny, [INT1_N:INTRO] None of the families will touch that shit! [INT1_O:INTRO] Times are changing. [INT1_P:INTRO] The families can't keep their backs turned while our enemies reap the rewards. [INT1_Q:INTRO] So, we send someone down to do the dirty work for us... [INT1_R:INTRO] and cut ourselves a nice quiet slice. OK? [INT1_S:INTRO] Who's our contact down there? [INT1_T:INTRO] Ken Rosenberg, schmuck of a lawyer. [INT1_U:INTRO] How's he gonna hold Vercetti's leash? [INT1_V:INTRO] We don't need him to. [INT1_W:INTRO] We just set him loose in Vice City, [INT1_X:INTRO] we give him a little cash to get started. OK? [INT1_Y:INTRO] Give it a few months. [INT1_Z:INTRO] Then we go down, [INT1_A1:INTRO] pay him a little visit, right? [INT1_A2:INTRO] see how he's doing. [INT2_A:INTRO] Hey, hey, guys! It's, uh, Ken Rosenberg here! Hey! Heh, heh, hey, great, hey! [INT2_B:INTRO] Well, uh, I'm gonna drive you guys to the meet, okay? [INT2_C:INTRO] Now, I've talked to the suppliers and they are very, huh-ha, [INT2_D:INTRO] keen to start a business relationship, so, uh, [INT2_E:INTRO] if all goes well, we should, uh, [INT2_F:INTRO] be doing very nicely for ourselves, which is, y'know... [INT2_G:INTRO] good.. [INT2_H:INTRO] Okay, so. They're brothers, okay. [INT2_I:INTRO] One operates the uh, the business, [INT2_J:INTRO] and the other one does the flying. [INT2_K:INTRO] Now they operate out of Mexico, [INT2_M:INTRO] They own a farm in Panama. [INT2_N:INTRO] Okay, all right, listen - [INT2_O:INTRO] you guys, when we get there should I stay in the car, [INT2_P:INTRO] or do you guys want me to come in with you guys? [INT2_Q:INTRO] No. Stay in the car. [INT2_R:INTRO] You know what, I thought about it, [INT2_S:INTRO] I'm gonna watch the car. [INT3_A:INTRO] Ok, that's them in the chopper. [INT3_B:INTRO] All right, here's the deal. [INT3_C:INTRO] They want a straight exchange on open ground. [INT3_D:INTRO] All right? Ok. Stay tight, let's go. [INT3_E:INTRO] All right. Take it easy, now. [INT3_F:INTRO] I'm right here. The cars running, baby! [INT3_G:INTRO] Got it? [INT3_H:INTRO] 100% pure grade-A Colombian, my friend. [INT3_I:INTRO] The greens? [INT3_J:INTRO] Tens and twenties...used. [INT3_L:INTRO] Go on, get out of here! Drive! [INT4_A:INTRO] Screwed! We're screwed! [INT4_B:INTRO] This is soo typical, [INT4_C:INTRO] I poke my head out of the gutter for one freakin' second, [INT4_D:INTRO] and fate shovels shit in my face! [INT4_E:INTRO] Well, screw you! [INT4_F:INTRO] Shut your face and quit complaining! You're alive, aren't ya'? [INT4_G:INTRO] Drop me right up here. [INT4_H:INTRO] Go dump the car, then go get some sleep. [INT4_I:INTRO] I'll drop by your office tomorrow and we can start sorting this mess out. [INT4_J:INTRO] OK, that's a good idea, I'll get some sleep. [INT4_K:INTRO] What are you gonna do? [INT4_L:INTRO] Make my way back to my hotel, [INT4_M:INTRO] clear my head, and figure this crap out. [INT4_N:INTRO] OK. [INTRO1:INTRO] I poke my head out of the gutter for one freakin' second and fate shovels shit in my face! [INTRO2:INTRO] Go get some sleep. [INTRO3:INTRO] What are you gonna do? [INTRO4:INTRO] I'll drop by your office tomorrow and we can start sorting this mess out. [INT3_K:INTRO] I think we have a deal, my friend. HA HA! [INT3_M:INTRO] Let me see it. [INT2_L:INTRO] no, no, no, wait... [INT3_N:INTRO] Oh Shit! {=================================== MISSION TABLE KENT1 ===================================} [KPM1_A:KENT1] Awright mush, I'm gonna save your Vera, mate. [KPM1_B:KENT1] What the hell are you talking about? [KPM1_C:KENT1] You know that wanker Diaz, the Bugle Master. [KPM1_D:KENT1] He's got your boy, Lance. Word is your mate tried to jump him... [KPM1_E:KENT1] didn't jump high enough if you know what I mean. [KPM1_F:KENT1] Where did he take him? In plain English? [KPM1_G:KENT1] Keep your barnet on! They got him across town at the junkyard. [KPM1_H:KENT1] Bloody hell....you nutter! [KPM1_2:KENT1] ~r~You were supposed to get Lance out alive! [KPM1_3:KENT1] LANCE'S HEALTH: [RESC_1:KENT1] You ok to use a gun? [RESC_2:KENT1] Sure...I guess...nice to see you, too. [RESC_3:KENT1] Let's get out of here. [RESC_4:KENT1] There goes my careful planning blown to shit, thanks to you. You screwed up real good, Lance! [RESC_5:KENT1] He killed my brother. What do you expect me to do, mow his lawns? [RESC_6:KENT1] We're gonna have to take out that prick Diaz before he takes us out. [RESC_7:KENT1] Get patched up and meet me on the bridge to Star Island, ok? [RESC_8:KENT1] Ok, I got you. [KPM1_1:KENT1] ~g~Lance is being held at the junk yard, Go and rescue him! [KPM1_4:KENT1] ~g~Get Lance to the hospital! [M_PASSN:KENT1] MISSION PASSED! [KPM1_5:KENT1] ~g~Diaz's guys are after you! Get Lance to the hospital. {=================================== MISSION TABLE KICKSTT ===================================} [KICK1_2:KICKSTT] ~r~You did not get back to the bike quickly enough! [KICK1_7:KICKSTT] ~r~You have wrecked the bike! [KICK1_8:KICKSTT] ~g~Get on the bike! [KICK1_T:KICKSTT] TIME TAKEN: [KICKTM:KICKSTT] ~b~EVENT TIME: ~1~:~1~ [KICKTM2:KICKSTT] ~b~EVENT TIME: ~1~:0~1~ [GETBIKE:KICKSTT] ~g~You have ~1~ seconds to return to a dirtbike before the mission ends. [KICK1_1:KICKSTT] ~g~Complete the course as quickly as possible. [KICK1_6:KICKSTT] ~g~Well done! [KICK_10:KICKSTT] ~g~Use the Sanchez to complete the course by passing through all of the checkpoints. [KICK_12:KICKSTT] ~r~You bottled it! [KICK_13:KICKSTT] ~r~You have taken too long! [KICK_11:KICKSTT] ~g~To leave the mission stand in the ~q~pink marker~g~ on foot. {=================================== MISSION TABLE LAWYER1 ===================================} [LAW1_A:LAWYER1] Go get some sleep, he says - [LAW1_B:LAWYER1] - I have been sitting in this chair all night with the lights off drinking coffee! [LAW1_C:LAWYER1] This is a disaster. We are so screwed, man! [LAW1_D:LAWYER1] These gorillas, listen to me, are gonna come down here and rip my head off. It's ridiculous! [LAW1_E:LAWYER1] I did NOT go to law school for this! Ok, now what the hell are we gonna do? [LAW1_F:LAWYER1] Shut up, sit down, relax. I'll tell you what we're gonna do. [LAW1_G:LAWYER1] You're gonna find out who took our cocaine - and then, I'm gonna kill them. [LAW1_H:LAWYER1] That's a good idea. That's a GREAT idea. Let me think, let me think, let me think. [LAW1_I:LAWYER1] - OH! There's this retired Colonel, Colonel Juan Garcia Cortez. [LAW1_J:LAWYER1] He's the one that helped me set up this deal [LAW1_K:LAWYER1] well away from Vice City's established thugs. Ok? [LAW1_L:LAWYER1] Now, listen. He's holding his party out in the bay on his expensive yacht [LAW1_M:LAWYER1] and all of Vice City's big players are gonna be there. OK? [LAW1_N:LAWYER1] I have an invite, of course I have an invite, [LAW1_O:LAWYER1] but there's no way that I'm going out there, sticking my head out the door - no way! Not gonna happen. [LAW1_P:LAWYER1] I told you, shut up! I'll go myself... [LAW1_Q:LAWYER1] Ho - whoa, whoa! Hey, I like 1978 too, but, y'know, this isn't gonna be a beer and strippers do. [LAW1_R:LAWYER1] I mean, no offense, but I think that you might turn heads on the runway for the wrong reasons. [LAW1_S:LAWYER1] What's wrong with the way I'm dressed? [LAW1_T:LAWYER1] Ok, look, here. Stop by Rafael's, tell him I sent 'ya. He'll make you look respectable. [LAW1_U:LAWYER1] OK, go, c'mon... [LAWP_1:LAWYER1] Buenas noches. [LAWP_2:LAWYER1] I understand you are here on the behalf of Mr. Rosenberg, [LAWP_3:LAWYER1] I hope any recent problems have not affected his health, or uh, [LAWP_4:LAWYER1] mental well being, Mr...uh? [LAWP_5:LAWYER1] Vercetti. He's just got a touch of...agoraphobia. [LAWP_6:LAWYER1] Excellent, excellent. And you? [LAWP_7:LAWYER1] I just want my merchandise. [LAWP_8:LAWYER1] Ah. It's an unfortunate set of circumstances for all involved. [LAWP_9:LAWYER1] Of course I have initiated my own lines of inquiry, [LAWP_10:LAWYER1] but such a delicate matter will take time. [LAWP_11:LAWYER1] Perhaps we will talk later. [LAWP_12:LAWYER1] Meanwhile, let me introduce you to my daughter, [LAWP_13:LAWYER1] Mercedes! [LAWP_14:LAWYER1] Caramia, could you look after our guest while I attend to my necessary obligations? [LAWP_15:LAWYER1] Of course, daddy. [LAWP_16:LAWYER1] Please excuse me. [LAWP_17:LAWYER1] Mercedes!? [LAWP_18:LAWYER1] You try living with it. [LAWP_19:LAWYER1] Anyway, let me point out some of our more distinguished guests... [LAWP_20:LAWYER1] That's our congressman Alex Shrub with rising silicone star Candy Suxxx... [LAWP_21:LAWYER1] And have you met my lovely wife Laura? No? [LAWP_22:LAWYER1] Well, unfortunately she's in Alabama. This is Candy. [LAWP_23:LAWYER1] And over there we have the Vice City Mambas' star tight end, BJ - [LAWP_24:LAWYER1] always the charmer. [LAWP_25:LAWYER1] I blocked down on him and then I put him in a wheelchair! [LAWP_26:LAWYER1] Haha, that is good! [LAWP_27:LAWYER1] Well now, I'm looking at some prime real estate property. [LAWP_28:LAWYER1] And that poolside amphibian is Jezz Torrent, [LAWP_29:LAWYER1] lead singer with Love Fist. [LAWP_30:LAWYER1] Can I tell yous - do you know how they play ping-pong in Thailand? [LAWP_31:LAWYER1] Let me tell you's, [LAWP_32:LAWYER1] it does not involve a paddle, if you know what I mean! [LAWP_33:LAWYER1] Impotent. [LAWP_34:LAWYER1] And the chatty trio. [LAWP_35:LAWYER1] That sleeping sweat gland is Papa's right hand gimp, Gonzalez [LAWP_36:LAWYER1] and the other two are Pastor Richards [LAWP_37:LAWYER1] and pseudo intellectual film director, Steve Scott. [LAWP_38:LAWYER1] ...passion with the nympho invaders, [LAWP_39:LAWYER1] when the giant shark comes in and [LAWP_40:LAWYER1] just bites their dicks off! [LAWP_41:LAWYER1] Ha now, you never saw anything like that before, have you? [LAWP_42:LAWYER1] Colonel! [LAWP_43:LAWYER1] your parties as ever are a triumph, hahahaha! [LAWP_44:LAWYER1] I can only apologize for my late arrival. [LAWP_45:LAWYER1] Ah, de nada amigo. How do we find you? [LAWP_46:LAWYER1] Our business is very trying - barbarians at the gates. [LAWP_47:LAWYER1] A time for rewarding one's friends and liquidating one's enemies, amigo. [LAWP_48:LAWYER1] Who's the loudmouth? [LAWP_49:LAWYER1] Ricardo Diaz. He's Mr. Coke. [LAWP_50:LAWYER1] Mercedes! [LAWP_51:LAWYER1] Oh, I was just taking my friend back into town. [LAWP_52:LAWYER1] Another time, Ricardo! [LAWP_53:LAWYER1] Let's get out of here. [LAWP_54:LAWYER1] Actually, take me to the Pole Position club. [LAW1_2:LAWYER1] ~g~Get to the Colonel's boat. [LAW1_4:LAWYER1] ~r~You killed the Colonel's daughter! [LAW1_5:LAWYER1] Will you be working for my father? [LAW1_6:LAWYER1] Maybe. [LAW1_7:LAWYER1] Do you mind me resting my hand in your lap? [LAW1_8:LAWYER1] Maybe... [LAW1_9:LAWYER1] It's so difficult having a rich and powerful father. Vamos. [LAW1_10:LAWYER1] See you around, handsome! [LAW1_11:LAWYER1] I'm sure you will. [LAW1_12:LAWYER1] Hmmmm...nice bike. [LAW1_13:LAWYER1] No! My Bike! [LAW1_3:LAWYER1] ~g~Take the Colonel's daughter to the Pole Position club. [HELP20:LAWYER1] Follow the ~h~T-shirt~w~ blip on the radar to find Rafael's. [LAW1_14:LAWYER1] Wow, I like, really dig your motorcycle. [LAW1_15:LAWYER1] Yeah babe, just picked it up from Howlin' Pete's {=================================== MISSION TABLE LAWYER2 ===================================} [LAW2_A:LAWYER2] Ah! Well, I hope you're having a good time. Because I'm going out of my mind with worry here. What did you find out? [LAW2_B:LAWYER2] That there are more criminals in this town than in prison. We need a lead from the streets... [LAW2_C:LAWYER2] Ok, let me think, let me think, let me think - [LAW2_D:LAWYER2] - AH! I've got it! [LAW2_E:LAWYER2] Ok, There's this limey, some music industry slimeball, [LAW2_F:LAWYER2] goes by the name of Kent Paul. [LAW2_G:LAWYER2] Anyway, he's got his nose so far up most of Vice City's ass [LAW2_I:LAWYER2] it's this guy, all right? He's always at The Malibu. [LAW2_J:LAWYER2] I'll go pay him a visit. [LAW2B_A:LAWYER2] Where'd you pop up from? [LAW2B_B:LAWYER2] I've been looking for a bird like you for ages, mate... [LAW2B_C:LAWYER2] Kent Paul, mate. Yeah, I'm the guvnor 'round here. [LAW2B_D:LAWYER2] I'm looking for some English guy... [LAW2B_E:LAWYER2] I sort things out, you know what I mean? [LAW2B_F:LAWYER2] I'll treat you. Whatever you want, I'll get you, girl. [LAW2B_G:LAWYER2] Don't you worry about a thing, mate. [LAW2B_H:LAWYER2] Get lost, honey. [LAW2B_I:LAWYER2] Oi oi oi oi oi! [LAW2B_J:LAWYER2] You Kent Paul? I'm a friend of Rosenberg's... [LAW2B_K:LAWYER2] Rosenberg...Rosenberg...Oh, that bonkers ambulance chaser! [LAW2B_L:LAWYER2] That guy could defend an innocent man all the way to death row! [LAW2B_M:LAWYER2] Give us another drink, bruv. [LAW2B_N:LAWYER2] Everybody's a comedian. [LAW2B_O:LAWYER2] Listen to me, I'm missing twenty keys and a lot of cash... [LAW2B_P:LAWYER2] Drugs, mate? It's a mug's game. [LAW2B_Q:LAWYER2] What do you know about it? [LAW2B_R:LAWYER2] Oi oi! What I was coming to was, [LAW2B_S:LAWYER2] there's some chef-cum-trumpetshifter who deals out kitchen of a hotel on Ocean Drive. [LAW2B_T:LAWYER2] He's been looking real pleased with himself lately. You could go and check him out...?! [LAW2B_U:LAWYER2] I will - and I'll be seeing you around. [LAW2B_V:LAWYER2] Yeah, that's right. Go on - walk away, you mug. I'll knock you spark out! [LAW2B_W:LAWYER2] Give me a drink - and where's that slut! [LAW2C_A:LAWYER2] Oh, way to go, tough guy. Beat him to a pulp. That should make him real chatty. [LAW2C_B:LAWYER2] You want some, too? [LAW2C_C:LAWYER2] Hey, chill. I want what you want, brother. [LAW2C_D:LAWYER2] Oh, yeah? And what's that? [LAW2C_E:LAWYER2] Your green - and my dead brother's white lady. Unfortunately, you just silenced our lead. [LAW2C_F:LAWYER2] Accidents happen. Get lost. [LAW2C_G:LAWYER2] Hey, hey, whoa. No need to go all 'Lone Ranger' on my ass. [LAW2C_H:LAWYER2] The way I see it - we two hombres in a strange town. We need to watch each other's back. [LAW2C_I:LAWYER2] My back's just fine, brother... [LAW2C_J:LAWYER2] You sure about that? Here, take this. [LAW2C_K:LAWYER2] Follow me! [LAW2_1:LAWYER2] Hey, whatchoo lookin' at? [LAW2_2:LAWYER2] You better start talking.. [LAW2_3:LAWYER2] Hey, make me, you prick! [LAW2_4:LAWYER2] This way! [LAW2_5:LAWYER2] I'm going to go see what I can dig up. I'll be watching you, Tommy. [LAW2_6:LAWYER2] ~g~Go to the Malibu Club and find Kent Paul. [LAW2_7:LAWYER2] ~g~Go and find the chef on Ocean Drive. [LAW2_10:LAWYER2] ~g~Drive back to the hotel. [LAW2_11:LAWYER2] ~g~Pick up his cell phone. [LAW2_12:LAWYER2] Cell phone acquired! You can now receive phone calls. [LAW2_13:LAWYER2] ~g~You've left Lance behind! Go and get him! [LAW2_14:LAWYER2] We gotta get the hell outta here! [GUN_2A:LAWYER2] Hold the ~h~~k~~PED_LOCK_TARGET~ ~w~button to ~h~auto-target~w~, press the ~h~~k~~PED_FIREWEAPON~ ~w~button to ~h~fire! [GUN_2C:LAWYER2] Hold the ~h~~k~~PED_LOCK_TARGET~ ~w~button to ~h~auto-target~w~, press the ~h~~k~~PED_FIREWEAPON~ ~w~button to ~h~fire! [GUN_2D:LAWYER2] Hold the ~h~~k~~PED_LOCK_TARGET~ ~w~button to ~h~auto-target~w~, press the ~h~~k~~PED_FIREWEAPON~ ~w~button to ~h~fire! [HELP17:LAWYER2] Press the ~h~~k~~PED_FIREWEAPON~ ~w~button to attack the chef. [HELP18:LAWYER2] Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to attack the chef. [LAW3_11:LAWYER2] Stand in the ~q~pink marker~w~ to view the weapons on offer. [LAW3_12:LAWYER2] You can select weapons by pressing ~h~left~w~ or ~h~right~w~ on the ~h~directional button. [LAW3_13:LAWYER2] If you have enough cash you can buy weapons by pressing the ~h~~k~~PED_SPRINT~ ~w~button. [LAW3_14:LAWYER2] You can exit the shop by pressing the ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~button. [LAW3_15:LAWYER2] Follow the ~h~Gun blip~w~ on the radar to find ~h~Ammu-Nation. [LAW2_15:LAWYER2] ~g~Go to Ammu-Nation. [LAW2_H:LAWYER2] that if anybody knows the whereabouts of 20 k's of coke, [LAW2_K:LAWYER2] Take it easy now. [LAW2_16:LAWYER2] One thing you gotta realize about this town. You gotta pack some heat. [LAW2_17:LAWYER2] C'mon, the local gun shop's a couple of blocks away. [LAW2_18:LAWYER2] Tommy, every man needs a little R&R once in a while. [LAW2_19:LAWYER2] This here's the Pole Position Strip Club. You might want to drop in some time. {=================================== MISSION TABLE LAWYER3 ===================================} [LAW3_A:LAWYER3] Aaah! Oh, for god's sake, it's you! Oh, Jeez - I'm gonna need new pants! [LAW3_B:LAWYER3] Hey, those psychos from up north - they've been on the horn, and they're coming down here soon. [LAW3_C:LAWYER3] Now where is the goddamn money?! [LAW3_D:LAWYER3] Relax, relax. We're not at that part yet. [LAW3_E:LAWYER3] Ohhh... I thought that you were taking care of this, I really did! [LAW3_F:LAWYER3] And now those guidos say we gotta do them a favor. [LAW3_G:LAWYER3] You mean I gotta do 'em a favor. [LAW3_H:LAWYER3] Oh, of course that's what I mean. Do I look like I can intimidate a jury? [LAW3_I:LAWYER3] I couldn't intimidate a child - and believe me, I've tried. [LAW3_J:LAWYER3] Now, look. It's either that, or Forelli's cousin, Giorgio, gets five years for fraud. [LAW3_K:LAWYER3] You gotta take these guys OUT! [LAW3_L:LAWYER3] I understand. Help the jury change their minds. Don't worry about it. [LAW3_M:LAWYER3] No no no no no - NO! I tried that. The jury case didn't go so well, [LAW3_N:LAWYER3] so MAKE them change their minds. [LAW3_1:LAWYER3] Giorgio sends his regards. [LAW3_2:LAWYER3] Remember, guilty is a dirty word. [LAW3_3:LAWYER3] Innocent until I say otherwise. [LAW3_4:LAWYER3] You know he's not guilty. [LAW3_5:LAWYER3] You remember Giorgio? You remember he's innocent. [LAW3_6:LAWYER3] Not guilty, understand? Good. [LAW3_8:LAWYER3] ~r~You killed a Juror! [LAW3_9:LAWYER3] ~g~Smash up the Juror's car to get him out! [HELP40:LAWYER3] You can smash cars up by using the hammer or a similar weapon. [HELP41:LAWYER3] or you can ram them with a vehicle [LAW3_20:LAWYER3] ~g~Smash up the Juror's car! [LAW3_21:LAWYER3] I can't believe this is happening! [LAW3_22:LAWYER3] Unbelievable! [LAW3_23:LAWYER3] Ok! Ok, man! I get the message! [LAW3_24:LAWYER3] ~g~That hammer would be useful. [LAW3_7:LAWYER3] ~g~Go and intimidate the two jurors, but DON'T kill them! [HELP23:LAWYER3] You can follow the ~h~hammer blip~w~ on the radar if you want to buy melee weapons from the hardware store. [LAW3_16:LAWYER3] Dumb Florida Moron. [LAW3_17:LAWYER3] Get out of the way! {=================================== MISSION TABLE LAWYER4 ===================================} [LAW4_A:LAWYER4] Avery, it goes without saying... Tommy! Tommy! Any progress? No, no, no - tell me later, tell me later. [LAW4_B:LAWYER4] Tommy, this is Avery Carrington - I believe you met at the party? [LAW4_C:LAWYER4] Not in person. [LAW4_D:LAWYER4] Howdy. [LAW4_E:LAWYER4] Avery here has a proposition. [LAW4_F:LAWYER4] Haven't we got other things on our mind? [LAW4_G:LAWYER4] I'm trying to keep the wolves from the door, so could you please cut me some slack? [LAW4_H:LAWYER4] I'm stretched like a wire and even if I'm dead by the end of the week, I'd like to think that I didn't die poor. [LAW4_I:LAWYER4] Now just calm down, both of you. [LAW4_J:LAWYER4] Son, you help me and any greaseballs giving you a hard time, I'll see to it they take a long dirt nap. [LAW4_K:LAWYER4] Ok. What could I do for ya'? [LAW4_L:LAWYER4] This delivery company's got its depot on some prime land. They won't sell. [LAW4_M:LAWYER4] They're hanging on like a big old prairie rat, so we gotta go in there and smoke that vermin out. [LAW4_N:LAWYER4] Head on down there and stir up a hornet's nest [LAW4_O:LAWYER4] - the security will have their hands full and then you can sneak in and put 'em out of business. [LAW4_P:LAWYER4] And you could drop by Rafael's for a change of clothes. You might be there a while, but yeah, go for it. [LAW4_Q:LAWYER4] Should be a riot. [LAW4_R:LAWYER4] If the balls drop like they should, stop by my office sometime... [LAW4_1:LAWYER4] Please disperse. The management will discuss any grievances in the appropriate manner! [LAW4_2:LAWYER4] Please disperse. Go back to your homes! [LAW4_3:LAWYER4] Please disperse! This is inappropriate! [LAW4_4:LAWYER4] Please disperse. You will all end up on the streets. [LAW4_5:LAWYER4] Sticks out, boys! Let's crack some commie skulls! [LAW4_13:LAWYER4] ~g~Start fighting with at least 4 workers to get a riot started. [LAW4_14:LAWYER4] ~g~Destroy the vans in the compound! [HELP38:LAWYER4] If you take out someone who's holding a weapon, they will drop it. [HELP39:LAWYER4] You can target and shoot explosive barrels but keep your distance. {=================================== MISSION TABLE MIAMI_1 ===================================} [T4X4_1A:MIAMI_1] ~g~You have ~1~ seconds to collect ~y~24~g~ checkpoints. ~g~You may collect them in ~y~ANY ORDER. [T4X4_1B:MIAMI_1] ~y~PASS THROUGH~g~ the first checkpoint to start the ~r~TIMER. [T4X4_1C:MIAMI_1] ~1~ of 24! [GETBIK1:MIAMI_1] You have ~1~ seconds to get on a PCJ 600! [GETBIK3:MIAMI_1] ~r~You need a PCJ 600 to attempt this mission! {=================================== MISSION TABLE MM ===================================} [BLOD_04:MM] CAR HEALTH: [BLOD_05:MM] ~g~TARGET TIME: ~1~ Minute [BLOD_06:MM] ~g~TARGET TIME: ~1~ Minutes [BLOD_07:MM] NEW Best Time: ~1~ Seconds [BLOD_08:MM] Cars Destroyed: ~1~ [BLOD_09:MM] $~1~ [BLOD_10:MM] WINNER!! [BLOD_01:MM] Drive through the checkpoints to increase your overall time. [BLOD_02:MM] You will fail if your overall time reaches zero. [BLOD_03:MM] Get your overall time above the Target Time to win! {=================================== MISSION TABLE OVALRIG ===================================} [HOTR_01:OVALRIG] ~g~The Race lasts for 12 laps. Only 1st, 2nd and 3rd places qualify for winnings. [HOTR_02:OVALRIG] ~g~If your car is destroyed you will be disqualified. [HOTR_03:OVALRIG] ~g~When your car is damaged you can get it repaired at the pitstop. [HOTR_04:OVALRIG] ~g~This is the way to leave the stadium. [HOTR_05:OVALRIG] Car Health: [HOTR_06:OVALRIG] Laps: [HOTR_07:OVALRIG] New best time: ~1~:0~1~ [HOTR_08:OVALRIG] Time: ~1~:~1~ [HOTR_10:OVALRIG] Race Time: [HOTR_09:OVALRIG] Position: [HOTR_12:OVALRIG] ~r~Your car has been destroyed! [HOTR_13:OVALRIG] ~r~You didn't win the race! [HOTR_14:OVALRIG] ~r~You have been disqualified! [HOTR_15:OVALRIG] Time: ~1~:~1~ [HOTR_16:OVALRIG] Time: ~1~:0~1~ [HOTR_17:OVALRIG] Best Time: ~1~:~1~ [HOTR_18:OVALRIG] Best Time: ~1~:0~1~ [HOTR_19:OVALRIG] Best Time: NA [HOTR_20:OVALRIG] New Best Time: ~1~:~1~ [HOTR_21:OVALRIG] New Best Time: ~1~:0~1~ [HOTR_22:OVALRIG] Best Result: NA [HOTR_23:OVALRIG] Best Result: 1st [HOTR_24:OVALRIG] Best Result: 2nd [HOTR_25:OVALRIG] Best Result: 3rd [HOTR_26:OVALRIG] Best Result: ~1~th [HOTR_27:OVALRIG] Best Lap Time: ~1~.~1~ seconds [HOTR_28:OVALRIG] Best Lap Time: ~1~.0~1~ seconds [HOTR_29:OVALRIG] $~1~ [HOTR_30:OVALRIG] 1ST PLACE [HOTR_31:OVALRIG] 2ND PLACE [HOTR_32:OVALRIG] 3RD PLACE [HOTR_33:OVALRIG] Best Lap Time: NA [HOTR_11:OVALRIG] New best lap time: ~1~.~1~ seconds [HOTR_34:OVALRIG] New best lap time: ~1~.0~1~ seconds {=================================== MISSION TABLE PHIL1 ===================================} [PHIL1_A:PHIL1] Phil? [PHIL1_B:PHIL1] RUN! [PHIL1_C:PHIL1] Run [PHIL1_E:PHIL1] Shit Phil, you drink that stuff? [PHIL1_F:PHIL1] Hell, you don't have to drink it [PHIL1_G:PHIL1] - just a good whiff will set you off. Hoowwee! [PHIL1_H:PHIL1] Listen Phil, you said you could fix me up with some firepower... [PHIL1_I:PHIL1] Sure thing. [PHIL1_J:PHIL1] There's some Mexican gun-runner been doing me for business of late. [PHIL1_K:PHIL1] He does his weekly run about now. [PHIL1_L:PHIL1] Ram his hardware off the back of his trucks before he goes to ground. [PHIL1_M:PHIL1] And you'd be doing me a favor while you're at it. [PHIL1_N:PHIL1] Then finish him off. [PHI1_01:PHIL1] ~g~Go and knock the arms off the back of the dealers' trucks. [PHI1_02:PHIL1] ~g~The arms dealer dropped his load. Smash the crate and pick up the weapon. [PHI1_03:PHIL1] ~g~Looks like they have called for back up. [PHI1_04:PHIL1] ~g~Now go and finish off the remaining arms dealers. [PHI1_HP:PHIL1] When using Detonator Grenades, throw a grenade then trigger the explosion at any time. [PHIL1_O:PHIL1] Hoooooweeeeee! [PHIL1_D:PHIL1] Never get a naked flame too close to one of Phil Cassidy's Boomshine stills! {=================================== MISSION TABLE PHIL2 ===================================} [PHIL2_A:PHIL2] Hey Phil, how's it goin? [PHIL2_B:PHIL2] Heeyyyy, Tommy. Howyadoin'? Ish been too long... [PHIL2_C:PHIL2] I swear you should lay off that boomshine, man - [PHIL2_D:PHIL2] smells like paint stripper. Making my eyes burn... [PHIL2_E:PHIL2] Shshs shhh youshelf Tommy, [PHIL2_F:PHIL2] and come over here because there's someshin' I wanna show you.. someshin. [PHIL2_G:PHIL2] Woof! God! Should I be able to smell that from way over here? I'm feeling woozy. [PHIL2_H:PHIL2] Don'tchaworry about the shmell Tommy, you jush wash thish. [PHIL2_I:PHIL2] Shittycheapbatteriesh or shumin'. There'sh shum more on the bench. [PHIL2_J:PHIL2] TA-DAAA! [PHIL2_K:PHIL2] Aww Damn! [PHI2_01:PHIL2] ~g~Quick, get Phil to the hospital. [PHI2_03:PHIL2] ~r~Phil Cassidy is dead!!! Now who's gonna supply arms in Vice City? [PHI2_05:PHIL2] Not the hospital, man! Too many cops and Viet Cong! [PHI2_06:PHIL2] There's an ex-army surgeon owes me a few favors and a lawnmower. [PHI2_07:PHIL2] He's got a place down Little Havana - ooo look, a giant fish. [PHI2_08:PHIL2] Watch out! Charlie in the tree line! [PHI2_09:PHIL2] Is it me or are the roads made of jelly? [PHI2_10:PHIL2] Broken Spoon to Mother Hen, you copy? [PHI2_11:PHIL2] Spooney Wooney Woo Woo Woooo! [PHI2_12:PHIL2] He's come for me boy! [PHI2_13:PHIL2] Black feathered wings beating all around... [PHI2_14:PHIL2] It's beautiful, man ... it's beautiful ... but so cold ... [PHI2_15:PHIL2] 10-4 we've got a drunk driver. [PHI2_04:PHIL2] PHIL'S HEALTH: [PHI_AS1:PHIL2] PHILS PLACE ASSET COMPLETED [PHI_AS2:PHIL2] ~g~New Weapons available to purchase from Phils Place. {=================================== MISSION TABLE PIZZA ===================================} [PIZ1_01:PIZZA] ~g~Go deliver these pizzas, you must throw the pizza to the customers. Do a drive-by to throw the pizzas. [PIZ1_02:PIZZA] ~g~You have thrown all your pizzas, go back and get some more. [PIZ1_05:PIZZA] ~g~You have five minutes to deliver the orders before the customers phone another pizza shop. [PIZ1_07:PIZZA] ~r~You killed the customer! You're fired. [PIZ1_08:PIZZA] ~r~You are out of time. You're fired. [PIZ1_09:PIZZA] ~r~You destroyed our bike! You're fired. [PIZ1_11:PIZZA] Hey! Get back on the bike! [PIZ1_12:PIZZA] Pizzas left: [PIZ1_06:PIZZA] Press the~h~ ~k~~TOGGLE_SUBMISSIONS~~w~ when on the bike to cancel the mission. [PIZ1_13:PIZZA] Get these delivered nice and hot. [PIZ1_14:PIZZA] Pal, pizza's for you. [PIZ1_15:PIZZA] Hey, come on Mister, deliver these quick. [PIZ1_16:PIZZA] What are you waiting around for Mister? You got pizza to deliver. [PIZ1_17:PIZZA] I know you didn't want to be a pizza boy, well I don't give a damn. [PIZ1_18:PIZZA] Deliver these. [PIZ1_19:PIZZA] These need delivering. [PIZ1_20:PIZZA] Come on Mister, deliver these things or you're sacked. [PIZ1_21:PIZZA] We got people waiting pal. [PIZ1_22:PIZZA] What are you waiting around for? These need delivering! [PIZ1_23:PIZZA] Deliver the damn food Mister. [PIZ1_24:PIZZA] These need delivering pal. [PIZ1_25:PIZZA] Man, can you take these? [PIZ1_26:PIZZA] Mister, deliver these pronto, avamos amigo. [PIZ1_27:PIZZA] Come on, we're in a rush, deliver these. [PIZ1_28:PIZZA] You again? well deliver these quick pal. [PIZ1_29:PIZZA] No wasting time this time pal. [PIZ1_30:PIZZA] Come on you lazy bastard, deliver this crap on time. [PIZ1_31:PIZZA] You'll never get a promotion unless you move faster this time. [PIZ1_32:PIZZA] ~r~Pizza's too hot to handle? [PIZ1_33:PIZZA] ~g~Return to the restaurant for more orders. [PIZ1_34:PIZZA] ~g~Pizza delivered, here's your cash. [PIZ_WON:PIZZA] Pizza Mission Complete. Your max Health increased to 150. {=================================== MISSION TABLE PORN1 ===================================} [POR1_A:PORN1] Action. [POR1_B:PORN1] Whoa! Now that's big. [POR1_C:PORN1] 12 inches. That is regulation baby. [POR1_D:PORN1] CUT!! Who IS this idiot? You! YOU! Why are you in my space? WHY? [POR1_E:PORN1] What is all this crap? [POR1_F:PORN1] Aliens? Fishing poles? [POR1_G:PORN1] Who's ever seen a shark that big? [POR1_H:PORN1] All this stuff's gotta go. [POR1_I:PORN1] Why'd you get in this business, ya prick? [POR1_J:PORN1] Huh? [POR1_K:PORN1] For the pussy, that's why! What is this?? [POR1_L:PORN1] This is my art - SECURITY! [POR1_M:PORN1] Look, you pompous asshole, I own you now. I own all of this. [POR1_N:PORN1] We're gonna turn this place around... [POR1_O:PORN1] I'm gonna make you rich. [POR1_P:PORN1] Uh. You're - You - you're Tommy Vercetti? But I thought that you were... [POR1_Q:PORN1] That's right. [POR1_R:PORN1] We're gonna be making some changes around here and start making some real money. [POR1_S:PORN1] Actually, have you ever thought about, umm... [POR1_T:PORN1] But first we're going to need some good-looking broads. [POR1_U:PORN1] Yeh, girls are fine but you... whew! [POR1_02:PORN1] ~g~ Go and take out Candy's pimp, then return and pick up Candy. [POR1_04:PORN1] Yo, Candy. I'm looking for movie talent - you interested? [POR1_05:PORN1] Sure! But, you'd have to talk to my agent... [POR1_06:PORN1] The HELL are you doin'? [POR1_07:PORN1] You should have stayed at home today! [POR1_7B:PORN1] Can you believe this asshole? [POR1_08:PORN1] Hey Mercedes! [POR1_09:PORN1] Hey Tommy! You wanna party? [POR1_10:PORN1] Not now sweets. You interested in doing some movies? [POR1_11:PORN1] Of course. As long as it's cheap and sleazy. [POR1_13:PORN1] ~g~Take the girls back to the Studio to meet Steve. [POR1_17:PORN1] Whoa, cool shark! [POR1_18:PORN1] ~r~Mercedes is dead! [POR1_20:PORN1] Tommy where are you going? Get back here! [POR1_21:PORN1] Where are you going? [POR1_22:PORN1] Tommy, when are we going to spend some time alone together? [POR1_01:PORN1] ~g~Candy Suxxx would be perfect for a starring role! [POR1_12:PORN1] ~g~Take Candy with you to meet up with Mercedes. [POR1_16:PORN1] Maybe later, babe... [POR1_24:PORN1] ~g~Go back and collect Candy. [POR1_25:PORN1] ~g~You have left Candy behind, go and get her. [POR1_23:PORN1] ~g~Candy will be taking care of business ~h~Downtown~g~. [POR1_26:PORN1] ~g~Here's Candy, looks like she has been with Congressman Shrub again. [POR1_15:PORN1] Tommy, you coming in for a warm-up? [POR1_14:PORN1] Heh heh - you're hired! [POR1_27:PORN1] Come on, let's go. [POR1_28:PORN1] Tommy be careful! My implants aren't insured yet! [POR1_29:PORN1] You call that driving? [POR1_30:PORN1] I can't do porno after this! [POR1_31:PORN1] What? Are you trying to kill me? I thought I was the star! {=================================== MISSION TABLE PORN2 ===================================} [POR2_A:PORN2] How's filming going, Steve? [POR2_B:PORN2] Well, Candy is a natural and that new girl - she's insatiable! [POR2_C:PORN2] She went through half the cast and crew before I even took a light reading. [POR2_D:PORN2] Anyway, hey, tomorrow we're going on location to shoot the boat scenes - [POR2_E:PORN2] Boat scenes?! What boat scenes? [POR2_F:PORN2] The fishermen are in the throes of passion when this giant shark comes in - [POR2_G:PORN2] What'd I say about the giant shark? [POR2_H:PORN2] I said, 'NO GIANT SHARK', alright? [POR2_I:PORN2] Just keep the cameras pointed at the poontang! [POR2_J:PORN2] Ok ok, hey Tommy, a guy's gotta try, right? [POR2_K:PORN2] Get those flyers printed up? [POR2_L:PORN2] Yeah, but nobody's gonna let us distribute those things, I mean [POR2_M:PORN2] They're just too, uh, they're unimaginative. [POR2_N:PORN2] You don't worry about that. [POR2_O:PORN2] I've got my own ideas for distribution. [POR2_P:PORN2] O.K. Hey, Candy, uh - in my trailer. [POR2_01:PORN2] ~g~There is a seaplane that was used as a prop in some old indie film round the back of the studios. [POR2_02:PORN2] ~g~Pick one of the checkpoints to start dropping the flyers from. [POR2_03:PORN2] ~g~Drop the flyers all the way to the end checkpoint. [POR2_04:PORN2] ~r~LOW FUEL!!! [POR2_05:PORN2] ~g~Use it to distribute the flyers around town. [DILDO:PORN2] Skimmer Fuel: [POR2_Q:PORN2] Oh, boy. [PORN2_9:PORN2] ~g~You have ~1~ seconds to return to a Skimmer before the mission ends. {=================================== MISSION TABLE PORN3 ===================================} [POR3_A:PORN3] Ok, what's the problem now? [POR3_B:PORN3] SSShhhh! [POR3_C:PORN3] Well, after his close encounter with the nympho-invaders, [POR3_D:PORN3] our hero finds himself unable to think of anything but this huge phallic mountain - [POR3_E:PORN3] and that's when I want to do the scene with the vat of mashed potatoes, but then we, uh - [POR3_F:PORN3] I don't give a crap about that! [POR3_G:PORN3] J - Just keep going, keep going! [POR3_H:PORN3] Hey Tommy... [POR3_I:PORN3] You mentioned something about some legal problem on the phone? [POR3_J:PORN3] Congressman Alex Shrub has jumped on the pre-election bandwagon, he's going after the puritan vote. [POR3_K:PORN3] Rumors are he's gonna support measures to restrict, shall we say, [POR3_L:PORN3] the more fleshy aspects of this nation's great entertainment industry. [POR3_M:PORN3] Great. [POR3_N:PORN3] Candy! You know Shrub, [POR3_O:PORN3] you guys get up to anything kinky? [POR3_P:PORN3] Oh yeah, oh yeah, oh yeah! Yes yes yes YES OOOoooh! [POR3_Q:PORN3] Please - tell me you got that. [POR3_R:PORN3] Was that part of the, uh... or was she talking to..? [POR3_S:PORN3] Hey, I can never tell. Anyway... [POR3_T:PORN3] You're probably best following her after the shoot, [POR3_U:PORN3] see if she'll lead you to their new love nest. [POR3_V:PORN3] You got a camera? [POR3_X:PORN3] Yeah. Get him a camera. [POR3_02:PORN3] ~r~You've killed the Congressman! There's no way you can blackmail him now. [POR3_03:PORN3] ~r~You've alerted the Congressman's protection, they will get him out of there immediately. [POR3_04:PORN3] Uh, Candy, could you call me Martha? [POR3_05:PORN3] Oh Alex - I mean Martha. Whatever you say... [POR3_06:PORN3] Martha, someone's watching.. how kinky. [POR3_07:PORN3] You! Give me that camera! [POR3_01:PORN3] ~g~Follow Candy's ~h~Stretch~g~. [POR3_15:PORN3] ~r~You trashed Candy's Stretch! [POR3_17:PORN3] ~g~Get back to the Porn Studios with the film. [POR3_19:PORN3] ~r~You ran out of film! [POR3_21:PORN3] ~g~You lost Candy's Stretch! [POR3_22:PORN3] ~g~The WK Chariot Hotel across from his balcony should provide an ideal photo-grabbing location. [POR3_23:PORN3] ~g~There is a side door that will allow you access to the hotel. [POR3_08:PORN3] Press and hold the~h~ ~k~~PED_LOCK_TARGET~ ~w~button to ~h~target~w~ with the camera. [POR3_09:PORN3] Press and hold the~h~ ~k~~PED_LOCK_TARGET~ ~w~button to ~h~target ~w~with the camera. [POR3_10:PORN3] Press the~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~button to ~h~zoom in ~w~with the camera and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~button to ~h~zoom out ~w~again. [POR3_11:PORN3] Press the~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~button to ~h~zoom in ~w~with the camera and the~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~button to ~h~zoom out ~w~again. [POR3_12:PORN3] Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to take a picture. [POR3_13:PORN3] Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to take a picture. [POR3_14:PORN3] Press the~h~ ~k~~PED_FIREWEAPON~ ~w~button to take a picture. [POR3_20:PORN3] ~g~If you need transport, use the ~h~Sparrow~g~ round the back. [POR3_16:PORN3] ~g~You need three good blackmail photographs of Alex Shrub with Candy. [POR3_24:PORN3] PHOTOS TAKEN: {=================================== MISSION TABLE PORN4 ===================================} [POR4_A:PORN4] I'm sorry, but I just can't swallow this right now. [POR4_B:PORN4] Oh COME ON darling! [POR4_C:PORN4] He's hung like a sperm whale for pity's sake, [POR4_D:PORN4] how can you not feel the part?! [POR4_E:PORN4] But Stevie... [POR4_F:PORN4] How's my star director? [POR4_G:PORN4] Oh, man. The struggle between the artistic integrity and [POR4_H:PORN4] the humping, pumping action continues unabated. [POR4_I:PORN4] And before you ask, yes, all four videos will be released by their... [POR4_J:PORN4] Honey, can you PLEASE keep the anaconda in the shot, [POR4_K:PORN4] he costs more per hour than you do! [POR4_L:PORN4] Oh, sorry Steve. [POR4_M:PORN4] I was thinking, we need some kind of big stunt to really promote the launch. [POR4_N:PORN4] Something that will make a real impact on the City - you got any ideas? [POR4_O:PORN4] Well, in the old days they used to have gala events, [POR4_P:PORN4] stars, limos, the night sky crisscrossed with searchlights... [POR4_Q:PORN4] Searchlights! I've got an idea... [POR4_R:PORN4] ...yeah, yeah, yeah. The little sequined numbers, and the limos, oh, premieres [POR4_S:PORN4] Oh, yes ma'am, of course ma'am, [POR4_T:PORN4] and the press, and the barrage of lights... [POR4_01:PORN4] ~g~Go ~y~Downtown~g~ and adjust the spotlight on top of the building. [POR4_02:PORN4] ~g~A fast bike will be needed to jump from roof to roof. The Security Guard usually drives a ~y~PCJ 600~g~ to work... [POR4_03:PORN4] ~g~You will need to get onto the roofs of the buildings. There should be a lift into one of the upper offices... [POR4_06:PORN4] ~g~Return to the lower office if you need access to the rooftops again. [POR4_07:PORN4] ~g~You will need a bike so you can jump from building to building. [POR4_08:PORN4] ~g~Smash through the window to start the course. You have until 07:00 before it gets too light to get up there unseen. [POR4_09:PORN4] ~g~The pickups will show you which building to jump to next. [POR4_10:PORN4] ~r~It's too light to get up there unseen. [POR4_11:PORN4] ~g~Return to the ladder if you need access to the rooftops again. [POR4_05:PORN4] ~g~These stairs will lead round to a lower office. [POR_AS1:PORN4] FILM STUDIO ASSET COMPLETED [POR_AS2:PORN4] ~g~Inter Global Films will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. {=================================== MISSION TABLE PROT1 ===================================} [PRO1_B:PROT1] I can't stand this look. Tommy, whadaya say? Whadaya say we put a bar in... [PRO1_D:PROT1] Listen to me, [PRO1_E:PROT1] The time to take over this town is now. It's all out there waiting for us. [PRO1_F:PROT1] We need to start seizing territory, [PRO1_G:PROT1] let Vice City know we're the new players in town, know what I'm saying? [PRO1_I:PROT1] What you need is a legitimate front Tommy, real estate. It's never done me no harm. [PRO1_J:PROT1] We need to start using some muscle or we can kiss all that hard work goodbye. [PRO1_K:PROT1] Local businesses know Diaz is dead, and they're refusing to pay protection! [PRO1_L:PROT1] Ooh! We could try bribery... [PRO1_M:PROT1] Bribery? Screw bribery! I'll show you how to make 'em scared! [PRO1_01:PROT1] ~g~Do a hit and run on the shop fronts and the owners will be begging for protection. [PRO1_03:PROT1] ~r~This was supposed to be a hit and run, not a 'hit and have coffee'. [PRO1_04:PROT1] My livelihood, destroyed! [PRO1_05:PROT1] Ruined...RUINED!! [PRO1_06:PROT1] I pay through the ass for protection! [PRO1_07:PROT1] My beautiful window display! [PRO1_08:PROT1] My store. My wonderful store. [PRO1_09:PROT1] Vercetti. Remember the name. [PRO1_10:PROT1] I run this town now. ME! [BUYP1:PROT1] You can now buy property in certain areas of the map. [BUYP2:PROT1] If you see a green house pickup, you can buy that property. [PRO1_N:PROT1] I'll be back here in five minutes... [PRO1_11:PROT1] ~g~Get to ~y~The North Point Mall~g~ in ~y~Vice Point~g~. [PRO1_12:PROT1] ~g~Smash the panes of glass in each shop front and the owners will be begging for new protection. [PRO1_A:PROT1] Oh, we gotta redecorate this place. We gotta make it look older. [PRO1_C:PROT1] You're my lawyer, Rosenberg, not my interior decorator. Got it? [BUYP3:PROT1] Stand inside the pickup, then press the ~h~~k~~PED_ANSWER_PHONE~~w~ button to purchase that property. [PRO1_13:PROT1] ~g~You have five minutes to smash them all. {=================================== MISSION TABLE PROT2 ===================================} [PRO2_A:PROT2] What's the problem? [PRO2_B:PROT2] Some bar is refusing to pay. [PRO2_C:PROT2] They reckon they're protected by a local gang of thugs. [PRO2_D:PROT2] But don't worry Tommy, I can handle it. [PRO2_E:PROT2] You call this handling it? [PRO2_F:PROT2] You two, off your asses... [PRO2_G:PROT2] Let's go. [PRO2_10:PROT2] ~g~Two more have made a run for it. Track them down and finish this. [PRO2_11:PROT2] Get in the car, useless. [PRO2_02:PROT2] Your protection needs a little more protection. [PRO2_03:PROT2] Aw hell, not again! I don't need this crap! [PRO2_04:PROT2] These idiots operate out of DBP Security around the block. [PRO2_05:PROT2] You guys just sort it out amongst yourselves. [PRO2_06:PROT2] I'll be seeing you later. [PRO2_07:PROT2] Yeah, yeah, whatever. [PRO2_09:PROT2] ~g~Go and speak to the Front Page Bar Owner. [PRO2_01:PROT2] ~g~Take out the guards protecting the Front Page Bar and find out who supplied them. [PRO2_08:PROT2] ~g~DBP Security will know you are on your way, go and get them before they clear out. {=================================== MISSION TABLE PROT3 ===================================} [PRO3_A:PROT3] You moron! What were you thinking?! [PRO3_B:PROT3] Do you realize what this means?! [PRO3_C:PROT3] We could all be sunk! [PRO3_D:PROT3] The timer must have got screwed. [PRO3_E:PROT3] That place was wired to go up like a firework factory. [PRO3_F:PROT3] Then somebody tipped off the cops... [PRO3_G:PROT3] What's the problem, fellas? [PRO3_H:PROT3] Mike was supposed to torch some place in the mall, [PRO3_I:PROT3] but he screwed the fuses and now the cops are crawling all over it. [PRO3_J:PROT3] We gotta get our stuff and get out of here! [PRO3_K:PROT3] Relax, both of you, let me think for a second! [PRO3_L:PROT3] Tommy Vercetti just doesn't cut and run. [PRO3_M:PROT3] The cops are gonna be going over that building with a fine toothed comb, right? [PRO3_N:PROT3] But that takes time. [PRO3_O:PROT3] We gotta go in and torch that place ourselves. [PRO3_P:PROT3] Yeah, but... [PRO3_Q:PROT3] No one but a cop could get within a mile of that place! [PRO3_R:PROT3] So we go as cops. [PRO3_S:PROT3] We gotta get uniforms - and we're gonna need a squad car. [PRO3_T:PROT3] All thanks to you Mike. [PRO3_U:PROT3] I'm sorry. [PRO3_V:PROT3] I got it. [PRO3_W:PROT3] What we got to do is lure the cops in with the finger, [PRO3_X:PROT3] put them in a lock-up [PRO3_Y:PROT3] and jump 'em. [PRO3_Z:PROT3] Good plan. Let's go! [PRO3_A1:PROT3] Alright. [PRO3_01:PROT3] Ok Lance, let's get the cops' attention! [PRO3_02:PROT3] ~g~ Take a cop car and go and plant the bomb at the Tarbrush Coffee Shop in the Mall. [PRO3_03:PROT3] ~g~ You've left Lance behind, go and get him. [PRO3_04:PROT3] ~g~ Let's go. [PRO3_05:PROT3] ~r~You killed Lance! [PRO3_07:PROT3] ~g~ You have blown your cover. Hurry up and plant the bomb! [PRO3_09:PROT3] Tie 'em up and gag 'em! [PRO3_10:PROT3] Ooo. Fits perfectly! [PRO3_11:PROT3] Bit tight around the crotch though... [PRO3_12:PROT3] Oh yeah yeah, mine too. Mine too. [PRO3_13:PROT3] Easy brother! No cop drives this bad! [PRO3_14:PROT3] Remember - smile at the other cops [PRO3_15:PROT3] Hey there officer. Nice badge, nice badge. [PRO3_16:PROT3] Real smooth, Lance. [PRO3_17:PROT3] Ok, timers are set, 5 seconds and ticking. [PRO3_18:PROT3] 5 seconds?!! We got to get the hell out of here! [PRO3_19:PROT3] Now that got them really irritated. [PRO3_20:PROT3] ~g~ Get two cops to follow you into the garage. [PRO3_21:PROT3] ~g~Get a wanted level so the cops will follow you into the lock-up. [PRO3_22:PROT3] ~g~The lock-up door is blocked! You need to clear the door so it can close. [PRO3_23:PROT3] ~g~Walk into the marker to plant the bomb. [PRO3_24:PROT3] ~g~Get clear of the Cafe! [PRO_AS1:PROT3] PROTECTION RING ASSET COMPLETED [PRO_AS2:PROT3] ~g~Vercetti Estate will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. [PRO3_08:PROT3] ~g~ Get back to ~h~Vercetti Estate~g~ on ~h~Starfish Island~g~. {=================================== MISSION TABLE RACES ===================================} [RACES_2:RACES] ~g~You need a vehicle to race, this is not a foot race! [RACES_3:RACES] 3..2..1.. GO GO GO! [RACES_8:RACES] ~r~You didn't win the race! [RACES00:RACES] Race ~1~: [RACES01:RACES] Terminal Velocity [RACES02:RACES] Ocean Drive [RACES03:RACES] Border Run [RACES04:RACES] Capital Cruise [RACES05:RACES] Tour! [RACES06:RACES] V.C. Endurance [RACES07:RACES] Entrance Fee: $~1~ [RACES08:RACES] Best Time: ~1~:~1~ [RACES09:RACES] Best Result: 1st [RACES10:RACES] Best Result: 2nd [RACES11:RACES] Best Result: 3rd [RACES12:RACES] Best Result: 4th [RACES13:RACES] Track Length: ~1~.~1~ km [RACES15:RACES] Best Time: NA [RACES16:RACES] Best Result: NA [RACES19:RACES] You cannot afford to enter this race. [RACES22:RACES] Best Time: ~1~:0~1~ [RACES23:RACES] Track Length: ~1~.~1~ miles [RACES_1:RACES] ~g~Get a fast vehicle and get to the starting grid. [RACEHLP:RACES] ~w~Press the~h~ ~k~~PED_SPRINT~ ~w~button to start the selected race. Press the~h~ ~k~~VEHICLE_ENTER_EXIT~ ~w~button to exit. {=================================== MISSION TABLE RCHELI1 ===================================} [WRECKED:RCHELI1] ~r~The vehicle is wrecked! [RCH1_4:RCHELI1] Checkpoints: [RCH1_7:RCHELI1] ~g~There are 20 checkpoints in total. [RCH1_12:RCHELI1] ~g~The RC helicopter is getting too far out of range! [RCH1_13:RCHELI1] ~r~The RC helicopter went out of range! [RCH1_8:RCHELI1] { reVC update } ~g~If you wish to quit this mission press the ~h~~k~~VEHICLE_FIREWEAPON~ ~g~button to detonate your RC Helicopter. {=================================== MISSION TABLE RCPLNE1 ===================================} [RCPL1_4:RCPLNE1] ~g~Compete in a CHECKPOINT RACE with 3 other RC Plane's [RCPL1_5:RCPLNE1] ~g~Fly through the checkpoints scattered throughout Vice City. [RCPL1_6:RCPLNE1] { reVC update } ~g~If you wish to quit this mission press the ~h~~k~~VEHICLE_FIREWEAPON~ ~g~button to detonate your RC Plane. [RCPL1_8:RCPLNE1] ~g~Your RC Plane is going out of range! [RCPL1_9:RCPLNE1] ~r~Your RC Plane went out of range! {=================================== MISSION TABLE RCRACE1 ===================================} [RCRC1_1:RCRACE1] ~g~Compete in a CHECKPOINT RACE with 3 other RC Bandits over 2 LAPS [RCRC1_3:RCRACE1] ~g~Final lap! [RCR1_4:RCRACE1] laps left: [RCR1_1:RCRACE1] ~g~Compete in a checkpoint race with 3 other RC Cars. [RCR1_2:RCRACE1] ~g~Be the first to complete two laps of the track to win! [RCR1_6:RCRACE1] ~g~Your RC Car is going out of range! [RCR1_7:RCRACE1] ~r~Your RC Car went out of range! {=================================== MISSION TABLE ROCK1 ===================================} [RBM1_A:ROCK1] AllllllllRrrighttt! [RBM1_B:ROCK1] Yessss! Brilliant, bloody brilliant! [RBM1_D:ROCK1] Hey, you ever met Love Fist before? [RBM1_E:ROCK1] No, I haven't but I've always loved your music. [RBM1_F:ROCK1] Let me introduce you to the band. [RBM1_G:ROCK1] This is Percy, Dick, and Willy's in the kaze, and that was Jezz in the booth earlier, [RBM1_H:ROCK1] and guys, I want you to meet a good friend of mine. [RBM1_I:ROCK1] This is Tommy. We go way back. [RBM1_J:ROCK1] All right, pal. [RBM1_K:ROCK1] And eh, what was your name again? [RBM1_L:ROCK1] Leave it out, Jezz you, remember - [RBM1_M:ROCK1] don't be playing them games with me, mate, [RBM1_N:ROCK1] I'm too crafty for that, sunshine! [RBM1_O:ROCK1] You see, the thing is, Tom, the boys need some help. [RBM1_P:ROCK1] They ain't too connected here, they don't have the old 'how's your father?' [RBM1_Q:ROCK1] We need some drugs, pal! [RBM1_R:ROCK1] Gonna get on the old Love Fist fury, you know?! [RBM1_S:ROCK1] Well, this is Vice City, man. What's the problem? [RBM1_U:ROCK1] Love Juice, man! [RBM1_V:ROCK1] Love Juice? [RBM1_W:ROCK1] Aye, two parts boomshine, 1 part trumpet, 5 fizz bombs and a liter of petrol. [RBM1_X:ROCK1] Can you help us out, pal? [RBM1_Y:ROCK1] Aw, it would really mean a lot to the boys. [RBM1_Z:ROCK1] You can do that for the boys, right? [RBM1_7:ROCK1] ~r~You did not get the Love Juice in time! [RBM1_8:ROCK1] ~r~Mercedes is dead! [RBM1_10:ROCK1] ~r~You idiot! You have destroyed the merchandise! [RBM1_13:ROCK1] ~g~Get the 'Love Juice' and Mercedes to the band before they are needed on stage. [RBM1_15:ROCK1] ~r~You have lost the dealer, our cash and the drugs! [RBM1_17:ROCK1] ~g~Kill the dealer and get the drugs! [MOB_07A:ROCK1] Hey mate, the guys could do with some company, if you know what I mean... [MOB_07B:ROCK1] I know just the girl. [ROK1_5:ROCK1] Hey, Mercedes! [ROK1_6:ROCK1] Hiya, Tommy. And how are you? [ROK1_7:ROCK1] Just fine. Listen, you fancy having Love Fist? [ROK1_8:ROCK1] Ok, but just as a favor I expect returned.. [RBM1_14:ROCK1] ~g~You need a car or a motorcycle! [RBM1_1:ROCK1] ~g~Go and collect Mercedes from her apartment. [RBM1_12:ROCK1] ~g~Go and collect the 'Love Juice' ingredients from the dealer. [ROK1_2:ROCK1] NO LONGER NEEDED [ROK1_3:ROCK1] NO LONGER NEEDED [MERC_39:ROCK1] I'll see you later, big boy. [RBM1_C:ROCK1] Hey, Tommy! Glad you could make it. [RBM1_9:ROCK1] ~g~Go and collect some love juice from the dealer for Love Fist! [ROK1_1A:ROCK1] Looking for something special? I got what you need! [ROK1_9:ROCK1] Thanks for the money, sucker! [RBM1_T:ROCK1] We need Love Juice, man, you know? {=================================== MISSION TABLE ROCK2 ===================================} [RBM2_A:ROCK2] Tommy, man. Am I glad to see you! [RBM2_B:ROCK2] What's going on? [RBM2_C:ROCK2] Bad vibes, Tommy.... [RBM2_E:ROCK2] There's this cat, we hardly know him, but he knows us. [RBM2_F:ROCK2] Like this cat. Knows all about us. [RBM2_G:ROCK2] Knows that Willy likes his ladies' underwear, eh! [RBM2_H:ROCK2] Or that Percy likes Duran Duran! [RBM2_K:ROCK2] Yeah, the love rocket thing, right. But listen, this cat... [RBM2_L:ROCK2] yeh, yeh, the guy, he wants Love Fist dead. [RBM2_M:ROCK2] Dead Tommy. [RBM2_N:ROCK2] Love Fist gone. You know what they say, the good die young. [RBM2_O:ROCK2] but Tommy, you gotta save Love Fist! [RBM2_P:ROCK2] We got a signing in two hours and I think... [RBM2_Q:ROCK2] And the boys think the stalker's gonna try some monkey business there. [RBM2_1:ROCK2] ~g~Drive the limo to the signing event and try to draw the psycho out. [RBM2_2:ROCK2] ~r~You've wrecked the band's car! [RBM2_3:ROCK2] ~g~Get to the signing! [RBM2_4:ROCK2] ~g~Get the Psycho! Don't let him escape! [RBM2_5:ROCK2] ~r~You lost him, you idiot! [RBM2_7:ROCK2] ~r~The fans have been attacked, the psycho won't show! [RBM2_8:ROCK2] ~r~The security guards have been attacked, the psycho won't show! [PSYCH_1:ROCK2] I'll see Love Fist burn! [PSYCH_2:ROCK2] Love Fist ruined my life! [RBM2_I:ROCK2] Shut up ye fool. Just 'cause Jezz bangs sheep. [RBM2_R:ROCK2] Oi shut it! [RBM2_D:ROCK2] Aye, I'm not joking, it's heavy stuff man, heavy you know? [RBM2_J:ROCK2] It's a love rocket thing, you know? {=================================== MISSION TABLE ROCK3 ===================================} [RBM3_A:ROCK3] Tommy! Tommy! Tommy, man, that psycho's back! [RBM3_B:ROCK3] What's going on? [RBM3_C:ROCK3] That psycho won't leave Love Fist alone! [RBM3_D:ROCK3] You didnae kill him man. And now he's back. [RBM3_E:ROCK3] Yeah, yeah, yeah, and the thing is... [RBM3_F:ROCK3] The thing is, we need someone to drive the limo we can trust, [RBM3_G:ROCK3] cause that nutter keeps making threats! [RBM3_I:ROCK3] We're all bricking ourselves, man. [RBM3_J:ROCK3] Okay guys, calm down, I'll handle this. [RBM3_K:ROCK3] Normally I wouldn't busy myself with driving around a bunch of drunken Scottish bisexuals, [RBM3_L:ROCK3] but, in your case I'll make an exception. [RBM3_4:ROCK3] ~r~You've killed Love Fist! [RBM3_6:ROCK3] DETONATION: [RBM3_1:ROCK3] ~g~Drive Love Fist to the venue. [RBM3_2:ROCK3] While the bomb is armed if you try to leave the car it will explode... [RBM3_3:ROCK3] If the detonation bar completely fills the bomb will explode. [RBM3_8:ROCK3] The faster you drive the lower the detonation bar will go. [RBM3_7:ROCK3] ~g~BOMB DEFUSED! [ROK3_62:ROCK3] so we thought we'd show you our Temple of Rock - [ROK3_63:ROCK3] Get a feel for that Love Fist fury! [ROK3_64:ROCK3] Listen to yourself, man. It's papier-mache and gaffa tape. [ROK3_65:ROCK3] Hey, to the kids, it's a temple and we are the priests! [ROK3_66:ROCK3] Aye, well, if the kids like their priests half cut and tone deaf, [ROK3_67:ROCK3] who am I to argue? [ROK3_68:ROCK3] Oh geez, the tape's getting chewed again. [ROK3_69:ROCK3] At this rate, we'll never get to play live. [ROK3_70:ROCK3] Oohh shite! My bowels... [ROK3_73:ROCK3] Jezz is running the tape, [RBM3_9:ROCK3] If you are stopped or drive slowly the detonation bar will increase. [RBM3_H:ROCK3] I'm shitin' masel' man. I need ma ma! [ROK3_71:ROCK3] We gotta get on with it - thanks again Tommy, Know what I am saying, nice one, bye! [ROK3_1:ROCK3] At last man, time for a well earned drink. The venue's just a hundred yards down the road. [ROK3_2:ROCK3] Better make it a large one then. Hey Tommy, change the tunes, man. [ROK3_3:ROCK3] I get confused if my head ain't banging. Ah look, what's this? Hey Tommy, stick this tape on. [ROK3_4:ROCK3] Love Fist. Your time polluting the airwaves is over. I gave you the chance to be friends. [ROK3_5:ROCK3] Now, I'm giving you the chance to die. Try to slow down and your limousine will explode, along with your BIG, HAIRY ARSES! [ROK3_6:ROCK3] Tommy pal, you gotta save the band! I'm getting bored of this. Just keep the pedal to the metal!! [ROK3_7:ROCK3] We gotta find the bomb! Can't we just drive around all day? Aye, we've got plenty to drink.. [ROK3_8:ROCK3] Won't the bomb not be in the engine? We'll have to stop to get it. We're all going to die! I'm gonna get drunk! [ROK3_9:ROCK3] Hey, there's a queue here pal! The answer ain't in the drinks cabinet! Get out of my way! [ROK3_10:ROCK3] Hey, the vodka bottle's got wires coming out of it! That's not vodka, that's BOOMSHINE! [ROK3_11:ROCK3] WAAAAAAGGGHHHH!!!! And it's wired to blow!! WAAAAAAAAAAAAGGGHHHHHHHH!!!! [ROK3_12:ROCK3] They always said the drink would kill me. I've seen this on the telly. you gotta pull out one of the wires. Which wire? I don't know, man. [ROK3_13:ROCK3] I don't have a clue. Willy, say something. I'm gonna play bass in hell. [ROK3_14:ROCK3] Tommy man, keep driving fast, pal. Somebody do something. Aye, clever! [ROK3_15:ROCK3] 'Somebody do something', what kind of crap is that, I've seen braver girls. Okay tough guy, you do something. [ROK3_16:ROCK3] Look, man, I play a musical instrument I don't have a clue about bomb disposal. Willy could just suck the boomshine out with a straw. [ROK3_17:ROCK3] Aye, I've heard that your good at that kind of thing. Hey, I was off my tits that night, as well you know! [ROK3_18:ROCK3] Just pass Willy a straw! A straw?!?! This is the Love Fist Tour Bus! [ROK3_19:ROCK3] Where am I gonna get a straw from, know wot I mean? Which wire, Tommy? The green one. There isn't a green one. [ROK3_20:ROCK3] Or is this one green? Any of these wires look green to you? [ROK3_21:ROCK3] Oh no! Death's on the cards! Everything looks green! I should have dumped you lot when I had the chance man. [ROK3_22:ROCK3] Glory seeker. Capitalist. I've been carrying you for years. Shut up. You're a muppit. [ROK3_23:ROCK3] A big screaming girl. Yeah. Shut up and pull a wire. Which wire? This one.. [ROK3_24:ROCK3] NO! Man, we're okay. We ain't been blown up, pal. [ROK3_25:ROCK3] Tommy, man, nice one. Rock and roll, man. Ain't we got a gig to go to? [ROK3_26:ROCK3] A racket to make? Groupies to abuse? LOVE FIST! [ROK3_27:ROCK3] Have you finished with that bottle? {=================================== MISSION TABLE SERG1 ===================================} [TEX1_A:SERG1] Come in and park yourself on the hide, son. [TEX1_B:SERG1] Hell, my daddy used to say, never look a gift horse in the mouth, and by golly, he never did. [TEX1_C:SERG1] Would you like a drop of the old Kentucky? [TEX1_D:SERG1] No thanks. [TEX1_E:SERG1] A clean thinker! I like that. [TEX1_F:SERG1] Now, the property business isn't all about high-falootin' paper pushing. [TEX1_G:SERG1] It's about dirt! And the will to claim that dirt! You with me, son? [TEX1_H:SERG1] Oh yeah. [TEX1_K:SERG1] Persuasion's my forte. [TEX1_L:SERG1] Yeh, he'll be down at the country club, down on the golf course. [TEX1_M:SERG1] They don't allow guns, so his bodyguards won't be packing lawgivers. [TEX1_N:SERG1] Go beat eight tons of crap out of him. [TEX1_O:SERG1] Here now - I got you a membership, and boy you're going to need more appropriate clothing. [TEX1_2:SERG1] ~g~Now head to the Leaf Links Golf Club. [TEX1_3:SERG1] Who's this guy? Boys, deal with him. [TEX1_6:SERG1] Nice ass baby! [TEX1_7:SERG1] Is this me? [TEX1_I:SERG1] Well, I need some tenacious bastard to let go of some dirt, [TEX1_J:SERG1] and you look to me like the kind of guy to persuade him. [TEX1_0:SERG1] ~g~The target is at the driving range enjoying a game of golf. Make sure it's his last. [TEX1_8:SERG1] Each time you enter a caddy you automatically receive a golf club, providing your melee weapon slot is empty. [TEX1_9:SERG1] Get him! [TEX1_10:SERG1] Kill that Psycho! [TEX1_1:SERG1] ~g~Go and pick up some golfing clothes from Jocksports. {=================================== MISSION TABLE SERG2 ===================================} [TEX_2A:SERG2] ~g~Excellent! They've spotted you! [TEX_2B:SERG2] ~r~Fool! People have to WITNESS a Cuban doing the hit! [TEX_2C:SERG2] ~g~Go get yourself some Cuban gang colors in Little Havana! [TEX_2D:SERG2] ~g~Take out the Haitian Gang Lord at Romero's Funeral Parlor! [TEX2_A:SERG2] Tommy, this is Donald Love. Donald, this here is Tommy Vercetti, [TEX2_B:SERG2] the latest gunslinger to come to these parts. [TEX2_C:SERG2] Yeh...uh... [TEX2_D:SERG2] Donald, you just shut up and listen, and you might learn something. [TEX2_E:SERG2] Now, nothing brings down real estate prices quicker than a good old-fashioned gang war - [TEX2_F:SERG2] 'cept maybe a disaster, like a biblical plague or something, [TEX2_G:SERG2] but, that may be going too far in this case. [TEX2_H:SERG2] You getting this down, you four-eyed prick? [TEX2_I:SERG2] Now recently a Haitian gang lord died. Apparently the Cubans did it, nobody's certain. [TEX2_J:SERG2] But let's make them certain! You disguise yourself as a Cuban hombre, [TEX2_K:SERG2] and head on down to crash that funeral. Mix it up, and then high tail it. [TEX2_L:SERG2] You getting this down, Donald? [TEX2_M:SERG2] Well, that ought to put the coyote in the chicken coop, huh? [TEX2_N:SERG2] And then we'll just sit back, and watch the prices tumble. [TEXEXIT:SERG2] ~g~Now get out of Little Haiti! {=================================== MISSION TABLE SERG3 ===================================} [TEX3_A:SERG3] Now look here, son. I got a problem and I reckon you could help me with it. [TEX3_B:SERG3] I'm no builder. [TEX3_C:SERG3] No, I was thinking more of your demolition skills. [TEX3_D:SERG3] Now this here, this is the development as planned and this, [TEX3_E:SERG3] this is the property that we're looking at. [TEX3_F:SERG3] You're trying to say this new office block is kind of in the way. [TEX3_G:SERG3] You catch on quick. [TEX3_H:SERG3] Now I'm going to head out of town for a while [TEX3_I:SERG3] and if that office development were to face sudden and insurmountable structural problems, then I.. [TEX3_J:SERG3] As a civil minded individual you'd feel obliged to step in and [TEX3_K:SERG3] save the rejuvenation of an important area of the city? [TEX3_L:SERG3] Where can I get more guys like you? [TEX3_1:SERG3] ~g~Use the RC helicopter to transport bombs to four demolition points on the building site. [TEX3_2:SERG3] ~g~You must place one bomb at each target. You can place bombs in any order. [TEX3_3:SERG3] ~g~Maneuver the RC helicopter next to a bomb to pick it up. it can carry one bomb at a time.. [TEX3_5:SERG3] ~g~If you place a bomb unsuccessfully you can pick it up and try again. [TEX3_7:SERG3] ~g~You must then place the remaining bombs in 7 minutes! [TEX3_8:SERG3] ~g~You missed the target! Pick up a bomb and try again! [TEX3_10:SERG3] ~g~Drop the bomb at a target. [TEX3_11:SERG3] targets left: [TEX3_17:SERG3] ~r~You ran out of time! [TEX3_18:SERG3] ~r~Your RC Helicopter has been destroyed! [TEX3_19:SERG3] ~r~You dropped your bomb in the water! That ain't no way to fish! [TEX3_20:SERG3] ~r~The RC Helicopter is nearly out of range! [TEX3_21:SERG3] ~r~The RC Helicopter went out of range! [TEX3_24:SERG3] Press the ~h~~k~~VEHICLE_LOOKLEFT~ ~w~button to rotate the helicopter counter-clockwise. [TEX3_25:SERG3] Press the ~h~~k~~VEHICLE_LOOKRIGHT~ ~w~button to rotate the helicopter clockwise. [TEX3_27:SERG3] ~g~A central stairway allows access to all the floors in the building. [TEX3_31:SERG3] ~r~You destroyed the TOPFUN van that contained the bombs and RC helicopter! [TEX3_32:SERG3] You can ~h~look behind~w~ by simultaneously pressing the ~h~~k~~VEHICLE_LOOKLEFT~~w~ and the ~h~~k~~VEHICLE_LOOKRIGHT~~w~ buttons. [TEX3_4:SERG3] { reVC update } ~g~To drop a bomb press the~h~ ~k~~VEHICLE_FIREWEAPON~ ~g~button. [TEX3_29:SERG3] { reVC update } To drop a bomb press the~h~ ~k~~VEHICLE_FIREWEAPON~ ~w~button. [TEX3_26:SERG3] Pressing the ~h~~k~~VEHICLE_BRAKE~ ~w~button ~w~decreases the rotor speed, causing the helicopter to~h~ descend. [TEX3_22:SERG3] Pressing the ~h~~k~~VEHICLE_ACCELERATE~ ~w~button increases the rotor speed, causing the helicopter to ~h~ascend. [TEX3_16:SERG3] ~g~Get to the ~w~TOPFUN ~g~van near the building site to be demolished. [TEX3_33:SERG3] Once you pick up a bomb the radar will show you the position of the target relative to the RC helicopter. [TEX3_34:SERG3] An ~h~upwards pointing triangular blip ~w~indicates the target is ~h~above ~w~the RC helicopter. [TEX3_35:SERG3] A ~h~downward pointing triangular blip ~w~indicates the target is ~h~below ~w~the RC helicopter. [TEX3_36:SERG3] A ~h~square blip ~w~indicates the target is on the ~h~same level ~w~as the RC helicopter [TEX3_6:SERG3] ~g~Once you have picked up a bomb for the first time, the detonation timer will start. [TEX3_28:SERG3] To ~h~pick up a bomb~w~, simply maneuver the RC helicopter next to it. The RC Helicopter can carry one bomb at a time. [TEX3_30:SERG3] ~g~To pick up a bomb, simply maneuver the RC helicopter next to it. The RC Helicopter can carry one bomb at a time. [TEX3_12:SERG3] ~g~Bomb planted! Only 3 more targets to go! Go back and get another bomb. [TEX3_13:SERG3] ~g~Bomb planted! Only 2 more targets to go! Go back and get another bomb. [TEX3_14:SERG3] ~g~Bomb planted! Only 1 more target to go! Go back and get another bomb. [TEX3_15:SERG3] ~r~Detonation timer initiated! ~g~You must plant the ~w~4 bombs ~g~in the remaining time! [TEX3_37:SERG3] Pushing ~h~back on the analog stick ~w~decreases the rotor speed, causing the helicopter to~h~ descend. [TEX3_38:SERG3] { reVC update } { Pressing the ~h~~k~~VEHICLE_ACCELERATE~ ~w~button increases the rotor speed, causing the helicopter to ~h~ascend. } Pushing ~h~forward on the analog stick ~w~increases the rotor speed, causing the helicopter to ~h~ascend. [TEX3_39:SERG3] ~g~To drop a bomb press the ~h~~k~~VEHICLE_HANDBRAKE~ ~g~button. [TEX3_40:SERG3] To drop a bomb press the ~h~~k~~VEHICLE_HANDBRAKE~ ~w~button. [TEX3_23:SERG3] Press the ~h~~k~~VEHICLE_TURRETUP~~w~ and ~h~~k~~VEHICLE_TURRETDOWN~~w~ buttons to tilt the helicopter in the direction you wish to maneuver it. {=================================== MISSION TABLE TAXI1 ===================================} [FARES:TAXI1] FARES: [TAXI1:TAXI1] ~g~Look for a fare. [TSCORE2:TAXI1] $~1~ [IN_ROW:TAXI1] ~1~ IN A ROW bonus! $~1~ [TAXI3:TAXI1] ~r~Your passenger fled in terror! [TAXI7:TAXI1] ~r~Your car is trashed, get it repaired. [TAXI4:TAXI1] Fare complete! [TAXI5:TAXI1] SPEED BONUS!! [TAXI6:TAXI1] Taxi mission over [TAXIH1:TAXI1] Stop near a highlighted pedestrian to pick them up then drive them to their destination before the time runs out. [FARE1:TAXI1] ~g~Destination ~w~'The Pole Position Club' ~g~in Ocean Beach. [FARE3:TAXI1] ~g~Destination ~w~'The Marina' ~g~in Ocean Beach. [FARE4:TAXI1] ~g~Destination ~w~'Ammu-Nation' ~g~in Ocean Beach. [FARE5:TAXI1] ~g~Destination ~w~'The Hardware store' ~g~in Washington Beach. [FARE6:TAXI1] ~g~Destination ~w~'The North Point Mall' ~g~in Vice Point. [MFARE1:TAXI1] ~g~Destination ~w~'Ammu-Nation' ~g~in Downtown. [MFARE2:TAXI1] ~g~Destination ~w~'the Terminal' ~g~at Escobar International Airport. [WFARE3:TAXI1] ~g~Destination ~w~'Sunshine Autos' ~g~in Little Havana. [WFARE4:TAXI1] ~g~Destination ~w~'Kaufman Cabs' ~g~in Little Haiti. [WFARE5:TAXI1] ~g~Destination ~w~'The Hardware store' ~g~in Little Havana. [WFARE6:TAXI1] ~g~Destination ~w~'Howlin Petes Bike Emporium' ~g~in Downtown. [FARE7:TAXI1] ~g~Destination ~w~'The Jewelers' ~g~in Vice Point. [FARE8:TAXI1] ~g~Destination ~w~'The Beach' ~g~in Ocean Beach. [FARE9:TAXI1] ~g~Destination ~w~'The Beach' ~g~in Washington Beach. [FARE10:TAXI1] ~g~Destination ~w~'The Beach' ~g~in Vice Point. [FARE11:TAXI1] ~g~Destination ~w~'Hospital' ~g~in Ocean Beach. [FARE12:TAXI1] ~g~Destination ~w~'Hospital' ~g~in Vice Point. [FARE13:TAXI1] ~g~Destination ~w~'Police Station' ~g~in Washington Beach. [FARE14:TAXI1] ~g~Destination ~w~'Police Station' ~g~in Vice Point. [FARE15:TAXI1] ~g~Destination ~w~'Pizza Restaurant' ~g~in Vice Point. [WFARE7:TAXI1] ~g~Destination ~w~'Police Station' ~g~in Little Havana. [WFARE8:TAXI1] ~g~Destination ~w~'Police Station' ~g~in Downtown. [WFARE9:TAXI1] ~g~Destination ~w~'Hospital' ~g~in Downtown. [WFARE10:TAXI1] ~g~Destination ~w~'Hospital' ~g~in Little Havana. [WFARE11:TAXI1] ~g~Destination ~w~'The Stadium' ~g~in Downtown. [WFARE12:TAXI1] ~g~Destination ~w~'Pizza Restaurant' ~g~in Little Haiti. [WFARE13:TAXI1] ~g~Destination ~w~'Pizza Restaurant' ~g~in Downtown. [WFARE14:TAXI1] ~g~Destination ~w~'Docks' ~g~in Viceport. [WFARE15:TAXI1] ~g~Destination ~w~'Chemist' ~g~in Little Haiti. [FARE2:TAXI1] ~g~Destination ~w~'The Malibu club' ~g~in Vice Point. {=================================== MISSION TABLE TAXICUT ===================================} [TAXC_A:TAXICUT] Guess you're the new owner. [TAXC_B:TAXICUT] What are you, mob? Cartel? You don't look Mexican... [TAXC_C:TAXICUT] Anyhoo, I guess you better get on with the 'things are gonna change around here' crap, [TAXC_D:TAXICUT] maybe threaten one of the drivers - [TAXC_E:TAXICUT] go steady on Ted over there, he's just had his hernia fixed. [TAXC_F:TAXICUT] Well, yeah. Things are going to change around here, lady. [TAXC_G:TAXICUT] Oh crap, sonny. Might as well leave this to me - [TAXC_H:TAXICUT] I've been doing this for years. [TAXC_I:TAXICUT] Now hear this. [TAXC_J:TAXICUT] We are now under new management and things are going to change around here again. [TAXC_K:TAXICUT] Our new management, the [TAXC_L:TAXICUT] - Which gang are you? [TAXC_M:TAXICUT] Well, I'm not part of any gang actually. [TAXC_N:TAXICUT] What's your goddamned name, kid? [TAXC_O:TAXICUT] Vercetti, Tommy Vercetti. [TAXC_P:TAXICUT] Our new management, the Vercetti Gang, [TAXC_Q:TAXICUT] is gonna make sure we get no trouble. [TAXC_R:TAXICUT] Capiche? Out! [TAXC_S:TAXICUT] Did you like the 'capiche'? I liked the 'capiche'. [TAXC_T:TAXICUT] So this is how it's worked in the past, [TAXC_U:TAXICUT] We run the firm as usual. [TAXC_V:TAXICUT] If we get any trouble from rival firms, you beat the crap out of them. [TAXC_W:TAXICUT] Then they beat the crap out of us, [TAXC_X:TAXICUT] then you beat the crap out of them, [TAXC_Y:TAXICUT] etcetera, etcetera. You got it? [TAXC_Z:TAXICUT] Uh, yeah, I guess... [TAXC_A1:TAXICUT] Just grab a taxi from the garage if you feel like jumping in. {=================================== MISSION TABLE TAXIWA1 ===================================} [OUTTIME:TAXIWA1] ~r~Too slow, man, too slow! [TAX1_1:TAXIWA1] Ok, we got a high class fare needs picking up from Starfish island - any takers? [TAX1_2:TAXIWA1] Tommy here, I'll take it! [TAX1_3:TAXIWA1] This is my fare, back off asshole! [TAX1_4:TAXIWA1] Come on come on, Get in, quick! [TAX1_5:TAXIWA1] Ok, ok! Just don't hurt me! [TAXW1_1:TAXIWA1] ~g~Pick up the V.I.P. on Starfish Island. [TAXW1_2:TAXIWA1] ~g~Get the V.I.P back! Take the other car out! [TAXW1_3:TAXIWA1] ~r~The V.I.P. is dead! [TAXW1_4:TAXIWA1] ~r~The V.I.P. has been dropped off! [TAXW1_6:TAXIWA1] ~g~Take the V.I.P. to the airport! {=================================== MISSION TABLE TAXIWA2 ===================================} [TAX2_1:TAXIWA2] Calling all cars, we're losing fares all over town. What's with you guys? [TAX2_2:TAXIWA2] VC Cabs keep beating us to it. They've just got too many cars - we can't compete! [TAX2_3:TAXIWA2] Mr. Vercetti, if you're out there listening in, you gotta put some VC Cabs out of action before we go bust! [TAXW2_1:TAXIWA2] ~g~Destroy 3 of the rival taxis! {=================================== MISSION TABLE TAXIWA3 ===================================} [TAX3_1:TAXIWA3] Car 13, We got a Miss Cortez, asked for you especially. [TAX3_2:TAXIWA3] Ok, I got it. Car 13 out! [TAX3_3:TAXIWA3] Hmmmm, no sign of Mercedes... [TAXW3_3:TAXIWA3] ~g~Take out the leader cab! [TAXW3_2:TAXIWA3] ~g~Stay alive until the timer runs out. [TAX_AS1:TAXIWA3] TAXI FIRM ASSET COMPLETED [TAX_AS2:TAXIWA3] ~g~Kaufman Cabs will now generate revenue up to a maximum of $~1~. Make sure you collect it regularly. [TAX3_4:TAXIWA3] It's time for Kaufman Cab's guardian angel to eat some fender! [TAX3_5:TAXIWA3] Hey boy I'm gonna tan your hide! { reVC updates } { new languages } [FEL_JAP] JAPANESE [FEL_POL] POLISH [FEL_RUS] RUSSIAN { new display menus } [FET_GFX] GRAPHICS SETUP [FED_MIP] MIP MAPPING [FED_AAS] ANTI ALIASING [FED_FIL] TEXTURE FILTERING [FED_BIL] BILINEAR [FED_TRL] TRILINEAR [FED_WND] WINDOWED [FED_FLS] FULLSCREEN [FEM_CSB] CUTSCENE BORDERS [FEM_SCF] SCREEN FORMAT [FEM_ISL] MAP MEMORY USAGE [FEM_LOW] LOW [FEM_MED] MEDIUM [FEM_HIG] HIGH [FEM_2PR] PS2 ALPHA TEST [FEC_FRC] FREE CAM { Linux joy detection } [FEC_JOD] DETECT JOYSTICK [FEC_JPR] Press any key on the joystick of your choice that you want to use on the game, and it will be selected. [FEC_JDE] Detected joystick { mission restart } [FET_RMS] REPLAY MISSION [FESZ_RM] RETRY? [FED_VPL] VEHICLE PIPELINE [FED_PRM] PED RIM LIGHT [FED_RGL] ROAD GLOSS [FED_CLF] COLOUR FILTER [FED_WLM] WORLD LIGHTMAPS [FED_MBL] MOTION BLUR [FEM_SIM] SIMPLE [FEM_NRM] NORMAL [FEM_MOB] MOBILE [FED_MFX] MATFX [FED_NEO] NEO [FEM_PS2] PS2 [FEM_XBX] XBOX [FEC_IVP] INVERT PAD VERTICALLY [FEM_NON] NONE [FEC_DS2] DUALSHOCK 2 [FEC_DS3] DUALSHOCK 3 [FEC_DS4] DUALSHOCK 4 [FEC_360] XBOX 360 CONTROLLER [FEC_ONE] XBOX ONE CONTROLLER [FEC_TYP] GAMEPAD TYPE [FET_AGS] GAMEPAD SETTINGS [FEM_AUT] { aspect ratio related } AUTO { end of file } [DUMMY] THIS LABEL NEEDS TO BE HERE !!! AS THE LAST LABEL DOES NOT GET COMPILED ================================================ FILE: utils/gxt/build.bat ================================================ gxt -g VC -i "american.txt" -o "../../gamefiles/TEXT/american.gxt" gxt -g VC -i "french.txt" -o "../../gamefiles/TEXT/french.gxt" gxt -g VC -i "german.txt" -o "../../gamefiles/TEXT/german.gxt" gxt -g VC -i "italian.txt" -o "../../gamefiles/TEXT/italian.gxt" gxt -g VC -i "spanish.txt" -o "../../gamefiles/TEXT/spanish.gxt" ================================================ FILE: utils/gxt/french.txt ================================================ { New strings are at the bottom of file. Do not change the order of strings. You can fix the typos of existing translation but please refrain from unnecessary edits like rephasing because you think it suits better for your taste. } [RAMPAGE] RODEO! [RAMP_F] ECHEC DU RODEO! [RAMP_P] RODEO REUSSI! [RAMP_A] TOUS LES RODEOS REUSSIS! [PAGE_01] Tue ~1~ membres de gang en 2 minutes! [PAGE_02] Détruis ~1~ véhicules en 2 minutes! [PAGE_03] Roule à côte des membres de gang et tue-en ~1~ en 2 minutes! [PAGE_04] Ecrase ~1~ membres de gang en 2 minutes! [PAGE_05] Eclate la tête de ~1~ membres de gang en 2 minutes! [FEC_ABR] Accélérer, freiner ou marche arrière [CLOHELP] Vêtements changés! [FE_MLG] LEGENDE DE LA CARTE [FED_RDR] RADAR [FED_HUD] L'INTERFACE [FED_RDL] GRAND [FED_RDB] BIPS UNIQUEMENT [FED_HUF] FONDU [FEI_BTU] ; = - [FEI_SCR] Fait défiler [FEST_HV] Missions d'Autodéfense, niveau maximum [LG_01] Position du joueur [LG_02] Avery Carrington [LG_03] Motards [LG_04] Colonel Cortez [LG_05] Ricardo Diaz [LG_06] Kent Paul [LG_07] Avocat [LG_08] Phil Cassidy [LG_09] Chantier naval [LG_10] Club Malibu [LG_11] Cubains [LG_12] Studio de Cinéma [LG_13] Ammu-Nation [LG_14] Haïtiens [LG_15] Quincaillerie [LG_16] Planque [LG_17] Crèmes glacées [LG_18] Taxi Kaufman [LG_19] Love Fist [LG_20] Imprimerie [LG_21] Propriété [LG_22] Pay 'n' Spray [LG_23] Boutique de fringues [LG_24] Manoir de Tommy [LG_25] Téléphone [LG_26] Station de radio Wildstyle [LG_27] Station de radio Flash FM [LG_28] Station de radio KChat [LG_29] Station de radio Fever 105 [LG_30] Station de radio VRock [LG_31] Station de radio VCPR [LG_32] Station de radio Espantoso [LG_33] Station de radio Emotion 98.3 [LG_34] Station de radio Onde 103 [LG_35] Destination [LG_36] Sun Yard [LG_37] Boîte de striptease [MAP_YAH] VOUS ETES ICI [MAP_LEG] Légende [SENTXS] Sentinelle XS [VCNMAV] VCN Maverick [TAXSHRT] ~g~Au lieu de conduire, tu peux te servir de ce taxi Kaufman pour te déplacer. Cela te coûtera $9. [BRIBE1] Tu viens de récupérer un pot-de-vin de la police. Cela réduira ton indice de recherche d'une étoile. [SUNSHIN] Sunshine Autos [KAUFCAB] Taxis Kaufman [BOATYAR] Le chantier naval [WANT_L] Tu as perdu ton indice de recherche. Ne commets pas de délit tant que les étoiles brillent ou tu seras de nouveau très recherché. [HOTRNG] HOTRING [BLODRNG] BLOODRING [DIRTRNG] DIRTRING [IN_VEH] ~g~Eh! Remonte dans ta caisse! [HEY] ~g~Te la joue pas perso, reste avec tes potes! [HELP3] T'es vite crevé, ton sprint ne dure donc pas longtemps. [HELP4_D] Pousse le ~h~joystick analogique droit ~w~ vers le haut pour ~h~accélérer. [HELP5_D] Tire le ~h~joystick analogique droit~w~ vers toi pour ~h~freiner~w~ ou pour ~h~reculer~w~ si le véhicule est à l'arrêt. [HELP7_A] Maintiens la ~h~ ~k~~PED_LOCK_TARGET~ ~w~ enfoncée pour ~h~viser~w~ avec le fusil à lunette. [HELP7_D] Maintiens la ~h~ ~k~~PED_LOCK_TARGET~ ~w~ enfoncée pour ~h~viser~w~ avec le fusil à lunette. [HELP10] Ce badge t'indique que t'es recherché par la police. [HELP11] Plus t'as de badges, plus ton indice de recherche est important. [HELP13] Tu as parfois intérêt à emprunter des chemins non indiqués sur le radar. [TIMER] Cette mission est en temps limité. Tu dois donc la réussir avant que le compteur n'atteigne zéro. [HORN] ~g~Klaxonne! [NOMONEY] ~g~T'as pas assez de cash! [REWARD] RECOMPENSE $~1~ [M_FAIL] ECHEC DE LA MISSION! [M_PASS] MISSION ACCOMPLIE! $~1~ [DEAD] T'ES MORT! [BUSTED] CHOPE! [WEATHE1] TEMPS ENSOLEILLE [WEATHE2] TEMPS TRES ENSOLEILLE [WEATHE3] TEMPS NUAGEUX [WEATHE4] TEMPS PLUVIEUX [WEATHE5] TEMPS BRUMEUX [WEATHE6] TEMPS NORMAL [NUMBER] ~1~ [LOADCAR] CHARGEMENT DU VEHICULE... (APPUIE SUR F1 POUR ANNULER) [CARSOFF] Trafic désactivé. [CARS_ON] Trafic activé. [TEXTXYZ] Ecriture des coordonnées sur le fichier... [CHEATON] Mode triche activé [CHEATOF] Mode triche désactivé [IMPORT1] Va dehors et attends ton véhicule. [PAGEB11] Lance-flammes livré à la planque [WANT_A] Tu ne seras arrêté que si tu as un ~h~indice de recherche. [WANT_B] La rangée d'étoiles dans le coin supérieur droit de l'écran symbolise ton ~h~indice de recherche~w~. [WANT_C] Ton ~h~indice de recherche~w~ est désormais de un... [WANT_D] deux... [WANT_E] trois... [WANT_F] Plus ton ~h~indice de recherche~w~ augmente, plus les forces de l'ordre t'en veulent. [WANT_G] Quand tu es ~h~'chopé'~w~, tu es amené au poste de police le plus proche. [WANT_H] Les flics se laisseront corrompre contre toutes tes armes et une partie de ton cash. [WANT_I] Toutes les missions en cours échoueront. [WANT_J] Plus tu joueras, plus tu trouveras de moyens de diminuer ton indice de recherche. [WANT_K] En voiture, les ~h~ATELIERS DE PEINTURE~w~ te permettront d'effacer ton indice de recherche. [HEAL_B] Quand tu es ~h~'H.S'~w~, t'es amené à l'hôpital le plus proche. [HEAL_C] On te confisque alors toute ton artillerie et les docteurs prennent ton argent pour te remettre en forme. [HEAL_E] En jouant, tu trouveras d'autres moyens de te soigner ou de te protéger. [SAVE1] Entre dans la corona pour ~h~sauvegarder la partie~w~. Tu ne peux pas sauvegarder en cours de mission. [SAVE2] Tout véhicule laissé dans ce garage sera enregistré lors de la sauvegarde. [AMMU] Va chez Ammu-Nation pour acheter une arme. [R_TIME] TEMPS DE COURSE : [PROP_1] Tu n'as pas assez de cash pour acheter cette propriété [PROP_2] Tu ne peux pas acheter une propriété en cours de mission [IND_ZON] Vice City Beach [COM_ZON] Partie centrale de Vice City [BEACH1] Ocean Beach [BEACH2] Washington Beach [BEACH3] Vice Point [GOLFC] Leaf Links [STARI] Starfish Island [DOCKS] Port de Vice [HAVANA] Little Havana [HAITI] Little Haiti [PORNI] Prawn Island [DTOWN] Centre [VICE_C] Vice City [A_PORT] Aéroport Escobar [JUNKY] Décharge [PISTOL] Pistolet [PYTHON] .357 [UZI] Uz-1 [TEC9] Tec 9 [M4] M4 [INGRAM] Mac [MP5] MP [RUGER] Kruger [SNIPE] Fusil à lunette [GRENADE] Grenades [SHOTGN1] Fusil à pompe [SHOTGN2] S.P.A.S. 12 [SHOTGN3] Fusil à pompe Stubby [ARMOUR] Gilet pare-balles [LASER] .308 Lunette [BASEBAT] Batte de baseball [HAMMER] Marteau [SCREWD] Tournevis [CLEVER] Couperet [MACHETE] Machette [KNIFE] Couteau [KATANA] Katana [CHAINSA] Tronçonneuse [G_COST] $~1~ [CAR_1] Ambulance [MALIBU] Malibu Club [MANSION] La résidence de Diaz [TMANS] Chez Tommy [STRIP] 'Pole Position Club' [MALL1] Centre commercial de North Point [BANKINT] El Banco Corrupto Grande [RANGE] Champ de tir [POL_HQ] QG de Vice City Police [INT_B] Un vieil ami [INTB_1] ~g~Va au bureau de l'avocat. [LAW_1] La Fête [LAW_2] Baston de Rue [LAW_3] Jury sous pression [LAW_4] Emeute [COL_1] Salaud de traître [COL_2] Fusillade [COL_3] Anges gardiens [COL_4] Chef, oui, chef ! [COL_5] Sur le Pont [COK_1] Course poursuite [COK_2] Phnom Penh '86 [COK_3] Speedboat [COK_4] Offre & demande [KENT_1] Couloir de la mort [ASS_1] Liquidation [BUD_1] EXTORSION [BUD_2] Rixe de Bar [BUD_3] CopLand [CAP_1] L'encaisseur [FIN_1] Amis proches [BANK_1] Sans Issue [BANK_2] Le Flingueur [BANK_3] Le Chauffeur [BANK_4] Hold-up [CNT_1] La Mèche est vendue [CNT_2] Halte au Messager [PORN_1] Bout d'essai [PORN_2] Dodo Vibro [PORN_3] Martha [PORN_4] Projecteur-G [TAX_1] Taxis Kaufman [TAXI_1] V.I.P. [TAXI_2] Concurrence Amicale [TAXI_3] Cabmaggedon [ICE_1] Distribution [TEX_1] Quatre fers [TEX_2] Funérailles [TEX_3] Demolition Man [PHIL_1] Trafiquant d'armes [PHIL_2] Boomshine Saigon [BIKE_1] Alliage d'Acier [BIKE_2] Colère [BIKE_3] Bécane [ROCK_1] Love Juice [ROCK_2] Psychopathe [ROCK_3] Promo [ROCK_4] Les Love Fist! [HAT_1] Juju [HAT_2] Bombes! [HAT_3] Raclée [CUB_1] Cascades aquatiques [CUB_2] Chair à Canon [CUB_3] Bataille Navale [CUB_4] Vaudou [JOB_1] Accident de la route [JOB_2] Bute la femme! [JOB_3] Autocide [JOB_4] Enregistrement [JOB_5] Détails [ANSWER] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour répondre au téléphone. [MOB_01A] Comment ça va, mon pote? C'est Paul! J'ai peut-être quelque chose d'intéressant pour toi, mais faut que je te parle en personne. [MOB_01B] Je suis en train de me la couler douce au Malibu. [MOB_01C] Tu me devras un ou deux petits services en échange, mon mignon. A tout à l'heure... [MOB_02A] Sniiiiiiiiiiiif! Hé, salut, Tommy! Tommy? [MOB_02B] On a un problème à l'imprimerie. Tu ferais mieux d'aller y jeter un coup d'oeil. [MOB_02C] Quelque chose a merdé. C'est le bordel. Faut que j'te laisse. [MOB_03A] Mr Vercetti? J'ai ici un document signé qui établit formellement [MOB_03B] que vous avez racheté toutes les dettes de BJ's Auto. [MOB_03C] La disparition soudaine de BJ ne me laisse pas d'autre choix [MOB_03D] que de vous tenir pour responsable du passif de cette société. [MOB_03E] Jusqu'à ce que ce compte soit complètement soldé [MOB_03F] vous devriez considérer les rues de Vice City comme très dangereuses. [MOB_04A] Comment va, vieille branche? [MOB_04B] Ecoute, Tommy, j'avais oublié de te dire qu'on va avoir besoin de balaises en plus pour le concert. [MOB_04C] Y'a un gang de motards dirigé par Mitch Baker, ça nous ferai une super publicité. Vraiment Rock'n'roll, Baby. [MOB_04D] Règle ça pour moi et je te laisserai accéder aux coulisses du concert, d'accord? [MOB_05A] Bien joué, Tommy, j'suis content d'avoir récupéré ma bécane. [MOB_05B] Dis à Mr Kent Paul que la sécurité sera assurée pour le concert. [MOB_05C] Tu as ma parole. [MOB_05D] Bon, maintenant, évite de t'attirer des emmerdes. [MOB_06A] Tommy! Ceux d'en bas, ils parlent de toi, mon petit... [MOB_06B] Je me disais que tu te laisserais peut-être tenter par un doux foyer et par une soupe au gombo de tata Poulet? Mmm? [MOB_06C] Fais un saut par ma cuisine un de ces jours, hein, Tommy? [MOB_08A] Salut, Tommy, je me disais que t'aurais peut-être besoin d'un conseil en affaires... [MOB_08B] Dès que tu as une opération en route, il faut que tu passes prendre le fric de la semaine. [MOB_08C] Si tu laisses les mecs penser que ce sont eux qui dirigent l'affaire, ils essayent de ronger tes bénefs! Ok? [MOB_08D] Ca va, Ken, je connais le business... [MOB_08E] Ok, ok, je sais que tu sais. Je sais. C'était... [MOB_08F] C'était juste pour te dire que je sais que tu sais que je sais... [MOB_08G] Ouvre simplement l'oeil! [MOB_08H] T'inquiète, Ken, t'inquiète... [MOB_09A] Salut, Léo! J'ai du boulot pour toi! [MOB_09B] C'est pas Léo. [MOB_09C] Si Léo apprend que t'as son téléphone, il va te buter aussi sec! [MOB_09D] Peut-être que Léo est mort. Peut-être même que c'est moi qui l'ai buté et que j'ai hérité de son téléphone. T'avais pas pensé à ça hein, connard? [MOB_09E] T'as buté Léo? Alors, c'est que t'en as dans le froc! Tu veux bosser pour moi? [MOB_09F] Passe au café de mon père dans Little Havana et on pourra causer entre hommes. [MOB_10A] Tommy! Ecoute, j'ai un service à te demander! [MOB_10B] Steve! Comment ça marche, le cinoche? [MOB_10C] Ca va, ça va. Mais on a besoin d'une scène de poursuite en bagnole et on a pas le budget pour ça. [MOB_10D] J'ai laissé des bagnoles dans les environs. Tu sauras quoi faire. [MOB_10E] OK, Steve, je vais voir ce que je peux faire. A plus tard. [MOB_11A] Salut petit, je me suis dit que j'allais te passer un coup de fil pour te donner un bon conseil. [MOB_11B] Salut Avery, qu'est-ce que tu voulais me dire? [MOB_11C] Les perspectives ne manquent pas dans cette ville, si tu possèdes le bon business... Tu vois ce que je veux dire? [MOB_11D] Je pense que oui... [MOB_11E] Je te conseille d'ouvrir l'oeil, et le bon, si tu veux dégotter un bon business. A plus tard. [MOB_11F] Ciao, Avery. [MOB12_A] Tommy! C'est Avery! Ecoute, je suis débordé de boulot pour le moment, [MOB12_B] et j'ai un de mes réprésentants qui a besoin d'être chaperonné jusqu'aux Gator Keys. [MOB12_C] Je suis sur l'achat d'un terrain là-bas, alors j'envoie un mec graisser quelques pattes. [MOB12_D] J'ai besoin que tu t'assures qu'il arrive bien là-bas. OK? [MOB12_E] OK, pas de problème, Avery! Mais où c'est que je le trouve, ce mec? [MOB12_F] Il boucle une affaire au chantier. Je lui ai dit que tu le prendrais là-bas. [MOB12_G] Ok, Avery. J'y vais. A plus tard. [MOB13_A] Vercetti? VERCETTI! Putain, mec, faut que tu me files un coup de main! [MOB13_B] Mr Moffat? Comment va la famille? [MOB13_C] Merde, mais quel con! Tu m'entends? [MOB13_D] Ca m'a fait plaisir d'avoir des nouvelles... [MOB13_E] Attends! Attends! Vercetti, euh... Tommy. Je peux t'appeler Tommy? [MOB13_F] On est tous les deux dans les affaires, pas vrai? Et tu sais renifler les bonnes, hein? [MOB13_G] J'ai pas le temps de causer, viens-en au fait! [MOB13_H] Du FRIC! Voilà le putain de fait! [MOB13_I] Je me suis encore tiré de taule, mais ça prend jamais longtemps avant qu'ils me retombent dessus. Pour eux, c'est qu'un putain de jeu! [MOB13_J] Je t'appelle d'une cabine quelque part dans le trou du cul du monde! [MOB13_K] Sors-moi de là avant qu'ils me chopent encore et... et... ah... meeeerde... [MOB13_L] Eh bien, c'est que je suis très occupé pour les prochaines- [MOB13_M] Non! Me laisse pas dans cette merde! T'as pas de coeur ou quoi? Putain, j'devrais pas avoir à m'abaisser comme ça! [MOB13_N] Je suis à genoux, Tommy! Et c'est les rotules dans la poussière que je te supplie... [MOB13_O] J'imagine que je pourrais passer dans le coin et voir si je te trouve... [MOB13_P] Oh, merde, les voilà! Putain, magne-toi, Tommy! Vite! [MOB_14A] Salut, Tommy, on va bien s'entendre, toi et moi. [MOB_14B] Une tendre amie est venu me sussurrer dans l'oreille que la division SWAT de Vice City a un coffre dans une grosse banque, [MOB_14C] et ils y gardent les pots-de-vin de toutes ces dernières années. [MOB_14D] C'est une sorte de caisse de retraite pour vieux garçons. [MOB_14E] Evidemment, si cette info pouvait t'aider à ramasser un peu de ce pognon, [MOB_14F] j'imagine que tu te sentirais obligé de m'en refiler un peu, pas vrai, Tommy? [MOB_14G] Je vais garder ça à l'esprit, merci, Kent. [MOB_14H] Je m'appelle Paul, abruti! Et je suis du Kent, près de Londres. [MOB_14I] Pour ce que j'en ai à foutre de la campagne anglaise, depuis l'école... [MOB15_A] Tommy, mon vieux, c'est Paul, du Kent. [MOB15_B] Il y a une ou deux authentiques dames qui ont ton nom plein la bouche, au Malibu. [MOB15_C] De quoi tu parles?! [MOB15_D] De gonzesses, de poulettes, de filles, quoi! Et bien élevées, pas le genre à causer pognon. [MOB15_E] Faut que tu viennes voir ça. [MOB16_A] Tommy, c'est Paulo, que pasa amigo? [MOB16_B] Qu'est-ce que tu m'veux, Paul? J'ai pas besoin de fausses fringues de marque. [MOB16_C] Très drôle, mon vieux, mais je fais pas dans la fringue de tapette, moi. [MOB16_D] Bon, je t'appelle parce que j'me demande si je ne pourrais pas avoir un rôle dans un de tes films. [MOB16_E] J'ai fait pas mal de pornos en Angleterre, mon vieux. [MOB16_F] Au plumard, je suis une mitrailleuse lourde. [MOB16_G] Merci de me proposer tes services, Paul, j'y réfléchirai. [MOB16_H] Sérieusement, pense à moi, après tout ce que j'ai fait pour toi... [MOB16_I] Justement, j'essaie de l'oublier. [MOB17_A] Tommy Vercetti, comment ça va, big boss? [MOB17_B] J'ai entendu tous ces trucs sur toi. Un flambeur en ville maintenant... [MOB17_C] Paul, t'es bourré. [MOB17_D] Naan! Pauvre abruti! J'suis pas saoul! [MOB17_E] J'ai juste pris un ou deux verres et quelques tournées, et ça fait deux jours que j'ai pas fermé l'oeil! [MOB17_F] De toute façon, ne me traite pas comme ça. [MOB17_G] Je suis pas une poire. Qui c'est qui t'a lancé dans cette ville? Hein, qui? Moi! [MOB17_H] Ah ouais? [MOB17_I] Ouais! [MOB17_J] Paul, t'énerve pas. J'étais occupé, sois pas stupide. [MOB17_K] Je suis pas stupide, connard! Ils me l'ont dit en maison de redressement! [MOB17_L] Si tu cherches les emmerdes, mon pote, tu vas les trouver! [MOB17_M] Tommy, s'il te plaît! T'étais mon grand espoir! Te fous pas de moi! [MOB17_N] Paul, va roupiller un bon coup, sérieusement. [MOB18_A] Tommy, c'est Paulo, comment ça va, mon pote, je me suis dit que j'allais te passer un petit coup de fil. [MOB18_B] Ah, mon pote, tu croiras jamais l'incroyable petit lot que je viens de lever... [MOB18_C] Elle se promenait juste en bas dans Little Havana, mon pote. [MOB18_D] Elle m'a dit qu'elle s'appelait Mercedes ou un truc comme ça. Ah, mon pote, faut que t'ailles voir cette fille! [MOB18_E] Elle ferait bander un eunuque! Elle m'a dit que j'étais le meilleur coup de sa vie et tout et tout! [MOB18_F] Vas-y, trouve-la. A plus tard. [MOB19_A] Tommy V, c'est KP! Kent Paul. Le bruit court que des mecs veulent te faire la peau. [MOB19_B] Ouvre l'oeil, mon pote. Et oublie pas... Je t'ai rien dit. [MOB_20A] Salut, Tommy, c'est Paul. Je viens juste d'entendre dire que t'avais été un vilain garçon. [MOB_20B] Il y a quelqu'un qui s'est vexé de te voir jouer les caïds et ça lui a donné de mauvaises idées... [MOB_20C] Bon, dis à personne que je t'ai rencardé. Les grandes gueules finissent toujours par l'avoir dans l'os. [MOB_20D] Enfin, j'ai entendu dire que ta tête a été mise à prix et que quelqu'un va essayer de t'avoir, [MOB_20E] alors fais gaffe, et m'oublie pas, mon pote. [MOB71_A] Tommy, Thomas, c'est Cortez. Que pasa? [MOB71_B] Les affaires tournent... Et toi, comment ça va? [MOB71_C] Tommy, ici, c'est toujours la guerre. Excusez la mauvaise ligne, mais on vient juste d'avoir un nouveau coup d'état manqué. [MOB71_D] Le peuple est la plus exigeante des maîtresses. [MOB71_E] Si je compte bien, y'a eu trois révolutions et quatre coups d'état depuis que je suis rentré de Vice City. [MOB71_F] Heureusement, à chaque fois, j'ai pris du galon. [MOB71_G] Je voulais vous demander quelque chose à propos de Mercedes... [MOB71_H] Ouais? Quoi? [MOB71_I] Voilà, Tommy, Tommy, j'ai entendu toutes ces histoires et je sais pas quoi penser... [MOB71_J] Peut-être que tout le monde essaye de m'humilier... [MOB71_K] Peut-être qu'elle se croit tout permis, mais dites-moi juste la vérité, Tommy, c'est vrai? [MOB71_L] Mais dites-moi juste la vérité, Tommy, c'est vrai? [MOB71_M] Qu'est-ce qui est vrai? [MOB71_N] Ces bruits qui courent... Elle va vraiment devenir avocate? [MOB71_O] Ah, Tommy, c'est une véritable honte! Nous, les Cortez, sommes une honorable famille et nous ne tolèrerons jamais qu'une de nos filles devienne avocate. [MOB71_P] Je vous en prie, dites-moi que ce n'est pas vrai, je ne crois pas que je pourrais le supporter. [MOB71_Q] Colonel, je peux vous assurer que Mercedes ne deviendra jamais avocate. Ne vous inquiètez pas. [MOB71_R] Merci, Tommy, cette honte me serait insupportable. C'est une femme, pas un parasite, vous comprenez. [MOB71_S] Je le sais, colonel. [MOB71_T] Bon, vous allez devoir m'excuser, Tommy, mais le nouveau ministre de l'intérieur vient d'arriver... [MOB71_U] Il y a de nombreuses années, j'ai tué son père au cours d'une tentative de coup d'état ratée et je dois me montrer poli. Adios, amigo. [MOB22_A] Tommy, vous vous révèlez très utile, amigo. [MOB22_B] Merci, Cortez. Et mon affaire? [MOB22_C] Tommy, je travaille inlassablement en ce sens pour aller au fond des choses et de la vérité dans cette terrible histoire. [MOB22_D] Vous avez ma parole, mais en attendant, [MOB22_E] acceptez, je vous en prie, les sincères remerciements de mon peuple pour vos actions bénéfiques. [MOB_25A] Tommy, c'est Cortez. Les Français me font beaucoup d'ennuis... [MOB_25B] Les sales hypocrites! Ils passent des siècles à piller les pays pauvres et ils osent me traiter de voleur! [MOB_25C] Je vais avoir besoin de votre aide aussi vite que possible. [MOB_25D] Vite, Tommy, j'ai vraiment besoin de vous. Je hais ces maudits Français! [MOB_26A] Allo, Tommy? [MOB_26B] Ouais? [MOB_26C] C'est moi, Baker. Je voulais juste te dire que j'ai beaucoup apprécié le spectacle. [MOB_26D] Moi et mes gars, on veut te remercier, et te rappeler [MOB_26E] que tu as tout notre respect. Bon, salut. Et continue comme ça, mon pote. [MOB_29A] Allo? Vous êtes bien M. Tommy Vercetti? [MOB_29B] Ouais. [MOB_29C] Bien. J'ai entendu dire que vous êtes l'homme à appeler quand on est victime d'une invasion de vermine. [MOB_29D] Ca se peut... [MOB_29E] Eh bien, là je suis vraiment envahi par la vermine. Les haïtiens sont partout... [MOB_29F] Je m'appelle Umberto Robina et je vous propose une rencontre au Café Robina aussitôt que vous pourrez. [MOB_29G] Moi j'vous le dis, ces maudits haïtiens sont allés trop loin, cette fois... [MOB_29H] Test [MOB_30A] Tommy, c'est Umberto Robina. [MOB_30B] Quoi de neuf au café? [MOB_30C] Oh, merveilleux, Tommy, vraiment incroyable! Ici, plus de mauviettes, Tommy. Juste de vrais hommes et des belles filles! [MOB_30D] Je voulais te dire que moi et papa, maintenant, on te considère comme cubain. [MOB_30E] T'as fait tes preuves, mon frère et t'en as une sacrée paire dans l'pantalon! [MOB_30F] Euh, merci, Umberto. Personne ne m'a dit ça depuis que je suis sorti de taule. Bon, salut. [MOB_33A] Tommy, c'est Phil! Bon, arrête tes conneries et écoute-moi, tu veux? [MOB_33B] Bon. J'ai du whisky extra fort, presque fermenté, et je me demandais si tu voulais pas venir boire un coup... [MOB_33C] Sans déconner, Tommy, si t'as besoin d'un verre ou de décoller de la peinture, t'es le bienvenu, ce truc fera de toi un homme! [MOB_33D] Ca me fait cet effet, même si je vois plus rien d'un oeil. Je t'attends, t'entends? [MOB_34A] Tommy, ça m'a fait vraiment plaisir de bosser avec toi. Je m'étais pas autant marré depuis la guerre du Vietnam, mon pote. [MOB_34B] Bon, si t'as besoin de quelque chose, tu m'appelles, t'entends? [MOB_34C] Je n'oublie jamais ceux avec qui j'ai servi [MOB_34D] et je suis sûr que je peux t'aider, t'entends? [MOB_35A] Tommy, la blessure cicatrise bien. Le truc drôle, c'est que [MOB_35B] j'ai combattu dans six batailles et que je m'en suis toujours sorti sans une égratignure, et voilà! [MOB_35C] 'Phil, le manchot d'un bras.' Enfin, j'ai encore une large selection d'armes de poing alors je serai jamais 'Phil, le désarmé', t'entends? [MOB_35D] Aller, mon pote, laisse tomber les conneries sentimentales et va te boire un verre, t'entends? [MOB_36A] Tommy, c'est Phil. Je voulais te remercier pour le coup de main, mon pote. [MOB_36B] Salopard de Charlie, toujours à tendre des embuscades ici ou là, [MOB_36C] mais la blessure cicatrise bien, et ça veut aussi dire que j'aurai plus à truander le gouvernement pour le contrôle d'invalidité. Merci, mon pote. [MOB_40A] Salut, Tommy, c'est Sonny. Alors ce bronzage? [MOB_40B] J'suis pas bronzé. [MOB_40C] Ouais et t'as pas non plus mon pognon, alors je m'interrogeais... [MOB_40D] Je me disais : 'Qu'est-ce qu'il fout?' Alors, je te le demande, Tommy, qu'est-ce que tu fous? [MOB_40E] Je cherche le pognon, Sonny, t'inquéte pas! [MOB_40F] Je m'inquiéte, Tommy, c'est mon genre, [MOB_40G] parce que j'ai ce problème récurrent dans la vie de tomber sur des gens indignes de confiance... [MOB_40H] Ne sois pas indigne de ma confiance, Tommy, s'il te plaît... [MOB_40I] Rends-nous à tous deux ce service. J'attends de tes nouvelles... [MOB_41A] Tommy, tu te souviens de moi? [MOB_41B] Salut, Sonny. [MOB_41C] Ouais, exactement, Sonny, on est de vieux amis, [MOB_41D] mais tu ne m'écris jamais, tu ne m'appelles jamais. Tu ne veux plus qu'on soit amis? [MOB_41E] J'ai été très occupé à régler des trucs ici et là. Tu ne m'aides pas beaucoup, ici. [MOB_41F] Oh, c'est ma faute? Bon, j'ai entendu que tu étais très occupé, d'accord, [MOB_41G] occupé à liquider des barons de la drogue, occupé à prendre le pouvoir. [MOB_41H] Alors je te préviens simplement de ne pas m'oublier, Tommy, parce que moi, je ne t'oublie pas... [MOB_42A] Tommy. [MOB_42B] Sonny. [MOB_42C] Faut croire que t'es à moitié sourd, alors je vais me répéter : [MOB_42D] Tommy, où est ce putain de pognon? Où est cette putain de marchandise? Et où est ma putain de part sur ta nouvelle affaire? [MOB_42E] Tu te fous vraiment d'ma gueule, Tommy, et ça me fait pas rire! [MOB_43A] Tommy! Tommy! J'ai eu Sonny au téléphone. Tu m'écoutes? [MOB_43B] Je ne sais pas pour toi, mais il y a un mec qui menace de buter toute ma famille, [MOB_43C] et ça me fout vraiment les jetons. Qu'est-ce que tu comptes faire? [MOB_43D] Reste calme, Ken, tout va bien se passer. [MOB_43E] Je suis calme! Calme comme un homme qui craint pour sa propre vie! [MOB_43F] Ken, j'ai d'autres choses en tête pour l'instant, [MOB_43G] on s'occupera de Forelli quand l'heure sera venue. [MOB_43H] Je suis calme. J'ai pas l'air calme? J'sais pas, c'est ma voix qui a changé à cause de la mort qui me pend au nez! [MOB45_A] Tommy, faut qu'on cause sérieusement. [MOB45_B] C'est quoi le problème, Lance? [MOB45_C] C'est toi le problème, mon pote, j'ai l'impression que tu m'files pas une part équitable... [MOB45_D] Mais le pire, c'est que tu m'as foutu la honte devant les mecs. Je supporte pas ça. [MOB45_E] Lance, c'est pas ça du tout. T'as fait des bourdes, c'est tout. [MOB45_F] Tommy, je suis pas ton petit messager et encore moins ton garçon de courses. [MOB45_G] Lance, déconne pas et on aura pas d'emmerdes! Et si c'est moi qui déconne, tu peux me rentrer dedans quand tu veux. [MOB45_H] Tommy, j'ai tout fait pour toi et tu me traites comme un con. Fais pas ça. [MOB45_I] Lance, je ne vais pas t'arnaquer ou te planter un couteau dans le dos, ok? [MOB45_J] T'énerve pas. C'est déjà assez dur comme ça, sans que tu perdes les pédales. [MOB45_K] Fais-moi confiance. Tu m'entends? Tu m'entends? [MOB45_L] Je t'entends, Tommy, mais je peux pas supporter ça plus longtemps. [MOB45_M] Lance, arrête de déconner. Maintenant, je te préviens. [MOB45_N] Tu m'entends? Relax! Prends quelques jours de repos et on en reparle, ok? [MOB46_A] Tommy, c'est Lance. [MOB46_B] Ouais? [MOB46_C] Même pas un 'ça fait plaisir d'avoir des tes nouvelles'? Allez, mec, sois sympa! [MOB46_D] Je suis occupé. Qu'est-ce que tu veux? [MOB46_E] Rien. C'était juste pour te dire... Tu sais, Tommy, on peut faire ce truc. [MOB46_F] Toi et moi, sans problème. Tu vois ce que je veux dire? [MOB46_G] Va bien falloir qu'on le fasse, parce que sinon, on est morts, Lance. [MOB46_H] On peut plus reculer, maintenant, mais merci de ton appel. On se reparle plus tard. [MOB_47A] Tommy, c'est Lance, on a de gros problèmes. Amène-toi ici en vitesse. [MOB52_A] Salut, Léo, je crois qu'on a un acheteur pour la marchandise de Diaz. [MOB52_B] Il faut que tu l'appelles, mec, pour organiser le deal, tu comprends? [MOB52_C] T'es où, là? [MOB52_D] T'es sûr que ça va, Léo? T'as une voix bizarre... [MOB52_E] Dis-moi juste où t'es. [MOB52_F] Qui c'est qui cause? Je veux parler à Léo! [MOB52_G] Léo est parti pour un moment, je le remplace. [MOB52_H] Va te faire foutre, connard! [MOB54_A] Salut, Tommy! [MOB54_B] Salut, Mercedes, quoi de neuf? [MOB54_C] J'ai un nouvel appartement dans Vice Point. [MOB54_D] J'ai pensé que tu voudrais peut-être passer un de ces quatre... [MOB54_E] J'adorerai. A plus tard. [MOB55_A] Tommy, c'est moi. [MOB55_B] Salut, Mercedes. [MOB55_C] Tommy, je m'ennuie tellement! Quand est-ce qu'on va s'amuser? [MOB55_D] Comment ça? [MOB55_E] Bon, je sais que t'es très occupé à te battre, à tuer des gens ou à les corrompre, [MOB55_F] mais moi, j'ai envie de m'amuser un peu! Alors, ne m'oublie pas, Tommy! [MOB56_A] Tommy, j'ai entendu dire que tu avais tué Ricardo Diaz! [MOB56_B] Un feu accidentel s'est déclenché dans sa résidence... [MOB56_C] Je pense qu'il a cramé dans sa chemise en acrylique. [MOB56_D] Tommy, je suis si fière de toi! Je savais que tu étais un vrai macho! [MOB56_E] Ah, mon dur de dur, je suis si fière d'être ton amie! [MOB56_F] Je sais que tu vas être très occupé à essayer de t'emparer de cette ville, [MOB56_G] mais ne m'oublie pas, d'accord? [MOB57_A] C'est Mercedes. Je ne t'aime plus, Tommy! [MOB57_B] C'est fini! C'est vrai! Parce que tu n'es plus gentil avec moi! [MOB57_C] Tu ne me traites plus comme une dame! Tu m'ignores et je te déteste! [MOB57_D] Je veux que tu viennes me voir tout de suite! " [MOB58_A] Tommy. [MOB58_B] Salut, Mercedes. [MOB58_C] Bonjour monsieur le vrai dur! Je suis très en colère contre toi, Tommy! [MOB58_D] Ne m'oblige plus jamais à supporter Jezz Torrent! [MOB58_E] Il est pathétique! En plein milieu il a commencé à pleurer à cause de son toutou [MOB58_F] qui est mort quand il avait 7 ans et parce que sa mère ne l'a jamais aimé... [MOB58_G] En plus, Tommy, il porte une perruque et un soutien-gorge dans l'intimité! [MOB58_H] Je t'en veux vraiment! [MOB59_A] Ooh Tommy, c'est Mercedes. [MOB59_B] Je voulais juste te dire que je m'étais éclatée sur le tournage! [MOB59_C] Si tu as quelque chose d'autre de ce genre, pense à moi! [MOB59_D] Je suis sincère! J'ai toujours voulu être actrice! [MOB59_E] Je crois que j'ai beaucoup appris sur l'art dramatique! [MOB59_F] C'est tellement révélateur! Merci beaucoup! On se voit très bientôt, adios! [MOB_99] Rends-toi à la cabine indiquée. [MOB_98] Rends-toi à la cabine indiquée. [MOB_97] Rends-toi à la cabine indiquée. [MOB_96] Rends-toi à la cabine indiquée. [MOB_95] Rends-toi à la cabine indiquée. [A_TIME] +~1~ secondes [F_RANGE] La radio du camion de pompiers ne capte plus rien. Rapproche-toi d'une caserne! [DODO_FT] Tu as 'volé' pendant ~1~ secondes! [GA_8] Utilise le détonateur pour armer la bombe. [GA_10] Bravo. Voilà tes ~1~$. [GA_11] On en a déjà des comme ça. Ca nous sert à rien! [GA_12] Bombe armée [GA_13] Comme un pro! Finis la liste et t'auras un bonus! [GA_14] Toutes les voitures! PARFAIT! Tiens, voilà un petit quelque chose! [GA_15] J'espère que t'aimes la nouvelle couleur. [GA_16] La peinture est en option! [GA_19] Ce modèle ne nous intéresse pas! [GA_20] On en a trop des comme ça! Désolé, mec. [CHASE] Attention maximale des médias [CHASE1] Ignoré [CHASE2] Ennuyeux [CHASE3] Plus ou moins intéressant [CHASE4] Journal local page 7 [CHASE5] Journal local La une [CHASE6] Vice Courier Rubrique 1 [CHASE7] Vice Courier La une [CHASE8] Télé locale 3h du mat' [CHASE9] Télé locale Infos [CHASE10] Télé locale Direct [CHASE11] UFA Today page 12 [CHASE12] UFA Today page 4 [CHASE13] Photo dans UFA Today [CHASE14] Télé nationale 4h du mat' [CHASE15] Télé nationale Infos [CHASE16] Télé nationale Direct [CHASE17] Infos internationales [CHASE18] Crise nationale [CHASE19] Crise internationale [CHASE20] Evénement mondial [CHASE21] Truc de légendes [CR_1] La grue ne peut pas soulever ce véhicule. [PU_MONY] T'as pas assez de liquide. [CO_ALL] Tu les as toutes eues. Tiens, voilà pour toi... [FEM_ON] Activé [FEM_OFF] Désactivé [FEM_YES] Oui [FEM_NO] Non [FEM_RET] Réessayer [FEC_NA] NA [FEC_CWL] Faire défiler les armes vers la gauche [FEC_CWR] Faire défiler les armes vers la droite [FEC_LOF] Regarder devant [FEC_TAR] Viser [FEC_MOV] Déplacement [FEC_CAM] Modes caméra [FEC_PAU] Pause [FEC_ENV] Monter dans un véhicule [FEC_JUM] Sauter [FEC_ATT] Attaquer ou tirer [FEC_RUN] Courir [FEC_FPC] Vue subjective [FEC_LL] Rergarder à gauche [FEC_LB] Regarder derrière [FEC_LR] Regarder à droite [FEC_HOR] Klaxon [FEC_VES] Commandes du véhicule [FEC_BRA] Freiner ou marche arrière [FEC_HAB] Frein à main [FEC_CAW] Arme du véhicule [FEC_ACC] Accélérateur [FEC_CCF] Configuration : [FEC_CF1] Config. 1 [FEC_CF2] Config. 2 [FEC_CF3] Config. 3 [FEC_CF4] Config. 4 [FEC_CDP] Affichage manette : [FEC_ONF] à pied [FEC_INC] En voiture [FEC_VIB] Vibrations : [FEL_ENG] Anglais [FEL_FRE] Français [FEL_GER] Allemand [FEL_ITA] Italien [FEL_SPA] Espagnol [FED_DBG] Menu Debug [FED_RID] Recharger IDE [FED_RIP] Recharger IPL [FED_PAH] Analyser Saut [FED_DFL] CTheScripts::DbgFlag [FED_DLS] Lumière blanche de debug allumée [FED_SPR] Affiche les groupes de pietons [FED_SCR] Affiche les groupes en voiture [FED_SCZ] Affiche les zones de massacre [FED_DSR] Debug les demandes de répartition par niveaux [FED_SCP] gb Indique les polys de collision [PL_STAT] Stats du joueur [PE_WAST] Personnes tuées par toi [PE_WSOT] Personnes tuées par d'autres [TM_BUST] Nb de fois capturé [GNG_WST] Gangsters tués [DED_CRI] Criminels tués [PER_COM] Pourcentage accompli [KGS_EXP] Kilos d'explosifs utilisés [ACCURA] Précision [ST_WEAP] Budget arsenal [ST_PROP] Budget propriété [ST_AUTO] Budget réparation et peinture voiture [ST_PHOT] Photos prises [ST_LOAN] Visites des usuriers [ST_STOR] Magasins liquidés [ST_MOVI] Cascades de films [ST_PIZZ] Pizza livrées [ST_GARB] Collectes de détritus effectuées [ST_ICEC] Crèmes glacées vendues [TOP_SHO] Meilleur score au champ de tir [SHO_RAN] Position au champ de tir [SEAGULL] Mouettes descendues [PROPOWN] Propriété possédée [ST_TIME] Temps de jeu [ST_FTIM] Heures de vol [ST_PRAN] Classement pilote [ST_RAN0] Apprenti [ST_RAN1] Navigateur [ST_RAN2] Copilote [ST_RAN3] Junior [ST_RAN4] Compétent [ST_RAN5] Senior [ST_RAN6] As [ST_RAN7] Baron rouge [ST_DRWN] Poissons nourris [ST_FASH] Budget fringues [ST_DAMA] Propriété détruite [TM_DED] Visites à l'hôpital [DAYSPS] Jours passés dans le jeu [NUMSHV] Visites dans l'entrepôt [MXCARD] Dist. max. de saut de Cascade dangereuse (ft) [MXCARJ] Hauteur max. de saut de Cascade dangereuse(ft) [MXCARDM] Dist. max. de saut de Cascade dangereuse (m) [MXCARJM] Hauteur max. de saut de Cascade dangereuse (m) [MXFLIP] Saltos max. de saut de Cascade dangereuse [MXJUMP] Rotation max. de saut de Cascade dangereuse [BUL_FIR] Balles tirées [BUL_HIT] Balles ayant atteint la cible [SPRAYIN] Peintures [BSTSTU] Meilleure cacade jusqu'à maintenant [INSTUN] Cascade dangereuse [PRINST] Cascade dangereuse parfaite [DBINST] Double cascade dangereuse [DBPINS] Double cascade dangereuse parfaite [TRINST] Triple cascade dangereuse [PRTRST] Triple cascade dangereuse parfaite [QUINST] Quadruple cascade dangereuse [PQUINS] Quadruple cascade dangereuse parfaite [NOSTUC] Aucune cascade dangereuse réalisée [NOUNIF] Sauts uniques accomplis [NMISON] Tentatives de mission [PASDRO] Passagers perdus [MONTAX] Total des courses en taxi [DAYPLC] Dépenses quotidiennes de police [CRIMRA] Taux de criminalité : [STPR_1] Le Malibu [STPR_2] Imprimerie [STPR_3] Studio de cinéma [STPR_4] Usine de crème glacée [STPR_5] Concession automobile [STPR_6] Compagnie de taxis [STPR_7] Chantier naval [SET1EN] Config. 1. Activée [GMSAVE] Sauvegarder partie [FEDS_TB] Retour [FEST_OO] sur [FEC_TUC] Commandes tourelle [FEC_RS3] Faire défiler les stations de radio (touche L3) [FEC_HO3] Klaxonner (touche L3) [C_FAIL] Mission d'autodéfense terminée! [C_ESCP] ~r~Le suspect s'est échappé! [C_VIGIL] BONUS AUTODEFENSE! [HEAL_A] Ton niveau de ~h~santé~w~ s'affiche en orange en haut à droite de l'écran. [WRONGCD] Disque invalide. Insère le bon disque. [NOCD] Le compartiment à disque est vide. Insère un disque. [OPENCD] Le compartiment à disque est ouvert. Referme-le. [CDERROR] Erreur au cours de la lecture du DVD de Grand Theft Auto: Vice City [RESTART] Démarrage d'une nouvelle partie [GA_3] Plus de cadeaux. 100$ pour repeindre! [GA_1] Oula! Je ne touche à rien d'aussi chaud, moi! [GA_1A] Reviens quand tu seras moins occupé... [HELP9_C] Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. [TAXI2] ~r~Tu n'as plus le temps! [PAGEB13] Santé livrée à la planque [PAGEB14] Adrénaline livrée à la planque [FESZ_CA] Annuler [FES_NGA] Nouvelle partie [FES_CAN] Annuler [FESZ_QL] Toutes les étapes non sauvegardées de cette partie seront perdues. Charger la partie? [FESZ_QD] Effacer cette sauvegarde? [FESZ_QO] Ecraser cette sauvegarde? [FESZ_QR] Veux-tu vraiment commencer une autre partie? Toutes les données depuis la dernière partie sauvegardée seront perdues. Continuer? [T4X4_1] 'Terrain de jeu PCJ' [BMX_1] 'Sélection par la boue' [BMX_2] 'Piste d'essai' [BMXFAIL] ~r~Tu n'as pas réussi à établir un nouveau record! [T4X4_3] 'DANS LA POCHE!' [MM_1] 'Cônes en folie' [T4X4_F] ~r~Tu t'es barré! Trop dur pour toi? [LANDSTK] Landstalker [IDAHO] Idaho [STINGER] Stinger [LINERUN] Linerunner [PEREN] Perennial [SENTINL] Sentinelle [RIO] Rio [PATRIOT] Patriote [FIRETRK] Camion de pompier [TRASHM] Trashmaster [STRETCH] Stretch [MANANA] Manana [INFERNS] Infernus [VOODOO] Voodoo [PONY] Pony [MULE] Mule [CHEETAH] Cheetah [AMBULAN] Ambulance [FBICAR] FBI Washington [MOONBM] Moonbeam [ESPERAN] Esperanto [TAXI] Taxi [WASHING] Washington [BOBCAT] Bobcat [WHOOPEE] Mr Whoopee [BFINJC] BF Injection [HUNTER] Hunter [POLICAR] Police [ENFORCR] Enforcer [SECURI] Securicar [BANSHEE] Banshee [PREDATR] Predator [BUS] Bus [RHINO] Rhino [BARRCKS] Barracks OL [CUBAN] Cuban Hermes [HELI] Hélicoptère [DODO] Dodo [COACH] Coach [CABBIE] Cabbie [STALION] Stallion [RUMPO] Rumpo [RCBANDT] RC Bandit [ROMERO] Corbillard de Romero [PACKER] Packer [ADMIRAL] Amiral [SQUALO] Squalo [SEASPAR] Sea Sparrow [PIZZABO] Pizza Boy [GANGBUR] Gang Burrito [TROPIC] Tropic [SPEEDER] Speeder [REEFER] Reefer [FLATBED] Flatbed [YANKEE] Yankee [CADDY] Caddy [ZEBRA] Taxi Zebra [TOPFUN] Top Fun [SKIMMER] Skimmer [PCJ600] PCJ 600 [PHOENIX] Phoenix [FAGGIO] Faggio [FREEWAY] Freeway [RCBARON] RC Baron [RCRAIDE] RC Raider [GLENDAL] Glendale [OCEANIC] Oceanic [SANCHEZ] Sanchez [SPARROW] Sparrow [LOVEFIS] Love Fist [COASTG] Garde-côte [DINGHY] Dinghy [HERMES] Hermes [SABRE] Sabre [SABRETU] Sabre Turbo [WALTON] Walton [REGINA] Regina [COMET] Comet [DELUXO] Deluxo [BURRITO] Burrito [SPAND] Spandex Express [MARQUIS] Marquis [BAGGAGE] Bagagiste [KAUFMAN] Taxi Kaufman [COASTMA] Garde-côte Maverick [MAVERIC] Maverick [RANCHER] Rancher [FBIRANC] FBI Rancher [VIRGO] Virgo [GREENWO] Greenwood [HOTRING] Hotring Racer [BLISTAC] Blista Compact [FEST_DF] Dist. parcourue à pied (miles) [FEST_DC] Dist. parcourue en voiture (miles) [FESTDFM] Distance parcourue à pied (m) [FESTDCM] Distance parcourue en voiture (m) [TOT_DIS] Distance totale parcourue (miles) [TOTDISM] Distance totale parcourue (m) [DISTHEL] Dist. parcourue en hélicoptère (miles) [DISTHEM] Dist. parcourue en hélicoptère (m) [DISTBOA] Dist. parcourue en bateau (miles) [DISTBOM] Dist. parcourue en bateau (m) [FEST_LS] Personnes secourues en ambulance [FEST_CC] Criminels tués lors de la mission d'autodéfense [FEST_FE] Nombre total de feux éteints [FEST_RP] Rodéos réussis [FEST_MP] Missions réussies [FEST_BB] Les fous du volant : [FEST_H0] Nombre maximum de points de passage [FEST_GC] Nombre total de voitures de gang : [FEST_H1] Destruction de diablo [FEST_H2] Massacre de la Mafia [FEST_H3] Le désastre du casino [FEST_H4] Le destructeur de Rumpo [USJ] BONUS POUR CASCADE UNIQUE! [RATNG1] Citoyen honnête [RATNG2] Quidam [RATNG3] Eboueur [RATNG4] Voleur à l'étalage [RATNG5] Vandale [RATNG6] Escroc [RATNG7] Pickpocket [RATNG8] Cleptomane [RATNG9] Mouchard [RATNG10] Rat [RATNG11] Sangsue [RATNG12] Le roi de l'arnaque [RATNG13] Aigrefin [RATNG14] Grippe-sou [RATNG15] Crapule [RATNG16] Rascal [RATNG17] Racaille [RATNG18] Garnement [RATNG19] Vaurien [RATNG20] Hors-la-loi [RATNG21] Voyou [RATNG22] Lâche [RATNG23] Idiot SA [RATNG24] Idiot [RATNG25] Récidiviste [RATNG26] Ancien taulard [RATNG27] Criminel [RATNG28] Clochard [RATNG29] Sage [RATNG30] Pilote [RATNG31] Gros bras [RATNG32] Tueur à gages [RATNG33] Chasseur de tête [RATNG34] Force de l'ordre [RATNG35] Ronin [RATNG36] Magouilleur [RATNG37] Tueur [RATNG38] Associé [RATNG39] Boucher [RATNG40] Nettoyeur [RATNG41] Assassin [RATNG42] Conseiller [RATNG43] Autodidacte [RATNG44] Bras droit [RATNG45] Exécutant [RATNG46] Lieutenant [RATNG47] Sous-chef [RATNG48] Capo [RATNG49] Patron [RATNG50] Noops [RATNG51] Don [RATNG52] Roi de la putain de ville [PAGE_00] . [WELCOME] BIENVENUE A [TSCORE] REVENUS : ~1~$ [PBOAT_2] { reVC update } Appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer avec les canons du bateau. [HJSTAT] Distance : ~1~.~1~ m Hauteur : ~1~.~1~ m Saltos : ~1~ Rotation : ~1~_ [HJSTATW] Distance : ~1~.~1~ m Hauteur : ~1~.~1~ m Saltos : ~1~ Rotation : ~1~_ Et quelle belle réception! [ATUTOR] Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions ambulance. [FEST_HA] Mission ambulance, niveau maximum [C_KILLS] CRIMINELS TUES : ~1~ [HJSTATF] Distance : ~1~ ft Hauteur : ~1~ ft Saltos : ~1~ Rotation : ~1~_ [HJSTAWF] Distance : ~1~ ft Hauteur : ~1~ ft Saltos : ~1~ Rotation : ~1~_ Et quelle belle réception! [CRED001] ROCKSTAR NORTH [CRD001A] DIRECTEUR DU STUDIO [CRD001B] ANDREW SEMPLE [CRED002] PRODUCTEUR [CRD002A] DIRECTEUR DU DEVELOPPEMENT [CRED003] LESLIE BENZIES [CRED004] CONCEPTEUR GRAPHIQUE [CRED005] AARON GARBUT [CRED006] DIRECTEURS TECHNIQUES [CRED007] OBBE VERMEIJ [CRED008] ADAM FOWLER [CRED010] ANDREW DUTHIE [CRED011] CRAIG FILSHIE [CRED012] WILLIAM MILLS [CRED013] CHRIS ROTHWELL [CRD013A] IMRAN SARWAR [CRD013B] JAMES WORRALL [CRD013C] JOHN HAIME [CRED014] ECRIT PAR [CRED015] JAMES WORRALL [CRED016] PAUL KUROWSKI [CRED017] DAN HOUSER [CRED018] CONCEPTEUR PRINCIPAL DES PERSONNAGES [CRD018A] CONCEPTEUR DES PERSONNAGES [CRED019] IAN MCQUE [CRD019A] TOKS SOLARIN [CRD019B] ALAN DAVIDSON [CRED020] ANIMATEUR PRINCIPAL [CRED021] ALEX HORTON [CRD022A] ANIMATEURS [CRED022] LEE MONTGOMERY [CRD022B] DUNCAN SHIELDS [CRD022C] GUS BRAID [CRED023] CONCEPTEURS DES VEHICULES [CRD023A] JOLYON ORME [CRD023B] ALAN DUNCAN [CRD024A] CONCEPTEUR PRINCIPAL DES VEHICULES [CRED024] PAUL KUROWSKI [CRED025] CONCEPTEURS DE CARTE [CRED026] ADAM COCHRANE [CRED027] NIK TAYLOR [CRED028] GARY MCADAM [CRED029] KEIRAN BAILLIE [CRED030] ALISDAIR WOOD [CRED031] ANDREW SOOSAY [CRD031A] STEVEN MULHOLLAND [CRD031B] WAYLAND STANDING [CRD031C] CAMPBELL J. DICK [CRD031D] CONCEPTEUR GRAPHIQUE [CRD031E] STUART PETRI [CRED032] PROGRAMMEUR PRINCIPAL [CRD032A] PROGRAMMEURS [CRED033] ALEXANDER ROGER [CRED034] GRAEME WILLIAMSON [CRED035] BARANE CHAN [CRED036] DEREK PAYNE [CRED037] GORDON YEOMAN [CRD037A] ALAN CAMPBELL [CRD037B] MARK HANLON [CRD037C] ANDRZEJ MADAJCZYK [CRED038] PRODUCTEUR MUSIQUE PRINCIPAL [CRED039] CRAIG CONNER [CRED040] STUART ROSS [CRED041] INGENIEUR AUDIO PRINCIPAL [CRED042] ALLAN WALKER [CRD041A] INGENIEUR DU SON [CRD041B] AUDIO [CRD042A] WILL MORTON [CRED043] PROGRAMMEUR AUDIO [CRED044] RAYMOND USHER [CRED045] RESPONSABLE DES TESTS [CRED046] CRAIG ARBUTHNOTT [CRED047] RESPONSABLE CQ [CRED048] NEIL CORBETT [CRED049] KEVIN WONG [CRED050] CQ [CRED051] DAVID BEDDOES [CRED052] DAVID WATSON [CRED053] BARRY CLARK [CRED054] ROSS SPARROW [CRED055] JAMES ALLAN [CRED056] NEIL MEIKLE [CRD056A] GEORGE WILLIAMSON [CRD056B] MATT JONES [CRD056C] ROB HARBOUR [CRD056D] TOM WHITTAKER [CRED057] ASSISTANCE TECHNIQUE PRINCIPALE [CRED058] LORRAINE ROY [CRD057A] ASSISTANCE TECHNIQUE [CRED059] CHRISTINE CHALMERS [CRD060A] SERVICES GENERAUX [CRD060B] KIM GURNEY [CRD060C] CASSIE OLIVER [CRED060] ROCKSTAR NEW YORK [CRED061] PRODUCTEUR EXECUTIF [CRED062] SAM HOUSER [CRED063] PRODUCTEUR [CRED064] DAN HOUSER [CRED065] VICE-PRESIDENT DU DEVELOPPEMENT [CRED066] JAMIE KING [CRED067] RESPONSABLE TECHNOLOGIE [CRED068] GARY J. FOREMAN [CRED069] PRODUCTEUR ASSOCIE [CRED070] JEREMY POPE [CRD071A] RESPONSABLE CONTROLE QUALITE [CRD072A] JEFF ROSA [CRED071] RESPONSABLE MUSIQUE [CRED072] TERRY DONOVAN [CRED073] EQUIPE DE PRODUCTION [CRED074] TERRY DONOVAN [CRED075] JENNIFER KOLBE [CRED076] JENEFER GROSS [CRED077] LAURA PATERSON [CRED078] JEFF CASTANEDA [CRED079] JERONIMO BARRERA [CRED080] CARLY SLATER [CRED081] JUNG KWAK [CRED082] BRIAN WOOD [CRED083] RENAUD SEBANNE [CRED084] RICHARD KRUGER [CRD084A] DANIEL EINZIG [CRD084B] JACEN BURROWS [CRD084C] LINN PR [CRD084D] COUVERTURE [CRD084E] STEPHEN BLISS [CRED085] KENT PAUL'S 80 NOSTALGIA ZONE [CRED086] ECRIT PAR DAN HOUSER [CRD086A] PRODUIT PAR ADAM TEDMAN [CRED087] WWW.KENTPAUL.COM ET WWW.VICECITY.COM [CRED088] CREES PAR [CRD088A] ADAM TEDMAN [CRD088B] DAVID YU [CRD088C] JERRY LUNA [CRD088D] STUART PETRI [CRD088E] MICHAEL CARNEVALE [CRD088F] GREG LAU [CRD088G] FUTABA HAYASHI [CRED089] RESPONSABLE CQ [CRED090] CRAIG ARBUTHNOTT [CRED091] ANALYSTE PRINCIPAL [CRED092] ADAM DAVIDSON [CRD092A] JOE HOWELL [CRD092B] MARC FERNANDEZ [CRED093] ANALYSTE DE JEU [CRED094] RICHARD HUIE [CRED095] EQUIPE DE TEST CHEZ ROCKSTAR [CRED096] LANCE WILLIAMS [CRED097] JOE GREENE [CRED098] BRIAN PLANER [CRD098A] ELIZABETH SATTERWHITE [CRD098B] JAMEEL VEGA [CRD098C] MIKE HONG [CRED099] LEE CUMMINGS [CRED100] HISTOIRE [CRED101] JAMES WORRALL [CRED102] DAN HOUSER [CRED103] ADAM TEDMAN [CRED104] PAUL YEATES [CRED105] JENEFER GROSS [CRED106] LAURA PATERSON [CRED107] CINEMATIQUES [CRED108] ECRITES PAR DAN HOUSER ET JAMES WORRALL [CRED109] DIRECTION DE L'AUDIO PAR DAN HOUSER ET NAVID KHONSARI [CRED110] PRODUCTION DE JAMIE KING [CRD110A] CASTING : JAMIE KING, SEAN MACALUSO [CRED111] DISTRIBUTION [CRD111A] PERSONNAGES SECONDAIRES [CRED112] TOMMY VERCETTI : RAY LIOTTA [CRED113] KEN ROSENBERG : WILLIAN FICHTNER [CRED114] SONNY FORELLI : TOM SIZEMORE [CRED115] STEVE SCOTT : DENNIS HOPPER [CRED116] AVERY CARRINGTON : BURT REYNOLDS [CRED117] RICARDO DIAZ : LUIS GUZMAN [CRED118] LANCE VANCE : PHILIP MICHAEL THOMAS [CRED119] COLONEL JUAN CORTEZ : ROBERT DAVI [CRED120] UMBERTO ROBINA : DANNY TREJO [CRED121] PHIL CASSIDY : GARY BUSEY [CRED122] MITCH BAKER : LEE MAJORS [CRED123] MERCEDES CORTEZ : FAIRUZA BALK [CRED124] KENT PAUL : DANNY DYER [CRED125] JEZZ TORRENT : KEVIN MCKIDD [CRED126] REGULATRICE TAXI : DEBORAH HARRY [CRED127] CANDY SUXX : JENNA JAMESON [CRED128] BJ SMITH : LAWRENCE TAYLOR [CRED129] TATA POULET : YOUREE CLEOMILI HARRIS [CRED130] FOURNISSEUR : ARMANDO RIESCO [CRED131] COUGAR : BLAYNE PERRY [CRED132] HILARY : CHARLES TUCKER [CRED133] ALEX SHRUB, REPRESENTANT DU CONGRES : CHRIS LUCAS [CRED134] LE VIEUX KELLY : GEORGE DICENZO [CRD134A] CAM JONES : GREG SIMS [CRD134B] PSYCHOPATHE : HUNTER PLATIN [CRD134C] MAUDE, LA VENDEUSE DE GLACES : JANE GENNARO [CRD134D] JETHRO : JOHN ZURHELLEN [CRD134E] GONZALES : JORGE PUPO [CRD134F] DWAYNE : NAVID KHONSARI [CRD134G] DICK : PETER MCKAY [CRD134H] MIKE, L'ACTEUR DE PORNO : ROBERT CIHRA [CRD134I] PERCY : RUSSELL FOREMAN [CRED135] CAPTURE DE MOUVEMENTS [CRED136] ANIME PAR [CRD136A] DIRECTION TECHNIQUE PAR ALEX HORTON [CRED137] DIRIGE PAR [CRD137A] DIRIGE PAR NAVID KHONSARI [CRED138] PRODUIT PAR [CRD138A] PRODUIT PAR JAMIE KING [CRD138B] RENAUD SEBBANE [CRED139] ENREGISTRE AUX PERSPECTIVE STUDIOS, BROOKLYN [CRED140] ACTEURS POUR LA CAPTURE DE MOUVEMENTS [CRD140A] BLAYNE PERRY [CRD140B] JONATHON SALE [CRD140C] CHARLES TUCKER [CRD140D] EDDIE MARRERO [CRD140E] WILLIAM MCCALL [CRD140F] JORGE PUPO [CRD140G] ROBERT JACKSON [CRD140H] TARA RADCLIFFE [CRD140I] JENIFER GAMBETESE [CRD140J] KRIS ACHEVARRIA [CRD140K] ALI ORDOUBADI [CRD140L] KAHLEEM POOLE [CRED141] DIALOGUES DES PIETONS [CRD141A] ECRIT PAR DAN HOUSER, MARC FERNANDEZ, GILLIAN TELLING ET NAVID KHONSARI [CRD141B] AVEC L'AIDE DE JEREMY POPE, LANCE WILLIAMS ET JENNY JEMISON [CRED142] ECRITS PAR [CRD142A] DAN HOUSER ET JAMES WORRALL [CRED143] DIRIGE PAR DAN HOUSER, CRAIG CONNER, MARC FERNANDEZ ET ALLAN WALKER [CRED144] PRODUITS PAR RENAUD SEBANNE [CRED145] PIETONS [CRED146] ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCIA, [CRED147] ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, [CRED148] ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, [CRED149] BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, [CRED150] CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LOPEZ, DAN LEE, [CRED151] DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, [CRED152] DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, [CRED153] DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, [CRED154] DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, [CRED155] ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, [CRED156] GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, [CRED157] HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, [CRED158] JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, [CRED159] KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, [CRED160] LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, [CRED161] LORNA JORDAN, LUCIO AMADIO, MARCO FERNANDEZ, MARIKO TANAKA, MARLON MATTHEWS, [CRED162] MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, [CRED163] MELISSA ALVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, [CRED164] NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, [CRED165] OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZALES, [CRED166] RANDY JOHNSON, REY CONCEPCION, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, [CRED167] ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NUNEZ, SALVADORE SUAZO, [CRED168] SAM WHITE, SANTOS GONZALES, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, [CRED169] STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, [CRED170] SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, [CRED171] DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, [CRED172] CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON [CRED175] ADAM DAVIDSON [CRED176] LANCE WILLIAMS [CRED177] NEIL MCCAFFREY [CRED178] LAURA PATERSON [CRED179] REY CONCEPCION [CRED180] CHARLES HEROLD [CRED181] ANDREW GREENWALD [CRED182] JAMES MIELKE [CRED183] PETER SUCIU [CRED184] ALEX ODULIO [CRED185] DON NKRUMAH [CRED186] KENDALL PITTMAN [CRED187] SAL SUAZO [CRED188] EREK MATEO [CRED189] CHRIS DIFATE [CRED190] LEILA MILTON [CRED191] DARREN ZOLTOWSKI [CRED192] VIRGINIA SMITH [CRED193] KEVIN CASSIN [CRED194] JASON SHIGEMORI [CRED195] KELLY KINSELLA [CRED196] MOLLIE STICKNEY [CRED197] STANTON SARJEANT [CRED198] LAURA WALSH [CRED199] MARK GARONE [CRED200] JOANNA SLY [CRED201] ELIZABETH HOWELL [CRED202] ANA HERCULES [CRED203] SHIRLEY IRICK [CRED204] KASHONA FIELDS [CRED205] JOEL M. LILJE [CRED206] JOHN DIBENEDETTO [CRED207] NANCY GILES [CRED208] RYAN CROY [CRED209] JENNIFER KOLBE [CRED210] LIAM BURKE [CRED211] SIGRID PREISSL [CRED212] ANITA FITZSIMONS [CRED213] PHILIPPA RASELLI [CRED214] WIL QUESNEL [CRED215] FALKO BURKERT [CRED216] SARA SEWELL [CRED217] STATIONS DE RADIO ET MUSIQUE [CRED218] CONSULTANT EN MUSIQUE [CRD218A] HEINZ HENN [CRD218B] STUART ROSS [CRED219] COORDINATEUR DE LA BANDE-SON [CRED220] TERRY DONOVAN [CRED221] PRODUCTEUR CHEZ ROCKSTAR GAMES [CRED222] DAN HOUSER ET LAZLOW [CRED223] PRODUCTEUR CHEZ ROCKSTAR NORTH [CRED224] CRAIG CONNER [CRED225] ALLAN WALKER [CRED226] LAZLOW [CRED227] DJ BANTER ET IMAGING [CRED228] ECRIT PAR DAN HOUSER ET LAZLOW [CRED229] FLASH FM [CRD229A] TONI-MARIA CHAMBERS [CRD229B] VOIX ET PRODUCTION : JEFF BERLIN [CRED230] REMERCIEMENTS SPECIAUX A [CRED231] TOMMY MOTTOLA, [CRED232] MICHELLE ANTHONY, [CRED233] STEVE BARNETT, [CRED234] CHUCK FLECKENSTEIN, [CRED235] RITA LIBERATOR [CRED236] MARTIN ET CLAIRE LOGAN [CRED237] SANDRA HUTTON [CRED238] CHRISTINE DAVIDSON [CRED239] ALAN, RED ET BIGFOOT [CRED240] LE T [CRED241] COLIN DONALD [CRED242] KERRY STALLWOOD [CRED243] ALAN MCGREGOR [CRED244] CHRIS MORTON [CRED245] EMIL BUSSE [CRED246] EMILY BAILLIE [CRED247] KEVIN ARCHIBALD [CRED248] MORAG KERR [CRED249] CATH WALKER [CRED250] ISO BAR [CRED251] WATERLINE [CRED252] NEWS CAFE [CRD251A] THE POND [CRD252A] PIVO [CRED253] BUDGET VIDEO RENTALS [CRED254] LORNA'S SCOOTER [CRED255] GARETH MURFIN [CRED256] GRAPHISMES ADDITIONNELS [CRED257] TONY PORTER [CRED258] CRAIG MOORE [CRED259] ANIMATION SYNCHRO LABIALE DES CINEMATIQUES [CRED260] COSGROVE HALL FILMS [CRED261] PRODUCTEUR : OWEN BALLHATCHET [CRED262] ANIMATEUR SENIOR : JON TURNER [CRED263] ANIMATEURS : RICHARD DRUMM [CRED264] DAVE BROWN [CRED265] MAIR THOMAS [CRED266] PRASHANT PATEL [CRED267] CONSULTANT TECHNOLOGIE AUDIO [CRED268] RIK EDE POUR GAMESOUND LTD. [CRED269] ASSISTANCE INTEGRATION DTS [CRED270] TED LAVERTY POUR DTS [CRED271] CHRIS GREER POUR DTS [CRED272] JASON PAGE POUR SCEE [CRED273] RECHERCHE ET ANALYSE [CRED274] VROCK [CRED275] DJ : LAZLOW [CRED276] VOIX : JOE KELLY [CRED277] PRODUCTION : JONATHAN HANST [CRED278] ONDE 103 [CRED279] DJ : ADAM FIRST-JAMIE CANFIELD [CRED280] VOIX : JEN SWEENLEY [CRED281] PRODUCTION : JONATHAN HANST [CRED282] FEVER 105 [CRED283] DJ : OLIVER 'LADYKILLER' BISCUIT-JULIUS DYSON [CRED284] VOIX D'HOMME : ED McMANN [CRED285] VOIX DE FEMME : SHWNEE SMITH [CRED286] PRODUCTION : LISTEN KISTEN [CRED287] EMOTION 98.3 [CRED288] DJ : FERNANDO- FRANK CHAVEZ [CRED289] VOIX : JEN SWEENLEY [CRED290] PRODUCTION : JONATHAN HANST [CRED291] RADIO ESPANTOSO [CRED292] DJ : PEPE-TONY CHILRODES [CRED293] WILDSTYLE [CRED294] DJ : MISTER MAGIC [CRED295] VOIX : FRANK SILVESTRO [CRED296] PRODUCTION : LAZLOW [CRED297] KCHAT [CRED298] ECRIT PAR DAN HOUSER ET LAZLOW [CRED299] PRODUIT ET EDITE PAR LAZLOW [CRED300] DJ AMY SHECKENHAUSEN : LEYNA WEBER [CRED301] JEZ TORRENT : KEVIN MCKIDD [CRED302] MANDY : COLLEEN CORBETT [CRED303] MICHELLE CARAPADIS : MARY BIRDSONG [CRED304] MR.ZOO : CARL DOWLING [CRED305] GETHSEMANEE : LYNN LIPTON [CRED306] CLAUDE MAGINOT : JOHN MAUCERI [CRED307] BJ SMITH : LAWRENCE TAYLOR [CRED308] THOR : FRANK FAVA [CRED309] INTERLOCUTEURS [CRED310] COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS [CRED311] LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, [CRED312] DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, [CRED313] KEITH BROADAS [CRED314] VCPR [CRED315] ECRIT PAR DAN HOUSER ET LAZLOW [CRED316] PRODUIT PAR LAZLOW [CRED317] MAURICE CHAVEZ : PHILLIP ANTHONY RODRIGUEZ [CRED318] JONATHAN FREELOADER : PATRICK OLSEN [CRED319] MICHELLE MONTANIUS : KELLY GUEST [CRED320] ALEX SHRUB : CHRIS LUCAS [CRED321] CALLUM CRAYSHAW : SEAN MODICA [CRED322] JOHN F. HICKORY : LJ GANSEN [CRED323] LE PASTEUR RICHARDS : DAVID GREEN [CRED324] JAN BROWN : MAUREEN SILLIMAN [CRED325] BARRY STARK : RENAUD SEBBANE [CRED326] JENNY LOUISE CRAB : MARY BIRDSONG [CRED327] KONSTANTINOS SMITH : KONSTANTINOS.COM [CRED328] JEREMY ROBARD : PETER SILVESTRO [CRED329] PUBLICITES RADIO [CRED330] ECRITES PAR DAN HOUSER ET LAZLOW [CRED331] PRODUITES PAR LAZLOW [CRED332] JINGLES SUPPLEMENTAIRES PRODUITS PAR CRAIG CONNER [CRED333] VOIX DES PUBLICITES : [CRED334] ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, [CRED335] ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS [CRED336] FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS [CRED337] HARRISON, ED MCMANN, FRANK CHAVEZ, FRANK FAVA, GENE HILGREEN, GREG [CRED338] SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, [CRED339] JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE [CRED340] WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, [CRED341] MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF [CRED342] CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD [CRD344A] ENREGISTREMENTS AUX DIGITAL ARTS STUDIOS, [CRED344] NYC, TRACK 9 STUDIOS, NYC, [CRED345] WEDDINGTON MULTIMEDIA, LOS ANGELES, [CRD345A] SYNC SOUND, NYC ET RADIO LAZLOW, LONG ISLAND. [CRED346] MERCI A AXEL ERICSON ET WON LEE DE DIGITAL ARTS, PAUL VASQUEZ DETRACK 9 STUDIOS, JOHN BOWEN ET JOHN HASSLER DE SYNC SOUND. [CRED347] MARK LLOYD [CRED348] TIM BATES [CRED349] KIT BROWN [CRED350] ANDY MASON [CRED351] PHIL DEANE [CRED352] PHIL ALEXANDER [CRED353] MATT HEWITT [CRED354] DENBY GRACE [CRED355] ANTOINE CABROL [CRED356] JONATHAN STONES [CRED357] MIKE BLACKBURN [CRED358] TIM MCGAFF [CINCAM] Caméra cinématique [RC4] 'RODEO DE RUMPO' [LEGAL] ~g~Elimine toute menace criminelle! [GA_2] Nouveau moteur et nouvelle peinture. Les flics ne te reconnaîtront plus! [HELP15] Si tu es à pied, appuie sur la ~h~~k~~PED_LOOKBEHIND~~w~ pour ~h~regarder derrière~w~. Utilise le ~h~joystick analogique droit~w~ pour ~h~regarder autour~w~ de toi. [FEC_LB4] Regarder derrière (touche R3) [PERPIC] Paquets cachés trouvés [CO_ONE] Paquet caché ~1~ sur ~1~ [GA_21] Impossible de garer plus de véhicules dans ce garage. [CHEAT1] Codes activés [CHEAT2] Code d'arme [CHEAT3] Code de santé [CHEAT4] Code d'armure [CHEAT5] Code d'indice de recherche [CHEAT6] Code d'argent [CHEAT7] Code de météo [USJ_ALL] TOUTES LES CASCADES ONT ETE ACCOMPLIES! [JAN] Jan [FEB] Fév [MAR] Mar [APR] Avr [MAY] Mai [JUN] Jun [JUL] Jui [AUG] Aoû [SEP] Sept [OCT] Oct [NOV] Nov [DEC] Déc [DEFDT] --:---:---- --:--:-- [BONUS] ~g~BONUS $~1~ [HORN1] Appuie sur la ~h~~k~~VEHICLE_HORN~ ~w~pour ~h~klaxonner. [HORN2] Appuie sur la ~h~~k~~VEHICLE_HORN~ ~w~pour ~h~klaxonner. [HORN3] Appuie sur la ~h~~k~~VEHICLE_HORN~ ~w~pour ~h~klaxonner. [FEC_EXV] Monter et descendre d'un véhicule [TAXI_M] 'CHAUFFEUR DE TAXI' [COP_M] 'AUTODEFENSE' [FIRE_M] 'POMPIER' [AMBUL_M] 'AMBULANCE' [HJ_IS] BONUS DE CASCADE DANGEREUSE : $~1~ [HJ_PIS] BONUS DE CASCADE DANGEREUSE PARFAITE : $~1~ [HJ_DIS] BONUS DE DOUBLE CASCADE DANGEREUSE : $~1~ [HJ_PDIS] BONUS DE DOUBLE CASCADE DANGEREUSE PARFAITE : $~1~ [HJ_TIS] BONUS DE TRIPLE CASCADE DANGEREUSE : $~1~ [HJ_PTIS] BONUS DE TRIPLE CASCADE DANGEREUSE PARFAITE : $~1~ [HJ_QIS] BONUS DE QUADRUPLE CASCADE DANGEREUSE : $~1~ [HJ_PQIS] BONUS DE QUADRUPLE CASCADE DANGEREUSE PARFAITE : $~1~ [FESZ_LS] Chargement effectué. [HELI_1A] Teste tes capacités avec le Sparrow et vois en combien de temps tu peux réussir le parcours. [HELI_1B] Parcours réussi! [HELIODD] Petits boulots en hélico [INT2_M] Ils sont proprios d'une ferme au Panama! [INT2_N] Ok, écoutez un peu... [INT2_O] Les gars, quand on sera là-bas, est-ce que je dois rester dans la bagnole, [INT2_P] où bien est-ce que vous voulez absolument que je vienne avec vous? [INT2_Q] Non, Reste dans la bagnole. [INT2_R] Vous savez quoi, j'ai réfléchi à la question, [INT2_S] Je vais surveiller la bagnole. [INT4_A] On est baisés! Baisés! [INT4_B] Putain, c'est vraiment le coup classique! [INT4_C] Je sors la tête d'un caniveau pour une seconde de défonce, [INT4_D] et le destin me chie de la merde plein la gueule! [INT4_E] Allez vous faire mettre! [INT4_F] Ferme-la et arrêter de gueuler! T'es encore vivant, pas vrai? [INT4_G] Laisse-moi là. [INT4_H] Débarrasse-toi de la caisse et va roupiller un bon coup. [INT4_I] Je passerai à ton bureau demain et on essayera de trouver une solution à ce merdier. [INT4_J] Ouais, bonne idée, je vais aller roupiller. [INT4_K] Qu'est-ce que tu vas faire? [INT4_L] Rentrer à mon hôtel. [INT4_M] Et penser à autre chose que ces conneries. [INT4_N] Ok. [LAW] MISSIONS AVOCAT [LAW1_1] ~g~Va te procurer de nouvelles fringues à la boutique de Rafael. [LAW4_6] Brûlez la direction! [LAW4_7] Tuez les patrons! [LAW4_8] Allez! Battez-vous! Allez! [LAW4_9] Plus de vacances! Moins de travail! [LAW4_11] Allez! Battez-vous! Allez! [LAW4_12] Viva la revolucion! [GENERAL] MISSIONS DU COLONEL [GEN3_4] Tommy Vercetti. Allons-y... [GEN3_13] C'est quoi ton problème, mec? Monte sur le toit de l'autre côté de la cour avant qu'ils rappliquent! [GEN3_17] Meeeerde! T'essayes de m'buter ou quoi? [GEN3_21] ~g~Il a le pognon de Diaz! Attrape-le et récupère le fric! [GEN3_24] ~r~Diaz est mort! Tu devais le protéger! [GEN3_26] ~r~T'as flingué Diaz! [GEN3_27] ~r~T'as descendu les gardes du corps de Diaz! [GEN3_31] ~g~Va au rencart et veille sur Diaz. [GEN3_32] ~g~Va te mettre en position sur le toit du bâtiment en face de Lance. [COKE] MISSIONS BARON DE LA COKE [COK1_3] J'espère que tu vas te casser la gueule! [COK1_6] J'en ai marre de ces merdeux. [COK2_7] Tu vois ces marqueurs? Essaye de flinguer les lumières! [COK2_10] En tout cas, tu tires mieux que tu causes. [COK2_11] Merci. T'as un certain charme aussi. [COK2_12] Je sais, Tommy. [COK2_18] T'aimes bien Kenny Loggins? [COK2_19] Tu parles, j'adore ce disque ouais! [COK2_26] ~r~T'as buté Lance! [COK3_1] Tire pas, mec! [COK3_2] Qu'est-ce que c'est que ça? [COK3_3] Il embarque le bateau! Merde! [COK3_4] A l'aide! Il y a un mec qui nous fauche le bateau! [COK4_W] Bon! C'est le dernier. [COK4_X] Je vais mettre le moteur en route. [COK4_Y] Je crois qu'on a de la visite... [COK4_2] Ouais. [COK4_6] Tu sais où on va? [COK4_7] On est paumés? [COK4_8] On a de la concurrence! [COK4_9] Bousille-les! [COK4_9A] L'heure de la danse de Lance Vance a sonné! [COK4_10] Réduits en miettes! Bons à nourrir la poiscaille! [COK4_11] On a réussi! Les autres bateaux ne sont pas de taille. [COK4_17] Ils sont prêts à tout! [COK4_18] J'ai les pieds mouillés! On prend la flotte! [COK4_21] Pont droit devant! [COK4_22] Ecope, on va chavirer! [COK4_23] Joli coup. [COK4_29] ~r~T'as buté Lance! [ASS1_6] Continue, Tommy, ça va aller! [ASS1_7] Prenez ça, enculés d'assassins! [ASS1_8] Je suis coincé! [ASS1_9] Je te couvre, Tommy! [ASS1_10] Hé, c'est chouette toutes ces plantes... [ASS1_11] Hé, Tommy, je peux avoir une chambre avec vue sur la baie? [ASS1_12] Il y a vraiment de superbes plafonds, ici... [ASS1_3] Lance! Couvre-moi! [ASS1_5] Lance! [ASS1_15] ~g~Attaque la résidence et tue Diaz! [ASS1_17] ~g~Il existe de nombreux accès à la résidence. [TAXWAR] MISSIONS GUERRE DES TAXIS [NOTAXI] ~g~T'as besoin d'un taxi Kaufman pour activer cette mission. [TAXW1_5] ~g~Faut que tu sois dans un taxi Kaufman! [TAX2_4] Vas-y, Tommy. [TAX2_5] Massacre-lui la tête. [TAX2_6] Il a même pas son permis. [TAX2_7] Putain de limousines! [TAXW3_1] ~g~Va prendre Mercedes. [RACE1] ~g~3..2..1.. PARTEZ! [RACE2] ~g~3 [RACE3] ~g~2 [RACE4] ~g~1 [RACE5] ~g~PARTEZ! [FIRST] ~b~1er [SECOND] ~b~2e [THIRD] ~b~3e [FOURTH] ~b~4e [RACETM] ~b~TEMPS DE COURSE : ~1~:~1~ [RACETM2] ~b~TEMPS DE COURSE : ~1~:0~1~ [RACEFA] ~r~Tu n'as pas gagné la course! [TEX1_5] ~r~Il s'est tiré! [SEG3_1] TEMPS: [SEG3_2] ~g~Va à la camionnette qui contient le bombardier télécommandé et les bombes à retardement. [SEG3_3] ~g~Utilise le bombardier télécommandé pour transporter 4 bombes vers 4 zones cibles sur le chantier. [SERG3_5] ~g~Tu ne peux transporter qu'une bombe à la fois et tu ne peux récupérer une bombe déjà larguée avec succès. [SEG3_7] ~g~Une fois que tu as largué la première bombe sur une zone cible, le compte à rebours s'enclenche. Tu dois larguer toutes les bombes avant la fin de celui-ci. [SEG3_8] ~g~Les 4 bombes doivent être larguées sur les 4 zones cibles pour réussir la mission et détruire le bâtiment. [SEG3_9] ~g~Tu as touché la cible! Plus que 3! [SEG3_10] ~g~Tu as touché la cible! Plus que 2! [SEG3_11] ~g~Tu as touché la cible! Plus qu'une! [SEG3_12] ~r~Tu as raté la cible! Va récupérer une bombe! [SEG3_13] ~g~Largue la bombe sur une zone cible. [SEG3_14] ~r~Le temps est écoulé et tu n'as pu détruire le bâtiment. [SEG3_15] ~r~Ton bombardier télécommandé a été détruit! Comment tu vas transporter les bombes, maintenant? [AVERY] MISSIONS AVERY [ASM] MISSIONS ASSASSIN [ASM_1] MISSION ASSASSIN 1 [ASM1_1] ~g~Ton aide dans l'éradication de ces indésirables fut une excellente affaire. J'ai un autre travail pour toi. Regarde sous le téléphone. [ASM1_2] ~g~Rends-toi à la cabine en dehors du centre commercial à Washington. [ASM1_3] ~g~Carl Pearson, livreur de pizzas. Il ne doit pas terminer sa tournée. [ASM1_4] ~g~Tue le livreur de pizzas avant qu'il le livre toutes ses pizzas. [ASM_2] MISSION ASSASSIN 2 [ASM_3] MISSION ASSASSIN 3 [ASM3_A] Marcus Hammond, Franco Carter, Dick Tanner, Nick Kong et Stuntman Driver appartiennent tous au syndicat européen qui s'apprête à faire un hold-up. [ASM3_B] Ils sont tous en position. Il faut qu'ils soient tous morts avant le commencement. Tu as 9 minutes. J'ai laissé quelques flingues à proximité qui devraient t'être utiles. [ASM3_1] ~g~Va récupérer l'arme que M. Black a laissé pour toi. [ASM3_2] ~g~Ne t'approche pas trop près de la cible ou tu risques d'être repéré. [ASM3_3] ~g~Pour un travail propre et rapide, installe-toi près de leurs positions dans des emplacements d'où tu pourras facilement les liquider, sans être repéré. [ASM3_4] ~g~Il t'a vu! Vaudrait mieux le buter en vitesse, maintenant! [ASM3_5] ~g~Marcus Hammond est en position près des panneaux publicitaires dans Washington. [ASM3_6] ~g~Franco Carter est en position à côté de DBP Security non loin d'Ocean Drive. [ASM3_7] ~g~Dick Tanner est en position à côté de la bijouterie dans Vice Point. [ASM3_8] ~g~Nick Kong est en position près de Washington Beach. [ASM3_9] ~g~Stuntman Driver est en position à Washington. [ASM3_10] ~r~Tu n'as pas réussi à tous les descendre. [ASM_4] MISSION ASSASSIN 4 [ASM4_1] ~g~Va récupérer le fusil laissé à ton intention dans le feuillage à l'extérieur du terminal de l'aéroport. [ASM4_2] ~g~Ne rate pas la cible où tu alerteras ses gardes du corps et garde tes distances pour qu'il ne te repère pas. [ASM4_3] ~g~Observe la femme sur le balcon au-dessus des comptoirs d'enregistrements du terminal. NE LA TUE PAS. [ASM4_4] ~g~Tue l'homme à qui elle donnera la serviette, mais seulement quand il l'aura récupérée. Prends ensuite la serviette et amène-la à Ammu-Nation dans le centre. [ASM4_5] ~g~Récupère la serviette! [ASM4_6] ~g~Amène la serviette à Ammu-Nation dans le centre! [ASM4_7] ~r~Imbécile! Tu as tué la femme! [ASM4_8] ~r~La cible t'a entendu tirer! Le deal est annulé! [ASM4_9] ~r~La cible a embarqué à bord de son avion! [ASM4_10] ~r~On dirait que tu n'es pas le seul à t'intéresser à cette serviette! Amène-la en vitesse à Ammu-Nation! [ASM4_11] ~r~La cible t'a repéré! Le deal est annulé! [ASM4_13] ~g~Il t'a repéré et essaye de s'enfuir! Rattrape-le et récupère la serviette! [ASM4_14] ~g~La barre de distance dans le coin supérieur droit de l'écran te signale ta proximité avec la cible. Ne la laisse pas devenir pleine ou il te repèrera. [ASM_5] MISSION ASSASSIN 5 [KICK] DEMARRAGE AU PIED [KICK1_3] ~g~Nombre de fautes de pied : ~1~ [KICK1_4] ~g~Pénalité de temps : ~1~ secondes [BANK] MISSIONS BRAQUAGE [BANK1] MISSION BRAQUAGE 1 [BANK2] MISSION BRAQUAGE 2 [BJM2_3] TAUX DE REUSSITE : ~1~% [BJM2_15] SCORE : [BJM2_18] SCORE A BATTRE : [BJM2_19] ~g~Touche autant de cibles que possible dans le temps imparti! [BJM2_21] ~g~Touche autant de cibles que possible jusqu'à épuisement de tes munitions. [BANK3] MISSION BRAQUAGE 3 [BJM3_1] ~g~Trouve une bagnole qui a des chevaux et rends-toi sur la grille de départ. [BNK4_2A] Les gars au garage ont fait du super boulot sur ce bébé. [BNK4_3G] Oh, merde, maintenant, on a les flics au cul! [BNK4_3H] Et on n'est même pas encore sur place... [BNK4_3K] Va d'abord falloir semer les flics... [BNK4_3L] Merde, Tommy, t'essayes de tous nous tuer? [BNK4_3N] Tout ce que j'aime part en fumée! [BNK422A] Cam, combien de temps? [BK4_23A] Donne-moi 3 minutes! [BNK4_26] Bordel de merde! Les voilà! [BNK4_32] Sers-toi des explosifs pour ouvrir les coffres! [BNK4_43] Je nous couvre, FONCE! [BNK4_51] Je te trouve très bien comme ça. [KENT] MISSIONS KENT PAUL [KENT1] MISSION KENT PAUL 1 [COUNT] MISSIONS CONTREFACON [COUNT1] MISSION CONTREFACON 1 [COUNT2] MISSION CONTREFACON 2 [BIKE] MISSIONS BIKERS [BIKE1] MISSION BIKERS 1 [BIKE2] MISSION BIKERS 2 [BIKE3] MISSION BIKERS 3 [GOAWAY1] Reviens quand tu auras fini les missions du gang haïtien. [HAIT] MISSIONS GANG HAITIEN [HAIT1] MISSION GANG HAITIEN 1 [HAIT2] MISSION GANG HAITIEN 2 [HAIT3] MISSION GANG HAITIEN 3 [HAM3_6] ~g~Utilise le fusil à lunette que je t'ai donné pour les tuer! [ROCK] MISSIONS GROUPE DE ROCK [ROK1_4] ~g~Ok, je crois que c'est ce que vous vouliez... [ROK1_1E] ~g~Ca te coûtera plus que ce que tu as! [ROK1_1F] ~g~Reviens quand tu auras le pognon! [RBM2_6] ~g~Waou! C'est un mec! Arrête-le! [ROCK3] MISSION GROUPE DE ROCK 3 [ROK3_6D] ~r~en même temps que vos sales grosses têtes chevelues! [ROK3_40] Près de la glacière? [RBM3_5] ~g~Emmène les Love Fist au concert. [CUBANM] MISSIONS GANG CUBAIN [CUBAN1] MISSION GANG CUBAIN 1 [CUBAN2] MISSION GANG CUBAIN 2 [CUB2_10] ~r~T'es supposé tuer des Haïtiens, pas des Cubains! [CUBAN3] MISSION GANG CUBAIN 3 [CUBAN4] MISSION GANG CUBAIN 40 [CUB4_25] Ok, allons-y! [PROT] MISSIONS DE PROTECTION [PRO1_H] Arrêtez de vous exciter, tous les deux. Je commence à peine à voir comment les choses marchent ici. [PRO1_02] ~g~Sors du centre commercial. [PRO3_06] ~g~Sème les flics. [PORN] MISSIONS PORNO [PORN1] MISSION PORNO 1 [POR1_03] ~r~Candy est morte! [PORN2] MISSION PORNO 2 [PORN3] MISSION PORNO 3 [POR3_18] T'as été repéré! [PORN4] MISSION PORNO 4 [POR4_04] ~g~Les bureaux se trouvent de l'autre côté de cette porte. [PHIL] MISSIONS PHIL [PHIL1] MISSION PHIL 1 [PHI1_06] Qu'est-ce que tu fous à conduire comme ça? [PHI1_07] Hé! [PHIL2] MISSION PHIL 2 [PIZ1_A] MISSION LIVREUR DE PIZZA [PIZ1_03] ~g~Retourne à la pizzeria pour d'autres commandes. [PIZ1_04] ~g~Voilà tes nouvelles commandes. [PIZ1_10] Appuie sur la ~h~ touche R3 ~w~ pour annuler les missions pizzas. [CNTBUY1] Imprimerie achetée : $~1~ [CARBUY] Concession achetée : $~1~ [PORNBUY] Studio de cinéma acheté: $~1~ [ICEBUY] Usine de crème glacée achetée: $~1~ [TAXIBUY] Compagnie de taxis achetée: $~1~ [BANKBUY] Club Malibu acheté : $~1~ [BOATBUY] Chantier naval acheté: $~1~ [PRNT_NO] Tu ne peux pas acheter l'imprimerie pour l'instant, reviens plus tard. [CAR_NO] Tu ne peux pas acheter la concession automobile pour l'instant, reviens plus tard. [PORN_NO] Tu ne peux pas acheter le studio de cinéma pour l'instant, reviens plus tard. [ICE_NO] Tu ne peux pas acheter l'usine de crème glacée pour l'instant, reviens plus tard. [TAXI_NO] Tu ne peux pas acheter la compagnie de taxis pour l'instant, reviens plus tard. [BANK_NO] Tu ne peux pas acheter le club Malibu pour l'instant, reviens plus tard. [BOAT_NO] Tu ne peux pas acheter le chantier naval pour l'instant, reviens plus tard. [PRNT_R3] Appuie sur la touche R3 pour acheter l'imprimerie au prix de $~1~ [CAR_R3] Appuie sur la touche R3 pour acheter la concession automobile au prix de $~1~ [PORN_R3] Appuie sur la touche R3 pour acheter le studio de cinéma au prix de $~1~ [ICE_R3] Appuie sur la touche R3 pour acheter l'usine de crème glacée au prix de $~1~ [TAXI_R3] Appuie sur la touche R3 pour acheter la compagnie de taxis au prix de $~1~ [BANK_R3] Appuie sur la touche R3 pour acheter le club Malibu au prix de $~1~ [BOAT_R3] Appuie sur la touche R3 pour acheter le chantier naval au prix de $~1~ [COL2_6] Arrête espèce de porc impérialiste d'Américain! [COL2_6B] Ceci est la propriété du gouvernement français! [COL2_6C] Et c'est terminé. [COL3_A] Thomas, merci d'être venu. [COL3_B] Désolé d'aller droit au but... [COL3_C] Diaz m'a demandé de superviser une petite transaction financière. [COL3_D] Espérons que ça se passera mieux que la dernière fois. [COL3_E] C'est la raison pour laquelle j'ai pensé à vous, mon ami. [COL3_F] J'ai laissé un revolver au parking à niveaux. [COL3_G] Récupèrez-le et allez surveiller les mecs de Diaz au dépôt. [COL4_2] Je sais pas, chef! [COL4_5] Chef, oui, chef! [COL4_10] Allons manger quelques beignets. [COL4_16] Chef, on déplace le véhicule, chef! [COL4_25] Autodestruction du véhicule initialisée! [COL5_6] Mercedes, cette fille aura ma peau. [COL5_8] Maudit cafard! [COL5_5] Crevez, porcs de Français! [CNT2_1] Tuez-le! [CNT2_2] Récupérez les plaques! [CNT2_3] Protégez le coursier! [FINKILL] Ok, les mecs, butez-le! [FIN_6] Sonny est en haut avec le coffre et MON argent... [FIN_10] Sonny? SONNY! Je viens te chercher! [RACES_4] 3 [RACES_5] 2 [RACES_6] 1 [RACES_7] PARTEZ! [RACES_9] Temps : ~1~:~1~ [RACES] TEMPS : [RACES17] Nv temps record : ~1~:~1~ [RACES20] Nv temps record : ~1~:0~1~ [RACES21] Temps : ~1~:0~1~ [RCH1_1] ~g~Utilise l'hélicoptère radiocommandé, le RC Raider, pour PASSER les points de passage. [RCH1_2] ~g~Les POINTS DE PASSAGE sont disséminés dans l'aéroport. [RCH1_3] ~g~Tu as ~c~8 minutes~g~ pour passer les ~c~20! [RCH1_5] Temps [RCRC1_1] ~g~Fais une COURSE DE POINTS DE PASSAGE contre deux autres RC Bandits sur 2 TOURS [RCRC1_2] ~g~Va sur la grille de départ! [RCRC1_3] ~g~Dernier tour! [RCRC1_4] ~g~3 [RCRC1_5] ~g~2 [RCRC1_6] ~g~1 [RCRC1_7] ~g~PARTEZ! [RCRC1_8] ~g~Temps de course : ~1~ secondes [RCPL1_1] ~g~Fais une COURSE DE POINTS DE PASSAGE contre 3 autres RC Baron [RCPL1_2] ~g~Tu dois passer par le ~o~CENTRE CORONA ~g~pour valider un point de passage. [RCPL1_3] ~g~Va sur la grille de départ maintenant! [FEA_2SP] 2 haut-parleurs [FEA_4SP] Plus de 2 haut-parleurs [FEA_EAR] Casque [FEA_NAH] PAS DE MATERIEL AUDIO [FET_APP] APPLIQUER [FES_SKN] NOM DU SKIN [FES_DAT] DATE [FES_SET] Utiliser Skin [FET_DEF] Par défaut [FESZ_QZ] Veux-tu vraiment sauvegarder cette partie? [FES_SCG] Sauvegarder la partie en cours? [FES_LCG] Charger la partie et continuer à jouer? [FEC_FIR] Tirer [FEC_NWE] Arme suivante [FEC_PWE] Arme précédente [FEC_FOR] Avant [FEC_BAC] Arrière [FEC_LEF] Gauche [FEC_RIG] Droite [FEC_ZIN] Zoom avant [FEC_ZOT] Zoom arrière [FEC_EEX] Entrer+sortir [FEC_RAD] Radio [FEC_SUB] Sous-mission [FEC_CMR] Changer caméra [FEC_JMP] Sauter [FEC_SPN] Courir [FEC_HND] Frein à main [FEC_LOL] Regarder à gauche [FEC_LOR] Regarder à droite [FEC_NTR] Cible suivante [FEC_PTT] Cible précédente [FEC_LBA] Regarder derrière [FEC_CEN] Centrer caméra [FET_CCN] Classique [FET_SCN] Standard [FET_CFT] A PIED [FET_CCR] EN VOITURE [FET_CAC] ACTION [FEC_IBT] - [FEC_MXO] MXB1 [FEC_MXT] MXB2 [FEC_UNB] NON LIE [FEC_TFL] Regard gauche+tourelle G [FEC_TFR] Regarde droite+tourelle D [FEC_MWF] VOLANT MS HAUT [FEC_MWB] VOLANT MX BAS [FEC_ORR] ou [FEC_NUS] NON UTILISE [FEC_LUD] Regarder en haut [FEC_LDU] Regarder en bas [FEC_CMP] COMBO : REGARDER G+D [LAW_1A] law_1a [LAW_1B] law_1b [LAW_2A] law_2a [LAW_2B] law_2b [FEH_STA] STATS [FEH_LOA] CHARGER [FEH_CON] COMMANDES [FEH_AUD] AUDIO [FEH_DIS] AFFICHAGE [FEH_LAN] LANGUE [FEH_SGA] LANCER NOUVELLE PARTIE [FEO_CON] Configuration manette [FEO_AUD] Configuration audio [FEO_DIS] Configuration affichage [FEO_LAN] Choix langue [FEO_PLA] Configuration joueur [FEA_OUT] Sortie [FEA_ST] Stéréo [FEA_DTS] DTS [FEA_RSS] Station de radio [FEA_NON] RADIO ETEINTE [FEA_FM0] WILDSTYLE [FEA_FM1] FLASH FM [FEA_FM2] KCHAT [FEA_FM3] FEVER 105 [FEA_FM4] VROCK [FEA_FM5] VCPR [FEA_FM7] EMOTION 98.3 [FEA_FM8] ONDE 103 [FED_BRI] Luminosité [FED_TRA] Rémanences : [FED_SUB] Sous-titres [FED_WIS] Ecran large [FED_POS] Position écran [FEP_RES] Reprendre [FEP_STG] Commencer partie [FEP_STA] Stats [FEP_BRI] Briefings [FEP_OPT] Options [FEP_QUI] Quitter partie [FES_LOA] Charger partie [FES_DEL] Effacer partie [FEC_CSU] Configuration manette [FEC_RED] Réassigner commandes [FEC_MOU] Paramètres souris [DISTGOL] Dist. parcourue en voiturette (miles) [DISTGOM] Distance parcourue en voiturette (m) [ST_FAVR] Station de radio préférée [ST_WSTR] Station de radio la moins écoutée [ST_FAVV] Véhicule préféré [ST_STAR] Total d'étoiles de recherche obtenues [ST_HEAD] Nombre de tirs à la tête [ST_GANG] Gang le moins apprécié [ST_STGN] Total d'étoiles de recherche évitées [TYREPOP] Pneus crevés avec des balles [TYRESLA] Pneus lacérés avec une lame [ST_BRK] Nombre de victimes dans le Bloodring [ST_LTBR] Plus longue durée dans le Bloodring (sec) [ST_GNG1] Cubains [ST_GNG2] Haïtiens [ST_GNG3] Faux voyous [ST_GNG4] Gang de Diaz [ST_GNG5] Agents de sécurité [ST_GNG6] Gang de motards [ST_GNG7] Gang de Vercetti [ST_GNG8] Golfeurs [FEA_FM6] ESPANTOSO [ST_ASSI] Contrats exécutés [DISTBIK] Dist. parcourue à moto (miles) [DISTBIM] Dist. parcourue à moto (m) [HOTEL] Hôtel Ocean View [ICC1_1] ~g~Utilise ta camionnette de crème glacée pour distribuer de la drogue à Vice City. [ICC1_2] ~g~Gare ta camionnette et appuie sur ~h~~k~~VEHICLE_HORN~~w~ pour jouer le jingle et signaler à tes clients que tu es prêt. [ICC1_3] ~g~Tu reçois de l'argent pour chaque transaction faite, mais plus tu fais d'affaires, plus tu attires l'attention de la police. [KICK1_9] POINTS DE PASSAGE RESTANTS : [FIN_B6] Tu n'as pas assez d'argent pour commencer cette mission. [TEX3_3] ~g~Pour ramasser une bombe, manoeuvre l'hélico au-dessus d'elle. La bombe est alors automatiquement fixée sous la carlingue. [TEX3_9] ~g~Ramasse une bombe en manoeuvrant l'hélico à côté d'elle. [HELP22] Va vers la position du bip de la maison verte sur le radar. [FES_FMS] Formatage terminé. Sélectionne OK pour continuer. [FES_SSC] Sauvegarde terminée. Sélectionne OK pour continuer. [FES_DSC] Suppression terminée. Sélectionne OK pour continuer. [FESZ_QC] Ecraser ce fichier de sauvegarde corrompu? [FES_CHE] Attention! Un ou plusieurs codes ont été activés, ce qui peut affecter votre fichier de sauvegarde. Sauvegarde déconseillée. [FET_SG] SAUVEGARDER [FEH_BRI] BRIEFING [FEH_MAP] CARTE [FEM_OK] OK [FEC_CRO] S'accroupir [FEC_CR3] S'accroupir (touche L3) [FEC_SMT] Sous-mission [FEC_SM3] Sous-mission (touche R3) [FEC_RSC] Stations de radio [ST_PR01] Pilote [ST_PR02] Soldat armée de l'air [ST_PR03] Officier pilote [ST_PR04] Caporal [ST_PR05] Lieutenant [ST_PR06] Sergent [ST_PR07] Capitaine [ST_PR08] Biggs [ST_PR09] Wedge [ST_PR10] Baron rouge [ST_PR11] Goose [ST_PR12] Viper [ST_PR13] Jester [ST_PR14] Chappy [ST_PR15] Iceman [ST_PR16] Maverick [ST_PR17] Noops [ST_PR18] Maréchal de l'air [ST_PR19] As [FET_LG] Charger partie [CAR_EXP] Véhicules routiers détruits [BOA_EXP] Bateaux détruits [HEL_DST] Avions et hélicoptères détruits [STFT_01] Meilleur temps sur 'Alliage d'Acier' [STFT_02] Meilleur temps sur 'Le chauffeur' [STFT_03] Meilleur temps sur Dirt Ring [STFT_04] Meilleur temps course avion télécommandé [STFT_05] Meilleur temps course voiture télécommandée [STFT_06] Meilleur temps hélico télécommandé [STFT_07] Meilleur temps sur 'Terminal Velocity' [STFT_08] Meilleur temps sur 'Ocean Drive' [STFT_09] Meilleur temps sur 'Border Run' [STFT_10] Meilleur temps sur 'Capital Cruise' [STFT_11] Meilleur temps sur 'Virée!' [STFT_12] Meilleur temps sur 'Endurance V.C.' [STHC_01] Meilleur score sur Le Flingueur. [STHC_02] Meilleur pourcentage de tir au but sur le Flingueur. [STHC_03] Nombre de deals de drogue [HELP24] Tu peux maintenant bosser pour le colonel. [HELP25] Tu peux maintenant bosser pour Avery Carrington [HELP29] Tu peux te rendre au magasin de fringues quand tu n'es pas en mission. [HELP30] Quand tu achètes de nouvelles fringues, ton indice de recherche retombe à zéro. [BJM2_22] ~r~Tu as quitté le stand de tir! [BJM2_23] ~g~Si tu quittes le stand de tir pendant la compétition, la mission est un échec. [ASM4_24] Distance : [RBM1_6] ~g~Ramène Mercedes et le Love Juice au groupe, au studio d'enregistrement. [RBM1_3] PLUS NECESSAIRE [HAM1_5] PLUS NECESSAIRE [RBM1_11] PLUS NECESSAIRE [HELP31] Pour réaliser un arrosage latéral, regarde d'abord à droite ou à gauche en utilisant ~k~~VEHICLE_LOOKLEFT~ ou ~k~~VEHICLE_LOOKRIGHT~. [HELP34] Tu dois être armé d'une mitraillette pour réaliser un arrosage latéral. [STRIP_1] ~r~T'as pas assez de pognon, pauvre fauché! [EXIT_1] Appuie sur ~k~~PED_SPRINT~ pour sortir. [ASM1_A] M. Teal, votre aide dans l'éradication de ces indésirables fut une excellente affaire. J'ai un autre travail plus en finesse pour vous. [ASM1_B] Regardez sous le téléphone. [ASM1_C] J'ai un autre travail plus en finesse pour vous. [SCARF] Appartement 3c [LAW4_10] Les patrons sont tous des salauds! [GEN1_04] ~g~Passe la porte pour accéder au toit de Gonzalez. [GEN1_17] ~g~Gonzalez se fait la malle! Suis-le et finis-en avec lui! [RCH1_9] ~b~TEMPS TOTAL : ~1~:~1~ [RCH1_10] ~b~TEMPS TOTAL : ~1~:0~1~ [WHEEL01] DOUBLE BONUS DEUX ROUES : $ ~1~ Distance : ~1~.~1~m Temps : ~1~ secondes [WHEEL02] DOUBLE BONUS DEUX ROUES : $ ~1~ Distance : ~1~ feet Temps : ~1~ secondes [WHEEL03] BONUS DEUX ROUES : $ ~1~ Temps : ~1~ secondes [WHEEL04] BONUS DEUX ROUES : $ ~1~ Distance : ~1~.~1~m [WHEEL05] BONUS DEUX ROUES : $ ~1~ Distance : ~1~ pieds [WHEEL06] BONUS ROUE ARRIERE : $ ~1~ Distance : ~1~.~1~m Temps : ~1~ secondes [WHEEL07] BONUS ROUE ARRIERE : $ ~1~ Distance : ~1~ feet Temps : ~1~ secondes [WHEEL08] BONUS ROUE ARRIERE : $ ~1~ Temps : ~1~ secondes [WHEEL09] BONUS ROUE ARRIERE : $ ~1~ Distance : ~1~.~1~m [WHEEL10] BONUS ROUE ARRIERE : $ ~1~ Distance : ~1~ pieds [WHEEL11] BONUS ROUE AVANT : $ ~1~ Distance : ~1~.~1~m Temps : ~1~ secondes [WHEEL12] BONUS ROUE AVANT : $ ~1~ Distance : ~1~ pieds Temps : ~1~ secondes [WHEEL13] BONUS ROUE AVANT: $ ~1~ Temps : ~1~ secondes [WHEEL14] BONUS ROUE AVANT : $ ~1~ Distance : ~1~.~1~m [WHEEL15] BONUS ROUE AVANT : $ ~1~ Distance : ~1~ pieds [ROK3_72] Love Fist! [POR1_19] Hé! [DESPERA] Desperado [MOB_99A] Rends-toi à la cabine près du centre commercial à Washington. [MOB_98A] Rends-toi à la cabine dans Vice Point. [MOB_96A] Rends-toi à la cabine du terminal de l'aéroport. [MOB_95A] Rends-toi à la cabine dans Little Havana. [BNK1_1] Je peux vous aider, monsieur? [BNK1_2] Il y a un imposteur! [BNK1_3] Il a pêté les plombs! [BNK1_4] Bon sang, t'es qui, toi? [BNK1_5] Où es ton badge? [BNK1_6] Les voilà! Descendez-les! [MOB_24A] Bonjour, c'est M. Vercetti? [MOB_24B] Oui. [MOB_24C] ici Cortez. Vous étiez à ma fête. [MOB_24D] Oui, je me souviens. [MOB_24E] M. Vercetti, c'est un regrettable incident, ce qui s'est produit avec votre affaire. [MOB_24F] Je sais. [MOB_24G] Je veux que vous sachiez que mes hommes et moi, faisons le maximum pour tirer ça au clair. [MOB_24H] Si vous souhaitez me parler en privé, vous me trouverez sur le bateau. Au revoir, senor. [BNK2_2] VISEZ 3-2-1 FEU! [BNK2_3] ZONE DEGAGEE! [BNK2_6] Ce mec est cinglé! [ANGEL] Angel [CUBJET] Jetmax cubain [SANDKIN] Sandking [POLMAV] Maverick Police [BOXVILL] Boxville [BENSON] Benson [HOTRINA] Hotring Racer [HOTRINB] Hotring Racer [BLOODRA] Bloodring Banger [BLOODRB] Bloodring Banger [MAFIACR] Mafia Cruiser [COP_M2] 'VICE SQUAD' [COP_M3] 'BROWN THUNDER' [BJM2_20] ~g~Quand tu n'as plus de ~w~temps ~g~ou de ~w~munitions ~g~, la manche est terminée! [BNK3_2] Pas question que je conduise pour toi! J'en parlerai pendant la réunion de groupe! [FEM_SL1] Pas de sauvegarde 1 [FEM_SL2] Pas de sauvegarde 2 [FEM_SL3] Pas de sauvegarde 3 [FEM_SL4] Pas de sauvegarde 4 [FEM_SL5] Pas de sauvegarde 5 [FEM_SL6] Pas de sauvegarde 6 [FEM_SL7] Pas de sauvegarde 7 [FEM_SL8] Pas de sauvegarde 8 [FEA_CHA] Passage en mode STEREO. Patiente un instant... [FEA_CHD] Attention! Tu es en train de passer du mode STEREO au mode DTS. Patiente un instant... [FEI_SEL] Sélectionner [FEI_BAC] Retour [FEI_RES] Reprendre [FEI_NAV] Explorer [FEI_BTX] Touche / - [FEI_BTT] Touche " - [FEI_STA] Touche START - [FEI_BTD] ; = > < - [FEI_STO] Arrêter [MOB_68A] Tommy, mon fiston, j'ai une surprise pour toi! [MOB_68B] Je suis au studio d'enregistrement avec quelques artistes réputés. [MOB_68C] Pourquoi tu ne viendrais pas faire un tour? [MOB_68D] C'est une bonne idée, non? Allez, à plus tard. C'est plutôt normal, non? Allez, à plus tard. [OUTFT1] Streetwear [OUTFT2] Soirée [OUTFT3] Bleu de travail [OUTFT4] Country club [OUTFT5] Cubano [OUTFT6] Flic [OUTFT7] Braqueur [OUTFT8] Décontracté [OUTFT9] M. Vercetti [OUTFT10] Survêtement [OUTFT13] MC Tommy [HOTR_07] Nouveau record : ~1~:0~1~ [HOTR_08] Temps : ~1~:~1~ [GEN3_45] Ils seront là d'une minute à l'autre. On ferait mieux de se trouver une bonne planque... [CAR_AS1] CONCESSION AUTOMOBILE ACQUISE [CAR_AS2] ~g~Sunshine Autos génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. [BUYSAVE] ~g~Puisque tu n'es pas en mission, tu peux sauvegarder ta partie gratuitement. [BUYGARG] ~g~Tu peux aussi garer des véhicules dans ce garage. [STRPBUY] Club Pole Position acquis : $~1~ [STRP_R3] Pour acheter le club Pole Position pour $~1~, appuie sur la touche R3. [NBMN_R3] Pour acheter Elswanko Casa pour $~1~, appuie sur la touche R3. [GA_4] Les bombes pour voiture coûtent $1000 pièce. [GA_5] Ta caisse est déjà équipée d'une bombe. [GA_6] { reVC update } Gare-toi, amorce-la en appuyant sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ et BARRE-TOI vite! [GA_7] { reVC update } Amorce la bombe en appuyant sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~. Elle explosera au démarrage. [GA_6B] { reVC update } Gare-toi, amorce-la en appuyant sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ et BARRE-TOI vite! [GA_7B] { reVC update } Amorce la bombe en appuyant sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~. Elle explosera au démarrage. [MOB_70A] Tommy, c'est moi, le colonel Cortez. Ecoutez senor, je parie qu'avec vous, tout est possible. [MOB_70B] Je serai sur le bateau. [PICK1] Gilet pare-balles livré dans l'hôtel Ocean View! [PICK2] .357 livré à la planque! [PICK3] Tronçonneuse livrée à la planque! [PICK4] Lance-flammes livré à la planque! [PICK5] .308 Lunette livré à la planque! [PICK6] Mitrailleuse livrée à la planque! [PICK7] Lance-roquettes livré à la planque! [PICK8] Sea Sparrow désormais disponible dans la résidence! [PICK9] Char désormais disponible dans la caserne de l'armée! [PICK10] Hunter désormais disponible dans la caserne de l'armée! [HELP41] tu peux aussi les défoncer avec un véhicule [ICC1_6] ~g~Utilise le Mr. Whopee pour distribuer des produits Cherry Poppers dans Vice City. [ICC1_12] PROPRIETE ACQUISE! [CLOTH1] Tenue de soirée livrée chez Rafael sur Ocean Beach. [CLOTH2] Tenue streetwear livrée aux planques. [CLOTH3] Bleu de travail livré chez Tooled Up dans le centre commercial de North Point. [CLOTH4] Tenue pour le Country Club livrée au Leaf Links Golf Club. [CLOTH5] Tenue Cubano livrée chez Little Havana Streetwear dans Little Havana. [CLOTH6] Uniforme de flic livré au poste de police de Washington Beach. [CLOTH7] Tenue décontractée livrée chez Gash dans le centre commercial de North Point. [CLOTH8] Tenue de M. Vercetti livrée chez Collar & Cuffs sur Ocean Beach. [CLOTH9] Survêtement livré chez Jocksport dans le centre. [CLOTH10] Tenue de braqueur livrée au Malibu de Vice Point. [RBM1_9] ~g~Va chez le dealer et rapporte du Love Juice pour les Love Fist! [MOB_62A] Tommy, c'est Ricardo Diaz, je voulais te remercier de m'avoir sauvé. [MOB_62B] J'ai demandé à ce con de Cortez. Il a dit que tu feras l'affaire, mon ami. Viens me voir à l'occasion. [MOB_62C] C'est d'un mec comme toi dont j'ai besoin. J'ai plus que des têtes de bites, [MOB_62D] des têtes de bite partout! Tu vas gagner plein de pognon. [GOAWAY2] ~g~Reviens quand tu auras terminé les missions Bikers. [COL2_9] Imbécile de Ricain! Ils vous ont suivi jusqu'ici! [LOADCOL] Chargement... [STFT_17] Meilleur temps sur Terrain de jeu PCJ [STFT_18] Meilleur temps sur 'Sélection par la boue' [STFT_19] Meilleur temps sur Piste d'essai [NEW_REC] Nouveau record! ~1~ minutes et ~1~ secondes. [BMX_HOW] ~g~Fais deux tours de circuit, ~y~en passant par ~g~les ~y~POINTS DE PASSAGE~g~! [BMXREW1] ~g~Chaque fois que tu inscris un nouveau record pour les deux tours, [BMXREW2] ~g~une meilleure ~y~RECOMPENSE ~g~t'est offerte! [BMXRAIN] ~g~On dirait qu'il pleut... [ITBEG] AU DEBUT... [NBMNBUY] El Swanko Casa acheté : $ ~1~ [LNKVBUY] Appartement de Links View acheté : $ ~1~ [HYCOBUY] Hyman Condo acheté : $ ~1~ [BUYGARS] ~g~Tu peux également stocker des véhicules dans ces garages. [OCHEBUY] Appartement d'Ocean Heights acheté : $ ~1~ [WASHBUY] 1102 Washington Street acheté : $ ~1~ [VCPTBUY] 3321 Vice Point acheté : $ ~1~ [SKUMBUY] Skumole Terrace achetée : $~1~ [HELP6_C] Appuie sur la ~h~~k~~VEHICLE_HANDBRAKE~~w~ pour actionner le frein à main. [HELP2_A] Appuie sur la ~h~~k~~PED_SPRINT~~w~ lorsque tu cours pour ~h~sprinter. [HELP4_A] Appuie sur la ~h~~k~~VEHICLE_ACCELERATE~~w~ pour accélérer. [HELP5_A] Appuie sur la ~h~~k~~VEHICLE_BRAKE~~w~ pour freiner ou faire marche arrière si le véhicule est à l'arrêt. [HELP8_A] Appuie sur la ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ pour faire un zoom avant avec le fusil et sur la ~x~touche /~w~ pour faire un zoom arrière. [PBOAT_1] { reVC update } Appuie sur la~h~ ~k~~VEHICLE_FIREWEAPON~~w~ pour tirer avec les canons du bateau. [SEG3_4] { reVC update } ~g~Tu peux ramasser des bombes en pilotant ton avion radiocommandé à côté. Appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~ ~g~touche. [RCR1_3] { reVC update } ~g~Si tu veux quitter cette mission, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~~g~ pour faire exploser ta voiture radiocommandée. [HELP32] { reVC update } Appuie ensuite sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer. [HELP33] { reVC update } Appuie ensuite sur la ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer. [TTUTOR] Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Taxi. [TTUTOR2] Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Taxi. [FTUTOR] Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Camion de pompiers. [FTUTOR2] Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Camion de pompiers. [CTUTOR] Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Auto-défense. [CTUTOR2] Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Auto-défense. [HELP8_B] Appuie sur la~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~pour faire un ~h~zoom avant~w~ avec le fusil et sur la ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ pour faire un ~h~zoom arrière~w~. [ATUTOR3] Appuie sur la ~h~~k~~TOGGLE_SUBMISSIONS~~w~ pour activer ou désactiver les missions Ambulance. [GUN_H1] ~w~Appuie sur la~h~ ~k~~PED_SPRINT~~w~ pour acheter. ~w~Appuie sur la~h~ ~k~~VEHICLE_ENTER_EXIT~~w~ pour quitter. [PU_CF3] { reVC update } Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour remplacer l'arme actuelle dans cet emplacement. [PU_CF4] { reVC update } Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour remplacer l'arme actuelle dans cet emplacement. [HELP9_B] Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. [HELP37] Si tu ne veux pas monter dans un véhicule quand tu braques le conducteur, appuie sur la ~h~~k~~PED_SPRINT~. [HELP6_A] Appuie sur la ~h~~k~~VEHICLE_HANDBRAKE~ ~w~ pour actionner le frein à main. [HELP6_D] Appuie sur la ~h~~k~~VEHICLE_HANDBRAKE~ ~w~ pour actionner le frein à main. [HELP26] Appuie sur la ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~ pour monter ou sortir d'un véhicule. [HELP27] Appuie sur ~h~~k~~VEHICLE_TURRETUP~~w~ ou ~h~~k~~VEHICLE_TURRETDOWN~~w~ pour déplacer ton poids sur une moto. [HELP28] Appuie sur ~h~~k~~VEHICLE_TURRETUP~~w~ ou ~h~~k~~VEHICLE_TURRETDOWN~~w~ pour déplacer ton poids sur une moto. [HELP35] Appuie sur ~h~~k~~GO_LEFT~~w~ ou ~h~~k~~GO_RIGHT~~w~ pour diriger le véhicule. [HELP36] Appuie sur ~h~~k~~GO_LEFT~~w~ ou ~h~~k~~GO_RIGHT~~w~ pour diriger le véhicule. [HELP42] Suis le ~q~point rose~w~ pour trouver l'hôtel. [HELP19] Marche sur le ~q~marqueur rose~w~ pour continuer. [HELP1] Arrête-toi au centre du ~q~marqueur rose. [HELP12] Marche au centre du ~q~marqueur rose~w~ pour lancer une mission. [SEG3_6] ~g~Pour toucher ta cible, tu dois larguer la bombe sur la zone indiquée par le ~q~marqueur rose~w~. Tu peux larguer les bombes dans n'importe quel ordre. [S_PROMP] Lorsque tu n'es pas en mission, tu peux sauvegarder la partie en ramassant la ~h~cassette. [HELP16] Franchis la porte d'entrée de l'hôtel ~h~Ocean View~w~ pour y pénétrer. [HELP43] ~g~Rends-toi à l'hôtel ~h~Ocean View~g~ sur Ocean Drive. [HELI_F1] ~r~Mission Point de passage hélico annulée! [AMMUHLP] Si tu as besoin d'armes, va chez ~h~Ammu-Nation~w~. Suis le ~h~point pistolet~w~ sur le radar. [HELI_1] Point de passage hélico du Centre [HELI_2] Point de passage hélico d'Ocean Beach [HELI_3] Point de passage hélico de Vice Point [HELI_4] Point de passage hélico de Little Haiti [FST_MFR] Station de radio préférée [FST_LFR] Station de radio la moins écoutée [FEI_HOL] Maintenir [FEI_ZOO] Zoom [FEI_BTR] > < - [FEI_NA] Aucun [MESA] Grande table [STRP_NO] Tu ne peux pas acheter la boîte de striptease maintenant. Reviens plus tard. [CHSE] POURSUITE [NBMN_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter El Swanko Casa pour $~1~. [NBMN_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter El Swanko Casa pour $~1~. [NBMN_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter El Swanko Casa pour $~1~. [LNKV_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement de Links View pour $~1~. [LNKV_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement de Links View pour $~1~. [LNKV_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement de Links View pour $~1~. [HYCO_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter Hyman Condo pour $~1~. [HYCO_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter Hyman Condo pour $~1~. [HYCO_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter Hyman Condo pour $~1~. [OCHE_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement d'Ocean Heights pour $~1~. [OCHE_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement d'Ocean Heights pour $~1~. [OCHE_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'appartement d'Ocean Heights pour $~1~. [WASH_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 1102 Washington Street pour $~1~. [WASH_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 1102 Washington Street pour $~1~. [WASH_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 1102 Washington Street pour $~1~. [VCPT_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 3321 Vice Point pour $~1~. [VCPT_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 3321 Vice Point pour $~1~. [VCPT_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le 3321 Vice Point pour $~1~. [SKUM_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour acheter la Skumole Terrace pour $~1~. [SKUM_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour acheter la Skumole Terrace pour $~1~. [SKUM_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~~w~ pour acheter la Skumole Terrace pour $~1~. [PRNT_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'imprimerie pour $~1~. [PRNT_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'imprimerie pour $~1~. [PRNT_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'imprimerie pour $~1~. [CAR_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la concession automobile pour $~1~. [CAR_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la concession automobile pour $~1~. [CAR_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la concession automobile pour $~1~. [PORN_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le studio de cinéma pour $~1~. [PORN_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le studio de cinéma pour $~1~. [PORN_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le studio de cinéma pour $~1~. [ICE_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'usine de crème glacée pour $~1~. [ICE_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'usine de crème glacée pour $~1~. [ICE_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter l'usine de crème glacée pour $~1~. [TAXI_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la compagnie de taxis pour $~1~. [TAXI_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la compagnie de taxis pour $~1~. [TAXI_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter la compagnie de taxis pour $~1~. [BANK_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Malibu pour $~1~. [BANK_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Malibu pour $~1~. [BANK_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Malibu pour $~1~. [BOAT_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le chantier naval pour $~1~. [BOAT_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le chantier naval pour $~1~. [BOAT_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le chantier naval pour $~1~. [STRP_L] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Pole Position Club pour $~1~. [STRP_T] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Pole Position Club pour $~1~. [STRP_C] Appuie sur la ~h~~k~~PED_ANSWER_PHONE~ ~w~pour acheter le Pole Position Club pour $~1~. [STOCK] ~r~stock écoulé [HELP14] Pour trouver le cabinet d'avocats, suis le ~h~point L~w~ sur le radar. [BOAT_AS] ~g~Le chantier naval génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. [BOAT_A2] CHANTIER NAVAL OK [BOAT_N] Checkpoint Charlie [BOAT_P] ~g~Récupère les paquets avant la fin du temps imparti. [FEI_R1B] Touches R1\R2 - [HELP9_A] Appuie sur la touche ~h~~k~~PED_FIREWEAPON~ ~w~pour tirer avec le fusil à lunette. [HELP21] Appuie sur la touche ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~pour monter dans un véhicule ou en sortir. [CREAM] Distribution [UMBERTO] Café Robina [PU_CF1] Appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~ ~w~pour ramasser cette arme. Elle remplacera toute autre arme du même type que tu possèdes. [FED_RDM] CARTE ET POINTS [FEC_ILU] Vue inversée à la 1ere personne : [NITRO] Tous les taxis disposent d'un saut turbo! Appuie simplement sur la touche klaxonner. [RATNG53] Faux cul [RATNG54] Faiseur d'emmerdes [RATNG55] Malhonnête [RATNG56] Tricheur [RATNG57] Mythomane [STHC_04] Meilleur score au beach ball de Keepie-Uppy [STHC_05] Meilleur résultat au Hotring [STFT_13] Meilleur tps point de passage hélico du centre [STFT_14] Meilleur tps point de passage hélico d'Ocean Beach [STFT_15] Meilleur tps point de passage hélico de Vice Point [STFT_16] Meilleur tps point de passage hélico de Little Haiti [STFT_21] Meilleur temps au Hotring [STFT_22] Meilleur temps au tour au Hotring [STFT_20] Meilleur temps au 'Cone Crazy' [HELP44] Arrête-toi sur le ~q~marqueur rose. [HELP45] Appuie sur la ~h~~k~~PED_DUCK~~w~ pour t'accroupir. Ceci augmente la précision des flingues que tu portes. [RCR1_5] Course de Bandit RC [RCPL1_7] Course de Baron RC [RCH1_11] Course de Raider RC [FEA_CTD] Attention! Un équipement matériel compatible DTS est requis pour cette fonction. Continuer? [FEM_STE] STEREO [FEM_UDY] DTS [GREET] Bien le bonjour de... [LANCE_1] Hé, mec, conduis un peu mieux! [LANCE_2] Hé, fais gaffe à ce que tu fais! [LANCE_3] Hé, tu vas où, là? [LANCE_4] On fait quoi maintenant? [LAW4_15] Plus de fric! [MERC_5] Belle voiture, M. Vercetti. [MERC_26] PLUS VITE, PLUS VITE, PLUS VITE! [MERC_27] Attention, Tommy, je me suis fait refaire le nez le mois dernier. [MERC_28] Tommy, conduis prudemment! [MERC_29] Tommy, va moins vite! [MERC_30] Tommy, tu veux bien tuer quelqu'un d'autre que moi? [MERC_31] Tommy, chéri, ne me tue pas! [MERC_32] Tommy, ça me très plaisir que t'aies volé cette caisse! [MERC_40] J'ai vraiment passé un bon moment. [MERC_43] Adios, mon chou. [MERC_44] Continue la muscu, d'accord? [MERC_45] Ciao, mon beau. [COL5_17] Oh mon dieu, ils ont un hélicoptère! [COL5_18] Abattez l'hélico! [COL5_19] Tommy, débarrasse-nous de cet hélico! [COL5_20] Il revient! Détruisez cet hélico! [COL5_21] Visez la taille de cet hélico! [COL5_22] Le revoilà! [FEA_DSM] Attention! Cette sauvegarde est paramétrée pour un son DTS. Du matériel compatible DTS doit être connecté. Choisir entre une sortie audio STEREO ou DTS. [STFT_23] Meilleur temps à Checkpoint Charlie [HELP50] Appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~~w~ pour placer la caméra derrière toi. [HELP51] Appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~~w~ pour placer la caméra derrière toi. [HELP52] Appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~~w~ pour placer la caméra derrière toi. [HELP53] Appuie sur la touche ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~ ou sur la touche ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~ pour faire défiler tes armes disponibles. [HELP46] Il existe huit différents types d'armes. [HELP47] Tu peux porter une arme de chaque type à la fois, un type de pistolet, un type de fusil à pompe, etc. [HELP54] ~w~Prix : $~1~ ~r~L'achat de cette arme remplacera celle que tu possèdes déjà. [HELP2A2] Appuie sur la touche ~h~~k~~PED_SPRINT~~w~ quand tu cours, pour ~h~sprinter. [HLPSN_A] Grâce au fusil à lunette, tu peux zoomer de loin sur une cible et la viser avec précision. [HLPSN_B] Appuie sur la touche~h~ ~k~~PED_LOCK_TARGET~ ~w~et maintiens-la enfoncée pour ~h~viser~w~ avec le fusil à lunette. [HLPSN_C] Appuie sur la touche~h~ ~k~~PED_LOCK_TARGET~ ~w~et maintiens-la enfoncée pour ~h~viser~w~ avec le fusil à lunette. [HLPSN_D] Appuie sur la touche ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ pour ~h~faire un zoom avant ~w~avec le fusil à lunette et sur la touche~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~pour ~h~faire un zoom arrière~w~. [HLPSN_E] Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. [HLPSN_F] Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. [HLPSN_G] Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ ~w~pour ~h~tirer~w~ avec le fusil à lunette. [PLANE_H] Utilise la touche ~h~~k~~VEHICLE_ACCELERATE~~w~ pour accélérer. Gauche et droite pour tourner. [PLANE_4] { reVC update } {Utilise la touche ~h~~k~~VEHICLE_ACCELERATE~~w~ pour accélérer. Gauche et droite pour tourner.} Utilise le joystick analogique droit pour accélérer, appuie vers le bas sur le joystick analogique gauche pour monter et vers le haut pour descendre. Gauche et droite pour tourner. [HELP55] Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ pour attaquer le chef. [STPR_8] Pole Position Club [STPR_9] 3321 Vice Point [STPR_10] Appartement de Links View [STPR_11] El Swanko Casa [STPR_12] 1102 Washington Street [STPR_13] Appartement d'Ocean Heights [STPR_14] Skumole Shack [STPR_15] Hyman Condo [RCCANX] ~r~Avion RC annulé. [CLT_HL2] Lorsque tu ramasses des fringues, une ou deux étoiles d'indice de recherche sont enlevées. [CRED009] CONCEPTION DES MISSIONS [CRED359] LEE JOHNSON [CRED360] HENDRIK LESSER [CRED361] PASQUALE STACCHIOTTI [CRED362] ENRIQUE FERNANDEZ [CRED363] PAUL BYERS [CRED364] MIKE EMENY [CRED365] ROB DUNKIN [CRED366] CHARLIE KINLOCH [CRED367] KEVIN HOBSON [CRED368] JIM CREE [MOB_66A] Tommy, Tommy, Tommy, pourquoi t'es revenu ici? [MOB_66B] On t'a déjà dit qu'on voulait plus te revoir. [MOB_67A] Tommy, je crois que tu devrais rester à l'écart, tu saisis? [MOB_67B] Les petits gars Haïtiens t'aiment pas beaucoup. [MOB_18A] Tommy, c'est Paulo. Tu vas bien? Bon, mec, il faut que je te parle. [MOB_18B] Ah, mon pote, tu croiras jamais l'incroyable petit lot que je viens de lever... [MOB_18C] Elle se promenait juste en bas dans Little Havana, mon pote. [MOB_18D] Elle m'a dit qu'elle s'appelait Mercedes ou un truc comme ça. [MOB_18E] Ah, mon pote, faut que t'ailles voir cette fille! [MOB_18F] Elle ferait bander un eunuque! Elle m'a dit que j'étais le meilleur coup de sa vie et tout et tout! [MOB_18G] Vas-y, trouve-la. A plus tard! [MOB_72A] Tommy, c'est moi, Lance. Ferme-la, Tommy, parce que j'ai pas le temps de discuter! [MOB_72B] J'me tape de ce que t'as à me dire. Pourquoi ça m'intéresserait? T'en as rien à foutre de moi, non? [MOB_72C] Il faut que tu sois plus sympa avec moi. Donne-moi une belle part. Tu vois... [MOB_72D] Tommy... Ecoute, mec, je suis désolé. C'est que... [MOB_72E] Les gens arrêtent pas de me materner, de me traiter comme un gamin. [MOB_72F] Mon frère pourrait me faire ça, mais pas toi. S'il te plaît. [MOB_72G] Faut que j'y aille. [MOB_63A] Tommy, c'est Earnest. Earnest Kelly. [MOB_63B] Ca va? [MOB_63C] Bien. Il va me falloir une canne pour marcher, mais je devrais revenir rapidement dans les affaires. [MOB_63D] Bien. [MOB_63E] J'ai appris au sujet de Lance. Quel enculé, hein? [MOB_63F] Ouais. [MOB_63G] Ne jamais faire confiance à un mec qui se trimballe en pyjama. C'est ce que je dis toujours. J'espère bien qu'il en a bavé, ce con. [MOB_63H] Je crois que oui. Je pensais pas qu'il était comme ça... [MOB_63I] Tommy, t'as beau être cinglé, t'es vraiment naïf. Il va falloir que je t'apprenne deux ou trois trucs sur la vie, dès que je serai remis sur pied. [MOB_63J] Prends ton temps, Earnest et surtout, prends soin de toi. [MOB_16A] Tommy, c'est Paulo. Que pasa amigo? [MOB_16B] Qu'est-ce tu m'veux, Paul? J'veux pas de fringues de contrefaçon. [MOB_16C] Très drôle, mon pote, mais tu sais que je fais pas dans la merde. Non, j'appelle juste pour savoir si t'aurais pas un rôle pour moi dans un de tes films. [MOB_16D] J'ai fait pas mal de X en Angleterre, mec. J'en ai plus que toi dans l'pantalon. [MOB_16E] Paul, merci pour la proposition, je vais y réfléchir. [MOB_16F] Sérieux, pense à moi, après tout ce que j'ai fait pour toi. [MOB_16G] C'est ce que j'essaie d'oublier, justement... [MOB_17A] Tommy Vercetti, comment vas-tu? J'ai entendu tous ces trucs sur toi. Un flambeur en ville maintenant... [MOB_17B] Paul, t'es bourré? [MOB_17C] Non, pauv' con, je suis pas bourré! J'ai juste pris deux ou trois verres et quelques tournées, et ça fait deux jours que j'ai pas fermé l'oeil! [MOB_17D] De toute façon, me traite pas comme ça. Je suis pas une poire. Qui c'est qui t'a lancé dans cette ville? Hein, qui? Moi! [MOB_17F] Ah ouais? [MOB_17G] Me traite pas comme ça? Qui c'est qui t'a présenté à des gens? Je t'ai montré tous les trucs, je me suis fait chier pour toi et voilà comment tu m'remercies. [MOB_17H] Tu m'ignores. Tu me laisses de côté après tout ce que j'ai fait pour toi! Qui tu crois que je suis? Une merde ou je sais pas quoi? [MOB_17I] Paul, calme-toi. J'ai été occupé, ne sois pas stupide. [MOB_17J] Je suis pas stupide, connard! Ils me l'ont dit en maison de redressement! Si tu cherches les emmerdes, mon pote, tu vas les trouver! [MOB_17K] Tommy, s'il te plaît! T'étais mon grand espoir! Te fous pas de moi! [MOB_17L] Paul, va roupiller un bon coup, sérieusement. [MOB_73A] Tommy, c'est Steve. [MOB_73B] Salut, Steve. [MOB_73C] Salut le génie. T'es merveilleux! Je suis une merveille! Ils nous adorent. On réécrit le livre des records, mon pote! [MOB_73D] Je parle de putain de récompenses. Enfin je peux envoyer mon père à l'hospice et lui dire de fermer sa gueule. [MOB_73E] Euh... C'est cool, Steve. [MOB_73F] Cool? C'est génial, mec. GENIAL quoi! Il n'a jamais voulu croire en moi, mais regarde ce qu'on a réussi à faire. [MOB_73G] Je suis le plus grand réalisateur de films de cul faits maison de la planète, mon pote. Je voulais juste te dire que je suis heureux de t'avoir rencontré. [MOB_73H] Merci, Steve. [MOB_73I] Je t'aime, bébé. Et reste comme tu es, d'accord? [MOB_73J] D'accord. Salut, Steve. [BOLLOX] Appuie sur la touche ~o~R1 ~w~pour larguer une bombe. Appuie sur la touche ~t~" ~w~pour annuler. [BRID_OP] Avis de tempête terminé : tous les ponts sont maintenant accessibles. [BRID_CL] Avis de tempête : tous les ponts sont fermés. [LG_38] Cible [ASSET_C] Pole Position, OK [ASSET_D] ~g~Le club Pole Position va maintenant créer un revenu d'un maximum de $~1~ par jour. Venez retirer votre argent régulièrement. [ST_WHEE] Temps maximum sur roue arrière (secs) [ST_STOP] Temps maximum sur roue avant (secs) [ST_2WHE] Temps maximum sur deux roues (secs) [ST_WHED] Distance maximum sur roue arrière (m) [ST_STOD] Distance maximum sur roue avant (m) [ST_2WHD] Distance maximum sur deux roues (m) [OUTFT11] Survêtement [OUTFT12] Frankie [RELOAD] ~g~Tu as gagné la possibilité de recharger ton arme rapidement [APACHE] Hunter livré à l'héliport d'Ocean Beach [CRED369] JOHN MCCARDLE [CRED370] DAVID MURDOCH [CRED371] CHRIS BROWN [CRED372] PAUL GREEN [CRED373] KYLE MILNE [CUNTY] Nouveaux vêtements livrés au Domaine Vercetti [GOODBOY] $50 pour bonne conduite [NEWCONT] Nouveau ~h~point de contact ~w~créé à la marina d'Ocean Beach [FIRELVL] Mission Camion de pompiers niveau ~1~ [HELP56] Appuie sur la touche ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ pour changer le mode de la caméra. [HELP57] Appuie sur la touche ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ pour changer le mode de la caméra. [HELP58] Tout en visant, appuie sur la touche ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~ ou ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~ pour faire défiler les cibles. [HELP59] Tout en visant, appuie sur la touche ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~ ou ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~ pour faire défiler les cibles. [HELP60] Si tu appuies sur la touche ~h~~k~~PED_SPRINT~ ~w~quand tu essaies de piquer une caisse, tu ne peux pas monter dedans. [HELP61] Tu as désormais des munitions illimitées et tous tes véhicules sont deux fois plus résistants. [CRED374] KEVIN YUN [CRED375] ERICK COBBS [CRED376] RANDY BLAKE [CRED377] BRANDON LIM [CRED378] BRANDON FENOL [CRED379] MICHAEL MANOLE [CRED380] ALETHEIA SIMONSON [CRED381] JOHN JANSEN [FEC_LB1] Regarder [FEC_LB2] derrière [FEC_LB3] Regarder derrière [FEC_R3] (touche R3) [FEC_PED] Commandes à pied [FEC_VEH] Commandes des véhicules [FEC_FPR] Commandes en vue subjective [FEC_CMM] Commandes principales [FEC_PWL] Aller à gauche [FEC_PWR] Aller à droite [FEC_PWF] Avancer [FEC_PWT] Avancer vers caméra [FEC_PLB] Vue arrière [FEC_PFR] Tirer [FEC_CLE] Défilement Gauche des armes [FEC_CRI] Défilement Droite des armes [FEC_LKT] Verrouiller cible [FEC_PJP] Saut à pied [FEC_PSP] Sprint à pied [FEC_PSH] Tir à pied [FEC_TLF] Cible suivante Gauche [FEC_TRG] Cible suivante Droite [FEC_CCM] Centrer caméra derrière joueur [FEC_SZI] Fusil à lunette zoom avant [FEC_SZO] Fusil à lunette zoom arrière [FEC_LKL] Regarder à gauche en vue subjective [FEC_LRT] Regarder à droite en vue subjective [FEC_LUP] Regarder en haut en vue subjective [FEC_LDN] Regarder en bas en vue subjective [FEC_LBH] Regarder derrière le véhicule [FEC_LLF] Regarder à gauche du véhicule [FEC_LRG] Regarder à droite du véhicule [FEC_HRN] Klaxon [FEC_HBR] Frein à main [FEC_ACL] Accélérer [FEC_BRK] Freiner [FEC_TSM] Activer/Désactiver sous-missions [FEC_CRD] Changer la station de radio [FEC_ENT] Entrer/Sortir d'un véhicule [FEC_WPN] Tirer [FEC_PAS] Pause [FEC_FPO] Changer d'arme en vue subjective [FEC_SMS] Afficher/Masquer curseur [FEC_CMS] Changer de mode de caméra [FEC_TSS] Faire une capture d'écran [FEC_DBG] Menu Debug [FEC_TGD] Alterner manette jeu/debug [FEC_TDO] Désactiver caméra debug [FEC_IVH] Inverser souris horizontale [FEC_MSL] BGS [FEC_MSM] BMS [FEC_MSR] BDS [FEC_QUE] ??? [FEC_TWO] Deux touches clavier au maximum [FEC_UMS] Boutons souris uniquement [FEC_OMS] Un bouton souris au maximum [FEC_UJS] Un bouton joystick au maximum [FEC_OJS] Un bouton joystick maximum par action [FEC_PTL] Utiliser verrouillage de cible avec commande de tir gauche [FEC_PTR] Utiliser verrouillage de cible avec commande de tir droite [FEC_LBC] Utiliser regarder gauche avec regarder droite [FEC_JBO] JOY ~1~ [FEC_WAR] Avertissement [FEC_OKK] O.K. [FEC_DLF] Erreur lors de suppression [FEC_SVU] Erreur lors de la sauvegarde [FEC_LUN] Erreur lors du chargement. Fichier corrompu, veuillez le supprimer. [FEC_PAD] Manette [FEC_JOY] Joystick [FES_CSA] Sélectionnez une apparence dans la liste suivante : [FET_HRD] PARAMETRES PAR DEFAUT RETABLIS [FET_MST] DIRECTION CONTROLEE PAR LA SOURIS [FEC_STR] ETOILE PAV.NUM. [FET_MIG] GAUCHE, DROITE, MOLETTE SOURIS POUR REGLER [FET_CIG] RETOUR ARRIERE POUR EFFACER - BGS, RETOUR POUR CHANGER [FET_DSN] Skin joueur par defaut.bmp [FET_RSO] PARAMETRE D'ORIGINE RETABLI [FET_RSC] MATERIEL INDISPONIBLE - PARAMETRE D'ORIGINE RETABLI [FEA_3DH] CONFIG. CARTE-SON [FEA_SPK] CONFIG. HAUT-PARLEURS [FEM_LOD] DISTANCE MODELES [FEM_VSC] SYNCHRO VIDEO [FEM_FRM] RESTRICTION VIDEO [FEM_MM] MENU PRINCIPAL [FED_RES] RESOLUTION ECRAN [FET_CTL] CONFIG. PERIPHERIQUE [FET_OPT] OPTIONS [FEC_MSH] SENSIBILITE SOURIS [FEC_IVV] INVERSER SOURIS VERTIC. [FET_MTI] CONFIG. SOURIS [FEC_FNC] F~1~ [FEC_IRT] INSER [FEC_DLL] SUPPR [FEC_HME] ORIG [FEC_END] FIN [FEC_PGU] PAGE HAUT [FEC_PGD] PAGE BAS [FEC_UPA] HAUT [FEC_DWA] BAS [FEC_LFA] GAUCHE [FEC_RFA] DROITE [FEC_NUM] PAV.NUM [FEC_NMN] PAV.NUM~1~ [FEC_FWS] PAV.NUM / [FEC_PLS] PAV.NUM + [FEC_MIN] PAV.NUM - [FEC_DOT] PAV.NUM . [FEC_NLK] VERR NUM [FEC_ETR] ENTR [FEC_SLK] ARRET DEFIL [FEC_PSB] PAUSE [FEC_BSP] RET. ARR. [FEC_TAB] TAB [FEC_CLK] VERR MAJ [FEC_RTN] RETOUR [FEC_LSF] MAJ. G [FEC_RSF] MAJ. D [FEC_LCT] CTRL G [FEC_RCT] CTRL D [FEC_LAL] ALT G [FEC_RAL] ALT D [FEC_LWD] WIN G [FEC_RWD] WIN D [FEC_WRC] CLIC WIN [FEC_SPC] ESP [WIN_TTL] Grand Theft Auto VC [WIN_95] Grand Theft Auto VC n'est pas compatible WINDOWS 95 [WIN_DX] Grand Theft Auto VC requiert la version 8.1 de DirectX minimum. [FET_EIG] IMPOSSIBLE DE PARAMETRER UNE TOUCHE POUR CETTE ACTION [FET_DAM] MODELAGE ACCOUST. DYNAMIQUE [FEQ_SRE] Etes-vous sûr de vouloir quitter ? Votre progression depuis la dernière sauvegarde sera perdue. Continuer ? [FEQ_SRW] Etes-vous sûr de vouloir quitter la partie ? [FET_QG] QUITTER PARTIE [FEN_STA] COMMENCER PARTIE [FET_PAU] MENU PAUSE [REPLAY] RALENTI [FET_PS] CONFIG. JOUEURS [FEC_ANS] Action [CVT_MSG] Conversion des textures vers un format optimal pour votre carte graphique [FEC_SFT] MAJ [FEH_VMP] VOIR CARTE [FES_DEE] Echec de la suppression ! Recommencer. [FES_CMP] Echec de la sauvegarde ! Recommencer. [FESZ_WR] Sauvegarde en cours. Un instant... [FELD_WR] Chargement en cours. Un instant... [FEDL_WR] Suppression en cours. Un instant... [PCRESRT] Lancement d'une nouvelle partie. Un instant... [FET_STI] Commandes standard [FET_CTI] Commandes classiques [FEH_NA] OPTION NON DISPONIBLE [FEH_MPH] SOURIS, CURSEURS POUR SE DEPLACER - PAGE HAUT, PAGE BAS, MOLETTE POUR ZOOMER, L - LEGENDE [FEA_MP3] LECTEUR MP3 [NO_PCCD] Insérer le disque de GTA Vice City ou appuyer sur ECHAP pour annuler [FEH_SSA] CURSEURS POUR SE DEPLACER - S POUR SAUVEGARDER LE FICHIER [FES_CMI] DERNIERE MISSION REUSSIE [FET_STS] STATS SAUVEGARDEES DANS 'STATS.HTML' + 'STATS.TXT' [WIN_VDM] Grand Theft Auto VC ne dispose pas de suffisamment de mémoire graphique. [FEC_ERI] Erreur ! Une ou plusieurs actions ne sont pas assignées à une touche ou à un bouton. Vérifier que toutes les actions sont bien assignées. [FEC_TFU] Tourelle + Orienter haut [FEC_TFD] Tourelle + Orienter bas [FET_RIG] CHOISIR UNE AUTRE COMMANDE POUR CETTE ACTION [FEA_NM3] AUCUN FICHIER MP3 TROUVE [FEA_MPB] VOLUME MP3 A FOND [FEA_MUS] VOLUME MUSIQUE [FEA_SFX] VOLUME EFFETS SONORES [CVT_ERR] Espace disque épuisé. Libérez de la mémoire sur votre disque dur pour continuer. Appuyez sur ECHAP pour annuler. [FEA_ADP] AUTODETECT MATERIEL {=================================== MISSION TABLE AMBULAE ===================================} [ATUTOR2:AMBULAE] ~g~Conduis les patients à l'hôpital. DOUCEMENT. Chaque secousse réduit leurs chances de survie. [A_FULL:AMBULAE] ~r~Ambulance pleine! [A_FAIL2:AMBULAE] ~r~Ton manque de rapidité a été fatal pour le patient! [A_FAIL3:AMBULAE] ~r~Le patient est mort!! [A_PASS:AMBULAE] Sauvé! [A_COMP2:AMBULAE] Tu ne seras plus jamais essoufflé! [A_COMP1:AMBULAE] Missions Ambulance réussies : $~1~ [A_CANC:AMBULAE] ~r~Mission ambulance annulée! [A_COMP3:AMBULAE] Missions ambulance accomplies! Maintenant, tu peux sprinter indéfiniment! [ALEVEL:AMBULAE] Mission ambulance, niveau ~1~ [A_FAIL1:AMBULAE] Mission ambulance achevée. [A_SAVES:AMBULAE] PERSONNES SAUVEES : ~1~ {=================================== MISSION TABLE ASSIN1 ===================================} [ASM1_5:ASSIN1] ~r~Il a livré toutes ses pizzas! [ASM1_6:ASSIN1] Livraisons restantes : [ASM1_7:ASSIN1] ~g~Carl Pearson, livreur de pizzas. Tue-le avant qu'il ne termine ses livraisons. [ASM1_D:ASSIN1] Ton aide dans l'éradication de ces indésirables fut une excellente affaire. {=================================== MISSION TABLE ASSIN2 ===================================} [ASM2_1:ASSIN2] ~g~Mme Dawson va bientôt quitter la bijouterie à Vice Point. Tue-la. Il faut que ça ressemble à un accident de voiture. [ASM2_3:ASSIN2] ~g~Ca va sauter, écarte toi! [ASM2_4:ASSIN2] ~r~T'as bousillé sa bagnole alors qu'elle était même pas dedans. Elle est pas près de l'utiliser, maintenant! [ASM2_5:ASSIN2] ~r~Elle s'est enfuie! [ASM2_6:ASSIN2] ~r~Tu étais trop près de l'accident! [ASM2_7:ASSIN2] ~g~N'utilise pas d'armes! Il faut que ça ressemble à un accident! Sors-la plutôt de la route! [ASM2_8:ASSIN2] ~g~La mort de madame Dawson doit avoir l'air d'un accident. N'utilise pas d'armes. [ASM2_9:ASSIN2] ~g~Il te faut une voiture pour ce boulot! [ASM2_10:ASSIN2] ~g~Quand sa bagnole prendra feu, éloigne-toi le plus possible du lieu de l'accident. [ASM2_11:ASSIN2] A l'aide! [ASM2_12:ASSIN2] Que quelqu'un m'aide! [ASM2_13:ASSIN2] Oh non! [ASM2_A:ASSIN2] Mes félicitations pour ce travail bien fait, Mr. Teal. Mon client était ravi. [ASM2_2:ASSIN2] Santé : {=================================== MISSION TABLE ASSIN3 ===================================} [ASM3_11:ASSIN3] TEMPS : [ASM3_C:ASSIN3] Un gang européen prépare un braquage de banque à Vice City. Mes employeurs préfèreraient que cela n'arrive pas. [ASM3_D:ASSIN3] Chaque membre du gang dispose d'une couverture à Vice City. Certains ont des petits boulots, d'autres jouent les touristes. [ASM3_E:ASSIN3] Chaque cible et leur position probable sont indiqués sous le téléphone. [ASM3_14:ASSIN3] ~g~Dick Tanner est à côté de DBP Security sur Ocean Drive. [ASM3_15:ASSIN3] ~g~Marcus Hammond et Franco Carter sont près de la bijouterie de Vice Point. [ASM3_16:ASSIN3] ~g~Nick Kong est à côté de Washington Beach. [ASM3_18:ASSIN3] ~g~Ne t'approche pas trop près de la cible où elle risque de te repérer et d'essayer de se faire la malle! [ASM3_19:ASSIN3] ~g~Il t'a repéré! Bute-le! [ASM3_20:ASSIN3] ~g~Ils t'ont repéré! Tue-les vite tous les deux! [ASM3_21:ASSIN3] ~r~Tu n'as pas tué tous les membres du gang à temps! [ASM3_22:ASSIN3] ~g~Ne t'approche pas trop des cibles ou elles risquent de te repérer et d'essayer de filer. [ASM3_12:ASSIN3] ~g~Des armes ont été laissées à ta disposition dans le coin si tu en as besoin. Tu as ~h~9 MINUTES ~g~pour tuer tous les membres du gang. [ASM3_13:ASSIN3] ~g~Mike Griffin travaille sur un panneau publicitaire à Washington. [ASM3_17:ASSIN3] ~g~Charlie Dilson est à moto sur Washington. {=================================== MISSION TABLE ASSIN4 ===================================} [ASM4_12:ASSIN4] Distance : [ASM4_15:ASSIN4] ~g~Prends le fusil à lunette sur ta droite. [ASM4_16:ASSIN4] ~g~Observe la femme au balcon. Elle va descendre l'escalator et demander l'heure à quelqu'un. [ASM4_17:ASSIN4] ~g~Lorsque la conversation est terminée, liquide son interlocuteur sans faire de mal à la femme. [ASM4_18:ASSIN4] ~g~Une fois la cible éliminée, récupère la serviette et amène-la à Ammu-Nation dans le centre. [ASM4_19:ASSIN4] ~g~Reste à distance de la cible. Sa proximité t'est indiquée par la barre de distance située dans le coin supérieur droit de l'écran. [ASM4_20:ASSIN4] ~g~Ne la laisse pas devenir pleine ou tu seras repéré. [ASM4_21:ASSIN4] ~g~Récupère la serviette! [ASM4_22:ASSIN4] ~g~Ramène la serviette à Ammu-Nation dans le centre. [ASM4_23:ASSIN4] ~g~Il t'a repéré et s'enfuit. Rattrape-le et récupère la serviette! [ASM4_25:ASSIN4] ~r~T'as buté la femme, imbécile! [ASM4_26:ASSIN4] ~r~La cible a embarqué pour son vol! [ASM4_27:ASSIN4] ~r~La cible t'a repéré! Tu devais garder tes distances! [ASM4_28:ASSIN4] ~r~La cible t'a repéré! Elle t'a entendu tirer! [ASM4_29:ASSIN4] ~r~Ne le tue qu'après qu'il ait parlé à la femme! [ASM4_A:ASSIN4] L'heure est venue de s'occuper d'un gros poisson, M. Teal. Il y a un fusil dans le feuillage à votre droite. [ASM4_B:ASSIN4] Surveillez la femme sur le balcon situé au-dessus des guichets d'enregistrement. Elle va avancer à travers la foule et demander l'heure à quelqu'un. [ASM4_C:ASSIN4] Vous devez tuer son interlocuteur, récupérer la mallette et l'apporter à l'emplacement indiqué sous le téléphone. {=================================== MISSION TABLE ASSIN5 ===================================} [ASM5_A:ASSIN5] Un deal important se déroule sur le toit de l'usine de crème glacée Cherry Popper. [ASM5_B:ASSIN5] Descendez toutes les personnes impliquées, embarquez la marchandise et amènez-la à la piste pour hélicos de l'aéroport. [ASM5_C:ASSIN5] Il y a une porte sur votre gauche qui mène à l'arrière de l'usine. [ASM5_1:ASSIN5] ~g~Entre dans l'enceinte située derrière l'usine de crème glacée Cherry Popper et va jusqu'au toit où se fait le deal. [ASM5_2:ASSIN5] ~g~Récupère la marchandise et amène-la à la piste pour hélicos de l'aéroport. [ASM5_3:ASSIN5] ~g~Emmène la marchandise à la piste pour hélicos de l'aéroport! {=================================== MISSION TABLE BANKJ1 ===================================} [WANTED1:BANKJ1] ~g~Largue les flics et perds ton indice de recherche! [BJM1_A:BANKJ1] Tommy! Hé, regarde-ça, c'est génial! J'ai fait installer un minibar! [BJM1_B:BANKJ1] Y'a déjà un vrai bar en bas, Ken. [BJM1_C:BANKJ1] Ouais, je sais, et alors? Bon, j'ai le tableau noir que tu m'as demandé. [BJM1_D:BANKJ1] Ah, c'est là qu'on voit l'intérêt de ton école de droit : t'as appris à suivre les instructions. [BJM1_E:BANKJ1] Bon, il me faut un mec sûr. [BJM1_F:BANKJ1] Euh, d'accord, laisse-moi réfléchir... Sûr... sûr... sûr... Je sais! Je connais un mec qui te plaira! [BJM1_G:BANKJ1] Aaah, non, merde, ce con est au trou. [BJM1_H:BANKJ1] Comment ça au trou? [BJM1_I:BANKJ1] Dans un poste de police. Il attend son transfert. [BJM1_J:BANKJ1] Je crois qu'il va être remis en liberté sur parole... [BJM1_1:BANKJ1] ~g~Sors Cam Jones de chez les flics! [BJM1_3:BANKJ1] ~g~Tu trouveras quelque chose d'utile dans la salle des casiers. [BJM1_21:BANKJ1] ~g~La carte d'accès aux cellules se trouve à l'étage. [BNK1_7:BANKJ1] Cam Jones? [BNK1_8:BANKJ1] Je viens t'aider à mettre les bouts! [BNK1_10:BANKJ1] Ouais, c'est moi... [BNK1_11:BANKJ1] Ca me va! [BNK1_13:BANKJ1] J'ai un boulot à faire et t'es mon perceur de coffre. [BNK1_14:BANKJ1] Marre de perdre mon temps dans une cellule. [BJM1_22:BANKJ1] ~g~Ramène Cam chez lui! [BJM1_23:BANKJ1] ~g~Tu dois d'abord trouver la carte d'accès! [BNK1_12:BANKJ1] Sème les flics et ramène-moi chez moi! [BJM1_20:BANKJ1] Jette ton flingue ou tu vas en subir les conséquences! [BJM1_5:BANKJ1] Accès réservé au personnel autorisé! [BJM1_2:BANKJ1] ~r~T'étais supposé faire sortir Cam, pas le faire tuer! [BJM1_4:BANKJ1] Il est armé! Descendez-le! {=================================== MISSION TABLE BANKJ2 ===================================} [BJM2_A:BANKJ2] Il nous faut un braqueur. T'en connais un? [BJM2_B:BANKJ2] Hé, Tommy, Tommy, ce truc ça permet d'rester dans l'coup, mec! [BJM2_C:BANKJ2] WoooOOOooo! [BJM2_D:BANKJ2] J'pourrais être ton braqueur! Haut les mains! Haut les mains! [BJM2_E:BANKJ2] T'es pas un braqueur, t'es un idiot. [BJM2_F:BANKJ2] Va boire un verre et ferme-la. [BJM2_G:BANKJ2] Hé, tire-toi de mon chemin! Ye ye ye ow ow! [BJM2_H:BANKJ2] Cam, qu'est-ce que t'en penses? [BJM2_I:BANKJ2] Ben, le meilleur tireur de cette ville, c'est un mec qui s'appelle Cassidy. [BJM2_J:BANKJ2] Ah ouais? [BJM2_K:BANKJ2] Ouais. Un militaire ou en tout cas c'est ce qu'il croit. [BJM2_L:BANKJ2] Je doute qu'il ait jamais été dans l'armée, mais il sait sûrement où dégotter des flingues. [BJM2_M:BANKJ2] Il devrait être au champ de tir. [BJM2_2A:BANKJ2] C'est toi, Phil Cassidy? [BJM2_2B:BANKJ2] Pourquoi? [BJM2_2C:BANKJ2] Je cherche un mec qui sache se servir d'un flingue. D'après ce que je vois, j'suis pas convaincu... [BJM2_2D:BANKJ2] Mon pote, je peux dégomme une mouche sur ta tête à 25 mètres! [BJM2_2E:BANKJ2] Vraiment? [BJM2_2F:BANKJ2] Ouais. J'ai appris ça à l'armée. [BJM2_2G:BANKJ2] Ils s'amusent souvent à descendre des mouches à l'armée? Heureusement que je paye pas d'impôts! [BJM2_2H:BANKJ2] Tu t'crois marrant peut-être, minus? [BJM2_2I:BANKJ2] Ha ha ha ha ha! [BJM2_2J:BANKJ2] Tirons quelques coups. [BJM2_1:BANKJ2] ~g~Va à Ammu-Nation dans le centre et parle à Phil Cassidy. [BJM2_4:BANKJ2] SCORE MANCHE 1 : ~1~ [BJM2_6:BANKJ2] SCORE MANCHE 2 : ~1~ [BJM2_7:BANKJ2] SCORE TOTAL : ~1~ [BJM2_9:BANKJ2] ~g~Va au point de départ de la deuxième manche. [BJM2_11:BANKJ2] ~r~Phil est mort! [BJM2_12:BANKJ2] ~r~Un des tireurs est mort! [BJM2_14:BANKJ2] ~g~Continue jusqu'au prochain secteur! [BJM2_17:BANKJ2] ~g~Va parler à Phil. [BJM2_24:BANKJ2] ~g~La cible la plus proche rapporte un point. [BJM2_25:BANKJ2] ~g~La cible intermédiaire rapporte deux points. [BJM2_27:BANKJ2] ~g~Toutes les cibles de cette manche rapportent un point. [BNK2_4:BANKJ2] Waou! [BNK2_5:BANKJ2] Il raterait une vache dans un couloir! [BNK2_7:BANKJ2] Bon, tu veux bien me rendre un service et m'assister sur un boulot? [BNK2_8:BANKJ2] Mon gars, vu comment tu tires, si tu me demandes en mariage, j'accepte. [BNK2_9A:BANKJ2] Mon gars, tu ferais mieux de remballer ton baratin et tes grands projets. T'es trop mauvais tireur. [BNK2_9B:BANKJ2] T'es trop mauvais tireur. [BJM2_28:BANKJ2] SCORE MANCHE TROIS : ~1~ [BJM2_26:BANKJ2] ~g~La cible éloignée rapporte trois points. [BNK2_1:BANKJ2] BALLES REELLES [RANGE_1:BANKJ2] SCORE POUR LE TIR : ~1~ [BJM2_2:BANKJ2] ~g~Pour quitter la manche, appuie sur la ~h~~k~~PED_JUMPING~. [BJM2_N:BANKJ2] Du calme... {=================================== MISSION TABLE BANKJ3 ===================================} [BJM3_A:BANKJ3] Les choses commencent à se mettre tranquillement en place, ici. [BJM3_B:BANKJ3] C'est quoi le plan, Tommy? Que pasa, amigo? [BJM3_C:BANKJ3] Le plan, c'est que toi, tu restes ici à glander. Bon, on a besoin d'un chauffeur. [BJM3_D:BANKJ3] Tommy, je m'en charge! Je peux conduire. [BJM3_E:BANKJ3] C'est Hilary que tu veux, mec! Pas un stupide vantard d'école de droit. [BJM3_F:BANKJ3] Hilary, c'est le meilleur. T'as jamais vu quelqu'un conduire aussi vite. Je vais l'appeler. [BJM3_G:BANKJ3] Salut, Hil, c'est Phil. Comment ça va? Non, attends, on causera plus tard, j'ai besoin que tu me rendes un service. [BJM3_H:BANKJ3] J'ai avec moi un mec du nord. Non, je crois pas qu'il était dans l'armée, mais il a besoin d'un conducteur. [BJM3_I:BANKJ3] Pour un peu d'adrénaline. Ok, compris. [BJM3_J:BANKJ3] Qu'est-ce qu'il a dit? [BJM3_K:BANKJ3] Bon, il marche, pas de problème. En fait, y'a juste un détail, tu vois, il pose toujours une condition. [BJM3_L:BANKJ3] Il refuse de bosser pour quelqu'un qui ne peut pas le battre. Un truc à cause de sa mère. [BJM3_M:BANKJ3] Quoi qu'il en soit, il veut t'affronter d'abord et il a dit qu'il te retrouvait dehors... [BJM3_2A:BANKJ3] C'est toi Tommy? Evidemment que c'est toi Tommy, enfin, je veux dire, [BJM3_2B:BANKJ3] pourquoi est-ce que quelqu'un d'autre parlerait avec moi? [BJM3_2C:BANKJ3] Bon. Ecoute-moi. [BJM3_2D:BANKJ3] Je conduis pour toi que SI, et seulement SI, tu peux conduire convenablement. [BJM3_2E:BANKJ3] Abandonne et je ne te pardonnerai jamais. [BJM3_2:BANKJ3] ~r~Hilary est mort! [BJM3_4:BANKJ3] ~g~T'as besoin d'une voiture! [BNK3_1:BANKJ3] Ok, je piloterai pour toi, mais s'il te plaît, traite-moi mal. [BNK3_3A:BANKJ3] Course de rue illégale dans Vice Point! [BNK3_3B:BANKJ3] Appel à tous les officiers. [BNK3_3C:BANKJ3] Les courses de rue sont formellement interdites! {=================================== MISSION TABLE BANKJ4 ===================================} [BNK4_A:BANKJ4] ~w~Comme vous pouvez voir, les mecs, ça va être le fric le plus facile qu'on se soit jamais fait! [BNK4_B:BANKJ4] ~w~Tommy, sérieusement, va falloir que tu considères la question judiciaire. [BNK4_C:BANKJ4] ~w~Merde! Qu'est-ce que tu fumes, mon pote? J'appelle pas ça un plan! [BNK4_D:BANKJ4] ~w~Bon, de toute manière, on a pas besoin de plan! [BNK4_E:BANKJ4] ~w~Prenez le communisme, ça c'était un plan! Et regarde où en est la Russie, maintenant! [BNK4_F:BANKJ4] ~w~Du calme, Ok? Avec une équipe comme ça, il n'y aura pas le moindre problème! [BNK4_G:BANKJ4] ~w~On amène Cam au coffre. Phil, toi et moi, on s'occupe de la sécurité et Hilary conduit la bagnole. [BNK4_H:BANKJ4] ~w~Hum, euh, t'oublies pas quelqu'un? Quelqu'un qui t'aurait aidé depuis le début dans cette ville? Quelqu'un... [BNK4_I:BANKJ4] ~w~Ken... Ken, c'est vrai. Bon, Ken va s'occuper de blanchir le pognon et de garder les boissons au frais! [BNK4_J:BANKJ4] ~w~Je comprends pas ce que je suis supposé faire ici. [BNK4_K:BANKJ4] ~w~Ecoute, c'est dans la poche. T'as jamais vu de film? [BNK4_L:BANKJ4] ~w~On entre dans la banque, on sort nos flingues et on ressort plein aux as! [P_DEAD:BANKJ4] ~r~Phil est mort! [C_DEAD:BANKJ4] ~r~Cam est mort! [H_DEAD:BANKJ4] ~r~Hilary est mort! [P_HIND:BANKJ4] ~r~T'as perdu Phil! [C_HIND:BANKJ4] ~r~T'as oublié Cam! [H_HIND:BANKJ4] ~r~Hilary est resté derrière! [GETCAR:BANKJ4] ~g~Monte dans la bagnole pour terminer le boulot! [TRASHED:BANKJ4] ~r~T'as détruit la bagnole pour s'enfuir! [BNK4_1:BANKJ4] Je vais conduire. [BNK4_2:BANKJ4] Super. Un passager. Attends que j'en parle à la réunion! [BNK4_3A:BANKJ4] Hé, fais gaffe à la caisse, Tommy! [BNK4_3B:BANKJ4] Tommy, Hilary prend trop de place! [BNK4_3C:BANKJ4] C'est pas vrai! [BNK4_3D:BANKJ4] Toi aussi! [BNK4_3E:BANKJ4] Hé, fermez-la tous les deux ou vous rentrez à pied! [BNK4_3F:BANKJ4] Ouais, Hilary. [BNK4_3I:BANKJ4] Putain, Phil! Arrête d'agiter ce truc devant moi! [BNK4_3J:BANKJ4] Ouais, tu vas éborgner quelqu'un! [BNK4_3M:BANKJ4] Mon bébé! Elle est foutue! [BNK4_3O:BANKJ4] Tu t'accroches à l'illusion de la permanence! [BNK4_3P:BANKJ4] Quoi?! [BNK4_3Q:BANKJ4] Tu crois que toutes les choses dureront! [BNK4_3R:BANKJ4] La jeunesse, les amours, les pizzas... [BNK4_3S:BANKJ4] Tout prend fin un jour, et tu dois l'accepter. [BNK4_3T:BANKJ4] Ouais, t'as raison, merci, Cam. [BNK4_3U:BANKJ4] De rien. [BNK4_3V:BANKJ4] Hé, Tommy, pourquoi on s'arrête? [BNK4_4A:BANKJ4] ~w~Hilary, continue à tourner autour du quartier. [BNK4_5:BANKJ4] ~w~Ok, Tommy, ok. [BNK4_6:BANKJ4] ~w~C'EST UN HOLD-UP! [BNK4_7:BANKJ4] ~w~PERSONNE NE BOUGE! [BNK4_8:BANKJ4] ~w~TOUT LE MONDE CONTRE LE MUR! [BNK4_9:BANKJ4] Phil! Monte la garde! [BNK4_10:BANKJ4] Bien compris! [BNK4_11:BANKJ4] Amène-toi, Cam, la salle des coffres est en haut... [BK4_12A:BANKJ4] Merde! C'est un Flange 9000! [BK4_12B:BANKJ4] Ca peut prendre des heures à ouvrir, [BK4_12C:BANKJ4] ou cinq minutes si tu peux trouver le directeur... [BNK4_13:BANKJ4] Je vais aller voir où il se planque. [BK4_14A:BANKJ4] Ca roule, Phil? [BNK4_15:BANKJ4] Pas de problème. Tout le monde se tient relax. [BNK4_16:BANKJ4] Toi! Tu viens avec moi! [BNK4_17:BANKJ4] D'accord! D'accord! Ne tirez pas! [BNK4_18:BANKJ4] J'AI DIT PERSONNE NE BOUGE! [BK4_19A:BANKJ4] C'est réglé sur une serrure à horloge, [BK4_19B:BANKJ4] Vous feriez aussi bien d'abandonner tout de suite! [BK4_20A:BANKJ4] Putain, je peux court-circuiter l'horloge, [BK4_20B:BANKJ4] on a juste besoin de ton code secret et c'est bingo! [BNK4_21:BANKJ4] Reste ici. T'essayes quoi que ce soit et t'es mort! Pigé? [BK4_24A:BANKJ4] Je vais voir Phil, je reviens tout de suite. [BK4_24B:BANKJ4] Je t'avais dit de pas toucher à cette alarme! [BNK4_25:BANKJ4] Le commando du SWAT sera là d'une minute à l'autre! [BNK4_27:BANKJ4] J'aurais besoin d'un coup de main, Tommy! [BNK4_28:BANKJ4] Ici le SWAT de Vice City! Vous êtes complètement encerclés! [BNK4_29:BANKJ4] Encerclés? HA HA HA HAAAAAaaa! [BNK4_30:BANKJ4] Ils racontent que des conneries, ces ripoux! [BK4_31A:BANKJ4] Tommy! La salle des coffres est ouverte! [BK4_34A:BANKJ4] Ok, on a la caisse de retraite du SWAT! Tirons-nous de là! [BK4_34B:BANKJ4] Bon, vous l'aurez cherché! C'était votre dernière chance! [BK4_35A:BANKJ4] Ils lancent l'assaut! [BK4_35B:BANKJ4] Planquez-vous! [BNK4_36:BANKJ4] Où est Cam? [BNK4_37:BANKJ4] Du passé... [BNK4_38:BANKJ4] C'était le dernier! On dégage! Allez! [BNK_39:BANKJ4] Merde! Où est Hilary? [BK4_40A:BANKJ4] Il me payera ça! [BNK4_42:BANKJ4] Hé! Les mecs! Montez! Je vous couvre! [BNK4_44:BANKJ4] On a réussi! On est riches! RICHES! [BNK4_45:BANKJ4] Dommage que Cam y soit resté, c'était un mec réglo! [BNK4_46:BANKJ4] Ouais. Enfin... On hérite de sa part! [BNK4_47:BANKJ4] Tu l'as dit! Ouais! [BNK4_48:BANKJ4] Tommy, que dirais-tu d'un massage? [BNK4_49:BANKJ4] Salut, Mercedes! Ouais, pourquoi pas, je me sens un peu tendu... [BNK450A:BANKJ4] Qu'est-ce que je te disais, Tommy? Hein, quoi? Le SWAT a du souci à se faire quand Kent Paul est en ville. [BNK450B:BANKJ4] Allez, allonge une plus grosse part, mon pote, j'ai besoin de nouvelles fringues! [BNK4_94:BANKJ4] ~w~Ok, les mecs. Tout roule comme prévu. [BM_DEAD:BANKJ4] ~r~T'avais besoin du directeur vivant! [ASSET_A:BANKJ4] BRAQUAGE DE BANQUE OK! [ASSET_B:BANKJ4] ~g~Le Malibu Club génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. [IDIOT:BANKJ4] ~r~T'as raison, trimballe-toi déguisé en malade pour attirer l'attention! Espèce d'abruti! {=================================== MISSION TABLE BARON1 ===================================} [COK1_A:BARON1] Allez, sale carne, allez! Allez! Putain... [COK1_B:BARON1] Connard de cheval! Je te ferai décapiter! [COK1_C:BARON1] C'est qui ce con? [COK1_D:BARON1] Tommy Vercetti, vous vous souvenez de moi? [COK1_E:BARON1] Excuse-moi, mais j'suis un peu enervé. Fais jamais confiance à une saloperie de canasson! [COK1_F:BARON1] Tu fais du bon boulot. Maintenant, tu travailles pour moi. [COK1_H:BARON1] Comme je disais, amigo, tu bosses pour moi. Maintenant, la ferme! Un salopard m'a trahi! [COK1_I:BARON1] Il croit que je sais pas combien de pognon je devrais gagner... Mais me faucher 3%, c'est pareil que d'me faucher 100%! [COK1_J:BARON1] Personne ne me fait ça! PERSONNE! [COK1_K:BARON1] Tu vas le suivre depuis son appart' pour voir où il va! On le descendra plus tard. [COK1_1:BARON1] Oh merde! [COK1_2:BARON1] Un peu trop lent, papi! [COK1_4:BARON1] Pauv' mec. [COK1_5:BARON1] Tu ferais mieux de continuer à courir, connard! [COK1_8:BARON1] ~g~Vite! Grimpe dans une caisse et suis-le! [COK1_9:BARON1] ~r~Tu es supposé le suivre, pas le buter! [COK1_10:BARON1] ~r~Va à la maison des voleurs et trouve où il planque le fric! [COK1_11:BARON1] ~g~Regarde par cette fenêtre. [COK1_7:BARON1] ~g~Il s'est échappé vers le toit. Suis-le mais ne le tue pas! [COK1_G:BARON1] Je travaille pour du fric. {=================================== MISSION TABLE BARON2 ===================================} [COK2_V:BARON2] Il commence à me faire confiance... [COK2_A:BARON2] Qui c'est qui m'a foutu un con pareil! [COK2_B:BARON2] CON! CON! CON! CON! [COK2_C:BARON2] Tommy - [COK2_D:BARON2] Quoi, Ricardo? [COK2_E:BARON2] Ces merdeux, faut toujours qu'ils essayent de m'baiser... [COK2_F:BARON2] C'est le problème avec ce genre de biz... [COK2_G:BARON2] Qu'est-ce que tu fous toi? [COK2_H:BARON2] Ces connards m'ont vraiment manqué de respect. [COK2_I:BARON2] Bientôt, le premier connard venu va penser qu'il peut vendre de la dope à Vice City. [COK2_J:BARON2] Et ensuite, ça sera quoi? Ces pourris de la Mafia, hein? [COK2_K:BARON2] Le Q.G. de ce gang est une vrai forteresse au rez-de-chaussée, [COK2_L:BARON2] alors Quentin ici... Quentin! QUENTIN! [COK2_M:BARON2] Il va te faire survoler la zone! [COK2_N:BARON2] Liquide-les! [COK2_O:BARON2] Qu'est-ce que tu fous toi? [COK2_P:BARON2] Qu'est-ce que tu fous ici? [COK2_Q:BARON2] Hé, je me suis un peu renseigné et c'est sûr [COK2_R:BARON2] que c'est Diaz qui a bousillé le deal et refroidi mon frère! [COK2_S:BARON2] Et il va t'buter toi aussi! [COK2_T:BARON2] Je peux me faire Diaz! [COK2_U:BARON2] Non, écoute-moi! JE m'occupe de Diaz! [COK2_1:BARON2] Il y a un truc que j'pige pas, pourquoi 'Quentin'? [COK2_2:BARON2] Je sais pas, ça sonne bien... Quentin Vance... [COK2_3:BARON2] Vance? Tu t'appelles Lance Vance? [COK2_4:BARON2] Ca va, hein! J'en ai déjà eu pour mon compte à l'école! [COK2_5:BARON2] T'as déjà tiré depuis un hélico? [COK2_8:BARON2] On va où, au fait? [COK2_9:BARON2] Prawn Island. [COK2_13:BARON2] Lance Vance. Mon pauv' vieux... [COK2_14:BARON2] Ok, on y est presque. [COK2_15:BARON2] On va faire un ou deux passages, [COK2_16:BARON2] Alors, descends autant de tireurs que tu peux. [COK2_17:BARON2] Et puis je te dépose et tu te débrouilles tout seul. [COK2_20:BARON2] Merde, c'est la guerre en bas! Descends ces tireurs! [COK2_21:BARON2] On s'fait canarder, mec! [COK2_22:BARON2] Ca coûte un max de réparer c'coucou, alors bute-les! [COK2_23:BARON2] Bon, t'es tout seul à partir de là. Bonne chance, mon frère! [COK2_24:BARON2] Etat hélico : [COK2_25:BARON2] ~g~Va récupérer le pognon sur le toit. [COK2_27:BARON2] T'es sur MES plates-bandes, connard! [COK2_28:BARON2] Ta fin est proche! [COK2_6:BARON2] Non. Je vais m'entraîner un peu en chemin. [OPEN_B:BARON2] Les barrières sur les routes pour le centre ville ont été retirées. {=================================== MISSION TABLE BARON3 ===================================} [COK3_A:BARON3] Tu fais moins le fier maintenant, hein! [COK3_B:BARON3] Ahahahaha, ahahahaha! [COK3_C:BARON3] Ho! Fais gaffe où tu braques ce truc! [COK3_D:BARON3] Plus de merde de pigeon sur ma bagnole, hein, Tommy! [COK3_E:BARON3] Faut croire que non... [COK3_F:BARON3] T'as sacrément raison. Bon, écoute, [COK3_G:BARON3] tu sais qui possède le bateau le plus rapide de la côte est? [COK3_H:BARON3] Je suis pas bien sûr. [COK3_I:BARON3] MOI! Et je voudrais pas que ça change... [COK3_J:BARON3] Tous les contrebandiers d'ici à Caracas ne rêvent que d'un seul truc, un bateau plus rapide que le mien. [COK3_K:BARON3] Et la rumeur prétend que le chantier naval Hull-o-caust vient juste d'en finir un [COK3_L:BARON3] pour le compte de je ne sais quel connard du Costa Rica. [COK3_M:BARON3] Alors, Tommy... JE VEUX CE BATEAU! [COK3_N:BARON3] Je crois que tes pigeons sont de retour... [COK3_O:BARON3] Ah! Je croyais que je t'avais eu! D'où tu sors? [COK3_P:BARON3] Pigeons d'mes deux! Boum! Aaaaah! [COK3_5:BARON3] ~g~Trouve l'interrupteur pour abaisser le bateau. [COK3_6:BARON3] ~g~Amène le bateau à la résidence. [COK3_7:BARON3] ~r~T'as détruit le bateau! [COK3_8:BARON3] ~g~Va au chantier naval des docks et fauche le bateau le plus rapide. [COK3_9:BARON3] ~g~Monte dans le bateau. {=================================== MISSION TABLE BARON4 ===================================} [COK4_A:BARON4] Ejection! Connerie d'engin! [COK4_B:BARON4] Pourquoi tu m'fais ça? [COK4_C:BARON4] Pour qui tu t'prends, putain de magnéto! Grrr! [COK4_D:BARON4] ENCULE! [COK4_E:BARON4] Si il m'bouffe ma cassette préférée d'El burro, il est fini! [COK4_F:BARON4] Qu'est-ce que je peux faire d'autre? [COK4_G:BARON4] Il doit pas être branché, c'est tout... [COK4_H:BARON4] Hein? [COK4_I:BARON4] Merde! Tant pis, j'peux m'en acheter cent si ça me chante. [COK4_J:BARON4] Bon, Tommy, [COK4_K:BARON4] tous les mois, il y a un indépendant qui met le cap sur Vice City pour y amarrer son yatch. [COK4_L:BARON4] Il vend sa cargaison au premier bateau. [COK4_M:BARON4] Je veux que tu prennes le speedboat, [COK4_N:BARON4] que tu largues tous les autres connards intéressés [COK4_O:BARON4] et que tu ramènes la cargaison ici. Ok? [COK4_P:BARON4] Laisse-moi deviner. T'as pensé que je pourrais avoir besoin d'un ange-gardien. [COK4_Q:BARON4] Je dis juste que t'as intérêt à me laisser entrer, mon pote. [COK4_R:BARON4] Tu peux m'sortir tout un tas de conneries sur le dur solitaire, [COK4_S:BARON4] mais je sais qu'un de ces quatre, j'vais t'sauver la mise. [COK4_T:BARON4] et qu'après tu voudras me rouler une pelle! [COK4_U:BARON4] Taré, va! [COK4_V:BARON4] Hahahaha [COK4_1:BARON4] Tommy, on sait que c'est Diaz qui a fait foirer notre deal... [COK4_3:BARON4] Alors pourquoi on bosse pour sa gueule? [COK4_4:BARON4] Plus on en apprend maintenant, plus on en saura quand on s'emparera de cette ville! [COK4_5:BARON4] J'aime bien ton style, mon pote. Vraiment culotté. [COK4_12:BARON4] Fais gaffe, ils arrivent de partout! [COK4_13:BARON4] On les a eu! Fonce chez Diaz aussi vite que tu peux! [COK4_14:BARON4] Tu veux ta part de plancton? [COK4_15:BARON4] Salue la poiscaille de ma part! [COK4_16:BARON4] Mange! Mange! [COK4_19:BARON4] Encore des emmerdes droit devant! [COK4_20:BARON4] Il y a des porte-flingues sur cette jetée! [COK4_24:BARON4] Joli coup, mon pote. T'es vraiment un cinglé de première! [COK4_25:BARON4] Ah... Merci. [COK4_26:BARON4] A la prochaine, Tommy. [COK4_27:BARON4] Okay, M. Lance Vance Danse. [COK4_28:BARON4] ~g~Atteins le yatch avant les autres bateaux! [COK4_31:BARON4] ~g~Va jusqu'au bateau le plus rapide de la jetée! [COK4_32:BARON4] ~r~Trop lent! [COK4_33:BARON4] ~r~T'as détruit le bateau! [COK4_34:BARON4] ~g~Bousille ces bateaux! [COK4_35:BARON4] Etat du bateau : {=================================== MISSION TABLE BARON5 ===================================} [PROP_A:BARON5] PROPRIETE ACHETEE! [COK4_30:BARON5] ~r~Lance est mort! [ASS1_A:BARON5] J'ai récupéré des flingues. Ils sont dans l'coffre. [ASS1_B:BARON5] Merde alors! Où c'est que t'as dégoté tout ça? [ASS1_C:BARON5] Je l'gardais au frais pour une grande occasion... [ASS1_D:BARON5] Ca te botte? [ASS1_E:BARON5] Tu parles que ça m'botte. [ASS1_F:BARON5] Espèce de connard... [ASS1_G:BARON5] Ma belle maison... [ASS1_H:BARON5] Regarde ce que t'en as fait! [ASS1_I:BARON5] Ca c'est pour mon frère! [ASS1_J:BARON5] Je te faisais confiance, Tommy... [ASS1_K:BARON5] Je t'aurais aidé à devenir... [ASS1_L:BARON5] Bonne nuit, Diaz. [ASS1_1:BARON5] Cet endroit va bientôt grouiller de salopards... Fais gaffe. [ASS1_2:BARON5] T'en fais pas, Tommy, je te couvre. [ASS1_4:BARON5] Diaz doit être à l'intérieur! [ASS1_13:BARON5] DIAZ! Je suis venu reprendre ton affaire! [ASS1_14:BARON5] TOMMY! Tu me trahis... Idiot! Je vais te buter tout de suite! [ASS1_16:BARON5] ~g~Tue Diaz! [BUD1:BARON5] Lance [ASS1_18:BARON5] ~g~La porte est verrouillée. Trouve un autre chemin. [ASS1_19:BARON5] Par ici! [ASS1_20:BARON5] Tommy, mon problème c'est Quentin, pas toi, mec! {=================================== MISSION TABLE BIKE1 ===================================} [BM1_A:BIKE1] Où est Baker? [BM1_B:BIKE1] Je cherche Big Mitch Baker... [BM1_C:BIKE1] Qui le cherche? [BM1_D:BIKE1] Tommy Vercetti. [BM1_E:BIKE1] Vercetti... [BM1_F:BIKE1] T'as pas l'air d'un flic, ce qui te donne droit à une minute... [BM1_G:BIKE1] T'as intérêt à faire court! [BM1_H:BIKE1] Kent Paul dit que ça pourrait t'intéresser de te charger de la sécurité pour un boulot qu'il prépare. [BM1_I:BIKE1] Kent Paul? Ah! Pas étonnant qu'il t'ai envoyé. [BM1_J:BIKE1] La dernière fois qu'il est passé ici, il en est ressorti par la fenêtre avec rien d'autre que son costume d'anglais. [BM1_K:BIKE1] Bon, t'es intéressé ou pas? [BM1_L:BIKE1] On ne rend des services qu'à ceux qui font partie de la bande. [BM1_M:BIKE1] Comment je me joins à vous? [BM1_N:BIKE1] On est pas un club de loisirs, mon pote! Tu peux conduire une bécane? [BM1_O:BIKE1] Tu peux t'asseoir sur un tabouret de bar et boire? [BM1_P:BIKE1] Cougar, Zeppelin, allez voir comment elle se débrouille sur une bécane... [BM1_2:BIKE1] ~g~Il te faut une Freeway ou une Angel pour participer! [BM1_3:BIKE1] ~r~Les concurrents ont été attaqués! [BIKE1_1:BIKE1] Bon, chouettes fringues. Voyons ce que tu peux faire. [BM1_1:BIKE1] ~g~Enfourche une Freeway ou une Angel et va sur la grille de départ. {=================================== MISSION TABLE BIKE2 ===================================} [BM2_4:BIKE2] ~r~Tu n'as pas rempli le chaosmètre à temps! [BM2_1:BIKE2] CHAOSMETRE : [BM2_A:BIKE2] Ha ha ha, je t'ai encore eu! [BM2_B:BIKE2] Hé, Vercetti. [BM2_C:BIKE2] Cougar dit que tu te débrouilles plutôt bien sur une bécane. [BM2_D:BIKE2] Ouais, mais combien d'épreuves il va encore falloir que je me tape? [BM2_E:BIKE2] Je suis très occupé, mon pote. [BM2_F:BIKE2] Si c'est une baston qui peut régler la question, alors amène-toi! [BM2_G:BIKE2] Devenir l'un d'entre nous, c'est pas qu'une question de baston. C'est appartenir à une famille. [BM2_H:BIKE2] Ouais, j'ai déjà appartenu à une famille avant et ça n'a pas bien collé. [BM2_I:BIKE2] Peut-être, mais notre famille veille sur les siens. [BM2_J:BIKE2] On demande à personne de faire le sale boulot pour l'abandonner au trou pendant quinze ans... [BM2_K:BIKE2] Et ouais, je me suis renseigné. [BM2_L:BIKE2] Ce que tu vois, c'est la plus grande famille d'inadaptés, de parias et d'ordures qui existe. [BM2_M:BIKE2] Certains ont même été trahis par leur propre pays! [BM2_N:BIKE2] On m'a foutu en taule pendant la guerre du Vietnam! Saloperie... [BM2_O:BIKE2] Voilà pourquoi je vais te demander d'aller foutre le bordel. [BM2_P:BIKE2] Ce pays de merde a vraiment besoin d'un coup de pied au cul et c'est nous qui allons le lui donner! [BM2_Q:BIKE2] Alors sors de là, attrape une bécane et montre à cette ville à quel point t'es énervé! [BM2_R:BIKE2] D'accord. [BM2_2:BIKE2] ~g~Tu dois remplir le chaosmètre dans le temps imparti pour nous prouver que t'es un dur de dur! [BM2_3:BIKE2] ~g~Ce bruit veut dire qu'une partie du chaosmètre est remplie, continue comme ça. {=================================== MISSION TABLE BIKE3 ===================================} [BM3_A:BIKE3] Salut, Mitch. [BM3_B:BIKE3] Hé! Mais c'est ce gros méchant de Vercetti! [BM3_C:BIKE3] Maintenant, je veux voir à quel point tu peux te battre pour ton secteur. [BM3_D:BIKE3] Un gang local a commis l'erreur de me faucher ma bécane... [BM3_E:BIKE3] Probablement un truc de machos ou quelque chose de ce genre. [BM3_F:BIKE3] Moi et mes gars, on pourrait aller leur donner la leçon qu'ils méritent... [BM3_G:BIKE3] Enfin... [BM3_H:BIKE3] Je me suis dit que ça serait une excellente initiation pour toi. [BM3_I:BIKE3] Tu me ramènes ma bécane et tu pourras dire à Paul qu'on assurera la sécurité. [BM3_2:BIKE3] ~r~Tu étais supposé ramener la bécane, pas la bousiller! [BM3_3:BIKE3] ~g~Ramène la bécane au bar! [BM3_4:BIKE3] ~g~Monte sur la bécane! [INTRUDE:BIKE3] ~g~T'as été repéré! [BM3_6:BIKE3] ~g~Ils se planquent derrière Ammu-Nation dans le centre. [BM3_7:BIKE3] ~g~T'auras besoin d'une bécane rapide pour accéder au toit. [BM3_8:BIKE3] ~g~Sers-toi de la bécane pour sauter de ces marches jusqu'à l'autre côté de la route. [BM3_1:BIKE3] ~g~Un gang local a volé l'Angel de Mitch Baker. Récupère-la! [BM3_9:BIKE3] ~g~Récupère l'Angel de Mitch Baker et dégage de là! {=================================== MISSION TABLE BMX_1 ===================================} [BMX_REC:BMX_1] ~g~Nouveau record établi : ~1~! [GETBIK2:BMX_1] Tu as ~1~ secondes pour enfourcher une bécane! {=================================== MISSION TABLE BOATBUY ===================================} [DRUG_1:BOATBUY] Bonjour? Y'a quelqu'un? Y'A QUELQU'UN??? [DRUG_2:BOATBUY] Ferme-la. Il y a un mec, ici. [DRUG_3:BOATBUY] Hé, mon pote! T'es le nouveau proprio? [DRUG_4:BOATBUY] Ouais. Lequel de ces bateaux est le plus rapide? [DRUG_5:BOATBUY] Il est déjà à l'eau, mon pote, [DRUG_6:BOATBUY] je me suis dit que tu voudrais peut-être l'essayer. [DRUG_7:BOATBUY] Mon pote, il est déjà équipé d'un moteur de 300 chevaux... [DRUG_8:BOATBUY] ...et avec la coque en fibre de verre, il touche même pas l'eau! [DRUG_9:BOATBUY] Il monte à 60 noeuds en quatre secondes, mon pote... [DRUG_10:BOATBUY] Et il peut embarquer vingt écopes de la meilleure jamaïquaine dans la coque. [DRUG_11:BOATBUY] Alors vas-y, mon pote, il t'attend pour décoler! [DRUG_12:BOATBUY] Ouais, hé, mon pote, t'as du feu? [DRUG_13:BOATBUY] Mec? {=================================== MISSION TABLE CAP_1 ===================================} [CAP1_B1:CAP_1] ~g~La mafia taxe tes commerces. Trouve-les et bute-les. [CAP1_B2:CAP_1] ~g~La mafia taxe le chantier naval! [CAP1_B3:CAP_1] ~g~La mafia taxe l'usine de crème glacée! [CAP1_B4:CAP_1] ~g~La mafia taxe la concession automobile! [CAP1_B5:CAP_1] ~g~La mafia taxe la compagnie de taxis! [CAP_01:CAP_1] Ok, où est l'urgence? [CAP_02:CAP_1] QUI? [CAP_03:CAP_1] Tommy... Des truands de la mafia... Ils ont dit qu'ils venaient prendre leur part... [CAP_04:CAP_1] ... Ils ont dit que c'était le pognon de Mr Forello... J'me sens mal... [CAP_05:CAP_1] Forelli? SONNY Forelli? [CAP_06:CAP_1] Ouais, c'est ça... je crois... Ils ont beaucoup insisté... [CAP_07:CAP_1] T'inquiète pas, je suis pas en colère contre toi. [CAP_08:CAP_1] Emmène-le à l'hosto. [CAP_09:CAP_1] Tommy... Taille un nouveau trou du cul à ce salopard de ma part... [CAP_10:CAP_1] Je vais lui en rajouter deux! [CAP1_2:CAP_1] Tu connais la règle, Vercetti! [CAP1_3:CAP_1] Avec les compliments de M. Forelli! [CAP1_4:CAP_1] C'est le boucher d'Harwood! [CAP1_5:CAP_1] Tu diras à Sonny... de dégager! [CAP1_6:CAP_1] Vice City est à moi, désormais, pas à lui! [CAP1_7:CAP_1] Tu crois que tu peux m'avoir, Vercetti? [CAP1_8:CAP_1] On va continuer à s'amener jusqu'à ce que tu sois raide mort, Vercetti! [CAP1_9:CAP_1] T'as pas une chance, connard psychotique! [CAP1_10:CAP_1] Je vais te descendre, Vercetti! [CAP1_11:CAP_1] T'as toujours été un con! [CAP1_12:CAP_1] Tu vas crever, Vercetti! [CAP1_B6:CAP_1] ~g~Tu as trouvé l'encaisseur. Tue-le. [CAP1_B7:CAP_1] ~g~Tu as perdu l'encaisseur. [CAP1_B8:CAP_1] ~r~L'encaisseur a taxé toutes tes affaires. [CAP1_B9:CAP_1] ~g~La mafia a taxé le Malibu! [CAP1_B0:CAP_1] ~g~La mafia a taxé le studio de cinéma! [CAP1_C2:CAP_1] ~g~La mafia est arrivée au chantier naval! [CAP1_C3:CAP_1] ~g~La mafia est arrivée à l'usine de crème glacée! [CAP1_C4:CAP_1] ~g~La mafia est arrivée à la concession automobile! [CAP1_C5:CAP_1] ~g~La mafia est arrivée à la compagnie de taxis! [CAP1_C9:CAP_1] ~g~La mafia est arrivée au Malibu! [CAP1_C0:CAP_1] ~g~La mafia est arrivée au studio de cinéma! [CAP1_D2:CAP_1] ~g~La mafia quitte le chantier naval! [CAP1_D3:CAP_1] ~g~La mafia quitte l'usine de crème glacée! [CAP1_D4:CAP_1] ~g~La mafia quitte la concession automobile! [CAP1_D5:CAP_1] ~g~La mafia quitte la compagnie de taxis! [CAP1_D9:CAP_1] ~g~La mafia quitte le Malibu! [CAP1_D0:CAP_1] ~g~La mafia quitte le studio de cinéma! [CAP1B10:CAP_1] Tu t'es occupé des taxeurs. Y'en a d'autres qui arrivent. {=================================== MISSION TABLE CARBUY ===================================} [CAR1_1:CARBUY] B.J. Smith. Et vous devez être Mr Vercetti. [CAR1_2:CARBUY] Vous voulez visiter? [CAR1_3:CARBUY] Pourquoi pas? [CAR1_4:CARBUY] Vous savez, je suis bien triste de vendre l'affaire... [CAR1_5:CARBUY] C'était mon premier investissement de professionnel. [CAR1_6:CARBUY] Mais l'heure est venue pour moi de continuer ma route. [CAR1_7:CARBUY] Vous quittez la ville? [CAR1_8:CARBUY] Pas trop pressé, j'espère? [CAR1_9:CARBUY] Non, je sors juste de ma retraite et je prépare mon retour dans les affaires. [CAR1_10:CARBUY] Cette affaire était pas trop solide, [CAR1_11:CARBUY] mais mon personnel a pris sur lui de faire un peu plus [CAR1_12:CARBUY] créatif en ce qui concerne les méthodes pour devenir rentable. [CAR1_13:CARBUY] Evidemment, je pourrais démanteler l'affaire avant de la refiler. [CAR1_14:CARBUY] Merde, je pourrais y foutre le feu si ça me chantait. [CAR1_15:CARBUY] C'est un terrain de premier ordre. [CAR1_16:CARBUY] Oh, je ne m'en ferais pas pour ça. [CAR1_17:CARBUY] Cet endroit me semble idéal. [CAR1_18:CARBUY] Ouais, il l'est, alors, marché conclu? {=================================== MISSION TABLE CARPAR1 ===================================} [MM_1_A:CARPAR1] ~g~Récupère ~y~5 points de passage~r~ SANS ~g~renverser de ~r~CONES! ~g~Tu peux les récupérer dans ~y~N'IMPORTE QUEL ORDRE. [CONE_1:CARPAR1] ~r~Tu as renversé un cône! [MM_1_C:CARPAR1] ~y~FRANCHIS~g~ un point de passage pour enclencher le compteur. ~g~Chaque point de passage te crédite ~y~~1~ SECONDES~g~. {=================================== MISSION TABLE COPCAR ===================================} [CLEVEL:COPCAR] Missions d'autodéfense niveau ~1~ [C_COMP1:COPCAR] Niveau 12 de mission Auto-défense réussi : votre max de gilet pare-balles passe à 150. [KILLS:COPCAR] VICTIMES : [C_BREIF:COPCAR] ~g~Suspect aperçu pour la dernière fois dans le secteur ~a~. [C_PASS:COPCAR] MENACE ENRAYEE : $~1~ [COPCART:COPCAR] ~g~Tu as ~1~ secondes pour retourner à une voiture de police avant que la mission ne s'achève. [C_CANC:COPCAR] ~r~Mission d'autodéfense annulée! [C_TIME:COPCAR] ~r~Ta carrière de flic est terminée! {=================================== MISSION TABLE COUNT1 ===================================} [CM1_A:COUNT1] Vercetti? Hé, vous avez acheté la vieille imprimerie? [CM1_B:COUNT1] Ouais, mon vieux a travaillé sur ces machines. [CM1_C:COUNT1] J'aurais voulu marcher sur ses traces, mais... J'ai suivi une autre voie. [CM1_D:COUNT1] Vous avez l'intention de vendre les vieilles machines, et de tout casser? [CM1_E:COUNT1] Je m'disais qu'on pourrait peut-être imprimer quelque chose, un journal ou un magazine... [CM1_F:COUNT1] Arrête tes conneries, fiston! J'ai toujours rêvé d'imprimer mon propre pognon! C'est pas très compliqué! [CM1_G:COUNT1] Tu sais, j'ai fait ça sur une petite échelle pendant des années. [CM1_H:COUNT1] Ah ouais? [CM1_I:COUNT1] Ouais. Mais il nous faut des plaques de bonne qualité. [CM1_J:COUNT1] Evidemment! Il y a un syndicat de contrefaçon qui opère déjà en Floride! [CM1_K:COUNT1] Un syndicat? [CM1_L:COUNT1] Ouais. C'est le bruit qui court en tout cas. [CM1_M:COUNT1] Je connais un mec qui en saura sûrement plus... [CM1_N:COUNT1] On avait l'habitude de passer nos soirées ensemble, à nettoyer les rouleaux... [CM1_2A:COUNT1] Mate-moi ce cul! [CM1_2B:COUNT1] Ok, ma belle, c'est l'heure de l'initiation! [CM1_2C:COUNT1] Salut, vieille branche, comment ça va? [CM1_2D:COUNT1] Que sais-tu à propos de la contrefaçon? [CM1_2E:COUNT1] Oh, je vais bien, Paul, et toi? [CM1_2F:COUNT1] Viens ici! [CM1_2G:COUNT1] Bon! Bon! Bon! Tu es visiblement un homme très occupé! [CM1_2H:COUNT1] Tout ce que je sais, c'est que les Triades fournissent les plaques. [CM1_2I:COUNT1] Elles ont une compagnie de navigation sur les quais [CM1_2J:COUNT1] et le patron devrait savoir quand les plaques arrivent! [CM1_2K:COUNT1] Merci, Paul. [CM1_2L:COUNT1] C'est quoi ton problème, pauvre maniaque! [CM1_2M:COUNT1] Un autre verre, en vitesse! [CM1_3:COUNT1] ~g~T'as été repéré! [CM1_5:COUNT1] ~g~Va retrouver Kent Paul au Malibu! [CNT1_1:COUNT1] T'es qui toi? Aïe! Pas le visage! Pas le visage! [CM1_1:COUNT1] ~g~Va au bateau de la Chartered Libertine Lines sur les docks. [CM1_2:COUNT1] ~g~L'officier de navigation détient l'information dont tu as besoin. [CNT1_2:COUNT1] Ok, je parle! Je parle! [CM1_6:COUNT1] ~g~Ramène l'info à l'imprimerie! {=================================== MISSION TABLE COUNT2 ===================================} [CNT2_B1:COUNT2] Bon, le coursier déplace les plaques des docks aujourd'hui. [CNT2_B2:COUNT2] Faut l'intercepter et récupérer ces plaques, semer les flics éventuels et revenir ici. [CNT2_B3:COUNT2] En fonction de comment ça marche, [CNT2_B4:COUNT2] on aura entre cinq minutes et toute l'année pour imprimer le pognon avant que le syndicat de contrefaçon rapplique. [CNT2_B5:COUNT2] Quoi qu'il en soit, je veux voir les premiers billets sortir de la presse cinq minutes après mon retour! C'est compris? [CNT2_B6:COUNT2] T'inquiète pas, Tommy, on sera prêts. [CNT2_B7:COUNT2] Les gars et moi, on sera dans le coin au cas où t'aurais besoin de renforts. [CNT2_B8:COUNT2] Bon, tout le monde a compris? Parfait. A plus tard. [CNT2_01:COUNT2] ~g~Le ~r~coursier~g~ qui porte les plaques va bientôt arriver aux ~y~docks~g~ en hélico. [CNT2_02:COUNT2] ~r~Le coursier s'est enfui en hélico! [CNT2_03:COUNT2] ~r~Le coursier est parvenu à destination sain et sauf, c'est trop tard! [CNT2_04:COUNT2] ~r~Tu as détruit les plaques dans l'explosion! [CNT2_05:COUNT2] ~g~Tu as les plaques. Emmène-les à l'imprimerie! [CNT2_06:COUNT2] ~g~Le coursier est mort et il a jeté les plaques. Récupère-les en premier. [CNT2_07:COUNT2] ~g~Un des gardes a ramassé les plaques, ne le laisse pas s'enfuir! [CNT2_08:COUNT2] ~g~Le ~r~coursier~g~ avec les plaques est arrivé aux docks. [CNT2_4:COUNT2] Affaire privée! Dégagez! [CNT2_09:COUNT2] IMPRIMERIE ACQUISE [CNT2_10:COUNT2] ~g~L'imprimerie va maintenant générer un revenu maximum de $~1~. Veille à relever régulièrement les compteurs. [CNT2_11:COUNT2] ~r~Les plaques sont au fond de la mer! {=================================== MISSION TABLE CUBAN1 ===================================} [CUB1_A:CUBAN1] Si, mec? [CUB1_B:CUBAN1] Hé, du calme, papa, ce mec est là pour moi. Hé, c'est toi le mec? [CUB1_C:CUBAN1] Oh ouais. C'est toi le mec. Je crois bien, tu sais. [CUB1_D:CUBAN1] Non, je crois pas. [CUB1_E:CUBAN1] Ah ouais? Amène-toi, gros dur! [CUB1_F:CUBAN1] Tu crois que tu peux jouer avec moi? [CUB1_G:CUBAN1] Tu penses que tu peux jouer au con avec moi? [CUB1_H:CUBAN1] Non, je pense que tu joues assez le con pour nous deux. [CUB1_I:CUBAN1] Hé, il t'a traité de con, fiston. [CUB1_J:CUBAN1] Et moi, je le traite de fillette, papa. [CUB1_K:CUBAN1] Regarde-le, tout habillé comme ça. [CUB1_L:CUBAN1] C'est la soirée des pouffiasses, ce soir? [CUB1_M:CUBAN1] Toi qu'est plutôt balaise, pourquoi tu te fringues en nana? [CUB1_N:CUBAN1] Et t'as une petite culotte comme les gonzesses? [CUB1_O:CUBAN1] Qu'est-ce que t'as contre les femmes? Tu préfères les mecs, mon gros? [CUB1_P:CUBAN1] J'aime les femmes! J'aime toutes les femmes! J'aime ma maman, Chico! [CUB1_Q:CUBAN1] Ok, ok, j'te crois sur parole. [CUB1_R:CUBAN1] Tu sais conduire, amigo? [CUB1_S:CUBAN1] Ouais... Comme une gonzesse. [CUB1_T:CUBAN1] Très drôle... Tu me plais, ma grosse, peut-être que tu peux aider. [CUB1_U:CUBAN1] Tu peux peut-être nous montrer que t'es un vrai mec? [CUB1_V:CUBAN1] Sors le bateau. [CUB1_W:CUBAN1] Prouve-moi que t'as des couilles énormes [CUB1_X:CUBAN1] et pas une paire de pois chiches! [CUB1_02:CUBAN1] Ok, mec, traite-la comme une gonzesse. [CUB1_03:CUBAN1] Pas mal, t'es un vrai macho. [CUB1_04:CUBAN1] Merde, t'en a vraiment dans le pantalon, amigo. [CUB1_05:CUBAN1] Amigo, t'es un vrai mec, mec. [CUB1_06:CUBAN1] Tu te prends pour un homme, mec? [CUB1_07:CUBAN1] T'es qu'une petite poule mouillée, petit, c'est ta mère qui te manque? [CUB1_08:CUBAN1] Tu gâches tout! Tu marches comme un mec, tu causes comme un mec, mais tu conduis comme une gonzesse! [CUB1_09:CUBAN1] Mec, t'es un vrai mec de chez mec, mec! Putain, je t'adore, mec! [CUB1_10:CUBAN1] C'est quand tu veux, mec. T'as des couilles de taureau et tous mes potes ont des couilles énormes! [CUB1_11:CUBAN1] ~r~T'as tué Rico! [CUB1_12:CUBAN1] Va jusqu'au premier point de contrôle pour commencer le test. [CUB1_14:CUBAN1] Retourne dans le bateau! [CUB1_15:CUBAN1] ~r~T'es trop lent, mec! [CUB1_01:CUBAN1] Salut, moi c'est Rico. C'est toi qu'a des couilles de taureau? [CUB1_13:CUBAN1] ~g~Tu as trois minutes pour terminer le parcours. {=================================== MISSION TABLE CUBAN2 ===================================} [CUB2_25:CUBAN2] BUTE TOUS LES HAITIENS! [CUB2_A:CUBAN2] Un cafecito, por favor, Alberto... [CUB2_N:CUBAN2] Tout de suite, Tommy. [CUB2_B:CUBAN2] Poppa! Un gran problema! [CUB2_O:CUBAN2] Umberto, mon fils, que s'est-il passé? [CUB2_C:CUBAN2] Les Haïtiens! Je hais les Haïtiens! [CUB2_D:CUBAN2] C'est la dernière fois qu'ils me font chier! [CUB2_E:CUBAN2] Ces Haïtiens... On va les liquider! [CUB2_F:CUBAN2] On a juste besoin de renforts... [CUB2_G:CUBAN2] J'ai déjà perdu pas mal de frères là-bas. [CUB2_H:CUBAN2] Amigo, tu conduis pas mal! [CUB2_I:CUBAN2] Pour une gonzesse... Pas vrai? [CUB2_J:CUBAN2] C'est pas l'heure des vannes! [CUB2_K:CUBAN2] Allez, conduis encore pour moi! [CUB2_L:CUBAN2] Récupère mes gars là-bas et puis on s'occupe de ces Haïtiens! [CUB2_M:CUBAN2] Ils m'ont cherché et ils sont tombés sur le mec le plus balaise de la ville! [CUB2_01:CUBAN2] C'est trop petit, mec, il te faut une plus grosse bagnole. [CUB2_02:CUBAN2] On a besoin de renforts du café! [CUB2_03:CUBAN2] ~g~Prend une bagnole et va ramasser les Cubains à l'extérieur du café de Robina. [CUB2_04:CUBAN2] ~g~Amène les Cubains à l'endroit de la bagarre. [CUB2_05:CUBAN2] Liquide ce lâche de tireur embusqué! [CUB2_07:CUBAN2] Ils se battent comme des filles! A couvert! [CUB2_09:CUBAN2] Un sniper sur le toit! [CUB2_11:CUBAN2] ~r~Imbécile, on avait besoin de cette bagnole! [CUB2_12:CUBAN2] Hé, amigo! Content de te voir que t'as réussi! [CUB2_13:CUBAN2] Putains de Haïtiens, on va tous les descendre! [CUB2_14:CUBAN2] CHAAAAAARGEZ! [CUB2_15:CUBAN2] Maintenant, mes frères! Chaaaargez! [CUB2_16:CUBAN2] Tommy, on a fait la preuve de notre courage! [CUB2_17:CUBAN2] Piquons cette camionnette bourrée de drogue et tirons-nous! [CUB2_18:CUBAN2] ~g~Trouve une bagnole et va chercher les Cubains. [CUB2_19:CUBAN2] On va se battre comme des hommes! [CUB2_21:CUBAN2] Battez-vous avec vos couilles! [CUB2_22:CUBAN2] ~g~Elimine les derniers Haïtiens pour que les Cubains puissent avancer. [CUB2_23:CUBAN2] ~g~Little Haiti va grouiller de Haïtiens voulant régler leurs comptes avec les Cubains. Fais gaffe à tes couilles. [CUB2_24:CUBAN2] ~g~Retourne au café de Robina avec la camionnette et gare-la derrière. {=================================== MISSION TABLE CUBAN3 ===================================} [CUB3_A:CUBAN3] Alberto. Un café, senor. [CUB3_B:CUBAN3] Papa, ne sers pas ce morbac... [CUB3_C:CUBAN3] T'es un hypocrite, Tommy! [CUB3_D:CUBAN3] T'es soit un hypocrite, soit une gonzesse! [CUB3_E:CUBAN3] Les Haïtiens, mec! Ils se foutent de ma gueule! [CUB3_F:CUBAN3] Du calme. C'est quoi ton problème? [CUB3_G:CUBAN3] Ils se foutent ma gueule, Tommy, de ma gueule! [CUB3_H:CUBAN3] Umberto Robina! Ils font comme ça leur chante! [CUB3_I:CUBAN3] Personne fait ce qu'il veut, Umberto, ils font ce que tu les laisses faire. [CUB3_J:CUBAN3] Quoi? [CUB3_K:CUBAN3] Tu veux que quelqu'un s'en occupe? [CUB3_L:CUBAN3] Je peux m'en charger, mais ça va te coûter cher! [CUB3_M:CUBAN3] Je sais qu'on est frères et tout et tout, mais ça, c'est les affaires... [CUB3_N:CUBAN3] Tommy, t'es un vrai mec! Un businessman! Un gentleman! [CUB3_O:CUBAN3] Ces Haïtiens. Ils ont de la marchandise de première qualité qui arrive par la mer. [CUB3_P:CUBAN3] On la prend et on en finit avec eux. [CUB3_Q:CUBAN3] Tu t'en charges et je veillerai sur toi. Comme mon frère. Comme mon fils. [CUB3_R:CUBAN3] J'préfère que tu me files du pognon, plutôt que de me faire sauter sur tes genoux, amigo... [CUB3_01:CUBAN3] Salut, Rico. Chouette bateau. T'es prêt? [CUB3_03:CUBAN3] ~g~Récupère toutes les serviettes pleines de drogue et de cash. [CUB3_04:CUBAN3] ~g~Ramène la drogue et le cash à Umberto. [CUB3_05:CUBAN3] Si, Tommy, faut que tu tires bien, aujourd'hui. [CUB3_06:CUBAN3] Mon bateau, je voudrais pas qu'il se retrouve plein de trous, ok? [CUB3_07:CUBAN3] ~g~Va retrouver Rico. Il te conduira au rendez-vous. [CUB3_02:CUBAN3] ~g~TUE TOUS LES HAITIENS EN BATEAU! [CUB3_08:CUBAN3] Oh oh... Les Cubains. On nous attaque! {=================================== MISSION TABLE CUBAN4 ===================================} [CUB4_A:CUBAN4] Salut, les filles, vous savez ce que je vais faire? [CUB4_B:CUBAN4] Je vais aller me descendre un Haïtien. Et après, vous savez quoi? [CUB4_C:CUBAN4] Après, je vais me vider les couilles comme un homme! [CUB4_D:CUBAN4] T'as déjà vu ça chica? Un peu comme ça... [CUB4_E:CUBAN4] Pauv' mec! [CUB4_F:CUBAN4] Connard! [CUB4_G:CUBAN4] Hé, ma poule, je te toucherai même pas avec une perche! [CUB4_H:CUBAN4] Umberto Robina, il aime les femmes! Pas les vieilles dindes en jupe! [CUB4_I:CUBAN4] Tommy! Tommy, je t'aime, je t'aime! Allons-y! [CUB4_J:CUBAN4] Où ça? Je peux pas avoir un café d'abord? [CUB4_K:CUBAN4] Pas le temps pour un café! D'ailleurs, je viens d'en prendre un! [CUB4_L:CUBAN4] Faut qu'on liquide les Haïtiens! [CUB4_M:CUBAN4] Tommy, tu sais comment on tue un serpent? [CUB4_N:CUBAN4] Tu le mords au cul! Ahahaha! [CUB4_O:CUBAN4] Si tu le dis, Umberto. [CUB4_P:CUBAN4] Tommy, va nous trouver une petite bagnole haïtienne! [CUB4_Q:CUBAN4] Dès que tu l'as, reviens récupérer mon petit [CUB4_R:CUBAN4] Pepe et emmène-le chez les Haïtiens. [CUB4_S:CUBAN4] Ensuite, tu vas au centre de retraitement des Haïtiens et tu te sers de leurs solvants comme explosif. [CUB4_T:CUBAN4] Boom! Et bye bye! [CUB4_U:CUBAN4] Et toi, Umberto? [CUB4_V:CUBAN4] Oh, je reste en arrière, je surveille le café avec papa. [CUB4_W:CUBAN4] Il ne se sent pas très bien, tu sais. [CUB4_02:CUBAN4] ~g~Les bombes ont un retardateur de 45 secondes. [CUB4_04:CUBAN4] ~r~Tu as mis leur base en alerte! Plus question d'entrer, maintenant! [CUB4_07:CUBAN4] Les solvants sont à l'arrière, amigo. [CUB4_08:CUBAN4] Hola, amigos. [CUB4_09:CUBAN4] Bueno. Putains d'Haïtiens. Muerte. [CUB4_10:CUBAN4] Vamos. [CUB4_11:CUBAN4] Si, vamos. [CUB4_12:CUBAN4] Hé, on a besoin d'une bagnole haïtienne! [CUB4_13:CUBAN4] Bueno, allons trouver ces muchachos! [CUB4_14:CUBAN4] Suis mes amigos. [CUB4_15:CUBAN4] Ok, tu entres... [CUB4_16:CUBAN4] Je vais poser la bombe, couvrez-moi! [CUB4_17:CUBAN4] COURS! [CUB4_18:CUBAN4] Mec, c'est un quartier cool... [CUB4_19:CUBAN4] Cet endroit est une décharge, mec. [CUB4_20:CUBAN4] J'avais une belle copine... Elle vivait dans le coin. [CUB4_21:CUBAN4] Tu sais, ils font de super pizzas, ici! [CUB4_22:CUBAN4] Hé, mec, tu conduis comme une putain de gonzesse! [CUB4_23:CUBAN4] T'es perdu, mec? [CUB4_24:CUBAN4] T'as laissé Pepe derrière, va le récupérer! [CUB4_03:CUBAN4] ~g~Ne sors pas de la bagnole avant qu'elle soit garée en sécurité dans l'enceinte. [CUB4_26:CUBAN4] ~g~Emmène Pepe, file au nord dans Little Haïti et fauche une bagnole Vaudou. [CUB4_27:CUBAN4] ~g~Va retrouver Rico et les autres Cubains. [CUB4_28:CUBAN4] ~g~Va rejoindre les Cubains dans l'usine de dope haïtienne. [CUB4_29:CUBAN4] ~g~Marche sur chacun des marqueurs pour y poser une bombe. [CUB4_30:CUBAN4] ~g~Une fois les trois bombes posées, sors de l'usine avant qu'elle n'explose. [CUB4_31:CUBAN4] ~g~Sors de l'usine! [CUB4_32:CUBAN4] ~g~Gare la bagnole à l'endroit du bip et descends. [CUB4_06:CUBAN4] ~r~Tu ne t'es pas assez éloigné de la base et on a dû annuler l'explosion! {=================================== MISSION TABLE FINALE ===================================} [FIN_1A:FINALE] Amène-toi, putain de salopard de traître! [FIN_1B:FINALE] Tu vas crever, Judas de mes deux! [FIN_1C:FINALE] C'est la dernière danse de Lance Vance! [FIN_2B:FINALE] Ah ouais, c'est ce que tu crois? [FIN_2C:FINALE] J'ai dit que je l'avais assez entendu celle-là à l'école! [FIN_3:FINALE] Plus personne pour couvrir ton cul, hein, Tommy? [FIN_4:FINALE] T'es plus que du passé, Tommy, du passé! [FIN_5:FINALE] T'as choisi le mauvais camp, Lance... [FIN_11A:FINALE] Tu m'as volé quinze ans de ma vie, Sonny... [FIN_11B:FINALE] Et je viens te présenter l'addition! [FIN_12A:FINALE] T'as encore rien compris! [FIN_12B:FINALE] Tu m'appartiens, Tommy... [FIN_12C:FINALE] C'est moi qui devait les faire ces quinze années! [FIN_13:FINALE] Attrapez-le, les mecs, il a jamais rien pigé. [FIN1_01:FINALE] Qu'est-ce qui se passe? [FIN1_02:FINALE] Tommy! Ah, bien, bien. Ecoute, écoute... Euh, écoute. [FIN1_03:FINALE] J'aime bien les poissons. Ouais, j'adore le poisson! [FIN1_04:FINALE] Je l'aime à toutes les sauces, dans le bocal ou dans l'assiette, [FIN1_05:FINALE] mais c'est pas parce que je l'aime que je veux dormir avec! [FIN1_06:FINALE] Et maintenant, il y a tes cousins ritals qui s'amènent pour me coller les pieds dans le béton, et je... [FIN1_07:FINALE] La ferme, Ken. Assis. [FIN1_08:FINALE] Lance, bordel, qu'est-ce qui se passe? [FIN1_09:FINALE] C'est tes amis du nord, Tommy... Ils ont pas aimé ce que t'as fait à leur mec. [FIN1_10:FINALE] Ils s'amènent aujourd'hui pour régler cette affaire. [FIN1_11:FINALE] Ils ont mis plus de temps que je pensais... [FIN1_12:FINALE] Les mecs, faut en finir et faire comprendre à tout le monde que désormais, c'est mon business! LE MIEN! [FIN1_13:FINALE] Ken, prend la première production de faux dollars et mets-en pour vingt millions dans des porte-documents. [FIN1_14:FINALE] Lance, tu rassembles tout le monde... [FIN2_01:FINALE] Tommy! [FIN2_02:FINALE] Quoi? T'embrasses pas ton vieux pote? [FIN2_03:FINALE] Ca fait quinze piges que je suis hors-circuit [FIN2_04:FINALE] et je suis un peu rouillé question étiquette. [FIN2_05:FINALE] Toujours la haine, hein, Tommy! [FIN2_06:FINALE] Je te l'avais pas dit que ton sale caractère te causerait des emmerdes? [FIN2_07:FINALE] Il y a vingt millions là-dedans... [FIN2_08:FINALE] Combien ils étaient déjà? Dix, non onze... [FIN2_09:FINALE] C'est pour ça qu'on t'a surnommé le boucher d'Hartwood! Hé hé hé! [FIN2_10:FINALE] Tu m'avais envoyé buter un mec, un seul mec. Mais ils savaient que je venais, Sonny... [FIN2_11:FINALE] Fais gaffe à ce que tu dis... [FIN2_12:FINALE] Quelqu'un pourrait penser que tu veux me coller sur le dos un malheureux concours de circonstances... [FIN2_13:FINALE] Prends simplement ce pognon... [FIN2_14:FINALE] Prendre le putain de fric? [FIN2_15:FINALE] Tu sais, Tommy, j'ai fait ce que je pouvais pour toi, j'ai tiré des ficelles et demandé des services. [FIN2_16:FINALE] J'étais ton pote, Tommy. J'espérais que tu entendrais raison et comprendrais ce qui est bon pour les affaires. [FIN2_17:FINALE] Je t'ai fait confiance, Tommy et je suis déçu... [FIN2_18:FINALE] Mais il y a au moins un mec dans ton organisation de merde qui s'y connaît en business, [FIN2_19:FINALE] Pas vrai, Lance? [FIN2_20:FINALE] Désolé, Tommy, On est à Vice City. C'est les affaires... [FIN2_21:FINALE] Tu nous as vendus... [FIN2_22:FINALE] Non, juste toi, Tommy, juste TOI. [FIN2_23:FINALE] Le vrai pognon est en haut dans le coffre! [FIN2_24:FINALE] Alors, Tommy, c'était quoi le grand plan? [FIN2_25:FINALE] Tu croyais que j'allais simplement prendre les faux biftons? [FIN2_26:FINALE] Sauver la face et m'enfuir la queue entre les jambes? [FIN2_27:FINALE] Non. [FIN2_28:FINALE] Je voulais juste te faire chier avant de te buter. [FIN3_01:FINALE] Tommy? [FIN3_02:FINALE] Oh, mon Dieu, Tommy! Qu'est-ce qui s'est passé? [FIN3_03:FINALE] A ton avis? [FIN3_04:FINALE] On dirait que t'as niqué ton costard! [FIN3_05:FINALE] Sans compter qu'il était chouette, Tommy! Mais qu'est-ce qui s'est passé? [FIN3_06:FINALE] J'ai eu un désaccord avec un partenaire commercial, tu sais comment c'est. [FIN3_07:FINALE] Tommy, moi, quand j'ai un désaccord avec un partenaire commercial, je lui envoie une lettre enflammée... [FIN3_08:FINALE] A la rigueur, je pisse dans sa boite aux lettres... Mais je commence pas la troisième guerre mondiale! [FIN3_09:FINALE] Tu sais, tu devrais peut-être voir mon psy... [FIN3_10:FINALE] Ce sale con de Lance... [FIN3_11:FINALE] Tommy. J'ai jamais pu blairer ce mec, tu sais? [FIN3_12:FINALE] C'est un névrosé dangereux et égocentrique. Un vrai salopard! [FIN3_13:FINALE] Je suis bien content que tu l'aies liquidé! [FIN3_14:FINALE] Je crois pas qu'on doive s'attendre à plus d'emmerdes en provenance du nord... [FIN3_15:FINALE] ... Parce qu'il y a plus de nord... [FIN3_16:FINALE] Maintenant, il n'y a plus que le sud... [FIN3_17:FINALE] Attends, est-ce que ça veut dire ce que je crois que ça veut dire, Tommy? [FIN3_18:FINALE] Qu'est-ce que tu crois que ça veut dire? [FIN3_19:FINALE] Qu'on est au commandement... Enfin, que tu es au commandement. Oh, Tommy... [FIN3_20:FINALE] Tu sais, Ken, c'est peut-être le début d'une grande amitié en affaires... [FIN3_21:FINALE] Après tout, t'es un voleur minable un peu magouilleur sur les bords... [FIN3_22:FINALE] Et moi je suis un tueur psychotique doublé d'un dealer... [FIN3_23:FINALE] Ouais. Si c'est pas grandiose! [FIN_B1:FINALE] ~g~Va tuer ~y~Vance~g~ le traître. [FIN_B2:FINALE] ~g~Trouve ~p~Sonni~g~ et règle-lui son compte pour de bon. [FIN_B3:FINALE] ~g~La mafia essaye de voler ton pognon. Défends le coffre. [FIN_B4:FINALE] ~g~Tu es presque mort. Va chercher ~w~de quoi te soigner~g~ en bas. [FIN_B5:FINALE] ~g~La mafia te vole ton pognon. Défends le ~c~coffre. [FIN_B7:FINALE] ~r~La mafia t'a volé tout ton pognon. [DEFSAFE:FINALE] ~g~Retourne au coffre et protège-le. {=================================== MISSION TABLE FIRETRK ===================================} [F_PASS1:FIRETRK] Feu éteint! [F_FAIL2:FIRETRK] ~r~T'arrives trop tard! [F_CANC:FIRETRK] ~r~Mission pompier annulée! [F_EXTIN:FIRETRK] INCENDIES : [F_START:FIRETRK] ~g~Véhicule en feu repéré dans le secteur ~a~. Va éteindre le feu. [SIREN_1:FIRETRK] Pour déclencher la sirène de ce véhicule, appuie brièvement sur la ~h~~k~~VEHICLE_HORN~~w~. [SIREN_2:FIRETRK] Pour déclencher la sirène de ce véhicule, appuie brièvement sur la ~h~~k~~VEHICLE_HORN~~w~. [FIREPRO:FIRETRK] Mission Camion de pompiers niveau 12 réussie. T'es complètement ignifugé maintenant! [F_FAIL1:FIRETRK] Mission Camion de pompiers achevée. [F_STAR1:FIRETRK] ~g~Véhicules en flammes dans la zone ~a~. Va éteindre l'incendie. [SPRAY_4:FIRETRK] { reVC update } Utilise la touche ~h~~k~~VEHICLE_FIREWEAPON~ ~w~pour tirer avec le canon à eau et le ~h~~k~~VEHICLE_TURRETLEFT~~w~ et ~h~~k~~VEHICLE_TURRETRIGHT~~w~ pour viser. [SPRAY_1:FIRETRK] { reVC update } Utilise la touche ~h~~k~~VEHICLE_FIREWEAPON~ ~w~pour tirer avec le canon à eau et le ~h~~k~~VEHICLE_TURRETLEFT~~w~ et ~h~~k~~VEHICLE_TURRETRIGHT~~w~ pour viser. {=================================== MISSION TABLE GENERA1 ===================================} [GEN1_A:GENERA1] Mr. Vercetti! [GEN1_B:GENERA1] Colonel. [GEN1_D:GENERA1] Non merci. [GEN1_E:GENERA1] Ca me gêne beaucoup d'admettre que la cause d'un de nos problèmes communs semble être un homme en qui j'avais confiance. [GEN1_F:GENERA1] J'ai supporté Gonzalez pendant des années, mais son incompétence a atteint de nouveaux sommets. [GEN1_G:GENERA1] Vous avez le droit de tuer Gonzalez... [GEN1_H:GENERA1] C'est lui qui m'a entubé? La seule chose qui m'intéresse, c'est le pognon! [GEN1_I:GENERA1] Je vous récompenserai pour ce service et nous chercherons ensuite votre argent ensemble. [GEN1_J:GENERA1] Il sera dans son appartement, probablement à moitié saoul. Servez-vous de ça... [GEN1_06:GENERA1] Hé! Il a une tronçonneuse! [GEN1_07:GENERA1] T'approche pas de moi, connard! [GEN1_08:GENERA1] Oh, mon Dieu, j'ai gâché ma vie et j'suis devenu un laidron! [GEN1_10:GENERA1] Je vais t'faire fermer ta grande gueule une bonne fois pour toutes. [GEN1_11:GENERA1] Essaye pas de t'enfuir, bâtard! [GEN1_12:GENERA1] Laisse-toi faire et ça sera rapide! [GEN1_13:GENERA1] Arrête de gueuler, ça intéresse personne! [GEN1_C:GENERA1] Merci d'être venu. Asseyez-vous. Du homard? [GEN1_05:GENERA1] ~g~Va tuer Gonzales! [GEN1_09:GENERA1] Je t'offre le double, Tommy, LE DOUBLE! [GEN1_18:GENERA1] ~r~Gonzalez a réussi à atteindre le poste de police vivant! [GEN1_19:GENERA1] ~g~Les flics de Vice City te cherchent! [GEN1_20:GENERA1] ~g~Monte dans une bagnole. [GEN1_21:GENERA1] ~g~Va au~h~ Pay 'N' Spray~g~ à~h~ Vice Point~g~. [GEN1_22:GENERA1] ~g~Conduis ton véhicule à l'intérieur de l'atelier de peinture pour perdre ton ~h~indice de recherche~g~, ~h~réparer ~g~et ~h~repeindre ~g~ton véhicule. Coût : ~h~$100~g~. Cette coup-ci, c'est gratuit. [GEN1_01:GENERA1] Quand tu cours, maintiens la~h~ ~k~~PED_FIREWEAPON~~w~ enfoncée pour préparer une attaque au corps à corps. [GEN1_02:GENERA1] Quand tu cours, maintiens la~h~ ~k~~PED_FIREWEAPON~~w~ enfoncée pour préparer une attaque au corps à corps. [GEN1_03:GENERA1] Quand tu cours, maintiens la~h~ ~k~~PED_FIREWEAPON~~w~ enfoncée pour préparer une attaque au corps à corps. [GEN1_14:GENERA1] Relâche la~h~ ~k~~PED_FIREWEAPON~~w~ pour attaquer. [GEN1_15:GENERA1] Relâche la~h~ ~k~~PED_FIREWEAPON~~w~ pour attaquer. [GEN1_16:GENERA1] Relâche la~h~ ~k~~PED_FIREWEAPON~~w~ pour attaquer. [GEN1_23:GENERA1] ~g~Franchis à nouveau les portes pour revenir au rez-de-chaussée. {=================================== MISSION TABLE GENERA2 ===================================} [COL2_A:GENERA2] Tommy! Venez donc vous joindre à moi! [COL2_B:GENERA2] Ca a l'air bon, hein! Un peu de museau de tapir? [COL2_C:GENERA2] Euh... Non. Non, merci. [COL2_D:GENERA2] Tommy, vous êtes comme le vent de la pampa qui m'a purifié de la puanteur de la corruption. [COL2_E:GENERA2] Je dois cependant montrer que je pleure sa mort et continuer les affaires comme d'habitude. [COL2_F:GENERA2] Ca me rapproche pas beaucoup de mon pognon... [COL2_G:GENERA2] Tommy, mon cher, vous n'êtes pas à Liberty. Ici, ça se passe autrement. [COL2_H:GENERA2] Je vais continuer mon enquête, mais en attendant, j'ai une bonne affaire à conclure. [COL2_I:GENERA2] Un service à rendre à un ami, Cortez. [COL2_J:GENERA2] Vous êtes un ami, Tommy. Je savais que vous ne me laisseriez pas tomber. [COL2_K:GENERA2] Il faut que vous rencontriez un coursier qui m'a obtenu une technologie intéressante... [COL2_1:GENERA2] Ah, la pluie est très dense en cette saison... [COL2_2:GENERA2] Quoi? [COL2_3:GENERA2] Ah, comment? [COL2_4:GENERA2] Ecoute, Cortez m'envoie. File-moi ces putain de processeurs. [COL2_5:GENERA2] Oh... D'accord. [COL2_B1:GENERA2] ~g~Retrouve le coursier au centre commercial. [COL2_B2:GENERA2] ~g~Le coursier se barre avec les processeurs de guidage. Ne le laisse pas filer! [COL2_B3:GENERA2] ~g~Ramène les processeurs de guidage au Colonel. [COL2_F1:GENERA2] ~r~T'as buté le contact! [COL2_F2:GENERA2] ~r~Le coursier est mort. Récupère les processeurs. [COL2_6A:GENERA2] Arrête, espèce de porc impérialiste d'américain! Ceci est la propriété du gouvernement français! Rendez-le! [BLIPHLP:GENERA2] Le bip sur le radar est un triangle pointant vers le haut, ce qui signifie que la cible est plus élevée que toi. [COL2_F3:GENERA2] ~r~Les processeurs de guidage sont au fond de la mer. [COL2_F4:GENERA2] ~r~Le coursier s'est fait la malle! Tu n'as pas récupéré les processeurs de guidage! {=================================== MISSION TABLE GENERA3 ===================================} [GEN3_49:GENERA3] Santé de Lance: [GEN3_A:GENERA3] Merci d'être venu, Thomas. [GEN3_B:GENERA3] Pardonnez-moi d'en venir directement au fait... [GEN3_C:GENERA3] Diaz m'a demandé de superviser une petite transaction... [GEN3_D:GENERA3] Espérons que ça se passera mieux que l'autre fois, n'est-ce pas? [GEN3_E:GENERA3] C'est pour ça que j'ai pensé à vous, amigo. [GEN3_F:GENERA3] J'ai déposé une arme dans le parking à niveaux. [GEN3_G:GENERA3] Récupèrez-le et allez surveiller les hommes de Diaz au rendez-vous. [GEN3_H:GENERA3] Gracias, amigo... [GEN3_1:GENERA3] On s'accapare toute l'action, à ce que je vois... [GEN3_2:GENERA3] Ecoute, tu veux pas faire autre chose que de me suivre partout comme mon ombre? Pourquoi tu ne viens pas pour me montrer à quoi t'es bon? [GEN3_3:GENERA3] Pourquoi pas... Au fait, moi c'est Lance. [GEN3_5:GENERA3] Tu dois être le nouveau porte-flingues de Cortez. [GEN3_6:GENERA3] Jusqu'à ce que de meilleures opportunités se présentent... [GEN3_7:GENERA3] Ils seront là d'une minute à l'autre. On ferait mieux de se trouver une bonne planque... [GEN3_8:GENERA3] OK! Je vais sur le balcon et toi tu t'mets sur le toit de l'autre côté de la cour. [GEN3_9:GENERA3] J'suis vivant! Têtes de noeuds! Et ça c'est grâce à toi! C'est quoi ton nom? [GEN3_10:GENERA3] Tommy. [GEN3_11:GENERA3] On se reverra bientôt, je pense! [GEN3_12:GENERA3] Bon, où est passé Lance? Merde... [GEN3_14:GENERA3] Tommy! J'ai besoin d'un coup de main! [GEN3_15:GENERA3] T'en fais pas, je te couvre! [GEN3_16:GENERA3] Les hommes de Diaz se font massacrer! [GEN3_19:GENERA3] ~g~Les haïtiens! Ils veulent foutre le deal en l'air! Protège Diaz! [GEN3_20:GENERA3] ~g~Va au parking à niveaux où tu peux récupérer le flingue que le Colonel a laissé pour toi. [GEN3_22:GENERA3] Santé de Diaz : [GEN3_23:GENERA3] ~g~T'as laissé Lance derrière! Va le chercher! [GEN3_25:GENERA3] ~r~Lance est mort! [GEN3_28:GENERA3] ~g~Ramène la serviette à Diaz. [GEN3_29:GENERA3] ~g~Récupère la serviette et ramène-la à Diaz. [GEN3_30:GENERA3] ~r~Il s'est barré avec le pognon! Diaz va te couper les couilles! [GEN3_33:GENERA3] ~r~Tu es supposé surveiller Diaz et ses hommes, pas les flinguer! [GEN3_34:GENERA3] ~r~Ils ne vont pas pouvoir faire affaire si tu descends les cubains! [GEN3_35:GENERA3] ~g~Il a volé le pognon de Diaz! [GEN3_36:GENERA3] ~g~Saute sur la moto, rattrape-le et récupère le pognon de Diaz! [GEN3_37:GENERA3] ~g~Voilà les Cubains. Surveille le deal en veillant à la sécurité de Diaz et de Lance. [GEN3_38:GENERA3] ~r~Diaz est mort! Tu devais le protéger! [GEN3_39:GENERA3] ~g~Prends position en haut des escaliers. [GEN3_44:GENERA3] ~g~Va avec Lance au rendez-vous et protège Diaz. [GEN3_40:GENERA3] { reVC update } Pour ~h~tirer droit devant ~w~lorsque tu es en ~h~moto~w~, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~. [GEN3_41:GENERA3] { reVC update } Pour ~h~tirer droit devant ~w~lorsque tu es en ~h~moto~w~, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~. [GEN3_46:GENERA3] Chiiiier! [GEN3_47:GENERA3] Tommy! [GEN3_48:GENERA3] Merde! [GEN3_50:GENERA3] ~r~Tu as paumé le fric de Diaz! La prochaine fois, essaie de ne pas cramer le blé! [GEN3_51:GENERA3] Encore des Haïtiens dans un camion merdique! [GEN3_54:GENERA3] s Restez pas plantés là, bande d'abrutis! Butez-moi ce Haïtien de merde! [GEN3_55:GENERA3] Tommy! Je reste là et je m'occupe de Diaz! [GEN3_18:GENERA3] ~g~Voilà les Cubains. Reste près de Diaz. Surveille les opérations tout en t'assurant que Diaz et Lance sont en sécurité. [GEN3_56:GENERA3] ~r~Diaz a été pris dans une embuscade et s'est fait tuer! La prochaine fois, ne le quitte pas des yeux! [GEN3_57:GENERA3] Le Kruger est un fusil d'assaut permettant de viser manuellement à la 1ere personne. [GEN3_58:GENERA3] Appuie sur la touche~h~ R1~w~ et maintiens-la enfoncée pour ~h~viser~w~ avec un fusil d'assaut. [GEN3_59:GENERA3] Appuie sur la touche~h~ L1~w~ et maintiens-la enfoncée pour ~h~viser~w~ avec un fusil d'assaut. [GEN3_60:GENERA3] Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ pour ~h~tirer~w~ avec un fusil d'assaut. [GEN3_61:GENERA3] Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ pour ~h~tirer~w~ avec un fusil d'assaut. [GEN3_62:GENERA3] Appuie sur la touche ~h~~k~~PED_FIREWEAPON~~w~ pour ~h~tirer~w~ avec un fusil d'assaut. [GEN3_63:GENERA3] Tu peux fusiller des types de côté sur une ~h~ moto ~w~, mais également ~h~tirer droit devant~w~. [GEN3_64:GENERA3] { reVC update } Appuie sur la touche ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer droit devant toi sur une moto. [GEN3_65:GENERA3] { reVC update } Appuie sur la touche ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer droit devant toi sur une moto. [GEN3_66:GENERA3] { reVC update } Appuie sur la touche ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour tirer droit devant toi sur une moto. [GEN3_67:GENERA3] Il te faut une mitrailleuse pour tirer droit devant toi sur une moto. [GEN3_53:GENERA3] Mon argent! [GEN3_52:GENERA3] Ces Haitiens pensent qu'ils peuvent se payer Ricardo Diaz! {=================================== MISSION TABLE GENERA4 ===================================} [DETON:GENERA4] DETONATION : [COL4_3:GENERA4] CONVOI, HALTE! [COL4_6:GENERA4] ON EST SOUS LE FEU ENNEMI! [COL4_7:GENERA4] Civil, éloigne-toi du char! [COL4_8:GENERA4] J'ai dit, dégage! IMMEDIATEMENT! [COL4_9:GENERA4] POSITIONS DEFENSIVES! [COL4_11:GENERA4] Dégagez ce civil de notre chemin, soldat! -Chef, oui, chef! [COL4_12:GENERA4] Un civil dans le char! ARRETEZ-LE! [COL4_13:GENERA4] Ceci est un convoi militaire, ne bloquez pas la route! [COL4_14:GENERA4] Descendez-le, soldat. [COL4_15:GENERA4] Dégagez ce véhicule civil de la route! -Chef, on déplace le véhicule, chef! [COL4_17:GENERA4] Ok, peloton, on dégage! [COL4_18:GENERA4] Y'a quelqu'un dans le char, chef! [COL4_19:GENERA4] Trouvez-moi quelques beignets, soldat! -Chef, oui, chef! [COL4_20:GENERA4] Cible verrouillée, chef! [COL4_21:GENERA4] TIREUR D'ELITE! [COL4_22:GENERA4] Je me tire d'ici. [COL4_23:GENERA4] Objectif atteint! Peloton, rompez! -Allons manger quelques beignets! [COL4_24:GENERA4] Protocole de sécurité Delta India Echo déclenché! Autodestruction du véhicule initialisée! [COL4_26:GENERA4] Prépare-toi à crever, ordure communiste! [COL4_B2:GENERA4] ~r~Le char est parvenu sans problème à destination! [COL4_B5:GENERA4] ~r~Le char est détruit! [COL4_01:GENERA4] Diaz était très content et il veut vous voir. [COL4_02:GENERA4] Et c'est une bonne chose? [COL4_03:GENERA4] Evidemment! Même si je commence à penser que Diaz est responsable de notre malheureuse perte... [COL4_04:GENERA4] Qu'est-ce qui vous fait dire ça? [COL4_05:GENERA4] On n'accuse pas comme ça un homme comme Diaz... Je ne faisais que penser à haute voix... [COL4_06:GENERA4] Quoi qu'il en soit, j'ai une proposition qui devrait vous intéresser... [COL4_07:GENERA4] Je n'ai plus le temps de faire des commissions, Cortez. [COL4_08:GENERA4] J'aurais pensé qu'un homme avec des dettes aussi délicates serait ouvert à toute opportunité. Mais écoutez quand même, Tommy. [COL4_09:GENERA4] Allez-y... [COL410:GENERA4] J'ai un acheteur pour une pièce d'artillerie qui est emmenée à travers la ville. Récupèrez-la pour moi. [COL411:GENERA4] Et une fois que vous l'aurez, je veux que vous m'appeliez de suite, et... [COL4_B4:GENERA4] ~g~Le char est verrouillé. Trouve le moyen d'en faire sortir les occupants. [COL4_1:GENERA4] Qu'est-ce qui se passe avec l'artilleur? - Aucune idée, chef! [COL4_4:GENERA4] Montez voir, soldat! - Chef, oui, chef! [COL4_B1:GENERA4] ~g~Va faucher la pièce d'artillerie escortée à travers la ville. [COL4_B3:GENERA4] ~g~Amène le char dans le garage du colonel avant qu'il ne s'autodétruise. [COL4_B6:GENERA4] ~g~Trouve un moyen de piquer le char! [COL4_B7:GENERA4] ~g~Conduis le char dans le garage. [COL4_B8:GENERA4] ~g~Sors du char et du garage. {=================================== MISSION TABLE GENERA5 ===================================} [COL5A_1:GENERA5] Les circonstances me forcent à un départ en hâte, amigo. [COL5A_2:GENERA5] Quel est le problème? [COL5A_3:GENERA5] Les français veulent récupérer leur technologie de missile et après le dernier incident, [COL5A_4:GENERA5] je crois que l'heure est venue de me mettre à l'abri. [COL5A_5:GENERA5] Ca serait pas plus sûr de partir en avion ? [COL5A_6:GENERA5] Je serais mort avant d'avoir atteint l'enregistrement. D'ailleurs, il faut que je fasse sortir la marchandise du pays. [COL5A_7:GENERA5] Besoin d'un autre flingue? [COL5A_8:GENERA5] Vous, amigo, vous en valez dix... hahahaha. [COL5B_1:GENERA5] Tommy, vous m'avez bien protégé et servi. [COL5B_2:GENERA5] Mais maintenant, vous devez nous laisser avant qu'on arrive en pleine mer. [COL5B_4:GENERA5] Merci, Colonel. [COL5B_5:GENERA5] Une dernière chose, pendant que je suis parti, pouvez-vous surveiller Mercedes pour moi? [COL5B_6:GENERA5] Je crois qu'elle peut se surveiller tout seule, mais soyez tranquille, je garderai l'oeil ouvert. [COL5B_7:GENERA5] Merci, mon ami. Jusqu'à mon retour. [COL5B_8:GENERA5] Adios, amigo. [COL5_7:GENERA5] Arrêtez de me tirer dessus! [COL5_9:GENERA5] Tommy, empêchez-les de me tirer dessus! [COL5_10:GENERA5] J'ai l'immunité diplomatique! [COL5_11:GENERA5] Ne tirez pas, je suis un Colonel! [COL5_12:GENERA5] Tommy, descendez-les, mon pays vous en sera reconnaissant. [COL5_13:GENERA5] Tommy, on est submergés par les Français! [COL5_14:GENERA5] Tommy, il y a des Français partout! Je déteste ça! [COL5_15:GENERA5] Tommy, comment ça va? [COL5_16:GENERA5] Ca, c'est pour Piaf et Gainsbourg et votre saloperie de pain français! [COL5_1:GENERA5] A bâbord! A bâbord! [COL5_2:GENERA5] Ils attaquent à tribord! [COL5_3:GENERA5] Le pont devant! [COL5_4:GENERA5] Ils ont un hélico! [COL5_B1:GENERA5] ~g~Protège le colonel et son yacht à tout prix. [COL5_B2:GENERA5] ~g~Passe devant et dégage la route pour le yacht du colonel. [COL5_B3:GENERA5] ~r~Le colonel est mort! [COL5_B4:GENERA5] ~g~Abats l'hélicoptère vous tire dessus. [COL5B_3:GENERA5] Je vous laisse ma vedette personnelle. Gardez-la en témoignage de ma gratitude! [COL5_B5:GENERA5] ~g~Descends les hélicos et protège bien le yacht. [COL5_B6:GENERA5] ~g~Tu n'as plus de munitions, va en chercher d'autres dans les escaliers du pont supérieur. [COL5_B7:GENERA5] ~g~Ta santé diminue. Régénère-la dans les escaliers du pont supérieur. {=================================== MISSION TABLE HAIT1 ===================================} [HAM1_A:HAIT1] Bonjour? Y'a quelqu'un? [HAM1_B:HAIT1] Entre, mon petit, et repose ton esprit. [HAM1_C:HAIT1] Tu dois être le grand méchant homme dont m'a parlé mon grand-père. [HAM1_D:HAIT1] Il me dit des choses sur toi, tu sais, quand il me rend visite [HAM1_E:HAIT1] et aussi des choses sur les autres qui t'attendent. [HAM1_F:HAIT1] Bon, nous sommes tous morts depuis longtemps, mais toi, [HAM1_G:HAIT1] je ne voudrais pas être à ta place, hé hé hé! [HAM1_H:HAIT1] J'ai reçu un message. De venir ici. [HAM1_I:HAIT1] Tu les entends? [HAM1_J:HAIT1] Ils crient ton nom, mon garçon, ils doivent vraiment te vouloir, tu ne crois pas? [HAM1_K:HAIT1] Maintenant, si tu rends service à tata Poulet, peut-être qu'elle peut t'aider. [HAM1_L:HAIT1] Peut-être qu'elle peut te donner un petit grigri après tout ça. [HAM1_M:HAIT1] Te donner de la magie pour donner le mauvais oeil au policier, mmmm? [HAM1_N:HAIT1] Ecoute, tout ça est très, hum... me donner quoi? [HAM1_O:HAIT1] Je... Je crois que je me suis planté d'adresse... [HAM1_P:HAIT1] Fais-ça pour moi, Tommy... [HAM1_Q:HAIT1] Les Cubains, ces vilains coqs arrogants, hum, [HAM1_R:HAIT1] ont fait des ennuis à mes adorables garçons haïtiens. [HAM1_S:HAIT1] Et maintenant, ils ont dit au policier où je cachais mes poudres! [HAM1_T:HAIT1] Ils pensent que c'est des drogues, les idiots! [HAM1_U:HAIT1] Alors, sois un gentil garçon, Tommy, et va chercher les poudres de tata Poulet. [HAM1_V:HAIT1] Oui, d'accord, d'accord. [HAM1_1:HAIT1] ~g~Les policiers se rapprochent des poudres! Va les récupérer avant eux! [HAM1_2:HAIT1] ~r~Les policiers ont trouvé la cachette les premiers! [HAM1_3:HAIT1] ~g~Ramène tout ça à la planque! [HAM1_4:HAIT1] ~g~Bien! Maintenant, la suivante! [HAM1_6:HAIT1] ~r~La cachette a été détruite, imbécile! [HAM1_7:HAIT1] ~g~Les policiers ont les poudres! Récupère-les avant qu'ils filent! [HAM1_8:HAIT1] ~g~Les flics sont en route pour récupérer les poudres, magne-toi! [HAT_1A:HAIT1] ~g~Bouge pas d'un poil, connard! {=================================== MISSION TABLE HAIT2 ===================================} [HAT2_B1:HAIT2] ~g~Va à la camionnette contenant les bombes volantes. [HAT2_B2:HAIT2] Tue les Cubains... [HAT2_B4:HAIT2] ... Et détruis leurs bateaux! [HAT2_B5:HAIT2] ~g~Les Cubains essayent de s'enfuir! Ne les laisse pas faire! [HAT2_B6:HAIT2] ~r~L'avion télécommandé est hors de portée! [HAT2_B7:HAIT2] ~g~Un des Cubains s'échappe en voiture. Ne laisse aucun témoin! [HAT2_B8:HAIT2] ~r~Tu n'as plus d'avions télécommandés! [HAT2_B9:HAIT2] Avions télécommandés : [HAT2_1:HAIT2] Oh, désolé, j'ai dû me tromper d'adresse... [HAT2_2:HAIT2] Eh bien, entre donc te détendre en buvant un peu de thé. [HAT2_3:HAIT2] Tu as quelque chose pour moi, Tommy? [HAT2_4:HAIT2] Ouais... [HAT2_5:HAIT2] Cet endroit me semble familier. Un vague souvenir d'enfance, un air de déjà vu... [HAT2_6:HAIT2] Tommy, je voudrais te demander de faire une petite commission pour moi... [HAT2_7:HAIT2] Tu me rappelles quelqu'un que... [HAT2_8:HAIT2] Les Cubains ont des bateaux très rapides qui leur servent à traverser les mers avec de la drogue. [HAT2_9:HAIT2] C'est leur gagne-pain. [HAT2_10:HAIT2] Mon neveu a fabriqué quelques bombes volantes pour nous en débarrasser. [HAT2_11:HAIT2] Transforme leurs bateaux en allumettes! [HAT2_12:HAIT2] Bon, merci pour le thé. [HAT2_B3:HAIT2] { reVC update } Appuie sur la touche ~h~~k~~VEHICLE_FIREWEAPON~~w~ pour larguer une bombe ou sur ~h~~k~~VEHICLE_ENTER_EXIT~~w~ pour annuler. {=================================== MISSION TABLE HAIT3 ===================================} [HAM3_A:HAIT3] Salut, euh, je... Je cherche quelqu'un dans le coin... [HAM3_B:HAIT3] Tu as l'air d'avoir faim, Tommy. [HAM3_C:HAIT3] On se connait? [HAM3_D:HAIT3] Chut! [HAM3_E:HAIT3] Encore une chose et je te laisse partir, Tommy. [HAM3_F:HAIT3] Mes garçons sont en guerre contre les Cubains. [HAM3_G:HAIT3] Mais pas de revolvers. [HAM3_H:HAIT3] Hum, mais les Cubains ont une suprise qui les attend! [HAM3_I:HAIT3] Pendant qu'ils se battent dans les rues, prends ce fusil et tue-les dans la confusion! [HAM3_J:HAIT3] Personne ne te verra, personne ne t'entendra! [HAM3_K:HAIT3] Si tu fais ça pour tata Poulet, Tommy, tu ne seras plus lié aux cordons de mon tablier! [HAM3_1:HAIT3] ~g~On doit gagner cette bataille! Si tous les Haïtiens meurent, c'est foutu! [HAM3_3:HAIT3] ~g~Les Cubains risquent de tricher, alors fais attention. [HAM3_4:HAIT3] ~r~Tu as été repéré! C'est foutu! [HAM3_5:HAIT3] ~g~Tu dois tuer les cubains de loin. Ne te fais pas voir. [HAM3_8:HAIT3] ~g~Les Haïtiens se font descendre! Vise mieux! [HAM3_7:HAIT3] ~g~Fais gaffe! Les Cubains ont amené des renforts. Liquide-les tous! [HAM3_2:HAIT3] ~r~Les Haïtiens sont morts! [HAM3_L:HAIT3] Tata... {=================================== MISSION TABLE HOTEL ===================================} [INTB_A:HOTEL] Tommy! Ca fait une paye! [INTB_B:HOTEL] Salut Sonny... [INTB_C:HOTEL] Je sais, je sais, t'es submergé par l'émotion, hein? [INTB_D:HOTEL] Quinze piges et c'est comme si c'était hier! [INTB_E:HOTEL] C'est une façon de voir les choses... [INTB_F:HOTEL] Hé, bosser pour la famille, tu sais, c'est pas de la tarte, [INTB_G:HOTEL] mais la famille veille toujours sur les siens, tu comprends? [INTB_H:HOTEL] Bon, raconte un peu comment l'affaire s'est passée? Ca y est, t'es blindé? [INTB_I:HOTEL] Ecoute, Sonny, on s'est fait piéger. C'était une embuscade. Et ils ont buté Harry et Lee. [INTB_J:HOTEL] J'espère pour toi que tu rigoles, Tommy... Et t'as plutôt intérêt à toujours avoir le fric... [INTB_K:HOTEL] ... Non... Sonny... J'ai plus le fric... [INTB_L:HOTEL] C'était mon fric, Tommy! MON FRIC!!! [INTB_M:HOTEL] Vaudrait mieux pas que t'essayes de me baiser Tommy, parce que je suis pas le genre à aimer ça... [INTB_N:HOTEL] Attend Sonny... [INTB_O:HOTEL] Je te donne ma parole d'honneur que je vais récupérer ton fric et la dope. [INTB_P:HOTEL] Et en prime, je t'enverrai les couilles de ceux qui ont fait ça! [INTB_Q:HOTEL] Ca, je le sais déjà... Tommy, t'es pas con, mais je te préviens, moi non plus... [INTB_R:HOTEL] Et si c'était pas toi, tu serais déjà mort... [INTB_S:HOTEL] Mais j'ai un faible pour toi et en souvenir du passé, je vais te laisser t'en charger. [INTB_T:HOTEL] T'en fais pas, Sonny, t'as ma parole! [INTB_U:HOTEL] Je te tiens au courant, ciao. {=================================== MISSION TABLE ICECRE1 ===================================} [ICC1_4:ICECRE1] ~g~Il n'y a aucun client dans ce secteur. Essaye ailleurs. [ICC1_5:ICECRE1] Deals effectués : [ICC1_7:ICECRE1] ~g~Tu reçois de l'argent pour chaque transaction faite, mais plus tu fais d'affaires, plus tu attires l'attention de la police. [ICC1_8:ICECRE1] ~g~Pour effectuer une transaction, ~h~gare ta camionnette ~g~et appuie sur la ~h~~k~~VEHICLE_HORN~ ~g~pour jouer le jingle et attirer les clients. [ICC1_9:ICECRE1] ~g~Les gangs locaux n'apprécient pas qu'on fasse des affaires sur leur territoire. Attends-toi à des représailles. [ICC1_10:ICECRE1] ~g~Tu as fait ~1~ deals! [ICC1_11:ICECRE1] ~g~Tu as fait ~1~ deals! [ICC1_13:ICECRE1] ~r~T'as fait aucun deal! [ICC1_14:ICECRE1] USINE DE CREME GLACEE OK [ICC1_15:ICECRE1] ~g~L'usine de crème glacée génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. [ICC1_16:ICECRE1] ~g~Utilise la camionnette de Mr Whoopee pour distribuer les produits Cherry Poppers dans Vice City. [ICE_AT1:ICECRE1] USINE DE CREME GLACEE OK [ICE_AT2:ICECRE1] ~g~L'usine Cherry Popper génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. [ICC1_17:ICECRE1] Mission Distribution terminée [ICC1_18:ICECRE1] Total des ventes de glaces : $~1~ [ICC1_19:ICECRE1] Total de deals réalisés : ~1~ {=================================== MISSION TABLE ICECUT ===================================} [ICC1_A:ICECUT] T'es qui toi? [ICC1_B:ICECUT] Le nouveau proprio. [ICC1_C:ICECUT] Est-ce que t'as déjà été enfant? [ICC1_D:ICECUT] Mais de quoi tu parles? [ICC1_E:ICECUT] As-tu été un gosse? [ICC1_F:ICECUT] Ouais! Vas-y, c'est quoi ton problème? [ICC1_G:ICECUT] Je le savais. Un chiard. [ICC1_H:ICECUT] Un putain de gosse pourri gâté, un chialeur, un emmerdeur, un bébé à sa maman! [ICC1_I:ICECUT] Un bébé... Un horrible et dégoûtant petit morveux. Ouin ouin. [ICC1_J:ICECUT] Ta maman t'aime pas. Petite merde! [ICC1_K:ICECUT] Hé! Calme-toi. [ICC1_L:ICECUT] Je HAIS les mouflets et les mômes. [ICC1_M:ICECUT] Ce sont des putains de pourris gâtés, de chialeurs, d'emmerdeurs... [ICC1_N:ICECUT] Ca suffit maintenant! [ICC1_O:ICECUT] C'est quoi ton problème? [ICC1_P:ICECUT] Tu confectionnes des glaces, non? C'est un truc pour les gosses. [ICC1_Q:ICECUT] T'es quoi comme genre de cinglé, toi? [ICC1_R:ICECUT] Que je comprenne... A quoi ça sert de rendre les gosses heureux si tu les déteste? [ICC1_S:ICECUT] Espèce d'abruti, petit curieux, fouinard- [ICC1_T:ICECUT] La ferme! [ICC1_U:ICECUT] -de merde! [ICC1_V:ICECUT] Les glaces c'est une couverture. [ICC1_W:ICECUT] On fait aussi dans autre chose que les produits laitiers. [ICC1_X:ICECUT] Et si je vois un gosse, j'en fait mon affaire. [ICC1_Y:ICECUT] N'est-ce pas, gamin? Ouais, c'est bien ça. Ta maman t'aime pas. [ICC1_Z:ICECUT] Elle te DETESTE! [ICC1_ZA:ICECUT] PROPRIETE ACQUISE! {=================================== MISSION TABLE INTRO ===================================} [INT1_A:INTRO] Tommy Vercetti... Oh, merde! [INT1_B:INTRO] J'pensais pas qu'ils le laisseraient sortir un jour... [INT1_C:INTRO] Il a gardé un profil bas, pour se faire oublier... [INT1_D:INTRO] Mais les gens vont vite retrouver la mémoire [INT1_E:INTRO] quand ils vont le voir débouler dans les rues du voisinage. [INT1_F:INTRO] Et ça va pas être bon pour les affaires... [INT1_G:INTRO] Et alors, qu'est-ce qu'on va faire, Sonny? [INT1_H:INTRO] On le traite comme un vieil ami et on lui trouve de quoi s'occuper en-dehors de la ville. OK? [INT1_I:INTRO] On parlait justement de s'étendre vers le sud... Pas vrai? [INT1_J:INTRO] Vice City, c'est du 24 carats, maintenant. [INT1_K:INTRO] Les colombiens, les mexicains... Putain, [INT1_L:INTRO] même les réfugiés cubains se taillent une belle part du gâteau! [INT1_M:INTRO] Mais ils font tous dans la dope, Sonny! [INT1_N:INTRO] Aucune famille ne touche à cette merde! [INT1_O:INTRO] Les temps changent... [INT1_P:INTRO] Les familles ne peuvent pas rester les bras croisés pendant que nos adversaires ramassent le jackpot. [INT1_Q:INTRO] Alors, on envoie un mec là-bas faire le sale boulot [INT1_R:INTRO] et pour nous en tailler une bonne part. OK? [INT1_S:INTRO] C'est qui, notre contact là-bas? [INT1_T:INTRO] Ken Rosenberg, le con d'avocat... [INT1_U:INTRO] Et comment il va faire pour tenir Vercetti en laisse? [INT1_V:INTRO] Il aura pas besoin de faire ça. [INT1_W:INTRO] On le lâche juste dans Vice City [INT1_X:INTRO] et on lui refile un peu de cash pour commencer. OK? [INT1_Y:INTRO] Donne-lui quelques mois... [INT1_Z:INTRO] Et puis, on descendra [INT1_A1:INTRO] lui rendre une petite visite, tu saisis? [INT1_A2:INTRO] Histoire de voir comment il se débrouille... [INT2_A:INTRO] Hey, salut les gars, c'est moi, Ken Rosenberg! Hé ! Super! [INT2_B:INTRO] Bon, euh, je vais vous conduire au rendez-vous, ok? [INT2_C:INTRO] J'ai parlé avec les fournisseurs, ils sont très, hé hé, [INT2_D:INTRO] très impatients de commencer cette affaire, et... euh, [INT2_E:INTRO] si tout se passe sans accroc, on devrait... euh, [INT2_F:INTRO] s'en mettre plein les poches, ce qui est vraiment, vous savez... [INT2_G:INTRO] une bonne chose... [INT2_H:INTRO] Ok. Bon, ils sont frères, ok. [INT2_I:INTRO] Il y en a un qui fait marcher... euh, l'affaire [INT2_J:INTRO] et l'autre qui s'occupe du transport en hélicoptère. [INT2_K:INTRO] Maintenant, ils opèrent du Mexique, [INT3_A:INTRO] Ok, c'est eux, dans l'hélico. [INT3_B:INTRO] Bon, voilà le deal. [INT3_C:INTRO] Ils veulent un échange en terrain découvert. [INT3_D:INTRO] OK? Bon, allons-y et restez groupés. [INT3_E:INTRO] Bon, personne s'énerve, maintenant. [INT3_F:INTRO] Je suis ici! Le moteur tourne! [INT3_G:INTRO] Tu l'as? [INT3_H:INTRO] De la colombienne 100% pure, la meilleure qualité, amigo. [INT3_I:INTRO] Les billets? [INT3_J:INTRO] Des 10 et des 20... usagés. [INT3_L:INTRO] Allez! Sors-nous de là! Roule! [INTRO1:INTRO] Je sors la tête d'un caniveau pour une seconde de défonce, et le destin me chie de la merde plein la gueule! [INTRO2:INTRO] Va roupiller. [INTRO3:INTRO] Qu'est-ce que tu vas faire? [INTRO4:INTRO] Je passerai à ton bureau demain et on essayera de trouver une solution à ce merdier. [INT3_K:INTRO] Je crois que nous allons faire affaire, amigo, hé hé hé! [INT3_M:INTRO] Laisse-moi voir. [INT2_L:INTRO] non, non, non, attends... {=================================== MISSION TABLE KENT1 ===================================} [KPM1_A:KENT1] D'accord, mon vieux, je vais sauver ton petit ami. [KPM1_B:KENT1] De quoi tu m'causes? [KPM1_C:KENT1] Tu connais ce branleur, Diaz, la grande gueule... [KPM1_D:KENT1] Il a ton pote, Lance. Il paraît que celui-ci a essayé de lui sauter sur le poil. [KPM1_E:KENT1] Mais il a pas sauté assez haut, si tu vois ce que je veux dire... [KPM1_F:KENT1] Où il l'a emmené? Et tourne pas autour du pot! [KPM1_G:KENT1] T'énerve pas! Ils l'ont emmené de l'autre côté de la ville, à la décharge. [KPM1_H:KENT1] Putain... Pauvre cinglé... [KPM1_2:KENT1] ~r~Tu étais supposé sortir Lance vivant de ce guêpier! [KPM1_3:KENT1] SANTE DE LANCE : [RESC_1:KENT1] Tu peux te servir d'un flingue? [RESC_2:KENT1] Ouais... je pense... Content de te voir moi aussi. [RESC_3:KENT1] Sortons de là! [RESC_4:KENT1] Ca fout tous mes plans en l'air, tes conneries. Cette fois-ci, t'as vraiment merdé pour de bon, Lance! [RESC_5:KENT1] Il a descendu mon frère! Tu croyais que j'allais faire quoi? Lui tondre sa pelouse? [RESC_6:KENT1] Va falloir qu'on liquide ce connard de Diaz avant qu'il en fasse autant avec nous. [RESC_7:KENT1] Va récupérer et retrouve-moi sur le pont de Star Island. Ok? [RESC_8:KENT1] OK. [KPM1_1:KENT1] ~g~Lance est retenu prisonnier dans la décharge. Va le sauver! [KPM1_4:KENT1] ~g~Amène Lance à l'hosto! [M_PASSN:KENT1] MISSION TERMINEE! [KPM1_5:KENT1] ~g~Les mecs de Diaz sont après toi, amène Lance à l'hosto. {=================================== MISSION TABLE KICKSTT ===================================} [KICK1_2:KICKSTT] ~r~Tu n'as pas récupéré la moto assez vite! [KICK1_7:KICKSTT] ~r~Tu as niqué la moto! [KICK1_8:KICKSTT] ~g~Monte sur la moto! [KICK1_T:KICKSTT] TEMPS PRIS : [KICKTM:KICKSTT] ~b~TEMPS DE L'EPREUVE : ~1~:~1~ [KICKTM2:KICKSTT] ~b~TEMPS DE L'EPREUVE : ~1~:0~1~ [GETBIKE:KICKSTT] ~g~Il te reste ~1~ secondes pour retourner à une moto avant la fin de la mission. [KICK1_1:KICKSTT] ~g~Termine le parcours aussi vite que possible. [KICK1_6:KICKSTT] ~g~Bien joué! [KICK_10:KICKSTT] ~g~Utilise la Sanchez pour terminer le parcours sans oublier aucun point de passage. [KICK_12:KICKSTT] ~r~Tu t'es dégonflé! [KICK_13:KICKSTT] ~r~T'as mis trop de temps! [KICK_11:KICKSTT] ~g~Pour quitter la mission, marche sur le ~q~marqueur rose~g~. {=================================== MISSION TABLE LAWYER1 ===================================} [LAW1_A:LAWYER1] Va roupiller, qu'il m'a dit - [LAW1_B:LAWYER1] - et je suis resté toute la nuit le cul vissé sur cette chaise dans le noir à boire du café! [LAW1_C:LAWYER1] C'est la merde. On est baisés jusqu'à l'os, mon pote! [LAW1_D:LAWYER1] Ecoute-moi, ces balaises, ils vont débarquer ici pour m'arracher la tête! C'est pas possible! [LAW1_E:LAWYER1] J'me suis pas tapé l'école de droit pour ça! Putain, qu'est-ce qu'on va faire maintenant? [LAW1_F:LAWYER1] Ferme-la, pose ton cul et détends-toi. Je vais te dire ce qu'on va faire. [LAW1_G:LAWYER1] Faut que tu trouves les types qui nous ont fauché la coke et après je les descends. [LAW1_H:LAWYER1] Bonne idée. Excellente idée. Laisse-moi réfléchir, laisse-moi réfléchir. [LAW1_I:LAWYER1] Je sais! Il y a ce Colonel à la retraite, le Colonel Juan Garcia Cortez. [LAW1_J:LAWYER1] C'est lui qui m'a aidé à organiser l'affaire [LAW1_K:LAWYER1] en-dehors des truands établis de Vice City. Ok? [LAW1_L:LAWYER1] Maintenant, écoute. Il organise une fiesta dans la baie, sur son yatch du luxe [LAW1_M:LAWYER1] et tous les gros pontes de Vice City seront là! [LAW1_N:LAWYER1] J'ai une invitation, bien sûr que j'ai une invitation, [LAW1_O:LAWYER1] mais il est pas question que je sorte d'ici. Non, pas question! [LAW1_P:LAWYER1] Ferme-la, je t'ai dit! Je vais y aller moi-même... [LAW1_Q:LAWYER1] Attends deux secondes! Tu sais, moi aussi, j'aime bien 1978, mais là, c'est pas une soirée bière et striptease... [LAW1_R:LAWYER1] Je veux dire, sans vouloir t'offenser, hein, tu risque de te faire refouler à l'entrée, quoi... [LAW1_S:LAWYER1] Y'a un problème avec mes fringues? [LAW1_T:LAWYER1] Bon, écoute. Fais un saut chez Rafael, dis-lui que tu viens de ma part et il te relookera. [LAW1_U:LAWYER1] Ok, allez, vas-y... [LAWP_1:LAWYER1] Buenas noches. [LAWP_2:LAWYER1] Si je comprends bien, c'est M. Rosenberg qui vous envoie. [LAWP_3:LAWYER1] J'espère que de récents problèmes n'ont pas affecté sa santé, ou ... hum... [LAWP_4:LAWYER1] son équilibre mental, M....? [LAWP_5:LAWYER1] Vercetti. Il fait simplement un peu... d'agoraphobie. [LAWP_6:LAWYER1] Excellent, excellent. Et vous? [LAWP_7:LAWYER1] Je veux juste ma marchandise. [LAWP_8:LAWYER1] Ah. C'est un malheureux concours de circonstances pour toutes les personnes impliquées... [LAWP_9:LAWYER1] Bien entendu, j'ai de mon côté ordonné une enquête... [LAWP_10:LAWYER1] Mais c'est un problème délicat qui nécessite du temps... [LAWP_11:LAWYER1] Peut-être en reparlerons-nous plus tard, voulez-vous? [LAWP_12:LAWYER1] En attendant, laissez-moi vous présenter ma fille... [LAWP_13:LAWYER1] Mercedes! [LAWP_14:LAWYER1] Caramia, pourrais-tu t'occuper de notre invité pendant que je m'occupe des autres convives? [LAWP_15:LAWYER1] D'accord, papa. [LAWP_16:LAWYER1] Veuillez m'excuser... [LAWP_17:LAWYER1] Mercedes? [LAWP_18:LAWYER1] Et il faut vivre avec ça... [LAWP_19:LAWYER1] Quoi qu'il en soit, laissez-moi vous montrer quelques uns de nos distingués invités... [LAWP_20:LAWYER1] Celui-ci est le membre du congrès Alex Shrub accompagné par la star montante siliconée Candy Suxxx... [LAWP_21:LAWYER1] Et connaissez-vous ma charmante femme Laura? Non? [LAWP_22:LAWYER1] Eh bien, malheureusement, elle est en Alabama, voici Candy. [LAWP_23:LAWYER1] Et là-bas, nous avons l'ailier vedette des Mambas de Vice City, BJ. [LAWP_24:LAWYER1] toujours aussi charmeur [LAWP_25:LAWYER1] Mais je l'ai plaqué et mis dans une chaise roulante! [LAWP_26:LAWYER1] Ha ha! Excellent! [LAWP_27:LAWYER1] En ce moment, je cherche une nouvelle propriété de luxe. [LAWP_28:LAWYER1] Et ce molusque c'est Jezz Torrent, [LAWP_29:LAWYER1] Le chanteur principal des Love Fist. [LAWP_30:LAWYER1] Savez-vous... comment ils jouent au ping-pong, en Thaïlande? [LAWP_31:LAWYER1] Je vais vous le dire... [LAWP_32:LAWYER1] Ils n'utilisent pas de raquettes, si vous voyez ce que je veux dire! [LAWP_33:LAWYER1] L'impotent. [LAWP_34:LAWYER1] Et le trio bavard. [LAWP_35:LAWYER1] Cette outre à sueur ronflante est le bras droit handicapé de papa, Gonzales. [LAWP_36:LAWYER1] Les deux autres sont le Pasteur Richards [LAWP_37:LAWYER1] et un réalisateur pseudo intellectuel nommé Steve Scott. [LAWP_38:LAWYER1] ...passion avec les envahisseuses nymphomanes, [LAWP_39:LAWYER1] Le requin géant s'amène et [LAWP_40:LAWYER1] il leur bouffe simplement les couilles! [LAWP_41:LAWYER1] Ah, c'est là. Vous n'avez jamais vu un truc pareil avant, hein? [LAWP_42:LAWYER1] Colonel! [LAWP_43:LAWYER1] Vos fêtes sont toujours aussi réussies, hahahahha! [LAWP_44:LAWYER1] Je ne puis que m'excuser de mon arrivée tardive. [LAWP_45:LAWYER1] Ah, de nada amigo. Comment allez-vous? [LAWP_46:LAWYER1] Nos affaires sont très difficile - Les barbares sont aux portes de la ville. [LAWP_47:LAWYER1] L'heure est venue de récompenser les amis et de liquider les ennemis, amigo. [LAWP_48:LAWYER1] C'est qui, la grande gueule? [LAWP_49:LAWYER1] Ricardo Diaz, c'est Mr.Coke. [LAWP_50:LAWYER1] Mercedes! [LAWP_51:LAWYER1] Oh, je raccompagnais justement mon ami en ville. [LAWP_52:LAWYER1] Une autre fois, Ricardo! [LAWP_53:LAWYER1] Partons d'ici! [LAWP_54:LAWYER1] En fait, emmenez-moi plutôt au Pole Position club. [LAW1_2:LAWYER1] ~g~Va au yatch du Colonel. [LAW1_4:LAWYER1] ~r~Tu as tué la fille du Colonel! [LAW1_5:LAWYER1] Vous allez travailler pour mon père? [LAW1_6:LAWYER1] Peut-être. [LAW1_7:LAWYER1] Ca vous ennuie si je pose ma main sur votre genou? [LAW1_8:LAWYER1] Peut-être... [LAW1_9:LAWYER1] C'est trop dur d'avoir un père riche et puissant! Vamos! [LAW1_10:LAWYER1] A plus tard, mon mignon! [LAW1_11:LAWYER1] Sans aucun doute. [LAW1_12:LAWYER1] Mmmm... Chouette bécane. [LAW1_13:LAWYER1] Non! Ma moto! [LAW1_3:LAWYER1] ~g~Emmène la fille du Colonel au Pole Position Club. [HELP20:LAWYER1] Suis le ~h~point T-shirt~w~ sur le radar pour trouver Rafael. [LAW1_14:LAWYER1] Mmmm, j'aime beaucoup ta grosse moto. [LAW1_15:LAWYER1] Ouais, bébé, je viens de piquer ça à ce con {=================================== MISSION TABLE LAWYER2 ===================================} [LAW2_A:LAWYER2] Ah! Eh bien, j'espère que tu t'es bien amusé! Parce que moi, je suis mort d'inquiétude! Qu'est-ce que t'as trouvé? [LAW2_B:LAWYER2] Qu'y'a plus de criminels dans cette ville qu'en taule. Faut qu'on trouve une piste dans la rue. [LAW2_C:LAWYER2] Attends, laisse-moi réfléchir, voyons... [LAW2_D:LAWYER2] Ah! J'ai trouvé! [LAW2_E:LAWYER2] Bon, il y a bien cet anglais, une espèce de déchet du biz de la musique, [LAW2_F:LAWYER2] il est connu sous le nom de Kent Paul. [LAW2_G:LAWYER2] Quoi qu'il en soit, il a le nez plongé dans la merde de Vice City depuis un moment [LAW2_H:LAWYER2] et ça, si quelqu'un sait où sont les 20 kilos de coke, [LAW2_I:LAWYER2] c'est bien lui, ok? Il est toujours fourré au Malibu... [LAW2_J:LAWYER2] Je vais aller lui rendre une petite visite... [LAW2B_A:LAWYER2] D'où tu sors, toi? [LAW2B_B:LAWYER2] Je cherche un oiseau dans ton genre depuis une éternité... [LAW2B_C:LAWYER2] Kent Paul, mon pote. Ouais, dans le coin, c'est moi le patron. [LAW2B_D:LAWYER2] Je cherche un Anglais... [LAW2B_E:LAWYER2] Je résouds les problèmes, si tu vois ce que je veux dire? [LAW2B_F:LAWYER2] Je vais m'occuper de toi. Quoi que tu demandes, tu l'auras... [LAW2B_G:LAWYER2] Ne t'inquiéte absolument de rien. [LAW2B_H:LAWYER2] Dégage, chérie! [LAW2B_I:LAWYER2] Hé hé hé hé hé! [LAW2B_J:LAWYER2] C'est toi, Kent Paul? Je suis un pote de Rosenberg... [LAW2B_K:LAWYER2] Rosenberg...Rosenberg... Ah, ce cinglé qui poursuit les ambulances! [LAW2B_L:LAWYER2] Ce mec peut défendre un innocent jusqu'au couloir de la mort! [LAW2B_M:LAWYER2] Donne-nous un autre verre! [LAW2B_N:LAWYER2] On est tous des comédiens. [LAW2B_O:LAWYER2] Ecoute-moi, il me manque vingt kilos et un gros tas de pognon... [LAW2B_P:LAWYER2] Une affaire de drogue? C'est toujours des pièges à con... [LAW2B_Q:LAWYER2] Qu'est-ce que tu sais à ce propos? [LAW2B_R:LAWYER2] Hé! Hé! Là où je voulais en venir, [LAW2B_S:LAWYER2] c'est à un cuisinier-trompettiste qui deale à l'extérieur de la cuisine d'un hôtel sur Ocean Drive. [LAW2B_T:LAWYER2] Il avait l'air très content de lui, ces derniers temps. Tu pourrais peut-être aller voir ça de plus près... [LAW2B_U:LAWYER2] Bon, je vais y aller. A plus tard. [LAW2B_V:LAWYER2] Ouais, c'est ça. Allez, dégage, imbécile ou je te botte le cul! [LAW2B_W:LAWYER2] Donne-moi à boire et putain, où est cette salope? [LAW2C_A:LAWYER2] Ouais, c'est bien, massacre-le, ça devrait le rendre plus bavard. [LAW2C_B:LAWYER2] T'en veux aussi? [LAW2C_C:LAWYER2] Hé, relax, je veux la même chose que toi, mon frère. [LAW2C_D:LAWYER2] Ah ouais? Et c'est quoi? [LAW2C_E:LAWYER2] Ton fric et la blanche de mon regretté frère. Malheureusement, tu viens de refroidir notre piste. [LAW2C_F:LAWYER2] C'était un accident. Dégage. [LAW2C_G:LAWYER2] Hé, hé! Pas la peine de jouer les justiciers solitaires avec moi! [LAW2C_H:LAWYER2] Voilà comment je vois les choses... On est deux mecs dans une ville étrange. Et on a besoin de veiller l'un sur l'autre. [LAW2C_I:LAWYER2] J'ai pas besoin de toi, mon frère... [LAW2C_J:LAWYER2] T'en es sûr? Tiens, attrape! [LAW2C_K:LAWYER2] Suis-moi! [LAW2_1:LAWYER2] Qu'est-ce que tu regardes? [LAW2_2:LAWYER2] Tu ferais mieux de commencer à causer... [LAW2_3:LAWYER2] Suce-moi, connard! [LAW2_4:LAWYER2] Par là! [LAW2_5:LAWYER2] Je vais voir ce que je peux trouver. Je garde un oeil sur toi, Tommy. [LAW2_6:LAWYER2] ~g~Va au Malibu Club et trouve Kent Paul. [LAW2_7:LAWYER2] ~g~Trouve le cuisinier sur Ocean Drive. [LAW2_10:LAWYER2] ~g~Retourne à l'hôtel. [LAW2_11:LAWYER2] ~g~Ramasse son portable! [LAW2_12:LAWYER2] Portable acquis! Tu peux maintenant recevoir des coups de fil. [LAW2_13:LAWYER2] ~g~Tu as laissé Lance derrière! Va le chercher! [LAW2_14:LAWYER2] On doit foutre le camp d'ici! [GUN_2A:LAWYER2] Maintiens la ~h~~k~~PED_LOCK_TARGET~ ~w~enfoncée pour ~h~viser automatiquement~w~. Appuie sur la~h~ ~k~~PED_FIREWEAPON~~w~ pour ~h~tirer! [GUN_2C:LAWYER2] Maintiens la ~h~~k~~PED_LOCK_TARGET~ ~w~enfoncée pour ~h~viser automatiquement~w~. Appuie sur la~h~ ~k~~PED_FIREWEAPON~~w~ pour ~h~tirer! [GUN_2D:LAWYER2] Maintiens la ~h~~k~~PED_LOCK_TARGET~ ~w~enfoncée pour ~h~viser automatiquement~w~. Appuie sur la~h~ ~k~~PED_FIREWEAPON~~w~ pour ~h~tirer! [HELP17:LAWYER2] Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour attaquer le chef. [HELP18:LAWYER2] Appuie sur la~h~ ~k~~PED_FIREWEAPON~~w~ pour attaquer le chef. [LAW3_11:LAWYER2] Place-toi sur le ~q~marqueur rose~w~ pour consulter les armes en vente. [LAW3_12:LAWYER2] Tu peux sélectionner des armes en appuyant sur ~h~gauche~w~ ou ~h~droite~w~ de la ~h~touche directionnelle. [LAW3_13:LAWYER2] Si tu as assez de blé, tu peux acheter des armes en appuyant sur la ~h~~k~~PED_SPRINT~. [LAW3_14:LAWYER2] Tu peux sortir du magasin en appuyant sur la ~h~~k~~VEHICLE_ENTER_EXIT~. [LAW3_15:LAWYER2] Suis le ~h~point pistolet~w~ sur le radar pour trouver ~h~Ammu-Nation [LAW2_15:LAWYER2] ~g~Va chez Ammu-Nation. [LAW2_K:LAWYER2] calme-toi, maintenant. [LAW2_16:LAWYER2] Il faut que tu comprennes un truc sur cette ville. Tu dois toujours avoir un flingue sur toi. [LAW2_17:LAWYER2] Allez, l'armurerie du coin n'est qu'à quelques pas d'ici. [LAW2_18:LAWYER2] Tommy, tout le monde a besoin de se la couler douce de temps en temps. [LAW2_19:LAWYER2] Voici le Pole Position Strip Club. Tu pourrais y passer de temps en temps. {=================================== MISSION TABLE LAWYER3 ===================================} [LAW3_A:LAWYER3] Aaaaaah! Oh, nom de Dieu, c'est toi! Merde, je vais avoir besoin d'un nouveau pantalon! [LAW3_B:LAWYER3] Putain, ces psychopathes du nord ont téléphoné pour dire qu'ils rappliquaient bientôt... [LAW3_C:LAWYER3] Alors, où est ce sacré pognon? [LAW3_D:LAWYER3] Relax, relax, on n'en est pas encore là. [LAW3_E:LAWYER3] Je croyais vraiment que tu t'en occupais... [LAW3_F:LAWYER3] Et maintenant, ces mecs disent qu'il faut qu'on leur rende un service... [LAW3_G:LAWYER3] Tu veux dire que JE dois leur rendre un service... [LAW3_H:LAWYER3] Ouais, évidemment! Est-ce que j'ai l'air de pouvoir impressionner un jury? [LAW3_I:LAWYER3] Je ne pourrais même pas faire peur à un gosse et crois-moi, j'ai essayé! [LAW3_J:LAWYER3] Maintenant, écoute, c'est ça ou le cousin de Forelli, Georgio, se prend cinq ans pour escroquerie. [LAW3_K:LAWYER3] Faut que tu t'occupes de ces mecs! [LAW3_L:LAWYER3] Je comprends. Demander au jury de changer d'opinion. T'en fais pas. [LAW3_M:LAWYER3] Non non non non! Ca, j'ai déjà essayé! Et l'affaire ne s'est pas arrangée! [LAW3_N:LAWYER3] Tu dois les OBLIGER à changer d'opinion! [LAW3_1:LAWYER3] Avec les compliments de Georgio... [LAW3_2:LAWYER3] N'oublie pas que le mot 'coupable' est proscrit! [LAW3_3:LAWYER3] Il est innocent, jusqu'à ce que je dise le contraire. [LAW3_4:LAWYER3] Tu sais qu'il n'est pas coupable... [LAW3_5:LAWYER3] Tu te souviens de Georgio? Et donc qu'il est innocent? [LAW3_6:LAWYER3] Non coupable. C'est compris... bien. [LAW3_8:LAWYER3] ~r~Tu as tué un juré! [LAW3_9:LAWYER3] ~g~Bousille la voiture du juré pour le faire sortir! [HELP40:LAWYER3] Tu peux bousiller les voitures avec le marteau ou un outil similaire. [LAW3_10:LAWYER3] ~g~Tu peux aller à la ~h~quincaillerie~g~ pour acheter une arme de corps à corps. [LAW3_20:LAWYER3] ~g~Bousille la voiture du juré! [LAW3_21:LAWYER3] Je n'arrive pas à le croire! [LAW3_22:LAWYER3] Incroyable! [LAW3_23:LAWYER3] Ok! C'est bon, j'ai compris! [LAW3_24:LAWYER3] ~g~Ce marteau pourrait être utile. [LAW3_7:LAWYER3] ~g~Va intimider les deux jurés, mais NE LES TUE PAS! [HELP23:LAWYER3] Tu peux suivre le ~h~marteau~w~ sur le radar si tu veux acheter des armes de corps à corps dans la quincaillerie. [LAW3_16:LAWYER3] de Pete de Floride. [LAW3_17:LAWYER3] Casse-toi de là! {=================================== MISSION TABLE LAWYER4 ===================================} [LAW4_A:LAWYER4] Avery, cela va sans dire... Ah, Tommy! Des nouvelles? Non, non, tu m'en parleras plus tard. Plus tard. [LAW4_B:LAWYER4] Tommy, voici Avery Carrington. Je crois que vous vous êtes rencontrés à la fête? [LAW4_C:LAWYER4] Pas directement. [LAW4_D:LAWYER4] Salut. [LAW4_E:LAWYER4] Avery a une proposition à nous faire. [LAW4_F:LAWYER4] On a pas déjà du pain sur la planche? [LAW4_G:LAWYER4] J'essaye de maintenir les loups au loin, alors lâche-moi un peu la grappe! [LAW4_H:LAWYER4] Je suis tendu comme un cable et même si je dois crever avant la fin de la semaine, j'ai pas envie de mourir pauvre! [LAW4_I:LAWYER4] Bon, vous énervez pas, tous les deux. [LAW4_J:LAWYER4] Ecoute, petit, tu me files un coup de main et le moindre latino qui te fait chier, je m'occupe de lui. [LAW4_K:LAWYER4] Ok, qu'est-ce que je peux faire pour toi? [LAW4_L:LAWYER4] Cette société de livraison a son dépôt sur un terrain très intéressant. Et ils ne veulent pas vendre. [LAW4_M:LAWYER4] Ils s'accrochent à leur terrain comme des rats entêtés, alors va falloir exploser cette vermine. [LAW4_N:LAWYER4] Va là-bas et mets le feu aux poudres. [LAW4_O:LAWYER4] La sécurité sera débordée et tu pourras te glisser dedans et en finir avec eux... [LAW4_P:LAWYER4] Tu peux aussi passer chez Rafael pour changer de fringues. T'en as sans doute pour un moment, mais bon, fonce! [LAW4_Q:LAWYER4] Ca va être une vraie émeute. [LAW4_R:LAWYER4] Si tout se passe comme sur des roulettes, passe me voir au bureau... [LAW4_1:LAWYER4] Dispersez-vous! La direction n'écoutera vos revendications que dans les conditions appropriées! [LAW4_2:LAWYER4] Dispersez-vous! Retournez chez vous! [LAW4_3:LAWYER4] Dispersez-vous! C'est inapproprié! [LAW4_4:LAWYER4] Dispersez-vous ou vous finirez tous à la rue! [LAW4_5:LAWYER4] Sortez vos bâtons, les mecs, on va briser quelques crânes de cocos! [LAW4_13:LAWYER4] ~g~Commence à te battre avec au moins 4 ouvriers pour déclencher une émeute. [LAW4_14:LAWYER4] ~g~Détruis les camionnettes du complexe! [HELP38:LAWYER4] Si tu butes quelqu'un qui porte une arme, il la lâche. [HELP39:LAWYER4] Tu peux tirer sur les barils d'explosifs, mais garde tes distances. {=================================== MISSION TABLE MIAMI_1 ===================================} [T4X4_1A:MIAMI_1] ~g~Tu as ~1~ secondes pour récupérer ~y~24~g~ points de passage. ~g~Fais-le dans ~y~N'IMPORTE QUEL ORDRE. [T4X4_1B:MIAMI_1] ~y~Passe A TRAVERS~g~ le premier point de passage pour déclencher le ~r~CHRONOMETRE. [T4X4_1C:MIAMI_1] ~1~ sur 24! [GETBIK1:MIAMI_1] Tu as ~1~ secondes pour enfourcher une PCJ 600! [GETBIK3:MIAMI_1] ~r~Tu as besoin d'une PCJ 600 pour cette mission! {=================================== MISSION TABLE MM ===================================} [BLOD_04:MM] ETAT DE LA VOITURE : [BLOD_05:MM] ~g~TEMPS VISE : ~1~ minute [BLOD_06:MM] ~g~TEMPS VISE : ~1~ minutes [BLOD_07:MM] NV meilleur tps : ~1~ secondes [BLOD_08:MM] Voitures détruites : ~1~ [BLOD_09:MM] $~1~ [BLOD_10:MM] VAINQUEUR! [BLOD_01:MM] Franchis les points de passage pour augmenter ton temps général. [BLOD_02:MM] Tu as perdu si ton temps général est à zéro. [BLOD_03:MM] Il faut que ton temps général soit supérieur au temps visé pour gagner! {=================================== MISSION TABLE OVALRIG ===================================} [HOTR_01:OVALRIG] ~g~La course dure 12 tours. Seuls les trois premiers arrivés reçoivent une récompense. [HOTR_02:OVALRIG] ~g~Tu es disqualifié si ta bagnole est détruite. [HOTR_03:OVALRIG] ~g~Si ta bagnole est bousillée, tu peux la faire réparer aux stands. [HOTR_04:OVALRIG] ~g~C'est par là pour sortir du stade. [HOTR_05:OVALRIG] Etat de la voiture : [HOTR_06:OVALRIG] Tours : [HOTR_10:OVALRIG] Temps de course : [HOTR_09:OVALRIG] Position : [HOTR_12:OVALRIG] ~r~Ta caisse est bousillée! [HOTR_13:OVALRIG] ~r~Tu n'as pas gagné la course! [HOTR_14:OVALRIG] ~r~Tu as été disqualifié! [HOTR_15:OVALRIG] Temps : ~1~:~1~ [HOTR_16:OVALRIG] Temps : ~1~:0~1~ [HOTR_17:OVALRIG] Meilleur temps : ~1~:~1~ [HOTR_18:OVALRIG] Meilleur temps : ~1~:0~1~ [HOTR_19:OVALRIG] Meilleur temps : aucun [HOTR_20:OVALRIG] Nv meilleur tps : ~1~:~1~ [HOTR_21:OVALRIG] Nv meilleur tps : ~1~:0~1~ [HOTR_22:OVALRIG] Meilleur résultat : aucun [HOTR_23:OVALRIG] Meilleur résultat : 1er [HOTR_24:OVALRIG] Meilleur résultat : 2e [HOTR_25:OVALRIG] Meilleur résultat : 3e [HOTR_26:OVALRIG] Meilleur résultat : ~1~e [HOTR_27:OVALRIG] Meilleur tps au tr : ~1~.~1~ secondes [HOTR_28:OVALRIG] Meilleur tps au tr : ~1~.0~1~ secondes [HOTR_29:OVALRIG] $~1~ [HOTR_30:OVALRIG] 1ERE PLACE [HOTR_31:OVALRIG] 2E PLACE [HOTR_32:OVALRIG] 3E PLACE [HOTR_33:OVALRIG] Meilleur tps au tr : aucun [HOTR_11:OVALRIG] Nv meilleur tps au tr : ~1~.~1~ secondes [HOTR_34:OVALRIG] Nv meilleur tps au tr : ~1~.0~1~ secondes {=================================== MISSION TABLE PHIL1 ===================================} [PHI1_HP:PHIL1] Tu peux lancer une grenade à détonateur quelque part puis l'y faire exploser au moment souhaité. [PHIL1_A:PHIL1] Phil? [PHIL1_B:PHIL1] COURS! [PHIL1_C:PHIL1] Cours. [PHIL1_E:PHIL1] Merde, Phil, tu bois cette saloperie? [PHIL1_F:PHIL1] Putain, t'as pas besoin de la boire! [PHIL1_G:PHIL1] Tu la renifles juste un bon coup et t'exploses direct! [PHIL1_H:PHIL1] Ecoute, Phil, t'avais dit que tu pourrais t'occuper de ma puissance de feu... [PHIL1_I:PHIL1] Pas de problème. [PHIL1_J:PHIL1] Il y a ce mexicain qui trafique des armes, il a pas été réglo avec moi. [PHIL1_K:PHIL1] Il fait sa tournée hebdomadaire, maintenant. [PHIL1_L:PHIL1] Défonce l'arrière de son camion et sers-toi avant qu'il réagisse. [PHIL1_M:PHIL1] Et rends-moi un service au passage tant que tu y es. [PHIL1_N:PHIL1] Ensuite, règle-lui son compte... [PHI1_01:PHIL1] ~g~Va piquer les armes à l'arrière du camion du trafiquant. [PHI1_02:PHIL1] ~g~Le trafiquant a largué son chargement. Brise la caisse et ramasse l'artillerie. [PHI1_03:PHIL1] ~g~On dirait qu'ils ont demandé des renforts... [PHI1_04:PHIL1] ~g~Va maintenant finir les trafiquants d'armes survivants. [PHIL1_O:PHIL1] Yeeeeeeehhaaaaa! [PHIL1_D:PHIL1] N'approche jamais une allumette enflammée du boomshine de Phil Cassidy! {=================================== MISSION TABLE PHIL2 ===================================} [PHIL2_A:PHIL2] Salut, Phil, ça roule? [PHIL2_B:PHIL2] Saluut Tommyyyy! Comment ça va? Ca fait tellement longtemps... [PHIL2_C:PHIL2] Je te jure que tu devrais arrêter le boomshine... [PHIL2_D:PHIL2] Ca sent comme du décapant à peinture, j'ai les yeux qui brûlent... [PHIL2_E:PHIL2] Ferme-la, Tommy [PHIL2_F:PHIL2] et amène-toi par là. Y'a quelque chose que je veux te montrer... Quelque chose... [PHIL2_G:PHIL2] Waou! Putain! C'est normal que je sente l'odeur d'ici? Je me sens tout drôle... [PHIL2_H:PHIL2] T'inquiète pas de l'odeur, Tommy, regarde juste ça. [PHIL2_I:PHIL2] Putain de saloperies de piles bon marché! Il y en a d'autres sur le banc. [PHIL2_J:PHIL2] TA-DAAA! [PHIL2_K:PHIL2] Oh merde! [PHI2_01:PHIL2] ~g~Vite, emmène Phil à l'hosto! [PHI2_03:PHIL2] ~r~Phil Cassidy est mort! Maintenant, qui va nous filer des flingues à Liberty? [PHI2_05:PHIL2] Pas à l'hosto, mec, c'est plein de flics et de vietcongs! [PHI2_06:PHIL2] Il y a un ancien chirurgien de l'armée qui me doit quelques services et une tondeuse... [PHI2_07:PHIL2] Il crêche à Little Havana, oh! Regarde! Un poisson géant! [PHI2_08:PHIL2] Fais gaffe! Charlie sur la ligne des arbres! [PHI2_09:PHIL2] C'est moi qui hallucine ou la route est vraiment toute molle? [PHI2_10:PHIL2] Cuillère Brisée à Mère Poule, tu me reçois? [PHI2_11:PHIL2] Spooney Wooney Woo Woo Woooo! [PHI2_12:PHIL2] La mort m'appelle, mon garçon! [PHI2_13:PHIL2] Des ailes noires qui battent tout autour... [PHI2_14:PHIL2] C'est magnifique, mec, magnifique... Mais tellement froid... [PHI2_15:PHIL2] 10-4 On a un conducteur en état d'ivresse. [PHI2_04:PHIL2] SANTE DE PHIL : [PHI_AS1:PHIL2] CHEZ PHILS PLACE OK [PHI_AS2:PHIL2] ~g~De nouvelles armes sont en vente chez Phils Place. {=================================== MISSION TABLE PIZZA ===================================} [PIZ1_01:PIZZA] ~g~Va livrer ces pizzas. Tu dois les balancer aux clients. Roule à côté d'eux pour lancer les pizzas. [PIZ1_02:PIZZA] ~g~T'as balancé toutes tes pizzas, retourne en chercher d'autres. [PIZ1_05:PIZZA] ~g~Tu as 5 minutes pour livrer les commandes avant que que les clients n'appelent une autre pizzeria. [PIZ1_07:PIZZA] ~r~T'as tué le client! T'es viré! [PIZ1_08:PIZZA] ~r~T'es à la bourre! T'es viré! [PIZ1_09:PIZZA] ~r~T'as bousillé notre bécane! T'es viré! [PIZ1_11:PIZZA] Hé! Retourne sur ta bécane! [PIZ1_12:PIZZA] Pizzas restantes : [PIZ1_06:PIZZA] Appuie sur la~h~ ~k~~TOGGLE_SUBMISSIONS~~w~ quand tu es sur la moto pour annuler la mission. [PIZ1_13:PIZZA] Effectue ces livraisons en vitesse. [PIZ1_14:PIZZA] Mon pote, un pizza pour toi! [PIZ1_15:PIZZA] Allez, mon vieux, livre ça en vitesse. [PIZ1_16:PIZZA] Qu'est-ce que t'attends, mon vieux? T'as des pizzas à livrer! [PIZ1_17:PIZZA] Je sais que tu voulais pas faire ce boulot, mais je m'en tape! [PIZ1_18:PIZZA] Va livrer ça. [PIZ1_19:PIZZA] On a besoin de livrer ça. [PIZ1_20:PIZZA] Allez, mon vieux, livre ça ou t'es viré! [PIZ1_21:PIZZA] Il y a des gens qui attendent, mon vieux. [PIZ1_22:PIZZA] Mais qu'est-ce que t'attends? Faut livrer ça! [PIZ1_23:PIZZA] Va livrer cette saloperie de bouffe! [PIZ1_24:PIZZA] Les clients ont les crocs, mon vieux! [PIZ1_25:PIZZA] Tu peux prendre ça, mon vieux? [PIZ1_26:PIZZA] Tiens, va livrer ça en vitesse, amigo. [PIZ1_27:PIZZA] Magne-toi, on est débordés. Va livrer ça! [PIZ1_28:PIZZA] Encore toi? Qu'est-ce que t'attends? Va livrer ça! [PIZ1_29:PIZZA] Perds pas de temps, cette fois-ci. [PIZ1_30:PIZZA] Espèce de bon à rien! Va livrer ça en vitesse! [PIZ1_31:PIZZA] T'auras jamais de promotion si tu traînes encore ce coup-ci. [PIZ1_32:PIZZA] ~r~La pizza est trop chaude ou quoi? [PIZ1_33:PIZZA] ~g~Retourne au restaurant pour d'autres commandes. [PIZ1_34:PIZZA] ~g~Pizza livrée. Voilà ton fric. [PIZ_WON:PIZZA] Mission Pizza terminée. votre max de Santé passe à 150. {=================================== MISSION TABLE PORN1 ===================================} [POR1_A:PORN1] Action [POR1_B:PORN1] Ooooh! C'est un sacré morceau! [POR1_C:PORN1] 30 centimètres, c'est la taille réglementaire, bébé. [POR1_D:PORN1] COUPEZ! Qui c'est cet abruti? Toi! Toi! T'es dans le champ? POURQUOI? [POR1_E:PORN1] Qu'est-ce que c'est que ces conneries? [POR1_F:PORN1] Des aliens? Des cannes à pêche? [POR1_G:PORN1] J'ai jamais vu un requin aussi gros... [POR1_H:PORN1] Faut que tout ça dégage! [POR1_I:PORN1] Pourquoi t'es dans le métier, connard? [POR1_J:PORN1] Hein? [POR1_K:PORN1] Ben, pour les chattes, évidemment! Alors, qu'est-ce que c'est que ça? [POR1_L:PORN1] Ceci est mon art - SECURITE! [POR1_M:PORN1] Ecoute-moi, trou du cul arrogant, tu m'appartiens maintenant. Tout m'appartient ici. [POR1_N:PORN1] On va révolutionner cet endroit... [POR1_O:PORN1] Je vais faire ta fortune... [POR1_P:PORN1] Oh. Tu es... Tu... es Tommy Vercetti? Mais je croyais que tu étais... [POR1_Q:PORN1] Exactement... [POR1_R:PORN1] Va y avoir quelques changements dans le coin et on va commencer à se faire des ronds. [POR1_S:PORN1] Au fait, as-tu jamais pensé à... [POR1_T:PORN1] Mais d'abord, on va avoir besoin de quelques putes bien foutues... [POR1_U:PORN1] Ouais, les filles c'est bien mais toi... waou! [POR1_02:PORN1] ~g~Bute le mac de Candy, puis retourne la récupérer. [POR1_04:PORN1] Salut, Candy. Je cherche des actrices, ça te branche? [POR1_05:PORN1] Et comment! Mais faut causer à mon agent... [POR1_06:PORN1] Putain qu'est-ce que tu fous? [POR1_07:PORN1] Tu aurais mieux fait de rester à la maison, aujourd'hui! [POR1_7B:PORN1] J'arrive pas à croire ce trou du cul! [POR1_08:PORN1] Salut, Mercedes! [POR1_09:PORN1] Salut, Tommy! Tu viens faire la fête? [POR1_10:PORN1] Non, pas maintenant, chérie. Ca te dirait de faire du cinéma? [POR1_11:PORN1] Evidemment! Mais seulement si c'est sordide et bon marché! [POR1_13:PORN1] ~g~Emmène les filles au studio y retrouver Steve. [POR1_14:PORN1] T'es engagé! [POR1_15:PORN1] Hé tommy, tu viens pour un bout d'essai? [POR1_17:PORN1] Waou! Sympa le requin! [POR1_18:PORN1] ~r~Mercedes est morte! [POR1_20:PORN1] Tommy! Où tu vas? Reviens ici! [POR1_21:PORN1] Où tu vas? [POR1_22:PORN1] Tommy, quand c'est qu'on va pouvoir être un peu seuls ensemble? [POR1_01:PORN1] ~g~Candy Suxxx serait parfaite pour un premier rôle! [POR1_12:PORN1] ~g~Emmène Candy avec toi pour rencontrer Mercedes. [POR1_16:PORN1] Plus tard, peut-être, mon chou... [POR1_24:PORN1] ~g~Retourne chercher Candy. [POR1_25:PORN1] ~g~Tu as laissé Candy. Retourne la chercher! [POR1_23:PORN1] ~g~Candy va s'occuper de l'affaire dans le ~h~Centre~g~. [POR1_26:PORN1] ~g~Voilà Candy. On dirait qu'elle a encore eu rendez-vous avec le représentant du Congrès, Alex Shrub. [POR1_27:PORN1] Allez, partons d'ici! [POR1_28:PORN1] Tommy, sois prudent! Mes implants ne sont pas encore assurés! [POR1_29:PORN1] Tu appelles ça conduire? [POR1_30:PORN1] Je pourrais plus faire du porno après ça! [POR1_31:PORN1] Quoi? T'essaies de me tuer ou quoi? Je croyais que c'était moi, la star! {=================================== MISSION TABLE PORN2 ===================================} [POR2_A:PORN2] Comment ça se passe, Steve? [POR2_B:PORN2] Eh ben, Candy est faite pour le rôle et la nouvelle fille est infatigable! [POR2_C:PORN2] Elle s'est tapé la moitié du casting et des techniciens avant même que j'ai fini de régler les éclairages! [POR2_D:PORN2] Enfin, demain, on va en extérieurs pour tourner les scènes du bateau... [POR2_E:PORN2] Les scènes du bateau? Quelles scènes du bateau? [POR2_F:PORN2] Les pêcheurs sont dans les filets de la passion quand ce requin géant arrive... [POR2_G:PORN2] Qu'est-ce que j'avais dit à propos du requin géant? [POR2_H:PORN2] J'avais dit : PAS DE REQUIN GEANT! Ok? [POR2_I:PORN2] Laisse simplement les caméras braquées sur le pieu! [POR2_J:PORN2] Ok ok, Tommy, j'avais bien le droit d'essayer... [POR2_K:PORN2] Les prospectus sont imprimés? [POR2_L:PORN2] Ouais, mais personne va nous laisser distribuer ces trucs, je veux dire... [POR2_M:PORN2] Ils sont un peu trop, hum, enfin, ils manquent un peu de poésie... [POR2_N:PORN2] T'en fais pas pour ça! [POR2_O:PORN2] J'ai mes propres plans pour la distribution... [POR2_P:PORN2] OK. Hé, Candy, amène-toi dans ma caravane! [POR2_01:PORN2] ~g~Il y a un hydravion à l'arrière des studios qui servait pour la propagande des vieux films. [POR2_02:PORN2] ~g~Atteins l'un des points de contrôle pour commencer à larguer les prospectus. [POR2_03:PORN2] ~g~Largue les prospectus tout le long du chemin jusqu'au dernier point de contrôle. [POR2_04:PORN2] ~r~NIVEAU DE CARBURANT BAS! [POR2_05:PORN2] Utilise-le pour distribuer les prospectus dans la ville. [DILDO:PORN2] Kérosène : [POR2_Q:PORN2] Oh, merde! [PORN2_9:PORN2] ~g~Il te reste ~1~ secondes pour retourner à une Skimmer avant la fin de la mission. {=================================== MISSION TABLE PORN3 ===================================} [POR3_A:PORN3] Bon, c'est quoi le problème, maintenant? [POR3_B:PORN3] Chuuuut! [POR3_C:PORN3] Eh bien, après l'intime rencontre avec les envahisseuses nymphomanes, [POR3_D:PORN3] notre jeune héros se retrouve incapable de penser à autre chose que cette énorme montagne phallique [POR3_E:PORN3] et c'est là qu'on veut placer la scène avec la cuve de purée de patates, mais alors on... [POR3_F:PORN3] Ces conneries m'intéressent pas! [POR3_G:PORN3] Continuez simplement le boulot, continuez... [POR3_H:PORN3] Hé, Tommy... [POR3_I:PORN3] T'as évoqué un problème légal au téléphone... [POR3_J:PORN3] Ah ouais! Le représentant du Congrès Alex Shrub a pris le train électoral en marche à la pêche des votes puritains... [POR3_K:PORN3] La rumeur laisse entendre qu'il soutient des mesures de restriction contre... [POR3_L:PORN3] Disons... les formes les plus charnelles de la grande industrie cinématographique de ce pays. [POR3_M:PORN3] Super. [POR3_N:PORN3] Candy! Tu connais Shrub. [POR3_O:PORN3] Vous faites des trucs bizarres tous les deux? [POR3_P:PORN3] Oh oui! Oui! Oh oui! Oui oui oui ouuuuuuuuuuuuuuuuuui! [POR3_Q:PORN3] Dis-moi que t'as eu ça... [POR3_R:PORN3] Est-ce que ça faisait partie du... euh... Ou bien elle parlait à... [POR3_S:PORN3] Impossible à dire... Dans tous les cas... [POR3_T:PORN3] Tu ferais mieux de la suivre après le tournage [POR3_U:PORN3] et voir si elle te mènera pas à leur nouveau nid d'amour. [POR3_V:PORN3] T'as un appareil photo? [POR3_X:PORN3] Hé, file-lui un appareil photo! [POR3_02:PORN3] ~r~Tu as tué le représentant du Congrès! T'auras du mal à le faire chanter, maintenant! [POR3_03:PORN3] ~r~Tu as alerté la sécurité du représentant du Congrès, ils vont le faire sortir tout de suite. [POR3_04:PORN3] Candy, tu peux m'appeler Martha? [POR3_05:PORN3] Oh Alex, enfin, je veux dire Martha. Comme tu veux... [POR3_06:PORN3] Martha, il y a quelqu'un qui regarde... C'est pervers... [POR3_07:PORN3] Vous là-bas, donnez-moi cet appareil photo! [POR3_01:PORN3] ~g~Suis la ~h~Stretch~g~ de Candy. [POR3_15:PORN3] ~r~T'as bousillé la Stretch de Candy! [POR3_17:PORN3] ~g~Retourne aux Porn Studios avec la pellicule. [POR3_19:PORN3] ~r~T'es à court de pellicule! [POR3_21:PORN3] ~g~T'as perdu la Stretch de Candy! [POR3_22:PORN3] ~g~Le WK Chariot Hotel de l'autre côté de ce balcon devrait être idéal, comme point de vue. [POR3_23:PORN3] ~g~Il y a une porte latérale qui te permettra d'accéder à l'hôtel. [POR3_08:PORN3] Appuie sur la~h~ ~k~~PED_LOCK_TARGET~ ~w~et maintiens-la enfoncée pour ~h~cadrer~w~ la photo. [POR3_09:PORN3] Appuie sur la~h~ ~k~~PED_LOCK_TARGET~ ~w~et maintiens-la enfoncée pour ~h~cadrer~w~ la photo. [POR3_10:PORN3] Appuie sur la~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~pour faire un ~h~zoom avant~w~ et sur la~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~pour faire un ~h~zoom arrière~w~. [POR3_11:PORN3] Appuie sur la~h~ ~k~~PED_SNIPER_ZOOM_IN~ ~w~pour faire un ~h~zoom avant~w~ et sur la~h~ ~k~~PED_SNIPER_ZOOM_OUT~ ~w~pour faire un ~h~zoom arrière~w~. [POR3_12:PORN3] Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour prendre une photo. [POR3_13:PORN3] Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour prendre une photo. [POR3_14:PORN3] Appuie sur la~h~ ~k~~PED_FIREWEAPON~ ~w~pour prendre une photo. [POR3_20:PORN3] ~g~Si tu as besoin de te déplacer, utilise la ~h~Sparrow~g~ garée derrière. [POR3_16:PORN3] ~g~Il te faut trois bons clichés d'Alex Shrub avec Candy pour le faire chanter. [POR3_24:PORN3] PHOTOS PRISES : {=================================== MISSION TABLE PORN4 ===================================} [POR4_A:PORN4] Je suis désolée, mais je ne peux pas avaler maintenant! [POR4_B:PORN4] Oh, ALLEZ! Chérie! [POR4_C:PORN4] Il décharge comme une baleine, par pitié [POR4_D:PORN4] comment tu fais pour ne pas saisir le rôle? [POR4_E:PORN4] Mais Stevie... [POR4_F:PORN4] Comment va mon réalisateur vedette? [POR4_G:PORN4] Ah, mon pote, la lutte incessante entre l'intégrité artistique et [POR4_H:PORN4] l'éjaculation sous toutes ses formes est loin d'être terminée. [POR4_I:PORN4] Et avant que tu me poses la question, je t'informe que les quatre vidéos sortiront dans les... [POR4_J:PORN4] Chérie, veux-tu garder l'anaconda dans le champ, S'IL TE PLAIT. [POR4_K:PORN4] Il prend plus de l'heure que toi! [POR4_L:PORN4] Oh, pardon, Steve... [POR4_M:PORN4] Je me disais, faudrait monter un gros coup de pub pour la promo du film. [POR4_N:PORN4] Un truc qui aurait vraiment de l'impact sur la ville... T'as pas une idée? [POR4_O:PORN4] Eh ben, autrefois, on faisait des galas, [POR4_P:PORN4] avec des vedettes, des limousines, la nuit éclairée par les projos... [POR4_Q:PORN4] Des projecteurs? J'ai une idée! [POR4_R:PORN4] ...Ouais, ouais, ouais! Le petit numéro de paillettes, les limousines, ah, les premières! [POR4_S:PORN4] Oh oui, chère madaaaame, bien sûr, chère madaaaame... [POR4_T:PORN4] et la presse, et le barrage de lumières... [POR4_01:PORN4] ~g~Va dans le ~y~Centre~g~ et ajuste le projecteur sur le toit du bâtiment. [POR4_02:PORN4] ~g~Une bécane rapide est nécessaire pour sauter de toit en toit. L'agent de sécurité a l'habitude de venir en ~y~PCJ 600~g~ au boulot... [POR4_03:PORN4] ~g~Faut que tu montes sur les toits. Il devrait y avoir un ascenceur dans l'un des immeubles de bureaux. [POR4_06:PORN4] ~g~Retourne à l'immeuble de bureaux du bas si tu as besoin d'accéder à nouveau au toit. [POR4_07:PORN4] ~g~Tu as besoin d'une bécane pour sauter de bâtiment en bâtiment! [POR4_08:PORN4] ~g~Fonce à travers la fenêtre pour commencer. Tu as jusqu'à 07:00 avant que la lumière du jour ne risque de te faire découvrir. [POR4_09:PORN4] ~g~Les marqueurs t'indiquent sur quel bâtiment sauter ensuite. [POR4_10:PORN4] ~r~Il fait maintenant trop clair pour monter sans te faire voir. [POR4_11:PORN4] Retourne à l'escalier si tu as à nouveau besoin d'accéder au toit. [POR4_05:PORN4] ~g~Ces escaliers mènent à un bureau. [POR_AS1:PORN4] STUDIO DE CINEMA OK [POR_AS2:PORN4] ~g~Inter Global Films génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. {=================================== MISSION TABLE PROT1 ===================================} [PRO1_B:PROT1] Je supporte pas ce style. Tommy, qu'est-ce que t'en penses? Qu'est-ce que tu dirais qu'on mette un bar dans... [PRO1_D:PROT1] Ecoutez-moi... [PRO1_E:PROT1] L'heure est venue de mettre la main sur cette ville! Le monde n'attend plus que nous! [PRO1_F:PROT1] Faut qu'on commence à s'emparer de territoires! [PRO1_G:PROT1] Faut qu'on fasse comprendre à tout Vice City qu'on est les nouveaux maîtres du jeu! Tu vois ce que je veux dire? [PRO1_I:PROT1] Ce qu'il te faut, c'est une façade légale, Tommy! Une affaire officielle! Ca m'a jamais rapporté d'emmerdes! [PRO1_J:PROT1] Faut qu'on commence à utiliser des balaises ou autant faire une croix sur tout le boulot qu'on s'est tapé jusque là. [PRO1_K:PROT1] Les commerces locaux savent que Diaz est mort et ils refusent de payer pour leur protection! [PRO1_L:PROT1] Ah. On pourrait essayer la corruption... [PRO1_M:PROT1] La corruption! Mon cul, la corruption! Je vais vous montrer comment on leur fait peur! [PRO1_01:PROT1] ~g~Défoule-toi sur les devantures des magasins et les proprios supplieront pour être protégés. [PRO1_03:PROT1] ~r~C'était supposé être un raid éclair, pas une pause café. [PRO1_04:PROT1] Mon gagne-pain est détruit! [PRO1_05:PROT1] Tout est en ruines! [PRO1_06:PROT1] Je paye le prix fort pour ma protection! [PRO1_07:PROT1] Ma belle vitrine! [PRO1_08:PROT1] Mon magasin! Mon beau magasin! [PRO1_09:PROT1] Vercetti. Souvenez-vous de mon nom... [PRO1_10:PROT1] Je règne désormais sur cette ville. MOI! [BUYP1:PROT1] Tu peux désormais acheter des propriétés dans certaines zones de la carte. [BUYP2:PROT1] Si tu vois un marqueur vert sur une propriété, c'est que tu peux l'acheter. [PRO1_N:PROT1] Je reviens dans cinq minutes... [PRO1_11:PROT1] ~g~Va au ~y~Centre commercial de North Point~g~ dans ~y~Vice Point~g~. [PRO1_12:PROT1] ~g~Brise les vitrines de chaque magasin et les proprios supplieront pour une nouvelle protection. [PRO1_A:PROT1] Ah, faut qu'on redécore cet endroit, faut qu'on lui donne un air plus vieux. [PRO1_C:PROT1] T'es mon avocat, Rosenberg. Pas mon décorateur. Compris? [BUYP3:PROT1] Tiens-toi sur le marqueur, puis appuie sur la touche ~h~~k~~PED_ANSWER_PHONE~~w~ pour acheter cette propriété. [PRO1_13:PROT1] ~g~Tu as cinq minutes pour tous les avoir. {=================================== MISSION TABLE PROT2 ===================================} [PRO2_A:PROT2] C'est quoi l'problème? [PRO2_B:PROT2] Un bar refuse de payer. [PRO2_C:PROT2] Le boss considère qu'il est sous la protection d'un gang de voyous du coin... [PRO2_D:PROT2] Mais t'en fais pas, Tommy, je peux m'en occuper. [PRO2_E:PROT2] C'est ça que t'appelles t'en occuper? [PRO2_F:PROT2] Vous deux, levez vos culs de là... [PRO2_G:PROT2] Allons-y. [PRO2_01:PROT2] ~g~Liquide les gardes qui protègent le Front Page Bar et trouve qui les a approvisionnés. [PRO2_10:PROT2] ~g~Il y en a deux de plus qui se sont fait la malle. Finis-en avec eux. [PRO2_11:PROT2] Montez dans la caisse, bons à rien. [PRO2_02:PROT2] Ta protection a besoin d'être légèrement renforcée. [PRO2_03:PROT2] Ah, non, pas encore! J'en ai marre! [PRO2_04:PROT2] Ces imbéciles opèrent dans le quartier à partir de DBP Security. [PRO2_05:PROT2] Réglez la question entre vous, les mecs. [PRO2_06:PROT2] Bon, à plus tard. [PRO2_07:PROT2] Ouais, ouais, c'est ça. [PRO2_08:PROT2] ~g~Les abrutis du DBP Security sauront que t'y vas, alors fonce et chope-les avant qu'ils décampent! [PRO2_09:PROT2] ~g~Va parler au proprio du Front Page Bar. {=================================== MISSION TABLE PROT3 ===================================} [PRO3_A:PROT3] Pauvre crétin! A quoi tu pensais? [PRO3_B:PROT3] Tu réalise ce qui aurait pu arriver? [PRO3_C:PROT3] On a failli tous se faire avoir! [PRO3_D:PROT3] Le retardateur a dû déconner... [PRO3_E:PROT3] Cet endroit était prêt à sauter comme une usine de feux d'artifice. [PRO3_F:PROT3] Et pis quelqu'un a rencardé les flics... [PRO3_G:PROT3] C'est quoi le problème, les mecs? [PRO3_H:PROT3] Mike devait foutre le feu quelque part dans le centre commercial, [PRO3_I:PROT3] mais il a foiré son coup et maintenant, les flics grouillent, là-bas. [PRO3_J:PROT3] Faut qu'on récupère notre bordel et qu'on s'tire de là! [PRO3_K:PROT3] Du calme, vous deux, laissez-moi réfléchir une seconde! [PRO3_L:PROT3] Tommy Vercetti a pas l'habitude de mettre les bouts comme ça... [PRO3_M:PROT3] Les flics vont passer ce bâtiment au peigne fin, pas vrai? [PRO3_N:PROT3] Mais ça prend du temps... [PRO3_O:PROT3] On va entrer et foutre le feu nous-mêmes! [PRO3_P:PROT3] Ouais, mais... [PRO3_Q:PROT3] Il y a que les flics qui peuvent approcher à moins d'un kilomètre de là-bas! [PRO3_R:PROT3] Alors, on ira en tant que flics! [PRO3_S:PROT3] On va se dégotter des uniformes et il nous faut aussi une bagnole de patrouille. [PRO3_T:PROT3] Tout ça à cause de toi, Mike. [PRO3_U:PROT3] Je suis désolé. [PRO3_V:PROT3] J'ai trouvé! [PRO3_W:PROT3] Tout ce qu'on a à faire, c'est appâter les flics avec un doigt levé, [PRO3_X:PROT3] de les prendre au piège [PRO3_Y:PROT3] et de leur sauter dessus! [PRO3_Z:PROT3] Super plan! En route! [PRO3_A1:PROT3] OK. [PRO3_01:PROT3] Ok Lance, attirons l'attention des flics! [PRO3_02:PROT3] ~g~Prends la bagnole des flics et va poser la bombe au Tarbrush Coffee Shop dans le centre commercial. [PRO3_03:PROT3] ~g~T'as laissé Lance derrière, va le récupérer! [PRO3_04:PROT3] ~g~Allons-y! [PRO3_05:PROT3] ~r~T'as buté Lance! [PRO3_07:PROT3] ~g~Tu as grillé ta couverture! Magne-toi de poser la bombe! [PRO3_09:PROT3] Ligotons et bâillonnons-les! [PRO3_10:PROT3] Aaah! Il me va parfaitement! [PRO3_11:PROT3] Mais quand même un peu serré au niveau des couilles... [PRO3_12:PROT3] Ah ouais? Le mien aussi, c'est pareil! [PRO3_13:PROT3] Du calme, mon pote! Les flics conduisent pas aussi mal! [PRO3_14:PROT3] Oublie pas... Faut sourire aux autres flics... [PRO3_15:PROT3] Salut officier. Bel insigne, bel insigne... [PRO3_16:PROT3] Tout doux, Lance. [PRO3_17:PROT3] Ok, les retardateurs sont programmés, 5 secondes et ça saute! [PRO3_18:PROT3] 5 secondes? Putain faut dégager d'ici en vitesse! [PRO3_19:PROT3] Maintenant, ils vont vraiment être fâchés... [PRO3_20:PROT3] ~g~Fais-toi suivre par deux flics dans le garage. [PRO3_21:PROT3] ~g~Tu dois avoir un indice de recherche pour que les flics te suivent dans le piège. [PRO3_22:PROT3] ~g~La porte est bloquée! Dégage le passage pour qu'elle puisse être refermée. [PRO3_23:PROT3] ~g~Marche sur le marqueur pour poser la bombe. [PRO3_24:PROT3] ~g~Tire-toi du café! [PRO_AS1:PROT3] PROTECTION OK [PRO_AS2:PROT3] ~g~Vercetti Estate génère désormais un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. [PRO3_08:PROT3] ~g~ Retourne à ~h~Vercetti Estate~g~ sur ~h~Starfish Island~g~. {=================================== MISSION TABLE RACES ===================================} [RACES_2:RACES] ~g~Il te faut un véhicule, c'est pas une course à pied! [RACES_3:RACES] 3... 2... 1... PARTEZ! [RACES_8:RACES] ~r~Tu n'as pas gagné la course! [RACES00:RACES] Course ~1~ : [RACES01:RACES] Terminal Velocity [RACES02:RACES] Ocean Drive [RACES03:RACES] Border Run [RACES04:RACES] Capital Cruise [RACES05:RACES] Virée! [RACES06:RACES] Endurance V.C. [RACES07:RACES] Participation : ~1~$ [RACES08:RACES] Meilleur temps : ~1~:~1~ [RACES09:RACES] Meilleure perf : 1er [RACES10:RACES] Meilleure perf : 2e [RACES11:RACES] Meilleure perf : 3e [RACES12:RACES] Meilleure perf : 4e [RACES13:RACES] Longueur circuit : ~1~.~1~ km [RACES15:RACES] Meilleur temps : NA [RACES16:RACES] Meilleure perf : NA [RACES18:RACES] TU AS GAGNE : $~1~ [RACES19:RACES] Tu n'as pas assez de moyens pour participer. [RACES22:RACES] Meilleur temps : ~1~:0~1~ [RACES23:RACES] Longueur circuit : ~1~.~1~ miles [RACES_1:RACES] ~g~Trouve un bolide et rends-toi sur la grille de départ. [RACEHLP:RACES] ~w~Appuie sur la~h~ ~k~~PED_SPRINT~~w~ pour commencer la course choisie. Appuie sur la~h~ ~k~~VEHICLE_ENTER_EXIT~~w~ pour quitter. {=================================== MISSION TABLE RCHELI1 ===================================} [WRECKED:RCHELI1] Ton véhicule est mort! [RCH1_4:RCHELI1] Points de passage restants : [RCH1_6:RCHELI1] ~g~Utilise l'hélico télécommandé pour passer les points de passage à travers l'aéroport. [RCH1_7:RCHELI1] ~g~Il y a 20 points de passage en tout. [RCH1_12:RCHELI1] ~g~L'hélicoptère télécommandé est presque hors de portée! [RCH1_13:RCHELI1] ~r~L'hélicoptère radiocommandé est hors de portée! [RCH1_8:RCHELI1] { reVC update } ~g~Si tu veux quitter cette mission, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~~g~ pour faire exploser ton hélicoptère radiocommandé. {=================================== MISSION TABLE RCPLNE1 ===================================} [RCPL1_4:RCPLNE1] ~g~Fais une COURSE DE POINTS DE PASSAGE avec 3 autres avions télécommandés. [RCPL1_5:RCPLNE1] ~g~Passe par les points de passage dispersés dans Vice City. [RCPL1_6:RCPLNE1] { reVC update } ~g~Si tu veux quitter cette mission, appuie sur la ~h~~k~~VEHICLE_FIREWEAPON~~g~ pour faire exploser ton avion radiocommandé. [RCPL1_8:RCPLNE1] ~g~Ton avion RC va sortir du périmètre! [RCPL1_9:RCPLNE1] ~r~Ton avion RC est sorti du périmètre! {=================================== MISSION TABLE RCRACE1 ===================================} [RCR1_4:RCRACE1] Tours restants : [RCR1_1:RCRACE1] ~g~Fais une course de points de passage contre 3 autres voitures RC. [RCR1_2:RCRACE1] ~g~Sois le premier à boucler 2 tours de circuit pour gagner! [RCR1_6:RCRACE1] ~g~Ta voiture RC va sortir du périmètre! [RCR1_7:RCRACE1] ~r~Ta voiture RC est sortie du périmètre! {=================================== MISSION TABLE ROCK1 ===================================} [RBM1_A:ROCK1] Okaaaaaaaaaay! [RBM1_B:ROCK1] Grandiose! Vraiment génial! [RBM1_D:ROCK1] Hé, t'as déjà rencontré les Love Fist? [RBM1_E:ROCK1] Non, mais j'adore votre musique! [RBM1_F:ROCK1] Laisse-moi te présenter au groupe. [RBM1_G:ROCK1] Voici P, Percy, Dick et Willy est aux chiottes et Jezz était dans la cabine tout à l'heure... [RBM1_H:ROCK1] Les mecs, je veux vous présenter un bon ami à moi. [RBM1_I:ROCK1] Voici Tommy. Ca fait un bail qu'on se connait. [RBM1_J:ROCK1] Ok, mon pote. [RBM1_K:ROCK1] Ah, euh... C'est quoi déjà, ton nom? [RBM1_L:ROCK1] Arrête-ça Jezz tu t'en souviens, [RBM1_M:ROCK1] et essaye pas de jouer au plus malin avec moi, [RBM1_N:ROCK1] je suis trop intelligent, mon mignon! [RBM1_O:ROCK1] Tu vois, le problème, Tom, c'est que ces garçons ont besoin d'aide. [RBM1_P:ROCK1] Ils n'ont pas beaucoup de contacts ici et ils savent pas à qui s'adresser. [RBM1_Q:ROCK1] On a besoin de drogue, mon pote! [RBM1_R:ROCK1] Faut qu'on réveille la vieille fureur de Love Fist, tu vois? [RBM1_S:ROCK1] Et bien, ici, c'est Vice City, quel est le problème? [RBM1_U:ROCK1] Du Love Juice, mec! [RBM1_V:ROCK1] Du Love Juice? [RBM1_W:ROCK1] Ouais, deux doses de boomshine, une dose de trompette, 5 bombes fizz et un litre d'essence. [RBM1_X:ROCK1] Tu peux nous aider, mon pote? [RBM1_Y:ROCK1] Ah, ça rendrait tellement service aux garçons! [RBM1_Z:ROCK1] Tu peux faire ça pour les garçons, pas vrai? [RBM1_8:ROCK1] ~r~Mercedes est morte! [RBM1_10:ROCK1] ~r~Imbécile! Tu as détruit la marchandise! [RBM1_13:ROCK1] ~g~Amène le Love Juice et Mercedes au groupe avant qu'ils n'entrent sur scène. [RBM1_15:ROCK1] ~r~Tu as perdu le dealer, notre cash et la drogue! [RBM1_17:ROCK1] ~g~Tue le dealer et prends la drogue! [MOB_07A:ROCK1] Salut, mon vieux, les gars auraient besoin d'un peu de compagnie, si tu vois ce que je veux dire... [MOB_07B:ROCK1] Je connais justement la fille qu'il faut. [ROK1_5:ROCK1] Salut Mercedes! [ROK1_6:ROCK1] Salut, Tommy. Comment vas-tu? [ROK1_7:ROCK1] Ca roule. Ecoute, les Love Fist, ça te branche? [ROK1_8:ROCK1] Ok, mais c'est un service qu'il faudra me retourner... [RBM1_14:ROCK1] ~g~T'as besoin d'une bagnole ou d'une bécane! [RBM1_1:ROCK1] ~g~Va prendre Mercedes à son appartement. [RBM1_12:ROCK1] ~g~Va récupérer les ingrédients du Love Juice chez le dealer. [ROK1_2:ROCK1] PLUS NECESSAIRE [ROK1_3:ROCK1] PLUS NECESSAIRE [MERC_39:ROCK1] A plus tard, champion. [RBM1_C:ROCK1] Hé, Tommy! Content que t'aies réussi! [ROK1_1A:ROCK1] Tu cherches quelque chose de spécial? J'ai ce qu'il te faut! [ROK1_9:ROCK1] Merci pour le pognon, suce boules! [RBM1_T:ROCK1] On a besoin de Love Juice, mec, tu vois? {=================================== MISSION TABLE ROCK2 ===================================} [RBM2_A:ROCK2] Tommy, mon pote, je suis content de te voir! [RBM2_B:ROCK2] Qu'est-ce qui se passe? [RBM2_C:ROCK2] Des mauvaises vibrations, Tommy... [RBM2_E:ROCK2] Il y a ce chat, on le connaît pas, mais lui il nous connaît. [RBM2_F:ROCK2] C'est comme ce chat. Il nous connaît tous. [RBM2_G:ROCK2] Il sait que Willy a un faible pour les culottes de ses femmes, hé hé! [RBM2_H:ROCK2] Ou que Percy aime bien Duran Duran! [RBM2_K:ROCK2] Ouais, le truc de la fusée d'amour, c'est vrai. Mais écoute, ce chat... [RBM2_L:ROCK2] ouais, ouais, ce gars, il veut voir les Love Fist morts! [RBM2_M:ROCK2] Morts, Tommy. [RBM2_N:ROCK2] Les Love Fist disparus. Tu sais ce qu'on dit, les meilleurs meurent jeunes... [RBM2_O:ROCK2] Tommy, faut que tu sauves les Love Fist! [RBM2_P:ROCK2] On a une séance d'autographes dans deux heures et je pense que... [RBM2_Q:ROCK2] Et les garçons pensent que le mec va essayer quelque chose de très vilain là-bas. [RBM2_1:ROCK2] ~g~Conduis la limousine à la séance d'autographes et essaye de démasquer le psychopathe . [RBM2_2:ROCK2] ~r~T'as bousillé la bagnole du groupe! [RBM2_3:ROCK2] ~g~Va à la séance d'autographes! [RBM2_4:ROCK2] ~g~Attrape le psychopathe! Ne le laisse pas s'échapper! [RBM2_5:ROCK2] ~r~Tu l'as perdu, imbécile! [RBM2_7:ROCK2] ~r~Les fans ont été attaqués, le psychopathe ne se montrera pas! [RBM2_8:ROCK2] ~r~Les agents de sécurité ont été attaqués, le psychopathe ne se montrera pas! [PSYCH_1:ROCK2] Je veux voir les Love Fist morts! [PSYCH_2:ROCK2] Les Love Fist ont gâché ma vie! [RBM2_I:ROCK2] Ferme-la un peu. C'est pas parce que Jezz encule les moutons... [RBM2_R:ROCK2] Oh, ferme-la! [RBM2_D:ROCK2] Je plaisante pas. C'est pas des conneries, mec, tu piges? [RBM2_J:ROCK2] C'est le truc de la fusée d'amour, tu sais? {=================================== MISSION TABLE ROCK3 ===================================} [RBM3_A:ROCK3] Tommy! Le psychopathe est de retour! [RBM3_B:ROCK3] Qu'est-ce qui se passe? [RBM3_C:ROCK3] Ce psychopathe ne laissera jamais les Love Fist tranquilles! [RBM3_D:ROCK3] Tu l'as pas tué, mon pote et il est revenu. [RBM3_E:ROCK3] Ouais, ouais et le truc c'est que... [RBM3_F:ROCK3] Le truc, c'est qu'on a besoin de quelqu'un de confiance pour conduire la limousine, [RBM3_G:ROCK3] parce que ce timbré continue à nous menacer! [RBM3_I:ROCK3] On chie tous dans nos frocs, mec. [RBM3_J:ROCK3] Ok, les gars, du calme, je vais m'en occuper... [RBM3_K:ROCK3] Normalement, j'ai mieux à foutre que de faire le taxi pour une bande d'Ecossais bisexuels bourrés, [RBM3_L:ROCK3] mais dans votre cas, je vais faire une exception. [ROK3_03:ROCK3] Autant pas faire dans la demi-mesure, alors. [ROK3_04:ROCK3] Hé, Tommy, change la musique! [ROK3_08:ROCK3] Ca commence à me faire chier, cette histoire. [ROK3_09:ROCK3] Garde juste le pied au plancher! [ROK3_61:ROCK3] Faut qu'on trouve la bombe! [ROK3_28:ROCK3] Je vais bientôt jouer de la basse en enfer... [ROK3_30:ROCK3] Que quelqu'un fasse quelque chose! [ROK3_32:ROCK3] Ok, toi monsieur le dur à cuire, fais quelque chose! [ROK3_34:ROCK3] Willy pourrait juste sucer le boomshine à la paille. [ROK3_37:ROCK3] Passez une paille à Willy! [ROK3_41:ROCK3] Quel fil, Tommy? [ROK3_42:ROCK3] Le vert. [ROK3_43:ROCK3] Y'en a pas de vert. Ou alors celui-la est vert? [ROK3_44:ROCK3] Est-ce qu'un de ces fils te semble vert? [ROK3_49:ROCK3] Ca fait des années que je te supporte! [ROK3_51:ROCK3] Une grosse poufiasse hurlante... [ROK3_52:ROCK3] Ouais! [ROK3_53:ROCK3] Ferme-la et arrache un fil! [ROK3_54:ROCK3] Lequel? [ROK3_55:ROCK3] Celui-là... [ROK3_56:ROCK3] Non! [ROK3_57:ROCK3] Hé, on est ok, on a pas sauté, les potes! Tommy, mon pote bravo. Et Rock and Roll! [ROK3_58:ROCK3] On a pas un concert où aller? Un racket à faire? Des fans à insulter? [ROK3_59:ROCK3] LOVE FIST! [ROK3_60:ROCK3] T'as fini avec cette bouteille? [RBM3_4:ROCK3] ~r~Tu as tué les Love Fist! [RBM3_6:ROCK3] DETONATION : [RBM3_1:ROCK3] ~g~Conduis les Love Fist jusqu'au concert. [RBM3_2:ROCK3] Si tu essayes de quitter la voiture tant que la bombe est armée, elle explosera. [RBM3_3:ROCK3] Si la jauge de détonation devient pleine, la bombe explose. [RBM3_8:ROCK3] Plus tu conduis vite, plus la jauge de détonation diminue. [RBM3_7:ROCK3] ~g~BOMBE DESAMORCEE! [ROK3_6A:ROCK3] ~g~Love Fist. Vous avez fini de polluer les ondes! [ROK3_6B:ROCK3] ~g~Je vous ai donné l'occasion de devenir mes amis. Je vais maintenant vous offrir celle de mourir! [ROK3_62:ROCK3] alors on a pensé te montrer notre Temple du rock- [ROK3_63:ROCK3] Que tu te fasses une idée de la fureur des Love Fist! [ROK3_64:ROCK3] Ecoute-toi toi même, mon pote. C'est de la daube! [ROK3_65:ROCK3] Hé, à tous les gosses, c'est un temple et nous sommes les prêtres! [ROK3_66:ROCK3] Ouais, eh ben, si les gosses aiment leurs prêtres à moitié saouls et complètement sourds, [ROK3_67:ROCK3] c'est leur problème. [ROK3_68:ROCK3] Ah, merde, la bande de la cassette est encore bouffée. [ROK3_69:ROCK3] Si ça continue, faudra jouer en live. [ROK3_70:ROCK3] Ooooh merde! Mes intestins... [ROK3_74:ROCK3] Ah, tiens, qu'est-ce que c'est que ça? Tommy, mets cette cassette. [ROK3_01:ROCK3] Mon pote, c'est enfin l'heure d'un verre bien mérité. [ROK3_02:ROCK3] La salle de concert est à une centaine de mètres plus loin. [ROK3_05:ROCK3] Je me sens bizarre quand ma tête ne tambourine pas. [ROK3_07:ROCK3] Tommy, faut que tu sauves le groupe! [ROK3_29:ROCK3] Tommy, continue de foncer, mec! [ROK3_31:ROCK3] Que quelqu'un fasse quelque chose... C'est quoi ces conneries? J'ai déjà vu des minettes plus courageuses. [ROK3_33:ROCK3] Ecoute, mec. Moi, je joue d'un instrument, j'y connais rien aux bombes. [ROK3_35:ROCK3] Ouais, j'ai entendu dire que t'étais doué pour ce genre de trucs! [ROK3_38:ROCK3] Une paille? Hé, ici, c'est le bus de tournée des Love Fist! [ROK3_39:ROCK3] Où c'est que tu veux que je trouve une paille? [ROK3_46:ROCK3] J'aurais dû te larguer quand j'en avais l'occasion. [ROK3_47:ROCK3] Capitaliste! [ROK3_48:ROCK3] Arriviste! [ROK3_73:ROCK3] Jezz écoute la cassette, [RBM3_9:ROCK3] Si tu t'arrêtes ou conduis trop lentement, la jauge de détonation augmente. [ROK3_50:ROCK3] Ferme-la, pauvre abruti! [ROK3_36:ROCK3] Hé, j'étais raide défoncé cette nuit-là, et tu le sais bien! [RBM3_H:ROCK3] Je chie dans mon froc, mec. Je veux ma maman! [ROK3_45:ROCK3] Oh, je vois la mort dans les cartes! Tout est vert! [ROK3_6C:ROCK3] ~g~Si tu ralentis, la limousine explosera et les GROS TOCARDS CHEVELUS avec! [ROK3_71:ROCK3] On doit continuer. Merci encore, Tommy! T'as assuré, mec! A plus! [ROK3_1:ROCK3] Enfin, mec, voici l'heure d'un verre bien mérité. La salle est juste à quelques mètres d'ici. [ROK3_2:ROCK3] Tu m'en sers un grand alors. Hé, Tommy, change de musique, mec. [ROK3_3:ROCK3] Je pète les plombs si je secoue pas la tête. Hé, regarde, c'est quoi ça? Tommy, mets cette cassette. [ROK3_4:ROCK3] Love Fist. Vous avez fini de polluer les ondes. Je vous ai donné une chance de devenir mes amis. [ROK3_5:ROCK3] Maintenant vous allez mourir. Essayez de freiner et c'est la fin! Paf, plus de limousine et plus de GROS NASES CHEVELUS non plus. [ROK3_6:ROCK3] Tommy, mec, tu dois sauver le groupe! J'en ai marre de ces conneries. Enlève pas ton pied de la pédale, mec! [ROK3_7:ROCK3] On doit trouver la bombe! On peut pas continuer à rouler toute la journée? Ouais, quoi, on a plein de trucs à boire... [ROK3_8:ROCK3] La bombe, elle serait pas dans le moteur? Va falloir qu'on s'arrête pour la désamorcer! Merde, mec, on va tous crever! J'vais m'bourrer la gueule! [ROK3_9:ROCK3] Hé, on est dans la mouise, mon pote! Tu trouveras pas ta réponse dans la boisson! Tire-toi de là! [ROK3_10:ROCK3] Hé, y'a des fils qui sortent de la bouteille de vodka! C'est pas de la vodka, c'est du BOOMSHINE! [ROK3_11:ROCK3] AAAAAHHHHHH! Elle va exploser! AAAAAAAAAAAHHHHH!!!!!!!!!! [ROK3_12:ROCK3] On m'avait prévenu que l'alcool me perdrait. J'ai déjà vu ça à la télé. Il suffit de tirer un de ces fils, mais lequel? J'en sais foutre rien, mec! [ROK3_13:ROCK3] Putain, j'en sais rien! Willy, dis quelque chose. J'vais jouer de la basse en enfer. [ROK3_14:ROCK3] Tommy, mec, continue de rouler à fond la caisse. Que quelqu'un fasse quelque chose! Ouais, c'est ça. [ROK3_15:ROCK3] 'Que quelqu'un fasse quelque chose'. Putain, c'est quoi ces conneries. J'ai connu des tantes plus braves. Vas-y le gros dur, fais quelque chose! [ROK3_16:ROCK3] Ecoute, mec, je suis musicien moi, j'sais pas comment ça fonctionne les bombes. Willy a qu'à sucer le boomshine à la paille. [ROK3_17:ROCK3] Ouais, on m'a dit que ça, tu savais faire. J'étais tout émoustillé ce soir-là, comme tu le sais! [ROK3_18:ROCK3] T'as qu'à filer une paille à Willy! Une paille? C'est le bus de tournée des Love Fist! [ROK3_19:ROCK3] Où est-ce que je vais bien pouvoir trouver une paille? Quel fil, Tommy? Le vert. Y'en a pas de vert. [ROK3_20:ROCK3] Où peut-être qu'il est vert celui-là? T'en vois un vert, toi? [ROK3_21:ROCK3] Oh non! On va crever! Je vois la vie en vert! J'aurais dû me débarrasser de toi qu'en j'en avais l'occasion. [ROK3_22:ROCK3] Chasse la gloire! Capitaliste! Ca fait des années que je te soutiens. Ferme-là, tafiole! [ROK3_23:ROCK3] Une grosse chialeuse, ouais, ferme-là et tire le putain de fil! Lequel? Celui-là... [ROK3_24:ROCK3] NON! On est pas morts. On a pas sauté, mec! [ROK3_25:ROCK3] Tommy, bien joué, mec. Rock and roll, mon pote! On a pas un concert à donner, nous? [ROK3_26:ROCK3] Amasser du blé? Abuser des minettes? LOVE FIST! [ROK3_27:ROCK3] T'as fini avec cette bouteille? {=================================== MISSION TABLE SERG1 ===================================} [TEX1_A:SERG1] Entre et pose ton cul sur une des banquettes. [TEX1_B:SERG1] Putain, mon père avait l'habitude de dire qu'à cheval donné, on ne regarde pas les dents et il l'a jamais fait! [TEX1_C:SERG1] Tu veux une larme de bon vieux whisky? [TEX1_D:SERG1] Non merci. [TEX1_E:SERG1] Un mec qui aime avoir les idées claires, c'est bien. [TEX1_F:SERG1] En ce moment, mes affaires tournent pas comme elles le devraient... [TEX1_G:SERG1] C'est la merde! Et je veux nettoyer une partie de cette merde! T'es avec moi? [TEX1_H:SERG1] Euh, ouais. [TEX1_I:SERG1] En fait, j'ai besoin d'un mec obstiné pour me débarrasser d'une merde. [TEX1_J:SERG1] T'as l'air du genre persuasif. [TEX1_K:SERG1] La persuasion c'est ma spécialité. [TEX1_L:SERG1] Bon, ce mec sera au country club du golf. [TEX1_M:SERG1] Ils autorisent pas les flingues, alors ses gardes du corps en auront pas. [TEX1_N:SERG1] Va me massacrer ce salopard. [TEX1_O:SERG1] Tiens, voilà ta carte de membre, mais va falloir te trouver des fringues plus appropriées. [TEX1_2:SERG1] ~g~Maintenant, direction le Leaf Links Golf Club. [TEX1_0:SERG1] ~g~La cible est à ta portée, en train de jouer au golf. Fais en sorte que ce soit sa dernière partie! [TEX1_3:SERG1] C'est qui ce mec? Occupez-vous de lui! [TEX1_6:SERG1] Quel beau cul! [TEX1_7:SERG1] C'est moi, ça? [TEX1_8:SERG1] Chaque fois que tu ouvres ton caddie, tu reçois automatiquement un club de golf si ton emplacement d'arme de corps à corps est vide. [TEX1_9:SERG1] Attrapez-le! [TEX1_10:SERG1] Tuez-moi ce dingue! [TEX1_1:SERG1] ~g~Va chercher des fringues de golfeur chez Jocksport's. {=================================== MISSION TABLE SERG2 ===================================} [TEXEXIT:SERG2] ~g~Maintenant, casse-toi de Little Haiti! [TEX_2A:SERG2] ~g~Parfait! Ils t'ont repéré! [TEX_2B:SERG2] ~r~Imbécile! Les gens doivent VOIR un cubain faire le coup! [TEX_2C:SERG2] ~g~Va te trouver des fringues de cubain dans Little Havana! [TEX_2D:SERG2] ~g~Maintenant, liquide le chef de gang haïtien au salon funéraire Romero! [TEX2_A:SERG2] Tommy, voici Donald Love. Donald, Tommy Vercetti, [TEX2_B:SERG2] la dernière crapule à débarquer ici. [TEX2_C:SERG2] Ouais... Euh... [TEX2_D:SERG2] Donald, ferme-la et écoute, t'apprendras peut-être quelque chose. [TEX2_E:SERG2] Bon. Il y a rien de mieux qu'une bonne guerre des gangs pour faire baisser les prix de l'immobilier. [TEX2_F:SERG2] A part une catastrophe, peut-être, comme une peste biblique ou un truc dans le genre. [TEX2_G:SERG2] Mais ça serait aller un peu trop loin, dans le cas présent. [TEX2_H:SERG2] Tu piges, tête de gland? [TEX2_I:SERG2] Un chef du gang haïtien est mort y'a pas longtemps. C'est apparemment un coup des Cubains, mais personne en est sûr. [TEX2_J:SERG2] Alors on va arranger ça! Tu te déguises en Cubain [TEX2_K:SERG2] et tu fonces foutre le bordel aux funérailles. Tu leur balances la sauce et tu dégages vite fait. [TEX2_L:SERG2] T'as compris, Donald? [TEX2_M:SERG2] C'est comme faire entrer le renard dans le poulailler, hein? [TEX2_N:SERG2] Et puis on attend tranquillement que les prix s'écroulent. {=================================== MISSION TABLE SERG3 ===================================} [TEX3_A:SERG3] Regarde un peu, mon pote. J'ai un problème et je compte sur ton aide. [TEX3_B:SERG3] Je suis pas constructeur. [TEX3_C:SERG3] Ben, je pensais surtout à tes talents de démolisseur. [TEX3_D:SERG3] Bon, là, c'est le développement prévu, et ça, [TEX3_E:SERG3] c'est la propriété sur laquelle on a des vues. [TEX3_F:SERG3] T'essayes de me dire que ce nouveau bloc de bureaux est comme qui dirait sur le chemin. [TEX3_G:SERG3] T'as tout compris. [TEX3_H:SERG3] Bon, je vais aller faire un tour en ville pendant un moment [TEX3_I:SERG3] et si la construction de ces bureaux devait faire face à de subits et insurmontables problèmes structurels, alors je... [TEX3_J:SERG3] En tant qu'honnête citoyen, vous vous sentiriez obligé d'intervenir [TEX3_K:SERG3] pour la réhabilitation d'un important secteur de la ville? [TEX3_L:SERG3] Où on peut en trouver d'autres, des mecs dans ton genre? [TEX3_1:SERG3] ~g~Utilise l'hélico télécommandé pour transporter des bombes sur les quatre cibles du bâtiment que tu dois détruire. [TEX3_2:SERG3] ~g~Tu dois larguer une bombe sur chaque cible, et ce, dans n'importe quel ordre. [TEX3_5:SERG3] ~g~Si tu largues une bombe sans succès, tu peux la récupérer pour un nouvel essai. [TEX3_7:SERG3] ~g~Tu a alors 7 minutes pour larguer toutes les autres bombes! [TEX3_8:SERG3] ~g~Tu as raté la cible! Récupère la bombe et réessaye! [TEX3_10:SERG3] ~g~Largue la bombe sur la cible. [TEX3_11:SERG3] Cibles restantes: [TEX3_17:SERG3] ~r~Tu as dépassé le temps imparti et le bâtiment n'est pas détruit. [TEX3_18:SERG3] ~r~Ton hélico télécommandé a été détruit! Comment vas-tu faire pour transporter les bombes, maintenant? [TEX3_19:SERG3] ~r~Tu as largué ta bombe dans l'eau! Tu as besoin des 4 bombes pour détruire le bâtiment! [TEX3_20:SERG3] ~g~Ton hélico télécommandé est presque hors de portée! Retourne au bâtiment et termine le travail! [TEX3_21:SERG3] ~r~Ton hélico télécommandé est hors de portée! [TEX3_24:SERG3] Appuie sur ~h~~k~~VEHICLE_LOOKLEFT~ ~w~pour faire pivoter l'hélico dans le sens inverse des aiguilles d'une montre. [TEX3_25:SERG3] Appuie sur ~h~~k~~VEHICLE_LOOKLEFT~ ~w~pour faire pivoter l'hélico dans le sens des aiguilles d'une montre. [TEX3_27:SERG3] ~g~L'escalier central mène à tous les étages du bâtiment. [TEX3_31:SERG3] ~r~Tu as détruit la camionnette qui contenait l'hélico télécommandé et les bombes! [TEX3_32:SERG3] Tu peux ~h~regarder derrière~w~ toi en ~h~appuyant simultanément sur ~k~~VEHICLE_LOOKLEFT~ et ~k~~VEHICLE_LOOKRIGHT~~w~. [TEX3_4:SERG3] { reVC update } ~g~Pour larguer une bombe, appuie sur la~h~ ~k~~VEHICLE_FIREWEAPON~~w~. [TEX3_29:SERG3] { reVC update } Pour larguer une bombe, appuie sur la~h~ ~k~~VEHICLE_FIREWEAPON~. [TEX3_26:SERG3] Appuie sur la ~h~~k~~VEHICLE_BRAKE~ ~w~pour réduire la vitesse du rotor et ainsi faire~h~ descendre l'hélicoptère. [TEX3_22:SERG3] Appuie sur la ~h~~k~~VEHICLE_ACCELERATE~ ~w~pour augmenter la vitesse du rotor et ainsi faire~h~ monter l'hélicoptère. [TEX3_16:SERG3] ~g~Va jusqu'au camion ~w~TOPFUN ~g~ près du bâtiment à démolir. [TEX3_33:SERG3] Une fois que tu as ramassé une bombe, le radar t'indique la position de la cible par rapport à l'hélico. [TEX3_34:SERG3] Le ~h~point triangulaire vers le haut ~w~indique que la cible se trouve ~h~au-dessus ~w~de l'hélicoptère radiocommandé. [TEX3_35:SERG3] Le ~h~point triangulaire vers le bas ~w~indique que la cible se trouve ~h~en dessous ~w~de l'hélicoptère radiocommandé. [TEX3_36:SERG3] Le ~h~point carré ~w~indique que la cible se trouve ~h~au même niveau ~w~que l'hélicoptère radiocommandé. [TEX3_6:SERG3] ~g~Une fois que tu as ramassé une bombe pour la première fois, le compte à rebours s'enclenche. [TEX3_28:SERG3] Pour ~h~ramasser une bombe~w~, manoeuvre l'hélico radiocommandé à côté. Il ne peut transporter qu'une seule bombe à la fois. [TEX3_30:SERG3] ~g~Pour ramasser une bombe, manoeuvre l'hélico radiocommandé à côté. Il ne peut transporter qu'une seule bombe à la fois. [TEX3_12:SERG3] ~g~Bombe posée! Plus que 3 cibles! Va chercher une autre bombe! [TEX3_13:SERG3] ~g~Bombe posée! Plus que 2 cibles! Va chercher une autre bombe! [TEX3_14:SERG3] ~g~Bombe posée! Plus qu'une cible! Va chercher une autre bombe! [TEX3_15:SERG3] ~r~Compte à rebours enclenché! ~g~Tu dois poser les ~w~4 bombes ~g~avant la fin du temps imparti! [TEX3_37:SERG3] Pousse le ~h~joystick analogique droit vers le haut ~w~pour vitesse la vitesse du rotor et ainsi faire ~h~ monter l'hélicoptère. [TEX3_38:SERG3] { reVC update } {Pousse le ~h~~k~~VEHICLE_ACCELERATE~ ~w~pour augmenter la réduire du rotor et ainsi faire ~h~ descendre l'hélicoptère.} Pousse le ~h~joystick analogique droit vers la bas ~w~pour augmenter la réduire du rotor et ainsi faire ~h~ descendre l'hélicoptère. [TEX3_39:SERG3] ~g~Appuie sur la touche ~h~~k~~VEHICLE_HANDBRAKE~ ~g~pour larguer une bombe. [TEX3_40:SERG3] Appuie sur la touche ~h~~k~~VEHICLE_HANDBRAKE~ ~w~pour larguer une bombe. [TEX3_23:SERG3] Appuyer sur les touches ~h~~k~~VEHICLE_TURRETUP~~w~ et ~h~~k~~VEHICLE_TURRETDOWN~~w~ pour orienter l'hélicoptère dans la direction souhaitée. {=================================== MISSION TABLE TAXI1 ===================================} [FARES:TAXI1] CLIENTS: [TAXI1:TAXI1] Cherche une course. [TSCORE2:TAXI1] ~1~$ [IN_ROW:TAXI1] ~1~ DE SUITE! Bonus : ~1~$ [TAXI3:TAXI1] ~r~Ton client est parti terrorisé! [TAXI7:TAXI1] ~r~Ta voiture est endommagée. Fais-la réparer. [TAXI4:TAXI1] La course est finie! [TAXI5:TAXI1] BONUS DE VITESSE! [TAXI6:TAXI1] La mission taxi est finie [TAXIH1:TAXI1] Arrête-toi près d'un piéton mis en évidence pour qu'il monte, puis conduis-le à destination avant la fin du temps imparti. [FARE1:TAXI1] ~g~Destination ~w~'le Pole Position Club' ~g~à Ocean Beach. [FARE3:TAXI1] ~g~Destination ~w~'le Marina' ~g~à Ocean Beach. [FARE4:TAXI1] ~g~Destination ~w~'Ammu-Nation' ~g~à Ocean Beach. [FARE5:TAXI1] ~g~Destination ~w~'la quincaillerie' ~g~à Washington Beach. [FARE6:TAXI1] ~g~Destination ~w~'le centre commercial de North Point' ~g~à Vice Point. [MFARE1:TAXI1] ~g~Destination ~w~'Ammu-Nation' ~g~dans le Centre. [MFARE2:TAXI1] ~g~Destination ~w~'le Terminal' ~g~à l'aéroport International Escobar. [WFARE3:TAXI1] ~g~Destination ~w~'Sunshine Autos' ~g~dans Little Havana. [WFARE4:TAXI1] ~g~Destination ~w~'les taxis Kaufman' ~g~dans Little Haiti. [WFARE5:TAXI1] ~g~Destination ~w~'la quincaillerie' ~g~dans Little Havana. [WFARE6:TAXI1] ~g~Destination ~w~'Howlin Petes Bike Emporium' ~g~dans le Centre. [FARE7:TAXI1] ~g~Destination ~w~'Les bijoutiers' ~g~à Vice Point. [FARE8:TAXI1] ~g~Destination ~w~'la plage' ~g~à Ocean Beach. [FARE9:TAXI1] ~g~Destination ~w~'la plage' ~g~à Washington Beach. [FARE10:TAXI1] ~g~Destination ~w~'la plage' ~g~à Vice Point. [FARE11:TAXI1] ~g~Destination ~w~'l'hôpital' ~g~à Ocean Beach. [FARE12:TAXI1] ~g~Destination ~w~'l'hôpital' ~g~à Vice Point. [FARE13:TAXI1] ~g~Destination ~w~'le commissariat de police' ~g~à Washington Beach. [FARE14:TAXI1] ~g~Destination ~w~'le commissariat de police' ~g~à Vice Point. [FARE15:TAXI1] ~g~Destination ~w~'le restaurant de pizza' ~g~à Vice Point. [WFARE7:TAXI1] ~g~Destination ~w~'le commissariat de police' ~g~à Little Havana. [WFARE8:TAXI1] ~g~Destination ~w~'le commissariat de police' ~g~à Downtown. [WFARE9:TAXI1] ~g~Destination ~w~'l'hôpital' ~g~à Downtown. [WFARE10:TAXI1] ~g~Destination ~w~'l'hôpital' ~g~à Little Havana. [WFARE11:TAXI1] ~g~Destination ~w~'le stade' ~g~à Downtown. [WFARE12:TAXI1] ~g~Destination ~w~'le restaurant de pizza' ~g~à Little Haiti. [WFARE13:TAXI1] ~g~Destination ~w~'le restaurant de pizza' ~g~à Downtown. [WFARE14:TAXI1] ~g~Destination ~w~'les docks' ~g~à Viceport. [WFARE15:TAXI1] ~g~Destination ~w~'la pharmacie' ~g~à Little Haiti. [FARE2:TAXI1] ~g~Destination ~w~'le Malibu club' ~g~à Vice Point. {=================================== MISSION TABLE TAXICUT ===================================} [TAXC_A:TAXICUT] J'imagine que t'es le nouveau proprio. [TAXC_B:TAXICUT] D'où tu sors? La Mafia? Le Cartel? T'as pas l'air mexicain... [TAXC_C:TAXICUT] Quoi qu'il en soit, tu ferais mieux de continuer avec le barratin comme quoi les choses vont changer, [TAXC_D:TAXICUT] peut-être même menacer des conducteurs... [TAXC_E:TAXICUT] Vas-y mollo avec Ted là-bas, on vient juste de lui soigner son hernie. [TAXC_F:TAXICUT] Eh bien, ouais. Les choses vont changer dans le coin, madame. [TAXC_G:TAXICUT] Arrête tes conneries, petit. Vaut mieux que tu me laisses faire. [TAXC_H:TAXICUT] Ca fait des années que je suis dans ce genre de biz. [TAXC_I:TAXICUT] Ecoutez tous. [TAXC_J:TAXICUT] On a une nouvelle direction et les choses vont encore changer dans le coin. [TAXC_K:TAXICUT] Notre nouvelle direction, la... [TAXC_L:TAXICUT] Tu fais partie de quel gang? [TAXC_M:TAXICUT] En fait, je fais pas partie d'un gang... [TAXC_N:TAXICUT] C'est quoi ton foutu nom, petit? [TAXC_O:TAXICUT] Vercetti, Tommy Vercetti. [TAXC_P:TAXICUT] Notre nouvelle direction, le gang Vercetti, [TAXC_Q:TAXICUT] va veiller à nous éviter les emmerdes. [TAXC_R:TAXICUT] Capiche? Terminé! [TAXC_S:TAXICUT] Qu'est-ce que t'as pensé du 'capiche'? Moi, j'ai bien aimé le 'capiche'. [TAXC_T:TAXICUT] Bon, alors c'est comme ça que ça marchait avant [TAXC_U:TAXICUT] et on va continuer à faire tourner l'entreprise comme ça. [TAXC_V:TAXICUT] Si on a des ennuis avec une compagnie concurrente, tu leur flanques une dérouillée. [TAXC_W:TAXICUT] Puis ils nous flanquent une dérouillée. [TAXC_X:TAXICUT] puis tu leur flanques une dérouillée, [TAXC_Y:TAXICUT] et cetera et cetera. T'as pigé? [TAXC_Z:TAXICUT] Euh, ouais, je crois... [TAXC_A1:TAXICUT] Prends simplement un taxi au garage si tu te sens de bosser. {=================================== MISSION TABLE TAXIWA1 ===================================} [OUTTIME:TAXIWA1] ~r~T'es trop lent, mec, trop lent! [TAX1_1:TAXIWA1] Ok, on a un client rupin qui a besoin d'être ramassé à Starfish island, ça intéresse quelqu'un? [TAX1_2:TAXIWA1] Ici, Tommy, je prends! [TAX1_3:TAXIWA1] C'est mon client, dégage! [TAX1_4:TAXIWA1] Allez, vite, monte! [TAX1_5:TAXIWA1] Ok, ok, mais ne me faites pas de mal! [TAXW1_1:TAXIWA1] ~g~Va prendre le V.I.P. sur Starfish Island. [TAXW1_2:TAXIWA1] ~g~Ramène le V.I.P.! Bousille l'autre bagnole! [TAXW1_3:TAXIWA1] ~r~Le V.I.P. est mort! [TAXW1_4:TAXIWA1] ~r~Le V.I.P. a été déposé! [TAXW1_6:TAXIWA1] ~g~Emmène le V.I.P. à l'aéroport! {=================================== MISSION TABLE TAXIWA2 ===================================} [TAX2_1:TAXIWA2] Appel à toutes les voitures, on perd des clients dans toute la ville! Qu'est-ce que vous foutez, les gars? [TAX2_2:TAXIWA2] Les taxis VC nous les prennent! Ils ont trop de voitures, on peut pas rivaliser! [TAX2_3:TAXIWA2] Vercetti, si t'es en ville en train d'écouter, va falloir mettre quelques taxis VC hors circuit ou c'est la faillite! [TAXW2_1:TAXIWA2] ~g~Détruis 3 taxis concurrents! {=================================== MISSION TABLE TAXIWA3 ===================================} [TAX3_1:TAXIWA3] Voiture 13, on a une demoiselle Cortez à prendre dans le centre, elle vous a spécialement demandé. [TAX3_2:TAXIWA3] Ok, je prends. Voiture 13 en course! [TAX3_3:TAXIWA3] Hmmmm, aucun signe de Mercedes... [TAXW3_3:TAXIWA3] ~g~Bousille le taxi leader! [TAXW3_2:TAXIWA3] ~g~Reste vivant jusqu'à la fin du temps imparti. [TAX_AS1:TAXIWA3] COMPAGNIE DE TAXIS ACQUISE [TAX_AS2:TAXIWA3] ~G~Les taxis Kaufman génère dorénavant un revenu de $~1~ maximum. Pense à récupérer le fric régulièrement. [TAX3_4:TAXIWA3] L'heure est venue pour l'ange gardien des taxis Kaufman de froisser de la tôle! [TAX3_5:TAXIWA3] Hé mec, j'vais te bousiller ta caisse! { reVC updates } { new languages } [FEL_JAP] JAPONAIS [FEL_POL] POLONAIS [FEL_RUS] RUSSE { new display menus } [FET_GFX] GRAPHICS SETUP [FED_MIP] MIP MAPPING [FED_AAS] ANTI ALIASING [FED_FIL] TEXTURE FILTERING [FED_BIL] BILINEAR [FED_TRL] TRILINEAR [FED_WND] WINDOWED [FED_FLS] FULLSCREEN [FEM_CSB] CUTSCENE BORDERS [FEM_SCF] SCREEN FORMAT [FEM_ISL] MAP MEMORY USAGE [FEM_LOW] LOW [FEM_MED] MEDIUM [FEM_HIG] HIGH [FEM_2PR] PS2 ALPHA TEST [FEC_FRC] FREE CAM { Linux joy detection } [FEC_JOD] DETECT JOYSTICK [FEC_JPR] Press any key on the joystick of your choice that you want to use on the game, and it will be selected. [FEC_JDE] Detected joystick { mission restart } [FET_RMS] REJOUER MISSION [FESZ_RM] REJOUER? [FED_VPL] VEHICLE PIPELINE [FED_PRM] PED RIM LIGHT [FED_RGL] ROAD GLOSS [FED_CLF] COLOUR FILTER [FED_WLM] WORLD LIGHTMAPS [FED_MBL] MOTION BLUR [FEM_SIM] SIMPLE [FEM_NRM] NORMAL [FEM_MOB] MOBILE [FED_MFX] MATFX [FED_NEO] NEO [FEM_PS2] PS2 [FEM_XBX] XBOX [FEC_IVP] INVERT PAD VERTICALLY [FEM_NON] NONE [FEC_DS2] DUALSHOCK 2 [FEC_DS3] DUALSHOCK 3 [FEC_DS4] DUALSHOCK 4 [FEC_360] XBOX 360 CONTROLLER [FEC_ONE] XBOX ONE CONTROLLER [FEC_TYP] GAMEPAD TYPE [FET_AGS] GAMEPAD SETTINGS [FEM_AUT] { aspect ratio related } AUTO [DUMMY] THIS LABEL NEEDS TO BE HERE !!! AS THE LAST LABEL DOES NOT GET COMPILED ================================================ FILE: utils/gxt/german.txt ================================================ { New strings are at the bottom of file. Do not change the order of strings. You can fix the typos of existing translation but please refrain from unnecessary edits like rephasing because you think it suits better for your taste. } [RAMPAGE] AMOKLAUF!! [RAMP_F] AMOKLAUF FEHLGESCHLAGEN!! [RAMP_P] AMOKLAUF BESTANDEN!! [RAMP_A] ALLE AMOKLÃUFE BESTANDEN!! [PAGE_01] Dein Job: ~1~ Gang-Mitglieder in 2 Minuten! [PAGE_02] Zerstöre ~1~ Fahrzeuge in 2 Minuten! [PAGE_03] Erledige ~1~ Gang-Mitglieder in 2 Minuten im Vorbeifahren! [PAGE_04] Überfahre ~1~ Gang-Mitglieder in 2 Minuten! [PAGE_05] Dein Job: ~1~ Gang-Mitglieder in 2 Minuten! [SENTXS] Sentinel XS [MAP_LEG] Legende [VCNMAV] VCN Maverick [LG_01] Position des Spielers [LG_02] Avery Carrington [LG_03] Biker-Kontaktpunkt [LG_04] Colonel Cortez [LG_05] Ricardo Diaz [LG_06] Kent Paul [LG_07] Rechtsanwalt [LG_08] Phil Cassidy [LG_09] Bootswerft [LG_10] Malibu Club [LG_11] Kubaner [LG_12] Filmstudio [LG_13] AmmuNation [LG_14] Haitianer [LG_15] Eisenwarenladen [LG_16] Versteck [LG_17] Eiscreme [LG_18] Kaufman-Taxis [LG_19] Love Fist [LG_20] Druckerei [LG_21] Immobilie [LG_22] Pay 'n' Spray [LG_23] Bekleidungsgeschãft [LG_24] Tommys Villa [LG_25] Telefon [LG_26] Radiosender Wildstyle [LG_27] Radiosender Flash FM [LG_28] Radiosender KChat [LG_29] Radiosender Fever 105 [LG_30] Radiosender VRock [LG_31] Polizeifunk-Zentrale [LG_32] Radiosender Espantoso [LG_33] Radiosender Emotion 98.3 [LG_34] Radiosender Wave 103 [LG_36] Sun Yard [LG_37] Stripper-Bar [MAP_YAH] DU BIST HIER [TAXSHRT] ~g~Du kannst dieses Kaufman-Taxi nehmen, statt selbst Auto zu fahren. Das kostet dich $9. [MOB_09D] Vielleicht hab ich Leo ja erledigt und mir sein Handy genommen. Hast du schon mal daran gedacht, du Penner? [FE_MLG] KARTENLEGENDE [FED_RDR] RADAR-MODUS [FED_HUD] HUD-MODUS [FED_RDL] GROSS [FED_RDB] NUR SYMBOLE [FED_HUF] INFOS EIN- & AUSBLENDEN [FEST_HV] Höchstes Bürgerwehr-Missions-Level [BRIBE1] Du hast soeben Polizei-Bestechungsgeld aufgenommen, dein Fahndungslevel verringert sich damit um einen Stern. [CLOHELP] Saubere Klamotten!! [SUNSHIN] Sunshine Autos [CHERRYP] Cherry Popper Eiscreme [KAUFCAB] Kaufman-Taxis [BOATYAR] Die Bootswerft [WANT_L] Dein Fahndungslevel ist bis auf weiteres aufgehoben. Solltest du ein Verbrechen begehen, wãhrend die Sterne blinken, wird dein volles Fahndungslevel wieder aktiv. [PICK1] Kugelsichere Weste im Ocean View Hotel angeliefert! [HOTRNG] HOTRING [BLODRNG] CHAOS-DERBY [DIRTRNG] DIRTRING [FEC_ABR] Beschleunigen, Bremsen oder Zurücksetzen [FEI_BTU] ; = - [FEI_SCR] Scrollen [SKUMBUY] Skumole Shack gekauft: $ ~1~ [SKUM_L] Drücke die ~h~L1-Taste~w~, um Skumole Shack zu kaufen. Preis: $~1~ [SKUM_T] Drücke die ~t~"-Taste~w~, um Skumole Shack zu kaufen. Preis: $~1~ [SKUM_C] Drücke die ~o~|-Taste~w~, um Skumole Shack zu kaufen. Preis: $~1~ [LG_35] Ziel [IN_VEH] ~g~Hey! Zurück ins Auto!! [HEY] ~g~Keine Alleingãnge. Halt die Gang beisammen! [HELP3] Du kannst nur kurze Zeit sprinten, ohne müde zu werden. [HELP4_D] Drücke den~h~ rechten Analog-Stick nach oben, um zu ~h~beschleunigen. [HELP5_D] Zieh den ~h~rechten Analog-Stick~w~ zurück, um zu ~h~bremsen~w~, oder um ~h~zurückzusetzen~w~, wenn das Fahrzeug steht. [HELP7_A] Halte die~h~ ~k~~PED_LOCK_TARGET~ ~w~gedrückt, um mit dem Prãzisionsgewehr zu zielen. [HELP7_D] Halte die~h~ ~k~~PED_LOCK_TARGET~ ~w~gedrückt, um mit dem Prãzisionsgewehr zu zielen. [HELP10] Dieser Stern zeigt an, dass du von der Polizei gesucht wirst. [HELP11] Je mehr Sterne, desto dringender wirst du gesucht. [HELP13] Manchmal musst du vielleicht Wege finden, die das Radar nicht zeigt. [TIMER] Diese Mission hat ein Zeitlimit. Du musst sie beendet haben, bevor die Zeit um ist. [HORN] ~g~Drück auf die Hupe. [NOMONEY] ~g~Du brauchst mehr Cash! [REWARD] BELOHNUNG $~1~ [M_FAIL] MISSION FEHLGESCHLAGEN! [M_PASS] MISSION ERFÜLLT! $~1~ [DEAD] AUSSER GEFECHT! [BUSTED] VERHAFTET! [WEATHE1] WETTER AUF SONNIG UMSTELLEN [WEATHE2] WETTER AUF SEHR SONNIG UMSTELLEN [WEATHE3] WETTER AUF BEWÖLKT UMSTELLEN [WEATHE4] WETTER AUF REGNERISCH UMSTELLEN [WEATHE5] WETTER AUF NEBLIG UMSTELLEN [WEATHE6] WETTER NORMAL [NUMBER] ~1~ [LOADCAR] LADE FAHRZEUG... (ABBRECHEN MIT L1) [CARSOFF] Deaktivierte Fahrzeuge. [CARS_ON] Aktivierte Fahrzeuge. [TEXTXYZ] Schreibe Koordinaten in Datei... [CHEATON] Cheat Modus AN [CHEATOF] Cheat Modus AUS [IMPORT1] Geh nach draußen und warte auf dein Fahrzeug. [PAGEB11] Flammenwerfer in Versteck angeliefert. [WANT_A] Verhaftet wirst du nur, wenn die Polizei nach dir ~h~fahndet. [WANT_B] Dein ~h~Fahndungslevel~w~ wird durch die Reihe von Sternen oben rechts auf dem Bildschirm dargestellt. [WANT_C] Du hast jetzt einen ~h~Fahndungslevel~w~ von eins... [WANT_D] zwei... [WANT_E] drei... [WANT_F] Steigt dein ~h~Fahndungslevel~w~, wirst du von besser ausgebildeten Polizisten gejagt. [WANT_G] Wirst du ~h~verhaftet~w~, wirst du zum nãchsten Polizeirevier gebracht. [WANT_H] Die Cops werden dir alle Waffen abnehmen und kassieren ein wenig Bestechungsgeld von dir. [WANT_I] Wenn dir das auf einer Mission passiert, ist die Mission fehlgeschlagen. [WANT_J] Im Verlauf des Spiels wirst du Möglichkeiten entdecken, deinen Fahndungslevel zu reduzieren. [WANT_K] Wenn du in einem Wagen sitzt, werden ~h~LACKIEREREIEN~w~ den Fahndungslevel ~h~annullieren. [HEAL_B] Wenn du ~h~'außer Gefecht'~w~ bist, wirst du zur nãchsten Klinik gebracht. [HEAL_C] Du verlierst alle Waffen, und die Ãrzte knöpfen dir ein wenig Cash für die Behandlung ab. [HEAL_E] Je lãnger du spielst, desto mehr Wege wirst du finden, dich selbst zu verarzten oder zu schützen. [SAVE1] Stell dich in die Markierung. So kannst du dein ~h~Spiel speichern~w~. Wãhrend einer Mission kannst du nicht speichern. [SAVE2] Jedes Fahrzeug, das in dieser Garage abgestellt wird, wird für dich aufbewahrt, wenn das Spiel gespeichert wird. [AMMU] Betritt den Ammu-Nation, um eine Waffe zu kaufen. [R_TIME] ZEIT: [PROP_1] Du hast nicht genug Cash für dieses Objekt. [PROP_2] Wãhrend einer Mission kannst du keine Objekte kaufen. [IND_ZON] Vice City Beach [COM_ZON] Vice City Mainland [BEACH1] Ocean Beach [BEACH2] Washington Beach [BEACH3] Vice Point [GOLFC] Leaf Links Golfclub [STARI] Starfish Island [DOCKS] Viceport [HAVANA] Little Havana [HAITI] Little Haiti [PORNI] Prawn Island [DTOWN] Downtown [VICE_C] Vice City [A_PORT] Escobar Inter. Airport [JUNKY] Schrottplatz [PISTOL] Pistole [PYTHON] .357 [UZI] Uz-1 [TEC9] Tec 9 [M4] M4 [INGRAM] Mac [MP5] MP [RUGER] Kruger [SNIPE] Prãzisionsgewehr [GRENADE] Granaten [SHOTGN1] Schrotflinte [SHOTGN2] S.P.A.S. 12 [SHOTGN3] Abgesãgte Schrotflinte [ARMOUR] Kugelsichere Weste [LASER] .308 Prãzisionsgewehr [BASEBAT] Baseballschlãger [HAMMER] Hammer [SCREWD] Schraubenzieher [CLEVER] Fleischerbeil [MACHETE] Machete [KNIFE] Messer [KATANA] Katana [CHAINSA] Kettensãge [G_COST] $~1~ [CAR_1] Krankenwagen [MALIBU] Malibu Club [MANSION] Diaz' Haus [TMANS] Vercetti Estate [STRIP] Pole Position Club [MALL1] North Point Einkaufszentrum [BANKINT] El Banco Corrupto Grande [RANGE] Schießstand [POL_HQ] Vice City Polizeihauptquartier [INT_B] Ein alter Freund [INTB_1] ~g~Begib dich in die Anwaltskanzlei. [LAW_1] Die Party [LAW_2] Dunkle Gassen [LAW_3] Die Geschworenen [LAW_4] Aufruhr [COL_1] Der Verrãter [COL_2] Kugelhagel im Einkaufszentrum [COL_3] Die Schutzengel [COL_4] Zu Befehl, Sir! [COL_5] Alle Mann an Deck! [COK_1] Die Jagd [COK_2] Phnom Penh '86 [COK_3] Das schnellste Boot [COK_4] Angebot & Nachfrage [KENT_1] Die Befreiungsaktion [ASS_1] Pizza Mortale [BUD_1] Fette Beute [BUD_2] Zoff in der Bar [BUD_3] Cop-Land [CAP_1] Der Eintreiber [FIN_1] Freunde und andere Feinde [BANK_1] Kein Entkommen? [BANK_2] Der Scharfschütze [BANK_3] Der Fahrer [BANK_4] Der Coup [CNT_1] Der Singvogel [CNT_2] Der Kurier [PORN_1] Alle meine Pferdchen [PORN_2] Der Dildo-Jet [PORN_3] Ein Mann namens Martha [PORN_4] Heiße Lightshow [TAX_1] Kaufman-Taxis [TAXI_1] V.I.P [TAXI_2] Konkurrentenjagd [TAXI_3] Das Taxi-Inferno [ICE_1] Eiscreme und andere Leckereien [TEX_1] Schlagende Argumente [TEX_2] Das Begrãbnis [TEX_3] Schutt und Asche [PHIL_1] Der Waffenschieber [PHIL_2] TNT-Whiskey [BIKE_1] Wheels of Steel [BIKE_2] Chaos-City [BIKE_3] Big Bakers Bike [ROCK_1] Love Juice [ROCK_2] Der Psychokiller [ROCK_3] Die PR-Tour [ROCK_4] Love Fist! [HAT_1] Mysteriöses Pulver [HAT_2] Fliegende Bomben [HAT_3] Schmutzige Methoden [CUB_1] Stunt-Boot-Action [CUB_2] Kanonenfutter [CUB_3] Die Seeschlacht [CUB_4] Trojanisches Voodoo [JOB_1] Der Pizza-Lieferant [JOB_2] Ein bedauerlicher Unfall [JOB_3] Die rasende Schrottfabrik [JOB_4] Trouble am Check-In [JOB_5] Gefeuert! [ANSWER] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Anruf auf deinem Handy entgegenzunehmen. [MOB_01A] Hey, mein Alter! Paul hier. Ich hãtte da vielleicht was für dich, aber das müssen wir unter vier Augen besprechen. [MOB_01B] Ich bin im Malibu und lass es mir gutgehen. [MOB_01C] Ich denke, ich hab einen gut bei dir, wenn du das hörst, Kumpel. Bis gleich. [MOB_02A] Hey! Hallo, Tommy? Tommy! [MOB_02B] In der Druckerei ist irgendwas im Busch. Fahr mal rüber und kümmere dich darum. [MOB_02C] Es scheint ziemlichen Ãrger zu geben. Ich muss Schluss machen. [MOB_03A] Mr. Vercetti? Ich habe hier einen unterschriebenen Wisch, [MOB_03B] der besagt, dass Sie alle Schulden von 'BJ's Autos' übernehmen. [MOB_03C] Nach BJ's plötzlichem Verschwinden hab ich keine andere Wahl, [MOB_03D] als Sie für seine Verbindlichkeiten zur Rechenschaft zu ziehen. [MOB_03E] Bis Sie die volle Summe beglichen haben, [MOB_03F] sollte Ihnen klar sein, dass Vice City ein heißes Pflaster für Sie ist. [MOB_04A] Wie geht's, mein Alter? [MOB_04B] Ich hab vergessen, dir zu sagen, dass wir für das Konzert noch ein paar Ordner brauchen. [MOB_04C] Da gibt's so 'ne Biker-Gang, die wãr super für die Publicity. [MOB_04D] Wenn du mir das arrangierst, besorg ich dir 'n VIP-Pass für den Abend. Ok? [MOB_05A] Gut gemacht, Tommy. Schön, den Ofen wiederzuhaben. [MOB_05B] Sag Mr. Kent Paul, dass er seine Ordner für das Konzert kriegt. [MOB_05C] Du hast mein Wort darauf. [MOB_05D] Halt die Ohren steif. [MOB_06A] Tommy, man hört so allerlei über dich, mein Junge. [MOB_06B] Wenn du willst, kocht Tante Poulet dir ein leckeres Süppchen, dann kannst du mal abschalten, hm? [MOB_06C] Komm doch die Tage mal bei mir vorbei, ok, Tommy? [MOB_08A] Tommy, ich dachte mir, du könntest meinen Rat als Geschãftsmann brauchen. [MOB_08B] Wenn du ein Unternehmen am Laufen hast, musst du einmal pro Woche die Einnahmen kassieren gehen. [MOB_08C] Sonst werden deine Leute übermütig und versuchen, in die eigene Tasche zu wirtschaften. Ok? [MOB_08D] Hey, ich weiß selbst, wie man so was macht, Ken, ok? [MOB_08E] Ok, ok. Ich weiß ja, dass du Bescheid weißt. [MOB_08F] Ich meinte ja nur, damit du weißt, dass ich im Zweifelsfall auch Bescheid weiß. [MOB_08G] Nur für alle Fãlle, mein Alter! [MOB_08H] Wenn du meinst, Ken... [MOB_09A] Hey, Leo! Ich hab Arbeit für dich! [MOB_09B] Hier ist nicht Leo. [MOB_09C] Hey, wenn Leo erfãhrt, dass du mit seinem Handy telefonierst, bist du fãllig. [MOB_09E] Du hast Leo erledigt? Du musst Mumm haben - willst du für mich arbeiten? [MOB_09F] Komm ins Café von meinem Vater in Little Havana, dann können wir reden. [MOB_10A] Tommy! Du musst mir einen Gefallen tun. [MOB_10B] Steve! Was machen die Dreharbeiten? [MOB_10C] Gut. Ich- ãh, WIR brauchen noch eine Autoverfolgungsjagd, aber unser Budget ist knapp. [MOB_10D] Ich hab in der Stadt verteilt ein paar Karren stehen. Du weißt, was zu tun ist. [MOB_10E] Ok, Steve, ich halt die Augen offen. Bis dann. [MOB_11A] Hallo, Söhnchen. Ich wollte dir kurz 'nen kleinen Tipp geben. [MOB_11B] Hi, Avery. Worum geht's denn? [MOB_11C] Man kann in dieser Stadt viel Geld machen, wenn man die richtigen Immobilien hat. Klingelt's? [MOB_11D] Ich denke schon. [MOB_11E] Also, halt die Augen offen, dann könnte sich dir die ideale Gelegenheit bieten. Bis bald. [MOB_11F] Ciao, Avery. [MOB12_A] Hey, Tommy. Avery hier. Hör mal, ich hab gerade alle Hãnde voll zu tun, [MOB12_B] und ein Bevollmãchtigter von mir müsste zu den Gator Keys eskortiert werden. [MOB12_C] Ich will da ein Stück Land kaufen und schick ihn hin, um den Deal abzuschließen. [MOB12_D] Könntest du dafür sorgen, dass er gut dort ankommt? [MOB12_E] Mach ich doch glatt, Avery. Wo soll ich ihn abholen? [MOB12_F] Er hat gerade noch an der Baustelle zu tun. Hol ihn doch am besten dort ab. [MOB12_G] Kein Problem. Bis dann, Avery. [MOB13_A] Vercetti? VERCETTI!! Verdammt, Mann, Sie müssen mir helfen! [MOB13_B] Mr. Moffat? Wie geht's der Familie? [MOB13_C] Zur Hölle mit Ihnen, hören Sie! [MOB13_D] Tja, war nett, mit Ihnen zu plaudern... [MOB13_E] WARTEN SIE! Vercetti - Tommy, kann ich Tommy zu Ihnen sagen? [MOB13_F] Wir sind beide Geschãftsleute. Sie erkennen doch ein gutes Geschãft sofort, oder? [MOB13_G] Ich hab keine Zeit für Palaver. Kommen Sie zum Punkt. [MOB13_H] GELD. Geld ist der Punkt, verdammt! [MOB13_I] Ich bin den Kerlen nochmal entwischt, aber sie sitzen mir im Nacken. Die machen sich einen Spaß daraus! [MOB13_J] Ich bin in einer Telefonzelle, irgendwo in diesem verdammten Loch. [MOB13_K] Holen Sie mich hier raus, bevor sie mich schnappen und-... oh Gott... [MOB13_L] Ich bin leider ausgebucht bis-... [MOB13_M] Nein! Verarschen Sie mich nicht, haben Sie Erbarmen! Kein Mensch hat das verdient! [MOB13_N] Ich flehe Sie an, Tommy, auf den Knien flehe ich Sie an! Bitte... [MOB13_O] Naja, ich könnte 'nen kurzen Abstecher machen. Vielleicht finde ich Sie... [MOB13_P] Oh Gott, sie kommen! Um Himmels Willen, beeilen Sie sich! [MOB_14A] Hey, Tommy, hör zu, das wird dir gefallen. [MOB_14B] Ein Vögelchen hat mir gesteckt, dass das Vice City-Spezialkommando in einem renommierten Finanzinstitut ein Schließfach hat. [MOB_14C] Da sind die Schmiergelder drin, die sie über die Jahre kassiert haben. [MOB_14D] So 'ne Art Altersversorgung für die ganze Mannschaft. [MOB_14E] Sollte dir diese Info je helfen, dir was von dem Kuchen anzueignen, [MOB_14F] würdest du dich sicher verpflichtet fühlen, mir 'n Stück davon abzugeben? [MOB_14G] Ich werd dich nicht vergessen. Danke, Kent. [MOB_14H] 'Paul'heiße ich, Scherzkeks. Ich KOMME aus Kent, Nãhe London. [MOB_14I] Mein geographisches Wissen über England ist eben nicht mehr, was es war. [MOB15_A] Tommy, Alter. Hier ist Paul aus Kent. [MOB15_B] Unten im Malibu, da sind ein paar Keulen, die haben ein Auge auf dich geworfen. [MOB15_C] Ich verstehe nur Bahnhof. [MOB15_D] Hasen. Miezen. Na, Puppen, eben. Coole Babes, keine Professionellen, oder so. [MOB15_E] Du solltest mal kommen und sie dir ansehen. [MOB16_A] Tommy, hier Paul. Wie geht's, mein Freund? [MOB16_B] Was willst du, Paul? Ich brauch keine getürkten Designer-Klamotten. [MOB16_C] Sehr witzig. Du weißt, dass ich mit getürkter Ware nichts am Hut habe. [MOB16_D] Wollte nur hören, ob ich nicht 'ne Rolle in einem von deinen Filmen kriegen könnte. [MOB16_E] In England habe ich viel einschlãgiges Zeug gedreht. [MOB16_F] Ich hab mehr zu bieten als du, mein Alter. [MOB16_G] Paul, danke für das Angebot. Ich komm auf dich zurück. [MOB16_H] Lass mich nicht hãngen. Denk dran, was ich alles für dich getan habe. [MOB16_I] Das versuch ich ja grade zu vergessen. [MOB17_A] Tommy Vercetti. Wie geht's, großer Meister? [MOB17_B] Man hört so einiges über dich. Bist jetzt 'ne große Nummer in der Stadt, hã? [MOB17_C] Paul, du bist betrunken. [MOB17_D] Nein, du Trottel, ich bin nicht betrunken. [MOB17_E] Hab mir nur ein paar Ladungen Stoff gegeben, war seit ein paar Tagen nicht im Bett. [MOB17_F] Und du brauchst mich nicht dumm anzureden. [MOB17_G] Ich bin nicht irgendwer. Wer hat dir denn in dieser Stadt den Weg geebnet? Ich! [MOB17_H] Tatsãchlich? [MOB17_I] Tatsãchlich! [MOB17_J] Paul, reg dich ab. Ich hatte viel zu tun. Sei kein Idiot. [MOB17_K] Ich bin kein Idiot. Das haben sie schon im Jugendknast gesagt. [MOB17_L] Wenn du Ãrger haben willst, Freundchen, den kannst du haben! [MOB17_M] Tommy, bitte! Du warst meine große Hoffnung. Bitte, mach dich nicht lustig über mich! [MOB17_N] Paul, schlaf mal 'ne Runde. Im Ernst. [MOB18_A] Tommy, hier Paul. Wie geht's, Alter? Hey, ich dachte mir, das musst du hören... [MOB18_B] Echt der Hammer. Du glaubst nicht, was mir für 'ne Puppe über den Weg gelaufen ist. [MOB18_C] 'ne Bordsteinschwalbe, oder sowas. Unten in Little Havana. [MOB18_D] Sagt, sie heißt Mercedes oder so ãhnlich. Wahnsinn, Alter. Die Puppe musst du dir geben. [MOB18_E] Da würde 'nen Toter Hormonkoller kriegen. Sie sagt, ich wãr der beste, den sie je hatte. [MOB18_F] Halt die Augen nach ihr offen. Bis dann. [MOB19_A] Tommy, hier KP - Kent Paul. Ich hab lãuten hören, dass jemand dich leimen will. [MOB19_B] Also, sei wachsam, mein Freund. Und kein Sterbenswörtchen, dass du das von mir weißt. [MOB_20A] Hallo, Tommy. Hier Paul. Ich höre, dass du ein paar Leuten auf den Schlips getreten bist. [MOB_20B] Irgendjemand sieht es anscheinend nicht gern, dass du auf einmal den großen Zampano spielst. [MOB_20C] Also, sag nicht, ich hãtte dich nicht gewarnt. Es rãcht sich, wenn man's übertreibt. [MOB_20D] Jedenfalls ist angeblich ein Kopfgeld auf dich ausgesetzt und es ist schon jemand hinter dir her. [MOB_20E] Also pass auf dich auf. Und vergiss mich nicht ganz. [MOB71_A] Tommy, Thomas, hier Cortez. Wie geht's? [MOB71_B] Es bleibt spannend. Und selbst? [MOB71_C] Ein ewiger Kampf, Tommy. Entschuldigen Sie die schlechte Verbindung, wir hatten wieder einen Putschversuch. [MOB71_D] Es gibt keine anspruchsvollere Geliebte als das Volk. [MOB71_E] Seit ich aus Vice City zurück bin, gab's drei Revolutionen und vier Staatsstreiche. [MOB71_F] Zum Glück bin ich jedesmal befördert worden. [MOB71_G] Ich wollte Sie wegen Mercedes etwas fragen. [MOB71_H] OK. Was ist mit ihr? [MOB71_I] Ich hör Geschichten über sie und weiß nicht, was ich davon halten soll. [MOB71_J] Vielleicht wollen mich alle demütigen. [MOB71_K] Vielleicht wird sie übermütig, seit ich weg musste, sagen Sie mir nur eins, Tommy. Ist das wahr?. [MOB71_L] sagen Sie mir nur eins, Tommy. Ist das wahr? [MOB71_M] Ist was wahr? [MOB71_N] Die Geschichten, die ich höre? Will sie wirklich Anwãltin werden? [MOB71_O] Welche Schande, Tommy. Wir Cortez sind eine stolze Familie und würden nie einer Tochter erlauben, Anwãltin zu werden. [MOB71_P] Sagen Sie mir, dass es nicht wahr ist. Das ertrage ich nicht. [MOB71_Q] Colonel, ich versichere Ihnen, dass Mercedes niemals Anwãltin wird. Keine Angst. [MOB71_R] Danke, Tommy - das wãre zu viel der Schande. Sie ist eine Dame, keine Schmarotzerin. [MOB71_S] Ich weiß, Colonel [MOB71_T] Tommy, Sie müssen jetzt entschuldigen, gerade kommt der neue Innenminister. [MOB71_U] Ich hab seinen Vater vor Jahren bei einem Putschversuch erledigt. Ich muss mich gut mit ihm stellen. Wiederhören. [MOB22_A] Tommy, Sie erweisen sich als sehr nützlich, mein Freund. [MOB22_B] Danke, Cortez. Was ist mit meinem Deal? [MOB22_C] Tommy, ich mühe mich ohne Unterlass, um diesem Sumpf von elenden Lügen und Intrigen auf den Grund zu gehen. [MOB22_D] Sie haben mein Wort darauf. Einstweilen möchte ich [MOB22_E] Ihnen den Dank meines Volkes aussprechen, für das Sie so viel getan haben. [MOB_25A] Tommy, hier Cortez. Die Franzosen machen mir Ãrger. Und wie. [MOB_25B] Verdammte Heuchler! Jahrhundertelang beuten sie arme Lãnder aus und mich schimpfen sie einen Dieb! [MOB_25C] Ich brauche dingend Ihre Hilfe. [MOB_25D] Beeilen Sie sich. Ich brauche Sie. Ich hasse die Franzosen. [MOB_26A] Hallo, Tommy? [MOB_26B] Ja? [MOB_26C] Baker hier. Wollte dir nur sagen, der Gig hat Spaß gemacht. [MOB_26D] Danke, auch im Namen der Gang. Eins sollst du wissen: [MOB_26E] Du hast unsern Respekt. Halt die Nase im Wind, Junge. [MOB_29A] Hallo? Spreche ich mit Mr. Tommy Vercetti? [MOB_29B] Ja. [MOB_29C] Ich hab mir sagen lassen, du wãrst der richtige Mann, wenn man Kroppzeug am Hals hat. [MOB_29D] Vielleicht... [MOB_29E] Tja, ich hab 'ne regelrechte Plage am Hals. Haitianer, überall. [MOB_29F] Mein Name ist Umberto Robina und es wãr mir recht, wenn du baldmöglichst ins Café Robina kãmst. [MOB_29G] Diesmal haben's die Haitianer nãmlich zu weit getrieben. [MOB_29H] Test [MOB_30A] Tommy, hier Umberto Robina. [MOB_30B] Wie lãuft das Café? [MOB_30C] Oh, bestens. Sagenhaft, Tommy, sagenhaft. Keine Memmen, Tommy, nur echte Mãnner. Und wunderschöne Frauen! [MOB_30D] Ich wollte nur sagen, für mich und Paps bist du jetzt einer von uns. Ein Kubaner. [MOB_30E] Du bist ein ganzer Kerl. Du hast Mumm in den Knochen. [MOB_30F] Danke, Umberto. Seit ich aus dem Knast raus bin, hat das keiner zu mir gesagt. Bis dann. [MOB_33A] Tommy, Phil hier. Spar dir die sentimentalen Worte, hör mir einfach zu, ok? [MOB_33B] Also, ich brau hier 'nen extra starken TNT-Whiskey und ich wollte fragen, ob du mal 'nen Schluck probieren willst. [MOB_33C] Im Ernst, Tommy, wenn du 'nen Drink willst, oder 'ne Wand abbeizen musst - das Zeug macht einen Mann aus dir. [MOB_33D] Bei mir hat's jedenfalls gewirkt, auch wenn ich jetzt auf einem Auge nichts mehr sehe. Ich warte auf dich. [MOB_34A] Tommy, war ein Vergnügen, mit dir zu arbeiten. So einen Spaß hatte ich seit Vietnam nicht mehr. [MOB_34B] Also, falls du je irgendwas brauchst, ruf mich an, ok? [MOB_34C] Ich vergesse keinen, mit dem ich in der Schlacht war. [MOB_34D] Und ich kann dir bestimmt irgendwann helfen, ok? [MOB_35A] Tommy, die Wunde verheilt gut. Ist nur komisch: [MOB_35B] da hab ich auf 6 Schlachtfeldern gekãmpft und nie 'n Kratzer abgekriegt und dann das! [MOB_35C] Jetzt bin ich der einarmige Phil. Hab aber 'n gutes Arsenal an Handfeuerwaffen, also bin ich auch mit Arm ab nicht arm dran. [MOB_35D] Also hör auf mit der sentimentalen Scheiße und hol dir 'nen Drink, ok! [MOB_36A] Tommy, hier Phil. Wollte mich bedanken, dass du mich da rausgehauen hast. [MOB_36B] Verdammte Vietnamesen. Wo du hinschaust ein Hinterhalt. [MOB_36C] Die Wunde heilt gut. Und jetzt kassiere ich meine Versehrtenrente endlich völlig zurecht. Danke, Kumpel. [MOB_40A] Hey, Tommy, hier Sonny. Was macht die Sonnenbrãune? [MOB_40B] Ich hab keine Sonnenbrãune. [MOB_40C] Naja, mein Geld hast du jedenfalls auch nicht. Daher frag ich mich, [MOB_40D] was treibst du eigentlich die ganze Zeit, Tommy? Sag doch mal? [MOB_40E] Ich bin auf der Suche nach deinem Geld, Sonny. Keine Sorge. [MOB_40F] Ich mach mir aber Sorgen, Tommy, ich kann nicht anders. [MOB_40G] Ich hab nãmlich anscheinend mit zu viel unzuverlãssigen Menschen zu tun. [MOB_40H] Sei du bitte kein unzuverlãssiger Mensch, Tommy. [MOB_40I] Tu dir und mir einen Gefallen. Ich freu mich, von dir zu hören. [MOB_41A] Tommy, kennst du mich noch? [MOB_41B] Hey, Sonny. [MOB_41C] Ja genau, Sonny. Wir sind doch alte Freunde, [MOB_41D] aber du schreibst nie, rufst nie an. Willst du denn nicht mehr mein Freund sein? [MOB_41E] Ich hab alle Hãnde voll zu tun, die Sache zu regeln. Und du bist auch keine große Hilfe. [MOB_41F] Ach, ich bin also schuld? Ja, ich hab gehört, dass du zu tun hast... [MOB_41G] Musst Drogenbarone erledigen, ihre Geschãfte übernehmen. [MOB_41H] Vergiss mich nicht, Tommy. Ich verspreche dir nãmlich, dass ich dich nicht vergesse... [MOB_42A] Tommy. [MOB_42B] Sonny. [MOB_42C] Anscheinend hörst du neuerdings schlecht, deshalb frag ich dich jetzt nochmal: [MOB_42D] Tommy, wo ist die verdammte Kohle? Wo ist der verdammte Stoff und wo ist mein Anteil an deinem neuen Geschãft? [MOB_42E] Du hãltst mich zum Narren, Tommy, ich find's bloß nicht zum Lachen. [MOB_43A] Tommy, Tommy, Tommy, Sonny hat gerade angerufen, dãmmert dir was? [MOB_43B] Ich weiß nicht, wie das mit dir ist, aber wenn mir jemand droht, dass er meine Familie umbringt, [MOB_43C] dann macht mir das eine Scheißangst. Was gedenkst du zu unternehmen? [MOB_43D] Nur die Ruhe, Ken, alles wird cool. [MOB_43E] Ich BIN ruhig. So ruhig wie man sein kann, wenn man um sein Leben fürchtet! [MOB_43F] Ken, ich muss mich gerade auf was anderes konzentrieren. [MOB_43G] Wir kümmern uns zu gegebener Zeit um Forelli. [MOB_43H] Ich bin ruhig. Hör ich mich nicht ruhig an? Muss die Todesangst sein, die auf die Stimme schlãgt. [MOB45_A] Tommy, wir müssen miteinander reden. [MOB45_B] Wo liegt das Problem, Lance? [MOB45_C] Bei dir, mein Freund: Ich finde, du gibst mir keinen fairen Anteil. [MOB45_D] Noch dazu hast du mich vor den Jungs blamiert. Das kann ich nicht auf mir sitzen lassen. [MOB45_E] Lance, das siehst du falsch. Du hast Fehler gemacht. [MOB45_F] Tommy, ich bin nicht dein Laufbursche. Nicht dein Lakai. [MOB45_G] Lance, alles ist ok, solange du keinen Mist baust. Und sollte ICH Mist bauen, steh ich dafür gerade. [MOB45_H] Tommy, ich hab alles für dich getan und du trittst mich mit Füßen. Mach das doch nicht. [MOB45_I] Lance, ich betrüg dich nicht und falle dir nicht in den Rücken, ok? [MOB45_J] Reiß dich zusammen. Es ist schwer genug, ohne dass du mir die Ohren vollheulst. [MOB45_K] Vertrau mir, hörst du. Hörst du? [MOB45_L] Ja, Tommy, aber ich halt das nicht mehr lange aus. [MOB45_M] Lance, komm mir bloß nicht so, ich warne dich. [MOB45_N] Reg dich ab, mach ein paar Tage frei, dann unterhalten wir uns, ok? [MOB46_A] Tommy - Lance. [MOB46_B] Ja? [MOB46_C] Kein 'Schön, dass du anrufst, Lance!' Kein freundliches Wort? Was soll das? [MOB46_D] Ich habe gerade zu tun, Lance. Was willst du? [MOB46_E] Nichts. Ich wollte dir nur sagen, wir kriegen das hin. [MOB46_F] Du und ich, ohne Probleme. Du verstehst mich? [MOB46_G] Wir müssen es hinkriegen. Sonst sind wir erledigt, Lance. [MOB46_H] Wir stecken schon viel zu tief drin. Aber danke für den Anruf. Du hörst von mir. [MOB_47A] Tommy - Lance. Wir stecken in der Patsche. Du musst sofort kommen. [MOB52_A] Hey, Leo, ich glaube, wir haben einen Kãufer für Diaz' Ware. [MOB52_B] Du musst ihn anrufen und den Deal in die Wege leiten, ok? [MOB52_C] Wo bist du gerade? [MOB52_D] Ist irgendwas, Leo? Du hörst dich so komisch an. [MOB52_E] Sag mir, wo du bist. [MOB52_F] Verdammt, wer ist da dran? Gib mir Leo! [MOB52_G] Leo ist 'ne Weile verreist, ich vertrete ihn. [MOB52_H] Zum Teufel mit dir! [MOB54_A] Hi, Tommy! [MOB54_B] Hi, Mercedes. Wie geht's? [MOB54_C] Ich hab 'ne neue Wohnung in Vice Point. [MOB54_D] Vielleicht kommst du mich ja mal besuchen. [MOB54_E] Klar, gern. Also, bis spãter. [MOB55_A] Tommy, ich bin's. [MOB55_B] Hi, Mercedes. [MOB55_C] Tommy, mir ist so langweilig. Wann hast du denn mal Zeit für mich? [MOB55_D] Was soll denn das heißen? [MOB55_E] Ich weiß, du hast so viel zu tun, musst Leute beseitigen und bestechen. [MOB55_F] Aber ich will mal wieder Spaß haben. Also vergiss mich nicht, hörst du? [MOB56_A] Tommy, es heißt, du hast Ricardo Diaz erledigt. [MOB56_B] In seinem Haus ist ein Feuer ausgebrochen. [MOB56_C] Ich glaube, er hatte ein Acrylhemd an und ist verbrannt. [MOB56_D] Tommy, ich bin so stolz auf dich. Ich wusste, du bist ein echter Mann. [MOB56_E] Und er war ein Mistkerl. Ich bin so stolz, dass du mein Freund bist. [MOB56_F] Ich weiß, du hast zu tun, du musst schließlich die Stadt erobern. [MOB56_G] Aber vergiss mich nicht, hörst du? [MOB57_A] Ich bin's, Mercedes. Ich liebe dich nicht mehr, Tommy. [MOB57_B] Wirklich nicht mehr. Du bist nicht mehr nett zu Mercedes. [MOB57_C] Du behandelst mich nicht mehr wie eine Dame. Du ignorierst mich und ich hasse dich. [MOB57_D] Ich bestehe darauf, dass du sofort zu mir kommst! [MOB58_A] Tommy. [MOB58_B] Hey, Mercedes. [MOB58_C] Selber hey, du toller Hecht. Ich bin sauer auf dich, Tommy. [MOB58_D] Schick mich nie mehr zu diesem Jezz Torrent. [MOB58_E] So ein Jammerlappen. Mittendrin fãngt er an zu weinen wegen seinem Hündchen, [MOB58_F] das gestorben ist, als er 7 war, und dass seine Mama ihn nie lieb hatte. [MOB58_G] Und Tommy - privat lãuft er in Perücke und BH rum. [MOB58_H] Ich bin nicht gut auf dich zu sprechen! [MOB59_A] Oh, Tommy. Ich bin's, Mercedes. [MOB59_B] Ich wollte dir nur sagen, es ist ganz toll beim Film. [MOB59_C] Wenn du nochmal etwas für mich hast, sag Bescheid. [MOB59_D] Wirklich. Ich wollte immer Schauspielerin werden. [MOB59_E] Ich denke, ich lerne viel über die Dramaturgie. [MOB59_F] Es ist so lehrreich! Vielen, vielen Dank. Bis bald. Adios. [MOB_99] Geh zu der Telefonzelle vor Ort. [MOB_98] Geh zu der Telefonzelle vor Ort. [MOB_97] Geh zu der Telefonzelle vor Ort. [MOB_96] Geh zu der Telefonzelle vor Ort. [MOB_95] Geh zu der Telefonzelle vor Ort. [A_TIME] +~1~ Sekunden [F_RANGE] ~g~Du bist außer Reichweite des Feuerwehrfunks. Fahr nãher an eine Feuerwache heran! [DODO_FT] Du bist ~1~ Sekunden geflogen! [GA_8] Benutze den Zünder, um die Bombe hochgehen zu lassen. [GA_10] Hübsche Karre. Hier sind deine $~1~. [GA_11] So eine Karre haben wir schon. Die können wir nicht gebrauchen. [GA_12] Bombe ist scharf. [GA_13] Auf dich ist Verlass. Wenn du alle, die auf der Liste stehen, abgeliefert hast, kriegst du einen Bonus. [GA_14] Du hast alle georderten Karren geliefert. Sehr gut. Hier, für dich. [GA_15] Hoffentlich gefãllt dir die neue Farbe. [GA_16] Das Umspritzen ist gratis. [GA_19] An dem Modell haben wir kein Interesse. [GA_20] Von der Sorte haben wir schon mehr als genug. Sorry, da kommen wir nicht ins Geschãft. [CHASE] Größtes Medieninteresse bisher [CHASE1] Null [CHASE2] Minimal [CHASE3] Vages Interesse [CHASE4] Lokalblatt Seite 7 [CHASE5] Lokalblatt Titelseite [CHASE6] Vice Courier einspaltig [CHASE7] Vice Courier Titelseite [CHASE8] Lokalsender 3-Uhr-Nachrichten [CHASE9] Lokalsender 20-Uhr-Nachrichten [CHASE10] Lokalsender Live-Berichterstattung [CHASE11] UFA Today Seite 12 [CHASE12] UFA Today Seite 4 [CHASE13] Foto in UFA Today [CHASE14] Landesweites TV 4-Uhr-News [CHASE15] Landesweites TV 20-Uhr-News [CHASE16] Landesweite Live-Berichterstattung [CHASE17] Internationale Berichterstattung [CHASE18] Nationale Krise [CHASE19] Internationale Krise [CHASE20] Weltereignis [CHASE21] Stoff aus dem Legenden sind [CR_1] Kran kann dieses Fahrzeug nicht anheben. [PU_MONY] Du hast nicht genug Geld. [CO_ALL] Du hast sie alle geliefert. Hier, für dich. [FEM_ON] AN [FEM_OFF] AUS [FEM_YES] Ja [FEM_NO] Nein [FEM_RET] Wiederholen [FEC_NA] Nicht verfügbar [FEC_CWL] Eine Waffe nach links [FEC_CWR] Eine Waffe nach rechts [FEC_LOF] Nach vorne sehen [FEC_TAR] Zielen [FEC_MOV] Bewegung [FEC_CAM] Blickwinkel [FEC_PAU] Pause [FEC_ENV] In Fahrzeug einsteigen [FEC_JUM] Springen [FEC_ATT] Angreifen oder Waffe abfeuern [FEC_RUN] Rennen [FEC_FPC] Subjektive Kamera [FEC_LL] Nach links sehen [FEC_LB] Nach hinten sehen [FEC_LR] Nach rechts sehen [FEC_HOR] Hupe [FEC_VES] Fahrzeugsteuerung [FEC_BRA] Bremsen oder rückwãrts fahren [FEC_HAB] Handbremse [FEC_CAW] Fahrzeugwaffe [FEC_ACC] Beschleunigen [FEC_CCF] Konfiguration : [FEC_CF1] Konfig. 1 [FEC_CF2] Konfig. 2 [FEC_CF3] Konfig. 3 [FEC_CF4] Konfig. 4 [FEC_CDP] Controller-Anzeige : [FEC_ONF] Zu Fuß [FEC_INC] Im Auto [FEC_VIB] Vibration : [FEL_ENG] Englisch [FEL_FRE] Französisch [FEL_GER] Deutsch [FEL_ITA] Italienisch [FEL_SPA] Spanisch [FED_DBG] Menu Debug [FED_RID] Reload IDE [FED_RIP] Reload IPL [FED_PAH] Parse Heap [FED_DFL] CTheScripts::DbgFlag [FED_DLS] Big White Debug Light Switched [FED_SPR] Show Ped Road Groups [FED_SCR] Show Car Road Grups [FED_SCZ] Show Cull Zones [FED_DSR] Debug Streaming Requests [FED_SCP] gbShowCollisionPolys [PL_STAT] Spielerstatistiken [PE_WAST] Von dir abservierte Personen [PE_WSOT] Von anderen abservierte Personen [TM_BUST] Zahl deiner Verhaftungen [GNG_WST] Gang-Mitglieder [DED_CRI] Kriminelle [PER_COM] Absolviert (in Prozent) [KGS_EXP] Sprengstoffverbrauch in kg [ACCURA] Treffsicherheit [ST_WEAP] Waffen-Budget [ST_PROP] Budget für Objekte [ST_AUTO] Budget für Autoreparaturen und Lackierung [ST_PHOT] Von dir gemachte Fotos [ST_LOAN] Besuche von Kredithaien [ST_STOR] Geknackte Lãden [ST_MOVI] Film-Stunts [ST_PIZZ] Gelieferte Pizzas [ST_GARB] Getãtigte Müllfuhren [ST_ICEC] Verkaufte Eiscreme [TOP_SHO] Beste Schießstand-Punktzahl [SHO_RAN] Schießstand-Rangliste [SEAGULL] Abgeschossene Möwen [PROPOWN] Objekte in deinem Besitz [ST_TIME] Gespielte Zeit [ST_FTIM] Flugstunden [ST_PRAN] Piloten-Rating [ST_RAN0] Anfãnger [ST_RAN1] Navigator [ST_RAN2] Co-Pilot [ST_RAN3] Halb-Profi [ST_RAN4] Könner [ST_RAN5] Profi [ST_RAN6] Ass [ST_RAN7] Roter Baron [ST_DRWN] Gefütterte Fische [ST_FASH] Kleidungsbudget [ST_DAMA] Zerstörte Objekte [TM_DED] Krankenhausbesuche [DAYSPS] Im Spiel verstrichene Tage [NUMSHV] Besuche in Versteck [MXCARD] Weitester IRRSINNS-Sprung (in Fuß) [MXCARJ] Höchster IRRSINNS-Sprung (in Fuß) [MXCARDM] Weitester IRRSINNS-Sprung (m) [MXCARJM] Höchster IRRSINNS-Sprung (m) [MXFLIP] Max. Anzahl Salti [MXJUMP] Max. Drehungen im Sprung [BUL_FIR] Verschossene Kugeln [BUL_HIT] Kugeln, die trafen [SPRAYIN] Anzahl Lackierungen [BSTSTU] Bester IRRSINNS-Stunt bisher [INSTUN] Irrsinns-Stunt [PRINST] Super Irrsinns-Stunt [DBINST] Doppelter Irrsinns-Stunt [DBPINS] Super-Doppel-Irrsinns-Stunt [TRINST] Dreifacher Irrsinns-Stunt [PRTRST] Super-Dreifach-Irrsinns-Stunt [QUINST] Vierfacher Irrsinns-Stunt [PQUINS] Super-Vierfach-Irrsinns-Stunt [NOSTUC] Bisher keine Irrsinns-Stunts geschafft [NOUNIF] Monster-Stunts geschafft [NMISON] Begonnene Missionen [PASDRO] Ans Ziel beförderte Fahrgãste [MONTAX] Mit Taxi verdientes Geld [DAYPLC] Tagesetat für Polizei [CRIMRA] Rang: [STPR_1] Malibu Club [STPR_2] Druckerei [STPR_3] Filmstudio [STPR_4] Eiscremefabrik [STPR_5] Autohaus [STPR_6] Taxiunternehmen [STPR_7] Bootswerft [SET1EN] Konfig. 1 aktiviert [GMSAVE] Spiel speichern [FEDS_TB] Zurück [FEST_OO] von [FEC_TUC] Geschützsteuerung [FEC_RS3] Radiosender auswãhlen (L3-Taste) [FEC_HO3] Hupe (L3-Taste) [C_FAIL] Bürgerwehr-Mission beendet! [C_ESCP] ~r~Der Verdãchtige ist entwischt! [C_VIGIL] BÜRGERWEHR BONUS!! [HEAL_A] Dein ~h~Gesundheitszustand~w~ wird rechts oben auf dem Bildschirm in Orange angezeigt. [WRONGCD] Falsche DVD. Bitte legen Sie die richtige DVD ein. [NOCD] Die DVD-Lade ist leer. Bitte DVD einlegen. [OPENCD] Die DVD-Lade ist offen. Bitte schließen. [CDERROR] Fehler beim Lesen der 'Grand Theft Auto: Vice City' DVD. [RESTART] Neues Spiel wird gestartet [GA_3] Keine Gratisjobs mehr. Umspritzen kostet $100! [GA_1] Hey, so was heißes rühre ich nicht an! [GA_1A] Komm wieder, wenn du nicht so viel zu tun hast... [HELP9_C] Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um das Prãzisionsgewehr ~h~abzufeuern~w~. [TAXI2] ~r~Die Zeit ist um! [PAGEB13] Gesundheits-Powerups in Versteck angeliefert. [PAGEB14] Adrenalin in Versteck vorrãtig. [FESZ_CA] Abbrechen [FES_NGA] Neues Spiel [FES_CAN] Abbrechen [FESZ_QL] Alle nicht gespeicherten Daten des aktuellen Spiels werden verloren gehen. Ladevorgang fortsetzen? [FESZ_QD] Dieses gespeicherte Spiel wirklich löschen? [FESZ_QO] Dieses gespeicherte Spiel wirklich überschreiben? [FESZ_QR] Wirklich ein neues Spiel beginnen? Alle Daten seit dem letzten Speichern werden verloren gehen. Weiter? [T4X4_1] 'PCJ Rallye' [BMX_1] 'Krasses Gelãnde' [BMX_2] 'Teststrecke' [BMXFAIL] ~r~Du hast es nicht geschafft, einen neuen Rekord aufzustellen! [BMX_REC] ~g~Neuer Rekord:~1~ !! [T4X4_3] 'CHECKPOINT FIEBER' [MM_1] 'PYLONEN-RALLYE' [T4X4_F] ~r~Du hast gekniffen! Schon überfordert? [LANDSTK] Landstalker [IDAHO] Idaho [STINGER] Stinger [LINERUN] Linerunner [PEREN] Perennial [SENTINL] Sentinel [RIO] Rio [PATRIOT] Patriot [FIRETRK] Feuerwehrwagen [TRASHM] Trashmaster [STRETCH] Stretch Limo [MANANA] Manana [INFERNS] Infernus [VOODOO] Voodoo [PONY] Pony [MULE] Mule [CHEETAH] Cheetah [AMBULAN] Krankenwagen [FBICAR] FBI Washington [MOONBM] Moonbeam [ESPERAN] Esperanto [TAXI] Taxi [WASHING] Washington [BOBCAT] Bobcat [WHOOPEE] Mr Whoopee [BFINJC] BF Injection [HUNTER] Hunter [POLICAR] Polizei [ENFORCR] Enforcer [SECURI] Securicar [BANSHEE] Banshee [PREDATR] Predator [BUS] Bus [RHINO] Rhino [BARRCKS] Barracks OL [CUBAN] Kubanischer Hermes [HELI] Helikopter [DODO] Dodo [COACH] Coach [CABBIE] Cabbie [STALION] Stallion [RUMPO] Rumpo [RCBANDT] RC Bandit [ROMERO] Romeros Leichenwagen [PACKER] Packer [ADMIRAL] Admiral [SQUALO] Squalo [SEASPAR] Sea Sparrow [PIZZABO] Pizza Boy [GANGBUR] Gang Burrito [TROPIC] Tropic [SPEEDER] Speeder [REEFER] Reefer [FLATBED] Flatbed [YANKEE] Yankee [CADDY] Caddy [ZEBRA] Zebra Cab [TOPFUN] Top Fun [SKIMMER] Skimmer [PCJ600] PCJ 600 [PHOENIX] Phoenix [FAGGIO] Faggio [FREEWAY] Freeway [RCBARON] RC Baron [RCRAIDE] RC Raider [GLENDAL] Glendale [OCEANIC] Oceanic [SANCHEZ] Sanchez [SPARROW] Sparrow [LOVEFIS] Love Fist [COASTG] Küstenwache [DINGHY] Dinghy [HERMES] Hermes [SABRE] Sabre [SABRETU] Sabre Turbo [WALTON] Walton [REGINA] Regina [COMET] Comet [DELUXO] Deluxo [BURRITO] Burrito [SPAND] Spandex Express [MARQUIS] Marquis [BAGGAGE] Baggage Handler [KAUFMAN] Kaufman-Taxi [COASTMA] Küstenwachen-Maverick [MAVERIC] Maverick [RANCHER] Rancher [FBIRANC] FBI Rancher [VIRGO] Virgo [GREENWO] Greenwood [HOTRING] Hotring Racer [BLISTAC] Blista Compact [FEST_DF] Zu Fuß zurückgel. Strecke (Meilen) [FEST_DC] Mit Auto gefahrene Strecke (Meilen) [FESTDFM] Zu Fuß zurückgelegte Strecke (Meter) [FESTDCM] Mit Auto gefahrene Strecke (Meter) [TOT_DIS] Insgesamt zurückgel. Strecke (Meilen) [TOTDISM] Insgesamt zurückgel. Strecke (Meter) [DISTHEL] Mit Helikopter geflogene Strecke (Meilen) [DISTHEM] Mit Helikopter geflogene Strecke (Meter) [DISTBOA] Mit Boot zurückgel. Strecke (Meilen) [DISTBOM] Mit Boot zurückgel. Strecke (Meter) [FEST_LS] Mit Krankenwagen gerettete Menschen [FEST_CC] Kriminelle bei Bürgerwehr-Mission [FEST_FE] Gelöschte Feuer gesamt [FEST_RP] Bestandene Amokfahrten [FEST_MP] Erledigte Missionen [FEST_BB] Schnelle Autos, Schnelles Geld: [FEST_H0] Meiste Checkpoints [FEST_GC] Geschrottete Gang-Autos: [FEST_H1] Im Dschungel der Diablos [FEST_H2] Mafia-Massaker [FEST_H3] Der Casino-Coup [FEST_H4] Rock'n'Roll mit Rumpo [USJ] MONSTER-STUNT-BONUS! [RATNG1] Unbescholtener Bürger [RATNG2] Durchschnittstyp [RATNG3] Falschparker [RATNG4] Ladendieb [RATNG5] Rowdy [RATNG6] Laufbursche [RATNG7] Taschendieb [RATNG8] Kleptomane [RATNG9] Informant [RATNG10] Spitzel [RATNG11] Verrãter [RATNG12] Hochstapler [RATNG13] Betrüger [RATNG14] Schieber [RATNG15] Falschspieler [RATNG16] Schlãger [RATNG17] Straßengauner [RATNG18] Gauner [RATNG19] Halunke [RATNG20] Outlaw [RATNG21] Schurke [RATNG22] Kurier [RATNG23] Gangster [RATNG24] Obergangster [RATNG25] Galgenvogel [RATNG26] Ex-Knacki [RATNG27] Schwerer Junge [RATNG28] Profi [RATNG29] Drahtzieher [RATNG30] Fahrer [RATNG31] Soldat [RATNG32] Gorilla [RATNG33] Kopfgeldjãger [RATNG34] Mann fürs Grobe [RATNG35] Ronin [RATNG36] Abrãumer [RATNG37] Hit-Man [RATNG38] Partner [RATNG39] Butcher [RATNG40] Troubleshooter [RATNG41] Attentãter [RATNG42] Adjutant [RATNG43] Gemachter Mann [RATNG44] Rechte Hand [RATNG45] Vollstrecker [RATNG46] Leutnant [RATNG47] Vize-Boss [RATNG48] Capo [RATNG49] Boss [RATNG50] Oberboss [RATNG51] Don [RATNG52] King of the City [PAGE_00] .. [WELCOME] WILLKOMMEN IN [TSCORE] EINKÜNFTE: $~1~ [PBOAT_2] { reVC update } Drücke die ~h~~k~~VEHICLE_FIREWEAPON~~w~, um die Bordkanonen abzufeuern. [HJSTAT] Distanz: ~1~.~1~m Höhe: ~1~.~1~m Saltos: ~1~ Drehungen: ~1~_ [HJSTATW] Distanz: ~1~.~1~m Höhe: ~1~.~1~m Saltos: ~1~ Drehungen: ~1~_ Und was für eine Landung! [ATUTOR] Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Krankenwagen-Missionen an- oder abzuschalten. [FEST_HA] Höchster Krankenwagen-Missions-Level [C_KILLS] KRIMINELLE: ~1~ [HJSTATF] Distanz: ~1~Fuß Höhe: ~1~Fuß Saltos: ~1~ Drehungen: ~1~_ [HJSTAWF] Distanz: ~1~Fuß Höhe: ~1~Fuß Saltos: ~1~ Drehungen: ~1~_ Und was für eine Landung! [CRED001] ROCKSTAR NORTH [CRD001A] STUDIO DIRECTOR [CRD001B] ANDREW SEMPLE [CRED002] PRODUCER [CRD002A] DEVELOPMENT DIRECTOR [CRED003] LESLIE BENZIES [CRED004] ART DIRECTOR [CRED005] AARON GARBUT [CRED006] TECHNICAL DIRECTORS [CRED007] OBBE VERMEIJ [CRED008] ADAM FOWLER [CRED010] ANDREW DUTHIE [CRED011] CRAIG FILSHIE [CRED012] WILLIAM MILLS [CRED013] CHRIS ROTHWELL [CRD013A] IMRAN SARWAR [CRD013B] JAMES WORRALL [CRD013C] JOHN HAIME [CRED014] WRITTEN BY [CRED015] JAMES WORRALL [CRED016] PAUL KUROWSKI [CRED017] DAN HOUSER [CRED018] LEAD CHARACTER DESIGNER [CRD018A] CHARACTER DESIGNER [CRED019] IAN MCQUE [CRD019A] TOKS SOLARIN [CRD019B] ALAN DAVIDSON [CRED020] LEAD ANIMATOR [CRED021] ALEX HORTON [CRD022A] ANIMATORS [CRED022] LEE MONTGOMERY [CRD022B] DUNCAN SHIELDS [CRD022C] GUS BRAID [CRED023] VEHICLE DESIGNERS [CRD023A] JOLYON ORME [CRD023B] ALAN DUNCAN [CRD024A] LEAD VEHICLE DESIGNER [CRED024] PAUL KUROWSKI [CRED025] MAP DESIGNERS [CRED026] ADAM COCHRANE [CRED027] NIK TAYLOR [CRED028] GARY MCADAM [CRED029] KEIRAN BAILLIE [CRED030] ALISDAIR WOOD [CRED031] ANDREW SOOSAY [CRD031A] STEVEN MULHOLLAND [CRD031B] WAYLAND STANDING [CRD031C] CAMPBELL J. DICK [CRD031D] GRAPHIC DESIGNER [CRD031E] STUART PETRI [CRED032] LEAD PROGRAMMER [CRD032A] PROGRAMMERS [CRED033] ALEXANDER ROGER [CRED034] GRAEME WILLIAMSON [CRED035] BARANE CHAN [CRED036] DEREK PAYNE [CRED037] GORDON YEOMAN [CRD037A] ALAN CAMPBELL [CRD037B] MARK HANLON [CRD037C] ANDRZEJ MADAJCZYK [CRED038] LEAD MUSIC PRODUCER [CRED039] CRAIG CONNER [CRED040] STUART ROSS [CRED041] LEAD AUDIO ENGINEER [CRED042] ALLAN WALKER [CRD041A] AUDIO ENGINEER [CRD041B] AUDIO [CRD042A] WILL MORTON [CRED043] AUDIO PROGRAMMER [CRED044] RAYMOND USHER [CRED045] TEST MANAGER [CRED046] CRAIG ARBUTHNOTT [CRED047] LEAD QA [CRED048] NEIL CORBETT [CRED049] KEVIN WONG [CRED050] QA [CRED051] DAVID BEDDOES [CRED052] DAVID WATSON [CRED053] BARRY CLARK [CRED054] ROSS SPARROW [CRED055] JAMES ALLAN [CRED056] NEIL MEIKLE [CRD056A] GEORGE WILLIAMSON [CRD056B] MATT JONES [CRD056C] ROB HARBOUR [CRD056D] TOM WHITTAKER [CRED057] LEAD TECHNICAL SUPPORT [CRED058] LORRAINE ROY [CRD057A] TECHNICAL SUPPORT [CRED059] CHRISTINE CHALMERS [CRD060A] OFFICE SUPPORT [CRD060B] KIM GURNEY [CRD060C] CASSIE OLIVER [CRED060] ROCKSTAR NEW YORK [CRED061] EXECUTIVE PRODUCER [CRED062] SAM HOUSER [CRED063] PRODUCER [CRED064] DAN HOUSER [CRED065] VP OF DEVELOPMENT [CRED066] JAMIE KING [CRED067] CHIEF TECHNOLOGY OFFICER [CRED068] GARY J. FOREMAN [CRED069] ASSOCIATE PRODUCER [CRED070] JEREMY POPE [CRD071A] DIRECTOR OF QUALITY ASSURANCE [CRD072A] JEFF ROSA [CRED071] MUSIC SUPERVISOR [CRED072] TERRY DONOVAN [CRED073] PRODUCTION TEAM [CRED074] TERRY DONOVAN [CRED075] JENNIFER KOLBE [CRED076] JENEFER GROSS [CRED077] LAURA PATERSON [CRED078] JEFF CASTANEDA [CRED079] JERONIMO BARRERA [CRED080] CARLY SLATER [CRED081] JUNG KWAK [CRED082] BRIAN WOOD [CRED083] RENAUD SEBANNE [CRED084] RICHARD KRUGER [CRD084A] DANIEL EINZIG [CRD084B] JACEN BURROWS [CRD084C] LINN PR [CRD084D] COVER ART [CRD084E] STEPHEN BLISS [CRED085] KENT PAUL'S 80 NOSTALGIA ZONE [CRED086] WRITTEN BY DAN HOUSER [CRD086A] PRODUCED BY ADAM TEDMAN [CRED087] WWW.KENTPAUL.COM AND WWW.VICECITY.COM [CRED088] CREATED BY [CRD088A] ADAM TEDMAN [CRD088B] DAVID YU [CRD088C] JERRY LUNA [CRD088D] STUART PETRI [CRD088E] MICHAEL CARNEVALE [CRD088F] GREG LAU [CRD088G] FUTABA HAYASHI [CRED089] QA MANAGER [CRED090] CRAIG ARBUTHNOTT [CRED091] LEAD ANALYST [CRED092] ADAM DAVIDSON [CRD092A] JOE HOWELL [CRD092B] MARC FERNANDEZ [CRED093] GAME ANALYST [CRED094] RICHARD HUIE [CRED095] ROCKSTAR TEST TEAM [CRED096] LANCE WILLIAMS [CRED097] JOE GREENE [CRED098] BRIAN PLANER [CRD098A] ELIZABETH SATTERWHITE [CRD098B] JAMEEL VEGA [CRD098C] MIKE HONG [CRED099] LEE CUMMINGS [CRED100] STORY [CRED101] JAMES WORRALL [CRED102] DAN HOUSER [CRED103] ADAM TEDMAN [CRED104] PAUL YEATES [CRED105] JENEFER GROSS [CRED106] LAURA PATERSON [CRED107] CUT SCENES [CRED108] WRITTEN BY DAN HOUSER AND JAMES WORRALL [CRED109] AUDIO DIRECTED BY DAN HOUSER AND NAVID KHONSARI [CRED110] PRODUCED BY JAMIE KING [CRD110A] TALENT PROCUREMENT: JAMIE KING, SEAN MACALUSO [CRED111] CAST [CRD111A] SUPPORTING CHARACTERS [CRED112] TOMMY VERCETTI - RAY LIOTTA [CRED113] KEN ROSENBERG - WILLIAN FICHTNER [CRED114] SONNY FORELLI - TOM SIZEMORE [CRED115] STEVE SCOTT - DENNIS HOPPER [CRED116] AVERY CARRINGTON - BURT REYNOLDS [CRED117] RICARDO DIAZ - LUIS GUZMAN [CRED118] LANCE VANCE - PHILIP MICHAEL THOMAS [CRED119] COLONEL JUAN CORTEZ - ROBERT DAVI [CRED120] UMBERTO ROBINA - DANNY TREJO [CRED121] PHIL CASSIDY - GARY BUSEY [CRED122] MITCH BAKER - LEE MAJORS [CRED123] MERCEDES CORTEZ - FAIRUZA BALK [CRED124] KENT PAUL - DANNY DYER [CRED125] JEZZ TORRENT - KEVIN MCKIDD [CRED126] TAXI CONTROLLER - DEBORAH HARRY [CRED127] CANDY SUXX - JENNA JAMESON [CRED128] BJ SMITH - LAWRENCE TAYLOR [CRED129] AUNTIE POULET - YOUREE CLEOMILI HARRIS [CRED130] SUPPLIER - ARMANDO RIESCO [CRED131] COUGAR - BLAYNE PERRY [CRED132] HILARY - CHARLES TUCKER [CRED133] CONGRESSMAN ALEX SHRUB - CHRIS LUCAS [CRED134] OLD MAN KELLY - GEORGE DICENZO [CRD134A] CAM JONES - GREG SIMS [CRD134B] PSYCHO - HUNTER PLATIN [CRD134C] MAUDE THE ICE CREAM LADY - JANE GENNARO [CRD134D] JETHRO - JOHN ZURHELLEN [CRD134E] GONZALES - JORGE PUPO [CRD134F] DWAYNE - NAVID KHONSARI [CRD134G] DICK - PETER MCKAY [CRD134H] MIKE THE GOON & PORN GUY - ROBERT CIHRA [CRD134I] PERCY - RUSSELL FOREMAN [CRED135] MOTION CAPTURE [CRED136] ANIMATED BY [CRD136A] TECHNICAL DIRECTION BY ALEX HORTON [CRED137] DIRECTED BY [CRD137A] DIRECTED BY NAVID KHONSARI [CRED138] PRODUCED BY [CRD138A] PRODUCED BY JAMIE KING [CRD138B] RENAUD SEBBANE [CRED139] RECORDED AT PERSPECTIVE STUDIOS, BROOKLYN [CRED140] MOTION CAPTURE ACTORS [CRD140A] BLAYNE PERRY [CRD140B] JONATHON SALE [CRD140C] CHARLES TUCKER [CRD140D] EDDIE MARRERO [CRD140E] WILLIAM MCCALL [CRD140F] JORGE PUPO [CRD140G] ROBERT JACKSON [CRD140H] TARA RADCLIFFE [CRD140I] JENIFER GAMBETESE [CRD140J] KRIS ACHEVARRIA [CRD140K] ALI ORDOUBADI [CRD140L] KAHLEEM POOLE [CRED141] PEDESTRIAN DIALOGUE [CRD141A] WRITTEN BY DAN HOUSER, MARC FERNANDEZ, GILLIAN TELLING AND NAVID KHONSARI [CRD141B] WITH HELP FROM JEREMY POPE, LANCE WILLIAMS, AND JENNY JEMISON [CRED142] WRITTEN BY [CRD142A] DAN HOUSER AND JAMES WORRALL [CRED143] DIRECTED BY DAN HOUSER, CRAIG CONNER, MARC FERNANDEZ, AND ALLAN WALKER [CRED144] PRODUCED BY RENAUD SEBANNE [CRED145] PEDESTRIANS [CRED146] ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCIA, [CRED147] ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, [CRED148] ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, [CRED149] BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, [CRED150] CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LOPEZ, DAN LEE, [CRED151] DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, [CRED152] DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, [CRED153] DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, [CRED154] DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, [CRED155] ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, [CRED156] GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, [CRED157] HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, [CRED158] JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, [CRED159] KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, [CRED160] LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, [CRED161] LORNA JORDAN, LUCIO AMADIO, MARCO FERNANDEZ, MARIKO TANAKA, MARLON MATTHEWS, [CRED162] MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, [CRED163] MELISSA ALVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, [CRED164] NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, [CRED165] OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZALES, [CRED166] RANDY JOHNSON, REY CONCEPCION, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, [CRED167] ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NUNEZ, SALVADORE SUAZO, [CRED168] SAM WHITE, SANTOS GONZALES, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, [CRED169] STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, [CRED170] SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, [CRED171] DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, [CRED172] CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON [CRED175] ADAM DAVIDSON [CRED176] LANCE WILLIAMS [CRED177] NEIL MCCAFFREY [CRED178] LAURA PATERSON [CRED179] REY CONCEPCION [CRED180] CHARLES HEROLD [CRED181] ANDREW GREENWALD [CRED182] JAMES MIELKE [CRED183] PETER SUCIU [CRED184] ALEX ODULIO [CRED185] DON NKRUMAH [CRED186] KENDALL PITTMAN [CRED187] SAL SUAZO [CRED188] EREK MATEO [CRED189] CHRIS DIFATE [CRED190] LEILA MILTON [CRED191] DARREN ZOLTOWSKI [CRED192] VIRGINIA SMITH [CRED193] KEVIN CASSIN [CRED194] JASON SHIGEMORI [CRED195] KELLY KINSELLA [CRED196] MOLLIE STICKNEY [CRED197] STANTON SARJEANT [CRED198] LAURA WALSH [CRED199] MARK GARONE [CRED200] JOANNA SLY [CRED201] ELIZABETH HOWELL [CRED202] ANA HERCULES [CRED203] SHIRLEY IRICK [CRED204] KASHONA FIELDS [CRED205] JOEL M. LILJE [CRED206] JOHN DIBENEDETTO [CRED207] NANCY GILES [CRED208] RYAN CROY [CRED209] JENNIFER KOLBE [CRED210] LIAM BURKE [CRED211] SIGRID PREISSL [CRED212] ANITA FITZSIMONS [CRED213] PHILIPPA RASELLI [CRED214] WIL QUESNEL [CRED215] FALKO BURKERT [CRED216] SARA SEWELL [CRED217] RADIO STATIONS AND MUSIC [CRED218] MUSIC CONSULTANCY [CRD218A] HEINZ HENN [CRD218B] STUART ROSS [CRED219] SOUNDTRACK CO-ORDINATOR [CRED220] TERRY DONOVAN [CRED221] PRODUCER FOR ROCKSTAR GAMES [CRED222] DAN HOUSER AND LAZLOW [CRED223] PRODUCER FOR ROCKSTAR NORTH [CRED224] CRAIG CONNER [CRED225] ALLAN WALKER [CRED226] LAZLOW [CRED227] DJ BANTER AND IMAGING [CRED228] WRITTEN BY DAN HOUSER AND LAZLOW [CRED229] FLASH FM [CRD229A] TONI-MARIA CHAMBERS [CRD229B] IMAGING VOICE AND PRODUCTION-JEFF BERLIN [CRED230] SPECIAL THANKS TO [CRED231] TOMMY MOTTOLA, [CRED232] MICHELLE ANTHONY, [CRED233] STEVE BARNETT, [CRED234] CHUCK FLECKENSTEIN, [CRED235] RITA LIBERATOR [CRED236] MARTIN & CLAIRE LOGAN [CRED237] SANDRA HUTTON [CRED238] CHRISTINE DAVIDSON [CRED239] ALAN, RED & BIGFOOT [CRED240] LE T [CRED241] COLIN DONALD [CRED242] KERRY STALLWOOD [CRED243] ALAN MCGREGOR [CRED244] CHRIS MORTON [CRED245] EMIL BUSSE [CRED246] EMILY BAILLIE [CRED247] KEVIN ARCHIBALD [CRED248] MORAG KERR [CRED249] CATH WALKER [CRED250] ISO BAR [CRED251] WATERLINE [CRED252] NEWS CAFE [CRD251A] THE POND [CRD252A] PIVO [CRED253] BUDGET VIDEO RENTALS [CRED254] LORNA'S SCOOTER [CRED255] GARETH MURFIN [CRED256] ADDITIONAL ART [CRED257] TONY PORTER [CRED258] CRAIG MOORE [CRED259] CUT SCENE LIP-SYNC ANIMATION [CRED260] COSGROVE HALL FILMS [CRED261] PRODUCER - OWEN BALLHATCHET [CRED262] SENIOR ANIMATOR - JON TURNER [CRED263] ANIMATORS - RICHARD DRUMM [CRED264] DAVE BROWN [CRED265] MAIR THOMAS [CRED266] PRASHANT PATEL [CRED267] AUDIO TECHNOLOGY CONSULTANT [CRED268] RIK EDE FOR GAMESOUND LTD. [CRED269] DTS INTEGRATION SUPPORT [CRED270] TED LAVERTY FOR DTS [CRED271] CHRIS GREER FOR DTS [CRED272] JASON PAGE FOR SCEE [CRED273] RESEARCH AND ANALYSIS [CRED274] VROCK [CRED275] DJ: LAZLOW AS HIMSELF [CRED276] IMAGING VOICE-JOE KELLY [CRED277] IMAGING PRODUCTION-JONATHAN HANST [CRED278] WAVE 103 [CRED279] DJ: ADAM FIRST-JAMIE CANFIELD [CRED280] IMAGING VOICE-JEN SWEENEY [CRED281] IMAGING PRODUCTION-JONATHAN HANST [CRED282] FEVER 105 [CRED283] DJ: OLIVER 'LADYKILLER' BISCUIT-JULIUS DYSON [CRED284] IMAGING VOICE MALE-ED MCMANN [CRED285] IMAGING VOICE FEMALE-SHAWNEE SMITH [CRED286] IMAGING PRODUCTION- LISTEN KITCHEN [CRED287] EMOTION 98.3 [CRED288] DJ: FERNANDO- FRANK CHAVEZ [CRED289] IMAGING VOICE-JEN SWEENEY [CRED290] IMAGING PRODUCTION-JONATHAN HANST [CRED291] RADIO ESPANTOSO [CRED292] DJ: PEPE-TONY CHILRODES [CRED293] WILDSTYLE [CRED294] DJ: MISTER MAGIC AS HIMSELF [CRED295] IMAGING VOICE-FRANK SILVESTRO [CRED296] IMAGING PRODUCTION-LAZLOW [CRED297] KCHAT [CRED298] WRITTEN BY DAN HOUSER AND LAZLOW [CRED299] PRODUCED AND EDITED BY LAZLOW [CRED300] DJ AMY SHECKENHAUSEN -LEYNA WEBER [CRED301] JEZ TORRENT-KEVIN MCKIDD [CRED302] MANDY -COLLEEN CORBETT [CRED303] MICHELLE CARAPADIS-MARY BIRDSONG [CRED304] MR.ZOO-CARL DOWLING [CRED305] GETHSEMANEE-LYNN LIPTON [CRED306] CLAUDE MAGINOT-JOHN MAUCERI [CRED307] BJ SMITH-LAWRENCE TAYLOR [CRED308] THOR-FRANK FAVA [CRED309] RADIO CALLERS [CRED310] COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS [CRED311] LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, [CRED312] DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, [CRED313] KEITH BROADAS [CRED314] VCPR [CRED315] WRITTEN BY DAN HOUSER AND LAZLOW [CRED316] PRODUCED BY LAZLOW [CRED317] MAURICE CHAVEZ-PHILLIP ANTHONY RODRIGUEZ [CRED318] JONATHAN FREELOADER- PATRICK OLSEN [CRED319] MICHELLE MONTANIUS-KELLY GUEST [CRED320] REP. ALEX SHRUB- CHRIS LUCAS [CRED321] CALLUM CRAYSHAW- SEAN MODICA [CRED322] JOHN F. HICKORY- LJ GANSEN [CRED323] PASTOR RICHARDS- DAVID GREEN [CRED324] JAN BROWN- MAUREEN SILLIMAN [CRED325] BARRY STARK- RENAUD SEBBANE [CRED326] JENNY LOUISE CRAB- MARY BIRDSONG [CRED327] KONSTANTINOS SMITH- KONSTANTINOS.COM [CRED328] JEREMY ROBARD-PETER SILVESTRO [CRED329] RADIO COMMERCIALS [CRED330] WRITTEN BY DAN HOUSER AND LAZLOW [CRED331] PRODUCED BY LAZLOW [CRED332] ADDITIONAL JINGLES PRODUCED BY CRAIG CONNER [CRED333] COMMERCIAL VOICES: [CRED334] ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, [CRED335] ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS [CRED336] FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS [CRED337] HARRISON, ED MCMANN, FRANK CHAVEZ, FRANK FAVA, GENE HILGREEN, GREG [CRED338] SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, [CRED339] JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE [CRED340] WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, [CRED341] MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF [CRED342] CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD [CRD344A] AUDIO RECORDED AT DIGITAL ARTS STUDIOS, [CRED344] NYC, TRACK 9 STUDIOS, NYC, [CRED345] WEDDINGTON MULTIMEDIA, LOS ANGELES, [CRD345A] SYNC SOUND, NYC AND RADIO LAZLOW, LONG ISLAND. [CRED346] THANKS TO AXEL ERICSON AND WON LEE AT DIGITAL ARTS, PAUL VASQUEZ AT TRACK 9 STUDIOS, JOHN BOWEN AND JOHN HASSLER AT SYNC SOUND [CRED347] MARK LLOYD [CRED348] TIM BATES [CRED349] KIT BROWN [CRED350] ANDY MASON [CRED351] PHIL DEANE [CRED352] PHIL ALEXANDER [CRED353] MATT HEWITT [CRED354] DENBY GRACE [CRED355] ANTOINE CABROL [CRED356] JONATHAN STONES [CRED357] MIKE BLACKBURN [CRED358] TIM MCGAFF [CINCAM] Cinematic-Kamera [RC4] 'ROCK'N ROLL MIT RUMPO' [LEGAL] ~g~Schalte die kriminelle Bedrohung aus! [GA_2] Neuer Motor und neue Lackierung. Die Cops werden dich nicht identifizieren! [HELP15] Wenn zu Fuß unterwegs, drücke die ~h~~k~~PED_LOOKBEHIND~~w~, um ~h~nach hinten zu sehen~w~. Benutze den ~h~rechten Analog-Stick~w~, um dich ~h~umzusehen~w~. [FEC_LB4] Nach hinten sehen (R3-Taste) [PERPIC] Versteckte Pãckchen gefunden [CO_ONE] Verstecktes Pãckchen ~1~ von ~1~ [GA_21] In dieser Garage bringst du keine Autos mehr unter. [CHEAT1] Cheat aktiviert [CHEAT2] Waffen-Cheat [CHEAT3] Health-Cheat [CHEAT4] Panzerungs-Cheat [CHEAT5] Fahndungslevel-Cheat [CHEAT6] Geld-Cheat [CHEAT7] Wetter-Cheat [USJ_ALL] ALLE MONSTER-STUNTS ABSOLVIERT! [JAN] Jan [FEB] Feb [MAR] Mãr [APR] Apr [MAY] Mai [JUN] Jun [JUL] Jul [AUG] Aug [SEP] Sept [OCT] Okt [NOV] Nov [DEC] Dez [DEFDT] --:---:---- --:--:-- [BONUS] ~g~BONUS $~1~ [HORN1] Drück die ~h~~k~~VEHICLE_HORN~~w~, um zu ~h~hupen. [HORN2] Drück die ~h~~k~~VEHICLE_HORN~~w~, um zu ~h~hupen. [HORN3] Drück die ~h~~k~~VEHICLE_HORN~~w~, um zu ~h~hupen. [FEC_EXV] In Fahrzeug ein- und aussteigen. [TAXI_M] 'TAXIFAHRER' [COP_M] 'BÜRGERWEHR' [FIRE_M] 'FEUERWEHRMANN' [AMBUL_M] 'SANITÃTER' [HJ_IS] IRRSINNS-STUNT-BONUS: $~1~ [HJ_PIS] SUPER IRRSINNS-STUNT-BONUS: $~1~ [HJ_DIS] DOPPELTER IRRSINNS-STUNT-BONUS: $~1~ [HJ_PDIS] SUPER-DOPPEL-IRRSINNS-STUNT-BONUS: $~1~ [HJ_TIS] DREIFACHER IRRSINNS-STUNT-BONUS: $~1~ [HJ_PTIS] SUPER-DREIFACH-IRRSINNS-STUNT-BONUS: $~1~ [HJ_QIS] VIERFACHER IRRSINNS-STUNT-BONUS: $~1~ [HJ_PQIS] SUPER-VIERFACH-IRRSINNS-STUNT-BONUS: $~1~ [FESZ_LS] Ladevorgang beendet. [HELI_1A] Teste dein Können mit dem 'Sparrow'. Probiere, wie schnell du den Kurs abfliegen kannst. [HELI_1B] Kurs absolviert! [HELIODD] Helikopter-Jobs [LAW] DIE ANWALTSMISSIONEN [LAW1_1] ~g~Besorge dir neue Sachen in Rafaels Shop. [LAW4_6] Nieder mit der Geschãftsleitung! [LAW4_7] Tod den Bossen! [LAW4_8] Kampf, Kampf, Kampf, Kampf! [LAW4_9] Mehr Urlaub, weniger Arbeit! [LAW4_11] Kampf, Kampf, Kampf, Kampf! [LAW4_12] Es lebe die Revolution! [GENERAL] DIE COLONEL-MISSIONEN [GEN3_4] Tommy Vercetti. Los, komm... [GEN3_13] Was ist los, Mann?! Steig auf das Dach gegenüber im Hof bevor sie kommen! [GEN3_17] Mist! Willst du mich umbringen?! [GEN3_21] ~g~Er hat Diaz' Geld! Stelle ihn und hole es zurück! [GEN3_24] ~r~Diaz hat's erwischt! Du hast versagt! [GEN3_26] ~r~Du hast Diaz erwischt! [GEN3_27] ~r~Du hast Diaz' Bodyguards erwischt! [GEN3_31] ~g~Begib dich zum Übergabeort und pass auf Diaz auf. [GEN3_32] ~g~Begib dich zu deinem Beobachtungsposten auf dem Dach des Gebãudes gegenüber von Lance. [COKE] DIE KOKS-BARON-MISSIONEN [COK1_3] Ich hoffe, du brichst dir das Genick! [COK1_6] Ich hab diese Idioten satt! [COK2_7] Siehst du diese Bojen? Versuch, die Lichter auszuschießen. [COK2_10] Eines ist sicher: Du schießt besser als du laberst. [COK2_11] Danke. Du bist ein echter Charmeur. [COK2_12] Ich weiß, Tommy. [COK2_18] Stehst du auf Kenny Loggins? [COK2_19] Mann, ich liebe diese Scheibe. [COK2_26] ~r~Du hast Lance erwischt! [COK3_1] Nicht schießen, Mann! [COK3_2] Was ist in dem Zeug drin? [COK3_3] Er klaut das Boot. Mist! [COK3_4] Hilfe! Irgendein Irrer stiehlt das Boot, Mann! [COK4_W] Uff! Das ist der letzte. [COK4_X] Ich lass die Mühle an. [COK4_Y] Ich glaube, wir haben ein paar neue Freunde. [COK4_2] Ja. [COK4_6] Weißt du, wo's lang geht? [COK4_7] Haben wir uns verfahren? [COK4_8] Wir haben Konkurrenz bekommen! [COK4_9] Knöpf sie dir vor! [COK4_9A] Zeit für den Lance Vance Dance! [COK4_10] Die sind Kleinholz! Und Fischfutter. [COK4_11] Geschafft! Die anderen Schiffchen taugen nicht viel. [COK4_17] Die wollen's wissen! [COK4_18] Ich hab nasse Füße! WIR KRIEGEN WASSER INS BOOT! [COK4_21] Achtung, Brücke! [COK4_22] Raus! Das Ding geht hoch! [COK4_23] Gut geschossen. [COK4_29] ~r~Du hast Lance erwischt! [ASS1_6] Geh nur, Tommy, ich komm schon zurecht! [ASS1_7] Das ist für euch, ihr verdammten Mörder!! [ASS1_8] Ich komm hier nicht weg! [ASS1_9] Ich bin da, Tommy! [ASS1_10] Hey, das ist aber ein herzallerliebstes Beet. [ASS1_11] Hey Tommy, krieg ich ein Zimmer mit Blick auf die Bucht? [ASS1_12] Schön hohe Decken hat's hier... [ASS1_3] Lance! Ich brauche Deckung! [ASS1_4] Diaz muss drinnen sein! [ASS1_5] Lance! [ASS1_15] ~g~Stürme die Villa und erledige Diaz! [ASS1_17] ~g~Es führen mehrere Wege in die Villa. [TAXWAR] TAXI-KRIEG-MISSIONEN [NOTAXI] ~g~Du brauchst ein Kaufman-Taxi, um diese Mission zu aktivieren. [TAXW1_5] ~g~Du musst in einem Kaufman-Taxi sitzen! [TAX2_4] Auf geht's, Tommy. [TAX2_5] Polier ihm die Visage. [TAX2_6] Der hat nicht mal eine Lizenz. [TAX2_7] Verdammte Limo-Services. [TAXW3_1] ~g~Hole Mercedes ab. [RACE1] ~g~3...2...1...LOS, LOS, LOS! [RACE2] ~g~3 [RACE3] ~g~2 [RACE4] ~g~1 [RACE5] ~g~LOS! [FIRST] ~b~1. [SECOND] ~b~2. [THIRD] ~b~3. [FOURTH] ~b~4. [RACETM] ~b~ZEIT: ~1~:~1~ [RACETM2] ~b~ZEIT: ~1~:0~1~ [RACEFA] ~r~Du hast das Rennen verloren! [TEX1_5] ~r~Er ist entkommen! [SEG3_1] ZEIT: [SEG3_2] ~g~Begib dich zu dem Transporter mit dem RC Raider und den Zeitzünder-Bomben. [SEG3_3] ~g~Du musst den RC Raider benutzen, um 4 Bomben zu 4 Zielzonen auf dem Grundstück zu bringen. [SERG3_5] ~g~Du kannst immer nur 1 Bombe transportieren und kannst erfolgreich platzierten Bomben nicht wieder aufnehmen. [SEG3_7] ~g~Sobald du die ERSTE Bombe platziert hast, lãuft der Timer des Zeitzünders an. Du musst dann alle Bomben innerhalb dieses Zeitraums platzieren. [SEG3_8] ~g~Alle 4 Bomben müssen in den 4 Zielzonen platziert werden, um die Mission zu erfüllen und das Gebãude zu demolieren. [SEG3_9] ~g~Zielzone getroffen! Noch 3 Bomben. [SEG3_10] ~g~Zielzone getroffen! Noch 2 Bomben. [SEG3_11] ~g~Zielzone getroffen! Noch 1 Bombe. [SEG3_12] ~r~Ziel verfehlt. Hol dir eine Bombe! [SEG3_13] ~g~Wirf die Bombe in einer Zielzone ab. [SEG3_14] ~r~Die Zeit ist um. Demolierung des Gebãudes fehlgeschlagen. [SEG3_15] ~r~Dein RC Raider ist zerstört. Wie willst du jetzt die Bomben transportieren? [AVERY] AVERY-MISSIONEN [ASM] ATTENTÃTERMISSIONEN [ASM_1] ATTENTÃTERMISSION 1 [ASM1_1] ~g~Mr. Teal, Ihre Hilfe bei der Beseitigung der Landeier war ãußerst wertvoll. Ich habe noch mehr Arbeit, die eine eher 'zupackende' Art verlangt. Ihr nãchster Job klebt unter dem Telefon. [ASM1_2] ~g~Begib zu dich dem Fernsprecher vor dem Einkaufszentrum in Washington. [ASM1_3] ~g~Carl Pearson, Pizza-Lieferant. Er darf seine Lieferungen nicht durchführen. [ASM1_4] ~g~Schalte den Pizza-Lieferanten aus, bevor er seine Lieferungen abschließt. [ASM_2] ATTENTÃTERMISSION 2 [ASM_3] ATTENTÃTERMISSION 3 [ASM3_A] Marcus Hammond, Franco Carter, Dick Tanner, Nick Kong und Stuntman Driver gehören zu einem europãischen Syndikat und planen einen Überfall. [ASM3_B] Sie sind alle bereits in Position. Schalten Sie sie aus, bevor es losgeht. Sie haben 9 Minuten. Ich habe in der Nãhe Waffen deponiert, die Sie brauchen werden. [ASM3_1] ~g~Hol dir die Waffe, die Mr. Black für dich deponiert hat. [ASM3_2] ~g~Geh nicht zu dicht an die Zielperson heran, sonst bemerkt sie dich. [ASM3_3] ~g~Es geht schneller, wenn du dir eine günstige Position nahe ihrem Standort suchst und zuschlãgst, ohne gesehen zu werden. [ASM3_4] ~g~Er hat dich gesehen. Du musst ihn irgendwie ausschalten! [ASM3_5] ~g~Marcus Hammond befindet sich bei den Werbetafeln in Washington Beach. [ASM3_6] ~g~Franco Carter befindet sich bei DBP Security nahe dem Ocean Drive. [ASM3_7] ~g~Dick Tanner ist in der Nãhe des Juweliers in Vice Point. [ASM3_8] ~g~Nick Kong befindet sich nãhe Washington Beach. [ASM3_9] ~g~Stuntman Driver ist in Washington. [ASM3_10] Du hast nicht alle ausgeschaltet. [ASM_4] ATTENTÃTERMISSION 4 [ASM4_1] ~g~Hol dir das Gewehr, das im Laub vor dem Flughafen-Terminal für dich deponiert wurde. [ASM4_2] ~g~Verfehle dein Ziel nicht, du alarmierst sonst die Leibwãchter. Und bleib auf Distanz, damit er dich nicht bemerkt. [ASM4_3] ~g~Beobachte die Frau über den Check-in-Schaltern im Terminal. TU IHR NICHTS. [ASM4_4] ~g~Erledige den Mann, dem sie den Aktenkoffer gibt, aber erst, NACHDEM ER IHN GENOMMEN HAT. Hole den Koffer und bringe ihn ins Ammu-Nation Downtown. [ASM4_5] Hol dir den Aktenkoffer. [ASM4_6] ~g~Bring den Aktenkoffer ins Ammu-Nation Downtown. [ASM4_7] ~r~Du hast die Frau erwischt, du Idiot! [ASM4_8] ~r~Die Zielperson hat gehört, wie du geschossen hast. Der Deal ist geplatzt! [ASM4_9] ~r~Die Zielperson hat das Flugzeug bestiegen! [ASM4_11] ~r~Die Zielperson hat dich gesehen! Der Deal ist geplatzt! [ASM4_13] ~g~Er hat dich bemerkt und flieht. Schalte ihn aus und hol dir den Aktenkoffer! [ASM4_14] ~g~Der Distanz-Balken am oberen rechten Bildschirmrand zeigt dir, wie nahe du der Zielperson bist. Lass ihn nicht voll werden, sonst sieht sie dich. [ASM_5] ATTENTÃTERMISSION 5 [KICK] KICKSTART [KICK1_3] ~g~Anzahl, wie oft der Fuß abgesetzt wurde: ~1~ [KICK1_4] ~g~Zeitstrafe: ~1~ Sekunden [BANK] BANK-MISSIONEN [BANK1] BANK-MISSION 1 [BANK2] BANK-MISSION 2 [BJM2_21] ~g~Triff so viele Ziele als möglich, solange deine Munition reicht. [BANK3] BANK-MISSION 3 [BJM3_1] ~g~Besorg dir ein schnelles Auto und fahre an den Start. [BNK4_2A] Die Mechaniker haben das Baby super hergerichtet. [BNK4_3G] Oh, Mist, jetzt haben wir die Cops am Hals! [BNK4_3H] Und wir sind noch nicht mal da. [BNK4_3K] Also, erst mal müssen wir die Cops abschütteln... [BNK4_3L] Großer Gott, Tommy, willst du uns alle umbringen?! [BNK4_3N] Alles, was ich gern habe, geht kaputt! [BNK4_26] Verdammt! Da sind sie schon! [BNK4_32] Spreng die Schließfãcher mit Sprengstoff auf. [BNK4_36] Wo ist Cam? [BNK4_37] In der Hölle. [BNK4_38] Das ist der letzte! LOS! LOS! LOS! [BNK_39] Shit! Wo bleibt Hilary? [BK4_40A] Ich werd was für seine Verlustãngste tun! [BNK4_42] Hey, Jungs! Los, rein! Ich geb euch Deckung! [BNK4_43] Ich hab alles im Griff! FAHR! [BNK4_44] Geschafft! Wir sind reich! REICH! [BNK4_45] Ein Jammer, dass Cam es nicht gepackt hat. Er war 'n guter Kerl. [BNK4_46] Ja. Andererseits, so bleibt mehr für uns! [BNK4_47] Sehr richtig! YEEEEHAAAH! [BNK4_48] Tommy, eine Massage gefãllig? [BNK4_49] Hi, Mercedes. Ja, ich bin ein bisschen verspannt... [BNK450A] Was hab ich gesagt, Tommy? Korrupte Bullen müssen sich vorsehen, wenn Kent Paul in der Stadt ist. [BNK450B] Komm, gib mir 'nen größeren Batzen ab. Na komm. Ich brauch neue Klamotten. [BNK4_51] Ich finde, du siehst ok aus. [KENT] KENT PAUL-MISSIONEN [KENT1] KENT PAUL-MISSION 1 [COUNT] FÃLSCHER-MISSIONEN [COUNT1] FÃLSCHER-MISSION 1 [COUNT2] FÃLSCHER-MISSION 2 [BIKE] DIE BIKER GANG-MISSIONEN [BIKE1] BIKER-MISSION 1 [BIKE2] BIKER-MISSION 2 [BIKE3] BIKER-MISSION 3 [GOAWAY1] Komm wieder, wenn du die Haiti-Gang-Missionen abgeschlossen hast. [HAIT] DIE HAITI-GANG-MISSIONEN [HAIT1] HAITI-MISSION 1 [HAIT2] HAITI-MISSION 2 [HAIT3] HAITI-MISSION 3 [HAM3_6] ~g~Verwende das Prãzisionsgewehr, das ich für dich besorgt habe. [ROCK] DIE ROCKBAND-GANG-MISSIONEN [ROK1_4] ~g~Ok, ich glaube, das ist es, was du suchst... [ROK1_1E] ~g~Das kostet mehr als du hast! [ROK1_1F] ~g~Komm wieder, wenn du die Kohle hast. [RBM2_6] ~g~Wow! Die ist 'n Kerl! Halt ihn auf! [ROCK3] ROCKBAND-MISSION 3 [ROK3_6D] ~r~und IHR UND EUER KOPFPUTZ mit dazu! [ROK3_40] Neben dem Kühler für die Babynahrung? [RBM3_5] ~g~Bring Love Fist zu ihrem Auftritt. [CUBANM] DIE KUBA-GANG-MISSIONEN [CUBAN1] KUBA-MISSION 1 [CUBAN2] KUBA-MISSION 2 [CUB2_10] ~r~Du sollst Haitianer ausschalten, keine Kubaner! [CUBAN3] KUBA-MISSION 3 [CUBAN4] KUBA-MISSION 4 [CUB4_04] ~r~Du hast die Basis alarmiert. Jetzt kommen wir niemals rein! [CUB4_05] ~r~Du solltest doch im Auto bleiben. Jetzt lassen sie uns niemals rein. [CUB4_25] Okay, los geht's. [PROT] SCHUTZGELD-MISSIONEN [PRO1_02] ~g~Verlasse das Einkaufszentrum. [PRO3_06] ~g~Hãng die Cops ab. [PORN] PORNO-MISSIONEN [PORN1] PORNO-MISSION 1 [POR1_03] ~r~Candy ist erledigt! [PORN2] PORNO-MISSION 2 [PORN3] PORNO-MISSION 3 [POR3_18] Man hat dich bemerkt! [PORN4] PORNO-MISSON 4 [POR4_04] ~g~Die Büros liegen auf der anderen Seite dieses Tors. [PHIL] PHIL-MISSIONEN [PHIL1] PHIL-MISSION 1 [PHIL2] PHIL-MISSION 2 [PIZ1_A] PIZZABOTEN-MISSION [PIZ1_03] ~g~Fahr zurück zum Pizza-Service für weitere Bestellungen. [PIZ1_04] ~g~Hier sind die neuen Bestellungen. [PIZ1_10] Drücke die ~h~R3-Taste~w~, um die Pizza-Missionen abzubrechen. [CNTBUY1] Druckerei gekauft: $ ~1~ [CARBUY] Autohaus gekauft: $ ~1~ [PORNBUY] Filmstudio gekauft: $ ~1~ [ICEBUY] Eiscremefabrik gekauft: $ ~1~ [TAXIBUY] Taxiunternehmen gekauft: $ ~1~ [BANKBUY] Malibu Club gekauft: $ ~1~ [BOATBUY] Bootswerft gekauft: $ ~1~ [PRNT_NO] Zurzeit kannst du die Druckerei nicht kaufen. Komm spãter wieder. [CAR_NO] Zurzeit kannst du das Autohaus nicht kaufen. Komm spãter wieder. [PORN_NO] Zurzeit kannst du das Filmstudio nicht kaufen. Komm spãter wieder. [ICE_NO] Zurzeit kannst du die Eiscremefabrik nicht kaufen. Komm spãter wieder. [TAXI_NO] Zurzeit kannst du das Taxiunternehmen nicht kaufen. Komm spãter wieder. [BANK_NO] Zurzeit kannst du den Malibu Club nicht kaufen. Komm spãter wieder. [BOAT_NO] Zurzeit kannst du die Bootswerft nicht kaufen. Komm spãter wieder. [PRNT_R3] Drücke R3, um die Druckerei zu kaufen. Preis: $~1~ [CAR_R3] Drücke R3, um das Autohaus zu kaufen. Preis: $~1~ [PORN_R3] Drücke R3, um das Filmstudio zu kaufen. Preis: $~1~ [ICE_R3] Drücke R3, um die Eiscremefabrik zu kaufen. Preis: $~1~ [TAXI_R3] Drücke R3, um das Taxiunternehmen zu kaufen. Preis: $~1~ [BANK_R3] Drücke R3, um den Malibu Club zu kaufen. Preis: $~1~ [BOAT_R3] Drücke R3, um die Bootswerft zu kaufen. Preis: $~1~ [COL2_6] Keine Bewegung, amerikanisches Imperialistenschwein! [COL2_6B] Das ist Eigentum des französischen Staates. [COL2_6C] Her damit! [COL3_A] Thomas, schön, dass Sie da sind. [COL3_B] Verzeihen Sie, dass ich direkt zum Geschãftlichen komme. [COL3_C] Diaz bat mich, eine kleine Transaktion für ihn zu überwachen. [COL3_D] Hoffentlich lãuft es diesmal besser als zuletzt. [COL3_E] Deshalb wende ich mich ja an Sie, mein Freund. [COL3_F] Ich habe im Parkhaus ein wenig Artillerie deponiert. [COL3_G] Holen Sie sich die und beschützen Sie Diaz' Mãnner bei der Übergabe. [COL4_2] Ich weiß nicht, Sir! [COL4_5] Sir, zu Befehl, Sir! [COL4_10] Gehen wir ein paar Donuts essen. [COL4_16] Fahrzeug wird weggeschafft, Sir! [COL4_25] Selbstzerstörung des Fahrzeugs eingeleitet! [COL5_6] Mercedes - dieses Kind bringt mich noch ins Grab. [COL5_8] Verdammte Bande! [COL5_5] Nehmt das, Franzosenschweine! [CNT2_1] Macht ihn fertig! [CNT2_2] Hol die Platten! [CNT2_3] Beschütze den Kurier! [FINKILL] Ok, Jungs, macht ihn fertig! [FIN_1A] Komm her, du hinterhãltiger Dreckskerl! [FIN_1B] Jetzt bist du fãllig, du mieser Verrãter! [FIN_1C] Das wird dein letzter 'Dance', Lance Vance! [FIN_2B] Ach, wirklich? [FIN_2C] Der dumme Spruch war schon im Kindergarten alt! [FIN_3] Keiner da, um dich rauszuhauen, diesmal, hã, Tommy? [FIN_4] Du bist am Ende, Tommy. [FIN_5] Du hast dich auf die falsche Seite geschlagen, Lance... [FIN_6] Sonny ist oben am Safe mit MEINEM Geld... [FIN_10] Sonny? SONNY! Jetzt bist du fãllig! [FIN_11A] Du hast mir 15 Jahre gestohlen, Sonny. [FIN_11B] Und das wirst du mir jetzt büßen! [FIN_12A] Du kapierst es immer noch nicht, oder? [FIN_12B] Dein Arsch gehört mir, Tommy. [FIN_12C] Diese 15 Jahre hast du anstelle von mir abgesessen! [FIN_13] Schnappt ihn euch. Er hat nie was begriffen. [RACES_4] 3 [RACES_5] 2 [RACES_6] 1 [RACES_7] LOS! [RACES_9] Zeit: ~1~:~1~ [RACES] ZEIT: [RACES17] Neue Bestzeit: ~1~:~1~ [RACES18] PREISGELD: $~1~ [RACES20] Neue Bestzeit: ~1~:0~1~ [RACES21] Zeit: ~1~:0~1~ [RCH1_1] ~g~Benutze den ferngesteuerten Helikopter, den RC Raider, um die Checkpoints zu PASSIEREN. [RCH1_2] ~g~Die CHECKPOINTS sind überall auf dem Flughafen verteilt. [RCH1_3] ~g~Du hast ~c~8 MINUTEN~g~, um alle ~c~20 zu passieren! [RCH1_5] Zeit [RCRC1_2] ~g~Begib dich zur Startlinie! [RCRC1_4] ~g~3 [RCRC1_5] ~g~2 [RCRC1_6] ~g~1 [RCRC1_7] ~g~LOS! [RCRC1_8] ~g~Absolvierte Zeit: ~1~ Sekunden [RCPL1_1] ~g~Liefere dir mit 3 anderen RC Barons ein CHECKPOINT-RENNEN. [RCPL1_2] ~g~Du musst durch die ~o~MITTLERE MARKIERUNG ~g~hindurch, um einen Checkpoint zu passieren. [RCPL1_3] ~g~Begib dich zur Startlinie! [ICC1_O] Was ist denn in Sie gefahren? [FEA_2SP] 2 Boxen [FEA_4SP] Mehr als 2 Boxen [FEA_EAR] Kopfhörer [FEA_NAH] KEINE AUDIO HARDWARE [FET_APP] ÜBERNEHMEN [FES_SKN] SKIN-NAME [FES_DAT] DATUM [FES_SET] Skin verwenden [FET_DEF] Standard wiederherst. [FESZ_QZ] Dieses Spiel wirklich speichern? [FES_SCG] Laufendes Spiel speichern? [FES_LCG] Spiel laden und weiterspielen? [FEC_FIR] Feuern [FEC_NWE] Nãchste Waffe [FEC_PWE] Vorherige Waffe [FEC_FOR] Vorwãrts [FEC_BAC] Rückwãrts [FEC_LEF] Links [FEC_RIG] Rechts [FEC_ZIN] Heranzoomen [FEC_ZOT] Hinauszoomen [FEC_EEX] Ein-/Aussteigen [FEC_RAD] Radio [FEC_SUB] Spezialmission [FEC_CMR] Blickwinkel ãndern [FEC_JMP] Springen [FEC_SPN] Sprinten [FEC_HND] Handbremse [FEC_LOL] Nach links sehen [FEC_LOR] Nach rechts sehen [FEC_NTR] Nãchstes Ziel [FEC_PTT] Vorheriges Ziel [FEC_LBA] Nach hinten sehen [FEC_CEN] Kamera zentrieren [FET_CCN] Classic [FET_SCN] Standard [FET_CFT] ZU FUSS [FET_CCR] IN FAHRZEUG [FET_CAC] AKTION [FEC_IBT] - [FEC_MXO] MXB1 [FEC_MXT] MXB2 [FEC_UNB] NICHT BEL. [FEC_TFL] Links sehen + Geschütz L [FEC_TFR] Rechts sehen + Geschütz R [FEC_MWF] MAUSRAD AUFW. [FEC_MWB] MAUSRAD ABW. [FEC_ORR] oder [FEC_NUS] NICHT VERWENDET [FEC_LUD] Aufw. sehen [FEC_LDU] Abw. sehen [FEC_CMP] COMBO: L+R SEHEN [LAW_1A] law_1a [LAW_1B] law_1b [LAW_2A] law_2a [LAW_2B] law_2b [FEH_STA] STATISTIKEN [FEH_LOA] LADEN [FEH_CON] STEUERUNG [FEH_AUD] AUDIO [FEH_DIS] ANZEIGE [FEH_LAN] SPRACHE [FEH_SGA] NEUES SPIEL STARTEN [FEO_CON] Controller-Setup [FEO_AUD] Audio-Setup [FEO_DIS] Anzeigen-Setup [FEO_LAN] Sprachen-Setup [FEO_PLA] Spieler-Setup [FEA_OUT] Ausgabe [FEA_ST] Stereo [FEA_DTS] DTS [FEA_RSS] Radiosender [FEA_NON] RADIO AUS [FEA_FM0] WILDSTYLE [FEA_FM1] FLASH FM [FEA_FM2] KCHAT [FEA_FM3] FEVER 105 [FEA_FM4] VROCK [FEA_FM5] VCPR [FEA_FM7] EMOTION 98.3 [FEA_FM8] WAVE 103 [FED_BRI] Helligkeit [FED_TRA] Unschãrfe-FX [FED_SUB] Untertitel [FED_WIS] Breitbild [FED_POS] Anzeige-Position [FEP_RES] Fortsetzen [FEP_STG] Spiel starten [FEP_STA] Statistiken [FEP_BRI] Missionsinfos [FEP_OPT] Optionen [FEP_QUI] Spiel beenden [FES_LOA] Spiel laden [FES_DEL] Spiel löschen [FEC_CSU] Controller-Setup [FEC_RED] Steuerung ãndern [FEC_MOU] Maus-Einstellg. [DISTGOL] Mit Golfwagen zurückgel. Entfernung (Meilen) [DISTGOM] Mit Golfwagen zurückgel. Entfernung (Meter) [ST_FAVR] Lieblings-Radiosender [ST_WSTR] Unbeliebtester Radiosender [ST_FAVV] Lieblingsfahrzeug [ST_STAR] Anzahl angehãufter Fahndungssterne [ST_HEAD] Anzahl Köpfe [ST_GANG] Unbeliebteste Gang [ST_STGN] Anzahl losgewordener Fahndungssterne [TYREPOP] Zerschossene Reifen [TYRESLA] Aufgeschlitzte Reifen [ST_BRK] Erledigte Gegner im Chaos-Derby [ST_LTBR] Lãngste Zeit im Chaos-Derby (Sekunden) [ST_GNG1] Kubaner [ST_GNG2] Haitianer [ST_GNG3] Möchtegern-Gangster [ST_GNG4] Diaz' Gang [ST_GNG5] Sicherheitsbeamte [ST_GNG6] Biker-Gang [ST_GNG7] Vercetti-Gang [ST_GNG8] Golfer [FEA_FM6] ESPANTOSO [ST_ASSI] Ausgeführte Attentãtermissionen [DISTBIK] Mit Motorrad zurückgel. Strecke (Meilen) [DISTBIM] Mit Motorrad zurückgel. Strecke (Meter) [HOTEL] Ocean View Hotel [KICK1_9] VERBLEIBENDE CHECKPOINTS: [FIN_B6] Du hast nicht genug Geld, um diese Mission zu beginnen. [TEX3_9] ~g~Steuere den Helikopter über eine Bombe, um sie aufzunehmen. [HELP22] Begib dich zu dem grünen Haus-Symbol auf dem Radar. [FES_SSC] Daten wurden gespeichert. Weiter mit OK. [FES_DSC] Daten wurden gelöscht. Weiter mit OK. [FESZ_QC] Dieses beschãdigte Spiel überschreiben? [FES_CHE] Achtung! Ein oder mehrere Cheats sind aktiviert, dies kann sich auf die Speicherung auswirken. Es wird empfohlen, dieses Spiel nicht zu speichern. [FET_SG] SPIEL SPEICHERN [FEH_BRI] MISSIONSINFO [FEH_MAP] KARTE [FEM_OK] OK [FEC_CRO] Ducken [FEC_CR3] Ducken (L3-Taste) [FEC_SMT] Spezialmission [FEC_SM3] Spezialmission (R3-Taste) [FEC_RSC] Radiosender [ST_PR01] Flieger [ST_PR02] Luftwaffensoldat [ST_PR03] Gefreiter [ST_PR04] Unteroffizier [ST_PR05] Leutnant [ST_PR06] Stabsunteroffizier [ST_PR07] Hauptmann [ST_PR08] Könner [ST_PR09] Profi [ST_PR10] Roter Baron [ST_PR11] Wildgans [ST_PR12] Viper [ST_PR13] Falke [ST_PR14] Adler [ST_PR15] Blitz [ST_PR16] Tornado [ST_PR17] Taifun [ST_PR18] Luftwaffengeneral [ST_PR19] Ass [FET_LG] SPIEL LADEN [CAR_EXP] Straßenfahrzeuge zerstört [BOA_EXP] Boote zerstört [HEL_DST] Flugzeuge & Helikopter zerstört [STFT_01] Schnellste Zeit bei 'Wheels of Steels' [STFT_02] Schnellste Zeit bei 'Der Fahrer' [STFT_03] Schnellste Zeit auf Gelãndemotorradstrecke [STFT_04] Schnellste Zeit bei Modellflugzeug-Rennen [STFT_05] Schnellste Zeit mit den ferngesteuerten Autos [STFT_06] Schnellste Zeit bei Helikopter-Rennen [STFT_07] Schnellste Zeit bei 'Todeskaracho' [STFT_08] Schnellste Zeit bei 'Ocean Drive' [STFT_09] Schnellste Zeit bei 'Küsten-Rallye' [STFT_10] Schnellste Zeit bei 'Capital Cruise' [STFT_11] Schnellste Zeit bei 'Tour!' [STFT_12] Schnellste Zeit bei 'V.C. Endurance' [STHC_01] High-Score bei Schießstand-Mission [STHC_02] Beste Trefferquote am Schießstand (in Prozent) [STHC_03] Anzahl getãtigter Drogendeals [HELP24] Du kannst jetzt Auftrãge vom Colonel annehmen. [HELP25] Du kannst jetzt Auftrãge von Avery Carrington annehmen. [HELP29] Außerhalb eine Mision kannst du zu dem Bekleidungsgeschãft gehen. [HELP30] Wenn du neue Klamotten kaufst, reduziert sich dein Fahndungslevel auf null. [ASM4_24] Entfernung: [RBM1_6] ~g~Bring Mercedes und den 'Love Juice' zu der Band ins Aufnahmestudio. [RBM1_3] NICHT MEHR BENÖTIGT [HAM1_5] NICHT MEHR BENÖTIGT [RBM1_11] NICHT MEHR BENÖTIGT [HELP31] Um aus dem fahrenden Fahrzeug zu schießen, sieh zuerst mit ~k~~VEHICLE_LOOKLEFT~ oder ~k~~VEHICLE_LOOKRIGHT~ nach links oder rechts. [HELP34] Du brauchst eine Maschinenpistole für einen 'Drive-By'. [STRIP_1] ~r~Nicht genug Cash, du windiger Geizhals! [EXIT_1] ~k~~PED_SPRINT~ zum Beenden. [ASM1_B] Ihr nãchster Auftrag klebt unter dem Telefon. [ASM1_C] Ich habe noch mehr Arbeit, die eine eher 'zupackende' Art verlangt. [SCARF] Apartment 3c [LAW4_10] Reiche Manager sind Schweine! [RCH1_6] ~g~Benutze den ferngesteuerten Helikopter, um Checkpoints überall auf dem Flughafen abzufliegen. [RCH1_9] ~b~GESAMTZEIT: ~1~:~1~ [RCH1_10] ~b~GESAMTZEIT: ~1~:0~1~ [WHEEL01] ZWEIRAD DOPPELBONUS: $ ~1~ Distanz: ~1~.~1~m Zeit: ~1~ Sekunden [WHEEL02] ZWEIRAD DOPPELBONUS: $ ~1~ Distanz: ~1~ Fuß Zeit: ~1~ Sekunden [WHEEL03] ZWEIRAD BONUS: $ ~1~ Zeit: ~1~ Sekunden [WHEEL04] ZWEIRAD BONUS: $ ~1~ Distanz: ~1~.~1~m [WHEEL05] ZWEIRAD BONUS: $ ~1~ Distanz: ~1~ Fuß [WHEEL06] WHEELIE BONUS: $ ~1~ Distanz: ~1~.~1~m Zeit: ~1~ Sekunden [WHEEL07] WHEELIE BONUS: $ ~1~ Distanz: ~1~ Fuß Time: ~1~ Sekunden [WHEEL08] WHEELIE BONUS: $ ~1~ Zeit: ~1~ Sekunden [WHEEL09] WHEELIE BONUS: $ ~1~ Distanz: ~1~.~1~m [WHEEL10] WHEELIE BONUS: $ ~1~ Distanz: ~1~.~1~ Fuß [WHEEL11] VOLLBREMSUNGSBONUS: $ ~1~ Distanz: ~1~.~1~m Zeit: ~1~ Sekunden [WHEEL12] VOLLBREMSUNGSBONUS: $ ~1~ Distanz: ~1~ Fuß Zeit: ~1~ Sekunden [WHEEL13] VOLLBREMSUNGSBONUS: $ ~1~ Zeit: ~1~ Sekunden [WHEEL14] VOLLBREMSUNGSBONUS: $ ~1~ Distanz: ~1~.~1~m [WHEEL15] VOLLBREMSUNGSBONUS: $ ~1~ Distanz: ~1~ Fuß [ROK3_72] Love Fist! [ROK3_74] Ah, seht mal, was ist das? Hey, Tommy, leg mal dieses Band ein. [POR1_19] Hey! [DESPERA] Desperado [MOB_99A] Begib dich zu dem Fernsprecher neben dem Einkaufszentrum in Washington Beach. [MOB_98A] Begib dich zu dem Fernsprecher in Vice Point. [MOB_96A] Begib dich zu dem Fernsprecher beim Flughafen-Terminal. [MOB_95A] Begib dich zu dem Fernsprecher in Little Havana. [BNK1_1] Kann ich Ihnen helfen, Sir? [BNK1_2] Der Typ ist verkleidet! [BNK1_3] Er ist verrückt geworden! [BNK1_4] Wer zum Teufel sind Sie? [BNK1_5] Wo ist Ihre Dienstmarke? [BNK1_6] Da sind sie! Feuer frei! [MOB_24A] Hallo, spricht da Mr. Vercetti? [MOB_24B] Ja. [MOB_24C] Hier Cortez. Sie waren bei meiner Party. [MOB_24D] Ja. Ich erinnere mich. [MOB_24E] Mr. Vercetti, es war höchst unglücklich, was da bei der Abwicklung Ihres Geschãfts vorgefallen ist. [MOB_24F] Ich weiß. [MOB_24G] Sie sollen wissen, dass ich und meine Leute alles tun, um der Sache auf den Grund zu gehen. [MOB_24H] Falls Sie mit mir persönlich sprechen wollen, finden Sie mich auf dem Schiff. Guten Tag, Senor. [BNK2_6] Der Typ ist geisteskrank! [ANGEL] Angel [CUBJET] Kubanischer Jetmax [SANDKIN] Sandking [POLMAV] Polizei-Maverick [BOXVILL] Boxville [BENSON] Benson [HOTRINA] Hotring Racer [HOTRINB] Hotring Racer [BLOODRA] Chaos-Derby Banger [BLOODRB] Chaos-Derby Banger [MAFIACR] Mafia Cruiser [COP_M2] 'EINSATZ IN VICE CITY' [COP_M3] 'DONNER ÜBER VICE CITY' [BNK3_2] Ich fahre nicht für dich, niemals. Das erzãhle ich in der Therapie. [FEM_SL1] Datei 1 nicht vorhanden [FEM_SL2] Datei 2 nicht vorhanden [FEM_SL3] Datei 3 nicht vorhanden [FEM_SL4] Datei 4 nicht vorhanden [FEM_SL5] Datei 5 nicht vorhanden [FEM_SL6] Datei 6 nicht vorhanden [FEM_SL7] Datei 7 nicht vorhanden [FEM_SL8] Datei 8 nicht vorhanden [FEA_CHA] Tonausgabe wird auf STEREO umgeschaltet. Bitte warten... [FEA_CHD] Achtung! Sie schalten die Tonausgabe von STEREO auf DTS um. Bitte warten... [FEI_SEL] Auswahl [FEI_BAC] Zurück [FEI_RES] Weiter [FEI_NAV] Navigieren [FEI_BTX] /-Taste - [FEI_BTT] "-Taste - [FEI_STA] START-Taste - [FEI_BTD] ; = > < - [FEI_STO] Stop [MOB_68A] Tommy, Alter, ich hab eine Überraschung für dich. [MOB_68B] Ich bin im Aufnahmestudio mit ein paar super Musikern. [MOB_68C] Warum kommst du nicht kurz vorbei? [MOB_68D] Kannst dir denken, dass es sich lohnt, oder? Bis dann. [OUTFT1] Straße [OUTFT2] Abendgarderobe [OUTFT3] Overall [OUTFT4] Country Club [OUTFT5] Havana [OUTFT6] Cop [OUTFT7] Bankrãuber [OUTFT8] Freizeit [OUTFT9] Mr. Vercetti [OUTFT10] Trainingsanzug [OUTFT13] MC Tommy [CAR_AS1] AUTOHAUS ERWORBEN [CAR_AS2] ~g~Sunshine Autos generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmãßig ab. [BUYSAVE] ~g~Außerhalb einer Mission kannst du dein Spiel hier umsonst speichern. [BUYGARG] ~g~Du kannst in dieser Garage auch Autos abstellen. [STRPBUY] Pole Position Club gekauft: $ ~1~ [STRP_R3] Drücke R3, um den Pole Position Club zu kaufen. Preis: $~1~ [NBMN_R3] Drücke R3, um Elswanko Casa zu kaufen. Preis: $~1~ [GA_4] Autobomben kosten $1000 pro Stück. [GA_5] In deinem Wagen ist schon eine Autobombe. [GA_6] { reVC update } Park die Karre, mach sie durch Drücken der ~h~~k~~VEHICLE_FIREWEAPON~~w~ scharf und dann HAU AB! [GA_7] { reVC update } Mach die Bombe mit der ~h~~k~~VEHICLE_FIREWEAPON~~w~ scharf. Dann geht sie hoch, wenn der Wagen angelassen wird. [GA_6B] { reVC update } Park die Karre, mach sie durch Drücken der ~h~~k~~VEHICLE_FIREWEAPON~~w~ scharf und dann HAU AB! [GA_7B] { reVC update } Mach die Bombe mit der ~h~~k~~VEHICLE_FIREWEAPON~~w~ scharf. Dann geht sie hoch, wenn der Wagen angelassen wird. [MOB_70A] Tommy, ich bin's, Colonel Cortez. Hören Sie, Sie sind doch ein Mann, der Dinge zu erledigen weiß. [MOB_70B] Sie finden mich auf dem Boot. [PICK2] .357 in Verstecken angeliefert! [PICK3] Kettensãgen in Verstecken angeliefert! [PICK4] Flammenwerfer in Verstecken angeliefert! [PICK5] .308 Prãzisionsgewehr in Verstecken angeliefert! [PICK6] Mini-Kanonen in Verstecken angeliefert! [PICK7] Raketenwerfer in Verstecken angeliefert! [PICK8] Sea Sparrow jetzt bei Vercetti Estate verfügbar! [PICK9] Panzer jetzt in Army-Kaserne verfügbar! [PICK10] Hunter jetzt in Army-Kaserne verfügbar! [CLOTH1] Abendgarderobe bei Rafaels in Ocean Beach erhãltlich. [CLOTH2] Straßenkleidung in Verstecken angeliefert. [CLOTH3] Overall bei 'Tooled Up' im North Point Einkaufszentrum erhãltlich. [CLOTH4] Country Club-Bekleidung beim Golf Club in Leaf Links erhãltlich. [CLOTH5] Havana-Outfit bei 'Little Havana Streetwear' in Little Havana erhãltlich. [CLOTH6] Polizeiuniform bei Polizeistation in Washington Beach erhãltlich. [CLOTH7] Freizeitbekleidung bei 'Gash' im North Point Einkaufszentrum erhãltlich. [CLOTH8] Mr. Vercetti-Outfit bei 'Collar & Cuffs'in Ocean Beach erhãltlich. [CLOTH9] Trainingsanzug bei 'Jocksport' in Downtown erhãltlich. [CLOTH10] Bankrãuber-Outfit beim Malibu Club in Vice Point erhãltlich. [MOB_62A] Tommy, hier Ricardo Diaz. Ich wollte dir danken, dass du mich gerettet hast. [MOB_62B] Ich hab den Trottel von Cortez gefragt. Er meint, du wãrst ein Mann für alle Fãlle. Komm doch mal bei mir vorbei. [MOB_62C] Ich brauche einen Kerl wie dich. Ich hab nãmlich nur Schwachköpfe. [MOB_62D] Nur lauter Schwachköpfe. Ich mache dich schwer reich. [GOAWAY2] Komm wieder, wenn du die Biker Gang-Missionen abgeschlossen hast. [COL2_9] Du amerikanischer Idiot! Sie sind dir hierher gefolgt! [LOADCOL] Lade... [STFT_17] Schnellste Zeit bei 'PCJ Rallye' [STFT_18] Schnellste Zeit bei 'Krasses Gelãnde' [STFT_19] Schnellste Zeit bei 'Teststrecke' [NEW_REC] Neuer Rekord!! ~1~ Minuten und ~1~ Sekunden. [BMX_HOW] ~g~Fahr zwei Runden auf der Gelãndemotorradstrecke. ~y~Passiere dabei ~g~die ~y~CHECKPOINTS~g~! [BMXREW1] ~g~Jedes Mal wenn du deine bisherige Bestzeit für die zwei Runden verbesserst, [BMXREW2] ~g~bekommst du eine noch höhere ~y~BELOHNUNG~g~! [BMXRAIN] ~g~Sieht nach Regen aus... [ITBEG] Am Anfang... [NBMNBUY] El Swanko Casa gekauft: $ ~1~ [LNKVBUY] Links View Apartment gekauft: $ ~1~ [HYCOBUY] Hyman Condo gekauft: $ ~1~ [BUYGARS] ~g~Du kannst in diesen Garagen auch Autos abstellen. [OCHEBUY] Ocean Heights Apartment gekauft: $ ~1~ [WASHBUY] 1102 Washington Street gekauft: $ ~1~ [VCPTBUY] 3321 Vice Point gekauft: $ ~1~ [HELP6_C] Drücke die ~h~~k~~VEHICLE_HANDBRAKE~~w~, um die Handbremse anzuziehen. [HELP2_A] Drücke die ~h~~k~~PED_SPRINT~~w~, um zu ~h~sprinten. [HELP4_A] Drücke die ~h~~k~~VEHICLE_ACCELERATE~~w~, um zu beschleunigen. [HELP5_A] Drücke die ~h~~k~~VEHICLE_BRAKE~~w~, um zu bremsen, oder um zurückzusetzen, wenn das Fahrzeug steht. [HELP8_A] Drücke die ~h~~k~~PED_SNIPER_ZOOM_IN~~w~, um an das Ziel heranzuzoomen und die ~x~/-Taste~w~,um herauszuzoomen. [PBOAT_1] { reVC update } Drücke die~h~ ~k~~VEHICLE_FIREWEAPON~~w~, um die Bordkanonen abzufeuern. [SEG3_4] { reVC update } ~g~Um Bomben aufzunehmen, steuere den RC Raider einfach nahe an sie heran. Um eine abzuwerfen, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~g~-Taste. [RCR1_3] { reVC update } ~g~Wenn du diese Mission abbrechen willst, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~g~, um dein Auto zu sprengen. [HELP32] { reVC update } Dann feuere mit der ~h~~k~~VEHICLE_FIREWEAPON~. [HELP33] { reVC update } Dann feuere mit der ~h~~k~~VEHICLE_FIREWEAPON~. [TTUTOR] Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Taxi-Missionen an- oder abzuschalten. [TTUTOR2] Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Taxi-Missionen an- oder abzuschalten. [FTUTOR] Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Feuerwehrwagen-Missionen an- oder abzuschalten. [FTUTOR2] Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Feuerwehrwagen-Missionen an- oder abzuschalten. [CTUTOR] Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Bürgerwehr-Missionen zu aktivieren oder zu deaktivieren. [CTUTOR2] Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Bürgerwehr-Missionen zu aktivieren oder zu deaktivieren. [HELP8_B] Drücke die~h~ ~k~~PED_SNIPER_ZOOM_IN~~w~, um ~h~an das Ziel heranzuzoomen ~w~und die~h~ ~k~~PED_SNIPER_ZOOM_OUT~~w~, um ~h~herauszuzoomen~w~. [ATUTOR3] Drücke die ~h~~k~~TOGGLE_SUBMISSIONS~~w~, um Krankenwagen-Missionen an- oder abzuschalten. [GUN_H1] ~w~Drück die~h~ ~k~~PED_SPRINT~~w~, um zu kaufen. ~w~Drück die~h~ ~k~~VEHICLE_ENTER_EXIT~~w~, um zu gehen. [PU_CF3] { reVC update } Drück die ~h~~k~~PED_ANSWER_PHONE~~w~, um die augenblickliche Waffe in diesem Slot auszutauschen. [PU_CF4] { reVC update } Drück die ~h~~k~~PED_ANSWER_PHONE~~w~, um die augenblickliche Waffe in diesem Slot auszutauschen. [HELP9_B] Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um das Prãzisionsgewehr ~h~abzufeuern~w~. [HELP37] Wenn du doch nicht in ein Auto einsteigen willst, das du im Begriff bist, zu klauen, drück die ~h~~k~~PED_SPRINT~. [HELP6_A] Drücke die ~h~~k~~VEHICLE_HANDBRAKE~~w~, um die Handbremse anzuziehen. [HELP6_D] Drücke die ~h~~k~~VEHICLE_HANDBRAKE~~w~, um die Handbremse anzuziehen. [HELP26] Drücke die ~h~~k~~VEHICLE_ENTER_EXIT~~w~, um in ein Fahrzeug ein- oder auszusteigen. [HELP27] Drücke ~h~~k~~VEHICLE_TURRETUP~~w~ oder ~h~~k~~VEHICLE_TURRETDOWN~~w~ um dein Gewicht auf einem Motorrad zu verlagern. [HELP28] Drücke ~h~~k~~VEHICLE_TURRETUP~~w~ oder ~h~~k~~VEHICLE_TURRETDOWN~~w~ um dein Gewicht auf einem Motorrad zu verlagern. [HELP35] Benutze die ~h~~k~~GO_LEFT~~w~ oder ~h~~k~~GO_RIGHT~~w~ um das Fahrzeug zu steuern. [HELP36] Benutze die ~h~~k~~GO_LEFT~~w~ oder ~h~~k~~GO_RIGHT~~w~ um das Fahrzeug zu steuern. [HELP42] Folge dem ~q~rosa Symbol~w~, um zum Hotel zu kommen. [HELP19] Stell dich in die ~q~rosa Markierung~w~, um weiterzumachen. [HELP1] Halte in der Mitte der ~q~rosa Markierung. [HELP12] Geh ins Zentrum der ~q~rosa Markierung~w~, um eine Mission zu starten. [SEG3_6] ~g~Um eine Zielzone zu treffen, musst du die Bombe innerhalb der ~q~rosa Markierung~g~ abwerfen. Die Reihenfolge spielt dabei keine Rolle. [S_PROMP] Außerhalb einer Mission kannst du dein Spiel speichern, indem du das ~h~Cassetten-Symbol aufnimmst. [HELP16] Geh durch die Eingangstür, um das ~h~Ocean View Hotel~w~ zu betreten. [HELP43] ~g~Begib dich zum ~h~Ocean View Hotel~g~ am Ocean Drive. [HELI_F1] ~r~Heli-Ceckpoint-Mission abgebrochen! [AMMUHLP] Wenn du Waffen brauchst, geh zu ~h~AmmuNation~w~. Das ~h~Pistolensymbol~w~ auf dem Radar zeigt dir den Weg. [HELI_1] Downtown Heli-Checkpoint [HELI_2] Ocean Beach Heli-Checkpoint [HELI_3] Vice Point Heli-Checkpoint [HELI_4] Little Haiti Heli-Checkpoint [FST_MFR] Lieblings-Radiosender [FST_LFR] Unbeliebtester Radiosender [FEI_HOL] Halten [FEI_ZOO] Zoom [FEI_BTR] > < - [FEI_NA] Nicht verfügbar [MESA] Mesa Grande [STRP_NO] Zurzeit kannst du die Stripper-Bar nicht kaufen. Komm spãter wieder. [CHSE] VERFOLGUNGSJAGD [NBMN_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um El Swanko Casa zu kaufen. Preis: $~1~ [NBMN_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um El Swanko Casa zu kaufen. Preis: $~1~ [NBMN_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um El Swanko Casa zu kaufen. Preis: $~1~ [LNKV_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Links View Apartment zu kaufen. Preis: $~1~ [LNKV_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Links View Apartment zu kaufen. Preis: $~1~ [LNKV_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Links View Apartment zu kaufen. Preis: $~1~ [HYCO_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Hyman Condo zu kaufen. Preis: $~1~ [HYCO_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Hyman Condo zu kaufen. Preis: $~1~ [HYCO_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Hyman Condo zu kaufen. Preis: $~1~ [OCHE_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Ocean Heights Apartment zu kaufen. Preis: $~1~ [OCHE_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Ocean Heights Apartment zu kaufen. Preis: $~1~ [OCHE_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Ocean Heights Apartment zu kaufen. Preis: $~1~ [WASH_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 1102 Washington Street zu kaufen. Preis: $~1~ [WASH_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 1102 Washington Street zu kaufen. Preis: $~1~ [WASH_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 1102 Washington Street zu kaufen. Preis: $~1~ [VCPT_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 3321 Vice Point zu kaufen. Preis: $~1~ [VCPT_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 3321 Vice Point zu kaufen. Preis: $~1~ [VCPT_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um 3321 Vice Point zu kaufen. Preis: $~1~ [PRNT_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Druckerei zu kaufen. Preis: $~1~ [PRNT_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Druckerei zu kaufen. Preis: $~1~ [PRNT_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Druckerei zu kaufen. Preis: $~1~ [CAR_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Autohaus zu kaufen. Preis: $~1~ [CAR_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Autohaus zu kaufen. Preis: $~1~ [CAR_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Autohaus zu kaufen. Preis: $~1~ [PORN_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Filmstudio zu kaufen. Preis: $~1~ [PORN_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Filmstudio zu kaufen. Preis: $~1~ [PORN_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Filmstudio zu kaufen. Preis: $~1~ [ICE_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Eiscremefabrik zu kaufen. Preis: $~1~ [ICE_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Eiscremefabrik zu kaufen. Preis: $~1~ [ICE_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Eiscremefabrik zu kaufen. Preis: $~1~ [TAXI_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Taxiunternehmen zu kaufen. Preis: $~1~ [TAXI_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Taxiunternehmen zu kaufen. Preis: $~1~ [TAXI_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um das Taxiunternehmen zu kaufen. Preis: $~1~ [BANK_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Malibu Club zu kaufen. Preis: $~1~ [BANK_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Malibu Club zu kaufen. Preis: $~1~ [BANK_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Malibu Club zu kaufen. Preis: $~1~ [BOAT_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Bootswerft zu kaufen. Preis: $~1~ [BOAT_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Bootswerft zu kaufen. Preis: $~1~ [BOAT_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um die Bootswerft zu kaufen. Preis: $~1~ [STRP_L] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Pole Position Club zu kaufen. Preis: $~1~ [STRP_T] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Pole Position Club zu kaufen. Preis: $~1~ [STRP_C] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~, um den Pole Position Club zu kaufen. Preis: $~1~ [STOCK] ~r~nicht vorrãtig [HELP14] Um das Büro des Anwalts zu finden, folge dem ~h~'L'~w~ auf dem Radar. [BOAT_AS] ~g~Die Bootswerft generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmãßig ab. [BOAT_A2] BOOTSWERFT-MISSIONEN ERFÜLLT [BOAT_N] Checkpoint Charlie [BOAT_P] ~g~Sammle die Pãckchen ein, ehe die Zeit um ist. [FEI_R1B] R1- \ R2-Taste - [HELP9_A] Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Prãzisionsgewehr abzufeuern. [HELP21] Drücke die ~h~~k~~VEHICLE_ENTER_EXIT~~w~-Taste, um in ein Fahrzeug ein- oder auszusteigen. [CREAM] Stoff-Auslieferung [UMBERTO] Café Robina [PU_CF1] Drück die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um diese Waffe aufzunehmen. Falls du schon eine Waffe von diesem Typ hast, wird sie durch die neue ersetzt. [FED_RDM] KARTE & SYMBOLE [FEC_ILU] Kamera invertieren : [NITRO] Alle Taxis haben jetzt einen Jumper-Turbo! Du musst nur die Taste für die Hupe drücken. [RATNG53] Windei [RATNG54] Niete [RATNG55] Hacker [RATNG56] Mogelpaket [RATNG57] Totaler Lügner [STHC_04] High-Score beim Fußball-Hochhalten [STHC_05] Bestes Hotring-Resultat [STFT_13] Schnellste Zeit bei Downtown Heli-Checkpoint [STFT_14] Schnellste Zeit Ocean Beach Heli-Checkpoint [STFT_15] Schnellste Zeit bei Vice Point Heli-Checkpoint [STFT_16] Schnellste Zeit bei Little Haiti Heli-Checkpoint [STFT_21] Schnellste Zeit bei Hotring [STFT_22] Schnellste Rundenzeit bei Hotring [STFT_20] Schnellste Zeit bei 'Pylonen-Rallye' [HELP44] Halte in der ~q~rosa Markierung. [HELP45] Drücke die ~h~~k~~PED_DUCK~~w~ um dich zu ducken. Dadurch erhöht sich die Treffsicherheit der Waffen, die du hãltst. [RCR1_5] RC Bandit-Rennen [RCPL1_7] RC Baron-Rennen [RCH1_11] RC Raider Checkpoint-Jagd [FEA_CTD] Achtung! Für diesen Modus muss DTS-kompatible Hardware angeschlosen sein. Fortfahren? [FEM_STE] AUF STEREO EINSTELLEN [FEM_UDY] AUF DTS EINSTELLEN [GREET] Grüße aus... [LANCE_1] Hey, Mann, fahr vorsichtiger! [LANCE_2] Hey, pass doch auf, was du machst! [LANCE_3] Hey, wo fahren wir jetzt hin? [LANCE_4] Was machen wir jetzt? [LAW4_15] Mehr Geld! [MERC_5] Schönes Auto, Mr. Vercetti. [MERC_26] SCHNELLER, SCHNELLER, SCHNELLER! [MERC_27] Vorsichtig, Tommy, ich hab mir erst letzten Monat die Nase korrigieren lassen. [MERC_28] Tommy, fahr vorsichtig. [MERC_29] Tommy, fahr langsamer. [MERC_30] Tommy, bring jemand anders um, aber bitte nicht mich. [MERC_31] Tommy, Baby, bring mich nicht um! [MERC_32] Tommy, ich bin froh, dass du dieses Auto gestohlen hast! [MERC_40] Ich hatte so viel Spaß. [MERC_43] Adios, mein Engel. [MERC_44] Und mach schön weiter Bodybuilding, hörst du? [MERC_45] Ciao, mein Hübscher. [COL5_17] Oh, Gott, sie haben einen Helikopter! [COL5_18] Schießt den Helikopter ab! [COL5_19] Tommy, schießen Sie den Helikopter ab! [COL5_20] Da kommt er wieder! Schießt den Helikopter ab! [COL5_21] Sieh dir diesen riesigen Helikopter an! [COL5_22] Da kommt er wieder! [FEA_DSM] Achtung! Dieses Spiel ist auf DTS-Tonausgabe eingestellt. Dazu muss DTS-kompatible Hardware angeschlossen sein. Bitte wãhlen Sie, ob Sie mit DTS oder STEREO-Tonausgabe fortfahren wollen. [STFT_23] Schnellste Zeit bei Checkpoint Carlie [HELP50] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um die Spielerfigur von hinten zu sehen. [HELP51] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um die Spielerfigur von hinten zu sehen. [HELP52] Drücke die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um die Spielerfigur von hinten zu sehen. [HELP53] Benutze die ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~-Taste und die ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~-Taste, um zwischen deinen Waffen zu wechseln. [HELP46] Es gibt acht verschiedene Waffentypen. [HELP47] Du kannst von jedem Waffentyp immer nur eine bei dir tragen - also einen Typ Pistole, einen Typ Schrotflinte, usw. [HELP54] ~w~Preis: $~1~ ~r~Deine augenblickliche Waffe wird ersetzt, wenn du diese kaufst. [HELP2A2] Drücke die ~h~~k~~PED_SPRINT~~w~-Taste, um zu ~h~sprinten. [HLPSN_A] Das Prãzisionsgewehr ermöglicht dir, an dein Ziel heranzuzoomen und auf größere Distanz mit hoher Genauigkeit zu schießen. [HLPSN_B] Halte die~h~ ~k~~PED_LOCK_TARGET~~w~-Taste gedrückt, um mit dem Prãzisionsgewehr zu ~h~zielen~w~. [HLPSN_C] Halte die~h~ ~k~~PED_LOCK_TARGET~~w~-Taste gedrückt, um mit dem Prãzisionsgewehr zu ~h~zielen~w~. [HLPSN_D] Drücke die ~h~~k~~PED_SNIPER_ZOOM_IN~~w~-Taste, um ~h~an das Ziel heranzuzoomen ~w~und die~h~ ~k~~PED_SNIPER_ZOOM_OUT~~w~,um ~h~herauszuzoomen~w~. [HLPSN_E] Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Prãzisionsgewehr ~h~abzufeuern~w~. [HLPSN_F] Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Prãzisionsgewehr ~h~abzufeuern~w~. [HLPSN_G] Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Prãzisionsgewehr ~h~abzufeuern~w~. [PLANE_H] Benutze die ~h~~k~~VEHICLE_ACCELERATE~~w~-Taste, um zu beschleunigen. Links bzw. Rechts für Richtungswechsel. [PLANE_4] { reVC update } {Benutze die ~h~~k~~VEHICLE_ACCELERATE~~w~-Taste, um zu beschleunigen. Links bzw. Rechts für Richtungswechsel.} Benutze den rechten Analog-Stick, um zu beschleunigen. Ziehe den linken Analog-Stick, um zu steigen oder drücke ihn nach vorn, um zu sinken. Links bzw. Rechts für Richtungswechsel. [HELP55] Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um den Küchenchef anzugreifen. [STPR_8] Pole Position Club [STPR_9] 3321 Vice Point [STPR_10] Links View Apartment [STPR_11] El Swanko Casa [STPR_12] 1102 Washington Street [STPR_13] Ocean Heights Apartment [STPR_14] Skumole Shack [STPR_15] Hyman Condo [RCCANX] ~r~Flugzeug-Mission abgebrochen. [CLT_HL2] Wenn du neue Kleider aufnimmst, werden dadurch Fahndungslevel von einem bis zwei Sternen annulliert. [CRED009] MISSION DESIGN [CRED359] LEE JOHNSON [CRED360] HENDRIK LESSER [CRED361] PASQUALE STACCHIOTTI [CRED362] ENRIQUE FERNANDEZ [CRED363] PAUL BYERS [CRED364] MIKE EMENY [CRED365] ROB DUNKIN [CRED366] CHARLIE KINLOCH [CRED367] KEVIN HOBSON [CRED368] JIM CREE [MOB_66A] Tommy, Tommy, Tommy, wieso bist du wieder zurückgekommen? [MOB_66B] Ich hab dir doch gesagt, wir wollen dich hier nicht mehr sehen. [MOB_67A] Tommy, ich glaube, du solltest dich hier nicht mehr blicken lassen, hörst du? [MOB_67B] Die Haitianer sind nicht sehr gut auf dich zu sprechen. [MOB_18A] Tommy, hier Paul. Wie geht's, Alter? Hey, ich dachte mir, das musst du hören... [MOB_18B] Echt der Hammer. Du glaubst nicht, was mir für 'ne Puppe über den Weg gelaufen ist. [MOB_18C] 'ne Bordsteinschwalbe, oder sowas. Unten in Little Havana. [MOB_18D] Sagt, sie heißt Mercedes oder so ãhnlich. [MOB_18E] Wahnsinn, Alter. Die Puppe musst du dir geben. [MOB_18F] Da würde 'nen Toter Hormonkoller kriegen. Sie sagt, ich wãr der beste, den sie je hatte. [MOB_18G] Halt die Augen nach ihr offen. Bis dann. [MOB_72A] Tommy, ich bin's, Lance. Du hãltst jetzt mal den Rand, Tommy, ich hab nãmlich keine Zeit für Geschwãtz. [MOB_72B] Interessiert mich auch nicht, was du zu sagen hast. Warum auch? Ich bin dir doch sowieso scheißegal, stimmt's? [MOB_72C] Du solltest dich mehr um mich kümmern. Mir einen fairen Anteil geben. Weißt du... [MOB_72D] Tommy... hör mal, Mann, es tut mir leid. Nur... [MOB_72E] ich werd schon mein Leben lang immer nur von oben herab behandelt, wie ein kleines Kind. [MOB_72F] Mein Bruder hat das auch immer gemacht. Bitte, mein Alter, mach du das nicht. [MOB_72G] Ich muss auflegen. [MOB_63A] Tommy, hier Earnest. Earnest Kelly. [MOB_63B] Wie geht's? [MOB_63C] Ganz gut. Werd zum Laufen 'nen Stock brauchen, müsste aber bald wieder arbeiten können. [MOB_63D] Gut. [MOB_63E] Ich hab das mit Lance gehört. Was für ein Schwein, hã? [MOB_63F] Ja. [MOB_63G] Trau nie einem Mann, der im Pyjama auf der Straße herumlãuft. Gut, dass du ihn erledigt hast. Ich hoffe, es war nicht kurz und schmerzlos. [MOB_63H] Eher nicht. Ich hãtte nur nicht gedacht, dass er so einer ist... [MOB_63I] Tommy, für einen wildgewordenen Irren bist du ziemlich naiv. Ich bin bald wieder an der Arbeit, dann bring ich dir mal ein paar Sachen übers Leben bei, ok? [MOB_63J] Lass dir Zeit, Earnest. Pass auf dich auf. [MOB_16A] Tommy, hier Paul. Wie geht's, mein Freund? [MOB_16B] Was willst du, Paul? Ich brauch keine getürkten Designer-Klamotten. [MOB_16C] Sehr witzig. Du weißt, dass ich mit getürkter Ware nichts am Hut habe. Wollte nur hören, ob ich nicht 'ne Rolle in einem von deinen Filmen kriegen könnte. [MOB_16D] In England habe ich damals viel einschlãgiges Zeug gedreht. Ich hab mehr zu bieten als du, mein Alter. [MOB_16E] Paul, danke für das Angebot. Ich komm auf dich zurück. [MOB_16F] Lass mich nicht hãngen. Denk dran, was ich alles für dich getan habe. [MOB_16G] Das versuch ich ja grade zu vergessen. [MOB_17A] Tommy Vercetti. Wie geht's, großer Meister? Man hört so einiges über dich. Bist jetzt 'ne große Nummer in der Stadt, hã? [MOB_17B] Paul, du bist betrunken. [MOB_17C] Nein, du Trottel, ich bin nicht betrunken. Hab mir nur ein paar Ladungen Stoff gegeben, war seit ein paar Tagen nicht im Bett. [MOB_17D] Und du brauchst mich nicht dumm anzureden. Ich bin nicht irgendwer. Wer hat dir denn in dieser Stadt den Weg geebnet? Ich! [MOB_17F] Tatsãchlich? [MOB_17G] Komm mir nicht so! Ich hab dich mit den ganzen Leuten bekanntgemacht. Hab dir gezeigt, wie der Hase lãuft, hab alles mögliche für dich getan, und so dankst du es mir?! [MOB_17H] Du ignorierst mich. Du gibst mir keine Chance, mitzumischen, nach allem, was ich für dich getan habe! Hãltst du mich für einen Schwachkopf? [MOB_17I] Paul, reg dich ab. Ich hatte viel zu tun. Sei kein Idiot. [MOB_17J] Ich bin kein Idiot. Das haben sie schon im Jugendknast gesagt. Wenn du Ãrger haben willst, Freundchen, den kannst du haben! [MOB_17K] Tommy, bitte! Du warst meine große Hoffnung. Bitte, mach dich nicht lustig über mich! [MOB_17L] Paul, schlaf mal 'ne Runde. Im Ernst. [MOB_73A] Tommy, hier Steve. [MOB_73B] Hey, Steve. [MOB_73C] Hey, aber wie! Du bist ein Genie! Ich bin ein Genie! Sie lieben uns alle. Wir brechen alle Rekorde, mein Alter. [MOB_73D] Uns winken ganz große Filmpreise. Jetzt kann ich endlich meinen alten Herrn ins Heim stecken und ihm sagen, er soll die Klappe halten. [MOB_73E] Ãh, das ist cool, Steve. [MOB_73F] Cool? Mann, das ist heiß! Heiß! H.E.I.ß! Er hat nie an mich geglaubt. Hat immer gedacht, ich wãre kein Künstler, und jetzt hab ich's geschafft! [MOB_73G] Ich bin der größte Porno-Regisseur aller Zeiten, mein Freund. Wollte dir nur sagen, es ist mir eine Freude, dich kennengelernt zu haben. [MOB_73H] Danke, Steve. [MOB_73I] Ich liebe dich, Baby. Bleib bloß genau so wie du bist, ok? [MOB_73J] Werd's mir merken. Ciao, Steve. [BOLLOX] Drücke die ~o~R1~w~-Taste, um eine Bombe abzuwerfen. Drücke die ~t~"~w~-Taste zum Abbrechen. [BRID_OP] Sturmwarnung vorüber. Alle Brücken zum Festland sind wieder geöffnet. [BRID_CL] Sturmwarnung: Alle Brücken zum Festland sind gesperrt. [LG_38] Ziel [ASSET_C] POLE POSITION ERWORBEN! [ASSET_D] ~g~Der Pole Position Club sorgt nun für ein Einkommen von bis zu $~1~ pro Tag. Hol dir dein Geld regelmãssig! [ST_WHEE] Lãngste 'Wheelie' Zeit (sekunden) [ST_STOP] Lãngste 'Stoppie' Zeit (sekunden) [ST_2WHE] Lãngste 2 Rad Zeit (sekunden) [ST_WHED] Lãngste 'Wheelie' Distanz (m) [ST_STOD] Lãngste 'Stoppie' Distanz (m) [ST_2WHD] Lãngste 2 Rad Distanz (m) [OUTFT11] Trainer [OUTFT12] Frankie [RELOAD] ~g~Du hast die schnell Nachladefãhigkeit gewonnen! [APACHE] Hunter zur Heli Landeplattform am Ocean Beach geliefert. [CRED369] JOHN MCCARDLE [CRED370] DAVID MURDOCH [CRED371] CHRIS BROWN [CRED372] PAUL GREEN [CRED373] KYLE MILNE [CUNTY] Neue Kleider wurden zum Vercetti Estate geliefert! [GOODBOY] $50 'Guter Bürger' Bonus! [NEWCONT] Neuer Kontaktpunkt am Jachthafen am Ocean Beach!! [FIRELVL] Feuerwehr-Mission Level ~1~ [HELP56] Drücke die ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~-Taste, um den Blickwinkel zu ãndern. [HELP57] Drücke die ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~-Taste, um den Blickwinkel zu ãndern. [HELP58] Beim Zielen kann durch Drücken der ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~-Taste zwischen Zielen hin- und her gewechselt werden. [HELP59] Beim Zielen kann durch Drücken der ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~-Taste zwischen Zielen hin- und her gewechselt werden. [HELP60] Wenn du wãhrend eines Autodiebstahls die ~h~~k~~PED_SPRINT~ ~w~-Taste drückst, steigst du nicht in das Fahrzeug ein. [HELP61] Du hast jetzt unbegrenzt Munition und doppelte Health für alle Fahrzeuge. [CRED374] KEVIN YUN [CRED375] ERICK COBBS [CRED376] RANDY BLAKE [CRED377] BRANDON LIM [CRED378] BRANDON FENOL [CRED379] MICHAEL MANOLE [CRED380] ALETHEIA SIMONSON [CRED381] JOHN JANSEN [FEC_LB1] Schau [FEC_LB2] nach hinten [FEC_LB3] Nach hinten schauen [FEC_R3] (R3-Taste) [FEC_PED] Steuerung zu Fuß [FEC_VEH] Steuerung in Fahrzeug [FEC_FPR] Steuerung für First-Person [FEC_CMM] Allgemeine Steuerung [FEC_PWL] Nach links [FEC_PWR] Nach rechts [FEC_PWF] Vorwãrts gehen [FEC_PWT] Auf Kamera zugehen [FEC_PLB] Nach hinten schauen [FEC_PFR] Waffe abfeuern [FEC_CLE] Eine Waffe nach links [FEC_CRI] Eine Waffe nach rechts [FEC_LKT] Ziel fixieren [FEC_PJP] Fußgãnger springen [FEC_PSP] Fußgãnger sprinten [FEC_PSH] Fußgãnger schießen [FEC_TLF] Ein Ziel nach links [FEC_TRG] Ein Ziel nach rechts [FEC_CCM] Kamera hinter Spieler zentrieren [FEC_SZI] Mit Prãzisionsgewehr heranzoomen [FEC_SZO] Mit Prãzisionsgewehr herauszoomen [FEC_LKL] First-Person nach links schauen [FEC_LRT] First-Person nach rechts schauen [FEC_LUP] First-Person nach oben schauen [FEC_LDN] First-Person nach unten schauen [FEC_LBH] Aus Fahrzeug nach hinten schauen [FEC_LLF] Aus Fahrzeug nach links schauen [FEC_LRG] Aus Fahrzeug nach rechts schauen [FEC_HRN] Hupe [FEC_HBR] Handbremse [FEC_ACL] Gas geben [FEC_BRK] Bremsen [FEC_TSM] Spezialmissionen An/Aus [FEC_CRD] Radiosender wechseln [FEC_ENT] In Fahrzeug Ein-/Aussteigen [FEC_WPN] Waffe abfeuern [FEC_PAS] Pause [FEC_FPO] First Person Weapons Toggle. [FEC_SMS] Mauszeiger An/Aus [FEC_CMS] Blickwinkel wechseln. [FEC_TSS] Screen Shot [FEC_DBG] Debug-Menü [FEC_TGD] Mit Pad zwischen Spiel- u. Debug-Modus wechseln [FEC_TDO] Debug-Kamera Aus [FEC_IVH] Maus horizontal invertieren [FEC_MSL] MAUSTASTE L [FEC_MSM] MAUSTASTE M [FEC_MSR] MAUSTASTE R [FEC_QUE] ??? [FEC_TWO] Nur zwei Tastaturtasten erlaubt [FEC_OMS] Nur eine Maustaste erlaubt [FEC_OJS] Nur ein Joystick-Button pro Aktion erlaubt [FEC_PTL] "Ziel fixieren" u. "Waffenauswahl links" gleichzeitig drücken. [FEC_PTR] "Ziel fixieren" u. "Waffenauswahl rechts" gleichzeitig drücken. [FEC_LBC] "Nach links schauen" u. "Nach rechts schauen" gleichzeitig drücken. [FEC_JBO] JOY ~1~ [FEC_WAR] Achtung! [FEC_OKK] OK [FEC_DLF] Löschen fehlgeschlagen. [FEC_SVU] Speichern fehlgeschlagen. [FEC_LUN] Laden fehlgeschlagen. Datei beschãdigt. Bitte löschen. [FEC_PAD] Gamepad [FEC_JOY] Joystick [FES_CSA] Wãhlen Sie eine Skin aus der Liste aus: [FET_HRD] STANDARDEINSTLLG. WIEDERHERGESTELLT [FET_MST] MAUSSTEUERUNG [FEC_STR] NUM STERN [FET_MIG] LINKS,RECHTS,MAUSRAD ZUR EINSTLLG. [FET_CIG] RÜCKT. ZUM LÖSCHEN - LMT,RETURN ZUM ÃNDERN [FET_DSN] Standard-Player Skin.bmp [FET_RSO] ORIGINAL-EINSTELLG. WIEDERHERGESTELLT [FET_RSC] HARDWARE NICHT VERFÜGBAR - ORIGINAL-EINSTELLG. WIEDERHERGESTELLT [FEA_3DH] AUDIO HARDWARE [FEA_SPK] BOXEN KONFIGURATION [FEM_LOD] DISTANZ-DARSTELLG. [FEM_VSC] FRAME SYNC [FEM_FRM] FRAME LIMITER [FEM_MM] HAUPTMENÜ [FED_RES] BILDSCHIRMAUFLSG. [FET_CTL] CONTROLLER-SETUP [FET_OPT] OPTIONEN [FEC_MSH] MAUSEMPFINDLICHKEIT [FEC_IVV] MAUS VERTIKAL INVERTIEREN [FET_MTI] MAUS STEURUNGSKONFIG. [FEC_FNC] F~1~ [FEC_IRT] EINFG [FEC_DLL] ENTF [FEC_HME] POS1 [FEC_END] ENDE [FEC_PGU] BILD AUF [FEC_PGD] BILD AB [FEC_UPA] AUF [FEC_DWA] AB [FEC_LFA] LINKS [FEC_RFA] RECHTS [FEC_NUM] NUM [FEC_NMN] NUM~1~ [FEC_FWS] NUM / [FEC_PLS] NUM + [FEC_MIN] NUM - [FEC_DOT] NUM , [FEC_NLK] NUMLOCK [FEC_ETR] ENT [FEC_SLK] ROLLEN [FEC_PSB] UNTBR [FEC_BSP] RÜCKT. [FEC_TAB] TAB [FEC_CLK] CAPSLOCK [FEC_RTN] RET [FEC_LSF] LUMSCHALT [FEC_RSF] RUMSCHALT [FEC_LCT] LSTRG [FEC_RCT] RSTRG [FEC_LAL] LALT [FEC_RAL] RALT [FEC_LWD] LWIN [FEC_RWD] RWIN [FEC_WRC] WINKLICK [FEC_SPC] LEERT. [WIN_TTL] GTA VC [WIN_95] GTA VC lãuft nicht unter Windows 95 [WIN_DX] GTA VC benötigt mind. DirectX Version 8.1 [FET_EIG] KANN DIESER AKTION KEINE STEUERUNG ZUWEISEN [FET_DAM] DYNAMISCHE AKUSTIK [FEQ_SRE] Wirklich beenden? Alle Daten seit dem letzten Speichern werden verlorengehen. Weiter? [FEQ_SRW] Spiel wirklich beenden? [FET_QG] SPIEL BEENDEN [FEN_STA] SPIEL STARTEN [REPLAY] WIEDERHOLUNG [FET_PAU] PAUSENMENÜ [FEC_ANS] Aktion [CVT_MSG] Texturen werden in optimales Format für Ihre Grafikkarte konvertiert [FEC_SFT] UMSCHALT [CVT_ERR] Kein Platz mehr auf der Festplatte. Bitte schaffen Sie Speicherplatz, bevor Sie fortfahren. ESC zum Abbrechen. [FEH_VMP] KARTE ANSEHEN [FES_DEE] Löschen fehlgeschlagen! Bitte noch einmal versuchen. [FES_CMP] Speichern fehlgeschlagen! Bitte noch einmal versuchen. [FESZ_WR] Spiel wird gespeichert. Bitte warten... [FELD_WR] Spiel wird geladen. Bitte warten... [FEDL_WR] Gespeichertes Spiel wird gelöscht. Bitte warten... [PCRESRT] Neues Spiel wird gestartet. Bitte warten... [FET_STI] Standard-Steuerung [FET_CTI] Classic-Steuerung [FET_PS] SPIELER-SKIN SETUP [FEH_NA] OPTION NICHT VERFÜGBAR [FEH_MPH] MAUS, CURSOR ZUM BEWEGEN - BILD AUFW., BILD ABW., MAUSRAD ZUM ZOOMEN, L - LEGENDE [FEA_MP3] MP3 PLAYER [NO_PCCD] Bitte legen Sie die GTA Vice City Disk ein, oder drücken Sie ESC zum Abbrechen [FEH_SSA] CURSOR ZUM BEWEGEN - S UM ZU SPEICHERN [FES_CMI] LETZTE ERFÜLLTE MISSION [FET_STS] STATISTIKEN GESPEICHERT IN 'STATS.HTML' + 'STATS.TXT' [WIN_VDM] Nicht genug verfügbarer Grafikspeicher für GTA Vice City vorhanden [FEC_ERI] Fehler! Eine oder mehrere Aktionen haben keine Tastenbelegungen. Bitte alle Aktionen belegen. [FEC_TFU] Geschütz + nach hinten neigen [FEC_TFD] Geschütz + nach vorne neigen [FET_RIG] WÃHLEN SIE EINE NEUE TASTENBELEGUNG FÜR DIESE AKTION [FEA_NM3] KEINE MP3-DATEIEN GEFUNDEN [FEA_MPB] MP3 LAUTSTÃRKE-BOOST [FEA_MUS] LAUTSTÃRKE MUSIK [FEA_SFX] LAUTSTÃRKE SFX [FEA_ADP] AUTOMATISCHE HARDWARE ERKENNUNG {=================================== MISSION TABLE AMBULAE ===================================} [A_COMP1:AMBULAE] Krankenwagen-Missionen abgeschlossen: $ ~1~ [ATUTOR2:AMBULAE] ~g~Fahre die Patienten VORSICHTIG in die Klinik. Jede Erschütterung verringert ihre Überlebenschancen. [A_FULL:AMBULAE] ~r~Krankenwagen voll!! [A_FAIL2:AMBULAE] ~r~Deine Bummelei war tödlich für den Patienten! [A_FAIL3:AMBULAE] ~r~Der Patient ist tot!! [A_PASS:AMBULAE] Gerettet! [A_COMP2:AMBULAE] Du ermüdest nie! [A_CANC:AMBULAE] ~r~Krankenwagen-Mission abgebrochen! [A_COMP3:AMBULAE] Krankenwagen-Missionen abgeschlossen! Du wirst beim Rennen nie ermüden! [ALEVEL:AMBULAE] Krankenwagen-Mission Level ~1~ [A_FAIL1:AMBULAE] Krankenwagen-Mission beendet. [A_SAVES:AMBULAE] GERETTETE MENSCHEN: ~1~ {=================================== MISSION TABLE ASSIN1 ===================================} [ASM1_5:ASSIN1] ~r~Er hat seine Lieferungen abgeschlossen! [ASM1_6:ASSIN1] Weitere Lieferungen: [ASM1_7:ASSIN1] ~g~Carl Pearson, Pizza-Lieferant. Schalte ihn aus, bevor er seine Lieferungen abschließt. [ASM1_A:ASSIN1] Mr. Teal, Ihre Hilfe bei der Beseitigung der Landeier war ãußerst wertvoll. Ich habe noch mehr Arbeit, die eine eher 'zupackende' Art verlangt. [ASM1_D:ASSIN1] Mr. Teal, Ihre Hilfe bei der Beseitigung der Landeier war ãußerst wertvoll. {=================================== MISSION TABLE ASSIN2 ===================================} [ASM2_1:ASSIN2] ~g~Mrs. Dawson verlãsst bald den Juwelier in Vice Point. Schalte sie aus. Es muss wie ein Autounfall aussehen. [ASM2_3:ASSIN2] ~g~Das Fahrzeug wird explodieren! Hau ab! [ASM2_4:ASSIN2] ~r~Du hast ihr Auto beschãdigt, obwohl sie nicht drin saß! Jetzt wird sie nicht einsteigen! [ASM2_5:ASSIN2] ~r~Sie ist entwischt! [ASM2_6:ASSIN2] ~r~Du warst zu nah am Unfallort! [ASM2_7:ASSIN2] ~g~Keine Waffen! Es soll wie ein Unfall aussehen! Drãnge sie stattdessen von der Fahrbahn! [ASM2_8:ASSIN2] ~g~Das ganze muss wie ein Unfall aussehen. Benutze keine Waffen. [ASM2_9:ASSIN2] Du brauchst einen fahrbaren Untersatz für diesen Job. [ASM2_10:ASSIN2] ~g~Wenn ihr Auto in Flammen aufgeht, entferne dich so weit wie möglich von der Unfallstelle. [ASM2_11:ASSIN2] Hilfe! [ASM2_12:ASSIN2] Hilf mir doch jemand! [ASM2_13:ASSIN2] Oh Gott! [ASM2_A:ASSIN2] Mein Kompliment für die gute Arbeit, Mr. Teal. Mein Kunde war sehr zufrieden. [ASM2_2:ASSIN2] Health: {=================================== MISSION TABLE ASSIN3 ===================================} [ASM3_11:ASSIN3] ZEIT: [ASM3_C:ASSIN3] Eine europãische Gang plant einen Überfall auf eine Bank in Vice City. Meinen Arbeitgebern wãre sehr daran gelegen, dass das nicht passiert. [ASM3_D:ASSIN3] Alle Mitglieder der Gang haben eine Tarnung, solange sie sich hier in Vice City aufhalten. Manche haben Jobs, andere geben sich als Touristen aus. [ASM3_E:ASSIN3] Infos über alle Zielpersonen und ihre wahrscheinlichen Aufenthaltsorte kleben unter dem Telefon. [ASM3_14:ASSIN3] ~g~Dick Tanner hãlt sich bei DBP Security am Ocean Drive auf. [ASM3_15:ASSIN3] ~g~Marc Hammond und Franco Carter halten sich in der Nãhe des Juwelierladens in Vice Point auf. [ASM3_16:ASSIN3] ~g~Nick Kong hãlt sich in der Nãhe von Washington Beach auf. [ASM3_18:ASSIN3] ~g~Geh nicht zu nahe an deine Zielperson heran, sonst entdeckt sie dich und du musst hinter ihr herjagen. [ASM3_19:ASSIN3] ~g~Er hat dich gesehen! Schalte ihn aus! [ASM3_20:ASSIN3] ~g~Sie haben dich gesehen! Schalte alle beide aus! [ASM3_21:ASSIN3] ~r~Du hast nicht alle Mitglieder der Gang rechtzeitig erledigt! [ASM3_22:ASSIN3] ~g~Geh nicht zu nahe an deine Zielpersonen heran, sonst entdecken sie dich und versuchen zu fliehen. [ASM3_12:ASSIN3] ~g~In der Nãhe sind einige Waffen für dich deponiert worden, falls du sie brauchen solltest. Du hast ~h~9 MINUTEN~g~, um alle Gang-Mitglieder auszuschalten. [ASM3_13:ASSIN3] ~g~Mike Griffin arbeitet an einer Plakatwand in Washington. [ASM3_17:ASSIN3] ~g~Charlie Dilson fãhrt mit dem Motorrad in Washington herum. {=================================== MISSION TABLE ASSIN4 ===================================} [ASM4_10:ASSIN4] ~g~Du warst anscheinend nicht der einzige, der hinter dem Aktenkoffer her war. Bring ihn schnell ins Ammu-Nation! [ASM4_12:ASSIN4] Distanz: [ASM4_15:ASSIN4] ~g~Nimm das Prãzisionsgewehr zu deiner Rechten. [ASM4_16:ASSIN4] ~g~Behalte die Frau auf der Empore im Auge. Sie wird die Rolltreppe hinuntergehen und die Zielperson nach der Uhrzeit fragen. [ASM4_17:ASSIN4] ~g~Schalte die Zielperson aus, NACHDEM die Frau mit ihr gesprochen hat. Aber erledige nicht die Frau. [ASM4_18:ASSIN4] ~g~Wenn die Zielperson ausgeschaltet ist, nimm ihren Aktenkoffer und bringe ihn zum Ammu-Nation in Downtown. [ASM4_19:ASSIN4] ~g~Halte Abstand von der Zielperson. Die Entfernungs-Anzeige rechts oben am Bildschirm zeigt an, wie nahe du an der Zielperson bist. [ASM4_20:ASSIN4] ~g~Lass den Anzeigebalken nicht an den Anschlag geraten, sonst sieht dich die Zielperson. [ASM4_21:ASSIN4] ~g~Schnapp dir den Aktenkoffer! [ASM4_22:ASSIN4] ~g~Bring den Aktenkoffer zum Ammu-Nation in Downtown. [ASM4_23:ASSIN4] ~g~Er hat dich entdeckt und versucht zu fliehen. Erledige ihn und schnapp dir den Aktenkoffer! [ASM4_25:ASSIN4] ~r~Du hast die Frau ausgeschaltet, du Idiot! [ASM4_26:ASSIN4] ~r~Die Zielperson hat das Flugzeug bestiegen! [ASM4_27:ASSIN4] ~r~Die Zielperson hat dich gesehen! Du hãttest Abstand halten sollen! [ASM4_28:ASSIN4] ~r~Die Zielperson hat dich gesehen! Er hat gehört, wie du geschossen hast! [ASM4_29:ASSIN4] ~r~Schalte ihn erst aus, wenn er mit der Frau gesprochen hat! [ASM4_A:ASSIN4] Es wird Zeit für dickere Brocken, Mr. Teal. Unter dem Busch zu Ihrer Rechten ist ein Gewehr. [ASM4_B:ASSIN4] Behalten Sie die Frau auf der Empore über dem Check-In im Auge. Sie wird durch die Menge gehen und jemanden nach der Uhrzeit fragen. [ASM4_C:ASSIN4] Sie müssen die betreffende Person ausschalten, ihren Aktenkoffer nehmen und ihn zu der Adresse, die unter dem Telefon klebt, bringen. {=================================== MISSION TABLE ASSIN5 ===================================} [ASM5_A:ASSIN5] Auf dem Dach der Cherry Popper-Eiscremefabrik findet eine Übergabe von wertvoller Ware statt. [ASM5_B:ASSIN5] Erledigen Sie alle Beteiligten, klauen Sie die Ware und bringen Sie sie zum Heliport am Flughafen. [ASM5_C:ASSIN5] Links von Ihnen ist ein Tor, das zur Rückseite der Fabrik führt. [ASM5_1:ASSIN5] ~g~Geh auf das Gelãnde hinter der Cherry Popper-Eiscremefabrik und dann auf das Dach, wo der Deal abgewickelt wird. [ASM5_2:ASSIN5] ~g~Schnapp dir die Ware und bring sie zum Heliport am Flughafen. [ASM5_3:ASSIN5] ~g~Bring die Ware zum Heliport am Flughafen! {=================================== MISSION TABLE BANKJ1 ===================================} [WANTED1:BANKJ1] ~g~Schüttle die Cops ab. Verringere deinen Fahndungslevel. [BJM1_A:BANKJ1] Tommy! Hey, Tommy, sieh mal, das ist super! Ich hab eine Minibar einbauen lassen! [BJM1_B:BANKJ1] Wir haben unten eine ausgewachsene Bar, Ken. [BJM1_C:BANKJ1] Ja, ja, wie auch immer. Tja, ich hab die Tafel besorgt, die du haben wolltest. [BJM1_D:BANKJ1] Ah, das ist der Lohn des Jurastudiums: Die Fãhigkeit, Anweisungen auszuführen. [BJM1_E:BANKJ1] Also, ich brauche einen Safeknacker. [BJM1_F:BANKJ1] Oh, ok, mal nachdenken...Safeknacker, Safeknacker...Ich hab's! Du wirst begeistert sein! [BJM1_G:BANKJ1] Aah, nicht dieser Schwachkopf. Der sitzt doch. [BJM1_H:BANKJ1] Wo sitzt er denn? [BJM1_I:BANKJ1] In einer Zelle in einem Polizeirevier. Er wartet auf seine Verlegung. [BJM1_J:BANKJ1] Ich glaube fast, er kommt auf Bewãhrung raus... [BJM1_1:BANKJ1] ~g~Befreie Cam Jones aus der Haft! [BJM1_3:BANKJ1] ~g~In der Umkleide des Reviers findest du etwas Nützliches. [BJM1_21:BANKJ1] ~g~Die Key Card zu den Zellen findet sich im Obergeschoss des Reviers. [BNK1_7:BANKJ1] Cam Jones? [BNK1_8:BANKJ1] Ich hol dich hier raus. [BNK1_10:BANKJ1] Ja, der bin ich... [BNK1_11:BANKJ1] Ganz wie du meinst! [BNK1_13:BANKJ1] Ich ziehe einen Job durch, und du bist mein Safeknacker. [BNK1_14:BANKJ1] Besser als in 'ner Zelle zu verrotten! [BJM1_22:BANKJ1] ~g~Bring Cam nach Hause! [BJM1_23:BANKJ1] ~g~Du brauchst zunãchst die Magnetkarte für die Tür! [BNK1_12:BANKJ1] Hãng die Bullen ab und bring mich nach Hause! [BJM1_20:BANKJ1] Die Waffe weg oder es passiert was! [BJM1_5:BANKJ1] Zutritt ab hier nur für befugtes Personal. [BJM1_2:BANKJ1] ~r~Du solltest Cam die Freiheit bringen, nicht den Tod! [BJM1_4:BANKJ1] Er ist bewaffnet! Erledigt ihn! {=================================== MISSION TABLE BANKJ2 ===================================} [BJM2_A:BANKJ2] Wir brauchen einen Überfall-Experten. Kennt ihr einen? [BJM2_B:BANKJ2] Hey, Tommy, Tommy, Tommy, das Zeug bringt dich nach vorn, Mann. [BJM2_C:BANKJ2] WoooOOOooo! [BJM2_D:BANKJ2] Ich könnte dein Experte sein! Überfall! Überfall! [BJM2_E:BANKJ2] Du bist kein Experte, du bist ein Idiot. [BJM2_F:BANKJ2] Mach dir 'nen Drink und halt die Klappe. [BJM2_G:BANKJ2] Hey, aus dem Weg! [BJM2_H:BANKJ2] Cam, was meinst du? [BJM2_I:BANKJ2] Tja, der beste Schütze in der Stadt ist ein Kerl namens Cassidy. [BJM2_J:BANKJ2] Ach ja? [BJM2_K:BANKJ2] Ja. Soldat, oder wenigstens hãlt er sich dafür. [BJM2_L:BANKJ2] Ich bezweifle, dass er je bei der Army war, aber er kann mit der Knarre umgehen. [BJM2_M:BANKJ2] Wahrscheinlich ist er in der Schießanlage. [BJM2_2A:BANKJ2] Bist du Phil Cassidy? [BJM2_2B:BANKJ2] Wieso? [BJM2_2C:BANKJ2] Ich suche einen, der mit der Kanone umgehen kann. Was ich hier so sehe, überzeugt mich nicht. [BJM2_2D:BANKJ2] Jungchen, ich schieße dir auf 25 Meter eine Fliege vom Kopf. [BJM2_2E:BANKJ2] Ach wirklich? [BJM2_2F:BANKJ2] Ja. Hab ich bei der Army gelernt. [BJM2_2G:BANKJ2] Ist Fliegen-Schießen so beliebt in der Army? Gut, dass ich keine Steuern zahle. [BJM2_2H:BANKJ2] Sollte das witzig sein, Jungchen? [BJM2_2I:BANKJ2] Ha ha ha ha ha! [BJM2_2J:BANKJ2] Lass uns schießen. [BJM2_1:BANKJ2] ~g~Begib dich nach Downtown ins Ammu-Nation und sprich mit Phil Cassidy. [BJM2_3:BANKJ2] TREFFERQUOTE: ~1~% [BJM2_4:BANKJ2] PUNKTE RUNDE EINS: ~1~ [BJM2_6:BANKJ2] PUNKTE RUNDE ZWEI: ~1~ [BJM2_7:BANKJ2] GESAMTPUNKTZAHL FÜR DAS SCHIESSEN: ~1~ [BJM2_9:BANKJ2] ~g~Begib dich zum Startpunkt für Runde Zwei. [BJM2_11:BANKJ2] ~r~Phil ist erledigt! [BJM2_12:BANKJ2] ~r~Einer der Schützen ist erledigt! [BJM2_14:BANKJ2] ~g~Begib dich zum nãchsten Areal! [BJM2_15:BANKJ2] PUNKTE: [BJM2_17:BANKJ2] ~g~Sprich mit Phil. [BJM2_18:BANKJ2] ZU SCHLAGEN: [BJM2_19:BANKJ2] ~g~Triff innerhalb des Zeitlimits so viele Ziele wie du kannst! [BJM2_22:BANKJ2] ~r~Du hast den Schießstand verlassen! [BJM2_23:BANKJ2] ~g~Wenn du den Schießstand wãhrend des Wettbewerbs verlãsst, ist die Mission gescheitert. [BJM2_24:BANKJ2] ~g~Das nahegelegenste Ziel bringt 1 Punkt. [BJM2_25:BANKJ2] ~g~Das mittlere Ziel bringt 2 Punkte. [BJM2_27:BANKJ2] ~g~Alle Ziele dieser Runde bringen 1 Punkt. [BNK2_2:BANKJ2] ZIELEN 3-2-1 FEUER! [BNK2_3:BANKJ2] AREAL KLAR! [BNK2_4:BANKJ2] Huuuiii! [BNK2_5:BANKJ2] Der trãfe nicht mal ein Scheunentor. [BNK2_7:BANKJ2] Also, was ist jetzt, hilfst du mir bei dem Job? [BNK2_8:BANKJ2] Jungchen, so wie du schießt, würde ich dich sogar heiraten. [BNK2_9A:BANKJ2] Junge, deine Sprüche und deine Hirngespinste kannst du dir sonst wohin stecken. Du bist ein lausiger Schütze. [BNK2_9B:BANKJ2] Du bist ein lausiger Schütze. [BJM2_28:BANKJ2] PUNKTE RUNDE DREI: ~1~ [BJM2_20:BANKJ2] ~g~Geht dir die ~w~Zeit ~g~oder die ~w~Munition ~g~aus, ist die Runde beendet! [BJM2_26:BANKJ2] ~g~Das am weitesten entfernte Ziel bringt 3 Punkte. [BNK2_1:BANKJ2] SCHARFE MUNITION [RANGE_1:BANKJ2] PUNKTZAHL SCHIESS-RUNDE:~1~ [BJM2_2:BANKJ2] ~g~Um die Runde zu beenden, drücke die ~h~~k~~PED_JUMPING~. [BJM2_N:BANKJ2] Nur die Ruhe. {=================================== MISSION TABLE BANKJ3 ===================================} [BJM3_A:BANKJ3] Langsam fügt sich alles sehr schön zusammen hier. [BJM3_B:BANKJ3] Was ist der Plan, Tommy? Was geht ab, Amigo? [BJM3_C:BANKJ3] Der Plan ist, dass du dich wie ein Vollidiot benimmst. Wir brauchen einen Fahrer. [BJM3_D:BANKJ3] Tommy, ich mach's. Ich kann fahren. [BJM3_E:BANKJ3] Nimm Hilary, Boss, nicht diesen Labersack von einem Rechtverdreher. [BJM3_F:BANKJ3] Hilary ist der beste. So schnell wie den hast noch keinen fahren sehen. Ich rufe ihn mal an. [BJM3_G:BANKJ3] Hey, Hil, hier Phil. Wie lãuft's? Nein, sag nichts. Dazu haben wir spãter Zeit. Tust du mir einen Gefallen? [BJM3_H:BANKJ3] Ich hab hier einen Typ aus dem Norden. Nein, ich glaub, er war nicht beim Militãr. Aber er braucht einen Fahrer. [BJM3_I:BANKJ3] Für einen Job. Ok, verstehe. [BJM3_J:BANKJ3] Was hat er gesagt? [BJM3_K:BANKJ3] Er macht's. Kein Problem. Na ja, ein kleines vielleicht: Er leidet unter Verlustãngsten. [BJM3_L:BANKJ3] Er arbeitet anscheinend nicht für Leute, die ihn nicht schlagen können. Hat was mit seiner Mutter zu tun. [BJM3_M:BANKJ3] Jedenfalls will er erst ein Rennen gegen dich fahren. Er wartet draußen auf dich. [BJM3_2A:BANKJ3] Bist du Tommy? Klar bist du Tommy, ich meine, [BJM3_2B:BANKJ3] wieso sollte sonst einer mit mir reden wollen? [BJM3_2C:BANKJ3] Ok. Das ganze lãuft so ab: [BJM3_2D:BANKJ3] Ich fahre für dich, WENN und NUR WENN du selbst anstãndig fãhrst. [BJM3_2E:BANKJ3] Verlierst du mich, verzeihe ich dir das nie. [BJM3_2:BANKJ3] Hilary ist erledigt! [BJM3_4:BANKJ3] ~g~Du brauchst ein Auto, um mitzumachen. [BNK3_1:BANKJ3] Ok, ich fahre für dich. Aber bitte, behandle mich schlecht. [BNK3_3A:BANKJ3] Illegales Straßenrennen bei Vice Point. [BNK3_3B:BANKJ3] An alle Einsatzkrãfte. [BNK3_3C:BANKJ3] Straßenrennen sind verboten und illegal! {=================================== MISSION TABLE BANKJ4 ===================================} [BNK4_A:BANKJ4] ~w~Ihr seht Gentlemen, das wird leicht verdientes Geld für uns. [BNK4_B:BANKJ4] ~w~Ernsthaft, Tommy, du solltest dir überlegen, Anwalt zu werden. [BNK4_C:BANKJ4] ~w~Was zum Geier rauchst du eigentlich, Mann? Das ist kein simpler Plan. [BNK4_D:BANKJ4] ~w~Ach, wer braucht schon simple Plãne? [BNK4_E:BANKJ4] ~w~Der Kommunismus, das war ein simpler Plan. Hat Russland aber nicht viel genützt, hah? [BNK4_F:BANKJ4] ~w~Ganz ruhig. Mit einem Team wie diesem ist das alles kein Problem. [BNK4_G:BANKJ4] ~w~Cam übernimmt den Safe. Phil? Wir beide kümmern uns um die Sicherheit, und Hilary fãhrt den Fluchtwagen. [BNK4_H:BANKJ4] ~w~Ãh, hast du nicht jemanden vergessen? Jemanden, der dir unzãhlige Male geholfen hat in dieser Stadt? Jemanden...? [BNK4_I:BANKJ4] ~w~Ken...Ken, richtig. Ken wãscht das Geld für uns und stellt schon mal die Drinks kalt. [BNK4_J:BANKJ4] ~w~Ich verstehe nicht, was ich hier soll. [BNK4_K:BANKJ4] ~w~Ist doch ganz einfach. Warst du noch nie im Kino? [BNK4_L:BANKJ4] ~w~Wir latschen in die Bank rein, fuchteln mit den Knarren rum und gehen stinkreich wieder raus. [P_DEAD:BANKJ4] ~r~Phil ist erledigt!! [C_DEAD:BANKJ4] ~r~Cam ist erledigt!! [H_DEAD:BANKJ4] ~r~Hilary ist erledigt!! [P_HIND:BANKJ4] ~r~Du hast Phil verloren! [C_HIND:BANKJ4] ~r~Cam wurde abgehãngt! [H_HIND:BANKJ4] ~r~Hilary wurde im Stich gelassen! [GETCAR:BANKJ4] Steig in den Fluchtwagen und führe den Plan aus! [TRASHED:BANKJ4] ~r~DU HAST DEN FLUCHTWAGEN GESCHROTTET!! [BNK4_1:BANKJ4] Ich fahre. [BNK4_2:BANKJ4] Na prima. Beifahrer. Wenn ich das in der Therapie erzãhle. [BNK4_3A:BANKJ4] Hey, pass auf, wo du hinfãhrst, Tommy! [BNK4_3B:BANKJ4] Tommy, Hilary macht sich so breit! [BNK4_3C:BANKJ4] Stimmt gar nicht! [BNK4_3D:BANKJ4] Doch! [BNK4_3E:BANKJ4] Hey, Klappe, ihr zwei, oder ihr könnt zu Fuß gehen. [BNK4_3F:BANKJ4] Ja, Hilary. [BNK4_3I:BANKJ4] Herrgott, Phil, hör auf, mit diesem Ding rumzufuchteln! [BNK4_3J:BANKJ4] Ja, am Ende geht das noch ins Auge! [BNK4_3M:BANKJ4] Mein Baby! Alles Schrott! [BNK4_3O:BANKJ4] Du hãngst zu sehr an der Illusion der Ewigkeit. [BNK4_3P:BANKJ4] Was? [BNK4_3Q:BANKJ4] Du denkst, alles bleibt ewig. [BNK4_3R:BANKJ4] Jugend, geliebte Menschen, Pizza, [BNK4_3S:BANKJ4] Alles geht mal vorbei, und das musst du akzeptieren. [BNK4_3T:BANKJ4] Hey, du hast recht. Danke, Cam. [BNK4_3U:BANKJ4] Nichts zu danken. [BNK4_3V:BANKJ4] Hey, Tommy, wieso halten wir? [BNK4_4A:BANKJ4] ~w~Hilary, fahr ein bisschen um den Block. [BNK4_5:BANKJ4] ~w~Ok, Tommy, ok. [BNK4_6:BANKJ4] ~w~DIES IST EIN ÜBERFALL! [BNK4_7:BANKJ4] ~w~KEINER BEWEGT SICH! [BNK4_8:BANKJ4] ~w~ALLE AN DIE WAND! [BNK4_9:BANKJ4] Phil, halt die Stellung! [BNK4_10:BANKJ4] Verstanden! [BNK4_11:BANKJ4] Los Cam, der Tresor ist oben... [BK4_12A:BANKJ4] Verdammt, das ist ein Flange 9000! [BK4_12B:BANKJ4] Könnte Stunden dauern, den zu knacken. [BK4_12C:BANKJ4] Oder 5 Minuten, wenn du den Manager findest. [BNK4_13:BANKJ4] Ich seh nach, wo er sich verkrochen hat. [BK4_14A:BANKJ4] Phil, alles im grünen Bereich? [BNK4_15:BANKJ4] Klar. Im dunkelgrünen Bereich. [BNK4_16:BANKJ4] Du da! Mitkommen! [BNK4_17:BANKJ4] Ok! Ok! Nicht schießen! [BNK4_18:BANKJ4] ICH SAGTE, KEINER RÜHRT SICH! [BK4_19A:BANKJ4] Der Safe hat eine Zeitsperre, [BK4_19B:BANKJ4] ihr könnt ebenso gut gleich aufgeben! [BK4_20A:BANKJ4] Ach, die Zeitsperre kann ich umgehen. [BK4_20B:BANKJ4] Dann brauchen wir nur noch deinen Code und alles ist in Butter! [BNK4_21:BANKJ4] Bleib hier. Wenn du Zicken machst, bist du Fischfutter, klar? [BNK422A:BANKJ4] Cam, wie lange noch? [BK4_23A:BANKJ4] Gib mir noch 3 Minuten! [BK4_24A:BANKJ4] Ich seh mal nach Phil. Bin gleich wieder da. [BK4_24B:BANKJ4] Ich hab gesagt, Finger weg vom Alarm! [BNK4_25:BANKJ4] Das Spezialkommando muss gleich hier sein! [BNK4_27:BANKJ4] Ich könnte ein bisschen Hilfe brauchen, Tommy! [BNK4_28:BANKJ4] Vice City Spezialkommando! Sie sind umzingelt! [BNK4_29:BANKJ4] Umzingelt? HA HA HA HAAAAAaaa! [BNK4_30:BANKJ4] Die scheißen sich ein, die korrupten Schweine! [BK4_31A:BANKJ4] Tommy, der Tresor ist offen! [BK4_34A:BANKJ4] Ok, wir haben die Pensionskasse der Bullen. Los, raus hier! [BK4_34B:BANKJ4] Ok, ihr habt es so gewollt. Ihr hattet eure letzte Chance! [BK4_35A:BANKJ4] Die stürmen den Laden! [BK4_35B:BANKJ4] In Deckung! [BNK4_94:BANKJ4] ~w~Ok, Jungs. Jeder hãlt sich an den Plan . [BM_DEAD:BANKJ4] ~r~Du brauchst den Bank Manager lebend!! [ASSET_A:BANKJ4] BANK-MISSIONEN ERFÜLLT! [ASSET_B:BANKJ4] ~g~Der Malibu Club generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmãßig ab. [IDIOT:BANKJ4] ~r~Na super - angezogen wie ein Irrer durch die Gegend laufen und Aufmerksamkeit erregen, IDIOT! {=================================== MISSION TABLE BARON1 ===================================} [COK1_A:BARON1] Komm schon, Brauner, komm! [COK1_B:BARON1] Dãmlicher Klepper! Dich mach ich einen Kopf kürzer! [COK1_C:BARON1] Wer ist der Blödmann? [COK1_D:BARON1] Tommy Vercetti, Sie erinnern sich. [COK1_E:BARON1] Tschuldigung, ich bin etwas nervös. Vertrau niemals einem verdammten Pferd! [COK1_F:BARON1] Du machst deine Sache gut. Du arbeitest jetzt für mich. [COK1_H:BARON1] Wie gesagt, Amigo, du arbeitest für mich. Basta. So ein Mistkerl hat mich betrogen. [COK1_I:BARON1] Er denkt, ich weiß nicht, wie viel Geld mir zusteht. Aber 3% zu klauen ist genauso schlimm wie 100% zu klauen. [COK1_J:BARON1] Niemand haut mich übers Ohr. NIEMAND!! [COK1_K:BARON1] Folge ihm von seiner Wohnung aus und sieh nach, wo er hin will. Spãter erledigen wir ihn. [COK1_1:BARON1] Oh, Shit! [COK1_2:BARON1] Zu langsam, Opa! [COK1_4:BARON1] Versager. [COK1_5:BARON1] Ich würde einen Zahn zulegen, du Mistsack! [COK1_8:BARON1] ~g~Schnell! Schnapp dir einen Untersatz und folge ihm! [COK1_9:BARON1] ~r~Du sollst ihn verfolgen, nicht umlegen! [COK1_10:BARON1] ~r~Begib dich zum Haus des Diebes und such das Geldversteck. [COK1_11:BARON1] ~g~Schau durch sein Fenster. [COK1_7:BARON1] ~g~Er ist aufs Dach geflohen. Bleib ihm auf den Fersen, aber erledige ihn nicht. [COK1_G:BARON1] Ich arbeite gegen Geld. {=================================== MISSION TABLE BARON2 ===================================} [COK2_A:BARON2] Was bist du eigentlich für ein inkompetenter Narr? [COK2_B:BARON2] NARR! NARR! NARR! NARR! [COK2_C:BARON2] Tommy- [COK2_D:BARON2] Was, Ricardo? [COK2_E:BARON2] Diese Idioten - dauernd wollen sie einen reinlegen. [COK2_F:BARON2] Das ist der Fehler an dieser Branche. [COK2_G:BARON2] Was soll denn das? Aaaaah! [COK2_H:BARON2] Diese Mistkerle haben mich bitter enttãuscht. [COK2_I:BARON2] Bald denkt jeder Depp, er kann in Vice City Koks verkaufen. [COK2_J:BARON2] Was kommt als nãchstes, hah? Die verstunkene Mafia?! [COK2_K:BARON2] Diese Bandengegend ist eine Festung ohne Mauern. [COK2_L:BARON2] Also: Quentin hier - Quentin! QUENTIN! [COK2_M:BARON2] Er fliegt dich über das Areal. [COK2_N:BARON2] Heb ihr Nest aus! [COK2_O:BARON2] Was soll denn das? [COK2_P:BARON2] Was willst du hier? [COK2_Q:BARON2] Hey, ich hab mich umgehört, und es ist klar, [COK2_R:BARON2] dass Diaz in den Deal reingeplatzt ist und meinen Bruder erledigt hat. [COK2_S:BARON2] Und dich erledigt er auch! [COK2_T:BARON2] Mit Diaz nehm ich's auf! [COK2_U:BARON2] Nein, hör zu! Ich übernehme Diaz - [COK2_V:BARON2] er beginnt mir zu vertrauen. [COK2_1:BARON2] Eines versteh ich nicht: Was soll das mit 'Quentin'!? [COK2_2:BARON2] Weiß nicht. Hat mir immer gefallen...Quentin Vance... [COK2_3:BARON2] Vance? Du heißt wirklich Lance Vance?! [COK2_4:BARON2] Hey! Das hab ich schon in der Schule oft genug gehört! [COK2_5:BARON2] Hast du so was schon mal aus einem Helikopter abgefeuert? [COK2_8:BARON2] Wo fliegen wir denn hier? [COK2_9:BARON2] Prawn Island. [COK2_13:BARON2] 'Lance Vance'. Du armer Teufel. [COK2_14:BARON2] Ok, wir sind gleich da. [COK2_15:BARON2] Wir fliegen ein paar Mal drüber weg. [COK2_16:BARON2] Schalte so viele Kerle aus wie du kannst. [COK2_17:BARON2] Dann setze ich dich ab, und du bist auf dich allein gestellt. [COK2_20:BARON2] Shit! Das ist ja wie im Krieg hier! Schalt welche von den Schützen aus! [COK2_21:BARON2] Wir werden getroffen, Mann! [COK2_22:BARON2] Reparaturen an dem Kübel hier kosten! Schalt sie aus! [COK2_23:BARON2] Ok, von jetzt an bist du auf dich alleine gestellt. Viel Glück! [COK2_24:BARON2] Zustand Helikopter: [COK2_25:BARON2] ~g~Hol das Geld auf dem Dach. [COK2_27:BARON2] Du bist auf MEINEM Gebiet, Mistkerl! [COK2_28:BARON2] Dich mach ich fertig! [COK2_6:BARON2] Nein. Aber ich kann ja unterwegs ein bisschen üben. [OPEN_B:BARON2] Die Straßensperren zum Festland sind aufgehoben worden. {=================================== MISSION TABLE BARON3 ===================================} [COK3_A:BARON3] Das gefãllt euch nicht, was?! [COK3_B:BARON3] Ahahahahaa, Ahahahahaa. [COK3_C:BARON3] Vorsicht! Passen Sie auf, wo Sie damit hin zielen! [COK3_D:BARON3] Kein Taubendreck mehr auf MEINEM Dach, was Tommy? [COK3_E:BARON3] Sieht nicht so aus. [COK3_F:BARON3] Das kannst du laut sagen. Also, hör zu: [COK3_G:BARON3] Weißt du, wem das schnellste Boot an der Ostküste gehört? [COK3_H:BARON3] Nicht wirklich. [COK3_I:BARON3] MIR. Und das soll auch so bleiben. [COK3_J:BARON3] Jeder Schmuggler von hier bis Caracas hat EINEN Traum: ein schnelleres Boot. [COK3_K:BARON3] Es heißt, die Hull Werft hat gerade so ein Boot fertiggestellt, [COK3_L:BARON3] für irgendeinen costaricanischen Idioten. [COK3_M:BARON3] Und, Tommy...ICH WILL DIESES BOOT!!! [COK3_N:BARON3] Ich glaube, die Tauben sind wieder da. [COK3_O:BARON3] Ah! Ich dachte, ich hab euch erwischt. Wo kommst du denn her? [COK3_P:BARON3] Tauben! Boom! Aaaaah! [COK3_5:BARON3] ~g~Suche den Schalter, um das Boot abzusenken. [COK3_6:BARON3] ~g~Bringe das Boot zur Villa. [COK3_7:BARON3] ~r~Du hast das Boot zerstört! [COK3_8:BARON3] ~g~Begib dich zur Bootswerft an den Docks und klau das schnellste Boot. [COK3_9:BARON3] ~g~Jetzt steig in das Boot. {=================================== MISSION TABLE BARON4 ===================================} [COK4_A:BARON4] Eject! PLASTIKMÜLL! [COK4_B:BARON4] Wie kannst du mir das antun? [COK4_C:BARON4] Was bildest du dir ein, du mieses Stück Plastikdreck! Aaargh! [COK4_D:BARON4] VERDAMMT! [COK4_E:BARON4] Wenn das Ding meinen Lieblings-El-Burro-Film frisst, ist Sense! [COK4_F:BARON4] Was kann ich noch tun? [COK4_G:BARON4] Ist wahrscheinlich nicht eingesteckt. [COK4_H:BARON4] Was? [COK4_I:BARON4] Verdammt. Egal, ich kann mir noch 100 davon kaufen. [COK4_J:BARON4] Also, Tommy, [COK4_K:BARON4] jeden Monat legt ein 'Freiberufler' mit seiner Jacht in Vice City an. [COK4_L:BARON4] Er verkauft seine Fracht an das erstbeste Schiff. [COK4_M:BARON4] Du schnappst dir das Rennboot, [COK4_N:BARON4] und bist vor allen anderen Pennern bei ihm. [COK4_O:BARON4] Dann bringst du die Fracht hierher, ok? [COK4_P:BARON4] Lass mich raten. Du dachtest, ich brauche einen Schutzengel. [COK4_Q:BARON4] Ich denke nur, du solltest mich mitnehmen, Mann. [COK4_R:BARON4] Du kannst lange den einsamen Macho-Helden markieren, [COK4_S:BARON4] aber eines Tages werde ich dir den Hintern retten [COK4_T:BARON4] und du wirst mich wahrscheinlich dafür küssen wollen. [COK4_U:BARON4] Spinner. [COK4_V:BARON4] Hahahaha! [COK4_1:BARON4] Tommy, wir wissen, es war Diaz, der unseren Deal platzen ließ. [COK4_3:BARON4] Wieso spielen wir dann die Laufburschen für ihn? [COK4_4:BARON4] Je mehr wir jetzt lernen, desto weniger müssen wir lernen, wenn wir diese Stadt übernehmen! [COK4_5:BARON4] Dein Stil gefãllt mir. Echt erfrischend. [COK4_12:BARON4] Pass auf, die kommen von überall [COK4_13:BARON4] Erwischt! Fahr zu Diaz so schnell du kannst! [COK4_14:BARON4] Willst du was abhaben? [COK4_15:BARON4] Schönen Gruß an die Fische! [COK4_16:BARON4] Nimm das! Und das! [COK4_19:BARON4] Gleich kommt's noch dicker! [COK4_20:BARON4] Da sind Schützen auf dem Pier! [COK4_24:BARON4] Gut geschossen, mein Freund. Du bist ein erstklassiger Psycho. [COK4_25:BARON4] Danke sehr. [COK4_26:BARON4] Wir sehen uns, Tommy. [COK4_27:BARON4] Ok, Mr. Lance Vance Dance. [COK4_28:BARON4] ~g~Sei vor den anderen Booten bei der Jacht! [COK4_31:BARON4] ~g~Begib dich zum schnellsten Boot auf dem Pier! [COK4_32:BARON4] ~r~Zu langsam! [COK4_33:BARON4] ~r~Du hast das Boot zerstört! [COK4_34:BARON4] ~g~Zieh diese Boote aus dem Verkehr! [COK4_35:BARON4] Zustand Boot: {=================================== MISSION TABLE BARON5 ===================================} [PROP_A:BARON5] OBJEKT ERWORBEN! [COK4_30:BARON5] ~r~Lance ist erledigt! [ASS1_A:BARON5] Im Kofferraum sind ein paar Kanonen. [ASS1_B:BARON5] Wahnsinn! Wo hast du denn die alle her? [ASS1_C:BARON5] Hab ich für schlechte Zeiten zurückgelegt. [ASS1_D:BARON5] Zufrieden? [ASS1_E:BARON5] Ja. Und wie! [ASS1_F:BARON5] Ihr dãmlichen Idioten! [ASS1_G:BARON5] Mein schönes Haus [ASS1_H:BARON5] Seht euch an, was ihr angestellt habt! [ASS1_I:BARON5] Das ist für meinen Bruder! [ASS1_J:BARON5] Ich habe dir vertraut, Tommy. [ASS1_K:BARON5] Ich hãtte was aus dir gemacht... [ASS1_L:BARON5] Gute Nacht, Mr. Diaz. [ASS1_1:BARON5] Bald wimmelt's hier von Arschlöchern. Sei vorsichtig. [ASS1_2:BARON5] Keine Panik, Tommy, ich geb dir Deckung. [ASS1_13:BARON5] DIAZ?! Ich bin hier, um deinen Laden zu übernehmen! [ASS1_14:BARON5] TOMMY! Du Verrãter...Du Idiot! Dich mache ich fix und fertig... [ASS1_16:BARON5] ~g~Erledige Diaz! [BUD1:BARON5] Lance [ASS1_18:BARON5] ~g~Die Tür ist verschlossen. Versuche es auf einem anderen Weg. [ASS1_19:BARON5] Da lang! [ASS1_20:BARON5] Tommy, ich hab ein Problem mit Quentin, nicht mit dir, Mann! {=================================== MISSION TABLE BIKE1 ===================================} [BM1_A:BIKE1] Wo ist Baker? [BM1_B:BIKE1] Ich suche Big Mitch Baker... [BM1_C:BIKE1] Wer sucht ihn? [BM1_D:BIKE1] Tommy Vercetti. [BM1_E:BIKE1] Vercetti. [BM1_F:BIKE1] Du siehst nicht wie ein Bulle aus, das heißt, du hast 1 Minute. [BM1_G:BIKE1] Also drück auf die Tube. [BM1_H:BIKE1] Kent Paul sagt, ihr wãrt interessiert, die Security für einen Gig zu übernehmen, den er plant. [BM1_I:BIKE1] Kent Paul? Pfff! Kein Wunder, dass er dich schickt. [BM1_J:BIKE1] Als er das letzte Mal hier war, ist er durch Fenster wieder gegangen - und zwar splitternackt. [BM1_K:BIKE1] Seid ihr nun interessiert oder nicht? [BM1_L:BIKE1] Gefãlligkeiten gibt's nur für Mitglieder. [BM1_M:BIKE1] Wie kann ich beitreten? [BM1_N:BIKE1] Wir sind hier kein Golfklub, Kleiner. Kannst du 'n Bike fahren? [BM1_O:BIKE1] Kannst du auf 'm Barhocker sitzen und saufen? [BM1_P:BIKE1] Cougar, Zeppelin, checkt mal ab, wie diese Sissy hier fãhrt. [BM1_2:BIKE1] ~g~Du brauchst eine Freeway oder eine Angel, um mitzumachen! [BM1_3:BIKE1] ~r~Die Fahrer wurden angegriffen! [BIKE1_1:BIKE1] Ok, Schickimickibürschchen, dann zeig mal, was du drauf hast. [BM1_1:BIKE1] ~g~Schnapp dir eine Freeway oder eine Angel und geh in Startposition. {=================================== MISSION TABLE BIKE2 ===================================} [BM2_2:BIKE2] ~g~Du musst den Chaosmesser in der vorgegebenen Zeit vollmachen, um uns zu zeigen, aus welchen Holz du bist! [BM2_4:BIKE2] ~r~Du hast den Chaosmesser nicht rechtzeitig vollgemacht! [BM2_1:BIKE2] CHAOSMESSER: [BM2_A:BIKE2] Ha, ha, ha, hab dich wieder erwischt. [BM2_B:BIKE2] Hey, Vercetti. [BM2_C:BIKE2] Cougar meint, du fãhrst ziemlich gut. [BM2_D:BIKE2] Ja, wie lange soll ich noch hier rumgurken? [BM2_E:BIKE2] Ich bin ein sehr beschãftigter Mann. [BM2_F:BIKE2] Wenn ich mich kloppen soll, damit das klar geht, dann los. [BM2_G:BIKE2] Es geht bei uns nicht nur ums dreinschlagen. Man muss zur Familie gehören. [BM2_H:BIKE2] Ja, ich hab schon mal zu einer Familie gehört. Hat nicht funktioniert. [BM2_I:BIKE2] Klar. Aber unsere Familie kümmert sich um ihre Leute. [BM2_J:BIKE2] Wir verlangen nicht, dass einer die Drecksarbeit macht und danach 15 Jahre einsitzt. [BM2_K:BIKE2] Ja, ganz recht. Ich hab meine Hausaufgaben gemacht. [BM2_L:BIKE2] Das hier ist die größte Familie von Außenseitern, Outlaws und Unruhestiftern. [BM2_M:BIKE2] Ein paar von uns wurden sogar von ihrem eigenen Land verraten. [BM2_N:BIKE2] Wãhrend des Vietnamkriegs war ich eingelocht. Miese Sache. [BM2_O:BIKE2] Drum sollst du denen ja zeigen, was Sache ist. [BM2_P:BIKE2] Dieses ganze verdammte Land braucht einen Arschtritt, und wir geben ihm diesen Tritt. [BM2_Q:BIKE2] Also los, schnapp dir ein Bike und zeig der Stadt, wie sauer du bist. [BM2_R:BIKE2] Alles klar. [BM2_3:BIKE2] ~g~Dieser Ton zeigt an, dass du einen Teil gefüllt hast. Mach ihn immer voller. {=================================== MISSION TABLE BIKE3 ===================================} [BM3_A:BIKE3] Hi, Mitch. [BM3_B:BIKE3] Ah, sieh an, 'Outlaw' Vercetti. [BM3_C:BIKE3] Jetzt will ich sehen, wie du für deine Kumpels kãmpfst. [BM3_D:BIKE3] Eine Straßengang von hier hat den Fehler gemacht, meinen Hobel zu klauen. [BM3_E:BIKE3] Wollten wahrscheinlich zeigen, was für coole Machos sie sind. [BM3_F:BIKE3] Ich und die Jungs wollten ihnen eigentlich ein bisschen Respekt einblãuen. [BM3_G:BIKE3] Aber- [BM3_H:BIKE3] -dann dachte ich mir, das wãre doch ein guter Test für dich. [BM3_I:BIKE3] Bring mir meine Maschine zurück und Paul kriegt seine Security. [BM3_2:BIKE3] ~r~Du solltest die Maschine zurückbringen, nicht schrotten! [BM3_3:BIKE3] ~g~Bring die Maschine zur Bar! [BM3_4:BIKE3] ~g~Setz dich auf die Maschine! [INTRUDE:BIKE3] ~g~Man hat dich bemerkt! [BM3_6:BIKE3] ~g~Sie sind Downtown hinter dem Ammu-Nation. [BM3_7:BIKE3] ~g~Du brauchst eine schnelle Maschine, um auf das Dach zu gelangen. [BM3_8:BIKE3] ~g~Spring mit dem Bike von diesen Stufen auf das Dach auf der anderen Seite der Straße. [BM3_1:BIKE3] ~g~Eine Gang hat Mitch Bakers Maschine geklaut. Beschaff sie ihm wieder! [BM3_9:BIKE3] ~g~Schnapp dir Mitchs Maschine und mach dich aus dem Staub! {=================================== MISSION TABLE BMX_1 ===================================} [GETBIK2:BMX_1] Du hast ~1~ Sekunden, um auf ein Gelãndemotorrad zu steigen! {=================================== MISSION TABLE BOATBUY ===================================} [DRUG_1:BOATBUY] Hallo? Hal-lo?! Hallo? [DRUG_13:BOATBUY] Meister? [DRUG_2:BOATBUY] Mach aus. Da kommt einer. [DRUG_3:BOATBUY] Tag, Herr Anzugmensch. Sie müssen der neue Eigentümer sein. [DRUG_4:BOATBUY] Ja. Welches von den Booten ist das schnellste? [DRUG_5:BOATBUY] Das ist schon im Wasser, Meister. [DRUG_6:BOATBUY] Dachte mir, dass Sie es mal testen wollen. [DRUG_7:BOATBUY] Das Ding hat jetzt schon 300 PS unter der Haube, Meister. [DRUG_8:BOATBUY] Und mit dem Fiberglas-Rumpf - das Ding pfeift nur so durch die Wellen! [DRUG_9:BOATBUY] In vier Sekunden von null auf sechzig Meilen, Meister. [DRUG_10:BOATBUY] Und im Rumpf kriegen Sie so 20 Ladungen vom besten jamaikanischen Gras unter. [DRUG_11:BOATBUY] Nur zu, Chef, machen Sie 'ne Spritztour! [DRUG_12:BOATBUY] Hey Anzugmensch, haben Sie mal Feuer? {=================================== MISSION TABLE CAP_1 ===================================} [CAP1_B1:CAP_1] ~g~Die Mafia verlangt Schutzgeld von dir. Stöbere sie auf und erledige sie. [CAP1_B2:CAP_1] ~g~Die Mafia verlangt von dir Schutzgeld für die Bootswerft! [CAP1_B3:CAP_1] ~g~Die Mafia verlangt von dir Schutzgeld für die Eiscremefabrik! [CAP1_B4:CAP_1] ~g~Die Mafia verlangt von dir Schutzgeld für das Autohaus! [CAP1_B5:CAP_1] ~g~Die Mafia verlangt von dir Schutzgeld für das Taxiunternehmen! [CAP_01:CAP_1] Ok, wo brennt's denn? [CAP_02:CAP_1] Wer war das? [CAP_03:CAP_1] Tommy... ein paar Mafia-Typen... sie wollen wiederkommen, ihren Anteil abkassieren. [CAP_04:CAP_1] Ein gewisser Mr. Forello hãtte dir Geld gegeben. Mir geht's schlecht. [CAP_05:CAP_1] Forelli? SONNY Forelli? [CAP_06:CAP_1] Ja, so heißt er, glaube ich... ich konnte nichts gegen sie ausrichten. [CAP_07:CAP_1] Keine Angst, mein Freund, ich mach dir keine Vorwürfe. [CAP_08:CAP_1] Bringt ihn ins Krankenhaus. [CAP_09:CAP_1] Tommy... mach den Kerl 'nen Kopf kürzer für mich... [CAP_10:CAP_1] Ich mach ihn zwei Köpfe kürzer... [CAP1_2:CAP_1] Du kennst die Regeln, Vercetti! [CAP1_3:CAP_1] Mr. Forelli bestellt schöne Grüße! [CAP1_4:CAP_1] Das ist der Harwood Butcher! [CAP1_5:CAP_1] Sag Sonny, er soll mir vom Leib bleiben! [CAP1_6:CAP_1] Vice City gehört nicht mehr ihm, sondern MIR! [CAP1_7:CAP_1] Du glaubst, du kannst mich vom Thron stürzen, Vercetti? [CAP1_8:CAP_1] Wir jagen dich bis ans Ende deiner Tage, Vercetti. [CAP1_9:CAP_1] Du hast keine Chance, du irrer Idiot! [CAP1_10:CAP_1] Ich mach dich fertig, Vercetti. [CAP1_11:CAP_1] Du warst schon immer ein Schwachkopf. [CAP1_12:CAP_1] Du wirst dran glauben, Vercetti. [CAP1_B6:CAP_1] ~g~Du hast den Eintreiber gefunden, erledige ihn. [CAP1_B7:CAP_1] ~g~Du hast den Eintreiber verloren. [CAP1_B8:CAP_1] ~r~Der Eintreiber verlangt für all deine Geschãfte Schutzgeld. [CAP1_B9:CAP_1] ~g~Die Mafia verlangt Schutzgeld für das Malibu! [CAP1_B0:CAP_1] ~g~Die Mafia verlangt Schutzgeld für das Filmstudio! [CAP1_C2:CAP_1] ~g~Die Mafia ist bei der Bootswerft angekommen! [CAP1_C3:CAP_1] ~g~Die Mafia ist bei der Eiscremefabrik angekommen! [CAP1_C4:CAP_1] ~g~Die Mafia ist bei dem Autohaus angekommen! [CAP1_C5:CAP_1] ~g~Die Mafia ist bei dem Taxiunternehmen angekommen! [CAP1_C9:CAP_1] ~g~Die Mafia ist im Malibu Club angekommen! [CAP1_C0:CAP_1] ~g~Die Mafia ist im Filmstudio angekommen! [CAP1_D2:CAP_1] ~g~Die Mafia verlãsst die Bootswerft! [CAP1_D3:CAP_1] ~g~Die Mafia verlãsst die Eiscremefabrik! [CAP1_D4:CAP_1] ~g~Die Mafia verlãsst das Autohaus! [CAP1_D5:CAP_1] ~g~Die Mafia verlãsst das Taxiunternehmen! [CAP1_D9:CAP_1] ~g~Die Mafia verlãsst den Malibu Club! [CAP1_D0:CAP_1] ~g~Die Mafia verlãsst das Filmstudio! [CAP1B10:CAP_1] Du hast die Eintreiber erledigt. Es kommen weitere. {=================================== MISSION TABLE CARBUY ===================================} [CAR1_1:CARBUY] B.J. Smith. Und Sie müssen Mr. Vercetti sein. [CAR1_2:CARBUY] Soll ich Sie herumführen? [CAR1_3:CARBUY] Warum nicht? [CAR1_4:CARBUY] Tja, ich verkaufe das Autohaus ja eigentlich nur ungern. [CAR1_5:CARBUY] War meine erste Investition, nachdem ich Football-Profi wurde. [CAR1_6:CARBUY] Aber es wird Zeit für eine Luftverãnderung. [CAR1_7:CARBUY] Sie verlassen die Stadt? [CAR1_8:CARBUY] Nicht in allzu großer Eile, hoffe ich doch? [CAR1_9:CARBUY] Nein, ich bereite mich nur auf mein Comeback als Football-Profi vor. Ich hatte schon aufgehört. [CAR1_10:CARBUY] Das Geschãft lief nicht allzu gut. [CAR1_11:CARBUY] Da haben sich meine Angestellten was einfallen lassen, [CAR1_12:CARBUY] um ein bisschen was 'nebenbei' zu erwirtschaften. [CAR1_13:CARBUY] Ich könnte den Laden auch dichtmachen, bevor ich ihn Ihnen verkaufe. [CAR1_14:CARBUY] Ich könnte die Bude bei Bedarf sogar abfackeln. [CAR1_15:CARBUY] Das ist erstklassiger Baugrund hier. [CAR1_16:CARBUY] Machen Sie sich mal keine Gedanken. [CAR1_17:CARBUY] Der Laden ist genau das, was ich brauche. [CAR1_18:CARBUY] Ja. Dann kommen wir also ins Geschãft? {=================================== MISSION TABLE CARPAR1 ===================================} [MM_1_A:CARPAR1] ~g~Passiere ~y~5 checkpoints~r~ OHNE irgendwelche ~r~Hütchen ~g~umzufahren! ~g~Die ~y~REIHENFOLGE IST BELIEBIG. [CONE_1:CARPAR1] ~r~Du hast ein Hütchen umgefahren!! [MM_1_C:CARPAR1] ~y~PASSIERE~g~ einen Checkpoint, dann lãuft die Zeit. ~g~Jeder Checkpoint bringt dir ~y~~1~ SEKUNDEN~g~. {=================================== MISSION TABLE COPCAR ===================================} [C_COMP1:COPCAR] Bürgerwehr-Mission Level 12 beendet: Deine max. Panzerung erhöht sich auf 150 [CLEVEL:COPCAR] Bürgerwehr-Mission Level ~1~ [C_PASS:COPCAR] BEDROHUNG AUSGERÃUMT: $ ~1~ [KILLS:COPCAR] HITS: [C_BREIF:COPCAR] ~g~Verdãchtiger wurde zuletzt in der Gegend von ~a~ gesichtet. [COPCART:COPCAR] ~g~Du hast ~1~ Sekunden, um zu einem Polizeifahrzeug zurückzukehren, bevor die Mission endet. [C_CANC:COPCAR] ~r~Bürgerwehr-Mission abgebrochen! [C_TIME:COPCAR] ~r~Deine Zeit als Gesetzeshüter ist vorbei! {=================================== MISSION TABLE COUNT1 ===================================} [CM1_A:COUNT1] Mr.Vercetti? Hey. Sie haben die alte Druckerei gekauft? [CM1_B:COUNT1] Ja, mein Vater hat an diesen Dingern gearbeitet. [CM1_C:COUNT1] Ich sollte in seine Fußstapfen treten, aber...es ist anders gekommen. [CM1_D:COUNT1] Wollen Sie die alten Maschinen verkaufen, das Werk abreißen? [CM1_E:COUNT1] Ich denke nach, ob wir was drucken sollten. Eine Zeitung, ein Magazin... [CM1_F:COUNT1] Ach, Blödsinn, absoluter Blödsinn. Ich drucke lieber Geld. Ist gar nicht so schwer. [CM1_G:COUNT1] In kleinerem Umfang mache ich das schon seit Jahren. [CM1_H:COUNT1] Tatsãchlich? [CM1_I:COUNT1] Klar. Aber wir brãuchten gute Platten. [CM1_J:COUNT1] Natürlich! In Florida gibt es schon ein Geldfãlscher-Syndikat. [CM1_K:COUNT1] Ein Syndikat? [CM1_L:COUNT1] Ja. Hab aber nur gerüchteweise davon gehört. [CM1_M:COUNT1] Ich kenne einen, der kennt sich mit Gerüchten aus... [CM1_N:COUNT1] Hab immer die Abende mit ihm verbracht, die Walzen reinigen. [CM1_2A:COUNT1] Sieh dir diesen Hintern an! [CM1_2B:COUNT1] Tja, Kleine, du weißt nicht, was dir entgeht! [CM1_2C:COUNT1] Na, alter Freund, wie lãuft's so? [CM1_2D:COUNT1] Was weißt du über Geldfãlscherei? [CM1_2E:COUNT1] 'Oh, alles bestens, Paul, und bei dir?' [CM1_2F:COUNT1] Komm her! [CM1_2G:COUNT1] Ist ja gut, ist ja gut. Anscheinend bist du schwer beschãftigt. [CM1_2H:COUNT1] Über Falschgeld weiß ich nur, dass die Triaden die Platten liefern. [CM1_2I:COUNT1] Die haben eine Reederei unten bei den Docks. [CM1_2J:COUNT1] Der Boss weiß, wann die nãchsten Platten reinkommen. [CM1_2K:COUNT1] Danke, Paul. [CM1_2L:COUNT1] Was ist bloß los mit dir, du Wahnsinniger! [CM1_2M:COUNT1] Gib mir noch einen Drink, hopp! [CM1_3:COUNT1] ~g~Man hat dich bemerkt! [CM1_5:COUNT1] ~g~Begib dich zu Kent Paul in den Malibu Club! [CNT1_1:COUNT1] Wer sind Sie? Uff! Aiiee! Nicht ins Gesicht! Nicht ins Gesicht! [CM1_1:COUNT1] ~g~Begib dich zum Schiff der Chartered Libertine Lines bei den Docks. [CM1_2:COUNT1] ~g~Der Reedereichef verfügt über die benötigten Informationen. [CNT1_2:COUNT1] Ok, ich rede ja! Ich rede! [CM1_6:COUNT1] ~g~Begib dich mit den Informationen zurück in die Druckerei! {=================================== MISSION TABLE COUNT2 ===================================} [CNT2_B1:COUNT2] Ok, der Kurier holt die Druckplatten heute vom Hafen ab. [CNT2_B2:COUNT2] Ich fange ihn ab, schnapp mir die Platten, hãng die Cops ab und komm wieder hierher. [CNT2_B3:COUNT2] Also, je nachdem wie es lãuft, haben wir [CNT2_B4:COUNT2] entweder 5 Minuten zum Gelddrucken, bis das Fãlschersyndikat uns findet, oder wir haben alle Zeit der Welt. [CNT2_B5:COUNT2] Jedenfalls will ich 5 Minuten nachdem ich hier bin, Scheinchen aus der Presse kommen sehen! [CNT2_B6:COUNT2] Keine Sorge, Tommy. Wir sind bereit. [CNT2_B7:COUNT2] Ich und die Jungs bleiben in der Nãhe, falls die Cops dir in die Quere kommen. [CNT2_B8:COUNT2] Ok. Jeder weiß, was er zu tun hat? Gut. Bis spãter. [CNT2_01:COUNT2] ~g~Der ~r~Kurier~g~ mit den Druckplatten kommt jeden Moment in einem Helikopter an den ~y~Docks~g~ an. [CNT2_02:COUNT2] ~r~Der Kurier mit den Platten ist im Helikopter geflohen. [CNT2_03:COUNT2] ~r~Der Kurier ist an seinem Ziel angekommen. Du kommst zu spãt! [CNT2_04:COUNT2] ~r~Du hast die Platten in der Explosion zerstört! [CNT2_05:COUNT2] ~g~Du hast die Druckplatten. Bring sie in die Druckerei. [CNT2_06:COUNT2] ~g~Der Kurier ist hinüber. Hol dir die Platten, ehe dir jemand zuvorkommt. [CNT2_07:COUNT2] ~g~Eine der Wachen hat die Platten genommen. Lass ihn nicht entkommen! [CNT2_08:COUNT2] ~g~Der ~r~Kurier~g~ mit den Platten ist an den Docks eingetroffen. [CNT2_4:COUNT2] Privatangelegenheit. Du hast hier nichts verloren! [CNT2_09:COUNT2] DRUCKEREI ERWORBEN [CNT2_10:COUNT2] ~g~Die Druckerei generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmãßig ab. [CNT2_11:COUNT2] ~r~Die Platten liegen auf dem Meeresgrund! {=================================== MISSION TABLE CUBAN1 ===================================} [CUB1_A:CUBAN1] Was ist, Mann? [CUB1_B:CUBAN1] Hey, langsam, Paps, der ist für mich. Bist du der Bursche? [CUB1_C:CUBAN1] Oh ja, du bist der Bursche. Glaub schon. [CUB1_D:CUBAN1] Nein, glaub ich nicht. [CUB1_E:CUBAN1] Ach ja? Komm her du Großmaul. [CUB1_F:CUBAN1] Meinst du, du kannst es mit mir aufnehmen? [CUB1_G:CUBAN1] Meinst du, du kannst mich für blöd verkaufen? [CUB1_H:CUBAN1] Nein, ich glaube, deine Blödheit reicht für uns beide. [CUB1_I:CUBAN1] Hey, er sagt, du bist dumm, mein Sohn. [CUB1_J:CUBAN1] Und ich sage, er ist ein kleines Mãdchen, Paps. [CUB1_K:CUBAN1] Sieh dir doch an, wie er angezogen ist. [CUB1_L:CUBAN1] Was ist los? Ist heute Weiberabend? [CUB1_M:CUBAN1] Du willst ein harter Kerl sein und ziehst dich an wie ein Weib? [CUB1_N:CUBAN1] Hast du auch ein Höschen an, oder was? [CUB1_O:CUBAN1] Was hast du gegen Frauen? Ziehst du Mãnner vor? [CUB1_P:CUBAN1] Ich liebe Frauen! Ich liebe alle Frauen! Ich liebe meine Mutter, Chico. [CUB1_Q:CUBAN1] Ok, ok, ich glaub's dir ja. [CUB1_R:CUBAN1] Kannst du fahren, Amigo? [CUB1_S:CUBAN1] Ja...wie eine Frau. [CUB1_T:CUBAN1] Sehr witzig. Du gefãllst mir, Großer. Vielleicht kannst du mir helfen. [CUB1_U:CUBAN1] Vielleicht kannst du beweisen, dass du ein Mann bist. Hah? [CUB1_V:CUBAN1] Schnapp dir das Boot. [CUB1_W:CUBAN1] Zeig mir, dass du ein ganzer Kerl bist, [CUB1_X:CUBAN1] und kein kleines Mãdchen. [CUB1_02:CUBAN1] Ok Mann, behandle es wie eine Frau. [CUB1_03:CUBAN1] Nicht schlecht, du bist ein echter Mann. [CUB1_04:CUBAN1] Mann, du bist ein ganzer Kerl, Amigo. [CUB1_05:CUBAN1] Amigo, du bist ein Mann, Mann. [CUB1_06:CUBAN1] Du willst ein Mann sein, Mann? [CUB1_07:CUBAN1] Du bist ein feiges Würstchen, Kleiner, heul dich bei Mammi aus. [CUB1_08:CUBAN1] Du bist zu nichts zu gebrauchen. Macht einen auf harter Mann, aber fãhrt wie ein Idiot. [CUB1_09:CUBAN1] Mann, du bist der Hammer, Mann. Du gefãllst mir. Du gefãllst mir sehr. [CUB1_10:CUBAN1] Du bist gut, Mann. Weil du ein ganzer Kerl bist. Alle meine Freunde sind ganze Kerle. [CUB1_11:CUBAN1] Du hast Rico erledigt! [CUB1_12:CUBAN1] Fahre durch den ersten Checkpoint, um den Test zu beginnen. [CUB1_14:CUBAN1] Steig wieder ins Boot! [CUB1_15:CUBAN1] Du bist zu langsam, Mann. [CUB1_01:CUBAN1] Hi, ich bin Rico. Bist du der 'ganze Kerl'? [CUB1_13:CUBAN1] ~g~Du hast drei Minuten, um den Rundkurs zu schaffen. {=================================== MISSION TABLE CUBAN2 ===================================} [CUB2_25:CUBAN2] ERLEDIGE ALLE HAITIANER!! [CUB2_M:CUBAN2] Wer sich mit mir anlegt, hat sich den stãrksten der Stadt rausgesucht! [CUB2_A:CUBAN2] Eine Kaffee, bitte, Alberto. [CUB2_N:CUBAN2] Kein Problem, Tommy. [CUB2_B:CUBAN2] Paps! Es gibt ein Riesenproblem! [CUB2_O:CUBAN2] Umberto, mein Sohn, was ist passiert? [CUB2_C:CUBAN2] Die Haitianer! Ich hasse die Haitianer! [CUB2_D:CUBAN2] Die haben mir das letzte Mal ans Bein gepinkelt! [CUB2_E:CUBAN2] Diese Haitianer. Wir machen sie fertig! [CUB2_F:CUBAN2] Aber dazu brauchen wir Hilfe. [CUB2_G:CUBAN2] Ich hab dabei schon ein paar Brüder verloren. [CUB2_H:CUBAN2] Amigo, du fãhrst gut! [CUB2_I:CUBAN2] Für eine Frau, was? [CUB2_J:CUBAN2] Jetzt ist nicht die Zeit für Witze! [CUB2_K:CUBAN2] Komm, fahr noch mal für mich! [CUB2_L:CUBAN2] Bring meine Jungs da rüber, und dann erledigen wir diese Haitianer! [CUB2_01:CUBAN2] Da ist nicht genug Platz, Mann, du brauchst ein größeres Auto. [CUB2_02:CUBAN2] Wir brauchen Verstãrkung aus dem Café! [CUB2_03:CUBAN2] ~g~Besorg dir ein Auto und hole die Kubaner vor Robinas Café ab. [CUB2_04:CUBAN2] ~g~Setze die Kubaner am Kampfort ab. [CUB2_05:CUBAN2] Schalte den feigen Heckenschützen aus! [CUB2_07:CUBAN2] Die kãmpfen wie Weiber! In Deckung! [CUB2_09:CUBAN2] Heckenschütze auf dem Dach! [CUB2_11:CUBAN2] ~r~Du Idiot! Das Auto hãtten wir gebraucht! [CUB2_12:CUBAN2] Hey, Amigo! Schön zu sehen, dass du's geschafft hast! [CUB2_13:CUBAN2] Stinkendes Haitianer-Nest. Das werden wir komplett ausheben! [CUB2_14:CUBAN2] ANGRIFF! [CUB2_15:CUBAN2] Los, meine Brüder, ANGRIFF!! [CUB2_16:CUBAN2] Tommy, wir haben unseren mannhaften Mut bewiesen! [CUB2_17:CUBAN2] Lass uns diesen Wagen voller Stoff nehmen und abhauen! [CUB2_18:CUBAN2] ~g~Besorge dir ein Auto und hole die Kubaner ab. [CUB2_19:CUBAN2] Wir werden kãmpfen wie Mãnner! [CUB2_21:CUBAN2] Kãmpfen wie ganze Kerle! [CUB2_22:CUBAN2] ~g~Schalte die restlichen Haitianer aus, damit die Kubaner vorrücken können. [CUB2_23:CUBAN2] ~g~In Little Haiti wird es von Haitianern wimmeln, die sich an den Kubanern rãchen wollen. Sei vorsichtig. [CUB2_24:CUBAN2] ~g~Kehre mit dem Van zu Robinas Café zurück und parke hinter dem Haus. {=================================== MISSION TABLE CUBAN3 ===================================} [CUB3_A:CUBAN3] Alberto? Einen Kaffee, Senor? [CUB3_B:CUBAN3] Paps, gib dieser falschen Schlange nichts. [CUB3_C:CUBAN3] Du hast zwei Gesichter, Tommy! [CUB3_D:CUBAN3] Du hast entweder zwei Gesichter oder du bist ein Feigling, Kleiner! [CUB3_E:CUBAN3] Die Haitianer, Mann, die lachen mich aus. [CUB3_F:CUBAN3] Ruhig, ruhig. Was gibt es für Probleme? [CUB3_G:CUBAN3] Sie lachen mich aus, Tommy. Mich! [CUB3_H:CUBAN3] Umberto Robina. Die tun, was sie wollen! [CUB3_I:CUBAN3] Die tun nicht, was sie wollen, Umberto. Sie tun, was du sie tun lãsst. [CUB3_J:CUBAN3] Was? [CUB3_K:CUBAN3] Soll jemand aus dem Weg gerãumt werden? [CUB3_L:CUBAN3] Ich kann das machen, aber es kostet. [CUB3_M:CUBAN3] Ich weiß, wir sind Brüder und alles, aber hier geht's um ein Geschãft. [CUB3_N:CUBAN3] Tommy. Du bist ein echter Mann. Ein Geschãftsmann, ein Gentleman. [CUB3_O:CUBAN3] Die Haitianer, sie erwarten eine Schiffladung Stoff, richtig gutes Zeug. [CUB3_P:CUBAN3] Wir schnappen es uns und erledigen sie. [CUB3_Q:CUBAN3] Du schnappst es dir, ich passe auf dich auf. Wie auf einen Bruder, einen Sohn. [CUB3_R:CUBAN3] Ich glaube, Cash ist mir lieber als auf deinen Knien zu reiten, Amigo. [CUB3_01:CUBAN3] Hey, Rico. Nettes Boot. Bist du bereit? [CUB3_03:CUBAN3] ~g~Sammle alle Aktenkoffer mit Stoff und Geld ein. [CUB3_04:CUBAN3] ~g~Bringe die Drogen und das Geld zu Umberto. [CUB3_05:CUBAN3] Ja, Tommy. Also, sei ein guter Kapitãn heute. [CUB3_06:CUBAN3] Mein Boot nützt mir nichts, wenn es durchlöchert ist, ok? [CUB3_07:CUBAN3] ~g~Begib dich zu Rico. Er fãhrt dich zum Treffpunkt. [CUB3_02:CUBAN3] ~g~ERLEDIGE ALLE HAITIANER AUF DEN BOOTEN!! [CUB3_08:CUBAN3] Oh-oh... eine Bande Kubaner. Wir werden angegriffen! {=================================== MISSION TABLE CUBAN4 ===================================} [CUB4_A:CUBAN4] Hey, Ladies. Wisst ihr, was ich mache? [CUB4_B:CUBAN4] Ich erledige einen Haitianer. Und dann? [CUB4_C:CUBAN4] Dann mache ich Liebe wie ein Mann. [CUB4_D:CUBAN4] Weißt du, Kleine? So in der Art. [CUB4_E:CUBAN4] Penner! [CUB4_F:CUBAN4] Vollidiot. [CUB4_G:CUBAN4] Hey, Baby, dich würde ich nicht mit 'ner Kneifzange anfassen! [CUB4_H:CUBAN4] Umberto Robina mag Frauen, keine Ziege mit Rock! [CUB4_I:CUBAN4] Tommy!! Tommy, ich liebe dich, ich liebe dich! Lass uns gehen! [CUB4_J:CUBAN4] Wohin denn? Kann ich nicht noch einen Kaffee trinken? [CUB4_K:CUBAN4] Keine Zeit! Außerdem habe ich erst einen getrunken. [CUB4_L:CUBAN4] Wir nehmen uns die Haitianer vor. [CUB4_M:CUBAN4] Tommy, wie erledigt man eine Schlange? [CUB4_N:CUBAN4] Man beißt sie in den Hintern! Hahaha! [CUB4_O:CUBAN4] Wenn du es sagst, Umberto. [CUB4_P:CUBAN4] Tommy, geh und besorge uns ein kleines haitianisches Auto. [CUB4_Q:CUBAN4] Wenn du es hast, komm zurück und hol meinen Jungen ab [CUB4_R:CUBAN4] Pepe, und fahre ihn zu den Haitianern. [CUB4_S:CUBAN4] Dann begibst du dich zur Laboranlage der Haitianer und verwendest ihr Lösungsmittel als Sprengstoff. [CUB4_T:CUBAN4] Bumm! Bye bye! [CUB4_U:CUBAN4] Umberto, was ist mit dir? [CUB4_V:CUBAN4] Oh, ich halte mich raus und passe mit Paps auf das Café auf. [CUB4_W:CUBAN4] Er fühlt sich nicht besonders, weißt du. [CUB4_02:CUBAN4] ~g~Die Bomben werden per Zeitzünder auf 45 sek. gestellt sein. [CUB4_07:CUBAN4] Zum Lösungsmittel geht's hintenrum, Amigo. [CUB4_08:CUBAN4] Hola, Amigos. [CUB4_09:CUBAN4] Bueno. Haitian Putas. Muerte. [CUB4_10:CUBAN4] Vamos. [CUB4_11:CUBAN4] Genau, vamos. [CUB4_12:CUBAN4] Hey, wir brauchen ein haitianisches Gang-Auto! [CUB4_13:CUBAN4] Oye, suchen wir unsere Muchachos! [CUB4_14:CUBAN4] Folge meinen Compadres. [CUB4_15:CUBAN4] Ok, nur immer rein... [CUB4_16:CUBAN4] Ich lege die Bombe. Gib mir Deckung. [CUB4_17:CUBAN4] RENN! [CUB4_18:CUBAN4] Mann, das ist ein schönes Viertel hier... [CUB4_19:CUBAN4] Das ist doch eine Müllhalde, Mann. [CUB4_20:CUBAN4] Ich kannte mal eine schöne Frau, die hat hier gewohnt. [CUB4_21:CUBAN4] Die machen gute Pizza hier. [CUB4_22:CUBAN4] Hey, Mann! Du fãhrst wie ein Verrückter! [CUB4_23:CUBAN4] Hast du dich verfahren, Mann? [CUB4_24:CUBAN4] Du hast Pepe vergessen, hole ihn. [CUB4_03:CUBAN4] ~g~Bleib im Wagen, bis er sicher auf dem Gelãnde geparkt ist. [CUB4_26:CUBAN4] ~g~Nimm dir Pepe, fahr Richtung Norden nach Little Haiti und klaue einen Voodoo. [CUB4_27:CUBAN4] ~g~Triff dich mit Rico und den anderen Kubanern. [CUB4_28:CUBAN4] ~g~Triff dich mit den anderen Kubanern bei der haitianischen Drogenfabrik. [CUB4_29:CUBAN4] ~g~Gehe in jede der Markierungen, um an dieser Stelle eine Bombe zu legen. [CUB4_30:CUBAN4] ~g~Wenn alle drei Bomben gelegt sind, entferne dich von der Fabrik bevor sie hochgeht. [CUB4_31:CUBAN4] ~g~Mach, dass du von der Fabrik wegkommst!! [CUB4_32:CUBAN4] ~g~Park den Wagen an der im Radar markierten Stelle und steig aus. [CUB4_06:CUBAN4] ~r~Du bist nicht weit genug von der Basis weggekommen, wir mussten die Detonation abbrechen! {=================================== MISSION TABLE FINALE ===================================} [FIN1_01:FINALE] Was ist los? [FIN1_02:FINALE] Tommy! Ah, gut, gut. Hör zu. Hör zu... [FIN1_03:FINALE] Ich mag Fische. Ich liebe Fische. [FIN1_04:FINALE] Ich liebe sie als Haustiere oder auch lecker zubereitet. [FIN1_05:FINALE] Aber ich liebe sie nicht so sehr, dass ich als Fischfutter im Hafen enden will. [FIN1_06:FINALE] Und jetzt kommen auf einmal deine italienischen Brüder an und wollen mir Zementschuhe verpassen. Und ich... [FIN1_07:FINALE] Halt den Mund, Ken. Setz dich. [FIN1_08:FINALE] Lance, was lãuft hier, verdammt? [FIN1_09:FINALE] Deine Freunde aus dem Norden, Tommy. Die sind nicht sehr froh, dass du ihren Mann erledigt hast. [FIN1_10:FINALE] Sie kommen heute, um nach dem Rechten zu sehen. [FIN1_11:FINALE] Sie haben lãnger gebraucht, als ich dachte... [FIN1_12:FINALE] Jungs, wir müssen denen ein für alle Mal klar machen, dass das mein Laden ist. MEINER! [FIN1_13:FINALE] Ken, hol die erste Ladung von dem Falschgeld und pack 20 Mios in Aktenkoffer. [FIN1_14:FINALE] Lance, du trommelst die anderen Jungs zusammen... [FIN2_01:FINALE] Tommy! [FIN2_02:FINALE] Was ist? Keine Umarmung für deinen alten Freund? [FIN2_03:FINALE] Ich war 15 Jahre weg vom Fenster, [FIN2_04:FINALE] bin nicht mehr auf dem Laufenden, was Manieren angeht. [FIN2_05:FINALE] Immer Wut im Bauch, hã, Tommy? [FIN2_06:FINALE] Ich sag's ja, dein Temperament wird dir nochmal schlecht bekommen. [FIN2_07:FINALE] In den Koffern sind 20 Millionen... [FIN2_08:FINALE] Wie viele waren es denn? Zehn? Nein, elf Mãnner. [FIN2_09:FINALE] So kommt man zu dem Spitznamen 'Harwood-Butcher'! Hehehe! [FIN2_10:FINALE] Ich sollte EINEN Mann für dich erledigen! Die wussten, dass ich komme, Sonny... [FIN2_11:FINALE] Wie redest du denn mit mir? [FIN2_12:FINALE] Wie kommst du darauf, mich für diesen unglücklichen Zufall verantwortlich zu machen? [FIN2_13:FINALE] Nimm einfach das Geld... [FIN2_14:FINALE] Holt das Geld! [FIN2_15:FINALE] Weißt du, Tommy, ich hab für dich getan, was nur ging. Himmel und Hölle in Bewegung gesetzt. [FIN2_16:FINALE] Ich war dein Freund. Ich dachte, du nimmst Vernunft an. Kapierst, was gut fürs Geschãft ist. [FIN2_17:FINALE] Ich hab dir vertraut, Tommy, und du hast mich enttãuscht. [FIN2_18:FINALE] Aber wenigstens einer in deiner mickrigen Organisation weiß, wie man Geschãfte macht. [FIN2_19:FINALE] Stimmt's, Lance? [FIN2_20:FINALE] Sorry, Tommy. So lãuft's in Vice City. So lãuft das Geschãft. [FIN2_21:FINALE] Du hast uns verraten... [FIN2_22:FINALE] Nein, ich hab DICH verraten, Tommy, nur DICH. [FIN2_23:FINALE] Die echten Piepen sind oben im Safe. [FIN2_24:FINALE] Tommy, wie hast du dir das denn vorgestellt? [FIN2_25:FINALE] Dachtest du, ich lass mich mit Blüten abspeisen? [FIN2_26:FINALE] Dass ich den Schwanz einziehe und abhaue, um nicht das Gesicht zu verlieren? [FIN2_27:FINALE] Nein. [FIN2_28:FINALE] Ich wollte dich nur noch ein bisschen ãrgern, bevor ich dich fertig mache. [FIN3_01:FINALE] Tommy? [FIN3_02:FINALE] Oh Gott, Tommy! Was ist passiert? [FIN3_03:FINALE] Wonach sieht's denn aus? [FIN3_04:FINALE] Sieht aus, als wãr dein Anzug ruiniert! [FIN3_05:FINALE] Und das war ein wunderbarer Anzug! Tommy, Herrgott, was ist passiert? [FIN3_06:FINALE] Kleine Meinungsverschiedenheit mit einem Geschãftsfreund. Wie das so ist. [FIN3_07:FINALE] Wenn ich eine Meinungsverschiedenheit mit einem Geschãftsfreund habe, schick ich ihm einen bösen Brief. [FIN3_08:FINALE] Oder ich pinkle ihm in den Briefkasten, aber ich fang nicht den 3. Weltkrieg an. [FIN3_09:FINALE] Vielleicht solltest du mal zu meinem Therapeuten gehen... [FIN3_10:FINALE] Dieser Dreckskerl von Lance... [FIN3_11:FINALE] Tommy, ich konnte den Kerl ja nie leiden. [FIN3_12:FINALE] Er ist neurotisch, unsicher, selbstsüchtig - Er ist ein Arschloch! [FIN3_13:FINALE] Gut, dass du ihn fertig gemacht hast! [FIN3_14:FINALE] Ich glaub auch nicht, dass wir nochmal Ãrger mit denen aus dem Norden kriegen... [FIN3_15:FINALE] Die aus dem Norden gibt's nãmlich nicht mehr. [FIN3_16:FINALE] Gibt nur noch die im Süden. [FIN3_17:FINALE] Moment, verstehe ich dich richtig, Tommy, Heißt das...? [FIN3_18:FINALE] Na, was meinst du, was das heißt? [FIN3_19:FINALE] Dass wir jetzt die Herren im Haus sind... ich meine, dass DU der Herr im Haus bist. Oh, Tommy... [FIN3_20:FINALE] Weißt du, Ken, das könnte der Beginn einer wunderbaren Geschãftsbeziehung sein... [FIN3_21:FINALE] Schließlich bist du ein hinterlistiger, mieser kleiner Dieb... [FIN3_22:FINALE] und ich bin ein verurteilter Psychopath und Dealer. [FIN3_23:FINALE] Ich weiß. Ist das nicht wunderbar? [FIN_B1:FINALE] ~g~Erledige den Verrãter ~y~Vance~g~. [FIN_B2:FINALE] ~g~Erledige ~p~Sonny~g~, um die Sache ein für alle Mal zuende zu bringen. [FIN_B3:FINALE] ~g~Die Mafia will dein Geld stehlen. Verteidige den Safe. [FIN_B4:FINALE] ~g~Du hãltst nicht mehr lange durch. Hol dir unten ein wenig ~w~Energie~g~. [FIN_B5:FINALE] ~g~Die Mafia stiehlt dein Geld. Verteidige den ~c~Safe. [FIN_B7:FINALE] ~r~Die Mafia hat dein gesamtes Geld gestohlen. [DEFSAFE:FINALE] ~g~Geh zurück zum Safe und verteidige ihn. {=================================== MISSION TABLE FIRETRK ===================================} [F_PASS1:FIRETRK] Feuer gelöscht! [F_FAIL2:FIRETRK] ~r~Du kommst zu spãt! [F_CANC:FIRETRK] ~r~Feuerwehr-Mission abgebrochen! [F_EXTIN:FIRETRK] FEUER: [F_START:FIRETRK] ~g~Brennendes Fahrzeug in der Gegend von ~a~ gemeldet. Lösche den Brand. [SIREN_1:FIRETRK] Um die Sirenen dieses Fahrzeugs einzuschalten, drücke die ~h~~k~~VEHICLE_HORN~~w~. [SIREN_2:FIRETRK] Um die Sirenen dieses Fahrzeugs einzuschalten, drücke die ~h~~k~~VEHICLE_HORN~~w~. [FIREPRO:FIRETRK] Feuerwehr-Mission Level 12 abgeschlossen: Du bist jetzt absolut feuerfest!! [F_FAIL1:FIRETRK] Feuerwehr-Mission beendet. [F_STAR1:FIRETRK] ~g~Brennende Fahrzeuge in der Gegend von ~a~ gemeldet. Lösche den Brand. [SPRAY_4:FIRETRK] { reVC update } Benutze die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste, um die Wasserkanone abzufeuern und den ~h~~k~~VEHICLE_TURRETLEFT~~w~ und ~h~~k~~VEHICLE_TURRETRIGHT~~w~, um mit der Wasserkanone zu zielen. [SPRAY_1:FIRETRK] { reVC update } Benutze die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste, um die Wasserkanone abzufeuern und den ~h~~k~~VEHICLE_TURRETLEFT~~w~ und ~h~~k~~VEHICLE_TURRETRIGHT~~w~, um mit der Wasserkanone zu zielen. {=================================== MISSION TABLE GENERA1 ===================================} [GEN1_I:GENERA1] Für diesen kleinen Gefallen werde ich Sie belohnen. Danach suchen wir Ihr Geld gemeinsam. [GEN1_A:GENERA1] Mr. Vercetti! [GEN1_B:GENERA1] Colonel. [GEN1_D:GENERA1] Nein danke. [GEN1_E:GENERA1] Es ist mir peinlich, aber es scheint, unser Problem ist zum Teil auf das lose Mundwerk einer mir vertrauten Person zurückzuführen. [GEN1_F:GENERA1] Seit Jahren schleppe ich Gonzalez mit, aber nun erreicht seine Unfãhigkeit einen neuen Höhepunkt. [GEN1_G:GENERA1] Es ist nur gerecht, wenn Sie Gonzalez erledigen. [GEN1_H:GENERA1] War er es? Mir geht es in erster Linie um das Geld. [GEN1_J:GENERA1] Er ist in seinem Penthouse, vermutlich halb betrunken. Nehmen Sie das hier. [GEN1_06:GENERA1] Er hat eine Kettensãge!! [GEN1_07:GENERA1] Bleib mir vom Leib, du elender Mistkerl! [GEN1_08:GENERA1] Gütiger Himmel, mein Leben ist ruiniert, und mein Aussehen dazu! [GEN1_10:GENERA1] Ich werd dir dein Mundwerk stopfen! [GEN1_11:GENERA1] Bleib stehen, du Fettsack! [GEN1_12:GENERA1] Bleib stehen, und es geht ganz schnell! [GEN1_13:GENERA1] Hör auf zu jammern, das interessiert keinen, Fettsack! [GEN1_C:GENERA1] Danke für Ihr Kommen. Setzen Sie sich. Hummer? [GEN1_04:GENERA1] ~g~Benutze den Eingang, um Zugang zu Gonzalez' Dachwohnung zu erhalten. [GEN1_05:GENERA1] ~g~Erledige Gonzalez! [GEN1_09:GENERA1] Ich zahle dir das Doppelte, Tommy, das DOPPELTE! [GEN1_17:GENERA1] ~g~Gonzalez versucht zu fliehen! Folge ihm durch die Türen und erledige ihn. [GEN1_18:GENERA1] ~r~Gonzalez hat das Polizeirevier sicher erreicht! [GEN1_19:GENERA1] ~g~Die Polizei von Vice City ist hinter dir her! [GEN1_20:GENERA1] ~g~Steig in ein Fahrzeug. [GEN1_21:GENERA1] ~g~Begib dich zur~h~ Pay 'N' Spray-Lackiererei~g~ in~h~ Vice Point~g~. [GEN1_22:GENERA1] ~g~Bring dein Fahrzeug in die Lackiererei, um deinen ~h~Fahndungslevel~g~ loszuwerden und das Fahrzeug zu ~h~reparieren ~g~und ~h~umzuspritzen~g~. Kosten - ~h~$100~g~. Diesmal gratis. [GEN1_01:GENERA1] Um im Laufen eine Nahkampfattacke vorzubereiten, die~o~ |-Taste~w~ gedrückt halten. [GEN1_02:GENERA1] Um im Laufen eine Nahkampfattacke vorzubereiten, die~x~ /-Taste~w~ gedrückt halten. [GEN1_03:GENERA1] Um im Laufen eine Nahkampfattacke vorzubereiten, die~h~ R1-Taste~w~ gedrückt halten. [GEN1_14:GENERA1] Lass die~h~ ~k~~PED_FIREWEAPON~~w~ los, um die Attacke auszuführen. [GEN1_15:GENERA1] Lasse die~h~ ~k~~PED_FIREWEAPON~~w~ los, um die Attacke auszuführen. [GEN1_16:GENERA1] Lasse die~h~ ~k~~PED_FIREWEAPON~~w~ los, um die Attacke auszuführen. [GEN1_23:GENERA1] ~g~Geh durch die Türen zurück ins Erdgeschoss. {=================================== MISSION TABLE GENERA2 ===================================} [COL2_A:GENERA2] Tommy! Setzen Sie sich zu mir. [COL2_B:GENERA2] Sieht das nicht köstlich aus? Tapirschnauze? [COL2_C:GENERA2] Ãh... Nein. Nein, danke. [COL2_D:GENERA2] Tommy, Sie sind wie eine Pampas-Brise, die mich vom Gestank der Korruption befreit hat. [COL2_E:GENERA2] Natürlich muss ich so tun, als trauere ich um ihn und muss wie immer meine Arbeit machen. [COL2_F:GENERA2] Das bringt mich meinem Geld nicht nãher... [COL2_G:GENERA2] Tommy, mein Freund, Sie sind hier nicht in Liberty. Hier regeln wir Dinge anders. [COL2_H:GENERA2] Ich werde weiter nachforschen, zunãchst hãtte ich aber ein lukratives Geschãft abzuschließen. [COL2_I:GENERA2] Freunden tu ich gern einen Gefallen, Cortez... [COL2_J:GENERA2] Sie sind ein guter Freund, Tommy. Ich wusste, ich kann auf Sie zãhlen. [COL2_K:GENERA2] Sie müssen sich mit einem Kurier treffen, der wertvolle 'Technologie' für mich beschafft hat. [COL2_1:GENERA2] Die Regen ist sich sehr nass um diese Jahreszeit... [COL2_2:GENERA2] Was? [COL2_3:GENERA2] Ãh, comment? [COL2_4:GENERA2] Hören Sie, Cortez schickt mich. Geben Sie mir die verdammten Mikrochips. [COL2_5:GENERA2] Oh... ok. [COL2_B1:GENERA2] ~g~Triff dich mit dem Kurier im Einkaufszentrum. [COL2_B2:GENERA2] ~g~Der Kurier flieht mit den Lenkwaffen-Chips. Lass ihn nicht entkommen! [COL2_B3:GENERA2] ~g~Bring die Lenkwaffen-Chips zum Colonel. [COL2_F1:GENERA2] ~r~Du hast die Kontaktperson erledigt! [COL2_F2:GENERA2] ~r~Der Kurier ist hinüber. Schnapp dir die Lenkwaffen-Chips. [COL2_6A:GENERA2] Keine Bewegung, amerikanisches Imperialistenschwein! Das ist Eigentum des französischen Staates. Her damit! [BLIPHLP:GENERA2] Das Radar zeigt ein nach oben zeigendes Dreieck, das bedeutet, dass das Ziel sich in größerer Höhe befindet als der Spieler. [COL2_F3:GENERA2] ~r~Die Lenkwaffen-Chips liegen auf dem Meeresgrund. [COL2_F4:GENERA2] ~r~Der Kurier ist entkommen! Du hast dir die Chips durch die Lappen gehen lassen. {=================================== MISSION TABLE GENERA3 ===================================} [GEN3_49:GENERA3] Lance' Gesundheitszustand: [GEN3_A:GENERA3] Thomas, danke, dass Sie kommen. [GEN3_B:GENERA3] Verzeihen Sie, wenn ich gleich zur Sache komme. [GEN3_C:GENERA3] Diaz bat mich, eine kleinere geschãftliche Transaktion zu überwachen. [GEN3_D:GENERA3] Wird hoffentlich besser laufen als die letzte, hah? [GEN3_E:GENERA3] Darum habe ich an Sie gedacht, mein Freund. [GEN3_F:GENERA3] Ich habe etwas zu Ihrem Schutz beim Parkhaus deponiert. [GEN3_G:GENERA3] Holen Sie es ab. Und dann bewachen Sie Diaz' Mãnner bei dem Deal. [GEN3_H:GENERA3] Danke, Amigo. [GEN3_1:GENERA3] Reißt sich alles selbst unter den Nagel, wie ich sehe. [GEN3_2:GENERA3] Sag mal, ist das alles, was du kannst, mir dauernd nachzuschnüffeln? Komm mit und zeig mir, dass du was drauf hast. [GEN3_3:GENERA3] Könnte ich machen. Ich heiße übrigens Lance. [GEN3_5:GENERA3] Du musst Cortez' neuer Mann sein. [GEN3_6:GENERA3] Bis ich was besseres finde... [GEN3_7:GENERA3] Sie müssen bald hier sein. Wir sollten uns gute Beobachtungsposten suchen. [GEN3_8:GENERA3] Ok! Ich nehm den Balkon, du kletterst auf das Dach drüben im Hof. [GEN3_9:GENERA3] Ich lebe! Idioten! Und das nur wegen dir! Wie heißt du? [GEN3_10:GENERA3] Tommy. [GEN3_11:GENERA3] Wir sehen uns bald wieder, glaube ich! [GEN3_12:GENERA3] Wo steckt denn Lance? Shit... [GEN3_14:GENERA3] Tommy! Ich brauch Hilfe! [GEN3_15:GENERA3] Keine Sorge, ich hab alles im Griff! [GEN3_16:GENERA3] Diaz' Mãnner werden umgemãht! [GEN3_19:GENERA3] ~g~Haitianer! Sie greifen an! Beschütze Diaz! [GEN3_20:GENERA3] ~g~Begib dich zum Parkhaus. Hol dir die Waffe, die der Colonel dort für dich deponiert hat. [GEN3_22:GENERA3] Diaz' Gesundheitszustand: [GEN3_23:GENERA3] ~g~Du hast Lance vergessen. Hole ihn! [GEN3_25:GENERA3] ~r~Lance hat's erwischt! [GEN3_28:GENERA3] ~g~Bring den Aktenkoffer zu Diaz zurück. [GEN3_29:GENERA3] ~g~Hol dir den Aktenkoffer und bringe ihn zu Diaz zurück. [GEN3_30:GENERA3] ~r~Er ist mit dem Geld entwischt! Dafür macht Diaz dich kalt! [GEN3_33:GENERA3] ~r~Du sollst Diaz und seine Mãnner bewachen, nicht beschießen! [GEN3_34:GENERA3] ~r~Es gibt keinen Deal, wenn du die Kubaner erledigst! [GEN3_35:GENERA3] ~g~Er hat Diaz' Geld gestohlen! [GEN3_36:GENERA3] ~g~Schnapp dir das Motorrad, stelle ihn und hol Diaz' Geld zurück! [GEN3_37:GENERA3] ~g~. Die Kubaner kommen. Überwache den Deal. Pass auf Diaz und Lance auf. [GEN3_38:GENERA3] ~r~Diaz hat's erwischt! Du hast versagt! [GEN3_39:GENERA3] ~g~Begib dich zu deinem Beobachtungsposten die Treppe hoch. [GEN3_44:GENERA3] ~g~Begib dich mit Lance zum Übergabeort und pass auf Diaz auf. [GEN3_45:GENERA3] Sie müssen bald hier sein. Wir sollten uns gute Beobachtungsposten suchen. [GEN3_40:GENERA3] { reVC update } Um auf einem ~h~Motorrad ~w~sitzend ~h~geradeaus zu feuern~w~, drücke die ~h~~k~~VEHICLE_FIREWEAPON~. [GEN3_41:GENERA3] { reVC update } Um auf einem ~h~Motorrad ~w~sitzend ~h~geradeaus zu feuern~w~, drücke die ~h~~k~~VEHICLE_FIREWEAPON~. [GEN3_46:GENERA3] Scheiße! [GEN3_47:GENERA3] Tommy! [GEN3_48:GENERA3] Verdammt! [GEN3_50:GENERA3] ~r~Du hast Diaz' Geld verloren! Versuch das nãchste Mal, das Geld nicht zu vernichten! [GEN3_51:GENERA3] Noch mehr verdammte Haitianer in einem beschissenen Van! [GEN3_54:GENERA3] Steht nicht rum, ihr Idioten! Schnappt euch diesen haitianischen Mistkerl! [GEN3_55:GENERA3] Tommy! Ich bleibe hier und passe auf Diaz auf! [GEN3_18:GENERA3] ~g~Die Kubaner kommen. Bleib in Diaz' Nãhe. Überwache den Deal. Pass auf Diaz und Lance auf. [GEN3_56:GENERA3] ~r~Diaz ist hinüber, er ist in einen Hinterhalt geraten. Pass das nãchste Mal auf ihn auf! [GEN3_57:GENERA3] Die Kruger ist ein Sturmgewehr, das einem erlaubt, in der subjektiven Kamera-Einstellung manuell zu zielen. [GEN3_58:GENERA3] Halte die~h~ R1~w~-Taste gedrückt, um mit dem Sturmgewehr zu ~h~zielen~w~. [GEN3_59:GENERA3] Halte die~h~ L1~w~-Taste gedrückt, um mit dem Sturmgewehr zu ~h~zielen~w~. [GEN3_60:GENERA3] Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Sturmgewehr ~h~abzufeuern~w~. [GEN3_61:GENERA3] Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Sturmgewehr ~h~abzufeuern~w~. [GEN3_62:GENERA3] Drücke die ~h~~k~~PED_FIREWEAPON~~w~-Taste, um das Sturmgewehr ~h~abzufeuern~w~. [GEN3_63:GENERA3] Auf~h~ Motorrãdern ~w~kann man nicht nur im Vorbeifahren seitlich auf Ziele schießen, man kann auch ~h~geradeaus feuern~w~. [GEN3_64:GENERA3] { reVC update } Um auf einem Motorrad sitzend geradeaus zu feuern, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste. [GEN3_65:GENERA3] { reVC update } Um auf einem Motorrad sitzend geradeaus zu feuern, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste. [GEN3_66:GENERA3] { reVC update } Um auf einem Motorrad sitzend geradeaus zu feuern, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste. [GEN3_67:GENERA3] Du brauchst eine Maschinenpistole, um auf einem Motorrad sitzend geradeaus zu feuern. [GEN3_53:GENERA3] MEIN GELD! [GEN3_52:GENERA3] Diese Haitianer denken sie könnten RICARDO DIAZ stürzen!?! {=================================== MISSION TABLE GENERA4 ===================================} [DETON:GENERA4] DETONATION: [COL4_3:GENERA4] KONVOI HALT! [COL4_6:GENERA4] WIR STEHEN UNTER FEINDLICHEM BESCHUSS! [COL4_7:GENERA4] Weg da von dem Panzer, Zivilist! [COL4_8:GENERA4] ICH SAGTE, WEG DA! SOFORT! [COL4_9:GENERA4] AUF GEFECHTSPOSITIONEN! [COL4_11:GENERA4] Schaffen Sie den Zivilisten aus dem Weg, Soldat! - Sir, zu Befehl, Sir! [COL4_12:GENERA4] Zivilist im PANZER! HALTET IHN AUF! [COL4_13:GENERA4] Dies ist ein Militãrkonvoi, machen Sie den Weg frei! [COL4_14:GENERA4] Erledigen sie ihn, Soldat. [COL4_15:GENERA4] Schaffen Sie das Zivilfahrzeug aus dem Weg! -Fahrzeug wird weggeschafft, Sir! [COL4_17:GENERA4] OK, EINHEIT WEITER! [COL4_18:GENERA4] Da ist jemand auf dem Panzer, Sir! [COL4_19:GENERA4] Gehen Sie ein paar Donuts holen, Soldat! - Zu Befehl, Sir! [COL4_20:GENERA4] Ziel erfasst, Sir. [COL4_21:GENERA4] HECKENSCHÜTZE! [COL4_22:GENERA4] Ich seh zu, dass ich hier wegkomme! [COL4_23:GENERA4] Einsatz durchgeführt! Einheit, wegtreten! - Gehen wir ein paar Donuts essen. [COL4_24:GENERA4] Sicherheitsmechanismus Delta India Echo aktiviert. Selbstzerstörung des Fahrzeugs eingeleitet! [COL4_26:GENERA4] Mach dein Testament, dreckiger Kommunist! [COL4_B2:GENERA4] ~r~Der Panzer ist wohlbehalten an seinem Ziel angekommen! [COL4_B5:GENERA4] ~r~Der Panzer ist zerstört worden! [COL4_01:GENERA4] Diaz war sehr angetan und würde Sie gerne wiedersehen. [COL4_02:GENERA4] Ist das eine gute Nachricht? [COL4_03:GENERA4] Aber sicher! Obwohl mich der Verdacht beschleicht, dass Diaz für unsere Verluste verantwortlich war... [COL4_04:GENERA4] Wie kommen Sie darauf? [COL4_05:GENERA4] Man erhebt keine Anschuldigungen gegen einen Mann wie Diaz. Ich habe nur laut nachgedacht. [COL4_06:GENERA4] Egal. Ich habe einen Vorschlag, der lukrativ für Sie sein könnte... [COL4_07:GENERA4] Ich habe keine Zeit für weitere Auftrãge, Cortez. [COL4_08:GENERA4] Ein Mann mit solch bedrohlichen Schulden sollte doch um jede Verdienstmöglichkeit dankbar sein. Hören Sie mich wenigstens an. [COL4_09:GENERA4] Na gut... [COL410:GENERA4] Ich habe einen Kãufer für ein 'Militãrgerãt', das durch die Stadt transportiert wird. Beschaffen Sie es! [COL411:GENERA4] Wenn Sie es haben, rufen Sie mich unverzüglich an... [COL4_B4:GENERA4] ~g~Der Panzer ist abgeschlossen. Lass dir etwas einfallen, um die Besatzung herauszulocken. [COL4_1:GENERA4] Was ist mit dem Kanonier? - Weiß nicht, Sir! [COL4_4:GENERA4] Los, sehen Sie nach, Soldat! - Zu Befehl, Sir! [COL4_B1:GENERA4] ~g~Besorge das militãrische Fahrzeug, das durch die Stadt gefahren wird. [COL4_B3:GENERA4] ~g~Liefere den Panzer in der Garage des Colonels ab, bevor er sich selbst zerstört. [COL4_B6:GENERA4] ~g~Finde einen Weg, den Panzer zu klauen! [COL4_B7:GENERA4] ~g~Fahr den Panzer in die Garage. [COL4_B8:GENERA4] ~g~Steig aus dem Panzer und verlasse die Garage. {=================================== MISSION TABLE GENERA5 ===================================} [COL5A_1:GENERA5] Die Lage erfordert ein eiliges Verschwinden, Amigo. [COL5A_2:GENERA5] Was gibt's für ein Problem? [COL5A_3:GENERA5] Die Franzosen wollen ihre Lenkwaffen-Chips wieder und nach dem letzten Zwischenfall [COL5A_4:GENERA5] zieht es mich in sicherere Gefilde. [COL5A_5:GENERA5] Wãre es nicht sicherer zu fliegen? [COL5A_6:GENERA5] Ich wãre erledigt, bevor ich eingecheckt hãtte. Außerdem muss ich Ware außer Landes schaffen. [COL5A_7:GENERA5] Brauchen Sie noch einen Bodyguard? [COL5A_8:GENERA5] Sie, mein Freund, sind zehn Bodyguards wert. Hahaha. [COL5B_1:GENERA5] Thomas, Sie haben mich beschützt und mir treu gedient. [COL5B_2:GENERA5] Aber jetzt müssen Sie uns verlassen, ehe wir das offene Meer erreichen. [COL5B_4:GENERA5] Danke, Colonel. [COL5B_5:GENERA5] Eine Bitte noch. Könnten Sie ein Auge auf Mercedes haben, solange ich weg bin? [COL5B_6:GENERA5] Ich glaub zwar, sie kann auf sich selbst aufpassen, aber klar. [COL5B_7:GENERA5] Danke, mein Freund. Bis zu meiner Rückkehr. [COL5B_8:GENERA5] Adios, Amigo. [COL5_7:GENERA5] Hören Sie auf, auf mich zu schießen! [COL5_9:GENERA5] Tommy, die sollen aufhören, auf mich zu schießen! [COL5_10:GENERA5] Ich genieße diplomatische Immunitãt. [COL5_11:GENERA5] Nicht schießen, ich bin ein Colonel! [COL5_12:GENERA5] Thomas, machen Sie sie fertig. Mein Land wird es Ihnen danken. [COL5_13:GENERA5] Tommy, wir werden von den Franzosen überrannt! [COL5_14:GENERA5] Tommy, wo ich hinsehe, überall Franzosen! Wie ich es hasse! [COL5_15:GENERA5] Tommy, alles in Ordnung? [COL5_16:GENERA5] Das ist für Piaf und Gainesbourg und für euer dãmliches französisches Weißbrot! [COL5_1:GENERA5] Backbord! Backbord! [COL5_2:GENERA5] Sie greifen von Steuerbord an! [COL5_3:GENERA5] Die Brücke da vorn! [COL5_4:GENERA5] Sie haben einen Helikopter! [COL5_B1:GENERA5] ~g~Beschütze den Colonel und seine Jacht um jeden Preis. [COL5_B2:GENERA5] ~g~Geh nach vorn und rãume der Jacht des Colonels den Weg frei. [COL5_B3:GENERA5] ~r~Der Colonel ist hinüber! [COL5_B4:GENERA5] ~g~Schieße den angreifenden Helikopter vom Himmel. [COL5B_3:GENERA5] Ich werde meine Privatbarkasse zu Wasser lassen. Sie gehört Ihnen, als Ausdruck meiner Dankbarkeit. [COL5_B5:GENERA5] ~g~Schieß die Helikopter ab, gefãhrde nicht die Jacht. [COL5_B6:GENERA5] ~g~Du hast keine Munition mehr. Hol dir an der Treppe des Oberdecks Nachschub. [COL5_B7:GENERA5] ~g~Du hast nur noch wenig Energie. Hol dir an der Treppe des Oberdecks Nachschub. {=================================== MISSION TABLE HAIT1 ===================================} [HAM1_A:HAIT1] Hallo? Hallo? [HAM1_B:HAIT1] Komm rein, mein Lieber, und ruh dich aus. [HAM1_C:HAIT1] Du musst der große böse Mann sein, von dem mein Großvater erzãhlt hat. [HAM1_D:HAIT1] Er erzãhlte immer von dir, wenn er zu Besuch kam, [HAM1_E:HAIT1] und von den anderen, die auf dich warten. [HAM1_F:HAIT1] Tja, wir müssen alle mal sterben, aber du... [HAM1_G:HAIT1] ....in deiner Haut möchte ich dann nicht stecken. Hehehe! [HAM1_H:HAIT1] Es hieß, ich soll hierher kommen. [HAM1_I:HAIT1] Kannst du sie hören? [HAM1_J:HAIT1] Sie rufen deinen Namen, Junge. Die müssen ziemlich scharf auf dich sein, was? [HAM1_K:HAIT1] Aber wenn du der alten Tante Poulet hilfst, hilft sie dir vielleicht auch. [HAM1_L:HAIT1] Vielleicht kann sie dir danach einen kleinen Talisman schenken. [HAM1_M:HAIT1] Ein bisschen Magie, die den Mãnnern des Gesetzes schlechte Augen macht, hmm? [HAM1_N:HAIT1] Hören Sie, das ist alles sehr, ãh... Sie geben mir was? [HAM1_O:HAIT1] Ich...ich...ich glaube, ich bin hier falsch. [HAM1_P:HAIT1] Erweise mir ein paar Gefãlligkeiten, Tommy... [HAM1_Q:HAIT1] Die Kubaner, miese, hochnãsige Narren, hmmm, [HAM1_R:HAIT1] haben meine lieben Haiti-Boys sehr geãrgert. [HAM1_S:HAIT1] Jetzt haben sie den Polizisten erzãhlt, wo ich meine Pülverchen versteckt habe. [HAM1_T:HAIT1] Sie denken, das sind Drogen, diese Dummköpfe. [HAM1_U:HAIT1] Sei ein braver Bub, Tommy, und hole Tante Poulet die Pülverchen. [HAM1_V:HAIT1] Ja, ja. Sicher, sicher. [HAM1_1:HAIT1] ~g~Die Cops nãhern sich dem Zeug. Hol es, bevor sie dort sind. [HAM1_2:HAIT1] ~r~Die Cops waren schneller bei dem Zeug! [HAM1_3:HAIT1] ~g~Bring das Zeug zum Unterschlupf! [HAM1_4:HAIT1] ~g~Gut. Jetzt das nãchste! [HAM1_6:HAIT1] ~r~Das Zeug wurde vernichtet, du Idiot! [HAM1_7:HAIT1] ~g~Die Cops haben das Zeug! Hol es dir wieder, bevor sie weg sind! [HAM1_8:HAIT1] ~g~Die Cops sind unterwegs, um das Zeug abzuholen. Beeil dich! [HAT_1A:HAIT1] ~g~Keine Bewegung, Freundchen! {=================================== MISSION TABLE HAIT2 ===================================} [HAT2_B1:HAIT2] ~g~Begib dich zu dem Wagen, in dem die fliegenden Bomben sind. [HAT2_B2:HAIT2] Schalte die Kubaner aus... [HAT2_B4:HAIT2] .... und zerstöre ihre Boote! [HAT2_B5:HAIT2] ~g~Die Kubaner hauen ab. Lass sie nicht entkommen! [HAT2_B6:HAIT2] ~r~Das ferngesteuerte Flugzeug ist außer Reichweite! [HAT2_B7:HAIT2] ~g~Einer der Kubaner flieht in einem Auto. Lass ihn nicht entkommen! [HAT2_B8:HAIT2] ~r~Du hast keine ferngesteuerten Flugzeuge mehr! [HAT2_B9:HAIT2] Ferngesteuerte Flugzeuge: [HAT2_1:HAIT2] Oh. Entschuldigung, ich muss die falsche Adresse haben... [HAT2_2:HAIT2] Du kannst gern reinkommen und dich bei einer Tasse Tee ausruhen. [HAT2_3:HAIT2] Hast du etwas für mich, Tommy? [HAT2_4:HAIT2] Ja... [HAT2_5:HAIT2] Das kommt mir so bekannt vor hier. Ein Geruch aus meiner Kindheit - das muss ein Déjà-vu sein... [HAT2_6:HAIT2] Tommy, du kannst was für mich tun, ich werd's dir erklãren. Hör gut zu, ja? [HAT2_7:HAIT2] Sie sehen aus wie jemand, den... [HAT2_8:HAIT2] Die Kubaner haben schnelle Boote, mit denen transportieren sie Drogen übers Meer. [HAT2_9:HAIT2] Davon leben sie. [HAT2_10:HAIT2] Mein Neffe hat kleine fliegende Bomben gebaut, um ihnen das Handwerk zu legen. [HAT2_11:HAIT2] Lass ihre Boote in die Luft fliegen. [HAT2_12:HAIT2] Tja, danke für den Tee. [HAT2_B3:HAIT2] { reVC update } Um eine Bombe abzuwerfen, drück die ~h~~k~~VEHICLE_FIREWEAPON~~w~-Taste. ~h~~k~~VEHICLE_ENTER_EXIT~~w~-Taste zum Beenden. {=================================== MISSION TABLE HAIT3 ===================================} [HAM3_A:HAIT3] Hallo, hallo, ich, ãh, ich suche hier jemanden... [HAM3_B:HAIT3] Du siehst hungrig aus, Tommy. [HAM3_C:HAIT3] Kenne ich Sie? [HAM3_D:HAIT3] Sei jetzt still. [HAM3_E:HAIT3] Eine Gefãlligkeit noch, dann lasse ich dich gehen, Tommy. [HAM3_F:HAIT3] Meine Jungs haben die Kubaner zum Kampf gefordert. [HAM3_G:HAIT3] Aber ohne Kanonen. [HAM3_H:HAIT3] Hmm, aber die Kubaner werden ihr blaues Wunder erleben. [HAM3_I:HAIT3] Wenn sie in den Straßen kãmpfen, nimmst du dieses Gewehr und rãumst auf. [HAM3_J:HAIT3] Keiner sieht dich, keiner hört dich. [HAM3_K:HAIT3] Wenn du das für mich tust, Tommy, dann kommst frei aus meinen Schürzenbãndern. [HAM3_1:HAIT3] ~g~Wir müssen gewinnen. Werden alle Haitianer erledigt, haben wir verloren. [HAM3_3:HAIT3] ~g~Vermutlich werden die Kubaner schummeln. Sei auf der Hut! [HAM3_4:HAIT3] ~r~Du wurdest entdeckt! Die Mission ist fehlgeschlagen! [HAM3_5:HAIT3] ~g~Du musst die Kubaner aus der Distanz erwischen. Man darf dich nicht sehen. [HAM3_8:HAIT3] ~g~Es gibt Verluste unter den Haitianern! Du musst besser zielen! [HAM3_7:HAIT3] ~g~Vorsicht! Die Kubaner haben Verstãrkung mitgebracht. Schalte sie alle aus!! [HAM3_2:HAIT3] ~r~Die Haitianer sind weg vom Fenster! [HAM3_L:HAIT3] Okay, Tantchen... {=================================== MISSION TABLE HOTEL ===================================} [INTB_A:HOTEL] Tommy! Tommy, wir haben uns lange nicht gesehen. [INTB_B:HOTEL] Hallo, Sonny. [INTB_C:HOTEL] Ich weiß, ich weiß. Dir kommen vor Rührung die Trãnen. [INTB_D:HOTEL] 15 Jahre ist es her - dabei kommt's mir vor, als wãr's gestern gewesen. [INTB_E:HOTEL] DU hast leicht reden. [INTB_F:HOTEL] Hey, für die Familie in den Knast zu gehen, ist kein Zuckerschlecken, [INTB_G:HOTEL] aber die Familie zeigt sich für sowas erkenntlich, ok? [INTB_H:HOTEL] Also, wie ist der Deal gelaufen - hast du Schnee an der Hand? [INTB_I:HOTEL] Sonny, wir sind reingelegt worden. Der Deal war eine Falle. Harry und Lee sind tot. [INTB_J:HOTEL] Das ist nicht dein Ernst, Tommy. Du hast doch hoffentlich noch das Geld. [INTB_K:HOTEL] Nein, Sonny... Ich hab das Geld nicht mehr. [INTB_L:HOTEL] Das war mein Geld, Tommy. MEIN GELD! [INTB_M:HOTEL] Versuch bloß nicht, mich reinzulegen, Tommy. Mich legt man nicht rein, das weißt du! [INTB_N:HOTEL] Warte, Sonny. [INTB_O:HOTEL] Du hast mein Wort darauf, dass ich dir dein Geld wiederbeschaffe. Und die Drogen. [INTB_P:HOTEL] Und ich liefere dir die Kerle, die dahinterstecken. [INTB_Q:HOTEL] Das weiß ich doch. Du bist kein Idiot, Tommy, aber ich warne dich - ich bin auch keiner. [INTB_R:HOTEL] Wenn du's nicht wãrst - ein anderer wãr lãngst fãllig! [INTB_S:HOTEL] Aber uns beide verbindet eine alte Freundschaft. Ich lasse dich das regeln. [INTB_T:HOTEL] Sonny, du hast mein Ehrenwort. [INTB_U:HOTEL] Du hörst von mir. {=================================== MISSION TABLE ICECRE1 ===================================} [ICC1_1:ICECRE1] ~g~Benutze deinen Eis-Wagen, um in Vice City Drogen zu verkaufen. [ICC1_2:ICECRE1] ~g~Parke den Eis-Wagen und drücke ~h~~k~~VEHICLE_HORN~~w~, um den Eiscreme-Jingle abzuspielen, damit deine Kunden wissen, dass du Ware zu verkaufen hast. [ICC1_3:ICECRE1] ~g~Für jede Transaktion bekommst du Geld. Aber je mehr Transaktionen du tãtigst, desto stãrker wird die Polizei auf dich aufmerksam. [ICC1_4:ICECRE1] ~g~In dieser Gegend sind keine Kunden. Versuche es woanders. [ICC1_5:ICECRE1] Getãtigte Deals: [ICC1_6:ICECRE1] ~g~Nimm den Mr. Whopee, um in Vice City Cherry Popper-Produkte zu vertreiben. [ICC1_7:ICECRE1] ~g~Für jede Transaktion bekommst du Geld. Aber je mehr Transaktionen du tãtigst, desto stãrker wird die Polizei auf dich aufmerksam. [ICC1_8:ICECRE1] ~g~Um eine Transaktion zu tãtigen, ~h~parke deinen Wagen ~g~und drücke die ~h~~k~~VEHICLE_HORN~~g~, um den Eiscreme-Jingle abzuspielen, damit deine Kunden wissen, dass du Ware zu verkaufen hast. [ICC1_9:ICECRE1] ~g~Andere Gangs werden es nicht gern sehen, dass du in ihrem Revier Geschãfte machst, du musst also mit Feindseligkeiten rechnen. [ICC1_10:ICECRE1] ~g~Du hast ~1~ Deals getãtigt! [ICC1_11:ICECRE1] ~g~Du hast ~1~ Deal getãtigt. [ICC1_12:ICECRE1] OBJEKT ERWORBEN! [ICC1_13:ICECRE1] ~r~Du hast keine Deals getãtigt! [ICC1_14:ICECRE1] EISCREME-MISSIONEN ERFÜLLT [ICC1_15:ICECRE1] ~g~Die Eiscremefabrik generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmãßig ab. [ICC1_16:ICECRE1] ~g~Nimm den Mr. Whoopee, um in Vice City Cherry Popper-Produkte zu vertreiben. [ICE_AT1:ICECRE1] EISCREMEFABRIK-MISSIONEN ERFÜLLT [ICE_AT2:ICECRE1] ~g~Die Cherry Popper-Fabrik generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmãßig ab. [ICC1_17:ICECRE1] Stoff-Auslieferungs-Mission beendet [ICC1_18:ICECRE1] Eiscremeverkauf insgesamt: $~1~ [ICC1_19:ICECRE1] Insgesamt getãtigte Deals: ~1~ {=================================== MISSION TABLE ICECUT ===================================} [ICC1_M:ICECUT] Sie sind schmutzige, verrotzte, verlauste, eklige, sabbernde kleine... [ICC1_I:ICECUT] Ein Baby... ein widerwãrtiges, grãßliches, ekelhaftes kleines Gör! [ICC1_J:ICECUT] Mammi liebt dich nicht. Du kleines Stück Scheiße! [ICC1_A:ICECUT] Wer sind Sie? [ICC1_B:ICECUT] Der neue Inhaber dieses Ladens. [ICC1_C:ICECUT] Sind Sie, oder waren Sie je ein Kind? [ICC1_D:ICECUT] Was soll denn das heißen? [ICC1_E:ICECUT] Waren Sie je ein Kind? [ICC1_F:ICECUT] Ja! Immer mit der Ruhe! Was ist denn mit Ihnen los? [ICC1_G:ICECUT] Ich wusste es. Ein Kind. [ICC1_H:ICECUT] Ein schmutziges, stinkendes, verrotztes, verlaustes, nölendes kleines Baby! [ICC1_K:ICECUT] Au! Beruhigen Sie sich doch! [ICC1_L:ICECUT] Ich HASSE Babies. Und ich hasse Kinder. [ICC1_N:ICECUT] Es reicht jetzt! [ICC1_P:ICECUT] Sie stellen doch Softeis her, oder? Das essen doch nur Kinder. [ICC1_Q:ICECUT] Sind Sie komplett irre? [ICC1_R:ICECUT] Erklãren Sie mir das mal - warum Kinder glücklich machen, wenn Sie sie hassen? [ICC1_S:ICECUT] Oh, du dãmliches, verrotztes, verlaustes- [ICC1_T:ICECUT] Schluss jetzt! [ICC1_U:ICECUT] - Gör! [ICC1_V:ICECUT] Das Eis ist nur Tarnung! [ICC1_W:ICECUT] Wir vertreiben auch andere Waren. Nicht-Milchprodukte. [ICC1_X:ICECUT] Und wenn ich ein Kind sehe, dann weiß ich, was ich mit ihm anfange. [ICC1_Y:ICECUT] Nicht wahr, Kinderchen? Ja, ja. Mammi hat euch gar nicht lieb. [ICC1_Z:ICECUT] Sie HASST euch! [ICC1_ZA:ICECUT] OBJEKT ERWORBEN! {=================================== MISSION TABLE INTRO ===================================} [INT1_A:INTRO] Tommy Vercetti...Hah! Shit. [INT1_B:INTRO] Hãtte nicht gedacht, dass der noch mal rauskommt. [INT1_C:INTRO] Er hat den Kopf eingezogen. War fast vergessen. [INT1_D:INTRO] Aber bald wird man sich an ihn erinnern. [INT1_E:INTRO] Wenn man ihn wieder durch ihre Viertel tigern sieht. [INT1_F:INTRO] Wird schlecht fürs Geschãft sein. [INT1_G:INTRO] Tja, was sollen wir machen, Sonny? [INT1_H:INTRO] Wir machen auf alte Kumpels und schicken ihn woanders hin. Ok? [INT1_I:INTRO] Wir wollten doch sowieso nach Süden expandieren, oder? [INT1_J:INTRO] In Vice City liegt zurzeit das Geld auf der Straße. [INT1_K:INTRO] Die Kolumbianer, die Mexikaner, [INT1_L:INTRO] ja, sogar die kubanischen Flüchtlinge machen alle glãnzende Geschãfte. [INT1_M:INTRO] Aber das geht nur mit Drogen, Sonny. [INT1_N:INTRO] Aber keine der Familien rührt dieses Zeug an! [INT1_O:INTRO] Die Zeiten ãndern sich. [INT1_P:INTRO] Die Familien können nicht wegsehen, wãhrend unsere Feinde groß abkassieren. [INT1_Q:INTRO] Also schicken wir jemanden für die Drecksarbeit da runter [INT1_R:INTRO] und schneiden uns 'ne hübsche Scheibe ab. Ok? [INT1_S:INTRO] Wer ist unser Kontaktmann da unten? [INT1_T:INTRO] Ken Rosenberg, ein Idiot von einem Anwalt. [INT1_U:INTRO] Wie soll der Vercetti im Zaum halten? [INT1_V:INTRO] Muss er gar nicht. [INT1_W:INTRO] Wir lassen ihn einfach auf Vice City los. [INT1_X:INTRO] Wir geben ihm ein bisschen Startgeld, ok? [INT1_Y:INTRO] Wir warten ein paar Monate. [INT1_Z:INTRO] Dann fahren wir hin [INT1_A1:INTRO] und schauen mal bei ihm rein, klar? [INT1_A2:INTRO] Mal sehen, wie er sich macht. [INT2_A:INTRO] Hey, hey, Jungs! Ich bin, ãh, Ken Rosenberg. Ha, ha, sehr gut, hey! [INT2_B:INTRO] Tja, ãh, ich soll euch zu dem Trefffen fahren, okay? [INT2_C:INTRO] Ich hab mit den Lieferanten geredet, und die würden, [INT2_D:INTRO] liebend gern mit uns ins Geschãft kommen. Und, ãh, [INT2_E:INTRO] wenn alles gut geht, dann dürfte da [INT2_F:INTRO] ein Haufen Kohle für uns drin sein. Und das ist doch, na ja... [INT2_G:INTRO] gut... [INT2_H:INTRO] Okay. Es sind zwei Brüder, ok? [INT2_I:INTRO] Der eine schmeißt, ãh, den Laden, [INT2_J:INTRO] der andere macht die Flüge. [INT2_K:INTRO] Die arbeiten von Mexiko aus, [INT2_M:INTRO] Sie haben eine Farm in Panama. [INT2_N:INTRO] Okay, passt auf, Jungs, [INT2_O:INTRO] wenn wir dort ankommen, sollte ich im Auto bleiben, [INT2_P:INTRO] oder soll ich mit reinkommen? [INT2_Q:INTRO] Nein. Bleib im Wagen. [INT2_R:INTRO] Wisst ihr was, ich hab's mir überlegt. [INT2_S:INTRO] Ich pass auf den Wagen auf. [INT3_A:INTRO] Ok, das sind sie, da im Helikopter. [INT3_B:INTRO] Ok, das ganze lãuft so ab: [INT3_C:INTRO] Die wollen eine saubere Übergabe auf offenem Gelãnde. [INT3_D:INTRO] Alles klar? Ok, dann wollen wir mal. [INT3_E:INTRO] Ok, ganz ruhig jetzt. [INT3_F:INTRO] Ich bin hier. Der Wagen lãuft, Baby! [INT3_G:INTRO] Hast du's? [INT3_H:INTRO] 100% astreines kolumbianisches Koks, mein Freund. [INT3_I:INTRO] Die Kohle? [INT3_J:INTRO] Zehner und Zwanziger. Gebrauchte Scheine. [INT3_L:INTRO] Los, los, weg hier! Fahr los! [INT4_A:INTRO] Gearscht. Wir sind voll gearscht! [INT4_B:INTRO] Das ist wieder typisch. [INT4_C:INTRO] Da passe ich eine einziges Mal nicht auf, [INT4_D:INTRO] und prompt kriege ich eins reingewürgt. [INT4_E:INTRO] Hier! [INT4_F:INTRO] Hör endlich auf zu jammern. Du lebst schließlich noch, oder? [INT4_G:INTRO] Lass mich hier raus. [INT4_H:INTRO] Schaff das Auto weg und leg dich schlafen. [INT4_I:INTRO] Ich komme morgen zu dir ins Büro, dann sehen wir weiter. [INT4_J:INTRO] Ok, gute Idee. Ich leg mich erstmal hin. [INT4_K:INTRO] Was hast du vor? [INT4_L:INTRO] Ich geh zurück in mein Hotel. [INT4_M:INTRO] Ich muss nachdenken, was schiefgelaufen ist. [INT4_N:INTRO] Ok. [INTRO1:INTRO] Da passe ich ein einziges Mal nicht auf, und prompt kriege ich eins reingewürgt. [INTRO2:INTRO] Leg dich schlafen. [INTRO3:INTRO] Was hast du vor? [INTRO4:INTRO] Ich komme morgen zu dir ins Büro, dann sehen wir weiter. [INT3_K:INTRO] Tja, der Deal kann steigen, mein Freund. [INT3_M:INTRO] Zeig her. [INT2_L:INTRO] Nein, nein, nein, wartet... {=================================== MISSION TABLE KENT1 ===================================} [KPM1_A:KENT1] Ok, du Knallkopf, ich werde deine Haut retten. [KPM1_B:KENT1] Was sagst du? [KPM1_C:KENT1] Du kennst doch Diaz, den Idioten, den Koks-König? [KPM1_D:KENT1] Er hat deinen Freund Lance. Es heißt, dein Kumpel wollte ihm an den Kragen. [KPM1_E:KENT1] Hat ihn aber fast den eigenen gekostet hat, falls du weißt, was ich meine. [KPM1_F:KENT1] Wo hat er ihn hingebracht? Im Klartext! [KPM1_G:KENT1] Mach dich mal locker! Sie haben ihn beim Schrottplatz erwischt. [KPM1_H:KENT1] Verdammt noch mal. Psycho. [KPM1_2:KENT1] ~r~Du solltest Lance lebend da rausholen! [KPM1_3:KENT1] Lance' Gesundheitszustand: [RESC_1:KENT1] Kannst du eine Waffe halten? [RESC_2:KENT1] Klar, glaub schon. Freut mich, dich zu sehen. [RESC_3:KENT1] Los, wir verschwinden von hier. [RESC_4:KENT1] Dank dir ist mein ganzer schöner Plan im Eimer. Das hast du sauber vermasselt, Lance. [RESC_5:KENT1] Er hat meinen Bruder auf dem Gewissen. Soll ich ihm den Rasen mãhen? [RESC_6:KENT1] Wir müssen diesen Diaz erledigen bevor er uns erledigt. [RESC_7:KENT1] Lass dich zusammenflicken, dann treffen wir uns auf der Brücke nach Star Island, ok? [RESC_8:KENT1] Ok, alles klar. [KPM1_1:KENT1] ~g~Lance wird auf dem Schrottplatz gefangengehalten. Rette ihn! [KPM1_4:KENT1] ~g~Bring Lance ins Krankenhaus! [M_PASSN:KENT1] MISSION ERFÜLLT! [KPM1_5:KENT1] ~g~Diaz' Leute sind hinter euch her. Bring Lance ins Krankenhaus. {=================================== MISSION TABLE KICKSTT ===================================} [KICK1_2:KICKSTT] ~r~Du warst nicht schnell genug bei der Maschine! [KICK1_7:KICKSTT] ~r~Du hast die Maschine geschrottet! [KICK1_8:KICKSTT] ~g~Setz dich auf das Motorrad! [KICK1_T:KICKSTT] BENÖTIGTE ZEIT: [KICKTM:KICKSTT] ~b~ZEIT: ~1~:~1~ [KICKTM2:KICKSTT] ~b~ZEIT: ~1~:0~1~ [GETBIKE:KICKSTT] ~g~Du hast ~1~ Sekunden, um zu einer Gelãndemaschine zurückzukehren, bevor die Mission endet. [KICK1_1:KICKSTT] ~g~Absolviere den Kurs so schnell wie möglich. [KICK1_6:KICKSTT] ~g~Gut gemacht! [KICK_10:KICKSTT] ~G~Nimm den Sanchez und absolviere den Kurs, indem du alle Checkpoints passierst. [KICK_12:KICKSTT] ~r~Du hast es vermasselt! [KICK_13:KICKSTT] ~r~Du hast zu lange gebraucht! [KICK_11:KICKSTT] ~g~Um die Mission zu beenden, stell dich zu Fuß in die ~q~rosa Markierung~g~. {=================================== MISSION TABLE LAWYER1 ===================================} [LAW1_A:LAWYER1] Leg dich schlafen, sagt er - [LAW1_B:LAWYER1] - Ich sitz die ganze Nacht im Dunklen hier rum und trink Kaffee! [LAW1_C:LAWYER1] Das ist eine Katastrophe. Wir sind so gearscht, Mann! [LAW1_D:LAWYER1] Diese Gorillas, die kommen hier runter und reißen mir den Kopf ab. Es ist beinahe zum lachen! [LAW1_E:LAWYER1] Dafür habe ich NICHT Jura studiert. Ok, was sollen wir jetzt machen? [LAW1_F:LAWYER1] Sei still, setz dich hin und bleib ruhig. Ich sag dir, was wir machen. [LAW1_G:LAWYER1] Du findest raus, wer das Koks geklaut hat - und ich nehm ihn mir vor. [LAW1_H:LAWYER1] Gute Idee. SEHR gute Idee. Lass mich nachdenken, lass mich nachdenken. [LAW1_I:LAWYER1] OH! Da gibt es diesen Colonel a.D., Colonel Juan Garcia Cortez. [LAW1_J:LAWYER1] Der half mir, diesen Deal einzufãdeln, [LAW1_K:LAWYER1] und zwar ohne Vice Citys Gangster-Establishment. Ok? [LAW1_L:LAWYER1] Pass auf, der gibt eine Party in der Bucht, auf seiner Luxusjacht [LAW1_M:LAWYER1] da kommt alles, was in Vice City Rang und Namen hat. [LAW1_N:LAWYER1] Ich hab natürlich eine Einladung, versteht sich, [LAW1_O:LAWYER1] aber mich kriegen keine zehn Pferde hier raus. Auf keinen Fall! [LAW1_P:LAWYER1] Halt die Klappe! Ich geh selbst hin... [LAW1_Q:LAWYER1] Moment! Hey, ich steh ja auch auf den 78er Look , aber das wird dort kein nostalgisches Saufgelage. [LAW1_R:LAWYER1] Ich meine, nichts gegen dich, aber mit den Klamotten wirst du dort ziemlich blöd angeschaut. [LAW1_S:LAWYER1] Wieso? Was ist mit meinen Sachen? [LAW1_T:LAWYER1] Pass auf. Fahr zu Rafael. Sag ihm, ich schicke dich, und er soll dich ordentlich einkleiden. [LAW1_U:LAWYER1] Ok, Los jetzt. Mach hinne... [LAWP_1:LAWYER1] Guten Abend. [LAWP_2:LAWYER1] Ich höre, Sie sind anstelle von Mr. Rosenberg hier. [LAWP_3:LAWYER1] Ich hoffe, gewisse Vorfãlle haben seiner Gesundheit nicht geschadet, [LAWP_4:LAWYER1] oder seiner Psyche, Mr...ãh? [LAWP_5:LAWYER1] Vercetti. Er leidet ein wenig an...Platzangst. [LAWP_6:LAWYER1] Ausgezeichnet, ausgezeichnet. Und Sie? [LAWP_7:LAWYER1] Ich will nur meine Ware. [LAWP_8:LAWYER1] Ah. Eine missliche Lage für alle Beteiligten. [LAWP_9:LAWYER1] Natürlich stelle ich selbst Nachforschungen an, aber [LAWP_10:LAWYER1] bei solch heiklen Sachen dauert das ein wenig. [LAWP_11:LAWYER1] Wir sprechen uns vielleicht spãter. Hm? [LAWP_12:LAWYER1] Inzwischen möchte ich Ihnen meine Tochter vorstellen, [LAWP_13:LAWYER1] Mercedes! [LAWP_14:LAWYER1] Könntest du dich um unseren Gast kümmern, wãhrend ich mich um andere Dinge [LAWP_15:LAWYER1] Natürlich, Daddy. [LAWP_16:LAWYER1] Entschuldigen Sie mich, bitte. [LAWP_17:LAWYER1] Mercedes!? [LAWP_18:LAWYER1] Leb du mal mit so 'nem Namen. [LAWP_19:LAWYER1] Na gut, ich zeig dir mal einige unserer bekannteren Gãste... [LAWP_20:LAWYER1] Das ist unser Abgeordneter Alex Shrub mit dem aufgehenden Sternchen Candy Suxx.. [LAWP_21:LAWYER1] Und kennen Sie schon meine reizende Frau Laura? Nein? [LAWP_22:LAWYER1] Nun, leider ist sie in Alabama. Das hier ist Candy. [LAWP_23:LAWYER1] Und hier haben wir den Star-Verteidiger der Vice City Mambas, BJ. [LAWP_24:LAWYER1] Immer charmant. [LAWP_25:LAWYER1] Ich hab ihn voll geblockt. Der sitzt heute im Rollstuhl! [LAWP_26:LAWYER1] Haha, das ist gut! [LAWP_27:LAWYER1] Tja, ich bin an einem super Grundstück dran. [LAWP_28:LAWYER1] Und der Schleimbold dort ist Jezz Torrent, [LAWP_29:LAWYER1] Der Sãnger von 'Love Fist'. [LAWP_30:LAWYER1] Wisst ihr, wie sie in Thailand Pingpong spielen? [LAWP_31:LAWYER1] Ich verrat's euch, [LAWP_32:LAWYER1] man spielt ohne Schlãger, wenn ihr wisst, was ich meine! [LAWP_33:LAWYER1] Impotent. [LAWP_34:LAWYER1] Und das schwatzhafte Trio. [LAWP_35:LAWYER1] Diese schlafende Schweißfabrik ist Papas Obersklave, Gonzalez. [LAWP_36:LAWYER1] und die anderen beiden sind Pastor Richards [LAWP_37:LAWYER1] und der pseudo-intellektuelle Regisseur Steve Scott. [LAWP_38:LAWYER1] ....leidenschaftlich mit den nymphomanischen Aliens [LAWP_39:LAWYER1] Da kommt der riesige Hai und [LAWP_40:LAWYER1] beißt ihnen ihr Ding ab! [LAWP_41:LAWYER1] Ha! So was hat doch die Welt noch nicht gesehen, oder? [LAWP_42:LAWYER1] Colonel! [LAWP_43:LAWYER1] Ihre Party ist wie immer fantastisch, hahahaha! [LAWP_44:LAWYER1] Ich entschuldige mich für die Verspãtung. [LAWP_45:LAWYER1] Ah, nicht doch, Amigo. Wie geht es ihnen? [LAWP_46:LAWYER1] Unsere Geschãfte laufen schwierig - die Barbaren stehen vor den Toren. [LAWP_47:LAWYER1] Eine Zeit, Freunde zu belohnen und Feinde auszuschalten, Amigo. [LAWP_48:LAWYER1] Wer ist das Großmaul? [LAWP_49:LAWYER1] Ricardo Diaz. Er ist Mr. Koks. [LAWP_50:LAWYER1] Mercedes! [LAWP_51:LAWYER1] Oh, ich will gerade meinen Freund in die Stadt bringen. [LAWP_52:LAWYER1] Ein andermal, Ricardo! [LAWP_53:LAWYER1] Lass uns verschwinden. [LAWP_54:LAWYER1] Fahr mich zum Pole Position Club. [LAW1_2:LAWYER1] ~g~Begib dich zur Jacht des Colonels. [LAW1_4:LAWYER1] ~r~Du hast die Tochter des Colonels erledigt! [LAW1_5:LAWYER1] Wirst du für meinen Vater arbeiten? [LAW1_6:LAWYER1] Vielleicht. [LAW1_7:LAWYER1] Darf ich meine Hand in deinen Schoß legen? [LAW1_8:LAWYER1] Vielleicht... [LAW1_9:LAWYER1] Es ist schwer, einen so reichen, mãchtigen Vater zu haben. Los. [LAW1_10:LAWYER1] Wir sehen uns, mein Hübscher! [LAW1_11:LAWYER1] Ganz bestimmt. [LAW1_12:LAWYER1] Hmm, netter Ofen. [LAW1_13:LAWYER1] Nein! Mein Motorrad! [LAW1_3:LAWYER1] ~g~Bring die Tochter des Colonels zum Pole Position Club. [HELP20:LAWYER1] Folge dem ~h~T-shirt-Symbol~w~ auf dem Radar, um Rafael's zu finden. [LAW1_14:LAWYER1] Wow, das ist ja wirklich ein tolles Motorrad. [LAW1_15:LAWYER1] Ja, Baby, hab ich mir gerade bei Howlin' Pete's besorgt. {=================================== MISSION TABLE LAWYER2 ===================================} [LAW2_A:LAWYER2] Ah! Tja, ich hoffe, du amüsierst dich gut, wãhrend ich hier vor Angst halb umkomme. Was hast du rausgefunden? [LAW2_B:LAWYER2] Dass es in dieser Stadt mehr Gangster gibt als im Knast. Wir brauchen einen Tipp von der Straße... [LAW2_C:LAWYER2] Ok, lass mich nachdenken, lass mich nachdenken - [LAW2_D:LAWYER2] - AH! Ich hab's! [LAW2_E:LAWYER2] Ok, es gibt da so 'n Englãnder, so 'n Idiot aus der Musikbranche. [LAW2_F:LAWYER2] Er nennt sich Kent Paul. [LAW2_G:LAWYER2] Und der ist in all den Kreisen von Vice City richtig dick drin. [LAW2_H:LAWYER2] Wenn einer weiß, wo 20 Kilo Koks abgeblieben sind, [LAW2_I:LAWYER2] dann dieser Typ. Er ist immer im Malibu. [LAW2_J:LAWYER2] Ich seh ihn mir mal an. [LAW2B_A:LAWYER2] Wo kommst du denn her? [LAW2B_B:LAWYER2] Nach einer wie dir suche ich schon seit Ewigkeiten. [LAW2B_C:LAWYER2] Kent Paul. Ja, ich bin hier die Nummer Eins. [LAW2B_D:LAWYER2] Ich suche einen Englãnder... [LAW2B_E:LAWYER2] Ich ziehe hier die Strippen, verstehst du? [LAW2B_F:LAWYER2] Ich lade dich ein. Ich kann dir alles besorgen, Süße. [LAW2B_G:LAWYER2] Mach dir keine Gedanken. [LAW2B_H:LAWYER2] Verzieh dich, Schãtzchen. [LAW2B_I:LAWYER2] Oi oi oi oi! [LAW2B_J:LAWYER2] Bist du Kent Paul? Ich bin ein Freund von Rosenberg... [LAW2B_K:LAWYER2] Rosenberg...Rosenberg... Ach, dieser abgedrehte Winkeladvokat! [LAW2B_L:LAWYER2] Der bringt noch den Unschuldigsten auf den elektrischen Stuhl! [LAW2B_M:LAWYER2] Mach uns noch 'nen Drink, mein Freund. [LAW2B_N:LAWYER2] Bist ein echter Komiker. [LAW2B_O:LAWYER2] Hör zu, ich vermisse 20 Kilo und einen Haufen Geld... [LAW2B_P:LAWYER2] Drogen? Das ist doch Schwachsinn. [LAW2B_Q:LAWYER2] Was weißt du darüber? [LAW2B_R:LAWYER2] Oi, oi! Gerade wollte ich's sagen... [LAW2B_S:LAWYER2] Es gibt da einen Koch, der verdealt Koks in der Küche eines Hotels am Ocean Drive. [LAW2B_T:LAWYER2] Macht einen ziemlich zufriedenen Eindruck in letzter Zeit. Solltest du mal auschecken. [LAW2B_U:LAWYER2] Mach ich. Und wir sehen uns. [LAW2B_V:LAWYER2] Ja, ja. Nur zu. Hau bloß ab, du Stinktier. Dir polier ich noch die Visage! [LAW2B_W:LAWYER2] Gib mir 'nen Drink. Und wo ist die Puppe? [LAW2C_A:LAWYER2] Oh, sehr gut, Rambo, schlag ihn ruhig zu Brei. Dann redet er ganz bestimmt. [LAW2C_B:LAWYER2] Willst du auch ein paar? [LAW2C_C:LAWYER2] Hey, ruhig. Ich will das gleiche wie du, Bruder. [LAW2C_D:LAWYER2] Ach ja? Und das wãre? [LAW2C_E:LAWYER2] Deine Kohle und den Stoff meines toten Bruders. Aber du hast gerade unsere Spur erledigt. [LAW2C_F:LAWYER2] Dumm gelaufen. Verzieh dich. [LAW2C_G:LAWYER2] Hey, hey! Kein Grund, hier den dicken Mann zu spielen. [LAW2C_H:LAWYER2] Sieh mal: Wir sind zwei Hombres in 'ner fremden Stadt. Wir sollten uns gegenseitig helfen. [LAW2C_I:LAWYER2] Ich helf mir selbst, Bruder. [LAW2C_J:LAWYER2] Bist du dir sicher? Hier nimm das. [LAW2C_K:LAWYER2] Komm mit! [LAW2_1:LAWYER2] Was glotzt du denn so? [LAW2_2:LAWYER2] Rede endlich... [LAW2_3:LAWYER2] Zwing mich doch dazu, du Pfeife! [LAW2_4:LAWYER2] Mir nach! [LAW2_5:LAWYER2] Ich seh zu, was ich rausfinde. Ich behalte dich im Auge, Tommy. [LAW2_6:LAWYER2] ~g~Begib dich zum Malibu Club und suche Kent Paul. [LAW2_7:LAWYER2] ~g~Suche den Küchenchef auf dem Ocean Drive. [LAW2_10:LAWYER2] ~g~Fahre zurück zum Hotel. [LAW2_11:LAWYER2] ~g~Nimm sein Handy. [LAW2_12:LAWYER2] Du hast jetzt ein Handy und kannst Telefongesprãche entgegennehmen! [LAW2_13:LAWYER2] ~g~Du hast Lance zurückgelassen! Geh ihn holen! [LAW2_14:LAWYER2] Verdammt, nichts wie weg hier! [GUN_2A:LAWYER2] Halte die ~h~~k~~PED_LOCK_TARGET~ ~w~gedrückt, um ~h~automatisch zu zielen~w~. Drücke die ~h~~k~~PED_FIREWEAPON~~w~, um zu ~h~feuern! [GUN_2C:LAWYER2] Halte die ~h~~k~~PED_LOCK_TARGET~ ~w~gedrückt, um ~h~automatisch zu zielen~w~. Drücke die ~h~~k~~PED_FIREWEAPON~~w~, um zu ~h~feuern! [GUN_2D:LAWYER2] Halte die ~h~~k~~PED_LOCK_TARGET~ ~w~gedrückt, um ~h~automatisch zu zielen~w~. Drücke die ~h~~k~~PED_FIREWEAPON~~w~, um zu ~h~feuern! [HELP17:LAWYER2] Drücke die ~h~~k~~PED_FIREWEAPON~~w~, um den Küchenchef anzugreifen. [HELP18:LAWYER2] Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um den Küchenchef anzugreifen. [LAW3_11:LAWYER2] Stell dich in die ~q~rosa Markierung~w~, um zu sehen, was im Angebot ist. [LAW3_12:LAWYER2] Du kannst Waffen auswãhlen, indem du die ~h~linke~w~ oder ~h~rechte~w~ ~h~Richtungstaste drückst. [LAW3_13:LAWYER2] Wenn du genug Geld hast, kannst du Waffen durch Drücken der ~h~~k~~PED_SPRINT~~w~ kaufen. [LAW3_14:LAWYER2] Um zu gehen, drücke die ~h~~k~~VEHICLE_ENTER_EXIT~ [LAW3_15:LAWYER2] Folge dem ~h~Pistolensymbol~w~ auf dem Radar, so kommst du zu AmmuNation. [LAW2_15:LAWYER2] ~g~Begib dich zu AmmuNation. [LAW2_K:LAWYER2] Nur die Ruhe. [LAW2_16:LAWYER2] Eins musst du wissen: In dieser Stadt darfst du nie unbewaffnet sein. [LAW2_17:LAWYER2] Komm, der nãchste Waffenladen ist ein paar Blocks von hier. [LAW2_18:LAWYER2] Tommy, jeder Mann braucht ab und zu mal ein bisschen Entspannung. [LAW2_19:LAWYER2] Das ist die Pole Position Stripper-Bar. Solltest du bei Gelegenheit mal reinschauen. {=================================== MISSION TABLE LAWYER3 ===================================} [_A:LAWYER3] Arrgh! Ach, du lieber Gott! Du! Meine Güte, ich brauch 'ne neue Hose! [LAW3_B:LAWYER3] Hey, diese Psychos aus dem Norden haben angerufen. Sie kommen bald hier runter. [LAW3_C:LAWYER3] Also, wo ist das verdammte Geld? [LAW3_D:LAWYER3] Ganz ruhig. Soweit sind wir noch nicht. [LAW3_E:LAWYER3] Ich hab wirklich gedacht, du erledigst das. [LAW3_F:LAWYER3] Und jetzt sagen diese Gauner, wir sollen ihnen einen Gefallen tun. [LAW3_G:LAWYER3] Du meinst, ICH soll ihnen einen Gefallen tun. [LAW3_H:LAWYER3] Du sagst es. Sehe ich aus, als könnte ich Geschworene einschüchtern? [LAW3_I:LAWYER3] Ich kann nicht mal ein Kind einschüchtern. Ich hab's versucht. [LAW3_J:LAWYER3] Hör zu, wenn du kneifst, kriegt Forellis Cousin Georgio 5 Jahre wegen Betrugs. [LAW3_K:LAWYER3] Du musst diese Typen ausschalten. [LAW3_L:LAWYER3] Verstehe. Den Geschworenen helfen 'umzudenken'. Kein Problem. [LAW3_M:LAWYER3] Nein, nein, nein! Das hab ich schon versucht. Das ist nicht gelaufen, [LAW3_N:LAWYER3] ZWING sie dazu, umzudenken. [LAW3_1:LAWYER3] Georgio lãsst grüßen. [LAW3_2:LAWYER3] Denk dran, 'Schuldig' ist ein hãssliches Wort. [LAW3_3:LAWYER3] 'Unschuldig', bis ich was anderes sage. [LAW3_4:LAWYER3] Er ist nicht schuldig. [LAW3_5:LAWYER3] Du kennst Georgio? Merk dir: Er ist nicht schuldig. [LAW3_6:LAWYER3] Nicht schuldig. Verstanden...gut. [LAW3_8:LAWYER3] ~r~Du hast einen Geschworenen erledigt! [LAW3_9:LAWYER3] ~g~Schrotte das Auto des Geschworenen, damit er aussteigt! [HELP40:LAWYER3] Du kannst Autos mit dem Hammer oder einer ãhnlichen Waffe zertrümmern. [HELP41:LAWYER3] Oder du kannst sie mit einem Fahrzeug rammen. [LAW3_10:LAWYER3] ~g~Eine Nahkampfwaffe kannst du im ~h~Eisenwarenladen~g~ kaufen. [LAW3_20:LAWYER3] ~g~Schrotte das Auto des Geschworenen! [LAW3_21:LAWYER3] Das kann doch wohl nicht wahr sein! [LAW3_22:LAWYER3] Unglaublich! [LAW3_23:LAWYER3] Ok! Ok! Ich hab kapiert! [LAW3_24:LAWYER3] ~g~Dieser Hammer wãre nützlich. [LAW3_7:LAWYER3] ~g~Schüchtere die 2 Geschworenen ein, aber erledige sie NICHT! [HELP23:LAWYER3] Folge dem ~h~Hammer-Symbol~w~ auf dem Radar, wenn du beim Eisenwarenladen Nahkampfwaffen kaufen willst. [LAW3_16:LAWYER3] Dãmlicher Florida-Idiot! [LAW3_17:LAWYER3] Aus dem Weg! {=================================== MISSION TABLE LAWYER4 ===================================} [LAW4_A:LAWYER4] Avery, es versteht sich von selbst... Tommy! Tommy! Fortschritte gemacht? Nein, erzãhl's mir spãter. [LAW4_B:LAWYER4] Tommy, das ist Avery Carrington. Kennt ihr euch nicht von der Party? [LAW4_C:LAWYER4] Nicht persönlich. [LAW4_D:LAWYER4] Tagchen. [LAW4_E:LAWYER4] Avery hat einen Vorschlag für uns. [LAW4_F:LAWYER4] Haben wir nicht was besseres zu tun? [LAW4_G:LAWYER4] Ich versuch hier, unseren Hals zu retten. Also würdest du mich bitte ausreden lassen? [LAW4_H:LAWYER4] Ich hab Angst. Aber wenn ich schon Ende der Woche sterbe, möchte ich wenigstens nicht arm sterben. [LAW4_I:LAWYER4] Jetzt beruhigt euch, ihr beiden. [LAW4_J:LAWYER4] Junge, wenn du mir hilfst, sorge ich dafür, dass jeder, der dir Ãrger macht, unter die Erde kommt. [LAW4_K:LAWYER4] Ok, was kann ich für Sie tun? [LAW4_L:LAWYER4] Eine Spedition hat ihr Lager auf einem Top-Grundstück - und will nicht verkaufen. [LAW4_M:LAWYER4] Die sitzen da drauf wie die Ratten in ihren Löchern. Also müssen wir dieses Ungeziefer ausrãuchern. [LAW4_N:LAWYER4] Fahr hin und stich ein wenig ins Wespennest. [LAW4_O:LAWYER4] Das wird die Security beschãftigen. Dann schleichst du dich rein und machst den Laden platt. [LAW4_P:LAWYER4] Und du könntest dich bei Rafael's neu einkleiden. Kann 'ne Weile dauern, aber mach das ruhig mal. [LAW4_Q:LAWYER4] Das gibt ein Fest. [LAW4_R:LAWYER4] Wenn alles lãuft wie geplant, komm mal zu mir ins Büro... [LAW4_1:LAWYER4] Bitte, geht auseinander! Die Geschãftsleitung wird sich aller Probleme annehmen! [LAW4_2:LAWYER4] Bitte, geht auseinander! Geht wieder nach Hause! [LAW4_3:LAWYER4] Bitte, geht auseinander! Das ist nicht akzeptabel! [LAW4_4:LAWYER4] Bitte, geht auseinander! Ihr landet alle auf der Straße! [LAW4_5:LAWYER4] Die Knüppel raus, Jungs! Diesen Kommis zeigten wir's! [LAW4_13:LAWYER4] Fange mit mind. 4 Arbeitern Streit an, um einen Aufruhr zu starten. [LAW4_14:LAWYER4] ~g~Zerstöre die Transporter auf dem Gelãnde! [HELP38:LAWYER4] Wenn du jemanden ausschaltest, der eine Waffe trãgt, lãsst er sie fallen. [HELP39:LAWYER4] Du kannst explosive Fãsser anvisieren und abschießen, aber bleib auf Distanz. {=================================== MISSION TABLE MIAMI_1 ===================================} [T4X4_1A:MIAMI_1] ~g~Du hast ~1~ Sekunden, um ~y~24~g~ Checkpoints abzufahren. ~g~Die ~y~REIHENFOLGE IST BELIEBIG. [T4X4_1B:MIAMI_1] ~y~PASSIERE~g~ den ersten Checkpoint, dann lãuft die ~r~STOPPUHR. [T4X4_1C:MIAMI_1] ~1~ von 24! [GETBIK1:MIAMI_1] Du hast ~1~ Sekunden, um auf eine PCJ 600 zu steigen! [GETBIK3:MIAMI_1] ~r~Du brauchst eine PCJ 600, um diese Mission durchzuführen! {=================================== MISSION TABLE MM ===================================} [BLOD_04:MM] ZUSTAND AUTO: [BLOD_05:MM] ~g~ZIELZEIT: ~1~ Minute [BLOD_06:MM] ~g~ZIELZEIT: ~1~ Minuten [BLOD_07:MM] NEUE Bestzeit: ~1~ Sekunden [BLOD_08:MM] Zerstörte Autos: ~1~ [BLOD_09:MM] $~1~ [BLOD_10:MM] SIEGER!! [BLOD_01:MM] Fahr durch die Checkpoints, um deine Gesamtzeit zu verlãngern. [BLOD_02:MM] Wenn die Gesamtzeit abgelaufen ist, hast du versagt. [BLOD_03:MM] Um zu gewinnen, muss deine Gesamtzeit die Zielzeit überschreiten! {=================================== MISSION TABLE OVALRIG ===================================} [HOTR_01:OVALRIG] ~g~Das Rennen geht über 12 Runden. Nur die ersten drei Plãtze qualifizeren für einen Gewinn. [HOTR_02:OVALRIG] ~g~Wird dein Auto zerstört, wirst du disqualifiziert. [HOTR_03:OVALRIG] ~g~Wird dein Auto beschãdigt, kannst du es an der Box reparieren lassen. [HOTR_04:OVALRIG] ~g~Da geht es aus dem Stadion raus. [HOTR_05:OVALRIG] Zustand Auto: [HOTR_06:OVALRIG] Runden: [HOTR_07:OVALRIG] Neue Bestzeit: ~1~:0~1~ [HOTR_08:OVALRIG] Zeit: ~1~:~1~ [HOTR_10:OVALRIG] Absolvierte Zeit: [HOTR_09:OVALRIG] Position: [HOTR_12:OVALRIG] ~r~Dein Fahrzeug ist zerstört worden! [HOTR_13:OVALRIG] ~r~Du hast das Rennen nicht gewonnen! [HOTR_14:OVALRIG] ~r~Du bist disqualifiziert worden! [HOTR_15:OVALRIG] Zeit: ~1~:~1~ [HOTR_16:OVALRIG] Zeit: ~1~:0~1~ [HOTR_17:OVALRIG] Bestzeit: ~1~:~1~ [HOTR_18:OVALRIG] Bestzeit: ~1~:0~1~ [HOTR_19:OVALRIG] Bestzeit: Nicht verfügbar [HOTR_20:OVALRIG] Neue Bestzeit: ~1~:~1~ [HOTR_21:OVALRIG] Neue Bestzeit: ~1~:0~1~ [HOTR_22:OVALRIG] Beste Platzierung: Nicht verfügbar [HOTR_23:OVALRIG] Beste Platzierung: 1. [HOTR_24:OVALRIG] Beste Platzierung: 2. [HOTR_25:OVALRIG] Beste Platzierung: 3. [HOTR_26:OVALRIG] Beste Platzierung: ~1~. [HOTR_27:OVALRIG] Beste Rundenzeit: ~1~.~1~ Sekunden [HOTR_28:OVALRIG] Beste Rundenzeit: ~1~.0~1~ Sekunden [HOTR_29:OVALRIG] $~1~ [HOTR_30:OVALRIG] 1. PLATZ [HOTR_31:OVALRIG] 2. PLATZ [HOTR_32:OVALRIG] 3. PLATZ [HOTR_33:OVALRIG] Beste Rundenzeit: Nicht verfügbar [HOTR_11:OVALRIG] Neue beste Rundenzeit: ~1~.~1~ Sekunden [HOTR_34:OVALRIG] Neue beste Rundenzeit: ~1~.0~1~ Sekunden {=================================== MISSION TABLE PHIL1 ===================================} [PHI1_HP:PHIL1] Wenn du Granaten mit Fernzünder benutzt, wirf die Granate, dann löse die Explosion zu einem beliebigen Zeitpunkt aus. [PHIL1_A:PHIL1] Phil? [PHIL1_B:PHIL1] SCHNELL WEG! [PHIL1_C:PHIL1] Schnell weg! [PHIL1_E:PHIL1] Scheiße, Phil, das Zeug trinkst du? [PHIL1_F:PHIL1] Hey, du musst es nicht trinken. [PHIL1_G:PHIL1] Das haut schon rein, wenn du nur dran riechst. [PHIL1_H:PHIL1] Hör mal, Phil, du sagtest, du könntest mir Artillerie besorgen... [PHIL1_I:PHIL1] Klar. [PHIL1_J:PHIL1] In letzter Zeit macht mir ein mexikanischer Waffenschieber Konkurrenz. [PHIL1_K:PHIL1] Der müsste jetzt gerade auf seiner wöchentlichen Runde sein. [PHIL1_L:PHIL1] Ramm mit deiner Karre die Ware von seinem Wagen runter, bevor er wieder abtaucht. [PHIL1_M:PHIL1] Tust mir einen großen Gefallen damit. [PHIL1_N:PHIL1] Und dann mach ihn fertig. [PHI1_01:PHIL1] ~g~Ramme die Waffen von der Ladeflãche des Waffenschiebers. [PHI1_02:PHIL1] ~g~Der Waffenhãndler hat die Ladung verloren. Schlag die Kiste kaputt und nimm die Waffe. [PHI1_03:PHIL1] ~g~Sie haben anscheinend Verstãrkung gerufen. [PHI1_04:PHIL1] ~g~Jetzt erledige die restlichen Waffenschieber. [PHI1_06:PHIL1] Pass doch auf, wo du hinfãhrst! [PHI1_07:PHIL1] Hey! [PHIL1_O:PHIL1] Huuuuhuuu! [PHIL1_D:PHIL1] Komm nie mit 'ner offenen Flamme in die Nãhe von Phil Cassidys TNT-Whiskey! {=================================== MISSION TABLE PHIL2 ===================================} [PHIL2_A:PHIL2] Hey, Phil, wie geht's? [PHIL2_B:PHIL2] Hey, Tommy. Alles klar? Lange nicht gesehen... [PHIL2_C:PHIL2] Du solltest wirklich die Finger von dem TNT-Whiskey lassen. [PHIL2_D:PHIL2] Das Zeug riecht ja wie Terpentin. Mir brennen schon die Augen. [PHIL2_E:PHIL2] Lass stecken, Tommy. [PHIL2_F:PHIL2] Komm hier rüber, ich will dir nãmlich was zeigen. [PHIL2_G:PHIL2] Wahnsinn! Das rieche ich ja schon von hier. Mir ist schon ganz schwindlig. [PHIL2_H:PHIL2] Kümmer dich nicht um den Geruch. Tommy, sieh dir das an. [PHIL2_I:PHIL2] Billige Schrottbatterien. Da auf der Bank sind noch welche. [PHIL2_J:PHIL2] Tata! [PHIL2_K:PHIL2] Oh, verflucht! [PHI2_01:PHIL2] ~g~Schnell, bring Phil ins Krankenhaus. [PHI2_03:PHIL2] ~r~Phil Cassidy ist tot!!! Wer soll Liberty nun mit Waffen versorgen? [PHI2_05:PHIL2] Nicht ins Krankenhaus, Mann! Zu viele Cops und Vietcong! [PHI2_06:PHIL2] Ich kenn 'nen Ex-Army-Arzt, der schuldet mir einen Gefallen und 'nen Rasenmãher. [PHI2_07:PHIL2] Er hat 'ne Praxis unten in Little Havana. Uh, guck mal, ein Riesenfisch. [PHI2_08:PHIL2] Achtung! Da in den Bãumen - Vietcong! [PHI2_09:PHIL2] Spinn ich, oder ist die Straße aus Gummi? [PHI2_10:PHIL2] Broken Spoon an Mother Hen, bitte kommen! [PHI2_11:PHIL2] Spooney Wooney Woo Woo Woooo! [PHI2_12:PHIL2] Er kommt mich holen, Jungchen! [PHI2_13:PHIL2] Schwarze Schwingen breiten sich über mir aus... [PHI2_14:PHIL2] Es ist wunderschön, Mann. Wunderschön... mir ist nur so kalt... [PHI2_15:PHIL2] Roger! Wir haben einen betrunkenen Fahrer. [PHI2_04:PHIL2] Phils Gesundheitszustand: [PHI_AS1:PHIL2] PHIL-MISSIONEN ERFÜLLT [PHI_AS2:PHIL2] ~g~Bei Phil gibt es neue Waffen zu kaufen. {=================================== MISSION TABLE PIZZA ===================================} [PIZ1_01:PIZZA] ~g~Liefere diese Pizzas aus. Du musst den Kunden die Pizzas zuwerfen, wãhrend du an ihnen vorbeifãhrst. [PIZ1_02:PIZZA] ~g~Du hast alle Pizzas zugestellt. Fahr zurück und hole noch mehr. [PIZ1_05:PIZZA] ~g~Du hast 5 Minuten, um die Pizzas zu liefern, sonst rufen die Kunden einen anderen Pizza-Service an. [PIZ1_07:PIZZA] ~r~Du hast den Kunden erledigt! Du bist gefeuert. [PIZ1_08:PIZZA] ~r~Die Zeit ist um. Du bist gefeuert. [PIZ1_09:PIZZA] ~r~Du hast dein Motorrad geschrottet! Du bist gefeuert. [PIZ1_11:PIZZA] Hey! Steig wieder aufs Motorrad! [PIZ1_12:PIZZA] Verbleibende Pizzas: [PIZ1_06:PIZZA] Drücke die ~h~ R3-Taste~w~, wenn du auf einem Bike sitzt und die Mission abbrechen willst. [PIZ1_13:PIZZA] Liefere sie schön heiß ab. [PIZ1_14:PIZZA] Kumpel, Pizzas für dich. [PIZ1_15:PIZZA] Hey, na los, Mister, liefere sie schnell aus. [PIZ1_16:PIZZA] Worauf wartest du, Mister? Du sollst Pizzas liefern. [PIZ1_17:PIZZA] Ich weiß, du wolltest kein Pizza-Lieferant sein. Na ja, mir egal. [PIZ1_18:PIZZA] Liefere die aus. [PIZ1_19:PIZZA] Die müssen ausgeliefert werden. [PIZ1_20:PIZZA] Na los, Mister, liefere die Dinger aus, oder du fliegst. [PIZ1_21:PIZZA] Die Leute warten, Kumpel. [PIZ1_22:PIZZA] Wartest du auf bessere Zeiten? Die müssen ausgeliefert werden! [PIZ1_23:PIZZA] Liefer den verdammten Fraß aus, Mister. [PIZ1_24:PIZZA] Die müssen ausgeliefert werden, Kumpel. [PIZ1_25:PIZZA] Mann, kannst du die übernehmen? [PIZ1_26:PIZZA] Mister, liefere die Dinger schnell ab, hopp, Amigo. [PIZ1_27:PIZZA] Komm schon, wir sind unter Druck, liefere die Dinger aus. [PIZ1_28:PIZZA] Du schon wieder? Liefere die hier schnell aus, Kumpel. [PIZ1_29:PIZZA] Keine Trödelei diesmal, Kumpel. [PIZ1_30:PIZZA] Na los, du fauler Hund, liefere den Fraß rechtzeitig aus. [PIZ1_31:PIZZA] Du wirst nie befördert, wenn du diesmal nicht schneller machst. [PIZ1_32:PIZZA] ~r~Ist dir die Pizza zu heiß? [PIZ1_33:PIZZA] ~g~Kehre zum Restaurant zurück, um weitere Auftrãge zu bekommen. [PIZ1_34:PIZZA] ~g~Pizza geliefert, hier ist dein Geld. [PIZ_WON:PIZZA] Pizza-Mission abgeschlossen. Deine max. Gesundheit erhöht sich auf 150 {=================================== MISSION TABLE PORN1 ===================================} [POR1_15:PORN1] Hey, Tommy, kommst du auf eine Aufwãrmrunde mit rein!? [POR1_14:PORN1] Du bist engagiert! [POR1_A:PORN1] Action! [POR1_B:PORN1] Wow! Der ist aber groß! [POR1_C:PORN1] 30cm, das ist Vorschrift, Baby. [POR1_D:PORN1] SCHNITT! Wer ist dieser Idiot? Du da! Was machst du in meinem Studio? Was willst du? [POR1_E:PORN1] Was soll das alles hier? [POR1_F:PORN1] Aliens? Angelruten? [POR1_G:PORN1] Wer hat jemals einen so großen Hai gesehen? [POR1_H:PORN1] Das muss alles raus hier. [POR1_I:PORN1] Wieso bist du in diese Branche gegangen, du Idiot? [POR1_J:PORN1] Hah? [POR1_K:PORN1] Wegen der Pussys, deswegen. Was ist das?? [POR1_L:PORN1] Das ist meine Kunst- SICHERHEITSDIENST! [POR1_M:PORN1] Hör zu, du aufgeblasener Penner, du gehörst jetzt mir. Mir gehört das alles hier. [POR1_N:PORN1] Wir krempeln den Laden hier um. [POR1_O:PORN1] Ich mache dich reich. [POR1_P:PORN1] Ãh, du...du bist Tommy Vercetti? Aber ich dachte, du wãrst... [POR1_Q:PORN1] Ganz recht. [POR1_R:PORN1] Wir ãndern hier ein paar Dinge und dann machen wir richtig Kohle. [POR1_S:PORN1] Hast du dir schon mal überlegt... [POR1_T:PORN1] Aber zuerst brauchen wir mal ein paar hübsche Mãdels hier. [POR1_U:PORN1] Ja, Girls sind ok, aber du...wow! [POR1_02:PORN1] ~g~Schalte Candys Agent aus, dann komm wieder und hole Candy. [POR1_04:PORN1] Hey, Candy. Ich suche Filmtalente. Interessiert? [POR1_05:PORN1] Klar! Aber da musst du mit meinem Agenten reden. [POR1_06:PORN1] Was zum Teufel soll das? [POR1_07:PORN1] Du hãttest heute zu Hause bleiben sollen! [POR1_7B:PORN1] Was sagt man zu so 'nem Arschloch? [POR1_08:PORN1] Hey, Mercedes! [POR1_09:PORN1] Hi, Tommy. Na, ein bisschen feiern? [POR1_10:PORN1] Jetzt nicht, Süße. Bist du an Filmaufnahmen interessiert? [POR1_11:PORN1] Klar. Wenn's schön billig und dreckig ist. [POR1_13:PORN1] ~g~Bring die Girls ins Studio zu Steve. [POR1_17:PORN1] Wow, cooler Hai! [POR1_18:PORN1] ~r~Mercedes ist erledigt! [POR1_20:PORN1] Tommy, wo willst du hin? Komm zurück! [POR1_21:PORN1] Wo willst du hin? [POR1_22:PORN1] Tommy, wann sehen wir uns mal ganz allein, nur du und ich? [POR1_01:PORN1] ~g~Candy Suxxx wãre perfekt für die Hauptrolle! [POR1_12:PORN1] ~g~Nimm Candy mit zu deinem Treffen mit Mercedes. [POR1_16:PORN1] Vielleicht spãter, Schãtzchen... [POR1_24:PORN1] ~g~Geh zurück und hole Candy. [POR1_25:PORN1] ~g~Du hast Candy vergessen. Geh sie holen. [POR1_23:PORN1] ~g~Candy wird sich um das Geschãft in ~h~Downtown~g~ kümmern. [POR1_26:PORN1] ~g~Da ist Candy. Sie scheint wieder mit dem Kongressabgeordneten Shrub zusammen gewesen zu sein. [POR1_27:PORN1] Los, gehen wir. [POR1_28:PORN1] Tommy, sei vorsichtig! Meine Implantate sind noch nicht versichert! [POR1_29:PORN1] Das nennst du fahren? [POR1_30:PORN1] Danach kann ich keinen Porno mehr machen! [POR1_31:PORN1] Was ist los? Willst du mich umbringen? Ich dachte, ich wãre der Star! {=================================== MISSION TABLE PORN2 ===================================} [POR2_A:PORN2] Was macht die Filmerei, Steve? [POR2_B:PORN2] Tja, Candy ist ein Naturtalent, und die Neue ist unersãttlich! [POR2_C:PORN2] Die hatte schon vor der ersten Probe das halbe Team durch. [POR2_D:PORN2] Jedenfalls, morgen haben wir einen Außendreh. Wir schießen ein paar Boot-Szenen. [POR2_E:PORN2] Boot-Szenen? Was für Boot-Szenen? [POR2_F:PORN2] Die Fischer zappeln im Netz der Leidenschaft, als ein riesiger Hai daherkommt - [POR2_G:PORN2] Was habe ich über den Riesenhai gesagt? [POR2_H:PORN2] Ich sagte, 'KEIN RIESENHAI', ok? [POR2_I:PORN2] Halt die Kameras auf die Mãdels gerichtet! [POR2_J:PORN2] Ok, ok. Hey, Tommy, probieren kann ich's ja mal, oder? [POR2_K:PORN2] Habt ihr die Flyer drucken lassen? [POR2_L:PORN2] Ja, aber man wird uns die Dinger nicht verteilen lassen. Ich meine, [POR2_M:PORN2] die sind einfach zu, ãh, zu deutlich. [POR2_N:PORN2] Mach dir darüber keine Gedanken. [POR2_O:PORN2] Ich hab da so meine Ideen, wie wir die verteilen. [POR2_P:PORN2] Ok. Hey, Candy, ãh, in meinen Wohnwagen. [POR2_01:PORN2] ~g~Hinter den Studios steht ein altes Wasserflugzeug, das mal als Requisite für einen Indy-Film diente. [POR2_02:PORN2] ~g~Suche dir einen Checkpoint aus, um mit dem Abwurf der Flyer zu beginnen. [POR2_03:PORN2] ~g~Wirf die Flyer überall bis zum End-Checkpoint ab. [POR2_04:PORN2] ~r~TREIBSTOFFMANGEL!!! [POR2_05:PORN2] Benutze es, um die Flyer in der Stadt zu verteilen. [DILDO:PORN2] Skimmer-Tankinhalt: [POR2_Q:PORN2] Oh, Mann. [PORN2_9:PORN2] ~g~Du hast ~1~ Sekunden, um zu einer Skimmer zurückzukehren, bevor die Mission endet. {=================================== MISSION TABLE PORN3 ===================================} [POR3_A:PORN3] Ok, was ist jetzt wieder? [POR3_B:PORN3] Schsch! [POR3_C:PORN3] Nun, nach seiner Begegnung mit den Nympho-Aliens [POR3_D:PORN3] kann unser Held an nichts anderes denken, als an einen riesigen Phallusberg- [POR3_E:PORN3] und da wollen wir die Szene mit dem Bottich voll Kartoffelpüree drehen, aber dann... [POR3_F:PORN3] Das interessiert mich nicht die Bohne. [POR3_G:PORN3] D-Dreh einfach weiter. Weiter, weiter. [POR3_H:PORN3] Hey, Tommy... [POR3_I:PORN3] Du hast am Telefon was von rechtlichen Problemen gesagt? [POR3_J:PORN3] Ach ja! Der Kongressabgeordnete Alex Shrub ist auf Wahlkampftour und buhlt um Stimmen bei den Puritanern. [POR3_K:PORN3] Es heißt, er unterstützt ein Verbot der, sagen wir mal, eher [POR3_L:PORN3] fleischlichen Bereiche der großartigen Unterhaltungsindustrie unseres Landes. [POR3_M:PORN3] Großartig. [POR3_N:PORN3] Candy! Du kennst doch Shrub. [POR3_O:PORN3] Macht ihr auch ausgefallene Sachen? [POR3_P:PORN3] Oh ja, oh ja, oh ja! Ja, ja, ja, JA, Oooooooh! [POR3_Q:PORN3] Bitte sag, dass du das hast. [POR3_R:PORN3] Gehörte das zum, ãh... oder war das die Antwort für... [POR3_S:PORN3] Hey, ich kann das nie unterscheiden. Jedenfalls... [POR3_T:PORN3] Das beste wird sein, du folgst ihr nach dem Dreh. [POR3_U:PORN3] Mal sehen, ob sie dich zu ihrem neuen Liebesnest führt. [POR3_V:PORN3] Hast du eine Kamera? [POR3_X:PORN3] Ja. Gebt ihm eine Kamera. [POR3_02:PORN3] ~r~Du hast den Abgeordneten erledigt! Jetzt kannst du ihn nicht mehr erpressen. [POR3_03:PORN3] ~r~Du hast die Bodyguards des Abgeordneten aufgescheucht. Sie werden ihn sofort wegbringen. [POR3_04:PORN3] Candy, könntest du mich Martha nennen? [POR3_05:PORN3] Oh, Alex - ich meine Martha - ich tu alles, was du willst. [POR3_06:PORN3] Martha, jemand sieht uns zu. Wie erregend. [POR3_07:PORN3] Sie da! Geben Sie mir die Kamera. [POR3_01:PORN3] ~g~Folge Candys ~h~Stretch-Limo~g~. [POR3_15:PORN3] ~r~Du hast Candys Stretch-Limo geschrottet! [POR3_17:PORN3] ~g~Begib dich mit dem Film zurück ins Pornostudio. [POR3_19:PORN3] ~r~Der Film ist alle! [POR3_21:PORN3] ~g~Du hast Candys Stretch-Limo verloren! [POR3_22:PORN3] ~g~Das WK Chariot Hotel gegenüber seines Balkons dürfte eine ideale Position zum Fotografieren bieten. [POR3_23:PORN3] ~g~Es gibt eine Seitentür, durch die du in das Hotel kommst. [POR3_08:PORN3] Halte die~h~ ~k~~PED_LOCK_TARGET~ ~w~gedrückt, um mit der Kamera zu ~h~zielen~w~. [POR3_09:PORN3] Halte die~h~ ~k~~PED_LOCK_TARGET~ ~w~gedrückt, um mit der Kamera zu ~h~zielen~w~. [POR3_10:PORN3] Drücke die~h~ ~k~~PED_SNIPER_ZOOM_IN~~w~, um ~h~heranzuzoomen ~w~und die~h~ ~k~~PED_SNIPER_ZOOM_OUT~~w~, um wieder ~h~wegzuzoomen~w~. [POR3_11:PORN3] Drücke die~h~ ~k~~PED_SNIPER_ZOOM_IN~~w~, um ~h~heranzuzoomen ~w~und die~h~ ~k~~PED_SNIPER_ZOOM_OUT~~w~, um wieder ~h~wegzuzoomen~w~. [POR3_12:PORN3] Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um ein Bild zu machen. [POR3_13:PORN3] Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um ein Bild zu machen. [POR3_14:PORN3] Drücke die~h~ ~k~~PED_FIREWEAPON~~w~, um ein Bild zu machen. [POR3_20:PORN3] ~g~Wenn du ein Transportmittel brauchst, nimm den ~h~Sparrow~g~ hinter dem Haus. [POR3_16:PORN3] ~g~Du brauchst drei gute kompromittierende Fotos von Alex Shrub mit Candy. [POR3_24:PORN3] GESCHOSSENE FOTOS: {=================================== MISSION TABLE PORN4 ===================================} [POR4_A:PORN4] Tut mir leid, ich kann das jetzt nicht schlucken. [POR4_B:PORN4] Ach KOMM, Darling! [POR4_C:PORN4] Der ist ausgestattet wie ein Pottwal, Herrgott noch mal, [POR4_D:PORN4] wie kannst du dich da nicht einfühlen?! [POR4_E:PORN4] Aber Stevie... [POR4_F:PORN4] Wie geht's meinem Starregisseur? [POR4_G:PORN4] Oh Mann. Der Kampf zwischen künstlerischer Ambition und [POR4_H:PORN4] diesem Rumgenudle tobt unvermindert. [POR4_I:PORN4] Und bevor du fragst: Ja, alle vier Videos werden veröffentlicht, wenn... [POR4_J:PORN4] Schãtzchen, kannst du BITTE die Anaconda im Bild halten, [POR4_K:PORN4] die kostet pro Stunde mehr als du! [POR4_L:PORN4] Oh, sorry, Steve. [POR4_M:PORN4] Ich hab mir gedacht, als Werbung für den Start brauchen wir einen richtigen Knaller. [POR4_N:PORN4] Irgendwas, was so richtig Furore macht in der Stadt. Hast du eine Idee? [POR4_O:PORN4] Na ja, früher gab's da immer Galas, [POR4_P:PORN4] Stars, Limos, riesige Suchscheinwerfer am Nachthimmel... [POR4_Q:PORN4] Suchscheinwerfer? Ich hab eine Idee... [POR4_R:PORN4] ....ja, ja, ja. Die heißen Mãdels mit ihren Paillettenkleidern und die Limos, oh, Premieren. [POR4_S:PORN4] O ja, Ma'am, natürlich, Ma'am, [POR4_T:PORN4] Und die Presse und die Lichter-Flut... [POR4_01:PORN4] ~g~Begib dich nach ~y~Downtown~g~und richte den Scheinwerfer auf dem Gebãude aus. [POR4_02:PORN4] ~g~Du brauchst ein schnelles Bike, um von Dach zu Dach zu springen. Der Wachmann fãhrt immer mit einer ~y~PCJ 600~g~zur Arbeit... [POR4_03:PORN4] ~g~Du wirst auf die Gebãudedãcher müssen. In eines der oberen Büros sollte ein Lift führen... [POR4_06:PORN4] ~g~Kehre in das tiefer gelegene Büro zurück, wenn du noch mal auf die Dãcher musst. [POR4_07:PORN4] ~g~Du brauchst ein Motorrad, um von Gebãude zu Gebãude zu springen. [POR4_08:PORN4] ~g~Brich durch das Fenster, um zu starten. Du hast bis 07:00 Zeit. Dann wird es zu hell, um ungesehen hinaufzukommen. [POR4_09:PORN4] ~g~Die Pfeile zeigen dir, zu welchem Gebãude du als nãchstes springen musst. [POR4_10:PORN4] ~r~Es ist zu hell, um ungesehen dort hinaufzukommen. [POR4_11:PORN4] Kehre zur Leiter zurück, wenn du noch mal auf die Dãcher musst. [POR4_05:PORN4] ~g~Diese Treppe führt zu einem tiefer gelegenen Büro. [POR_AS1:PORN4] FILMSTUDIO-MISSIONEN ERFÜLLT [POR_AS2:PORN4] ~g~Inter Global Films generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmãßig ab. {=================================== MISSION TABLE PROT1 ===================================} [PRO1_A:PROT1] Oh, wir müssen diesen Laden umbauen. Das muss ãlter aussehen. [PRO1_B:PROT1] Ich kann diesen Look nicht ab, Tommy. Was meinst du, sollen wir eine Bar einbauen..? [PRO1_D:PROT1] Hört mal. [PRO1_E:PROT1] Die Zeit ist gekommen, die Stadt zu übernehmen. Sie wartet nur auf uns. [PRO1_F:PROT1] Wir müssen langsam Gebiete einnehmen. [PRO1_G:PROT1] Vice City soll merken, dass wir die neuen Bosse sind, versteht ihr? [PRO1_H:PROT1] Jetzt beruhigt euch mal kurz. Allmãhlich kapiere ich schon, wie das ganze hier lãuft. [PRO1_I:PROT1] Was du brauchst, ist eine legale Fassade, Tommy, Immobilien. Hat mir nicht geschadet. [PRO1_J:PROT1] Wir müssen die Muskeln spielen lassen, sonst war die ganze harte Arbeit umsonst. [PRO1_K:PROT1] Die Geschãftsleute hier wissen, dass Diaz weg ist und weigern sich, Schutzgeld zu zahlen. [PRO1_L:PROT1] Oh, wir könnten es mit Schmiergeld versuchen... [PRO1_M:PROT1] Schmiergeld? Blödsinn! Ich zeig euch, wie man denen Angst macht. [PRO1_01:PROT1] ~g~Demoliere die Schaufenster der Lãden und die Inhaber werden darum betteln, zahlen zu dürfen. [PRO1_03:PROT1] ~r~Du sollst abhauen, nicht Kaffee trinken gehen. [PRO1_04:PROT1] Mein Lebenswerk! Zerstört! [PRO1_05:PROT1] Ich bin ruiniert...RUINIERT!! [PRO1_06:PROT1] Ich zahle einen Haufen Schutzgeld! [PRO1_07:PROT1] Mein schönes Schaufenster! [PRO1_08:PROT1] Mein Laden! Mein schöner Laden! [PRO1_09:PROT1] Vercetti. Merkt euch den Namen. [PRO1_10:PROT1] Ich bin jetzt der Boss in der Stadt. ICH! [BUYP1:PROT1] Du kannst jetzt in bestimmten Gegenden auf der Karte Objekte kaufen. [BUYP2:PROT1] Wenn du ein Gebãude mit grüner Markierung siehst, kannst du dieses kaufen. [PRO1_N:PROT1] Ich bin in fünf Minuten zurück... [PRO1_11:PROT1] ~g~Begib dich zum ~y~North Point Einkaufszentrum~g~ in ~y~Vice Point~g~. [PRO1_12:PROT1] ~g~Zerbrich die Schaufenster eines jeden Ladens, und die Inhaber werden um neuen Schutz betteln. [PRO1_C:PROT1] Du bist mein Anwalt, nicht mein Innenarchitekt. Klar? [BUYP3:PROT1] Stell dich in die Markierung und drücke die ~h~~k~~PED_ANSWER_PHONE~~w~-Taste, um das Objekt zu kaufen. [PRO1_13:PROT1] ~g~Du hast fünf Minuten, um alle zu demolieren. {=================================== MISSION TABLE PROT2 ===================================} [PRO2_01:PROT2] ~g~Schalte die Wachen aus, die die Front Page Bar beschützen und finde raus, wer hinter ihnen steckt. [PRO2_08:PROT2] ~g~Die DBP Security wird wissen, dass du kommst. Schnapp sie dir, ehe sie abhauen. [PRO2_A:PROT2] Was gibt's für Probleme? [PRO2_B:PROT2] Eine Bar weigert sich zu zahlen. [PRO2_C:PROT2] Die glauben, die werden von so einer Schlãgerbande beschützt. [PRO2_D:PROT2] Aber keine Sorge, Tommy, ich regle das. [PRO2_E:PROT2] Das nennst du regeln? [PRO2_F:PROT2] Ihr zwei. Hebt mal den Hintern... [PRO2_G:PROT2] Los. [PRO2_10:PROT2] Zwei sind abgehauen. Finde sie und bring die Sache zuende. [PRO2_11:PROT2] Steig in den Wagen, Nichtsnutz. [PRO2_02:PROT2] Dein Schutz braucht ein bisschen mehr Schutz. [PRO2_03:PROT2] Ach, verdammt! Nicht schon wieder! Das brauche ich wirklich nicht! [PRO2_04:PROT2] Diese Idioten arbeiten eigentlich für die DBP Security gleich um die Ecke. [PRO2_05:PROT2] Macht ihr das mal unter euch aus. [PRO2_06:PROT2] Wir sehen uns. [PRO2_07:PROT2] Ja, ja. Wenn's sein muss. [PRO2_09:PROT2] ~g~Begib dich zur Front Page Bar und sprich mit dem Besitzer. {=================================== MISSION TABLE PROT3 ===================================} [PRO3_A:PROT3] Du Trottel! Was hast du dir dabei gedacht?! [PRO3_B:PROT3] Ist dir klar, was das bedeutet?! [PRO3_C:PROT3] Das könnte das Ende für uns alle sein! [PRO3_D:PROT3] Der Zeitzünder muss hin gewesen sein. [PRO3_E:PROT3] Der Laden war mit Sprengstoff vollgepackt wie eine Feuerwerkfabrik. [PRO3_F:PROT3] Dann hat jemand den Cops einen Tipp gegeben... [PRO3_G:PROT3] Was gibt's für Probleme, Jungs? [PRO3_H:PROT3] Mike sollte einen Laden im Einkaufszentrum abfackeln, [PRO3_I:PROT3] aber er hat's vermasselt und jetzt wimmelt es dort von Bullen. [PRO3_J:PROT3] Wir müssen unser Zeug holen und verschwinden! [PRO3_K:PROT3] Langsam, ihr beiden, lasst mich kurz nachdenken! [PRO3_L:PROT3] Tommy Vercetti lãuft nicht einfach weg. [PRO3_M:PROT3] Die Cops werden das Gebãude sorgfãltig durchkãmmen, oder? [PRO3_N:PROT3] Aber das dauert. [PRO3_O:PROT3] Wir müssen den Laden selbst abfackeln. [PRO3_P:PROT3] Ja, aber... [PRO3_Q:PROT3] Nur ein Cop kommt auch nur in die Nãhe von dem Laden! [PRO3_R:PROT3] Dann gehen wir eben als Cops. [PRO3_S:PROT3] Wir brauchen Uniformen und einen Streifenwagen. [PRO3_T:PROT3] Und das alles nur wegen dir, Mike. [PRO3_U:PROT3] Tut mir leid. [PRO3_V:PROT3] Ich hab's. [PRO3_W:PROT3] Wir müssen die Cops hereinlocken, [PRO3_X:PROT3] dann sperren wir sie ein [PRO3_Y:PROT3] und überwãltigen sie. [PRO3_Z:PROT3] Guter Plan. Los geht's! [PRO3_A1:PROT3] Ok. [PRO3_01:PROT3] Ok, Lance, machen wir die Cops auf uns aufmerksam! [PRO3_02:PROT3] ~g~ Nimm den Streifenwagen und lege die Bombe im Tarbrush Coffee Shop im Einkaufszentrum. [PRO3_03:PROT3] ~g~Du hast Lance vergessen. Hole ihn. [PRO3_04:PROT3] ~g~ Los geht's. [PRO3_05:PROT3] ~r~Du hast Lance erledigt! [PRO3_07:PROT3] ~g~Die Tarnung ist aufgeflogen. Beeilung, platziere die Bombe! [PRO3_09:PROT3] Fessle und kneble sie! [PRO3_10:PROT3] Uuh! Passt perfekt! [PRO3_11:PROT3] Bisschen eng im Schritt vielleicht... [PRO3_12:PROT3] Oh ja, ja. Meine auch, meine auch. [PRO3_13:PROT3] Vorsicht, Bruder! Kein Cop fãhrt so schlecht! [PRO3_14:PROT3] Denk dran, lãchle die anderen Cops an. [PRO3_15:PROT3] Hallo, Officer. Hübsche Marke, hübsche Marke. [PRO3_16:PROT3] Ganz toll, Lance. [PRO3_17:PROT3] Ok, die Zünder sind auf 5 Sekunden gestellt. [PRO3_18:PROT3] 5 Sekunden?!! Nichts wie raus hier! [PRO3_19:PROT3] Jetzt sind sie so richtig stocksauer. [PRO3_20:PROT3] ~g~Bring 2 Cops dazu, dir in die Garage zu folgen. [PRO3_21:PROT3] ~g~Beschaff dir einen Fahndungslevel, damit dir die Cops in die Garage folgen. [PRO3_22:PROT3] ~g~Das Garagentor ist blockiert! Du musst es freirãumen, damit es schließen kann. [PRO3_23:PROT3] ~g~Stell dich in die Markierung, um die Bombe zu platzieren. [PRO3_24:PROT3] ~g~Verschwinde aus der Nãhe des Cafés! [PRO_AS1:PROT3] SCHUTZGELD-MISSIONEN ERFÜLLT [PRO_AS2:PROT3] ~g~Das Vercetti Estate generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmãßig ab. [PRO3_08:PROT3] ~g~Du musst zurück zu ~h~Vercetti Estate~g~ auf ~h~Starfish Island~g~. {=================================== MISSION TABLE RACES ===================================} [RACES_2:RACES] ~g~Du brauchst ein Fahrzeug! Das ist kein Wettlaufen! [RACES_3:RACES] 3..2..1.. LOS, LOS, LOS! [RACES_8:RACES] ~r~Du hast das Rennen nicht gewonnen! [RACES00:RACES] Rennen ~1~: [RACES01:RACES] Todeskaracho [RACES02:RACES] Ocean Drive [RACES03:RACES] Küsten-Rallye [RACES04:RACES] Capital Cruise [RACES05:RACES] Tour! [RACES06:RACES] V.C. Endurance [RACES07:RACES] Startgebühr: $~1~ [RACES08:RACES] Bestzeit: ~1~:~1~ [RACES09:RACES] Beste Platzierung: 1. [RACES10:RACES] Beste Platzierung: 2. [RACES11:RACES] Beste Platzierung: 3. [RACES12:RACES] Beste Platzierung: 4. [RACES13:RACES] Streckenlãnge: ~1~.~1~ km [RACES15:RACES] Bestzeit: Nicht verfügbar [RACES16:RACES] Beste Platzierung: Nicht verfügbar [RACES19:RACES] Du hast nicht genug Geld, um an diesem Rennen teilzunehmen. [RACES22:RACES] Bestzeit: ~1~:0~1~ [RACES23:RACES] Streckenlãnge: ~1~.~1~ Meilen [RACES_1:RACES] ~g~Schnapp dir ein schnelles Fahrzeug und begib dich zur Startlinie. [RACEHLP:RACES] ~w~Drücke die~h~ ~k~~PED_SPRINT~~w~, um das ausgewãhlte Rennen zu starten. Drücke die~h~ ~k~~VEHICLE_ENTER_EXIT~~w~, um abzubrechen. {=================================== MISSION TABLE RCHELI1 ===================================} [WRECKED:RCHELI1] ~r~Das Fahrzeug ist Schrott! [RCH1_4:RCHELI1] Verbleibende Checkpoints: [RCH1_7:RCHELI1] ~g~Es gibt insgesamt 20 Checkpoints. [RCH1_12:RCHELI1] ~g~Der ferngesteuerte Helikopter gerãt außer Reichweite! [RCH1_13:RCHELI1] ~r~Der ferngesteuerte Helikopter ist außer Reichweite! [RCH1_8:RCHELI1] { reVC update } ~g~Wenn du diese Mission abbrechen willst, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~g~, um deinen Heli zu sprengen. {=================================== MISSION TABLE RCPLNE1 ===================================} [RCPL1_4:RCPLNE1] ~g~Miss dich mit 3 anderen ferngesteuerten Maschinen in einem CHECKPOINT RENNEN [RCPL1_5:RCPLNE1] ~g~Flieg durch die Checkpoints, die über Vice City verteilt sind. [RCPL1_6:RCPLNE1] { reVC update } ~g~Wenn du diese Mission abbrechen willst, drücke die ~h~~k~~VEHICLE_FIREWEAPON~~g~, um dein Flugzeug zu sprengen. [RCPL1_8:RCPLNE1] ~g~Dein ferngesteuertes Flugzeug gerãt außer Reichweite! [RCPL1_9:RCPLNE1] ~r~Dein ferngesteuertes Flugzeug ist außer Reichweite! {=================================== MISSION TABLE RCRACE1 ===================================} [RCRC1_1:RCRACE1] ~g~Liefere dir mit 3 anderen RC Bandits ein CHECKPOINT-RENNEN über 2 RUNDEN. [RCRC1_3:RCRACE1] ~g~Letzte Runde! [RCR1_4:RCRACE1] Verbleibende Runden: [RCR1_1:RCRACE1] ~g~Tritt in einem Checkpoint-Rennen gegen 3 andere ferngesteuerte Autos an. [RCR1_2:RCRACE1] ~g~Du musst als erster 2 volle Runden auf dem Kurs fahren! [RCR1_6:RCRACE1] ~g~Dein ferngesteuertes Auto gerãt außer Reichweite! [RCR1_7:RCRACE1] ~r~Dein ferngesteuertes Auto ist außer Reichweite! {=================================== MISSION TABLE ROCK1 ===================================} [RBM1_A:ROCK1] AllllllllRrrighttt! [RBM1_B:ROCK1] Fantastisch, einfach fantastisch! [RBM1_D:ROCK1] Hey, kennst du die Jungs von Love Fist? [RBM1_E:ROCK1] Nein, aber ihre Musik habe ich immer geliebt. [RBM1_F:ROCK1] Ich stelle dir die Band vor. [RBM1_G:ROCK1] Das ist P, Percy, Dick, und Willy ist auf dem Klo. Und das vorher in der Kabine war Jezz. [RBM1_H:ROCK1] Jungs, ich möchte euch einen guten Freund vorstellen. [RBM1_I:ROCK1] Das ist Tommy. Wir kennen uns schon ewig. [RBM1_J:ROCK1] Alles paletti, Mann. [RBM1_K:ROCK1] Und, ãh, wie war dein Name noch mal? [RBM1_L:ROCK1] Lass das, Jezz. Merk dir eines, [RBM1_M:ROCK1] mit mir kannst du diese Spielchen nicht machen. [RBM1_N:ROCK1] Ich bin dir einfach über, Sonnenscheinchen. [RBM1_O:ROCK1] Die Sache ist die, Tom, die Jungs brauchen Hilfe. [RBM1_P:ROCK1] Die haben hier keine Connections, kein Vitamin B. [RBM1_Q:ROCK1] Wir brauchen Stoff, Alter. [RBM1_R:ROCK1] Damit wir den alten Love Fist Spirit finden, verstehst du?! [RBM1_S:ROCK1] Wir sind hier in Vice City, Mann. Wo liegt das Problem? [RBM1_U:ROCK1] Love Juice, Mann! [RBM1_V:ROCK1] Love Juice? [RBM1_W:ROCK1] Genau. 2 Teile TNT-Whiskey, 1 Teil Koks, 5 Pãckchen Brause und 1 Liter Sprit. [RBM1_X:ROCK1] Kannst du uns weiterhelfen, Alter? [RBM1_Y:ROCK1] Es würde den Jungs wirklich viel bedeuten. [RBM1_Z:ROCK1] Du kannst den Jungs doch helfen, oder? [RBM1_7:ROCK1] ~r~Du hast den Love Juice nicht rechtzeitig besorgt! [RBM1_8:ROCK1] ~r~Mercedes ist erledigt! [RBM1_10:ROCK1] ~r~Du Idiot! Du hast die Ware vernichtet! [RBM1_13:ROCK1] ~g~Bring den Love Juice und Mercedes zu der Band bevor sie auf die Bühne muss. [RBM1_15:ROCK1] ~r~Du hast den Dealer verloren, unser Geld und den Stoff! [RBM1_17:ROCK1] ~g~Knöpf dir den Dealer vor und hol dir den Stoff! [MOB_07A:ROCK1] Hey, die Jungs könnten ein bisschen Gesellschaft vertragen, falls du weißt, was ich meine... [MOB_07B:ROCK1] Da kenne ich genau die richtige. [ROK1_5:ROCK1] Hey, Mercedes! [ROK1_6:ROCK1] Hi, Tommy. Na, wie lãuft's so? [ROK1_7:ROCK1] Alles bestens. Hör mal, willst du die Jungs von Love Fist verführen? [ROK1_8:ROCK1] Ok, aber dafür bist du mir einen Gefallen schuldig. [RBM1_14:ROCK1] ~g~Du brauchst ein Auto oder Motorrad! [RBM1_1:ROCK1] ~g~Hol Mercedes in ihrer Wohnung ab. [RBM1_12:ROCK1] ~g~Besorg die Zutaten für den 'Love Juice' von dem Dealer. [ROK1_2:ROCK1] NICHT MEHR BENÖTIGT [ROK1_3:ROCK1] NICHT MEHR BENÖTIGT [MERC_39:ROCK1] Wir sehen uns spãter, Big Boy. [RBM1_C:ROCK1] Hey, Tommy! Schön, dass du kommen konntest. [RBM1_9:ROCK1] ~g~Besorg von dem Dealer Love Juice für Love Fist! [ROK1_1A:ROCK1] Suchst du was Bestimmtes? Ich habe genau, was du brauchst! [ROK1_9:ROCK1] Danke für die Kohle, du Trottel! [RBM1_T:ROCK1] Wir brauchen Love Juice, Mann, klar? {=================================== MISSION TABLE ROCK2 ===================================} [RBM2_A:ROCK2] Tommy, Mann, bin ich froh, dich zu sehen! [RBM2_B:ROCK2] Was ist los? [RBM2_C:ROCK2] Schlechte Vibes, Tommy... [RBM2_E:ROCK2] Da ist so 'n Typ, wir kennen ihn kaum, aber er kennt uns. [RBM2_F:ROCK2] So wie er da. Weiß alles über uns. [RBM2_G:ROCK2] Weiß, dass Willy auf Damenunterwãsche steht, eh! [RBM2_H:ROCK2] Oder dass Percy auf Duran Duran steht! [RBM2_K:ROCK2] Ja, die Liebesrakete, schon gut. Aber hör zu, dieser Typ... [RBM2_L:ROCK2] ja, ja, der Typ will Love Fist ausknipsen. [RBM2_M:ROCK2] Ausknipsen, Tommy. [RBM2_N:ROCK2] Love Fist soll verschwinden. Wie es immer heißt: Die Besten sterben jung. [RBM2_O:ROCK2] Aber Tommy, du musst Love Fist retten! [RBM2_P:ROCK2] Wir geben in zwei Stunden eine Autogrammstunde, und ich glaube... [RBM2_Q:ROCK2] Und die Jungs glauben, dass der Kerl dort irgendwas plant. [RBM2_1:ROCK2] ~g~Fahre die Limo zum Ort der Autogrammstunde und versuche, den Irren zu finden. [RBM2_2:ROCK2] ~r~Du hast den Wagen der Band geschrottet! [RBM2_3:ROCK2] ~g~Geh zu der Autogrammstunde! [RBM2_4:ROCK2] ~g~Erledige den Irren. Lass ihn nicht entkommen! [RBM2_5:ROCK2] ~r~Du hast ihn verloren, du Idiot! [RBM2_7:ROCK2] ~r~Die Fans wurden angegriffen. Der Irre wird nicht auftauchen! [RBM2_8:ROCK2] ~r~Die Security-Leute wurden angegriffen. Der Irre wird nicht auftauchen! [PSYCH_1:ROCK2] Love Fist wird in der Hölle braten! [PSYCH_2:ROCK2] Love Fist hat mein Leben zerstört! [RBM2_I:ROCK2] Halt's Maul, du Irrer! Nur weil Jezz auf Schafe steht. [RBM2_R:ROCK2] Hey, Schnauze! [RBM2_D:ROCK2] Das ist kein Spaß, das ist 'ne brutale Geschichte, brutal, ok? [RBM2_J:ROCK2] Ich sage nur, die Liebesrakete, ok? {=================================== MISSION TABLE ROCK3 ===================================} [RBM3_A:ROCK3] Tommy! Tommy! Tommy, Mann, der Irre ist wieder da! [RBM3_B:ROCK3] Was ist denn los? [RBM3_C:ROCK3] Dieser Irre lãsst Love Fist nicht in Ruhe! [RBM3_D:ROCK3] Du hast ihn nicht erwischt, Mann. Und jetzt ist er wieder da. [RBM3_E:ROCK3] Ja, ja, ja, und es ist nãmlich so... [RBM3_F:ROCK3] wir brauchen für die Limo einen Fahrer, dem wir vertrauen können, [RBM3_G:ROCK3] weil der Irre uns stãndig bedroht! [RBM3_I:ROCK3] Wir scheißen uns alle in die Hosen, Mann. [RBM3_J:ROCK3] Ok, Jungs, nur die Ruhe. Ich mach das schon. [RBM3_K:ROCK3] Normalerweise würde ich keine besoffenen, schottischen Tucken durch die Gegend kutschieren, [RBM3_L:ROCK3] aber für euch mache ich eine Ausnahme. [ROK3_03:ROCK3] Dann mach mir einen großen. [ROK3_04:ROCK3] Hey, Tommy, nun mach mal halblang, Mann. [ROK3_08:ROCK3] Langsam wird's langweilig. [ROK3_09:ROCK3] Halt bloß das Pedal durchgedrückt!! [ROK3_61:ROCK3] Wir müssen die Bombe finden! [ROK3_28:ROCK3] Ich werd Bass in der Hölle spielen. [ROK3_30:ROCK3] Tu doch einer was! [ROK3_32:ROCK3] Ok, Obermacho, dann tu du doch was! [ROK3_34:ROCK3] Willy könnte den TNT-Whiskey mit einem Strohhalm raussaugen. [ROK3_37:ROCK3] Gebt Willy einen Strohhalm! [ROK3_41:ROCK3] Welchen Draht, Tommy? [ROK3_42:ROCK3] Den grünen. [ROK3_43:ROCK3] Da ist kein grüner. Oder ist der hier grün? [ROK3_44:ROCK3] Sieht für dich einer von den Drãhten grün aus? [ROK3_49:ROCK3] Ich hab euch doch jahrelang nur mitgeschleppt. [ROK3_51:ROCK3] Ein großes keifendes Weib. [ROK3_52:ROCK3] Ja. [ROK3_53:ROCK3] Klappe jetzt und zieht einen Draht raus. [ROK3_54:ROCK3] Welchen? [ROK3_55:ROCK3] Den da... [ROK3_56:ROCK3] NEIN! [ROK3_57:ROCK3] Mann, alles ok. Wir sind nicht hochgegangen, Alter. Tommy, Mann, gut gemacht. Rock 'n' Roll, Mann. [ROK3_58:ROCK3] Müssen wir nicht zu einem Gig? Krach machen? Groupies abgreifen? [ROK3_59:ROCK3] LOVE FIST! [ROK3_60:ROCK3] Bist du fertig mit der Pulle? [RBM3_4:ROCK3] ~r~Love Fist ist Geschichte! [RBM3_6:ROCK3] DETONATION: [RBM3_1:ROCK3] ~g~Chauffiere Love Fist zu ihrem Auftritt. [RBM3_2:ROCK3] Falls du versuchst, aus dem Auto auszusteigen, wenn die Bombe scharfgemacht ist, wird sie explodieren... [RBM3_3:ROCK3] Ist der Detonations-Balken am Anschlag, explodiert die Bombe. [RBM3_8:ROCK3] Je schneller du fãhrst, desto niedriger der Detonations-Balken. [RBM3_7:ROCK3] ~g~BOMBE ENTSCHÃRFT! [ROK3_6A:ROCK3] ~g~Love Fist. Ihr habt den Ãther lange genug verschmutzt! [ROK3_6B:ROCK3] ~g~Ich wollte euer Freund sein. Jetzt will ich, dass ihr untergeht. [ROK3_62:ROCK3] Wir dachten, wir zeigen dir mal unseren Rocktempel... [ROK3_63:ROCK3] Kannst dir die Power von Love Fist reinziehen! [ROK3_64:ROCK3] Hör sich das einer an, Mann. Das ist Pappmaché und Klebeband. [ROK3_65:ROCK3] Hey, für die Kids ist es ein Tempel, und wir sind die Priester! [ROK3_66:ROCK3] Tja, wenn die Kids es toll finden, dass ihre Priester dicht und unmusikalisch sind, [ROK3_67:ROCK3] was soll man da sagen? [ROK3_68:ROCK3] Oh, Mist, das Ding frisst schon wieder das Band. [ROK3_69:ROCK3] Wenn das so ist, müssen wir live spielen. [ROK3_70:ROCK3] Ooooh Shit! Mein Darm... [ROK3_01:ROCK3] Endlich, Mann, Zeit für einen wohlverdienten Drink. [ROK3_02:ROCK3] Die Halle ist nur 100 Meter die Straße runter. [ROK3_05:ROCK3] Ich werd irre, wenn ich keinen Sprit kriege. [ROK3_07:ROCK3] Tommy, mein Freund, wir müssen die Band retten! [ROK3_29:ROCK3] Tommy, bleib auf dem Gas, Alter. [ROK3_31:ROCK3] Ganz toll. 'Tu doch einer was.' Was ist denn das für eine Ansage? Da kenn ich mutigere Mãdels. [ROK3_33:ROCK3] Alter, ich bin Musiker. Vom Bomben-Entschãrfen hab ich keinen blassen Dunst. [ROK3_35:ROCK3] Eben, so was liegt dir doch, was ich so höre. [ROK3_38:ROCK3] Einen Strohhalm?! Dies ist der Tour-Bus von Love Fist! [ROK3_39:ROCK3] Wo zum Geier sollte hier ein Strohhalm sein? [ROK3_46:ROCK3] Ich hãtte euch alle rausschmeißen sollen, als es noch ging, Mann. [ROK3_47:ROCK3] Kapitalist. [ROK3_48:ROCK3] Kameradenschwein. [ROK3_73:ROCK3] Jezz spielt das Band ab, [RBM3_9:ROCK3] Wenn du angehalten wirst oder langsam fãhrst, wãchst der Detonations-Balken. [ROK3_50:ROCK3] Halt die Klappe. Du bist ein Idiot. [ROK3_36:ROCK3] Hey, ich war an dem Abend vielleicht komplett zugedröhnt! [RBM3_H:ROCK3] Ich scheiß mir in die Hose, Mann. Ich will zu meiner Mama! [ROK3_45:ROCK3] Oh nein. Im Angesicht des Todes sieht alles grün aus. [ROK3_6C:ROCK3] ~g~Wenn ihr langsamer werdet, geht eure Limo hoch, IHR UND EURE HAARIGEN ÃRSCHE mit dazu! [ROK3_71:ROCK3] Wir müssen uns ranhalten. Danke nochmal, Tommy, du hast es echt drauf. Ciao. [ROK3_1:ROCK3] Endlich, Mann, Zeit für einen wohlverdienten Drink. Die Halle ist nur 100 Meter die Straße runter. [ROK3_2:ROCK3] Mach mir mal einen großen. Hey, Tommy, leg mal 'nen anderen Sound auf, Mann. [ROK3_3:ROCK3] Ich werd wirr im Kopf, wenn nichts zum Headbangen lãuft. Hey, Tommy, leg mal dieses Band ein. [ROK3_4:ROCK3] Love Fist. Ihr habt den Ãther lange genug verschmutzt! Ich wollte euer Freund sein. [ROK3_5:ROCK3] Jetzt will ich, dass ihr untergeht. Wenn ihr langsamer werdet, geht eure Limo hoch, IHR UND EURE HAARIGEN ÃRSCHE mit dazu! [ROK3_6:ROCK3] Tommy, mein Freund, du musst die Band retten! Langsam wird's langweilig. Halt bloß das Pedal durchgedrückt!! [ROK3_7:ROCK3] Wir müssen die Bombe finden! Können wir nicht einfach den ganzen Tag rumfahren? Wir haben jedenfalls jede Menge zu saufen. [ROK3_8:ROCK3] Könnte die Bombe nicht unter der Motorhaube sein? Da kommen wir nie ran, ohne anzuhalten! Wir werden alle sterben! Ich besauf mich! [ROK3_9:ROCK3] Hey, hier gibt's eine Warteschlange, Alter! Die Lösung liegt nicht in der Minibar! Weg da! [ROK3_10:ROCK3] Hey, aus der Wodkapulle kommen Drãhte raus! Das ist kein Wodka, das ist TNT-WHISKEY! [ROK3_11:ROCK3] WAAAAAAGGGHHHH!!!! Das Ding ist scharfgemacht!! WAAAAAAAAAAGGGHHHHHH!!!! [ROK3_12:ROCK3] Man hat mir immer gesagt, der Alk wird mich killen... Das kenn ich aus dem Fernsehen. Du musst einen der Drãhte rausziehen. Welchen Draht? Weiß ich doch nicht, Mann. [ROK3_13:ROCK3] Keinen Schimmer. Willy, sag doch mal was. Ich werd Bass in der Hölle spielen. [ROK3_14:ROCK3] Tommy, bleib auf dem Gas, Mann. Tu doch einer was! Ganz toll... [ROK3_15:ROCK3] 'Tu doch einer was.' Was ist denn das für eine Ansage? Da kenn ich mutigere Mãdchen. Ok, Obermacho, dann tu du doch was! [ROK3_16:ROCK3] Alter, ich bin Musiker. Mit Bomben kenne ich mich nicht aus. Willy könnte den TNT-Whiskey mit einem Strohhalm raussaugen. [ROK3_17:ROCK3] Eben, so was liegt dir doch, was ich so höre. Hey, ich war total blau an dem Abend, das wisst ihr ganz genau! [ROK3_18:ROCK3] Gebt Willy einen Strohhalm! Einen Strohhalm?! Wi sind im Band-Auto von Love Fist! [ROK3_19:ROCK3] Wo zum Geier sollte hier ein Strohhalm sein? Welchen Draht, Tommy? Den grünen. Da ist kein grüner. [ROK3_20:ROCK3] Oder ist der hier grün? Sieht für dich einer von den Drãhten grün aus? [ROK3_21:ROCK3] Oh nein. Im Angesicht des Todes sieht alles grün aus. Ich hãtte euch alle rausschmeißen sollen, als es noch ging, Mann. [ROK3_22:ROCK3] Kameradenschwein. Kapitalist. Ich hab euch doch jahrelang nur mitgeschleppt. Halt die Klappe. Du bist ein Idiot. [ROK3_23:ROCK3] Ein großes keifendes Weib. Ja. Klappe jetzt und zieht einen Draht raus. Welchen? Den da... [ROK3_24:ROCK3] NEIN! Mann, alles ok. Wir sind nicht hochgegangen, Alter. [ROK3_25:ROCK3] Tommy, Mann, gut gemacht. Rock 'n' Roll, Mann. Müssen wir nicht zu einem Gig? [ROK3_26:ROCK3] Krach machen? Groupies abgreifen? LOVE FIST! [ROK3_27:ROCK3] Bist du fertig mit der Pulle? {=================================== MISSION TABLE SERG1 ===================================} [TEX1_0:SERG1] ~g~Die Zielperson ist auf der Driving Range. Sorg dafür, dass er seinen letzten Golfball geschlagen hat! [TEX1_A:SERG1] Komm rein und setz dich auf deinen Hintern, Junge. [TEX1_B:SERG1] Mein Daddy hat immer gesagt, einem geschenkten Gaul schaut man nicht ins Maul. Hat's auch nie getan. [TEX1_C:SERG1] Ein Glãschen alter Kentucky gefãllig? [TEX1_D:SERG1] Nein danke. [TEX1_E:SERG1] Ein nüchterner Denker, das gefãllt mir. [TEX1_F:SERG1] Im Immobiliengeschãft geht's nicht um hochtrabende Vertrãge. [TEX1_G:SERG1] Es geht um Land. Und darum, dieses Land zu kriegen. Kannst du mir folgen? [TEX1_H:SERG1] Oh, ja. [TEX1_I:SERG1] Ich will, dass so ein sturer Hund sein Land hergibt. [TEX1_J:SERG1] Mir scheint, du könntest ihn dazu überreden. [TEX1_K:SERG1] Ich bin der reinste Überredungskünstler. [TEX1_L:SERG1] Ja. Er wird im Country Club sein, auf dem Golfplatz. [TEX1_M:SERG1] Knarren sind dort verboten. Seine Bodyguards werden also unbewaffnet sein. [TEX1_N:SERG1] Du sollst ihn so richtig gründlich vermöbeln. [TEX1_O:SERG1] Hier, ich hab dir einen Mitgliedsausweis besorgt. Aber du brauchst passendere Kleidung. [TEX1_2:SERG1] ~g~Begib dich jetzt zum Leaf Links Golf Club. [TEX1_3:SERG1] Wer ist der Kerl? Jungs, nehmt ihn euch vor. [TEX1_6:SERG1] Hübscher Hintern, Baby! [TEX1_7:SERG1] Bin ich das? [TEX1_8:SERG1] Jedesmal wenn du in einen Golfwagen steigst, erhãltst du automatisch einen Golfschlãger, vorausgesetzt, du hast nicht schon eine Nahkampfwaffe. [TEX1_9:SERG1] Schnapp ihn dir! [TEX1_10:SERG1] Mach den Irren fertig! [TEX1_1:SERG1] ~g~Besorg dir bei Jocksport Golferklamotten. {=================================== MISSION TABLE SERG2 ===================================} [TEXEXIT:SERG2] ~g~Jetzt verschwinde aus Little Haiti! [TEX_2A:SERG2] ~g~Großartig! Sie haben dich bemerkt! [TEX_2B:SERG2] ~r~Narr! Die Leute müssen SEHEN, dass der Tãter ein Kubaner ist! [TEX_2C:SERG2] ~g~Besorge dir bei Rafael's Kleidung in den Farben der kubanischen Gang. [TEX_2D:SERG2] ~g~Nimm dir jetzt den Boss der Haitianer in Romeros Beerdigungsinstitut vor. [TEX2_A:SERG2] Tommy, das ist Donald Love. Donald, das ist Tommy Vercetti, [TEX2_B:SERG2] der neueste Draufgãnger hier in der Stadt. [TEX2_C:SERG2] Ja...ãh... [TEX2_D:SERG2] Donald, sei still und hör zu. Vielleicht kannst du was lernen. [TEX2_E:SERG2] Also. Nichts lãsst Immobilienpreise schneller abstürzen als ein guter alter Bandenkrieg. [TEX2_F:SERG2] Außer vielleicht eine Katastrophe, eine biblische Plage oder so, [TEX2_G:SERG2] aber das ginge hier wohl zu weit. [TEX2_H:SERG2] Kannst du mir folgen, Brillenschlange? [TEX2_I:SERG2] Jüngst starb ein haitianischer Gang-Boss. Man tippt, es waren die Kubaner, aber keiner weiß es. [TEX2_J:SERG2] Aber wir wollen sicher gehen. Du verkleidest dich als kubanischer Hombre [TEX2_K:SERG2] und machst Krawall bei der Beerdigung. Misch sie auf und dann verzieh dich. [TEX2_L:SERG2] Kannst du mir folgen, Donald? [TEX2_M:SERG2] Das dürfte das Fass zum Überlaufen bringen, was? [TEX2_N:SERG2] Und wir lehnen uns zurück und sehen zu, wie die Preise purzeln. {=================================== MISSION TABLE SERG3 ===================================} [TEX3_A:SERG3] Pass auf, Junge. Ich habe ein Problem und denke, du könntest mir weiterhelfen. [TEX3_B:SERG3] Ich bin kein Bauunternehmer. [TEX3_C:SERG3] Nein, ich dachte mehr an deine Talente als Abrissunternehmer. [TEX3_D:SERG3] Das hier zeigt, was wir geplant haben. Und so- [TEX3_E:SERG3] -so sieht der betreffende Grund heute aus. [TEX3_F:SERG3] Sie wollen sagen, das neue Bürohaus da ist Ihnen im Weg. [TEX3_G:SERG3] Gut mitgedacht. [TEX3_H:SERG3] Also, ich verdrücke mich eine Weile aus der Stadt, [TEX3_I:SERG3] und wenn dieses Bürogebãude urplötzlich irreparable Schãden aufweist, dann... [TEX3_J:SERG3] ....fühlen Sie sich als guter Mensch verpflichtet, einzuspringen und [TEX3_K:SERG3] -für die Neugestaltung eines wichtigen Gebiets der Stadt zu sorgen? [TEX3_L:SERG3] Wo finde ich mehr Mãnner wie dich? [TEX3_1:SERG3] ~g~Benutze den ferngesteuerten Helikopter, um Bomben zu 4 Zielen an dem zur Sprengung vorgesehenen Gebãude zu transportieren. [TEX3_2:SERG3] ~g~Du musst an jedem Ziel eine Bombe abwerfen. Die Reihenfolge ist beliebig. [TEX3_3:SERG3] ~g~Steuere den Helikopter direkt über eine Bombe, um sie aufzunehmen. Die Bombe heftet sich dann automatisch an den Helikopter. [TEX3_5:SERG3] ~g~Wenn eine Bombe ihr Ziel verfehlt, kannst du sie erneut aufnehmen und es nochmal versuchen. [TEX3_7:SERG3] ~g~Dann hast du noch 7 Minuten, um die restlichen Bomben im Ziel zu platzieren. [TEX3_8:SERG3] ~g~Du hast das Ziel verfehlt! Nimm die Bombe wieder auf und versuche es nochmal! [TEX3_10:SERG3] ~g~Wirf die Bombe über einem der Ziele ab. [TEX3_11:SERG3] Verbleibende Ziele: [TEX3_17:SERG3] ~r~Die Zeit ist um. Du hast es nicht geschafft, das Gebãude zu sprengen. [TEX3_18:SERG3] ~r~Dein Helikopter wurde zerstört! Wie willst du jetzt die Bomben transportieren? [TEX3_19:SERG3] ~r~Deine Bombe ist im Wasser gelandet! Du brauchst ALLE 4 Bomben, um die Sprengung vorzunehmen. [TEX3_20:SERG3] ~g~Dein Helikopter ist fast außer Reichweite. Du musst zurück zur Baustelle, um deine Arbeit zuende zu bringen! [TEX3_21:SERG3] ~r~Dein Helikopter befindet sich außer Reichweite! [TEX3_24:SERG3] Drücke ~h~~k~~VEHICLE_LOOKLEFT~~w~, um den Helikopter gegen den Uhrzeigersinn zu drehen. [TEX3_25:SERG3] Drücke ~h~~k~~VEHICLE_LOOKLEFT~~w~, um den Helikopter im Uhrzeigersinn zu drehen. [TEX3_27:SERG3] ~g~Über eine Haupttreppe hat man Zugang zu allen Stockwerken des Gebãudes. [TEX3_31:SERG3] ~r~Du hast den Wagen mit den Bomben und dem ferngesteuerten Helikopter zerstört! [TEX3_32:SERG3] Du kannst ~h~nach hinten sehen~w~, indem du ~h~gleichzeitig ~k~~VEHICLE_LOOKLEFT~ und ~k~~VEHICLE_LOOKRIGHT~ drückst~w~. [TEX3_4:SERG3] { reVC update } ~g~Um eine Bombe abzuwerfen, drücke die~h~ ~k~~VEHICLE_FIREWEAPON~~w~. [TEX3_29:SERG3] { reVC update } Um eine Bombe abzuwerfen, drücke die~h~ ~k~~VEHICLE_FIREWEAPON~. [TEX3_26:SERG3] Drücke die ~h~~k~~VEHICLE_BRAKE~~w~, um die Rotorgeschwindigkeit zu verringern, der Helikopter ~h~verliert dann an Höhe. [TEX3_22:SERG3] Drücke die ~h~~k~~VEHICLE_ACCELERATE~~w~, um die Rotorgeschwindigkeit zu erhöhen, der Helikopter ~h~gewinnt dann an Höhe. [TEX3_16:SERG3] ~g~Begib dich zu dem ~w~TOPFUN~g~-Wagen nahe dem zum Abriss vorgesehenen Gebãude. [TEX3_33:SERG3] Wenn du eine Bombe aufgenommen hast, zeigt dir das Radar die Position des Ziels in Relation zu dem ferngesteuerten Helikopter. [TEX3_34:SERG3] Ein ~h~nach oben zeigendes Dreieck ~w~bedeutet, dass das Ziel sich in ~h~größerer Höhe ~w~befindet als der Helikopter. [TEX3_35:SERG3] Ein ~h~nach unten zeigendes Dreieck ~w~bedeutet, dass das Ziel sich in ~h~geringerer Höhe ~w~befindet als der Helikopter. [TEX3_36:SERG3] Ein ~h~Viereck ~w~ bedeutet, dass das Ziel sich auf ~h~der gleichen Höhe ~w~befindet wie der Helikopter. [TEX3_6:SERG3] ~g~Wenn du die erste Bombe aufgenommen hast, wird der Zeitzünder aktiviert. [TEX3_28:SERG3] Um ~h~eine Bombe aufzunehmen~w~, steuere den Helikopter direkt über sie. Der Helikopter kann immer nur eine Bombe tragen. [TEX3_30:SERG3] ~g~Um eine Bombe aufzunehmen, steuere den Helikopter direkt über sie. Der Helikopter kann immer nur eine Bombe tragen. [TEX3_12:SERG3] ~g~Bombe platziert! Es bleiben nur noch 3 Ziele! Hol die nãchste Bombe. [TEX3_13:SERG3] ~g~Bombe platziert! Es bleiben nur noch 2 Ziele! Hol die nãchste Bombe. [TEX3_14:SERG3] ~g~Bombe platziert! Es bleibt nur noch 1 Ziel! Hol die nãchste Bombe. [TEX3_15:SERG3] ~r~Zeitzünder aktiviert! ~g~ Du musst die ~w~4 Bomben ~g~in der verbleibenden Zeit platzieren. [TEX3_37:SERG3] Zieh den ~h~ Rechten Analog-Stick zurück~w~, um die Rotorgeschwindigkeit zu erhöhen, der Helikopter ~h~ gewinnt dann an Höhe. [TEX3_38:SERG3] { reVC update } {Drück den ~h~ ~k~~VEHICLE_ACCELERATE~~w~, um die Rotorgeschwindigkeit zu verringern, der Helikopter ~h~ verliert dann an Höhe.} Drück den ~h~ Rechten Analog-Stick nach vorn~w~, um die Rotorgeschwindigkeit zu verringern, der Helikopter ~h~ verliert dann an Höhe. [TEX3_39:SERG3] Um eine Bombe abzuwerfen, drücke die ~h~~k~~VEHICLE_HANDBRAKE~~g~-Taste. [TEX3_40:SERG3] Um eine Bombe abzuwerfen, drücke die ~h~~k~~VEHICLE_HANDBRAKE~~w~-Taste. [TEX3_23:SERG3] Mit ~h~~k~~VEHICLE_TURRETUP~~w~ und ~h~~k~~VEHICLE_TURRETDOWN~~w~ neigst du den Helikopter in die Richtung, in die du ihn steuern willst. {=================================== MISSION TABLE TAXI1 ===================================} [FARES:TAXI1] FAHRTEN: [TAXI1:TAXI1] ~g~Besorg dir einen Fahrgast. [TSCORE2:TAXI1] $~1~ [IN_ROW:TAXI1] ~1~ SERIEN-Bonus! $~1~ [TAXI3:TAXI1] ~r~Dein Fahrgast ist entsetzt geflohen! [TAXI7:TAXI1] ~r~Dein Wagen ist Schrott. Repariere ihn. [TAXI4:TAXI1] Fahrt abgeschlossen! [TAXI5:TAXI1] SPEED BONUS!! [TAXI6:TAXI1] Taxi-Mission beendet [TAXIH1:TAXI1] Halte neben einem markierten Fußgãnger, um ihn einsteigen zu lassen, dann bringe ihn rechtzeitig an sein Fahrtziel. [FARE1:TAXI1] ~g~Fahrtziel ~w~'Pole Position Club' ~g~in Ocean Beach. [FARE3:TAXI1] ~g~Fahrtziel ~w~'Marina' ~g~in Ocean Beach. [FARE4:TAXI1] ~g~Fahrtziel ~w~'Ammu-Nation' ~g~in Ocean Beach. [FARE5:TAXI1] ~g~Fahrtziel ~w~'Eisenwarenladen' ~g~in Washington Beach. [FARE6:TAXI1] ~g~Fahrtziel ~w~'North Point Einkaufszentrum' ~g~in Vice Point. [MFARE1:TAXI1] ~g~Fahrtziel ~w~'Ammu-Nation' ~g~in Downtown. [MFARE2:TAXI1] ~g~Fahrtziel ~w~'Terminal' ~g~im Escobar International Airport. [WFARE3:TAXI1] ~g~Fahrtziel ~w~'Sunshine Autos' ~g~in Little Havana. [WFARE4:TAXI1] ~g~Fahrtziel ~w~'Kaufman-Taxis' ~g~in Little Haiti. [WFARE5:TAXI1] ~g~Fahrtziel ~w~'Eisenwarenladen' ~g~in Little Havana. [WFARE6:TAXI1] ~g~Fahrtziel ~w~'Howlin Petes Bike Emporium' ~g~in Downtown. [FARE7:TAXI1] ~g~Fahrtziel ~w~'die Juweliere' ~g~in Vice Point. [FARE8:TAXI1] ~g~Fahrtziel ~w~'der Strand' ~g~in Ocean Beach. [FARE9:TAXI1] ~g~Fahrtziel ~w~'der Strand' ~g~in Washington Beach. [FARE10:TAXI1] ~g~Fahrtziel ~w~'der Strand' ~g~in Vice Point. [FARE11:TAXI1] ~g~Fahrtziel ~w~'das Krankenhaus' ~g~in Ocean Beach. [FARE12:TAXI1] ~g~Fahrtziel ~w~'das Krankenhaus' ~g~in Vice Point. [FARE13:TAXI1] ~g~Fahrtziel ~w~'die Polizeistation' ~g~in Washington Beach. [FARE14:TAXI1] ~g~Fahrtziel ~w~'die Polizeistation' ~g~in Vice Point. [FARE15:TAXI1] ~g~Fahrtziel ~w~'die Pizzagaststãtte' ~g~in Vice Point. [WFARE7:TAXI1] ~g~Fahrtziel ~w~'die Polizeistation' ~g~in Little Havana. [WFARE8:TAXI1] ~g~Fahrtziel ~w~'die Polizeistation' ~g~in Downtown. [WFARE9:TAXI1] ~g~Fahrtziel ~w~'das Krankenhaus' ~g~in Downtown. [WFARE10:TAXI1] ~g~Fahrtziel ~w~'das Krankenhaus' ~g~in Little Havana. [WFARE11:TAXI1] ~g~Fahrtziel ~w~'der Stadium' ~g~in Downtown. [WFARE12:TAXI1] ~g~Fahrtziel ~w~'die Pizzagaststãtte' ~g~in Little Haiti. [WFARE13:TAXI1] ~g~Fahrtziel ~w~'die Pizzagaststãtte' ~g~in Downtown. [WFARE14:TAXI1] ~g~Fahrtziel ~w~'die Docks' ~g~in Viceport. [WFARE15:TAXI1] ~g~Fahrtziel ~w~'die Apotheke' ~g~in Little Haiti. [FARE2:TAXI1] ~g~Fahrtziel ~w~'Malibu Club' ~g~in Vice Point. {=================================== MISSION TABLE TAXICUT ===================================} [TAXC_A:TAXICUT] Schãtze, sie sind der neue Besitzer. [TAXC_B:TAXICUT] Sind Sie 'n Mafioso? Oder vom Kartell? Sehen nicht aus wie ein Mexikaner. [TAXC_C:TAXICUT] Egal. Halten Sie schon endlich Ihre Predigt von wegen 'Jetzt wird alles anders', [TAXC_D:TAXICUT] bedrohen Sie ein paar von den Fahrern- [TAXC_E:TAXICUT] aber nicht Ted, der ist gerade an der Leiste operiert. [TAXC_F:TAXICUT] Tja, also, hier wird sich einiges ãndern, Lady. [TAXC_G:TAXICUT] Aber nicht doch, Jungchen. Überlassen Sie das lieber mir - [TAXC_H:TAXICUT] Ich mach das schon seit Jahren. [TAXC_I:TAXICUT] Alles mal herhören. [TAXC_J:TAXICUT] Wir haben eine neue Geschãftsleitung, und es wird sich wieder mal einiges ãndern hier. [TAXC_K:TAXICUT] Unsere neue Geschãftsleitung, die- [TAXC_L:TAXICUT] Von welcher Gang sind Sie? [TAXC_M:TAXICUT] Ich gehöre keiner Gang an. [TAXC_N:TAXICUT] Und wie heißen Sie, junger Mann? [TAXC_O:TAXICUT] Vercetti, Tommy Vercetti. [TAXC_P:TAXICUT] Unsere neue Geschãftsleitung, die Vercetti Gang, [TAXC_Q:TAXICUT] wird dafür sorgen, dass wir keinen Ãrger kriegen. [TAXC_R:TAXICUT] Capiche? Ende! [TAXC_S:TAXICUT] Wie fanden Sie das 'Capiche'? Ich fand's gut. [TAXC_T:TAXICUT] Also, so ist das immer gelaufen: [TAXC_U:TAXICUT] Wir führen die Firma weiter wie gewohnt. [TAXC_V:TAXICUT] Wenn die Konkurrenz Ãrger macht, gebt ihr ihnen eines auf die Mütze. [TAXC_W:TAXICUT] Dann geben die uns eines auf die Mütze. [TAXC_X:TAXICUT] Dann geben sie denen eines auf die Mütze. [TAXC_Y:TAXICUT] Und so weiter, und so fort. Kapiert? [TAXC_Z:TAXICUT] Ãh, ja, ich glaub schon. [TAXC_A1:TAXICUT] Schnappen Sie sich ein Taxi aus der Garage, wenn Sie Lust haben. {=================================== MISSION TABLE TAXIWA1 ===================================} [OUTTIME:TAXIWA1] ~r~Zu langsam, Mann, zu langsam! [TAX1_1:TAXIWA1] Ok, eine V.I.P. müsste von Starfish Island abgeholt werden. Jemand interessiert? [TAX1_2:TAXIWA1] Tommy hier. Ich übernehme das. [TAX1_3:TAXIWA1] Das ist meine Fuhre. Hau ab! [TAX1_4:TAXIWA1] Los, los, steigen Sie ein. Schnell! [TAX1_5:TAXIWA1] Ok, ok! Aber tun Sie mir nichts! [TAXW1_1:TAXIWA1] ~g~Hol die V.I.P. auf Starfish Island ab. [TAXW1_2:TAXIWA1] ~g~Hol die V.I.P. zurück! Schalte den anderen Wagen aus! [TAXW1_3:TAXIWA1] ~r~Die V.I.P. ist Geschichte! [TAXW1_4:TAXIWA1] ~r~Die V.I.P. wurde abgesetzt! [TAXW1_6:TAXIWA1] ~g~Bring die V.I.P zum Flughafen! {=================================== MISSION TABLE TAXIWA2 ===================================} [TAX2_1:TAXIWA2] An alle Wagen. Wir kriegen nirgends Fahrgãste. Was ist los mit euch? [TAX2_2:TAXIWA2] VC-Taxi ist dauernd schneller als wir. Die haben einfach zu viele Autos. Keine Chance. [TAX2_3:TAXIWA2] Mr. Vercetti, wenn Sie zufãllig mithören: Sie müssen ein paar VC-Taxis ausschalten, sonst sind wir pleite! [TAXW2_1:TAXIWA2] ~g~Schalte 3 Taxis der Konkurrenz aus! {=================================== MISSION TABLE TAXIWA3 ===================================} [TAX3_1:TAXIWA3] Wagen 13, eine Miss Cortez möchte von Ihnen ganz persönlich in Downtown abgeholt werden. [TAX3_2:TAXIWA3] Ok, verstanden. Wagen 13 Ende. [TAX3_3:TAXIWA3] Hmmm, keine Spur von Mercedes... [TAXW3_3:TAXIWA3] ~g~Schalte das Taxi des Anführers aus! [TAXW3_2:TAXIWA3] ~g~Halte durch, bis die Zeit abgelaufen ist. [TAX_AS1:TAXIWA3] TAXIUNTERNEHMEN ERWORBEN [TAX_AS2:TAXIWA3] ~g~Kaufman-Taxis generiert nun bis zu $~1~ Einkünfte. Hol dir das Geld regelmãßig ab. [TAX3_4:TAXIWA3] Wird Zeit, dass der Schutzengel von Kaufman-Taxis eine vor den Latz kriegt! [TAX3_5:TAXIWA3] Hey, Freundchen, dir zieh ich das Fell über die Ohren! { reVC updates } { new languages } [FEL_JAP] JAPANISCH [FEL_POL] POLNISCH [FEL_RUS] RUSSISCH { new display menus } [FET_GFX] GRAPHICS SETUP [FED_MIP] MIP MAPPING [FED_AAS] ANTI ALIASING [FED_FIL] TEXTURE FILTERING [FED_BIL] BILINEAR [FED_TRL] TRILINEAR [FED_WND] WINDOWED [FED_FLS] FULLSCREEN [FEM_CSB] CUTSCENE BORDERS [FEM_SCF] SCREEN FORMAT [FEM_ISL] MAP MEMORY USAGE [FEM_LOW] LOW [FEM_MED] MEDIUM [FEM_HIG] HIGH [FEM_2PR] PS2 ALPHA TEST [FEC_FRC] FREE CAM { Linux joy detection } [FEC_JOD] DETECT JOYSTICK [FEC_JPR] Press any key on the joystick of your choice that you want to use on the game, and it will be selected. [FEC_JDE] Detected joystick { mission restart } [FET_RMS] MISSION WIEDERHOLEN [FESZ_RM] WIEDERHOLEN? [FED_VPL] VEHICLE PIPELINE [FED_PRM] PED RIM LIGHT [FED_RGL] ROAD GLOSS [FED_CLF] COLOUR FILTER [FED_WLM] WORLD LIGHTMAPS [FED_MBL] MOTION BLUR [FEM_SIM] SIMPLE [FEM_NRM] NORMAL [FEM_MOB] MOBILE [FED_MFX] MATFX [FED_NEO] NEO [FEM_PS2] PS2 [FEM_XBX] XBOX [FEC_IVP] INVERT PAD VERTICALLY [FEM_NON] NONE [FEC_DS2] DUALSHOCK 2 [FEC_DS3] DUALSHOCK 3 [FEC_DS4] DUALSHOCK 4 [FEC_360] XBOX 360 CONTROLLER [FEC_ONE] XBOX ONE CONTROLLER [FEC_TYP] GAMEPAD TYPE [FET_AGS] GAMEPAD SETTINGS [FEM_AUT] { aspect ratio related } AUTO [DUMMY] THIS LABEL NEEDS TO BE HERE !!! AS THE LAST LABEL DOES NOT GET COMPILED ================================================ FILE: utils/gxt/italian.txt ================================================ { New strings are at the bottom of file. Do not change the order of strings. You can fix the typos of existing translation but please refrain from unnecessary edits like rephasing because you think it suits better for your taste. } [IN_VEH] ~g~Ehi! Torna nel veicolo! [HEY] ~g~Non procedere da solo, stai insieme ai tuoi compagni! [HELP3] Ricorda che puoi eseguire uno scatto solo per brevi tratti. [HELP4_D] Sposta la ~h~levetta analogica destra~w~ verso l'alto per ~h~accelerare~w~. [HELP5_D] Sposta la ~h~levetta analogica destra~w~ verso il basso per ~h~frenare~w~ o, se il veicolo è fermo, per inserire la ~h~retromarcia~w~. [HELP7_A] Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ con il fucile di precisione. [HELP7_D] Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ con il fucile di precisione. [HELP10] Questa stella indica il tuo livello di sospetto. [HELP11] Più stelle hai, maggiore è il tuo livello di sospetto. [HELP13] In alcuni casi potresti dover utilizzare passaggi non segnati sul radar. [TIMER] Questa è una missione a tempo: devi portarla a termine prima che il contatore raggiunga lo zero. [HORN] ~g~Suona il clacson. [NOMONEY] ~g~Ti servono più soldi! [REWARD] RICOMPENSA ~1~$ [M_FAIL] MISSIONE FALLITA! [M_PASS] MISSIONE COMPLETATA! ~1~$ [DEAD] MASSACRATO! [BUSTED] BECCATO! [WEATHE1] FORZA TEMPO SOLEGGIATO [WEATHE2] FORZA TEMPO MOLTO SOLEGGIATO [WEATHE3] FORZA TEMPO NUVOLOSO [WEATHE4] FORZA TEMPO PIOVOSO [WEATHE5] FORZA TEMPO NEBBIOSO [WEATHE6] TEMPO NORMALE [NUMBER] ~1~ [LOADCAR] CARICAMENTO VEICOLO... (PREMI L1 PER ANNULLARE) [CARSOFF] Veicoli disabilitati. [CARS_ON] Veicoli abilitati. [TEXTXYZ] Scrittura coordinate sul file... [CHEATON] Modalità trucchi attivata [CHEATOF] Modalità trucchi disattivata [IMPORT1] Esci fuori e aspetta il tuo veicolo. [PAGEB11] Lanciafiamme depositato nel nascondiglio [WANT_A] Verrai arrestato solo se hai un ~h~livello di sospetto~w~. [WANT_B] Il tuo ~h~livello di sospetto~w~ è rappresentato da una riga di stelle nell'angolo superiore destro dello schermo. [WANT_C] Adesso hai un ~h~livello di sospetto~w~ pari a uno... [WANT_D] due... [WANT_E] tre... [WANT_F] Man mano che il tuo ~h~livello di sospetto~w~ aumenta, attirerai l'attenzione di forze dell'ordine sempre più potenti. [WANT_G] Se vieni ~h~'beccato'~w~, verrai portato alla più vicina stazione di polizia. [WANT_H] I poliziotti prenderanno tutte le tue armi e parte dei tuoi risparmi come bustarella. [WANT_I] Qualsiasi missione stavi affrontando verrà considerata come fallita. [WANT_J] Scoprirai alcuni modi per ridurre il tuo livello di sospetto procedendo nel gioco. [WANT_K] Se sei in un veicolo, i ~h~carozzieri~w~ potranno ~h~azzerare il tuo livello di sospetto~w~. [HEAL_B] Quando sei ~h~'massacrato'~w~, verrai trasportato al più vicino ospedale. [HEAL_C] Perderai tutte le tue armi e i dottori prenderanno parte dei tuoi risparmi per rimetterti in sesto. [HEAL_E] Scoprirai alcuni modi per curarti o per proteggerti dagli attacchi procedendo nel gioco. [SAVE1] Passa attraverso la corona per ~h~salvare la partita~w~. Non puoi salvare durante una missione. [SAVE2] Qualsiasi veicolo parcheggiato nel garage verrà salvato insieme alla partita. [AMMU] Entra in Ammu-Nation per comprare un'arma. [R_TIME] TEMPO DI GARA: [PROP_1] Non hai abbastanza soldi per comprare questa proprietà. [PROP_2] Non puoi comprare una proprietà mentre sei in missione. [IND_ZON] Vice City Beach [COM_ZON] Vice City Mainland [BEACH1] Ocean Beach [BEACH2] Washington Beach [BEACH3] Vice Point [GOLFC] Leaf Links [STARI] Starfish Island [DOCKS] Porto [HAVANA] Little Havana [HAITI] Little Haiti [PORNI] Prawn Island [DTOWN] Downtown [VICE_C] Vice City [A_PORT] Escobar International [JUNKY] Discarica [PISTOL] Pistola [PYTHON] .357 [UZI] Uz-1 [TEC9] Tec 9 [M4] M4 [INGRAM] Mac [MP5] MP [RUGER] Kruger [SNIPE] Fucile di precisione [GRENADE] Granate [SHOTGN1] Fucile a pompa [SHOTGN2] S.P.A.S. 12 [SHOTGN3] Fucile a canne mozze [ARMOUR] Giubbotto antiproiettile [LASER] .308 Fucile di precisione [BASEBAT] Mazza da baseball [HAMMER] Martello [SCREWD] Cacciavite [CLEVER] Mannaia [MACHETE] Macete [KNIFE] Coltello [KATANA] Katana [CHAINSA] Motosega [G_COST] ~1~$ [CAR_1] Ambulanza [MALIBU] Il club Malibu [MANSION] Villa di Diaz [TMANS] Chez Tommy [STRIP] Il club Pole Position [MALL1] North Point Mall [BANKINT] El Banco Corrupto Grande [RANGE] Poligono di tiro [POL_HQ] Polizia di VC [INT_B] Un vecchio amico [INTB_1] ~g~Vai all'ufficio degli avvocati. [LAW_1] Il party [LAW_2] Rissa nel vicolo [LAW_3] Furia sulla giuria [LAW_4] Sommossa [COL_1] Fottuto traditore [COL_2] Sparatoria al Mall [COL_3] Angeli custodi [COL_4] Sissignore, signore! [COL_5] Tutti sul ponte! [COK_1] L'inseguimento [COK_2] Phnom Penh '86 [COK_3] La barca più veloce [COK_4] Domanda e offerta [KENT_1] Il braccio della morte [ASS_1] Eliminazione [BUD_1] Estorsione [BUD_2] Rissa al bar [BUD_3] Cop Land [CAP_1] Fuori l'esattore [FIN_1] Tieniti stretto gli amici... [BANK_1] Senza via di fuga? [BANK_2] Il tiratore [BANK_3] L'autista [BANK_4] La rapina [CNT_1] Sfornare verdoni [CNT_2] Colpire il corriere [PORN_1] Giro di reclutamento [PORN_2] Il dodo del dildo [PORN_3] La foto di Martha [PORN_4] Riflettori sul punto G [TAX_1] Taxi di Kaufman [TAXI_1] VIP [TAXI_2] Rivalità amichevole [TAXI_3] Cabmaggedon [ICE_1] Distribuzione [TEX_1] Ferro quattro [TEX_2] Due in un colpo [TEX_3] Il demolitore [PHIL_1] Corriere delle armi [PHIL_2] Broda e Saigon [BIKE_1] Due ruote d'acciaio [BIKE_2] Bollire la pentola [BIKE_3] Valla a prendere! [ROCK_1] Il succo dell'amore [ROCK_2] Killer psicotico [ROCK_3] Giro pubblicitario [ROCK_4] Love Fist! [HAT_1] Le polveri della zia [HAT_2] Bombardamento! [HAT_3] Tutto di nascosto [CUB_1] Gara sull'acqua [CUB_2] Carne da cannone [CUB_3] Battaglia navale [CUB_4] Voodoo a doppio taglio [JOB_1] Morte per strada [JOB_2] Elimina la moglie [JOB_3] Incidente... [JOB_4] L'ultima partenza [JOB_5] Risoluzione [ANSWER] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per rispondere al cellulare. [MOB_01A] Ciao vecchio mio! Sono Paul. Potrei avere delle informazioni per te, ma ti voglio parlare in privato. [MOB_01B] Mi sto godendo un aperitivo al Malibu. [MOB_01C] Credo che mi dovrai un favore o due dopo questo, carino. Ci vediamo dopo. [MOB_02A] Ssssnniiiiffffff! Ehi! Tommy? Ciao, Tommy! [MOB_02B] Ci sono dei problemi alla stamperia. Faresti meglio ad andare a controllare. [MOB_02C] Un qualche tipo di casino. C'è qualcosa che non va. Devo andare. [MOB_03A] Mr. Vercetti? Ho qui davanti a me un pezzo di carta che afferma [MOB_03B] che lei si è preso carico dei debiti di BJ's Auto. [MOB_03C] Con l'improvvisa scomparsa di BJ, non ho altra scelta [MOB_03D] che ritenerla responsabile delle sue debolezze finanziarie. [MOB_03E] Finché questo debito non sarà ripagato, [MOB_03F] dovrà considerare le strade di Vice City non molto amichevoli. [MOB_04A] Come butta, amico? [MOB_04B] Ascolta Tommy, mi sono dimenticato di dirti che avremo bisogno di qualche gorilla extra per il concerto. [MOB_04C] C'è una banda di motociclisti e sarebbero una perfetta pubblicità. [MOB_04D] Occupatene tu e ti farò accedere al back stage dello spettacolo, OK? [MOB_05A] Ehi, sono Mitch! Ottimo lavoro, Tommy: è bello riavere indietro la mia vecchia bambina. [MOB_05B] Dì a Mr. Kent Paul che avrà la sorveglianza per lo spettacolo. [MOB_05C] Hai la mia parola. [MOB_05D] Adesso tieniti fuori dai guai. [MOB_06A] Tommy, si è parlato molto di te, ragazzo. [MOB_06B] Pensavo ti servisse un po' di calore, per cui la zia Poulet ti ha preparato una bella zuppa, hmmm? [MOB_06C] Passa a trovarmi in cucina ogni tanto, OK Tommy? [MOB_08A] Ehi, Tommy, ho pensato che ti potessero servire dei consigli. [MOB_08B] Quando hai un'operazione in corso, dovrai passare a ritirare i soldi della settimana. [MOB_08C] Lascia pensare ai ragazzi che abbiano il controllo del posto e loro cercheranno di ridurne i profitti, OK? [MOB_08D] Ehi Ken, so come far funzionare le cose, OK. [MOB_08E] OK, OK! Lo so che lo sai. Lo so. Stavo solo... [MOB_08F] Ti stavo solo ricordando che lo so, cioè che tu sai che io so. [MOB_08G] Solo per ricordartelo! [MOB_08H] Sì Ken, va bene... [MOB_09A] Ehi Leo! Ho del lavoro per te! [MOB_09B] Non sono Leo! [MOB_09C] Ehi, se Leo scopre che hai il suo telefono, ti farà a pezzi! [MOB_09D] Forse Leo è già morto. Forse ho ucciso Leo e ho preso il suo telefono: non ci hai pensato, idiota? [MOB_09E] Hai ucciso Leo? Devi avere due grandi cojones. Vuoi lavorare per me? [MOB_09F] Passa dal bar di mio padre a Little Havana e ne possiamo parlare. [MOB_10A] Tommy! Senti, devo chiederti un favore. [MOB_10B] Steve! Come va il film? [MOB_10C] Bene, bene. Senti ho, ehm, abbiamo bisogno di una scena di inseguimento, ma siamo a corto di denaro. [MOB_10D] Ho lasciato qualche macchina in città. Sono certo che sai cosa fare. [MOB_10E] OK Steve, terrò un occhio aperto. Ci vediamo! [MOB_11A] Ciao figliolo, t'ho chiamato per darti un consiglio. [MOB_11B] Ciao Avery. Come butta? [MOB_11C] Ci sono un sacco di opportunità in questa città se possiedi le proprietà giuste, mi segui? [MOB_11D] Tutto chiaro... [MOB_11E] Volevo dirti di tenere gli occhi aperti: potresti trovare l'occasione giusta per far soldi. Ci becchiamo! [MOB_11F] Ciao, Avery. [MOB12_A] Ehi Tommy! Sono Avery! Ascolta: sono piuttosto incasinato al momento [MOB12_B] e un mio rappresentante ha bisogno di un accompagnatore fino a Gator Keys. [MOB12_C] Sto cercando di acquistare del terreno da quelle parti, per cui mando qualcuno in avanscoperta. [MOB12_D] Mi faresti un favore? Potresti controllare che arrivi a destinazione tutto intero? [MOB12_E] Certo, nessun problema Avery. Dove vuoi che lo passi a prendere? [MOB12_F] Sta finendo un lavoro presso il cantiere. Gli ho detto che saresti passato lì. [MOB12_G] Nessun problema. Ci vediamo, Avery. [MOB13_A] Vercetti? VERCETTI!!! Maledizione, mi devi aiutare! [MOB13_B] Mr. Moffat? Come sta la famiglia? [MOB13_C] Brutto maledetto, BASTARDO! Mi hai sentito? [MOB13_D] Beh, è stato un piacere chiacchierare... [MOB13_E] ASPETTA! Aspetta Vercetti... Tommy, posso chiamarti Tommy? [MOB13_F] Siamo entrambi professionisti, vero? Sono certo che sai riconoscere un affare, vero? [MOB13_G] Non ho tempo da perdere, arriva al punto. [MOB13_H] SOLDI. I soldi sono il punto! [MOB13_I] Sono fuggito nuovamente da quei maledetti, ma non ci metteranno molto a trovarmi... Pensano si tratti di uno stupido gioco! [MOB13_J] Sono a un telefono pubblico da qualche parte in questo posto di merda. [MOB13_K] Aiutami a scappare prima che mi prendano e... e... oddio... [MOB13_L] Beh, sono impegnato per i prossimi... [MOB13_M] No! Non fare lo stronzo con me, abbi cuore! Nessuno dovrebbe fare una... una cosa simile! [MOB13_N] Sono in ginocchio, Tommy: nella polvere a chiederti pietà... [MOB13_O] Beh, forse potrei passare da quelle parti, vediamo se riesco a trovarti... [MOB13_P] Oddio, stanno arrivando! Ti supplico, muoviti, muoviti! [MOB_14A] Ehi, ciao Tommy: vedrai che mi adorerai! [MOB_14B] Un piccolo uccellino mi ha detto che la divisione SWAT di Vice City ha un deposito presso un'importante banca, [MOB_14C] dove tengono tutte le bustarelle intascate nel corso degli anni, [MOB_14D] un po' come una pensione per gli anni a venire. [MOB_14E] Logicamente, se questa informazione dovesse aiutarti a ottenere i soldi, [MOB_14F] immagino che ti sentiresti obbligato a passarmene una parte... [MOB_14G] Me lo ricorderò, grazie Kent. [MOB_14H] Sono Paul. Vengo dal Kent, vicino a Londra, cretino. [MOB_14I] Le mie conoscenze di geografia non sono mai state un gran che. [MOB15_A] Tommy, amico, sono Paul, dal Kent, [MOB15_B] un paio di squinzie hanno il tuo nome scritto dappertutto giù al Malibu. [MOB15_C] Di cosa stai parlando? [MOB15_D] Squinzie. Passere. Chiaro no? Strafighe. E anche ben messe, non credo fossero mignotte... [MOB15_E] Dovresti venire a dare un'occhiata. [MOB16_A] Tommy, sono Paulo, que pasa amigo? [MOB16_B] Che cosa vuoi Paul: non sono interessato a vestiti di marca taroccati. [MOB16_C] Molto divertente, amico, ma lo sai che non tratto merce di seconda classe. [MOB16_D] Nah, ti ho chiamato per sapere se potevo avere una parte nei tuoi film, [MOB16_E] quando ero in Inghilterra ho fatto un sacco di cose, sai? [MOB16_F] Ho attributi più forniti di te, amico. [MOB16_G] Paul, grazie per l'offerta, lo terrò presente. [MOB16_H] Davvero, non scordarti di me dopo tutto quello che ho fatto per te. [MOB16_I] È proprio ciò che sto cercando di dimenticare... [MOB17_A] Tommy Vercetti: come va, Mr. Pezzo Grosso? [MOB17_B] Ho saputo le tue novità: un vero giocatore in città, eh... [MOB17_C] Paul, sei ubriaco. [MOB17_D] No, idiota. Non sono ubriaco. [MOB17_E] Solo un paio di bicchierini e qualche pillola: non dormo da un paio di giorni, sai... [MOB17_F] Comunque, niente prediche. [MOB17_G] Non sono uno stupido. Chi ti ha portato in questa città? Chi? Io, ecco chi. [MOB17_H] Davvero? [MOB17_I] Davvero! [MOB17_J] Paul, sta' buono. Sono stato occupato, non fare l'idiota. [MOB17_K] Non sono un idiota, chiaro? Questo è ciò che hanno detto in riformatorio. [MOB17_L] Stai forse cercando guai, amico? Beh, stai per trovarli! [MOB17_M] Tommy, amico. Per favore. Sei la mia più grande speranza! Per favore, non ridere di me. [MOB17_N] Paul, cerca di dormire, davvero. [MOB18_A] Tommy, sono Paulo, come butta? Bene amico, comunque, volevo farti una chiamata. [MOB18_B] Ossignore, amico, non crederai mai che razza di strafiga ho appena incontrato. [MOB18_C] Non so esattamente chi fosse, ma passeggiava per Little Havana. [MOB18_D] Ha detto che si chiamava Mercedes o qualcosa del genere. Oddio, dovresti proprio vedere questa gnocca. [MOB18_E] Riuscirebbe a succhiare via la mina da una matita. Mi ha detto che ero il migliore che avesse mai avuto e via dicendo. [MOB18_F] Dovresti farci un giro. Ci vediamo! [MOB19_A] Tommy V, sono KP. Kent Paul. Gira voce che ti vogliano fare a pezzi. [MOB19_B] Tieni gli occhi bene aperti, amico e ricorda: io non ti ho detto niente al riguardo. [MOB_20A] Ehi Tommy, sono Paul. Ho appena sentito che sei stato un ragazzaccio cattivo. [MOB_20B] Qualcuno si è offeso per il tuo comportamento da grand'uomo, e adesso è davvero arrabbiato. [MOB_20C] Beh, non dire che non ti ho avvertito: vantarsi è un gioco pericoloso, amico. [MOB_20D] Comunque, ho anche sentito che c'è una taglia sulla tua testa e che qualcuno ti ha già preso di mira, [MOB_20E] per cui guardati la schiena e ricordati di me, amico. [MOB71_A] Tommy, Tommy, sono Cortez. Que pasa? [MOB71_B] La situazione è interessante. A lei come va? [MOB71_C] Tommy, qui è sempre una battaglia. Scusami per la battuta scontata, ma abbiamo sventato un altro golpe. [MOB71_D] La gente non smette mai di pretendere di più. [MOB71_E] Da quando sono tornato da Vice City, abbiamo avuto tre rivoluzioni e quattro golpe falliti. [MOB71_F] Fortunatamente, sono stato promosso ogni volta. [MOB71_G] Ti volevo chiedere di Mercedes. [MOB71_H] Sì? Cosa vuole sapere? [MOB71_I] Oh Tommy, ho sentito tutte queste storie e non so cosa pensare. [MOB71_J] Forse tutti si divertono a umiliarmi. [MOB71_K] Forse pensa di poter fare ciò che vuole ma dimmi, Tommy: è tutto vero? [MOB71_L] ma dimmi, Tommy: è tutto vero? [MOB71_M] Che cosa? [MOB71_N] Le storie che ho sentito: sta pensando davvero di diventare avvocato? [MOB71_O] Oh Tommy, che vergogna. Noi Cortez siamo una famiglia all'antica e non permetteremo mai che una di noi diventasse avvocato. [MOB71_P] Ti prego, dimmi che non è vero. Non credo resisterei al colpo. [MOB71_Q] Colonnello, le assicuro che Mercedes non diventerà mai un avvocato. Non si preoccupi. [MOB71_R] Grazie Tommy, la vergogna sarebbe schiacciante. Lei è una vera dama, non una parassita, capisci? [MOB71_S] Capisco, Colonnello. [MOB71_T] Comunque, Tommy, devi scusarmi: è appena arrivato il nuovo ministro degli interni. [MOB71_U] Qualche anno fa, ho ucciso suo padre in un golpe fallito, per cui devo essere gentile. Buona giornata, amico. [MOB22_A] Tommy, ti stai rivelando molto utile, amico. [MOB22_B] Grazie, Cortez. Cosa mi dice del nostro accordo? [MOB22_C] Tommy, sto lavorando senza sosta nel tentativo di raggiungere il fondo di questa fossa piena di bugie e inganni, [MOB22_D] hai la mia parola al riguardo, ma nel frattempo, [MOB22_E] ti prego di accettare i più sinceri ringraziamenti da parte della gente per cui stai lavorando. [MOB_25A] Tommy, sono Cortez. I Francesi mi stanno causando tutti i problemi possibili. [MOB_25B] Maledetti ipocriti. Hanno passato secoli a derubare la povera gente e adesso osano chiamare me ladro! [MOB_25C] Avrò bisogno del tuo aiuto il prima possibile. [MOB_25D] Sbrigati, Tommy: ho bisogno di te. Odio questi dannati Francesi. [MOB_26A] Ehi, Tommy? [MOB_26B] Sì? [MOB_26C] Sono Baker. Volevo dirti che ho davvero apprezzato lo show. [MOB_26D] Io e i ragazzi vogliamo ringraziarti e ricordarti [MOB_26E] che hai tutto il nostro rispetto. Buona giornata, continua così. [MOB_29A] Salve, parlo con Mr. Tommy Vercetti? [MOB_29B] Sì. [MOB_29C] Beh, ho sentito dire che sei tu l'uomo da chiamare in caso di infestazione da parassiti. [MOB_29D] Forse... [MOB_29E] Beh, ho in corso un'infestazione davvero pesante: Haitiani da tutte le parti. [MOB_29F] Mi chiamo Umberto Robina e vorrei incontrarmi con te al Cafe Robina il prima possibile, [MOB_29G] perché questa volta i fottuti Haitiani sono andati troppo oltre. [MOB_29H] Test [MOB_30A] Tommy, sono Umberto Robina. [MOB_30B] Come va al caffè? [MOB_30C] Oh, meraviglioso. Incredibile, Tommy, incredibile. Niente perdenti, Tommy, solo veri uomini, sai no, e donne stupende. [MOB_30D] Comunque, volevo dirti che per tutti noi adesso sei un vero Cubano. [MOB_30E] Hai provato di essere all'altezza. Hai provato di avere due grandi cojones. [MOB_30F] Err... grazie, Umberto. Nessuno mi ha detto una cosa simile da quando ho lasciato la galera. Ci becchiamo in giro. [MOB_33A] Tommy, sono Phil: smettila di perdere tempo con le vaccate e ascoltami, intesi? [MOB_33B] Bene. Ho un bel po' di broda bella forte quasi a perfetta fermentazione e mi chiedevo se ne volevi assaggiare un po'. [MOB_33C] Ti assicuro, Tommy, che se ami bere, o se devi rimuovere della vernice, questa è la roba che fa per te. [MOB_33D] Per me è stata una bomba, anche se non ci vedo più bene da un occhio. Ti aspetto, ciao. [MOB_34A] Tommy, mi è davvero piaciuto lavorare con te. Non mi divertivo così dalle scorrerie in Vietnam, amico. [MOB_34B] Se mai ti servisse qualcosa, chiamami, capito? [MOB_34C] Ricordo sempre quelli coi quali ho prestato servizio, [MOB_34D] e sono sicuro di poterti dare una mano, chiaro? [MOB_35A] Tommy, la ferita si sta rimarginando bene. È divertente: [MOB_35B] ho combattuti in sei campi di battaglia e sono sempre uscito senza un graffio... e adesso questo! [MOB_35C] Phil senza un braccio. Comunque, ho una abbondante selezione di armi a una mano, per cui almeno non sarò disarmato! [MOB_35D] Comunque, figliolo, lasciamo stare le stronzate sentimentali e vedi di scolarti qualcosa da parte mia! [MOB_36A] Tommy, sono Phil. Ti volevo ringraziare di essere stato sul campo, figliolo. [MOB_36B] Maledetti Viet, non perdono occasione per farti un attentato... [MOB_36C] Comunque, la ferita sta guarendo: adesso non mi sentirò più in colpa quando riceverò il mio consueto assegno per disabili. Grazie, amico. [MOB_40A] Ehi, Tommy, sono Sonny. Come va l'abbronzatura? [MOB_40B] Non ho preso il sole. [MOB_40C] Beh, non hai preso neanche i miei soldi, per cui mi chiedevo: [MOB_40D] che cosa stai combinando? Dimmi un po', Tommy, che cosa fai? [MOB_40E] Sto cercando i soldi, Sonny, non ti preoccupare. [MOB_40F] Invece mi preoccupo, Tommy, è il mio stile, [MOB_40G] poiché ho sempre dei problemi con gente che non ama rispettare i patti. [MOB_40H] Non diventare inaffidabile, Tommy, fallo per me... [MOB_40I] Fai un favore a entrambi. Non vedo l'ora di ricevere buone nuove. [MOB_41A] Tommy, ti ricordi di me? [MOB_41B] Ehi, Sonny. [MOB_41C] Sì, sono proprio io, Sonny. Siamo vecchi amici, [MOB_41D] e tu non mi scrivi mai, non mi chiami mai. Non vuoi più essere mio amico? [MOB_41E] Sono stato occupato a mettere le cose a posto. Non mi hai dato un gran che di aiuto da queste parti. [MOB_41F] Ah, è questa la mia colpa? Beh, ho sentito che sei stato molto occupato. [MOB_41G] Occupato a uccidere i baroni della droga. Occupato a prendere il potere. [MOB_41H] Non dimenticarti di noi, Tommy, poiché, te lo assicuro, io non mi sono dimenticato di te. [MOB_42A] Tommy. [MOB_42B] Sonny. [MOB_42C] Hai di sicuro dei problemi di udito, per cui proverò ancora... [MOB_42D] Tommy, dove sono i maledetti soldi, dov'è la maledetta roba e dov'è la mia parte dei tuoi ultimi guadagni? [MOB_42E] Tommy, mi stai facendo fare la figura dell'idiota, e io non sto ridendo. [MOB_43A] Tommy, Tommy, Tommy, avevo Sonny al telefono... OK, sei con me? [MOB_43B] Non so te, ma so che qualcuno sta minacciando di morte la mia famiglia, [MOB_43C] il che mi sta davvero facendo cagare sotto. Che cosa intendi fare? [MOB_43D] Stai calmo, Ken, andrà tutto bene. [MOB_43E] Io SONO calmo, calmo quanto un uomo in pericolo di vita! [MOB_43F] Ken, ora come ora ho altre cose per la testa, [MOB_43G] ci occuperemo di Forelli al momento giusto. [MOB_43H] Io sono calmo. Non ti sembro calmo? Forse è la morte incombente che mi sta facendo tremare la voce. [MOB45_A] Tommy, dobbiamo parlare di un po' di cose. [MOB45_B] C'è qualche problema, Lance? [MOB45_C] Si tratta di te, amico, non mi sembra di ricevere la fetta che merito. [MOB45_D] E inoltre, mi hai imbarazzato davanti ai ragazzi. Non lo posso permettere. [MOB45_E] Lance, non è andata così. Hai commesso degli errori. [MOB45_F] Tommy, non sono il tuo fattorino. Non sono un tuo scagnozzo. [MOB45_G] Lance, non fare casini e non avremo alcun problema. Se faccio un errore, me la puoi far pagare in qualsiasi momento. [MOB45_H] Tommy, ho fatto tutto per te e mi tratti come un idiota. Non farlo. [MOB45_I] Lance, non ti causerò problemi e non ti pugnalerò alle spalle, promesso. [MOB45_J] Rilassati. La cosa è già abbastanza difficile senza che tu ti metta a fare il tenero con me. [MOB45_K] Fidati: mi hai sentito, mi hai sentito? [MOB45_L] Ti ho sentito, ma Tommy, non sopporterò ancora per lungo. [MOB45_M] Lance, non fare così: adesso sono io ad avvertirti. [MOB45_N] Mi hai sentito? Rilassati, prenditi qualche giorno e poi ne riparliamo, OK? [MOB46_A] Tommy, sono Lance. [MOB46_B] Sì? [MOB46_C] Sono felice di sentirti Lance. Forza, stai tranquillo, stai tranquillo. [MOB46_D] Sono nel mezzo di un problema: ho bisogno di te. [MOB46_E] Niente, giusto per dire. Tommy, ce ne possiamo occupare. [MOB46_F] Io e te, nessun problema. Capisci ciò che intendo? [MOB46_G] Dobbiamo proprio farlo, se no siamo entrambi morti, Lance. [MOB46_H] Siamo andati troppo oltre, ma grazie della chiamata. Ci sentiamo più tardi. [MOB_47A] Tommy, sono Lance. Abbiamo un problema serio. Vieni qui in fretta. [MOB52_A] Ehi, Leo, sembra ci sia un compratore per la roba di Diaz. [MOB52_B] Devi fargli uno squillo, ragazzo, e preparare l'accordo, chiaro? [MOB52_C] Dove ti trovi adesso? [MOB52_D] Stai bene Leo? Mi sembri un po' strano... [MOB52_E] Dimmi dove ti trovi. [MOB52_F] Chi diavolo sei? Passami subito Leo! [MOB52_G] Leo se ne è andato per un po' e adesso sono io in carica. [MOB52_H] Fottiti, stronzo! [MOB54_A] Ciao Tommy! [MOB54_B] Ciao Mercedes, come butta? [MOB54_C] Ho un nuovo appartamento in Vice Point... [MOB54_D] magari un giorno di questi potresti farci un salto. [MOB54_E] Mi piacerebbe, ci sentiamo. [MOB55_A] Tommy, sono io. [MOB55_B] Ciao Mercedes. [MOB55_C] Tommy, sono così annoiata: perché non ci divertiamo un po' insieme? [MOB55_D] Cosa intendi dire? [MOB55_E] Beh, lo so che sei occupato a combattere, uccidere e corrompere gente, [MOB55_F] ma io ho voglia di divertirmi un po'. Non ti scordare di me, capito? [MOB56_A] Tommy, ho sentito che hai ucciso Ricardo Diaz. [MOB56_B] C'è stato uno sfortunato incendio nella sua villa. [MOB56_C] Credo sia morto bruciato nella sua camicia in acrilico. [MOB56_D] Tommy, sono così fiera di te. Lo sapevo che eri un vero uomo. [MOB56_E] Era un inutile idiota senza testa: mi rendi orgogliosa di essere tua amica. [MOB56_F] No, lo so che sei impegnato a prendere il possesso di questa città, [MOB56_G] ma non dimenticarti di me, capito? [MOB57_A] Sono Mercedes. Non ti amo più, Tommy. [MOB57_B] Proprio no, onestamente. Tu non sei più gentile con la tua Mercedes. [MOB57_C] Non mi tratti più come una signora: mi ignori e io ti odio. [MOB57_D] Insisto che tu venga a trovarmi immediatamente! [MOB58_A] Tommy. [MOB58_B] Ehi Mercedes. [MOB58_C] Ehi sì, Mr. Tipo Duro. Sono davvero arrabbiata con te, Tommy. [MOB58_D] Non farmi più passare le serate con Jezz Torrent. [MOB58_E] È davvero patetico. A metà delle scale ha iniziato a piagnucolare per il suo cane [MOB58_F] che è morto quando aveva sette anni e a ripetere che la mamma non lo ha mai amato. [MOB58_G] E Tommy, indossa una parrucca e un reggiseno quando è in intimità. [MOB58_H] Non sono molto felice di te! [MOB59_A] Ooh Tommy, sono Mercedes. [MOB59_B] Volevo solo dirti che mi sono divertita così tanto sul set del film. [MOB59_C] Se hai qualcos'altro del genere, fammelo sapere. [MOB59_D] Lo penso davvero. Ho sempre desiderato fare l'attrice. [MOB59_E] Credo di aver imparato molto sull'approccio drammatico. [MOB59_F] È così illuminante! Grazie, grazie! Spero di vederti presto! Adios. [MOB_99] Raggiungi il telefono pubblico. [MOB_98] Raggiungi il telefono pubblico. [MOB_97] Raggiungi il telefono pubblico. [MOB_96] Raggiungi il telefono pubblico. [MOB_95] Raggiungi il telefono pubblico. [A_TIME] +~1~ secondi [F_RANGE] ~g~Sei fuori dal raggio d'azione della radio. Avvicinati alla stazione dei pompieri! [DODO_FT] Hai volato per ~1~ secondi! [GA_8] Usa il detonatore per attivare la bomba. [GA_10] Non male. Eccoti ~1~$ [GA_11] Ne abbiamo già una. A noi non serve! [GA_12] Bomba innescata [GA_13] Un furto da manuale. Completa la lista e ci sarà un bonus per te. [GA_14] Hai portato tutte le macchine. BENE! Eccoti un bonus... [GA_15] Spero che ti piaccia il nuovo colore. [GA_16] La riverniciatura è gratuita. [GA_19] Non siamo interessati a questo modello. [GA_20] Di questo modello ne abbiamo in abbondanza. Mi dispiace, non siamo interessati. [CHASE] Massima attenzione dei media [CHASE1] Ignoto [CHASE2] Noioso [CHASE3] Appena interessante [CHASE4] Giornale locale, pagina 7 [CHASE5] Giornale locale, prima pagina [CHASE6] Vice Courier, 1 colonna [CHASE7] Vice Courier, prima pagina [CHASE8] TV locale, 3 AM [CHASE9] TV locale, news [CHASE10] TV locale, servizio in diretta [CHASE11] UFA Today, pagina 12 [CHASE12] UFA Today, pagina 4 [CHASE13] Foto in UFA Today [CHASE14] TV nazionale, 4 AM [CHASE15] TV nazionale, news [CHASE16] TV nazionale, servizio in diretta [CHASE17] Avvenimento internazionale [CHASE18] Crisi nazionale [CHASE19] Crisi internazionale [CHASE20] Evento mondiale [CHASE21] Roba da leggenda [CR_1] La gru non può alzare questo veicolo. [PU_MONY] Non hai abbastanza soldi. [CO_ALL] Li hai presi tutti. Eccoti un piccolo extra... [FEM_ON] ON [FEM_OFF] OFF [FEM_YES] Sì [FEM_NO] No [FEM_RET] Riprova [FEC_NA] ND [FEC_CWL] Scorri armi a sinistra [FEC_CWR] Scorri armi a destra [FEC_LOF] Guarda avanti [FEC_TAR] Bersaglio [FEC_MOV] Movimento [FEC_CAM] Modalità visuale [FEC_PAU] Pausa [FEC_ENV] Sali sul veicolo [FEC_JUM] Salta [FEC_ATT] Attacca o fuoco con arma [FEC_RUN] Corri [FEC_FPC] Visuale in prima persona [FEC_LL] Guarda a sinistra [FEC_LB] Guarda indietro [FEC_LR] Guarda a destra [FEC_HOR] Clacson [FEC_VES] Comandi veicolo [FEC_BRA] Freno o retromarcia [FEC_HAB] Freno a mano [FEC_CAW] Arma vettura [FEC_ACC] Accelera [FEC_CCF] Configurazione : [FEC_CF1] Config 1 [FEC_CF2] Config 2 [FEC_CF3] Config 3 [FEC_CF4] Config 4 [FEC_CDP] Schermata controller : [FEC_ONF] A piedi [FEC_INC] In macchina [FEC_VIB] Vibrazione : [FEL_ENG] Inglese [FEL_FRE] Francese [FEL_GER] Tedesco [FEL_ITA] Italiano [FEL_SPA] Spagnolo [FED_DBG] Menu Debug [FED_RID] Reload IDE [FED_RIP] Reload IPL [FED_PAH] Parse Heap [FED_DFL] CTheScripts::DbgFlag [FED_DLS] Big White Debug Light Switched [FED_SPR] Show Ped Road Groups [FED_SCR] Show Car Road Grups [FED_SCZ] Show Cull Zones [FED_DSR] Debug Streaming Requests [FED_SCP] gbShowCollisionPolys [PL_STAT] Statistiche giocatore [PE_WAST] Persone massacrate [PE_WSOT] Persone massacrate da altri [TM_BUST] Arresti subiti [GNG_WST] Membri delle gang massacrati [DED_CRI] Criminali massacrati [PER_COM] Percentuale completata [KGS_EXP] Kg di esplosivo utilizzati [ACCURA] Precisione [ST_WEAP] Budget armi [ST_PROP] Budget proprietà [ST_AUTO] Budget riparazioni e colorazione auto [ST_PHOT] Fotografie scattate [ST_LOAN] Visite degli usurai [ST_STOR] Edifici razziati [ST_MOVI] Stunt nei film [ST_PIZZ] Pizze consegnate [ST_GARB] Spazzatura raccolta [ST_ICEC] Gelati venduti [TOP_SHO] Miglior punteggio al poligono [SHO_RAN] Graduatoria al poligono [SEAGULL] Gabbiani abbattuti [PROPOWN] Proprietà posseduti [ST_TIME] Tempo di gioco [ST_FTIM] Ore di volo [ST_PRAN] Graduatoria pilota [ST_RAN0] Apprendista [ST_RAN1] Navigatore [ST_RAN2] Copilota [ST_RAN3] Pratico [ST_RAN4] Competente [ST_RAN5] Anziano [ST_RAN6] Asso [ST_RAN7] Barone Rosso [ST_DRWN] Pesci sfamati [ST_FASH] Budget abbigliamento [ST_DAMA] Proprietà distrutte [TM_DED] Visite all'ospedale [DAYSPS] Giorni trascorsi nel gioco [NUMSHV] Visite al rifugio [MXCARD] Distanza max salto FOLLE (ft) [MXCARJ] Altezza max salto FOLLE (ft) [MXCARDM] Distanza max salto FOLLE (m) [MXCARJM] Altezza max salto FOLLE (m) [MXFLIP] Numero max ribaltamenti FOLLI [MXJUMP] Numero max rotazioni FOLLI [BUL_FIR] Colpi sparati [BUL_HIT] Colpi a segno [SPRAYIN] Riverniciature [BSTSTU] Migliore acrobazia FOLLE [INSTUN] Acrobazia folle [PRINST] Acrobazia folle perfetta [DBINST] Doppia acrobazia folle [DBPINS] Doppia acrobazia folle perfetta [TRINST] Tripla acrobazia folle [PRTRST] Tripla acrobazia folle perfetta [QUINST] Quadrupla acrobazia folle [PQUINS] Quadrupla acrobazia folle perfetta [NOSTUC] Nessuna acrobazia FOLLE effettuata [NOUNIF] Acrobazie uniche completate [NMISON] Missioni provate [PASDRO] Passeggeri scaricati [MONTAX] Soldi guadagnati in taxi [DAYPLC] Spesa giornaliera polizia [CRIMRA] Livello criminalità: [STPR_1] Malibu [STPR_2] Tipografia [STPR_3] Studio cinematografico [STPR_4] Fabbrica di gelato [STPR_5] Lavaggio auto [STPR_6] Compagnia di taxi [STPR_7] Cantiere navale [SET1EN] Config. 1. Abilitata. [GMSAVE] Salva partita [FEDS_TB] Indietro [FEST_OO] su [FEC_TUC] Comandi torretta [FEC_RS3] Stazioni radio (tasto L3) [FEC_HO3] Clacson (tasto L3) [C_FAIL] Missione Vigilante terminata! [C_ESCP] ~r~Il sospettato è fuggito! [C_VIGIL] BONUS VIGILANTE! [HEAL_A] La tua ~h~salute~w~ è indicata in color arancione in alto a destra sullo schermo. [WRONGCD] Disco errato. Inserisci il disco corretto. [NOCD] Il cassetto del disco è vuoto. Inserisci il disco. [OPENCD] Il cassetto del disco è aperto: è necessario chiuderlo. [CDERROR] Errore di lettura dal DVD Grand Theft Auto: Vice City. [RESTART] Avvio di una nuova partita [GA_3] Niente più sconti. $100 per la riverniciatura! [GA_1] Wow! Non tocco niente di COSÌ caldo! [GA_1A] Torna quando non sarai così occupato... [HELP9_C] Premi il ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile di precisione. [TAXI2] ~r~Tempo scaduto! [PAGEB13] Salute consegnata al nascondiglio [PAGEB14] Adrenalina consegnata al nascondiglio [FESZ_CA] Annulla [FES_NGA] Nuova partita [FES_CAN] Annulla [FESZ_QL] Tutti i progressi della partita attuale non ancora salvati andranno perduti. Vuoi procedere con il caricamento? [FESZ_QD] Vuoi eliminare questa partita salvata? [FESZ_QO] Vuoi procedere con la sovrascrittura di questa partita salvata? [FESZ_QR] Sei sicuro di voler iniziare una nuova partita? Tutti i progressi fatti fino all'ultimo salvataggio andranno perduti. Vuoi procedere? [T4X4_1] 'Parco divertimenti PCJ' [BMX_1] 'Prova del fango' [BMX_2] 'Tracciato di prova' [BMXFAIL] ~r~Non sei riuscito a stabilire un nuovo record! [T4X4_3] 'TENUTA!' [MM_1] 'FOLLIA CONICA' [T4X4_F] ~r~Ti sei ritirato! Troppo difficile per te? [LANDSTK] Landstalker [IDAHO] Idaho [STINGER] Stinger [LINERUN] Linerunner [PEREN] Familiare [SENTINL] Sentinel [RIO] Rio [PATRIOT] Patriot [FIRETRK] Camion dei pompieri [TRASHM] Nettezza urbana [STRETCH] Stretch [MANANA] Manana [INFERNS] Infernus [VOODOO] Voodoo [PONY] Pony [MULE] Mule [CHEETAH] Cheetah [AMBULAN] Ambulanza [FBICAR] FBI Washington [MOONBM] Moonbeam [ESPERAN] Esperanto [TAXI] Taxi [WASHING] Washington [BOBCAT] Bobcat [WHOOPEE] Mr Whoopee [BFINJC] BF a iniezione [HUNTER] Hunter [POLICAR] Polizia [ENFORCR] Volante [SECURI] Securicar [BANSHEE] Banshee [PREDATR] Predator [BUS] Autobus [RHINO] Rhino [BARRCKS] Caserma OL [CUBAN] Cuban Hermes [HELI] Elicottero [DODO] Dodo [COACH] Pullman [CABBIE] Vecchio taxi [STALION] Stallion [RUMPO] Rumpo [RCBANDT] Bandit radiocomandato [ROMERO] Carro funebre di Romero [PACKER] Packer [ADMIRAL] Admiral [SQUALO] Squalo [SEASPAR] Sea Sparrow [PIZZABO] Pizza Boy [GANGBUR] Gang Burrito [TROPIC] Tropic [SPEEDER] Speeder [REEFER] Reefer [FLATBED] Flatbed [YANKEE] Yankee [CADDY] Caddy [ZEBRA] Taxi Zebra [TOPFUN] Top Fun [SKIMMER] Skimmer [PCJ600] PCJ 600 [PHOENIX] Phoenix [FAGGIO] Faggio [FREEWAY] Freeway [RCBARON] Baron radiocomandato [RCRAIDE] Raider radiocomandato [GLENDAL] Glendale [OCEANIC] Oceanic [SANCHEZ] Sanchez [SPARROW] Sparrow [LOVEFIS] Love Fist [COASTG] Coast Guard [DINGHY] Dinghy [HERMES] Hermes [SABRE] Sabre [SABRETU] Sabre Turbo [WALTON] Walton [REGINA] Regina [COMET] Comet [DELUXO] Deluxo [BURRITO] Burrito [SPAND] Spandex Express [MARQUIS] Marquis [BAGGAGE] Baggage Handler [KAUFMAN] Taxi Kaufman [COASTMA] Coastguard Maverick [MAVERIC] Maverick [RANCHER] Rancher [FBIRANC] FBI Rancher [VIRGO] Virgo [GREENWO] Greenwood [HOTRING] Hotring Racer [BLISTAC] Blista Compact [FEST_DF] Distanza a piedi (miglia) [FEST_DC] Distanza in auto (miglia) [FESTDFM] Distanza a piedi (metri) [FESTDCM] Distanza in auto (metri) [TOT_DIS] Distanza totale (miglia) [TOTDISM] Distanza totale (metri) [DISTHEL] Distanza in elicottero (miglia) [DISTHEM] Distanza in elicottero (metri) [DISTBOA] Distanza in barca (miglia) [DISTBOM] Distanza in barca (metri) [FEST_LS] Persone salvate in ambulanza [FEST_CC] Criminali uccisi in missioni Vigilante [FEST_FE] Incendi estinti [FEST_RP] Violenze eseguite [FEST_MP] Missioni completate [FEST_BB] Bling-bling Scramble: [FEST_H0] Maggior numero di punti di controllo [FEST_GC] Auto delle gang distrutte: [FEST_H1] Distruzione Diablo [FEST_H2] Massacro Mafioso [FEST_H3] Calamità al Casinò [FEST_H4] Demolizioni Rumpo [USJ] BONUS ACROBAZIA UNICA! [RATNG1] Cittadino onesto [RATNG2] Sconosciuto [RATNG3] Pezzente [RATNG4] Furfantello [RATNG5] Vandalo [RATNG6] Fattorino [RATNG7] Borsaiolo [RATNG8] Cleptomane [RATNG9] Teppista [RATNG10] Ladruncolo [RATNG11] Sanguisuga [RATNG12] Truffatore [RATNG13] Farabutto [RATNG14] Informatore [RATNG15] Imbroglione [RATNG16] Bullo [RATNG17] Canaglia [RATNG18] Scapestrato [RATNG19] Ruffiano [RATNG20] Fuorilegge [RATNG21] Delinquente [RATNG22] Ladro [RATNG23] Scagnozzo [RATNG24] Tirapiedi [RATNG25] Avanzo di galera [RATNG26] Ex galeotto [RATNG27] Criminale [RATNG28] Picchiatore [RATNG29] Saggio [RATNG30] Autista [RATNG31] Guardia del corpo [RATNG32] Attaccabrighe [RATNG33] Cacciatore di taglie [RATNG34] Vigilante [RATNG35] Ronin [RATNG36] Riparatore [RATNG37] Sicario [RATNG38] Socio [RATNG39] Macellaio [RATNG40] Pulitore [RATNG41] Assassino [RATNG42] Consigliere [RATNG43] Consulente [RATNG44] Braccio destro [RATNG45] Esecutore [RATNG46] Tenente [RATNG47] Aiutante del boss [RATNG48] Capo [RATNG49] Boss [RATNG50] Intoccabile [RATNG51] Don [RATNG52] Re della fottuta città [PAGE_00] . [WELCOME] BENVENUTO A [TSCORE] GUADAGNI: ~1~$ [PBOAT_2] { reVC update } Premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sparare con i cannoni della barca. [HJSTAT] Distanza: ~1~.~1~m Altezza: ~1~.~1~m Ribaltamenti: ~1~ Rotazioni: ~1~_ [HJSTATW] Distanza: ~1~.~1~m Altezza: ~1~.~1~m Ribaltamenti: ~1~ Rotazioni: ~1~_ E che grande atterraggio! [ATUTOR] Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Infermiere. [FEST_HA] Livello massimo della missione Infermiere [C_KILLS] CRIMINALI UCCISI: ~1~ [HJSTATF] Distanza: ~1~ piedi Altezza: ~1~ piedi Ribaltamenti: ~1~ Rotazioni: ~1~_ [HJSTAWF] Distanza: ~1~ piedi Altezza: ~1~ piedi Ribaltamenti: ~1~ Rotazioni: ~1~_ E che grande atterraggio! [CRED001] ROCKSTAR NORTH [CRD001A] DIRETTORE DELLO STUDIO [CRD001B] ANDREW SEMPLE [CRED002] PRODUTTORE [CRD002A] DIRETTORE DI SVILUPPO [CRED003] LESLIE BENZIES [CRED004] DIRETTORE ARTISTICO [CRED005] AARON GARBUT [CRED006] DIRETTORI TECNICI [CRED007] OBBE VERMEIJ [CRED008] ADAM FOWLER [CRED010] ANDREW DUTHIE [CRED011] CRAIG FILSHIE [CRED012] WILLIAM MILLS [CRED013] CHRIS ROTHWELL [CRD013A] IMRAN SARWAR [CRD013B] JAMES WORRALL [CRD013C] JOHN HAIME [CRED014] SCRITTO DA [CRED015] JAMES WORRALL [CRED016] PAUL KUROWSKI [CRED017] DAN HOUSER [CRED018] PROGETTAZIONE PERSONAGGI PRINCIPALI [CRD018A] PROGETTAZIONE PERSONAGGI [CRED019] IAN MCQUE [CRD019A] TOKS SOLARIN [CRD019B] ALAN DAVIDSON [CRED020] ANIMATORE CAPO [CRED021] ALEX HORTON [CRD022A] ANIMATORI [CRED022] LEE MONTGOMERY [CRD022B] DUNCAN SHIELDS [CRD022C] GUS BRAID [CRED023] PROGETTAZIONE VEICOLI [CRD023A] JOLYON ORME [CRD023B] ALAN DUNCAN [CRD024A] CAPO PROGETTAZIONE VEICOLI [CRED024] PAUL KUROWSKI [CRED025] PROGETTAZIONE MAPPA [CRED026] ADAM COCHRANE [CRED027] NIK TAYLOR [CRED028] GARY MCADAM [CRED029] KEIRAN BAILLIE [CRED030] ALISDAIR WOOD [CRED031] ANDREW SOOSAY [CRD031A] STEVEN MULHOLLAND [CRD031B] WAYLAND STANDING [CRD031C] CAMPBELL J. DICK [CRD031D] PROGETTAZIONE GRAFICA [CRD031E] STUART PETRI [CRED032] PROGRAMMATORE CAPO [CRD032A] PROGRAMMATORI [CRED033] ALEXANDER ROGER [CRED034] GRAEME WILLIAMSON [CRED035] BARANE CHAN [CRED036] DEREK PAYNE [CRED037] GORDON YEOMAN [CRD037A] ALAN CAMPBELL [CRD037B] MARK HANLON [CRD037C] ANDRZEJ MADAJCZYK [CRED038] CAPO PRODUZIONE MUSICA [CRED039] CRAIG CONNER [CRED040] STUART ROSS [CRED041] CAPO INGEGNERE AUDIO [CRED042] ALLAN WALKER [CRD041A] INGEGNERE AUDIO [CRD041B] AUDIO [CRD042A] WILL MORTON [CRED043] PROGRAMMATORE AUDIO [CRED044] RAYMOND USHER [CRED045] RESPONSABILE TESTING [CRED046] CRAIG ARBUTHNOTT [CRED047] CAPO CQ [CRED048] NEIL CORBETT [CRED049] KEVIN WONG [CRED050] CQ [CRED051] DAVID BEDDOES [CRED052] DAVID WATSON [CRED053] BARRY CLARK [CRED054] ROSS SPARROW [CRED055] JAMES ALLAN [CRED056] NEIL MEIKLE [CRD056A] GEORGE WILLIAMSON [CRD056B] MATT JONES [CRD056C] ROB HARBOUR [CRD056D] TOM WHITTAKER [CRED057] CAPO SUPPORTO TECNICO [CRED058] LORRAINE ROY [CRD057A] SUPPORTO TECNICO [CRED059] CHRISTINE CHALMERS [CRD060A] SUPPORTO UFFICIO [CRD060B] KIM GURNEY [CRD060C] CASSIE OLIVER [CRED060] ROCKSTAR NEW YORK [CRED061] PRODUTTORE ESECUTIVO [CRED062] SAM HOUSER [CRED063] PRODUTTORE [CRED064] DAN HOUSER [CRED065] VP DI SVILUPPO [CRED066] JAMIE KING [CRED067] CAPO RESPONSABILE TECNOLOGICO [CRED068] GARY J. FOREMAN [CRED069] PRODUTTORE ASSOCIATO [CRED070] JEREMY POPE [CRD071A] DIRETTORE DEL CQ [CRD072A] JEFF ROSA [CRED071] SUPERVISORE MUSICA [CRED072] TERRY DONOVAN [CRED073] SQUADRA DI PRODUZIONE [CRED074] TERRY DONOVAN [CRED075] JENNIFER KOLBE [CRED076] JENEFER GROSS [CRED077] LAURA PATERSON [CRED078] JEFF CASTANEDA [CRED079] JERONIMO BARRERA [CRED080] CARLY SLATER [CRED081] JUNG KWAK [CRED082] BRIAN WOOD [CRED083] RENAUD SEBANNE [CRED084] RICHARD KRUGER [CRD084A] DANIEL EINZIG [CRD084B] JACEN BURROWS [CRD084C] LINN PR [CRD084D] GRAFICA DI COPERTINA [CRD084E] STEPHEN BLISS [CRED085] KENT PAUL'S 80 NOSTALGIA ZONE [CRED086] COMPOSTA DA DAN HOUSER [CRD086A] PRODOTTA DA ADAM TEDMAN [CRED087] WWW.KENTPAUL.COM E WWW.VICECITY.COM [CRED088] CREATO DA [CRD088A] ADAM TEDMAN [CRD088B] DAVID YU [CRD088C] JERRY LUNA [CRD088D] STUART PETRI [CRD088E] MICHAEL CARNEVALE [CRD088F] GREG LAU [CRD088G] FUTABA HAYASHI [CRED089] RESPONSABILE CQ [CRED090] CRAIG ARBUTHNOTT [CRED091] CAPO ANALISTA [CRED092] ADAM DAVIDSON [CRD092A] JOE HOWELL [CRD092B] MARC FERNANDEZ [CRED093] ANALISTA DI GIOCO [CRED094] RICHARD HUIE [CRED095] SQUADRA TESTING ROCKSTAR [CRED096] LANCE WILLIAMS [CRED097] JOE GREENE [CRED098] BRIAN PLANER [CRD098A] ELIZABETH SATTERWHITE [CRD098B] JAMEEL VEGA [CRD098C] MIKE HONG [CRED099] LEE CUMMINGS [CRED100] SCENEGGIATURA [CRED101] JAMES WORRALL [CRED102] DAN HOUSER [CRED103] ADAM TEDMAN [CRED104] PAUL YEATES [CRED105] JENEFER GROSS [CRED106] LAURA PATERSON [CRED107] SEQUENZE ANIMATE [CRED108] SCRITTE DA DAN HOUSER E JAMES WORRALL [CRED109] DIREZIONE AUDIO DI DAN HOUSER E NAVID KHONSARI [CRED110] PRODOTTE DA JAMIE KING [CRD110A] RICERCA TALENTI: JAMIE KING, SEAN MACALUSO [CRED111] CAST [CRD111A] PERSONAGGI PRINCIPALI [CRED112] TOMMY VERCETTI - RAY LIOTTA [CRED113] KEN ROSENBERG - WILLIAN FICHTNER [CRED114] SONNY FORELLI - TOM SIZEMORE [CRED115] STEVE SCOTT - DENNIS HOPPER [CRED116] AVERY CARRINGTON - BURT REYNOLDS [CRED117] RICARDO DIAZ - LUIS GUZMAN [CRED118] LANCE VANCE - PHILIP MICHAEL THOMAS [CRED119] COLONNELLO JUAN CORTEZ - ROBERT DAVI [CRED120] UMBERTO ROBINA - DANNY TREJO [CRED121] PHIL CASSIDY - GARY BUSEY [CRED122] MITCH BAKER - LEE MAJORS [CRED123] MERCEDES CORTEZ - FAIRUZA BALK [CRED124] KENT PAUL - DANNY DYER [CRED125] JEZZ TORRENT - KEVIN MCKIDD [CRED126] CENTRALINISTA TAXI - DEBORAH HARRY [CRED127] CANDY SUXX - JENNA JAMESON [CRED128] BJ SMITH - LAWRENCE TAYLOR [CRED129] AUNTIE POULET - YOUREE CLEOMILI HARRIS [CRED130] SPACCIATORE - ARMANDO RIESCO [CRED131] COUGAR - BLAYNE PERRY [CRED132] HILARY - CHARLES TUCKER [CRED133] DEPUTATO ALEX SHRUB - CHRIS LUCAS [CRED134] VECCHIO KELLY - GEORGE DICENZO [CRD134A] CAM JONES - GREG SIMS [CRD134B] PSICOPATICO - HUNTER PLATIN [CRD134C] MAUDE, LA SIGNORA DEI GELATI - JANE GENNARO [CRD134D] JETHRO - JOHN ZURHELLEN [CRD134E] GONZALES - JORGE PUPO [CRD134F] DWAYNE - NAVID KHONSARI [CRD134G] DICK - PETER MCKAY [CRD134H] MIKE & PORNOSTAR - ROBERT CIHRA [CRD134I] PERCY - RUSSELL FOREMAN [CRED135] MOTION CAPTURE [CRED136] ANIMATO DA [CRD136A] DIREZIONE TECNICA DI ALEX HORTON [CRED137] DIRETTO DA [CRD137A] DIRETTO DA NAVID KHONSARI [CRED138] PRODOTTO DA [CRD138A] PRODOTTO DA JAMIE KING [CRD138B] RENAUD SEBBANE [CRED139] REGISTRATO PRESSO PERSPECTIVE STUDIOS, BROOKLYN [CRED140] ATTORI MOTION CAPTURE [CRD140A] BLAYNE PERRY [CRD140B] JONATHON SALE [CRD140C] CHARLES TUCKER [CRD140D] EDDIE MARRERO [CRD140E] WILLIAM MCCALL [CRD140F] JORGE PUPO [CRD140G] ROBERT JACKSON [CRD140H] TARA RADCLIFFE [CRD140I] JENIFER GAMBETESE [CRD140J] KRIS ACHEVARRIA [CRD140K] ALI ORDOUBADI [CRD140L] KAHLEEM POOLE [CRED141] DIALOGHI PEDONI [CRD141A] SCRITTI DA DAN HOUSER, MARC FERNANDEZ, GILLIAN TELLING E NAVID KHONSARI [CRD141B] CON L'AIUTO DI JEREMY POPE, LANCE WILLIAMS E JENNY JEMISON [CRED142] SCRITTI DA [CRD142A] DAN HOUSER E JAMES WORRALL [CRED143] DIRETTI DA DAN HOUSER, CRAIG CONNER, MARC FERNANDEZ E ALLAN WALKER [CRED144] PRODOTTI DA RENAUD SEBANNE [CRED145] PEDONI [CRED146] ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCIA, [CRED147] ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, [CRED148] ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, [CRED149] BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, [CRED150] CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LOPEZ, DAN LEE, [CRED151] DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, [CRED152] DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, [CRED153] DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, [CRED154] DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, [CRED155] ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, [CRED156] GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, [CRED157] HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, [CRED158] JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, [CRED159] KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, [CRED160] LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, [CRED161] LORNA JORDAN, LUCIO AMADIO, MARCO FERNANDEZ, MARIKO TANAKA, MARLON MATTHEWS, [CRED162] MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, [CRED163] MELISSA ALVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, [CRED164] NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, [CRED165] OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZALES, [CRED166] RANDY JOHNSON, REY CONCEPCION, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, [CRED167] ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NUNEZ, SALVADORE SUAZO, [CRED168] SAM WHITE, SANTOS GONZALES, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, [CRED169] STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, [CRED170] SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, [CRED171] DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, [CRED172] CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON [CRED175] ADAM DAVIDSON [CRED176] LANCE WILLIAMS [CRED177] NEIL MCCAFFREY [CRED178] LAURA PATERSON [CRED179] REY CONCEPCION [CRED180] CHARLES HEROLD [CRED181] ANDREW GREENWALD [CRED182] JAMES MIELKE [CRED183] PETER SUCIU [CRED184] ALEX ODULIO [CRED185] DON NKRUMAH [CRED186] KENDALL PITTMAN [CRED187] SAL SUAZO [CRED188] EREK MATEO [CRED189] CHRIS DIFATE [CRED190] LEILA MILTON [CRED191] DARREN ZOLTOWSKI [CRED192] VIRGINIA SMITH [CRED193] KEVIN CASSIN [CRED194] JASON SHIGEMORI [CRED195] KELLY KINSELLA [CRED196] MOLLIE STICKNEY [CRED197] STANTON SARJEANT [CRED198] LAURA WALSH [CRED199] MARK GARONE [CRED200] JOANNA SLY [CRED201] ELIZABETH HOWELL [CRED202] ANA HERCULES [CRED203] SHIRLEY IRICK [CRED204] KASHONA FIELDS [CRED205] JOEL M. LILJE [CRED206] JOHN DIBENEDETTO [CRED207] NANCY GILES [CRED208] RYAN CROY [CRED209] JENNIFER KOLBE [CRED210] LIAM BURKE [CRED211] SIGRID PREISSL [CRED212] ANITA FITZSIMONS [CRED213] PHILIPPA RASELLI [CRED214] WIL QUESNEL [CRED215] FALKO BURKERT [CRED216] SARA SEWELL [CRED217] STAZIONI RADIO E MUSICA [CRED218] CONSULENZA MUSICALE [CRD218A] HEINZ HENN [CRD218B] STUART ROSS [CRED219] COORDINATORE TRACCE AUDIO [CRED220] TERRY DONOVAN [CRED221] PRODUTTORI ROCKSTAR GAMES [CRED222] DAN HOUSER E LAZLOW [CRED223] PRODUTTORE ROCKSTAR NORTH [CRED224] CRAIG CONNER [CRED225] ALLAN WALKER [CRED226] LAZLOW [CRED227] DJ BANTER E IMAGING [CRED228] SCRITTO DA DAN HOUSER E LAZLOW [CRED229] FLASH FM [CRD229A] TONI-MARIA CHAMBERS [CRD229B] VOCE E PRODUZIONE IMAGING - JEFF BERLIN [CRED230] UN RINGRAZIAMENTO SPECIALE A [CRED231] TOMMY MOTTOLA, [CRED232] MICHELLE ANTHONY, [CRED233] STEVE BARNETT, [CRED234] CHUCK FLECKENSTEIN, [CRED235] RITA LIBERATOR [CRED236] MARTIN & CLAIRE LOGAN [CRED237] SANDRA HUTTON [CRED238] CHRISTINE DAVIDSON [CRED239] ALAN, RED & BIGFOOT [CRED240] LE T [CRED241] COLIN DONALD [CRED242] KERRY STALLWOOD [CRED243] ALAN MCGREGOR [CRED244] CHRIS MORTON [CRED245] EMIL BUSSE [CRED246] EMILY BAILLIE [CRED247] KEVIN ARCHIBALD [CRED248] MORAG KERR [CRED249] CATH WALKER [CRED250] ISO BAR [CRED251] WATERLINE [CRED252] NEWS CAFE [CRD251A] THE POND [CRD252A] PIVO [CRED253] BUDGET VIDEO RENTALS [CRED254] LORNA'S SCOOTER [CRED255] GARETH MURFIN [CRED256] GRAFICA ADDIZIONALE [CRED257] TONY PORTER [CRED258] CRAIG MOORE [CRED259] SINCRONIZZAZIONE LABIALE SEQUENZE [CRED260] COSGROVE HALL FILMS [CRED261] PRODUTTORE - OWEN BALLHATCHET [CRED262] ANIMATORE SENIOR- JON TURNER [CRED263] ANIMATORI - RICHARD DRUMM [CRED264] DAVE BROWN [CRED265] MAIR THOMAS [CRED266] PRASHANT PATEL [CRED267] CONSULENTE TECNOLOGICO AUDIO [CRED268] RIK EDE DI GAMESOUND LTD. [CRED269] SUPPORTO INTEGRAZIONE DTS [CRED270] TED LAVERTY DI DTS [CRED271] CHRIS GREER DI DTS [CRED272] JASON PAGE DI DTS [CRED273] RICERCA E ANALISI [CRED274] VROCK [CRED275] DJ: LAZLOW COME SE STESSO [CRED276] VOCE IMAGING - JOE KELLY [CRED277] PRODUZIONE IMAGING - JONATHAN HANST [CRED278] WAVE 103 [CRED279] DJ: ADAM FIRST - JAMIE CANFIELD [CRED280] VOCE IMAGING - JEN SWEENEY [CRED281] PRODUZIONE IMAGING - JONATHAN HANST [CRED282] FEVER 105 [CRED283] DJ: OLIVER 'LADYKILLER' BISCUIT - JULIUS DYSON [CRED284] VOCE IMAGING MASCHILE - ED MCMANN [CRED285] VOCE IMAGING FEMMINILE - SHAWNEE SMITH [CRED286] PRODUZIONE IMAGING - LISTEN KITCHEN [CRED287] EMOTION 98.3 [CRED288] DJ: FERNANDO - FRANK CHAVEZ [CRED289] VOCE IMAGING - JEN SWEENEY [CRED290] PRODUZIONE IMAGING - JONATHAN HANST [CRED291] RADIO ESPANTOSO [CRED292] DJ: PEPE - TONY CHILRODES [CRED293] WILDSTYLE [CRED294] DJ: MISTER MAGIC COME SE STESSO [CRED295] VOCE IMAGING - FRANK SILVESTRO [CRED296] PRODUZIONE IMAGING - LAZLOW [CRED297] KCHAT [CRED298] SCRITTA DA DAN HOUSER E LAZLOW [CRED299] PRODOTTA E MIXATA DA LAZLOW [CRED300] DJ AMY SHECKENHAUSEN - LEYNA WEBER [CRED301] JEZ TORRENT - KEVIN MCKIDD [CRED302] MANDY - COLLEEN CORBETT [CRED303] MICHELLE CARAPADIS - MARY BIRDSONG [CRED304] MR.ZOO - CARL DOWLING [CRED305] GETHSEMANEE - LYNN LIPTON [CRED306] CLAUDE MAGINOT - JOHN MAUCERI [CRED307] BJ SMITH - LAWRENCE TAYLOR [CRED308] THOR - FRANK FAVA [CRED309] VOCI PUBBLICITÀ [CRED310] COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS [CRED311] LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, [CRED312] DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, [CRED313] KEITH BROADAS [CRED314] VCPR [CRED315] SCRITTO DA DAN HOUSER E LAZLOW [CRED316] PRODOTTO DA LAZLOW [CRED317] MAURICE CHAVEZ - PHILLIP ANTHONY RODRIGUEZ [CRED318] JONATHAN FREELOADER - PATRICK OLSEN [CRED319] MICHELLE MONTANIUS - KELLY GUEST [CRED320] DEP. ALEX SHRUB - CHRIS LUCAS [CRED321] CALLUM CRAYSHAW - SEAN MODICA [CRED322] JOHN F. HICKORY - LJ GANSEN [CRED323] PASTOR RICHARDS - DAVID GREEN [CRED324] JAN BROWN - MAUREEN SILLIMAN [CRED325] BARRY STARK - RENAUD SEBBANE [CRED326] JENNY LOUISE CRAB - MARY BIRDSONG [CRED327] KONSTANTINOS SMITH - KONSTANTINOS.COM [CRED328] JEREMY ROBARD - PETER SILVESTRO [CRED329] PUBBLICITÀ RADIO [CRED330] SCRITTE DA DAN HOUSER E LAZLOW [CRED331] PRODOTTE DA LAZLOW [CRED332] JINGLE AGGIUNTIVI PRODOTTI DA CRAIG CONNER [CRED333] VOCI PUBBLICITÀ [CRED334] ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, [CRED335] ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS [CRED336] FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS [CRED337] HARRISON, ED MCMANN, FRANK CHAVEZ, FRANK FAVA, GENE HILGREEN, GREG [CRED338] SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, [CRED339] JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE [CRED340] WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, [CRED341] MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF [CRED342] CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD [CRD344A] AUDIO REGISTRATO PRESSO DIGITAL ARTS STUDIOS, [CRED344] NYC, TRACK 9 STUDIOS, NYC, [CRED345] WEDDINGTON MULTIMEDIA, LOS ANGELES, [CRD345A] SYNC SOUND, NYC E RADIO LAZLOW, LONG ISLAND. [CRED346] UN RINGRAZIAMENTO A AXEL ERICSON E WON LEE DI DIGITAL ARTS, PAUL VASQUEZ DI TRACK 9 STUDIOS, JOHN BOWEN E JOHN HASSLER DI SYNC SOUND [CRED347] MARK LLOYD [CRED348] TIM BATES [CRED349] KIT BROWN [CRED350] ANDY MASON [CRED351] PHIL DEANE [CRED352] PHIL ALEXANDER [CRED353] MATT HEWITT [CRED354] DENBY GRACE [CRED355] ANTOINE CABROL [CRED356] JONOTHAN STONES [CRED357] MIKE BLACKBURN [CRED358] TIM MCGAFF [CINCAM] Camera mobile [RC4] 'LA FURIA DI RUMPO' [LEGAL] ~g~Elimina la minaccia criminale! [GA_2] Motore nuovo e carrozzeria riverniciata. Gli sbirri non ti riconosceranno! [HELP15] Quando sei a piedi, premi il ~h~~k~~PED_LOOKBEHIND~~w~ per ~h~guardare indietro~w~. Usa la ~h~levetta analogica destra~w~ per ~h~guardarti attorno~w~. [FEC_LB4] Guarda indietro (tasto R3) [PERPIC] Pacchetti speciali recuperati [CO_ONE] Pacchetto speciale ~1~ su ~1~ [GA_21] Non puoi parcheggiare altri veicoli in questo garage. [CHEAT1] Trucco attivato [CHEAT2] Trucco armi [CHEAT3] Trucco salute [CHEAT4] Trucco armatura [CHEAT5] Trucco livello di sospetto [CHEAT6] Trucco soldi [CHEAT7] Trucco tempo atmosferico [USJ_ALL] TUTTE LE ACROBAZIE UNICHE COMPLETATE [JAN] Gen [FEB] Feb [MAR] Mar [APR] Apr [MAY] Mag [JUN] Giu [JUL] Lug [AUG] Ago [SEP] Set [OCT] Ott [NOV] Nov [DEC] Dic [DEFDT] --:---:---- --:--:-- [BONUS] ~g~BONUS ~1~$ [HORN1] Premi il ~h~~k~~VEHICLE_HORN~~w~ per attivare il ~h~clacson~w~. [HORN2] Premi il ~h~~k~~VEHICLE_HORN~~w~ per attivare il ~h~clacson~w~. [HORN3] Premi il ~h~~k~~VEHICLE_HORN~~w~ per attivare il ~h~clacson~w~. [FEC_EXV] Esci dal veicolo [TAXI_M] 'TAXI DRIVER' [COP_M] 'VIGILANTE' [FIRE_M] 'POMPIERE' [AMBUL_M] 'INFERMIERE' [HJ_IS] BONUS ACROBAZIA FOLLE: ~1~$ [HJ_PIS] BONUS ACROBAZIA FOLLE PERFETTA: ~1~$ [HJ_DIS] BONUS DOPPIA ACROBAZIA FOLLE: ~1~$ [HJ_PDIS] BONUS DOPPIA ACROBAZIA FOLLE PERFETTA: ~1~$ [HJ_TIS] BONUS TRIPLA ACROBAZIA FOLLE: ~1~$ [HJ_PTIS] BONUS TRIPLA ACROBAZIA FOLLE PERFETTA: ~1~$ [HJ_QIS] BONUS QUADRUPLA ACROBAZIA FOLLE: ~1~$ [HJ_PQIS] BONUS QUADRUPLA ACROBAZIA FOLLE PERFETTA: ~1~$ [FESZ_LS] Caricamento completato. [HELI_1A] Prova le tue abilità con lo Sparrow: scopri quanto velocemente riesci a completare il percorso. [HELI_1B] Percorso completato! [HELIODD] Strani incarichi con l'elicottero [INT2_M] Hanno una fattoria in Panama. [INT2_N] OK, va bene, ascoltate: [INT2_O] quando arriviamo volete che resti in macchina [INT2_P] o preferite che venga con voi altri? [INT2_Q] No, resta in macchina. [INT2_R] Lo sapete, ci ho pensato su. [INT2_S] Penso proprio che resterò in macchina. [INT4_A] Fottuti! Siamo fottuti! [INT4_B] È sempre la stessa storia, [INT4_C] metto fuori la testa per un maledetto secondo, [INT4_D] e il fato mi tira palate di merda in faccia! [INT4_E] Bene, vaffanculo! [INT4_F] Smettila di fare casino e di lamentarti. Siamo vivi, non è vero? [INT4_G] Fammi scendere qua. [INT4_H] Fai scomparire la macchina e fatti una bella dormita. [INT4_I] Passerò domani a trovarti in ufficio e troveremo un modo per risolvere questo casino. [INT4_J] OK, mi sembra un'ottima idea, andrò a dormire. [INT4_K] Che cosa intendi fare? [INT4_L] Tornare nella mia stanza, [INT4_M] schiarirmi le idee e risolvere questo casino. [INT4_N] OK. [LAW] MISSIONI DELL'AVVOCATO [LAW1_1] ~g~Vai a prendere degli abiti nuovi dal negozio di vestiti Rafael's. [LAW4_6] Brucia l'amministrazione! [LAW4_7] Uccidi i responsabili! [LAW4_8] Combattere, combattere, combattere, combattere,. [LAW4_9] Più ferie, meno lavoro! [LAW4_11] Combattere, combattere, combattere, combattere,. [LAW4_12] Viva la rivoluzione. [GENERAL] MISSIONI DEL COLONNELLO [GEN3_4] Tommy Vercetti. Andiamo... [GEN3_13] Che cosa diavolo ti prende? Vai sul tetto dall'altra parte del cortile prima che arrivino! [GEN3_17] Meeerda! Stai cercando di ammazzarmi? [GEN3_21] ~g~Ha preso i soldi di Diaz! Inseguilo e riprendili! [GEN3_24] ~r~Diaz è morto! Non sei riuscito a proteggerlo! [GEN3_26] ~r~Hai ucciso Diaz! [GEN3_27] ~r~Hai ucciso una guardia di Diaz! [GEN3_31] ~g~Vai all'appuntamento e proteggi Diaz. [GEN3_32] ~g~Raggiungi il tetto nell'edificio di fronte a Lance e appostati. [COKE] MISSIONI DEL BARONE DELLA COCA [COK1_3] Spero tu cada e ti rompa l'osso del collo! [COK1_6] Sono stanco di tutto questo. [COK2_7] Vedi quei segnali, ragazzo? Cerca di colpire le luci! [COK2_10] Di certo sei più bravo a sparare che a parlare. [COK2_11] Grazie. Anche tu hai una bella parlantina. [COK2_12] Lo so, Tommy. [COK2_18] Ti diverti con Kenny Loggins. [COK2_19] Cielo, adoro questo album! [COK2_26] ~r~Hai ucciso Lance! [COK3_1] Non sparare, amico! [COK3_2] Che cosa sta succedendo? [COK3_3] Sta prendendo la barca. Farabutto! [COK3_4] Aiuto! Un idiota sta rubando la barca! [COK4_W] Uugghh! Questo è l'ultimo. [COK4_X] Vado ad accendere... [COK4_Y] A quanto sembra abbiamo dei nuovi amici. [COK4_2] Sì. [COK4_6] Sai dove stiamo andando? [COK4_7] Ci siamo persi? [COK4_8] Abbiamo un po' di competizione! [COK4_9] Falli fuori! [COK4_9A] È giunta l'ora di un po' di Lance Vance Dance! [COK4_10] Sono pronti per il macero! E per sfamare i pesci. [COK4_11] Ce l'abbiamo fatta! Le altre imbarcazioni non sono della nostra classe. [COK4_17] Stanno iniziando a pregare! [COK4_18] I miei piedi sono bagnati! STIAMO IMBARCANDO ACQUA! [COK4_21] Ponte in arrivo! [COK4_22] Lanciati, sta per esplodere! [COK4_23] Bel colpo! [COK4_29] ~r~Hai ucciso Lance! [ASS1_6] Vai Tommy, andrà tutto bene! [ASS1_7] Beccatevi questo, maledetti assassini! [ASS1_8] Sono bloccato! [ASS1_9] Ti copro io Tommy! [ASS1_10] Ehi, questo sì che è un bel confine alberato. [ASS1_11] Ehi Tommy, posso avere la stanza con la vista sulla baia? [ASS1_12] Che bel soffitto alto che c'è qui... [ASS1_3] Lance! Ho bisogno di copertura! [ASS1_5] Lance! [ASS1_15] ~g~Distruggi la dimora e uccidi Diaz! [ASS1_17] ~g~Sono disponibili più strade all'interno della villa. [TAXWAR] MISSIONI GUERRA DEI TAXI [NOTAXI] ~g~Hai bisogno di un taxi Kaufman per attivare questa missione. [TAXW1_5] ~g~Devi avere un taxi Kaufman! [TAX2_4] Forza, Tommy. [TAX2_5] Riempilo di botte! [TAX2_6] Non ha neppure il permesso. [TAX2_7] Maledetti servizi di limousine. [TAXW3_1] ~g~Vai a prendere Mercedes. [RACE1] ~g~3..2..1.. VIA VIA VIA! [RACE2] ~g~3 [RACE3] ~g~2 [RACE4] ~g~1 [RACE5] ~g~VIA! [FIRST] ~b~PRIMO [SECOND] ~b~SECONDO [THIRD] ~b~TERZO [FOURTH] ~b~QUARTO [RACETM] ~b~TEMPO DI GARA: ~1~:~1~ [RACETM2] ~b~TEMPO DI GARA: ~1~:0~1~ [RACEFA] ~r~Non sei riuscito a vincere la gara! [TEX1_5] ~r~È riuscito a fuggire! [SEG3_1] TEMPO: [SEG3_2] ~g~Raggiungi il van che contiene il Raider radiocomandato e le bombe a tempo. [SEG3_3] ~g~Devi utilizzare il RAIDER RADIOCOMANDATO per trasportare le 4 bombe nelle 4 aree bersaglio all'interno del cantiere. [SERG3_5] ~g~Puoi trasportare solo una bomba alla volta e non puoi raccogliere dell'esplosivo correttamente posizionato. [SEG3_7] ~g~Una volta posizionata la PRIMA bomba con successo su di un bersaglio, partirà il conto alla rovescia, dovrai piazzare tutti gli esplosivi entro il tempo limite. [SEG3_8] ~g~Le 4 bombe devono essere tutte posizionate su ognuno dei bersagli per passare la missione e demolire l'edificio. [SEG3_9] ~g~Bomba piazzata! Ancora 3. [SEG3_10] ~g~Bomba piazzata! Ancora 2. [SEG3_11] ~g~Bomba piazzata! Ne manca una sola. [SEG3_12] ~g~Bomba NON piazzata! Recupera l'esplosivo! [SEG3_13] ~g~Piazza la bomba nella zona bersaglio. [SEG3_14] ~r~Tempo esaurito: hai fallito nel demolire l'edificio. [SEG3_15] ~r~Il Raider radiocomandato è stato distrutto! Come farai adesso a trasportare le bombe? [AVERY] MISSIONI DI AVERY [ASM] MISSIONI DA SICARIO [ASM_1] MISSIONE DA SICARIO 1 [ASM1_1] ~g~Mr. Teal, il tuo aiuto nello sradicare gli stranieri è stato inestimabile per il nostro lavoro. Ho un altro lavoro con un contatto più 'diretto'. I dettagli sono stati attaccati sotto al telefono. [ASM1_2] ~g~Raggiungi il telefono pubblico presso il Mall in Washington Beach. [ASM1_3] ~g~Carl Pearson, fattorino di una pizzeria. Non deve completare le sue consegne. [ASM1_4] ~g~Uccidi il fattorino prima che possa completare le sue consegne. [ASM_2] MISSIONE DA SICARIO 2 [ASM_3] MISSIONE DA SICARIO 3 [ASM3_A] Marcus Hammond, Franco Carter, Dick Tanner, Nick Kong e Stuntman Driver fanno tutti parte di un'associazione europea che si sta preparando a commettere una rapina. [ASM3_B] Si trovano tutti nella loro posizione pronti all'azione. Voglio che vengano tutti eliminati prima che inizi il colpo. Ho disposto delle utili armi nei paraggi. [ASM3_1] ~g~Recupera le armi che Mr. Black ha lasciato per te. [ASM3_2] ~g~Non avvicinarti troppo, se no il bersaglio potrebbe vederti. [ASM3_3] ~g~Per un'eliminazione più rapida, colpisci da una locazione vicino alle loro postazioni ed evita di farti vedere. [ASM3_4] ~g~Ti ha visto! Fallo fuori il prima possibile! [ASM3_5] ~g~Marcus Hammond si trova presso dei cartelli pubblicitari in Washington. [ASM3_6] ~g~Franco Carter si trova presso la DBP Security vicino a Ocean Drive. [ASM3_7] ~g~Dick Tanner si trova presso la gioielleria in Vice Point. [ASM3_8] ~g~Nick Kong si trova presso Washington Beach. [ASM3_9] ~g~Stuntman Driver si trova presso Washington Beach. [ASM3_10] ~r~Non sei riuscito a eliminarli tutti quanti. [ASM_4] MISSIONE DA SICARIO 4 [ASM4_1] ~g~Recupera il fucile che ti abbiamo lasciato tra le foglie presso il terminal dell'aeroporto. [ASM4_2] ~g~Non mancare il bersaglio o allarmerai le sue guardie del corpo: ricorda di tenere una certa distanza per evitare di essere visto. [ASM4_3] ~g~Osserva la donna sul balcone sopra ai banconi del check-in dentro il terminal. NON UCCIDERLA. [ASM4_4] ~g~Elimina l'uomo a cui passa la valigetta ma solo DOPO CHE L'AVRÀ RACCOLTA. Poi recupera la valigetta e portala ad Ammu-Nation in Downtown. [ASM4_5] ~g~Recupera la valigetta! [ASM4_6] ~g~Porta la valigetta ad Ammu-Nation in Downtown [ASM4_7] ~r~Hai ucciso la donna, idiota! [ASM4_8] ~r~Il bersaglio ha sentito il colpo d'arma da fuoco! L'accordo è saltato! [ASM4_9] ~r~Il bersaglio è salito a bordo del volo! [ASM4_10] ~g~Sembra tu non sia l'unico interessato alla valigetta! Portala in fretta ad Ammu-Nation! [ASM4_11] ~r~Il bersaglio ti ha visto! L'accordo è saltato! [ASM4_13] ~g~Ti ha visto e sta scappando! Uccidilo e recupera la valigetta! [ASM4_14] ~g~L'indicatore della distanza nella parte superiore destra dello schermo ti tiene informato sulla distanza dal bersaglio. Se si riempirà, verrai avvistato. [ASM_5] MISSIONE DA SICARIO 5 [KICK] KICKSTART [KICK1_3] ~g~Numero di volte che hai appoggiato i piedi: ~1~ [KICK1_4] ~g~Penalità: ~1~ secondi [BANK] MISSIONI RAPINA [BANK1] MISSIONE RAPINA 1 [BANK2] MISSIONE RAPINA 2 [BJM2_3] PERCENTUALE DI CENTRI: ~1~% [BJM2_15] PUNTEGGIO: [BJM2_18] PUNTEGGIO DA BATTERE: [BJM2_19] ~g~Colpisci il maggior numero di bersagli entro il tempo limite! [BJM2_21] ~g~Colpisci il maggior numero di bersagli finché hai ancora colpi. [BANK3] MISSIONE RAPINA 3 [BJM3_1] ~g~Trova una macchina da corsa e posizionati sulla griglia di partenza. [BNK4_2A] I ragazzi dell'officina hanno fatto un ottimo lavoro su questa piccola. [BNK4_3G] Ommerda, abbiamo la polizia alle calcagna! [BNK4_3H] ...e non siamo ancora neanche arrivati. [BNK4_3K] Dobbiamo seminare prima la polizia... [BNK4_3L] Cristo Tommy, stai cercando di farci fuori? [BNK4_3N] Tutto ciò che amo finisce a pezzi! [BNK422A] Cam, quanto tempo? [BK4_23A] Mi bastano 3 minuti! [BNK4_26] Maledizione! Ecco che arrivano! [BNK4_32] Usa l'esplosivo per aprire le cassette di sicurezza! [BNK4_43] Vi copro io il culo, PARTI! [BNK4_51] A me sembri a posto. [KENT] MISSIONI DI KENT PAUL [KENT1] MISSIONE DI KENT PAUL 1 [COUNT] MISSIONI DI FALSIFICAZIONE [COUNT1] MISSIONE DI FALSIFICAZIONE 1 [COUNT2] MISSIONE DI FALSIFICAZIONE 2 [BIKE] MISSIONI DEI MOTOCICLISTI [BIKE1] MISSIONE DEI MOTOCICLISTI 1 [BIKE2] MISSIONE DEI MOTOCICLISTI 2 [BIKE3] MISSIONE DEI MOTOCICLISTI 3 [GOAWAY1] Ritorna quando avrai completato le missioni per la gang di Haiti. [HAIT] MISSIONI DEGLI HAITIANI [HAIT1] MISSIONE DEGLI HAITIANI 1 [HAIT2] MISSIONE DEGLI HAITIANI 2 [HAIT3] MISSIONE DEGLI HAITIANI 3 [HAM3_6] ~g~Utilizza il fucile di precisione che ti ho lasciato per completare la missione. [ROCK] MISSIONI DELLA BAND [ROK1_4] ~g~OK, credo tu stessi cercando questo... [ROK1_1E] ~g~Ti costerà di più di quanto hai! [ROK1_1F] ~g~Ritorna quando avrai i soldi. [RBM2_6] ~g~Wow! È un uomo, bloccalo! [ROCK3] MISSIONE DELLA BAND 3 [ROK3_6D] ~r~insieme alle vostre GROSSE TESTE DA CAPELLONI! [ROK3_40] Vicino al fottuto minibar? [RBM3_5] ~g~Porta i Love Fist allo spettacolo. [CUBANM] MISSIONI DEI CUBANI [CUBAN1] MISSIONE DEI CUBANI 1 [CUBAN2] MISSIONE DEI CUBANI 2 [CUB2_10] ~r~Dovresti uccidere gli Haitiani, non i Cubani. [CUBAN3] MISSIONE DEI CUBANI 3 [CUBAN4] MISSIONE DEI CUBANI 4 [CUB4_05] ~r~Ti avevo detto di restare in macchina! Adesso non riusciremo mai a entrare! [CUB4_25] OK, andiamo. [PROT] MISSIONI DI PROTEZIONE [PRO1_H] Perché non ti calmi per un secondo, sto finalmente cominciando a capire come funzionano le cose da queste parti. [PRO1_02] ~g~Esci dal Mall. [PRO3_06] ~g~Fai perdere le tracce. [PORN] MISSIONI PORNOGRAFIA [PORN1] MISSIONE PORNOGRAFIA 1 [POR1_03] ~r~Candy è morta! [PORN2] MISSIONE PORNOGRAFIA 2 [PORN3] MISSIONE PORNOGRAFIA 3 [POR3_18] Sei stato visto! [PORN4] MISSIONE PORNOGRAFIA 4 [POR4_04] ~g~Gli uffici sono dall'altra parte di questo cancello. [PHIL] MISSIONI DI PHIL [PHIL1] MISSIONE DI PHIL 1 [PHI1_06] Ma che combini, come diavolo guidi? [PHI1_07] Ehi! [PHIL2] MISSIONE DI PHIL 2 [PIZ1_A] MISSIONE RECAPITO PIZZE [PIZ1_03] ~g~Ritorna alla pizzeria e prendi le nuove ordinazioni. [PIZ1_04] ~g~Ecco le nuove ordinazioni. [PIZ1_10] Premi il ~h~tasto R3~w~ per annullare la missione. [CNTBUY1] Tipografia acquistata: ~1~$ [CARBUY] Concessionario acquistato: ~1~$ [PORNBUY] Studio cinematografico acquistato: ~1~$ [ICEBUY] Fabbrica di gelato acquistata: ~1~$ [TAXIBUY] Compagnia di taxi acquistata: ~1~$ [BANKBUY] Malibu acquistato: ~1~$ [BOATBUY] Cantiere navale acquistato: ~1~$ [PRNT_NO] Non puoi ancora comprare la tipografia, prova più tardi. [CAR_NO] Non puoi ancora comprare il concessionario, prova più tardi. [PORN_NO] Non puoi ancora comprare lo studio cinematografico, prova più tardi. [ICE_NO] Non puoi ancora comprare la fabbrica di gelato, prova più tardi. [TAXI_NO] Non puoi ancora comprare la compagnia di taxi, prova più tardi. [BANK_NO] Non puoi ancora comprare il Malibu, prova più tardi. [BOAT_NO] Non puoi ancora comprare il cantiere navale, prova più tardi. [PRNT_R3] Premi il tasto R3 per acquistare la tipografia per ~1~$ [CAR_R3] Premi il tasto R3 per acquistare il concessionario per ~1~$ [PORN_R3] Premi il tasto R3 per acquistare lo studio cinematografico per ~1~$ [ICE_R3] Premi il tasto R3 per acquistare la fabbrica di gelato per ~1~$ [TAXI_R3] Premi il tasto R3 per acquistare la compagnia di taxi per ~1~$ [BANK_R3] Premi il tasto R3 per acquistare il Malibu per ~1~$ [BOAT_R3] Premi il tasto R3 per acquistare il cantiere navale per ~1~$ [COL2_6] Fermo, brutto maiale imperialista americano! [COL2_6B] Questa è proprietà del governo francese. [COL2_6C] È finita! [COL3_A] Thomas, grazie per essere venuto. [COL3_B] Scusami se giungo subito al punto. [COL3_C] Diaz mi ha chiesto di sovrintendere una piccola transizione di lavoro. [COL3_D] Speriamo vada meglio dell'ultima... [COL3_E] Ed è per questo che ho pensato a te, amico. [COL3_F] Ho lasciato un po' di supporto al parcheggio multipiano. [COL3_G] Raccogli il materiale, poi raggiungi e sorveglia gli uomini di Diaz all'incontro. [COL4_2] Non so signore! [COL4_5] Signorsì, signore! [COL4_10] Andiamo a prendere delle ciambelle. [COL4_16] Signore! Sposto il veicolo, signore! [COL4_25] Autodistruzione del veicolo attivata! [COL5_6] Mercedes, quella ragazza sarà la mia rovina. [COL5_8] Maledetti scarafaggi! [COL5_5] Morite, maiali Francesi! [CNT2_1] Uccidetelo [CNT2_2] Recuperate le matrici! [CNT2_3] Proteggete il corriere. [FINKILL] OK ragazzi, uccidetelo! [FIN_6] Sonny è interessato alla mia cassaforte e ai miei soldi... [FIN_10] Sonny? SONNY! Sto venendo a prenderti! [RACES_4] 3 [RACES_5] 2 [RACES_6] 1 [RACES_7] VIA! [RACES_9] Tempo: ~1~:~1~ [RACES] TEMPO: [RACES17] Nuovo miglior tempo: ~1~:~1~ [RACES20] Nuovo miglior tempo: ~1~:0~1~ [RACES21] Tempo: ~1~:0~1~ [RCH1_1] ~g~Usa l'elicottero radiocomandato, il Raider, per PASSARE ATTRAVERSO i punti di controllo. [RCH1_2] ~g~I punti di controllo sono sparsi per tutto l'aeroporto. [RCH1_3] ~g~Hai ~c~8 MINUTI~g~ per attraversarli tutti e ~c~20~g~! [RCH1_5] Tempo [RCRC1_1] ~g~Sfida nella GARA A TAPPE altri 3 Bandit radiocomandati su 2 GIRI. [RCRC1_2] ~g~Raggiungi subito la griglia di partenza! [RCRC1_3] ~g~Giro finale! [RCRC1_4] ~g~3 [RCRC1_5] ~g~2 [RCRC1_6] ~g~1 [RCRC1_7] ~g~VIA! [RCRC1_8] ~g~Tempo di gara: ~1~ secondi [RCPL1_1] ~g~Sfida nella GARA A TAPPE altri 3 Baron radiocomandati. [RCPL1_2] ~g~Devi attraversare la ~o~CORONA CENTRALE~g~ per passare con successo un punto di controllo. [RCPL1_3] ~g~Raggiungi subito la griglia di partenza! [FEA_2SP] 2 altoparlanti [FEA_4SP] Più di 2 altoparlanti [FEA_EAR] Cuffie [FEA_NAH] NESSUNA PERIFERICA AUDIO [FET_APP] APPLICA [FES_SKN] NOME SKIN [FES_DAT] DATA [FES_SET] Usa skin [FET_DEF] Ripristina predefiniti [FESZ_QZ] Sei sicuro di voler salvare la partita? [FES_SCG] Salvare la partita? [FES_LCG] Vuoi caricare e continuare la partita? [FEC_FIR] Fuoco [FEC_NWE] Arma successiva [FEC_PWE] Arma precedente [FEC_FOR] Avanti [FEC_BAC] Indietro [FEC_LEF] Sinistra [FEC_RIG] Destra [FEC_ZIN] Ingrandisci [FEC_ZOT] Riduci [FEC_EEX] Entra o esci [FEC_RAD] Radio [FEC_SUB] Sottomissione [FEC_CMR] Cambia visuale [FEC_JMP] Salto [FEC_SPN] Scatto [FEC_HND] Freno a mano [FEC_LOL] Sguardo a sinistra [FEC_LOR] Sguardo a destra [FEC_NTR] Bersaglio successivo [FEC_PTT] Bersaglio precedente [FEC_LBA] Sguardo indietro [FEC_CEN] Centra visuale [FET_CCN] Classico [FET_SCN] Predefinito [FET_CFT] A PIEDI [FET_CCR] IN MACCHINA [FET_CAC] AZIONE [FEC_IBT] - [FEC_MXO] MXB1 [FEC_MXT] MXB2 [FEC_UNB] NON ASSEGNATO [FEC_TFL] Sguardo+Torretta a SX [FEC_TFR] Sguardo+Torretta a DX [FEC_MWF] VOLANTE SU [FEC_MWB] VOLANTE GIÙ [FEC_ORR] o [FEC_NUS] NON USATO [FEC_LUD] Sguardo su [FEC_LDU] Sguardo giù [FEC_CMP] COMBO: SGUARDO SX+DX [LAW_1A] law_1a [LAW_1B] law_1b [LAW_2A] law_2a [LAW_2B] law_2b [FEH_STA] STATISTICHE [FEH_LOA] CARICA [FEH_CON] COMANDI [FEH_AUD] AUDIO [FEH_DIS] VIDEO [FEH_LAN] LINGUA [FEH_SGA] NUOVA PARTITA [FEO_CON] Impostazioni controller [FEO_AUD] Impostazioni audio [FEO_DIS] Impostazioni video [FEO_LAN] Impostazioni lingua [FEO_PLA] Impostazioni giocatore [FEA_OUT] Uscita [FEA_ST] Stereo [FEA_DTS] DTS [FEA_RSS] Stazione radio [FEA_NON] NO RADIO [FEA_FM0] WILDSTYLE [FEA_FM1] FLASH FM [FEA_FM2] KCHAT [FEA_FM3] FEVER 105 [FEA_FM4] VROCK [FEA_FM5] VCPR [FEA_FM7] EMOTION 98.3 [FEA_FM8] WAVE 103 [FED_BRI] Luminosità [FED_TRA] Tracce : [FED_SUB] Sottotitoli [FED_WIS] Panoramico [FED_POS] Posizione schermo [FEP_RES] Riprendi [FEP_STG] Avvia partita [FEP_STA] Statistiche [FEP_BRI] Briefing [FEP_OPT] Opzioni [FEP_QUI] Esci [FES_LOA] Carica partita [FES_DEL] Elimina partita [FEC_CSU] Impostazioni controller [FEC_RED] Ridefinisci comandi [FEC_MOU] Impostazioni mouse [DISTGOL] Distanza percorsa su cart da golf (miglia) [DISTGOM] Distanza percorsa su cart da golf (metri) [ST_FAVR] Stazione radio preferita [ST_WSTR] Stazione radio meno amata [ST_FAVV] Veicolo preferito [ST_STAR] Totale stelle di sospetto [ST_HEAD] Colpi alla testa [ST_GANG] Gang meno amata [ST_STGN] Totale stelle di sospetto evase [TYREPOP] Pneumatici esplosi con proiettili [TYRESLA] Pneumatici tagliati con una lama [ST_BRK] Numero di uccisioni nel Bloodring [ST_LTBR] Permanenza massima nel Bloodring (sec) [ST_GNG1] Cubani [ST_GNG2] Haitiani [ST_GNG3] Teppistelli [ST_GNG4] Gang di Diaz [ST_GNG5] Sorveglianza [ST_GNG6] Gang di motociclisti [ST_GNG7] Gang di Vercetti [ST_GNG8] Giocatori di golf [FEA_FM6] ESPANTOSO [ST_ASSI] Contratti da sicario completati [DISTBIK] Distanza percorsa in moto (miglia) [DISTBIM] Distanza percorsa in moto (metri) [HOTEL] Hotel Ocean View [ICC1_1] ~g~Utilizza il camioncino dei gelati per distribuire droga in Vice City. [ICC1_2] ~g~Parcheggia il camioncino e premi ~h~~k~~VEHICLE_HORN~~w~ per attivare il campanello e attrarre i clienti. [ICC1_3] ~g~Riceverai soldi per ogni transizione, ma più ne esegui, maggiori saranno le probabilità di attirare l'attenzione della polizia. [KICK1_9] PUNTI DI CONTROLLO RIMANENTI: [FIN_B6] Non hai abbastanza soldi per avviare questa missione. [TEX3_3] ~g~Per raccogliere una bomba, avvicinati con l'elicottero: la bomba verrà automaticamente raccolta. [TEX3_9] ~g~Raccogli una bomba avvicinandole l'elicottero. [HELP22] Raggiungi la casa verde sul radar. [FES_FMS] Formattazione completata. Seleziona OK per continuare. [FES_SSC] Salvataggio completato. Seleziona OK per continuare. [FES_DSC] Eliminazione completata. Seleziona OK per continuare. [FESZ_QC] Vuoi sovrascrivere il salvataggio corrotto? [FES_CHE] Attenzione! Uno o più trucchi sono stati attivati: ciò influenzerà il salvataggio. Si consiglia di non salvare questa partita. [FET_SG] SALVA PARTITA [FEH_BRI] BRIEFING [FEH_MAP] MAPPA [FEM_OK] OK [FEC_CRO] Abbassarsi [FEC_CR3] Abbassarsi (tasto L3) [FEC_SMT] Sottomissione [FEC_SM3] Sottomissione (tasto R3) [FEC_RSC] Stazioni radio [ST_PR01] Apprendista [ST_PR02] Aviere [ST_PR03] Ufficiale [ST_PR04] Caporale [ST_PR05] Tenente [ST_PR06] Sergente [ST_PR07] Capitano [ST_PR08] Biggs [ST_PR09] Wedge [ST_PR10] Barone rosso [ST_PR11] Goose [ST_PR12] Viper [ST_PR13] Jester [ST_PR14] Chappy [ST_PR15] Iceman [ST_PR16] Maverick [ST_PR17] Noops [ST_PR18] Maresciallo [ST_PR19] Asso [FET_LG] CARICA PARTITA [CAR_EXP] Veicoli distrutti [BOA_EXP] Imbarcazioni distrutte [HEL_DST] Velivoli distrutti [STFT_01] Tempo migliore in 'Due ruote d'acciaio' [STFT_02] Tempo migliore in 'L'autista' [STFT_03] Tempo migliore nella prova del fango [STFT_04] Tempo migliore con gli aerei radiocomandati [STFT_05] Tempo migliore con le macchine radiocomandate [STFT_06] Tempo migliore di raccolta con l'elicottero [STFT_07] Tempo migliore in 'Terminal Velocity' [STFT_08] Tempo migliore in 'Ocean Drive' [STFT_09] Tempo migliore in 'Border Run' [STFT_10] Tempo migliore in 'Capital Cruise' [STFT_11] Tempo migliore in 'Tour!' [STFT_12] Tempo migliore in 'V.C. Endurance' [STHC_01] Punteggio migliore al poligono [STHC_02] Miglior percentuale di colpi al poligono [STHC_03] Numero di traffici di droga [HELP24] Adesso puoi accettare le missioni del Colonnello. [HELP25] Adesso puoi accettare le missioni di Avery Carrington. [HELP29] Puoi far visita al negozio di abbigliamento quando non sei in missione. [HELP30] Quando compri nuovi abiti, il tuo livello di sospetto scenderà a zero. [BJM2_22] ~r~Sei uscito dal poligono di tiro! [BJM2_23] ~g~Se esci dal poligono di tiro durante la competizione, fallirai la missione. [ASM4_24] Distanza: [RBM1_6] ~g~Porta Mercedes e la 'Love Juice' al gruppo nello studio di registrazione. [RBM1_3] NON NECESSARIO [HAM1_5] NON NECESSARIO [RBM1_11] NON NECESSARIO [HELP31] Per eseguire un assalto in macchina, guarda a destra o a sinistra premendo il tasto L2 o il tasto R2. [HELP34] Hai bisogno di una mitragliatrice per eseguire un assalto in macchina. [STRIP_1] ~r~Soldi insufficienti, brutto idiota. [EXIT_1] Premi ~k~~PED_SPRINT~ per uscire. [ASM1_A] Mr. Teal, il tuo aiuto nello sradicare gli stranieri è stato inestimabile per il nostro lavoro. Ho un altro compito con un contatto più 'diretto'. [ASM1_B] I dettagli per il prossimo colpo sono stati attaccati sotto al telefono. [ASM1_C] Ho un altro compito con un contatto più 'diretto'. [SCARF] Appartamento 3c [LAW4_10] La ricca amministrazione ci ha rotto! [GEN1_04] ~g~Attraversa la porta per accedere alla piattaforma sul tetto di Gonzalez. [GEN1_17] ~g~Gonzalez sta fuggendo! Seguilo attraverso le porte e finiscilo! [RCH1_9] ~b~TEMPO TOTALE: ~1~:~1~ [RCH1_10] ~b~TEMPO TOTALE: ~1~:0~1~ [WHEEL01] BONUS DOPPIO SU DUE RUOTE: ~1~$ Distanza: ~1~.~1~ m Tempo: ~1~ secondi [WHEEL02] BONUS DOPPIO SU DUE RUOTE: ~1~$ Distanza: ~1~ piedi Tempo: ~1~ secondi [WHEEL03] BONUS SU DUE RUOTE: ~1~$ Tempo: ~1~ secondi [WHEEL04] BONUS SU DUE RUOTE: ~1~$ Distanza: ~1~.~1~ m [WHEEL05] BONUS SU DUE RUOTE: ~1~$ Distanza: ~1~ piedi [WHEEL06] BONUS IMPENNATA: ~1~$ Distanza: ~1~.~1~ m Tempo: ~1~ secondi [WHEEL07] BONUS IMPENNATA: ~1~$ Distanza: ~1~ piedi Tempo: ~1~ secondi [WHEEL08] BONUS IMPENNATA: ~1~$ Tempo: ~1~ secondi [WHEEL09] BONUS IMPENNATA: ~1~$ Distanza: ~1~.~1~ m [WHEEL10] BONUS IMPENNATA: ~1~$ Distanza: ~1~ piedi [WHEEL11] BONUS FRENATA IN PUNTA: ~1~$ Distanza: ~1~.~1~ m Tempo: ~1~ secondi [WHEEL12] BONUS FRENATA IN PUNTA: ~1~$ Distanza: ~1~ piedi Tempo: ~1~ secondi [WHEEL13] BONUS FRENATA IN PUNTA: ~1~$ Tempo: ~1~ secondi [WHEEL14] BONUS FRENATA IN PUNTA: ~1~$ Distanza: ~1~.~1~ m [WHEEL15] BONUS FRENATA IN PUNTA: ~1~$ Distanza: ~1~ piedi [ROK3_72] Love Fist! [POR1_19] Ehi! [DESPERA] Desperado [MOB_99A] Raggiungi il telefono pubblico vicino al Mall in Washington. [MOB_98A] Raggiungi il telefono pubblico in Vice Point. [MOB_96A] Raggiungi il telefono pubblico al terminal dell'aeroporto. [MOB_95A] Raggiungi il telefono pubblico in Little Havana. [BNK1_1] La posso aiutare, signore? [BNK1_2] Un impostore! [BNK1_3] È impazzito! [BNK1_4] Ma chi diavolo sei? [BNK1_5] Dov'è il tuo distintivo? [BNK1_6] Eccoli là! Sparate per uccidere! [MOB_24A] Salve: Mr Vercetti? [MOB_24B] Sì. [MOB_24C] Sono Cortez. Eri invitato al mio party. [MOB_24D] Sì, ricordo. [MOB_24E] Mr. Vercetti, è stato davvero uno sfortunato incidente quello avvenuto al tuo accordo. [MOB_24F] Lo so. [MOB_24G] Sappi che io e la mia gente stiamo facendo il massimo per scoprire cosa è successo. [MOB_24H] Se vuoi parlarne in prima persona, mi troverai sulla mia barca. Buona giornata, senor. [BNK2_2] MIRARE 3-2-1 FUOCO! [BNK2_3] AREA RIPULITA! [BNK2_6] Questo tipo è pazzo! [ANGEL] Angel [CUBJET] Jetmax Cubano [SANDKIN] Sandking [POLMAV] Maverick della polizia [BOXVILL] Boxville [BENSON] Benson [HOTRINA] Hotring Racer [HOTRINB] Hotring Racer [BLOODRA] Bloodring Banger [BLOODRB] Bloodring Banger [MAFIACR] Cruiser della Mafia [COP_M2] 'VICE SQUAD' [COP_M3] 'BROWN THUNDER' [BJM2_20] ~g~Quando finisci il ~w~tempo~g~ o i ~w~colpi~g~, il turno è finito! [BNK3_2] Non intendo guidare per te, per niente, e lo dirò ai miei compagni di terapia. [FEM_SL1] Salvataggio 1 non trovato [FEM_SL2] Salvataggio 2 non trovato [FEM_SL3] Salvataggio 3 non trovato [FEM_SL4] Salvataggio 4 non trovato [FEM_SL5] Salvataggio 5 non trovato [FEM_SL6] Salvataggio 6 non trovato [FEM_SL7] Salvataggio 7 non trovato [FEM_SL8] Salvataggio 8 non trovato [FEA_CHA] Modifica uscita audio in STEREO. Attendere... [FEA_CHD] Attenzione! Modifica uscita audio da STEREO a DTS. Attendere... [FEI_SEL] Seleziona [FEI_BAC] Indietro [FEI_RES] Riprendi [FEI_NAV] Naviga [FEI_BTX] Tasto / - [FEI_BTT] Tasto " - [FEI_STA] Tasto START - [FEI_BTD] ; = > < - [FEI_STO] Stop [MOB_68A] Ehi Tommy, ho una sorpresa per te. [MOB_68B] Sono allo studio cinematografico con dei grandi artisti. [MOB_68C] Perché non vieni a farci visita? [MOB_68D] Lo sai che è una buona idea, vero? Ci vediamo. [OUTFT1] Da strada [OUTFT2] Eleganti [OUTFT3] Tuta [OUTFT4] Sportivo [OUTFT5] Havana [OUTFT6] Da poliziotto [OUTFT7] Da rapina [OUTFT8] Casual [OUTFT9] Mr. Vercetti [OUTFT10] Da corsa [OUTFT13] MC Tommy [HOTR_07] Nuovo miglior tempo: ~1~:0~1~ [HOTR_08] Tempo: ~1~:~1~ [GEN3_45] Arriveranno fra pochi minuti: troviamo un buon punto dove appostarci... [CAR_AS1] BENI CONCESSIONARIA ACQUISITI [CAR_AS2] ~g~La concessionaria d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. [BUYSAVE] ~g~Se non sei in missione, potrai ora salvare la partita da qui gratuitamente. [BUYGARG] ~g~Potrai anche depositare i mezzi in questo garage. [STRPBUY] Club Pole Position acquistato: ~1~$ [STRP_R3] Premi il tasto R3 per acquistare il club Pole Position per ~1~$ [NBMN_R3] Premi il tasto R3 per acquistare Elswanko Casa per ~1~$ [GA_4] Le bombe per le macchine costano 1000$ [GA_5] La tua macchina ha già una bomba installata. [GA_6] { reVC update } Parcheggiala, attivala premendo il ~h~~k~~VEHICLE_FIREWEAPON~~w~ e DATTELA A GAMBE! [GA_7] { reVC update } Arma la bomba con il ~h~~k~~VEHICLE_FIREWEAPON~~w~: esploderà non appena qualcuno cercherà di avviarla. [GA_6B] { reVC update } Parcheggiala, attivala premendo il ~h~~k~~VEHICLE_FIREWEAPON~~w~ e DATTELA A GAMBE! [GA_7B] { reVC update } Arma la bomba con il ~h~~k~~VEHICLE_FIREWEAPON~~w~: esploderà non appena qualcuno cercherà di avviarla. [MOB_70A] Tommy, sono io, il Colonnello Cortez. Ascolta senor, credo tu sia una persona capace di risolvere i problemi. [MOB_70B] Mi troverai sulla mia barca. [PICK1] Giubbotto antiproiettile consegnato all'hotel Ocean View! [PICK2] .357 consegnato al nascondiglio! [PICK3] Motosega consegnata al nascondiglio! [PICK4] Lanciafiamme consegnato al nascondiglio! [PICK5] .308 Fucile di precisione consegnato al nascondiglio! [PICK6] Mitragliatore consegnato al nascondiglio! [PICK7] Lanciamissili consegnato al nascondiglio! [PICK8] Sea Sparrow adesso disponibile alla villa! [PICK9] Carro armato adesso disponibile alle caserme! [PICK10] Hunter adesso disponibile alle caserme! [HELP41] o puoi speronarli con il tuo mezzo [ICC1_6] ~g~Utilizza il Mr. Whopee per distribuire i prodotti Cherry Poppers per Vice City. [ICC1_12] PROPRIETÀ ACQUISTATA! [CLOTH1] Abiti eleganti consegnati a Rafaels in Ocean Beach. [CLOTH2] Abiti da strada consegnati al nascondiglio. [CLOTH3] Tuta consegnata a Tooled Up nel Mall North Point. [CLOTH4] Abiti sportivi consegnati al club di Golf in Leafs Links. [CLOTH5] Abiti havana consegnati a Little Havana Streetwear in Little Havana. [CLOTH6] Abiti da poliziotto consegnati alla stazione di polizia in Washington Beach. [CLOTH7] Abiti casual consegnati a Gash nel Mall North Point. [CLOTH8] Abiti Mr. Vercetti consegnati a Collar & Cuffs in Ocean Beach. [CLOTH9] Abiti da corsa consegnati a Jocksport in Downtown. [CLOTH10] Abiti da rapina consegnati al club Malibu in Vice Point. [RBM1_9] ~g~Recupera della Love Juice dallo spacciatore per i Love Fist! [MOB_62A] Tommy, sono Ricardo Diaz. Ti volevo ringraziare per l'aiuto ai miei uomini. [MOB_62B] Ho chiesto a quel coglione di Cortez e mi ha detto che il vero affare sei tu. Amico, vienimi a trovare. [MOB_62C] Ho bisogno di un tipo come te. Sono circondato da coglioni, [MOB_62D] coglioni dappertutto, davvero. Ti farò diventare ricco. [GOAWAY2] ~g~Ritorna quando avrai completato le missioni dei motociclisti. [COL2_9] Idiota di un americano! Ti sei fatto seguire! [LOADCOL] Caricamento... [STFT_17] Tempo migliore in 'Parco divertimenti PCJ' [STFT_18] Tempo migliore in 'Prova del fango' [STFT_19] Tempo migliore in 'Tracciato di prova' [NEW_REC] Nuovo record!!! ~1~ minuti e ~1~ secondi. [BMX_HOW] ~g~Fai due giri sul tracciato sterrato, ~y~passando attraverso~g~ i ~y~PUNTI DI CONTROLLO~g~! [BMXREW1] ~g~Ogni volta che batti il tuo record precedente per i due giri, [BMXREW2] ~g~otterrai una ~y~RICOMPENSA~g~ più cospicua! [BMXRAIN] ~g~È come se piovesse... [ITBEG] All'inizio... [NBMNBUY] El Swanko Casa acquistata: ~1~$ [LNKVBUY] Appartamento Links View acquistato: ~1~$ [HYCOBUY] Condominio Hyman acquistato: ~1~$ [BUYGARS] ~g~Puoi parcheggiare altri veicoli in questi garage. [OCHEBUY] Appartamento Ocean Heights acquistato: ~1~$ [WASHBUY] 1102 Washington Street acquistata: ~1~$ [VCPTBUY] 3321 Vice Point acquistata: ~1~$ [SKUMBUY] Skumole Shack acquistato: ~1~$ [HELP6_C] Premi il ~h~~k~~VEHICLE_HANDBRAKE~~w~ per tirare il freno a mano. [HELP2_A] Premi il ~h~~k~~PED_SPRINT~~w~ mentre corri per effettuare uno ~h~scatto~w~. [HELP4_A] Premi il ~h~~k~~VEHICLE_ACCELERATE~~w~ per ~h~accelerare~w~. [HELP5_A] Premi il ~h~~k~~VEHICLE_BRAKE~~w~ per frenare o, se il veicolo è fermo, per inserire la retromarcia. [HELP8_A] Premi il ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per zoomare col fucile e il ~x~tasto /~w~ per allargare il campo. [PBOAT_1] { reVC update } Premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sparare con i cannoni della barca. [SEG3_4] { reVC update } ~g~Puoi raccogliere una bomba avvicinando il Raider telecomandato. Per posizionare la bomba, premi il ~h~~k~~VEHICLE_FIREWEAPON~. [RCR1_3] { reVC update } ~g~Se desideri interrompere la missione, premi il ~h~~k~~VEHICLE_FIREWEAPON~~g~ per far esplodere la macchina. [HELP32] { reVC update } Poi spara con il ~h~~k~~VEHICLE_FIREWEAPON~~w~. [HELP33] { reVC update } Poi spara con il ~h~~k~~VEHICLE_FIREWEAPON~~w~. [TTUTOR] Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Taxi. [TTUTOR2] Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Taxi. [FTUTOR] Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Camion dei pompieri. [FTUTOR2] Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Camion dei pompieri. [CTUTOR] Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Vigilante. [CTUTOR2] Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Vigilante. [HELP8_B] Premi il ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per ~h~zoomare~w~ col fucile e il ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ per ~h~allargare~w~ il campo. [ATUTOR3] Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ per attivare o disattivare le missioni Infermiere. [GUN_H1] ~w~Premi il ~h~~k~~PED_SPRINT~~w~ per comprare. ~w~Premi il ~h~~k~~VEHICLE_ENTER_EXIT~~w~ per uscire. [PU_CF3] { reVC update } Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per sostituire l'arma attuale con quella presente in questo slot. [PU_CF4] { reVC update } Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per sostituire l'arma attuale con quella presente in questo slot. [HELP9_B] Premi il ~h~~k~~PED_FIREWEAPON~~w~ per sparare con il fucile di precisione. [HELP37] Se non vuoi entrare in un veicolo mentre stai assalendo un guidatore, premi il ~h~~k~~PED_SPRINT~~w~. [HELP6_A] Premi il ~h~~k~~VEHICLE_HANDBRAKE~~w~ per tirare il freno a mano. [HELP6_D] Premi il ~h~~k~~VEHICLE_HANDBRAKE~~w~ per tirare il freno a mano. [HELP26] Premi il ~h~~k~~VEHICLE_ENTER_EXIT~~w~ per entrare o uscire da un veicolo. [HELP27] Premi il ~h~~k~~VEHICLE_TURRETUP~~w~ o ~h~~k~~VEHICLE_TURRETDOWN~~w~ per spostare il peso sulla moto. [HELP28] Premi il ~h~~k~~VEHICLE_TURRETUP~~w~ o ~h~~k~~VEHICLE_TURRETDOWN~~w~ per spostare il peso sulla moto. [HELP35] Premi il ~h~~k~~GO_LEFT~~w~ o la ~h~~k~~GO_RIGHT~~w~ per sterzare. [HELP36] Premi il ~h~~k~~GO_LEFT~~w~ o la ~h~~k~~GO_RIGHT~~w~ per sterzare. [HELP42] Segui il ~q~segnalino rosa~w~ per trovare l'hotel. [HELP19] Cammina sul ~q~segnalino rosa~w~ per continuare. [HELP1] Fermati al centro del ~q~segnalino rosa~w~. [HELP12] Cammina nel centro del ~q~segnalino rosa~w~ per attivare una missione. [SEG3_6] ~g~Per colpire con successo un bersaglio, devi sganciare la bomba nell'area con un ~q~segnalino rosa~g~. Puoi posizionare le bombe in un ordine qualsiasi. [S_PROMP] Quando non sei in missione, puoi salvare la partita raccogliendo il bonus ~h~audiocassetta~w~. [HELP16] Passa attraverso la porta principale dell'hotel ~h~Ocean View~w~ per entrare nell'edificio. [HELP43] ~g~Raggiungi l'hotel ~h~Ocean View~g~ in Ocean Drive. [HELI_F1] ~r~Gara a tappe con l'elicottero annullata! [AMMUHLP] Se hai bisogno di armi, fai un salto ad ~h~Ammu-Nation~w~. Segui il ~h~segnale a forma di pistola~w~ sul radar. [HELI_1] Tappa elicottero Downtown [HELI_2] Tappa elicottero Ocean Beach [HELI_3] Tappa elicottero Vice Point [HELI_4] Tappa elicottero Little Haiti [FST_MFR] Stazione radio preferita [FST_LFR] Stazione radio meno amata [FEI_HOL] Trattieni [FEI_ZOO] Zoom [FEI_BTR] > < - [FEI_NA] N\D [MESA] Mesa Grande [STRP_NO] Non puoi ancora comprare il club a luci rosse, prova più tardi. [CHSE] INSEGUI [NBMN_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare El Swanko Casa per ~1~$ [NBMN_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare El Swanko Casa per ~1~$ [NBMN_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare El Swanko Casa per ~1~$ [LNKV_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Links View per ~1~$ [LNKV_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Links View per ~1~$ [LNKV_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Links View per ~1~$ [HYCO_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il condominio Hyman per ~1~$ [HYCO_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il condominio Hyman per ~1~$ [HYCO_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il condominio Hyman per ~1~$ [OCHE_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Ocean Heights per ~1~$ [OCHE_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Ocean Heights per ~1~$ [OCHE_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare l'appartamento Ocean Heights per ~1~$ [WASH_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 1102 Washington Street per ~1~$ [WASH_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 1102 Washington Street per ~1~$ [WASH_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 1102 Washington Street per ~1~$ [VCPT_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 3321 Vice Point per ~1~$ [VCPT_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 3321 Vice Point per ~1~$ [VCPT_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare 3321 Vice Point per ~1~$ [SKUM_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo Skumole Shack per ~1~$ [SKUM_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo Skumole Shack per ~1~$ [SKUM_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo Skumole Shack per ~1~$ [PRNT_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la tipografia per ~1~$ [PRNT_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la tipografia per ~1~$ [PRNT_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la tipografia per ~1~$ [CAR_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il concessionario per ~1~$ [CAR_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il concessionario per ~1~$ [CAR_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il concessionario per ~1~$ [PORN_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo studio cinematografico per ~1~$ [PORN_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo studio cinematografico per ~1~$ [PORN_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare lo studio cinematografico per ~1~$ [ICE_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la fabbrica di gelato per ~1~$ [ICE_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la fabbrica di gelato per ~1~$ [ICE_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la fabbrica di gelato per ~1~$ [TAXI_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la compagnia di taxi per ~1~$ [TAXI_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la compagnia di taxi per ~1~$ [TAXI_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la compagnia di taxi per ~1~$ [BANK_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il Malibu per ~1~$ [BANK_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il Malibu per ~1~$ [BANK_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il Malibu per ~1~$ [BOAT_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il cantiere navale per ~1~$ [BOAT_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il cantiere navale per ~1~$ [BOAT_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il cantiere navale per ~1~$ [STRP_L] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il club Pole Position per ~1~$ [STRP_T] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il club Pole Position per ~1~$ [STRP_C] Premi il ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare il club Pole Position per ~1~$ [STOCK] ~r~scorte esaurite [HELP14] Per trovare l'ufficio dell'avvocato, segui la ~h~L~w~ sul radar. [RAMPAGE] VIOLENZA! [RAMP_F] VIOLENZA FALLITA! [RAMP_P] VIOLENZA COMPIUTA! [RAMP_A] TUTTE LE VIOLENZE COMPIUTE! [PAGE_01] Uccidi ~1~ membri della gang in 2 minuti! [PAGE_02] Distruggi ~1~ veicoli in 2 minuti! [PAGE_03] Uccidi sparando in corsa ~1~ membri della gang in 2 minuti! [PAGE_04] Travolgi ~1~ membri della gang in 2 minuti! [PAGE_05] Fai saltare la testa a ~1~ membri della gang in 2 minuti! [SENTXS] Sentinel XS [MAP_LEG] Legenda [VCNMAV] VCN Maverick [LG_01] Posizione giocatore [LG_02] Avery Carrington [LG_03] Contatto motociclisti [LG_04] Colonnello Cortez [LG_05] Ricardo Diaz [LG_06] Kent Paul [LG_07] Avvocato [LG_08] Phil Cassidy [LG_09] Cantiere navale [LG_10] Club Malibu [LG_11] Cubani [LG_12] Studio cinematografico [LG_13] AmmuNation [LG_14] Haitiani [LG_15] Ferramenta [LG_16] Nascondiglio [LG_17] Fabbrica di gelato [LG_18] Taxi Kaufman [LG_19] Love Fist [LG_20] Tipografia [LG_21] Proprietà [LG_22] Pay 'n' Spray [LG_23] Negozio di vestiario [LG_24] Dimora di Tommy [LG_25] Telefono [LG_26] Stazione radio Wildstyle [LG_27] Stazione radio Flash FM [LG_28] Stazione radio KChat [LG_29] Stazione radio Fever 105 [LG_30] Stazione radio VROck [LG_31] Stazione radio VCPR [LG_32] Stazione radio Espantoso [LG_33] Stazione radio Emotion 98.3 [LG_34] Stazione radio Wave 103 [LG_36] Sun Yard [LG_37] Club a luci rosse [MAP_YAH] TU SEI QUI [TAXSHRT] ~g~Puoi utilizzare questo taxi Kaufman per arrivare a destinazione invece di guidare. Ti costerà 9$. [FE_MLG] LEGENDA MAPPA [FED_RDR] RADAR [FED_HUD] INTERFACCIA [FED_RDL] GRANDE [FED_RDB] SOLO SEGNALI [FED_HUF] DISSOLVENZA [FEST_HV] Massimo livello missione Vigilante [BRIBE1] Hai appena raccolto una bustarella della polizia: il tuo livello di sospetto è sceso di una stella. [CLOHELP] Abiti puliti! [SUNSHIN] Sunshine Autos [CHERRYP] Gelateria Cherry Popper [KAUFCAB] Taxi Kaufman [BOATYAR] Cantiere navale [WANT_L] Il tuo livello di sospetto è sospeso: se commetterai un crimine mentre le stelle lampeggiano, il tuo livello di sospetto verrà ripristinato. [HOTRNG] HOTRING [BLODRNG] BLOODRING [DIRTRNG] DIRTRING [FEC_ABR] Accelera, frena o inverti marcia [FEI_BTU] ; = - [FEI_SCR] Scorri [LG_35] Destinazione [BOAT_AS] ~g~Il cantiere navale d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. [BOAT_A2] CANTIERE NAVALE COMPLETATO [BOAT_N] Tappa Charlie [BOAT_P] ~g~Raccogli i pacchi prima dello scadere del tempo. [FEI_R1B] Tasto R1 \ R2 - [HELP9_A] Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per sparare con il fucile di precisione. [HELP21] Premi il tasto ~h~~k~~VEHICLE_ENTER_EXIT~~w~ per entrare o uscire da un veicolo. [CREAM] Distribuzione [UMBERTO] Cafe Robina [PU_CF1] Premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w~ per raccogliere quest'arma e sostituirla con qualsiasi altra dello stesso tipo in tuo possesso. [FED_RDM] MAPPA E INDICATORI [FEC_ILU] Sguardo invertito in prima persona : [NITRO] Tutti i taxi adesso hanno il super salto! Premi il pulsante del clacson. [RATNG53] Inaffidabile [RATNG54] Imbarazzante [RATNG55] Hacker [RATNG56] Baro [RATNG57] Bugiardo assoluto [STHC_04] Miglior punteggio con la palla sulla spiaggia [STHC_05] Miglior risultato Hotring [STFT_13] Tempo migliore corsa in elicottero a Downtown [STFT_14] Tempo migliore corsa in elicottero a Ocean Beach [STFT_15] Tempo migliore corsa in elicottero a Vice Point [STFT_16] Tempo migliore corsa in elicottero a Little Haiti [STFT_21] Tempo migliore nell'Hotring [STFT_22] Giro migliore nell'Hotring [STFT_20] Tempo migliore in 'Cone Crazy' [HELP44] Fermati nel ~q~ segnalino rosa. [HELP45] Premi il ~h~~k~~PED_DUCK~~w~ per abbassarti. In questo modo migliorerai la precisione con le armi da fuoco. [RCR1_5] Corsa Bandit radiocomandati [RCPL1_7] Corsa Baron radiocomandati [RCH1_11] Raccolta Raider radiocomandato [FEA_CTD] Attenzione! Questa caratteristica richiede delle periferiche compatibili DTS. Vuoi procedere? [FEM_STE] USA STEREO [FEM_UDY] USA DTS [GREET] Saluti da... [LANCE_1] Ehi bello, guida con più prudenza! [LANCE_2] Ehi, guarda cosa hai combinato! [LANCE_3] Ehi, ma dove stai andando adesso? [LANCE_4] Cosa diavolo stai facendo? [LAW4_15] Più soldi! [MERC_5] Bella machina, Mr. Vercetti. [MERC_26] PIÙ VELOCE, PIÙ VELOCE, PIÙ VELOCE! [MERC_27] Attento Tommy, mi hanno rifatto il naso il mese scorso. [MERC_28] Tommy, guida con attenzione. [MERC_29] Tommy, vai più piano! [MERC_30] Tommy per favore, fai fuori qualcun altro! [MERC_31] Tommy, baby, non farmi fuori! [MERC_32] Tommy, sono felice che hai rubato questa macchina! [MERC_40] Mi sono davvero divertita. [MERC_43] Adios, angioletto. [MERC_44] Continua a lavorarci, capito? [MERC_45] Ciao, bellezza. [COL5_17] Oddio, hanno un elicottero! [COL5_18] Abbatti l'elicottero! [COL5_19] Tommy, abbatti quell'elicottero! [COL5_20] Sta tornando! Distruggi l'elicottero! [COL5_21] Ma guarda quanto è grosso! [COL5_22] Sta tornando di nuovo! [FEA_DSM] Attenzione! Questo salvataggio utilizza il DTS e richiede periferiche compatibili. Scegli se vuoi procedere utilizzando l'uscita DTS o quella STEREO. [STFT_23] Tempo migliore per la tappa Charlie [HELP50] Premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w~ per posizionare la visuale da dietro. [HELP51] Premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w ~ per posizionare la visuale da dietro. [HELP52] Premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w ~ per posizionare la visuale da dietro. [HELP53] Premi il tasto ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~ o il tasto ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~ per passare in rassegna le armi disponibili. [HELP46] Sono disponibili otto diversi tipi di arma. [HELP47] Puoi trasportare una sola arma per tipo alla volta: un tipo di pistola, un tipo di fucile a pompa... [HELP54] ~w~Costo: ~1~$ ~r~Comprando quest'arma sostituirai quella in uso. [HELP2A2] Premi il tasto ~h~~k~~PED_SPRINT~~w~ mentre corri per effettuare uno ~h~scatto~w~. [HLPSN_A] Il fucile di precisione ti permette di ingrandire l'immagine e colpire i bersagli con precisione dalla distanza. [HLPSN_B] Tieni premuto il tasto ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ col fucile di precisione. [HLPSN_C] Tieni premuto il tasto ~h~L1~w~ per ~h~mirare~w~ col fucile di precisione. [HLPSN_D] Premi il tasto ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per ~h~zoomare~w~ col fucile e il tasto ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ per ~h~allargare il campo~w~. [HLPSN_E] Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile di precisione. [HLPSN_F] Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile di precisione. [HLPSN_G] Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile di precisione. [PLANE_H] Usa il tasto ~h~~k~~VEHICLE_ACCELERATE~~w~ per accelerare e a sinistra e a destra per curvare. [PLANE_4] { reVC update } {Usa il tasto ~h~~k~~VEHICLE_ACCELERATE~~w~ per accelerare e a sinistra e a destra per curvare.} Usa la levetta analogica destra per accelerare, premi la levetta analogica sinistra in basso per salire, in alto per scendere e a sinistra e a destra per curvare. [HELP55] Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per attaccare lo chef. [STPR_8] Club Pole Position [STPR_9] 3321 Vice Point [STPR_10] Appartamento Links View [STPR_11] El Swanko Casa [STPR_12] 1102 Washington Street [STPR_13] Appartamento Ocean Heights [STPR_14] Skumole Shack [STPR_15] Condominio Hyman [RCCANX] ~r~Aereo radiocomandato annullato. [CLT_HL2] Quando viene raccolto un bonus vestiti, vengono rimosse una o due stelle del livello di sospetto. [CRED009] PROGETTAZIONE MISSIONI [CRED359] LEE JOHNSON [CRED360] HENDRIK LESSER [CRED361] PASQUALE STACCHIOTTI [CRED362] ENRIQUE FERNANDEZ [CRED363] PAUL BYERS [CRED364] MIKE EMENY [CRED365] ROB DUNKIN [CRED366] CHARLIE KINLOCH [CRED367] KEVIN HOBSON [CRED368] JIM CREE [MOB_66A] Tommy, Tommy, Tommy, perché sei tornato qua? [MOB_66B] Te l'avevo detto che non vi volevamo più vedere. [MOB_67A] Tommy, credo che dovresti andartene, capito? [MOB_67B] I ragazzi Haitiani non sono molto felici di te. [MOB_18A] Tommy, sono Paulo, come stai? Bene amico, ascolta: volevo farti una chiamata. [MOB_18B] Ossignore, amico, non crederai mai che razza di strafiga ho appena incontrato. [MOB_18C] Una prostituta forse, passeggiava per Little Havana. [MOB_18D] Ha detto che si chiamava Mercedes o qualcosa del genere. [MOB_18E] Oddio, dovresti proprio vedere questa gnocca. [MOB_18F] Riuscirebbe a succhiare via la mina da una matita. Mi detto che ero il migliore che avesse mai avuto e via dicendo. [MOB_18G] Dovresti farci un giro. Ci vediamo! [MOB_72A] Tommy, sono io, Lance. Tieni la bocca chiusa Tommy, perché non ho tempo da perdere. [MOB_72B] Non mi importa cos'hai da dire. Perché dovrei? Non te ne frega niente di me, non è vero? [MOB_72C] Dovresti fare più attenzione a me. Dovresti darmi la fetta che merito. Lo sai... [MOB_72D] Tommy... senti amico, mi dispiace. È solo che... [MOB_72E] La gente mi ha sempre trattato con accondiscenda, come se fossi un bambino. [MOB_72F] Mio fratello lo farebbe. Per favore, amico, tu non farlo. [MOB_72G] Devo andare. [MOB_63A] Tommy, sono Earnest. Earnest Kelly. [MOB_63B] Come stai? [MOB_63C] Non male. Avrò bisogno di un bastone per camminare, ma dovrei tornare al lavoro ben presto. [MOB_63D] Bene. [MOB_63E] Ho sentito di Lance. Che brutto storno, eh? [MOB_63F] Sì. [MOB_63G] Mai fidarsi di qualcuno che cammina per strada in pigiama. Ecco cosa dico. Sono felice che l'hai fatto fuori. Spero che sia stato doloroso per lui. [MOB_63H] Credo di sì. Non credevo fosse quel genere di persona... [MOB_63I] Tommy, per essere un pazzo violento, sei piuttosto ingenuo. Tornerò presto al lavoro e ti insegnerò un paio di cose sulla vita. Ci sentiamo. [MOB_63J] Fai con calma, Earnest. Abbi cura di te. [MOB_16A] Tommy, sono Paulo, que pasa amigo? [MOB_16B] Che cosa vuoi Paul: non sono interessato a vestiti di marca taroccati. [MOB_16C] Molto divertente, amico, ma lo sai che non tratto merce di seconda classe. Nah, ti ho chiamato per sapere se potevo avere una parte nei tuoi film. [MOB_16D] Quando ero in Inghilterra ho fatto un sacco di cose, sai? Ho attributi più forniti di te, amico. [MOB_16E] Paul, grazie per l'offerta, lo terrò presente. [MOB_16F] Davvero, non scordarti di me dopo tutto quello che ho fatto per te. [MOB_16G] È proprio ciò che sto cercando di dimenticare... [MOB_17A] Tommy Vercetti: come va, Mr. Pezzo Grosso? Ho saputo le tue novità: un vero giocatore in città, eh... [MOB_17B] Paul, sei ubriaco. [MOB_17C] No, idiota. Non sono ubriaco. Solo un paio di bicchierini e qualche pillola: non dormo da un paio di giorni, sai... [MOB_17D] Comunque, niente prediche. Non sono uno stupido. Chi ti ha portato in questa città? Chi? Io, ecco chi. [MOB_17F] Davvero? [MOB_17G] Niente prediche, smettila! Ti ho fatto conoscere la gente giusta, ti ho mostrato che corde tirare. Ho fatto molto per te e questo è il modo con cui mi ringrazi. [MOB_17H] Mi ignori. Non mi aiuti a entrare dopo tutto ciò che ho fatto per te! Cosa credi che sia? Un idiota? [MOB_17I] Paul, sta' buono. Sono stato occupato, non fare l'idiota. [MOB_17J] Non sono un idiota, chiaro? Questo è ciò che hanno detto in riformatorio. Stai forse cercando guai, amico? Beh, stai per trovarli! [MOB_17K] Tommy, amico. Per favore. Sei la mia più grande speranza! Per favore, non ridere di me. [MOB_17L] Paul, cerca di dormire, davvero. [MOB_73A] Tommy, sono Steve. [MOB_73B] Ehi Steve. [MOB_73C] Ehi, ciao genio. Sei uno spettacolo! Anch'io sono uno spettacolo! Ci adorano. Stiamo battendo tutti i record, amico. [MOB_73D] Stiamo ottenendo dei grandi riconoscimenti. Finalmente posso comprare una casa al mio vecchio così se ne starà zitto. [MOB_73E] Eeer, mi sembra perfetto, Steve. [MOB_73F] Perfetto? È meraviglioso! Meraviglioso, MERAVIGLIOSO! Non ha mai creduto in me. Non pensava fossi un artista, ma adesso ce l'ho fatta. [MOB_73G] Sono il miglior direttore sadomaso di tutti i tempi, amico. Volevo solo dirti che è stato un piacere averti incontrato. [MOB_73H] Grazie Steve. [MOB_73I] Ti voglio bene, amico. Non cambiare, capito? [MOB_73J] Capito. Ci sentiamo Steve. [BOLLOX] Premi il tasto ~o~R1~w~ per sganciare una bomba. Premi il tasto ~t~"~w~ per annullare. [BRID_OP] Pericolo uragano terminato: tutti i ponti per la terra ferma sono nuovamente aperti. [BRID_CL] Pericolo uragano: tutti i ponti per la terra ferma sono chiusi. [LG_38] Bersaglio [ASSET_C] POLE POSITION COMPLETATA! [ASSET_D] ~g~ Il Club Pole Position generera' un reddito fino ad un massimo di $~1~ al giorno. Ritira il denaro regolarmente! [ST_WHEE] Impennata più lunga (sec) [ST_STOP] Frenata in punta più lunga (sec) [ST_2WHE] Permanenza su 2 ruote più lunga (sec) [ST_WHED] Distanza piu' lunga in impennata (m) [ST_STOD] Distanza piu' lunga in frenata (m) [ST_2WHD] Distanza piu' lunga su 2 ruote (m) [OUTFT11] Tuta da ginnastica [OUTFT12] Frankie [RELOAD] ~g~Hai vinto l'abilita' di ricarica veloce! [APACHE] Hunter consegnato all'eliporto di Ocean Beach [CRED369] JOHN MCCARDLE [CRED370] DAVID MURDOCH [CRED371] CHRIS BROWN [CRED372] PAUL GREEN [CRED373] KYLE MILNE [CUNTY] Nuovi vestiti consegnati alla Proprieta' di Vercetti! [GOODBOY] $50 Come Bonus Buon Cittadino! [NEWCONT] Nuovo ~h~Punto di Contatto~w~ disponibile alla Marina di Ocean Beach!! [FIRELVL] Missione Camion dei pompieri livello ~1~ [HELP56] Premi il tasto ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ per cambiare la modalità di visuale. [HELP57] Premi il tasto ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ per cambiare la modalità di visuale. [HELP58] Mentre prendi la mira, premi il ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~, ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~ per cambiare bersaglio. [HELP59] Mentre prendi la mira, premi il ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~, ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~ per cambiare bersaglio. [HELP60] Se premi il tasto ~h~~k~~PED_SPRINT~~w~ mentre assalti una macchina, non entrerai nel veicolo. [HELP61] Adesso hai colpi infiniti e il doppio dell'energia su tutti i veicoli. [CRED374] KEVIN YUN [CRED375] ERICK COBBS [CRED376] RANDY BLAKE [CRED377] BRANDON LIM [CRED378] BRANDON FENOL [CRED379] MICHAEL MANOLE [CRED380] ALETHEIA SIMONSON [CRED381] JOHN JANSEN [FEC_LB1] Guarda [FEC_LB2] indietro [FEC_LB3] Guarda indietro [FEC_R3] (tasto R3) [FEC_PED] Comandi a piedi [FEC_VEH] Comandi in un veicolo [FEC_FPR] Comandi in prima persona [FEC_CMM] Comandi comuni [FEC_PWL] Cammina a sinistra [FEC_PWR] Cammina a destra [FEC_PWF] Cammina in avanti [FEC_PWT] Cammina verso la telecamera [FEC_PLB] Guardati indietro [FEC_PFR] Spara con l'arma [FEC_CLE] Arma precedente [FEC_CRI] Arma successiva [FEC_LKT] Aggancia bersaglio [FEC_PJP] Salto Ped [FEC_PSP] Scatto Ped [FEC_PSH] Sparo Ped [FEC_TLF] Bersaglio successivo a sinistra [FEC_TRG] Bersaglio successivo a destra [FEC_CCM] Centra visuale dietro al giocatore [FEC_SZI] Ingrandimento fucile di precisione [FEC_SZO] Riduzione fucile di precisione [FEC_LKL] Sguardo a sinistra [FEC_LRT] Sguardo a destra [FEC_LUP] Sguardo verso l'alto [FEC_LDN] Sguardo verso il basso [FEC_LBH] Guarda dietro al veicolo [FEC_LLF] Guarda a sinistra del veicolo [FEC_LRG] Guarda a destra del veicolo [FEC_HRN] Clacson [FEC_HBR] Freno a mano [FEC_ACL] Acceleratore [FEC_BRK] Freno [FEC_TSM] Attiva/Disattiva sottomissioni [FEC_CRD] Cambia stazione radio [FEC_ENT] Entra/Esci da un veicolo [FEC_WPN] Spara con l'arma [FEC_PAS] Pausa [FEC_FPO] Attiva/Disattiva visuale di puntamento [FEC_SMS] Mostra/Nascondi puntatore del mouse [FEC_CMS] Cambia la modalità di visuale in qualsiasi situazione [FEC_TSS] Salva un'immagine di gioco [FEC_DBG] Menu debug [FEC_TGD] Pad game/Debug [FEC_TDO] Disabilita visuale debug [FEC_IVH] Inverti mouse orizzontalmente [FEC_MSL] PULSANTE SX [FEC_MSM] PULSANTE CEN [FEC_MSR] PULSANTE DX [FEC_QUE] ??? [FEC_TWO] Un massimo di due tasti per comando. [FEC_UMS] Un pulsante del mouse può essere assegnato a un solo comando! [FEC_OMS] Solo un pulsante del mouse. [FEC_UJS] Un pulsante del joystick può essere usato per un solo comando! [FEC_OJS] Solo un pulsante del joystick. [FEC_PTL] Usa Aggancia bersaglio con Arma precedente [FEC_PTR] Usa Aggancia bersaglio con Arma successiva [FEC_LBC] Usa Guarda a sinistra con Guarda a destra [FEC_JBO] JOY ~1~ [FEC_WAR] Attenzione [FEC_OKK] OK [FEC_DLF] Eliminazione fallita. [FEC_SVU] Salvataggio fallito. [FEC_LUN] Caricamento fallito. File corrotto: è necessario eliminarlo. [FEC_PAD] Gamepad [FEC_JOY] Joystick [FES_CSA] Seleziona una skin dalla seguente lista: [FET_HRD] IMPOSTAZIONI PREDEFINITE RIPRISTINATE [FET_MST] STERZO VIA MOUSE [FEC_STR] NUM ASTERISCO [FET_MIG] SINISTRA, DESTRA, ROTELLA PER MODIFICARE [FET_CIG] INDIETRO PER AZZERARE - PULSANTE SX, INVIO PER MODIFICARE [FET_DSN] Skin predefinita.bmp [FET_RSO] RIPRISTINATE IMPOSTAZIONI ORIGINALI [FET_RSC] HARDWARE NON DISPONIBILE - RIPRISTINATE IMPOSTAZIONI ORIGINALI [FEA_3DH] HARDWARE AUDIO [FEA_SPK] CONFIGURAZIONE ALTOPARLANTI [FEM_LOD] DISTANZA VISUALE [FEM_VSC] SINCRONIA FRAME [FEM_FRM] LIMITATORE FRAME [FEM_MM] MENU PRINCIPALE [FED_RES] RISOLUZIONE [FET_CTL] IMPOSTAZIONI DEI COMANDI [FET_OPT] OPZIONI [FEC_MSH] SENSIBILITÀ MOUSE [FEC_IVV] INVERTI MOUSE VERTICALMENTE [FET_MTI] CONFIGURAZIONE COL MOUSE [FEC_FNC] F~1~ [FEC_IRT] INS [FEC_DLL] CANC [FEC_HME] HOME [FEC_END] FINE [FEC_PGU] PAG SU [FEC_PGD] PAG GIÙ [FEC_UPA] SU [FEC_DWA] GIÙ [FEC_LFA] SINISTRA [FEC_RFA] DESTRA [FEC_NUM] NUM [FEC_NMN] NUM~1~ [FEC_FWS] NUM / [FEC_PLS] NUM + [FEC_MIN] NUM - [FEC_DOT] NUM . [FEC_NLK] BLOC NUM [FEC_ETR] INVIO [FEC_SLK] BLOC SCORR [FEC_PSB] PAUSA [FEC_BSP] INDIETRO [FEC_TAB] TAB [FEC_CLK] BLOC MAIUSC [FEC_RTN] RET [FEC_LSF] MAIUSC SX [FEC_RSF] MAIUSC DX [FEC_LCT] CTRL SX [FEC_RCT] CTRL DX [FEC_LAL] ALT SX [FEC_RAL] ALT DX [FEC_LWD] WIN SX [FEC_RWD] WIN DX [FEC_WRC] CLIC WIN [FEC_SPC] BARRA [WIN_TTL] Grand Theft Auto VC [WIN_95] Grand Theft Auto VC non è supportato da Windows 95 [WIN_DX] Grand Theft Auto VC richiede DirectX versione 8.1 o superiore [FET_EIG] IMPOSSIBILE ASSEGNARE UN COMANDO A QUESTA AZIONE [FET_DAM] MODELLAZIONE ACUSTICA DINAMICA [FEQ_SRE] Sei sicuro di voler uscire? Tutti i progressi dall'ultimo salvataggio verranno persi. Vuoi procedere? [FEQ_SRW] Sei sicuro di voler uscire dal gioco? [FET_QG] ESCI DAL GIOCO [FEN_STA] AVVIA LA PARTITA [FET_PAU] MENU PAUSA [REPLAY] REPLAY [FEC_ANS] Azione [CVT_MSG] Conversione texture per prestazioni ottimali con la tua scheda video [FEC_SFT] MAIUSC [FEH_VMP] VEDI MAPPA [FES_DEE] Eliminazione fallita! Prova nuovamente. [FES_CMP] Salvataggio fallito! Prova nuovamente. [FESZ_WR] Salvataggio partita. Un momento... [FELD_WR] Caricamento partita. Un momento... [FEDL_WR] Eliminazione salvataggio. Un momento... [PCRESRT] Avvio nuova partita. Un momento... [FET_STI] Configurazione standard [FET_CTI] Configurazione classica [FET_PS] IMPOSTAZIONE SKIN GIOCATORE [FEH_NA] OPZIONE NON DISPONIBILE [FEH_MPH] MOUSE, FRECCE PER MUOVERSI - PAGSU, PAGGIÙ, ROTELLINA PER LO ZOOM, L - LEGENDA [FEA_MP3] RIPRODUTTORE MP3 [NO_PCCD] Inserisci il CD di GTA Vice City o premi Esc per annullare [FEH_SSA] FRECCE PER MUOVERSI - S PER SALVARE [FES_CMI] ULTIMA MISSIONE COMPLETATA [FET_STS] STATISTICHE SALVATE NEL FILE 'STATS.HTML' + 'STATS.TXT' [WIN_VDM] Memoria video insufficiente per eseguire Grand Theft Auto VC [FEC_ERI] Errore! Uno o più azioni non sono assegnate a nessun tasto o pulsante. Assicurati che a tutte le azioni corrisponda un comando. [FEC_TFU] Torretta + Inclina in su [FEC_TFD] Torretta + Inclina in giù [FET_RIG] SELEZIONA IL NUOVO COMANDO PER QUESTA AZIONE [FEA_NM3] NESSUN FILE MP3 TROVATO [FEA_MPB] INCREMENTO VOLUME MP3 [FEA_MUS] VOLUME MUSICA [FEA_SFX] VOLUME EFFETTI [CVT_ERR] Spazio su disco esaurito; è necessario liberare dello spazio prima di poter procedere. Premi ESC per annullare. [FEA_ADP] RILEVAMENTO AUTOMATICO {=================================== MISSION TABLE AMBULAE ===================================} [ATUTOR2:AMBULAE] ~g~Conduci i pazienti all'ospedale guidando CON PRUDENZA! Qualsiasi scossone può ucciderli. [A_FULL:AMBULAE] ~r~Ambulanza piena! [A_FAIL2:AMBULAE] ~r~La tua scarsa sollecitudine è stata fatale al paziente! [A_FAIL3:AMBULAE] ~r~Il paziente è morto! [A_PASS:AMBULAE] Salvato! [A_COMP2:AMBULAE] Non ti stancherai mai! [A_COMP1:AMBULAE] Missioni Infermiere completate: ~1~$ [A_CANC:AMBULAE] ~r~Missione Infermiere annullata! [A_COMP3:AMBULAE] Missione Infermiere completata! Non ti stancherai mentre corri! [ALEVEL:AMBULAE] Missione Infermiere livello ~1~ [A_FAIL1:AMBULAE] Missione Infermiere terminata. [A_SAVES:AMBULAE] PERSONE SALVATE: ~1~ {=================================== MISSION TABLE ASSIN1 ===================================} [ASM1_5:ASSIN1] ~r~Ha completato le sue consegne! [ASM1_6:ASSIN1] Consegne rimanenti: [ASM1_7:ASSIN1] ~g~Carl Pearson, fattorino di una pizzeria. Uccidilo prima che completi le sue consegne. [ASM1_D:ASSIN1] Mr. Teal, il tuo aiuto nello sradicare gli stranieri è stato inestimabile per il nostro lavoro. {=================================== MISSION TABLE ASSIN2 ===================================} [ASM2_1:ASSIN2] ~g~Mrs. Dawson lascerà presto la gioielleria in Vice Point. Devi ucciderla. Deve sembrare un incidente d'auto. [ASM2_3:ASSIN2] ~g~Sta per esplodere! Allontanati! [ASM2_4:ASSIN2] ~r~Hai danneggiato la sua auto, ma lei non è sopra! Adesso non ci entrerà di sicuro! [ASM2_5:ASSIN2] ~r~È riuscita a scappare! [ASM2_6:ASSIN2] ~r~Sei stato visto troppo vicino alla scena dell'incidente! [ASM2_7:ASSIN2] ~g~Non utilizzare armi! Deve sembrare un incidente! Spingila fuori strada! [ASM2_8:ASSIN2] ~g~La morte di Mrs. Dawson deve sembrare un incidente. Non utilizzare armi. [ASM2_9:ASSIN2] ~g~Avrai bisogno di una vettura per questo lavoro! [ASM2_10:ASSIN2] ~g~Quando la sua macchina prende fuoco, allontanati il più possibile dal luogo dell'incidente. [ASM2_11:ASSIN2] Aiuto! [ASM2_12:ASSIN2] Qualcuno mi aiuti! [ASM2_13:ASSIN2] Oddio! [ASM2_A:ASSIN2] Complimenti per l'ottimo lavoro, Mr. Teal. Il mio cliente è molto soddisfatto. [ASM2_2:ASSIN2] Salute: {=================================== MISSION TABLE ASSIN3 ===================================} [ASM3_11:ASSIN3] TEMPO: [ASM3_C:ASSIN3] Una gang europea ha pianificato un furto in banca a Vice City. I miei clienti preferirebbero che il colpo non vada a segno. [ASM3_D:ASSIN3] Ogni membro della gang ha un nascondiglio nella città: alcuni hanno dei lavori di copertura, altri sono in vacanza. [ASM3_E:ASSIN3] Dettagli e posizione più probabile di ogni bersaglio sono riportati nel nastro sotto al telefono. [ASM3_14:ASSIN3] ~g~Dick Tanner si trova presso la DBP Security in Ocean Drive. [ASM3_15:ASSIN3] ~g~Marcus Hammond e Franco Carter si trovano presso la gioielleria in Vice Point. [ASM3_16:ASSIN3] ~g~Nick Kong si trova presso Washington Beach. [ASM3_18:ASSIN3] ~g~Non avvicinarti troppo, se no il bersaglio potrebbe vederti e sarai costretto a inseguirlo! [ASM3_19:ASSIN3] ~g~Ti ha visto! Fallo fuori! [ASM3_20:ASSIN3] ~g~Ti hanno visto! Falli fuori entrambi! [ASM3_21:ASSIN3] ~r~Non hai ucciso tutti i membri della gang in tempo! [ASM3_22:ASSIN3] ~g~Non avvicinarti troppo, se no i bersagli potrebbero vederti e cercare di scappare! [ASM3_12:ASSIN3] ~g~Un assortimento di armi è stato lasciato per te nel caso di bisogno. Hai ~h~9 MINUTI~g~ per uccidere tutti i membri della gang. [ASM3_13:ASSIN3] ~g~Mike Griffin lavora su un cartello pubblicitario in Washington. [ASM3_17:ASSIN3] ~g~Charlie Dilson è in macchina in Washington. {=================================== MISSION TABLE ASSIN4 ===================================} [ASM4_12:ASSIN4] Distanza: [ASM4_15:ASSIN4] ~g~Recupera il fucile di precisione alla tua destra. [ASM4_16:ASSIN4] ~g~Osserva la donna sul balcone: scenderà per le scale e chiederà l'ora a un uomo. [ASM4_17:ASSIN4] ~g~Solo quando la conversazione avrà termine, dovrai uccidere l'uomo con cui ha parlato ma NON la donna. [ASM4_18:ASSIN4] ~g~Una volta eliminato il bersaglio, recupera la sua valigetta e portala ad Ammu-Nation in Downtown. [ASM4_19:ASSIN4] ~g~Tieni la distanza dal bersaglio: la barra nella parte superiore destra dello schermo indica quanto gli sei vicino. [ASM4_20:ASSIN4] ~g~Se si riempirà, verrai avvistato. [ASM4_21:ASSIN4] ~g~Recupera la valigetta! [ASM4_22:ASSIN4] ~g~Porta la valigetta ad Ammu-Nation in Downtown. [ASM4_23:ASSIN4] ~g~Ti ha visto e sta fuggendo! Uccidilo e recupera la valigetta! [ASM4_25:ASSIN4] ~r~Hai ucciso la donna, idiota! [ASM4_26:ASSIN4] ~r~Il bersaglio è salito a bordo del volo! [ASM4_27:ASSIN4] ~r~Il bersaglio ti ha visto! Avresti dovuto tenerti a distanza! [ASM4_28:ASSIN4] ~r~Il bersaglio ti ha visto! Ti ha sentito sparare! [ASM4_29:ASSIN4] ~r~Dovevi ucciderlo solo dopo che aveva parlato con la donna! [ASM4_A:ASSIN4] È giunta l'ora di occuparsi di pesci più grossi, Mr Teal. Troverai un fucile tra le foglie alla tua destra. [ASM4_B:ASSIN4] Osserva la donna sul balcone sopra i banchi dei check-in: scenderà per le scale e chiederà l'ora a un uomo. [ASM4_C:ASSIN4] Dovrai uccidere questa persona, recuperare la sua valigetta e portarla nella locazione riportata sotto al telefono. {=================================== MISSION TABLE ASSIN5 ===================================} [ASM5_A:ASSIN5] C'è uno scambio di beni sopra il tetto della fabbrica di gelato Cherry Popper. [ASM5_B:ASSIN5] Uccidi tutti gli elementi coinvolti, recupera la merce e portala all'eliporto presso l'aeroporto. [ASM5_C:ASSIN5] Alla tua sinistra troverai un cancello che porta al retro della fabbrica. [ASM5_1:ASSIN5] ~g~Entra nel cortile dietro alla fabbrica di gelato Cherry Popper e apriti la strada fino al luogo dello scambio. [ASM5_2:ASSIN5] ~g~Recupera la merce e portala all'eliporto presso l'aeroporto. [ASM5_3:ASSIN5] ~g~Porta la merce all'eliporto presso l'aeroporto! {=================================== MISSION TABLE BANKJ1 ===================================} [WANTED1:BANKJ1] ~g~Semina i poliziotti per perdere il tuo livello di sospetto. [BJM1_A:BANKJ1] Tommy! Ehi Tommy, guarda qua, è grandioso! Ho fatto installare un minibar! [BJM1_B:BANKJ1] Abbiamo un intero bar al piano di sotto, Ken. [BJM1_C:BANKJ1] Sì, sì, certo. Beh, ho portato quella lavagna che mi avevi richiesto. [BJM1_D:BANKJ1] Ah, questi sono i benefici della scuola dell'obbligo: l'abilità di seguire le istruzioni. [BJM1_E:BANKJ1] Adesso mi serve uno scassinatore. [BJM1_F:BANKJ1] Oh, va bene, beh, fammi pensare... scassinatore, scassinatore, scassinatore... Trovato! Questo tipo ti farà impazzire! [BJM1_G:BANKJ1] Ahh, nah, quello schmuck. È dentro. [BJM1_H:BANKJ1] Dentro dove? [BJM1_I:BANKJ1] In una cella nel quartier generale della polizia in attesa di trasferimento. [BJM1_J:BANKJ1] Credo stia per essere rilasciato sulla parola... [BJM1_1:BANKJ1] ~g~Fai fuggire Cam Jones dalla custodia della polizia! [BJM1_3:BANKJ1] ~g~Troverai ciò che ti serve nello spogliatoio della stazione. [BJM1_21:BANKJ1] ~g~La tessera per le celle si trova nel piano superiore della stazione. [BNK1_7:BANKJ1] Cam Jones? [BNK1_8:BANKJ1] Ti sto facendo scappare! [BNK1_10:BANKJ1] Sì, sono io... [BNK1_11:BANKJ1] Come vuoi! [BNK1_13:BANKJ1] Devo fare un lavoro e tu sarai il mio scassinatore. [BNK1_14:BANKJ1] Sempre meglio che restare col culo in galera! [BJM1_22:BANKJ1] ~g~Riporta Cam a casa sua! [BJM1_23:BANKJ1] ~g~Prima devi recuperare la tessera! [BNK1_12:BANKJ1] Fai perdere le tracce e riportami a casa! [BJM1_20:BANKJ1] Metti via l'arma o ne pagherai le conseguenze! [BJM1_5:BANKJ1] Solo personale autorizzato oltre a questo punto! [BJM1_2:BANKJ1] ~r~Dovevi far fuggire Cam, non farlo morire! [BJM1_4:BANKJ1] È armato! Uccidetelo! {=================================== MISSION TABLE BANKJ2 ===================================} [BJM2_A:BANKJ2] Abbiamo bisogno di un tiratore. Ne conosci uno? [BJM2_B:BANKJ2] Ehi Tommy, Tommy, Tommy, questa roba ti tiene in forma, amico. [BJM2_C:BANKJ2] WoooOOOooo! [BJM2_D:BANKJ2] Potrei essere io il tuo tiratore! Spara! Spara! [BJM2_E:BANKJ2] Non sei un tiratore, sei un idiota. [BJM2_F:BANKJ2] Fatti un drink e sta zitto. [BJM2_G:BANKJ2] Ehi, via dai miei piedi! Ye ye ye ow ow. [BJM2_H:BANKJ2] Cam, cosa mi dici? [BJM2_I:BANKJ2] Beh, il miglior tiratore in città è un tipo chiamato Cassidy. [BJM2_J:BANKJ2] Davvero? [BJM2_K:BANKJ2] Sì. Un militare, o per lo meno crede di esserlo. [BJM2_L:BANKJ2] Non credo sia mai stato nell'esercito, ma sicuramente sa come tenere in mano un'arma. [BJM2_M:BANKJ2] Lo troverai di sicuro al poligono di tiro. [BJM2_2A:BANKJ2] Sei Phil Cassidy? [BJM2_2B:BANKJ2] Perché? [BJM2_2C:BANKJ2] Sto cercando un uomo che sappia come sparare. Da quello che vedo, non sono molto convinto... [BJM2_2D:BANKJ2] Figliolo, potrei colpire una mosca sulla tua testa a 30 metri di distanza. [BJM2_2E:BANKJ2] Davvero? [BJM2_2F:BANKJ2] Certo. Ho fatto pratica nell'esercito. [BJM2_2G:BANKJ2] Lo sparo alla mosca è così popolare nell'esercito? Per fortuna non pago le tasse. [BJM2_2H:BANKJ2] Stai cercando di fare lo spiritoso? [BJM2_2I:BANKJ2] Ah ah ah ah ah! [BJM2_2J:BANKJ2] Spariamo. [BJM2_1:BANKJ2] ~g~Raggiungi Ammu-Nation in Downtown e parla con Phil Cassidy. [BJM2_4:BANKJ2] PUNTEGGIO PRIMO TURNO: ~1~ [BJM2_6:BANKJ2] PUNTEGGIO SECONDO TURNO: ~1~ [BJM2_7:BANKJ2] PUNTEGGIO TOTALE: ~1~ [BJM2_9:BANKJ2] ~G~Raggiungi il punto di partenza del secondo turno. [BJM2_11:BANKJ2] ~r~Phil è morto! [BJM2_12:BANKJ2] ~r~Uno dei tiratori è morto! [BJM2_14:BANKJ2] ~g~Raggiungi la prossima area! [BJM2_17:BANKJ2] ~g~Vai a parlare con Phil. [BJM2_24:BANKJ2] ~g~Il bersaglio più vicino vale un punto. [BJM2_25:BANKJ2] ~g~Il bersaglio centrale vale due punti. [BJM2_27:BANKJ2] ~g~In questo turno, tutti i bersagli valgono un punto. [BNK2_4:BANKJ2] Hoooeee! [BNK2_5:BANKJ2] Non riusciresti a colpire neanche un elefante! [BNK2_7:BANKJ2] Fammi un favore, aiutami a mettere a segno un colpo. [BNK2_8:BANKJ2] Figliolo, da come hai sparato, se mi chiedessi di sposarti direi di sì. [BNK2_9A:BANKJ2] Figliolo, ficcati la tua parlata e le tue grandi idee là dove non batte il sole. Non sai sparare per niente. [BNK2_9B:BANKJ2] Non sai sparare per niente. [BJM2_28:BANKJ2] PUNTEGGIO TERZO TURNO: ~1~ [BJM2_26:BANKJ2] ~g~Il bersaglio più lontano vale tre punti. [BNK2_1:BANKJ2] MUNIZIONI VERE [RANGE_1:BANKJ2] PUNTEGGIO PER COLPO: ~1~ [BJM2_2:BANKJ2] ~g~Per interrompere il turno, premi il ~h~~k~~PED_JUMPING~~g~. [BJM2_N:BANKJ2] Rilassati. {=================================== MISSION TABLE BANKJ3 ===================================} [BJM3_A:BANKJ3] Le cose stanno iniziando a prendere forma. [BJM3_B:BANKJ3] Qual è il piano Tommy? Que pasa, amigo? [BJM3_C:BANKJ3] La tua parte consiste nel continuare a fare l'idiota. Comunque, adesso abbiamo bisogno di un pilota. [BJM3_D:BANKJ3] Tommy, lo farò io. Io so guidare bene. [BJM3_E:BANKJ3] Ti serve Hilary, capo. Di certo non un sapientone avvocato del cazzo. [BJM3_F:BANKJ3] Hilary è la persona giusta. Non troverai nessuno capace di guidare così veloce. Gli posso fare una chiamata. [BJM3_G:BANKJ3] Ehi Hil, sono Phil. Come butta? No, lascia stare, ne parliamo un'altra volta. Mi faresti un favore? [BJM3_H:BANKJ3] Ho qui un tipo del nord... No, no, non credo abbia fatto il servizio militare, ma ha bisogno di un guidatore. [BJM3_I:BANKJ3] Per un po' di azione. OK, capisco. [BJM3_J:BANKJ3] Che cosa ha detto? [BJM3_K:BANKJ3] Beh, ci sta, nessun problema. Beh, un piccolo problema c'è: ha subito uno shock da abbandono. [BJM3_L:BANKJ3] Sembra non voglia lavorare con nessuno che non sia capace di batterlo. Qualcosa che ha a che fare con la sua vecchia. [BJM3_M:BANKJ3] Comunque, vuole prima sfidarti in una corsa, ha detto che ci aspettava fuori... [BJM3_2A:BANKJ3] Sei Tommy? Beh, certo che sei Tommy, cioè... [BJM3_2B:BANKJ3] Per quale altro motivo qualcuno vorrebbe parlare con me? [BJM3_2C:BANKJ3] OK, vedila in questo modo... [BJM3_2D:BANKJ3] Io guiderò SE, e solo SE, tu sai guidare come si deve. [BJM3_2E:BANKJ3] Lasciami solo... e non te lo perdonerò mai. [BJM3_2:BANKJ3] ~r~Hilary è morto! [BJM3_4:BANKJ3] ~g~Hai bisogno di una vettura per partecipare! [BNK3_1:BANKJ3] OK. Guiderò per te, ma per favore trattami male! [BNK3_3A:BANKJ3] Corsa illegale in corso presso Vice Point. [BNK3_3B:BANKJ3] A tutte le pattuglie. [BNK3_3C:BANKJ3] Queste corse sono illegali e vietate! {=================================== MISSION TABLE BANKJ4 ===================================} [BNK4_A:BANKJ4] ~w~Come potete vedere, signori, questi saranno i soldi più facili della storia. [BNK4_B:BANKJ4] ~w~Tommy, seriamente, dovresti pensare di fare l'avvocato. [BNK4_C:BANKJ4] ~w~Ma che cosa ti fumi, amico? Non è per niente un piano semplice! [BNK4_D:BANKJ4] ~w~Beh, chi ha bisogno di un piano semplice? [BNK4_E:BANKJ4] ~w~Prendi il comunismo, beh, quello era un piano semplice. Non ha fatto così bene alla Russia, no? [BNK4_F:BANKJ4] ~w~Rilassatevi, OK? Con una squadra come la nostra, non ci saranno problemi. [BNK4_G:BANKJ4] ~w~Abbiamo Cam per la cassaforte. Phil? Noi ci occuperemo della sorveglianza, mentre Hilary guiderà l'auto per la fuga. [BNK4_H:BANKJ4] ~w~Uh, eh eh, non ti stai dimenticando qualcuno? Qualcuno che ti ha costantemente aiutato in questa città? Qualcuno...? [BNK4_I:BANKJ4] ~w~Ken... Ken, ma certo. Il nostro Ken si occuperà di lavare il denaro sporco e di tenere le birre al fresco. [BNK4_J:BANKJ4] ~w~Non ho ancora capito cosa ci sto a fare io qua. [BNK4_K:BANKJ4] ~w~Beh, è semplice: non hai mai visto un film d'azione? [BNK4_L:BANKJ4] ~w~Entriamo nella banca, sventoliamo in giro l'arma e usciamo molto ricchi. [P_DEAD:BANKJ4] ~r~Phil è morto! [C_DEAD:BANKJ4] ~r~Cam è morto! [H_DEAD:BANKJ4] ~r~Hilary è morto! [P_HIND:BANKJ4] ~r~Hai perso Phil! [C_HIND:BANKJ4] ~r~Cam è restato indietro! [H_HIND:BANKJ4] ~r~Hai abbandonato Hilary! [GETCAR:BANKJ4] ~g~Entra nell'auto e preparati alla rapina! [TRASHED:BANKJ4] ~r~HAI DISTRUTTO L'AUTO PER LA FUGA!!! [BNK4_1:BANKJ4] Guido io. [BNK4_2:BANKJ4] Ottimo. Un passeggero. Aspetta che lo dica al resto del gruppo. [BNK4_3A:BANKJ4] Ehi, fai attenzione alla macchina, Tommy! [BNK4_3B:BANKJ4] Tommy, Hilary occupa troppo spazio! [BNK4_3C:BANKJ4] Non è vero! [BNK4_3D:BANKJ4] Invece sì! [BNK4_3E:BANKJ4] Ehi, state entrambi zitti o ve ne andate a piedi. [BNK4_3F:BANKJ4] Sì Hilary. [BNK4_3I:BANKJ4] Perdio, Phil, smettila di sventolare quell'affare in giro! [BNK4_3J:BANKJ4] Sì, finirai col cavare un occhio a qualcuno! [BNK4_3M:BANKJ4] La mia bambina! È a pezzi! [BNK4_3O:BANKJ4] Sei accecato dall'illusione di permanenza. [BNK4_3P:BANKJ4] Cosa? [BNK4_3Q:BANKJ4] Pensi che tutto duri per sempre. [BNK4_3R:BANKJ4] La giovinezza, le persone care, la pizza... [BNK4_3S:BANKJ4] Ma tutto passa e finisce e tu lo devi accettare. [BNK4_3T:BANKJ4] Ehi, hai ragione. Grazie Cam. [BNK4_3U:BANKJ4] È un piacere. [BNK4_3V:BANKJ4] Ehi Tommy, perché ci siamo fermati? [BNK4_4A:BANKJ4] ~w~Hilary, continua a guidare attorno al quartiere. [BNK4_5:BANKJ4] ~w~OK Tommy, OK. [BNK4_6:BANKJ4] ~w~QUESTA È UNA RAPINA! [BNK4_7:BANKJ4] ~w~NESSUNO SI MUOVA! [BNK4_8:BANKJ4] ~w~TUTTI CONTRO QUEL MURO! [BNK4_9:BANKJ4] Phil, mantieni il controllo! [BNK4_10:BANKJ4] Ricevuto capo! [BNK4_11:BANKJ4] Vieni Cam, la cassaforte è di sopra... [BK4_12A:BANKJ4] Maledizione! È una Flange 9000! [BK4_12B:BANKJ4] Potrei impiegare ore ad aprirla, [BK4_12C:BANKJ4] o cinque minuti se trovi l'amministratore. [BNK4_13:BANKJ4] Vado a scoprire dove si è rintanato. [BK4_14A:BANKJ4] Phil, va tutto bene? [BNK4_15:BANKJ4] Certo. Tutto liscio come l'olio. [BNK4_16:BANKJ4] Tu, tu vieni con me! [BNK4_17:BANKJ4] OK, OK! Ti prego, non sparare! [BNK4_18:BANKJ4] HO DETTO NESSUNO SI MUOVA! [BK4_19A:BANKJ4] L'apertura è temporizzata, [BK4_19B:BANKJ4] potete anche rinunciarci! [BK4_20A:BANKJ4] Diamine, posso aggirare l'apertura a tempo, [BK4_20B:BANKJ4] ma poi avremo bisogno del tuo codice per spalancarla! [BNK4_21:BANKJ4] Sta qua. Se fai uno scherzo diventi cibo per pesci. Chiaro? [BK4_24A:BANKJ4] Vado a controllare Phil, torno subito. [BK4_24B:BANKJ4] Te l'avevo detto di non toccare l'allarme! [BNK4_25:BANKJ4] La squadra SWAT sarà qui fra pochi minuti! [BNK4_27:BANKJ4] Un po' di aiuto non farebbe male, Tommy. [BNK4_28:BANKJ4] SWAT di Vice City! Siete completamente circondati! [BNK4_29:BANKJ4] Circondati? AH AH AH Aaaaaaaaaah! [BNK4_30:BANKJ4] Si stanno cagando addosso, corrotti bastardi! [BK4_31A:BANKJ4] Tommy! La cassaforte è aperta! [BK4_34A:BANKJ4] OK, abbiamo i fondi pensione della SWAT. Andiamocene da qui! [BK4_34B:BANKJ4] OK, l'avete voluto voi! Il tempo è scaduto! [BK4_35A:BANKJ4] Stanno assalendo l'edificio! [BK4_35B:BANKJ4] Al riparo! [BNK4_36:BANKJ4] Dov'è Cam? [BNK4_37:BANKJ4] Non ce l'ha fatta... [BNK4_38:BANKJ4] Questo è l'ultimo. VIA! VIA! VIA! [BNK_39:BANKJ4] Merda! Dov'è Hilary? [BK4_40A:BANKJ4] Gliela do io l'illusione di permanenza! [BNK4_42:BANKJ4] Ehi, ragazzi! Entrate, vi copro io! [BNK4_44:BANKJ4] Ce l'abbiamo fatta! Siamo ricchi, RICCHI! [BNK4_45:BANKJ4] Peccato che Cam non ce l'ha fatta, era un bravo ragazzo! [BNK4_46:BANKJ4] Sì, comunque... ora ce n'è più per noi! [BNK4_47:BANKJ4] Maledettamente vero! YEEEEHAAAH! [BNK4_48:BANKJ4] Tommy, vuoi un massaggio? [BNK4_49:BANKJ4] Ciao Mercedes! Sì, effettivamente sono un po' teso... [BNK450A:BANKJ4] Sai cosa ti dico Tommy? Sai cosa ti dico? I membri corrotti della SWAT faranno meglio a fare attenzione finché Kent Paul è in città. [BNK450B:BANKJ4] Eddai, dammene una fetta più grande! Amico, dai! Mi devo comprare dei vestiti nuovi! [BNK4_94:BANKJ4] ~w~OK, ragazzi. Procediamo come pianificato. [BM_DEAD:BANKJ4] ~r~Hai bisogno dell'amministratore vivo! [ASSET_A:BANKJ4] RAPINA ALLA BANCA COMPLETATA! [ASSET_B:BANKJ4] ~g~Il Malibu d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. [IDIOT:BANKJ4] ~r~Vai in giro vestito come un pazzo e attiri l'attenzione! Sei un IDIOTA! {=================================== MISSION TABLE BARON1 ===================================} [COK1_A:BARON1] Forza, dai, forza! Yeh yeh aaaah... [COK1_B:BARON1] Stupido cavallo! Ti taglierò la testa! [COK1_C:BARON1] Chi è questo stronzo? [COK1_D:BARON1] Tommy Vercetti, ti ricorderai di me. [COK1_E:BARON1] Scusami, sono un po' ansioso. Mai fidarsi di un fottuto cavallo! [COK1_F:BARON1] Hai fatto un bel lavoro: adesso lavori per me. [COK1_H:BARON1] Come ti ho detto, amigo, lavori per me. Adesso sta zitto. Un qualche giuda mi ha tradito. [COK1_I:BARON1] Pensava non sapessi quanti soldi stessi facendo, ma rubarmi il 3% è come rubarmi il 100%. [COK1_J:BARON1] Nessuno fa questo a me. NESSUNO!!! [COK1_K:BARON1] Seguilo nel suo appartamento e scopri dove va! Successivamente, lo elimineremo noi. [COK1_1:BARON1] Ommerda! [COK1_2:BARON1] Troppo lento, nonnino! [COK1_4:BARON1] Perdente. [COK1_5:BARON1] Ti conviene continuare a correre, stronzo! [COK1_8:BARON1] ~g~Svelto! Recupera un mezzo e inseguilo! [COK1_9:BARON1] ~r~Dovevi inseguirlo, non ucciderlo! [COK1_10:BARON1] ~r~Entra nella casa del ladro e scopri dove ha nascosto il denaro. [COK1_11:BARON1] ~g~Dai un'occhiata da questa finestra. [COK1_7:BARON1] ~g~È scappato sul tetto: stagli dietro ma non ucciderlo! [COK1_G:BARON1] Lavoro per soldi. {=================================== MISSION TABLE BARON2 ===================================} [COK2_A:BARON2] Ma che fottuti incompetenti non siete? [COK2_B:BARON2] IDIOTI! IDIOTI! IDIOTI! IDIOTI! [COK2_C:BARON2] Tommy, [COK2_D:BARON2] questi idioti... cercano sempre di fregarti. [COK2_E:BARON2] È questo il problema in questo business. [COK2_F:BARON2] Che cosa credi di fare? Aaaaah! [COK2_G:BARON2] Questi stronzi hanno fallito miseramente. [COK2_H:BARON2] Ben presto anche i papà e le mamme penseranno di poter spacciare coca in Vice City. [COK2_I:BARON2] Chi sarà il prossimo adesso? La fottuta mafia? [COK2_J:BARON2] La base della loro banda è una fortezza al piano terra, [COK2_K:BARON2] per cui Quentin... Quentin! QUENTIN! [COK2_L:BARON2] Lui ti porterà in volo sull'area! [COK2_M:BARON2] Sradicateli! [COK2_N:BARON2] Che cosa credi di fare? [COK2_O:BARON2] Che cosa fai qua? [COK2_P:BARON2] Ehi, ho chiesto in giro ed è ovvio [COK2_Q:BARON2] che Diaz ha fatto saltare l'accordo e ha freddato mio fratello. [COK2_R:BARON2] E ucciderà anche te! [COK2_S:BARON2] Io posso far fuori Diaz! [COK2_T:BARON2] No, ascoltami! Io mi occuperò di Diaz: [COK2_U:BARON2] sta cominciando a fidarsi di me. [COK2_1:BARON2] Ho una domanda, però: perché 'Quentin'? [COK2_2:BARON2] Non lo so, mi è sempre piaciuto... Quentin Vance... [COK2_3:BARON2] Vance? Tu sei Lance Vance? [COK2_4:BARON2] Ehi, ne ho avuto abbastanza a scuola! [COK2_5:BARON2] Hai mai provato a sparare con quelli da un elicottero? [COK2_8:BARON2] Dove ci stiamo dirigendo? [COK2_9:BARON2] Prawn Island. [COK2_13:BARON2] Lance Vance. Brutto bastardo. [COK2_14:BARON2] OK, siamo quasi arrivati. [COK2_15:BARON2] Faremo un paio di passaggi. [COK2_16:BARON2] Cerca di colpire il maggior numero di nemici possibile. [COK2_17:BARON2] Poi ti scarico e dovrai cavartela da solo. [COK2_20:BARON2] Merda! Siamo in una zona di guerra! Abbatti un po' di gente! [COK2_21:BARON2] Stiamo sotto tiro, amico! [COK2_22:BARON2] Questo affare è maledettamente costoso da riparare! Falli fuori! [COK2_23:BARON2] OK, adesso dovrai cavartela da solo... Buona fortuna, fratello! [COK2_24:BARON2] Condizioni elicottero: [COK2_25:BARON2] ~g~Recupera i soldi sul tetto. [COK2_27:BARON2] Sei nel MIO territorio, stronzo! [COK2_28:BARON2] Stai per affondare! [COK2_6:BARON2] No. Farò un po' di pratica lungo la strada. [OPEN_B:BARON2] Il blocco stradale per la terraferma è stato rimosso. [COK2_V:BARON2] sta cominciando a fidarsi di me. {=================================== MISSION TABLE BARON3 ===================================} [COK3_A:BARON3] Scommetto che adesso non vi divertite più! [COK3_B:BARON3] Ah ah ah ah! [COK3_C:BARON3] Woh! Fai attenzione a dove punti quel coso. [COK3_D:BARON3] Basta con merda di piccione sulla MIA macchina, vero Tommy? [COK3_E:BARON3] Direi di sì. [COK3_F:BARON3] Hai proprio ragione. Adesso ascolta: [COK3_G:BARON3] sai chi possiede la barca più veloce della costa est? [COK3_H:BARON3] Non così su due piedi. [COK3_I:BARON3] IO. E voglio che la situazione non cambi. [COK3_J:BARON3] Ogni contrabbandiere da qui a Caracas ha un solo sogno: un'imbarcazione più veloce. [COK3_K:BARON3] Si mormora che il cantiere Hull-o-Caust ha appena finito di costruirne una [COK3_L:BARON3] per una testa di cazzo del Costa Rica. [COK3_M:BARON3] E Tommy... IO VOGLIO QUELLA BARCA!!! [COK3_N:BARON3] Credo che siano tornati i tuoi piccioni. [COK3_O:BARON3] Ah! Pensavo di averti preso. Da dove salti fuori? [COK3_P:BARON3] Piccioni! Boom! Ah ah ah! [COK3_5:BARON3] ~g~Trova l'interruttore per abbassare l'imbarcazione. [COK3_6:BARON3] ~g~Porta la barca alla villa di Diaz. [COK3_7:BARON3] ~r~Hai distrutto la barca! [COK3_8:BARON3] ~g~Raggiungi il cantiere al porto e ruba la barca più veloce. [COK3_9:BARON3] ~g~Adesso entra nella barca. {=================================== MISSION TABLE BARON4 ===================================} [COK4_A:BARON4] Sputa fuori! CATORCIO DI PLASTICA! [COK4_B:BARON4] Come osi farmi questo? [COK4_C:BARON4] Chi credi di essere, brutto pezzo di merdosa plastica! Aaarrgh! [COK4_D:BARON4] VAFFANCULO! [COK4_E:BARON4] Se hai distrutto il mio film favorito di El Burro ti spacco! [COK4_F:BARON4] Che altro posso fare? [COK4_G:BARON4] Forse non è collegato... [COK4_H:BARON4] Cosa? [COK4_I:BARON4] Merda... non importa, ne posso comprare a centinaia. [COK4_J:BARON4] Adesso Tommy, [COK4_K:BARON4] ogni mese un freelance naviga fino a Vice City e ormeggia il suo yacht. [COK4_L:BARON4] Vende il suo carico alla prima imbarcazione. [COK4_M:BARON4] Voglio che tu prenda la barca più veloce, [COK4_N:BARON4] batta tutti gli altri stronzi [COK4_O:BARON4] e riporti qui il carico, OK? [COK4_P:BARON4] Fammi indovinare, pensavi mi avrebbe fatto comodo un angelo custode? [COK4_Q:BARON4] Vorrei solo che mi lasciassi venire, amico mio. [COK4_R:BARON4] Puoi continuare con queste stronzate alla rambo, [COK4_S:BARON4] ma sono sicuro che un giorno o l'altro ti salverò il culo. [COK4_T:BARON4] e allora vorrai baciarmi! [COK4_U:BARON4] Sei pazzo. [COK4_V:BARON4] Ah ah ah! [COK4_1:BARON4] Allora Tommy, sappiamo che è stato Diaz a far saltare il nostro affare... [COK4_3:BARON4] Allora per quale motivo continuiamo a lavorare per lui? [COK4_4:BARON4] Più cose scopriamo adesso, meno dovremo imparare quando ci impossesseremo di questa città! [COK4_5:BARON4] Mi piace il tuo stile, amico. Molto fico. [COK4_12:BARON4] Attento, stanno arrivando da tutte le parti! [COK4_13:BARON4] Preso! Torniamo da Diaz il più velocemente possibile! [COK4_14:BARON4] Ne vuoi un po'? [COK4_15:BARON4] Salutami i pesci! [COK4_16:BARON4] Prendi questo! E questo! [COK4_19:BARON4] Altri guai in arrivo! [COK4_20:BARON4] Ci stanno sparando da quel molo! [COK4_24:BARON4] Bel colpo, amico. Sei un vero pazzo di prima classe. [COK4_25:BARON4] Beh, grazie. [COK4_26:BARON4] Ci vediamo, Tommy. [COK4_27:BARON4] OK, Mr. Lance Vance Dance. [COK4_28:BARON4] ~g~Raggiungi lo yacht prima delle altre imbarcazioni! [COK4_31:BARON4] ~g~Raggiungi la barca più veloce attraccata al molo! [COK4_32:BARON4] ~r~Troppo lento! [COK4_33:BARON4] ~r~Hai distrutto la barca! [COK4_34:BARON4] ~g~Affonda quelle imbarcazioni! [COK4_35:BARON4] Condizioni barca: {=================================== MISSION TABLE BARON5 ===================================} [PROP_A:BARON5] PROPRIETÀ ACQUISITA! [COK4_30:BARON5] ~r~Lance è morto! [ASS1_A:BARON5] Ho messo un po' di cannoni nel bagagliaio. [ASS1_B:BARON5] Accidenti! Dove hai trovato tutta questa roba? [ASS1_C:BARON5] La tenevo da parte per una giornata di pioggia. [ASS1_D:BARON5] Ti piace? [ASS1_E:BARON5] Sì, mi piace. [ASS1_F:BARON5] Brutto idiota... [ASS1_G:BARON5] La mia bellissima casa... [ASS1_H:BARON5] Guarda cosa hai combinato! [ASS1_I:BARON5] Questo è per mio fratello! [ASS1_J:BARON5] Mi fidavo di te, Tommy! [ASS1_K:BARON5] Ti avrei fatto diventare... [ASS1_L:BARON5] Buona notte, Mr. Diaz. [ASS1_1:BARON5] Questo posto sarà zeppo di stronzi... fai attenzione... [ASS1_2:BARON5] Non preoccuparti, Tommy, ti copro io. [ASS1_4:BARON5] Diaz deve essere qui dentro! [ASS1_13:BARON5] DIAZ? Sono venuto a prendermi il tuo business! [ASS1_14:BARON5] TOMMY! Mi hai tradito... Idiota! Ti ucciderò fra un attimo! [ASS1_16:BARON5] ~g~Uccidi Diaz! [BUD1:BARON5] Lance [ASS1_18:BARON5] ~g~La porta è chiusa: trova un percorso alternativo. [ASS1_19:BARON5] Di qua! [ASS1_20:BARON5] Tommy, ho un problema con Quentin, non con te! {=================================== MISSION TABLE BIKE1 ===================================} [BM1_A:BIKE1] Dov'è Baker? [BM1_B:BIKE1] Sto cercando Big Mitch Baker... [BM1_C:BIKE1] Chi lo cerca? [BM1_D:BIKE1] Tommy Vercetti. [BM1_E:BIKE1] Vercetti. [BM1_F:BIKE1] Non mi sembri della polizia, il che ti ha fatto guadagnare un minuto. [BM1_G:BIKE1] Ti conviene parlare in fretta. [BM1_H:BIKE1] Kent Paul mi ha detto che saresti stato interessato a occuparti della sorveglianza di uno spettacolo che sta allestendo. [BM1_I:BIKE1] Kent Paul? Sheesh! Non mi stupisce abbia mandato te. [BM1_J:BIKE1] L'ultima volta che è stato qui è uscito dalla finestra con il vestitino che gli hanno dato alla nascita. [BM1_K:BIKE1] Sei interessato o no? [BM1_L:BIKE1] Facciamo solo favori tra compagni. [BM1_M:BIKE1] Come faccio a diventarlo? [BM1_N:BIKE1] Non è uno circolo sportivo, amico. Sai guidare una moto? [BM1_O:BIKE1] Sai sederti su uno sgabello e bere birra? [BM1_P:BIKE1] Cougar, Zeppelin, scoprite se questa ragazzina sa portare una moto... [BM1_2:BIKE1] ~g~Hai bisogno di una Freeway o di un'Angel per gareggiare! [BM1_3:BIKE1] ~r~I partecipanti sono stati attaccati! [BIKE1_1:BIKE1] Bene, bei vestiti. Vediamo cosa sai fare. [BM1_1:BIKE1] ~g~Recupera una Freeway o un'Angel e posizionati sulla griglia di partenza. {=================================== MISSION TABLE BIKE2 ===================================} [BM2_1:BIKE2] CAOS: [BM2_A:BIKE2] Ah ah ah, ti ho preso di nuovo! [BM2_B:BIKE2] Ehi, Vercetti. [BM2_C:BIKE2] Cougar ha detto che sai portare la moto come si deve. [BM2_D:BIKE2] Già, quanti altri giretti devo ancora fare? [BM2_E:BIKE2] Sono un uomo molto impegnato. [BM2_F:BIKE2] Se serve una bella scazzottata per mettere le cose a posto, dillo chiaramente. [BM2_G:BIKE2] Essere uno di noi non significa solo azzuffarsi. Si tratta di far parte di una famiglia. [BM2_H:BIKE2] Sì, sono già stato parte di una famiglia. Non ha funzionato. [BM2_I:BIKE2] Sì, certo, ma questa famiglia si occupa dei suoi membri. [BM2_J:BIKE2] Non chiediamo a uno di fare il lavoro sporco per poi lasciarlo marcire quindici anni ai lavori forzati. [BM2_K:BIKE2] Sì, esatto. Ho fatto la mia parte. [BM2_L:BIKE2] Questa è la più grande famiglia di disadattati, reietti e derelitti. [BM2_M:BIKE2] Merda, alcuni di noi sono stati persino traditi dalla propria nazione. [BM2_N:BIKE2] Sono stato imprigionato durante il Vietnam. Brutta storia. [BM2_O:BIKE2] Ed è per questo che ti chiedo di far bollire la pentola. [BM2_P:BIKE2] Questa fottuta nazione ha bisogno di un bel calcio nel culo, e saremo noi a darglielo. [BM2_Q:BIKE2] Per cui esci, prendi una moto e mostra a questa città quanto siamo arrabbiati! [BM2_R:BIKE2] Va bene. [BM2_2:BIKE2] ~g~Devi riempire l'indicatore del caos entro il tempo stabilito per mostrare quanto sei arrabbiato! [BM2_3:BIKE2] ~g~Questo suono indica che hai riempito parte dell'indicatore, continua così. [BM2_4:BIKE2] ~r~Non sei riuscito a riempire l'indicatore del caos in tempo! {=================================== MISSION TABLE BIKE3 ===================================} [BM3_A:BIKE3] Ehi, ciao Mitch. [BM3_B:BIKE3] Accidenti Vercetti, hai davvero le palle. [BM3_C:BIKE3] Adesso voglio vedere se sei pronto a combattere per la tua famiglia. [BM3_D:BIKE3] Una gang locale ha fatto l'errore di rubare il mio mezzo... [BM3_E:BIKE3] probabilmente come prova di coraggio o qualcosa del genere. [BM3_F:BIKE3] Io e i miei uomini stavamo per andare a dar loro una lezione sul rispetto. [BM3_G:BIKE3] Comunque, [BM3_H:BIKE3] ho pensato che sarebbe stata un'ottima occasione per la tua iniziazione. [BM3_I:BIKE3] Riportami indietro la mia moto e potrai dire a Paul che avrà la sua sorveglianza. [BM3_2:BIKE3] ~r~Dovevi riportare indietro la moto, non distruggerla! [BM3_3:BIKE3] ~g~Riporta la moto al bar! [BM3_4:BIKE3] ~g~Sali sulla moto! [INTRUDE:BIKE3] ~g~Ti hanno visto! [BM3_6:BIKE3] ~g~Si sono rintanati dietro Ammu-Nation in Downtown. [BM3_7:BIKE3] ~g~Avrai bisogno di una moto più veloce per accedere al tetto. [BM3_8:BIKE3] ~g~Usa la moto per saltare da queste scale fino al tetto nella parte più lontana della strada. [BM3_1:BIKE3] ~g~Una gang locale ha rubato la Angel di Mitch Baker: riportala indietro! [BM3_9:BIKE3] ~g~Recupera l'Angel di Mitch Baker e vattene! {=================================== MISSION TABLE BMX_1 ===================================} [BMX_REC:BMX_1] ~g~Nuovo record stabilito: ~1~!! [GETBIK2:BMX_1] Hai ~1~ secondi per salire su una Dirt Bike! {=================================== MISSION TABLE BOATBUY ===================================} [DRUG_1:BOATBUY] Salve? Saaalve? Salve? [DRUG_2:BOATBUY] Tranquillo amico, sono qua. [DRUG_3:BOATBUY] Ehi, damerino! Dimmi, sei il nuovo proprietario? [DRUG_4:BOATBUY] Sì. Qual è la barca più veloce? [DRUG_5:BOATBUY] È già in acqua, amico, [DRUG_6:BOATBUY] pensavo che la volessi provare. [DRUG_7:BOATBUY] Amico, ha già sopra un bel motore da 300 cavalli... [DRUG_8:BOATBUY] ...e la struttura in vetroresina: vola davvero sull'acqua! [DRUG_9:BOATBUY] Può andare da zero a sessanta in quattro secondi spaccati... [DRUG_10:BOATBUY] e può tenere venti pacchi della migliore erba giamaicana nella stiva! [DRUG_11:BOATBUY] Dacci dentro, amico, è pronta a volare! [DRUG_12:BOATBUY] Yo yo, uh, ehi amico, hai da accendere? [DRUG_13:BOATBUY] Amico? {=================================== MISSION TABLE CAP_1 ===================================} [CAP1_B1:CAP_1] ~g~La Mafia sta tassando il tuo business. Trovali e falli fuori. [CAP1_B2:CAP_1] ~g~La Mafia ha tassato il tuo cantiere navale! [CAP1_B3:CAP_1] ~g~La Mafia ha tassato la tua fabbrica di gelato! [CAP1_B4:CAP_1] ~g~La Mafia ha tassato la concessionaria! [CAP1_B5:CAP_1] ~g~La Mafia ha tassato la tua compagnia di taxi! [CAP_01:CAP_1] OK, qual è l'emergenza? [CAP_02:CAP_1] CHI? [CAP_03:CAP_1] Tommy... dei maledetti mafiosi... dicevano di essere venuti a prendere la loro fetta... [CAP_04:CAP_1] ...dicevano che erano soldi di Mr. Forello... mi sento di merda. [CAP_05:CAP_1] Forelli? SONNY Forelli? [CAP_06:CAP_1] Sì, quel nome... credo... hanno davvero insistito... [CAP_07:CAP_1] Non preoccuparti amico, non sono arrabbiato con te. [CAP_08:CAP_1] Portalo all'ospedale. [CAP_09:CAP_1] Tommy... fai a quel tipo un nuovo buco da cui cagare... [CAP_10:CAP_1] Ho intenzione di fargliene due. [CAP1_2:CAP_1] Conosci le regole, Vercetti! [CAP1_3:CAP_1] Mr. Forelli manda i suoi saluti! [CAP1_4:CAP_1] È il macellaio di Harwood! [CAP1_5:CAP_1] Dì a Sonny di stare alla larga! [CAP1_6:CAP_1] Vice City è MIA adesso, NON sua. [CAP1_7:CAP_1] Pensi di potermi tagliar fuori, Vercetti? [CAP1_8:CAP_1] Ti inseguiremo fino a farti fuori, Vercetti. [CAP1_9:CAP_1] Non hai nessuna possibilità, brutto cazzone psicopatico. [CAP1_10:CAP_1] Ti ucciderò, Vercetti. [CAP1_11:CAP_1] Sei sempre stato uno stronzo. [CAP1_12:CAP_1] Ti ammazzo, Vercetti. [CAP1_B6:CAP_1] ~g~Hai trovato l'esattore: uccidilo! [CAP1_B7:CAP_1] ~g~Hai perso l'esattore. [CAP1_B8:CAP_1] ~r~L'esattore ha tassato tutte le tue proprietà. [CAP1_B9:CAP_1] ~g~La Mafia ha tassato il Malibu! [CAP1_B0:CAP_1] ~g~La Mafia ha tassato lo studio cinematografico! [CAP1_C2:CAP_1] ~g~La Mafia è arrivata al cantiere navale! [CAP1_C3:CAP_1] ~g~La Mafia è arrivata alla fabbrica di gelato! [CAP1_C4:CAP_1] ~g~La Mafia è arrivata al concessionario! [CAP1_C5:CAP_1] ~g~La Mafia è arrivata alla compagnia di taxi! [CAP1_C9:CAP_1] ~g~La Mafia è arrivata al Malibu! [CAP1_C0:CAP_1] ~g~La Mafia è arrivata allo studio cinematografico! [CAP1_D2:CAP_1] ~g~La Mafia sta abbandonando il cantiere navale! [CAP1_D3:CAP_1] ~g~La Mafia sta abbandonando la fabbrica di gelato! [CAP1_D4:CAP_1] ~g~La Mafia sta abbandonando il concessionario! [CAP1_D5:CAP_1] ~g~La Mafia sta abbandonando la compagnia di taxi! [CAP1_D9:CAP_1] ~g~La Mafia sta abbandonando il Malibu! [CAP1_D0:CAP_1] ~g~La Mafia sta abbandonando lo studio cinematografico! [CAP1B10:CAP_1] Hai beccato gli esattori. Altri stanno arrivando. {=================================== MISSION TABLE CARBUY ===================================} [CAR1_1:CARBUY] B.J. Smith. E tu devi essere Mr. Vercetti. [CAR1_2:CARBUY] Ti andrebbe un giro? [CAR1_3:CARBUY] Perché no. [CAR1_4:CARBUY] Bene, mi dispiace un po' vendere il concessionario. [CAR1_5:CARBUY] È stato il mio primo investimento da quando mi sono messo in proprio. [CAR1_6:CARBUY] Ma adesso è giunta l'ora di andare avanti. [CAR1_7:CARBUY] Lasci la città? [CAR1_8:CARBUY] Non troppo in fretta, spero? [CAR1_9:CARBUY] No. Sto per prepararmi al pensionamento e ai miei futuri investimenti. [CAR1_10:CARBUY] Il mercato non tirava molto, [CAR1_11:CARBUY] e la mia squadra si è preoccupata personalmente di diventare [CAR1_12:CARBUY] più creativa nella produzione di contanti. [CAR1_13:CARBUY] Logicamente, potrei smantellare l'organizzazione prima di vendere. [CAR1_14:CARBUY] Diamine, potrei radere al suolo tutto quanto se volessi. [CAR1_15:CARBUY] Quest'area è in pieno sviluppo. [CAR1_16:CARBUY] Oh, non mi preoccuperei di ciò. [CAR1_17:CARBUY] Il posto mi sembra perfetto. [CAR1_18:CARBUY] Sì, lo è, allora siamo d'accordo? {=================================== MISSION TABLE CARPAR1 ===================================} [MM_1_A:CARPAR1] ~g~Attraversa ~y~5 punti di controllo ~r~SENZA~g~ abbattere i ~r~CONI~g~! ~g~Puoi attraversarli in ~y~QUALSIASI ORDINE~g~. [CONE_1:CARPAR1] ~r~Hai abbattuto un cono! [MM_1_C:CARPAR1] ~y~ATTRAVERSA~g~ un punto di controllo per attivare il TIMER. ~g~Ogni punto di controllo ti fornirà ~y~~1~ SECONDI~g~ extra. {=================================== MISSION TABLE COPCAR ===================================} [KILLS:COPCAR] UCCISIONI: [C_BREIF:COPCAR] ~g~Sospettato visto in prossimità dell'area ~a~. [C_PASS:COPCAR] MINACCIA ELIMINATA: ~1~$ [COPCART:COPCAR] ~g~Hai ~1~ secondi per tornare a un veicolo della polizia prima che la missione abbia termine. [C_CANC:COPCAR] ~r~Missione Vigilante annullata! [C_TIME:COPCAR] ~r~È scaduto il tuo tempo come tutore della legge! [C_COMP1:COPCAR] Missione Vigilante livello 12 completata: valore massimo dell'armatura aumentato a 150. [CLEVEL:COPCAR] Missione Vigilante livello ~1~ {=================================== MISSION TABLE COUNT1 ===================================} [CM1_A:COUNT1] Mr. Vercetti? Ehi, hai comprato la vecchia tipografia? [CM1_B:COUNT1] Sì, il mio vecchio un tempo lavorava nel campo. [CM1_C:COUNT1] Avrei voluto seguire le sue orme, ma... ho deciso di vivere diversamente. [CM1_D:COUNT1] Stai progettando di vendere il vecchio macchinario smontandolo in pezzi? [CM1_E:COUNT1] Ho pensato che potremmo stampare ancora qualcosa... un giornale, una rivista... [CM1_F:COUNT1] Che merda, figliolo, roba di bassa qualità. Ho sempre sognato stampare soldi. Non è poi così difficile. [CM1_G:COUNT1] Sai, l'ho fatto su scala ridotta per anni. [CM1_H:COUNT1] Davvero? [CM1_I:COUNT1] Certo. Ma prima abbiamo bisogno di matrici di buona qualità. [CM1_J:COUNT1] Ma certo! C'è già un'organizzazione di falsificatori in azione in Florida. [CM1_K:COUNT1] Un'organizzazione? [CM1_L:COUNT1] Sì. Beh, ho sentito solo delle voci. [CM1_M:COUNT1] Conosco un tipo che si intende di voci... [CM1_N:COUNT1] Un tempo passavo le serate con lui, mentre pulivo i rulli... [CM1_2A:COUNT1] Accidenti che culo incredibile! [CM1_2B:COUNT1] Va bene, bellezza, sei tu che ci perdi! [CM1_2C:COUNT1] Ehi, ciao amico, come butta? [CM1_2D:COUNT1] Che cosa sai della falsificazione? [CM1_2E:COUNT1] Oh, sto bene Paul, e a te? [CM1_2F:COUNT1] Vieni qua. [CM1_2G:COUNT1] Va bene! Va bene! Va bene! Sei ovviamente un tipo impegnato. [CM1_2H:COUNT1] Tutto ciò che so sul campo è che la Triade fornisce le matrici. [CM1_2I:COUNT1] Hanno una società di spedizioni nel porto, [CM1_2J:COUNT1] il capo di certo saprà quando passeranno nuovamente delle matrici! [CM1_2K:COUNT1] Grazie Paul. [CM1_2L:COUNT1] Qual è il tuo problema, sei un maniaco! [CM1_2M:COUNT1] Dammi un altro drink, in fretta! [CM1_3:COUNT1] ~g~Ti hanno visto! [CM1_5:COUNT1] ~g~Incontrati con Kent Paul al Malibu Club! [CNT1_1:COUNT1] Chi sei? Oooof! Aaiieee! Non il viso! Non il viso! [CM1_1:COUNT1] ~g~Vai alla compagnia marittima Chartered Libertine nel porto. [CM1_2:COUNT1] ~g~L'ufficiale delle spedizioni è in possesso delle informazioni che ti servono. [CNT1_2:COUNT1] OK, parlerò, parlerò! [CM1_6:COUNT1] ~g~Ritorna con le informazioni alla tipografia! {=================================== MISSION TABLE COUNT2 ===================================} [CNT2_B1:COUNT2] Bene, il corriere porterà le matrici al porto oggi stesso. [CNT2_B2:COUNT2] Ho intenzione di intercettare il corriere, recuperare le matrici, far perdere le tracce e tornare qua. [CNT2_B3:COUNT2] Bene. A seconda di quanto bene vadano le cose, [CNT2_B4:COUNT2] potremmo avere cinque minuti per stampare soldi prima che l'associazione dei falsificatori ci trovi, o potremmo avere tutto l'anno. [CNT2_B5:COUNT2] In ogni caso, voglio veder bigliettoni verdi uscire cinque minuti dopo che sarò tornato. Tutto chiaro? [CNT2_B6:COUNT2] Non preoccuparti, Tommy. Saremo pronti. [CNT2_B7:COUNT2] Io e i ragazzi saremo in giro per il quartiere se mai dovessi avere bisogno di una mano con gli inseguitori. [CNT2_B8:COUNT2] Ottimo: siete tutti pronti? Perfetto. Ci vediamo più tardi... [CNT2_01:COUNT2] ~g~Il ~r~corriere~g~ con le matrici arriverà fra pochi attimi al ~y~porto~g~ in elicottero. [CNT2_02:COUNT2] ~r~Il corriere è scappato con l'elicottero. [CNT2_03:COUNT2] ~r~Il corriere è arrivato a destinazione sano e salvo. Sei in ritardo! [CNT2_04:COUNT2] ~r~Hai distrutto le matrici nell'esplosione! [CNT2_05:COUNT2] ~g~Hai preso le matrici. Riportale alla tipografia. [CNT2_06:COUNT2] ~g~Il corriere è morto e ha lasciato cadere le matrici: raccoglile prima che lo faccia qualcun altro. [CNT2_07:COUNT2] ~g~Una delle guardie ha raccolto le matrici: non lasciarlo scappare! [CNT2_08:COUNT2] ~g~Il ~r~corriere~g~ con le matrici è arrivato al porto. [CNT2_4:COUNT2] Faccende private, non sei il ben venuto. [CNT2_09:COUNT2] BENI TIPOGRAFIA ACQUISITI [CNT2_10:COUNT2] ~g~La tipografia d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. [CNT2_11:COUNT2] ~r~Le matrici adesso sono in fondo all'oceano! {=================================== MISSION TABLE CUBAN1 ===================================} [CUB1_A:CUBAN1] Sì amico? [CUB1_B:CUBAN1] Ehi, tranquillo papà, è venuto per me. Sei tu l'uomo? [CUB1_C:CUBAN1] Oh sì, sei tu l'uomo. Credo per lo meno, vero? [CUB1_D:CUBAN1] No, non credo. [CUB1_E:CUBAN1] Ah sì? Vieni qua, osso duro. [CUB1_F:CUBAN1] Pensi di potermi prendere in giro? [CUB1_G:CUBAN1] Credi di poter fare lo stupido con me? [CUB1_H:CUBAN1] No, credo tu stia facendo abbastanza lo stupido per entrambi. [CUB1_I:CUBAN1] Ehi figliolo, ha detto che sei stupido. [CUB1_J:CUBAN1] E io lo chiamo femminuccia, papà. [CUB1_K:CUBAN1] Guarda un po' come va in giro vestito. [CUB1_L:CUBAN1] Che cos'è questa? L'ultima moda femminile per la notte? [CUB1_M:CUBAN1] Perché un osso duro come te si veste da donnina? [CUB1_N:CUBAN1] Hai le mutandine come le signorine, eh? [CUB1_O:CUBAN1] Cos'hai contro le donne? Preferisci gli uomini, amico? [CUB1_P:CUBAN1] Mi piacciono le donne! Tutte le donne! Adoro mia madre, chico. [CUB1_Q:CUBAN1] Va bene, va bene, ti credo sulla parola. [CUB1_R:CUBAN1] Sai guidare, amigo? [CUB1_S:CUBAN1] Certo... come una donna. [CUB1_T:CUBAN1] Molto divertente. Mi piaci, ragazzo. Forse ci puoi aiutare. [CUB1_U:CUBAN1] Potresti provare di essere un uomo. Huh? [CUB1_V:CUBAN1] Porta fuori la barca. [CUB1_W:CUBAN1] Mostra di avere due grossi cojones, [CUB1_X:CUBAN1] e non piccoli minuscoli chiquita. [CUB1_02:CUBAN1] OK amico, trattala come una vera signora. [CUB1_03:CUBAN1] Non male, sei un vero uomo. [CUB1_04:CUBAN1] Accidenti, hai davvero due grossi cojones, amigo. [CUB1_05:CUBAN1] Amigo, sei un vero uomo. [CUB1_06:CUBAN1] E tu ti reputi un uomo? [CUB1_07:CUBAN1] Sei un piccolo gattino impaurito, bamboccio: vai a piangere dalla mamma! [CUB1_08:CUBAN1] Sei un grande spreco di spazio. Cammini come un uomo, parli come un uomo, ma guidi come un idiota. [CUB1_09:CUBAN1] Amico, sei tu l'uomo. Mi piaci, amico. Mi piaci molto. [CUB1_10:CUBAN1] A qualsiasi ora, amico, perché tu hai i cojones e tutti i miei amici hanno grandi cajones. [CUB1_11:CUBAN1] ~r~Hai ucciso Rico! [CUB1_12:CUBAN1] Passa per il primo punto di controllo per cominciare il test. [CUB1_14:CUBAN1] Torna alla barca! [CUB1_15:CUBAN1] ~r~Sei troppo lento, amico. [CUB1_01:CUBAN1] Ehi, sono Rico. Sei tu l'uomo con i grandi cojones? [CUB1_13:CUBAN1] ~g~Hai tre minuti per completare il percorso. {=================================== MISSION TABLE CUBAN2 ===================================} [CUB2_A:CUBAN2] Un cafecito, por favor, Alberto... [CUB2_N:CUBAN2] Nessun problema, Tommy. [CUB2_B:CUBAN2] Papà! Un gran problema! [CUB2_O:CUBAN2] Umberto, figlio mio, cosa succede? [CUB2_C:CUBAN2] Gli Haitiani! Odio quei maledetti Haitiani! [CUB2_D:CUBAN2] Hanno osato crearmi problemi per l'ultima vota! [CUB2_E:CUBAN2] Questi Haitiani. Facciamoli fuori! [CUB2_F:CUBAN2] Avremo bisogno di supporto. [CUB2_G:CUBAN2] Ho già perso degli hermanos là fuori. [CUB2_H:CUBAN2] Amigo, tu guidi bene! [CUB2_I:CUBAN2] Per una donna, giusto? [CUB2_J:CUBAN2] Non è il momento degli scherzi! [CUB2_K:CUBAN2] Forza, guida nuovamente per me! [CUB2_L:CUBAN2] Porta i miei ragazzi in posizione e faremo fuori quegli Haitiani! [CUB2_M:CUBAN2] Si sono messi contro me, hanno scherzato con il pezzo grosso della città! [CUB2_01:CUBAN2] Troppo piccola, amico, devi prendere una macchina più grande. [CUB2_02:CUBAN2] Abbiamo bisogno di rinforzi dal café! [CUB2_03:CUBAN2] ~g~Prendi una macchina e recupera i Cubani di fronte al café Robina. [CUB2_04:CUBAN2] ~g~Scarica i Cubani sul luogo dello scontro. [CUB2_05:CUBAN2] Fai fuori quel codardo di un cecchino! [CUB2_07:CUBAN2] Combattono come donnicciole! Mettetevi al riparo! [CUB2_09:CUBAN2] Cecchino sul tetto! [CUB2_11:CUBAN2] ~r~Idiota, avevamo bisogno della macchina. [CUB2_12:CUBAN2] Ehi amigo! Sono felice che ce l'hai fatta. [CUB2_13:CUBAN2] Covo puzzolente di Haitiani, li uccideremo tutti! [CUB2_14:CUBAN2] CAAARICAAAA! [CUB2_15:CUBAN2] Adesso, fratelli, CAAARICAAAA! [CUB2_16:CUBAN2] Tommy, abbiamo provato il nostro coraggio! [CUB2_17:CUBAN2] Rubiamo quel van pieno di droga e scappiamo in fretta! [CUB2_18:CUBAN2] ~g~Recupera una macchina per caricare i Cubani. [CUB2_19:CUBAN2] Combatteremo come uomini! [CUB2_21:CUBAN2] Combattiamo come uomini con grandi cojones! [CUB2_22:CUBAN2] ~g~Elimina il resto degli Haitiani così i Cubani potranno avanzare. [CUB2_23:CUBAN2] ~g~Little Haiti sarà piena di Haitiani desiderosi di pareggiare il conto con i Cubani. Guardati la schiena. [CUB2_24:CUBAN2] ~g~Torna al Café Robina con il van e parcheggialo sul retro. [CUB2_25:CUBAN2] UCCIDI TUTTI GLI HAITIANI!!! {=================================== MISSION TABLE CUBAN3 ===================================} [CUB3_A:CUBAN3] Alberto. Un caffè, senor. [CUB3_B:CUBAN3] Papà, non servire questo serpente nascosto nella paglia. [CUB3_C:CUBAN3] Sei un doppiogiochista, Tommy! [CUB3_D:CUBAN3] O fai il doppio gioco o sei un perdente, ragazzo! [CUB3_E:CUBAN3] Gli Haitiani! Merda, stanno ridendo di me. [CUB3_F:CUBAN3] Ehi. Calmati. Qual è il tuo problema? [CUB3_G:CUBAN3] Ridono di me, Tommy! Di me! [CUB3_H:CUBAN3] Umberto Robina! Fanno tutto ciò che vogliono! [CUB3_I:CUBAN3] Nessuno fa ciò che vuole, Umberto, fanno ciò che tu permetti loro di fare. [CUB3_J:CUBAN3] Cosa? [CUB3_K:CUBAN3] Vuoi che mi occupi di qualcuno? [CUB3_L:CUBAN3] Posso pensarci io, ma ti costerà. [CUB3_M:CUBAN3] Lo so che siamo come fratelli, ma stiamo parlando di affari. [CUB3_N:CUBAN3] Tommy, sei un vero uomo. Un vero businessman, un gentleman. [CUB3_O:CUBAN3] Questi Haitiani. Hanno un carico di merce in arrivo via mare, roba di prima qualità. [CUB3_P:CUBAN3] La prendiamo e li facciamo fuori. [CUB3_Q:CUBAN3] Tu la prendi, io ti copro. Come mio fratello, come mio figlio. [CUB3_R:CUBAN3] Preferisco i soldi che saltellare sulle tue gambe, amigo. [CUB3_01:CUBAN3] Ehi Rico, bella barca. Sei pronto? [CUB3_03:CUBAN3] ~g~Raccogli tutte le valigette piene di soldi e droga. [CUB3_04:CUBAN3] ~g~Riporta la droga e i soldi a Umberto. [CUB3_05:CUBAN3] Sì Tommy. Vedi di sparare come si deve oggi, [CUB3_06:CUBAN3] non voglio riempire la mia barca di buchi, OK? [CUB3_07:CUBAN3] ~g~Incontrati con Rico, lui ti porterà fino alla locazione dell'incontro. [CUB3_02:CUBAN3] ~g~UCCIDI TUTTI GLI HAITIANI SULLE BARCHE!!! [CUB3_08:CUBAN3] Uh oh... un gruppo di Cubani. Siamo sotto attacco! {=================================== MISSION TABLE CUBAN4 ===================================} [CUB4_A:CUBAN4] Ehi signorine, sapete che cosa voglio fare? [CUB4_B:CUBAN4] Voglio far fuori un Haitiano. E poi? [CUB4_C:CUBAN4] Poi voglio fare l'amore come un vero uomo. [CUB4_D:CUBAN4] Capito chica? Proprio così. [CUB4_E:CUBAN4] Fallito! [CUB4_F:CUBAN4] Cafone. [CUB4_G:CUBAN4] Ehi bella, non ti toccherei neanche con un bastone da lontano! [CUB4_H:CUBAN4] Umberto Robina, a lui piacciono le vere donne! Non delle capre in minigonna! [CUB4_I:CUBAN4] Tommy! Tommy, ti voglio bene, davvero! Andiamo! [CUB4_J:CUBAN4] Dove? Non posso prendere prima una tazza di caffè? [CUB4_K:CUBAN4] Non c'è tempo per il caffè! Inoltre, io l'ho già preso. [CUB4_L:CUBAN4] Dobbiamo far fuori gli Haitiani. [CUB4_M:CUBAN4] Tommy, come fai a uccidere un serpente? [CUB4_N:CUBAN4] Lo mordi sul culo! Ah ah ah! [CUB4_O:CUBAN4] Se lo dici te, Umberto. [CUB4_P:CUBAN4] Tommy, recupera una piccola auto Haitiana. [CUB4_Q:CUBAN4] Quando ce l'hai, torna qui e carica il mio ragazzo, [CUB4_R:CUBAN4] Pepe, e portalo fino agli Haitiani. [CUB4_S:CUBAN4] Poi aggiri l'impianto di lavorazione degli Haitiani e utilizzi il solvente come esplosivo. [CUB4_T:CUBAN4] Boom! Bye bye! [CUB4_U:CUBAN4] Umberto, e tu? [CUB4_V:CUBAN4] Oh, io resto nelle retrovie e controllo il café con papà. [CUB4_W:CUBAN4] Non si sente molto bene, sai? [CUB4_02:CUBAN4] ~g~Le bombe verranno piazzate con un timer di 45 secondi. [CUB4_04:CUBAN4] ~r~Hai allarmato la base, adesso non riusciremo mai a entrare! [CUB4_07:CUBAN4] Il solvente è sul retro, amigo. [CUB4_08:CUBAN4] Hola, Amigos. [CUB4_09:CUBAN4] Bueno. Haitiane Putas. Muerte. [CUB4_10:CUBAN4] Vamos. [CUB4_11:CUBAN4] Vamos davvero. [CUB4_12:CUBAN4] Ehi, abbiamo bisogno di un'auto degli Haitiani! [CUB4_13:CUBAN4] Oye, andiamo a trovare i nostri muchachos! [CUB4_14:CUBAN4] Seguimi, compadres. [CUB4_15:CUBAN4] OK, vai dentro... [CUB4_16:CUBAN4] Io posizionerò le bombe, coprimi! [CUB4_17:CUBAN4] CORRI! [CUB4_18:CUBAN4] Amico, questa sì che è una bella parte della città... [CUB4_19:CUBAN4] Questo luogo è una fogna. [CUB4_20:CUBAN4] Avevo una bella donna... viveva da queste parti. [CUB4_21:CUBAN4] Lo sai, fanno delle buone pizze da queste parti. [CUB4_22:CUBAN4] Whoa, amico. Guidi come un cavallo impazzito! [CUB4_23:CUBAN4] Ti sei perso, amico? [CUB4_24:CUBAN4] Hai lasciato indietro Pepe, vai a riprenderlo! [CUB4_03:CUBAN4] ~g~Resta in macchina fino a quando questa non sarà parcheggiata all'interno. [CUB4_26:CUBAN4] ~g~Prendi Pepe, dirigiti a nord verso Little Haiti e ruba una macchina Voodoo. [CUB4_27:CUBAN4] ~g~Raggiungi Rico e gli altri Cubani. [CUB4_28:CUBAN4] ~g~Unisciti agli altri Cubani presso la fabbrica di droga Haitiana. [CUB4_29:CUBAN4] ~g~Cammina su ogni segnalino per posizionare una bomba. [CUB4_30:CUBAN4] ~g~Dopo aver posizionato tutte e tre le bombe, scappa dalla fabbrica prima che esploda. [CUB4_31:CUBAN4] ~g~Scappa dalla fabbrica!!! [CUB4_32:CUBAN4] ~g~Parcheggia l'auto nel punto segnalato ed esci. [CUB4_06:CUBAN4] ~r~Non ti sei allontanato a sufficienza dalla base e abbiamo dovuto interrompere l'esplosione! {=================================== MISSION TABLE FINALE ===================================} [FIN_1A:FINALE] Vieni qui brutto doppiogiochista di merda! [FIN_1B:FINALE] Ti faccio a pezzi, traditore del cazzo! [FIN_1C:FINALE] Questo è l'ultimo ballo per Lance Vance! [FIN_2B:FINALE] Oh, ma davvero! [FIN_2C:FINALE] Te l'avevo detto che ne avevo avuto abbastanza a scuola! [FIN_3:FINALE] Adesso chi ti coprirà il culo, eh Tommy? [FIN_4:FINALE] Sei storia, Tommy, storia! [FIN_5:FINALE] Hai scelto il lato sbagliato, Lance... [FIN_11A:FINALE] Sonny, mi hai rubato quindici anni di vita... [FIN_11B:FINALE] E adesso te la farò pagare! [FIN_12A:FINALE] Non hai ancora capito, vero? [FIN_12B:FINALE] Io ti POSSIEDO, Tommy. [FIN_12C:FINALE] Quei quindici anni erano miei! [FIN_13:FINALE] Prendetelo ragazzi, non ha mai capito niente. [FIN1_01:FINALE] Cosa sta succedendo? [FIN1_02:FINALE] Tommy! Oh bene, bene. Ascolta, ascolta. Uh, ascolta. [FIN1_03:FINALE] A me piacciono i pesci. Adoro i pesci. [FIN1_04:FINALE] Mi piacciono quando nuotano in una boccia o come cibo in un piatto, [FIN1_05:FINALE] ma per quanto li ami, non voglio dormire in loro compagnia. [FIN1_06:FINALE] Va bene, ma adesso i tuoi fratelli italiani stanno venendo per mettermi ai piedi delle belle scarpe in cemento e io... [FIN1_07:FINALE] Sta zitto Ken. Siediti. [FIN1_08:FINALE] Lance, di che diavolo stai parlando? [FIN1_09:FINALE] Sono i tuoi amici del nord, Tommy. Non sono felici di cosa hai fatto al loro uomo. [FIN1_10:FINALE] Oggi verranno giù per questioni di lavoro. [FIN1_11:FINALE] Ci hanno messo di più di quanto credessi... [FIN1_12:FINALE] Ragazzi, dobbiamo essere molto chiari. Non voglio che ci siano dubbi sul fatto che questa è la mia operazione. MIA! [FIN1_13:FINALE] Ken, recupera la prima partita di soldi falsi e metti venti milioni in delle valigette. [FIN1_14:FINALE] Lance, raduna i ragazzi... [FIN2_01:FINALE] Tommy! [FIN2_02:FINALE] Cosa? Niente abbraccio per un vecchio amico? [FIN2_03:FINALE] Sono stato per quindici anni fuori dal giro, [FIN2_04:FINALE] sono un po' arrugginito in fatto di etichetta familiare. [FIN2_05:FINALE] Sempre arrabbiato, vero Tommy? [FIN2_06:FINALE] Non ti avevo detto che il tuo carattere ti avrebbe messo nei guai, eh? [FIN2_07:FINALE] Ci sono venti milioni in quelle valigette... [FIN2_08:FINALE] Quanti hai detto? Dieci? No, undici uomini. [FIN2_09:FINALE] È così che sei diventato il macellaio di Harwood! Eh eh eh! [FIN2_10:FINALE] Mi hai inviato per uccidere un uomo, UN UOMO. Sapevano che stavo arrivando Sonny... [FIN2_11:FINALE] Modera il tono. [FIN2_12:FINALE] Qualcuno potrebbe pensare che stai incolpando me per le sfortunate circostanze. [FIN2_13:FINALE] Prendi i fottuti soldi... [FIN2_14:FINALE] Prendi i fottuti soldi? [FIN2_15:FINALE] Lo sai, Tommy? Ho fatto quello che ho potuto per te. Ho tirato i fili, ho chiesto favori. [FIN2_16:FINALE] Ero tuo amico, Tommy. Pensavo che avresti avuto buon senso, che avresti capito cosa è bene per il business. [FIN2_17:FINALE] Mi fidavo di te, Tommy. E tu mi hai deluso. [FIN2_18:FINALE] Ma alla fine qualcuno nella tua organizzazione del cazzo ha capito come si fa il business. [FIN2_19:FINALE] Non è vero, Lance? [FIN2_20:FINALE] Mi dispiace Tommy. Questa è Vice City. Questo è il suo business. [FIN2_21:FINALE] Ci hai venduto... [FIN2_22:FINALE] No. Ho venduto TE, Tommy, ho venduto TE. [FIN2_23:FINALE] I soldi veri sono di sopra nella cassaforte. [FIN2_24:FINALE] Allora Tommy, questo era il grande piano? [FIN2_25:FINALE] Pensavi davvero che avrei preso i soldi finti? [FIN2_26:FINALE] Salva la faccia e scappa con la coda tra le gambe! [FIN2_27:FINALE] No. [FIN2_28:FINALE] Volevo solo farti incazzare prima di ucciderti. [FIN3_01:FINALE] Tommy? [FIN3_02:FINALE] Oh mio Dio, Tommy! Che cosa è successo? [FIN3_03:FINALE] Che cosa ti sembra sia successo? [FIN3_04:FINALE] Mi sembra ti si sia rovinato l'abito! [FIN3_05:FINALE] Accidenti, Tommy, era così bello! Tommy, che diavolo è successo? [FIN3_06:FINALE] Ho avuto una divergenza di vedute con un socio in affari, sai come vanno queste cose. [FIN3_07:FINALE] Tommy, se io ho una divergenza con un socio, gli mando una lettera di insulti. [FIN3_08:FINALE] Forse gli piscio nella casella della posta. Ma non do il via alla terza guerra mondiale! [FIN3_09:FINALE] Lo sai, forse dovresti parlare con il mio strizzacervelli... [FIN3_10:FINALE] Quello stupido idiota, Lance... [FIN3_11:FINALE] Tommy, non mi è mai piaciuto quel tipo, OK? [FIN3_12:FINALE] È nevrotico, insicuro, egocentrico... quel tipo è un vero stronzo! [FIN3_13:FINALE] Sono felice che l'hai fatto fuori! [FIN3_14:FINALE] Non penso che riceveremo ulteriori problemi dal nord... [FIN3_15:FINALE] ...fondamentalmente perché non c'è più un 'nord'. [FIN3_16:FINALE] Adesso è tutto a sud. [FIN3_17:FINALE] Aspetta, vuoi forse dire ciò che penso io, caro il mio Tommy? [FIN3_18:FINALE] Cosa credi che significhi? [FIN3_19:FINALE] Che siamo in carica... cioè, che sei in carica! Oh Tommy... [FIN3_20:FINALE] Lo sai, Ken, questo potrebbe essere l'inizio di uno splendido rapporto lavorativo... [FIN3_21:FINALE] Dopo tutto, tu sei un convincente, diffamante, fottuto ladro. [FIN3_22:FINALE] e io sono uno psicopatico assassino convinto e uno spacciatore. [FIN3_23:FINALE] Lo so, non è meraviglioso? [FIN_B1:FINALE] ~g~Uccidi ~y~Vance~g~, il fottuto traditore. [FIN_B2:FINALE] ~g~Trova ~p~Sonny~g~ e metti la parola fine a questa storia. [FIN_B3:FINALE] ~g~La Mafia sta cercando di rubare i tuoi soldi. Difendi la cassaforte. [FIN_B4:FINALE] ~g~Sei prossimo alla morte: recupera della ~w~salute~g~ dal basso. [FIN_B5:FINALE] ~g~La Mafia sta rubando i tuoi soldi. Difendi la ~c~cassaforte~g~. [FIN_B7:FINALE] ~r~La Mafia ha rubato i tuoi soldi! [DEFSAFE:FINALE] ~g~Ritorna alla cassaforte e difendila. {=================================== MISSION TABLE FIRETRK ===================================} [F_PASS1:FIRETRK] Incendio spento! [F_FAIL2:FIRETRK] ~r~Sei arrivato tardi! [F_CANC:FIRETRK] ~r~Missione Pompieri annullata! [F_EXTIN:FIRETRK] INCENDI: [F_START:FIRETRK] ~g~Veicolo in fiamme avvistato in ~a~. Vai a spegnere il fuoco. [SIREN_1:FIRETRK] Per attivare la sirena di questo veicolo premi il ~h~~k~~VEHICLE_HORN~~w~. [SIREN_2:FIRETRK] Per attivare la sirena di questo veicolo premi il ~h~~k~~VEHICLE_HORN~~w~. [FIREPRO:FIRETRK] Missione Camion dei pompieri livello 12 completata: adesso sei permanentemente ignifugo!!! [F_FAIL1:FIRETRK] Missione Camion dei pompieri terminata. [F_STAR1:FIRETRK] ~g~Veicoli in fiamme presso l'area ~a~. Vai a spegnere l'incendio. [SPRAY_4:FIRETRK] { reVC update } Premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sparare con il cannone ad acqua e la ~h~~k~~VEHICLE_TURRETLEFT~~w~ e ~h~~k~~VEHICLE_TURRETRIGHT~~w~ per mirare. [SPRAY_1:FIRETRK] { reVC update } Premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sparare con il cannone ad acqua e la ~h~~k~~VEHICLE_TURRETLEFT~~w~ e ~h~~k~~VEHICLE_TURRETRIGHT~~w~ per mirare. {=================================== MISSION TABLE GENERA1 ===================================} [GEN1_A:GENERA1] Mr. Vercetti! [GEN1_B:GENERA1] Colonnello. [GEN1_D:GENERA1] No, grazie. [GEN1_E:GENERA1] Mi vergogno ad ammettere che una delle cause dei nostri problemi comuni sembra sia stata la linguaccia di una persona di cui credevo di potermi fidare. [GEN1_F:GENERA1] Mi sono tenuto Gonzalez per anni, ma ora la sua incompetenza ha raggiunto il fondo. [GEN1_G:GENERA1] Sarebbe solo un bene se eliminassi Gonzalez... [GEN1_H:GENERA1] È stato lui? A me importa solo dei miei soldi. [GEN1_I:GENERA1] Questa gentilezza verrà ricompensata dopo troveremo insieme i tuoi soldi. [GEN1_J:GENERA1] Lo troverai nella sua Penthouse, probabilmente mezzo ubriaco. Usa questo... [GEN1_06:GENERA1] Eeek! Ha una motosega! [GEN1_07:GENERA1] Stammi lontano, brutto bastardo! [GEN1_08:GENERA1] Oddio santo, ho distrutto la mia vita e il mio look! [GEN1_10:GENERA1] Ho deciso di chiudere quella tua maledetta boccaccia! [GEN1_11:GENERA1] Smettila di correre, brutto grassone! [GEN1_12:GENERA1] Stai fermo, così la facciamo finita! [GEN1_13:GENERA1] Smettila di strillare, non gliene frega a nessuno, grassone! [GEN1_C:GENERA1] Grazie per essere venuto. Siediti. Vuoi dell'aragosta? [GEN1_05:GENERA1] ~g~Uccidi Gonzalez! [GEN1_09:GENERA1] Ti pagherò doppio, Tommy, DOPPIO! [GEN1_18:GENERA1] ~r~Gonzalez ha raggiunto sano e salvo la stazione di polizia! [GEN1_19:GENERA1] ~g~La polizia di Vice City ti sta inseguendo! [GEN1_20:GENERA1] ~g~Prendi un veicolo. [GEN1_21:GENERA1] ~g~Raggiungi il ~h~Pay 'N' Spray~g~ in ~h~Vice Point~g~. [GEN1_22:GENERA1] ~g~Guida il tuo veicolo attraverso il carrozziere per perdere il ~h~livello di sospetto~g~, ~h~riparare~g~ e ~h~ricolorare~g~ il mezzo. Costo: ~h~100$~g~. Questa volta è gratis. [GEN1_01:GENERA1] Quando corri, tieni premuto il ~o~tasto |~w~ per preparare un attacco corpo a corpo. [GEN1_02:GENERA1] Quando corri, tieni premuto il ~x~tasto /~w~ per preparare un attacco corpo a corpo. [GEN1_03:GENERA1] Quando corri, tieni premuto il ~h~tasto R1~w~ per preparare un attacco corpo a corpo. [GEN1_14:GENERA1] Rilascia il ~h~~k~~PED_FIREWEAPON~~w~ per eseguire l'attacco. [GEN1_15:GENERA1] Rilascia il ~h~~k~~PED_FIREWEAPON~~w~ per eseguire l'attacco. [GEN1_16:GENERA1] Rilascia il ~h~~k~~PED_FIREWEAPON~~w~ per eseguire l'attacco. [GEN1_23:GENERA1] ~g~Passa per le porte per tornare al piano terra. {=================================== MISSION TABLE GENERA2 ===================================} [COL2_A:GENERA2] Tommy! Vieni qui da me. [COL2_B:GENERA2] Non ti sembra delizioso, vero? Lingua di Tapia? [COL2_C:GENERA2] Eerr... No, no grazie. [COL2_D:GENERA2] Tommy, sei come un vento fresco della pampa che mi libera dall'olezzo della corruzione, [COL2_E:GENERA2] benché mi addolori la recente dipartita, devo continuare come sempre con il mio lavoro. [COL2_F:GENERA2] Non mi sembra ci stiamo avvicinando ai miei soldi... [COL2_G:GENERA2] Tommy, amico mio, non sei più a Liberty. Qui le cose vanno in modo diverso. [COL2_H:GENERA2] Continuerò le mie ricerche, ma nel frattempo ho un interessante accordo da proporti. [COL2_I:GENERA2] Un favore per un amico, Cortez. [COL2_J:GENERA2] Sei un vero amico, Tommy, Ero certo che non mi avresti deluso. [COL2_K:GENERA2] Ho bisogno che tu incontri un corriere che mi ha recuperato una tecnologia di cui ho bisogno... [COL2_1:GENERA2] La pioggia, è così très umido in questo periodo dell'anno... [COL2_2:GENERA2] Cosa? [COL2_3:GENERA2] Ah, comment? [COL2_4:GENERA2] Ascolta, mi manda Cortez. Dammi quei maledetti chip. [COL2_5:GENERA2] Oh... d'accord. [COL2_B1:GENERA2] ~g~Incontra il corriere al Mall. [COL2_B2:GENERA2] ~g~Il correre sta scappando con i chip: non farlo scappare! [COL2_B3:GENERA2] ~g~Porta i chip al Colonnello. [COL2_F1:GENERA2] ~r~Hai ucciso il contatto! [COL2_F2:GENERA2] ~r~Il corriere è morto. Raccogli i chip. [COL2_6A:GENERA2] Fermo, brutto maiale imperialista americano! Questa è proprietà del governo francese. Passamela! [BLIPHLP:GENERA2] Se il segnale sul radar è un triangolo verso l'alto, significa che il bersaglio è in posizione più elevata rispetto al giocatore. [COL2_F3:GENERA2] ~r~I chip adesso riposano in fondo all'oceano. [COL2_F4:GENERA2] ~r~Il corriere è scappato! Non sei riuscito a recuperare i chip. {=================================== MISSION TABLE GENERA3 ===================================} [GEN3_A:GENERA3] Thomas, grazie per essere venuto. [GEN3_B:GENERA3] Perdonami se giungo subito al punto. [GEN3_C:GENERA3] Diaz mi ha chiesto di sovrintendere una piccola transizione di lavoro. [GEN3_D:GENERA3] Speriamo vada meglio dell'ultima... [GEN3_E:GENERA3] Ed è per questo che ho pensato a te, amico. [GEN3_F:GENERA3] Ho lasciato un po' di supporto al parcheggio multipiano. [GEN3_G:GENERA3] Raccogli il materiale, poi raggiungi e sorveglia gli uomini di Diaz all'incontro. [GEN3_H:GENERA3] Gracias, amigo. [GEN3_1:GENERA3] Ami l'azione, vedo... [GEN3_2:GENERA3] Senti, ti capita mai di fare qualcos'altro che non sia seguirmi dappertutto? Perché non vieni con me e mi dimostri di servire a qualcosa? [GEN3_3:GENERA3] Potrei anche farlo. A proposito, mi chiamo Lance. [GEN3_5:GENERA3] Devi essere il nuovo uomo di Cortez. [GEN3_6:GENERA3] Fino a quando non mi capiteranno opportunità più lucrose. [GEN3_7:GENERA3] Arriveranno fra pochi minuti: meglio trovare un buon punto dove appostarsi... [GEN3_8:GENERA3] OK! Io prendo il balcone, tu prendi il tetto dall'altra parte del cortile. [GEN3_9:GENERA3] Sono vivo, brutti stronzi! E tutto grazie a te! Come ti chiami? [GEN3_10:GENERA3] Tommy. [GEN3_11:GENERA3] Ci incontreremo di nuovo, credo! [GEN3_12:GENERA3] Dove diavolo è andato Lance? Merda... [GEN3_14:GENERA3] Tommy! Ho bisogno di aiuto! [GEN3_15:GENERA3] Non preoccuparti, ti copro io! [GEN3_16:GENERA3] Gli uomini di Diaz si stanno facendo massacrare! [GEN3_19:GENERA3] ~g~Haitiani! Stanno mandando a monte l'affare! Proteggi Diaz! [GEN3_20:GENERA3] ~g~Entra nel parcheggio multipiano e recupera ciò che il Colonnello ha lasciato per te. [GEN3_22:GENERA3] Salute di Diaz: [GEN3_23:GENERA3] ~g~Hai lasciato Lance indietro! Vallo a prendere! [GEN3_25:GENERA3] ~r~Lance è morto! [GEN3_28:GENERA3] ~g~Riporta la valigetta a Diaz. [GEN3_29:GENERA3] ~g~Raccogli la valigetta e riportala a Diaz. [GEN3_30:GENERA3] ~r~È scappato con i soldi! Diaz ti strapperà le palle per questo! [GEN3_33:GENERA3] ~r~Dovresti proteggere Diaz e i suoi uomini, non sparargli addosso! [GEN3_34:GENERA3] ~r~Non ci sarà nessun accordo se spari ai Cubani! [GEN3_35:GENERA3] ~g~Ha rubato i soldi di Diaz! [GEN3_36:GENERA3] ~g~Prendi la moto, inseguilo e recupera i soldi di Diaz! [GEN3_37:GENERA3] ~g~Stanno arrivando i Cubani. Assicurati che niente vada storto e proteggi Diaz e Lance. [GEN3_38:GENERA3] ~r~Diaz è morto! Non sei riuscito a proteggerlo! [GEN3_39:GENERA3] ~g~Raggiungi la tua postazione sopra alle scale. [GEN3_44:GENERA3] ~g~Vai con Lance all'appuntamento e proteggi Diaz. [GEN3_40:GENERA3] { reVC update } Per ~h~sparare davanti~w~ su una ~h~moto~w~ premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_41:GENERA3] { reVC update } Per ~h~sparare davanti~w~ su una ~h~moto~w~ premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_46:GENERA3] Meerda! [GEN3_47:GENERA3] Tommy! [GEN3_48:GENERA3] Maledizione! [GEN3_49:GENERA3] SALUTE DI LANCE: [GEN3_50:GENERA3] ~r~Hai distrutto i soldi di Diaz! La prossima volta cerca di non ridurre le banconote in cenere! [GEN3_51:GENERA3] Altri fottuti Haitiani in un merdoso van! [GEN3_54:GENERA3] Non restare lì impalato, idiota, insegui quello stronzo di un Haitiano! [GEN3_55:GENERA3] Tommy! Io resto qua a proteggere Diaz! [GEN3_18:GENERA3] ~g~Stanno arrivando i Cubani, resta vicino a Diaz. Assicurati che niente vada storto e proteggi Diaz e Lance. [GEN3_56:GENERA3] ~r~Diaz è stato assalito e ucciso! La prossima volta, cerca di proteggerlo! [GEN3_57:GENERA3] Il Kruger è un fucile d'assalto che permette di mirare in prima persona. [GEN3_58:GENERA3] Tieni premuto il tasto ~h~R1~w~ per ~h~mirare~w~ con il fucile d'assalto. [GEN3_59:GENERA3] Tieni premuto il tasto ~h~L1~w~ per ~h~mirare~w~ col fucile d'assalto. [GEN3_60:GENERA3] Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile d'assalto. [GEN3_61:GENERA3] Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile d'assalto. [GEN3_62:GENERA3] Premi il tasto ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~ col fucile d'assalto. [GEN3_63:GENERA3] Oltre a permetterti di sparare in corsa, con le ~h~moto~w~ puoi anche ~h~sparare in avanti~w~. [GEN3_64:GENERA3] { reVC update } Per sparare in avanti quando sei su una moto, premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_65:GENERA3] { reVC update } Per sparare in avanti quando sei su una moto, premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_66:GENERA3] { reVC update } Per sparare in avanti quando sei su una moto, premi il tasto ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_67:GENERA3] Hai bisogno di una mitragliatrice per poter sparare in avanti. [GEN3_53:GENERA3] I MIEI SOLDI! [GEN3_52:GENERA3] Questi Haitiani pensano che possono farsi RICARDO DIAZ!?! {=================================== MISSION TABLE GENERA4 ===================================} [DETON:GENERA4] DETONAZIONE: [COL4_3:GENERA4] CONVOGLIO ALT! [COL4_6:GENERA4] SIAMO SOTTO FUOCO NEMICO! [COL4_7:GENERA4] Civile, si allontani dal carro armato! [COL4_8:GENERA4] HO DETTO, si allontani, IMMEDIATAMENTE! [COL4_9:GENERA4] POSIZIONI DIFENSIVE! [COL4_11:GENERA4] Porta via quel civile, soldato! - Signorsì, signore! [COL4_12:GENERA4] Civile nel CARRO ARMATO! FERMATELO! [COL4_13:GENERA4] Questo è un convoglio militare, non ostacoli il passaggio. [COL4_14:GENERA4] Fallo fuori, soldato. [COL4_15:GENERA4] Spostate quel veicolo civile via dalla strada! -Signore! Sposto il veicolo, signore! [COL4_17:GENERA4] OK, PLOTONE, IN MARCIA! [COL4_18:GENERA4] Qualcuno è entrato nel carro armato, signore! [COL4_19:GENERA4] Vai a prendere delle ciambelle, soldato! - Signorsì, signore! [COL4_20:GENERA4] Bersaglio acquisito, signore! [COL4_21:GENERA4] CECCHINO! [COL4_22:GENERA4] Io me la squaglio. [COL4_23:GENERA4] Obiettivo completato! Plotone riposo! -Andiamo a mangiarci qualche ciambella. [COL4_24:GENERA4] Protocollo di sicurezza Delta India Echo attivato! Autodistruzione del veicolo attivata! [COL4_26:GENERA4] Preparati a morire, maledetto comunista! [COL4_B2:GENERA4] ~r~Il carro armato è arrivato con successo a destinazione! [COL4_B5:GENERA4] ~r~Il carro armato è stato distrutto! [COL4_01:GENERA4] Diaz era soddisfatto e vorrebbe incontrarti nuovamente. [COL4_02:GENERA4] È un buon segno? [COL4_03:GENERA4] Ma certo! Benché credo che Diaz sia il responsabile della nostra sfortunata perdita... [COL4_04:GENERA4] Che cosa glielo fa pensare? [COL4_05:GENERA4] Nessuno accusa direttamente un uomo come Diaz... sto solo pensando a voce altra... [COL4_06:GENERA4] Non importa. Ho una proposta che potrebbe interessarti... [COL4_07:GENERA4] Non ho più tempo per i suoi lavori, Cortez. [COL4_08:GENERA4] Pensavo che un uomo con dei debiti così pericolosi fosse desideroso di opportunità. Per favore, Tommy, almeno ascoltami. [COL4_09:GENERA4] Mi dica... [COL410:GENERA4] Ho un acquirente per un mezzo militare che sta attraversando la città. Recuperalo per me... [COL411:GENERA4] e, quando lo avrai, vorrei che mi chiamassi immediatamente. Poi... [COL4_B4:GENERA4] ~g~Il carro armato è chiuso. Trova un modo per far uscire gli occupanti. [COL4_1:GENERA4] Che cos'ha il mitragliatore? -Non so signore! [COL4_4:GENERA4] -Vai di sopra, soldato. -Signorsì, signore! [COL4_B1:GENERA4] ~g~Impadronisciti del mezzo militare che sta attraversando la città. [COL4_B3:GENERA4] ~g~Porta il carro armato al garage del Colonnello prima che si autodistrugga. [COL4_B6:GENERA4] ~g~Trova un modo per rubare il carro armato! [COL4_B7:GENERA4] ~g~Porta il carro armato dentro al garage. [COL4_B8:GENERA4] ~g~Esci dal carro armato e abbandona il garage. {=================================== MISSION TABLE GENERA5 ===================================} [COL5A_1:GENERA5] Le circostanze richiedono una rapida partenza, amigo. [COL5A_2:GENERA5] Qual è il problema? [COL5A_3:GENERA5] Uh, i Francesi rivogliono la loro tecnologia missilistica indietro dopo quell'incidente... [COL5A_4:GENERA5] Sento che è giunta l'ora di partire per porti più sicuri. [COL5A_5:GENERA5] Non sarebbe meglio via aria? [COL5A_6:GENERA5] Sarei morto ancor prima di raggiungere il check-in. Inoltre, devo portare la mia merce fuori dal paese. [COL5A_7:GENERA5] Serve un'altra pistola? [COL5A_8:GENERA5] Tu, amico, vali come dieci pistole... ah ah ah! [COL5B_1:GENERA5] Thomas, mi hai protetto e servito bene. [COL5B_2:GENERA5] Ma adesso devi abbandonarci prima che raggiungiamo il mare aperto. [COL5B_4:GENERA5] Grazie mille, Colonnello. [COL5B_5:GENERA5] Un'ultima richiesta, mentre sono via: terresti un occhio su Mercedes per me? [COL5B_6:GENERA5] Credo sappia badare a sé stessa, ma certo, la terrò sott'occhio. [COL5B_7:GENERA5] Grazie mille, amico. A presto! [COL5B_8:GENERA5] Adios, amigo. [COL5_7:GENERA5] Smettila di spararmi! [COL5_9:GENERA5] Tommy, falli smettere di sparare! [COL5_10:GENERA5] Ho l'immunità diplomatica! [COL5_11:GENERA5] Non sparare, sono il Colonnello! [COL5_12:GENERA5] Thomas, uccidili: la mia nazione ti sarà riconoscente. [COL5_13:GENERA5] Tommy, siamo assaliti dai Francesi! [COL5_14:GENERA5] Tommy, dovunque guardo vedo dei Francesi. Io li odio! [COL5_15:GENERA5] Tommy, come stai? [COL5_16:GENERA5] Questo è per la Piaf e Gainesbourg e per le vostre stupide baguette! [COL5_1:GENERA5] A babordo! A babordo! [COL5_2:GENERA5] Ci attaccano da tribordo! [COL5_3:GENERA5] Il ponte qui davanti! [COL5_4:GENERA5] Hanno un elicottero! [COL5_B1:GENERA5] ~g~Difendi a tutti i costi il Colonnello e il suo yacht. [COL5_B2:GENERA5] ~g~Vai a prua e libera la strada per il passaggio dello yacht del Colonnello. [COL5_B3:GENERA5] ~r~Il Colonnello è morto! [COL5_B4:GENERA5] ~g~Abbatti l'elicottero! [COL5B_3:GENERA5] Farò calare la mia lancia personale. Tienila, amico, un pegno della mia gratitudine. [COL5_B5:GENERA5] ~g~Abbatti gli elicotteri e proteggi lo yacht. [COL5_B6:GENERA5] ~g~Hai finito i colpi: recuperane altri dalle scale al ponte superiore. [COL5_B7:GENERA5] ~g~Sei a corto di salute: recuperane altra dalle scale al ponte superiore {=================================== MISSION TABLE HAIT1 ===================================} [HAM1_A:HAIT1] Permesso? [HAM1_B:HAIT1] Entra pure, caro, e riposa l'anima. [HAM1_C:HAIT1] Devi essere il grande uomo cattivo di cui parlava mio nonno. [HAM1_D:HAIT1] Mi ha raccontato di te, sai, quando mi fa visita, [HAM1_E:HAIT1] e degli altri che ti aspettano. [HAM1_F:HAIT1] Adesso, siamo tutti morti da tempo, ma tu sai, [HAM1_G:HAIT1] non vorrei essere nei tuoi panni, eh eh eh! [HAM1_H:HAIT1] Ho ricevuto un messaggio. Sono venuto. [HAM1_I:HAIT1] Li puoi sentire? [HAM1_J:HAIT1] Stanno chiamando il tuo nome, devono proprio volerti, non credi? [HAM1_K:HAIT1] Magari adesso dai una mano alla zia Poulet, uh, e forse lei ti aiuta. [HAM1_L:HAIT1] Forse potrà darti un piccolo juju dopo tutto questo. [HAM1_M:HAIT1] Un po' di magia per dare all'uomo di legge il malocchio, hmmm? [HAM1_N:HAIT1] Ascolta, tutto questo è, uhm... darmi cosa? [HAM1_O:HAIT1] Credo, credo proprio di aver sbagliato indirizzo... [HAM1_P:HAIT1] Fai per me questo favore, Tommy... [HAM1_Q:HAIT1] I Cubani, foofoo molto orgogliosi, hmmm, [HAM1_R:HAIT1] hanno dato molti grattacapi ai miei ragazzi Haitiani. [HAM1_S:HAIT1] Adesso hanno detto alla polizia dove nascondo le mie polveri. [HAM1_T:HAIT1] Pensano sia droga, gli stupidi. [HAM1_U:HAIT1] Adesso fai il bravo, Tommy, e vai a prendere le polveri per la zia Poulet. [HAM1_V:HAIT1] Sì, sì, certo, vado. [HAM1_1:HAIT1] ~g~I poliziotti si stanno avvicinando alle nostre riserve. Fermali prima che ci riescano. [HAM1_2:HAIT1] ~r~I poliziotti sono arrivati per primi alla riserva! [HAM1_3:HAIT1] ~g~Riporta questa roba al nascondiglio! [HAM1_4:HAIT1] ~g~Ottimo! Adesso pensa al prossimo! [HAM1_6:HAIT1] ~r~La riserva è stata distrutta, idiota! [HAM1_7:HAIT1] ~g~La polizia ha preso la nostra riserva! Recuperala prima che se ne vada! [HAM1_8:HAIT1] ~g~I poliziotti sono vicini alla riserva: datti una mossa! [HAT_1A:HAIT1] ~g~Non muoverti, allocco! {=================================== MISSION TABLE HAIT2 ===================================} [HAT2_B1:HAIT2] ~g~Raggiungi il van che contiene le bombe volanti. [HAT2_B2:HAIT2] Uccidi i Cubani... [HAT2_B4:HAIT2] ...e distruggi le loro imbarcazioni! [HAT2_B5:HAIT2] ~g~I Cubani stanno fuggendo: non lasciarli scappare! [HAT2_B6:HAIT2] ~r~L'aereo radiocomandato si sta allontanando dal raggio d'azione! [HAT2_B7:HAIT2] ~g~Uno dei Cubani sta fuggendo in macchina. Non lasciare nessun testimone! [HAT2_B8:HAIT2] ~g~Non hai più aerei radiocomandati! [HAT2_B9:HAIT2] Aerei radiocomandati: [HAT2_1:HAIT2] Oh. Scusa, devo aver sbagliato indirizzo... [HAT2_2:HAIT2] Puoi entrare comunque a riposare i piedi e a bere un po' di tè. [HAT2_3:HAIT2] Hai qualcosa per me, Tommy? [HAT2_4:HAIT2] Sì... [HAT2_5:HAIT2] Questo posto mi sembra familiare. Un odore della mia gioventù... un deja vu... [HAT2_6:HAIT2] Adesso Tommy, ti sussurrerò una piccola commissione che devi farmi. Ascoltami attentamente, va bene? [HAT2_7:HAIT2] Mi ricordi qualcuno che... [HAT2_8:HAIT2] I Cubani hanno imbarcazioni molto veloci che utilizzano per trasportare la droga via mare. [HAT2_9:HAIT2] È il loro modo di vivere. [HAT2_10:HAIT2] Mio nipote ha costruito delle simpatiche bombe per farli fuori. [HAT2_11:HAIT2] Fai esplodere le barche e inchioda le loro bare. [HAT2_12:HAIT2] Beh, grazie per il tè. [HAT2_B3:HAIT2] { reVC update } Premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~ per sganciare una bomba. Premi il ~h~~k~~VEHICLE_ENTER_EXIT~ "~w~ per annullare. {=================================== MISSION TABLE HAIT3 ===================================} [HAM3_A:HAIT3] Salve, salve, io, uh... Stavo cercando qualcuno... [HAM3_B:HAIT3] Mi sembri affamato, Tommy. [HAM3_C:HAIT3] Ci conosciamo? [HAM3_D:HAIT3] Zitto ora. [HAM3_E:HAIT3] Un'ultima cosa e potrai andare, Tommy. [HAM3_F:HAIT3] I miei ragazzi sono in guerra con i Cubani. [HAM3_G:HAIT3] Ma niente pistole. [HAM3_H:HAIT3] Hmmm, ma i Cubani hanno una sorpresa in serbo. [HAM3_I:HAIT3] Mentre combattono per la strada, tu prendi questo fucile e li elimini durante la confusione. [HAM3_J:HAIT3] Nessuno ti vede, nessuno ti sente. [HAM3_K:HAIT3] Adesso, Tommy, fai questo per me e non sarai più aggrappato al mio grembiule. [HAM3_1:HAIT3] ~g~Dobbiamo vincere questa battaglia. Se tutti gli Haitiani muoiono, sarà la fine. [HAM3_3:HAIT3] ~g~Sospetto che i Cubani bareranno, per cui stai in guardia. [HAM3_4:HAIT3] ~r~Ti hanno visto! La missione è fallita! [HAM3_5:HAIT3] ~g~Devi eliminare i Cubani da lontano. Non devono vederti. [HAM3_8:HAIT3] ~g~Gli Haitiani stanno morendo! Migliora la tua mira! [HAM3_7:HAIT3] ~g~Attento! I Cubani hanno chiamato rinforzi. Uccidili tutti quanti! [HAM3_2:HAIT3] ~r~Gli Haitiani sono morti! [HAM3_L:HAIT3] Sssì zia... {=================================== MISSION TABLE HOTEL ===================================} [INTB_A:HOTEL] Tommy! Tommy, quanto tempo è passato! [INTB_B:HOTEL] Ciao Sonny. [INTB_C:HOTEL] Lo so, lo so, sei sopraffatto dall'emozione. [INTB_D:HOTEL] Quindici anni... sembra come se fosse ieri. [INTB_E:HOTEL] Credo dipenda dal punto di vista. [INTB_F:HOTEL] Ehi, stare dentro per la famiglia non è una passeggiata, [INTB_G:HOTEL] ma la famiglia si preoccupa dei suoi, OK? [INTB_H:HOTEL] Allora, raccontami come è andata... Eri in ballo con l'oro bianco? [INTB_I:HOTEL] Ascolta Sonny, ci avevano incastrato. L'incontro era un'imboscata. Harry e Lee sono morti. [INTB_J:HOTEL] Basta con queste storie, Tommy. Hai ancora i soldi, vero? [INTB_K:HOTEL] ...no Sonny... non ho i soldi. [INTB_L:HOTEL] Quelli erano i miei soldi, Tommy, I MIEI SOLDI! [INTB_M:HOTEL] Non cercare di fottermi, Tommy, perché sai che non sono una persona a cui piace essere presa per il culo! [INTB_N:HOTEL] Aspetta Sonny. [INTB_O:HOTEL] Hai la mia parola che recupererò i tuoi soldi e la droga. [INTB_P:HOTEL] E ti farò avere per posta l'uccello dei responsabili. [INTB_Q:HOTEL] Ehi, ne sono certo. Non sei uno stupido, Tommy, ma ti avverto: non lo sono neanch'io. [INTB_R:HOTEL] Se fosse stato qualcun altro, adesso sarebbe già MORTO. [INTB_S:HOTEL] Ma visto che sei tu, che ci conosciamo da tempo, ti permetterò di gestire la situazione. [INTB_T:HOTEL] Ehi, Sonny, hai la mia parola. [INTB_U:HOTEL] Mi farò vivo. {=================================== MISSION TABLE ICECRE1 ===================================} [ICC1_4:ICECRE1] ~g~Non ci sono clienti in quest'area, prova in un'altra. [ICC1_5:ICECRE1] Transazioni effettuate: [ICC1_7:ICECRE1] ~g~Riceverai soldi per ogni transazione, ma più ne esegui, maggiori saranno le probabilità di attirare l'attenzione della polizia. [ICC1_8:ICECRE1] ~g~Per eseguire una transazione, ~h~parcheggia il camioncino~g~ e premi il ~h~~k~~VEHICLE_HORN~~g~ per suonare il campanello e attrarre i clienti. [ICC1_9:ICECRE1] ~g~Le gang locali non apprezzeranno il tuo lavoro nel loro territorio, per cui aspettati azioni ostili. [ICC1_10:ICECRE1] ~g~Hai portato a termine ~1~ transazioni! [ICC1_11:ICECRE1] ~g~Hai portato a termine ~1~ transazione! [ICC1_13:ICECRE1] ~r~Non hai portato a termine nessuna transazione. [ICC1_14:ICECRE1] BENI FABBRICA DI GELATO ACQUISITI [ICC1_15:ICECRE1] ~g~La fabbrica di gelato d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. [ICC1_16:ICECRE1] ~g~Utilizza il Mr. Whoopee per distribuire i prodotti Cherry Poppers per Vice City. [ICE_AT1:ICECRE1] FABBRICA DI GELATO COMPLETATA [ICE_AT2:ICECRE1] ~g~La fabbrica Cherry Popper d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. [ICC1_17:ICECRE1] Missione di distribuzione terminata. [ICC1_18:ICECRE1] Vendita totale gelati: ~1~$ [ICC1_19:ICECRE1] Transizioni totali eseguite: ~1~ {=================================== MISSION TABLE ICECUT ===================================} [ICC1_A:ICECUT] Chi sei? [ICC1_B:ICECUT] Il nuovo proprietario. [ICC1_C:ICECUT] Sei per caso, o lo sei mai stato, un bambino? [ICC1_D:ICECUT] Ma di cosa stai parlando? [ICC1_E:ICECUT] Sei stato un bambino? [ICC1_F:ICECUT] Certo! Calmati! Che ti prende? [ICC1_G:ICECUT] Lo sapevo. Un bambino. [ICC1_H:ICECUT] Uno sporco, puzzolente, moccioso, lamentoso, vile, vomitevole, piagnucoloso piccolo bambino! [ICC1_I:ICECUT] Un bambino... un mostruoso, orribile disgustoso piccolo bambino. Boo hoo. [ICC1_J:ICECUT] La mamma non ti ama, brutto piccolo avanzo! [ICC1_K:ICECUT] Ow! Accidenti, calmati! [ICC1_L:ICECUT] Io ODIO i bambini, odio i poppanti. [ICC1_M:ICECUT] Sono sporchi, puzzolenti, mocciosi, vomitevoli piccoli... [ICC1_N:ICECUT] Ne ho avuto abbastanza! [ICC1_O:ICECUT] Ma che diavolo hai? [ICC1_P:ICECUT] Tu fai gelati, vero? Sono solo per bambini. [ICC1_Q:ICECUT] Ma che psicopatica sei? [ICC1_R:ICECUT] È così mi sono chiesta: perché fare felici i bambini se li odio? [ICC1_S:ICECUT] Oh, stupidi, schifosi, mocciosi... [ICC1_T:ICECUT] Sta zitta! [ICC1_U:ICECUT] ...Monellaccio! [ICC1_V:ICECUT] La gelateria è solo una facciata. [ICC1_W:ICECUT] Noi distribuiamo altro, prodotti non caseari. [ICC1_X:ICECUT] E se vedo un bambino, non perdo occasione. [ICC1_Y:ICECUT] Non è vero, bambini? Sì, sì, lo faccio. La mamma non vi ama. [ICC1_Z:ICECUT] Lei vi ODIA! [ICC1_ZA:ICECUT] PROPRIETÀ ACQUISTATA! {=================================== MISSION TABLE INTRO ===================================} [INT1_A:INTRO] Tommy Vercetti... Ommerda! [INT1_B:INTRO] Pensavo non lo avrebbero mai rilasciato. [INT1_C:INTRO] Ha tenuto la testa bassa... molta gente ha dimenticato. [INT1_D:INTRO] Ma molti ricorderanno ben presto... [INT1_E:INTRO] Quando lo vedranno camminare per le strade del loro vicinato. [INT1_F:INTRO] Non sarà una cosa positiva per il business. [INT1_G:INTRO] Beh, che cosa facciamo, Sonny? [INT1_H:INTRO] Lo trattiamo come un amico e lo teniamo impegnato fuori città, OK? [INT1_I:INTRO] Parlavamo di espanderci verso sud, vero? [INT1_J:INTRO] Vice City è oro puro in questi giorni. [INT1_K:INTRO] I Colombiani, i Messicani, che diamine, [INT1_L:INTRO] anche i rifugiati Cubani si sono ritagliati una bella fetta della torta. [INT1_M:INTRO] Ma si tratta di droga, Sonny, [INT1_N:INTRO] nessuno delle famiglie toccherà quella merda! [INT1_O:INTRO] I tempi cambiano. [INT1_P:INTRO] Le famiglie non possono girare le spalle mentre i nostri nemici si godono il raccolto. [INT1_Q:INTRO] Per cui mandiamo qualcuno a fare il lavoro sporco [INT1_R:INTRO] e a prendere una bella fetta per noi, OK? [INT1_S:INTRO] Chi è il nostro contatto laggiù? [INT1_T:INTRO] Ken Rosenberg, uno schmuck di un avvocato. [INT1_U:INTRO] Come farà a tenere al guinzaglio Vercetti? [INT1_V:INTRO] Non dovrà farlo. [INT1_W:INTRO] Mandiamolo alla ribalta a Vice City [INT1_X:INTRO] con un po' di denaro per cominciare, OK? [INT1_Y:INTRO] Lasciamogli qualche mese. [INT1_Z:INTRO] Poi andiamo giù, [INT1_A1:INTRO] gli facciamo visita, giusto? [INT1_A2:INTRO] E vediamo come se la sta cavando... [INT2_A:INTRO] Ehi, ehi, amici! Sono, uh, Ken Rosenberg! Eh, eh, ottimo, ehi! [INT2_B:INTRO] Bene, uh, vi accompagno in auto all'appuntamento, OK? [INT2_C:INTRO] Ho parlato con i fornitori e loro sono molto, ehm, [INT2_D:INTRO] desiderosi di avviare una relazione commerciale, per cui, uh, [INT2_E:INTRO] se tutto dovesse andare come deve, noi, dovremmo, uh, [INT2_F:INTRO] ottenere dei buoni risultati economici, il che, cioè... [INT2_G:INTRO] è bene... [INT2_H:INTRO] OK allora. Sono fratelli, OK. [INT2_I:INTRO] Uno si occupa del, uh, business, [INT2_J:INTRO] mentre l'altro si occupa dei voli. [INT2_K:INTRO] Adesso operano dal Messico, [INT3_A:INTRO] OK, sono loro sull'elicottero. [INT3_B:INTRO] Bene, questo è l'accordo. [INT3_C:INTRO] Vogliono uno scambio diretto in campo aperto. [INT3_D:INTRO] Va bene? OK, nervi saldi, andiamo. [INT3_E:INTRO] Vedi di star calmo. [INT3_F:INTRO] Resto qui con l'auto pronta a partire! [INT3_G:INTRO] Chiaro? [INT3_H:INTRO] Colombiana 100% pura di prima classe, amici miei. [INT3_I:INTRO] I verdoni? [INT3_J:INTRO] Da dieci e da venti... usati. [INT3_L:INTRO] Forza, vieni qua! Muoviti! [INTRO1:INTRO] metto fuori la testa per un maledetto secondo, e il fato mi tira palate di merda in faccia! [INTRO2:INTRO] Vai a riposarti. [INTRO3:INTRO] Che cosa intendi fare? [INTRO4:INTRO] Passerò domani a trovarti in ufficio e troveremo un modo per risolvere questo casino. [INT3_K:INTRO] Siamo d'accordo allora, amico. AH AH! [INT3_M:INTRO] Fammi dare un'occhiata. [INT2_L:INTRO] no, no, no, aspetta... {=================================== MISSION TABLE KENT1 ===================================} [KPM1_A:KENT1] Ehi bello, ho delle informazioni sul tuo amico. [KPM1_B:KENT1] Ma di cosa stai parlando? [KPM1_C:KENT1] Conosci quel fallito di Diaz, il signore degli spacciatori? [KPM1_D:KENT1] Ha preso il tuo amico, Lance. Sembra che abbia cercato di scavalcarlo... [KPM1_E:KENT1] ma non ha saltato abbastanza in alto, se capisci cosa intendo. [KPM1_F:KENT1] Dove lo hanno portato? Senza giri di parole! [KPM1_G:KENT1] Stai calmo! Lo hanno trasportato attraverso la città fino alla discarica. [KPM1_H:KENT1] Maledizione... Brutto idiota. [KPM1_2:KENT1] ~r~Dovevi portare Lance fuori vivo da lì! [KPM1_3:KENT1] SALUTE DI LANCE: [RESC_1:KENT1] Ce la fai a usare un'arma? [RESC_2:KENT1] Certo... credo... È bello rivederti! [RESC_3:KENT1] Andiamocene da qua. [RESC_4:KENT1] Bene, con questo hai mandato a puttane tutti i miei bei piani. L'hai proprio fatta grossa, Lance. [RESC_5:KENT1] Ha ucciso mio fratello. Che cosa ti aspettavi facessi, che gli tagliassi il prato? [RESC_6:KENT1] Dobbiamo far fuori quello stronzo di Diaz prima che sia lui a farlo. [RESC_7:KENT1] Preparati e incontriamoci al ponte di Star Island, OK? [RESC_8:KENT1] OK, ricevuto. [KPM1_1:KENT1] ~g~Lance viene tenuto nella discarica: vai a salvarlo! [KPM1_4:KENT1] ~g~Porta Lance all'ospedale! [M_PASSN:KENT1] MISSIONE COMPLETATA! [KPM1_5:KENT1] ~g~Gli uomini di Diaz ti stanno dietro: porta Lance all'ospedale. {=================================== MISSION TABLE KICKSTT ===================================} [KICK1_2:KICKSTT] ~r~Non sei tornato abbastanza velocemente alla tua moto! [KICK1_7:KICKSTT] ~r~Hai distrutto la moto! [KICK1_8:KICKSTT] ~g~Ritorna sulla moto! [KICK1_T:KICKSTT] TEMPO IMPIEGATO: [KICKTM:KICKSTT] ~b~DURATA EVENTO: ~1~:~1~ [KICKTM2:KICKSTT] ~b~DURATA EVENTO: ~1~:0~1~ [GETBIKE:KICKSTT] ~g~Hai ~1~ secondi per tornare sulla tua moto prima che la missione abbia termine. [KICK1_1:KICKSTT] ~g~Completa il tracciato il più velocemente possibile. [KICK1_6:KICKSTT] ~g~Complimenti! [KICK_10:KICKSTT] ~g~Usa il Sanchez per completare il percorso passando per tutti i punti di controllo. [KICK_12:KICKSTT] ~g~Ti sei bloccato! [KICK_13:KICKSTT] ~g~Ci hai messo troppo tempo! [KICK_11:KICKSTT] ~g~Per abbandonare la missione, posizionati a piedi sul ~q~segnalino rosa~g~. {=================================== MISSION TABLE LAWYER1 ===================================} [LAW1_A:LAWYER1] Vai a riposarti un po', ha detto... [LAW1_B:LAWYER1] ...ho passato tutta la sera su questa seggiola a bere caffè con la luce spenta. [LAW1_C:LAWYER1] È un disastro. Siamo veramente fottuti, amico! [LAW1_D:LAWYER1] Quei gorilla, non sto scherzando, verranno qui da me e mi staccheranno la testa. È ridicolo! [LAW1_E:LAWYER1] NON mi sono laureato in giurisprudenza per questo! Bene, dimmi un po': che cosa hai intenzione di fare? [LAW1_F:LAWYER1] Sta zitto, siediti e rilassati. Ti dirò che cosa faremo. [LAW1_G:LAWYER1] Tu dovrai scoprire chi ha preso la nostra cocaina... e io li farò fuori. [LAW1_H:LAWYER1] È una buona idea. Anzi, è un'OTTIMA idea. Fammici pensare, fammici pensare, fammici pensare. [LAW1_I:LAWYER1] Ah! C'è quel Colonnello in pensione, il Colonnello Juan Garcia Cortez. [LAW1_J:LAWYER1] È stato lui che mi ha aiutato a preparare il piano [LAW1_K:LAWYER1] ben lontano dai criminali di Vice City. OK? [LAW1_L:LAWYER1] Adesso ascolta: sta organizzando un party nella baia sul suo costoso yacht [LAW1_M:LAWYER1] e tutte le personalità di spicco di Vice City saranno presenti. [LAW1_N:LAWYER1] Io ho un invito, chiaramente ne ho uno... [LAW1_O:LAWYER1] ma non ho la minima intenzione di mettere la testa fuori dalla porta, per nessun motivo! [LAW1_P:LAWYER1] Te l'ho detto, sta zitto! Ci andrò io... [LAW1_Q:LAWYER1] Whoa, whoa, whoa! Ehi, anche a me piace lo stile del 78, ma, lo sai, non si tratta di una festa con birra e spogliarelliste. [LAW1_R:LAWYER1] Intendo dire, senza offesa, che molta gente potrebbe girarsi a guardarti per la ragione sbagliata... [LAW1_S:LAWYER1] Vuoi dire che c'è qualcosa che non va in come mi vesto? [LAW1_T:LAWYER1] OK, ascolta. Fermati da Rafael's, digli che ti mando io e lui si occuperà di renderti presentabile. [LAW1_U:LAWYER1] OK, forza, andiamo... [LAWP_1:LAWYER1] Buenas noches. [LAWP_2:LAWYER1] Da quanto ho capito, è qui per conto di Mr. Rosenberg. [LAWP_3:LAWYER1] Spero che gli avvenimenti recenti non abbiano avuto effetti sulla sua salute o, uh, [LAWP_4:LAWYER1] sulla sua sanità mentale, Mr...? [LAWP_5:LAWYER1] Vercetti. Ha solo avuto un attacco di... agorafobia. [LAWP_6:LAWYER1] Eccellente, eccellente. E lei? [LAWP_7:LAWYER1] Voglio solo la mia merce. [LAWP_8:LAWYER1] Ah. Si è trattato di sfortunate circostanze per tutte le persone coinvolte. [LAWP_9:LAWYER1] Chiaramente, ho dato il via a delle ricerche personali [LAWP_10:LAWYER1] ma una situazione così delicata richiede tempo. [LAWP_11:LAWYER1] Magari ne parleremo più tardi, va bene? [LAWP_12:LAWYER1] Nel frattempo, le presento mia figlia, [LAWP_13:LAWYER1] Mercedes! [LAWP_14:LAWYER1] Cara mia, perché non ti prendi cura del nostro ospite mentre io mi occupo delle mie cose? [LAWP_15:LAWYER1] Certamente, papà. [LAWP_16:LAWYER1] Mi scusi. [LAWP_17:LAWYER1] Mercedes? [LAWP_18:LAWYER1] Non è facile portare questo nome. [LAWP_19:LAWYER1] Comunque, lasci che le presenti alcuni dei nostri distinti ospiti... [LAWP_20:LAWYER1] Lui è Alex Shrub, il nostro deputato, con Candy Suxxx, la famosa star siliconata... [LAWP_21:LAWYER1] Avete conosciuto la mia dolce moglie, Laura? [LAWP_22:LAWYER1] Veramente questa è Candy, mia moglie si trova in Alabama ora. [LAWP_23:LAWYER1] E da quella parte abbiamo la stella dei Vice City Mambas, BJ. [LAWP_24:LAWYER1] un vero ammaliatore [LAWP_25:LAWYER1] L'ho placcato, davvero, e l'ho fatto finire su una sedia a rotelle! [LAWP_26:LAWYER1] Ah, ah, questa è buona! [LAWP_27:LAWYER1] Beh, adesso sto cercando di acquistare dei terreni di qualità. [LAWP_28:LAWYER1] E quella specie di anfibio da piscina è Jezz Torrent, [LAWP_29:LAWYER1] voce solista dei Love Fist. [LAWP_30:LAWYER1] Ve lo posso dire... sapete come giocano a ping-pong in Tailandia? [LAWP_31:LAWYER1] Da non crederci... [LAWP_32:LAWYER1] di certo, non richiede l'uso della racchetta, se capite cosa intendo! [LAWP_33:LAWYER1] Impotente. [LAWP_34:LAWYER1] E il loquace trio. [LAWP_35:LAWYER1] Quella pustola sudata e addormentata è il braccio destro di papà, Gonzalez, [LAWP_36:LAWYER1] mentre gli altri due sono il pastore Richards [LAWP_37:LAWYER1] e il regista pseudo-intellettuale, Steve Scott. [LAWP_38:LAWYER1] ...pare che abbiano una passione per le ninfomani... [LAWP_39:LAWYER1] ...quando lo squalo gigante arriva e [LAWP_40:LAWYER1] strappa a morsi i loro uccelli! [LAWP_41:LAWYER1] Ah! Non si è mai visto qualcosa del genere prima, vero? [LAWP_42:LAWYER1] Colonnello! [LAWP_43:LAWYER1] I suoi party sono sempre un successo! Ah ah ah! [LAWP_44:LAWYER1] Posso solo chiedere scusa per il mio ritardo. [LAWP_45:LAWYER1] Ah, de nada amigo. E come vanno gli affari? [LAWP_46:LAWYER1] Sai, sono molto difficili...i nemici sono sempre alle costole. [LAWP_47:LAWYER1] Il tempo di ricompensare gli amici e liquidare i nemici, amigo. [LAWP_48:LAWYER1] Chi è il chiacchierone? [LAWP_49:LAWYER1] Ricardo Diaz. È Mr. Coca. [LAWP_50:LAWYER1] Mercedes! [LAWP_51:LAWYER1] Oh, stavo giusto riaccompagnando i miei amici in città. [LAWP_52:LAWYER1] Un'altra volta, Ricardo! [LAWP_53:LAWYER1] Andiamocene. [LAWP_54:LAWYER1] Anzi, portatemi al club Pole Position. [LAW1_2:LAWYER1] ~g~Raggiungi lo yacht del Colonnello. [LAW1_4:LAWYER1] ~r~Hai ucciso la figlia del Colonnello! [LAW1_5:LAWYER1] Lavorerai per mio padre? [LAW1_6:LAWYER1] Forse. [LAW1_7:LAWYER1] Ti dispiace se appoggio la mano tra le tue gambe? [LAW1_8:LAWYER1] Forse... [LAW1_9:LAWYER1] È così difficile avere un padre ricco e potente. Vamos. [LAW1_10:LAWYER1] Ci vediamo in giro, stallone! [LAW1_11:LAWYER1] Sono sicuro di sì. [LAW1_12:LAWYER1] Hmmm... bella moto. [LAW1_13:LAWYER1] No! La mia moto! [LAW1_3:LAWYER1] ~g~Porta la figlia del Colonnello al club Pole Position. [HELP20:LAWYER1] Segui il ~h~segnale a forma di maglietta~w~ per trovare Rafael's. [LAW1_14:LAWYER1] Accidenti, mi piace: la tua moto va alla grande. [LAW1_15:LAWYER1] Eh sì, l'ho appena presa da Howlin' Pete's. {=================================== MISSION TABLE LAWYER2 ===================================} [LAW2_A:LAWYER2] Ah! Beh, spero tu ti sia divertito. Io stavo quasi per impazzire dalla preoccupazione. Che cos'hai scoperto? [LAW2_B:LAWYER2] Che ci sono più criminali a piede libero in città che nella prigione. Abbiamo bisogno di qualcuno di strada... [LAW2_C:LAWYER2] OK, fammici pensare, fammici pensare, fammici pensare... [LAW2_D:LAWYER2] Ah! Trovato! [LAW2_E:LAWYER2] OK, c'è questo inglese, un tizio legato al campo della musica, [LAW2_F:LAWYER2] mi sembra si chiami Kent Paul. [LAW2_G:LAWYER2] Comunque, ha ficcato il naso nelle chiappe di metà della gente di Vice City [LAW2_H:LAWYER2] per cui, se qualcuno sa dove sono questi 20 chili di coca, [LAW2_I:LAWYER2] è sicuramente lui, chiaro? È sempre al Malibu. [LAW2_J:LAWYER2] Andrò a trovarlo. [LAW2B_A:LAWYER2] Ehi, da dove sbuchi? [LAW2B_B:LAWYER2] Ho cercato per anni un fiorellino come te, bellezza... [LAW2B_C:LAWYER2] Kent Paul, tesoro. Sì, sono il capo da queste parti. [LAW2B_D:LAWYER2] Sto cercando un tipo inglese... [LAW2B_E:LAWYER2] Risolvo i problemi, capisci cosa intendo? [LAW2B_F:LAWYER2] Ti tratterò bene: qualsiasi cosa tu voglia, te la farò avere, cara. [LAW2B_G:LAWYER2] Non preoccuparti di niente, bellezza. [LAW2B_H:LAWYER2] Vattene, tesoro. [LAW2B_I:LAWYER2] Oi oi oi oi oi! [LAW2B_J:LAWYER2] Sei Kent Paul? Sono un amico di Rosenberg... [LAW2B_K:LAWYER2] Rosenberg... Rosenberg... Oh, l'avvocato matto! [LAW2B_L:LAWYER2] Quel tipo potrebbe difendere un innocente fino alla sedia elettrica! [LAW2B_M:LAWYER2] Dacci un altro drink, amico. [LAW2B_N:LAWYER2] Un altro comico. [LAW2B_O:LAWYER2] Ascoltami: mi hanno soffiato venti chili e un sacco di contanti... [LAW2B_P:LAWYER2] Droga amico? È roba da delinquenti. [LAW2B_Q:LAWYER2] Che cosa ne sai? [LAW2B_R:LAWYER2] Oi oi! Ciò che volevo dire è [LAW2B_S:LAWYER2] che c'è uno chef che spaccia sottobanco nella cucina di un hotel su Ocean Drive. [LAW2B_T:LAWYER2] Recentemente, mi è sembrato molto felice di come gli buttava... Potresti passare a trovarlo e investigare... [LAW2B_U:LAWYER2] Lo farò... ci becchiamo in giro. [LAW2B_V:LAWYER2] Beh, sì certo! Vai, vai pure idiota. Ci penserò io a tenerti a bada! [LAW2B_W:LAWYER2] Dammi un bicchiere... e dov'è finita quella sgualdrina? [LAW2C_A:LAWYER2] Oh, ottimo lavoro, sei proprio un duro. Riducilo in poltiglia, questo sicuramente lo aiuterà a diventare più loquace! [LAW2C_B:LAWYER2] Ne vuoi anche tu? [LAW2C_C:LAWYER2] Ehi, calma. Quel che va bene a te, va bene a me, amico. [LAW2C_D:LAWYER2] Ah sì? E di cosa si tratta? [LAW2C_E:LAWYER2] I verdoni... e l'amica bianca del mio povero fratello. Sfortunatamente, hai appena fatto fuori il nostro contatto. [LAW2C_F:LAWYER2] Gli incidenti capitano. Scompari. [LAW2C_G:LAWYER2] Ehi, ehi, whoa. Non c'è bisogno di recitare la parte del 'Giustiziere solitario'. [LAW2C_H:LAWYER2] Io la vedo così: siamo due hombres in una strana città. Dobbiamo guardarci a vicenda le spalle. [LAW2C_I:LAWYER2] Le mie spalle stanno benissimo, fratello... [LAW2C_J:LAWYER2] Ne sei sicuro? Ehi, prendi questa. [LAW2C_K:LAWYER2] Seguimi! [LAW2_1:LAWYER2] Che cos'hai da guardare? [LAW2_2:LAWYER2] Ti conviene iniziare a parlare... [LAW2_3:LAWYER2] Maledetto stronzo! [LAW2_4:LAWYER2] Da questa parte! [LAW2_5:LAWYER2] Vediamo cosa riesco a scoprire. Ci vediamo, Tommy. [LAW2_6:LAWYER2] ~g~Vai al Malibu e trova Kent Paul. [LAW2_7:LAWYER2] ~g~Trova lo chef in Ocean Drive. [LAW2_10:LAWYER2] ~g~Torna in macchina all'hotel. [LAW2_11:LAWYER2] ~g~Raccogli il suo cellulare. [LAW2_12:LAWYER2] Cellulare acquisito! Adesso potrai ricevere telefonate! [LAW2_13:LAWYER2] ~g~Hai lasciato Lance indietro! Vallo a prendere! [LAW2_14:LAWYER2] Andiamocene da qui! [GUN_2A:LAWYER2] Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per la ~h~mira automatica~w~ e premi il ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~! [GUN_2C:LAWYER2] Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per la ~h~mira automatica~w~ e premi il ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~! [GUN_2D:LAWYER2] Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per la ~h~mira automatica~w~ e premi il ~h~~k~~PED_FIREWEAPON~~w~ per ~h~sparare~w~! [HELP17:LAWYER2] Premi il ~h~~k~~PED_FIREWEAPON~~w~ per attaccare lo chef. [HELP18:LAWYER2] Premi il ~h~~k~~PED_FIREWEAPON~~w~ per attaccare lo chef. [LAW3_11:LAWYER2] Posizionati sul ~q~segnalino rosa~w~ per vedere le armi in offerta. [LAW3_12:LAWYER2] Puoi selezionare l'arma premendo il ~h~tasto direzionale~w~ a ~h~sinistra~w~ o a ~h~destra~w~. [LAW3_13:LAWYER2] Se hai denaro a sufficienza puoi comprare armi premendo il ~h~~k~~PED_SPRINT~~w~. [LAW3_14:LAWYER2] Puoi uscire da un negozio premendo il ~h~~k~~VEHICLE_ENTER_EXIT~~w~. [LAW3_15:LAWYER2] Segui il ~h~segnale a forma di pistola~w~ per trovare ~h~Ammu-Nation~w~. [LAW2_15:LAWYER2] ~g~Raggiungi Ammu-Nation. [LAW2_K:LAWYER2] Vedi di star calmo. [LAW2_16:LAWYER2] Devi sapere qualcosa su questa città: hai bisogno di potenza di fuoco. [LAW2_17:LAWYER2] Forza, l'armaiolo locale è solo a qualche isolato di distanza. [LAW2_18:LAWYER2] Tommy, tutti quanti hanno bisogno di un po' di relax ogni tanto. [LAW2_19:LAWYER2] Ecco il club a luci rosse Pole Position. Facci un salto ogni tanto. {=================================== MISSION TABLE LAWYER3 ===================================} [LAW3_A:LAWYER3] Aarrgh! Maledizione, sei tu! Oddio, avrò bisogno di un nuovo paio di pantaloni! [LAW3_B:LAWYER3] Ehi, quei psicopatici del nord hanno drizzato le orecchie e verranno a visitarci molto presto. [LAW3_C:LAWYER3] Dove diavolo sono i fottuti soldi? [LAW3_D:LAWYER3] Rilassati, rilassati. Non siamo ancora arrivati a questa parte. [LAW3_E:LAWYER3] Pensavo ti stessi prendendo cura di questo aspetto, davvero. [LAW3_F:LAWYER3] E adesso quei delinquenti si aspettano addirittura da noi un favore. [LAW3_G:LAWYER3] Vuoi dire che io devo far loro un favore. [LAW3_H:LAWYER3] Oh, beh, certo, proprio così. Credi davvero che potrei intimidire una giuria? [LAW3_I:LAWYER3] Non riuscirei a intimidire neppure un bambino... e credimi, ci ho provato. [LAW3_J:LAWYER3] Adesso o ci riusciamo, oppure il cugino di Forelli, Giorgio, si becca cinque anni per frode. [LAW3_K:LAWYER3] Devi CONVINCERE questi tipi! [LAW3_L:LAWYER3] Capisco. Devo aiutare la giuria a cambiare idea. Non preoccuparti... [LAW3_M:LAWYER3] No no no no NO! Ci ho provato. Le cose con la giuria non sono andate per il meglio, [LAW3_N:LAWYER3] per cui ASSICURATI che cambino davvero idea. [LAW3_1:LAWYER3] Giorgio ti manda i suoi saluti. [LAW3_2:LAWYER3] Ricorda: colpevole è una parolaccia. [LAW3_3:LAWYER3] Innocente fino a prova contraria... ecco cosa dire. [LAW3_4:LAWYER3] Lo sai che non è colpevole. [LAW3_5:LAWYER3] Ti ricordi di Giorgio? Ricordati che è innocente. [LAW3_6:LAWYER3] Non colpevole... Capito? Bene. [LAW3_8:LAWYER3] ~r~Hai ucciso un giurato! [LAW3_9:LAWYER3] ~g~Distruggi l'auto del giurato per farlo uscire! [HELP40:LAWYER3] Puoi distruggere le macchine utilizzando un martello o un'arma simile. [LAW3_10:LAWYER3] ~g~Visita il ~h~ferramenta~g~ per comprare un'arma corpo a corpo. [LAW3_20:LAWYER3] ~g~Distruggi la macchina del giurato! [LAW3_21:LAWYER3] Non posso credere che sta succedendo! [LAW3_22:LAWYER3] Incredibile! [LAW3_23:LAWYER3] OK! OK! Ho compreso il messaggio! [LAW3_24:LAWYER3] ~g~Quel martello potrebbe tornare utile. [LAW3_7:LAWYER3] ~g~Minaccia i due giurati, ma NON ucciderli! [HELP23:LAWYER3] Segui il ~h~segnale a forma di martello~w~ se vuoi comprare delle armi corpo a copro dal ferramenta. [LAW3_16:LAWYER3] Stupidi idioti del Florida. [LAW3_17:LAWYER3] Via dai piedi. {=================================== MISSION TABLE LAWYER4 ===================================} [LAW4_A:LAWYER4] Avery, è chiaro che... Tommy! Tommy! Qualche progresso? No no no, parliamone dopo, mi racconti dopo. [LAW4_B:LAWYER4] Tommy, ti presento Avery Carrington: credo tu lo abbia incontrato al party. [LAW4_C:LAWYER4] Non di persona. [LAW4_D:LAWYER4] Piacere. [LAW4_E:LAWYER4] Avery ha una proposta da farci. [LAW4_F:LAWYER4] Non ti sembra che abbiamo già altro da fare? [LAW4_G:LAWYER4] Sto cercando di tenere lontani i lupi dalla porta, per cui apprezzerei un po' di collaborazione. [LAW4_H:LAWYER4] Sono tirato come un filo e, anche se dovessi morire alla fine della settimana, desidererei non farlo da povero! [LAW4_I:LAWYER4] Adesso calmatevi, tutti e due. [LAW4_J:LAWYER4] Figliolo, dammi una mano e vedrò di mettere a nanna sottoterra qualsiasi stronzo vi stia dando problemi. [LAW4_K:LAWYER4] OK, cosa posso fare? [LAW4_L:LAWYER4] Questa società di consegne ha un deposito su un terreno di prima qualità. Non vogliono vendere. [LAW4_M:LAWYER4] Si sono rintanati come dei ratti di prateria, per cui vorrei andassi a trovarli e li facessi uscire con un po' di fumo. [LAW4_N:LAWYER4] Raggiungi il posto e fai scoppiare un vespaio: [LAW4_O:LAWYER4] mentre la sorveglianza sarà impegnata, entra dentro e mettili fuori gioco. [LAW4_P:LAWYER4] Passa pure da Rafael's per un cambio d'abito: ci potresti mettere un po', ma va bene lo stesso. [LAW4_Q:LAWYER4] Potrebbe funzionare una sommossa. [LAW4_R:LAWYER4] Se le cose vanno come devono, passa a trovarmi nel mio ufficio... [LAW4_1:LAWYER4] Disperdetevi! L'amministrazione discuterà qualsiasi rimostranza in modo appropriato! [LAW4_2:LAWYER4] Disperdetevi! Tornate a casa! [LAW4_3:LAWYER4] Disperdetevi! Questo comportamento è inaccettabile! [LAW4_4:LAWYER4] Disperdetevi! Rischiate di finire tutti per strada! [LAW4_5:LAWYER4] Forza, ragazzi! Spacchiamo il cranio a qualche rosso! [LAW4_13:LAWYER4] ~g~Comincia a combattere con almeno 4 lavoratori per avviare la sommossa. [LAW4_14:LAWYER4] ~g~Distruggi i van nell'autorimessa! [HELP38:LAWYER4] Se uccidi qualcuno con un'arma, la lascerà cadere. [HELP39:LAWYER4] Puoi far esplodere i barili di esplosivo, ma tieni la distanza! {=================================== MISSION TABLE MIAMI_1 ===================================} [T4X4_1A:MIAMI_1] ~g~Hai ~1~ secondi per attraversare ~y~24~g~ punti di controllo. ~g~Puoi attraversarli in ~y~QUALSIASI ORDINE~g~. [T4X4_1B:MIAMI_1] ~y~ATTRAVERSA~g~ il primo punto di controllo per attivare il ~r~TIMER~g~. [T4X4_1C:MIAMI_1] ~1~ su 24! [GETBIK1:MIAMI_1] Hai ~1~ secondi per salire su una PCJ 600! [GETBIK3:MIAMI_1] ~r~Hai bisogno di una PCJ 600 per affrontare questa missione! {=================================== MISSION TABLE MM ===================================} [BLOD_04:MM] CONDIZIONI MACCHINA: [BLOD_05:MM] ~g~TEMPO BERSAGLIO: ~1~ minuto [BLOD_06:MM] ~g~TEMPO BERSAGLIO: ~1~ minuti [BLOD_07:MM] NUOVO miglior tempo: ~1~ secondi [BLOD_08:MM] Veicoli distrutti: ~1~ [BLOD_09:MM] ~1~$ [BLOD_10:MM] VINCITORE!!! [BLOD_01:MM] Guida attraverso i punti di controllo per aumentare il tuo tempo globale. [BLOD_02:MM] Fallirai se il tempo globale scadrà a zero. [BLOD_03:MM] Porta il tuo tempo globale sopra a quello bersaglio per vincere! {=================================== MISSION TABLE OVALRIG ===================================} [HOTR_01:OVALRIG] ~g~La gara dura 12 giri: solo il primo, il secondo e il terzo si qualificheranno come vincitori. [HOTR_02:OVALRIG] ~g~Se il tuo mezzo viene distrutto, sarai squalificato. [HOTR_03:OVALRIG] ~g~Se danneggi il mezzo, potrai ripararlo ai box. [HOTR_04:OVALRIG] ~g~Da qui potrai uscire dallo stadio. [HOTR_05:OVALRIG] Condizioni macchina: [HOTR_06:OVALRIG] Giri: [HOTR_10:OVALRIG] Tempo di gara: [HOTR_09:OVALRIG] Posizione: [HOTR_12:OVALRIG] ~r~La tua macchina è stata distrutta! [HOTR_13:OVALRIG] ~r~Non hai vinto la gara! [HOTR_14:OVALRIG] ~r~Sei stato squalificato! [HOTR_15:OVALRIG] Tempo: ~1~:~1~ [HOTR_16:OVALRIG] Tempo: ~1~:0~1~ [HOTR_17:OVALRIG] Tempo migliore: ~1~:~1~ [HOTR_18:OVALRIG] Tempo migliore: ~1~:0~1~ [HOTR_19:OVALRIG] Tempo migliore: NA [HOTR_20:OVALRIG] Nuovo tempo migliore: ~1~:~1~ [HOTR_21:OVALRIG] Nuovo tempo migliore: ~1~:0~1~ [HOTR_22:OVALRIG] Miglior risultato: NA [HOTR_23:OVALRIG] Miglior risultato: PRIMO [HOTR_24:OVALRIG] Miglior risultato: SECONDO [HOTR_25:OVALRIG] Miglior risultato: TERZO [HOTR_26:OVALRIG] Miglior risultato: ~1~ [HOTR_27:OVALRIG] Miglior giro: ~1~.~1~ secondi [HOTR_28:OVALRIG] Miglior giro: ~1~.0~1~ secondi [HOTR_29:OVALRIG] ~1~$ [HOTR_30:OVALRIG] PRIMA POSIZIONE [HOTR_31:OVALRIG] SECONDA POSIZIONE [HOTR_32:OVALRIG] TERZA POSIZIONE [HOTR_33:OVALRIG] Miglior giro: NA [HOTR_11:OVALRIG] Nuovo giro migliore: ~1~.~1~ secondi [HOTR_34:OVALRIG] Nuovo giro migliore: ~1~.0~1~ secondi {=================================== MISSION TABLE PHIL1 ===================================} [PHIL1_A:PHIL1] Phil? [PHIL1_B:PHIL1] CORRI! [PHIL1_C:PHIL1] Corri! [PHIL1_E:PHIL1] Merda Phil, tu bevi quella schifezza? [PHIL1_F:PHIL1] Accidenti, non la devi mica bere... [PHIL1_G:PHIL1] basta una sniffata per sballare. [PHIL1_H:PHIL1] Ascoltami Phil, mi hai detto che potevi fornirmi delle armi... [PHIL1_I:PHIL1] Stanne certo! [PHIL1_J:PHIL1] C'è un trafficante d'armi Messicano con cui ho fatto un po' di affari. [PHIL1_K:PHIL1] Fa la sua consegna settimanale più o meno adesso. [PHIL1_L:PHIL1] Sperona il suo camion e fai cadere le armi prima che scappi. [PHIL1_M:PHIL1] E già che ci sei, fammi un favore: [PHIL1_N:PHIL1] fallo fuori. [PHI1_01:PHIL1] ~g~Fai cadere le armi dal camion del trafficante. [PHI1_02:PHIL1] ~g~Il trafficante ha lasciato cadere il carico. Distruggi le casse e recupera le armi. [PHI1_03:PHIL1] ~g~Sembra abbiano chiesto rinforzi. [PHI1_04:PHIL1] ~g~Adesso finisci i trafficanti ancora vivi. [PHI1_HP:PHIL1] Quando utilizzi una granata a tempo, lanciala e poi falla esplodere in un secondo momento. [PHIL1_O:PHIL1] Hoooooweeeeee! [PHIL1_D:PHIL1] Nona avvicinare mai una fiamma alla broda di Phil Cassidy! {=================================== MISSION TABLE PHIL2 ===================================} [PHIL2_A:PHIL2] Ehi Phil, come butta? [PHIL2_B:PHIL2] Eeeeehi Tommy, Come va? E passcato moolto tempo... [PHIL2_C:PHIL2] Devi davvero smetterla con quella broda, [PHIL2_D:PHIL2] accidenti, puzza di solvente. Mi fa bruciare gli occhi... [PHIL2_E:PHIL2] Shshs shhh silenzio Tommy, [PHIL2_F:PHIL2] e vieni qua che ho qualcoscia da farti vedere... qualcoscia... [PHIL2_G:PHIL2] Woof! Oddio! Riuscirei a sniffarlo da qui in fondo! Mi gira già la testa! [PHIL2_H:PHIL2] Non preoccuparti per l'odore, Tommy, guarda quescto... [PHIL2_I:PHIL2] La fottuta batteria o qualcosc'altro... Scen'è un altra sulla mensola... [PHIL2_J:PHIL2] TA-DAAA! [PHIL2_K:PHIL2] Ommerda! [PHI2_01:PHIL2] ~g~Forza, porta Phil all'ospedale. [PHI2_03:PHIL2] ~r~Phil Cassidy è morto!!! Adesso chi ti rifornirà di armi? [PHI2_05:PHIL2] Non all'ospedale, amico! Troppi poliziotti e Viet Cong! [PHI2_06:PHIL2] C'è un vecchio ex-medico militare che mi deve qualche favore e una falciatrice. [PHI2_07:PHIL2] Si trova lungo Little Havana, oooh guarda, un pesce gigante. [PHI2_08:PHIL2] Attento! Nemico tra gli alberi! [PHI2_09:PHIL2] Mi sbaglio o le strade sono di gelatina? [PHI2_10:PHIL2] Cucchiaio Storto a Mamma Gallina, mi ricevi? [PHI2_11:PHIL2] Cucchiaione-ione Woo Woo Woooo! [PHI2_12:PHIL2] Sta venendo per me, amico! [PHI2_13:PHIL2] Piume nere che svolazzano dappertutto... [PHI2_14:PHIL2] È così bello, amico... così bello... ma così freddo... [PHI2_15:PHIL2] 10-4, abbiamo un guidatore ubriaco. [PHI2_04:PHIL2] SALUTE DI PHIL: [PHI_AS1:PHIL2] PHILS PLACE COMPLETATO [PHI_AS2:PHIL2] ~g~Nuove armi disponibili presso Phils Place. {=================================== MISSION TABLE PIZZA ===================================} [PIZ1_01:PIZZA] ~g~Vai a consegnare queste pizze: devi lanciarle ai clienti. Fai un giro e tira le pizze. [PIZ1_02:PIZZA] ~g~Hai lanciato tutte le pizze, torna indietro e prendine altre. [PIZ1_05:PIZZA] ~g~Hai cinque minuti per consegnare le pizze prima che i clienti chiamino un altro negozio di consegne. [PIZ1_07:PIZZA] ~r~Hai ucciso un cliente! Sei licenziato! [PIZ1_08:PIZZA] ~r~Tempo scaduto. Sei licenziato! [PIZ1_09:PIZZA] ~r~Hai distrutto il nostro mezzo! Sei licenziato! [PIZ1_11:PIZZA] Ehi! Rimettiti in sella! [PIZ1_12:PIZZA] Pizze mancanti: [PIZ1_06:PIZZA] Premi il ~h~~k~~TOGGLE_SUBMISSIONS~~w~ mentre sei in moto per interrompere la missione. [PIZ1_13:PIZZA] Consegnale belle calde e fragranti. [PIZ1_14:PIZZA] Amico, eccoti le pizze. [PIZ1_15:PIZZA] Ehi, amico, consegnale in fretta. [PIZ1_16:PIZZA] Che cosa stai aspettando, amico? Hai delle pizze da consegnare! [PIZ1_17:PIZZA] So che non volevi fare il fattorino delle pizze, ma non me ne frega niente. [PIZ1_18:PIZZA] Consegna queste. [PIZ1_19:PIZZA] Devi consegnare queste. [PIZ1_20:PIZZA] Forza, ragazzo, consegna queste o sei licenziato. [PIZ1_21:PIZZA] C'è gente in attesa, amico. [PIZ1_22:PIZZA] Che cosa stai aspettando? Devono essere consegnate! [PIZ1_23:PIZZA] Consegna queste maledette pizze. [PIZ1_24:PIZZA] Forza, consegnale calde. [PIZ1_25:PIZZA] Ehi, ti occuperesti di queste? [PIZ1_26:PIZZA] Ehi, consegna subito queste: forza amico. [PIZ1_27:PIZZA] Forza, siamo di fretta: consegnale subito. [PIZ1_28:PIZZA] Ancora tu? Bene, consegnale in fretta, amico. [PIZ1_29:PIZZA] Non c'è tempo da perdere, consegnale tutte. [PIZ1_30:PIZZA] Muoviti, brutto pelandrone, consegna questa roba in tempo. [PIZ1_31:PIZZA] Non otterrai una promozione se non ti muoverai più in fretta. [PIZ1_32:PIZZA] ~r~La pizza è troppo calda per te? [PIZ1_33:PIZZA] ~g~Torna alla pizzeria per altre ordinazioni. [PIZ1_34:PIZZA] ~g~Pizze consegnate, ecco i soldi. [PIZ_WON:PIZZA] Missione Pizza completata. Valore massimo dell'salute aumentato a 150. {=================================== MISSION TABLE PORN1 ===================================} [POR1_A:PORN1] Azione! [POR1_B:PORN1] Whoa! Questo sì che è un bel... [POR1_C:PORN1] 30 centimetri, è questa la norma, baby. [POR1_D:PORN1] TAGLIA! Chi è quell'idiota? Tu! TU! Che cosa stai facendo sul mio set? PERCHÉ? [POR1_E:PORN1] Cosa sono tute queste stronzate? [POR1_F:PORN1] Alieni? Canne da pesca? [POR1_G:PORN1] Chi ha mai visto uno squalo così grande? [POR1_H:PORN1] Tutta questa roba deve andarsene. [POR1_I:PORN1] Perché sei venuto a rompere, idiota? [POR1_J:PORN1] Eh? [POR1_K:PORN1] Per la passera, ecco perché! Che cos'è questo? [POR1_L:PORN1] Questa è la mia arte. SORVEGLIANZA! [POR1_M:PORN1] Ascolta, lurido fighettino, ti ho comprato. Ho comprato tutto questo. [POR1_N:PORN1] E ho intenzione di cambiare le cose da queste parti... [POR1_O:PORN1] Ti farò ricco. [POR1_P:PORN1] Uh. Tu sei... tu... tu sei Tommy Vercetti? Pensavo che tu fossi... [POR1_Q:PORN1] Esatto. [POR1_R:PORN1] Faremo numerosi cambiamenti da queste parte e inizieremo a fare soldi veri. [POR1_S:PORN1] Effettivamente, hai mai pensato che... [POR1_T:PORN1] Ma prima dobbiamo trovare delle tope che meritino. [POR1_U:PORN1] Sì. Le ragazze sono carine, ma sai... Wow! [POR1_02:PORN1] ~g~Metti fuori gioco il pappone di Candy e poi passa a prenderla. [POR1_04:PORN1] Yo, Candy. Sto cercando delle attrici talentuose: sei interessata? [POR1_05:PORN1] Certo! Ma prima dovrai parlare con il mio agente... [POR1_06:PORN1] Che DIAVOLO stai facendo? [POR1_07:PORN1] Avresti fatto meglio a stare a casa oggi! [POR1_7B:PORN1] Ma hai visto questo stronzo? [POR1_08:PORN1] Ehi Mercedes! [POR1_09:PORN1] Ehi Tommy, vuoi divertirti? [POR1_10:PORN1] Non adesso, tesoro. Sei interessata a fare dei film? [POR1_11:PORN1] Certo, sempre che possano premiare il mio talento. [POR1_13:PORN1] ~g~Porta le ragazze allo studio e presentale a Steve. [POR1_14:PORN1] Sei assunta! [POR1_15:PORN1] Ehi Tommy, vieni anche tu per un riscaldamento? [POR1_17:PORN1] Whoa, che bello squalo! [POR1_18:PORN1] ~r~Mercedes è morta! [POR1_20:PORN1] Tommy, dove stai andando? Torna qui! [POR1_21:PORN1] Dove stai andando? [POR1_22:PORN1] Tommy, quando passeremo un po' di tempo io e te da soli? [POR1_01:PORN1] ~g~Candy Suxxx sarebbe perfetta per il ruolo da protagonista! [POR1_12:PORN1] ~g~Porta Candy con te all'appuntamento con Mercedes. [POR1_16:PORN1] Magari più tardi... [POR1_24:PORN1] ~g~Torna indietro e prendi Candy. [POR1_25:PORN1] ~g~Hai lasciato Candy indietro, torna a prenderla. [POR1_23:PORN1] ~g~Candy starà lavorando in ~h~Downtown~g~. [POR1_26:PORN1] ~g~Ecco Candy, sembra sia stata ancora con il deputato Shrub. [POR1_27:PORN1] Forza, andiamo. [POR1_28:PORN1] Tommy, fai attenzione! Il mio seno siliconato non è ancora assicurato! [POR1_29:PORN1] È questo il modo di guidare? [POR1_30:PORN1] Non posso recitare dopo tutto questo! [POR1_31:PORN1] Cosa? Stai cercando di uccidermi? Credevo di essere una star! {=================================== MISSION TABLE PORN2 ===================================} [POR2_A:PORN2] Come stanno andando le riprese, Steve? [POR2_B:PORN2] Beh, Candy è un talento naturale e l'altra ragazza... è insaziabile! [POR2_C:PORN2] Si è passata metà del cast e dello staff prima ancora che iniziassi a fare i controlli delle luci. [POR2_D:PORN2] Comunque, eh, domani andiamo fuori a registrare le scene sulla barca... [POR2_E:PORN2] Scene sulla barca? Quali scene sulla barca? [POR2_F:PORN2] I pescatori sono presi dalla passione quando lo squalo gigante arriva e... [POR2_G:PORN2] Che cosa ho detto sullo squalo gigante? [POR2_H:PORN2] Ho detto: 'NIENTE SQUALO GIGANTE', tutto chiaro? [POR2_I:PORN2] Lascia semplicemente le telecamere puntate sull'azione! [POR2_J:PORN2] OK, OK. Ehi Tommy, ci devo pure provare, no? [POR2_K:PORN2] Hai fatto stampare quei volantini? [POR2_L:PORN2] Sì, ma nessuno ci farà distribuire quella roba, cioè... [POR2_M:PORN2] Lasciano semplicemente troppo, uh... poco all'immaginazione. [POR2_N:PORN2] Non preoccuparti per quello. [POR2_O:PORN2] Ho le mie idee sulla distribuzione. [POR2_P:PORN2] OK. Ehi Candy, uh, nella mia roulotte. [POR2_01:PORN2] ~g~C'è un idrovolante che è stato utilizzato nelle scene di alcuni vecchi film di Indy nel retro degli studi. [POR2_02:PORN2] ~g~Passa sopra uno dei punti di controllo per cominciare a distribuire volantini. [POR2_03:PORN2] ~g~Lancia i volantini fino alla fine del punto di controllo. [POR2_04:PORN2] ~r~CARBURANTE AL MINIMO!!! [POR2_05:PORN2] Usalo per distribuire i volantini per la città. [DILDO:PORN2] Carburante: [POR2_Q:PORN2] Accidenti. [PORN2_9:PORN2] ~g~Hai ~1~ secondi per tornare sulla tua Skimmer prima che la missione abbia termine. {=================================== MISSION TABLE PORN3 ===================================} [POR3_A:PORN3] OK, qual è il problema adesso? [POR3_B:PORN3] SSShhhh! [POR3_C:PORN3] Beh, dopo il suo incontro ravvicinato con le ninfo-invasori, [POR3_D:PORN3] il nostro eroe non riesce a pensare ad altro che a questa immensa montagna fallica... [POR3_E:PORN3] ed è qui che vogliamo girare la scena con la tinozza piena di puré, ma poi noi... [POR3_F:PORN3] Non me ne può fregar di meno, [POR3_G:PORN3] Basta che continui, continui, continui. [POR3_H:PORN3] Ehi Tommy... [POR3_I:PORN3] mi hai accennato qualcosa al telefono riguardo dei problemi legali... [POR3_J:PORN3] Ah sì! Il deputato Alex Shrub, lanciato verso la cattura di nuovi elettori, ha deciso di conquistare i puritani. [POR3_K:PORN3] Si mormora che voglia supportare delle misure per limitare, diciamo, [POR3_L:PORN3] l'aspetto più carnale della grande industria nazionale dell'intrattenimento. [POR3_M:PORN3] Ottimo. [POR3_N:PORN3] Candy! Conosci Shrub, [POR3_O:PORN3] voi ragazzi vi siete lanciati in qualcosa di perverso? [POR3_P:PORN3] Oh sì, oh sì, oh sì! Sì sì sì SÌ OOOoooh! [POR3_Q:PORN3] Ti prego, dimmi che lo hai ripreso. [POR3_R:PORN3] Faceva parte del... uh... o stava parlando con... [POR3_S:PORN3] Ehi, non saprei dire. Comunque... [POR3_T:PORN3] Ti consiglio vivamente di seguirla dopo le riprese, [POR3_U:PORN3] vedi se va dritta al suo nuovo nido d'amore. [POR3_V:PORN3] Hai una macchina fotografica? [POR3_X:PORN3] Certo, dategliene una. [POR3_02:PORN3] ~r~Hai ucciso il deputato! Adesso come farai a ricattarlo? [POR3_03:PORN3] ~r~Hai allarmato la sorveglianza del deputato: lo porteranno immediatamente via. [POR3_04:PORN3] Candy, mi chiameresti Martha? [POR3_05:PORN3] Oh Alex, cioè Martha. Tutto ciò che vuoi! [POR3_06:PORN3] Martha, qualcuno ci sta osservando... che cosa perversa. [POR3_07:PORN3] Ehi tu. Dammi quella macchina fotografica! [POR3_01:PORN3] ~g~Segui la ~h~limousine~g~ di Candy. [POR3_15:PORN3] ~r~Hai distrutto la limousine di Candy! [POR3_17:PORN3] ~g~Torna allo studio con il rullino. [POR3_19:PORN3] ~r~Hai finito il rullino! [POR3_21:PORN3] ~g~Hai perso la limousine di Candy! [POR3_22:PORN3] ~g~L'hotel WR Chariot di fronte al balcone dovrebbe essere un luogo ideale per scattare le foto. [POR3_23:PORN3] ~g~C'è un ingresso laterale che ti permetterà di entrare nell'hotel. [POR3_08:PORN3] Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ con la macchina fotografica. [POR3_09:PORN3] Tieni premuto il ~h~~k~~PED_LOCK_TARGET~~w~ per ~h~mirare~w~ con la macchina fotografica. [POR3_10:PORN3] Premi il ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per ~h~zoomare~w~ con la macchina fotografica e il ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ per ~h~allargare il campo~w~. [POR3_11:PORN3] Premi il ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ per ~h~zoomare~w~ con la macchina fotografica e il ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ per ~h~allargare il campo~w~. [POR3_12:PORN3] Premi il ~h~~k~~PED_FIREWEAPON~~w~ per scattare una foto. [POR3_13:PORN3] Premi il ~h~~k~~PED_FIREWEAPON~~w~ per scattare una foto. [POR3_14:PORN3] Premi il ~h~~k~~PED_FIREWEAPON~~w~ per scattare una foto. [POR3_20:PORN3] ~g~Se hai bisogno di un mezzo, usa lo ~h~Sparrow~g~ sul retro. [POR3_16:PORN3] ~g~Hai bisogno di tre foto di qualità di Alex Shrub con Candy. [POR3_24:PORN3] FOTO SCATTATE: {=================================== MISSION TABLE PORN4 ===================================} [POR4_A:PORN4] Mi dispiace, ma adesso proprio non riesco ad ingoiarlo. [POR4_B:PORN4] Oh EDDAI tesoro! [POR4_C:PORN4] È inalberato come un capodoglio, accidenti, [POR4_D:PORN4] come fai a non farti coinvolgere dalla parte? [POR4_E:PORN4] Ma Stevie... [POR4_F:PORN4] Come sta andando il mio regista? [POR4_G:PORN4] Oh, accidenti. Lo scontro tra l'integrità artistica e [POR4_H:PORN4] i sobbalzi e le pompanti azioni continua senza sosta. [POR4_I:PORN4] E, prima che tu me lo chieda, sì, tutti e quattro i film saranno pronti per... [POR4_J:PORN4] Dolcezza, potresti PER FAVORE tenere l'anaconda nell'inquadratura, [POR4_K:PORN4] mi costa più lei all'ora di te! [POR4_L:PORN4] Oh, scusa Steve. [POR4_M:PORN4] Pensavo, abbiamo bisogno di qualche grossa acrobazia per promuovere il lancio. [POR4_N:PORN4] Qualcosa che farà davvero impatto sulla città... ti viene in mente niente? [POR4_O:PORN4] Beh, nei vecchio tempi si facevano le feste di gala, [POR4_P:PORN4] attori, limousine, il cielo notturno illuminato dai riflettori... [POR4_Q:PORN4] Riflettori? Mi è venuta un'idea... [POR4_R:PORN4] ...sì, sì, sì. I vestiti pieni di lustrini, e le limousine, oh, le prime visioni... [POR4_S:PORN4] Oh, sì signora, certo signora, [POR4_T:PORN4] e la stampa, e gli sbarramenti di luci... [POR4_01:PORN4] ~g~Raggiungi ~y~Downtown~g~ e posiziona i riflettori sopra l'edificio. [POR4_02:PORN4] ~g~Avrai bisogno di una moto veloce per saltare da un tetto all'altro. La guardia della sorveglianza possiede una ~y~PCJ 600~g~... [POR4_03:PORN4] ~g~Devi raggiungere i tetti degli edifici. Ci dovrebbe essere un ascensore in uno degli uffici ai piani superiori... [POR4_06:PORN4] ~g~Torna all'ufficio ai piani inferiori se vuoi accedere nuovamente ai tetti. [POR4_07:PORN4] ~g~Avrai bisogno di una moto per saltare da un edificio all'altro. [POR4_08:PORN4] ~g~Lanciati attraverso la finestra per avviare il percorso. Avrai tempo fino alle 07:00 prima che diventi troppo luminoso per muoverti non visto. [POR4_09:PORN4] ~g~I bonus ti mostreranno il successivo edificio su cui saltare. [POR4_10:PORN4] ~r~È troppo tardi per arrivare in cima non visto. [POR4_11:PORN4] Ritorna alla scala se vuoi accedere nuovamente ai tetti. [POR4_05:PORN4] ~g~Queste scale portano a un ufficio ai piani inferiori. [POR_AS1:PORN4] STUDIO CINEMATOGRAFICO COMPLETATO [POR_AS2:PORN4] ~g~L'Inter Global Films d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. {=================================== MISSION TABLE PROT1 ===================================} [PRO1_B:PROT1] Non sopporto più questo aspetto. Tommy, che ne dici? E se mettessimo un bar... [PRO1_D:PROT1] Ascoltami, [PRO1_E:PROT1] è giunta l'ora di prendere in mano questa città. È tutto pronto per questo. [PRO1_F:PROT1] Dobbiamo cominciare a impadronirci del territorio [PRO1_G:PROT1] e far sapere a Vice City che ci sono nuovi giocatori in città, mi segui? [PRO1_I:PROT1] Tommy, ciò di cui hai bisogno è una facciata legale, delle proprietà. A me ha sempre fatto comodo. [PRO1_J:PROT1] Dobbiamo iniziare a mostrare i denti o tutto questo lavoro sarà stato vano. [PRO1_K:PROT1] I gruppi locali sanno che Diaz è morto e si rifiutano di pagare per la protezione! [PRO1_L:PROT1] Potremmo provare con la corruzione... [PRO1_M:PROT1] Corruzione? A fanculo la corruzione! Ti mostrerò io come si fa a spaventarli. [PRO1_01:PROT1] ~g~Assali i negozi e fuggi: i proprietari chiederanno la tua protezione. [PRO1_03:PROT1] ~r~Si trattava di un assali e fuggi, non di un assali e bevi un caffè. [PRO1_04:PROT1] Tutti i miei beni, distrutti! [PRO1_05:PROT1] Rovinato... ROVINATO! [PRO1_06:PROT1] Maledizione, devo pagare la protezione! [PRO1_07:PROT1] La mia meravigliosa vetrina! [PRO1_08:PROT1] Il mio negozio, il mio bellissimo negozio. [PRO1_09:PROT1] Vercetti, ricorda questo nome. [PRO1_10:PROT1] Adesso gestisco io questa città. IO! [BUYP1:PROT1] Non puoi comprare proprietà in certe aree della mappa. [BUYP2:PROT1] Se vedi un'icona a forma di casa verde, puoi comprare quella proprietà. [PRO1_N:PROT1] Sarò di ritorno fra cinque minuti... [PRO1_11:PROT1] ~g~Raggiungi il ~y~Mall North Point~g~ in ~y~Vice Point~g~. [PRO1_12:PROT1] ~g~Distruggi le vetrine di tutti i negozi e i proprietari saranno pronti a chiederti protezione. [PRO1_A:PROT1] Oh, dobbiamo ridecorare questo posto, deve sembrare più vecchio. [PRO1_C:PROT1] Sei il mio avvocato, Rosenberg, non il mio arredatore. Chiaro? [BUYP3:PROT1] Posizionati all'interno del simbolo e premi il tasto ~h~~k~~PED_ANSWER_PHONE~~w~ per acquistare la proprietà. [PRO1_13:PROT1] ~g~Hai cinque minuti per distruggerli tutti. {=================================== MISSION TABLE PROT2 ===================================} [PRO2_A:PROT2] Qual è il problema? [PRO2_B:PROT2] Alcuni bar si rifiutano di pagare. [PRO2_C:PROT2] Dicono di essere protetti da una gang locale di malviventi. [PRO2_D:PROT2] Ma non preoccuparti, Tommy, me ne occupo io. [PRO2_E:PROT2] E tu te ne occupi in questo modo? [PRO2_F:PROT2] Voi due, alzate il culo... [PRO2_G:PROT2] Andiamo. [PRO2_01:PROT2] ~g~Fai fuori le guardie che sorvegliano il Front Page Bar e scopri chi le ha rifornite. [PRO2_10:PROT2] ~g~Altri due sono riusciti a scappare. Rintracciali e finisci questa storia. [PRO2_11:PROT2] Entra in macchina, idiota. [PRO2_02:PROT2] La tua protezione ha bisogno di più protezione. [PRO2_03:PROT2] Maledizione, non ancora! Non ne voglio sapere! [PRO2_04:PROT2] Questi idioti operano per la DBP Security, con sede dietro all'isolato. [PRO2_05:PROT2] Vedete di risolverla per i fatti vostri. [PRO2_06:PROT2] Ci si vede più tardi. [PRO2_07:PROT2] Sì, sì, certo. [PRO2_08:PROT2] ~g~La DBP Security presto scoprirà le tue intenzioni: attacca prima che si ritiri. [PRO2_09:PROT2] ~g~Vai a parlare con il proprietario del Front Page. {=================================== MISSION TABLE PROT3 ===================================} [PRO3_A:PROT3] Cretino! Ma a cosa pensavi? [PRO3_B:PROT3] Ti rendi conto che cosa hai combinato? [PRO3_C:PROT3] Potremmo essere tutti rovinati! [PRO3_D:PROT3] Il timer deve essersi inceppato. [PRO3_E:PROT3] Quel posto era stato minato per saltare come una fabbrica di fuochi d'artificio. [PRO3_F:PROT3] Ma qualcuno ha informato i poliziotti... [PRO3_G:PROT3] Qual è il problema, ragazzi? [PRO3_H:PROT3] Mike doveva dar fuoco a un locale del Mall, [PRO3_I:PROT3] ma ha fatto casino con i fusibili e adesso la polizia sta indagando. [PRO3_J:PROT3] Dobbiamo preparare la nostra roba e andarcene da qui! [PRO3_K:PROT3] Rilassatevi, tutti e due: fatemi pensare per un attimo! [PRO3_L:PROT3] Tommy Vercetti non scappa con la coda fra le gambe. [PRO3_M:PROT3] I poliziotti passeranno al setaccio l'intero edificio, giusto? [PRO3_N:PROT3] Ma tutto ciò richiede tempo. [PRO3_O:PROT3] Dobbiamo entrare e dar fuoco noi stessi al locale. [PRO3_P:PROT3] Sì, ma... [PRO3_Q:PROT3] Solo un poliziotto potrebbe avvicinarsi a meno di un chilometro dal luogo! [PRO3_R:PROT3] Allora andiamo come poliziotti. [PRO3_S:PROT3] Recupereremo delle uniformi... e avremo bisogno di una volante. [PRO3_T:PROT3] Tutto questo grazie a te, Mike. [PRO3_U:PROT3] Mi dispiace. [PRO3_V:PROT3] Ho capito. [PRO3_W:PROT3] Quello che dobbiamo fare è attirare i poliziotti con un gestaccio, [PRO3_X:PROT3] Metterli con le spalle al muro [PRO3_Y:PROT3] e sopraffarli. [PRO3_Z:PROT3] Ottimo piano, andiamo! [PRO3_A1:PROT3] Bene. [PRO3_01:PROT3] OK Lance, attira l'attenzione dei poliziotti! [PRO3_02:PROT3] ~g~Prendi la macchina della polizia e vai a posizionare la bomba nel Tarbrush Café dentro al Mall. [PRO3_03:PROT3] ~g~Hai lasciato Lance indietro, torna a prenderlo. [PRO3_04:PROT3] ~g~Andiamo. [PRO3_05:PROT3] ~r~Hai ucciso Lance! [PRO3_07:PROT3] ~g~È saltata la vostra copertura. Muoviti a piazzare la bomba! [PRO3_09:PROT3] Legali e imbavagliali! [PRO3_10:PROT3] Oooh. Mi calza a pennello! [PRO3_11:PROT3] Un po' stretto sul pacco... [PRO3_12:PROT3] Oh sì, hai ragione, anche a me! [PRO3_13:PROT3] Tranquillo fratello: nessun poliziotto guida così male! [PRO3_14:PROT3] Ricorda: sorridi agli altri poliziotti! [PRO3_15:PROT3] Ehi agente, bel distintivo, bel distintivo. [PRO3_16:PROT3] Un po' più sciolto, Lance. [PRO3_17:PROT3] OK, i timer sono pronti, 5 secondi alla scadenza. [PRO3_18:PROT3] 5 secondi? Dobbiamo scappare di corsa da qua! [PRO3_19:PROT3] Questo deve davvero averli irritati. [PRO3_20:PROT3] ~g~Fatevi inseguire dai due poliziotti nel garage. [PRO3_21:PROT3] ~g~Ottieni un livello di sospetto così i poliziotti ti inseguiranno fino al garage. [PRO3_22:PROT3] ~g~La porta del garage è bloccata! Liberala così potrai chiuderla. [PRO3_23:PROT3] ~g~Cammina sul segnalino per piazzare la bomba. [PRO3_24:PROT3] ~g~Allontanati dal Café! [PRO_AS1:PROT3] CIRCOLO DI PROTEZIONE COMPLETATO [PRO_AS2:PROT3] ~g~La proprietà di Vercetti d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. [PRO3_08:PROT3] ~g~Torna alla ~h~proprietà di Vercetti~g~ su ~h~Starfish Island~g~. {=================================== MISSION TABLE RACES ===================================} [RACES_2:RACES] ~g~Hai bisogno di un veicolo per partecipare, non è mica una maratona! [RACES_3:RACES] 3..2..1.. VIA VIA VIA! [RACES_8:RACES] ~r~Non hai vinto la corsa! [RACES00:RACES] Corsa ~1~: [RACES01:RACES] Terminal Velocity [RACES02:RACES] Ocean Drive [RACES03:RACES] Border Run [RACES04:RACES] Capital Cruise [RACES05:RACES] Tour! [RACES06:RACES] V.C. Endurance [RACES07:RACES] Costo d'ingresso: ~1~$ [RACES08:RACES] Tempo migliore: ~1~:~1~ [RACES09:RACES] Miglior risultato: PRIMO [RACES10:RACES] Miglior risultato: SECONDO [RACES11:RACES] Miglior risultato: TERZO [RACES12:RACES] Miglior risultato: QUARTO [RACES13:RACES] Lunghezza tracciato: ~1~.~1~ km [RACES15:RACES] Miglior tempo: ND [RACES16:RACES] Miglior risultato: ND [RACES18:RACES] HAI VINTO: ~1~$ [RACES19:RACES] Non puoi permetterti di partecipare a questa gara. [RACES22:RACES] Tempo migliore: ~1~:0~1~ [RACES23:RACES] Lunghezza tracciato: ~1~.~1~ miglia [RACES_1:RACES] ~g~Recupera un veicolo veloce e raggiungi la griglia di partenza. [RACEHLP:RACES] ~w~Premi il ~h~~k~~PED_SPRINT~~w~ per avviare la corsa selezionata. ~w~Premi il ~h~~k~~VEHICLE_ENTER_EXIT~~w~ per uscire. {=================================== MISSION TABLE RCHELI1 ===================================} [WRECKED:RCHELI1] ~r~Il veicolo è a pezzi! [RCH1_4:RCHELI1] Punti di controllo rimanenti: [RCH1_6:RCHELI1] ~g~Usa l'elicottero radiocomandato per attraversare i punti di controllo nella zona dell'aeroporto. [RCH1_7:RCHELI1] ~g~In totale ci sono 20 punti di controllo. [RCH1_12:RCHELI1] ~r~L'elicottero radiocomandato si sta allontanando dal raggio d'azione! [RCH1_13:RCHELI1] ~r~L'elicottero radiocomandato è fuori portata! [RCH1_8:RCHELI1] { reVC update } ~g~Se desideri interrompere la missione, premi il ~h~~k~~VEHICLE_FIREWEAPON~~g~ per far esplodere l'elicottero. {=================================== MISSION TABLE RCPLNE1 ===================================} [RCPL1_4:RCPLNE1] ~g~Sfida nella GARA A TAPPE altri 3 aerei radiocomandati. [RCPL1_5:RCPLNE1] ~g~Vola attraverso i punti di controllo in Vice City. [RCPL1_6:RCPLNE1] { reVC update } ~g~Se desideri interrompere la missione, premi il ~h~~k~~VEHICLE_FIREWEAPON~~g~ per far esplodere l'aereo. [RCPL1_8:RCPLNE1] ~g~L'aereo radiocomandato è quasi fuori portata! [RCPL1_9:RCPLNE1] ~r~L'aereo radiocomandato è fuori portata! {=================================== MISSION TABLE RCRACE1 ===================================} [RCR1_4:RCRACE1] Giri rimanenti: [RCR1_1:RCRACE1] ~g~Sfida in una gara a tappe altre 3 macchine radiocomandate. [RCR1_2:RCRACE1] ~g~Per vincere, completa per primo due giri del tracciato! [RCR1_6:RCRACE1] ~g~La macchina radiocomandata è quasi fuori portata! [RCR1_7:RCRACE1] ~r~La macchina radiocomandata è fuori portata! {=================================== MISSION TABLE ROCK1 ===================================} [RBM1_A:ROCK1] AllllllllRrrighttt! [RBM1_B:ROCK1] Brillante, davvero brillante! [RBM1_D:ROCK1] Ehi, hai già incontrato i Love Fist? [RBM1_E:ROCK1] No, non ancora, ma mi piace molto la vostra musica. [RBM1_F:ROCK1] Lascia che ti presenti la band. [RBM1_G:ROCK1] Questo è P, Percy, Dick. Willy è nel cesso! Jezz è invece il tipo che hai visto prima nella cabina di registrazione. [RBM1_H:ROCK1] Ragazzi, vorrei presentarvi un mio carissimo amico. [RBM1_I:ROCK1] Si chiama Tommy. Ci conosciamo da tempo. [RBM1_J:ROCK1] Ciao amico. [RBM1_K:ROCK1] E, ehm, qual era il tuo nome? [RBM1_L:ROCK1] Eddai Jezz che lo sai, [RBM1_M:ROCK1] non fare questi scherzetti con me, amico, [RBM1_N:ROCK1] sono troppo furbo per cascarci, tesoro! [RBM1_O:ROCK1] Vedi, la situazione Tom è che i ragazzi hanno bisogno di aiuto. [RBM1_P:ROCK1] Non hanno i contatti giusti da questa parte, del tipo 'come sta tuo padre'. [RBM1_Q:ROCK1] Abbiamo bisogno di droga, amico! [RBM1_R:ROCK1] Dobbiamo fare un po' di casino alla Love Fist, capito? [RBM1_S:ROCK1] Beh, questa è Vice City. Qual è il problema? [RBM1_U:ROCK1] Love Juice, sì! [RBM1_V:ROCK1] Love Juice? [RBM1_W:ROCK1] Sì, due parti di broda, una parte di polvere, cinque bombe effervescenti e un litro di petrolio. [RBM1_X:ROCK1] Ci puoi aiutare, amico? [RBM1_Y:ROCK1] Beh, significherebbe molto per i ragazzi. [RBM1_Z:ROCK1] Lo farai per loro, vero? [RBM1_7:ROCK1] ~r~Non hai portato la Love Juice in tempo! [RBM1_8:ROCK1] ~r~Mercedes è morta! [RBM1_10:ROCK1] ~r~Idiota! Hai distrutto la merce! [RBM1_13:ROCK1] ~g~Porta la 'Love Juice' e Mercedes alla band prima che salgano sul palco. [RBM1_15:ROCK1] ~r~Hai perso lo spacciatore, i soldi e la droga! [RBM1_17:ROCK1] ~g~Uccidi lo spacciatore e recupera la droga! [MOB_07A:ROCK1] Ehi amico, ai ragazzi non dispiacerebbe un po' di compagnia, se capisci cosa intendo... [MOB_07B:ROCK1] Conosco la ragazza giusta. [ROK1_5:ROCK1] Ehi Mercedes! [ROK1_6:ROCK1] Ciao Tommy, come stai? [ROK1_7:ROCK1] Non male. Senti, ti piacerebbe intrattenere i Love Fist? [ROK1_8:ROCK1] OK, ma per questo favore voglio una ricompensa... [RBM1_14:ROCK1] ~g~Hai bisogno di una macchina o di una moto! [RBM1_1:ROCK1] ~g~Vai a prendere Mercedes al suo appartamento. [RBM1_12:ROCK1] ~g~Raccogli gli ingredienti per la 'Love Juice' dallo spacciatore. [ROK1_2:ROCK1] NON NECESSARIO [ROK1_3:ROCK1] NON NECESSARIO [MERC_39:ROCK1] Ci vediamo più tardi, tesoro. [RBM1_C:ROCK1] Ehi, Tommy! Sono felice che ce tu l'abbia fatta. [ROK1_1A:ROCK1] Stai cercando qualcosa di speciale? Ho proprio ciò che ti serve! [ROK1_9:ROCK1] Grazie per i soldi, idiota! [RBM1_T:ROCK1] Abbiamo bisogno di Love Juice, hai presente? {=================================== MISSION TABLE ROCK2 ===================================} [RBM2_A:ROCK2] Tommy, amico, sono felice di vederti! [RBM2_B:ROCK2] Cosa succede? [RBM2_C:ROCK2] Brutte vibrazioni, Tommy... [RBM2_E:ROCK2] C'è questo tipo, lo conosciamo appena, ma lui la sa lunga su di noi. [RBM2_F:ROCK2] Come questo tipo. Sa tutto su di noi. [RBM2_G:ROCK2] Sa che Willy ama la biancheria intima femminile, eh! [RBM2_H:ROCK2] O che a Percy piacciono i Duran Duran! [RBM2_K:ROCK2] Sì, come nella storia dei missili, esatto. Ma ascolta, questo bastardo... [RBM2_L:ROCK2] cioè, sì, questo tipo, vuole vedere i Love Fist morti. [RBM2_M:ROCK2] Morti, Tommy. [RBM2_N:ROCK2] Love Fist morti. Lo sai come si dice, i buoni muoiono giovani. [RBM2_O:ROCK2] Ma Tommy, devi salvare i Love Fist! [RBM2_P:ROCK2] Abbiamo un concerto fra due ore e credo... [RBM2_Q:ROCK2] I ragazzi credono che il maniaco stia architettando qualcosa di losco. [RBM2_1:ROCK2] ~g~Guida la limousine al concerto e cerca di attirare allo scoperto lo psicopatico. [RBM2_2:ROCK2] ~r~Hai distrutto l'auto della band! [RBM2_3:ROCK2] ~g~Vai al concerto! [RBM2_4:ROCK2] ~g~Becca lo psicopatico! Non farlo scappare! [RBM2_5:ROCK2] ~r~Lo hai perso, idiota! [RBM2_7:ROCK2] ~r~I fan sono stati attaccati: lo psicopatico non si farà vivo! [RBM2_8:ROCK2] ~r~Le guardie della sorveglianza sono state attaccate: lo psicopatico non si farà vivo! [PSYCH_1:ROCK2] Vedrò i Love Fist bruciare! [PSYCH_2:ROCK2] I Love Fist hanno rovinato la mia vita! [RBM2_I:ROCK2] Sta zitto, idiota. Solo perché a Jezz piacciono le pecore. [RBM2_R:ROCK2] Ma sta' zitto! [RBM2_D:ROCK2] Certo, niente scherzi, è roba forte, davvero forte sai? [RBM2_J:ROCK2] È una questione di passione, sai? {=================================== MISSION TABLE ROCK3 ===================================} [RBM3_A:ROCK3] Tommy! Tommy! Tommy amico, lo psicopatico è tornato! [RBM3_B:ROCK3] Che succede? [RBM3_C:ROCK3] Quello psicopatico non vuole lasciar stare i Love Fist! [RBM3_D:ROCK3] Non lo hai ucciso, amico. E adesso è tornato. [RBM3_E:ROCK3] Sì, sì, sì e il problema è che... [RBM3_F:ROCK3] Il problema è che abbiamo bisogno di qualcuno di cui ci fidiamo per guidare la limousine, [RBM3_G:ROCK3] poiché il pazzo continua a minacciarci! [RBM3_I:ROCK3] Stiamo letteralmente cadendo a pezzi. [RBM3_J:ROCK3] OK ragazzi, calmatevi. Me ne occuperò io. [RBM3_K:ROCK3] Generalmente non perdo tempo a portare in giro un gruppo di ubriaconi scozzesi bisessuali, [RBM3_L:ROCK3] ma, nel vostro caso, farò un'eccezione. [ROK3_03:ROCK3] Conviene fare un giro più lungo. [ROK3_04:ROCK3] Ehi Tommy, cambia la stazione. [ROK3_08:ROCK3] Mi sto annoiando di tutto questo. [ROK3_09:ROCK3] Tieni premuto il maledetto pedale! [ROK3_61:ROCK3] Dobbiamo trovare la bomba! [ROK3_28:ROCK3] Suonerò il basso all'inferno. [ROK3_30:ROCK3] Qualcuno faccia qualcosa. [ROK3_32:ROCK3] OK, osso duro, fai qualcosa tu allora. [ROK3_34:ROCK3] Willy, potresti succhiare la broda con una cannuccia. [ROK3_37:ROCK3] Passa una cannuccia a Willy! [ROK3_41:ROCK3] Quale filo, Tommy? [ROK3_42:ROCK3] Quello verde. [ROK3_43:ROCK3] Non c'è un filo verde! Questo forse è verde... [ROK3_44:ROCK3] Qualcuno di questi fili ti sembra verde? [ROK3_49:ROCK3] Ti ho tenuto con me per anni. [ROK3_51:ROCK3] Una ragazzina isterica. [ROK3_52:ROCK3] Sì. [ROK3_53:ROCK3] Taci e taglia un filo. [ROK3_54:ROCK3] Quale filo? [ROK3_55:ROCK3] Questo qua... [ROK3_56:ROCK3] NO! [ROK3_57:ROCK3] Oddio, siamo vivi. Non siamo esplosi, amico. Tommy, bel lavoro. Rock and roll, ragazzi. [ROK3_58:ROCK3] Non abbiamo uno spettacolo a cui andare? Del casino da fare? Fans di cui abusare? [ROK3_59:ROCK3] LOVE FIST! [ROK3_60:ROCK3] Hai finito con quella bottiglia? [RBM3_4:ROCK3] ~r~Hai ucciso i Love Fist! [RBM3_6:ROCK3] DETONAZIONE: [RBM3_1:ROCK3] ~g~Trasporta i Love Fist al concerto. [RBM3_2:ROCK3] Mentre la bomba è innescata, esploderà se cercherai di lasciare la macchina... [RBM3_3:ROCK3] Se la barra di detonazione si riempie completamente, la bomba esploderà. [RBM3_8:ROCK3] Più velocemente guidi, più lentamente si riempirà. [RBM3_7:ROCK3] ~g~BOMBA DISINNESCATA! [ROK3_6A:ROCK3] ~g~Love Fist. Avete finito di inquinare l'etere. [ROK3_6B:ROCK3] ~g~Vi ho dato la possibilità di essere amici. Adesso vi darò la possibilità di morire. [ROK3_62:ROCK3] Per cui abbiamo pensato di mostrarti il nostro Tempio del Rock. [ROK3_63:ROCK3] Per percepire il furore alla Love Fist! [ROK3_64:ROCK3] Ascolta come parli, amico. Si tratta di carta pesta e adesivo. [ROK3_65:ROCK3] Ehi, ragazzi, questo è il tempio e noi siamo i sacerdoti. [ROK3_66:ROCK3] Beh, se i ragazzi amano i loro sacerdoti a pezzi e mezzi sordi, [ROK3_67:ROCK3] chi sono per discutere? [ROK3_68:ROCK3] Oddio, si è rovinato nuovamente il nastro. [ROK3_69:ROCK3] Se andiamo avanti così, dovremo suonare dal vivo. [ROK3_70:ROCK3] Oooh merda! La pancia... [ROK3_74:ROCK3] Ah guarda: che cos'è? Ehi Tommy, metti su questo nastro. [ROK3_01:ROCK3] Finalmente, amico: è l'ora di un bel drink. [ROK3_02:ROCK3] Il concerto è a cento metri lungo la strada. [ROK3_05:ROCK3] Mi rimbambisco se non agito la testa. [ROK3_07:ROCK3] Tommy, amico, devi salvare la band! [ROK3_29:ROCK3] Tommy, continua a guidare senza rallentare! [ROK3_31:ROCK3] Bella: 'Qualcuno faccia qualcosa'. Ma che idiozia è questa? Ho conosciuto froci più coraggiosi. [ROK3_33:ROCK3] Ascolta, sono un musicista. Non ho la minima idea di come disarmare una bomba. [ROK3_35:ROCK3] Sì, mi è giunta voce che sei bravo in queste cose. [ROK3_38:ROCK3] Una cannuccia? Questo è il furgone da tour dei Love Fist! [ROK3_39:ROCK3] Dove diavolo la trovo una cannuccia, amico? [ROK3_46:ROCK3] Avrei dovuto scaricarti quando ne ho avuto la possibilità. [ROK3_47:ROCK3] Capitalista. [ROK3_48:ROCK3] Schiavo dalla gloria. [ROK3_73:ROCK3] Jezz sta riproducendo il nastro. [RBM3_9:ROCK3] Se ti fermi o guidi lentamente, la barra di detonazione aumenterà. [ROK3_50:ROCK3] Sta zitto. Sei un idiota. [ROK3_36:ROCK3] Ehi, ero completamente fuori di testa quella notte, e voi lo sapete! [RBM3_H:ROCK3] Mi sto cagando addosso, amico. Voglio la mamma! [ROK3_45:ROCK3] La fine imminente mi fa vedere tutto verde. [ROK3_6C:ROCK3] ~g~Rallenta e la limousine esploderà, insieme ai vostri GROSSI CULI PELOSI! [ROK3_71:ROCK3] Dobbiamo andare avanti con quello che abbiamo. Grazie ancora Tommy. Cioè, sai cosa intendo, ciao! [ROK3_1:ROCK3] Alla fine è giunta l'ora di un meritato bicchierino. Lo spettacolo si tiene a circa cento metri lungo la strada. [ROK3_2:ROCK3] Conviene fare un giro più lungo. Ehi Tommy, cambia la stazione. [ROK3_3:ROCK3] Sì Tommy, mi rimbambisco se non agito la testa. Ehi guarda, cos'è questo? Ehi Tommy, metti su questa cassetta. [ROK3_4:ROCK3] Love Fist. Avete finito di inquinare l'etere. Vi ho dato la possibilità di essere amici. [ROK3_5:ROCK3] Adesso vi darò la possibilità di morire. Se rallentate, la vostra limousine esploderà, insieme ai vostri GROSSI CULI PELOSI! [ROK3_6:ROCK3] Tommy, devi salvare la band! Mi sto annoiando di tutto questo. Tieni premuto il maledetto pedale! [ROK3_7:ROCK3] Dobbiamo trovare la bomba! Perché? Non possiamo guidare tutto il giorno? È vero, c'è un sacco di roba da bere... [ROK3_8:ROCK3] La bomba non sarà nel motore? Dovremmo fermarci per prenderla! Moriremo tutti quanti! Credo che mi ubriacherò! [ROK3_9:ROCK3] Ehi, c'è una coda da rispettare, amico! La risposta non è nel minibar! Spostati! [ROK3_10:ROCK3] Ehi, dalla bottiglia di vodka escono un sacco di fili! Non è vodka, quella è BRODA! [ROK3_11:ROCK3] WAAAAAAGGGHHHH!!! Ed è predisposta per esplodere! WAAAAAAAAAAAAGGGHHHHHHHH!!! [ROK3_12:ROCK3] Lo dicevano che l'alcol mi avrebbe fatto fuori... L'ho visto in televisione, devi tagliare uno dei fili. Quale? Non lo so, amico. [ROK3_13:ROCK3] Non ne ho idea. Willy, dimmi qualcosa. Suonerò il basso all'inferno. [ROK3_14:ROCK3] Tommy, continua a guidare senza rallentare! Qualcuno faccia qualcosa. Bella! [ROK3_15:ROCK3] 'Qualcuno faccia qualcosa'. Ma che idiozia è questa? Ho conosciuto froci più coraggiosi. OK, osso duro, fai qualcosa tu allora. [ROK3_16:ROCK3] Ascolta, io sono un musicista. Non ho la minima idea di come disarmare una bomba. Willy, potresti succhiare la broda con una cannuccia. [ROK3_17:ROCK3] Sì, mi è giunta voce che sei bravo in queste cose. Ehi, ero completamente fuori di testa quella notte, e voi lo sapete! [ROK3_18:ROCK3] Passa una cannuccia a Willy! Una cannuccia? Questo è il furgone da tour di Love Fist! [ROK3_19:ROCK3] Dove diavolo la trovo una cannuccia, non so se mi spiego? Quale filo, Tommy? Quello verde. Non c'è un filo verde! [ROK3_20:ROCK3] Questo forse è verde... Qualcuno di questi fili ti sembra verde? [ROK3_21:ROCK3] Ih no! La fine imminente mi fa vedere tutto verde! Avrei dovuto scaricarti quando ne ho avuto la possibilità. [ROK3_22:ROCK3] Accecato dalla gloria. Capitalista. Ti ho tenuto con me per anni. Sta zitto. Sei un idiota. [ROK3_23:ROCK3] Una ragazzina isterica. Sì. Taci e taglia un filo. Quale filo? Questo qua... [ROK3_24:ROCK3] NO! Oddio, siamo vivi. Non siamo esplosi, amico. [ROK3_25:ROCK3] Tommy, bel lavoro. Rock and roll, ragazzi. Non abbiamo uno spettacolo a cui andare? [ROK3_26:ROCK3] Del casino da fare? Fans di cui abusare? LOVE FIST! [ROK3_27:ROCK3] Hai finito con quella bottiglia? {=================================== MISSION TABLE SERG1 ===================================} [TEX1_A:SERG1] Vieni a metterti qui vicino a me, figliolo. [TEX1_B:SERG1] Diamine, mio padre diceva sempre 'A caval donato non si guarda in bocca' ed effettivamente non lo ha mai fatto. [TEX1_C:SERG1] Vuoi un goccio di sano Kentucky? [TEX1_D:SERG1] No, grazie. [TEX1_E:SERG1] Sei uno dalla testa lucida, mi piace. [TEX1_F:SERG1] Ora, il business dei terreni non ha niente a che fare con quello che dicono i giornali. [TEX1_G:SERG1] Si tratta solo di polvere! E la volontà di possederla! Mi segui, figliolo? [TEX1_H:SERG1] Sì, certo. [TEX1_J:SERG1] Tu mi sembri la persona giusta per convincerlo. [TEX1_K:SERG1] La persuasione è il mio forte. [TEX1_L:SERG1] Bene. Lo troverai al circolo sportivo, presso il campo da golf. [TEX1_M:SERG1] Non permettono di entrare con le armi, per cui le sue guardie del corpo non saranno armate. [TEX1_N:SERG1] Vai e riempilo di santissime mazzate. [TEX1_O:SERG1] Prendi, ti ho iscritto al club, inoltre, credo proprio avrai bisogno di abiti più adeguati. [TEX1_2:SERG1] ~g~Raggiungi il club di golf Leaf Links. [TEX1_0:SERG1] ~g~Il bersaglio si sta allenando a golf: assicurati che sia la sua ultima partita. [TEX1_3:SERG1] Chi è questo tipo? Ragazzi, occupatevene voi. [TEX1_6:SERG1] Bel culo bimba! [TEX1_7:SERG1] Ma sono io? [TEX1_I:SERG1] Bene, c'è un tenace bastardo che non si decide a vendere. [TEX1_8:SERG1] Ogni volta che entri su un cart da golf, ottieni automaticamente una mazza da golf, sempre se lo spazio per l'arma corpo a corpo è disponibile. [TEX1_9:SERG1] Prendilo! [TEX1_10:SERG1] Uccidi lo psicopatico! [TEX1_1:SERG1] ~g~Recupera degli abiti da golf presso Jocksport's. {=================================== MISSION TABLE SERG2 ===================================} [TEX_2A:SERG2] ~g~Eccellente! Ti hanno visto! [TEX_2B:SERG2] ~r~Pazzo! La gente deve essere TESTIMONE di un attacco da parte di un Cubano! [TEX_2C:SERG2] ~g~Vai a recuperare dei vestiti con i colori della gang dei Cubani da Rafael's! [TEX_2D:SERG2] ~g~Adesso metti fuori gioco il capo della gang degli Haitiani presso l'impresa di pompe funebri Romero's. [TEX2_A:SERG2] Tommy, questo è Donald Love. Donald, questo è Tommy Vercetti, [TEX2_B:SERG2] l'ultimo cowboy arrivato da queste parti. [TEX2_C:SERG2] Yeh... uh... [TEX2_D:SERG2] Donald, sta zitto e ascoltami, potresti imparare qualcosa. [TEX2_E:SERG2] Adesso, niente fa calare i prezzi dei terreni come una bella guerra tra gang rivali. [TEX2_F:SERG2] Eccetto, forse, un disastro, come una piaga biblica o qualcosa del genere. [TEX2_G:SERG2] Ma per il caso in questione mi sembra un po' troppo. [TEX2_H:SERG2] Capisci, brutto idiota quattr'occhi? [TEX2_I:SERG2] Recentemente è morto un capo gang degli Haitiani e, a quanto sembra, sono stati i Cubani. [TEX2_J:SERG2] Togliamo loro qualsiasi dubbio, allora! Vestiti da ombre Cubano [TEX2_K:SERG2] e vai a disturbare il funerale. Fai un bel casino e dattela a gambe. [TEX2_L:SERG2] Hai capito, Donald? [TEX2_M:SERG2] Questo dovrebbe mettere il coyote nel pollaio, vero? [TEX2_N:SERG2] Dopodiché dovremo solo attendere e veder crollare i prezzi. [TEXEXIT:SERG2] ~g~Vattene da Little Haiti! {=================================== MISSION TABLE SERG3 ===================================} [TEX3_A:SERG3] Guarda qua, figliolo. Ho un problema e sono certo tu possa darmi una mano. [TEX3_B:SERG3] Non sono un geometra. [TEX3_C:SERG3] No, mi riferivo principalmente alle tue doti di demolitore. [TEX3_D:SERG3] Ecco la faccenda: questo è il progetto approvato, e questo, [TEX3_E:SERG3] questo è il terreno che stiamo seguendo. [TEX3_F:SERG3] Stai cercando di dirmi che questo nuovo isolato di uffici è nel posto sbagliato. [TEX3_G:SERG3] Capisci al volo. [TEX3_H:SERG3] Adesso devo allontanarmi per un po' dalla città... [TEX3_I:SERG3] ...e se la costruzione di quegli uffici dovesse subire un improvviso e insormontabile problema strutturale, beh, allora... [TEX3_J:SERG3] come ogni civile e ragionevole individuo, ti sentiresti obbligato a intervenire [TEX3_K:SERG3] per favorire il ringiovanimento di un'area così importante della città? [TEX3_L:SERG3] Dove posso ordinare altra gente come te? [TEX3_1:SERG3] ~g~Utilizza l'elicottero radiocomandato per trasportare le bombe nelle quattro aree bersaglio dell'edificio. [TEX3_2:SERG3] ~g~Devi posizionare una bomba su ogni bersaglio. Puoi piazzare l'esplosivo in qualsiasi ordine. [TEX3_5:SERG3] ~g~Se fai cadere una bomba senza successo, potrai raccoglierla nuovamente e riprovare. [TEX3_7:SERG3] ~g~Dovrai piazzare tutti gli esplosivi rimanenti in 7 minuti! [TEX3_8:SERG3] ~g~Hai mancato il bersaglio! Raccogli la bomba e riprova! [TEX3_10:SERG3] ~g~Fai cadere la bomba sul bersaglio. [TEX3_11:SERG3] Bersagli rimanenti: [TEX3_17:SERG3] ~r~Il tempo è scaduto: non sei riuscito a demolire l'edificio! [TEX3_18:SERG3] ~r~L'elicottero radiocomandato è stato distrutto! Come farai adesso a trasportare le bombe? [TEX3_19:SERG3] ~r~Hai fatto cadere la bomba in acqua! Hai bisogno di tutte le 4 bombe per demolire il cantiere! [TEX3_20:SERG3] ~g~L'elicottero radiocomandato è quasi fuori portata. Torna indietro al cantiere e completa il lavoro! [TEX3_21:SERG3] ~r~L'elicottero radiocomandato è fuori portata! [TEX3_24:SERG3] Premi il ~h~~k~~VEHICLE_LOOKLEFT~~w~ per ruotare l'elicottero in senso antiorario. [TEX3_25:SERG3] Premi il ~h~~k~~VEHICLE_LOOKLEFT~~w~ per ruotare l'elicottero in senso orario. [TEX3_27:SERG3] ~g~Le scale centrali ti permetteranno di accedere a tutti i piani dell'edificio. [TEX3_31:SERG3] ~r~Hai distrutto il van contenente le bombe e l'elicottero radiocomandato! [TEX3_32:SERG3] Puoi ~h~guardare indietro~w~ premendo ~h~simultaneamente ~k~~VEHICLE_LOOKLEFT~ e ~k~~VEHICLE_LOOKRIGHT~~w~. [TEX3_4:SERG3] { reVC update } ~g~Per sganciare una bomba, premi il ~h~~k~~VEHICLE_FIREWEAPON~~g~. [TEX3_29:SERG3] { reVC update } Per sganciare una bomba, premi il ~h~~k~~VEHICLE_FIREWEAPON~~w~. [TEX3_26:SERG3] Premi il ~h~~k~~VEHICLE_BRAKE~~w~ per ridurre la velocità del rotore dell'elicottero in modo da farlo abbassare. [TEX3_22:SERG3] Premi il ~h~~k~~VEHICLE_ACCELERATE~~w~ per aumentare la velocità del rotore dell'elicottero in modo da ~h~farlo alzare. [TEX3_16:SERG3] ~g~Raggiungi il van ~w~TOPFUN~g~ presso il cantiere edile da demolire. [TEX3_33:SERG3] Una volta raccolta una bomba, il radar mostrerà la pozione del bersaglio rispetto all'elicottero radiocomandato. [TEX3_34:SERG3] Un ~h~triangolo verso l'alto~w~ indica che il bersaglio si trova ~h~più in alto~w~ dell'elicottero. [TEX3_35:SERG3] Un ~h~triangolo verso il basso~w~ indica che il bersaglio si trova ~h~più in basso~w~ dell'elicottero. [TEX3_36:SERG3] Un ~h~quadrato~w~ indica che il bersaglio si trova alla ~h~stessa altezza~w~ dell'elicottero. [TEX3_6:SERG3] ~g~Una volta raccolta la prima bomba, comincerà il conto alla rovescia. [TEX3_28:SERG3] Per ~h~raccogliere una bomba~w~, avvicinaci l'elicottero radiocomandato. L'elicottero può trasportare una sola bomba alla volta. [TEX3_30:SERG3] Per raccogliere una bomba, avvicinaci l'elicottero radiocomandato. L'elicottero può trasportare una sola bomba alla volta. [TEX3_12:SERG3] ~g~Bomba posizionata! Ancora 3 bersagli! Torna indietro e recupera un'altra bomba. [TEX3_13:SERG3] ~g~Bomba posizionata! Ancora 2 bersagli! Torna indietro e recupera un'altra bomba. [TEX3_14:SERG3] ~g~Bomba posizionata! Ancora 1 bersaglio! Torna indietro e recupera un'altra bomba. [TEX3_15:SERG3] ~r~Conto alla rovescia avviato! ~g~Devi posizionare le ~w~4 bombe~g~ in tempo! [TEX3_37:SERG3] Sposta ~h~in basso la levetta analogica destra~w~ per aumentare la velocità del rotore e ~h~far salire~w~ l'elicottero. [TEX3_38:SERG3] { reVC update } {Sposta ~h~~k~~VEHICLE_ACCELERATE~~w~ per ridurre la velocità del rotore e ~h~far scendere~w~ l'elicottero.} Sposta ~h~in alto la levetta analogica destra~w~ per ridurre la velocità del rotore e ~h~far scendere~w~ l'elicottero. [TEX3_39:SERG3] ~g~Per sganciare una bomba, premi il tasto ~h~~k~~VEHICLE_HANDBRAKE~~g~. [TEX3_40:SERG3] Per sganciare una bomba, premi il tasto ~h~~k~~VEHICLE_HANDBRAKE~~w~. [TEX3_23:SERG3] Premi i tasti ~h~~k~~VEHICLE_TURRETUP~~w~ e ~h~~k~~VEHICLE_TURRETDOWN~~w~ per inclinare l'elicottero nella direzione desiderata. {=================================== MISSION TABLE TAXI1 ===================================} [FARES:TAXI1] CLIENTI: [TAXI1:TAXI1] ~g~Trova un passeggero. [TSCORE2:TAXI1] ~1~$ [IN_ROW:TAXI1] Bonus ~1~ DI SEGUITO! ~1~$ [TAXI3:TAXI1] ~r~Il passeggero è fuggito terrorizzato! [TAXI7:TAXI1] ~r~La tua auto è distrutta, falla riparare. [TAXI4:TAXI1] Corsa completata! [TAXI5:TAXI1] BONUS VELOCITÀ! [TAXI6:TAXI1] Missione taxi terminata [TAXIH1:TAXI1] Fermati vicino a un pedone evidenziato per farlo salire a bordo e portarlo a destinazione prima che scada il tempo. [FARE1:TAXI1] ~g~Destinazione: ~w~'Club Pole Position'~g~ in Ocean Beach. [FARE3:TAXI1] ~g~Destinazione: ~w~'Marina'~g~ in Ocean Beach. [FARE4:TAXI1] ~g~Destinazione: ~w~'Ammu-Nation'~g~ in Ocean Beach. [FARE5:TAXI1] ~g~Destinazione: ~w~'Hardware store'~g~ in Washington Beach. [FARE6:TAXI1] ~g~Destinazione: ~w~'Mall North Point'~g~ in Vice Point. [MFARE1:TAXI1] ~g~Destinazione: ~w~'Ammu-Nation'~g~ in Downtown. [MFARE2:TAXI1] ~g~Destinazione: ~w~'il terminal'~g~ all'aeroporto internazionale Escobar. [WFARE3:TAXI1] ~g~Destinazione: ~w~'Sunshine Autos'~g~ in Little Havana. [WFARE4:TAXI1] ~g~Destinazione: ~w~'Taxi Kaufman'~g~ in Little Haiti. [WFARE5:TAXI1] ~g~Destinazione: ~w~'Hardware store'~g~ in Little Havana. [WFARE6:TAXI1] ~g~Destinazione: ~w~'Howlin Petes Bike'~g~ in Downtown. [FARE7:TAXI1] ~g~Destinazione: ~w~'il negozio dei gioiellieri'~g~ in Vice Point. [FARE8:TAXI1] ~g~Destinazione: ~w~'la spiaggia'~g~ in Ocean Beach. [FARE9:TAXI1] ~g~Destinazione: ~w~'la spiaggia'~g~ in Washington Beach. [FARE10:TAXI1] ~g~Destinazione: ~w~'la spiaggia'~g~ in Vice Point. [FARE11:TAXI1] ~g~Destinazione: ~w~'l'ospedale'~g~ in Ocean Beach. [FARE12:TAXI1] ~g~Destinazione: ~w~'l'ospedale'~g~ in Vice Point. [FARE13:TAXI1] ~g~Destinazione: ~w~'la stazione di polizia'~g~ in Washington Beach. [FARE14:TAXI1] ~g~Destinazione: ~w~'la stazione di polizia'~g~ in Vice Point. [FARE15:TAXI1] ~g~Destinazione: ~w~'il ristorante della pizza'~g~ in Vice Point. [WFARE7:TAXI1] ~g~Destinazione: ~w~'la stazione di polizia'~g~ in Little Havana. [WFARE8:TAXI1] ~g~Destinazione: ~w~'la stazione di polizia'~g~ in Downtown. [WFARE9:TAXI1] ~g~Destinazione: ~w~'l'ospedale'~g~ in Downtown. [WFARE10:TAXI1] ~g~Destinazione: ~w~'l'ospedale'~g~ in Little Havana. [WFARE11:TAXI1] ~g~Destinazione: ~w~'lo stadio'~g~ in Downtown. [WFARE12:TAXI1] ~g~Destinazione: ~w~'il ristorante della pizza'~g~ in Little Haiti. [WFARE13:TAXI1] ~g~Destinazione: ~w~'il ristorante della pizza'~g~ in Downtown. [WFARE14:TAXI1] ~g~Destinazione: ~w~'i bacini'~g~ in Viceport. [WFARE15:TAXI1] ~g~Destinazione: ~w~'la farmacia'~g~ in Little Haiti. [FARE2:TAXI1] ~g~Destinazione: ~w~'Club Malibu'~g~ in Vice Point. {=================================== MISSION TABLE TAXICUT ===================================} [TAXC_A:TAXICUT] Immagino tu sia il nuovo proprietario. [TAXC_B:TAXICUT] Di chi fai parte? Mafia? Cartello? Non sembri Messicano... [TAXC_C:TAXICUT] Comunque, immagino ora comincerai con la vecchia frase 'le cose stanno per cambiare da queste parti', [TAXC_D:TAXICUT] magari minaccerai uno degli autisti... [TAXC_E:TAXICUT] vacci piano con quello là, Ted, ha appena avuto un'ernia al disco. [TAXC_F:TAXICUT] Beh, sì. Le cose stanno per cambiare da queste parti, signora. [TAXC_G:TAXICUT] Che felicità, figliolo. Me ne occupo io... [TAXC_H:TAXICUT] Ormai lo faccio da anni. [TAXC_I:TAXICUT] Attenzione a tutti: [TAXC_J:TAXICUT] è cambiata la gestione e le cose stanno nuovamente per cambiare da queste parti. [TAXC_K:TAXICUT] Il nostro nuovo direttore, della... [TAXC_L:TAXICUT] Di quale gang fai parte? [TAXC_M:TAXICUT] Beh, non faccio parte di nessuna gang. [TAXC_N:TAXICUT] Allora qual è il tuo maledetto nome, ragazzo? [TAXC_O:TAXICUT] Vercetti, Tommy Vercetti. [TAXC_P:TAXICUT] In nostro nuovo direttore, della Vercetti Gang, [TAXC_Q:TAXICUT] si assicurerà che non ci succedano incidenti. [TAXC_R:TAXICUT] Capisce? Fine! [TAXC_S:TAXICUT] Ti è piaciuto il 'Capisce'? A me è piaciuto il 'Capisce'. [TAXC_T:TAXICUT] Allora è così che ha funzionato nel passato: [TAXC_U:TAXICUT] noi portiamo avanti l'azienda come sempre. [TAXC_V:TAXICUT] Se abbiamo problemi con la concorrenza, tu ti occupi di riempirli di botte. [TAXC_W:TAXICUT] Poi loro ci riempiono di botte, [TAXC_X:TAXICUT] poi tu riempi loro di botte nuovamente, [TAXC_Y:TAXICUT] eccetera, eccetera. Tutto chiaro? [TAXC_Z:TAXICUT] Uh, sì, almeno credo... [TAXC_A1:TAXICUT] Prendi un taxi dal garage se ti senti di lanciarti subito nella mischia. {=================================== MISSION TABLE TAXIWA1 ===================================} [OUTTIME:TAXIWA1] ~r~Troppo lento, troppo lento! [TAX1_1:TAXIWA1] OK, abbiamo un cliente danaroso che richiede un taxi da Starfish Island: qualche volontario? [TAX1_2:TAXIWA1] Qui Tommy, lo prendo io! [TAX1_3:TAXIWA1] Questo cliente è mio, indietro! [TAX1_4:TAXIWA1] Forza, forza, entra in fretta! [TAX1_5:TAXIWA1] OK, OK! Ti prego, non farmi male! [TAXW1_1:TAXIWA1] ~g~Passa a prendere il VIP su Starfish Island. [TAXW1_2:TAXIWA1] ~g~Porta indietro il VIP! Metti fuori gioco l'altra macchina! [TAXW1_3:TAXIWA1] ~r~Il VIP è morto! [TAXW1_4:TAXIWA1] ~r~Il VIP è arrivato a destinazione! [TAXW1_6:TAXIWA1] ~g~Porta il VIP all'aeroporto! {=================================== MISSION TABLE TAXIWA2 ===================================} [TAX2_1:TAXIWA2] Chiamata a tutti i veicoli: stiamo perdendo clienti in tutta la città. Che cosa sta succedendo? [TAX2_2:TAXIWA2] I taxi VC continuano a batterci sul tempo. Sono in troppi, non possiamo competere! [TAX2_3:TAXIWA2] Mr. Vercetti, se sei in ascolto, devi mettere fuori gioco un po' dei taxi VC, se no falliamo! [TAXW2_1:TAXIWA2] ~g~Distruggi 3 taxi rivali! {=================================== MISSION TABLE TAXIWA3 ===================================} [TAX3_1:TAXIWA3] Taxi 13, la signorina Cortez ha bisogno di essere prelevata da Down Town e ha richiesto esplicitamente di te. [TAX3_2:TAXIWA3] OK, ricevuto. Taxi 13 chiudo. [TAX3_3:TAXIWA3] Hmmm, nessun segno di Mercedes... [TAXW3_3:TAXIWA3] ~g~Metti fuori combattimento il capo dei taxisti! [TAXW3_2:TAXIWA3] ~g~Resta vivo fino allo scadere del tempo. [TAX_AS1:TAXIWA3] BENI COMPAGNIA DEI TAXI ACQUISITI [TAX_AS2:TAXIWA3] La compagnia dei taxi d'ora in poi genererà introiti per un massimo di ~1~$. Ricordati di recuperarli regolarmente. [TAX3_4:TAXIWA3] È giunta l'ora che l'angelo custode dei Taxi Kaufman entri in azione! [TAX3_5:TAXIWA3] Ehi ragazzo, ti abbronzo il fondoschiena! { re3 updates } { new languages } [FEL_JAP] GIAPPONESE [FEL_POL] POLACCO [FEL_RUS] RUSSO { new display menus } [FET_GFX] GRAPHICS SETUP [FED_MIP] MIP MAPPING [FED_AAS] ANTI ALIASING [FED_FIL] TEXTURE FILTERING [FED_BIL] BILINEAR [FED_TRL] TRILINEAR [FED_WND] WINDOWED [FED_FLS] FULLSCREEN [FEM_CSB] CUTSCENE BORDERS [FEM_SCF] SCREEN FORMAT [FEM_ISL] MAP MEMORY USAGE [FEM_LOW] LOW [FEM_MED] MEDIUM [FEM_HIG] HIGH [FEM_2PR] PS2 ALPHA TEST [FEC_FRC] FREE CAM { Linux joy detection } [FEC_JOD] DETECT JOYSTICK [FEC_JPR] Press any key on the joystick of your choice that you want to use on the game, and it will be selected. [FEC_JDE] Detected joystick { mission restart } [FET_RMS] RIGIOCA MISSIONE [FESZ_RM] RIGIOCA? [FED_VPL] VEHICLE PIPELINE [FED_PRM] PED RIM LIGHT [FED_RGL] ROAD GLOSS [FED_CLF] COLOUR FILTER [FED_WLM] WORLD LIGHTMAPS [FED_MBL] MOTION BLUR [FEM_SIM] SIMPLE [FEM_NRM] NORMAL [FEM_MOB] MOBILE [FED_MFX] MATFX [FED_NEO] NEO [FEM_PS2] PS2 [FEM_XBX] XBOX [FEC_IVP] INVERT PAD VERTICALLY [FEM_NON] NONE [FEC_DS2] DUALSHOCK 2 [FEC_DS3] DUALSHOCK 3 [FEC_DS4] DUALSHOCK 4 [FEC_360] XBOX 360 CONTROLLER [FEC_ONE] XBOX ONE CONTROLLER [FEC_TYP] GAMEPAD TYPE [FET_AGS] GAMEPAD SETTINGS [FEM_AUT] { aspect ratio related } AUTO [DUMMY] THIS LABEL NEEDS TO BE HERE !!! AS THE LAST LABEL DOES NOT GET COMPILED ================================================ FILE: utils/gxt/spanish.txt ================================================ { New strings are at the bottom of file. Do not change the order of strings. You can fix the typos of existing translation but please refrain from unnecessary edits like rephasing because you think it suits better for your taste. SPANISH NOTE: This is European Spanish, do not mix it with Latin American Spanish. If you want the Latin American Spanish translation you'd have to create a separate txt for it. } { Grand Theft Auto Vice City Spanish (Spain) Translation } { Contains some of the official fixes made by Rockstar for the iOS port } { Additional translation rewrites, corrections and fixes by IlDucci } [IN_VEH] ~g~¡Eh! ¡Vuelve al vehículo! [HEY] ~g~¡No vayas solo, mantén tu grupo unido! [HELP3] Solo puedes esprintar durante cortos periodos de tiempo, antes de cansarte. [HELP4_D] Mueve el ~h~joystick analógico derecho~w~ hacia arriba para ~h~acelerar. [HELP5_D] Tira del ~h~joystick analógico derecho~w~ hacia atrás para ~h~frenar~w~, o para ~h~dar marcha atrás~w~ si el vehículo se ha detenido. [HELP7_A] Pulsa y mantén pulsado el ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar~w~ con el rifle de francotirador. [HELP7_D] Pulsa y mantén pulsado el ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar~w~ con el rifle de francotirador. [HELP10] Esta insignia indica que tienes un nivel de se busca por la policía. [HELP11] Cuantas más insignias tengas, mayor es tu nivel de se busca. [HELP13] Algunas veces puedes necesitar ir por caminos que el radar no detecta. [TIMER] Ésta es una misión cronometrada, debes completarla antes de que el cronómetro llegue a cero. [HORN] ~g~Toca la bocina. [NOMONEY] ~g~¡Necesitas más dinero! [REWARD] RECOMPENSA ~1~ $ [M_FAIL] ¡MISIÓN FALLIDA! [M_PASS] ¡MISIÓN SUPERADA! ~1~ $ [DEAD] ¡ELIMINADO! [BUSTED] ¡ARRESTADO! [WEATHE1] FORZAR TIEMPO SOLEADO [WEATHE2] FORZAR TIEMPO EXTREMADAMENTE SOLEADO [WEATHE3] FORZAR TIEMPO NUBLADO [WEATHE4] FORZAR TIEMPO LLUVIOSO [WEATHE5] FORZAR TIEMPO CON NIEBLA [WEATHE6] TIEMPO NORMAL [NUMBER] ~1~ [LOADCAR] CARGANDO VEHÍCULO... (PULSA L1 PARA CANCELAR) [CARSOFF] Coches desactivados. [CARS_ON] Coches activados. [TEXTXYZ] Escribiendo coordenadas en el archivo... [CHEATON] Modo trucos ACTIVADO [CHEATOF] Modo trucos DESACTIVADO [IMPORT1] Ve fuera y espera tu vehículo. [PAGEB11] Entregado lanzallamas en el escondite [WANT_A] Sólo serás arrestado si tienes un ~h~nivel de se busca. [WANT_B] Tu ~h~nivel de se busca~w~ viene representado por la fila de estrellas en la parte superior derecha de la pantalla. [WANT_C] Ahora tienes un ~h~nivel de se busca~w~ de una... [WANT_D] dos... [WANT_E] tres... [WANT_F] A medida que tu ~h~nivel de se busca~w~ aumente, atraerás a representantes más poderosos de las fuerzas policiales. [WANT_G] Cuando te hayan ~h~''arrestado''~w~ serás conducido a la comisaría más cercana. [WANT_H] Los polis te quitarán todas las armas y parte de tu dinero como soborno. [WANT_I] Cualquier misión en la que estuvieses habrá fracasado. [WANT_J] Encontrarás modos de reducir tu nivel de se busca cuanto más juegues. [WANT_K] Si estás en un coche, el ~h~TALLER DE PINTURA~w~ limpiará ~h~tu nivel de búsqueda. [HEAL_B] Cuando seas ~h~''eliminado''~w~ serás conducido al hospital más cercano. [HEAL_C] Perderás tus armas y los médicos te quitarán algo de dinero por recomponerte. [HEAL_E] Encontrarás modos de curarte o de protegerte cuanto más juegues al juego. [BRIBE1] Acabas de recoger un soborno de la policía, esto disminuirá en una estrella tu nivel de se busca. [SAVE1] Entra al corona para ~h~guardar la partida~w~. No puedes guardar durante una misión. [SAVE2] Cualquier vehículo que quede en este garaje será almacenado cuando se guarde la partida. [AMMU] Ve dentro de Ammu-Nation para comprar un arma. [R_TIME] TIEMPO DE CARRERA: [PROP_1] No tienes suficiente dinero para adquirir esta propiedad [PROP_2] No puedes comprar propiedades mientras estás en una misión. [IND_ZON] Vice City Beach [COM_ZON] Península de Vice City [BEACH1] Ocean Beach [BEACH2] Washington Beach [BEACH3] Vice Point [GOLFC] Leaf Links [STARI] Starfish Island [DOCKS] Viceport [HAVANA] Little Havana [HAITI] Little Haiti [PORNI] Prawn Island [DTOWN] Centro de la ciudad [VICE_C] Vice City [A_PORT] Escobar International [JUNKY] Vertedero [PISTOL] Pistola [PYTHON] .357 [UZI] Uz-1 [TEC9] Tec 9 [M4] M4 [INGRAM] Mac [MP5] MP [RUGER] Kruger [SNIPE] Rifle de francotirador [GRENADE] Granadas [SHOTGN1] Escopeta [SHOTGN2] S.P.A.S. 12 [SHOTGN3] Escopeta de cañones recortados [ARMOUR] Chaleco antibalas [LASER] .308 Rifle de francotirador [BASEBAT] Bate de béisbol [HAMMER] Martillo [SCREWD] Destornillador [CLEVER] Cuchillo de carnicero [MACHETE] Machete [KNIFE] Cuchillo [KATANA] Katana [CHAINSA] Sierra eléctrica [G_COST] ~1~ $ [CAR_1] Ambulancia [MALIBU] El Club Malibú [MANSION] Mansión de Díaz [TMANS] Finca de Vercetti [STRIP] El Club Pole Position [MALL1] Centro comercial North Point [BANKINT] El Banco Corrupto Grande [RANGE] Campo de tiro con rifle [POL_HQ] Cuartel general de VCPD [INT_B] Un viejo amigo [INTB_1] ~g~Ve a la oficina del Abogado. [LAW_1] La fiesta [LAW_2] Pelea en el callejón trasero [LAW_3] Furia en el jurado [LAW_4] Disturbios [COL_1] Cerdo traidor [COL_2] Tiros en el centro comercial [COL_3] Ángeles guardianes [COL_4] ¡Señor, sí, señor! [COL_5] ¡Todos con las manos arriba! [COK_1] La caza [COK_2] Phnom Penh '86 [COK_3] El barco más rápido [COK_4] Oferta y demanda [KENT_1] El corredor de la muerte [ASS_1] Borrar [BUD_1] Extorsión [BUD_2] Pelea en el bar [BUD_3] Tierra de polis [CAP_1] Liquida al cobrador [FIN_1] Mantén cerca a tus amigos... [BANK_1] ¿Sin escapatoria? [BANK_2] El tirador [BANK_3] El conductor [BANK_4] El atraco [CNT_1] Descubriendo el pastel [CNT_2] Ataca al mensajero [PORN_1] Campaña de reclutamiento [PORN_2] Consolador Dodo [PORN_3] La foto policial de Marta [PORN_4] Punto G [TAX_1] Taxis Kaufman [TAXI_1] V.I.P. [TAXI_2] Rivalidad amistosa [TAXI_3] Taxigedón [ICE_1] Distribución [TEX_1] Hierro número cuatro [TEX_2] Dos leves impactos [TEX_3] Demoledor [PHIL_1] Traficante de armas [PHIL_2] Boomshine Saigon [BIKE_1] Moto con llantas de aleación [BIKE_2] Incitando al macho [BIKE_3] Moto robada [ROCK_1] Love Juice [ROCK_2] Asesino psicópata [ROCK_3] Gira publicitaria [ROCK_4] ¡Love Fist! [HAT_1] Poción mágica [HAT_2] ¡Bombas fuera! [HAT_3] Juego sucio [CUB_1] El desafío del barco trucado [CUB_2] Carne de cañón [CUB_3] Encuentro naval [CUB_4] Vudú troyano [JOB_1] Muerte en la carretera [JOB_2] Elimina a la esposa [JOB_3] Autocidio [JOB_4] Comprobar el registro [JOB_5] Cabos sueltos [ANSWER] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para responder a una llamada a tu teléfono móvil. [MOB_01A] ¡Estás bien figura! Soy Paul. Puede que tenga algún resultado para ti, pero necesito hablar contigo cara a cara. [MOB_01B] Estoy de relax en el Malibu. [MOB_01C] Creo que después de esto me vas a deber un favor o dos, guapo. Hasta luego. [MOB_02A] Ssssnniiiiffffff... ¡Eh! Hola, ¿Tommy? ¡Tommy! [MOB_02B] Tenemos un problema en la imprenta. Mejor que vayas y lo compruebes. [MOB_02C] Algún tipo de lío. Las cosas andan revueltas. Tengo que irme. [MOB_03A] ¿Sr. Vercetti? Tengo un trozo de mierda firmada que dice [MOB_03B] que usted ha asumido todas las deudas de BJ's Auto. [MOB_03C] Con la repentina desaparición de BJ no tengo más remedio [MOB_03D] que hacerle responsable de su inestabilidad financiera [MOB_03E] Hasta que esta cuenta quede completamente saldada [MOB_03F] debería considerar que las calles de Vice City son poco seguras. [MOB_04A] ¿Cómo estás, tío? Soy Paulo. [MOB_04B] Mira, Tommy, me olvidé de mencionar que vamos a necesitar más seguridad para el concierto. [MOB_04C] Hay una banda de moteros liderada por Mitch Baker que serían la publicidad ideal. [MOB_04D] Solucióname esto y te conseguiré un pase para la actuación, ¿vale? [MOB_05A] Soy Mitch. Hiciste un buen trabajo, Tommy. Es bueno volver a tener a mi pequeña en casa. [MOB_05B] Dile al Sr. Kent Paul que dispondrá de seguridad para la actuación. [MOB_05C] Tiene mi palabra de ello. [MOB_05D] Intenta no meterte en problemas. [MOB_06A] Tommy, esos de abajo han estado rajando sobre ti, chico. [MOB_06B] Pensé que podrías necesitar consuelo casero así que la tía Poulet te preparará una sopa, ¿hmmm? [MOB_06C] Pásate por mi cocina alguna vez, ¿de acuerdo Tommy? [MOB_08A] Eh Tommy, pensé que podrías necesitar algún asesoramiento financiero. [MOB_08B] Una vez que tengas la operación en marcha, tendrás que pasarte a recoger la recaudación de la semana. [MOB_08C] Deja que esos tipos crean que son los amos del lugar e intentarán cepillarse los beneficios... ¿vale? [MOB_08D] Eh, sé como llevar el negocio, Ken. [MOB_08E] Vale, vale. Lo sé, ya sabes. Lo sé. Estaba, [MOB_08F] Sólo estaba, ya sabes, diciéndote que sé, que tú sabes que yo lo sé. [MOB_08G] ¡Solo manteniéndolo a punto, nene! [MOB_08H] Lo que sea, Ken, lo que sea... [MOB_09A] ¡Eh, Leo! ¡Tengo trabajo para ti! [MOB_09B] No soy Leo. [MOB_09C] ¡Eh, como Leo sepa que tienes su teléfono, te va a liquidar! [MOB_09D] Quizás Leo ya esté muerto. Quizás yo le maté y cogí su teléfono, ¿te has parado a pensar eso, gilipollas? [MOB_09E] ¿Mataste a Leo? Debes tener unas pelotas muy grandes... ¿quieres trabajar para mí? [MOB_09F] Pásate por el café de mi padre en Little Havana y podremos hablar de tú a tú. [MOB_10A] ¡Tommy! Mira, tengo que pedirte un favor. [MOB_10B] ¡Steve! ¿Cómo van con el rodaje? [MOB_10C] Bien, bien. Yo, je, NOSOTROS necesitamos una escena con una persecución de coches, pero nuestro presupuesto no da para más. [MOB_10D] Me han dejado un coche en la ciudad. Tú sabrás qué hacer. [MOB_10E] De acuerdo, Steve, estaré al tanto. Te veo luego. [MOB_11A] ¿Qué hay, hijo? Pensé sólo en llamarte para darte algún consejo. [MOB_11B] Hola, Avery. ¿Qué te preocupa? [MOB_11C] Hay muchas oportunidades en esta ciudad si dispones de las propiedades adecuadas, ¿entiendes lo que quiero decir? [MOB_11D] Creo que sí... [MOB_11E] Lo que digo es que mantengas los ojos abiertos y podrás encontrar la oportunidad de negocio perfecta. Te veré luego. [MOB_11F] Nos vemos, Avery. [MOB12_A] ¡Eh, Tommy, soy Avery! Ahora escucha, estoy muy liado en este momento [MOB12_B] y uno de mis representantes necesita carabina para ir a Gator Keys. [MOB12_C] Estoy detrás de unas tierras por allí, así que voy a enviar alguien a que ablande el trato. [MOB12_D] Podrías hacerme un favor y asegurarte de que llega allí, ¿entendido? [MOB12_E] Sí, eso está hecho, Avery. ¿Dónde te gustaría que le recogiese? [MOB12_F] Está terminando algunos negocios en la obra. Le dije que le recogerías allí. [MOB12_G] Ningún problema. Nos vemos, Avery. [MOB13_A] ¿Vercetti? ¡VERCETTI! ¡Maldito seas, tío, tienes que ayudarme! [MOB13_B] ¿Sr. Moffat? ¿Cómo le va la vida en familia? [MOB13_C] Maldito seas, DEMONIOS, ¿me oyes? [MOB13_D] Bueno, fue agradable charlar... [MOB13_E] ¡ESPERA! Espera, Vercetti Tommy, ¿Puedo llamarte Tommy? [MOB13_F] Los dos somos hombres de negocios, ¿verdad? Reconoces un buen negocio cuando lo ves. [MOB13_G] No tengo tiempo para charlar, vaya al grano. [MOB13_H] DINERO. El dinero es el maldito asunto. [MOB13_I] Me escapado de la trena otra vez, pero nunca tardan mucho en encontrarme... ¡creen que es un maldito juego! [MOB13_J] Estoy en un teléfono público en alguna parte de este agujero de mierda olvidado de la mano de Dios. [MOB13_K] Sácame de aquí antes de que me lleven de vuelta y... oh... Dios... [MOB13_L] Bueno, estoy ocupado durante las próximas... [MOB13_M] ¡No! ¡No me jodas, ten piedad! Ningún hombre debería tener que hacer esas, esas cosas. [MOB13_N] Estoy de rodillas, Tommy, en el fango, rogándote, por favor... [MOB13_O] Supongo que podría desviarme hacia allí, veré si puedo localizarte... [MOB13_P] Oh, Dios, ya vienen. Por el amor de Dios, date prisa, ¡date prisa! [MOB_14A] Hola, Tommy, me vas a adorar, amigo. [MOB_14B] Un pajarito me ha dicho que la división SWAT de Vice City tiene un depósito en una entidad bancaria ciertamente grande, [MOB_14C] donde guardan todos los sobornos que han recibido durante estos años, [MOB_14D] ¡como una especie de fondo de retiro para los veteranos! [MOB_14E] Por supuesto, si esta información te ayudase a adquirir parte de ese dinero, [MOB_14F] ¿Supongo que te sentirías obligado a mandarme algo? [MOB_14G] Lo tendré en mente, gracias Kent. [MOB_14H] Es Paul. Soy de Kent, cerca de Londres, idiota. [MOB_14I] Mi conocimiento de la geografía provincial inglesa ya no es lo que era. [MOB15_A] Tommy, colega, soy Paul, de Kent, [MOB15_B] un par de titis han escrito tu nombre por todas partes, en el Malibú. [MOB15_C] ¿De qué estás hablando? [MOB15_D] Tías. Palomitas. Ya sabes. Chicas. De las buenas, no creas que son furcias ni nada por el estilo. [MOB15_E] Tienes que venir a echarles un vistazo. [MOB16_A] Tommy, aquí Paulo, ¿qué pasa amigo? [MOB16_B] ¿Qué quieres Paul? No quiero ropa de imitación. [MOB16_C] Muy divertido, colega, pero yo no toco la ropa falsificada. [MOB16_D] Nada, sólo llamaba para ver si consigo un papel en una de tus películas, [MOB16_E] en Inglaterra hice un montón de porno, colega. [MOB16_F] Guardo más fuego que tu, hijo mío. [MOB16_G] Paul, gracias por la oferta, lo tendré en mente. [MOB16_H] En serio, no te olvides de mí, después de todo lo que he hecho por ti. [MOB16_I] Eso es lo que estoy intentando olvidar. [MOB19_A] Tommy V, aquí KP. Kent Paul. En las calles se dice que hay gente que te quiere despellejar. [MOB19_B] Mantén los ojos abiertos. Y recuerda, yo no te he dicho nada de esto. [MOB_20A] De acuerdo, Tommy, soy Paul. Me ha dicho un pajarito que has sido un chico muy malo. [MOB_20B] A alguien le ha ofendido que de repente estés actuando como si fueses el mandamás, dándote aires de grandeza. [MOB_20C] Bueno, no digas que nunca te advertí. Presumir es un juego de tontos, hijo. [MOB_20D] En cualquier caso, oí que se ha puesto un precio a tu cabeza y que alguien te la va a partir, [MOB_20E] así que ten cuidado, y acuérdate de mí, colega. [MOB21_A] Tommy, Thomas, soy Cortez. ¿Qué pasa? [MOB21_B] Las cosas van bien. ¿Cómo estás? [MOB21_G] De todos modos, Tommy, quisiera preguntarte algo sobre Mercedes. [MOB21_H] ¿Sí? ¿Qué pasa con ella? [MOB21_I] Oh Tommy, Tommy, oigo todas esas historias y no sé qué pensar. [MOB21_K] Quizás ella piense que puede hacer lo que quiera ahora que pero dime, Tommy, ¿es verdad? [MOB21_M] ¿El qué es verdad? [MOB21_N] Las historias que he oído. ¿De verdad va a ser abogada? [MOB21_O] Oh Tommy, la vergüenza. Los Cortez somos una familia orgullosa [MOB21_P] y nunca permitiríamos que una hija nuestra se convirtiese en abogada. Por favor, dime que no es así. No creo que pudiese soportarlo. [MOB21_Q] Coronel, puedo asegurarle que Mercedes nunca será abogada. No tema. [MOB21_R] Gracias, Tommy, gracias. La vergüenza sería insoportable. [MOB21_S] Lo sé, coronel. [MOB21_T] Bueno, Tommy, debes perdonarme, ha llegado el nuevo ministro de interior. [MOB21_U] Hace muchos años, yo maté a su padre en un golpe fallido así que debo ser cortés. Buenos días, amigo. [MOB21_C] Tommy, aquí siempre es una lucha. Siento el ruido, acabamos de tener otro golpe fallido. [MOB21_D] La gente es la señora más exigente de todas. [MOB21_E] Desde que he vuelto de Vice City y hasta ahora, hemos tenido tres revoluciones y cuatro golpes. [MOB21_F] Afortunadamente, me han ascendido cada vez. [MOB21_J] Quizás todo el mundo me está humillando. [MOB21_L] pero dime, Tommy, ¿es verdad? [MOB22_A] Tommy, estás demostrando ser muy útil, amigo mío. [MOB22_B] Gracias, Cortez. ¿Qué hay de mi asunto? [MOB22_C] Tommy, estoy trabajando infatigablemente en tu nombre para conseguir llegar al fondo de esta zanja de apestosas mentiras y engaños, [MOB22_D] tienes mi palabra al respecto, pero mientras tanto, [MOB22_E] por favor, acepta el estimable agradecimiento de mi gente por tu trabajo para nosotros. [MOB_25A] Tommy, soy Cortez. Los franceses me están causando todo tipo de problemas. [MOB_25B] Malditos hipócritas. ¡Se pasan cien años robando a países pobres y me llaman ladrón a mí! [MOB_25C] Voy a necesitar tu ayuda tan pronto como sea posible. [MOB_25D] Por favor, Tommy, date prisa, te necesito, ¿de acuerdo? Odio a esos malditos franceses. [MOB_26A] Hola, ¿Tommy? [MOB_26B] ¿Sí? [MOB_26C] Soy Baker. Sólo quería decirte que verdaderamente disfruté con el espectáculo. [MOB_26D] Los chicos y yo queremos darte las gracias, y recordarte, [MOB_26E] que te tienes nuestro respeto. Buenos días. Sigue cabalgando duro, chico. [MOB_29A] Hola, ¿hablo con el Sr. Tommy Vercetti? [MOB_29B] Sí. [MOB_29C] Bueno, he oído por radio macuto que usted es el hombre que hay que buscar cuando alguien tiene una plaga de bichos. [MOB_29D] Puede... [MOB_29E] Bueno, yo tengo una plaga de bichos de verdad. Haitianos, por todas partes. [MOB_29F] Mi nombre es Umberto Robina y quiero que nos reunamos en el Café Robina lo antes posible, [MOB_29G] porque, como le digo, estos malditos haitianos se han pasado de la raya esta vez. [MOB_29H] Prueba [MOB_30A] Tommy, soy Umberto Robina. [MOB_30B] ¿Cómo está el café? [MOB_30C] Oh, maravilloso. Increíble. Tommy, increíble. Sin blandengues, Tommy, sólo hombres de verdad, ya sabes, ¡y las mujeres hermosas! [MOB_30D] De todos modos, quería decirte que para papá y para mí, para nosotros, ahora eres un cubano. [MOB_30E] Has demostrado que tienes unas pelotas de cojones, tío. [MOB_30F] Gracias, Umberto. Nadie me había dicho eso desde que salí de la cárcel. Nos vemos. [MOB_33A] Tommy, soy Phil, olvida toda esa mierda del pasado y escúchame, ¿me oyes? [MOB_33B] Bien. Tengo un pelotazo extra a punto de fermentar y me preguntaba si te apetecería probarlo. [MOB_33C] En serio, Tommy, si quieres una copa, o si necesitas ponerte a tono, este material hará de ti un hombre. [MOB_33D] A mí me pasó aunque todavía no puedo ver por un ojo. Te estaré esperando, lo que oyes. [MOB_34A] Tommy, de verdad que he disfrutado trabajando contigo. No me había divertido tanto desde la loma en Vietnam, colega. [MOB_34B] De todas formas, si necesitas algo, llámame, ¿me oyes? [MOB_34C] Siempre me acuerdo de aquellos con los que he servido, [MOB_34D] y estoy seguro de que puedo ayudarte, ¿me oyes? [MOB_35A] Tommy, la herida está cicatrizando bien. Lo gracioso es [MOB_35B] que he luchado en 6 zonas de combate y siempre he salido sin un rasguño, ¡y ahora esto! [MOB_35C] Phil el manco. Aún así, tengo una buena selección armas de fuego para mancos, así que nunca seré Phil el desarmado, lo que oyes. [MOB_35D] Venga, hijo, corta con esa mierda sentimental y pídete una copa, ¡me oyes! [MOB_36A] Tommy, soy Phil, quiero darte las gracias por ayudarme allí, hijo, [MOB_36B] maldito Charlie, siempre te tenderá una emboscada de un modo o de otro. [MOB_36C] Bueno, la herida está cicatrizando bien, y eso quiere decir que voy a dejar de estafar al gobierno con mi cheque de incapacidad. Gracias, colega. [MOB_40A] Eh, Tommy, soy Sonny. ¿Qué tal el bronceado? [MOB_40B] No tengo bronceado. [MOB_40C] Bueno, tampoco tienes mi dinero, así que me estoy preguntando, [MOB_40D] ¿qué estás haciendo? Así que, dime Tommy ¿qué estás haciendo? [MOB_40E] Estoy buscando el dinero, Sonny. No te preocupes. [MOB_40F] Me estoy preocupando, Tommy, ese es mi estilo, [MOB_40G] porque parece que en mi vida tengo este problema con gente que no es de confianza. [MOB_40H] No seas una persona poco fiable, Tommy, por favor. [MOB_40I] Haznos un favor. Tengo muchas ganas de tener noticias tuyas. [MOB_41A] Tommy, ¿te acuerdas de mí? [MOB_41B] Hola, Sonny. [MOB_41C] Sí, está bien, Sonny. Somos viejos amigos, [MOB_41D] y nunca me escribes, nunca llamas. ¿No quieres que sigamos siendo amigos? [MOB_41E] He estado muy ocupado intentando arreglar las cosas. No me diste mucho apoyo. [MOB_41F] Oh, y es culpa mía, ¿verdad? Bueno, he oído que has estado muy ocupado. [MOB_41G] Ocupado matando a capos. Ocupado tomando el poder. [MOB_41H] No te olvides de nosotros, Tommy, porque puedo asegurarte que yo no me he olvidado de ti. [MOB_42A] Tommy. [MOB_42B] Sonny. [MOB_42C] Obviamente tienes un problema a la hora de escuchar a los demás, de modo que lo intentaré de nuevo. [MOB_42D] Tommy, ¿dónde está el maldito dinero, dónde está el maldito material y dónde está mi parte de tus nuevos golpes? [MOB_42E] Me estás haciendo quedar como un idiota, Tommy, y no me hace gracia. [MOB_43A] Tommy, Tommy, Tommy, tenía a Sonny al teléfono, de acuerdo, ¿me sigues? [MOB_43B] No sé tú, pero hay algo sobre un hombre que amenaza con asesinar a mi familia [MOB_43C] que realmente me pone enfermo. ¿Qué vas a hacer? [MOB_43D] Ten calma, Ken, todo irá bien. [MOB_43E] ¡ESTOY calmado, todo lo calmado que puede estar un hombre cuando teme por su vida! [MOB_43F] Ken, ahora mismo tengo otras cosas en la cabeza, [MOB_43G] nos ocuparemos de Forelli cuando llegue el momento. [MOB_43H] Estoy calmado. ¿No te lo parece? Debe ser la muerte inminente la que le hace esto a mi voz. [MOB45_A] Tommy, tenemos cosas de las que hablar. [MOB45_B] ¿Cuál es el problema, Lance? [MOB45_C] Eres tú, amigo, creo que no me estás dando una tajada justa. [MOB45_D] Y lo que es más, me estás avergonzando delante de los chicos. No puedo permitirlo. [MOB45_E] Lance, las cosas no son así. Has estado cometiendo errores. [MOB45_F] Tommy, no soy tu chico de los recados. No soy tu emisario. [MOB45_G] Lance, no la cagues, y no tendremos ningún problema. Si la cago yo, entonces podrás venir a reprochármelo en cualquier momento. [MOB45_H] Tommy, he hecho de todo por ti, pero me tratas como si fuese estúpido. No hagas eso. [MOB45_I] Lance no te voy a timar ni a apuñalarte por la espalda, de acuerdo. [MOB45_J] Tómatelo con calma. Esto ya es lo suficientemente duro sin que te me pongas sentimental. [MOB45_K] Confía en mí. ¿Me oyes, me oyes? [MOB45_L] Te oigo, pero Tommy, no puedo aguantar esto por más tiempo. [MOB45_M] Lance, no seas así. Ahora te estoy dando un aviso. [MOB45_N] ¿Me oyes? Relájate, tómate unos días libres, y luego hablaremos, ¿vale? [MOB46_A] ¡Eh, Tommy! Soy Lance. [MOB46_B] ¿Sí? [MOB46_C] Me alegro tanto de tener noticias tuyas, Lance. Venga, tío, enróllate, enróllate. [MOB46_D] Estoy ocupado. ¿Qué quieres? [MOB46_E] Nada. Solo llamo para decirte, ya sabes. Mira Tommy, podemos hacer esto. [MOB46_F] Tú y yo, sin ningún problema. ¿Sabes lo que quiero decir? [MOB46_G] Vamos a tener que hacerlo, porque de otro modo, estaremos muertos, Lance. [MOB46_H] Ya hemos ido demasiado lejos. Pero gracias por llamar. Hablaré contigo más tarde. [MOB_47A] Tommy, Lance, tenemos problemas serios. Ven aquí. Enseguida. [MOB52_A] Eh, Leo, creo que tengo un comprador para la mercancía de Díaz. [MOB52_B] Tienes que llamarle, tío, montar el trato, ¿sabes? [MOB52_C] ¿Dónde estás? [MOB52_D] ¿Estás bien, Leo? Te noto un tanto diferente. [MOB52_E] Simplemente dime dónde estás. [MOB52_F] ¿Quién demonios eres? ¡Que se ponga Leo! [MOB52_G] Leo se ha ido de viaje unos días, me dejó al cargo de todo. [MOB52_H] ¡Que te jodan, tío! [MOB54_A] ¡Hola, Tommy! [MOB54_B] Hola, Mercedes, ¿cómo te va? [MOB54_C] Tengo un apartamento nuevo en Vice Point, [MOB54_D] pensé que quizás querrías pasarte en algún momento. [MOB54_E] Me encantaría. Te veo luego. [MOB55_A] Tommy, soy yo. [MOB55_B] Hola, Mercedes. [MOB55_C] Tommy, estoy tan aburrida, ¿cuándo vamos a divertirnos un poco? [MOB55_D] ¿Qué quieres decir? [MOB55_E] Bueno, sé que estás ocupado luchando y matando y sobornando a gente, [MOB55_F] pero yo sólo quiero divertirme un poco. Así que no te olvides de mí, ¿me oyes? [MOB56_A] Tommy, oí que te cargaste a Ricardo Díaz. [MOB56_B] Hubo un desafortunado tiroteo en su mansión. [MOB56_C] Creo que se quemó hasta morir en su camisa acrílica. [MOB56_D] Tommy, estoy tan orgullosa de ti. Sabía que eras un hombre de verdad. [MOB56_E] Era un marrano en pantalones, me haces sentir muy orgullosa de ser tu amiga. [MOB56_F] No, sé que vas a estar ocupado intentando tomar las riendas de esta ciudad, [MOB56_G] pero no te olvides de mí, ¿me oyes? [MOB57_A] Soy Mercedes. Ya no te amo, Tommy. [MOB57_B] Ya no lo hago. De verdad. Porque ya no eres amable con Mercedes. [MOB57_C] Ya no la tratas como una dama. Me ignoras y te odio. [MOB57_D] ¡Insisto en que vengas a verme enseguida! [MOB58_A] Tommy. [MOB58_B] Hola, Mercedes. [MOB58_C] Hola, tipo duro. Estoy muy enfadada contigo, Tommy. [MOB58_D] No me hagas ir nunca más por ahí con Jezz Torrent otra vez. [MOB58_E] Es patético. A mitad de camino, empieza a llorar por su perrito [MOB58_F] que murió cuando él tenía siete años y que si su madre nunca le había querido. [MOB58_G] Y Tommy. En privado lleva peluca y sostén. [MOB58_H] ¡No estoy muy contenta contigo! [MOB59_A] Ooh Tommy, soy Mercedes. [MOB59_B] Sólo quería decirte que me divertí mucho en el plató de rodaje. [MOB59_C] Cualquier otra cosa que tengas así, me lo dices. [MOB59_D] Lo digo de verdad. Siempre quise ser actriz. [MOB59_E] Creo que aprendí mucho sobre el proceso dramático. [MOB59_F] ¡Es tan instructivo! Gracias. Gracias. Hasta muy, muy pronto. Adiós. [MOB_99] Ve al teléfono público del lugar. [MOB_98] Ve al teléfono público del lugar. [MOB_97] Ve al teléfono público del lugar. [MOB_96] Ve al teléfono público del lugar. [MOB_95] Ve al teléfono público del lugar. [A_TIME] +~1~ segundos [DODO_FT] ¡Volaste durante ~1~ segundos! [GA_8] Utiliza el detonador para activar la bomba. [GA_10] Muy bien. Aquí están tus ~1~ $. [GA_11] Ya tenemos ese coche. ¡No nos sirve! [GA_12] Bomba activada. [GA_13] Entregado como un profesional. Completa la lista y habrá una bonificación para ti. [GA_14] Todos los coches. ¡ESTUPENDO! Aquí tienes una cosilla. [GA_15] Espero que te guste el nuevo color. [GA_16] Volver a pintarlo es gratis. [GA_19] No estamos interesados en este modelo. [GA_20] Tenemos más de eso de los que podemos colocar. Lo siento, tío, no hay trato. [CHASE] Atención de la prensa [CHASE1] Ignorado [CHASE2] Aburrido [CHASE3] Algo interesante [CHASE4] Página 7 del periódico local [CHASE5] Portada del periódico local [CHASE6] Página 2 del Vice Courier [CHASE7] Portada del Vice Courier [CHASE8] TV Local a las 3 de la mañana [CHASE9] Noticias locales en TV [CHASE10] Cobertura en directo de la TV Local [CHASE11] Página 12 del UFA Today [CHASE12] Página 4 del UFA Today [CHASE13] Imagen en UFA Today [CHASE14] TV Nacional a las 4 de la mañana [CHASE15] Noticias de la TV Nacional [CHASE16] Cobertura en directo de la TV Nacional [CHASE17] Noticias internacionales [CHASE18] Crisis nacional [CHASE19] Crisis internacional [CHASE20] Acontecimiento mundial [CHASE21] Leyenda [CR_1] La grúa no puede levantar este vehículo [PU_MONY] No tienes suficiente dinero. [CO_ALL] Los tienes todos. Aquí tienes una cosilla... [FEM_ON] SÍ [FEM_OFF] NO [FEM_YES] Sí [FEM_NO] No [FEM_RET] REINTENTAR [FEC_NA] NO DISPONIBLE [FEC_CWL] Escoger arma hacia la izquierda [FEC_CWR] Escoger arma hacia la derecha [FEC_LOF] Mirar hacia adelante [FEC_TAR] Objetivo [FEC_MOV] Movimiento [FEC_CAM] Modos de cámara [FEC_PAU] Pausa [FEC_ENV] Entrar en el vehículo [FEC_JUM] Saltar [FEC_ATT] Atacar o disparar arma [FEC_RUN] Correr [FEC_FPC] Cámara en primera persona [FEC_LL] Mirar a la izquierda [FEC_LB] Retrovisor [FEC_LR] Mirar a la derecha [FEC_ABR] Acelerar, frenar o dar marcha atrás [FEC_HOR] Bocina [FEC_VES] Control del vehículo [FEC_BRA] Freno o marcha atrás [FEC_HAB] Freno de mano [FEC_CAW] Arma del vehículo [FEC_ACC] Acelerar [FEC_CCF] Configuración: [FEC_CF1] Configuración 1 [FEC_CF2] Configuración 2 [FEC_CF3] Configuración 3 [FEC_CF4] Configuración 4 [FEC_CDP] Pantalla del mando [FEC_ONF] A pie [FEC_INC] En coche [FEC_VIB] Vibración : [FEL_ENG] Inglés [FEL_FRE] Francés [FEL_GER] Alemán [FEL_ITA] Italiano [FEL_SPA] Español [FED_DBG] Menú de soluciones técnicas [FED_RID] Recargar IDE [FED_RIP] Recargar IPL [FED_PAH] Parse Heap [FED_DFL] CLos guiones::DbgBandera [FED_DLS] Gran luz blanca de solución técnica encendida [FED_SPR] Mostrar grupos de aceras [FED_SCR] Mostrar grupos de carreteras [FED_SCZ] Mostrar zonas de desechos [FED_DSR] Solicitud de solución técnica del streaming [FED_SCP] gbMostrarcolisiónPolys [PL_STAT] Estadísticas del jugador [PE_WAST] Gente que te has cargado [PE_WSOT] Gente eliminada por otros [TM_BUST] Veces detenido [GNG_WST] Miembros de la banda liquidados [DED_CRI] Criminales liquidados [PER_COM] Porcentaje completado [KGS_EXP] Kilogramos de explosivos empleados [ACCURA] Precisión [ST_WEAP] Presupuesto armamentístico [ST_PROP] Presupuesto de propiedades [ST_AUTO] Presupuesto para reparación de coches y pintura [ST_PHOT] Fotografías tomadas [ST_LOAN] Visitas de usureros prestamistas [ST_STOR] Almacenes desvalijados [ST_MOVI] Escenas de especialista [ST_PIZZ] Pizzas entregadas [ST_GARB] Recogidas de basura efectuadas [ST_ICEC] ''Helado'' vendido [TOP_SHO] Puntuación máxima en el campo de tiro [SHO_RAN] Clasificación del campo de tiro [SEAGULL] Gaviotas disparadas [PROPOWN] Propiedad en posesión [ST_TIME] Tiempo de juego [ST_FTIM] Horas de vuelo [ST_PRAN] Clasificación de los pilotos [ST_RAN0] Aprendiz [ST_RAN1] Navegante [ST_RAN2] Copiloto [ST_RAN3] Junior [ST_RAN4] Competente [ST_RAN5] Senior [ST_RAN6] As [ST_RAN7] Barón rojo [ST_DRWN] Peces alimentados [ST_FASH] Presupuesto para moda [ST_DAMA] Coste de propiedades destruidas [TM_DED] Visitas al hospital [DAYSPS] Días transcurridos en el juego [NUMSHV] Visitas a pisos francos [MXCARD] Max. Distancia de salto LOCO (pies) [MXCARJ] Max. Altura de salto LOCO (pies) [MXCARDM] Máxima distancia de salto LOCO (m) [MXCARJM] Máxima altura de salto LOCO (m) [MXFLIP] Máximas vueltas de salto LOCO [MXJUMP] Máxima rotación de salto LOCO [BUL_FIR] Balas disparadas [BUL_HIT] Aciertos [SPRAYIN] Pintura [BSTSTU] Mejor maniobra LOCA hasta ahora [INSTUN] Maniobra loca [PRINST] Maniobra loca perfecta [DBINST] Maniobra loca doble [DBPINS] Maniobra loca doble perfecta [TRINST] Maniobra loca triple [PRTRST] Maniobra loca triple perfecta [QUINST] Maniobra loca cuádruple [PQUINS] Maniobra loca cuádruple perfecta [NOSTUC] No se ha completado ninguna [NOUNIF] Saltos únicos completados [NMISON] Intentos de misión [PASDRO] Pasajeros en destino [MONTAX] Dinero conseguido con el taxi [DAYPLC] Gastos diarios provocados a la policía [CRIMRA] Tasa criminal: [STPR_1] El Malibú [STPR_2] Imprenta [STPR_3] Estudio de cine [STPR_4] Fábrica de helados [STPR_5] Salón de coches [STPR_6] Compañía de taxis [STPR_7] Astillero [SET1EN] Configuración 1. Activada [GMSAVE] Guardar partida [FEDS_TB] Volver [FEST_OO] de [FEC_TUC] Control de la torreta [FEC_RS3] Cambiar emisora de radio (Botón L3) [FEC_HO3] Bocina (Botón L3) [C_FAIL] ¡Misión de justiciero terminada! [C_ESCP] ~r~¡El sospechoso ha escapado! [C_VIGIL] ¡BONIFICACIÓN DE JUSTICIERO! [HEAL_A] Tu ~h~salud~w~ aparece en naranja en la parte superior derecha de la pantalla. [WRONGCD] Disco incorrecto. Introduce el disco correcto. [NOCD] La bandeja del disco está vacía. Por favor, inserta el disco. [OPENCD] La bandeja del disco está abierta. Por favor, cierra la bandeja de disco. [CDERROR] Error al leer el DVD de Grand Theft Auto: Vice City [RESTART] Iniciando nueva partida [GA_3] No hay más regalitos. ¡100 $ para volver a pintar! [GA_1] ¡Hala! ¡Demasiado peligroso para mi gusto! [GA_1A] Vuelve cuando no estés tan ocupado... [HELP9_C] Pulsa ~h~~k~~k~~PED_FIREWEAPON~~w~para ~h~disparar~w~ el fusil de francotirador. [TAXI2] ~r~¡Te has quedado sin tiempo! [PAGEB13] Salud entregada en el escondite [PAGEB14] Adrenalina entregada en el escondite [FESZ_CA] Cancelar [FES_NGA] Nueva partida [FES_CAN] Cancelar [FESZ_QL] Todo el progreso no guardado de tu partida actual se perderá. ¿Continuar cargando? [FESZ_QD] ¿Proceder a borrar esta partida guardada? [FESZ_QO] ¿Proceder para sobrescribir esta partida guardada? [FESZ_QR] ¿Seguro que quieres empezar una nueva partida? Todo el progreso desde la última partida guardada se perderá. ¿Continuar? [T4X4_1] Prueba de PCJ [BMX_1] Trial de tierra [BMX_2] Pista de pruebas [BMXFAIL] ~r~¡No has conseguido un nuevo récord! [BMX_REC] ~g~¡Nuevo récord establecido: ~1~! [T4X4_3] ¡AGARRADO! [MM_1] CONOCIRCUITO [T4X4_F] ~r~¡Te rajaste! ¿Demasiado duro para ti? [LANDSTK] Landstalker [IDAHO] Idaho [STINGER] Stinger [LINERUN] Linerunner [PEREN] Perennial [SENTINL] Sentinel [RIO] Río [PATRIOT] Patriot [FIRETRK] Camión de bomberos [TRASHM] Trashmaster [STRETCH] Limusina [MANANA] Mañana [INFERNS] Infernus [VOODOO] Voodoo [PONY] Pony [MULE] Mule [CHEETAH] Cheetah [AMBULAN] Ambulancia [FBICAR] FBI Washington [MOONBM] Moonbeam [ESPERAN] Esperanto [TAXI] Taxi [WASHING] Washington [BOBCAT] Bobcat [WHOOPEE] Mr Whoopee [BFINJC] BF Injection [HUNTER] Hunter [POLICAR] Policía [ENFORCR] Enforcer [SECURI] Securicar [BANSHEE] Banshee [PREDATR] Predator [BUS] Autobús [RHINO] Rhino [BARRCKS] Barracks OL [CUBAN] Cuban Hermes [HELI] Helicóptero [DODO] Dodo [COACH] Autocar [CABBIE] Taxi clásico [STALION] Stallion [RUMPO] Rumpo [RCBANDT] Bandit RC [ROMERO] Romero's Hearse [PACKER] Packer [ADMIRAL] Admiral [SQUALO] Squalo [SEASPAR] Sea Sparrow [PIZZABO] Pizza Boy [GANGBUR] Gang Burrito [TROPIC] Tropic [SPEEDER] Speeder [REEFER] Reefer [FLATBED] Flatbed [YANKEE] Yankee [CADDY] Caddy [ZEBRA] Taxi cebra [TOPFUN] Top Fun [SKIMMER] Skimmer [PCJ600] PCJ 600 [PHOENIX] Phoenix [FAGGIO] Faggio [FREEWAY] Freeway [RCBARON] Barón RC [RCRAIDE] Raider RC [GLENDAL] Glendale [OCEANIC] Oceanic [SANCHEZ] Sanchez [SPARROW] Sparrow [LOVEFIS] Love Fist [COASTG] Guardacostas [DINGHY] Dinghy [HERMES] Hermes [SABRE] Sabre [SABRETU] Sabre Turbo [WALTON] Walton [REGINA] Regina [COMET] Comet [DELUXO] Deluxo [BURRITO] Burrito [SPAND] Spand Express [MARQUIS] Marquis [BAGGAGE] Mozo de equipaje [KAUFMAN] Taxi Kaufman [COASTMA] Guardacostas Maverick [MAVERIC] Maverick [RANCHER] Rancher [FBIRANC] FBI Rancher [VIRGO] Virgo [GREENWO] Greenwood [HOTRING] Hotring Racer [BLISTAC] Blista Compact [FEST_DF] Dist. recorrida a pie (millas) [FEST_DC] Dist. recorrida en coche (millas) [FESTDFM] Distancia recorrida a pie (m) [FESTDCM] Distancia recorrida en coche (m) [TOT_DIS] Distancia total recorrida (millas) [TOTDISM] Distancia total recorrida (m) [DISTHEL] Dist. recorrida en helicóptero (millas) [DISTHEM] Distancia recorrida en helicóptero (m) [DISTBOA] Dist. recorrida en barco (millas) [DISTBOM] Distancia recorrida en barco (m) [FEST_LS] Gente salvada en una ambulancia [FEST_CC] Criminales asesinados en misión Vigilante [FEST_FE] Total de fuegos extinguidos [FEST_RP] Masacres superadas [FEST_MP] Misiones superadas [FEST_BB] Bling-bling Scramble: [FEST_H0] Mayoría de puntos de control [FEST_GC] Total de coches de bandas: [FEST_H1] Destrucción del diablo [FEST_H2] Masacre de la mafia [FEST_H3] Casino Calamity [FEST_H4] Rumpo Wrecker [USJ] ¡BONIFICACIÓN POR MANIOBRA ÚNICA! [USJ_DUN] MANIOBRA YA COMPLETADA [RATNG1] Ciudadano honrado [RATNG2] Nadie en especial [RATNG3] Basurero [RATNG4] Cleptómano [RATNG5] Vándalo [RATNG6] Chico de los recados [RATNG7] Carterista [RATNG8] Cleptómano [RATNG9] Chivato [RATNG10] Rata [RATNG11] Lince [RATNG12] Estafador [RATNG13] Timador [RATNG14] Corredor de apuestas [RATNG15] Buscavidas [RATNG16] Matón [RATNG17] Chusma [RATNG18] Bribón [RATNG19] Rufián [RATNG20] Proscrito [RATNG21] Malhechor [RATNG22] Asesino [RATNG23] Super matón [RATNG24] Matón [RATNG25] Presidiario [RATNG26] Exconvicto [RATNG27] Criminal [RATNG28] Saqueador [RATNG29] Mafioso [RATNG30] Conductor [RATNG31] Gorila [RATNG32] Asesino profesional [RATNG33] Asuntos internos [RATNG34] Agente [RATNG35] Ronin [RATNG36] Manitas [RATNG37] Asesino a sueldo [RATNG38] Asociado [RATNG39] Carnicero [RATNG40] Encargado de la limpieza [RATNG41] Asesino [RATNG42] Consigliere [RATNG43] Maleante [RATNG44] Mano derecha [RATNG45] Ejecutor [RATNG46] Teniente [RATNG47] Subjefe [RATNG48] Capo [RATNG49] Jefe [RATNG50] Negado [RATNG51] Don [RATNG52] Rey de la Jodida Ciudad [PAGE_00] . [WELCOME] BIENVENIDO A [TSCORE] GANANCIAS: ~1~ $ [PBOAT_2] { reVC update } Pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~ para disparar los cañones del barco. [HJSTAT] Distancia: ~1~.~1~m Altura: ~1~.~1~m Vueltas: ~1~ Rotación: ~1~_ [HJSTATW] Distancia: ~1~.~1~m Altura: ~1~.~1~m Vueltas: ~1~ Rotación: ~1~_ ¡qué gran aterrizaje! [ATUTOR] Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para activar o desactivar las misiones de ATS. [FEST_HA] Nivel más alto de misión de ATS [C_KILLS] CRIMINALES ASESINADOS: ~1~ [HJSTATF] Distancia: ~1~ pies Altura: ~1~ pies Vueltas: ~1~ Rotación: ~1~_ [HJSTAWF] Distancia: ~1~ pies Altura: ~1~ pies Vueltas: ~1~ Rotación: ~1~_, ¡y qué gran aterrizaje! [CRED001] ROCKSTAR NORTH [CRD001A] DIRECTOR DEL ESTUDIO [CRD001B] ANDREW SEMPLE [CRED002] PRODUCTOR [CRD002A] DIRECTOR DE DESARROLLO [CRED003] LESLIE BENZIES [CRED004] DIRECTOR DE ARTE [CRED005] AARON GARBUT [CRED006] DIRECTORES TÉCNICOS [CRED007] OBBE VERMEIJ [CRED008] ADAM FOWLER [CRED010] ANDREW DUTHIE [CRED011] CRAIG FILSHIE [CRED012] WILLIAM MILLS [CRED013] CHRIS ROTHWELL [CRD013A] IMRAN SARWAR [CRD013B] JAMES WORRALL [CRD013C] JOHN HAIME [CRED014] ESCRITO POR [CRED015] JAMES WORRALL [CRED016] PAUL KUROWSKI [CRED017] DAN HOUSER [CRED018] DISEÑO DEL PERSONAJE PRINCIPAL [CRD018A] DISEÑO DE PERSONAJES [CRED019] IAN MCQUE [CRD019A] TOKS SOLARIN [CRD019B] ALAN DAVIDSON [CRED020] ANIMACIÓN PRINCIPAL [CRED021] ALEX HORTON [CRD022A] ANIMACIÓN [CRED022] LEE MONTGOMERY [CRD022B] DUNCAN SHIELDS [CRD022C] GUS BRAID [CRED023] DISEÑO DE VEHÍCULOS [CRD023A] JOLYON ORME [CRD023B] ALAN DUNCAN [CRD024A] DISEÑO DEL VEHÍCULO PRINCIPAL [CRED024] PAUL KUROWSKI [CRED025] DISEÑO DE MAPAS [CRED026] ADAM COCHRANE [CRED027] NIK TAYLOR [CRED028] GARY MCADAM [CRED029] KEIRAN BAILLIE [CRED030] ALISDAIR WOOD [CRED031] ANDREW SOOSAY [CRD031A] STEVEN MULHOLLAND [CRD031B] WAYLAND STANDING [CRD031C] CAMPBELL J. DICK [CRD031D] DISEÑO GRÁFICO [CRD031E] STUART PETRI [CRED032] PROGRAMADOR JEFE [CRD032A] PROGRAMADORES [CRED033] ALEXANDER ROGER [CRED034] GRAEME WILLIAMSON [CRED035] BARANE CHAN [CRED036] DEREK PAYNE [CRED037] GORDON YEOMAN [CRD037A] ALAN CAMPBELL [CRD037B] MARK HANLON [CRD037C] ANDRZEJ MADAJCZYK [CRED038] PRODUCTOR JEFE MUSICAL [CRED039] CRAIG CONNER [CRED040] STUART ROSS [CRED041] INGENIERO JEFE DE AUDIO [CRED042] ALLAN WALKER [CRD041A] INGENIERO DE AUDIO [CRD041B] AUDIO [CRD042A] WILL MORTON [CRED043] PROGRAMADOR DE AUDIO [CRED044] RAYMOND USHER [CRED045] JEFE DE PRUEBAS [CRED046] CRAIG ARBUTHNOTT [CRED047] CONTROL DE CALIDAD PRINCIPAL [CRED048] NEIL CORBETT [CRED049] KEVIN WONG [CRED050] CONTROL DE CALIDAD [CRED051] DAVID BEDDOES [CRED052] DAVID WATSON [CRED053] BARRY CLARK [CRED054] ROSS SPARROW [CRED055] JAMES ALLAN [CRED056] NEIL MEIKLE [CRD056A] GEORGE WILLIAMSON [CRD056B] MATT JONES [CRD056C] ROB HARBOUR [CRD056D] TOM WHITTAKER [CRED057] SOPORTE TÉCNICO PRINCIPAL [CRED058] LORRAINE ROY [CRD057A] SOPORTE TÉCNICO [CRED059] CHRISTINE CHALMERS [CRD060A] SOPORTE DE OFICINA [CRD060B] KIM GURNEY [CRD060C] CASSIE OLIVER [CRED060] ROCKSTAR NUEVA YORK [CRED061] GABINETE DE APOYO [CRED062] SAM HOUSER [CRED063] PRODUCTOR [CRED064] DAN HOUSER [CRED065] VICEPRESIDENTE DE DESARROLLO [CRED066] JAMIE KING [CRED067] OFICIAL JEFE DE TECNOLOGÍA [CRED068] GARY J. FOREMAN [CRED069] PRODUCTOR ASOCIADO [CRED070] JEREMY POPE [CRD071A] DIRECTOR DE CONTROL DE CALIDAD [CRD072A] JEFF ROSA [CRED071] SUPERVISOR MUSICAL [CRED072] TERRY DONOVAN [CRED073] EQUIPO DE PRODUCCIÓN [CRED074] TERRY DONOVAN [CRED075] JENNIFER KOLBE [CRED076] JENEFER GROSS [CRED077] LAURA PATERSON [CRED078] JEFF CASTANEDA [CRED079] JERONIMO BARRERA [CRED080] CARLY SLATER [CRED081] JUNG KWAK [CRED082] BRIAN WOOD [CRED083] RENAUD SEBANNE [CRED084] RICHARD KRUGER [CRD084A] DANIEL EINZIG [CRD084B] JACEN BURROWS [CRD084C] LINN PR [CRD084D] DISEÑO DE PORTADA [CRD084E] STEPHEN BLISS [CRED085] KENT PAUL'S 80 NOSTALGIA ZONE [CRED086] ESCRITO POR DAN HOUSER [CRD086A] PRODUCIDO POR ADAM TEDMAN [CRED087] WWW.KENTPAUL.COM AND WWW.VICECITY.COM [CRED088] CREADO POR [CRD088A] ADAM TEDMAN [CRD088B] DAVID YU [CRD088C] JERRY LUNA [CRD088D] STUART PETRI [CRD088E] MICHAEL CARNEVALE [CRD088F] GREG LAU [CRD088G] FUTABA HAYASHI [CRED089] JEFE DE CONTROL DE CALIDAD [CRED090] CRAIG ARBUTHNOTT [CRED091] ANALISTA JEFE [CRED092] ADAM DAVIDSON [CRD092A] JOE HOWELL [CRD092B] MARC FERNANDEZ [CRED093] ANALISTA DEL JUEGO [CRED094] RICHARD HUIE [CRED095] EQUIPO DE PRUEBAS DE ROCKSTAR [CRED096] LANCE WILLIAMS [CRED097] JOE GREENE [CRED098] BRIAN PLANER [CRD098A] ELIZABETH SATTERWHITE [CRD098B] JAMEEL VEGA [CRD098C] MIKE HONG [CRED099] LEE CUMMINGS [CRED100] HISTORIA [CRED101] JAMES WORRALL [CRED102] DAN HOUSER [CRED103] ADAM TEDMAN [CRED104] PAUL YEATES [CRED105] JENEFER GROSS [CRED106] LAURA PATERSON [CRED107] SECUENCIAS [CRED108] ESCRITAS POR DAN HOUSER Y JAMES WORRALL [CRED109] DIRECCIÓN DE AUDIO DE DAN HOUSER Y NAVID KHONSARI [CRED110] PRODUCIDAS POR JAMIE KING [CRD110A] SELECCIÓN DE ACTORES: JAMIE KING, SEAN MACALUSO [CRED111] REPARTO [CRD111A] PERSONAJES DE REPARTO [CRED112] TOMMY VERCETTI - RAY LIOTTA [CRED113] KEN ROSENBERG - WILLIAN FICHTNER [CRED114] SONNY FORELLI - TOM SIZEMORE [CRED115] STEVE SCOTT - DENNIS HOPPER [CRED116] AVERY CARRINGTON - BURT REYNOLDS [CRED117] RICARDO DÍAZ - LUIS GUZMÁN [CRED118] LANCE VANCE - PHILIP MICHAEL THOMAS [CRED119] CORONEL JUAN CORTEZ - ROBERT DAVI [CRED120] UMBERTO ROBINA - DANNY TREJO [CRED121] PHIL CASSIDY - GARY BUSEY [CRED122] MITCH BAKER - LEE MAJORS [CRED123] MERCEDES CORTEZ - FAIRUZA BALK [CRED124] KENT PAUL - DANNY DYER [CRED125] JEZZ TORRENT - KEVIN MCKIDD [CRED126] SUPERVISOR TAXIS - DEBORAH HARRY [CRED127] CANDY SUXX - JENNA JAMESON [CRED128] BJ SMITH - LAWRENCE TAYLOR [CRED129] TÍA POULET - YOUREE CLEOMILI HARRIS [CRED130] PROVEEDOR - ARMANDO RIESCO [CRED131] COUGAR - BLAYNE PERRY [CRED132] HILARY - CHARLES TUCKER [CRED133] CONGRESISTA ALEX SHRUB - CHRIS LUCAS [CRED134] VIEJO KELLY - GEORGE DICENZO [CRD134A] CAM JONES - GREG SIMS [CRD134B] PSICÓPATA - HUNTER PLATIN [CRD134C] MAUDE LA VENDEDORA DE HELADOS - JANE GENNARO [CRD134D] JETHRO - JOHN ZURHELLEN [CRD134E] GONZÁLEZ - JORGE PUPO [CRD134F] DWAYNE - NAVID KHONSARI [CRD134G] DICK - PETER MCKAY [CRD134H] MIKE EL MATÓN & CHICO PORNO - ROBERT CIHRA [CRD134I] PERCY - RUSSELL FOREMAN [CRED135] CAPTURA DE MOVIMIENTOS [CRED136] ANIMACIÓN DE [CRD136A] DIRECCIÓN TÉCNICA DE ALEX HORTON [CRED137] DIRIGIDO POR [CRD137A] DIRIGIDO POR NAVID KHONSARI [CRED138] PRODUCIDO POR [CRD138A] PRODUCIDO POR JAMIE KING [CRD138B] RENAUD SEBBANE [CRED139] GRABADO EN PERSPECTIVE STUDIOS, BROOKLYN [CRED140] ACTORES DE CAPTURA DE MOVIMIENTOS [CRD140A] BLAYNE PERRY [CRD140B] JONATHON SALE [CRD140C] CHARLES TUCKER [CRD140D] EDDIE MARRERO [CRD140E] WILLIAM MCCALL [CRD140F] JORGE PUPO [CRD140G] ROBERT JACKSON [CRD140H] TARA RADCLIFFE [CRD140I] JENIFER GAMBETESE [CRD140J] KRIS ACHEVARRIA [CRD140K] ALI ORDOUBADI [CRD140L] KAHLEEM POOLE [CRED141] DIÁLOGOS DE LOS PEATONES [CRD141A] ESCRITOS POR DAN HOUSER, MARC FERNÁNDEZ, GILLIAN TELLING Y NAVID KHONSARI [CRD141B] CON LA AYUDA DE JEREMY POPE, LANCE WILLIAMS, Y JENNY JEMISON [CRED142] ESCRITO POR [CRD142A] DAN HOUSER Y JAMES WORRALL [CRED143] DIRIGIDO POR DAN HOUSER, CRAIG CONNER, MARC FERNÁNDEZ, Y ALLAN WALKER [CRED144] PRODUCIDO POR RENAUD SEBANNE [CRED145] PEATONES [CRED146] ADAM DAVIDSON, ADAM WATKINS, ALEJANDRO K. BROWN, ALEX ANTHONY SIOUKAS, ALEX GARCÍA, [CRED147] ALICE SALTZMAN, ALISON CIHRA, AMY SALIMA, AMY SALZMAN, ANDREA VIDELA, ANTHONY ATTI, [CRED148] ANTHONY RIVERA, BIJAN SHAMS, BLAYNE PERRY, BRETT BISOGNO, BREYE MATA, BRIAN PANEN, [CRED149] BROCK VODER, CAREY BERTINI, CHARISSE LAMBERT, CHRIS DIFAT, CHRIS REISENBERGER, [CRED150] CHRISTOPHER BRODAY, CHRISTOPHER CARRO, CYNTHIA GREENE, DAMARIES LÓPEZ, DAN LEE, [CRED151] DAN SCHNEIDER, DAN TOYAMA, DAVID DEAN CHALTFIELD, JR., DAVID HARRISON, DAVID WILEY, [CRED152] DEBORAH COLLINS, DEBRANDA CHANEY-GILES, DEMETRA KOUKOULAS, DENISE ROSADO, [CRED153] DEVIN BENNETT, DEVIN WINTERBOTTOM, DORIS WOO, DOUGLAS HARRISON, DUNCAN COUTTS, [CRED154] DUPE AJAYI, EDWIN AVELLANEDA, ELIZABETH HOWELL, ELIZABETH SATTERWHITE, ERIC NAGLE, [CRED155] ESTEBAN KARPLUS, F. FONT, FUTABA HAYASHI, GENE HILGREEN, GERALD COSGROVE, [CRED156] GERARD LUNA, GILLIAN TELLING, GREGG CARLUCCI, GREGORY CLERVOIX, GREGORY SCHWEIZER, [CRED157] HADLEY TOMICKI, J. ROSSETT, JAMEEL VEGA, JASON JONES, JEFF ROSA, JENNIFER JEMISON, [CRED158] JEREMY TAGGERT, JESSICA RIDER, JOSEPH GREENE,JOSEPH HOWELL, KATE DUKICH, [CRED159] KEL O'NEILL, KEVIN HOPKINS, LADAWN JAMES, LANCE WILLIAMS, LAURA BUBBLES, [CRED160] LAURA PATTERSON, LEE CUMMINGS, LETICIA L. YOUNG, LINDSAY KENNEDY, LISA ORITZ, [CRED161] LORNA JORDAN, LUCIO AMADIO, MARCO FERNÁNDEZ, MARIKO TANAKA, MARLON MATTHEWS, [CRED162] MARY TELLING, MASAYOSHI MITSUYAMA, MATTHEW CHUNG, MAX ALLSTADT, MAX BOGDANOV, [CRED163] MELISSA ÁLVAREZ, MICHAEL MAY, MICHAEL ROTHSTEIN, MIGUEL VIDAL, MIKE FEDERLINE, [CRED164] NATALIE DESCALZO, N'GAI MEMBERS, NICOLAS MALLO, NOELLE SADLER, NORBERT MORIVAN, [CRED165] OSWALD GREENE, JR., PETER MCKAY, PETER APPEL, PRESTON SAVARESE, RAFAEL GONZÁLEZ, [CRED166] RANDY JOHNSON, REY CONCEPCIÓN, RICHARD KROGER, ROB TIBBS, ROBERT JACKSON, [CRED167] ROBERT SCHULER, ROSS A. MCINTYRE, RUSSELL FOREMAN, RUTH NÚÑEZ, SALVADORE SUAZO, [CRED168] SAM WHITE, SANTOS GONZÁLEZ, SCOTT SMITH, SEYMOUR FRAILMAN, SPELMAN BRAUMAN, [CRED169] STEPHANIE TELLING, STEVE KNEZEVICH, STEVE ROBERT, SUMIKO YASUDA, SUSAN LEWIS, [CRED170] SYLVIA COLACIOS, TOMOKO MIYAZAKI, TRON, VERDEL HALE, YVES MONDESIR, ZENO LEINFELDER, [CRED171] DAVID BEDDOES, CHRISTINE CHALMERS, BARRY CLARK, NEIL CORBETT, KIM GURNEY, NEIL MEIKLE, [CRED172] CASSIE OLIVER, LORRAINE ROY, DAVID WATSON, KEVIN WONG, WILL MORTON [CRED175] ADAM DAVIDSON [CRED176] LANCE WILLIAMS [CRED177] NEIL MCCAFFREY [CRED178] LAURA PATERSON [CRED179] REY CONCEPCIÓN [CRED180] CHARLES HEROLD [CRED181] ANDREW GREENWALD [CRED182] JAMES MIELKE [CRED183] PETER SUCIU [CRED184] ALEX ODULIO [CRED185] DON NKRUMAH [CRED186] KENDALL PITTMAN [CRED187] SAL SUAZO [CRED188] EREK MATEO [CRED189] CHRIS DIFATE [CRED190] LEILA MILTON [CRED191] DARREN ZOLTOWSKI [CRED192] VIRGINIA SMITH [CRED193] KEVIN CASSIN [CRED194] JASON SHIGEMORI [CRED195] KELLY KINSELLA [CRED196] MOLLIE STICKNEY [CRED197] STANTON SARJEANT [CRED198] LAURA WALSH [CRED199] MARK GARONE [CRED200] JOANNA SLY [CRED201] ELIZABETH HOWELL [CRED202] ANA HÉRCULES [CRED203] SHIRLEY IRICK [CRED204] KASHONA FIELDS [CRED205] JOEL M. LILJE [CRED206] JOHN DIBENEDETTO [CRED207] NANCY GILES [CRED208] RYAN CROY [CRED209] JENNIFER KOLBE [CRED210] LIAM BURKE [CRED211] SIGRID PREISSL [CRED212] ANITA FITZSIMONS [CRED213] PHILIPPA RASELLI [CRED214] WIL QUESNEL [CRED215] FALKO BURKERT [CRED216] SARA SEWELL [CRED217] EMISORAS DE RADIO Y MÚSICA [CRED218] ASESOR MUSICAL [CRD218A] HEINZ HENN [CRD218B] STUART ROSS [CRED219] COORDINADOR DE LA BANDA SONORA [CRED220] TERRY DONOVAN [CRED221] PRODUCTOR DE ROCKSTAR GAMES [CRED222] DAN HOUSER Y LAZLOW [CRED223] PRODUCTOR DE ROCKSTAR NORTH [CRED224] CRAIG CONNER [CRED225] ALLAN WALKER [CRED226] LAZLOW [CRED227] DJ BANTER E IMÁGENES [CRED228] ESCRITO POR DAN HOUSER Y LAZLOW [CRED229] FLASH FM [CRD229A] TONI-MARIA CHAMBERS [CRD229B] VOCES FICTICIAS Y PRODUCCIÓN-JEFF BERLIN [CRED230] GRACIAS ESPECIALES A [CRED231] TOMMY MOTTOLA, [CRED232] MICHELLE ANTHONY, [CRED233] STEVE BARNETT, [CRED234] CHUCK FLECKENSTEIN, [CRED235] RITA LIBERATOR [CRED236] MARTIN & CLAIRE LOGAN [CRED237] SANDRA HUTTON [CRED238] CHRISTINE DAVIDSON [CRED239] ALAN, RED & BIGFOOT [CRED240] LE T [CRED241] COLIN DONALD [CRED242] KERRY STALLWOOD [CRED243] ALAN MCGREGOR [CRED244] CHRIS MORTON [CRED245] EMIL BUSSE [CRED246] EMILY BAILLIE [CRED247] KEVIN ARCHIBALD [CRED248] MORAG KERR [CRED249] CATH WALKER [CRED250] ISO BAR [CRED251] WATERLINE [CRED252] NEWS CAFE [CRD251A] THE POND [CRD252A] PIVO [CRED253] BUDGET VIDEO RENTALS [CRED254] LORNA'S SCOOTER [CRED255] GARETH MURFIN [CRED256] ARTE ADICIONAL [CRED257] TONY PORTER [CRED258] CRAIG MOORE [CRED259] ANIMACIÓN DE SINCRONIZACIÓN LABIAL DE LAS SECUENCIAS [CRED260] COSGROVE HALL FILMS [CRED261] PRODUCTOR - OWEN BALLHATCHET [CRED262] ANIMADOR SENIOR - JON TURNER [CRED263] ANIMADORES - RICHARD DRUMM [CRED264] DAVE BROWN [CRED265] MAIR THOMAS [CRED266] PRASHANT PATEL [CRED267] ASESOR TECNOLOGÍA DE AUDIO [CRED268] RIK EDE PARA GAMESOUND LTD. [CRED269] SOPORTE DTS INTEGRATION [CRED270] TED LAVERTY DE DTS [CRED271] CHRIS GREER DE DTS [CRED272] JASON PAGE DE SCEE [CRED273] INVESTIGACIÓN Y ANÁLISIS [CRED274] VROCK [CRED275] DJ: LAZLOW COMO ÉL MISMO [CRED276] VOZ-JOE KELLY [CRED277] PRODUCCIÓN-JONATHAN HANST [CRED278] WAVE 103 [CRED279] DJ: ADAM FIRST-JAMIE CANFIELD [CRED280] VOZ-JEN SWEENEY [CRED281] PRODUCCIÓN-JONATHAN HANST [CRED282] FEVER 105 [CRED283] DJ: OLIVER 'LADYKILLER' BISCUIT-JULIUS DYSON [CRED284] VOZ MASCULINA-ED MCMANN [CRED285] VOZ FEMENINA-SHAWNEE SMITH [CRED286] PRODUCCIÓN- LISTEN KITCHEN [CRED287] EMOTION 98.3 [CRED288] DJ: FERNANDO- FRANK CHAVEZ [CRED289] VOZ-JEN SWEENEY [CRED290] PRODUCCIÓN-JONATHAN HANST [CRED291] RADIO ESPANTOSO [CRED292] DJ: PEPE-TONY CHILRODES [CRED293] WILDSTYLE [CRED294] DJ: MISTER MAGIC AS HIMSELF [CRED295] VOZ-FRANK SILVESTRO [CRED296] PRODUCCIÓN-LAZLOW [CRED297] KCHAT [CRED298] ESCRITO POR DAN HOUSER Y LAZLOW [CRED299] PRODUCIDO Y EDITADO POR LAZLOW [CRED300] DJ AMY SHECKENHAUSEN -LEYNA WEBER [CRED301] JEZ TORRENT-KEVIN MCKIDD [CRED302] MANDY -COLLEEN CORBETT [CRED303] MICHELLE CARAPADIS-MARY BIRDSONG [CRED304] MR.ZOO-CARL DOWLING [CRED305] GETHSEMANEE-LYNN LIPTON [CRED306] CLAUDE MAGINOT-JOHN MAUCERI [CRED307] BJ SMITH-LAWRENCE TAYLOR [CRED308] THOR-FRANK FAVA [CRED309] PERSONAS LLAMANDO A LA RADIO [CRED310] COUZIN ED, JOSH CLARK, JASON BUHRMESTER, JUAN ALLER, WAYNE OLIVER, SUSAN LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, KEITH BROADAS [CRED311] LEWIS, GILLIAN TELLING, TOM MURRAY, MIKE FERRANTE SR., EMMANUEL GOLDSTEIN, [CRED312] DAN HOUSER, NICK MANDELOS, GERRY COSGROVE, MIKE PALERMO, PORKCHOP, [CRED313] KEITH BROADAS [CRED314] VCPR [CRED315] ESCRITO POR DAN HOUSER Y LAZLOW [CRED316] PRODUCIDO POR LAZLOW [CRED317] MAURICE CHAVEZ-PHILLIP ANTHONY RODRIGUEZ [CRED318] JONATHAN FREELOADER- PATRICK OLSEN [CRED319] MICHELLE MONTANIUS-KELLY GUEST [CRED320] REP. ALEX SHRUB- CHRIS LUCAS [CRED321] CALLUM CRAYSHAW- SEAN MODICA [CRED322] JOHN F. HICKORY- LJ GANSEN [CRED323] PASTOR RICHARDS- DAVID GREEN [CRED324] JAN BROWN- MAUREEN SILLIMAN [CRED325] BARRY STARK- RENAUD SEBBANE [CRED326] JENNY LOUISE CRAB- MARY BIRDSONG [CRED327] KONSTANTINOS SMITH- KONSTANTINOS.COM [CRED328] JEREMY ROBARD-PETER SILVESTRO [CRED329] ANUNCIOS DE RADIO [CRED330] ESCRITOS POR DAN HOUSER Y LAZLOW [CRED331] PRODUCIDOS POR LAZLOW [CRED332] JINGLES ADICIONALES PRODUCIDOS POR CRAIG CONNER [CRED333] VOCES EN LOS ANUNCIOS: [CRED334] ADAM DAVIDSON, ALEX ANTHONY, ALICE SALTZMAN, AMY SALZMAN, KATE DUKICH, [CRED335] ARAN RONICLE, BARB JONES, BEN KRECH, BRIAN THOMAS, BROCK YODER, CHRIS [CRED336] FERRANTE, CRAIG CONNER, DAVE RYAN, DAVID GREEN, DORIS WOO, DOUGLAS [CRED337] HARRISON, ED MCMANN, FRANK CHÁVEZ, FRANK FAVA, GENE HILGREEN, GREG [CRED338] SCHWEIZER, HUNTER PLATIN, JAMES FERRANTE, JEFF BERLIN, JEFF ROSA, JOE KELLY, [CRED339] JOHN MAUCERI, JOSH CLARK, JULIE WEMYSS, KEVIN STRALEY, KIM GURNEY, LANCE [CRED340] WILLIAMS, LAURA PATERSON, LAZLOW, LISA ORTIZ, LORNA JORDAN, LUCIEN JONES, [CRED341] MAUREEN SILLIMAN, MIKE FERRANTE JR., PETE GUSTIN, PETER SILVESTRO, RAFF [CRED342] CROLLA, RANDY JOHNSON, RICHARD KRUGER, RON REEVE, SHELLEY MILLER, SKY, TJ ALLARD [CRD344A] AUDIO GRABADO EN DIGITAL ARTS STUDIOS, [CRED344] NYC, TRACK 9 STUDIOS, NYC, [CRED345] WEDDINGTON MULTIMEDIA, LOS ANGELES, [CRD345A] SYNC SOUND, NYC Y RADIO LAZLOW, LONG ISLAND. [CRED346] GRACIAS A AXEL ERICSON Y WON LEE EN DIGITAL ARTS, PAUL VÁSQUEZ EN TRACK 9 STUDIOS, JOHN BOWEN Y JOHN HASSLER EN SYNC SOUND [CRED347] MARK LLOYD [CRED348] TIM BATES [CRED349] KIT BROWN [CRED350] ANDY MASON [CRED351] PHIL DEANE [CRED352] PHIL ALEXANDER [CRED353] MATT HEWITT [CRED354] DENBY GRACE [CRED355] ANTOINE CABROL [CRED356] JONOTHAN STONES [CRED357] MIKE BLACKBURN [CRED358] TIM MCGAFF [CINCAM] Cámara cinemática [RC4] MASACRES RUMPO [LEGAL] ~g~¡Elimina la amenaza criminal! [GA_2] Nuevo motor y trabajo de pintura. ¡Los policías no te reconocerán! [HELP15] Cuando vayas a pie, pulsa ~h~~k~~PED_LOOKBEHIND~~w~ para ~h~mirar detrás~w~. Utiliza el ~h~joystick analógico derecho~w~ para ~h~mirar a tu alrededor~w~. [FEC_LB4] Mirar detrás (botón R3) [PERPIC] Objetos ocultos encontrados [CO_ONE] Objeto oculto ~1~ de ~1~ [GA_21] No puedes almacenar más coches en este garaje. [CHEAT1] Truco activado [CHEAT2] Truco de arma [CHEAT3] Truco de salud [CHEAT4] Truco de blindaje [CHEAT5] Truco de nivel de se busca [CHEAT6] Truco de dinero [CHEAT7] Truco de tiempo [USJ_ALL] ¡TODAS LAS MANIOBRAS ÚNICAS COMPLETADAS! [JAN] Ene [FEB] Feb [MAR] Mar [APR] Abr [MAY] May [JUN] Jun [JUL] Jul [AUG] Ago [SEP] Sep [OCT] Oct [NOV] Nov [DEC] Dic [DEFDT] --:---:---- --:--:-- [BONUS] ~g~BONIFICACIÓN ~1~ $ [HORN1] Pulsa ~h~~k~~VEHICLE_HORN~~w~para tocar el ~h~claxon. [HORN2] Pulsa ~h~~k~~VEHICLE_HORN~~w~para tocar el ~h~claxon. [HORN3] Pulsa ~h~~k~~VEHICLE_HORN~~w~para tocar el ~h~claxon. [FEC_EXV] Entrar y salir de vehículo [TAXI_M] 'TAXISTA' [COP_M] 'JUSTICIERO' [FIRE_M] 'BOMBERO' [AMBUL_M] 'ATS' [HJ_IS] BONIFICACIÓN POR ACROBACIA: ~1~ $ [HJ_PIS] BONIFICACIÓN POR ACROBACIA PERFECTA: ~1~ $ [HJ_DIS] BONIFICACIÓN POR ACROBACIA DOBLE: ~1~ $ [HJ_PDIS] BONIFICACIÓN POR ACROBACIA DOBLE PERFECTA: ~1~ $ [HJ_TIS] BONIFICACIÓN POR ACROBACIA TRIPLE: ~1~ $ [HJ_PTIS] BONIFICACIÓN POR ACROBACIA TRIPLE PERFECTA: ~1~ $ [HJ_QIS] BONIFICACIÓN POR ACROBACIA CUÁDRUPLE: ~1~ $ [HJ_PQIS] BONIFICACIÓN POR ACROBACIA CUÁDRUPLE PERFECTA: ~1~ $ [FESZ_LS] Carga completada con éxito. [HELI_1A] Prueba tus habilidades con el Sparrow, comprueba lo rápido que puedes completar el circuito. [HELI_1B] ¡Circuito completado! ~1~ $ [HELIODD] Trabajos esporádicos del helicóptero [LAW] LAS MISIONES DEL ABOGADO [LAW1_1] ~g~Consigue unos trapos nuevos en la tienda de ropa de Rafael. [LAW4_6] ¡Quemad la dirección! [LAW4_7] ¡Matad a los jefes! [LAW4_8] Luchad, luchad, luchad, luchad. [LAW4_9] ¡Más vacaciones, menos trabajo! [LAW4_11] ¡Luchad! ¡Luchad! ¡Luchad! ¡Luchad! [LAW4_12] ¡Viva la revolución! [GENERAL] LAS MISIONES DEL CORONEL [GEN3_4] Tommy Vercetti. Vamos... [GEN3_13] ¿Cuál es tu problema, tío? ¡Sube al tejado al otro lado del patio antes de que aparezcan! [GEN3_17] ¡Mierda! ¿Intentas matarme? [GEN3_21] ~g~¡Tiene el dinero de Díaz! ¡Ve tras él y recupéralo! [GEN3_24] ~r~¡Díaz murió! ¡Has fracasado al protegerle! [GEN3_26] ~r~¡Le pegaste un tiro a Díaz! [GEN3_27] ~r~¡Disparaste a los guardaespaldas de Díaz! [GEN3_31] ~g~Ahora ve al punto de entrega y cuida de Díaz. [GEN3_32] ~g~Ve a tu posición elevada en el tejado del edificio opuesto a donde está Lance. [COKE] LAS MISIONES DEL BARÓN DE LA NIEVE [COK1_3] ¡Espero que te caigas y te rompas el cuello! [COK1_6] Estoy cansado de estos mamones. [COK2_7] ¿Veis esos marcadores chicos? ¡Probad a dar a las luces! [COK2_10] Desde luego eres mejor disparando que charlando. [COK2_11] Gracias. Eres un verdadero encanto. [COK2_12] Lo sé, Tommy. [COK2_18] ¿Te gusta Kenny Loggins? [COK2_19] Dios, ¡me encanta este disco! [COK2_26] ~r~¡Mataste a Lance! [COK3_1] ¡No dispares tío! [COK3_2] ¿Qué hay ahí? [COK3_3] Se está llevando el barco. Gorrón. [COK3_4] ¡Ayuda! ¡Un tipo me está robando el barco, tío! [COK4_W] ¡Uugghh! Ése era el último. [COK4_X] Voy a arrancarlo [COK4_Y] Creo que tenemos algunos amigos nuevos. [COK4_2] Sí. [COK4_6] ¿A dónde crees que vamos? [COK4_7] ¿Nos hemos perdido? [COK4_8] ¡Tenemos competencia! [COK4_9] ¡Elimínales! [COK4_9A] ¡Es hora del baile de Lance Vance! [COK4_10] ¡Ya son astillas! Y comida para los peces. [COK4_11] ¡Lo conseguimos! Esos otros barcos no son de la clase VIP. [COK4_17] ¡Se están desesperando! [COK4_18] ¡Mis malditos pies están mojados! ¡NOS ESTÁ ENTRANDO AGUA! [COK4_21] ¡Subiendo el puente! [COK4_22] ¡Salta, está apunto de estallar! [COK4_23] Buen disparo. [COK4_29] ~r~¡Mataste a Lance! [ASS1_6] ¡Adelante Tommy, yo estaré bien! [ASS1_7] ¡Comeos esto, asesinos hijos de puta! [ASS1_8] ¡Me han dado! [ASS1_9] ¡Te tengo cubierto, Tommy! [ASS1_10] Eh, este seto es precioso. [ASS1_11] Tommy, ¿puedo tener una habitación con vistas a la bahía? [ASS1_12] Unos techos altos muy bonitos... [ASS1_3] ¡Lance! ¡Necesito que me cubras! [ASS1_4] ¡Díaz debe de estar dentro! [ASS1_5] ¡Lance! [TAXWAR] MISIONES DE LA GUERRA DE TAXIS [NOTAXI] ~g~Necesitas un Taxi Kaufman para activar esta misión. [TAXW1_5] ~g~¡Necesitas estar en un taxi Kaufman! [TAX2_4] Adelante, Tommy. [TAX2_5] Dale una buena paliza. [TAX2_6] Ni siquiera tiene licencia. [TAX2_7] Malditos servicios de limusina. [TAXW3_1] ~g~Ve y recoge a Mercedes. [RACE1] ~g~3..2..1.. ¡VAMOS VAMOS VAMOS! [RACE2] ~g~3 [RACE3] ~g~2 [RACE4] ~g~1 [RACE5] ~g~¡ADELANTE! [FIRST] ~b~1 [SECOND] ~b~2 [THIRD] ~b~3 [FOURTH] ~b~4 [RACETM] ~b~TIEMPO DE CARRERA: ~1~:~1~ [RACETM2] ~b~TIEMPO DE CARRERA: ~1~:0~1~ [RACEFA] ~r~¡Has fracasado en ganar la carrera! [HOTRNG] HOTRING [BLODRNG] BLOODRING [DIRTRNG] DIRTRING [TEX1_5] ~r~¡Se escapó! [SEG3_1] TIEMPO: [SEG3_2] ~g~Ve a la camioneta que contiene el Helicótero RC y las bombas por control remoto con temporizador. [SEG3_3] ~g~Debes emplear el Helicótero RC para transportar 4 bombas hasta 4 objetivos en lugar del edificio. [SERG3_5] ~g~Sólo puedes llevar una bomba en cada ocasión y no puedes recoger bombas colocadas con éxito. [SEG3_7] ~g~Una vez que hayas soltado con éxito la PRIMERA bomba sobre una zona del objetivo, el cronómetro de detonación se pondrá en marcha; de modo que tendrás que soltar todas las bombas dentro de este período de tiempo. [SEG3_8] ~g~Las 4 bombas deben estar colocadas en las 4 zonas objetivo para superar la misión y demoler el edificio. [SEG3_9] ~g~¡Alcanzaste el objetivo! Quedan 3. [SEG3_10] ~g~¡Alcanzaste el objetivo! Quedan 2. [SEG3_11] ~g~¡Alcanzaste el objetivo! ¡Sólo queda 1! [SEG3_12] ~r~¡Fallaste el blanco! ¡Ve a por una bomba! [SEG3_13] ~g~Deja caer la bomba en una zona del objetivo. [SEG3_14] ~r~Te quedaste sin tiempo y fracasaste en demoler el edificio. [SEG3_15] ~r~¡Tu Helicótero RC ha sido destruido! ¿Cómo vas a transportar las bombas ahora? [AVERY] MISIONES DE AVERY [ASM] MISIONES DE ASESINO [ASM_1] MISIÓN DE ASESINO 1 [ASM1_1] ~g~Sr. Teal, su ayuda en la eliminación de los forasteros fue inestimablemente buena para el negocio. Tengo más trabajo para usted y más próximo a la ''intervención''. Encontrará su siguiente trabajo pegado con cinta adhesiva bajo el teléfono. [ASM1_2] ~g~Ve a la cabina telefónica que hay frente al centro comercial de Washington. [ASM1_3] ~g~Carl Pearson, un repartidor de pizza. No debe finalizar sus entregas. [ASM1_4] ~g~Elimina al repartidor de pizza antes de que complete sus entregas. [ASM_2] MISIÓN DE ASESINO 2 [ASM_3] MISIÓN DE ASESINO 3 [ASM3_1] ~g~Ve a coger el arma que el Sr. Black ha dejado para ti. [ASM3_2] ~g~No te acerques demasiado al objetivo o podría verte. [ASM3_3] ~g~Para un trabajo más rápido, colócate en lugares cercanos a su ubicación y evita ser visto. Esto te proporcionará una buena posición para eliminarles. [ASM3_4] ~g~¡Te ha visto! ¡Mejor elimínale de cualquier modo que puedas! [ASM3_5] ~g~Marcus Hammond está situado cerca de los anuncios de Washington. [ASM3_6] ~g~Franco Carter está situado en el DBP de Seguridad, cerca de Ocean Drive. [ASM3_7] ~g~Dick Tanner está cerca de la joyería de Vice Point. [ASM3_8] ~g~Nick Kong está colocado cerca de Washington Beach. [ASM3_9] ~g~El conductor está situado en Washington. [ASM3_10] ~r~Fallaste en matarles a todos. [ASM_4] MISIÓN DE ASESINO 4 [ASM4_1] ~g~Ve a por el rifle que te han dejado entre el follaje que hay en las cercanías de la terminal del aeropuerto. [ASM4_2] ~g~No falles tu blanco o podrías alertar a sus guardaespaldas, y recuerda mantener las distancias para que no te detecte. [ASM4_3] ~g~Vigila a la mujer de la galería que hay sobre los mostradores de facturación, dentro de la terminal del aeropuerto. PERO NO LA MATES. [ASM4_4] ~g~Mata al hombre al que ella dé el maletín, pero sólo DESPUÉS DE QUE ÉSTE LO RECOJA. Entonces recupera el maletín y llévalo a Ammu-Nation en el centro de la ciudad. [ASM4_5] ~g~¡Consigue el maletín! [ASM4_6] ~g~Lleva al maletín a Ammu-Nation en el centro de la ciudad. [ASM4_7] ~r~¡Estúpido, has matado a la mujer! [ASM4_8] ~r~¡El blanco te oyó disparar el arma! ¡El trato queda anulado! [ASM4_9] ~r~¡El blanco ha embarcado en su vuelo! [ASM4_11] ~r~¡El blanco te ha visto! ¡El trato queda anulado! [ASM4_13] ~g~Te ha visto y está huyendo, ¡atrápale y consigue el maletín! [ASM4_14] ~g~La barra de distancia en la parte superior derecha de la pantalla te proporciona una indicación de lo cerca que estás de tu blanco. No permitas que se llene o éste te verá. [ASM_5] MISIÓN DE ASESINO 5 [KICK] ARRANQUE [KICK1_3] ~g~Número de veces que pusiste el pie: ~1~ [KICK1_4] ~g~Tiempo de penalización: ~1~ segundos [BANK] MISIONES DEL ATRACO AL BANCO [BANK1] MISIÓN DE ATRACO AL BANCO 1 [BANK2] MISIÓN DE ATRACO AL BANCO 2 [BJM2_21] ~g~Acierta a tantos blancos como puedas mientras te quede munición. [BANK3] MISIÓN DE ATRACO AL BANCO 3 [BJM3_1] ~g~Consigue un coche rápido y ve a la parrilla de salida. [BNK4_2A] Los chicos hicieron un trabajo genial con este pequeño. [BNK4_3G] ¡Oh, mierda, ahora los polis andan tras nosotros! [BNK4_3H] Y ni siquiera hemos llegado. [BNK4_3K] Primero tendremos que despistar a los polis... [BNK4_3L] Joder, Tommy, ¿intentas matarnos a todos? [BNK4_3N] ¡Todo lo que me importa acaba destrozado! [BNK4_26] ¡Maldición! ¡Aquí vienen! [BNK4_32] ¡Usa los explosivos para abrir las cajas de depósito! [BNK4_36] ¿Dónde está Cam? [BNK4_37] Es historia... [BNK4_38] Ése era el último. ¡Vamos! ¡Vamos! ¡Vamos! [BNK_39] ¡Mierda! ¿Dónde está Hilary? [BK4_40A] ¡Ya le daré yo miedo al abandono! [BNK4_42] ¡Eh, tíos! ¡Subid! ¡Yo os cubro! [BNK4_43] ¡Ya cubro yo nuestros culos, tú conduce! [BNK4_44] ¡Lo conseguimos! ¡Somos ricos! ¡Ricos! [BNK4_45] Es una pena que Cam no lo consiguiese, ¡era un buen tipo! [BNK4_46] Sí. Pero aún así... ¡más tajada para nosotros! [BNK4_47] ¡Claro que sí! ¡Sí! [BNK4_48] Tommy, ¿te gustaría un masaje? [BNK4_49] ¡Hola, Mercedes! Sí, estoy un poco tenso... [BNK450A] ¿Qué te dije, Tommy? ¿Qué te dije? Maldito SWAT, será mejor que tengas cuidado cuando Kent Paul esté en la ciudad. [BNK450B] Vamos, dame una tajada más grande, colega, venga. Tengo que comprarme ropa nueva. [BNK4_51] Yo creo que tienes buen aspecto. [KENT] MISIONES DE KENT PAUL [KENT1] MISIÓN DE KENT PAUL 1 [COUNT] MISIONES DE FALSIFICACIÓN [COUNT1] MISIÓN DE FALSIFICACIÓN 1 [COUNT2] MISIÓN DE FALSIFICACIÓN 2 [BIKE] LAS MISIONES DE LA PANDILLA DE MOTEROS [BIKE1] MISIÓN DE MOTORISTA 1 [BIKE2] MISIÓN DE MOTORISTA 2 [BIKE3] MISIÓN DE MOTORISTA 3 [GOAWAY1] Vuelve cuando hayas terminado con las misiones de la banda haitiana. [HAIT] LAS MISIONES DE LA BANDA HAITIANA [HAIT1] MISIÓN HAITIANA 1 [HAIT2] MISIÓN HAITIANA 2 [HAIT3] MISIÓN HAITIANA 3 [HAM3_6] ~g~Utiliza el rifle de francotirador que te he dejado para cumplir tu tarea. [ROCK] LAS MISIONES DE LA BANDA DE ROCK [ROK1_4] ~g~De acuerdo, creo que esto es lo que andabais buscando... [ROK1_1E] ~g~¡Costará más de lo que tienes! [ROK1_1F] ~g~Vuelve cuando tengas el dinero. [RBM2_6] ~g~¡Guau! ¡Ella es un tío, detenle! [ROCK3] MISIONES DE LA BANDA DE ROCK 3 [RBM3_5] ~g~Lleva a los Love Fist al escenario. [CUBANM] LAS MISIONES DE LA BANDA CUBANA [CUBAN1] MISIÓN CUBANA 1 [CUBAN2] MISIÓN CUBANA 2 [CUB2_10] ~r~¡Se supone que debes matar a haitianos, no a cubanos! [CUBAN3] MISIÓN CUBANA 3 [CUBAN4] MISIÓN CUBANA 4 [PROT] MISIONES DE PROTECCIÓN [PORN] MISIONES PORNO [PORN1] MISIÓN PORNO 1 [POR1_03] ~r~¡Candy está muerta! [PORN2] MISIÓN PORNO 2 [PORN3] MISIÓN PORNO 3 [PORN4] MISIÓN PORNO 4 [PHIL] MISIONES DE PHIL [PHIL1] MISIÓN DE PHIL 1 [PHIL2] MISIÓN DE PHIL 2 [PIZ1_A] MISIÓN DEL REPARTIDOR DE PIZZAS [CNTBUY1] Compra de la imprenta: ~1~ $ [CARBUY] Compra del salón de coches: ~1~ $ [PORNBUY] Compra del estudio cinematográfico: ~1~ $ [ICEBUY] Compra de la fábrica de helados: ~1~ $ [TAXIBUY] Compra de la empresa de taxis: ~1~ $ [BANKBUY] Compra del Malibú: ~1~ $ [BOATBUY] Compra del astillero: ~1~ $ [PRNT_NO] No puedes comprar la imprenta en este momento, vuelve más tarde. [CAR_NO] No puedes comprar el salón de coches en este momento, vuelve más tarde. [PORN_NO] No puedes comprar el estudio cinematográfico en este momento, vuelve más tarde. [ICE_NO] No puedes comprar la fábrica de helados en este momento, vuelve más tarde. [TAXI_NO] No puedes comprar la compañía de taxis en este momento, vuelve más tarde. [BANK_NO] No puedes comprar el Malibú en este momento, vuelve más tarde. [BOAT_NO] No puedes comprar el astillero en este momento, vuelve más tarde. [COL2_6] ¡Alto, cerdo imperialista americano! [COL2_6B] Eso es propiedad del gobierno francés. [COL2_6C] ¡y se acabó! [COL3_A] Thomas, aprecio que vinieses. [COL3_B] Perdóname por ir directamente al asunto. [COL3_C] Diaz me ha pedido que supervise una transacción de un negocio menor. [COL3_D] Esperemos que vaya mejor que la última vez. [COL3_E] Por eso es por lo que pensé en ti, amigo mío. [COL3_F] He dejado algo de protección en el aparcamiento. [COL3_G] Recógele y luego id a ver a los hombres de Diaz en el punto de entrega. [COL4_2] ¡No lo sé, señor! [COL4_5] ¡Señor, sí, señor! [COL4_10] Vamos a comer unos donuts. [COL4_16] ¡Señor! ¡Moviendo el vehículo, señor! [COL4_25] ¡Iniciada autodestrucción del vehículo! [COL5_6] Mercedes, esa chica me matará. [COL5_8] ¡Maldita cucaracha! [COL5_5] ¡Morid, cerdos franceses! [CNT2_1] Matadle. [CNT2_2] ¡Conseguid las planchas! [CNT2_3] ¡Proteged al mensajero! [FINKILL] ¡De acuerdo, chicos, matadle! [FIN_1A] ¡Ven aquí, traidor, pedazo de mierda! [FIN_1B] ¡Vas a caer, mamón traicionero! [FIN_1C] ¡Éste es el último baile para Lance Vance! [FIN_2B] ¡Oh, eso crees tú! [FIN_2C] ¡Ya tuve suficiente de eso en la escuela! [FIN_3] Ahora no hay nadie para cubrirte el trasero, ¿eh, Tommy? [FIN_4] Eres historia, Tommy, historia. [FIN_5] Escogiste el lado equivocado, Lance... [FIN_6] Sonny está arriba con la caja fuerte y mi dinero... [FIN_10] ¿Sonny? ¡Sonny! ¡Voy por ti! [FIN_11A] Me arrebataste quince años, Sonny... [FIN_11B] ¡Y ahora voy a hacértelo pagar! [FIN_12A] Todavía no lo captas, ¿verdad? [FIN_12B] Eres de mi propiedad, Tommy. [FIN_12C] ¡Esos quince años eran míos para gastarlos! [FIN_13] Atrapadle chicos, nunca comprendió nada. [RACES_4] 3 [RACES_5] 2 [RACES_6] 1 [RACES_7] ¡ADELANTE! [RACES_9] Tiempo: ~1~:~1~ [RACES] TIEMPO: [RACES17] Nuevo mejor tiempo: ~1~:~1~ [RACES18] HAS GANADO: ~1~ $ [RACES20] Nuevo mejor tiempo: ~1~:0~1~ [RACES21] Tiempo: ~1~:0~1~ [RCH1_2] ~g~Los CONTROLES están esparcidos a lo largo del aeropuerto. [RCH1_5] Tiempo: [RCRC1_2] ~g~¡Ve a la parrilla de salida! [RCRC1_4] ~g~3 [RCRC1_5] ~g~2 [RCRC1_6] ~g~1 [RCRC1_7] ~g~¡ADELANTE! [RCRC1_8] ~g~Tiempo de carrera: ~1~ segundos [RCPL1_1] ~g~Compite en una CARRERA DE PUNTOS DE CONTROL con otros tres Barón RC. [RCPL1_2] ~g~Debes atravesar la ~o~CORONA DEL CENTRO ~g~para pasar con éxito un punto de control. [RCPL1_3] ~g~¡Ve ahora a la parrilla de salida! [ICC1_O] ¿Qué pasa contigo? [FEA_2SP] 2 altavoces [FEA_4SP] Más de 2 altavoces [FEA_EAR] Auriculares [FEA_NAH] NO HAY HARDWARE DE AUDIO [FET_APP] APLICAR [FES_SKN] NOMBRE DE APARIENCIA [FES_DAT] FECHA [FES_SET] Utilizar apariencia [FET_DEF] Restaurar valores por defecto [FESZ_QZ] ¿Seguro de que quieres guardar esta partida? [FES_SCG] ¿Guardar la partida actual? [FES_LCG] ¿Cargar la partida y continuar jugando? [FEC_FIR] Disparar [FEC_NWE] Siguiente arma [FEC_PWE] Arma anterior [FEC_FOR] Avanzar [FEC_BAC] Retroceder [FEC_LEF] Izquierda [FEC_RIG] Derecha [FEC_ZIN] Acercar zoom [FEC_ZOT] Alejar zoom [FEC_EEX] Entrar y salir [FEC_RAD] Radio [FEC_SUB] Misión secundaria [FEC_CMR] Cambiar cámara [FEC_JMP] Saltar [FEC_SPN] Esprintar [FEC_HND] Freno de mano [FEC_LOL] Mirar hacia la izquierda [FEC_LOR] Mirar hacia la derecha [FEC_NTR] Siguiente objetivo [FEC_PTT] Objetivo anterior [FEC_LBA] Mirar detrás [FEC_CEN] Centrar cámara [FET_CFT] A PIE [FET_CCR] EN COCHE [FET_CAC] ACCIÓN [FEC_IBT] - [FEC_MXO] MXB1 [FEC_MXT] MXB2 [FEC_UNB] ILIMITADO [FEC_TFL] Mirar a izquierd+Torreta L [FEC_TFR] Mirar a derecha+Torreta R [FEC_MWF] RUEDA DEL RATÓN ARRIBA [FEC_MWB] RUEDA DEL RATÓN ABAJO [FEC_ORR] o [FEC_NUS] NO UTILIZADO [FEC_LUD] Mirar arriba [FEC_LDU] Mirar abajo [FEC_CMP] COMBO: MIRAR I+d [LAW_1A] law_1a [LAW_1B] law_1b [LAW_2A] law_2a [LAW_2B] law_2b [FEH_STA] ESTADÍSTICAS [FEH_LOA] CARGAR [FEH_CON] CONTROLES [FEH_AUD] SONIDO [FEH_DIS] PANTALLA [FEH_LAN] IDIOMA [FEH_SGA] INICIAR NUEVA PARTIDA [FEO_CON] Configuración del mando [FEO_AUD] Configuración de sonido [FEO_DIS] Configuración de pantalla [FEO_LAN] Configuración de idioma [FEO_PLA] Configuración del jugador [FET_PS] CONFIGURACIÓN DE APARIENCIA [FEA_OUT] Salida [FEA_ST] Estéreo [FEA_DTS] DTS [FEA_RSS] Emisora de radio [FEA_NON] RADIO APAGADA [FEA_FM0] WILDSTYLE [FEA_FM1] FLASH FM [FEA_FM2] KCHAT [FEA_FM3] FEVER 105 [FEA_FM4] VROCK [FEA_FM5] VCPR [FEA_FM7] EMOTION 98.3 [FEA_FM8] WAVE 103 [FED_BRI] Brillo [FED_TRA] Estelas [FED_SUB] Subtítulos [FED_WIS] Formato 16:9 [FED_POS] Posición de la pantalla [FED_RDR] RADAR [FED_HUD] MODO HUD [FED_RDB] SOLO ICONOS [FE_MLG] LEYENDA DEL MAPA [FEI_SCR] Desplazarse [FEP_RES] Continuar [FEP_STG] Iniciar partida [FEP_STA] Estadísticas [FEP_BRI] Resumen [FEP_OPT] Opciones [FEP_QUI] Salir del juego [FES_LOA] Cargar partida [FES_DEL] Borrar partida [FEC_CSU] Configuración del controlador [FEC_RED] Redefinir controles [FEC_MOU] Configuración del ratón [DISTGOL] Distancia recorrida en carro de golf (millas) [DISTGOM] Distancia recorrida en carro de golf (m) [ST_FAVR] Estación de radio favorita [ST_WSTR] estación de radio que menos te gusta [ST_FAVV] Vehículo favorito [ST_STAR] Número total de estrellas se busca conseguidas [ST_HEAD] Número de disparos a la cabeza [ST_GANG] Banda que menos te gusta [ST_STGN] Número total de estrellas se busca evadidas [TYREPOP] Neumáticos reventados por disparos [TYRESLA] Neumáticos rajados con una navaja [ST_BRK] Número de muertes en el anillo sangriento [ST_LTBR] Mayor tiempo en el anillo sangriento (segs.) [ST_GNG1] Cubanos [ST_GNG2] Haitianos [ST_GNG3] Aspirantes callejeros [ST_GNG4] Pandilleros de Díaz [ST_GNG5] Guardias de seguridad [ST_GNG6] Banda de motoristas [ST_GNG7] Banda de Vercetti [ST_GNG8] Golfistas [FEA_FM6] ESPANTOSO [ST_ASSI] Contratos de asesinato completados [DISTBIK] Dist. Recorrida en moto (millas) [DISTBIM] Distancia recorrida en moto (m) [HOTEL] Ocean View [KICK1_9] PUNTOS DE CONTROL: [FIN_B6] No tienes suficiente dinero para empezar esta misión. [TEX3_9] ~g~Recoge una bomba pasando con el helicóptero RC cerca de ella. [HELP22] Ve a la señal de la casa verde que hay en el radar. [FES_FMS] Éxito al formatear. Selecciona Aceptar para continuar. [FES_SSC] Éxito al guardar. Selecciona Aceptar para continuar. [FES_DSC] Éxito al borrar. Selecciona Aceptar para continuar. [FESZ_QC] ¿Deseas sobreescribir esta partida guardada dañada? [FES_CHE] ¡Atención! Se han activado uno o más trucos, esto puede afectar a tus partidas guardadas. Te recomendamos no guardar esta partida. [FET_SG] GUARDAR PARTIDA [FEH_BRI] INFORME [FEH_MAP] MAPA [FEM_OK] Aceptar [FEC_CRO] Agacharse [FEC_CR3] Agacharse (Botón L3) [FEC_SMT] Sub-misión [FEC_SM3] Sub-misión (Botón R3) [FEC_RSC] Emisoras de radio [ST_PR01] Aviador [ST_PR02] Piloto del ejército [ST_PR03] Oficial piloto [ST_PR04] Cabo [ST_PR05] Teniente [ST_PR06] Sargento [ST_PR07] Capitán [ST_PR08] Biggs [ST_PR09] Wedge [ST_PR10] Barón Rojo [ST_PR11] Ganso [ST_PR12] Víbora [ST_PR13] Jester [ST_PR14] Chappy [ST_PR15] Iceman [ST_PR16] Maverick [ST_PR17] Negado [ST_PR18] General de la Armada aérea [ST_PR19] As [FET_LG] CARGAR JUEGO [CAR_EXP] Vehículos destruidos [BOA_EXP] Barcos destruidos [HEL_DST] Aviones y helicópteros destruidos [STFT_01] Tiempo más rápido de ''Moto con llantas de aleación'' [STFT_02] Tiempo más rápido de ''El conductor'' [STFT_03] Tiempo más rápido en el Circuito de Tierra [STFT_04] Tiempo más rápido en la Carrera de Aviones RC [STFT_05] Tiempo más rápido en la Carrera de Coches RC [STFT_06] Tiempo más rápido Control del Helicóptero RC [STFT_07] Tiempo más rápido en ''Velocidad terminal'' [STFT_08] Tiempo más rápido en ''Ocean Drive'' [STFT_09] Tiempo más rápido en ''Carrera por el borde'' [STFT_10] Tiempo más rápido en ''Capital Cruise'' [STFT_11] Tiempo más rápido en ''¡Tour!'' [STFT_12] Tiempo más rápido en ''Aguante en V.C.'' [STHC_01] Puntuación más alta en el campo de tiro [STHC_02] Mejor porcentaje de impactos en el campo de tiro [STHC_03] Ventas de droga realizadas [HELP24] Ahora puedes aceptar trabajos del Coronel. [HELP25] Ahora puedes aceptar trabajos de Avery Carrington. [HELP29] Puedes visitar la tienda de ropa cuando no estés en una misión. [HELP30] Cuando compres ropa nueva, tu nivel de se busca se establecerá en cero. [ASM4_24] distancia: [RBM1_6] ~g~Lleva a Mercedes y el ''love juice'' a la banda en el estudio de grabación. [HELP31] Para hacer una pasada, mira primero a izquierda o derecha con ~k~~VEHICLE_LOOKLEFT~ o ~k~~VEHICLE_LOOKRIGHT~. [HELP34] Para realizar una pasada deberás tener un subfusil. [STRIP_1] ~r~No tienes dinero suficiente, tacaño miserable. [EXIT_1] Pulsa ~k~~PED_SPRINT~ para salir. [ASM1_B] Encontrará su siguiente trabajo pegado con cinta adhesiva bajo el teléfono. [ASM1_C] Tengo más trabajo para usted que requerirá tomar ''el volante'' de la situación. [SCARF] Apartamento 3c [LAW4_10] ¡Mamones de directores! [RCH1_6] ~g~Utiliza el helicóptero RC para pasar por los puntos de control dispersados por el aeropuerto. [RCH1_9] ~b~TIEMPO TOTAL: ~1~:~1~ [RCH1_10] ~b~TIEMPO TOTAL: ~1~:0~1~ [WHEEL01] DOBLE BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Distancia: ~1~,~1~m Tiempo: ~1~ segundos [WHEEL02] DOBLE BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Distancia: ~1~ pies Tiempo: ~1~ segundos [WHEEL03] BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Tiempo: ~1~ segundos [WHEEL04] BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Distancia: ~1~,~1~m [WHEEL05] BONIFICACIÓN EN DOS RUEDAS: ~1~ $ Distancia: ~1~ pies [WHEEL06] BONIFICACIÓN EN CABALLITO: ~1~ $ Distancia: ~1~,~1~m Tiempo: ~1~ segundos [WHEEL07] BONIFICACIÓN EN CABALLITO: ~1~ $ Distancia: ~1~ pies Tiempo: ~1~ segundos [WHEEL08] BONIFICACIÓN EN CABALLITO: ~1~ $ Tiempo: ~1~ segundos [WHEEL09] BONIFICACIÓN EN CABALLITO: ~1~ $ Distancia: ~1~,~1~m [WHEEL10] BONIFICACIÓN EN CABALLITO: ~1~ $ Distancia: ~1~ pies [WHEEL11] BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Distancia: ~1~,~1~m Tiempo: ~1~ segundos [WHEEL12] BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Distancia: ~1~ pies Tiempo: ~1~ segundos [WHEEL13] BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Tiempo: ~1~ segundos [WHEEL14] BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Distancia: ~1~,~1~m [WHEEL15] BONIFICACIÓN EN PARADA MÁS LARGA: ~1~ $ Distancia: ~1~ pies [ROK3_72] ¡Love Fist! [POR1_19] ¡Eh! [DESPERA] Desesperado [MOB_99A] Ve hasta el teléfono público junto al centro comercial en Washington. [MOB_98A] Ve hasta el teléfono público en Vice Point. [MOB_96A] Ve hasta el teléfono público en la terminal del aeropuerto. [MOB_95A] Ve hasta el teléfono público en Little Havana. [BNK1_1] ¿Le puedo ayudar, señor? [BNK1_2] ¡Hay un impostor! [BNK1_3] ¡Se ha vuelto loco! [BNK1_4] ¿Quién demonios eres? [BNK1_5] ¿Dónde está tu placa? [BNK1_6] ¡Ahí están! ¡Dispara a matar! [MOB_24A] Hola, ¿Sr. Vercetti? [MOB_24B] Sí. [MOB_24C] Soy Cortez. Usted estuvo en mi fiesta. [MOB_24D] Sí, lo recuerdo. [MOB_24E] Sr. Vercetti, ha sido un desgraciado incidente lo que ocurrió con su operación de negocios. [MOB_24F] Lo sé. [MOB_24G] Quiero que sepa que tanto yo como mi gente estamos haciendo todo lo que podemos para llegar al fondo de este tema. [MOB_24H] Si desea hablar conmigo de manera más privada, me podrá encontrar en el barco. Buenos días, señor. [BNK2_6] ¡Este tío está chalado! [ANGEL] Ángel [CUBJET] Jetmax Cubano [SANDKIN] Sandking [POLMAV] Maverick de la policía [BOXVILL] Boxville [BENSON] Benson [HOTRINA] Corredor de Hotring [HOTRINB] Corredor de Hotring [BLOODRA] Coche de Bloodring [BLOODRB] Coche de Bloodring [MAFIACR] Yate de la Mafia [COP_M2] ANTI VICIO [COP_M3] TRUENO MARRÓN [BNK3_2] No voy a conducir por ti, ni soñando, voy a compartir esto con el grupo. [FEM_SL1] No hay archivo 1 guardado [FEM_SL2] No hay archivo 2 guardado [FEM_SL3] No hay archivo 3 guardado [FEM_SL4] No hay archivo 4 guardado [FEM_SL5] No hay archivo 5 guardado [FEM_SL6] No hay archivo 6 guardado [FEM_SL7] No hay archivo 7 guardado [FEM_SL8] No hay archivo 8 guardado [FEA_CHA] Cambiando la salida de audio a ESTÉREO. Espera... [FEA_CHD] ¡Aviso! Estás cambiando la salida de ESTÉREO a DTS. Espera... [FEI_SEL] Selec. [FEI_BAC] Atrás [FEI_RES] Reanudar [FEI_NAV] Navegar [FEI_BTX] botón / - [FEI_BTT] botón " - [FEI_STA] Botón START - [FEI_BTD] ; = > < - [FEI_STO] Parar [MOB_68A] Tommy, tío, tengo una sorpresa para ti. [MOB_68B] Me encuentro en el estudio de grabación con unos artistas importantes. [MOB_68C] ¿Por qué no te vienes por aquí? [MOB_68D] Sabes que esto tiene sentido, ¿a que sí? Hasta dentro de un rato. [OUTFT1] Calle [OUTFT2] Etiqueta [OUTFT3] Mono de trabajo [OUTFT4] Club de campo [OUTFT5] Habana [OUTFT6] Policía [OUTFT7] Atraco al banco [OUTFT8] Informal [OUTFT9] Sr. Vercetti [OUTFT10] Chándal [OUTFT13] MC Tommy [CAR_AS1] CONCESIONARIO DE COCHES CONSOLIDADO [CAR_AS2] ~g~El Concesionario de coches Sunshine generará ahora unos ingresos de hasta ~1~ $ máximo. Asegúrate de recaudarlos regularmente. [BUYSAVE] ~g~A partir de ahora, podrás guardar tu partida aquí cuando no estés en una misión. [BUYGARG] ~g~También puedes almacenar vehículos en este garaje. [STRPBUY] Adquirido el club Pole Position: ~1~ $ [GA_4] Las bombas de coche son 500 $ cada una. [GA_5] Tu coche ya está equipado con una bomba. [GA_6] { reVC update } ¡Apárcalo, actívala pulsando ~h~~k~~VEHICLE_FIREWEAPON~~w~ y SAL PITANDO! [GA_7] { reVC update } ¡Ármalo pulsando ~h~~k~~VEHICLE_FIREWEAPON~~w~. La bomba explotará cuando se arranque el motor. [GA_6B] { reVC update } ¡Apárcalo, actívala pulsando ~h~~k~~VEHICLE_FIREWEAPON~~w~ y SAL PITANDO! [GA_7B] { reVC update } ¡Ármalo pulsando ~h~~k~~VEHICLE_FIREWEAPON~~w~. La bomba explotará cuando se arranque el motor. [MOB_70A] Tommy, soy yo, el coronel Cortez. Mira, me parece que eres la clase de hombre que lleva a cabo los trabajos. Así que ayúdame, por favor. [MOB_70B] Me podrás encontrar en el barco. [PICK1] ¡El chaleco antibalas ha sido entregado en el hotel Ocean View! [PICK2] ¡La .357 ha sido entregada en el hotel Ocean View! [PICK3] ¡La sierra mecánica ha sido entregada en el hotel Ocean View! [PICK4] ¡El lanzallamas ha sido entregado en el hotel Ocean View! [PICK5] ¡El rifle de francotirador .308 ha sido entregado en el hotel Ocean View! [PICK6] ¡La Ametralladora Pesada ha sido entregada en el hotel Ocean View! [PICK7] ¡El lanzacohetes ha sido entregado en el hotel Ocean View! [PICK8] ¡El Sea Sparrow está ahora disponible en la mansión de Starfish Island! [PICK9] ¡El tanque está ahora disponible en los cuarteles del ejército! [PICK10] ¡El Hunter está ahora disponible en los cuarteles del ejército! [CLOTH1] El traje de etiqueta ha sido entregado en Rafaels en Ocean Beach. [CLOTH2] El traje de calle ha sido entregado en los pisos francos. [CLOTH3] El mono de trabajo ha sido entregado en Tooled Up en el centro comercial de North Point. [CLOTH4] El traje del club de campo ha sido entregado en el club de golf de Leaf Links. [CLOTH5] El traje Havana ha sido entregado en la tienda de ropa de Little Havana. [CLOTH6] El traje de policía ha sido entregado en la jefatura de policía de Washington Beach. [CLOTH7] El traje informal ha sido entregado en Gash del centro comercial de North Point. [CLOTH8] El traje del Sr. Vercetti ha sido entregado en Collar & Cuffs de Ocean Beach. [CLOTH9] El chándal ha sido entregado en Jocksport del centro de la ciudad. [CLOTH10] El traje para el atraco al banco ha sido entregado en el Club Malibu de Vice Point. [MOB_62A] Tommy, soy Ricardo Díaz, quiero darte las gracias por cuidar de mí, tío. [MOB_62B] He preguntado al gilipollas de Cortez, y me ha dicho que eres bueno, amigo, ¿por qué no vienes a verme? [MOB_62C] Necesito un tío como tú. Todos los que tengo son gilipollas perdidos, [MOB_62D] gilipollas por todas partes, tío. Yo te puedo hacer muy rico. [GOAWAY2] ~g~Regresa cuando hayas terminado las misiones de la banda de moteros. [COL2_9] ¡Estúpido americano! ¡Te han seguido hasta aquí! [LOADCOL] Cargando... [STFT_17] Tiempo más rápido en la ''Prueba PCJ'' [STFT_18] Tiempo más rápido en el ''Trial de tierra'' [STFT_19] Tiempo más rápido en la ''Pista de Pruebas'' [NEW_REC] ¡Nuevo récord establecido! ~w~~1~ minutos ~g~y ~w~~1~ segundos. [BMX_HOW] ~g~¡Da dos vueltas a la pista de tierra, ~y~pasando por ~g~los ~y~PUNTOS DE CONTROL ~g~por el camino! [BMXREW1] ~g~¡Cada vez que batas tu récord anterior de las dos vueltas [BMXREW2] ~g~conseguirás una ~y~RECOMPENSA ~g~mejor! [BMXRAIN] ~g~Parece lluvia... [ITBEG] Al principio... [NBMNBUY] Casa Swanko adquirida: ~1~ $ [LNKVBUY] Apartamento en Links View adquirido: ~1~ $ [HYCOBUY] Piso de Hyman adquirido: ~1~ $ [BUYGARS] ~g~Puedes guardar vehículos en estos garajes. [OCHEBUY] Apartamento de Ocean Heights adquirido: ~1~ $ [WASHBUY] 1102 de la calle Washington adquirido: ~1~ $ [VCPTBUY] 3321 de Vice Point adquirido: ~1~ $ [SKUMBUY] Chabola Skumole adquirida: ~1~ $ [HELP6_C] Pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~ para usar el freno de mano del vehículo. [HELP2_A] Pulsa ~h~~k~~PED_SPRINT~~w~~w~ cuando estés corriendo para ~h~esprintar. [HELP4_A] Pulsa ~h~~k~~VEHICLE_ACCELERATE~~w~ para acelerar. [HELP5_A] Pulsa ~h~~k~~VEHICLE_BRAKE~~w~ para frenar o para dar marcha atrás si el vehículo se ha detenido. [HELP8_A] Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ para hacer zoom con el rifle y ~h~~k~~PED_SNIPER_ZOOM_OUT~~w~ para alejar la vista. [PBOAT_1] { reVC update } Pulsa ~h~ ~k~~VEHICLE_FIREWEAPON~~w~ para disparar los cañones del barco. [SEG3_4] { reVC update } ~g~Puedes recoger bombas simplemente pilotando tu Helicóptero RC cerca de cada una de ellas, para soltar una bomba pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~. [RCR1_3] { reVC update } ~g~Si quieres abandonar esta misión, pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~ para detonar tu coche RC. [HELP32] { reVC update } A continuación dispara pulsando ~h~~k~~VEHICLE_FIREWEAPON~. [HELP33] { reVC update } A continuación dispara pulsando ~h~~k~~VEHICLE_FIREWEAPON~. [TTUTOR] Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de taxista. [TTUTOR2] Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de taxista. [FTUTOR] Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones del camión de bomberos. [FTUTOR2] Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones del camión de bomberos. [CTUTOR] Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de justiciero. [CTUTOR2] Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de justiciero. [HELP8_B] Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~ ~w~para ~h~acercar la vista ~w~con el rifle y ~h~~k~~PED_SNIPER_ZOOM_OUT~ ~w~para ~h~alejar la vista ~w~otra vez. [ATUTOR3] Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ para comenzar o cancelar las misiones de ATS. [GUN_H1] ~w~Pulsa ~h~~k~~PED_SPRINT~~w~ para comprar. ~w~Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~~w~ para salir. [PU_CF3] { reVC update } Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para reemplazar tu arma actual en esta ranura. [PU_CF4] { reVC update } Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para reemplazar tu arma actual en esta ranura. [HELP9_B] Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para ~h~disparar~w~ el rifle de francotirador. [HELP37] Si no quieres subir al coche mientras se lo estás robando a alguien, pulsa ~h~~k~~PED_SPRINT~. [HELP6_A] Pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~ para usar el freno de mano del vehículo. [HELP6_D] Pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~ para usar el freno de mano del vehículo. [HELP26] Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~~w~ para subir o bajar de un vehículo. [HELP27] Pulsa ~h~~k~~VEHICLE_TURRETUP~~w~ o ~h~~k~~VEHICLE_TURRETDOWN~~w~ para equilibrar tu peso en la moto. [HELP28] Pulsa ~h~~k~~VEHICLE_TURRETUP~~w~ o ~h~~k~~VEHICLE_TURRETDOWN~~w~ para equilibrar tu peso en la moto. [HELP35] Pulsa ~h~~k~~GO_LEFT~~w~ o ~h~~k~~GO_RIGHT~~w~ para conducir el vehículo. [HELP36] Pulsa ~h~~k~~GO_LEFT~~w~ o ~h~~k~~GO_RIGHT~~w~ para conducir el vehículo. [HELP42] Ve al ~q~icono rosa~w~ para encontrar el hotel. [HELP19] Camina hasta el ~q~marcador rosa~w~ para continuar. [HELP1] Párate en el centro del ~q~marcador rosa. [HELP12] Camina hasta el centro del ~q~marcador rosa~w~ para activar una misión. [SEG3_6] ~g~Para acertar en una zona objetivo con éxito, deberás soltar una bomba en la zona representada por el ~q~marcador rosa~w~. Puedes soltar las bombas en cualquier orden. [S_PROMP] Cuando no estés en una misión podrás guardar tu progreso recogiendo las ~h~cintas de cassette~w~. [HELP16] Cruza la puerta principal del hotel ~h~Ocean View~w~ para entrar en el edificio. [HELP43] ~g~Ve al hotel ~h~Ocean View~g~ en Ocean Drive. [HELI_F1] ~r~¡Misión de puntos de control cancelada! [AMMUHLP] Si necesitas armas pásate por ~h~Ammu-Nation~w~. Está indicado en el radar como una ~h~pistola~w~ azul. [HELI_1] Control del Helicóptero de Centro de la ciudad [HELI_2] Control del Helicóptero de Ocean Beach [HELI_3] Control del Helicóptero de Vice Point [HELI_4] Control del Helicóptero de Little Haiti [FST_MFR] Emisora de radio preferida [FST_LFR] Emisora de radio menos escuchada [FEI_HOL] Mantener [FEI_ZOO] Zoom [FEI_BTR] > < - [FEI_NA] N\A [MESA] Mesa Grande [STRP_NO] No puedes comprar el club de striptease en este momento, vuelve luego. [CHSE] PERSECUCIÓN [NBMN_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la casa Swanko por ~1~ $. [NBMN_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la casa Swanko por ~1~ $. [NBMN_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la casa Swanko por ~1~ $. [LNKV_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Links View por ~1~ $. [LNKV_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Linsk View por ~1~ $. [LNKV_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Links View por ~1~ $. [HYCO_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el piso de Hyman por ~1~ $. [HYCO_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el piso de Hyman por ~1~ $. [HYCO_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el piso de Hyman por ~1~ $. [OCHE_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Ocean Heights por ~1~ $. [OCHE_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Ocean Heights por ~1~ $. [OCHE_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el apartamento de Ocean Heights por ~1~ $. [WASH_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 1102 de la calle Washington por ~1~ $. [WASH_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 1102 de la calle Washington por ~1~ $. [WASH_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 1102 de la calle Washington por ~1~ $. [VCPT_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 3321 de Vice Point por ~1~ $. [VCPT_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 3321 de Vice Point por ~1~ $. [VCPT_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el 3321 de Vice Point por ~1~ $. [SKUM_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la chabola Skumole por ~1~ $. [SKUM_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~para comprar la chabola Skumole por ~1~ $. [SKUM_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~ ~w~ para comprar la chabola Skumole por ~1~ $. [PRNT_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la imprenta por ~1~ $. [PRNT_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la imprenta por ~1~ $. [PRNT_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la imprenta por ~1~ $. [CAR_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el concesionario de coches por ~1~ $. [CAR_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el concesionario de coches por ~1~ $. [CAR_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el concesionario de coches por ~1~ $. [PORN_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar los estudios de cine por ~1~ $. [PORN_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar los estudios de cine por ~1~ $. [PORN_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar los estudios de cine por ~1~ $. [ICE_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la fábrica de helados por ~1~ $. [ICE_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la fábrica de helados por ~1~ $. [ICE_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la fábrica de helados por ~1~ $. [TAXI_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la compañía de taxis por ~1~ $. [TAXI_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la compañía de taxis por ~1~ $. [TAXI_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la compañía de taxis por ~1~ $. [BANK_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el Malibú por ~1~ $. [BANK_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el Malibú por ~1~ $. [BANK_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el Malibú por ~1~ $. [BOAT_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el astillero por ~1~ $. [BOAT_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el astillero por ~1~ $. [BOAT_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el astillero por ~1~ $. [STRP_L] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el club Pole Position por ~1~ $. [STRP_T] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el club Pole Position por ~1~ $. [STRP_C] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar el club Pole Position por ~1~ $. [STOCK] ~r~Agotado [HELP14] Para encontrar el bufete del abogado, ve hacia la ~h~señal de la L~w~ que hay en el radar. [RAMPAGE] ¡MASACRE! [RAMP_F] ¡MASACRE FALLIDA! [RAMP_P] ¡MASACRE COMPLETADA! [RAMP_A] ¡TODAS LAS MASACRES COMPLETADAS! [PAGE_01] ¡Liquida a ~1~ criminales en 2 minutos! [PAGE_02] ¡Destruye ~1~ vehículos en 2 minutos! [PAGE_03] ¡Pasa en el coche y cárgate a ~1~ criminales en 2 minutos! [PAGE_04] ¡Atropella y mata a ~1~ criminales en 2 minutos! [PAGE_05] ¡Abate a tiros a ~1~ criminales en 2 minutos! [SENTXS] Sentinel XS [MAP_LEG] Leyenda [VCNMAV] VCN Maverick [LG_01] Posición del jugador [LG_02] Avery Carrington [LG_03] Contacto del motero [LG_04] Coronel Cortez [LG_05] Ricardo Díaz [LG_06] Kent Paul [LG_07] Abogado [LG_08] Phil Cassidy [LG_09] Astillero [LG_10] Club Malibú [LG_11] Cubanos [LG_12] Estudio Cinematográfico [LG_13] Ammu-Nation [LG_14] Haitianos [LG_15] Ferretería [LG_16] Piso franco [LG_17] Helado [LG_18] Taxis Kaufman [LG_19] Love Fist [LG_20] Imprenta [LG_21] Propiedad [LG_22] Taller de Pintura [LG_23] Tienda de ropa [LG_24] Mansión de Tommy [LG_25] Teléfono [LG_26] Emisora de radio Wildstyle [LG_27] Emisora de radio Flash FM [LG_28] Emisora de radio Kchat [LG_29] Emisora de radio Fever 105 [LG_30] Emisora de radio VRock [LG_31] Emisora de radio VCPR [LG_32] Emisora de radio Espantoso [LG_33] Emisora de radio Emotion 98.3 [LG_34] Emisora de radio Wave 103 [LG_35] Destino [LG_36] Solarium [LG_37] Club de Striptease [LG_38] Objetivo [MAP_YAH] ESTÁS AQUÍ [TAXSHRT] ~g~Puedes coger este taxi Kaufman para ir a tu destino sin conducir. Te costará 9 $. [FEST_HV] Nivel más alto de misión de justiciero [CLOHELP] ¡Ropa limpia! [SUNSHIN] Coches Sunshine [CHERRYP] Helados Cherry Popper [KAUFCAB] Taxis Kaufman [BOATYAR] El Astillero [WANT_L] Has perdido tu nivel de se busca, si cometes un crimen mientras las estrellas parpadean, tu nivel total de se busca se restablecerá. [FEI_BTU] ; = - [BOAT_AS] ~g~El astillero generará ahora unos ingresos hasta un máximo de ~1~ $. Asegúrate de recaudarlos de manera regular. [BOAT_A2] ASTILLERO COMPLETADO [BOAT_N] Punto de control Charlie [BOAT_P] ~g~Recoge los paquetes antes de que se acabe el tiempo. [FEI_R1B] Botón R1 \ R2 - [HELP9_A] Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para disparar el rifle de francotirador. [HELP21] Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~para subir o bajar de un vehículo. [CREAM] Distribución [UMBERTO] Café Robina [PU_CF1] Pulsa ~h~~k~~PED_ANSWER_PHONE~ ~w~para recoger esta arma. Reemplazará cualquier arma del mismo tipo que tengas. [FED_RDM] MAPA E ICONOS [FEC_ILU] Invertir vista en primera persona [NITRO] ¡Ahora todos los taxis disponen de un impulsor de salto! Simplemente toca el claxon. [RATNG53] Informal [RATNG54] Vergüenza [RATNG55] Hacker [RATNG56] Liante [RATNG57] Mentiroso compulsivo [STHC_04] Puntuación más alta por mantener la pelota en el aire en la playa [STHC_05] Mejor resultado de Hotring [STFT_13] Mejor tiempo en el Control del Helicóptero del centro [STFT_14] Mejor tiempo en el Control del Helicóptero de Ocean Beach [STFT_15] Mejor tiempo en el Control del Helicóptero de Vice Point [STFT_16] Mejor tiempo en el Control del Helicóptero de Little Haití [STFT_21] Tiempo más rápido en Hotring [STFT_22] Vuelta más rápida en Hotring [STFT_20] Tiempo más rápido en el ''Conocircuito'' [HELP44] Detente en el ~q~marcador rosa. [HELP45] Pulsa ~h~~k~~PED_DUCK~~w~ para agacharte. Esto aumentará tu puntería con las armas que llevas. [RCR1_5] Carrera Bandit RC [RCPL1_7] Carrera Barón RC [RCH1_11] Recogidas del Raider RC [FEA_CTD] ¡Aviso! Esta característica requiere que el dispositivo compatible DTS esté conectado. ¿Continuar? [FEM_STE] UTILIZAR ESTÉREO [GREET] Saludos desde... [LANCE_1] ¡Venga, tío, conduce con más cuidado! [LANCE_2] ¡Eh, mira lo que haces! [LANCE_3] ¿Eh, dónde vamos ahora? [LANCE_4] ¿Qué estamos haciendo ahora? [LAW4_15] ¡Más dinero! [MERC_5] Bonito coche, Mr. Vercetti. [MERC_26] ¡DEPRISA, DEPRISA, DEPRISA! [MERC_27] Con cuidado Tommy, me arreglaron la nariz el mes pasado. [MERC_28] Conduce con cuidado Tommy. [MERC_29] Ve más despacio Tommy. [MERC_30] Tommy, si no te importa mata a quien quieras menos a mí. [MERC_31] ¡Tommy, cielo, no me mates! [MERC_32] ¡Tommy, me alegro que robaras este coche! [MERC_40] Me he divertido tanto. [MERC_43] Adiós, mi ángel. [MERC_44] Ahora sigue machacándote, me oyes. [MERC_45] Ciao, guapetón. [COL5_17] ¡Oh, Dios mío, tienen un helicóptero! [COL5_18] ¡Dispara al helicóptero! [COL5_19] Tommy, ¡hazte con ese helicóptero! [COL5_20] ¡Ahí viene otra vez! ¡Derriba a ese helicóptero! [COL5_21] ¡Mira el tamaño de ese helicóptero! [COL5_22] ¡Ahí viene de nuevo! [FEA_DSM] ¡Aviso! Esta partida guardada está configurada para utilizar DTS. Esto requiere que el dispositivo compatible DTS esté conectado. Por favor selecciona si quieres continuar usando el sistema DTS o ESTÉREO. [STFT_23] Tiempo más rápido en Punto de control Charlie [HELP50] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para colocar la cámara detrás de ti. [HELP51] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para colocar la cámara detrás de ti. [HELP52] Pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para colocar la cámara detrás de ti. [HELP53] Pulsa ~h~~k~~PED_CYCLE_WEAPON_LEFT~~w~ o ~h~~k~~PED_CYCLE_WEAPON_RIGHT~~w~ para elegir entre todas tus armas disponibles. [HELP46] Hay ocho categorías de armas diferentes. [HELP47] Puedes llevar una arma de cada categoría al mismo tiempo: un tipo de pistola, un tipo de rifle. [HELP54] ~w~Cuesta: ~1~ $. ~r~Si compras esta arma reemplazarás la actual. [HELP2A2] Pulsa ~h~~k~~PED_SPRINT~~w~ cuando corras para ~h~esprintar. [HLPSN_A] El rifle de francotirador te permite hacer zoom de acercamiento y disparar con más precisión a objetivos a una cierta distancia. [HLPSN_B] Mantén pulsado ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar~w~ con el rifle de francotirador. [HLPSN_C] Mantén pulsado ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar~w~ con el rifle de francotirador. [HLPSN_D] Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~~w~ para ~h~acercar la vista ~w~con el rifle y ~h~~k~~PED_SNIPER_ZOOM_OUT~ ~w~para ~h~alejarla~w~. [HLPSN_E] Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ el rifle de francotirador. [HLPSN_F] Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ el rifle de francotirador. [HLPSN_G] Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ el rifle de francotirador. [PLANE_H] Mueve ~h~~k~~VEHICLE_ACCELERATE~~w~ hacia delante para acelerar y a izquierda o derecha para girar. [PLANE_4] { reVC update } {Mueve ~h~~k~~VEHICLE_ACCELERATE~~w~ hacia delante para acelerar y a izquierda o derecha para girar.} Utiliza el joystick analógico derecho para acelerar, tira hacia atrás el joystick analógico izquierdo para ascender, empújalo hacia adelante para descender. Para girar muévelo a izquierda o derecha. [HELP55] Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para atacar al chef. [STPR_8] Club Pole Position [STPR_9] 3321 de Vice Point [STPR_10] Apartamento de Links View [STPR_11] Casa Swanko [STPR_12] 1102 de Washington Street [STPR_13] Apartamento de Ocean Heights [STPR_14] Chabola Skumole [STPR_15] Piso de Hyman [RCCANX] ~r~Misión de avión RC cancelada. [CLT_HL2] Cuando recojas ropa, te librarás de una o dos estrellas del nivel de se busca. [CRED009] DISEÑO DE MISIONES [CRED359] LEE JOHNSON [CRED360] HENDRIK LESSER [CRED361] PASQUALE STACCHIOTTI [CRED362] ENRIQUE FERNÁNDEZ [CRED363] PAUL BYERS [CRED364] MIKE EMENY [CRED365] ROB DUNKIN [CRED366] CHARLIE KINLOCH [CRED367] KEVIN HOBSON [CRED368] JIM CREE [MOB_66A] Tommy, Tommy, ¿para qué has vuelto? [MOB_66B] Ya te he dicho que no queremos verte más. [MOB_67A] Tommy, creo que no deberías acercarte, ¿me oyes? [MOB_67B] Los chicos haitianos no están muy contentos contigo. [MOB_18A] Tommy, soy Paulo, ¿cómo estás? Vale hombre, bueno pensé que tenía que mandarte una nota. [MOB_18B] Oh Dios misericordioso, hijo mío, no te vas a creer la categoría de la furcia que acabo de conocer. [MOB_18C] Prostituta callejera o algo parecido, cerca de Little Havana, tío. [MOB_18D] Me dijo que se llamaba Mercedes o algo así. [MOB_18E] Oh Dios, tío, tienes que probar a esa palomita. [MOB_18F] Sabe como ponerte a tono. Dijo que yo era el mejor tío con el que había estado en toda su vida. [MOB_18G] Estate al tanto por si la ves. Hasta luego. [MOB_72A] Tommy, soy yo, Lance. Mantén la boca cerrada Tommy porque no tengo ganas de hablar. [MOB_72B] No estoy interesado en lo que tengas que decir. ¿Por qué debería estarlo? ¿Te preocupas tú por mí? [MOB_72C] Tienes que cuidarme un poco mejor. Darme una buena tajada, ya sabes... [MOB_72D] Tommy... oh, mira, tío, Lo siento. Es solo que... [MOB_72E] La gente me ha estado sobreprotegiendo toda mi vida, tratándome como a un niño. [MOB_72F] Mi hermano haría eso. Por favor, tío, no lo hagas. [MOB_72G] Tengo que irme. [MOB_63A] Tommy, soy Earnest. Earnest Kelly. [MOB_63B] ¿Cómo estás? [MOB_63C] Estoy bien. Necesitaré un bastón para andar pero muy pronto volveré al trabajo. [MOB_63D] Bien. [MOB_63E] Me enteré de lo de Lance. Vaya cabroncete, ¿eh? [MOB_63F] Sí. [MOB_63G] No te fíes nunca de un hombre que va por la calle en pijama. Y es lo que digo yo. Menos mal que lo mataste. Espero que el mamón sufriera. [MOB_63H] Creo que sí. Lo que pasa es que yo no creí que... [MOB_63I] Tommy, para ser un chalado furioso, eres bastante ingenuo. Cuando vuelva a trabajar pronto te enseñaré un par de cosas sobre la vida, me oyes. [MOB_63J] Tómate tu tiempo, Earnest. Cuídate. [MOB_16A] Tommy, soy Paulo, ¿qué pasa amigo? [MOB_16B] Qué quieres Paul. No necesito ropa de marca de imitación. [MOB_16C] Muy gracioso, tío, pero sabes que no toco nada de mercancía ilegal. No, sólo llamaba para ver si puedo conseguir un papel en una de tus películas. [MOB_16D] Allí en Inglaterra hice un montón de porno, colega. Soy bastante más ardiente que tú, pequeño. [MOB_16E] Paul, gracias por la oferta, lo tendré en cuenta. [MOB_16F] En serio, después de todo lo que he hecho por ti, no te olvides de mí. [MOB_16G] Eso es lo que intento olvidar. [MOB_17A] Tommy Vercetti, ¿Qué tal te va Sr. Influyente? He oído un montón de cosas sobre ti, así que ahora eres el que manda en la ciudad, eh... [MOB_17B] Paul, estás borracho. [MOB_17C] No, imbécil, no estoy borracho. Sólo he tomado un par de copas y alguna que otra invitación, no me he acostado en los dos últimos días, sabes. [MOB_17D] En cualquier caso, no me vengas con esto. No soy tonto. ¿Quién te estableció en esta ciudad? ¿Quién? Yo. Que te enteres. [MOB_17F] ¿De verdad? [MOB_17G] No me vengas con esto. Yo te presenté a la gente. Te enseñé cómo hacer bien los trabajos, hice muchas cosas por ti, y así es como me lo pagas. [MOB_17H] Me ignoras. Ni siguieras me das acceso a nada, después de todo lo que hice por ti. ¿Quién te crees que soy? ¿Un retrasado mental o algo así? [MOB_17I] Paul, tranquilízate. He estado muy ocupado, no te comportes como un idiota.. [MOB_17J] No soy un idiota, colega. Eso es lo que decían en el reformatorio. ¿Estás buscando bronca? ¡Porque la vas a tener! [MOB_17K] Tommy, colega. Por favor. ¡Tú eras mi gran esperanza! Por favor, ¡no te rías de mí! [MOB_17L] Paul, vete a dormir un poco, de verdad. [MOB_73A] Tommy, soy Steve. [MOB_73B] Eh, Steve. [MOB_73C] ¡Eres un portento! ¡Soy un portento! La gente está loca por nosotros. Vamos a entrar en la historia, amigo. [MOB_73D] Se trata de los premios más importantes. Podré llevar a mi padre a una residencia y no tener que escucharle nunca más. [MOB_73E] Eso está bien, Steve. [MOB_73F] Mejor que bien tío. Mejor, mucho mejor. Nunca creyó en mí. Nunca creyó que pudiera ser un artista y ahora lo he conseguido. [MOB_73G] Soy el mejor director de cine porno de todos los tiempos, amigo. Solo quería decirte que ha sido un placer conocerte. [MOB_73H] Gracias Steve. [MOB_73I] Te quiero tío, No cambies, me oyes. [MOB_73J] Te oigo. Adiós Steve. [BOLLOX] Pulsa ~h~~k~~VEHICLE_HANDBRAKE~ ~w~para soltar una bomba. Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~ ~w~para cancelar. [BRID_OP] El temporal ha pasado, todos los puentes hacia la península están ahora abiertos. [BRID_CL] Alerta de temporal: Cerrados todos los puentes hacia la península. [ASSET_C] ¡CLUB POLE POSITION CONSOLIDADO! [ASSET_D] El club Pole Position generará ahora unos ingresos máximos de hasta ~1~ $ al día. ¡Asegúrate de recaudarlos regularmente! [ST_WHEE] Mayor tiempo en caballito (seg) [ST_STOP] Mayor tiempo en parada (seg) [ST_2WHE] Mayor tiempo en dos ruedas (seg) [ST_WHED] Mayor distancia en caballito (m) [ST_STOD] Mayor distancia en parada (m) [ST_2WHD] Mayor distancia en dos ruedas (m) [OUTFT11] Chándal [OUTFT12] Frankie [RELOAD] ~g~¡Has conseguido la habilidad de recargar rápidamente! [APACHE] Hunter entregado en el helipuerto de Ocean Beach. [CRED369] JOHN MCCARDLE [CRED370] DAVID MURDOCH [CRED371] CHRIS BROWN [CRED372] PAUL GREEN [CRED373] KYLE MILNE [CRED374] KEVIN YUN [CRED375] ERICK COBBS [CRED376] RANDY BLAKE [CRED377] BRANDON LIM [CRED378] BRANDON FENOL [CRED379] MICHAEL MANOLE [CRED380] ALETHEIA SIMONSON [CRED381] JOHN JANSEN [CUNTY] ¡Ropa nueva entregada en la finca de Vercetti! [GOODBOY] ¡Prima de 50 $ por comportamiento ejemplar! [NEWCONT] ¡Nuevo ~h~punto de contacto ~w~disponible en el puerto deportivo en Ocean Beach! [FIRELVL] Misión de camión de bomberos nivel ~1~ [HELP56] Pulsa ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ para ajustar la cámara. [HELP57] Pulsa ~h~~k~~CAMERA_CHANGE_VIEW_ALL_SITUATIONS~~w~ para ajustar la cámara. [HELP58] Mientras estás apuntando, puedes pulsar ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~ o ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~para cambiar de objetivo. [HELP59] Mientras estás apuntando, puedes pulsar ~h~~k~~PED_CYCLE_TARGET_LEFT~~w~ o ~h~~k~~PED_CYCLE_TARGET_RIGHT~~w~para cambiar de objetivo. [HELP60] Si pulsas ~h~~k~~PED_SPRINT~ ~w~ mientras estás robando un coche, no te subirás en él. [HELP61] Ahora dispones de munición ilimitada y el doble de robustez en todos los vehículos. [FEC_LB1] Mirar [FEC_LB2] detrás [FEC_LB3] Mirar detrás [FEC_R3] (botón R3) [FEC_PED] Controles a pie [FEC_VEH] Controles en vehículo [FEC_FPR] Controles en primera persona [FEC_CMM] Controles comunes [FEC_PWL] Ir a la izquierda [FEC_PWR] Ir a la derecha [FEC_PWF] Andar hacia delante [FEC_PWT] Andar hacia la cámara [FEC_PLB] Mirar hacia atrás. [FEC_PFR] Disparar arma [FEC_CLE] Cambiar Arma a la Izq. [FEC_CRI] Cambiar Arma a la Dcha. [FEC_LKT] Bloquear objetivo [FEC_PJP] Salto Peatón [FEC_PSP] Sprint Peatón [FEC_PSH] Disparar Peatón [FEC_TLF] Siguiente objetivo a la izq. [FEC_TRG] Siguiente objetivo a la dcha. [FEC_CCM] Centrar cámara detrás del jugador [FEC_SZI] Acercar vista rifle francotirador [FEC_SZO] Alejar vista rifle francotirador [FEC_LKL] Mirar izq. en primera persona [FEC_LRT] Mirar dcha. en primera persona [FEC_LUP] Mirar arr. en primera persona [FEC_LDN] Mirar abj. en primera persona [FEC_LBH] Mirar hacia atrás en vehículo [FEC_LLF] Mirar a la izq. en vehículo [FEC_LRG] Mirar a la dch. en vehículo [FEC_HRN] Claxon [FEC_HBR] Freno de mano del vehículo [FEC_ACL] Acelerar vehículo [FEC_BRK] Frenar vehículo [FEC_TSM] Misiones secundarias [FEC_CRD] Cambiar emisora de radio [FEC_ENT] Entrar/salir de vehículo [FEC_WPN] Disparar arma [FEC_PAS] Pausa [FEC_FPO] Cambiar armas primera persona [FEC_SMS] Mostrar puntero del ratón [FEC_CMS] Cambiar modo cámara en todas situaciones. [FEC_TSS] Capturar pantalla [FEC_DBG] DEBUG MENU [FEC_TGD] Cambiar mando de juego/depuración [FEC_TDO] Cambiar cámara depuración No [FEC_IVH] Invertir horizontalidad ratón [FEC_MSL] BIR [FEC_MSM] BCR [FEC_MSR] BDR [FEC_QUE] ??? [FEC_TWO] Permitidas sólo dos teclas del teclado. [FEC_UMS] Por favor sólo teclas de ratón. [FEC_OMS] Permitida sólo una tecla del ratón. [FEC_UJS] Por favor sólo botones de joystick. [FEC_OJS] Permitido sólo un botón de joystick por acción. [FEC_PTL] Usar Bloquear objetivo al cambiar arma a izq. [FEC_PTR] Usar Bloquear objetivo al cambiar arma a dcha. [FEC_LBC] Usar Mirar a izq. con Mirar a la dcha. [FEC_JBO] JOY ~1~ [FEC_WAR] Aviso [FEC_OKK] Aceptar [FEC_DLF] Error al borrar. [FEC_SVU] Error al guardar. [FEC_LUN] Error al cargar. Archivo dañado, por favor bórralo. [FEC_PAD] Gamepad [FEC_JOY] Joystick [FES_CSA] Seleccionar aspecto de la lista inferior: [FET_HRD] AJUSTES POR DEFECTO RESTAURADOS [FET_MST] CONDUCCIÓN CONTROLADA POR EL RATÓN [FEC_STR] NUM. INICIO [FET_MIG] IZQUIERDA, DERECHA, RUEDA DEL RATÓN PARA AJUSTAR [FET_CIG] RETROCESO PARA QUITAR - BIR, VOLVER A CAMBIAR [FET_DSN] Apariencia predeterminada del jugador.bmp [FET_RSO] RESTAURADO AJUSTE ORIGINAL [FET_RSC] HARDAWARE NO DISPONIBLE. RESTAURADO AJUSTE ORIGINAL [FEA_3DH] HARDWARE DE SONIDO [FEA_SPK] AJUSTE DE ALTAVOCES [FEM_LOD] DIST. DE REPRESENTACIÓN [FEM_VSC] SINCRONÍA DE IMAGEN [FEM_FRM] LIMITADOR DE CUADROS [FEM_MM] MENÚ PRINCIPAL [FED_RES] RESOLUCIÓN DE PANTALLA [FET_CTL] CONFIGURAR MANDO [FET_OPT] OPCIONES [FEC_MSH] SENSIBILIDAD DEL RATÓN [FEC_IVV] INVERTIR VERTICALIDAD RATÓN [FEC_FNC] F~1~ [FEC_IRT] INSERT [FEC_DLL] SUPR [FEC_HME] INICIO [FEC_END] FIN [FEC_PGU] RE PÁG [FEC_PGD] AV PÁG [FEC_UPA] ARRIBA [FEC_DWA] ABAJO [FEC_LFA] IZDA [FEC_RFA] DCHA [FEC_NUM] NUM. [FEC_NMN] NUM. ~1~ [FEC_FWS] NUM. / [FEC_PLS] NUM. + [FEC_MIN] NUM. - [FEC_DOT] NUM. . [FEC_NLK] BLOQ NUM [FEC_ETR] INTRO [FEC_SLK] BLOQ DESPL [FEC_PSB] PAUSA INTER [FEC_BSP] RETROCESO [FEC_TAB] TAB [FEC_CLK] BLOQ MAYÚS [FEC_RTN] INTRO [FEC_LSF] MAYÚS IZQ [FEC_RSF] MAYÚS DCH [FEC_LCT] CTRL I [FEC_RCT] CTRL D [FEC_LAL] ALT I [FEC_RAL] ALT D [FEC_LWD] WIN I [FEC_RWD] WIN D [FEC_WRC] APMENÚ [FEC_SPC] ESPACIO [WIN_TTL] Grand Theft Auto VC [WIN_95] Grand Theft Auto VC no se puede ejecutar bajo W95 [WIN_DX] Grand Theft Auto VC necesita al menos la versión 8.1 de DirectX [FET_EIG] NO SE PUEDE DEFINIR UN CONTROL PARA ESTA ACCIÓN [FET_DAM] MODELADO ACÚSTICO DINÁMICO [FEQ_SRE] ¿Seguro que quieres salir? Se perderán todos los progresos desde la última partida guardada. ¿Quieres proceder? [FEQ_SRW] ¿Seguro que quieres salir del juego? [FET_QG] SALIR DEL JUEGO [FEN_STA] INICIAR JUEGO [FET_PAU] MENÚ PAUSA [REPLAY] VOLVER A JUGAR [FEC_ANS] Acción [CVT_MSG] Convertir texturas a un formato óptimo para tu tarjeta de vídeo [FEC_SFT] MAYÚS [FEH_VMP] VER MAPA [FES_DEE] ¡Error al eliminar! Por favor, inténtalo de nuevo. [FES_CMP] ¡Error al guardar! Por favor, inténtalo de nuevo. [FESZ_WR] Guardando partida actual. Por favor, espera... [FELD_WR] Cargando el juego. Por favor, espera... [FEDL_WR] Eliminando partida guardada. Por favor, espera... [PCRESRT] Iniciando nueva partida. Por favor, espera... [FET_STI] Controles estándar [FET_CTI] Controles clásicos [FEH_NA] OPCIÓN NO DISPONIBLE [FEH_MPH] RATÓN, CURSORES PARA MOVERSE - RE. PAG, AV. PAG, RUEDA RATÓN PARA ZOOM, L - LEYENDA [FEA_MP3] REPRODUCTOR MP3 [NO_PCCD] Por favor, introduce tu CD de GTA Vice City o pulsa ESC para cancelar [FEH_SSA] CURSORES PARA MOVERSE - S PARA GUARDAR EN UN ARCHIVO [FES_CMI] ÚLTIMA MISIÓN SUPERADA [FET_STS] ESTADÍSTICAS GUARDADAS EN 'STATS.HTML' + 'STATS.TXT' [WIN_VDM] Grand Theft Auto VC no pudo encontrar suficiente memoria de vídeo disponible [FEC_ERI] ¡Error! Una o más acciones de control no están vinculadas a ninguna tecla o botón. Comprueba que todas las acciones de control estén definidas. [FEC_TFU] Torreta + Inclinar arriba [FEC_TFD] Torreta + Inclinar abajo [FET_RIG] ELIGE UN NUEVO CONTROL PARA ESTA ACCIÓN [FEA_NM3] NO SE ENCONTRARON ARCHIVOS MP3 [FEA_MPB] SUBIR VOLUMEN DE MP3 [FEA_MUS] VOLUMEN DE MÚSICA [FEA_SFX] VOLUMEN DE EFECTOS [CVT_ERR] Te has quedado sin espacio en el disco duro. Por favor, antes de seguir consigue algo de espacio en tu disco duro. Pulsa ESC para cancelar. [FEA_ADP] DETECTAR EL HARDWARE AUTOMÁTICAMENTE {=================================== MISSION TABLE AMBULAE ===================================} [ATUTOR2:AMBULAE] ~g~Lleva a los pacientes al hospital CON CUIDADO. Cada bache reduce sus posibilidades de supervivencia. [A_FULL:AMBULAE] ~r~¡Ambulancia llena! [A_FAIL2:AMBULAE] ~r~¡Tu falta de urgencia ha sido mortal para el paciente! [A_FAIL3:AMBULAE] ~r~¡El paciente está muerto! [A_PASS:AMBULAE] ¡Rescatado! [A_COMP2:AMBULAE] ¡Nunca te cansarás! [A_CANC:AMBULAE] ~r~¡Misión de ATS cancelada! [A_COMP3:AMBULAE] ¡Misiones de ATS completadas! ¡Nunca te cansarás cuando estés corriendo! [ALEVEL:AMBULAE] Misión de ATS nivel ~1~ [A_FAIL1:AMBULAE] Misión de ATS terminada. [A_SAVES:AMBULAE] PERSONAS SALVADAS: ~1~ [A_COMP1:AMBULAE] Misiones de ATS completas: ~1~ $ {=================================== MISSION TABLE ASSIN1 ===================================} [ASM1_5:ASSIN1] ~r~¡Completó sus entregas! [ASM1_6:ASSIN1] Entregas restantes: [ASM1_7:ASSIN1] ~g~Carl Pearson, repartidor de pizza. Mátale antes que finalice el reparto. [ASM1_A:ASSIN1] Sr. Teal, su ayuda en erradicar a esos forasteros fue inestimable para el negocio. Tengo más trabajo para usted y más cercano a la ''intervención''. [ASM1_D:ASSIN1] Sr. Teal, su ayuda en erradicar a esos forasteros fue inestimable para el negocio. {=================================== MISSION TABLE ASSIN2 ===================================} [ASM2_1:ASSIN2] ~g~La Sra. Dawson dejará pronto la joyería de Vice Point. Mátala. Debe parecer un accidente de coche. [ASM2_3:ASSIN2] ~g~¡Va a estallar! ¡Largo de aquí! [ASM2_4:ASSIN2] ~r~Dañaste su coche ¡y ni siquiera está en él! ¡Ahora no se subirá ahí! [ASM2_5:ASSIN2] ~r~¡Se escapó! [ASM2_6:ASSIN2] ~r~¡Estabas demasiado cerca de la escena del ''accidente''! [ASM2_7:ASSIN2] ~g~¡No utilices armas! ¡Se supone que debe parecer un accidente! ¡En vez de eso, sácala de la carretera! [ASM2_8:ASSIN2] ~g~Debes hacer que la muerte de la Sra. Dawson parezca un accidente. No utilices ningún arma. [ASM2_9:ASSIN2] ~g~¡Necesitas un coche para este trabajo! [ASM2_10:ASSIN2] ~g~Cuando el coche explote en llamas vete lo más lejos posible de la escena del accidente. [ASM2_11:ASSIN2] ¡Ayúdame! [ASM2_12:ASSIN2] ¡Que alguien me ayude! [ASM2_13:ASSIN2] ¡oh, Dios mío! [ASM2_A:ASSIN2] Felicidades por un trabajo bien hecho, Sr. Teal. Mi cliente está muy satisfecho. [ASM2_2:ASSIN2] salud: {=================================== MISSION TABLE ASSIN3 ===================================} [ASM3_11:ASSIN3] TIEMPO: [ASM3_C:ASSIN3] Una banda europea planea atracar un banco en Vice City. A mis jefes les gustaría que esto no sucediese. [ASM3_D:ASSIN3] Cada miembro de la banda tiene una tapadera mientras está aquí en Vice City. Algunos tienen trabajos sin importancia y otros están de vacaciones. [ASM3_E:ASSIN3] Cada objetivo y sus posibles paraderos están pegados con cinta adhesiva bajo el teléfono. [ASM3_14:ASSIN3] ~g~Dick Tanner está ubicado junto al DBP de Seguridad en Ocean Drive. [ASM3_15:ASSIN3] ~g~Marcus Hammond y Franco Carter están ubicados junto a la joyería en Vice Point. [ASM3_16:ASSIN3] ~g~Nick Kong está en su barco cerca de Washington Beach. [ASM3_18:ASSIN3] ~g~¡No te acerque demasiado al objetivo o te descubrirá y tendrás que perseguirle hasta atraparle! [ASM3_19:ASSIN3] ~g~¡Te ha visto! ¡Acaba con él! [ASM3_20:ASSIN3] ~g~¡Te han visto! ¡Asegúrate de acabar con los dos! [ASM3_21:ASSIN3] ~r~¡No mataste a todos los miembros de la banda a tiempo! [ASM3_22:ASSIN3] ~g~¡No te acerques demasiado a los objetivos o te descubrirán e intentarán escapar. [ASM3_12:ASSIN3] ~g~Se ha dejado cerca una selección de armas en caso de necesidad. Dispones de ~h~9 MINUTOS ~g~para matar a todos los miembros de la banda. [ASM3_13:ASSIN3] ~g~Mike Griffin está trabajando en una valla de publicidad en Washington. [ASM3_17:ASSIN3] ~g~Charlie Dilson está dando una vuelta en su moto en Washington. {=================================== MISSION TABLE ASSIN4 ===================================} [ASM4_10:ASSIN4] ~g~¡Parece que no eres el único que persigue el maletín! ¡Llévalo deprisa a Ammu- Nation! [ASM4_12:ASSIN4] Distancia: [ASM4_15:ASSIN4] ~g~Busca el rifle de francotirador hacia tu derecha. [ASM4_16:ASSIN4] ~g~Observa a la mujer de la galería, bajará por las escaleras mecánicas y preguntará a alguien la hora. [ASM4_17:ASSIN4] ~g~Solamente cuando la conversación haya concluido, mata a la persona con la que habló, pero no la mates a ella. [ASM4_18:ASSIN4] ~g~Cuando el objetivo haya muerto, recupera el maletín y llévalo a Ammu-Nation, en el centro. [ASM4_19:ASSIN4] ~g~Mantén tu distancia del objetivo, la barra de distancia de la parte superior de la pantalla te indica lo cerca que estás del objetivo. [ASM4_20:ASSIN4] ~g~No dejes que se llene o te verá. [ASM4_21:ASSIN4] ~g~¡Recoge el maletín! [ASM4_22:ASSIN4] ~g~Lleva el maletín a Ammu-Nation, en el centro. [ASM4_23:ASSIN4] ~g~¡Te ha visto y se escapa, atrápale y consigue el maletín! [ASM4_25:ASSIN4] ~r~¡Has matado a la mujer, idiota! [ASM4_26:ASSIN4] ~r~¡El objetivo ha subido a su avión! [ASM4_27:ASSIN4] ~r~¡El objetivo te ha visto! ¡Deberías mantener tu distancia! [ASM4_28:ASSIN4] ~r~¡El objetivo te ha visto! ¡Te ha escuchado disparar el arma! [ASM4_29:ASSIN4] ~r~¡Mátale sólo después que haya hablado con la mujer! [ASM4_A:ASSIN4] Es hora de ir a por el pez gordo, Sr. Teal. Hay un rifle a su derecha, tras la maceta. [ASM4_B:ASSIN4] Observe a la mujer que está en la galería por encima de los mostradores de facturación. Caminará por entre la gente y le preguntará a alguien la hora. [ASM4_C:ASSIN4] Debe matar a esa persona, recoger el maletín y llevarlo al lugar adherido con cinta bajo el teléfono. {=================================== MISSION TABLE ASSIN5 ===================================} [ASM5_A:ASSIN5] Un valioso canje va a tener lugar en el tejado de la compañía de helados Cherry Popper. [ASM5_B:ASSIN5] Liquide a todos los implicados, robe el maletín y llévelo al helipuerto del aeropuerto. [ASM5_C:ASSIN5] Hay una puerta a su izquierda que conduce a la parte trasera de la fábrica. [ASM5_1:ASSIN5] ~g~Entra al complejo por detrás de la compañía de helados Cherry Popper y dirígete al tejado donde va a tener lugar el trato. [ASM5_2:ASSIN5] ~g~Consigue el maletín y llévalo al helipuerto del aeropuerto. [ASM5_3:ASSIN5] ~g~¡Lleva el maletín al helipuerto del aeropuerto! {=================================== MISSION TABLE BANKJ1 ===================================} [WANTED1:BANKJ1] ~g~¡Quítate de encima a los polis y pierde tu nivel de se busca! [BJM1_A:BANKJ1] ¡Tommy! ¡Eh, Tommy, mira esto, es genial! ¡He hecho que nos instalen un minibar! [BJM1_B:BANKJ1] Tenemos todo un bar abajo, Ken. [BJM1_C:BANKJ1] Sí, sí, vale. Bueno, tengo la pizarra que pediste. [BJM1_D:BANKJ1] Eso es lo bueno de estudiar derecho: te enseñan a seguir las instrucciones. [BJM1_E:BANKJ1] Necesito un hombre para la caja fuerte. [BJM1_F:BANKJ1] De acuerdo, bueno, déjame pensar... caja fuerte, caja fuerte, caja fuerte, caja fuerte... ¡Lo tengo! ¡Este tipo te va a encantar! [BJM1_G:BANKJ1] Ah, ese idiota no. Está encerrado. [BJM1_H:BANKJ1] ¿Encerrado dónde? [BJM1_I:BANKJ1] En la celda de una comisaría esperando el traslado. [BJM1_J:BANKJ1] Creo que está a punto de conseguir la libertad condicional... [BJM1_1:BANKJ1] ~g~¡Libera a Cam Jones de la comisaría de policía! [BJM1_3:BANKJ1] ~g~Encontrarás algo útil en el vestuario de la comisaría. [BJM1_21:BANKJ1] ~g~Puedes encontrar la llave de las celdas en el piso de arriba de la comisaría. [BNK1_7:BANKJ1] ¿Cam Jones? [BNK1_8:BANKJ1] ¡Te voy a sacar de aquí! [BNK1_10:BANKJ1] Sí, soy yo... [BNK1_11:BANKJ1] ¡Lo que tú digas! [BNK1_13:BANKJ1] Voy a hacer un trabajo y tu serás mi revienta cajas fuertes. [BNK1_14:BANKJ1] ¡Eso es mejor que pudrirse en una celda! [BJM1_22:BANKJ1] ~g~¡Lleva a Cam de vuelta a su casa! [BJM1_23:BANKJ1] ~g~¡Necesitas coger la tarjeta llave primero! [BNK1_12:BANKJ1] ¡Piérdeles el rastro y llévame a mi casa! [BJM1_20:BANKJ1] ¡Guarda el arma o te enfréntate a las consecuencias! [BJM1_5:BANKJ1] ¡Sólo personal autorizado más allá de este punto! [BJM1_2:BANKJ1] ~r~¡Se supone que tenías que sacar a rastras a Cam, no matarle! [BJM1_4:BANKJ1] ¡Está armado! ¡Mátale! {=================================== MISSION TABLE BANKJ2 ===================================} [BJM2_A:BANKJ2] Necesitamos a un atracador. ¿Conoces a alguno? [BJM2_B:BANKJ2] Eh, Tommy, Tommy, Tommy, esta mierda te pone a tono, tío. [BJM2_C:BANKJ2] ¡Guauuuuu! [BJM2_D:BANKJ2] ¡Yo podría ser tu atracador! ¡Manos arriba! ¡Manos arriba! [BJM2_E:BANKJ2] Tú no eres un atracador, tú eres un idiota. [BJM2_F:BANKJ2] Ahora ponte una copa y cállate. [BJM2_G:BANKJ2] Eh. ¡Quítate de mi camino! Sí, sí, sí, eh, eh. [BJM2_H:BANKJ2] ¿Cam, tú qué opinas? [BJM2_I:BANKJ2] Bueno, el mejor tirador de esta ciudad es un tipo llamado Cassidy. [BJM2_J:BANKJ2] ¿Seguro? [BJM2_K:BANKJ2] Sí. Un militar, o al menos él piensa que lo es. [BJM2_L:BANKJ2] Dudo que estuviese alguna vez en el ejército, pero desde luego sabe manejar las armas. [BJM2_M:BANKJ2] Estará en el campo de tiro. [BJM2_2A:BANKJ2] ¿Tú eres Phil Cassidy? [BJM2_2B:BANKJ2] ¿Por qué? [BJM2_2C:BANKJ2] Estoy buscando a un hombre que sepa manejar un arma. Pero ahora que te veo, no estoy muy convencido. [BJM2_2D:BANKJ2] Hijo, podría quitarte una mosca de la cabeza de un solo disparo a 100 metros. [BJM2_2E:BANKJ2] Oh, ¿de verdad? [BJM2_2F:BANKJ2] Sí. Aprendí en el ejército. [BJM2_2G:BANKJ2] ¿Se suele disparar a las moscas en el ejército? Me alegra no pagar impuestos. [BJM2_2H:BANKJ2] ¿Intentas ser gracioso? [BJM2_2I:BANKJ2] ~n~ [BJM2_2J:BANKJ2] Vamos a disparar. [BJM2_1:BANKJ2] ~g~Ve a Ammu-Nation en el centro de la ciudad y habla con Phil Cassidy. [BJM2_3:BANKJ2] TASA DE IMPACTOS: ~1~% [BJM2_4:BANKJ2] PUNTUACIÓN PRIMERA FASE: ~1~ [BJM2_6:BANKJ2] PUNTUACIÓN SEGUNDA FASE: ~1~ [BJM2_7:BANKJ2] PUNTUACIÓN TOTAL DE DISPARO: ~1~ [BJM2_9:BANKJ2] ~g~Ve al punto de inicio de la segunda fase. [BJM2_11:BANKJ2] ~r~¡Phil está muerto! [BJM2_12:BANKJ2] ~r~¡Uno de los tiradores está muerto! [BJM2_14:BANKJ2] ~g~¡Pasa a la siguiente fase! [BJM2_15:BANKJ2] PUNTUACIÓN: [BJM2_17:BANKJ2] ~g~Ve y habla con Phil. [BJM2_18:BANKJ2] PUNTUACIÓN A SUPERAR: [BJM2_19:BANKJ2] ~g~¡Acierta a tantos blancos como puedas dentro del límite de tiempo! [BJM2_22:BANKJ2] ~r~¡Has abandonado el campo de tiro! [BJM2_23:BANKJ2] ~g~Si abandonas el campo de tiro durante la competición, fracasarás en la misión. [BJM2_24:BANKJ2] ~g~El objetivo más cercano vale un punto. [BJM2_25:BANKJ2] ~g~El objetivo del medio vale dos puntos. [BJM2_27:BANKJ2] ~g~Todos los objetivos de esta ronda valen un punto. [BNK2_2:BANKJ2] APUNTA, 3, 2, 1... ¡FUEGO! [BNK2_3:BANKJ2] ¡ZONA DESPEJADA! [BNK2_4:BANKJ2] ¡Yupiiiiiii! [BNK2_5:BANKJ2] ¡No podrías darle ni a un elefante a 2 metros! [BNK2_7:BANKJ2] Entonces, ¿quieres hacerme un favor y ayudarme a preparar un atraco? [BNK2_8:BANKJ2] Hijo, disparando de ese modo, si me pidieras que sea tu mujer, te diría que sí. [BNK2_9A:BANKJ2] Hijo, mejor será que te vayas y te metas tus ideas y palabrería donde te quepan. Eres una mierda disparando. [BNK2_9B:BANKJ2] Eres una mierda disparando. [BJM2_28:BANKJ2] PUNTUACIÓN RONDA TRES: ~1~ [BJM2_20:BANKJ2] ~g~¡La ronda se acabará cuando te quedes sin ~w~tiempo ~g~o ~w~munición~g~! [BJM2_26:BANKJ2] ~g~El objetivo más lejano vale tres puntos. [BNK2_1:BANKJ2] MUNICIÓN CARGADA [RANGE_1:BANKJ2] PUNTUACIÓN POR DISPARO: ~1~ [BJM2_2:BANKJ2] ~g~Para abandonar la ronda, pulsa ~h~~k~~PED_JUMPING~. [BJM2_N:BANKJ2] Tranqui. {=================================== MISSION TABLE BANKJ3 ===================================} [BJM3_A:BANKJ3] Las cosas empiezan a ir bien. [BJM3_B:BANKJ3] ¿Cuál es el plan, Tommy? ¿Qué pasa, amigo? [BJM3_C:BANKJ3] El plan es que tú sigas haciendo el imbécil. En fin, necesitamos a un conductor. [BJM3_D:BANKJ3] Tommy, yo lo haré. Sé conducir. [BJM3_E:BANKJ3] Necesitamos a Hilary, señor, no a un picapleitos charlatán. [BJM3_F:BANKJ3] Hilary es nuestra mejor opción. Seguro que no has visto nunca conducir a nadie tan rápido. Voy a darle un toque. [BJM3_G:BANKJ3] Eh, Hil, soy Phil. ¿Cómo te va? No, no hables. Recordaremos los viejos tiempos más tarde. ¿Quieres hacerme un favor? [BJM3_H:BANKJ3] Tengo a un tipo del norte... No, no, no creo que sirviese en el ejército, pero quiere un conductor. [BJM3_I:BANKJ3] Por un poco de acción. De acuerdo, comprendo. [BJM3_J:BANKJ3] ¿Qué dijo? [BJM3_K:BANKJ3] Bueno, lo hará, no hay problema. Bueno, podría haber un pequeño problema... Verás, tiene miedo al abandono. [BJM3_L:BANKJ3] Y parece que no trabajará para nadie que no pueda derrotarle. Tiene algo que ver con su madre o algo así. [BJM3_M:BANKJ3] De todas formas primero quiere echarte una carrera, dice que se encontrará contigo fuera... [BJM3_2A:BANKJ3] ¿Eres Tommy? Por supuesto que eres Tommy, quiero decir, [BJM3_2B:BANKJ3] ¿quién más querría hablar conmigo? [BJM3_2C:BANKJ3] Vale. Esto es lo que hay. [BJM3_2D:BANKJ3] Conduciré para ti solamente si me demuestras que conduces bien. [BJM3_2E:BANKJ3] Déjame tirado, y nunca te perdonaré. [BJM3_2:BANKJ3] ~r~¡Hilary está muerto! [BJM3_4:BANKJ3] ~g~¡Necesitas un coche para participar! [BNK3_1:BANKJ3] Vale. Te llevaré pero por favor, machácame un poco. [BNK3_3A:BANKJ3] Carrera callejera ilegal en progreso en Vice Point. [BNK3_3B:BANKJ3] Llamando a todos los agentes. [BNK3_3C:BANKJ3] Corredores callejeros, ¡esto es ilegal y no está permitido! {=================================== MISSION TABLE BANKJ4 ===================================} [BNK4_A:BANKJ4] ~w~Como podéis ver, caballeros, éste va a ser el dinero más fácil que hayamos ganado jamás. [BNK4_B:BANKJ4] ~w~Tommy, de verdad, tienes que plantearte seriamente el meterte en la abogacía. [BNK4_C:BANKJ4] ~w~Pero, ¿qué coño te estás fumando, tío? ¡Este no es un plan sencillo! [BNK4_D:BANKJ4] ~w~Bueno, de todas formas, ¿quién necesita un plan sencillo? [BNK4_E:BANKJ4] ~w~Mira el comunismo, bien, pues ese era un plan sencillo. Y jodió bien a Rusia ¿no? [BNK4_F:BANKJ4] ~w~Cálmate, ¿vale? Con un equipo como éste, no va a haber problemas. [BNK4_G:BANKJ4] ~w~Tenemos a Cam en la caja fuerte. ¿Phil? Tú y yo nos encargaremos de la seguridad y Hilary conducirá el coche de huída. [BNK4_H:BANKJ4] ~w~Uh, je, je, ¿no te olvidas de alguien? ¿Alguien que te ayudó siempre en esta ciudad? ¿Alguien...? [BNK4_I:BANKJ4] ~w~Ken... Ken, es cierto. Aquí Ken, él nos lavará el dinero y mantendrá las bebidas frías. [BNK4_J:BANKJ4] ~w~No comprendo qué se supone que debo hacer aquí. [BNK4_K:BANKJ4] ~w~Mira, es sencillo. ¿No has visto nunca una película? [BNK4_L:BANKJ4] ~w~Entramos en el banco, enseñamos las armas y nos marchamos siendo hombres muy ricos. [P_DEAD:BANKJ4] ~r~¡Phil está muerto! [C_DEAD:BANKJ4] ~r~¡Cam está muerto! [H_DEAD:BANKJ4] ~r~¡Hilary está muerto! [P_HIND:BANKJ4] ~r~¡Has perdido a Phil! [C_HIND:BANKJ4] ~r~¡Cam se ha quedado atrás! [H_HIND:BANKJ4] ~r~¡Hilary ha sido abandonado! [GETCAR:BANKJ4] ~g~¡Sube en el coche de escapada para hacer el trabajito del banco! [TRASHED:BANKJ4] ~r~¡DESTROZASTE EL COCHE DE FUGA! [BNK4_1:BANKJ4] Yo conduciré. [BNK4_2:BANKJ4] Genial. Un pasajero. Espera a que les hable de esto al grupo. [BNK4_3A:BANKJ4] ¡Eh, cuidado con las ruedas, Tommy! [BNK4_3B:BANKJ4] ¡Tommy, Hilary está ocupando mucho espacio! [BNK4_3C:BANKJ4] ¡No es verdad! [BNK4_3D:BANKJ4] ¡Sí! [BNK4_3E:BANKJ4] Eh, callaos los dos o iréis andando. [BNK4_3F:BANKJ4] Sí, Hilary. [BNK4_3I:BANKJ4] Por el amor de Dios, Phil, deja de agitar esa cosa. [BNK4_3J:BANKJ4] ¡Sí, le sacarás un ojo a alguien! [BNK4_3M:BANKJ4] ¡Mi pequeña! ¡Está hecha un desastre! [BNK4_3O:BANKJ4] Te aferras a la ilusión de una permanencia perpetua... [BNK4_3P:BANKJ4] ¿Qué? [BNK4_3Q:BANKJ4] Crees que todas las cosas durarán. [BNK4_3R:BANKJ4] La juventud, los seres queridos, la pizza... [BNK4_3S:BANKJ4] Todo pasará o terminará y debes aceptar eso. [BNK4_3T:BANKJ4] Sí, tienes razón. Gracias, Cam. [BNK4_3U:BANKJ4] No hay de que. [BNK4_3V:BANKJ4] Eh, Tommy, ¿por qué nos hemos detenido? [BNK4_4A:BANKJ4] ~w~Hilary, sigue conduciendo en torno a la manzana. [BNK4_5:BANKJ4] ~w~De acuerdo, Tommy, de acuerdo. [BNK4_6:BANKJ4] ~w~¡ESTO ES UN ATRACO! [BNK4_7:BANKJ4] ~w~¡QUE NADIE SE MUEVA! [BNK4_8:BANKJ4] ~w~¡TODO EL MUNDO CONTRA ESA PARED! [BNK4_9:BANKJ4] ¡Phil, quédate vigilando! [BNK4_10:BANKJ4] ¡No problemo! [BNK4_11:BANKJ4] Vamos, Cam, la cámara acorazada está arriba... [BK4_12A:BANKJ4] ¡Maldición! ¡Es una Flange 9000! [BK4_12B:BANKJ4] Podría tardar horas en abrirla, [BK4_12C:BANKJ4] o cinco minutos si encuentras al director. [BNK4_13:BANKJ4] Iré a averiguar en qué agujero se ha metido. [BK4_14A:BANKJ4] Phil, ¿van bien las cosas? [BNK4_15:BANKJ4] Sí. Todo está muy tranquilo. [BNK4_16:BANKJ4] ¡Tú, ven conmigo! [BNK4_17:BANKJ4] ¡De acuerdo! ¡De acuerdo! ¡Por favor, no dispare! [BNK4_18:BANKJ4] ¡DIJE QUE NO SE MOVIESE NADIE! [BK4_19A:BANKJ4] Tiene un cierre temporizado. [BK4_19B:BANKJ4] ¡Podríais rendiros ahora mismo! [BK4_20A:BANKJ4] Diablos, puedo hacer un puente en el cierre temporizado, [BK4_20B:BANKJ4] ¡Así que solo necesitaremos tu código de acceso y ya está! [BNK4_21:BANKJ4] Quédate aquí. Intenta algo y serás comida para los peces. ¿Lo captas? [BNK422A:BANKJ4] Cam, ¿cuánto tiempo? [BK4_23A:BANKJ4] ¡Dame cinco minutos! [BK4_24A:BANKJ4] Voy a comprobar qué tal va Phil, volveré. [BK4_24B:BANKJ4] ¡Te dije que no tocases esa alarma! [BNK4_25:BANKJ4] ¡Los de operaciones especiales estarán aquí en cualquier momento! [BNK4_27:BANKJ4] ¡Me vendría bien un poco de ayuda aquí, Tommy! [BNK4_28:BANKJ4] ¡SWAT de Vice City! ¡Estáis rodeados! [BNK4_29:BANKJ4] ¿Rodeados? [BNK4_30:BANKJ4] La están cagando, ¡bastardos corruptos! [BK4_31A:BANKJ4] ¡Tommy! ¡La cámara acorazada está abierta! [BK4_34A:BANKJ4] De acuerdo, tenemos el fondo de pensiones de los SWAT. ¡Salgamos de aquí! [BK4_34B:BANKJ4] ¡De acuerdo, vosotros lo habéis pedido! ¡Habéis tenido vuestra última oportunidad! [BK4_35A:BANKJ4] ¡Van a arrasar el lugar! [BK4_35B:BANKJ4] ¡A cubierto! [BNK4_94:BANKJ4] ~w~De acuerdo chicos. Ha sido tan fácil como lo habíamos planeado. [BM_DEAD:BANKJ4] ~r~¡Necesitas al director del banco vivo! [ASSET_A:BANKJ4] ¡ATRACO AL BANCO COMPLETADO! [ASSET_B:BANKJ4] ~g~El Club Malibú generará ahora unos ingresos máximos de hasta ~1~ $ al día. ¡Asegúrate de recaudarlos regularmente! [IDIOT:BANKJ4] ~r~ Eso es, ve por ahí vestido como un chiflado y llamando la atención, ¡IDIOTA! {=================================== MISSION TABLE BARON1 ===================================} [COK1_A:BARON1] Vamos nena, vamos, sí, ¡sí! [COK1_B:BARON1] ¡Estúpido caballo! ¡Te cortaré la cabeza! [COK1_C:BARON1] ¿Quién es este gilipollas? [COK1_D:BARON1] Tommy Vercetti, ¿me recuerdas? [COK1_E:BARON1] Perdóname, estoy un poco alterado. ¡Nunca te fíes de un maldito caballo! [COK1_F:BARON1] Hiciste un buen trabajo... ahora trabajas para mí. [COK1_H:BARON1] Como dije amigo, trabajas para mí. Ahora, cállate. Algún Judas me ha traicionado. [COK1_I:BARON1] Él cree que no sé cuando dinero debería sacar con esto, pero robar un 3% es tan malo como robar el 100%. [COK1_J:BARON1] Nadie me hace esto a mí. ¡NADIE! [COK1_K:BARON1] ¡Le sigues desde su apartamento y ves a dónde va! Más tarde, le mataremos. [COK1_1:BARON1] ¡Mierda! [COK1_2:BARON1] ¡Demasiado lento, abuelo! [COK1_4:BARON1] Perdedor. [COK1_5:BARON1] ¡Mejor sigue corriendo, gilipollas! [COK1_8:BARON1] ~g~¡Rápido! ¡Consigue un vehículo y síguele! [COK1_9:BARON1] ~r~¡Se supone que debes seguirle, no matarle! [COK1_10:BARON1] ~r~Ve a la casa del ladrón y descubre dónde esconde el dinero. [COK1_11:BARON1] ~g~Echa un vistazo por su ventana. [COK1_7:BARON1] ~g~¡Se ha escapado por el tejado, ve tras él pero no le mates! [COK1_G:BARON1] Trabajo por dinero. {=================================== MISSION TABLE BARON2 ===================================} [COK2_A:BARON2] ¿Qué clase de tonto incompetente eres? [COK2_B:BARON2] ¡TONTO! ¡TONTO! ¡TONTO! ¡TONTO! [COK2_C:BARON2] Tommy... [COK2_D:BARON2] ¿Qué pasa, Ricardo? [COK2_E:BARON2] Estos idiotas... Siempre intentan joderle a uno. [COK2_F:BARON2] Ese es el problema de este negocio. [COK2_G:BARON2] ¿Qué crees que estás haciendo? [COK2_H:BARON2] Estos mamones me han fallado miserablemente. [COK2_I:BARON2] Pronto cualquier madre o padre creerán que pueden vender farlopa en Vice City. [COK2_J:BARON2] ¿Y ahora qué, eh? ¿La apestosa Mafia? [COK2_K:BARON2] Ese antro de mafiosos es una fortaleza inexpugnable desde el suelo, [COK2_L:BARON2] así que Quentin... ¡Quentin! ¡QUENTIN! [COK2_M:BARON2] ¡Volará sobre la zona! [COK2_N:BARON2] ¡Hacedles desaparecer! [COK2_O:BARON2] ¿Qué crees que estás haciendo? [COK2_P:BARON2] ¿Qué haces aquí? [COK2_Q:BARON2] He estado haciendo preguntas por ahí, y es obvio [COK2_R:BARON2] que Diaz se saltó el trato y se cargó a mi hermano. [COK2_S:BARON2] ¡Y te matará a ti también! [COK2_T:BARON2] ¡Puedo encargarme de Diaz! [COK2_U:BARON2] No. ¡Escúchame! Yo me encargaré de Diaz... [COK2_V:BARON2] Está empezando a confiar en mí. [COK2_1:BARON2] Hay una cosa que me mosquea... ¿por qué Quentin? [COK2_2:BARON2] No sé, siempre me ha gustado... Quentin Vance... [COK2_3:BARON2] ¿Vance? ¿Tu nombre es Lance Vance? [COK2_4:BARON2] ¡Eh! ¡Ya tuve bastante con eso en la escuela! [COK2_5:BARON2] ¿Alguna vez has disparado uno de esos desde un helicóptero? [COK2_8:BARON2] En fin, ¿a dónde nos dirigimos? [COK2_9:BARON2] A Prawn Island. [COK2_13:BARON2] Lance Vance. Pobre desgraciado. [COK2_14:BARON2] Bien, casi hemos llegado. [COK2_15:BARON2] Daremos un par de pasadas. [COK2_16:BARON2] Así que elimina a tantos como puedas. [COK2_17:BARON2] Después te dejaré abajo y te las arreglarás solo. [COK2_20:BARON2] ¡Mierda! ¡Ésto es una zona de guerra! ¡Elimina a algunos de esos pistoleros! [COK2_21:BARON2] ¡Nos están alcanzando, tío! [COK2_22:BARON2] ¡Arreglar esta cosa no sale barato! ¡Elimínalos! [COK2_23:BARON2] Vale, de ahora en adelante es cosa tuya... ¡Buena suerte, hermano! [COK2_24:BARON2] Helicóptero: [COK2_25:BARON2] ~g~Ve y recoge el dinero del tejado. [COK2_27:BARON2] ¡Estás en mi territorio, gilipollas! [COK2_28:BARON2] ¡Vas a desaparecer! [COK2_6:BARON2] No. Practicaré un poco por el camino. [OPEN_B:BARON2] Los controles policiales con dirección a la península han sido levantados {=================================== MISSION TABLE BARON3 ===================================} [COK3_A:BARON3] Ahora ya no os sentís tan complacidas, ¿eh? [COK3_B:BARON3] ~n~ [COK3_C:BARON3] ¡Guau! Mira hacia donde mueves esa cosa. [COK3_D:BARON3] Ya no habrá más mierda de paloma en mi coche, ¿eh, Tommy? [COK3_E:BARON3] Supongo que no. [COK3_F:BARON3] Tienes toda la razón. Ahora escucha, [COK3_G:BARON3] ¿sabes quién posee el barco más rápido de la costa este? [COK3_H:BARON3] Ni idea. [COK3_I:BARON3] Yo. Y quiero que siga siendo así. [COK3_J:BARON3] Cada traficante de aquí a Caracas tiene un sueño, un barco más rápido. [COK3_K:BARON3] Se rumorea que el astillero acaba de terminar un barco así [COK3_L:BARON3] para algún mamón costarricense. [COK3_M:BARON3] Y Tommy... ¡QUIERO ESE BARCO! [COK3_N:BARON3] Creo que han vuelto tus palomas. [COK3_O:BARON3] ¡Ah! Pensé que te tenía. ¿De dónde has salido? [COK3_P:BARON3] ¡Palomas! ¡Boom! ¡Aaaaah! [COK3_5:BARON3] ~g~Localiza el interruptor para bajar el barco. [COK3_6:BARON3] ~g~Lleva el barco a la mansión. [COK3_7:BARON3] ~r~¡Destruiste el barco! [COK3_8:BARON3] ~g~Ve al astillero, en los muelles y roba el barco más rápido. [COK3_9:BARON3] ~g~Ahora sube al barco. {=================================== MISSION TABLE BARON4 ===================================} [COK4_A:BARON4] ¡Expúlsala! ¡MIERDA DE PLÁSTICO! [COK4_B:BARON4] ¿Por qué me haces esto? [COK4_C:BARON4] ¿Quién te crees que eres, maldita mierda de plástico? [COK4_D:BARON4] ¡QUE TE JODAN! [COK4_E:BARON4] Se traga mi película favorita de El burro, ¡se muere! [COK4_F:BARON4] ¿Qué más podría haber hecho? [COK4_G:BARON4] Probablemente no estaba enchufado. [COK4_H:BARON4] ¿Qué? [COK4_I:BARON4] Maldición... No importa, puedo comprar cien más. [COK4_J:BARON4] Ahora, Tommy, [COK4_K:BARON4] cada mes un camello autónomo navega hasta Vice City y amarra su yate. [COK4_L:BARON4] Vende su partida al primer barco. [COK4_M:BARON4] Quiero que cojas la lancha motora [COK4_N:BARON4] y que elimines a todos los otros cabrones, [COK4_O:BARON4] luego me traes aquí el alijo, ¿entendido? [COK4_P:BARON4] Déjame adivinar, pensaste que me podía venir bien un ángel guardián. [COK4_Q:BARON4] Solo digo que tienes que dejarme participar, tío. [COK4_R:BARON4] Ahora ya puedes contarme toda esa basura del 'chico duro solitario', [COK4_S:BARON4] pero sé que un día te voy a salvar el culo [COK4_T:BARON4] ¡y probablemente querrás darme un beso! [COK4_U:BARON4] Colgado. [COK4_V:BARON4] ~n~ [COK4_1:BARON4] Así que Tommy, sabemos que fue Díaz quien jodió nuestro trato... [COK4_3:BARON4] Así que, ¿por qué coño vamos por ahí haciéndole recados? [COK4_4:BARON4] Cuanto más aprendamos ahora, ¡menos tendremos que aprender cuando tomemos el control de esta ciudad! [COK4_5:BARON4] Me encanta tu estilo, tío. Muy fresco. [COK4_12:BARON4] ¡Cuidado, vienen de todas partes! [COK4_13:BARON4] Ya está. ¡Dirígete hacia Díaz tan rápido como puedas! [COK4_14:BARON4] ¿Queréis un poco de esto? [COK4_15:BARON4] ¡A dormir con los peces! [COK4_16:BARON4] ¡Comed! ¡COMED! [COK4_19:BARON4] ¡Más problemas ahí adelante! [COK4_20:BARON4] ¡Hay más pistoleros en el malecón! [COK4_24:BARON4] Buen disparo, amigo mío. Eres un verdadero y auténtico lunático de categoría A. [COK4_25:BARON4] Bueno, gracias. [COK4_26:BARON4] Nos vemos, Tommy. [COK4_27:BARON4] Vale, Sr. Lance Vance. [COK4_28:BARON4] ~g~¡Ve hasta el yate antes de que lleguen los otros barcos! [COK4_31:BARON4] ~g~¡Ve al barco más rápido del embarcadero! [COK4_32:BARON4] ~r~¡Demasiado lento! [COK4_33:BARON4] ~r~¡Has destruido el barco! [COK4_34:BARON4] ~g~¡Hunde esos barcos! [COK4_35:BARON4] Estado del barco: {=================================== MISSION TABLE BARON5 ===================================} [PROP_A:BARON5] ¡PROPIEDAD ADQUIRIDA! [COK4_30:BARON5] ~r~¡Lance está muerto! [ASS1_A:BARON5] He conseguido unos juguetes nuevos. Están en el maletero. [ASS1_B:BARON5] ¡Mierda! ¿Dónde conseguiste todo este material? [ASS1_C:BARON5] Lo he estado guardando para un momento como éste. [ASS1_D:BARON5] ¿Te gusta? [ASS1_E:BARON5] Sí, me gusta. [ASS1_F:BARON5] Estúpidos mamones... [ASS1_G:BARON5] Mi hermosa casa. [ASS1_H:BARON5] ¡Mirad lo que le habéis hecho! [ASS1_I:BARON5] ¡Esto es por mi hermano! [ASS1_J:BARON5] Confiaba en ti, Tommy. [ASS1_K:BARON5] Te hubiese convertido en... [ASS1_L:BARON5] Diga buenas noches, Sr. Díaz. [ASS1_1:BARON5] Este lugar va a estar hasta arriba de capullos... ten cuidado... [ASS1_2:BARON5] No te preocupes Tommy, te cubro. [ASS1_13:BARON5] ¡DÍAZ! ¡He venido a encargarme de tu negocio! [ASS1_14:BARON5] ¡TOMMY! Me has traicionado... ¡Imbécil! Voy a acabar contigo muy pronto... [ASS1_15:BARON5] ~g~¡Arrasa esa mansión y mata a Diaz! [ASS1_16:BARON5] ~g~¡Mata a Diaz! [ASS1_17:BARON5] ~g~Hay múltiples caminos para entrar en la mansión. [BUD1:BARON5] Lance [ASS1_18:BARON5] ~g~La puerta está cerrada con llave, intenta otra ruta. [ASS1_19:BARON5] ¡Por aquí! [ASS1_20:BARON5] ¡Tommy, mi problema es con Quentin, no contigo, tío! {=================================== MISSION TABLE BIKE1 ===================================} [BM1_A:BIKE1] ¿Dónde está Baker? [BM1_B:BIKE1] Estoy buscando al gran Mitch Baker... [BM1_C:BIKE1] ¿Quién le busca? [BM1_D:BIKE1] Tommy Vercetti. [BM1_E:BIKE1] Vercetti. [BM1_F:BIKE1] No pareces policía, así que eso te concede un minuto. [BM1_G:BIKE1] Habla rápido. [BM1_H:BIKE1] Kent Paul dijo que podrías estar interesado en llevar la seguridad de una gira que él ha organizado. [BM1_I:BIKE1] ¿Kent Paul? No me extraña que te enviase en su lugar. [BM1_J:BIKE1] La última vez que estuvo aquí, se marchó por la ventana sin nada, tal cual le trajo su madre al mundo. [BM1_K:BIKE1] ¿Estás interesado o no? [BM1_L:BIKE1] Sólo hacemos favores a los nuestros. [BM1_M:BIKE1] ¿Cómo puedo unirme a vosotros? [BM1_N:BIKE1] Este no es un club de campo, chico. ¿Sabes montar en moto? [BM1_O:BIKE1] ¿Sabes sentarte en un taburete y beber? [BM1_P:BIKE1] Cougar, Zeppelin, id a ver cómo se le da la moto a esta nenaza... [BM1_2:BIKE1] ~g~¡Necesitas una Freeway o una Ángel para competir! [BM1_3:BIKE1] ~r~¡Los corredores han sido atacados! [BIKE1_1:BIKE1] Bien, modelitos caros. Veamos qué puedes hacer. [BM1_1:BIKE1] ~g~Consigue una Freeway o una Ángel y ve a la parrilla de salida. {=================================== MISSION TABLE BIKE2 ===================================} [BM2_1:BIKE2] CAOSMETRO: [BM2_A:BIKE2] Te he vuelto a ganar. [BM2_B:BIKE2] Eh, Vercetti. [BM2_C:BIKE2] Cougar dice que sabes manejar muy bien una moto. [BM2_D:BIKE2] Sí, ¿cuántos recados más voy a tener que hacer? [BM2_E:BIKE2] Soy un hombre muy ocupado. [BM2_F:BIKE2] Si es un combate lo que va arreglarlo todo, entonces adelante. [BM2_G:BIKE2] Ser uno de nosotros no va solo de fanfarronear. Va de ser parte de una familia. [BM2_H:BIKE2] Sí, he sido parte de una familia antes. No funcionó. [BM2_I:BIKE2] Sí, seguro, pero esta familia cuida de los suyos. [BM2_J:BIKE2] No le pedimos a un hombre que haga el trabajo sucio y luego le dejamos pasar quince años de sequía. [BM2_K:BIKE2] Sí, es verdad. He hecho mis deberes. [BM2_L:BIKE2] Ésta de aquí es la familia más grande de inadaptados, rebeldes y tipos duros. [BM2_M:BIKE2] Diablos, algunos de nosotros incluso hemos sido traicionados por nuestro propio país. [BM2_N:BIKE2] Estuve preso durante Vietnam. Un tema desagradable. [BM2_O:BIKE2] Por eso es por lo que voy a pedirte que vayas a armar jaleo. [BM2_P:BIKE2] Este maldito país necesita una patada en el culo, y nosotros se la vamos a dar. [BM2_Q:BIKE2] ¡Así que sal de ahí, pilla una moto, demuestra a esta ciudad lo cabreado que estás! [BM2_R:BIKE2] De acuerdo. [BM2_2:BIKE2] ~g~¡Debes llenar el Caosmetro en el tiempo determinado para demostrarnos lo duro que eres! [BM2_3:BIKE2] ~g~Este sonido indica que has llenado una parte del contador, continúa así. [BM2_4:BIKE2] ~r~¡Has fracasado en llenar el Caosmetro a tiempo! {=================================== MISSION TABLE BIKE3 ===================================} [BM3_A:BIKE3] Hola, Mitch. [BM3_B:BIKE3] Bueno, si es el ''tipo duro'' Vercetti. [BM3_C:BIKE3] Ahora quiero ver lo bien que sabes luchar por tu territorio. [BM3_D:BIKE3] Una banda callejera local cometió el error de robar mi moto, [BM3_E:BIKE3] probablemente para hacerse los machos o algo así. [BM3_F:BIKE3] Los chicos y yo iríamos a darles una lección sobre el respeto a las propiedades ajenas y todo eso. [BM3_G:BIKE3] En fin... [BM3_H:BIKE3] Entonces empecé a pensar que esto sería una buena iniciación para ti. [BM3_I:BIKE3] Devuélveme mi moto y puedes decirle a Paul que tiene su seguridad. [BM3_2:BIKE3] ~r~¡Se suponía que debías traer de vuelta la moto, no destruirla! [BM3_3:BIKE3] ~g~¡Lleva la moto de vuelta al bar! [BM3_4:BIKE3] ~g~¡Sube a la moto! [INTRUDE:BIKE3] ~g~¡Te han visto! [BM3_6:BIKE3] ~g~Están refugiados detrás de Ammu-Nation en la zona del centro. [BM3_7:BIKE3] ~g~Necesitarás una moto rápida para conseguir acceder al tejado. [BM3_8:BIKE3] ~g~Utiliza la moto para saltar desde esas escaleras hasta el tejado que hay en el extremo opuesto de la carretera. [BM3_1:BIKE3] ~g~Una banda local ha robado la Ángel de Mitch Baker. ¡Recupérala! [BM3_9:BIKE3] ~g~¡Recupera la Ángel de Mitch y sal de ahí! {=================================== MISSION TABLE BMX_1 ===================================} [GETBIK2:BMX_1] ¡Tienes ~1~ segundos para subirte a una moto de motocross! {=================================== MISSION TABLE BOATBUY ===================================} [DRUG_1:BOATBUY] ¿Hola? [DRUG_2:BOATBUY] Apágalo. Hay un tipo ahí afuera. [DRUG_3:BOATBUY] ¡Eh, el tipo del traje! Supongo que eres el nuevo propietario. [DRUG_4:BOATBUY] Sí. ¿Cuál de los barcos es el más rápido? [DRUG_5:BOATBUY] Ya está en el agua, tío, [DRUG_6:BOATBUY] Pensé que querrías probarlo. [DRUG_7:BOATBUY] Tío, funciona con un motor de 300 caballos... [DRUG_8:BOATBUY] y el casco es de fibra de vidrio, ¡sale disparado por las olas! [DRUG_9:BOATBUY] Puede ponerse de cero a noventa tan sólo en cuatro segundos, tío... [DRUG_10:BOATBUY] ¡y puede llevar como veinte contenedores de la mejor hierba jamaicana justo en el casco! [DRUG_11:BOATBUY] Así que adelante, tío, ¡está preparada para volar! [DRUG_12:BOATBUY] Tipo trajeado, ¿tienes fuego? [DRUG_13:BOATBUY] ¿Colega? {=================================== MISSION TABLE CAP_1 ===================================} [CAP1_B1:CAP_1] ~g~La mafia está imponiendo impuestos a tus negocios. Encuéntrales y mátales. [CAP1_B2:CAP_1] ~g~¡La mafia ha saqueado el astillero! [CAP1_B3:CAP_1] ~g~¡La mafia ha saqueado la fábrica de helados! [CAP1_B4:CAP_1] ~g~¡La mafia ha saqueado el salón de autos! [CAP1_B5:CAP_1] ~g~¡La mafia ha saqueado la compañía de taxis! [CAP_01:CAP_1] ¿Cuál es la emergencia? [CAP_02:CAP_1] ¿QUIÉN? [CAP_03:CAP_1] Tommy... unos matones mafiosos... dijeron que vendrían a recoger su parte... [CAP_04:CAP_1] Dijeron que era dinero del Sr. Forello... me siento una basura. [CAP_05:CAP_1] ¿Forelli? ¿SONNY Forelli? [CAP_06:CAP_1] Sí, ése es el tipo... creo... insistieron mucho... [CAP_07:CAP_1] No te preocupes, abuelo, no estoy enfadado contigo. [CAP_08:CAP_1] Llevadle al hospital. [CAP_09:CAP_1] Tommy... rájale un nuevo culo a ese tipo por mí... [CAP_10:CAP_1] Voy a rajarle en dos. [CAP1_2:CAP_1] ¡Conoces las reglas, Vercetti! [CAP1_3:CAP_1] ¡El Sr. Forelli envía sus saludos! [CAP1_4:CAP_1] ¡Es el carnicero de Harwood! [CAP1_5:CAP_1] Decidle a Sonny... ¡Que permanezca alejado! [CAP1_6:CAP_1] Vice City ahora es MÍA, NO suya. [CAP1_7:CAP_1] ¿Crees que puedes eliminarme, Vercetti? [CAP1_8:CAP_1] Seguiremos yendo tras de ti hasta que mueras, Vercetti. [CAP1_9:CAP_1] No tienes ni una oportunidad, mamón psicótico. [CAP1_10:CAP_1] Te mataré, Vercetti. [CAP1_11:CAP_1] Siempre fuiste un gilipollas. [CAP1_12:CAP_1] Vas a morir, Vercetti. [CAP1_B6:CAP_1] ~g~Has encontrado al cobrador, remátale. [CAP1_B7:CAP_1] ~g~Has perdido al cobrador. [CAP1_B8:CAP_1] ~r~El cobrador ha saqueado todos tus negocios. [CAP1_B9:CAP_1] ~g~¡La mafia ha saqueado a El Malibu! [CAP1_B0:CAP_1] ~g~¡La mafia ha saqueado al estudio cinematográfico! [CAP1_C2:CAP_1] ~g~¡La mafia ha llegado al astillero! [CAP1_C3:CAP_1] ~g~¡La mafia ha llegado a la fábrica de helados! [CAP1_C4:CAP_1] ~g~¡La mafia ha llegado al concesionario de coches! [CAP1_C5:CAP_1] ~g~¡La mafia ha llegado a la compañía de taxis! [CAP1_C9:CAP_1] ~g~¡La mafia ha llegado al Malibú! [CAP1_C0:CAP_1] ~g~¡La mafia ha llegado al estudio cinematográfico! [CAP1_D2:CAP_1] ~g~¡La mafia abandona el astillero! [CAP1_D3:CAP_1] ~g~La mafia abandona la fábrica de helados! [CAP1_D4:CAP_1] ~g~¡La mafia abandona el concesionario de coches! [CAP1_D5:CAP_1] ~g~¡La mafia abandona la compañía de taxis! [CAP1_D9:CAP_1] ~g~¡La mafia abandona el Malibú! [CAP1_D0:CAP_1] ~g~¡La mafia abandona el estudio cinematográfico! [CAP1B10:CAP_1] Has puesto límite a los recaudadores. Vienen más de camino. {=================================== MISSION TABLE CARBUY ===================================} [CAR1_1:CARBUY] B.J. Smith. Y usted debe ser el Sr. Vercetti. [CAR1_2:CARBUY] ¿Le gustaría dar una vuelta? [CAR1_3:CARBUY] Podría ser. [CAR1_4:CARBUY] Estoy muy triste por vender el negocio. [CAR1_5:CARBUY] Esta fue mi primera inversión después de convertirme en profesional. [CAR1_6:CARBUY] Pero es hora de que me mude. [CAR1_7:CARBUY] ¿Deja la ciudad? [CAR1_8:CARBUY] No demasiado deprisa, espero. [CAR1_9:CARBUY] No. Me estoy retirando pero simplemente para preparar mi regreso. [CAR1_10:CARBUY] El negocio no era demasiado fuerte, [CAR1_11:CARBUY] y mi equipo quizás se puso demasiado [CAR1_12:CARBUY] creativo con la generación de la riqueza. [CAR1_13:CARBUY] Obviamente, habría podido desmantelar el negocio antes de cederlo. [CAR1_14:CARBUY] Diablos, podría haber quemado el sitio si hubiese querido. [CAR1_15:CARBUY] Esta es tierra para una urbanización de primera. [CAR1_16:CARBUY] Oh, yo no me preocuparía por nada de eso. [CAR1_17:CARBUY] Este lugar parece perfecto. [CAR1_18:CARBUY] Sí, así es, ¿así que entiendo que tenemos un trato? {=================================== MISSION TABLE CARPAR1 ===================================} [MM_1_A:CARPAR1] ~g~Atraviesa ~y~5 puntos de control ~r~¡SIN ~g~aplastar ningún ~r~CONO! ~g~Puedes atravesarlos en ~y~CUALQUIER ORDEN. [CONE_1:CARPAR1] ~r~¡Aplastaste un cono! [MM_1_C:CARPAR1] ~y~PASA POR~g~ un punto de control para activar el temporizador. ~g~Cada punto de control te dará ~y~~1~ SEGUNDOS~g~. {=================================== MISSION TABLE COPCAR ===================================} [KILLS:COPCAR] MUERTES: [C_BREIF:COPCAR] ~g~Sospechoso visto por última vez en la zona de ~a~. [C_PASS:COPCAR] AMENAZA ELIMINADA: ~1~ $ [COPCART:COPCAR] ~g~Tienes ~1~ segundos para volver al vehículo de policía antes de que termine la misión. [C_CANC:COPCAR] ~r~¡Misión de justiciero cancelada! [C_TIME:COPCAR] ~r~¡Tu tiempo como representante de la ley se ha terminado! [C_COMP1:COPCAR] Has completado el nivel 12 de la misión de justiciero: tu armadura personal máxima ha aumentado a 150. [CLEVEL:COPCAR] Misión de justiciero nivel ~1~ {=================================== MISSION TABLE COUNT1 ===================================} [CM1_A:COUNT1] ¿Sr. Vercetti? ¿Comprando la vieja imprenta? [CM1_B:COUNT1] Sí, mi viejo solía trabajar con ellas. [CM1_C:COUNT1] Iba a seguirle en el negocio, pero... viví una vida diferente. [CM1_D:COUNT1] ¿Planea vender la vieja maquinaria o destruirla? [CM1_E:COUNT1] Estoy pensando que podíamos imprimir algo, un periódico, una revista... [CM1_F:COUNT1] Oh, mierda, hijo, todo eso es mierda de baja calidad. Siempre me ha apetecido imprimir dinero. No es tan difícil. [CM1_G:COUNT1] Ya sabes, llevo haciéndolo a pequeña escala durante años. [CM1_H:COUNT1] ¿De verdad? [CM1_I:COUNT1] Claro. Pero necesitamos unas planchas de buena calidad. [CM1_J:COUNT1] ¡Por supuesto! Hay un sindicato de la falsificación que opera ya en Florida. [CM1_K:COUNT1] ¿Un sindicato? [CM1_L:COUNT1] Sí. He escuchado algunos rumores. [CM1_M:COUNT1] Conozco a un tipo que es bueno con los rumores... [CM1_N:COUNT1] Solía pasar las tardes con él, limpiando los rodillos... [CM1_2A:COUNT1] ¡Menudo trasero! [CM1_2B:COUNT1] ¡Vale chica, tú te lo pierdes! [CM1_2C:COUNT1] Buenas, figura, ¿cómo va todo? [CM1_2D:COUNT1] ¿Qué sabes sobre falsificación? [CM1_2E:COUNT1] Estoy bien, Paul, ¿qué tal tú? [CM1_2F:COUNT1] ¡Ven aquí! [CM1_2G:COUNT1] ¡Vale! ¡Vale! Obviamente eres un hombre ocupado. [CM1_2H:COUNT1] Todo lo que sé sobre los asuntos turbios es que las tríadas suministran las planchas. [CM1_2I:COUNT1] Tienen una compañía de envíos en los muelles, [CM1_2J:COUNT1] ¡el jefe sabrá cuándo vienen las próximas planchas! [CM1_2K:COUNT1] Gracias, Paul. [CM1_2L:COUNT1] ¡Cuál es tu problema, lunático! [CM1_2M:COUNT1] ¡Ponme otra copa, pronto! [CM1_3:COUNT1] ~g~¡Te han visto! [CM1_5:COUNT1] ~g~¡Ve a encontrarte con Kent Paul en el Club Malibú! [CNT1_1:COUNT1] ¿Quién eres tú? ¡En la cara no! ¡En la cara no! [CM1_1:COUNT1] ~g~Ve al barco de la línea charter Libertine en el muelle. [CM1_2:COUNT1] ~g~El encargado de los envíos tendrá toda la información necesaria. [CNT1_2:COUNT1] Vale, ¡hablaré! ¡hablaré! [CM1_6:COUNT1] ~g~¡Lleva toda la información de vuelta a la imprenta! {=================================== MISSION TABLE COUNT2 ===================================} [CNT2_B1:COUNT2] De acuerdo, hoy el mensajero moverá las planchas de los muelles. [CNT2_B2:COUNT2] Voy a interceptarles, coger las planchas, deshacerme de las pruebas, y volver aquí. [CNT2_B3:COUNT2] Dependiendo de lo bien que vaya esto, [CNT2_B4:COUNT2] podemos tener cinco minutos para imprimir el dinero antes de que el sindicato de falsificación nos encuentre, o puede que tengamos todo el año. [CNT2_B5:COUNT2] De cualquier modo, quiero que los verdes salgan de las prensas cinco minutos después de que vuelva. ¿Entendido? [CNT2_B6:COUNT2] No te preocupes, Tommy. Estaremos preparados. [CNT2_B7:COUNT2] Los otros chicos y yo estaremos por aquí en el vecindario, en caso de que necesites que nos ocupemos de cualquier problema. [CNT2_B8:COUNT2] De acuerdo, ¿todo el mundo está tranquilo? De acuerdo. Os veré luego... [CNT2_01:COUNT2] ~g~El ~r~mensajero~g~ con las planchas de falsificación llegará en cualquier momento a los ~y~muelles~g~ en un helicóptero. [CNT2_02:COUNT2] ~r~El mensajero de las planchas ha huido en el helicóptero. [CNT2_03:COUNT2] ~r~¡El mensajero ha llegado a su destino sano y salvo, llegas demasiado tarde! [CNT2_04:COUNT2] ~r~¡Has destruido las planchas en la explosión! [CNT2_05:COUNT2] ~g~Tienes las planchas de falsificación. Llévalas a la imprenta. [CNT2_06:COUNT2] ~g~El mensajero ha muerto y ha dejado caer las planchas, recógelas antes de que lo haga otro. [CNT2_07:COUNT2] ~g~Una de las guardias ha recogido las planchas, no la dejes escapar. [CNT2_08:COUNT2] ~g~El ~r~mensajero~g~ ha llegado a los muelles con las planchas. [CNT2_4:COUNT2] Negocio privado. ¡No eres bienvenido! [CNT2_09:COUNT2] IMPRENTA CONSOLIDADA [CNT2_10:COUNT2] ~g~La imprenta generará ahora unos ingresos hasta un máximo de ~1~ $. Asegúrate que lo recaudas de manera regular. [CNT2_11:COUNT2] ~r~¡Las planchas están en el fondo del mar! {=================================== MISSION TABLE CUBAN1 ===================================} [CUB1_A:CUBAN1] ¿Sí, tío? [CUB1_B:CUBAN1] Eh, tranquilo papito, este hombre es para mí. Tú, ¿tú eres el chico? [CUB1_C:CUBAN1] Oh, sí. Tú eres el chico. Eso creo, ¿sabes? [CUB1_D:CUBAN1] No. No creo que lo sepa. [CUB1_E:CUBAN1] ¿Oh, sí? Ven aquí, tipo duro. [CUB1_F:CUBAN1] ¿Crees que puedes desafiarme? [CUB1_G:CUBAN1] ¿Crees que puedes jugar a ese estúpido juego conmigo? [CUB1_H:CUBAN1] No, creo que ya lo estás haciendo tú lo suficientemente estúpido por los dos. [CUB1_I:CUBAN1] Eh, te llamó tonto, hijo. [CUB1_J:CUBAN1] Y yo le llamé niñita, papá. [CUB1_K:CUBAN1] Mírale, vestido así. [CUB1_L:CUBAN1] ¿Qué es esto, un traje de noche? [CUB1_M:CUBAN1] ¿Eres alguna clase de tipo duro y vistes como una mujer? [CUB1_N:CUBAN1] También llevas pantis como una mujer, ¿eh? [CUB1_O:CUBAN1] ¿Qué tienes contra las mujeres? ¿Prefieres a los hombres, grandullón? [CUB1_P:CUBAN1] ¡Me gustan las mujeres! ¡Me gustan las mujeres! ¡Amo a mi madre, chico! [CUB1_Q:CUBAN1] De acuerdo, de acuerdo. Aceptaré tu palabra. [CUB1_R:CUBAN1] ¿Sabes conducir, amigo? [CUB1_S:CUBAN1] Sí... como una mujer. [CUB1_T:CUBAN1] Muy gracioso. Me gustas, grandullón. Quizás puedas ayudarme. [CUB1_U:CUBAN1] Quizás puedas demostrar que eres un hombre. ¿Eh? [CUB1_V:CUBAN1] Saca el barco. [CUB1_W:CUBAN1] Demuéstrame que tienes un par de cojones, [CUB1_X:CUBAN1] y no unos chiquitos de ratón. [CUB1_02:CUBAN1] De acuerdo, tío, trátalo como a una mujer. [CUB1_03:CUBAN1] No está mal, un hombre de verdad. [CUB1_04:CUBAN1] Tío, tienes unas pelotas bien grandes, amigo. [CUB1_05:CUBAN1] Amigo, eres un tío. [CUB1_06:CUBAN1] ¿Puedes decir que eres un machote, tío? [CUB1_07:CUBAN1] Eres un ratón asustado, niñito, vete a llorar con tu mamá. [CUB1_08:CUBAN1] Eres una bazofia. Andas como un tío, hablas con un tío, pero conduces como un idiota. [CUB1_09:CUBAN1] Brother, eres un macho. Me gustas, tío. Me gustas un montón. [CUB1_10:CUBAN1] En cualquier momento, tío. Porque tienes un par. Y todos mis amigos tienen un buen par de cojones. [CUB1_11:CUBAN1] ~r~¡Mataste a Rico! [CUB1_12:CUBAN1] Atraviesa el primer punto de control para comenzar la prueba. [CUB1_14:CUBAN1] ¡Vuelve al bote! [CUB1_15:CUBAN1] ~r~Brother, eres demasiado lento. [CUB1_01:CUBAN1] Eh, soy Rico. ¿Eres tú el tío de las pelotas grandes? [CUB1_13:CUBAN1] ~g~Tienes tres minutos para recorrer el circuito. {=================================== MISSION TABLE CUBAN2 ===================================} [CUB2_A:CUBAN2] Un cafetito, por favor, Alberto... [CUB2_N:CUBAN2] Sin problema, Tommy. [CUB2_B:CUBAN2] ¡Papá! ¡Un gran problema! [CUB2_O:CUBAN2] Umberto, mi hijo, ¿qué ocurre? [CUB2_C:CUBAN2] ¡Los haitianos! ¡Odio a estos haitianos! [CUB2_D:CUBAN2] ¡Me han molestado por última vez! [CUB2_E:CUBAN2] ¡Nos desharemos de ellos! [CUB2_F:CUBAN2] Sólo necesitamos un poco de apoyo. [CUB2_G:CUBAN2] Ya he perdido algunos hermanos ahí fuera. [CUB2_H:CUBAN2] ¡Amigo, tú conduces bien! [CUB2_I:CUBAN2] Para ser una mujer. ¿No? [CUB2_J:CUBAN2] ¡Éste no es momento de bromear! [CUB2_K:CUBAN2] ¡Vamos, maneja de nuevo para mí! [CUB2_L:CUBAN2] ¡Lleva a mis chicos allí y eliminaremos a esos haitianos! [CUB2_M:CUBAN2] ¡Se meten conmigo y se meten con el chico grande de la ciudad! [CUB2_01:CUBAN2] No hay suficiente espacio, brother, necesitas un coche más grande. [CUB2_02:CUBAN2] ¡Necesitamos refuerzos del café! [CUB2_03:CUBAN2] ~g~Consigue un coche y recoge a los cubanos frente al Café Robina. [CUB2_04:CUBAN2] ~g~Ve y deja a los cubanos en la zona de combate. [CUB2_05:CUBAN2] ¡Elimina a ese francotirador cobarde! [CUB2_07:CUBAN2] ¡Luchan como nenas! ¡Ponte a cubierto! [CUB2_09:CUBAN2] ¡Francotirador en el tejado! [CUB2_11:CUBAN2] ~r~Estúpido, necesitábamos ese coche. [CUB2_12:CUBAN2] ¡Eh, amigo! ¡Me alegro de que hayas podido venir! [CUB2_13:CUBAN2] ¡Apestoso nido de haitianos, vamos a matarlos a todos! [CUB2_14:CUBAN2] ¡A LA CARGA! [CUB2_15:CUBAN2] ¡Ahora, hermanos, A LA CAAARGA! [CUB2_16:CUBAN2] ¡Tommy, hemos demostrado nuestro valor y nuestra hombría! [CUB2_17:CUBAN2] ¡Vamos a robar esa furgoneta llena de farlopa y pegarnos el piro! [CUB2_18:CUBAN2] ~g~Consigue un coche y recoge a los cubanos. [CUB2_19:CUBAN2] ¡Vamos a combatir como machos! [CUB2_21:CUBAN2] ¡Luchad como machos con un par de cojones! [CUB2_22:CUBAN2] ~g~Termina con el resto de los haitianos para que los cubanos puedan avanzar. [CUB2_23:CUBAN2] ~g~Little Haiti estará atestado de haitianos intentando desquitarse con los cubanos. ¡Ten cuidado! [CUB2_24:CUBAN2] ~g~Vuelve al Café Robina en la furgoneta y aparca en la parte trasera. [CUB2_25:CUBAN2] ¡MATA A TODOS LOS HAITIANOS! {=================================== MISSION TABLE CUBAN3 ===================================} [CUB3_A:CUBAN3] Alberto. Un cafetito, señor. [CUB3_B:CUBAN3] Papaíto, no sirvas a esta sabandija. [CUB3_C:CUBAN3] ¡Tienes dos caras, Tommy! [CUB3_D:CUBAN3] ¡Tú, guapito de cara, o eres un falso o eres un calzonazos! [CUB3_E:CUBAN3] ¡Los haitianos! Tío, se están riendo de mí. [CUB3_F:CUBAN3] Vale, vale. ¿Qué problema tienes? [CUB3_G:CUBAN3] Se están riendo de mí, Tommy. ¡De mí! [CUB3_H:CUBAN3] ¡Umberto Robina! ¡Están haciendo lo que quieren! [CUB3_I:CUBAN3] Nadie hace lo que quiere, Umberto, hacen lo que les dejes hacer. [CUB3_J:CUBAN3] ¿Qué? [CUB3_K:CUBAN3] ¿Quieres que alguien se ocupe del asunto? [CUB3_L:CUBAN3] Puedo manejarlo, pero va a costarte. [CUB3_M:CUBAN3] Sé que somos hermanos y todo eso, pero esto es un negocio. [CUB3_N:CUBAN3] Tommy. Eres un verdadero macho. Un tío de negocios, un caballero. [CUB3_O:CUBAN3] Estos haitianos. Tienen un alijo de producto que va a venir desde alta mar, un material de primera. [CUB3_P:CUBAN3] Lo agarraremos y acabaremos con ellos. [CUB3_Q:CUBAN3] Tú lo agarrarás y yo cuidaré de ti. Como a un hermano. Como a un hijo. [CUB3_R:CUBAN3] Creo que prefiero el dinero a que me mezas en tus rodillas, amigo. [CUB3_01:CUBAN3] Eh, Rico. Bonito barco, ¿estás preparado? [CUB3_03:CUBAN3] ~g~Recoge todos los maletines llenos de nieve y dinero. [CUB3_04:CUBAN3] ~g~Lleva la nieve y la pasta de vuelta a Umberto. [CUB3_05:CUBAN3] Sí, Tommy. Tienes que apuntar bien. [CUB3_06:CUBAN3] Mi barco, no me sirve lleno de agujeros, ¿eh? [CUB3_07:CUBAN3] ~g~Ve y reúnete con Rico. Te llevará al punto de encuentro. [CUB3_02:CUBAN3] ~g~¡MATA A TODOS LOS HAITIANOS DEL BARCO! [CUB3_08:CUBAN3] Oh, oh... Una banda de cubanos. ¡Nos atacan! {=================================== MISSION TABLE CUBAN4 ===================================} [CUB4_A:CUBAN4] Ey, señoritas. ¿Sabéis qué voy a hacer? [CUB4_B:CUBAN4] Primero voy a matar un haitiano. ¿Y después? [CUB4_C:CUBAN4] Voy a hacer el amor como un hombre. [CUB4_D:CUBAN4] ¿Sabes chica? Algo así. [CUB4_E:CUBAN4] ¡Perdedor! [CUB4_F:CUBAN4] Mamón. [CUB4_G:CUBAN4] ¡Ey, nena, no te tocaría ni con una pértiga de tres metros! [CUB4_H:CUBAN4] ¡A Umberto Robina le gustan las damas! ¡No una cabra con faldas! [CUB4_I:CUBAN4] ¡Tommy! ¡Tommy, te quiero, te quiero! ¡Vámonos! [CUB4_J:CUBAN4] ¿Irnos a donde? ¿No me puedo tomar primero una taza de café? [CUB4_K:CUBAN4] ¡No hay tiempo para café! Además, acabo de tomarme uno. [CUB4_L:CUBAN4] Vamos a eliminar a los haitianos. [CUB4_M:CUBAN4] Tommy, ¿qué tiene un elefante dentro de la trompa? [CUB4_N:CUBAN4] ¡Dos metros de mocos! ¡Ja, ja, ja! [CUB4_O:CUBAN4] Lo que tú digas, Umberto. [CUB4_P:CUBAN4] Tommy, ve y consíguenos un cochecito haitiano. [CUB4_Q:CUBAN4] Cuando lo tengas, regresa y recoge a mi chico [CUB4_R:CUBAN4] Pepe, y llévale a donde los haitianos. [CUB4_S:CUBAN4] Luego te vas hasta la planta de procesamiento de los haitianos y utilizas su disolvente como explosivo. [CUB4_T:CUBAN4] ¡Boom! Y ¡Bye bye! [CUB4_U:CUBAN4] Umberto, ¿qué hay de ti? [CUB4_V:CUBAN4] Oh, voy a quedarme atrás, a vigilar el café con papá. [CUB4_W:CUBAN4] No se siente muy bien, ¿sabes? [CUB4_02:CUBAN4] ~g~Las bombas se colocarán con un temporizador de 45 segundos. [CUB4_04:CUBAN4] ~r~¡Alertaste a la base, ahora no habrá modo de entrar! [CUB4_07:CUBAN4] El disolvente está a la vuelta, amigo. [CUB4_08:CUBAN4] Hola, amigos. [CUB4_09:CUBAN4] Bueno. Haitianos putas. Muerte. [CUB4_10:CUBAN4] Vamos. [CUB4_11:CUBAN4] Sí, vamos. [CUB4_12:CUBAN4] ¡Ey, necesitamos un coche de la banda haitiana! [CUB4_13:CUBAN4] ¡Oye, vamos a encontrar a nuestros muchachos! [CUB4_14:CUBAN4] Sigue a mis compadres. [CUB4_15:CUBAN4] De acuerdo, allá vas... [CUB4_16:CUBAN4] ¡Voy a colocar la bomba, cubridme! [CUB4_17:CUBAN4] ¡CORRED! [CUB4_18:CUBAN4] Tío, esta es una parte bonita de la ciudad... [CUB4_19:CUBAN4] Compadre, este lugar es un vertedero. [CUB4_20:CUBAN4] Yo tenía una hermosa mujer... vivía por este barrio. [CUB4_21:CUBAN4] Sabes, allí hacen unas buenas pizzas. [CUB4_22:CUBAN4] ¡Tío! ¡Conduces como un loco! [CUB4_23:CUBAN4] ¿Te has perdido tío? [CUB4_24:CUBAN4] Dejaste atrás a Pepe, ve a por él. [CUB4_03:CUBAN4] ~g~Permanece en el coche hasta que puedas aparcar sin percances dentro del recinto. [CUB4_26:CUBAN4] ~g~Recoge a Pepe, dirígete al norte a Little Haiti y roba un coche Voodoo. [CUB4_27:CUBAN4] ~g~Ve y reúnete con Rico y los otros cubanos. [CUB4_28:CUBAN4] ~g~Reúnete con los otros cubanos en el laboratorio de sustancias químicas haitiano. [CUB4_29:CUBAN4] ~g~Camina hacia cada uno de los marcadores para colocar la bomba en ese lugar. [CUB4_30:CUBAN4] ~g~Después de colocar las tres bombas, aléjate de la fábrica antes que salte por los aires. [CUB4_31:CUBAN4] ~g~¡Aléjate de la fábrica! [CUB4_32:CUBAN4] ~g~Aparca el coche en el marcador y bájate de él. [CUB4_06:CUBAN4] ~r~No te alejaste lo suficiente de la base, ¡y hemos tenido que abortar la explosión! {=================================== MISSION TABLE FINALE ===================================} [FIN1_01:FINALE] ¿Qué está pasando? [FIN1_02:FINALE] ¡Tommy! Oh, bien, bien. Escucha, escucha. Uh, escucha, [FIN1_03:FINALE] me gusta el pescado. Me encanta el pescado. [FIN1_04:FINALE] Me encantan como mascotas en peceras, o como comida en un plato, [FIN1_05:FINALE] pero aunque los adoro, no quiero dormir con ellos. [FIN1_06:FINALE] De acuerdo, pero ahora mismo, tus hermanos italianos van a salir de ahí para ponerme esos zapatos de cemento y yo... [FIN1_07:FINALE] Cállate, Ken. Siéntate. [FIN1_08:FINALE] Lance, ¿qué demonios ocurre? [FIN1_09:FINALE] Es tu amigo del norte, Tommy. No está muy contento de que matases a su hombre. [FIN1_10:FINALE] Vienen hoy para ver el negocio. [FIN1_11:FINALE] Tardaron más de lo que pensaba... [FIN1_12:FINALE] Chicos, tenemos que acabar con esto y no dejar duda de que ésta es mi operación. ¡Mía! [FIN1_13:FINALE] Ken, pon tres millones de la primera tanda de dinero falsificado en maletines. [FIN1_14:FINALE] Lance, reúne a los muchachos... [FIN2_01:FINALE] ¡Tommy! [FIN2_02:FINALE] ¿Qué? ¿No hay un gran abrazo para tu viejo colega? [FIN2_03:FINALE] He pasado quince años fuera del negocio, [FIN2_04:FINALE] estoy un poco oxidado respecto a las costumbres de la familia. [FIN2_05:FINALE] Siempre enfadado, ¿eh? Tommy. [FIN2_06:FINALE] No te dije que tu temperamento te metería en problemas, ¿eh? [FIN2_07:FINALE] Hay tres millones en los maletines... [FIN2_08:FINALE] ¿Cuántos fueron? ¿Diez? No, once hombres. [FIN2_09:FINALE] ¡Así es como llegaron a llamarte el Carnicero de Harwood! ¡Je je je! [FIN2_10:FINALE] Me enviaste a matar a un hombre, UN HOMBRE. Sabían que iba para allá. [FIN2_11:FINALE] Vigila tu tono. [FIN2_12:FINALE] Cualquiera pensaría que me echas la culpa por ese desafortunado conjunto de circunstancias. [FIN2_13:FINALE] Simplemente coge el dinero... [FIN2_14:FINALE] ¿Que coja el maldito dinero? [FIN2_15:FINALE] ¿Sabes, Tommy? Hice lo que pude por ti, tiré de algunos hilos, pedí favores. [FIN2_16:FINALE] Yo era tu amigo, Tommy. Esperaba que entraras en razón, ver qué es bueno para el negocio. [FIN2_17:FINALE] Confié en ti, Tommy, y me decepcionaste. [FIN2_18:FINALE] Pero al menos alguno de tu organización de mierda sabe hacer negocios, [FIN2_19:FINALE] ¿No es verdad, Lance? [FIN2_20:FINALE] Lo siento, Tommy. Esto es Vice City. Son negocios. [FIN2_21:FINALE] Nos vendiste. [FIN2_22:FINALE] No. Te vendí a TI, Tommy, te vendí a TI. [FIN2_23:FINALE] El dinero de verdad está arriba, en la caja fuerte. [FIN2_24:FINALE] Así que Tommy, ¿cuál es el gran plan? [FIN2_25:FINALE] ¿Creías que solo me llevaría el dinero falso? [FIN2_26:FINALE] ¿Salvar el culo y huir con el rabo entre las piernas? [FIN2_27:FINALE] No. [FIN2_28:FINALE] Solo quería joderte antes de matarte. [FIN3_01:FINALE] ¿Tommy? [FIN3_02:FINALE] Oh, Dios mío, ¡Tommy! ¿Qué ha sucedido? [FIN3_03:FINALE] ¿A ti qué te parece? [FIN3_04:FINALE] ¡Parece que te han arruinado el traje! [FIN3_05:FINALE] ¡Y Tommy, era un traje muy bonito! Tommy, ¿qué diablos ha ocurrido? [FIN3_06:FINALE] Tuve un desacuerdo con un socio del negocio, ya sabes cómo son estas cosas. [FIN3_07:FINALE] Tommy, cuando tengo un desacuerdo con un socio, le envío una carta muy enfadado. [FIN3_08:FINALE] Quizás me mee en su buzón. No empiezo la tercera guerra mundial. [FIN3_09:FINALE] ¿Sabes? Quizás deberías hablar con mi psiquiatra... [FIN3_10:FINALE] Ése estúpido mamón, Lance... [FIN3_11:FINALE] Tommy. A mí nunca me gustó ese tipo, ¿de acuerdo? [FIN3_12:FINALE] Era neurótico, era inseguro, era egocéntrico... ¡ese tipo era un gilipollas! [FIN3_13:FINALE] ¡Me alegro de que lo eliminases! [FIN3_14:FINALE] No creo que vayamos a tener más líos desde la parte norte... [FIN3_15:FINALE] porque ya no hay 'parte norte'. [FIN3_16:FINALE] Ahora es todo sur. [FIN3_17:FINALE] Espera, eso quiere decir lo que creo que quiere decir, Tommy, ¿nene? [FIN3_18:FINALE] ¿Qué crees que significa? [FIN3_19:FINALE] Que estamos al mando... Quiero decir, que tú estás al mando. Oh, Tommy... [FIN3_20:FINALE] ¿Sabes, Ken? Este podría ser el comienzo de una hermosa relación comercial... [FIN3_21:FINALE] Después de todo, eres un cómplice, traicionero, ladrón de tres al cuarto [FIN3_22:FINALE] y yo soy un asesino psicópata convicto y además paso farlopa. [FIN3_23:FINALE] Lo sé. ¿No es sencillamente hermoso? [FIN_B1:FINALE] ~g~Ve y liquida al traidor de ~y~Lance Vance~g~. [FIN_B2:FINALE] ~g~Liquida a ~p~Sonny~g~ y acaba con esto de una vez por todas. [FIN_B3:FINALE] ~g~La mafia está intentando robarte el dinero. Protege la caja fuerte. [FIN_B4:FINALE] ~g~Estás casi muerto, consigue ~w~salud~g~ en el piso de abajo. [FIN_B5:FINALE] ~g~La mafia está robándote el dinero, protege la ~c~caja fuerte~g~. [FIN_B7:FINALE] ~r~La mafia te ha robado todo el dinero. [DEFSAFE:FINALE] ~g~Regresa a la caja fuerte y protégela. {=================================== MISSION TABLE FIRETRK ===================================} [F_PASS1:FIRETRK] ¡Fuego extinguido! [F_FAIL2:FIRETRK] ~r~¡Llegas demasiado tarde! [F_CANC:FIRETRK] ~r~¡Misión de bombero cancelada! [F_EXTIN:FIRETRK] FUEGOS: [F_START:FIRETRK] ~g~Se ha informado de un vehículo ardiendo en la zona de ~a~. Ve y extingue el fuego. [SIREN_1:FIRETRK] Para activar la sirena de este vehículo, pulsa ~h~~k~~VEHICLE_HORN~~w~. [SIREN_2:FIRETRK] Para activar la sirena de este vehículo, pulsa ~h~~k~~VEHICLE_HORN~~w~. [FIREPRO:FIRETRK] Nivel 12 de la misión del camión de bomberos completado. ¡Ahora eres completamente ignífugo! [F_FAIL1:FIRETRK] Misión de bombero terminada. [F_STAR1:FIRETRK] ~g~Se ha informado de que hay vehículos ardiendo en la ~a~ zona. Ve y apaga el fuego. [SPRAY_4:FIRETRK] { reVC update } Pulsa ~h~~k~~VEHICLE_FIREWEAPON~ ~w~para disparar el cañón de agua y ~h~~k~~VEHICLE_TURRETLEFT~~w~ o ~h~~k~~VEHICLE_TURRETRIGHT~~w~ para apuntar con él. [SPRAY_1:FIRETRK] { reVC update } Pulsa ~h~~k~~VEHICLE_FIREWEAPON~ ~w~para disparar el cañón de agua y ~h~~k~~VEHICLE_TURRETLEFT~~w~ o ~h~~k~~VEHICLE_TURRETRIGHT~~w~ para apuntar con él. {=================================== MISSION TABLE GENERA1 ===================================} [GEN1_A:GENERA1] ¡Sr. Vercetti! [GEN1_B:GENERA1] Coronel. [GEN1_D:GENERA1] No, gracias. [GEN1_E:GENERA1] Me avergüenza admitir que una de las causas de nuestro mutuo problema parece ser la lengua larga de un hombre en el que solía confiar. [GEN1_F:GENERA1] He tenido conmigo a González durante años, pero ahora su incompetencia alcanza nuevas cotas. [GEN1_G:GENERA1] Lo correcto es que mates a González... [GEN1_H:GENERA1] ¿Lo hizo él? El dinero es lo único importante para mí. [GEN1_I:GENERA1] Por tu gentileza te compensaré y después buscaremos juntos tu dinero. [GEN1_J:GENERA1] Estará en su ático, probablemente medio borracho. Utiliza esto... [GEN1_K:GENERA1] Utiliza esto... [GEN1_06:GENERA1] ¡Eh! ¡Tiene una sierra! [GEN1_07:GENERA1] ¡Mantente alejado de mí, cabrón miserable! [GEN1_08:GENERA1] ¡Maldición, he desperdiciado mi vida y mi belleza! [GEN1_10:GENERA1] ¡Te voy a cerrar esa bocaza tuya! [GEN1_11:GENERA1] ¡Deja de correr gorda bola de sebo! [GEN1_12:GENERA1] ¡Estate quieto y haré que sea rápido! [GEN1_13:GENERA1] Deja de chillar, a nadie le importa, ¡gordo! [GEN1_C:GENERA1] Gracias por venir. Por favor siéntese. ¿Langosta? [GEN1_05:GENERA1] ~g~¡Ve y liquida a González! [GEN1_09:GENERA1] Te pagaré el doble, Tommy, ¡EL DOBLE! [GEN1_18:GENERA1] ~r~¡González ha conseguido llegar a la Jefatura de Policía! [GEN1_19:GENERA1] ~g~¡La policía de Vice City viene a por ti! [GEN1_20:GENERA1] ~g~Sube a un vehículo. [GEN1_21:GENERA1] ~g~Ve al~h~ taller de pintura~g~ de~h~ Vice Point~g~. [GEN1_22:GENERA1] ~g~Conduce tu vehículo hasta el interior del ~h~taller de pintura~w~ para perder tu ~h~nivel de se busca~w~, reparar y volver a pintar tu vehículo. El precio es ~h~100 $~g~. Esta vez es gratis. [GEN1_01:GENERA1] Cuando estés corriendo, mantén pulsado ~h~~k~~PED_FIREWEAPON~~w~ para preparar un ataque cuerpo a cuerpo. [GEN1_02:GENERA1] Cuando estés corriendo, mantén pulsado ~h~~k~~PED_FIREWEAPON~~w~ para preparar un ataque cuerpo a cuerpo. [GEN1_03:GENERA1] Cuando estés corriendo, mantén pulsado ~h~~k~~PED_FIREWEAPON~~w~ para preparar un ataque cuerpo a cuerpo. [GEN1_14:GENERA1] Suelta ~h~~k~~PED_FIREWEAPON~~w~ para atacar. [GEN1_15:GENERA1] Suelta ~h~~k~~PED_FIREWEAPON~~w~ para atacar. [GEN1_16:GENERA1] Suelta ~h~~k~~PED_FIREWEAPON~~w~ para atacar. [GEN1_23:GENERA1] ~g~Vuelve pasando por las puertas para regresar a la planta baja. {=================================== MISSION TABLE GENERA2 ===================================} [COL2_A:GENERA2] ¡Tommy! Ven, acércate. [COL2_B:GENERA2] Qué buena pinta, ¿eh? ¿Hocico de tapir? [COL2_C:GENERA2] No. No, gracias. [COL2_D:GENERA2] Tommy, eres como una brisa de la pampa que me ha liberado de la peste de la corrupción, [COL2_E:GENERA2] aunque, parece que debo llorar su paso y seguir con el negocio como siempre. [COL2_F:GENERA2] Esto no me está acercando mucho a mi dinero... [COL2_G:GENERA2] Tommy, amigo mío, ahora no estás en Liberty. Aquí hacemos las cosas de un modo diferente. [COL2_H:GENERA2] Continuaré con mis investigaciones pero mientras tanto tengo un valioso trato que cerrar. [COL2_I:GENERA2] Un favor para un amigo, Cortez. [COL2_J:GENERA2] Eres un buen amigo, Tommy. Sabía que no me decepcionarías. [COL2_K:GENERA2] Necesito que te encuentres con un mensajero que ha conseguido una tecnología valiosa para mí... [COL2_1:GENERA2] La lluvia, está muy húmeda esta época del año... [COL2_2:GENERA2] ¿Qué? [COL2_3:GENERA2] Ah, comment? [COL2_4:GENERA2] Mira, me envía Cortez. Tan sólo dame los malditos chips. [COL2_5:GENERA2] Oh... d'accord. [COL2_B1:GENERA2] ~g~Encuéntrate con el mensajero en el centro comercial. [COL2_B2:GENERA2] ~g~El mensajero está huyendo con los chips guía, ¡no le dejes escapar! [COL2_B3:GENERA2] ~g~Recupera los chips guía y llévaselos de vuelta al Coronel. [COL2_F1:GENERA2] ~r~¡Mataste al contacto! [COL2_F2:GENERA2] ~r~El mensajero está muerto. Coge los chips guía. [COL2_6A:GENERA2] ¡Alto, cerdo imperialista americano! Eso es propiedad del gobierno francés. ¡Y se acabó! [BLIPHLP:GENERA2] Cuando el icono en el radar es un triángulo apuntando hacia arriba, indica que el objetivo está por encima del jugador. [COL2_F3:GENERA2] ~r~Los chips de orientación están en el fondo del mar. [COL2_F4:GENERA2] ~r~¡El mensajero ha escapado! Has fracasado en conseguir los chips de orientación. {=================================== MISSION TABLE GENERA3 ===================================} [GEN3_A:GENERA3] Thomas, agradezco que hayas venido. [GEN3_B:GENERA3] Perdóname por ir directamente a los negocios. [GEN3_C:GENERA3] Díaz me ha pedido que supervise una transacción comercial poco importante. [GEN3_D:GENERA3] Esperemos que vaya mejor que la última vez, ¿eh? [GEN3_E:GENERA3] Por eso es por lo que pensé en ti, amigo mío. [GEN3_F:GENERA3] He dejado protección en un aparcamiento de varias plantas. [GEN3_G:GENERA3] Recógela... luego ve a vigilar a los hombres de Díaz en el punto de entrega. [GEN3_H:GENERA3] Gracias, amigo. [GEN3_1:GENERA3] Monopolizando toda la acción, ya veo... [GEN3_2:GENERA3] Mira, ¿quieres dejar de seguirme a todas partes? ¿Por qué no vienes y me demuestras que vales para algo? [GEN3_3:GENERA3] Podría hacerlo. Mi nombre es Lance, por cierto. [GEN3_5:GENERA3] Tú debes de ser el nuevo matón de Cortez. [GEN3_6:GENERA3] Hasta que surjan oportunidades mejor pagadas. [GEN3_7:GENERA3] Estarán aquí en cualquier momento. Será mejor que consigamos una posición ventajosa... [GEN3_8:GENERA3] ¡De acuerdo! Yo iré al balcón, tú sube a ese tejado al otro lado del patio. [GEN3_9:GENERA3] ¡Estoy vivo! ¡Mamones! ¡Y todo gracias a ti! ¿Cómo te llamas? [GEN3_10:GENERA3] Tommy. [GEN3_11:GENERA3] Nos veremos pronto, ¡creo! [GEN3_12:GENERA3] Mierda. ¿Dónde está Lance? [GEN3_14:GENERA3] ¡Tommy! ¡Necesito ayuda aquí! [GEN3_15:GENERA3] ¡No te preocupes, te tengo cubierto! [GEN3_16:GENERA3] ¡Los hombres de Díaz están siendo eliminados! [GEN3_19:GENERA3] ~g~¡Haitianos! ¡Están saboteando el trato! ¡Protege a Díaz! [GEN3_20:GENERA3] ~g~Ve al aparcamiento donde podrás recoger el arma que te dejó el Coronel. [GEN3_22:GENERA3] Salud de Díaz: [GEN3_23:GENERA3] ~g~¡Has dejado a Lance atrás! ¡Ve a por él! [GEN3_25:GENERA3] ~r~¡Lance murió! [GEN3_28:GENERA3] ~g~Devuélvele el maletín a Díaz. [GEN3_29:GENERA3] ~g~Recoge el maletín y llévaselo a Díaz. [GEN3_30:GENERA3] ~r~¡Se escapó con el dinero! ¡Díaz te arrancará las pelotas por ésto! [GEN3_33:GENERA3] ~r~¡Mira adónde disparas! Se supone que tienes que proteger a Díaz y a sus hombres, ¡no dispararles! [GEN3_34:GENERA3] ~r~¡No va a haber ningún trato si disparas a los cubanos! [GEN3_35:GENERA3] ~g~¡Ha robado el dinero de Díaz! [GEN3_36:GENERA3] ~g~¡Pilla una moto, persíguele y recupera el dinero de Díaz! [GEN3_37:GENERA3] ~g~Aquí vienen los cubanos. Vigila el trato y asegúrate que Díaz y Lance están a salvo. [GEN3_38:GENERA3] ~r~¡Díaz ha muerto! ¡Has fallado en protegerle! [GEN3_39:GENERA3] ~g~Ve a tu posición estratégica en el piso de arriba. [GEN3_44:GENERA3] ~g~Ve con Lance al punto de entrega y vigila a Díaz. [GEN3_45:GENERA3] Estarán aquí en cualquier minuto, mejor será que vayamos tomando posiciones estratégicas. [GEN3_40:GENERA3] { reVC update } Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_41:GENERA3] { reVC update } Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_46:GENERA3] ¡Mierda! [GEN3_47:GENERA3] ¡Tommy! [GEN3_48:GENERA3] ¡Maldición! [GEN3_49:GENERA3] Salud de Lance: [GEN3_50:GENERA3] ~r~¡Has perdido el dinero de Díaz! ¡La próxima vez intenta no convertir mi dinero en cenizas! [GEN3_51:GENERA3] ¡Más condenados haitianos en una furgoneta apestosa! [GEN3_54:GENERA3] ¡No os quedéis ahí, panda de gilipollas, perseguid a ese haitiano cabrón! [GEN3_55:GENERA3] ¡Tommy! ¡Yo me quedo aquí a proteger a Díaz! [GEN3_18:GENERA3] ~g~Aquí llegan los cubanos, mantente cerca de Díaz. Asegúrate de que la negociación entre Díaz y Lance sea segura. [GEN3_56:GENERA3] ~r~¡Díaz ha caído en una emboscada y ha muerto! ¡La próxima vez no le pierdas de vista! [GEN3_57:GENERA3] El Kruger es un rifle de asalto que te permite apuntar en primera persona manualmente. [GEN3_58:GENERA3] Mantén pulsado ~h~~k~~PED_LOCK_TARGET~~w~ para ~h~apuntar~w~ con un rifle de asalto. [GEN3_59:GENERA3] Mantén pulsado ~h~~k~~PED_LOCK_TARGET~~w~ para ~h~apuntar~w~ con un rifle de asalto. [GEN3_60:GENERA3] Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ un rifle de asalto. [GEN3_61:GENERA3] Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ un rifle de asalto. [GEN3_62:GENERA3] Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para ~h~disparar~w~ un rifle de asalto. [GEN3_63:GENERA3] Además de realizar maniobras de acercamiento,~h~ las motos ~w~te permiten ~h~disparar hacia adelante~w~. [GEN3_64:GENERA3] { reVC update } Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_65:GENERA3] { reVC update } Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_66:GENERA3] { reVC update } Para ~h~disparar hacia el frente ~w~sobre una ~h~moto ~w~pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~. [GEN3_67:GENERA3] Para disparar hacia el frente en una moto deberás tener un subfusil. [GEN3_53:GENERA3] ¡Mi dinero! [GEN3_52:GENERA3] ¿Esos haitianos creen que pueden enfrentarse a RICARDO DÍAZ? {=================================== MISSION TABLE GENERA4 ===================================} [DETON:GENERA4] DETONACIÓN: [COL4_3:GENERA4] ¡CONVOY, ALTO! [COL4_6:GENERA4] ¡NOS ESTÁN DISPARANDO! [COL4_7:GENERA4] ¡Civil, aléjese del tanque! [COL4_8:GENERA4] ¡DIJE, alejaos, INMEDIATAMENTE! [COL4_9:GENERA4] ¡POSICIONES DEFENSIVAS! [COL4_11:GENERA4] - ¡Quite a esos civiles fuera de mi camino, soldado! - ¡Señor, sí, señor! [COL4_12:GENERA4] ¡Civil en el TANQUE! ¡DETENEDLE! [COL4_13:GENERA4] Este es un convoy militar, no obstruyan nuestra ruta. [COL4_14:GENERA4] Tírelo, soldado. [COL4_15:GENERA4] - ¡Mueva ese vehículo civil fuera de nuestro camino! - ¡Señor! ¡Moviendo el vehículo, señor! [COL4_17:GENERA4] ¡Bien, PELOTÓN, MUÉVANSE! [COL4_18:GENERA4] ¡Hay alguien en el tanque, señor! [COL4_19:GENERA4] - ¡Vaya a conseguir unos donuts, soldado! - ¡Señor, sí, señor! [COL4_20:GENERA4] Blanco localizado, señor. [COL4_21:GENERA4] ¡FRANCOTIRADOR! [COL4_22:GENERA4] Me voy de aquí. [COL4_23:GENERA4] - ¡Objetivo completado! ¡Pelotón, rompan filas! - Vamos a comer unos cuantos donuts. [COL4_24:GENERA4] ¡Activado protocolo de seguridad Delta India Eco! ¡Iniciada autodestrucción del vehículo! [COL4_26:GENERA4] ¡Prepárate para morir, basura comunista! [COL4_B2:GENERA4] ~r~El tanque llegó a su destino a salvo! [COL4_B5:GENERA4] ~r~¡El tanque ha sido destruido! [COL4_01:GENERA4] Díaz estaba complacido, y le gustaría verte de nuevo. [COL4_02:GENERA4] ¿Es eso algo bueno? [COL4_03:GENERA4] ¡Por supuesto! Aunque estoy empezando a pensar que Díaz fue el responsable de nuestra desafortunada pérdida... [COL4_04:GENERA4] ¿Qué te hace decir eso? [COL4_05:GENERA4] Uno no esgrime acusaciones contra un hombre como Díaz... Solo estoy pensando en voz alta... [COL4_06:GENERA4] No importa. Tengo una propuesta de la que te podrías beneficiar... [COL4_07:GENERA4] No tengo tiempo para hacer más recados, Cortez. [COL4_08:GENERA4] Yo habría pensado que un hombre con unas deudas tan grandes estaría ansioso por encontrar oportunidades. Por favor, Tommy, al menos escúchame. [COL4_09:GENERA4] Adelante... [COL410:GENERA4] Tengo un comprador para una pieza de equipo militar que está siendo transportada a través de la ciudad. Recógela para mí... [COL411:GENERA4] y una vez que la tengas, quiero que me llames inmediatamente, entonces... [COL4_B4:GENERA4] ~g~El tanque está bloqueado. Busca un modo de engañar a los ocupantes. [COL4_1:GENERA4] - ¿Que qué pasa con el artillero? - ¡No lo sé, señor! [COL4_4:GENERA4] - Ve arriba soldado. - ¡Sí, señor! [COL4_B1:GENERA4] ~g~Ve y adquiere el vehículo militar que se está paseando por la ciudad. [COL4_B3:GENERA4] ~g~Deja el tanque en el escondite del Coronel antes que se autodestruya. [COL4_B6:GENERA4] ~g~¡Encuentra el modo de robar el tanque! [COL4_B7:GENERA4] ~g~Conduce el tanque al garaje. [COL4_B8:GENERA4] ~g~Bájate del tanque y sal del garaje. {=================================== MISSION TABLE GENERA5 ===================================} [COL5A_1:GENERA5] Las circunstancias fuerzan una apresurada partida, amigo. [COL5A_2:GENERA5] ¿Cuál es el problema? [COL5A_3:GENERA5] Los franceses quieren recuperar la tecnología de sus misiles y después del último incidente, [COL5A_4:GENERA5] siento que es momento de encontrar puertos más seguros. [COL5A_5:GENERA5] ¿No sería más seguro volar? [COL5A_6:GENERA5] Estaría muerto antes de alcanzar el mostrador de facturación de equipajes. Además, necesito sacar del país mi mercancía. [COL5A_7:GENERA5] ¿Necesitas otro guardaespaldas? [COL5A_8:GENERA5] Tú, amigo mío, vales por diez guardaespaldas... [COL5B_1:GENERA5] Thomas, me has protegido y servido bien. [COL5B_2:GENERA5] Pero ahora debes dejarnos antes de que lleguemos a mar abierto. [COL5B_4:GENERA5] Gracias, Coronel. [COL5B_5:GENERA5] Una petición más, mientras estoy fuera, ¿podrías vigilar a Mercedes por mí? [COL5B_6:GENERA5] Creo que ella puede cuidarse por sí misma, pero seguro, la vigilaré. [COL5B_7:GENERA5] Gracias, amigo mío. Hasta luego. [COL5B_8:GENERA5] Adiós, amigo. [COL5_7:GENERA5] ¡Deja de dispararme! [COL5_9:GENERA5] ¡Tommy, haz que dejen de dispararme! [COL5_10:GENERA5] ¡Tengo inmunidad diplomática! [COL5_11:GENERA5] ¡No disparen, soy un Coronel! [COL5_12:GENERA5] Thomas, mátales, mi país te adorará. [COL5_13:GENERA5] ¡Tommy, los franceses nos superan! [COL5_14:GENERA5] Tommy, mire a donde mire, hay franceses, ¡lo odio! [COL5_15:GENERA5] Tommy, ¿cómo estás? [COL5_16:GENERA5] ¡Ésto es por Piaf y Gainesbourg y por vuestro estúpido pan francés! [COL5_1:GENERA5] ¡A babor! ¡A babor! [COL5_2:GENERA5] ¡Están atacando desde estribor! [COL5_3:GENERA5] ¡El puente está ahí delante! [COL5_4:GENERA5] ¡Tienen un helicóptero! [COL5_B1:GENERA5] ~g~Protege al Coronel y su yate a toda costa. [COL5_B2:GENERA5] ~g~Ponte delante y despeja la ruta del yate del Coronel. [COL5_B3:GENERA5] ~r~¡El Coronel está muerto! [COL5_B4:GENERA5] ~g~Destruye el helicóptero de ataque. [COL5B_3:GENERA5] Bajaré mi lancha personal. Quédatela, amigo, en señal de mi agradecimiento. [COL5_B5:GENERA5] ~g~Dispara para abatir los helicópteros, no pongas en peligro el yate. [COL5_B6:GENERA5] ~g~Te has quedado sin munición, consigue más de las escaleras en la cubierta superior. [COL5_B7:GENERA5] ~g~Te estás quedando sin salud, consigue más de las escaleras en la cubierta superior. {=================================== MISSION TABLE HAIT1 ===================================} [HAM1_A:HAIT1] ¿Hola? ¿Hola? [HAM1_B:HAIT1] Entra, cielo, y descansa los pies. [HAM1_C:HAIT1] Tú debes de ser el gran hombre malo sobre el que ha estado hablando mi abuelo. [HAM1_D:HAIT1] Me cuenta cosas sobre ti, ya sabes, cuando me visita, [HAM1_E:HAIT1] y sobre los otros que te esperan. [HAM1_F:HAIT1] Bien, todos llevamos muertos mucho tiempo, pero tú... [HAM1_G:HAIT1] No me gustaría estar en tu pellejo. [HAM1_H:HAIT1] Recibí un mensaje. Para que viniese aquí. [HAM1_I:HAIT1] ¿Puedes oírles? [HAM1_J:HAIT1] Están diciendo tu nombre, deben quererte mucho, ¿no crees? [HAM1_K:HAIT1] Ahora ayudarás a la tía Poulet y quizás ella te ayude. [HAM1_L:HAIT1] Quizás ella pueda darte un pequeño talismán después de todo esto. [HAM1_M:HAIT1] ¿Darte algo de magia para echar el mal de ojo a la policía? [HAM1_N:HAIT1] Mira, esto es todo muy... ¿darme qué? [HAM1_O:HAIT1] Yo, yo, creo que recibí la dirección equivocada... [HAM1_P:HAIT1] Haz esto por mí, Tommy... [HAM1_Q:HAIT1] Los cubanos, tontos orgullosos y desagradables, [HAM1_R:HAIT1] han estado causando problemas a mis encantadores chicos haitianos. [HAM1_S:HAIT1] Ahora le han dicho a la policía donde he estado almacenando mis polvos. [HAM1_T:HAIT1] Ellos piensan que es farlopa, pardillos. [HAM1_U:HAIT1] Ahora sé un buen chico, Tommy, y ve a conseguir los polvos para la tía Poulet. [HAM1_V:HAIT1] Sí, sí, seguro, seguro. [HAM1_1:HAIT1] ~g~Los polis se están acercando a nuestros escondites. Ve y consíguelos antes que ellos lo hagan. [HAM1_2:HAIT1] ~r~¡Los polis llegaron primero al escondite! [HAM1_3:HAIT1] ~g~¡Lleva este material de vuelta al escondite! [HAM1_4:HAIT1] ~g~¡Bien! ¡Ahora el siguiente! [HAM1_6:HAIT1] ~r~¡Ese almacén está destruido, idiota! [HAM1_7:HAIT1] ~g~¡Los polis han conseguido nuestro alijo! ¡Recupéralo antes de que se marchen! [HAM1_8:HAIT1] ~g~Los polis están de camino para recoger el alijo, ¡date prisa! [HAT_1A:HAIT1] ~g~¡No muevas ni un solo dedo! {=================================== MISSION TABLE HAIT2 ===================================} [HAT2_B1:HAIT2] ~g~Ve hasta la camioneta que contiene las bombas aéreas. [HAT2_B2:HAIT2] Mata a los cubanos... [HAT2_B4:HAIT2] ¡Y destruye sus barcos! [HAT2_B5:HAIT2] ~g~Los cubanos están huyendo. ¡No les dejes escapar! [HAT2_B6:HAIT2] ~r~¡El avión RC está yendo demasiado lejos del alcance! [HAT2_B7:HAIT2] ~g~Uno de los cubanos está escapando en un coche. ¡No dejes ningún testigo! [HAT2_B8:HAIT2] ~r~¡No te quedan aviones RC! [HAT2_B9:HAIT2] Aviones RC: [HAT2_1:HAIT2] Oh. Lo siento, debo tener la dirección equivocada... [HAT2_2:HAIT2] Bueno, también podrías entrar y descansar los pies y tomarte una taza de té. [HAT2_3:HAIT2] ¿Tienes algo ahí para mí? [HAT2_4:HAIT2] Sí... [HAT2_5:HAIT2] Este lugar me parece familiar. Un olor de la niñez... un deja vu... [HAT2_6:HAIT2] Bien, Tommy, te voy a susurrar un pequeño recado para que lo hagas. Escúchame bien, ¿vale? [HAT2_7:HAIT2] Te pareces a alguien que yo... [HAT2_8:HAIT2] Los cubanos tienen barcos rápidos que usan para cruzar el charco cargados de polvo. [HAT2_9:HAIT2] Es su sustento. [HAT2_10:HAIT2] Mi sobrino ha estado preparando pequeñas bombas aéreas para eliminarles. [HAT2_11:HAIT2] Vuela los barcos y conviértelos en madera de ataúd. [HAT2_12:HAIT2] Muchas gracias por el té. [HAT2_B3:HAIT2] { reVC update } Pulsa ~h~~k~~VEHICLE_FIREWEAPON~~w~ para soltar una bomba. Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~~w~ para cancelar. {=================================== MISSION TABLE HAIT3 ===================================} [HAM3_A:HAIT3] ¿Hola? Hola, eh... Estaba buscando a alguien por aquí... [HAM3_B:HAIT3] Pareces hambriento, Tommy, [HAM3_C:HAIT3] ¿Te conozco? [HAM3_D:HAIT3] Silencio. [HAM3_E:HAIT3] Una cosa más y te podré dejar marchar, Tommy. [HAM3_F:HAIT3] Mis chicos están en guerra con los cubanos. [HAM3_G:HAIT3] Pero sin armas. [HAM3_H:HAIT3] Pero los cubanos tienen una sorpresa por llegar. [HAM3_I:HAIT3] Mientras combaten en las calles, tú tomarás este rifle y los matarás en medio del alboroto. [HAM3_J:HAIT3] Nadie te verá, nadie te oirá. [HAM3_K:HAIT3] Tommy, si haces esto por mí, ya no estarás atado a los lazos de mi delantal. [HAM3_1:HAIT3] ~g~Debemos ganar esta batalla. Si todos los haitianos mueren, perdemos nosotros. [HAM3_3:HAIT3] ~g~Espero que los cubanos hagan trampas así que permanece alerta. [HAM3_4:HAIT3] ~r~¡Te han visto! ¡La misión ha fracasado! [HAM3_5:HAIT3] ~g~Debes matar a los cubanos desde lejos. No deben verte. [HAM3_8:HAIT3] ~g~¡Los haitianos están muriendo! Mejora tu puntería. [HAM3_7:HAIT3] ~g~¡Cuidado! Los cubanos han traído refuerzos. ¡¡Acaba con todos ellos!! [HAM3_2:HAIT3] ~r~¡Los haitianos han muerto! [HAM3_L:HAIT3] Vale, tía... {=================================== MISSION TABLE HOTEL ===================================} [INTB_A:HOTEL] ¡Tommy! Tommy, ha pasado tanto tiempo. [INTB_B:HOTEL] Hola, Sonny. [INTB_C:HOTEL] Lo sé, lo sé. Te abruma la emoción. [INTB_D:HOTEL] Quince años... si parece que fue ayer. [INTB_E:HOTEL] Supongo que es cuestión de perspectiva. [INTB_F:HOTEL] Eh, estar en el talego por la familia no es agradable, [INTB_G:HOTEL] aunque la familia cuida de los suyos, ¿no es verdad? [INTB_H:HOTEL] Y bien, cómo fue el negocio... ¿estás sentado en una pila de oro blanco? [INTB_I:HOTEL] Mira, Sonny, nos tendieron una trampa. El negocio fue una emboscada. Harry y Lee están muertos. [INTB_J:HOTEL] Me estás tomando el pelo, ¿no? Tommy. Dime que aún tienes el dinero. [INTB_K:HOTEL] No Sonny... no tengo el dinero. [INTB_L:HOTEL] Ése era mi dinero, Tommy, ¡mi dinero! [INTB_M:HOTEL] ¡Espero que no me andes jodiendo, Tommy, porque sabes que soy un hombre con el que no se puede jugar! [INTB_N:HOTEL] Espera Sonny. [INTB_O:HOTEL] Tienes mi garantía personal de que voy a conseguir recuperar el dinero y el alijo. [INTB_P:HOTEL] Y te voy a enviar por correo las pelotas de los responsables. [INTB_Q:HOTEL] Eh, ya lo sé. No eres tonto, Tommy, pero te prevengo, yo tampoco lo soy. [INTB_R:HOTEL] Si se tratase de cualquier otro, ya estarías MUERTO. [INTB_S:HOTEL] Pero como se trata de ti, como hemos hecho historia, voy a dejar que te ocupes de esto. [INTB_T:HOTEL] Mira, Sonny, tienes mi palabra. [INTB_U:HOTEL] Estaremos en contacto. {=================================== MISSION TABLE ICECRE1 ===================================} [ICC1_1:ICECRE1] ~g~Utiliza la furgoneta de los helados para distribuir perica en Vice City. [ICC1_3:ICECRE1] ~g~Recibirás dinero por cada venta que hagas, pero cuantas más transacciones hagas más atraerás la atención de la policía. [ICC1_4:ICECRE1] ~g~No hay ningún cliente en esta zona, inténtalo en otra. [ICC1_5:ICECRE1] Negocios hechos: [ICC1_6:ICECRE1] ~g~Utiliza la furgoneta Mr. Whoopee para distribuir los productos Cherry Popper por Vice City. [ICC1_7:ICECRE1] ~g~Por cada transacción que hagas recibirás dinero, aunque cuantas más operaciones lleves a cabo más atención provocarás de la policía. [ICC1_8:ICECRE1] ~g~Para llevar a cabo el negocio ~h~aparca la furgoneta, ~g~y pulsa el ~h~~k~~VEHICLE_HORN~ ~g~para que suene la música que llamará la atención de la gente. [ICC1_9:ICECRE1] ~g~A las bandas locales no les gustará que hagas negocios en su territorio así que si haces esto, prepárate para hostilidades. [ICC1_10:ICECRE1] ~g~Has hecho ~1~ tratos! [ICC1_11:ICECRE1] ~g~Has hecho ~1~ trato! [ICC1_12:ICECRE1] ¡PROPIEDAD ADQUIRIDA! [ICC1_13:ICECRE1] ~r~¡No conseguiste ningún trato! [ICC1_14:ICECRE1] FÁBRICA DE HELADOS CONSOLIDADA [ICC1_15:ICECRE1] ~g~La fábrica de helados generará ahora unos ingresos máximos de hasta ~1~ $. Asegúrate de recaudarlos regularmente. [ICC1_2:ICECRE1] ~g~Aparca la furgoneta y pulsa ~h~~k~~VEHICLE_HORN~~w~ para que suene la música indicando a los clientes que estás listo para hacer empezar. [ICC1_16:ICECRE1] ~g~Utiliza la furgoneta Mr. Whoopee para distribuir los productos Cherry Popper por Vice City. [ICE_AT1:ICECRE1] FÁBRICA DE HELADOS CONSOLIDADA [ICE_AT2:ICECRE1] ~g~La fábrica Cherry Popper generará ahora unos ingresos hasta un máximo de ~1~ $. Asegúrate de recaudarlos de manera regular. [ICC1_17:ICECRE1] Misión de Distribución finalizada [ICC1_18:ICECRE1] Venta total de helados: ~1~ $ [ICC1_19:ICECRE1] Total de negocios hechos: ~1~ {=================================== MISSION TABLE ICECUT ===================================} [ICC1_A:ICECUT] ¿Quién eres tú? [ICC1_B:ICECUT] El nuevo propietario. [ICC1_C:ICECUT] ¿Fuiste ahora o en cualquier momento un niño? [ICC1_D:ICECUT] ¿De qué estás hablando? [ICC1_E:ICECUT] ¿Fuiste niño? [ICC1_F:ICECUT] ¡Sí! ¡Calma! ¿Qué pasa contigo? [ICC1_G:ICECUT] Lo sabía. Un niño. [ICC1_H:ICECUT] ¡Un maldito, apestoso, lloriqueante, mocoso, vil, vomitivo y pequeño bebé chillón! [ICC1_I:ICECUT] Bebés... Criaturas repugnantes, horribles y espantosas. [ICC1_J:ICECUT] Mami no te quiere. ¡mierdecilla! [ICC1_K:ICECUT] Cálmate. [ICC1_L:ICECUT] ODIO los bebés, odio los niños. [ICC1_M:ICECUT] Son unos pequeños guarros, llorones, mocosos, perversos y vomitivos... [ICC1_N:ICECUT] ¡Ya es suficiente! [ICC1_O:ICECUT] ¿Qué pasa contigo? [ICC1_P:ICECUT] ¿Fabricas helado, de acuerdo? Es básicamente para los niños. [ICC1_Q:ICECUT] ¿Qué clase de psicópata eres tú? [ICC1_R:ICECUT] Solo para que lo entienda, ¿para qué hacer felices a los niños si los odias? [ICC1_S:ICECUT] Oh, estúpido, apestoso, lloriqueante... [ICC1_T:ICECUT] ¡Cállate! [ICC1_U:ICECUT] ¡Mocoso! [ICC1_V:ICECUT] El helado es una tapadera. [ICC1_W:ICECUT] Distribuimos otros productos no lácteos. [ICC1_X:ICECUT] Y si veo un niño, le saco provecho. [ICC1_Y:ICECUT] ¿Verdad, niños? Sí, sí, lo hago. Mamá no os quiere. [ICC1_Z:ICECUT] ¡Os ODIA! [ICC1_ZA:ICECUT] ¡PROPIEDAD ADQUIRIDA! {=================================== MISSION TABLE INTRO ===================================} [INT1_A:INTRO] Tommy Vercetti... Mierda. [INT1_B:INTRO] No pensé que le fueran a soltar nunca. [INT1_C:INTRO] Mantuvo la cabeza gacha, ayuda a la gente a olvidar. [INT1_D:INTRO] La gente lo recordará antes de lo que crees. [INT1_E:INTRO] Cuando le vean paseando por las calles de su barrio. [INT1_F:INTRO] Será malo para el negocio. [INT1_G:INTRO] Bien, ¿qué vamos a hacer, Sonny? [INT1_H:INTRO] Le tratamos como a un viejo amigo y le tenemos ocupado fuera de la ciudad. ¿De acuerdo? [INT1_I:INTRO] Hemos estado hablando de expandirnos hacia el sur. ¿Verdad? [INT1_J:INTRO] Hoy en día Vice City es oro de veinticuatro quilates. [INT1_K:INTRO] Los colombianos, los mejicanos, demonios, [INT1_L:INTRO] incluso esos refugiados cubanos se están repartiendo un buen pedazo de la acción. [INT1_M:INTRO] Pero todo es por la nieve, Sonny. [INT1_N:INTRO] ¡Ninguna de las familias tocará esa mierda! [INT1_O:INTRO] Los tiempos cambian. [INT1_P:INTRO] Las familias no pueden seguir dando la espalda mientras nuestros enemigos recogen las recompensas. [INT1_Q:INTRO] Así que enviaremos a alguien para que nos hagan el trabajo sucio [INT1_R:INTRO] y tranquilamente nos llevaremos un buen cacho. ¿Vale? [INT1_S:INTRO] ¿Quién es nuestro contacto allí? [INT1_T:INTRO] Ken Rosenberg, un estúpido abogado. [INT1_U:INTRO] ¿Cómo va a atar corto a Vercetti? [INT1_V:INTRO] No necesitamos que lo haga. [INT1_W:INTRO] Simplemente le soltaremos en Vice City [INT1_X:INTRO] y le daremos un poco de dinero para que empiece. ¿De acuerdo? [INT1_Y:INTRO] Le damos unos cuantos meses. [INT1_Z:INTRO] Luego vamos, [INT1_A1:INTRO] y le hacemos una pequeña visita, ¿de acuerdo? [INT1_A2:INTRO] Para ver cómo le va. [INT2_A:INTRO] ¡Eh, eh, tíos! ¡Aquí, eh, Ken Rosenberg! ¡Je, je, genial! [INT2_B:INTRO] Bueno, voy a llevaros a la reunión, ¿vale? [INT2_C:INTRO] He hablado con los proveedores, y ellos están muy... [INT2_D:INTRO] interesados en empezar una relación de negocios, así que... [INT2_E:INTRO] si todo esto va bien, deberíamos... [INT2_F:INTRO] empezar a obtener beneficios, lo cual es, ya sabes... [INT2_G:INTRO] Bueno. [INT2_H:INTRO] Entonces. Son hermanos, ¿vale? [INT2_I:INTRO] Uno dirige el negocio, [INT2_J:INTRO] y el otro se encarga de los vuelos. [INT2_K:INTRO] Ahora operan fuera de Méjico [INT3_A:INTRO] Bien, son los del helicóptero. [INT3_B:INTRO] De acuerdo, este es el trato. [INT3_C:INTRO] Quieren un intercambio directo en campo abierto. [INT3_D:INTRO] ¿De acuerdo? De acuerdo, permanece firme, vamos. [INT3_E:INTRO] De acuerdo, ahora con cuidado. [INT3_F:INTRO] Estoy aquí. ¡El coche está en marcha, nena! [INT3_G:INTRO] ¿Lo tienes? [INT3_H:INTRO] Colombiana, 100% con un grado de pureza A, amigo. [INT3_I:INTRO] ¿Los verdes? [INT3_J:INTRO] De diez y de veinte... usados. [INT3_L:INTRO] ¡Vamos, fuera de aquí! ¡Arranca! [INT4_A:INTRO] ¡Jodidos! ¡Estamos jodidos! [INT4_B:INTRO] Muy típico, [INT4_C:INTRO] saco la cabeza del agujero un jodido segundo, [INT4_D:INTRO] ¡Y el destino me echa la mierda a la cara! [INT4_E:INTRO] Bueno, ¡jódete! [INT4_F:INTRO] Cierra el pico y deja de quejarte. Estás vivo, ¿no? [INT4_G:INTRO] Déjame justo aquí. [INT4_H:INTRO] Ve y abandona el coche y luego vete a dormir un poco. [INT4_I:INTRO] Mañana me pasaré por tu oficina y podemos empezar a solucionar esto. [INT4_J:INTRO] De acuerdo, es una buena idea, iré a dormir un poco. [INT4_K:INTRO] ¿Qué vas a hacer tú. [INT4_L:INTRO] Me las arreglaré para volver a mi hotel. [INT4_M:INTRO] A despejar la mente y a pensar sobre esta mierda. [INT4_N:INTRO] Vale. [INTRO1:INTRO] Saco la cabeza del agujero un jodido segundo y el destino, ¡me echa la mierda en la cara! [INTRO2:INTRO] Vete a dormir un poco. [INTRO3:INTRO] ¿Qué es lo que vas a hacer? [INTRO4:INTRO] Mañana me pasaré por tu oficina y empezaremos a solucionar todo este desorden. [INT3_K:INTRO] Creo que hemos llegado a un acuerdo, amigo mío. [INT3_M:INTRO] Déjame ver. [INT2_L:INTRO] no, no, no, espera... [INT3_N:INTRO] ¡Mierda! {=================================== MISSION TABLE KENT1 ===================================} [KPM1_A:KENT1] A ver tío, te voy a salvar el culo, colega. [KPM1_B:KENT1] ¿De qué coño me estás hablando? [KPM1_C:KENT1] Conoces a ese bastardo de Díaz, Don Farlopa. [KPM1_D:KENT1] Tiene a tu colega, Lance. Dicen que tu amigo intentó atacarle por sorpresa... [KPM1_E:KENT1] pero no fue lo suficientemente sorprendente, si entiendes lo que te quiero decir. [KPM1_F:KENT1] ¿A dónde le llevó? [KPM1_G:KENT1] ¡Tranquilo! Le hicieron cruzar la ciudad hasta el desguace. [KPM1_H:KENT1] Maldita sea... ¡Chalado! [KPM1_2:KENT1] ~r~¡Se suponía que debías sacar con vida a Lance! [KPM1_3:KENT1] SALUD DE LANCE: [RESC_1:KENT1] ¿Crees que puedes usar un arma? [RESC_2:KENT1] Sí... supongo... yo también me alegro de verte. [RESC_3:KENT1] Salgamos de aquí. [RESC_4:KENT1] Ahí va todo mi cuidadoso plan, directo a la mierda, gracias a ti. La jodiste bien. [RESC_5:KENT1] Mató a mi hermano. ¿Qué esperabas que hiciese, cortarle el césped? [RESC_6:KENT1] Vamos a tener que eliminar a ese mamón de Díaz antes de que él nos elimine a nosotros. [RESC_7:KENT1] Haz que te curen y encuéntrate conmigo en el puente que va a Star Island, ¿de acuerdo? [RESC_8:KENT1] De acuerdo, entendido. [KPM1_1:KENT1] ~g~¡Lance está siendo retenido en el desguace, ve y libérale! [KPM1_4:KENT1] ~g~¡Lleva a Lance al hospital! [M_PASSN:KENT1] ¡MISIÓN SUPERADA! [KPM1_5:KENT1] ~g~¡Los hombres de Díaz te están buscando! Lleva a Lance al hospital. {=================================== MISSION TABLE KICKSTT ===================================} [KICK1_2:KICKSTT] ~r~¡No volviste a la moto lo suficientemente rápido! [KICK1_7:KICKSTT] ~r~¡Has destrozado la moto! [KICK1_8:KICKSTT] ~g~¡Sube a la moto! [KICK1_T:KICKSTT] TIEMPO EMPLEADO: [KICKTM:KICKSTT] ~b~TIEMPO DE PRUEBA: ~1~:~1~ [KICKTM2:KICKSTT] ~b~TIEMPO DE PRUEBA: ~1~:0~1~ [GETBIKE:KICKSTT] ~g~Tienes ~1~ segundos para volver a subir a una moto antes de que termine la misión. [KICK1_1:KICKSTT] ~g~Completa el circuito lo más rápidamente posible. [KICK1_6:KICKSTT] ~g~¡Bien hecho! [KICK_10:KICKSTT] ~g~Utiliza la Sanchez para completar el circuito pasando por todos los puntos de control. [KICK_12:KICKSTT] ~r~¡La has cagado! [KICK_13:KICKSTT] ~r~¡Has tardado mucho tiempo! [KICK_11:KICKSTT] ~g~Para abandonar la misión, sitúate en el ~q~marcador rosa~g~. {=================================== MISSION TABLE LAWYER1 ===================================} [LAW1_A:LAWYER1] Ve a dormir un poco, dice... [LAW1_B:LAWYER1] ¡Llevo sentado en esta silla toda la noche con las luces apagadas y bebiendo café! [LAW1_C:LAWYER1] Esto es un desastre. ¡Estamos muy jodidos, tío! [LAW1_D:LAWYER1] Estos gorilas, escúchame, van a venir aquí a arrancarme la cabeza. ¡Es ridículo! [LAW1_E:LAWYER1] ¡NO fui a la universidad para ésto! De acuerdo, ¿ahora qué coño vamos a hacer? [LAW1_F:LAWYER1] Cállate, siéntate y relájate. Te diré lo que vamos a hacer. [LAW1_G:LAWYER1] Vas a descubrir quién se llevó nuestra nieve... y después voy a cargármelos. [LAW1_H:LAWYER1] Es una buena idea. Es una idea GENIAL. Déjame pensar, déjame pensar, déjame pensar. [LAW1_I:LAWYER1] ¡Oh! Está este Coronel retirado, el coronel Juan García Cortez. [LAW1_J:LAWYER1] Él es el que me ayudó a montar esta operación [LAW1_K:LAWYER1] bien lejos de los matones establecidos de Vice City. ¿Entendido? [LAW1_L:LAWYER1] Bien, escucha. Está dando una fiesta en su lujoso yate en la bahía [LAW1_M:LAWYER1] y todos los hombres más importantes de Vice City van a estar allí. [LAW1_N:LAWYER1] Tengo invitación, por supuesto que tengo invitación, [LAW1_O:LAWYER1] pero no sueñes que vaya a asomar la cabeza por esa puerta... ¡ni lo sueñes! [LAW1_P:LAWYER1] ¡Te lo dije, cállate! Iré yo mismo... [LAW1_Q:LAWYER1] A mí también me gusta el estilo de 1978, pero esto no va a ser una fiesta con cerveza y tías en pelotas. [LAW1_R:LAWYER1] Quiero decir, sin ánimo de ofender, creo que podrías hacer que la gente se volviese a mirar por razones equivocadas. [LAW1_S:LAWYER1] ¿Qué hay de malo con cómo voy vestido? [LAW1_T:LAWYER1] Vale, mira, ten. Pásate por donde Rafael, dile que te envío yo, hará que parezcas respetable. [LAW1_U:LAWYER1] Bien, ve, vamos... [LAWP_1:LAWYER1] Buenas noches. [LAWP_2:LAWYER1] Tengo entendido que viene de parte del Sr. Rosenberg, [LAWP_3:LAWYER1] Espero que ningún problema reciente le haya afectado a su salud física, o uh, [LAWP_4:LAWYER1] o mental, Sr... ¿eh? [LAWP_5:LAWYER1] Vercetti. Tan sólo tiene un poco de... agorafobia. [LAWP_6:LAWYER1] Excelente, excelente. ¿Y usted? [LAWP_7:LAWYER1] Yo solo quiero mi farlopa. [LAWP_8:LAWYER1] Ah. Ha sido un desgraciado cúmulo de circunstancias para todos los interesados. [LAWP_9:LAWYER1] Por supuesto, he iniciado mis propias pesquisas [LAWP_10:LAWYER1] pero un asunto tan delicado llevará tiempo. [LAWP_11:LAWYER1] Quizás hablemos más tarde. ¿Eh? [LAWP_12:LAWYER1] Mientras tanto, permítame presentarle a mi hija. [LAWP_13:LAWYER1] ¡Mercedes! [LAWP_14:LAWYER1] Cara mía, ¿podrías acompañar a nuestro invitado mientras atiendo a mis obligaciones? [LAWP_15:LAWYER1] Por supuesto, papá. [LAWP_16:LAWYER1] Por favor, discúlpeme. [LAWP_17:LAWYER1] ¿Mercedes? [LAWP_18:LAWYER1] Intenta acostumbrarte. [LAWP_19:LAWYER1] En fin, déjame presentarte a algunos de nuestros invitados más distinguidos... [LAWP_20:LAWYER1] Ese es nuestro congresista Alex Shrub con la sonriente estrella de la silicona Candy Suxxx... [LAWP_21:LAWYER1] ¿Conoces a mi encantadora esposa, Laura? ¿No? [LAWP_22:LAWYER1] Bueno, desafortunadamente ella está en Alabama. Te presento a Candy. [LAWP_23:LAWYER1] Y por ahí tenemos a la estirada estrella de las Mambas de Vice City, BJ. [LAWP_24:LAWYER1] Siempre tan encantador. [LAWP_25:LAWYER1] Le paré en seco, lo hice, ¡y le dejé en una silla de ruedas! [LAWP_26:LAWYER1] ¡Ja, ja, ésa es buena! [LAWP_27:LAWYER1] Bien, ahora estoy mirando una verdadera propiedad de primera. [LAWP_28:LAWYER1] Y ese anfibio al lado de la piscina es Jezz Torrent, [LAWP_29:LAWYER1] cantante principal de Love Fist. [LAWP_30:LAWYER1] ¿Sabéis cómo juegan al ping-pong en Tailandia? [LAWP_31:LAWYER1] Dejad que os lo cuente, [LAWP_32:LAWYER1] ¡no hace falta una pala precisamente! ¿entendéis lo que quiero decir? [LAWP_33:LAWYER1] Impotente. [LAWP_34:LAWYER1] Y el trío charlatán. [LAWP_35:LAWYER1] Esa glándula sudorípara durmiente es la mano derecha de papá, González [LAWP_36:LAWYER1] y los otros dos son el pastor Richards [LAWP_37:LAWYER1] y el director de cine seudointelectual, Steve Scott. [LAWP_38:LAWYER1] Pasión con las invasoras ninfómanas, [LAWP_39:LAWYER1] entonces aparece el tiburón gigante y... [LAWP_40:LAWYER1] ¡Les arranca las pollas de un mordisco! [LAWP_41:LAWYER1] Ja, bien. Nunca has visto nada igual, ¿Verdad? [LAWP_42:LAWYER1] ¡Coronel! [LAWP_43:LAWYER1] ¡Sus fiestas tienen siempre un gran éxito! [LAWP_44:LAWYER1] Sólo puedo pedir disculpas por mi tardía llegada. [LAWP_45:LAWYER1] Ah, de nada amigo. ¿Cómo nos encontramos? [LAWP_46:LAWYER1] Nuestro negocio es muy estresante, siempre intentan desbancarnos. [LAWP_47:LAWYER1] Hay un momento para recompensar a los amigos de uno y para liquidar a los enemigos, amigo. [LAWP_48:LAWYER1] ¿Quién es el bocazas? [LAWP_49:LAWYER1] Ricardo Díaz, alias Don Farlopa. [LAWP_50:LAWYER1] ¡Mercedes! [LAWP_51:LAWYER1] Oh, estaba a punto de llevar a mi amigo de vuelta a la ciudad. [LAWP_52:LAWYER1] ¡En otro momento, Ricardo! [LAWP_53:LAWYER1] Salgamos de aquí. [LAWP_54:LAWYER1] Mejor llévame al club Pole Position. [LAW1_2:LAWYER1] ~g~Ve al barco del Coronel. [LAW1_4:LAWYER1] ~r~¡Mataste a la hija del Coronel! [LAW1_5:LAWYER1] ¿Trabajarás para mi padre? [LAW1_6:LAWYER1] Quizás. [LAW1_7:LAWYER1] ¿Te importa que pose mi mano en tu regazo? [LAW1_8:LAWYER1] Quizás... [LAW1_9:LAWYER1] Es tan difícil tener un padre rico y poderoso. Vamos. [LAW1_10:LAWYER1] ¡Nos vemos, guapo! [LAW1_11:LAWYER1] Estoy seguro. [LAW1_12:LAWYER1] Bonita moto. [LAW1_13:LAWYER1] ¡No! ¡Mi moto! [LAW1_3:LAWYER1] ~g~Lleva a la hija del Coronel al club Pole Position. [HELP20:LAWYER1] Ve al icono de la ~h~camiseta~w~ que hay en el radar para encontrar la tienda de Rafael. [LAW1_14:LAWYER1] ¡Guau!, Me encantaría probar tu moto. [LAW1_15:LAWYER1] Sí pequeño, pues recógela de Howlin' Pete's {=================================== MISSION TABLE LAWYER2 ===================================} [LAW2_A:LAWYER2] ¡Ah! Espero que te lo estés pasando bien. Porque yo me estoy volviendo loco de preocupación. ¿Qué has descubierto? [LAW2_B:LAWYER2] Que hay más criminales en esta ciudad que en la cárcel. Necesitamos un confidente de las calles... [LAW2_C:LAWYER2] Vale, déjame pensar, déjame pensar, déjame pensar... [LAW2_D:LAWYER2] ¡AH! ¡Lo tengo! [LAW2_E:LAWYER2] Está este inglés, un baboso de la industria musical, [LAW2_F:LAWYER2] se hace llamar Kent Paul. [LAW2_G:LAWYER2] Bueno, tiene la cabeza tan metida en la mayoría de los culos de Vice City [LAW2_H:LAWYER2] que si alguien sabe el paradero de 20 kilos de farlopa, [LAW2_I:LAWYER2] es este tío, ¿de acuerdo? Siempre está en el Malibú. [LAW2_J:LAWYER2] Le haré una visita. [LAW2B_A:LAWYER2] ¿De dónde saliste? [LAW2B_B:LAWYER2] He estado buscando una chica como tú durante siglos, tía... [LAW2B_C:LAWYER2] Kent Paul, tía. Por aquí soy el que manda. [LAW2B_D:LAWYER2] Estoy buscando a un tipo inglés... [LAW2B_E:LAWYER2] Muevo hilos, ¿entiendes lo que quiero decir? [LAW2B_F:LAWYER2] Te invitaré. Lo que quieras, te lo conseguiré, chica. [LAW2B_G:LAWYER2] No te preocupes por nada, tía. [LAW2B_H:LAWYER2] Piérdete, guapa. [LAW2B_I:LAWYER2] ¡Eh, eh, eh, eh, eh! [LAW2B_J:LAWYER2] ¿Eres Kent Paul? Soy amigo de Rosenberg... [LAW2B_K:LAWYER2] Rosenberg... Rosenberg... ¡Oh, ese chalado abogado de pacotilla! [LAW2B_L:LAWYER2] ¡Ese tipo podría defender a un inocente y mandarle directamente al corredor de la muerte! [LAW2B_M:LAWYER2] Ponnos otra copa, hermano. [LAW2B_N:LAWYER2] Todo el mundo es cómico. [LAW2B_O:LAWYER2] Escúchame, he perdido veinte kilos y un montón de pasta... [LAW2B_P:LAWYER2] ¿Drogas, tío? Es un juego de tontos. [LAW2B_Q:LAWYER2] ¿Qué sabes al respecto? [LAW2B_R:LAWYER2] ¡Eh, eh! A lo que iba es que, [LAW2B_S:LAWYER2] hay un chef-camello que tiene su negocio en la cocina de un hotel en Ocean Drive. [LAW2B_T:LAWYER2] Últimamente parece muy satisfecho de sí mismo. ¿Podrías ir y tantearle? [LAW2B_U:LAWYER2] Lo haré... y ya nos veremos. [LAW2B_V:LAWYER2] Sí, está bien. Venga... vete, idiota. ¡Te voy a quitar las luces de un puñetazo! [LAW2B_W:LAWYER2] Ponme una copa... ¡y dónde está esa zorra! [LAW2C_A:LAWYER2] Oh, así se hace, tipo duro. Machácale. Eso debería hacerle muy parlanchín. [LAW2C_B:LAWYER2] ¿Tú también quieres un poco? [LAW2C_C:LAWYER2] Eh, relájate. Quiero lo que tú quieres, hermano. [LAW2C_D:LAWYER2] ¿Oh, sí? ¿Y qué es eso? [LAW2C_E:LAWYER2] Tu dinero... y la nieve de mi hermano muerto. Desgraciadamente, hiciste callar a nuestro contacto. [LAW2C_F:LAWYER2] Los accidentes ocurren. Piérdete. [LAW2C_G:LAWYER2] Eh, eh, guau. No hay necesidad de hacerte el llanero solitario conmigo. [LAW2C_H:LAWYER2] Tal y como yo lo veo... somos dos hombres en una ciudad extraña. Necesitamos cubrirnos las espaldas mutuamente. [LAW2C_I:LAWYER2] Mi espalda está bien, hermano... [LAW2C_J:LAWYER2] ¿Estás seguro de eso? Ten, toma esto... [LAW2C_K:LAWYER2] ¡Sígueme! [LAW2_1:LAWYER2] ¿Qué estás mirando? [LAW2_2:LAWYER2] Mejor empieza a hablar... [LAW2_3:LAWYER2] ¡Oblígame, mamón! [LAW2_4:LAWYER2] ¡Por aquí! [LAW2_5:LAWYER2] Voy a ver qué puedo averiguar. Te estaré vigilando, Tommy. [LAW2_6:LAWYER2] ~g~Ve al club Malibú y busca a Kent Paul. [LAW2_7:LAWYER2] ~g~Ve y busca al chef en Ocean Drive. [LAW2_10:LAWYER2] ~g~Regresa al hotel. [LAW2_11:LAWYER2] ~g~Recoge su teléfono móvil. [LAW2_12:LAWYER2] ¡Adquirido un teléfono móvil! Ahora puedes recibir llamadas telefónicas. [LAW2_13:LAWYER2] ~g~¡Has dejado a Lance atrás! ¡Ve y tráele! [LAW2_14:LAWYER2] ¡Tenemos que salir pitando de aquí! [GUN_2A:LAWYER2] ¡Mantén ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar automáticamente~w~ y pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para ~h~disparar! [GUN_2C:LAWYER2] ¡Mantén ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar automáticamente~w~ y pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para ~h~disparar! [GUN_2D:LAWYER2] ¡Mantén ~h~~k~~PED_LOCK_TARGET~ ~w~para ~h~apuntar automáticamente~w~ y pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para ~h~disparar! [HELP17:LAWYER2] Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para atacar al chef. [HELP18:LAWYER2] Pulsa ~h~~k~~PED_FIREWEAPON~~w~ para atacar al chef. [LAW3_11:LAWYER2] Sitúate en el ~q~marcador rosa~w~ para ver las armas disponibles. [LAW3_12:LAWYER2] Puedes seleccionar armas pulsando ~h~izquierda~w~ o ~h~derecha~w~ en los ~h~botones de dirección~w~. [LAW3_13:LAWYER2] Si dispones de suficiente dinero, puedes comprar armas pulsando ~h~~k~~PED_SPRINT~~w~. [LAW3_14:LAWYER2] Puedes salir de la tienda pulsando ~h~~k~~VEHICLE_ENTER_EXIT~~w~. [LAW3_15:LAWYER2] Ve al ~h~icono de la pistola~w~ que hay en el radar para encontrar el ~h~Ammu-Nation~w~. [LAW2_15:LAWYER2] ~g~Ve a Ammu-Nation. [LAW2_K:LAWYER2] Tómatelo con calma. [LAW2_16:LAWYER2] Tienes que entender una cosa sobre esta ciudad. Tienes que saber protegerte. [LAW2_17:LAWYER2] Venga, la tienda de armas local está a un par de manzanas de aquí. [LAW2_18:LAWYER2] Tommy, cada hombre necesita un poco de rock de vez en cuando. [LAW2_19:LAWYER2] Este de aquí es el club de striptease Pole Position. Quizás quieras entrar alguna vez. {=================================== MISSION TABLE LAWYER3 ===================================} [LAW3_A:LAWYER3] ¡Aagh! ¡Oh, por el amor de Dios, eres tú! Oh, Jesús, ¡voy a necesitar pantalones nuevos! [LAW3_B:LAWYER3] Eh, esos psicópatas del norte han estado entrometiéndose y pronto vendrán aquí. [LAW3_C:LAWYER3] Bien, ¿dónde está el maldito dinero? [LAW3_D:LAWYER3] Relájate, relájate. Aún no hemos llegado a esa parte. [LAW3_E:LAWYER3] Pensé que te estabas ocupando de esto, de verdad que sí. [LAW3_F:LAWYER3] Y ahora esos tipos dicen que les tenemos que hacer un favor. [LAW3_G:LAWYER3] Quieres decir que yo tengo que hacerles un favor. [LAW3_H:LAWYER3] Oh, por supuesto, eso es lo que quiero decir. ¿Doy yo la impresión de poder intimidar a un jurado? [LAW3_I:LAWYER3] No podría intimidar a un niño y créeme, lo he intentado. [LAW3_J:LAWYER3] Ahora mira, es eso, o al primo de Forelli, Giorgio, le caerán cinco años por fraude. [LAW3_K:LAWYER3] ¡Tienes que encargarte de esos tipos! [LAW3_L:LAWYER3] Comprendo. Ayuda al jurado a que cambie de idea. No te preocupes por ello. [LAW3_M:LAWYER3] ¡No no no no... NO! Ya intenté eso. El caso del jurado no fue tan bien, [LAW3_N:LAWYER3] así que HAZ que cambien de idea. [LAW3_1:LAWYER3] Giorgio envía sus saludos. [LAW3_2:LAWYER3] Recuerda, ''culpable'' es una palabra fea. [LAW3_3:LAWYER3] Inocente hasta que yo diga lo contrario. [LAW3_4:LAWYER3] Sabes que no es culpable. [LAW3_5:LAWYER3] ¿Recuerdas a Giorgio? Recuerda que es inocente. [LAW3_6:LAWYER3] No culpable. ¿Comprendido? Bien. [LAW3_8:LAWYER3] ~r~¡Mataste a un miembro del jurado! [LAW3_9:LAWYER3] ~g~¡Destroza el coche del miembro del jurado para sacarle de ahí! [HELP40:LAWYER3] Puedes destrozar coches usando el martillo o un arma similar. [HELP41:LAWYER3] o puedes aplastarlas con un vehículo [LAW3_20:LAWYER3] ~g~¡Destroza el coche del miembro del jurado! [LAW3_21:LAWYER3] ¡No puedo creer lo que está pasando! [LAW3_22:LAWYER3] ¡Increíble! [LAW3_23:LAWYER3] ¡Ya! ¡Ya! ¡Entendido! [LAW3_24:LAWYER3] ~g~Ese martillo será útil. [LAW3_7:LAWYER3] ~g~¡Ve y coacciona a los dos miembros del jurado pero NO los mates! [HELP23:LAWYER3] Puedes ir al ~h~icono del martillo~w~ que hay en el radar si quieres comprar armas blancas en la ferretería. [LAW3_16:LAWYER3] Estúpido cretino de Florida. [LAW3_17:LAWYER3] ¡Quítate de en medio! {=================================== MISSION TABLE LAWYER4 ===================================} [LAW4_A:LAWYER4] Avery, ni que decir tiene... ¡Tommy! ¡Tommy! ¿Algún progreso? No, no, no... cuéntamelo luego, cuéntamelo luego. [LAW4_B:LAWYER4] Tommy, éste es Avery Carrington... ¿Creo que os conocisteis en la fiesta? [LAW4_C:LAWYER4] No en persona. [LAW4_D:LAWYER4] ¿Qué tal? [LAW4_E:LAWYER4] Avery tiene una proposición. [LAW4_F:LAWYER4] ¿No tenemos otras cosas en mente? [LAW4_G:LAWYER4] Estoy intentando mantener todo bajo control así que, ¿podrías darme un respiro? [LAW4_H:LAWYER4] Estoy tenso como un alambre e incluso si estoy muerto al final de la semana, me gustaría pensar que no moriré pobre. [LAW4_I:LAWYER4] Bien, calmaos, los dos. [LAW4_J:LAWYER4] Hijo, si me ayudas, me encargaré de que cualquier capullo que te esté molestando se eche una buena siesta. [LAW4_K:LAWYER4] De acuerdo, ¿qué puedo hacer por ti? [LAW4_L:LAWYER4] Esta compañía de reparto tiene el almacén en unos terrenos de primera. No venderán. [LAW4_M:LAWYER4] Están aguantando como una vieja rata de la pradera, así que tenemos que ir allí y ahumar a esas alimañas para hacerlas salir. [LAW4_N:LAWYER4] Ve allí y alborota el avispero. [LAW4_O:LAWYER4] Los de seguridad tendrán las manos ocupadas y entonces podrás colarte y dejarles fuera de combate. [LAW4_P:LAWYER4] Y podrías pasarte por Rafael para cambiarte de ropa. Podrías estar allí un rato, pero sí, ve. [LAW4_Q:LAWYER4] Debería ser un motín. [LAW4_R:LAWYER4] Si las cosas salen como deberían, pásate alguna vez por mi oficina... [LAW4_1:LAWYER4] Por favor, dispérsense. ¡La dirección discutirá cualquier queja de la forma apropiada! [LAW4_2:LAWYER4] Por favor, dispérsense. ¡Vuelvan a sus casas! [LAW4_3:LAWYER4] ¡Por favor, dispérsense! ¡Esto no es apropiado! [LAW4_4:LAWYER4] Por favor, dispérsense. Todos acabaréis en la calle. [LAW4_5:LAWYER4] ¡Desenfundad, chicos! ¡Vamos a romper unos cuantos cráneos comunistas! [LAW4_13:LAWYER4] ~g~Empieza a luchar con al menos 4 trabajadores para iniciar una revuelta. [LAW4_14:LAWYER4] ~g~¡Destruye las furgonetas del complejo! [HELP38:LAWYER4] Si matas a alguien que lleva un arma, la dejará caer. [HELP39:LAWYER4] Puedes disparar a los barriles explosivos, pero mantén la distancia. {=================================== MISSION TABLE MIAMI_1 ===================================} [T4X4_1A:MIAMI_1] ~g~Tienes ~1~ segundos para pasar por ~y~24~g~ puntos de control. ~g~Puedes atravesarlos en ~y~CUALQUIER ORDEN. [T4X4_1B:MIAMI_1] ~y~ATRAVIESA~g~ el primer punto de control para poner en marcha el ~r~CRONÓMETRO. [T4X4_1C:MIAMI_1] ¡~1~ de 24! [GETBIK1:MIAMI_1] ¡Tienes ~1~ segundos para subirte en una PCJ 600! [GETBIK3:MIAMI_1] ~r~Necesitas una PCJ 600 para realizar esta misión! {=================================== MISSION TABLE MM ===================================} [BLOD_04:MM] ESTADO DEL COCHE: [BLOD_05:MM] ~g~TIEMPO OBJETIVO: ~1~ Minuto [BLOD_06:MM] ~g~TIEMPO OBJETIVO: ~1~ Minutos [BLOD_07:MM] NUEVO Mejor Tiempo: ~1~ Segundos [BLOD_08:MM] Coches destrozados: ~1~ [BLOD_09:MM] ~1~ $ [BLOD_10:MM] ¡GANADOR! [BLOD_01:MM] Pasa por todos los puntos de control para aumentar tu tiempo total. [BLOD_02:MM] Fracasarás si tu tiempo total llega a cero. [BLOD_03:MM] ¡Consigue que tu tiempo total sean superior al objetivo a superar! {=================================== MISSION TABLE OVALRIG ===================================} [HOTR_01:OVALRIG] ~g~La carrera dura 12 vueltas. Solamente los puestos 1, 2 y 3 obtienen premios en metálico. [HOTR_02:OVALRIG] ~g~Si destrozas tu coche serás descalificado. [HOTR_03:OVALRIG] ~g~Cuando tu coche sufra daños podrás repararlo en boxes. [HOTR_04:OVALRIG] ~g~Este es el camino para salir del estadio. [HOTR_05:OVALRIG] Estado del coche: [HOTR_06:OVALRIG] Vueltas: [HOTR_07:OVALRIG] Nuevo mejor tiempo: ~1~:0~1~ [HOTR_08:OVALRIG] Tiempo: ~1~:~1~ [HOTR_10:OVALRIG] Tiempo de carrera: [HOTR_09:OVALRIG] Posición: [HOTR_12:OVALRIG] ~r~¡Tu coche está destrozado! [HOTR_13:OVALRIG] ~r~¡No ganaste la carrera! [HOTR_14:OVALRIG] ~r~¡Has sido descalificado! [HOTR_15:OVALRIG] Tiempo: ~1~:~1~ [HOTR_16:OVALRIG] Tiempo: ~1~:0~1~ [HOTR_17:OVALRIG] Mejor Tiempo: ~1~:~1~ [HOTR_18:OVALRIG] Mejor Tiempo: ~1~:0~1~ [HOTR_19:OVALRIG] Mejor Tiempo: NA [HOTR_20:OVALRIG] Nuevo Mejor Tiempo: ~1~:~1~ [HOTR_21:OVALRIG] Nuevo Mejor Tiempo: ~1~:0~1~ [HOTR_22:OVALRIG] Mejor Resultado: NA [HOTR_23:OVALRIG] Mejor resultado: 1 [HOTR_24:OVALRIG] Mejor resultado: 2 [HOTR_25:OVALRIG] Mejor resultado: 3 [HOTR_26:OVALRIG] Mejor resultado: ~1~ [HOTR_27:OVALRIG] Mejor Tiempo de Vuelta: ~1~.~1~ segundos [HOTR_28:OVALRIG] Mejor Tiempo de Vuelta: ~1~.0~1~ segundos [HOTR_29:OVALRIG] ~1~ $ [HOTR_30:OVALRIG] PUESTO: 1 [HOTR_31:OVALRIG] PUESTO: 2 [HOTR_32:OVALRIG] PUESTO: 3 [HOTR_33:OVALRIG] Mejor tiempo de vuelta: ninguno [HOTR_11:OVALRIG] Nuevo mejor tiempo de vuelta: ~1~,~1~ segundos [HOTR_34:OVALRIG] Nuevo mejor tiempo de vuelta: ~1~,0~1~ segundos {=================================== MISSION TABLE PHIL1 ===================================} [PHIL1_A:PHIL1] ¿Phil? [PHIL1_B:PHIL1] ¡CORRE! [PHIL1_C:PHIL1] ¡Corre! [PHIL1_E:PHIL1] Mierda Phil, ¿te bebes ese mejunje? [PHIL1_F:PHIL1] Diablos, no tienes que bebértelo... [PHIL1_G:PHIL1] solo tienes que olerlo para colocarte. [PHIL1_H:PHIL1] Escucha, Phil, dijiste que podrías proporcionarme algo de potencia de fuego... [PHIL1_I:PHIL1] Por supuesto. [PHIL1_J:PHIL1] Hay un traficante de armas mejicano que me ha estado pisando el negocio últimamente. [PHIL1_K:PHIL1] Suele realizar su ronda semanal a esta hora. [PHIL1_L:PHIL1] Haz caer la mercancía de la parte de atrás de sus camiones antes de que acabe fuera de nuestro alcance. [PHIL1_M:PHIL1] Y mientras estás en ello me estarás haciendo un favor. [PHIL1_N:PHIL1] Luego acaba con él. [PHI1_01:PHIL1] ~g~Ve y pilla las armas de la parte trasera de los camiones del traficante. [PHI1_02:PHIL1] ~g~El traficante de armas ha perdido la carga. Destroza la caja y recoge el arma. [PHI1_03:PHIL1] ~g~Parece que han llamado pidiendo refuerzos. [PHI1_04:PHIL1] ~g~Ahora ve y acaba con el resto de los traficantes. [PHI1_HP:PHIL1] Cuando utilices Detonador de Granadas, lanza una granada y después provoca la explosión en cualquier momento. [PHIL1_O:PHIL1] ¡Jooooodeeeeer! [PHIL1_D:PHIL1] ¡Nunca acerques una llama a uno de los alambiques de boomshine de Phil Cassidy! {=================================== MISSION TABLE PHIL2 ===================================} [PHIL2_A:PHIL2] Hola, Phil, ¿cómo te va? [PHIL2_B:PHIL2] Ehhh, Tommy. ¿Cómo estás? Hace tanto tiempo... [PHIL2_C:PHIL2] Te juro que deberías dejar ese mejunje, tío. [PHIL2_D:PHIL2] Huele como decapante de pintura. Hace que me ardan los ojos... [PHIL2_E:PHIL2] Shhh, shhh, tú mismo, Tommy, [PHIL2_F:PHIL2] y ven aquí porque hay algo que quiero enseñarte... algo. [PHIL2_G:PHIL2] ¡Dios! ¿Debería poder oler eso desde aquí? Me estoy mareando. [PHIL2_H:PHIL2] No te preocupes por el olor, Tommy, mira esto. [PHIL2_I:PHIL2] Las malditas pilas baratas o algo así. Hay más en el banco. [PHIL2_J:PHIL2] ¡TA-DAAA! [PHIL2_K:PHIL2] ¡Maldición! [PHI2_01:PHIL2] ~g~Rápido, lleva a Phil al hospital. [PHI2_03:PHIL2] ~r~¡Phil Cassidy está muerto! ¿Ahora quién va a suministrar armas en Vice City? [PHI2_05:PHIL2] ¡Al hospital no, tío! ¡Demasiados polis y vietcongs! [PHI2_06:PHIL2] Hay un excirujano del ejército que me debe unos cuantos favores... y un cortador de césped. [PHI2_07:PHIL2] Tiene un lugar llamado Little Havana, oh mira, un pez gigante. [PHI2_08:PHIL2] ¡Cuidado! ¡Charlies en la línea de los árboles! [PHI2_09:PHIL2] ¿Soy yo o las carreteras están hechas de gelatina? [PHI2_10:PHIL2] Cuchara rota a madre gallina, ¿me recibes? [PHI2_11:PHIL2] ¡Cucharita, rita, rit, rit! [PHI2_12:PHIL2] ¡Viene a por mí, chico! [PHI2_13:PHIL2] Hay alas de plumas negras aleteando por todas partes... [PHI2_14:PHIL2] Es hermoso, tío... es hermoso... pero hace tanto frío... [PHI2_15:PHIL2] 10-4 tenemos un conductor borracho. [PHI2_04:PHIL2] SALUD DE PHIL: [PHI_AS1:PHIL2] LOCAL DE PHIL CONSOLIDADO [PHI_AS2:PHIL2] ~g~Nuevas armas disponibles para comprar en el Local de Phil. {=================================== MISSION TABLE PIZZA ===================================} [PIZ1_01:PIZZA] ~g~Ve a entregar estas pizzas, debes lanzar la pizza a los clientes. Pasa cerca para lanzar las pizzas. [PIZ1_02:PIZZA] ~g~Has lanzado todas tus pizzas, vuelve y recoge algunas más. [PIZ1_05:PIZZA] ~g~Tienes cinco minutos para entregar los pedidos antes de que los clientes telefoneen a otra pizzería. [PIZ1_07:PIZZA] ~r~¡Mataste al cliente! Estás despedido. [PIZ1_08:PIZZA] ~r~Te has quedado sin tiempo. Estás despedido. [PIZ1_09:PIZZA] ~r~¡Destruiste nuestra moto! Estás despedido. [PIZ1_11:PIZZA] ¡Eh! ¡Vuelve a subir a la moto! [PIZ1_12:PIZZA] Pizzas restantes: [PIZ1_06:PIZZA] Pulsa ~h~~k~~TOGGLE_SUBMISSIONS~~w~ cuando estés en la moto para cancelar la misión. [PIZ1_13:PIZZA] Reparte éstas bien calentitas. [PIZ1_14:PIZZA] Amigo, te toca repartir éstas. [PIZ1_15:PIZZA] A ver, tío, repártelas rápido. [PIZ1_16:PIZZA] ¿Qué estás esperando, tío? Tienes pizzas que repartir. [PIZ1_17:PIZZA] Ya sé que no querías ser el chico de las pizzas, vale me importa un cuerno. [PIZ1_18:PIZZA] Reparte éstas. [PIZ1_19:PIZZA] Hay que repartir éstas. [PIZ1_20:PIZZA] Venga hombre, reparte estas cosas o estás despedido. [PIZ1_21:PIZZA] Tenemos gente esperando amigo. [PIZ1_22:PIZZA] ¿Qué estás esperando? ¡Hay que repartir esto! [PIZ1_23:PIZZA] Haz el reparto de la maldita comida hombre. [PIZ1_24:PIZZA] Hay que repartir éstas, amigo. [PIZ1_25:PIZZA] Tío, ¿te puedes llevar éstas? [PIZ1_26:PIZZA] Oye, haz el reparto pronto, vamos amigo. [PIZ1_27:PIZZA] Venga, tenemos prisa, haz ya el reparto. [PIZ1_28:PIZZA] ¿Otra vez tú? Bueno, reparte éstas deprisa, amigo. [PIZ1_29:PIZZA] No pierdas el tiempo amigo. [PIZ1_30:PIZZA] Venga ya vago hijo de puta, reparte esta mierda de una vez. [PIZ1_31:PIZZA] No tendrás nunca un ascenso a menos que te muevas más rápido esta vez. [PIZ1_32:PIZZA] ~r~¿La pizza es demasiado caliente para encargarte de ella? [PIZ1_33:PIZZA] ~g~Vuelve al restaurante a por más pedidos. [PIZ1_34:PIZZA] ~g~Pizza repartida, aquí está tu dinero. [PIZ_WON:PIZZA] Misión de Pizza Completada. Tu salud Personal máxima ha aumentado a 150. {=================================== MISSION TABLE PORN1 ===================================} [POR1_A:PORN1] Acción. [POR1_B:PORN1] ¡Guau! Eso sí que es grande. [POR1_C:PORN1] 30 cm, eso es para pensárselo, nene. [POR1_D:PORN1] ¡CORTEN! ¿Quién es este idiota? ¡Tú! ¡Tú! ¿Por qué estás en el set? ¿POR QUÉ? [POR1_E:PORN1] ¿De qué va toda esta basura? [POR1_F:PORN1] ¿Alienígenas? ¿Cañas de pescar? [POR1_G:PORN1] ¿Quién ha visto alguna vez a un tiburón tan grande? [POR1_H:PORN1] Hay que desechar todo este material. [POR1_I:PORN1] ¿Por qué te metiste en este negocio, mamón? [POR1_J:PORN1] ¿Eh? [POR1_K:PORN1] ¡Por los chochos, evidentemente! ¿Qué es esto? [POR1_L:PORN1] Éste es mi arte. ¡SEGURIDAD! [POR1_M:PORN1] Mira, pomposo gilipollas, ahora eres de mi propiedad. Yo poseo todo esto. [POR1_N:PORN1] Vamos a cambiar este lugar... [POR1_O:PORN1] Voy a hacerte rico. [POR1_P:PORN1] Usted es... Usted... ¿Usted es Tommy Vercetti? Pero yo creí que usted era... [POR1_Q:PORN1] Eso es. [POR1_R:PORN1] Vamos a hacer algunos cambios por aquí y vamos a empezar a hacer dinero de verdad. [POR1_S:PORN1] En realidad, alguna vez has pensado en... [POR1_T:PORN1] Pero primero, vamos a necesitar unas chicas guapas de verdad... [POR1_U:PORN1] Sí, las chicas están bien, pero tú... ¡guau! [POR1_02:PORN1] ~g~ Ve y elimina al chulo de Candy, luego vuelve y recoge a Candy. [POR1_04:PORN1] Candy. Estoy buscando una estrella de cine. ¿Estás interesada? [POR1_05:PORN1] ¡Claro! Pero tendrás que hablar con mi agente... [POR1_06:PORN1] ¿Qué DEMONIOS estás haciendo? [POR1_07:PORN1] ¡Hoy deberías haberte quedado en casa! [POR1_7B:PORN1] ¿Puedes creer a este gilipollas? [POR1_08:PORN1] ¡Eh, Mercedes! [POR1_09:PORN1] ¡Eh, Tommy! ¿Quieres ir de fiesta? [POR1_10:PORN1] Ahora no, preciosa. ¿Te interesa hacer algunas películas? [POR1_11:PORN1] Por supuesto. Mientras que sea una peli barata y cutre. [POR1_13:PORN1] ~g~Lleva a las chicas de vuelta al Estudio para que conozcan a Steve. [POR1_14:PORN1] ¡Contratada! [POR1_15:PORN1] Eh Tommy, ¿¡qué, vienes a entrar en calor!? [POR1_17:PORN1] ¡Guau, un tiburón genial! [POR1_18:PORN1] ~r~¡Mercedes está muerta! [POR1_20:PORN1] Tommy, ¿a dónde vas? ¡Vuelve aquí! [POR1_21:PORN1] ¿A dónde vas? [POR1_22:PORN1] Tommy, ¿cuando vamos a pasar algún tiempo a solas? [POR1_01:PORN1] ~g~¡Candy Suxxx sería perfecta para el papel principal! [POR1_12:PORN1] ~g~Ve con Candy a encontrarte con Mercedes. [POR1_16:PORN1] Quizás después, nena... [POR1_24:PORN1] ~g~Vuelve y recoge a Candy. [POR1_25:PORN1] ~g~Te has dejado atrás a Candy, ve y tráela. [POR1_23:PORN1] ~g~Candy estará ocupándose de unos asuntos en el ~h~centro de la ciudad~g~. [POR1_26:PORN1] ~g~Aquí está Candy, parece que ha estado otra vez con el congresista Shrub. [POR1_27:PORN1] Venga, vamos. [POR1_28:PORN1] ¡Tommy ten cuidado! ¡Aún no he asegurado mis implantes! [POR1_29:PORN1] ¿Y a esto le llamas conducir? [POR1_30:PORN1] ¡No podré hacer ningún numerito porno después de esto! [POR1_31:PORN1] ¿Qué? ¿Me estás intentando matar? ¡Creía que era la estrella era yo! {=================================== MISSION TABLE PORN2 ===================================} [POR2_A:PORN2] ¿Cómo va el rodaje, Steve? [POR2_B:PORN2] Bien, Candy es una fuera de serie y esa chica nueva ¡es insaciable! [POR2_C:PORN2] Se repasó a la mitad del reparto antes de que pudiese medir la luz. [POR2_D:PORN2] De todas formas, eh, mañana vamos a exteriores a rodar las escenas del barco... [POR2_E:PORN2] ¿Escenas del barco, qué escenas del barco? [POR2_F:PORN2] Los pescadores son presa de la pasión cuando aparece el tiburón gigante... [POR2_G:PORN2] ¿Qué te dije del tiburón gigante? [POR2_H:PORN2] Dije, ''SIN TIBURÓN GIGANTE'', ¿de acuerdo? [POR2_I:PORN2] ¡Tú solo tienes que mantener las cámaras apuntando a los conejos! [POR2_J:PORN2] De acuerdo, de acuerdo, eh Tommy, uno tiene que intentarlo, ¿de acuerdo? [POR2_K:PORN2] ¿Has hecho que impriman esos folletos? [POR2_L:PORN2] Sí, pero nadie va a distribuir esas cosas, quiero decir que... [POR2_M:PORN2] son demasiado... poco imaginativos. [POR2_N:PORN2] No te preocupes por eso. [POR2_O:PORN2] Tengo mis propias ideas para la distribución. [POR2_P:PORN2] Vale. Candy, ven a mi caravana. [POR2_01:PORN2] ~g~Hay un hidroavión que se utilizó como atrezzo en una antigua película independiente a la vuelta de los estudios. [POR2_02:PORN2] ~g~Escoge uno de los puntos de control para empezar a dejar caer los folletos. [POR2_03:PORN2] ~g~Deja caer los folletos por todo el camino hasta el último punto de control. [POR2_04:PORN2] ~r~¡COMBUSTIBLE BAJO! [POR2_05:PORN2] Utilízalo para distribuir los folletos por toda la ciudad. [DILDO:PORN2] Combustible del Skimmer: [POR2_Q:PORN2] ¡Jo!, tío. [PORN2_9:PORN2] ~g~Tienes ~1~ segundos para volver a subir a una Skimmer antes de que termine la misión. {=================================== MISSION TABLE PORN3 ===================================} [POR3_A:PORN3] Vale, ¿cuál es el problema ahora? [POR3_B:PORN3] ¡SSShhhh! [POR3_C:PORN3] Bien, después de este encuentro cercano con las invasoras ninfómanas [POR3_D:PORN3] nuestro héroe se encuentra incapaz de pensar en nada excepto en esta enorme montaña fálica... [POR3_E:PORN3] y es entonces cuando queremos hacer la escena con la tina de puré de patatas, pero entonces... [POR3_F:PORN3] ¡Me importa una mierda! [POR3_G:PORN3] ¡Sigue, sigue! [POR3_H:PORN3] Eh Tommy... [POR3_I:PORN3] ¿Mencionaste algo sobre un problema legal al teléfono? [POR3_J:PORN3] ¡Vaya que sí! El congresista Alex Shrub se ha subido al tren preelectoral y va tras el voto puritano. [POR3_K:PORN3] Hay rumores de que va a apoyar medidas restrictivas, digamos, [POR3_L:PORN3] para el aspecto más lujurioso de la gran industria del entretenimiento de esta nación. [POR3_M:PORN3] Genial. [POR3_N:PORN3] ¡Candy! Tú conoces a Shrub, [POR3_O:PORN3] ¿llegasteis a algo pervertido? [POR3_P:PORN3] ¡Oh sí, oh sí, oh sí! ¡Sí sí sí SÍ! ¡OOOoooh! [POR3_Q:PORN3] Por favor dime que filmaste eso. [POR3_R:PORN3] ¿Eso era parte del eh... o estaba hablando conmigo...? [POR3_S:PORN3] Eh, uno nunca podría decirlo... De todas formas... [POR3_T:PORN3] Probablemente sea mejor que la sigas después del rodaje, [POR3_U:PORN3] para ver si te lleva a su nuevo nido de amor. [POR3_V:PORN3] ¿Tienes una cámara? [POR3_X:PORN3] Vale, conseguidle una cámara. [POR3_02:PORN3] ~r~¡Has asesinado al Congresista! Ahora no habrá modo de que puedas chantajearle. [POR3_03:PORN3] ~r~Has alertado a la protección del Congresista, le sacarán de aquí inmediatamente. [POR3_04:PORN3] Candy, ¿podrías llamarme Marta? [POR3_05:PORN3] Oh Alex, quiero decir Marta. Lo que tú digas. [POR3_06:PORN3] Marta, alguien está mirando... qué pervertido. [POR3_07:PORN3] Tú, el de ahí. Dame esa cámara. [POR3_01:PORN3] ~g~Sigue la ~h~limusina~g~ de Candy. [POR3_15:PORN3] ~r~¡Has destruido la limusina de Candy! [POR3_17:PORN3] ~g~Regresa a la compañía de pornografía con las fotos. [POR3_19:PORN3] ~r~¡Te has quedado sin película! [POR3_21:PORN3] ~g~¡Has perdido la limusina de Candy! [POR3_22:PORN3] ~g~El Hotel WK Chariot frente a este balcón debería proporcionar el lugar ideal para la sesión fotográfica. [POR3_23:PORN3] ~g~Hay una puerta lateral que te permitirá acceder al hotel. [POR3_08:PORN3] Mantén pulsado ~h~~k~~PED_LOCK_TARGET~ ~w~para~h~ enfocar~w~ con la cámara. [POR3_09:PORN3] Mantén pulsado ~h~~k~~PED_LOCK_TARGET~ ~w~para~h~ enfocar~w~ con la cámara. [POR3_10:PORN3] Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~ ~w~para ~h~acercar el zoom ~w~con la cámara y ~h~~k~~PED_SNIPER_ZOOM_OUT~ ~w~para ~h~alejarlo ~w~de nuevo. [POR3_11:PORN3] Pulsa ~h~~k~~PED_SNIPER_ZOOM_IN~ ~w~para ~h~acercar el zoom ~w~con la cámara y ~h~~k~~PED_SNIPER_ZOOM_OUT~ ~w~para ~h~alejarlo ~w~de nuevo. [POR3_12:PORN3] Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para sacar una foto. [POR3_13:PORN3] Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para sacar una foto. [POR3_14:PORN3] Pulsa ~h~~k~~PED_FIREWEAPON~ ~w~para sacar una foto. [POR3_20:PORN3] ~g~Si necesitas transporte, utiliza el ~h~Sparrow~g~ que está en la parte de atrás. [POR3_16:PORN3] ~g~Necesitas tres buenas fotos de Alex Shrub con Candy para chantajearle. [POR3_24:PORN3] FOTOS HECHAS: {=================================== MISSION TABLE PORN4 ===================================} [POR4_A:PORN4] Lo siento, pero no puedo tragarme eso ahora. [POR4_B:PORN4] ¡Oh, VENGA, cariño! [POR4_C:PORN4] La tiene como un cachalote, por el amor de Dios, [POR4_D:PORN4] ¿cómo es que no puedes sentir el papel? [POR4_E:PORN4] Pero Stevie... [POR4_F:PORN4] ¿Cómo está mi director estrella? [POR4_G:PORN4] Oh, tío. La lucha entre la integridad artística y [POR4_H:PORN4] la acción cargante y bombeante continúa sin perder intensidad. [POR4_I:PORN4] Y antes de que puedas preguntarlo, sí, los cuatro vídeos serán lanzados en sus... [POR4_J:PORN4] Cariño, podrías POR FAVOR mantener la anaconda dentro del plano, [POR4_K:PORN4] ¡cuesta por hora más de lo que cuestas tú! [POR4_L:PORN4] Oh, lo siento, Steve. [POR4_M:PORN4] Estaba pensando, necesitamos algún tipo de gran truco publicitario para promover de verdad el lanzamiento. [POR4_N:PORN4] Algo que tenga un verdadero impacto en la ciudad, ¿tienes alguna idea? [POR4_O:PORN4] Bueno, en los viejos tiempos solían organizar fiestas, [POR4_P:PORN4] estrellas, limusinas, el cielo nocturno con los focos entrecruzados... [POR4_Q:PORN4] ¿Focos? Tengo una idea... [POR4_R:PORN4] ...sí, sí, sí. Los numeritos de lentejuelas, y las limusinas, oh, premieres. [POR4_S:PORN4] Oh, sí, madam, por supuesto madam, [POR4_T:PORN4] y la prensa, y el bombardeo de luces... [POR4_01:PORN4] ~g~Ve al ~y~centro~g~ y ajusta los focos sobre lo alto del edificio. [POR4_02:PORN4] ~g~Necesitarás una moto rápida para saltar de un tejado a otro. El guardia de seguridad normalmente lleva una ~y~PCJ 600~g~ para ir a trabajar... [POR4_03:PORN4] ~g~Necesitarás subir a los tejados de los edificios. Debería de haber un ascensor que lleve a una de las oficinas superiores... [POR4_06:PORN4] ~g~Vuelve a la oficina inferior si de nuevo necesitas acceso a los tejados. [POR4_07:PORN4] ~g~Necesitarás una moto para poder saltar de un edificio a otro. [POR4_08:PORN4] ~g~Atraviesa la ventana para empezar el recorrido. Tienes hasta las 07:00 antes de que haya demasiada luz como para subir allí sin ser visto. [POR4_09:PORN4] ~g~Los marcadores rosas te mostrarán a qué edificio saltar después. [POR4_10:PORN4] ~r~Hay demasiada luz como para subir ahí sin ser visto. [POR4_11:PORN4] ~g~Vuelve a la escalera si necesitas acceder de nuevo a los tejados. [POR4_05:PORN4] ~g~Estas escaleras llevan a una oficina en el piso inferior. [POR_AS1:PORN4] ESTUDIO CINEMATOGRÁFICO CONSOLIDADO [POR_AS2:PORN4] ~g~Inter Global Films generará ahora unos ingresos máximos de hasta ~1~ $. Asegúrate que lo recaudas de forma regular. {=================================== MISSION TABLE PROT1 ===================================} [PRO1_B:PROT1] No puedo soportar este look. Tommy, ¿Tú que dices? ¿Qué te parece si ponemos un bar en... [PRO1_D:PROT1] Escúchame, [PRO1_E:PROT1] Es hora de tomar esta ciudad. Está ahí, esperándonos. [PRO1_F:PROT1] Necesitamos empezar a ampliar nuestro territorio, [PRO1_G:PROT1] vamos a dejar que Vice City sepa que somos los nuevos tíos importantes de la ciudad, ¿sabes a lo que me refiero? [PRO1_I:PROT1] Lo que necesitas es un frente legítimo, Tommy, una verdadera propiedad. A mí nunca me ha hecho daño. [PRO1_J:PROT1] Necesitamos empezar a utilizar la fuerza o podemos despedirnos de todo el trabajo duro que hemos hecho. [PRO1_K:PROT1] ¡Los negociantes locales saben que Díaz está muerto, y se niegan a pagar la protección! [PRO1_L:PROT1] Oh. Podríamos intentar el soborno... [PRO1_M:PROT1] ¿Soborno? ¡Que le jodan al soborno! Te mostraré cómo hacer que se asusten. [PRO1_01:PROT1] ~g~Ve a atracar los escaparates de las tiendas y tendrás a los propietarios suplicando protección. [PRO1_03:PROT1] ~r~Se suponía que deberías atacar y salir corriendo, no atacar y tomarte un café. [PRO1_04:PROT1] ¡Mi sustento, destruido! [PRO1_05:PROT1] ¡Arruinado... ESTOY ARRUINADO! [PRO1_06:PROT1] ¡Pagaré lo que sea por protección! [PRO1_07:PROT1] ¡Mi hermoso escaparate destruido! [PRO1_08:PROT1] Mi tienda. Mi maravillosa tienda. [PRO1_09:PROT1] Vercetti. Recuerda el nombre. [PRO1_10:PROT1] Ahora dirijo esta ciudad. ¡YO! [BUYP1:PROT1] Ahora puedes comprar propiedades en ciertas zonas de la ciudad. [BUYP2:PROT1] Si ves un indicador con una casa verde, puedes comprar esa propiedad. [PRO1_N:PROT1] Volveré aquí en cinco minutos... [PRO1_11:PROT1] ~g~Ve al ~y~Centro Comercial North Point~g~ en ~y~Vice Point~g~. [PRO1_12:PROT1] ~g~Destroza los escaparates de todas las tiendas y los propietarios te suplicarán para obtener nueva protección. [PRO1_A:PROT1] Oh, tenemos que volver a pintar este sitio. Tenemos que hacer que parezca más viejo. [PRO1_C:PROT1] Rosenberg, eres mi abogado, no mi diseñador de interiores, ¿lo entiendes? [BUYP3:PROT1] Sitúate en el marcador, a continuación pulsa ~h~~k~~PED_ANSWER_PHONE~~w~ para comprar la propiedad. [PRO1_13:PROT1] ~g~Dispones de cinco minutos para machacarlos a todos. {=================================== MISSION TABLE PROT2 ===================================} [PRO2_A:PROT2] ¿Cuál es el problema? [PRO2_B:PROT2] Un bar se niega a pagar. [PRO2_C:PROT2] Creen que están protegidos por una banda de matones locales. [PRO2_D:PROT2] Pero no te preocupes, Tommy, yo me encargo. [PRO2_E:PROT2] ¿Llamas a esto encargarte? [PRO2_F:PROT2] Vosotros dos, en pie... [PRO2_G:PROT2] Vamos. [PRO2_01:PROT2] ~g~Elimina a los guardias que vigilan el Bar Front Page y descubre quién los ha puesto ahí. [PRO2_10:PROT2] ~g~Dos más han huido. Sígueles la pista y acaba con ellos. [PRO2_11:PROT2] Sube al coche, inútil. [PRO2_02:PROT2] Tu seguridad necesita un poco más de seguridad. [PRO2_03:PROT2] ¡Ahh! demonios, ¡otra vez no! ¡no necesito esta basura! [PRO2_04:PROT2] Estos idiotas pertenecen al DBP de Seguridad que hay a la vuelta de la esquina. [PRO2_05:PROT2] ¡Eh tíos! Arregladlo entre vosotros. [PRO2_06:PROT2] Nos vemos en un rato. [PRO2_07:PROT2] Sí, vale, lo que tú digas. [PRO2_08:PROT2] ~g~El DBP de Seguridad sabrá que estás de camino, ve y atrápalos antes que escapen. [PRO2_09:PROT2] ~g~Ve y habla con el dueño del bar Front Page. {=================================== MISSION TABLE PROT3 ===================================} [PRO3_A:PROT3] ¡Idiota! ¿En qué estabas pensando? [PRO3_B:PROT3] ¿Te das cuenta de lo que significa ésto? [PRO3_C:PROT3] ¡Podríamos estar todos hundidos! [PRO3_D:PROT3] El temporizador debe haberse jodido. [PRO3_E:PROT3] Ese lugar estaba preparado para estallar como una fábrica de fuegos artificiales. [PRO3_F:PROT3] Entonces alguien avisó a la poli... [PRO3_G:PROT3] ¿Cuál es el problema, colegas? [PRO3_H:PROT3] Se suponía que Mike iba a quemar algún sitio en el centro comercial, [PRO3_I:PROT3] pero la cagó con los fusibles y ahora eso está plagado de polis. [PRO3_J:PROT3] ¡Tenemos que conseguir nuestras cosas y salir de aquí! [PRO3_K:PROT3] ¡Relajaos los dos, dejadme pensar un segundo! [PRO3_L:PROT3] Tommy Vercetti no corta por lo sano y huye. [PRO3_M:PROT3] Los polis van a repasar ese edificio con un buen cepillo de dientes, ¿verdad? [PRO3_N:PROT3] Pero eso lleva tiempo. [PRO3_O:PROT3] Tenemos que ir y quemar nosotros mismos ese sitio. [PRO3_P:PROT3] Sí, pero... [PRO3_Q:PROT3] ¡Nadie excepto un poli podría acercarse a una milla de ese lugar! [PRO3_R:PROT3] Así que iremos como polis. [PRO3_S:PROT3] Tenemos que conseguir uniformes, y vamos a necesitar un coche patrulla. [PRO3_T:PROT3] Todo gracias a ti, Mike. [PRO3_U:PROT3] Lo siento. [PRO3_V:PROT3] Lo capto. [PRO3_W:PROT3] Lo que tenemos que hacer es atraer a los polis enseñándoles el dedo, [PRO3_X:PROT3] encerrarles [PRO3_Y:PROT3] y asaltarles. [PRO3_Z:PROT3] Buen plan. ¡Vamos! [PRO3_A1:PROT3] De acuerdo. [PRO3_01:PROT3] De acuerdo, Lance, ¡vamos a por los polis! [PRO3_02:PROT3] ~g~ Roba el coche de policía y coloca la bomba en el Café Tarbrush del centro comercial. [PRO3_03:PROT3] ~g~ Has dejado atrás a Lance, vuelve y recógele. [PRO3_04:PROT3] ~g~ Vamos. [PRO3_05:PROT3] ~r~¡Has matado a Lance! [PRO3_07:PROT3] ~g~ Te has descubierto. ¡Date prisa y coloca la bomba! [PRO3_08:PROT3] ~g~Vuelve a la ~h~finca de Vercetti~g~ en ~h~Starfish Island~g~. [PRO3_09:PROT3] ¡Átales y amordázales! [PRO3_10:PROT3] ¡Me queda como un guante! [PRO3_11:PROT3] Un poco ajustado en la entrepierna... [PRO3_12:PROT3] Oh, sí, sí, el mío también. El mío también. [PRO3_13:PROT3] ¡Relájate, hermano! ¡Ningún poli conduce tan mal! [PRO3_14:PROT3] Recuerda, sonríe a los otros polis. [PRO3_15:PROT3] Hola, agente. Bonita placa, bonita placa. [PRO3_16:PROT3] Muy desenvuelto, Lance. [PRO3_17:PROT3] De acuerdo, los temporizadores están colocados, 5 segundos. [PRO3_18:PROT3] ¿5 segundos? ¡Tenemos que salir de aquí pitando! [PRO3_19:PROT3] Ahora que los hemos enfadado de verdad. [PRO3_20:PROT3] ~g~ Haz que dos polis te sigan hasta el garaje. [PRO3_21:PROT3] ~g~Consigue un nivel de se busca suficiente para que los polis te sigan hasta el garaje. [PRO3_22:PROT3] ~g~¡La puerta del garaje está bloqueada! El acceso tiene que estar despejado para que se pueda cerrar. [PRO3_23:PROT3] ~g~Camina hacia el marcador para colocar la bomba. [PRO3_24:PROT3] ~g~¡Aléjate del Café! [PRO_AS1:PROT3] ANILLO DE PROTECCIÓN COMPLETADO [PRO_AS2:PROT3] ~g~La finca de Vercetti generará ahora unos ingresos máximos de hasta ~1~ $. Asegúrate de recaudarlo regularmente. {=================================== MISSION TABLE RACES ===================================} [RACES_2:RACES] ~g~¡Necesitas un vehículo para correr, esto no es una carrera a pie! [RACES_3:RACES] 3... 2... 1... ¡ADELANTE! ¡ADELANTE! ¡ADELANTE! [RACES_8:RACES] ~r~¡No has ganado la carrera! [RACES00:RACES] Carrera ~1~: [RACES01:RACES] Velocidad terminal [RACES02:RACES] Ocean Drive [RACES03:RACES] Carrera por el borde [RACES04:RACES] Capital Cruise [RACES05:RACES] ¡Tour! [RACES06:RACES] Aguante en V.C. [RACES07:RACES] Precio de entrada: ~1~ $ [RACES08:RACES] Mejor tiempo: ~1~:~1~ [RACES09:RACES] Mejor resultado: 1 [RACES10:RACES] Mejor resultado: 2 [RACES11:RACES] Mejor resultado: 3 [RACES12:RACES] Mejor resultado: 4 [RACES13:RACES] Longitud de pista: ~1~.~1~ km [RACES15:RACES] Mejor tiempo: NA [RACES16:RACES] Mejor resultado: NA [RACES19:RACES] No puedes permitirte entrar en esta carrera. [RACES22:RACES] Mejor tiempo: ~1~:0~1~ [RACES23:RACES] Longitud de la pista: ~1~.~1~ millas [RACES_1:RACES] ~g~Consigue un vehículo rápido y ve a la parrilla de salida. [RACEHLP:RACES] ~w~Pulsa ~h~~k~~PED_SPRINT~~w~ para empezar la carrera seleccionada. Pulsa ~h~~k~~VEHICLE_ENTER_EXIT~~w~ para salir. {=================================== MISSION TABLE RCHELI1 ===================================} [WRECKED:RCHELI1] ~r~¡El vehículo está destrozado! [RCH1_4:RCHELI1] Puntos de control: [RCH1_7:RCHELI1] ~g~Hay 20 puntos de control en total. [RCH1_12:RCHELI1] ~g~¡El helicóptero RC está saliendo fuera de su alcance de control! [RCH1_13:RCHELI1] ~r~¡El helicóptero RC se ha salido de su radio de alcance! [RCH1_8:RCHELI1] { reVC update } ~g~Si deseas abandonar esta misión, pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~ para detonar tu helicóptero RC. {=================================== MISSION TABLE RCPLNE1 ===================================} [RCPL1_4:RCPLNE1] ~g~Compite en una CARRERA DE PUNTOS DE CONTROL contra otros tres aviones RC. [RCPL1_5:RCPLNE1] ~g~Vuela a través de los puntos de control dispersados por Vice City. [RCPL1_6:RCPLNE1] { reVC update } ~g~Si deseas abandonar esta misión, pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~ para detonar tu avión RC. [RCPL1_8:RCPLNE1] ~g~¡Tu avión RC está saliendo fuera del alcance! [RCPL1_9:RCPLNE1] ~r~¡Tu avión RC se salió del alcance! {=================================== MISSION TABLE RCRACE1 ===================================} [RCRC1_1:RCRACE1] ~g~Compite en una CARRERA DE PUNTOS DE CONTROL contra 3 Barón RC durante dos vueltas. [RCRC1_3:RCRACE1] ~g~¡Vuelta final! [RCR1_4:RCRACE1] vueltas restantes: [RCR1_1:RCRACE1] ~g~Compite en una carrera de puntos de control contra otros tres coches RC. [RCR1_2:RCRACE1] ~g~¡Consigue ser el primero en completar las dos vueltas del circuito para ganar! [RCR1_6:RCRACE1] ~g~¡Tu coche RC se está saliendo fuera del alcance! [RCR1_7:RCRACE1] ~r~¡Tu coche RC se salió del alcance! {=================================== MISSION TABLE ROCK1 ===================================} [RBM1_A:ROCK1] ~n~ [RBM1_B:ROCK1] ¡Brillante, jodidamente brillante! [RBM1_D:ROCK1] ¿Eh, habías conocido antes a los Love Fist? [RBM1_E:ROCK1] No, pero siempre me encantó vuestra música. [RBM1_F:ROCK1] Déjame presentarte a la banda. [RBM1_G:ROCK1] Estos son P, Percy, Dick, y Willy está en el kaze, y ese de antes de la cabina era Jezz, [RBM1_H:ROCK1] y tíos, quiero que conozcáis a un buen amigo mío. [RBM1_I:ROCK1] Este es Tommy. Nos conocemos desde hace mucho. [RBM1_J:ROCK1] De acuerdo, colega. [RBM1_K:ROCK1] Y, eh, ¿cuál era tu nombre? [RBM1_L:ROCK1] Déjalo, Jezz, lo recuerdas, [RBM1_M:ROCK1] no juegues a eso conmigo, colega, [RBM1_N:ROCK1] ¡soy demasiado astuto para eso, encanto! [RBM1_O:ROCK1] Verás, Tom, la cosa es que los chicos necesitan algo de ayuda. [RBM1_P:ROCK1] No tienen muchos contactos en la zona, gente a la que preguntarle ''¿vienes mucho por aquí?'' y todo eso. [RBM1_Q:ROCK1] ¡Necesitamos unos tiritos, colega! [RBM1_R:ROCK1] ¿Vamos a pillar la vieja furia Love Fist, sabes? [RBM1_S:ROCK1] Bueno, esto es Vice City, tío. ¿Cuál es el problema? [RBM1_U:ROCK1] ¡Love juice, tío! [RBM1_V:ROCK1] ¿Love juice? [RBM1_W:ROCK1] Sí, dos partes de boomshine, una parte de farlopa, cinco caramelos con picapica y un litro de gasolina. [RBM1_X:ROCK1] ¿Puedes ayudarnos, colega? [RBM1_Y:ROCK1] De verdad significaría mucho para los chicos. [RBM1_Z:ROCK1] Puedes hacer eso por los muchachos, ¿verdad? [RBM1_7:ROCK1] ~r~¡No conseguiste a tiempo el ''love juice''! [RBM1_8:ROCK1] ~r~¡Mercedes está muerta! [RBM1_10:ROCK1] ~r~¡Idiota! ¡Has destruido el material! [RBM1_13:ROCK1] ~g~Lleva el ''love juice'' y a Mercedes a la banda antes de que sean requeridos en el escenario. [RBM1_15:ROCK1] ~r~¡Has perdido al camello, nuestra pasta y la nieve! [RBM1_17:ROCK1] ~g~¡Mata al camello y consigue los ingredientes! [MOB_07A:ROCK1] Eh colega, a los chicos les vendría bien algo de compañía, si entiendes lo que quiero decir... [MOB_07B:ROCK1] Conozco a la chica apropiada. [ROK1_5:ROCK1] ¡Hola, Mercedes! [ROK1_6:ROCK1] Hola, Tommy. ¿Cómo estás? [ROK1_7:ROCK1] Bien. Escucha, ¿te apetece acostarte con los Love Fist? [ROK1_8:ROCK1] De acuerdo, pero sólo como un favor que espero que me sea devuelto... [RBM1_14:ROCK1] ~g~¡Necesitas un coche o una moto! [RBM1_1:ROCK1] ~g~Ve y recoge a Mercedes en su apartamento. [RBM1_12:ROCK1] ~g~Ve y reúnete con el camello para conseguir los ingredientes del ''love juice''. [ROK1_2:ROCK1] YA NO ES NECESARIO [ROK1_3:ROCK1] YA NO ES NECESARIO [MERC_39:ROCK1] Hasta luego grandullón. [RBM1_C:ROCK1] ¡Eh, Tommy! Me alegro que pudieras venir. [RBM1_9:ROCK1] ~g~¡Ve y reúnete con el camello de los Love Fist para conseguir algo de ''love juice''! [ROK1_1A:ROCK1] ¿Buscas algo especial? ¡Yo tengo lo que necesitas! [ROK1_9:ROCK1] ¡Gracias por el dinero, mamón! [RBM1_T:ROCK1] Necesitamos ''love juice'', tío, ¿sabes? {=================================== MISSION TABLE ROCK2 ===================================} [RBM2_A:ROCK2] Tommy, tío. ¡Me alegro de verte! [RBM2_B:ROCK2] ¿Qué sucede? [RBM2_C:ROCK2] Malas vibraciones, Tommy... [RBM2_E:ROCK2] Hay un tipo, apenas le conocemos, pero él nos conoce a nosotros. [RBM2_F:ROCK2] Como este tipo, que lo sabe todo de nosotros. [RBM2_G:ROCK2] Sabe que a Willy le gusta ponerse ropa interior de mujeres. [RBM2_H:ROCK2] ¡O que a Percy le gusta Duran Duran! [RBM2_K:ROCK2] Sí, bien, tu cohete del amor. Pero escucha, este tipo... [RBM2_L:ROCK2] El tipo, quiere ver a los Love Fist muertos. [RBM2_M:ROCK2] Muertos, Tommy. [RBM2_N:ROCK2] El final Love Fist. Ya sabes lo que dicen, los buenos mueren jóvenes. [RBM2_O:ROCK2] ¡Pero Tommy, tienes que salvar a los Love Fist! [RBM2_P:ROCK2] Tenemos que firmar en dos horas y creo... [RBM2_Q:ROCK2] Los chicos creen que el sospechoso va a intentar hacer algún recibimiento infame. [RBM2_1:ROCK2] ~g~Conduce la limusina a la firma de autógrafos e intenta encontrar al psicópata. [RBM2_2:ROCK2] ~r~¡Destrozaste el coche de la banda! [RBM2_3:ROCK2] ~g~¡Ve a la firma de autógrafos! [RBM2_4:ROCK2] ~g~¡Atrapa al psicópata! ¡No le dejes escapar! [RBM2_5:ROCK2] ~r~¡Lo perdiste, idiota! [RBM2_7:ROCK2] ~r~¡Los fans han sido atacados, el psicópata no aparecerá! [RBM2_8:ROCK2] ~r~¡Los guardas de seguridad han sido atacados, el psicópata no aparecerá! [PSYCH_1:ROCK2] ¡Me encantaría ver arder a los Love Fist! [PSYCH_2:ROCK2] ¡Los Love Fist arruinaron mi vida! [RBM2_I:ROCK2] Cállate, idiota. Sólo porque Jezz se folla ovejas. [RBM2_R:ROCK2] ¡Eh, cierra la boca! [RBM2_D:ROCK2] Sí, no estoy de broma, es un tema muy gordo tío, muy gordo, ¿sabes? [RBM2_J:ROCK2] Es lo que atrae a mi cohete del amor, ¿entiendes? {=================================== MISSION TABLE ROCK3 ===================================} [RBM3_A:ROCK3] ¡Tommy! ¡Tommy! ¡Tommy, tío, ese psicópata ha vuelto! [RBM3_B:ROCK3] ¿Qué está pasando? [RBM3_C:ROCK3] ¡Ese psicópata no dejará en paz a los Love Fist! [RBM3_D:ROCK3] No le mataste, tío. Y ahora ha vuelto. [RBM3_E:ROCK3] Sí, sí, sí, y la cuestión es... [RBM3_F:ROCK3] La cuestión es que necesitamos alguien en quien poder confiar para que conduzca la limo, [RBM3_G:ROCK3] ¡porque ese chiflado sigue con las amenazas! [RBM3_I:ROCK3] Aquí estamos todos acojonados, tío. [RBM3_J:ROCK3] De acuerdo tíos, calmaos, yo me encargaré de esto. [RBM3_K:ROCK3] Normalmente, no me ocuparía de ir por ahí haciendo de chófer para una panda de bisexuales escoceses bebidos, [RBM3_L:ROCK3] pero, en vuestro caso, haré una excepción. [RBM3_4:ROCK3] ~r~¡Has matado a los Love Fist! [RBM3_6:ROCK3] DETONACIÓN: [RBM3_1:ROCK3] ~g~Conduce a los Love Fist al local del concierto. [RBM3_2:ROCK3] Si intentas abandonar el coche mientras la bomba está armada explotará... [RBM3_3:ROCK3] Si la barra de detonación se llena completamente la bomba explotará. [RBM3_8:ROCK3] Cuanto más rápido conduzcas más despacio se moverá la barra de detonación. [RBM3_7:ROCK3] ~g~¡BOMBA DESACTIVADA! [ROK3_62:ROCK3] y pensamos que podríamos enseñarte nuestro Templo del Rock. [ROK3_63:ROCK3] ¡Para que te vayas acostumbrando a la furia de Love Fist! [ROK3_64:ROCK3] Escúchate a ti mismo, tío. Es papel maché y cinta aislante. [ROK3_65:ROCK3] Eh, para los chavales, ¡esto es un templo y nosotros somos los sacerdotes! [ROK3_66:ROCK3] Si, vale, si a los chavales les gustan los sacerdotes colgados y sin oído musical, [ROK3_67:ROCK3] ¿quién soy yo para discutirlo? [ROK3_68:ROCK3] Vaya por Dios, se está comiendo la cinta otra vez. [ROK3_69:ROCK3] Con este ritmo tendremos que tocar en directo. [ROK3_70:ROCK3] Oohh ¡mierda! Mis tripas... [ROK3_73:ROCK3] Jezz está escuchando la cinta, [RBM3_9:ROCK3] Si te paran o reduces la velocidad la barra de detonación aumentará. [RBM3_H:ROCK3] ¡Me estoy cagando de miedo tío. Necesito a mi, mamá! [ROK3_71:ROCK3] Tenemos que seguir con esto. Gracias de nuevo Tommy, ya sabes lo que quiero decir, apúntate una, ¡adiós! [ROK3_1:ROCK3] Al fin tío, hora de un merecido trago. La sala de conciertos está solo a unos cientos de metros calle abajo. [ROK3_2:ROCK3] Mejor ponme uno doble. Eh Tommy, cambia los temas, tío. [ROK3_3:ROCK3] Me siento perdido si la cabeza no me estalla. Ah, mira, ¿Qué es esto? Eh Tommy, pon esta cinta. [ROK3_4:ROCK3] Love Fist. Vuestro tiempo contaminando las ondas se ha terminado. Os di la oportunidad de ser amigos. [ROK3_5:ROCK3] Ahora os doy la oportunidad de morir. ¡Intentad aminorar la marcha y vuestra limusina explotará junto con vuestros GRANDES Y PELUDOS CULOS! [ROK3_6:ROCK3] Tommy amigo, ¡tienes que salvar a la banda! Yo me estoy cansando de esto. ¡Mantén pisado el acelerador! [ROK3_7:ROCK3] ¡Tenemos que encontrar la bomba! ¿Podremos conducir por ahí todo el día? Sí, tenemos suficiente bebida.. [ROK3_8:ROCK3] ¿La bomba no estará en el motor? Tendremos que parar y cogerla. ¡Vamos a morir todos! ¡Me voy a emborrachar! [ROK3_9:ROCK3] Eh, ¡amigo, aquí hay cola! ¡La respuesta no está en el mueble bar! ¡Sal de mi camino! [ROK3_10:ROCK3] Eh, ¡a la botella de vodka le salen unos cables! ¡Eso no es vodka!, ¡Eso es una BOMBA! [ROK3_11:ROCK3] ¡Y está preparada para explotar! [ROK3_12:ROCK3] Siempre me dijeron que la bebida acabaría conmigo. He visto esto en la tele. Tienes que tirar de uno de los cables. ¿De qué cable? Pues no sé tío. [ROK3_13:ROCK3] No tengo ni idea. Willy, di algo. Voy a tocar el bajo en el otro mundo. [ROK3_14:ROCK3] Tommy tío, sigue conduciendo deprisa, amigo. Que alguien haga algo. Si, ¡qué listo! [ROK3_15:ROCK3] ''Que alguien haga algo'', qué mierda es esa, he visto a niñas con más valor. Tipo duro, haz tú algo. [ROK3_16:ROCK3] Mira tío, yo toco un instrumento y no tengo ni idea de desactivar bombas. Willy podrías beberte el líquido de la bomba con una pajita. [ROK3_17:ROCK3] Sí, he oído que eres bueno en ese tipo de cosas. ¡Eh, aquella noche estaba muy colgado, como bien sabes! [ROK3_18:ROCK3] ¡Anda pásale una pajita a Willy! ¿Una pajita? ¡Este es el autobús de la gira de los Love Fist! [ROK3_19:ROCK3] ¿De dónde voy a sacar yo una pajita? ¿me entiendes? ¿Qué cable Tommy? El verde. No hay ninguno verde. [ROK3_20:ROCK3] ¿O es que este es verde? ¿Te parece verde alguno de estos cables? [ROK3_21:ROCK3] ¡Oh no! ¡La muerte está en las cartas! ¡Todo parece verde! Tenía que haberos dejado tirados cuando tuve la oportunidad. [ROK3_22:ROCK3] Buscador de gloria. Capitalista. He estado tirando de ti durante años. Cállate. Eres un espantajo. [ROK3_23:ROCK3] Una nena grande y chillona. Sí. Cállate y tira del cable. ¿Qué cable? Este... [ROK3_24:ROCK3] ¡NO, tío! estamos bien. ¡No hemos saltado por los aires, amigo. [ROK3_25:ROCK3] Tommy, tío, chachi. Rock and roll, tío. ¿No tenemos que actuar? [ROK3_26:ROCK3] ¿Meter bulla? ¿Abusar de las fans? ¡LOVE FIST! [ROK3_27:ROCK3] ¿Has terminado con esa botella? {=================================== MISSION TABLE SERG1 ===================================} [TEX1_A:SERG1] Entra y disfruta de los asientos de cuero, hijo. [TEX1_B:SERG1] Demonios, mi padre solía decir, a caballo regalado no le mires el diente, y por Dios que nunca lo hizo. [TEX1_C:SERG1] ¿Te gustaría un trago del viejo Kentucky? [TEX1_D:SERG1] No gracias. [TEX1_E:SERG1] Alguien con la mente despejada, me gusta eso. [TEX1_F:SERG1] El negocio de la propiedad no se trata solo de papeleo pretencioso. [TEX1_G:SERG1] ¡Se trata de tierra! ¡Y el derecho a reclamarla! ¿Me sigues, hijo? [TEX1_H:SERG1] Sí. [TEX1_J:SERG1] Y yo creo que eres la clase de tío para persuadirle. [TEX1_K:SERG1] La persuasión es mi fuerte. [TEX1_L:SERG1] Sí, estará en el club de campo, en el campo de golf. [TEX1_M:SERG1] Allí no permiten armas, así que sus guardaespaldas no llevarán protección. [TEX1_N:SERG1] Ve y dale una paliza inolvidable. [TEX1_O:SERG1] Bien, toma... te he conseguido un carné de socio, y chico, vas a necesitar un tipo de ropa más apropiada. [TEX1_2:SERG1] ~g~Ahora ve al Club de golf Leaf Links. [TEX1_0:SERG1] ~g~El objetivo está en el campo de prácticas disfrutando de una partida de golf. ¡Asegúrate de que sea la última! [TEX1_3:SERG1] ¿Quién es este tipo? Chicos, ocuparos de él. [TEX1_6:SERG1] ¡Bonito culo nena! [TEX1_7:SERG1] ¿Este soy yo? [TEX1_I:SERG1] Bueno, necesito algún cabrón obstinado que suelte algo de mierda. [TEX1_8:SERG1] Cada vez que subas a un Caddy recibirás automáticamente un palo de golf, siempre que la ranura de armas blancas esté vacía. [TEX1_9:SERG1] ¡Atrapadle! [TEX1_10:SERG1] ¡Mata a ese psicópata! [TEX1_1:SERG1] ~g~Ve y consigue ropa de golf en Jocksports. {=================================== MISSION TABLE SERG2 ===================================} [TEX_2A:SERG2] ~g~¡Excelente! ¡Te han visto! [TEX_2B:SERG2] ~r~¡Idiota! ¡La gente debe PRESENCIAR a un cubano haciendo el trabajito! [TEX_2C:SERG2] ~g~¡Ve y consígue algo con los colores de la banda cubana en Little Havana! [TEX_2D:SERG2] ~g~¡Ahora elimina al capo de la banda haitiana en la Funeraria de Romero! [TEX2_A:SERG2] Tommy, este es Donald Love. Donald, te presento a Tommy Vercetti, [TEX2_B:SERG2] el último tirador más rápido en llegar a estos lares. [TEX2_C:SERG2] Si... eh... [TEX2_D:SERG2] Donald, ahora cállate y escucha, puede que aprendas algo. [TEX2_E:SERG2] Veamos. Nada hace que los precios de las propiedades bajen más rápido que una guerra entre bandas al viejo estilo. [TEX2_F:SERG2] Excepto un desastre quizás, como la peste de la que habla la biblia o algo así, [TEX2_G:SERG2] aunque puede que en este caso vayamos demasiado lejos. [TEX2_H:SERG2] ¿Te estás enterando, capullo cuatro ojos? [TEX2_I:SERG2] Hace poco murió un capo de una banda haitiana. Al parecer lo hicieron los cubanos, nadie lo sabe. [TEX2_J:SERG2] ¡Pero se lo haremos saber! Disfrázate de cubano [TEX2_K:SERG2] y ve a reventar el funeral. Crea confusión y después le sigues bien de cerca. [TEX2_L:SERG2] ¿Te estás enterando, Donald? [TEX2_M:SERG2] Eso debería meter al zorro en el gallinero. ¿Eh? [TEX2_N:SERG2] y ahora sólo tenemos que sentarnos a esperar y ver cómo caen los precios. [TEXEXIT:SERG2] ~g~¡Lárgate de Little Haiti ahora! {=================================== MISSION TABLE SERG3 ===================================} [TEX3_A:SERG3] Bien, mira, hijo. Tengo un problema y creo que podrías ayudarme con él. [TEX3_B:SERG3] No soy constructor. [TEX3_C:SERG3] No, estaba pensando más bien en tus habilidades para la demolición. [TEX3_D:SERG3] Bien, esto de aquí es el centro empresarial tal y como se ha planeado, y éste [TEX3_E:SERG3] es el solar que estamos buscando. [TEX3_F:SERG3] Intentas decir que este nuevo bloque de oficinas está en medio. [TEX3_G:SERG3] Lo captas rápido. [TEX3_H:SERG3] Bien, voy a irme de esta ciudad durante un tiempo [TEX3_I:SERG3] y si ese centro de oficinas se enfrentase a problemas estructurales repentinos e insalvables, entonces yo... [TEX3_J:SERG3] ¿Como buen ciudadano se sentirá obligado a intervenir y [TEX3_K:SERG3] salvar la remodelación de una zona tan importante de la ciudad? [TEX3_L:SERG3] ¿Dónde puedo conseguir más tipos como tú? [TEX3_1:SERG3] ~g~Utiliza el helicóptero RC para transportar las bombas a los cuatro objetivos del edificio que debes demoler. [TEX3_2:SERG3] ~g~Debes colocar una bomba en cada objetivo. Puedes colocarlas en cualquier orden. [TEX3_3:SERG3] ~g~Para recoger una bomba, dirige el helicóptero RC hasta a ella. Solo puedes llevar las bombas de una en una. [TEX3_4:SERG3] { reVC update } ~g~Para soltar una bomba pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~. [TEX3_5:SERG3] ~g~Si fallas al colocar una bomba podrás recogerla e intentarlo de nuevo. [TEX3_6:SERG3] ~g~Cuando hayas recogido una bomba por primera vez, el temporizador de detonación se pondrá en marcha. [TEX3_7:SERG3] ~g~¡A continuación deberás conseguir colocar el resto de las bombas en 7 minutos! [TEX3_8:SERG3] ~g~¡Has fallado en el objetivo! ¡Recoge la bomba e inténtalo de nuevo! [TEX3_10:SERG3] ~g~Suelta la bomba en el objetivo. [TEX3_11:SERG3] objetivos restantes: [TEX3_17:SERG3] ~r~¡Te has quedado sin tiempo! [TEX3_18:SERG3] ~r~¡Tu helicóptero RC ha sido destruido! [TEX3_19:SERG3] ~r~¡Has lanzado la bomba en el agua! ¡Esa no es manera de pescar! [TEX3_20:SERG3] ~g~¡Tu Helicóptero RC está casi fuera del alcance del control remoto! [TEX3_21:SERG3] ~r~¡Tu Helicóptero RC está fuera del alcance del control remoto! [TEX3_24:SERG3] Pulsa ~h~~k~~VEHICLE_LOOKLEFT~ ~w~para girar el helicóptero en sentido contrario a las agujas del reloj. [TEX3_25:SERG3] Pulsa ~h~~k~~VEHICLE_LOOKLEFT~ ~w~para girar el helicóptero en sentido de las agujas del reloj. [TEX3_27:SERG3] ~g~Una escalera central permite el acceso a todas las plantas del edificio. [TEX3_31:SERG3] ~r~¡Has destruido la furgoneta de TOPFUN que tenía las bombas y el helicóptero RC! [TEX3_32:SERG3] Puedes ~h~mirar atrás~w~ si ~h~pulsas simultáneamente ~k~~VEHICLE_LOOKLEFT~ y ~k~~VEHICLE_LOOKRIGHT~~w~. [TEX3_29:SERG3] { reVC update } ~g~Para soltar una bomba pulsa ~h~~k~~VEHICLE_FIREWEAPON~~g~. [TEX3_26:SERG3] Pulsa ~h~~k~~VEHICLE_BRAKE~~w~ para disminuir la velocidad del rotor y ~h~descender~w~. [TEX3_22:SERG3] Pulsa ~h~~k~~VEHICLE_ACCELERATE~~w~ para aumentar la velocidad del rotor y así ~h~ascender~w~. [TEX3_16:SERG3] ~g~Ve a la furgoneta ~w~TOPFUN ~g~cercana al solar de construcción que se va a demoler. [TEX3_33:SERG3] Una vez que recojas una bomba el radar te mostrará la posición del objetivo con relación al helicóptero RC. [TEX3_34:SERG3] Un ~h~icono triangular apuntando hacia arriba ~w~indica que el objetivo está ~h~por encima ~w~del helicóptero RC. [TEX3_35:SERG3] Un ~h~icono triangular apuntando hacia abajo ~w~indica que el objetivo está ~h~por debajo ~w~del helicóptero RC. [TEX3_36:SERG3] Un ~h~icono cuadrado ~w~indica que el objetivo está al ~h~mismo nivel ~w~que el helicóptero RC. [TEX3_28:SERG3] Para ~h~recoger una bomba~w~, simplemente dirige el helicóptero RC hasta ella. El helicóptero RC sólo puede transportar una bomba. [TEX3_30:SERG3] ~g~Para recoger una bomba, simplemente dirige el helicóptero RC hasta ella. El helicóptero RC sólo puede transportar una bomba. [TEX3_12:SERG3] ~g~¡Bomba colocada! ¡Quedan 3 objetivos más! Vuelve a por otra bomba. [TEX3_13:SERG3] ~g~¡Bomba colocada! ¡Quedan 2 objetivos más! Vuelve a por otra bomba. [TEX3_14:SERG3] ~g~¡Bomba colocada! ¡Queda 1 objetivo más! Vuelve a por otra bomba. [TEX3_15:SERG3] ~r~¡El temporizador de detonación se ha puesto en marcha! ~g~Debes colocar las ~w~4 bombas ~g~en el tiempo restante! [TEX3_37:SERG3] Si empujas ~h~el joystick analógico derecho hacia atrás~w~, aumentarás la velocidad del rotor y así ~h~ascenderá~w~. [TEX3_38:SERG3] { reVC update } {Pulsa ~h~~k~~VEHICLE_ACCELERATE~~w~ para aumentar la velocidad del rotor y así ~h~ascender~w~.} Si empujas ~h~el joystick analógico derecho hacia adelante ~w~la velocidad del rotor aumenta, haciendo que el helicóptero ~h~descienda. [TEX3_39:SERG3] ~g~Para soltar una bomba pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~. [TEX3_40:SERG3] ~g~Para soltar una bomba pulsa ~h~~k~~VEHICLE_HANDBRAKE~~w~. [TEX3_23:SERG3] Pulsa ~h~~k~~VEHICLE_TURRETUP~~w~ y ~h~~k~~VEHICLE_TURRETDOWN~~w~ para inclinar el helicóptero en la dirección deseada. {=================================== MISSION TABLE TAXI1 ===================================} [FARES:TAXI1] CARRERAS: [TAXI1:TAXI1] ~g~Busca un cliente. [TSCORE2:TAXI1] ~1~ $ [IN_ROW:TAXI1] ~1~ ¡Racha de clientes! ~1~ $ [TAXI3:TAXI1] ~r~¡Tu pasajero huye aterrorizado! [TAXI7:TAXI1] ~r~Tu coche está destrozado, haz que lo reparen. [TAXI4:TAXI1] ¡Viaje completado! [TAXI5:TAXI1] ¡VIAJE RÁPIDO! [TAXI6:TAXI1] Misión de taxista cancelada [TAXIH1:TAXI1] Detente cerca de un peatón que esté resaltado para recogerle y llevarle a su destino antes de que se agote el tiempo. [FARE1:TAXI1] ~g~Destino ~w~''el club Pole Position'' ~g~en Ocean Beach. [FARE3:TAXI1] ~g~Destino ~w~''el club náutico'' ~g~en Ocean Beach. [FARE4:TAXI1] ~g~Destino ~w~''Ammu-Nation'' ~g~en Ocean Beach. [FARE5:TAXI1] ~g~Destino ~w~''la ferretería'' ~g~en Washington Beach. [FARE6:TAXI1] ~g~Destino ~w~''el centro comercial North Point'' ~g~en Vice Point. [MFARE1:TAXI1] ~g~Destino ~w~''Ammu-Nation'' ~g~en el centro. [MFARE2:TAXI1] ~g~Destino ~w~''la terminal'' ~g~en el aeropuerto Escobar International. [WFARE3:TAXI1] ~g~Destino ~w~''Coches Sunshine'' ~g~en Little Havana. [WFARE4:TAXI1] ~g~Destino ~w~''Taxis Kaufman'' ~g~en Little Haiti. [WFARE5:TAXI1] ~g~Destino ~w~''la ferretería'' ~g~en Little Havana. [WFARE6:TAXI1] ~g~Destino ~w~''Emporio Howlin Petes Bike'' ~g~en el centro. [FARE7:TAXI1] ~g~Destino ~w~''la joyería'' ~g~en Vice Point. [FARE8:TAXI1] ~g~Destino ~w~''la playa'' ~g~en Ocean Beach. [FARE9:TAXI1] ~g~Destino ~w~''la playa'' ~g~en Washington Beach. [FARE10:TAXI1] ~g~Destino ~w~''la playa'' ~g~en Vice Point. [FARE11:TAXI1] ~g~Destino ~w~''el hospital'' ~g~en Ocean Beach. [FARE12:TAXI1] ~g~Destino ~w~''el hospital'' ~g~en Vice Point. [FARE13:TAXI1] ~g~Destino ~w~''la comisaría de policía'' ~g~en Washington Beach. [FARE14:TAXI1] ~g~Destino ~w~''la comisaría de policía'' ~g~en Vice Point. [FARE15:TAXI1] ~g~Destino ~w~''la pizzería'' ~g~en Vice Point. [WFARE7:TAXI1] ~g~Destino ~w~''la comisaría de policía'' ~g~en Little Havana. [WFARE8:TAXI1] ~g~Destino ~w~''la comisaría de policía'' ~g~en el centro de la ciudad. [WFARE9:TAXI1] ~g~Destino ~w~''el hospital'' ~g~en el centro de la ciudad. [WFARE10:TAXI1] ~g~Destino ~w~''el hospital'' ~g~en Little Havana. [WFARE11:TAXI1] ~g~Destino ~w~''el estadio'' ~g~en el centro de la ciudad. [WFARE12:TAXI1] ~g~Destino ~w~''la pizzería'' ~g~en Little Haiti. [WFARE13:TAXI1] ~g~Destino ~w~''la pizzería'' ~g~en el centro de la ciudad. [WFARE14:TAXI1] ~g~Destino ~w~''los muelles'' ~g~en Viceport. [WFARE15:TAXI1] ~g~Destino ~w~''la farmacia'' ~g~en Little Haiti. [FARE2:TAXI1] ~g~Destino ~w~''el club Malibú'' ~g~en Vice Point. {=================================== MISSION TABLE TAXICUT ===================================} [TAXC_A:TAXICUT] Supongo que eres el nuevo propietario. [TAXC_B:TAXICUT] ¿Qué eres, mafioso? ¿Del cártel? No pareces mejicano... [TAXC_C:TAXICUT] En fin, será mejor que sueltes el rollo de ''las cosas van a cambiar por aquí'', [TAXC_D:TAXICUT] quizás amenazar a alguno de los conductores... [TAXC_E:TAXICUT] ve con calma con Ted, el de ahí, acaban de curarle la hernia. [TAXC_F:TAXICUT] Bueno, sí. Las cosas van a cambiar por aquí, señora. [TAXC_G:TAXICUT] Oh, mierda, hijo. Anda dejarme esto a mí... [TAXC_H:TAXICUT] Llevo haciéndolo años. [TAXC_I:TAXICUT] Ahora oíd esto. [TAXC_J:TAXICUT] Ahora tenemos una nueva dirección y las cosas van a cambiar por aquí. [TAXC_K:TAXICUT] Nuestra nueva dirección, los... [TAXC_L:TAXICUT] ¿De qué banda eres? [TAXC_M:TAXICUT] Bueno, en realidad no formo parte de ninguna banda. [TAXC_N:TAXICUT] ¿Cómo coño te llamas, muchacho? [TAXC_O:TAXICUT] Vercetti, Tommy Vercetti. [TAXC_P:TAXICUT] Nuestra nueva administración, la banda de Vercetti, [TAXC_Q:TAXICUT] se va a encargar de que no tengamos problemas. [TAXC_R:TAXICUT] ¿Capiche? ¡Corto! [TAXC_S:TAXICUT] ¿Te gustó el ''capiche''? A mí me gustó el ''capiche''. [TAXC_T:TAXICUT] Así que así es como funcionaban las cosas en el pasado, [TAXC_U:TAXICUT] Dirigimos la compañía como siempre. [TAXC_V:TAXICUT] Si tenemos algún problema con las compañías rivales, tú les das una somanta de palos. [TAXC_W:TAXICUT] Luego ellos nos la dan a nosotros, [TAXC_X:TAXICUT] y después, se la vuelves a dar a ellos, [TAXC_Y:TAXICUT] etcétera, etcétera. ¿Lo captas? [TAXC_Z:TAXICUT] Sí, supongo... [TAXC_A1:TAXICUT] Simplemente toma un taxi del garaje si tienes ganas de subir a bordo. {=================================== MISSION TABLE TAXIWA1 ===================================} [OUTTIME:TAXIWA1] ~r~¡Demasiado lento, tío, demasiado lento! [TAX1_1:TAXIWA1] Tenemos un cliente importante que necesita que lo recojan en Starfish Island. ¿Alguien me recibe? [TAX1_2:TAXIWA1] ¡Aquí Tommy, yo lo recogeré! [TAX1_3:TAXIWA1] ¡Este es mi cliente, lárgate, gilipollas! [TAX1_4:TAXIWA1] Venga, venga, entre, ¡rápido! [TAX1_5:TAXIWA1] ¡Vale, vale! ¡No me haga daño! [TAXW1_1:TAXIWA1] ~g~Recoge al V.I.P. en Starfish Island. [TAXW1_2:TAXIWA1] ~g~¡Recupera al V.I.P! ¡Destroza el otro coche! [TAXW1_3:TAXIWA1] ~r~¡El V.I.P. está muerto! [TAXW1_4:TAXIWA1] ~r~¡El V.I.P. se ha apeado! [TAXW1_6:TAXIWA1] ~g~¡Lleva al V.I.P. al aeropuerto! {=================================== MISSION TABLE TAXIWA2 ===================================} [TAX2_1:TAXIWA2] Llamando a todos los taxis, estamos perdiendo clientes por toda la ciudad. ¿Qué os pasa, tíos? [TAX2_2:TAXIWA2] Taxis VC sigue llevándonos la delantera. ¡Tienen tantos coches que no podemos competir! [TAX2_3:TAXIWA2] Sr. Vercetti, si está ahí escuchando, ¡tiene que poner fuera de circulación unos cuantos Taxis VC antes de que nos hundan! [TAXW2_1:TAXIWA2] ~g~¡Destruye 3 de los taxis rivales! {=================================== MISSION TABLE TAXIWA3 ===================================} [TAX3_1:TAXIWA3] Coche 13, tengo a una tal Señorita Cortez, preguntó especialmente por usted. [TAX3_2:TAXIWA3] Vale, entendido. ¡Coche 13 en ruta! [TAX3_3:TAXIWA3] No hay rastro de Mercedes... [TAXW3_3:TAXIWA3] ~g~Destrulle al taxi principal! [TAXW3_2:TAXIWA3] ~g~Permanece con vida hasta que se agote el temporizador. [TAX_AS1:TAXIWA3] COMPAÑÍA DE TAXIS CONSOLIDADA [TAX_AS2:TAXIWA3] ~g~Los taxis Kaufman generarán ahora unos ingresos de hasta ~1~ $ máximo. Asegúrate de recaudarlos regularmente. [TAX3_4:TAXIWA3] ¡Es hora que el ángel guardián de Taxis Kaufman coma algo del parachoques! [TAX3_5:TAXIWA3] ¡Eh chico te voy a zurrar la badana! { reVC updates } { new languages } [FEL_JAP] JAPONÉS [FEL_POL] POLACO [FEL_RUS] RUSO { new display menus } [FET_GFX] CONFIGURACIÓN DE GRÁFICOS [FED_MIP] MIPMAPPING [FED_AAS] SUAVIZADO DE BORDES [FED_FIL] FILTRO DE TEXTURAS [FED_BIL] BILINEAL [FED_TRL] TRILINEAL [FED_WND] VENTANA [FED_FLS] PANTALLA COMPLETA [FEM_CSB] BORDES EN CINEMÁTICAS [FEM_SCF] FORMATO DE IMAGEN [FEM_ISL] USO DE MEMORIA [FEM_LOW] BAJO [FEM_MED] MEDIO [FEM_HIG] ALTO [FEM_2PR] ALPHA TEST TIPO PS2 [FEC_FRC] CÁMARA LIBRE { Linux joy detection } [FEC_JOD] DETECTAR JOYSTICK [FEC_JPR] Pulsa cualquier botón del joystick que quieras usar con el juego para seleccionarlo. [FEC_JDE] Joystick detectado { mission restart } [FET_RMS] REPETIR MISIÓN [FESZ_RM] ¿REINTENTAR? [FED_VPL] FLUJO DE VEHÍCULOS [FED_PRM] LUCES EN PEATONES [FED_RGL] BRILLO DE CARRETERAS [FED_CLF] FILTRO DE COLOR [FED_WLM] MAPAS DE LUZ DEL MUNDO [FED_MBL] DESENFOQ. MOVIMIENTO [FEM_SIM] SIMPLE [FEM_NRM] NORMAL [FEM_MOB] MÓVIL [FED_MFX] MATFX [FED_NEO] NEO [FEM_PS2] PS2 [FEM_XBX] XBOX [FEC_IVP] INVERTIR VERTICALIDAD MANDO [FEM_NON] NADA [FEC_DS2] DUALSHOCK 2 [FEC_DS3] DUALSHOCK 3 [FEC_DS4] DUALSHOCK 4 [FEC_360] MANDO DE XBOX 360 [FEC_ONE] MANDO DE XBOX ONE [FEC_TYP] TIPO DE MANDO [FET_AGS] AJUSTES DE MANDO [FEM_AUT] { aspect ratio related } AUTO { end of file } [DUMMY] THIS LABEL NEEDS TO BE HERE !!! AS THE LAST LABEL DOES NOT GET COMPILED ================================================ FILE: vendor/libsndfile/ChangeLog ================================================ 2013-04-05 Erik de Castro Lopo * Makefile.am Make sure checkprograms are built as part of 'make test-tarball'. Closes: https://github.com/erikd/libsndfile/issues/37 2013-03-29 Erik de Castro Lopo * tests/dft_cmp.c Fix a buffer overflow detected using GCC 4.8's -fsantiize=address runtime error checking functionality. This was a buffer overflow in libsndfile's test suite, not in the actual library code. 2013-03-09 Erik de Castro Lopo * M4/gcc_version.m4 Fix to work with OpenBSD's sed. 2013-03-07 Erik de Castro Lopo * src/ALAC/alac_encoder.c Patch from Michael Pruett (author of libaudiofile) to add correct byte swapping for the mChannelLayoutTag field. 2013-03-02 Erik de Castro Lopo * doc/bugs.html Bugs should bt reported on the github issue tracker. 2013-02-22 Erik de Castro Lopo * configure.ac Improve sanitization of FLAC_CFLAGS value. 2013-02-21 Erik de Castro Lopo * src/Makefile.am Call python interpreter instead of using '#!' in script. Thanks to Jan Stary for reporting this. * doc/index.html doc/FAQ.html Make internal links relative. Patch from Jan Stary. 2013-02-13 Erik de Castro Lopo * src/test_endswap.def src/test_endswap.tpl Add tests for psf_put_be32() and psf_put_be64(). * src/sfendian.h src/test_endswap.(def|tpl) Add functions psf_get_be(16|32|64) with tests. These are needed for platforms where un-aligned accesses cause bus faults. * src/ALAC/ag_enc.c src/ALAC/alac_decoder.c Replace all un-aligned accesses with safe alternatives. Closes: https://github.com/erikd/libsndfile/issues/19 2013-02-12 Erik de Castro Lopo * src/sfendian.h Add big endian versions of H2BE_16 and H2BE_32. 2013-02-11 Erik de Castro Lopo * src/ALAC/ Replace Apple endswap routines with ones from libsndfile. * merge from libsndfile-cart repo Add ability to set and get a cart chunk with WAV and RF64. Orignal patch by Chris Roberts required a number of tweaks. 2013-02-10 Erik de Castro Lopo * src/common.h Bump SF_HEADER_LEN from 8192 to 12292, the value it was in the 1.0.25 release. 2013-02-09 Erik de Castro Lopo * src/alac.c Fix segfault when encoding 8 channel files. Closes: https://github.com/erikd/libsndfile/issues/30 2013-02-07 Erik de Castro Lopo * src/ALAC/EndianPortable.c Fall back to compiler's __BYTE_ORDER__ for endian-ness detection. 2013-02-06 Erik de Castro Lopo * configure.ac src/common.h src/ima_adpcm.c src/ms_adpcm.c src/paf.c Drop tests for and #ifdef hackery for C99 struct flexible array feature. libsndfile assumes the compiler supports most of the ISO C99 standard. * src/alac.c Fix valgrind invalid realloc. Reported by nu774. Closes: https://github.com/erikd/libsndfile/issues/31 2013-02-05 Erik de Castro Lopo * src/alac.c The 'pakt' chunk header should now be written correctly. Closes: https://github.com/erikd/libsndfile/issues/24 * configure.ac Makefile.am Use PKG_INSTALLDIR when it exists. Suggestion from Christoph Thompson. Closes: https://github.com/erikd/libsndfile/pull/28 2013-02-03 Erik de Castro Lopo * src/common.h src/caf.c Read the ALAC 'pakt' header and stash the values. * src/sfendian.h Add functions psf_put_be64() and psf_put_be32(). * src/alac.c Start work on filling on the 'pakt' chunk header. 2013-02-02 Erik de Castro Lopo * doc/FAQ.html Add missing opening

tag. * src/alac.c Increase ALAC_BYTE_BUFFER_SIZE to 82000. 2013-01-27 Erik de Castro Lopo * doc/FAQ.html Improve question #8. 2013-01-02 Erik de Castro Lopo * src/ogg_opus.c Add skeleton implementation so someone else can run with it. 2012-12-12 Erik de Castro Lopo * src/common.h src/dwd.c src/rx2.c src/txw.c Fix for compiling when configured with --enable-experimental. Thanks to Eric Wong for reporting this. 2012-12-01 Erik de Castro Lopo * configure.ac programs/sndfile-play.c OS X 10.8 uses a different audio API to previous versions. Fix compile failure on by disabling sndfile-play on this version. Someone needs to supply code for the new API. 2012-11-30 Erik de Castro Lopo * Octave/Makefile.am Octave/octave_test.sh Fix 'make distcheck'. 2012-10-13 Erik de Castro Lopo * M4/octave.m4 Relax constraints on Octave version. 2012-10-11 Erik de Castro Lopo * tests/utils.tpl Improve compare_*_or_die() functions. * src/command.c Fix bug reported by Keiler Florian. When reading short or int data from a file containing float data, and setting SFC_SET_SCALE_FLOAT_INT_READ to SF_TRUE would fail 3, 5, 7 and other channels counts. Problem was that psf_calc_signal_max() was not calculating the signal max correctly. Calculation of the signal max was failing because it was trying to read a sample count that was not an integer multiple of the channel count. * tests/channel_test.c tests/Makefile.am tests/test_wrapper.sh.in Add test for the above. 2012-09-25 Erik de Castro Lopo * src/sndfile.hh Added a constructor to allow the use of SF_VIRTUAL_IO. Patch from DannyDaemonic : https://github.com/erikd/libsndfile/pull/20 2012-08-23 Erik de Castro Lopo * doc/octave.html Fix link to octave.sourceforge.net. Thanks to IOhannes m zmoelnig. * src/mat5.c Allow reading of mat5 files without a specified sample rate (default to 44.1kHz). Thanks to IOhannes m zmoelnig. 2012-08-19 Erik de Castro Lopo * src/paf.c Error out if channel count is zero. Bug report from William ELla via launchpad: https://bugs.launchpad.net/ubuntu/+source/libsndfile/+bug/1036831 2012-08-04 Erik de Castro Lopo * configure.ac programs/sndfile-play.c Patch from Ricci Adams to use OSX's AudioQueues on OSX 10.7 and greater. 2012-07-09 Erik de Castro Lopo * programs/common.c Accept "ogg" as a file extention for Ogg/Vorbis files. 2012-06-22 Erik de Castro Lopo * src/flac.c Make sure any previously allocated FLAC stream encoder and stream decoder objects are deleted before a new one is allocated. 2012-06-20 Erik de Castro Lopo * tests/utils.tpl Rename gen_lowpass_noise_float() to gen_lowpass_signal_float() and add a sine wave component so that different FLAC compression levels can be tested. * src/sndfile.h.in doc/command.html Add SFC_SET_COMPRESSION_LEVEL and document it. * src/sndfile.c Catch SFC_SET_VBR_ENCODING_QUALITY command and implement it as the inverse of SFC_SET_COMPRESSION_LEVEL. * src/ogg_vorbis.c src/flac.c Implement SFC_SET_COMPRESSION_LEVEL command. * tests/test_wrapper.sh.in tests/compression_size_test.c Use the compression_size_test on FLAC as well. 2012-06-19 Erik de Castro Lopo * tests/ Rename vorbis_test.c -> compression_size_test.c so it can be extended to test FLAC as well. 2012-06-18 Erik de Castro Lopo * src/broadcast.c Fix a bug where a file with a 'bext' chunk with a zero length coding history field would get corrupted when the file was closed. Reported by Paul Davis of the Ardour project. * src/test_broadcast_var.c Add a test for the above. 2012-05-19 Erik de Castro Lopo * src/sndfile.c sf_format_check: For SF_FORMAT_AIFF, reject endian-ness setttings for non-PCM formats. 2012-04-12 Erik de Castro Lopo * src/aiff.c Fix regression in handling of odd length SSND chunks. Thanks Olivier Tristan for the example file. * src/aiff.c src/wav.c Exit parser loop when marker == 0. 2012-04-04 Erik de Castro Lopo * doc/FAQ.html Fix text. Thanks to Richard Collins. 2012-03-24 Erik de Castro Lopo * src/caf.c Exit parse loop if the marker is zero. Pass jump offsets as size_t instead of int. 2012-03-20 Erik de Castro Lopo * src/alac.c Fix segfault when decoding CAF/ALAC file with more than 4 channels. Fixes github issue #8 reported by Charles Van Winkle. 2012-03-20 Erik de Castro Lopo * src/common.h Change 'typedef SF_CHUNK_ITERATOR { ... } SF_CHUNK_ITERATOR' into 'struct SF_CHUNK_ITERATOR { ... }' to prevent older compilers from complaining of re-typedef-ing of SF_CHUNK_ITERATOR. * configure.ac Fix if test for empty $prefix. 2012-03-18 Erik de Castro Lopo * src/*.c tests/chunk_test.c Reworking of custom chunk handling code. - Memory for the iterator is now attached to the SF_PRIVATE struct and freed one sf_close(). - Rename sf_create_chunk_iterator() -> sf_get_chunk_iterator(). - Each SNDFILE handle never has more than one SF_CHUNK_ITERATOR handle. * tests/string_test.c Fix un-initialised char buffer. 2012-03-17 Erik de Castro Lopo * src/*.c tests/chunk_test.c Add improved handling of custom chunk getting and settings. Set of patches from IOhannes m zmoelnig submitted via github pull request #6. * src/alac.c Fix calculated frame count for files with zero block length. 2012-03-13 Erik de Castro Lopo * src/avr.c Remove double assignment to psf->endian. Thanks Kao Dome. * src/gsm610.c Fix clearing of buffers. Thanks Kao Dome. * src/paf.c Remove duplicate code. Thanks Kao Dome. * src/test_strncpy_crlf.c Fix minor error in test. Thanks Kao Dome. * src/common.h src/*.c Fix a bunch of valgrind errors. 2012-03-13 Erik de Castro Lopo * src/sndfile.c Fix typo in error string 'Uknown' -> 'Unknown'. * tests/fix_this.c Fix potential int overflow. 2012-03-10 Erik de Castro Lopo * src/alac.c Fix decoding of last block so that the decode length is not a multiple of the block length. Fixes github issue #4 reported by Charles Van Winkle. * src/sfconfig.h src/sfendian.h Fix for MinGW cross compiling. Use '#if (defined __*66__)' instead of '#if __*86__' because the MinGW header use '#ifdef __x86_64__'. 2012-03-10 Erik de Castro Lopo * src/ALAC/ src/alac.c Unify the interface between libsndfile and Apple ALAC codec. Regardless of file bit width samples are now passed between the two as int32_t that are justified towards the most significant bit. Without this modification, 16 conversion functions would have been needed between the libsndfile (short, int, float, double) types and the ALAC types (16, 20, 24 and 32 bit). With this mod, only 4 are needed. * tests/floating_point_test.tpl tests/write_read_test.(def|tpl) Add tests for 20 and 24 bit ALAC/CAF files. * src/command.c Add ALAC/CAF to the SFC_GET_FORMAT_* commands. Fixes github issue #5. * configure.ac Only use automake AM_SLIENT_RULES where supported. Thanks Dave Yeo. * tests/pipe_test.tpl Disable tests on OS/2. Thanks Dave Yeo. 2012-03-09 Erik de Castro Lopo * configure.ac src/sfconfig.h src/sfendian.h For GCC, use inline assembler for endian swapping. This should work with older versions of GCC like the one currently used in OS/2. 2012-03-06 Erik de Castro Lopo * src/alac.c Make sure temp file gets opened in binary mode. * src/alac.c src/common.c src/common.h Fix function alac_write16_d(). * tests/floating_point_test.tpl Add tests for 16 bit ALAC/CAF. * src/alac.c src/common.c src/common.h Add support for 32 bit ALAC/CAF files. * tests/floating_point_test.tpl tests/write_read_test.tpl Add tests for 32 bit ALAC/CAF files. 2012-03-05 Erik de Castro Lopo * src/ Refactor chunk storage so it work on big as well as little endian CPUs. * tests/chunk_test.c Clean up error messages. * src/sfendian.h src/*.c Rename endian swapping macros and add ENDSWAP_64 and BE2H_64. * configure.ac Detect presence of header file. * src/sfendian.h Use intrinsics (ie for MinGW) when is not present. Make ENDSWAP_64() work with i686-w64-mingw32 compiler. * src/ALAC/EndianPortable.c Add support for __powerpc__. * src/sfconfig.h Make sure HAVE_X86INTRIN_H is either 1 or 0. 2012-03-03 Erik de Castro Lopo * src/ALAC/* Big dump of code for Apple's ALAC file format. The copyyright to this code is owned by Apple who have released it under an Apache style license. A few small modifications were made to allow this to be integrated into libsndfile but unfortunately the history of those changes were lost because they were developed in a Bzr tree and during that time libsndfile moved to Git. * src/alac.c src/caf.c src/common.[ch] src/Makefile.am src/sndfile.h.in src/sndfile.c Hook new ALAC codec in. * programs/sndfile-convert.c Add support for alac codec. * tests/write_read_test.tpl Expand tests to cover ALAC. 2012-03-02 Erik de Castro Lopo * src/aiff.c src/wav.c Fix a couple of regressions from version 1.0.25. 2012-03-01 Erik de Castro Lopo * src/strings.c Minor refactoring. Make sure that the memory allocation size if always > 0 to avoid undefined behaviour. 2012-02-29 Erik de Castro Lopo * src/chunk.c Fix buffer overrun introduced in recently added chunk logging. This chunk logging has not yet made it to a libsndfile release version. Thanks to Olivier Tristan for providing an example file. * src/wav.c Fix handling of odd sized chunks which was causing the parser to lose some chunks. Thanks to Olivier Tristan for providing an example file. 2012-02-26 Erik de Castro Lopo * tests/util.tpl Used gnu_printf format checking with mingw-w64 compiler. * tests/header_test.tpl Printf format fixes. 2012-02-25 Erik de Castro Lopo * M4/extra_pkg.m4 Update PKG_CHECK_MOD_VERSION macro to add an AC_TRY_LINK step. This fix allows the configure process to catch attempts to link incompatible libraries. For example, linking 32 bit version of eg libFLAC to a 64 bit version of libsndfile will now fail. Similarly, when cross compiling libsndfile from Linux to Windows linking the Linux versions of a library to the Windows version of libsndfile will now also fail. * src/sndfile.h.in src/sndfile.c src/common.h src/create_symbols_file.py Add API function sf_current_byterate(). * src/dwvw.c src/flac.c src/ogg_vorbis.c src/sds.c Add codec specific handlers for current byterate. * tests/floating_point_test.tpl Add initial test for sf_current_byterate(). 2012-02-24 Erik de Castro Lopo * src/common.[ch] Add function psf_decode_frame_count(). * src/dwvw.c Fix a termnation bug that caused the decoder to go into an infinite loop. 2012-02-24 Erik de Castro Lopo * src/wav.c Fix a regression in the WAV header parser. Thanks to Olivier Tristan for bug report and the example file. 2012-02-21 Erik de Castro Lopo * src/sndfile.c Return error when SF_BROADCAST_INFO struct has bad coding_history_size. Thanks to Alex Weiss for the report. 2012-02-20 Erik de Castro Lopo * src/au.c src/flac.c src/g72x.c src/ogg_vorbis.c src/wav_w64.c Don't fake psf->bytewidth values. 2012-02-19 Erik de Castro Lopo * tests/string_test.c Fix valgrind warnings. * src/common.h src/sndfile.c src/strings.c Make string storage dynamically allocated. * src/sndfile.c Add extra validation for custom chunk handling. 2012-02-18 Erik de Castro Lopo * src/wav.c Improve handlling unknown chunk types. Thanks to Olivier Tristan for sending example files. * src/utils.tpl Add GCC specific testing for format string parameters for exit_if_true(). * tests/*.c tests/*.tpl Fix all printf format warnings. * programs/sndfile-play.c Remove un-needed OSX include . Thanks jamesfmilne for github issue #3. * tests/chunk_test.c Extend custom chunk test. 2012-02-12 Erik de Castro Lopo * src/wav.c Jump over some more chunk types while parsing. 2012-02-04 Erik de Castro Lopo * src/common.h src/strings.c Change way strings are stored in SF_PRIVATE in preparation for dynamically allocating the storage. 2012-02-02 Erik de Castro Lopo * src/common.h src*.c Improve encapsulation of string data in SF_PRIVATE. 2012-02-01 Erik de Castro Lopo * src/common.h src*.c Remove the buffer union from SF_PRIVATE. Most uses of this have been replaced with a BUF_UNION that is allocated on the stack. 2012-01-31 Erik de Castro Lopo * src/common.h src*.c Rename logbuffer field of SF_PRIVATE to parselog and reduce its size. Put the parselog buffer and the index inside a struct within SF_PRIVATE. 2012-01-26 Erik de Castro Lopo * configure.ac Fix typo, FLAC_CLFAGS -> FLAC_CFLAGS. Thanks to Jeremy Friesner. 2012-01-21 Erik de Castro Lopo * src/sndfile.c src/ogg.c Fix misleading error message when trying to create an SF_FORMAT_OGG file with anything other than SF_FORMAT_FILE. Thanks to Charles Van Winkle for the bug report. Github issue #1. 2012-01-20 Erik de Castro Lopo * src/sndfile.c src/wav.c Allow files opened in RDWR mode with string data in the tailer to be extended. Thanks to Bodo for the patch. * tests/string_test.c Add tests for the above changes (patch from Bodo). 2012-01-09 Erik de Castro Lopo * src/aiff.c Refactor reading of chunk size and use of psf_store_read_chunk(). * src/(caf|wav).c Correct storing of chunk offset. 2012-01-05 Erik de Castro Lopo * src/aiff.c src/wav.c src/common.h Refactor common code into src/common.h. * src/caf.c Make custom chunks work for CAF files. * tests/chunk_test.c tests/test_wrapper.sh.in Test CAF files with custom chunks. * src/sndfile.c Prevent psf->codec_close() being called more than once. 2012-01-04 Erik de Castro Lopo * programs/sndfile-cmp.c Catch the case where the second file has more frames than the first. 2012-01-02 Erik de Castro Lopo * src/create_symbols_file.py Add sf_set_chunk/sf_get_chunk_size/sf_get_chunk_data. 2011-12-31 Erik de Castro Lopo * tests/chunk_test.c tests/Makefile.am New test for custom chunks. * src/aiff.c src/chunk.c src/common.h src/sndfile.c Make custom chunks work on AIFF files. * src/wav.c Make custom chunks work on WAV files (includes refactoring). 2011-11-12 Erik de Castro Lopo * src/sndfile.h.in src/common.h src/sndfile.c Start working on setting/getting chunks. 2011-11-24 Erik de Castro Lopo * src/binheader_writef_check.py src/create_symbols_file.py Make it work for Python 2 and 3. Thanks Michael. 2011-11-19 Erik de Castro Lopo * libsndfile.spec.in Change field name 'URL' to 'Url'. * src/sndfile.h.in Add SF_SEEK_SET/CUR/END. 2011-11-05 Erik de Castro Lopo * src/id3.c Fix a stack overflow that can occur when parsing a file with multiple ID3 headers which would cause libsndfile to go into an infinite recursion until it blew the stack. Thanks to Anders Svensson for supplying an example file. 2011-10-30 Erik de Castro Lopo * src/double64.c src/float32.c src/common.h Make (float32|double_64)_(be|le)_read() functions const correct. 2011-10-28 Erik de Castro Lopo * src/sfendian.h Minor tweaking of types. Cast to ptr to correct final type rather void*. * programs/sndfile-play.c tests/utils.tpl Fix compiler warnings with latest MinGW cross compiler. 2011-10-13 Erik de Castro Lopo * src/file_io.c Use the non-deprecated resource fork name on OSX. Thanks to Olivier Tristan. 2011-10-12 Erik de Castro Lopo * src/wav.c Jump over the 'olym' chunks when parsing. 2011-10-06 Erik de Castro Lopo * tests/write_read_test.tpl Remove windows only truncate() implementation. 2011-09-04 Erik de Castro Lopo * src/sd2.c src/sndfile.c Make sure 23 bit PCM SD2 files are readable/writeable. * tests/write_read_test.tpl Add tests for 32 bit PCM SD2 files. 2011-08-23 Erik de Castro Lopo * configure.ac Use AC_SYS_LARGEFILE instead of AC_SYS_EXTRA_LARGEFILE as suggested by Jan Willies. 2011-08-07 Erik de Castro Lopo * configure.ac Makefile.am Move ACLOCAL_AMFLAGS setup to Makefile.am. 2011-07-15 Erik de Castro Lopo * doc/command.html Merge two separate blocks of SFC_SET_VBR_ENCODING_QUALITY documentation. * src/paf.c Replace ppaf24->samplesperblock with a compile time constant. 2011-07-13 Erik de Castro Lopo * src/ogg_vorbis.c Fix return value of SFC_SET_VBR_ENCODING_QUALITY command. * doc/command.html Document SFC_SET_VBR_ENCODING_QUALITY, SFC_GET/SET_LOOP_INFO and SFC_GET_INSTRUMENT. * NEWS README configure.ac doc/*.html Updates for 1.0.25. 2011-07-07 Erik de Castro Lopo * src/sfconfig.h Add handling for HAVE_SYS_WAIT_H. * Makefile.am src/Makefile.am tests/Makefile.am Add 'checkprograms' target. 2011-07-05 Erik de Castro Lopo * src/common.h src/sndfile.c Purge SF_ASSERT macro. Use standard C assert instead. * src/paf.c src/common.h src/sndfile.c Fix for Secunia Advisory SA45125, heap overflow (heap gets overwritten with byte value of 0) due to integer overflow if PAF file handler. * src/ima_adpcm.c src/ms_adpcm.c src/paf.c Use calloc instead of malloc followed by memset. * tests/utils.tpl Clean up use of memset. 2011-07-05 Erik de Castro Lopo * src/ogg.c Fix log message. * tests/format_check_test.c Fix compiler warnings. 2011-07-04 Erik de Castro Lopo * src/sndfile.c Fix error message for erro code SFE_ZERO_MINOR_FORMAT. * tests/format_check_test.c Add a test to for SF_FINFO format field validation. * src/ogg.c src/ogg_vorbis.c src/ogg.h src/ogg_pcm.c src/ogg_speex.c src/common.h src/Makefile.am Move vorbis specific code to ogg_vorbis.c, add new files for handling PCM and Speex codecs in an Ogg container. The later two are only enabled with ENABLE_EXPERIMENTAL_CODE config variable. 2011-06-28 Erik de Castro Lopo * src/strings.c Clean up and refactor storage of SF_STR_SOFTWARE. 2011-06-23 Erik de Castro Lopo * src/sndfile.h.in doc/api.html Fix definition of SF_STR_LAST and update SF_STR_* related docs. Thanks to Tim van der Molen for the patch. 2011-06-21 Erik de Castro Lopo * programs/sndfile-interleave.c Fix handling of argc. Thanks to Marius Hennecke. * src/wav_w64.c Accept broken WAV files with blockalign == 0. Thanks to Olivier Tristan for providing example files. * src/wav.c Jump over 'FLLR' chunks. 2011-06-14 Erik de Castro Lopo * src/sndfile.h.in Fix -Wundef warning due to ENABLE_SNDFILE_WINDOWS_PROTOTYPES. * configure.ac Add -Wundef to CFLAGS. * src/ogg.c Fix -Wunder warning. 2011-05-18 Erik de Castro Lopo * configure.ac Use int64_t instead of off_t when they are the same size. * src/Makefile.am tests/Makefile.am Use check_PROGRAMS instead of noinst_PROGRAMS where appropriate. 2011-05-08 Erik de Castro Lopo * src/wav.c Don't allow unknown and/or un-editable chunks to prevent the file from being opened in SFM_RDWR mode. 2011-04-25 Erik de Castro Lopo * tests/format_check_test.c Fix segfault in test program. 2011-04-25 Erik de Castro Lopo * tests/format_check_test.c New test program to check to make sure that sf_open() and sf_check_format() agree as to what is a valid program. * tests/Makefile.am tests/test_wrapper.sh.in Hook into build and test runner. * src/sndfile.c Fix some sf_format_check() problems. Thanks to Charles Van Winkle for the notification. 2011-04-06 Erik de Castro Lopo * src/caf.c Add validation to size of 'data' chunk and fix size of written 'data' chunk. Thanks to Michael Pruett for reporting this. 2011-03-28 Erik de Castro Lopo * src/* tests/* programs/* Fix a bunch of compiler warnings with gcc-4.6. 2011-03-25 Erik de Castro Lopo * tests/util.tpl Add NOT macro to util.h. * src/strings.c Fix handling of SF_STR_SOFTWARE that resulted in a segfault due to calling strlen() on an unterminated string. Thanks to Francois Thibaud for reporting this problem. * tests/string_test.c Add test for SF_STR_SOFTWARE segfault bug. * configure.ac Sanitize FLAC_CFLAGS value supplied by pkg-config which returns a value of '-I${includedir}/FLAC'. However FLAC also provides an include file which clashes with the Standard C header of the same name. The solution is strip the 'FLAC' part off the end and include all FLAC headers as . * configure.ac src/Makefile.am Use non-recursive make in src/ directory. 2011-03-23 Erik de Castro Lopo * NEWS README docs/*.html Updates for 1.0.24 release. 2011-03-22 Erik de Castro Lopo * configure.ac Fix up usage of sed (should not assume GNU sed). * M4/add_(c|cxx)flags.m4 Test flags in isolation. * tests/cpp_test.cc Fix a broken test (test segfaults). Report by Dave Flogeras. 2011-03-21 Erik de Castro Lopo * programs/common.[ch] Add function program_name() which returns the program name minus the path from argv [0]. * programs/*.c programs/Makefile.am Use program_name() where appropriate. Fix build. 2011-03-20 Erik de Castro Lopo * src/wav.c For u-law and A-law files, write an 18 byte 'fmt ' chunk instead of a 16 byte one. Win98 accepts files with a 16 but not 18 byte 'fmt' chunk. Later version accept 18 byte but not 16 byte. 2011-03-15 Erik de Castro Lopo * doc/FAQ.html Add examples for question 12. * doc/libsndfile.css.in Add tweaks for h4 element. * doc/api.html Add documentation for virtual I/O functionality. Thanks to Uli Franke. * tests/util.tpl Add static inline functions sf_info_clear() and sf_info_setup(). * tests/(alaw|dwvw|ulaw)_test.c Use functions sf_info_clear() and sf_info_setup(). 2011-03-08 Erik de Castro Lopo * configure.ac Fail more gracefully if pkg-config is missing. Suggestion from Brian Willoughby. 2011-02-27 Erik de Castro Lopo * src/common.c Use size_t instead of int for size params with varargs. 2011-02-09 Erik de Castro Lopo * doc/index.html Update supported platforms with more Debian platforms and Android. 2011-01-27 Erik de Castro Lopo * src/sndfile.hh Add an LPCWSTR version of the SndfileHandle constructor to the SndfileHandle class definition. Thanks to Eric Eizenman for pointing out this was missing. * tests/cpp_test.cc Add test for LPCWSTR version of the SndfileHandle constructor. 2011-01-19 Erik de Castro Lopo * programs/sndfile-play.c Remove cruft. 2010-12-01 Erik de Castro Lopo * src/sndfile.hh Add methods rawHandle() and takeOwnership(). Thanks to Tim Blechmann for the patch. * tests/cpp_test.cc Add tests for above two methods. Also supplied by Tim Blechmann. 2010-11-11 Erik de Castro Lopo * doc/api.html Add mention of use of sf_strerror() when sf_open() fails. 2010-11-01 Erik de Castro Lopo * configure.ac Make TYPEOF_SF_COUNT_T int64_t where possible. This may fix problems where people are compiling on a 64 bit system with the GCC -m32 flag. * src/sndfile.h.in Fix comments on sf_count_t. 2010-10-26 Erik de Castro Lopo * src/aiff.c Handle non-zero offset field in SSND chunk. Thanks to Michael Chinen. 2010-10-20 Erik de Castro Lopo * configure.ac Sed fix for FreeBSD. Thanks Tony Theodore. 2010-10-14 Erik de Castro Lopo * shave.in M4/shave.m4 Fix shave invocation of windres compiler. Thanks Damien Lespiau (upstream shave author). * configure.ac M4/shave.m4 shave-libtool.in shave.in Switch from shave to automake-1.11's AM_SILENT_RULES. 2010-10-13 Erik de Castro Lopo * shave-libtool.in shave.in Sync to upstream version. * src/rf64.c More work to make the parser more robust and accepting of mal-formed files. 2010-10-12 Erik de Castro Lopo * src/common.h Add functions psf_strlcpy() and psf_strlcat(). * src/broadcast.c src/sndfile.c src/strings.c src/test_main.c src/test_main.h src/test_strncpy_crlf.c Use functions psf_strlcpy() and psf_strlcat() as appropriate. * tests/string_test.c Add tests for SF_STR_GENRE and SF_STR_TRACKNUMBER. * src/rf64.c Fix size of 'ds64' chunk when writing RF64. 2010-10-10 Erik de Castro Lopo * programs/*.c Add the libsndfile version to the usage message of all programs. 2010-10-10 Erik de Castro Lopo * configure.ac src/version-metadata.rc.in src/Makefile.am Add version string resources to the windows DLL. * doc/api.html Update to add missing SF_FORMAT_* values. Closed Debian bug #545257. * NEWS README configure.ac doc/*.html Updates for 1.0.23 release. 2010-10-09 Erik de Castro Lopo * tests/pedantic-header-test.sh.in Handle unusual values of CC environment variable. * src/rf64.c Minor tweaks and additional sanity checking. * src/Makefile.am src/binheader_writef_check.py Use python 2.6. 2010-10-08 Erik de Castro Lopo * src/sndfile.hh Add a missing 'inline' before a constructor defintion. 2010-10-06 Erik de Castro Lopo * src/common.h Add macro NOT. * src/rf64.c Minor tweaks. * Makefile.am */Makefile.am Add *~ to CLEANFILES. 2010-10-05 Erik de Castro Lopo * src/sndfile.c Fix a typo in the error string for SFE_OPEN_PIPE_RDWR. Thanks to Charles Van Winkle for the report. 2010-10-04 Erik de Castro Lopo * src/flac.c src/ogg.c src/sndfile.h.in src/strings.c src/wav.c Add ability to read/write tracknumber and genre to flac/ogg/wav files. Thanks to Matti Nykyri for the patch. * src/common.h src/broadcast.c src/strings.c Add function psf_safe_strncpy() and use where appropriate. 2010-10-04 Erik de Castro Lopo * NEWS README configure.ac doc/*.html Updates for 1.0.22 release. 2010-10-03 Erik de Castro Lopo * src/common.h src/broadcast.c src/rf64.c src/sndfile.c src/wav.c Rewrite of SF_BROADCAST_INFO handling. * src/test_broadcast_var.c tests/command_test.c Tweak SF_BROADCAST_INFO tests. * src/test_broadcast_var.c Fix OSX stack check error. 2010-09-30 Erik de Castro Lopo * src/sds.c Set sustain_loop_end to 0 as suggested by Brian Lewis. 2010-09-29 Erik de Castro Lopo * src/sds.c Make sure the correct frame count gets written into the header. * tests/write_read_test.tpl Don't allow SDS files to have a long frame count. 2010-09-17 Erik de Castro Lopo * src/sds.c Apply a pair of patches from Brian Lewis to fix the packet number location and the checksum. 2010-09-10 Erik de Castro Lopo * src/aiff.c src/file_io.c src/ogg.c src/rf64.c src/sndfile.c src/strings.c src/test_audio_detect.c src/test_strncpy_crlf.c src/wav.c tests/pcm_test.tpl Fix a bunch of minor issues found using static analysis. 2010-08-23 Erik de Castro Lopo * src/test_broadcast_var.c New file containing tests for broadcast_set_var(). * src/Makefile.am src/test_main.[ch] Hook test_broadcast_var.c into tests. 2010-08-22 Erik de Castro Lopo * src/broadcast.c src/common.(c|h) Move function strncpy_crlf() to src/common.c so the function can be tested in isolation. * src/test_strncpy_crlf.c New file. * src/Makefile.am src/test_main.[ch] Hook test_strncpy_crlf.c into tests. 2010-08-18 Erik de Castro Lopo * src/common.h Move code around to make comments make sense. * src/broadcast.c Add debugging code that is disabled by default. 2010-08-02 Erik de Castro Lopo * src/flac.c When the file meta data says the file has zero frames set psf->sf.frames to SF_COUNT_MAX. Fixes Debian bug #590752. * programs/sndfile-info.c Print 'unknown' if frame count == SF_COUNT_MAX. 2010-06-27 Erik de Castro Lopo * src/sndfile.c Only support writing mono SVX files. Multichannel SVX files are not interleaved and there is no support infrastructure to cache and write multiple channels to create a non-interleaved file. * src/file_io.c Don't call close() on a file descriptor of -1. Thanks to Jeremy Friesner for the bug report. 2010-06-09 Erik de Castro Lopo * src/common.h Add macro SF_ASSERT. * src/sndfile.c Use SF_ASSERT to ensure sizeof (sf_count_t) == 8. * src/svx.c Add support for reading and writing stereo SVX files. 2010-05-07 Erik de Castro Lopo * configure.ac When compiling with x86_64-w64-mingw32-gcc link with -static-libgcc flags. * programs/common.c programs/sndfile-metadata-set.c Update metadata after the audio data is copied. Other minor fixes. Patch from Marius Hennecke. 2010-05-04 Erik de Castro Lopo * src/nist.c Fix a regression reported by Hugh Secker-Walker. * src/api.html Add comment about sf_open_fd() not working on Windows if the application and the libsndfile DLL are linked to different versions of the Microsoft C runtime DLL. 2010-04-23 Erik de Castro Lopo * tests/pedantic-header-test.sh.in Fix 'make distcheck'. 2010-04-21 Erik de Castro Lopo * tests/pedantic-header-test.sh.in New file to test whether sndfile.h can be compiled with gcc's -pedantic flag. * configure.ac tests/test_wrapper.sh.in Hook pedantic-header-test into test suite. * src/sndfile.h.in Fix -pedantic warning. 2010-04-19 Erik de Castro Lopo * programs/sndfile-salvage.c programs/Makefile.am New program to salvage the audio data from WAV/WAVEX/AIFF files which are greater than 4Gig in size. 2010-04-09 Erik de Castro Lopo * programs/sndfile-convert.c Fix valgrind warning. 2010-04-06 Erik de Castro Lopo * programs/sndfile-cmp.c When files differ in the PCM data, also print the difference offset. Minor cleanup. 2010-03-19 Erik de Castro Lopo * src/aiff.c Don't use the 'twos' marker for 24 and 32 bit PCM, use 'in24' and 'in32' instead. Thanks to Paul Davis (Ardour) for this suggestion. 2010-02-28 Erik de Castro Lopo * configure.ac Clean up configure report. * tests/utils.tpl Add functions test_read_raw_or_die and test_write_raw_or_die. * tests/rdwr_test.(def|tpl) tests/Makefile.am Add new test program and hook into build. * src/sndfile.c Fix minor issues with sf_read/write_raw(). Bug reported by Milan Křápek. * tests/test_wrapper.sh.in Add rdwr_test to the test wrapper script. 2010-02-22 Erik de Castro Lopo * configure.ac Remove -fpascal-strings from OSX's OS_SPECIFIC_CFLAGS. * programs/common.[ch] programs/sndfile-metadata-set.c Apply a patch from Robin Gareus allowing the setting of the time reference field of the BEXT chunk. 2010-02-06 Erik de Castro Lopo * src/ima_adpcm.c Add a fix from Jonatan Liljedahl to handle predictor overflow when decoding IMA4. 2010-01-26 Erik de Castro Lopo * src/sndfile.hh Add a constructor which takes an existing file descriptor and then calls sf_open_fd(). Patch from Sakari Bergen. 2010-01-10 Erik de Castro Lopo * programs/sndfile-deinterleave.c programs/sndfile-interleave.c Improve usage messages. 2010-01-09 Erik de Castro Lopo * src/id3.c src/Makefile.am Add new file src/id3.c and hook into build. * src/sndfile.c src/common.h Detect and skip and ID3 header at the start of the file. 2010-01-07 Erik de Castro Lopo * programs/common.c Fix update_strings() copyright, comment, album and license are correctly written. Thanks to Todd Allen for reporting this. * man/Makefile.am Change GNU makeism to something more widely supported. Thanks to Christian Weisgerber for reporting this. * configure.ac programs/Makefile.am programs/sndfile-play.c Apply patch from Christian Weisgerber and Jacob Meuserto add support for OpenBSD's sndio. 2010-01-05 Erik de Castro Lopo * doc/api.html Discourage the use of sf_read/write_raw(). 2009-12-28 Erik de Castro Lopo * configure.ac Test for Unix pipe() and waitpid() functions. * src/sfconfig.h tests/pipe_test.tpl Disable pipe_test if pipe() and waitpid() aren't available. 2009-12-16 Erik de Castro Lopo * configure.ac src/Makefile.am src/create_symbols_file.py src/make-static-lib-hidden-privates.sh Change name of generated file src/Symbols.linux to Symbols.gnu-binutils and and use the same symbols file for other systems which use GNU binutils like Debian's kfreebsd. * M4/shave.m4 shave.in Update shave files from upstream. 2009-12-15 Erik de Castro Lopo * man/sndfile-metadata-get.1 Fix typo. * man/sndfile-interleave.1 man/Makefile.am New man page. 2009-12-13 Erik de Castro Lopo * src/ogg.c When decoding to short or int, clip the decoded signal to [-1.0, 1.0] if its too hot. Thanks to Dmitry Baikov for suggesting this. * NEWS README doc/*.html Updates for 1.0.21. 2009-12-09 Erik de Castro Lopo * programs/sndfile-jackplay.c man/sndfile-jackplay.1 Remove these which will now be in found in the sndfile-tools package. * programs/Makefile.am man/Makefile.am Remove build rules for sndfile-jackplay. * configure.ac Remove detection of JACK Audio Connect Kit. * programs/sndfile-concat.c man/sndfile-concat.1 Add new program with man page. * man/Makefile.am programs/Makefile.am Hook sndfile-concat into build system. 2009-12-08 Erik de Castro Lopo * tests/error_test.c Don't terminate when sf_close() returns zero in error_close_test(). It seems that Windows 7 behaves differently from earlier versions of Windows. 2009-12-03 Erik de Castro Lopo * configure.ac M4/*.m4 Rename all custom macros from AC_* to MN_*. * programs/sndfile-interleave.c Make it actually work. 2009-12-02 Erik de Castro Lopo * doc/*.html configure.ac Corrections and clarifications courtesy of Robin Forder. * programs/sndfile-convert.c programs/common.[ch] Move some code from convert to common for reuse. * programs/sndfile-interleave.c programs/sndfile-interleave.c Add new programs sndfile-interleave and sndfile-deinterleave. * programs/Makefile.am Hook new programs into build. 2009-12-01 Erik de Castro Lopo * src/create_symbols_file.py tests/stdio_test.c tests/win32_test.c Minor OS/2 tweaks as suggested by David Yeo. * tests/multi_file_test.c Fix file creation flags on windows. Thanks to Bruce Sharpe. * src/sf_unistd.h Set all group and other file create permssions to zero. * tests/win32_test.c Add a new test. 2009-11-30 Erik de Castro Lopo * doc/print.css doc/*.html Add a print stylesheet and update all HTML documents to reference it. Thanks to Aditya Bhargava for suggesting this. * doc/index.html Minor corrections. 2009-11-29 Erik de Castro Lopo * sndfile.pc.in Add a Libs.private entry to assist with static linking. 2009-11-28 Erik de Castro Lopo * src/make-static-lib-hidden-privates.sh src/Makefile.am Add a script to hide all non-public symbols in the libsndfile.a static library. 2009-11-22 Erik de Castro Lopo * tests/locale_test.c Correct usage of ENABLE_SNDFILE_WINDOWS_PROTOTYPES. 2009-11-20 Erik de Castro Lopo * src/windows.c Correct usage of ENABLE_SNDFILE_WINDOWS_PROTOTYPES. 2009-11-16 Erik de Castro Lopo * programs/sndfile-convert.c Allow the program to read from stdin by specifying '-' on the command line as the input file. * src/sndfile.h.in Hash define ENABLE_SNDFILE_WINDOWS_PROTOTYPES to 1 for greater safety. * tests/virtual_io_test.c Add a PAF/PCM_24 test and verify the file length is not negative immediately after openning the file for write. 2009-10-18 Erik de Castro Lopo * src/wav.c When writing loop lengths, adjust the end position by one to make up for Microsoft's screwed up spec. Thanks to Olivier Tristan for the patch. 2009-10-14 Erik de Castro Lopo * src/flac.c Apply patch from Uli Franke allowing FLAC files to be encoded at any sample rate. 2009-10-09 Erik de Castro Lopo * src/nist.c Fix parsing of odd ulaw encoded file provided by Jan Silovsky. * configure.ac Insist on libvorbis >= 1.2.3. Earlier verions have bugs that cause the libsndfile test suite to fail on MIPS, PowerPC and others. See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=549899 2009-10-06 Erik de Castro Lopo * man/sndfile-convert.1 Fix warning from Debian's lintian checks. * man/sndfile-cmp.1 man/sndfile-jackplay.1 man/sndfile-metadata-get.1 man/Makefile.am Add three new minimal manpages and hook into build. 2009-10-05 Erik de Castro Lopo * tests/test_wrapper.sh.in Don't run cpp_test on x86_64-w64-mingw32. 2009-09-28 Erik de Castro Lopo * tests/utils.tpl On windows, make sure the open() function doesn't get called with a third parameter of 0 which fails for no good reason. Also make sure this third parameter doesn't get called with S_IRGRP when compiling for windows because Wine complains. * src/sndfile.hh Add a SndfileHandle constructor for windows that takes a 'const wchar_t *' string. * doc/FAQ.html Add Q/A : I'm cross compiling libsndfile for another platform. How can I run the test suite? * src/create_symbols_file.py src/Makefile.am Add Symbols.static target, a list of symbols, one per line. 2009-09-27 Erik de Castro Lopo * tests/test_wrapper.sh.in Update to allow all tests to be gathered up into a testsuite tarball and then be run using this script. * build-test-tarball.mk.in Add a Make script to build a tarball of all the test binaries and the test wrapper script. This is useful for cross compiling; you can build the binaries, build test test tarball and transfer the test tarball to the target machine for testing. 2009-09-26 Erik de Castro Lopo * src/common.h src/*.c Modify SF_FILE struct to allow it to carry either 8-bit or 16-bit strings for the file path, directory and name. Fixes for this change throughout. * src/windows.c src/Makefile.am New file defining new windows only public function sf_wchar_open() which takes a 'const wchar_t *' string (LPCWSTR) for the file name parameter. * src/sndfile.h.in Add SF_CHANNEL_MAP_ABISONIC_* entries. Add windows only defintion for sf_wchar_open(). * src/create_symbols_file.py Add sf_wchar_open() to the list of public symbols (windows only). * tests/locale_test.c Add a wchar_test() to test sf_wchar_open(). 2009-09-25 Erik de Castro Lopo * src/common.h src/*.c Split file stuff into PSF_FILE struct within the SF_PRIVATE struct. 2009-09-23 Erik de Castro Lopo * src/aiff.c src/voc.c When a byte is needed, use unsigned char. * src/ima_oki_adpcm.c src/broadcast.c src/test_ima_oki_adpcm.c Include sfconfig.h to prevent compile errors with MinGW compilers. * configure.ac Remove AM_CONFIG_HEADER due to warnings from autoconf 2.64. * tests/locale_test.c Update to work with xx_XX.UTF-8 style locales. Refactoring. 2009-09-22 Erik de Castro Lopo * configure.ac Set __USE_MINGW_ANSI_STDIO to 1 when compiling using MinGW compilers. Remove unneeded AC_SUBST. Report Host CPU/OS/vendor. 2009-09-19 Erik de Castro Lopo * src/sndfile.c Fix error message string. * src/flac.c Add 88200 to the list of supported sample rates. * src/ogg.c Fix compiler warning when using gcc-4.5.0. * programs/sndfile-info.c tests/utils.tpl Remove WIN32 snprintf #define. * src/ima_adpcm.c Fix minor bug in aiff_ima_encode_block. Thanks to Denis Fileev for finding this. 2009-09-16 Erik de Castro Lopo * src/caf.c Use the correct C99 format specifier for int64_t. * M4/endian.m4 Fix detection of CPU endian-ness when cross compiling. Thanks to Pierre Ossman for the bug report. * src/caf.c src/sndfile.c Fix reading and writing of PEAK chunks in CAF files. * tests/peak_chunk_test.c tests/test_wrapper.sh.in Run peak_chunk_test on CAF files. 2009-09-15 Erik de Castro Lopo * src/aiff.c src/wav.c Use the correct C99 format specifier for int64_t. 2009-08-30 Erik de Castro Lopo * src/rf64.c src/sndfile.c src/wav.c src/wav_w64.h Apply a patch (massaged slightly) from Uli Franke adding handling of the BEXT chunk in RF64 files. * tests/command_test.c Update channel_map_test() function so WAV test passes. * src/rf64.c Add channel mapping and ambisonic support. * src/sndfile.h Add comments showing correspondance between libsndfile channel map defintiions and those used by Apple and MS. Add handling of reading/writing channel map info. * tests/command_test.c tests/test_wrapper.sh.in Update channel map tests. 2009-07-29 Erik de Castro Lopo * src/common.h Add function psf_isprint() a replacement for the standard C isprint() function which ignores any locale settings and treats all input as ASCII. * src/(aiff|common|rf64|sd2|strings|svx|wav).c Use psf_isprint() instead of isprint(). 2009-07-13 Erik de Castro Lopo * src/command.c Add string descriptions for SF_FORMAT_RF64 and SF_FORMAT_MPC2K. 2009-06-30 Erik de Castro Lopo * programs/sndfile-play.c Allow use of Open Sound System audio output under FreeBSD. 2009-06-24 Erik de Castro Lopo * configure.ac Add patch from Conrad Parker to add --disable-jack. 2009-05-28 Erik de Castro Lopo * src/alaw.c src/float32.c src/htk.c src/pcm.c src/sds.c src/ulaw.c Fix bugs where invalid files can cause a divide by zero error (SIGFPE). Thanks to Sami Liedes for reporting this a Debian bug #530831. 2009-05-26 Erik de Castro Lopo * src/chanmap.[ch] New files for channel map decoding/encoding. 2009-05-25 Erik de Castro Lopo * configure.ac src/sndfile.h.in Fix MSVC definition of sf_count_t. 2009-05-24 Erik de Castro Lopo * src/wav_w64.[ch] Add wavex_channelmask to WAV_PRIVATE struct and add a function to convert an array of SF_CHANNEL_MASK_* values into a bit mask for use in WAV files. * src/wav.c Add ability to write the channel mask. 2009-05-23 Erik de Castro Lopo * programs/sndfile-info.c Add -c command line option to dump the channel map information. * src/wav_w64.c Don't bail from parser if channel map bitmask is faulty. * src/common.h src/sndfile.c Remove error code SFE_W64_BAD_CHANNEL_MAP which is not needed any more. * src/sndfile.c On SFC_SET_CHANNEL_MAP_INFO pass the channel map command down to container's command handler. 2009-05-22 Erik de Castro Lopo * src/sndfile.h.in src/common.h src/sndfile.c src/wav_w64.c Apply a patch from Lennart Poettering (PulseAudio) to allow reading of channel data in WAV and W64 files. Add a test for the above. 2009-05-20 Erik de Castro Lopo * src/FAQ.html Update the section about pre-compiled binaries for Win64. 2009-05-14 Erik de Castro Lopo * src/common.h src/test_conversions.c Be more careful when including so compiling on pre-C99 platforms (hello Slowlaris) might actually work. * NEWS README doc/*.html Updates for 1.0.20. 2009-04-21 Erik de Castro Lopo * src/voc.c Fix a bug whereby opening a specially crafted VOC file could result in a heap overflow. Thanks to Tobias Klein (http://www.trapkit.de) for reporting this issue. * src/aiff.c Fix potential (heap) buffer overflow when parsing 'MARK' chunk. 2009-04-12 Erik de Castro Lopo * tests/stdin_test.c Check psf->error after opening file. * src/file_io.c Fix obscure seeking bug reported by Hugh Secker-Walker. * tests/utils.tpl Add check of sf_error to test_open_file_or_die(). * src/sndfile.c Clear error if opening resource fork fails. 2009-04-11 Erik de Castro Lopo * tests/alaw_test.c tests/locale_test.c tests/ulaw_test.c Cleanup output. 2009-03-25 Erik de Castro Lopo * src/float32.c Fix f2s_clip_array. 2009-03-24 Erik de Castro Lopo * src/float32.c In host_read_f2s call convert instead of f2s_array. * src/ima_adpcm.c Remove dead code. * src/test_ima_oki_adpcm.c examples/generate.c tests/dither_test.c tests/dwvw_test.c tests/fix_this.c tests/generate.c tests/multi_file_test.c Minor fixes. 2009-03-23 Erik de Castro Lopo * M4/shave.m4 shave.in Pulled update from upstream. 2009-03-19 Erik de Castro Lopo * doc/api.html Add pointers to example programs in source code tarball. 2009-03-17 Erik de Castro Lopo * src/common.h Define SF_PLATFORM_S64 for non-gcc compilers with 'long long' type. * configure.ac Add documentation for --disable-external-libs and improve error handling for that option. * src/sndfile.c src/sndfile.h.in src/create_symbols_file.py Add public function sf_version_string. * tests/sfversion.c Test function sf_version_string. * M4/shave.m4 shave-libtool.in shave.in Add new files from 'git clone git://git.lespiau.name/shave'. * configure.ac Enable shave. * src/Makefile.am src/binheader_writef_check.py Octave/* Shave related tweaks. 2009-03-15 Erik de Castro Lopo * src/common.h src/caf.c src/sndfile.c Add SF_MAX_CHANNELS (set to 256) and use it. * src/sndfile.h.in Check for either _MSCVER or _MSC_VER being defined. 2009-03-04 Erik de Castro Lopo * tests/vorbis_test.c Relax test slighly to allow test to pass on more CPUs etc. 2009-03-03 Erik de Castro Lopo * configure.ac Detect vorbis_version_string() correctly. 2009-03-02 Erik de Castro Lopo * doc/index.html Add a 'See Also' section with a link to sndfile-tools. * NEWS README doc/*.html Updates for 1.0.19 release. * configure.ac Fix --enable-external-libs logic. 2009-03-01 Erik de Castro Lopo * src/aiff.c Fix resource leak and potential read beyond end of buffer. * src/nist.c Fix reading of header value sample_n_bytes. * src/sd2.c src/wav.c Fix potential read beyond end of buffer. * src/sndfile.c src/svx.c Check return values of file_io functions. * tests/win32_test.c Fix resource leak. * configure.ac Detect the presence/absence of vorbis_version_string() in libvorbis. * src/ogg.c Only call vorbis_version_string() from libvorbis if present. 2009-02-24 Erik de Castro Lopo * tests/win32_test.c Don't use sprintf, even on windows. * src/aiff.c src/rf64.c src/wav.c Eliminate dead code, more validation of data read from file. 2009-02-22 Erik de Castro Lopo * src/ima_adpcm.c Clamp values to a valid range before indexing ima_step_size array. * src/GSM610/*.c tests/*c programs/*.c src/audio_detect.c Don't include un-needed headers. * programs/sndfile-info.c Remove dead code. * tests/test_wrapper.sh.in Add 'set -e' so the script exits on error. * src/test_ima_oki_adpcm.c Fix read beyond end of array. * tests/win32_test.c Add missing close on file descriptor. * src/nist.c programs/sndfile-metadata-set.c Fix 'unused variable' warnings. * src/aiff.c Fix potential memory leak in handling of 'MARK' chunk. Remove un-needed test (unsigned > 0). * src/sd2.c Improve handling of heap allocated buffer. * src/sndfile.c Remove un-needed test (always true). * src/wav.c src/rf64.c Ifdef out dead code that will be resurected some time in the future. * src/wav.c src/w64.c src/xi.c Handle error return values from psf_ftell. * src/wav_w64.c Fix handling and error checking of MSADPCM coefficient arrays. * regtest/*.c Bunch of fixes. * src/test_file_io.c Use snprintf instead of strncpy in test program. 2009-02-21 Erik de Castro Lopo * src/sd2.c Validate data before using. * src/caf.c Validate channels per frame value before using, fixing a possible integer overflow bug, leading to a possible heap overflow. Found by Alin Rad Pop of Secunia Research (CVE-2009-0186). 2009-02-20 Erik de Castro Lopo * Octave/octave_test.sh Unset TERM environment variable and export LD_LIBRARY_PATH. 2009-02-16 Erik de Castro Lopo * src/file_io.c In windows code, cast LPVOID to 'char*' in printf. 2009-02-15 Erik de Castro Lopo * M4/octave.m4 Clear the TERM environment before evaluating anything in Octave. This works around problems that might occur if a users TERM settings are incorrect. Thanks to Rob Til Freedmen for helping to debug this. * src/wav.c Handle four zero bytes as a marker within a LIST or INFO chunk. Thanks to Rogério Brito for supplying an example file. 2009-02-14 Erik de Castro Lopo * src/common.h src/*.c Use C99 snprintf everywhere. 2009-02-11 Erik de Castro Lopo * tests/test_wrapper.sh.in New file to act as the template for the test wrapper script. * configure.ac Generate tests/test_wrapper.sh from the template. * tests/Makefile.am Replace all tests with a single invocation of the test wrapper script. 2009-02-09 Erik de Castro Lopo * src/ogg.c Record vorbis library version string. * configure.ac Require libvorbis >= 1.2.2. * M4/endian.m4 Fix bracketing of function for autoconf 2.63. Thanks to Richard Ash. * M4/octave.m4 M4/mkoctfile_version.m4 Clean up AC_WITH_ARG usage using AC_HELP_STRING. 2009-02-08 Erik de Castro Lopo * Octave/Makefile.am Use $(top_buildir) instead of $(builddir) which may not be defined. * M4/octave.m4 Improve logic and status reporting. 2009-02-07 Erik de Castro Lopo * configure.ac AUTHORS NEWS README doc/*.html Final tweaks for 1.0.18 release. 2009-02-03 Erik de Castro Lopo * programs/sndfile-convert.c Add 'htk' to the list of convert formats. * programs/sndfile-info.c Simplify get_signal_max using SFC_CALC_SIGNAL_MAX command. Increase size of files for which signal max will be calculated. 2009-01-14 Erik de Castro Lopo * doc/index.html Fix links for SoX and WavPlay. Thanks to Daniel Griscom. 2009-01-11 Erik de Castro Lopo * programs/sndfile-metadata-get.c Make valgrind clean. Clean up temp string array usage. Error out if trying to update coding history in RDWR mode. 2009-01-10 Erik de Castro Lopo * doc/index.html Fix links to versions of the LGPL. 2008-12-14 Erik de Castro Lopo * tests/string_test.c Add test for RDWR mode where the file ends up shorter than when it was opened. * src/wav.c Truncate the file on close for RDWR mode where the file ends up shorter than when it was opened. 2008-11-30 Erik de Castro Lopo * M4/add_cflags.m4 Fix problem with quoting of '#include'. * M4/add_cxxflags.m4 configure.ac Add new file M4/add_cxxflags.m4 and use it in configure.ac. 2008-11-19 Erik de Castro Lopo * programs/sndfile-info.c Apply patch from Conrad Parker to calculate and display total duration when more than one file is dumped. 2008-11-10 Erik de Castro Lopo * configure.ac src/Makefile.am Tweaks to generation of Symbols files. * tests/win32_ordinal_test.c Update tests for above changes. 2008-11-06 Erik de Castro Lopo * programs/common.c When merging broadcast info, make sure to clear the destination field before copying in the new data. * programs/test-sndfile-metadata-set.py Add test for the above. * src/broadcast.c Fix checking of required coding_history_size. 2008-10-28 Erik de Castro Lopo * tests/command_test.c Add test to detect if coding history is truncated. * src/broadcast.c Fix truncation of coding history. 2008-10-27 Erik de Castro Lopo * tests/command_test.c Add broadcast_coding_history_size test. * programs/*.[ch] Use SF_BROADCAST_INFO_VAR to manipulate larger 'bext' chunks. * src/rf64.c Add code to prevent infinite loop on malformed file. * src/common.h src/sndfile.c src/w64.c src/wav_w64.c Rationalize and improve error handling when parsing 'fmt ' chunk. * M4/octave.m4 Simplify and remove cruft. Check for correct Octave version. * Octave/* Reduce 3 C++ files to one, fix build for octave 3.0, fix build. * Octave/sndfile.cc Octave/PKG_ADD Add Octave function sfversion which returns the libsndfile version that the module is linked against. * Octave/Makefile.am Bunch of build and 'make distcheck' fixes. 2008-10-26 Erik de Castro Lopo * programs/common.c Return 1 if SFC_SET_BROADCAST_INFO fails. * programs/test-sndfile-metadata-set.py Update for new programs directory, exit on any error. * tests/error_test.c Fix failure behaviour in error_number_test. * src/common.h src/sndfile.c Add error number SFE_BAD_BROADCAST_INFO_SIZE. * src/* Reimplement handling of broadcast extentioon chunk in WAV/WAVEX files. * src/broadcast.c Fix generation of added coding history. 2008-10-25 Erik de Castro Lopo * programs/sndfile-metadata-get.c programs/sndfile-info.c Exit with non-zero on errors. 2008-10-21 Erik de Castro Lopo * examples/sndfile-to-text.c examples/Makefile.am Add a new example program and hook it into the build. * examples/ programs/ Add a new directory programs and move sndfile-info, sndfile-play and other real programs to the new directory, leaving example programs where they were. 2008-10-20 Erik de Castro Lopo * tests/Makefile.am Automake 1.10 MinGW cross compiling fixes. 2008-10-19 Erik de Castro Lopo * examples/sndfile-play.c Remove call to deprecated function snd_pcm_sw_params_get_xfer_align. Fix gcc-4.3 compiler warnings. * tests/command_test.c Fix a valgrind warning. * tests/error_test.c tests/multi_file_test.c tests/peak_chunk_test.c tests/pipe_test.tpl tests/stdio_test.c tests/win32_test.c Fix gcc-4.3 compiler warnings. 2008-10-17 Erik de Castro Lopo * src/broadcast.c Fix termination of desitination string in strncpy_crlf. When copying BROADCAST_INFO chunk, make sure destination gets correct line endings. * examples/common.c Fix copying of BROADCAST_INFO coding_history field. 2008-10-13 Erik de Castro Lopo * tests/command_test.c Add test function instrument_rw_test, but don't hook it into the testing yet. * src/common.h src/command.c src/sndfile.c src/flac.c Error code rationalization. * src/common.h src/sndfile.c Set psf->error to SFE_CMD_HAS_DATA when adding metadata via sf_command() fails due to psf->have_written being true. * doc/command.html Document the SFC_GET/SET_BROADCAST_INFO comamnds. 2008-10-10 Erik de Castro Lopo * tests/command_test.c Improve error reporting when '\0' is found in coding history. Fix false failure. 2008-10-09 Erik de Castro Lopo * src/broadcast.c Convert all coding history line endings to \r\n. * tests/command_test.c Add test to make sure all line endings are converted to \r\n. 2008-10-08 Erik de Castro Lopo * src/broadcast.c Changed the order of coding history fields. * tests/command_test.c Update bextch test to cope with previous change. * examples/common.c Add extra length check when copying broadcast info data. 2008-10-05 Erik de Castro Lopo * tests/utils.tpl tests/pcm_test.tpl Update check_file_hash_or_die to use 64 bit hash. * tests/checksum_test.c tests/Makefile.am Add new checksum_test specifically for lossy compression of headerless files. 2008-10-04 Erik de Castro Lopo * src/gsm610.c Seek to psf->dataoffset before decoding first block. * src/sndfile.c Fix detection of mpc2k files on big endian systems. 2008-10-03 Erik de Castro Lopo * src/broadcast.c Use '\r\n' newlines in Coding History as required by spec. 2008-10-02 Erik de Castro Lopo * src/test_conversions.c Use int64_t instead of 'long long'. 2008-10-01 Erik de Castro Lopo * examples/sndfile-metadata-set.c Remove --bext-coding-history-append command line option because it didn't really make sense. * examples/sndfile-metadata-(get|set).c Add usage messages. * examples/test-sndfile-metadata-set.py Start work on test coding history. 2008-09-30 Erik de Castro Lopo * README doc/win32.html Bring these up to date. * src/aiff.c Fix parsing of REX files. 2008-09-29 Erik de Castro Lopo * src/file_io.c Use intptr_t instead of long for return value of _get_osfhandle. * src/test_conversions.c src/test_endswap.tpl Fix printing of int64_t values. * examples/sndfile-play.c Fix win64 issues. * tests/win32_ordinal_test.c Fix calling of GetProcAddress with ordinal under win64. * tests/utils.tpl Fix win64 issues. 2008-09-25 Erik de Castro Lopo * examples/* Rename copy_data.[ch] to common.[ch]. Fix build. Move code from sndfile-metadata-set.c to common.c. * examples/Makefile.am tests/Makefile.am regtest/Makefile.am Clean paths. 2008-09-19 Erik de Castro Lopo * doc/tutorial.html doc/Makefile.am Add file doc/tutorial.html and hook into build/dist system. 2008-09-14 Erik de Castro Lopo * examples/sndfile-metadata-set.c Clean up handling of bext command line params. 2008-09-13 Erik de Castro Lopo * src/w64.c Add handling/skipping of a couple of new chunk types. 2008-09-09 Erik de Castro Lopo * configure.ac Add -funsigned-char to CFLAGS if the compiler supports it. * examples/sndfile-metadata-(get|set).c Add handling for more metadata types. 2008-09-04 Erik de Castro Lopo * src/common.h Add macros SF_CONTAINER, SF_CODEC and SF_ENDIAN useful for splitting format field of SF_INFO into component parts. * src/*.c Use new macros everywhere it is appropriate. 2008-09-02 Erik de Castro Lopo * examples/sndfile-bwf-set.c Massive reworking. 2008-08-24 Erik de Castro Lopo * examples/sndfile-bwf-set.c Add --info-auto-create-date command line option. * examples/sndfile-metadata-set.c examples/sndfile-metadata-get.c examples/Makefile.am examples/test-sndfile-bwf-set.py Rename sndfile-bwf-(set|get).c to sndfile-metadata-(set|get).c. Change command line args. 2008-08-23 Erik de Castro Lopo * src/wav.c Allow 'PAD ' chunk to be modified in RDWR mode. * src/sndfile.h.in src/sndfile.c Add handling (incomplete) for SFC_SET_ADD_HEADER_PAD_CHUNK. * tests/Makefile.am tests/write_read_test.tpl tests/header_test.tpl tests/misc_test.c Add tests for RF64. * src/rf64.c Fixes to make sure all tests pass. * tests/Makefile.am tests/string_test.c Add string tests (not yet passing). 2008-08-22 Erik de Castro Lopo * src/rf64.c First pass at writing RF64 now working. 2008-08-21 Erik de Castro Lopo * examples/sndfile-convert.c Add SF_FORMAT_RF64 to format_map. * src/common.h src/sndfile.c More RF64 support code. * examples/sndfile-bwf-set.c Fix the month number in autogenerated date string and use hypen in date instead of slash. * examples/test-sndfile-bwf-set.py Update tests. * examples/sndfile-info.c When called with -i or -b option, operate on all files on command line, not just the first. 2008-08-19 Erik de Castro Lopo * src/rf64.c New file to handle RF64 (WAV like format supportting > 4Gig files). * src/sndfile.h.in src/common.h src/sndfile.c src/Makefile.am Hook the above into build so hacking can begin. * src/pcm.c Improve log message when pcm_init fails. * src/sndfile-info.c Only calculate and print 'Signal Max' if file is less than 10 megabytes in length. 2008-08-18 Erik de Castro Lopo * tests/string_test.c Polish string_multi_set_test. * src/wav.c In RDWR mode, pad the header if necessary (ie LIST chunk has moved or length has changed). Minor fixes in wav_write_strings. Write PAD chunk with default endian-ness, not a specific endian-ness. * examples/test-sndfile-bwf-set.py Add Python script to test sndfile-bwf-set/get. * examples/sndfile-bwf-set.c Clean up and fixes. * src/wav.c Merge function wavex_write_header into wav_write_header, deleting about 70 lines of code. * src/common.h Double value of SF_MAX_STRINGS. * tests/string_test.c Add string tests for WAVEX and RIFX files. * tests/command_test.c Add broadcast test for WAVEX files. 2008-08-17 Erik de Castro Lopo * tests/string_test.c Add a new string_rdwr_test (currently failing for WAV). Add a new string_multi_set_test (currently failing). * tests/command_test.c Add new broadcast_rdwr_test (currently failing). * src/wav.c Fix to WAV parser to allow 'bext' chunk to be updated in place. In wav_write_tailer, seek to psf->dataend if its greater than zero. * src/sndfile.c Make sure psf->have_written gets set correctly in mode SFM_RDWR. * configure.ac Test for and gettimeofday. * src/common.c Use gettimeofday() to initialize psf_rand_int32. * src/common.h src/sndfile.c Add unique_id field to SF_PRIVATE struct. * src/common.h src/sndfile.c src/wav.c src/wav_w64.[ch] Move wavex_ambisonic field from SF_PRIVATE struct to WAV_PRIVATE struct. * src/common.h src/strings.c Add function psf_location_string_count. 2008-08-16 Erik de Castro Lopo * configure.ac Test for localtime and localtime_r. * examples/sndfile-convert.c In function copy_metadata(), copy broadcast info if present. * examples/copy_data.[ch] examples/Makefile.am Break some functionality out of sndfile-convert.c so it can be used in examples/sndfile-bwf-set.c. * tests/utils.tpl Add new function create_short_sndfile(). * examples/sndfile-bwf-set.c examples/sndfile-bwf-get.c examples/Makefile.am Add new files and hook into build. 2008-08-11 Erik de Castro Lopo * src/sndfile.h.in Fix comments. Patch from Mark Glines. 2008-07-30 Erik de Castro Lopo * tests/misc_test.c Use zero_data_test on Ogg/Vorbis files. * src/ogg.c Fix segfault when closing an Ogg/Vorbis file that has been opened for write but had no actual data written to it. Bug reported by Chinoy Gupta. * tests/Makefile.am Make sure to run mist_test on Ogg/Vorbis files. 2008-07-19 Erik de Castro Lopo * regtest/Makefile.am Use SQLITE3_CFLAGS to locate sqlite headers. 2008-07-10 Erik de Castro Lopo * doc/index.html doc/FAQ.html Add notes about which versions of windows libsndfile works on. 2008-07-03 Erik de Castro Lopo * tests/misc_test.c Add a test for correct handling of Ambisonic files. Thanks to Fons Adriaensen for the test. * src/wav.c src/wav_w64.c Fix handling of Ambisonic files. Thanks to Fons Adriaensen for the patch. 2008-06-29 Erik de Castro Lopo * configure.ac Fix detection/enabling of external libs. * M4/extra_pkg.m4 M4/Makefile.am Add m4 macro PKG_CHECK_MOD_VERSION which is a hacked version PKG_CHECK_MODULES. The new macro prints the version number of the package it is searching for. 2008-06-14 Erik de Castro Lopo * src/aiff.c Apply a fix from Axel Röbel where if the second loop in the instrument chunk is none, the loop mode is written into the first loop. 2008-05-31 Erik de Castro Lopo * src/test_float.c src/test_main.(c|h) src/Makefile.am Add new file to test functions float32_(le|be)_(read|write) and double64_(le|be)_(read|write). Hook into build and testsuite. * src/double64.c src/float32.c Fix bugs in functions found by test added above. Thanks to Nicolas Castagne for reporting this bug. * src/sndfile.h.in Change time_reference_(low|high) entries of SF_BROADCAST_INFO struct to unsigned. * examples/sndfile-info.c Print out the BEXT time reference in a sensible format. 2008-05-21 Erik de Castro Lopo * src/*.c Fuzz fixes. * src/ogg.c Add call to ogg_stream_clear to fix valgrind warning. * src/aiff.c Fix x86_64 compile issue. * configure.ac src/Makefile.am src/flac.c src/ogg.c Link to external versions of FLAC, Ogg and Vorbis. * tests/lossy_comp_test.c tests/ogg_test.c tests/string_test.c tests/vorbis_test.c tests/write_read_test.tpl Fix tests when configured with --disable-external-libs. * tests/external_libs_test.c tests/Makefile.am Add new test and hook into build and test suite. * src/command.c Use HAVE_EXTERNAL_LIBS to ensure that the SFC_GET_FORMAT_* commands return the right data when external libs are disabled. 2008-05-11 Erik de Castro Lopo * tests/write_read_test.tpl Add a test for extending a file during write by seeking past the current end of file. * src/sndfile.c Allow seeking past end of file during write. 2008-05-10 Erik de Castro Lopo * doc/api.html doc/command.html Move all information about the sf_command function to command.html and add a link from documentation of the sf_read/write_raw function to the SFC_RAW_NEEDS_ENDSWAP command. * doc/index.html doc/FAQ.html doc/libsndfile.css Minor documentation tweaks. 2008-05-09 Erik de Castro Lopo * configure.ac Add AM_PROG_CC_C_O. 2008-04-27 Erik de Castro Lopo * tests/error_test.c Add a test to make sure if file opened with sf_open_fd, and then the file descriptor is closed, then sf_close will return an error code. Thanks to Dave Flogeras for the bug report. * src/sndfile.c Make sf_close return an error is the file descriptor is already closed. 2008-04-19 Erik de Castro Lopo * configure.ac Set object format to aout for OS/2. Thanks to David Yeo. * src/mpc2k.c src/sndfile.c src/sndfile.h.in src/common.h src/Makefile.am Add ability to read MPC 2000 file. * tests/write_read_test.tpl tests/misc_test.c tests/header_test.tpl tests/Makefile.am Add tests for MPC 2000 file format. * examples/sndfile-convert.c Allow conversion to MPC 2000 file format. 2008-04-17 Erik de Castro Lopo * src/VORBIS/lib/codebook.c Sync from upstream SVN. * autogen.sh configure.ac Minor tweaks. 2008-04-13 Erik de Castro Lopo * src/ogg.c Add a patch that fixes finding the length in samples of an Ogg/Vorbis file. The patch as supplied segfaulted and required many hours of debugging. * src/OGG/bitwise.c Sync from upstream SVN. 2008-04-09 Erik de Castro Lopo * src/aiff.c Fix up handling of 'APPL' chunk. Thanks to Axel Röbel for bringing up this issue. 2008-04-06 Erik de Castro Lopo * tests/*.c Add calls to sf_close() where needed. * tests/utils.tpl tests/multi_file_test.c Always pass 0 as the third argument to open when OS_IS_WIN32. 2008-04-03 Erik de Castro Lopo * src/test_* Add files test_main.[ch]. Collapse all tests into a single executable. 2008-03-30 Erik de Castro Lopo * src/FLAC Sync to upstream CVS. 2008-03-25 Erik de Castro Lopo * src/common.h Make SF_MIN and SF_MAX macros MinGW friendly. * examples/sndfile-(info|play).c Use Sleep function from instead of _sleep. * tests/locale_test.c Disable some tests when OS_IS_WIN32. * src/FLAC/src/share/replaygain_anal/replaygain_analysis.c src/FLAC/src/share/utf8/utf8.c MinGW fixes. 2008-03-11 Erik de Castro Lopo * doc/FAQ.html Tweaks to pcm16 <-> float conversion answer. 2008-02-10 Erik de Castro Lopo * src/OGG Sync to SVN upstream. * Makefile.am Add 'DISTCHECK_CONFIGURE_FLAGS = --enable-gcc-werror'. 2008-02-05 Erik de Castro Lopo * examples/sndfile-jackplay.c Minor tweaks to warning message printed when compiled without libjack. 2008-01-27 Erik de Castro Lopo * tests/peak_chunk_test.c Improve read_write_peak_test to find more errors. Inspired by example provided by Nicolas Castagne. * src/aiff.c Another SFM_RDWR fix shown up by above test. 2008-01-24 Erik de Castro Lopo * src/aiff.c Fix reading of COMM encoding string. * src/chunk.c src/common.h src/Makefile.am New file for storing and retrieving info about header chunks. Hook into build. * src/aiff.c Use new chunk logging to fix problem with AIFF in RDWR mode. 2008-01-22 Erik de Castro Lopo * src/command.c Add WVE to the list of major formats. * tests/aiff_rw_test.c Fix error reporting. 2008-01-21 Erik de Castro Lopo * src/common.[ch] Add internal functions str_of_major_format, str_of_minor_format, str_of_open_mode and str_of_endianness. * tests/write_read_test.tpl Fix reporting of errors in new_rdwr_XXXX_test. 2008-01-20 Erik de Castro Lopo * examples/sndfile-play.c Apply patch from Yair K. to fix compiles with OSS v4. * src/common.h src/float32.c src/double64.c Rename psf->float_enswap to psf->data_endswap. * src/sndfile.h.in src/sndfile.c src/pcm.c Add command SFC_RAW_NEEDS_ENDSWAP. * tests/command.c Add test for SFC_RAW_NEEDS_ENDSWAP. * doc/command.html Document SFC_RAW_NEEDS_ENDSWAP. * tests/peak_chunk_test.c Add test function read_write_peak_test. Thanks to Nicolas Castagne for the bug report. 2008-01-09 Erik de Castro Lopo * examples/sndfile-cmp.c Add new example program contributed by Conrad Parker. * examples/Makefile.am Hook into build. * doc/development.html Change use or reconfigure.mk to autogen.sh. 2008-01-08 Erik de Castro Lopo * tests/win32_test.c Add another win32 test. * tests/util.tpl Add function file_length_fd which wraps fstat. * tests/Makefile.am Run the multi_file_test on AU files. * tests/multi_file_test.c Use function file_length_fd() instead of file_length() to overcome stupid win32 bug. Fscking hell Microsoft sucks so much. 2008-01-05 Erik de Castro Lopo * src/sd2.c Fix a rsrc parsing bug. Example file supplied by Uli Franke. 2007-12-28 Erik de Castro Lopo * doc/index.html Allow use of either LGPL v2.1 or LGPL v3. * tests/header_test.tpl Add header_shrink_test from Axel Röbel. * src/wav.c Add fix from Axel Röbel for writing files with float data but no peak chunk (ie peak chunk gets removed after the file is opened). * src/aiff.c tests/header_test.tpl Apply similar fix to above for AIFF files. * src/wav.c tests/header_test.tpl Apply similar fix to above for WAVEX files. * src/command.c Add Ogg/Vorbis to 'get format' commands. 2007-12-16 Erik de Castro Lopo * src/ogg.c Fix seeking on multichannel Ogg Vorbis files. Reported by Bodo. Set the default encoding quality to 0.4 instead of 4.0 (Bodo again). * tests/ogg_test.c Add stereo seek tests. 2007-12-14 Erik de Castro Lopo * tests/ogg_test.c Add a test (currently failing) for stereo seeking on Ogg Vorbis files. Test case supplied by Bodo. * tests/utils.(def|tpl) Add compare_XXX_or_die functions. 2007-12-05 Erik de Castro Lopo * src/aiff.c Fix a bug where ignoring ssnd_fmt.offset and ssnd_fmt.blocksize caused misaligned reading of 24 bit data. Thanks to Uli Franke for reporting this. 2007-12-03 Erik de Castro Lopo * src/vox_adpcm.c src/ima_oki_adpcm.[ch] src/Makefile.am Merge in code from the vox-patch branch. Thanks to Robs for the patch which fixes a long standing bug in the VOX codec. 2007-12-01 Erik de Castro Lopo * examples/sndfile-convert.c Fix handling of -override-sample-rate=X option. 2007-11-25 Erik de Castro Lopo * src/ogg.c src/VORBIS Merge in Ogg Vorbis support from John ffitch of the Csound project. 2007-11-24 Erik de Castro Lopo * src/sndfile.c Recognise files with 'vox6' extension as 6kHz OKI VOX ADPCM files. Also recognise 'vox8' as and 'vox' as 8kHz files. * configure.ac Detect libjack (JACK Audio Connect Kit). * examples/sndfile-jackplay.c examples/Makefile.am Add new example program to play sound files using the JACK audio server. Thanks to Jonatan Liljedahl for allowing this to be included. 2007-11-21 Erik de Castro Lopo * doc/index.html Update support table with SD2 and FLAC. 2007-11-17 Erik de Castro Lopo * src/sndfile.c Fix calculation of internal value psf->read_current when attempting to read past end of audio data. Remove redundant code. * tests/lossy_comp_test.c Add read_raw_test to check that raw reads do not go past the end of the audio data section. Clean up error output messages. * src/sndfile.c Add code to prevent sf_read_raw from reading past the end of the audio data. * tests/Makefile.am Add the wav_pcm lossy_comp_test. 2007-11-16 Erik de Castro Lopo * configure.ac src/Makefile.am src/create_symbols_file.py More OS/2 fixes from David Yeo. 2007-11-12 Erik de Castro Lopo * src/file_io.c tests/utils.tpl tests/benchmark.tpl Improve handling of requirements for O_BINARY as suggested by Ed Schouten. 2007-11-11 Erik de Castro Lopo * src/common.h Fix symbol class when SF_MIN is nested inside SF_MAX or vice versa. * src/create_symbols_file.py Add support for OS/2 contributed by David Yeo. 2007-11-05 Erik de Castro Lopo * M4/gcc_version.m4 Add macro AC_GCC_VERSION to detect GCC_MAJOR_VERSION and GCC_MINOR_VERSION. * configure.ac Use AC_GCC_VERSION to work around gcc-4.2 inline warning stupidity. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33995 Use -fgnu-inline to prevent stupid warnings. 2007-11-03 Erik de Castro Lopo * tests/util.tpl Increase the printing width for print_test_name(). * tests/command_test.c tests/Makefile.am Add tests for correct updating of broadcast WAV coding history. * examples/sndfilehandle.cc examples/Makefile.am Add example program using the C++ SndfileHandle class. 2007-10-29 Erik de Castro Lopo * src/common.h src/sndfile.c Add error codes SFE_ZERO_MAJOR_FORMAT and SFE_ZERO_MINOR_FORMAT. 2007-10-26 Erik de Castro Lopo * src/sd2.c Identify sample-rate/sample-size/channels by resource id. 2007-10-25 Erik de Castro Lopo * src/broadcast.c src/common.h src/sndfile.c Improvements to handling of broadcast info in WAV files. Thanks to Frederic Cornu and other for their input. 2007-10-24 Erik de Castro Lopo * src/FLAC/include/share/alloc.h Mingw fix for SIZE_T_MAX from Uli Franke. 2007-10-23 Erik de Castro Lopo * tests/open_fail_test.c tests/error_test.c tests/Makefile.am Move tests from open_fail_test.c to error_test.c and remove the former. 2007-10-22 Erik de Castro Lopo * tests/scale_clip_test.(def|tpl) Add tests for SFC_SET_INT_FLOAT_WRITE command. * doc/command.html Add docs for SFC_SET_INT_FLOAT_WRITE command. * examples/sndfile-play.c tests/dft_cmp.c Fix gcc-4.2 warning messages. 2007-10-21 Erik de Castro Lopo * src/sndfile.h.in src/sndfile.c Add command SFC_GET_CURRENT_SF_INFO. * src/sndfile.h.in src/sndfile.c src/create_symbols_file.py Remove function sf_get_info (only ever in pre-release code). * tests/command_test.c Add test for SFC_GET_CURRENT_SF_INFO. 2007-10-15 Erik de Castro Lopo * src/wav.c Add parsing of 'exif' chunks. Originally coded by Trent Apted. * configure.ac Put config stuff in Cfg directory. Remove check for inttypes.h. 2007-10-10 Erik de Castro Lopo * src/w64.c Fix writing of 'riff' chunk length and check for correct value in parser. 2007-09-20 Erik de Castro Lopo * doc/index.html Link to MP3 FAQ entry. 2007-09-18 Erik de Castro Lopo * src/flac.c Move the blocksize check to an earlier stage of flac_buffer_copy. 2007-09-12 Erik de Castro Lopo * src/FLAC Huge merge from FLAC upstream. 2007-09-10 Erik de Castro Lopo * examples/*.c Change license to all example programs to BSD. 2007-09-08 Erik de Castro Lopo * src/FLAC/include/FLAC/metadata.h Include to prevent compile error on OSX. * Octave/octave_test.sh Disable test on OSX. Can't get it to work. * src/flac.c Check the blocksize returned from the FLAC decoder to prevent buffer overruns. Reported by Jeremy Friesner. Thanks. 2007-09-07 Erik de Castro Lopo * Makefile.am M4/octave.m4 Fix build when Octave headers are not present. 2007-08-27 Erik de Castro Lopo * doc/development.html Add note about bzr repository directory looking empty. 2007-08-26 Erik de Castro Lopo * configure.ac Octave/* M4/octave_* Bunch of changes to add ability to build GNU Octave modules to read/write sound files using libsndfile from Octave. 2007-08-23 Erik de Castro Lopo * acinclude.m4 configure.ac ... Get rid of acinclude.m4 and replace it with an M4 directory. 2007-08-21 Erik de Castro Lopo * src/sndfile.h.in Remove crufty Metrowerks compiler support. Allow header file to be compiled on windows with both GCC and microsoft compiler. 2007-08-19 Erik de Castro Lopo * tests/dft_cmp.[ch] tests/floating_point_test.tpl Clean up floating point tests. 2007-08-14 Erik de Castro Lopo * src/aiff.c Fix segfault when COMM chunk length is byte swapped. 2007-08-09 Erik de Castro Lopo * src/common.h src/mat4.c src/mat5.c src/sndfile.c Add a generic SFE_CHANNEL_COUNT_ZERO error, remove format specific errors. * src/au.c Fix crash on AU files with zero channel count. Reported by Ben Alison. 2007-08-08 Erik de Castro Lopo * src/voc.c Fix bug in handling file supplied by Matt Olenik. 2007-07-31 Erik de Castro Lopo * src/OGG Merge from OGG upstream sources. 2007-07-25 Erik de Castro Lopo * src/FLAC Merge from FLAC upstream sources. 2007-07-15 Erik de Castro Lopo * src/flac.c Fix memory leak; set copy parameter to FALSE in call to FLAC__metadata_object_vorbiscomment_append_comment. * src/common.[ch] Add function psf_rand_int32(). 2007-07-14 Erik de Castro Lopo * src/FLAC Merge from FLAC upstream sources. * src/strings.c tests/string_test.c tests/Makefile.am Make sure string tests for SF_STR_LICENSE actually works. 2007-07-13 Erik de Castro Lopo * tests/string_test.c Add ability to test strings stored in metadata secion of FLAC files. * src/string.c Fix logic for testing if audio data has been written and string is added. Make sure SF_STR_ALBUM actually works. * src/flac.c Finalize reading/writing string metadata. Tests pass. * src/sndfile.h.in tests/string_test.c src/flac.c Add string type SF_STR_LICENSE, update test and use for FLAC files. * src/sndfile.h.in Add definition for SFC_SET_SCALE_FLOAT_INT_WRITE command. * src/common.h src/double64.c src/float32.c src/sndfile.c Add support for SFC_SET_SCALE_FLOAT_INT_WRITE (still needs testing). 2007-07-12 Erik de Castro Lopo * src/flac.c Apply patch from Ed Schouten to read artist and title metadata from FLAC files. Improve reporting of FLAC metadata. * src/sndfile.h.in tests/string_test.c src/flac.c Add string type SF_STR_ALBUM, update test and use for FLAC files. 2007-06-28 Erik de Castro Lopo * src/FLAC/* Merge from upstream CVS. 2007-06-16 Erik de Castro Lopo * src/FLAC/* Update from upstream CVS. 2007-06-14 Erik de Castro Lopo * tests/cpp_test.cc Add extra tests for when the SndfileHandle constructor fails. * src/sndfile.hh Make sure failure to open the file in the constructor does not allow later calls to other methods to fail. 2007-06-10 Erik de Castro Lopo * tests/util.tpl Add function write_mono_file. * tests/generate.[ch] tests/Makefile.am Add files generate.[ch] and hook into build. * tests/write_read_test.tpl Add multi_seek_test. * src/flac.c Fix buffer overflow bug. Test provided by Jeremy Friesner and fix provided by David Viens. 2007-06-07 Erik de Castro Lopo * doc/FAQ.html Minor update. * configure.ac src/FLAC/src/libFLAC/ia32/Makefile.am src/Makefile.am Apply patch from Trent Apted make it compile on Intel MacOSX. Thanks Trent. 2007-05-28 Erik de Castro Lopo * src/wav.c Fix writing of MSGUID subtypes. Thanks to Bruce Sharpe. 2007-05-22 Erik de Castro Lopo * src/wav.c Fix array indexing bug raised by Bruce Sharpe. 2007-05-12 Erik de Castro Lopo * src/FLAC/src/share/getopt/getopt.c Fix Mac OSX / PowerPC compile warnings. * configure.ac Make sure WORDS_BIGENDIAN gets correctly defined for FLAC code. 2007-05-04 Erik de Castro Lopo * doc/FAQ.html Add Q/A about MP3 support. 2007-05-03 Erik de Castro Lopo * doc/new_file_type.HOWTO Minor updates. 2007-05-02 Erik de Castro Lopo * src/wve.c Fix a couple bad parameters with psf_log_printf. * src/pcm.c Improve error reporting. * src/common.h src/common.c Constify psf_hexdump. 2007-04-30 Erik de Castro Lopo * src/FLAC Ditch and re-import required FLAC code. * configure.ac Force FLAC__HAS_OGG variable to 1. * src/FLAC/src/libFLAC/stream_encoder.c Fix compiler warnings. 2007-04-23 Erik de Castro Lopo * configure.ac tests/win32_ordinal_test.c Detect if win32 DLL is beging generated and only run win32_ordinal_test if true. * src/G72x/Makefile.am src/Makefile.am Use $(EXEEXT) where possible. 2007-04-18 Erik de Castro Lopo * src/wve.c src/common.h src/sndfile.c Complete definition of SfE_WVE_NO_WVE error message. * src/wve.c Fix error in files generated on big endian systems. Robustify parsing. 2007-04-16 Erik de Castro Lopo * src/double64.c Fix clipping of double to short conversions on 64 bit systems. * src/flac.c regtest/database.c tests/cpp_test.cc Fix compile warnings for 64 bit systems. 2007-04-15 Erik de Castro Lopo * src/wav.c src/wav_w64.c Use audio detect function when 'fmt ' chunk data is suspicious. * configure.ac Add ugly hack to remove -Werror from some Makefiles. 2007-04-14 Erik de Castro Lopo * src/GSM610/long_term.c src/macbinary3.c tests/cpp_test.cc Add patch from André Pang to clean up compiles on OSX. * src/wve.c src/common.h src/sndfile.c src/sndfile.h.in examples/sndfile-convert.c Merge changes from Reuben Thomas to improve WVE support. * tests/lossy_comp_test.c tests/Makefile.am Add tests for WVE files. 2007-04-11 Erik de Castro Lopo * src/sndfile.hh Add a static SndfileHandle::formatCheck method as suggested by Jorge Jiménez. 2007-04-09 Erik de Castro Lopo * src/sndfile.c Fixed a bug in sf_error() where the function itself was being compared against zero. Add a check for a NULL return from peak_info_calloc. Fix a possible NULL dereference. 2007-04-07 Erik de Castro Lopo * src/flac.c Turn off seekable flag when writing, return SFE_BAD_RDWR_FORMAT when opening file for RDWR. * src/sndfile.c Improve error message for SFE_BAD_RDWR_FORMAT. * src/mat4.c Fix array indexing issue. Thanks to Ben Allison (Nullsoft) for alerting me. 2007-03-05 Erik de Castro Lopo * doc/FAQ.html Add Q/A 19 on project files. 2007-03-01 Erik de Castro Lopo * src/sndfile.c Guard agains MacOSX universal binary compiles. * doc/FAQ.html Add Q/A 18 and clean up Q3. 2007-02-22 Erik de Castro Lopo * src/aiff.c Add support for 'in24' files. 2007-02-13 Erik de Castro Lopo * src/wav.c src/wav_w64.c src/wav_w64.h Start work towards detecting ausio codec type from the actual audio data. * src/audio_detect.c src/test_audio_detect.c Add new file and its unit test. 2007-02-07 Erik de Castro Lopo * examples/cooledit-fixer.c examples/Makefile.am Remove old broken example program. 2007-02-06 Erik de Castro Lopo * src/sndfile.c src/sndfile.h.in src/create_symbols_file.py Add function sf_get_info. 2007-01-25 Erik de Castro Lopo * examples/sndfile-play.c For ALSA, use the 'default' device instead of 'plughw:0'. 2007-01-22 Erik de Castro Lopo * src/sndfile.c Allow writing of WAV/WAVEX 'BEXT' chunks in SFM_RDWR mode. 2007-01-21 Erik de Castro Lopo * doc/development.html doc/embedded_files.html man/sndfile-play.1 Minor documentation fixes. Thanks Reuben Thomas. 2006-12-16 Erik de Castro Lopo * examples/sndfile-convert.c Add -override-sample-rate command line option. 2006-11-19 Erik de Castro Lopo * tests/misc_test.c Force errno to zero at start of some tests. * src/sndfile.c Minor clean up of error handling. * configure.ac Remove an assembler test which was failing on OSX. 2006-11-15 Erik de Castro Lopo * src/common.h Fix the definition of SF_PLATFORM_S64 for MinGW. * src/FLAC/Makefile.am src/FLAC/share/grabbag/Makefile.am Fix path problems for MinGW. 2006-11-13 Erik de Castro Lopo * src/sfendian.h Add include guard. * src/Makefile.am src/flac.c Clean up include paths. * src/test_conversions.c New file to test psf_binheader_readf/writef functions. * src/Makefile.am src/test_file_io.c src/test_log_printf.c src/common.c Clean up unit testing. * src/common.c Fix a bug reading/writing 64 bit header fields. Thanks to Jonathan Woithe for reporting this. * src/test_conversions.c Complete unit test for above fix. 2006-11-11 Erik de Castro Lopo * src/sndfile.c More refactoring to clean up psf_open_file() and vairous sf_open() functions. 2006-11-09 Erik de Castro Lopo * src/wav.c Apply a patch from Jonathan Woithe to allow opening of (malformed) WAV files of over 4 gigabytes. 2006-11-05 Erik de Castro Lopo * src/sndfile.c Refactor function psf_open_file() to provide a single return point. * tests/misc_test.c Fix permission_test to ensure that read only file can be created. 2006-11-03 Erik de Castro Lopo * src/common.h Add SF_PLATFORM_S64 macro as a platform independant way of doing signed 64 bit integers. * src/aiff.c src/svx.c src/wav.c Add warning in log if files are larger than 4 gigabytes in size. 2006-11-01 Erik de Castro Lopo * src/FLAC src/OGG confgure.ac src/Makefile.am Pull in all required FLAC and OGG code so external libraries are not needed. This makes compiling on stupid fscking Windoze easier. 2006-10-27 Erik de Castro Lopo * src/sd2.c Add workaround for switched sample rate and sample size. * src/wav.c Add workaround for excessively long coding history in the 'bext' chunk. 2006-10-23 Erik de Castro Lopo * src/sndfile.h.in src/sndfile.c src/wav.c doc/command.html Use SF_AMBISONIC_* instead of SF_TRUE/SF_FALSE. 2006-10-22 Erik de Castro Lopo * src/sndfile.h.in src/wav.c src/wav_w64.c src/common.h doc/command.html Apply a patch from Fons Adriaensen to allow writing on WAVEX Ambisonic files. Still needs a little tweaking before its ready for release. * src/*.c Use the UNUSED macro to prevent compiler warnings. 2006-10-19 Erik de Castro Lopo * src/aiff.c Fix a bug in parsing AIFF files with a slightly unusual 'basc' chunk. Thanks to David Viens for providing two example files. * src/common.(c|h) src/aiff.c Add a function psf_sanitize_string and use it in aiff.c. 2006-10-18 Erik de Castro Lopo * src/wav_w64.c Apply a patch from Fons Adriaensen which fixes a minor WAVEX GUID issue. 2006-10-17 Erik de Castro Lopo * src/Makefile.am Fix problem related to recent test coverage changes. 2006-10-15 Erik de Castro Lopo * configure.ac tests/Makefile.am Add --enable-test-coverage configure option. 2006-10-05 Erik de Castro Lopo * src/sndfile.hh Add an std::string SndfileHandle constructor. * tests/scale_clip_test.tpl Fix the 'make distcheck' target. 2006-10-03 Erik de Castro Lopo * src/double64.c src/float32.c Add optional clipping on float file data to int read data conversions. * tests/tests/scale_clip_test.(def|tpl) Add test for above new code. 2006-09-06 Erik de Castro Lopo * tests/aiff_rw_test.c Add 'MARK' chunks to make sure they are parsed correctly. 2006-09-05 Erik de Castro Lopo * src/aiff.c Fix parsing of MARK chunks. Many thanks to Sciss for generating files to help debug the problem. 2006-09-02 Erik de Castro Lopo * src/common.h Make the SF_MIN and SF_MAX macros at least partially type safe. * tests/lossy_comp_test.c Fix overflow problems when ensuring that signalis not zero. 2006-08-31 Erik de Castro Lopo * configure.ac docs/*.html Changes for release 1.0.17. 2006-08-08 Erik de Castro Lopo * src/flac.c Remove inline from functions called by pointer. Thanks to Sampo Savolainen for notifying me of this. 2006-07-31 Erik de Castro Lopo * src/sndfile.hh Add writeSync method. Add copy constructor and assignment operator (thanks Daniel Schmitt). Add methods readRaw and writeRaw. Make read/write/readf/writef simple overlaods instead of templates (thanks to Trent Apted for suggesting this). * tests/cpp_test.cc Cleanup. Add tests. 2006-07-30 Erik de Castro Lopo * src/sndfile.hh Templatize the read/write/readf/writef methods as suggested by Lars Luthman. Prevent the potential leak of SNDFILE* pointers in the openRead/openWrite/ openReadWrite methods. Add const to SF_INFO pointer in Sndfile constructor. Make the destrictor call the close() method. * tests/cpp_test.cc Add more tests. 2006-07-29 Erik de Castro Lopo * tests/cpp_test.cc Remove the generated file so "make distcheck" passes. * src/Makefile.am Add sndfile.hh to distributed header files. * src/sndfile.hh Change the license for the C++ wrapper to modified BSD. 2006-07-28 Erik de Castro Lopo * src/sndfile.hh Complete it. * tests/cpp_test.cc Add more tests. 2006-07-27 Erik de Castro Lopo * tests/utils.tpl Add extern C to generated header file. * src/sndfile.hh Work towards completing this. * tests/cpp_test.cc tests/Makefile.am Add a C++ test and hook into build. * configure.ac Add appropriate CXXFLAGS. 2006-07-26 Erik de Castro Lopo * configure.ac Test if compiler supports -Wpointer-arith. * src/common.c Fix a warning resulting from -Wpointer-arith. 2006-07-15 Erik de Castro Lopo * examples/sndfile-play.c Explicitly set endian-ness as well as setting 16 bit output. * examples/sndfile-info.c Make sure to parse info if file fails to open. * src/sndfile.c Handle parse error a little better. * src/wav_w64.[ch] Minor clean up, add detection of IPP ITU G723.1. 2006-06-23 Erik de Castro Lopo * src/sndfile.c Make sure psf->dataoffset gets reset to zero when openning headersless files based on the file name extension. 2006-06-21 Erik de Castro Lopo * tests/(command|lossy_comp|pcm|scale_clip)_test.c tests/fix_this.c tests/write_read_test.(tpl|def) Fix gcc-4.1 compiler warnings about "dereferencing type-punned pointer will break strict-aliasing rules". * examples/cooledit-fixer.c More fixes like above. 2006-06-20 Erik de Castro Lopo * src/file_io.c Fix a windows bug where the syserr string of SF_PRIVATE was not being set correctly. * src/sndfile.c Fixed a logic bug in sf_seek(). Thanks to Paul Davis for finding this. 2006-06-04 Erik de Castro Lopo * configure.ac Fixed detection of S_IRGRP. 2006-05-30 Erik de Castro Lopo * sndfile-convert.c Add conversion SF_INSTRUMENT data when present. 2006-05-22 Erik de Castro Lopo * doc/development.html Removed references to tla on windows. * src/common.h src/sndfile.c Add separate void pointers for file containter and file codec data to SF_PRIVATE struct. Still need to move all existing fdata pointers. * tests/write_read_test.tpl Change the order of some tests. * src/aiff.c When writing 'AIFC' files, make sure get an 'FVER' gets added. * src/common.h src/(dwvw|flac|g72x|gsm610|ima_adpcm|ms_adpcm|paf|sds).c src/(sndfile|voc|vox_adpcm|xi).c Remove fdata field from SF_PRIVATE struct and replace it with codec_data. 2006-05-10 Erik de Castro Lopo * Win32/testprog.c Win32/Makefile.am Add a minimal win32 test program. * Win32/README-precompiled-dll.txt Mingw-make-dist.sh Update readme and Mingw build script. 2006-05-09 Erik de Castro Lopo * configure.ac acinclude.m4 Minor fixes for Solaris. 2006-05-05 Erik de Castro Lopo * src/test_endswap.(def|tpl) Fix printf formatting for int64_t on 64 bit machines. 2006-05-04 Erik de Castro Lopo * src/binhead_check.py New file to check for bad parameters passed to psf_binheader_writef(). * src/Makefile.am Hook into test suite. * src/voc.c src/caf.c src/wav.c src/mat5.c src/mat4.c Fix bugs found by new test program. * src/double64.c Clean up double64_get_capability(). 2006-05-03 Erik de Castro Lopo * src/wav_w64.c Fix a bug on x86_64 where an int was being passed via stdargs and being read using size_t which is 64 bits. Thenks to John ffitch for giving me a login on his box. 2006-05-02 Erik de Castro Lopo * src/caf.c src/double64.c examples/sndfile-info.c tests/virtual_io_test.c tests/utils.tpl Fix a couple of signed/unsigned problems. 2006-05-01 Erik de Castro Lopo * tests/command_test.c Add channel map tests. * src/common.h src/sndfile.c Add a pointer to the SF_PRIVATE struct and make sure it gets freed in sf_close(). 2006-04-30 Erik de Castro Lopo * configure.ac doc/(command|index|api).html NEWS README Updates for 1.0.16 release. * src/sndfile.h.in Define enums for channel mapping. * examples/sndfile-info.c Clean up usage of SF_INFO struct. 2006-04-29 Erik de Castro Lopo * tests/util.tpl Add function testing function exit_if_true(). * tests/floating_point_test.tpl Fix a problem where the test program was not exiting when the test failed. 2006-04-15 Erik de Castro Lopo * src/sndfile.h.in src/sndfile.c src/common.h src/command.c Implement new commands SFC_GET_SIGNAL_MAX and SFC_GET_MAX_ALL_CHANNELS. * doc/commands.html Document new commands. Other minor updates. * tests/peak_chunk_test.c Update tests for new commands. 2006-04-02 Erik de Castro Lopo * tests/peak_chunk_test.c Add test for RIFX and WAVEX files. Try and confuse the PEAK chunk writing by enabling and disabling it. * src/sndfile.c Fix a bug where enabling and disabling PEAK chunk was screwing up. 2006-03-31 Erik de Castro Lopo * src/sndfile.h.in Add the block of 190 reserved bytes into this struct to allow for future expansion. * src/wav.c src/sndfile.c src/broadcast.c Significant cleanup of broadcast wave stuff. * examples/sndfile-info.c Fix print message. * tests/command_test.c tests/Makefile.am Complete bext tests, hook test in test suite. 2006-03-30 Erik de Castro Lopo * src/sndfile.h.in Make coding_history field of SF_BROADCAST_INFO struct a char array instead of a char pointer. * src/sndfile.c src/common.h src/wav.c Clean up knock on effects of above chnage. * examples/sndfile-info.c Add -b command line option to usage message. Clean up output of broadcast wave info. * src/wav.c Ignore and skip the 'levl' chunk. 2006-03-26 Erik de Castro Lopo * configure.ac Fix handling of --enable and --disable configure args. Thanks to Diego 'Flameeyes' Pettenò who sent the patch. 2006-03-22 Erik de Castro Lopo * doc/win32.html Make it really clear that although the MSVC++ cannot compile libsndfile, the precompiled DLL can be used in C++ programs compiled with MSVC++. 2006-03-18 Erik de Castro Lopo * src/aiff.c Fix bug in writing of INST chunk in AIFF files. Fix potential bug in writing MARK chunks. * src/sndfile.c Make sure the instrument chunk can only be written at the start of the file. * tests/command_test.c Add check of log buffer. * tests/utils.tpl Add usage of space character to psf_binheader_writef. 2006-03-17 Erik de Castro Lopo * src/Makefile.am tests/Makefile.am Remove --source-time argument from autogen command lines. * src/broadcast.c New file for EBU Broadcast chunk in WAV files. * src/sndfile.c src/sndfile.h.in src/wav.c src/common.h Add patch from Paul Davis implementing read/write of the BEXT chunk. 2006-03-16 Erik de Castro Lopo * Win32/README-precompiled-dll.txt New file descibing how to use the precompiled DLL. * Win32/Makefile.am Add Win32/README-precompiled-dll.txt to EXTRA_DIST files. * configure.ac Bump version to 1.0.15. 2006-03-11 Erik de Castro Lopo * src/wav.c On read, only add the endian flag if the file is big endian. * src/ms_adpcm.c Fixed writing of APDCM coeffs in RIFX files. * tests/write_read_test.tpl tests/lossy_comp_test.c Add tests for RIFX files. 2006-03-10 Erik de Castro Lopo * Mingw-make-dist.sh Bunch of improvements. * doc/win32.html Update MinGW program versions. 2006-03-09 Erik de Castro Lopo * src/create_symbols_file.py Fix the library name in created win32 DEF file. Add correct DLL name for Cygwin DLL. * Win32/Makefile.am tests/Makefile.am Remove redundant files, add win32_ordinal_test to test suite. * tests/win32_ordinal_test.c Update to do test in cygsndfile-1.dll as well. * doc/win32.html Fix typo, mention that -mno-cygwin with the Cygwin compiler does not work. * src/wav.c src/wav_w64.c src/sndfile.c src/sndfile.h.in Apply large patch from Jesse Chappell which adds support for RIFX files. 2006-03-08 Erik de Castro Lopo * Makefile.am Add Mingw-make-dist.sh to the extra dist files. * configure.ac Fix setting SHLIB_VERSION_ARG for MinGW. * tests/win32_ordinal_test.c New test program to test that the win32 DLL ordinals agree with the DEF file. 2006-03-04 Erik de Castro Lopo * src/common.h Add a static inline function to convert an int to a size_t. This will be a compile to nothing on 32 bit CPUs and a sign extension on 64 bit CPUs. * src/aiff.c src/avr.c src/common.c src/xi.c src/gsm610.c Fix an ia64 problem where a varargs function was being passed an int in some places and a size_t in other places. * src/sd2.c Add a workaround for situations where OSX seems to add an extra 0x52 bytes to the start of the resource fork. 2006-02-19 Erik de Castro Lopo * Mingw-make-dist.sh Add a shell script to build the windows binary/source ZIP file. * doc/index.html Add download link for windows binary/source ZIP file. Add links for GPG signatures. * doc/win32.html Remove info about building using microsoft compiler. * configure.ac Bump version to 1.0.14. 2006-02-11 Erik de Castro Lopo * src/sd2.c Improve logging of errors in resource fork parser. 2006-01-31 Erik de Castro Lopo * Win32/Makefile.msvc Replace au_g72x.* with g72x.*. Thanks to ussell Borogove. 2006-01-29 Erik de Castro Lopo * src/common.c Make sure return values are initialised header buffer is full. * src/wav.c Add workarounds for messed up WAV files. 2006-01-21 Erik de Castro Lopo * Win32/config.h Undef HAVE_INTTYPES_H for win32. * tests/command_test.c Don't exit on error in instrument test for XI files. * configure.ac Bump version to 1.0.13. * doc/*.html NEWS README Update version numbers. 2006-01-19 Erik de Castro Lopo * src/xi.c Start work on add read/write of instrument chunks. * src/command_test.c Add tests for XI instrument chunk. * tests/largefile_test.c tests/Makefile.am Add new test and hook it into the build system. This test will not be run automatically because it requires 3 Gig of disk space and takes 3 minutes to run. 2006-01-10 Erik de Castro Lopo * examples/sndfile-play.c Fix calculation of samples remaining in win32 code. Thanks Axel Röbel. * src/common.h Make sure length of header buffer can hold header plus strings. Thanks Axel Röbel. 2006-01-09 Erik de Castro Lopo * src/sndfile.h.in src/aiff.c src/wav.c Apply a patch from John ffitch (Csound project). Add detune field to SF_INSTRUMENT struct. Add reading/writing instrument chunks to WAV files. * tests/command_test.c Update SF_INSTRUMENT tests. * tests/Makefile.am Hook instrument tests into test suite. 2006-01-05 Erik de Castro Lopo * configure.ac Check for because some broken systems (like Solaris) don't have which is the 1999 ISO C standard file containing int64_t. * src/sfendian.h src/common.h Use if is not available. 2005-12-30 Erik de Castro Lopo * tests/peak_chunk_test.c Extend and clean up tests. * src/sndfile.c Fix a bug that prevented the turning off of PEAK chunks. 2005-12-29 Erik de Castro Lopo * tests/error_test.c Make the test distclean correct. * src/file_io.c Fix an SD2 MacOSX bug (reported by vince schwarzinger). 2005-12-28 Erik de Castro Lopo * src/aiff.c tests/command_test.c Apply a big patch from John ffitch (Csound project) to add reading and writing of instrument chunks to AIFF files. Also update the test. 2005-12-10 Erik de Castro Lopo * tests/aiff_rw_test.c tests/virtual_io_test.c tests/utils.tpl Move test function dump_data_to_file() to utils.tpl. * tests/error_test.c tests/Makefile.am Updates, including a new test to test that sf_error() returns a valid error number. 2005-12-07 Erik de Castro Lopo * examples/list_formats.c Make sure the SF_INFO struct is memset to all zero before being used. Thanks to Stephen F. Booth. * src/sndfile.c Make the return value of sf_error() match the API documentation. 2005-11-19 Erik de Castro Lopo * examples/sndfile-convert.c Allow conversion to raw gsm610. * src/common.h src/sndfile.c src/au.c Remove au_nh_open() and all references to it (wasn't working anyway). * tests/headerless_test.c Add new test for file extension based detection. * src/sndfile.c Rejig file extension based file type detection. 2005-11-16 Erik de Castro Lopo * src/sndfile.c Add "gsm" as a recognised file extension when no magic number can be found. * tests/lossy_comp_test.c tests/Makefile.am Test headerless GSM610. 2005-11-13 Erik de Castro Lopo * doc/api.html Fix a minor typo and a minor error. Thanks Christoph Kobe and John Pavel. 2005-10-30 Erik de Castro Lopo * src/wav_w64.c Add more reporting of 'fmt ' chunk for G721 encoded files. * src/wav.c Gernerate a more correct 20 byte 'fmt ' chunk rather than a 16 byte one. 2005-10-29 Erik de Castro Lopo * src/G72x/g72x.[ch] Minor cleanup of interface. 2005-10-28 Erik de Castro Lopo * src/ogg.c Removed the horribly broken and non-functional OGG implementation when --enable-experimental was enabled. When OGG does finally work it will be merged. * src/caf.c Fix a memory leak. 2005-10-27 Erik de Castro Lopo * src/g72x.c src/G72x/*.(c|h) src/common.h src/sndfile.c src/wav.c src/au.c Add support for G721 encoded WAV files. * doc/index.html Update support matrix. * tests/lossy_comp_test.c For file formats that support it, add string data after the audio data and make sure it isn't treated as audio data on read. * src/gsm610.c Add code to ensure that the container close function (ie for WAV files) gets called after the codec's close function. This allows GSM610 encoded WAV files to have string data following the audio data. Add an AIFF specific check on psf->datalength. * src/wav.c Simplify wav_close function. * src/aiff.c Make sure the tailer data gets written at an even file offset. Pad if necessary. * src/common.h Replace the close function pointer in SF_PRIVATE with separate functions codec_close and container_close. The former is always called first. * src/*.c Fix knock on effects of above. 2005-10-26 Erik de Castro Lopo * examples/sndfile-info.c Complete dumping SF_INSTRUMENT data. * src/dwvw.c src/ima_adpcm.c src/gsm610.c src/ms_adpcm.c Add extra checks in *_init function. * tests/lossy_comp_test.c Add a string comment to the end of the files to make sure that the decoder doesn't decode beyond the end of the audio data section. 2005-10-25 Erik de Castro Lopo * examples/sndfile-info.c Minor code cleanup. Start work on dumping SF_INSTRUMENT data. 2005-10-23 Erik de Castro Lopo * src/sndfile.h.in src/common.h src/common.c Update definition of SF_INSTRUMENT struct and create a function to allocate and initialize the struct (input from David Viens). Clean up definition of SF_INSTRUMENT struct. * src/wav.c src/wav_w64.c Add support for Ambisoncs B WAVEX files (David Viens). * src/aiff.c src/wav.c src/wav_w64.c Start work on reading/writing the SF_INSTRUMENT data. * src/sndfile.c Add code to get and set SF_INSTRUMENT data. * tests/command_test.* tests/Makefile.am Add test for set and getof SF_INSTRUMENT data. The file command_test.c is no longer autogen generated. 2005-10-15 Erik de Castro Lopo * src/gsm610.c Minor cleanup. 2005-10-14 Erik de Castro Lopo * tests/lossy_comp_test.c Minor cleanup. 2005-10-13 Erik de Castro Lopo * src/*.c Ensure sfconfig.h is included before any other header file. * src/file_io.c Add comments documenting the three sections of the file. * src/gsm610.c Make sure SF_FORMAT_WAVEX are handled correctly. 2005-10-07 Erik de Castro Lopo * configure.ac Add options to allow disabling of FLAC and ALSA. Suggested by Ben Greear. 2005-09-30 Erik de Castro Lopo * tests/locale_test.c Modify the way the unicode strings were encoded so that older compilers do not complain. Thanks Axel Röbel. * configure.ac Bump the version to 1.0.12 for release. * NEWS README Win32/config.h doc/(FAQ|index.html|command|api).html Update version numbers. 2005-09-26 Erik de Castro Lopo * src/flac.c Fix valgrind error and minor cleanup. 2005-09-25 Erik de Castro Lopo * src/(au|paf|aiff|w64|wav|svx).c Make sure structs are initialised. 2005-09-24 Erik de Castro Lopo * configure.ac Make -Wdeclaration-after-statement work with --enable-gcc-werror configure option. Add -std=gnu99 (C99 plus posix style stuff like gmtime_r) to CFLAGS if the compiler supports it. 2005-09-23 Erik de Castro Lopo * configure.ac acinclude.m4 Add -Wdeclaration-after-statement to CFLAGS if the compilers supports it. 2005-09-22 Erik de Castro Lopo * tests/util.(tpl|def) Make the test_write_*_or_die() functions const safe. 2005-09-21 Erik de Castro Lopo * src/nist.c Make sure the data offset is read from the file header. Thanks to David A. van Leeuwen for a patch. 2005-09-20 Erik de Castro Lopo * configure.ac src/sfconfig.h Check for and the function setlocale(). Set config variables to zero if not found. * tests/locale_test.c tests/Makefile.am Add new test program and hook into build/test system. 2005-09-18 Erik de Castro Lopo * src/common.h src/file_io.c On windows, use windows specific types for file handles. Add functions psf_init_files() and psf_use_rsrc(). * src/sd2.c Make resource fork handling independant of file desciptor/handles. * src/sndfile.c src/test_file_io.c Fix knock on effects. 2005-09-06 Erik de Castro Lopo * src/float_cast.h The lrint and lrintf implementations in Cygwin are both buggy and slow. Add replacements which were pulled from the Public Domain MinGW math.h header file. 2005-09-05 Erik de Castro Lopo * tests/(lossy_comp_test|virtual_io_test).c More Valgrind fixups. * configure.ac Simplify and correct configuring for Cygwin. * Win32/config.h Win32/sndfile.h Win32/Makefile.msvc Update build for MSVC. 2005-09-04 Erik de Castro Lopo * tests/lossy_comp_test.c Make sure to close SNDFILE when exiting test when file format is not seekable. * tests/(aiff_rw_test|virtual_io_test).c Do a few valgrind fix ups. 2005-09-03 Erik de Castro Lopo * src/float32.c src/double64.c Replace floating point equality comparisons with greater/less comparisons. Found by John Pavel using the Intel compiler. * src/sfconfig.h New file to clean up issues surrounding autoconf generated preprocessor symbols. * src/*.(c|h) tests/*.(c|tpl) examples/*.c Fixed a bunch of other stuff found by John Pavel using the Intel compiler. * src/file_io.c Remove Mac OS9 Metrowerks compiler specific hacks. 2005-08-31 Erik de Castro Lopo * src/w64.c Cast integer literal to sf_count_t in call to psf_binheader_writef() to prevent Valgrind error. 2005-08-30 Erik de Castro Lopo * doc/command.html Improve documentation of SF_GET_FORMAT_SUBTYPE. 2005-08-26 Erik de Castro Lopo * examples/sndfile-convert.c Allow files to be converted to SD2 format. * src/sd2.c Fix a bug in reading and writing of SD2 files on little endian CPUs. Thanks to Matthew Willis for finding this. 2005-08-25 Erik de Castro Lopo * doc/api.html Update Note2 to point to SFC_SET_SCALE_FLOAT_INT_READ. 2005-08-16 Erik de Castro Lopo * configure.ac Use $host_os instead of $target_os (thanks to Mo De Jong). 2005-08-15 Erik de Castro Lopo * src/Makefile.am Apply a patch from Mo DeJong to allow building outside of the source dir. * src/file_io.c Fix psf_fsync() for win32. * src/wav.c src/wav_w64.(c|h) Move some code from wav.c to wav_w64.c to improve the log output of files of type WAVE_FORMAT_EXTENSIBLE. 2005-08-10 Erik de Castro Lopo * src/create_symbols_file.py Make sure sf_write_fsync is an exported symbol. * examples/sndfile-convert.c Add support for writing VOX adpcm files. 2005-07-31 Erik de Castro Lopo * doc/api.html Document the new function sf_write_sync(). * doc/FAQ.html Do you plan to support XYZ codec. 2005-07-28 Erik de Castro Lopo * src/sndfile.h.in src/sndfile.c Add function sf_write_sync() to the API. * src/common.h src/file_io.c Low level implementation (win32 not done yet). * tests/write_read_test.tpl Use the new function in the tests. 2005-07-24 Erik de Castro Lopo * src/common.h src/double64.c src/float32.c src/sndfile.c Change the way PEAK chunk info is stored. Peaks now stored as an sf_count_t for position and a double as the value. * src/aiff.c src/caf.c src/wav.c Fix knock on effects of above changes. * src/caf.c Implement 'peak' chunk for file wuth data in SF_FORMAT_FLOAT or SF_FORMAT_DOUBLE format. 2005-07-23 Erik de Castro Lopo * src/nist.c Fix a bug where a variable was being used without being initialized. * src/flac.c Add extra debug in sf_flac_meta_callback. Make a bunch of private functions static. * src/aiff.c src/wav.c Fix allocation for PEAK_CHUNK (bug found using valgrind). 2005-07-21 Erik de Castro Lopo * src/common.h Move the peak_loc field of SF_PRIVATE to the PEAK_CHUNK struct. Remove had_peak field of SF_PRIVATE, use pchunk != NULL instead. Rename PEAK_CHUNK and PEAK_POS to PEAK_CHUNK_32 and PEAK_POS_32. * src/aiff.c src/caf.c src/wav.c src/float32.c src/double64.c Fix knock on effects from above. 2005-07-19 Erik de Castro Lopo * src/wav.c Prevent files with unknown chunks from being opened read/write. 2005-07-14 Erik de Castro Lopo * src/flac.c Do not use psf->end_of_file because it never gets set to anything. * src/common.h Remove unused SF_PRIVATE field end_of_file. 2005-07-12 Erik de Castro Lopo * src/common.c Change the 'S' format specifier of psf_binheader_writef() to write AIFF style strings (no terminating character). * src/aiff.c Move to new (correct) AIFF string style. Thanks to Axel Röbel for being so persistent on this issue. 2005-07-11 Erik de Castro Lopo * src/sndfile.c Allow SFE_UNSUPPORTED_FORMAT as an error from sf_open(). * doc/api.html doc/command.html Documentation updates (thanks to Kyroz for promoting these updates). * src/mat5.c Modify the way the header is written. 2005-07-10 Erik de Castro Lopo * src/caf.c Add a 'free' chunk to the written file so that the audio data starts at an offset of 0x1000. * src/sndfile.c Allow SFE_UNSUPPORTED_FORMAT as an error from sf_open(). 2005-07-09 Erik de Castro Lopo * src/caf.c src/sndfile.c Add support for signed 8 bit integers. * tests/write_read_test.tpl Add test for signed 8 bit integers in CAF files. * doc/index.html Update matrix for signed 8 bit integers in CAF files. 2005-07-08 Erik de Castro Lopo * src/sndfile.c Update sf_check_format() to support CAF. * examples/sndfile-convert.c Add support for ".caf" file extension. * doc/index.html Add Apple CAF to the support matrix. * src/caf.c Add file write support. * src/common.c Fix printing of Frames. * tests/Makefile.am tests/write_read_test.tpl tests/lossy_comp_test.c tests/header_test.tpl misc_test.c Add tests for CAF files. 2005-07-07 Erik de Castro Lopo * doc/FAQ.html Fix Q/A about reading/writing memory buffers. * src/caf.c Bunch of work to support reading of CAF files. 2005-07-04 Erik de Castro Lopo * src/(aiff|ima_adpcm|mat4|mat5|ms_adpcm).c examples/sndfile-play.c Fix sign conversion errors reported by gcc-4.0. * src/caf.c New file for Apple's Core Audio File format. * src/sndfile.c src/common.h src/sndfile.h.in src/Makefile.am Hook new file into build system. 2005-06-21 Erik de Castro Lopo * src_wav_w64.c Fix handling of stupidly large 'fmt ' chunks. Thanks to Vadim Berezniker for supplying an example file. * src/common.h src/sndfile.c Remove redundant error code SFE_WAV_FMT_TOO_BIG. 2005-06-20 Erik de Castro Lopo * src/sndfile.h.in src/common.h src/sndfile.c Add public error value SF_ERR_MALFORMED_FILE. * src/sndfile.c When parsing a file header fails and we don't have a system error, then set the error number to SF_ERR_MALFORMED_FILE (suggested by Kyroz). * configure.ac Allow sqlite support to be disabled in configure script. * regtest/database.c regtest/sndfile-regtest.c Fix compiling when sqlite is missing. 2005-06-11 Erik de Castro Lopo * src/file_io.c Fix psf_is_pipe() and return value of psf_fread() when using virtual i/o. * src/sndfile.c Fix VALIDATE_AND_ASSIGN_PSF macro for virtual i/o. * tests/virtual_io_test.c Fill in skeleton test program. * tests/Makefile.am Move virtual i/o tests to end of tests with stdio/pipe tests. * src/(sndfile.h.in|file_io.c|common.h|sndfile.c) tests/virtual_io_test.c Rename some of the virtual i/o functions and data types. 2005-06-10 Erik de Castro Lopo * src/sndfile.c Fix the return values of sf_commands : SFC_SET_NORM_DOUBLE, SFC_SET_NORM_FLOAT, SFC_GET_LIB_VERSION and SFC_GET_LOG_INFO. Thanks to Kyroz for pointing out these errors. * doc/command.html Correct documented return values for SFC_SET_NORM_DOUBLE and SFC_SET_NORM_FLOAT. Thanks to Kyroz again. 2005-05-17 Erik de Castro Lopo * regtest/* Add new files for sndfile-regtest program. * configure.ac Makefile.am Hook regetest into build. * src/wav.c src/common.c Fix a regression where long ICMT chunks were causing the WAV parser to exit. 2005-05-15 Erik de Castro Lopo * libsndfile.spec.in Add html docs to the files section as suggested by Karsten Jeppesen. * src/aiff.c Fix parsing of odd length ANNO chunks. 2005-05-13 Erik de Castro Lopo * src/common.h Change the include guard to prevent clashes with other code. 2005-05-12 Erik de Castro Lopo * examples/sndfile-play.c Improve error handling in code for playback under Linux/ALSA. 2005-05-10 Erik de Castro Lopo * src/ircam.c Fix writing of IRCAM files on big endian systems (thanks to Axel Röbel). * src/wav.c Add workaround for files created by the Peak audio editor on Mac which can produce files with very short LIST chunks (thanks to Jonathan Segel who supplied the file). 2005-04-30 Erik de Castro Lopo * src/aiff.c Apply a patch From David Viens to make the parsing of basc chunks more robust. * src/wav.c Another patch from David Viens to write correct wavex channel masks for the most common channel configurations. 2005-04-08 Erik de Castro Lopo * src/command.c Only allow FLAC in the format arrays if FLAC is enabled. Thanks to Leigh Smith. 2005-03-09 Erik de Castro Lopo * src/common.h Add a directory field for storing the file directory to the SF_PRIVATE struct. * src/sndfile.c Grab the directory name when copying the file path. * src/file_io.c Cleanup psf_open_rsrc() and also check for resource fork in .AppleDouble/filename. 2005-03-01 Erik de Castro Lopo * src/svx.c Fix a bug in the printing of the channel count. Bug reported by Michael Schwendt. Thanks. 2005-01-26 Erik de Castro Lopo * src/paf.c Fix a seek bug for 24 bit PAF files. * tests/write_read_test.tpl Update write_read_test to trigger the previously hidden PAF seek bug. 2005-01-25 Erik de Castro Lopo * src/aiff.c src/w64.c src/wav.c Do not return a header parse error when the log buffer overflows. Continuing parsing works even on files where the log buffer does overflow. This avoids a bug on some weirdo WAV (and other) files. * src/common.h src/sndfile.c Remove SFE_LOG_OVERRIN error and its associated error message. * src/file_io.c Fix a rsrc fork problem on MacOSX. 2004-12-31 Erik de Castro Lopo * src/sndfile-play.c In the ALSA output code, added call to snd_pcm_drain() just before snd_pcm_close() as suggested by Thomas Kaeding. In the OSS output code, added two ioctls (SNDCTL_DSP_POST and SNDCTL_DSP_SYNC) just before the close of the audio device. * tests/virtual_io_test.c tests/Makefile.am Add a new test program (currently empty) and add it to the build. 2004-12-29 Erik de Castro Lopo * src/sndfile.h.in src/sndfile.h src/common.h src/file_io.c src/create_symbols_file.py Apply patch from Steve Baker which is the beginnings of a virtual I/O interface. 2004-12-23 Erik de Castro Lopo * src/*.c src/sndfile.h.in Const-ify the write path throughout the library. 2004-12-14 Erik de Castro Lopo * doc/development.html Minor improvements. 2004-11-29 Erik de Castro Lopo * doc/bugs.html Minor improvements. 2004-11-18 Erik de Castro Lopo * src/aiff.c Add workaround for Logic Platinum AIFF files with broken COMT chunks. 2004-11-16 Erik de Castro Lopo * doc/FAQ.html Remove some ambiguities in the SD2 FAQ answer. 2004-11-15 Erik de Castro Lopo * Win32/sndfile.h Win32/config.h MacOS9/sndfile.h MacOS9/config.h Updates from autoconfig versions. 2004-11-13 Erik de Castro Lopo * src/aiff.c Fix parsing of COMT chunks. Store SF_STR_COMMENT data in ANNO chunks instead of COMT chunk. 2004-11-07 Erik de Castro Lopo * src/file_io.c src/common.h Change the ptr argument to psf_write() from "void*" to a "const void*". Thanks to Tobias Gehrig for suggesting this. 2004-10-31 Erik de Castro Lopo * src/file_io.c src/common.h Add functions psf_close_rsrc() and read length of resourse fork into rsrclength field of SF_PRIVATE. * src/sd2.c Make sure resource fork gets closed. * tests/util.tpl Add functions to check for file descriptor leakage. * src/write_read_test.tpl Use the file descriptor leak checks. * src/sndfile.h.in Add SFC_GET_LOOP_INFO and SF_LOOP_INFO struct. * src/common.h Add SF_LOOP_INFO pointer to SF_PRIVATE. * src/wav.c src/aiff.c Improve and add parsing of 'ACID' and 'basc' chunks, filling in SF_LOOP_INFO data in SF_PRIVATE. 2004-10-30 Erik de Castro Lopo * src/sd2.c Further cleanup: remove printfs, change snprintf to LSF_SNPRINTF. * Win32/config.h Win32/sndfile.h Updates. * tests/util.tpl Add win32 macro for snprintf. 2004-10-29 Erik de Castro Lopo * src/sfendian.h Add macros : H2BE_SHORT, H2BE_INT, H2LE_SHORT and H2LE_INT. * src/sd2.c Use macros to make sure writing SD2 files on little endian machines works correctly. * tests/util.tpl Add a delete_file() function which also deletes the resource fork of SD2 files. * tests/write_read_test.tpl Use delete_file() so that "make distcheck" works. 2004-10-28 Erik de Castro Lopo * src/sndfile.c src/file_io.c Move resource filename construction and testing to psf_open_rsrc(). * src/common.h src/sndfile.c Add error SFE_SD2_FD_DISALLOWED. * tests/util.tpl tests/*.(c|tpl) Add and allow_fd parameter to test_open_file_or_die() so that use of sf_open_fd() can be avoided when opening SD2 files. 2004-10-27 Erik de Castro Lopo * src/wav.c Update ACID chunk parsing. * src/sd2.c More fixes for files with large resource forks. 2004-10-23 Erik de Castro Lopo * src/common.h src/sndfile.c Add error numbers and messages for sd2 files. * src/sd2.c Reading of sd2 (resource fork version) now seems to be working. 2004-10-17 Erik de Castro Lopo * src/file_io.h Update file_io.c to include win32 psf_rsrc_open(). * tests/floating_point_test.tpl Remove use of __func__ in test programs (MSVC++ doesn't grok this). * Win32/(config|sndfile).h MacOS9/(config|sndfile).h Updates. 2004-10-13 Erik de Castro Lopo * src/sfendian.h Fix endswap_int64_t_(array|copy). * src/test_endswap.(tpl|def) Add tests for above and inprove all tests. 2004-10-12 Erik de Castro Lopo * src/sfendian.h Improve type safety, add endswap_double_array(). * src/double64.c Use endswap_double_array() instead of endswap_long_array(). * src/test_endswap.(tpl|def) src/Makefile.am Add preliminary endswap tests and hook into build system. 2004-10-06 Erik de Castro Lopo * src/configure.ac src/makefile.am Finally fix the bulding of DLLs on Win32/MinGW. * tests/makefile.am Fix running of tests on Win32/MinGW. 2004-10-01 Erik de Castro Lopo * src/sndfile.h.in src/sndfile.c tests/floating_point_test.tpl Rename SFC_SET_FLOAT_INT_MULTIPLIER to SFC_SET_SCALE_FLOAT_INT_READ. * doc/command.html Document SFC_SET_SCALE_FLOAT_INT_READ. 2004-09-30 Erik de Castro Lopo * tests/floating_point_test.(tpl|def) Derived from floating_point_test.c. Add (float|double)_(short|int)_test functions. * tests/util.(tpl|def) Make separate float and double versions of gen_windowed_sine(). * tests/write_read_test.tpl Fix after changes to gen_windowed_sine(). * src/(float32|double64).c Implement SFC_SET_FLOAT_INT_MULTIPPLIER. 2004-09-29 Erik de Castro Lopo * acinclude.m4 Fix warnings from automake 1.8 and later. * examples/sndfile-info.c Add a "fflush (stdout)" after printing Win32 message. 2004-09-28 Erik de Castro Lopo * Win32/Makefile.mingw.in Add a "make install" target. 2004-09-24 Erik de Castro Lopo * src/sndfile.h.in src/common.h src/sndfile.c src/command.c Start work on adding command SFC_SET_FLOAT_INT_MULTIPLIER. 2004-09-22 Erik de Castro Lopo * examples/sndfile-convert.c Fix a bug converting stereo integer PCM files to float. 2004-09-22 Erik de Castro Lopo * examples/sndfile-play.c Appy patch from Conrad Parker to make Mac OSX error messages more consistent and informative. * doc/api.html Fix a HTML HREF which was wrong. * doc/win32.html Add information about when nmake fails. 2004-09-05 Erik de Castro Lopo * examples/sndfile-play.c Another patch from Denis Cote to prevent race conditions. 2004-09-02 Erik de Castro Lopo * src/common.h src/ms_adpcm.c src/ima_adpcm.c Fix alternative to ISO standard flexible struct array feature for broken compilers. 2004-08-31 Erik de Castro Lopo * src/common.h src/string.c src/sndfile.c Make sf_set_string() return an error if trying to set a string when in read mode. 2004-08-29 Erik de Castro Lopo * src/common.h Change the unnamed union into a named union so gcc-2.95 will compile it. * src/*.c Fixes to allow for the above change. 2004-08-20 Erik de Castro Lopo * examples/sndfile-play.c Fixes for Win32. Thanks to Denis Cote. * Win32/Win32/Makefile.(msvc|mingw.in) Fix build system after removal of sfendian.h. Build sndfile-convert. * src/Makefile.am Remove sfendian.c from dependancies. 2004-08-10 Erik de Castro Lopo * src/sndfile.h.in Fix typo in comments (thanks Tommi Sakari Uimonen). 2004-07-31 Erik de Castro Lopo * tests/(a|u)law_test.c Minor cleanup. 2004-07-29 Erik de Castro Lopo * src/(pcm|float|double64|ulaw|alaw|xi).c Optimise read/write loops by removing a redundant variable. 2004-07-24 Erik de Castro Lopo * src/file_io.c Remove call to fsync() in psf_close(). 2004-07-19 Erik de Castro Lopo * src/pcm.c Inline x2y_array() functions where possible. * configure.ac Detect presence of type int64_t. * src/sfendian.c src/sfendian.h Move functions in the first file to the sfendian.h as static inline functions. Improve endswap_long_*() where possible. 2004-07-17 Erik de Castro Lopo * src/pcm.c When converting from unsigned char to float or double, subtract 128 before converting to float/double rather than after to save a floating point operation as suggested by Stefan Briesenick. * src/(pcm|sfendian|alaw|ulaw|double64|float32).c Optimize inner loops by changing the loop counting slightly as suggested by Stefan Briesenick. * configure.ac Detect presence of . * src/sfendian.h Use if present as suggested by Stefan Briesenick. * src/pcm.c Update bytewapping. 2004-07-02 Erik de Castro Lopo * src/common.h src/*.c Change the psf->buffer field of SF_PRIVATE into a more type safe union with double, float, int etc elements. 2004-06-28 Erik de Castro Lopo * examples/sndfile-play.c Merge slightly modifed patch from Stanko Juzbasic which allows playback of mono files on MacOSX. 2004-06-25 Erik de Castro Lopo * examples/sndfile-convert.c Move copy_metadata() after the second sf_open(). 2004-06-21 Erik de Castro Lopo * examples/sndfile-convert.c Fix a bug which caused the program to go into an infinite loop if the source file has no meta-data. Thanks to Ron Parker for reporting this. * src/sndfile.h.in Add SF_STR_FIRST and SF_STR_LAST to allow enumeration of string types. * Win32/sndfile.h MacOS9/sndfile.h Update these as per the above file. 2004-06-17 Erik de Castro Lopo * configure.ac src/common.h src/ogg.c src/sndfile.c src/sndfile.h.in src/Makefile.am Apply large patch from Conrad Parker implementing Ogg Vorbis, Ogg Speex and Annodex support via liboggz and libfishsound. Thanks Conrad. 2004-06-15 Erik de Castro Lopo * src/avr.c src/ircam.c src/nist.c src/paf.c src/xi.c Add cast to size_t for some parameters passed to psf_binheader_writef. This is Debian bug number 253490. Thanks to Anand Kumria and Andreas Jochens. * src/w64.c Found and fixed a bug resulting from use of size_t when writing W64 'fmt ' chunk. 2004-06-14 Erik de Castro Lopo * configure.ac Bump version to 1.0.10 ready for release. * Makefile.am Remove redundant files (check_libsndfile.py libsndfile_version_convert.py) from distribution tarball. * tests/header_test.tpl Fix uninitialised variable. * src/GSM610/short_term.c Fix compiler warning on MSVC++. 2004-05-23 Erik de Castro Lopo * src/wav.c Improve record keeping of chunks seen and return an error if a file with unusual chunks is opened in mode SFM_RDWR. * src/mmreg.h This file not needed so remove it. 2004-05-22 Erik de Castro Lopo * tests/header_test.tpl Add extra_header_test(). * src/common.h src/sndfile.c Add SFE_RDWR_BAD_HEADER error number and string. 2004-05-21 Erik de Castro Lopo * tests/utils.tpl tests/*.c tests/*.tpl Add a line number argument to check_log_buffer_or_die() and update all files that use that function. * tests/header_test.tpl Modify/update tests for files opened SFM_RDWR and SFC_UPDATE_HEADER_AUTO. * src/aiff.c src/wav.c Fix another bug in AIFF and WAV files opened in SFM_RDWR and using SFC_UPDATE_HEADER_AUTO. * src/test_file_io.c Add a test for psf_ftruncate() function. 2004-05-19 Erik de Castro Lopo * src/sndfile.c Fix another weird corner case bug found by Martin Rumori. Thanks. * tests/header_test.(tpl|def) Two new files to test for the absence of the above bug and include tests moved from tests/misc_test.c. * tests/Makefile.am Hook new tests into build/test system. * tests/misc_test.c Remove update_header_test() which has been moved to the new files above. 2004-05-16 Erik de Castro Lopo * src/aiff.c Fixed a bug reported by Martin Rumori on the LAD list. If a file created with a format of SF_FORMAT_FLOAT and then closed before any data is written to it, the header can get screwed up (PEAK chunk gets overwritten). * tests/write_read_test.tpl Add a test (empty_file_test) for the above bug. 2004-05-13 Erik de Castro Lopo * Win32/Makefile.mingw.in Added a Makefile for MinGW (needs to be processed by configure). * src/mmsystem.h src/mmreg.h Add files from the Wine project (under the LGPL) to allow build of sndfile-play.exe under MinGW. 2004-05-12 Erik de Castro Lopo * src/GSM610/gsm610_priv.h Replace ugly macros with inline functions. * src/GSM610/*.c Remove temporary variables used by macros and other minor fixes required by above change. 2004-05-10 Erik de Castro Lopo * tests/pipe_test.tpl tests/stdio_test.c Win32/Makefile.msvc Make sure these programs compile (even though they do nothing) on Win32 and add them to the "make check" target. * src/sfendian.h Fix warning on Sparc CPU and code cleanup. 2004-05-09 Erik de Castro Lopo * src/file_io.c Fix warning messages when compiling under MinGW. 2004-05-01 Erik de Castro Lopo * configure.ac Set HAVE_FLEXIBLE_ARRAY in src/config.h depending on whether the compiler accepts the flexible array struct member as per 1999 ISO C standard. * src/common.h src/ima_adpcm.c src/paf.c src/ms_adpcm.c Added ugly #if HAVE_FLEXIBLE_ARRAY and provided a non-standards compliant hack for non 1999 ISO C compliant compilers. 2004-04-26 Erik de Castro Lopo * src/strings.c If adding an SF_STR_SOFTWARE string, only append libsndfile-X.Y.Z if the string does not already have libsndfile in the string. Thanks to Conrad Parker. * tests/string_test.c Add test to verify the above. * examples/sndfile-convert.c Add ability to transcode meta data as well (Conrad Parker). 2004-04-25 Erik de Castro Lopo * doc/command.html Fix minor error. Thanks to Simon Burton. * doc/win32.html Started adding instructions for compiling libsndfile under MinGW. * configure.ac Add --enable-bow-docs to enable black text on a white background HTML docs. * doc/libsndfile.css.in This is now a template file for configure which sets the foreground and background colours. 2004-04-20 Erik de Castro Lopo * configure.ac Do some MinGW fixes. * configure.ac doc/Makefile.am Install HTML docs when doing make install. 2004-04-19 Erik de Castro Lopo * examples/sndfile-info.c Print out the dB level with the signal max. 2004-04-15 Erik de Castro Lopo * src/file_io.c Define S_ISSOCK in src/file_io.c if required. 2004-04-03 Erik de Castro Lopo * configure.ac Improve printout configuration summary (as suggested by Axel Röbel). * doc/index.html Add link to pre-release location. * src/sndfile.h.in Remove comma after last element of enum. * src/float32.c src/double64.c Fix read/write of float/double encoded raw files to/from pipes. * tests/pipe_test.c tests/pipe_test.tpl tests/pipe_test.def Turn pipe_test.c into an autogenerated file and add tests for reading/ writing floats and doubles. * tests/Makefile.am Hook tests/pipe_test.* into build system. 2004-04-02 Erik de Castro Lopo * configure.ac acinclude.m4 Rename AC_C_STRUCT_HACK macro to AC_C99_FLEXIBLE_ARRAY. 2004-03-31 Erik de Castro Lopo * tests/misc_test.c Perform update_header_test in RDWR mode as well. * src/aiff.c Fix problems when updating header in RDWR mode. 2004-03-30 Erik de Castro Lopo * src/wav.c src/w64.c src/wav_w64.c Integrate code supplied by David Viens for supporting microsoft's WAVEFORMATEXTENSIBLE stuff. Thanks David for supplying this. * configure.ac doc/*.html Bump version to 1.0.9. 2004-03-28 Erik de Castro Lopo * src/command.c src/sndfile.c src/sndfile.h.in src/wav.c Started work on supporting microsoft's WAVEFORMATEXTENSIBLE gunk. 2004-03-26 Erik de Castro Lopo * src/avr.c New file to handle Audio Visual Resaerch files. * src/sndfile.h.in src/common.h src/sndfile.c src/command.c Hook AVR into everything else. * tests/Makefile.am tests/write_read_test.tpl tests/misc_test.c Add testing for AVR files. 2004-03-22 Erik de Castro Lopo * src/file_io.c Fix psf_set_file() for win32. Thanks to Vincent Trussart (Plogue Art et Technologie) for coming up with the solution. 2004-03-21 Erik de Castro Lopo * tests/write_read_test.tpl Fixed a bug that was causing valgrind to report a memory leak. The bug was in the test code itself, not the library. 2004-03-20 Erik de Castro Lopo * examples/generate.cs An example showing how to use libsndfile from C#. Thanks to James Robson for providing this. 2004-03-19 Erik de Castro Lopo * src/common.c Fix problems with WAV files containing large chunks after the 'data' chunk. Thanks to Koen Tanghe for providing a sample file. 2004-03-17 Erik de Castro Lopo * configure.ac Detect presense of ALSA (Advanced Linux Sound Architecture). * examples/sndfile-play.c Add ALSA output support. * examples/Makefile.am Add ALSA_LIBS to link line of sndfile-play.c. 2004-03-15 Erik de Castro Lopo * acinclude.m4 Add new macro (AC_C_STRUCT_HACK) to detect whether the C compiler allows the use of the what is known as the struct hack introduced by the 1999 ISO C Standard. * configure.ac The last release would not compile with gcc-2.95 due to the use of features (ie struct hack) introduced by the 1999 ISO C Standard. Add check to make sure compiler handles this and bomb out if it doesn't. 2004-03-14 Erik de Castro Lopo * tests/write_read_test.tpl Fix compiler warning on Win32. * src/file_io.c Fix use of an un-initialised variable in Win32 stuff. * Win32/config.h examples/sndfile-play.c Win32 fixes. 2004-03-10 Erik de Castro Lopo * configure.ac Fix bug which occurres when configuring for MinGW. If compiler is gcc and cross compiling use -nostdinc. 2004-03-09 Erik de Castro Lopo * src/common.h src/aiff.c src/wav.c src/float32.c src/double64.c src/sndfile.c Fix a bug with PEAK chunk handling for files with more than 16 channels. Thanks to Remy Bruno for finding this. 2004-03-08 Erik de Castro Lopo * src/common.c Fix a bug which was preventing WAV files being openned correctly if the file had a very large header. Thanks to Eldad Zack for finding this. 2004-03-04 Erik de Castro Lopo * configure.ac src/file_io.c Fix cross-compiling from Linux to Win32 using the MinGW tools. 2004-03-01 Erik de Castro Lopo * src/create_symbols_file.sh Christian Weisgerber pointed out that the shell script did not run on a real Bourne shell although it did run under Bash in Bourne shell mode. * src/create_symbols_file.py Rewrite of above in Python. Also add support for writing Win32 .def files. The Python script generates Symbols.linux, Symbols.darwin and libsndfile.def (Win32 version). These files get shipped with the tarball so there should not be necessary to run the Python script when building the code from the tarball. * configure.ac src/Makefile.am Win32/Makefile.am Hook new Python script into the build system. 2004-02-25 Erik de Castro Lopo * src/configure.ac Add --enable-gcc-werror option and move GCC specific stuff down. 2004-02-24 Erik de Castro Lopo * acinclude.m4 configure.ac Fix clip mode detection (tested in one of HP's testdrive Itanium II boxes). * src/file_io.c Added check for sizeof (off_t) != sizeof (sf_count_t) to prevent recurrence of missing large file support on Linux and Solaris. 2004-02-19 Erik de Castro Lopo * examples/sndfile-play.c Fix a MacOSX specific bug which was caused by a space being inserted in the middle of a file name. * configure.ac src/Makefile.am examples/Makefile.am Fix a couple of MacOSX build issues. 2004-02-17 Erik de Castro Lopo * doc/command.html Document SFC_SET_CLIPPING and SFC_GET_CLIPPING. 2004-02-14 Erik de Castro Lopo * doc/*.html Applied patch from Frank Neumann (author of lakai) which fixes many minor typos in documentation. Thanks Frank. 2004-02-13 Erik de Castro Lopo * ChangeLog Changed my email address throughout source and docs. 2004-02-08 Erik de Castro Lopo * src/file_io.c Make sure config.h is included before stdio.h to make sure large file support is enabled on Linux (and Solaris). * tests/misc_test.c Disable update_header test on Win32. This should work but doesn't and I'm not sure why. * Make.bat Win32/Makefile.msvc Updates. 2004-01-07 Erik de Castro Lopo * src/common.h Changed logindex, headindex and headend files of SF_PRIVATE from unsigned int to int to prevent weird arithmetic bugs. * src/common.c src/aiff.c src/wav.c src/w64.c Fixed compiler warnings resulting from above change. 2004-01-06 Erik de Castro Lopo * src/common.c Fixed a bug in header reader for some files with data after the sample data. 2003-12-29 Erik de Castro Lopo * tests/lossy_comp_test.c tests/Makefile.am Add tests for AIFF/IMA files. 2003-12-26 Erik de Castro Lopo * src/macbinary3.c src/macos.c Two new files required for handling SD2 files. * src/common.h Add prototypes for functions in above two files. * src/Makefile.am Hook new files into build system. 2003-12-21 Erik de Castro Lopo * configure.ac Add checks for mmap() and getpagesize() which might be used at some time for faster file reads. Add detection of MacOSX. 2003-12-13 Erik de Castro Lopo * doc/FAQ.html Minor mods to pkg-config section. 2003-12-12 Erik de Castro Lopo * src/create_symbols_file.sh Andre Pang (also known as Ozone) pointed out that on MacOSX, all non static symbols are exported causing troubles when trying to link libsndfile with another library which has any of the same symbols. He fixed this by supplying the MacOSX linker with a file containing all the public symbols so that only they would be exported and then supplied a patch for libsndfile. This wasn't quite ideal, because I would have to maintain two (3 if you include Win32) separate files containing the exported symbols. A better solution was to create this script which can generate a Symbols file for Linux, MacoSX and any other OS that supports minimising the number of exported symbols. * configure.ac src/Makefile.am Hook the new script into the build process. 2003-12-10 Erik de Castro Lopo * doc/index.html Added comments about Steve Dekorte's SoundConverter scam. 2003-12-07 Erik de Castro Lopo * src/file_io.c Axel Röbel pointed out that on Mac OSX a pipe is not considered a fifo (S_ISFIFO (st.st_mode) is false) but a socket (S_ISSOCK (st.st_mode) is true). The test has therefore been changed to is S_ISREG and anything which which does not return true for S_ISREG is considered a pipe. 2003-11-25 Erik de Castro Lopo * tests/misc_test.c Fix update_header_test to pass SDS. * src/sds.c More minor fixes. * tests/floating_point_test.c Add test for SDS files. * src/command.c Add SDS to major_formats array. 2003-11-24 Erik de Castro Lopo * tests/write_read_test.tpl tests/misc_test.c Add tests for SDS files. * src/sds.c Fix a bug in header update code. 2003-11-23 Erik de Castro Lopo * src/sds.c Get file write working. * src/paf.c Fix a potential bug in paf24_seek(). 2003-11-04 Erik de Castro Lopo * doc/FAQ.html Add Q/A about u-law encoded WAV files. * Win32/*.h Updated so it compiles on Win32. 2003-11-03 Erik de Castro Lopo * examples/sndfile-convert.c Add -alaw and -ulaw command line arguments. * configure.ac Add library versioning comments. Add arguments to AC_INIT. 2003-10-28 Erik de Castro Lopo * src/file_io.c Ross Bencina has contributed code to replace all of the (mostly broken) Win32 POSIX emulation calls with calls the native Win32 file I/O API. This code still needs testing but is likely to be a huge improvemnt of support for Win32. Thanks Ross. 2003-10-27 Erik de Castro Lopo * src/dwvw.c Removed filedes field from the DWVW_PRIVATE struct. * src/file_io.c Change psf_fopen() so it returns psf->error instead of the file descriptor. Add new functions psf_set_stdio() and psf_set_file(). * src/sndfile.c Change these to work with changed psf_fopen() return value. Remove all uses of psf->filedes from sndfile, making it easier to slot native Win32 API file handling functions. * src/test_file_io.c Minor changes to make it compile with new file_io.c stuff. 2003-10-26 Erik de Castro Lopo * src/gsm610.h Rename a variable from true to true_flag. As Ross Bencina points out, true is defined in the C99 header . * src/file_io.c If fstat() fails, return SF_TRUE instead of -1 (Ross Bencina). 2003-10-09 Erik de Castro Lopo * src/common.h Increase the size of SF_BUFFER_LEN and SF_HEADER_LEN. * src/sndfile.c Fix sf_read/write_raw which were dividing by psf->bytwidth and psf->blockwidth which can both be zero. * examples/sndfile-info.c Increase size of BUFFER_LEN. 2003-09-21 Erik de Castro Lopo * configure.ac Add checks for and ssize_t. Other Win32/MinGW checks. * src/aiff.c src/au_g72x.c src/file_io.c src/gsm610.c src/interleave.c src/paf.c src/sds.c src/svx.c src/voc.c src/w64.c src/wav.c src/xi.c Fix compiler warnings. 2003-09-20 Erik de Castro Lopo * tests/scale_clip_test.tpl Add definition of M_PI if needed. 2003-09-19 Erik de Castro Lopo * configure.ac Detect if S_IRGRP is declared in . * src/file_io.c tests/*.tpl tests/*.c More fixes for Win32/MSVC++ and MinGW. MinGW does have but that file doesn't declare S_IRGRP. 2003-10-18 Erik de Castro Lopo * src/config.h.in Add comment stating that the sf_count_t typedef is determined when libsndfile is being compiled. * tests/utils.tpl Modified so that utils.c gets one copy of the GPL and not two. 2003-09-17 Erik de Castro Lopo * Win32/unistd.h src/sf_unistd.h Move first file to the second. This will help for Win32/MSVC++ and MinGW. * Win32/Makefile.am src/Makefile.am Changed in line with above. * Win32/Makefile.msvc Removed "/I Win32" which is no longer required. * src/file_io.c src/test_file_io.c tests/*.tpl tests/*.c If HAVE_UNISTD_H include else include . This should work for Win32, MinGW and other fakes Unix-like OSes. * src/*.c Removed #include from files which didn't need it. 2003-09-16 Erik de Castro Lopo * libsndfile.spec.in Apply fix from Andrew Schultz. 2003-09-07 Erik de Castro Lopo * src/vox_adpcm.c Only set psf->sf.samplerate if the existing value is invalid. 2003-09-06 Erik de Castro Lopo * examples/sndfile-play.c Started adding support for ALSA output. 2003-09-04 Erik de Castro Lopo * src/sndfile.h.in Removed from sndfile.h. * src/*.c examples/*.c tests/*.c tests/*.tpl Added where needed. 2003-09-02 Erik de Castro Lopo * src/common.h Added ARRAY_LEN, SF_MAX and SF_MIN macros. 2003-08-19 Erik de Castro Lopo * doc/index.html Remove statements about alternative licensing arrangements. 2003-08-17 Erik de Castro Lopo * MacOS MacOS9 Makefile.am configure.ac Change directory name from MacOS to MacOS9 * MacOS9/MacOS9-readme.txt Change name to make it really obvious, add text to top of file to make it still more obvious again. 2003-08-16 Erik de Castro Lopo * src/test_log_printf.c Add tests for %u conversions. * src/common.c Fix psf_log_printf() %u conversions. 2003-08-15 Erik de Castro Lopo * src/aiff.c Fixed a bug where opening a file with a non-trival header in SFM_RDWR mode would over-write part of the header. Thanks to Axel Röbel for pointing this out. Axel also provided a patch to fix this but I came up with a neater and more general solution. Return error when openning an AIFF file with data after the SSND chunk (Thanks Axel Röbel). * tests/aiff_rw_test.c Improvements to test program which will later allow it to be generalised to test WAV, SVX and others as required. 2003-08-14 Erik de Castro Lopo * tests/pipe_test.c Add useek_pipe_rw_test() submitted by Russell Francis. * src/sndfile.c In sf_open_fd(), check if input file descriptor is a pipe. * src/sndfile.[ch] Fix typo in variable name do_not_close_descriptor. 2003-08-13 Erik de Castro Lopo * src/test_log_printf.c Improve the tests for %d and %s conversions. * src/common.c Fixed a few problems in psf_log_printf() found using new tests. 2003-08-06 Erik de Castro Lopo * configure.ac Add -Wwrite-strings warning to CFLAGS if the compiler is GCC. Thanks to Peter Miller (Aegis author) for suggesting this and supplying a patch. * src/*.c examples/*.c tests/*.c Fix all compiler warnings arising from the above. 2003-08-02 Erik de Castro Lopo * tests/aiff_rw_test.c tests/Makefile.am New test program to check for errors re-writing the headers of AIFC files opened in mode SFM_RDWR. 2003-07-21 Erik de Castro Lopo * examples/sndfile-play.c Applied a patch from Tero Pelander to allow this program to run on systems using devfs which used /dev/sound/dsp instead of /dev/dsp. 2003-07-11 Erik de Castro Lopo * doc/new_file_type.HOWTO Updated document. Still incomplete. 2003-06-29 Erik de Castro Lopo * src/sndfile.c Fix VALIDATE_SNDFILE_AND_ASSIGN_PSF which was returning an error rather than saving it and returning zero. 2003-06-25 Erik de Castro Lopo * src/file_io.c Two fixes for Mac OS9. Fix all casts from sf_count_t to ssize_t (not size_t). 2003-06-22 Erik de Castro Lopo * src/wav.c Fix for reading files with RIFF length of 8 and data length of 0. 2003-06-14 Erik de Castro Lopo * src/*.c tests/*.c tests/*.tpl Added comments to mark code for removal when make Lite version of libsndfile. 2003-06-09 Erik de Castro Lopo * examples/sndfile-convert.c Add extra error checking for unrecognised arguments. 2003-06-08 Erik de Castro Lopo * src/ima_adpcm.c Started adding code to write IMA ADPCM encoded AIFF files. * src/test_log_printf.c src/Makefile.am New file to test psf_log_printf() function and add hooks into build system. * src/common.c Move psf_log_printf() function to top of the file and only compile the rest of the file if if PSF_LOG_PRINTF_ONLY is not defined. 2003-06-03 Erik de Castro Lopo * Win32/config.h Win32/sndfile.h Updated with new config variables. * Win32/unistd.h src/file_io.c Added implementation of S_ISFIFO macro which Win32 seems to lack and is used in src/file_io.c. * tests/utils.tpl Added #include to pull in Win32/unistd.h so it compiles for Win32. * src/Makefile.msvc Added src\test_file_io.exe build target and run this as the very first test. * tests/win32_test.c Add support for testing Cygwin32. * configure.ac Detect POSIX fsync() and fdatasync() functions. * src/file_io.c If compiling for Cygwin, call fsync() before calling fstat() to retrieve file length. * tests/pcm_test.tpl Add a test for lrintf() function. This was required to detect a really broken lrint() and lrintf() on Cygwin. * tests/misc_test.c Don't run permission test when compiling under Cygwin. * src/float_cast.h Fix fallback macro for lrint() and lrintf() to cast to long instead of int to match official function prototypes. 2003-06-02 Erik de Castro Lopo * examples/sndfile-convert.c Modifications to improve accuracy of conversions; use double data for floating point and int for everything else. * src/ima_apdcm.c Completed work on decoding IMA ADPCM encoded AIFF files. Still need to get encoding working. 2003-05-28 Erik de Castro Lopo * src/aiff.c src/ima_adpcm.c Start working on getting IMA ADPCM encoded AIFF files working. 2003-05-27 Erik de Castro Lopo * configure.ac Fixed the touch command for when the autogen program is not found (Matt Flax). * src/ulaw.c src/alaw.c Made these pipe-able. 2003-05-24 Erik de Castro Lopo * src/paf.c src/ircam.c Fixed writing to pipe. * src/wav.c src/aiff.c src/nist.c src/mat*.c src/svx.c src/w64.c Return SFE_NO_PIPE_WRITE if an attempt is made to write to a pipe. 2003-05-23 Erik de Castro Lopo * examples/sndfile-info.c Modified to detect unknown file lengths. * src/mat4.c Fix reading from a pipe. 2003-05-22 Erik de Castro Lopo * tests/pipe_test.c Add more file types to tests. * src/mat4.c Removed explicit setting of psf->sf.seekable to SF_TRUE. * tests/utils.tpl Add macro for generating and check data in the stdio and pipe tests. * tests/stdout_test.c tests/stdin_test.c Use the above macro to generate known data on output and check data on input. * src/voc.c src/htk.c common.h sndfile.c Disallow reading/writing VOC and HTK files from/to pipes be returning new error values. * src/w64.c Fixes to allow reading from a pipe. 2003-05-21 Erik de Castro Lopo * configure.ac src/sndfile.h.in When the configure script determines the sizeof (sf_count_t), also set the value of SF_COUNT_MAX in sndfile.h. * configure.ac Remove -pedantic flag from default GCC compiler flags. * tests/pipe_test.c Add a pipe_read_test() before doing pipe_write_test(). * tests/scale_clip_test.c Add test to make sure non-normalized values also clip in the right way. 2003-05-18 Erik de Castro Lopo * configure.ac Add test to detect processor clipping capabilities. * tests/stdin_test.c tests/stdout_test.c Fix a pair of compiler warnings. * src/common.h Add new pipeoffset field to SF_PRIVATE. This will contain the current file offset when operating on a pipe. * src/common.c Removed direct calls to psf_fread()/psf_fseek()/psf_fgets() etc from psf_binheader_readf and redirect them to new buffered versions header_read(), header_seek() and header_gets(). Add "G" format specifier to emulate fgets() functionality with buffering. This will allow reading some file types from pipes. * src/file_io.c When the file descriptor is a pipe, manintain psf->pipeoffset. * src/pvf.c Change use of psf_fgets() to psf_binheader_readf() as required but changes to header re * src/au.c Fix reading from a pipe. 2003-05-17 Erik de Castro Lopo * src/pcm.c Add clipping versions of the f2XXX_array() functions to allow option of clipping data that would otherwise overflow. * tests/scale_clip_test.tpl tests/scale_clip_test.def New files test that clipping option does actually work. 2003-05-14 Erik de Castro Lopo * doc/index.html Fixed a typo ("OS(" instead of "OS9"). 2003-05-13 Erik de Castro Lopo * tests/open_fail_test.c Include to prevent warning message of missing declaration of memset(). 2003-05-12 Erik de Castro Lopo * src/common.h Add new "add_clipping" field to SF_PRIVATE. * src/sndfile.h.in src/sndfile.c Add command SFC_SET_CLIPPING which sets/resets add_clipping field. 2003-05-11 Erik de Castro Lopo * doc/api.html Add docs for sf_set_string() and sf_get_string(). * src/common.h src/sndfile.c Add new SFE_STR_BAD_STRING error. * tests/stdin_test.c tests/stdout_test.c Removed all non-error print statements. * tests/stdio_test.c tests/pipe_test.c tests/Makefile.am Add print statements removed from two files above. 2003-05-10 Erik de Castro Lopo * libsndfile.spec.in Fixed a coulpe of minor errors discovered by someone calling themselves Agent Smith. * src/common.c src/common.h src/file_io.h Added is_pipe field to SF_PRIVATE and declaration of psf_is_pipe() function. (Axel Röbel) * src/sndfile.c Fixed determination of whether the file is a pipe. (Axel Röbel) * src/paf.c Force paf24 to start with undefined mode. (Axel Röbel) * tests/pipe_test.c Mods to make this test work and actually do the test on RAW files. (Axel Röbel). 2003-05-05 Erik de Castro Lopo * src/sndfile.c Fixed a potential bug where psf->sf.seekable was being set to FALSE when operating on stdin or stdout but then the default initialiser was reseting it to TRUE. Thanks to Axel Röbel. * src/aiff.c Fixed a bug in the header parser where it was not handling an odd length COMM chunk correctly. Thanks to Axel Röbel. * src/test_file_io.c Add more tests. * tests/win32_test.c New file for showing the bugs in the Win32 implementation of the POSIX API. It also runs on Linux for sanity checking. * tests/Makefile.am Win32/Makefile.msvc Hook the new test program into the build system. 2003-05-04 Erik de Castro Lopo * src/test_file_io.c New test program to test operation of functions defined in file_io.c. This should make supporting win32 significantly easier. * src/Makefile.am Hook new test program into the build system. * src/file_io.c Add compile/run time check that sizeof statbuf.st_size and sf_count_t are the same. * src/common.h src/sndfile.c Added new error code and error message for new check. * tests/benchmark.tpl Fix to use frames instead of samples in SF_INFO. 2003-05-03 Erik de Castro Lopo * src/file_io.c More stuffing about working around PLAIN OLD-FASHIONED **BUGS** in Win32. * examples/sndfile-info.c Applied patch from Conrad Parker to add "--help" and "-h" options as well as an improved usage message. 2003-05-02 Erik de Castro Lopo * src/au.c Added embedded file support. * tests/multi_file_test.c Added tests for embedded AU files. Added verbose testing mode. * src/common.h src/sndfile.c Added an embedded AU specific error code and message. * src/wav.c Added patch from Conrad Parker which filled in a little more information about ACIDized WAV files. 2003-04-30 Erik de Castro Lopo * src/file_io.c Fixed Win32 version of psf_fseek() which was calling psf_get_filelen() which was in turn calling psf_fseek() which in the end blew the stack. Now of course this would have been easy to find on Linux, but this blow up was happening in kernel32.dll and the fscking MSVC++ debugger couldn't figure out what call caused this (it couldn't even tell me the stack had overflowed) and was absolutley useless for this debugging exercise. On top of that, the reason I got into this mess was that windoze doesn't have a working fstat() function which can return file lengths > 2 Gig. It HAS a fscking _fstati64() but the file length value is only updated AFTER the bloody file is closed. That makes it completely useless. How the hell do people stand working on this crap excuse of an OS? 2003-04-29 Erik de Castro Lopo * Win32/unistd.h src/file_io.c Moved definitions of S_IGRP etc from file_io.c to unistd.h so that these can be used in the test programs. * Win32/libsndfile.def Added sf_open_fd. * Win32/sndfile.h Updated to match src/sndfile.h.in. * Win32/Makefile.msvc Added dither.c and htk.c to libsndfile.dll target. 2003-04-28 Erik de Castro Lopo * src/file_io.c First attempt at getting the Win32 versions of the these functions working. They still need to be tested. 2003-04-27 Erik de Castro Lopo * src/strings.c Found and fixed a bug which was causing psf_store_string() to fail on Motorola 68k processors. Many thanks to Joshua Haberman (Debian maintainer of libsndfile) for compiling and running debug code to help me debug the problem. 2003-04-26 Erik de Castro Lopo * src/sndfile.c src/file_io.c src/wav.c src/aiff.c Much hacking to get reading and writing of embedded files working (ie sound files at a non-zero files offset). * doc/embedded_files.html First pass atempt at documenting reading/writing embedded files. 2003-04-21 Erik de Castro Lopo * doc/FAQ.html Updated answer to "Why doesn't libsndfile do interleaving/de-interleaving?" 2003-04-19 Erik de Castro Lopo * src/wav.c src/aiff.c Fix retrieving and storing of string data from files. Need to be careful about using psf->buffer for strings. 2003-04-18 Erik de Castro Lopo * src/file_io.c Fix psf_fseek() for seeks withing embedded files. 2003-04-15 Erik de Castro Lopo * src/sndfile.h.in Changed the definition of SNDFILE slightly to produce warnings when it isn't used correctly. This should have zero affect in code which uses the SNDFILE type correctly. * src/sndfile.c Fixed a few compiler warnings cause by the changes to the SNDFILE type. 2003-04-12 Erik de Castro Lopo * doc/FAQ.html Added question and answer to the question "How about adding the ability to write/read sound files to/from memory buffers?". 2003-04-08 Erik de Castro Lopo * tests/write_read_test.tpl Removed un-needed enums declaring TRUE and FALSE and replaced usage of these with SF_TRUE and SF_FALSE. * tests/multi_file_test.c New test program to test sf_open_fd() on files containing data other than a single sound file. 2003-04-06 Erik de Castro Lopo * src/file_io.c When creating files, set the readable by others flag. This still allows further restrictions to be enforced by use of the user's umask. Fix suggested by Eric Lyon. 2003-04-05 Erik de Castro Lopo * src/sndfile.h.in src/sndfile.c Changed sf_open_fd(). Dropped offset parameter and added a close_desc parameter. If close desc is TRUE, the file descritpor passed into the library will be closed when sf_close() is called. * tests/utils.tpl Modified call to sf_open_fd() to set close_desc parameter to SF_TRUE. 2003-04-04 Erik de Castro Lopo * tests/write_read_test.tpl Add a string (using sf_set_string() function) before and after data section of all files. This will make sure that if string data can be added, it doesn't overwrite real audio data. 2003-04-02 Erik de Castro Lopo * src/sndfile.c Started work on supporting a non-zero offset parameter for sf_open_fd (). * src/.c Removed many uses of psf_fseek (SEEK_END) which to allow for future use of sf_open_fd() with non-zero offset. Associated refactoring. * src/aiff.c Implemented functionality required to get sf_get_string() and sf_set_string() working for AIFF files. 2003-04-01 Erik de Castro Lopo * tests/utils.tpl Modified test_open_file_or_die() to alternately use sf_open() and sf_open_fd(). * src/svx.c Fixed a bug which occurred when openning an existing file for read/write using sf_open_fd(). In this case, the existing NAME chunk needs to be read into psf->filename. Fixed printing of sf_count_t types to logbuffer. 2003-03-31 Erik de Castro Lopo * src/sndfile.h.in Added prototype for new function sf_open_fd(). * src/sndfile.c Moved most of the code in sf_open() to a new function psf_open_file(). Created new function sf_open_fd() which also uses psf_open_file() but does not currently support the offset parameter. * doc/api.html Document sf_open_fd(). 2003-03-09 Erik de Castro Lopo * src/sndfile.c Fixed a memory leak reported by Evgeny Karpov. Memory leak only occurred when an attempt was made to read and the open() call fails. 2003-03-08 Erik de Castro Lopo * tests/open_fail_test.c New test program to check for memory leaks when sf_open fails on a valid file. Currently this must be run manually under valgrid. * tests/Makefile.am Hook new test program into build. 2003-03-03 Erik de Castro Lopo * Octave/sndfile_save.m Octave/sndfile_play.m Added a -mat-binary option to the octave save command to force the output to binary mode even if the user has set ascii data as the default. Found by Christopher Moore. 2003-02-27 Erik de Castro Lopo * doc/dither.html New file which will document the interface which allows the addition of audio dither when sample word sizes are being reduced. * src/dither.c More work. 2003-02-26 Erik de Castro Lopo * tests/misc_test.c In update_header_test(), make HTK files a special case. * doc/index.html Added HTK to the feature matrix. 2003-02-25 Erik de Castro Lopo * src/htk.c New file for reading/writing HMM Tool Kit files. * src/sndfile.h.in src/sndfile.c src/command.c src/Makefile.am Hook in htk.c * tests/write_read_test.tpl tests/misc_test.c tests/Makefile.am Add tests for HTK files. 2003-02-22 Erik de Castro Lopo * src/wav.c Fixed a bug where the LIST chunk length was being written incorrectly. * tests/string_test.c Added call to check_log_buffer(). Minor cleanups. 2003-02-10 Erik de Castro Lopo * src/wav_w64.h Applied patch from Antoine Mathys to add extra WAV format definitions and a G72x_ADPCM_WAV_FMT struct definition. * src/wav_w64.c Applied patch from Antoine Mathys which converts wav_w64_format_str() from one huge inefficient switch statement to a binary search. * tests/string_test.c Dump log buffer if tests fail. 2003-02-07 Erik de Castro Lopo * tests/string_test.c David Viens supplied some modifications to this file which showed up a bug when using sf_set_string() and the sf_writef_float() functions. * src/sndfile.c Fixed the above bug. 2003-02-06 Erik de Castro Lopo * doc/FAQ.html Added Q and A on how to detect libsndfile in configure.in (at the suggestion of Davy Durham). 2003-02-05 Erik de Castro Lopo * src/sndfile.h.in Add enums and typedefs for dither. Deprecate SFC_SET_ADD_DITHER_ON_WRITE and SFC_SET_ADD_DITHER_ON_READ, to be replaced with SFC_SET_DITHER_ON_WRITE and SFC_SET_DITHER_ON_READ which will allow different dither algorithms to be enabled. Added SFC_GET_DITHER_INFO_COUNT and SFC_GET_DITHER_INFO. * src/sndfile.h.in src/Version_script.in Win32/libsndfile.def. Added public sf_dither_*() functions. * src/sndfile.c Implement commands above. * src/dither.c More work. Framework and external hooks into dither algorithms complete. 2003-02-03 Erik de Castro Lopo * doc/version-1.html libsndfile_version_convert.py Remove redundant files. * doc/index.html doc/api.html Remove links to version-1.html. * src/dither.c New file to allow the addition of audio dither on input and output. * src/common.h Add prototype for dither_init() function. * Makefile.am doc/Makefile.am Changes for added and removed files. 2003-02-02 Erik de Castro Lopo * Win32/Makefile.msvc Changes to force example binaries to be placed in the top level directory instead of the examples/ directory. Add src/strings.c and src/xi.c to the build. Add string_test to build and to tests on WAV files. * doc/index.html Added XI to support matrix. 2003-01-27 Erik de Castro Lopo * src/sndfile.h.in Added prototypes for sf_get_string() and sf_set_string() and SF_STR_* enum values. * src/sndfile.c Added public interface to sf_get_string() and sf_set_string(). * src/wav.c Added code for setting and getting strings in WAV files. * tests/string_test.c New test program for sf_get_string() and sf_set_string() functionality. * tests/Makefile.am Hook new test program into build and test framework. 2003-01-26 Erik de Castro Lopo * src/common.h Added fields to SF_PRIVATE for string data needed to implement sf_get_string() and sf_set_string(). * src/strings.c New file for storing and retrieving strings to/from files. * src/Makefile.am Added strings.c to build. 2003-01-25 Erik de Castro Lopo * src/xi.c Read seems to be working so looking at write. * src/sndfile.h.in Added SF_FORMAT_XI, SF_FORMAT_DPCM_8 and SF_FORMAT_DPCM_16 enum values. * tests/floating_point_test.c tests/lossy_comp_test.c tests/Makefile.am Added test for 8 and 16 bit XI format files. 2003-01-24 Erik de Castro Lopo * doc/index.html Added a non-lawyer readable summary of the licensing provisions as suggested by Steve Dekorte. 2003-01-23 Erik de Castro Lopo * src/wav.c Fixed a compiler warning found by Alexander Lerch. 2003-01-18 Erik de Castro Lopo * configure.ac Fixed the multiple linking of libm. 2003-01-17 Erik de Castro Lopo * Win32/Makefile.mcvs Added comments on the correct way to set up the MSVCDir environment variable. * doc/win32.html Add on how to set up the MSVCDir environment variable. 2003-01-15 Erik de Castro Lopo * examples/sndfile-play.c examples/sndfile-info.c When run on Win32 without any command line parameters print a message and then sleep for 5 seconds. This means the when somebody double clicks on these programs in explorer the user will actually see the message. 2003-01-14 Erik de Castro Lopo * tests/misc_test.c Bypass permission test if running as root because root is allowed to open a readonly file for write. 2003-01-08 Erik de Castro Lopo * Win32/Makefile.msvc Added pvf.c and xi.c source files to project. * src/sndfile.h Updated for PVF files. 2003-01-07 Erik de Castro Lopo * src/sndfile.c Modified validate_sfinfo() to force samplerate, channels and sections to be >= 1. In format_from_extension() replaced calls to does_extension_match() with strcmp(). * src/xi.c More work. 2003-01-06 Erik de Castro Lopo * doc/Makefile.am Added octave.html which had been left out. Found by Jan Weil. 2003-01-05 Erik de Castro Lopo * src/pvf.c src/common.h src/sndfile.c Fixed error handling for PVF files. * src/xi.c New file for handling Fasttracker 2 Extended Instrument files. Not working yet and included when configured with --enable-experimental. * src/sndfile.c src/common.h Hooked in new file xi.c. 2002-12-30 Erik de Castro Lopo * src/rx2.c Added a patch from Marek Peteraj which sheds a little more light on the slices within an RX2 file. Still need to find out data encoding. 2002-12-20 Erik de Castro Lopo * src/wav.c Started work on decoding 'acid' and 'strc' chunks. 2002-12-14 Erik de Castro Lopo * tests/peak_check_test.c Minor cleanup. 2002-12-12 Erik de Castro Lopo * tests/write_read_test.tpl Added check to make sure no error was generated when an attempt was made to read past the end of the file. 2002-12-11 Erik de Castro Lopo * doc/lists.html Added "mailto" links for all three lists. * src/pvf.c New file for Portable Voice Format files. * src/sndfile.h.in src/sndfile.c src/common.h src/command.c src/Makefile.am Added hooks for SF_FORMAT_PVF format files. * tests/write_read_test.tpl tests/std*.c Add tests for SF_FORMAT_PVF. * doc/index.html Add PVF to the compatibility matrix. * src/pcm.c src/alaw.c src/ulaw.c src/float32.c src/double64.c Previously, attempts to read beyond the end of a file would set psf->error to SFE_SHORT_ERROR. This behaviour diverged from the behaviour of the POSIX read() call but has now been fixed. Attempts to read beyond the end of the file will return a short read count but will not longer set any error. 2002-12-09 Erik de Castro Lopo * src/sndfile.c Add more sanity checking when opening a RAW file for read. When format is not RAW, zero out all members of the SF_INFO struct. * tests/raw_test.c Add bad_raw_test() to check for above problem. * tests/stdin_test.c examples/sndfile-info.c Set the format field of the SF_INFO struct to zero before calling sf_open(). * doc/api.html Add information about the need to set the format field of the SF_INFO struct to zero when opening non-RAW files for read. * configure.ac Removed use of conversion script on Solaris. Not all Solaris versions support it. * doc/lists.html New file containg details of the mailing lists. * doc/index.html Add a link to the above new file. 2002-12-04 Erik de Castro Lopo * tests/dft_cmp.c Fixed a SIGFPE on Alpha caused by a log10 (0.0). Thanks to Joshua Haberman for providing the gdb traceback. 2002-11-28 Erik de Castro Lopo * src/wav.c Added more capabilities to 'smpl' chunk parser. * src/sndfile.c Fixed some (not all) possible problems found with Flawfinder. 2002-11-24 Erik de Castro Lopo * src/sndfile.c Fixed a bug in sf_seek(). This bug could only occur when an attempt was made to read beyond the end and then sf_seek() was called with a whence parameter of SEEK_CUR. * src/file_io.c Win32's _fstati64() does not work, it returns BS. Re-implemented psf_get_filelen() in terms of psf_fseek(). * tests/write_read_test.tpl Add a test to detect above bug. * src/float_cast.h Modification to prevent compiler warnings on Mac OS X. * src/file_io.c Fixes for windows (what a f**ked OS). 2002-11-08 Erik de Castro Lopo * configure.ac Disable use of native lrint()/lrintf() on Mac OSX. These functions exist on Mac OSX 10.2 but not on 10.1. Forcing the use of the versions in src/float_cast.h means that a library compiled on 10.2 will still work on 10.1. 2002-11-06 Erik de Castro Lopo * configure.in configure.ac Renamed configure.in to configure.ac as expected by later versions of autoconf. Slight hacking of configure.ac to work with version 2.54 of autoconf. Changed to using -dumpversion instead of --version for determining GCC version numer as suggested by Anand Kumria. * src/G72x/Makefile.am Slight hacking required for operation with automake 1.6.3. 2002-11-05 Erik de Castro Lopo * src/common.c In psf_binheader_readf() changed type parameter type "b" type from size_t to int to prevent errors on IA64 CPU where sizeof (size_t) != sizeof (int). Thanks to Enrique Robledo Arnuncio for debugging this. 2002-11-04 Erik de Castro Lopo * test/command_test.tpl Changed test value so test would pass on Solaris. * src/Version_script.in Modified version numbering so that later versions of 1.0.X can replace earlier versions without recompilation. * src/vox_adpcm.c Fixed bug causing short reads. 2002-11-03 Erik de Castro Lopo * test/floating_point_test.c Code cleanup using functions from util.c. Add test for IEEE replacement floats and doubles. 2002-11-01 Erik de Castro Lopo * src/wav.c Fixed a possible divide by zero error when read the 'smpl' chunk. Thanks to Serg Repalov for the example file. * tests/pcm_test.tpl Used sf_command (SFC_TEST_IEEE_FLOAT_REPLACE) to test IEEE replacement code. Clean up pcm_double_test(). * src/float32.c src/double64.c Force use of IEEE replacement code using psf->ieee_replace is TRUE, Print message to log_buffer as well. Rename all broken_read_* and broken_write* functions to replace_read_* and replace_write_*. * tests/util.tpl Added string_in_log_buffer(). * tests/pcm_test.tpl Use string_in_log_buffer() to ensure that IEEE replacement code has been used. * configure.in Removed --enable-force-broken-float option. IEEE replacement code is now always tested. 2002-10-31 Erik de Castro Lopo * src/double64.c Implement code for read/writing IEEE doubles on platforms where the native double format is not IEEE. * src/float32.c src/common.h Remove float32_read() and float32_write(). Replace with float32_le_read(), float32_be_read(), float32_le_write() and float32_be_write() to match stuff in src/double64.c. * src/common.c Fix all usage of float32_write(). * src/sndfile.h.in Added SFC_TEST_IEEE_FLOAT_REPLACE command (testing only). * src/common.h Added SF_PRIVATE field ieee_replace. * src/sndfile.c In sf_command() set/reset psf->ieee_replace. 2002-10-26 Erik de Castro Lopo * tests/pcm_test.tpl Fixed a problem when testing with --enable-force-broken-float. The test was generating a value of negative zero and the broken float code is not able to write negative zero. Removing the negative zero fixed the test. 2002-10-25 Erik de Castro Lopo * src/file_io.c Added fix for Cygwin (suggested by Maros Michalik). 2002-10-23 Erik de Castro Lopo * src/file_io.c Improved error detection and handling. * src/file_io.c src/common.h Removed functions psf_ferror() and psf_clearerr() which were redundant after above improvements. * src/aiff.c src/svx.c src/w64.c src/wav.c Removed all use of psf_ferror() and psf_clearerr(). * src/sndfile.c Removed #include of , , and which are no longer needed. * tests/misc_test.c Added test to make sure the correct error message is returned with an existing read-only file is openned for write. 2002-10-21 Erik de Castro Lopo * doc/index.html doc/api.html Updated for OKI Dialogic ADPCM files. * src/command.c Added VOX ADPCM to sub_fomats. 2002-10-20 Erik de Castro Lopo * src/vox_adpcm.c src/Makefile.am New file for handling OKI Dialogic ADPCM files. * src/sndfile.h Add new subtype SF_FORMAT_VOX_ADPCM. * src/sndfile.c Renamed function is_au_snd_file () to format_from_extenstion () and expanded its functionality to detect headerless VOX files. * src/raw.c Added hooks for SF_FORMAT_VOX_ADPCM. * examples/sndfile-info.c Print out file duration (suggested by Conrad Parker). * libsndfile.spec.in Force installation of sndfile.pc file (found by John Thompson). * tests/Makefile.am tests/lossy_comp_test.c tests/floating_point_test.c Add tests for SF_FORMAT_VOX_ADPCM. 2002-10-18 Erik de Castro Lopo * tests/misc_test.c Add test which attempts to write to /dev/full (on Linux anyway) to check for correct handling of writing to a full filesystem. * src/sndfile.c Return correct error message if the header cannot be written because the filesystem is full. * tests/util.tpl Corrected printing of file mode in error reporting. * src/mat5.c Fixed a bug where a MAT5 file written by libsndfile could not be opened by Octave 2.1.36. 2002-10-13 Erik de Castro Lopo * src/common.h src/file_io.c All low level file I/O have been modified to be better able to report system errors resulting from calling system level open/read/write etc. * src/*.c Updated for compatibility with above changes. * examples/cooledit-fixer.c New example program which fixes badly broken file created by Syntrillium's Cooledit which are marked as containing PCM samples but actually contain floating point data. * examples/Makefile.am Hooked cooledit-fixer into the build system. 2002-10-10 Erik de Castro Lopo * doc/command.html Document SFC_GET_FORMAT_INFO. 2002-10-09 Erik de Castro Lopo * examples/wav32_aiff24.c examples/sndfile2oct.c examples/sfhexdump.c examples/sfdump.c Removed these files because they weren't interesting. * examples/sfconvert.c examples/sndfile-convert.c Renamed the first to the latter. * examples/Makefile.am Added sndfile-convert to the bin_PROGRAMS, so it is installed when the lib is installed. Removed old programs wav32_aiff24 and sndfile2oct. * man/sndfile-convert.1 New man page. * examples/sndfile-convert.c Added some gloss now that sndfile-convert.c is an installed program. * src/sndfile.h.in src/sndfile.c src/common.h src/command.h Added command SFC_GET_FORMAT_INFO. * tests/command_test.c Added tests form SFC_GET_FORMAT_INFO. 2002-10-08 Erik de Castro Lopo * src/sndfile.c In sf_format_check() return error if samplerate < 0. 2002-10-07 Erik de Castro Lopo * src/aiff.c Fixed bug in handling of COMM chunks with a 4 byte encoding byte but no encoding string. 2002-10-06 Erik de Castro Lopo * src/sndfile.c Fixed repeated word in an error message. 2002-10-05 Erik de Castro Lopo * doc/index.html Improved advertising in Features section. 2002-10-04 Erik de Castro Lopo * src/wav.c Added decoding of 'labl' chunks within 'LIST' chunks. * src/common.h Added (experimental only) SF_FORMAT_OGG and SF_FORMAT_VORBIS and definition of ogg_open(). This is nowhere near working yet. * src/sndfile.c Added detection of 'OggS' file marker and added call to ogg_open() to switch statement. * src/ogg.c New file. Very early start of Ogg Vorbis support. * src/wav.c Added handling of brain-damaged and broken Cooledit "32 bit 24.0 float type 1" files. These files are marked as being 24 bit WAVE_FORMAT_PCM with a block alignment of 4 times the numbers of channels but are in fact 32 bit floating point. 2002-10-02 Erik de Castro Lopo * configure.in Modified option --enable-experimental to set ENABLE_EXPERIMENTAL_CODE in config.h to either 0 or 1. * src/sndfile.c Modify sf_command (SFC_GET_LIB_VERSION) to append "-exp" to the version string if experimental code has been enabled. 2002-10-01 Erik de Castro Lopo * src/Makefile.am Added -lm to libsndfile_la_LIBADD. This means that -lm is not longer needed in the link line when linking something to libsndfile. * tests/Makefile.am examples/Makefile.am Removed -lm from all link lines. * sndfile.pc.in Removed -lm from Libs line. 2002-09-24 Erik de Castro Lopo * src/file_io.c Removed all perror() calls. * src/nist.c Removed calls to exit() function. Added check to detect NIST files dammaged from Unix CR -> Win32 CRLF conversion process. 2002-09-24 Erik de Castro Lopo * src/sndfile.h.in src/sndfile.c New function sf_strerror() which will eventually replace functions sf_perror() and sf_error_str(). Function sf_error_number() has also been changed, but this was documented as being for testing only. * doc/api.html Documented above changes. * tests/*.c examples/*.c Changed to new error functions. 2002-09-22 Erik de Castro Lopo * configure.in Detect GCC version, and print a warning message about writeable strings it GCC major version number is less than 3. 2002-09-21 Erik de Castro Lopo * src/sndfile.h.in doc/api.html Documentation fixes. 2002-09-19 Erik de Castro Lopo * src/Version_script.in src/Makefile.am configure.in Use the version script to prevent the exporting of all non public symbols. This currently only works with Linux. Will test on Solaris as well. * src/float_cast.h Added #ifndef to prevent the #warning directives killing the SGI MIPSpro compiler. * src/au_g72x.c src/double64.c src/float32.c src/gsm610.c src/ima_adpcm.c src/ms_adpcm.c Fix benign compiler warnings arising from previously added compiler flags. 2002-09-18 Erik de Castro Lopo * src/sndfile.c Fixed a bug in sf_error_str() where errnum was used as the index instead of k. Found by Tim Hockin. * examples/sndfile-play.c Fixed a compiler warning resulting from a variable shadowing a previously defined local. 2002-09-17 Erik de Castro Lopo * src/sndfile.h.in src/sndfile.c Added command SFC_SET_RAW_START_OFFSET. * doc/command.html Document SFC_SET_RAW_START_OFFSET. * tests/raw_test.c tests/Makefile.am Add new file for testing SF_FORMAT_RAW specific functionality. * tests/dwvw_test.c Updates. 2002-09-16 Erik de Castro Lopo * src/wav.c Modified reading of 'smpl' chunk to take account of the sampler data field. * tests/utils.tpl tests/utils.h Added function print_test_name(). * tests/misc_test.c tests/write_read_test.tpl tests/lossy_comp_test.c tests/pcm_test.tpl tests/command_test.tpl tests/floating_point_test.c Convert to use function print_test_name(). 2002-09-15 Erik de Castro Lopo * doc/octave.html Added a link to some other Octave scripts for reading and writing sound files. * src/paf.c Change type of dummy data field to int. This should fix a benign compiler warning on some CPUs. Removed superfluous casts resulting from the above change. * src/rx2.c More hacking. 2002-09-14 Erik de Castro Lopo * src/mat5.c src/common.c Changed usage of snprintf() to LSF_SNPRINTF(). * Win32/Makefile.msvc Updated to include new files and add new tests. * Win32/config.h Win32/sndfile.h Updated. * doc/api.html Added note about the possibility of "missing" features actually being implemented as an sf_command(). 2002-09-13 Erik de Castro Lopo * tests/misc_test.c Added previously missing update_header_test and zero_data_tests for PAF, MAT4 and MAT5 formats. * src/paf.c src/mat4.c src/mat5.c Fixed bugs uncovered by new tests above. * src/mat5.c Generalised parsing of name fields of MAT5 files. * src/mat5.c src/sndfile.c Added support for unsigned 8 bit PCM MAT5 files. * tests/write_read_test.tpl Added test for unsigned 8 bit PCM MAT5 files. * doc/index.html Added unsigned 8 bit PCM MAT5 to capabilities matrix. 2002-09-12 Erik de Castro Lopo * test/update_header_test.c tests/misc_test.c Renamed update_header_test.c to misc_test.c. Added zero_data_test() to check for case where file is opened for write and closed immediately. The resulting file can be left in a state where libsndfile cannot open it. Problem reported by Werner Schweer, the author of Muse. * src/aiff.c Removed superfluous cast. * src/wav.c src/svx.c Fixed case of file generated with no data. Removed superfluous cast. * src/sndfile.c Fixed error on IA64 platform caused by incorrect termination of SndfileErrors struct array. This problem was found in the Debian buildd logs (http://buildd.debian.org/). * configure.in Added Octave directory. * Octave/Makefile.ma New Makfile.am for Octave directory. * Octave/sndfile_load.m Octave/sndfile_save.m Octave/sndfile_play.m New files for working with Octave. * doc/octave.html Document explaining the use of the above three Octave scripts. 2002-09-10 Erik de Castro Lopo * src/sndfile.c Fixed bug in RDWR mode. 2002-09-09 Erik de Castro Lopo * src/common.c Fixed psf_get_date_str() for systems which don't have gmtime_r() or gmtime(). * src/file_io.c Added #include for Win32. Reported by Koen Tanghe. 2002-09-08 Erik de Castro Lopo * src/common.c Added 'S' format specifier for psf_binheader_writef() which writes a C string, including single null terminator to the header. Added 'j' format specifier to allow jumping forwards or backwards in the header. Added function psf_get_date_str(). * src/mat5.c Complete read and write support. * doc/index.html Added entries for MAT4 and MAT5 in capabilities matrix. 2002-09-06 Erik de Castro Lopo * src/mat4.c Completed read and write support. * src/common.h src/sndfile.c Added MAT4 and MAT5 specific error messages. * tests/write_read_test.tpl tests/Makefile.am Added tests for MAT4 and MAT5 files. * tests/stdio_test.c tests/stdout_test.c tests/stdin_test.c Added tests for MAT4 and MAT5 files. 2002-09-05 Erik de Castro Lopo * src/command.c Added elements for SF_FORMAT_MAT4 and SF_FORMAT_MAT5 to major_formats array. * examples/sfconvert.c Added mat4 and mat5 output targets. 2002-09-04 Erik de Castro Lopo * src/sndfile.c Added check to prevent errors openning read only formats for read/write. * src/interleave.c New file for interleaving non-interleaved data. Non-interleaved data is only supported on read. * src/Makefile.am Added src/interleave.c to build. 2002-09-03 Erik de Castro Lopo * src/double64.c src/common.h Added double64_be_read(), double64_le_read(), double64_be_write() and double64_le_write() which replace double64_read() and double64_write(). * src/common.c Cleanup of psf_binheader_readf() and add ability to read big and little endian doubles (required by mat4.c and mat5.c). Add ability for psf_binheader_writef() to write doubles to sound file headers. 2002-09-01 Erik de Castro Lopo * src/mat5.c New file for reading Matlab (tm) version 5 data files. This is also the native binary file format for version 2.1.X of GNU Octave which will be used for testing. Not complete yet. * src/mat4.c New file for reading Matlab (tm) version 4.2 data files. This is also the native binary file format for version 2.0.X of GNU Octave which will be used for testing. Not complete yet. * src/sndfile.h.in src/sndfile.c src/common.h src/command.c src/Makefile.am Mods to add Matlab files. * src/common.[ch] Added readf_endian field to SF_PRIVATE struct allowing endianness to remembered across calls to sf_binheader_readf(). Fixed bug in width_specifier behaviour for printing hex values. 2002-08-31 Erik de Castro Lopo * src/file_io.c Check return value of close() call in psf_fclose(). 2002-08-24 Erik de Castro Lopo * src/ms_adpcm.c Commented out some code where 0x10000 was being subtracted from a short and the result assigned to a short again. Andrew Zaja found this. 2002-08-23 Erik de Castro Lopo * doc/command.html Fixed typo found by Tommi Ilmonen. * src/ima_adpcm.c Changed type of diff from short to int to prevent errors which can occur during very rare circumstances. Thanks to FUWAFUWA. 2002-08-16 Erik de Castro Lopo * tests/floating_point_test.c Disable testing on machines without lrintf(). * Win32/Makefile.msvc Added dwd.c and wve.c to build. * configure.in Bumped version to 1.0.0. 2002-08-15 Erik de Castro Lopo * src/file_io.c Add a #include for Mac OS 9. Thanks to Stephane Letz. * src/wav.c Changed an snprintf to LSF_SNPRINTF. * doc/Makefile.am Added version-1.html. 2002-08-14 Erik de Castro Lopo * configure.in Bumped version to 1.0.rc6. * src/*.c Modified scaling of normalised floats and doubles to integers. Until now this has been done by multiplying by 0x8000 for short output, 0x80000000 for 32 bit ints and so on. Unfortunately this can cause an overflow and wrap around in the target value. All thes values have therefore been reduced to 0x7FFF, 0x7FFFFFFF and so on. The conversion from ints to normalised floats and doubles remains unchanged. This does mean that for repeated conversions normalised float -> pcm16 -> normalised float would result in a decrease in amplitude of 0x7FFF/0x8000 on every round trip. This is undesirable but less undesireable than the wrap around I am trying to avoid. * tests/floating_point_test.c Removed file hash checking because new float scaling procedure introduced above prevented the ability to crate a has on both x86 and PowerPC systems. 2002-08-13 Erik de Castro Lopo * src/txw.c Completed reading of TXW files. Seek doesn't work yet. * src/file_io.c Added a MacOS 9 replacement for ftruncate(). * MacOS/sndfile.h Added MacOS 9 header file. This should be copied into src/ to compile libsndfile for MacOS9. 2002-08-12 Erik de Castro Lopo * src/sndfile.c Fixed commands SF_SET_NORM_DOUBLE and SFC_SET_NORM_FLOAT to return their values after being set. Reported by Jussi Laako. * configure.in If autogen is not found, touch all .c and .h files in tests/. * src/common.c Added format width specifier to psf_log_printf() for %u, %d, %D and %X. * src/dwd.c Completed implementation of read only access to these files. * src/common.h src/*.c src/pcm.c Removed redundant field chars from SF_PRIVATE struct and modified pcm_init() to do without it. 2002-08-11 Erik de Castro Lopo * src/wve.c New file implementing read of Psion Alaw files. This will be a read only format. Implementation complete. * src/dwd/c Started implementation of DiamondWare Digitized files. Also read only, not complete. * src/wav.c Add parsing of 'smpl' chunk. * src/paf.c Fixed reading on un-normalized doubles and floats from 24 bit PAF files. This brings it into line with the reading of 8 bit files into un-normalized doubles which returns values in the range [-128, 127]. * src/common.c Modified psf_log_printf() to accept the %% conversion specifier to allow printing of a single '%'. * src/sds.c Read only of 16 bit samples is working. Need to build a test harness for this and other read only formats. 2002-08-10 Erik de Castro Lopo * configure.in Added --enable-experimental configure option. Removed pkg-config message at the end of the configure process. * src/sds.c src/txw.c src/rx2.c src/sd2.c Moved all the code in these files inside #if ENABLE_EXPERIMENTAL_CODE blocks and added new *_open() function for the case where experimental is not enabled. These new functions just return SFE_UNIMPLMENTED. * Win32/sndfile.h src/sndfile.h.in src/common.h Removed un-necessary #pragma pack commands. * src/file_io.c Implemented psf_ftruncate() and much other hacking for Win32. * Win32/Makefile.msvc Updated. * doc/win32.html Updated to include the copying of the sndfile.h file from the Win32/ directory to the src/ directory. * Make.bat Batch file to make compiling on Wi32 a little easier. Implements "make" and "make check". 2002-08-09 Erik de Castro Lopo * src/file_io.c Add place holder for ftruncate() on Win32 which doesn't have ftruncate(). This will need to be fixed later. * src/sndfile.h.in New file (copy of sndfile.h) with sets up @TYPEOF_SF_COUNT_T@ which will be replaced by the correct type during configure. * configure.in Modified to find a good type for TYPEOF_SF_COUNT_T. * src/aiff.c Fixed a bug when reading malformed headers. * src/common.c Set read values to zero before performing read. 2002-08-08 Erik de Castro Lopo * doc/command.html Fixed some HTML tags which were not allowing jumps to links within the page. * src/sds.c Massive hacking on this. * src/wav.c Added recognition of 'clm ' tag. 2002-08-07 Erik de Castro Lopo * doc/index.html Added beginning of a capabilities list beyond simple file formats which can be read/written. * src/aiff.c Added parsing of INST and MARK chunks of AIFF files. At the moment this data is simply recorded in the log buffer. Later it will be possible to read this data from an application using sf_command(). * src/wav.c Added parsing of 'cue ' chunk which contains loop information in WAV files. * exampes/sndfile-info.c Changed reporting of Samples to Frames. * src/wav.c src/w64.c src/aiff.c src/wav_w64.h Moved from a samples to a frames nomenclature to avoid confusion. * doc/FAQ.html What's the best format for storing temporary files? * src/sds.c New file for reading/writing Midi Sample Dump Standard files. * src/Makefile.am src/sndfile.c src/common.[ch] Added hooks for sds.c. * examples/sndfile-info.c Changed from using sf_perror() to using sf_error_str(). 2002-08-06 Erik de Castro Lopo * doc/api.html Added explanation of mode parameter for sf_open(). Added explanation of usage of SFM_* values in sf_seek(). * src/sndfile.[ch] src/command.c src/file_io.c src/common.h Implemented SFC_FILE_TRUNCATE to allow a file to be truncated. File truncation was suggested by James McCartney. * src/command.html Documented SFC_FILE_TRUNCATE. * tests/command_test.c Add tests for SFC_FILE_TRUNCATE. * src/sndfile.c Added a thrid parameter to the VALIDATE_SNDFILE_AND_ASSIGN_PSF macro to make resetting the error number optional. All uses of the macro other than in error reporting functions were changed to reset the error number. * src/pcm.c Fixed a bug were sf_read_* was logging an SFE_SHORT_READ even when no error occurred. * tests/write_read_test.tpl Added tests of internal error state. 2002-08-05 Erik de Castro Lopo * src/GSM610/private.h src/GSM610/*.c src/GSM610/Makefile.am Renamed private.h to gsm610_priv.h to prevent clash with other headers named private.h in other directories. (Probably only a problem on MacOS 9). * src/G72x/private.h src/G72x/*.c src/G72x/Makefile.am Renamed private.h to g72x_priv.h to prevent clash with other headers named private.h in other directories. (Probably only a problem on MacOS 9). * MacOS/config.h Changed values of HAVE_LRINT and HAVE_LRINTF to force use of code in float_cash.h. * src/sndfile.h Changes the name of samples field of the SF_INFO to frames. The old name had caused too much confusion and it simply had to be changed. There will be at least one more pre-release. 2002-08-04 Erik de Castro Lopo * doc/index.html Updated formats matrix to include RAW (header-less) GSM 6.10. Fix specificaltion of table and spelling mistakes. * src/sndfile.c src/command.c Fixed bug in SFC_CALC_MAX_SIGNAL family and psf_calc_signal_max (). * tests/command.c Removed cruft. Added test for SFC_CALC_MAX_SIGNAL and SFC_CALC_NORM_MAX_SIGNAL. * configure.in Update version to 1.0.0rc5. * sfendian.h Removed inclusion of un-necessary header. 2002-08-03 Erik de Castro Lopo * src/aiff.c Minor fixes of info written to log buffer. * src/float_cast.h Add definition of HAVE_LRINT_REPLACEMENT. * tests/floating_point_test.c Fix file hash check on systems without lrint/lrintf. * tests/dft_cmp.c Limit SNR to less than -500.0dB. * examples/sndfile2oct.c Fixed compiler warnings. * doc/api.html Fixed error where last parameter of sf_error_str() was sf_count_t instead of size_t. 2002-08-02 Erik de Castro Lopo * doc/FAQ.html Why doesn't libsndfile do interleaving/de-interleaving. * tests/pcm_test.tpl On Win32 do not perform hash check on files containing doubles. 2002-08-01 Erik de Castro Lopo * src/common.h Defined SF_COUNT_MAX_POSITIVE() macro, a portable way of setting variables of type sf_count_t to their maximum positive value. * src/dwvw.c src/w64.c Used SF_COUNT_MAX_POSITIVE(). 2002-07-31 Erik de Castro Lopo * src/paf.c Fixed bug in reading/writing of 24 bit PCM PAF files on big endian systems. * tests/floating_point_tests.c Fixed hash values for 24 bit PCM PAF files. Disabled file has check if lrintf() function is not available and added warning. Decreased level of signal from a peak of 1.0 to a value of 0.95 to prevent problems on platforms without lrintf() ie Solaris. 2002-07-30 Erik de Castro Lopo * src/wav.c Fixed a problem with two different kinds of mal-formed WAV file header. The first had the 'fact' chunk before the 'fmt ' chunk, the other had an incomplete 'INFO' chunk at the end of the file. * src/w64.c Added fix to allow differentiation between W64 files and ACID files. * src/au_g72x.c src/common.h src/sndfile.c Added error for G72x encoded files with more than one channel. * tests/pcm_test.tpl tests/utils.tpl Moved function check_file_hash_or_die() to utils.tpl. Function was then modified to calculate the has of the whole file. * src/wav.c Fixed problem writing the 'fact' chunk on big endian systems. * tests/sfconvert.c Fixed bug where .paf files were being written as Sphere NIST. 2002-07-29 Erik de Castro Lopo * src/voc.c Fix for reading headers generated using SFC_UPDATE_HEADER_NOW. * doc/command.html Add docs for SFC_UPDATE_HEADER_NOW and SFC_SET_UPDATE_HEADER_AUTO. 2002-07-28 Erik de Castro Lopo * man/sndfile-info.1 man/sndfile-play.1 Added manpages supplied by Joshua Haberman the Debian maintainer for libsndfile. Additional tweaks by me. * configure.in man/Makefile.am Hooked manpages into autoconf/automake system. * src/sndfile.c Added hooks for SFC_SET_UPDATE_HEADER_AUTO. * tests/update_header_test.c Improved rigor of testing. * src/*.c Fixed problem with *_write_header() functions. 2002-07-27 Erik de Castro Lopo * doc/*.html Updates to documentation to fix problems found by wdg-html-validator. * src/common.h src/command.c Added normalize parameter to calls to psf_calc_signal_max() and psf_calc_max_all_channels(). * src/sndfile.c Added handling for commands SFC_CALC_NORM_SIGNAL_MAX and SFC_CALC_NORM_MAX_ALL_CHANNELS. * doc/command.html Added entry for SFC_CALC_NORM_SIGNAL_MAX and SFC_CALC_NORM_MAX_ALL_CHANNELS. 2002-07-26 Erik de Castro Lopo * examples/sndfile-play.c Win32/Makefile.msvc Get sndfile-play program working on Win32. The Win32 PCM sample I/O API sucks. The sndfile-play program now works on Linux, MacOSX, Solaris and Win32. 2002-07-25 Erik de Castro Lopo * doc/FAQ.html New file for frequently asked questsions. 2002-07-22 Erik de Castro Lopo * doc/api.html Documentation fixes. * src/au.[ch] src/au_g72x.c src/G72x/g72x.h Add support of 40kbps G723 ADPCM encoding. * tests/lossy_comp_test.c tests/floating_point_test.c Add tests for 40kbps G723 ADPCM encoding. * doc/index.html Update support matrix. 2002-07-21 Erik de Castro Lopo * doc/command.html Documented SFC_GET_SIMPLE_FORMAT_COUNT, SFC_GET_SIMPLE_FORMAT, SFC_GET_FORMAT_* and SFC_SET_ADD_PEAK_CHUNK. * src/sndfile.c src/pcm.c Add ability to turn on and off the addition of a PEAK chunk for floating point WAV and AIFF files. * src/sndfile.[ch] src/common.h src/command.c Added sf_command SFC_CALC_MAX_ALL_CHANNELS. Implemented by Maurizio Umberto Puxeddu. * doc/command.html Docs for SFC_CALC_MAX_ALL_CHANNELS (assisted by Maurizio Umberto Puxeddu). 2002-07-18 Erik de Castro Lopo * src/sndfile.c src/gsm610.c Finalised support for GSM 6.10 AIFF files and added support for GSM 6.10 encoded RAW (header-less) files. * src/wav.c Add support for IBM_FORMAT_MULAW and IBM_FORMAT_ALAW encodings. * src/api.html Fixed more documentation bugs. 2002-07-17 Erik de Castro Lopo * src/sndfile.h src/common.h Moved some yet-to-be-implelmented values for SF_FORMAT_* from the public header file sndfile.h to the private header file common.h to avoid confusion about the actual capabilities of libsndfile. 2002-07-16 Erik de Castro Lopo * src/aiff.c src/wav.c Fixed file parsing for WAV and AIFF files containing non-audio data after the data chunk. * src/aiff.c src/sndfile.c Add support for GSM 6.10 encoded AIFF files. * tests/lossy_comp_test.c tests/Makefile.am Add tests for GSM 6.10 encoded AIFF files. * src/*.c Fix compiler warnings. 2002-07-15 Erik de Castro Lopo * tests/command_test.c For SFC_SET_NORM_* tests, change the file format from SF_FORMAT_WAV to SF_FORMAT_RAW. * src/sndfile.c Added sf_command(SFC_TEST_ADD_TRAILING_DATA) to allow testing of reading from AIFF and WAV files with non-audio data after the audio chunk. * src/common.h Add test commands SFC_TEST_WAV_ADD_INFO_CHUNK and SFC_TEST_AIFF_ADD_INST_CHUNK. When these commands are working, they will be moved to src/sndfile.h * src/aiff.c src/wav.c Begin implementation of XXXX_command() hook for sf_command(). * tests/write_read_test.tpl Added sf_command (SFC_TEST_ADD_TRAILING_DATA) to ensure above new code was working. 2002-07-13 Erik de Castro Lopo * tests/update_header_test.c Allow read sample count == write sample count - 1 to fix problems with VOC files. * tests/write_read_test.tpl tests/pcm_test.tpl Fixed some problems in the test suite discovered by using Valgrind. 2002-07-12 Erik de Castro Lopo * tests/utils.[ch] tests/*.c Renamed check_log_buffer() to check_log_buffer_or_die(). * src/sndfile.c SFC_UPDATE_HEADER_NOW and SFC_SETUPDATE_HEADER_AUTO almost finished. Works for all file formats other than VOC. 2002-07-11 Erik de Castro Lopo * src/sndfile.[ch] src/common.h Started adding functionality to allow the file header to be updated before the file is closed on files open for SFM_WRITE. This was requested by Maurizio Umberto Puxeddu who is using libsndfile for file I/O in iCSound. * tests/update_header_test.c New test program to test that the above functionality is working correctly. * tests/peak_chunk_test.c tests/floating_point_test.c Cleanups. 2002-07-10 Erik de Castro Lopo * src/sfendian.[ch] Changed length count parameters for all endswap_XXX() functions from sf_count_t (which can be 64 bit even on 32 bit architectures) to int. These functions are only called frin inside the library, are always called with integer parameters and doing the actual calculation on 64 bit values is slow in comparision to doing it on ints. * examples/sndfile-play.c More playback hacking for Win32. 2002-07-09 Erik de Castro Lopo * src/common.c In psf_log_printf(), changed %D format conversion specifier to %M (marker) and added %D specifier for printing the sf_count_t type. * src/*.c Changed all usage of psf_log_printf() with %D format conversion specifiers to use %M conversion instead. * tests/pcm_test.tpl tests/pcm_test.def New files to autogen pcm_test.c. * src/pcm.c Fixed bug in scaling floats and doubles to 24 bit PCM and vice versa. 2002-07-08 Erik de Castro Lopo * configure.in Fix setup of $ac_cv_sys_largefile_CFLAGS so that sndfile.pc gets valid values for CFLAGS. * examples/sndfile-play.c Start adding playback support for Win32. 2002-07-07 Erik de Castro Lopo * src/*.c Worked to removed compiler warnings. Extensive refactoring. * src/common.[ch] Added function psf_memset() which works like the standard C function memset but takes and sf_count_t as the length parameter. * src/sndfile.c Replaced calls to memset(0 with calls to psf_memset() as required. 2002-07-06 Erik de Castro Lopo * src/sndfile.c Added "libsndfile : " to the start of all error messages. This was suggested by Conrad Parker author of Sweep ( http://sweep.sourceforge.net/ ). * src/sfendian.[ch] Added endswap_XXXX_copy() functions. * src/pcm.c src/float32.c src/double64.c Use endswap_XXXX_copy() functions and removed dead code. Cleanups and optimisations. 2002-07-05 Erik de Castro Lopo * src/sndfile.c src/sndfile.h Gave values to all the SFC_* enum values to allow better control of the interface as commands are added and removed. Added new command SFC_SET_ADD_PEAK_CHUNK. * src/wav.c src/aiff.c Modified wav_write_header and aiff_write_header to make addition of a PEAK chunk optional, even on floating point files. * tests/benchmark.tpl Added call to sf_command(SFC_SET_ADD_PEAK_CHUNK) to turn off addition of a PEAK chunk for the benchmark where we are trying to miximize speed. * src.pcm.c Changed tribyte typedef to something more sensible. Further conversion speed ups. 2002-07-03 Erik de Castro Lopo * src/command.c In major_formats rename "Sphere NIST" to "NIST Sphere". * src/common.c src/sfendian.c Moved all endswap_XXX_array() functions to sfendian.c. These functions will be tweaked to provide maximum performance. Since maximum performance on one platform does not guarantee maximum performance on another, a small set of functions will be written and the optimal one chosen at compile time. * src/common.h src/sfendian.h Declarations of all endswap_XXX_array() functions moved to sfendian.h. * src/Makefile.am Add sfendian.c to build targets. 2002-07-01 Erik de Castro Lopo * src/pcm.c src/sfendian.h Re-coded PCM encoders and decoders to match or better the speed of libsndfile version 0.0.28. 2002-06-30 Erik de Castro Lopo * src/wav.c Add checking for WAVPACK data in standard PCM WAV file. Return error if found. This WAVPACK is *WAY* broken. It uses the same PCM WAV file header and then stores non-PCM data. * tests/benchmark.tpl Added more tests. 2002-06-29 Erik de Castro Lopo * tests/benchmark.tpl Added conditional definition of M_PI. For Win32, set WRITE_PERMS to 0777. * Win32/Makefile.msvc Added target to make generate program on Win32. * src/samplitude.c Removed handler for Samplitude RAP file format. This file type seems rarer than hens teeth and is completely undocumented. * src/common.h src/sndfile.c src/Makefile.am Win32/Makefile.msvc Removed references to sampltiude RAP format. * tests/benchmark.tpl Benchmark program now prints the libsndfile version number when run. This program was also backported to version 0 to compare results. Version 1.0.0rc2 is faster than version 0.0.28 on most conversions but slower on some. The slow ones need to be fixed before final release. 2002-06-28 Erik de Castro Lopo * tests/benchmark.def tests/benchmark.tpl New files which generate tests/benchmark.c using Autogen. Added int -> SF_FORMAT_PCM_24 test. * tests/benchmark.c Now and Autogen output file. * tests/Makefile.am Updated for above changes. 2002-06-27 Erik de Castro Lopo * tests/benchmark.c Basic benchmark program complete. Need to convert it to Autogen. * Win32/Makefile.msvc Added benchmark.exe target. 2002-06-26 Erik de Castro Lopo * examples/generate.c New program to generate a number of different output file formats from a single input file. This allows testing of the created files. * tests/benchmark.c New test program to benchmark libsndfile. Nowhere near complete yet. * examples/Makefile.am tests/Makefile.am New make rules for the two new programs. 2002-06-25 Erik de Castro Lopo * Win32/libsndfile.def Removed definition for sf_signal_max(). * src/sndfile.c Removed cruft. * doc/index.html A number of documentation bugs were fixed. Thanks to Anand Kumria. * doc/version-1.html Minor doc updates. * configure.in Bumped version to 1.0.0rc2. * src/sf_command.h src/Makefile.am Removed the header file as it was no longer being used. Thanks to Anand Kunria for spotting this. * doc/index.html A number of documentation bugs were fixed. Thanks to Anand Kumria. 2002-06-24 Erik de Castro Lopo * src/common.h Test for Win32 before testing SIZEOF_OFF_T so that it works correctly on Win32.. * src/file_io.c Win32 fixes to ensure O_BINARY is used for file open. * doc/win32.html New file documenting the building libsndfile on Win32. * doc/*.html Updating of documentation. 2002-06-23 Erik de Castro Lopo * tests/pcm_test.c Minor changes to allow easier determination of test file name. * src/sndfile.[ch] Removed function sf_signal_max(). * examples/sndfile-play.c Changed call to sf_signal_max() to a call to sf_command(). 2002-06-22 Erik de Castro Lopo * src/format.c src/command.c Renamed format.c to command.c which will now include code for sf_command() calls to perform operations other than format commands. * src/sndfile.c src/sndfile.h Removed function sf_get_signal_max() which is replaced by commands passed to sf_command(). * src/command.c Implement commands SFC_CALC_SIGNAL_MAX. * doc/command.html Documented SFC_CALC_SIGNAL_MAX. 2002-06-21 Erik de Castro Lopo * examples/sndfile-play.c Mods to make sndfile-play work on Solaris. The program sndfile-play now runs on Linux, MaxOSX and Solaris. Win32 to come. * src/format.c Added SF_FORMAT_DWVW_* to subtype_formats array. * src/nist.c Added support for 8 bit NIST Sphere files. Example file supplied by Anand Kumria. 2002-06-20 Erik de Castro Lopo * examples/sndfile-info.c Tidy up of output format. * examnples/sndfile-play.c Mods to make sndfile-play work on MacOSX using Apple's CoreAudio API. * configure.in Add new variables OS_SPECIFIC_INCLUDES and OS_SPECIFIC_LINKS which were required to supply extra include paths and link parameters to get sndfile-play working on MacOSX. * examples/Makefile.am Use OS_SPOECIFIC_INCLUDES and OS_SPECIFIC_LINKS to build commands for sndfile-play. 2002-06-19 Erik de Castro Lopo * src/nist.c Added ability to read/write new NIST Sphere file types (A-law, u-law). Header parser was re-written from scratch. Example files supplied by Anand Kumria. * src/sndfile.c Support for A-law and u-law NIST files. * tests/Makefile.am tests/lossy_comp_test.c Tests for A-law and u-law NIST files. 2002-06-18 Erik de Castro Lopo * tests/utils.c Fixed an error in error string. 2002-06-17 Erik de Castro Lopo * acinclude.m4 Removed exit command to allow cross-compiling. * Win32/unistd.h src/file_io.c Moved contents of first file into the second file (enclosed in #ifdef). Win32/unistd.h is now an empty file but still must be there for libsndfile to compile on Win32. * src/sd2.c, src/sndfile.c: Fixes for Sound Designer II files on big endian systems. 2002-06-16 Erik de Castro Lopo * configure.in Modified to work around problems with crappy MacOSX version of sed. Added sanity check for proper values for CFLAGS. 2002-06-14 Erik de Castro Lopo * src/sndfile.c Code clean up in sf_open (). * Win32/Makefile.msvc Michael Fink's contributed MSVC++ makefile was hacked to bits and put back together in a new improved form. * src/file_io.c Fixes for Win32; _lseeki64() returns an invalid argument for calls like _lseeki64(fd, 0, SEEK_CUR) so need to use _telli64 (fd) instead. * src/common.h src/sndfile.c src/wav.c src/aiff.c Added SFE_LOG_OVERRUN error. Added termination for potential infinite loop when parsing file headers. * src/wav.c src/w64.c Fixed bug casuing incorrect header generation when opening file read/write. 2002-06-12 Erik de Castro Lopo * doc/api.html Improved the documentation to make it clearer that the file read method and the underlying file format are completely disconnected. Suggested by Josh Green. * doc/command.html Started correcting docs to take into account changes made to the operations of the sf_command () function. Not complete yet. * src/sndfile.c Reverted some changes which had broken the partially working SDII header parsing. Now have access to an iBook with OS X so reading and writing SDII files on all platforms should be a reality in the near future. On Mac this will involve reading the resource fork via the standard MacOS API. To move a file from Mac to another OS, the resource and data forks will need to be combined before transfer. The combined file will be read on both Mac and other OSes like any other file. 2002-06-08 Erik de Castro Lopo * ltmain.sh Applied a patch from http://fink.sourceforge.net/doc/porting/libtool.php which allows libsndfile to compile on MacOSX 10.1. This patch should not interfere with compiling on other OSes. * src/GSM610/private.h Changes to fix compile problems on MacOSX (see src/GSM610/ChangeLog). * src/float_cast.h Added MacOSX replacements for lrint() and lrintf(). 2002-06-05 Erik de Castro Lopo * src/sndfile.c Replaced the code to print the filename to the log buffer when a file is opened. This code seems to have been left out during the merge of sf_open_read() and sf_open_write() to make a single functions sf_open(). 2002-06-01 Erik de Castro Lopo * src/wav.c Fixed a bug where the WAV header parser was going into an infinite loop on a badly formed LIST chunk. File supplied by David Viens. 2002-05-25 Erik de Castro Lopo * configure.in Added a message at the end of the configuration process to warn about the need for the use of pkg-config when linking programs against version 1 of libsndfile. * doc/pkg-config.html New documentation file containing details of how to use pkg-config to retrieve settings for CFLAGS and library locations for linking files against version 1 of libsndfile. 2002-05-17 Erik de Castro Lopo * src/wav.c Fixed minor bug in handling of so-called ACIDized WAV files. 2002-05-16 Erik de Castro Lopo * Win32/libsndfile.def Win32/Makefile.msvc Two new files contributed by Michael Fink (from the winLAME project) which allows libsndfile to be built on windows in a MSDOS box by doing "nmake -f Makefile.msvc". Way cool! 2002-05-15 Erik de Castro Lopo * configure.in MacOSX is SSSOOOOOOO screwed up!!! I can't believe how hard it is to generate a tarball which will configure and compile on that platform. Joined the libtool mailing list to try and get some answers. 2002-05-13 Erik de Castro Lopo * configure.in Changed to autoconf version 2.50. MacOSX uses autoconf version 2.53 which is incompatible with with version 2.13 which had been using until now. The AC_SYS_LARGE_FILE macro distributed withe autoconf 2.50 is missing a few features so AC_SYS_EXTRA_LARGE file was defined to replace it. * configure.in Changed to automake version 1.5 to try and make a tarball which will work on MacOSX. 2002-05-12 Erik de Castro Lopo * src/wav_gsm610.c Changed name to gsm610.c. Added reading/writing of headerless files. * src/sndfile.c src/raw.c Added ability to read/write headerless (SF_FORMAT_RAW) GSM 6.10 files. 2002-05-11 Erik de Castro Lopo * tests/lossy_comp_test.c Clean up in preparation for Autogen-ing this file. * src/GSM610/*.[ch] Code cleanup and prepartion forgetting file seek working. Details in src/GSM610/ChangeLog. * sndfile.pc.in Testing complete. Is sndfile.m4 still needed? 2002-05-09 Erik de Castro Lopo * tests/write_read_test.tpl tests/rdwr_test.tpl Merged tests from these two programs into write_read_test.tpl and deleted rdwr_test.tpl. 2002-05-08 Erik de Castro Lopo * src/w64.c src/svx.c src/paf.c Fixed bugs in read/write mode. 2002-05-07 Erik de Castro Lopo * examples/Makefile.am Renamed sfplay.c to sndfile-play.c and sndfile_info.c to sndfile-info.c for consistency when these programs become part of the Debian package sndfile-programs. * sndfile.pc.in New file to replace sndfile-config.in. Libsndfile now uses the pkg-config model for providing installation parameters to dependant programs. * src/sndfile.c Cleanup of code in sf_open(). 2002-05-06 Erik de Castro Lopo * tests/utils.tpl tests/write_read_test.tpl More conversion to Autogen fixes and enchancements. * src/*.c Read/write mode is now working for 16, 24 and 32 bit PCM as well as 32 bit float and 64 bit double data. More tests still required. * src/Makefile.am Added DISTCLEANFILES target to remove config.status and config.last. * Win32/Makefile.am MacOS/Makefile.am Added DISTCLEANFILES target to remove Makefile. 2002-05-05 Erik de Castro Lopo * src/*.[ch] tests/rdwr_test.c More verifying workings of read/write mode. Fixing bugs found. * tests/utils.[ch] Made these files Autogen generated files. * tests/util.tpl tests/util.def New Autogen files to generate utils.[ch]. Moved some generic test functions into this file. Autogen is such a great tool! 2002-05-03 Erik de Castro Lopo * src/pcm.c src/float_cast.h Win32/config.h Fixed a couple of Win32 specific bugs pointed out by Michael Fink (maintainer of WinLAME) and David Viens. * tests/check_log_buffer.[ch] tests/utils.[ch] Moved check_log_buffer() to utils.[ch] and deleted old file. 2002-05-02 Erik de Castro Lopo * src/common.[ch] src/sndfile.c New function psf_default_seek() which will be the default seek function for things like PCM and floating point data. This default is set for both read and write in sf_open() but can be over-ridden by any codec during it's initialisation. 2002-05-01 Erik de Castro Lopo * src/au.c AU files use a data size value of -1 to mean unknown. Fixed au_open_read() to allow opening files like this. * tests/rdwr_test .c Added more tests. * src/sndfile.c Fixed bugs in read/write mode found due to improvements in the test program. 2002-04-30 Erik de Castro Lopo * tests/rdwr_test .c New file for testing read/write mode. 2002-04-29 Erik de Castro Lopo * m4/* Removed all m4 macros from this directory as they get concatenated to form the file aclocal.m4 anyway. * sndfile.m4 Moved this from the m4 directory to the root directory asn this is part of the distribution and is installed during "make install". 2002-04-29 Erik de Castro Lopo * src/float32.c Removed logging of peaks for all file formats other than AIFF and WAV. * tests/write_read_test.tpl tests/write_read_test.def New files which autogen uses to generate write_read_test.c. Doing it this way makes write_read_test.c far easier to maintain. Other test programs will be converted to autogen in the near future. * src/*.c Fixed a few bugs found when testing on Sparc (bug endian) Solaris. 2002-04-28 Erik de Castro Lopo * doc/*.html Fixed documention versioning. * configure.in Fixed a bug in the routines which search for Large File Support on systems which have large file support by defualt. 2002-04-27 Erik de Castro Lopo * src/*.[ch] Found and fixed an issue which can cause a bug in other software (I was porting Conrad Parker's Sweep program from version 0 of the library to version 1). When opening a file for write, the libsndfile code would set the sfinfo.samples field to a maximum value. * tests/write_read_test.c Added tests to detect the above problem. 2002-04-25 Erik de Castro Lopo * src/*.[ch] Finished base implementation of read/write mode. Much more testing still needed. * m4/largefile.m4 Macro for detecting Large File Standard capabilities. This macro was ripped out of the aclocal.m4 file of GNU tar-1.13. * configure.in Added detection of large file support. Files larger than 2 Gigabytes should now be supported on 64 bit platforms and many 32 bit platforms including Linux (2.4 kernel, glibc-2.2), *BSD, MacOS, Win32. * libsndfile_convert_version.py A Python script which attempts to autoconvert code written to use version 0 to version 1. 2002-04-24 Erik de Castro Lopo * src/*.[ch] Finished base implementation of read/write mode. Much more testing still needed. * tests/write_read_test.c Preliminary tests for read/write mode added. More needed. 2002-04-20 Erik de Castro Lopo * src/sndfile.[ch] Removed sf_open_read() and sf_open_write() functions,replacting them with sf_open() which takes an extra mode parameter (SF_OPEN_READ, SF_OPEN_WRITE, or SF_OPEN_RDWR). This new function sf_open can now be modified to allow opening a file formodification (RDWR). 2002-04-19 Erik de Castro Lopo * src/*.c Completed merging of separate xxx_open_read() and xxx_open_write() functions. All tests pass. 2002-04-18 Erik de Castro Lopo * src/au.c Massive refactoring required to merge au_open_read() with au_open_write() to create au_open(). 2002-04-17 Erik de Castro Lopo * src/*.c Started changes required to allow a sound file to be opened in read/write mode, with separate file pointers for read and write. This involves merging of encoder/decoder functions like pcm_read_init() and pcm_write_init() int a new function pcm_init() as well as doing something similar for all the file type specific functions ie aiff_open_read() and aiff_open_write() were merged to make the function aiff_open(). 2002-04-15 Erik de Castro Lopo * src/file_io.c New file containing psf_fopen(), psf_fread(), psf_fwrite(), psf_fseek() and psf_ftell() functions. These function will replace use of fopen/fread/fwrite etc and allow access to files larger than 2 gigabytes on a number of 32 bit OSes (Linux on x86, 32 bit Solaris user space apps, Win32 and MacOS). * src/*.c Replaced all instances of fopen with psf_open, fread with psd_read, fwrite with psf_write and so on. 2002-03-11 Erik de Castro Lopo * src/dwvw.c Finally fixed all known problems with 12, 16 and 24 bit DWVW encoding. * tests/floating_point_test.c Added tests for 12, 16 and 24 bit DWVW encoding. 2002-03-03 Erik de Castro Lopo * m4/endian.m4 Defines a new m4 macro AC_C_FIND_ENDIAN, for determining the endian-ness of the target CPU. It first checks for the definition of BYTE_ORDER in , then in and . If none of these work and the C compiler is not a cross compiler it compiles and runs a program to test for endian-ness. If the compiler is a cross compiler it makes a guess based on $target_cpu. * configure.in Modified to use AC_C_FIND_ENDIAN. * src/sfendian.h Simplified. 2002-02-23 Erik de Castro Lopo * tests/floating_point_test.c Tests completely rewritten using the dft_cmp function. Now able to calculate a quick guesstimate of the Signal to Noise Ratio of the encoder. 2002-02-15 Erik de Castro Lopo * tests/dft_cmp.[ch] New files containing functions for comparing pre and post lossily compressed data using a quickly hacked DFT. * tests/utils.[ch] New files containing functions for saving pre and post encoded data in a file readable by the GNU Octave package. 2002-02-13 Erik de Castro Lopo * m4/lrint.m4 m4/lrintf.m4 Fixed m4 macros to define HAVE_LRINT and HAVE_LRINTF even when the test is cached. 2002-02-12 Erik de Castro Lopo * tests/floating_point_test.c Fixed improper use of strncat (). 2002-02-11 Erik de Castro Lopo * tests/headerless_test.c New test program to test the ability to open and read a known file type as a RAW header-less file. 2002-02-07 Erik de Castro Lopo * tests/losy_comp_test.c Added a test to ensure that the data read from a file is not all zeros. * examples/sfconvert.c Added "-gsm610" encoding types. 2002-01-29 Erik de Castro Lopo * examples/sfconvert.c Added "-dwvw12", "-dwvw16" and "-dwvw24" encoding types. * tests/dwvw_test.c New file for testing DWVW encoder/decoder. 2002-01-28 Erik de Castro Lopo * src/dwvw.c Implemented writing of DWVW. 12 bit seems to work, 16 and 24 bit still broken. * src/aiff.c Improved reporting of encoding types. * src/voc.c Clean up. 2002-01-27 Erik de Castro Lopo * src/dwvw.c New file implementing lossless Delta Word Variable Width (DWVW) encoding. Reading 12 bit DWVW is now working. * src/aiff.c common.h sndfile.c Added hooks for DWVW encoded AIFF and RAW files. 2002-01-15 Erik de Castro Lopo * src/w64.c Robustify header parsing. * src/wav_w64.h Header file wav.h was renamed to wav_w64.h to signify sharing of definitions across the two file types. * src/wav.c src/w64.c src/wav_w64.c Refactoring. Modified and moved functions with a high degree of similarity between wav.c and w64.c to wav_w64.c. 2002-01-14 Erik de Castro Lopo * src/w64.c Completed work on getting read and write working. * examples/sfplay.c Added code to scale floating point data so it plays at a reasonable volume. * tests/Makefile.am tests/write_read_test.c Added tests for W64 files. 2002-01-13 Erik de Castro Lopo * src/*.c Modded all code in file header writing routines to use psf_new_binheader_writef(). Removed psf_binheader_writef() from src/common.c. Globally replaced psf_new_binheader_writef with psf_binheader_writef. 2002-01-12 Erik de Castro Lopo * src/*.c Modded all code in file parsing routines to use psf_new_binheader_readf(). Removed psf_binheader_readf() from src/common.c. Globally replaced psf_new_binheader_readf with psf_binheader_readf. * src/common.[ch] Added new function psf_new_binheader_writef () which will soon replace psf_binheader_writef (). The new function has basically the same function as the original but has a more flexible and capable interface. It also allows the writing of 64 bit integer values for files contains 64 bit file offsets. 2002-01-11 Erik de Castro Lopo * src/formats.c src/sndfile.c src/sndfile.h Added code allowing full enumeration of supported file formats via the sf_command () interface. This feature will allow applications to avoid needing recompilation when support for new file formats are added to libsndfile. * tests/command_test.c Added test code for the above feature. * examples/list_formats.c New file. An example of the use of the supported file enumeration interface. This program lists all the major formats and for each major format the supported subformats. 2002-01-10 Erik de Castro Lopo * src/*.[ch] tests/*.c Changed command parameter of sf_command () function from a test string to an int. The valid values for the command parameter begin with SFC_ and are listed in src/sndfile.h. 2001-12-20 Erik de Castro Lopo * src/formats.c src/sndfile.c Added an way of enumerating a set of common file formats using the sf_command () interface. This interface was suggested by Dominic Mazzoni, one of the main authors of Audacity (http://audacity.sourceforge.net/). 2001-12-26 Erik de Castro Lopo * src/sndfile.c Added checking of filename parameter in sf_open_read (). Previousy, if a NULL pointer was passed the library would segfault. 2001-12-18 Erik de Castro Lopo * src/common.c src/common.h Changed the len parameter of the endswap_*_array () functions from type int to type long. * src/pcm.c Fixed a problem which 2001-12-15 Erik de Castro Lopo * src/sndfile.c Added conditional #include for EMX/gcc on OS/2. Thanks to Paul Hartman for pointing this out. * tests/lossy_comp_test.c tests/floating_point_test.c Added definitions for M_PI for when it isn't defined in . 2001-11-30 Erik de Castro Lopo * src/ircam.c Re-implemented the header reader. Old version was making incorrect assumptions about the endian-ness of the file from the magic number at the start of the file. The new code looks at the integer which holds the number of channels and determines the endian-ness from that. 2001-11-30 Erik de Castro Lopo * src/aiff.c Added support for other AIFC types ('raw ', 'in32', '23ni'). Further work on IMA ADPCM encoding. 2001-11-29 Erik de Castro Lopo * src/ima_adpcm.c Renamed from wav_ima_adpcm.c. This file will soon handle IMA ADPCM encodings for both WAV and AIFF files. * src/aiff.c Started adding IMA ADPCM support. 2001-11-28 Erik de Castro Lopo * src/double.c New file for handling double precision floating point (SF_FORMAT_DOUBLE) data. * src/wav.c src/aiff.c src/au.c src/raw.c Added support for SF_FORMAT_DOUBLE data. * src/common.[ch] Addition of endswap_long_array () for endian swapping 64 bit integers. This function will work correctly on processors with 32 bit and 64 bit longs. Optimised endswap_short_array () and endswap_int_array (). * tests/pcm_test.c Added and extra check. After the first file of each type is written to disk a checksum is performed of the first 64 bytes and checked against a pre- calculated value. This will work whatever the endian-ness of the host machine. 2001-11-27 Erik de Castro Lopo * src/aiff.c Added handling of u-law, A-law encoded AIFF files. Thanks to Tom Erbe for supplying example files. * tests/lossy_comp_test.c Added tests for above. * src/common.h src/*.c Removed function typedefs from common.h and function pointer casting in all the other files. This allows the compiler to perform proper type checking. Hopefully this will prevernt problems like the sf_seek bug for OpenBSD, BeOS etc. * src/common.[ch] Added new function psf_new_binheader_readf () which will eventually replace psf_binheader_readf (). The new function has basically the same function as the original but has a more flexible and capable interface. It also allows the reading of 64 bit integer values for files contains 64 bit file offsets. 2001-11-26 Erik de Castro Lopo * src/voc.c Completed implementation of VOC file handling. Can now handle 8 and 16 bit PCM, u-law and A-law files with one or two channels. * src/write_read_test.c tests/lossy_comp_test.c Added tests for VOC files. 2001-11-22 Erik de Castro Lopo * src/float_cast.h Added inline asm version of lrint/lrintf for MacOS. Solution provided by Stephane Letz. * src/voc.c More work on this braindamaged format. The VOC files produced by SoX also have a number of inconsistencies. 2001-11-19 Erik de Castro Lopo * src/paf.c Added support for 8 bit PCM PAF files. * tests/write_read_test.c Added tests for 8 bit PAF files. 2001-11-18 Erik de Castro Lopo * tests/pcm_test.c New test program to test for correct scaling of integer values between different sized integer containers (ie short -> int). The new specs for libsndfile state that when the source and destination containers are of a different size, the most significant bit of the source value becomes the most significant bit of the destination container. * src/pcm.c src/paf.c Modified to pass the above test program. * tests/write_read_test.c tests/lossy_comp_test.c Modified to work with the new scaling rules. 2001-11-17 Erik de Castro Lopo * src/raw.c tests/write_read_test.c tests/write_read_test.c Added ability to do raw reads/writes of float, u-law and A-law files. * src/*.[ch] examples/*.[ch] tests/*.[ch] Removed dependance on pcmbitwidth field of SF_INFO struct and moved to new SF_FORMAT_* types and use of SF_ENDIAN_BIG/LITTLE/CPU. 2001-11-12 Erik de Castro Lopo * src/*.[ch] Started implmentation of major changes documented in doc/version1.html. Removed all usage of off_t which is not part of the ISO C standard. All places which were using it are now using type long which is the type of the offset parameter for the fseek function. This should fix problems on BeOS, MacOS and *BSD like systems which were failing "make check" because sizeof (long) != sizeof (off_t). -------------------------------------------------------------------------------- This is the boundary between version 1 of the library above and version 0 below. -------------------------------------------------------------------------------- 2001-11-11 Erik de Castro Lopo * examples/sfplay_beos.cpp Added BeOS version of sfplay.c. This needs to be compiled using a C++ compiler so is therefore not built by default. Thanks to Marcus Overhagen for providing this. 2001-11-10 Erik de Castro Lopo * examples/sfplay.c New example file showing how libsndfile can be used to read and play a sound file. At the moment on Linux is supported. Others will follow in the near future. 2001-11-09 Erik de Castro Lopo * src/pcm.c Fixed problem with normalisation code where a value of 1.0 could map to a value greater than MAX_SHORT or MAX_INT. Thanks to Roger Dannenberg for pointing this out. 2001-11-08 Erik de Castro Lopo * src/pcm.c Fixed scaling issue when reading/writing 8 bit files using sf_read/sf_write_short (). On read, values are scaled so that the most significant bit in the char ends up in the most significant bit of the short. On write, values are scaled so that most significant bit in the short ends up as the most significant bit in the char. 2001-11-07 Erik de Castro Lopo * src/au.c src/sndfile.c Added support for 32 bit float data in big and little endian AU files. * tests/write_read_test.c Added tests for 32 bit float data in AU files. 2001-11-06 Erik de Castro Lopo * tests/lossy_comp_test.c Finalised testing of stereo files where possible. 2001-11-05 Erik de Castro Lopo * src/wav_ms_adpcm.c Fixed bug in writing stereo MS ADPCM WAV files. Thanks to Xu Xin for pointing out this problem. 2001-10-24 Erik de Castro Lopo * src/wav_ms_adpcm.c Modified function srate2blocksize () to handle 44k1Hz stereo files. 2001-10-21 Erik de Castro Lopo * src/w64.c Added support for Sonic Foundry 64 bit WAV format. As Linux (my main development platform) does not yet support 64 bit file offsets by default, current handling of this file format treats everything as 32 bit and fails openning the file, if it finds anything that goes beyond 32 bit values. * src/sndfile.[hc] src/common.h src/Makefile.am Added hooks for W64 support. 2001-10-21 Erik de Castro Lopo * configure.in Added more warnings options to CFLAGS when the gcc compiler is detected. * src/*.[ch] tests/*.c examples/*.c Started fixing the warning messages due to the new CFLASG. * src/voc.c More work on VOC file read/writing. * src/paf.c Found that PAF files were not checking the normalisation flag when reading or writing floats and doubles. Fixed it. * tests/floating_point_test.c Added specific test for the above problem. * src/float_cast.h src/pcm.c Added a section for Win32 to define lrint () and lrintf () in the header and implement it in the pcm.c 2001-10-20 Erik de Castro Lopo * sndfile-config.in m4/sndfile.m4 These files were donated by Conrad Parker who also provided instructions on how to install them using autoconf/automake. * src/float_cast.h Fiddled around with this file some more. On Linux and other gcc supported OSes use the C99 functions lrintf() and lrint() for casting from floating point to int without incurring the huge perfromance penalty (particularly on the i386 family) caused by the regular C cast from float to int. These new C99 functions replace the FLOAT_TO_* and DOUBLE_TO_* macros which I had been playing with. * configure.in m4/lrint.m4 m4/lrintf.m4 Add detection of these functions. 2001-10-17 Erik de Castro Lopo * src/voc.c Completed code for reading VOC files containing a single audio data segment. Started implementing code to handle files with multiple VOC_SOUND_DATA segments but couldn't be bothered finishing it. Multiple segment files can have different sample rates for different sections and other nasties like silence and repeat segments. 2001-10-16 Erik de Castro Lopo * src/common.h src/*.c Removed SF_PRIVATE struct field fdata and replaced it with extra_data. * src/voc.c Further development of the read part of this woefult file format. 2001-10-04 Erik de Castro Lopo * src/float_cast.h Implemented gcc and i386 floating point to int cast macros. Standard cast will be used when not on gcc for i385. * src/pcm.c Modified all uses of FLOAT/DOUBLE_TO_INT and FLOAT/DOUBLE_TO_SHORT casts to comply with macros in float_cast.h. 2001-10-04 Erik de Castro Lopo * src/voc.c Changed the TYPE_xxx enum names to VOC_TYPE_xxx to prevent name clashes on MacOS with CodeWarrior 6.0. * MacOS/MacOS-readme.txt Updated the compile instructions. Probably still need work as I don't have access to a Mac. 2001-10-01 Erik de Castro Lopo * src/wav.c src/aiff.c common.c Changed all references to snprintf to LSF_SNPRINTF and all vsnprintf to LSF_VSNPRINTF. LSF_VSNPRINTF and LSF_VSNPRINTF are defined in common.h. * src/common.h Added checking of HAVE_SNPRINTF and HAVE_VSNPRINTF and defining LSF_VSNPRINTF and LSF_VSNPRINTF to appropriate values. * src/missing.c New file containing a minimal implementation of snprintf and vsnprintf functions named missing_snprintf and missing_vsnprintf respectively. These are only compliled into the binary if snprintf and/or vsnprintf are not available. 2001-09-29 Erik de Castro Lopo * src/ircam.c New file to handle Berkeley/IRCAM/CARL files. * src/sndfile.c src/common.h Modified for IRCAM handling. * tests/*.c Added tests for IRCAM files. 2001-09-27 Erik de Castro Lopo * src/wav.c Apparently microsoft windows (tm) doesn't like ulaw and Alaw WAV files with 20 byte format chunks (contrary to ms's own documentation). Fixed the WAV header writing code to generate smaller ms compliant ulaw and Alaw WAV files. 2001-09-17 Erik de Castro Lopo * tests/stdio_test.sh tests/stdio_test.c Shell script was rewritten as a C program due to incompatibilities of the sh shell on Linux and Solaris. 2001-09-16 Erik de Castro Lopo * tests/stdio_test.sh tests/stdout_test.c tests/stdin_test.c New test programs to verify the correct operation of reading from stdin and writing to stdout. * src/sndfile.c wav.c au.c nist.c paf.c Fixed a bugs uncovered by the new test programs above. 2001-09-15 Erik de Castro Lopo * src/sndfile.c wav.c Fixed a bug preventing reading a file from stdin. Found by T. Narita. 2001-09-12 Erik de Castro Lopo * src/common.h Fixed a problem on OpenBSD 2.9 which was causing sf_seek() to fail on IMA WAV files. Root cause was the declaration of the func_seek typedef not matching the functions it was actually being used to point to. In OpenBSD sizeof (off_t) != sizeof (int). Thanks to Heikki Korpela for allowing me to log into his OpenBSD machine to debug this problem. 2001-09-03 Erik de Castro Lopo * src/sndfile.c Implemented sf_command ("norm float"). * src/*.c Implemented handling of sf_command ("set-norm-float"). Float normalization can now be turned on and off. * tests/double_test.c Renamed to floating_point_test.c. Modified to include tests for all scaled reads and writes of floats and doubles. * src/au_g72x.c Fixed bug in normalization code found with improved floating_point_test program. * src/wav.c Added code for parsing 'INFO' and 'LIST' chunks. Will be used for extract text annotations from WAV files. * src/aiff.c Added code for parsing '(c) ' and 'ANNO' chunks. Will be used for extract text annotations from WAV files. 2001-09-02 Erik de Castro Lopo * examples/sf_info.c example/Makefile.am Renamed to sndfile_info.c. The program sndfile_info will now be installed when the library is installed. * src/float_cast.h New file defining floating point to short and int casts. These casts will eventually replace all flot and double casts to short and int. See comments at the top of the file for the reasoning. * src/*.c Changed all default float and double casts to short or int with macros defined in floatcast.h. At the moment these casts do nothing. They will be replaced with faster float to int cast operations in the near future. 2001-08-31 Erik de Castro Lopo * tests/command_test.c New file for testing sf_command () functionality. * src/sndfile.c Revisiting of error return values of some functions. Started implementing sf_command () a new function will allow on-the-fly modification of library behaviour, or instance, sample value scaling. * src/common.h Added hook for format specific sf_command () calls to SNDFILE struct. * doc/api.html Updated and errors corrected. * doc/command.html New documentation file explaining new sf_command () function. 2001-08-11 Erik de Castro Lopo * src/sndfile.c Fixed error return values from sf_read*() and sf_write*(). There were numerous instances of -1 being returned through size_t. These now all set error int the SF_PRIVATE struct and return 0. Thanks to David Viens for spotting this. 2001-08-01 Erik de Castro Lopo * src/common.c Fixed use of va_arg() calls that were causing warning messages with the latest version of gcc (thanks Maurizio Umberto Puxeddu). 2001-07-25 Erik de Castro Lopo * src/*.c src/sfendian.h Moved definition of MAKE_MARKER macro to sfendian.h 2001-07-23 Erik de Castro Lopo * src/sndfile.c Modified sf_get_lib_version () so that version string will be visible using the Unix strings command. * examples/Makefile.am examples/sfinfo.c Renamed sfinfo program and source code to sf_info. This prevents a name clash with the program included with libaudiofile. 2001-07-22 Erik de Castro Lopo * tests/read_seek_test.c tests/lossy_comp_test.c Added tests for sf_read_float () and sf_readf_float (). * src/voc.c New files for handling Creative Voice files (not complete). * src/samplitude.c New files for handling Samplitude files (not complete). 2001-07-21 Erik de Castro Lopo * src/aiff.c src/au.c src/paf.c src/svx.c src/wav.c Converted these files to using psf_binheader_readf() function. Will soon be ready to attempt to make reading writing from pipes work reliably. * src/*.[ch] Added code for sf_read_float () and sf_readf_float () methods of accessing file data. 2001-07-20 Erik de Castro Lopo * src/paf.c src/wav_gsm610.c Removed two printf()s which had escaped notice for some time (thanks Sigbjørn Skjæret). 2001-07-19 Erik de Castro Lopo * src/wav_gsm610.c Fixed a bug which prevented GSM 6.10 encoded WAV files generated by libsndfile from being played in Windoze (thanks klay). 2001-07-18 Erik de Castro Lopo * src/common.[ch] Implemented psf_binheader_readf() which will do for file header reading what psf_binheader_writef() did for writing headers. Will eventually allow libsndfile to read and write from pipes, including named pipes. 2001-07-16 Erik de Castro Lopo * MacOS/config.h Win32/config.h Attempted to bring these two files uptodate with src/config.h. As I don't have access to either of these systems support for them may be completely broken. 2001-06-18 Erik de Castro Lopo * src/float32.c Fixed bug for big endian processors that can't read 32 bit IEEE floats. Now tested on Intel x86 and UltraSparc processors. 2001-06-13 Erik de Castro Lopo * src/aiff.c Modified to allow REX files (from Propellorhead's Recycle and Reason programs) to be read. REX files are basically an AIFF file with slightly unusual sequence of chunks (AIFF files are supposed to allow any sequence) and some extra application specific information. Not yet able to write a REX file as the details of the application specific data is unknown. 2001-06-12 Erik de Castro Lopo * src/wav.c Fixed endian bug when reading PEAK chunk on big endian machines. * src/common.c Fixed endian bug when reading PEAK chunk on big endian machines with --enable-force-broken-float configure option. Fix psf_binheader_writef for (FORCE_BROKEN_FLOAT ||______) 2001-06-07 Erik de Castro Lopo * configure.in src/config.h.in Removed old CAN_READ_WRITE_x86_IEEE configure variable now that float capabilities are detected at run time. Added FORCE_BROKEN_FLOAT to allow testing of broken float code on machines where the processor can in fact handle floats correctly. * src/float32.c Rejigged code reading and writing of floats on broken processors. * m4/ Removed this directory and all its files as they are no longer needed. 2001-06-05 Erik de Castro Lopo * tests/peak_chunk_test.c New test to validate reading and writing of peak chunk. * examples/sfconvert Added -float32 option. * src/*.c Changed all error return values to negative values (ie the negative of what they were). * src/sndfile.c tests/error_test.c Modified to take account of the previous change. 2001-06-04 Erik de Castro Lopo * src/float32.c File renamed from wav_float.c and renamed function to something more general. Added runtime detection of floating point capabilities. Added recording of peaks during write for generation of PEAK chunk. * src/wav.c src/aiff.c Added handing for PEAK chunk for floating point files. PEAK is read when the file headers are read and generated when the file is closed. Logic is in place for adding PEAK chunk to end of file when writing to a pipe (reading and writing from/to pipe to be implemented soon). * src/sndfile.c Modified sf_signal_max () to use PEAK values if present. 2001-06-03 Erik de Castro Lopo * src/*.c Added pcm_read_init () and pcm_write_init () to src/pcm.c and removed all other calls to functions in this file from the filetype specific files. * src/*.c Added alaw_read_init (), alaw_write_int (), ulaw_read_init () and ulaw_write_init () and removed all other calls to functions in alaw.c and ulaw.c from the filetype specific files. * tests/write_read_test.c Added tests to validate sf_seek () on all file types. * src/raw.c Implemented raw_seek () function to fix a bug where sf_seek (file, 0, SEEK_SET) on a RAW file failed. * src/paf.c Fixed a bug in paf24_seek () found due to added seeks tests in tests/write_read_test.c 2001-06-01 Erik de Castro Lopo * tests/read_seek_test.c Fixed a couple of broken binary files. * src/aiff.c src/wav.c Added handling of PEAK chunks on file read. 2001-05-31 Erik de Castro Lopo * check_libsndfile.py New file for the regression testing of libsndfile. check_libsndfile.py is a Python script which reads in a file containing filenames of audio files. Each file is checked by running the examples/sfinfo program on them and checking for error or warning messages in the libsndfile log buffer. * check_libsndfile.list This is an example list of audio files for use with check_libsndfile.py * tests/lossy_comp_test.c Changed the defined value of M_PI for math header files which don't have it. This fixed validation test failures on MetroWerks compilers. Thanks to Lord Praetor Satanus of Acheron for bringing this to my attention. 2001-05-30 Erik de Castro Lopo * src/common.[ch] Removed psf_header_setf () which was no longer required after refactoring and simplification of header writing. Added 'z' format specifier to psf_binheader_writef () for zero filling header with N bytes. Used by paf.c and nist.c * tests/check_log_buffer.c New file implementing check_log_buffer () which reads the log buffer of a SNDFILE* object and searches for error and warning messages. Calls exit () if any are found. * tests/*.c Added calls to check_log_buffer () after each call to sf_open_XXX (). 2001-05-29 Erik de Castro Lopo * src/wav.c src/wav_ms_adpcm.c src/wav_gsm610.c Major rehack of header writing using psf_binheader_writef (). 2001-05-28 Erik de Castro Lopo * src/wav.c src/wav_ima_adpcm.c Major rehack of header writing using psf_binheader_writef (). 2001-05-27 Erik de Castro Lopo * src/wav.c Changed return type of get_encoding_str () to prevent compiler warnings on Mac OSX. * src/aiff.c src/au.c Major rehack of header writing using psf_binheader_writef (). 2001-05-25 Erik de Castro Lopo * src/common.h src/common.c Added comments. Name of log buffer changed from strbuffer to logbuffer. Name of log buffer index variable changed from strindex to logindex. * src/*.[ch] Changed name of internal logging function from psf_sprintf () to psf_log_printf (). Changed name of internal header generation functions from psf_[ab]h_printf () to psf_asciiheader_printf () and psf_binheader_writef (). Changed name of internal header manipulation function psf_hsetf () to psf_header_setf (). 2001-05-24 Erik de Castro Lopo * src/nist.c Fixed reading and writing of sample_byte_format header. "01" means little endian and "10" means big endian regardless of bit width. * configure.in Detect Mac OSX and disable -Wall and -pedantic gcc options. Mac OSX is way screwed up and spews out buckets of warning messages from the system headers. Added --disable-gcc-opt configure option (sets gcc optimisation to -O0 ) for easier debugging. Made decision to harmonise source code version number and .so library version number. Future releases will stick to this rule. * doc/new_file_type.HOWTO New file to document the addition of new file types to libsndfile. 2001-05-23 Erik de Castro Lopo * src/nist.c New file for reading/writing Sphere NIST audio file format. Originally requested by Elis Pomales in 1999. Retrieved from unstable (and untouched for 18 months) branch of libsndfile. Some vital information gleaned from the source code to Bill Schottstaedt's sndlib library : ftp://ccrma-ftp.stanford.edu/pub/Lisp/sndlib.tar.gz Currently reading and writing 16, 24 and 32 bit, big-endian and little endian, stereo and mono files. * src/common.h src/common.c Added psf_ah_printf () function to help construction of ASCII headers (ie NIST). * configure.in Added test for vsnprintf () required by psf_ah_printf (). * tests/write_read_test.c Added tests for supported NIST files. 2001-05-22 Erik de Castro Lopo * tests/write_read_test.c Added tests for little endian AIFC files. * src/aiff.c Minor re-working of aiff_open_write (). Added write support for little endian PCM encoded AIFC files. 2001-05-13 Erik de Castro Lopo * src/aiff.c Minor re-working of aiff_open_read (). Added read support for little endian PCM encoded AIFC files from the Mac OSX CD ripper program. Guillaume Lessard provided a couple of sample files and a working patch. The patch was not used as is but gave a good guide as to what to do. 2001-05-11 Erik de Castro Lopo * src/sndfile.h Fixed comments about endian-ness of WAV and AIFF files. Guillaume Lessard pointed out the error. 2001-04-23 Erik de Castro Lopo * examples/make_sine.c Re-write of this example using sample rate and required frequency in Hz. 2001-02-11 Erik de Castro Lopo * src/sndfile.c Fixed bug that prevented known file types from being read as RAW PCM data. 2000-12-16 Erik de Castro Lopo * src/aiff.c Added handing of COMT chunk. 2000-11-16 Erik de Castro Lopo * examples/sfconvert.c Fixed bug in normalisatio code. Pointed out by Johnny Wu. 2000-11-08 Erik de Castro Lopo * Win32/config.h Fixed the incorrect setting of HAVE_ENDIAN_H parameter. Win32 only issue. 2000-10-27 Erik de Castro Lopo * tests/Makefile.am Added -lm for write_read_test_LDADD. 2000-10-16 Erik de Castro Lopo * src/sndfile.c src/au.c Fixed bug which prevented writing of G723 24kbps AU files. * tests/lossy_comp_test.c Corrrection to options for G723 tests. * configure.in Added --disable-gcc-pipe option for DJGPP compiler (gcc on MS-DOS) which doesn't allow gcc -pipe option. 2000-09-03 Erik de Castro Lopo * src/ulaw.c src/alaw.c src/wav_imaadpcm.c src/msadpcm.c src/wav_gsm610.c Fixed normailsation bugs shown up by new double_test program. 2000-08-31 Erik de Castro Lopo * src/pcm.c Fixed bug in normalisation code (spotted by Steve Lhomme). * tests/double_test.c New file to test scaled and unscaled sf_read_double() and sf_write_double() functions. 2000-08-28 Erik de Castro Lopo * COPYING Changed to the LGPL COPYING file (spotted by H. S. Teoh). 2000-08-21 Erik de Castro Lopo * src/sndfile.h Removed prototype of unimplemented function sf_get_info(). Added prototype for sf_error_number() Thanks to Sigbjørn Skjæret for spotting these. 2000-08-18 Erik de Castro Lopo * src/newpcm.h New file to contain a complete rewrite of the PCM data handling. 2000-08-15 Erik de Castro Lopo * src/sndfile.c Fixed a leak of FILE* pointers in sf_open_write(). Thanks to Sigbjørn Skjæret for spotting this one. 2000-08-13 Erik de Castro Lopo * src/au_g72x.c src/G72x/g72x.c Added G723 encoded AU file support. * tests/lossy_comp_test.c Added tests for G721 and G723 encoded AU files. 2000-08-06 Erik de Castro Lopo * all files Changed the license to LGPL. Albert Faber who had copyright on Win32/unistd.h gave his permission to change the license on that file. All other files were either copyright erikd AT mega-nerd DOT com or copyright under a GPL/LGPL compatible license. 2000-08-06 Erik de Castro Lopo * tests/lossy_comp_test.c Fixed incorrect error message. * src/au_g72x.c src/G72x/* G721 encoded AU files now working. * Win32/README-Win32.txt Replaced this file with a new one which gives a full explanation of how to build libsndfile under Win32. Thanks to Mike Ricos. 2000-08-05 Erik de Castro Lopo * src/*.[ch] Removed double leading underscores from the start of all variable and function names. Identifiers with a leading underscores are reserved for use by the compiler. * src/au_g72x.c src/G72x/* Continued work on G721 encoded AU files. 2000-07-12 Erik de Castro Lopo * src/G72x/* New files for reading/writing G721 and G723 ADPCM audio. These files are from a Sun Microsystems reference implementation released under a free software licence. Extensive changes to this code to make it fit in with libsndfile. See the ChangeLog in this directory for details. * src/au_g72x.c New file for G721 encoded AU files. 2000-07-08 Erik de Castro Lopo * libsndfile.spec.in Added a spec file for making RPMs. Thanks to Josh Green for supplying this. 2000-06-28 Erik de Castro Lopo * src/sndfile.c src/sndfile.h Add checking for and handling of header-less u-law encoded AU/SND files. Any file with a ".au" or ".snd" file extension and without the normal AU file header is treated as an 8kHz, u-law encoded file. * src/au.h New function for opening a headerless u-law encoded file for read. 2000-06-04 Erik de Castro Lopo * src/paf.c Add checking for files shorter than minimal PAF file header length. 2000-06-02 Erik de Castro Lopo * tests/write_read_test.c Added extra sf_perror() calls when sf_write_XXXX fails. 2000-05-29 Erik de Castro Lopo * src/common.c Modified usage of va_arg() macro to work correctly on PowerPC Linux. Thanks to Kyle Wheeler for giving me ssh access to his machine while I was trying to track this down. * configure.in src/*.[ch] Sorted out some endian-ness issues brought up by PowerPC Linux. * tests/read_seek_test.c Added extra debugging for when tests fail. 2000-05-18 Erik de Castro Lopo * src/wav.c Fixed bug in GSM 6.10 handling for big-endian machines. Thanks to Sigbjørn Skjæret for reporting this. 2000-04-25 Erik de Castro Lopo * src/sndfile.c src/wav.c src/wav_gsm610.c Finallised writing of GSM 6.10 WAV files. * tests/lossy_comp_test.c Wrote new test code for GSM 6.10 files. * examples/sfinfo.c Fixed incorrect format in printf() statement. 2000-04-06 Erik de Castro Lopo * src/sndfile.h.in Fixed comments about sf_perror () and sf_error_str (). 2000-03-14 Erik de Castro Lopo * configure.in Fixed --enable-justsrc option. 2000-03-07 Erik de Castro Lopo * wav.c Fixed checking of bytespersec field of header. Still some weirdness with some files. 2000-03-05 Erik de Castro Lopo * tests/lossy_comp_test.c Added option to test PCM WAV files (sanity check). Fixed bug in sf_seek() tests. 2000-02-29 Erik de Castro Lopo * src/sndfile.c src/wav.c Minor changes to allow writing of GSM 6.10 WAV files. 2000-02-28 Erik de Castro Lopo * configure.in Makefile.am src/Makefile.am Finally got around to figuring out how to build a single library from multiple source directories. Reading GSM 6.10 files now seems to work. 2000-01-03 Erik de Castro Lopo * src/wav.c Added more error reporting in read_fmt_chunk(). 1999-12-21 Erik de Castro Lopo * examples/sfinfo.c Modified program to accept multiple filenames from the command line. 1999-11-27 Erik de Castro Lopo * src/wav_ima_adpcm.c Moved code around in preparation to adding ability to read/write IMA ADPCM encoded AIFF files. 1999-11-16 Erik de Castro Lopo * src/common.c Fixed put_int() and put_short() macros used by _psf_hprintf() which were causing seg. faults on Sparc Solaris. 1999-11-15 Erik de Castro Lopo * src/common.c Added string.h to includes. Thanks to Sigbjxrn Skjfret. * src/svx.c Fixed __svx_close() function to ensure FORM and BODY chunks are correctly set. 1999-10-01 Erik de Castro Lopo * src/au.c Fixed handling of incorrect size field in AU header on read. Thanks to Christoph Lauer for finding this problem. 1999-09-28 Erik de Castro Lopo * src/aiff.c Fixed a bug with incorrect SSND chunk length being written. This also lead to finding an minor error in AIFF header parsing. Thanks to Dan Timis for pointing this out. 1999-09-24 Erik de Castro Lopo * src/paf.c Fixed a bug with reading and writing 24 bit stereo PAF files. This problem came to light when implementing tests for the new functions which operate in terms of frames rather than items. 1999-09-23 Erik de Castro Lopo * src/sndfile.c Modified file type detection to use first 12 bytes of file rather than file name extension. Required this because NIST files use the same filename extension as Microsoft WAV files. * src/sndfile.c src/sndfile.h Added short, int and double read/write functions which work in frames rather than items. This was originally suggested by Maurizio Umberto Puxeddu. 1999-09-22 Erik de Castro Lopo * src/svx.c Finished off implementation of write using __psf_hprintf(). 1999-09-21 Erik de Castro Lopo * src/common.h Added a buffer to SF_PRIVATE for writing the header. This is required to make generating headers for IFF/SVX files easier as well as making it easier to do re-write the headers which will be required when sf_rewrite_header() is implemented. * src/common.c Implemented __psf_hprintf() function. This is an internal function which is documented briefly just above the code. 1999-09-05 Erik de Castro Lopo * src/sndfile.c Fixed a bug in sf_write_raw() where it was returning incorrect values (thanks to Richard Dobson for finding this one). Must put in a test routine for sf_read_raw and sf_write_raw. * src/aiff.c Fixed default FORMsize in __aiff_open_write (). * src/sndfile.c Added copy of filename to internal data structure. IFF/SVX files contain a NAME header chunk. Both sf_open_read() and sf_open_write() copy the file name (less the leading path information) to the filename field. * src/svx.c Started implementing writing of files. 1999-08-04 Erik de Castro Lopo * src/svx.c New file for reading/writing 8SVX and 16SVX files. * src/sndfile.[ch] src/common.h Changes for SVX files. * src/aiff.c Fixed header parsing when unknown chunk is found. 1999-08-01 Erik de Castro Lopo * src/paf.c New file for reading/writing Ensoniq PARIS audio file format. * src/sndfile.[ch] src/common.h Changes for PAF files. * src/sndfile.[ch] Added stuff for sf_get_lib_version() function. 1999-07-31 Erik de Castro Lopo * src/sndfile.h MacOS/config.h Fixed minor MacOS configuration issues. 1999-07-30 Erik de Castro Lopo * MacOS/ Added a new directory for the MacOS config.h file and the readme file. * src/aiff.c Fixed calculation of datalength when reading SSND chunk. Thanks to Sigbjørn Skjæret for pointing out this error. 1999-07-29 Erik de Castro Lopo * src/sndfile.c src/sndfile.h src/raw.c Further fixing of #includes for MacOS. 1999-07-25 Erik de Castro Lopo * src/wav.c src/aiff.c Added call to ferror () in main header parsing loop of __XXX_open_read functions. This should fix problems on platforms (MacOS, AmigaOS) where fseek()ing or fread()ing beyond the end of the file puts the FILE* stream in an error state until clearerr() is called. * tests/write_read_test.c Added tests for RAW header-less PCM files. * src/common.h Moved definition of struct tribyte to pcm.c which is the only place which needs it. * src/pcm.c Modified all code which assumed sizeof (struct tribyte) == 3. This code did not work on MacOS. Thanks to Ben "Jacobs" for pointing this out. * src/au.c Removed from list of #includes (not being used). * src/sndfile.c Added MacOS specific #ifdef to replace . * src/sndfile.h Added MacOS specific #ifdef to replace . * src/sndfile.h Added MacOS specific typedef for off_t. * MacOS-readme.txt New file with instructions for building libsndfile under MacOS. Thanks to Ben "Jacobs" for supplying these instructions. 1999-07-24 Erik de Castro Lopo * configure.in Removed sndfile.h from generated file list as there were no longer any autoconf substitutions being made. * src/raw.c New file for handling raw header-less PCM files. In order to open these for read, the user must specify format, pcmbitwidth and channels in the SF_INFO struct when calling sf_open_read (). * src/sndfile.c Added support for raw header-less PCM files. 1999-07-22 Erik de Castro Lopo * examples/sfinfo.c Removed options so the sfinfo program always prints out all the information. 1999-07-19 Erik de Castro Lopo * src/alaw.c New file for A-law encoding (similar to u-law). * tests/alaw_test.c New test program to test the A-law encode/decode lookup tables. * tests/lossy_comp_test.c Added tests for a-law encoded WAV, AU and AULE files. 1999-07-18 Erik de Castro Lopo * src/sndfile.c src/au.c Removed second "#include ". Thanks to Ben "Jacobs" for pointing this out. 1999-07-18 Erik de Castro Lopo * tests/ulaw_test.c New test program to test the u-law encode/decode lookup tables. 1999-07-16 Erik de Castro Lopo * src/sndfile.h Made corrections to comments on the return values from sf_seek (). * src/sndfile.c Fixed boundary condition checking bug and accounting bug in sf_read_raw (). 1999-07-15 Erik de Castro Lopo * src/au.c src/ulaw.c Finished implementation of u-law encoded AU files. * src/wav.c Implemented reading and writing of u-law encoded WAV files. * tests/ Changed name of adpcm_test.c to lossy_comp_test.c. This test program will now be used to test Ulaw and Alaw encoding as well as APDCM. Added tests for Ulaw encoded WAV files. 1999-07-14 Erik de Castro Lopo * tests/adpcm_test.c Initialised amp variable in gen_signal() to remove compiler warning. 1999-07-12 Erik de Castro Lopo * src/aiff.c In __aiff_open_read () prevented fseek()ing beyond end of file which was causing trouble on MacOS with the MetroWerks compiler. Thanks to Ben "Jacobs" for pointing this out. *src/wav.c Fixed as above in __wav_open_read (). 1999-07-01 Erik de Castro Lopo * src/wav_ms_adpcm.c Implemented MS ADPCM encoding. Code cleanup of decoder. * tests/adpcm_test.c Added tests for MS ADPCM WAV files. * src/wav_ima_adpcm.c Fixed incorrect parameter in call to srate2blocksize () from __ima_writer_init (). 1999-06-23 Erik de Castro Lopo * tests/read_seek_test.c Added test for 8 bit AIFF files. 1999-06-18 Erik de Castro Lopo * tests/write_read_test.c Removed test for IMA ADPCM WAV files which is now done in adpcm_test.c * configure.in Added -Wconversion to CFLAGS. * src/*.c tests/*.c examples/*.c Fixed all warnings resulting from use of -Wconversion. 1999-06-17 Erik de Castro Lopo * src/wav.c Added fact chunk handling on read and write for all non WAVE_FORMAT_PCM WAV files. * src/wav_ima.c Changed block alignment to be dependant on sample rate. This should make WAV files created with libsndfile compatible with the MS Windows media players. * tests/adpcm_test.c Reimplemented adpcm_test_short and implemented adpcm_test_int and adpcm_test_double. Now have full testing of IMA ADPCM WAV file read, write and seek. 1999-06-15 Erik de Castro Lopo * src/wav_float.c Fixed function prototype for x86f2d_array () which was causing ocassional seg. faults on Sparc Solaris machines. 1999-06-14 Erik de Castro Lopo * src/aiff.c Fixed bug in __aiff_close where the length fields in the header were not being correctly calculated before writing. * tests/write_read_test.c Modified to detect the above bug in WAV, AIFF and AU files. 1999-06-12 Erik de Castro Lopo * Win32/* Added a contribution from Albert Faber to allow libsndfile to compile under Win32 systems. libsndfile will now be used as part of LAME the the MPEG 1 Layer 3 encoder (http://internet.roadrunner.com/~mt/mp3/). 1999-06-11 Erik de Castro Lopo * configure.in Changed to reflect previous changes. * src/wav_ima_adpcm.c Fixed incorrect calculation of bytespersec header field (IMA ADPCM only). Fixed bug when writing from int or double data to IMA ADPCM file. Will need to write test code for this. Fixed bug in __ima_write () whereby the length of the current block was calculated incorrectly. Thanks to Jongcheon Park for pointing this out. 1999-03-27 Erik de Castro Lopo * src/*.c Changed all read/write/lseek function calls to fread/fwrite/ fseek/ftell and added error checking of return values from fread and fwrite in critical areas of the code. * src/au.c Fixed incorrect datasize element in AU header on write. * tests/error_test.c Add new test to check all error values have an associated error string. This will avoid embarrassing real world core dumps. 1999-03-23 Erik de Castro Lopo * src/wav.c src/aiff.c Added handling for unknown chunk markers in the file. 1999-03-22 Erik de Castro Lopo * src/sndfile.c Filled in missing error strings in SndfileErrors array. Missing entries can cause core dumps when calling sf_error-str (). Thanks to Sam for finding this problem. 1999-03-21 Erik de Castro Lopo * src/wav_ima_adpcm.c Work on wav_ms_adpcm.c uncovered a bug in __ima_read () when reading stereo files. Caused by not adjusting offset into buffer of decoded samples for 2 channels. A similar bug existed in __ima_write (). Need a test for stereo ADPCM files. * src/wav_ms_adpcm.c Decoder working correctly. 1999-03-18 Erik de Castro Lopo * configure.in Makefile.am Added --enable-justsrc configuration variable sent by Sam . * src/wav_ima_adpcm.c Fixed bug when reading beyond end of data section due to not checking pima->blockcount. This uncovered __ima_seek () bug due to pima->blockcount being set before calling __ima_init_block (). 1999-03-17 Erik de Castro Lopo * src/wav.c Started implementing MS ADPCM decoder. If file is WAVE_FORMAT_ADPCM and length of data chunk is odd, this encoder seems to add an extra byte. Why not just give an even data length? 1999-03-16 Erik de Castro Lopo * src/wav.c Split code out of wav.c to create wav_float.c and wav_ima_adpcm.c. This will make it easier to add and debug other kinds of WAV files in future. 1999-03-14 Erik de Castro Lopo * tests/ Added adpcm_test.c which implements test functions for IMA ADPCM reading/writing/seeking etc. * src/wav.c Fixed many bugs in IMA ADPCM encoder and decoder. 1999-03-11 Erik de Castro Lopo * src/wav.c Finished implementing IMA ADPCM encoder and decoder (what a bitch!). 1999-03-03 Erik de Castro Lopo * src/wav.c Started implementing IMA ADPCM decoder. 1999-03-02 Erik de Castro Lopo * src/sndfile.c Fixed bug where the sf_read_XXX functions were returning a incorrect read count when reading past end of file. Fixed bug in sf_seek () when seeking backwards from end of file. * tests/read_seek_test.c Added multiple read test to short_test(), int_test () and double_test (). Added extra chunk to all test WAV files to test that reading stops at end of 'data' chunk. 1999-02-21 Erik de Castro Lopo * tests/write_read_test.c Added tests for little DEC endian AU files. * src/au.c Add handling for DEC format little endian AU files. 1999-02-20 Erik de Castro Lopo * src/aiff.c src/au.c src/wav.c Add __psf_sprintf calls during header parsing. * src/sndfile.c src/common.c Implement sf_header_info (sndfile.c) function and __psf_sprintf (common.c). * tests/write_read_test.c Added tests for 8 bit PCM files (WAV, AIFF and AU). * src/au.c src/aiff.c Add handling of 8 bit PCM data format. * src/aiff.c On write, set blocksize in SSND chunk to zero like everybody else. 1999-02-16 Erik de Castro Lopo * src/pcm.c: Fixed bug in let2s_array (cptr was not being initialised). * src/sndfile.c: Fixed bug in sf_read_raw and sf_write_raw. sf_seek should now work when using these functions. 1999-02-15 Erik de Castro Lopo * tests/write_read_test.c: Force test_buffer array to be double aligned. Sparc Solaris requires this. 1999-02-14 Erik de Castro Lopo * src/pcm.c: Fixed a bug which was causing errors in the reading and writing of 24 bit PCM files. * doc/api.html Finished of preliminary documentaion. 1999-02-13 Erik de Castro Lopo * src/aiff.c: Changed reading of 'COMM' chunk to avoid reading an int which overlaps an int (4 byte) boundary. ================================================ FILE: vendor/libsndfile/NEWS ================================================ Version 1.0.28 (2017-04-02) * Fix buffer overruns in FLAC and ID3 handling code. * Move to variable length header storage. * Fix detection of Large File Support for 32 bit systems. * Remove large stack allocations in ALAC handling code. * Remove all use of Variable Length Arrays. * Minor bug fixes and improvements. Version 1.0.27 (2016-06-19) * Fix an SF_INFO seekable flag regression introduced in 1.0.26. * Fix potential infinite loops on malformed input files. * Add string metadata read/write for CAF and RF64. * Add handling of CUE chunks. * Fix endian-ness issues in PAF files. * Minor bug fixes and improvements. Version 1.0.26 (2015-11-22) * Fix for CVE-2014-9496, SD2 buffer read overflow. * Fix for CVE-2014-9756, file_io.c divide by zero. * Fix for CVE-2015-7805, AIFF heap write overflow. * Add support for ALAC encoder in a CAF container. * Add support for Cart chunks in WAV files. * Minor bug fixes and improvements. Version 1.0.25 (2011-07-13) * Fix for Secunia Advisory SA45125, heap overflow in PAF file handler. * Accept broken WAV files with blockalign == 0. * Minor bug fixes and improvements. Version 1.0.24 (2011-03-23) * WAV files now have an 18 byte u-law and A-law fmt chunk. * Document virtual I/O functionality. * Two new methods rawHandle() and takeOwnership() in sndfile.hh. * AIFF fix for non-zero offset value in SSND chunk. * Minor bug fixes and improvements. Version 1.0.23 (2010-10-10) * Add version metadata to Windows DLL. * Add a missing 'inline' to sndfile.hh. * Update docs. * Minor bug fixes and improvements. Version 1.0.22 (2010-10-04) * Couple of fixes for SDS file writer. * Fixes arising from static analysis. * Handle FLAC files with ID3 meta data at start of file. * Handle FLAC files which report zero length. * Other minor bug fixes and improvements. Version 1.0.21 (2009-12-13) * Add a couple of new binary programs to programs/ dir. * Remove sndfile-jackplay (now in sndfile-tools package). * Add windows only function sf_wchar_open(). * Bunch of minor bug fixes. Version 1.0.20 (2009-05-14) * Fix potential heap overflow in VOC file parser (Tobias Klein, http://www.trapkit.de/). Version 1.0.19 (2009-03-02) * Fix for CVE-2009-0186 (Alin Rad Pop, Secunia Research). * Huge number of minor bug fixes as a result of static analysis. Version 1.0.18 (2009-02-07) * Add Ogg/Vorbis support (thanks to John ffitch). * Remove captive FLAC library. * Many new features and bug fixes. * Generate Win32 and Win64 pre-compiled binaries. Version 1.0.17 (2006-08-31) * Add sndfile.hh C++ wrapper. * Update Win32 MinGW build instructions. * Minor bug fixes and cleanups. Version 1.0.16 (2006-04-30) * Add support for Broadcast (BEXT) chunks in WAV files. * Implement new commands SFC_GET_SIGNAL_MAX and SFC_GET_MAX_ALL_CHANNELS. * Add support for RIFX (big endian WAV variant). * Fix configure script bugs. * Fix bug in INST and MARK chunk writing for AIFF files. Version 1.0.15 (2006-03-16) * Fix some ia64 issues. * Fix precompiled DLL. * Minor bug fixes. Version 1.0.14 (2006-02-19) * Really fix MinGW compile problems. * Minor bug fixes. Version 1.0.13 (2006-01-21) * Fix for MinGW compiler problems. * Allow readin/write of instrument chunks from WAV and AIFF files. * Compile problem fix for Solaris compiler. * Minor cleanups and bug fixes. Version 1.0.12 (2005-09-30) * Add support for FLAC and Apple's Core Audio Format (CAF). * Add virtual I/O interface (still needs docs). * Cygwin and other Win32 fixes. * Minor bug fixes and cleanups. Version 1.0.11 (2004-11-15) * Add support for SD2 files. * Add read support for loop info in WAV and AIFF files. * Add more tests. * Improve type safety. * Minor optimisations and bug fixes. Version 1.0.10 (2004-06-15) * Fix AIFF read/write mode bugs. * Add support for compiling Win32 DLLS using MinGW. * Fix problems resulting in failed compiles with gcc-2.95. * Improve test suite. * Minor bug fixes. Version 1.0.9 (2004-03-30) * Add handling of AVR (Audio Visual Research) files. * Improve handling of WAVEFORMATEXTENSIBLE WAV files. * Fix for using pipes on Win32. Version 1.0.8 (2004-03-14) * Correct peak chunk handing for files with > 16 tracks. * Fix for WAV files with huge number of CUE chunks. Version 1.0.7 (2004-02-25) * Fix clip mode detection on ia64, MIPS and other CPUs. * Fix two MacOSX build problems. Version 1.0.6 (2004-02-08) * Added support for native Win32 file access API (Ross Bencina). * New mode to add clippling then a converting from float/double to integer would otherwise wrap around. * Fixed a bug in reading/writing files > 2Gig on Linux, Solaris and others. * Many minor bug fixes. * Other random fixes for Win32. Version 1.0.5 (2003-05-03) * Added support for HTK files. * Added new function sf_open_fd() to allow for secure opening of temporary files as well as reading/writing sound files embedded within larger container files. * Added string support for AIFF files. * Minor bug fixes and code cleanups. Version 1.0.4 (2003-02-02) * Added suport of PVF and XI files. * Added functionality for setting and retreiving strings from sound files. * Minor code cleanups and bug fixes. Version 1.0.3 (2002-12-09) * Minor bug fixes. Version 1.0.2 (2002-11-24) * Added support for VOX ADPCM. * Improved error reporting. * Added version scripting on Linux and Solaris. * Minor bug fixes. Version 1.0.1 (2002-09-14) * Added MAT and MAT5 file formats. * Minor bug fixes. Version 1.0.0 (2002-08-16) * Final release for 1.0.0. Version 1.0.0rc6 (2002-08-14) * Release candidate 6 for the 1.0.0 series. * MacOS9 fixes. Version 1.0.0rc5 (2002-08-10) * Release candidate 5 for the 1.0.0 series. * Changed the definition of sf_count_t which was causing problems when libsndfile was compiled with other libraries (ie WxWindows). * Minor bug fixes. * Documentation cleanup. Version 1.0.0rc4 (2002-08-03) * Release candidate 4 for the 1.0.0 series. * Minor bug fixes. * Fix broken Win32 "make check". Version 1.0.0rc3 (2002-08-02) * Release candidate 3 for the 1.0.0 series. * Fix bug where libsndfile was reading beyond the end of the data chunk. * Added on-the-fly header updates on write. * Fix a couple of documentation issues. Version 1.0.0rc2 (2002-06-24) * Release candidate 2 for the 1.0.0 series. * Fix compile problem for Win32. Version 1.0.0rc1 (2002-06-24) * Release candidate 1 for the 1.0.0 series. Version 0.0.28 (2002-04-27) * Last offical release of 0.0.X series of the library. Version 0.0.8 (1999-02-16) * First offical release. ================================================ FILE: vendor/libsndfile/include/sndfile.h ================================================ /* ** Copyright (C) 1999-2016 Erik de Castro Lopo ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU Lesser General Public License as published by ** the Free Software Foundation; either version 2.1 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* ** sndfile.h -- system-wide definitions ** ** API documentation is in the doc/ directory of the source code tarball ** and at http://www.mega-nerd.com/libsndfile/api.html. */ #ifndef SNDFILE_H #define SNDFILE_H /* This is the version 1.0.X header file. */ #define SNDFILE_1 #include #include #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* The following file types can be read and written. ** A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise ** ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and ** SF_FORMAT_SUBMASK can be used to separate the major and minor file ** types. */ enum { /* Major formats. */ SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian default). */ SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */ SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */ SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */ SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */ SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */ SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */ SF_FORMAT_VOC = 0x080000, /* VOC files. */ SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */ SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */ SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */ SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */ SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */ SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */ SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */ SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */ SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */ SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */ SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */ SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */ SF_FORMAT_CAF = 0x180000, /* Core Audio File format */ SF_FORMAT_WVE = 0x190000, /* Psion WVE format */ SF_FORMAT_OGG = 0x200000, /* Xiph OGG container */ SF_FORMAT_MPC2K = 0x210000, /* Akai MPC 2000 sampler */ SF_FORMAT_RF64 = 0x220000, /* RF64 WAV file */ /* Subtypes from here on. */ SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */ SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */ SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */ SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */ SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */ SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */ SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */ SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */ SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */ SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */ SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */ SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */ SF_FORMAT_VOX_ADPCM = 0x0021, /* OKI / Dialogix ADPCM */ SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */ SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */ SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */ SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */ SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */ SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */ SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */ SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */ SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */ SF_FORMAT_VORBIS = 0x0060, /* Xiph Vorbis encoding. */ SF_FORMAT_ALAC_16 = 0x0070, /* Apple Lossless Audio Codec (16 bit). */ SF_FORMAT_ALAC_20 = 0x0071, /* Apple Lossless Audio Codec (20 bit). */ SF_FORMAT_ALAC_24 = 0x0072, /* Apple Lossless Audio Codec (24 bit). */ SF_FORMAT_ALAC_32 = 0x0073, /* Apple Lossless Audio Codec (32 bit). */ /* Endian-ness options. */ SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */ SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */ SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */ SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */ SF_FORMAT_SUBMASK = 0x0000FFFF, SF_FORMAT_TYPEMASK = 0x0FFF0000, SF_FORMAT_ENDMASK = 0x30000000 } ; /* ** The following are the valid command numbers for the sf_command() ** interface. The use of these commands is documented in the file ** command.html in the doc directory of the source code distribution. */ enum { SFC_GET_LIB_VERSION = 0x1000, SFC_GET_LOG_INFO = 0x1001, SFC_GET_CURRENT_SF_INFO = 0x1002, SFC_GET_NORM_DOUBLE = 0x1010, SFC_GET_NORM_FLOAT = 0x1011, SFC_SET_NORM_DOUBLE = 0x1012, SFC_SET_NORM_FLOAT = 0x1013, SFC_SET_SCALE_FLOAT_INT_READ = 0x1014, SFC_SET_SCALE_INT_FLOAT_WRITE = 0x1015, SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020, SFC_GET_SIMPLE_FORMAT = 0x1021, SFC_GET_FORMAT_INFO = 0x1028, SFC_GET_FORMAT_MAJOR_COUNT = 0x1030, SFC_GET_FORMAT_MAJOR = 0x1031, SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032, SFC_GET_FORMAT_SUBTYPE = 0x1033, SFC_CALC_SIGNAL_MAX = 0x1040, SFC_CALC_NORM_SIGNAL_MAX = 0x1041, SFC_CALC_MAX_ALL_CHANNELS = 0x1042, SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043, SFC_GET_SIGNAL_MAX = 0x1044, SFC_GET_MAX_ALL_CHANNELS = 0x1045, SFC_SET_ADD_PEAK_CHUNK = 0x1050, SFC_SET_ADD_HEADER_PAD_CHUNK = 0x1051, SFC_UPDATE_HEADER_NOW = 0x1060, SFC_SET_UPDATE_HEADER_AUTO = 0x1061, SFC_FILE_TRUNCATE = 0x1080, SFC_SET_RAW_START_OFFSET = 0x1090, SFC_SET_DITHER_ON_WRITE = 0x10A0, SFC_SET_DITHER_ON_READ = 0x10A1, SFC_GET_DITHER_INFO_COUNT = 0x10A2, SFC_GET_DITHER_INFO = 0x10A3, SFC_GET_EMBED_FILE_INFO = 0x10B0, SFC_SET_CLIPPING = 0x10C0, SFC_GET_CLIPPING = 0x10C1, SFC_GET_CUE_COUNT = 0x10CD, SFC_GET_CUE = 0x10CE, SFC_SET_CUE = 0x10CF, SFC_GET_INSTRUMENT = 0x10D0, SFC_SET_INSTRUMENT = 0x10D1, SFC_GET_LOOP_INFO = 0x10E0, SFC_GET_BROADCAST_INFO = 0x10F0, SFC_SET_BROADCAST_INFO = 0x10F1, SFC_GET_CHANNEL_MAP_INFO = 0x1100, SFC_SET_CHANNEL_MAP_INFO = 0x1101, SFC_RAW_DATA_NEEDS_ENDSWAP = 0x1110, /* Support for Wavex Ambisonics Format */ SFC_WAVEX_SET_AMBISONIC = 0x1200, SFC_WAVEX_GET_AMBISONIC = 0x1201, /* ** RF64 files can be set so that on-close, writable files that have less ** than 4GB of data in them are converted to RIFF/WAV, as per EBU ** recommendations. */ SFC_RF64_AUTO_DOWNGRADE = 0x1210, SFC_SET_VBR_ENCODING_QUALITY = 0x1300, SFC_SET_COMPRESSION_LEVEL = 0x1301, /* Cart Chunk support */ SFC_SET_CART_INFO = 0x1400, SFC_GET_CART_INFO = 0x1401, /* Following commands for testing only. */ SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001, /* ** SFC_SET_ADD_* values are deprecated and will disappear at some ** time in the future. They are guaranteed to be here up to and ** including version 1.0.8 to avoid breakage of existing software. ** They currently do nothing and will continue to do nothing. */ SFC_SET_ADD_DITHER_ON_WRITE = 0x1070, SFC_SET_ADD_DITHER_ON_READ = 0x1071 } ; /* ** String types that can be set and read from files. Not all file types ** support this and even the file types which support one, may not support ** all string types. */ enum { SF_STR_TITLE = 0x01, SF_STR_COPYRIGHT = 0x02, SF_STR_SOFTWARE = 0x03, SF_STR_ARTIST = 0x04, SF_STR_COMMENT = 0x05, SF_STR_DATE = 0x06, SF_STR_ALBUM = 0x07, SF_STR_LICENSE = 0x08, SF_STR_TRACKNUMBER = 0x09, SF_STR_GENRE = 0x10 } ; /* ** Use the following as the start and end index when doing metadata ** transcoding. */ #define SF_STR_FIRST SF_STR_TITLE #define SF_STR_LAST SF_STR_GENRE enum { /* True and false */ SF_FALSE = 0, SF_TRUE = 1, /* Modes for opening files. */ SFM_READ = 0x10, SFM_WRITE = 0x20, SFM_RDWR = 0x30, SF_AMBISONIC_NONE = 0x40, SF_AMBISONIC_B_FORMAT = 0x41 } ; /* Public error values. These are guaranteed to remain unchanged for the duration ** of the library major version number. ** There are also a large number of private error numbers which are internal to ** the library which can change at any time. */ enum { SF_ERR_NO_ERROR = 0, SF_ERR_UNRECOGNISED_FORMAT = 1, SF_ERR_SYSTEM = 2, SF_ERR_MALFORMED_FILE = 3, SF_ERR_UNSUPPORTED_ENCODING = 4 } ; /* Channel map values (used with SFC_SET/GET_CHANNEL_MAP). */ enum { SF_CHANNEL_MAP_INVALID = 0, SF_CHANNEL_MAP_MONO = 1, SF_CHANNEL_MAP_LEFT, /* Apple calls this 'Left' */ SF_CHANNEL_MAP_RIGHT, /* Apple calls this 'Right' */ SF_CHANNEL_MAP_CENTER, /* Apple calls this 'Center' */ SF_CHANNEL_MAP_FRONT_LEFT, SF_CHANNEL_MAP_FRONT_RIGHT, SF_CHANNEL_MAP_FRONT_CENTER, SF_CHANNEL_MAP_REAR_CENTER, /* Apple calls this 'Center Surround', Msft calls this 'Back Center' */ SF_CHANNEL_MAP_REAR_LEFT, /* Apple calls this 'Left Surround', Msft calls this 'Back Left' */ SF_CHANNEL_MAP_REAR_RIGHT, /* Apple calls this 'Right Surround', Msft calls this 'Back Right' */ SF_CHANNEL_MAP_LFE, /* Apple calls this 'LFEScreen', Msft calls this 'Low Frequency' */ SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, /* Apple calls this 'Left Center' */ SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, /* Apple calls this 'Right Center */ SF_CHANNEL_MAP_SIDE_LEFT, /* Apple calls this 'Left Surround Direct' */ SF_CHANNEL_MAP_SIDE_RIGHT, /* Apple calls this 'Right Surround Direct' */ SF_CHANNEL_MAP_TOP_CENTER, /* Apple calls this 'Top Center Surround' */ SF_CHANNEL_MAP_TOP_FRONT_LEFT, /* Apple calls this 'Vertical Height Left' */ SF_CHANNEL_MAP_TOP_FRONT_RIGHT, /* Apple calls this 'Vertical Height Right' */ SF_CHANNEL_MAP_TOP_FRONT_CENTER, /* Apple calls this 'Vertical Height Center' */ SF_CHANNEL_MAP_TOP_REAR_LEFT, /* Apple and MS call this 'Top Back Left' */ SF_CHANNEL_MAP_TOP_REAR_RIGHT, /* Apple and MS call this 'Top Back Right' */ SF_CHANNEL_MAP_TOP_REAR_CENTER, /* Apple and MS call this 'Top Back Center' */ SF_CHANNEL_MAP_AMBISONIC_B_W, SF_CHANNEL_MAP_AMBISONIC_B_X, SF_CHANNEL_MAP_AMBISONIC_B_Y, SF_CHANNEL_MAP_AMBISONIC_B_Z, SF_CHANNEL_MAP_MAX } ; /* A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer. */ typedef struct SNDFILE_tag SNDFILE ; /* The following typedef is system specific and is defined when libsndfile is ** compiled. sf_count_t will be a 64 bit value when the underlying OS allows ** 64 bit file offsets. ** On windows, we need to allow the same header file to be compiler by both GCC ** and the Microsoft compiler. */ #if (defined (_MSCVER) || defined (_MSC_VER) && (_MSC_VER < 1310)) typedef __int64 sf_count_t ; #define SF_COUNT_MAX 0x7fffffffffffffffi64 #else typedef __int64 sf_count_t ; #define SF_COUNT_MAX 0x7FFFFFFFFFFFFFFFLL #endif /* A pointer to a SF_INFO structure is passed to sf_open () and filled in. ** On write, the SF_INFO structure is filled in by the user and passed into ** sf_open (). */ struct SF_INFO { sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */ int samplerate ; int channels ; int format ; int sections ; int seekable ; } ; typedef struct SF_INFO SF_INFO ; /* The SF_FORMAT_INFO struct is used to retrieve information about the sound ** file formats libsndfile supports using the sf_command () interface. ** ** Using this interface will allow applications to support new file formats ** and encoding types when libsndfile is upgraded, without requiring ** re-compilation of the application. ** ** Please consult the libsndfile documentation (particularly the information ** on the sf_command () interface) for examples of its use. */ typedef struct { int format ; const char *name ; const char *extension ; } SF_FORMAT_INFO ; /* ** Enums and typedefs for adding dither on read and write. ** See the html documentation for sf_command(), SFC_SET_DITHER_ON_WRITE ** and SFC_SET_DITHER_ON_READ. */ enum { SFD_DEFAULT_LEVEL = 0, SFD_CUSTOM_LEVEL = 0x40000000, SFD_NO_DITHER = 500, SFD_WHITE = 501, SFD_TRIANGULAR_PDF = 502 } ; typedef struct { int type ; double level ; const char *name ; } SF_DITHER_INFO ; /* Struct used to retrieve information about a file embedded within a ** larger file. See SFC_GET_EMBED_FILE_INFO. */ typedef struct { sf_count_t offset ; sf_count_t length ; } SF_EMBED_FILE_INFO ; /* ** Struct used to retrieve cue marker information from a file */ typedef struct { int32_t indx ; uint32_t position ; int32_t fcc_chunk ; int32_t chunk_start ; int32_t block_start ; uint32_t sample_offset ; char name [256] ; } SF_CUE_POINT ; #define SF_CUES_VAR(count) \ struct \ { uint32_t cue_count ; \ SF_CUE_POINT cue_points [count] ; \ } typedef SF_CUES_VAR (100) SF_CUES ; /* ** Structs used to retrieve music sample information from a file. */ enum { /* ** The loop mode field in SF_INSTRUMENT will be one of the following. */ SF_LOOP_NONE = 800, SF_LOOP_FORWARD, SF_LOOP_BACKWARD, SF_LOOP_ALTERNATING } ; typedef struct { int gain ; char basenote, detune ; char velocity_lo, velocity_hi ; char key_lo, key_hi ; int loop_count ; struct { int mode ; uint32_t start ; uint32_t end ; uint32_t count ; } loops [16] ; /* make variable in a sensible way */ } SF_INSTRUMENT ; /* Struct used to retrieve loop information from a file.*/ typedef struct { short time_sig_num ; /* any positive integer > 0 */ short time_sig_den ; /* any positive power of 2 > 0 */ int loop_mode ; /* see SF_LOOP enum */ int num_beats ; /* this is NOT the amount of quarter notes !!!*/ /* a full bar of 4/4 is 4 beats */ /* a full bar of 7/8 is 7 beats */ float bpm ; /* suggestion, as it can be calculated using other fields:*/ /* file's length, file's sampleRate and our time_sig_den*/ /* -> bpms are always the amount of _quarter notes_ per minute */ int root_key ; /* MIDI note, or -1 for None */ int future [6] ; } SF_LOOP_INFO ; /* Struct used to retrieve broadcast (EBU) information from a file. ** Strongly (!) based on EBU "bext" chunk format used in Broadcast WAVE. */ #define SF_BROADCAST_INFO_VAR(coding_hist_size) \ struct \ { char description [256] ; \ char originator [32] ; \ char originator_reference [32] ; \ char origination_date [10] ; \ char origination_time [8] ; \ uint32_t time_reference_low ; \ uint32_t time_reference_high ; \ short version ; \ char umid [64] ; \ char reserved [190] ; \ uint32_t coding_history_size ; \ char coding_history [coding_hist_size] ; \ } /* SF_BROADCAST_INFO is the above struct with coding_history field of 256 bytes. */ typedef SF_BROADCAST_INFO_VAR (256) SF_BROADCAST_INFO ; struct SF_CART_TIMER { char usage [4] ; int32_t value ; } ; typedef struct SF_CART_TIMER SF_CART_TIMER ; #define SF_CART_INFO_VAR(p_tag_text_size) \ struct \ { char version [4] ; \ char title [64] ; \ char artist [64] ; \ char cut_id [64] ; \ char client_id [64] ; \ char category [64] ; \ char classification [64] ; \ char out_cue [64] ; \ char start_date [10] ; \ char start_time [8] ; \ char end_date [10] ; \ char end_time [8] ; \ char producer_app_id [64] ; \ char producer_app_version [64] ; \ char user_def [64] ; \ int32_t level_reference ; \ SF_CART_TIMER post_timers [8] ; \ char reserved [276] ; \ char url [1024] ; \ uint32_t tag_text_size ; \ char tag_text [p_tag_text_size] ; \ } typedef SF_CART_INFO_VAR (256) SF_CART_INFO ; /* Virtual I/O functionality. */ typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ; typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ; typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data) ; typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data) ; typedef sf_count_t (*sf_vio_tell) (void *user_data) ; struct SF_VIRTUAL_IO { sf_vio_get_filelen get_filelen ; sf_vio_seek seek ; sf_vio_read read ; sf_vio_write write ; sf_vio_tell tell ; } ; typedef struct SF_VIRTUAL_IO SF_VIRTUAL_IO ; /* Open the specified file for read, write or both. On error, this will ** return a NULL pointer. To find the error number, pass a NULL SNDFILE ** to sf_strerror (). ** All calls to sf_open() should be matched with a call to sf_close(). */ SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ; /* Use the existing file descriptor to create a SNDFILE object. If close_desc ** is TRUE, the file descriptor will be closed when sf_close() is called. If ** it is FALSE, the descriptor will not be closed. ** When passed a descriptor like this, the library will assume that the start ** of file header is at the current file offset. This allows sound files within ** larger container files to be read and/or written. ** On error, this will return a NULL pointer. To find the error number, pass a ** NULL SNDFILE to sf_strerror (). ** All calls to sf_open_fd() should be matched with a call to sf_close(). */ SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ; SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ; /* sf_error () returns a error number which can be translated to a text ** string using sf_error_number(). */ int sf_error (SNDFILE *sndfile) ; /* sf_strerror () returns to the caller a pointer to the current error message for ** the given SNDFILE. */ const char* sf_strerror (SNDFILE *sndfile) ; /* sf_error_number () allows the retrieval of the error string for each internal ** error number. ** */ const char* sf_error_number (int errnum) ; /* The following two error functions are deprecated but they will remain in the ** library for the foreseeable future. The function sf_strerror() should be used ** in their place. */ int sf_perror (SNDFILE *sndfile) ; int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ; /* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ int sf_command (SNDFILE *sndfile, int command, void *data, int datasize) ; /* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ int sf_format_check (const SF_INFO *info) ; /* Seek within the waveform data chunk of the SNDFILE. sf_seek () uses ** the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as ** stdio.h function fseek (). ** An offset of zero with whence set to SEEK_SET will position the ** read / write pointer to the first data sample. ** On success sf_seek returns the current position in (multi-channel) ** samples from the start of the file. ** Please see the libsndfile documentation for moving the read pointer ** separately from the write pointer on files open in mode SFM_RDWR. ** On error all of these functions return -1. */ enum { SF_SEEK_SET = SEEK_SET, SF_SEEK_CUR = SEEK_CUR, SF_SEEK_END = SEEK_END } ; sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ; /* Functions for retrieving and setting string data within sound files. ** Not all file types support this features; AIFF and WAV do. For both ** functions, the str_type parameter must be one of the SF_STR_* values ** defined above. ** On error, sf_set_string() returns non-zero while sf_get_string() ** returns NULL. */ int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ; const char* sf_get_string (SNDFILE *sndfile, int str_type) ; /* Return the library version string. */ const char * sf_version_string (void) ; /* Return the current byterate at this point in the file. The byte rate in this ** case is the number of bytes per second of audio data. For instance, for a ** stereo, 18 bit PCM encoded file with an 16kHz sample rate, the byte rate ** would be 2 (stereo) * 2 (two bytes per sample) * 16000 => 64000 bytes/sec. ** For some file formats the returned value will be accurate and exact, for some ** it will be a close approximation, for some it will be the average bitrate for ** the whole file and for some it will be a time varying value that was accurate ** when the file was most recently read or written. ** To get the bitrate, multiple this value by 8. ** Returns -1 for unknown. */ int sf_current_byterate (SNDFILE *sndfile) ; /* Functions for reading/writing the waveform data of a sound file. */ sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ; sf_count_t sf_write_raw (SNDFILE *sndfile, const void *ptr, sf_count_t bytes) ; /* Functions for reading and writing the data chunk in terms of frames. ** The number of items actually read/written = frames * number of channels. ** sf_xxxx_raw read/writes the raw data bytes from/to the file ** sf_xxxx_short passes data in the native short format ** sf_xxxx_int passes data in the native int format ** sf_xxxx_float passes data in the native float format ** sf_xxxx_double passes data in the native double format ** All of these read/write function return number of frames read/written. */ sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ; sf_count_t sf_writef_short (SNDFILE *sndfile, const short *ptr, sf_count_t frames) ; sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ; sf_count_t sf_writef_int (SNDFILE *sndfile, const int *ptr, sf_count_t frames) ; sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ; sf_count_t sf_writef_float (SNDFILE *sndfile, const float *ptr, sf_count_t frames) ; sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ; sf_count_t sf_writef_double (SNDFILE *sndfile, const double *ptr, sf_count_t frames) ; /* Functions for reading and writing the data chunk in terms of items. ** Otherwise similar to above. ** All of these read/write function return number of items read/written. */ sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ; sf_count_t sf_write_short (SNDFILE *sndfile, const short *ptr, sf_count_t items) ; sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ; sf_count_t sf_write_int (SNDFILE *sndfile, const int *ptr, sf_count_t items) ; sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ; sf_count_t sf_write_float (SNDFILE *sndfile, const float *ptr, sf_count_t items) ; sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ; sf_count_t sf_write_double (SNDFILE *sndfile, const double *ptr, sf_count_t items) ; /* Close the SNDFILE and clean up all memory allocations associated with this ** file. ** Returns 0 on success, or an error number. */ int sf_close (SNDFILE *sndfile) ; /* If the file is opened SFM_WRITE or SFM_RDWR, call fsync() on the file ** to force the writing of data to disk. If the file is opened SFM_READ ** no action is taken. */ void sf_write_sync (SNDFILE *sndfile) ; /* The function sf_wchar_open() is Windows Only! ** Open a file passing in a Windows Unicode filename. Otherwise, this is ** the same as sf_open(). ** ** In order for this to work, you need to do the following: ** ** #include ** #define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 ** #including */ #if (defined (ENABLE_SNDFILE_WINDOWS_PROTOTYPES) && ENABLE_SNDFILE_WINDOWS_PROTOTYPES) SNDFILE* sf_wchar_open (LPCWSTR wpath, int mode, SF_INFO *sfinfo) ; #endif /* Getting and setting of chunks from within a sound file. ** ** These functions allow the getting and setting of chunks within a sound file ** (for those formats which allow it). ** ** These functions fail safely. Specifically, they will not allow you to overwrite ** existing chunks or add extra versions of format specific reserved chunks but ** should allow you to retrieve any and all chunks (may not be implemented for ** all chunks or all file formats). */ struct SF_CHUNK_INFO { char id [64] ; /* The chunk identifier. */ unsigned id_size ; /* The size of the chunk identifier. */ unsigned datalen ; /* The size of that data. */ void *data ; /* Pointer to the data. */ } ; typedef struct SF_CHUNK_INFO SF_CHUNK_INFO ; /* Set the specified chunk info (must be done before any audio data is written ** to the file). This will fail for format specific reserved chunks. ** The chunk_info->data pointer must be valid until the file is closed. ** Returns SF_ERR_NO_ERROR on success or non-zero on failure. */ int sf_set_chunk (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info) ; /* ** An opaque structure to an iterator over the all chunks of a given id */ typedef struct SF_CHUNK_ITERATOR SF_CHUNK_ITERATOR ; /* Get an iterator for all chunks matching chunk_info. ** The iterator will point to the first chunk matching chunk_info. ** Chunks are matching, if (chunk_info->id) matches the first ** (chunk_info->id_size) bytes of a chunk found in the SNDFILE* handle. ** If chunk_info is NULL, an iterator to all chunks in the SNDFILE* handle ** is returned. ** The values of chunk_info->datalen and chunk_info->data are ignored. ** If no matching chunks are found in the sndfile, NULL is returned. ** The returned iterator will stay valid until one of the following occurs: ** a) The sndfile is closed. ** b) A new chunk is added using sf_set_chunk(). ** c) Another chunk iterator function is called on the same SNDFILE* handle ** that causes the iterator to be modified. ** The memory for the iterator belongs to the SNDFILE* handle and is freed when ** sf_close() is called. */ SF_CHUNK_ITERATOR * sf_get_chunk_iterator (SNDFILE * sndfile, const SF_CHUNK_INFO * chunk_info) ; /* Iterate through chunks by incrementing the iterator. ** Increments the iterator and returns a handle to the new one. ** After this call, iterator will no longer be valid, and you must use the ** newly returned handle from now on. ** The returned handle can be used to access the next chunk matching ** the criteria as defined in sf_get_chunk_iterator(). ** If iterator points to the last chunk, this will free all resources ** associated with iterator and return NULL. ** The returned iterator will stay valid until sf_get_chunk_iterator_next ** is called again, the sndfile is closed or a new chunk us added. */ SF_CHUNK_ITERATOR * sf_next_chunk_iterator (SF_CHUNK_ITERATOR * iterator) ; /* Get the size of the specified chunk. ** If the specified chunk exists, the size will be returned in the ** datalen field of the SF_CHUNK_INFO struct. ** Additionally, the id of the chunk will be copied to the id ** field of the SF_CHUNK_INFO struct and it's id_size field will ** be updated accordingly. ** If the chunk doesn't exist chunk_info->datalen will be zero, and the ** id and id_size fields will be undefined. ** The function will return SF_ERR_NO_ERROR on success or non-zero on ** failure. */ int sf_get_chunk_size (const SF_CHUNK_ITERATOR * it, SF_CHUNK_INFO * chunk_info) ; /* Get the specified chunk data. ** If the specified chunk exists, up to chunk_info->datalen bytes of ** the chunk data will be copied into the chunk_info->data buffer ** (allocated by the caller) and the chunk_info->datalen field ** updated to reflect the size of the data. The id and id_size ** field will be updated according to the retrieved chunk ** If the chunk doesn't exist chunk_info->datalen will be zero, and the ** id and id_size fields will be undefined. ** The function will return SF_ERR_NO_ERROR on success or non-zero on ** failure. */ int sf_get_chunk_data (const SF_CHUNK_ITERATOR * it, SF_CHUNK_INFO * chunk_info) ; #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* SNDFILE_H */ ================================================ FILE: vendor/libsndfile/include/sndfile.hh ================================================ /* ** Copyright (C) 2005-2012 Erik de Castro Lopo ** ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the author nor the names of any contributors may be used ** to endorse or promote products derived from this software without ** specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* ** The above modified BSD style license (GPL and LGPL compatible) applies to ** this file. It does not apply to libsndfile itself which is released under ** the GNU LGPL or the libsndfile test suite which is released under the GNU ** GPL. ** This means that this header file can be used under this modified BSD style ** license, but the LGPL still holds for the libsndfile library itself. */ /* ** sndfile.hh -- A lightweight C++ wrapper for the libsndfile API. ** ** All the methods are inlines and all functionality is contained in this ** file. There is no separate implementation file. ** ** API documentation is in the doc/ directory of the source code tarball ** and at http://www.mega-nerd.com/libsndfile/api.html. */ #ifndef SNDFILE_HH #define SNDFILE_HH #include #include #include // for std::nothrow class SndfileHandle { private : struct SNDFILE_ref { SNDFILE_ref (void) ; ~SNDFILE_ref (void) ; SNDFILE *sf ; SF_INFO sfinfo ; int ref ; } ; SNDFILE_ref *p ; public : /* Default constructor */ SndfileHandle (void) : p (NULL) {} ; SndfileHandle (const char *path, int mode = SFM_READ, int format = 0, int channels = 0, int samplerate = 0) ; SndfileHandle (std::string const & path, int mode = SFM_READ, int format = 0, int channels = 0, int samplerate = 0) ; SndfileHandle (int fd, bool close_desc, int mode = SFM_READ, int format = 0, int channels = 0, int samplerate = 0) ; SndfileHandle (SF_VIRTUAL_IO &sfvirtual, void *user_data, int mode = SFM_READ, int format = 0, int channels = 0, int samplerate = 0) ; #ifdef ENABLE_SNDFILE_WINDOWS_PROTOTYPES SndfileHandle (LPCWSTR wpath, int mode = SFM_READ, int format = 0, int channels = 0, int samplerate = 0) ; #endif ~SndfileHandle (void) ; SndfileHandle (const SndfileHandle &orig) ; SndfileHandle & operator = (const SndfileHandle &rhs) ; /* Mainly for debugging/testing. */ int refCount (void) const { return (p == NULL) ? 0 : p->ref ; } operator bool () const { return (p != NULL) ; } bool operator == (const SndfileHandle &rhs) const { return (p == rhs.p) ; } sf_count_t frames (void) const { return p ? p->sfinfo.frames : 0 ; } int format (void) const { return p ? p->sfinfo.format : 0 ; } int channels (void) const { return p ? p->sfinfo.channels : 0 ; } int samplerate (void) const { return p ? p->sfinfo.samplerate : 0 ; } int error (void) const ; const char * strError (void) const ; int command (int cmd, void *data, int datasize) ; sf_count_t seek (sf_count_t frames, int whence) ; void writeSync (void) ; int setString (int str_type, const char* str) ; const char* getString (int str_type) const ; static int formatCheck (int format, int channels, int samplerate) ; sf_count_t read (short *ptr, sf_count_t items) ; sf_count_t read (int *ptr, sf_count_t items) ; sf_count_t read (float *ptr, sf_count_t items) ; sf_count_t read (double *ptr, sf_count_t items) ; sf_count_t write (const short *ptr, sf_count_t items) ; sf_count_t write (const int *ptr, sf_count_t items) ; sf_count_t write (const float *ptr, sf_count_t items) ; sf_count_t write (const double *ptr, sf_count_t items) ; sf_count_t readf (short *ptr, sf_count_t frames) ; sf_count_t readf (int *ptr, sf_count_t frames) ; sf_count_t readf (float *ptr, sf_count_t frames) ; sf_count_t readf (double *ptr, sf_count_t frames) ; sf_count_t writef (const short *ptr, sf_count_t frames) ; sf_count_t writef (const int *ptr, sf_count_t frames) ; sf_count_t writef (const float *ptr, sf_count_t frames) ; sf_count_t writef (const double *ptr, sf_count_t frames) ; sf_count_t readRaw (void *ptr, sf_count_t bytes) ; sf_count_t writeRaw (const void *ptr, sf_count_t bytes) ; /**< Raw access to the handle. SndfileHandle keeps ownership. */ SNDFILE * rawHandle (void) ; /**< Take ownership of handle, if reference count is 1. */ SNDFILE * takeOwnership (void) ; } ; /*============================================================================== ** Nothing but implementation below. */ inline SndfileHandle::SNDFILE_ref::SNDFILE_ref (void) : sf (NULL), sfinfo (), ref (1) {} inline SndfileHandle::SNDFILE_ref::~SNDFILE_ref (void) { if (sf != NULL) sf_close (sf) ; } inline SndfileHandle::SndfileHandle (const char *path, int mode, int fmt, int chans, int srate) : p (NULL) { p = new (std::nothrow) SNDFILE_ref () ; if (p != NULL) { p->ref = 1 ; p->sfinfo.frames = 0 ; p->sfinfo.channels = chans ; p->sfinfo.format = fmt ; p->sfinfo.samplerate = srate ; p->sfinfo.sections = 0 ; p->sfinfo.seekable = 0 ; p->sf = sf_open (path, mode, &p->sfinfo) ; } ; return ; } /* SndfileHandle const char * constructor */ inline SndfileHandle::SndfileHandle (std::string const & path, int mode, int fmt, int chans, int srate) : p (NULL) { p = new (std::nothrow) SNDFILE_ref () ; if (p != NULL) { p->ref = 1 ; p->sfinfo.frames = 0 ; p->sfinfo.channels = chans ; p->sfinfo.format = fmt ; p->sfinfo.samplerate = srate ; p->sfinfo.sections = 0 ; p->sfinfo.seekable = 0 ; p->sf = sf_open (path.c_str (), mode, &p->sfinfo) ; } ; return ; } /* SndfileHandle std::string constructor */ inline SndfileHandle::SndfileHandle (int fd, bool close_desc, int mode, int fmt, int chans, int srate) : p (NULL) { if (fd < 0) return ; p = new (std::nothrow) SNDFILE_ref () ; if (p != NULL) { p->ref = 1 ; p->sfinfo.frames = 0 ; p->sfinfo.channels = chans ; p->sfinfo.format = fmt ; p->sfinfo.samplerate = srate ; p->sfinfo.sections = 0 ; p->sfinfo.seekable = 0 ; p->sf = sf_open_fd (fd, mode, &p->sfinfo, close_desc) ; } ; return ; } /* SndfileHandle fd constructor */ inline SndfileHandle::SndfileHandle (SF_VIRTUAL_IO &sfvirtual, void *user_data, int mode, int fmt, int chans, int srate) : p (NULL) { p = new (std::nothrow) SNDFILE_ref () ; if (p != NULL) { p->ref = 1 ; p->sfinfo.frames = 0 ; p->sfinfo.channels = chans ; p->sfinfo.format = fmt ; p->sfinfo.samplerate = srate ; p->sfinfo.sections = 0 ; p->sfinfo.seekable = 0 ; p->sf = sf_open_virtual (&sfvirtual, mode, &p->sfinfo, user_data) ; } ; return ; } /* SndfileHandle std::string constructor */ inline SndfileHandle::~SndfileHandle (void) { if (p != NULL && --p->ref == 0) delete p ; } /* SndfileHandle destructor */ inline SndfileHandle::SndfileHandle (const SndfileHandle &orig) : p (orig.p) { if (p != NULL) ++p->ref ; } /* SndfileHandle copy constructor */ inline SndfileHandle & SndfileHandle::operator = (const SndfileHandle &rhs) { if (&rhs == this) return *this ; if (p != NULL && --p->ref == 0) delete p ; p = rhs.p ; if (p != NULL) ++p->ref ; return *this ; } /* SndfileHandle assignment operator */ inline int SndfileHandle::error (void) const { return sf_error (p->sf) ; } inline const char * SndfileHandle::strError (void) const { return sf_strerror (p->sf) ; } inline int SndfileHandle::command (int cmd, void *data, int datasize) { return sf_command (p->sf, cmd, data, datasize) ; } inline sf_count_t SndfileHandle::seek (sf_count_t frame_count, int whence) { return sf_seek (p->sf, frame_count, whence) ; } inline void SndfileHandle::writeSync (void) { sf_write_sync (p->sf) ; } inline int SndfileHandle::setString (int str_type, const char* str) { return sf_set_string (p->sf, str_type, str) ; } inline const char* SndfileHandle::getString (int str_type) const { return sf_get_string (p->sf, str_type) ; } inline int SndfileHandle::formatCheck (int fmt, int chans, int srate) { SF_INFO sfinfo ; sfinfo.frames = 0 ; sfinfo.channels = chans ; sfinfo.format = fmt ; sfinfo.samplerate = srate ; sfinfo.sections = 0 ; sfinfo.seekable = 0 ; return sf_format_check (&sfinfo) ; } /*---------------------------------------------------------------------*/ inline sf_count_t SndfileHandle::read (short *ptr, sf_count_t items) { return sf_read_short (p->sf, ptr, items) ; } inline sf_count_t SndfileHandle::read (int *ptr, sf_count_t items) { return sf_read_int (p->sf, ptr, items) ; } inline sf_count_t SndfileHandle::read (float *ptr, sf_count_t items) { return sf_read_float (p->sf, ptr, items) ; } inline sf_count_t SndfileHandle::read (double *ptr, sf_count_t items) { return sf_read_double (p->sf, ptr, items) ; } inline sf_count_t SndfileHandle::write (const short *ptr, sf_count_t items) { return sf_write_short (p->sf, ptr, items) ; } inline sf_count_t SndfileHandle::write (const int *ptr, sf_count_t items) { return sf_write_int (p->sf, ptr, items) ; } inline sf_count_t SndfileHandle::write (const float *ptr, sf_count_t items) { return sf_write_float (p->sf, ptr, items) ; } inline sf_count_t SndfileHandle::write (const double *ptr, sf_count_t items) { return sf_write_double (p->sf, ptr, items) ; } inline sf_count_t SndfileHandle::readf (short *ptr, sf_count_t frame_count) { return sf_readf_short (p->sf, ptr, frame_count) ; } inline sf_count_t SndfileHandle::readf (int *ptr, sf_count_t frame_count) { return sf_readf_int (p->sf, ptr, frame_count) ; } inline sf_count_t SndfileHandle::readf (float *ptr, sf_count_t frame_count) { return sf_readf_float (p->sf, ptr, frame_count) ; } inline sf_count_t SndfileHandle::readf (double *ptr, sf_count_t frame_count) { return sf_readf_double (p->sf, ptr, frame_count) ; } inline sf_count_t SndfileHandle::writef (const short *ptr, sf_count_t frame_count) { return sf_writef_short (p->sf, ptr, frame_count) ; } inline sf_count_t SndfileHandle::writef (const int *ptr, sf_count_t frame_count) { return sf_writef_int (p->sf, ptr, frame_count) ; } inline sf_count_t SndfileHandle::writef (const float *ptr, sf_count_t frame_count) { return sf_writef_float (p->sf, ptr, frame_count) ; } inline sf_count_t SndfileHandle::writef (const double *ptr, sf_count_t frame_count) { return sf_writef_double (p->sf, ptr, frame_count) ; } inline sf_count_t SndfileHandle::readRaw (void *ptr, sf_count_t bytes) { return sf_read_raw (p->sf, ptr, bytes) ; } inline sf_count_t SndfileHandle::writeRaw (const void *ptr, sf_count_t bytes) { return sf_write_raw (p->sf, ptr, bytes) ; } inline SNDFILE * SndfileHandle::rawHandle (void) { return (p ? p->sf : NULL) ; } inline SNDFILE * SndfileHandle::takeOwnership (void) { if (p == NULL || (p->ref != 1)) return NULL ; SNDFILE * sf = p->sf ; p->sf = NULL ; delete p ; p = NULL ; return sf ; } #ifdef ENABLE_SNDFILE_WINDOWS_PROTOTYPES inline SndfileHandle::SndfileHandle (LPCWSTR wpath, int mode, int fmt, int chans, int srate) : p (NULL) { p = new (std::nothrow) SNDFILE_ref () ; if (p != NULL) { p->ref = 1 ; p->sfinfo.frames = 0 ; p->sfinfo.channels = chans ; p->sfinfo.format = fmt ; p->sfinfo.samplerate = srate ; p->sfinfo.sections = 0 ; p->sfinfo.seekable = 0 ; p->sf = sf_wchar_open (wpath, mode, &p->sfinfo) ; } ; return ; } /* SndfileHandle const wchar_t * constructor */ #endif #endif /* SNDFILE_HH */ ================================================ FILE: vendor/libsndfile/lib/Win32/libsndfile-1.def ================================================ ; Auto-generated by create_symbols_file.py LIBRARY libsndfile-1.dll EXPORTS sf_command @1 sf_open @2 sf_close @3 sf_seek @4 sf_error @7 sf_perror @8 sf_error_str @9 sf_error_number @10 sf_format_check @11 sf_read_raw @16 sf_readf_short @17 sf_readf_int @18 sf_readf_float @19 sf_readf_double @20 sf_read_short @21 sf_read_int @22 sf_read_float @23 sf_read_double @24 sf_write_raw @32 sf_writef_short @33 sf_writef_int @34 sf_writef_float @35 sf_writef_double @36 sf_write_short @37 sf_write_int @38 sf_write_float @39 sf_write_double @40 sf_strerror @50 sf_get_string @60 sf_set_string @61 sf_version_string @68 sf_open_fd @70 sf_wchar_open @71 sf_open_virtual @80 sf_write_sync @90 sf_set_chunk @100 sf_get_chunk_size @101 sf_get_chunk_data @102 sf_get_chunk_iterator @103 sf_next_chunk_iterator @104 sf_current_byterate @110 ================================================ FILE: vendor/libsndfile/lib/Win32/pkgconfig/sndfile.pc ================================================ prefix=c:/devel/target/libsndfile exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include Name: sndfile Description: A library for reading and writing audio files Requires: Version: 1.0.28 Libs: -L${libdir} -lsndfile Libs.private: Ext/libflac.la Ext/libvorbis.la Ext/libogg.la Cflags: -I${includedir} ================================================ FILE: vendor/libsndfile/lib/Win64/libsndfile-1.def ================================================ ; Auto-generated by create_symbols_file.py LIBRARY libsndfile-1.dll EXPORTS sf_command @1 sf_open @2 sf_close @3 sf_seek @4 sf_error @7 sf_perror @8 sf_error_str @9 sf_error_number @10 sf_format_check @11 sf_read_raw @16 sf_readf_short @17 sf_readf_int @18 sf_readf_float @19 sf_readf_double @20 sf_read_short @21 sf_read_int @22 sf_read_float @23 sf_read_double @24 sf_write_raw @32 sf_writef_short @33 sf_writef_int @34 sf_writef_float @35 sf_writef_double @36 sf_write_short @37 sf_write_int @38 sf_write_float @39 sf_write_double @40 sf_strerror @50 sf_get_string @60 sf_set_string @61 sf_version_string @68 sf_open_fd @70 sf_wchar_open @71 sf_open_virtual @80 sf_write_sync @90 sf_set_chunk @100 sf_get_chunk_size @101 sf_get_chunk_data @102 sf_get_chunk_iterator @103 sf_next_chunk_iterator @104 sf_current_byterate @110 ================================================ FILE: vendor/libsndfile/lib/Win64/pkgconfig/sndfile.pc ================================================ prefix=c:/devel/target/libsndfile exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include Name: sndfile Description: A library for reading and writing audio files Requires: Version: 1.0.28 Libs: -L${libdir} -lsndfile Libs.private: Ext/libflac.la Ext/libvorbis.la Ext/libogg.la Cflags: -I${includedir} ================================================ FILE: vendor/milessdk/include/mss.h ================================================ #pragma once // fake mss.h header for use with re3, to make using mss32.dll possible // gta3 uses miles 6.1a // check https://github.com/withmorten/re3mss for more info #include typedef char C8; typedef uint8_t U8; typedef int8_t S8; typedef int16_t S16; typedef uint16_t U16; typedef int32_t S32; typedef uint32_t U32; typedef float F32; typedef double F64; typedef void *HSTREAM; typedef U32 HPROVIDER; typedef void *H3DPOBJECT; typedef H3DPOBJECT H3DSAMPLE; typedef void *HSAMPLE; typedef void *HDIGDRIVER; typedef U32 HPROENUM; #define HPROENUM_FIRST 0 typedef S32 M3DRESULT; #define M3D_NOERR 0 enum { ENVIRONMENT_CAVE = 8 }; #define AIL_3D_2_SPEAKER 0 #define AIL_3D_HEADPHONE 1 #define AIL_3D_4_SPEAKER 3 #define DIG_MIXER_CHANNELS 1 #define DIG_F_MONO_16 1 #define DIG_PCM_SIGN 1 #define SMP_PLAYING 4 typedef struct _AILSOUNDINFO { S32 format; void const *data_ptr; U32 data_len; U32 rate; S32 bits; S32 channels; U32 samples; U32 block_size; void const *initial_ptr; } AILSOUNDINFO; typedef U32 (WINAPI *AIL_file_open_callback)(char const * Filename, U32 * FileHandle); typedef void (WINAPI *AIL_file_close_callback)(U32 FileHandle); #define AIL_FILE_SEEK_BEGIN 0 #define AIL_FILE_SEEK_CURRENT 1 #define AIL_FILE_SEEK_END 2 typedef S32(WINAPI *AIL_file_seek_callback)(U32 FileHandle, S32 Offset, U32 Type); typedef U32(WINAPI *AIL_file_read_callback)(U32 FileHandle, void* Buffer, U32 Bytes); #ifdef RE3MSS_EXPORTS #define RE3MSS_EXPORT __declspec(dllexport) #else #define RE3MSS_EXPORT __declspec(dllimport) #endif #ifdef __cplusplus extern "C" { #endif RE3MSS_EXPORT S32 WINAPI AIL_enumerate_3D_providers(HPROENUM *next, HPROVIDER *dest, C8 **name); RE3MSS_EXPORT void WINAPI AIL_release_3D_sample_handle(H3DSAMPLE S); RE3MSS_EXPORT void WINAPI AIL_close_3D_provider(HPROVIDER lib); RE3MSS_EXPORT void WINAPI AIL_set_3D_provider_preference(HPROVIDER lib, C8 const *name, void const *val); RE3MSS_EXPORT M3DRESULT WINAPI AIL_open_3D_provider(HPROVIDER lib); RE3MSS_EXPORT C8 *WINAPI AIL_last_error(void); RE3MSS_EXPORT S32 WINAPI AIL_3D_room_type(HPROVIDER lib); RE3MSS_EXPORT void WINAPI AIL_set_3D_room_type(HPROVIDER lib, S32 room_type); RE3MSS_EXPORT void WINAPI AIL_3D_provider_attribute(HPROVIDER lib, C8 const *name, void *val); RE3MSS_EXPORT H3DSAMPLE WINAPI AIL_allocate_3D_sample_handle(HPROVIDER lib); RE3MSS_EXPORT void WINAPI AIL_set_3D_sample_effects_level(H3DSAMPLE S, F32 effects_level); RE3MSS_EXPORT void WINAPI AIL_set_3D_speaker_type(HPROVIDER lib, S32 speaker_type); RE3MSS_EXPORT HSTREAM WINAPI AIL_open_stream(HDIGDRIVER dig, C8 const *filename, S32 stream_mem); RE3MSS_EXPORT void WINAPI AIL_stream_ms_position(HSTREAM S, S32 *total_milliseconds, S32 *current_milliseconds); RE3MSS_EXPORT void WINAPI AIL_close_stream(HSTREAM stream); RE3MSS_EXPORT S32 WINAPI AIL_digital_handle_release(HDIGDRIVER drvr); RE3MSS_EXPORT S32 WINAPI AIL_digital_handle_reacquire(HDIGDRIVER drvr); RE3MSS_EXPORT C8 *WINAPI AIL_set_redist_directory(C8 const *dir); RE3MSS_EXPORT S32 WINAPI AIL_startup(void); RE3MSS_EXPORT S32 WINAPI AIL_set_preference(U32 number, S32 value); RE3MSS_EXPORT HDIGDRIVER WINAPI AIL_open_digital_driver(U32 frequency, S32 bits, S32 channel, U32 flags); RE3MSS_EXPORT void *WINAPI AIL_mem_alloc_lock(U32 size); RE3MSS_EXPORT HSAMPLE WINAPI AIL_allocate_sample_handle(HDIGDRIVER dig); RE3MSS_EXPORT void WINAPI AIL_init_sample(HSAMPLE S); RE3MSS_EXPORT void WINAPI AIL_set_sample_type(HSAMPLE S, S32 format, U32 flags); RE3MSS_EXPORT void WINAPI AIL_pause_stream(HSTREAM stream, S32 onoff); RE3MSS_EXPORT void WINAPI AIL_release_sample_handle(HSAMPLE S); RE3MSS_EXPORT void WINAPI AIL_mem_free_lock(void *ptr); RE3MSS_EXPORT void WINAPI AIL_close_digital_driver(HDIGDRIVER dig); RE3MSS_EXPORT void WINAPI AIL_shutdown(void); RE3MSS_EXPORT void WINAPI AIL_set_3D_sample_volume(H3DSAMPLE S, S32 volume); RE3MSS_EXPORT void WINAPI AIL_set_sample_volume(HSAMPLE S, S32 volume); RE3MSS_EXPORT void WINAPI AIL_set_sample_address(HSAMPLE S, void const *start, U32 len); RE3MSS_EXPORT S32 WINAPI AIL_set_3D_sample_info(H3DSAMPLE S, AILSOUNDINFO const *info); RE3MSS_EXPORT void WINAPI AIL_set_3D_position(H3DPOBJECT obj, F32 X, F32 Y, F32 Z); RE3MSS_EXPORT void WINAPI AIL_set_3D_sample_distances(H3DSAMPLE S, F32 max_dist, F32 min_dist); RE3MSS_EXPORT void WINAPI AIL_set_sample_pan(HSAMPLE S, S32 pan); RE3MSS_EXPORT void WINAPI AIL_set_sample_playback_rate(HSAMPLE S, S32 playback_rate); RE3MSS_EXPORT void WINAPI AIL_set_3D_sample_playback_rate(H3DSAMPLE S, S32 playback_rate); RE3MSS_EXPORT void WINAPI AIL_set_sample_loop_block(HSAMPLE S, S32 loop_start_offset, S32 loop_end_offset); RE3MSS_EXPORT void WINAPI AIL_set_3D_sample_loop_block(H3DSAMPLE S, S32 loop_start_offset, S32 loop_end_offset); RE3MSS_EXPORT void WINAPI AIL_set_sample_loop_count(HSAMPLE S, S32 loop_count); RE3MSS_EXPORT void WINAPI AIL_set_3D_sample_loop_count(H3DSAMPLE S, S32 loops); RE3MSS_EXPORT U32 WINAPI AIL_sample_status(HSAMPLE S); RE3MSS_EXPORT U32 WINAPI AIL_3D_sample_status(H3DSAMPLE S); RE3MSS_EXPORT void WINAPI AIL_start_sample(HSAMPLE S); RE3MSS_EXPORT void WINAPI AIL_start_3D_sample(H3DSAMPLE S); RE3MSS_EXPORT void WINAPI AIL_end_sample(HSAMPLE S); RE3MSS_EXPORT void WINAPI AIL_end_3D_sample(H3DSAMPLE S); RE3MSS_EXPORT void WINAPI AIL_set_stream_loop_count(HSTREAM stream, S32 count); RE3MSS_EXPORT S32 WINAPI AIL_service_stream(HSTREAM stream, S32 fillup); RE3MSS_EXPORT void WINAPI AIL_start_stream(HSTREAM stream); RE3MSS_EXPORT void WINAPI AIL_set_stream_ms_position(HSTREAM S, S32 milliseconds); RE3MSS_EXPORT void WINAPI AIL_set_stream_volume(HSTREAM stream, S32 volume); RE3MSS_EXPORT void WINAPI AIL_set_stream_pan(HSTREAM stream, S32 pan); RE3MSS_EXPORT S32 WINAPI AIL_stream_status(HSTREAM stream); RE3MSS_EXPORT void WINAPI AIL_set_file_callbacks(AIL_file_open_callback opencb, AIL_file_close_callback closecb, AIL_file_seek_callback seekcb, AIL_file_read_callback readcb); #ifdef __cplusplus } #endif ================================================ FILE: vendor/mpg123/include/fmt123.h ================================================ /* libmpg123: MPEG Audio Decoder library separate header just for audio format definitions not tied to library code copyright 1995-2020 by the mpg123 project free software under the terms of the LGPL 2.1 see COPYING and AUTHORS files in distribution or http://mpg123.org */ #ifndef MPG123_ENC_H #define MPG123_ENC_H /** \file fmt123.h Audio format definitions. */ /** \defgroup mpg123_enc mpg123 PCM sample encodings * These are definitions for audio formats used by libmpg123 and * libout123. * * @{ */ /** An enum over all sample types possibly known to mpg123. * The values are designed as bit flags to allow bitmasking for encoding * families. * This is also why the enum is not used as type for actual encoding variables, * plain integers (at least 16 bit, 15 bit being used) cover the possible * combinations of these flags. * * Note that (your build of) libmpg123 does not necessarily support all these. * Usually, you can expect the 8bit encodings and signed 16 bit. * Also 32bit float will be usual beginning with mpg123-1.7.0 . * What you should bear in mind is that (SSE, etc) optimized routines may be * absent for some formats. We do have SSE for 16, 32 bit and float, though. * 24 bit integer is done via postprocessing of 32 bit output -- just cutting * the last byte, no rounding, even. If you want better, do it yourself. * * All formats are in native byte order. If you need different endinaness, you * can simply postprocess the output buffers (libmpg123 wouldn't do anything * else). The macro MPG123_SAMPLESIZE() can be helpful there. */ enum mpg123_enc_enum { /* 0000 0000 0000 1111 Some 8 bit integer encoding. */ MPG123_ENC_8 = 0x00f /* 0000 0000 0100 0000 Some 16 bit integer encoding. */ , MPG123_ENC_16 = 0x040 /* 0100 0000 0000 0000 Some 24 bit integer encoding. */ , MPG123_ENC_24 = 0x4000 /* 0000 0001 0000 0000 Some 32 bit integer encoding. */ , MPG123_ENC_32 = 0x100 /* 0000 0000 1000 0000 Some signed integer encoding. */ , MPG123_ENC_SIGNED = 0x080 /* 0000 1110 0000 0000 Some float encoding. */ , MPG123_ENC_FLOAT = 0xe00 /* 0000 0000 1101 0000 signed 16 bit */ , MPG123_ENC_SIGNED_16 = (MPG123_ENC_16|MPG123_ENC_SIGNED|0x10) /* 0000 0000 0110 0000 unsigned 16 bit */ , MPG123_ENC_UNSIGNED_16 = (MPG123_ENC_16|0x20) /* 0000 0000 0000 0001 unsigned 8 bit */ , MPG123_ENC_UNSIGNED_8 = 0x01 /* 0000 0000 1000 0010 signed 8 bit */ , MPG123_ENC_SIGNED_8 = (MPG123_ENC_SIGNED|0x02) /* 0000 0000 0000 0100 ulaw 8 bit */ , MPG123_ENC_ULAW_8 = 0x04 /* 0000 0000 0000 1000 alaw 8 bit */ , MPG123_ENC_ALAW_8 = 0x08 /* 0001 0001 1000 0000 signed 32 bit */ , MPG123_ENC_SIGNED_32 = MPG123_ENC_32|MPG123_ENC_SIGNED|0x1000 /* 0010 0001 0000 0000 unsigned 32 bit */ , MPG123_ENC_UNSIGNED_32 = MPG123_ENC_32|0x2000 /* 0101 0000 1000 0000 signed 24 bit */ , MPG123_ENC_SIGNED_24 = MPG123_ENC_24|MPG123_ENC_SIGNED|0x1000 /* 0110 0000 0000 0000 unsigned 24 bit */ , MPG123_ENC_UNSIGNED_24 = MPG123_ENC_24|0x2000 /* 0000 0010 0000 0000 32bit float */ , MPG123_ENC_FLOAT_32 = 0x200 /* 0000 0100 0000 0000 64bit float */ , MPG123_ENC_FLOAT_64 = 0x400 /* Any possibly known encoding from the list above. */ , MPG123_ENC_ANY = ( MPG123_ENC_SIGNED_16 | MPG123_ENC_UNSIGNED_16 | MPG123_ENC_UNSIGNED_8 | MPG123_ENC_SIGNED_8 | MPG123_ENC_ULAW_8 | MPG123_ENC_ALAW_8 | MPG123_ENC_SIGNED_32 | MPG123_ENC_UNSIGNED_32 | MPG123_ENC_SIGNED_24 | MPG123_ENC_UNSIGNED_24 | MPG123_ENC_FLOAT_32 | MPG123_ENC_FLOAT_64 ) }; /** Get size of one PCM sample with given encoding. * This is included both in libmpg123 and libout123. Both offer * an API function to provide the macro results from library * compile-time, not that of you application. This most likely * does not matter as I do not expect any fresh PCM sample * encoding to appear. But who knows? Perhaps the encoding type * will be abused for funny things in future, not even plain PCM. * And, by the way: Thomas really likes the ?: operator. * \param enc the encoding (mpg123_enc_enum value) * \return size of one sample in bytes */ #define MPG123_SAMPLESIZE(enc) ( \ (enc) < 1 \ ? 0 \ : ( (enc) & MPG123_ENC_8 \ ? 1 \ : ( (enc) & MPG123_ENC_16 \ ? 2 \ : ( (enc) & MPG123_ENC_24 \ ? 3 \ : ( ( (enc) & MPG123_ENC_32 \ || (enc) == MPG123_ENC_FLOAT_32 ) \ ? 4 \ : ( (enc) == MPG123_ENC_FLOAT_64 \ ? 8 \ : 0 \ ) ) ) ) ) ) /** Representation of zero in differing encodings. * This exists to define proper silence in various encodings without * having to link to libsyn123 to do actual conversions at runtime. * You have to handle big/little endian order yourself, though. * This takes the shortcut that any signed encoding has a zero with * all-zero bits. Unsigned linear encodings just have the highest bit set * (2^(n-1) for n bits), while the nonlinear 8-bit ones are special. * \param enc the encoding (mpg123_enc_enum value) * \param siz bytes per sample (return value of MPG123_SAMPLESIZE(enc)) * \param off byte (octet) offset counted from LSB * \return unsigned byte value for the designated octet */ #define MPG123_ZEROSAMPLE(enc, siz, off) ( \ (enc) == MPG123_ENC_ULAW_8 \ ? (off == 0 ? 0xff : 0x00) \ : ( (enc) == MPG123_ENC_ALAW_8 \ ? (off == 0 ? 0xd5 : 0x00) \ : ( (((enc) & (MPG123_ENC_SIGNED|MPG123_ENC_FLOAT)) || (siz) != ((off)+1)) \ ? 0x00 \ : 0x80 \ ) ) ) /** Structure defining an audio format. * Providing the members as individual function arguments to define a certain * output format is easy enough. This struct makes is more comfortable to deal * with a list of formats. * Negative values for the members might be used to communicate use of default * values. */ struct mpg123_fmt { long rate; /**< sampling rate in Hz */ int channels; /**< channel count */ /** encoding code, can be single value or bitwise or of members of * mpg123_enc_enum */ int encoding; }; /* @} */ #endif ================================================ FILE: vendor/mpg123/include/mpg123.h ================================================ /* libmpg123: MPEG Audio Decoder library (version 1.26.3) copyright 1995-2015 by the mpg123 project free software under the terms of the LGPL 2.1 see COPYING and AUTHORS files in distribution or http://mpg123.org */ #ifndef MPG123_LIB_H #define MPG123_LIB_H #include /** \file mpg123.h The header file for the libmpg123 MPEG Audio decoder */ /** A macro to check at compile time which set of API functions to expect. * This should be incremented at least each time a new symbol is added * to the header. */ #define MPG123_API_VERSION 45 #ifndef MPG123_EXPORT /** Defines needed for MS Visual Studio(tm) DLL builds. * Every public function must be prefixed with MPG123_EXPORT. When building * the DLL ensure to define BUILD_MPG123_DLL. This makes the function accessible * for clients and includes it in the import library which is created together * with the DLL. When consuming the DLL ensure to define LINK_MPG123_DLL which * imports the functions from the DLL. */ #ifdef BUILD_MPG123_DLL /* The dll exports. */ #define MPG123_EXPORT __declspec(dllexport) #else #ifdef LINK_MPG123_DLL /* The exe imports. */ #define MPG123_EXPORT __declspec(dllimport) #else /* Nothing on normal/UNIX builds */ #define MPG123_EXPORT #endif #endif #endif /* This is for Visual Studio, so this header works as distributed in the binary downloads */ #if defined(_MSC_VER) && !defined(MPG123_DEF_SSIZE_T) #define MPG123_DEF_SSIZE_T #include typedef ptrdiff_t ssize_t; #endif #ifndef MPG123_NO_CONFIGURE /* Enable use of this file without configure. */ #include #include /* Simplified large file handling. I used to have a check here that prevents building for a library with conflicting large file setup (application that uses 32 bit offsets with library that uses 64 bits). While that was perfectly fine in an environment where there is one incarnation of the library, it hurt GNU/Linux and Solaris systems with multilib where the distribution fails to provide the correct header matching the 32 bit library (where large files need explicit support) or the 64 bit library (where there is no distinction). New approach: When the app defines _FILE_OFFSET_BITS, it wants non-default large file support, and thus functions with added suffix (mpg123_open_64). Any mismatch will be caught at link time because of the _FILE_OFFSET_BITS setting used when building libmpg123. Plus, there's dual mode large file support in mpg123 since 1.12 now. Link failure is not the expected outcome of any half-sane usage anymore. More complication: What about client code defining _LARGEFILE64_SOURCE? It might want direct access to the _64 functions, along with the ones without suffix. Well, that's possible now via defining MPG123_NO_LARGENAME and MPG123_LARGESUFFIX, respectively, for disabling or enforcing the suffix names. */ /* Now, the renaming of large file aware functions. By default, it appends underscore _FILE_OFFSET_BITS (so, mpg123_seek_64 for mpg123_seek), if _FILE_OFFSET_BITS is defined. You can force a different suffix via MPG123_LARGESUFFIX (that must include the underscore), or you can just disable the whole mess by defining MPG123_NO_LARGENAME. */ #if (!defined MPG123_NO_LARGENAME) && ((defined _FILE_OFFSET_BITS) || (defined MPG123_LARGESUFFIX)) /* Need some trickery to concatenate the value(s) of the given macro(s). */ #define MPG123_MACROCAT_REALLY(a, b) a ## b #define MPG123_MACROCAT(a, b) MPG123_MACROCAT_REALLY(a, b) #ifndef MPG123_LARGESUFFIX #define MPG123_LARGESUFFIX MPG123_MACROCAT(_, _FILE_OFFSET_BITS) #endif #define MPG123_LARGENAME(func) MPG123_MACROCAT(func, MPG123_LARGESUFFIX) #define mpg123_open_fixed MPG123_LARGENAME(mpg123_open_fixed) #define mpg123_open MPG123_LARGENAME(mpg123_open) #define mpg123_open_fd MPG123_LARGENAME(mpg123_open_fd) #define mpg123_open_handle MPG123_LARGENAME(mpg123_open_handle) #define mpg123_framebyframe_decode MPG123_LARGENAME(mpg123_framebyframe_decode) #define mpg123_decode_frame MPG123_LARGENAME(mpg123_decode_frame) #define mpg123_tell MPG123_LARGENAME(mpg123_tell) #define mpg123_tellframe MPG123_LARGENAME(mpg123_tellframe) #define mpg123_tell_stream MPG123_LARGENAME(mpg123_tell_stream) #define mpg123_seek MPG123_LARGENAME(mpg123_seek) #define mpg123_feedseek MPG123_LARGENAME(mpg123_feedseek) #define mpg123_seek_frame MPG123_LARGENAME(mpg123_seek_frame) #define mpg123_timeframe MPG123_LARGENAME(mpg123_timeframe) #define mpg123_index MPG123_LARGENAME(mpg123_index) #define mpg123_set_index MPG123_LARGENAME(mpg123_set_index) #define mpg123_position MPG123_LARGENAME(mpg123_position) #define mpg123_length MPG123_LARGENAME(mpg123_length) #define mpg123_framelength MPG123_LARGENAME(mpg123_framelength) #define mpg123_set_filesize MPG123_LARGENAME(mpg123_set_filesize) #define mpg123_replace_reader MPG123_LARGENAME(mpg123_replace_reader) #define mpg123_replace_reader_handle MPG123_LARGENAME(mpg123_replace_reader_handle) #define mpg123_framepos MPG123_LARGENAME(mpg123_framepos) #endif /* largefile hackery */ #endif /* MPG123_NO_CONFIGURE */ #ifdef __cplusplus extern "C" { #endif /** \defgroup mpg123_init mpg123 library and handle setup * * Functions to initialise and shutdown the mpg123 library and handles. * The parameters of handles have workable defaults, you only have to tune them when you want to tune something;-) * Tip: Use a RVA setting... * * @{ */ /** Opaque structure for the libmpg123 decoder handle. */ struct mpg123_handle_struct; /** Opaque structure for the libmpg123 decoder handle. * Most functions take a pointer to a mpg123_handle as first argument and operate on its data in an object-oriented manner. */ typedef struct mpg123_handle_struct mpg123_handle; /** Function to initialise the mpg123 library. * This should be called once in a non-parallel context. It is not explicitly * thread-safe, but repeated/concurrent calls still _should_ be safe as static * tables are filled with the same values anyway. * * \return MPG123_OK if successful, otherwise an error number. */ MPG123_EXPORT int mpg123_init(void); /** Superfluous Function to close down the mpg123 library. * This was created with the thought that there sometime will be cleanup code * to be run after library use. This never materialized. You can forget about * this function and it is only here for old programs that do call it. */ MPG123_EXPORT void mpg123_exit(void); /** Create a handle with optional choice of decoder (named by a string, see mpg123_decoders() or mpg123_supported_decoders()). * and optional retrieval of an error code to feed to mpg123_plain_strerror(). * Optional means: Any of or both the parameters may be NULL. * * \param decoder optional choice of decoder variant (NULL for default) * \param error optional address to store error codes * \return Non-NULL pointer to fresh handle when successful. */ MPG123_EXPORT mpg123_handle *mpg123_new(const char* decoder, int *error); /** Delete handle, mh is either a valid mpg123 handle or NULL. * \param mh handle */ MPG123_EXPORT void mpg123_delete(mpg123_handle *mh); /** Free plain memory allocated within libmpg123. * This is for library users that are not sure to use the same underlying * memory allocator as libmpg123. It is just a wrapper over free() in * the underlying C library. */ MPG123_EXPORT void mpg123_free(void *ptr); /** Enumeration of the parameters types that it is possible to set/get. */ enum mpg123_parms { MPG123_VERBOSE = 0, /**< set verbosity value for enabling messages to stderr, >= 0 makes sense (integer) */ MPG123_FLAGS, /**< set all flags, p.ex val = MPG123_GAPLESS|MPG123_MONO_MIX (integer) */ MPG123_ADD_FLAGS, /**< add some flags (integer) */ MPG123_FORCE_RATE, /**< when value > 0, force output rate to that value (integer) */ MPG123_DOWN_SAMPLE, /**< 0=native rate, 1=half rate, 2=quarter rate (integer) */ MPG123_RVA, /**< one of the RVA choices above (integer) */ MPG123_DOWNSPEED, /**< play a frame N times (integer) */ MPG123_UPSPEED, /**< play every Nth frame (integer) */ MPG123_START_FRAME, /**< start with this frame (skip frames before that, integer) */ MPG123_DECODE_FRAMES, /**< decode only this number of frames (integer) */ MPG123_ICY_INTERVAL, /**< Stream contains ICY metadata with this interval (integer). Make sure to set this _before_ opening a stream.*/ MPG123_OUTSCALE, /**< the scale for output samples (amplitude - integer or float according to mpg123 output format, normally integer) */ MPG123_TIMEOUT, /**< timeout for reading from a stream (not supported on win32, integer) */ MPG123_REMOVE_FLAGS, /**< remove some flags (inverse of MPG123_ADD_FLAGS, integer) */ MPG123_RESYNC_LIMIT, /**< Try resync on frame parsing for that many bytes or until end of stream (<0 ... integer). This can enlarge the limit for skipping junk on beginning, too (but not reduce it). */ MPG123_INDEX_SIZE /**< Set the frame index size (if supported). Values <0 mean that the index is allowed to grow dynamically in these steps (in positive direction, of course) -- Use this when you really want a full index with every individual frame. */ ,MPG123_PREFRAMES /**< Decode/ignore that many frames in advance for layer 3. This is needed to fill bit reservoir after seeking, for example (but also at least one frame in advance is needed to have all "normal" data for layer 3). Give a positive integer value, please.*/ ,MPG123_FEEDPOOL /**< For feeder mode, keep that many buffers in a pool to avoid frequent malloc/free. The pool is allocated on mpg123_open_feed(). If you change this parameter afterwards, you can trigger growth and shrinkage during decoding. The default value could change any time. If you care about this, then set it. (integer) */ ,MPG123_FEEDBUFFER /**< Minimal size of one internal feeder buffer, again, the default value is subject to change. (integer) */ ,MPG123_FREEFORMAT_SIZE /**< Tell the parser a free-format frame size to * avoid read-ahead to get it. A value of -1 (default) means that the parser * will determine it. The parameter value is applied during decoder setup * for a freshly opened stream only. */ }; /** Flag bits for MPG123_FLAGS, use the usual binary or to combine. */ enum mpg123_param_flags { MPG123_FORCE_MONO = 0x7 /**< 0111 Force some mono mode: This is a test bitmask for seeing if any mono forcing is active. */ ,MPG123_MONO_LEFT = 0x1 /**< 0001 Force playback of left channel only. */ ,MPG123_MONO_RIGHT = 0x2 /**< 0010 Force playback of right channel only. */ ,MPG123_MONO_MIX = 0x4 /**< 0100 Force playback of mixed mono. */ ,MPG123_FORCE_STEREO = 0x8 /**< 1000 Force stereo output. */ ,MPG123_FORCE_8BIT = 0x10 /**< 00010000 Force 8bit formats. */ ,MPG123_QUIET = 0x20 /**< 00100000 Suppress any printouts (overrules verbose). */ ,MPG123_GAPLESS = 0x40 /**< 01000000 Enable gapless decoding (default on if libmpg123 has support). */ ,MPG123_NO_RESYNC = 0x80 /**< 10000000 Disable resync stream after error. */ ,MPG123_SEEKBUFFER = 0x100 /**< 000100000000 Enable small buffer on non-seekable streams to allow some peek-ahead (for better MPEG sync). */ ,MPG123_FUZZY = 0x200 /**< 001000000000 Enable fuzzy seeks (guessing byte offsets or using approximate seek points from Xing TOC) */ ,MPG123_FORCE_FLOAT = 0x400 /**< 010000000000 Force floating point output (32 or 64 bits depends on mpg123 internal precision). */ ,MPG123_PLAIN_ID3TEXT = 0x800 /**< 100000000000 Do not translate ID3 text data to UTF-8. ID3 strings will contain the raw text data, with the first byte containing the ID3 encoding code. */ ,MPG123_IGNORE_STREAMLENGTH = 0x1000 /**< 1000000000000 Ignore any stream length information contained in the stream, which can be contained in a 'TLEN' frame of an ID3v2 tag or a Xing tag */ ,MPG123_SKIP_ID3V2 = 0x2000 /**< 10 0000 0000 0000 Do not parse ID3v2 tags, just skip them. */ ,MPG123_IGNORE_INFOFRAME = 0x4000 /**< 100 0000 0000 0000 Do not parse the LAME/Xing info frame, treat it as normal MPEG data. */ ,MPG123_AUTO_RESAMPLE = 0x8000 /**< 1000 0000 0000 0000 Allow automatic internal resampling of any kind (default on if supported). Especially when going lowlevel with replacing output buffer, you might want to unset this flag. Setting MPG123_DOWNSAMPLE or MPG123_FORCE_RATE will override this. */ ,MPG123_PICTURE = 0x10000 /**< 17th bit: Enable storage of pictures from tags (ID3v2 APIC). */ ,MPG123_NO_PEEK_END = 0x20000 /**< 18th bit: Do not seek to the end of * the stream in order to probe * the stream length and search for the id3v1 field. This also means * the file size is unknown unless set using mpg123_set_filesize() and * the stream is assumed as non-seekable unless overridden. */ ,MPG123_FORCE_SEEKABLE = 0x40000 /**< 19th bit: Force the stream to be seekable. */ ,MPG123_STORE_RAW_ID3 = 0x80000 /**< store raw ID3 data (even if skipping) */ ,MPG123_FORCE_ENDIAN = 0x100000 /**< Enforce endianess of output samples. * This is not reflected in the format codes. If this flag is set along with * MPG123_BIG_ENDIAN, MPG123_ENC_SIGNED16 means s16be, without * MPG123_BIG_ENDIAN, it means s16le. Normal operation without * MPG123_FORCE_ENDIAN produces output in native byte order. */ ,MPG123_BIG_ENDIAN = 0x200000 /**< Choose big endian instead of little. */ ,MPG123_NO_READAHEAD = 0x400000 /**< Disable read-ahead in parser. If * you know you provide full frames to the feeder API, this enables * decoder output from the first one on, instead of having to wait for * the next frame to confirm that the stream is healthy. It also disables * free format support unless you provide a frame size using * MPG123_FREEFORMAT_SIZE. */ ,MPG123_FLOAT_FALLBACK = 0x800000 /**< Consider floating point output encoding only after * trying other (possibly downsampled) rates and encodings first. This is to * support efficient playback where floating point output is only configured for * an external resampler, bypassing that resampler when the desired rate can * be produced directly. This is enabled by default to be closer to older versions * of libmpg123 which did not enable float automatically at all. If disabled, * float is considered after the 16 bit default and higher-bit integer encodings * for any rate. */ ,MPG123_NO_FRANKENSTEIN = 0x1000000 /**< Disable support for Frankenstein streams * (different MPEG streams stiched together). Do not accept serious change of MPEG * header inside a single stream. With this flag, the audio output format cannot * change during decoding unless you open a new stream. This also stops decoding * after an announced end of stream (Info header contained a number of frames * and this number has been reached). This makes your MP3 files behave more like * ordinary media files with defined structure, rather than stream dumps with * some sugar. */ }; /** choices for MPG123_RVA */ enum mpg123_param_rva { MPG123_RVA_OFF = 0 /**< RVA disabled (default). */ ,MPG123_RVA_MIX = 1 /**< Use mix/track/radio gain. */ ,MPG123_RVA_ALBUM = 2 /**< Use album/audiophile gain */ ,MPG123_RVA_MAX = MPG123_RVA_ALBUM /**< The maximum RVA code, may increase in future. */ }; /** Set a specific parameter, for a specific mpg123_handle, using a parameter * type key chosen from the mpg123_parms enumeration, to the specified value. * \param mh handle * \param type parameter choice * \param value integer value * \param fvalue floating point value * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_param( mpg123_handle *mh , enum mpg123_parms type, long value, double fvalue ); /** Get a specific parameter, for a specific mpg123_handle. * See the mpg123_parms enumeration for a list of available parameters. * \param mh handle * \param type parameter choice * \param value integer value return address * \param fvalue floating point value return address * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_getparam( mpg123_handle *mh , enum mpg123_parms type, long *value, double *fvalue ); /** Feature set available for query with mpg123_feature. */ enum mpg123_feature_set { MPG123_FEATURE_ABI_UTF8OPEN = 0 /**< mpg123 expects path names to be given in UTF-8 encoding instead of plain native. */ ,MPG123_FEATURE_OUTPUT_8BIT /**< 8bit output */ ,MPG123_FEATURE_OUTPUT_16BIT /**< 16bit output */ ,MPG123_FEATURE_OUTPUT_32BIT /**< 32bit output */ ,MPG123_FEATURE_INDEX /**< support for building a frame index for accurate seeking */ ,MPG123_FEATURE_PARSE_ID3V2 /**< id3v2 parsing */ ,MPG123_FEATURE_DECODE_LAYER1 /**< mpeg layer-1 decoder enabled */ ,MPG123_FEATURE_DECODE_LAYER2 /**< mpeg layer-2 decoder enabled */ ,MPG123_FEATURE_DECODE_LAYER3 /**< mpeg layer-3 decoder enabled */ ,MPG123_FEATURE_DECODE_ACCURATE /**< accurate decoder rounding */ ,MPG123_FEATURE_DECODE_DOWNSAMPLE /**< downsample (sample omit) */ ,MPG123_FEATURE_DECODE_NTOM /**< flexible rate decoding */ ,MPG123_FEATURE_PARSE_ICY /**< ICY support */ ,MPG123_FEATURE_TIMEOUT_READ /**< Reader with timeout (network). */ ,MPG123_FEATURE_EQUALIZER /**< tunable equalizer */ ,MPG123_FEATURE_MOREINFO /**< more info extraction (for frame analyzer) */ ,MPG123_FEATURE_OUTPUT_FLOAT32 /**< 32 bit float output */ ,MPG123_FEATURE_OUTPUT_FLOAT64 /**< 64 bit float output (usually never) */ }; /** Query libmpg123 features. * \param key feature selection * \return 1 for success, 0 for unimplemented functions */ MPG123_EXPORT int mpg123_feature(const enum mpg123_feature_set key); /** Query libmpg123 features with better ABI compatibility * * This is the same as mpg123_feature(), but this time not using * the enum as argument. Compilers don't have to agree on the size of * enums and hence they are not safe in public API. * * \param key feature selection * \return 1 for success, 0 for unimplemented functions */ MPG123_EXPORT int mpg123_feature2(int key); /* @} */ /** \defgroup mpg123_error mpg123 error handling * * Functions to get text version of the error numbers and an enumeration * of the error codes returned by libmpg123. * * Most functions operating on a mpg123_handle simply return MPG123_OK (0) * on success and MPG123_ERR (-1) on failure, setting the internal error * variable of the handle to the specific error code. If there was not a valid * (non-NULL) handle provided to a function operating on one, MPG123_BAD_HANDLE * may be returned if this can not be confused with a valid positive return * value. * Meaning: A function expected to return positive integers on success will * always indicate error or a special condition by returning a negative one. * * Decoding/seek functions may also return message codes MPG123_DONE, * MPG123_NEW_FORMAT and MPG123_NEED_MORE (all negative, see below on how to * react). Note that calls to those can be nested, so generally watch out * for these codes after initial handle setup. * Especially any function that needs information about the current stream * to work will try to at least parse the beginning if that did not happen * yet. * * On a function that is supposed to return MPG123_OK on success and * MPG123_ERR on failure, make sure you check for != MPG123_OK, not * == MPG123_ERR, as the error code could get more specific in future, * or there is just a special message from a decoding routine as indicated * above. * * @{ */ /** Enumeration of the message and error codes and returned by libmpg123 functions. */ enum mpg123_errors { MPG123_DONE=-12, /**< Message: Track ended. Stop decoding. */ MPG123_NEW_FORMAT=-11, /**< Message: Output format will be different on next call. Note that some libmpg123 versions between 1.4.3 and 1.8.0 insist on you calling mpg123_getformat() after getting this message code. Newer verisons behave like advertised: You have the chance to call mpg123_getformat(), but you can also just continue decoding and get your data. */ MPG123_NEED_MORE=-10, /**< Message: For feed reader: "Feed me more!" (call mpg123_feed() or mpg123_decode() with some new input data). */ MPG123_ERR=-1, /**< Generic Error */ MPG123_OK=0, /**< Success */ MPG123_BAD_OUTFORMAT, /**< Unable to set up output format! */ MPG123_BAD_CHANNEL, /**< Invalid channel number specified. */ MPG123_BAD_RATE, /**< Invalid sample rate specified. */ MPG123_ERR_16TO8TABLE, /**< Unable to allocate memory for 16 to 8 converter table! */ MPG123_BAD_PARAM, /**< Bad parameter id! */ MPG123_BAD_BUFFER, /**< Bad buffer given -- invalid pointer or too small size. */ MPG123_OUT_OF_MEM, /**< Out of memory -- some malloc() failed. */ MPG123_NOT_INITIALIZED, /**< You didn't initialize the library! */ MPG123_BAD_DECODER, /**< Invalid decoder choice. */ MPG123_BAD_HANDLE, /**< Invalid mpg123 handle. */ MPG123_NO_BUFFERS, /**< Unable to initialize frame buffers (out of memory?). */ MPG123_BAD_RVA, /**< Invalid RVA mode. */ MPG123_NO_GAPLESS, /**< This build doesn't support gapless decoding. */ MPG123_NO_SPACE, /**< Not enough buffer space. */ MPG123_BAD_TYPES, /**< Incompatible numeric data types. */ MPG123_BAD_BAND, /**< Bad equalizer band. */ MPG123_ERR_NULL, /**< Null pointer given where valid storage address needed. */ MPG123_ERR_READER, /**< Error reading the stream. */ MPG123_NO_SEEK_FROM_END,/**< Cannot seek from end (end is not known). */ MPG123_BAD_WHENCE, /**< Invalid 'whence' for seek function.*/ MPG123_NO_TIMEOUT, /**< Build does not support stream timeouts. */ MPG123_BAD_FILE, /**< File access error. */ MPG123_NO_SEEK, /**< Seek not supported by stream. */ MPG123_NO_READER, /**< No stream opened. */ MPG123_BAD_PARS, /**< Bad parameter handle. */ MPG123_BAD_INDEX_PAR, /**< Bad parameters to mpg123_index() and mpg123_set_index() */ MPG123_OUT_OF_SYNC, /**< Lost track in bytestream and did not try to resync. */ MPG123_RESYNC_FAIL, /**< Resync failed to find valid MPEG data. */ MPG123_NO_8BIT, /**< No 8bit encoding possible. */ MPG123_BAD_ALIGN, /**< Stack aligmnent error */ MPG123_NULL_BUFFER, /**< NULL input buffer with non-zero size... */ MPG123_NO_RELSEEK, /**< Relative seek not possible (screwed up file offset) */ MPG123_NULL_POINTER, /**< You gave a null pointer somewhere where you shouldn't have. */ MPG123_BAD_KEY, /**< Bad key value given. */ MPG123_NO_INDEX, /**< No frame index in this build. */ MPG123_INDEX_FAIL, /**< Something with frame index went wrong. */ MPG123_BAD_DECODER_SETUP, /**< Something prevents a proper decoder setup */ MPG123_MISSING_FEATURE /**< This feature has not been built into libmpg123. */ ,MPG123_BAD_VALUE /**< A bad value has been given, somewhere. */ ,MPG123_LSEEK_FAILED /**< Low-level seek failed. */ ,MPG123_BAD_CUSTOM_IO /**< Custom I/O not prepared. */ ,MPG123_LFS_OVERFLOW /**< Offset value overflow during translation of large file API calls -- your client program cannot handle that large file. */ ,MPG123_INT_OVERFLOW /**< Some integer overflow. */ }; /** Look up error strings given integer code. * \param errcode integer error code * \return string describing what that error error code means */ MPG123_EXPORT const char* mpg123_plain_strerror(int errcode); /** Give string describing what error has occured in the context of handle mh. * When a function operating on an mpg123 handle returns MPG123_ERR, you should check for the actual reason via * char *errmsg = mpg123_strerror(mh) * This function will catch mh == NULL and return the message for MPG123_BAD_HANDLE. * \param mh handle * \return error message */ MPG123_EXPORT const char* mpg123_strerror(mpg123_handle *mh); /** Return the plain errcode intead of a string. * \param mh handle * \return error code recorded in handle or MPG123_BAD_HANDLE */ MPG123_EXPORT int mpg123_errcode(mpg123_handle *mh); /*@}*/ /** \defgroup mpg123_decoder mpg123 decoder selection * * Functions to list and select the available decoders. * Perhaps the most prominent feature of mpg123: You have several (optimized) decoders to choose from (on x86 and PPC (MacOS) systems, that is). * * @{ */ /** Get available decoder list. * \return NULL-terminated array of generally available decoder names (plain 8bit ASCII) */ MPG123_EXPORT const char **mpg123_decoders(void); /** Get supported decoder list. * \return NULL-terminated array of the decoders supported by the CPU (plain 8bit ASCII) */ MPG123_EXPORT const char **mpg123_supported_decoders(void); /** Set the active decoder. * \param mh handle * \param decoder_name name of decoder * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_decoder(mpg123_handle *mh, const char* decoder_name); /** Get the currently active decoder name. * The active decoder engine can vary depening on output constraints, * mostly non-resampling, integer output is accelerated via 3DNow & Co. but for * other modes a fallback engine kicks in. * Note that this can return a decoder that is only active in the hidden and not * available as decoder choice from the outside. * \param mh handle * \return The decoder name or NULL on error. */ MPG123_EXPORT const char* mpg123_current_decoder(mpg123_handle *mh); /*@}*/ /** \defgroup mpg123_output mpg123 output audio format * * Functions to get and select the format of the decoded audio. * * Before you dive in, please be warned that you might get confused by this. * This seems to happen a lot, therefore I am trying to explain in advance. * If you do feel confused and just want to decode your normal MPEG audio files that * don't alter properties in the middle, just use mpg123_open_fixed() with a fixed encoding * and channel count and forget about a matrix of audio formats. If you want to get funky, * read ahead ... * * The mpg123 library decides what output format to use when encountering the first frame in a stream, or actually any frame that is still valid but differs from the frames before in the prompted output format. At such a deciding point, an internal table of allowed encodings, sampling rates and channel setups is consulted. According to this table, an output format is chosen and the decoding engine set up accordingly (including optimized routines for different output formats). This might seem unusual but it just follows from the non-existence of "MPEG audio files" with defined overall properties. There are streams, streams are concatenations of (semi) independent frames. We store streams on disk and call them "MPEG audio files", but that does not change their nature as the decoder is concerned (the LAME/Xing header for gapless decoding makes things interesting again). * * To get to the point: What you do with mpg123_format() and friends is to fill the internal table of allowed formats before it is used. That includes removing support for some formats or adding your forced sample rate (see MPG123_FORCE_RATE) that will be used with the crude internal resampler. Also keep in mind that the sample encoding is just a question of choice -- the MPEG frames do only indicate their native sampling rate and channel count. If you want to decode to integer or float samples, 8 or 16 bit ... that is your decision. In a "clean" world, libmpg123 would always decode to 32 bit float and let you handle any sample conversion. But there are optimized routines that work faster by directly decoding to the desired encoding / accuracy. We prefer efficiency over conceptual tidyness. * * People often start out thinking that mpg123_format() should change the actual decoding format on the fly. That is wrong. It only has effect on the next natural change of output format, when libmpg123 will consult its format table again. To make life easier, you might want to call mpg123_format_none() before any thing else and then just allow one desired encoding and a limited set of sample rates / channel choices that you actually intend to deal with. You can force libmpg123 to decode everything to 44100 KHz, stereo, 16 bit integer ... it will duplicate mono channels and even do resampling if needed (unless that feature is disabled in the build, same with some encodings). But I have to stress that the resampling of libmpg123 is very crude and doesn't even contain any kind of "proper" interpolation. * * In any case, watch out for MPG123_NEW_FORMAT as return message from decoding routines and call mpg123_getformat() to get the currently active output format. * * @{ */ /** They can be combined into one number (3) to indicate mono and stereo... */ enum mpg123_channelcount { MPG123_MONO = 1 /**< mono */ ,MPG123_STEREO = 2 /**< stereo */ }; /** An array of supported standard sample rates * These are possible native sample rates of MPEG audio files. * You can still force mpg123 to resample to a different one, but by * default you will only get audio in one of these samplings. * This list is in ascending order. * \param list Store a pointer to the sample rates array there. * \param number Store the number of sample rates there. */ MPG123_EXPORT void mpg123_rates(const long **list, size_t *number); /** An array of supported audio encodings. * An audio encoding is one of the fully qualified members of mpg123_enc_enum (MPG123_ENC_SIGNED_16, not MPG123_SIGNED). * \param list Store a pointer to the encodings array there. * \param number Store the number of encodings there. */ MPG123_EXPORT void mpg123_encodings(const int **list, size_t *number); /** Return the size (in bytes) of one mono sample of the named encoding. * \param encoding The encoding value to analyze. * \return positive size of encoding in bytes, 0 on invalid encoding. */ MPG123_EXPORT int mpg123_encsize(int encoding); /** Configure a mpg123 handle to accept no output format at all, * use before specifying supported formats with mpg123_format * \param mh handle * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_format_none(mpg123_handle *mh); /** Configure mpg123 handle to accept all formats * (also any custom rate you may set) -- this is default. * \param mh handle * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_format_all(mpg123_handle *mh); /** Set the audio format support of a mpg123_handle in detail: * \param mh handle * \param rate The sample rate value (in Hertz). * \param channels A combination of MPG123_STEREO and MPG123_MONO. * \param encodings A combination of accepted encodings for rate and channels, p.ex MPG123_ENC_SIGNED16 | MPG123_ENC_ULAW_8 (or 0 for no support). Please note that some encodings may not be supported in the library build and thus will be ignored here. * \return MPG123_OK on success, MPG123_ERR if there was an error. */ MPG123_EXPORT int mpg123_format( mpg123_handle *mh , long rate, int channels, int encodings ); /** Set the audio format support of a mpg123_handle in detail: * \param mh handle * \param rate The sample rate value (in Hertz). Special value 0 means * all rates (the reason for this variant of mpg123_format()). * \param channels A combination of MPG123_STEREO and MPG123_MONO. * \param encodings A combination of accepted encodings for rate and channels, * p.ex MPG123_ENC_SIGNED16 | MPG123_ENC_ULAW_8 (or 0 for no support). * Please note that some encodings may not be supported in the library build * and thus will be ignored here. * \return MPG123_OK on success, MPG123_ERR if there was an error. */ MPG123_EXPORT int mpg123_format2( mpg123_handle *mh , long rate, int channels, int encodings ); /** Check to see if a specific format at a specific rate is supported * by mpg123_handle. * \param mh handle * \param rate sampling rate * \param encoding encoding * \return 0 for no support (that includes invalid parameters), MPG123_STEREO, * MPG123_MONO or MPG123_STEREO|MPG123_MONO. */ MPG123_EXPORT int mpg123_format_support( mpg123_handle *mh , long rate, int encoding ); /** Get the current output format written to the addresses given. * If the stream is freshly loaded, this will try to parse enough * of it to give you the format to come. This clears the flag that * would otherwise make the first decoding call return * MPG123_NEW_FORMAT. * \param mh handle * \param rate sampling rate return address * \param channels channel count return address * \param encoding encoding return address * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_getformat( mpg123_handle *mh , long *rate, int *channels, int *encoding ); /** Get the current output format written to the addresses given. * This differs from plain mpg123_getformat() in that you can choose * _not_ to clear the flag that would trigger the next decoding call * to return MPG123_NEW_FORMAT in case of a new format arriving. * \param mh handle * \param rate sampling rate return address * \param channels channel count return address * \param encoding encoding return address * \param clear_flag if true, clear internal format flag * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_getformat2( mpg123_handle *mh , long *rate, int *channels, int *encoding, int clear_flag ); /*@}*/ /** \defgroup mpg123_input mpg123 file input and decoding * * Functions for input bitstream and decoding operations. * Decoding/seek functions may also return message codes MPG123_DONE, MPG123_NEW_FORMAT and MPG123_NEED_MORE (please read up on these on how to react!). * @{ */ /** Open a simple MPEG file with fixed properties. * * This function shall simplify the common use case of a plain MPEG * file on disk that you want to decode, with one fixed sample * rate and channel count, and usually a length defined by a Lame/Info/Xing * tag. It will: * * - set the MPG123_NO_FRANKENSTEIN flag * - set up format support according to given parameters, * - open the file, * - query audio format, * - fix the audio format support table to ensure the format stays the same, * - call mpg123_scan() if there is no header frame to tell the track length. * * From that on, you can call mpg123_getformat() for querying the sample * rate (and channel count in case you allowed both) and mpg123_length() * to get a pretty safe number for the duration. * Only the sample rate is left open as that indeed is a fixed property of * MPEG files. You could set MPG123_FORCE_RATE beforehand, but that may trigger * low-quality resampling in the decoder, only do so if in dire need. * The library will convert mono files to stereo for you, and vice versa. * If any constraint cannot be satisified (most likely because of a non-default * build of libmpg123), you get MPG123_ERR returned and can query the detailed * cause from the handle. Only on MPG123_OK there will an open file that you * then close using mpg123_close(), or implicitly on mpg123_delete() or the next * call to open another file. * * So, for your usual CD rip collection, you could use * * mpg123_open_fixed(mh, path, MPG123_STEREO, MPG123_ENC_SIGNED_16) * * and be happy calling mpg123_getformat() to verify 44100 Hz rate, then just * playing away with mpg123_read(). The occasional mono file, or MP2 file, * will also be decoded without you really noticing. Just the speed could be * wrong if you do not care about sample rate at all. * \param mh handle * \param path filesystem path * \param channels allowed channel count, either 1 (MPG123_MONO) or * 2 (MPG123_STEREO), or bitwise or of them, but then you're halfway back to * calling mpg123_format() again;-) * \param encoding a definite encoding from enum mpg123_enc_enum * or a bitmask like for mpg123_format(), defeating the purpose somewhat */ MPG123_EXPORT int mpg123_open_fixed(mpg123_handle *mh, const char *path , int channels, int encoding); /** Open and prepare to decode the specified file by filesystem path. * This does not open HTTP urls; libmpg123 contains no networking code. * If you want to decode internet streams, use mpg123_open_fd() or mpg123_open_feed(). * \param mh handle * \param path filesystem path * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_open(mpg123_handle *mh, const char *path); /** Use an already opened file descriptor as the bitstream input * mpg123_close() will _not_ close the file descriptor. * \param mh handle * \param fd file descriptor * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_open_fd(mpg123_handle *mh, int fd); /** Use an opaque handle as bitstream input. This works only with the * replaced I/O from mpg123_replace_reader_handle()! * mpg123_close() will call the cleanup callback for your handle (if you gave one). * \param mh handle * \param iohandle your handle * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_open_handle(mpg123_handle *mh, void *iohandle); /** Open a new bitstream and prepare for direct feeding * This works together with mpg123_decode(); you are responsible for reading and feeding the input bitstream. * Also, you are expected to handle ICY metadata extraction yourself. This * input method does not handle MPG123_ICY_INTERVAL. It does parse ID3 frames, though. * \param mh handle * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_open_feed(mpg123_handle *mh); /** Closes the source, if libmpg123 opened it. * \param mh handle * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_close(mpg123_handle *mh); /** Read from stream and decode up to outmemsize bytes. * * Note: The type of outmemory changed to a void pointer in mpg123 1.26.0 * (API version 45). * * \param mh handle * \param outmemory address of output buffer to write to * \param outmemsize maximum number of bytes to write * \param done address to store the number of actually decoded bytes to * \return MPG123_OK or error/message code */ MPG123_EXPORT int mpg123_read(mpg123_handle *mh , void *outmemory, size_t outmemsize, size_t *done ); /** Feed data for a stream that has been opened with mpg123_open_feed(). * It's give and take: You provide the bytestream, mpg123 gives you the decoded samples. * \param mh handle * \param in input buffer * \param size number of input bytes * \return MPG123_OK or error/message code. */ MPG123_EXPORT int mpg123_feed( mpg123_handle *mh , const unsigned char *in, size_t size ); /** Decode MPEG Audio from inmemory to outmemory. * This is very close to a drop-in replacement for old mpglib. * When you give zero-sized output buffer the input will be parsed until * decoded data is available. This enables you to get MPG123_NEW_FORMAT (and query it) * without taking decoded data. * Think of this function being the union of mpg123_read() and mpg123_feed() (which it actually is, sort of;-). * You can actually always decide if you want those specialized functions in separate steps or one call this one here. * * Note: The type of outmemory changed to a void pointer in mpg123 1.26.0 * (API version 45). * * \param mh handle * \param inmemory input buffer * \param inmemsize number of input bytes * \param outmemory output buffer * \param outmemsize maximum number of output bytes * \param done address to store the number of actually decoded bytes to * \return error/message code (watch out especially for MPG123_NEED_MORE) */ MPG123_EXPORT int mpg123_decode( mpg123_handle *mh , const unsigned char *inmemory, size_t inmemsize , void *outmemory, size_t outmemsize, size_t *done ); /** Decode next MPEG frame to internal buffer * or read a frame and return after setting a new format. * \param mh handle * \param num current frame offset gets stored there * \param audio This pointer is set to the internal buffer to read the decoded audio from. * \param bytes number of output bytes ready in the buffer * \return MPG123_OK or error/message code */ MPG123_EXPORT int mpg123_decode_frame( mpg123_handle *mh , off_t *num, unsigned char **audio, size_t *bytes ); /** Decode current MPEG frame to internal buffer. * Warning: This is experimental API that might change in future releases! * Please watch mpg123 development closely when using it. * \param mh handle * \param num last frame offset gets stored there * \param audio this pointer is set to the internal buffer to read the decoded audio from. * \param bytes number of output bytes ready in the buffer * \return MPG123_OK or error/message code */ MPG123_EXPORT int mpg123_framebyframe_decode( mpg123_handle *mh , off_t *num, unsigned char **audio, size_t *bytes ); /** Find, read and parse the next mp3 frame * Warning: This is experimental API that might change in future releases! * Please watch mpg123 development closely when using it. * \param mh handle * \return MPG123_OK or error/message code */ MPG123_EXPORT int mpg123_framebyframe_next(mpg123_handle *mh); /** Get access to the raw input data for the last parsed frame. * This gives you a direct look (and write access) to the frame body data. * Together with the raw header, you can reconstruct the whole raw MPEG stream without junk and meta data, or play games by actually modifying the frame body data before decoding this frame (mpg123_framebyframe_decode()). * A more sane use would be to use this for CRC checking (see mpg123_info() and MPG123_CRC), the first two bytes of the body make up the CRC16 checksum, if present. * You can provide NULL for a parameter pointer when you are not interested in the value. * * \param mh handle * \param header the 4-byte MPEG header * \param bodydata pointer to the frame body stored in the handle (without the header) * \param bodybytes size of frame body in bytes (without the header) * \return MPG123_OK if there was a yet un-decoded frame to get the * data from, MPG123_BAD_HANDLE or MPG123_ERR otherwise (without further * explanation, the error state of the mpg123_handle is not modified by * this function). */ MPG123_EXPORT int mpg123_framedata( mpg123_handle *mh , unsigned long *header, unsigned char **bodydata, size_t *bodybytes ); /** Get the input position (byte offset in stream) of the last parsed frame. * This can be used for external seek index building, for example. * It just returns the internally stored offset, regardless of validity -- * you ensure that a valid frame has been parsed before! * \param mh handle * \return byte offset in stream */ MPG123_EXPORT off_t mpg123_framepos(mpg123_handle *mh); /*@}*/ /** \defgroup mpg123_seek mpg123 position and seeking * * Functions querying and manipulating position in the decoded audio bitstream. * The position is measured in decoded audio samples, or MPEG frame offset for the specific functions. * If gapless code is in effect, the positions are adjusted to compensate the skipped padding/delay - meaning, you should not care about that at all and just use the position defined for the samples you get out of the decoder;-) * The general usage is modelled after stdlib's ftell() and fseek(). * Especially, the whence parameter for the seek functions has the same meaning as the one for fseek() and needs the same constants from stdlib.h: * - SEEK_SET: set position to (or near to) specified offset * - SEEK_CUR: change position by offset from now * - SEEK_END: set position to offset from end * * Note that sample-accurate seek only works when gapless support has been enabled at compile time; seek is frame-accurate otherwise. * Also, really sample-accurate seeking (meaning that you get the identical sample value after seeking compared to plain decoding up to the position) is only guaranteed when you do not mess with the position code by using MPG123_UPSPEED, MPG123_DOWNSPEED or MPG123_START_FRAME. The first two mainly should cause trouble with NtoM resampling, but in any case with these options in effect, you have to keep in mind that the sample offset is not the same as counting the samples you get from decoding since mpg123 counts the skipped samples, too (or the samples played twice only once)! * Short: When you care about the sample position, don't mess with those parameters;-) * Also, seeking is not guaranteed to work for all streams (underlying stream may not support it). * And yet another caveat: If the stream is concatenated out of differing pieces (Frankenstein stream), seeking may suffer, too. * * @{ */ /** Returns the current position in samples. * On the next successful read, you'd get that sample. * \param mh handle * \return sample offset or MPG123_ERR (null handle) */ MPG123_EXPORT off_t mpg123_tell(mpg123_handle *mh); /** Returns the frame number that the next read will give you data from. * \param mh handle * \return frame offset or MPG123_ERR (null handle) */ MPG123_EXPORT off_t mpg123_tellframe(mpg123_handle *mh); /** Returns the current byte offset in the input stream. * \param mh handle * \return byte offset or MPG123_ERR (null handle) */ MPG123_EXPORT off_t mpg123_tell_stream(mpg123_handle *mh); /** Seek to a desired sample offset. * Usage is modelled afer the standard lseek(). * \param mh handle * \param sampleoff offset in PCM samples * \param whence one of SEEK_SET, SEEK_CUR or SEEK_END * \return The resulting offset >= 0 or error/message code */ MPG123_EXPORT off_t mpg123_seek( mpg123_handle *mh , off_t sampleoff, int whence ); /** Seek to a desired sample offset in data feeding mode. * This just prepares things to be right only if you ensure that the next chunk of input data will be from input_offset byte position. * \param mh handle * \param sampleoff offset in PCM samples * \param whence one of SEEK_SET, SEEK_CUR or SEEK_END * \param input_offset The position it expects to be at the * next time data is fed to mpg123_decode(). * \return The resulting offset >= 0 or error/message code */ MPG123_EXPORT off_t mpg123_feedseek( mpg123_handle *mh , off_t sampleoff, int whence, off_t *input_offset ); /** Seek to a desired MPEG frame offset. * Usage is modelled afer the standard lseek(). * \param mh handle * \param frameoff offset in MPEG frames * \param whence one of SEEK_SET, SEEK_CUR or SEEK_END * \return The resulting offset >= 0 or error/message code */ MPG123_EXPORT off_t mpg123_seek_frame( mpg123_handle *mh , off_t frameoff, int whence ); /** Return a MPEG frame offset corresponding to an offset in seconds. * This assumes that the samples per frame do not change in the file/stream, which is a good assumption for any sane file/stream only. * \return frame offset >= 0 or error/message code */ MPG123_EXPORT off_t mpg123_timeframe(mpg123_handle *mh, double sec); /** Give access to the frame index table that is managed for seeking. * You are asked not to modify the values... Use mpg123_set_index to set the * seek index * \param mh handle * \param offsets pointer to the index array * \param step one index byte offset advances this many MPEG frames * \param fill number of recorded index offsets; size of the array * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_index( mpg123_handle *mh , off_t **offsets, off_t *step, size_t *fill ); /** Set the frame index table * Setting offsets to NULL and fill > 0 will allocate fill entries. Setting offsets * to NULL and fill to 0 will clear the index and free the allocated memory used by the index. * \param mh handle * \param offsets pointer to the index array * \param step one index byte offset advances this many MPEG frames * \param fill number of recorded index offsets; size of the array * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_set_index( mpg123_handle *mh , off_t *offsets, off_t step, size_t fill ); /** An old crutch to keep old mpg123 binaries happy. * WARNING: This function is there only to avoid runtime linking errors with * standalone mpg123 before version 1.23.0 (if you strangely update the * library but not the end-user program) and actually is broken * for various cases (p.ex. 24 bit output). Do never use. It might eventually * be purged from the library. */ MPG123_EXPORT int mpg123_position( mpg123_handle *mh, off_t frame_offset, off_t buffered_bytes, off_t *current_frame, off_t *frames_left, double *current_seconds, double *seconds_left); /*@}*/ /** \defgroup mpg123_voleq mpg123 volume and equalizer * * @{ */ /** another channel enumeration, for left/right choice */ enum mpg123_channels { MPG123_LEFT=0x1 /**< The Left Channel. */ ,MPG123_RIGHT=0x2 /**< The Right Channel. */ ,MPG123_LR=0x3 /**< Both left and right channel; same as MPG123_LEFT|MPG123_RIGHT */ }; /** Set the 32 Band Audio Equalizer settings. * \param mh handle * \param channel Can be MPG123_LEFT, MPG123_RIGHT or MPG123_LEFT|MPG123_RIGHT for both. * \param band The equaliser band to change (from 0 to 31) * \param val The (linear) adjustment factor. * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_eq( mpg123_handle *mh , enum mpg123_channels channel, int band, double val ); /** Get the 32 Band Audio Equalizer settings. * \param mh handle * \param channel Can be MPG123_LEFT, MPG123_RIGHT or MPG123_LEFT|MPG123_RIGHT for (arithmetic mean of) both. * \param band The equaliser band to change (from 0 to 31) * \return The (linear) adjustment factor (zero for pad parameters) */ MPG123_EXPORT double mpg123_geteq(mpg123_handle *mh , enum mpg123_channels channel, int band); /** Reset the 32 Band Audio Equalizer settings to flat * \param mh handle * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_reset_eq(mpg123_handle *mh); /** Set the absolute output volume including the RVA setting, * vol<0 just applies (a possibly changed) RVA setting. * \param mh handle * \param vol volume value (linear factor) * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_volume(mpg123_handle *mh, double vol); /** Adjust output volume including the RVA setting by chosen amount * \param mh handle * \param change volume value (linear factor increment) * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_volume_change(mpg123_handle *mh, double change); /** Return current volume setting, the actual value due to RVA, and the RVA * adjustment itself. It's all as double float value to abstract the sample * format. The volume values are linear factors / amplitudes (not percent) * and the RVA value is in decibels. * \param mh handle * \param base return address for base volume (linear factor) * \param really return address for actual volume (linear factor) * \param rva_db return address for RVA value (decibels) * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_getvolume(mpg123_handle *mh, double *base, double *really, double *rva_db); /* TODO: Set some preamp in addition / to replace internal RVA handling? */ /*@}*/ /** \defgroup mpg123_status mpg123 status and information * * @{ */ /** Enumeration of the mode types of Variable Bitrate */ enum mpg123_vbr { MPG123_CBR=0, /**< Constant Bitrate Mode (default) */ MPG123_VBR, /**< Variable Bitrate Mode */ MPG123_ABR /**< Average Bitrate Mode */ }; /** Enumeration of the MPEG Versions */ enum mpg123_version { MPG123_1_0=0, /**< MPEG Version 1.0 */ MPG123_2_0, /**< MPEG Version 2.0 */ MPG123_2_5 /**< MPEG Version 2.5 */ }; /** Enumeration of the MPEG Audio mode. * Only the mono mode has 1 channel, the others have 2 channels. */ enum mpg123_mode { MPG123_M_STEREO=0, /**< Standard Stereo. */ MPG123_M_JOINT, /**< Joint Stereo. */ MPG123_M_DUAL, /**< Dual Channel. */ MPG123_M_MONO /**< Single Channel. */ }; /** Enumeration of the MPEG Audio flag bits */ enum mpg123_flags { MPG123_CRC=0x1, /**< The bitstream is error protected using 16-bit CRC. */ MPG123_COPYRIGHT=0x2, /**< The bitstream is copyrighted. */ MPG123_PRIVATE=0x4, /**< The private bit has been set. */ MPG123_ORIGINAL=0x8 /**< The bitstream is an original, not a copy. */ }; /** Data structure for storing information about a frame of MPEG Audio */ struct mpg123_frameinfo { enum mpg123_version version; /**< The MPEG version (1.0/2.0/2.5). */ int layer; /**< The MPEG Audio Layer (MP1/MP2/MP3). */ long rate; /**< The sampling rate in Hz. */ enum mpg123_mode mode; /**< The audio mode (Mono, Stereo, Joint-stero, Dual Channel). */ int mode_ext; /**< The mode extension bit flag. */ int framesize; /**< The size of the frame (in bytes, including header). */ enum mpg123_flags flags; /**< MPEG Audio flag bits. Just now I realize that it should be declared as int, not enum. It's a bitwise combination of the enum values. */ int emphasis; /**< The emphasis type. */ int bitrate; /**< Bitrate of the frame (kbps). */ int abr_rate; /**< The target average bitrate. */ enum mpg123_vbr vbr; /**< The VBR mode. */ }; /** Data structure for even more detailed information out of the decoder, * for MPEG layer III only. * This was added to support the frame analyzer by the Lame project and * just follows what was used there before. You know what the fields mean * if you want use this structure. */ struct mpg123_moreinfo { double xr[2][2][576]; double sfb[2][2][22]; /* [2][2][SBMAX_l] */ double sfb_s[2][2][3*13]; /* [2][2][3*SBMAX_s] */ int qss[2][2]; int big_values[2][2]; int sub_gain[2][2][3]; int scalefac_scale[2][2]; int preflag[2][2]; int blocktype[2][2]; int mixed[2][2]; int mainbits[2][2]; int sfbits[2][2]; int scfsi[2]; int maindata; int padding; }; /** Get frame information about the MPEG audio bitstream and store it in a mpg123_frameinfo structure. * \param mh handle * \param mi address of existing frameinfo structure to write to * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_info(mpg123_handle *mh, struct mpg123_frameinfo *mi); /** Trigger collection of additional decoder information while decoding. * \param mh handle * \param mi pointer to data storage (NULL to disable collection) * \return MPG123_OK if the collection was enabled/disabled as desired, MPG123_ERR * otherwise (e.g. if the feature is disabled) */ MPG123_EXPORT int mpg123_set_moreinfo( mpg123_handle *mh , struct mpg123_moreinfo *mi ); /** Get the safe output buffer size for all cases * (when you want to replace the internal buffer) * \return safe buffer size */ MPG123_EXPORT size_t mpg123_safe_buffer(void); /** Make a full parsing scan of each frame in the file. ID3 tags are found. An * accurate length value is stored. Seek index will be filled. A seek back to * current position is performed. At all, this function refuses work when * stream is not seekable. * \param mh handle * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_scan(mpg123_handle *mh); /** Return, if possible, the full (expected) length of current track in * MPEG frames. * \param mh handle * \return length >= 0 or MPG123_ERR if there is no length guess possible. */ MPG123_EXPORT off_t mpg123_framelength(mpg123_handle *mh); /** Return, if possible, the full (expected) length of current * track in samples (PCM frames). * * This relies either on an Info frame at the beginning or a previous * call to mpg123_scan() to get the real number of MPEG frames in a * file. It will guess based on file size if neither Info frame nor * scan data are present. In any case, there is no guarantee that the * decoder will not give you more data, for example in case the open * file gets appended to during decoding. * \param mh handle * \return length >= 0 or MPG123_ERR if there is no length guess possible. */ MPG123_EXPORT off_t mpg123_length(mpg123_handle *mh); /** Override the value for file size in bytes. * Useful for getting sensible track length values in feed mode or for HTTP streams. * \param mh handle * \param size file size in bytes * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_set_filesize(mpg123_handle *mh, off_t size); /** Get MPEG frame duration in seconds. * \param mh handle * \return frame duration in seconds, <0 on error */ MPG123_EXPORT double mpg123_tpf(mpg123_handle *mh); /** Get MPEG frame duration in samples. * \param mh handle * \return samples per frame for the most recently parsed frame; <0 on errors */ MPG123_EXPORT int mpg123_spf(mpg123_handle *mh); /** Get and reset the clip count. * \param mh handle * \return count of clipped samples */ MPG123_EXPORT long mpg123_clip(mpg123_handle *mh); /** The key values for state information from mpg123_getstate(). */ enum mpg123_state { MPG123_ACCURATE = 1 /**< Query if positons are currently accurate (integer value, 0 if false, 1 if true). */ ,MPG123_BUFFERFILL /**< Get fill of internal (feed) input buffer as integer byte count returned as long and as double. An error is returned on integer overflow while converting to (signed) long, but the returned floating point value shold still be fine. */ ,MPG123_FRANKENSTEIN /**< Stream consists of carelessly stitched together files. Seeking may yield unexpected results (also with MPG123_ACCURATE, it may be confused). */ ,MPG123_FRESH_DECODER /**< Decoder structure has been updated, possibly indicating changed stream (integer value, 0 if false, 1 if true). Flag is cleared after retrieval. */ ,MPG123_ENC_DELAY /** Encoder delay read from Info tag (layer III, -1 if unknown). */ ,MPG123_ENC_PADDING /** Encoder padding read from Info tag (layer III, -1 if unknown). */ ,MPG123_DEC_DELAY /** Decoder delay (for layer III only, -1 otherwise). */ }; /** Get various current decoder/stream state information. * \param mh handle * \param key the key to identify the information to give. * \param val the address to return (long) integer values to * \param fval the address to return floating point values to * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_getstate( mpg123_handle *mh , enum mpg123_state key, long *val, double *fval ); /*@}*/ /** \defgroup mpg123_metadata mpg123 metadata handling * * Functions to retrieve the metadata from MPEG Audio files and streams. * Also includes string handling functions. * * @{ */ /** Data structure for storing strings in a safer way than a standard C-String. * Can also hold a number of null-terminated strings. */ typedef struct { char* p; /**< pointer to the string data */ size_t size; /**< raw number of bytes allocated */ size_t fill; /**< number of used bytes (including closing zero byte) */ } mpg123_string; /** Allocate and intialize a new string. * \param val optional initial string value (can be NULL) */ MPG123_EXPORT mpg123_string* mpg123_new_string(const char* val); /** Free memory of contents and the string structure itself. * \param sb string handle */ MPG123_EXPORT void mpg123_delete_string(mpg123_string* sb); /** Initialize an existing mpg123_string structure to {NULL, 0, 0}. * If you hand in a NULL pointer here, your program should crash. The other * string functions are more forgiving, but this one here is too basic. * \param sb string handle (address of existing structure on your side) */ MPG123_EXPORT void mpg123_init_string(mpg123_string* sb); /** Free-up memory of the contents of an mpg123_string (not the struct itself). * This also calls mpg123_init_string() and hence is safe to be called * repeatedly. * \param sb string handle */ MPG123_EXPORT void mpg123_free_string(mpg123_string* sb); /** Change the size of a mpg123_string * \param sb string handle * \param news new size in bytes * \return 0 on error, 1 on success */ MPG123_EXPORT int mpg123_resize_string(mpg123_string* sb, size_t news); /** Increase size of a mpg123_string if necessary (it may stay larger). * Note that the functions for adding and setting in current libmpg123 * use this instead of mpg123_resize_string(). * That way, you can preallocate memory and safely work afterwards with * pieces. * \param sb string handle * \param news new minimum size * \return 0 on error, 1 on success */ MPG123_EXPORT int mpg123_grow_string(mpg123_string* sb, size_t news); /** Copy the contents of one mpg123_string string to another. * Yes the order of arguments is reversed compated to memcpy(). * \param from string handle * \param to string handle * \return 0 on error, 1 on success */ MPG123_EXPORT int mpg123_copy_string(mpg123_string* from, mpg123_string* to); /** Move the contents of one mpg123_string string to another. * This frees any memory associated with the target and moves over the * pointers from the source, leaving the source without content after * that. The only possible error is that you hand in NULL pointers. * If you handed in a valid source, its contents will be gone, even if * there was no target to move to. If you hand in a valid target, its * original contents will also always be gone, to be replaced with the * source's contents if there was some. * \param from source string handle * \param to target string handle * \return 0 on error, 1 on success */ MPG123_EXPORT int mpg123_move_string(mpg123_string* from, mpg123_string* to); /** Append a C-String to an mpg123_string * \param sb string handle * \param stuff to append * \return 0 on error, 1 on success */ MPG123_EXPORT int mpg123_add_string(mpg123_string* sb, const char* stuff); /** Append a C-substring to an mpg123 string * \param sb string handle * \param stuff content to copy * \param from offset to copy from * \param count number of characters to copy (a null-byte is always appended) * \return 0 on error, 1 on success */ MPG123_EXPORT int mpg123_add_substring( mpg123_string *sb , const char *stuff, size_t from, size_t count ); /** Set the content of a mpg123_string to a C-string * \param sb string handle * \param stuff content to copy * \return 0 on error, 1 on success */ MPG123_EXPORT int mpg123_set_string(mpg123_string* sb, const char* stuff); /** Set the content of a mpg123_string to a C-substring * \param sb string handle * \param stuff the future content * \param from offset to copy from * \param count number of characters to copy (a null-byte is always appended) * \return 0 on error, 1 on success */ MPG123_EXPORT int mpg123_set_substring( mpg123_string *sb , const char *stuff, size_t from, size_t count ); /** Count characters in a mpg123 string (non-null bytes or Unicode points). * This function is of limited use, as it does just count code points * encoded in an UTF-8 string, only loosely related to the count of visible * characters. Get your full Unicode handling support elsewhere. * \param sb string handle * \param utf8 a flag to tell if the string is in utf8 encoding * \return character count */ MPG123_EXPORT size_t mpg123_strlen(mpg123_string *sb, int utf8); /** Remove trailing \\r and \\n, if present. * \param sb string handle * \return 0 on error, 1 on success */ MPG123_EXPORT int mpg123_chomp_string(mpg123_string *sb); /** Determine if two strings contain the same data. * This only returns 1 if both given handles are non-NULL and * if they are filled with the same bytes. * \param a first string handle * \param b second string handle * \return 0 for different strings, 1 for identical */ MPG123_EXPORT int mpg123_same_string(mpg123_string *a, mpg123_string *b); /** The mpg123 text encodings. This contains encodings we encounter in ID3 tags or ICY meta info. */ enum mpg123_text_encoding { mpg123_text_unknown = 0 /**< Unkown encoding... mpg123_id3_encoding can return that on invalid codes. */ ,mpg123_text_utf8 = 1 /**< UTF-8 */ ,mpg123_text_latin1 = 2 /**< ISO-8859-1. Note that sometimes latin1 in ID3 is abused for totally different encodings. */ ,mpg123_text_icy = 3 /**< ICY metadata encoding, usually CP-1252 but we take it as UTF-8 if it qualifies as such. */ ,mpg123_text_cp1252 = 4 /**< Really CP-1252 without any guessing. */ ,mpg123_text_utf16 = 5 /**< Some UTF-16 encoding. The last of a set of leading BOMs (byte order mark) rules. * When there is no BOM, big endian ordering is used. Note that UCS-2 qualifies as UTF-8 when * you don't mess with the reserved code points. If you want to decode little endian data * without BOM you need to prepend 0xff 0xfe yourself. */ ,mpg123_text_utf16bom = 6 /**< Just an alias for UTF-16, ID3v2 has this as distinct code. */ ,mpg123_text_utf16be = 7 /**< Another alias for UTF16 from ID3v2. Note, that, because of the mess that is reality, * BOMs are used if encountered. There really is not much distinction between the UTF16 types for mpg123 * One exception: Since this is seen in ID3v2 tags, leading null bytes are skipped for all other UTF16 * types (we expect a BOM before real data there), not so for utf16be!*/ ,mpg123_text_max = 7 /**< Placeholder for the maximum encoding value. */ }; /** The encoding byte values from ID3v2. */ enum mpg123_id3_enc { mpg123_id3_latin1 = 0 /**< Note: This sometimes can mean anything in practice... */ ,mpg123_id3_utf16bom = 1 /**< UTF16, UCS-2 ... it's all the same for practical purposes. */ ,mpg123_id3_utf16be = 2 /**< Big-endian UTF-16, BOM see note for mpg123_text_utf16be. */ ,mpg123_id3_utf8 = 3 /**< Our lovely overly ASCII-compatible 8 byte encoding for the world. */ ,mpg123_id3_enc_max = 3 /**< Placeholder to check valid range of encoding byte. */ }; /** Convert ID3 encoding byte to mpg123 encoding index. * \param id3_enc_byte the ID3 encoding code * \return the mpg123 encoding index */ MPG123_EXPORT enum mpg123_text_encoding mpg123_enc_from_id3(unsigned char id3_enc_byte); /** Store text data in string, after converting to UTF-8 from indicated encoding * A prominent error can be that you provided an unknown encoding value, or this build of libmpg123 lacks support for certain encodings (ID3 or ICY stuff missing). * Also, you might want to take a bit of care with preparing the data; for example, strip leading zeroes (I have seen that). * \param sb target string * \param enc mpg123 text encoding value * \param source source buffer with plain unsigned bytes (you might need to cast from signed char) * \param source_size number of bytes in the source buffer * \return 0 on error, 1 on success (on error, mpg123_free_string is called on sb) */ MPG123_EXPORT int mpg123_store_utf8(mpg123_string *sb, enum mpg123_text_encoding enc, const unsigned char *source, size_t source_size); /** Sub data structure for ID3v2, for storing various text fields (including comments). * This is for ID3v2 COMM, TXXX and all the other text fields. * Only COMM, TXXX and USLT may have a description, only COMM and USLT * have a language. * You should consult the ID3v2 specification for the use of the various text fields * ("frames" in ID3v2 documentation, I use "fields" here to separate from MPEG frames). */ typedef struct { char lang[3]; /**< Three-letter language code (not terminated). */ char id[4]; /**< The ID3v2 text field id, like TALB, TPE2, ... (4 characters, no string termination). */ mpg123_string description; /**< Empty for the generic comment... */ mpg123_string text; /**< ... */ } mpg123_text; /** The picture type values from ID3v2. */ enum mpg123_id3_pic_type { mpg123_id3_pic_other = 0 /**< see ID3v2 docs */ ,mpg123_id3_pic_icon = 1 /**< see ID3v2 docs */ ,mpg123_id3_pic_other_icon = 2 /**< see ID3v2 docs */ ,mpg123_id3_pic_front_cover = 3 /**< see ID3v2 docs */ ,mpg123_id3_pic_back_cover = 4 /**< see ID3v2 docs */ ,mpg123_id3_pic_leaflet = 5 /**< see ID3v2 docs */ ,mpg123_id3_pic_media = 6 /**< see ID3v2 docs */ ,mpg123_id3_pic_lead = 7 /**< see ID3v2 docs */ ,mpg123_id3_pic_artist = 8 /**< see ID3v2 docs */ ,mpg123_id3_pic_conductor = 9 /**< see ID3v2 docs */ ,mpg123_id3_pic_orchestra = 10 /**< see ID3v2 docs */ ,mpg123_id3_pic_composer = 11 /**< see ID3v2 docs */ ,mpg123_id3_pic_lyricist = 12 /**< see ID3v2 docs */ ,mpg123_id3_pic_location = 13 /**< see ID3v2 docs */ ,mpg123_id3_pic_recording = 14 /**< see ID3v2 docs */ ,mpg123_id3_pic_performance = 15 /**< see ID3v2 docs */ ,mpg123_id3_pic_video = 16 /**< see ID3v2 docs */ ,mpg123_id3_pic_fish = 17 /**< see ID3v2 docs */ ,mpg123_id3_pic_illustration = 18 /**< see ID3v2 docs */ ,mpg123_id3_pic_artist_logo = 19 /**< see ID3v2 docs */ ,mpg123_id3_pic_publisher_logo = 20 /**< see ID3v2 docs */ }; /** Sub data structure for ID3v2, for storing picture data including comment. * This is for the ID3v2 APIC field. You should consult the ID3v2 specification * for the use of the APIC field ("frames" in ID3v2 documentation, I use "fields" * here to separate from MPEG frames). */ typedef struct { char type; /**< mpg123_id3_pic_type value */ mpg123_string description; /**< description string */ mpg123_string mime_type; /**< MIME type */ size_t size; /**< size in bytes */ unsigned char* data; /**< pointer to the image data */ } mpg123_picture; /** Data structure for storing IDV3v2 tags. * This structure is not a direct binary mapping with the file contents. * The ID3v2 text frames are allowed to contain multiple strings. * So check for null bytes until you reach the mpg123_string fill. * All text is encoded in UTF-8. */ typedef struct { unsigned char version; /**< 3 or 4 for ID3v2.3 or ID3v2.4. */ mpg123_string *title; /**< Title string (pointer into text_list). */ mpg123_string *artist; /**< Artist string (pointer into text_list). */ mpg123_string *album; /**< Album string (pointer into text_list). */ mpg123_string *year; /**< The year as a string (pointer into text_list). */ mpg123_string *genre; /**< Genre String (pointer into text_list). The genre string(s) may very well need postprocessing, esp. for ID3v2.3. */ mpg123_string *comment; /**< Pointer to last encountered comment text with empty description. */ /* Encountered ID3v2 fields are appended to these lists. There can be multiple occurences, the pointers above always point to the last encountered data. */ mpg123_text *comment_list; /**< Array of comments. */ size_t comments; /**< Number of comments. */ mpg123_text *text; /**< Array of ID3v2 text fields (including USLT) */ size_t texts; /**< Numer of text fields. */ mpg123_text *extra; /**< The array of extra (TXXX) fields. */ size_t extras; /**< Number of extra text (TXXX) fields. */ mpg123_picture *picture; /**< Array of ID3v2 pictures fields (APIC). Only populated if MPG123_PICTURE flag is set! */ size_t pictures; /**< Number of picture (APIC) fields. */ } mpg123_id3v2; /** Data structure for ID3v1 tags (the last 128 bytes of a file). * Don't take anything for granted (like string termination)! * Also note the change ID3v1.1 did: comment[28] = 0; comment[29] = track_number * It is your task to support ID3v1 only or ID3v1.1 ...*/ typedef struct { char tag[3]; /**< Always the string "TAG", the classic intro. */ char title[30]; /**< Title string. */ char artist[30]; /**< Artist string. */ char album[30]; /**< Album string. */ char year[4]; /**< Year string. */ char comment[30]; /**< Comment string. */ unsigned char genre; /**< Genre index. */ } mpg123_id3v1; #define MPG123_ID3 0x3 /**< 0011 There is some ID3 info. Also matches 0010 or NEW_ID3. */ #define MPG123_NEW_ID3 0x1 /**< 0001 There is ID3 info that changed since last call to mpg123_id3. */ #define MPG123_ICY 0xc /**< 1100 There is some ICY info. Also matches 0100 or NEW_ICY.*/ #define MPG123_NEW_ICY 0x4 /**< 0100 There is ICY info that changed since last call to mpg123_icy. */ /** Query if there is (new) meta info, be it ID3 or ICY (or something new in future). * \param mh handle * \return combination of flags, 0 on error (same as "nothing new") */ MPG123_EXPORT int mpg123_meta_check(mpg123_handle *mh); /** Clean up meta data storage (ID3v2 and ICY), freeing memory. * \param mh handle */ MPG123_EXPORT void mpg123_meta_free(mpg123_handle *mh); /** Point v1 and v2 to existing data structures wich may change on any next read/decode function call. * v1 and/or v2 can be set to NULL when there is no corresponding data. * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_id3( mpg123_handle *mh , mpg123_id3v1 **v1, mpg123_id3v2 **v2 ); /** Return pointers to and size of stored raw ID3 data if storage has * been configured with MPG123_RAW_ID3 and stream parsing passed the * metadata already. Null value with zero size is a possibility! * The storage can change at any next API call. * \param v1 address to store pointer to v1 tag * \param v1_size size of v1 data in bytes * \param v2 address to store pointer to v2 tag * \param v2_size size of v2 data in bytes * \return MPG123_OK or MPG123_ERR. Only on MPG123_OK the output * values are set. */ MPG123_EXPORT int mpg123_id3_raw( mpg123_handle *mh , unsigned char **v1, size_t *v1_size , unsigned char **v2, size_t *v2_size ); /** Point icy_meta to existing data structure wich may change on any next read/decode function call. * \param mh handle * \param icy_meta return address for ICY meta string (set to NULL if nothing there) * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_icy(mpg123_handle *mh, char **icy_meta); /** Decode from windows-1252 (the encoding ICY metainfo used) to UTF-8. * Note that this is very similar to mpg123_store_utf8(&sb, mpg123_text_icy, icy_text, strlen(icy_text+1)) . * \param icy_text The input data in ICY encoding * \return pointer to newly allocated buffer with UTF-8 data (You free() it!) */ MPG123_EXPORT char* mpg123_icy2utf8(const char* icy_text); /* @} */ /** \defgroup mpg123_advpar mpg123 advanced parameter API * * Direct access to a parameter set without full handle around it. * Possible uses: * - Influence behaviour of library _during_ initialization of handle (MPG123_VERBOSE). * - Use one set of parameters for multiple handles. * * The functions for handling mpg123_pars (mpg123_par() and mpg123_fmt() * family) directly return a fully qualified mpg123 error code, the ones * operating on full handles normally MPG123_OK or MPG123_ERR, storing the * specific error code itseld inside the handle. * * @{ */ /** Opaque structure for the libmpg123 decoder parameters. */ struct mpg123_pars_struct; /** Opaque structure for the libmpg123 decoder parameters. */ typedef struct mpg123_pars_struct mpg123_pars; /** Create a handle with preset parameters. * \param mp parameter handle * \param decoder decoder choice * \param error error code return address * \return mpg123 handle */ MPG123_EXPORT mpg123_handle *mpg123_parnew( mpg123_pars *mp , const char* decoder, int *error ); /** Allocate memory for and return a pointer to a new mpg123_pars * \param error error code return address * \return new parameter handle */ MPG123_EXPORT mpg123_pars *mpg123_new_pars(int *error); /** Delete and free up memory used by a mpg123_pars data structure * \param mp parameter handle */ MPG123_EXPORT void mpg123_delete_pars(mpg123_pars* mp); /** Configure mpg123 parameters to accept no output format at all, * use before specifying supported formats with mpg123_format * \param mp parameter handle * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_fmt_none(mpg123_pars *mp); /** Configure mpg123 parameters to accept all formats * (also any custom rate you may set) -- this is default. * \param mp parameter handle * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_fmt_all(mpg123_pars *mp); /** Set the audio format support of a mpg123_pars in detail: * \param mp parameter handle * \param rate The sample rate value (in Hertz). * \param channels A combination of MPG123_STEREO and MPG123_MONO. * \param encodings A combination of accepted encodings for rate and channels, * p.ex MPG123_ENC_SIGNED16|MPG123_ENC_ULAW_8 (or 0 for no * support). * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_fmt(mpg123_pars *mp , long rate, int channels, int encodings); /** Set the audio format support of a mpg123_pars in detail: * \param mp parameter handle * \param rate The sample rate value (in Hertz). Special value 0 means * all rates (reason for this variant of mpg123_fmt). * \param channels A combination of MPG123_STEREO and MPG123_MONO. * \param encodings A combination of accepted encodings for rate and channels, * p.ex MPG123_ENC_SIGNED16|MPG123_ENC_ULAW_8 (or 0 for no * support). * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_fmt2(mpg123_pars *mp , long rate, int channels, int encodings); /** Check to see if a specific format at a specific rate is supported * by mpg123_pars. * \param mp parameter handle * \param rate sampling rate * \param encoding encoding * \return 0 for no support (that includes invalid parameters), MPG123_STEREO, * MPG123_MONO or MPG123_STEREO|MPG123_MONO. */ MPG123_EXPORT int mpg123_fmt_support(mpg123_pars *mp, long rate, int encoding); /** Set a specific parameter, for a specific mpg123_pars, using a parameter * type key chosen from the mpg123_parms enumeration, to the specified value. * \param mp parameter handle * \param type parameter choice * \param value integer value * \param fvalue floating point value * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_par( mpg123_pars *mp , enum mpg123_parms type, long value, double fvalue ); /** Get a specific parameter, for a specific mpg123_pars. * See the mpg123_parms enumeration for a list of available parameters. * \param mp parameter handle * \param type parameter choice * \param value integer value return address * \param fvalue floating point value return address * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_getpar( mpg123_pars *mp , enum mpg123_parms type, long *value, double *fvalue); /* @} */ /** \defgroup mpg123_lowio mpg123 low level I/O * You may want to do tricky stuff with I/O that does not work with mpg123's default file access or you want to make it decode into your own pocket... * * @{ */ /** Replace default internal buffer with user-supplied buffer. * Instead of working on it's own private buffer, mpg123 will directly use the one you provide for storing decoded audio. * Note that the required buffer size could be bigger than expected from output * encoding if libmpg123 has to convert from primary decoder output (p.ex. 32 bit * storage for 24 bit output). * * Note: The type of data changed to a void pointer in mpg123 1.26.0 * (API version 45). * * \param mh handle * \param data pointer to user buffer * \param size of buffer in bytes * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_replace_buffer(mpg123_handle *mh , void *data, size_t size); /** The max size of one frame's decoded output with current settings. * Use that to determine an appropriate minimum buffer size for decoding one frame. * \param mh handle * \return maximum decoded data size in bytes */ MPG123_EXPORT size_t mpg123_outblock(mpg123_handle *mh); /** Replace low-level stream access functions; read and lseek as known in POSIX. * You can use this to make any fancy file opening/closing yourself, * using mpg123_open_fd() to set the file descriptor for your read/lseek * (doesn't need to be a "real" file descriptor...). * Setting a function to NULL means that the default internal read is * used (active from next mpg123_open call on). * Note: As it would be troublesome to mess with this while having a file open, * this implies mpg123_close(). * \param mh handle * \param r_read callback for reading (behaviour like POSIX read) * \param r_lseek callback for seeking (like POSIX lseek) * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_replace_reader( mpg123_handle *mh , ssize_t (*r_read) (int, void *, size_t) , off_t (*r_lseek)(int, off_t, int) ); /** Replace I/O functions with your own ones operating on some kind of * handle instead of integer descriptors. * The handle is a void pointer, so you can pass any data you want... * mpg123_open_handle() is the call you make to use the I/O defined here. * There is no fallback to internal read/seek here. * Note: As it would be troublesome to mess with this while having a file open, * this mpg123_close() is implied here. * \param mh handle * \param r_read callback for reading (behaviour like POSIX read) * \param r_lseek callback for seeking (like POSIX lseek) * \param cleanup A callback to clean up an I/O handle on mpg123_close, * can be NULL for none (you take care of cleaning your handles). * \return MPG123_OK on success */ MPG123_EXPORT int mpg123_replace_reader_handle( mpg123_handle *mh , ssize_t (*r_read) (void *, void *, size_t) , off_t (*r_lseek)(void *, off_t, int) , void (*cleanup)(void*) ); /* @} */ #ifdef __cplusplus } #endif #endif ================================================ FILE: vendor/openal-soft/COPYING ================================================ GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ================================================ FILE: vendor/openal-soft/include/AL/al.h ================================================ #ifndef AL_AL_H #define AL_AL_H #if defined(__cplusplus) extern "C" { #endif #ifndef AL_API #if defined(AL_LIBTYPE_STATIC) #define AL_API #elif defined(_WIN32) #define AL_API __declspec(dllimport) #else #define AL_API extern #endif #endif #if defined(_WIN32) #define AL_APIENTRY __cdecl #else #define AL_APIENTRY #endif /* Deprecated macros. */ #define OPENAL #define ALAPI AL_API #define ALAPIENTRY AL_APIENTRY #define AL_INVALID (-1) #define AL_ILLEGAL_ENUM AL_INVALID_ENUM #define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION /* Supported AL versions. */ #define AL_VERSION_1_0 #define AL_VERSION_1_1 /** 8-bit boolean */ typedef char ALboolean; /** character */ typedef char ALchar; /** signed 8-bit 2's complement integer */ typedef signed char ALbyte; /** unsigned 8-bit integer */ typedef unsigned char ALubyte; /** signed 16-bit 2's complement integer */ typedef short ALshort; /** unsigned 16-bit integer */ typedef unsigned short ALushort; /** signed 32-bit 2's complement integer */ typedef int ALint; /** unsigned 32-bit integer */ typedef unsigned int ALuint; /** non-negative 32-bit binary integer size */ typedef int ALsizei; /** enumerated 32-bit value */ typedef int ALenum; /** 32-bit IEEE754 floating-point */ typedef float ALfloat; /** 64-bit IEEE754 floating-point */ typedef double ALdouble; /** void type (for opaque pointers only) */ typedef void ALvoid; /* Enumerant values begin at column 50. No tabs. */ /** "no distance model" or "no buffer" */ #define AL_NONE 0 /** Boolean False. */ #define AL_FALSE 0 /** Boolean True. */ #define AL_TRUE 1 /** * Relative source. * Type: ALboolean * Range: [AL_TRUE, AL_FALSE] * Default: AL_FALSE * * Specifies if the Source has relative coordinates. */ #define AL_SOURCE_RELATIVE 0x202 /** * Inner cone angle, in degrees. * Type: ALint, ALfloat * Range: [0 - 360] * Default: 360 * * The angle covered by the inner cone, where the source will not attenuate. */ #define AL_CONE_INNER_ANGLE 0x1001 /** * Outer cone angle, in degrees. * Range: [0 - 360] * Default: 360 * * The angle covered by the outer cone, where the source will be fully * attenuated. */ #define AL_CONE_OUTER_ANGLE 0x1002 /** * Source pitch. * Type: ALfloat * Range: [0.5 - 2.0] * Default: 1.0 * * A multiplier for the frequency (sample rate) of the source's buffer. */ #define AL_PITCH 0x1003 /** * Source or listener position. * Type: ALfloat[3], ALint[3] * Default: {0, 0, 0} * * The source or listener location in three dimensional space. * * OpenAL, like OpenGL, uses a right handed coordinate system, where in a * frontal default view X (thumb) points right, Y points up (index finger), and * Z points towards the viewer/camera (middle finger). * * To switch from a left handed coordinate system, flip the sign on the Z * coordinate. */ #define AL_POSITION 0x1004 /** * Source direction. * Type: ALfloat[3], ALint[3] * Default: {0, 0, 0} * * Specifies the current direction in local space. * A zero-length vector specifies an omni-directional source (cone is ignored). */ #define AL_DIRECTION 0x1005 /** * Source or listener velocity. * Type: ALfloat[3], ALint[3] * Default: {0, 0, 0} * * Specifies the current velocity in local space. */ #define AL_VELOCITY 0x1006 /** * Source looping. * Type: ALboolean * Range: [AL_TRUE, AL_FALSE] * Default: AL_FALSE * * Specifies whether source is looping. */ #define AL_LOOPING 0x1007 /** * Source buffer. * Type: ALuint * Range: any valid Buffer. * * Specifies the buffer to provide sound samples. */ #define AL_BUFFER 0x1009 /** * Source or listener gain. * Type: ALfloat * Range: [0.0 - ] * * A value of 1.0 means unattenuated. Each division by 2 equals an attenuation * of about -6dB. Each multiplicaton by 2 equals an amplification of about * +6dB. * * A value of 0.0 is meaningless with respect to a logarithmic scale; it is * silent. */ #define AL_GAIN 0x100A /** * Minimum source gain. * Type: ALfloat * Range: [0.0 - 1.0] * * The minimum gain allowed for a source, after distance and cone attenation is * applied (if applicable). */ #define AL_MIN_GAIN 0x100D /** * Maximum source gain. * Type: ALfloat * Range: [0.0 - 1.0] * * The maximum gain allowed for a source, after distance and cone attenation is * applied (if applicable). */ #define AL_MAX_GAIN 0x100E /** * Listener orientation. * Type: ALfloat[6] * Default: {0.0, 0.0, -1.0, 0.0, 1.0, 0.0} * * Effectively two three dimensional vectors. The first vector is the front (or * "at") and the second is the top (or "up"). * * Both vectors are in local space. */ #define AL_ORIENTATION 0x100F /** * Source state (query only). * Type: ALint * Range: [AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED] */ #define AL_SOURCE_STATE 0x1010 /* Source state values. */ #define AL_INITIAL 0x1011 #define AL_PLAYING 0x1012 #define AL_PAUSED 0x1013 #define AL_STOPPED 0x1014 /** * Source Buffer Queue size (query only). * Type: ALint * * The number of buffers queued using alSourceQueueBuffers, minus the buffers * removed with alSourceUnqueueBuffers. */ #define AL_BUFFERS_QUEUED 0x1015 /** * Source Buffer Queue processed count (query only). * Type: ALint * * The number of queued buffers that have been fully processed, and can be * removed with alSourceUnqueueBuffers. * * Looping sources will never fully process buffers because they will be set to * play again for when the source loops. */ #define AL_BUFFERS_PROCESSED 0x1016 /** * Source reference distance. * Type: ALfloat * Range: [0.0 - ] * Default: 1.0 * * The distance in units that no attenuation occurs. * * At 0.0, no distance attenuation ever occurs on non-linear attenuation models. */ #define AL_REFERENCE_DISTANCE 0x1020 /** * Source rolloff factor. * Type: ALfloat * Range: [0.0 - ] * Default: 1.0 * * Multiplier to exaggerate or diminish distance attenuation. * * At 0.0, no distance attenuation ever occurs. */ #define AL_ROLLOFF_FACTOR 0x1021 /** * Outer cone gain. * Type: ALfloat * Range: [0.0 - 1.0] * Default: 0.0 * * The gain attenuation applied when the listener is outside of the source's * outer cone. */ #define AL_CONE_OUTER_GAIN 0x1022 /** * Source maximum distance. * Type: ALfloat * Range: [0.0 - ] * Default: FLT_MAX * * The distance above which the source is not attenuated any further with a * clamped distance model, or where attenuation reaches 0.0 gain for linear * distance models with a default rolloff factor. */ #define AL_MAX_DISTANCE 0x1023 /** Source buffer position, in seconds */ #define AL_SEC_OFFSET 0x1024 /** Source buffer position, in sample frames */ #define AL_SAMPLE_OFFSET 0x1025 /** Source buffer position, in bytes */ #define AL_BYTE_OFFSET 0x1026 /** * Source type (query only). * Type: ALint * Range: [AL_STATIC, AL_STREAMING, AL_UNDETERMINED] * * A Source is Static if a Buffer has been attached using AL_BUFFER. * * A Source is Streaming if one or more Buffers have been attached using * alSourceQueueBuffers. * * A Source is Undetermined when it has the NULL buffer attached using * AL_BUFFER. */ #define AL_SOURCE_TYPE 0x1027 /* Source type values. */ #define AL_STATIC 0x1028 #define AL_STREAMING 0x1029 #define AL_UNDETERMINED 0x1030 /** Unsigned 8-bit mono buffer format. */ #define AL_FORMAT_MONO8 0x1100 /** Signed 16-bit mono buffer format. */ #define AL_FORMAT_MONO16 0x1101 /** Unsigned 8-bit stereo buffer format. */ #define AL_FORMAT_STEREO8 0x1102 /** Signed 16-bit stereo buffer format. */ #define AL_FORMAT_STEREO16 0x1103 /** Buffer frequency (query only). */ #define AL_FREQUENCY 0x2001 /** Buffer bits per sample (query only). */ #define AL_BITS 0x2002 /** Buffer channel count (query only). */ #define AL_CHANNELS 0x2003 /** Buffer data size (query only). */ #define AL_SIZE 0x2004 /* Buffer state. Not for public use. */ #define AL_UNUSED 0x2010 #define AL_PENDING 0x2011 #define AL_PROCESSED 0x2012 /** No error. */ #define AL_NO_ERROR 0 /** Invalid name paramater passed to AL call. */ #define AL_INVALID_NAME 0xA001 /** Invalid enum parameter passed to AL call. */ #define AL_INVALID_ENUM 0xA002 /** Invalid value parameter passed to AL call. */ #define AL_INVALID_VALUE 0xA003 /** Illegal AL call. */ #define AL_INVALID_OPERATION 0xA004 /** Not enough memory. */ #define AL_OUT_OF_MEMORY 0xA005 /** Context string: Vendor ID. */ #define AL_VENDOR 0xB001 /** Context string: Version. */ #define AL_VERSION 0xB002 /** Context string: Renderer ID. */ #define AL_RENDERER 0xB003 /** Context string: Space-separated extension list. */ #define AL_EXTENSIONS 0xB004 /** * Doppler scale. * Type: ALfloat * Range: [0.0 - ] * Default: 1.0 * * Scale for source and listener velocities. */ #define AL_DOPPLER_FACTOR 0xC000 AL_API void AL_APIENTRY alDopplerFactor(ALfloat value); /** * Doppler velocity (deprecated). * * A multiplier applied to the Speed of Sound. */ #define AL_DOPPLER_VELOCITY 0xC001 AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value); /** * Speed of Sound, in units per second. * Type: ALfloat * Range: [0.0001 - ] * Default: 343.3 * * The speed at which sound waves are assumed to travel, when calculating the * doppler effect. */ #define AL_SPEED_OF_SOUND 0xC003 AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value); /** * Distance attenuation model. * Type: ALint * Range: [AL_NONE, AL_INVERSE_DISTANCE, AL_INVERSE_DISTANCE_CLAMPED, * AL_LINEAR_DISTANCE, AL_LINEAR_DISTANCE_CLAMPED, * AL_EXPONENT_DISTANCE, AL_EXPONENT_DISTANCE_CLAMPED] * Default: AL_INVERSE_DISTANCE_CLAMPED * * The model by which sources attenuate with distance. * * None - No distance attenuation. * Inverse - Doubling the distance halves the source gain. * Linear - Linear gain scaling between the reference and max distances. * Exponent - Exponential gain dropoff. * * Clamped variations work like the non-clamped counterparts, except the * distance calculated is clamped between the reference and max distances. */ #define AL_DISTANCE_MODEL 0xD000 AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel); /* Distance model values. */ #define AL_INVERSE_DISTANCE 0xD001 #define AL_INVERSE_DISTANCE_CLAMPED 0xD002 #define AL_LINEAR_DISTANCE 0xD003 #define AL_LINEAR_DISTANCE_CLAMPED 0xD004 #define AL_EXPONENT_DISTANCE 0xD005 #define AL_EXPONENT_DISTANCE_CLAMPED 0xD006 /* Renderer State management. */ AL_API void AL_APIENTRY alEnable(ALenum capability); AL_API void AL_APIENTRY alDisable(ALenum capability); AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability); /* State retrieval. */ AL_API const ALchar* AL_APIENTRY alGetString(ALenum param); AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values); AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values); AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values); AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values); AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param); AL_API ALint AL_APIENTRY alGetInteger(ALenum param); AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param); AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param); /* Error retrieval. */ /** Obtain the first error generated in the AL context since the last check. */ AL_API ALenum AL_APIENTRY alGetError(void); /** Query for the presence of an extension on the AL context. */ AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname); /** * Retrieve the address of a function. The returned function may be context- * specific. */ AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname); /** * Retrieve the value of an enum. The returned value may be context-specific. */ AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename); /* Set Listener parameters */ AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value); AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values); AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value); AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3); AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values); /* Get Listener parameters */ AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value); AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values); AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value); AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3); AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values); /** Create Source objects. */ AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources); /** Delete Source objects. */ AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources); /** Verify a handle is a valid Source. */ AL_API ALboolean AL_APIENTRY alIsSource(ALuint source); /* Set Source parameters. */ AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value); AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values); AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value); AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values); /* Get Source parameters. */ AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value); AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values); AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value); AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values); /** Play, replay, or resume (if paused) a list of Sources */ AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources); /** Stop a list of Sources */ AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources); /** Rewind a list of Sources */ AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources); /** Pause a list of Sources */ AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources); /** Play, replay, or resume a Source */ AL_API void AL_APIENTRY alSourcePlay(ALuint source); /** Stop a Source */ AL_API void AL_APIENTRY alSourceStop(ALuint source); /** Rewind a Source (set playback postiton to beginning) */ AL_API void AL_APIENTRY alSourceRewind(ALuint source); /** Pause a Source */ AL_API void AL_APIENTRY alSourcePause(ALuint source); /** Queue buffers onto a source */ AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers); /** Unqueue processed buffers from a source */ AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers); /** Create Buffer objects */ AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers); /** Delete Buffer objects */ AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers); /** Verify a handle is a valid Buffer */ AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer); /** Specifies the data to be copied into a buffer */ AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq); /* Set Buffer parameters, */ AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value); AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values); AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value); AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values); /* Get Buffer parameters. */ AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value); AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values); AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value); AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values); /* Pointer-to-function type, useful for dynamically getting AL entry points. */ typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability); typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability); typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability); typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param); typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values); typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values); typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values); typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values); typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param); typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param); typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param); typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param); typedef ALenum (AL_APIENTRY *LPALGETERROR)(void); typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname); typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname); typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename); typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value); typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values); typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value); typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3); typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values); typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value); typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values); typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value); typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3); typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values); typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources); typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources); typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source); typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value); typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values); typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value); typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values); typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value); typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values); typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value); typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values); typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources); typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources); typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources); typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources); typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source); typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source); typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source); typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source); typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers); typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers); typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers); typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers); typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer); typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq); typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value); typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values); typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value); typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values); typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value); typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values); typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value); typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values); typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value); typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value); typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value); typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel); #if defined(__cplusplus) } /* extern "C" */ #endif #endif /* AL_AL_H */ ================================================ FILE: vendor/openal-soft/include/AL/alc.h ================================================ #ifndef AL_ALC_H #define AL_ALC_H #if defined(__cplusplus) extern "C" { #endif #ifndef ALC_API #if defined(AL_LIBTYPE_STATIC) #define ALC_API #elif defined(_WIN32) #define ALC_API __declspec(dllimport) #else #define ALC_API extern #endif #endif #if defined(_WIN32) #define ALC_APIENTRY __cdecl #else #define ALC_APIENTRY #endif /* Deprecated macros. */ #define ALCAPI ALC_API #define ALCAPIENTRY ALC_APIENTRY #define ALC_INVALID 0 /** Supported ALC version? */ #define ALC_VERSION_0_1 1 /** Opaque device handle */ typedef struct ALCdevice ALCdevice; /** Opaque context handle */ typedef struct ALCcontext ALCcontext; /** 8-bit boolean */ typedef char ALCboolean; /** character */ typedef char ALCchar; /** signed 8-bit 2's complement integer */ typedef signed char ALCbyte; /** unsigned 8-bit integer */ typedef unsigned char ALCubyte; /** signed 16-bit 2's complement integer */ typedef short ALCshort; /** unsigned 16-bit integer */ typedef unsigned short ALCushort; /** signed 32-bit 2's complement integer */ typedef int ALCint; /** unsigned 32-bit integer */ typedef unsigned int ALCuint; /** non-negative 32-bit binary integer size */ typedef int ALCsizei; /** enumerated 32-bit value */ typedef int ALCenum; /** 32-bit IEEE754 floating-point */ typedef float ALCfloat; /** 64-bit IEEE754 floating-point */ typedef double ALCdouble; /** void type (for opaque pointers only) */ typedef void ALCvoid; /* Enumerant values begin at column 50. No tabs. */ /** Boolean False. */ #define ALC_FALSE 0 /** Boolean True. */ #define ALC_TRUE 1 /** Context attribute: Hz. */ #define ALC_FREQUENCY 0x1007 /** Context attribute: Hz. */ #define ALC_REFRESH 0x1008 /** Context attribute: AL_TRUE or AL_FALSE synchronous context? */ #define ALC_SYNC 0x1009 /** Context attribute: requested Mono (3D) Sources. */ #define ALC_MONO_SOURCES 0x1010 /** Context attribute: requested Stereo Sources. */ #define ALC_STEREO_SOURCES 0x1011 /** No error. */ #define ALC_NO_ERROR 0 /** Invalid device handle. */ #define ALC_INVALID_DEVICE 0xA001 /** Invalid context handle. */ #define ALC_INVALID_CONTEXT 0xA002 /** Invalid enum parameter passed to an ALC call. */ #define ALC_INVALID_ENUM 0xA003 /** Invalid value parameter passed to an ALC call. */ #define ALC_INVALID_VALUE 0xA004 /** Out of memory. */ #define ALC_OUT_OF_MEMORY 0xA005 /** Runtime ALC major version. */ #define ALC_MAJOR_VERSION 0x1000 /** Runtime ALC minor version. */ #define ALC_MINOR_VERSION 0x1001 /** Context attribute list size. */ #define ALC_ATTRIBUTES_SIZE 0x1002 /** Context attribute list properties. */ #define ALC_ALL_ATTRIBUTES 0x1003 /** String for the default device specifier. */ #define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 /** * String for the given device's specifier. * * If device handle is NULL, it is instead a null-char separated list of * strings of known device specifiers (list ends with an empty string). */ #define ALC_DEVICE_SPECIFIER 0x1005 /** String for space-separated list of ALC extensions. */ #define ALC_EXTENSIONS 0x1006 /** Capture extension */ #define ALC_EXT_CAPTURE 1 /** * String for the given capture device's specifier. * * If device handle is NULL, it is instead a null-char separated list of * strings of known capture device specifiers (list ends with an empty string). */ #define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 /** String for the default capture device specifier. */ #define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311 /** Number of sample frames available for capture. */ #define ALC_CAPTURE_SAMPLES 0x312 /** Enumerate All extension */ #define ALC_ENUMERATE_ALL_EXT 1 /** String for the default extended device specifier. */ #define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 /** * String for the given extended device's specifier. * * If device handle is NULL, it is instead a null-char separated list of * strings of known extended device specifiers (list ends with an empty string). */ #define ALC_ALL_DEVICES_SPECIFIER 0x1013 /* Context management. */ /** Create and attach a context to the given device. */ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist); /** * Makes the given context the active process-wide context. Passing NULL clears * the active context. */ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context); /** Resumes processing updates for the given context. */ ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context); /** Suspends updates for the given context. */ ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context); /** Remove a context from its device and destroys it. */ ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context); /** Returns the currently active context. */ ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void); /** Returns the device that a particular context is attached to. */ ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context); /* Device management. */ /** Opens the named playback device. */ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename); /** Closes the given playback device. */ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device); /* Error support. */ /** Obtain the most recent Device error. */ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device); /* Extension support. */ /** * Query for the presence of an extension on the device. Pass a NULL device to * query a device-inspecific extension. */ ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname); /** * Retrieve the address of a function. Given a non-NULL device, the returned * function may be device-specific. */ ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname); /** * Retrieve the value of an enum. Given a non-NULL device, the returned value * may be device-specific. */ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname); /* Query functions. */ /** Returns information about the device, and error strings. */ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param); /** Returns information about the device and the version of OpenAL. */ ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); /* Capture functions. */ /** * Opens the named capture device with the given frequency, format, and buffer * size. */ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); /** Closes the given capture device. */ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device); /** Starts capturing samples into the device buffer. */ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device); /** Stops capturing samples. Samples in the device buffer remain available. */ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device); /** Reads samples from the device buffer. */ ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); /* Pointer-to-function type, useful for dynamically getting ALC entry points. */ typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist); typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context); typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context); typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context); typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context); typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void); typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context); typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename); typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device); typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device); typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname); typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname); typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname); typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param); typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device); typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device); typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device); typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); #if defined(__cplusplus) } #endif #endif /* AL_ALC_H */ ================================================ FILE: vendor/openal-soft/include/AL/alext.h ================================================ /** * OpenAL cross platform audio library * Copyright (C) 2008 by authors. * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Or go to http://www.gnu.org/copyleft/lgpl.html */ #ifndef AL_ALEXT_H #define AL_ALEXT_H #include /* Define int64_t and uint64_t types */ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #include #elif defined(__cplusplus) && __cplusplus >= 201103L #include #elif defined(_WIN32) && defined(__GNUC__) #include #elif defined(_WIN32) typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #else /* Fallback if nothing above works */ #include #endif #include "alc.h" #include "al.h" #ifdef __cplusplus extern "C" { #endif #ifndef AL_LOKI_IMA_ADPCM_format #define AL_LOKI_IMA_ADPCM_format 1 #define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000 #define AL_FORMAT_IMA_ADPCM_STEREO16_EXT 0x10001 #endif #ifndef AL_LOKI_WAVE_format #define AL_LOKI_WAVE_format 1 #define AL_FORMAT_WAVE_EXT 0x10002 #endif #ifndef AL_EXT_vorbis #define AL_EXT_vorbis 1 #define AL_FORMAT_VORBIS_EXT 0x10003 #endif #ifndef AL_LOKI_quadriphonic #define AL_LOKI_quadriphonic 1 #define AL_FORMAT_QUAD8_LOKI 0x10004 #define AL_FORMAT_QUAD16_LOKI 0x10005 #endif #ifndef AL_EXT_float32 #define AL_EXT_float32 1 #define AL_FORMAT_MONO_FLOAT32 0x10010 #define AL_FORMAT_STEREO_FLOAT32 0x10011 #endif #ifndef AL_EXT_double #define AL_EXT_double 1 #define AL_FORMAT_MONO_DOUBLE_EXT 0x10012 #define AL_FORMAT_STEREO_DOUBLE_EXT 0x10013 #endif #ifndef AL_EXT_MULAW #define AL_EXT_MULAW 1 #define AL_FORMAT_MONO_MULAW_EXT 0x10014 #define AL_FORMAT_STEREO_MULAW_EXT 0x10015 #endif #ifndef AL_EXT_ALAW #define AL_EXT_ALAW 1 #define AL_FORMAT_MONO_ALAW_EXT 0x10016 #define AL_FORMAT_STEREO_ALAW_EXT 0x10017 #endif #ifndef ALC_LOKI_audio_channel #define ALC_LOKI_audio_channel 1 #define ALC_CHAN_MAIN_LOKI 0x500001 #define ALC_CHAN_PCM_LOKI 0x500002 #define ALC_CHAN_CD_LOKI 0x500003 #endif #ifndef AL_EXT_MCFORMATS #define AL_EXT_MCFORMATS 1 /* Provides support for surround sound buffer formats with 8, 16, and 32-bit * samples. * * QUAD8: Unsigned 8-bit, Quadraphonic (Front Left, Front Right, Rear Left, * Rear Right). * QUAD16: Signed 16-bit, Quadraphonic. * QUAD32: 32-bit float, Quadraphonic. * REAR8: Unsigned 8-bit, Rear Stereo (Rear Left, Rear Right). * REAR16: Signed 16-bit, Rear Stereo. * REAR32: 32-bit float, Rear Stereo. * 51CHN8: Unsigned 8-bit, 5.1 Surround (Front Left, Front Right, Front Center, * LFE, Side Left, Side Right). Note that some audio systems may label * 5.1's Side channels as Rear or Surround; they are equivalent for the * purposes of this extension. * 51CHN16: Signed 16-bit, 5.1 Surround. * 51CHN32: 32-bit float, 5.1 Surround. * 61CHN8: Unsigned 8-bit, 6.1 Surround (Front Left, Front Right, Front Center, * LFE, Rear Center, Side Left, Side Right). * 61CHN16: Signed 16-bit, 6.1 Surround. * 61CHN32: 32-bit float, 6.1 Surround. * 71CHN8: Unsigned 8-bit, 7.1 Surround (Front Left, Front Right, Front Center, * LFE, Rear Left, Rear Right, Side Left, Side Right). * 71CHN16: Signed 16-bit, 7.1 Surround. * 71CHN32: 32-bit float, 7.1 Surround. */ #define AL_FORMAT_QUAD8 0x1204 #define AL_FORMAT_QUAD16 0x1205 #define AL_FORMAT_QUAD32 0x1206 #define AL_FORMAT_REAR8 0x1207 #define AL_FORMAT_REAR16 0x1208 #define AL_FORMAT_REAR32 0x1209 #define AL_FORMAT_51CHN8 0x120A #define AL_FORMAT_51CHN16 0x120B #define AL_FORMAT_51CHN32 0x120C #define AL_FORMAT_61CHN8 0x120D #define AL_FORMAT_61CHN16 0x120E #define AL_FORMAT_61CHN32 0x120F #define AL_FORMAT_71CHN8 0x1210 #define AL_FORMAT_71CHN16 0x1211 #define AL_FORMAT_71CHN32 0x1212 #endif #ifndef AL_EXT_MULAW_MCFORMATS #define AL_EXT_MULAW_MCFORMATS 1 #define AL_FORMAT_MONO_MULAW 0x10014 #define AL_FORMAT_STEREO_MULAW 0x10015 #define AL_FORMAT_QUAD_MULAW 0x10021 #define AL_FORMAT_REAR_MULAW 0x10022 #define AL_FORMAT_51CHN_MULAW 0x10023 #define AL_FORMAT_61CHN_MULAW 0x10024 #define AL_FORMAT_71CHN_MULAW 0x10025 #endif #ifndef AL_EXT_IMA4 #define AL_EXT_IMA4 1 #define AL_FORMAT_MONO_IMA4 0x1300 #define AL_FORMAT_STEREO_IMA4 0x1301 #endif #ifndef AL_EXT_STATIC_BUFFER #define AL_EXT_STATIC_BUFFER 1 typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei); #ifdef AL_ALEXT_PROTOTYPES AL_API void AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq); #endif #endif #ifndef ALC_EXT_EFX #define ALC_EXT_EFX 1 #include "efx.h" #endif #ifndef ALC_EXT_disconnect #define ALC_EXT_disconnect 1 #define ALC_CONNECTED 0x313 #endif #ifndef ALC_EXT_thread_local_context #define ALC_EXT_thread_local_context 1 typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context); typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void); #ifdef AL_ALEXT_PROTOTYPES ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context); ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); #endif #endif #ifndef AL_EXT_source_distance_model #define AL_EXT_source_distance_model 1 #define AL_SOURCE_DISTANCE_MODEL 0x200 #endif #ifndef AL_SOFT_buffer_sub_data #define AL_SOFT_buffer_sub_data 1 #define AL_BYTE_RW_OFFSETS_SOFT 0x1031 #define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032 typedef void (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei); #ifdef AL_ALEXT_PROTOTYPES AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length); #endif #endif #ifndef AL_SOFT_loop_points #define AL_SOFT_loop_points 1 #define AL_LOOP_POINTS_SOFT 0x2015 #endif #ifndef AL_EXT_FOLDBACK #define AL_EXT_FOLDBACK 1 #define AL_EXT_FOLDBACK_NAME "AL_EXT_FOLDBACK" #define AL_FOLDBACK_EVENT_BLOCK 0x4112 #define AL_FOLDBACK_EVENT_START 0x4111 #define AL_FOLDBACK_EVENT_STOP 0x4113 #define AL_FOLDBACK_MODE_MONO 0x4101 #define AL_FOLDBACK_MODE_STEREO 0x4102 typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei); typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK); typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void); #ifdef AL_ALEXT_PROTOTYPES AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback); AL_API void AL_APIENTRY alRequestFoldbackStop(void); #endif #endif #ifndef ALC_EXT_DEDICATED #define ALC_EXT_DEDICATED 1 #define AL_DEDICATED_GAIN 0x0001 #define AL_EFFECT_DEDICATED_DIALOGUE 0x9001 #define AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT 0x9000 #endif #ifndef AL_SOFT_buffer_samples #define AL_SOFT_buffer_samples 1 /* Channel configurations */ #define AL_MONO_SOFT 0x1500 #define AL_STEREO_SOFT 0x1501 #define AL_REAR_SOFT 0x1502 #define AL_QUAD_SOFT 0x1503 #define AL_5POINT1_SOFT 0x1504 #define AL_6POINT1_SOFT 0x1505 #define AL_7POINT1_SOFT 0x1506 /* Sample types */ #define AL_BYTE_SOFT 0x1400 #define AL_UNSIGNED_BYTE_SOFT 0x1401 #define AL_SHORT_SOFT 0x1402 #define AL_UNSIGNED_SHORT_SOFT 0x1403 #define AL_INT_SOFT 0x1404 #define AL_UNSIGNED_INT_SOFT 0x1405 #define AL_FLOAT_SOFT 0x1406 #define AL_DOUBLE_SOFT 0x1407 #define AL_BYTE3_SOFT 0x1408 #define AL_UNSIGNED_BYTE3_SOFT 0x1409 /* Storage formats */ #define AL_MONO8_SOFT 0x1100 #define AL_MONO16_SOFT 0x1101 #define AL_MONO32F_SOFT 0x10010 #define AL_STEREO8_SOFT 0x1102 #define AL_STEREO16_SOFT 0x1103 #define AL_STEREO32F_SOFT 0x10011 #define AL_QUAD8_SOFT 0x1204 #define AL_QUAD16_SOFT 0x1205 #define AL_QUAD32F_SOFT 0x1206 #define AL_REAR8_SOFT 0x1207 #define AL_REAR16_SOFT 0x1208 #define AL_REAR32F_SOFT 0x1209 #define AL_5POINT1_8_SOFT 0x120A #define AL_5POINT1_16_SOFT 0x120B #define AL_5POINT1_32F_SOFT 0x120C #define AL_6POINT1_8_SOFT 0x120D #define AL_6POINT1_16_SOFT 0x120E #define AL_6POINT1_32F_SOFT 0x120F #define AL_7POINT1_8_SOFT 0x1210 #define AL_7POINT1_16_SOFT 0x1211 #define AL_7POINT1_32F_SOFT 0x1212 /* Buffer attributes */ #define AL_INTERNAL_FORMAT_SOFT 0x2008 #define AL_BYTE_LENGTH_SOFT 0x2009 #define AL_SAMPLE_LENGTH_SOFT 0x200A #define AL_SEC_LENGTH_SOFT 0x200B typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*); typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*); typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*); typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum); #ifdef AL_ALEXT_PROTOTYPES AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data); AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); #endif #endif #ifndef AL_SOFT_direct_channels #define AL_SOFT_direct_channels 1 #define AL_DIRECT_CHANNELS_SOFT 0x1033 #endif #ifndef ALC_SOFT_loopback #define ALC_SOFT_loopback 1 #define ALC_FORMAT_CHANNELS_SOFT 0x1990 #define ALC_FORMAT_TYPE_SOFT 0x1991 /* Sample types */ #define ALC_BYTE_SOFT 0x1400 #define ALC_UNSIGNED_BYTE_SOFT 0x1401 #define ALC_SHORT_SOFT 0x1402 #define ALC_UNSIGNED_SHORT_SOFT 0x1403 #define ALC_INT_SOFT 0x1404 #define ALC_UNSIGNED_INT_SOFT 0x1405 #define ALC_FLOAT_SOFT 0x1406 /* Channel configurations */ #define ALC_MONO_SOFT 0x1500 #define ALC_STEREO_SOFT 0x1501 #define ALC_QUAD_SOFT 0x1503 #define ALC_5POINT1_SOFT 0x1504 #define ALC_6POINT1_SOFT 0x1505 #define ALC_7POINT1_SOFT 0x1506 typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*); typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum); typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei); #ifdef AL_ALEXT_PROTOTYPES ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName); ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type); ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); #endif #endif #ifndef AL_EXT_STEREO_ANGLES #define AL_EXT_STEREO_ANGLES 1 #define AL_STEREO_ANGLES 0x1030 #endif #ifndef AL_EXT_SOURCE_RADIUS #define AL_EXT_SOURCE_RADIUS 1 #define AL_SOURCE_RADIUS 0x1031 #endif #ifndef AL_SOFT_source_latency #define AL_SOFT_source_latency 1 #define AL_SAMPLE_OFFSET_LATENCY_SOFT 0x1200 #define AL_SEC_OFFSET_LATENCY_SOFT 0x1201 typedef int64_t ALint64SOFT; typedef uint64_t ALuint64SOFT; typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble); typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble); typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*); typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*); typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*); typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*); typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT); typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT); typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*); typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*); typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*); typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*); #ifdef AL_ALEXT_PROTOTYPES AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value); AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3); AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values); AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value); AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3); AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values); AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value); AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3); AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values); AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value); AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3); AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values); #endif #endif #ifndef ALC_EXT_DEFAULT_FILTER_ORDER #define ALC_EXT_DEFAULT_FILTER_ORDER 1 #define ALC_DEFAULT_FILTER_ORDER 0x1100 #endif #ifndef AL_SOFT_deferred_updates #define AL_SOFT_deferred_updates 1 #define AL_DEFERRED_UPDATES_SOFT 0xC002 typedef void (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void); typedef void (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void); #ifdef AL_ALEXT_PROTOTYPES AL_API void AL_APIENTRY alDeferUpdatesSOFT(void); AL_API void AL_APIENTRY alProcessUpdatesSOFT(void); #endif #endif #ifndef AL_SOFT_block_alignment #define AL_SOFT_block_alignment 1 #define AL_UNPACK_BLOCK_ALIGNMENT_SOFT 0x200C #define AL_PACK_BLOCK_ALIGNMENT_SOFT 0x200D #endif #ifndef AL_SOFT_MSADPCM #define AL_SOFT_MSADPCM 1 #define AL_FORMAT_MONO_MSADPCM_SOFT 0x1302 #define AL_FORMAT_STEREO_MSADPCM_SOFT 0x1303 #endif #ifndef AL_SOFT_source_length #define AL_SOFT_source_length 1 /*#define AL_BYTE_LENGTH_SOFT 0x2009*/ /*#define AL_SAMPLE_LENGTH_SOFT 0x200A*/ /*#define AL_SEC_LENGTH_SOFT 0x200B*/ #endif #ifndef ALC_SOFT_pause_device #define ALC_SOFT_pause_device 1 typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device); typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device); #ifdef AL_ALEXT_PROTOTYPES ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device); ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); #endif #endif #ifndef AL_EXT_BFORMAT #define AL_EXT_BFORMAT 1 /* Provides support for B-Format ambisonic buffers (first-order, FuMa scaling * and layout). * * BFORMAT2D_8: Unsigned 8-bit, 3-channel non-periphonic (WXY). * BFORMAT2D_16: Signed 16-bit, 3-channel non-periphonic (WXY). * BFORMAT2D_FLOAT32: 32-bit float, 3-channel non-periphonic (WXY). * BFORMAT3D_8: Unsigned 8-bit, 4-channel periphonic (WXYZ). * BFORMAT3D_16: Signed 16-bit, 4-channel periphonic (WXYZ). * BFORMAT3D_FLOAT32: 32-bit float, 4-channel periphonic (WXYZ). */ #define AL_FORMAT_BFORMAT2D_8 0x20021 #define AL_FORMAT_BFORMAT2D_16 0x20022 #define AL_FORMAT_BFORMAT2D_FLOAT32 0x20023 #define AL_FORMAT_BFORMAT3D_8 0x20031 #define AL_FORMAT_BFORMAT3D_16 0x20032 #define AL_FORMAT_BFORMAT3D_FLOAT32 0x20033 #endif #ifndef AL_EXT_MULAW_BFORMAT #define AL_EXT_MULAW_BFORMAT 1 #define AL_FORMAT_BFORMAT2D_MULAW 0x10031 #define AL_FORMAT_BFORMAT3D_MULAW 0x10032 #endif #ifndef ALC_SOFT_HRTF #define ALC_SOFT_HRTF 1 #define ALC_HRTF_SOFT 0x1992 #define ALC_DONT_CARE_SOFT 0x0002 #define ALC_HRTF_STATUS_SOFT 0x1993 #define ALC_HRTF_DISABLED_SOFT 0x0000 #define ALC_HRTF_ENABLED_SOFT 0x0001 #define ALC_HRTF_DENIED_SOFT 0x0002 #define ALC_HRTF_REQUIRED_SOFT 0x0003 #define ALC_HRTF_HEADPHONES_DETECTED_SOFT 0x0004 #define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT 0x0005 #define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994 #define ALC_HRTF_SPECIFIER_SOFT 0x1995 #define ALC_HRTF_ID_SOFT 0x1996 typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index); typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs); #ifdef AL_ALEXT_PROTOTYPES ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index); ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs); #endif #endif #ifndef AL_SOFT_gain_clamp_ex #define AL_SOFT_gain_clamp_ex 1 #define AL_GAIN_LIMIT_SOFT 0x200E #endif #ifndef AL_SOFT_source_resampler #define AL_SOFT_source_resampler #define AL_NUM_RESAMPLERS_SOFT 0x1210 #define AL_DEFAULT_RESAMPLER_SOFT 0x1211 #define AL_SOURCE_RESAMPLER_SOFT 0x1212 #define AL_RESAMPLER_NAME_SOFT 0x1213 typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index); #ifdef AL_ALEXT_PROTOTYPES AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index); #endif #endif #ifndef AL_SOFT_source_spatialize #define AL_SOFT_source_spatialize #define AL_SOURCE_SPATIALIZE_SOFT 0x1214 #define AL_AUTO_SOFT 0x0002 #endif #ifndef ALC_SOFT_output_limiter #define ALC_SOFT_output_limiter #define ALC_OUTPUT_LIMITER_SOFT 0x199A #endif #ifndef ALC_SOFT_device_clock #define ALC_SOFT_device_clock 1 typedef int64_t ALCint64SOFT; typedef uint64_t ALCuint64SOFT; #define ALC_DEVICE_CLOCK_SOFT 0x1600 #define ALC_DEVICE_LATENCY_SOFT 0x1601 #define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602 #define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202 #define AL_SEC_OFFSET_CLOCK_SOFT 0x1203 typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); #ifdef AL_ALEXT_PROTOTYPES ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); #endif #endif #ifndef AL_SOFT_direct_channels_remix #define AL_SOFT_direct_channels_remix 1 #define AL_DROP_UNMATCHED_SOFT 0x0001 #define AL_REMIX_UNMATCHED_SOFT 0x0002 #endif #ifndef AL_SOFT_bformat_ex #define AL_SOFT_bformat_ex 1 #define AL_AMBISONIC_LAYOUT_SOFT 0x1997 #define AL_AMBISONIC_SCALING_SOFT 0x1998 /* Ambisonic layouts */ #define AL_FUMA_SOFT 0x0000 #define AL_ACN_SOFT 0x0001 /* Ambisonic scalings (normalization) */ /*#define AL_FUMA_SOFT*/ #define AL_SN3D_SOFT 0x0001 #define AL_N3D_SOFT 0x0002 #endif #ifndef ALC_SOFT_loopback_bformat #define ALC_SOFT_loopback_bformat 1 #define ALC_AMBISONIC_LAYOUT_SOFT 0x1997 #define ALC_AMBISONIC_SCALING_SOFT 0x1998 #define ALC_AMBISONIC_ORDER_SOFT 0x1999 #define ALC_MAX_AMBISONIC_ORDER_SOFT 0x199B #define ALC_BFORMAT3D_SOFT 0x1507 /* Ambisonic layouts */ #define ALC_FUMA_SOFT 0x0000 #define ALC_ACN_SOFT 0x0001 /* Ambisonic scalings (normalization) */ /*#define ALC_FUMA_SOFT*/ #define ALC_SN3D_SOFT 0x0001 #define ALC_N3D_SOFT 0x0002 #endif #ifndef AL_SOFT_effect_target #define AL_SOFT_effect_target #define AL_EFFECTSLOT_TARGET_SOFT 0x199C #endif #ifndef AL_SOFT_events #define AL_SOFT_events 1 #define AL_EVENT_CALLBACK_FUNCTION_SOFT 0x19A2 #define AL_EVENT_CALLBACK_USER_PARAM_SOFT 0x19A3 #define AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT 0x19A4 #define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x19A5 #define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x19A6 typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param, ALsizei length, const ALchar *message, void *userParam); typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable); typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam); typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname); typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values); #ifdef AL_ALEXT_PROTOTYPES AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable); AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam); AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname); AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values); #endif #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: vendor/openal-soft/include/AL/efx-creative.h ================================================ /* The tokens that would be defined here are already defined in efx.h. This * empty file is here to provide compatibility with Windows-based projects * that would include it. */ ================================================ FILE: vendor/openal-soft/include/AL/efx-presets.h ================================================ /* Reverb presets for EFX */ #ifndef EFX_PRESETS_H #define EFX_PRESETS_H #ifndef EFXEAXREVERBPROPERTIES_DEFINED #define EFXEAXREVERBPROPERTIES_DEFINED typedef struct { float flDensity; float flDiffusion; float flGain; float flGainHF; float flGainLF; float flDecayTime; float flDecayHFRatio; float flDecayLFRatio; float flReflectionsGain; float flReflectionsDelay; float flReflectionsPan[3]; float flLateReverbGain; float flLateReverbDelay; float flLateReverbPan[3]; float flEchoTime; float flEchoDepth; float flModulationTime; float flModulationDepth; float flAirAbsorptionGainHF; float flHFReference; float flLFReference; float flRoomRolloffFactor; int iDecayHFLimit; } EFXEAXREVERBPROPERTIES, *LPEFXEAXREVERBPROPERTIES; #endif /* Default Presets */ #define EFX_REVERB_PRESET_GENERIC \ { 1.0000f, 1.0000f, 0.3162f, 0.8913f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_PADDEDCELL \ { 0.1715f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.1700f, 0.1000f, 1.0000f, 0.2500f, 0.0010f, { 0.0000f, 0.0000f, 0.0000f }, 1.2691f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ROOM \ { 0.4287f, 1.0000f, 0.3162f, 0.5929f, 1.0000f, 0.4000f, 0.8300f, 1.0000f, 0.1503f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.0629f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_BATHROOM \ { 0.1715f, 1.0000f, 0.3162f, 0.2512f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.6531f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 3.2734f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_LIVINGROOM \ { 0.9766f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.5000f, 0.1000f, 1.0000f, 0.2051f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2805f, 0.0040f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_STONEROOM \ { 1.0000f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 2.3100f, 0.6400f, 1.0000f, 0.4411f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1003f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_AUDITORIUM \ { 1.0000f, 1.0000f, 0.3162f, 0.5781f, 1.0000f, 4.3200f, 0.5900f, 1.0000f, 0.4032f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7170f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CONCERTHALL \ { 1.0000f, 1.0000f, 0.3162f, 0.5623f, 1.0000f, 3.9200f, 0.7000f, 1.0000f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.9977f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CAVE \ { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 2.9100f, 1.3000f, 1.0000f, 0.5000f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.7063f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_ARENA \ { 1.0000f, 1.0000f, 0.3162f, 0.4477f, 1.0000f, 7.2400f, 0.3300f, 1.0000f, 0.2612f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.0186f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_HANGAR \ { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 10.0500f, 0.2300f, 1.0000f, 0.5000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2560f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CARPETEDHALLWAY \ { 0.4287f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 0.3000f, 0.1000f, 1.0000f, 0.1215f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.1531f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_HALLWAY \ { 0.3645f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 1.4900f, 0.5900f, 1.0000f, 0.2458f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.6615f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_STONECORRIDOR \ { 1.0000f, 1.0000f, 0.3162f, 0.7612f, 1.0000f, 2.7000f, 0.7900f, 1.0000f, 0.2472f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 1.5758f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ALLEY \ { 1.0000f, 0.3000f, 0.3162f, 0.7328f, 1.0000f, 1.4900f, 0.8600f, 1.0000f, 0.2500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.9954f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.9500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_FOREST \ { 1.0000f, 0.3000f, 0.3162f, 0.0224f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.0525f, 0.1620f, { 0.0000f, 0.0000f, 0.0000f }, 0.7682f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CITY \ { 1.0000f, 0.5000f, 0.3162f, 0.3981f, 1.0000f, 1.4900f, 0.6700f, 1.0000f, 0.0730f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1427f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_MOUNTAINS \ { 1.0000f, 0.2700f, 0.3162f, 0.0562f, 1.0000f, 1.4900f, 0.2100f, 1.0000f, 0.0407f, 0.3000f, { 0.0000f, 0.0000f, 0.0000f }, 0.1919f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_QUARRY \ { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0000f, 0.0610f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.7000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_PLAIN \ { 1.0000f, 0.2100f, 0.3162f, 0.1000f, 1.0000f, 1.4900f, 0.5000f, 1.0000f, 0.0585f, 0.1790f, { 0.0000f, 0.0000f, 0.0000f }, 0.1089f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_PARKINGLOT \ { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 1.6500f, 1.5000f, 1.0000f, 0.2082f, 0.0080f, { 0.0000f, 0.0000f, 0.0000f }, 0.2652f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_SEWERPIPE \ { 0.3071f, 0.8000f, 0.3162f, 0.3162f, 1.0000f, 2.8100f, 0.1400f, 1.0000f, 1.6387f, 0.0140f, { 0.0000f, 0.0000f, 0.0000f }, 3.2471f, 0.0210f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_UNDERWATER \ { 0.3645f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 1.4900f, 0.1000f, 1.0000f, 0.5963f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 7.0795f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 1.1800f, 0.3480f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_DRUGGED \ { 0.4287f, 0.5000f, 0.3162f, 1.0000f, 1.0000f, 8.3900f, 1.3900f, 1.0000f, 0.8760f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 3.1081f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_DIZZY \ { 0.3645f, 0.6000f, 0.3162f, 0.6310f, 1.0000f, 17.2300f, 0.5600f, 1.0000f, 0.1392f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4937f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.8100f, 0.3100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_PSYCHOTIC \ { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } /* Castle Presets */ #define EFX_REVERB_PRESET_CASTLE_SMALLROOM \ { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 1.2200f, 0.8300f, 0.3100f, 0.8913f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE \ { 1.0000f, 0.8900f, 0.3162f, 0.3162f, 0.1000f, 2.3200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CASTLE_MEDIUMROOM \ { 1.0000f, 0.9300f, 0.3162f, 0.2818f, 0.1000f, 2.0400f, 0.8300f, 0.4600f, 0.6310f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1550f, 0.0300f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CASTLE_LARGEROOM \ { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.1259f, 2.5300f, 0.8300f, 0.5000f, 0.4467f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1850f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CASTLE_LONGPASSAGE \ { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 3.4200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CASTLE_HALL \ { 1.0000f, 0.8100f, 0.3162f, 0.2818f, 0.1778f, 3.1400f, 0.7900f, 0.6200f, 0.1778f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CASTLE_CUPBOARD \ { 1.0000f, 0.8900f, 0.3162f, 0.2818f, 0.1000f, 0.6700f, 0.8700f, 0.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 3.5481f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CASTLE_COURTYARD \ { 1.0000f, 0.4200f, 0.3162f, 0.4467f, 0.1995f, 2.1300f, 0.6100f, 0.2300f, 0.2239f, 0.1600f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3700f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_CASTLE_ALCOVE \ { 1.0000f, 0.8900f, 0.3162f, 0.5012f, 0.1000f, 1.6400f, 0.8700f, 0.3100f, 1.0000f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } /* Factory Presets */ #define EFX_REVERB_PRESET_FACTORY_SMALLROOM \ { 0.3645f, 0.8200f, 0.3162f, 0.7943f, 0.5012f, 1.7200f, 0.6500f, 1.3100f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.1190f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE \ { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 2.5300f, 0.6500f, 1.3100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_FACTORY_MEDIUMROOM \ { 0.4287f, 0.8200f, 0.2512f, 0.7943f, 0.5012f, 2.7600f, 0.6500f, 1.3100f, 0.2818f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1740f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_FACTORY_LARGEROOM \ { 0.4287f, 0.7500f, 0.2512f, 0.7079f, 0.6310f, 4.2400f, 0.5100f, 1.3100f, 0.1778f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2310f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_FACTORY_LONGPASSAGE \ { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 4.0600f, 0.6500f, 1.3100f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_FACTORY_HALL \ { 0.4287f, 0.7500f, 0.3162f, 0.7079f, 0.6310f, 7.4300f, 0.5100f, 1.3100f, 0.0631f, 0.0730f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_FACTORY_CUPBOARD \ { 0.3071f, 0.6300f, 0.2512f, 0.7943f, 0.5012f, 0.4900f, 0.6500f, 1.3100f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.1070f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_FACTORY_COURTYARD \ { 0.3071f, 0.5700f, 0.3162f, 0.3162f, 0.6310f, 2.3200f, 0.2900f, 0.5600f, 0.2239f, 0.1400f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2900f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_FACTORY_ALCOVE \ { 0.3645f, 0.5900f, 0.2512f, 0.7943f, 0.5012f, 3.1400f, 0.6500f, 1.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1140f, 0.1000f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } /* Ice Palace Presets */ #define EFX_REVERB_PRESET_ICEPALACE_SMALLROOM \ { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 1.5100f, 1.5300f, 0.2700f, 0.8913f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1640f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ICEPALACE_SHORTPASSAGE \ { 1.0000f, 0.7500f, 0.3162f, 0.5623f, 0.2818f, 1.7900f, 1.4600f, 0.2800f, 0.5012f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ICEPALACE_MEDIUMROOM \ { 1.0000f, 0.8700f, 0.3162f, 0.5623f, 0.4467f, 2.2200f, 1.5300f, 0.3200f, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ICEPALACE_LARGEROOM \ { 1.0000f, 0.8100f, 0.3162f, 0.5623f, 0.4467f, 3.1400f, 1.5300f, 0.3200f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ICEPALACE_LONGPASSAGE \ { 1.0000f, 0.7700f, 0.3162f, 0.5623f, 0.3981f, 3.0100f, 1.4600f, 0.2800f, 0.7943f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.0400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ICEPALACE_HALL \ { 1.0000f, 0.7600f, 0.3162f, 0.4467f, 0.5623f, 5.4900f, 1.5300f, 0.3800f, 0.1122f, 0.0540f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0520f, { 0.0000f, 0.0000f, 0.0000f }, 0.2260f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ICEPALACE_CUPBOARD \ { 1.0000f, 0.8300f, 0.3162f, 0.5012f, 0.2239f, 0.7600f, 1.5300f, 0.2600f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1430f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ICEPALACE_COURTYARD \ { 1.0000f, 0.5900f, 0.3162f, 0.2818f, 0.3162f, 2.0400f, 1.2000f, 0.3800f, 0.3162f, 0.1730f, { 0.0000f, 0.0000f, 0.0000f }, 0.3162f, 0.0430f, { 0.0000f, 0.0000f, 0.0000f }, 0.2350f, 0.4800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_ICEPALACE_ALCOVE \ { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 2.7600f, 1.4600f, 0.2800f, 1.1220f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1610f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } /* Space Station Presets */ #define EFX_REVERB_PRESET_SPACESTATION_SMALLROOM \ { 0.2109f, 0.7000f, 0.3162f, 0.7079f, 0.8913f, 1.7200f, 0.8200f, 0.5500f, 0.7943f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 0.1880f, 0.2600f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE \ { 0.2109f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 3.5700f, 0.5000f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1720f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM \ { 0.2109f, 0.7500f, 0.3162f, 0.6310f, 0.8913f, 3.0100f, 0.5000f, 0.5500f, 0.3981f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2090f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPACESTATION_LARGEROOM \ { 0.3645f, 0.8100f, 0.3162f, 0.6310f, 0.8913f, 3.8900f, 0.3800f, 0.6100f, 0.3162f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2330f, 0.2800f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE \ { 0.4287f, 0.8200f, 0.3162f, 0.6310f, 0.8913f, 4.6200f, 0.6200f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPACESTATION_HALL \ { 0.4287f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 7.1100f, 0.3800f, 0.6100f, 0.1778f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2500f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPACESTATION_CUPBOARD \ { 0.1715f, 0.5600f, 0.3162f, 0.7079f, 0.8913f, 0.7900f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1810f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPACESTATION_ALCOVE \ { 0.2109f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.1600f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1920f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } /* Wooden Galleon Presets */ #define EFX_REVERB_PRESET_WOODEN_SMALLROOM \ { 1.0000f, 1.0000f, 0.3162f, 0.1122f, 0.3162f, 0.7900f, 0.3200f, 0.8700f, 1.0000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE \ { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.7500f, 0.5000f, 0.8700f, 0.8913f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_WOODEN_MEDIUMROOM \ { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.2818f, 1.4700f, 0.4200f, 0.8200f, 0.8913f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_WOODEN_LARGEROOM \ { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.2818f, 2.6500f, 0.3300f, 0.8200f, 0.8913f, 0.0660f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_WOODEN_LONGPASSAGE \ { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.3162f, 1.9900f, 0.4000f, 0.7900f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4467f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_WOODEN_HALL \ { 1.0000f, 1.0000f, 0.3162f, 0.0794f, 0.2818f, 3.4500f, 0.3000f, 0.8200f, 0.8913f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_WOODEN_CUPBOARD \ { 1.0000f, 1.0000f, 0.3162f, 0.1413f, 0.3162f, 0.5600f, 0.4600f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_WOODEN_COURTYARD \ { 1.0000f, 0.6500f, 0.3162f, 0.0794f, 0.3162f, 1.7900f, 0.3500f, 0.7900f, 0.5623f, 0.1230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_WOODEN_ALCOVE \ { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.2200f, 0.6200f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } /* Sports Presets */ #define EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM \ { 1.0000f, 1.0000f, 0.3162f, 0.4467f, 0.7943f, 6.2600f, 0.5100f, 1.1000f, 0.0631f, 0.1830f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPORT_SQUASHCOURT \ { 1.0000f, 0.7500f, 0.3162f, 0.3162f, 0.7943f, 2.2200f, 0.9100f, 1.1600f, 0.4467f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1260f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPORT_SMALLSWIMMINGPOOL \ { 1.0000f, 0.7000f, 0.3162f, 0.7943f, 0.8913f, 2.7600f, 1.2500f, 1.1400f, 0.6310f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_SPORT_LARGESWIMMINGPOOL \ { 1.0000f, 0.8200f, 0.3162f, 0.7943f, 1.0000f, 5.4900f, 1.3100f, 1.1400f, 0.4467f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2220f, 0.5500f, 1.1590f, 0.2100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_SPORT_GYMNASIUM \ { 1.0000f, 0.8100f, 0.3162f, 0.4467f, 0.8913f, 3.1400f, 1.0600f, 1.3500f, 0.3981f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0450f, { 0.0000f, 0.0000f, 0.0000f }, 0.1460f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPORT_FULLSTADIUM \ { 1.0000f, 1.0000f, 0.3162f, 0.0708f, 0.7943f, 5.2500f, 0.1700f, 0.8000f, 0.1000f, 0.1880f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SPORT_STADIUMTANNOY \ { 1.0000f, 0.7800f, 0.3162f, 0.5623f, 0.5012f, 2.5300f, 0.8800f, 0.6800f, 0.2818f, 0.2300f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } /* Prefab Presets */ #define EFX_REVERB_PRESET_PREFAB_WORKSHOP \ { 0.4287f, 1.0000f, 0.3162f, 0.1413f, 0.3981f, 0.7600f, 1.0000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_PREFAB_SCHOOLROOM \ { 0.4022f, 0.6900f, 0.3162f, 0.6310f, 0.5012f, 0.9800f, 0.4500f, 0.1800f, 1.4125f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_PREFAB_PRACTISEROOM \ { 0.4022f, 0.8700f, 0.3162f, 0.3981f, 0.5012f, 1.1200f, 0.5600f, 0.1800f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_PREFAB_OUTHOUSE \ { 1.0000f, 0.8200f, 0.3162f, 0.1122f, 0.1585f, 1.3800f, 0.3800f, 0.3500f, 0.8913f, 0.0240f, { 0.0000f, 0.0000f, -0.0000f }, 0.6310f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.1210f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_PREFAB_CARAVAN \ { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.1259f, 0.4300f, 1.5000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } /* Dome and Pipe Presets */ #define EFX_REVERB_PRESET_DOME_TOMB \ { 1.0000f, 0.7900f, 0.3162f, 0.3548f, 0.2239f, 4.1800f, 0.2100f, 0.1000f, 0.3868f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 1.6788f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_PIPE_SMALL \ { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 5.0400f, 0.1000f, 0.1000f, 0.5012f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 2.5119f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_DOME_SAINTPAULS \ { 1.0000f, 0.8700f, 0.3162f, 0.3548f, 0.2239f, 10.4800f, 0.1900f, 0.1000f, 0.1778f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0420f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_PIPE_LONGTHIN \ { 0.2560f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 9.2100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_PIPE_LARGE \ { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 8.4500f, 0.1000f, 0.1000f, 0.3981f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_PIPE_RESONANT \ { 0.1373f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 6.8100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } /* Outdoors Presets */ #define EFX_REVERB_PRESET_OUTDOORS_BACKYARD \ { 1.0000f, 0.4500f, 0.3162f, 0.2512f, 0.5012f, 1.1200f, 0.3400f, 0.4600f, 0.4467f, 0.0690f, { 0.0000f, 0.0000f, -0.0000f }, 0.7079f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS \ { 1.0000f, 0.0000f, 0.3162f, 0.0112f, 0.6310f, 2.1300f, 0.2100f, 0.4600f, 0.1778f, 0.3000f, { 0.0000f, 0.0000f, -0.0000f }, 0.4467f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON \ { 1.0000f, 0.7400f, 0.3162f, 0.1778f, 0.6310f, 3.8900f, 0.2100f, 0.4600f, 0.3162f, 0.2230f, { 0.0000f, 0.0000f, -0.0000f }, 0.3548f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_OUTDOORS_CREEK \ { 1.0000f, 0.3500f, 0.3162f, 0.1778f, 0.5012f, 2.1300f, 0.2100f, 0.4600f, 0.3981f, 0.1150f, { 0.0000f, 0.0000f, -0.0000f }, 0.1995f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_OUTDOORS_VALLEY \ { 1.0000f, 0.2800f, 0.3162f, 0.0282f, 0.1585f, 2.8800f, 0.2600f, 0.3500f, 0.1413f, 0.2630f, { 0.0000f, 0.0000f, -0.0000f }, 0.3981f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } /* Mood Presets */ #define EFX_REVERB_PRESET_MOOD_HEAVEN \ { 1.0000f, 0.9400f, 0.3162f, 0.7943f, 0.4467f, 5.0400f, 1.1200f, 0.5600f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0800f, 2.7420f, 0.0500f, 0.9977f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_MOOD_HELL \ { 1.0000f, 0.5700f, 0.3162f, 0.3548f, 0.4467f, 3.5700f, 0.4900f, 2.0000f, 0.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1100f, 0.0400f, 2.1090f, 0.5200f, 0.9943f, 5000.0000f, 139.5000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_MOOD_MEMORY \ { 1.0000f, 0.8500f, 0.3162f, 0.6310f, 0.3548f, 4.0600f, 0.8200f, 0.5600f, 0.0398f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.4740f, 0.4500f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } /* Driving Presets */ #define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \ { 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_DRIVING_PITGARAGE \ { 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_DRIVING_INCAR_RACER \ { 0.0832f, 0.8000f, 0.3162f, 1.0000f, 0.7943f, 0.1700f, 2.0000f, 0.4100f, 1.7783f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS \ { 0.0832f, 0.8000f, 0.3162f, 0.6310f, 1.0000f, 0.1700f, 0.7500f, 0.4100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY \ { 0.2560f, 1.0000f, 0.3162f, 0.1000f, 0.5012f, 0.1300f, 0.4100f, 0.4600f, 0.7943f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND \ { 1.0000f, 1.0000f, 0.3162f, 0.2818f, 0.6310f, 3.0100f, 1.3700f, 1.2800f, 0.3548f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.1778f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND \ { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 0.7943f, 4.6200f, 1.7500f, 1.4000f, 0.2082f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_DRIVING_TUNNEL \ { 1.0000f, 0.8100f, 0.3162f, 0.3981f, 0.8913f, 3.4200f, 0.9400f, 1.3100f, 0.7079f, 0.0510f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.0500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 155.3000f, 0.0000f, 0x1 } /* City Presets */ #define EFX_REVERB_PRESET_CITY_STREETS \ { 1.0000f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.7900f, 1.1200f, 0.9100f, 0.2818f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 0.1995f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CITY_SUBWAY \ { 1.0000f, 0.7400f, 0.3162f, 0.7079f, 0.8913f, 3.0100f, 1.2300f, 0.9100f, 0.7079f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CITY_MUSEUM \ { 1.0000f, 0.8200f, 0.3162f, 0.1778f, 0.1778f, 3.2800f, 1.4000f, 0.5700f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_CITY_LIBRARY \ { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.0891f, 2.7600f, 0.8900f, 0.4100f, 0.3548f, 0.0290f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } #define EFX_REVERB_PRESET_CITY_UNDERPASS \ { 1.0000f, 0.8200f, 0.3162f, 0.4467f, 0.8913f, 3.5700f, 1.1200f, 0.9100f, 0.3981f, 0.0590f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1400f, 0.2500f, 0.0000f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CITY_ABANDONED \ { 1.0000f, 0.6900f, 0.3162f, 0.7943f, 0.8913f, 3.2800f, 1.1700f, 0.9100f, 0.4467f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9966f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } /* Misc. Presets */ #define EFX_REVERB_PRESET_DUSTYROOM \ { 0.3645f, 0.5600f, 0.3162f, 0.7943f, 0.7079f, 1.7900f, 0.3800f, 0.2100f, 0.5012f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0060f, { 0.0000f, 0.0000f, 0.0000f }, 0.2020f, 0.0500f, 0.2500f, 0.0000f, 0.9886f, 13046.0000f, 163.3000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_CHAPEL \ { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 1.0000f, 4.6200f, 0.6400f, 1.2300f, 0.4467f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.1100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } #define EFX_REVERB_PRESET_SMALLWATERROOM \ { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } #endif /* EFX_PRESETS_H */ ================================================ FILE: vendor/openal-soft/include/AL/efx.h ================================================ #ifndef AL_EFX_H #define AL_EFX_H #include #include "alc.h" #include "al.h" #ifdef __cplusplus extern "C" { #endif #define ALC_EXT_EFX_NAME "ALC_EXT_EFX" #define ALC_EFX_MAJOR_VERSION 0x20001 #define ALC_EFX_MINOR_VERSION 0x20002 #define ALC_MAX_AUXILIARY_SENDS 0x20003 /* Listener properties. */ #define AL_METERS_PER_UNIT 0x20004 /* Source properties. */ #define AL_DIRECT_FILTER 0x20005 #define AL_AUXILIARY_SEND_FILTER 0x20006 #define AL_AIR_ABSORPTION_FACTOR 0x20007 #define AL_ROOM_ROLLOFF_FACTOR 0x20008 #define AL_CONE_OUTER_GAINHF 0x20009 #define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A #define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B #define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C /* Effect properties. */ /* Reverb effect parameters */ #define AL_REVERB_DENSITY 0x0001 #define AL_REVERB_DIFFUSION 0x0002 #define AL_REVERB_GAIN 0x0003 #define AL_REVERB_GAINHF 0x0004 #define AL_REVERB_DECAY_TIME 0x0005 #define AL_REVERB_DECAY_HFRATIO 0x0006 #define AL_REVERB_REFLECTIONS_GAIN 0x0007 #define AL_REVERB_REFLECTIONS_DELAY 0x0008 #define AL_REVERB_LATE_REVERB_GAIN 0x0009 #define AL_REVERB_LATE_REVERB_DELAY 0x000A #define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B #define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C #define AL_REVERB_DECAY_HFLIMIT 0x000D /* EAX Reverb effect parameters */ #define AL_EAXREVERB_DENSITY 0x0001 #define AL_EAXREVERB_DIFFUSION 0x0002 #define AL_EAXREVERB_GAIN 0x0003 #define AL_EAXREVERB_GAINHF 0x0004 #define AL_EAXREVERB_GAINLF 0x0005 #define AL_EAXREVERB_DECAY_TIME 0x0006 #define AL_EAXREVERB_DECAY_HFRATIO 0x0007 #define AL_EAXREVERB_DECAY_LFRATIO 0x0008 #define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009 #define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A #define AL_EAXREVERB_REFLECTIONS_PAN 0x000B #define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C #define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D #define AL_EAXREVERB_LATE_REVERB_PAN 0x000E #define AL_EAXREVERB_ECHO_TIME 0x000F #define AL_EAXREVERB_ECHO_DEPTH 0x0010 #define AL_EAXREVERB_MODULATION_TIME 0x0011 #define AL_EAXREVERB_MODULATION_DEPTH 0x0012 #define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013 #define AL_EAXREVERB_HFREFERENCE 0x0014 #define AL_EAXREVERB_LFREFERENCE 0x0015 #define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016 #define AL_EAXREVERB_DECAY_HFLIMIT 0x0017 /* Chorus effect parameters */ #define AL_CHORUS_WAVEFORM 0x0001 #define AL_CHORUS_PHASE 0x0002 #define AL_CHORUS_RATE 0x0003 #define AL_CHORUS_DEPTH 0x0004 #define AL_CHORUS_FEEDBACK 0x0005 #define AL_CHORUS_DELAY 0x0006 /* Distortion effect parameters */ #define AL_DISTORTION_EDGE 0x0001 #define AL_DISTORTION_GAIN 0x0002 #define AL_DISTORTION_LOWPASS_CUTOFF 0x0003 #define AL_DISTORTION_EQCENTER 0x0004 #define AL_DISTORTION_EQBANDWIDTH 0x0005 /* Echo effect parameters */ #define AL_ECHO_DELAY 0x0001 #define AL_ECHO_LRDELAY 0x0002 #define AL_ECHO_DAMPING 0x0003 #define AL_ECHO_FEEDBACK 0x0004 #define AL_ECHO_SPREAD 0x0005 /* Flanger effect parameters */ #define AL_FLANGER_WAVEFORM 0x0001 #define AL_FLANGER_PHASE 0x0002 #define AL_FLANGER_RATE 0x0003 #define AL_FLANGER_DEPTH 0x0004 #define AL_FLANGER_FEEDBACK 0x0005 #define AL_FLANGER_DELAY 0x0006 /* Frequency shifter effect parameters */ #define AL_FREQUENCY_SHIFTER_FREQUENCY 0x0001 #define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION 0x0002 #define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION 0x0003 /* Vocal morpher effect parameters */ #define AL_VOCAL_MORPHER_PHONEMEA 0x0001 #define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING 0x0002 #define AL_VOCAL_MORPHER_PHONEMEB 0x0003 #define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING 0x0004 #define AL_VOCAL_MORPHER_WAVEFORM 0x0005 #define AL_VOCAL_MORPHER_RATE 0x0006 /* Pitchshifter effect parameters */ #define AL_PITCH_SHIFTER_COARSE_TUNE 0x0001 #define AL_PITCH_SHIFTER_FINE_TUNE 0x0002 /* Ringmodulator effect parameters */ #define AL_RING_MODULATOR_FREQUENCY 0x0001 #define AL_RING_MODULATOR_HIGHPASS_CUTOFF 0x0002 #define AL_RING_MODULATOR_WAVEFORM 0x0003 /* Autowah effect parameters */ #define AL_AUTOWAH_ATTACK_TIME 0x0001 #define AL_AUTOWAH_RELEASE_TIME 0x0002 #define AL_AUTOWAH_RESONANCE 0x0003 #define AL_AUTOWAH_PEAK_GAIN 0x0004 /* Compressor effect parameters */ #define AL_COMPRESSOR_ONOFF 0x0001 /* Equalizer effect parameters */ #define AL_EQUALIZER_LOW_GAIN 0x0001 #define AL_EQUALIZER_LOW_CUTOFF 0x0002 #define AL_EQUALIZER_MID1_GAIN 0x0003 #define AL_EQUALIZER_MID1_CENTER 0x0004 #define AL_EQUALIZER_MID1_WIDTH 0x0005 #define AL_EQUALIZER_MID2_GAIN 0x0006 #define AL_EQUALIZER_MID2_CENTER 0x0007 #define AL_EQUALIZER_MID2_WIDTH 0x0008 #define AL_EQUALIZER_HIGH_GAIN 0x0009 #define AL_EQUALIZER_HIGH_CUTOFF 0x000A /* Effect type */ #define AL_EFFECT_FIRST_PARAMETER 0x0000 #define AL_EFFECT_LAST_PARAMETER 0x8000 #define AL_EFFECT_TYPE 0x8001 /* Effect types, used with the AL_EFFECT_TYPE property */ #define AL_EFFECT_NULL 0x0000 #define AL_EFFECT_REVERB 0x0001 #define AL_EFFECT_CHORUS 0x0002 #define AL_EFFECT_DISTORTION 0x0003 #define AL_EFFECT_ECHO 0x0004 #define AL_EFFECT_FLANGER 0x0005 #define AL_EFFECT_FREQUENCY_SHIFTER 0x0006 #define AL_EFFECT_VOCAL_MORPHER 0x0007 #define AL_EFFECT_PITCH_SHIFTER 0x0008 #define AL_EFFECT_RING_MODULATOR 0x0009 #define AL_EFFECT_AUTOWAH 0x000A #define AL_EFFECT_COMPRESSOR 0x000B #define AL_EFFECT_EQUALIZER 0x000C #define AL_EFFECT_EAXREVERB 0x8000 /* Auxiliary Effect Slot properties. */ #define AL_EFFECTSLOT_EFFECT 0x0001 #define AL_EFFECTSLOT_GAIN 0x0002 #define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003 /* NULL Auxiliary Slot ID to disable a source send. */ #define AL_EFFECTSLOT_NULL 0x0000 /* Filter properties. */ /* Lowpass filter parameters */ #define AL_LOWPASS_GAIN 0x0001 #define AL_LOWPASS_GAINHF 0x0002 /* Highpass filter parameters */ #define AL_HIGHPASS_GAIN 0x0001 #define AL_HIGHPASS_GAINLF 0x0002 /* Bandpass filter parameters */ #define AL_BANDPASS_GAIN 0x0001 #define AL_BANDPASS_GAINLF 0x0002 #define AL_BANDPASS_GAINHF 0x0003 /* Filter type */ #define AL_FILTER_FIRST_PARAMETER 0x0000 #define AL_FILTER_LAST_PARAMETER 0x8000 #define AL_FILTER_TYPE 0x8001 /* Filter types, used with the AL_FILTER_TYPE property */ #define AL_FILTER_NULL 0x0000 #define AL_FILTER_LOWPASS 0x0001 #define AL_FILTER_HIGHPASS 0x0002 #define AL_FILTER_BANDPASS 0x0003 /* Effect object function types. */ typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*); typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*); typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint); typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*); typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat); typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*); typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*); typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*); typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*); typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*); /* Filter object function types. */ typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*); typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*); typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint); typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*); typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat); typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*); typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*); typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*); typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*); typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*); /* Auxiliary Effect Slot object function types. */ typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*); typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint); typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint); typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*); typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat); typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*); typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*); typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*); typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); #ifdef AL_ALEXT_PROTOTYPES AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects); AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects); AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect); AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue); AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues); AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue); AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues); AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue); AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues); AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue); AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters); AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters); AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter); AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue); AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues); AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue); AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues); AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue); AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues); AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue); AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots); AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot); AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue); AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues); AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue); AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues); AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue); AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue); AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); #endif /* Filter ranges and defaults. */ /* Lowpass filter */ #define AL_LOWPASS_MIN_GAIN (0.0f) #define AL_LOWPASS_MAX_GAIN (1.0f) #define AL_LOWPASS_DEFAULT_GAIN (1.0f) #define AL_LOWPASS_MIN_GAINHF (0.0f) #define AL_LOWPASS_MAX_GAINHF (1.0f) #define AL_LOWPASS_DEFAULT_GAINHF (1.0f) /* Highpass filter */ #define AL_HIGHPASS_MIN_GAIN (0.0f) #define AL_HIGHPASS_MAX_GAIN (1.0f) #define AL_HIGHPASS_DEFAULT_GAIN (1.0f) #define AL_HIGHPASS_MIN_GAINLF (0.0f) #define AL_HIGHPASS_MAX_GAINLF (1.0f) #define AL_HIGHPASS_DEFAULT_GAINLF (1.0f) /* Bandpass filter */ #define AL_BANDPASS_MIN_GAIN (0.0f) #define AL_BANDPASS_MAX_GAIN (1.0f) #define AL_BANDPASS_DEFAULT_GAIN (1.0f) #define AL_BANDPASS_MIN_GAINHF (0.0f) #define AL_BANDPASS_MAX_GAINHF (1.0f) #define AL_BANDPASS_DEFAULT_GAINHF (1.0f) #define AL_BANDPASS_MIN_GAINLF (0.0f) #define AL_BANDPASS_MAX_GAINLF (1.0f) #define AL_BANDPASS_DEFAULT_GAINLF (1.0f) /* Effect parameter ranges and defaults. */ /* Standard reverb effect */ #define AL_REVERB_MIN_DENSITY (0.0f) #define AL_REVERB_MAX_DENSITY (1.0f) #define AL_REVERB_DEFAULT_DENSITY (1.0f) #define AL_REVERB_MIN_DIFFUSION (0.0f) #define AL_REVERB_MAX_DIFFUSION (1.0f) #define AL_REVERB_DEFAULT_DIFFUSION (1.0f) #define AL_REVERB_MIN_GAIN (0.0f) #define AL_REVERB_MAX_GAIN (1.0f) #define AL_REVERB_DEFAULT_GAIN (0.32f) #define AL_REVERB_MIN_GAINHF (0.0f) #define AL_REVERB_MAX_GAINHF (1.0f) #define AL_REVERB_DEFAULT_GAINHF (0.89f) #define AL_REVERB_MIN_DECAY_TIME (0.1f) #define AL_REVERB_MAX_DECAY_TIME (20.0f) #define AL_REVERB_DEFAULT_DECAY_TIME (1.49f) #define AL_REVERB_MIN_DECAY_HFRATIO (0.1f) #define AL_REVERB_MAX_DECAY_HFRATIO (2.0f) #define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f) #define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f) #define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f) #define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) #define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f) #define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f) #define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) #define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f) #define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f) #define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) #define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f) #define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f) #define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) #define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) #define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) #define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) #define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) #define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) #define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) #define AL_REVERB_MIN_DECAY_HFLIMIT AL_FALSE #define AL_REVERB_MAX_DECAY_HFLIMIT AL_TRUE #define AL_REVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE /* EAX reverb effect */ #define AL_EAXREVERB_MIN_DENSITY (0.0f) #define AL_EAXREVERB_MAX_DENSITY (1.0f) #define AL_EAXREVERB_DEFAULT_DENSITY (1.0f) #define AL_EAXREVERB_MIN_DIFFUSION (0.0f) #define AL_EAXREVERB_MAX_DIFFUSION (1.0f) #define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f) #define AL_EAXREVERB_MIN_GAIN (0.0f) #define AL_EAXREVERB_MAX_GAIN (1.0f) #define AL_EAXREVERB_DEFAULT_GAIN (0.32f) #define AL_EAXREVERB_MIN_GAINHF (0.0f) #define AL_EAXREVERB_MAX_GAINHF (1.0f) #define AL_EAXREVERB_DEFAULT_GAINHF (0.89f) #define AL_EAXREVERB_MIN_GAINLF (0.0f) #define AL_EAXREVERB_MAX_GAINLF (1.0f) #define AL_EAXREVERB_DEFAULT_GAINLF (1.0f) #define AL_EAXREVERB_MIN_DECAY_TIME (0.1f) #define AL_EAXREVERB_MAX_DECAY_TIME (20.0f) #define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f) #define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f) #define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f) #define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f) #define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f) #define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f) #define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f) #define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f) #define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f) #define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) #define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f) #define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f) #define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) #define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f) #define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f) #define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f) #define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) #define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f) #define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f) #define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) #define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f) #define AL_EAXREVERB_MIN_ECHO_TIME (0.075f) #define AL_EAXREVERB_MAX_ECHO_TIME (0.25f) #define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f) #define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f) #define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f) #define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f) #define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f) #define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f) #define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f) #define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f) #define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f) #define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f) #define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) #define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) #define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) #define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f) #define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f) #define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f) #define AL_EAXREVERB_MIN_LFREFERENCE (20.0f) #define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f) #define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f) #define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) #define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) #define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) #define AL_EAXREVERB_MIN_DECAY_HFLIMIT AL_FALSE #define AL_EAXREVERB_MAX_DECAY_HFLIMIT AL_TRUE #define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE /* Chorus effect */ #define AL_CHORUS_WAVEFORM_SINUSOID (0) #define AL_CHORUS_WAVEFORM_TRIANGLE (1) #define AL_CHORUS_MIN_WAVEFORM (0) #define AL_CHORUS_MAX_WAVEFORM (1) #define AL_CHORUS_DEFAULT_WAVEFORM (1) #define AL_CHORUS_MIN_PHASE (-180) #define AL_CHORUS_MAX_PHASE (180) #define AL_CHORUS_DEFAULT_PHASE (90) #define AL_CHORUS_MIN_RATE (0.0f) #define AL_CHORUS_MAX_RATE (10.0f) #define AL_CHORUS_DEFAULT_RATE (1.1f) #define AL_CHORUS_MIN_DEPTH (0.0f) #define AL_CHORUS_MAX_DEPTH (1.0f) #define AL_CHORUS_DEFAULT_DEPTH (0.1f) #define AL_CHORUS_MIN_FEEDBACK (-1.0f) #define AL_CHORUS_MAX_FEEDBACK (1.0f) #define AL_CHORUS_DEFAULT_FEEDBACK (0.25f) #define AL_CHORUS_MIN_DELAY (0.0f) #define AL_CHORUS_MAX_DELAY (0.016f) #define AL_CHORUS_DEFAULT_DELAY (0.016f) /* Distortion effect */ #define AL_DISTORTION_MIN_EDGE (0.0f) #define AL_DISTORTION_MAX_EDGE (1.0f) #define AL_DISTORTION_DEFAULT_EDGE (0.2f) #define AL_DISTORTION_MIN_GAIN (0.01f) #define AL_DISTORTION_MAX_GAIN (1.0f) #define AL_DISTORTION_DEFAULT_GAIN (0.05f) #define AL_DISTORTION_MIN_LOWPASS_CUTOFF (80.0f) #define AL_DISTORTION_MAX_LOWPASS_CUTOFF (24000.0f) #define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF (8000.0f) #define AL_DISTORTION_MIN_EQCENTER (80.0f) #define AL_DISTORTION_MAX_EQCENTER (24000.0f) #define AL_DISTORTION_DEFAULT_EQCENTER (3600.0f) #define AL_DISTORTION_MIN_EQBANDWIDTH (80.0f) #define AL_DISTORTION_MAX_EQBANDWIDTH (24000.0f) #define AL_DISTORTION_DEFAULT_EQBANDWIDTH (3600.0f) /* Echo effect */ #define AL_ECHO_MIN_DELAY (0.0f) #define AL_ECHO_MAX_DELAY (0.207f) #define AL_ECHO_DEFAULT_DELAY (0.1f) #define AL_ECHO_MIN_LRDELAY (0.0f) #define AL_ECHO_MAX_LRDELAY (0.404f) #define AL_ECHO_DEFAULT_LRDELAY (0.1f) #define AL_ECHO_MIN_DAMPING (0.0f) #define AL_ECHO_MAX_DAMPING (0.99f) #define AL_ECHO_DEFAULT_DAMPING (0.5f) #define AL_ECHO_MIN_FEEDBACK (0.0f) #define AL_ECHO_MAX_FEEDBACK (1.0f) #define AL_ECHO_DEFAULT_FEEDBACK (0.5f) #define AL_ECHO_MIN_SPREAD (-1.0f) #define AL_ECHO_MAX_SPREAD (1.0f) #define AL_ECHO_DEFAULT_SPREAD (-1.0f) /* Flanger effect */ #define AL_FLANGER_WAVEFORM_SINUSOID (0) #define AL_FLANGER_WAVEFORM_TRIANGLE (1) #define AL_FLANGER_MIN_WAVEFORM (0) #define AL_FLANGER_MAX_WAVEFORM (1) #define AL_FLANGER_DEFAULT_WAVEFORM (1) #define AL_FLANGER_MIN_PHASE (-180) #define AL_FLANGER_MAX_PHASE (180) #define AL_FLANGER_DEFAULT_PHASE (0) #define AL_FLANGER_MIN_RATE (0.0f) #define AL_FLANGER_MAX_RATE (10.0f) #define AL_FLANGER_DEFAULT_RATE (0.27f) #define AL_FLANGER_MIN_DEPTH (0.0f) #define AL_FLANGER_MAX_DEPTH (1.0f) #define AL_FLANGER_DEFAULT_DEPTH (1.0f) #define AL_FLANGER_MIN_FEEDBACK (-1.0f) #define AL_FLANGER_MAX_FEEDBACK (1.0f) #define AL_FLANGER_DEFAULT_FEEDBACK (-0.5f) #define AL_FLANGER_MIN_DELAY (0.0f) #define AL_FLANGER_MAX_DELAY (0.004f) #define AL_FLANGER_DEFAULT_DELAY (0.002f) /* Frequency shifter effect */ #define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY (0.0f) #define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY (24000.0f) #define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY (0.0f) #define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION (0) #define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION (2) #define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0) #define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN (0) #define AL_FREQUENCY_SHIFTER_DIRECTION_UP (1) #define AL_FREQUENCY_SHIFTER_DIRECTION_OFF (2) #define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0) #define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2) #define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0) /* Vocal morpher effect */ #define AL_VOCAL_MORPHER_MIN_PHONEMEA (0) #define AL_VOCAL_MORPHER_MAX_PHONEMEA (29) #define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA (0) #define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24) #define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24) #define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0) #define AL_VOCAL_MORPHER_MIN_PHONEMEB (0) #define AL_VOCAL_MORPHER_MAX_PHONEMEB (29) #define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB (10) #define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24) #define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24) #define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0) #define AL_VOCAL_MORPHER_PHONEME_A (0) #define AL_VOCAL_MORPHER_PHONEME_E (1) #define AL_VOCAL_MORPHER_PHONEME_I (2) #define AL_VOCAL_MORPHER_PHONEME_O (3) #define AL_VOCAL_MORPHER_PHONEME_U (4) #define AL_VOCAL_MORPHER_PHONEME_AA (5) #define AL_VOCAL_MORPHER_PHONEME_AE (6) #define AL_VOCAL_MORPHER_PHONEME_AH (7) #define AL_VOCAL_MORPHER_PHONEME_AO (8) #define AL_VOCAL_MORPHER_PHONEME_EH (9) #define AL_VOCAL_MORPHER_PHONEME_ER (10) #define AL_VOCAL_MORPHER_PHONEME_IH (11) #define AL_VOCAL_MORPHER_PHONEME_IY (12) #define AL_VOCAL_MORPHER_PHONEME_UH (13) #define AL_VOCAL_MORPHER_PHONEME_UW (14) #define AL_VOCAL_MORPHER_PHONEME_B (15) #define AL_VOCAL_MORPHER_PHONEME_D (16) #define AL_VOCAL_MORPHER_PHONEME_F (17) #define AL_VOCAL_MORPHER_PHONEME_G (18) #define AL_VOCAL_MORPHER_PHONEME_J (19) #define AL_VOCAL_MORPHER_PHONEME_K (20) #define AL_VOCAL_MORPHER_PHONEME_L (21) #define AL_VOCAL_MORPHER_PHONEME_M (22) #define AL_VOCAL_MORPHER_PHONEME_N (23) #define AL_VOCAL_MORPHER_PHONEME_P (24) #define AL_VOCAL_MORPHER_PHONEME_R (25) #define AL_VOCAL_MORPHER_PHONEME_S (26) #define AL_VOCAL_MORPHER_PHONEME_T (27) #define AL_VOCAL_MORPHER_PHONEME_V (28) #define AL_VOCAL_MORPHER_PHONEME_Z (29) #define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID (0) #define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE (1) #define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH (2) #define AL_VOCAL_MORPHER_MIN_WAVEFORM (0) #define AL_VOCAL_MORPHER_MAX_WAVEFORM (2) #define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM (0) #define AL_VOCAL_MORPHER_MIN_RATE (0.0f) #define AL_VOCAL_MORPHER_MAX_RATE (10.0f) #define AL_VOCAL_MORPHER_DEFAULT_RATE (1.41f) /* Pitch shifter effect */ #define AL_PITCH_SHIFTER_MIN_COARSE_TUNE (-12) #define AL_PITCH_SHIFTER_MAX_COARSE_TUNE (12) #define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE (12) #define AL_PITCH_SHIFTER_MIN_FINE_TUNE (-50) #define AL_PITCH_SHIFTER_MAX_FINE_TUNE (50) #define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE (0) /* Ring modulator effect */ #define AL_RING_MODULATOR_MIN_FREQUENCY (0.0f) #define AL_RING_MODULATOR_MAX_FREQUENCY (8000.0f) #define AL_RING_MODULATOR_DEFAULT_FREQUENCY (440.0f) #define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF (0.0f) #define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF (24000.0f) #define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f) #define AL_RING_MODULATOR_SINUSOID (0) #define AL_RING_MODULATOR_SAWTOOTH (1) #define AL_RING_MODULATOR_SQUARE (2) #define AL_RING_MODULATOR_MIN_WAVEFORM (0) #define AL_RING_MODULATOR_MAX_WAVEFORM (2) #define AL_RING_MODULATOR_DEFAULT_WAVEFORM (0) /* Autowah effect */ #define AL_AUTOWAH_MIN_ATTACK_TIME (0.0001f) #define AL_AUTOWAH_MAX_ATTACK_TIME (1.0f) #define AL_AUTOWAH_DEFAULT_ATTACK_TIME (0.06f) #define AL_AUTOWAH_MIN_RELEASE_TIME (0.0001f) #define AL_AUTOWAH_MAX_RELEASE_TIME (1.0f) #define AL_AUTOWAH_DEFAULT_RELEASE_TIME (0.06f) #define AL_AUTOWAH_MIN_RESONANCE (2.0f) #define AL_AUTOWAH_MAX_RESONANCE (1000.0f) #define AL_AUTOWAH_DEFAULT_RESONANCE (1000.0f) #define AL_AUTOWAH_MIN_PEAK_GAIN (0.00003f) #define AL_AUTOWAH_MAX_PEAK_GAIN (31621.0f) #define AL_AUTOWAH_DEFAULT_PEAK_GAIN (11.22f) /* Compressor effect */ #define AL_COMPRESSOR_MIN_ONOFF (0) #define AL_COMPRESSOR_MAX_ONOFF (1) #define AL_COMPRESSOR_DEFAULT_ONOFF (1) /* Equalizer effect */ #define AL_EQUALIZER_MIN_LOW_GAIN (0.126f) #define AL_EQUALIZER_MAX_LOW_GAIN (7.943f) #define AL_EQUALIZER_DEFAULT_LOW_GAIN (1.0f) #define AL_EQUALIZER_MIN_LOW_CUTOFF (50.0f) #define AL_EQUALIZER_MAX_LOW_CUTOFF (800.0f) #define AL_EQUALIZER_DEFAULT_LOW_CUTOFF (200.0f) #define AL_EQUALIZER_MIN_MID1_GAIN (0.126f) #define AL_EQUALIZER_MAX_MID1_GAIN (7.943f) #define AL_EQUALIZER_DEFAULT_MID1_GAIN (1.0f) #define AL_EQUALIZER_MIN_MID1_CENTER (200.0f) #define AL_EQUALIZER_MAX_MID1_CENTER (3000.0f) #define AL_EQUALIZER_DEFAULT_MID1_CENTER (500.0f) #define AL_EQUALIZER_MIN_MID1_WIDTH (0.01f) #define AL_EQUALIZER_MAX_MID1_WIDTH (1.0f) #define AL_EQUALIZER_DEFAULT_MID1_WIDTH (1.0f) #define AL_EQUALIZER_MIN_MID2_GAIN (0.126f) #define AL_EQUALIZER_MAX_MID2_GAIN (7.943f) #define AL_EQUALIZER_DEFAULT_MID2_GAIN (1.0f) #define AL_EQUALIZER_MIN_MID2_CENTER (1000.0f) #define AL_EQUALIZER_MAX_MID2_CENTER (8000.0f) #define AL_EQUALIZER_DEFAULT_MID2_CENTER (3000.0f) #define AL_EQUALIZER_MIN_MID2_WIDTH (0.01f) #define AL_EQUALIZER_MAX_MID2_WIDTH (1.0f) #define AL_EQUALIZER_DEFAULT_MID2_WIDTH (1.0f) #define AL_EQUALIZER_MIN_HIGH_GAIN (0.126f) #define AL_EQUALIZER_MAX_HIGH_GAIN (7.943f) #define AL_EQUALIZER_DEFAULT_HIGH_GAIN (1.0f) #define AL_EQUALIZER_MIN_HIGH_CUTOFF (4000.0f) #define AL_EQUALIZER_MAX_HIGH_CUTOFF (16000.0f) #define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF (6000.0f) /* Source parameter value ranges and defaults. */ #define AL_MIN_AIR_ABSORPTION_FACTOR (0.0f) #define AL_MAX_AIR_ABSORPTION_FACTOR (10.0f) #define AL_DEFAULT_AIR_ABSORPTION_FACTOR (0.0f) #define AL_MIN_ROOM_ROLLOFF_FACTOR (0.0f) #define AL_MAX_ROOM_ROLLOFF_FACTOR (10.0f) #define AL_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) #define AL_MIN_CONE_OUTER_GAINHF (0.0f) #define AL_MAX_CONE_OUTER_GAINHF (1.0f) #define AL_DEFAULT_CONE_OUTER_GAINHF (1.0f) #define AL_MIN_DIRECT_FILTER_GAINHF_AUTO AL_FALSE #define AL_MAX_DIRECT_FILTER_GAINHF_AUTO AL_TRUE #define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO AL_TRUE #define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_FALSE #define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE #define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE #define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE #define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE #define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE /* Listener parameter value ranges and defaults. */ #define AL_MIN_METERS_PER_UNIT FLT_MIN #define AL_MAX_METERS_PER_UNIT FLT_MAX #define AL_DEFAULT_METERS_PER_UNIT (1.0f) #ifdef __cplusplus } /* extern "C" */ #endif #endif /* AL_EFX_H */ ================================================ FILE: vendor/openal-soft/libs/Win32/OpenAL32.def ================================================ EXPORTS alBuffer3f alBuffer3i alBufferData alBufferf alBufferfv alBufferi alBufferiv alDeleteBuffers alDeleteSources alDisable alDistanceModel alDopplerFactor alDopplerVelocity alEnable alGenBuffers alGenSources alGetBoolean alGetBooleanv alGetBuffer3f alGetBuffer3i alGetBufferf alGetBufferfv alGetBufferi alGetBufferiv alGetDouble alGetDoublev alGetEnumValue alGetError alGetFloat alGetFloatv alGetInteger alGetIntegerv alGetListener3f alGetListener3i alGetListenerf alGetListenerfv alGetListeneri alGetListeneriv alGetProcAddress alGetSource3f alGetSource3i alGetSourcef alGetSourcefv alGetSourcei alGetSourceiv alGetString alIsBuffer alIsEnabled alIsExtensionPresent alIsSource alListener3f alListener3i alListenerf alListenerfv alListeneri alListeneriv alSource3f alSource3i alSourcePause alSourcePausev alSourcePlay alSourcePlayv alSourceQueueBuffers alSourceRewind alSourceRewindv alSourceStop alSourceStopv alSourceUnqueueBuffers alSourcef alSourcefv alSourcei alSourceiv alSpeedOfSound alcCaptureCloseDevice alcCaptureOpenDevice alcCaptureSamples alcCaptureStart alcCaptureStop alcCloseDevice alcCreateContext alcDestroyContext alcGetContextsDevice alcGetCurrentContext alcGetEnumValue alcGetError alcGetIntegerv alcGetProcAddress alcGetString alcGetThreadContext alcIsExtensionPresent alcMakeContextCurrent alcOpenDevice alcProcessContext alcSetThreadContext alcSuspendContext ================================================ FILE: vendor/openal-soft/libs/Win64/OpenAL32.def ================================================ EXPORTS alBuffer3f alBuffer3i alBufferData alBufferf alBufferfv alBufferi alBufferiv alDeleteBuffers alDeleteSources alDisable alDistanceModel alDopplerFactor alDopplerVelocity alEnable alGenBuffers alGenSources alGetBoolean alGetBooleanv alGetBuffer3f alGetBuffer3i alGetBufferf alGetBufferfv alGetBufferi alGetBufferiv alGetDouble alGetDoublev alGetEnumValue alGetError alGetFloat alGetFloatv alGetInteger alGetIntegerv alGetListener3f alGetListener3i alGetListenerf alGetListenerfv alGetListeneri alGetListeneriv alGetProcAddress alGetSource3f alGetSource3i alGetSourcef alGetSourcefv alGetSourcei alGetSourceiv alGetString alIsBuffer alIsEnabled alIsExtensionPresent alIsSource alListener3f alListener3i alListenerf alListenerfv alListeneri alListeneriv alSource3f alSource3i alSourcePause alSourcePausev alSourcePlay alSourcePlayv alSourceQueueBuffers alSourceRewind alSourceRewindv alSourceStop alSourceStopv alSourceUnqueueBuffers alSourcef alSourcefv alSourcei alSourceiv alSpeedOfSound alcCaptureCloseDevice alcCaptureOpenDevice alcCaptureSamples alcCaptureStart alcCaptureStop alcCloseDevice alcCreateContext alcDestroyContext alcGetContextsDevice alcGetCurrentContext alcGetEnumValue alcGetError alcGetIntegerv alcGetProcAddress alcGetString alcGetThreadContext alcIsExtensionPresent alcMakeContextCurrent alcOpenDevice alcProcessContext alcSetThreadContext alcSuspendContext ================================================ FILE: vendor/openal-soft/readme.txt ================================================ OpenAL Soft Binary Distribution These binaries are provided as a convenience. Users and developers may use it so they can use OpenAL Soft without having to build it from source. Note that it is still expected to install the OpenAL redistributable provided by Creative Labs (at http://openal.org/), as that will provide the "router" OpenAL32.dll that applications talk to, and may provide extra drivers for the user's system. The DLLs provided here will simply add additional devices for applications to select from. If you do not wish to use the redistributable, then rename soft_oal.dll to OpenAL32.dll (note: even the 64-bit DLL should be named OpenAL32.dll). Just be aware this will prevent other system-installed OpenAL implementations from working. To use the 32-bit DLL, copy it from the bin\Win32 folder to the folder that the 32-bit OpenAL32.dll router is installed in. For 32-bit Windows, the Win32 DLL will typically go into the system32 folder. For 64-bit Windows, the Win32 DLL will typically go into the SysWOW64 folder. To use the 64-bit DLL, copy it from the bin\Win64 folder to the folder that the 64-bit OpenAL32.dll router is installed in. For 64-bit Windows, this will typically be the system32 folder. The included openal-info32.exe and openal-info64.exe programs can be used to tell if the OpenAL Soft DLL is being detected. It should be run from a command shell, as the program will exit as soon as it's done printing information. A configuration GUI app is provided in the alsoft-config folder. It is a front- end to editing %AppData%\alsoft.ini, which can be used to modify certain behaviors for OpenAL Soft devices. Have fun!